pax_global_header00006660000000000000000000000064126501707230014515gustar00rootroot0000000000000052 comment=cb4369aefaf781dc40a20668459c0b911b08e9f1 qtads-2.1.7/000077500000000000000000000000001265017072300126405ustar00rootroot00000000000000qtads-2.1.7/.gitignore000066400000000000000000000002561265017072300146330ustar00rootroot00000000000000*.[oa] /src/charmap_inst_dir.h /src/doc_inst_dir.h /src/i18n_inst_dir.h /tmp/ /obj/ /Makefile /qmake_image_collection.cpp /qtads /qtads.pro.user* /qrc_resources.cpp /android qtads-2.1.7/AUTHORS000066400000000000000000000001761265017072300137140ustar00rootroot00000000000000Tads portable reference code: Michael J. Roberts QTads code: Nikos Chantziaras qtads-2.1.7/COPYING000066400000000000000000000432541265017072300137030ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, 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 or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's 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 give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, 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 Program, 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 Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) 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; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, 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 executable. However, as a special exception, the source code 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. If distribution of executable or 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 counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program 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. 5. 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 Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program 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 to this License. 7. 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 Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program 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 Program. 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. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program 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. 9. The Free Software Foundation may publish revised and/or new versions of the 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 Program 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 Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, 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 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. qtads-2.1.7/HTML_TADS_LICENSE000066400000000000000000000054551265017072300153150ustar00rootroot00000000000000----------------------------------------------------------------------- You can redistribute and/or modify QTads either under the terms of the HTML TADS Freeware Source Code License as appearing in this file, or under the terms of the GNU General Public License as published by the Free Software Foundation and appearing in the file COPYING included in the packaging of this file (either version 2 of the license, or, at your option, any later version). You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. The remainder of this file consists of the terms and conditions of the TADS 3 Freeware Source Code License. ----------------------------------------------------------------------- HTML TADS FREEWARE SOURCE CODE LICENSE The HTML TADS source code is Copyright 1997, 2003 by Michael J. Roberts. The author hereby grants you permission to use, copy, and distribute this software, if you agree to the following conditions: 1. You must include this license and the copyright notice with all copies. 2. You may not require or collect a fee for copies of this software, or any part of this software, that you give to other people. 3. You may not include this software with any other software for which a fee is collected. 4. You may not modify this software except as permitted below (see "derivative works"), and each copy you make and distribute must be a full and complete copy of the software you originally received. 5. Anyone to whom you give a copy of this software receives all of the same permissions that you did under this license and is subject to all of the same restrictions. 6. You are not allowed to create derivative works, which are works that contain or are based on all or part of this work, except under the conditions described below. 7. Any derivative works are subject to this same license. Derivative Works ---------------- This source code is distributed for the specific purpose of facilitating the creation of versions of TADS on various computers and operating systems. All other derivative works are prohibited without the written permission of the author. Please contact the author if you have any questions about this or if you'd like permission to create a derived work. If you port TADS to a new platform, the author does grant permission for you to distribute your ported version - I encourage it, in fact. We ask that you provide your contact information in any distribution package you create, so that users of your version will know how to contact you if they have any questions relating specifically to your version. qtads-2.1.7/INSTALL000066400000000000000000000024041265017072300136710ustar00rootroot00000000000000First of all, if you're on Gentoo Linux or some othe Portage-based system, then there's no need to continue; you can find QTads ebuilds in the "interactive-fiction" overlay; simply add it with the "layman" utility and then emerge the "games-engines/qtads" package. QTads uses qmake as its build system. To build it: cd qmake make This will create a "qtads" binary which you can then copy and run from anywhere. There are no files that need to be installed. For it to build correctly, you will need to have the needed libraries along with their development headers/tools installed. At this time, this would be: Qt4 "Core" and "GUI" modules Qt4 development tools (qmake, uic, etc.) SDL SDL_mixer SDL_sound Most Linux distributions provide the development versions in packages that have "-dev" appended to the package name. On Ubuntu and Debian, it is usually enough to install the following packages in order to be able to build QTads: libqt4-dev libsdl-mixer1.2-dev libsdl-sound1.2-dev Debian Lenny users should note that even though QTads will build and run with Qt 4.4 (Lenny only offers that version), the results will be less than satisfactory. For good results, Qt 4.5 or higher is recommended. qtads-2.1.7/Info.plist000066400000000000000000000043411265017072300146120ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleShortVersionString 2.1.7 NSHumanReadableCopyright Copyright © 2013, Mike Roberts; © 2016, Nikos Chantziaras. CFBundleIconFile @ICON@ CFBundlePackageType APPL CFBundleGetInfoString Created by Qt/QMake CFBundleSignature ???? CFBundleExecutable @EXECUTABLE@ CFBundleIdentifier net.sourceforge.qtads.QTads NOTE This file was generated by Qt/QMake. NSPrincipalClass NSApplication NSHighResolutionCapable True CFBundleDocumentTypes CFBundleTypeRole Viewer CFBundleTypeExtensions gam CFBundleTypeIconFile QTadsGameFile.icns CFBundleTypeMIMETypes application/x-tads CFBundleTypeName TADS 2 Game CFBundleTypeOSTypes **** CFBundleTypeRole Viewer CFBundleTypeExtensions t3 t3x CFBundleTypeIconFile QTadsGameFile.icns CFBundleTypeMIMETypes application/x-t3vm-image CFBundleTypeName TADS 3 Game CFBundleTypeOSTypes **** qtads-2.1.7/NEWS000066400000000000000000001055721265017072300133510ustar00rootroot000000000000002.1.7 - 2016-01-21 ================== - The TADS virtual machines have been updated to 2.5.17/3.1.3 - Added support for Mac OS X high resolution (retina) displays. - The interpreter will now try to detect whether a game is stuck in an infinite loop when quitting. This should avoid the situation where the QTads window is closed, but a process is still running in the background, consuming CPU cycles. - Selecting text with the mouse is now supported, as is drag&drop of text to and from the game window. Furthermore, you can paste the current word under the mouse cursor instead of selecting it by holding down the "Ctrl" key ("Command" key on the Mac) while double clicking. This behavior can be disabled in the configuration dialog in the "Misc" section. - On Mac OS X, the main window should now properly get focus again when closing a dialog. - Text-grid banner windows should now be able to correctly show their background color instead of always using black. - Printing a table without any cells in it will no longer result in the screen getting "stuck" and failing to update. - Fixed a problem with the online update check, which would result in a a connection error, or sometimes an application crash. Thanks to Denis Pokataev for finding and fixing the crash case. - Added Ctrl+F command history search. It finds previous commands that begin with the same text as the text that is currently to the left of the input caret. If multiple matches are found, repeatedly pressing Ctrl+F will cycle through all matches. Thanks to tetzank for the patch. 2.1.6 - 2013/01/01 ================== - Fixed a problem where the interpreter was not able to create savegames on some systems. The problem occured most notably on MS Windows, but in general affected all systems where it's possible to have absolute paths that don't start with a path seperator ('/' or '\'). - File handling should now be fully internationalized. File paths and filenames (saved games, transcripts, game files, etc.) should now work in any language on all systems. - The TADS 3 VM no longer produces the same sequence of random numbers every time the interpreter is launched. - For those of you who like to build QTads from source, you can now build it against Qt 5. The released binaries will still be using Qt 4 though, since it's well tested. 2.1.5 - 2012/09/02 ================== - The TADS virtual machines have been updated to 2.5.16/3.1.2. 2.1.4 - 2012/08/06 ================== - The TADS virtual machines have been updated to 2.5.15/3.1.1. - QTads now comes with an application icon for Mac OS X and Windows. On OS X, it also registers the file types it can open and provides icons for them. - On rare occasions, games that present GUI dialogs to the user would not recognize which button was clicked. This has been fixed. 2.1.3 - 2012/03/29 ================== - Fixed a bug where games could not create new files and would print a "file operation prohibited by user-specified file safety level" error, even if the file was being created in the game's directory and the write permission setting in the configuration dialog was set to permit that. - Some fonts ("Garamond Premier Pro" for example) could cause the interpreter to crash or fall back to the Helvetica font. This should no longer occur. - The application should now no longer crash on Windows XP systems. - A problem where games were unable to find their data files has been fixed. - MIDI music looping should now work on Mac OS. Previously, MIDI tracks would only play once, even when the game intended them to play forever. - Under some circumstances, the application would still prompt for confirmation when quitting or restarting a game even if confirmation was disabled in the preferences. This has been corrected. 2.1.2 - 2011/12/29 ================== - The TADS virtual machines have been updated to 2.5.15/3.1.0. Note that QTads cannot run WebUI games (yet) because the "tads-net" function set is not implemented yet. For the time being you can run WebUI games with FrobTADS. - A "soft scrolling" option is now available in the configuration dialog. When enabled, output from the game will scroll-in line by line instead of all at once. - On Mac OS X, text positioning could be off by a few pixels in some circumstances. This should now no longer occur. - An error dialog is now displayed when trying to open a file that is not a Tads game. Previously, the error message was displayed on stdout. - When running in KDE, it is not possible anymore to move the interpreter window by simply clicking somewhere inside the game window where no text has been printed yet. The game window is never really "empty", so this kind of "window grabbing" should have not been allowed to begin with. - The confirmation dialog that appears when trying to restart or quit a game can now be disabled via newly introduced configuration dialog options. - File I/O safety levels can now be configured seperately for read and write access. - An online update check is now performed when the application starts. The behavior is configurable via new configuration dialog options. 2.1.1 - 2011/02/18 ================== - When the interpreter needs to scale an image, it can now use bilinear filtering. This results in smoother images (without filtering, scaled images can appear pixelated or have other scaling artifacts). However, the configuration dialog provides an option to disable filtering, since it also makes scaled images appear less sharp. - The "Game Information" metadata viewer now supports and displays cover art images (as specified by the Treaty of Babel.) Also, the list of recognized names and values (like "Genre", "Headline", "Forgiveness", etc) should now be complete. - The list of recently played games now behaves a bit smarter; it will use the actual name from the game's meta data (if available) rather than the path/filename, and will also try to avoid duplicate entries (which can happen when symbolic links are used in the path and/or filename.) - Fixed a bug that resulted in games using the wrong font in some places. "Six Stories" is one example; it would previously use a wrong font for input, titles and statusbar. - It is now possible to use the main game font as input font through a new option found in the configuration dialog. - The file I/O safety level feature of the VM is now configurable through the configuration dialog. - The interpreter now correctly responds to game queries about whether graphics, audio and hyperlinks are currently enabled or disabled. Previously, the interpreter would always report that these features are enabled, making it impossible for games to adapt their behavior (for example "Six Stories" alters its behavior according to whether graphics and sound are enabled in the interpreter preferences.) - Fades and crossfades are now correctly advertised as being supported. Previously, the interpreter wrongly responded with "not supported" when a game was querying about it. - Fixed a rare crash that occured while entering non-latin characters. 2.1.0 - 2011/02/01 ================== - Page-pauses (aka "MORE" prompts) are now occurring when the game displays more content that will fit in the window. - Banners can now have borders. Note that borders are controlled by the game; it's not a feature that can be switched on or off by the player. - Banners now correctly take margins into account when calculating sizes. This fixes the banner at the bottom in "Walker & Silhouette" for example. - The interpreter will no longer allow scrolling in banners unless the game allows it. - Text rendering is now faster and performs less drawing operations. - The interpreter is now able to perform on online check for new versions through the "Help->Check for Updates" menu item. - Some GUI elements were changed to better comply with various "Human Interface Guidelines" on several platforms. This includes changes in the menus as well as using sheets for some dialogs on Mac OS X. - On the Mac, running a game by dropping its file on the QTads icon is now supported. - When the VM requests input from the player, the maximum allowed length of input is now taken into account. Previously, the interpreter would allow input to be much longer than what the VM accepts and the extra characters were simply truncated. - Renamed the Digital Sound/MIDI Music sound options. They were misleading. The "Enable MIDI Sound" option did not control MIDI but rather the background sound layer, which is not restricted to MIDI. - The interpreter will now correctly disable keyboard cursor blinking if needed (for example when blinking has been disabled in the operating system's settings.) Furthermore, blinking will only occur for as long as the interpreter window has focus. - The last entered directory for the "Open New Game" dialog is now remembered between sessions. 2.0.2 - 2011/01/13 ================== - In games that use background images, there was a visual glitch in the part of the background image near the vertical scrollbar. This has been fixed. - You can now drag&drop TADS game files into the application window to load them into the interpreter. - Clicking on a banner window no longer results in not being able to compose accented characters (or other characters requiring "dead keys".) - Text grid banners should now have the correct foreground/background color (light gray/black) when a game doesn't explicitly set them. - Games are now able again to display dialog windows with buttons. - A crash at exit that occured on some systems after running games that play MIDI music has been fixed. 2.0.1 - 2010/10/10 ================== - A new "Restart Current Game" menu option has been added. - Digital sound fade-in, fade-out and cross-fades are now fully supported on all operating systems. I think... - MP3 and WAV sounds that use "odd" sampling rates should now play correctly. Previously, they sometimes were playing at double speed. - Input of non-ASCII characters for languages that use compose keys ("dead keys") should now work correctly for TADS 3. - A memory leak during unloading of MP3 and WAV sounds has been fixed. - Decoding of long MP3s on Windows should not take several millenia anymore. - A new configuration option was introduced that allows for selecting the character set encoding to be used when playing TADS 2 games. Previously, the interpreter was treating TADS 2 games the same as TADS 3 ones, which wouldn't work correctly with games that use characters outside the ASCII range. - When built on Linux with Qt 4.6 or newer, appropriate icons from the desktop environment's current theme will be used for the various menu items. - A sane set of default fonts and sizes is now used on Mac and Windows. - Sometimes the interpreter would not scroll down to the bottom when opening TADS 2 games. This should no longer be occuring. 2.0 - 2010/09/28 ================ QTads 2 is a new major version, bringing multimedia capabilities (HTML Tads) and is based on Qt version 4 (4.4 and newer). Some stuff had to go during the rewrite of major parts of the code. This was necessary in order to decrease the initial porting effort and to release something that works within a reasonable time frame. I'm hoping to bring everything back bit by bit as development progresses. 1.9 - 2009/06/24 ================ - A bug where HTML character entities would appear in the window title has been fixed (for example QTads would display "Lydia’s Heart" instead of "Lydia's Heart".) - The "Recent Games" list no longer displays games that are no longer accessible (due to deletion or file permissions or other factors.) - Fixed some visual glitches in the main window and status line. 1.8 - 2009/05/27 ================ - Updated the Tads virtual machine to version 2.5.14/3.0.18.1 - Fixed Unicode filename problems. Running games from directories that contain Unicode characters as well as saving and loading gamestates in such directories should work now. - Fixed a compilation problem on BSD systems. - Fixed a display problem with Tads 2 games that use non-latin characters and don't make use of character mapping files. The system's local character encoding will now be used by default for Tads 2. Russian games for example that are written in the CP1251 character set using the "RTADS" libraries will now display correctly if the system's character set is also CP1251 ("ru_RU.CP1251" in Linux for example; note that "ru_RU.UTF-8" won't work since the character set must match the one of the game.) Tads 2 games should really use mapping files though; this is just a work-around to allow for those that don't to display correctly. 1.7 - 2008/03/31 ================ Wow, over 3 years since the last update :P - Updated the Tads virtual machine to version 2.5.10/3.0.15.3 - The amount of turns the player can UNDO in Tads 3 games has been increased. In previous versions, the player could only UNDO a few turns (about 6 or so; it varies by game). Now you can undo more than 100 turns. Note that there's an upper-limit of turns that the player can UNDO (255) imposed by the T3 VM. - Fixed some non-working shortcuts in the configuration dialog. - QTads is now able to parse the meta-information resource embedded in a game (Tads 2 as well as Tads 3). For this, a new menu entry has been added ("Game->Game Information"). HTML code in the meta-info tags (if it exists) is supported and displayed correctly. - Fixed compilation errors with recent compilers. - The interpreter will now correctly restore the window coordinates and size of the previous session even when starting in fullscreen mode. Changing from fullscreen to windowed mode will now use the geometry that was set in the previous session before entering fullscreen mode. - The "File Open" dialog will now correctly show both Tads 2 as well as Tads 3 games. 1.6c - 2004/12/15 ================= Due do a stupidity-related mistake, version 1.6 wouldn't compile nor run with versions lower than 3.3 of Qt. The problem was in the user interface files (forms/*.ui); they were saved in the new Qt Designer 3.3 format, which is *so* not compatible with prior versions. This problem has been fixed in this release. Another quite important change is the compile-time detection of the system's endianess (big vs little endian). QTads should now run almost everywhere (at least I hope it does). - Fixed compilation problems with Qt versions lower than 3.3. - The system's endianess now shouldn't matter when compiling QTads. - Enhanced the way icons are loaded. In previous versions, they didn't use the alpha channel for transparency effects, resulting in pixelized and blocky appearance. This has been fixed. (You can now find the icons in the images/ directory. They won't get installed, but will be embedded into the executable at build time.) 1.6 - 2004/11/21 ================ I feel like I'll never implement that darn Tads 3 banner-support! I tried it -- really -- but the implementation was chaotic, buggy, didn't work most of the time, and who knows what else. All I can say is that I'm still working on it. Help is *really* more than welcome, as my Real World life changed completely in the last few months. No, it's not marriage (thank God); not imprisonment; didn't die either. It's worse: I'm a student now! (And probably will remain in this state for the next five years, give but not take a few. And if you're curious about what I'm studying, it's "information and communication systems engineering", which is kind of a funny coincidence since the impression I had about software engineers wasn't the most flattering one.) - QTads now has text-color support for Tads 3 games (foreground only). For example, "I Must Play" (an IF-Comp 2004 entry) is playable in color. - A non-bug (long story; don't even ask) caused transcript files to be saved in UTF-8 encoded Unicode. This has been fixed; transcript files will now use the system's local character set. Furthermore, the bug where Tads 2 transcripts contained spurious 01 bytes among the text has been fixed. Mac OS X will still use UTF-8, since, as it happens, that's the "native" character set on this system. Same goes for modern Linux systems that use UTF-8 for everything (SuSE 9.1, for example). - The clock's state is now preserved between sessions. - The TadsVM file I/O safety levels are now supported and can be adjusted in the configuration dialog. There are 5 safety levels: 0: Safety mechanism disabled; unlimited read/write access. 1: Read everywhere, write to current (game's) directory only. 2: Reading/writing is only allowed in the current directory. 3: Read in current directory only; no writing allowed. 4: Completely disable file I/O. The default level is 2. This change doesn't affect the saving and restoring of games, since safety levels only apply to file I/O initiated by the game, not by the VM. - In Mac OS X, the interpreter's title bar got hidden behind the menu bar on start-up (when QTads was started for the first time). This has been fixed; now the OS decides where to put the window. - Fixed another bug where game-text could be modified by the player. I really need to replace the default input-handlers offered by Qt, since this kind of bug keeps showing up and the work-arounds create quite a mess in the sources (yeah, OK, they're already a mess, but that's no reason to ignore the problem). - The context menu ("right-click menu") now pops up even when the click was inside the left/right margin, and the menu and tool bar don't have a context menu anymore; the tool bar can be hidden/shown using the game-window's context menu ("Show/hide toolbar"). Furthermore, the items in the context menu now have accelerators; F1 to show/hide the menu bar, F2 for the tool bar, and F3 for the scroll bar. - The most important change: new icons! They're also smaller than the previous ones (16x16 instead of 22x22). - Added some German translations that were missing. - Updated the Tads virtual machine to version 2.5.9/3.0.8. - Other minor fixes and enhancements here and there. 1.5 - 2004/06/04 ================ Guess what? Still no Tads 3 banners. ("Avalon" anyone?) At least we have plenty of bug-fixes and enhancements. The most important addition is the timed input-event support; "The Recruit" (an IF Comp 2003 entry by M. Sousa and R. Sherwin) is now playable and won't crash anymore. IMPORTANT: The T3 VM is known to crash with some versions of the GNU Compiler (3.3.x and maybe 3.4.x). This seems to be either a problem with GCC's optimizer, or with T3's code. Probably the former. When QTads aborts with a segmentation fault or similar, add the "-fno-strict-aliasing" option to the CFLAGS and CXXFLAGS variables when building the interpreter (see the INSTALL file for details). - QTads now fully supports timed input-events. It works 100% with Tads 2 games; Tads 3 timed *input* (as opposed to timed input *events*, which are available now) is still not implemented. Let's hope for one of the next versions. - In Mac OS X, the Carbon Preferences mechanism is now used to store the settings. (Linux and other Unices will still use the "$HOME/.qt/qtadsrc" file. - Soft scrolling can now be disabled. Default is enabled. - Fixed a bug where it was necessary to click on the "Open new game" button twice if a game was specified as a command-line argument. - Drag&dropping text inside the game window was broken (game text was being overriden). This has been fixed. - Fixed a text-redraw bug when the current text-alignment was changed. - In previous versions, it wasn't possible to scroll the text with the PageUp/PageDown keys if the game had stopped running, and it was possible to modify game-text when using them. - PageUp/PageDown now also works when in a "more" prompt. - File-extension recognition didn't work. The game extension (.gam or .t3) can be omitted now. - The warning dialog that said "The QTDIR environment variable is not set ..." is no more. If Qt's translation-files can't be found, QTads will silently fall-back to the default English versions. - The Tads 3 version and copyright information at the start of a game won't appear anymore. These changes don't affect functionality: - In Mac OS X, the application is now called "QTads" instead of "qtads", since capitalization of program names seems to be considered a Good Thing (ugh!) - Added a manual page (qtads.6.gz). This won't get installed or anything; it's mainly for the Debian package (maintained by Daniel Schepler), since Debian considers the lack of a man page to be a "bug". 1.4 - 2004/05/13 ================ (This version was never officially released, mainly due to lack of time; at the time the release could be made, v1.5 was already waiting to be tarballed and sourceforged.) The good news is that this version introduces support for Mac OS X. A big "thank you" to Matt Herberg, who also is the maintainer of the Mac OS X distribution and co-developer, and Curt Siffert. Thanks guys! The bad news is that we still don't have Tads 3 banner support. I hope we'll have it in the next version. - Added Mac OS X support. - Fixed a bug where files requested by the game could not be accessed if the interpreter was not started from the game's directory. For example, you can now put the registration key-file of "Losing Your Grip" (regkey.txt) in the same directory as the game itself, and the game will find it. - Prompting for a game to run at startup is now optional; it can be enabled/disabled in the configuration dialog. - The directory where the documentation is installed no longer has a version number attached. Every time you install a new version of the interpreter, the new documentation will simply replace the older files. Opinions differ if this is better or worse, but I think it's somewhat awkward to having to delete the old directory manually after installing a new version. - Updated the Tads virtual machine to version 2.5.8/3.0.6p. 1.3a - 2003/09/28 ================= Oops, I did a mistake in the last release; the "Recent Games" menu was behaving very weird (newly loaded games didn't show up until QTads was restarted). This release only fixes this bug; nothing else. 1.3 - 2003/09/26 ================ Although this version lacks enough changes to justify a new release, I nonetheless decided to release it because of the upcoming IF competition (http://www.ifcomp.org). - Added a "Recent Games" menu. (Was about time this got added.) - Fixed a bug where the mouse cursor was staying invisible when it shouldn't (when the window was losing keyboard focus, for example). - Enhanced the documentation in the sources, since several people have found QTads' code quite useful. (Where "several" is any number between 1 and 2.) - Updated the Tads virtual machine to version 2.5.8/3.0.6k. 1.2 - 2003/08/10 ================ - QTads now supports italic text. Previously, the tag was rendered in bold, and was ignored; both are now rendered in italics (as in HTML TADS). Furthermore, the tag is now recognized and rendered in bold italics ("strong emphasis"). - Every pair of ASCII dashes ("--") is now replaced with a typographical em-dash in Tads 2 games (Tads 3 games don't need this, since they already display em-dashes on their own). This is useful for games that don't make use of the "—" Multimedia Tads character entity, or make the false assumption that character entities are not supported in text-only interpreters. This feature can be enabled/disabled in the configuration dialog, and it works only in Tads 2 games. (Tip: Try it with "Worlds Apart.") - Fixed two cosmetic bugs in the command history. 1) When using the up/down key to cycle through the previous inputs, they were displayed using the game window's font settings, not the user input settings. 2) The current command is not saved in the history anymore if it is the same as the previous one. - Fixed the (hopefully) last remaining bugs where it was possible to modify game-text. (Leif Huhn provided this fix.) - Added "Help->Version Information" menu. - Changed some icons. I hope you like them. - The documentation has been restructured. - Updated the Tads virtual machine to version 2.5.8/3.0.6j. 1.1 - 2003/05/28 ================ The QTads homepage has moved from Tripod to SourceForge. The new location is: http://qtads.sourceforge.net (the old page at http://members.lycos.co.uk/realnc/qtads still exists, but contains nothing except a pointer to the new location). If you had links to the old site, please update them. - QTads now uses Unicode when running Tads 3 games. This means that Multimedia TADS character entities like typographical (``curly'') quotes and dashes of different lengths (en- and em-dashes) are displayed correctly. The only limitation is that the font you use must support these characters; most fonts out there support them, but I saw a few ones that don't. - QTads is also able to display typographical quotes and dashes in Tads 2 games. Note that this feature must be supported by the game itself; if the game simply prints out a normal (") quote instead of a typographical one, QTads can do nothing about it. See the TIPS file for a list of games that use typographical quotes instead of normal ones. - Support for Tads 2 character mapping files has been added. QTads now provides mapping files for every ISO 8859 (known as ISO Latin) character set that Tads 2 is able to use (located in the charmaps/ subdirectory). Note that QTads doesn't provide (or need) mapping files for Tads 3 games, since it uses Unicode. - QTads can display curly apostrophes in every game. This can be disabled in the configuration dialog. - The "Curly quotes" setting in the configuration dialog has been implemented. 1.0 - 2003/05/05 ================ This is the first "stable" version of QTads (not that the previous one was unstable or something). - User input can now be displayed in any combination of bold, italic or underline. - Added a clock that keeps track of how much time a game is running. - Added internationalization support and translated everything to German. (Hmm, no Greek yet.) This also includes support for translations of the Qt library itself (as documented in the INSTALL file now). - Ctrl+C (Copy), Ctrl+V (Paste) and similar shortcuts are working now. (In earlier versions, the selection got removed as soon as Ctrl was pressed.) - QTads doesn't eat up CPU cycles when idle now, which means you can let it run in the background and it won't slow the system down. In earlier versions, QTads was using about 30% of CPU time on my system when idle; now it uses only 0.1%. (Christophe Antoine provided this fix.) - Fixed the fix of the fix of the statusline. (It's obvious that I can't code.) It should not word-wrap without need now and not resize its height the first time it prints something. - Fixed Tads 2 doublespacing problems. Note that the new T3 VM doesn't support doublespacing anymore; it's handled by the game file itself. This means that older games (compiled with earlier Tads 3 versions) will never use doublespacing while new ones will always use it, no matter how QTads is configured. This raises a problem I don't know how to fix; doublespacing in block-justified text doesn't work as it should. This is a bug in Qt's text renderer. - Fullscreen-mode is saved when quitting QTads. - Millisecond precision is now supported when the VM asks QTads for the elapsed time (os_get_sys_clock_ms()). 1.0 beta - 2003/03/21 ===================== This is the first public release of QTads. - Theme support added. A "theme" is a set of preferences (colors, margins, fonts, etc) that can be saved and then activated at runtime. The themes can be edited in the configuration dialog and then activated using the new Display->Theme menu. - Fixed a bug where QTads was trying to restore a saved game when loading Tads 3 games and failed with a message saying that "this interpreter can't restore [...]" - Tads 3 text rendering is more "smooth" now. This fixes the "scrolling in steps" problem. - Changed the frame around the game window. This fixes some visual bugs related to margins. - Fixed a bug in the statusline where both parts of it (text and score) were equally wide, no matter how much text they contained. - Further reduced the statusline's height. - The makefile's "dist" target should now be able to create a working source archive of QTads. - Improved dependency tracking in the project file. All files should correctly be recompiled, no matter how deep the dependency-nesting is in each case. This eliminates the problem where a "touch tads2/os.h" (or similar) was required in order to achieve a recompilation. - The PageUp and PageDown keys can now be used to scroll up or down. - Added a few more keyboard shortcuts (like restoring, restarting, saving, scrolling, etc) and documented them in a new dialog (Help->Shortcuts). 1.0 alpha 3 - 2003/03/17 ======================== This version was a private beta-testing release. The main change in this version is the included T3 VM. It's a bit difficult to test QTads with Tads 3 games, since only a few have been released. The games I tested with are: * The Tads 3 Library Sample Game (Mike Roberts) * Forever Always (Iain Merrick) * Eric's Gift (Joao Mendes) * The Demon's Eye, 2003/03/01 (Guilherme De Sousa, not released yet) * Lost In Somewhere (my own WIP, not released yet) QTads lacks banner-support, so the library sample game is semi-functional in QTads. I hope to add banners in the next version. Note that Tads 3 doesn't use HTML for banners. It uses an API, so banners can be implemented even in non-HTML interpreters. As far as I know, at the time of this writing QTads is the only X11 non-KDE interpreter with Tads 3 support! Wheee... :) Overview of changes: - Basic support for Tads 3 games. - Restructured source directory layout. QTads is now built from and in the distribution's root directory, not tads2/qt. 1.0 alpha 2 - 2003/03/14 ======================== This version was a private beta-testing release. QTads has got a homepage: http://members.lycos.co.uk/realnc/qtads/ This is now the main distribution point for new versions. Thanks to the betatesters (listed in the interpreter's "About" dialog), this version has many improvements over the previous one. The list below includes the most visible and/or useful changes. - Now compiles with GCC 2.95.x. Tested with 2.95.3. - Fixed a bug in the random-number generator. Games that make heavy use of random numbers should now behave as in most other interpreters. - Fixed a few bugs that allowed the user to modify game text. - The game window no longer performs a top-to-bottom scroll when exiting the configuration dialog. - The "Doublespace" option in the configuration dialog is now saved when quitting QTads. - "About" dialog. "No program should be without one! [sic]" - More menus as well as a toolbar have been added. QTads is now almost useable! ;) - Fixed a compilation problem in src/missing.cc. Some systems were unable to compile QTads because a system-header #include was missing. - Window position and size are now preserved between QTads sessions. If this doesn't work as expected, please report it (some window managers are broken, but I can include special code for them; if I know how they behave, that is). - Text rendering is now less noticeable. This eliminates the delay when switching to bold text and back. - The "Top" and "Bottom" margins are no more. Most people found them annoying. The rest (including me) considered them useless toys. - The DEBUG variable has been removed from the project file. To compile the debug version of QTads, the BUILD variable must be used. Like this: qmake BUILD=debug The default is "BUILD=release". - QTads now builds successfully on systems that lack the single-threaded Qt library. The project file now uses the threaded library by default, since that's the one KDE is using. If the threaded lib cannot be found, the single-threaded lib will be used. - The game window's context menu (also known as "right-click menu") has been removed completely. It didn't work anyway. I hope to fix this in the next version. Suggestions as what the menu should contain (or not) are welcome. - Improved configuration dialog design (more compact). This includes a fix that prevents the preview from growing too large when selecting huge font sizes. - Command editing (including history) optimized to the speed of light. This should lower QTads' system requirements a bit; try it on a P60 or similar. - QTads now prompts for a game when none has been specified in the command line. - A nicely formatted message is now shown when the current game ends. - It is no longer possible to close the application without the game being notified. Previously, it was possible that the TADS VM was still executing although the window has been closed, requiring the user to do a Ctrl+C or send a kill-signal. - It is no longer possible to choose a unit for the margin values. Values are now always in pixels. Being able to choose between "characters" and "pixels" was, more or less, a useless feature. - Command-line options have been removed. Since the default command-line options of the TADS VM were a bit wacky to handle, they have been removed completely. I'll implement native options in the next version. The only option still recognized is the game's filename. - Statusline: decreased the height, disabled text-selection, and changed appearance (a bit more flat). - Typing text while the mouse cursor is inside the game's text will hide the mouse cursor. Just move it to make it visible again. - Lots of other things not worth mentioning here. 1.0 alpha - 2003/02/01 ====================== This version was a private beta-testing release. qtads-2.1.7/QTads.icns000066400000000000000000003362701265017072300145450ustar00rootroot00000000000000icnsis32 Я##;ƀLJ:"̪8 Gn۩~;()XdoeνQB?YaۏU_ҽhGLe_̀E_A%MKoɾ]OSjҾc%1aܹxUS|÷^ L~ ȼ~yagV Ƕx e\eTͶ^fVFԚeE;y˻޷[V]ۦƟڰҕyɰȯs€Epڽ㾝ؿһxΥڶįֱj vռôh йphus[R + tl gn}xt}fǛgha޿qkzȡڃ؝XõȦ^Ⱥƞdʱ{kfyiie~qac`m^Ŷj\\ZfUZ yoxW= eeG=:3?D1+0=as8mk%|57[T{wz3^|il32 &FHcrŋX"2~/025̆<>/& D=4"‚Y:1':;-% O$ĞxVSiU׽t& (-(:H]RpnA8M5(gSYD@BHM88dc`OJ`Z^Q>2r[_cJqCb~gcP7'ZSUX_UA-0\[XJ,'PLLMPdYηUMC 2_hijky ɳ(qja5ERRQPOɔhQC2HII#εnH1)MNOOQwdzrO+SW`kvؔ|_"2ax,о݊rEHd~ư܋sP[ރڄM 8X ˺v.9c̻mB_r畩ؾ~,'3ց~ ׁ˽~~Ҁ nVT9 pN!΢~j3&8IG5# !9AFZ\q}íՇp`ރzRVܗvRMaqڃTj\U ١ٔmi_MA͂pg\Q;*[Ճۄ՝cWN>5OVɶαrQJRQQpwݯγZakg]+Н@ςrzs/rŌz˭yoˁvd`}o]pɄi]ĩzǓz[eހ ۾z܀2ȧdwԸy#ݾxڻzӁyOρ ܾ|Sހ1Լ}{}DZwߺyzʃڸty cՂ ʷ}vҵmWe̷~vȫgr؀ٺhpwŶh ~ǹ|nhh̻ }vsqos }w`BMuih^KRd{u\2 *JT]gjv{{c#ڗT86Г/c{ʏI؀͏:lm։z׌?t|y X݈EnalziZsn}r]E ʀR~zn^VP|}ްYBzDqktyzۜOsy^\:ʯu@־u>A}\4HvVþǧnʺڸnZǽsòĀj9gЬǠyQ4Gg=c?sD4XnKo~>;_˺ЊWlýȎ\$‡`C->[ǟ\OȩXrD3IaXCcT>^αTEiQ1ڻ}gJ20UQ:˸MػpV>+8TiM4ʴI5/C\uI1 ´F t_G2*>[F;Bs_L3)9NaxuA5>ä4J]o R8:כǭmK:- bMвsS9750+&,8L~W<3.)%';Tk/ +($!4Xnx,""IvNl8mkʩior /~l<Wh SS¡nnxwXYw#t'o'|p$it32+#$$&%%&$""&%  &*)('%"#$%#    !"0ARctm "8Obv}}{~u~}v}{~}}u}}}z}B( }~o'$q{z s~B'+)!+v}uy~!+& }r{k)++,-.'|t|X23--/-+({v}K4661/,+)%xy|C678:2++(&$r{}<9:<974)(%$" {|>:=;8752&%"!zx |?==:97431$!sv{F?=:874310" yyO=<976420.-  r{tZ=<975420-,+ t~i;<97431/-*)({z8<97421/,*)'% vt@:86320-+)'%$! j~[87421.,+(&%#" vwy36410-,)'&$" F420.,*'&%"  Luun/2/.+(&%#! uy|q ;//,)(&$!  ) Xvs)-*)&$#! "$(4+szG))&%" !"$%($,1(+1װ|%'%#!!#%&(19323.$!!ڃ ٱcGMQSGe#  #$&'-6>EJ0+'# #!( ۲bDINPQSSRa zD "%&(+4=DJHC5#!! !'>ݴ_AFJMNPPOPONMMPPGܾrv~%"%''*1;DKJE@=BGJKMMLMLKJHEE^zHMK`պswz|}|{nu ))/9BJMGB?:;=3&(*" '/(8>."ᴋ_;?DGHIJIJIHGFBAZtPIJ=ѵytx{|}|{xuvyZ(6?INJD@<;>>@C)+-. 230vAB='[8;ACEFGFGFEDB?>WqBGDuux{|}|{xtxKGOMGB==>@ACDF;.03( )83nȿCC9 y|{xty~NID?>@ACDEGHIL1451s4;/lKLJHBF@Nkg?A9vxg>>?BDEFHIKLMO>89; @>HŵHQOOMKE1>=?7>;cECEFHIKLNOPRRN:=?48D?qUUTRRPIX9;7o>:;)xqDJKLNOQRSUVWZ@@B?@H>¼S[YYWVURT%77.i472 WNPRRTVVXYZ[^NEGI1FDVÉ^_]][[YXOu141h+43GywOTVWYZ[\]^`a_FGF:0B@u]ca`_]][c 11*>01!̫n{^Y[\^^_abccbcDDC; 6@:\`abdeecb`V m+.+jg+.) ݼyuy{}{{u\_`bdedcba`aKAA@6<7c]__`abcefect ++("+*8ͭswz|}|zvstdeffdba``_^]Q>+ :8PU[\]^`acef]X&($k;'(޽zty{}|{xt|}uedba``_^\\[X9::0(64lyWZZ[[\^_``acc %$0d!%!tmwz|}|zvsz~`a``_]\[[ZY[9871*2.zSVXYZZ[\\]__aaW݁5!" ""zxt~ug_^][[ZZYXWW?442)/)OTTUVWXYZZ[\]^] GF }sZYWWVUB0)-&nQRST UVWXYZ[[]S' nT5 zXWVUTTSF-..((0MOPPQRSTTUVXYYWe_qW>#ubUTSRQP.K())!%$>HLMNNOOPPQRSSTUUVQ+uZ@% ~oQPPOONMLN#&& " FjIKLLMMNOOQPQSTP)! x\C'  3m |~KNNMLLK M&"""MRG HIJJKLLMLMOPPS u  SbE*  &aԁyULLKJHHGJ( !'0-YMRQPNMLKJJIJJKLLMF  VɁˮteGJ%KLNOQS8,.,.;7S[\\[[ZZXWVUTRQPNMLKJZ  JȬtuyz }vRUWXYZ[]D816ECSihggfeedccaa`_^]\\[YOT?{Ĩsvy{|}{zvh|_bcddefggiRCDD3B@Hjghiijihhf 2qrvz{||{zvsznjihghTAAC.99;b``ababcdeef[؁۾rwz|}|{yvsw~nbbaabaa`aN89:'113^Z[ Y׹swz|}|{yutt~sYZ [I022"+**YVUTTUTIӶ{tx{|}|{xuv}{SVUVW@)*+ &&"VQRRQRQPONIвxtx{|}|{xuw|NPQR S8%%' PNMLMLKLKKJIIHGN̯vux{|}|{xty{TLMMNMNNP1 JKKJKJJIHGHGFFEEDD:ɫtuy{|}|{xt{z\HIIJ KM*DHGFED CDDEDDEECŧsvy{|}|zwsxeCFG J#=FEDEFGHHGHFQrz{}}|zwsw~oCEDE I3GHIHIJ?szwrètust~xDHHG K+JIHHIJIJKJKKLMHѷswz{|}|x}DIHHIG "LIIJJKJJKLMNNLyƫxux{|}|{yvt}MJ >$OKLMNONOQRVXZ պsvy{|}|zxtz|VMLMLLM4PNMNOOPOP QSVY\`cgc̯ztx{|}|{yvsހ|^MNO-FOPQ SUX[^aeilorq¦tvy{|}|{xuxހ{bOR!6QRRQQRSUX[^`cgjmqsvy{|ѷswz{||{zwsހ1{gPQRQQT'VRRSTVX[]`cfiloquwy{~ƪuux{|}|{xut݀z~oVTTRSEYVY[]`begjmpruwy|~ջrvz{| zws}݀ z~u_^\ZY6 S`adfhkmqsuwy{~ɯxtx{|}|{yvsۀ ߿y~yigdbd0$" (*,Mijmoqtvxz|~}ـr{|}|zxtyۀy~|rqmlk-/,'057Atruwx{|~{ހ ovr ۀ x~}yxvu`:97*5?B@ryz|~xހھڀ x}~}{|RECA"2HHJb~{yހɲswvڀx}~~JLJ?KIORT||~|݀Һtvy{|}}|xڀ x}~oTSR:DVXX~}ހ"«yuxz|}}|{yvuۀ w}}b\YT2^^`p~~ހ&ʳswz{|}|{ywt w}}ab_L%Vdge~ހ һuvy{|}}|zxt| w}}pihh<Glhdrހìzuxz|}}|{yvv؀ x}}_dg[\db\|݀ ˴twy{|}|{ywt׀ x}}k_ae@#)Fa^\m~xހ!Ӽvvy{|}||zxt~׀ y}~U\^U V[YS }ys݀"Ĭ|txz|}}|{xuwր y}cUX[> %AYUS]}zuo܀swy{|}|{ywt րڶz~}KRTNMQPIt |ytqj܀z|zwt ׀ ۮ{~wINS6 $0PLMO |yuqmeڀċưnր ڦ|~uKMBO?ONNh|xtplh_ڀ ̷uvy{|}zր ۛ|~pMNMPPL|xuqmheZـ һyux{|}}|{xuzրܓ|~kP;)(:RQPR|xuqnifaV؀#¬~txz||}|{yvu܊}hHGQPN\~|xurmjgc^V΀ DZtwy{|}|{zwsր݀}c,#%MOONp|xurnjfc__Vր"̶vvy{|}}|zxu{܁x}P4*0SRNE{{yurnkgd__a\ һzux{|}}|{yvwՀv~~p=>NID@zyusolhd``bbr܀¬twz||}|{ywt Ҁx}}?D?;9xrokid`abccف$Ȳtwy{|}||zxuҀֶz}}#;:615ukjebabdfd׀٠wvy{|}}|{xvyӀצ|}~}#41,%2qfcbdegheՀt}}|{ywu Ҁؕ|}~ ,'".kcdfghjeՀptӀن}#ifgijkd ҃w}~~hjklmmہ  щx~}}hn؀ ҌԻy} ZtnԀ Аح{}} CtӀ ЀяǴ}}~}  #!&Ӂ А˺wxz||}~}}"#%)߁ ϐоywz{|}}~}}~}}~~!$$Vـ πЏò}wy{|}~}~}}~}"#|Ԁ ΐƶvxz|}~kc}~~~#р ΀Ϗ̺wxz||}~~wiZI7'}т ΐѿ{wy{|}~~x]SJDA=9, *ς͑ijvy{|}}~~zvsrwh6+?JOLG9!#πΏɸwxz||}~~}{wslfellG#(:EOG2'ͼyxz||}|}}zwusqrrK"!492 }wy{|}|{~}|bE%  Ĵvyz|}}~ucP8  ̀6ɸxxz||}~p`TOU^citwyxrkb[Q>/-*'$   :Ͻ{wz{|}}~}}~~p_L;*!&.48=AEJNTVURLGC?;73.*&!  vy{|}}~}&~~}}~$)-26:?CGKPUXWSNIEA<840+% qp{}~}~}~ '-29?CJQWYYSKF@93-'~~~}~*4)M9=7/.5;9::83*3=? 1)4657;<<=;:97456789975/.,i *%--02345667531.+5@M\iwX #")6DSbp~gg׃ױeh_hg߂`gehgh߂݀_gghg߀dgiPF ߀ ڒ oghb>>? ހ[ed܁ ^hiPA@A<Q `g^܀ ߳chj@@BGIHA݀qg[݀ehaCAEKRXK9 f]fhYIKKPYXVN*9 ܀ e_܀ugiTLRWZZXUSN17ڀ ݷbbvgiRRW^d]VUSQL36 ځ]eۀmgiSW^fdb]URPNK4:߁xexfiW]ffca_\QOMKI46ڀݡdaڀfh]fieca_\ZOLIHF0.׀^_؀ݔehflifda_\ZXMIGEB,*ـc؀ܰcgkkhfd`^\YWVJECB>&(]eր_gkkheb`^[YWTSFC@>9&׀ޗ^ngikheb_][YVTQPA>=;5 ׂփum׀ۙehjheb`\ZYVTQOL=;87.7ל_ր_gigdb^\YWTRPMLI8754'!SՁwghfc`^[XVTROMJIA431.!֛۩`րطahfb`][WURQOLJHG900.,ރؔ~Ӂpgea^\YVSQPLJHGDC0.02-/_ށ٤^܁_g`]ZYUSPNKJGECB@0469%+774րצ_cfZӁ݃g`\ZWTRNLKHEDBCE@69;: *IFmُ`fZWVSOMLHFCCEHJM==AG=7FEQJځ сױa]UTPNLIFDEGJLNPPCJMN-3POPӀӀ܈fSQOLIHFHJLOPRW\TOQPF6PHSJԁ¯҂jaNLJHGJKNQRV[^beQPQQ$/XUZԁӀ`WIHILOQRUZ_ceed]QSUH4 GP]Uhρ Կ ӱnZ_hс םeLLOQTUZ_cfgfiVXZZ ,c``ݫbւվ ̬i\`degfeUҀxeOTVY_dghgfghjlf[^`N8 2Vhakncρ ӽ韚ȧc\adfgfeb^_΂a`X_chjihijkmoqsacee kkiݚrsoԂ Ô^]aefgfea]aрԜedhkkjjkmoprsuwphilT Vtorzwvoҁɏށᐓcfda]cρhgmlklnprtuwyz|~knqeiwoܗ~~}|wҁ߁܀ڀ۪_΁Ѱajnoqsuwx{|~wsuxB4|̀zԁ م܁ځׂƇ́vfruwy{}x{|g1jڞЁ ځ؁׀ՁӀ ؋~ҁдak|}~}{{ҍӁzӁсЧ ˁne{[:سρ~~сρ́zׁϦcmp`חҁ r}}xπ́͆|}suVʁedu l|Һ́ xzwρˁɀƀƢwzvԮb]befdсЕet'r~zәЁjvusɁǀā¿ovu~v[`dfgfd_[zʀ_c~Y>{ýqroŁĀ€qrhհd]beffeb]fʁuf~{|{gZxuǥρȁeonvlolT`dfged_\|ˀΡdgyxxkatqɋˀwjkdfjjccb\hǀ`e}vuraql ́bgffgb ʀ |fu}pqr?cmhʀYcc_ wbdce Ȁ-˞eenmnLhhnˀ ]_\|hY[]_`_`YƁ"`fijjT9fdv~ˁQ\[m }iWWY[[\[YUYˁmfsdffVFb`|~ ʀcXYQiUSUWXXYWVSNLoȀ̑eg#ebbUJ`]||}}~́QTQiTNQSTUSRPKFdŀɬcf}~~}|~f^`UPc`z~}~ɀ GQQMMOPQPOMHB[ ā_gs~}~~iab\Ufcy~~́HMNMKJE?O{߽|\^bdp΁mgl ndebYjiu}GIHFB7!sfbdffgghg$hgddfioty~|vqlgc^TD:/ vjbdefgghg&hhgghi>O[jqu{~yshWL3b\eghghggh?JQRSRRQMF9DRU@4CFDGMPQQPPONLLNONKDCC2.m 9-87=@BEHJLOONMLKOSZ`flrw} ).204p垖IČ$㏁ ǔ]Bc ʖE@4 !$^ZX ͙  G^]^To Н  ލgd^`dgbZѦ $͎K,f_djpxfN԰2 Ž5Aipywwl:P־N 򷎀'QlpvzzxvslENщi 򶍀(^qv}wusqjJM 䈀p򯌀mw~}uspnjJT暁*@򳋀*q~~|rpnjiKO湉S񿉀6w~|ypmjhfFD݄  ˇFy~|zwnjgec@?9tۄas~{ywukfdb]9=󽆀z󀀉_|yvsqfc`^X/;҂H   @}{yvsqoa_][S .&"񭅀I ~{xutpnk^[YXJ[ ҁt}zwurpmjgYWUT@9u)U|ywspoljhaUTRN37X񥂀f"~{xusqnkigeYQOMJ|:<z"n}zvtrolihecaPNQTIK^SNSv n1|yvspnlifdb_^RVXZ=ATUb}U z4vzxuqomjheb`ceaY\^[ AommUVVWVUJ ~(yusqmjiebbehil``fl\S hhxr|v^ ZytqokifddhjkmprjotuDLxwxy~w |~7zromjgegjmnqsy~zxyxhOtjzs}~z%y}~}}~1vmjffjmnqsx~yxxy3 E}y}|vs}|~o Pmhilortw}y{}jK er쓁{ |x߂ހ緅W# w|{ Lemprux|~+ =zx؁ڀ ~Lz' zuw|qO F{呒zvԁր֦vF  =nrzyB. 涙wxyЁҀ-u  Aqqyx|Ip w wޛwxś( Gwvx縦vwv}Ɂ K{swyZK[Gݫsvppv~&Ctuttuv^9乻ot l踽 ׻ jt|}Qrsrʸ ﳹrsuP2û€kr i쯵ԛf.jrx ĸ'ŇpqpðӯK IpqtAHŀ 4ɼhp e秭֝h.4ojpqh }Y׺҃opm򸨩򵨩K  Plopv%a$ƹʶhonnod!Т7llnpJݶvmnj > Rdnmmnoϰŧhml h˚ﺛiwlr+MƽZмmk lg𒘘 Ĭ hk mGºpϫ hkjo򲓕Ƴ3`kjjkd}Uèei jb܉ ȳqipNh'gihhgzʵfhk;q5bhggh^𳅈϶*ahggiQy~efe}~z̛jr\fgf]`fZ|~}zvƕg7 &{efl2 tdecz{zywsy`0 beh30_dZjq!뺉Z* .a^deG Ikbc_$䵄T" 2dXcdVr [^bah!ޮ}N 7h˼`bdH`^aX#بxH  =kͺ |_ag$ǁRƌ^`^%ТqB  ?p]`d+ :ĀV_2[˜k<  Ft۾Z_a8ł ǁȀˀt]_$Vėe6  IzྲྀV_`CɀȀ ʀˀ́ ̚Z]![鿐^/ N}ƴR^]]^LhҀ˿˅̀ ŽT]\fRԴ£{X\]VHփ¿ˀd[\O'U³!rP.  lZ[[\_,ʀ Ϳ\ʀŀ YZUO㵳bA WXZ`ǀɹȀUZYYW&دvT3*LlWY^! ĀöPYXc(ܶgG$ 8YzUX\(ļ|cWXJɗU8 $FgRWZ-U|UVVWN'帊]/ 2SsPVX3y yҘRUP#ңxJ A`NUW9j$'ԶNUR d7  -NnLUV<]KTR{ګ~Q$ HtѿJTU?QǾò ISRdٗl?  .Z箞HS TAG x΀XQRL @o٦FRSB?ŭ8ˀԀhPQD &TɣDQ RE1ŵWvNPE㯥hCP QE'`}MOE ǠuY< iBOPDϽׄƅKNEѨcF)EFN OENԀŎJMF ө mP4+HdKM F¼vŁÎIMEƥ)vZ>  !?ZwJLM Fť[ ƁGKDͳcG*5QmIJK E6ÖFKD«mQ4,Hd~ !#?[v꿝N6Ql=E F1,R^zƀzBD;l)Ib~nU:!j>D F-6ƀmAC 8꺖t[A'M>C E'G\;1K`AC6鵕{aG. .Y?CE#SnkVAB3ꮔ&gN4(AZr?BEcJ@AA@C먓 mT;! :Rkm?ADvwcfR;@?Q졒 rZB(2Jc|^>@DTjs3?=^욑!y_G/*B\tI>?@ķ5>;q!~fN5";Sl />86ʡ6=%9釐lS;#3Ld}3=>1aIЊ8< 5ى?),D[t5<=*cZq8;1̉%=Tl7;=#faX8:+羉 6Md|"ƣ^;:<"ef99::98鰉vѭiD*/3689:<qm*9887Zꠈ۷sO,-156799895 h߸/74v*ž|X3*/3567878/ h26"1̨b='-13567679*'mif3556+Ȃ~سlH(*.23565672,&  pe:4'~~|侘vQ-',0234545 672-'"2vH%4332Oɤ[6$*.023434563.(#}*2 /yԮe@#',/023234.)$ yՇ-1+߸pJ&%)-/0221233/*%   I=N/0#ĞzU,"'+-/0123/*&! ,Otҭ.0/0ϩ_9%*-.0 1121,'# Ef~ţ/ .FnC!#'+,./0,(#1PrƮm.-+-. //0,($  %Ii~,- ../-)%! Af|N,-)%" 1]ô}xwlV+,)&"" Lv|w}sW>*+)&#$0QsʿyiZ3 (()**)&# #)>XuοĿoa,L& &Up}vP('[vƼlD33Kk{eJyt8mk@*b{ĪnI)A\q$7K^qOG#)=PcuP:`L-!5:_Vp{ a;F, .G8 P%C,@$6%K2a6{'lABdEj'?PWE2c@a9jYb9mVixw=-t880OmrDz^B֡lJca*| 4DzAi9ʕ_(8~U \|t@)PvĎ^= 7!%5J\I- ic08{ jP ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2cOQ2R \ PXX`XX`XX`XXXPPXdKakadu-v5.2.1  xI,QUv ` F(~GiC :%w/ZTk7Vhx^ƒt5otîujif׊}(m[8jmO'8r$ ޡDo2IvsP<H٨>l<q2ho`fM 2?/J4>ڽQ%I2)\{=eV&;u2'ïqK1,,?@\6f${1։&EQu~ |Pf;[_ :`.3`UؘJ!!+w8}(b:X7jOOd( 0{fȹ4+ONSXa32]ܖ%2Ƭ#[ h$ ?Q*Pj}V{ s %}iڠI3ojiB2(M;Oh,Z@^&W=C$uEF6 HokW>ͭ /i3vS jnvюU O^/L1I -TjPS_bFdQF*6!T"}![}m^iz2L,s]Ʋ&N\o ,0BǤXs̟#3x?\$;|#/@XU;9Pz9XwQ?0mFb)8b|#? hHkjBGŁֵW*az/M,)_ cDiĸwL.wƳ,C?̀72;\+1 L!#Fnp9fQe-Y]փ S";5Sۡ+҃ة;$M? ˵ipT8>΁-Iyi[uZ 9wO?zEbɚ QN0onJhuC~*z&++~?ND\&l<ߖ-Յu0DKs{)j|AB&jj.Xf'8Fް9?V<tP2Gl3쪄otwu /~:t8:.$8wƪ^@Bx ـ і#]1Y $Bh=`& VMcq+:xQ8pBx#Ύks+!v%Q?#_xX:93S܅E70]iJ Qڳ,T{Ŀ"|4JI tbw6rXBT%wrh@e:Fϫ(/r.!`QJ/'LYMTWReݩTMUYTʵRw`ZTC|fX.ϧR+\.+FMm[Uro?ByI݇Pr qe:E_Ge׀1lKp~麉G!w:ՒL1(ŢI{ $]_@M ^1|9 w E7j;5%)S`^ns2^WDfPs1Bm; u::Zȩ5SW7Suxc`>zWoK B`K#8_*5dAn6j[@[*jU/' 8;7G,@|ƟuG M^0{8Z?hqGm,j :חa+[ Zʎ&|C$8Ctsr랞!YtCc$ @?Sl7>/ϲ 7T2.L=E4mB͡\x g"K3ֻv5%Z=% DD bnwgA|7m߀F<ѭ%*iVQj?3SqD[{ El̋Sn 4kj:ESA6l?2H9em]JDBNk2--:-"!w<~L,VfpOʃǰn."l弄z|BdMqVw ywKo &0F(v'Z+_eqjdJd븻_,*,,b3rQI z/I[[K >s#|a 1,E6),I| ྥ!?NW-, VmŤj=dP`{jvqan!Q H:5d/U/R|73[l$UWz*/Ƽk9,P[K(2Ntc} xX^m{$5* SwpWt8DQ +`S t|!S!#QC3nS[w½\i?Y PN1$jGoAso*h@t>ƈ~Ey>54E5AjZ5(eo+!YD}44Hpҷw) L 8v1.TIPz6r94G5%55H4)hriT Eh#)7$WB 9tgvygSg[ ͕,!h?7lC,ʍk0B2#$%՜Xw`(lxKh'S69y\\-)!ZGThY{ 7Dk?ΜA(yt?2 any[{8W=J,+v2gᬊ!NhFhf~ڥ;7Ͳv^Λni>bvUS{zQHv;oN|"ӂ)+씨e#f ,v* !1 ,k=h:R. c$gۤhA4;(&]~W}(,Wg,Οa[bRHnUhhu&O8zGF2G. 3cYr /cêp)wTPe1Z.lzB\8mjAdde ݝ𶈣 m8| 13 UygξjlX + Cr3= կpBV=rŷ/7 +>"!3> $e*hTsR2 s :7 ]r~IL r+vm^ k]rۿ(cfq-)uD9{-!g/u6Yy3֬fϺIӍ>gUavd;tYrx9]S mM.&Όe0qzpZ<Y%-Ve{h^aq ֤%>zSzA|j 0&9Xo3h^NW2Ԗ@}ΟNgk'w>F'Wy e3"g<,\ZсA0~Ū })a %}V k8GQ!S+p%|/rSPK 2嵺=חTr4ƨsB toNp}#?VFp{GDX6Q kmen5JP+![Ck,qyCݶ"~܇fz`GX'L:IqSZTk,jNOLDlAˁM8Z)G %lh5R=׵4-` `u<&(Da*Xj'N!Vu>̈HAFq,0T}aj'0j#]Y1 (#=liF(},`k 5jNzEJ2.%e? _&[ga-,'a\~[Ư;kP0Ӈ7rn. !BڕbLJ)XW%P8LkӑX<(Ywܶ)d6@}»z~9 AD9y3ڏQTxc,K=ݼ[ )z}#h^w)@0ze v7\ ʇxAV6? Ome5бTpd; Xd{,tE,xtjti]HdwlahGo*'lUzdekHvTú&taԚԸ"R{ i*k-INgC#<$i!unhq`{alYH`!c!]$Qfъk| /|(N5nE|:0z!ް݁ \$Lg^_VD㤹(='WsG/!PFGx`xG_A?}ZaCe-<4JrQ rӗR'RDuڨ cʦb0 qc&(+3{}NȪ|s#Nk/fZLx]P)*ja9vԺL"vgc?{A^-ٷFe _i44G1#u&1#`ݚĪTd&7'Ʌ.6r"gJ=~r.we9Ev<.d&2sP%h/.N"uANOI_pNJ9u+>^ vͰD1ɜ,]ZMNih0бٴò:)lL KbŰl=C*V#ƨ8Z6:^aɌy@~/lsMcik6)^Gl~Kr 1N χױc&Hokd;vA?Cj6$]]no5kQ*bYa GA/ݒdgG([_ܜ3=գ.X. hJ.ku{c[44}<+ws¢-# L9^ ,¥~GI 0)sV m/Va2yEr-nKWO`pj{s"ɫI!c9 U=+uLp" f%/{/, p?>ɦz 5om)O NJo4"w+?!i.'53򞷭븍|Qp]Kv9+B e1(fOl[GQκrfV#, 0rn /fKRUѫ:7 ajx*"(&V:9pDTܗyGWd->aNx6 2HlW6[^$DKNOS2i7#}X}dHtE B{(]=F?a 1I$P%y/5UfOi. XldY8% 5e=NmBYxBEap!✒Xu?rq w<`G] Kz+1 _-̨;w`;Qp&qu^CȉGyW_D}|7S?#g- 29#yύQ/44fMʛHf8k噦 S &oM ?nANB_'wZ3$U \*~ikz;y5.@U$;uy3̮KߋC)#֝=6H(MvT"/4TAMn;ҏQNv4nh?x&kkVz ˜/)JpzoI$v56 |Y_~,=^ziR-ѕg4݀̀b ~SPcgF=`)4AT:yaG#p~ i伌5;pd.iޥ5IbWi;[|YOq {M%G&B&n=hc I}N}ss3&Be !`f.rk< cVgmtpNl 4[W)X9geOeg.Km\Q``G& 1@c^`lhzm0pvZV)p;h9^)9=Iz$^M^x7TM:tӀZ8i!eP#rH8-A 6dRâ)X \un4A^Z´ ' /W;@ZI57Ͽ.dC2ff M.] u-FGk?ԗ Yr5h!yɱ+Sб(Dx>#pl0BשsQ.G SlA\1ԤJU{P0FYq0㫛ʌG6!.TJunX/} $Q\JarIQV2U&-A;D ؐ{+뽌 7i yL 8Տ#'gfS_ {fANPv>y#8,qly!ˠv3aWIcN0ӼexsVq㖑=Nܵ9a΄ Մ=br 8 |TAˇ_„LLvo+ A].'+sfkʪBm}eߜq׾sZY3xI|᳿Uִ``?xb=û ϓ֬sn@? ?O p8P_UMXNFprëY v$V'FI~#9 |fK~Qc^<<Zpu/"2|m8@A9!Fc!-tb^f:?*lUH;?.ޜT+ 7pW$Vy:ArY-^?P5DNnZaҝe2a(t 8Y>-}T79o3[WciyIv#`/r""TmIn4dxyPe>9\YTf| 7v-psP$)Pm Zf.NFT@}cw{ @LlXE> &*jw~y8,&Mc1/st4N&uA jg;pL!ӡ}r!y IR(/X9 g7nEEb,|A9/ք6B 2:{4t `'oy]LHQR%% SpΘB^:Ut5).[&|(3, &vh@0Xud(˂,Ie}Qj&~E!&^a.6o„J`&?+og}6S03&Ѻm3dtӽ$7Q^CSn|_q_h]SC+KscDIOt;_Q ( }(?n 8>"PɨY'a|zVi0TT E'CUɦ44%@HmюkYI8zd_dנ$5TGCQH1QDyBvؕW-ff-[h,)\\OAݹ m]/J"#dq: IlW¨LJHu RapL:ZMP`J/Lk8YI,X@NcL|5>QE[{8Dy^2!Ⱥ&#.:yVATA~u<,%|QfMsEf>35g/W]" YMC;] a_4:zMv$ap(/h hȫT&F5 YQ'#^:?cHݾx)Ynsp8L/ 6ߌGsQ8D<> t.Q;x8Q}CwAZNn*0Vo,Q͏G:3(0o|CfSVPv=b.Ls=fk$7-{b18rf44f8>9l)pVp:PtPx`c(穘߆3NKle֡QV,LrTYz]%D>*6 G\p5#v+N3TvGUO_s'4%94?gwLw3o%O5Ѿ$$pL޻KnHPf 糓nR:2K/Ll085:R/#|Lީ[ 4aׂKtii`d;HM4CUc#[y}MdX|;B9rKIE_rwVgj5U<{5qeQ5:qT?o6V oAy_jbI oy(.zk}BjhzRյtw ,y 3 ]zH}?5X7OÞq-|5f,ot Za9#srlJešqHg#yliDK0ZUR-='O3_ hv45g}eʃT=(V۹a#7htX{ݩs'zG|噩u&x)#GE*6n ;Cw̹?K{my~VaZDHOɧ5M 'P}wN@giFP--}tPk>Ģ"u0R2P6 p,- ȍ 襜:⊾ʹND; ",HDQj7#HtkEZد:~ ۠Rxy뙤]us"@3KZO*y{"v< |d_6N'#OhzPTXDG৳_dsSRv/ietR.nOw&GU k%ܨ!gg4\6eyb&+N^u[5;Cyv7+o.͉(۝V|MGƃibFN۪,+7jm.S W@,+}CЧOwLKycZ4OJ0z481_!jzah6EJAI7) ˯spz蜳N@a=r8gCcTQj#qE8Jpd-Cݯ̗P߇]?q+c< \#-`e"%]*1V&֛ҾE?vD<<(Ib;./EmR4B=('vY^Ψ>e~yCq:~fȭK=!Ы|&;vOYEޅԌ!l7iE.Sr:K21J`Hu`E/ sC 6~VO#~2Bf ^BME+vR|bXfsDR..UԋI U=$!X2e)(ȓV2tZ/.,z9M Tda-讫]ʝ2@Gbji;*~5 bLQ-'Uk76Ì(|P zV ފ꯫qDz9wC/ Xm0ܜ<1%t8@.8lb,C?|zwΠxn?K18jX~ÐxIt[ƨޯښu&m%{3֪kf6$ܘ-`cCAu2e9aN#J^ Xy>p rfţ?)0<|{p myIV6飓 ƚ߽,\$iYBbqqL4\NmVg'*&73Fr&'ќOr}|/*eb,EHr$/|O&  1_o$. (WK[ 0>o9x oW݈ػY@OIʵü!@'TǑ,%8l&1B 7qn^>Y+[g^y fNgy:0଻B;vډ3AJaMwF1sOϺ0z5N?4157{,B-)$,4j]:喇a7MhAJMw:2K ޲-m_7n; ^# S[I׀NBXzn 9_A%[k*QR.Ze٢ )NYSɿPQ>{ڔ[4Hyo9o[Ze %~v +ٝUS(%;WkQogYއmatl{9 v7F9s6@ScX4Ht! %WT;Iֿpå7p3Ryl[b.CI4,~+Њ'i5!b)b;I.ȣI/q#Xg% }CF3JN,xlfm5 m՛4)ؙG"GիZx3_n@QoiY=dU/1F;ਗ%WJ'yS#]]a ‡T[hYD)[Xe}EV!ԑ&ְ:y++~9 mt5t 63@1/[/V0@\ BPKə)Df+.q\/?eϳdX؈;};1XAD_3BlASJ^˾d6gԻځUn}"ߝ46kS}fFG$qvPC ϸ\UZLgeY8s1p*Հ -bRZo8^) kî'% 2X} eWC]o GpYV_Ә)+NPȳhя\ؽv&Bo8wG>QʾQVX~yƻ q^芐 =ʅ;vk/U~h@Sh"#k}hEJ3i%2fWNĥU!~$!06@q9iX%@ M-Q|H E^4əhTFsGH2ZvqyՉۃ g(GioTi!$Z޸G( 0]{[~7?9w9M2Lf<o># #!GfL*N4pZWz֢IC~[DA!`RM:Tx 1A/}&@͒ q:_ؖ48&bWgD2?Z6(⛣vڵy -7/ٹ؃E'ޭW͒ͨtRE0ƶ=\72td6rA<X jR+i ;oeSDW?%(4vpE3*y18ϢbFk*=i3DIDYy$2}"0D!hU2WNi$VN "KE_d=VT;z{hzb UlpXoHY[$s }ۻKYtρ->A0}Vu. + h vSӝτYʹ@= S|*Ql2%867KlPz-TR>ܴ[ᡔh0"C=HSR@wuO.f~Nnړ{eLşP1k?e7# ӹZDw~ 5ЄCFmSGБb÷L79.{.\/B#)n (.Zl_u!oX=$;ƒ e0D+,r @Sgd/gP-V tu tlv;BnR O|5LҀO\PPQ[$Ll-]zkR&TTdrW(c3f9@j cAblvK"7{0g(ݖ‹tɽw5N&B}vK^s a7b}4 'I}/{>K&Bxҥ1H\tm4(̌he OsO)Q56^I;^h 8@ApMaWSOPBx!w|ϛT|ݨN}6#IhZ,.C.P9~JLf-:I3`缒>ِ7Hk!X\I6[ɝ1Ws1ѳ߼ĶӸ%4@& tkoSekb**g IOq-? 6sNLAQ4 H?2 `=ސ|?TwO>_V)b/+<3aо҂$y%䷧6l*4G4OHǔہ`,n">R5\{.ŷ p܉uO[=/tZ!rlzEtMeg˯|Xftu芕u\mc=W*:h'V--JIcmat}+̨Bh/ E63U玢Lo־l- O__fN/l=5 #{nвvbSr aRGkU6WC+M@ecɆgF^Z0U䪁#0qre EHSƴ' R ]uQJS9L8-$Biu\h.?Of0F͒P]QD'&arC2wY>Oy- /4<5Cc#NI֢5u /;=$"}ѷKԯܥ PpX~@+rl_B;I]KTinӺ7G17PM0gfU~% p[!ќGwQpߗչt:مmhBOEr'i[,~$[~!IIgr- &*Yh.=H%8`OVu5FY Xw i$+S-Qv#uK^ԏꄰGm|{۝/X cڞ33gUEp]@t7Aд3D8h[|F#" `%6G&Ӹ۽/h]!sIޅHoo$daؕچX.h· 6U/hjPK”ٚKH &Ƕ,Ph \eo 0-Z)iܸuj>Jpn2[}т%LoY0J ʾ)ԓ/&ؗI%8#hK]:z7e,g!^=@ p7~Ʌ%b!EP䙰HzE@wsGp ?A1MqÙ'ygQڤ]J"5PwBu ئ hhDhqQ)jPѩ̔3oId0GxUkM-?jRŒ?V+z/[$'7F5㞘9>%|uBօ2 I"ʸ=mw`tXee_.bGs^W `AB.cطr*?Ytp0'^6 Nh7.QBt 1k6.ܔud׆nLdkaPi Ϩ  `qj%!2b 쇼|m6f8U0BNS>CR~S#V3_Ü7*Upʳ Treg\wDS-縎HɛLFJѕL6n6ƪz6H+.TӘ,+OD3Y@ϔn˛S)H"3>;qU2~^KxZ|N7QXCOtצYE$=g!2iVNNY}5([9_>BH+ xD؈4E6> ~%e(ɨdp{wQÖ 6U;zBn.V!0Mо`/z?^{*ʕ#7C3@߅&wMcm̉e\x\0 G´}7,o$k@B:<4:7H&ȴ i-muv5jp!^E,[ _gy%, e:,K\Ft)A0fuݨI@?:xYaz38ʢEO&\oUṷo#!:jTh২H}3`i$cm&H*jd`EU~ĦFQk'JK076#L50\w{]}WqB{۴xݜpW4Mv"P%+F5OF d~k|=DW>zIi7Xyuh\RO#}lfJ,y#f'A ;"G:ߌ6,/;"6bgH '<-Z^ZL'Wǣ!"EHؚf6!4k|R yBZl|PƗWP7f#yf7O[jGB1wj轞kàK|пK}SRT۶CSLm.=]bXlWִ!nnڑ.VLiBwȷ+HHHpQSL$/r;]ρT'9 _"k2 GbF}11ڠSk}"ڿ\AmϏx7OO(۩p92k#``仞Di=T-s5_%*ӕ $2~PY40AG4րΎNy^-#=Wls+_aݭUɱ/0C4#/^ϏY(W~ 'oA?[+ E4iXq _6 d)2jBRkc4BJ$b!1[SZrBgWޢhs M){h]%6 ɤF@M. .瞭2_ρqw <,Y83g1jG'W)=Yz8ը𺼒l/ݲ^Xئ$rQs_f_FF j8E2 >zX8'7vW,k1 6[p )FK j*5gh@-g9;0 T*b.E, S'6xj6WqQd-Cvqa6 ? cQX%eO@RZF*;}gޥGc45J qw\vqrXhSȷ;_,V1ab}Ĵں kWW8VG]oW֌ n֣ BY~- JG!vmS*bHYdHɝ v \I+h$e4 !tHtt}T[Nb5i]ѭb96wtVGGk[tC&;C.jg2]g9)pō_xT>y.FסۃY ,UK.M8sJk|!crvʹ{7E~ev˼& h #vr\Ԯui 쒷!U]G"@pn>i`%itr_+AF|tV3w4ӛ 21`3A %U1Ս>FdCmi6T ^56,k7JDGͩJBHsUk-?@0y&=c^JN?|8 pߍm LZmm2('r/eTH}$Im1w鎹cOt*_FA-{[]=LB9 A=,[/NG֟Az'_1,]w6jW/\!Mh SmwNn٩Htj\J-:[8дȐxV?$GpAv)R˯0XI)6Ý(dq5L1&n)%l7]Gw U{t#?{vg!F>ۣƮp Ѱkix*|l jH-|՟ys傽F>UُJ,_KYM:xĦh&Ʌg9t fQknyY|y{~HV{sz5Ѯ}&;/{<ť{?D}IoգW5oPɋ誝\ўݡD$ɅQ fs"$zjhؕ% eɱ|(@JTIsV~̈VٹRYIBJ7_H{y:7wD|@ gd\D4NUStp_ye}՚U0;>ɭ@dj*_4f܁Z/\ەܦnͰT7?Hʴ$glbL7iZKs-ehM73P؉L:mOk,bR͇革v~r{ ĥj7C+-8ag [hĖPA/*ۯRV2wvœ"1r\j Ypc _({0ĭTإP̾ϙΫi%XEWBc7*TuP63xh1<痖'8% qךn2wawlе/t;R(qį?k\~СôKԢ@όX2 CC=IA݂\M+ pFk'^$y^.Ϙc},~o_MKd޺\a15Ū8.vJ=0i3" e*xyܖ=E[2y) xoH5ȘylM2lP<|$fne6eHDEtIH-M18ii%xaߧWX/^bȼی*|<{(N#=:b`Ld:<;0!i吔 tD?fD0eu_JL:h+£%u3I}?!Hv^w 3dY_тř6Ǝq"KY!/A1M&>d:YF`H]\5&$̂,>f)pY;[6ovDMkiP4|0y%)3idk5rVu`@QAVS8W5+BhU:"47J>S.*0ٽU4Ju$ |Sne@/27al^U#) ƉЀxk͞s ҀQ ,76Ki?i<~[ ԅ[ZL(.e8J|ȺevYF Tp깛HDž- >İ=Şzy pGvJcamؼTH0-pT!`q>G(X9C -[3z =qn hըJ\b06!>ٽC't*埼\jwKƝYhzb5ӥK,4:x7ډJaMN?tT(Ee%,>HI@l]<뮍a@5kt ݷ_fV3#l$~^gm09Eb!8O; T Q`"gCIj ֟ΎNͷv}>聑&y8 ,*VK2L#y0w ݜ(_۹[ŀ| )RkSv4|2/J:r 4uR >UmY_tx*uG.Μe?;`d&x%^'hz&Ro wߟѷ{fNEBSL2jɜO IfdHC{9Whd%ף Qn٥ Q ԧ%b}vug{}{gtx?;6bgRP +Az~Pr- VN?:5m#طsAw&S˱N^6ɍZ Ȥ 8dԋ[ [$OFʾ XȿȬģeQ>Mr60=x*i\Ɛxz;oicm1оyFiI(Kg^ K3RpU1cvOj!fr1&_L&ROj3[d⺦O0n@g]:3R/hw1H/jP22[}UԆL3pc] J1-"+rOYW.v쿅[U H1Aѫ49s\Dd&R#t뭅r_#!l`tL\EIFYDPsLΝVUQ >1W#UImP۳-2$茶H7؎$I(2}LrXRj57$A\%12lury3F+~r}գ7&7/r MDJyakC>Mڷr^2#ZcJ"u`Lqks+=> >֛i SAϐ(isyʖG̀+l7da =BtctHSIn?J+=SSbl?*F3h!eѿ_a?ZSMGlfB2~{upA/a͇,_]YձR|lyլ <E, }9AG%2xYI{XխFH>#;K6k]"ϵO6Ep:&,-YFh:ג/`ח̆C'r.YiA(\;J-P+n-{ {R2I纋di\ЇdGηph ۙ1O(QyY@1BB&(|0bY~Af pmCj pk;07ai :1US/ǟo:nh ʉ^/Jǀ7Xd6p|p? o)az^7C#5`޷6cFS1P {o#b Zݗ`^5pp&Qb?zuE"~̀T3lg/J C+p-Yxp/ ;*+Dܷۙ_yitgTަ̇QpKY w>9 \:yX݁ JrFؾJQ"$wWRNd bqm[AE*2~%ܷ(hssDbrlXxڥD#v*mOb687v7M؃ܵ4kg-f/2at;HjqθstGıL]z+ЀxM7j_,{ϗg{SAyf RP>ѡh8A/TtSgՊ,#S.>ZlaiMS&*<_(NBgA藓bYh#;!yg65oϠSKJ),ir +2_^<:v+{^K>Ր>T#7ߓbz$~Zzn <sW'"U ig٠U*v?Z3ià 1A"/q>orF^:* 9HjnQ Vd7d)*uq(n5 ٖr &\ᨭեH"Ȱ8X(cq^iRdOOu/o:Hp@fNrƐl^knͬZPr*s2*;O}vH>Ƿ8y҈_KޟuXHvW+'G%" oWaw:cɉI]KC^G {xV:ꠥNds %$?e PٲxUŒdG$ҤY^)}~fSAsA1fm m{)ˑNP3kE⤮"N. "+F}EGsjm]# 1RFjJ螝U VdvkT.0JX|4;J;2!SAթcr[\GRsb`ngFK*_`i#BW%Ts`I e56W7ANY6  5ZEocF&˂78P`6-$j坞}9VdRgIe|Vt %}3իdkyT) L]|>Wͻ< p0_ Du+dKi &B"q ʺJDi ¤/>a eC9bo,o` K_ǗYQ4\v:N0t}WqʗԊM)@>wUBLnSSWaDΛFXx%u5Dc-9i}X a' ;G7%uNAz xl;KKask3 Ӗ,@1 K!G&_s?ily"-Z2G8fwKYO 7:EuBN=o\+hзIqzdD'ժgEj.ec׵ywG]|*sŕ{Mbu9S>- gOCzj)v32T4WU&Pt~9@sJ4ȹQ-{:oڄ8K oCfI4zo8=ΰR2iij%R wFza*l6IAQfƱ LȠ`.{=ݑ+zhuCA̳ 3U6IkovSWpJHRC(kH meiS7 Sг076(ˆgӢ譋sAmJxI"Qa%jyMXMaBj`iB#粙M2~gwN(E@^Oh P&f8H¸;;m\꽸 zH<|3 ssG Hqna%xN}w*t>fF0'qƝu06 0@;"u{"u'd S/z $ ^l5|- EZiKa"鯾*l.{ٹb,77-R jx#pxb($cҙ4jݴӇ%pyr‘ ^e;f"\vG;̢`g#D#@_R[!CG&g[CKplVןoNt@a4bԀsjEq(ilrCiXU^_RB]HgQ~(%jpl ](` d?4/XJSXu>\/B8 )+'[\ÐCXzlu:^Ow 74o;2_T@MD0||esKYe5=1@[뾍 @oWqtB]v&`VR(xp/g^Q"^lG+zHPFk;#5g wVBo[1\iQKߵj,]6bfbfx"re0 CS*ze PYM pd|Ґ'WqmU#AdoLlʗ(L(Q< ~(CdR~W9.a0@퇤Cc݀bVCWy4ZL$M)]4;:BfИ&0%kxWTD?5'm~,_?: {c\t"R=Ћpl/T/ø. ͚#Ҧ¢ZsĮ坐`96>Z5S(y3SpN')P4 9N'~'6QVۺ#cW M@Vڈʐ! f+V xO̞#Y,Ӯ Ck_$w[k3t \T_sM{ S'R_f&qaվvn]A$E˪u1iNOQl}v}BHͼ JR 4jOuy7rCK`R!;taAC7~5tNg-CTATv8 ^•w3ar{ዯ浫UR=(ɞDpA:0Ӽ8C.\$WуʓﳓQ~ 5M%۱TJs7 g5[J=0I3VEJ&P|;hSqx.kq!ܰ~)zP}Y_aV[,AAS5[SX\ľvyBStj1Ke xbqSh.#̐7"')`\eGFFC_1nsΆӯ~pf [y`_ b"([Ä- FU>{7ɢ8BrHf"yG(L_B!\ujO:Fnj<Ӈ>vџvMkn04T7;*Nn+ G ( P'D+qy&i=h)Oz2:f#[to5Gq-bUnUmC(޼Jh_Kq?75l!Lorw#_Z cV~ ۛCSqr>K_ZcPWiF6 _>9T9^DhZm,W͂H*(C*rC唐6%6 @= ,lݱ <2: Qi}[,GdĒ&!y`766@ĆO>):2E bD837 aj*[tu^?g18!h&+egCBʕ&pA Qk BSܘod"Fy-Mvz0M`3١5nw9v0 A'*-P^dL݋&a*EnQy0OiF.%1n/(TSN> bʳu/b5Wl{p -+ q{5PCV7@>^~&rjH{ w>xh|?fPo!I&ĭ@}-į5V`t8e?-:ya UIHI0',xLFύӴmS`(ॢ^ER|]4 wkm &xc*eLD=P?S48&t2ΠFʳcRes "Կj~qނe`XNa'06?=>'9O,o1ȨUmX)3 pΉ$1\b^ߝlX 0Bp|=9"QM2-W'B/0 ݟLG.,![{#/*{UI/kZr~@LGbaU]AN<Vn67 eݟrכ[圥g.i)K~2HrPt[k1P%0N9sAY ?̱Δ5] PHxEʼPS+r @^GHVpw U$.Osdn),'. Rn\&!C z?18vkRIl]NGލܩri?ZV`N˹-kty˄ScW6]:uVWOrhbpDg,Z:HgBEꄂ"BBfʮ,ɝfKԤZsɞ<^lǁŲĕ.KcgG v/j7zn,byVC#G" ̬fFqGg񍭽7#o6S(ks˳nK3XIHWTkxdyTCv >L)zߙ`*7Pر#Ys16cto>D6p+b$3V[ُ'GO2%Qe[d.SIt ^r ~+dMHnȍJ.5 jwL:f0Kʪt @sN*=Nv+$4I{±ܔ*Ul:9㹓/@HyO^hkk 2.>-*BWc_wW[li^8M({Y!DID r#v( 튮&A}]~㯫_Wzo8A\unUheoy}\fg[Xq_X‹ B i5+@#& E8~oD7Itκ".8t=NU`,]JUJJ:qXbTٗ^x7>[MRJaQU@f5Zpm(g1 1_cGUo2mܦxcZz4Fh!AqCq<1[jbdц!D6NIR6.nAݡLt J$AbqdY}C8@&d窇3C;& |U _P"$#Xa{fvRގpخ-5l[??` h@YLx)w*_QY&5Fd S2m;X2^O t2&Y[K4jB~е8IEΑmѝS<\I*goƍWwp?d)-ұjdžx[ؤ"(eM{dUZKQzs "u!$ئ*hhޑa`w;Z#~ȖƑ%_v|uN7mIG{ `אEȘ?z;_8WsU_̻tl&VN-`r''T#K, \BP ()plƐLԪ #nE]V6_]t3xܱiv:2)1 owv†@mdHڀwTl#H'qgЩh>h(FHEU6sYZdpx5&ȾBxqLd[Q*>d< y&iȫA^Hw|PLov # a|FahLi?=u=jSnp_1Y][w(gC 1:Ԁ O^8?l"Ux!fq3aZTJSA-LRWoTTWһOGn(4)g4 o,  m[NkP{]M_K>q+jjErH}IRg!Q68w \x$A,As.L}ǃ#gWMk0`bƕ~|}$+_Wy Zđ1_+v(|.kTM[7 7MN.yXV q7"u1|H%n@ :qs+P؜3bA [{QM6nJ^ nE A=^3dG&E+Ӣw llI-0,{]? mю7K@hMϖt(D,)VR Houy3p݁Պ_"LuODLP23O#$$a 7BIfK-_VV@4̮Pڜ>g\ $}̀xB *?8>n1T3Tq- T'i@򉃟d'93h2 #~ߕ%hAGKiF)s:D$uWj-֠]n*`8FaI `7p4lTq|q ܲvriW_~(4sN~?,k-mgiVo7 0ZR5@}Au.ޓ1TV2%u^'$LrF-נJCxH.|+KE2E~ e)'5p̥^*ߠc& 4]Xk7V_BXGiHx-Y33}5^щ+8ށo;DitJKh ?5,0.pB花V9"Vn<_ˈ0Q`رPmF }zQmD{@kM( RK'9VCGw՚JQ'" r]Vx<$r5ÞoLGOiQ䏜?lv~P",{j7Ha$Cbe͌IDwN6&Y_r 2u%4&=9lBXبen3n_ Сԁ;3ϱFH8ςF0^K0 D CrJ,1wTWؠ9&-oXg+uJFv%LD VR^uRmN"|¥hyֱR`'"O uI .t5+#|Z7G<43|Vɣgy=72ghpE.^bHO%9 ^XaIzLDUD135TKpW2EG -86ܾ'jû)5YE!俘 mI;HhngQFʓk )tNsKʬLA.,5j Wk05 0|^5_WA27*J?BByb- K776 mɟMf^%@^,I:6}q޶c[QY*?)qG0a?K/Cw+dZqnۻ3%4GE~`Sɫ'ձDU,,8 s==P\(пZW~Ck /I7ZHAH""#]{&Bk+sd(%8 zLſj?a Ŧ[]p `W p-KTHH&gj}I`F2=afT 6:Fbys$"Kpv%'%~!l:Hq򸸉VM{{3y p;Bk$P7f(dN$4sPZvU>q32kf zhQ9ikAE!E "{R9 ⓐ4iC¼9EBhXG=cQ 0 `¤/; mxE50+\FڿtԊ{Y Ee1&A [iXR 1p*ZR !LyhF+XQO5?}vⷩQ@*%tU5N{vB=ن,]t -0&Zom Qm0fx *gJ 6C(?!|uǘOsd\ᑝ:ӚO6`>ƘmǢ'WYK4Z+=!ÿ4q6ے6珸wdzXOY a+/o%WI%43s^uD2B Fpm-~P ~HTbN_$ `dqb03-:)$,eb3v#[ Qm/֧,L[?W ue HϷ4c@`&4ظk v YHM޻_md)=bq <3_{HgAV^`͛H"u=lT%.t.{nヒ5"\znq"%PEaT.ѫdF۞}pW8D*y@>Љ& _ ;`9kR䌓@O <NmJs- Z)Bi+@(hc^RRc?uoE |vk ~s84qSuJ١.@/eB{mP`X(  AnRArw]#`OsZXsZ~R3]T&Iδ"QZa*Ol ”`MUm ޘ3֢25' ڝW` m2O0Dܯ4lJ-؅"G6o i9N\Zѧd7:Bǯ_;&"yYdQtخIrJņ eҩ=)i"w"4ҕ*N_SZҬ~- b}=l 8ː=Wֽ=CnzˮwcA(D]/PPB Fc|hH>`< uj$Z6y11:bsi!A4uwu[<>OV'6 :Z=h. P^uG3z|&bm"$u-TP]&FLd2/:ͭn:Z?=$eVo 8t+HJA|2=(|7_j)ƻ? HA(YF:bv(x-eUJ18 uUWcמ31q/P4z Y2PS*悕 @/"܊35 hYrf\ױmj!vIORl/[_Y؃;ۥ$OdQ=qtYQʦPv,w AS,sᲆzv<1PY^BH 9!ȓA@51`4ֶfgϞjo;–=m szQ˂Gp( NOdo׈,Bp# 5F.ڶh&6='7|QMiʅgʙ9NQu tadAXroQ.xS[N_=Kl8 ,*+A݋???9$aԻXٖ!k3pH!E[8,V? qcqr':e J`~W +)Ri\bTED/5a C]s)i'2(08K׊"4u&N2}K;T}Ynr6DMatx= l@8z$QHLN\6eLq|ϭM+gF6cfSP*[u!a|H {_M+VEjՉ0o)"QgfCNd|&&Wp&iH}q pя Yo@.eM\ 0p;;'H,+J2'P+^BneLHEVVocN~vY! '(ZC7Ď}-DYg kə)eT#ɬ7(kNԠ7 Xڳpg.P)ۺC%c[LBЦL1өӑ %}G ^|=p& PpHSRG5PYXS츽zCw٩DM0^NC|#PptN6yEƀ5 Bn!RUtY9lA(æWo_Ni1· Da\,PSR瀏xdpoVY A* Mk 16 ;m3ګ{љVY&NdnVBf٣P ;k/xe'(uRc O-{7'Q);|'xumgJx0"]}7{?Wt $?oc?|_WuwUw{,|ůt 0q!x(3]T'Xk2t\D;D> IXv+ 2]7VĒ>_EVֆ!7POX[+R#v1B`CG*.J03k_d+%g‴'L\!!&Jk^FGr񗧤Bg$jQ|i&LpÂI3;^bF[xy!&7B: QŨo}gh݄7u_w˯ْtW(-]p'n(|>w(R{"Ƒ@_ d d<d[^;ʡpBThPey\[2&oiIǷ5s*@;C{T(Ȧ9KBI.̰Bd)?#˛"NLQv'ĸhHRTŮQ݃F*Bh@y sJ,ّc.)Aq"s ogVSxJLŠ[#NO61bS`Ց)Jl"CLXCn_^J_m6KnH?4:#2r,088[ZqgTjpHс0[oqY ]4E{wgzȰ ^#w=~@C9 P+VJ 5dG}h=i_9WC衍AJwMAΔy~*% xo0$AcwVAqPf< FN41&"9i"^rݖ'[u,̛ح9c4d=w^'JOJ7Jl+Nz a[" HtoxWML޴rʰ7[N u'OYC:dABmVI}ֻݖ9ygޙS\!$#jGu]E .X%%\7?o>wЬB$¢C7":L"=?ܿyk㜒D1 VKKœHcη f%DÑ֯s Lwt(W27VL tx8;ļ˫,)KZ r/2톳WeYt^ ~0S|$ڨT=v/6 N( "fQ[ŵrQ̀*v2yntc| Kk88Y :b1^*\ 60aYd j )5:I[_`6e`oU,;,71ՌaD"ՐqvO,ϑ8Xi^up5EЛu`y_Z))J1}3?I޴Bâ#ׯ.}1')1&Ԍ`~g%u [|.(EgJGڗ<'[duú}8qZnM̶ ܮjuܲѐB(4>K@Y+c ojp`fl!][şT\ċ ;DÚ0=_SW Ɛ9w*J J8Mb{##=c(3g排63?_>[(^IF{ȟz/Y'5R"XK!:zY攄5QKqM-ђ<Uij(.pX / PXY uTeX~MM>VG>ޕ~nBK{˙Y Ё7- _mw{!uc@& Zh{Ҙ2q(3Ɍ+.h)kҁB^#tXT6+P[;Do3M9D'YGx,Qs6@K2T>╰&޵y} G-YbLGi R5BMgkhyxo[.ڞD90HEdTtxlCb(Y^a' ;o2kt,l8yV쨀;?%[y{[{M|jhp',P0@.v,%h,ۓTIBѲ%RA]0/~y!Ae d^>TyS[:l*|ݑpPv/(TbS*FAEM^\-)[<RYJdr K xfoqWpa8od6_h\nOPC_2"1;āT}l- ~ f͈o+S뒹aힴ^HvM-Bj]{Bh{MrjHK-zTaWmPKplJJkɕY|T cDmo#_U?uϼ7a aq!;w$ɐR)&bE~@^~XV ˉ͕o-C9i I_bZ8 0pLӭ^{ҶJb Q'nrq=L#\u+ȭ1fEsBkd9," nJp_>\!xl96?~iԤ3'p6ٖ;6s^RO4 џdV~ k(H%?S5{fOYy%y`e I*r }+Jc޹E;T ]eA-1\cWw!eX*|(݄7-\tV(Gihƒs$-|6>?T(!|uoiMQKS유>џz^8V u\Z2k ?j|sI^d:H+<4ћupծB_@z0lBQ$%n؃1Svc,WT$@=ҹ+COU ;,kxN|z}%8}s/a(<ţ64 ֩Q["s9;˵'5ˏ{l"PI}_ͣ.OR/|r* N8UnYeY>6됔t2sV"Lˋ!o̢8U-1I|[]2]Щ#?uFlDm̔0zq!->5v>$UUjY )6qMq+NtLf<k"BMV@ ZZ."җ`1< Dt~_H`{$|d-xn얥Ν姣|q!\SN]0ءǎo0i"ut9vJA\S|1b\f$K${=52g*:v:xg!Uʿ62|'w|Vv:s"V0y8$ =[y c=M";Y- y$p&^q=I}KDRܐia4!j=]QytOkx{E2F20b\ ~~|z" j4&#vq w}/۵W5 ŵ`ܤD LGouSX|0M`Z]k@-m7t weZ_s5|>5{'ϲD Fյcst g@29%VNC?h? 6dyd; DN1uA%HOC] +DbcA$ ξkm?۷]u~†W{\=D L jy]L8bb6!:}nL {pz/_5҇Mi'5LjQV:Le2|((T)mlu562$R,*~.18~Nczը[t|Ҍ ( ?VPɎsA너[xKri:׹t棬bJDx6x;3EV7tw!HI]-i;Ļ<2:",%Qs#~ H[kÝ+xuaKpy+qy:Ք5XƘCT i!Ȩ)}21˨Ƥ䁠o m=$Η3ǥWt58/q)GVuvNZ0}6.(ܚ HF[,?c$om։$&17(rQ'zP,1xj9W74|{7E.hS WPm!?ze;TCՁd8i`I Uoשt#LO םν dDL?8,w^HDs0ţ` 2roam _(9Tw+__s0/˜mޛJxm}j{;N"un DX$F.MmXˉ(?7 "ii/ HzoNIZ /%dm=g}1u${zNZ椴NV)w)XȇTQL}fmϼfk'ΪAtpP#ZE3J9]kCT~#Eek;Tc7>߬&U[FC"vuߌaE^E'zfi& {y+i@V1Y ds/#ʳ5c*1Z{L^އ6o6wI{d^8Nj3%B"6o.3H;ϪTޜ9}>F1hlzU5tY sorqk/2=ed7 \'8ݡD&. ꩦ_y <;EK:N/v8z뺹mODׂK bV0,Ym9WuTY:٤w NC=&d'zmzE1(sp#2l8ξ&/1mZńCWcu]5P7tWh`Wڋ hk0M Al gӳP`,,.3hmmT C=UxǤAŕ`:mbGd=l Ųa- #˱%N6 s   ;Ń?Zh-_r?au\6n#.tYTf'L5ƌ→<3"%Xq ).:7E1i_&A{tKi PxZ:Pq[uo{xMJ%QWU z%50 &~oSC8;3v(B'&Tq}ظ[~ J ,&\yPՖQ'NH#9Gp_Od[Q?}Ywúb l.vQ uP Ttt F>DO$L,@߂/ )#Zs6w+}Sr9 pzzsVJ8iXT]ş#V]vQ8K-=~"# ~FD>Ty79g4H.<ٕ~NBn֚P DԶfE5H4.ou dqUbO\"/7<*܈^`*pK"/X48GY06VQ\JK;-k{U!haLR~]{{cGlK>F,t#sOEOxMGrBm1eZ@bOZi2`izcɢ[癉͐:O+097̺Ns_`.Ȏq"=-"~j܀em o PĊ"=׽J[ث7*F rG O{$ mAtN A"r`P~C4 o;H̺Wc_N7?wza U"}*h(v_Ytbd#<k<>l(mTڊMyq9/lz /ʭ:hkBqn NI$BR H"Ÿ )-]0E|D:ߥE59h6-7KH@ǭqX `r2RjD>FzR^b! w+'4h. b~@l1^cItKALK.BE$0p!^Ył'{)tco6K4<'{ З|4c6b,@JpG)e"mЌּ|[Ra<ȥTS`3L9=8m1\յ̍5I|[[W[!2髞@o$l=j9ߊz+=[KAJ jjg/D_vlmq!/'IH$bJh# ncj'`bk)'\K:mxtQRpj/AE=7/lbֲ8(ܣOu](ޭAFjTH.$)nJRwQH`&P'ͬNqN,o~2XT)ԍE;js+xRxt\-wҖbljc!=D<'T 2hߣГ bQgl U&ZtQҦjFN ӿ,k=)]e!v2A~ŕ6#i#̾+7iA'˨놯WwJ>ն~W v줚ؾ皘8YC^Ei ٩t>o˛܋8=wTzzxa938>/th4P3CK%XiA 歽9cz4B9Iqg Իl_n%~S^^KN󹾎dR8Vs-ct9aHm.SLI%$Sʞ?i&o6\s4\h g:#ᅻ~S҄ndE9A҂.L 1CLehrh_^Fgi~*S2>`⒈ PaTz`,Y=H6 #s,v ^"3s%3RvB.6\lIg!%ђdK $}תҒΛ&zE)stTӵC:JXm|+) ; |H d;m:`y%'̔ DhGժns+ai1Fxj3ʞX|O쪍 _ٕ5g/i|h@rQ$ҒF췅Ɉd-'O$FWJTH(9١'YEGZVDlb1~q ˏy~!2L0G)42T=ꯥ9 LW:=ӡ N*"[y5M-3)FڨKNFtex9WJ-q]b3lf}U % 2 z zwlJ'?` $ƑtXoREq& 66ycz {hwk`BX%@1n|s2*"SwGetW/mI^b<-HJ~Te]^ AQ_Gˬ.*%N,E W_x]E IqRYBU;#pߢiBhQ37E]$];hQL]'yH0r.̞a8DCvҿ2dzJDVK誆1j@RIa'g͸5]CUz f(5g}pL+Eְ+yGw#?,:!u~%'~lg™p,V|F2a{\ύ8w2ɢ @ԕJ QVT~l.JJQ8 |/@w j%)6o/@ Ct(M'0JrnΓib7]kM,(&r 5] <,TU.O>c% ȯ~^ax\_IaxDܡѭY[3lZ/%Ř3;:kXٔIlt&JvY_wRV{g_T,0[$"m=DEZoi5_RQ{$>#aI-ja>[vE?ih)mg$31mRX-gil /&ۧzsL }gM曛veCfNz'ា竏 &P (jHuDsi@s=u|+Tϯ}{78@ɾCॻ#+)Lߠ|ғdicnV Bffqtads-2.1.7/QTadsGameFile.icns000066400000000000000000002267471265017072300161460ustar00rootroot00000000000000icns-is32ڈف]xx۾ݴuԢUaqϭ1TnОNڿkzԜ-`Թgv⟱Դ̹drᙌǴamܖx5¯]iդ©Ze˺˷³W^zupnpx[TVVUT\x`TQOڈف]'yڣۃtϯάluzϬQS_oޛuП[qkmڗpԝJV{ԓk⟱Ա̜fᙌպǘaܖx3”\դV˶˷e)J# WJ Sڈف]'yڣۃtϯǣzϺޛuЯŜڗpԫԓk⟱Զˀ̜fᙌպǘaܖx3”\դV˶˷e)J# WJ Ss8mk8v r?1N wW }?????????7'S______iXil32 ݙڗ֕Ӕ τxw ˁǂvz||ٛty{زӸ@ޞrwѥ@ۜpuѡпKؙosНлՖmr|ɜϹ~ӓkp{ɘϵ~БinxɗҲ~(͎glvǖҮ~ ~ҀˋfjsӬrǃҌdir羔Ҩ7Љbgz縔ѥ͇`ew糔٤ʁ˄_ct笔{xɁ]bq碔~vuŁ~[`oޟx{Y^lѨĽu߿ڹyW\i¡ʟvVZf̩̮kVTYc ˻uZTSRWWVUT ]ſbRQUTSReiUPPPONNMݙڗ֕Ӕ τ ˁٰ<RزӥeWT6ѥW-<1S4ѡнa/=HY]=Q2Нй:]acef>O/ɜϷFiklno?N-ɘϳJrtuvb1M+ɗҰNze!H,BK(ǖҬ0+8ՀL&Ӫ˃[$羔Ҧ7Z !縔ѤX 糔٣ʁV ~笔{xU z碔~vuŁS xޟxQuѨĽu߿ڹOq¡ʜNn~̩̫z9 W}gG3  W˺Ooſd+1y=ݙڗ֕Ӕ τ ˁٰ<Rزӥe•T6ѥƓw{S4ѡнחwQ2НйO/ɜϷ~N-ɘϳӷlM+ɗҰغ!csK(ǖҬmaj܀L&Ӫԃ[$羔Ҧ7Z !縔ѤX 糔٣ʁV ~笔{xU z碔~vuŁS xޟxQuѨĽu߿ڹOq¡ʜNn~̩̫z9 W}gG3  W˺Ooſd+1y=l8mk#O/;}C;WO+gSwGSSit32v\߀܆݀ۅ܀څ لك؀׀ րՀ ՁЁФޮ ΁С܂ܮ̀Рۀށ܅vvفΚۀہۀwwwˀΚڀۀڀӁwwvՁ͖؀ڀـvvuɀ̒؀ۀ ֈouvuˀ̐؀ׅ߾yrut{½ˍր؁suy¿ʋր׀ئrtszyzzyxwwvusq{džՀ פqtyyzyzyx؀ԀvԀ ֣qssrrzyxwـփʌuӀ  բprsxywلՄɊttӁw ԥpryxwvtԀۻȑstovҨnqrqrwҟȌj΃{ ѧoqxwrᨕũҁ## Цmppqpvr᫕¿Ё+  Ϧnqpwr᫕"0   Τlovvuq᪕ տ6   ͤmpuvqᥕ¿վ <   ̣knutoᥕԾ B   ˢlotuuoᥕ#ԼF   ʢkmtnᦕ ԻK   ɡkn"sststn⣕(Һ!  ȧimltsnށ⡕#ҹ)  Ǧilmrn⟖%Ѹ)  Ǧhlksrrmہ✖ѷ)  Ťhlkklqmف㚖ж)  Ťhkrql؁㗖¿ж(  ģgjkjjplՁ㕖е(  âgkjqlՁ㒖!ϴ(  ¡fijppokҁᓖ!γ(  ƿfjiopkߓ!β(   ŀ ﺾehonnjρܓ ͱ(  Ž񹽾eionnoojπȿڔ#Ͱ(   Ā 󸼽dgnmíǾؔ%̯(   € 󷻼dh#mnmmnhŽՔ ̮(   € 󶺻cgfnmgɁļӔ"˭(  N 󴹺cfgl mlg»Е"ˬ(  L€ 󳸹bfemllgƁºΕʫ(  F÷󲷸bfeefklg˕ɪ(  @ö󱶷belkfÁƖ!¿ɩ(   @µadedejf¿Ȩ(  ;󯴵aedke⼗¿ȧ( 3_cdjie㷗'¿ǧ( 4_dcije㱗#ÿǥ(.^bihhd䭘 ÿƤ&^ciihiid䧘&ƣ]ahgc墘Ţ]bghgghc杘š]a`hgb昘$Ġ\af$gfb䖘Ġ \``__gffa6ۗßŃ[_`efa7՘ÞƆ[_fe`5͘~~ƇZ^_^_d`7Ǚ~|zœʼn"Z^e_9~}|zzĊ ǀY]]^]c_7帚~}|{yv|ĊNjY^]d_8沛~}|{yxvrƌLjX\cbb^'穛~}|{yxvvsyƋňX]bbc^;裛~}|{yxvuurrƌŇW[ba]9際~}|{yxvuusqnċŇW\9ababb]☜~}|{yxvuusrpn •ČÆW[Za\:ך~}|{yxvuusqpomČÅV[``a`a\0̛~}|{yxvuusqpomuŒÅUZZYYa`\/~}||{yxvuusqpomk„‹UYZ_8\絜wvwvvusqpommjTY`__[1詝¿}rtsrpommjÿTXYXY^Z:ꞝÿoppommki¾TX_^Z;噞nnmmkh򻿋¾SWWXX]BYٛ{kmkjk򺿌SXW^Y<˜~kkjf󹽽竗RVW]]\X<躞xjjg}󹽽ˡRWV]Y:꩞ijg}󸀽 د齺QU\[X:ꛟhg|򷀻廓δQV[\[\\X:לjg|򷁻%ȞPT[W:f{򶁻'֬OUZZ[Z[WT`]\\]]^_d($#0r%32/h⣕Һ:"'& %"&>Sa_]]^^_`aabf($#0ʂ$2112/g⡕(ҹA$%&"&=Sb`^^__`aabcdi($#0ɀ#21.f⟖%ѸA$%$Uca`_``abbcdefk(#"0ɀ$1.f✖"ѷA#%$e`aabcdeefghm(#"0"0/-e㚖ж@#$$gcdeffghhijjo(#"/#00//,d㗖¿ж@#$$ieffghiijkkllq'"!/}"/.//,c㕖е?"#"lghhijjkllmnnt'"!/}"/..-+b㒖!ϴ?"##niijklmmnoopv'"!/}".+bᓖ!γ?"##pkklmmnopqrry'" .ƿ| -,*aߓ#β?!#!smmnnoppqrrst{&! .ŀﺾ{ -,)`ܓ ͱ>!"!uopqqrsstuuvvw'! .Ž ,)`ſڔ#Ͱ>!"!wqqrrsttuvvwwx|b>!" -Ā󸼿 ,+**(_Ŀؔ ̯>!"!zstuvvwxxy}d? ,€ 󷻾#++**'^ýՔ ̮= ! |uvwxxyyz}eA ! #€ 󶺽!*)*)']¼Ӕ ˭= ! ~wxyyzz{~hD  !! ` 󴹼!)&\Еˬ= ! z{{|lF  !! ^€ 󳸻!)&\Ε*ʫ=!||}oG" !W÷󲷺 ('%[˕3ɪ< pJ# !! Qö󱶹 ('$ZƖ#¿ɩ< !{M& Rµ'$Z-¿Ȩ<!!   M󯴷&&%%#Y⼗¿ȧ<  F&#X㷗'¿ǧ; F%$"W㱗#ÿǥ; A$!W䭘 ÿƤ*9$!V䧘&ƣ#" U墘Ţ#"T杘š""!!T昘$Ġ!S䖘Ġ !7RۗßŃ 7Q՘ÞƆ7Q͘~~Ƈ;PǙ~|zœʼn";O~}|zzĊ ǀ;O帚~}|{yv|ĊNj<N沛~}|{yxvrƌLj+M穛~}|{yxvvsyƋň=L裛~}|{yxvuurrƌŇ;K際~}|{yxvuusqnċŇ.J☜~}|{yxvuusrpn •ČÆAJך~}|{yxvuusqpomČÅ0I̛~}|{yxvuusqpomuŒÅ0I~}||{yxvuusqpomk„‹2H絜wvwvvusqpommj2G詝¿}rtsrpommjÿ;Gꞝÿoppommki¾<E噞nnmmkh򻿋¾<Eٛ{kmkjk򺿌=D˜~kkjf󹽽竗=C躞xjjg}󹽽!ˡ;C꩞ijg}󸀽$د齺;Aꛟhg|򷀻廓δ;Aלjg|򷁻)Ȟ;Af{򶁻'֬;@c{򵂹)丐֩L?mz򴹹ƚ麏J >Ŷx󲴿ԩ˜ =Ͼ vⶍܮ \ =ɶy ŗ Ӏ￑r3  <Ѿ}t~Ӧ!ѡK  ;yY?+3U ~ᴊ!㲈`"  :~]?   Ax+{“ēx9  :}^@! k(̿צO  <}gR=   !}ѽ금f(  Pɘ|@"ޫU񽋁k0FН}E ^}ݰZ t~p5/JE_& ^w:߀܆݀ۅ܀څ لك؀׀ րՀ ՁЁФޮ ΁С܂ܮ̀Рۀށ܅فΚۀہۀˀΚڀۀڀ́Ձ͖؀ڀـɀ̒؀ۀ (ˀ̐؀ׅ ½ˍր؁H¿ʋր׀m -WdžՀ jX[mszԀԀ j Oփ6Ӏ  iL݄Մ5Ӂ߰ oKԀۻEԅ}l{x  Rҟ<#҂岀krtt{w&<;::7pᨕ}ԁ鵂kqsttsr{w$76674m᫕¿с븅iqrsrqyv#763l᫕" horsr plzspox v#763l᪕ տ gnqrqplvronvu"542kᥕ¿վgnpqpnkvrmlut&541jᥕԾ Ðflpqpnjsqkktt%43441iᥕLԼƒfknppoonjqokjrs%43320hᦕ Ի͔fjnonlionihqr%32/h⣕Һ|ln*lhnmhgpʂ$2112/g⡕+ҹlmmhmlfenɀ#21.f⟖!Ѹ}kli&jddlɀ$1.f✖Kѷ}ijijdcl"0/-e㚖Kж{higibbk#00//,d㗖 ¿ж{ggf'ha`i}"/.//,c㕖$еxefdľ#g`_i}"/..-+b㒖,ϴxddcǀf^^g}".+bᓖOγwbcae]]fƿ| -,*aߓ=βvab`d\\eŀﺾ{ -,)`ܓOͱu`a_d[[dŽ ,)`ſڔ=Ͱt_`^߶ZZYcĀ󸼿 ,+**(_Ŀؔ=̯r^^]⺅USXYZXb€ 󷻾#++**'^ýՔ=̮r\][仉VSWYYXXVPZ€ 󶺽!*)*)']¼Ӕ ˭q[\ZՀ XRWX UQM 󴹼!)&\Е,ˬoZ[YďWPVX WUPL€ 󳸻!)&\Ε)ʫoYZXɐYPUWUPJ÷󲷺 ('%[˕3ɪmXYWʔ\NTWVWVTPIzö󱶹 ('$ZƖ¿ɩmWږ_MTV UTOH{µ'$Z-¿ȨkUWVLSUVUUSOGv󯴷&&%%#Y⼗¿ȧkTVUSOFq&#X㷗'¿ǧiSTTSOFp%$"W㱗#ÿǥhPNEl$!W䭘 ÿƤZf$!V䧘&ƣ#" U墘Ţ#"T杘š""!!T昘$Ġ!S䖘Ġ !7RۗßŃ 7Q՘ÞƆ7Q͘~~Ƈ;PǙ~|zœʼn";O~}|zzĊ ǀ;O帚~}|{yv|ĊNj<N沛~}|{yxvrƌLj+M穛~}|{yxvvsyƋň=L裛~}|{yxvuurrƌŇ;K際~}|{yxvuusqnċŇ.J☜~}|{yxvuusrpn •ČÆAJך~}|{yxvuusqpomČÅ0I̛~}|{yxvuusqpomuŒÅ0I~}||{yxvuusqpomk„‹2H絜wvwvvusqpommj2G詝¿}rtsrpommjÿ;Gꞝÿoppommki¾<E噞nnmmkh򻿋¾<Eٛ{kmkjk򺿌=D˜~kkjf󹽽竗=C躞xjjg}󹽽!ˡ;C꩞ijg}󸀽$د齺;Aꛟhg|򷀻廓δ;Aלjg|򷁻)Ȟ;Af{򶁻'֬;@c{򵂹)丐֩L?mz򴹹ƚ麏J >Ŷx󲴿ԩ˜ =Ͼ vⶍܮ \ =ɶy ŗ Ӏ￑r3  <Ѿ}t~Ӧ!ѡK  ;yY?+3U ~ᴊ!㲈`"  :~]?   Ax+{“ēx9  :}^@! k(̿צO  <}gR=   !}ѽ금f(  Pɘ|@"ޫU񽋁k0FН}E ^}ݰZ t~p5/JE_& ^w:t8mk@Mԏ2wKW{ nDaVNG5),af3To_FT'yp+ELFcF7F9 FUFEZYYYYYYYYYYYYXglllll}lkllllllllllllllvllllm\YYYYYYYYYYYZSFFFFFFFFFFFFH344444+{6n)_UF|82HFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAo,2c[TIC ic08f jP ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2cOQ2R \ PXX`XX`XX`XXXPPXdKakadu-v5.2.1 e |Z^c~4d# jɁvoP&W*jAAN߇/%mAY".8{_՜,~>^ ɃU!w,C6cBȎl'e(n"`]Bҫ׀ nZꪅ}s}+;1AbpSfsA0e8^5+[T Õs<-]qk~h m5~̹Ƞ/s}SS@E~+AbC1*c  ; z+:KՏq>/ݍ +Ab?x܌΃dc(u1ƞ#Xf(uIg x@sgqw,' gWj' FR>@ړ@4= E"$)z)@.j$ @,1(rMmREq^ W-sb/.xd86YN^V}Itm ,z/iyX D9n[~1JpCǭ $;")Yh_Kۖ?}%䠛DJi?3(~~]%v oQdVP%G`Lyk3[SfE*$s(m+7SC> d( GJ%ǥtj6AJ$v5z3$=i()ju6ݐŐL+Nua7ftv9a\A$qB3 2 "J/v6Rsڝ~?"ܕ+◤$iC6TB|Įr_[!y*IJ:-74 =Gr[jb^ į7K?쮌9& >ԉEht Eb^g$m;qdc8pm>'3Gt_ңҪV+IrXb|^ .꒿MfNh<'L\!D`\=:z@nKe=d{vVS|UԚw:y"0({ <ۮ/*ys&ɧagE?5s\OR;@^wdme=?Rk! H7i=6ߎ.VJ ryuǛ;^E*D47(Ripr-(L%%euGGEI?zX|:ibhߚӤar̡i nA 832xAU5+8t[dY@;utA@|6ݧBhXSz/Zx.bNx$V2*D^&H5"vf8iRs Z+rzT9FVI1̽s:"7)+o7WVI]چgژ0cDf]Z$V=QUb(E am /~dikŷpE~5I2=vlF@̄Q$?8~ `5$BL~kLAp cdxLfhs:?6Ԥ$tS@bI _ qm/V-ԴcaTkrRއCZ}0l%1vlZwPV)LN+OJ)rbPFZq;VJp VhWV*9y 40$VWYHqSvi|-V,8_ )8z=b@a+18 ݎ-to1 ߼[zҥxU!x)'y lj KقjSLNsM /}M~T~t8&Qq=( LLR gᵟɁS^=ýW(i Դ1e䂚/8G~sDHb/NL_&&Ȳ4 Š{BLX蔨uRuمM}UĔ!15 ؞:t8B8&X) g(NZУOMQ4s 17_SmW%R& _~E8rne LMr3 (p=r9(w8 )ʼnR葅L[*DdX+X_JUuE韌abSv̿usO7ek9n|wEkYҦR 8}܅4/:xcUH`$$qĸ 7z+x}C"Kt[v奧ߗ5*_^94gpz*r>%j͠Ug`ؼhޚ5FL6Bffg{r-DdEܯ!x'bAf57yQܼM;ws7!R RuT%)4lpS)::*1s0TL(0 {L/c: DCD~ Txlo NSw@m/n?v0F=fx5%W?&Cɬ fB^`x1ޡ .5uNq7$+ gWnZgj1,mǻihV D ]LgXk/PQ?$,v!F\ <Yr&~f/k-jL|eqgVK @Qv5PZG=7@F A|@:|ȵAt#fi35elAĐ.u4LA|ռw ӧ6~SKD)sMnZ1TvD,rD 3:9[E!:FFUS.Z?xÚxgbpmRg6^4⎃v /ʃ߉!kq"ȷf/d եכW'%:.GQ6u@$@ž@l@_/wpjOU+BD,><]KX!nv= dAY𢠰ۥ KII|Ubqptٟ{G f/s MI0%x5@ÅaGV=v7c__* iͪ A#A|+&^Po0Y7mp.L$n(lvICw^MGMJ_{b{m-.‡=D7 ޅ%X`y&rQpY4=(\-y3^Qrq\0f ybڄTxx\Ш} n }ji'(}$K\JњCb怈Ve$m򞕟\26Fq τԬJrþ@ \mM-I]=IE.j7F>T5,"MPֶM"FzIN{8Ғ+m5ZЕ c`!Ǿ 5d_f ׻5j3C3$ۯ-}!-m_ͦx$1y?tw-bxN|ZȢ4InL WeJ4Es]Y?9lLW m(]rRv"Ydp: m~d><5P;1[V~m jgΥ[$hF ҹaNCpEz|C(#~jImt/Rꅣ!-1s0[r+*vd8"ǝ TL2+_BŒs=b$x즚e/O&K@g3{Gj{- TJHH/?.",T`/ rV*{ c 17l}ewk7LaL[rJ5_J!;j)G>QIj\[P(Y9렏y ;]{L`^aZ`G:_{_B?!u]983+U, "41M#[#[QŽ<ʇ`(7#]iMpo5[X Fbm#à᠊<(.9r/t@&zY$Ondq},p S۝׈sCmF7T R; 3&vKms6&:ѹL; RgS#pTw4Fr9w+H.o>mwpȓx' fSDL$6.LJF 2!QgѼ6Bs0 /;BX'?& ,@:'"B΅*Y]UE~l%v#ˇ,S+!B^e~?V3}˼8cuC)WƈLG?NBS0ao!dҩ5u 7>mjUSz1Qݭ.hZP B(DŽy^cxLХ(5qnW-BlN)>^O6:H콆?l|i{QkiI uE.^!h7lV]/+ Idw2Tƒ["TQk9\P = Dߚ^kU6ؒE>T9EJ5qaܙDw~ebj/c猼7㜪lb F$ʜ,],lgKMm62=x$e1 7W{9CwJ_jar|iP%),9~CNƞBv&>uZoJΨlݨBw2 l]PaZӍk*Bgu跤˯-2/`|su !pCGTX |DH)CDUn$G7TdwķH(;,:)&P7x$D@dmWW6WT[c'\v3BނʄX2H=K45eA}W7`+&jm~4ofbN$pϣ6*Bi1U9(5RБ 14&zb0n$ ;r,Ǝy/$L{!G?6ۍsчVy`Z^e-8zpEKc?nJ'Hl"4nl%ߴq1$j#U42") , Oa5.`W c^::Rq9"c>lK>Užm[i*qVK·O#Ek:*""0( (ǯ'".~<#ء#L 5YyL 47ƙ IVе\6=y5F_rkϘ˘O^Kq*͹Ֆa:7sn. Wr8[Vyg*[*Skk9N07Ӏn E-iOi>s| y/0Br[mUm- A^mn?֬4 r15?PGm7kͺٲkBCodhCៈU%sמ n5&l؝ͬjl]{hÆKZH 2LAOzGܵQx;$&A[e⑉qR^_М.94_'ȹJe (VX3UHImr+oJ6Nv ,5{#Ț%øL'..ʮ%c<Ϝ5Q $+ơuRsǔ2 A\|ѥp Bq\GH +:Lv;/x5a(y%TTJKД:<ą5_c8I(fޣqMcR(5hpjy!K5P#8Q4RWuy1Jϑ~|sNA~c=b@卑D()+=9IU9zQb5{EՊWy8ɭwuX3*uOOrHAlgڈcP"j4hRKhS[?Ӊ"Ybo/4R|T;HZ vD !B){6ItAS \vetAՏ0VEXKPn>G}'DtP RQc6sAh,<ܧr#<(Յ?XTZӭ̾7Y=&e譪KzhF4T1 1dWq:ٞ:0^А`| #(.?ƐwMSN&QFgRhx:P"\RmRⲝr\r'HbvŖ,:2hR\ڬ{86A zBfv3>kQmN.HX]eHYPI|-j%tq$5sHg$3|MWLˈR.`E=\8:=qDo*MsM3A¢t`a+|U栨?SF$7=€< yfS1=1ӫ_D> ([[aиxx=n/F&YvQL5VWݕ՗HzKC4R}RiMYfd4![e" < Р(쁄›g< W*Tݾ`zWvmHl]AT L $cbqݜP1GQ.D~cR0M-vߘH*XPp}TLuEACuClԚra"!G eHd$AEgv?k_ߏ#-U"pw!1h :oKI֢F<+宂[1_K6V/Ok$ 7EnܸZ&R(k'^飩ws-_f;4/饩[M6; Ni:%#\럊Zx3Ho ¹r`h o ӃǎS frn{.$`4<;oib|0m :cqp8E#$R{Qs1$^Ƃ1 3;jafɳmL26R.h +$T]uM?&ю3&,Ls**?`0 QDd\Y~kL?Ur `6J\XFw@] B@B^g?+rTcy%L׌^-w80? rrߛ-?]47bX`׉Ρt(B/4w^aꚾi@TmB35 1K5M2fI% ִXMZjj #E,`%a5CP8i)Hg: mӗ|X{(UB`˃;!< ӘBpvt|ZwG@sNJn=Ce8mCĥ Vs' w4> :, &0ASQ$%x6UJlȹ`d)-NLU2&as)k%f8h 9aݞ<~bLpGPEݮn)4T綋V9)JSt{+MĤrHG rȯMqA) PC:S?K B++7;)DXwKDȕm\SI9c3RlL,I'C1[2=TSxs` /T = O"IB*f#<6ɯ^ kC`oesN!GZ# ;A'o#N҇sΕ!3Ypr"$*{9B:ъnԴwlc'ȕA#"m;?I-0FpE78B^tSz'4m}@$z&ˠMԭsyt'Ǜ}nI(c}k.^ʼnRu%,T#MݑՉ`}bjQAxc\u;P%CB?[ }*=|JdN/ը|XxKn}7qN7'ExrN{|pvѢ$HZ[2):"R}HQD-Ce|ṯ/r-Y.+g-;-B)S'p,ڌMoQtoFF1h)sÍcMѠ^WS0W$KպR=1+dcy/uF% ʸXzXj*a} i=IZcX&alֆx>}OO@9(yE I1~ J86,`NF 6ag,3wA{!S^/ wVTam8=ZS|q] W;Fy+jAjaGɖ:ERXϾp9U 6"}fJLXd/aS(~ڎ }$M@q}uw"hF|En$T2ӡ'/W1Є` r CWlH7qvh3K&ٚ[/ӫǘqZ H[XJ (*!Chn6Âl rUoT:{Ö/uQ{uOč:771l~~L?5 x3K^Yc$c=5>`]69a9śISeE}Fn60 &^3TRdr2YCɉr2cRKH^fV,E!0gaZ/6eZx2h-J>o4@U)N` `M͹.xFHq oFrޢtlCN5*@f]]qZ7ݙǐ^ZkQK6 }vBF)pAwmE x#0 Æ'|O 2%2@Vcd۸X'瞘m;$Řׇ$QK]K*KTd}r6jE8z%Gдgk'zY%?AKnM,.G+vW9[Yv9hĵ]qZm'kEb~{ԣ"dgxÐl]oGO8Fp:s}eWNNr T}yjgJ8:$Q_JVibG/eoJ+lGK&DEMX0_^GZRϙks'Y@ñ/`~k֠ Pq"֖[,)?Z lr$ YMhũwԲ@M7]ҟ: g- ceisjU{f\o^-qQm #vi' *6T/,܇QNl,?*-^ Oc+*E(: =_I\պ|!@SUDQ.[aȀ[Ӫ[hb$Wͽ]Gqe2g*y[@ {@x89{rr1<֝}'uaJ}]Ll.ey2o %f7Ȝwoj](YD-́()^,)c|U:?_gըV cPƶ1gR| ,U88J,-tCJ O鯆#һ0Q'+]ؖ')o\= ssj E& 壠T: +6FފrVQ>!m[iŶ(5(IMMH CȑLä.m4eӚ9Kݩ= )L͎9KCd6ߵ_nSrf,]& 5uhѣAILeڬ@/!ZqqT)PGRwWpC->8 9B(f#ن.~rJsAf΅/+n|.m5vi}MQ*3*]R?ԬN@يP_oѸ%ik-Jӕ&JHU}/tsu7أzk<%Xf+vz}N+"Bm1e[ݳ XQ?ɖ;3_=<)s)LmhhܙC3:gj^pJ|)Hp4*д.Zjfmix}HJ:lw>vQ"?z= 5JR4{!Ʉb~3'̕~j_mV-7T-[@;Wr=Q(ZD?@7y+5fˌ9M }2QbFaE9.;>oovuRrdlEuǑȻYxp8F|/n>W؃&%j@M@()=ge'%%,{X^Vh2r:e;ɿhCDh@7)1Ux]6c [$HړN}Cҧi5e+l3V͎¬hYG}-)O4ˆ],sXMKBT} a=6^Lh_m/ܸzs%Tr# ,h%O^~)#󱢸rS9=>G5myeŏ?;Yҁ)ܭFdiz9 XRMc[=`iPFXR>m|KGno0RYVwV1685:Dr}mxOmKX^F݊ĸW&,qR IGy6`OZp2z_J|+ ߮G{ΝmRE^X2 y'm)hqB r#"yb#4B v\m(/ΣT se1 XpơR [1gύ%&reԵQ3ֵ&K2}kz+H/ H5Q`WPQ؄hJIwGCI.5HOtgvE,s@ix3i?ҞxFҲuL;ʛ;@N6ZhW ` %iY6sנU:PN)$;GO@lVQF1\DfC]MZ߾'F1ȵ1OK .,RHВ ؖ>$cA9穁U֧+yvn{qx[aP߹D> d۞μIJ-^omh5su+Gd”cBnXOCPy3y GT^+Ofܻs݆($,Kw"-PY0K޼BNzԺ kR.P86ykdLac;sv|Dﴃ-h2˱8齤}XWs3I AvO\}ɡi"Ψ@N11żpr]JGLC!jRu]RޞT!,$$hL$4 3vn"+W".7ӝ|~T( TkE l: %QwTXԁ'PC{՛'glIz#Cv Nw[>Tۣא˞rXFJ ū~%,oQsRXò&)m~/dh g֟<DR&Yɳg 9L8`{fzja4Т"N;@cj\t p ;ވB(@K]3L,ckP./m{s ) BI#>FLIFBZur PF#`w)bfHBQEjl,^ʉ7ŬrTt{+p`H{mU)q{c;v9{Q7jLKvn.MZ7zT8,sO{e/.+T>߃Cin` 3. t7¥nMm:U6YLvj0@DK`VXϺe*Qi:se!?i̲PT&_eTָqiI: >lw#iFB?WZe1 ź^4>uPM1:uY^3 =:h&cˎ}XUZHC@_k}ƝpW6ЗBK[y!arp"f"-i#*Ď٣Gkh|(:ʚjf{ }_H>p]&uiQ1@U%A#TjjQBW{pSi \v-_*vB4i܋bB/48Kq%tQR`pnc+0o.C.# mҩHr:L #"ct솓*^,P;*ZrI =f1^ ش-mS{A[f,Q<ء\^;ݼ+ ZA%XnEuI'cFA@~\mch'>CXn'TNb-*r )vi6m)l(O%|ˢq>?.D Aʆck*u^R;]DdnؤH_b* ((Z[r> "*^#x4}Kێ[8@GŠWv44\Ru-U!@Y=V^d钁=}X2(>Fq^pXnW :/ >ᡅSz1 ;J$}8IWlY3-lx(Z/&i`0AsA_KHӦ2{T>+lmy#H>g՝u Ca1|uh Dcz"Tet1k3R,UJ*)AYFR`O@r7Rx!7"+ZJ_*aRcwq讹@4^0[UwʆF[+.Elbo<2It>h7N||ڳNTIsɉs ;"cciG> GHmVVKP] ' (& [^7 &־&/gBwQ|ܽ%-'{<2Wf?u 6b8O"fmBFv| i-ƫBf1y0a[!e4hVwF#$)Me',Cwk^>oe@17TvX ?Cdl%b9+O8-l*Saj'9f0?4\˳7'$j;ڃe>T4yfV~X聡\$|wB85ڐM'vVd-9Um8fJΡmt_MqP]Mլy0]}.9+ ;Z9=}>Lj}07i, 5ܖ WK_y# K|0 2 lkY]\/+Hېlc4$b6])WЋ֓+[P. ]JBAN8FL(s \cO͈T8EAZ쮹ڂbKnN|`]RW!^6@"*Au_5؜-By6F>1N"!*XFMVNk`WXORgt/~P$W;yMFWjUVUQs 2w:||^=fL.QϮږt\:> z/.]vq', RVh@ NAM^nϢ#aWE%pD? )$d6 FȒbUUɦFVޘn+qR0*@:E2N{MJI~ bnOLg̉Q>r usjU cro۬PܳE 1H\IqNr|A-&39dϿ[Jϲ߽TP@ӫGg%պ= {Gi*|.9@ckRT^sjQ# g<1/,qPdl㪳ʻFKrŏEP@ԁ:Ckףp9[H / V ;)wR#f*T=l<tWօ[Nu@KQ^=%,cEOҪyr+R@]})QDY9>8K4XY^I]۟^\ҦFV>l:;hDa,rnƊi$9[ǎ 1w27I\f=s[j%^t\e`uض¢5!g#&BC,٭0tO>W4뼷pBMHCo6Xs ÒB<^<+tBP.dyupR[I'p&>!Y%ΝXΎ;NXSCN.{;UnPQ^" h#:H!gs[ H'OppM I++pAjiS_DXy @;E(IB] jW g8Z4~~2 DA 'ooj?a g4Oˣgۜz!f!/6tsn[-ҹ*WuOr`%/Fl5zaU,?{RE !L[d/jަR|Z [&-,m0|;vRJe_!%zmuG Gr4ZCn_ŮO\d6FuxP9Rm3`:m&#mK;_]GyEg)k{y97?YcT= /xi/l?b*bƗ̘9eK@ (JwM/7n8%W ]m~v>a^lD<=dwԽX =$s d .7tSKk^ԲdcDtpY+}i65c_wgmW"0)vgb3i҅5 P; \v216#r^Vxz;0b5PihD f=a_zhyF?d$Gn;az~f8;w?nD >?n6 paeb`F4bOMV i8C&a2EŌY#阎 HaBF ENFp5_)DE⣩Q\̌kJbyV$Q3DGxY >v,UROZΓ5h*'-(֧\zГB~Az#S,Х)ET48,aObMϿ|1tu<:LUOK}07J8o˹ʟZB`&R mwt?`%Ο@C2ܫnmn=`Bo$4\^%,a޼:QpCP)a\TP3P͢aP%~}D7]ؕM+,I0!{=ahO֭/ &Sg>;JױkY"LyHW2](YOpQ}PB}f$DNL.= _>\igI0KH'1 7:،J 1#Q =FCM|s`c!Gy{`$pm"AK:qGXuʦGYQ .HD@kԺdDgK#38vX-פZ-L`GcΎWf LX<)RSYG#JTko'qhnr*O3!a n"Yu!p,>JX z|{k1ĦDq)! Z𨸏;〱鰴*% jw's-CRٹw TjM1-#Rgu\v XVEY`xp62nj.(Yo '/u߷\)l}ZC}ZS_I/ [Բ a{_ &ܟPD>XNv%]fu_7eE+3>#$ТM{ŝMKN6CFO$&n)'ms!M' 8/Iv3k0(JSy5?i+mwZQ%\0 !Ci"féҴ-/)Ҧtj$9NJj~gہ(϶ȱȔ|E*.A.v*xt7&5˂=Lʩ`ٙ䷆]ÜnV/k0):6\剜g˫$YSLթO~RmNOT4-e~NsQ\>ck{ò"b~WJfvzZ9H g: a$;t?)& $Fo=-U`TLV.=:wP4N%+Eu?im+6uH8\87Cg:;mFg6a'!0^JPea\P&Hu-J`ZAu,j 8WÎOlJ~g'Kr2̀0h3FQa]>S_I']fѳFM\0@4NF1JF`C&hOS>'lF +r7sZ,*\4i}, :2tW q\AW;89}Oc _4d%ZN4: :QY&6(.xoYhϫ jc]DBl#Ҥ5pѹ[ ~5pURAMYINPx;ߠ0s`2vѩ:`',21X J1c#aUc1XQtjWti༞@qz$V9=UiҤQ#8 Iq}Uce>&Jz;,KUOǔk]h##@zȫ:ȚqCp~cB}_x[ϭ9-a.$]D΂ߍ8Ej+npZbg#n 03IBjrnMS;AVK&سTCif֒ y p@؃kn+"zpV5OPzE kVr[QȽY ?#26j(i  .Z -*ٖXYF DGD:x&_p%:$q6wS_ĵUY墄Um2/('ꂄ_Lm"QfwN"D?3dߡY䆃ZQ_ cx}gU2DkIv9o80F6%6_ LٔgOz;Fd2sw_fU#lcg0M/?dOW݊9?ɣ|GԢ. @F5iN4".uaBJxNb*ygŭKc~0J\Σza\*  xFQz.`U"K-~%(;֧4,NwvK^pBƿj6d+zR $f `ϗu+yoz]@zm0MެQ =hf DH s_}a. 5L;M 0̰jV,g4ƍU>V^;aLs;@l ^C?+ƫ^` Wpviq|,p%PrÄgr<H(Q|&lx(E!2ńi2-){H `Aes LtQ&6WE˳##A_i6 {q;w.1!yZ݄6j_O9&-rʹ0TO:,;>8R&*r gST?kHB/"&d?_NǏ۵#gUT rPV|IH^pOr[IZjt+8s|7$WRJh⸛},WOo^ﯶBckCNc^-x骪w{%f L@*-fd} ?1+0[*0_M$'e$Wo5|R ?`0 cŕqf$&#ۭ%y` @4Rޑpi M(~QA~dn9ڀÑKf u|x˖= / ҁqa4_HO$P $0N0<^'@11MEY͵ts9_qn79~5HZcxcuV> П-@MR&<v)P^~ ú0_vy- dG1[7nNp世r[۔Ɵ ne]P}_Hmx7 nKr$14ǵs0Pxue̵h`Bc_PxR)ݢv<),NHӇ%YHey74 8 ^.͑{hEb,>jKwoRW.+'޷"6S &_'7q5 z^>5& XSY8٦MB 91Cåt.| l5qʾ7,8yU~KvT񐘯ӓAƉږXѾiaPFaB>TWU "vL9eHhH: 1[u&{Iӕom*巛Μ9/|D71gEHXx2W9 )/+ke ցyb_Δt @p^'޻t'0o;KYR_b$;e~3|HbJsؾaAHEgicnV Bffqtads-2.1.7/README000066400000000000000000000103531265017072300135220ustar00rootroot00000000000000QTads - A multimedia interpreter for Tads games. Written and maintained by Nikos Chantziaras. Based on Mike Roberts' portable reference implementation of Tads. This file contains an overview of QTads. Other documentation files in the distribution include: AUTHORS People who wrote QTads' code. COPYING The GNU General Public License (GPL). QTads is distributed under the terms and conditions of this license. (Note that this file might be missing in the Debian distribution; it can be found at /usr/share/common-licenses/GPL-2 or in the "Help->About" menu of the program.) HTML_TADS_LICENSE The HTML TADS Freeware Source Code License. QTads can be distributed and modified under the terms of this license if you cannot or don't want to accept the terms of the GPL. INSTALL Installation and usage instructions. NEWS Contains the list of changes between QTads versions. qtads.6.gz Manual page. The newest version of QTads can be downloaded from its homepage at: http://qtads.sourceforge.net Previous versions can be found at the project page: http://sourceforge.net/projects/qtads About Tads and QTads ==================== QTads is a so called "interpreter" for games created with the Text Adventure Development System, or "Tads" for short, a C-like object oriented programming language for authoring Interactive Fiction (similar to the Infocom or Legend Entertainment games, like "Zork"). Yes, we are talking about text adventures here. The Tads compiler creates executables for the Tads Virtual Machine (the "Tads VM"). QTads is an implementation of this Virtual Machine; it runs Tads VM executables. One can also say that QTads is a Tads VM emulator. This is similar to Java; the Java compiler creates JavaVM programs which must be executed with a JavaVM implementation. Both Tads 2 as well as Tads 3 games are supported. QTads is not written from scratch; it uses Mike Roberts' portable reference implementations of the two Tads VMs; the T2 VM (written in C) and T3 VM (written in C++). For more information about Tads, visit its home page: http://www.tads.org On what systems does it run? ============================ Theoretically, it should run on *any* system for which a Qt-port exists (many Unices, including Linux and Mac OSX, Windows, Haiku, even embedded devices like cell phones and PDAs). The systems it is known to run are Linux, Mac OS X 10.5 and newer, MS Windows XP/Vista/7 and Haiku. What's Interactive Fiction? =========================== Well, this is just a poor README file and therefore not the appropriate place for an introduction to Interactive Fiction. For more information about Interactive Fiction (or "IF" for short), just go to the Tads page (see above) and follow some links. Or search for "interactive fiction" in Google; you'll be amazed about how many results you'll get. And you'll be even more amazed about how active the IF community is. At qtads.sourceforge.net you can find further information about online places where the community gathers and where to find games. I don't like QTads. It sucks. ============================== Then help me improve it. Send me suggestions and bug reports, or even code if you know C++ and Qt. Now! Don't be afraid to do it. Thank you. Copyright ========= QTads is Copyright (C) 2013 by Nikos Chantziaras. QTads is free software; you can redistribute it and/or modify it either under the terms of the HTML TADS Freeware Source Code License as appearing in the file HTML_TADS_LICENSE included in the packaging of this file, or under the terms of the GNU General Public License as published by the Free Software Foundation and appearing in the file COPYING included in the packaging of this file; either version 2 of the License, or (at your option) any later version. QTads is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. qtads-2.1.7/htmltads/000077500000000000000000000000001265017072300144605ustar00rootroot00000000000000qtads-2.1.7/htmltads/html_os.h000066400000000000000000000054261265017072300163050ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/html/html_os.h,v 1.2 1999/05/17 02:52:21 MJRoberts Exp $ */ /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name html_os.h - OS definitions for TADS HTML Function Notes Include this file to define portable interfaces to non-portable functionality. This file (indirectly, via a separate #include file) defines types, macros, and functions that can be called from portable code but which are implemented in a system-specific manner. This file is the "switchboard" for OS-specific header inclusion. To avoid cluttering the code (in this file, in the portable files, and in the system-specific files) with a confusing maze of ifdef's, this file doesn't do anything except include another file based on pre-defined macros that specify the target system. To add a new operating system, create a new file in the subdirectory for system-specific code for the new OS, named "xxx/hos_xxx.h". (The "xxx" specifies the system name. For 32-bit Windows, it's "w32"; for Macintosh, it might be "mac".). Then add an ifdef to this file that includes your new file. Place the required definitions in your new file. See win32/hos_w32.h for information on what each system must implement in its own xxx/hos_xxx.h file. Do NOT include your hos_xxx.h file directly from any other code. Simply include html_os.h from any code that needs the definitions in hos_xxx.h. In addition, you should not put any interfaces in your hos_xxx.h file that are used exclusively by your system-specific code; hos_xxx.h is meant only for defining portable interfaces to non-portable functionality. For any interfaces that you use only from your system-specific code, create a system-specific header file of your own. If you're porting this system to a new platform, you should send me your #include addition to this file for inclusion in the generic distribution. This will make it easier for you to update to new versions in the future - the generic copy will already have your system-specific #include, so you won't need to make any edits to this file. Modified 10/24/97 MJRoberts - Creation */ /* ------------------------------------------------------------------------ */ /* * Include definitions for 32-bit Windows */ #ifdef T_WIN32 #include "hos_w32.h" #endif /* ------------------------------------------------------------------------ */ /* * Include definitions for MacOS X */ #ifdef MAC_OS_X #include "hos_macosx.h" #endif /* ------------------------------------------------------------------------ */ /* * Include definitions for QTads */ #ifdef TROLLTECH_QT #include "hos_qt.h" #endif qtads-2.1.7/htmltads/htmlattr.h000066400000000000000000000077731265017072300165060ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/html/htmlattr.h,v 1.2 1999/05/17 02:52:21 MJRoberts Exp $ */ /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlattr.h - HTML attributes Function Notes Modified 09/01/97 MJRoberts - Creation */ #ifndef HTMLATTR_H #define HTMLATTR_H /* * Attribute ID's. Each valid attribute name has an entry in this * table. Note that the attribute ID's are global, so this table * contains the union of all of the valid attributes from all of the * tags. In addition, this table contains the valid value names for * those attribute values that use enumerated value names; for example, * the ALIGN attribute can be set to values such as LEFT, RIGHT, and * CENTER, so these names use these ID's as well. It is always clear * from context what a particular identifier means, so we put all of the * identifier names into one big table for simplicity. */ enum HTML_Attrib_id_t { /* special value for invalid or unknown attribute */ HTML_Attrib_invalid, HTML_Attrib_background, HTML_Attrib_bgcolor, HTML_Attrib_text, HTML_Attrib_link, HTML_Attrib_vlink, HTML_Attrib_alink, HTML_Attrib_align, HTML_Attrib_compact, HTML_Attrib_type, HTML_Attrib_start, HTML_Attrib_value, HTML_Attrib_width, HTML_Attrib_noshade, HTML_Attrib_size, HTML_Attrib_valign, HTML_Attrib_border, HTML_Attrib_cellspacing, HTML_Attrib_cellpadding, HTML_Attrib_nowrap, HTML_Attrib_rowspan, HTML_Attrib_colspan, HTML_Attrib_height, HTML_Attrib_name, HTML_Attrib_checked, HTML_Attrib_maxlength, HTML_Attrib_src, HTML_Attrib_multiple, HTML_Attrib_selected, HTML_Attrib_rows, HTML_Attrib_cols, HTML_Attrib_href, HTML_Attrib_rel, HTML_Attrib_rev, HTML_Attrib_title, HTML_Attrib_alt, HTML_Attrib_hspace, HTML_Attrib_vspace, HTML_Attrib_usemap, HTML_Attrib_ismap, HTML_Attrib_codebase, HTML_Attrib_code, HTML_Attrib_face, HTML_Attrib_color, HTML_Attrib_clear, HTML_Attrib_shape, HTML_Attrib_coords, HTML_Attrib_nohref, HTML_Attrib_black, HTML_Attrib_silver, HTML_Attrib_gray, HTML_Attrib_white, HTML_Attrib_maroon, HTML_Attrib_red, HTML_Attrib_purple, HTML_Attrib_fuchsia, HTML_Attrib_green, HTML_Attrib_lime, HTML_Attrib_olive, HTML_Attrib_yellow, HTML_Attrib_navy, HTML_Attrib_blue, HTML_Attrib_teal, HTML_Attrib_aqua, HTML_Attrib_left, HTML_Attrib_right, HTML_Attrib_center, HTML_Attrib_disc, HTML_Attrib_square, HTML_Attrib_circle, HTML_Attrib_top, HTML_Attrib_middle, HTML_Attrib_bottom, HTML_Attrib_password, HTML_Attrib_checkbox, HTML_Attrib_radio, HTML_Attrib_submit, HTML_Attrib_reset, HTML_Attrib_file, HTML_Attrib_hidden, HTML_Attrib_image, HTML_Attrib_all, HTML_Attrib_rect, HTML_Attrib_poly, HTML_Attrib_justify, HTML_Attrib_id, HTML_Attrib_previous, HTML_Attrib_remove, HTML_Attrib_to, HTML_Attrib_indent, HTML_Attrib_dp, HTML_Attrib_decimal, HTML_Attrib_plain, HTML_Attrib_continue, HTML_Attrib_seqnum, HTML_Attrib_layer, HTML_Attrib_foreground, HTML_Attrib_ambient, HTML_Attrib_cancel, HTML_Attrib_repeat, HTML_Attrib_loop, HTML_Attrib_random, HTML_Attrib_fadein, HTML_Attrib_fadeout, HTML_Attrib_interrupt, HTML_Attrib_sequence, HTML_Attrib_replace, HTML_Attrib_cycle, HTML_Attrib_bgambient, HTML_Attrib_statusbg, HTML_Attrib_statustext, HTML_Attrib_removeall, HTML_Attrib_noenter, HTML_Attrib_append, HTML_Attrib_asrc, HTML_Attrib_hsrc, HTML_Attrib_hlink, HTML_Attrib_char, HTML_Attrib_word, HTML_Attrib_forced, HTML_Attrib_hover, HTML_Attrib_input, HTML_Attrib_volume }; #endif /* HTMLATTR_H */ qtads-2.1.7/htmltads/htmldisp.cpp000066400000000000000000004467571265017072300170370ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/html/htmldisp.cpp,v 1.3 1999/07/11 00:46:40 MJRoberts Exp $"; #endif /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmldisp.cpp - HTML display objects Function Notes Modified 09/08/97 MJRoberts - Creation */ #include #include #include #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef HTMLDISP_H #include "htmldisp.h" #endif #ifndef HTMLSYS_H #include "htmlsys.h" #endif #ifndef HTMLFMT_H #include "htmlfmt.h" #endif #ifndef HTMLTXAR_H #include "htmltxar.h" #endif #ifndef HTMLURL_H #include HTMLURL_H #endif #ifndef HTMLATTR_H #include "htmlattr.h" #endif #ifndef HTMLRC_H #include "htmlrc.h" #endif #ifndef HTML_OS_H #include "html_os.h" #endif #ifndef HTMLTAGS_H #include "htmltags.h" #endif /* ------------------------------------------------------------------------ */ /* * Display Site implementation mix-in */ /* * create */ CHtmlDispDisplaySite::CHtmlDispDisplaySite(CHtmlSysWin *win, const CHtmlRect *pos, CHtmlResCacheObject *image) { /* remember our window and position in the window */ win_ = win; posp_ = pos; /* no timer yet */ timer_ = 0; /* remember our image, and set a reference if it's valid */ site_image_ = image; if (image != 0) { /* add a reference */ image->add_ref(); /* if the image is valid, register as its display site */ if (image->get_image() != 0) image->get_image()->set_display_site(this); } } /* * destroy */ CHtmlDispDisplaySite::~CHtmlDispDisplaySite() { /* unset our window reference */ unset_win(); /* if we have an image, remove our reference to it */ if (site_image_ != 0) { /* remove myself as the image's display site */ if (site_image_->get_image() != 0) site_image_->get_image()->set_display_site(0); /* remove our reference */ site_image_->remove_ref(); } } /* * unset our window reference */ void CHtmlDispDisplaySite::unset_win() { /* if we have a timer, delete it */ if (win_ != 0 && timer_ != 0) { win_->delete_timer(timer_); timer_ = 0; } /* forget our window reference */ win_ = 0; } /* * Cancel playback. If our image is an animation, we'll stop animating it. */ void CHtmlDispDisplaySite::cancel_playback() { /* if we have an image resource, tell it to halt playback */ if (site_image_ != 0 && site_image_->get_image() != 0) site_image_->get_image()->cancel_playback(); } /* * Set the image being displayed in the site. */ void CHtmlDispDisplaySite::set_site_image(CHtmlResCacheObject *image) { /* if we have an outgoing image, release it */ if (site_image_ != 0) { /* we're no longer the display site for this image */ if (site_image_->get_image() != 0) { /* pause the playback, in case it's animated */ site_image_->get_image()->pause_playback(); /* unset the display site */ site_image_->get_image()->set_display_site(0); } /* remove our reference to the outgoing image */ site_image_->remove_ref(); } /* remember the new image */ site_image_ = image; /* if we have a new image, attach to it */ if (image != 0) { /* add a reference to the new image */ image->add_ref(); /* register as the image's display site */ if (image->get_image() != 0) { /* resume playback, if it's animated */ site_image_->get_image()->resume_playback(); /* set the display site */ image->get_image()->set_display_site(this); } } } /* * CHtmlSysImageDisplaySite implementation: invalidate an area of the * display site */ void CHtmlDispDisplaySite::dispsite_inval(unsigned int x, unsigned int y, unsigned int width, unsigned int height) { /* if we have a window, invalidate the desired area */ if (win_ != 0) { CHtmlRect area; /* * set up the area - start with the relative coordinates within our * display area, then offset the rectangle by our own document * coordinates to get the actual document coordinates in the window * of the sub-area to be invalidated */ area.set(x, y, x + width, y + height); area.offset(posp_->left, posp_->top); /* invalidate the area of our window */ win_->inval_doc_coords(&area); } } /* * Callback for our timer event */ void CHtmlDispDisplaySite::img_timer_cb(void *ctx) { /* * the context is the CHtmlSysImageAnimated object that wants the timer * event notification */ ((CHtmlSysImageAnimated *)ctx)->notify_timer(); } /* * CHtmlSysImageDisplaySite implementation: set a timer event */ void CHtmlDispDisplaySite::dispsite_set_timer(unsigned long delay_ms, CHtmlSysImageAnimated *image) { /* if we have a window, set up our timer */ if (win_ != 0) { /* if we don't have a timer yet, create one */ if (timer_ == 0) timer_ = win_->create_timer(&img_timer_cb, image); /* if we managed to create a timer, set it */ if (timer_ != 0) win_->set_timer(timer_, delay_ms, FALSE); } } /* * CHtmlSysImageDisplaySite implementation: cancel the timer */ void CHtmlDispDisplaySite::dispsite_cancel_timer(CHtmlSysImageAnimated *) { /* if we have a window and a timer, delete the timer */ if (win_ != 0 && timer_ != 0) { /* delete the timer */ win_->delete_timer(timer_); /* forget it, now that we've deleted it */ timer_ = 0; } } /* ------------------------------------------------------------------------ */ /* * Basic display object implementation */ /* * Memory allocation - use the formatter for memory management */ void *CHtmlDisp::operator new(size_t siz, CHtmlFormatter *formatter) { void *mem; htmldisp_heapid_t heap_id; /* * increase the size by one byte (bumped to the proper system * alignment boundary), so we can store the heap ID at the first * byte of each block we allocate; we'll need to identify the heap * source when it comes time to delete the block */ siz += os_align_size(1); /* see if we have a formatter */ if (formatter != 0) { /* we have a formatter - ask it for the memory from its heap */ mem = formatter->heap_alloc(siz); heap_id = HTMLDISP_HEAPID_FMT; } else { /* no formatter - get the memory from the system heap */ mem = ::operator new(siz); heap_id = HTMLDISP_HEAPID_SYS; } /* store the heap ID in the first byte of the block we allocated */ *(char *)mem = (char)heap_id; /* * skip past the prefix, so that the caller sees the part after our * heap ID entry, which is for our use only */ mem = (void *)(((char *)mem) + os_align_size(1)); /* return the memory */ return mem; } void CHtmlDisp::operator delete(void *ptr) { /* * adjust the memory to point to our original block, including the * prefix */ ptr = (void *)(((char *)ptr) - os_align_size(1)); /* check the prefix, to determine where the memory came from */ switch(*(char *)ptr) { case HTMLDISP_HEAPID_FMT: /* * it's in the formatter's heap -- ignore the request, since the * formatter will take care of deleting the object when it * resets its heap, and doesn't free individual objects */ break; case HTMLDISP_HEAPID_SYS: /* * it came from the system heap - delete the memory as normal */ ::operator delete(ptr); break; } } /* * measure for table metrics */ void CHtmlDisp::measure_for_table(CHtmlSysWin *win, CHtmlTableMetrics *metrics) { int mywid = measure_width(win); /* if there's a no-break flag before me, clear out the leftover */ if (!metrics->no_break_ && !metrics->no_break_sect_) metrics->clear_leftover(); /* add to the running leftover space */ metrics->leftover_width_ += mywid; /* allow breaking immediately after this item */ metrics->last_char_ = ' '; metrics->no_break_ = FALSE; } /* * find the next line break */ CHtmlDisp *CHtmlDisp::find_line_break(CHtmlFormatter *formatter, CHtmlSysWin *win, long space_avail, CHtmlLineBreak *break_pos) { int mywid = measure_width(win); /* * Set the "previous character" to a space, so that any text item * that immediately follows can put a line break between this item * and the text item if necessary. */ break_pos->last_char_ = ' '; /* * By default, we can't break up a display object across two lines. * So, if we don't fit into the available space, break at the start of * this object (that is, end the current line just before this object, * and put this object on the next line). Note that we ignore the * previous character in making this determination, since this default * implementation is for objects that don't contain any text, hence we * can always set this object on a new line even if it was immediately * preceded by non-breakable text. Note also that we ignore prv and * prv_pos, since these are used when an object can't be separated from * its previous object, and by default we will allow a break just * before this object. However, we cannot break on this item if we * have an explicit no-break flag just before this item. */ if (space_avail < mywid) { /* * if we're allowed to break before me, break before me; otherwise, * if we have a prior break position, break there */ if (!break_pos->no_break_) return this; else if (break_pos->item_ != 0) return break_pos->item_->do_line_break(formatter, win, break_pos); } /* * we're not an explicit no-break element, and we can break to the * right of this item if necessary: clear the no-break indication */ break_pos->no_break_ = FALSE; /* we're not whitespace */ break_pos->note_non_ws(); /* return null to indicate that we didn't break the line */ return 0; } /* * Do a line break at a position previously noted. The default * implementation never notes a previous position for a break, so this * routine should never be called. */ CHtmlDisp *CHtmlDisp::do_line_break(CHtmlFormatter *, CHtmlSysWin *, CHtmlLineBreak *) { /* by default, just break at the start of this item */ return this; } /* * Do a line break after skipping whitespace in a break position we * previously found in an item before us. */ CHtmlDisp *CHtmlDisp::do_line_break_skipsp(CHtmlFormatter *, CHtmlSysWin *) { /* by default, just break at the start of this item */ return this; } /* * Delete this item and all following items in its list */ void CHtmlDisp::delete_list(CHtmlDisp *head) { CHtmlDisp *cur; CHtmlDisp *nxt; /* loop through the list */ for (cur = head ; cur != 0 ; cur = nxt) { /* * remember the next item, since we're about to delete the * current one, which will invalidate its forward link pointer */ nxt = cur->get_next_disp(); /* delete the current item */ delete cur; } } /* * invalidate my area on the display */ void CHtmlDisp::inval(CHtmlSysWin *win) { win->inval_doc_coords(&pos_); } /* * Insert a list after another display object in a list. This adds me * and any other items following me in my list. */ void CHtmlDisp::add_list_after(CHtmlDisp *prv) { CHtmlDisp *list_next; CHtmlDisp *my_last; /* * remember the next item after the previous item in the list into * which we're inserting */ list_next = prv->nxt_; /* add me right after the previous item in the list */ prv->nxt_ = this; /* find the last item in my list */ for (my_last = this ; my_last->nxt_ != 0 ; my_last = my_last->nxt_) ; /* * set the next item after the last item in my list to point to the * next item in the list into which we're inserting me */ my_last->nxt_ = list_next; } /* * Receive notification that we've been moved into the display list from * the deferred list. If we're aligned left or right, we can't be added * to the display list until the start of a new line; at that point, we * need to float to the appropriate margin and adjust the text margins to * exclude the space we occupy. */ void CHtmlDisp::basic_format_deferred(class CHtmlSysWin *win, class CHtmlFormatter *formatter, long line_spacing, HTML_Attrib_id_t align, long height) { long wid; long disp_wid; long ypos; long left_margin, right_margin; /* get the total horizontal space we need */ wid = measure_width(win); /* get the total size of the window */ disp_wid = win->get_disp_width(); /* get the current y position, and adjust for line spacing */ ypos = formatter->getcurpos().y + line_spacing; /* get the current margin settings */ left_margin = formatter->get_left_margin(); right_margin = formatter->get_right_margin(); /* float over to the appropriate margin */ switch(align) { case HTML_Attrib_left: /* set my position at the current left margin */ pos_.set(left_margin, ypos, left_margin + wid, ypos + height); /* * move left margin past me; if we take up more than the available * space, though, simply move the left margin into the right * margin */ if (wid <= disp_wid - left_margin - right_margin) left_margin += wid + 1; else left_margin = disp_wid - right_margin - 1; break; case HTML_Attrib_right: /* * set my position at the current right margin, but in any case no * further left than the left margin, if we're too wide to fit * between the margins */ if (wid < disp_wid - left_margin - right_margin) { /* I fit between the margins - align flush right */ pos_.set(disp_wid - right_margin - wid, ypos, disp_wid - right_margin, ypos + height); /* move right margin before me */ right_margin += wid + 1; } else { /* I don't fit - align left */ pos_.set(left_margin, ypos, left_margin + wid, ypos + height); /* there's no room left after me */ right_margin = disp_wid - left_margin - 1; } break; default: /* we're not floating, so there's nothing we need to do */ return; } /* tell the formatter to flow text around this item */ formatter->begin_flow_around(this, left_margin, right_margin, pos_.bottom); } /* * Get the hyperlink associated with the item. */ CHtmlDispLink *CHtmlDisp::get_link( CHtmlFormatter *formatter, int /*x*/, int /*y*/) const { /* * Ordinary items aren't linkable themselves, so we have no direct link * to return. However, we can inherit a division link from an * enclosing DIV tag. So look for an inherited DIV link and return * what we find. */ return find_div_link(formatter); } /* * Get the hyperlink object associated with the item. Most display items * aren't directly linkable; however, an item can be in a division that has * special hyperlink behavior, so we need to look at the containing * divisions for hyperlink references. */ CHtmlDispLinkDIV *CHtmlDisp::find_div_link(CHtmlFormatter *formatter) const { CHtmlDispDIV *div; /* * Since the base display item type isn't directly linkable, we have no * link object of our own. However, we can inherit a special link * object from a containing division. So, check to see if we're in a * division, and if so look to see if it has a hyperlink. If we have a * division but it doesn't have a hyperlink, check the DIV's parents as * well. */ for (div = formatter->find_div_by_ofs(get_text_ofs(), pos_.top) ; div != 0 ; div = div->get_parent_div()) { CHtmlDispLinkDIV *link; /* if this division has a DIV-level hyperlink, return it */ if ((link = div->get_div_link()) != 0) return link; } /* there's no hyperlink associated with this object */ return 0; } /* ------------------------------------------------------------------------ */ /* * DIV display item */ /* * Get the hyperlink object associated with the item. */ CHtmlDispLink *CHtmlDispDIV::get_link( CHtmlFormatter *formatter, int x, int y) const { const CHtmlDispDIV *div; /* * look for a link associated with this DIV or any enclosing DIV - the * innermost link takes precedence */ for (div = this ; div != 0 ; div = div->get_parent_div()) { CHtmlDispLink *link; /* if this division has a hyperlink, return it */ if ((link = div->get_div_link()) != 0) return link; } /* we didn't find a link */ return 0; } /* ------------------------------------------------------------------------ */ /* * DIV hyperlink */ /* * Construction. We're not a real link, so we can supply suitable dummy * parameters for the underlying CHtmlDispLink construction. Note that we * use the 'hidden' style - this ensures that we'll add attributes (such as * underlines) on hover only when those attributes are explicitly requested * in the DIV HOVER=LINK(...) attribute. */ CHtmlDispLinkDIV::CHtmlDispLinkDIV(CHtmlDispDIV *div) : CHtmlDispLink(LINK_STYLE_HIDDEN, FALSE, FALSE, 0, 0, 0) { /* remember my DIV display item */ div_ = div; /* link the display item back to me */ div->set_div_link(this); } /* set the clicked state */ void CHtmlDispLinkDIV::set_clicked(CHtmlSysWin *win, int clicked) { CHtmlDisp *cur; CHtmlDisp *tail; CHtmlDispLink *prv_link; /* * For our purposes, we only care about hovering. Treat 'clicked' as * hovering, since the mouse is over the link in the clicked state. */ clicked = (clicked != 0 ? CHtmlDispLink_hover : 0); /* if the state isn't changing, there's nothing to do */ if (clicked_ == clicked) return; /* remember the new state */ clicked_ = clicked; /* * For child links, we want to let them know that it's their parent DIV * that we're hovering over, not the link itself. So, notify them that * their new clicked state is DIV-hover, not direct hover. Use * DIV-hover mode if the child is in either 'hover' or 'clicked' mode, * because in either case the mouse is over the child. */ clicked = (clicked != 0 ? CHtmlDispLink_divhover : 0); /* * run through all the links within the DIV and set their state to our * new state */ for (cur = get_next_disp(), tail = div_->get_div_tail(), prv_link = 0 ; cur != 0 && cur != tail ; cur = cur->get_next_disp()) { CHtmlDispLink *link; /* get this item's link container */ link = cur->get_container_link(); /* * if we found a link, and it's different from the last one we * found, update this link */ if (link != 0 && link != prv_link) { /* * set the new state for the link and its subitems (we * obviously don't want to go back and notify the division, * since we'd come right back here and recurse forever) */ link->set_clicked_sub(win, clicked); /* * remember this as the previous link - this will ensure that * we don't repeatedly set the same item's state if there are * several items within the same ... section */ prv_link = link; } } } /* ------------------------------------------------------------------------ */ /* * Line-wrapping marker implementation */ /* measure in a table */ void CHtmlDispNOBR::measure_for_table(CHtmlSysWin *, CHtmlTableMetrics *metrics) { /* set the no-break-section flag according to our status */ metrics->no_break_sect_ = nobr_; } /* ------------------------------------------------------------------------ */ /* * Link object implementation */ /* * set the clicked state, and invalidate the screen area of all linked * items */ void CHtmlDispLink::set_clicked(CHtmlSysWin *win, int clicked) { CHtmlDispLinkDIV *div_link; /* if the state hasn't changed, there's nothing to do */ if (clicked_ == clicked) return; /* * if we're part of a division, and the 'hover' part of the clicked * state is changing, notify the division of the change as well */ if ((div_link = find_div_link(win->get_formatter())) != 0) { /* notify the enclosing division */ div_link->set_clicked(win, clicked); /* * keep the div-hover mode if we set it in the DIV call (the DIV * will have looped back and set our mode to div-hover if * applicable; we want to keep that in case we're not hovering * directly any longer) */ clicked |= (clicked_ & CHtmlDispLink_divhover); } /* set the new state for myself and my linked sub-items */ set_clicked_sub(win, clicked); } /* * Set the clicked state for myself and my linked sub-items. This routine * doesn't notify the enclosing DIV - it only affects my sub-items. */ void CHtmlDispLink::set_clicked_sub(CHtmlSysWin *win, int clicked) { CHtmlDisp *disp; /* * ignore the 'clicked-off' state - this is equivalent to no * highlighting, or div-hover if there's an enclosing division that * reveals links */ clicked &= ~CHtmlDispLink_clickedoff; /* remember the new state */ clicked_ = clicked; /* * invalidate all of the linkable items in my list, since they may * have a different appearance when clicked */ for (disp = first_ ; disp != 0 ; disp = disp->get_next_disp()) { /* * if this item is linked to me, invalidate it; don't bother * with items that aren't linked to me (which should simply mean * that they're not linkable at all - everything in my list * that's linkable at all should be linked to me), since their * screen appearance isn't affected by this change */ if (disp->get_container_link() == this) disp->on_click_change(win); /* if this was the last one, we're done */ if (disp == last_) break; } } #if 0 // removed /* ------------------------------------------------------------------------ */ /* * CHtmlDispDelegator implementation */ /* * Delegate the result of a line break. Let the underlying item find * its own line break, then redirect the return value accordingly: if * the underlying item breaks at its own start, return myself, since I * always cover the underlying item. If the underlying item creates a * new item, create a clone of myself to cover the new item. */ CHtmlDisp *CHtmlDispDelegator:: delegate_line_break(CHtmlFormatter *formatter, CHtmlDisp *line_start, CHtmlLineBreak *break_pos) { /* * if they inserted themselves as the next line break object, * translate that to me */ if (break_pos->item_ == delegate_) break_pos->item_ = this; /* if it returned null, return null */ if (line_start == 0) return 0; /* if it returned itself, return me, since I always cover it */ if (line_start == delegate_) return this; /* * it must have created a new item - we need to create a new * delegator (of my same type) to cover the newly created item */ return clone_delegator(formatter, line_start); } #endif // removed /* ------------------------------------------------------------------------ */ /* * "Body" display object */ /* * animated image display site - invalidate */ void CHtmlDispBody::dispsite_inval(unsigned int x, unsigned int y, unsigned int width, unsigned int height) { /* ask the window to invalidate the background image area */ if (win_ != 0) win_->inval_html_bg_image(x, y, width, height); } /* ------------------------------------------------------------------------ */ /* * text display object implementation */ CHtmlDispText::CHtmlDispText(CHtmlSysWin *win, CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs) { CHtmlPoint siz; int ascent; /* set members */ font_ = font; txt_ = txt; len_ = (unsigned short)len; displen_ = (unsigned short)len; txtofs_ = txtofs; /* measure the size */ siz = win->measure_text(font, txt, len, &ascent); pos_.set(0, 0, siz.x, siz.y); ascent_ht_ = (unsigned short)ascent; } /* * additional construction-time initialization for linked text */ void CHtmlDispText::linked_text_cons(CHtmlSysWin *win, CHtmlDispLink *link) { CHtmlSysFont *font; CHtmlPoint siz; int ascent; /* tell the link object about us */ link->add_link_item(this); /* * get the font we use for drawing as a link in 'hover' and 'clicked' * state - this will ensure that we'll leave space for any * ornamentation (such as underlining) that we apply in our fully * ornamented mode */ font = get_draw_text_font(win, link, CHtmlDispLink_clicked | CHtmlDispLink_hover); /* calculate our *real* size, now that we have the proper font */ siz = win->measure_text(font, txt_, len_, &ascent); pos_.bottom = pos_.top + siz.y; ascent_ht_ = (unsigned short)ascent; } /* * draw the text */ void CHtmlDispText::draw(CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, int clicked) { /* draw the text normally (normal text is never linked) */ draw_text(win, sel_start, sel_end, 0, clicked, &pos_); } /* * draw the text, with appropriate style settings */ void CHtmlDispText::draw_text(CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, class CHtmlDispLink *link, int clicked, const CHtmlRect *orig_pos) { const textchar_t *p; size_t rem; unsigned long ofs; CHtmlRect pos; CHtmlSysFont *font; /* start at the beginning of the string */ p = txt_; rem = displen_; ofs = txtofs_; pos = *orig_pos; /* get the font, adjusting for the link appearance if necessary */ font = get_draw_text_font(win, link, clicked); /* * as an optimization, if we're entirely normal or entirely * highlighted, draw in one piece -- this should go faster than the * general routine, since we don't have to measure the text again * (measuring requires a system call) */ if (ofs >= sel_end || ofs + displen_ <= sel_start || (ofs >= sel_start && ofs + displen_ <= sel_end)) { /* draw all of our text with the appropriate highlighting */ win->draw_text(ofs >= sel_start && ofs + displen_ <= sel_end, pos.left, pos.top, font, txt_, displen_); /* we're done - there's nothing more to draw */ return; } /* * If we contain the starting point, draw up to the starting point * in normal color */ if (ofs < sel_start) draw_text_part(win, FALSE, &pos, &p, &rem, &ofs, (unsigned long)(sel_start - ofs), font); /* * If we contain the ending point, draw up to the ending point in * highlight color */ if (ofs >= sel_start && ofs < sel_end) draw_text_part(win, TRUE, &pos, &p, &rem, &ofs, sel_end - ofs, font); /* * If there's anything left after the ending point, draw the * remainder in normal color */ if (rem > 0) draw_text_part(win, FALSE, &pos, &p, &rem, &ofs, rem, font); } /* * Get the font to use when drawing our text. We'll use our current font, * adjusting it if needed for the hyperlink appearance. */ CHtmlSysFont *CHtmlDispText::get_draw_text_font(CHtmlSysWin *win, class CHtmlDispLink *link, int clicked) { CHtmlSysFont *font; int link_style = (link != 0 ? link->get_style() : LINK_STYLE_PLAIN); /* start with the current font */ font = font_; /* * If we're drawing a link, add the appropriate color and underlining * style for link text to the font. We're drawing this as a link if * either we have the "normal" link style and links are being shown, OR * we have the "forced" link style, OR we have the "hidden" link style * and the link is in 'hover' mode and links are being shown. (The * "forced" style overrides any global link visibility setting, so we * use the link appearance in this case regardless of the global * settings.) */ if (link_style == LINK_STYLE_FORCED || (link_style == LINK_STYLE_NORMAL && win->get_html_show_links()) || (link_style == LINK_STYLE_HIDDEN && (clicked & (CHtmlDispLink_hover | CHtmlDispLink_clicked)) != 0 && win->get_html_show_links())) { CHtmlFontDesc font_desc; /* * get the current font's description, so that we can add the * hyperlink rendering features to the attributes inherited from * the surrounding text */ font_->get_font_desc(&font_desc); /* * Get the font color. If the containing font has a non-default * color, it means that we have an explicit inside * the tag, so use the font's color setting. If the containing * font uses the default color, then use the appropriate color * depending on the link's status. */ if (font_desc.default_color) { /* * the font doesn't have an explicit color setting, so use the * appropriate hyperlink text color according to the current * status of the link (normal, clicked, or hovering) */ font_desc.color = ((clicked & CHtmlDispLink_clicked) != 0 ? win->get_html_alink_color() : ((clicked & CHtmlDispLink_hover) != 0 ? win->get_html_hlink_color() : win->get_html_link_color())); /* note that we now have an explicitly-set color */ font_desc.default_color = FALSE; } /* * If we're hovering or clicked, and we have explicit hover colors, * apply them. Any explicit hover colors override the color we've * come up with so far. */ if ((clicked & (CHtmlDispLink_hover | CHtmlDispLink_clicked)) != 0 && link != 0) { HTML_color_t c; /* apply the explicit foreground hover color, if set */ if (link->get_hover_fg(&c)) { font_desc.color = c; font_desc.default_color = FALSE; } /* apply the explicit background hover color, if set */ if (link->get_hover_bg(&c)) { font_desc.bgcolor = c; font_desc.default_bgcolor = FALSE; } /* add underlining, if set */ if (link->hover_underline()) font_desc.underline = TRUE; } /* * Underline the font if appropriate. For normal and FORCED links, * add the underline if the window style says so. For HOVER links, * don't add the underline - we'll let the HOVER style determine * that. For PLAIN links, don't add the underline. Note that we * never take away an underline if it's inherited from enclosing * text. */ if ((link_style == LINK_STYLE_NORMAL || link_style == LINK_STYLE_FORCED) && win->get_html_link_underline()) font_desc.underline = TRUE; /* get a new font matching the modified description */ font = win->get_font(&font_desc); } /* return the font we decided upon */ return font; } /* get our character set */ int CHtmlDispText::get_charset(oshtml_charset_id_t &c) const { if (font_ != 0) { c = font_->get_charset(); return TRUE; } else return FALSE; } /* * Draw a portion of the text, with optional selection highlighting, * from a given point in the string, for a given maximum length. We'll * update the string pointer (*p), length remaining pointer (*rem), * offset pointer (*ofs), and position pointer (*pos), according to how * much we draw; we'll draw the lesser of *rem and len. */ void CHtmlDispText::draw_text_part(CHtmlSysWin *win, int hilite, CHtmlRect *pos, const textchar_t **p, size_t *rem, unsigned long *ofs, unsigned long len, CHtmlSysFont *font) { /* limit drawing to the lesser of *rem and len */ if (len > *rem) len = *rem; /* draw it */ win->draw_text(hilite, pos->left, pos->top, font, *p, len); /* * update the counters according to what we drew; as an * optimization, we don't need to update anything but the remaining * length pointer (*rem) if there's nothing left, since we won't be * doing any more drawing on this string in that case */ *rem -= len; if (*rem != 0) { /* move right by the size of the text we drew */ pos->left += win->measure_text(font, *p, len, 0).x; /* update the string pointer and the offset */ *p += len; *ofs += len; } } /* * get the horizontal extent of the text */ int CHtmlDispText::measure_width(CHtmlSysWin *) { /* return the horizontal extent of the text in the current font */ return pos_.right - pos_.left; } /* * invalidate a range */ void CHtmlDispText::inval_range(CHtmlSysWin *win, unsigned long start_ofs, unsigned long end_ofs) { unsigned long ofs; size_t len; CHtmlRect rc; /* if the range is outside of my text, do nothing */ if (end_ofs <= txtofs_ || start_ofs >= txtofs_ + len_) return; /* start off with my entire area and range */ rc = pos_; ofs = 0; len = len_; /* if the starting point is within my text, set the left limit */ if (start_ofs > txtofs_) { /* skip to the starting offset */ ofs = start_ofs - txtofs_; /* limit the length */ len -= ofs; /* adjust the left end for the offset */ rc.left = get_text_pos(win, txtofs_ + ofs).x; } /* if the ending point is within my text, set the right limit */ if (end_ofs < txtofs_ + len_) { /* reduce the length */ len -= txtofs_ + len_ - end_ofs; /* adjust the right end for the offset */ rc.right = get_text_pos(win, txtofs_ + ofs + len).x; } /* * Adjust the range by a few pixels at each side for character * overhand. (We really should get the actual text metrics here, * but we'll put that off for now and just use a few pixels of slop * instead.) */ if (rc.left < 3) rc.left = 0; else rc.left -= 3; rc.right += 3; /* invalidate the area */ win->inval_doc_coords(&rc); } /* * measure for table metrics */ void CHtmlDispText::measure_for_table(CHtmlSysWin *win, CHtmlTableMetrics *metrics) { const textchar_t *last_break_pos; const textchar_t *p; const textchar_t *nxt; textchar_t prev_char; size_t rem; oshtml_charset_id_t charset; /* note our character set */ charset = font_->get_charset(); /* start at our first character */ p = txt_; rem = len_; /* * the last character of the previous item is the initial previous * character */ prev_char = metrics->last_char_; /* * if we have a no-break flag just before us, or we're in a * section, skip our first character */ if ((metrics->no_break_ || metrics->no_break_sect_) && len_ != 0) { /* skip our first character, since we can't break there */ nxt = os_next_char(charset, p, rem); rem -= (nxt - p); p = nxt; } /* * check to see if we can break just after the previous item - that * is, immediately to the left of this item */ if (len_ != 0 && !metrics->no_break_sect_ && allow_line_break_after(metrics->last_char_, *txt_)) { /* * We can break just before our first character - add any trailing * space from the previous item, and start at the beginning of our * line. */ last_break_pos = txt_; /* add our width up to this point to the leftover */ metrics->leftover_width_ += win->measure_text(font_, txt_, p - txt_, 0).x; /* we don't need to use the leftover width */ metrics->clear_leftover(); } else { /* * We can't break at our first character - we don't have a break * position established yet */ last_break_pos = 0; } /* * Run through the line, find word breaks, and measure unbreakable * stretches of text (words in word-wrap mode, characters in * character-wrap mode). */ for ( ; rem != 0 ; rem -= (nxt - p), p = nxt) { /* note the location of the next character after this one */ nxt = os_next_char(charset, p, rem); /* * we can break here if it's a space, or the previous character * is a hyphen and this character isn't a hyphen */ if (!metrics->no_break_sect_ && allow_line_break_at(prev_char, *p)) { long wid; /* measure the word if there's anything to measure */ if (p != last_break_pos) { /* * Figure the size of the current word. If there's a * previous break position, it's the width from the * previous break position to the current position; * otherwise, it's the width of the start of the item to * the current position plus the leftover from the * previous item. */ if (last_break_pos != 0) { /* measure the width from the last break position */ wid = win-> measure_text(font_, last_break_pos, (size_t)(p - last_break_pos), 0).x; } else { /* * measure the width from the start of the line to * the current position, and add the leftover width * from the previous item */ wid = win->measure_text(font_, txt_, (size_t)(p - txt_), 0).x + metrics->leftover_width_; /* we've now consumed the leftover width */ metrics->leftover_width_ = 0; } /* add this word to the metrics as an indivisible item */ metrics->add_indivisible(wid); /* add the word to the line total */ metrics->add_to_cur_line(wid); } /* add the separator to the line total */ wid = win->measure_text(font_, p, nxt - p, 0).x; metrics->add_to_cur_line(wid); /* remember the next character as the most recent break */ last_break_pos = nxt; } /* remember the previous character */ prev_char = *p; } /* * If there aren't any break positions within this item, add my * entire width to the leftover total. Otherwise, add the remainder * from the last break position to the leftover total. */ if (last_break_pos == 0) metrics->leftover_width_ += measure_width(win); else if (last_break_pos < txt_ + len_) metrics->leftover_width_ += win->measure_text(font_, last_break_pos, (size_t)(len_ - (last_break_pos - txt_)), 0).x; /* remember my last character, if I have any characters */ if (len_ != 0) metrics->last_char_ = prev_char; } /* * Find the next line break. This routine is a little complicated, * mostly because it's important that it goes as fast as possible. Our * strategy is to first determine if we can fit in the allotted space; * if so, we'll simply find the last break position within our text and * return. Otherwise, we'll figure out how many of our characters will * fit by asking the operating system to do the measurement, then we'll * find the last breakable position before that size. */ CHtmlDisp *CHtmlDispText::find_line_break(CHtmlFormatter *formatter, CHtmlSysWin *win, long space_avail, CHtmlLineBreak *break_pos) { size_t fit_cnt; size_t cur_cnt; textchar_t prev_char; const textchar_t *p; oshtml_charset_id_t charset; /* get our font's character set */ charset = font_->get_charset(); /* determine how much of our text will fit in the available space */ if (space_avail <= 0) { /* we obviously can't fit anything into zero or negative space */ fit_cnt = 0; } else if (space_avail == 0) { /* * there's no space available, so we can't fit anything; but since * the preceding material fits exactly, we can check for a break * at the current position */ fit_cnt = 0; } else if (space_avail >= pos_.right - pos_.left) { /* the available space is bigger than we are, so we fit entirely */ fit_cnt = len_; } else { /* * we're bigger than the available space, so calculate how much of * our text (starting from the left) will fit in the available * space */ fit_cnt = win->get_max_chars_in_width(font_, txt_, len_, space_avail); } /* start scanning at the start of our text */ prev_char = break_pos->last_char_; p = txt_; cur_cnt = 0; /* * store the last character of this item in the break state, so that * the next item can determine if it can break at its start (if we * don't have any text at all, don't change the previous character, * since we didn't add anything) */ if (len_ != 0) break_pos->last_char_ = get_break_pos_last_char(); /* * if an explicit no-break flag immediately precedes us, then skip out * first character, since we can't break at our first character if * we're explicitly told not to */ if (break_pos->no_break_ && cur_cnt < len_) { /* note the initial character in the break tracker */ break_pos->note_char(*p, this, (void *)p); /* we can't break at the first character, so skip it */ prev_char = *p; p = os_next_char(charset, p, len_ - cur_cnt); cur_cnt = p - txt_; } /* * If we allow breaking after the previous character, and we have at * least one more character, we can break at the current position. * * We have to check this case specially because of the possibility of * a no-break flag preceding us: we'll have skipped our first * character, because we can't break to its left, but if we have at * least two characters then we might still be able to break to the * right of the first character. In the main loop, we only check to * see if we can break to the left of the current character, so we * must check specially for breaking to the right of the previous * character on the first iteration only. */ if (cur_cnt < len_ && allow_line_break_after(prev_char, *p)) { /* * Tell the break tracker that we can break at the current * position. * * Note that we haven't yet told the break tracker about the * current character (what's at *p), because we're breaking to the * LEFT of the current character - thus the only thing that * matters to the break tracker is what comes before the current * character. */ break_pos->set_break_pos(this, (void *)p); } /* * If we have non-zero length, clear out the no-break flag in the * break state, as we are not an explicit no-break indicator. (If * we're empty, we have no effect on the no-break state, as we do not * contribute any text that would allow breaking.) */ if (len_ != 0) break_pos->no_break_ = FALSE; /* scan our text for breaking points */ for ( ; cur_cnt < len_ ; p = os_next_char(charset, p, len_ - cur_cnt), cur_cnt = p - txt_) { /* note the current character */ break_pos->note_char(*p, this, (void *)p); /* check to see if we allow breaking here */ if (allow_line_break_at(prev_char, *p)) { /* note the break position */ break_pos->set_break_pos(this, (void *)p); } /* * If we've at least reached the first character past what will * fit, and we've found a valid breaking point somewhere up to * this point, stop here and break the line at the latest breaking * point. We know we can't fit anything more in the available * space, so we have to go back to the most recent breaking point * and break the line there. * * If we haven't yet reached the maximum fit point, keep looking, * since we might find another, better breakpoint later in our * text. */ if (cur_cnt >= fit_cnt && break_pos->item_ != 0) { /* break at the most recent valid break point */ return break_pos->item_->do_line_break(formatter, win, break_pos); } /* remember the previous character for the next round */ prev_char = *p; } /* * If we got this far, it either means that we've scanned this entire * item and we still haven't found a breaking point, or that we didn't * need a breaking point because this item entirely fits. * * If we've scanned past the maximum fit size, and we have found a * break point, we now know that we can't do any better than that * previous break point, so break there. */ if (cur_cnt > fit_cnt && break_pos->item_ != 0) { /* * there's no break within this item, and we're past the maximum * fit point - go back and break at the previous item */ return break_pos->item_->do_line_break(formatter, win, break_pos); } /* return null to indicate that we didn't break the line */ return 0; } /* * Apply a line break position that we found in this object and noted, * before we moved on to another object. An object after us in the * display list will call back to us if it finds that it needs to do a * break but can't find a suitable spot later than the position we * already noted. */ CHtmlDisp *CHtmlDispText::do_line_break(CHtmlFormatter *formatter, CHtmlSysWin *win, CHtmlLineBreak *break_pos) { return break_at_pos(formatter, win, (const textchar_t *)break_pos->pos_); } /* * Apply a line break position that we previously found in a prior item, * skipping whitespace at the start of the new line. */ CHtmlDisp *CHtmlDispText::do_line_break_skipsp(CHtmlFormatter *formatter, CHtmlSysWin *win) { /* break from the start of this item */ return break_at_pos(formatter, win, txt_); } /* * Break the line into two pieces at a given position within our text */ CHtmlDisp *CHtmlDispText::break_at_pos(CHtmlFormatter *formatter, CHtmlSysWin *win, const textchar_t *last_break_pos) { CHtmlDispText *newdisp; CHtmlRect newpos; const textchar_t *p; size_t rem; oshtml_charset_id_t charset; /* note our character set */ charset = font_->get_charset(); /* * if the last break position contains a space, skip the leading * space, since this text will be at the start of a new line */ p = last_break_pos; rem = len_ - (p - txt_); while (rem != 0 && is_space(*p)) { const textchar_t *nxt = os_next_char(charset, p, rem); rem -= (nxt - p); p = nxt; } /* * If we're breaking at the beginning or end of the current item, * skip creation of an empty text item. */ if (rem == len_) { /* * my entire text will go into the new item, so this is * equivalent to just breaking at the start of this item */ return this; } else if (rem == 0) { /* * I'm going to break after all of my text, so this is equivalent * to breaking at the start of the next item. Make sure we skip * past any spaces before we select a breaking position. */ if (get_next_disp() != 0) return get_next_disp()->do_line_break_skipsp(formatter, win); else return 0; } /* * create a new text object for the part of the text after the last * break position, link it into the display list after the current * item, and return it - we'll break just before the new item. */ newdisp = create_disp_for_break(formatter, win, font_, p, rem, txtofs_ + len_ - rem); newdisp->add_after(this); /* adjust my size to remove the newly created area */ newpos = newdisp->get_pos(); pos_.right -= (newpos.right - newpos.left); /* keep only the text up to the new part */ len_ -= (unsigned short)rem; displen_ = len_; /* return the new object - it's the start of the next line */ return newdisp; } /* * Find trailing whitespace */ void CHtmlDispText::find_trailing_whitespace(CHtmlDisp_wsinfo *info) { const textchar_t *p; const textchar_t *nxt; size_t rem; oshtml_charset_id_t charset; /* note our character set */ charset = font_->get_charset(); /* scan for whitespace */ for (p = txt_, rem = len_ ; rem != 0 ; nxt = os_next_char(charset, p, rem), rem -= nxt - p, p = nxt) { /* check if this is whitespace */ if (is_space(*p)) { /* it's whitespace - note the start of a run of whitespace */ info->set_ws_pos(this, (void *)p); } else { /* it's not whitespace, so we're not in a run of whitespace */ info->clear_ws_pos(); } } } /* * Remove trailing whitespace from a position we previously found. */ void CHtmlDispText::remove_trailing_whitespace(CHtmlSysWin *win, void *pos) { /* check to see if the run of whitespace started in me */ if (pos != 0) { /* * The run of whitespace starts within this item. Calculate our * new length as the distance from the start of our text to the * first character of the trailing whitespace, and set the new * length - this will remove the whitespace from the display item. */ len_ = ((const textchar_t *)pos) - txt_; } else { /* * the run of whitespace started in a prior item - simply collapse * down to zero width */ len_ = 0; } /* display at the new length */ displen_ = len_; /* calculate our new display size */ pos_.right = pos_.left + win->measure_text(font_, txt_, len_, 0).x; } /* * Hide trailing whitespace */ void CHtmlDispText::hide_trailing_whitespace(CHtmlSysWin *win, void *pos) { /* check to see if the run of whitespace started in me */ if (pos != 0) { /* it's part of my text, so hide from the whitespace on */ displen_ = ((const textchar_t *)pos) - txt_; } else { /* it's not part of my text, so hide the entire thing */ displen_ = 0; } } /* * Measure width of trailing whitesapce */ long CHtmlDispText::measure_trailing_ws_width(CHtmlSysWin *win) const { const textchar_t *p; size_t rem; /* scan for whitespace */ for (p = txt_ + len_, rem = len_ ; rem != 0 ; --rem) { /* move to the next character */ --p; /* if it's not whitespace, we can stop looking */ if (!is_space(*p)) break; } /* if we didn't find any spaces, the width is zero */ if (rem == len_) return 0; /* measure the width of the spaces */ return win->measure_text(font_, p + 1, len_ - rem, 0).x; } /* * Get my text space needs */ int CHtmlDispText::get_text_height(CHtmlSysWin *) { /* return my space above the text baseline */ return ascent_ht_; } /* * Get my overall vertical space needs */ void CHtmlDispText::get_vertical_space(CHtmlSysWin *, int /*text_height*/, int *above_base, int *below_base, int *total) { const CHtmlFontDesc *desc; /* calculate the total height */ *total = (pos_.bottom - pos_.top); /* set the amount above the font baseline */ *above_base = ascent_ht_; /* * calculate the amount below the baseline as the total less the * amount above the baseline */ *below_base = *total - *above_base; /* * if I'm a superscript or a subscript, offset the positions * accordingly */ desc = font_->get_font_desc_ref(); if (desc->superscript) { *above_base += *total / 4; *below_base -= *total / 4; if (*below_base < 0) *below_base = 0; } else if (desc->subscript) { *above_base -= *total / 4; *below_base += *total / 4; if (*above_base < 0) *above_base = 0; } } /* * Set my position */ void CHtmlDispText::set_line_pos(class CHtmlSysWin *, int left, int top, int /*total_height*/, int text_base_offset, int /*max_text_height*/) { int total; const CHtmlFontDesc *desc; /* reset to my original display size */ displen_ = len_; /* measure the total height and the amount above the baseline we need */ total = (pos_.bottom - pos_.top); /* * We want to be aligned such that our text baseline matches the * text baselines of other text items on the line, so set my top to * my text height above the text baseline. */ pos_.offset(left - pos_.left, top - pos_.top + text_base_offset - ascent_ht_); /* * if I'm a superscript or a subscript, offset the positions * accordingly */ desc = font_->get_font_desc_ref(); if (desc->superscript) pos_.offset(0, -total/4); else if (desc->subscript) pos_.offset(0, total/4); } /* * Get the position of a character within this item */ CHtmlPoint CHtmlDispText::get_text_pos(CHtmlSysWin *win, unsigned long txtofs) { int wid; size_t ofs; /* get the offset, and make sure it's limited to our text */ ofs = (size_t)(txtofs - txtofs_); if (ofs > len_) ofs = len_; /* get the width of the text up to the given character */ if (ofs != 0) wid = win->measure_text(font_, txt_, ofs, 0).x; else wid = 0; /* * return my upper left position offset right by the width of the * characters up to the character of interest */ return CHtmlPoint(pos_.left + wid, pos_.top); } /* * Get the text offset of a character given its position */ unsigned long CHtmlDispText::find_textofs_by_pos(CHtmlSysWin *win, CHtmlPoint pos) const { size_t fit_cnt; /* * if it's outside my bounds, place it at the start or end of my * text run; if it's above or to the left or me, it's at the start, * otherwise at the end */ if (!pos_.contains(pos)) { /* see whether it's above/left or below/right */ if (pos.y <= pos_.top || pos.x <= pos_.left) return txtofs_; else return txtofs_ + len_; } /* figure out how much of our text is before the given position */ fit_cnt = win->get_max_chars_in_width(font_, txt_, len_, pos.x - pos_.left); /* if we're over halfway into the next character, skip ahead one */ if (fit_cnt + 1 <= len_) { /* note our character set */ oshtml_charset_id_t charset = font_->get_charset(); /* find byte length of the next character */ size_t clen = os_next_char(charset, txt_ + fit_cnt, len_ - fit_cnt) - (txt_ + fit_cnt); /* check to see if we're halfway into it */ if (pos.x > (pos_.left + win->measure_text(font_, txt_, fit_cnt, 0).x + win->measure_text(font_, txt_ + fit_cnt, clen, 0).x / 2)) { /* skip ahead to the next character */ fit_cnt += clen; } } /* return the text offset of the selected character */ return txtofs_ + fit_cnt; } /* * Add my text to a buffer, limiting the change to the given starting * and ending offsets */ void CHtmlDispText::add_text_to_buf_clip(CStringBuf *buf, unsigned long startofs, unsigned long endofs) const { size_t ofs; size_t len; /* * compute the starting offset within my buffer: if the desired * starting offset is after my starting point, offset my starting * point accordingly */ if (startofs > txtofs_) ofs = startofs - txtofs_; else ofs = 0; /* limit the length to the amount remaining */ if (ofs > len_) len = 0; else len = len_ - ofs; /* * if the desired ending offset is before my ending point, limit the * amount we'll copy accordingly */ if (endofs < txtofs_ + ofs) len = 0; else if (endofs < txtofs_ + ofs + len) len = endofs - txtofs_ - ofs; /* if we have anything left to copy, copy it now */ if (len > 0) buf->append(txt_ + ofs, len); } /* * Get the cursor type for a linked text item. This is a service routine * for our linked subclasses. */ Html_csrtype_t CHtmlDispText::get_cursor_type_for_link( class CHtmlSysWin *win, class CHtmlFormatter *formatter, int x, int y, int more_mode) const { /* * If links are disabled, or we're in "more" mode, return the default * I-beam cursor. If we have the "forced" style, we're visible even * when links are globally disabled, so show the link "hand" cursor * instead in this case. */ if (more_mode || (!win->get_html_show_links() && !get_link(formatter, x, y)->is_forced())) return HTML_CSRTYPE_IBEAM; /* use the hand cursor */ return HTML_CSRTYPE_HAND; } /* * Draw this text item as a link. This is a service routine for our * linked subclasses. */ void CHtmlDispText::draw_as_link(CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, CHtmlDispLink *link) { /* * If we're in the DIV-hover state, we're to draw in the hover mode * applicable to our linked DIV, not our own direct link. If we have * any other flags set, ignore the DIV-hover and use our direct link's * appearance. */ if (link->get_clicked() == CHtmlDispLink_divhover) { CHtmlDispDIV *div; /* find our immediate enclosing division */ div = win->get_formatter()->find_div_by_ofs(get_text_ofs(), pos_.top); /* find the nearest enclosing DIV that's in hover state */ for ( ; div != 0 ; div = div->get_parent_div()) { CHtmlDispLinkDIV *div_link; /* if this DIV has a hover-state link, it's the one */ if ((div_link = div->get_div_link()) != 0 && (div_link->get_clicked() & CHtmlDispLink_hover) != 0) { /* use this DIV link */ link = div_link; /* stop looking - just go draw */ break; } } } /* * Draw my text as a linked item, drawing it in the "clicked" style if * we're being clicked. If the link has the "plain" rendering style, * render as ordinary text unless we're being clicked. */ draw_text(win, sel_start, sel_end, link, link->is_plain() ? FALSE : link->get_clicked(), &pos_); } /* ------------------------------------------------------------------------ */ /* * Preformatted text item */ /* * Measure for table metrics. Performatted items can't be broken, so * this entire item must be added as an indivisible item. */ void CHtmlDispTextPre::measure_for_table(CHtmlSysWin *win, CHtmlTableMetrics *metrics) { /* * act like the base display item - we're indivisible, but we can * break to our left or right */ CHtmlDisp::measure_for_table(win, metrics); } /* * Find the next line break. Preformatted text doesn't allow line * breaks, since line breaks are part of the preformatting. */ CHtmlDisp *CHtmlDispTextPre::find_line_break(CHtmlFormatter *, CHtmlSysWin *win, long space_avail, CHtmlLineBreak *break_pos) { /* * If I don't fit in the given area, break at the start of this item. * However, don't allow breaking here if we have an explicit no-break * flag just before me. */ if (space_avail < measure_width(win) && !break_pos->no_break_) return this; /* * even if we contain whitespace, we don't count this as a run of * whitespace, since pre-formatted spaces aren't trimmable */ break_pos->last_char_ = 0; break_pos->note_non_ws(); /* ...but if something breakable follows, don't prevent a break there */ break_pos->no_break_ = FALSE; /* return null to indicate that we didn't break the line */ return 0; } /* ------------------------------------------------------------------------ */ /* * Text input display item */ CHtmlDispTextInput::CHtmlDispTextInput(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs) : CHtmlDispText(win, font, txt, len, txtofs) { textchar_t *newbuf; /* * make a private copy of our text buffer, so that if the * underlying text buffer changes, we can compare our buffer to the * new buffer */ newbuf = new textchar_t[len]; memcpy(newbuf, txt, len); txt_ = newbuf; /* we're the only thing in the our input line so far */ input_continuation_ = 0; } CHtmlDispTextInput::~CHtmlDispTextInput() { /* delete our private buffer */ delete [] (textchar_t *)txt_; } void CHtmlDispTextInput::diff_input_lists(CHtmlDispTextInput *other, class CHtmlSysWin *win) { const textchar_t *p1; const textchar_t *p2; size_t len; int did_inval; /* * Compare my buffer with the other one. If we find a difference, * start invalidating at the difference. */ for (did_inval = FALSE, len = 0, p1 = txt_, p2 = other->txt_ ; len < len_ && len < other->len_ ; ++p1, ++p2, ++len) { /* see if this character is different */ if (*p1 != *p2) { /* they differ - invalidate from here to the end of the line */ inval_eol(win, p1 - txt_); /* note that we did the invalidation */ did_inval = TRUE; /* * no need to continue checking, since we're redrawing the * entire rest of the line */ break; } } /* * If we didn't already invalidate the rest of the line, and if one * of the lists ran out before the other, invalidate from the * current position to the end of the line */ if (!did_inval && len_ != other->len_) { /* invalidate the one with more text remaining */ if (len_ > other->len_) inval_eol(win, p1 - txt_); else other->inval_eol(win, p2 - other->txt_); } /* if more items follow in both lists, diff the following items */ if (input_continuation_ != 0 && other->input_continuation_ != 0) { /* both have follow-on's - diff those */ input_continuation_-> diff_input_lists(other->input_continuation_, win); } else { /* * one ends here, the other doesn't - invalidate to the bottom * of the display */ inval_below(win); } } /* * invalidate the window from a given offset to the end of the line */ void CHtmlDispTextInput::inval_eol(CHtmlSysWin *win, int textofs) { CHtmlRect r; /* start with our own position */ r = pos_; /* advance past the given offset in the text */ r.left += win->measure_text(font_, txt_, textofs, 0).x; /* go all the way to the right */ r.right = HTMLSYSWIN_MAX_RIGHT; /* invalidate the area */ win->inval_doc_coords(&r); } /* * invalidate the window below this item to the bottom of the window */ void CHtmlDispTextInput::inval_below(CHtmlSysWin *win) { CHtmlRect r; /* start with our own position */ r = pos_; /* * go from my bottom to the bottom of the window, and all the way * across the line below */ r.left = 0; r.right = HTMLSYSWIN_MAX_RIGHT; r.top = r.bottom; r.bottom = HTMLSYSWIN_MAX_BOTTOM; /* invalidate it */ win->inval_doc_coords(&r); } /* ------------------------------------------------------------------------ */ /* * Debugger source file text display item */ CHtmlDispTextDebugsrc::CHtmlDispTextDebugsrc(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs, unsigned long src_linenum) : CHtmlDispTextPre(win, font, txt, len, txtofs) { CHtmlPoint iconsize; /* remember the source file line number */ line_num_ = src_linenum; /* get the size of the icon in the left margin */ iconsize = win->measure_dbgsrc_icon(); /* add the width of the icon */ pos_.right += iconsize.x; /* make sure the line is tall enough to contain the icon */ if (pos_.bottom - pos_.top < iconsize.y) pos_.bottom = pos_.top + iconsize.y; /* clear the initial state */ dbgstat_ = 0; } /* * draw a debugger source line */ void CHtmlDispTextDebugsrc::draw(CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, int clicked) { int margin_wid; CHtmlRect iconpos; CHtmlRect txtpos; /* * our position encompasses both the margin icon and the text; * figure out where the text goes by insetting our left edge by the * size of the margin icon */ margin_wid = win->measure_dbgsrc_icon().x; txtpos = pos_; txtpos.left += margin_wid; /* the rest is the icon */ iconpos = pos_; iconpos.right = iconpos.left + margin_wid; /* draw the appropriate margin icon */ win->draw_dbgsrc_icon(&iconpos, dbgstat_); /* draw the text normally within these bounds */ draw_text(win, sel_start, sel_end, 0, clicked, &txtpos); } /* * get the cursor type */ Html_csrtype_t CHtmlDispTextDebugsrc::get_cursor_type( class CHtmlSysWin *win, class CHtmlFormatter *formatter, int x, int y, int more_mode) const { /* determine if the cursor is within the margin icon */ if (x < pos_.left + win->measure_dbgsrc_icon().x) { /* it's in the margin icon - use an arrow */ return HTML_CSRTYPE_ARROW; } else { /* it's in the text - use the normal text cursor */ return CHtmlDispTextPre::get_cursor_type(win, formatter, x, y, more_mode); } } /* * Find text given a window position. We must adjust for the offset * created by the margin icon. */ unsigned long CHtmlDispTextDebugsrc::find_textofs_by_pos( CHtmlSysWin *win, CHtmlPoint pos) const { /* adjust the position to account for the icon in the margin */ pos.x -= win->measure_dbgsrc_icon().x; /* now do the calculation normally */ return CHtmlDispTextPre::find_textofs_by_pos(win, pos); } /* * Find the position of a given text offset. We must adjust for the * offset created by the margin icon. */ CHtmlPoint CHtmlDispTextDebugsrc::get_text_pos(CHtmlSysWin *win, unsigned long txtofs) { CHtmlPoint pos; /* get the position using the normal mechanism */ pos = CHtmlDispTextPre::get_text_pos(win, txtofs); /* adjust for the margin icon */ pos.x += win->measure_dbgsrc_icon().x; /* return the result */ return pos; } /* * Update the status */ void CHtmlDispTextDebugsrc::set_dbg_status(CHtmlSysWin *win, unsigned int newstat) { CHtmlRect iconrc; /* ignore the request if the status isn't changing */ if (newstat == dbgstat_) return; /* set my new status */ dbgstat_ = newstat; /* invalidate the icon area so that we redraw with the new status */ iconrc = pos_; iconrc.right = iconrc.left + win->measure_dbgsrc_icon().x; win->inval_doc_coords(&iconrc); } /* * get my text into a buffer - add a newline at the end */ void CHtmlDispTextDebugsrc::add_text_to_buf(CStringBuf *buf) const { /* inherit default */ CHtmlDispTextPre::add_text_to_buf(buf); /* add a newline */ buf->append("\n", 1); } void CHtmlDispTextDebugsrc:: add_text_to_buf_clip(CStringBuf *buf, unsigned long startofs, unsigned long endofs) const { /* inherit default */ CHtmlDispTextPre::add_text_to_buf_clip(buf, startofs, endofs); /* if the ending offset is beyond my ending offset, add a newline */ if (startofs <= txtofs_ + len_ + 1 && endofs > txtofs_ + len_ + 1) buf->append("\n", 1); } /* ------------------------------------------------------------------------ */ /* * Text Grid row */ /* cell color record */ typedef struct textgrid_cellcolor_t textgrid_cellcolor_t; struct textgrid_cellcolor_t { /* foreground color */ HTML_color_t fg; /* background color, or 0x01000000 if transparent */ HTML_color_t bg; }; /* * construction */ CHtmlDispTextGrid::CHtmlDispTextGrid(CHtmlSysWin *win, CHtmlSysFont *font, unsigned long ofs) { /* remember my initial offset */ ofs_ = ofs; /* there's nothing in our buffer yet */ bytes_ = 0; chars_ = 0; /* set the font */ set_font(win, font); /* allocate an initial set of cell colors */ colors_max_ = 50; colors_ = (textgrid_cellcolor_t *)th_malloc( colors_max_ * sizeof(colors_[0])); } /* * deletion */ CHtmlDispTextGrid::~CHtmlDispTextGrid() { /* delete our color list */ th_free(colors_); } /* * Write text into the grid row */ int CHtmlDispTextGrid::write_text(CHtmlSysWin *win, CHtmlSysFont *font, int col, const textchar_t *txt, size_t len) { /* get my font descriptor */ CHtmlFontDesc desc; font->get_font_desc(&desc); /* get my character set */ oshtml_charset_id_t cs = desc.charset; /* count the number of characters in the new text */ size_t charlen = 0; for (const textchar_t *tp = txt ; tp < txt + len ; tp = os_next_char(cs, tp, len - (tp - txt)), ++charlen) ; /* assume we won't have to expand the row */ int expanded = FALSE; /* seek the starting point, accounting for multi-byte characters */ int i; char *p; for (i = 0, p = buf_.get() ; i < col && i < chars_ ; ++i, p = os_next_char(cs, p, bytes_ - (p - buf_.get()))) ; int starti = p - buf_.get(); /* seek the ending point in the existing text */ for ( ; i < col + (int)charlen && i < chars_ ; ++i, p = os_next_char(cs, p, bytes_ - (p - buf_.get()))) ; int endi = p - buf_.get(); /* * figure the number of bytes after the replacement - this is the * current length minus the removed length plus the added length */ size_t new_bytes = bytes_ - (endi - starti) + len; /* * add room for padding, if necessary: we pad with spaces if the * insertion column is more than one past the existing end column */ if (col > chars_) new_bytes += col - chars_; /* ensure that we have enough space in our string buffer */ buf_.ensure_size(new_bytes); /* make sure we have enough space for the cell colors */ if (col + charlen > colors_max_) { /* expand a bit over the immediate need, and reallocate */ colors_max_ = col + charlen + 50; colors_ = (textgrid_cellcolor_t *)th_realloc( colors_, colors_max_ * sizeof(colors_[0])); } /* add padding spaces if inserting past the last current column */ textgrid_cellcolor_t *cp; for (p = buf_.get() + starti, cp = colors_ + chars_, i = chars_ ; i < col ; ++i, ++p, ++starti, ++endi, ++cp, ++bytes_, ++chars_) { /* add a space */ *p = ' '; /* make it black/transparent (using our special transparent flag) */ cp->fg = HTML_make_color(0x00, 0x00, 0x00); cp->bg = 0x01000000; } /* * Move existing trailing text to its new position. The new text * replaces the old text with the same number of *characters*, but it * might nonetheless have a different *byte* length, since we could be * using a multi-byte character set. The trailing text might need to * move as a result to compensate for the change in byte length of the * replaced text. */ int trailing_len = bytes_ - endi; int trailingi = starti + len; if (trailing_len > 0 && trailingi != endi) memmove(buf_.get() + trailingi, buf_.get() + endi, trailing_len); /* copy the replacement text into the buffer */ memcpy(buf_.get() + starti, txt, len); /* figure the new byte length */ bytes_ = trailingi + trailing_len; /* set the colors */ for (i = col, cp = colors_ + col ; i < col + (int)charlen ; ++i, ++cp) { /* set the foreground color */ cp->fg = desc.color; /* set the background color; use our special flag for transparency */ cp->bg = (desc.default_bgcolor ? 0x01000000 : desc.bgcolor); } /* adjust the maximum column and size if we've expanded the width */ if (col + (int)charlen >= chars_) { /* note the new maximum column */ chars_ = col + charlen; /* calculate the new size */ CHtmlPoint siz = win->measure_text(font_, buf_.get(), bytes_, 0); /* adjust our position for the new size */ pos_.right = pos_.left + siz.x; /* note that we had to expand the row */ expanded = TRUE; } /* invalidate our affected on-screen area */ inval_range(win, ofs_ + starti, ofs_ + starti + len); /* return an indication of whether or not we expanded our width */ return expanded; } /* * Set our font */ void CHtmlDispTextGrid::set_font(CHtmlSysWin *win, CHtmlSysFont *font) { /* remember my new font */ font_ = font; /* calculate my new size */ const textchar_t *txt = (bytes_ != 0 && buf_.get() != 0 ? buf_.get() : "X"); int ascent; CHtmlPoint siz = win->measure_text(font, txt, strlen(txt), &ascent); /* change my position to account for the new size */ pos_.set(pos_.left, pos_.top, pos_.left + siz.x, pos_.top + siz.y); /* remember my new ascent height */ ascent_ht_ = (unsigned short)ascent; } /* * draw the text */ void CHtmlDispTextGrid::draw(CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, int /*clicked*/) { /* draw the part up to the start of the selection */ draw_part(win, FALSE, ofs_, sel_start); /* draw the selected section */ draw_part(win, TRUE, sel_start, sel_end); /* draw the part after the end of the selection */ draw_part(win, FALSE, sel_end, ofs_ + bytes_); } /* draw a text section, limiting to our range */ void CHtmlDispTextGrid::draw_part(CHtmlSysWin *win, int hilite, unsigned long l, unsigned long r) { /* get my font descriptor */ CHtmlFontDesc desc; font_->get_font_desc(&desc); /* get my character set ID */ oshtml_charset_id_t cs = desc.charset; /* limit the start and end positions to our range */ if (l < ofs_) l = ofs_; if (r > ofs_ + bytes_) r = ofs_ + bytes_; /* if that leaves anything to draw, draw it */ if (l < r) { /* adjust 'l' and 'r' to be relative to our starting offset */ l -= ofs_; r -= ofs_; /* find the starting character column ('l' is a *byte* index) */ size_t col; const textchar_t *p0 = buf_.get(); const textchar_t *pl = p0 + l, *pr = p0 + r; const textchar_t *p; for (col = 0, p = p0 ; p < pl ; ++col, p = os_next_char(cs, p, bytes_ - (p - p0))) ; /* find the left pixel position of the update area */ int x = pos_.left; x += win->measure_text(font_, p0, p - p0, 0).x; /* draw in pieces of like color */ for (;;) { /* get the color of the first character to draw */ textgrid_cellcolor_t *cp = &colors_[col]; /* find the next cell with a different color */ for ( ; p < pr ; ++col, p = os_next_char(cs, p, bytes_ - (p - p0))) { /* if the color differs, stop scanning */ if (colors_[col].fg != cp->fg || colors_[col].bg != cp->bg) break; } /* update the base font descriptor with the current color */ desc.default_color = FALSE; desc.color = cp->fg; desc.default_bgcolor = (cp->bg == 0x01000000); desc.bgcolor = cp->bg; /* create a font object for the updated color */ CHtmlSysFont *font = win->get_font(&desc); /* draw the text */ win->draw_text(hilite, x, pos_.top, font, pl, p - pl); /* if that was everything, stop looping */ if (p >= pr) break; /* move horizontally past the chunk's text size */ x += win->measure_text(font_, pl, p - pl, 0).x; /* skip this chunk */ pl = p; } } } /* * invalidate a text range */ void CHtmlDispTextGrid::inval_range(CHtmlSysWin *win, unsigned long start_ofs, unsigned long end_ofs) { /* make sure the range overlaps us */ if (start_ofs >= ofs_ + bytes_ || end_ofs < ofs_) return; /* limit the range to our actual range */ if (start_ofs < ofs_) start_ofs = ofs_; if (end_ofs > ofs_ + bytes_) end_ofs = ofs_ + bytes_; /* calculate the starting column and number of columns in the range */ size_t col = (size_t)(start_ofs - ofs_); size_t len = (size_t)(end_ofs - start_ofs); /* start with our full rectangle */ CHtmlRect inval = pos_; /* reduce the area to the desired range */ inval.left += win->measure_text(font_, buf_.get(), col, 0).x; inval.right = inval.left + win->measure_text(font_, buf_.get() + col, len, 0).x; /* invalidate our window area */ win->inval_doc_coords(&inval); } /* * get the position of a character within the item */ CHtmlPoint CHtmlDispTextGrid::get_text_pos(CHtmlSysWin *win, unsigned long txtofs) { /* * if the offset is before us, or we have no text, return our upper * left corner */ if (txtofs < ofs_ || buf_.get() == 0) return CHtmlPoint(pos_.left, pos_.top); /* if the offset is after us, return our upper right corner */ if (txtofs >= ofs_ + bytes_) return CHtmlPoint(pos_.right, pos_.top); /* start with our upper left corner */ CHtmlPoint pt; pt.x = pos_.left; pt.y = pos_.top; /* add the size of our text to the desired offset */ pt.x += win->measure_text(font_, buf_.get(), txtofs - ofs_, 0).x; /* return the position */ return pt; } /* * get the text offset of a character given a position */ unsigned long CHtmlDispTextGrid::find_textofs_by_pos( CHtmlSysWin *win, CHtmlPoint pt) const { size_t fit_cnt; textchar_t *txt = buf_.get(); /* if it doesn't contain us, use an offset just before or after us */ if (!pos_.contains(pt) || txt == 0) { /* see whether it's above/left or below/right */ if (pt.y <= pos_.top || pt.x <= pos_.left) return ofs_; else return ofs_ + bytes_; } /* figure out how much of our text is before the given position */ fit_cnt = win->get_max_chars_in_width( font_, txt, bytes_, pt.x - pos_.left); /* if we're over halfway into the next character, skip ahead one */ if (fit_cnt + 1 <= (size_t)bytes_ && pt.x > (pos_.left + win->measure_text(font_, txt, fit_cnt, 0).x + win->measure_text(font_, txt + fit_cnt, 1, 0).x / 2)) ++fit_cnt; /* return the text offset of the selected character */ return ofs_ + fit_cnt; } /* ------------------------------------------------------------------------ */ /* * Special text item base class */ /* * invalidate range */ void CHtmlDispTextSpecial::inval_range(CHtmlSysWin *win, unsigned long start_ofs, unsigned long end_ofs) { /* if the range is outside of my text, do nothing */ if (end_ofs <= txtofs_ || start_ofs >= txtofs_ + len_) return; /* * Simply invalidate my entire area. Don't try to limit the area to * the given range, as the normal text item does, since the special * text item types don't necessarily map their display area directly to * their nominal underlying text. */ win->inval_doc_coords(&pos_); } /* ------------------------------------------------------------------------ */ /* * Soft Hyphen */ /* * Find a line break. If a hyphen character will fit in the available * space, we'll note this as a possible breaking point. */ CHtmlDisp *CHtmlDispSoftHyphen::find_line_break(CHtmlFormatter *formatter, CHtmlSysWin *win, long space_avail, CHtmlLineBreak *break_pos) { /* check to see if we have space to add a hyphen character */ if (win->measure_text(font_, "-", 1, 0).x <= space_avail) { /* * our added hyphen will fit; remember us as a possible breaking * point, in case we need to break here later */ break_pos->set_break_pos(this, 0); } /* * don't actually break here; we'll only break here if we can't find a * better breaking point later */ return 0; } /* * Do a line break at this item. */ CHtmlDisp *CHtmlDispSoftHyphen::do_line_break(CHtmlFormatter *formatter, CHtmlSysWin *win, CHtmlLineBreak *break_pos) { CHtmlPoint siz; /* * we're taking the option of breaking here, so actually display as a * hyphen from now on */ vis_ = TRUE; /* set our width to the hyphen's width */ siz = win->measure_text(font_, "-", 1, 0); pos_.right = pos_.left + siz.x; pos_.bottom = pos_.top + siz.y; /* * break after the hyphen; so, the start of the next line is the next * item after us */ return get_next_disp(); } /* * Draw our text */ void CHtmlDispSoftHyphen::draw_text(CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, class CHtmlDispLink *link, int clicked, const CHtmlRect *orig_pos) { /* if we're showing as a hyphen, draw the hyphen */ if (vis_) { CHtmlSysFont *font; /* get the font, adjusting for the link appearance if necessary */ font = get_draw_text_font(win, link, clicked); /* * draw our text as normal, except never draw highlighting; we're * not really a part of the text, so we can't be selected */ win->draw_text(FALSE, pos_.left, pos_.top, font, "-", 1); } } /* ------------------------------------------------------------------------ */ /* * Non-breaking space */ /* * Find a line break. A non-breaking space obviously can't be a line * breaking point. Furthermore, if we're a zero-width space, we prevent a * line break from occurring immediately to our right, even if a break were * otherwise allowed there. */ CHtmlDisp *CHtmlDispNBSP::find_line_break(CHtmlFormatter *formatter, CHtmlSysWin *win, long space_avail, CHtmlLineBreak *break_pos) { /* * for line-breaking purposes, we don't count as a space or any other * breakable character, so clear out the previous-character indicator * in the break state */ break_pos->last_char_ = 0; /* * If we're a zero-width space, we explicitly prevent a break from * occurring to our right, even if such a break were ordinarily * allowed; flag this with the explicit no-break flag in the break * state. */ break_pos->no_break_ = (len_ == 0); /* we don't count as ordinary whitespace */ break_pos->note_non_ws(); /* * don't actually break here; we'll only break here if we can't find a * better breaking point later */ return 0; } /* * measure for a table */ void CHtmlDispNBSP::measure_for_table(class CHtmlSysWin *win, class CHtmlTableMetrics *metrics) { int mywid = measure_width(win); /* * if breaking before this point is allowed (i.e., we're following a * space), and I'm not zero-width (in which case I'm actually a * no-break flag), clear the leftover */ if (len_ != 0 && !metrics->no_break_ && !metrics->no_break_sect_ && is_space(metrics->last_char_)) metrics->clear_leftover(); /* add our width to the running leftover */ metrics->leftover_width_ += mywid; /* disallow breaking just after us if we're zero-width */ metrics->no_break_ = (len_ == 0); /* we're not an ordinary whitespace character in any case */ metrics->last_char_ = 0; } /* * Find trailing whitespace. */ void CHtmlDispNBSP::find_trailing_whitespace(CHtmlDisp_wsinfo *info) { /* we do not count as ordinary trimmable whitespace */ info->clear_ws_pos(); } /* ------------------------------------------------------------------------ */ /* * Typographical space */ CHtmlDispSpace::CHtmlDispSpace(CHtmlSysWin *win, CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs, int wid, int pre) : CHtmlDispTextSpecial(win, font, txt, len, txtofs) { CHtmlPoint siz; CHtmlFontDesc desc; /* remember the preformatted flag */ pre_ = pre; /* * Check to see if we're in a monospaced font; if so, then operate in * whole space units. For a proportional font, use proportional * widths. */ if (font->is_fixed_pitch()) { const textchar_t *sp; /* * We have a fixed-pitch font. Check the desired width and adjust * to whole spaces. */ switch(wid) { case HTMLTAG_SPWID_ORDINARY: case HTMLTAG_SPWID_FIGURE: /* use one space for these */ sp = " "; break; case 0: /* use zero for a zero-width space */ sp = ""; break; /* * For anything else, round to ordinary spaces, which we'll * fix at about a third of an em. For an em space, use three * ordinary spaces; for an en, use two; a three-per-em is 2/3 * en or 2 ordinary; for anything smaller (i.e., higher * denominator), use a single ordinary space. Never round * down to zero, as we want every non-zero-width space to show * up as at least one space. */ case 1: /* em space - use three ordinary spaces */ sp = " "; break; case 2: case 3: /* en space/three-per-em - use two ordinary spaces */ sp = " "; break; case 8: case HTMLTAG_SPWID_PUNCT: /* punctuation/hair spaces - use zero width for these */ sp = ""; break; default: /* for anything smaller, use a single space */ sp = " "; break; } /* use the width of the number of ordinary spaces we figured */ siz = win->measure_text(font, sp, get_strlen(sp), 0); pos_.set(0, 0, siz.x, siz.y); } else { /* get our correct proportional width */ switch(wid) { case HTMLTAG_SPWID_ORDINARY: /* * just use the width of a space - since our nominal text is * also just an ordinary space, this is the width we've * already calculated in the constructor, so we have nothing * more to do */ break; case 0: /* * we have zero width, which is what we've already calculated * in the constructor (since our nominal text is an empty * string) */ break; case HTMLTAG_SPWID_FIGURE: /* calculate the width of a digit '0' */ siz = win->measure_text(font, "0", 1, 0); pos_.set(0, 0, siz.x, siz.y); break; case HTMLTAG_SPWID_PUNCT: /* calculate the size of a period */ siz = win->measure_text(font, ".", 1, 0); pos_.set(0, 0, siz.x, siz.y); break; default: /* * Anything else is the denominator of the fraction of an em * space. Get the em space size from the font, and use the * desired fraction. */ pos_.right = pos_.left + font->get_em_size()/wid; break; } } } /* * Find a line break. A typographical space can be a breaking point, as * long as it's not bordered on both sides by a no-break flag. */ CHtmlDisp *CHtmlDispSpace::find_line_break(CHtmlFormatter *formatter, CHtmlSysWin *win, long space_avail, CHtmlLineBreak *break_pos) { /* * if we're in preformatted text, we can't break before or after a * typographical space */ if (pre_) { break_pos->last_char_ = 0; break_pos->no_break_ = TRUE; break_pos->note_non_ws(); return 0; } /* we count as ordinary whitespace for trimming purposes */ break_pos->note_ws(this, 0); /* * Never actually break at a space, because we can always leave spaces * hanging past the right edge. Simply remember this as a valid break * position, as long as we don't have an explicit no-break flag * preceding us. */ if (!break_pos->no_break_) { /* note that we're a valid breaking point */ break_pos->set_break_pos(this, 0); } /* * Clear out any explicit no-break flag, as we do allow a break to our * right, and note that the last character for line-breaking purposes * is an ordinary space. */ break_pos->no_break_ = FALSE; break_pos->last_char_ = ' '; /* do not break here */ return 0; } /* * Do a line break at this item. */ CHtmlDisp *CHtmlDispSpace::do_line_break(CHtmlFormatter *formatter, CHtmlSysWin *win, CHtmlLineBreak *break_pos) { /* * Since we're pure whitespace, we don't want to start a line after a * soft line break with this item. Instead, simply ask the next item * to skip any leading whitespace and find the next line start. * * This means we're going at the end of a line, which means that we're * trailing space, which means we shouldn't be visible; so set my * width to zero. */ //$$$ pos_.right = pos_.left; /* skip any subsequent whitespace as well */ if (get_next_disp() != 0) return get_next_disp()->do_line_break_skipsp(formatter, win); else return 0; } /* * Draw our text */ void CHtmlDispSpace::draw_text(CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, class CHtmlDispLink *link, int clicked, const CHtmlRect *orig_pos) { CHtmlSysFont *font; /* do nothing if we're zero-width or we're entirely hidden */ if (pos_.right == pos_.left || displen_ == 0) return; /* get the font, adjusting for the link appearance if necessary */ font = get_draw_text_font(win, link, clicked); /* * Draw our text as a space of a particular pixel width. * * Since we're always exactly one character long, we're always either * entirely highlighted or entirely unhighlighted - it's not possible * for a one-character block of text to be partially highlighted. So, * simply draw our text in the appropriate highlighting mode. */ win->draw_text_space(txtofs_ >= sel_start && txtofs_ + len_ <= sel_end, pos_.left, pos_.top, font, pos_.right - pos_.left); } /* * Find trailing whitespace. */ void CHtmlDispSpace::find_trailing_whitespace(CHtmlDisp_wsinfo *info) { /* we count as whitespace, so note our position */ info->set_ws_pos(this, 0); } /* * Remove trailing whitespace from a position we previously found. */ void CHtmlDispSpace::remove_trailing_whitespace(CHtmlSysWin *, void *) { /* * we're all whitespace, so disappear entirely - set our length and * width to zero */ pos_.right = pos_.left; len_ = 0; } /* * Hide trailing whitespace from a position we previously found. */ void CHtmlDispSpace::hide_trailing_whitespace(CHtmlSysWin *, void *) { /* * we're all whitespace, so disappear entirely - set our display * length to zero */ displen_ = 0; } /* ------------------------------------------------------------------------ */ /* * line break */ void CHtmlDispBreak::measure_for_table(CHtmlSysWin *, CHtmlTableMetrics *metrics) { /* start a new line in the metrics */ metrics->start_new_line(); } /* ------------------------------------------------------------------------ */ /* * horizontal rule object */ CHtmlDispHR::CHtmlDispHR(CHtmlSysWin *win, int shade, long height, long width, int width_is_pct, long margin_left, long margin_right, CHtmlResCacheObject *image) : CHtmlDispDisplaySite(win, &pos_, image) { /* figure out how big it is in absolute terms */ if (width_is_pct) { /* take the specified percentage of the window's width */ width_ = (long)((double)(win->get_disp_width() - margin_right - margin_left) * (double)width / 100.0); } else { /* the width specified is in pixels */ width_ = width; } /* note whether the width is relative */ width_is_pct_ = (width_is_pct != 0); /* store the other attributes */ shade_ = shade; height_ = height; /* remember the image */ image_ = image; /* see if we have an image */ if (image_ != 0) { /* add a reference to the image as long as we have it */ image_->add_ref(); } /* see if the height was specified */ if (height == 0) { /* * if we have an image, use it's height; otherwise, use a * default based on whether we have shading or not */ if (image_ != 0 && image_->get_image() != 0) { /* use the image's height as the default height */ height_ = image_->get_image()->get_height(); } else if (shade_) { /* shading - need two pixels for this */ height_ = 2; } else { /* unshaded - use only a single pixel */ height_ = 1; } } /* if it's shaded and there's no image, minimum height is 2 */ if (shade_ && image_ == 0 && height < 2) height_ = 2; /* set my initial position */ pos_.set(0, 0, width_, height_); } CHtmlDispHR::~CHtmlDispHR() { /* we're no longer going to reference the image object */ if (image_ != 0) image_->remove_ref(); } void CHtmlDispHR::draw(CHtmlSysWin *win, unsigned long /*sel_start*/, unsigned long /*sel_end*/, int /*clicked*/) { /* if we have an image, draw the image */ if (image_ != 0 && image_->get_image() != 0) { /* tile the image into the drawing area */ image_->get_image()->draw_image(win, &pos_, HTMLIMG_DRAW_TILE); } else { /* tell the window to draw a standard hrule */ win->draw_hrule(&pos_, shade_); } } /* * measure for a table */ void CHtmlDispHR::measure_for_table(CHtmlSysWin *win, CHtmlTableMetrics *metrics) { /* * If the rule's size is relative, it makes no contribution to the * table metrics, since it can resize to any size according to the * table's layout. However, if the size is absolute, add it to the * line width and indivisible length as we would any other object. */ if (!width_is_pct_) { int mywid = measure_width(win); metrics->add_indivisible(mywid); metrics->add_to_cur_line(mywid); } /* no leftover width from this item */ metrics->clear_leftover(); /* allow breaking after the rule */ metrics->last_char_ = ' '; } /* * Find trailing whitespace. We count as text, so any previous * whitespace is not trailing. */ void CHtmlDispHR::find_trailing_whitespace(CHtmlDisp_wsinfo *info) { info->clear_ws_pos(); } /* ------------------------------------------------------------------------ */ /* * List item header display items */ CHtmlDispListitem::CHtmlDispListitem(CHtmlSysWin *win, CHtmlSysFont *font, long width) { int ascent; /* remember the font */ font_ = font; /* set my initial position based on our width and the font height */ pos_.set(0, 0, width, win->measure_text(font, " ", 1, &ascent).y); ascent_ht_ = (unsigned short)ascent; } void CHtmlDispListitem::get_vertical_space(CHtmlSysWin *, int /*text_height*/, int *above_base, int *below_base, int *total) { /* calculate my vertical space as though I contained some text */ *total = (pos_.bottom - pos_.top); *above_base = ascent_ht_; *below_base = *total - *above_base; } void CHtmlDispListitem::set_line_pos(CHtmlSysWin *, int left, int top, int /*total_height*/, int text_base_offset, int /*max_text_height*/) { /* * We want to be aligned such that our text baseline matches the * text baselines of other text items on the line, so set my top to * my text height above the text baseline. */ pos_.offset(left - pos_.left, top - pos_.top + text_base_offset - ascent_ht_); } /* ------------------------------------------------------------------------ */ /* * set up a bulleted list item header */ CHtmlDispListitemBullet::CHtmlDispListitemBullet (CHtmlSysWin *win, CHtmlSysFont *font, long width, HTML_Attrib_id_t type, CHtmlResCacheObject *image) : CHtmlDispListitem(win, font, width), CHtmlDispDisplaySite(win, &pos_, image) { /* note our image */ image_ = image; if (image != 0) { /* add a reference, since we'll keep the pointer */ image->add_ref(); /* make sure we're big enough to show the entire image */ if (image->get_image() != 0) { long image_wid = image->get_image()->get_width(); long image_ht = image->get_image()->get_height(); /* make sure we're wide enough */ if (pos_.right - pos_.left < image_wid) pos_.right = pos_.left + image_wid; /* make sure we're tall enough */ if (pos_.bottom - pos_.top < image_ht) { pos_.bottom = pos_.top + image_ht; ascent_ht_ = (unsigned short)image_ht; } } } /* see what type of bullet we're using */ switch(type) { default: case HTML_Attrib_disc: type_ = HTML_SYSWIN_BULLET_DISC; break; case HTML_Attrib_circle: type_ = HTML_SYSWIN_BULLET_CIRCLE; break; case HTML_Attrib_square: type_ = HTML_SYSWIN_BULLET_SQUARE; break; case HTML_Attrib_plain: type_ = HTML_SYSWIN_BULLET_PLAIN; break; } } CHtmlDispListitemBullet::~CHtmlDispListitemBullet() { /* if we have an image, remove our reference to it */ if (image_ != 0) image_->remove_ref(); } void CHtmlDispListitemBullet::get_vertical_space(CHtmlSysWin *win, int text_height, int *above_base, int *below_base, int *total) { /* * if we have an image to display, calculate the space needs as we * would for an image; otherwise, use the default handling */ if (image_ != 0 && image_->get_image() != 0) { /* center the image vertically */ *total = pos_.bottom - pos_.top; *above_base = (text_height + *total) / 2; *below_base = *total - *above_base; } else { /* no image - use default handling */ CHtmlDispListitem::get_vertical_space(win, text_height, above_base, below_base, total); } } void CHtmlDispListitemBullet::set_line_pos(CHtmlSysWin *win, int left, int top, int total_height, int text_base_offset, int max_text_height) { /* * if we have an image, center the image vertically relative to the * text area; otherwise, use the default list item handling */ if (image_ != 0 && image_->get_image() != 0) { long y_offset; /* calculate position as we would a vertically centered image */ y_offset = (max_text_height + pos_.bottom - pos_.top) / 2; pos_.offset(left - pos_.left, top - pos_.top + text_base_offset - y_offset); } else { CHtmlDispListitem::set_line_pos(win, left, top, total_height, text_base_offset, max_text_height); } } /* * Draw a bulleted list item header */ void CHtmlDispListitemBullet::draw(CHtmlSysWin *win, unsigned long /*sel_start*/, unsigned long /*sel_end*/, int /*clicked*/) { /* see if we have an image */ if (image_ != 0 && image_->get_image() != 0) { long image_wid, image_ht; long pos_wid, pos_ht; CHtmlRect drawpos; /* center the image in our area */ pos_wid = pos_.right - pos_.left; pos_ht = pos_.bottom - pos_.top; image_wid = image_->get_image()->get_width(); image_ht = image_->get_image()->get_height(); drawpos.left = pos_.left + (pos_wid - image_wid)/2; drawpos.right = drawpos.left + image_wid; drawpos.top = pos_.top + (pos_ht - image_ht)/2; drawpos.bottom = drawpos.top + image_ht; /* draw the image, clipping to the drawing area */ image_->get_image()->draw_image(win, &drawpos, HTMLIMG_DRAW_CLIP); } else { /* no image - draw a bullet of the appropriate type */ win->draw_bullet(FALSE, pos_.left, pos_.top, font_, type_); } } /* ------------------------------------------------------------------------ */ /* * Set up a numbered list item header */ CHtmlDispListitemNumber::CHtmlDispListitemNumber( CHtmlSysWin *win, CHtmlSysFont *font, long width, textchar_t style, long value) : CHtmlDispListitem(win, font, width) { const int bufmax = 128; textchar_t buf[bufmax]; textchar_t *p; /* prepare to write to our buffer in reverse order */ p = buf + bufmax; /* null-terminate the value and end it with a period */ *--p = '\0'; *--p = '.'; /* generate format appropriate for the style */ switch(style) { default: case '1': /* if the value is less than zero, generate leading minus sign */ if (value < 0) { value = -value; *--p = '-'; } /* format the header as a number */ do { *--p = int_to_ascii_digit(value % 10); value /= 10; } while (p > buf && value != 0); break; case 'A': case 'a': /* ignore nonpositive values */ if (value <= 0) { *--p = '?'; break; } /* format the header as a letter sequence */ do { --value; *--p = (textchar_t)((value % 26) + style); value /= 26; } while (p > buf && value != 0); break; case 'i': case 'I': { static struct { const textchar_t *lc; const textchar_t *uc; int val; } *cur, mapping[] = { { "m", "M", 1000 }, { "cm", "CM", 900 }, { "d", "D", 500 }, { "cd", "CD", 400 }, { "c", "C", 100 }, { "xc", "XC", 90 }, { "l", "L", 50 }, { "xl", "XL", 40 }, { "x", "X", 10 }, { "ix", "IX", 9 }, { "v", "V", 5 }, { "iv", "IV", 4 }, { "i", "I", 1 } }; /* don't allow non-positive values or values over 4999 */ if (value <= 0 || value >= 5000) { *--p = '?'; break; } /* do roman numbers in forward order */ p = buf; /* * Map the number. For each item in our table, add that * item as many times as we can, subtracting its value each * time we add it. When the value is too small to add any * more of that value, move on to the next value. Repeat * until the value has been entirely consumed. */ for (cur = mapping ; value > 0 ; ++cur) { /* add the current item as many times as it will fit */ while (value >= cur->val) { const textchar_t *src; /* add this value to the string */ for (src = (style == 'i' ? cur->lc : cur->uc) ; *src ; *p++ = *src++) ; /* reduce the value by the amount expressed this time */ value -= cur->val; } } /* since we went forward, point p back to the buffer start */ *p++ = '.'; *p = '\0'; p = buf; } break; } /* set my display string to the string we generated */ num_.set(p); } /* * Draw a numbered list item header */ void CHtmlDispListitemNumber::draw(CHtmlSysWin *win, unsigned long /*sel_start*/, unsigned long /*sel_end*/, int /*clicked*/) { long text_len; /* figure the size we need to display our text */ text_len = win->measure_text(font_, num_.get(), get_strlen(num_.get()), 0).x + win->measure_text(font_, " ", 2, 0).x; /* draw our number as ordinary text, right-justified in our rectangle */ win->draw_text(FALSE, pos_.right - text_len, pos_.top, font_, num_.get(), get_strlen(num_.get())); } /* ------------------------------------------------------------------------ */ /* * Image display item */ CHtmlDispImg::CHtmlDispImg(CHtmlSysWin *win, CHtmlResCacheObject *image, CStringBuf *alt, HTML_Attrib_id_t align, long hspace, long vspace, long width, int width_set, long height, int height_set, long border, class CHtmlUrl *usemap, int ismap) : CHtmlDispDisplaySite(win, &pos_, image) { /* remember the settings */ image_ = image; alt_.set(alt); align_ = align; hspace_ = hspace; vspace_ = vspace; width_ = width; height_ = height; border_ = border; usemap_.set_url(usemap); ismap_ = ismap; /* if we have an image, set it up */ if (image_ != 0) { /* count our reference to the image as long as we're around */ image_->add_ref(); /* if we successfully loaded the image, get its size */ if (image_->get_image() != 0) { /* * Get the size from the image. If either dimension was * explicitly set, though, keep the explicit setting. */ if (!width_set) { /* get the width from the image */ width_ = image_->get_image()->get_width(); } if (!height_set) { /* get the height from the image */ height_ = image_->get_image()->get_height(); } /* * If one of the dimensions was explicitly set but the other * one wasn't, scale the unset dimension so that the picture * stays at its original aspect ratio. */ if (width_set && !height_set) { /* scale the height proportionally to the width */ height_ = (long)((double)height_ * (double)width_ / (double)image_->get_image()->get_width()); } else if (height_set && !width_set) { /* scale the width proportionally to the height */ width_ = (long)((double)width_ * (double)height_ / (double)image_->get_image()->get_height()); } } } /* set our position based on the final size */ pos_.set(0, 0, width_, height_); } /* * delete */ CHtmlDispImg::~CHtmlDispImg() { /* we're no longer going to reference the image object */ if (image_ != 0) image_->remove_ref(); } /* * Forget an image. This must be called if we wish to change one of our * internal images dynamically. */ void CHtmlDispImg::forget_image(CHtmlResCacheObject **image) { /* if there's no existing image, there's nothing to do */ if (*image == 0) return; /* * if this is our current display site image, we no longer have any * image in our display site */ if (get_site_image() == *image) set_site_image(0); /* drop our reference to the image */ (*image)->remove_ref(); /* forget the image */ *image = 0; } /* * Set the extra "hover" and "active" sub-images. Ordinary image display * items don't allow these sub-images; we'll simply log an error to the * debug log mentioning the problem. */ void CHtmlDispImg::set_extra_images(CHtmlResCacheObject *a_image, CHtmlResCacheObject *h_image) { /* log an error for the ASRC image, if one was specified */ if (a_image != 0) oshtml_dbg_printf(": ASRC is not allowed except in " "hyperlinked images\n"); /* log an error for the HSRC image, if one was specified */ if (h_image != 0) oshtml_dbg_printf(": HSRC is not allowed except in " "hyperlinked images\n"); } /* * get the width of the picture */ int CHtmlDispImg::measure_width(CHtmlSysWin *) { /* * return the image width plus twice the size of the horizontal * whitespace (since we draw it on both sides) plus twice the size * of the border (since we draw that on both sides, as well) */ return width_ + 2*hspace_ + 2*border_; } /* * draw the picture */ void CHtmlDispImg::draw(CHtmlSysWin *win, unsigned long /*sel_start*/, unsigned long /*sel_end*/, int /*clicked*/) { /* draw the normal image */ draw_image(win, image_); } /* * draw the picture, using a particular image */ void CHtmlDispImg::draw_image(CHtmlSysWin *win, CHtmlResCacheObject *image) { CHtmlRect drawpos; /* * calculate where to draw the image - offset by border size and * surrounding whitespace sizes, and set the width and height to our * image width and height (to exclude surrounding whitespace from * the image area) */ drawpos = pos_; drawpos.offset(border_ + hspace_, border_ + vspace_); drawpos.right = drawpos.left + width_; drawpos.bottom = drawpos.top + height_; /* draw it */ if (image == 0 || image->get_image() == 0) { /* invalid image - draw a placeholder box instead */ win->draw_hrule(&drawpos, TRUE); } else { /* draw the image, scaling to fill the target rectangle */ image->get_image()->draw_image(win, &drawpos, HTMLIMG_DRAW_STRETCH); } } /* * get my overall vertical space needs */ void CHtmlDispImg::get_vertical_space(CHtmlSysWin *, int text_height, int *above_base, int *below_base, int *total) { /* our total space is always the same */ *total = measure_height(); /* our arrangement of the space depends on our alignment */ switch(align_) { case HTML_Attrib_top: /* * We want to be aligned with the top of the text, so we need * the maximum text height above the baseline. Add extra for * the top spacing (the vertical whitespace above the image and * the top border), since we want the top of the image to align * with the text. */ *above_base = text_height + vspace_ + border_; break; case HTML_Attrib_middle: /* * we're centered relative to the text, so half of my height * above the the text center point, and the text center point is * half of the text height above the text baseline, so we need * the sum of these two is the total we need above the text * baseline */ *above_base = (text_height + *total) / 2; break; case HTML_Attrib_bottom: /* * We're aligned at the bottom of the text, so we need our * entire height above the text baseline. Note that we want the * image itself aligned at the bottom, so subtract off the * bottom spacing (the vertical whitespace and border below the * image). */ *above_base = *total - vspace_ - border_; break; default: /* other cases should never occur; ignore them if they do */ break; } /* the amount needed below the baseline is whatever's left over */ *below_base = *total - *above_base; } /* * set my line position */ void CHtmlDispImg::set_line_pos(CHtmlSysWin *, int left, int top, int /*total_height*/, int text_base_offset, int max_text_height) { int y_offset; /* our positioning depends on our alignment */ switch(align_) { case HTML_Attrib_top: /* * Align at the top of the text - put our top above the text * baseline by the max text height, which means that our top is * offset from the line top by the text baseline offset minus * the text height. Note that we want the top of the image * itself to align with the top of the text, so offset by the * top spacing (the vertical whitespace above the image and the * top border). */ y_offset = max_text_height + vspace_ + border_; break; case HTML_Attrib_middle: /* * We're centered relative to the text, so half of my height * above the the text center point, and the text center point is * half of the text height above the text baseline, so the sum * of these two is the total we need above the text baseline. */ y_offset = (max_text_height + measure_height()) / 2; break; case HTML_Attrib_bottom: /* * We're aligned at the bottom of the text, so put our top at * the text baseline minus our total height. Note that we want * the image itself aligned at the bottom, so subtract off the * bottom spacing (the border and the vertical whitespace below * the image). */ y_offset = measure_height() - vspace_ - border_; break; default: /* * if I'm a floater, my position is outside the line, so ignore * changes during line formatting */ return; } /* set my position */ pos_.offset(left - pos_.left, top - pos_.top + text_base_offset - y_offset); } /* * Measure for table. Non-floating images act like default display * items. Floating images contribute to the minimum size, but do not * contribute to the overall line length, because they always get * inserted at a line break. */ void CHtmlDispImg::measure_for_table(CHtmlSysWin *win, CHtmlTableMetrics *metrics) { /* check our alignment type */ switch(align_) { case HTML_Attrib_left: case HTML_Attrib_right: /* * we're floating, so we're not part of a line; just add our * invidisible width */ metrics->add_indivisible(measure_width(win)); break; default: /* * We're not floating, so we go inside a line. Act like an * ordinary display item: we're unbreakable within, but we can * break at either edge. */ CHtmlDisp::measure_for_table(win, metrics); break; } /* allow breaking after me */ metrics->last_char_ = ' '; metrics->no_break_ = FALSE; } /* * Find line break. If this image is aligned left or right, so that * text floats around it, don't break at the image, since the image is * never part of a line; otherwise, use the normal breaking strategy. */ CHtmlDisp *CHtmlDispImg::find_line_break(CHtmlFormatter *formatter, CHtmlSysWin *win, long space_avail, CHtmlLineBreak *break_pos) { switch(align_) { case HTML_Attrib_left: case HTML_Attrib_right: /* * we float outside the text - this type of item isn't part of a * line at all, so it can't be the first item on a line */ return 0; default: /* * other alignment types go inline with the text - follow the * normal line breaking algorithm */ return CHtmlDisp::find_line_break(formatter, win, space_avail, break_pos); } } /* * Find trailing whitespace. We count as text, so any previous * whitespace is not trailing. */ void CHtmlDispImg::find_trailing_whitespace(CHtmlDisp_wsinfo *info) { info->clear_ws_pos(); } /* * Determine what type of cursor to use over the image */ Html_csrtype_t CHtmlDispImg::get_cursor_type(CHtmlSysWin *win, CHtmlFormatter *formatter, int x, int y, int more_mode) const { CHtmlFmtMapZone *zone; /* * If I have an image map, ask the formatter to find the zone at the * current location - if there's a match, use its link. If links are * disabled, ignore the image map. Note that if the link is a "forced" * style link, we'll show it as a link even when links are globally * disabled. */ if ((zone = get_map_zone(formatter, x, y)) != 0 && !more_mode && (win->get_html_show_links() || zone->link_->is_forced())) { /* use a hand cursor if the zone has a URL, arrow otherwise */ return zone->link_->href_.get_url() != 0 && !zone->link_->nohref_ ? HTML_CSRTYPE_HAND : HTML_CSRTYPE_ARROW; } /* no image map - use default handling */ return CHtmlDisp::get_cursor_type(win, formatter, x, y, more_mode); } CHtmlDispLink *CHtmlDispImg::get_link(CHtmlFormatter *formatter, int x, int y) const { CHtmlFmtMapZone *zone; /* * if I have an image map, ask the formatter to find the zone at the * current location - if there's a match, use its link */ if ((zone = get_map_zone(formatter, x, y)) != 0) return zone->link_; /* no image map - use default handling */ return CHtmlDisp::get_link(formatter, x, y); } /* * If we have an image map, find the map zone containing a given point. * If we don't have an image map, or the image map doesn't have a zone * containing the given point, returns null. */ CHtmlFmtMapZone *CHtmlDispImg::get_map_zone(CHtmlFormatter *formatter, int x, int y) const { const char *map_name; CHtmlFmtMap *map; /* if we don't have an image map, there's definitely not a zone */ if ((map_name = usemap_.get_url()) == 0) return 0; /* * Get the map name. If it starts with a pound sign, it means that * it's a local map. Since this is the only kind of map we accept, * the pound sign doesn't make any actual difference to us; so, * simply remove it if it's present. */ if (*map_name == '#') ++map_name; /* * ask the formatter for a map matching our map name; if there isn't * any such map, there's no zone match */ map = formatter->get_map(map_name, get_strlen(map_name)); if (map == 0) return 0; /* ask the map to find the point */ return map->find_zone(x, y, &pos_); } /* * Format deferred - this is used to format the image as a floating * marginal item. */ void CHtmlDispImg::format_deferred(class CHtmlSysWin *win, class CHtmlFormatter *formatter, long line_spacing) { /* inherit default handling to set up our temporary margins */ basic_format_deferred(win, formatter, line_spacing, align_, measure_height()); } /* ------------------------------------------------------------------------ */ /* * Linkable image */ /* * destroy */ CHtmlDispImgLink::~CHtmlDispImgLink() { /* if we have a hover image, remove our reference to it */ if (h_image_ != 0) h_image_->remove_ref(); /* if we have an acive (clicked) image, remove our reference */ if (a_image_ != 0) a_image_->remove_ref(); } /* * set the hover and activate images */ void CHtmlDispImgLink::set_extra_images(CHtmlResCacheObject *a_image, CHtmlResCacheObject *h_image) { /* if we have existing images, remove them */ forget_image(&a_image_); forget_image(&h_image_); /* remember the new images */ a_image_ = a_image; h_image_ = h_image; /* add references to them if they're valid */ if (a_image_ != 0) a_image_->add_ref(); if (h_image_ != 0) h_image_->add_ref(); } /* * Get the link, given the cursor position. If we have an image map, we'll * return the link for the region of the map we're over; otherwise, we'll * return the fixed link for the entire image. */ CHtmlDispLink *CHtmlDispImgLink::get_link(CHtmlFormatter *formatter, int x, int y) const { CHtmlFmtMapZone *zone; /* * if I have an image map, ask the formatter to find the zone at the * current location - if there's a match, use its link */ if ((zone = get_map_zone(formatter, x, y)) != 0) return zone->link_; /* no image map - return my enclosing link */ return link_; } /* * Get the cursor type when the mouse is at a particular position over the * image. If we have an image map, we'll check to see if we're over a * linked part of the map; otherwise, we'll use the appropriate cursor for * our fixed link. */ Html_csrtype_t CHtmlDispImgLink::get_cursor_type( CHtmlSysWin *win, CHtmlFormatter *formatter, int x, int y, int more_mode) const { CHtmlFmtMapZone *zone; CHtmlDispLink *link; /* get the map zone (if any), and the link for the given position */ zone = get_map_zone(formatter, x, y); link = get_link(formatter, x, y); /* if there's no link, use an arrow cursor */ if (link == 0) return HTML_CSRTYPE_HAND; /* * if links are disabled, and the link doesn't use the "forced" style, * inherit the default handling */ if (more_mode || (!win->get_html_show_links() && !link->is_forced())) return CHtmlDispImg::get_cursor_type(win, formatter, x, y, more_mode); /* if I have an image map, see if the cursor is in an image map zone */ if (zone != 0) { /* use a hand cursor if the zone has a URL, or an arrow otherwise */ return link->href_.get_url() != 0 && !link->nohref_ ? HTML_CSRTYPE_HAND : HTML_CSRTYPE_ARROW; } /* use the hand cursor, since the whole image is linked */ return HTML_CSRTYPE_HAND; } /* * draw the picture */ void CHtmlDispImgLink::draw(CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, int /*clicked*/) { int clicked; CHtmlResCacheObject *new_pic; /* get our link status */ clicked = link_->get_clicked(); /* figure out which picture to display based on our status */ if (clicked == 0) { /* unclicked - use the ordinary SRC image */ new_pic = image_; } else if ((clicked & CHtmlDispLink_clicked) != 0) { /* * clicked - use the ASRC image if there is one; otherwise fall * back on the HSRC image if there is one; otherwise fall back on * the regular SRC image */ new_pic = a_image_; if (new_pic == 0 || new_pic->get_image() == 0) new_pic = h_image_; if (new_pic == 0 || new_pic->get_image() == 0) new_pic = image_; } else if ((clicked & CHtmlDispLink_hover) != 0) { /* * hovering - use the HSRC image if there is one; otherwise fall * back on the regular SRC image */ new_pic = h_image_; if (new_pic == 0 || new_pic->get_image() == 0) new_pic = image_; } /* * if the picture has changed, register the change with the display * site implementation */ if (new_pic != get_site_image()) set_site_image(new_pic); /* draw using the selected image */ draw_image(win, new_pic); } /* ------------------------------------------------------------------------ */ /* * TABLE display item */ CHtmlDispTable::CHtmlDispTable(class CHtmlSysWin *win, long border, HTML_Attrib_id_t align, int floating, long width, int width_pct, int width_set, long win_width, HTML_color_t bgcolor, int use_bgcolor, CHtmlResCacheObject *bg_image) : CHtmlDispDisplaySite(win, &pos_, bg_image) { /* remember the settings */ border_ = border; align_ = align; floating_ = floating; width_ = width; width_pct_ = width_pct; width_set_ = width_set; win_width_ = win_width; width_min_ = width_max_ = 0; pos_.set(0, 0, 0, 0); bgcolor_ = bgcolor; use_bgcolor_ = use_bgcolor; sub_head_ = 0; } /* * Measure the table for inclusion in a table. This will be used when * the table is nested within another table's cell. This is never * called for a top-level table, because it's called for a table's * contents rather than for the table itself; a table will only be * measured when it's inside another table. * * We calculate the space needed for the nested table according to its * contents. The minimum width for the nested table is the sum of the * minimum widths for its columns; likewise for the maximum width. * * Nested tables are measured before their enclosing tables, because the * formatter goes through the table's display list after everything * inside the table has been formatted during pass 1. The nested table * is measured first because it end first. By the time the enclosing * table is ready to ask for measurements, the nested table has been * completely calculated. Note that we'll run through the contents of * the nested table and calculate their sizes; this is a redundant but * harmless exercise, since the nested table's contents are necessarily * no larger than the nested table itself. */ void CHtmlDispTable::measure_for_table(CHtmlSysWin *, CHtmlTableMetrics *metrics) { /* * add our minimum width to the indivisible widths, since this is * the smallest we can go */ metrics->add_indivisible(width_min_); /* * add our maximum width to the total line width, since this is the * largest we'd need */ metrics->add_to_cur_line(width_max_); /* nothing left over from this item */ metrics->clear_leftover(); metrics->last_char_ = ' '; /* * clear the current line width in the metrics - our contents * effectively go on a new line, since the table's contents overlap the * table's area in the layout */ metrics->start_new_line(); } /* * Measure the table's contribution to a banner's width. If the table * is based on a percentage of the window width, we won't contribute * anything to the minimum width, because we don't know yet how big the * banner is going to be (since this routine is called to calculate * that), hence we don't know what our percentage applies to yet. */ void CHtmlDispTable::measure_for_banner(CHtmlSysWin *, CHtmlTableMetrics *metrics) { /* * add our minimum width to the indivisible widths, since this is the * smallest we can go */ metrics->add_indivisible(width_min_); /* * add our maximum width to the total line width, since this is the * largest we'd need */ metrics->add_to_cur_line(width_max_); /* nothing left over from this item */ metrics->clear_leftover(); metrics->last_char_ = ' '; } /* * draw the table */ void CHtmlDispTable::draw(CHtmlSysWin *win, unsigned long /*sel_start*/, unsigned long /*sel_end*/, int /*clicked*/) { /* if there's a background image or color, draw it */ if (get_site_image() != 0 && get_site_image()->get_image() != 0) { /* tile our image into our drawing area */ get_site_image()->get_image() ->draw_image(win, &pos_, HTMLIMG_DRAW_TILE); } else if (use_bgcolor_) { /* fill in our area with our background color */ win->draw_table_bkg(&pos_, bgcolor_); } /* if we have a border, draw it */ if (border_ != 0) win->draw_table_border(&pos_, border_, FALSE); } int CHtmlDispTable::measure_width(CHtmlSysWin *) { /* return the width we have set in our position */ return pos_.right - pos_.left; } void CHtmlDispTable::get_vertical_space(CHtmlSysWin *, int /*text_height*/, int *above_base, int *below_base, int *total) { /* * the text baseline is irrelevant to a table, since tables are not * set as part of a line of text; so, put our entire height above * the baseline */ *above_base = *total = pos_.bottom - pos_.top; *below_base = 0; } /* * Find trailing whitespace. We count as text, so any previous * whitespace is not trailing. */ void CHtmlDispTable::find_trailing_whitespace(CHtmlDisp_wsinfo *info) { info->clear_ws_pos(); } /* * Calculate the table's actual size. Based on the actual size, * calculate the sizes of the columns. */ void CHtmlDispTable::calc_table_size(CHtmlFormatter *formatter, CHtmlTagTABLE *tag) { long wid; long x_offset; /* * check to see if the user asked for a particular size via the * WIDTH attribute */ if (width_set_) { /* * Use the requested size, but in no event go any smaller than * the minimum width. */ wid = width_; if (wid < width_min_) wid = width_min_; } else { /* * No WIDTH attribute is specified for the table, so let the * contents determine the table's size, constraining the size if * possible to our available window width. */ wid = tag->calc_table_width(width_min_, width_max_, win_width_); } /* * Figure out where to put the table based on the horizontal * alignment, now that we know the width of the available space and * the amount of space we'll take up. */ switch(align_) { case HTML_Attrib_left: default: /* left alignment - put it at the left margin */ x_offset = 0; break; case HTML_Attrib_right: /* * Right alignment - if it's smaller than the available space, * offset it to the right by the amount of space left over so that * it's flush against the right margin. If we don't have room for * that, align it at the left margin, as that's the furthest left * we can go. (It's better to hang off the right side, since the * scrollbar allows scrolling in that direction, than to hang off * the left side.) */ if (wid < win_width_) x_offset = win_width_ - wid; else x_offset = 0; break; case HTML_Attrib_center: /* * centered - if it's smaller than the available space, center it * in the available space; otherwise, put it at the left margin, * since that's as far left as we're allowed to go */ if (wid < win_width_) x_offset = (win_width_ - wid) / 2; else x_offset = 0; break; } /* set the column widths */ tag->set_column_widths(x_offset, wid); /* set our position and width */ pos_.left += x_offset; pos_.right = pos_.left + wid; } /* * Format deferred - this is used to format the table as a floating * marginal item. */ void CHtmlDispTable::format_deferred(class CHtmlSysWin *win, class CHtmlFormatter *formatter, long line_spacing) { CHtmlRect old_pos; long dx, dy; CHtmlDisp *cur; /* * note the original position, so we can determine how much we move in * setting our final position */ old_pos = pos_; /* * inherit default handling to set our final position and set up the * temporary margins that exclude display items in the main flow from * the area we're occupying at the edge of the document */ basic_format_deferred(win, formatter, line_spacing, align_, pos_.bottom - pos_.top); /* calculate how far we've moved */ dx = pos_.left - old_pos.left; dy = pos_.top - old_pos.top; /* move each of our subitems by the same amount we moved */ for (cur = sub_head_ ; cur != 0 ; cur = cur->get_next_disp()) { CHtmlRect pos; /* get the position of this item */ pos = cur->get_pos(); /* move it by the same amount that the table moved */ pos.offset(dx, dy); /* set the new position */ cur->set_pos(&pos); } /* * add our contents sublist to the master display list - our inherited * base implementation just called will have added us to the display * list, so we can simply add our contents now */ formatter->add_to_disp_list(sub_head_); } /* * Set our sublist - this gives us our list of contained display items for * use in formatting floating tables. */ void CHtmlDispTable:: set_contents_sublist(CHtmlDisp *head) { /* remember the head of the list */ sub_head_ = head; } /* ------------------------------------------------------------------------ */ /* * Table cell */ CHtmlDispTableCell::CHtmlDispTableCell(CHtmlSysWin *win, int border, HTML_color_t bgcolor, int use_bgcolor, CHtmlResCacheObject *bg_image, long width_attr, int width_pct, int width_set) : CHtmlDispDisplaySite(win, &pos_, bg_image) { /* save the settings */ pos_.set(0, 0, 0, 0); border_ = (border != 0); bgcolor_ = bgcolor; use_bgcolor_ = use_bgcolor; width_attr_ = width_attr; width_pct_ = width_pct; width_set_ = width_set; } void CHtmlDispTableCell::draw(CHtmlSysWin *win, unsigned long /*sel_start*/, unsigned long /*sel_end*/, int /*clicked*/) { /* if there's a background image or color, draw it */ if (get_site_image() != 0 && get_site_image()->get_image() != 0) { /* tile our image into our area */ get_site_image()->get_image() ->draw_image(win, &pos_, HTMLIMG_DRAW_TILE); } else if (use_bgcolor_) { /* fill in our area with our background color */ win->draw_table_bkg(&pos_, bgcolor_); } /* * If the table has a border, draw the cell with a one-pixel border. * (The size of the cell's border is always one pixel; the only * effect the table's BORDER attribute has on the cell's border is * whether it's drawn at all.) */ if (border_) win->draw_table_border(&pos_, 1, TRUE); } int CHtmlDispTableCell::measure_width(CHtmlSysWin *) { /* return the width we have set in our position */ return pos_.right - pos_.left; } /* * Measure the metrics of this item for use in a table cell. Since this * *is* a table cell, this routine is called only when we're in a nested * table - we're being measured for use in the enclosing table's cell that * contains our entire table. */ void CHtmlDispTableCell::measure_for_table(class CHtmlSysWin *win, class CHtmlTableMetrics *metrics) { /* * If we have a pixel (not percentage-based) WIDTH setting, include * that as our minimum size. If our width is not set, do not * calculate any contribution, since we'll size either as a percentage * of our container's size or according to our contents size; in * either case, the size doesn't depend on us at all, so we have * nothing to add. */ if (width_set_ && !width_pct_) metrics->add_indivisible(width_attr_); /* nothing left over from this item */ metrics->clear_leftover(); metrics->last_char_ = ' '; } /* * Measure the cell for its contribution to the banner size. We'll only * contribute a minimum width if our WIDTH attribute was explicitly set * to a pixel (not a percentage) value, since we will otherwise adapt to * the size of the banner rather than vice versa. */ void CHtmlDispTableCell::measure_for_banner(CHtmlSysWin *win, CHtmlTableMetrics *metrics) { /* * if we have a pixel (not percentage-based) WIDTH setting, include * that as our minimum size; otherwise, do not make a contribution, * since we will adapt to the banner width */ if (width_set_ && !width_pct_) metrics->add_indivisible(width_attr_); /* add our current calculated width to the line width */ metrics->add_to_cur_line(measure_width(win)); /* nothing left over from this item */ metrics->clear_leftover(); metrics->last_char_ = ' '; } void CHtmlDispTableCell::get_vertical_space(CHtmlSysWin *, int /*text_height*/, int *above_base, int *below_base, int *total) { /* * the text baseline is irrelevant to a table, since tables are not * set as part of a line of text; so, put our entire height above * the baseline */ *above_base = *total = pos_.bottom - pos_.top; *below_base = 0; } /* * Find trailing whitespace. We count as text, so any previous * whitespace is not trailing. */ void CHtmlDispTableCell::find_trailing_whitespace(CHtmlDisp_wsinfo *info) { info->clear_ws_pos(); } /* ------------------------------------------------------------------------ */ /* * Whitespace search object implementation */ /* * remove trailing whitespace from the last item we found with trailing * whitespace */ void CHtmlDisp_wsinfo::remove_trailing_whitespace( CHtmlSysWin *win, CHtmlDisp *disp, CHtmlDisp *nxt) { CHtmlDisp *cur; /* * scan for trailing whitespace, starting at the given item and * continuing to the end of the display list */ for (cur = disp ; cur != 0 && cur != nxt ; cur = cur->get_next_disp()) cur->find_trailing_whitespace(this); /* if we found trailing whitespace, go remove it */ if (last_ws_disp_ != 0) { /* * remove trailing whitespace from the display item where we found * the start of the run of whitespace */ last_ws_disp_->remove_trailing_whitespace(win, last_ws_pos_); /* * Any items after this item must be entirely whitespace, since * they're part of the run of contiguous whitespace from the * starting position to the end of the list. Go through all of * the remaining items and remove all of their whitespace. */ for (cur = last_ws_disp_->get_next_disp() ; cur != 0 && cur != nxt ; cur = cur->get_next_disp()) cur->remove_trailing_whitespace(win, 0); } } /* * hide trailing whitespace - this is similar to * remove_trailing_whitespace, but merely hides the trailing spaces from * the visual appearance, rather than deleting them from memory */ void CHtmlDisp_wsinfo::hide_trailing_whitespace( CHtmlSysWin *win, CHtmlDisp *disp, CHtmlDisp *nxt) { CHtmlDisp *cur; /* scan the given item list for trailing whitespace */ for (cur = disp ; cur != 0 && cur != nxt ; cur = cur->get_next_disp()) cur->find_trailing_whitespace(this); /* if we found trailing whitespace, go hide it */ if (last_ws_disp_ != 0) { /* hide whitespace from the item where the trailing run starts */ last_ws_disp_->hide_trailing_whitespace(win, last_ws_pos_); /* entirely hide all of the items after this one */ for (cur = last_ws_disp_->get_next_disp() ; cur != 0 && cur != nxt ; cur = cur->get_next_disp()) cur->hide_trailing_whitespace(win, 0); } } qtads-2.1.7/htmltads/htmldisp.h000066400000000000000000003323141265017072300164630ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/html/htmldisp.h,v 1.3 1999/07/11 00:46:40 MJRoberts Exp $ */ /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmldisp.h - HTML formatter display objects Function The display objects are the objects that the HTML formatter prepares from the parse tree. Display objects map directly onto the visual display, so it's easy to take a list of display objects and draw the document into a window. Display objects are organized into a simple linear list. Each object has a rectangular boundary in the drawing coordinate system. Notes Modified 09/08/97 MJRoberts - Creation */ #ifndef HTMLDISP_H #define HTMLDISP_H #include #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef HTMLATTR_H #include "htmlattr.h" #endif #ifndef HTMLURL_H #include "htmlurl.h" #endif #ifndef HTMLSYS_H #include "htmlsys.h" #endif /* ------------------------------------------------------------------------ */ /* * Memory heap source ID. For each memory block we allocate in * CHtmlDisp::operator new, we keep track of whether the memory came * from the formatter heap or from the system heap. */ enum htmldisp_heapid_t { HTMLDISP_HEAPID_SYS = 0, /* system heap */ HTMLDISP_HEAPID_FMT = 1 /* formatter heap */ }; /* ------------------------------------------------------------------------ */ /* * Whitespace search object. This lets us search a series of display * items to find and remove trailing whitespace. */ class CHtmlDisp_wsinfo { public: CHtmlDisp_wsinfo() { /* we haven't found any whitespace yet */ last_ws_disp_ = 0; } /* * Scan for and remove trailing whitespace. We'll start with the * given display item and scan ahead to (but not including) 'nxt', or * the end of the list if 'nxt' is null. We'll trim off any * contiguous run of whitespace we find at the end of the display * list. */ void remove_trailing_whitespace(class CHtmlSysWin *win, class CHtmlDisp *disp, class CHtmlDisp *nxt); /* * Hide trailing whitespace. This doesn't actually remove any * trailing whitespace, but merely suppresses the display of trailing * whitespace on a line. */ void hide_trailing_whitespace(class CHtmlSysWin *win, class CHtmlDisp *disp, class CHtmlDisp *nxt); /* * For the display item's use: remember a position in the current * display item as the start of the trailing whitespace in this * item. If we find that this is the last item with text, we'll * come back here to remove trailing whitespace later. */ void set_ws_pos(class CHtmlDisp *disp, void *pos) { /* if we don't have a previous position already, set this one */ if (last_ws_disp_ == 0) { last_ws_disp_ = disp; last_ws_pos_ = pos; } } /* * For the display item's use: clear the trailing whitespace * position. If we find that the current display item doesn't have * any whitespace, but it has something that negates the "trailing" * status of any previous whitespace (such as more non-whitespace * text), we need to forget about the previous whitespace, since * it's not trailing after all. */ void clear_ws_pos() { last_ws_disp_ = 0; } private: /* last display item containing whitespace */ class CHtmlDisp *last_ws_disp_; /* * position of whitespace in last display item; this context * information is meaningful only to the display item */ void *last_ws_pos_; }; /* ------------------------------------------------------------------------ */ /* * Display site implementation. This class can be multiply inherited into * a CHtmlDisp subclass to implement a CHtmlSysImageDisplaySite interface * on the display object. * * To use this mix-in with a CHtmlDisp subclass, the subclass must do the * following: * * 1. Add this class to its base class list * * 2. Explicitly call our constructor from the subclass constructor, * passing us the window object, the address of the 'pos_' record from the * subclass, and the image object. * * 3. On a call to the subclass's virtual unset_win() method, explicitly * call our unset_win() method. * * 4. On a call to the subclass's virtual cancel_playback() method, * explicitly call our cancel_playback() method. */ class CHtmlDispDisplaySite: public CHtmlSysImageDisplaySite { public: /* create */ CHtmlDispDisplaySite(class CHtmlSysWin *win, const CHtmlRect *pos, class CHtmlResCacheObject *image); /* destroy */ virtual ~CHtmlDispDisplaySite(); /* unset our window reference */ void unset_win(); /* cancel playback - if our image is animated, we'll halt it */ void cancel_playback(); /* get/set the current image being displayed in the site */ class CHtmlResCacheObject *get_site_image() const { return site_image_; } void set_site_image(class CHtmlResCacheObject *image); /* -------------------------------------------------------------------- */ /* * CHtmlSysImageDisplaySite interface implementation */ /* invalidate an area of the display site */ virtual void dispsite_inval(unsigned int x, unsigned int y, unsigned int width, unsigned int height); /* set a timer event */ virtual void dispsite_set_timer(unsigned long delay_ms, class CHtmlSysImageAnimated *image); /* cancel timer events */ virtual void dispsite_cancel_timer(class CHtmlSysImageAnimated *image); protected: /* timer callback */ static void img_timer_cb(void *ctx); /* the position record from the CHtmlDisp object */ const CHtmlRect *posp_; /* the window we're associated with */ class CHtmlSysWin *win_; /* * our timer - this is used if the object being displayed requests * timer operations from its display site (i.e., us) */ class CHtmlSysTimer *timer_; /* the image we're displaying */ class CHtmlResCacheObject *site_image_; }; /* ------------------------------------------------------------------------ */ /* * Basic display object */ class CHtmlDisp { public: CHtmlDisp() { nxt_ = 0; line_id_bit_ = 0; } virtual ~CHtmlDisp() { } /* * Unset the window - receive notification that the window is about to * be deleted. Most display items don't keep a reference to their * window, so this does nothing by default. This can be overridden in * subclasses that keep a reference to the containing window, so they * know the reference is not usable after this point. */ virtual void unset_win() { } /* * Cancel playback. This should stop playback of any active media in * the display item; for example, animations should be halted. */ virtual void cancel_playback() { } /* memory allocator - use the formatter for memory management */ static void *operator new(size_t siz, class CHtmlFormatter *formatter); static void operator delete(void *ptr); /* * Get/set the line ID. The formatter needs to be able to tell when * two adjacent items in the display list are in different lines. * To accomplish this, it tags each item with a line ID; upon * starting a new line, it increments the ID. If the line ID's of * two adjacent items are the same, the items are on the same line, * otherwise they're on different lines. Note that since we only * need to distinguish adjacent items, we don't need a lot of * precision in the line ID; we keep more than one bit, though, to * allow for line ID's to be updated in the formatter *too often* * (more than once per line), since it's often difficult to * increment the line ID exactly once per line. */ int get_line_id() const { return line_id_bit_; } void set_line_id(int id) { line_id_bit_ = id; } /* * Draw the object in a window. If the selection range intersects * the item, the appropriate portion should be drawn selected, if it * has a special selected appearance. If 'clicked' is true, it * means the mouse button is being held down with the mouse over the * item, so the item should be drawn with any appropriate feedback. */ virtual void draw(class CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, int clicked) = 0; /* * Get the cursor type that should be used when the cursor is over * this item. Returns the standard arrow cursor by default. */ virtual Html_csrtype_t get_cursor_type(class CHtmlSysWin *, class CHtmlFormatter *, int /*x*/, int /*y*/, int /*more_mode*/) const { return HTML_CSRTYPE_ARROW; } /* delete this item and all following items in its list */ static void delete_list(CHtmlDisp *head); /* * Measure the native width of this display object. This is the * amount of space that the object would take up without any * external constraints. For text, this is the width of the text in * the current font; for a picture, it's the width of the graphic or * the declared size. */ virtual int measure_width(class CHtmlSysWin *win) = 0; /* * Get the amount of space this item needs above the text baseline * for displaying text. Generally, items that don't display text * should return zero, since they don't contribute to the text area. */ virtual int get_text_height(class CHtmlSysWin *) { return 0; } /* * Get the overall vertical space needs for the item. text_height * is the vertical space above the text baseline used by the tallest * text item. *above_base is the amount of space this item needs * above the text baseline, and *total is the amount of vertical * space this item needs overall. */ virtual void get_vertical_space(class CHtmlSysWin *win, int text_height, int *above_base, int *below_base, int *total) = 0; /* * Set my position, given the top of the area for the current line, * the total height of the line, the offset of the text baseline from * the top of the line area, and the maximum height of the text in the * line. The default implementation simply sets this item aligned at * the top of the line. */ virtual void set_line_pos(class CHtmlSysWin *, int left, int top, int /*total_height*/, int /*text_base_offset*/, int /*max_text_height*/) { pos_.offset(left - pos_.left, top - pos_.top); } /* * Receive notification that the item has been moved from the deferred * list to the normal display list. Only certain types of display * items are deferred, so this default implementation does nothing and * must be overridden for deferrable items. * * line_spacing is the vertical whitespace above the current line; the * item should generally be vertically offset by this amount. */ virtual void format_deferred(class CHtmlSysWin *, class CHtmlFormatter *, long /*line_spacing*/) { } /* get next object in display list */ CHtmlDisp *get_next_disp() const { return nxt_; } /* * Find the line break position, given the amount of space * remaining in the current line. If the current display object can * be broken into two pieces such that the first piece fits into the * availble space but adding the second piece would not, we will * break it into two pieces, creating a new display object for the * second piece and linking the new object into the list, and return * the new object. If the current object fits in its entirety, * we'll see if we can break within the next object. prev_char is * the last character of text from the previous display object, or * zero if the previous object did not contain text; if two text * objects are adjacent, they'll use this in determining if a line * break can be inserted between them. If prv and prv_pos are * non-null, and we find that this object won't fit and we can't * break before it, we'll call the previous object's * find_line_break_prv method to tell it that it should break at the * previous break position it found. */ virtual CHtmlDisp *find_line_break(class CHtmlFormatter *formatter, class CHtmlSysWin *win, long space_avail, class CHtmlLineBreak *break_pos); /* * Do a break at a position we previously found. 'break_pos' is the * break position information that we previously set up. */ virtual CHtmlDisp *do_line_break(class CHtmlFormatter *formatter, class CHtmlSysWin *win, class CHtmlLineBreak *break_pos); /* * Do a break to the right of a position we previously found, skipping * leading spaces on the new line. This is called when the break * position was in a preceding item, but it found that it had only * whitespace after the break position; we should simply break at the * start of this item, or wherever the next non-whitespace can be * found. */ virtual CHtmlDisp *do_line_break_skipsp(class CHtmlFormatter *formatter, class CHtmlSysWin *win); /* * Find the location of trailing whitespace in the display item. If * the item doesn't have any trailing whitespace, but it does have * text or something else that would prevent any previous whitespace * from being the trailing whitespace, this should clear the * whitespace position in the search state. If we have trailing * whitespace, this should set the position in the search state * object so that we can come back and remove this whitespace. By * default, this routine does nothing, since default display items * don't affect the text stream. */ virtual void find_trailing_whitespace(class CHtmlDisp_wsinfo *) { } /* * Remove trailing whitespace that we previously found with a call to * find_trailing_whitespace(). * * 'pos' is the position of the whitespace that this object set on the * previous call to find_trailing_whitespace(). If 'pos' is null, it * means that the start of the run of whitespace was in an earlier * item in the list, in which case this item must be all whitespace * and can be made completely invisible. */ virtual void remove_trailing_whitespace(class CHtmlSysWin *, void * /*pos*/) { } /* * hide trailing whitespace - this doesn't actually delete the * whitespace, but merely prevents it from being displayed visually */ virtual void hide_trailing_whitespace(class CHtmlSysWin *, void * /*pos*/) { } /* * Measure the width of the trailing whitespace, if any. */ virtual long measure_trailing_ws_width(class CHtmlSysWin *) const { return 0; } /* * Measure the metrics of this item for use in a table cell. * Measures this item's contribution to the minimum and maximum * width of items in the cell. */ virtual void measure_for_table(class CHtmlSysWin *win, class CHtmlTableMetrics *metrics); /* * Measure the metrics of this item for use in a banner. Measurs * the item's contribution to the minimum and maximum width of the * items within the banner, so that we can determine the proper * horizontal extent of a vertical banner. This does almost exactly * the same thing as measure_for_table, and in most cases simply * calls that method to carry out the work. However, in a few * cases, we want to do something different; in particular, for tags * that can be based on a percentage of the window size, we don't * make a contribution to the banner sizes at all, since we won't * know the window size until we know the banner size, hence we * can't guess about our contribution. */ virtual void measure_for_banner(class CHtmlSysWin *win, class CHtmlTableMetrics *metrics) { /* for most cases, simply do the same thing as we'd do in a table */ measure_for_table(win, metrics); } /* get/set the position of this display object */ CHtmlRect get_pos() const { return pos_; } void set_pos(CHtmlRect *pos) { pos_ = *pos; } void set_pos(int left, int top, int right, int bottom) { pos_.set(left, top, right, bottom); } /* * Insert me after another display object in a list. This can only * be used to add a single item. */ void add_after(CHtmlDisp *prv) { nxt_ = prv->nxt_; prv->nxt_ = this; } /* * Insert a list after another display object in a list. This adds * me and any other items following me in my list. */ void add_list_after(CHtmlDisp *prv); /* make me the end of my chain by forgetting my next item */ void clear_next_disp() { nxt_ = 0; } /* invalidate my area on the display */ void inval(class CHtmlSysWin *win); /* * Invalidate my area on the display, limiting the invalidation to * the given selection range. By default, we'll just invalidate our * area. Some subclasses (text, in particular) should override this * to limit the invalidation to the selected range. */ virtual void inval_range(class CHtmlSysWin *win, unsigned long start_ofs, unsigned long end_ofs) { /* by default, invalidate our entire area */ inval(win); } /* * Invalidate my area on the display only if I'm a link. By * default, I won't do anything, since default dispaly items aren't * linked. */ virtual void inval_link(class CHtmlSysWin *) { } /* invalidate if appropriate for a change in the link 'clicked' status */ virtual void on_click_change(class CHtmlSysWin *win) { inval(win); } /* * Get my offset in the linear array containing the text stream. * If this item doesn't contain text, return the text offset of the * next item in the display list. By default, items don't contain * any text. */ virtual unsigned long get_text_ofs() const { return (nxt_ == 0 ? ULONG_MAX : nxt_->get_text_ofs()); } /* * Determine if the item contains a given text offset. By default, * we'll simply return false, since non-text items don't contain any * text offsets. */ virtual int contains_text_ofs(unsigned long /*ofs*/) const { return FALSE; } /* determine if I intersect the given text offset range */ virtual int overlaps_text_range(unsigned long l, unsigned long r) const { return get_text_ofs() >= l && get_text_ofs() <= r; } /* * Determine if the item is past a given text offset. If there's no * next item, considers this item part of the range (hence returns * false). * * If there's a next item, we return true if the next item's offset is * greater than the given offset. If the next item's offset is less * than the given offset, we're obviously not beyond the offset because * the next item is still within the range, hence we must return false. * If the next item's offset is equal to the given offset, we haven't * yet reached that offset, so we want to return false in this case as * well. */ virtual int is_past_text_ofs(unsigned long ofs) const { return nxt_ == 0 ? FALSE : (nxt_->get_text_ofs() > ofs); } /* * Get the position of a character within this item. txtofs is the * offset within the linear text array for the text stream of the * character we're interested in. By default, we don't have any * text, so we'll just return our own position. */ virtual CHtmlPoint get_text_pos(class CHtmlSysWin *, unsigned long /*txtofs*/) { return CHtmlPoint(pos_.left, pos_.top); } /* * Get the text offset of a character, given its position. Since * basic objects don't contain text, this will simply return the * object's text offset by default. */ virtual unsigned long find_textofs_by_pos(class CHtmlSysWin *, CHtmlPoint) const { return get_text_ofs(); } /* * Append my text to a string buffer. By default, this does * nothing, since default objects have no text. */ virtual void add_text_to_buf(CStringBuf *) const { } /* * add my text to a buffer, clipping the text to the given starting * and ending offsets */ virtual void add_text_to_buf_clip(CStringBuf *buf, unsigned long startofs, unsigned long endofs) const { } /* * Get the hyperlink object associated with the item. Returns null if * this item isn't hyperlinked. If the item is associated with a * hypertext link, this returns the link object. * * The base display item isn't linkable itself, but it can inherit a * division link from an enclosing DIV. So, by default, we just look * for an inherited DIV link. */ virtual class CHtmlDispLink *get_link(class CHtmlFormatter *formatter, int /*x*/, int /*y*/) const; /* * Get the link I inherit from my enclosing DIV, if any. This looks * for the innermost enclosing DIV that has an associated link. */ class CHtmlDispLinkDIV *find_div_link(class CHtmlFormatter *formatter) const; /* * Get the container link. If this item is part of a group of items * linked to a containing tag, this returns the containing * link. This ignores any image map or other internal structure. */ virtual class CHtmlDispLink *get_container_link() const { return 0; } /* * Get the character set for this item's text, if applicable. Returns * true if we successfully fill in 'c' with a character set, false if * not. */ virtual int get_charset(oshtml_charset_id_t &c) const { return FALSE; } /* * Determine if this item can qualify as an inexact hit when seeking * the item nearest a mouse location. Since inexact hits are used * to determine text selection ranges, we only allow inexact matches * of text items. */ virtual int allow_inexact_hit() const { return FALSE; } /* * Determine if this item is in the background as far as mouse * clicks are concerned. If this returns true, we'll ignore this * object for the purposes of locating a mouse click, and instead * look further in the list for an overlaid object that contains the * click. Table cells use this, for example, so that mouse clicks * go to the cell contents rather than to the cell object itself. */ virtual int is_in_background() const { return FALSE; } /* * Get my ALT text, if I have any. Some object types, such as * IMG's, have an alternative textual representation that they can * display when a textual description is needed. */ virtual const textchar_t *get_alt_text() const { return 0; } protected: /* * Basic deferred formatting implementation for floating margin items. * The subclass must calculate the height and provide the margin * alignment, and we'll set things up in the formatter accordingly. */ void basic_format_deferred(class CHtmlSysWin *win, class CHtmlFormatter *formatter, long line_spacing, HTML_Attrib_id_t align, long height); /* position */ CHtmlRect pos_; private: /* next object in the list */ CHtmlDisp *nxt_; /* line ID bit */ int line_id_bit_ : 8; }; /* ------------------------------------------------------------------------ */ /* * A display item representing a DIV (division) tag. This doesn't display * anything; it's an internal placeholder to keep track of the contents of * the division. */ class CHtmlDispDIV: public CHtmlDisp { public: CHtmlDispDIV(unsigned long startofs, unsigned long endofs) { /* remember the text range */ startofs_ = startofs; endofs_ = endofs; /* we don't know our parent DIV yet */ parent_div_ = 0; /* we don't have a DIV-level hyperlink */ div_link_ = 0; /* we don't know the last display item in the DIV yet */ div_tail_ = 0; } /* get our associated hyperlink object */ virtual class CHtmlDispLink *get_link(class CHtmlFormatter *formatter, int x, int y) const; /* get our start/end offset */ unsigned long get_start_ofs() const { return startofs_; } unsigned long get_end_ofs() const { return endofs_; } /* we have no visual presence, so there's nothing to draw */ virtual void draw(class CHtmlSysWin *, unsigned long, unsigned long, int clicked) { } /* we don't take up any space */ virtual int measure_width(class CHtmlSysWin *) { return 0; } virtual void get_vertical_space(class CHtmlSysWin *, int, int *above_base, int *below_base, int *total) { *above_base = *below_base = *total = 0; } /* get/set the parent DIV */ CHtmlDispDIV *get_parent_div() const { return parent_div_; } void set_parent_div(CHtmlDispDIV *par) { parent_div_ = par; } /* get/set the DIV-level hyperlink object */ class CHtmlDispLinkDIV *get_div_link() const { return div_link_; } void set_div_link(class CHtmlDispLinkDIV *link) { div_link_ = link; } /* * Get/set the last display item in the DIV. The formatter gives us * this information when building the display list; the DIV covers * everything from the next item after the DIV to this last item. */ CHtmlDisp *get_div_tail() const { return div_tail_; } void set_div_tail(CHtmlDisp *tail) { div_tail_ = tail; } private: /* the range of text offsets this division covers */ unsigned long startofs_; unsigned long endofs_; /* the last display item in the DIV */ CHtmlDisp *div_tail_; /* the parent DIV display item */ CHtmlDispDIV *parent_div_; /* our DIV-level hyperlink object */ class CHtmlDispLinkDIV *div_link_; }; /* ------------------------------------------------------------------------ */ /* * flag. This doesn't actually display anything; it's just a flag * that we can insert into the display stream to tell us that word-wrapping * is being enabled or disabled. This lets us take the wrapping mode into * effect when calculating widths. */ class CHtmlDispNOBR: public CHtmlDisp { public: CHtmlDispNOBR(int nobr) { nobr_ = nobr; } /* * Measure for a table or banner. We don't take up any space, but we * do need to set the current wrapping status in the metrics. */ void measure_for_table(class CHtmlSysWin *win, class CHtmlTableMetrics *metrics); /* we don't have any on-screen appearance */ void draw(class CHtmlSysWin *, unsigned long, unsigned long, int) { } /* we don't take up any space */ int measure_width(class CHtmlSysWin *) { return 0; } void get_vertical_space(class CHtmlSysWin *, int, int *above_base, int *below_base, int *total) { *above_base = *below_base = *total = 0; } private: /* are we in a section? */ int nobr_; }; /* ------------------------------------------------------------------------ */ /* * Link object. This item doesn't actually have any display presence, * but is used to store the link information used by all of the linkable * objects within a ... group. Each linkable object refers * back to this object for their link information. * * The link object keeps track of the first and last display items * belonging to the group, so that group-wide operations (for example, * highlighting all of the items in the group) can be done. * * Although this object doesn't have a display presence, it's * implemented as a display item to simplify memory management - it goes * in the display list along with all of the other display items, so it * will be deleted along with them. */ /* clicked states */ const int CHtmlDispLink_none = 0x00; /* not clicked/hovering */ const int CHtmlDispLink_clicked = 0x01; /* mouse is being clicked */ const int CHtmlDispLink_hover = 0x02; /* mouse is hovering */ const int CHtmlDispLink_divhover = 0x04; /* mouse is hovering over DIV */ const int CHtmlDispLink_clickedoff = 0x08; /* mouse is clicked but off link */ class CHtmlDispLink: public CHtmlDisp { public: CHtmlDispLink(int style, int append, int noenter, CHtmlUrl *href, const textchar_t *title, size_t titlelen) { /* remember our style */ style_ = style; append_ = append; noenter_ = noenter; /* remember our link information */ if (href != 0) { href_.set_url(href); nohref_ = FALSE; } else nohref_ = TRUE; title_.set(title, titlelen); /* contained items will be added later */ first_ = last_ = 0; /* not yet clicked */ clicked_ = FALSE; /* presume we're not using special hover settings */ use_hover_fg_ = use_hover_bg_ = hover_underline_ = FALSE; } /* * Is this a "clickable" link? A clickable link is one with an active * UI presence, where clicking on the link activates the hyperlink. If * a link isn't clickable, the UI should treat a click on the link as * though it were a click on an ordinary, unlinked item. */ virtual int is_clickable_link() const { return TRUE; } /* set hover foreground color */ void set_hover_fg(HTML_color_t color) { hover_fg_ = color; use_hover_fg_ = TRUE; } /* set hover background color */ void set_hover_bg(HTML_color_t color) { hover_bg_ = color; use_hover_bg_ = TRUE; } /* set HOVER=UNDERLINE */ void set_hover_underline() { hover_underline_ = TRUE; } /* * get the hover foreground/background color - returns true if the * color is set, false if not */ int get_hover_fg(HTML_color_t *cp) const { *cp = hover_fg_; return use_hover_fg_; } int get_hover_bg(HTML_color_t *cp) const { *cp = hover_bg_; return use_hover_bg_; } /* add an underline while hovering? */ int hover_underline() const { return hover_underline_; } /* links have no effect on line breaking, since they're invisible */ CHtmlDisp *find_line_break(class CHtmlFormatter *, class CHtmlSysWin *, long, class CHtmlLineBreak *) { return 0; } /* add an item linked to this item */ void add_link_item(CHtmlDisp *item) { /* if we don't yet have a first item, remember it as the first */ if (first_ == 0) first_ = item; /* it's the last one we've heard about so far */ last_ = item; } /* get the first and last item in our list */ class CHtmlDisp *get_first_link_item() const { return first_; } class CHtmlDisp *get_last_link_item() const { return last_; } /* get the link style (LINK_STYLE_xxx) */ int get_style() const { return style_; } /* * Check if the link's contents are to be rendered in plain style * (i.e., not using any special link rendering style). The link is * drawn plain if its style is PLAIN, or its style is HIDDEN and the * mouse isn't currently over or clicking on the link. */ int is_plain() const { return (style_ == LINK_STYLE_PLAIN || (style_ == LINK_STYLE_HIDDEN && !(clicked_ & (CHtmlDispLink_hover | CHtmlDispLink_clicked)))); } /* check if the link is shown even when links are globally hidden */ int is_forced() const { return style_ == LINK_STYLE_FORCED; } /* get current clicked state */ int get_clicked() const { return clicked_; } /* * Set clicked state, and invalidate all items linked to me. * clicked is a combination of the CHtmlDispLink_xxx states (see * above). */ virtual void set_clicked(class CHtmlSysWin *win, int clicked); /* * Set the clicked state for my sub-items only. This doesn't notify * our DIV. */ void set_clicked_sub(class CHtmlSysWin *win, int clicked); /* * CHtmlDisp Overrides - mostly these are trivial implementations, * since we don't do much as a display item */ /* no display presence, so drawing is trivial */ void draw(class CHtmlSysWin *, unsigned long, unsigned long, int) { } /* no display area */ int measure_width(class CHtmlSysWin *) { return 0; } void measure_for_table(CHtmlSysWin *, CHtmlTableMetrics *) { } int get_text_height(class CHtmlSysWin *) { return 0; } void get_vertical_space(class CHtmlSysWin *, int, int *above_base, int *below_base, int *total) { *above_base = 0; *below_base = 0; *total = 0; } /* invalidate if I'm a link - I am, so invalidate me */ void inval_link(class CHtmlSysWin *win) { inval(win); } /* get my command-entering attributes */ int get_append() const { return append_; } int get_noenter() const { return noenter_; } public: /* * Link information. If nohref_ is set, it means that the link has * no HREF - this is appropriate for AREA tags with the NOHREF * attribute. */ CHtmlUrl href_; int nohref_ : 1; CStringBuf title_; protected: /* first and last display items linked to this item */ CHtmlDisp *first_; CHtmlDisp *last_; /* hover colors, foreground and background */ HTML_color_t hover_fg_; HTML_color_t hover_bg_; /* * flag indicating that I'm in the clicked state - i.e., the mouse * button was clicked down over this item, the mouse is still over * this item, and the mouse button is still being held down */ int clicked_; /* link style (LINK_STYLE_xxx) */ unsigned int style_ : 2; /* * NOENTER - true means that we don't automatically enter the * command, but allow the player to continue editing after adding * our HREF */ int noenter_ : 1; /* * APPEND - true means that we append our HREF to the command line * under construction, rather than clearing any existing command */ int append_ : 1; /* use the hover foreground/background colors */ int use_hover_fg_ : 1; int use_hover_bg_ : 1; /* * HOVER=UNDERLINE - true means that we add an underline when the mouse * is hovering on this link; false means that we leave the underlining * status as it is while hovering. */ int hover_underline_ : 1; }; /* * A special hyperlink object for a DIV tag. A DIV link isn't a real link; * it's just a proxy for the links within the DIV group. */ class CHtmlDispLinkDIV: public CHtmlDispLink { public: /* * create - a DIV link isn't a real hyperlink, so it doesn't have an * HREF or any display style; it's just a proxy for all of the actual * links within the division */ CHtmlDispLinkDIV(class CHtmlDispDIV *div); /* * DIV links aren't clickable. These links are for hovering purposes * only, and don't have any effect when clicked. For the purposes of * clicking, an item linked to a DIV link looks like an ordinary, * unlinked item. */ virtual int is_clickable_link() const { return FALSE; } /* set the clicked state */ virtual void set_clicked(class CHtmlSysWin *win, int clicked); protected: /* the DIV I belong to */ class CHtmlDispDIV *div_; }; #if 0 /* ------------------------------------------------------------------------ */ /* * Delegator display item. This display item type can be subclassed to * implement a plug-in class that can override virtuals from the * CHtmlDisp base class for any instance of any CHtmlDisp subclass. For * methods not overridden by a subclass of this class, we call the * corresponding method in the underlying object. */ class CHtmlDispDelegator: public CHtmlDisp { public: CHtmlDispDelegator(CHtmlDisp *underlying_object) { /* remember the underlying object */ delegate_ = underlying_object; } ~CHtmlDispDelegator() { /* delete the underlying object */ delete delegate_; } void draw(class CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, int clicked) { delegate_->draw(win, sel_start, sel_end, clicked); } Html_csrtype_t get_cursor_type(class CHtmlSysWin *win, class CHtmlFormatter *fmt, int x, int y, int more_mode) const { return delegate_->get_cursor_type(win, fmt, x, y, more_mode); } int measure_width(class CHtmlSysWin *win) { return delegate_->measure_width(win); } int get_text_height(class CHtmlSysWin *win) { return delegate_->get_text_height(win); } void get_vertical_space(class CHtmlSysWin *win, int text_height, int *above_base, int *below_base, int *total) { delegate_->get_vertical_space(win, text_height, above_base, below_base, total); } void set_line_pos(class CHtmlSysWin *win, int left, int top, int total_height, int text_base_offset, int max_text_height) { CHtmlRect pos; /* set the position in the delegate */ delegate_->set_line_pos(win, left, top, total_height, text_base_offset, max_text_height); /* copy the delegate's position */ pos = delegate_->get_pos(); set_pos(&pos); } void format_deferred(class CHtmlSysWin *win, class CHtmlFormatter *formatter, long line_spacing) { delegate_->format_deferred(win, formatter, line_spacing); } CHtmlDisp *find_line_break(class CHtmlFormatter *formatter, class CHtmlSysWin *win, long space_avail, class CHtmlLineBreak *break_pos) { return delegate_line_break(formatter, delegate_->find_line_break( formatter, win, space_avail, break_pos), break_pos); } CHtmlDisp *do_line_break(class CHtmlFormatter *formatter, class CHtmlSysWin *win, class CHtmlLineBreak *break_pos) { return delegate_line_break(formatter, delegate_->do_line_break( formatter, win, break_pos), break_pos); } CHtmlDisp *do_line_break_skipsp(class CHtmlFormatter *formatter, class CHtmlSysWin *win) { "Not Implemented (but this class isn't currently used)!"; } void find_trailing_whitespace(class CHtmlDisp_wsinfo *info) { delegate_->find_trailing_whitespace(info); } void remove_trailing_whitespace(class CHtmlSysWin *win, void *pos) { delegate_->remove_trailing_whitespace(win, pos); } long measure_trailing_ws_width(class CHtmlSysWin *win) const { return delegate_->measure_trailing_ws_width(win); } void measure_for_table(class CHtmlSysWin *win, class CHtmlTableMetrics *metrics) { delegate_->measure_for_table(win, metrics); } void on_click_change(class CHtmlSysWin *win) { delegate_->on_click_change(win); } unsigned long get_text_ofs() const { return delegate_->get_text_ofs(); } int contains_text_ofs(unsigned long ofs) const { return delegate_->contains_text_ofs(ofs); } virtual int overlaps_text_range(unsigned long l, unsigned long r) const { return delegate_->overlaps_text_range(l, r); } int is_past_text_ofs(unsigned long ofs) const { return delegate_->is_past_text_ofs(ofs); } CHtmlPoint get_text_pos(class CHtmlSysWin *win, unsigned long txtofs) { return delegate_->get_text_pos(win, txtofs); } unsigned long find_textofs_by_pos(CHtmlSysWin *win, CHtmlPoint pos) const { return delegate_->find_textofs_by_pos(win, pos); } void add_text_to_buf(CStringBuf *buf) const { delegate_->add_text_to_buf(buf); } void add_text_to_buf_clip(CStringBuf *buf, unsigned long startofs, unsigned long endofs) const { delegate_->add_text_to_buf_clip(buf, startofs, endofs); } int allow_inexact_hit() const { return delegate_->allow_inexact_hit(); } int is_in_background() const { return delegate_->is_in_background(); } protected: /* * Delegate the result of a line break within this item. We apply * this to the return value of any line breaking routine. A line * break routine can return null, which we'll map to null; it can * return the underlying item, in which case we'll return 'this', * since we always want to cover references to the underlying item; * or it can return an entirely new item, in which case we'll create * a clone of this delegator to cover the new item. */ CHtmlDisp *delegate_line_break(class CHtmlFormatter *formatter, CHtmlDisp *line_start, class CHtmlLineBreak *break_pos); /* * Clone myself, setting the underlying delegate object to the given * object. This must be overridden by each concrete subclass to * create an object of its type and set up the new object with the * same values. */ virtual CHtmlDisp *clone_delegator(class CHtmlFormatter *formatter, CHtmlDisp *delegate) = 0; /* * the original object to which we delegate methods not overridden * by our subclasses */ CHtmlDisp *delegate_; }; #endif // removed /* ------------------------------------------------------------------------ */ /* * "Body" display object. This is a pseudo-display object representing the * entire document body. We keep one body object per window. */ class CHtmlDispBody: public CHtmlDisp, public CHtmlDispDisplaySite { public: CHtmlDispBody(class CHtmlSysWin *win) : CHtmlDisp(), CHtmlDispDisplaySite(win, &pos_, 0) { /* I'm a fake display object that takes up no actual screen space */ pos_.set(0, 0, 0, 0); } /* * Animated image display site - invalidate a portion of the display * site. We must override this, because we need to handle this * specially through our window: only our window knows exactly how it * draws the background image. */ void dispsite_inval(unsigned int x, unsigned int y, unsigned int width, unsigned int height); /* forward unset_win() on to the display site */ void unset_win() { CHtmlDispDisplaySite::unset_win(); } /* forward cancel_playback() on to the display site */ void cancel_playback() { CHtmlDispDisplaySite::cancel_playback(); } /* we're not a real display item, so we don't do a bunch of things */ void draw(class CHtmlSysWin *, unsigned long, unsigned long, int) { } int measure_width(class CHtmlSysWin *) { return 0; } void get_vertical_space(class CHtmlSysWin *, int, int *above, int *below, int *total) { *above = *below = *total = 0; } }; /* ------------------------------------------------------------------------ */ /* * Text display object. Each text object keeps track of its font and * text string. */ class CHtmlDispText: public CHtmlDisp { public: CHtmlDispText(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs); /* use the I-beam cursor when over text */ Html_csrtype_t get_cursor_type(class CHtmlSysWin *, class CHtmlFormatter *, int, int, int) const { return HTML_CSRTYPE_IBEAM; } /* get my width */ int measure_width(class CHtmlSysWin *win); /* invalidate a range */ virtual void inval_range(class CHtmlSysWin *win, unsigned long start_ofs, unsigned long end_ofs); /* draw the text */ void draw(class CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, int clicked); /* do a line break */ CHtmlDisp *find_line_break(class CHtmlFormatter *formatter, class CHtmlSysWin *win, long space_avail, class CHtmlLineBreak *break_pos); /* do a break at a position we previously found */ CHtmlDisp *do_line_break(class CHtmlFormatter *formatter, class CHtmlSysWin *win, class CHtmlLineBreak *break_pos); /* do a break, skipping whitespace from a preceding item */ CHtmlDisp *do_line_break_skipsp(class CHtmlFormatter *formatter, class CHtmlSysWin *win); /* find/remove/hide trailing whitespace */ void find_trailing_whitespace(class CHtmlDisp_wsinfo *info); void remove_trailing_whitespace(class CHtmlSysWin *win, void *pos); void hide_trailing_whitespace(class CHtmlSysWin *win, void *pos); /* measure trailing whitespace */ long measure_trailing_ws_width(class CHtmlSysWin *win) const; /* measure for inclusion in a table cell */ void measure_for_table(class CHtmlSysWin *win, class CHtmlTableMetrics *metrics); /* get the text height */ int get_text_height(class CHtmlSysWin *win); /* get my overall vertical space needs */ void get_vertical_space(class CHtmlSysWin *win, int text_height, int *above_base, int *below_base, int *total); /* set my position */ void set_line_pos(class CHtmlSysWin *win, int left, int top, int total_height, int text_base_offset, int max_text_height); /* get my text offset */ unsigned long get_text_ofs() const { return txtofs_; } /* see if I contain a given text offset */ int contains_text_ofs(unsigned long ofs) const { return (ofs >= txtofs_ && ofs < txtofs_ + len_); } int overlaps_text_range(unsigned long l, unsigned long r) const { return (txtofs_ <= r && txtofs_ + len_ >= l); } /* see if I'm past a given text offset */ int is_past_text_ofs(unsigned long ofs) const { return (txtofs_ > ofs); } /* * this is a text item, so allow inexact hits when seeking the items * contained in a selection range */ virtual int allow_inexact_hit() const { return TRUE; } /* get the position of a character within this item */ CHtmlPoint get_text_pos(class CHtmlSysWin *win, unsigned long txtofs); /* find the text offset of a given position */ unsigned long find_textofs_by_pos(CHtmlSysWin *win, CHtmlPoint pos) const; /* add my text to a buffer */ void add_text_to_buf(CStringBuf *buf) const { buf->append(txt_, len_); } /* * add my text to a buffer, clipping the text to the given starting * and ending offsets */ void add_text_to_buf_clip(CStringBuf *buf, unsigned long startofs, unsigned long endofs) const; /* get my text length */ size_t get_text_len() const { return len_; } protected: /* do some special construction-time initialization for linked text */ void linked_text_cons(class CHtmlSysWin *win, class CHtmlDispLink *link); /* * service routine for linkable subclasses: draw the text in the style * appropriate for a linked item */ void draw_as_link(class CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, class CHtmlDispLink *link); /* * service routine for linkable subclasses: get the cursor type over * this as a linked text item */ Html_csrtype_t get_cursor_type_for_link( class CHtmlSysWin *win, class CHtmlFormatter *formatter, int x, int y, int more_mode) const; /* * Find a line break in character-wrapping mode. This method is * provided so that each subclass of the text item can easily * implement a separate subclass that wraps in character-wrapping * mode; to do this, subclass the subclass, and implement * find_line_break() as simply a call to this routine. */ CHtmlDisp *find_line_break_char(class CHtmlFormatter *formatter, class CHtmlSysWin *win, unsigned long space_avail, class CHtmlLineBreak *break_pos); /* * Get the effective last character of our text, for the purposes of * calculating line breaks. By default, we'll return the last * character from our text. * * For subclasses that implement character-wrap mode, this should be * overridden to return a space instead of the actual last character. * In character-wrap mode, no matter what our last character is, we * can break to its right; we can indicate this by returning a space * as our last character, since in any wrapping mode we can break to * the right of a space. */ virtual textchar_t get_break_pos_last_char() { return *(txt_ + len_ - 1); } /* * Determine if we allow a line break at the given character position. * prev_char is the previous character, and cur_char is the current * character. * * Our default implementation is for word-wrapping mode. We allow * breaking at a space, or after a hyphen not followed by a another * hyphen. * * Subclasses can override this to implement character-wrap mode, * which should generally allow breaking anywhere. */ virtual int allow_line_break_at(textchar_t prev_char, textchar_t cur_char) { /* * If this is a space, or the previous character was a hyphen and * this character isn't a hyphen, we can break here. */ return is_space(cur_char) || (prev_char == '-' && cur_char != '-'); } /* * Determine if we allow line breaking after the given character. Our * default implementation is for word-wrapping mode, so we allow line * breaking after a space, or after a hyphen not followed by another * hyphen. */ virtual int allow_line_break_after(textchar_t prev_char, textchar_t cur_char) { /* * if the previous character is a space, or the previous character * is a hyphen and the current character isn't a space, we can * break after the previous character */ return is_space(prev_char) || (prev_char == '-' && cur_char != '-'); } /* do a line break at a given position */ CHtmlDisp *break_at_pos(class CHtmlFormatter *formatter, class CHtmlSysWin *win, const textchar_t *last_break_pos); /* create a new display item of the same type for breaking the line */ virtual CHtmlDispText *create_disp_for_break( class CHtmlFormatter *formatter, class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs) { /* default implementation returns new item of base text type */ return new (formatter) CHtmlDispText(win, font, txt, len, txtofs); } /* * Draw the text. If 'link' is true, draw with the appropriate * style for a hypertext link. If 'clicked' is true, the mouse * button is being held down with the mouse over the item. */ virtual void draw_text(class CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, class CHtmlDispLink *link, int clicked, const CHtmlRect *pos); /* get our character set */ int get_charset(oshtml_charset_id_t &c) const; /* get the font to use for drawing our text */ class CHtmlSysFont *get_draw_text_font(class CHtmlSysWin *win, class CHtmlDispLink *link, int clicked); /* * Draw a portion of the text, with optional highlighting. Draws * the lesser of len and *rem; updates *p, *pos, *rem, and *ofs * according to the amount of text actually drawn. */ void draw_text_part(class CHtmlSysWin *win, int hilite, CHtmlRect *pos, const textchar_t **p, size_t *rem, unsigned long *ofs, unsigned long len, class CHtmlSysFont *font); /* font for displaying our text */ class CHtmlSysFont *font_; /* our text, and its address in the formatter's text array */ const textchar_t *txt_; unsigned long txtofs_; unsigned short len_; /* * Display length - this is the amount of text we're actually * displaying, which can be less than our actual underlying text * length (usually because we're suppressing the display of trailing * spaces). */ unsigned short displen_; /* portion of text above the text baseline */ unsigned short ascent_ht_; }; /* ------------------------------------------------------------------------ */ /* * Macro to define a character-wrapping version of a CHtmlDispText * subclass. Each CHtmlDispText subclass should define a subclass of * itself for character-wrapping mode. This version requires the explicit * list of formals and actuals to the constructor. */ #define DEF_CHARWRAP_SUBCLASS_CT(cls, ct_formals, ct_actuals, cl_actuals) \ class cls##CharWrap: public cls \ { \ public: \ /* constructor */ \ cls##CharWrap ct_formals : cls ct_actuals { } \ \ /* allow wrapping to our right by indicating we end with a space */ \ virtual textchar_t get_break_pos_last_char() \ { return ' '; } \ \ /* allow breaking at any of our characters */ \ virtual int allow_line_break_at(textchar_t, textchar_t) \ { return TRUE; } \ \ /* allow breaking after any character */ \ virtual int allow_line_break_after(textchar_t, textchar_t) \ { return TRUE; } \ \ /* create a new display item of the same type for breaking the line */ \ virtual CHtmlDispText *create_disp_for_break( \ class CHtmlFormatter *formatter, class CHtmlSysWin *win, \ class CHtmlSysFont *font, const textchar_t *txt, size_t len, \ unsigned long txtofs) \ { \ /* return a new item of the same type */ \ return new (formatter) cls##CharWrap cl_actuals; \ } \ } /* define a char-wrapping subclass with the standard constructor */ #define DEF_CHARWRAP_SUBCLASS(cls) \ DEF_CHARWRAP_SUBCLASS_CT(cls, \ (class CHtmlSysWin *win, \ class CHtmlSysFont *font, \ const textchar_t *txt, size_t len, \ unsigned long txtofs), \ (win, font, txt, len, txtofs), \ (win, font, txt, len, txtofs)) /* ------------------------------------------------------------------------ */ /* * Define a character-wrapping version of the base text display item. * We'll create one of these whenever we create a regular text item and * the formatter is currently in character-wrapping mode. */ DEF_CHARWRAP_SUBCLASS(CHtmlDispText); /* ------------------------------------------------------------------------ */ /* * Define some add-in methods for linked text subclasses. To define a * linked version of a text subclass, add this macro to the 'public' * section of the class definition. This will define some overrides for * CHtmlDispText virtuals, and declare a link_ member to store the * CHtmlDispLink pointer for the link. */ #define LINKED_TEXT_METHODS \ /* use the hand cursor when over a link */ \ Html_csrtype_t get_cursor_type(class CHtmlSysWin *win, \ class CHtmlFormatter *formatter, \ int x, int y, int more_mode) const \ { \ return get_cursor_type_for_link(win, formatter, x, y, more_mode); \ } \ \ /* draw as a link */ \ void draw(class CHtmlSysWin *win, unsigned long sel_start, \ unsigned long sel_end, int /*clicked*/) \ { draw_as_link(win, sel_start, sel_end, link_); } \ \ /* invalidate if I'm a link - I am, so invalidate me */ \ void inval_link(class CHtmlSysWin *win) { inval(win); } \ \ /* get my link object */ \ CHtmlDispLink *get_link(class CHtmlFormatter *, int, int) const \ { return link_; } \ \ /* get my containing link object */ \ class CHtmlDispLink *get_container_link() const \ { return link_; } \ \ /* get my ALT text - return the TITLE text for the underlying link */ \ virtual const textchar_t *get_alt_text() const \ { \ /* if I have a link and it has a TITLE, return the link's title */ \ if (link_ != 0 && link_->title_.get() != 0) \ return link_->title_.get(); \ \ /* return the inherited ALT text */ \ return CHtmlDispText::get_alt_text(); \ } \ \ protected: \ /* link object */ \ CHtmlDispLink *link_; \ \ public: /* * Linked-text initialization. Remember our link object in our 'link_' * member, and perform additional construction-time intialization. This * must be called in the subclass constructor for all linked text * subclasses. */ #define linked_text_init(win, link) \ ((link_ = (link)), linked_text_cons(win, link)) /* ------------------------------------------------------------------------ */ /* * Link text display object. When text appears within a ... * region, we'll create one of these objects instead of the * standard text display object. */ class CHtmlDispTextLink: public CHtmlDispText { public: CHtmlDispTextLink(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs, class CHtmlDispLink *link) : CHtmlDispText(win, font, txt, len, txtofs) { /* initialize the linked text item */ linked_text_init(win, link); } /* define the linked text methods */ LINKED_TEXT_METHODS /* create a new display item of the same type for breaking the line */ CHtmlDispText *create_disp_for_break (class CHtmlFormatter *formatter, class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs) { /* default implementation returns new item of base text type */ return new (formatter) CHtmlDispTextLink(win, font, txt, len, txtofs, link_); } }; /* * Define a character-wrapping version of the linked text display item */ DEF_CHARWRAP_SUBCLASS_CT(CHtmlDispTextLink, (class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs, class CHtmlDispLink *link), (win, font, txt, len, txtofs, link), (win, font, txt, len, txtofs, link_)); /* ------------------------------------------------------------------------ */ /* * Preformatted text display object. This acts mostly like a normal * text object, but doesn't break at the margins; instead, it just runs * on as though it can't break internally. Preformatted text should * always be set on separate lines from normal text, so it should never * be necessary to consider line breaks in any line containing * preformatted text. */ class CHtmlDispTextPre: public CHtmlDispText { public: CHtmlDispTextPre(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs) : CHtmlDispText(win, font, txt, len, txtofs) { } /* do a line break */ CHtmlDisp *find_line_break(class CHtmlFormatter *formatter, class CHtmlSysWin *win, long space_avail, class CHtmlLineBreak *break_pos); /* measure for inclusion in a table cell */ void measure_for_table(class CHtmlSysWin *win, class CHtmlTableMetrics *metrics); }; /* ------------------------------------------------------------------------ */ /* * Linkable subclass of preformatted text object */ class CHtmlDispTextPreLink: public CHtmlDispTextPre { public: CHtmlDispTextPreLink(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs, CHtmlDispLink *link) : CHtmlDispTextPre(win, font, txt, len, txtofs) { /* initialize the linked text item */ linked_text_init(win, link); } /* add in the linked text methods */ LINKED_TEXT_METHODS }; /* ------------------------------------------------------------------------ */ /* * Display item for text of a command input line. This behaves mostly * the same as a normal text display item, but adds the ability to * compare itself against another text display item and invalidate the * screen where they differ. */ class CHtmlDispTextInput: public CHtmlDispText { public: CHtmlDispTextInput(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs); ~CHtmlDispTextInput(); /* compare myself to another text display list */ void diff_input_lists(CHtmlDispTextInput *other, class CHtmlSysWin *win); /* create a new display item of the same type for breaking the line */ virtual CHtmlDispText *create_disp_for_break( class CHtmlFormatter *, class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs) { /* * Create and remember the new item as my continuation item. * Since this item is open for editing, allocate space in system * memory (i.e., use null as the 'formatter' placement argument * to operator new), because items in the formatter's heap don't * get individually deleted, and we may be making many changes * during active editing. */ input_continuation_ = new ((class CHtmlFormatter *)0) CHtmlDispTextInput( win, font, txt, len, txtofs); /* return the new item */ return input_continuation_; } private: /* * invalidate from a given text offset to the end of the line on * the display */ void inval_eol(class CHtmlSysWin *win, int textofs); /* * invalidate from below this item to the bottom of the window on * the display */ void inval_below(class CHtmlSysWin *win); /* * next item in this same input list - applies when the input line * is broken across multiple lines on the display */ CHtmlDispTextInput *input_continuation_; }; /* * Define a character-wrapping version of the input text display item */ DEF_CHARWRAP_SUBCLASS(CHtmlDispTextInput); /* ------------------------------------------------------------------------ */ /* * Display item for text in a debugger source file window. This behaves * very much like a preformatted text item, except that it records the * line number in the source file of the line (so that it can be related * back to information in the .GAM file's debug records), and displays * an informational icon in the margin showing the breakpoint and * execution status of the line. */ class CHtmlDispTextDebugsrc: public CHtmlDispTextPre { public: CHtmlDispTextDebugsrc(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs, unsigned long linenum); /* get the cursor type */ Html_csrtype_t get_cursor_type(class CHtmlSysWin *, class CHtmlFormatter *formatter, int x, int y, int more_mode) const; /* draw the line of text */ void draw(class CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, int clicked); /* find text given a mouse position */ unsigned long find_textofs_by_pos(class CHtmlSysWin *, CHtmlPoint) const; /* find the position of the given text */ CHtmlPoint get_text_pos(class CHtmlSysWin *win, unsigned long txtofs); /* get the debugger source file position for this item */ unsigned long get_debugsrcpos() const { return line_num_; } /* * Set the current line status. Invalidates the line's icon on the * screen so that it redraws with the new status. */ void set_dbg_status(class CHtmlSysWin *win, unsigned int newstat); /* get my text into a buffer */ void add_text_to_buf(CStringBuf *buf) const; void add_text_to_buf_clip(CStringBuf *buf, unsigned long startofs, unsigned long endofs) const; private: /* source file line number of this line of text */ unsigned long line_num_; /* debugger line status */ unsigned int dbgstat_; }; /* ------------------------------------------------------------------------ */ /* * Display item for text in a Text Grid banner window. This behaves very * much like a preformatted text item, except that it records the row * number in the text grid, and we can dynamically add to and overwrite the * text in the display item. */ class CHtmlDispTextGrid: public CHtmlDisp { public: CHtmlDispTextGrid(class CHtmlSysWin *win, class CHtmlSysFont *font, unsigned long ofs); ~CHtmlDispTextGrid(); /* draw the line of text */ void draw(class CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, int clicked); /* * Writes text into the line, starting at the given column position. * Returns true if we expand the row, which will affect text offsets in * subsequent rows, false if we merely overwrite text that was already * in the row. */ int write_text(class CHtmlSysWin *win, class CHtmlSysFont *font, int col, const textchar_t *txt, size_t len); /* change our font, recalculating our size */ void set_font(class CHtmlSysWin *win, class CHtmlSysFont *font); /* get my text offset */ virtual unsigned long get_text_ofs() const { return ofs_; } /* set my text offset */ void set_text_ofs(unsigned long ofs) { ofs_ = ofs; } /* get the number of characters in my text */ int get_text_columns() const { return chars_; } /* * check to see if we contain a text offset - add one to our range for * a newline at the end of the line */ virtual int contains_text_ofs(unsigned long ofs) const { return ofs >= ofs_ && ofs < ofs_ + bytes_ + 1; } /* determine if I'm past a given offset */ int is_past_text_ofs(unsigned long ofs) const { return (ofs_ > ofs); } /* determine if I overlap a given text range */ virtual int overlaps_text_range(unsigned long l, unsigned long r) const { return ofs_ <= r && ofs_ + bytes_ + 1 > l; } /* get my width */ int measure_width(class CHtmlSysWin *) { /* we keep our position up to date with our current text width */ return pos_.right - pos_.left; } /* get my text height */ virtual int get_text_height(class CHtmlSysWin *) { return ascent_ht_; } /* get my vertical measurements */ void get_vertical_space(class CHtmlSysWin *, int, int *above_base, int *below_base, int *total) { /* our total size is always up to date in our position */ *total = pos_.bottom - pos_.top; /* our ascent height is the amount above the baseline */ *above_base = ascent_ht_; /* the balance is below the baseline */ *below_base = *total - ascent_ht_; } /* get the character at the given text offset */ textchar_t get_char_at_ofs(unsigned long ofs) const { /* if it's outside of our offset range, it's not ours */ if (ofs < ofs_ || ofs > ofs_ + bytes_) return '\0'; /* if it's just past our last character, it's a newline */ if (ofs == ofs_ + bytes_) return '\n'; /* return the character from our buffer, if we have a buffer */ return buf_.get() != 0 ? buf_.get()[ofs - ofs_] : '\0'; } /* is the character at the given offset a word character? */ int is_word_char_at_ofs(oshtml_charset_id_t cs, unsigned long ofs) { /* if it's outside of our offset range, it's not ours */ if (ofs < ofs_ || ofs > ofs_ + bytes_) return FALSE; /* ask the OS helper to interpret the character */ size_t local_ofs = (size_t)(ofs - ofs_); const textchar_t *p = buf_.get() + local_ofs; size_t rem = bytes_ - local_ofs; return os_is_word_char(cs, p, rem); } /* increment an offset by one character */ unsigned long inc_text_ofs_char( oshtml_charset_id_t cs, unsigned long ofs) const { /* if it's outside of our offset range, it's not ours */ if (ofs < ofs_ || ofs > ofs_ + bytes_) return FALSE; /* ask the OS helper to interpret the character */ size_t local_ofs = (size_t)(ofs - ofs_); const textchar_t *p = buf_.get() + local_ofs; size_t rem = bytes_ - local_ofs; return ofs + (os_next_char(cs, p, rem) - p); } /* decrement an offset by one character */ unsigned long dec_text_ofs_char( oshtml_charset_id_t cs, unsigned long ofs) const { /* if it's outside of our offset range, it's not ours */ if (ofs < ofs_ || ofs > ofs_ + bytes_) return FALSE; /* ask the OS helper to interpret the character */ size_t local_ofs = (size_t)(ofs - ofs_); const textchar_t *p = buf_.get() + local_ofs; size_t rem = bytes_ - local_ofs; return ofs + (os_prev_char(cs, p, buf_.get()) - p); } /* use an I-beam cursor over our text */ virtual Html_csrtype_t get_cursor_type(class CHtmlSysWin *, class CHtmlFormatter *, int /*x*/, int /*y*/, int /*more_mode*/) const { return HTML_CSRTYPE_IBEAM; } /* invalidate a text range */ virtual void inval_range(class CHtmlSysWin *win, unsigned long start_ofs, unsigned long end_ofs); /* get the position of a character within the item */ virtual CHtmlPoint get_text_pos(class CHtmlSysWin *win, unsigned long txtofs); /* get the text offset of a character given a position */ virtual unsigned long find_textofs_by_pos(class CHtmlSysWin *win, CHtmlPoint pt) const; /* append my text to a string buffer */ virtual void add_text_to_buf(CStringBuf *buf) const { /* if I have anything to append, add it */ if (buf_.get() != 0) { /* add my text */ buf->append(buf_.get()); /* append a newline */ buf->append("\n"); } } /* get my text, clipping to the given offsets */ virtual void add_text_to_buf_clip(CStringBuf *buf, unsigned long startofs, unsigned long endofs) const { /* start with our entire range */ size_t ofs = 0; size_t len = bytes_; /* ignore if it's entirely outside our range, or we have no data */ if (startofs >= ofs_ + bytes_ + 1 || endofs < ofs_ || buf_.get() == 0) return; /* limit on the left if the starting offset is after our start */ if (startofs > ofs_) { /* adjust our offset and length to skip the leading part */ ofs += startofs - ofs_; len -= startofs - ofs_; } /* limit on the right if the ending offset is before our end */ if (endofs < ofs_ + bytes_) len -= ofs_ + bytes_ - endofs; /* add a newline if they want the charater just past our end */ int add_nl = (endofs >= ofs_ + bytes_); /* append the data */ buf->append(buf_.get() + ofs, len); /* append the newline if desired */ if (add_nl) buf->append("\n"); } private: /* draw a portion of our text in the given selection range */ void draw_part(CHtmlSysWin *win, int hilite, unsigned long l, unsigned long r); /* our text buffer */ CStringBuf buf_; /* array of character colors - one per character cell */ struct textgrid_cellcolor_t *colors_; size_t colors_max_; /* our font */ class CHtmlSysFont *font_; /* ascender height of my font */ unsigned short ascent_ht_; /* * The number of characters we store, and the number of bytes. These * might differ if we're using a multi-byte character set (such as * UTF-8). Note that text offsets are *byte* oriented. */ int chars_; int bytes_; /* my starting text offset */ unsigned long ofs_; }; /* ------------------------------------------------------------------------ */ /* * Base class for special text types. These are used for text-like display * elements that display something other than ordinary text, such as * non-breaking spaces, soft hyphens, and specific-width spaces. */ class CHtmlDispTextSpecial: public CHtmlDispText { public: /* refer the constructor to the base text constructor */ CHtmlDispTextSpecial(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs) : CHtmlDispText(win, font, txt, len, txtofs) { } /* * Get my width. Assume that we've calculated our width in advance, so * that we can simply use our position any time we're asked. */ int measure_width(class CHtmlSysWin *) { return pos_.right - pos_.left; } /* invalidate a range */ void inval_range(class CHtmlSysWin *win, unsigned long start_ofs, unsigned long end_ofs); /* get the position of the given text */ CHtmlPoint get_text_pos(CHtmlSysWin *win, unsigned long txtofs) { /* if the offset is equal to my offset, it's my left edge */ if (txtofs == txtofs_) return CHtmlPoint(pos_.left, pos_.top); /* * if my length is exactly one, and they want the position of the * next character, use my full width */ if (len_ == 1) return CHtmlPoint(pos_.right, pos_.top); /* in other cases, use the inherited code */ return CHtmlDispText::get_text_pos(win, txtofs); } /* get the text offset of a character given a position */ unsigned long find_textofs_by_pos(class CHtmlSysWin *win, CHtmlPoint pt) const { /* if we have no text, just return out text offset */ if (len_ == 0) return txtofs_; /* * if we're exactly one character long, and the position is within * my area, base the calculation on our width alone */ if (len_ == 1 && pos_.contains(pt)) { /* * if it's in my left half, return my text offset; otherwise, * return the next text offset */ return txtofs_ + (pt.x - pos_.left > (pos_.right - pos_.left)/2 ? 1 : 0); } /* in other cases, use the inherited handling */ return CHtmlDispText::find_textofs_by_pos(win, pt); } protected: }; /* * Soft Hyphen display item. */ class CHtmlDispSoftHyphen: public CHtmlDispTextSpecial { public: CHtmlDispSoftHyphen(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs) : CHtmlDispTextSpecial(win, font, txt, len, txtofs) { /* we're not showing as a hyphen yet */ vis_ = FALSE; } /* find a line break */ CHtmlDisp *find_line_break(class CHtmlFormatter *formatter, class CHtmlSysWin *win, long space_avail, class CHtmlLineBreak *break_pos); /* do a line break */ CHtmlDisp *do_line_break(class CHtmlFormatter *formatter, class CHtmlSysWin *win, class CHtmlLineBreak *break_pos); /* do a line break from a previous item, skipping whitespace */ CHtmlDisp *do_line_break_skipsp(class CHtmlFormatter *formatter, class CHtmlSysWin *win) { /* we don't have any whitespace, so just do our normal line break */ return do_line_break(formatter, win, 0); } /* draw our text */ void draw_text(class CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, class CHtmlDispLink *link, int clicked, const CHtmlRect *pos); protected: /* * flag: we're showing as an actual visible hyphen, because we've * taken the option of breaking the line at this point */ int vis_; }; /* * Linked version of soft hyphen - this is for soft hyphens that occur * inside hyperlinked text. */ class CHtmlDispSoftHyphenLink: public CHtmlDispSoftHyphen { public: CHtmlDispSoftHyphenLink(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs, CHtmlDispLink *link) : CHtmlDispSoftHyphen(win, font, txt, len, txtofs) { /* initialize the linked text item */ linked_text_init(win, link); } /* define the linked text methods */ LINKED_TEXT_METHODS }; /* ------------------------------------------------------------------------ */ /* * Non-breaking space display item. */ class CHtmlDispNBSP: public CHtmlDispTextSpecial { public: CHtmlDispNBSP(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs) : CHtmlDispTextSpecial(win, font, txt, len, txtofs) { } /* find a line break */ CHtmlDisp *find_line_break(class CHtmlFormatter *formatter, class CHtmlSysWin *win, long space_avail, class CHtmlLineBreak *break_pos); /* measure for table inclusion */ void measure_for_table(class CHtmlSysWin *win, class CHtmlTableMetrics *metrics); /* find trailing whitespace */ void find_trailing_whitespace(CHtmlDisp_wsinfo *info); protected: }; /* * Hyperlinked version of non-breaking space */ class CHtmlDispNBSPLink: public CHtmlDispNBSP { public: CHtmlDispNBSPLink(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs, CHtmlDispLink *link) : CHtmlDispNBSP(win, font, txt, len, txtofs) { /* initialize the linked text item */ linked_text_init(win, link); } /* define the linked text methods */ LINKED_TEXT_METHODS }; /* ------------------------------------------------------------------------ */ /* * Special explicit-width space display item. This type of item is used * to display the various typographical Unicode spacing characters. These * are all breakable spaces for line-wrapping purposes. */ class CHtmlDispSpace: public CHtmlDispTextSpecial { public: CHtmlDispSpace(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs, int wid, int pre); /* find a line break */ CHtmlDisp *find_line_break(class CHtmlFormatter *formatter, class CHtmlSysWin *win, long space_avail, class CHtmlLineBreak *break_pos); /* do a line break */ CHtmlDisp *do_line_break(class CHtmlFormatter *formatter, class CHtmlSysWin *win, class CHtmlLineBreak *break_pos); /* do a line break from a previous item, skipping whitespace */ CHtmlDisp *do_line_break_skipsp(class CHtmlFormatter *formatter, class CHtmlSysWin *win) { /* do our normal line breaking, which will skip to the next item */ return do_line_break(formatter, win, 0); } /* draw our text */ void draw_text(class CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, class CHtmlDispLink *link, int clicked, const CHtmlRect *pos); /* find trailing whitespace */ void find_trailing_whitespace(CHtmlDisp_wsinfo *info); /* remove/hide trailing whitespace */ void remove_trailing_whitespace(CHtmlSysWin *win, void *pos); void hide_trailing_whitespace(class CHtmlSysWin *win, void *pos); /* measure for table inclusion */ void measure_for_table(class CHtmlSysWin *win, class CHtmlTableMetrics *metrics) { /* * use the base display item handling, not the text handling - we * don't have the usual text contents */ CHtmlDisp::measure_for_table(win, metrics); } protected: /* is the space in preformatted text? */ int pre_; }; /* * Hyperlinked version of explicit-width space display item. */ class CHtmlDispSpaceLink: public CHtmlDispSpace { public: CHtmlDispSpaceLink(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t len, unsigned long txtofs, int wid, int pre, CHtmlDispLink *link) : CHtmlDispSpace(win, font, txt, len, txtofs, wid, pre) { /* initialize the linked text item */ linked_text_init(win, link); } /* define the linked text methods */ LINKED_TEXT_METHODS }; /* ------------------------------------------------------------------------ */ /* * Tab display object. This object's purpose is to provide horizontal * spacing to align material at a tab stop. */ class CHtmlDispTab: public CHtmlDisp { public: CHtmlDispTab(const CHtmlPoint *pos) { init(pos, 0); } CHtmlDispTab(const CHtmlPoint *pos, long width) { init(pos, width); } /* my width is fixed */ int measure_width(class CHtmlSysWin *) { return (int)width_; } /* nothing to draw */ void draw(class CHtmlSysWin *, unsigned long /*sel_start*/, unsigned long /*sel_end*/, int /*clicked*/) { } /* no vertical space */ void get_vertical_space(class CHtmlSysWin *, int, int *above_base, int *below_base, int *total) { *above_base = 0; *below_base = 0; *total = 0; } /* * Set my width. The formatter cannot always set a tab's width * immediately, but must sometimes wait until the next tab stop to * determine the size of the tab. This routine lets the formatter * set the size of the tab when it becomes known. */ void set_width(long width) { width_ = width; } /* this item is effectively text, so allow inexact hits */ virtual int allow_inexact_hit() const { return TRUE; } /* treat my text contents as a tab character */ void add_text_to_buf(CStringBuf *buf) const { buf->append("\t", 1); } void add_text_to_buf_clip(CStringBuf *buf, unsigned long, unsigned long) const { buf->append("\t", 1); } private: void init(const CHtmlPoint *pos, long width) { width_ = width; pos_.left = pos_.right = pos->x; pos_.top = pos_.bottom = pos->y; } /* horizontal spacing */ long width_; }; /* ------------------------------------------------------------------------ */ /* * Line break display object. This object's only purpose is to provide * a marker for a line break in the display list. */ class CHtmlDispBreak: public CHtmlDisp { public: CHtmlDispBreak(int vertical_space) { vertical_space_ = vertical_space; } /* I have no width */ int measure_width(class CHtmlSysWin *) { return 0; } /* nothing to draw */ void draw(class CHtmlSysWin *, unsigned long /*sel_start*/, unsigned long /*sel_end*/, int /*clicked*/) { } void get_vertical_space(class CHtmlSysWin *, int, int *above_base, int *below_base, int *total) { *above_base = 0; *below_base = 0; *total = vertical_space_; } /* measure for inclusion in a table cell */ void measure_for_table(class CHtmlSysWin *win, class CHtmlTableMetrics *metrics); /* treat my text contents as a newline */ void add_text_to_buf(CStringBuf *buf) const { buf->append("\n", 1); } void add_text_to_buf_clip(CStringBuf *buf, unsigned long, unsigned long) const { buf->append("\n", 1); } private: int vertical_space_; }; /* ------------------------------------------------------------------------ */ /* * Horizontal rule object */ class CHtmlDispHR: public CHtmlDisp, public CHtmlDispDisplaySite { public: CHtmlDispHR(class CHtmlSysWin *win, int shade, long height, long width, int width_is_pct, long margin_left, long margin_right, class CHtmlResCacheObject *image); ~CHtmlDispHR(); /* unset our window reference - process via the display site */ virtual void unset_win() { CHtmlDispDisplaySite::unset_win(); } /* cancel playback - process via the display site */ virtual void cancel_playback() { CHtmlDispDisplaySite::cancel_playback(); } /* measure my width */ int measure_width(class CHtmlSysWin *) { return width_; } /* draw it */ void draw(class CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, int clicked); /* get my overall vertical space needs */ void get_vertical_space(class CHtmlSysWin *, int, int *above_base, int *below_base, int *total) { *above_base = 0; *below_base = 0; *total = height_; } /* set my position */ void set_line_pos(class CHtmlSysWin *, int left, int top, int /*total_height*/, int text_base_offset, int /*max_text_height*/) { pos_.offset(left - pos_.left, top + text_base_offset - pos_.top); } /* measure for inclusion in a table cell */ void measure_for_table(class CHtmlSysWin *win, class CHtmlTableMetrics *metrics); /* find trailing whitespace */ void find_trailing_whitespace(class CHtmlDisp_wsinfo *info); /* treat my text contents as a newline */ void add_text_to_buf(CStringBuf *buf) const { buf->append("\n", 1); } void add_text_to_buf_clip(CStringBuf *buf, unsigned long startofs, unsigned long endofs) const { buf->append("\n", 1); } private: /* width in pixels */ long width_; /* * Flag indicating whether the width is absolute or relative. The * distinction is only important when measuring a table; when a * relative rule is in a table, it adds no space during the table * measurement phase, because the rule will be resized according to * the table's final size. */ int width_is_pct_ : 1; /* vertical size */ long height_; /* shading */ int shade_ : 1; /* the image, if we have one */ class CHtmlResCacheObject *image_; }; /* ------------------------------------------------------------------------ */ /* * List item display items. One of these items is displayed in the left * margin of each item of a list. */ class CHtmlDispListitem: public CHtmlDisp { public: CHtmlDispListitem(class CHtmlSysWin *win, class CHtmlSysFont *font, long width); /* get my width - use the fixed width set at creation time */ int measure_width(class CHtmlSysWin *) { return (int)(pos_.right - pos_.left); } /* get my overall vertical space needs */ void get_vertical_space(class CHtmlSysWin *win, int text_height, int *above_base, int *below_base, int *total); /* set my position */ void set_line_pos(class CHtmlSysWin *win, int left, int top, int total_height, int text_base_offset, int max_text_height); /* treat my text contents as a newline */ void add_text_to_buf(CStringBuf *buf) const { buf->append("\n", 1); } void add_text_to_buf_clip(CStringBuf *buf, unsigned long, unsigned long) const { buf->append("\n", 1); } protected: /* ascender height of my font */ unsigned short ascent_ht_; /* my font */ CHtmlSysFont *font_; }; /* * Numbered list item header */ class CHtmlDispListitemNumber: public CHtmlDispListitem { public: CHtmlDispListitemNumber(class CHtmlSysWin *win, class CHtmlSysFont *font, long width, textchar_t style, long value); void draw(class CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, int clicked); private: CStringBuf num_; }; /* * Bulleted list item header */ class CHtmlDispListitemBullet: public CHtmlDispListitem, public CHtmlDispDisplaySite { public: CHtmlDispListitemBullet(class CHtmlSysWin *win, class CHtmlSysFont *font, long width, HTML_Attrib_id_t type, class CHtmlResCacheObject *image); ~CHtmlDispListitemBullet(); /* unset our window reference - process via the display site */ virtual void unset_win() { CHtmlDispDisplaySite::unset_win(); } /* cancel playback - process via the display site */ virtual void cancel_playback() { CHtmlDispDisplaySite::cancel_playback(); } void get_vertical_space(class CHtmlSysWin *win, int text_height, int *above_base, int *below_base, int *total); void set_line_pos(class CHtmlSysWin *win, int left, int top, int total_height, int text_base_offset, int max_text_height); void draw(class CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, int clicked); private: /* bullet type (disc, square, circle) */ enum HTML_SysWin_Bullet_t type_; /* image, if we're displaying an image as our bullet */ class CHtmlResCacheObject *image_; }; /* ------------------------------------------------------------------------ */ /* * Image display object */ class CHtmlDispImg: public CHtmlDisp, public CHtmlDispDisplaySite { public: CHtmlDispImg(class CHtmlSysWin *win, class CHtmlResCacheObject *image, CStringBuf *alt, HTML_Attrib_id_t align, long hspace, long vspace, long width, int width_set, long height, int height_set, long border, CHtmlUrl *usemap, int ismap); ~CHtmlDispImg(); /* unset our window reference - process via the display site */ virtual void unset_win() { CHtmlDispDisplaySite::unset_win(); } /* cancel playback - process via the display site */ virtual void cancel_playback() { CHtmlDispDisplaySite::cancel_playback(); } /* get my width */ int measure_width(class CHtmlSysWin *win); /* get my height */ int measure_height() { return height_ + 2*vspace_ + 2*border_; } /* measure for inclusion in a table cell */ void measure_for_table(class CHtmlSysWin *win, class CHtmlTableMetrics *metrics); /* draw the image */ void draw(class CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, int clicked); /* find a line break */ CHtmlDisp *find_line_break(class CHtmlFormatter *formatter, class CHtmlSysWin *win, long space_avail, class CHtmlLineBreak *break_pos); /* * find trailing whitespace - an image counts as non-whitespace * text, so we'll just forget about any preceding whitespace (it's * not trailing, since the image follows) */ void find_trailing_whitespace(class CHtmlDisp_wsinfo *info); /* get my overall vertical space needs */ void get_vertical_space(class CHtmlSysWin *win, int text_height, int *above_base, int *below_base, int *total); /* set my position */ void set_line_pos(class CHtmlSysWin *win, int left, int top, int total_height, int text_base_offset, int max_text_height); /* determine what type of cursor to use over the image */ Html_csrtype_t get_cursor_type(class CHtmlSysWin *, class CHtmlFormatter *, int, int, int) const; /* get my link object */ CHtmlDispLink *get_link(class CHtmlFormatter *, int, int) const; /* * An unliked image won't change appearance on click changes, so don't * invalidate. (Images can take up large areas of the screen, so it's * worth overriding this to avoid this default invalidation that would * otherwise occur.) */ void on_click_change(class CHtmlSysWin *) { } /* treat my text contents as a space */ void add_text_to_buf(CStringBuf *buf) const { buf->append(" ", 1); } void add_text_to_buf_clip(CStringBuf *buf, unsigned long, unsigned long) const { buf->append(" ", 1); } /* * invalidate if I'm a link - if I have a map, I have a link, so * invalidate me, otherwise do nothing */ void inval_link(class CHtmlSysWin *win) { if (usemap_.get_url() != 0 && usemap_.get_url()[0] != '\0') inval(win); } /* get my ALT text */ virtual const textchar_t *get_alt_text() const { return alt_.get(); } /* format deferred (i.e., as a floating item) */ virtual void format_deferred(class CHtmlSysWin *win, class CHtmlFormatter *formatter, long line_spacing); /* * Set the hovering/active images. For regular image tags, this has no * effect; only hyperlinked images can have these extra images. As a * diagnostic aid, we'll log an error to the debug log mentioning that * the extra subimages aren't allowed in ordinary images. */ virtual void set_extra_images(class CHtmlResCacheObject *a_image, class CHtmlResCacheObject *h_image); protected: /* * If we have an image map, find the map zone containing a given * point. If we don't have an image map, or the image map doesn't * have a zone containing the given point, returns null. */ class CHtmlFmtMapZone *get_map_zone(CHtmlFormatter *formatter, int x, int y) const; /* draw using the given image object */ void draw_image(class CHtmlSysWin *win, class CHtmlResCacheObject *image); /* forget one of our internal images */ void forget_image(class CHtmlResCacheObject **image); protected: /* alignment type */ HTML_Attrib_id_t align_; /* alternate textual representation */ CStringBuf alt_; /* horizontal/vertical whitespace around edges */ long hspace_; long vspace_; /* height and width */ long height_; long width_; /* border size */ long border_; /* map setting */ CHtmlUrl usemap_; /* is-map flag */ int ismap_ : 1; /* the image */ class CHtmlResCacheObject *image_; }; /* ------------------------------------------------------------------------ */ /* * Linkable image display item */ class CHtmlDispImgLink: public CHtmlDispImg { public: CHtmlDispImgLink(class CHtmlSysWin *win, class CHtmlResCacheObject *image, CStringBuf *alt, HTML_Attrib_id_t align, long hspace, long vspace, long width, int width_set, long height, int height_set, long border, CHtmlUrl *usemap, int ismap, CHtmlDispLink *link) : CHtmlDispImg(win, image, alt, align, hspace, vspace, width, width_set, height, height_set, border, usemap, ismap) { /* remember our link */ link_ = link; /* add this item to the link */ link->add_link_item(this); /* presume we won't have any special images */ a_image_ = h_image_ = 0; } ~CHtmlDispImgLink(); /* set our active/hover images */ virtual void set_extra_images(class CHtmlResCacheObject *a_image, class CHtmlResCacheObject *h_image); /* use the hand cursor when over a link */ Html_csrtype_t get_cursor_type(class CHtmlSysWin *, class CHtmlFormatter *, int, int, int) const; /* invalidate if I'm a link - I am, so invalidate me */ void inval_link(class CHtmlSysWin *win) { inval(win); } /* get my link object */ CHtmlDispLink *get_link(class CHtmlFormatter *, int, int) const; /* get my containing link object */ class CHtmlDispLink *get_container_link() const { return link_; } /* draw as a link */ void draw(class CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, int /*clicked*/); /* * On a click state change, we might change appearance if we have a * separate "hover" or "active" image - if so, invalidate our display * area on a click change. */ void on_click_change(class CHtmlSysWin *win) { /* * if we have an alternate image, invalidate our area so that we'll * display the new image as needed */ if (h_image_ != 0 || a_image_ != 0) inval(win); } /* get my ALT text - get the TITLE for the link, if it has one */ virtual const textchar_t *get_alt_text() const { /* if I have a link, and it has a TITLE, return the title */ if (link_ != 0 && link_->title_.get() != 0) return link_->title_.get(); /* return the inherited ALT text from the image */ return get_alt_text(); } private: /* our containing link object */ CHtmlDispLink *link_; /* the image to display when hovering */ class CHtmlResCacheObject *h_image_; /* the image to display when clicked ("active") */ class CHtmlResCacheObject *a_image_; }; /* ------------------------------------------------------------------------ */ /* * Tables */ class CHtmlDispTable: public CHtmlDisp, public CHtmlDispDisplaySite { public: CHtmlDispTable(class CHtmlSysWin *win, long border, HTML_Attrib_id_t align, int floating, long width, int width_pct, int width_set, long win_width, HTML_color_t bgcolor, int use_bgcolor, class CHtmlResCacheObject *bg_image); /* change my window width */ void set_win_width(long win_width) { win_width_ = win_width; } /* measure for inclusion in a table cell */ void measure_for_table(class CHtmlSysWin *win, class CHtmlTableMetrics *metrics); /* measure for inclusion in a banner */ void measure_for_banner(class CHtmlSysWin *win, class CHtmlTableMetrics *metrics); /* draw the table */ void draw(class CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, int clicked); /* measure the width of the table */ int measure_width(class CHtmlSysWin *win); /* * table display items are in the background - let mouse clicks go * to the table's contents instead */ virtual int is_in_background() const { return TRUE; } /* get the vertical space needed by the table */ void get_vertical_space(class CHtmlSysWin *win, int text_height, int *above_base, int *below_base, int *total); /* find trailing whitespace */ void find_trailing_whitespace(class CHtmlDisp_wsinfo *info); /* * Set the size limits of the table. This doesn't set the final * size of the table, but rather sets the limits: the minimum size * that the table can have with as much line wrapping as possible; * and the maximum size that the table needs in order to display * everything without any line wrapping at all. These limits are * needed later, so that they can be compared to the available space * for the table to determine how the space is apportioned; here, we * just calculate and remember the limits. */ void set_table_size_limits(long min_width, long max_width) { width_min_ = min_width; width_max_ = max_width; } /* * Calculate the final size of the table. Here we compute the * actual size of the table, based on the available screen space and * the limits that we calculated earlier in * calc_table_size_limits(). Note that calc_table_size_limits() * must be called before this routine. */ void calc_table_size(class CHtmlFormatter *formatter, class CHtmlTagTABLE *tag); /* * Set the table's height. Our tag will call this after our final * height has been determined in pass 2. */ void set_table_height(long height) { pos_.bottom = pos_.top + height; } /* treat my text contents as a newline */ void add_text_to_buf(CStringBuf *buf) const { buf->append("\n", 1); } void add_text_to_buf_clip(CStringBuf *buf, unsigned long, unsigned long) const { buf->append("\n", 1); } /* * Set our sublist - this gives us our list of contained display items * for use in formatting floating tables. */ void set_contents_sublist(CHtmlDisp *head); /* format deferred (as a floating item) */ virtual void format_deferred(class CHtmlSysWin *win, class CHtmlFormatter *formatter, long line_spacing); /* forward unset_win() on to the display site */ void unset_win() { CHtmlDispDisplaySite::unset_win(); } /* forward cancel_playback() on to the display site */ void cancel_playback() { CHtmlDispDisplaySite::cancel_playback(); } private: /* border size setting */ long border_; /* alignment setting */ HTML_Attrib_id_t align_; /* * width of the table, in pixels, as set in the TABLE tag's * attributes */ long width_; /* floating? */ int floating_ : 1; /* flag indicating whether the table's WIDTH is a percentage */ int width_pct_ : 1; /* flag indicating whether the TABLE tag has a WIDTH setting */ int width_set_ : 1; /* width of available space in the window */ long win_width_; /* * Width limits. The minimum width is the smallset space we need to * display the table's contents without clipping anything, but with * maximum line wrapping. The maximum width is the smallest space * we need to display the table's contents without any line * wrapping. */ long width_min_; long width_max_; /* background color, if set */ HTML_color_t bgcolor_; int use_bgcolor_; /* our contents sublist */ CHtmlDisp *sub_head_; }; /* * Table cell display item */ class CHtmlDispTableCell: public CHtmlDisp, public CHtmlDispDisplaySite { public: CHtmlDispTableCell(class CHtmlSysWin *win, int border, HTML_color_t bgcolor, int use_bgcolor, class CHtmlResCacheObject *bg_image, long width_attr, int width_pct, int width_set); /* draw the cell */ void draw(class CHtmlSysWin *win, unsigned long sel_start, unsigned long sel_end, int clicked); /* measure the width of the cell */ int measure_width(class CHtmlSysWin *win); /* measure our contribution to the containing banner's size */ void measure_for_banner(class CHtmlSysWin *win, class CHtmlTableMetrics *metrics); /* * Measure the metrics of this item for use in a table cell. Since * this *is* a table cell, this routine is called only when we're in a * nested table - we're being measured for use in the enclosing * table's cell that contains our entire table. */ virtual void measure_for_table(class CHtmlSysWin *win, class CHtmlTableMetrics *metrics); /* get the vertical space needed by the cell */ void get_vertical_space(class CHtmlSysWin *win, int text_height, int *above_base, int *below_base, int *total); /* * cell display items are in the background - let mouse clicks go to * the table's contents instead */ virtual int is_in_background() const { return TRUE; } /* find trailing whitespace */ void find_trailing_whitespace(class CHtmlDisp_wsinfo *info); /* * Set the height of the cell. Our tag will call this after pass 2 * is completed and the final height of the cell's row or rows is * known. */ void set_cell_height(long height) { pos_.bottom = pos_.top + height; } /* * Set the width of the cell. Our tag will call this during pass 2, * when the final width of the cell's column or columns is known. */ void set_cell_width(long width) { pos_.right = pos_.left + width; } /* treat my text contents as a tab */ void add_text_to_buf(CStringBuf *buf) const { buf->append("\t", 1); } void add_text_to_buf_clip(CStringBuf *buf, unsigned long, unsigned long) const { buf->append("\t", 1); } /* forward unset_win() on to the display site */ void unset_win() { CHtmlDispDisplaySite::unset_win(); } /* forward cancel_playback() on to the display site */ void cancel_playback() { CHtmlDispDisplaySite::cancel_playback(); } private: /* * Border setting - true means that the cell has a border, false * means that the cell is drawn without a border. This should be * set to true if the table has a non-zero BORDER attribute, false * if the table has no BORDER attribute or the BORDER attribute has * a value of zero. */ int border_ : 1; /* background color, if set */ HTML_color_t bgcolor_; int use_bgcolor_; /* * WIDTH attribute value, and flags indicating whether the WIDTH * attribute is present and whether it is based on a percentage */ long width_attr_; int width_set_ : 1; int width_pct_ : 1; }; #endif /* HTMLDISP_H */ qtads-2.1.7/htmltads/htmlfmt.cpp000066400000000000000000007266711265017072300166620ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/html/htmlfmt.cpp,v 1.4 1999/07/11 00:46:40 MJRoberts Exp $"; #endif /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlfmt.cpp - HTML formatter Function Notes Modified 09/07/97 MJRoberts - Creation */ #include #include #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef HTMLFMT_H #include "htmlfmt.h" #endif #ifndef HTMLPRS_H #include "htmlprs.h" #endif #ifndef HTMLTAGS_H #include "htmltags.h" #endif #ifndef HTMLSYS_H #include "htmlsys.h" #endif #ifndef HTMLDISP_H #include "htmldisp.h" #endif #ifndef HTMLTXAR_H #include "htmltxar.h" #endif #ifndef HTMLRC_H #include "htmlrc.h" #endif #ifndef HTMLHASH_H #include "htmlhash.h" #endif #ifndef HTML_OS_H #include "html_os.h" #endif #ifndef HTMLRF_H #include "htmlrf.h" #endif #ifndef HTMLSND_H #include "htmlsnd.h" #endif /* ------------------------------------------------------------------------ */ /* * Display factory for unlinkable objects */ class CHtmlFmtDispFactoryNormal: public CHtmlFmtDispFactory { public: CHtmlFmtDispFactoryNormal(CHtmlFormatter *formatter) : CHtmlFmtDispFactory(formatter) { } virtual CHtmlDisp *new_disp_text(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) { return new (formatter_) CHtmlDispText(win, font, txt, txtlen, txtofs); } virtual CHtmlDispTextInput *new_disp_text_input( class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) { return new ((CHtmlFormatter *)0) CHtmlDispTextInput( win, font, txt, txtlen, txtofs); } virtual class CHtmlDisp *new_disp_soft_hyphen( class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) { return new (formatter_) CHtmlDispSoftHyphen(win, font, txt, txtlen, txtofs); } virtual class CHtmlDisp *new_disp_nbsp( class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) { return new (formatter_) CHtmlDispNBSP(win, font, txt, txtlen, txtofs); } virtual class CHtmlDisp *new_disp_space( class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs, int wid, int pre) { return new (formatter_) CHtmlDispSpace(win, font, txt, txtlen, txtofs, wid, pre); } virtual CHtmlDisp *new_disp_text_pre(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) { return new (formatter_) CHtmlDispTextPre(win, font, txt, txtlen, txtofs); } virtual CHtmlDisp *new_disp_text_debug(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs, unsigned long linenum) { return new (formatter_) CHtmlDispTextDebugsrc(win, font, txt, txtlen, txtofs, linenum); } virtual CHtmlDispImg *new_disp_img(class CHtmlSysWin *win, class CHtmlResCacheObject *image, CStringBuf *alt, HTML_Attrib_id_t align, long hspace, long vspace, long width, int width_set, long height, int height_set, long border, CHtmlUrl *usemap, int ismap) { return new (formatter_) CHtmlDispImg(win, image, alt, align, hspace, vspace, width, width_set, height, height_set, border, usemap, ismap); } }; /* * Display factory for unlinkable objects in character-wrap mode */ class CHtmlFmtDispFactoryCharWrap: public CHtmlFmtDispFactoryNormal { public: CHtmlFmtDispFactoryCharWrap(class CHtmlFormatter *fmt) : CHtmlFmtDispFactoryNormal(fmt) { } /* create character-wrapping text items */ virtual CHtmlDisp *new_disp_text(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) { return new (formatter_) CHtmlDispTextCharWrap(win, font, txt, txtlen, txtofs); } virtual CHtmlDispTextInput *new_disp_text_input( class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) { return new ((CHtmlFormatter *)0) CHtmlDispTextInputCharWrap( win, font, txt, txtlen, txtofs); } }; /* * Display factor for linkable objects */ class CHtmlFmtDispFactoryLink: public CHtmlFmtDispFactory { public: CHtmlFmtDispFactoryLink(CHtmlFormatter *formatter) : CHtmlFmtDispFactory(formatter) { } virtual CHtmlDisp *new_disp_text(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) { /* create the text item */ return new (formatter_) CHtmlDispTextLink(win, font, txt, txtlen, txtofs, formatter_->get_cur_link()); } virtual class CHtmlDisp *new_disp_soft_hyphen( class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) { return new (formatter_) CHtmlDispSoftHyphenLink(win, font, txt, txtlen, txtofs, formatter_->get_cur_link()); } virtual class CHtmlDisp *new_disp_nbsp( class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) { return new (formatter_) CHtmlDispNBSPLink(win, font, txt, txtlen, txtofs, formatter_->get_cur_link()); } virtual class CHtmlDisp *new_disp_space( class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs, int wid, int pre) { return new (formatter_) CHtmlDispSpaceLink(win, font, txt, txtlen, txtofs, wid, pre, formatter_->get_cur_link()); } virtual CHtmlDispTextInput *new_disp_text_input( class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) { /* input text can't be meaningfully linked; use a regular text item */ return new ((CHtmlFormatter *)0) CHtmlDispTextInput( win, font, txt, txtlen, txtofs); } virtual CHtmlDisp *new_disp_text_pre(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) { /* create the text item */ return new (formatter_) CHtmlDispTextPreLink(win, font, txt, txtlen, txtofs, formatter_->get_cur_link()); } virtual CHtmlDisp *new_disp_text_debug(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs, unsigned long linenum) { /* create the text item */ return new (formatter_) CHtmlDispTextDebugsrc(win, font, txt, txtlen, txtofs, linenum); } virtual CHtmlDispImg *new_disp_img(class CHtmlSysWin *win, class CHtmlResCacheObject *image, CStringBuf *alt, HTML_Attrib_id_t align, long hspace, long vspace, long width, int width_set, long height, int height_set, long border, CHtmlUrl *usemap, int ismap) { /* create and return the image item */ return new (formatter_) CHtmlDispImgLink(win, image, alt, align, hspace, vspace, width, width_set, height, height_set, border, usemap, ismap, formatter_->get_cur_link()); } }; /* * Display factory for linked objects in character-wrap mode */ class CHtmlFmtDispFactoryLinkCharWrap: public CHtmlFmtDispFactoryLink { public: CHtmlFmtDispFactoryLinkCharWrap(class CHtmlFormatter *fmt) : CHtmlFmtDispFactoryLink(fmt) { } /* create character-wrapping text items */ virtual CHtmlDisp *new_disp_text(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) { return new (formatter_) CHtmlDispTextLinkCharWrap(win, font, txt, txtlen, txtofs, formatter_->get_cur_link()); } virtual CHtmlDispTextInput *new_disp_text_input( class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) { /* input text can't be meaningfully linked; use a regular text item */ return new ((CHtmlFormatter *)0) CHtmlDispTextInputCharWrap( win, font, txt, txtlen, txtofs); } }; /* ------------------------------------------------------------------------ */ /* * formatter implementation */ /* * statics */ CHtmlResCache *CHtmlFormatter::res_cache_ = 0; CHtmlResFinder *CHtmlFormatter::res_finder_ = 0; int CHtmlFormatter::formatter_cnt_ = 0; CHtmlSoundQueue *CHtmlFormatter::sound_queues_[4] = { 0, 0, 0, 0 }; /* * construction */ CHtmlFormatter::CHtmlFormatter(CHtmlParser *parser) { /* count the new formatter */ ++formatter_cnt_; /* if this is the first formatter, initialize the static singletons */ if (formatter_cnt_ == 1) { /* create the global resource cache */ res_cache_ = new CHtmlResCache(); /* create the global resource finder */ res_finder_ = new CHtmlResFinder(); } /* save parser setting */ parser_ = parser; /* window is as yet unknown */ win_ = 0; /* no font yet */ curfont_ = 0; /* no lines stored yet */ line_count_ = 0; line_id_ = 0; /* allocate the line starts table */ line_starts_ = new CHtmlLineStarts; /* there's no display list yet */ body_disp_ = 0; disp_head_ = disp_tail_ = 0; /* there's no deferred floater list yet */ defer_head_ = defer_tail_ = 0; /* no selection yet */ sel_start_ = sel_end_ = 0; /* create the tab stop hash table */ tab_table_ = new CHtmlHashTable(32, new CHtmlHashFuncCI); /* create the image map hash table */ map_table_ = new CHtmlHashTable(128, new CHtmlHashFuncCI); cur_map_ = 0; /* use fairly large heap allocation units for our heap allocator */ heap_alloc_unit_ = 16*1024; /* no heap pages yet */ heap_pages_alloced_ = 0; heap_pages_max_ = 0; heap_pages_ = 0; heap_page_cur_ = 0; heap_page_cur_ofs_ = 0; /* not in a link yet */ cur_link_ = 0; /* not in a DIV yet */ cur_div_ = 0; /* set up the display item factories */ disp_factory_normal_ = new CHtmlFmtDispFactoryNormal(this); disp_factory_charwrap_ = new CHtmlFmtDispFactoryCharWrap(this); disp_factory_link_ = new CHtmlFmtDispFactoryLink(this); disp_factory_link_charwrap_ = new CHtmlFmtDispFactoryLinkCharWrap(this); cur_disp_factory_ = disp_factory_normal_; /* start off in word-wrap mode */ char_wrap_mode_ = FALSE; /* start at first sound generation */ sound_gen_id_ = 1; /* no saved table environments yet */ table_env_save_sp_ = 0; /* not yet freezing adjustments */ freeze_display_adjust_ = FALSE; /* we haven't set any positions yet */ disp_max_y_pos_ = 0; layout_max_y_pos_ = 0; } /* * destruction */ CHtmlFormatter::~CHtmlFormatter() { size_t i; /* delete any display list */ delete_display_list(); /* delete line starts table */ delete line_starts_; /* delete the tab stop hash table */ delete tab_table_; /* delete the image map table */ delete map_table_; /* if there's an image map under construction, delete it */ if (cur_map_ != 0) delete cur_map_; /* delete the display item heap pages */ for (i = 0 ; i < heap_pages_alloced_ ; ++i) th_free(heap_pages_[i].mem); /* delete the heap page array as well */ if (heap_pages_ != 0) th_free(heap_pages_); /* delete the display item factories */ delete disp_factory_normal_; delete disp_factory_charwrap_; delete disp_factory_link_; delete disp_factory_link_charwrap_; /* count the loss of a formatter */ --formatter_cnt_; /* if there are no formatters left in the system, delete the statics */ if (formatter_cnt_ == 0) { /* delete the global resource cache */ delete res_cache_; /* delete the global resource finder */ delete res_finder_; } /* delete the body display item if it's still around */ if (body_disp_ != 0) delete body_disp_; } /* * Unset the window. This is called just before my window is about to be * deleted. */ void CHtmlFormatter::unset_win() { CHtmlDisp *cur; /* if we've already unset our window, there's nothing to do here */ if (win_ == 0) return; /* unset the window in our display list and defer list */ for (cur = disp_head_ ; cur != 0 ; cur = cur->get_next_disp()) cur->unset_win(); for (cur = defer_head_ ; cur != 0 ; cur = cur->get_next_disp()) cur->unset_win(); /* unset the window in the special body display item */ body_disp_->unset_win(); /* forget our window */ win_ = 0; /* delete the special "body" display item */ delete body_disp_; body_disp_ = 0; } /* * delete the display list */ void CHtmlFormatter::delete_display_list() { /* delete everything in the display list */ CHtmlDisp::delete_list(disp_head_); /* delete everything in the deferred display list */ CHtmlDisp::delete_list(defer_head_); /* clear out the division list */ div_list_.clear(); cur_div_ = 0; /* there's nothing in the lists now */ disp_head_ = disp_tail_ = 0; defer_head_ = defer_tail_ = 0; /* forget any current line start we were keeping */ line_head_ = 0; /* * Reset the display item pool - start at the first byte of the * first page. Note that we don't delete the old pages; we'll * re-use any existing pages if we're reformatting. */ heap_page_cur_ = 0; heap_page_cur_ofs_ = 0; } /* * Terminate playback of all active media in the display list. */ void CHtmlFormatter::cancel_playback() { CHtmlDisp *cur; /* run through the display lists and cancel all playback */ for (cur = disp_head_ ; cur != 0 ; cur = cur->get_next_disp()) cur->cancel_playback(); for (cur = defer_head_ ; cur != 0 ; cur = cur->get_next_disp()) cur->cancel_playback(); /* notify the body display item (if we still have one) */ if (body_disp_ != 0) body_disp_->cancel_playback(); } /* * set the window that will be displaying our data */ void CHtmlFormatter::set_win(class CHtmlSysWin *win, const CHtmlRect *margins) { /* remember the window */ win_ = win; /* store the physical margins provided by the window */ phys_margins_ = *margins; /* initialize everything by resetting to the top of the page */ start_at_top(FALSE); /* * Create the special "body" pseudo-display item. Don't actually add * this to the display list; just create it and set it aside. */ body_disp_ = new (0) CHtmlDispBody(win_); } /* * set the physical margins */ void CHtmlFormatter::set_phys_margins(const CHtmlRect *margins) { /* store the margins */ phys_margins_ = *margins; /* we'll need to reformat everything */ start_at_top(FALSE); } /* * Clear the display list */ void CHtmlFormatter::reset_disp_list() { /* if there's no window, there's nothing to do */ if (win_ == 0) return; /* advise the window that we're about to delete the display list */ win_->advise_clearing_disp_list(); /* delete any old display list */ delete_display_list(); /* no lines yet in line start list */ line_starts_->clear_count(); line_count_ = 0; line_id_ = 0; } /* * Initialize or reset internal formatting settings. This resets all * internal state *except* the display list, which we leave intact; this is * normally used to reset state during start_at_top(). */ void CHtmlFormatter::reset_formatter_state(int reset_sounds) { /* not in title mode */ title_mode_ = FALSE; /* if there's no window, there's nothing to do */ if (win_ == 0) return; /* we're at the upper left corner of the document */ curpos_.set(margin_left_, phys_margins_.top); disp_max_y_pos_ = 0; layout_max_y_pos_ = 0; /* make sure the window forgets any background image */ win_->set_html_bg_image(0); /* allow formatting */ stop_formatting_ = FALSE; /* at the beginning of a line */ last_was_newline_ = TRUE; line_spacing_ = 0; /* we don't have anything in the line under construction yet */ avail_line_width_ = 0; /* default to wrapping lines that are too wide to fit in the margins */ wrap_lines_ = TRUE; /* we're not indented from the margins yet */ margin_left_ = phys_margins_.left; margin_right_ = phys_margins_.right; temp_margins_ = FALSE; /* clear the margin stack */ margin_stack_depth_ = 0; left_flow_stk_.reset(); right_flow_stk_.reset(); /* use default alignment */ div_alignment_ = HTML_Attrib_invalid; blk_alignment_ = HTML_Attrib_invalid; /* ask the window for the default starting font */ curfont_ = win_->get_default_font(); /* default base font size is 3 */ font_base_size_ = 3; /* nothing in the current line yet */ line_head_ = 0; /* maximum line width is nil so far */ max_line_width_ = 0; outer_max_line_width_ = 0; /* no pending tab yet */ pending_tab_ = FALSE; pending_tab_disp_ = 0; /* clear out the tab table */ tab_table_->delete_all_entries(); /* not in a link yet */ cur_link_ = 0; /* clear out the image map table */ map_table_->delete_all_entries(); /* get rid of any image map under construction */ if (cur_map_ != 0) { delete cur_map_; cur_map_ = 0; } /* we haven't seen any ordered lists yet - start at sequence number 1 */ ol_cont_seqnum_ = 1; /* no current or previous tag yet */ curtag_ = prvtag_ = 0; /* no table yet */ table_pass_ = 0; current_table_ = 0; /* * If we're resetting sounds, clear all current sounds in all layers, * and start a new generation of sounds. Otherwise, keep the * generation ID unchanged, so that we can tell that sounds we've * already played during this generation should not be played again. */ if (reset_sounds) { /* clear current sounds in all layers */ cancel_sound(HTML_Attrib_invalid, 0.0, FALSE, FALSE); /* start a new sound generation */ ++sound_gen_id_; } /* start with the normal display item factory */ cur_disp_factory_ = disp_factory_normal_; /* start off in word-wrap mode */ char_wrap_mode_ = FALSE; /* no saved table environments yet */ table_env_save_sp_ = 0; } /* * reset the formatter to restart from the beginning of the document */ void CHtmlFormatter::start_at_top(int reset_sounds) { /* reset the formatting state */ reset_formatter_state(reset_sounds); /* clear the display list */ reset_disp_list(); } /* * Get the text array pointer */ class CHtmlTextArray *CHtmlFormatter::get_text_array() const { return parser_ != 0 ? parser_->get_text_array() : 0; } /* * get the character at the given text offset */ textchar_t CHtmlFormatter::get_char_at_ofs(unsigned long ofs) const { /* ask the parser's text array for the character */ return parser_->get_text_array()->get_char(ofs); } /* * Get the number of characters between two offsets */ unsigned long CHtmlFormatter::get_chars_in_ofs_range( unsigned long startofs, unsigned long endofs) const { /* ask the parser's text array for the information */ return parser_->get_text_array()->get_char_count(startofs, endofs); } /* * Get a pointer to the text starting at the given offset */ const textchar_t *CHtmlFormatter::get_text_ptr(unsigned long ofs) const { /* get the text from the parser's text array */ return parser_->get_text_array()->get_text(ofs); } /* increment a text offset by one byte */ unsigned long CHtmlFormatter::inc_text_ofs(unsigned long ofs) const { return parser_->get_text_array()->inc_ofs(ofs); } /* decrement a text offset by one byte */ unsigned long CHtmlFormatter::dec_text_ofs(unsigned long ofs) const { return parser_->get_text_array()->dec_ofs(ofs); } /* * increment a text offset by one character (takes into account multi-byte * characters) */ unsigned long CHtmlFormatter::inc_text_ofs_char( oshtml_charset_id_t cs, unsigned long ofs) const { return parser_->get_text_array()->inc_ofs_char(cs, ofs); } /* * decrement a text offset by one character (takes into account multi-byte * characters) */ unsigned long CHtmlFormatter::dec_text_ofs_char( oshtml_charset_id_t cs, unsigned long ofs) const { return parser_->get_text_array()->dec_ofs_char(cs, ofs); } /* * get the maximum text address assigned so far */ unsigned long CHtmlFormatter::get_text_ofs_max() const { return get_text_array()->get_max_addr(); } /* * is the character at the given offset a word character? */ int CHtmlFormatter::is_word_char_at_ofs( oshtml_charset_id_t cs, unsigned long ofs) const { return get_text_array()->is_word_char(cs, ofs); } /* * Search for the given text */ int CHtmlFormatter::search_for_text(const textchar_t *txt, size_t txtlen, int exact_case, int whole_word, int wrap, int dir, unsigned long startofs, unsigned long *match_start, unsigned long *match_end) { /* ask the parser's text array to perform the search */ return parser_->get_text_array()->search( txt, txtlen, exact_case, whole_word, wrap, dir, startofs, match_start, match_end); } /* * Extract text from the text stream into the given buffer. Pulls the * text from the given starting text offset to the given ending text * offset. */ void CHtmlFormatter::extract_text(CStringBuf *buf, unsigned long start_ofs, unsigned long end_ofs) const { CHtmlDisp *disp; /* traverse our display items and pull out their text values */ for (disp = find_by_txtofs(start_ofs, FALSE, TRUE) ; disp != 0 ; disp = disp->get_next_disp()) { /* if we're past the ending offset, we're done */ if (disp->is_past_text_ofs(end_ofs)) break; /* add this item's text to the buffer */ disp->add_text_to_buf_clip(buf, start_ofs, end_ofs); } } /* * Do some formatting and return. We'll remember where we were, so the * client can repeatedly call this routine to format an entire document. * We return without formatting everything so that an interactive * program can continue servicing user interface events while a long * formatting operation is in progress. */ void CHtmlFormatter::do_formatting() { /* * make sure we have a non-container as the trailing tag, so that we * can properly process exits */ parser_->fix_trailing_cont(); /* * Keep going until we've built a line to display, or we run out of * tags, whichever comes first. Do not stop at the end of a line if * the line is within a table, since we need to format each table as * a unit. */ for (line_output_ = FALSE ; curtag_ != 0 && curtag_->ready_to_format() && !stop_formatting_ && (!line_output_ || table_pass_ != 0) ; ) { /* format the current tag */ curtag_->format(win_, this); /* remember the last tag we formatted */ prvtag_ = curtag_; /* move on to the next tag in format order */ curtag_ = curtag_->get_next_fmt_tag(this); } /* * If we just did the last tag, figure the positions for items in * the partially-filled last line, since we'd normally figure line * positions at the next line break. */ if (curtag_ == 0 || stop_formatting_) { /* put the last line's items temporarily on a new line */ ++line_id_; /* adjust positions of items in final line */ set_line_positions(line_head_, 0); /* * in case we re-do the last line, do it at the same line number * as we just did */ --line_id_; } } /* * Figure out if I have more work to do. */ int CHtmlFormatter::more_to_do() { /* if we've halted formatting, there's nothing left to do */ if (stop_formatting_) return FALSE; /* if the current tag isn't ready to proceed, stop for now */ if (curtag_ != 0 && !curtag_->ready_to_format()) return FALSE; /* if we have a non-null current tag, there's definitely more to do */ if (curtag_ != 0) return TRUE; /* * There's no current tag, so we must have formatted everything we * had in the parse tree when last we formatted anything; however, * more parse tree may have since been built. To find out, we can * look to see if there's a tag following the last tag we parsed; if * so, move to that new tag and proceed. */ if (prvtag_ != 0) { /* * make sure we have a non-container as the trailing tag, so that * we can properly process exits */ parser_->fix_trailing_cont(); /* * Ask the previous tag that we formatted to check again to see * if there's anything after it, and re-set the current tag to * point to the item after the last tag. If we added anything, * this will find it. */ curtag_ = prvtag_->get_next_fmt_tag(this); /* if we have a non-null current tag after that, there's more to do */ if (curtag_ != 0 && !stop_formatting_) return TRUE; } /* nothing left to do */ return FALSE; } /* * Add an item to the line starts table */ void CHtmlFormatter::add_line_start(CHtmlDisp *item) { /* * only add the item if we're not in multi-column mode, and the item * is non-null */ if (item != 0 && !is_multi_col()) { unsigned long item_ofs; /* * The line starts table is indexed in ascending order of both y * position and text offset. The y position is taken care of * automatically by our insistence that we only add line starts in * single-column areas and our monotonic y positioning strategy, * so we need only check the text offset. If this item has a * lower text offset than previous items, remove old items until * this one is greater than the last item. */ item_ofs = item->get_text_ofs(); for ( ; line_count_ != 0 ; --line_count_) { CHtmlDisp *cur; /* get this item */ cur = line_starts_->get(line_count_ - 1); /* * If this one is ordered properly with respect to text * offset, we can add the new one after it. Note that, since * we always make this check before adding an item, the entire * existing list is always in order; so as soon as we find an * item we're higher than, we know we're higher than all of * the earlier items as well. */ if (cur->get_text_ofs() <= item_ofs) break; } /* add the item */ line_starts_->add(line_count_, item, curpos_.y); ++line_count_; } /* update the line ID */ ++line_id_; } /* * Add an item to the display list. This is an internal routine that * simply links the item into the list without performing any formatting * on it. */ void CHtmlFormatter::add_to_disp_list(CHtmlDisp *item) { CHtmlDisp *nxt; /* * add it after the current end; or, if the list is empty, make it * the head of the list */ if (disp_tail_) item->add_list_after(disp_tail_); else disp_head_ = item; /* move the display tail to the end of the list */ for (disp_tail_ = item ; (nxt = disp_tail_->get_next_disp()) != 0 ; disp_tail_ = nxt) ; } /* * Add an item to the end of the display list */ void CHtmlFormatter::add_disp_item(CHtmlDisp *item) { CHtmlRect pos; CHtmlDisp *new_line_head; /* if we're in title mode, simply ignore the item */ if (title_mode_) { /* this item doesn't show up as actual text, so delete it */ delete item; /* that's all we need to do with it */ return; } /* assume the item isn't a line break */ last_was_newline_ = FALSE; /* * for now, set the new item to the maximum y position, so it * doesn't show up at the top of the screen if we should draw at an * intermediate state before setting the final positions of all of * the items */ pos = item->get_pos(); pos.offset(0, layout_max_y_pos_ + 1000 - pos.top); item->set_pos(&pos); /* add it to the display list */ add_to_disp_list(item); /* * Add this item to the current line, and check for breaks. Keep * going as long as we find new line breaks, since we may break up * this item into numerous lines. */ do { /* * If we don't have anything in the current line yet, remember * this item as the start of the current line. */ if (line_head_ == 0) { /* this is the first item in the line */ line_head_ = item; /* reset the break position record */ breakpos_.clear(); /* the current margins will determine the width of this line */ avail_line_width_ = get_space_between_margins(); } refigure_line_break: /* * Try breaking the line if needed. Note that we don't break * lines if we're not in line-wrapping mode, and we also don't * break lines if we're in the first pass through a table, since * the format results of the first pass through a table are * always discarded (so breaking lines would be wasted work). */ if (wrap_lines_ && table_pass_ != 1) { CHtmlDisp *cur; long wid; /* * Scan the current item and all subsequent items until we * find a line break. We may have new items due to other * line breaks we've added in past iterations on this item. */ for (wid = 0, cur = item ; cur != 0 ; cur = cur->get_next_disp()) { /* scan the current item for a line break */ new_line_head = cur->find_line_break( this, win_, avail_line_width_ - wid, &breakpos_); /* if we found a break, we can stop looking */ if (new_line_head != 0) break; /* add this item's width into our running width */ wid += cur->measure_width(win_); } } else new_line_head = 0; /* * if this item indicates that a line break should precede it, * and it's already at the start of the line, ignore its desire * to place a line break here */ if (new_line_head == item && item == line_head_) new_line_head = 0; /* * Go through the items that will remain on this line and make * sure they fit. If they don't, try breaking free of marginal * material to make them fit, to ensure that we don't overlap * material in the right margin. This exercise is pointless if * we don't have any marginal material, since there's nothing to * break clear of, so skip it in that case. */ if (left_flow_stk_.can_end_top_item(current_table_) || right_flow_stk_.can_end_top_item(current_table_)) { int changed_margins; int reached_line_head; CHtmlDisp *prv; /* * Check to see if the new line head is *prior* to the * current item. If this is the case, we went back and * found an earlier break, in which case everything should * definitely fit, and we don't need to worry about clearing * the margins. */ for (prv = new_line_head ; prv != 0 && prv != item ; prv = prv->get_next_disp()) ; if (prv != item) { /* haven't yet changed any margins */ changed_margins = FALSE; /* * Go through the items left on this line, and make sure * they all fit. */ for (reached_line_head = FALSE ; item != 0 ; ) { long wid; /* get this item's width */ wid = item->measure_width(win_); /* * note if we've reached the line break we * previously found */ if (item == new_line_head) reached_line_head = TRUE; /* * If it doesn't fit, the line is too long, and we * couldn't break it anywhere; in this case, try * breaking clear of any marginal material so that * we don't overlap anything in the right margin. */ if (wid > avail_line_width_) { /* * If we've already changed margins, try * re-breaking the line without making any * further margin changes */ if (changed_margins) goto refigure_line_break; /* * if we were already going to break the line by * this point, there's no need to clear any more * space - everything up to this point fits */ if (reached_line_head) { new_line_head = item; break; } /* * If we've already determined that this is to * be the last item on the line, check its size * minus trailing whitespace and see if it fits * then. */ if (item->get_next_disp() == new_line_head && (wid - item->measure_trailing_ws_width(win_) <= avail_line_width_)) { /* * it actually does fit - adjust the width * accordingly and keep going */ wid -= item->measure_trailing_ws_width(win_); } else { /* see if we can free up any space */ if (try_clearing_margins(wid)) changed_margins = TRUE; /* * if this item still doesn't fit, try * breaking the line again */ if (wid > avail_line_width_) goto refigure_line_break; } } /* * deduct this one's width from the available line * width, and add it to the current position */ avail_line_width_ -= wid; curpos_.x += wid; /* * Move on to the next item */ item = item->get_next_disp(); if (item == 0) break; /* * If we don't have a new line head, scan this item * for a line break. We don't care about finding an * actual line break; what we care about is making * sure that we've noted possible positions to break * the line when adding future items. If we've * already found a line break don't bother with * this, since we'll scan from the start of the new * line when we format that line. */ if (new_line_head == 0) item->find_line_break(this, win_, avail_line_width_, &breakpos_); } } } /* if there was a line break, process it */ if (new_line_head != 0) { /* break the line */ start_new_line(new_line_head); /* start the line breaking process again at the new line head */ item = new_line_head; /* * since we added the new item to the list, we need to make * sure we have the correct tail pointer */ while (disp_tail_->get_next_disp() != 0) disp_tail_ = disp_tail_->get_next_disp(); } else { /* * everything fits on this line - go through the items and * deduct their space from the running total */ if (item != 0) { for (;;) { long wid; /* deduct this item's space from the total */ wid = item->measure_width(win_); avail_line_width_ -= wid; curpos_.x += wid; /* move on to the next item, and stop if it's the last */ item = item->get_next_disp(); if (item == 0) break; /* * Scan this item for a line break -- since we * already know it fits, we won't actually find a * line break, but we at least want to go through it * and find its last possible break position in case * the next item we add doesn't fit. */ item->find_line_break(this, win_, avail_line_width_, &breakpos_); } } } } while (new_line_head != 0); } /* * Add a hypertext link display item */ void CHtmlFormatter::add_disp_item_link(CHtmlDispLink *link) { /* remember the current link */ cur_link_ = link; /* adjust the display item factory now that we're in link mode */ adjust_disp_factory(); /* add the item to the display list as normal */ add_disp_item(link); } /* * End the current hypertext link */ void CHtmlFormatter::end_link() { /* forget the link */ cur_link_ = 0; /* adjust the display item factory now that we're not in link mode */ adjust_disp_factory(); } /* * Enter a
item */ void CHtmlFormatter::begin_div(CHtmlDispDIV *disp) { /* add the display item to our division list */ div_list_.add_ele(disp); /* note this DIV's parent, and make this DIV current */ disp->set_parent_div(cur_div_); cur_div_ = disp; /* add it to the main display list as well */ add_to_disp_list(disp); } /* * End the current
*/ void CHtmlFormatter::end_div() { /* if we have a current DIV display item, close it up */ if (cur_div_ != 0) { /* note the last item covered in the DIV range */ cur_div_->set_div_tail(disp_tail_); /* pop the DIV stack */ cur_div_ = cur_div_->get_parent_div(); } } /* * Adjust the display item factory for the current set of modes. */ void CHtmlFormatter::adjust_disp_factory() { /* * if we're within a link, use a linking factory; otherwise, use a * normal non-linking factory */ if (cur_link_ != 0) { /* use the appropriate linking factory for the wrapping mode */ cur_disp_factory_ = (char_wrap_mode_ ? disp_factory_link_charwrap_ : disp_factory_link_); } else { /* use the appropriate non-linking factory for the wrapping mode */ cur_disp_factory_ = (char_wrap_mode_ ? disp_factory_charwrap_ : disp_factory_normal_); } } /* * Hash table entry for a tab stop */ class CHtmlFmtHashEntryTab: public CHtmlHashEntryCI { public: CHtmlFmtHashEntryTab(const textchar_t *str, size_t len, HTML_Attrib_id_t align, textchar_t dp, long xpos) : CHtmlHashEntryCI(str, len, FALSE) { align_ = align; dp_ = dp; xpos_ = xpos; } /* alignment type */ HTML_Attrib_id_t align_; /* decimal point (for ALIGN=DECIMAL) */ textchar_t dp_; /* x position */ long xpos_; }; /* * Define a tab stop */ void CHtmlFormatter::set_tab(const textchar_t *tab_id, HTML_Attrib_id_t align, textchar_t dp) { CHtmlFmtHashEntryTab *entry; /* if no alignment was specified, assume left */ if (align == HTML_Attrib_invalid) align = HTML_Attrib_left; /* if no DP was specified, use '.' */ if (dp == 0) dp = '.'; /* if we already have an entry for this tab ID, re-use it */ entry = (CHtmlFmtHashEntryTab *)tab_table_-> find(tab_id, get_strlen(tab_id)); if (entry != 0) { /* already there - re-define this entry */ entry->align_ = align; entry->dp_ = dp; entry->xpos_ = curpos_.x; } else { /* create a new entry and add it to the table */ entry = new CHtmlFmtHashEntryTab(tab_id, get_strlen(tab_id), align, dp, curpos_.x); tab_table_->add(entry); } } /* * Tab to a defined tab stop */ void CHtmlFormatter::tab_to(CHtmlTagTAB *tag) { CHtmlFmtHashEntryTab *entry; HTML_Attrib_id_t align; textchar_t dp; long tab_xpos; /* if there's an INDENT setting, use that */ if (tag->use_indent_ || tag->use_multiple_) { long en_size; long spacing; /* * measure an 'n' in the current font (okay, it's not * technically what the 'en' unit means, but it gives us a * pretty good approximation) */ en_size = win_->measure_text(get_font(), "n", 1, 0).x; spacing = tag->indent_ * en_size; /* check for the MULTIPLE attribute */ if (tag->use_multiple_) { long next_stop; /* * since we need to divide by the spacing, if the multiple * is zero, force it to one en */ if (spacing == 0) spacing = en_size; /* it's MULTIPLE - space to a multiple of the given spacing */ next_stop = ((curpos_.x / spacing) + 1) * spacing; /* if that doesn't get us at least an en, use next stop */ if (next_stop < curpos_.x + en_size) next_stop += spacing; /* figure the amount of space to get to this stop */ spacing = next_stop - curpos_.x; } /* add the spacing */ add_disp_item(new (this) CHtmlDispTab(&curpos_, spacing)); /* all done */ return; } /* close any pending tab */ close_pending_tab(); /* get the tag's alignment attributes */ align = tag->align_; dp = tag->dp_; /* if there's no ID, treat it as a right-margin stop */ if (tag->id_.get() == 0) { /* anonymous tab - see what kind of alignment we have */ switch(align) { case HTML_Attrib_left: /* treat it as a space */ add_disp_item(new (this) CHtmlDispText(win_, get_font(), " ", 1, tag->txtofs_)); return; default: /* align text with respect to right margin */ tab_xpos = win_->get_disp_width() - margin_right_; break; } } else { /* find the given ID */ entry = (CHtmlFmtHashEntryTab *)tab_table_-> find(tag->id_.get(), get_strlen(tag->id_.get())); /* see if we found it */ if (entry == 0) { /* treat the tab as a single space */ add_disp_item(new (this)CHtmlDispText(win_, get_font(), " ", 1, tag->txtofs_)); /* there's nothing more to do */ return; } /* get the position of the original tab */ tab_xpos = entry->xpos_; /* use the original tab's alignment if not overridden */ if (align == HTML_Attrib_invalid) align = entry->align_; /* use the original tab's DP if not overridden */ if (dp == 0) dp = entry->dp_; } /* * Add a display item for the tab. We'll set the width of this * display item when we figure out how to align the material at the * tab stop. */ pending_tab_disp_ = new (this) CHtmlDispTab(&curpos_); add_disp_item(pending_tab_disp_); /* check the alignment */ switch(align) { case HTML_Attrib_left: /* add space up to the tab stop */ if (curpos_.x < tab_xpos) { pending_tab_disp_->set_width(tab_xpos - curpos_.x); curpos_.x += tab_xpos - curpos_.x; } /* this tab is fully resolved */ pending_tab_ = FALSE; pending_tab_disp_ = 0; break; case HTML_Attrib_center: case HTML_Attrib_right: case HTML_Attrib_decimal: /* * we can't determine the tab's spacing until the next tab or * the end of the line */ pending_tab_ = TRUE; pending_tab_align_ = align; pending_tab_xpos_ = tab_xpos; pending_tab_dp_ = (dp == 0 ? '.' : dp); break; default: /* other cases should never occur; ignore them if they do */ break; } } /* * Close any pending tab. For tabs aligned other than LEFT, we need to * wait until we reach the next tab stop or the end of the line to set a * tab; when one of these events happens, this routine must be called to * finish setting the pending tab. */ void CHtmlFormatter::close_pending_tab() { long start_x; long text_wid; long open_space; long spacing; unsigned long ofs; CHtmlDisp *disp; /* if there's no pending tab, there's nothing to do */ if (pending_tab_ == 0) return; /* note where the material after the tab started */ start_x = pending_tab_disp_->get_pos().left; /* note the size of the material after the tab */ text_wid = curpos_.x - start_x; /* note the amount of space between the tab and the starting point */ open_space = pending_tab_xpos_ - start_x; /* * take appropriate action based on the alignment of the previous * tab */ switch(pending_tab_align_) { case HTML_Attrib_center: /* * Special case: if we're centering at an anonymous tab stop * centered, center text between the start of the tab material * and the right margin */ if (pending_tab_xpos_ == win_->get_disp_width() - margin_right_) { spacing = (pending_tab_xpos_ - start_x - text_wid) / 2; pending_tab_disp_->set_width(spacing); curpos_.x += spacing; break; } /* * Expand the tab filler so that it fills up half the space left * between here and the tab stop. */ spacing = open_space - text_wid/2; if (spacing > 0) { long max_spacing; /* * Check to make sure this doesn't push material past the * right margin */ max_spacing = win_->get_disp_width() - margin_right_ - text_wid - start_x; if (spacing > max_spacing) spacing = max_spacing; /* apply the spacing if possible */ if (spacing > 0) { pending_tab_disp_->set_width(spacing); curpos_.x += spacing; } } break; case HTML_Attrib_right: do_align_right: /* * Expand the tab filler so that it fills up the space left * between here and the tab stop. Leave it unexpanded if we're * already past the tab stop. */ spacing = open_space - text_wid; if (spacing > 0) { pending_tab_disp_->set_width(spacing); curpos_.x += spacing; } break; case HTML_Attrib_decimal: /* * Align material to the left of the decimal point character * with flush right with the tab position. If we can't find the * decimal point, align the whole thing right. */ for (ofs = pending_tab_disp_->get_text_ofs() ;; ) { unsigned long nxtofs; /* if this is the character we're looking for, we're done */ if (get_char_at_ofs(ofs) == pending_tab_dp_) { /* move forward to include the decimal character */ ofs = inc_text_ofs(ofs); /* no need to look further */ break; } /* get the next character offset; stop if we're at the end */ nxtofs = inc_text_ofs(ofs); if (nxtofs == ofs) break; /* move on to the next position */ ofs = nxtofs; } /* measure the text from the start to this offset */ for (text_wid = 0, disp = pending_tab_disp_ ; disp != 0 ; disp = disp->get_next_disp()) { CHtmlRect pos; /* get the position of this item */ pos = disp->get_pos(); /* * if this is the item that contains the ending offset, * measure only up to the ending offset and stop looking */ if (disp->contains_text_ofs(ofs)) { CHtmlPoint pt; /* find out where the character is within the item */ pt = disp->get_text_pos(win_, ofs); /* add the width to that point */ text_wid += pt.x - pos.left; /* no need to look any further */ break; } /* include the full width of this item */ text_wid += pos.right - pos.left; } /* now align this amount of text flush right with the tab */ goto do_align_right; default: /* other cases should never occur; ignore them if they do */ break; } /* no more pending tab */ pending_tab_ = FALSE; pending_tab_disp_ = 0; } /* * Add an item to the tail of the display list, replacing the current * command input display item(s), if any. This is used during editing * of a command line to update the display after the user has made a * change in the contents of the command line. The default * implementation here is not input-capable, so we simply add the * display item as though it were any other display item. */ void CHtmlFormatter::add_disp_item_input(CHtmlDispTextInput *item, CHtmlTagTextInput *) { add_disp_item(item); } /* * Add a floating display item. We don't add this type of item to the * display list immediately, but put it in a list of items that will be * added when we start the next line. */ void CHtmlFormatter::add_disp_item_deferred(CHtmlDisp *item) { /* if we're on pass 1 of a table, treat this as an ordinary item */ if (table_pass_ == 1) { add_disp_item(item); return; } /* add the floating item to the deferred floating item list */ if (defer_tail_ != 0) item->add_after(defer_tail_); else defer_head_ = item; defer_tail_ = item; /* * If we haven't yet consumed any of the horizontal space of the * current line, or we don't have anything on the current line yet, * add the deferred item immediately. */ if (line_head_ == 0 || avail_line_width_ == get_space_between_margins()) { add_all_deferred_items(0, 0, FALSE); line_head_ = 0; } } /* * Add an item that was previously deferred to the display list. */ void CHtmlFormatter::deferred_item_ready(CHtmlDisp *disp) { /* tell the item that it's being added */ disp->format_deferred(win_, this, line_spacing_); } /* * Add all of the items from the deferred floating item list to the * display list. */ void CHtmlFormatter::add_all_deferred_items(CHtmlDisp *prv_line_head, CHtmlDisp *nxt_line_head, int force) { CHtmlDisp *again_head = 0; CHtmlDisp *again_tail = 0; /* if there are no deferred items, there's nothing to do */ if (defer_head_ == 0) return; /* * We want to make sure the deferred items are inserted at the start * of the new line. If the items in the next line have already been * inserted into the display list, we need to temporarily remove * them before inserting the deferred items, then re-insert those * items. */ if (nxt_line_head != 0) { CHtmlDisp *prv; /* find the item preceding the head of the new line */ for (prv = prv_line_head ; prv != 0 && prv->get_next_disp() != nxt_line_head ; prv = prv->get_next_disp()) ; /* make the end of the previous line the end of the list for now */ if (prv != 0) { /* found it - remove it from the list */ prv->clear_next_disp(); disp_tail_ = prv; } else { /* * didn't find it - it's not there, so we can't remove it, * so don't add it in later */ nxt_line_head = 0; } } /* keep going until we run out of items */ while (defer_head_ != 0) { CHtmlDisp *cur; /* remember this item */ cur = defer_head_; /* move on to the next item */ defer_head_ = cur->get_next_disp(); if (defer_head_ == 0) defer_tail_ = 0; /* unlink this item from the list */ cur->clear_next_disp(); /* * If this item fits or we don't have indented margins, add it; if * not, we'll have to add it later when the margins get wider. If * we're forcing adding the deferred items, add it even if it * doesn't fit. */ if (force || !is_multi_col() || cur->measure_width(win_) <= get_space_between_margins()) { /* add this item to the display list */ deferred_item_ready(cur); } else { /* * it doesn't fit - hang onto it, and put it back into the * deferred item list for another try later (we'll just keep * trying at the end of each line until we can find a place * to put it) */ if (again_tail == 0) again_head = cur; else cur->add_after(again_tail); again_tail = cur; } } /* * If we found any items that didn't fit, put them back into the * deferred item list. Note that we don't want to go through any of * the processing that accompanied their first insertion into the * deferred item list, since they were already in the list -- just * put them directly into the list. This is especially easy, since * the list is entirely empty right now. */ defer_head_ = again_head; defer_tail_ = again_tail; /* * Now that we've added the deferred items, add the items from the * next line back onto the end of the display list. */ if (nxt_line_head != 0) add_to_disp_list(nxt_line_head); } /* * Remove the last item added to the display list from the current line, * and start a new line flowing around the last item. The flow mode * will continue until we reach the y position given as the bottom of * this object. */ void CHtmlFormatter::begin_flow_around(CHtmlDisp *item, long new_left_margin, long new_right_margin, long flow_bottom) { CHtmlRect pos; /* * if we're not already in multi-column mode, and we have no lines * yet, add this item to the line list so that we can find items * within the multi-column area */ if (line_count_ == 0) add_line_start(item); /* * add this item to the display list, but don't perform any of the * usual formatting on it */ add_to_disp_list(item); /* push the change onto the appropriate flow-around stack */ if (new_left_margin != margin_left_) left_flow_stk_.push(margin_left_ - new_left_margin, flow_bottom, current_table_); if (new_right_margin != margin_right_) right_flow_stk_.push(margin_right_ - new_right_margin, flow_bottom, current_table_); /* set the new margins */ set_margins(new_left_margin, new_right_margin); /* * if this item goes past the current maximum width of the document, * notify the window that it will have to adjust the horizontal * scrolling limits */ pos = item->get_pos(); if (pos.right > outer_max_line_width_) { /* remember the outer-level max line width */ outer_max_line_width_ = pos.right; /* adjust the window's horizontal scrollbar */ win_->fmt_adjust_hscroll(); } } /* * Remove items from the display list starting with the given item. * We'll work forward from the given line to find the item. */ void CHtmlFormatter::remove_from_disp_list(long start_line, CHtmlDisp *item) { CHtmlDisp *cur; /* start at the head of the given starting line */ cur = line_starts_->get(start_line); /* try to find the item just before the given item */ for ( ; cur != 0 && cur->get_next_disp() != item ; cur = cur->get_next_disp()) ; /* if we found it, remove this item */ if (cur != 0) { /* clear the forward pointer of the previous item */ cur->clear_next_disp(); /* move the display list tail to the previous item */ disp_tail_ = cur; } /* if this item was the head, forget it */ if (disp_head_ == item) disp_head_ = disp_tail_ = 0; } /* * Check flow-around items to see if we're past the end of them and can * return to the enclosing margins. */ int CHtmlFormatter::check_flow_end() { int found_end; /* haven't found any ended items yet */ found_end = FALSE; /* check the left flow stack */ while (left_flow_stk_.is_past_end(curpos_.y, current_table_)) { /* restore the margins in effect with this item */ set_margins(margin_left_ + left_flow_stk_.get_top()->old_margin_delta, margin_right_); /* pop this flow stack item */ left_flow_stk_.pop(); /* note that we found an ended item */ found_end = TRUE; } /* check the right flow stack */ while (right_flow_stk_.is_past_end(curpos_.y, current_table_)) { /* restore the margins in effect with this item */ set_margins(margin_left_, margin_right_ + right_flow_stk_.get_top()->old_margin_delta); /* pop this flow stack item */ right_flow_stk_.pop(); /* note that we found an ended item */ found_end = TRUE; } /* * return an indication as to whether or not we found any items that * reached their end */ return found_end; } /* * Compute the total amount of space available between the margins */ long CHtmlFormatter::get_space_between_margins() { /* * calculate the total space between the margins as the display * width available in the window less the sum of the margin sizes */ return win_->get_disp_width() - (margin_left_ + margin_right_); } /* * Add an item on a new line */ void CHtmlFormatter::add_disp_item_new_line(CHtmlDisp *item) { /* start a new line with this item */ start_new_line(item); /* add the item to the display list */ add_disp_item(item); /* note that we just started a new line */ last_was_newline_ = TRUE; } /* * Start a new line after the given item, making the item the last item * in the current line. */ void CHtmlFormatter::add_disp_item_new_line_after(CHtmlDisp *item) { /* add this item to the display list */ add_disp_item(item); /* * Create an empty line break item and add it as the first item on * the next line. We'll make the vertical size of the new item zero * so that it doesn't contribute to the size of the next line. */ add_disp_item_new_line(new (this) CHtmlDispBreak(0)); /* note that we just started a new line */ last_was_newline_ = TRUE; } /* * Add minimum line spacing. This makes sure that we're starting a new * line, and that the line spacing for the new line is at least as much * as requested. If we're already on a new line and have provided the * requested amount of inter-line spacing, this doesn't do anything. */ void CHtmlFormatter::add_line_spacing(int spacing) { /* if we're not at the beginning of a line, add a new line */ if (!last_was_newline_) add_disp_item_new_line(new (this) CHtmlDispBreak(0)); /* set the desired spacing to at least the requested amount */ if (line_spacing_ < spacing) line_spacing_ = spacing; } /* * Add vertical whitespace as needed to move past floating items in one * or both margins. Returns true if any line spacing was added. */ int CHtmlFormatter::break_to_clear(int left, int right) { int i; int y; int cur; /* presume we won't need to go anywhere */ y = 0; /* get the bottom-most item in the left stack, if desired */ if (left) { /* scan from the most deeply nested outward */ for (i = left_flow_stk_.get_count() ; i != 0 ; ) { /* move to the next item */ --i; /* * if this item can't close due to table nesting, we can stop * scanning - we must remove items in stack order, so once we * reach an unclosable item, we can't close anything enclosing * it, either */ if (!left_flow_stk_.can_end_item(i, current_table_)) break; /* get this item's bottom */ cur = left_flow_stk_.get_by_index(i)->bottom; /* if it's the bottom-most bottom so far, remember it */ if (cur > y) y = cur; } } /* do the same for the right stack, if desired */ if (right) { /* scan from most depely nested outward */ for (i = right_flow_stk_.get_count() ; i != 0 ; ) { /* move to the next item */ --i; /* stop if we can't remove this item */ if (!right_flow_stk_.can_end_item(i, current_table_)) break; /* get this item's bottom */ cur = right_flow_stk_.get_by_index(i)->bottom; /* if it's the bottom-most bottom so far, remember it */ if (cur > y) y = cur; } } /* if necessary, insert enough space to move below this point */ if (y + 1 > curpos_.y) { add_line_spacing(y - curpos_.y + 1); return TRUE; } /* we didn't add any spacing */ return FALSE; } /* * Try to add vertical whitespace until we free up the given amount of * space, if we have any floating material in the margins. If we don't * have floating material, this routine has no effect; otherwise, it * will add the minimum amount of whitespace necessary to have the given * width free in the line. Returns true if we made any changes, false * if not. Note that we return true if we adjust the margins, even if * we don't free up the required amount of space. */ int CHtmlFormatter::try_clearing_margins(long wid) { int changed; /* presume we won't make any changes */ changed = FALSE; /* * keep going until we run out of marginal material or free up * enough space */ while ((left_flow_stk_.can_end_top_item(current_table_) || right_flow_stk_.can_end_top_item(current_table_)) && avail_line_width_ < wid) { CHtmlRect area; long y, yleft, yright; /* see which currently endable item has less space left on it */ yleft = (left_flow_stk_.can_end_top_item(current_table_) ? left_flow_stk_.get_top()->bottom : 0); yright = (right_flow_stk_.can_end_top_item(current_table_) ? right_flow_stk_.get_top()->bottom : 0); /* * use the one with less space remaining (or the one with an * item if the other is empty) */ if (yleft == 0) y = yright; else if (yright == 0) y = yleft; else y = (yleft < yright ? yleft : yright); /* invalidate the entire vertical area we're skipping */ area.set(0, curpos_.y, HTMLSYSWIN_MAX_RIGHT, y + 1); win_->inval_doc_coords(&area); /* advance our y position so we're just clear of this item */ curpos_.y = y + 1; /* * Remove the flow items from the margins. This will update * avail_line_width_ and curpos_.x appropriately for the change, * and will remove from the flow stacks any items that we have * moved past. */ if (!check_flow_end()) break; /* we've made a change */ changed = TRUE; } /* return margin change indication */ return changed; } /* * Start a new line. If a display list item is given, the item will be * set as the first item on the new line; otherwise, everything in the * list from the start of the last line will be on one line. If a new * line head isn't given, this routine doesn't actually break the line, * but instead simply sets the positions for the items in the current * line. */ void CHtmlFormatter::start_new_line(CHtmlDisp *next_line_head) { CHtmlDisp *old_line_head; CHtmlDisp *cur; /* finish any pending tab alignment */ close_pending_tab(); /* remember the start of the current line */ old_line_head = line_head_; /* add this line start item to the line starts table */ add_line_start(line_head_); /* set positions of items in the current line */ set_line_positions(line_head_, next_line_head); /* forget the old line head if we have a new one */ if (next_line_head != 0) line_head_ = 0; /* if temporary margins are in effect, restore original margins */ if (temp_margins_) set_margins(margin_left_ + margin_left_delta_nxt_, margin_right_ + margin_right_delta_nxt_); /* check the flow-around items to see if we're past them */ check_flow_end(); /* * if we have an item for the next line, we're definitely moving to * a new line (we could add more later to the current line, if not); * if this is the case, we can now move all of the deferred items * into the display list, since these items were deferred until the * start of the next line, which we have now reached */ if (next_line_head != 0) { /* add the deferred items */ add_all_deferred_items(old_line_head, next_line_head, FALSE); /* set the x position back to the start of the line */ curpos_.x = margin_left_; } /* we've output a line */ line_output_ = TRUE; /* * make sure the items on the next lines have a reasonable default * position, in case we draw them while we're still working on this * line */ for (cur = next_line_head ; cur != 0 ; cur = cur->get_next_disp()) { CHtmlRect pos; pos = cur->get_pos(); pos.bottom = layout_max_y_pos_ + (pos.bottom - pos.top); pos.top = layout_max_y_pos_; cur->set_pos(&pos); } } /* * Set line positions - sets the horizontal and vertical positions of * the items in the line according to their sizes and display * properties. */ void CHtmlFormatter::set_line_positions(CHtmlDisp *line_head, CHtmlDisp *next_line_head) { CHtmlDisp *cur; long x; int max_text_height; int max_above_base; int max_below_base; int max_total; int total_width; CHtmlRect area; HTML_Attrib_id_t align; /* * Go through the display items and determine how much space we * need for the text. */ for (max_text_height = 0, cur = line_head ; cur != 0 && cur != next_line_head ; cur = cur->get_next_disp()) { int text_height; /* set this item's line ID */ cur->set_line_id(line_id_); /* get this item's text space needs */ text_height = cur->get_text_height(win_); /* if this exceeds the maximum needed so far, it's the new max */ if (text_height > max_text_height) max_text_height = text_height; } /* * Now that we know the total height of the text, go through and * ask each item how much space it needs overall, and how much space * it needs above the text baseline. */ for (max_total = 0, max_below_base = 0, max_above_base = 0, cur = line_head, total_width = 0 ; cur != 0 && cur != next_line_head ; cur = cur->get_next_disp()) { int total; int above_base; int below_base; /* get this item's specific vertical space needs */ cur->get_vertical_space(win_, max_text_height, &above_base, &below_base, &total); /* if this exceeds the maxima needed so far, it's the new max */ if (total > max_total) max_total = total; if (below_base > max_below_base) max_below_base = below_base; if (above_base > max_above_base) max_above_base = above_base; /* get this item's horizontal space needs */ total_width += cur->measure_width(win_); } /* * If the total above plus the total below exceeds the overall * total, increase the overall total accordingly */ if (max_above_base + max_below_base > max_total) max_total = max_above_base + max_below_base; /* figure out where to start, according to the alignment settings */ align = (blk_alignment_ == HTML_Attrib_invalid ? div_alignment_ : blk_alignment_); switch(align) { case HTML_Attrib_invalid: case HTML_Attrib_left: case HTML_Attrib_justify: default: /* left alignment -- start at the left margin */ x = margin_left_; break; case HTML_Attrib_right: /* * Right alignment -- start far enough in to line up with the * right margin: subtract the total space needs for the line * from the total amount of physical space in the window minus * the size of the right margin. Limit the position so that we * don't start left of the left margin. */ x = (win_->get_disp_width() - margin_right_) - total_width; if (x < margin_left_) x = margin_left_; break; case HTML_Attrib_center: /* * Centered -- start far enough in to center the line: subtract * the total space needs for the line from the total amount of * logical space in the line (which is the amount of physical * space minus the space taken up by the margins), divide that * in half, and add it to the left margin. Limit the position * so that we don't start left of the left margin. */ x = ((win_->get_disp_width() - margin_right_ - margin_left_) - total_width)/2 + margin_left_; if (x < margin_left_) x = margin_left_; break; } /* add the line spacing */ max_total += line_spacing_; max_above_base += line_spacing_; /* * Now that we know how much vertical space is required overall, we * can figure out where the text baseline goes, so go through the * items in the line again and set each item's position. */ for (cur = line_head ; cur != 0 && cur != next_line_head ; cur = cur->get_next_disp()) { /* set this item's position */ cur->set_line_pos(win_, x, curpos_.y, max_total, max_above_base, max_text_height); /* move past this item horizontally */ x += cur->measure_width(win_); } /* hide trailing whitespace on the line */ CHtmlDisp_wsinfo wsinfo; wsinfo.hide_trailing_whitespace(win_, line_head, next_line_head); /* * If the line width exceeds the maximum line width so far, note * the new maximum and let the window know that it's changing, so * that it can adjust scrollbar settings if necessary. */ if (x > max_line_width_) { /* remember the maximum line width */ max_line_width_ = x; } /* * if we're not within a table, set the outermost max line width as * well */ if (table_pass_ == 0 && x > outer_max_line_width_) { /* remember the outer-level max line width */ outer_max_line_width_ = x; /* adjust the window's horizontal scrollbar */ win_->fmt_adjust_hscroll(); } /* adjust the total height record to include this line */ layout_max_y_pos_ = curpos_.y + max_total; /* * if this is above the display max y position, and we're not laying * out a table, advance the display pos */ if (layout_max_y_pos_ > disp_max_y_pos_ && table_pass_ == 0) disp_max_y_pos_ = layout_max_y_pos_; /* * invalidate the screen area of the new text (unless display * updating is frozen, in which case the caller will have to take * care of it) */ if (!freeze_display_adjust_) { area.set(0, curpos_.y, HTMLSYSWIN_MAX_RIGHT, curpos_.y + max_total); win_->inval_doc_coords(&area); } /* * adjust the current vertical position to the top of the next * line, if another line follows */ if (next_line_head != 0) { /* adjust the vertical position */ curpos_.y += max_total; /* we've used the line spacing */ line_spacing_ = 0; } /* note the new x position */ curpos_.x = x; /* * notify the window of the new line (unless the display is frozen * or we're in the middle of a table -- in either case, we'll wait * until we're done with the frozen section or the table to update * the window size range) */ if (!freeze_display_adjust_ && table_pass_ == 0) win_->fmt_adjust_vscroll(); } /* * Add an extra line height to the display height, beyond the height * required for the layout height. */ void CHtmlFormatter::add_line_to_disp_height() { int ascent; unsigned long new_disp_ht; /* * figure the new display height - add one line height in the current * font to the current layout height */ new_disp_ht = layout_max_y_pos_ + win_->measure_text(curfont_, " ", 1, &ascent).y; /* if it's higher than the maximum so far, update the maximum */ if (new_disp_ht > disp_max_y_pos_ && table_pass_ == 0) { /* update the height */ disp_max_y_pos_ = new_disp_ht; /* refigure the scrollbars */ win_->fmt_adjust_vscroll(); } } /* * Push the current margin settings and set new margins */ void CHtmlFormatter::push_margins(long left, long right) { /* make sure there's room on the stack */ if (margin_stack_depth_ >= MARGIN_STACK_DEPTH_MAX) { /* * simply note the error and forget the enclosing margins, but * go ahead and count the stack depth change anyway, since we'll * want to at least keep count as the blocks get closed */ parser_->log_error("too many nested blocks with margin changes"); } else { long left_delta, right_delta; /* * store the deltas from the current margins -- we want to store * the amount to add to the new margins to get back to the * current margins */ left_delta = margin_left_ - left; right_delta = margin_right_ - right; margin_stack_[margin_stack_depth_].set(left_delta, right_delta); } /* increase the stack depth */ ++margin_stack_depth_; /* set the new margins */ set_margins(left, right); } /* * Pop the margin stack, restoring the margins that were in effect just * before the last call to push_margins() */ void CHtmlFormatter::pop_margins() { /* decrease the nesting level */ --margin_stack_depth_; /* * if we got too deeply nested, we can't restore the margins because * we didn't save them; otherwise, restore them by applying the * deltas to the current margins */ if (margin_stack_depth_ < MARGIN_STACK_DEPTH_MAX) { long new_left, new_right; new_left = margin_left_ + margin_stack_[margin_stack_depth_].left_delta_; new_right = margin_right_ + margin_stack_[margin_stack_depth_].right_delta_; set_margins(new_left, new_right); } } /* * Set margins, and adjust the available line width for the change */ void CHtmlFormatter::set_margins(long left, long right) { /* adjust the available line width for the change in margins */ avail_line_width_ -= (left - margin_left_); avail_line_width_ -= (right - margin_right_); curpos_.x += (left - margin_left_); /* set the new margins */ margin_left_ = left; margin_right_ = right; /* temporary margins are not in effect */ temp_margins_ = FALSE; } /* * Set temporary margins. These margins will remain in effect until the * end of the current line, at which time the margins in effect prior to * this call will be restored. */ void CHtmlFormatter::set_temp_margins(long left, long right) { /* remember the current margins as deltas from the new margins */ margin_left_delta_nxt_ = margin_left_ - left; margin_right_delta_nxt_ = margin_right_ - right; /* set the new margins */ set_margins(left, right); /* note that we have temporary margins */ temp_margins_ = TRUE; } /* * Draw everything in a given area */ void CHtmlFormatter::draw(const CHtmlRect *area, int clip_lines, long *clip_y) { CHtmlDisp *cur; CHtmlRect curpos; long cur_ypos; long line_index; /* check line start records */ if (line_starts_->get_count() == 0) { /* no line starts - start at beginning of list */ line_index = 0; cur = disp_head_; cur_ypos = 0; } else { /* start at the first line containing the top of the area to draw */ line_index = line_starts_->find_by_ypos(area->top); cur = line_starts_->get(line_index); cur_ypos = line_starts_->get_ypos(line_index); } /* draw lines until we're out of view */ for (;;) { CHtmlDisp *nxt_line; long nxt_ypos; /* remember the next line and its position */ if (line_index + 1 < line_count_) { /* there's another line - get it */ nxt_line = line_starts_->get(line_index + 1); nxt_ypos = line_starts_->get_ypos(line_index + 1); } else { /* this is the last line */ nxt_line = 0; nxt_ypos = layout_max_y_pos_; } /* * if we're clipping lines, and this line doesn't entirely fit, * don't draw anything more */ if (clip_lines && nxt_ypos > area->bottom) { /* tell the caller where we started clipping */ if (clip_y) *clip_y = cur_ypos; /* *. .. but keep drawing - we don't actually clip out the * lines, but just note where clipping would have occurred */ } /* * draw the current line; stop if we reach the start of the next * line */ for ( ; cur != 0 && cur != nxt_line ; cur = cur->get_next_disp()) { /* draw it only if it intersects the display area horizontally */ curpos = cur->get_pos(); if (curpos.right >= area->left && curpos.left <= area->right) cur->draw(win_, sel_start_, sel_end_, FALSE); } /* move on to the next line */ ++line_index; cur_ypos = nxt_ypos; /* if the next line isn't in view, don't display it */ if (cur == 0 || nxt_ypos > area->bottom) break; } } /* * Invalidate links that are visible on the screen. This is used to * quickly redraw the window's links (and only its links) for a * short-term change in the link visibility state. This can be used to * implement a mode where links are highlighted only if a key is held * down. */ void CHtmlFormatter::inval_links_on_screen(const CHtmlRect *area) { CHtmlDisp *cur; CHtmlRect curpos; long line_index; /* check line start records */ if (line_starts_->get_count() == 0) { /* no line starts - start at beginning of list */ line_index = 0; cur = disp_head_; } else { /* start at the first line containing the top of the area to draw */ line_index = line_starts_->find_by_ypos(area->top); cur = line_starts_->get(line_index); } /* invalidate links until we're out of view */ for (;;) { CHtmlDisp *nxt_line; long nxt_ypos; /* remember the next line and its position */ if (line_index + 1 < line_count_) { /* there's another line - get it */ nxt_line = line_starts_->get(line_index + 1); nxt_ypos = line_starts_->get_ypos(line_index + 1); } else { /* this is the last line */ nxt_line = 0; nxt_ypos = layout_max_y_pos_; } /* * invalidate links in the current line; stop when we reach the * start of the next line */ for ( ; cur != 0 && cur != nxt_line ; cur = cur->get_next_disp()) cur->inval_link(win_); /* move on to the next line */ ++line_index; /* if the next line isn't in view, don't display it */ if (cur == 0 || nxt_ypos > area->bottom) break; } } /* * Get the height of the line at the given y position */ long CHtmlFormatter::get_line_height_ypos(long ypos) const { long line_index; long ypos_cur; long ypos_nxt; /* get the line at the given position */ line_index = line_starts_->find_by_ypos(ypos); /* get the y position of that line */ ypos_cur = line_starts_->get_ypos(line_index); /* get the y position of the next line */ if (line_index + 1 < line_count_) ypos_nxt = line_starts_->get_ypos(line_index + 1); else ypos_nxt = layout_max_y_pos_; /* * the height of the desired line is the difference in positions of * the desired line and the following line */ return ypos_nxt - ypos_cur; } /* * Get the position of a character, given the text offset in the * character array */ CHtmlPoint CHtmlFormatter::get_text_pos(unsigned long txtofs) const { long index; CHtmlDisp *disp; /* * Search the line starts table for the line containing the given * text offset; this won't find the exact item, but will quickly * narrow it down to a single line. If we don't find any index * entries, start at the first display item. */ index = line_starts_->find_by_txtofs(txtofs); disp = line_starts_->get(index); if (disp == 0) disp = disp_head_; /* search for the item starting at this line start */ return get_text_pos_list(disp, txtofs); } /* * Search the display list for an item containing a text offset, * starting at a particular point in the display list, and return the * position of the text in that item. */ CHtmlPoint CHtmlFormatter::get_text_pos_list(CHtmlDisp *start, unsigned long txtofs) const { CHtmlDisp *cur; /* scan the line for the particular item containing this offset */ for (cur = start ; cur ; cur = cur->get_next_disp()) { /* if this one is in range, it's the one we want */ if (txtofs >= cur->get_text_ofs() && (cur->get_next_disp() == 0 || txtofs < cur->get_next_disp()->get_text_ofs())) { /* return the position of the text within this item */ return cur->get_text_pos(win_, txtofs); } } /* didn't find it - return the next formatting position */ if (disp_tail_) { CHtmlRect r = disp_tail_->get_pos(); return CHtmlPoint(r.left, r.top); } else return curpos_; } /* * Find an item given a position */ CHtmlDisp *CHtmlFormatter::find_by_pos(CHtmlPoint pos, int exact) const { CHtmlDisp *cur; CHtmlDisp *curline; CHtmlDisp *nxtline; CHtmlDisp *best_inexact_match; CHtmlDisp *prv; long index; /* check if position is out of range above */ if (pos.y < 0) { /* * it's a negative y position - start with the first item if * they'll allow a non-exact match, otherwise there's nothing * that matches */ if (exact) return 0; else return get_first_disp(); } /* if there are no line starts, use the entire list */ if (line_starts_->get_count() == 0) { /* no line starts - search the entire list */ index = 0; curline = get_first_disp(); nxtline = 0; } else { /* find the line containing the y position */ index = line_starts_->find_by_ypos(pos.y); curline = line_starts_->get(index); /* note the first item in the next line */ nxtline = (index + 1 >= line_starts_->get_count() ? 0 : line_starts_->get(index + 1)); } /* search the line for an item containing the given point */ for (prv = 0, best_inexact_match = 0, cur = curline ; cur != 0 && cur != nxtline ; prv = cur, cur = cur->get_next_disp()) { CHtmlRect curpos; /* if this item contains the given point, it's the one */ curpos = cur->get_pos(); if (curpos.contains(pos)) { /* * If this display item ignores clicks, go on to the next * one. Some display items are overlaid on other items, * such as table cells and their contents; in these cases, * the "background" item will ignore clicks so that we move * on to the "foreground" objects. */ if (cur->is_in_background()) continue; /* this is the one */ return cur; } /* * if we'll allow non-exact matches, and we're to the left of * or above this item, it could be an inexact match */ if (!exact && cur->allow_inexact_hit() && (pos.y < curpos.top || (pos.x < curpos.left && pos.y >= curpos.top && pos.y <= curpos.bottom))) { /* * if we don't have a possible inexact match yet, note this * one, since the only one is automatically the best; if we * have one already, take this one if it's above the current * best inexact match */ if (best_inexact_match == 0) { /* this is the first one that could be an inexact match */ best_inexact_match = cur; } else { CHtmlRect matchpos; /* get the position of the best fitting one so far */ matchpos = best_inexact_match->get_pos(); /* * if it's above the last match, or it's left of the * last match but not below the last match, use it */ if (curpos.top < matchpos.top || (curpos.left < matchpos.left && curpos.top < matchpos.bottom)) best_inexact_match = cur; } } } /* * We didn't find a match. If they wanted an exact match, return * null; if they only wanted the nearest item, return the first item * on the next line. */ if (exact) { /* * We required exact match, and we didn't find one. As a last * resort, try looking for a
tag that covers this area. */ return find_div_by_pos(pos); } else if (best_inexact_match != 0) { /* * exact match is not required, and we found a near fit; return * the best near fit we found */ return best_inexact_match; } else { /* * Exact match is not required; return the last item on the * line, if we found one, otherwise return the first item on * next line; if this is the last line, return the start of the * current (i.e., last) line */ if (prv != 0) return prv; else if (nxtline != 0) return nxtline; else return disp_tail_; } } /* * Find the enclosing
, if any, given spatial coordinates within the * document. */ CHtmlDispDIV *CHtmlFormatter::find_div_by_pos(CHtmlPoint pos) const { /* get our text offste, and find the item that way */ return find_div_by_ofs(find_textofs_by_pos(pos), pos.y); } /* * Find the enclosing
, if any, given a text offset. */ CHtmlDispDIV *CHtmlFormatter::find_div_by_ofs( unsigned long ofs, long ypos) const { int lo, hi; /* * Search the
table for a match. The
table is always in * ascending order of text offset, so we can do a binary search. */ for (lo = 0, hi = div_list_.get_count() - 1 ; ; ) { int cur; CHtmlDispDIV *div; CHtmlDispDIV *par; /* stop if we've exhausted the list */ if (lo > hi) return 0; /* split the difference */ cur = lo + (hi - lo)/2; /* get this entry */ div = (CHtmlDispDIV *)div_list_.get_ele(cur); /* * DIVs can be nested, which means that the ranges aren't strictly * monotonic, which means that we can't search for ranges with a * simple binary search. However, we can search for *top-level* * DIV ranges with a binary search, because top-level DIVs are * non-overlapping and monotonic. So, only consider top-level * DIVs. */ while ((par = div->get_parent_div()) != 0) div = par; /* if we've gone below the bounds of the range, readjust the range */ if (cur < lo) lo = cur; /* check to see if we're high or low */ if (ofs < div->get_start_ofs()) { /* this one's too high - continue searching the lower half */ hi = (cur == hi ? hi - 1 : cur); } else if (ofs > div->get_end_ofs()) { /* this one's too low - continue searching the upper half */ lo = (cur == lo ? lo + 1 : cur); } else { CHtmlDisp *nxt; size_t chi_idx; /* * find the actual index of 'div' - the index 'cur' might * currently refer to a child of 'div' since we always seek the * top-level item at each point */ while (cur != 0 && (CHtmlDispDIV *)div_list_.get_ele(cur) != div) --cur; /* * This DIV covers the area, but make sure that we're within * the vertical extent of the actual contents of the DIV. We * could be to the right of the last display item above the * DIV, in which case we're in the right text-offset range but * not within the actual visual area of the DIV. * * So, get the next item, and make sure our y position is * greater than (i.e., visually below) the top of the next * item. If not, we're not within this DIV after all; and * since we only scan top-level DIVs, we can be sure there's * not another one that contains the text area. Therefore, * we're simply not in a DIV at all. */ nxt = div->get_next_disp(); if (nxt == 0 || nxt->get_pos().top > ypos) return 0; /* * Okay, we found the top-level DIV that contains this * position. There's one more thing to do: we need to check to * see if any child of this DIV contains the point. If so, we * want to return the innermost child that contains the point. * * We can find the child as follows. Scan ahead and look at * subsequent items, stopping when we reach another top-level * item. (All of our children will be after us and before the * next top-level item, so this will find all of our children.) * Only consider our *direct* children. For each child we * find, if that child contains the position, start this * process over with that child's direct children. As with * top-level items, a child item's children are after the child * and before the next sibling - i.e., the next item with the * same parent. On any pass, if we don't find children * containing the position, return the current item. */ child_scan: /* scan the current DIV's children */ for (par = div->get_parent_div(), chi_idx = cur + 1 ; chi_idx < div_list_.get_count() ; ++chi_idx) { CHtmlDispDIV *chi; /* get this child element */ chi = (CHtmlDispDIV *)div_list_.get_ele(chi_idx); /* * if this is a sibling of 'div', we're done scanning * children of 'div' */ if (chi->get_parent_div() == par) break; /* if this isn't a direct child of 'div', skip it */ if (chi->get_parent_div() != div) continue; /* * does this child contain the text offset and vertical * position? */ if (ofs >= chi->get_start_ofs() && ofs <= chi->get_end_ofs() && (nxt = chi->get_next_disp()) != 0 && ypos >= nxt->get_pos().top) { /* * This child contains the position, so we want to * return this child rather than the parent. However, * we need to continue the scan into the child. Simply * reset everything with the main find set to this * child, and start the child scan again, this time * with this item's children. */ cur = chi_idx; div = chi; goto child_scan; } } /* * We didn't find any children of 'div' that contain the * position, so 'div' is definitely the one - return it. */ return div; } } } /* * Get the text offset given a position */ unsigned long CHtmlFormatter::find_textofs_by_pos(CHtmlPoint pos) const { CHtmlDisp *disp; unsigned long ofs; unsigned long max_ofs; /* * Find the display item containing the position. Don't require an * exact match; if we can't find anything containing the given * position, use the nearest item. */ if ((disp = find_by_pos(pos, FALSE)) == 0) return 0; /* note the maximum text offset */ max_ofs = get_text_ofs_max(); /* * As a special case, if we're out of range above or below the * entire text stream, go to the beginning of the first item or end * of the last item, respectively */ if (pos.y < 0) return 0; else if (disp == disp_tail_ && pos.y > disp->get_pos().bottom) return max_ofs; /* find the offset of the given position */ ofs = disp->find_textofs_by_pos(win_, pos); return (ofs > max_ofs ? max_ofs : ofs); } /* * Find an item by the debugger source file position */ CHtmlDisp *CHtmlFormatter::find_by_debugsrcpos(unsigned long linenum) const { long index; /* if there are no lines, there's nothing to do */ if (line_starts_->get_count() == 0) return 0; /* if the file position is zero, start at the beginning */ if (linenum == 0) return disp_head_; /* find the line at the given position */ index = line_starts_->find_by_debugsrcpos(linenum); return line_starts_->get(index); } /* * Find a line number given the debugger source file position */ long CHtmlFormatter::findidx_by_debugsrcpos(unsigned long linenum) const { /* if there are no lines, there's nothing to do */ if (line_starts_->get_count() == 0) return 0; /* if the file position is zero, start at the beginning */ if (linenum == 0) return 0; /* find the line at the given position and return the line index */ return line_starts_->find_by_debugsrcpos(linenum); } /* * Find an item given the line number */ CHtmlDisp *CHtmlFormatter::get_disp_by_linenum(long linenum) const { /* if there aren't any lines, there's nothing to return */ if (line_starts_->get_count() == 0) return 0; /* if it's past the last line, return the last line */ if (linenum >= line_starts_->get_count()) linenum = line_starts_->get_count() - 1; /* get the item from the line starts table */ return line_starts_->get(linenum); } /* * Get the character set for the text at the given text offset. */ oshtml_charset_id_t CHtmlFormatter::get_charset(unsigned long ofs) const { /* look up the display item at the given offset */ CHtmlDisp *disp = find_by_txtofs(ofs, TRUE, TRUE); /* get the character set from the display item, if we foudn one */ oshtml_charset_id_t cs; if (disp != 0 && disp->get_charset(cs)) return cs; /* that didn't work - use the default character set for the window */ CHtmlSysWin *win; CHtmlSysWinGroup *wingroup; if ((win = get_win()) != 0 && (wingroup = win->get_win_group()) != 0) return wingroup->get_default_win_charset(); /* still no luck - use the system default character set */ return os_get_default_charset(); } /* * Find an item by text offset */ CHtmlDisp *CHtmlFormatter::find_by_txtofs(unsigned long ofs, int use_prv, int use_nxt) const { CHtmlDisp *cur; CHtmlDisp *prv; CHtmlDisp *nxtline; long index; /* if the text offset is zero, start at the beginning */ if (ofs == 0) return disp_head_; /* use the line starts if possible; if not, search the entire list */ if (line_starts_->get_count() != 0) { /* find the line containing the text offset in the line starts */ index = line_starts_->find_by_txtofs(ofs); cur = line_starts_->get(index); /* note the first item in the next line */ nxtline = (index + 1 >= line_starts_->get_count() ? 0 : line_starts_->get(index + 1)); } else { /* no line starts - search the entire list */ index = 0; cur = disp_head_; nxtline = 0; } /* search the line for an item containing the given point */ for (prv = 0 ; cur != 0 && cur != nxtline ; prv = cur, cur = cur->get_next_disp()) { /* if this item contains the given point, it's the one */ if (cur->contains_text_ofs(ofs) || (use_nxt && cur->get_text_ofs() > ofs) || (use_prv && (cur->get_next_disp() == 0 || cur->get_next_disp()->get_text_ofs() > ofs))) return cur; /* * as a special case, if this item is exactly at the text offset * (which means that this item contains zero length text, since * otherwise we would have matched it), and the next line is not * at the same offset, and the offset we're looking for is right * at this item, return this item if we're allowed any sort of * approximation (i.e., either use_nxt or use_prv is set) */ if (cur->get_text_ofs() == ofs && (cur->get_next_disp() == 0 || cur->get_next_disp()->get_text_ofs() != ofs) && (use_nxt || use_prv)) return cur; } /* * if we didn't find anything, use either the last item on the * previous line or the first item on the next line, depending on * which way they asked us to go */ if (use_prv) return prv; else if (use_nxt) { /* * if the last item we found is the last item, and starts at the * desired offset, it must be a zero-length item; since there's * no next item, it's the desired item */ if (prv != 0 && prv->get_next_disp() == 0 && prv->get_text_ofs() == ofs) return prv; /* return start of the next line */ return nxtline; } /* we didn't find a match */ return 0; } /* * Set the selection range */ void CHtmlFormatter::set_sel_range(unsigned long start, unsigned long end) { unsigned long l1, l2, r1, r2; /* make sure they're in canonical order - start <= end */ if (start > end) { long tmp = end; end = start; start = tmp; } /* * Invalidate the area outside the intersection of the old range * and the new range, since that area is changing it's highlighting * state. First, calculate l1 (the leftmost starting point), l2 * (the rightmost starting point), r1 (the leftmost ending point), * and r2 (the rightmost ending point). */ if (start <= sel_start_) l1 = start, l2 = sel_start_; else l1 = sel_start_, l2 = start; if (end <= sel_end_) r1 = end, r2 = sel_end_; else r1 = sel_end_, r2 = end; /* * Invalidate from the leftmost edge (which is always l1) to the * further left of l2 and r1 */ inval_offset_range(l1, (l2 < r1 ? l2 : r1)); /* * Invalidate from the further right of r1 and l2 to the rightmost * edge (which is always r2) */ inval_offset_range((l2 > r1 ? l2 : r1), r2); /* remember the new range */ sel_start_ = start; sel_end_ = end; } /* * Set the selection range given the starting and ending coordinates */ void CHtmlFormatter::set_sel_range(CHtmlPoint start, CHtmlPoint end, unsigned long *startofsp, unsigned long *endofsp) { unsigned long startofs; unsigned long endofs; /* find the text offsets of the starting and ending points */ startofs = find_textofs_by_pos(start); if (end.x == start.x && end.y == start.y) endofs = startofs; else endofs = find_textofs_by_pos(end); /* set the selection range for the given offsets */ set_sel_range(startofs, endofs); /* return the start and end offsets if desired */ if (startofsp) *startofsp = startofs; if (endofsp) *endofsp = endofs; } /* * Increment or decrement a text array address to the next or previous * word boundary. If dir is positive, we'll increment the pointer, * otherwise we'll decrement the pointer. */ unsigned long CHtmlFormatter::find_word_boundary( unsigned long start, int dir) const { /* note the character type at the starting position */ oshtml_charset_id_t cs = get_charset(start); int start_is_word = is_word_char_at_ofs(cs, start); /* keep going until we cross a word boundary or run out of text */ unsigned long cur = start; unsigned long nxt; for ( ;; ) { /* * Get the tag containing the current character if moving forward * or the previous byte if moving backwards. Once we have the tag, * use it to get the character set for the text containing the * character we're traversing - this is necessary to ensure proper * traversal of multi-byte characters. */ /* move the pointer in the appropriate direction */ if (dir > 0) { /* going forward - increment the pointer */ nxt = inc_text_ofs_char(cs, cur); if (nxt > get_text_ofs_max()) break; /* get the character set context for the new character */ cs = get_charset(nxt); } else { /* going backwards - if already at the beginning, stop */ if (cur == 0) break; /* * get the character set context for the previous character * (which necessarily contains the previous byte, so we can * just ask for the context for the previous byte - this avoids * a catch-22 where we have to know the previous character set * to find the bounds of the previous character, for which we * need to know the previous character set...) */ cs = get_charset(dec_text_ofs(cur)); nxt = dec_text_ofs_char(cs, cur); } /* if we've reached the beginning or end of the text, stop here */ if (nxt == cur) break; /* get the character type at the new position */ int cur_is_word = is_word_char_at_ofs(cs, nxt); /* if we crossed a boundary, stop */ if (cur_is_word != start_is_word) break; /* move to the next character */ cur = nxt; } /* return the final position */ return (dir > 0 ? nxt : cur); } /* * Given a text offset, get the extent of the word containing the * offset */ void CHtmlFormatter::get_word_limits(unsigned long *start, unsigned long *end, unsigned long txtofs) const { /* find the start and end boundaries */ *start = find_word_boundary(txtofs, -1); *end = find_word_boundary(txtofs, 1); } /* * Given a text offset, get the extent of the line containing the * offset. If the offset is in the current input line, we'll get the * limits of the input line, not including any prompt that appears on * the same line. */ void CHtmlFormatter::get_line_limits(unsigned long *start, unsigned long *end, unsigned long txtofs) const { CHtmlDisp *cur; CHtmlDisp *lstart; long index; /* if we have no lines, there's nothing to do */ if (line_starts_->get_count() == 0) { *start = *end = 0; return; } /* find the start of this line */ index = line_starts_->find_by_txtofs(txtofs); cur = line_starts_->get(index); /* * We may not have a line start record at the start of the line with * the target offset, since we don't keep line starts for every * line; hence, we need to search for the start of the target line. * Scan from the starting display item to the display item * containing the text offset we're looking for, noting line * changes. */ for (lstart = cur ; cur != 0 ; cur = cur->get_next_disp()) { /* * if this item is on a different line than the start of the * current line, it's the start of a new line */ if (cur->get_line_id() != lstart->get_line_id()) lstart = cur; /* if this item contains our text offset, we can stop looking */ if (cur->contains_text_ofs(txtofs)) break; /* * if the next item is above our target offset, consider the * point to be the current item */ if (cur->get_next_disp() != 0 && cur->get_next_disp()->get_text_ofs() > txtofs) break; } /* the start offset is the offset of the first item on the line */ *start = (lstart != 0 ? lstart->get_text_ofs() : 0); /* * Now keep scanning until we find the start of the next line */ for ( ; cur != 0 ; cur = cur->get_next_disp()) { /* * if this item is on a different line than the previous one, * we're done */ if (cur->get_line_id() != lstart->get_line_id()) break; } /* * if there's another line, the end offset is the offset of the * first item on the next line, otherwise select to the end of the * text */ if (cur != 0) *end = cur->get_text_ofs(); else *end = get_text_ofs_max(); } /* * Invalidate a range of text given by offsets */ void CHtmlFormatter::inval_offset_range(long l, long r) { CHtmlDisp *displ; // CHtmlDisp *dispr; CHtmlDisp *dispmax; CHtmlDisp *cur; long index; /* if the offsets match, do nothing */ if (l == r) return; /* find the items bounding the given offsets */ displ = find_by_txtofs(l, TRUE, FALSE); // dispr = find_by_txtofs(r, FALSE, TRUE); /* * Find the next line after the ending text offset. This gives us a * reliable upper bound for how far we have to go. We can't merely * stop when we reach a text offset higher than our ending text * offset, because we might have non-monotonic offsets due to tables * or other multi-column areas. To allow for this possibility, we'll * keep going until we reach the start of the next line. */ index = line_starts_->find_by_txtofs(r); if (index + 1 < line_count_) { /* stop at the first item from the next line */ ++index; dispmax = line_starts_->get(index); } else { /* there is no next line, so look at all remaining items */ dispmax = 0; } /* * invalidate all of the items within range, starting with the first * item and continuing until we reach the start of the next line */ for (cur = displ ; cur != 0 ; cur = cur->get_next_disp()) { /* invalidate this item if it's within range */ if (cur->overlaps_text_range(l, r)) cur->inval_range(win_, l, r); /* if we've reached the start of the next line, stop scanning */ if (cur == dispmax) break; } } /* * Invalidate the whole window */ void CHtmlFormatter::inval_window() { /* if we have a window, invalidate it */ if (win_ != 0) { CHtmlRect rc; /* invalidate the entire window */ rc.set(0, 0, HTMLSYSWIN_MAX_RIGHT, HTMLSYSWIN_MAX_BOTTOM); win_->inval_doc_coords(&rc); } } /* * Heap allocator. The formatter provides a simple heap for allocating * display items. Only display items should be allocated here, since an * item allocated here is automatically deleted when we start * reformatting, and can't be removed from memory any other way. */ void *CHtmlFormatter::heap_alloc(size_t siz) { void *ret; /* * if they're asking for more than the maximum page size, we must * increase the unit size to satisfy the request */ if (siz > heap_alloc_unit_) { size_t i; /* increase the page size */ heap_alloc_unit_ = siz; /* * to avoid a big pile of pages that we can't use, delete all of * the pages that we've previously allocated after the current * page -- we'll reallocate them as needed eventually, but at * the new larger size */ for (i = heap_page_cur_ + 1 ; i < heap_pages_alloced_ ; ++i) th_free(heap_pages_[i].mem); heap_pages_alloced_ = heap_page_cur_ + 1; } /* keep going until we satisfy the request */ for (;;) { /* if there's room in the current page, allocate out of it */ if (heap_page_cur_ < heap_pages_alloced_ && heap_page_cur_ofs_ + siz <= heap_pages_[heap_page_cur_].siz) { /* there's room in this page - return pointer to next free byte */ ret = (void *)(heap_pages_[heap_page_cur_].mem + heap_page_cur_ofs_); /* advance past the new block */ heap_page_cur_ofs_ += os_align_size(siz); /* return the pointer */ return ret; } /* * We've exhausted the current page, so we need to move on to * the next one. If we haven't allocated a page at the new * slot, do so now. */ if (heap_page_cur_ + 1 < heap_pages_alloced_) { /* * we've already allocated a page here - start over at the * new page */ ++heap_page_cur_; heap_page_cur_ofs_ = 0; /* go back and try with this new page */ continue; } /* * If necessary, extend the page array */ if (heap_page_cur_ + 1 >= heap_pages_max_) { size_t alloc_size; /* allocate space for another bunch of pages */ heap_pages_max_ += 20; alloc_size = heap_pages_max_ * sizeof(CHtmlFmt_heap_page_t); /* allocate or reallocate the page array */ if (heap_pages_ == 0) { /* allocate the initial page array */ heap_pages_ = (CHtmlFmt_heap_page_t *)th_malloc(alloc_size); /* start at the first page */ heap_page_cur_ = 0; } else { /* reallocate the current page array */ heap_pages_ = (CHtmlFmt_heap_page_t *) th_realloc(heap_pages_, alloc_size); /* move on to the next page */ ++heap_page_cur_; } } else { /* there's room in the array - move on to the next page */ ++heap_page_cur_; } /* allocate a new page */ heap_pages_[heap_pages_alloced_].siz = heap_alloc_unit_; heap_pages_[heap_pages_alloced_].mem = (unsigned char *) th_malloc(heap_alloc_unit_); ++heap_pages_alloced_; /* start at the beginning of the new page */ heap_page_cur_ofs_ = 0; } } /* * Hash table entry for an image map */ class CHtmlFmtHashEntryMap: public CHtmlHashEntryCI { public: CHtmlFmtHashEntryMap(const textchar_t *str, size_t len, CHtmlFmtMap *map) : CHtmlHashEntryCI(str, len, TRUE) { map_ = map; } ~CHtmlFmtHashEntryMap() { /* delete my map */ delete map_; } /* my map */ CHtmlFmtMap *map_; }; /* * Begin a map */ void CHtmlFormatter::begin_map(const textchar_t *map_name, size_t name_len) { /* make sure the previous map is closed */ end_map(); /* create a new map */ cur_map_ = new CHtmlFmtMap(map_name, name_len); } /* * end the current map */ void CHtmlFormatter::end_map() { CHtmlFmtHashEntryMap *entry; /* if there's no map, there's nothing to do */ if (cur_map_ == 0) return; /* if there's already a map with this name, replace it */ entry = (CHtmlFmtHashEntryMap *) map_table_->find(cur_map_->get_name(), get_strlen(cur_map_->get_name())); if (entry != 0) { /* delete the old map in the entry */ delete entry->map_; /* replace it with the new map */ entry->map_ = cur_map_; } else { /* create a new entry */ entry = new CHtmlFmtHashEntryMap(cur_map_->get_name(), get_strlen(cur_map_->get_name()), cur_map_); /* add it to the table */ map_table_->add(entry); } /* * the new map is finished and has been added to the table, so * forget about it in the "under construction" area */ cur_map_ = 0; } /* * Get a map given a map name */ CHtmlFmtMap *CHtmlFormatter::get_map(const textchar_t *mapname, size_t namelen) { CHtmlFmtHashEntryMap *entry; /* look up the map in the hash table */ entry = (CHtmlFmtHashEntryMap *)map_table_->find(mapname, namelen); /* if we found the entry, return the map */ return (entry != 0 ? entry->map_ : 0); } /* * add a hot spot to the current map */ void CHtmlFormatter::add_map_area(HTML_Attrib_id_t shape, CHtmlUrl *href, const textchar_t *alt, size_t altlen, const CHtmlTagAREA_coords_t *coords, int coord_cnt, int append, int noenter) { CHtmlDispLink *link; CHtmlFmtMapZone *zone; /* if there's no open map, we can't add a map area */ if (cur_map_ == 0) return; /* * Set up a link for the hot spot and add it to the display list. * Note that we use the direct item adder, not the link item adder, * because we do not want to open a link range -- this is a * single-use link that doesn't contain any other display items. */ link = new (this) CHtmlDispLink(FALSE, append, noenter, href, alt, altlen); add_disp_item(link); /* set up an appropriate zone object based on the shape setting */ switch(shape) { case HTML_Attrib_rect: zone = new CHtmlFmtMapZoneRect(coords, link); break; case HTML_Attrib_circle: zone = new CHtmlFmtMapZoneCircle(coords, link); break; case HTML_Attrib_poly: zone = new CHtmlFmtMapZonePoly(coords, coord_cnt, link); break; default: /* other cases should never occur; ignore them if they do */ break; } /* add the new zone to the current map */ cur_map_->add_zone(zone); } /* * Begin a table */ CHtmlTagTABLE *CHtmlFormatter::begin_table(CHtmlTagTABLE *table_tag, CHtmlDispTable *table_disp, CHtmlPoint *enclosing_table_pos, CHtmlTagCAPTION *caption_tag) { CHtmlTagTABLE *enclosing_table; long y_ofs; /* remember the enclosing table, so we can return it to the caller */ enclosing_table = current_table_; /* remember the new table tag as the current table */ current_table_ = table_tag; /* presume we won't need a y offset for the contents as we format */ y_ofs = 0; /* check which pass over the table we're running */ switch(table_pass_) { case 0: /* * If we're on the first line (i.e., there are no line starts), * insert a zero-height line before the start of the table. */ if (line_count_ == 0) { /* * add a blank item to anchor the first line, then start a * new line -- make both of these zero-height breaks, since * we only need something to serve as a placeholder in the * list and don't want to actually take up any screen space */ add_disp_item(new (this) CHtmlDispBreak(0)); add_disp_item_new_line(new (this) CHtmlDispBreak(0)); } /* * We're not yet doing a table, so this is an outermost (not * nested) table. Prepare for the first pass, which will simply * measure the contents of the table without actually setting * any positions. We'll go through the motions of formatting * contents during this pass, but we won't break any lines and * we won't save any of the results of our work apart from the * statistics on how large the contents of the various cells * are. So, we'll remember the state of the display list and * heap as it was prior to the table, so that we can restore all * of this state when the first pass is through. */ pre_table_heap_page_cur_ = heap_page_cur_; pre_table_heap_page_cur_ofs_ = heap_page_cur_ofs_; pre_table_disp_tail_ = disp_tail_; pre_table_line_head_ = line_head_; pre_table_line_count_ = line_count_; pre_table_line_id_ = line_id_; pre_table_line_starts_count_ = line_starts_->get_count(); pre_table_div_list_count_ = div_list_.get_count(); pre_table_cur_div_ = cur_div_; pre_table_max_line_width_ = max_line_width_; pre_table_avail_line_width_ = avail_line_width_; pre_table_line_spacing_ = line_spacing_; pre_table_last_was_newline_ = last_was_newline_; /* we're now in pass 1 */ table_pass_ = 1; /* * In case we try to refresh the display while we're working on the * table, "hide" everything in the table under construction by * pushing it all off the screen below. */ if (win_ != 0) y_ofs = win_->get_disp_height() * 2; break; case 1: /* * We're doing the first pass through a table, so this must be a * nested table. There's nothing extra to do at this point. */ break; case 2: /* * If this is a floating table, put it at the start of a new line. * This is unnecessary for regular tables, since they're formatted * as paragraph breaks anyway, but for floating tables we need to * make sure we're at the start of a line in order for everything * to line up properly. Note that we need to do this before * calculating the table size information, so that the table is * initially positioned under the same conditions as its contents, * which will always effectively start on new lines since each new * cell is considered a new line. */ if (table_tag->is_floating()) { CHtmlRect pos; /* * momentarily restore the enclosing table as the active * table, so that we can end any margin items at the enclosing * level before starting this new table */ current_table_ = enclosing_table; /* * push a snapshot of the current environment, in case it * changes - we'll need to restore this information when we * finish with the table so that we can resume formatting the * line under construction under the same conditions */ push_table_env(); /* start a new line here, in the context of the enclosing table */ start_new_line(table_disp); /* * make sure that display item is operating with the full * window width available, in case we changed the margins * because of floating items */ table_disp->set_win_width(win_->get_disp_width() - margin_left_ - margin_right_); /* restore the current table tag */ current_table_ = table_tag; } /* * We're doing the second pass through a table. All of the * horizontal metrics of the table are known on this pass, so * tell the display item to calculate the actual sizes of the * columns and thus the actual size of the table. */ table_disp->calc_table_size(this, table_tag); break; } /* * If the caption is to be set at the top of the table, format its * contents here. */ check_format_caption(caption_tag, table_disp, TRUE); /* * Add the display item to the display list directly (we can bypass the * normal line formatting, since a table is never part of a line - * tables are vertical blocks). */ add_to_disp_list(table_disp); /* * If there's nothing in the line list yet, make the new table the * first thing on the line. This ensures that we'll position it * properly when we close the cell. * * Likewise, if it's a floating table, it's the start of its own line. */ if (line_head_ == 0 || table_tag->is_floating()) line_head_ = table_disp; /* * Remember the output position at the start of the table, since we'll * want to come back here at the end of the table. Let the caller * remember where the enclosing table began, so that we can restore * the enclosing table start position when we end this table. */ *enclosing_table_pos = pre_table_curpos_; pre_table_curpos_ = curpos_; /* adjust the output position for the "hiding" y offset, if needed */ curpos_.y += y_ofs; /* return the enclosing table */ return enclosing_table; } /* * End a table */ void CHtmlFormatter::end_table(CHtmlDispTable *table_disp, CHtmlTagTABLE *enclosing_table, const CHtmlPoint *enclosing_table_pos, CHtmlTagCAPTION *caption_tag) { CHtmlTagTABLE *ending_table; /* * If there is no current table, there's nothing to do. This can * happen when we try to exit formatting of a table incorrectly or * prematurely; if we just ignore it, we will usually come back and try * again at a more appropriate time later. */ if (current_table_ == 0) return; /* remember the current table as long as we're working on it */ ending_table = current_table_; /* restore the enclosing table as the current table */ current_table_ = enclosing_table; switch(table_pass_) { case 1: /* * First pass. If there's an enclosing table, we simply need to * restore the enclosing table as the current table; otherwise, * we need to run table formatting pass 2. Note that, in the * case of a nested table, running the second pass on the * enclosing table will also run the second pass on all nested * tables; furthermore, we can't run the second pass on embedded * tables until we've finished pass 1 on the enclosing table, * because the metrics of the embedded tables depend on the * metrics of the enclosing table, which aren't known until the * end of pass 1 on the enclosing table. Therefore, we can't * run pass 2 on embedded tables individually; we can only run * pass 2 when we've finished pass 1 on the outermost table. */ if (enclosing_table == 0) { /* time for pass two */ run_table_pass_two(ending_table); /* * our table's display item will have changed, since we will * have reformatted the table on the second pass - get the new * table display item from the table tag */ table_disp = ending_table->get_disp(); } else { /* * insert the embedded table's caption if appropriate, to * ensure that it's counted in the sizes of the embedded * items */ check_format_caption(caption_tag, table_disp, FALSE); /* restore starting position of enclosing table */ pre_table_curpos_ = *enclosing_table_pos; } /* done */ break; case 2: /* * Second pass. Tell the table to calculate the row sizes, then * set the final vertical positions of the rows. Note that the * operations must be performed in sequence, since we must * compute not only the main table's height, but also the * heights of any nested tables, before we can even begin * setting final row positions. */ ending_table->compute_table_height(); ending_table->set_row_positions(this); /* * Insert the caption, if it's meant to be at the bottom of the * table */ check_format_caption(caption_tag, table_disp, FALSE); /* * If it's a floating item, remove it from the stream and add it * back in for deferred placement. */ if (ending_table->is_floating()) { CHtmlDisp *sub_head; /* * Pull out everything after the table's initial item as a * separate list; this gives us the list of all of the display * items within the table. We don't defer this list in the * usual way - we instead attach it to the table, so that the * table can add it to the format list when the table itself * is formatted. * * We don't want to defer the table contents, because they're * not floating items themselves - they're just contained in a * floating item. */ sub_head = table_disp->get_next_disp(); /* detach the contents list from the table item itself */ table_disp->clear_next_disp(); /* attach the contents list to the table as its sublist */ table_disp->set_contents_sublist(sub_head); /* * restore the formatting environment information we saved at * the start of pass 2 for this table */ pop_table_env(); /* add the table back to the list as a deferred item */ add_disp_item_deferred(table_disp); } else { long table_right; /* * the table is added to its own vertical space, so check to * see if we're at the end of any floating area */ check_flow_end(); /* * if this leaves us at the outermost table level (i.e., we * just finished with a top-level table), adjust the horizontal * extent according to the table's position and width */ table_right = table_disp->get_pos().right; if (current_table_ == 0 && table_right > outer_max_line_width_) { /* remember the outer-level max line width */ outer_max_line_width_ = table_right; /* adjust the window's horizontal scrollbar */ win_->fmt_adjust_hscroll(); } /* * Now that we have the final table position, invalidate its * entire area. We need to invalidate the area of the table * itself, because there might be empty space (for borders, * spacing, and padding) that cells will have left untouched, * but which we nonetheless have to draw to ensure our * background is displayed properly. */ CHtmlRect pos = table_disp->get_pos(); win_->inval_doc_coords(&pos); } /* done with pass 2 */ break; } } /* * Receive information on the total size of the table. After we've * formatted the table, we'll use this to set the position of the next * item after the table. */ void CHtmlFormatter::end_table_set_size(CHtmlTagTABLE * /*enclosing_table*/, long height, const CHtmlPoint *enclosing_table_pos) { /* * set the current position to the position at the start of the * table offset vertically by the height of the table. */ curpos_ = pre_table_curpos_; curpos_.y += height; /* set the maximum y position based to the bottom of the table */ layout_max_y_pos_ = curpos_.y; /* make the display height at least the layout height */ if (layout_max_y_pos_ > disp_max_y_pos_) disp_max_y_pos_ = layout_max_y_pos_; /* restore the position of the enclosing table */ pre_table_curpos_ = *enclosing_table_pos; } /* * Format the caption tag if we're at the proper point in the table. * 'top' is true if we're at the top of the table, false if at the * bottom. This routine should be called once just before starting the * table and again just after finishing it, to ensure that the caption * is inserted at the proper point. */ void CHtmlFormatter::check_format_caption(CHtmlTagCAPTION *caption_tag, CHtmlDispTable *table_disp, int top) { /* * if we have a caption, and we're at the right point, format the * caption */ if (caption_tag != 0 && (caption_tag->is_at_top_of_table() != 0) == (top != 0)) { CHtmlRect pos; long left, right; /* get margins for the table */ pos = table_disp->get_pos(); left = pos.left; right = win_->get_disp_width() - (left + (pos.right - pos.left)); /* * offset everything by the left margin if we're at the top of * the table, since we haven't set the table display item's * final line position yet */ if (top) { left += margin_left_; right -= margin_left_; } /* set the margins */ push_margins(left, right); /* format the caption */ caption_tag->format_caption(win_, this); /* restore outer margins */ pop_margins(); } } /* * Run the second table formatting pass on the current table */ void CHtmlFormatter::run_table_pass_two(CHtmlTagTABLE *table_tag) { CHtmlTag *tag; CHtmlTag *post_table_tag; /* * First, delete the display list that resulted from the first table * formatting pass. The first pass was a measuring pass; we didn't * know how to lay out the table, so the display list that resulted * from the first pass is not correct. Restore the display list and * formatter heap to the state they were in prior to starting the * table. */ if (pre_table_disp_tail_ != 0) CHtmlDisp::delete_list(pre_table_disp_tail_->get_next_disp()); heap_page_cur_ = pre_table_heap_page_cur_; heap_page_cur_ofs_ = pre_table_heap_page_cur_ofs_; disp_tail_ = pre_table_disp_tail_; if (disp_tail_ == 0) { /* there was nothing in the display list before the table */ disp_head_ = 0; } else { /* clear the 'next' pointer of the previous item in the list */ disp_tail_->clear_next_disp(); } line_head_ = pre_table_line_head_; line_count_ = pre_table_line_count_; line_id_ = pre_table_line_id_; line_starts_->restore_count(pre_table_line_starts_count_); div_list_.trunc(pre_table_div_list_count_); cur_div_ = pre_table_cur_div_; max_line_width_ = pre_table_max_line_width_; avail_line_width_ = pre_table_avail_line_width_; /* go back to the position of the start of the table */ curpos_ = pre_table_curpos_; last_was_newline_ = pre_table_last_was_newline_; line_spacing_ = pre_table_line_spacing_; /* * Begin pass two */ table_pass_ = 2; /* * Run through the format tag list again, starting with the first tag * in the table and continuing until we reach the current tag. * * The behavior of tag formatting on this pass will be largely the same * as it was on the first pass, except that the table tags will see * that they're in pass two, and so will format their contents at the * proper sizes. So, the results of this second pass will be directly * usable for displaying. In addition, since we're in pass two, we * won't try to run pass two again when we finish with the table. * * Note that we want to format tags until we get to the present * curtag_, and then format that tag as well - the enclosing tag * scanning loop will have last set curtag_ to the final tag within the * table, and clearly we need to format that tag again before we * finish. * * However, there's one special case to worry about. If the table was * unclosed on the last formatting round, and has just been closed, * we'll format the table as a side effect of checking for more tags * (via more_to_do()), and in this case curtag_ will still be null from * the last (partial) formatting round. So if we waited to reach * curtag_, we'd wait forever and blast through the whole rest of the * formatting list - which we'd then do *again* as we proceed on from * the table. So, we need to stop if we reach a tag outside of the * table. To check for this case, note the tag that comes after the * table in formatting order, and stop if we encounter this tag; don't * format this tag, since it lies outside of the table and will be * formatted separately when we get back to the main loop. */ post_table_tag = table_tag->get_post_table_tag(); for (tag = table_tag ; tag != 0 ; ) { CHtmlTag *nxt; /* format this tag */ tag->format(win_, this); /* get the next tag in format order */ nxt = tag->get_next_fmt_tag(this); /* * if we just did the 'current' tag in the enclosing formatting * loop, it means that we have just reformatted the last tag in * the table, so we're done with the reformat */ if (tag == curtag_) break; /* * if the new tag is the post-table tag, we've blasted out of the * table, so we want to stop here */ if (nxt == post_table_tag) break; /* move to the next tag */ tag = nxt; } /* * Done with the table. Reset the table pass counter to zero to * indicate that we're no longer formatting a table. */ table_pass_ = 0; } /* * Push the formatting environment for later restoration */ void CHtmlFormatter::push_table_env() { /* if we have room in the stack, store the environment */ if (table_env_save_sp_ < CHTMLFMT_TABLE_ENV_SAVE_MAX) { CHtmlFmtTableEnvSave *p; /* get a pointer to the stack slot */ p = &table_env_save_[table_env_save_sp_]; /* save the information at this level */ p->left_count = left_flow_stk_.get_count(); p->right_count = right_flow_stk_.get_count(); p->margin_left = margin_left_; p->margin_right = margin_right_; p->disp_tail = disp_tail_; p->line_head = line_head_; p->max_line_width = max_line_width_; p->avail_line_width = avail_line_width_; p->curpos = curpos_; p->line_spacing = line_spacing_; p->last_was_newline = last_was_newline_; p->line_count = line_count_; p->line_id = line_id_; p->line_starts_count = line_starts_->get_count(); } /* * consume the stack level - note that we do this even if we can't * store anything at this level (due to having exhausted the stack), * since this way we at least keep track of the nesting depth even if * we go beyond what we can store */ ++table_env_save_sp_; } /* * Pop a formatting environment */ void CHtmlFormatter::pop_table_env() { CHtmlFmtTableEnvSave *p; /* if we have nothing to pop, ignore it */ if (table_env_save_sp_ == 0) return; /* pop a level */ --table_env_save_sp_; /* * if this level is beyond the maximum number of levels we can store, * there's nothing stored, so we can return without any further work */ if (table_env_save_sp_ >= CHTMLFMT_TABLE_ENV_SAVE_MAX) return; /* get a pointer to the stack slot */ p = &table_env_save_[table_env_save_sp_]; /* * If the left or right flow stacks have had items removed since we * saved the environment, add an item back in as necessary to restore * the equivalent environment. * * Note that we don't have to restore the actual items or even the * actual stack depth from the old environment; what's important is * that we restore the *equivalent* environment, which for our * purposes merely provides an item on each stack as needed to take * margins from where they were before to where they are now as soon * as we start a new line. Note that the y position of each new flow * stack item can be zero, since we merely need to set things up to * pop this level at the end of the next line. */ if (p->left_count > left_flow_stk_.get_count()) left_flow_stk_.push(margin_left_ - p->margin_left, 0, current_table_); if (p->right_count > right_flow_stk_.get_count()) right_flow_stk_.push(margin_right_ - p->margin_right, 0, current_table_); /* * restore the left and right margins as they were when we saved this * flow stack environment */ margin_left_ = p->margin_left; margin_right_ = p->margin_right; /* * remove everything that we formatted after saving the environment - * the caller will have saved the new stuff before calling us and will * add it back in when appropriate */ disp_tail_ = p->disp_tail; if (disp_tail_ == 0) disp_head_ = 0; else disp_tail_->clear_next_disp(); /* go back to the output position as it was when saved */ line_head_ = p->line_head; max_line_width_ = p->max_line_width; avail_line_width_ = p->avail_line_width; curpos_ = p->curpos; line_spacing_ = p->line_spacing; last_was_newline_ = p->last_was_newline; line_count_ = p->line_count; line_id_ = p->line_id; line_starts_->restore_count(p->line_starts_count); } /* * Begin a table cell. On pass 2, this will set the formatter's margins * according to the table's column area. */ void CHtmlFormatter::begin_table_cell(CHtmlDispTableCell *cell_disp, long cell_x_offset, long content_x_offset, long content_width) { long right_margin; CHtmlRect pos; long wid; switch(table_pass_) { case 2: /* * Start a new line. This will ensure that the items in the * previous cell are fully contained in the previous cell. */ start_new_line(cell_disp); /* * remember the current position, so that when we're done with * the cell we can calculate the height of the cell */ curpos_ = pre_table_curpos_; /* set the horizontal position of the cell */ pos = cell_disp->get_pos(); wid = pos.right - pos.left; pos.left = margin_left_ + cell_x_offset; pos.right = pos.left + wid; cell_disp->set_pos(&pos); /* * This is the second pass, so the column's position and size * have been finalized. Set up the margins so that the * following text will be set inside the column's horizontal * extent. */ right_margin = win_->get_disp_width() - (margin_left_ + content_x_offset + content_width); push_margins(margin_left_ + content_x_offset, right_margin); break; default: /* there's nothing to do on other passes */ break; } /* * Add the cell to the display list. Don't format the display item * at all, since it doesn't contribute to the line. */ add_to_disp_list(cell_disp); } /* * Format all pending items for a table cell. This will ensure that all * pending deferred items have been added to the list. */ void CHtmlFormatter::pre_end_table_cell() { /* end the current line */ add_disp_item_new_line(new (this) CHtmlDispBreak(0)); /* clear any floating items currently in the margins */ break_to_clear(TRUE, TRUE); /* if we have any deferred items remaining, add them all now */ while (defer_head_ != 0) { /* * Add the deferred items, forcing the addition of all of the * items even if they don't fit. */ add_all_deferred_items(0, 0, TRUE); /* move past them */ add_disp_item_new_line(new (this) CHtmlDispBreak(0)); break_to_clear(TRUE, TRUE); } } /* * End the current table cell. On table pass 1, this will calculate the * minimum and maximum width of the cell's contents, and tell the cell * about it. */ void CHtmlFormatter::end_table_cell(CHtmlTagTableCell *cell_tag, CHtmlDispTableCell *cell_disp) { CHtmlDisp *disp; CHtmlTableMetrics metrics; /* * See which pass we're on */ switch(table_pass_) { case 1: /* * First pass: this pass measures the contents of each table * cell so that we can determine the metrics of the overall * table. */ /* if there's no cell display item, there's nothing to calculate */ if (cell_disp == 0) break; /* * Run through all of the items in the display list, starting * with the item after the cell item itself, and have them * calculate their contributions to the minimum and maximum * widths. */ for (metrics.clear(), disp = cell_disp->get_next_disp() ; disp != 0 ; disp = disp->get_next_disp()) disp->measure_for_table(win_, &metrics); /* make sure we flush out the current line */ metrics.start_new_line(); /* tell the cell the results of the measurements */ cell_tag->set_table_cell_metrics(&metrics); break; case 2: /* * Second pass: restore the margins that were in effect before * we started the cell. */ pop_margins(); /* * Calculate the height of the cell, and tell the cell about it. * The cell will use this to figure its contribution to the * total row height. */ cell_tag->set_table_cell_height(curpos_.y - pre_table_curpos_.y, pre_table_curpos_.y); break; } } /* * Set the y positions of the contents of a table cell. y_offset is an * offset from the top of the table. disp_first and disp_last are the * first and last display items in the cell. base_y_pos is the original * (tentative) vertical position of the cell's display items; we'll keep * the offset from the final position for each item the same as its * offset from this position. */ void CHtmlFormatter::set_table_cell_y_pos(CHtmlDisp *disp_first, CHtmlDisp *disp_last, long y_offset, long base_y_pos, long row_y_offset) { CHtmlDisp *cur; CHtmlRect pos; long ht; /* * Run through the display list from first to last, and set each * item's vertical position. Note that we skip the very first item, * since it's always the cell; we set the cell's position * separately. Note also that if there is no last display item * (disp_last == 0), the cell must have no contents at all, hence we * don't need to do any work here. */ if (disp_last != 0) { for (cur = disp_first->get_next_disp() ; cur != 0 && cur != disp_last ; cur = cur->get_next_disp()) { /* get the old position */ pos = cur->get_pos(); /* calculate the new position */ ht = pos.bottom - pos.top; pos.top = pre_table_curpos_.y + y_offset + (pos.top - base_y_pos); pos.bottom = pos.top + ht; /* set the new position */ cur->set_pos(&pos); } } /* * Set the y position of the display item for the cell itself to the * top of the row */ pos = disp_first->get_pos(); ht = pos.bottom - pos.top; pos.top = pre_table_curpos_.y + row_y_offset; pos.bottom = pos.top + ht; disp_first->set_pos(&pos); } /* * Start playing a sound */ void CHtmlFormatter::play_sound(CHtmlTagSOUND *tag, HTML_Attrib_id_t layer, class CHtmlResCacheObject *res, int repeat_count, int random_start, HTML_Attrib_id_t sequence) { CHtmlSoundQueue *queue; /* * if the sound's generation is the same as the current generation, we * can ignore the sound, since it's already been played during this * generation */ if (tag->get_gen_id() == sound_gen_id_) return; /* mark the sound as having been played in this generation */ tag->set_gen_id(sound_gen_id_); /* remember the last sound tag */ set_last_sound_tag(tag); /* choose the appropriate queue based on the layer */ switch(layer) { case HTML_Attrib_background: queue = sound_queues_[0]; break; case HTML_Attrib_bgambient: queue = sound_queues_[1]; break; case HTML_Attrib_ambient: queue = sound_queues_[2]; break; case HTML_Attrib_foreground: queue = sound_queues_[3]; break; default: /* invalid - ignore the request */ return; } /* if the sound queue isn't set up, ignore the request */ if (queue == 0) return; /* play the sound */ queue->add_sound( res, repeat_count, random_start, sequence, tag->get_volume(), tag->get_fade_in(), tag->get_fade_out(), tag->get_crossfade_in(), tag->get_crossfade_out()); } /* * Cancel the current sound in a given layer */ void CHtmlFormatter::cancel_sound(HTML_Attrib_id_t layer, double fade_out, int bg_fade, int sync) { /* check the layer */ switch(layer) { case HTML_Attrib_background: if (sound_queues_[0] != 0) sound_queues_[0]->stop_current_sound(sync, fade_out, bg_fade); break; case HTML_Attrib_bgambient: if (sound_queues_[1] != 0) sound_queues_[1]->stop_current_sound(sync, fade_out, bg_fade); break; case HTML_Attrib_ambient: if (sound_queues_[2] != 0) sound_queues_[2]->stop_current_sound(sync, fade_out, bg_fade); break; case HTML_Attrib_foreground: if (sound_queues_[3] != 0) sound_queues_[3]->stop_current_sound(sync, fade_out, bg_fade); break; default: /* cancel sound in all layers */ { int i; for (i = 0 ; i < 4 ; ++i) { if (sound_queues_[i] != 0) { sound_queues_[i]->stop_current_sound( sync, fade_out, bg_fade); } } } break; } } /* ------------------------------------------------------------------------ */ /* * Debugger source window formatter */ /* * start formatting at the start of the document */ void CHtmlFormatterSrcwin::start_at_top(int reset_sounds) { CHtmlSysFont *font; /* remember the current font */ font = curfont_; /* inherit default */ CHtmlFormatter::start_at_top(reset_sounds); /* ask the parser for the first tag in the document */ prvtag_ = 0; curtag_ = parser_->get_outer_container(); /* * restore the font - ignore the window default, since we want to use * the explicit font that the debugger told us to use */ curfont_ = font; } /* ------------------------------------------------------------------------ */ /* * Stand-alone window formatter */ /* * start formatting at the start of the document */ void CHtmlFormatterStandalone::start_at_top(int reset_sounds) { /* inherit default */ CHtmlFormatter::start_at_top(reset_sounds); /* ask the parser for the first tag in the document */ prvtag_ = 0; curtag_ = parser_->get_outer_container(); } /* ------------------------------------------------------------------------ */ /* * Main formatter implementation */ CHtmlFormatterMain::CHtmlFormatterMain(CHtmlParser *parser) : CHtmlFormatter(parser) { /* no banners yet */ banners_ = 0; } CHtmlFormatterMain::~CHtmlFormatterMain() { /* * Delete the banner list. Do this before deleting the display * list, since banners may refer to tags in the display list. */ remove_all_banners(FALSE); /* * Delete any display list. Note that we need to do this here, even * though it's also done in our base class destructor, because we * need to run the *overridden* version of the display list deleter, * and by the time the base class destructor is called it will be * too late to do that. So, run it here, too; it's harmless to call * it twice. Note also that we need to delete the display list * *before* deleting the resource cache, because the display list * may contain references into the resource cache. */ delete_display_list(); /* * if our window is still around, detach from it (note that we must * do this BEFORE we delete the resource cache, because we cancel * any active sounds in unset_win(), which will in turn delete the * sounds from the cache) */ if (win_ != 0) unset_win(); } /* * Set my window */ void CHtmlFormatterMain::set_win(class CHtmlSysWin *win, const CHtmlRect *r) { int i; CHtmlSoundQueue *prv; /* inherit default */ CHtmlFormatter::set_win(win, r); /* * Create our sound queues - one each for the background, background * ambient, ambient, and foreground layers. Note that we start with * the background queue and move through ambient to the foreground, so * that for each new queue, the previous queue created is the next one * in the background. * * The sound queues are global, but we manage them as part of the main * text formatter, because this formatter is always around. */ for (prv = 0, i = 0 ; i < 4 ; ++i) { /* create this sound queue */ sound_queues_[i] = new CHtmlSoundQueue(win, prv); /* this is the background queue for the next queue */ prv = sound_queues_[i]; } } /* * Receive notification that the window is about to be deleted. Our * sound queues depend on the window, so we need to delete the sound * queues before the window is deleted. */ void CHtmlFormatterMain::unset_win() { int i; /* delete the sound queues */ for (i = 0 ; i < 4 ; ++i) { if (sound_queues_[i] != 0) { delete sound_queues_[i]; sound_queues_[i] = 0; } } /* inherit default */ CHtmlFormatter::unset_win(); } /* * Prepare to delete the parser. We'll forget about any banner tags * that we're keeping a pointer to, since those tags will be deleted * when the parser is deleted. */ void CHtmlFormatterMain::release_parser() { CHtmlFmtBannerItem *cur; /* go through all of our banners */ for (cur = banners_ ; cur != 0 ; cur = cur->nxt_) { /* forget any BANNER tags we're keeping pointers to */ cur->last_tag_ = 0; cur->first_tag_ = 0; /* tell the subformatter to release the parser as well */ if (cur->subformatter_ != 0) cur->subformatter_->release_parser(); } /* inherit default */ CHtmlFormatter::release_parser(); } /* * start formatting at the start of the document */ void CHtmlFormatterMain::start_at_top(int reset_sounds) { CHtmlFmtBannerItem *cur; /* inherit default */ CHtmlFormatter::start_at_top(reset_sounds); /* ask the parser for the first tag in the document */ prvtag_ = 0; curtag_ = parser_->get_outer_container(); /* no title */ title_buf_.set(""); /* no sound tags yet */ last_sound_tag_ = 0; /* * Reset the heights of all of the banner subwindows, since we may * be reformatting with some new metric that would affect the banner * heights (for example, the font size may have changed). In * addition, forget the previous tags of the banners, since these * are no longer valid. */ for (cur = banners_ ; cur != 0 ; cur = cur->nxt_) { /* no height set yet */ cur->height_set_ = FALSE; /* no tags seen on this run */ cur->last_tag_ = 0; cur->first_tag_ = 0; cur->is_first_ = TRUE; /* the banner doesn't have a start tag yet */ cur->subformatter_->set_banner_start_tag(0); } } /* * add a display item */ void CHtmlFormatterMain::add_disp_item(CHtmlDisp *item) { /* if we're in title mode, add the text to the title buffer */ if (title_mode_) item->add_text_to_buf(&title_buf_); /* inherit default behavior */ CHtmlFormatter::add_disp_item(item); } /* * End the title. Sets the window title from the contents of the title * buffer. */ void CHtmlFormatterMain::end_title(CHtmlTagTITLE *tag) { /* note we're no longer in title mode */ title_mode_ = FALSE; /* set the window's caption to the contents of the title buffer */ win_->set_window_title(title_buf_.get()); /* set the title text in the TITLE tag */ tag->set_title_text(title_buf_.get()); /* forget the title buffer contents */ title_buf_.set(""); } /* * Begin a banner */ int CHtmlFormatterMain:: format_banner(const textchar_t *banner_id, long height, int height_set, int height_prev, int height_pct, long width, int width_set, int width_prev, int width_pct, CHtmlTagBANNER *banner_tag) { CHtmlFmtBannerItem *cur; CHtmlFormatterBannerSub *subformatter; CHtmlSysWin *subwin; CHtmlSysFrame *frame; HTML_BannerWin_Units_t width_units; HTML_BannerWin_Units_t height_units; /* get the frame object; if there isn't one, we can't proceed */ if ((frame = CHtmlSysFrame::get_frame_obj()) == 0) return FALSE; /* * If we're in T3 mode, and the OS layer supports the banner API, don't * allow the tag - the caller must use the banner API instead. */ if (is_t3_mode() && !banner_tag->is_about_box()) { long result; /* * Get information on banner API support from the OS. If the API * is supported, then don't allow . */ if (os_get_sysinfo(SYSINFO_BANNERS, 0, &result) && result != 0) { /* generate a debug message to explain what's wrong */ oshtml_dbg_printf(" is not allowed in TADS 3 - use " "the banner API instead\n"); /* indicate failure */ return FALSE; } } /* * for our purposes, act as though the width and height haven't been * set if we're being asked to use a previous width and height */ if (height_prev) height_set = FALSE; if (width_prev) width_set = FALSE; /* if the width or height aren't set, they're not percentages */ if (!width_set) width_pct = FALSE; if (!height_set) height_pct = FALSE; /* use pixels or percentage units, as appropriate */ width_units = (width_pct ? HTML_BANNERWIN_UNITS_PCT : HTML_BANNERWIN_UNITS_PIX); height_units = (height_pct ? HTML_BANNERWIN_UNITS_PCT : HTML_BANNERWIN_UNITS_PIX); /* * See if we can find an existing banner with the same name. If so, * re-use its window space. */ for (cur = banners_ ; cur != 0 ; cur = cur->nxt_) { /* is this a match? */ if (cur->matches(banner_id, banner_tag->is_about_box())) { /* stop looking */ break; } } /* see if we found a match */ if (cur != 0) { unsigned long style; /* found it - reuse the existing banner */ subformatter = cur->subformatter_; subwin = subformatter->get_win(); /* * if there's no window, it means we've closed the UI, which means * the app is terminating; no need to proceed */ if (subwin == 0) return FALSE; /* * make sure we redraw the window, since we're replacing its * entire contents */ subformatter->inval_window(); /* set the style flags */ style = 0; if (banner_tag->has_border()) style |= OS_BANNER_STYLE_BORDER; /* * update the banner settings, in case they've changed in the * new tag */ subwin->set_banner_info(banner_tag->get_alignment(), style); /* * we can't use the previous height if the height hasn't been * set yet, or has been reset due to a full reformat; likewise * for the width */ if (!cur->height_set_) height_prev = FALSE; if (!cur->width_set_) width_prev = FALSE; /* * If this banner was previously used by another tag earlier in * this formatting list, and the last tag wasn't the first * instance of the banner, we can tell the old tag to skip * formatting on future passes through the formatting list, * since the new tag will always override the previous tag. (We * need to keep the first instance around, so that the banner is * re-created in the proper order relative to other banners if * the window should be reformatted from scratch.) */ if (cur->last_tag_ != 0 && !cur->is_first_) cur->last_tag_->notify_obsolete(get_text_array()); /* if we have a previous instance, this new one isn't the first */ if (cur->last_tag_ != 0) cur->is_first_ = FALSE; } else { unsigned long style; /* * This banner doesn't exist yet. Create a new banner * subformatter to format the banner contents */ subformatter = new CHtmlFormatterBannerSub(parser_, this, banner_tag); /* set up the style flags */ style = 0; if (banner_tag->has_border()) style |= OS_BANNER_STYLE_BORDER; /* * Ask the window to create a banner subwindow. If this operation * fails, the window either doesn't allow banners or is not able to * create a new banner; in any case, ignore the banner entirely if * we can't create a banner window. Note that the tag can * only be used to create banners within the main window, never as * sub-banners of other banners, so the parent is null to indicate * the new banner is a child of the main text area. */ subwin = (banner_tag->is_about_box() ? frame->create_aboutbox_window(subformatter) : frame->create_banner_window(0, HTML_BANNERWIN_BANNERTAG, subformatter, OS_BANNER_LAST, 0, banner_tag->get_alignment(), style)); if (subwin == 0) { /* delete the sub-formatter, since we won't be needing it */ delete subformatter; /* indicate that we couldn't format the banner */ return FALSE; } /* create a new list entry for this item */ cur = new CHtmlFmtBannerItem(banner_id, banner_tag->is_about_box(), subformatter); /* link it at the head of the list */ cur->nxt_ = banners_; banners_ = cur; /* * if HEIGHT=PREVIOUS or WIDTH=PREVIOUS were specified, ignore * them, since there wasn't a previous height or width to use - * use the natural window size (i.e, the height and width of the * contents) instead */ height_prev = FALSE; width_prev = FALSE; } /* * set default color scheme, in case the banner doesn't have a BODY * tag anywhere */ subwin->set_html_bg_color(0, TRUE); subwin->set_html_text_color(0, TRUE); subwin->set_html_input_color(0, TRUE); subwin->set_html_link_colors(0, TRUE, 0, TRUE, 0, TRUE, 0, TRUE); /* tell the subformatter where to begin */ subformatter->set_banner_start_tag(banner_tag); /* tell the subformatter to start at the top */ subformatter->start_at_top(FALSE); /* tell the subwindow to format the banner contents */ subwin->do_formatting(FALSE, FALSE, FALSE); /* * initialize the window's size, now that we have its contents laid out * (we must wait until after we've formatted the contents because the * size of the window could depend upon the size of the contents) */ subformatter->init_size(banner_tag->get_alignment(), width, width_units, width_set, width_prev, height, height_units, height_set, height_prev); /* this item's width and height have now been set */ cur->width_set_ = TRUE; cur->height_set_ = TRUE; /* remember that this tag formatted to the banner */ cur->last_tag_ = banner_tag; /* if we don't have a first tag yet, this is the first */ if (cur->first_tag_ == 0) cur->first_tag_ = banner_tag; /* indicate success */ return TRUE; } #if 0 /* * Force all open banners closed */ void CHtmlFormatterMain::force_banners_closed() { int changed; /* keep going until we fail to find an open */ for (changed = FALSE ; ; ) { CHtmlTagContainer *cur; int found; /* * scan from the current inner container outward looking for * banners */ for (found = FALSE, cur = parser_->get_inner_container() ; cur != 0 ; cur = cur->get_container()) { /* if this is a BANNER tag, note that we found one */ if (cur->tag_name_matches("BANNER", 6)) { /* note that we found a banner */ found = TRUE; /* look no further */ break; } } /* if we didn't find a BANNER tag, we're done */ if (!found) break; /* close the current tag, and check again for open BANNER tags */ parser_->close_current_tag(); } /* we if changed anything, reformat */ if (changed) win_->do_formatting(FALSE, FALSE, FALSE); } #endif /* * Remove all of the banners */ void CHtmlFormatterMain::remove_all_banners(int replacing) { /* remove the first banner repeatedly until we have no more banners */ while (banners_ != 0) remove_banner(banners_, replacing); } /* * Remove a banner */ void CHtmlFormatterMain::remove_banner(const textchar_t *banner_id) { CHtmlFmtBannerItem *cur; /* find the banner matching this ID */ for (cur = banners_ ; cur != 0 ; cur = cur->nxt_) { /* is this a match? */ if (cur->matches(banner_id, FALSE)) { /* remove it */ remove_banner(cur, TRUE); /* done */ break; } } } /* * Remove a banner */ void CHtmlFormatterMain::remove_banner(CHtmlFmtBannerItem *cur, int replacing) { CHtmlFmtBannerItem *prv; CHtmlSysFrame *frame; /* get the frame object */ frame = CHtmlSysFrame::get_frame_obj(); /* * Tell the window to remove the banner -- this will destroy the * window in the process. Note that we can skip this if the window * has already been deleted. */ if (cur->subformatter_->get_win() != 0 && frame != 0) { CHtmlSysWin *win; /* * remember the window for a moment, then dissociate the formatter * from the window, since we're about to destroy the windwo */ win = cur->subformatter_->get_win(); cur->subformatter_->unset_win(); /* remove the system window */ frame->remove_banner_window(win); } /* * If we're replacing the banner, and the banner was formatted by a * tag in this same tag list, tell the tag it's no longer needed, * since the banner it creates will always be destroyed. Also * notify the first instance of the tag of the same thing; we always * keep around the first and last instance of a particular banner * until the banner is removed, so that the banner is created in the * correct order relative to other banners on reformattings of the * entire list. */ if (replacing && cur->last_tag_ != 0) { /* notify the last tag that it's obsolete */ cur->last_tag_->notify_obsolete(get_text_array()); /* if the first tag is different, obsolete it as well */ if (cur->first_tag_ != cur->last_tag_) cur->first_tag_->notify_obsolete(get_text_array()); } /* delete the associated subformatter */ delete cur->subformatter_; /* unlink this item from the list */ if (cur == banners_) { /* it's at the head of the list - advance the head */ banners_ = banners_->nxt_; } else { /* find the previous item in the list */ for (prv = banners_ ; prv != 0 && prv->nxt_ != cur ; prv = prv->nxt_); /* if we found it, advance the previous item's link */ if (prv != 0) prv->nxt_ = cur->nxt_; } /* delete this item -- we have no other use for it */ delete cur; } /* ------------------------------------------------------------------------ */ /* * Input-capable formatter */ CHtmlFormatterInput::CHtmlFormatterInput(CHtmlParser *parser) : CHtmlFormatterMain(parser) { /* there's no command input yet */ input_pre_ = 0; input_tag_ = 0; } /* * start formatting from the top of the document */ void CHtmlFormatterInput::start_at_top(int reset_sounds) { /* inherit default */ CHtmlFormatterMain::start_at_top(reset_sounds); /* forget all the input information */ input_pre_ = 0; input_tag_ = 0; input_head_ = 0; input_line_head_ = 0; input_line_count_ = 0; input_line_id_ = 0; freeze_display_adjust_ = FALSE; } /* * delete the display list */ void CHtmlFormatterInput::delete_display_list() { /* inherit default */ CHtmlFormatterMain::delete_display_list(); /* forget the current input line tag */ input_pre_ = 0; input_tag_ = 0; } /* * Figure out if I have more work to do before the command input line */ int CHtmlFormatterInput::more_before_input() { /* if there's nothing left at all, there's nothing before the input */ if (!more_to_do()) return FALSE; /* * if the current tag is at or beyond the input tag, there's nothing * left to do */ if (input_tag_ != 0) { CHtmlTag *cur; /* * scan from the input tag forward, and see if the next * rendering tag is within the input line */ for (cur = input_tag_ ; cur != 0 ; cur = cur->get_next_fmt_tag(this)) { /* if this is the next rendering tag, we're in the command line */ if (cur == curtag_) return FALSE; } } /* we must have more left before the command line */ return TRUE; } /* * Add an input item, replacing the current input item(s) */ void CHtmlFormatterInput::add_disp_item_input(CHtmlDispTextInput *item, class CHtmlTagTextInput *tag) { int old_freeze; /* freeze display updating while we're formatting the input line */ old_freeze = freeze_display_adjust_; freeze_display_adjust_ = TRUE; /* * if the tag differs from the current one, end the old command and * start a new one */ if (input_tag_ != tag) { /* if we have an input tag already, end it */ clear_old_input(); /* remember the new coordinating input tag */ input_tag_ = tag; } /* * if we have an active input item, unchain it from the list and set * it aside */ if (input_pre_ != 0) { /* unlink it from the list */ input_pre_->clear_next_disp(); disp_tail_ = input_pre_; /* * Reset our internal running total for the amount of space * remaining in the current line, since we're setting a portion * of the line over again. Set the total to the same point it * was at when we started the command. */ avail_line_width_ = input_avail_line_width_; /* back up to the y position at the start of the input */ curpos_.y = input_y_pos_; curpos_.x = input_x_pos_; /* back up to the input line number */ line_count_ = input_line_count_; line_id_ = input_line_id_; line_starts_->restore_count(input_line_starts_count_); /* restore the line head at the start of the input line */ line_head_ = input_line_head_; /* forget any break position */ breakpos_.clear(); /* add the new item */ add_disp_item(item); /* set line positions in the last line */ set_line_positions(line_head_, 0); /* ensure that the scroll position is properly set */ if (!old_freeze) win_->fmt_adjust_vscroll(); /* * compare the old input item list and the new input item list; * if we find a point at which they differ, invalidate the * display from that point forward so that we'll redraw the * changes */ item->diff_input_lists(input_head_, win_); /* we're done with the old input display items, so delete them */ CHtmlDisp::delete_list(input_head_); /* set the new input list */ input_head_ = item; } else { /* * No command is active. Note the current command tail, so that * we can replace the display item we're adding now on future * changes. */ input_pre_ = disp_tail_; /* remember the first item of the input */ input_head_ = item; /* * Remember the available line width before we started on the * command, since we'll have to come back to this point again * whenever we reformat the input line after the UI changes the * buffer contents. Also remember the current y position and * line number, since we'll need to back up here each time we * reformat the line (since the input may spill over onto * multiple lines). */ input_avail_line_width_ = avail_line_width_; input_y_pos_ = curpos_.y; input_x_pos_ = curpos_.x; input_line_count_ = line_count_; input_line_id_ = line_id_; input_line_starts_count_ = line_starts_->get_count(); input_line_head_ = line_head_; /* add the input line item as normal */ add_disp_item(item); /* set line positions in the last line */ set_line_positions(line_head_, 0); /* make sure we redraw the new item immediately */ item->inval(win_); /* ensure that the scroll position is properly set */ if (!old_freeze) win_->fmt_adjust_vscroll(); } /* resume normal display adjustments */ freeze_display_adjust_ = old_freeze; } /* * Prepare for input */ void CHtmlFormatterInput::prepare_for_input() { CHtmlTagContainer *cont; int depth; int close_count; /* * If there are any tags still open, close them. This is * important because the two-pass table layout algorithm makes it * impossible to read input interactively in the middle of a table. */ for (depth = 1, close_count = 0, cont = parser_->get_inner_container() ; cont != 0 ; cont = cont->get_container(), ++depth) { /* if this is a TABLE tag, note that we have to close this far */ if (cont->tag_name_matches("TABLE", 5)) { /* it's a TABLE tag - we need to close at least to this depth */ close_count = depth; } } /* close each open
tag */ for ( ; close_count != 0 ; --close_count) parser_->close_current_tag(); } /* * Begin a new command input line */ class CHtmlTagTextInput *CHtmlFormatterInput::begin_input(const textchar_t *buf, size_t len) { /* create and remember a new input item tag */ input_tag_ = parser_->append_input_tag(buf, len); /* * if we've already formatted everything that was previously queued * up, make sure we're set up to start formatting again at the new * input tag - the new input tag itself won't require anything at this * point, so just consider it to be the previously formatted tag */ if (curtag_ == 0) prvtag_ = input_tag_; /* return the new tag */ return input_tag_; } /* * Store input text temporarily in the text array */ unsigned long CHtmlFormatterInput::store_input_text_temp( const textchar_t *txt, size_t len) { /* ask the parser's text array to store the text */ return parser_->get_text_array()->store_text_temp(txt, len); } /* * End the current command input line */ void CHtmlFormatterInput::end_input() { /* clear the old input line */ clear_old_input(); /* consider this tag to have been formatted if desired */ while (curtag_ != 0) { prvtag_ = curtag_; curtag_ = curtag_->get_next_fmt_tag(this); } } /* * Clear the old command input line */ void CHtmlFormatterInput::clear_old_input() { /* if we have a command, commit it */ if (input_tag_ != 0) input_tag_->commit(get_text_array()); /* forget the current input line settings */ input_tag_ = 0; input_head_ = 0; input_pre_ = 0; input_line_head_ = 0; } /* * Get the position of a character in the input. When seeking a * position that is known to be in the current input buffer, this is * somewhat faster than the generic get_text_pos() function, because we * don't need to search beyond the input line. */ CHtmlPoint CHtmlFormatterInput::get_text_pos_input(unsigned long txtofs) const { return get_text_pos_list(input_head_, txtofs); } /* ------------------------------------------------------------------------ */ /* * Banner formatter implementation */ /* * Initialize the banner window's size. Use the sizes given, if they're * specified; if one or the other size isn't specified, size the banner * according to its current contents in that dimension. */ void CHtmlFormatterBanner::init_size( HTML_BannerWin_Pos_t alignment, long width, HTML_BannerWin_Units_t width_units, int width_set, int width_prev, long height, HTML_BannerWin_Units_t height_units, int height_set, int height_prev) { /* * determine the height - if it wasn't explicitly set to a value or to * PREVIOUS, use the new content height */ if (!height_set && !height_prev) { /* calculate the height of the contents */ height = calc_content_height(alignment); /* we're now using pixel units */ height_units = HTML_BANNERWIN_UNITS_PIX; } /* * determine the width - if it wasn't explicitly set to a value or to * PREVIOUS, use the new content width */ if (!width_set && !width_prev) { /* calculate the content width */ width = calc_content_width(alignment); /* we're now using pixel units */ width_units = HTML_BANNERWIN_UNITS_PIX; } /* * set the size if they provided a setting or didn't specify * PREVIOUS for either dimension */ win_->set_banner_size(width, width_units, width_set || !width_prev, height, height_units, height_set || !height_prev); } /* * Calculate the height of the current contents of the banner. This is * used when we wish to size the banner to its natural content height. */ long CHtmlFormatterBanner::calc_content_height(HTML_BannerWin_Pos_t) { /* * use the current y position of the banner's contents, and add in the * bottom margin (we don't need to include the top margin, since we * will have already incorporated the top margin when positioning our * contents) */ return get_layout_max_y_pos() + get_bottom_margin(); } /* * Calculate the width of the current contents of the banner. This is used * when we wish to size the banner to its natural content width. */ long CHtmlFormatterBanner::calc_content_width(HTML_BannerWin_Pos_t alignment) { long width; CHtmlDisp *disp; CHtmlTableMetrics metrics; CHtmlRect submargins; /* find the appropriate width, according to the banner alignment */ switch(alignment) { case HTML_BANNERWIN_POS_LEFT: case HTML_BANNERWIN_POS_RIGHT: /* * This is a vertical banner, so default to the minimum width that * will work. To determine this, run through our window's display * list and ask for the banner metrics. */ for (metrics.clear(), disp = get_first_disp() ; disp != 0 ; disp = disp->get_next_disp()) disp->measure_for_banner(get_win(), &metrics); /* flush the last line */ metrics.start_new_line(); /* use the minimum width from the metrics */ width = metrics.min_width_; /* add the window's physical margins as well */ submargins = get_phys_margins(); width += submargins.left + submargins.right; break; default: /* * for horizontal banners, the width doesn't really matter, but get * the maximum line width in case the OS layer is interested for * some reason */ width = get_max_line_width(); break; } /* return the calculated width */ return width; } /* ------------------------------------------------------------------------ */ /* * subformatter implementation */ /* * Receive notification that we've finished formatting the banner. * We'll set our stop_formatting_ flag to indicate that we shouldn't do * any more formatting. */ void CHtmlFormatterBannerSub::end_banner() { /* note that it's time to stop formatting */ stop_formatting_ = TRUE; /* make sure we clear any margin floating items */ break_to_clear(TRUE, TRUE); /* end the current line */ add_disp_item_new_line(new (this) CHtmlDispBreak(0)); } /* * start new banner contents */ void CHtmlFormatterBannerSub::start_at_top(int reset_sounds) { /* inherit default to clear out our formatting list */ CHtmlFormatterBanner::start_at_top(reset_sounds); /* get the first tag in the banner's contents */ curtag_ = (banner_start_tag_ == 0 ? 0 : banner_start_tag_->get_banner_contents()); } /* ------------------------------------------------------------------------ */ /* * External banner formatter subclass */ /* * Create an "external" banner. An external banner is one that is * controlled from a separate output stream unrelated to the main * formatter. Returns the new formatter object. */ CHtmlFormatterBannerExt *CHtmlFormatterBannerExt::create_extern_banner( CHtmlFormatterBannerExt *parent_fmt, int where, CHtmlFormatterBannerExt *other_fmt, int wintype, HTML_BannerWin_Pos_t alignment, int siz, int siz_units, unsigned long style) { CHtmlFormatterBannerExt *formatter; CHtmlParser *parser; CHtmlSysWin *win; HTML_BannerWin_Type_t bantype; CHtmlSysWin *other_win; CHtmlSysWin *parent_win; CHtmlSysFrame *frame; /* get the frame object; if there isn't one, we can't proceed */ if ((frame = CHtmlSysFrame::get_frame_obj()) == 0) return 0; /* check for a supported window type */ switch(wintype) { case OS_BANNER_TYPE_TEXT: /* translate to our internal banner type */ bantype = HTML_BANNERWIN_TEXT; /* * This is a text stream banner, so it requires its own parser. * Unlike an ordinary banner, which shares the parser with the * parent formatter, an external banner gets an independent parser * of its own. */ parser = new CHtmlParser(); /* * Create the new banner formatter. Note that we don't have to * worry about deleting the parser after this point, because the * formatter takes ownership of it from now on. */ formatter = new CHtmlFormatterBannerExtText( parent_fmt, parser, style); /* we're ready to create the window */ break; case OS_BANNER_TYPE_TEXTGRID: /* translate to our internal banner type */ bantype = HTML_BANNERWIN_TEXTGRID; /* * A text grid banner requires the special text grid formatter. * Text grids don't use parsers, since they do not support HTML. */ parser = 0; formatter = new CHtmlFormatterBannerExtGrid(parent_fmt, style); /* ready to create the window */ break; default: /* unsupported window type */ return 0; } /* get the parent window, if given */ parent_win = (CHtmlSysWin *)(parent_fmt == 0 ? 0 : parent_fmt->get_win()); /* if a relative insertion point is specified, get its window object */ if ((where == OS_BANNER_BEFORE || where == OS_BANNER_AFTER) && other_fmt != 0) other_win = other_fmt->get_win(); else other_win = 0; /* create the window */ win = frame->create_banner_window(parent_win, bantype, formatter, where, other_win, alignment, style); /* * if we failed to create the new window, give up - the underlying * display system must not support banner windows */ if (win == 0) { /* delete the formatter */ delete formatter; /* return failure */ return 0; } /* set the default color scheme in the new window */ win->set_html_bg_color(0, TRUE); win->set_html_text_color(0, TRUE); win->set_html_input_color(0, TRUE); win->set_html_link_colors(0, TRUE, 0, TRUE, 0, TRUE, 0, TRUE); /* set the initial size, if a non-zero size was given */ if (siz != 0) { HTML_BannerWin_Units_t html_units; /* convert the units to our own unit system */ switch(siz_units) { case OS_BANNER_SIZE_PCT: default: /* use a percentage-based size */ html_units = HTML_BANNERWIN_UNITS_PCT; break; case OS_BANNER_SIZE_ABS: /* use the natural units, according to the window type */ html_units = formatter->get_natural_size_units(); break; } /* set the size, according to the alignment */ switch(alignment) { case HTML_BANNERWIN_POS_TOP: case HTML_BANNERWIN_POS_BOTTOM: /* top/bottom banner - set the height */ win->set_banner_size(0, html_units, FALSE, siz, html_units, TRUE); break; case HTML_BANNERWIN_POS_LEFT: case HTML_BANNERWIN_POS_RIGHT: win->set_banner_size(siz, html_units, TRUE, 0, html_units, FALSE); break; } } /* return the new window's formatter */ return formatter; } /* * Delete an external banner. */ void CHtmlFormatterBannerExt::delete_extern_banner( CHtmlFormatterBannerExt *fmt) { CHtmlSysFrame *frame; /* get the frame object */ frame = CHtmlSysFrame::get_frame_obj(); /* * if we have a frame, and the formatter still has a window (which it * might not - the window could have been deleted first), delete the * window */ if (frame != 0 && fmt->get_win() != 0) { CHtmlSysWin *win; /* * remember the window for a moment, then dissociate the formatter * from the window, since we're about to destroy the window */ win = fmt->get_win(); fmt->unset_win(); /* remove the window */ frame->remove_banner_window(win); } /* delete the formatter */ delete fmt; } /* * Set the size */ void CHtmlFormatterBannerExt::set_banner_size(int siz, int siz_units) { HTML_BannerWin_Pos_t align; unsigned long style; HTML_BannerWin_Units_t html_units; /* if there's no window, ignore it */ if (get_win() == 0) return; /* get our window information (for the alignment) */ get_win()->get_banner_info(&align, &style); /* convert the size units to the appropriate HTML constants */ switch(siz_units) { case OS_BANNER_SIZE_PCT: /* percentage units */ html_units = HTML_BANNERWIN_UNITS_PCT; break; case OS_BANNER_SIZE_ABS: /* absolute units - use our natural size */ html_units = get_natural_size_units(); break; default: /* invalid units - ignore it */ return; } /* * initialize the size, keeping the existing size for each dimension we * don't want to set, sizing to the contents for each dimension we do * want to set */ switch(align) { case HTML_BANNERWIN_POS_TOP: case HTML_BANNERWIN_POS_BOTTOM: /* top/bottom banner - set the height */ get_win()->set_banner_size(0, HTML_BANNERWIN_UNITS_PIX, FALSE, siz, html_units, TRUE); break; case HTML_BANNERWIN_POS_LEFT: case HTML_BANNERWIN_POS_RIGHT: /* left/right banner - set the width */ get_win()->set_banner_size(siz, html_units, TRUE, 0, HTML_BANNERWIN_UNITS_PIX, FALSE); break; } } /* * Size to our contents */ void CHtmlFormatterBannerExt::size_to_contents(int set_width, int set_height) { HTML_BannerWin_Pos_t pos; unsigned long style; /* if there's no window, ignore it */ if (get_win() == 0) return; /* get our window information (for the alignment) */ get_win()->get_banner_info(&pos, &style); /* * initialize the size, keeping the existing size for each dimension we * don't want to set, sizing to the contents for each dimension we do * want to set */ init_size(pos, 0, HTML_BANNERWIN_UNITS_PIX, FALSE, !set_width, 0, HTML_BANNERWIN_UNITS_PIX, FALSE, !set_height); } /* * Calculate the height of the current contents of the banner. This is * used when we wish to size the banner to its natural content height. */ long CHtmlFormatterBannerExt::calc_content_height(HTML_BannerWin_Pos_t align) { size_t i; long ht; /* inherit the default to calculate our internal content height */ ht = CHtmlFormatterBanner::calc_content_height(align); /* include the heights of any "strut" children */ for (i = 0 ; i < children_.get_count() ; ++i) { CHtmlFormatterBannerExt *chi; /* get this child */ chi = (CHtmlFormatterBannerExt *)children_.get_ele(i); /* if it has the "strut" style, include its height */ if (chi->get_win() != 0) { HTML_BannerWin_Pos_t pos; unsigned long style; /* get the style */ chi->get_win()->get_banner_info(&pos, &style); /* if it's a vertical strut, include its height */ if ((chi->orig_style_ & OS_BANNER_STYLE_VSTRUT) != 0) { /* calculate the child's content height */ long chi_ht = chi->calc_content_height(pos); /* * If the child is a horizontal (top or bottom) banner, add * its height to our internal height, since its contents * are vertically added to our own. If it's vertical (left * or right), use its height as a minimum, since it gets a * column slice of our space, sharing our overall height. */ switch (pos) { case HTML_BANNERWIN_POS_TOP: case HTML_BANNERWIN_POS_BOTTOM: /* it's horizontal - add its height to our own */ ht += chi_ht; break; case HTML_BANNERWIN_POS_LEFT: case HTML_BANNERWIN_POS_RIGHT: /* it's vertical - its height is our minimum height */ if (ht < chi_ht) ht = chi_ht; break; } } } } /* return the calculated height */ return ht; } /* * Calculate the width of the current contents of the banner. This is used * when we wish to size the banner to its natural content width. */ long CHtmlFormatterBannerExt::calc_content_width(HTML_BannerWin_Pos_t align) { size_t i; long wid; /* inherit the default to calculate our internal content width */ wid = CHtmlFormatterBanner::calc_content_width(align); /* include the widths of any "strut" children */ for (i = 0 ; i < children_.get_count() ; ++i) { CHtmlFormatterBannerExt *chi; /* get this child */ chi = (CHtmlFormatterBannerExt *)children_.get_ele(i); /* if it has the "strut" style, include its width */ if (chi->get_win() != 0) { HTML_BannerWin_Pos_t pos; unsigned long style; /* get the style */ chi->get_win()->get_banner_info(&pos, &style); /* if it's a horizontal strut, include its width */ if ((chi->orig_style_ & OS_BANNER_STYLE_HSTRUT) != 0) { /* calculate the child's content width */ long chi_wid = chi->calc_content_width(pos); /* * If the child is a vertical (left or right) banner, add * its width to our internal width, since its contents are * horizontally added to our own. If it's horizontal (top * or bottom), use its width as a minimum, since it gets a * row slice of our space, sharing our overall width. */ switch (pos) { case HTML_BANNERWIN_POS_TOP: case HTML_BANNERWIN_POS_BOTTOM: /* it's horizontal - its width is our minimum width */ if (wid < chi_wid) wid = chi_wid; break; case HTML_BANNERWIN_POS_LEFT: case HTML_BANNERWIN_POS_RIGHT: /* it's vertical - add its width to our own */ wid += chi_wid; break; } } } } /* return the calculated width */ return wid; } /* ------------------------------------------------------------------------ */ /* * External banner formatter - ordinary text stream window */ /* * create */ CHtmlFormatterBannerExtText::CHtmlFormatterBannerExtText( CHtmlFormatterBannerExt *parent, CHtmlParser *parser, unsigned long style) : CHtmlFormatterBannerExt(parent, parser, style) { /* create our text buffer */ txtbuf_ = new CHtmlTextBuffer(); } /* * delete */ CHtmlFormatterBannerExtText::~CHtmlFormatterBannerExtText() { /* we own our parser, so we must delete it if it's still around */ if (parser_ != 0) { CHtmlParser *parser; /* remember our parser for a moment */ parser = parser_; /* forget the parser, so we don't try to access it again */ parser_ = 0; /* now that we're disentangled, we can delete the parser */ delete parser; } /* delete our text buffer */ delete txtbuf_; } /* * clear my contents */ void CHtmlFormatterBannerExtText::clear_contents() { /* cancel any animations on the outgoing page */ cancel_playback(); /* clear the page in the parser */ if (parser_ != 0) parser_->clear_page(); /* invalidate the entire window */ inval_window(); /* notify the window that we're clearing it */ if (win_ != 0) win_->notify_clear_contents(); /* * reset the formatter - don't reset sounds, as sounds are the province * of the main window only */ start_at_top(FALSE); } /* * turn HTML mode on or off */ void CHtmlFormatterBannerExtText::set_html_mode(int mode) { /* adjust the parser mode, according to the caller's request */ if (mode) { /* * turning HTML mode on - tell the parser to interpret markups and * treat whitespace as insignificant */ parser_->obey_whitespace(FALSE, TRUE); parser_->obey_markups(TRUE); } else { /* * Turn HTML mode off - treat whitespace as significant, and do not * interpret markups. Since this is a programmatic mode rather * than a mode set with a tag, there's no way for a tag to end this * mode, so there's no need to interpret any text that looks like * an end tag. */ parser_->obey_whitespace(TRUE, TRUE); parser_->obey_markups(FALSE); parser_->obey_end_markups(FALSE); } } /* * Get HTML mode */ int CHtmlFormatterBannerExtText::get_html_mode() const { /* HTML mode is on if we're obeying markups */ return parser_->get_obey_markups(); } /* * start at the top of our format list */ void CHtmlFormatterBannerExtText::start_at_top(int reset_sounds) { /* inherit default handling */ CHtmlFormatterBannerExt::start_at_top(reset_sounds); /* set up at the parser's outer tag */ curtag_ = parser_->get_outer_container(); } /* * Add source text for formatting */ void CHtmlFormatterBannerExtText::add_source_text( const textchar_t *txt, size_t len) { /* append the text to our buffer - we'll parse and format it later */ txtbuf_->append(txt, len); } /* * Flush the source text */ void CHtmlFormatterBannerExtText::flush_txtbuf(int fmt) { /* if the OS window has already been closed, ignore the request */ if (win_ == 0) return; /* parse what's in the source buffer */ parser_->parse(txtbuf_, win_->get_win_group()); /* we're done with this source, so clear the buffer */ txtbuf_->clear(); /* parse and format the source if desired */ if (fmt) win_->do_formatting(FALSE, FALSE, FALSE); } /* ------------------------------------------------------------------------ */ /* * Text Grid Banner implementation */ /* * construction */ CHtmlFormatterBannerExtGrid::CHtmlFormatterBannerExtGrid( CHtmlFormatterBannerExt *parent, unsigned long style) : CHtmlFormatterBannerExt(parent, 0, style) { /* start the output position at the upper left corner */ csr_row_ = 0; csr_col_ = 0; /* we haven't written any content yet */ row_cnt_ = 0; col_cnt_ = 0; /* we don't have any text stored yet */ max_ofs_ = 0; /* we've never been sized to our contents */ last_sized_ht_ = 0; last_sized_wid_ = 0; } /* * start formatting at the top of the tag list */ void CHtmlFormatterBannerExtGrid::start_at_top(int reset_sounds) { CHtmlDispTextGrid *disp; int row; CHtmlSysFont *old_font; /* remember the old font while we're resetting the state */ old_font = get_font(); /* * We don't need to rebuild the display list the way an ordinary * formatter does, since our display list isn't affected by window * layout changes. We also don't need to worry about sounds, since * grid windows don't process sound tags at all. We do need to reset * the formatter to the initial state, though. */ reset_formatter_state(FALSE); /* if we had a font before, restore it, so that we restore the colors */ if (old_font != 0) set_font(old_font); /* * get the current font, in case it's changed, but keep the current * font color scheme */ get_grid_font(TRUE); /* start at the top margin */ curpos_.set(margin_left_, phys_margins_.top); /* run through the display list and set everything to the new font */ for (disp = (CHtmlDispTextGrid *)disp_head_, row = 0 ; disp != 0 ; disp = (CHtmlDispTextGrid *)disp->get_next_disp(), ++row) { CHtmlRect pos; /* set this item's font - this will update its size */ disp->set_font(get_win(), curfont_); /* get this item's updated size */ pos = disp->get_pos(); /* fix up this line start */ line_starts_->set_ypos(row, pos.top); /* set line positions for the item */ set_line_positions(disp, disp->get_next_disp()); /* move to the next line */ curpos_.y = disp->get_pos().bottom; curpos_.x = margin_left_; layout_max_y_pos_ = curpos_.y; } /* * If we've ever been sized to our contents, or sized to a particular * character cell size, recalculate our size at the same character cell * size. We could have a new font that changes our pixel size. */ if (last_sized_ht_ != 0 && last_sized_wid_ != 0) { HTML_BannerWin_Pos_t align; unsigned long style; CHtmlPoint pos; /* get the display item for the last row */ disp = get_disp_by_row(last_sized_ht_ - 1); /* if we didn't find the row, write some text to create one */ if (disp == 0) { /* write some text at the desired position */ write_text_at(" ", 1, last_sized_ht_ - 1, last_sized_wid_ - 1, FALSE); /* get the display item again */ disp = get_disp_by_row(last_sized_ht_ - 1); } /* if it's not wide enough, write some text to expand it */ if (disp->get_text_columns() < last_sized_wid_) disp->write_text(get_win(), curfont_, last_sized_wid_ - 1, " ", 1); /* * Now, finally, we can be sure that we actually have a display * item at the row with text at the column we're interested in. * Get the display item's position, and measure its width up to the * column of interest, and that'll give us the pixel coordinates to * use as the extent of the window. With that in hand, we can set * the new window size. */ pos = disp->get_text_pos(get_win(), disp->get_text_ofs() + last_sized_wid_); /* set the size based on the alignment type */ get_win()->get_banner_info(&align, &style); switch(align) { case HTML_BANNERWIN_POS_TOP: case HTML_BANNERWIN_POS_BOTTOM: /* top/bottom banner - set the height */ get_win()->set_banner_size(0, HTML_BANNERWIN_UNITS_PIX, FALSE, disp->get_pos().bottom, HTML_BANNERWIN_UNITS_PIX, TRUE); break; case HTML_BANNERWIN_POS_LEFT: case HTML_BANNERWIN_POS_RIGHT: get_win()->set_banner_size(pos.x, HTML_BANNERWIN_UNITS_PIX, TRUE, 0, HTML_BANNERWIN_UNITS_PIX, FALSE); break; } } } /* * Set the size */ void CHtmlFormatterBannerExtGrid::set_banner_size(int siz, int siz_units) { /* * if we're sizing in character cell units, remember the explicit size * setting so that we can adapt at the same cell-count size to any * change in our font */ if (siz_units == OS_BANNER_SIZE_ABS) { HTML_BannerWin_Pos_t align; unsigned long style; /* get our alignment */ get_win()->get_banner_info(&align, &style); /* remember the new height or width, depending on our orientation */ if (align == HTML_BANNERWIN_POS_TOP || align == HTML_BANNERWIN_POS_BOTTOM) { /* top/bottom - the size is our height */ last_sized_ht_ = siz; } else { /* left/right - the size is our width */ last_sized_wid_ = siz; } } /* inherit the default handling */ CHtmlFormatterBannerExt::set_banner_size(siz, siz_units); } /* * Get the default font for the grid */ void CHtmlFormatterBannerExtGrid::get_grid_font(int keep_color) { CHtmlFontDesc desc; /* use the window's default fixed-pitch font at the default size */ desc.htmlsize = 3; desc.fixed_pitch = TRUE; /* * if we're keeping the existing color scheme, and we already have a * font, get the colors from the current font */ if (keep_color && get_font() != 0) { CHtmlFontDesc old_desc; /* get the current font's description */ get_font()->get_font_desc(&old_desc); /* copy the text foreground color from the old description */ desc.color = old_desc.color; desc.default_color = old_desc.default_color; /* copy the text background color from the old description */ desc.bgcolor = old_desc.bgcolor; desc.default_bgcolor = old_desc.default_bgcolor; } /* get the font, if we have a window available */ if (get_win() != 0) curfont_ = get_win()->get_font(&desc); } /* * Get the display item for a given row number */ CHtmlDispTextGrid *CHtmlFormatterBannerExtGrid::get_disp_by_row(int row) const { /* * The items are in the line starts array by row number, so simply use * the row as an index into the line starts. If there is no such line * start, there's no such line. */ return (CHtmlDispTextGrid *)line_starts_->get(row); } /* * add source text for formatting */ void CHtmlFormatterBannerExtGrid::write_text_at( const textchar_t *txt, size_t len, int row, int col, int move_cursor) { CHtmlDispTextGrid *fix_disp; CHtmlDispTextGrid *disp; const textchar_t *start; unsigned long ofs; /* if we have no window, ignore it */ if (get_win() == 0) return; /* presume we won't have to fix up any row offsets */ fix_disp = 0; /* keep going until we run out of text to add */ for (start = txt ; ; ++txt, --len) { /* * If we're out of text, or this is a newline or return character, * add the text up to this point. If we don't have anything in the * preceding chunk (i.e., txt == start), then there's nothing to * flush at this point. */ if ((len == 0 || *txt == '\n' || *txt == '\r') && txt != start) { /* * First, find the display item for the current row. If this * past the last row for which we have a display item, add new * display items up to this row. */ if (row >= row_cnt_) { int cur_row; /* add new rows until we get to the desired item */ for (cur_row = row_cnt_ ; cur_row <= row ; ++cur_row) { /* create the new display item */ disp = new (this) CHtmlDispTextGrid( get_win(), curfont_, max_ofs_++); /* add the item to our list */ add_to_disp_list(disp); /* set the line positions for the item */ set_line_positions(disp, 0); /* add a line start for the item */ add_line_start(disp); /* set curpos_ to the bottom of this item */ curpos_.x = margin_left_; curpos_.y = disp->get_pos().bottom; /* count the new row */ ++row_cnt_; } } else { /* get the display item for the given row */ disp = get_disp_by_row(row); } /* if we have a display item, add the text to the item */ if (disp != 0 && disp->write_text(get_win(), curfont_, col, start, txt - start) && fix_disp == 0) { /* * Writing this text expanded the row. We must fix up the * offsets for all subsequent display items accordingly. * Don't do this immediately, because we might still have * more to write that will change even more; so, simply * remember this item for later. */ fix_disp = disp; } /* adjust the column position */ col += (txt - start); /* if this is the highest column so far, note the new maximum */ if (col + (txt - start) > col_cnt_) col_cnt_ = col + (txt - start); } /* if we're out of text, we're done */ if (len == 0) break; /* check for special characters */ switch(*txt) { case '\n': /* newline - move to the first column of the next row */ ++row; col = 0; /* the next chunk starts after the newline */ start = txt + 1; break; case '\r': /* carriage return - move to the start of this row */ col = 0; /* the next chunk starts after the return character */ start = txt + 1; break; default: /* other characters are simply displayed */ break; } } /* * if we expanded any of the lines we wrote, we must fix up the text * offsets for all subsequent lines to account for the new text offsets * in the expanded lines */ if (fix_disp != 0) { /* scan lines, starting at the first one we expanded */ for (disp = fix_disp, ofs = disp->get_text_ofs() ; disp != 0 ; disp = (CHtmlDispTextGrid *)disp->get_next_disp()) { /* set this item's new text offset */ disp->set_text_ofs(ofs); /* adjust the offset by this item's width */ ofs += disp->get_text_columns(); } /* remember the new maximum offset */ max_ofs_ = ofs; } /* move the cursor to the new position if desired */ if (move_cursor) { /* remember the new row and column */ csr_row_ = row; csr_col_ = col; } } /* * clear the contents */ void CHtmlFormatterBannerExtGrid::clear_contents() { CHtmlSysFont *old_font; /* invalidate the entire window */ inval_window(); /* reset the display list */ reset_disp_list(); /* remember the old font before resetting the formatter state */ old_font = get_font(); /* * reset formatter state - since we don't have any sounds, there's no * need to reset anything in the sound queues */ reset_formatter_state(FALSE); /* if we had a font previously, restore it, so we can keep its colors */ if (old_font != 0) set_font(old_font); /* get the correct current font, keeping the current color scheme */ get_grid_font(TRUE); /* notify the window that we're clearing it */ if (win_ != 0) win_->notify_clear_contents(); /* move the output position to the upper left corner */ csr_row_ = 0; csr_col_ = 0; /* we no longer have any content */ row_cnt_ = 0; col_cnt_ = 0; /* we don't have any text stored any more */ max_ofs_ = 0; } /* * get the character at the given text offset */ textchar_t CHtmlFormatterBannerExtGrid::get_char_at_ofs( unsigned long ofs) const { /* find the display item containing the offset */ CHtmlDispTextGrid *disp = (CHtmlDispTextGrid *)find_by_txtofs(ofs, TRUE, FALSE); /* if we didn't find a display item for the offset, there's no text */ if (disp == 0) return '\0'; /* get the character at the given offset from the display item */ return disp->get_char_at_ofs(ofs); } /* increment a text offset by one character */ unsigned long CHtmlFormatterBannerExtGrid::inc_text_ofs_char( oshtml_charset_id_t cs, unsigned long ofs) const { /* find the display item containing the offset */ CHtmlDispTextGrid *disp = (CHtmlDispTextGrid *)find_by_txtofs(ofs, TRUE, FALSE); /* let it do the work */ return disp != 0 ? disp->inc_text_ofs_char(cs, ofs) : ofs + 1; } /* decrement a text offset by one character */ unsigned long CHtmlFormatterBannerExtGrid::dec_text_ofs_char( oshtml_charset_id_t cs, unsigned long ofs) const { /* find the display item containing the offset */ CHtmlDispTextGrid *disp = (CHtmlDispTextGrid *)find_by_txtofs(ofs, TRUE, FALSE); /* let it do the work */ return disp != 0 ? disp->dec_text_ofs_char(cs, ofs) : ofs - 1; } /* is the character at the given offset a word character? */ int CHtmlFormatterBannerExtGrid::is_word_char_at_ofs( oshtml_charset_id_t cs, unsigned long ofs) const { /* find the display item containing the offset */ CHtmlDispTextGrid *disp = (CHtmlDispTextGrid *)find_by_txtofs(ofs, TRUE, FALSE); /* * if there's no display item, there's no text there, so it can't be a * word character */ if (disp == 0) return FALSE; /* ask the display item to make the determination */ return disp->is_word_char_at_ofs(cs, ofs); } /* * Internal service routine: translate a color from an os_color_t to an * HTML_color_t value. */ HTML_color_t CHtmlFormatterBannerExtGrid::xlat_color(os_color_t oscolor) { /* check for parameterized colors */ if (os_color_is_param(oscolor)) { HTML_color_t hc; /* map to the appropriate system color */ switch(oscolor) { case OS_COLOR_P_TRANSPARENT: /* * it doesn't matter what we use for this - it'll turn into a * default color whatever we use */ hc = HTML_COLOR_TEXT; break; case OS_COLOR_P_TEXT: hc = HTML_COLOR_TEXT; break; case OS_COLOR_P_TEXTBG: hc = HTML_COLOR_BGCOLOR; break; case OS_COLOR_P_STATUSLINE: hc = HTML_COLOR_STATUSTEXT; break; case OS_COLOR_P_STATUSBG: hc = HTML_COLOR_STATUSBG; break; case OS_COLOR_P_INPUT: hc = HTML_COLOR_INPUT; break; default: /* unknown color - use the text color */ hc = HTML_COLOR_TEXT; break; } /* map the color through the system window */ return get_win()->map_system_color(hc); } else { /* it's a simple RGB color - map to the HTML_color_t type */ return HTML_make_color(os_color_get_r(oscolor), os_color_get_g(oscolor), os_color_get_b(oscolor)); } } /* * Set the color */ void CHtmlFormatterBannerExtGrid::set_banner_text_color( os_color_t fg, os_color_t bg) { CHtmlFontDesc desc; /* if we don't have a window, ignore it */ if (get_win() == 0) return; /* get the formatter's current font description */ get_font()->get_font_desc(&desc); /* set the new foreground color */ desc.color = xlat_color(fg); desc.default_color = (fg == OS_COLOR_P_TRANSPARENT); /* set the new background color, if it's not 'transparent' */ desc.bgcolor = xlat_color(bg); desc.default_bgcolor = (bg == OS_COLOR_P_TRANSPARENT); /* set the new font */ set_font(get_win()->get_font(&desc)); } /* * Set the window background color */ void CHtmlFormatterBannerExtGrid::set_banner_screen_color(os_color_t color) { /* if we don't have a window, there's nothing we can do */ if (get_win() == 0) return; /* set the window's background color */ get_win()->set_html_bg_color(xlat_color(color), color == OS_COLOR_P_TRANSPARENT); } /* ------------------------------------------------------------------------ */ /* * Line-start table implementation */ CHtmlLineStarts::CHtmlLineStarts() { /* allocate an initial block of page pointers */ top_pages_allocated_ = 1024; second_pages_allocated_ = 0; pages_ = (CHtmlLineStartEntry **) th_malloc(top_pages_allocated_ * sizeof(CHtmlLineStartEntry *)); count_ = 0; } CHtmlLineStarts::~CHtmlLineStarts() { /* if we have a page array, delete it */ if (pages_) { size_t i; CHtmlLineStartEntry **page; /* free each second-level page array we've allocated */ for (i = 0, page = pages_ ; i < second_pages_allocated_ ; ++i, ++page) th_free(*page); /* free the top-level page array */ th_free(pages_); pages_ = 0; top_pages_allocated_ = 0; second_pages_allocated_ = 0; } } /* * Add an item at a given index */ void CHtmlLineStarts::add(long index, class CHtmlDisp *item, long ypos) { unsigned int page; unsigned int offset; /* get the location of this item */ calc_location(index, &page, &offset); /* if the second-level page isn't allocated, allocate it */ ensure_page(page); /* store it */ (*(pages_ + page) + offset)->disp = item; (*(pages_ + page) + offset)->ypos = ypos; /* note it if it's the highest index */ if (index >= count_) count_ = index + 1; } /* * Ensure that a page is allocated */ void CHtmlLineStarts::ensure_page(unsigned int page) { /* make sure the top-level array is big enough */ if (page >= top_pages_allocated_) { /* increase the master page size */ top_pages_allocated_ += 1024; /* reallocate the block */ pages_ = (CHtmlLineStartEntry **) th_realloc(pages_, (top_pages_allocated_ * sizeof(CHtmlLineStartEntry **))); } /* make sure the second-level array is allocated */ while (page >= second_pages_allocated_) { /* allocate a new page */ *(pages_ + second_pages_allocated_) = (CHtmlLineStartEntry *) th_malloc(HTML_LINE_START_PAGESIZE * sizeof(CHtmlLineStartEntry)); /* count it */ ++second_pages_allocated_; } } /* * Search for a given entry. Performs a binary search through the * items, so the search criterion must be something on which the line * start entries are sorted. */ long CHtmlLineStarts::find(const CHtmlLineStartSearcher *searcher) const { long low_index; long high_index; long cur_index; /* do a binary search for the item containing the given position */ for (low_index = 0, high_index = count_ - 1 ; ; ) { CHtmlLineStartEntry *cur_entry; CHtmlLineStartEntry *nxt_entry; /* stop if we've exhausted the list */ if (low_index > high_index) return 0; /* split the difference between the two ends of the range */ cur_index = low_index + (high_index - low_index)/2; /* get this item and the next item */ cur_entry = get_internal(cur_index); nxt_entry = (cur_index == count_ - 1 ? 0 : get_internal(cur_index + 1)); /* if this one matches, accept it */ if (searcher->is_between(cur_entry, nxt_entry)) return cur_index; /* see if we're above or below */ if (searcher->is_low(cur_entry)) { /* * We're looking for something above this position. If * we're also below the next item, this is the one they * want. If this is the last item, consider this item to be * a match, since the high end is effectively unbounded. */ if (cur_index == count_ - 1) return cur_index; /* * This one doesn't match. Move on to the next item; since * we want something above this item, only consider those * items after the current one. */ low_index = (cur_index == low_index ? low_index + 1 : cur_index); } else { /* * This item is too high -- consider only items below this * item. */ high_index = (cur_index == high_index ? high_index - 1 : cur_index); } } } /* * Find a line start item given a y position */ class CHtmlLineStartSearcherYpos: public CHtmlLineStartSearcher { public: CHtmlLineStartSearcherYpos(long ypos) { ypos_ = ypos; } int is_between(const CHtmlLineStartEntry *a, const CHtmlLineStartEntry *b) const { /* * if the desired y position is over a's y position, and either * a is the last item or the desired y position is below b's y * position, this is the one we're looking for */ return (ypos_ >= a->ypos && (b == 0 || ypos_ < b->ypos)); } int is_low(const CHtmlLineStartEntry *item) const { /* this one is low if the desired y position is higher */ return (ypos_ >= item->ypos); } private: long ypos_; }; long CHtmlLineStarts::find_by_ypos(long ypos) const { CHtmlLineStartSearcherYpos searcher(ypos); return find(&searcher); } /* * Find a line start item given a text offset */ class CHtmlLineStartSearcherTxtofs: public CHtmlLineStartSearcher { public: CHtmlLineStartSearcherTxtofs(unsigned long txtofs) { txtofs_ = txtofs; } int is_between(const CHtmlLineStartEntry *a, const CHtmlLineStartEntry *b) const { /* * if the desired offset is over a's offset, and either a is * the last item or the desired offset is below b's offset, this * is the one we're looking for */ return (txtofs_ >= a->disp->get_text_ofs() && (b == 0 || txtofs_ < b->disp->get_text_ofs())); } int is_low(const CHtmlLineStartEntry *item) const { /* this one is low if the desired offset is higher */ return (txtofs_ >= item->disp->get_text_ofs()); } private: unsigned long txtofs_; }; long CHtmlLineStarts::find_by_txtofs(unsigned long txtofs) const { CHtmlLineStartSearcherTxtofs searcher(txtofs); return find(&searcher); } /* * Find a line start item given a debugger source file position */ class CHtmlLineStartSearcherDebugpos: public CHtmlLineStartSearcher { public: CHtmlLineStartSearcherDebugpos(unsigned long fpos) { fpos_ = fpos; } int is_between(const CHtmlLineStartEntry *a, const CHtmlLineStartEntry *b) const { CHtmlDispTextDebugsrc *dispa = (CHtmlDispTextDebugsrc *)a->disp; CHtmlDispTextDebugsrc *dispb = (b == 0 ? 0 : (CHtmlDispTextDebugsrc *)b->disp); /* * return true if the desired source file location is between * the two positions */ return (fpos_ >= dispa->get_debugsrcpos() && (b == 0 || fpos_ < dispb->get_debugsrcpos())); } int is_low(const CHtmlLineStartEntry *item) const { CHtmlDispTextDebugsrc *disp = (CHtmlDispTextDebugsrc *)item->disp; /* this one is low if the desired offset is higher */ return (fpos_ >= disp->get_debugsrcpos()); } private: /* debugger source file position */ unsigned long fpos_; }; long CHtmlLineStarts::find_by_debugsrcpos(unsigned long linenum) const { CHtmlLineStartSearcherDebugpos searcher(linenum); return find(&searcher); } /* ------------------------------------------------------------------------ */ /* * Flow stack implementation */ /* * push a new stack level */ void CHtmlFmtFlowStack::push(long old_margin_delta, long bottom, CHtmlTagTABLE *cur_table) { /* if we don't have any room, don't write anything */ if (sp >= sizeof(stk)/sizeof(stk[0])) return; /* record this item */ stk[sp].old_margin_delta = old_margin_delta; stk[sp].bottom = bottom; stk[sp].cur_table = cur_table; /* add it to the stack */ ++sp; } /* * pop a stack level */ void CHtmlFmtFlowStack::pop() { /* make sure we have something to pop */ if (sp == 0) return; /* remove it */ --sp; } /* * check to see if we're past the end of the current flow stack level */ int CHtmlFmtFlowStack::is_past_end(long ypos, CHtmlTagTABLE *cur_table) const { /* if we're empty, there's nothing to be past the end of */ if (is_empty()) return FALSE; /* * if we're not past the vertical extent of the last item on the * stack, we're not past this level */ if (ypos <= get_top()->bottom) return FALSE; /* * we're past the vertical extent of the most deeply nested item, but * we can only clear it if the table nesting allows it */ return can_end_item(get_top(), cur_table); } /* * Check to see if table nesting allows an item to end now. We can only * pop a level if we're not nested inside a new table started after this * level was created, because a table must be contiguously in a single * level. */ int CHtmlFmtFlowStack::can_end_item(const CHtmlFmtFlowStkItem *item, const CHtmlTagTABLE *cur_table) const { CHtmlTagTABLE *enc; /* * if there's no current table, there's obviously no new table nesting * since the floating item was created, so we can end the item */ if (cur_table == 0) return TRUE; /* * We have a current table, so we must make sure it's not nested * within the level that created this stack position. Check this by * looking up the table stack from this level's active table to see if * we can find the currently active table among this level's active * table and its enclosing tables - if so, we CAN close this level, * because we're not inside any new tables created since this level * was created. */ for (enc = item->cur_table ; enc != 0 && enc != cur_table ; enc = enc->get_enclosing_table()) ; /* * if we found the active table among the enclosing tables, we can * close the level; otherwise, we must be within a table created since * this level was created, so we cannot yet close the level */ return (enc != 0); } /* ------------------------------------------------------------------------ */ /* * Image maps */ /* * delete a map - delete all zones in the map */ CHtmlFmtMap::~CHtmlFmtMap() { while (first_zone_ != 0) { CHtmlFmtMapZone *nxt; /* remember the next zone */ nxt = first_zone_->next_; /* delete this zone */ delete first_zone_; /* advance the list */ first_zone_ = nxt; } } /* * add a zone to an image map */ void CHtmlFmtMap::add_zone(CHtmlFmtMapZone *zone) { if (last_zone_ == 0) first_zone_ = zone; else last_zone_->next_ = zone; last_zone_ = zone; } /* * find a zone containing a point */ CHtmlFmtMapZone *CHtmlFmtMap::find_zone(int x, int y, const CHtmlRect *image_bounds) const { CHtmlFmtMapZone *cur; /* go through the list looking for a matching zone */ for (cur = first_zone_ ; cur != 0 ; cur = cur->next_) { /* if this zone contains the point, return it */ if (cur->pt_in_zone(x, y, image_bounds)) return cur; } /* didn't find a matching zone */ return 0; } /* * Zone implementation */ /* * Compute a coordinate, given the COORD settinng and the image bounds. */ long CHtmlFmtMapZone::compute_coord(const struct CHtmlTagAREA_coords_t *coord, const CHtmlRect *image_bounds, int vert) { long offset; /* get the base offset from the image bounds */ offset = (vert ? image_bounds->top : image_bounds->left); /* see if it's a percentage */ if (coord->pct_) { long range; /* get the range from the image bounds */ range = (vert ? image_bounds->bottom - image_bounds->top : image_bounds->right - image_bounds->left); /* compute the value as a percentage of the range */ return offset + (long)(((double)coord->val_ * (double)range) / 100.0); } else { /* compute the value as an offset from the base */ return offset + coord->val_; } } /* * RECT shape implementation */ CHtmlFmtMapZoneRect::CHtmlFmtMapZoneRect (const struct CHtmlTagAREA_coords_t *coords, CHtmlDispLink *link) : CHtmlFmtMapZone(link) { /* save the coordinates */ memcpy(coords_, coords, sizeof(coords_)); } int CHtmlFmtMapZoneRect::pt_in_zone(int x, int y, const CHtmlRect *image_bounds) { long left, top, right, bottom; /* compute my rectangle */ left = compute_coord(&coords_[0], image_bounds, FALSE); top = compute_coord(&coords_[1], image_bounds, TRUE); right = compute_coord(&coords_[2], image_bounds, FALSE); bottom = compute_coord(&coords_[3], image_bounds, TRUE); /* see if the point is in this area */ return (x >= left && x <= right && y >= top && y <= bottom); } /* * CIRCLE shape implementation */ CHtmlFmtMapZoneCircle::CHtmlFmtMapZoneCircle (const struct CHtmlTagAREA_coords_t *coords, CHtmlDispLink *link) : CHtmlFmtMapZone(link) { /* save the x,y coordinates */ memcpy(coords_, coords, sizeof(coords_)); /* save the radius - it can't be a percentage */ radius_ = coords[2].val_; } int CHtmlFmtMapZoneCircle::pt_in_zone(int x, int y, const CHtmlRect *image_bounds) { long x0, y0; long dx, dy; long dist_squared; /* compute my origin */ x0 = compute_coord(&coords_[0], image_bounds, FALSE); y0 = compute_coord(&coords_[1], image_bounds, TRUE); /* compute the point's distance from the origin */ dx = x - x0; dy = y - y0; dist_squared = (dx * dx) + (dy * dy); /* if this is within the radius, it's in the circle */ return (dist_squared <= radius_ * radius_); } /* * POLY shape implementation */ CHtmlFmtMapZonePoly::CHtmlFmtMapZonePoly (const struct CHtmlTagAREA_coords_t *coords, int coord_cnt, CHtmlDispLink *link) : CHtmlFmtMapZone(link) { /* save the coordinates */ memcpy(coords_, coords, coord_cnt * sizeof(coords_[0])); } int CHtmlFmtMapZonePoly::pt_in_zone(int /*x*/, int /*y*/, const CHtmlRect * /*image_bounds*/) { // $$$ return FALSE; } qtads-2.1.7/htmltads/htmlfmt.h000066400000000000000000003072741265017072300163210ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/html/htmlfmt.h,v 1.3 1999/07/11 00:46:40 MJRoberts Exp $ */ /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlfmt.h - TADS HTML formatter Function Takes an HTML parse tree as input, and formats the text into a draw list. The draw list is a set of rectangular areas suitable for display. Notes Modified 09/07/97 MJRoberts - Creation */ #ifndef HTMLFMT_H #define HTMLFMT_H #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef HTMLATTR_H #include "htmlattr.h" #endif #ifndef HTMLURL_H #include "htmlurl.h" #endif #ifndef HTMLSYS_H #include "htmlsys.h" #endif /* ------------------------------------------------------------------------ */ /* * Line break state object. This structure lets each display item keep * track of the last location for a line break, so that when we exceed * the available display width we can apply a line break. */ class CHtmlLineBreak { public: CHtmlLineBreak() { clear(); } /* clear the break position */ void clear() { item_ = 0; pos_ = 0; last_char_ = 0; no_break_ = FALSE; ws_start_item_ = 0; ws_start_pos_ = 0; brk_ws_start_item_ = 0; brk_ws_start_pos_ = 0; } /* * Set the break position. If we're currently in a run of whitespace, * and the existing break position is in the same run of whitespace, * we'll ignore this new break position, so that we always break at * the start of a run of whitespace. */ void set_break_pos(class CHtmlDisp *item, void *pos) { /* * if we're in a run of whitespace, and the current break position * is in the same run of whitespace, ignore the new position, * since breaking anywhere in the run of whitespace is equivalent * to breaking at the earliest possible point in the run of * whitespace */ if (ws_start_item_ != 0 && brk_ws_start_item_ == ws_start_item_ && brk_ws_start_pos_ == ws_start_pos_) return; /* note the new break position */ item_ = item; pos_ = pos; /* note the run of whitespace containing the break position */ brk_ws_start_item_ = ws_start_item_; brk_ws_start_pos_ = ws_start_pos_; } /* note a character, for the whitespace run calculation */ void note_char(textchar_t ch, class CHtmlDisp *item, void *pos) { /* note the whitspace or non-whitespace, as appropriate */ if (is_space(ch)) note_ws(item, pos); else note_non_ws(); } /* * set the current whitespace position, if it's not already set - this * can be called each time whitespace is encountered to set the start * of a whitespace run if we're not already in a whitespace run */ void note_ws(class CHtmlDisp *item, void *pos) { /* if we're not already in a run, this is the start of a run */ if (ws_start_item_ == 0) { /* remember the given item and position as the start of a run */ ws_start_item_ = item; ws_start_pos_ = pos; } } /* * clear the whitespace run start - this can be called each time a * non-whitespace character is encountered to note that we're not in a * run of whitespace */ void note_non_ws() { ws_start_item_ = 0; } /* * Display object containing the line break. Whenever a display * object identifies a line break, it should store a pointer to * itself here, so that as soon as we exceed the display width we * can go back to the last break point. */ class CHtmlDisp *item_; /* * position pointer, for use of display object - when a display * object finds a suitable break position, it can use this to * identify the internal location of the break */ void *pos_; /* * The item and position of the run of whitespace containing the * current break position. Each time we accept a new break position, * we copy the current whitespace run start point into these members; * this way, we can tell if a subsequent break position is part of the * same run of whitespace by comparing these to the current whitespace * start point. */ class CHtmlDisp *brk_ws_start_item_; void *brk_ws_start_pos_; /* * Last character of previous item, for break purposes. Even when * we don't find any breaks in an item, the item should set this * variable so that the next item can determine whether it's * possible to break between the two items. If an item doesn't * contain text, it should set this to a character value that allows * the next item to determine if a break is allowed between the two * items; setting this to a space always allows a break, whereas * setting it to an alphabetic character prevents a break it * non-whitespace text follows. */ textchar_t last_char_; /* * No-break flag: this indicates that we have an explicitly no-break * indication just before the current position, which means that we * can't break to the left of the current position, even if the next * item would normally allow a break here. */ int no_break_; /* * Display item and position giving the start of the last run of * whitespace we encountered. We'll set these to null every time we * encounter anything other than whitespace, and set them to the * current item and position every time they're null and we find a * whitespace character. The line breaking routine can use these to * go back and remove visual trailing whitespace after breaking the * line. */ class CHtmlDisp *ws_start_item_; void *ws_start_pos_; }; /* ------------------------------------------------------------------------ */ /* * Table cell metrics measurement object. This object is used to * measure the minimum and maximum widths of items in a table cell. */ class CHtmlTableMetrics { public: /* clear metrics - set up for the start of a new cell */ void clear() { last_char_ = 0; leftover_width_ = 0; cur_line_width_ = 0; min_width_ = max_width_ = 0; no_break_ = FALSE; no_break_sect_ = FALSE; } /* * Add an indivisible item (i.e., an item that can't be broken up * across lines). If this item is wider than the widest indivisible * item so far, we'll update the minimum width to reflect this * item's width. This does not affect the total length of the * current line; the item must be separately added to the total line * width if appropriate. */ void add_indivisible(long wid) { if (wid > min_width_) min_width_ = wid; } /* * Add to the size of the current line. If this makes the current * line longer than the maximum width so far, we'll update the * maximum width to reflect the width of this line. */ void add_to_cur_line(long wid) { cur_line_width_ += wid; if (cur_line_width_ > max_width_) max_width_ = cur_line_width_; } /* * Clear the leftover width. This should be called any time an item * doesn't need to use or aggregate any space into the leftover * width. This will add any current leftover width to the current * line and indivisible widths, and clear the leftover. */ void clear_leftover() { /* add the leftover to the indivisible width and line width */ add_indivisible(leftover_width_); add_to_cur_line(leftover_width_); /* forget the leftover width */ leftover_width_ = 0; } /* * Start a new line. Resets the current line's width to zero. */ void start_new_line() { /* * if there's any leftover width, add it to both the indivisible * width and the total line width */ add_to_cur_line(leftover_width_); add_indivisible(leftover_width_); /* forget the current line width and leftover width */ cur_line_width_ = 0; leftover_width_ = 0; /* there is no previous character on the line */ last_char_ = 0; /* there's no no-break flag yet */ no_break_ = FALSE; } /* * Minimum width so far. This is the size of the largest single * item that can't be broken across lines; since this item can't be * broken, its width is the smallest we can go without clipping * anything. */ long min_width_; /* * Maximum width so far. This is the size of the longest line. * This is the widest that we'd have to go to avoid having to break * up any lines. */ long max_width_; /* * Size of the current line so far. Each time we encounter a line * break, we'll reset this to zero. */ long cur_line_width_; /* * last character of previous item, for break purposes (see the * notes on the last_char_ member of CHtmlLineBreak for details on * how this works) */ textchar_t last_char_; /* * Leftover width from previous item - this is the amount of space * left over after the last break in the previous item. If we can't * insert a break between the end of the previous item and the start * of the current item, this leftover space must be added to the * space at the start of the current item. */ long leftover_width_; /* flag: we just passed an explicit no-break item (such as an  ) */ unsigned int no_break_ : 1; /* flag: we're in a section */ unsigned int no_break_sect_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Line-start table. We need to be able to find line starts quickly, * so that we can navigate from a screen coordinate (such as might come * from a mouse click) to a display object. To facilitate quick * searching by position, we keep an array of the display items that are * first in their lines, and we keep the array in order of vertical line * position. Since the number of lines could grow quite large, we keep * a two-level array: the top-level array keeps pointers to the * second-level arrays, which in turn keep pointers to the line starts. * The second-level arrays are fixed-size, so it's a simple calculation * to determine which second-level array contains a given index, and the * offset of the line within the second-level array. On storing a new * item, we ensure that the array is large enough to hold the item, so * the caller is not responsible for doing any initialization or bounds * checking on storing a value. However, note that we don't check items * on retrieval to ensure that they're within bounds or have been * initialized; the caller is responsible for checking that items * retrieved have actually been stored. */ const int HTML_LINE_START_PAGESIZE = 1024; /* pointers in a 2nd-level page */ const int HTML_LINE_START_SHIFT = 10; /* log2(pagesize) */ struct CHtmlLineStartEntry { long ypos; class CHtmlDisp *disp; }; /* * line start searcher class */ class CHtmlLineStartSearcher { public: virtual ~CHtmlLineStartSearcher() {} /* * true -> the item we're seeking is between the two given items; b * == 0 means that a is the last entry */ virtual int is_between(const CHtmlLineStartEntry *a, const CHtmlLineStartEntry *b) const = 0; /* * true -> item is too low - we're looking for something higher */ virtual int is_low(const CHtmlLineStartEntry *item) const = 0; }; class CHtmlLineStarts { public: CHtmlLineStarts(); ~CHtmlLineStarts(); /* add an entry at a given index with a given line start position */ void add(long index, class CHtmlDisp *item, long y); /* get an entry at a given index */ class CHtmlDisp *get(long index) const { return (index >= count_ ? 0 : get_internal(index)->disp); } /* get the y position of a line */ long get_ypos(long index) const { return (index >= count_ ? 0 : get_internal(index)->ypos); } /* change the y position of a line */ void set_ypos(long index, long ypos) { if (index < count_) get_internal(index)->ypos = ypos; } /* forget about all of our entries */ void clear_count() { count_ = 0; } /* get the current count of line starts */ long get_count() const { return count_; } /* restore the count to a setting previously noted with get_count */ void restore_count(long count) { count_ = count; } /* * Get the index of the entry that contains a given y offset. This * will find the entry with the greatest y offset less than or equal * to the given y offset. */ long find_by_ypos(long ypos) const; /* * Get the index of the entry that contains a given text offset */ long find_by_txtofs(unsigned long txtofs) const; /* * Get the index of the entry at the given debugger source file * position. This is used only for debug source windows. */ long find_by_debugsrcpos(unsigned long srcfpos) const; /* * General finder routine, using the given searcher to determine the * ordering. Performs a binary search of our line start array using * the given searcher, and returns the index of the item we found. */ long find(const CHtmlLineStartSearcher *searcher) const; private: /* get the internal entry for an item */ CHtmlLineStartEntry *get_internal(long index) const { unsigned int page; unsigned int offset; calc_location(index, &page, &offset); return *(pages_ + page) + offset; } /* * calculate the second-level array containing the item, and the * item's index within the second-level array */ void calc_location(long index, unsigned int *page, unsigned int *offset) const { *page = (unsigned int)(index >> HTML_LINE_START_SHIFT); *offset = (unsigned int)(index & (HTML_LINE_START_PAGESIZE - 1)); } /* ensure that a particular page is allocated */ void ensure_page(unsigned int page); /* the top-level array */ CHtmlLineStartEntry **pages_; /* size of the top-level array */ size_t top_pages_allocated_; /* number of second-level pages allocated */ size_t second_pages_allocated_; /* * number of items stored so far (actually, the maximum index plus * one, since the caller may not fill in all items) */ long count_; }; /* ------------------------------------------------------------------------ */ /* * Flow-around item stack. A flow-around item is an item in the left or * right margin that text flows around. To control the text, we * increase the margins temporarily, until we get past the bottom of the * object in the margin. This stack tracks the objects that we're * currently flowing text around; there's one of these for each margin. */ /* flow stack item */ class CHtmlFmtFlowStkItem { public: /* * margin setting in effect before this item was pushed, stored as a * delta from the new margin set for the item */ long old_margin_delta; /* y position of bottom of this flow item */ long bottom; /* * table active when stack position was entered - we cannot leave this * stack level unless this same table is active */ class CHtmlTagTABLE *cur_table; }; /* flow stack */ class CHtmlFmtFlowStack { public: CHtmlFmtFlowStack() { /* nothing on the stack yet */ sp = 0; } /* push the current margins */ void push(long old_margin_delta, long bottom, class CHtmlTagTABLE *cur_table); /* get the item at the top of the stack */ const CHtmlFmtFlowStkItem *get_top() const { return &stk[sp - 1]; } /* determine if there's anything on the stack */ int is_empty() const { return sp == 0; } /* pop the top item */ void pop(); /* determine if we're past the end of the item at the top of the stack */ int is_past_end(long ypos, class CHtmlTagTABLE *cur_table) const; /* * determine if it's legal to end this item, based on the current * table nesting level */ int can_end_item(const CHtmlFmtFlowStkItem *item, const class CHtmlTagTABLE *cur_table) const; /* determine if it's legal to end an item given its index */ int can_end_item(int index, class CHtmlTagTABLE *cur_table) const { return can_end_item(get_by_index(index), cur_table); } /* determine if we can end the top item */ int can_end_top_item(class CHtmlTagTABLE *cur_table) const { return !is_empty() && can_end_item(get_top(), cur_table); } /* get the number of items on the stack */ int get_count() const { return sp; } /* * get an item at an index from the top - 0 is the top, 1 is the * next item, and so on */ const CHtmlFmtFlowStkItem *get_by_index(int index) const { return &stk[sp - index - 1]; } /* reset - discard all items */ void reset() { sp = 0; } private: /* stack pointer */ size_t sp; /* stack */ CHtmlFmtFlowStkItem stk[100]; }; /* * Table formatting environment snapshot. This records the necessary * information to resume formatting after finishing with a floating table. */ class CHtmlFmtTableEnvSave { public: /* left/right stack depths */ int left_count; int right_count; /* left/right margin settings at the current stack level */ long margin_left; long margin_right; /* output position and status */ CHtmlDisp *disp_tail; CHtmlDisp *line_head; long max_line_width; long avail_line_width; CHtmlPoint curpos; int line_spacing; long line_count; long line_id; long line_starts_count; int last_was_newline : 1; }; /* maximum table environment save depth */ const size_t CHTMLFMT_TABLE_ENV_SAVE_MAX = 20; /* ------------------------------------------------------------------------ */ /* * formatter heap page structure */ struct CHtmlFmt_heap_page_t { unsigned char *mem; size_t siz; }; /* ------------------------------------------------------------------------ */ /* * Display item factory. Certain types of display items come in * linkable and unlinkable varieties. When we're in a link, we need to * create a linkable type, and at other times we need to create the * normal unlinkable kind. To simplify and centralize these decisions, * we maintain a display item factory that creates the appropriate types * of objects. The current item factory should always be used when * creating an object; it will create the appropriate type of objects * for the current context. Note that most display items are not * linkable at all, so there's no need to go through the factory -- it's * only needed where both linkable and unlinkable types exist. */ class CHtmlFmtDispFactory { public: CHtmlFmtDispFactory(class CHtmlFormatter *formatter) { formatter_ = formatter; } virtual ~CHtmlFmtDispFactory() {} /* create a text display item */ virtual CHtmlDisp *new_disp_text(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) = 0; /* create an input-editor text display item */ virtual class CHtmlDispTextInput *new_disp_text_input( class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) = 0; /* create a soft-hyphen display item */ virtual class CHtmlDisp *new_disp_soft_hyphen( class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) = 0; /* create a non-breaking space display item */ virtual class CHtmlDisp *new_disp_nbsp( class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) = 0; /* create an special typographical space display item */ virtual class CHtmlDisp *new_disp_space( class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs, int wid, int pre) = 0; /* create a pre-formatted text display item */ virtual CHtmlDisp *new_disp_text_pre(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs) = 0; /* create a debugger-window text display item */ virtual CHtmlDisp *new_disp_text_debug(class CHtmlSysWin *win, class CHtmlSysFont *font, const textchar_t *txt, size_t txtlen, unsigned long txtofs, unsigned long linenum) = 0; /* create an image display item */ virtual class CHtmlDispImg *new_disp_img( class CHtmlSysWin *win, class CHtmlResCacheObject *image, CStringBuf *alt, HTML_Attrib_id_t align, long hspace, long vspace, long width, int width_set, long height, int height_set, long border, class CHtmlUrl *usemap, int ismap) = 0; protected: class CHtmlFormatter *formatter_; }; /* ------------------------------------------------------------------------ */ /* * Formatter */ /* maximum depth of margin stack */ const int MARGIN_STACK_DEPTH_MAX = 20; class CHtmlFormatter { public: CHtmlFormatter(class CHtmlParser *parser); virtual ~CHtmlFormatter(); /* * get/set T3 mode - this indicates that our caller is a T3 program, * which changes certain parsing rules */ int is_t3_mode() const { return t3_mode_; } void set_t3_mode(int f) { t3_mode_ = f; } /* * receive notification that the parser is about to be deleted; we * must release any references we have to the parser or the format * tag list (which is owned by the parser, so will be deleted when * the parser is deleted). */ virtual void release_parser() { /* forget the parser */ parser_ = 0; } /* set my window and the physical margins to use */ virtual void set_win(class CHtmlSysWin *win, const CHtmlRect *r); /* * Receive notification that my window is being deleted. By * default, I'll just forget the window. */ virtual void unset_win(); /* get my window pointer */ class CHtmlSysWin *get_win() const { return win_; } /* invalidate the whole window area */ void inval_window(); /* get the special BODY display item */ class CHtmlDispBody *get_body_disp() const{ return body_disp_; } /* get the resource cache (a global singleton) */ static class CHtmlResCache *get_res_cache() { return res_cache_; } /* get the resource finder (a global singleton) */ static class CHtmlResFinder *get_res_finder() { return res_finder_; } /* play a sound */ void play_sound(class CHtmlTagSOUND *tag, HTML_Attrib_id_t layer, class CHtmlResCacheObject *res, int repeat_count, int random_start, HTML_Attrib_id_t sequence); /* * Stop all sounds in the given layer. 'fade_out' gives the fade time: * if zero, it means that we should cancel sounds immediately, * otherwise the sounds should be faded out over the given interval in * seconds. If 'fade_in_background' is set, the fade should happen in * the background, so that new sounds can start playing in the layer * concurrently with the fade; otherwise the layer should remain * occupied until the fade is finished, at which point the next sound * in the layer's queue can begin. */ virtual void cancel_sound(HTML_Attrib_id_t layer, double fade_out, int fade_in_background, int sync); /* terminate all active playback (such as animations) */ void cancel_playback(); /* get/set the last sound tag */ virtual class CHtmlTagSOUND *get_last_sound_tag() const = 0; virtual void set_last_sound_tag(class CHtmlTagSOUND *tag) = 0; /* do we allow tags in this window? */ virtual int allow_sound_tags() const = 0; /* * Flush the text buffer. By default, we do nothing, since the basic * formatter doesn't own its own text buffer. This can be overridden * for formatter subclasses that do own an independent text buffer. */ virtual void flush_txtbuf(int /*fmt*/) { } /* * Add an extra line's worth of vertical whitespace to the display * height, beyond what's strictly needed for the layout. This can be * used to provide visual feedback after a user presses Enter at the * end of an input line. We simply add one line height worth of space * to the display height and refigure the scrollbar positions, which * will generally cause the display window to scroll one line. */ void add_line_to_disp_height(); /* * Set the "physical" margins. The window should call this if it * wants to specify an inset from the window coordinates, to provide * some whitespace around the edges of the window. The bottom and * right coordinates in the rectangle are inset amounts, so they * will usually be small positive numbers (.right probably will * equal .left). Note that the window generally won't need to * change this after it's initially set up, which happens when it * plugs in to the formatter with set_win(). */ void set_phys_margins(const CHtmlRect *r); /* get the physical margin settings */ CHtmlRect get_phys_margins() const { return phys_margins_; } /* set the physical margins */ void set_phys_margins(long left, long top, long right, long bottom) { phys_margins_.set(left, top, right, bottom); } /* * Re-start rendering at the top of the document. This should be * called whenever the current formatting becomes invalid, such as * when the window is resized horizontally. * * If reset_sounds is true, we'll stop all currently playing sounds * and start over with the tags that we encounter in the * course of formatting the page. If reset_sounds is false, we'll * leave current sounds playing and ignore all tags that we * encounter. reset_sounds should be false whenever we're * reformatting the current page (for example, after resizing the * window or changing the default font size), and should be true * when switching to a new page. */ virtual void start_at_top(int reset_sounds); /* * Do some more formatting. This routine does a little work and * then returns, so that an interactive program can check for events * and respond to user actions while formatting is in progress. A * non-interactive program can simply call this routine repeatedly * as long as more_to_do() returns true. An interactive program can * monitor the progress of the formatting with getcurpos(); * normally, an interactive program will want to do formatting until * it has enough to fill the current window (or until the formatting * is done, whichever comes first), then process user events while * formatting any additional text in the background, since further * text is out of view at the moment. */ void do_formatting(); /* do we have more rendering left to do? */ int more_to_do(); /* get the current rendering position */ CHtmlPoint getcurpos() const { return curpos_; } /* * get the maximum y position so far (i.e., the display height of the * page) */ unsigned long get_max_y_pos() const { return disp_max_y_pos_; } /* get the maximum y position assigned to a layout item so far */ unsigned long get_layout_max_y_pos() const { return layout_max_y_pos_; } /* * Increase the page height by a given amount (which can be negative * to decrease the position). This should be used only for temporary * adjustments to the scrolling area during times when new text won't * be added, since the new y position will affect future text * formatting if not undone. */ void inc_max_y_pos(long amount) { disp_max_y_pos_ += amount; } /* * Add an item to the tail of the display list, adjust the current * line under construction to compensate for the new item. */ virtual void add_disp_item(class CHtmlDisp *item); /* * Add a link display item. To enter a hypertext link (), * create a link item and call this routine. Subsequent linkable * items will refer back to this link object. Call end_link() when * the matching is reached. */ void add_disp_item_link(class CHtmlDispLink *link); /* end the current link (opened with add_disp_item_link()) */ void end_link(); /* * Get/set character-wrapping mode. The default is word-wrap mode; * when character-wrap mode is off, word-wrap mode is selected. */ int is_char_wrap_mode() const { return char_wrap_mode_; } void set_char_wrap_mode(int f) { /* remember the new wrap mode */ char_wrap_mode_ = f; /* set the correct factory based on the new mode */ adjust_disp_factory(); } /* get the current link item */ class CHtmlDispLink *get_cur_link() const { return cur_link_; } /* get the current display item factory */ CHtmlFmtDispFactory *get_disp_factory() const { return cur_disp_factory_; } /* * Add an item to the tail of the display list, replacing the * current command input display item(s), if any. This is used * during editing of a command line to update the display after the * user has made a change in the contents of the command line. * Default formatters are not input-capable, so we simply add the * display item as though it were any other display item. * Input-capable formatter subclasses should override this to * provide proper input editing formatting. */ virtual void add_disp_item_input(class CHtmlDispTextInput *item, class CHtmlTagTextInput *); /* * Add a display item that floats to the left or right margin with * text flowing around it. We can't add this type of item * immediately; instead, we need to wait until we start a new line, * at which point we can add the item and adjust the margins so that * text flows around the item. This routine adds the item to an * internal list of items to be deferred until the start of the next * line. */ void add_disp_item_deferred(class CHtmlDisp *item); /* Add an item, starting a new line with the item */ void add_disp_item_new_line(class CHtmlDisp *item); /* Add an item, starting a new line immediately after the item. */ void add_disp_item_new_line_after(class CHtmlDisp *item); /* Add a
*/ void begin_div(class CHtmlDispDIV *div); /* end the current
*/ void end_div(); /* define a tab stop at the current position */ void set_tab(const textchar_t *tab_id, HTML_Attrib_id_t align, textchar_t dp); /* tab to a previously-defined tab stop */ void tab_to(class CHtmlTagTAB *tag); /* * Add minimum line spacing. This makes sure that we're starting a * new line, and that the line spacing for the new line is at least * as much as requested. If we're already on a new line and have * provided the requested amount of inter-line spacing, this doesn't * do anything. */ void add_line_spacing(int spacing); /* * Add vertical whitespace as needed to move past floating items in * one or both margins. Returns true if any line spacing was added, * false if not. */ int break_to_clear(int left, int right); /* get current font */ class CHtmlSysFont *get_font() const { return curfont_; } /* set a new font */ void set_font(class CHtmlSysFont *font) { curfont_ = font; } /* get/set the current base font size */ int get_font_base_size() const { return font_base_size_; } void set_font_base_size(int siz) { font_base_size_ = siz; } /* * Draw everything in a given area. If clip_lines is false, we'll * draw the last line even if it doesn't fit entirely in the area, * otherwise we'll stop when we reach a line that doesn't entirely * fit. If clip_lines is true, we'll return in clip_y the y * position of the first line we didn't draw. */ void draw(const CHtmlRect *area, int clip_lines, long *clip_y); /* * Invalidate links that are visible on the screen. This is used to * quickly redraw the window's links (and only its links) for a * short-term change in the link visibility state. This can be used * to implement a mode where links are highlighted only if a key is * held down. */ void inval_links_on_screen(const CHtmlRect *area); /* invalidate the display area occupied by the selected text */ void inval_sel_range() { /* if the selection range isn't empty, invalidate it */ if (sel_start_ != sel_end_) inval_offset_range(sel_start_, sel_end_); } /* get the maximum line width */ long get_max_line_width() const { return max_line_width_; } /* * Get the maximum line width at the outer level. If we're inside a * table, we'll return the outermost enclosing max line width; * otherwise, we'll return the current maximum line width. The * outer maximum line width is useful for calculating the horizontal * scrollbar range; the current line width is not interesting for * scrollbars if it is ultimately wrapped within a table. */ long get_outer_max_line_width() const { return outer_max_line_width_; } /* * Extract text from the text stream into the given buffer. Pulls the * text from the given starting text offset to the given ending text * offset. */ void extract_text(CStringBuf *buf, unsigned long start_ofs, unsigned long end_ofs) const; /* get the height of the line at the given y position */ long get_line_height_ypos(long ypos) const; /* * Get the position of a character, given the text offset in the * character array */ CHtmlPoint get_text_pos(unsigned long txtofs) const; /* * Get the text offset, given a position in doc coordinates */ unsigned long find_textofs_by_pos(CHtmlPoint pos) const; /* * Given a text offset, get the extent of the word containing the * offset */ void get_word_limits(unsigned long *start, unsigned long *end, unsigned long txtofs) const; /* * Given a text offset, get the extent of the line containing the * offset. If the offset is in the current input line, we'll get * the limits of the input line, not including any prompt that * appears on the same line. */ void get_line_limits(unsigned long *start, unsigned long *end, unsigned long txtofs) const; /* * Find an object by position. If 'exact' is true, we'll return null * if we don't find an item that actually contains the given item; * otherwise, we'll return the item at the start of the next line after * the given position. */ class CHtmlDisp *find_by_pos(CHtmlPoint pos, int exact) const; /* * Find the
tag that covers the area containing the given * position. */ class CHtmlDispDIV *find_div_by_pos(CHtmlPoint pos) const; /* * Find a
tag by text offset. The vertical coordinate must be * provided because the text offset isn't quite precise enough - it * could lead us to include areas to the right of the line above the * start of the division's vertical extent. */ class CHtmlDispDIV *find_div_by_ofs(unsigned long txtofs, long y) const; /* * Find an object by text offset. If we don't find an item * containing the exact position, we'll check use_prv and use_nxt: * if use_prv is true, we'll return the last item on the previous * line; otherwise, if use_nxt is true, we'll return the first item * on the next line; otherwise we'll return null. */ class CHtmlDisp *find_by_txtofs(unsigned long ofs, int use_prv, int use_nxt) const; /* get the character set at a given text position */ oshtml_charset_id_t get_charset(unsigned long ofs) const; /* * Find an object given a debugger source file seek position. This * is used only for debugger source windows. */ class CHtmlDisp *find_by_debugsrcpos(unsigned long srcfpos) const; /* * Get the line number given the debugger source position */ long findidx_by_debugsrcpos(unsigned long srcpos) const; /* * Get the item given a line number */ class CHtmlDisp *get_disp_by_linenum(long linenum) const; /* get the first display item in the display list */ class CHtmlDisp *get_first_disp() const { return disp_head_; } /* set the selection range given the starting and ending text offsets */ void set_sel_range(unsigned long start, unsigned long end); /* * Set the selection range given the positions of the starting and * ending points. If startofs or endofs are non-null, we'll fill * these in with the text offsets of the start and end points; these * will correspond to the start and end positions given, so they * will not necessarily be in canonical order. */ void set_sel_range(CHtmlPoint start, CHtmlPoint end, unsigned long *startofs, unsigned long *endofs); /* get the selection range */ void get_sel_range(unsigned long *start, unsigned long *end) { *start = sel_start_; *end = sel_end_; } /* * enter/exit capture mode - this captures (or ignores) the * contents of the tag */ virtual void begin_title() { title_mode_ = TRUE; } virtual void end_title(class CHtmlTagTITLE *) { title_mode_ = FALSE; } /* * Enter/exit a MAP. When the map is open, we can add AREA values * to the map. */ void begin_map(const textchar_t *map_name, size_t name_len); void end_map(); /* * Add a hot spot area to the current map. If href is null, it * indicates that the hot spot is a NOHREF area. 'append' and * 'noenter' specify the APPEND and NOENTER attribute settings for * the link. */ void add_map_area(HTML_Attrib_id_t shape, CHtmlUrl *href, const textchar_t *alt, size_t altlen, const struct CHtmlTagAREA_coords_t *coords, int coord_cnt, int append, int noenter); /* get a map given a map name */ class CHtmlFmtMap *get_map(const textchar_t *mapname, size_t namelen); /* * Begin a table. Returns the enclosing table. The caller is * responsible for restoring the enclosing table with a call to * end_table() when this table is finished. *enclosing_table_pos * will be filled in with the position of the enclosing table; this * must be passed to end_table_set_size() to restore the enclosing * table position at the end of the table. */ class CHtmlTagTABLE *begin_table(class CHtmlTagTABLE *table_tag, class CHtmlDispTable *table_disp, CHtmlPoint *enclosing_table_pos, class CHtmlTagCAPTION *caption_tag); /* * End the current table, and restore the enclosing table (which was * returned from the earlier corresponding call to begin_table()). */ void end_table(class CHtmlDispTable *table_disp, class CHtmlTagTABLE *enclosing_table, const CHtmlPoint *enclosing_table_pos, class CHtmlTagCAPTION *caption_tag); /* * Set the total size of the current table. The <TABLE> tag will * call this after it's calculated the total size of the table. For * the outermost table, we'll use this to set the position of the * next item after the table. enclosing_table_pos is the position * of the enclosing table that was returned from the corresponding * call to begin_table(). */ void end_table_set_size(class CHtmlTagTABLE *enclosing_table, long height, const CHtmlPoint *enclosing_table_pos); /* * Begin a table cell. On table pass 2, this will set up the * formatter's margins according to the column settings. */ void begin_table_cell(class CHtmlDispTableCell *cell_disp, long cell_x_offset, long content_x_offset, long content_width); /* * Prepare to close out a table cell. This will add any pending * deferred items immediately, and clear any marginal floating * items. */ void pre_end_table_cell(); /* * End the current table cell. On table pass 1, this will calculate * the minimum and maximum width of the cell's contents, and tell * the cell about it. */ void end_table_cell(class CHtmlTagTableCell *cell_tag, class CHtmlDispTableCell *cell_disp); /* * Set the y positions of the contents of a table cell. * row_y_offset and cell_y_offset are offsets from the top of the * table. base_y_pos is the original base position of the cell; the * vertical offsets of the cell's contents from this position are * preserved, so that the cell contents end up at the same offset * from the new position of the cell. disp_first and disp_last are * the first and last display items in the cell; disp_first must be * the display item for the cell itself. */ void set_table_cell_y_pos(class CHtmlDisp *disp_first, class CHtmlDisp *disp_last, long cell_y_offset, long base_y_pos, long row_y_offset); /* * freeze display adjustment - this can be used when a large * formatting job is about to be done, and the caller wants to wait * until the whole job is done to redraw the display */ void freeze_display(int flag) { freeze_display_adjust_ = flag; } /* check if we're at the beginning of a line */ int last_was_newline() { return last_was_newline_; } /* * Start text flow around the given item. The new margins remain in * effect until we reach the y position given by flow_bottom. The * item must not be part of the display list when this is called. */ void begin_flow_around(class CHtmlDisp *item, long new_left_margin, long new_right_margin, long flow_bottom); /* * Push a margin change. The margins will be set to the new sizes; * note that the right margin is an amount to inset the margin from * the right. These margins will remain in effect until the margin * settings are popped. */ void push_margins(long left, long right); /* * Set temporary margins. The margins set here will be in effect * until the end of the current line, at which time we'll revert to * the normal margins. This can be used to indent the first line of * a paragraph, or un-indent the bullet of a list item. */ void set_temp_margins(long left, long right); /* * Restore margins that were in effect before the most recent call * to push_margins(). These calls nest, two pushes must be matched * by two pops. */ void pop_margins(); /* get the current left and right margin settings */ long get_left_margin() const { return margin_left_; } long get_right_margin() const { return margin_right_; } /* * Get the current top and bottom margin settings. Note that we * don't have our own vertical margins; these are simply the * "physical" vertical insets that the window says we should use. */ long get_top_margin() const { return phys_margins_.top; } long get_bottom_margin() const { return phys_margins_.bottom; } /* turn line wrapping on or off */ void set_line_wrapping(int wrap) { wrap_lines_ = wrap; } /* get line wrapping state */ int get_line_wrapping() { return wrap_lines_; } /* * Get/set the division alignment. The enclosing division * alignment is used to align a block element whenever the block * element doesn't specify an explicit alignment of its own. */ HTML_Attrib_id_t get_div_alignment() const { return div_alignment_; } void set_div_alignment(HTML_Attrib_id_t align) { div_alignment_ = align; } /* * Get/set the block alignment. This overrides the division * alignment if set to a valid value. */ HTML_Attrib_id_t get_blk_alignment() const { return blk_alignment_; } void set_blk_alignment(HTML_Attrib_id_t align) { blk_alignment_ = align; } /* * Start formatting a banner. Default formatters can't create * banners, so this routine simply ignores the request, which will * cause the banner contents to appear in the same window as their * surrounding markups. If height_set is true, the banner height * should be set to the given height; otherwise, if height_prev is * true, the banner height should be set to the same height it had * previously. If neither height_set nor height_prev is true, or * height_prev is true but there is no previous instance of the * banner, the banner is given the same height as its contents. * Returns true if the banner was successfully formatted, false if * not. */ virtual int format_banner(const textchar_t * /*banner_id*/, long /*height*/, int /*height_set*/, int /*height_prev*/, int /*height_pct*/, long /*width*/, int /*width_set*/, int /*width_prev*/, int /*width_pct*/, class CHtmlTagBANNER *) { /* by default, we can't handle banners */ return FALSE; } /* * Receive notification that we've reached the end of the banner. * By default, we ignore this, since the default formatter isn't * itself a banner. */ virtual void end_banner() { } /* * Remove a banner. By default, we don't support banners, so there * can't be anything to remove. */ virtual void remove_banner(const textchar_t * /*banner_id*/) { } /* * Remove all in-line banners. This should be used before switching to * another history page, since each history page may have a unique set * of banners. The default implementation does nothing, since default * formatters don't support banners at all. */ virtual void remove_all_banners(int /*replacing*/) { } /* * Set/get the continuation sequence number for ordered lists. This * allows a new ordered list to pick up numbering where the previous * ordered list left off. */ void set_ol_continue_seqnum(long val) { ol_cont_seqnum_ = val; } long get_ol_continue_seqnum() const { return ol_cont_seqnum_; } /* * Formatter heap management. The formatter maintains its own heap * for allocating memory for display items, since display items have * particular behavior that we can exploit to achieve a more * efficient heap implementation. In particular, the display list * can be treated as a single large chunk of memory, so we can have * a very simple allocator that doesn't allow a chunk to be * individually deleted, thus saving the overhead the * general-purpose allocator needs to keep track of individual * allocation units. */ void *heap_alloc(size_t siz); /* get my "stop" flag */ int get_stop_formatting() const { return stop_formatting_; } /* * Add one or more items to the internal display list, without any * formatting. If more items are linked after the given item, they'll * be included in the display list. */ void add_to_disp_list(class CHtmlDisp *item); /* -------------------------------------------------------------------- */ /* * Text array access methods. Rather than exposing our text array * directly, we provide our own virtual interface to the text array. * This allows subclasses that don't use text array objects to provide * their own implementations of the text access methods. */ /* * Get the number of characters between two text offsets. Offsets are * not necessarily contiguous (i.e., there might be gaps), so this * routine can be used to find out how many characters are actually * stored in the given offset range. */ virtual unsigned long get_chars_in_ofs_range( unsigned long startofs, unsigned long endofs) const; /* get the character at the given text offset */ virtual textchar_t get_char_at_ofs(unsigned long ofs) const; /* is the character at the given offset a word character? */ virtual int is_word_char_at_ofs( oshtml_charset_id_t cs, unsigned long ofs) const; virtual int is_word_char_at_ofs(unsigned long ofs) { return is_word_char_at_ofs(get_charset(ofs), ofs); } /* * Increment/decrement a text offset by one byte. Since text offsets * are not necessarily continuous (i.e., gaps can exist, so two * adjacent characters might have text offsets that differ by more than * 1), these routines should always be used to traverse the display * list's text. * * This function operates on bytes, so it can leave the offset pointing * into the middle of a multi-byte character (e.g., it could leave the * offset pointing at the second byte of a three-byte UTF-8 character). */ virtual unsigned long inc_text_ofs(unsigned long ofs) const; virtual unsigned long dec_text_ofs(unsigned long ofs) const; /* * Increment/decrement a text offset by a whole character. These work * like inc/dec_text_ofs() but operates on characters rather than * bytes. If the next/previous character is a multi-byte character * (e.g., a three-byte UTF-8 sequence), this skips all of the bytes of * the character. This ensures that the new offset points to the lead * byte of a whole character, never at the middle byte of a multi-byte * character. The underlying text is assumed to be in the system local * character set, as parsed by os_next_char() and os_prev_char(). */ virtual unsigned long inc_text_ofs_char( oshtml_charset_id_t cs, unsigned long ofs) const; virtual unsigned long dec_text_ofs_char( oshtml_charset_id_t cs, unsigned long ofs) const; /* * get the maximum text offset */ virtual unsigned long get_text_ofs_max() const; /* * Get a pointer to the text starting at the given offset. The * returned text is not guaranteed to hold more than the text of a * single span of text or of a single tag. * * This routine should be used ONLY by tags in the course of formatting * themselves. For callers needing to obtain a run of text from the * display list, use extract_text(). */ virtual const textchar_t *get_text_ptr(unsigned long ofs) const; /* determine if searching is possible in this display list */ virtual int can_search_for_text() const { return TRUE; } /* search for text in the display list */ virtual int search_for_text(const textchar_t *txt, size_t len, int exact_case, int whole_word, int wrap, int dir, unsigned long startofs, unsigned long *match_start, unsigned long *match_end); protected: /* get our text array */ class CHtmlTextArray *get_text_array() const; /* adjust the display factory for the current mode settings */ void adjust_disp_factory(); /* * Internal service routines to reset formatting. start_at_top() * normally calls both of these, but some subclasses might want just * the display list clearing or just the internal state reset, so we * provide this separation of operations. */ void reset_disp_list(); void reset_formatter_state(int reset_sounds); /* * Insert a table's caption here if we're on the proper side of the * table */ void check_format_caption(class CHtmlTagCAPTION *caption, class CHtmlDispTable *table_disp, int top); /* remove items from the display list starting at the given item */ void remove_from_disp_list(long search_start_line, class CHtmlDisp *first_item_to_remove); /* add an item as a new line to the line starts table */ void add_line_start(class CHtmlDisp *item); /* calculate the space available between the margins */ long get_space_between_margins(); /* add an item that was previously deferred to the display list */ void deferred_item_ready(class CHtmlDisp *disp); /* add all of the deferred items to the display list */ void add_all_deferred_items(class CHtmlDisp *prv_line_head, class CHtmlDisp *nxt_line_head, int force); /* * Check to see if we're past the end of the flow items, and restore * enclosing margins if so. Returns true if any flow items ended, * false if not. */ int check_flow_end(); /* * Try to free up a given amount of width in the current line by * breaking clear of marginal material. Returns true if any changes * are made to the margins, whether or not the requested amount of * space is made available. */ int try_clearing_margins(long width); /* set margins and adjust available line width for the change */ void set_margins(long left, long right); /* invalidate a range of text given by offsets */ void inval_offset_range(long l, long r); /* * Find a text position for a given text offset by searching the * display list, starting at a particular item in the list. */ CHtmlPoint get_text_pos_list(class CHtmlDisp *start, unsigned long txtofs) const; /* * Start a new line. If a new line head is given, we'll break the * line so that the new line head is at the start of the next line, * otherwise we'll break the line starting with the next item added * to the display list (hence all items in the current display list * from the start of the current line will be in the current line). * */ void start_new_line(CHtmlDisp *next_line_head); /* * Close a pending tab -- align text between the previous tab stop * and the current position. Call this whenever a new tab is * encountered, or the end of a line is reached. */ void close_pending_tab(); /* delete the display list */ virtual void delete_display_list(); /* set positions for objects in a newly set line */ void set_line_positions(CHtmlDisp *line_head, CHtmlDisp *next_line_head); /* determine if a character is part of a word, punctuation, or space */ int get_char_class(textchar_t c) const { if (is_alpha(c) || is_digit(c) || c == '\'' || c == '-' || c == '_') { /* part of a word */ return 1; } else if (is_space(c)) { /* whitespace */ return 2; } else { /* punctuation/other */ return 3; } } /* move forward or backward to a word boundary */ unsigned long find_word_boundary(unsigned long start, int dir) const; /* * Determine if we're in a multi-column mode. If we have a * flow-around item in either margin, we're in a multi-column mode. */ int is_multi_col() const { /* if there are any flow-around items, treat as multi-column */ if (!left_flow_stk_.is_empty() || !right_flow_stk_.is_empty()) return TRUE; /* tables are always multi-column */ if (table_pass_ != 0) return TRUE; /* not multi-column */ return FALSE; } /* * Run the second table formatting pass on the current table */ void run_table_pass_two(class CHtmlTagTABLE *table_tag); /* * Push/pop a table formatting environment. This allows us to save * the environment at the start of a floating table so that we can * pick up where we left off when we finish with the table. */ void push_table_env(); void pop_table_env(); /* parser */ class CHtmlParser *parser_; /* system window object into which we'll draw the formatted results */ class CHtmlSysWin *win_; /* my special "body" pseudo-display item */ class CHtmlDispBody *body_disp_; /* * Current font. Note that we never "own" a font object -- we are * not responsible for deleting the object. The window must keep * track of font objects and delete them when the window closes. * The window should take care not to delete any font objects that * it gives to the formatter as long as the formatter is around. */ class CHtmlSysFont *curfont_; /* * Base font size. Relative font size settings are given as * amounts above or below this base font size setting. */ int font_base_size_; /* horizontal space remaining in current line */ long avail_line_width_; /* flag indicating that we've finished building a new line */ int line_output_ : 1; /* current margins, as offsets from the bounds of the window */ long margin_left_; long margin_right_; /* * Margin deltas to put into effect at the end of the current line. * When temporary margins are in effect, these members store the * amounts to add to the current margins to restore at the end of * the current line, when the temporary margins go out of scope. */ long margin_left_delta_nxt_; long margin_right_delta_nxt_; /* flag indicating that temporary margins are in effect */ int temp_margins_ : 1; /* current margin stack depth - each push increases the depth by one */ int margin_stack_depth_; /* margin stack */ struct margin_stack_t { long left_delta_; long right_delta_; void set(long left_delta, long right_delta) { left_delta_ = left_delta; right_delta_ = right_delta; } } margin_stack_[MARGIN_STACK_DEPTH_MAX]; /* * division alignment type - left, center, right, justify, or * invalid (which indicates that the default should be used) */ HTML_Attrib_id_t div_alignment_; /* * block alignment - this overrides the division alignment, if set * to a valid value */ HTML_Attrib_id_t blk_alignment_; /* * "physical" margins, specified by the window; we'll always set * our layout inset from the window coordinates by these amounts */ CHtmlRect phys_margins_; /* current rendering position */ CHtmlPoint curpos_; /* * highest y position for display purposes - this is the display * height of the page */ unsigned long disp_max_y_pos_; /* * highest y position for layout purposes - this is the maximum y * position we've assigned to a display item */ unsigned long layout_max_y_pos_; /* display list head and tail */ class CHtmlDisp *disp_head_; class CHtmlDisp *disp_tail_; /* * Deferred item list head and tail. Certain items can't be added * to the display list immediately when encountered in the parse * tree, but must instead be added at the start of the next line. * When we encounter such an item, we'll put it into this deferred * item list; then, whenever we start a new line, we'll put all the * items in the deferred item list into the display list. */ class CHtmlDisp *defer_head_; class CHtmlDisp *defer_tail_; /* * suppress scrolling and window invalidation while formatting * lines -- normally, we scroll and update the window as we display * text, but while formatting input we save updates until we're done * formatting the entire input, to avoid flashing the screen */ int freeze_display_adjust_ : 1; /* * First display element in the current line. This points to an * item in the main display list; as we build a line, we keep this * pointed at the first element in the current line so that we can * go back through the line and position everything correctly once * we're done formatting the entire line. */ class CHtmlDisp *line_head_; /* number of lines we've stored */ long line_count_; /* * ID of line - this is an arbitrary designation that we use to * distinguish adjacent items on different lines. We update this * whenever we start a new line, whether or not we store a line * start record for the line. */ long line_id_; /* width of longest line */ long max_line_width_; /* width of longest line at outermost level */ long outer_max_line_width_; /* * Tag we're currently rendering (i.e., the tag that we'll render * on the next call to do_formatting). */ class CHtmlTag *curtag_; /* * Last tag we rendered. We hold on to this so that we can keep * going from the last position if more text gets added after we've * formatted everything, at which point curtag_ will be null and we * wouldn't otherwise be able to figure out where we left off. */ class CHtmlTag *prvtag_; /* * Flag indicating that we should stop formatting, even if we have * more left in our list. This is used by sub-formatters to limit * the extent of the tags they format: when a sub-formatter reaches * its ending point, it will set this flag, and we'll act as though * there were nothing left in the tag list. */ int stop_formatting_ : 1; /* last valid break position */ CHtmlLineBreak breakpos_; /* line start table */ CHtmlLineStarts *line_starts_; /* DIV list */ CHArrayList div_list_; /* current DIV tag we're processing */ class CHtmlDispDIV *cur_div_; /* current selection range */ unsigned long sel_start_; unsigned long sel_end_; /* true -> we're at the beginning of a new line */ int last_was_newline_ : 1; /* amount of space to add before the current line */ int line_spacing_; /* line wrapping mode - false means lines are only broken explicitly */ int wrap_lines_ : 1; /* flow stacks for left and right margins */ CHtmlFmtFlowStack left_flow_stk_; CHtmlFmtFlowStack right_flow_stk_; /* flow stack environment save stack (for floating table formatting) */ CHtmlFmtTableEnvSave table_env_save_[CHTMLFMT_TABLE_ENV_SAVE_MAX]; size_t table_env_save_sp_; /* tab stop table */ class CHtmlHashTable *tab_table_; /* pending tab stop information */ int pending_tab_ : 1; long pending_tab_xpos_; HTML_Attrib_id_t pending_tab_align_; textchar_t pending_tab_dp_; class CHtmlDispTab *pending_tab_disp_; /* * Heap increment size. Subclasses may want to change this value at * construction time according to their memory management * characteristics. */ size_t heap_alloc_unit_; /* list of heap pages */ CHtmlFmt_heap_page_t *heap_pages_; /* maximum number of entries in the heap page list */ size_t heap_pages_max_; /* number of pages in the list that have actually been allocated */ size_t heap_pages_alloced_; /* * current heap page - at any given time, we're allocating out of * one heap page; when we exhaust it, we'll move on to the next one */ size_t heap_page_cur_; /* offset of next free byte in current heap page */ size_t heap_page_cur_ofs_; /* * current hypertext link object - when we're within a link, this * item must be set to contain the link information for objects * within the link */ class CHtmlDispLink *cur_link_; /* * flag: character wrapping mode is in effect (if not, word-wrapping * mode is used) */ int char_wrap_mode_; /* current display item factory */ CHtmlFmtDispFactory *cur_disp_factory_; /* * display item factories for each combination of linkability and * character/word wrap modes */ CHtmlFmtDispFactory *disp_factory_normal_; CHtmlFmtDispFactory *disp_factory_charwrap_; CHtmlFmtDispFactory *disp_factory_link_; CHtmlFmtDispFactory *disp_factory_link_charwrap_; /* current map under construction */ class CHtmlFmtMap *cur_map_; /* map table */ class CHtmlHashTable *map_table_; /* ordered list continuation sequence number */ long ol_cont_seqnum_; /* * Current table formatting pass number: *. 0 -> not formatting a table at all *. 1 -> first pass: measuring cell contents *. 2 -> second pass: formatting cell contents */ int table_pass_; /* format tag for current table */ class CHtmlTagTABLE *current_table_; /* pre-table heap state */ size_t pre_table_heap_page_cur_; size_t pre_table_heap_page_cur_ofs_; /* pre-table display list and line list state */ class CHtmlDisp *pre_table_disp_tail_; class CHtmlDisp *pre_table_line_head_; long pre_table_line_count_; long pre_table_line_id_; long pre_table_line_starts_count_; size_t pre_table_div_list_count_; class CHtmlDispDIV *pre_table_cur_div_; long pre_table_max_line_width_; long pre_table_avail_line_width_; /* pre-table output position */ CHtmlPoint pre_table_curpos_; int pre_table_line_spacing_; int pre_table_last_was_newline_ : 1; /* * Formatter sound generation ID. Each time we start formatting a * brand new page, we'll increment the sound generation ID. When a * sound is played, we'll mark it with its generation. If a sound * has already been played on the current generation, we'll ignore * it; this allows us to resize and otherwise reformat an existing * page repeatedly without having to restart all of its sounds, * while still detecting when we need to start sounds again from * scratch. */ unsigned int sound_gen_id_; /* * Flag: we're in T3 mode. This means our caller is a T3 program, * which changes certain parsing rules. */ int t3_mode_; /* flag: we're capturing a <TITLE> */ unsigned int title_mode_ : 1; /* resource cache (a global singleton) */ static class CHtmlResCache *res_cache_; /* resource finder (a global singleton) */ static class CHtmlResFinder *res_finder_; /* * Sound queues. We maintain four sound queues: one for the * foreground layer, one for the background ambient layer, one for the * ambient layer, and one for the background layer. * * The sound queues are global to the entire application. We maintain * these with the main text window, because that window is always * around. */ static class CHtmlSoundQueue *sound_queues_[4]; /* * number of active formatters (we use this for initializing and * uninitializing the global singletons) */ static int formatter_cnt_; }; /* ------------------------------------------------------------------------ */ /* * Source window formatter. This is used for displaying the text of a * source file window in the debugger. */ class CHtmlFormatterSrcwin: public CHtmlFormatter { public: CHtmlFormatterSrcwin(class CHtmlParser *parser) : CHtmlFormatter(parser) { } /* * get/set the last sound tag - these do nothing in this subclass, as * source windows don't use sound tags */ virtual class CHtmlTagSOUND *get_last_sound_tag() const { return 0; } virtual void set_last_sound_tag(class CHtmlTagSOUND *) { } /* <SOUND> tags are not allowed in source windows */ virtual int allow_sound_tags() const { return FALSE; } /* start formatting from the top of the document */ virtual void start_at_top(int reset_sounds); protected: }; /* ------------------------------------------------------------------------ */ /* * Stand-alone window formatter. This can be used for any independent * HTML window. * * This formatter can be used for displaying old pages in history-browsing * mode. A history window is like a main window, but can't redisplay its * banners. This formatter can also be used for displaying separate * windows such as "About" boxes. */ class CHtmlFormatterStandalone: public CHtmlFormatter { public: CHtmlFormatterStandalone(class CHtmlParser *parser) : CHtmlFormatter(parser) { } /* <SOUND> tags are not allowed in standalone windows */ virtual int allow_sound_tags() const { return FALSE; } /* * get/set the last sound tag - these do nothing in this subclass, * since we don't use sounds in old windows recalled via history * browsing */ virtual class CHtmlTagSOUND *get_last_sound_tag() const { return 0; } virtual void set_last_sound_tag(class CHtmlTagSOUND *) { } /* start formatting from the top of the document */ virtual void start_at_top(int reset_sounds); protected: }; /* ------------------------------------------------------------------------ */ /* * Main formatter. This formatter subclass is used for the main text * window. */ class CHtmlFormatterMain: public CHtmlFormatter { public: CHtmlFormatterMain(class CHtmlParser *parser); ~CHtmlFormatterMain(); /* set my window and the physical margins to use */ virtual void set_win(class CHtmlSysWin *win, const CHtmlRect *r); /* receive notification that the window is about to be deleted */ virtual void unset_win(); /* prepare to delete the parser */ virtual void release_parser(); /* start formatting from the top of the document */ virtual void start_at_top(int reset_sounds); /* <SOUND> tags are allowed in the main window */ virtual int allow_sound_tags() const { return TRUE; } /* get the last sound tag */ virtual class CHtmlTagSOUND *get_last_sound_tag() const { /* return the last sound tag we stored */ return last_sound_tag_; } /* set the last sound tag */ virtual void set_last_sound_tag(class CHtmlTagSOUND *tag) { /* remember the tag */ last_sound_tag_ = tag; } /* * Add an item to the tail of the display list, adjust the current line * under construction to compensate for the new item. If we're in * <TITLE> mode, capture text to our title buffer for later use. */ virtual void add_disp_item(class CHtmlDisp *item); /* when we end the <TITLE> tag, set the window title */ virtual void end_title(class CHtmlTagTITLE *tag); /* format a banner */ virtual int format_banner(const textchar_t *banner_id, long height, int height_set, int height_prev, int height_pct, long width, int width_set, int width_prev, int width_pct, class CHtmlTagBANNER *banner_tag); /* remove all of the in-line banners */ virtual void remove_all_banners(int replacing); /* remove a banner */ virtual void remove_banner(const textchar_t *banner_id); protected: /* remove a banner */ void remove_banner(class CHtmlFmtBannerItem *item, int replacing); /* title buffer */ CStringBuf title_buf_; /* * Banner array. Since the number of banners used in a particular * game should be small, we're not bothering to use an * efficiently-searched data structure; we just use a linked list of * banner tracking objects. */ class CHtmlFmtBannerItem *banners_; /* last sound tag */ class CHtmlTagSOUND *last_sound_tag_; }; /* banner list item */ class CHtmlFmtBannerItem { public: CHtmlFmtBannerItem(const textchar_t *banner_id, int is_about_box, class CHtmlFormatterBannerSub *subformatter) { id_.set(banner_id); is_about_box_ = is_about_box; subformatter_ = subformatter; nxt_ = 0; height_set_ = FALSE; width_set_ = FALSE; last_tag_ = first_tag_ = 0; is_first_ = TRUE; } /* does this item match the given ID? */ int matches(const textchar_t *banner_id, int is_about_box) { return ((is_about_box_ != 0) == (is_about_box != 0) && get_strcmp(banner_id, id_.get()) == 0); } /* the ID string for the banner */ CStringBuf id_; /* flag indicating whether this is the about box banner */ int is_about_box_ : 1; /* the sub-formatter that does the formatting for the banner contents */ class CHtmlFormatterBannerSub *subformatter_; /* next banner in banner list */ CHtmlFmtBannerItem *nxt_; /* flag indicating that the height/width have been set */ int height_set_ : 1; int width_set_ : 1; /* last BANNER tag using this banner */ class CHtmlTagBANNER *last_tag_; /* * First BANNER tag using this same ID, at least since the last * <BANNER REMOVE> tag for this ID. We always keep the first and * last banners of a series of replacements (without removals) of * the same banner, so that the banner will be re-created in the * same order when the entire list is reformatted from scratch. */ class CHtmlTagBANNER *first_tag_; /* flag indicating that this is the first occurrence of this banner */ int is_first_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Input-capable formatter. An input-capable formatter is always a main * formatter. */ class CHtmlFormatterInput: public CHtmlFormatterMain { public: CHtmlFormatterInput(CHtmlParser *parser); /* start formatting from the top of the document */ virtual void start_at_top(int reset_sounds); /* delete the display list */ virtual void delete_display_list(); /* do we have more rendering prior to the input line? */ int more_before_input(); /* * Add an item to the tail of the display list, replacing the * current command input display item(s), if any. This is used * during editing of a command line to update the display after the * user has made a change in the contents of the command line. */ virtual void add_disp_item_input(class CHtmlDispTextInput *item, class CHtmlTagTextInput *tag); /* * Prepare for input. This can be called prior to finishing formatting * in preparation for input. The purpose of this routine is to correct * any ill-formed HTML prior to pausing for interactive command * editing. Specifically, we close any <TABLE> that hasn't yet been * finished. */ void prepare_for_input(); /* * Begin a new input. 'buf' is the buffer, managed by the caller, * which the caller will use to edit the input. Returns the text * input tag which the caller uses to coordinate the input. * * 'buflen' is the initial length of the text in the buffer -- this * isn't the size of the buffer, but simply the length of the text * that we should display as part of the initial input contents. * When starting a new line with nothing in the buffer, this should * be zero. Note that in some cases (such as after a * partially-entered command interrupted by a real-time event), we * will start a new command with some text already typed in for the * user; in these cases, 'buflen' indicates the length of this * pre-typed text. */ class CHtmlTagTextInput *begin_input(const textchar_t *buf, size_t buflen); /* * End the current command input line. No further editing of the * current line is possible after calling this method. */ void end_input(); /* * Get the position of a character, given the text offset in the * character array, searching only the current input line. This is * somewhat faster than the generic get_text_pos(), since it only * needs to search the current input line. */ CHtmlPoint get_text_pos_input(unsigned long txtofs) const; /* * Store input text temporarily in the text array. This is used by the * command-input text tag to update the text array with an input line * under construction. This doesn't commit the text to the text array, * but simply stores it temporarily so that it can be displayed during * interactive command editing. */ virtual unsigned long store_input_text_temp( const textchar_t *txt, size_t len); protected: /* clear the old input line */ void clear_old_input(); /* * Item just before the active command input display list head -- * the active command input items are always the last items in the * display list. Note that we keep track of the item before the * start of the command input list, since this is the tail of the * non-command list. */ class CHtmlDisp *input_pre_; /* the item which starts the active input */ class CHtmlDispTextInput *input_head_; /* input text tag coordinating the current command line input */ class CHtmlTagTextInput *input_tag_; /* available line width when the input line was started */ long input_avail_line_width_; /* x,y position at start of input */ long input_x_pos_; long input_y_pos_; /* line count at start of input */ long input_line_count_; long input_line_id_; long input_line_starts_count_; /* first item on input line */ class CHtmlDisp *input_line_head_; }; /* ------------------------------------------------------------------------ */ /* * Banner formatter. This formatter subclass is used to format banner * subwindows. */ class CHtmlFormatterBanner: public CHtmlFormatter { public: CHtmlFormatterBanner(class CHtmlParser *parser) : CHtmlFormatter(parser) { /* * use a fairly small heap allocation unit size, since banners * don't tend to contain a lot of data */ heap_alloc_unit_ = 2048; } /* * Initialize the window size. If width_set is true, it indicates that * the window's width should be set to the width value explicitly * given, which is in pixels unless width_pct is true, in which case * it's a percentage of the main frame window size. If width_set is * false, and width_prev is true, then we simply keep the existing * window size with no changes. If width_set and width_prev are both * false, then we size the window according to its current contents. * The height parameters are parallel in meaning. */ void init_size(HTML_BannerWin_Pos_t alignment, long width, HTML_BannerWin_Units_t width_units, int width_set, int width_prev, long height, HTML_BannerWin_Units_t height_units, int height_set, int height_prev); protected: /* * Calculate the natural content height and width, for purposes of * sizing the banner to its contents. */ virtual long calc_content_height(HTML_BannerWin_Pos_t alignment); virtual long calc_content_width(HTML_BannerWin_Pos_t alignment); }; /* ------------------------------------------------------------------------ */ /* * Formatter for banners created with the <BANNER> tag. A tag banner is * in many ways a child window of the main text window. */ class CHtmlFormatterBannerSub: public CHtmlFormatterBanner { public: CHtmlFormatterBannerSub(class CHtmlParser *parser, class CHtmlFormatter *parent, class CHtmlTagBANNER *banner_tag) : CHtmlFormatterBanner(parser) { /* remember our parent and starting point */ parent_ = parent; banner_start_tag_ = banner_tag; } /* <SOUND> tags are allowed in banner subwindows */ virtual int allow_sound_tags() const { return TRUE; } /* get the last sound tag */ virtual class CHtmlTagSOUND *get_last_sound_tag() const { /* * we're effectively part of our parent's tag stream, so return * the last sound tag from our parent formatter */ return parent_->get_last_sound_tag(); } /* set the last sound tag */ virtual void set_last_sound_tag(class CHtmlTagSOUND *tag) { /* we're part of the parent's stream of tags */ parent_->set_last_sound_tag(tag); } /* * Set the starting tag for the subformatter -- this is the BANNER * container tag that opened the banner. */ void set_banner_start_tag(class CHtmlTagBANNER *start_tag) { banner_start_tag_ = start_tag; } /* receive notification that we've reached the end of the banner */ virtual void end_banner(); /* * Re-start rendering at the top of the document. This should be * called whenever the current formatting becomes invalid, such as * when the window is resized horizontally. */ virtual void start_at_top(int reset_sounds); protected: /* parent formatter */ CHtmlFormatter *parent_; /* BANNER tag at the top of the banner contents */ class CHtmlTagBANNER *banner_start_tag_; }; /* ------------------------------------------------------------------------ */ /* * "External" banner formatter. An external banner is a banner not * associated with a <BANNER> tag in the main text stream, but rather * controlled via a completely separate text stream. * * The external banner provides its own management of the source text, so * that the external stream source can write the stream data directly to * the formatter object. */ class CHtmlFormatterBannerExt: public CHtmlFormatterBanner { public: /* * Create an external banner. This is essentially an implementation of * the tads2 osifc routine os_banner_create(). * * 'parent' is the parent banner. A child takes space out of its * parent's display area. If 'parent' is null, then the new banner is * a child of the main text area. * * 'where' is OS_BANNER_FIRST, OS_BANNER_LAST, OS_BANNER_BEFORE, or * OS_BANNER_AFTER, specifying where the new banner is to go in the * banner list. 'other' is the existing banner object that we're using * as the relative insertion point for OS_BANNER_BEFORE or * OS_BANNER_AFTER. * * 'wintype' is an OS_BANNER_TYPE_xxx value giving the type of banner * window to create. * * 'siz' is the initial size of the window, in units given by * 'siz_units', which is an OS_BANNER_SIZE_xxx value. * * 'style' is a combination of OS_BANNER_STYLE_xxx flags giving the * requested style of the banner window. */ static CHtmlFormatterBannerExt *create_extern_banner( CHtmlFormatterBannerExt *parent, int where, CHtmlFormatterBannerExt *other, int wintype, HTML_BannerWin_Pos_t alignment, int siz, int siz_units, unsigned long style); /* deletion */ ~CHtmlFormatterBannerExt() { size_t i, cnt; /* detach from our parent */ if (parent_ != 0) parent_->remove_child(this); /* detach from our children */ for (i = 0, cnt = children_.get_count() ; i < cnt ; ++i) ((CHtmlFormatterBannerExt *)children_.get_ele(i))->parent_ = 0; } /* delete an external banner */ static void delete_extern_banner(class CHtmlFormatterBannerExt *banner); /* clear the contents */ virtual void clear_contents() = 0; /* add source text for formatting */ virtual void add_source_text(const textchar_t *txt, size_t len) = 0; /* * Set the window's size. 'siz_units' is an OS_BANNER_SIZE_xxx value * specifying the units for 'siz'. */ virtual void set_banner_size(int siz, int siz_units); /* size the window to its current contents */ virtual void size_to_contents(int set_width, int set_height); /* flush the source text */ virtual void flush_txtbuf(int fmt) = 0; /* turn HTML parsing mode on or off */ virtual void set_html_mode(int mode) = 0; /* get HTML parsing mode */ virtual int get_html_mode() const = 0; /* set the text color */ virtual void set_banner_text_color(os_color_t fg, os_color_t bg) = 0; /* set the current text attributes */ virtual void set_banner_text_attr(int attrs) = 0; /* set the banner's window background color */ virtual void set_banner_screen_color(os_color_t color) = 0; /* * Set the output cursor position to the given row and column. This is * meaningless on stream-oriented banner windows; on grid windows, this * sets the position to the given coordinates, expressed in character * cells, numbered from 0,0 for the upper left corner. */ virtual void set_cursor_pos(int /*row*/, int /*col*/) { /* this is not meaningful by default, so simply ignore it */ } /* get my "natural" size units */ virtual HTML_BannerWin_Units_t get_natural_size_units() const = 0; /* <SOUND> tags are not allowed in external banner windows */ virtual int allow_sound_tags() const { return FALSE; } virtual class CHtmlTagSOUND *get_last_sound_tag() const { return 0; } virtual void set_last_sound_tag(class CHtmlTagSOUND *) { } protected: /* * Construction and destruction are handled via static method we expose * publicly - callers do not call our constructors or destructors * directly, so make these protected. */ CHtmlFormatterBannerExt(CHtmlFormatterBannerExt *parent, class CHtmlParser *parser, unsigned long style) : CHtmlFormatterBanner(parser) { /* remember my parent */ parent_ = parent; /* tell my parent about its new child */ if (parent != 0) parent->add_child(this); /* remember the original requested style */ orig_style_ = style; } /* calculate the natural content height and width */ virtual long calc_content_height(HTML_BannerWin_Pos_t alignment); virtual long calc_content_width(HTML_BannerWin_Pos_t alignment); /* add a new child to my list of children */ void add_child(CHtmlFormatterBannerExt *chi) { children_.add_ele(chi); } /* remove a child from my list of children */ void remove_child(CHtmlFormatterBannerExt *chi) { children_.remove_ele(chi); } /* my parent formatter, if any */ CHtmlFormatterBannerExt *parent_; /* my list of child formatters, if any */ CHArrayList children_; /* * The original style flags passed at creation. Some of these flags * are handled by the underlying OS implementation, so the presence of * a flag bit doesn't necessarily mean that the corresponding style is * actually in use. This merely records the style flags that were * requested. */ unsigned long orig_style_; }; /* ------------------------------------------------------------------------ */ /* * External Banner Formatter subclass for ordinary text stream windows. */ class CHtmlFormatterBannerExtText: public CHtmlFormatterBannerExt { friend class CHtmlFormatterBannerExt; public: /* start formatting at the top of the tag list */ virtual void start_at_top(int reset_sounds); /* add source text for formatting */ virtual void add_source_text(const textchar_t *txt, size_t len); /* flush the source text */ virtual void flush_txtbuf(int fmt); /* clear the contents */ virtual void clear_contents(); /* turn HTML parsing mode on or off */ virtual void set_html_mode(int mode); /* get HTML parsing mode */ virtual int get_html_mode() const; /* * set the text color - callers must use HTML in text banners, so we * can simply ignore this entirely */ virtual void set_banner_text_color(os_color_t, os_color_t) { } /* * set the current text attributes - callers must use HTML tags in * text banners, so we can ignore this */ virtual void set_banner_text_attr(int) { } /* set the background color - callers must use HTML tags, so ignore it */ virtual void set_banner_screen_color(os_color_t) { } /* get my "natural" size units */ virtual HTML_BannerWin_Units_t get_natural_size_units() const { /* text windows are sized in character cells */ return HTML_BANNERWIN_UNITS_CHARS; } protected: /* construction and destruction */ CHtmlFormatterBannerExtText(CHtmlFormatterBannerExt *parent, class CHtmlParser *parser, unsigned long style); ~CHtmlFormatterBannerExtText(); /* text buffer for sending text to the parser */ class CHtmlTextBuffer *txtbuf_; }; /* ------------------------------------------------------------------------ */ /* * External Banner Formatter subclass for Text Grid windows. */ class CHtmlFormatterBannerExtGrid: public CHtmlFormatterBannerExt { friend class CHtmlFormatterBannerExt; public: /* start formatting at the top of the tag list */ virtual void start_at_top(int reset_sounds); /* add source text for formatting */ virtual void add_source_text(const textchar_t *txt, size_t len) { /* * write the text at the current cursor position, and move the * cursor to the end of the new text */ write_text_at(txt, len, csr_row_, csr_col_, TRUE); } /* flush the source text - we don't buffer anything, so this is trivial */ virtual void flush_txtbuf(int) { } /* clear the contents */ virtual void clear_contents(); /* move the output cursor */ virtual void set_cursor_pos(int row, int col) { /* remember the new cursor position */ csr_row_ = row; csr_col_ = col; } /* * Turn HTML parsing mode on or off. A text grid banner doesn't * support HTML parsing at all, so there's nothing to do here. */ virtual void set_html_mode(int) { } /* get the HTML mode - we're always in non-HTML mode */ virtual int get_html_mode() const { return FALSE; } /* set the text color */ virtual void set_banner_text_color(os_color_t fg, os_color_t bg); /* set text attributes - we don't support any attributes, so ignore it */ virtual void set_banner_text_attr(int) { } /* set the background color */ virtual void set_banner_screen_color(os_color_t color); /* set the window's size */ virtual void set_banner_size(int siz, int siz_units); /* size the window to its current contents */ void size_to_contents(int set_width, int set_height) { /* * remember my character size as of this last explicit sizing, so * that we can restore our size in terms of the character cell size * if the font changes and we thus have to recalculate the * new pixel size corresponding to the same character size */ last_sized_ht_ = row_cnt_; last_sized_wid_ = col_cnt_; /* inherit the default handling */ CHtmlFormatterBannerExt::size_to_contents(set_width, set_height); } /* -------------------------------------------------------------------- */ /* * We don't use a text array to store our text, so we must override the * text array access methods. */ /* get the character at the given text offset */ virtual textchar_t get_char_at_ofs(unsigned long ofs) const; /* is the character at the given offset a word character? */ virtual int is_word_char_at_ofs( oshtml_charset_id_t cs, unsigned long ofs) const; /* * Get the number of characters in an offset range. We store our * character offsets continuously (without gaps), so the number of * characters in an offset range is simply the difference between the * offsets. */ virtual unsigned long get_chars_in_ofs_range( unsigned long startofs, unsigned long endofs) const { return endofs - startofs; } /* our text offsets are always continuous without gaps */ virtual unsigned long inc_text_ofs(unsigned long ofs) const { return ofs + 1; } virtual unsigned long dec_text_ofs(unsigned long ofs) const { return ofs - 1; } virtual unsigned long inc_text_ofs_char( oshtml_charset_id_t cs, unsigned long ofs) const; virtual unsigned long dec_text_ofs_char( oshtml_charset_id_t cs, unsigned long ofs) const; /* get the maximum text offset */ virtual unsigned long get_text_ofs_max() const { return max_ofs_; } /* * Get the text for the given offset. We don't format our tags through * a parser, so we don't support this operation. */ virtual const textchar_t *get_text_ptr(unsigned long ofs) const { return 0; } /* we don't implement searching */ virtual int can_search_for_text() const { return FALSE; } virtual int search_for_text(const textchar_t *, size_t, int, int, int, int, unsigned long, unsigned long *, unsigned long *) { return FALSE; } /* get my "natural" size units */ virtual HTML_BannerWin_Units_t get_natural_size_units() const { /* text grid windows are sized in character cells */ return HTML_BANNERWIN_UNITS_CHARS; } protected: /* construction */ CHtmlFormatterBannerExtGrid(CHtmlFormatterBannerExt *parent, unsigned long style); /* translate an os_color_t to an HTML_color_t */ HTML_color_t xlat_color(os_color_t oscolor); /* write text at the given row and column, optionally moving the cursor */ void write_text_at(const textchar_t *txt, size_t len, int row, int col, int move_cursor); /* get a display item by row number */ class CHtmlDispTextGrid *get_disp_by_row(int row) const; /* get the current default fixed-pitch font and remember it */ void get_grid_font(int keep_color); /* current output position, in character cell coordinates */ int csr_row_; int csr_col_; /* number of rows in the display list */ int row_cnt_; /* number of characters in the widest row */ int col_cnt_; /* * the maximum text offset - we don't use a text array to store our * text, so we need to keep track of our own text array information */ unsigned long max_ofs_; /* * Our height and width, in character cells, as of the last explicit * size-to-contents invocation. When the font changes on us, we might * have to resize the window to adjust for the change in font size; * since we're effectively sized in character cells, this information * allows us to adapt to the new font size automatically. */ int last_sized_ht_; int last_sized_wid_; }; /* ------------------------------------------------------------------------ */ /* * Image maps */ /* * An image map. A map has a name and a collection of zones. */ class CHtmlFmtMap { public: CHtmlFmtMap(const textchar_t *map_name, size_t name_len) { /* remember my name */ name_.set(map_name, name_len); /* no zones yet */ first_zone_ = last_zone_ = 0; } ~CHtmlFmtMap(); /* add a zone */ void add_zone(class CHtmlFmtMapZone *zone); /* get my name */ const textchar_t *get_name() const { return name_.get(); } /* * Find a zone, given a point and an image's bounds. Returns null * if there's no zone containing the given point. */ class CHtmlFmtMapZone *find_zone(int x, int y, const CHtmlRect *image_bounds) const; private: /* name of the map */ CStringBuf name_; /* first/last zones in my list */ class CHtmlFmtMapZone *first_zone_; class CHtmlFmtMapZone *last_zone_; }; /* * A map zone */ class CHtmlFmtMapZone { public: CHtmlFmtMapZone(class CHtmlDispLink *link) { /* remember my link object */ link_ = link; /* nothing else in list yet */ next_ = 0; } virtual ~CHtmlFmtMapZone() {} /* * Determine if a point within a given image is in the zone. This * must be overridden by each subclass to perform the appropriate * computation for its shape. */ virtual int pt_in_zone(int x, int y, const CHtmlRect *image_bounds) = 0; public: /* next zone in map's zone list */ CHtmlFmtMapZone *next_; /* my link object */ class CHtmlDispLink *link_; protected: /* compute a coordinate, given the COORD settinng and the image bounds */ long compute_coord(const struct CHtmlTagAREA_coords_t *coord, const CHtmlRect *image_bounds, int vert); }; /* * Map zones for the various shapes */ class CHtmlFmtMapZoneRect: public CHtmlFmtMapZone { public: CHtmlFmtMapZoneRect(const struct CHtmlTagAREA_coords_t *coords, class CHtmlDispLink *link); int pt_in_zone(int x, int y, const CHtmlRect *image_bounds); private: CHtmlTagAREA_coords_t coords_[4]; }; class CHtmlFmtMapZoneCircle: public CHtmlFmtMapZone { public: CHtmlFmtMapZoneCircle(const struct CHtmlTagAREA_coords_t *coords, class CHtmlDispLink *link); int pt_in_zone(int x, int y, const CHtmlRect *image_bounds); private: CHtmlTagAREA_coords_t coords_[2]; long radius_; }; class CHtmlFmtMapZonePoly: public CHtmlFmtMapZone { public: CHtmlFmtMapZonePoly(const struct CHtmlTagAREA_coords_t *coords, int coord_cnt, class CHtmlDispLink *link); int pt_in_zone(int x, int y, const CHtmlRect *image_bounds); private: CHtmlTagAREA_coords_t coords_[CHtmlTagAREA_max_coords]; }; #endif /* HTMLFMT_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmlhash.cpp�������������������������������������������������������������������0000664�0000000�0000000�00000022171�12650170723�0016777�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/html/htmlhash.cpp,v 1.2 1999/05/17 02:52:21 MJRoberts Exp $"; #endif /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlhash.cpp - hash table implementation Function Notes Modified 10/25/97 MJRoberts - Creation */ #include <assert.h> #include <memory.h> #include <string.h> #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef HTMLHASH_H #include "htmlhash.h" #endif /* ------------------------------------------------------------------------ */ /* * Simple case-insensitive hash function implementation. */ unsigned int CHtmlHashFuncCI::compute_hash(const textchar_t *s, size_t l) { unsigned int acc; /* * add up all the character values in the string, converting all * characters to upper-case */ for (acc = 0 ; l != 0 ; ++s, --l) acc += is_lower(*s) ? to_upper(*s) : *s; /* return the accumulated value */ return acc; } /* ------------------------------------------------------------------------ */ /* * Simple case-sensitive hash function implementation */ unsigned int CHtmlHashFuncCS::compute_hash(const textchar_t *s, size_t l) { unsigned int acc; /* * add up all the character values in the string, treating case as * significant */ for (acc = 0 ; l != 0 ; ++s, --l) acc += *s; /* return the accumulated value */ return acc; } /* ------------------------------------------------------------------------ */ /* * Hash table symbol entry. This is an abstract class; subclasses must * provide a symbol-matching method. */ CHtmlHashEntry::CHtmlHashEntry(const textchar_t *str, size_t len, int copy) { /* not linked into a list yet */ nxt_ = 0; /* see if we can use the original string or need to make a private copy */ if (copy) { textchar_t *buf; /* allocate space for a copy */ buf = new textchar_t[len]; /* copy it into our buffer */ memcpy(buf, str, len * sizeof(*buf)); /* remember it */ str_ = buf; } else { /* we can use the original */ str_ = str; } /* remember the length */ len_ = len; /* remember whether or not we own the string */ copy_ = copy; } CHtmlHashEntry::~CHtmlHashEntry() { /* if we made a private copy of the string, we own it, so delete it */ if (copy_) delete [] str_; } /* ------------------------------------------------------------------------ */ /* * Concrete subclass of CHtmlHashEntry providing a case-insensitive * symbol match implementation */ int CHtmlHashEntryCI::matches(const textchar_t *str, size_t len) { /* * it's a match if the strings are the same length and all * characters match, ignoring case */ return (len == len_ && memicmp(str, str_, len * sizeof(*str)) == 0); } /* ------------------------------------------------------------------------ */ /* * Concrete subclass of CHtmlHashEntry providing a case-sensitive symbol * match implementation */ int CHtmlHashEntryCS::matches(const textchar_t *str, size_t len) { /* * it's a match if the strings are the same length and all * characters match, treating case as significant */ return (len == len_ && memcmp(str, str_, len * sizeof(*str)) == 0); } /* ------------------------------------------------------------------------ */ /* * Hash table */ CHtmlHashTable::CHtmlHashTable(int hash_table_size, CHtmlHashFunc *hash_function) { CHtmlHashEntry **entry; size_t i; /* make sure it's a power of two */ assert(is_power_of_two(hash_table_size)); /* make sure we got a hash function */ assert(hash_function != 0); /* save the hash function */ hash_function_ = hash_function; /* allocate the table */ table_ = new CHtmlHashEntry *[hash_table_size]; table_size_ = hash_table_size; /* clear the table */ for (entry = table_, i = 0 ; i < table_size_ ; ++i, ++entry) *entry = 0; } CHtmlHashTable::~CHtmlHashTable() { /* delete the hash function object */ delete hash_function_; /* delete each entry in the hash table */ delete_all_entries(); /* delete the hash table */ delete [] table_; } /* * delete all entries in the hash table, but keep the table itself */ void CHtmlHashTable::delete_all_entries() { CHtmlHashEntry **tableptr; size_t i; for (tableptr = table_, i = 0 ; i < table_size_ ; ++i, ++tableptr) { CHtmlHashEntry *entry; CHtmlHashEntry *nxt; /* delete each entry in the list at this element */ for (entry = *tableptr ; entry ; entry = nxt) { /* remember the next entry */ nxt = entry->nxt_; /* delete this entry */ delete entry; } /* there's nothing at this table entry now */ *tableptr = 0; } } /* * Verify that a value is a power of two. Hash table sizes must be * powers of two. */ int CHtmlHashTable::is_power_of_two(int n) { /* divide by two until we have an odd number */ while ((n & 1) == 0) n >>= 1; /* make sure the result is 1 */ return (n == 1); } /* * Compute the hash value for an entry */ unsigned int CHtmlHashTable::compute_hash(CHtmlHashEntry *entry) { return compute_hash(entry->getstr(), entry->getlen()); } /* * Compute the hash value for a string */ unsigned int CHtmlHashTable::compute_hash(const textchar_t *str, size_t len) { return (hash_function_->compute_hash(str, len) & (table_size_ - 1)); } /* * Add an object to the table */ void CHtmlHashTable::add(CHtmlHashEntry *entry) { unsigned int hash; /* compute the hash value for this entry */ hash = compute_hash(entry); /* link it into the slot for this hash value */ entry->nxt_ = table_[hash]; table_[hash] = entry; } /* * Remove an object */ void CHtmlHashTable::remove(CHtmlHashEntry *entry) { unsigned int hash; /* compute the hash value for this entry */ hash = compute_hash(entry); /* * if it's the first item in the chain, advance the head over it; * otherwise, we'll need to find the previous item to unlink it */ if (table_[hash] == entry) { /* it's the first item - simply advance the head to the next item */ table_[hash] = entry->nxt_; } else { CHtmlHashEntry *prv; /* find the previous item in the list for this hash value */ for (prv = table_[hash] ; prv != 0 && prv->nxt_ != entry ; prv = prv->nxt_) ; /* if we found it, unlink this item */ if (prv != 0) prv->nxt_ = entry->nxt_; } } /* * Find an object in the table matching a given string. */ CHtmlHashEntry *CHtmlHashTable::find(const textchar_t *str, size_t len) { unsigned int hash; CHtmlHashEntry *entry; /* compute the hash value for this entry */ hash = compute_hash(str, len); /* scan the list at this hash value looking for a match */ for (entry = table_[hash] ; entry ; entry = entry->nxt_) { /* if this one matches, return it */ if (entry->matches(str, len)) return entry; } /* didn't find anything */ return 0; } /* * Find an object in the table matching a given leading substring. * We'll return the longest-named entry that matches a leading substring * of the given string. For example, if there's are entires A, AB, ABC, * and ABCE, and this routine is called to find something matching * ABCDEFGH, we'll return ABC as the match (not ABCE, since it doesn't * match any leading substring of the given string, and not A or AB, * even though they match, since ABC also matches and it's longer). */ CHtmlHashEntry *CHtmlHashTable::find_leading_substr( const textchar_t *str, size_t len) { size_t sublen; CHtmlHashEntry *entry; /* * try to find each leading substring, starting with the longest, * decreasing by one character on each iteration, until we've used * the whole string */ for (sublen = len ; sublen > 0 ; --sublen) { /* if this substring matches, use it */ if ((entry = find(str, sublen)) != 0) return entry; } /* we didn't find it */ return 0; } /* * Enumerate all entries */ void CHtmlHashTable::enum_entries(void (*func)(void *, CHtmlHashEntry *), void *ctx) { CHtmlHashEntry **tableptr; size_t i; /* go through each hash value */ for (tableptr = table_, i = 0 ; i < table_size_ ; ++i, ++tableptr) { /* go through each entry at this hash value */ CHtmlHashEntry *entry, *nxt; for (entry = *tableptr ; entry ; entry = nxt) { /* remember the next entry in case the callback deletes this one */ nxt = entry->nxt_; /* invoke the callback on this entry */ (*func)(ctx, entry); } } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmlhash.h���������������������������������������������������������������������0000664�0000000�0000000�00000014444�12650170723�0016450�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/html/htmlhash.h,v 1.2 1999/05/17 02:52:21 MJRoberts Exp $ */ /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlhash.h - hash table implementation Function Notes Modified 10/25/97 MJRoberts - Creation */ #ifndef HTMLHASH_H #define HTMLHASH_H #ifndef TADSHTML_H #include "tadshtml.h" #endif /* ------------------------------------------------------------------------ */ /* * Hash function interface class. Hash table clients must implement an * appropriate hash function to use with the hash table; this abstract * class provides the necessary interface. */ class CHtmlHashFunc { public: virtual ~CHtmlHashFunc() { } virtual unsigned int compute_hash(const textchar_t *str, size_t len) = 0; }; /* ------------------------------------------------------------------------ */ /* * Hash table symbol entry. This is an abstract class; subclasses must * provide a symbol-matching method. */ class CHtmlHashEntry { public: /* * Construct the hash entry. 'copy' indicates whether we should * make a private copy of the value; if not, the caller must keep * the original string around as long as this hash entry is around. * If 'copy' is true, we'll make a private copy of the string * immediately, so the caller need not keep it around after * constructing the entry. */ CHtmlHashEntry(const textchar_t *str, size_t len, int copy); virtual ~CHtmlHashEntry(); /* determine if this entry matches a given string */ virtual int matches(const textchar_t *str, size_t len) = 0; /* list link */ CHtmlHashEntry *nxt_; /* get the string pointer and length */ const textchar_t *getstr() const { return str_; } size_t getlen() const { return len_; } protected: const textchar_t *str_; size_t len_; int copy_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Hash table */ class CHtmlHashTable { public: /* * Construct a hash table. IMPORTANT: the hash table object takes * ownership of the hash function object, so the hash table object * will delete the hash function object when the table is deleted. */ CHtmlHashTable(int hash_table_size, CHtmlHashFunc *hash_function); /* delete the hash table */ ~CHtmlHashTable(); /* * Add a symbol. If 'copy' is true, it means that we need to make * a private copy of the string; otherwise, the caller must ensure * that the string remains valid as long as the hash table entry * remains valid, since we'll just store a pointer to the original * string. IMPORTANT: the hash table takes over ownership of the * hash table entry; the hash table will delete this object when the * hash table is deleted, so the client must not delete the entry * once it's been added to the table. */ void add(CHtmlHashEntry *entry); /* * Remove an object from the cache. This routine does not delete * the object. */ void remove(CHtmlHashEntry *entry); /* * Delete all entries in the table */ void delete_all_entries(); /* * Find an entry in the table matching the given string */ CHtmlHashEntry *find(const textchar_t *str, size_t len); /* * find an entry that matches the longest leading substring of the * given string */ CHtmlHashEntry *find_leading_substr(const textchar_t *str, size_t len); /* * Enumerate all entries, invoking a callback for each entry in the * table */ void enum_entries(void (*func)(void *ctx, class CHtmlHashEntry *entry), void *ctx); private: /* internal service routine for checking hash table sizes for validity */ int is_power_of_two(int n); /* compute the hash value for an entry/a string */ unsigned int compute_hash(CHtmlHashEntry *entry); unsigned int compute_hash(const textchar_t *str, size_t len); /* the table of hash entries */ CHtmlHashEntry **table_; size_t table_size_; /* hash function */ CHtmlHashFunc *hash_function_; }; /* ------------------------------------------------------------------------ */ /* * Simple case-insensitive hash function */ class CHtmlHashFuncCI: public CHtmlHashFunc { public: unsigned int compute_hash(const textchar_t *str, size_t len); }; /* ------------------------------------------------------------------------ */ /* * Simple case-sensitive hash function implementation */ class CHtmlHashFuncCS: public CHtmlHashFunc { public: unsigned int compute_hash(const textchar_t *str, size_t len); }; /* ------------------------------------------------------------------------ */ /* * Concrete subclass of CHtmlHashEntry providing a case-insensitive * symbol match implementation */ class CHtmlHashEntryCI: public CHtmlHashEntry { public: CHtmlHashEntryCI(const textchar_t *str, size_t len, int copy) : CHtmlHashEntry(str, len, copy) { } virtual int matches(const textchar_t *str, size_t len); }; /* ------------------------------------------------------------------------ */ /* * Concrete subclass of CHtmlHashEntry providing a case-sensitive symbol * match implementation */ class CHtmlHashEntryCS: public CHtmlHashEntry { public: CHtmlHashEntryCS(const textchar_t *str, size_t len, int copy) : CHtmlHashEntry(str, len, copy) { } virtual int matches(const textchar_t *str, size_t len); }; /* ------------------------------------------------------------------------ */ /* * Simple hash function that hashes an (unsigned int) as its key */ class CHtmlHashFuncUInt: public CHtmlHashFuncCS { }; /* * Hash entry that maps an integer key to a void* value */ class CHtmlHashEntryUInt: public CHtmlHashEntryCS { public: CHtmlHashEntryUInt(unsigned int key, void *val) : CHtmlHashEntryCS((const textchar_t *)&key, sizeof(key), FALSE) { key_ = key; str_ = (const textchar_t *)&key_; val_ = val; } /* the key vlaue */ unsigned int key_; /* the value associated with the key */ void *val_; }; #endif /* HTMLHASH_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmlinp.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000051226�12650170723�0016645�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/html/htmlinp.cpp,v 1.2 1999/05/17 02:52:21 MJRoberts Exp $"; #endif /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlinp.cpp - input editor services class Function Notes Modified 09/28/97 MJRoberts - Creation */ #include <stdlib.h> #include <string.h> #include <memory.h> #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef HTMLINP_H #include "htmlinp.h" #endif CHtmlInputBuf::~CHtmlInputBuf() { size_t i; /* drop history items */ for (i = 0 ; i < histcnt_ ; ++i) th_free(history_[i]); /* drop history buffer and the saved current line */ if (history_ != 0) th_free(history_); if (saved_cur_line_ != 0) th_free(saved_cur_line_); /* delete the undo buffer if we have one */ if (undo_buf_) th_free(undo_buf_); } /* * set the buffer */ void CHtmlInputBuf::setbuf(textchar_t *buf, size_t bufsiz, size_t initlen) { /* remember the buffer information */ changebuf(buf, bufsiz); /* set up with the initial command */ len_ = initlen; /* set up at the end of the current command */ sel_start_ = len_; sel_end_ = len_; caret_ = len_; /* reset the history position */ set_last_hist(); /* caret starts off visible */ caret_vis_ = TRUE; /* drop any undo we have */ if (undo_buf_ != 0) { th_free(undo_buf_); undo_buf_ = 0; } } /* * Change the buffer, without resetting any of the buffer settings. This * can be used to resume an existing editing session with a different * buffer. */ void CHtmlInputBuf::changebuf(textchar_t *buf, size_t bufsiz) { /* remember the buffer information */ buf_ = buf; bufsiz_ = bufsiz; /* if this is a null buffer, note that we have nothing in the buffer */ if (buf_ == 0) { /* we have no contents, so the content length is zero */ len_ = 0; /* forget any selection */ sel_start_ = sel_end_ = caret_ = 0; } } /* * delete the selected range */ int CHtmlInputBuf::del_selection() { /* if nothing's selected, there's nothing to do */ if (sel_start_ == sel_end_) return FALSE; /* save undo */ save_undo(); /* move the tail over the selection, if necessary */ if (len_ != sel_end_) memmove(buf_ + sel_start_, buf_ + sel_end_, len_ - sel_end_); /* reduce the length by the selection size */ len_ -= sel_end_ - sel_start_; /* move the selection end to the start */ caret_ = sel_end_ = sel_start_; show_caret(); /* we made a change */ return TRUE; } /* * move the cursor left one character */ void CHtmlInputBuf::move_left(int extend, int word) { size_t idx; /* if not already showing the caret, make it visible again */ show_caret(); /* if we're already at the start of the command, there's nothing to do */ if (caret_ == 0) return; /* start by moving back one character */ idx = prevchar(caret_); /* if moving by a word, skip to the start of the current/prior word */ if (word) { /* move left until we're not in spaces */ while (idx > 0 && is_space(buf_[idx])) idx = prevchar(idx); /* move left until the character to the left is a space */ while (idx > 0 && !is_space(buf_[prevchar(idx)])) idx = prevchar(idx); } /* adjust selection */ adjust_sel(idx, extend); } /* * move the cursor right one character */ void CHtmlInputBuf::move_right(int extend, int word) { size_t idx; /* if not already showing the caret, make it visible again */ show_caret(); /* if we're already at the end of the command, there's nothing to do */ if (caret_ == len_) return; /* see if we're doing a word or a character */ if (word) { /* move right until we find a space */ idx = caret_; while (idx < len_ && !is_space(buf_[idx])) idx = nextchar(idx); /* move right until we find something other than a space */ while (idx < len_ && isspace(buf_[idx])) idx = nextchar(idx); } else { /* move by one character */ idx = nextchar(caret_); } /* adjust selection */ adjust_sel(idx, extend); } /* * backspace */ int CHtmlInputBuf::backspace() { /* if not already showing the caret, make it visible again */ show_caret(); /* see if there's a selection range */ if (sel_start_ != sel_end_) { /* there's a range - delete it */ return del_selection(); } else if (caret_ > 0) { /* save undo */ save_undo(); /* * no range - delete character to left of cursor. Move the * characters to the right of the cursor over the character to * the left of the cursor, if necessary. */ size_t prv = prevchar(caret_); if (len_ > caret_) memmove(buf_ + prv, buf_ + caret_, len_ - caret_); /* it's now one character shorter */ len_ -= (caret_ - prv); /* back up the caret one position */ sel_start_ = sel_end_ = caret_ = prv; /* we made a change */ return TRUE; } else { /* we didn't change anything */ return FALSE; } } /* * delete right */ int CHtmlInputBuf::del_right() { /* if not already showing the caret, make it visible again */ show_caret(); /* see if there's a selection range */ if (sel_start_ != sel_end_) { /* there's a range - delete it */ return del_selection(); } else if (caret_ < len_) { /* save undo */ save_undo(); /* * no range - delete character to right of cursor. Move the * characters after the character to the right of the cursor * over the character to the right of the cursor, if necessary. */ size_t nxt = nextchar(caret_); if (len_ > nxt) memmove(buf_ + caret_, buf_ + nxt, len_ - nxt); /* it's now one character shorter */ len_ -= (nxt - caret_); /* we made a change */ return TRUE; } else { /* we didn't change anything */ return FALSE; } } /* * add a character */ int CHtmlInputBuf::add_char(textchar_t c) { int changed; /* if there's a selection range, delete it */ changed = del_selection(); /* if there's room, insert the character */ if (len_ < bufsiz_) { /* if we didn't already make a change, save undo */ if (!changed) save_undo(); /* move characters to right of cursor to make room if necessary */ if (caret_ != len_) memmove(buf_ + caret_ + 1, buf_ + caret_, len_ - caret_); /* add the new character */ buf_[caret_] = c; ++len_; /* move the insertion point */ sel_start_ = sel_end_ = ++caret_; show_caret(); /* we made a change */ changed = TRUE; } /* return change indication */ return changed; } /* * Add a UTF-8 character */ int CHtmlInputBuf::add_char_utf8(unsigned int c) { int changed; /* we only accept BMP characters */ if (c > 0xffff) return FALSE; /* figure the size (in bytes) of the character's UTF-8 representation */ size_t siz = (c <= 0x7f ? 1 : c <= 0x7ff ? 2 : 3); /* if there's a selection, delete it first */ changed = del_selection(); /* if there's room, add the new character */ if (len_ + siz <= bufsiz_) { /* save undo if we haven't made a change already */ if (!changed) save_undo(); /* move characters to right of cursor to make room if necessary */ if (caret_ != len_) memmove(buf_ + caret_ + siz, buf_ + caret_, len_ - caret_); /* add the new character */ if (c < 0x7f) { /* 0..7f -> 1-byte UTF-8 */ buf_[caret_] = (textchar_t)(c & 0x7f); } else if (c < 0x7ff) { /* 80..7ff -> 2-byte UTF-8 */ buf_[caret_] = (textchar_t)(0xC0 | ((c >> 6) & 0x1F)); buf_[caret_+1] = (textchar_t)(0x80 | (c & 0x3F)); } else { /* 800..ffff -> 3-byte UTF-8 */ buf_[caret_] = (textchar_t)(0xE0 | ((c >> 12) & 0x0F)); buf_[caret_+1] = (textchar_t)(0x80 | ((c >> 6) & 0x3F)); buf_[caret_+2] = (textchar_t)(0x80 | (c & 0x3F)); } /* count it */ len_ += siz; /* move the insertion point */ sel_start_ = sel_end_ = caret_ = caret_ + siz; show_caret(); /* we made a change */ changed = TRUE; } /* return the change indication */ return changed; } /* * Add a string to the buffer. Inserts the new string at the current * insertion point, replacing any current selection range. */ int CHtmlInputBuf::add_string(const textchar_t *str, size_t len, int trunc) { int changed; /* see if it fits, or if we need to truncate */ if (len_ + len > bufsiz_ - (sel_end_ - sel_start_)) { /* it doesn't fit - if we can't truncate, we can't do anything */ if (!trunc) return FALSE; /* * Truncate the new text to what will fit in our buffer. First, * figure out how much buffer size is simply not currently used - * this is the size of the buffer minus the length of the current * text in the buffer (bufsiz_ - len_). Then, since we're replacing * the current selection range, the length of the current selection * range (sel_end_ - sel_start_) will also be available to us as * free space. */ len = (bufsiz_ - len_) + (sel_end_ - sel_start_); } /* if there's a selection range, delete it */ changed = del_selection(); /* add the new string, if there is one */ if (len != 0) { /* if we didn't already make a change, save undo */ if (!changed) save_undo(); /* move characters to right of cursor to make room if necessary */ if (caret_ != len_) memmove(buf_ + caret_ + len, buf_ + caret_, len_ - caret_); /* add the string */ memcpy(buf_ + caret_, str, len); /* update the length */ len_ += len; /* move the insertion point past the new string */ caret_ += len; sel_start_ = sel_end_ = caret_; show_caret(); /* we changed it */ changed = TRUE; } /* return the change indication */ return changed; } /* * Move to the start of the command */ void CHtmlInputBuf::start_of_line(int extend) { /* adjust selection */ adjust_sel(0, extend); } /* * Move to the end of the command */ void CHtmlInputBuf::end_of_line(int extend) { /* adjust selection */ adjust_sel(len_, extend); } /* * Delete to end of line */ int CHtmlInputBuf::del_eol() { /* if not already showing the caret, make it visible again */ show_caret(); /* if there's anything to the right of the cursor, delete it all */ if (caret_ < len_) { /* save undo */ save_undo(); /* drop everything after the cursor */ len_ = caret_; /* zero out the selection */ sel_start_ = sel_end_ = caret_; /* it's different now */ return TRUE; } /* nothing changed */ return FALSE; } /* * Delete entire line */ int CHtmlInputBuf::del_line() { /* if not already showing the caret, make it visible again */ show_caret(); /* if there's nothing in the line, there's nothing to do */ if (len_ == 0) return FALSE; /* save undo */ save_undo(); /* drop everything */ len_ = 0; /* move the cursor to the beginning of the line */ sel_start_ = sel_end_ = caret_ = 0; /* it's changed */ return TRUE; } /* * Adjust the selection range */ void CHtmlInputBuf::adjust_sel(size_t newpos, int extend) { if (extend) { /* * Extending range - move whichever end of the current range is * at the caret so that it stays with the caret, and leave the * other end of the range alone. If we have no selection range * now, start one. */ if (sel_start_ == sel_end_) { /* * no range now - move one of the ends to follow the * cursor, keeping start <= end */ if (newpos < sel_start_) sel_start_ = newpos; else sel_end_ = newpos; } else { /* * we already have a range - move the end which is at the * cursor to the new position, and leave the other end where * it is */ if (sel_start_ == caret_) sel_start_ = newpos; else sel_end_ = newpos; } } else { /* not extending -- simply move everything to the new position */ sel_start_ = sel_end_ = newpos; } /* in any case, move the caret to its new position */ caret_ = newpos; show_caret(); } /* * Set the maximum history size */ void CHtmlInputBuf::set_hist_size(size_t max_hist_cnt) { /* allocate the buffer */ history_ = (textchar_t **)th_malloc(max_hist_cnt * sizeof(textchar_t *)); histsiz_ = max_hist_cnt; /* no history entries yet */ histcnt_ = 0; histpos_ = 0; /* no saved current line yet */ saved_cur_line_ = 0; } /* * Select the previous history item */ int CHtmlInputBuf::select_prev_hist() { /* if we're at the first item already, we can't go back any further */ if (histpos_ == 0) return FALSE; /* if we're editing a line not in the history buffer, save it */ if (histpos_ == histcnt_) save_cur_line(); /* save undo */ save_undo(); /* back up one position */ --histpos_; /* select this item */ return select_cur_hist(); } /* * Select a previous history item that matches the given leading substring. * This treats the history buffer as circular. */ int CHtmlInputBuf::select_prev_hist_prefix(const textchar_t *pre, size_t len) { size_t cur, start; /* if there's nothing in the history buffer, ignore this */ if (histcnt_ == 0) return FALSE; /* start with the previous line, wrapping at the start of the list */ cur = start = (histpos_ == 0 ? histcnt_ : histpos_) - 1; /* search for a matching line; if we get back to where we started, fail */ do { /* if this one matches, stop here */ if (strlen(history_[cur]) > len && memcmp(history_[cur], pre, len) == 0) { int ret; /* remember the current caret position so we can restore it */ size_t old_caret = caret_; /* activate this position */ histpos_ = cur; /* we're about to change the buffer, so save undo */ save_undo(); /* select this item */ ret = select_cur_hist(); /* if that succeeded, restore the caret position */ if (ret) sel_start_ = sel_end_ = caret_ = old_caret; /* return the result */ return ret; } /* move to the previous line, wrapping at the start of the list */ cur = (cur == 0 ? histcnt_ : cur) - 1; } while (cur != start); /* we didn't find a match; ignore it */ return FALSE; } /* * Select a previous history item based on the current buffer contents and * caret position. */ int CHtmlInputBuf::select_prev_hist_prefix() { /* select based on the current buffer up to the caret position */ return select_prev_hist_prefix(buf_, caret_); } /* * Select the next history item */ int CHtmlInputBuf::select_next_hist() { /* if we're at the last item already, we can't go any further */ if (histpos_ == histcnt_) return FALSE; /* save undo */ save_undo(); /* move on to the next item */ ++histpos_; /* select the item */ return select_cur_hist(); } /* * Select the current history item into the buffer */ int CHtmlInputBuf::select_cur_hist() { size_t len; const textchar_t *src; /* if we're after the last item, simply clear the line */ if (histpos_ == histcnt_) src = saved_cur_line_; else src = history_[histpos_]; /* if we don't have anything, delete the line and be done with it */ if (src == 0) return del_line(); /* if this item doesn't fit in the buffer, truncate it */ len = strlen(src); if (len > bufsiz_) len = bufsiz_; /* copy this item into the buffer */ memcpy(buf_, src, len); len_ = len; /* position the cursor at the end of this item */ sel_start_ = sel_end_ = caret_ = len; show_caret(); /* we changed the buffer */ return TRUE; } /* * Save the current line, so that if we scroll through the history we * can come back to the original line under construction */ void CHtmlInputBuf::save_cur_line() { /* if we have no buffer, there's nothing to save */ if (buf_ == 0) return; /* if we have an old saved line, lose it */ if (saved_cur_line_) th_free(saved_cur_line_); /* allocate space for the current line and store it */ saved_cur_line_ = (textchar_t *) th_malloc((len_ + 1) * sizeof(textchar_t)); memcpy(saved_cur_line_, buf_, len_); saved_cur_line_[len_] = '\0'; } /* * Add an item to the history buffer. If the history is full, delete * the oldest item to make room. */ void CHtmlInputBuf::add_hist(const textchar_t *str, size_t len) { textchar_t *cpy; /* if it's an exact copy of the most recent history item, ignore it */ if (histcnt_ != 0 && strlen(history_[histcnt_ - 1]) == len && memcmp(history_[histcnt_ - 1], str, len) == 0) { /* it's an exact duplicate - don't bother adding it */ return; } /* if we don't have room, make room by deleting the oldest item */ if (histcnt_ == histsiz_) { /* delete the oldest one */ th_free(history_[0]); /* move the array down a notch */ memmove(history_, history_ + 1, (histsiz_ - 1) * sizeof(history_[0])); /* that's one less entry */ --histcnt_; } /* make a copy of this item, adding null termination */ cpy = (textchar_t *)th_malloc((len + 1) * sizeof(textchar_t)); memcpy(cpy, str, len); cpy[len] = '\0'; /* add the new item to our list */ history_[histcnt_++] = cpy; } /* * Undo the last change */ int CHtmlInputBuf::undo() { textchar_t *undo_buf; size_t undo_len; size_t undo_sel_start, undo_sel_end, undo_caret; size_t undo_histpos; /* if we have no buffer, there's nothing to do */ if (buf_ == 0) return FALSE; /* do nothing if there's no undo */ if (undo_buf_ == 0) return FALSE; /* * take over the current undo buffer, so that we can save the * current buffer as the new undo buffer but still have the old undo * information around so that we can apply it */ undo_buf = undo_buf_; undo_len = undo_len_; undo_sel_start = undo_sel_start_; undo_sel_end = undo_sel_end_; undo_caret = undo_caret_; undo_histpos = undo_histpos_; /* we now own the old undo buffer - forget it globally */ undo_buf_ = 0; /* save the current buffer as the new undo */ save_undo(); /* apply the undo */ memcpy(buf_, undo_buf, undo_len); len_ = undo_len; sel_start_ = undo_sel_start; sel_end_ = undo_sel_end; caret_ = undo_caret; histpos_ = undo_histpos; /* drop the old undo buffer */ th_free(undo_buf); /* we made some changes */ return TRUE; } /* * Save a change for later undoing */ void CHtmlInputBuf::save_undo() { /* if we have no buffer, there's nothing to do */ if (buf_ == 0) return; /* drop any previous undo buffer */ if (undo_buf_ != 0) th_free(undo_buf_); /* allocate space for the current buffer */ undo_buf_ = (textchar_t *)th_malloc(len_); undo_len_ = len_; memcpy(undo_buf_, buf_, len_); /* remember the selection points for after the undo */ undo_sel_start_ = sel_start_; undo_sel_end_ = sel_end_; undo_caret_ = caret_; /* remember history position */ undo_histpos_ = histpos_; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmlinp.h����������������������������������������������������������������������0000664�0000000�0000000�00000030377�12650170723�0016316�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/html/htmlinp.h,v 1.2 1999/05/17 02:52:21 MJRoberts Exp $ */ /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlinp.h - HTML input editor Function This class handles the internals of editing an input buffer. This class does not provide a user interface; instead, it's intended to be used by user interface code to simplify the maintenance of an input buffer. This class also provides a command history function. Notes The caller provides the buffer, and is responsible for managing the memory containing the buffer (in particular, we will not delete the buffer). Modified 09/28/97 MJRoberts - Creation */ #ifndef HTMLINP_H #define HTMLINP_H #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef HTML_OS_H #include "html_os.h" #endif class CHtmlInputBuf { public: CHtmlInputBuf(size_t max_hist_cnt) { undo_buf_ = 0; setbuf(0, 0); set_hist_size(max_hist_cnt); utf8_ = FALSE; } CHtmlInputBuf(textchar_t *buf, size_t bufsiz, size_t max_hist_cnt) { undo_buf_ = 0; setbuf(buf, bufsiz); set_hist_size(max_hist_cnt); utf8_ = FALSE; } ~CHtmlInputBuf(); /* * Set the input buffer's character set (also known as the "code * page"). This MUST be called IF the system implementation of * os_next_char() and os_prev_char() require the character set * information. * * In general, the system window code will need to call this for * implementations that (a) support multi-byte character sets (MBCS) * and (b) allow the character set to change on the fly. On * single-byte-only platforms, os_next_char() and os_prev_char() are * trivial in that one character always equals one byte, so the * character set information isn't needed to implement them. Even if * MBCSs are supported, the character set information isn't needed if * the character set can't change during execution, since the system * code can simply assume everything's in the fixed app-wide character * set. It's only necessary to have the per-string information on * systems like Windows, where MBCSs are supported and where each * string can be in its own separate character set. * * If the character set information is needed, this must be called * immediately after construction, and again each time the buffer * character set changes (for example, due to a user preference * change). * * Note that only one character set can be selected at a time per input * buffer. It's not possible for an individual input buffer to use a * mix of different character sets - the whole buffer must be in a * single character set. */ void set_charset(oshtml_charset_id_t charset) { charset_ = charset; } /* * Put the buffer in UTF-8 mode. We're in single-byte mode by default, * meaning that one character == one byte. In UTF-8 mode, we'll * observe UTF-8 multi-byte characters when stepping through the * buffer, to ensure that we always move by whole characters. (Note, * however, that we don't check explicit position settings sent in from * the caller for validity, such as in set_caret() or set_sel_range(). * We only check our own changes; we assume that when our caller puts * us in UTF-8 mode, the caller is also UTF-8 aware, so we leave it up * to the caller to get its own position settings right.) */ void set_utf8_mode(int f) { utf8_ = f; } int get_utf8_mode() const { return utf8_; } /* increment a buffer offset, observing utf-8 mode as needed */ size_t nextchar(size_t idx) { /* move to the next byte */ idx = os_next_char(charset_, buf_ + idx, len_ - idx) - buf_; /* in utf-8 mode, skip continuation characters (up to 2 per char) */ if (utf8_) { if ((*(buf_ + idx) & 0xC0) == 0x80) ++idx; if ((*(buf_ + idx) & 0xC0) == 0x80) ++idx; } /* return the result */ return idx; } /* decrement a pointer into our buffer, observing utf-8 mode as needed */ size_t prevchar(size_t idx) { /* move to the previous byte */ idx = os_prev_char(charset_, buf_ + idx, buf_) - buf_; /* in utf-8 mode, skip continuation characters (up to 2 per char) */ if (utf8_) { if (idx > 0 && (*(buf_ + idx) & 0xC0) == 0x80) --idx; if (idx > 0 && (*(buf_ + idx) & 0xC0) == 0x80) --idx; } /* return the result */ return idx; } /* set the buffer and initialize the editing state */ void setbuf(textchar_t *buf, size_t bufsiz) { setbuf(buf, bufsiz, 0); } void setbuf(textchar_t *buf, size_t bufsiz, size_t initlen); /* change the buffer, without resetting any editing state */ void changebuf(textchar_t *buf, size_t bufsiz); /* set the selection range */ void get_sel_range(size_t *start, size_t *end, size_t *caret) const { /* set the range */ *start = sel_start_; *end = sel_end_; *caret = caret_; } bool has_sel_range() { return sel_start_ != sel_end_; } /* show or hide the caret */ void show_caret() { caret_vis_ = TRUE; } void hide_caret() { caret_vis_ = FALSE; } /* determine if the caret is showing */ int is_caret_visible() const { return caret_vis_; } /* get/set the current command length */ size_t getlen() const { return len_; } void setlen(size_t len) { len_ = len; } /* get the buffer pointer */ textchar_t *getbuf() const { return buf_; } /* * Set the selection range and caret position. The caret should * always be at one end or the other of the selection range. */ void set_sel_range(size_t start, size_t end, size_t caret) { /* make sure everything is in range */ if (start > len_) start = len_; if (end > len_) end = len_; if (caret > len_) caret = len_; /* save the caret position */ caret_ = caret; /* make sure the range is in canonical order (start <= end) */ if (start <= end) { sel_start_ = start; sel_end_ = end; } else { sel_end_ = start; sel_start_ = end; } /* show the caret */ show_caret(); } /* set the insertion point and set a zero-length range */ void set_caret(size_t caret) { /* make sure the value is in range */ if (caret > len_) caret = len_; /* set the insertion point and range to the new point */ caret_ = sel_start_ = sel_end_ = caret; } /* get the caret position */ size_t get_caret() const { return caret_; } /* * Delete the selected range. Returns true if anything changed, * false if not. */ int del_selection(); /* * Move the cursor left or right one character. If 'extend' is * true, we'll extend the selection range, otherwise we'll move the * cursor and set a zero-length selection range. If 'word' is true, * we'll move to the next word break. */ void move_left(int extend, int word); void move_right(int extend, int word); /* * Backspace - delete selection range, or delete character to the * left of the cursor if the selection range length is zero. * Returns true if anything changed, false if not. */ int backspace(); /* * Delete right - delete the selection range, or delete the * character to the right of the cursor if the selection range * length is zero. Returns true if anything changed, false if not. */ int del_right(); /* * Delete to end of line. Returns true if anything changed. */ int del_eol(); /* * Delete entire line. Returns true if anything changed. */ int del_line(); /* * Add a character; replaces the selection range, or inserts the * character at the selection point if the range is zero length. * Returns true if anything changed, false if not. */ int add_char(textchar_t c); /* * Add a UTF-8 character. We accept characters in the basic * multilingual plane (u0000 to uFFFF) only. We'll encode the * character into multiple bytes as needed to represent it as UTF-8. * Returns true if anything changed, false if not. If the character is * too large to fit the buffer when expanded into UTF-8, we'll return * false and insert nothing - we'll never insert an ill-formed partial * character. */ int add_char_utf8(unsigned int c); /* * Add a string; replaces the selection range or inserts the string * at the selection point if the range is zero length. Returns true * if anything changed, false if not. If 'trunc' is true, we'll * truncate the input if necessary to make it fit in the buffer, * otherwise we'll ignore the entire request if the input won't fit. */ int add_string(const textchar_t *str, size_t len, int trunc); /* * Move the selection to the start/end of the command. If 'extend' * is true, extends range to the start/end of the command. */ void start_of_line(int extend); void end_of_line(int extend); /* add a history item */ void add_hist(const textchar_t *str, size_t len); /* add the current buffer contents to the history list */ void add_hist() { add_hist(buf_, len_); } /* * Set the history cursor to the last (most recent) history item. * Calling select_prev_hist() will select the last item into the * buffer. This should be called at the start of each new command. */ void set_last_hist() { histpos_ = histcnt_; } /* * Select the current/next/previous history item into the buffer. * These return true if anything happened. */ int select_cur_hist(); int select_next_hist(); int select_prev_hist(); /* select a previous history item matching the given prefix string */ int select_prev_hist_prefix(const textchar_t *pre, size_t len); /* select based on the current buffer contents and caret position */ int select_prev_hist_prefix(); /* check to see if we can undo anything in this command */ int can_undo() { return undo_buf_ != 0; } /* undo the last change */ int undo(); private: /* initialize the history size */ void set_hist_size(size_t max_hist_cnt); /* adjust selection range */ void adjust_sel(size_t newpos, int extend); /* * save the current line under construction for later * reconstruction if we scroll through the history back to the new, * otherwise unsaved, line */ void save_cur_line(); /* save the current buffer in the undo */ void save_undo(); /* buffer */ textchar_t *buf_; size_t bufsiz_; /* the character set */ oshtml_charset_id_t charset_; /* length of the input in the buffer */ size_t len_; /* selection range */ size_t sel_start_; size_t sel_end_; /* * caret position - if a range is selected, the caret will always * be at one end or the other of the selected range */ size_t caret_; /* command history list */ textchar_t **history_; /* saved current line under construction */ textchar_t *saved_cur_line_; /* maximum number of entries in history list */ size_t histsiz_; /* current number of entries in history list */ size_t histcnt_; /* current position in history buffer */ size_t histpos_; /* caret visibility */ int caret_vis_ : 1; /* flag: true -> we're in UTF-8 mode; false -> single-byte mode */ int utf8_ : 1; /* undo buffer - contents of buffer prior to last change */ textchar_t *undo_buf_; size_t undo_len_; size_t undo_sel_start_; size_t undo_sel_end_; size_t undo_caret_; size_t undo_histpos_; }; #endif /* HTMLINP_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmlprs.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000255413�12650170723�0016667�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/html/htmlprs.cpp,v 1.2 1999/05/17 02:52:21 MJRoberts Exp $"; #endif /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlprs - HTML parser Function Notes The parser assumes that the client will parse HTML source in chunks of a reasonable size. In particular, the client must write a well-formed chunk of HTML to a buffer, then submit the buffer to the parser; the caller can't break a single tag or other syntactic unit across chunks. Modified 08/23/97 MJRoberts - Creation */ #include <assert.h> #include <memory.h> #include <string.h> #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef HTML_OS_H #include "html_os.h" #endif #ifndef HTMLPRS_H #include "htmlprs.h" #endif #ifndef HTMLTAGS_H #include "htmltags.h" #endif #ifndef HTMLTXAR_H #include "htmltxar.h" #endif #ifndef HTMLHASH_H #include "htmlhash.h" #endif #ifndef HTMLSYS_H #include "htmlsys.h" #endif #ifdef TADSHTML_DEBUG #include <stdio.h> #include <stdarg.h> #endif /* ------------------------------------------------------------------------ */ /* * Hash table entry subclass for an ampersand sequence name. Entity * names are case-sensitive, thus the xxxCS base class. */ class CHtmlHashEntryAmp: public CHtmlHashEntryCS { public: CHtmlHashEntryAmp(const textchar_t *str, size_t len, int copy, unsigned int charval) : CHtmlHashEntryCS(str, len, copy) { charval_ = charval; } /* * Character value. We store the standard HTML code value, which is * the same as the Unicode value of the character. (Whenever we use * a character entity value, we'll translate the character value to * the appropriate local character set code point.) */ unsigned int charval_; }; /* ------------------------------------------------------------------------ */ /* * Hash table entry subclass for a tag name. Entity names are * case-insensitive, hence the xxxCI base class. */ class CHtmlHashEntryTag: public CHtmlHashEntryCI { public: CHtmlHashEntryTag(const textchar_t *str, size_t len, int copy, class CHtmlTag *(*create_func)(class CHtmlParser *), void (*end_func)(class CHtmlParser *, const textchar_t *, size_t)) : CHtmlHashEntryCI(str, len, copy) { create_func_ = create_func; end_func_ = end_func; } /* invoke the creator function and return the result */ CHtmlTag *create_new_tag(class CHtmlParser *parser) { return (*create_func_)(parser); } /* process the closing form of this tag (the </xxx> tag) */ void process_end_tag(class CHtmlParser *parser, const textchar_t *tag, size_t len) { (*end_func_)(parser, tag, len); } private: /* tag object creation function */ CHtmlTag *(*create_func_)(class CHtmlParser *); /* end tag function */ void (*end_func_)(class CHtmlParser *, const textchar_t *, size_t); }; /* ------------------------------------------------------------------------ */ /* * Hash table entry for attribute names */ class CHtmlHashEntryAttr: public CHtmlHashEntryCI { public: CHtmlHashEntryAttr(const textchar_t *str, size_t len, int copy, HTML_Attrib_id_t id) : CHtmlHashEntryCI(str, len, copy) { id_ = id; } /* hash ID value */ HTML_Attrib_id_t id_; }; /* ------------------------------------------------------------------------ */ /* * HTML parser. The client first writes HTML source code to a * CHtmlTextBuffer object, then submits the buffer object to the parser * to turn into a parsed tag list. The parser can then in turn be * submitted to a renderer for display. */ /* * Initialize */ void CHtmlParser::init(int literal_mode) { /* set up a table of '&' character name sequences */ struct amp_tbl_t { const textchar_t *cname; unsigned int cval; }; static const struct amp_tbl_t amp_tbl[] = { { "amp", '&' }, { "quot", '"' }, { "gt", '>' }, { "lt", '<' }, { "nbsp", 160 }, { "iexcl", 161 }, { "cent", 162 }, { "pound", 163 }, { "curren", 164 }, { "yen", 165 }, { "brvbar", 166 }, { "sect", 167 }, { "uml", 168 }, { "copy", 169 }, { "ordf", 170 }, { "laquo", 171 }, { "not", 172 }, { "shy", 173 }, { "reg", 174 }, { "macr", 175 }, { "deg", 176 }, { "plusmn", 177 }, { "sup2", 178 }, { "sup3", 179 }, { "acute", 180 }, { "micro", 181 }, { "para", 182 }, { "middot", 183 }, { "cedil", 184 }, { "sup1", 185 }, { "ordm", 186 }, { "raquo", 187 }, { "frac14", 188 }, { "frac12", 189 }, { "frac34", 190 }, { "iquest", 191 }, { "times", 215 }, { "divide", 247 }, { "AElig", 198 }, { "Aacute", 193 }, { "Acirc", 194 }, { "Agrave", 192 }, { "Aring", 197 }, { "Atilde", 195 }, { "Auml", 196 }, { "Ccedil", 199 }, { "ETH", 208 }, { "Eacute", 201 }, { "Ecirc", 202 }, { "Egrave", 200 }, { "Euml", 203 }, { "Iacute", 205 }, { "Icirc", 206 }, { "Igrave", 204 }, { "Iuml", 207 }, { "Ntilde", 209 }, { "Oacute", 211 }, { "Ocirc", 212 }, { "Ograve", 210 }, { "Oslash", 216 }, { "Otilde", 213 }, { "Ouml", 214 }, { "THORN", 222 }, { "Uacute", 218 }, { "Ucirc", 219 }, { "Ugrave", 217 }, { "Uuml", 220 }, { "Yacute", 221 }, { "aacute", 225 }, { "acirc", 226 }, { "aelig", 230 }, { "agrave", 224 }, { "aring", 229 }, { "atilde", 227 }, { "auml", 228 }, { "ccedil", 231 }, { "eacute", 233 }, { "ecirc", 234 }, { "egrave", 232 }, { "eth", 240 }, { "euml", 235 }, { "iacute", 237 }, { "icirc", 238 }, { "igrave", 236 }, { "iuml", 239 }, { "ntilde", 241 }, { "oacute", 243 }, { "ocirc", 244 }, { "ograve", 242 }, { "oslash", 248 }, { "otilde", 245 }, { "ouml", 246 }, { "szlig", 223 }, { "thorn", 254 }, { "uacute", 250 }, { "ucirc", 251 }, { "ugrave", 249 }, { "uuml", 252 }, { "yacute", 253 }, { "thorn", 254 }, { "yuml", 255 }, /* TADS extensions to the standard characters */ { "lsq", 8216 }, { "rsq", 8217 }, { "ldq", 8220 }, { "rdq", 8221 }, { "endash", 8211 }, { "emdash", 8212 }, { "trade", 8482 }, { "zwsp", 0x200b }, { "zwnbsp", 0xfeff }, { "tpmsp", 0x2004 }, { "fpmsp", 0x2005 }, { "spmsp", 0x2006 }, { "figsp", 0x2007 }, { "puncsp", 0x2008 }, { "hairsp", 0x200a }, /* HTML 4.0 character extensions */ { "ndash", 8211 }, { "mdash", 8212 }, { "lsquo", 8216 }, { "rsquo", 8217 }, { "ldquo", 8220 }, { "rdquo", 8221 }, { "sbquo", 8218 }, { "bdquo", 8222 }, { "lsaquo", 8249 }, { "rsaquo", 8250 }, { "dagger", 8224 }, { "Dagger", 8225 }, { "OElig", 338 }, { "oelig", 339 }, { "permil", 8240 }, { "Yuml", 376 }, { "scaron", 353 }, { "Scaron", 352 }, { "circ", 710 }, { "tilde", 732 }, { "ensp", 0x2002 }, { "emsp", 0x2003 }, { "thinsp", 0x2009 }, /* Greek letters */ { "Alpha", 913 }, { "Beta", 914 }, { "Gamma", 915 }, { "Delta", 916 }, { "Epsilon", 917 }, { "Zeta", 918 }, { "Eta", 919 }, { "Theta", 920 }, { "Iota", 921 }, { "Kappa", 922 }, { "Lambda", 923 }, { "Mu", 924 }, { "Nu", 925 }, { "Xi", 926 }, { "Omicron", 927 }, { "Pi", 928 }, { "Rho", 929 }, { "Sigma", 931 }, { "Tau", 932 }, { "Upsilon", 933 }, { "Phi", 934 }, { "Chi", 935 }, { "Psi", 936 }, { "Omega", 937 }, { "alpha", 945 }, { "beta", 946 }, { "gamma", 947 }, { "delta", 948 }, { "epsilon", 949 }, { "zeta", 950 }, { "eta", 951 }, { "theta", 952 }, { "iota", 953 }, { "kappa", 954 }, { "lambda", 955 }, { "mu", 956 }, { "nu", 957 }, { "xi", 958 }, { "omicron", 959 }, { "pi", 960 }, { "rho", 961 }, { "sigmaf", 962 }, { "sigma", 963 }, { "tau", 964 }, { "upsilon", 965 }, { "phi", 966 }, { "chi", 967 }, { "psi", 968 }, { "omega", 969 }, { "thetasym", 977 }, { "upsih", 978 }, { "piv", 982 }, /* general punctuation marks */ { "bull", 8226 }, { "hellip", 8230 }, { "prime", 8242 }, { "Prime", 8243 }, { "oline", 8254 }, { "frasl", 8260 }, /* letter-like symbols */ { "weierp", 8472 }, { "image", 8465 }, { "real", 8476 }, { "alefsym", 8501 }, /* arrows */ { "larr", 8592 }, { "uarr", 8593 }, { "rarr", 8594 }, { "darr", 8595 }, { "harr", 8596 }, { "crarr", 8629 }, { "lArr", 8656 }, { "uArr", 8657 }, { "rArr", 8658 }, { "dArr", 8659 }, { "hArr", 8660 }, /* mathematical operators */ { "forall", 8704 }, { "part", 8706 }, { "exist", 8707 }, { "empty", 8709 }, { "nabla", 8711 }, { "isin", 8712 }, { "notin", 8713 }, { "ni", 8715 }, { "prod", 8719 }, { "sum", 8721 }, { "minus", 8722 }, { "lowast", 8727 }, { "radic", 8730 }, { "prop", 8733 }, { "infin", 8734 }, { "ang", 8736 }, { "and", 8743 }, { "or", 8744 }, { "cap", 8745 }, { "cup", 8746 }, { "int", 8747 }, { "there4", 8756 }, { "sim", 8764 }, { "cong", 8773 }, { "asymp", 8776 }, { "ne", 8800 }, { "equiv", 8801 }, { "le", 8804 }, { "ge", 8805 }, { "sub", 8834 }, { "sup", 8835 }, { "nsub", 8836 }, { "sube", 8838 }, { "supe", 8839 }, { "oplus", 8853 }, { "otimes", 8855 }, { "perp", 8869 }, { "sdot", 8901 }, { "lceil", 8968 }, { "rceil", 8969 }, { "lfloor", 8970 }, { "rfloor", 8971 }, { "lang", 9001 }, { "rang", 9002 }, /* geometric shapes */ { "loz", 9674 }, /* miscellaneous symbols */ { "spades", 9824 }, { "clubs", 9827 }, { "hearts", 9829 }, { "diams", 9830 }, /* Latin-extended B */ { "fnof", 402 }, /* Latin-2 characters */ { "Aogon", 260 }, { "breve", 728 }, { "Lstrok", 321 }, { "Lcaron", 317 }, { "Sacute", 346 }, { "Scedil", 350 }, { "Tcaron", 356 }, { "Zacute", 377 }, { "Zcaron", 381 }, { "Zdot", 379 }, { "aogon", 261 }, { "ogon", 731 }, { "lstrok", 322 }, { "lcaron", 318 }, { "sacute", 347 }, { "caron", 711 }, { "scedil", 351 }, { "tcaron", 357 }, { "zacute", 378 }, { "dblac", 733 }, { "zcaron", 382 }, { "zdot", 380 }, { "Racute", 340 }, { "Abreve", 258 }, { "Lacute", 313 }, { "Cacute", 262 }, { "Ccaron", 268 }, { "Eogon", 280 }, { "Ecaron", 282 }, { "Dcaron", 270 }, { "Dstrok", 272 }, { "Nacute", 323 }, { "Ncaron", 327 }, { "Odblac", 336 }, { "Rcaron", 344 }, { "Uring", 366 }, { "Udblac", 368 }, { "Tcedil", 354 }, { "racute", 341 }, { "abreve", 259 }, { "lacute", 314 }, { "cacute", 263 }, { "ccaron", 269 }, { "eogon", 281 }, { "ecaron", 283 }, { "dcaron", 271 }, { "dstrok", 273 }, { "nacute", 324 }, { "ncaron", 328 }, { "odblac", 337 }, { "rcaron", 345 }, { "uring", 367 }, { "udblac", 369 }, { "tcedil", 355 }, { "dot", 729 } }; const struct amp_tbl_t *ampptr; size_t i; struct tag_tbl_t { const textchar_t *(*name_func)(); CHtmlTag *(*create_func)(class CHtmlParser *); void (*end_func)(class CHtmlParser *, const textchar_t *, size_t); }; static const struct tag_tbl_t tag_tbl[] = { #define HTML_REG_TAG(clsname) \ { &clsname::get_tag_name_st, \ &clsname::create_tag_instance, \ &clsname::process_end_tag }, #include "htmlreg.h" { 0, 0 } }; const struct tag_tbl_t *tagptr; /* structure for the attribute name list */ struct attr_t { const textchar_t *nm; HTML_Attrib_id_t id; }; struct attr_t *attr; /* list of identifiers that can appear as attribute names */ static struct attr_t attr_list[] = { { "background", HTML_Attrib_background }, { "bgcolor", HTML_Attrib_bgcolor }, { "text", HTML_Attrib_text }, { "link", HTML_Attrib_link }, { "vlink", HTML_Attrib_vlink }, { "alink", HTML_Attrib_alink }, { "hlink", HTML_Attrib_hlink }, { "align", HTML_Attrib_align }, { "compact", HTML_Attrib_compact }, { "type", HTML_Attrib_type }, { "start", HTML_Attrib_start }, { "value", HTML_Attrib_value }, { "width", HTML_Attrib_width }, { "noshade", HTML_Attrib_noshade }, { "size", HTML_Attrib_size }, { "valign", HTML_Attrib_valign }, { "border", HTML_Attrib_border }, { "cellspacing", HTML_Attrib_cellspacing }, { "cellpadding", HTML_Attrib_cellpadding }, { "nowrap", HTML_Attrib_nowrap }, { "rowspan", HTML_Attrib_rowspan }, { "colspan", HTML_Attrib_colspan }, { "height", HTML_Attrib_height }, { "name", HTML_Attrib_name }, { "checked", HTML_Attrib_checked }, { "maxlength", HTML_Attrib_maxlength }, { "src", HTML_Attrib_src }, { "asrc", HTML_Attrib_asrc }, { "hsrc", HTML_Attrib_hsrc }, { "multiple", HTML_Attrib_multiple }, { "selected", HTML_Attrib_selected }, { "rows", HTML_Attrib_rows }, { "cols", HTML_Attrib_cols }, { "href", HTML_Attrib_href }, { "rel", HTML_Attrib_rel }, { "rev", HTML_Attrib_rev }, { "title", HTML_Attrib_title }, { "alt", HTML_Attrib_alt }, { "hspace", HTML_Attrib_hspace }, { "vspace", HTML_Attrib_vspace }, { "usemap", HTML_Attrib_usemap }, { "ismap", HTML_Attrib_ismap }, { "codebase", HTML_Attrib_codebase }, { "code", HTML_Attrib_code }, { "face", HTML_Attrib_face }, { "color", HTML_Attrib_color }, { "clear", HTML_Attrib_clear }, { "shape", HTML_Attrib_shape }, { "coords", HTML_Attrib_coords }, { "nohref", HTML_Attrib_nohref }, { "id", HTML_Attrib_id }, { "remove", HTML_Attrib_remove }, { "to", HTML_Attrib_to }, { "indent", HTML_Attrib_indent }, { "dp", HTML_Attrib_dp }, { "plain", HTML_Attrib_plain }, { "forced", HTML_Attrib_forced }, { "continue", HTML_Attrib_continue }, { "seqnum", HTML_Attrib_seqnum }, { "layer", HTML_Attrib_layer }, { "cancel", HTML_Attrib_cancel }, { "repeat", HTML_Attrib_repeat }, { "random", HTML_Attrib_random }, { "fadein", HTML_Attrib_fadein }, { "fadeout", HTML_Attrib_fadeout }, { "interrupt", HTML_Attrib_interrupt }, { "sequence", HTML_Attrib_sequence }, { "removeall", HTML_Attrib_removeall }, { "append", HTML_Attrib_append }, { "noenter", HTML_Attrib_noenter }, { "char", HTML_Attrib_char }, { "word", HTML_Attrib_word }, { "hidden", HTML_Attrib_hidden }, { "hover", HTML_Attrib_hover }, { "input", HTML_Attrib_input }, { "volume", HTML_Attrib_volume }, { 0, HTML_Attrib_invalid } }; /* list of identifiers that can appear as attribute values */ static struct attr_t val_list[] = { { "black", HTML_Attrib_black }, { "silver", HTML_Attrib_silver }, { "gray", HTML_Attrib_gray }, { "white", HTML_Attrib_white }, { "maroon", HTML_Attrib_maroon }, { "red", HTML_Attrib_red }, { "purple", HTML_Attrib_purple }, { "fuchsia", HTML_Attrib_fuchsia }, { "green", HTML_Attrib_green }, { "lime", HTML_Attrib_lime }, { "olive", HTML_Attrib_olive }, { "yellow", HTML_Attrib_yellow }, { "navy", HTML_Attrib_navy }, { "blue", HTML_Attrib_blue }, { "teal", HTML_Attrib_teal }, { "aqua", HTML_Attrib_aqua }, { "left", HTML_Attrib_left }, { "right", HTML_Attrib_right }, { "center", HTML_Attrib_center }, { "disc", HTML_Attrib_disc }, { "square", HTML_Attrib_square }, { "circle", HTML_Attrib_circle }, { "top", HTML_Attrib_top }, { "middle", HTML_Attrib_middle }, { "bottom", HTML_Attrib_bottom }, { "password", HTML_Attrib_password }, { "checkbox", HTML_Attrib_checkbox }, { "radio", HTML_Attrib_radio }, { "submit", HTML_Attrib_submit }, { "reset", HTML_Attrib_reset }, { "file", HTML_Attrib_file }, { "hidden", HTML_Attrib_hidden }, { "image", HTML_Attrib_image }, { "all", HTML_Attrib_all }, { "rect", HTML_Attrib_rect }, { "poly", HTML_Attrib_poly }, { "justify", HTML_Attrib_justify }, { "decimal", HTML_Attrib_decimal }, { "previous", HTML_Attrib_previous }, { "border", HTML_Attrib_border }, { "foreground", HTML_Attrib_foreground }, { "ambient", HTML_Attrib_ambient }, { "bgambient", HTML_Attrib_bgambient }, { "background", HTML_Attrib_background }, { "loop", HTML_Attrib_loop }, { "random", HTML_Attrib_random }, { "replace", HTML_Attrib_replace }, { "cycle", HTML_Attrib_cycle }, { "cancel", HTML_Attrib_cancel }, { "statusbg", HTML_Attrib_statusbg }, { "statustext", HTML_Attrib_statustext }, { "link", HTML_Attrib_link }, { "alink", HTML_Attrib_alink }, { "hlink", HTML_Attrib_hlink }, { "bgcolor", HTML_Attrib_bgcolor }, { "text", HTML_Attrib_text }, { "char", HTML_Attrib_char }, { "word", HTML_Attrib_word }, { "input", HTML_Attrib_input }, { 0, HTML_Attrib_invalid } }; /* no system frame object yet */ frame_ = 0; /* create a hash table for the '&' sequence names, and fill it up */ amp_table_ = new CHtmlHashTable(256, new CHtmlHashFuncCS); for (i = 0, ampptr = amp_tbl ; i < sizeof(amp_tbl)/sizeof(amp_tbl[0]) ; ++i, ++ampptr) { CHtmlHashEntryAmp *entry; /* create a new hash table entry and add it to the hash table */ entry = new CHtmlHashEntryAmp(ampptr->cname, get_strlen(ampptr->cname), FALSE, ampptr->cval); amp_table_->add(entry); } /* create a hash table for the tags, and fill it up */ tag_table_ = new CHtmlHashTable(256, new CHtmlHashFuncCI); for (tagptr = tag_tbl ; tagptr->name_func != 0 ; ++tagptr) { CHtmlHashEntryTag *entry; const textchar_t *tagname; /* * get the tag name from the class's static member function for * naming */ tagname = (*tagptr->name_func)(); /* create a new hash table entry and add it to the hash table */ entry = new CHtmlHashEntryTag(tagname, get_strlen(tagname), FALSE, tagptr->create_func, tagptr->end_func); tag_table_->add(entry); } /* create and populate the hash table for attribute names */ attr_table_ = new CHtmlHashTable(256, new CHtmlHashFuncCI); for (attr = attr_list ; attr->nm != 0 ; ++attr) { CHtmlHashEntryAttr *entry; entry = new CHtmlHashEntryAttr(attr->nm, get_strlen(attr->nm), FALSE, attr->id); attr_table_->add(entry); } /* create and populate the hash table for attribute value names */ attr_val_table_ = new CHtmlHashTable(256, new CHtmlHashFuncCI); for (attr = val_list ; attr->nm != 0 ; ++attr) { CHtmlHashEntryAttr *entry; entry = new CHtmlHashEntryAttr(attr->nm, get_strlen(attr->nm), FALSE, attr->id); attr_val_table_->add(entry); } /* allocate the text array for storing the text stream */ text_array_ = new CHtmlTextArray; /* create the special outermost container */ container_depth_ = 1; container_ = new CHtmlTagOuter; /* start off obeying markups, unless in literal mode */ obey_markups_ = !literal_mode; obey_end_markups_ = !literal_mode; /* * start off treating whitespace characters as mere separators, * unless in literal mode */ obey_whitespace_ = (literal_mode != 0); break_long_lines_ = TRUE; /* start off eating whitespace characters */ eat_whitespace_ = TRUE; } /* * Destroy the parser */ CHtmlParser::~CHtmlParser() { /* delete the hash tables */ delete amp_table_; delete tag_table_; delete attr_table_; delete attr_val_table_; /* clear the page, deleting any tag lists we have accumulated */ clear_page(); /* delete the text array */ delete text_array_; /* * delete the special outermost container object, which stays * around until the bitter end (which would be now) */ delete container_; } /* * Clear the page */ void CHtmlParser::clear_page() { /* find the outermost container and make it current */ while (container_->get_container() != 0) container_ = container_->get_container(); /* delete everything within the outermost container */ container_->delete_contents(); /* delete all of the text in the text array */ text_array_->clear(); /* clear out the text accumulator */ curtext_.clear(); /* reset container depth */ container_depth_ = 1; /* start off eating whitespace, unless we're in obedient mode */ if (!obey_whitespace_) eat_whitespace_ = TRUE; } /* * Turn whitespace obedience on or off. When we first turn on * whitespace obedience, we check to see if we're at the end of a line; * if so, we skip up to the first character past the line break. */ void CHtmlParser::obey_whitespace(int flag, int break_long_lines) { /* note the break-long-lines setting */ break_long_lines_ = break_long_lines; /* ignore if this isn't a change from the status quo */ if (flag == obey_whitespace_) return; /* set the new mode */ obey_whitespace_ = flag; /* see if we're turning whitespace obedience on or off */ if (!flag) { const textchar_t *p = curtext_.getbuf(); size_t len = curtext_.getlen(); /* * Return to normal mode. If we added a newline to our * verbatim text just prior to this directive, take it back out. */ if (len > 0 && *(p + len - 1) == '\n') curtext_.setlen(len - 1); } } /* * Parse a character */ size_t CHtmlParser::parse_char(textchar_t *result, size_t result_buf_size, oshtml_charset_id_t *charset, int *changed_charset, unsigned int *special) { size_t char_bytes; /* presume no special unicode meaning */ if (special != 0) *special = 0; /* presume we won't need to change the character set */ if (changed_charset != 0) *changed_charset = FALSE; /* * note the length of the character in bytes (in case we're using a * multi-byte character set) */ char_bytes = p_.char_bytes(frame_->get_default_win_charset()); if (char_bytes > 1) { size_t i; /* * It's a multi-byte character. Assume that this means that it's * not a markup, since only ASCII characters can be involved in * markups, and we assume that our multi-byte character sets are * arranged in such a way that ASCII characters are all * single-byte. Simply copy all of the bytes of the character, to * ensure that we don't separate the bytes of a single character * into separate buffer runs, and return the character without * further interpretation. */ for (i = 0 ; i < char_bytes && i < result_buf_size ; p_.inc(), ++i) result[i] = p_.curchar(); /* null-terminate the result buffer */ result[i] = '\0'; /* return the result length */ return char_bytes; } /* start with the character as-is */ result[0] = p_.curchar(); result[1] = '\0'; /* if it's an ampersand, we need to parse the entity */ if (p_.curchar() == '&') return parse_char_entity(result, result_buf_size, charset, changed_charset, special); /* if it's a newline character, skip the newline sequence */ if (is_newline(result[0])) { /* skip the entire newline sequence */ p_.skip_newline(); /* * always use \n as the parsed text stream result, regardless of * what kind of newline sequence was in the source text */ result[0] = '\n'; } else p_.inc(); /* it's a single character */ return 1; } /* * Parse the character entity we're looking at. This is a subroutine for * parse_char(); this routine shouldn't be called directly by other code. * This will only be called when we find a '&' in the input stream. * * '*special' is a Unicode character code that we treat specially, such as * the special space characters. If this is provided, we'll set it to a * non-zero value on return if we find one of these Unicode values. We'll * also fill in the result buffer with an approximation, which case be * used if the caller isn't interested in the special Unicode character * but just wants a textual approximation. * * The caller will already have initialized '*charset' to false, and will * have set up the result buffer with a null character at the second * element (which assumes that we'll return a one-character string). */ size_t CHtmlParser::parse_char_entity(textchar_t *result, size_t result_size, oshtml_charset_id_t *charset, int *changed_charset, unsigned int *special) { size_t len; size_t skipcnt; /* presume we'll return a single character */ len = 1; /* if we're not obeying markups, parse the ampersand literally */ if (!obey_markups_) { /* skip the '&', and return the '&' literally */ p_.inc(); result[0] = '&'; return 1; } /* * make sure the entity is complete - if not, defer parsing it until we * get more text */ if (!check_entity_complete()) return 0; /* * check the next character - if it's not followed by a letter or '#' * and a digit, it's not a markup */ p_.inc(); if (p_.getlen() >= 2 && p_.curchar() == '#') { int hex = FALSE; /* skip the '#' */ skipcnt = 1; p_.inc(); /* if it's hex, accumulate the value in hex */ if (p_.curchar() == 'x' || p_.curchar() == 'X') { hex = TRUE; p_.inc(); ++skipcnt; } /* if the next character isn't a digit, it's not a markup */ if (is_digit(p_.curchar()) || (hex && is_hex_digit(p_.curchar()))) { unsigned int acc; /* read the digits */ for (acc = 0 ; p_.getlen() != 0 && (is_digit(p_.curchar()) || (hex && is_hex_digit(p_.curchar()))) ; p_.inc(), ++skipcnt) { acc *= (hex ? 16 : 10); acc += ascii_hex_to_int(p_.curchar()); } /* * if we stopped at a semicolon, the semicolon is part of the * markup, so skip it */ if (p_.getlen() != 0 && p_.curchar() == ';') { p_.inc(); ++skipcnt; } /* translate the character */ if (!special_entity(result, result_size, &len, acc, special)) { if (frame_ != 0) len = frame_->xlat_html4_entity(result, result_size, acc, charset, changed_charset); else result[0] = (textchar_t)acc; } } else { /* it's not a digit sequence, so it's not a markup */ p_.dec(skipcnt); result[0] = '&'; } } else if (p_.getlen() != 0 && is_alpha(p_.curchar())) { const int markup_maxlen = 12; textchar_t markup[markup_maxlen + 1]; textchar_t *dst; CHtmlHashEntryAmp *entry; /* accumulate the markup name */ for (skipcnt = 0, dst = markup ; p_.getlen() != 0 && (is_alpha(p_.curchar()) || is_digit(p_.curchar())) && dst - markup < markup_maxlen ; p_.inc(), ++skipcnt) { /* add this character */ *dst++ = p_.curchar(); } /* if we found a semicolon, it's part of the markup, so skip it */ if (p_.getlen() != 0 && p_.curchar() == ';') { p_.inc(); ++skipcnt; } /* find the markup in the markup table */ entry = (CHtmlHashEntryAmp *)amp_table_->find(markup, dst - markup); /* * if we didn't find an exact match, search for any sequence that * matches the leading substring */ if (entry == 0) { /* find the entry by leading substring */ entry = (CHtmlHashEntryAmp *)amp_table_-> find_leading_substr(markup, dst - markup); /* if we found it, skip the appropriate length */ if (entry != 0) p_.dec(skipcnt - entry->getlen()); } /* see if we found it */ if (entry != 0) { /* * found it - translate the character value to the current * character set */ if (!special_entity(result, result_size, &len, entry->charval_, special)) { if (frame_ != 0) len = frame_->xlat_html4_entity(result, result_size, entry->charval_, charset, changed_charset); else result[0] = (textchar_t)entry->charval_; } } else { /* we didn't find a match -- copy the whole thing as-is */ p_.dec(skipcnt); result[0] = '&'; } } else { /* it's not a markup */ result[0] = '&'; } /* return the length */ return len; } /* * Check for a special Unicode entity. This checks a Unicode character * value to determine if it's one of the special codes we interpret * directly, rather than mapping to the local character set. * * We give special meanings to the following characters: * * Note that this is used specifically to check entities given as '&' * markups before any local character set translation, which means the * character value we're looking up is always given in Unicode, regardless * of the local character set. This code is thus universal - it doesn't * matter what kind of local character set we're using, because we're * operating purely in the Unicode domain at this point. * * Returns true if we find a special character, false if not. Our * approximations are always simple ASCII, so there's no need for the * caller to perform further character set translation on the result when * we provide one. */ int CHtmlParser::special_entity(textchar_t *result, size_t result_size, size_t *outlen, unsigned int ch, unsigned int *special) { int found; /* presume we won't find a special character */ found = FALSE; /* check the input character */ switch (ch) { case 0x00AD: /* soft hyphen */ case 0xFEFF: /* zero-width non-breaking space */ case 0x200B: /* zero-width space */ /* * Approximate these as an empty string, since none of these has * any visible presence under normal conditions. (The soft hyphen * does appear visually as a hyphen when it's actually used as a * line breaking point, but if the caller is only interested in * the approximation, then they won't know to break the line here, * so they'll never need to render the hyphen.) */ *outlen = 0; result[0] = '\0'; found = TRUE; break; case 0x0015: /* our own quoted space */ case 0x00A0: /* non-breaking space */ case 0x2002: /* en space */ case 0x2003: /* em space */ case 0x2004: /* three-per-em space */ case 0x2005: /* four-per-em space */ case 0x2006: /* six-per-em space */ case 0x2007: /* figure space */ case 0x2008: /* punctuation space */ case 0x2009: /* thin space */ case 0x200A: /* hair space */ /* these all look like an ordinary space in text approximations */ *outlen = 1; result[0] = ' '; found = TRUE; break; } /* * if we found a special character, and the caller is interested in * the special meaning, provide it - the special meaning is simply the * unicode value for the special character itself */ if (found && special != 0) *special = ch; /* indicate whether or not we found anything special */ return found; } /* * Parse an identifier */ textchar_t *CHtmlParser::scan_ident(textchar_t *buf, size_t buflen) { textchar_t *endp; /* scan through the identifier until we find a space, '>', or '=' */ for (endp = buf ; p_.getlen() != 0 ; p_.inc()) { /* if we're at a space or the closing '>' delimiter, we're done */ if (is_space(p_.curchar()) || p_.curchar() == '>' || p_.curchar() == '=') break; /* accumulate this character, unless we're out of buffer space */ if ((size_t)(endp - buf) < buflen - 1) *endp++ = p_.curchar(); } /* null-terminate it */ *endp = '\0'; /* return a pointer to the end of the buffer */ return endp; } /* * Determine if a tag matches the current container tag. Returns true if * so, false if not. If it doesn't match, and "log" is true, we'll log the * mismatch as an error; otherwise, we'll silently ignore it. * * If 'find' is true, we'll search the stack for the matching open tag. If * we find a matching tag somewhere in the stack, we'll close all of the * tags nested within it. This makes us more tolerant of ill-formed HTML, * where an end-tag is omitted, by allowing us to match up the close tag * even if there was an unclosed tag nested within it. */ int CHtmlParser::end_tag_matches(const textchar_t *end_tag_name, size_t end_tag_len, int log, int find) { const textchar_t *start_tag_name; /* get the name of the starting tag */ start_tag_name = get_inner_container()->get_tag_name(); /* see if it matches the ending tag */ if (!get_inner_container()->tag_name_matches(end_tag_name, end_tag_len)) { /* log the error if desired */ if (log) { /* * Error - end tag doesn't match current start tag. Log * the error, but proceed as though it had matched. */ log_error("end tag </%.*s> doesn't match start tag <%s>", (int)end_tag_len, end_tag_name, start_tag_name); } /* * If 'find' is true, search the stack for the corresponding open * tag. If we find it, close the nested tags. This makes us * tolerant of ill-formed HTML that omits an end tag for a nested * structure. */ if (find) { CHtmlTagContainer *open_tag; /* scan the stack */ for (open_tag = get_inner_container() ; open_tag != 0 ; open_tag = open_tag->get_container()) { /* if this one matches our end tag, it's the one */ if (open_tag->tag_name_matches(end_tag_name, end_tag_len)) break; } /* * if we found the matching open tag, close everything nested * within it */ if (open_tag != 0) { /* found it - close everything within our open tag */ while (get_inner_container() != 0 && get_inner_container() != open_tag) { /* close the current innermost container */ close_current_tag(); } } } /* return mismatch indication */ return FALSE; } else { /* it matches */ return TRUE; } } /* * Skip whitespace and newlines following a tag */ void CHtmlParser::skip_posttag_whitespace() { /* * If we have whitespace followed by a newline, skip up through the * newline -- a newline after a directive doesn't count as * whitespace. Likewise, keep skipping until we find a line that * contains something other than whitespace. */ for (;;) { CCntlenStrPtrSaver oldpos; /* save the current position */ p_.save_position(&oldpos); /* skip whitespace */ while (p_.getlen() != 0) { /* if it's a newline, stop looking */ if (p_.curchar() == '\n') break; /* if this is a space, skip it and keep going */ if (is_space(p_.curchar())) p_.inc(); else break; } /* * if we're at a newline, eat it and keep going, skipping any * runs of whitespace and newlines that follow; otherwise, go * back to where we were and resume parsing */ if (p_.getlen() == 0 || p_.curchar() != '\n') { /* restore the old position and resume parsing */ p_.restore_position(&oldpos); break; } /* skip the newline and keep going */ p_.inc(); } } /* * Check that a directive is lexically complete. If it's not, we'll just * throw it in our pending buffer and return false, so that we can wait for * more text to show up before we attempt to parse the directive. If it's * complete, we'll return true with the parsing position unchanged. */ int CHtmlParser::check_directive_complete() { CCntlenStrPtr p(p_); /* * If we have at least 4k of text remaining in the incoming stream, * then assume it's complete. Even if it's not actually complete, this * will set a limit on lexically incomplete tags, to avoid waiting * forever if someone forgets a close bracket. */ if (p.getlen() > 4096) return TRUE; /* skip the '<' */ p.inc(); /* check for a close tag */ if (p.nextchar() == '/' || p.nextchar() == '\\') { /* close tag - make sure we have a '>' following */ while (p.getlen() != 0 && p.curchar() != '>') p.inc(); /* if we found the '>', we're good - tell the caller it's complete */ if (p.getlen() != 0 && p.curchar() == '>') return TRUE; } else { textchar_t qu; /* scan for the close bracket, noting quoted sequences */ for (qu = '\0' ; p.getlen() != 0 ; p.inc()) { /* * if we're in a quoted string, check for the end of the quoted * string; otherwise, check for an open quote or close bracket */ if (qu != '\0') { /* in quoted text - check for the matching quote */ if (p.curchar() == qu) { /* * it's the close quote - note we're no longer in * quoted text */ qu = '\0'; } } else { /* check for lexically significant characters */ switch(p.curchar()) { case '>': /* * close bracket - this is what we're looking for, so * we can simply return success */ return TRUE; case '"': case '\'': /* it's an open quote - note we're in quoted text */ qu = p.curchar(); break; } } } } /* * If we got this far, it means we ran out of source text without * finding the end of the directive. Move the entire remaining string * to the pending buffer, so that we can try parsing it again when we * get more text. */ pending_.append(p_.gettext(), p_.getlen()); /* * we've made a copy of the source in the pending buffer, so we're * finished for now with the incoming source - skip it all */ p_.inc(p_.getlen()); /* tell the caller that the tag is not complete */ return FALSE; } /* * Check that an entity (a '&' sequence) is complete. If it's not, we'll * move it to our pending buffer and return false, so that we can wait for * more text to show up before we attempt to parse the directive. If it's * complete, we'll return true with the parsing position unchanged. */ int CHtmlParser::check_entity_complete() { CCntlenStrPtr p(p_); /* * Cap entities at 16 characters. This is bigger than any entity, so * if we reach this limit, something is ill-formed. The cap ensures * that we don't go on forever waiting for an ill-formed sequence to * end. */ if (p.getlen() >= 16) return TRUE; /* skip the '&' */ p.inc(); if (p.getlen() == 0) goto incomplete; /* check for a '#' */ if (p.curchar() == '#') { /* it's a numeric entity - skip the '#' */ p.inc(); if (p.getlen() == 0) goto incomplete; /* check for hex or decimal */ if (p.curchar() == 'x') { /* it's hex - skip the 'x' */ p.inc(); if (p.getlen() == 0) goto incomplete; /* skip any hex digits */ while (p.getlen() != 0 && is_hex_digit(p.curchar())) p.inc(); } else { /* it's decimal - skip any decimal digits */ while (p.getlen() != 0 && is_digit(p.curchar())) p.inc(); } /* if we have anything after the digits, the entity is complete */ if (p.getlen() != 0) return TRUE; } else if (is_alpha(p.curchar())) { /* * It's a named entity. The name is a sequence of alphanumerics, * optionally terminated with a semicolon. Skip alphanumerics. */ while (p.getlen() != 0 && (is_alpha(p.curchar()) || is_digit(p.curchar()))) p.inc(); /* if we have anything left after the name, the entity is complete */ if (p.getlen() != 0) return TRUE; } else { /* it's not a valid entity, so the '&' is complete all by itself */ return TRUE; } incomplete: /* * If we got here, it means we ran out of source text without finding * the end of the entity. Move the entire remaining string to the * pending buffer, so that we can try parsing it again when we get more * text. */ pending_.append(p_.gettext(), p_.getlen()); /* * we've made a copy of the source in the pending buffer, so we're * finished for now with the incoming source - skip it all */ p_.inc(p_.getlen()); /* tell the caller that the tag is not complete */ return FALSE; } /* * Parse the directive we're looking at. We'll advance past the * closing '>' of the directive. */ void CHtmlParser::parse_directive() { const int dir_buf_siz = 128; textchar_t directive[dir_buf_siz]; textchar_t *dir_end; int end_tag; /* * if we're not translating markups, parse it literally unless it's * the end tag for the section we're in */ if (!obey_markups_) { int parse_as_text; /* presume we're going to parse it as text */ parse_as_text = TRUE; /* check for end tag if we are obeying end markups */ if (obey_end_markups_ && (p_.nextchar() == '/' || p_.nextchar() == '\\')) { CCntlenStrPtrSaver oldpos; /* save the current position */ p_.save_position(&oldpos); /* skip the '</' sequence and read the directive */ p_.inc(2); dir_end = scan_ident(directive, dir_buf_siz); /* * restore the old position regardless of what happens - if * we're going to treat it literally, we want to go back to * the start of the tag, and if we're going to parse it, * we're going to go through the full normal parsing, so * we'll still want to be back at the beginning of the tag */ p_.restore_position(&oldpos); /* * see if it's our end tag - if so, we'll want to parse * this as an ordinary tag after all */ if (end_tag_matches(directive, dir_end - directive, FALSE, FALSE)) parse_as_text = FALSE; } /* see if we're going to parse the tag as text */ if (parse_as_text) { /* treat it as a literal */ parse_text(); /* we're done with it */ return; } } /* * check for lexical completeness - if it's not complete, just throw it * in the pending buffer and consider it done */ if (!check_directive_complete()) return; /* skip the opening '<' */ p_.inc(); /* check for comments */ if (p_.getlen() != 0 && p_.curchar() == '!') { /* skip the '!' */ p_.inc(); /* process up to the closing '>' */ for (;;) { /* if we're out of text, stop scanning */ if (p_.getlen() == 0) return; /* if we're at the closing '>', skip it and we're done */ if (p_.curchar() == '>') { /* skip the closing '>' */ p_.inc(); /* skip following whitespace */ skip_posttag_whitespace(); /* done */ return; } /* see what we have */ if (p_.curchar() == '-' && p_.nextchar() == '-') { oshtml_charset_id_t cs = frame_->get_default_win_charset(); /* it's a comment - skip up to the next '--' sequence */ p_.inc(2); while (p_.getlen() != 0 && p_.curchar() != '>' && (p_.curchar() != '-' || p_.nextchar() != '-')) p_.inc_char(cs); /* skip any additional consecutive hyphens */ while (p_.getlen() != 0 && p_.curchar() == '-') p_.inc(); } else { /* we have a comment directive */ // $$$ TBD: process comment directives properly // for now, we'll just ignore them p_.inc(); } /* skip any trailing whitespace */ while (p_.getlen() != 0 && is_space(p_.curchar())) p_.inc(); } } /* check for an end tag */ if (p_.getlen() != 0 && (p_.curchar() == '/' || p_.curchar() == '\\')) { /* note that we're looking at an end tag and skip the slash */ end_tag = TRUE; p_.inc(); } else { /* not an end tag */ end_tag = FALSE; } /* scan the directive */ dir_end = scan_ident(directive, dir_buf_siz); /* null-terminate the directive */ *dir_end = '\0'; /* * If we're at an end-tag, there can't be any attributes, so skip * up to the closing '>' */ if (end_tag) { size_t end_tag_len; CHtmlHashEntryTag *entry; /* skip whitespace */ while (p_.getlen() != 0 && is_space(p_.curchar())) p_.inc(); /* * if we're not at a closing '>', it's an error - log it, and * skip up to the closing '>' */ if (p_.getlen() == 0 || p_.curchar() != '>') { log_error("closing '>' of end-tag </%s> not found", directive); while (p_.getlen() != 0 && p_.curchar() != '>') p_.inc(); } /* figure the length of the tag name */ end_tag_len = dir_end - directive; /* find the tag */ entry = (CHtmlHashEntryTag *)tag_table_->find(directive, end_tag_len); if (entry != 0) { /* process the end tag */ entry->process_end_tag(this, directive, end_tag_len); } else { /* * We don't recognize this tag, so don't close anything -- just * ignore it, as we probably did the opening tag. Note that * this could be an accidental mismatch with the opening tag, * in which case we'll leave the container open incorrectly, * but this behavior is generally desirable because it causes * unrecognized <tag>...</tag> sequences to be ignored * silently. */ log_error("unrecognized end tag - </%s> - ignored", directive); } } else { CHtmlHashEntryTag *entry; CHtmlTagContainer *cont; CHtmlTag *new_tag; /* * start or non-container tag - find the directive in the table * of known directives */ entry = (CHtmlHashEntryTag *)tag_table_-> find(directive, dir_end - directive); /* if we found it, create a new tag; otherwise, ignore it entirely */ if (entry) { /* create a tag object */ new_tag = entry->create_new_tag(this); } else { /* log the error, and otherwise ignore the entire markup */ log_error("unknown tag <%s>", directive); /* skip up to the closing '>' */ while (p_.getlen() != 0 && p_.curchar() != '>') p_.inc(); /* ...and skip the closing '>' if we found it */ if (p_.getlen() != 0 && p_.curchar() == '>') p_.inc(); /* done */ return; } /* * check with each enclosing container to make sure that this * tag is allowed */ for (cont = container_ ; cont ; cont = cont->get_container()) { if (!cont->allow_tag(new_tag)) { log_error("tag <%s> not allowed by container", directive); delete new_tag; return; } } /* * Parse attributes, if any are present. For each attribute, * we'll scan its name and value, and call the tag to let it * know about the new attribute. */ for (;;) { const int attrib_buf_siz = 128; textchar_t attrib[attrib_buf_siz]; textchar_t *attrib_end; HTML_attrerr err; CHtmlHashEntryAttr *attr_entry; /* skip whitespace */ while (p_.getlen() && is_space(p_.curchar())) p_.inc(); /* if we're out of text, there aren't any more attributes */ if (p_.getlen() == 0) break; /* if we're out of text or at the closing '>', we're done */ if (p_.getlen() == 0 || p_.curchar() == '>') break; /* make sure we have an attribute */ if (!is_alpha(p_.curchar())) { /* log it */ log_error("invalid attribute name - must start with a letter"); /* skip to next whitespace character */ while (p_.getlen() && !is_space(p_.curchar()) && p_.curchar() != '>') p_.inc(); /* move on to the next attribute */ continue; } /* scan the attribute name */ attrib_end = scan_ident(attrib, attrib_buf_siz); /* * Look up the attribute name in the hash table. If we * find it, we can use its ID to refer to the attribute; if * we don't find it, it's an error. */ attr_entry = (CHtmlHashEntryAttr *)attr_table_-> find(attrib, attrib_end - attrib); if (attr_entry == 0) { /* log the error, but go ahead and parse the value part */ log_error("attribute \"%s\" not valid in tag <%s>", attrib, directive); } /* * if we're at a '=', we have a value, otherwise we have * just a flag */ curattr_.clear(); if (p_.getlen() && p_.curchar() == '=') { textchar_t quote; /* skip the '=' */ p_.inc(); /* see if we have an open quote */ if (p_.getlen() && (p_.curchar() == '"' || p_.curchar() == '\'')) { /* remember the quote */ quote = p_.curchar(); /* skip it */ p_.inc(); } else { /* no quote */ quote = 0; } /* * read characters until we find the closing quote, if * we had an open quote, or a space or '>' */ for ( ; p_.getlen() ; ) { textchar_t c; textchar_t buf[50]; size_t len; /* get the next literal character of input */ c = p_.curchar(); /* see if we're done */ if (quote != 0) { /* quoted - done if we're at the closing quote */ if (c == quote) { /* skip the quote in the input stream */ p_.inc(); /* we're done */ break; } } else { /* not quoted - done if we're at a space or '>' */ if (c == '>' || is_space(c)) break; } /* parse the character, which can contain '&' markups */ len = parse_char(buf, sizeof(buf), 0, 0, 0); /* not done - add this character to the value buffer */ curattr_.append(buf, len); } } else { /* set the attribute value to the name of the attribute */ curattr_.append(attrib, attrib_end - attrib); } /* if the attribute name was invalid, skip this attribute */ if (attr_entry == 0) continue; /* Set this attribute in the tag */ err = new_tag->set_attribute(this, attr_entry->id_, curattr_.getbuf(), curattr_.getlen()); switch(err) { case HTML_attrerr_ok: /* no problem */ break; case HTML_attrerr_inv_attr: /* invalid attribute for this tag */ log_error("attribute \"%s\" not valid for tag <%s>", attrib, directive); break; case HTML_attrerr_inv_type: /* invalid type for attribute value */ log_error("invalid value type for attribute \"%s\" in " "tag <%s>", attrib, directive); break; case HTML_attrerr_inv_enum: /* value doesn't match allowed choices */ log_error("value for attribute \"%s\" in tag <%s> doesn't " "match any of the allowed values", attrib, directive); break; case HTML_attrerr_out_of_range: /* value is out of range */ log_error("value for attribute \"%s\" in tag <%s> " "is out of range", attrib, directive); break; case HTML_attrerr_too_many_coords: log_error("too many coordinates given in POLY COORDS"); break; case HTML_attrerr_odd_coords: log_error("even number of coordinates required in POLY"); break; case HTML_attrerr_missing_delim: log_error("missing delimiter in value of attribute \"%s\" " "in tag <%s>", attrib, directive); break; } } /* * The tag looks good. Add the current block of text to its * container as a new text tag, then inform the new tag that * it's been parsed. */ add_text_tag(); new_tag->on_parse(this); } /* * if we're at the closing '>', skip it; otherwise, we have an * incomplete tag at the end of the text, which is an error */ if (p_.curchar() == '>') { /* well-formed tag - skip the closing '>' */ p_.inc(); /* skip whitespace after the tag */ skip_posttag_whitespace(); } else { /* incomplete tag at the end of the document - ignore it entirely */ log_error("incomplete tag at end of document"); } } /* * Process a normal end tag. */ void CHtmlParser::end_normal_tag(const textchar_t *tag, size_t len) { /* * Find the matching container and call its pre-close method. This * allows close tags that implicitly close other tags to do the * implicit closing before we decide whether we're at the matching * level. */ pre_close_tag(tag, len); /* * Make sure it matches the most recent start tag on the container * stack. If so, pop the start tag; otherwise, it's an error. */ end_tag_matches(tag, len, TRUE, TRUE); /* * If we only have one container in the stack, it's the special outer * container, which we can't pop - they must have specified more end * tags than start tags. Log it as an error and ignore the end tag. */ if (get_container_depth() == 1) { /* log the error and proceed */ log_error("too many end tags - </%s> ignored", tag); } else { /* close the current tag */ close_current_tag(); } } /* * Process a closing </P> tag. We don't treat <P> as a true container, so * we will never have an outstanding container for a </P> tag to match. * So, whenever we see </P>, simply treat it as a paragraph break and * otherwise ignore it. * * Also treat the </BODY> tag specially. Our <BODY> tag isn't a container * as it is in standard HTML, so we don't need a matching end tag. * However, allow the end tag by simply ignoring it. */ void CHtmlParser::end_p_tag() { /* add the current text block as a new text tag */ add_text_tag(); /* end the current paragraph, and otherwise ignore the </P> */ end_paragraph(FALSE); } /* * Pre-close a tag. Find a matching container, and call its pre-close * method. This allows close tags that implicitly close other tags to * do the implicit closing before we decide whether we have a matching * tag. */ void CHtmlParser::pre_close_tag(const textchar_t *nm, size_t nmlen) { CHtmlTagContainer *tag; /* look up the container list for a matching tag */ for (tag = get_inner_container() ; tag != 0 ; tag = tag->get_container()) { /* check if this is a matching tag */ if (tag->tag_name_matches(nm, nmlen)) { /* let this tag do its close recognition work */ tag->pre_close(this); /* no need to look further */ return; } } } /* * Close a tag if it's open */ void CHtmlParser::close_tag_if_open(const textchar_t *nm) { size_t nmlen = get_strlen(nm); /* if the immediate container matches this tag, close it */ if (get_inner_container()->tag_name_matches(nm, nmlen)) { /* this is the one we want - close it */ close_current_tag(); } } /* * Close the current tag */ void CHtmlParser::close_current_tag() { CHtmlTagContainer *closing_tag; /* add the current text block as a new text tag */ add_text_tag(); /* tell the current container it's closing */ closing_tag = get_inner_container(); closing_tag->on_close(this); /* pop the innermost container */ pop_inner_container(); /* tell the container we just closed it */ closing_tag->post_close(this); } /* * Look up an attribute value in the enumerated attribute value list. * Returns an attribute ID if the value matches one of the enumerated * values. */ HTML_Attrib_id_t CHtmlParser::attrval_to_id(const textchar_t *val, size_t vallen) { CHtmlHashEntryAttr *entry; entry = (CHtmlHashEntryAttr *)attr_val_table_->find(val, vallen); return (entry ? entry->id_ : HTML_Attrib_invalid); } /* * Add the current text stream to the parse tree as a text tag */ void CHtmlParser::add_text_tag() { CHtmlTagText *text_tag; /* if there's no text, don't bother adding a new tag */ if (curtext_.getlen() == 0) return; /* make a new tag for the text */ text_tag = (obey_whitespace_ && !break_long_lines_) ? new CHtmlTagTextPre(8, text_array_, &curtext_) : new CHtmlTagText(text_array_, &curtext_); /* add it to the parse tree */ append_tag(text_tag); /* clear the text buffer */ curtext_.clear(); } /* * Parse a hard tab character */ void CHtmlParser::parse_tab() { if (obey_whitespace_) { CHtmlTagTAB *tab; /* skip the tab character in the input */ p_.inc(); /* flush the current text */ add_text_tag(); /* create a <TAB MULTIPLE=4> tag */ tab = new CHtmlTagTAB(this); tab->set_multiple_val(4); tab->on_parse(this); } else { /* not in preformatted text - treat as ordinary whitespace */ parse_whitespace(); } } /* * Parse whitespace */ void CHtmlParser::parse_whitespace() { /* * if we're obeying whitespace literally, simply add this character * to the text stream directly */ if (obey_whitespace_) { parse_text(); return; } /* skip past any additional contiguous whitespace */ while (p_.getlen() > 0 && is_space(p_.curchar())) p_.inc(); /* * If the last character of the text stream was already whitespace, * ignore this one; otherwise, add it. */ if (!eat_whitespace_) { /* add a whitespace character to the text */ append_to_text(' '); /* eat whitespace until we get something else */ eat_whitespace_ = TRUE; } } /* * Parse a newline character. For the most part, newlines are treated * as whitespace. In preformatted text, newlines count as line breaks. */ void CHtmlParser::parse_newline() { /* if obeying whitespace literally, treat this as a line break */ if (obey_whitespace_) { /* skip the newline */ p_.inc(); /* add current text block as a new text tag */ add_text_tag(); /* add a line break tag */ append_tag(new CHtmlTagBR(this)); /* * add a newline to the text stream, so that we find whitespace * between lines if we perform a search or copy/paste */ get_text_array()->append_text_noref("\n", 1); /* done */ return; } /* otherwise, treat it as any other whitespace */ parse_whitespace(); } /* * Parse text */ void CHtmlParser::parse_text() { textchar_t buf[50]; size_t len; oshtml_charset_id_t charset; int changed_charset; unsigned int special; /* * If our text buffer is getting big, flush it - this will ensure that * we won't create gigantic text display items, and that we won't * overflow the 16-bit length limit of an individual text display * item. */ if (curtext_.getlen() > 30000) add_text_tag(); /* get the current character */ len = parse_char(buf, sizeof(buf), &charset, &changed_charset, &special); /* if it's a special unicode character, handle it specially */ if (special != 0) { size_t len; int spwid; /* * Most of the special space characters subsume any ordinary * whitespace that precedes them. If we have a trailing space in * our pending text buffer, and we have one of these special space * characters, remove the trailing space. */ if ((len = curtext_.getlen()) != 0 && curtext_.getbuf()[len - 1] == ' ') { /* check for a special space character */ switch(special) { case 0x0015: /* our own quoted space */ case 0x2002: /* en space */ case 0x2003: /* em space */ case 0x2004: /* three-per-em space */ case 0x2005: /* four-per-em space */ case 0x2006: /* six-per-em space */ case 0x2007: /* figure space */ case 0x2008: /* punctuation space */ case 0x2009: /* thin space */ case 0x200A: /* hair space */ /* * These all subsume adjacent space - remove the trailing * space from the pending text. Note that we can assume * that we will have at most one trailing space, since we * always collapse runs of whitespace as we build the * pending text buffer in the first place. */ curtext_.setlen(len - 1); break; } } /* * Whatever we have next will require a separate element to * represent, so send the pending text to the tag list as a * text-run element. */ add_text_tag(); /* * presume that this special character doesn't subsume any * immediately following whitespace */ eat_whitespace_ = FALSE; /* now check the special character and add the appropriate tag */ switch(special) { case 0x00AD: /* soft hyphen */ /* add a soft-hyphen tag */ append_tag(new CHtmlTagSoftHyphen(text_array_)); break; case 0x00A0: /* non-breaking space */ /* add a non-breaking space tag with a width of one space */ append_tag(new CHtmlTagNBSP(text_array_, " ")); break; case 0xFEFF: /* zero-width non-breaking space */ /* add a non-breaking space tag with a zero width */ append_tag(new CHtmlTagNBSP(text_array_, "")); break; case 0x200B: /* zero-width space */ /* add a zero-width space */ spwid = 0; goto add_space_no_eat; case 0x0015: /* our own quoted space */ /* add a space with a width of one ordinary space */ spwid = HTMLTAG_SPWID_ORDINARY; goto add_space; case 0x2002: /* en space */ /* * an en space is 1/2 em (we represent fractions of an em by * using the denominator of the fractional em size: 2 * indicates 1/2 em, 3 is 1/3 em, and so on) */ spwid = 2; goto add_space; case 0x2003: /* em space */ /* use a full em space */ spwid = 1; goto add_space; case 0x2004: /* three-per-em space */ /* use a 1/3 em space */ spwid = 3; goto add_space; case 0x2005: /* four-per-em space */ /* use a 1/4 em space */ spwid = 4; goto add_space; case 0x2006: /* six-per-em space */ /* use a 1/6 em space */ spwid = 6; goto add_space; case 0x2007: /* figure space */ /* use the special figure space indicator */ spwid = HTMLTAG_SPWID_FIGURE; goto add_space; case 0x2008: /* punctuation space */ /* use the special punctuation space indicator */ spwid = HTMLTAG_SPWID_PUNCT; goto add_space; case 0x2009: /* thin space */ /* use a 1/5 em space */ spwid = 5; goto add_space; case 0x200A: /* hair space */ /* use a 1/8 em space */ spwid = 8; goto add_space; add_space: /* * consolidate any subsequent whitespace with this space - most * of the typographical spaces (other than the zero-width * spaces) subsume adjacent ordinary whitespace */ eat_whitespace_ = TRUE; add_space_no_eat: /* add the space tag */ append_tag(new CHtmlTagSpace( text_array_, spwid, obey_whitespace_ && !break_long_lines_)); /* done */ break; } } else { /* * if necessary, generate a FONT tag to switch to the correct * character set - we only need to do this if the character set of * the character is different than the default frame character set */ if (changed_charset) { /* flush the text output */ add_text_tag(); /* generate a character set tag for the switch */ push_container(new CHtmlTagCharset(charset)); } /* add this character to our current text stream */ curtext_.append(buf, len); /* switch back to our old character set if necessary */ if (changed_charset) { /* flush the text output */ add_text_tag(); /* close the character set tag */ close_current_tag(); } /* * if this isn't a whitespace character, we'll want to include the * next whitespace character we see into the text */ if (len != 0 && !is_space(buf[len-1])) eat_whitespace_ = FALSE; } } /* * Add text to the current buffer */ void CHtmlParser::append_to_text(textchar_t c) { /* flush the text buffer if it's getting big */ if (curtext_.getlen() > 30000) add_text_tag(); /* add the text */ curtext_.append(&c, 1); } /* * Break the current paragraph */ void CHtmlParser::end_paragraph(int isexplicit) { /* add the current text to the output stream */ add_text_tag(); /* add a plain paragraph tag */ append_tag(new CHtmlTagP(isexplicit, this)); } /* * Process a piece of text, appending it to the end of the HTML stream * processed so far. */ void CHtmlParser::parse(const textchar_t *src, size_t len, CHtmlSysWinGroup *frame) { CHtmlTextBuffer new_src; /* * If we have pending buffered text, add this text to the pending * buffer, and parse from the pending buffer. */ if (pending_.getlen() != 0) { /* make a working copy of the pending source */ new_src.append(pending_.getbuf(), pending_.getlen()); /* add the new text to the working copy */ new_src.append(src, len); /* * the pending text is now our working text, so it's no longer * pending: clear the pending buffer */ pending_.clear(); /* parse from the new working buffer */ src = new_src.getbuf(); len = new_src.getlen(); } /* start at the beginning of the buffer */ p_.set(src, len); p_start_.set(src, len); /* remember the system frame (for use in lower-level routines) */ frame_ = frame; /* keep going until we run out of text in the buffer */ for (;;) { /* if we're out of text, we're done */ if (p_.getlen() == 0) break; /* see what we have */ switch(p_.curchar()) { case '<': /* start of a directive */ parse_directive(); break; case ' ': /* regular whitespace */ parse_whitespace(); break; case '\t': /* hard tab character */ parse_tab(); break; case '\n': case '\r': parse_newline(); break; default: /* anything else is to be added literally */ parse_text(); break; } } /* flush any text that we've gathered up */ add_text_tag(); } /* * Push a container tag */ void CHtmlParser::push_container(CHtmlTagContainer *tag) { /* add this object to the current container's list */ container_->append_to_sublist(tag); /* this is the new innermost container */ container_ = tag; /* increase the depth counter */ ++container_depth_; } /* * Pop the innermost container off the stack */ void CHtmlParser::pop_inner_container() { /* * if there's no container, or the container doesn't have a * container, we can't pop anything -- we can never pop the special * outermost container */ if (container_ == 0 || container_->get_container() == 0) return; /* make the current object's container the new innermost container */ container_ = container_->get_container(); /* count the change in depth */ --container_depth_; } /* * Fix up a trailing container close */ void CHtmlParser::fix_trailing_cont() { /* * If the last tag we added is a container, add a "relax" tag, so that * we have a non-container to land on after traversing out of the * container. */ if (get_inner_container() != 0 && get_inner_container()->get_last_contents() != 0 && get_inner_container()->get_last_contents()->is_container_tag()) { /* we need a "relax" tag - create a new one and append it */ get_inner_container()->append_to_sublist(new CHtmlTagRelax()); } } /* * Append an item to the current innermost container's sublist */ void CHtmlParser::append_tag(CHtmlTag *tag) { get_inner_container()->append_to_sublist(tag); } /* * Append a new input tag */ CHtmlTagTextInput *CHtmlParser::append_input_tag(const textchar_t *buf, size_t len) { CHtmlTagTextInput *new_tag; /* create the input tag */ new_tag = new CHtmlTagTextInput(text_array_, buf, len); /* add it to our list */ get_inner_container()->append_to_sublist(new_tag); /* return the new tag */ return new_tag; } /* * Find the outermost container */ CHtmlTagContainer *CHtmlParser::get_outer_container() const { CHtmlTagContainer *cont; /* * scan outward from the inner container until we find the * outermost one */ for (cont = container_ ; cont->get_container() != 0 ; cont = cont->get_container()) ; /* return what we found */ return cont; } /* * Export the parse tree */ void CHtmlParser::export_parse_tree(CHtmlParserState *state) { /* save my state in the state object */ state->text_array_ = text_array_; state->container_ = container_; state->outer_container_ = get_outer_container(); state->container_depth_ = container_depth_; /* forget my state information */ text_array_ = 0; container_ = 0; /* create a new text array */ text_array_ = new CHtmlTextArray; /* create a new outermost container */ container_ = new CHtmlTagOuter; container_depth_ = 1; /* reset internal variables to start a new page */ clear_page(); } /* * Import a parse tree */ void CHtmlParser::import_parse_tree(CHtmlParserState *state) { /* discard my current tags */ clear_page(); if (container_ != 0) delete container_; /* discard my current text array */ if (text_array_ != 0) delete text_array_; /* load the information from the state structure */ text_array_ = state->text_array_; container_ = state->container_; container_depth_ = state->container_depth_; /* clear out the state structure - we own the state now */ state->text_array_ = 0; state->container_ = 0; state->outer_container_ = 0; } /* * Destroy a parse tree. */ void CHtmlParser::destroy_parse_tree(CHtmlParserState *state) { /* delete the text array */ if (state->text_array_ != 0) delete state->text_array_; /* delete the tags */ if (state->outer_container_ != 0) { /* delete the top container's children */ state->outer_container_->delete_contents(); /* delete the top container */ delete state->outer_container_; } /* clear the state structure */ state->text_array_ = 0; state->outer_container_ = 0; state->container_ = 0; } /* * Prune the parse tree by deleting the given percentage of top-level * nodes. */ void CHtmlParser::prune_tree(unsigned long max_text_array_size) { CHtmlTag *cur; CHtmlTag *prv; CHtmlTag *nxt; long top_cnt; /* * Count the top-level tags. Always try to keep around at least a * few of them, so that we don't leave the window entirely blank. */ for (cur = get_outer_container()->get_contents(), top_cnt = 0 ; cur != 0 ; cur = cur->get_next_tag(), ++top_cnt) ; /* * Run through the list and delete each top-level node that we can * safely delete. Keep deleting until we run out of things to * delete, or the text array is no larger than the given size. */ for (cur = get_outer_container()->get_contents(), prv = 0 ; cur != 0 ; cur = nxt) { int can_delete; int can_prune_contents; /* presume we can delete this tag and prune its contents */ can_delete = can_prune_contents = TRUE; /* * check to see if we've pruned enough space yet - if so, don't * bother deleting this one or pruning its contents */ if (get_text_array()->get_mem_in_use() <= max_text_array_size) can_delete = can_prune_contents = FALSE; /* stop if we're running too low on top-level tags */ if (top_cnt < 125) can_delete = can_prune_contents = FALSE; /* remember the next node in line, in case we delete this one */ nxt = cur->get_next_tag(); /* * if this is our last tag, always keep it and its contents, so * that we have something still around after we're done */ if (nxt == 0) can_delete = can_prune_contents = FALSE; /* * If we still think we can delete the tag, check with the tag * itself to see if it can be deleted. If it can't be deleted, * so note; but we might still allow pruning its contents. */ if (can_delete && !cur->can_prune_tag()) can_delete = FALSE; /* * If we can delete this one, do so. Otherwise, go through its * contents and try pruning the contents, if desired. */ if (can_delete) { /* adjust the previous link */ if (prv != 0) prv->set_next_tag(nxt); else get_outer_container()->set_contents(nxt); /* notify the tag of the pruning */ cur->prune_pre_delete(get_text_array()); /* delete this tag */ delete cur; /* that's one less top-level tag remaining */ --top_cnt; } else { /* we can't delete this one, so it'll be the next 'prv' */ prv = cur; /* tell the tag to prune its contents, if necessary */ if (can_prune_contents) cur->try_prune_contents(get_text_array()); } } } /* * Log an error */ void CHtmlParser::log_error(const textchar_t *msg, ...) { va_list argptr; size_t curofs; size_t startofs; CCntlenStrPtr ctxp; textchar_t buf[80]; textchar_t *dst; /* * get some context - go back from the current point by 50 * characters if possible, and display from that point to the * current point plus 30 characters */ curofs = p_start_.getlen() - p_.getlen(); startofs = (curofs > 50 ? curofs - 50 : 0); ctxp.set(&p_start_); ctxp.inc(startofs); for (dst = buf ; dst < buf + sizeof(buf) - 1 && ctxp.getlen() > 0 ; ctxp.inc()) *dst++ = ctxp.curchar(); *dst = '\0'; /* display the context */ oshtml_dbg_printf("==============================\n%s\n", buf); /* display a pointer to the current position */ for (dst = buf ; dst < buf + sizeof(buf) - 1 && startofs < curofs ; *dst++ = ' ', ++startofs) ; *dst = '\0'; oshtml_dbg_printf("%s^\n", buf); /* display the error message */ va_start(argptr, msg); oshtml_dbg_printf("Error: "); oshtml_dbg_vprintf(msg, argptr); oshtml_dbg_printf("\n"); va_end(argptr); } #ifdef TADSHTML_DEBUG /* * dump tree to stdout for debugging purposes */ void CHtmlParser::debug_dump() { /* dump the outermost container */ get_outer_container()->debug_dump(0, text_array_); } #endif #if 0 /* ------------------------------------------------------------------------ */ /* * TEST SECTION */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include "htmlfmt.h" #include "htmlsys_w32.h" int main(int argc, char **argv) { FILE *fp; CHtmlTextBuffer *buf; CHtmlParser *parser; CHtmlFormatter *formatter; CHtmlSysWin *win; /* ensure they specified a file */ if (argc < 2) { printf("usage: %s filename\n", argv[0]); exit(1); } /* open the file */ fp = fopen(argv[1], "r"); if (fp == 0) { printf("can't open input file\n"); exit(2); } /* create a text buffer */ buf = new CHtmlTextBuffer; /* read the file and load it into our text buffer */ for (;;) { char lin[256]; /* read the next line */ if (!fgets(lin, sizeof(lin), fp)) break; /* load the line into the text buffer */ buf->append(lin, strlen(lin)); } /* run it through the parser */ parser = new CHtmlParser; parser->parse(buf, 0); /* dump the parse tree */ parser->debug_dump(); /* run it through the formatter */ win = new CHtmlSysWin_win32(); formatter = new CHtmlFormatter(parser, win); while (formatter->more_to_do()) formatter->do_formatting(); formatter->start_new_line(0); /* done with the parser */ delete parser; /* done with the formatter */ delete formatter; /* done with the buffer */ delete buf; return 0; } #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmlprs.h����������������������������������������������������������������������0000664�0000000�0000000�00000037632�12650170723�0016335�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/html/htmlprs.h,v 1.2 1999/05/17 02:52:22 MJRoberts Exp $ */ /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlprs.h - HTML parser Function Notes Modified 08/26/97 MJRoberts - Creation */ #ifndef HTMLPRS_H #define HTMLPRS_H #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef HTMLATTR_H #include "htmlattr.h" #endif #ifndef HTML_OS_H #include "html_os.h" #endif /* ------------------------------------------------------------------------ */ /* * HTML parser. The client first writes HTML source code to a * CHtmlTextBuffer object, then submits the buffer object to the parser * to turn into a parsed tag list. The parser can then in turn be * submitted to a renderer for display. */ class CHtmlParser { friend class CHtmlParserState; public: /* set up the parser */ CHtmlParser() { init(FALSE); } /* * Set up the parser, optionally initializing in "literal" mode -- * if 'literal' is true, we won't interpret HTML markups, and we'll * treat whitespace and newlines as significant. */ CHtmlParser(int literal_mode) { init(literal_mode); } /* delete the parser */ ~CHtmlParser(); /* process a buffer containing HTML source */ void parse(const textchar_t *txt, size_t len, class CHtmlSysWinGroup *frame); void parse(const CHtmlTextBuffer *src, class CHtmlSysWinGroup *frame) { parse(src->getbuf(), src->getlen(), frame); } /* * clear the page, deleting all tags in all lists and sublists * (apart from the special outermost container, which we keep as * long as the parser itself is around) */ void clear_page(); /* * Add a new input tag. The input tag is special in that allows * editing of the contents of the contents in the parse tree; this * tag provides for input buffer editing in the user interface. */ class CHtmlTagTextInput *append_input_tag(const textchar_t *buf, size_t len); /* add a tag to the current innermost container's tag list */ void append_tag(class CHtmlTag *tag); /* close a tag if it's open */ void close_tag_if_open(const textchar_t *nm); /* close the current tag */ void close_current_tag(); /* * pre-close the current tag - call upon recognizing a close tag, * before we've actually closed anything */ void pre_close_tag(const textchar_t *nm, size_t nmlen); /* parse the directive we're looking at */ void parse_directive(); /* * Begin skipping whitespace. A tag's on_parse() method can call * this whenever it wants to skip all whitespace separating the tag * from the next non-blank text. The P and BR tags use this to * ensure that there isn't any stray whitespace at the start of the * first line following the tag. */ void begin_skip_sp() { eat_whitespace_ = TRUE; } /* * Stop skipping whitespace. A tag's on_parse() or on_close() method * can call this if the tag inserts text into the stream (such as <Q>). * Most tags don't do any such thing, but the rare ones that do should * always call this to ensure that adjacent whitespace is considered * significant. */ void end_skip_sp() { eat_whitespace_ = FALSE; } /* * Turn whitespace obedience on (flag=true) or off (flag=false). * Whitespace is obeyed only within the special preformatted text * containers. If break_long_lines is true, we'll continue to allow * breaking long lines within the block; generally, when whitespace * is obeyed, implicit line breaks are not allowed. Note that the * break_long_lines setting is ignored when not obeying whitespace. */ void obey_whitespace(int flag, int break_long_lines); int get_obey_whitespace() const { return obey_whitespace_; } int get_break_long_lines() const { return break_long_lines_; } /* * Turn markup translation on (flag=true) or off(flag=false). * Markups are translated except within the special listing-style * preformatted text containers. */ void obey_markups(int flag) { obey_markups_ = flag; } int get_obey_markups() const { return obey_markups_; } /* * If in obey_markups(FALSE) mode, you can set this additional flag * to determine whether or not an *end* markup of the current type * will be obeyed. Normally, end markups of markups that start a * verbatim mode (such as </PRE>) should be obeyed. However, if the * caller wants markups ignored for some reason other than an * opening markup, it can set obey_end_markups(FALSE) mode, in which * case we'll never obey any markup of any kind. */ void obey_end_markups(int flag) { obey_end_markups_ = flag; } int get_obey_end_markups() const { return obey_end_markups_; } /* push a container onto the container stack */ void push_container(class CHtmlTagContainer *tag); /* pop the innermost container */ void pop_inner_container(); /* * Fix up a trailing container. If the last tag we formatted was a * container end tag, this will add a "relax" tag at the end of the * current open container's sublist so that we have a non-container to * land on at the end of the formatting cycle. This is important when * we're traversing the list for formatting, because this helps us * ensure we don't repeatedly call format_exit on a closing tag by * ensuring we always have a place to go after traversing out of a * container. */ void fix_trailing_cont(); /* get the innermost container on the container stack */ class CHtmlTagContainer *get_inner_container() const { return container_; } /* get the outermost container */ class CHtmlTagContainer *get_outer_container() const; /* get the depth of the container stack */ int get_container_depth() const { return container_depth_; } /* * End the current paragraph. If explicit is true, it means that * this is a real paragraph break, so paragraph spacing should be * inserted. Otherwise, it means that the paragraph was ended * implicitly, so we shouldn't add paragraph spacing. */ void end_paragraph(int isexplicit); HTML_IF_DEBUG(void debug_dump();) /* * Log an error. This generally does nothing, but the user * interface may provide a mechanism that allows the user to see the * errors produced when parsing a document. */ void log_error(const textchar_t *errmsg, ...); /* * Look up an attribute value in the enumerated attribute value * list. Returns an attribute ID if the value matches one of the * enumerated values. */ HTML_Attrib_id_t attrval_to_id(const textchar_t *val, size_t vallen); /* get the text array */ class CHtmlTextArray *get_text_array() const { return text_array_; } /* * Export a parse tree. This should only be used after the source * has been completely parsed. After exporting the parse tree, the * parser forgets all information about the parse tree -- the parser * no longer references the parse tree once it has been exported. */ void export_parse_tree(class CHtmlParserState *state); /* * Import a parse tree. This restores a parse tree saved with * save_parse_tree(). Any existing parse tree is destroyed. */ void import_parse_tree(class CHtmlParserState *state); /* * Get the system window frame object - this is valid during * parsing, so tags can use it if necessary (the main reason a tag * would need this object is to translate an HTML entity value to a * character value). */ class CHtmlSysWinGroup *get_sys_frame() const { return frame_; } /* * Prune the parse tree. Attempts to reduce the memory allocated to * the text array to the given size; we'll delete top-level nodes in * the parse tree, starting with the oldest nodes, until we run out * of nodes that can be deleted or the text array size is no larger * than the given size. * * Note that the actual amount of memory in use after this call will * be greater than the given size, since the parse nodes themselves * take up space. In a typical document, where most of the * information in the document is text, the text array size will * dominate; documents that contain extensive mark-up information * will naturally need more space for parse nodes for a given amount * of text. */ void prune_tree(unsigned long max_text_array_size); /* process a closing tag for most kinds of tags */ void end_normal_tag(const textchar_t *tag, size_t len); /* process a closing </P> tag */ void end_p_tag(); private: /* internal initialization */ void init(int literal_mode); /* destroy an externalized parse tree */ static void destroy_parse_tree(class CHtmlParserState *state); /* check that we have a lexically complete tag to parse */ int check_directive_complete(); /* check that we have a lexically complete entity to parse */ int check_entity_complete(); /* determine if a given string matches the start tag name */ int end_tag_matches(const textchar_t *end_tag_name, size_t end_tag_len, int log, int find); /* * Parse and return a single character, turning an '&' sequence into * the corresponding single character. Increments the pointer to * point to the next character. Fills in the result buffer with the * result of the translation. Returns the length (excluding null * termination) of the result. * * If charset is not null, it indicates that we're in a context where * we can change character sets; in this case, we'll fill in *charset * with the character set to use for this character. If charset is * null, it means that we can't change the character set, so we'll * attempt to map any '&' entities to the current character set. If * we need to change to a new character set, we'll set changed_charset * (if the pointer isn't null) to true, otherwise we'll set it to * false. * * '*special' returns with a non-zero value if the character is one of * the Unicode characters which carry a special meaning for us. In * this case, we'll still fill in the result buffer with a text-only * approximation, in case the caller doesn't care about the special * meaning. */ size_t parse_char(textchar_t *result, size_t result_buf_size, oshtml_charset_id_t *charset, int *changed_charset, unsigned int *special); /* parse a special Unicode character to see if it has a special meaning */ int special_entity(textchar_t *result, size_t result_size, size_t *outlen, unsigned int ch, unsigned int *special); /* * Parse a character entity. This is a subroutine for parse_char(), * and shouldn't be called directly by other code. */ size_t parse_char_entity(textchar_t *result, size_t result_buf_size, oshtml_charset_id_t *charset, int *changed_charset, unsigned int *special); /* parse whitespace */ void parse_whitespace(); /* * parse a hard tab character - we'll treat this as whitespace in * most cases, but in pre-formatted text we'll insert spacing to the * next tab stop */ void parse_tab(); /* parse a newline */ void parse_newline(); /* parse text */ void parse_text(); /* append a character to our text */ void append_to_text(textchar_t c); /* * make a text tag out of the current text stream and add it to the * current container */ void add_text_tag(); /* * Scan an identifier (tag name, attribute name) within a tag. * Returns a pointer to the next character after the last character * of the identifier in the buffer. */ textchar_t *scan_ident(textchar_t *buf, size_t buflen); /* skip runs of blank lines following a tag */ void skip_posttag_whitespace(); /* text array - we store the stream of text for the document here */ class CHtmlTextArray *text_array_; /* current buffer position */ CCntlenStrPtr p_; /* * Pending buffer. Whenever we find an incomplete tag in the input * stream, we'll add what we have so far to this buffer, and defer * parsing it until more data arrive. */ CHtmlTextBuffer pending_; /* * starting buffer position (we keep track of this so that we can * display some context before the current position when an error * occurs) */ CCntlenStrPtr p_start_; /* Hash table for ampersand sequence names */ class CHtmlHashTable *amp_table_; /* Hash table for the tag names */ class CHtmlHashTable *tag_table_; /* hash table for attribute names */ class CHtmlHashTable *attr_table_; /* hash table for attribute value names */ class CHtmlHashTable *attr_val_table_; /* current container */ class CHtmlTagContainer *container_; /* * Text buffer containing current output stream. The output stream * accumulates until we encounter a new tag, at which point we build * a text-container tag out of the current buffer and insert it into * the tag stream. */ CHtmlTextBuffer curtext_; /* text buffer containing an attribute value being scanned */ CHtmlTextBuffer curattr_; /* Depth of containment */ int container_depth_; /* Flag: true -> obeying whitespace literally */ int obey_whitespace_ : 1; /* Flag: if obey_whitespace_ is true, allow breaking long lines */ int break_long_lines_ : 1; /* Flag: true -> translating markups normally */ int obey_markups_ : 1; /* Flag: true -> translating end markups normally when in verbatim mode */ int obey_end_markups_ : 1; /* * Flag: eat any whitespace characters. This flag is set whenever * we start off a new paragraph or add a new whitespace character to * the text during normal formatting. */ int eat_whitespace_ : 1; /* * System application frame object - whenever we enter the parser, * we remember the frame object passed in to the parse() call here. * We need the system frame to perform certain work for us, such as * translating unicode characters to the current system character * set. */ class CHtmlSysWinGroup *frame_; }; /* ------------------------------------------------------------------------ */ /* * Parser state saver. This object can be used to save a parse tree for * later use. Note that we don't save any state involved in parsing -- * we only save a completely parsed tag tree. */ class CHtmlParserState { friend class CHtmlParser; public: CHtmlParserState() { text_array_ = 0; container_ = 0; outer_container_ = 0; container_depth_ = 0; } ~CHtmlParserState() { /* ask the parser to destroy my contents */ CHtmlParser::destroy_parse_tree(this); } /* get the text array */ class CHtmlTextArray *get_text_array() const { return text_array_; } private: /* text array */ class CHtmlTextArray *text_array_; /* current container */ class CHtmlTagContainer *container_; /* outermost container */ class CHtmlTagContainer *outer_container_; /* container nesting depth */ int container_depth_; }; #endif /* HTMLPRS_H */ ������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmlrc.cpp���������������������������������������������������������������������0000664�0000000�0000000�00000012434�12650170723�0016461�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/html/htmlrc.cpp,v 1.2 1999/05/17 02:52:22 MJRoberts Exp $"; #endif /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlrc.cpp - resource cache Function Notes Modified 10/25/97 MJRoberts - Creation */ #ifndef HTMLRC_H #include "htmlrc.h" #endif #ifndef HTMLRF_H #include "htmlrf.h" #endif #ifndef HTMLSYS_H #include "htmlsys.h" #endif #ifndef HTMLHASH_H #include "htmlhash.h" #endif #ifndef HTMLSND_H #include "htmlsnd.h" #endif /* ------------------------------------------------------------------------ */ /* * Resource cache object */ /* * Delete an resource cache object. Note that we're only deleted by the * remove_ref() method. */ CHtmlResCacheObject::~CHtmlResCacheObject() { /* if I'm in a cache, remove myself from the cache */ if (cache_ != 0) cache_->remove(this); /* we own the resource, so delete it */ if (resource_ != 0) delete resource_; } /* * Downcast routines */ /* get the resource as an image */ CHtmlSysImage *CHtmlResCacheObject::get_image() { return (resource_ != 0 ? resource_->cast_image() : 0); } /* get the resource as a sound */ CHtmlSysSound *CHtmlResCacheObject::get_sound() { return (resource_ != 0 ? resource_->cast_sound() : 0); } /* ------------------------------------------------------------------------ */ /* * Hash entry subclass for resource cache objects */ class CHtmlHashEntryRes: public CHtmlHashEntryCS { public: CHtmlHashEntryRes(CHtmlResCacheObject *obj) : CHtmlHashEntryCS(obj->get_url(), get_strlen(obj->get_url()), FALSE) { cache_obj_ = obj; } ~CHtmlHashEntryRes() { /* delete our associated cache object */ // $$$ delete cache_obj_; } /* cache object */ CHtmlResCacheObject *cache_obj_; }; /* ------------------------------------------------------------------------ */ /* * Resource cache */ CHtmlResCache::CHtmlResCache() { /* create my hash table with a case-sensitive hash function */ hashtable_ = new CHtmlHashTable(512, new CHtmlHashFuncCS); } CHtmlResCache::~CHtmlResCache() { /* delete the hash table */ delete hashtable_; } /* * Find an existing resource in the cache */ CHtmlResCacheObject *CHtmlResCache::find(const CHtmlUrl *url) { CHtmlHashEntryRes *entry; /* if the url is empty, there's no object */ if (url->get_url() == 0) return 0; /* look in the hash table for the object */ entry = (CHtmlHashEntryRes *) hashtable_->find(url->get_url(), get_strlen(url->get_url())); /* if we found a hash table entry, return the cache object */ return (entry == 0 ? 0 : entry->cache_obj_); } /* * Find an existing resource, or create a new one if there isn't one in * the cache matching the given URL. */ CHtmlResCacheObject *CHtmlResCache::find_or_create( CHtmlSysWin *win, CHtmlResFinder *res_finder, const CHtmlUrl *url) { CHtmlResCacheObject *entry; CHtmlSysResource *resource; htmlres_loader_func_t loader_func; unsigned long seekpos; unsigned long filesize; CStringBuf fname; int cacheable; /* search for an existing entry, and return it if we can find it */ if ((entry = find(url)) != 0) return entry; /* presume we're not going to be able to load the resource */ resource = 0; /* * We didn't find one - load the resource from the URL. First * determine the resource type, and see if we can find the resource * in the .GAM file. */ if (CHtmlResType::get_res_mapping(url->get_url(), &loader_func) != HTML_res_type_unknown) { /* find the resource in the .GAM file or externally */ res_finder->get_file_info(&fname, url->get_url(), get_strlen(url->get_url()), &seekpos, &filesize); /* load it */ resource = (*loader_func)(url, fname.get(), seekpos, filesize, win); } /* note if the entry is cacheable */ cacheable = (resource == 0 || resource->is_cacheable()); /* create a new cache object for the resource */ entry = new CHtmlResCacheObject(url, resource, cacheable ? this : 0); /* if the resource is cacheable, add it to our cache */ if (cacheable) add(entry); /* return the new entry */ return entry; } /* * Add an object to the cache. */ void CHtmlResCache::add(CHtmlResCacheObject *obj) { CHtmlHashEntryRes *entry; /* create an entry for the object */ entry = new CHtmlHashEntryRes(obj); /* add it to the hash table */ hashtable_->add(entry); } /* * Remove an object from the cache. */ void CHtmlResCache::remove(CHtmlResCacheObject *obj) { CHtmlHashEntry *entry; /* find the hash table entry for the object */ entry = hashtable_->find(obj->get_url(), get_strlen(obj->get_url())); /* * If we found it, remove it from the hash table, and delete the * hash entry object, since its only purpose is to fill the slot in * the hash table. */ if (entry != 0) { hashtable_->remove(entry); delete entry; } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmlrc.h�����������������������������������������������������������������������0000664�0000000�0000000�00000012424�12650170723�0016125�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/html/htmlrc.h,v 1.2 1999/05/17 02:52:22 MJRoberts Exp $ */ /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlrc.h - resource cache Function Implements a cache for resources, such as images and sounds. Because these binary resources can be large objects, it's desirable to re-use them whenever possible, rather than load a separate copy of the resource for each use. Resources are identified by URL's, and contain associated binary data loaded from external files. The details of loading and using the binary data associated with the resource are not part of the cache's responsibilities; the cache merely provides the reference tracking mechanism. This cache: - Allows a single resource to be referenced multiple times, and tracks the references, so that the resource is deleted from memory when and only when nothing is referencing it. (Of course, this requires that the objects doing the referencing are cooperating by adding and removing references when necessary, but apart from that, client objects don't need to worry about the operations of the cache.) - Finds a resource that's already in memory, given an URL to the resource. Notes Modified 10/25/97 MJRoberts - Creation */ #ifndef HTMLRC_H #define HTMLRC_H #ifndef HTMLURL_H #include "htmlurl.h" #endif /* ------------------------------------------------------------------------ */ /* * Resource cache object. The cache object implements referencing * counting, and automatically deletes itself when its reference count * drops to zero. The cache object owns the associated resource, and * deletes the resource when the cache object is deleted. */ class CHtmlResCacheObject { public: /* create a cache object */ CHtmlResCacheObject(const CHtmlUrl *url, class CHtmlSysResource *resource, class CHtmlResCache *cache) { /* remember the resource */ resource_ = resource; /* remember the URL */ url_.set_url(url); /* no references yet -- all references must be explicitly added */ refcnt_ = 0; /* remember the cache */ cache_ = cache; } /* get the URL of the resource */ const textchar_t *get_url() const { return url_.get_url(); } /* get the resource */ class CHtmlSysResource *get_resource() { return resource_; } /* * Get the resource as specific resource subtypes. These downcast * routines use the typesafe downcast routines in the * CHtmlSysResource interface, so these methods return null when * called on inappropriate resource types. */ class CHtmlSysImage *get_image(); class CHtmlSysSound *get_sound(); /* * Add/remove a reference. When the last reference is removed, the * object is automatically deleted. */ void add_ref() { ++refcnt_; } void remove_ref() { --refcnt_; if (refcnt_ == 0) delete this; } private: /* * the constructor is never called explicitly by a client; instead, * the client simply removes its reference, and we'll automatically * delete ourselves when our reference count drops to zero */ ~CHtmlResCacheObject(); /* URL of the resource */ CHtmlUrl url_; /* the resource object - contains the resource's binary data */ class CHtmlSysResource *resource_; /* number of references */ long refcnt_; /* cache containing the resource */ class CHtmlResCache *cache_; }; /* ------------------------------------------------------------------------ */ /* * Resource cache */ class CHtmlResCache { friend class CHtmlResCacheObject; public: CHtmlResCache(); ~CHtmlResCache(); /* * Add an object to the cache. Note that although the cache has a * reference to the object, it is a "weak" reference, in that it * doesn't add to the reference count for the object and the * presence of this reference doesn't keep the object in memory. * Instead, the cache object is responsible for notifying us if it's * deleted, and we'll remove our reference to the object. */ void add(class CHtmlResCacheObject *obj); /* * Find an existing cache object matching an URL. Returns null if * there is no entry in the cache matching the URL. */ class CHtmlResCacheObject *find(const CHtmlUrl *url); /* * Find an existing cache object matching an URL, or create a new * one if one is not already in the cache. */ class CHtmlResCacheObject *find_or_create(class CHtmlSysWin *win, class CHtmlResFinder *resfinder, const CHtmlUrl *url); private: /* * Remove an object from the cache. This should never be called * except by the cache object itself as it's about to be deleted. */ void remove(class CHtmlResCacheObject *obj); /* hash table containing the cache objects */ class CHtmlHashTable *hashtable_; }; #endif /* HTMLRC_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmlreg.h����������������������������������������������������������������������0000664�0000000�0000000�00000007362�12650170723�0016303�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/html/htmlreg.h,v 1.2 1999/05/17 02:52:22 MJRoberts Exp $ */ /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlreg.h - HTML class registry Function Defines a "registry" of classes used by the HTML parser. A class is registered here by mentioning the class in an appropriate key macro. Functions that wish to enumerate classes registered under a particular key define the macro for that key then include this file. Notes This file can be safely included multiple times, because each inclusion merely provides an enumeration of classes registered under the key or keys selected immediately prior to the inclusion. Modified 08/26/97 MJRoberts - Creation */ #ifndef HTMLREG_H #define HTMLREG_H #endif /* HTMLREG_H */ /* * This portion of the file is intentionally unprotected against * multiple inclusion, because multiple inclusion of this section is * valid. */ /* ------------------------------------------------------------------------ */ /* * Tag classes. Each class that implements a named tag should be * registered here. */ /* if the key macro isn't defined by the includer, don't generate these keys */ #ifndef HTML_REG_TAG # define HTML_REG_TAG(cls) #endif /* block structuring elements */ HTML_REG_TAG(CHtmlTagBODY) HTML_REG_TAG(CHtmlTagTITLE) HTML_REG_TAG(CHtmlTagH1) HTML_REG_TAG(CHtmlTagH2) HTML_REG_TAG(CHtmlTagH3) HTML_REG_TAG(CHtmlTagH4) HTML_REG_TAG(CHtmlTagH5) HTML_REG_TAG(CHtmlTagH6) HTML_REG_TAG(CHtmlTagP) HTML_REG_TAG(CHtmlTagPRE) HTML_REG_TAG(CHtmlTagLISTING) HTML_REG_TAG(CHtmlTagXMP) HTML_REG_TAG(CHtmlTagADDRESS) HTML_REG_TAG(CHtmlTagBLOCKQUOTE) HTML_REG_TAG(CHtmlTagBQ) HTML_REG_TAG(CHtmlTagCREDIT) HTML_REG_TAG(CHtmlTagDIV) HTML_REG_TAG(CHtmlTagCENTER) /* list elements */ HTML_REG_TAG(CHtmlTagUL) HTML_REG_TAG(CHtmlTagOL) HTML_REG_TAG(CHtmlTagDIR) HTML_REG_TAG(CHtmlTagMENU) HTML_REG_TAG(CHtmlTagLI) HTML_REG_TAG(CHtmlTagDL) HTML_REG_TAG(CHtmlTagDT) HTML_REG_TAG(CHtmlTagDD) HTML_REG_TAG(CHtmlTagLH) /* phrase markup */ HTML_REG_TAG(CHtmlTagCITE) HTML_REG_TAG(CHtmlTagCODE) HTML_REG_TAG(CHtmlTagEM) HTML_REG_TAG(CHtmlTagKBD) HTML_REG_TAG(CHtmlTagSAMP) HTML_REG_TAG(CHtmlTagSTRONG) HTML_REG_TAG(CHtmlTagVAR) HTML_REG_TAG(CHtmlTagB) HTML_REG_TAG(CHtmlTagI) HTML_REG_TAG(CHtmlTagU) HTML_REG_TAG(CHtmlTagSTRIKE) HTML_REG_TAG(CHtmlTagS) HTML_REG_TAG(CHtmlTagTT) HTML_REG_TAG(CHtmlTagA) HTML_REG_TAG(CHtmlTagBR) HTML_REG_TAG(CHtmlTagHR) HTML_REG_TAG(CHtmlTagIMG) HTML_REG_TAG(CHtmlTagFONT) HTML_REG_TAG(CHtmlTagBASEFONT) HTML_REG_TAG(CHtmlTagBIG) HTML_REG_TAG(CHtmlTagSMALL) HTML_REG_TAG(CHtmlTagDFN) HTML_REG_TAG(CHtmlTagSUP) HTML_REG_TAG(CHtmlTagSUB) HTML_REG_TAG(CHtmlTagNOBR) HTML_REG_TAG(CHtmlTagWRAP) HTML_REG_TAG(CHtmlTagBANNER) HTML_REG_TAG(CHtmlTagABOUTBOX) HTML_REG_TAG(CHtmlTagTAB) HTML_REG_TAG(CHtmlTagMAP) HTML_REG_TAG(CHtmlTagAREA) HTML_REG_TAG(CHtmlTagSOUND) HTML_REG_TAG(CHtmlTagQ) HTML_REG_TAG(CHtmlTagNOLOG) HTML_REG_TAG(CHtmlTagLOG) /* table elements */ HTML_REG_TAG(CHtmlTagTABLE) HTML_REG_TAG(CHtmlTagCAPTION) HTML_REG_TAG(CHtmlTagTR) HTML_REG_TAG(CHtmlTagTH) HTML_REG_TAG(CHtmlTagTD) /* internal in-line attribute tags */ HTML_REG_TAG(CHtmlTagHILITEON) HTML_REG_TAG(CHtmlTagHILITEOFF) /* internal processing directives */ HTML_REG_TAG(CHtmlTagQT2) HTML_REG_TAG(CHtmlTagQT3) /* unimplemented HTML 3.2 tags */ /* HEAD BASE ISINDEX SCRIPT STYLE LINK META NXTID FORM INPUT SELECT OPTION TEXTAREA APPLET PARAM */ /* * done with key macros for this pass - undefine them so that they can * be redefined differently, if necessary, on a subsequent inclusion of * this header file */ #undef HTML_REG_TAG ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmlrf.cpp���������������������������������������������������������������������0000664�0000000�0000000�00000055525�12650170723�0016474�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/html/htmlrf.cpp,v 1.2 1999/05/17 02:52:22 MJRoberts Exp $"; #endif /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlrf.cpp - HTML resource finder Function Notes Modified 12/03/97 MJRoberts - Creation */ /* include TADS os interface functions */ #include <os.h> #ifndef HTMLRF_H #include "htmlrf.h" #endif #ifndef HTMLHASH_H #include "htmlhash.h" #endif #ifndef HTMLSYS_H #include "htmlsys.h" #endif /* ------------------------------------------------------------------------ */ /* * Hash table entry subclass for a resource map entry */ class CHtmlHashEntryFileres: public CHtmlHashEntryCI { public: CHtmlHashEntryFileres(const textchar_t *str, size_t len, unsigned long seekofs, unsigned long siz, int fileno) : CHtmlHashEntryCI(str, len, TRUE) { seekofs_ = seekofs; siz_ = siz; fileno_ = fileno; } CHtmlHashEntryFileres(const textchar_t *str, size_t len, const textchar_t *linkstr, size_t linklen) : CHtmlHashEntryCI(str, len, TRUE) { seekofs_ = 0; siz_ = 0; fileno_ = 0; link_.set(linkstr, linklen); } /* seek offset and size of the resource in the resource file */ unsigned long seekofs_; unsigned long siz_; /* * File number - the resource finder keeps a list of resource files * that we've mapped; this gives the ID of the file in the list. */ int fileno_; /* * Linked filename. Resources can be given as links to the local file * system rather than as stored binary data. This is mostly used for * debugging, to save the time and disk space of making a copy of the * binary resource data when the local files are all present anyway. */ CStringBuf link_; }; /* ------------------------------------------------------------------------ */ /* * File list entry. The resource finder keeps a list of files that * we've loaded. */ class CHtmlRfFile { public: CHtmlRfFile() { /* assume the seek base is zero */ res_seek_base_ = 0; } CHtmlRfFile(const textchar_t *fname) { /* set the filename */ filename_.set(fname); /* we don't know the seek base yet - assume it will be zero */ res_seek_base_ = 0; } /* get the filename */ const textchar_t *get_fname() { return filename_.get(); } /* change the filename */ void set_fname(const textchar_t *fname) { filename_.set(fname); } /* get/set the seek base position */ unsigned long get_seek_base() const { return res_seek_base_; } void set_seek_base(unsigned long val) { res_seek_base_ = val; } private: /* name of the file */ CStringBuf filename_; /* base seek offset of resources in .GAM file */ unsigned long res_seek_base_; }; /* ------------------------------------------------------------------------ */ /* * Resource finder implementation */ CHtmlResFinder::CHtmlResFinder() { /* initialize */ init_data(); } CHtmlResFinder::~CHtmlResFinder() { /* delete the old data */ delete_data(); } /* * initialize our data */ void CHtmlResFinder::init_data() { /* create a hash table for the resource map */ restab_ = new CHtmlHashTable(256, new CHtmlHashFuncCI); /* * Initialize the file list. We always allocate the first entry, * since this is the dedicated entry for the .GAM file. */ files_alloced_ = 5; files_ = (CHtmlRfFile **) th_malloc(files_alloced_ * sizeof(CHtmlRfFile *)); files_used_ = 1; files_[0] = new CHtmlRfFile(); /* we have no external resource file search path directories yet */ search_path_head_ = search_path_tail_ = 0; /* assume we're running as a normal interpreter (not a debugger) */ debugger_mode_ = FALSE; } /* * Delete our data */ void CHtmlResFinder::delete_data() { int i; /* delete the hash table */ delete restab_; /* delete the list of files */ for (i = 0 ; i < files_used_ ; ++i) delete files_[i]; th_free(files_); /* delete the external file search path */ while (search_path_head_ != 0) { /* remember the next one */ CHtmlResFinderPathEntry *nxt = search_path_head_->get_next(); /* delete this entry */ delete search_path_head_; /* unlink it */ search_path_head_ = nxt; } } /* * Reset */ void CHtmlResFinder::reset() { /* delete the old data */ delete_data(); /* re-initialize */ init_data(); } /* * Determine if an entry exists */ int CHtmlResFinder::resfile_exists(const textchar_t *resname, size_t resnamelen) { char cvtbuf[OSFNMAX]; /* * see if we can find an entry in the resource map - if so, it * definitely exists */ CHtmlHashEntryFileres *entry = (CHtmlHashEntryFileres *)restab_->find(resname, resnamelen); if (entry != 0) return TRUE; /* check with the local system to see if it's an embedded resource */ if (CHtmlSysFrame::get_frame_obj() != 0) { /* ask the system for the resource */ unsigned long pos; unsigned long siz; if (CHtmlSysFrame::get_frame_obj()->get_exe_resource( resname, resnamelen, cvtbuf, sizeof(cvtbuf), &pos, &siz)) return TRUE; } /* * There's no entry in the resource map matching this name - map the * resource name to a stand-alone external filename. If that fails, * return failure. */ if (!resname_to_filename( cvtbuf, sizeof(cvtbuf), resname, resnamelen, TRUE)) return FALSE; /* * if the file exists, the resource exists - so, if we don't get an * error code back from osfacc, return true */ return !osfacc(cvtbuf); } /* * Get the filename and seek offset to use to read a resource. If we * can find the resource in our .GAM file resource map, we'll return the * GAM filename and the offset of the resource in the .GAM file; * otherwise, we'll return the name of the resource itself and a seek * offset of zero. */ void CHtmlResFinder::get_file_info(CStringBuf *outbuf, const textchar_t *resname, size_t resnamelen, unsigned long *start_seekpos, unsigned long *file_size) { /* find the resource */ if (!find_resource(outbuf, resname, resnamelen, start_seekpos, file_size, 0)) { /* failed - log a debug message */ oshtml_dbg_printf("resource loader error: resource not found: %s\n", resname); } } /* * Find a resource. Returns true if we find the resource, false if not. * * If we find the resource, and fp_ret is not null, we'll fill in *fp_ret * with an osfildef* handle to the file, with its seek position set to the * start of the resource. * * If *filenamebuf is non-null, we'll fill it in with the name of the file * containing the resource. */ int CHtmlResFinder::find_resource(CStringBuf *filenamebuf, const char *resname, size_t resnamelen, unsigned long *start_seekpos, unsigned long *res_size, osfildef **fp_ret) { char cvtbuf[OSFNMAX]; const char *fname; size_t fnamelen; int is_url; /* see if we can find an entry in the resource map */ CHtmlHashEntryFileres *entry = (CHtmlHashEntryFileres *)restab_->find(resname, resnamelen); if (entry != 0 && entry->link_.get() == 0) { /* * Found an entry - return the seek position of the entry in the * file (calculate the actual seek position as the resource map * base seek address plus the offset of this resource within the * resource map). Get the filename from the files_[] list entry * for the file number stored in the resource entry. */ *start_seekpos = files_[entry->fileno_]->get_seek_base() + entry->seekofs_; *res_size = entry->siz_; /* tell the caller the name, if desired */ if (filenamebuf != 0) filenamebuf->set(files_[entry->fileno_]->get_fname()); /* if the caller wants the file handle, open the file */ if (fp_ret != 0) { /* open the file */ *fp_ret = osfoprb(files_[entry->fileno_]->get_fname(), OSFTBIN); /* seek to the start of the file */ if (*fp_ret != 0) osfseek(*fp_ret, *start_seekpos, OSFSK_SET); } /* indicate that we found the resource */ return TRUE; } else if (entry != 0) { /* it's a linked resource - get the local filename from the link */ fname = entry->link_.get(); fnamelen = strlen(fname); is_url = FALSE; } else { /* * check with the local system to see if it's a native embedded * resource (that is, embedded in the application executable, such * as a Mac resource fork item or a Windows .EXE resource) */ if (CHtmlSysFrame::get_frame_obj() != 0) { /* ask the system for the resource */ if (CHtmlSysFrame::get_frame_obj()->get_exe_resource( resname, resnamelen, cvtbuf, sizeof(cvtbuf), start_seekpos, res_size)) { /* it's an embedded resource - tell them the filename */ if (filenamebuf != 0) filenamebuf->set(cvtbuf); /* open the file if the caller is interested */ if (fp_ret != 0) { /* open the file */ *fp_ret = osfoprb(cvtbuf, OSFTBIN); /* seek to the start of the resource */ if (*fp_ret != 0) osfseek(*fp_ret, *start_seekpos, OSFSK_SET); } /* indicate that we found the resource */ return TRUE; } } /* use the resource name as a filename, after URL conversion */ fname = resname; fnamelen = resnamelen; is_url = TRUE; } /* * There's no entry in the resource map matching this name. Map the * resource name to a local filename in the resource directory; if that * fails, there's nowhere else to look, of return failure. */ if (!resname_to_filename(cvtbuf, sizeof(cvtbuf), fname, fnamelen, is_url)) { if (fp_ret != 0) *fp_ret = 0; *res_size = 0; return FALSE; } /* * when resource comes from an external local file, the entire file is * the single resource, so the resource data starts at offset zero */ *start_seekpos = 0; /* store the name of the stand-alone external file */ if (filenamebuf != 0) filenamebuf->set(cvtbuf); /* try opening the file */ osfildef *fp = osfoprb(cvtbuf, OSFTBIN); /* * if we failed, and the path looks like an "absolute" local path, try * opening the resource name directly, without any url-to-local * translations */ if (fp == 0) { /* make a null-terminated copy of the original resource name */ if (fnamelen > sizeof(cvtbuf) - 1) fnamelen = sizeof(cvtbuf) - 1; memcpy(cvtbuf, fname, fnamelen); cvtbuf[fnamelen] = '\0'; /* if the path looks absolute, try opening it as a local file */ if (os_is_file_absolute(cvtbuf)) { /* store the name of the external file */ if (filenamebuf != 0) filenamebuf->set(cvtbuf); /* try opening the file */ fp = osfoprb(cvtbuf, OSFTBIN); } } /* check to see if we got a file */ if (fp == 0) { /* no file - indicate failure */ *res_size = 0; return FALSE; } /* seek to the end of the file to determine its size */ osfseek(fp, 0, OSFSK_END); *res_size = (unsigned long)osfpos(fp); /* if the caller wants the file, return it; otherwise, close it */ if (fp_ret != 0) { /* * position the file back at the start of the resource data (which * is simply the start of the file, since the entire file is the * resource's data) */ osfseek(fp, 0, OSFSK_SET); /* give the handle to the caller */ *fp_ret = fp; } else { /* they don't want it - close the file */ osfcls(fp); } /* indicate success */ return TRUE; } /* * Given a resource name, generate the external filename for the * resource */ int CHtmlResFinder::resname_to_filename( char *outbuf, size_t outbufsize, const char *resname, size_t resname_len, int is_url) { char buf[OSFNMAX]; char rel_fname[OSFNMAX]; /* make sure the file safety level allows read access to external files */ int read_safety = 4, write_safety = 4; if (appctx_ != 0 && appctx_->get_io_safety_level != 0) appctx_->get_io_safety_level(appctx_->io_safety_level_ctx, &read_safety, &write_safety); if (read_safety > 3) return FALSE; /* generate a null-terminated version of the resource name */ if (resname_len > sizeof(buf) - 1) resname_len = sizeof(buf) - 1; memcpy(buf, resname, resname_len); buf[resname_len] = '\0'; /* convert the URL to a filename, if desired */ if (is_url) os_cvt_url_dir(rel_fname, sizeof(rel_fname), buf); else strcpy(rel_fname, buf); /* * If we have a non-URL absolute path, and we're running as a debugger, * allow access without enforcing any sandbox restrictions. In debug * builds, the compiler embed links to resource files rather than * actually embedding the resource data. These links can come from * library folders or elsewhere. In normal interpreter mode, this * could pose a security risk, as a malicious game could embed a link * to an absolute path to a system file - so we have to sandbox links * in interpreter mode, just as we sandbox any direct file reference. * But in debugger mode, the user is presumably working with the game's * source code, and in most cases with his or her own source code, so * we can assume that the code is more trusted. */ if (debugger_mode_ && !is_url && os_is_file_absolute(rel_fname) && !osfacc(rel_fname)) { safe_strcpy(outbuf, outbufsize, rel_fname); return TRUE; } /* * Get the resource path prefix. If we have an explicit resource root * directory, use that. Otherwise, use the path portion of the game * filename, so that we look for individual resources in the same * directory containing the game file. */ if (res_dir_.get() != 0 && res_dir_.get()[0] != '\0') { /* use the resource directory */ os_build_full_path(outbuf, outbufsize, res_dir_.get(), rel_fname); /* * Make sure the result is within this directory; if so, and it * exists, return this file. The same-directory check is for file * safety purposes: we don't want to let the game peek outside the * sandbox using ".." paths, symlinks, etc. If the file safety * level is 1 or less (1 is "read any/write local", 0 is "read * any/write any"), allow read access regardless of location. */ if ((read_safety <= 1 || os_is_file_in_dir(outbuf, res_dir_.get(), TRUE, FALSE)) && !osfacc(outbuf)) return TRUE; } else if (files_[0]->get_fname() != 0) { /* get the path name from the game filename */ os_get_path_name(buf, sizeof(buf), files_[0]->get_fname()); /* build the full path */ os_build_full_path(outbuf, outbufsize, buf, rel_fname); /* if the result exists and satifies file safety, return it */ if ((read_safety <= 1 || os_is_file_in_dir(outbuf, buf, TRUE, FALSE)) && !osfacc(outbuf)) return TRUE; } else { /* * we have no root path; just use the relative filename as it is; * limit the copy length to the buffer size */ size_t len = strlen(rel_fname); if (len > outbufsize) len = outbufsize - 1; /* copy and null-terminate the result */ memcpy(outbuf, rel_fname, len); outbuf[len] = '\0'; /* if the result exists and satifies file safety, return it */ if ((read_safety <= 1 || os_is_file_in_dir(outbuf, OSPATHPWD, TRUE, FALSE)) && !osfacc(outbuf)) return TRUE; } /* no luck with the basic resource directory; check the search path */ for (const CHtmlResFinderPathEntry *entry = search_path_head_ ; entry != 0 ; entry = entry->get_next()) { /* try building this path into a full filename */ os_build_full_path(outbuf, outbufsize, entry->get_path(), rel_fname); /* if the result exists and satifies file safety, return it */ if ((read_safety <= 1 || os_is_file_in_dir(outbuf, entry->get_path(), TRUE, FALSE)) && !osfacc(outbuf)) return TRUE; } /* no luck finding the file */ return FALSE; } /* * Set the name of the game file */ void CHtmlResFinder::set_game_name(const char *fname) { /* * Remember the name of the game file - we'll use this to look up * resources. The first entry in our file list is dedicated to the * GAM file, so set this entry's filename. In addition, we'll use * the path to the game when looking for resources, since we look in * the game's directory for external resources. */ files_[0]->set_fname(fname); } /* * Set the root directory path for individual resources */ void CHtmlResFinder::set_res_dir(const char *dir) { /* remember the name */ res_dir_.set(dir); } /* * Add a resource. We'll add an entry to our hash table with the * resource information. */ void CHtmlResFinder::add_resource(unsigned long ofs, unsigned long siz, const char *nm, size_t nmlen, int fileno) { /* create and add a new hash table entry */ restab_->add(new CHtmlHashEntryFileres(nm, nmlen, ofs, siz, fileno)); } /* * Add a resource link. This is used mainly for debugging purposes, to * create links from resource names directly to local files, rather than * copying the resource binary data into the game file. */ void CHtmlResFinder::add_resource(const char *fname, size_t fnamelen, const char *resnm, size_t resnmlen) { /* create and add a new hash entry */ restab_->add(new CHtmlHashEntryFileres(resnm, resnmlen, fname, fnamelen)); } /* * Add a resource file search path. */ void CHtmlResFinder::add_res_path(const char *path, size_t len) { /* create a search path entry */ CHtmlResFinderPathEntry *entry = new CHtmlResFinderPathEntry(path, len); /* link it in at the end of our list */ if (search_path_tail_ != 0) search_path_tail_->set_next(entry); else search_path_head_ = entry; search_path_tail_ = entry; } /* * Set the resource seek base for a resource file */ void CHtmlResFinder::set_resmap_seek(unsigned long seekpos, int fileno) { files_[fileno]->set_seek_base(seekpos); } /* * Add a resource file, returning the file number for the new resource * file. */ int CHtmlResFinder::add_resfile(const char *fname) { int fileno; /* note the new file's index */ fileno = files_used_; /* * If there isn't room in our array of resource files, expand the * array. */ if (files_used_ == files_alloced_) { files_alloced_ += 5; files_ = (CHtmlRfFile **) th_realloc(files_, files_alloced_ * sizeof(CHtmlRfFile *)); } /* add the new entry */ files_[files_used_] = new CHtmlRfFile(fname); ++files_used_; /* return the new file number */ return fileno; } /* ------------------------------------------------------------------------ */ /* * Static functions invoked from TADS loader. We'll refer these to an * instance of a CHtmlResFinder, which is stored in the callback * context. */ void CHtmlResFinder::cb_set_game_name(void *ctx, const char *fname) { ((CHtmlResFinder *)ctx)->set_game_name(fname); } void CHtmlResFinder::cb_set_res_dir(void *ctx, const char *dir) { ((CHtmlResFinder *)ctx)->set_res_dir(dir); } void CHtmlResFinder::cb_set_resmap_seek(void *ctx, unsigned long seekpos, int fileno) { ((CHtmlResFinder *)ctx)->set_resmap_seek(seekpos, fileno); } void CHtmlResFinder::cb_add_resource(void *ctx, unsigned long ofs, unsigned long siz, const char *nm, size_t nmlen, int fileno) { ((CHtmlResFinder *)ctx)->add_resource(ofs, siz, nm, nmlen, fileno); } void CHtmlResFinder::cb_add_resource_link( void *ctx, const char *fname, size_t fnamelen, const char *nm, size_t nmlen) { ((CHtmlResFinder *)ctx)->add_resource(fname, fnamelen, nm, nmlen); } int CHtmlResFinder::cb_add_resfile(void *ctx, const char *fname) { return ((CHtmlResFinder *)ctx)->add_resfile(fname); } void CHtmlResFinder::cb_add_res_path(void *ctx, const char *path, size_t len) { ((CHtmlResFinder *)ctx)->add_res_path(path, len); } /* * Determine if a resource exists */ int CHtmlResFinder::cb_resfile_exists(void *ctx, const char *res_name, size_t res_name_len) { return ((CHtmlResFinder *)ctx)->resfile_exists(res_name, res_name_len); } /* * Find a resource */ osfildef *CHtmlResFinder::cb_find_resource(void *ctx, const char *resname, size_t resnamelen, unsigned long *res_size) { unsigned long start_pos; osfildef *fp; /* try finding the resource */ if (((CHtmlResFinder *)ctx)->find_resource( 0, resname, resnamelen, &start_pos, res_size, &fp)) { /* success - return the file handle */ return fp; } else { /* failed - return a null file handle */ return 0; } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmlrf.h�����������������������������������������������������������������������0000664�0000000�0000000�00000020630�12650170723�0016126�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/html/htmlrf.h,v 1.2 1999/05/17 02:52:22 MJRoberts Exp $"; #endif /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlrf.h - HTML resource finder Function Finds HTML resources. An HTML resource can be in an external file, or it can be part of the .GAM file, stored in the HTMLRES resource structure. Notes Modified 12/03/97 MJRoberts - Creation */ #ifndef HTMLRF_H #define HTMLRF_H #include <stdlib.h> #ifndef TADSHTML_H #include "tadshtml.h" #endif /* include the TADS main runtime header */ #ifndef TRD_H #include "trd.h" #endif class CHtmlResFinder { public: CHtmlResFinder(); ~CHtmlResFinder(); /* reset - delete any old resource finder information */ void reset(); /* * Set up a TADS appctxdef structure for use with the TADS loader. * The TADS loader makes calls through function pointers in this * structure; we'll set up the structure to refer to our methods, so * that we can learn about the resources in the .GAM file. */ void init_appctx(appctxdef *appctx) { /* * hang on to a reference to the context - we need it to get the * current file safety level when searching for unbundled resources */ appctx_ = appctx; /* load up the callback functions to point to my callbacks */ appctx->set_game_name = &cb_set_game_name; appctx->set_game_name_ctx = this; appctx->set_res_dir = &cb_set_res_dir; appctx->set_res_dir_ctx = this; appctx->set_resmap_seek = &cb_set_resmap_seek; appctx->set_resmap_seek_ctx = this; appctx->add_resource = &cb_add_resource; appctx->add_resource_ctx = this; appctx->add_res_path = &cb_add_res_path; appctx->add_res_path_ctx = this; appctx->add_resfile = &cb_add_resfile; appctx->add_resfile_ctx = this; appctx->resfile_exists = &cb_resfile_exists; appctx->resfile_exists_ctx = this; appctx->find_resource = &cb_find_resource; appctx->find_resource_ctx = this; appctx->add_resource_link = &cb_add_resource_link; appctx->add_resource_link_ctx = this; } /* * Set debugger mode. The host system should call this with 'flag' set * to TRUE if this is a debugger application, rather than an ordinary * interpreter. In debugger mode, we allow resource file links (as * opposed to embedded resources) to access files outside of the * resource directory path list. In normal interpreter mode, we * enforce the standard file safety sandbox settings on resource links, * to prevent malicious game programs from using links to access files * outside of the sandbox. We override the file safety sandbox for * resource links when the application is a debugger, because we assume * that the debugger is only used with code written by the local user * or a trusted collaborator. */ void set_debugger_mode(int flag) { debugger_mode_ = flag; } /* * Get the filename and seek offset to use to read a resource. If * we can find the resource in our .GAM file resource map, we'll * return the .GAM filename and the offset of the resource in the *. GAM file; otherwise, we'll return the name of the resource * itself and a seek offset of zero. */ void get_file_info(CStringBuf *outbuf, const textchar_t *resource_name, size_t resnamelen, unsigned long *start_seekpos, unsigned long *file_size); /* determine if a resource file exists */ int resfile_exists(const textchar_t *resource_name, size_t resnamelen); private: /* find a resource, optionally returning a handle to the file */ int find_resource(CStringBuf *filenamebuf, const char *resname, size_t resnamelen, unsigned long *start_seekpos, unsigned long *res_size, osfildef **fp_ret); /* initialize our data */ void init_data(); /* delete all of our data */ void delete_data(); /* set the .GAM file name */ void set_game_name(const char *fname); /* set the root path for individual resources */ void set_res_dir(const char *dir); /* set the resource base seek position */ void set_resmap_seek(unsigned long seekpos, int fileno); /* add a resource in the .GAM resource map */ void add_resource(unsigned long ofs, unsigned long siz, const char *nm, size_t nmlen, int fileno); /* * add a resource in the .GAM resource map as a link to a local * filename - this is used for debugging, so that we can still look up * resources stored under artificial resource names, but without * consuming the time and disk space of actually copying the binary * data into the compiled .GAM file */ void add_resource(const char *fname, size_t fnamelen, const char *resname, size_t resnamelen); /* add a stand-alone resource file search path */ void add_res_path(const char *path, size_t len); /* add a resource file */ int add_resfile(const char *fname); /* * Given a resource name, construct the name of the external file * containing the resource. Returns true on success, false on failure. */ int resname_to_filename(char *fname_buf, size_t fname_buf_size, const char *resname, size_t resname_len, int is_url); /* * Static functions used as function pointers in the appctx * structure. These functions invoke our methods. */ static void cb_set_game_name(void *ctx, const char *fname); static void cb_set_res_dir(void *ctx, const char *dir); static void cb_set_resmap_seek(void *ctx, unsigned long seekpos, int fileno); static void cb_add_resource(void *ctx, unsigned long ofs, unsigned long siz, const char *nm, size_t nmlen, int fileno); static void cb_add_resource_link(void *ctx, const char *fname, size_t fnamelen, const char *resnm, size_t resnmlen); static void cb_add_res_path(void *ctx, const char *path, size_t len); static osfildef *cb_find_resource(void *ctx, const char *resname, size_t resnamelen, unsigned long *res_size); static int cb_add_resfile(void *ctx, const char *fname); static int cb_resfile_exists(void *ctx, const char *res_name, size_t res_name_len); /* hash table containing resource information */ class CHtmlHashTable *restab_; /* * array of file objects, number of items used in the list, and * number of entries allocated in the array */ class CHtmlRfFile **files_; int files_used_; int files_alloced_; /* root path for resource files, if one was specified */ CStringBuf res_dir_; /* head and tail of stand-alone resource file search path */ class CHtmlResFinderPathEntry *search_path_head_; class CHtmlResFinderPathEntry *search_path_tail_; /* the host application context object */ appctxdef *appctx_; /* are we running as a debugger? */ int debugger_mode_; }; /* * Search path entry. We keep a linked list of these entries, giving the * list of directories to search when we're looking for a stand-alone * external resource. */ class CHtmlResFinderPathEntry { public: /* construct */ CHtmlResFinderPathEntry(const char *path, size_t len) : path_(path, len) { /* no 'next' link yet */ nxt_ = 0; } /* get/set the 'next' pointer */ CHtmlResFinderPathEntry *get_next() const { return nxt_; } void set_next(CHtmlResFinderPathEntry *nxt) { nxt_ = nxt; } /* get a pointer to the null-terminated string giving the path */ const char *get_path() const { return path_.get(); } private: /* our directory path string */ CStringBuf path_; /* the next entry in the list */ CHtmlResFinderPathEntry *nxt_; }; #endif /* HTMLRF_H */ ��������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmlsnd.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000051436�12650170723�0016646�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/html/htmlsnd.cpp,v 1.2 1999/05/17 02:52:22 MJRoberts Exp $"; #endif /* * Copyright (c) 1998 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlsnd.cpp - sound support Function Notes Modified 01/10/98 MJRoberts - Creation */ #include <stdlib.h> /* include TADS OS layer */ #include <os.h> #ifndef HTML_OS_H #include "html_os.h" #endif #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef HTMLSND_H #include "htmlsnd.h" #endif #ifndef HTMLRC_H #include "htmlrc.h" #endif #ifndef HTMLSYS_H #include "htmlsys.h" #endif #ifndef HTMLATTR_H #include "htmlattr.h" #endif /* ------------------------------------------------------------------------ */ /* * Sound queue entry implementation */ CHtmlSoundQueueEntry::CHtmlSoundQueueEntry( CHtmlResCacheObject *res, int repeat_count, int random_start, HTML_Attrib_id_t seq, int vol, double fade_in, double fade_out, int crossfade_in, int crossfade_out) { /* remember the cache object, and add a reference to it */ res_ = res; res_->add_ref(); /* not in any list yet */ next_ = prev_ = 0; /* remember the attributes */ seq_ = seq; random_start_ = random_start; repeat_count_ = orig_repeat_count_ = repeat_count; fade_in_ = fade_in; fade_out_ = fade_out; crossfade_in_ = crossfade_in; crossfade_out_ = crossfade_out; vol_ = vol; /* this sound hasn't been cancelled */ cancel_ = FALSE; } CHtmlSoundQueueEntry::~CHtmlSoundQueueEntry() { /* remove our reference from our cache object */ res_->remove_ref(); } /* ------------------------------------------------------------------------ */ /* * Sound queue implementation */ CHtmlSoundQueue::CHtmlSoundQueue(CHtmlSysWin *win, CHtmlSoundQueue *bgqueue) { /* remember our window and the next queue in the background */ win_ = win; next_bg_queue_ = bgqueue; /* we haven't had any timer events yet */ last_timer_event_time_ = os_get_time(); /* register with the window to receive timer callbacks */ win->register_timer_func(&timer_func_cb, (void *)this); /* nothing in the list yet */ first_entry_ = last_entry_ = 0; /* nothing is playing yet */ playing_ = 0; last_played_ = 0; /* start off in default sequence mode */ seq_mode_ = HTML_Attrib_invalid; /* we're not suspended yet */ fg_suspender_ = 0; } CHtmlSoundQueue::~CHtmlSoundQueue() { /* make sure the current sound is stopped */ stop_current_sound(TRUE, 0.0, FALSE); /* remove our timer function */ win_->unregister_timer_func(&timer_func_cb, (void *)this); /* delete all of the entries in the queue */ clear(); } /* * Clear the queue. Deletes all entries in the queue. */ void CHtmlSoundQueue::clear() { CHtmlSoundQueueEntry *cur; CHtmlSoundQueueEntry *nxt; /* delete all of our list entries */ for (cur = first_entry_ ; cur != 0 ; cur = nxt) { /* remember the next item, since we're about to delete this one */ nxt = cur->next_; /* delete this item */ delete cur; } /* clear all of our references to queue entries, since they're all gone */ first_entry_ = last_entry_ = 0; playing_ = 0; last_played_ = 0; } /* * Add a sound to the queue */ void CHtmlSoundQueue::add_sound(CHtmlResCacheObject *res, int repeat_count, int random_start, HTML_Attrib_id_t seq, int vol, double fade_in, double fade_out, int crossfade_in, int crossfade_out) { /* if it's not a sound, ignore the request */ if (res->get_sound() == 0) return; /* add the entry to the end of the queue */ add_queue_entry( res, repeat_count, random_start, seq, vol, fade_in, fade_out, crossfade_in, crossfade_out); /* * if there's nothing currently playing, we may want to start the * new sound immediately */ if (playing_ == 0) maybe_start_sound(); } /* * Immediately stop the sound currently playing. Do not start a new * sound. If 'sync' is true, this call must not return until the sound * has actually stopped. */ void CHtmlSoundQueue::stop_current_sound( int sync, double fade_out, int fade_in_background) { CHtmlSoundQueueEntry *entry; if (playing_ != 0) { /* * We have a sound playing, so we must end it and then clear out * the queue. First, mark all of the objects currently in the * queue as cancelled */ for (entry = first_entry_ ; entry != 0 ; entry = entry->next_) entry->cancel(); /* * Next, tell the current sound to stop playing. Ending the sound * will invoke our callback as normal; at that point we'll clear * all of the canceled sound objects out of the queue. */ playing_->get_res()->get_sound()->cancel_sound( win_, sync, (long)(fade_out * 1000.0), fade_in_background); } else { /* nothing is playing - delete everything in the queue */ while (first_entry_ != 0) delete_queue_entry(first_entry_); } } /* * Create a new queue entry and add it at the end of the queue */ void CHtmlSoundQueue::add_queue_entry(CHtmlResCacheObject *res, int repeat_count, int random_start, HTML_Attrib_id_t seq, int vol, double fade_in, double fade_out, int crossfade_in, int crossfade_out) { CHtmlSoundQueueEntry *entry; /* create the new entry */ entry = new CHtmlSoundQueueEntry( res, repeat_count, random_start, seq, vol, fade_in, fade_out, crossfade_in, crossfade_out); /* * If there's a crossfade-in, it means that they want to do a crossfade * against the outgoing previous track. The way we actually accomplish * this is to do a crossfade-out on the outgoing track, because the * outgoing track controls when the next track starts playing. So, * what we need to do is set the outgoing crossfade on the prior track. */ if (crossfade_in && last_entry_ != 0) { CHtmlSysSound *snd; /* * If it's playing, request the cross-fade on the current playback. * Use the fade-in time of the new track as the fade-out time of * the outgoing track, since we want them to overlap over the same * duration. */ if ((snd = last_entry_->get_res()->get_sound()) != 0) snd->add_crossfade(win_, (int)(fade_in * 1000.0)); /* * mark the queue entry as having a cross-fade, using the fade-in * time of the new track as the fade-out time of the old track */ last_entry_->set_crossfade_out(fade_in); } /* link it into the end of our list */ entry->prev_ = last_entry_; if (last_entry_ != 0) last_entry_->next_ = entry; else first_entry_ = entry; last_entry_ = entry; } /* * Remove an entry from the queue and delete the entry */ void CHtmlSoundQueue::delete_queue_entry(CHtmlSoundQueueEntry *entry) { /* unlink it from the list */ if (entry->prev_ != 0) entry->prev_->next_ = entry->next_; else first_entry_ = entry->next_; if (entry->next_ != 0) entry->next_->prev_ = entry->prev_; else last_entry_ = entry->prev_; /* * forget any pointers to this sound, since it won't be valid after * we return */ if (playing_ == entry) playing_ = 0; /* * if this was the last-played entry, back up to the previous surviving * queue element */ if (last_played_ == entry) last_played_ = entry->prev_; /* delete the entry */ entry->next_ = entry->prev_ = 0; delete entry; } /* * Process timer notification */ void CHtmlSoundQueue::on_timer() { /* if we have a sound currently playing, there's nothing to do here */ if (playing_ != 0) return; /* * Check the interval from the last timer call. If less than a * second has elapsed, ignore the call - the system is delivering * timer events faster than we can use them. */ if (os_get_time() < last_timer_event_time_ + OS_TIMER_UNITS_PER_SECOND) return; /* note the current time as the time of the last timer event */ last_timer_event_time_ = os_get_time(); /* check to see if we should start a new sound */ maybe_start_sound(); } /* * Check to see if we should start a new sound. This routine is invoked * whenever we finish playing a sound, or when we add a new sound and * nothing else is playing, or at timer intervals when nothing is * playing. */ void CHtmlSoundQueue::maybe_start_sound() { CHtmlSoundQueueEntry *nxt = 0; CHtmlSoundQueueEntry *orig_nxt = 0; int more_in_loop = FALSE; /* if anything is playing already, we can't start a new sound */ if (playing_ != 0) return; /* * Check the current sequence mode to determine what to do next */ switch(seq_mode_) { case HTML_Attrib_invalid: case HTML_Attrib_replace: default: /* * Default sequence mode. Play the next sound in line. If * last_played_ is not null, it's the next in line, since it's * either the last sound we played (which means the sound is set * to loop), or the next sound after that sound (which means * that the previous sound was deleted). */ if (last_played_ != 0) nxt = last_played_; else nxt = first_entry_; break; case HTML_Attrib_loop: /* * Looping mode. If we've reached the last sound in the queue, * start over at the beginning. */ if (last_played_ != 0 && last_played_->next_ != 0) nxt = last_played_->next_; else nxt = first_entry_; /* * Note if there's more to play in this loop. There's more to play * if (a) there's another sound after 'nxt', or (b) we can loop * back to the beginning and find a distinct entry. */ if (nxt != 0 && (nxt->next_ != 0 || first_entry_ != nxt)) more_in_loop = TRUE; break; case HTML_Attrib_random: /* * Choose a new sound at random. Count the elements in the queue, * then randomly choose one of them. If we don't have any * entries, do nothing here. */ if (first_entry_ != 0) { int cnt; CHtmlSoundQueueEntry *cur; int idx; /* count the entries */ for (cur = first_entry_, cnt = 0 ; cur != 0 ; cur = cur->next_, ++cnt) ; /* pick a random number from 0 to the number of entries */ idx = rand() % cnt; /* find that entry */ for (nxt = first_entry_ ; nxt != 0 && idx != 0 ; nxt = nxt->next_, --idx) ; /* * Note if there's more to play in this random loop. There's * more to play if the number of entries is greater than 1. */ more_in_loop = (cnt > 1); } else { /* there are no entries; there's nothing we can randomly start */ nxt = 0; } break; } /* make sure we found something */ if (nxt == 0) { /* * There's nothing that's next in line for playback. However, * if we have anything in the queue that can be randomly * started, we can go back and play that now. */ for (nxt = first_entry_ ; nxt != 0 ; nxt = nxt->next_) { /* if this can be randomly started, consider starting it */ if (nxt->get_random_start() != 0) break; } /* if we still didn't find anything, there's nothing to play */ if (nxt == 0) return; } /* * Check this sound for randomness. If it has a random start * probability, randomly decide whether to start it; if it fails, * move on to the next sound. */ orig_nxt = nxt; for (;;) { /* if this one isn't random, start it immediately */ if (nxt->get_random_start() == 0) break; /* * choose a random number from 0 to 100 - if it's less than the * start probability, start the sound, otherwise skip it for now */ if ((rand() % 100) <= nxt->get_random_start()) break; /* * move on to the next sound, wrapping to the start of the queue * if we reach the end */ nxt = nxt->next_; if (nxt == 0) nxt = first_entry_; /* if we've looped, we don't want to start anything now */ if (nxt == orig_nxt) return; } /* remember this as the current sound */ playing_ = nxt; /* start the sound, if we found one */ if (nxt != 0) { CHtmlSysSound *snd; int repeat; /* * If the new sound's sequence mode is REPLACE, it means that * everything in the queue before this sound should be removed. */ if (nxt->get_sequence() == HTML_Attrib_replace) { /* * keep removing the first entry until the new sound is the * first entry */ while (first_entry_ != 0 && first_entry_ != nxt) delete_queue_entry(first_entry_); } /* get the sound to play */ snd = nxt->get_res()->get_sound(); /* * tell next queue in background what we're about to do, so that * it can suspend playback if it has a sound that can't be * played simultaneously with our new sound - since we're in the * foreground, we have priority */ if (next_bg_queue_ != 0) next_bg_queue_->start_fg_sound(snd); /* remember its sequence mode */ seq_mode_ = nxt->get_sequence(); /* * If we're in REPLACE mode, and this sound is repeated and * non-random, tell the low-level sound player to repeat the sound; * we can allow this because REPLACE-mode non-random sounds always * play back their full number of repetitions before we can move on * to the next sound. The low-level system player might be more * efficient at looping the sound than we can, so let it control * the looping if it wants to (not all systems implement that). */ if ((seq_mode_ == HTML_Attrib_invalid || seq_mode_ == HTML_Attrib_replace) && nxt->get_random_start() == 0) { /* get the looping count from the sound */ repeat = nxt->get_repeat_count(); } else { /* * in other sequence modes, repeated sounds don't repeat * until other sounds in the queue have had a chance to * play, so we can't let the low-level player loop these * sounds */ repeat = 1; } /* * Figure the fading parameters. If this is the first iteration, * fade in according to the tag parameter; otherwise don't fade in, * since we don't use fades between repeats. */ double fade_in = 0, fade_out = 0; if (nxt->get_play_count() == 0) fade_in = nxt->get_fade_in() * 1000.0; /* * If we're scheduling all remaining iterations, fade out according * to the tag parameter; otherwise, don't fade out, since we'll be * scheduling more iterations later and, again, we don't want to * fade between iterations. Similarly, if we're in a looping mode * (SEQUENCE=LOOP or SEQUENCE=RANDOM), and we found more left in * the queue after this sound, we have to assume that this isn't * the last iteration. */ if (repeat == nxt->get_repeat_count() && repeat != 0 && !more_in_loop) fade_out = nxt->get_fade_out() * 1000.0; /* start it playing */ if (snd->play_sound(win_, sound_done_cb, this, repeat, nxt->get_res()->get_url(), nxt->get_volume(), (int)fade_in, (int)fade_out, nxt->get_crossfade_out())) { /* cancel this sound */ nxt->cancel(); /* * playback failed - act as though the current sound has * just stopped with zero repetitions */ on_sound_done(0); } } } /* * Process notification that our sound has finished playing */ void CHtmlSoundQueue::on_sound_done(int repeat_count) { CHtmlSoundQueueEntry *entry; CHtmlSoundQueueEntry *nxt; /* * remember this sound's sequence mode -- the sequence mode is taken * from the last sound we played */ seq_mode_ = (playing_ != 0 ? playing_->get_sequence() : HTML_Attrib_invalid); /* remember the sound that played last */ last_played_ = playing_; /* nothing is playing any more */ playing_ = 0; /* * Tell the next background queue that we've just finished this * sound. If a background queue had to suspend its sound in order * for us to play, the background queue can now resume its sound. */ if (next_bg_queue_ != 0 && last_played_ != 0) next_bg_queue_->end_fg_sound(last_played_->get_res()->get_sound()); /* * Decrement this sound's repeat count. If we've exhausted its * repeat count, delete the entry from the queue, since we can't * play it again. */ if (last_played_ != 0 && last_played_->dec_repeat_count(repeat_count)) delete_queue_entry(last_played_); /* * Check the queue for cancellations, and delete any sounds that * have been cancelled. */ for (entry = first_entry_ ; entry != 0 ; entry = nxt) { /* remember the next entry, in case we delete this one */ nxt = entry->next_; /* if this entry has been cancelled, delete it */ if (entry->cancel_) delete_queue_entry(entry); } /* check to see if there's another sound to play */ maybe_start_sound(); } /* * Receive notification that a queue in the foreground is about to start * a new sound. If the system can't play the new sound and our current * sound at the same time, we must suspend our current sound to make * room for the new sound. */ void CHtmlSoundQueue::start_fg_sound(CHtmlSysSound *fgsnd) { /* * suspend our sound if necessary - this is system dependent, so we * may not have to do anything */ if (playing_ != 0 && playing_->get_res()->get_sound()->maybe_suspend(fgsnd)) { /* * we had to suspend our sound for this new sound - remember the * foreground sound so that we can resume playback when the * foreground sound ends */ fg_suspender_ = fgsnd; } /* tell the next queue in the background about it */ if (next_bg_queue_ != 0) next_bg_queue_->start_fg_sound(fgsnd); } /* * Receive notification that a queue in the foreground has just finished * playing a sound. If we suspended our sound to make room for the * foreground sound, we can now resume playback of our sound. */ void CHtmlSoundQueue::end_fg_sound(CHtmlSysSound *fgsnd) { /* * if we suspended our sound for this sound, and we still have a * sound in our queue, resume our sound now */ if (fg_suspender_ == fgsnd && playing_ != 0) playing_->get_res()->get_sound()->resume(); /* tell the next queue in the background about it */ if (next_bg_queue_ != 0) next_bg_queue_->end_fg_sound(fgsnd); } /* * Timer callback. This is the static member that we register with the * window; we'll convert the context to an object and invoke the * appropriate member method. */ void CHtmlSoundQueue::timer_func_cb(void *ctx) { /* invoke the timer function in the object */ ((CHtmlSoundQueue *)ctx)->on_timer(); } /* * End-of-sound callback */ void CHtmlSoundQueue::sound_done_cb(void *ctx, int repeat_count) { /* invoke the end-of-sound function in the object */ ((CHtmlSoundQueue *)ctx)->on_sound_done(repeat_count); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmlsnd.h����������������������������������������������������������������������0000664�0000000�0000000�00000037341�12650170723�0016312�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/html/htmlsnd.h,v 1.2 1999/05/17 02:52:22 MJRoberts Exp $ */ /* * Copyright (c) 1998 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlsnd.h - sound support Function Notes Modified 01/10/98 MJRoberts - Creation */ #ifndef HTMLSND_H #define HTMLSND_H #ifndef HTML_OS_H #include "html_os.h" #endif #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef HTMLATTR_H #include "htmlattr.h" #endif /* ------------------------------------------------------------------------ */ /* * Sound queue entry. Each entry in a sound queue is represented by one * of these objects. */ class CHtmlSoundQueueEntry { friend class CHtmlSoundQueue; public: CHtmlSoundQueueEntry(class CHtmlResCacheObject *res, int repeat_count, int random_start, HTML_Attrib_id_t seq, int vol, double fade_in, double fade_out, int crossfade_in, int crossfade_out); ~CHtmlSoundQueueEntry(); /* get the sound resource */ class CHtmlResCacheObject *get_res() const { return res_; } /* get the sequence type */ HTML_Attrib_id_t get_sequence() const { return seq_; } /* get the repeat count remaining - zero indicates looping forever */ int get_repeat_count() const { return repeat_count_; } /* * Decrement the repeat count. This should be called when we finish * playing the sound to determine how many more times the sound * should be played. This routine returns true if we've exhausted * the repeat count. */ int dec_repeat_count(int count) { /* if the repeat count is zero, we play forever */ if (repeat_count_ == 0) return FALSE; /* decrement the counter; return true if it's reached zero */ repeat_count_ -= count; return (repeat_count_ == 0); } /* * Get the number of times the playback has been completed. This * returns the number of iterations that we've completed, as indicated * by calls to dec_repeat_count(). */ int get_play_count() const { return orig_repeat_count_ - repeat_count_; } /* * Exhaust the repeat count in preparation for repeated playback. * This should be called when the sound is scheduled to be played * for its full number of repetitions. We'll set the repeat count * to 1 if it's non-zero, in which case when the sound has finished * playing it will be evident that it doesn't need to be played * again, or leave it at zero if it's zero. */ void exhaust_repeat_count() { /* if the repeat count is zero, we play forever */ if (repeat_count_ == 0) return; /* set the counter to 1 to indicate that it has no more plays left */ repeat_count_ = 1; } /* get the random start probability */ int get_random_start() const { return random_start_; } /* * Mark the sound for cancellation. When the end-of-sound callback * fires, we'll remove the sound from its queue. */ void cancel() { cancel_ = TRUE; } /* check if the sound has been cancelled */ int is_cancelled() const { return cancel_; } /* get the base volume (0-100) */ int get_volume() const { return vol_; } /* get the fade durations */ double get_fade_in() const { return fade_in_; } double get_fade_out() const { return fade_out_; } /* get the crossfade parameters */ int get_crossfade_in() const { return crossfade_in_; } int get_crossfade_out() const { return crossfade_out_; } /* * Set a crossfade-out. If we already have a fade, this sets the fade * to the longer of the current fade and the new fade. */ void set_crossfade_out(double ms) { /* note that we have a crossfade on the fade-out */ crossfade_out_ = TRUE; /* if the new fade is longer, use it as the fade time */ if (ms > fade_out_) fade_out_ = ms; } protected: /* this item's sound resource */ class CHtmlResCacheObject *res_; /* sequencing type */ HTML_Attrib_id_t seq_; /* repeat count - number of iterations remaining to be played */ int repeat_count_; /* original repeat count */ int orig_repeat_count_; /* random start probability */ int random_start_; /* next and previous items in my queue */ class CHtmlSoundQueueEntry *next_; class CHtmlSoundQueueEntry *prev_; /* volume level (0-100, 0=silence, 100=unattenuated) */ int vol_; /* fade times */ double fade_in_; double fade_out_; /* crossfade flags */ int crossfade_in_ : 1; int crossfade_out_ : 1; /* flag indicating that the sound is to be cancelled */ int cancel_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Sound queue. */ class CHtmlSoundQueue { public: /* * Create a sound queue. The queue is associated with the given * system window. 'bgqueue' is the next queue further in the * background; whenever we play a sound, we will tell each queue * behind me about it so that they have a chance to suspend their * playback if the sound device requires exclusive access. At the * end of each sound, we'll inform each background queue so that * they can resume playback if they suspended a sound for us. */ CHtmlSoundQueue(class CHtmlSysWin *win, CHtmlSoundQueue *bgqueue); ~CHtmlSoundQueue(); /* delete all entries in the queue */ void clear(); /* * Add an entry to the queue. * * 'repeat' is the number of times to repeat the sound. 0 indicates * that the sound is to be looped indefinitely. * * 'random_start' is the random start probability. 0 indicates that * the sound is to be played without randomization. A value from 1 to * 100 is the probability that the sound will be started at any given * time that the queue is idle. Each time a sound is played, if the * repeat count is one, we'll remove the sound from the queue, and if * the repeat count is higher than one, we'll decrement it. * * 'seq' is the sequence code. HTML_Attrib_replace indicates that any * previous sounds in the queue should be removed once this sound * plays. HTML_Attrib_random indicates that other sounds should be * kept, and a new sound from the queue chosen randomly after this * sound has been played. HTML_Attrib_cycle indicates that other * sounds should be kept; after this sound is finished playing, we * should move on to the next sound, or back to the first sound in the * queue if nothing's left. * * 'fade_in' and 'fade_out' are the start-of-track and end-of-track * fade intervals, in seconds. A fade time of 0.0 means that the track * simply starts and stops at full volume. Non-zero fade times mean * that we fade smoothly in and/or out over the given intervals. */ void add_sound(class CHtmlResCacheObject *res, int repeat_count, int random_start, HTML_Attrib_id_t seq, int vol, double fade_in, double fade_out, int crossfade_in, int crossfade_out); /* * Immediately stop the sound currently playing. Do not start a new * sound. This halts the queue so that a transition can be made, such * as clearing out all current music and starting a new set of music. * * If sync is true, we must not return until the sound has been stopped * and the callback has been invoked. Normally, we'll schedule the * sound for cancellation and return; the sound may not actually be * stopped by the time the function returns. If sync is true, however, * we won't return until after the callback has been invoked. * * If fade_out is non-zero, the sounds should be faded out gradually * over the given interval in seconds. * * If fade_in_bg is true, the fade (if any) should occur in the * background: this means that the current track continues to play for * the duration of the fade, but is "detached" from the queue * immediately, allowing the next track in the queue to start playing * concurrently with the old track's fade. If fade_in_bg is false, the * current track will simply continue playing for the duration of the * fade, and will then be stopped and removed from the queue as soon as * the fade ends (or as soon as the track itself ends, if that happens * earlier). */ void stop_current_sound(int sync, double fade_out, int fade_in_bg); /* * Receive notification that a queue further in the foreground is * about to start a sound. If we're playing back a sound that uses * the same device, and playback requires exclusive access to the * device (whether this is true or not is system-dependent), we will * suspend our playback until we hear that the foreground sound is * finished. The end-of-sound callback is not called here no matter * what happens, because we're only suspending playback rather than * cancelling playback. */ void start_fg_sound(class CHtmlSysSound *snd); /* * Receive notification that a foreground sound is terminating. If * we suspended a sound on a previous call to start_fg_sound, we * should resume playback now. */ void end_fg_sound(CHtmlSysSound *snd); protected: /* create a new queue entry and add it at the end of the queue */ void add_queue_entry(class CHtmlResCacheObject *res, int repeat_count, int random_start, HTML_Attrib_id_t seq, int vol, double fade_in, double fade_out, int crossfade_in, int crossfade_out); /* remove a queue entry from the queue and delete it */ void delete_queue_entry(class CHtmlSoundQueueEntry *entry); /* process a timer callback */ void on_timer(); /* process an end-of-sound event */ void on_sound_done(int repeat_count); /* * Check to see if we need to start a new sound. This routine is * invoked whenever we add a new sound and nothing else is playing, * whenever we end a sound, and at timer intervals when nothing else * is playing. */ void maybe_start_sound(); /* * Timer callback function. When we're created, we register with * our associated window to receive periodic timer callbacks. This * is the function invoked by the window for these callbacks. */ static void timer_func_cb(void *ctx); /* * End-of-sound callback function. When we play a sound, we'll tell * the sound to call this function when it's finished playing. */ static void sound_done_cb(void *ctx, int repeat_count); /* * The window I'm associated with. A sound queue is always * associated with a window; the window provides access to the * system services that allow us to play sounds. */ class CHtmlSysWin *win_; /* next queue in background behind me */ CHtmlSoundQueue *next_bg_queue_; /* list of sound queue entries */ class CHtmlSoundQueueEntry *first_entry_; class CHtmlSoundQueueEntry *last_entry_; /* * Current queue entry that's playing. If the queue is currently * idle (i.e., no sound is currently playing), this member is null. */ class CHtmlSoundQueueEntry *playing_; /* * Previous sound that we played. We use this for sequencing to * determine the next sound to play. */ class CHtmlSoundQueueEntry *last_played_; /* * Current sequence mode. The sequence mode is selected by the last * sound we played back. */ HTML_Attrib_id_t seq_mode_; /* * Foreground sound for which we're suspended. If the system * requires that a particular type of sound be played exclusively * (i.e, the sound can't play simultaneously with any other sounds * of certain types), we'll suspend our sound whenever a queue * further in the foreground needs to start a sound and we're * playing an incompatible sound. This member records the * foreground sound that suspended us so that we can resume playback * when the foreground sound ends. */ class CHtmlSysSound *fg_suspender_; /* system time of last timer event */ os_timer_t last_timer_event_time_; }; /* ------------------------------------------------------------------------ */ /* * Sound loader. The sound loaders are very simple; they merely record * the file name, start seek offset of the sound data, and length of the * sound data. The system sound objects are responsible for reading the * data. * * Note that we don't read the data into memory, because most systems * will want to stream sounds off of disk rather than loading them all * at once. Sound files tend to be quite large, so it's usually more * efficient to stream a sound off of disk as needed during playback * than to keep the entire file loaded throughout playback. */ class CHtmlSound { public: CHtmlSound() { seekpos_ = 0; filesize_ = 0; } ~CHtmlSound() { } /* * Set file information. This merely records the file information * for later use. */ void set_file(const textchar_t *fname, unsigned long seekpos, unsigned long filesize) { fname_.set(fname); seekpos_ = seekpos; filesize_ = filesize; } /* get the file information */ const textchar_t *get_fname() const { return fname_.get(); } unsigned long get_seekpos() const { return seekpos_; } unsigned long get_filesize() const { return filesize_; } protected: CStringBuf fname_; unsigned long seekpos_; unsigned long filesize_; }; /* * MIDI sound loader - trivial subclass of generic sound loader */ class CHtmlMidi: public CHtmlSound { public: CHtmlMidi() { } /* read a MIDI file */ int read_midi_file(const textchar_t *fname, unsigned long seekpos, unsigned long filesize) { /* remember the file information */ set_file(fname, seekpos, filesize); return 0; } }; /* * WAV sound loader - trivial subclass of generic sound loader */ class CHtmlWav: public CHtmlSound { public: CHtmlWav() { } /* read a WAV file */ int read_wav_file(const textchar_t *fname, unsigned long seekpos, unsigned long filesize) { /* remember the file information */ set_file(fname, seekpos, filesize); return 0; } }; /* * MPEG 2.0 audio sound loader - trivial subclass of generic sound * loader */ class CHtmlMpeg: public CHtmlSound { public: CHtmlMpeg() { } /* read an MPEG file */ int read_mpeg_file(const textchar_t *fname, unsigned long seekpos, unsigned long filesize) { /* remember the file information */ set_file(fname, seekpos, filesize); return 0; } }; /* * Ogg Vorbis sound loader */ class CHtmlOgg: public CHtmlSound { public: CHtmlOgg() { } /* read an .ogg file */ int read_ogg_file(const textchar_t *fname, unsigned long seekpos, unsigned long filesize) { /* remember the file information */ set_file(fname, seekpos, filesize); return 0; } }; #endif /* HTMLSND_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmlsys.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000003453�12650170723�0016674�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/html/htmlsys.cpp,v 1.2 1999/05/17 02:52:22 MJRoberts Exp $"; #endif /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlsys.cpp - system class definitions - portable interfaces Function Notes Modified 09/15/97 MJRoberts - Creation */ #include <string.h> #include <memory.h> #ifndef HTMLSYS_H #include "htmlsys.h" #endif #ifndef HTMLFMT_H #include "htmlfmt.h" #endif /* ------------------------------------------------------------------------ */ /* * font description */ /* * copy a font description into this font description */ void CHtmlFontDesc::copy_from(const CHtmlFontDesc *src) { memcpy(this, src, sizeof(*src)); #if 0 // $$$ no longer necessary - just copy the bytes now /* copy the members */ pointsize = src->pointsize; weight = src->weight; italic = src->italic; underline = src->underline; strikeout = src->strikeout; default_color = src->default_color; default_bgcolor = src->default_bgcolor; color = src->color; bgcolor = src->bgcolor; memcpy(face, src->face, sizeof(face)); fixed_pitch = src->fixed_pitch; serif = src->serif; htmlsize = src->htmlsize; superscript = src->superscript; subscript = src->subscript; pe_big = src->pe_big; pe_small = src->pe_small; pe_em = src->pe_em; pe_strong = src->pe_strong; pe_dfn = src->pe_dfn; pe_code = src->pe_code; pe_samp = src->pe_samp; pe_kbd = src->pe_kbd; pe_var = src->pe_var; pe_cite = src->pe_cite; pe_address = src->pe_address; charset = src->charset; default_charset = src->default_charset; #endif } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmlsys.h����������������������������������������������������������������������0000664�0000000�0000000�00000363405�12650170723�0016347�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/html/htmlsys.h,v 1.4 1999/07/11 00:46:40 MJRoberts Exp $ */ /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlsys.h - HTML system API interface Function These classes define a set of system-dependent functions that the HTML formatter needs to do its work. These classes must be implemented separately for each operating and/or window system. The purpose of these classes is to provide a portable interface to these system- dependent functions, so that the HTML formatter doesn't need to be modified for each new operating system that it's ported to. These classes isolate the system-dependent code so that it can be easily found for porting purposes. Note that these classes are designed as abstract interfaces. The reason for this design is to allow these classes to be easily used with an existing C++ application framework. To use the classes from an application framework to implement the window object interface (CHtmlSysWin), for example, simply create a subclass of the appropriate window class from your framework, and make your subclass multiply inherit from CHtmlSysWin as well as from the framework class. Because CHtmlSysWin is an abstract interface class, it should always be easy to use it in a multiple inheritance design without worrying about any of the problematic aspects of multiple inheritance (such as virtual base classes). Notes Modified 09/07/97 MJRoberts - Creation */ #ifndef HTMLSYS_H #define HTMLSYS_H #include <stdarg.h> #include <time.h> #ifndef HTML_OS_H #include "html_os.h" #endif #ifndef TADSHTML_H #include "tadshtml.h" #endif /* ------------------------------------------------------------------------ */ /* * Font description. This is used to specify the characteristics of a * desired font to a window, so that the window can create an * appropriate system font appropriate for the description. * * The oshtml_charset_id_t type is a system-defined type that specifies * the character set to be used for the font. On systems such as * Windows where fonts are mapped to particular code pages, we use this * to select a font that contains the code page mapping that we need for * each text string. * * Note that this is a portable concrete class - there is no need to * subclass this class per platform. This class is defined to provide a * portable means of describing a font, so that an appropriate system * font object can be created based on a description generated by * portable code. */ const int HTMLFONTDESC_FACE_MAX = 128; class CHtmlFontDesc { public: CHtmlFontDesc() { face[0] = '\0'; face_set_explicitly = FALSE; pointsize = 0; default_color = TRUE; default_bgcolor = TRUE; color = 0; bgcolor = 0; weight = 400; italic = FALSE; underline = FALSE; strikeout = FALSE; fixed_pitch = FALSE; serif = TRUE; htmlsize = 0; superscript = subscript = FALSE; pe_big = pe_small = FALSE; pe_em = pe_strong = pe_dfn = pe_code = pe_samp = pe_kbd = pe_var = pe_cite = pe_address = FALSE; default_charset = TRUE; } void copy_from(const CHtmlFontDesc *src); /* * Size of the font in points. Note that we will ignore the * pointsize if the htmlsize member is set to a non-zero value. */ int pointsize; /* * Weight, on a scale of 0 to 1000. The weights available for a * particular font vary, but by convention the following weights * apply to most faces: 200 is light; 400 is normal or book weight; * 700 is bold; 900 is black (ultrabold). */ int weight; /* italic, underline, strike-out */ int italic : 1; int underline : 1; int strikeout : 1; /* * Color. If default_color is TRUE, we'll ignore this and use an * appropriate default text color instead. * * Note that font descriptors only contain RGB values. Special color * ID values (the HTML_COLOR_xxx parameterized color IDs) are not used * in font descriptors. */ int default_color : 1; HTML_color_t color; /* * Background color. If default_bgcolor is TRUE, we'll ignore this and * draw text transparently over the existing background. * * Note that font descriptors only contain RGB values. Special color * ID values (the HTML_COLOR_xxx parameterized color IDs) are not used * in font descriptors. */ int default_bgcolor : 1; HTML_color_t bgcolor; /* * Face name. If the face name is provided, we'll use the face to * choose a font and ignore the characteristics. If the face name * is left empty, we'll use the characteristics to choose a font. * Multiple face names can be specified by separating the names with * commas; the first face that is available on the machine at * run-time is used. * * Note that the following parameterized font names can be used. * These names must be mapped to an appropriate actual font on each * system. The purpose of these names is to allow games more * control over choosing a specific style of font while retaining * portability and also giving the player more control of the * presentation. When possible, the player should be allowed to * select the actual system font to be used for each of these fonts * through a preferences mechanism. Note that these parameterized * font names are mapped to actual system fonts by * CHtmlSysWin::get_font(). * * TADS-serif - a serifed font, usually proportional *. TADS-sans - a san serif font, usually proportional *. TADS-script - a script or cursive font, usually proportional *. TADS-typewriter - a typewriter-style font, usually fixed-width *. TADS-input - the font to use for player commands */ textchar_t face[HTMLFONTDESC_FACE_MAX]; /* * The parameterized font names. Note that, as with tag and attribute * names, these names are to be parsed WITHOUT case sensitivity. */ #define HTMLFONT_TADS_SERIF "TADS-serif" #define HTMLFONT_TADS_SANS "TADS-sans" #define HTMLFONT_TADS_SCRIPT "TADS-script" #define HTMLFONT_TADS_TYPEWRITER "TADS-typewriter" #define HTMLFONT_TADS_INPUT "TADS-input" /* * Flag: the face was set explicitly in the descriptor. This is set * in cases where the face name is being explicitly changed, rather * than inherited from an enclosing block of text. When this is set * to true, if the face is a parameterized font name (such as * "TADS-input"), and the parameterized font carries other attributes * such as color and italics settings, we will apply the other * attributes. When this is set to false, we'll use the parameterized * face name, but we'll otherwise ignore the extra baggage carried * with the face, if any. * * For example, suppose that we're formatting along in a string of * text, and we want to change the text color to red for a bit of * text, but we want to continue using the other attributes of the * text up to this point; in such a case, we'd copy the font * descriptor currently in use, so we'd fill in the current face name, * but we'd leave this flag set to false to indicate that we don't * want any of the baggage associated with the font if it's a * parameterized font. */ int face_set_explicitly : 1; /* * Characteristics. In the absence of an explicit face name, we * can use these characteristics to choose a font suitable for a * particular HTML usage. If the face name is provided, these * characteristics are ignored. */ /* true -> fixed pitch, false -> variable pitch */ int fixed_pitch : 1; /* true -> serif face, false -> sans serif face */ int serif : 1; /* * HTML size, on a scale of 1 to 7. If this value is zero, the * pointsize member will be used instead. */ int htmlsize; /* superscript/subscript (mutually exclusive) */ int superscript : 1; int subscript : 1; /* HTML BIG/SMALL settings (mutually exclusive) */ int pe_big : 1; int pe_small : 1; /* HTML phrase/block elements that select a font */ int pe_em : 1; int pe_strong : 1; int pe_dfn : 1; int pe_code : 1; int pe_samp : 1; int pe_kbd : 1; int pe_var : 1; int pe_cite : 1; int pe_address : 1; /* * Character set identifier; this is system-dependent, and is opaque * to the generic code. Character set identifiers can be obtained * from the system (for example, via the CHtmlSysWinGroup method * get_default_win_charset()), and can then be passed back to the * system when, for example, creating a font. * * When a font descriptor is used to create a font object, * 'default_charset' can be set to TRUE to use the default system * character set. In this case, 'charset' is ignored. * * Once a font object is created, the system must set 'charset' to a * valid character set if 'default_charset' is specified in the * creation descriptor. This allows a caller to obtain the actual * character set for a font object, even if the font was created with * the default flag (and thus no valid 'charset' data). */ oshtml_charset_id_t charset; int default_charset; }; /* ------------------------------------------------------------------------ */ /* * System font object. This is an abstract interface; each system must * have its own implementation of the interface. * * Note that instances of (concrete subclasses of) CHtmlSysFont are created * by the system window code. Refer to CHtmlSysWin::get_font(). * * A note on memory management: The portable code never deletes font * objects. Instead, the system code is expected to keep a list of all of * the font objects ever created. Each time a caller asks to create a new * font via CHtmlSysWin::get_font(), the system code first scans the list * to see if the same font (with the identical descriptor) has been * allocated already; if so, it simply returns the existing CHtmlSysFont * instance. If not, it creates a new CHtmlSysFont instance, adds it to * the list, and returns the new instance. * * So, even though the portable code never deletes font objects, it won't * leak memory as long as the system code uses this cache list approach. * Font memory will never shrink, but it will only grow up to a fixed upper * limit - specifically, the maximum font memory will be the amount needed * to hold one instance of each distinct font used in the game. * * The reasoning behind this approach is that (a) a given game has a * relatively small number of distinct fonts it uses, (b) each font tends * to be used over and over many times, and (c) on many systems it's a * relatively heavy operation to create a system font resource. So it's * more efficient to use this cache approach than to create and destroy * font objects on each use. Fonts objects would also be fairly complex to * account for, so this scheme avoids the need for reference counting or * other means to avoid font memory leaks. */ class CHtmlSysFont { public: virtual ~CHtmlSysFont() { } /* get the metrics for the font */ virtual void get_font_metrics(class CHtmlFontMetrics *) = 0; /* * Is this a fixed-pitch font? This indicates whether or not the * actual system font that this object represents is monospaced. For * efficiency, implementations should (if possible) note the font's * type upon instantiation of this object, and simply return the * remembered information here. */ virtual int is_fixed_pitch() = 0; /* * Get the "em" size of the font, expressed in pixels. On many * systems, a font's em size is a design property of the font, stored * with the font's OS-level resource (or equivalent) information. * * For systems where the em size isn't stored with the font, * implementations should simply return the nominal point size of the * font translated to a pixel size. In traditional typography, a * font's em size is the same as the font's nominal height. * * For efficiency, implementations should (if possible) note the em * size upon instantiation of this object, and simply return the stored * size information here. */ virtual int get_em_size() = 0; /* * Get the description of the font (or a pointer to it). This is the * concrete description, which isn't necessarily identical to the * descriptor that was used to create the font, since the font creation * process might have resolved parameterized font names (TADS-Input, * TADS-Sans, etc) and filled in concrete values for defaults. */ void get_font_desc(class CHtmlFontDesc *dst) const { dst->copy_from(&desc_); } const CHtmlFontDesc *get_font_desc_ref() const { return &desc_; } /* * Get the color, and whether this color should be used (if not, an * appropriate default color should be used instead). */ HTML_color_t get_font_color() const { return desc_.color; } int use_font_color() const { return !desc_.default_color; } /* get the character set of the font */ oshtml_charset_id_t get_charset() const { return desc_.charset; } /* * Get the background color, and whether this color should be used. If * the background color is not to be used, the text should be displayed * over the existing background. If the background color is to be * used, then the background color should fill in the bounding * rectangle of the text. */ HTML_color_t get_font_bgcolor() const { return desc_.bgcolor; } int use_font_bgcolor() const { return !desc_.default_bgcolor; } protected: /* * the font descriptor - the system must set this to the font * descriptor when the font is created */ CHtmlFontDesc desc_; }; /* * Font metrics (portable class; this is used to parameterize certain * functions that measure text displays). * * The 'ascent height' is the distance between the TOP of the box * containing any character in the font and the BASELINE. The baseline is * defined by the font's designer, but in general it's the alignment point * for the bottoms of the capital letters and of the minuscules that don't * have tails (tails: g, j, p, q, y - the bits that drop below the * baseline). The reason the ascent height is important is that it tells * us where the baseline is relative to the overall rectangle containing a * character in the font, and the baseline is important because it's the * reference point for aligning adjacent text from different fonts. * * The 'descent height' is the height below the baseline. * * The total height is the sum of the ascender and descender heights. */ class CHtmlFontMetrics { public: int ascender_height; /* height in pixels above baseline */ int descender_height; /* height in pixels below baseline */ int total_height; /* total height in pixels */ }; /* ------------------------------------------------------------------------ */ /* * Enumeration status code return values for status functions. These are * not currently used in the portable interface, but are included here for * possible future use, and can be used within the platform-specific * implementation if convenient. * * These are obsolescent - the corresponding OS_EVT_xxx status codes * should be used instead. */ enum htmlsys_input_stat_t { /* input successfully read */ HTMLSYS_INPUT_STAT_OK, /* end of file, application terminated, or other error reading input */ HTMLSYS_INPUT_STAT_EOF, /* operation timed out - input interrupted before user pressed "Enter" */ HTMLSYS_INPUT_STAT_TIMEOUT }; /* ------------------------------------------------------------------------ */ /* * Banner position settings. Banners can be horizontal, in which case they * are above or below the main window and run across the whole width of the * main window; or vertical, in which case they are left or right of the * main window and run down its entire height. * * Multiple banners can be created with the same alignment, in which case * each new banner is aligned just inside of the previous banner. ("Inside * of" in the sense of relative to the boundaries of the outer frame * window.) * * For example, the first top-aligned banner goes at the top of the main * window, with the second top-aligned banner aligned immediately below it, * and the input window immediately below that. Similarly, the first * right-aligned banner is aligned at the right edge of the main window, * the second right-aligned banner is just to the left of the first, and * the main window is just to the left of those. * * Every window is always rectangular. To determine the positioning of the * banners, start off with the input window equal in size to the overall * main window. For each banner, divide the input window's area according * to the type of banner and its size. For example, for a bottom-aligned * banner, divide the main window into two horizontal bands -- one for the * banner and one for the remaining input window -- each the full width of * the input window; the banner is given an amount of vertical space * determined by its height, and the remainder is left for the new input * window area. Shrink the input window to the new size left over after * removing the new banner's area from the original input window area. Now * apply the same process to each subsequent banner window. * * Note that these codes are mapped directly to the corresponding * OS_BANNER_ALIGN_xxx codes, to simplify using our banner window system as * an implementation of the tads 2 os_banner interface. */ enum HTML_BannerWin_Pos_t { /* * Default alignment - the banner is positioned at the top of the main * window, and runs across the whole width of the main window */ HTML_BANNERWIN_POS_TOP = OS_BANNER_ALIGN_TOP, /* banner is at the bottom of the main input window */ HTML_BANNERWIN_POS_BOTTOM = OS_BANNER_ALIGN_BOTTOM, /* * The banner divides the main window vertically, and is aligned at the * left edge of the main window. The banner runs down the entire * height of the main window. */ HTML_BANNERWIN_POS_LEFT = OS_BANNER_ALIGN_LEFT, /* vertical banner aligned at the right edge of the main window */ HTML_BANNERWIN_POS_RIGHT = OS_BANNER_ALIGN_RIGHT }; /* * Banner window size units. These are used to specify the interpretation * of the size value when a size is specified. */ enum HTML_BannerWin_Units_t { /* * Percentage size. The size is given as a percentage of the REMAINING * space available in the parent window. */ HTML_BANNERWIN_UNITS_PCT, /* * "Character" size. The size is given in rows or columns of * characters. For a window with proportionally-spaced text, or * varying text sizes, this is in terms of the size of a '0' character * in the window's default font. */ HTML_BANNERWIN_UNITS_CHARS, /* * Pixel size. The size is given in pixels. */ HTML_BANNERWIN_UNITS_PIX }; /* * Banner window type codes. When a banner window is created, one of these * type codes will be passed to the system code to let it know what's going * on in the window. */ enum HTML_BannerWin_Type_t { /* * <BANNER> tag window. This type of window is created for an in-line * <BANNER> tag in the main window's HTML. This type of window is * based on contents from the same output stream as the main game * window. */ HTML_BANNERWIN_BANNERTAG, /* * Text stream window, created programmatically (such as through the * os_banner API). This type of window has an independent output * stream from the main game window, but otherwise behaves pretty much * the same as a <BANNER> tag window. */ HTML_BANNERWIN_TEXT, /* * Text grid window. This type of window doesn't have an HTML parser, * although it does use the same list of display items (CHtmlDisp * objects) as any other window, so the OS window implementation * doesn't usually have to do anything extra to support this. */ HTML_BANNERWIN_TEXTGRID }; /* ------------------------------------------------------------------------ */ /* * System application frame interface. This interface must be implemented * by some object in the system-dependent layer to provide the interface * that the HTML version of the TADS display OS interface layer uses to * operate the HTML display. * * This interface can be implemented by the main system window object * (i.e., the same C++ object that implements the CHtmlSysWin interface), * if desired; alternatively, it can be implemented by another object, such * as the main application object (if there is one in the system-dependent * implementation). * * Note: the system-dependent code is responsible for creating a singleton * CHtmlSysFrame object. After creating the object, the system code must * call CHtmlSysFrame::set_frame_obj() to tell the oshtml layer about the * object. Similarly, the system code must call * CHtmlSysFrame::set_frame_obj(0) when the frame object is about to be * deleted, to ensure that the portable code doesn't attempt to make any * calls to it after it's gone. */ class CHtmlSysFrame { public: virtual ~CHtmlSysFrame() {} /* * Set the system frame object. * * During initialization, the system-specific code must call this * routine to initialize the frame object as soon as the frame object * has been created. (The system-specific code is responsible for * creating the frame object in the first place, too.) * * During termination, the system code should call this to set the * frame object to a null pointer so we know it's forgotten. */ static void set_frame_obj(CHtmlSysFrame *frame) { app_frame_ = frame; } /* get the app frame object */ static CHtmlSysFrame *get_frame_obj() { return app_frame_; } /* * Check for end-of-file on the console. Returns true on EOF, false if * not. * * End-of-file in this context means that no more user input events are * forthcoming. In a graphical environment, this happens when the user * closes the main UI window, selects a Quit menu command, or takes * some similar action that's intended to terminate the session. * * Console EOF detection is important because TADS doesn't immediately * terminate the byte code program when the user closes the UI. The * byte code program might have work to perform before it exits, such * as saving current state for later restoration, so TADS allows it to * continue running even after the UI has been closed. However, if the * program makes any calls to read input from the console after the UI * has been closed, the input reader functions must immediately return * an EOF indication. That's where this method comes in: the input * reader functions call this to check for EOF, and immediately return * an EOF indication to their callers if this method returns true. */ static int eof_on_console(); /* * Kill the current process. This is a static function that must be * implemented by the port-specific code. * * This is called when the input layer (in oshtml.cpp) finds that the * program is looping on input after the console has been closed. This * routine should force the program to terminate. The simplest way to * implement this is usually via an OS API that explicitly terminates * the current process. If that's not possible, an alternative might * be to use longjmp() to jump to a setjmp() point that's initialized * in the OS-specific main startup code, allowing the program to jump * out of any nested loops and return from its main entrypoint. The * process termination doesn't need to be particularly graceful, since * the whole point is to terminate an errant byte code program, so it's * fine to use a heavy-handed approach that unceremoniously kills the * whole process. * * As explained above in the eof_on_console() notes, TADS allows a byte * code program to continue running after the UI has been closed. If * the program makes any calls to read input from the console after the * UI has closed, the input functions will return EOF errors. Byte * code programs are meant to recognize these errors and handle them * gracefully, by performing any final tasks they need to perform and * then exiting. However, it's possible to write errant programs that * ignore the EOF errors and continue polling for input indefinitely. * (Examples of TADS 2 programs that do this actually exist in the * wild, in part because the EOF condition wasn't well defined in TADS * 2 releases that predated HTML TADS. But since it's up to the byte * code program to properly interpret and handle EOF errors, it's * readily possible to write programs that don't exit properly on EOF * even with the most recent versions of the system.) This method is * part of a mechanism that's designed to detect and handle such * programs. The input reader functions (in oshtml.cpp) monitor for * repeated calls after an EOF condition occurs; if the byte code * program makes a large number of input calls that result in EOF * errors, the input readers assume that the program is stuck in an * infinite loop as a consequence of ignoring or mishandling EOF * errors, and explicitly terminate the program by calling this method. */ static void kill_process(); /* * Flush the text output buffer. * * If 'fmt' is true, format the new text and add it to the display; * otherwise, simply parse it. * * If 'immediate_redraw' is true, the contents of the window should be * updated on the display immediately if possible. This is used to * ensure that the display is updated before a possibly lengthy * computation; on event-driven operating systems, we might not be * able to process any redraw events during the computation, so we * want to ensure that any necessary drawing is done before the * computation begins to avoid the appearance that the program is * frozen during the delay. */ virtual void flush_txtbuf(int fmt, int immediate_redraw) = 0; /* get the parser associated with the main window */ virtual class CHtmlParser *get_parser() = 0; /* * Start a new page. This clears out the main text window, including * any banner windows created with <BANNER> tags in the main text * window. * * Note that this should NOT remove banner windows created through the * os_banner API - it should ONLY remove <BANNER> tag windows. Most * implementations will simply call remove_all_banners() on the main * text window's CHtmlFormatter object; this will automatically remove * the <BANNER> tag windows and preserve any os_banner API windows. */ virtual void start_new_page() = 0; /* * Set non-stop mode. The system window must handle MORE prompting, so * it will need to keep track of this non-stop mode setting. */ virtual void set_nonstop_mode(int flag) = 0; /* * Display the string of the given byte length. Note that because an * explicit length is given, the string might not be null-terminated. */ virtual void display_output(const textchar_t *buf, size_t len) = 0; /* * Check for a break key sequence. Returns true if the * (system-defined) break key has been hit, false if not. */ virtual int check_break_key() = 0; /* * Read a keyboard command. Returns false if the application is * quitting, true otherwise. */ virtual int get_input(textchar_t *buf, size_t bufsiz) = 0; /* * Read a keyboard command, with an optional timeout. This is * essentially an implementation of os_gets_timeout() and behaves in * essentially the same way; refer to osifc.h for full details on that * routine. * * Returns an OS_EVT_xxx code as follows: * * OS_EVT_LINE - we successfully read a line of input, terminated by * the user pressing Return (or the local equivalent). If the user * entered data in some other fashion that would normally work in * get_input(), such as clicking on a hyperlink or selecting a command * from a menu, this routine still returns OS_EVT_LINE; we are not * interested in any arbitrary events, but only complete input lines. * The buffer should be filled in with the line of text the user * entered. * * OS_EVT_EOF - an error occurred, or the application is being * terminated. * * OS_EVT_TIMEOUT - the timeout expired without the user having * completed input. The buffer should be filled in with the text the * user entered before the timeout expired. Everything should be left * as-is in the user interface, in case we resume editing the same * input with no intervening operations. This routine must statically * store its complete editing state, including the text being edited, * caret position, selected text range, insert/overwrite mode, and * anything else that is significant, so that the routine can restore * the same editing state seamlessly if the routine is invoked * subsequently without an intervening call to get_input_cancel(). * * Note that the timeout is optional. If use_timeout is FALSE, the * routine should behave roughly the same as the regular get_input(), * except that this routine should still resume the previously * interrupted editing session if there is one. */ virtual int get_input_timeout(textchar_t *buf, size_t buflen, unsigned long timeout, int use_timeout) = 0; /* * Cancel input that was interrupted by a timeout. This routine * terminates the input operation that was interrupted, making any * associated display changes (such as moving the caret to a new line * and removing the selected text range) that would normally be made * when editing is finished on a line of text. * * If 'reset' is true, it means that we are to forget all of our * static editing state from the interrupted editing, so that the next * call to get_input_timeout() starts with an empty line of text. If * 'reset' is false, then the next call to get_input_timeout() will * resume the previous editing session. * * (The distinction between "canceling" and "resetting" is subtle. * Canceling simply finishes the previous editing in terms of the user * interface state, so that new display operations can be performed; * the effect is the same as though the user had pressed Return to * terminate the input that timed out, except that we don't forget the * input. After canceling, when we call get_input_timeout(), we * re-display the saved input from the interrupted session, and resume * the session where we left off, but with a freshly-displayed copy of * the command line on the screen. Resetting does everything that * canceling does, but in addition forgets all of the state from the * interrupted editing session, so that the next get_input_timeout() * simply starts from scratch with an empty input buffer.) * * This routine MUST be called before any display operation (input or * output) can be performed after get_input_timeout() returns * OS_EVT_TIMEOUT, *except* that os_input_timeout() can be called * again to resume the interrupted session. */ virtual void get_input_cancel(int reset) = 0; /* * Read an input event. This routine follows the semantics of the * TADS os_get_event() interface, and returns one of the OS_EVT_xxx * event types. */ virtual int get_input_event(unsigned long timeout_in_milliseconds, int use_timeout, os_event_info_t *info) = 0; /* * Wait for a keystroke from the main window. Returns the character * corresponding to the keystroke, if it's a "normal" (i.e., ascii) * character, zero if it's a system-specific keystroke, such as a * function key. If 'pause_only' is true, it means that the caller * doesn't actually care about the keystroke returned, but is asking * for a keystroke purely for the purpose of pausing until the user * acknowledges a message by hitting a key; the window may want to * display an appropriate status line message or provide a similar * indication that we're waiting for a player keystroke. */ virtual textchar_t wait_for_keystroke(int pause_only) = 0; /* * Pause before exiting. Wait for a keystroke. This is similar to * wait_for_keystroke(); it's a separate routine in case the window * wants to display something to notify the user that the program * will exit on the next keystroke, such as a status line message to * this effect. */ virtual void pause_for_exit() = 0; /* * Display the MORE prompt, wait for the user to respond, and remove * the MORE prompt. */ virtual void pause_for_more() = 0; /* * Display a message to the debug log window. The debug window is * optional, so these functions can have empty implementations. The * debug window is used to display internal HTML TADS status * messages when the system is compiled with the macro * TADSHTML_DEBUG; in addition, it's used to display HTML error * messages that game authors may find helpful in debugging their * games. */ virtual void dbg_print(const char *msg) = 0; /* * Create a banner window. Return null if this is not possible. * * 'parent' is the parent window. If this is null, then the new * banner is a child of the main game window (the text window * containing the main game transcript). Otherwise, 'parent' is an * existing banner subwindow; the new window is carved out of the * space of the parent. * * 'where' specifies where the banner goes in the list of children of * the given parent, which determines how the space is divided among * the windows. This is OS_BANNER_FIRST to make the new banner window * the first child of its parent, OS_BANNER_LAST to make it the last * child, OS_BANNER_BEFORE to put it immediately before the banner * window specified by 'other', or OS_BANNER_AFTER to put it * immediately after 'other'. When BEFORE or AFTER are specified, * 'other' must be an existing child of the given parent; if 'other' * isn't a child of the parent, then the routine should behave as * though OS_BANNER_LAST were specified. * * 'pos' gives the alignment type of the banner. * * 'style' is a combination of OS_BANNER_STYLE_xxx flags, as defined in * osifc.h (from the tads 2 sources). */ virtual class CHtmlSysWin *create_banner_window(class CHtmlSysWin *parent, HTML_BannerWin_Type_t window_type, class CHtmlFormatter *formatter, int where, class CHtmlSysWin *other, HTML_BannerWin_Pos_t pos, unsigned long style) = 0; /* * "Orphan" a banner window. This is simply an implementation of * os_banner_orphan() from the tads 2 osifc definition. * * A valid implementation is simply to call os_banner_delete(), * passing the formatter object as the banner handle. * * If the OS implementation wishes to keep orphaned banner windows on * the screen for a while after they're orphaned, it can take * ownership of the banner here, and delete the banner at leisure. */ virtual void orphan_banner_window( class CHtmlFormatterBannerExt *banner) = 0; /* * Create a window for an "about box." Return null if this is not * possible. If the system has a customary way of displaying an "about * box" (a dialog that displays information on the application, such as * its name, version, and copyright information), the window returned * should be used when the about box is needed. This window will be a * standard HTML display window, which the game will fill in with HTML * as it desires. * * Only one about box is needed for the entire application; if an about * box subwindow already exists, this routine should simply return the * existing window. Note that this about box is for information about * the *game*, so any menu item that accesses it should say something * like "About This Game" rather than "About HTML TADS." */ virtual CHtmlSysWin *create_aboutbox_window(class CHtmlFormatter *formatter) = 0; /* * Remove a banner subwindow. This call can be ignored if the window * doesn't support banners. The CHtmlSysWin object must be deleted by * this call, so the caller cannot refer to the window after this call * returns. */ virtual void remove_banner_window(CHtmlSysWin *win) = 0; /* * Look up an "embedded resource." This is a resource (an image file, * sound file, etc) that's bundled with the interpreter application * itself - not part of a game, but part of the interpreter system. * * The resource finder calls this routine to resolve any resource name * that can't be found in the game file (the .GAM or .T3 file). In * most cases, the system will want to adopt a local naming convention; * on Windows, for example, these names are all prefixed with "exe:", * but each system can use its own convention, as the only uses of * these resources are in the system-specific code to begin with. * * If the resource is found, copy the name of the OS file containing * the resource into the caller's buffer, set *seek_pos and *siz to the * seek offset within the file and the size (in bytes) of the * resource's data within the file, respectively, and return true. If * there is no such embedded resource, return false. * * This function is purely for the use of the system-specific * interpreter implementation. If the interpreter doesn't have any use * for embedded resources, this need not be implemented. This function * was originally created to allow HTML TADS on Windows to use an HTML * window for its "About" box - this routine is used to allow the HTML * in the About box to refer to a JPEG image file that's embedded in * the interpreter .EXE file. This function is called from the * portable code, but only in response to HTML text that's generated by * the platform-specific code - so if your platform doesn't generate * any such HTML source in its own internal dialogs or whatever, this * will never be called. */ virtual int get_exe_resource(const textchar_t *resname, size_t resnamelen, textchar_t *fname_buf, size_t fname_buf_len, unsigned long *seek_pos, unsigned long *siz) = 0; private: /* * The system frame is a singleton object, defined and managed by the * OS code, that implements the CHtmlSysFrame interface. The OS code * must create this object at startup and delete it at termination. */ static CHtmlSysFrame *app_frame_; }; /* ------------------------------------------------------------------------ */ /* * Window Group interface. This interface should be associated with the * object that contains the main HTML window and any banner subwindows. * * Depending on how the system code is designed, this interface could be * associated with the same object as the CHtmlSysWin interface for the * main HTML window, or could be associated with the entire application. * This interface is provided separately from either of those two * interfaces to allow for the possibility of an intermediate container. * (This is how the Windows implementation is designed - the * intermediate container object is a container window that provides a * single system window frame for the main HTML window and its * associated banner subwindows.) */ class CHtmlSysWinGroup { public: virtual ~CHtmlSysWinGroup() {} /* * Get the current default character set ID for the frame. This * will normally reflect the system-wide localization settings, * although on some systems, the application may wish to provide a * user preference to change the default character set, on an * application-wide basis or possibly even per window, depending on * system localization conventions. */ virtual oshtml_charset_id_t get_default_win_charset() const = 0; /* * Translate an HTML 4 character code point value to a local * character code point. This is used to map '&' entities, both * named and numeric, to local character set code points. The HTML * 4 character codes are the same as Unicode values. HTML TADS * attempts to support at least the ISO Latin-1 character set plus * the HTML 4 named symbol entity character set. * * If 'charset' is not null, this routine should fill in *charset * with the OS-specific character set ID to use for the character. * If 'charset' is null, the caller cannot change the character set, * so we must attempt to map the character to the current default * character set. If the character cannot be mapped (either because * no mapping is available at all, or because charset is null and * the character can't be mapped to the current default character * set), this routine should produce a suitable invalid character * value. * * If 'charset' is not null, 'changed_charset' should be non-null as * well. On return, we'll set *changed_charset to true if we * returned a non-default character set in *charset, false otherwise. * * The return value is the length in bytes of the result. This * routine is allowed to return more than a single byte to allow for * multi-character approximations; for example, a copyright symbol * could be approximated as "(c)" when the local character set * doesn't have a suitable character value. */ virtual size_t xlat_html4_entity(textchar_t *result, size_t result_size, unsigned int charval, oshtml_charset_id_t *charset, int *changed_charset) = 0; }; /* ------------------------------------------------------------------------ */ /* * System window object. This is an abstract interface; each system must * have its own implementation of the interface. * * Note that, for simplicity, the same system window interface is used for * regular HTML windows and HTML banner windows. * * Instances of (concrete subclasses of) CHtmlSysWin are created by system * code. The startup code (which is always system-specific) creates the * main game window, and CHtmlSysFrame::create_banner_window() creates * banner windows. */ /* * for inval_doc_coords, these values can be used for the .right and * .bottom settings to indicate that the window should be invalidated * all the way to the right or all the way to the bottom, respectively */ const long HTMLSYSWIN_MAX_RIGHT = -1; const long HTMLSYSWIN_MAX_BOTTOM = -1; /* * Bullet styles */ enum HTML_SysWin_Bullet_t { HTML_SYSWIN_BULLET_SQUARE, /* filled-in square */ HTML_SYSWIN_BULLET_CIRCLE, /* circle outline */ HTML_SYSWIN_BULLET_DISC, /* filled-in circle */ HTML_SYSWIN_BULLET_PLAIN /* plain - no bullet at all */ }; class CHtmlSysWin { public: CHtmlSysWin(class CHtmlFormatter *formatter) { formatter_ = formatter; } virtual ~CHtmlSysWin() { } /* get the formatter associated with the window */ class CHtmlFormatter *get_formatter() const { return formatter_; } /* * forget my formatter; since the container application owns the * formatter, it will have to delete it at some point, and can use this * routine to notify the window that the formatter is about to be * deleted */ void forget_formatter() { formatter_ = 0; } /* get the window group object for the group I'm associated with */ virtual class CHtmlSysWinGroup *get_win_group() = 0; /* * Receive notification that the contents of the window are being * cleared. The portable code calls this to let the system layer know * that it's clearing the window. * * The system code isn't required to do anything here; this is purely a * notification to let the system code take care of any necessary * internal bookkeeping. For example, a window might want to clear its * internal MORE mode counter here, if it uses one, to reflect the fact * that we're starting over with fresh content. */ virtual void notify_clear_contents() = 0; /* * Close the window. Returns true if the window successfully * closed, false if not; the window may not be closed, for example, * if the user cancelled a "save changes" dialog. If 'force' is true, * the window should be closed without asking the user any questions * (about saving work, for example). This should always return true * if 'force' is true. */ virtual int close_window(int force) = 0; /* get the current width of the HTML display area of the window */ virtual long get_disp_width() = 0; /* get the current height of the HTML display area of the window */ virtual long get_disp_height() = 0; /* get the number of pixels per inch */ virtual long get_pix_per_inch() = 0; /* * Get the bounds of a string rendered in the given font. Returns a * point, with x set to the width of the *string* as rendered, and y * set to the height of the *font*. The returned point's y value * should give the total height of the text, including ascenders and * descenders. If 'ascent' is not null, it should be filled in with * the portion of the height above the text baseline (i.e., the * ascender size for the font). * * The width returned should indicate the *positioning* width of the * text, which might not necessarily be the same as the bounding box of * the glyphs contained in the text. The difference is that the * positioning width tells us where the next piece of text will go if * we're drawing a string piecewise. For example, suppose we have a * string "ABCDEF", and we call draw_text() to render it at position * (0,0). Now suppose we have to redraw a portion of the string, say * the "CDE" part, to adjust the display for a visual change that * doesn't affect character positioning - the user selected the text * with the mouse, say. In this case, the portable code would call * measure_text() on the part *up to* the part we're redrawing - "AB" - * to figure out where to position the part we *are* redrawing - "CDE". * It would add the width returned from measure_text("AB") to the * original position (0,0) to figure out where to draw "CDE". So, it's * important that the width returned from measure_text("AB") properly * reflects the character positioning distance. On most modern systems * with proportional typefaces, the positioning width of a character * often differs from its rendering width, because a glyph can overhang * its positioning area and overlap the previous and next glyph areas. * System APIs usually therefore offer separate measurement functions * for the bounding box (the overall area occupied by all pixels in a * string) and the positioning box (the area used to calculate where * adjacent glyphs should be drawn). * * Note that the heights returned - both the returned y value giving * the overall box height, as well as the ascent height - must be the * DESIGN HEIGHTS for the font, NOT the actual height of the specific * set of characters in 'str'. The returned height is thus the * *maximum* of the individual heights of *all* of the characters in * the font. This DOESN'T mean that a port has to actually run through * all of the possible characters in a font and measure each one to * find the largest value - instead, you should be able to call a * simple OS API that gets this information directly. Virtually every * GUI font engine stores the design metrics as part of its internal * data about a font, and makes them available to apps via a function * that retrieves "font metrics" or something of the like. * * (The 'ascent height' is the distance between the TOP of the box * containing any character in the font and the BASELINE. The baseline * is defined by the font's designer, but in general it's the alignment * point for the bottoms of the capital letters and of the minuscules * that don't have tails (tails: g, j, p, q, y - the bits that drop * below the baseline). The reason the ascent height is important is * that it tells us where the baseline is relative to the overall * rectangle containing a character in the font, and the baseline is * important because it's the reference point for aligning adjacent * text from different fonts.) */ virtual CHtmlPoint measure_text(class CHtmlSysFont *font, const textchar_t *str, size_t len, int *ascent) = 0; /* * Get the size of the debugger source window control icon. This is * a small icon in the left margin of a line of text displayed in a * debugger source window; the icon is meant to show the current * status of the line (current line, breakpoint set on line, etc). * This routine is only needed if the platform implements the TADS * debugger; provide an empty implementation if you're not * implementing the debugger on your platform. */ virtual CHtmlPoint measure_dbgsrc_icon() = 0; /* * Determine how much of a given string can fit in a given width, * assuming that the text is all on one line. This routine is an * optimization that allows an operating system mechanism to be used * directly when we need to figure out how much text we can put on a * line. If the operating system doesn't provide a native mechanism * for determining this, this routine should scan the string using the * OS's text measuring mechanism. In such cases, an efficient means * should be used, such as a binary search through the string for the * appropriate length. If the entire string fits in the given width, * the length of the string should be returned. * * The return value is expressed in byte units, even for multi-byte * character sets. The system implementation must take care to handle * multi-byte characters properly, if the system supports MBCS's. */ virtual size_t get_max_chars_in_width(class CHtmlSysFont *font, const textchar_t *str, size_t len, long wid) = 0; /* * Draw text. The text is to be drawn with its upper left corner * at the given position. If hilite is true, the text should be * drawn with selection highlighting; otherwise, it should be drawn * normally. */ virtual void draw_text(int hilite, long x, long y, class CHtmlSysFont *font, const textchar_t *str, size_t len) = 0; /* * Draw typographical text space. This is related to draw_text(), but * rather than drawing text, this simply draws a space of the given * pixel width in the given font. The effect should be as though * draw_text() were called with a string consisting of a number of * spaces equal in total horizontal extent to the given pixel width * (note, however, that the width need not be an integral multiple of * the width of a single ordinary space, so it's not possible in all * cases to obtain the identical effect with draw_text). This can be * used for purposes such as drawing typographical spaces (spaces of * particular special sizes), or inserting extra space for flush * justification. * * Note that all of the same attributes of regular text drawing should * apply. Highlighting should work the same as with ordinary text, * and if the font would draw ordinary space characters with any sort * of decoration (such as underlines, strikethroughs, or overbars), * the same decorations should be applied here. * * The simplest and safest implementation of this function is as * follows. Measure the size of an ordinary space in the given font. * Start at the given x,y coordinates. With clipping to the full * height of the font and the given width, draw a space character. * Move right by the width of the space. Repeat until past the * desired drawing width. This approach will ensure that the space * drawn will have exactly the same appearance as ordinary spaces; the * iteration will ensure enough space is filled, and the clipping will * ensure that there is no drawing beyond the desired space. */ virtual void draw_text_space(int hilite, long x, long y, class CHtmlSysFont *font, long wid) = 0; /* draw a bullet */ virtual void draw_bullet(int hilite, long x, long y, class CHtmlSysFont *font, HTML_SysWin_Bullet_t style) = 0; /* * Draw a horizontal rule covering the given rectangle. If shade is * true, draw with 3D-style shading, otherwise draw as a solid bar. */ virtual void draw_hrule(const CHtmlRect *pos, int shade) = 0; /* * Draw a table or cell border. The border should be drawn inside * the given rectangle with the given width. If cell is true, this * is an individual cell border, otherwise it's the overall table's * border. */ virtual void draw_table_border(const CHtmlRect *pos, int width, int cell) = 0; /* * Draw a table or cell's background. This should simply fill in * the given area with the given color. */ virtual void draw_table_bkg(const CHtmlRect *pos, HTML_color_t bgcolor) = 0; /* * Draw a debugger source line icon. This is used only for the TADS * debugger; if this platform doesn't implement the debugger, this * routine can have an empty implementation. * * 'stat' gives a status code; this is a combination of the * HTMLTDB_STAT_xxx codes (see tadshtml.h). */ virtual void draw_dbgsrc_icon(const CHtmlRect *pos, unsigned int stat) = 0; /* * Do formatting -- run the formatter until it runs out of content * to format. This routine can be implemented simply to call the * formatter's formatting routine until the formatter reports that * it's out of tags to format; however, we provide this method in * the window class so that the window can optimize redrawing. This * routine should return true if it did any display updating, false * if not. If show_status is true, it indicates that the operation * may be lengthy, so a status line message, wait cursor, and/or * other indication of the busy status should be displayed; * otherwise the operation is expected to finish quickly enough that * no such display is desired. show_status should be set to true * when reformatting after operations that will reformat the entire * window, such as resizing the window. If update_win is true, we * should update the window as soon as possible (i.e., as soon as * we've formatted enough text to redraw the window); otherwise, we * should leave redrawing to the normal draw message handler. If * freeze_display is true, the formatter should be told not to * update the window until it's done; this is used when the whole * window needs to be reformatted, such as after a font preference * change, to avoid making the user watch the window's entire * contents scroll by as the reformatting is done. */ virtual int do_formatting(int show_status, int update_win, int freeze_display) = 0; /* -------------------------------------------------------------------- */ /* * Palette management. These routines are needed only if we're * using an index-based palette to display colors, and then only if * applications must manage the palette. For systems with high * color resolution (generally greater than 8-bit color) or * automatic color table management, these routines can have empty * implementations. */ /* * Recalculate the palette. The formatter calls this routine when * it adds a new image to the list of initial images, which are used * to determine the system palette. If the system is not using a * palette, this routine doesn't need to do anything. */ virtual void recalc_palette() = 0; /* * Determine if we need to use a palette. If this system is * currently using an index-based palette to select display colors, * this should return true, otherwise it should return false. Some * systems use an index-based palette when the display hardware is * showing 8 bits of color resolution or less (notably Windows). */ virtual int get_use_palette() = 0; /* -------------------------------------------------------------------- */ /* * Font management. The window always owns all of the font * objects, which means that it is responsible for tracking them and * deleting them when the window is destroyed. Fonts that are given * to a formatter must remain valid as long as the window is in * existence. Note that this doesn't mean that the font class can't * internally release system resources, if it's necessary on a given * system to minimize the number of system font resources in use * simultaneously -- the portable code can never access the actual * system resources directly, so the implementation of this * interface can manage the system resources internally as * appropriate. */ /* get the default font */ virtual class CHtmlSysFont *get_default_font() = 0; /* * Get a font suitable for the given characteristics. Note that this * routine is responsible for checking the font descriptor for the * following parameterized names, and providing the appropriate actual * system font when one of the parameterized names is used (the names * are case-insensitive): * * TADS-serif - a serifed font, usually proportional *. TADS-sans - a san serif font, usually proportional *. TADS-script - a script or cursive font, usually proportional *. TADS-typewriter - a typewriter-style font, usually fixed-width *. TADS-input - the font to use for player commands * * When possible, the player should be able to select the parameterized * fonts in a "preferences" dialog or similar mechanism. The purpose * of the parameterized fonts is to allow the game to specify the style * to use, while letting the player specify the exact details of the * presentation. * * Note that, on some systems, parameterized fonts might let the user * specify attributes in addition to the font face. For example, on * the Windows interpreter, "TADS-Input" lets the user specify the text * color and set boldface and italics. When these additional * attributes are part of a parameterized font name, they should be * applied ONLY when the 'face_set_explicitly' flag in the font_desc is * set to TRUE - this ensures that the attributes can be overridden by * nesting other attribute tags (<font color=red> or <i>, for example) * within the <font> tag that explicitly selects the font face. * * Note on memory management: the portable code never deletes a font * object obtained from this routine. Instead, this routine is * expected to keep a list of font objects previously allocated, and to * reuse an existing object if there's one in the list that matches the * descriptor. This ensures that the lack of deletion won't leak * memory: a given font object is never deleted, but only one font * object will ever be created for a given descriptor. */ virtual class CHtmlSysFont *get_font(const class CHtmlFontDesc *font_desc) = 0; /* * Get the font for drawing bullets, given a text font. This can * simply return the same font if there isn't a separate bullet font * or bullets are drawn independently of the font. The current font * is provided so that a suitable size can be selected in the bullet * font. */ virtual class CHtmlSysFont *get_bullet_font(class CHtmlSysFont *current_font) = 0; /* -------------------------------------------------------------------- */ /* * Timers */ /* * Register a timer callback routine. This routine should be called * with the given context data as its argument periodically. If * possible, the routine should be called about once per second, but * the precise interval isn't important; all that's important is to * call the function every so often so that the function can check * for pending work. Since the frequency of the callback invocation * is not tightly specified, the callback must check the current * time if it's time-sensitive. * * The callback should not be invoked via an interrupt or in a * separate thread; instead, it should be invoked in the course of * the window's normal event message dispatching mechanism. * * Note that any number of timer functions may be simultaneously * registered. The order in which such functions are called is * unspecified. */ virtual void register_timer_func(void (*timer_func)(void *), void *func_ctx) = 0; /* * Remove an timer callback function previously registered. */ virtual void unregister_timer_func(void (*timer_func)(void *), void *func_ctx) = 0; /* * Create a timer object that calls a given callback function when the * timer event occurs. This doesn't register a timer, but merely * creates a timer object that can be used in set_timer(). * * Returns a CHtmlSysTimer object that can be used to unregister the * timer. */ virtual class CHtmlSysTimer *create_timer(void (*timer_func)(void *), void *func_ctx) = 0; /* * Set a timer, previously created with create_timer(), to the given * interval in milliseconds. If 'repeat' is true, we'll invoke the * callback associated with the timer repeatedly at the given interval; * otherwise, we'll invoke the callback just once and then make the * timer inactive. */ virtual void set_timer(class CHtmlSysTimer *timer, long interval_ms, int repeat) = 0; /* * Cancel a timer. */ virtual void cancel_timer(class CHtmlSysTimer *timer) = 0; /* * Delete a timer previously created with register_timer_func_ms(). If * the timer is currently active, it is cancelled. */ virtual void delete_timer(class CHtmlSysTimer *timer) = 0; /* -------------------------------------------------------------------- */ /* * General HTML operations */ /* * Adjust the horizontal scrollbar. The formatter calls this whenever * a new line is formatted that exceeds the maximum line width so far. * This routine should adjust the horizontal scrollbar accordingly - * that is, it should update the range (min/max) of the OS-level * scrollbar control to match the new document width. * * Note that the routine should by default NOT perform any horizonal * scrolling here - it should simply update the min/max RANGE of the * scrollbar and leave the scrolling position unchanged. However, if * the window is a banner window with the style flag * OS_BANNER_STYLE_AUTO_HSCROLL set (via create_banner_window() or * set_banner_info()), then this routine should immediately scroll the * window horizontally to bring the right edge of the document into * view; the window should be invalidated and/or redrawn as needed, and * the scrollbar's thumb position should be updated accordingly. */ virtual void fmt_adjust_hscroll() = 0; /* * Adjust the vertical scrollbar. The formatter calls this whenever a * new line is added or the document otherwise grows vertically. This * routine should adjust the min/max range of the vertical scrollbar to * match the new document height. * * Note that this routine should normally NOT perform any vertical * scrolling - it should simply update the RANGE of the min/max * scrollbar and leave the current scrolling position unchanged. * However, if the window is a banner window with the style flag * OS_BANNER_STYLE_AUTO_VSCROLL set (via create_banner_window() or * set_banner_info()), then this routine should immediately scroll the * window vertically to bring the bottom edge of the document into * view; the window should be invalidated and/or redrawn as needed, and * the scrollbar's thumb position should be updated accordingly. * * MORE MODE: We leave it up to the port code to determine how to * handle MORE mode, if such a thing exists locally. In a regular * "main" window, or in a banner window with the style flag * OS_BANNER_STYLE_MOREMODE set, the port should implement MORE mode * according to local conventions. One possible implementation is to * do nothing special: since we presumably are showing the text in a * standard GUI window with a scrollbar, any text that spills beyond * the vertical extent of the window will simply be scrolled off the * bottom, and we can leave it up to the user to manually use the * scrollbar to see the text below the bottom of the window. * * In most cases, though, it's best to simulate the behavior of the * Unix "more" program, since (a) this is much more convenient for * users, and (b) it's what most users are accustomed to in a * terminal-style interface like ours. Here's roughly how this works. * Whenever fmt_adjust_vscroll() notifies the window that the document * has grown vertically beyond the current vertical extent of the * window, the window sets an internal flag saying "more mode is * pending." Whenever the program makes an input call (get_input(), * get_input_timeout(), etc), the window checks the "more mode pending" * flag, and if set, clears that flag and sets another one saying "more * mode is active." In the input routines, as long as the "more mode" * flag is set, we treat keyboard input specially: the SPACE key * scrolls down by a page, the RETURN key scrolls down by a "line" * (which is an essentially arbitrary unit, since we can have a mixture * of text heights in the window - the design height of the default or * current font is usually a good choice), the up/down arrow keys * scroll by a line one way or the other; assign other keys according * to local conventions or your preferences, but generally ignore keys * to which you haven't assigned a special more-mode meaning. When in * more mode, if at any time the vertical scroll position becomes such * that the bottom of the document is in view, cancel more mode and * switch to ordinary input mode. In ordinary input mode, you'd * normally use a different scrolling behavior: if the input caret * isn't visible, and the user presses any key that affects the input, * immediately jump directly to a scrolling position that brings the * caret into view. This allows the user to scroll up to look at old * text in the transcript, but then immediately jump back to the * command line just by starting to type some input. * * We leave all of this up to the port code, though, because we want to * give each port maximum flexibility in applying local customs. Some * ports might not even want a MORE mode at all, and some might use * different key mappings or different overall behaviors. Some ports * might provide a visual indication that MORE mode is active * (something like the "[More]" prompt that the Unix "more" command and * the TADS text-only interpreters display), while some might be * content to rely on the scrollbars as a sufficient visual cue. The * Windows interpreter uses something like the traditional "[More]" * prompt, but it has a couple of user-configurable options for how to * display it: it can be shown in the status bar along the bottom of * the window frame, or it can be displayed as an overlay within the * document area of the window itself, superimposed over the document's * own contents at the bottom of the window. (The status line approach * is a lot easier to implement.) */ virtual void fmt_adjust_vscroll() = 0; /* * Invalidate the window area given by the document coordinates (i.e., * before taking into account any scrolling). * * Note that the area->right and area->bottom coordinates can be passed * in as HTMLSYSWIN_MAX_RIGHT and/or HTMLSYSWIN_MAX_BOTTOM, defined * above - these indicate that you should invalidate all the way to the * right or bottom edge of the window. */ virtual void inval_doc_coords(const class CHtmlRect *area) = 0; /* * Receive notification that the formatter associated with this * window is about to reset the display list, deleting all existing * display list items. Any display list items that the window is * referencing should be forgotten. It's particularly likely that * the window is keeping track of a link display item, such as the * link that the mouse is hovering over. The window should simply * forget about any such display list items at this time. */ virtual void advise_clearing_disp_list() = 0; /* * Scroll a document position into view. This should try to scroll * the window so that the given coordinates appear at the middle of * the window. If possible, the entire rectangle should be within * the window. */ virtual void scroll_to_doc_coords(const class CHtmlRect *pos) = 0; /* * Get the current scrolling position in document coordinates. * Returns a value suitable for passing to scroll_to_doc_coords to * restore this same scrolling position at a later time. */ virtual void get_scroll_doc_coords(class CHtmlRect *pos) = 0; /* set the window's title */ virtual void set_window_title(const textchar_t *title) = 0; /* * Set the background color, text color, and link colors. If * use_default for a particular color is true, we'll ignore the * provided color and use the default color instead. These calls are * used to advise the system window implementation of the color * settings made programmatically, such as through the <BODY> tag. * * The "background" color is the color used to fill the background of * the window, where no text or graphics are explicitly drawn. * * The "text" color is the default color for ordinary text (i.e., text * that isn't hyperlinked or given an explicit font color via <FONT * COLOR=xxx> or the like). * * The "input" color is the color to use for input text. * * The "link" color is the color for hyperlinked text - that is, text * inside an <A HREF> tag. * * The "vlink" color is the "visited" hyperlink text color. We include * this for completeness, but currently we don't use it, since we don't * really have a sense of visited vs unvisited links in the same way * that a browser would. * * The "alink" color is the color for "active" hyperlink text. This is * the color for hyperlink text when the link is being clicked by the * mouse; that is, when the user has moved the mouse over the hyperlink * and then clicked and held down the button. This is the color to use * as long as the button is being held down. * * The "hlink" color is the "hover" hyperlink text color. This is the * color for when the mouse is hovering over the hyperlink, but the * mouse button isn't being held down. * * Note that the system implementation is free to ignore all of these * color settings, or to use them only if user preferences allow, or * under any other conditions suitable for the local system. On most * systems, there are four possible color sources for any given text * fragment: user preferences; set_html_xxx_color() settings; explicit * <FONT> and other settings; and OS defaults. The interpreter then * implements a hierarchy to determine which of these to use. The * exact hierarchy is up to the interpreter, but it's usually like * this: * * - If the user preferences allow the user to select an "override" * option, and the user selects this option, use the user preference * color setting applicable to a given text fragment. (The user * preferences might allow the user to select each of these color types * individually - text, background, input, alink, vlink, etc - in which * case the interpreter should pick the right one for the context. If * the preferences don't distinguish color by context, just use the * single color setting.) * * - If "override" isn't selected (or isn't offered), and there's a * <FONT COLOR=xxx> setting for a given text fragment, use that. * * - Otherwise, if there's been a call to set_html_xxx_color() for the * given context with use_default=FALSE, use that color. * * - If there's been no call to set_html_xxx_color() for the given * context, or there's been a call with use_default=TRUE, AND there's a * user preference setting applicable to the context, use the user * preference setting. * * - Otherwise, use the system defaults. */ virtual void set_html_bg_color(HTML_color_t color, int use_default) = 0; virtual void set_html_text_color(HTML_color_t color, int use_default) = 0; virtual void set_html_input_color(HTML_color_t clr, int use_default) = 0; virtual void set_html_link_colors(HTML_color_t link_color, int link_use_default, HTML_color_t vlink_color, int vlink_use_default, HTML_color_t alink_color, int alink_use_default, HTML_color_t hlink_color, int hlink_use_default) = 0; /* * Map a special parameterized color value to the corresponding RGB * value. The parameterized color values are the HTML_COLOR_xxx * values defined in tadshtml.h. The system should use an * appropriate mechanism for determining the colors; the system may * use fixed values, or may choose colors appropriate for the * current display device, or can let the user control these * settings through a preferences mechanism. */ virtual HTML_color_t map_system_color(HTML_color_t color) = 0; /* * Get the link colors. The link colors are stored in the window * object, so that the system-specific code can save, use, and * customize user preferences as needed. The colors are normal (link), * activated/clicked (alink), visited (vlink), and hovering (hlink). * These are the colors that are used to draw hyperlinked text in the * respective states. * * Note that these should return the colors appropriate for the current * system-specific preference settings. For example, if the user * preferences the user to select whether or not links are highlighted * on "hovering" with the mouse cursor, and hover highlighting is * currently turned off, then the hlink color should return the same as * the normal link color. */ virtual HTML_color_t get_html_link_color() const = 0; virtual HTML_color_t get_html_alink_color() const = 0; virtual HTML_color_t get_html_vlink_color() const = 0; virtual HTML_color_t get_html_hlink_color() const = 0; /* determine if textual links should be drawn underlined */ virtual int get_html_link_underline() const = 0; /* * Determine if links are to be shown. This should be offered as a * user-settable preference if possible. Returns true if links * should be shown as links, false if not. If links are not to be * shown as links, we'll draw links as ordinary text. */ virtual int get_html_show_links() const = 0; /* * Determine if graphics can be displayed. On systems where * graphics can be displayed, this should be offered as a * user-settable preference if possible. On systems where graphics * can't be displayed at all, this should always return false. If * this returns false, we'll render graphics using a suitable * textual substitute if possible, such as the ALT attribute of an * IMG tag, or simply leave any graphics out entirely. If this * returns true, we'll render graphics as normal. */ virtual int get_html_show_graphics() const = 0; /* * Set the background image. The image is an ordinary HTML resource * cache entry, whose get_image() method will return a CHtmlSysImage * object (or null if there's not a valid object associated with the * cache entry). * * In graphical implementations, the background image is normally drawn * as the background of the document, tiled to fill out the window * space. The image should scroll with the document. As the document * scrolls down, the image is normally repeatedly tiled vertically to * fill in all displayed space. The image is normally drawn out to the * visible edges of the window; no empty margin space is normally shown * between the interior edges of the window's frame and the image. */ virtual void set_html_bg_image(class CHtmlResCacheObject *image) = 0; /* * Invalidate a portion of the background image. This is called when * the background image is animated to trigger redrawing of the * portions of the image changed from one animation frame to the next. * * The coordinates given are relative to the upper left corner of the * background image itself. These are not in document or window * coordinates - they're in *image* coordinates. The portable caller * can't make any assumptions about the placement of the background * image relative to the window or document coordinate systems, so we * must rely on the window itself to figure out what needs to be * invalidated within the image. * * It's important to note that if the image is tiled, so that it's * drawn multiple times in the window, then *each visible tile* must be * invalidated here. If all of the visible tiles are not invalidated, * they would incorrectly remain un-updated - but they'd draw in their * new form eventually when the window is next updated by virtue of * having a portion uncovered. Therefore, it's crucial that this * routine properly invalidate all visible copies of the image. */ virtual void inval_html_bg_image(unsigned int x, unsigned int y, unsigned int wid, unsigned int ht) = 0; /* * Set the size of this window as a banner. A call to this routine * should be ignored for a main window. * * Note that normally only one of the dimensions is actually used. If * this is a horizontal banner, normally only the height is meaningful, * because horizontal banners are constrained to run across the entire * width of the main window. Similarly, only the width is meaningful * for vertical banners. * * If use_height is false, it means that the height parameter value * should be ignored and the current window height retained; likewise * use_width. */ virtual void set_banner_size( long width, HTML_BannerWin_Units_t width_units, int use_width, long height, HTML_BannerWin_Units_t height_units, int use_height) = 0; /* * Set alignment and style for this existing banner window. A call to * this routine should be ignored for a main window. * * 'style' is a combination of OS_BANNER_STYLE_xxx flags, as defined * in osifc.h (from tads 2). */ virtual void set_banner_info(HTML_BannerWin_Pos_t pos, unsigned long style) = 0; /* * Get information on this banner window. '*pos' is filled in with the * banner's alignment, and '*style' is filed in with a combination of * OS_BANNER_STYLE_xxx flags describing the styles actually provided by * the window. * * Note that the style flags returned might not be the same ones as * originally requested in CHtmlSysFrame::create_banner_window(), * because we might not support some styles that the caller requested, * and we might unconditionally use some styles not requested. The * style flags returned here describe the *actual* window, not the * requested one. */ virtual void get_banner_info(HTML_BannerWin_Pos_t *pos, unsigned long *style) = 0; protected: /* formatter for the window */ class CHtmlFormatter *formatter_; }; /* ------------------------------------------------------------------------ */ /* * Timer. This is an opaque object for the system window implementation's * use. */ class CHtmlSysTimer { public: CHtmlSysTimer(void (*func)(void *), void *ctx) { /* remember the function and its context */ func_ = func; func_ctx_ = ctx; /* presume we won't repeat */ repeat_ = FALSE; /* we're not yet active */ active_ = FALSE; } /* set repeat mode */ void set_repeating(int repeat) { repeat_ = repeat; } /* am I a repeating timer? */ int is_repeating() const { return repeat_; } /* invoke my function */ void invoke_callback() { (*func_)(func_ctx_); } /* get/set active status */ int is_active() const { return active_; } void set_active(int active) { active_ = active; } protected: /* function (and its context) to call upon timer expiration */ void (*func_)(void *ctx); void *func_ctx_; /* flag: the timer is active */ int active_; /* flag: the timer is to be repeated (as opposed to called just once) */ int repeat_; }; /* ------------------------------------------------------------------------ */ /* * System resource object interface. This is the base type for resources, * such as sounds and images. This interface is subclassed to provide the * specific resource types; the subclassed interfaces must in turn be * implemented by the non-portable system-dependent code. This interface * itself is not to be implemented by system code -- only the final * subclasses of this interface need to be implemented. * * We generally deal with resources at a generic level, since we can't tell * the type of an image until we've loaded it. However, when we use a * resource, we generally need to downcast the resource to a particular * "branch" of the resource taxonomy based on context; for example, when * we're attempting to draw a resource loaded from an <IMG> tag, we'll need * a CHtmlSysImage object. To accommodate the need to downcast resources * with type safety, this interface provides virtuals that cast to the * major resource type branches. Note that it should never be necessary to * downcast beyond the immediate subclasses of this interface, since any * further subclasses should be invisible to client code (for example, * CHtmlSysImage is sufficiently polymorphic that client code can do * everything it needs to do to an image without having to worry about * whether it's a PNG or JPEG image). The downcast methods return null for * resources that are not of the requested type. */ class CHtmlSysResource { public: virtual ~CHtmlSysResource() { } /* get the resource type */ virtual HTML_res_type_t get_res_type() const = 0; /* downcasts to major resource types */ virtual class CHtmlSysImage *cast_image() { return 0; } virtual class CHtmlSysSound *cast_sound() { return 0; } /* * Can the resource be kept in the resource cache and reused? This is * true for stateless resources, such as static pictures, and false for * stateful resources, such as animated images (MNG's, for example). * Stateful resources cannot be reused if they appear multiple times in * a document because each copy in the document must have a separate * resource instance to manage its state. */ virtual int is_cacheable() const { return TRUE; } }; /* ------------------------------------------------------------------------ */ /* * drawing modes - these specify how to draw an image into a rectangle * whose size differs from the size of the image */ enum htmlimg_draw_mode_t { /* * Draw the image at its exact size, aligning the upper left corner of * the image at the upper left corner of the drawing rectangle. Clip * the image at the right and bottom to fit the drawing area if the * image is too big. Leave any excess right and bottom margin * unaffected if the image is smaller than the drawing area. */ HTMLIMG_DRAW_CLIP, /* * Stretch the image to fit the drawing area, by expanding or shrinking * the image as needed. The image should be scaled as visually * smoothly as possible. */ HTMLIMG_DRAW_STRETCH, /* * Tile the image to fit the bounding area, starting at the upper left * corner and drawing it repeatedly without any space between adjacent * tiles. If the image is too large to fit the area, clip it to fit, * aligning the upper left corner of the image at the upper left * corner of the target rectangle. */ HTMLIMG_DRAW_TILE }; /* ------------------------------------------------------------------------ */ /* * System image object. This type of object allows us to display an * image. This interface must be subclassed for each image type (JPEG, * etc). This interface is not to be implemented by system code; only * the final subclasses that provide interfaces for the specific image * types need to be implemented. */ class CHtmlSysImage: public CHtmlSysResource { public: virtual ~CHtmlSysImage() { } /* this is an image - override the downcast method */ CHtmlSysImage *cast_image() { return this; } /* cast to an animated image */ virtual class CHtmlSysImageAnimated *cast_animated() { return 0; } /* * Set the display site. This is REQUIRED for animated images. For * non-animated images, this has no effect, but as a convenience, we * define the method as a virtual in the base image class so that an * image container can simply always set the site if it provides one. */ virtual void set_display_site(class CHtmlSysImageDisplaySite *) { } /* * Cancel playback. If the image is animated, this should halt the * animation. This has no effect for static images. */ virtual void cancel_playback() { } /* * Pause/resume playback. If the image is animated, this should pause * playback until 'resume' is called. This has no effect for static * images. */ virtual void pause_playback() { } virtual void resume_playback() { } /* * Display the image at a given position in a given window. The 'mode' * argument indicates what to do if the size of the image doesn't * exactly match the size of the rectangle we're drawing into: we can * clip, stretch, or tile the image. */ virtual void draw_image(class CHtmlSysWin *win, class CHtmlRect *pos, htmlimg_draw_mode_t mode) = 0; /* get the image's dimensions */ virtual unsigned long get_width() const = 0; virtual unsigned long get_height() const = 0; /* * Map the image's palette. If 'foreground' is true, the image's * palette should take over the system palette, if possible; * otherwise, the image's palette should be mapped as well as * possible into the existing system palette. Returns true if * anything changed in the system palette, false if not. For * systems that don't use palettes, this routine doesn't need to do * anything. */ virtual int map_palette(class CHtmlSysWin *win, int foreground) = 0; }; /* * System JPEG image object. */ class CHtmlSysImageJpeg: public CHtmlSysImage { public: virtual HTML_res_type_t get_res_type() const { return HTML_res_type_JPEG; } /* * Create a new system-specific JPEG object, loading the JPEG data * from the given seek offset in the given file. Note that this is * a static routine that must be implemented for this class in a * system-specific module. This routine should return a new * instance of the correct subclass of this class for the current * operating system. * * Note that 'filesize' is the size of the data starting at * 'seekpos' that the resource loader considers to be part of this * resource; the actual file may be larger than this value, since * other data may follow the resource in the file. * * 'url' is provided for diagnostic purposes; it should be used in * any error message that this routine generates (with * oshtml_dbg_printf). It can otherwise be ignored. * * The 'win' argument is provided so that the loader can check the * current environment for any information necessary for loading the * resource. For example, the loader may want to call * win->get_use_palette() to determine if the image's color map must * be mapped to a palette. */ static CHtmlSysResource *create_jpeg(const class CHtmlUrl *url, const textchar_t *filename, unsigned long seekpos, unsigned long filesize, class CHtmlSysWin *win); }; /* * System PNG image object */ class CHtmlSysImagePng: public CHtmlSysImage { public: virtual HTML_res_type_t get_res_type() const { return HTML_res_type_PNG; } /* * Create a new system-specific PNG object, loading the PNG data * from the given seek offset in the given file. Note that this is * a static routine that must be implemented for this class in a * system-specific module. This routine should return a new * instance of the correct subclass of this class for the current * operating system. * * Note that 'filesize' is the size of the data starting at * 'seekpos' that the resource loader considers to be part of this * resource; the actual file may be larger than this value, since * other data may follow the resource in the file. * * 'url' is provided for diagnostic purposes; it should be used in * any error message that this routine generates (with * oshtml_dbg_printf). It can otherwise be ignored. * * The 'win' argument is provided so that the loader can check the * current environment for any information necessary for loading the * resource. For example, the loader may want to call * win->get_use_palette() to determine if the image's color map must * be mapped to a palette. */ static CHtmlSysResource *create_png(const class CHtmlUrl *url, const textchar_t *filename, unsigned long seekpos, unsigned long filesize, class CHtmlSysWin *win); }; /* * System Animated Image object. This subclass of image is for animated * image types; these objects require special interaction with their * display site to manage timed playback. */ class CHtmlSysImageAnimated: public CHtmlSysImage { public: /* cast to an animated image */ virtual class CHtmlSysImageAnimated *cast_animated() { return this; } /* * Animated images are all non-shareable, because an animated image * resource must keep track of state information (i.e., where it is in * displaying the animation). */ virtual int is_cacheable() const { return FALSE; } /* * Receive notification from the display site of a timer event. The * display site must call this method after a given delay has occurred * after a request from the image made to the display site via the * display site interface (CHtmlSysImageDisplaySite). */ virtual void notify_timer() = 0; /* * Receive notification from the image helper (if we use one) that the * image has changed. This doesn't necessarily need to be implemented: * it's only needed if the system implementation of this class uses a * portable helper object, and the helper object can change the image * in response to a timer, and the system implementation of this class * has its own separate buffer that it must refresh from the helper * object in such cases. */ virtual void notify_image_change(int x, int y, int wid, int ht) = 0; }; /* * System MNG image object */ class CHtmlSysImageMng: public CHtmlSysImageAnimated { public: virtual HTML_res_type_t get_res_type() const { return HTML_res_type_MNG; } /* * Create a new system-specific MNG object, loading the MNG data from * the given seek offset in the given file. Note that this is a static * routine that must be implemented for this class in a system-specific * module. This routine should return a new instance of the correct * subclass of this class for the current operating system. * * Note that 'filesize' is the size of the data starting at 'seekpos' * that the resource loader considers to be part of this resource; the * actual file may be larger than this value, since other data may * follow the resource in the file. * * 'url' is provided for diagnostic purposes; it should be used in any * error message that this routine generates (with oshtml_dbg_printf). * It can otherwise be ignored. * * The 'win' argument is provided so that the loader can check the * current environment for any information necessary for loading the * resource. */ static CHtmlSysResource *create_mng(const class CHtmlUrl *url, const textchar_t *filename, unsigned long seekpos, unsigned long filesize, class CHtmlSysWin *win); }; /* ------------------------------------------------------------------------ */ /* * Display site interface. Unlike static images, where the display site * runs the show and always calls the image to cause a display, animated * images are active, so they have to be able to notify the display site * whenever an animation event occurs. This interface provides a means for * an animated image object to notify the window (or object within a * window) where the animation is displayed when events occur. * * Since this is an abstract interface, animated images not bound to any * particular type of display container; any class that wants to display an * animated image resource can implement this interface. */ class CHtmlSysImageDisplaySite { public: virtual ~CHtmlSysImageDisplaySite() {} /* * Invalidate a portion of the display site: the MNG image's contents * have changed (due to animation playback) within the invalidated * area, so the area should be redisplayed as soon as possible. The * area to be invalidated is given relative to the upper left corner of * the display site. */ virtual void dispsite_inval(unsigned int x, unsigned int y, unsigned int width, unsigned int height) = 0; /* * Set a timer event. The site must call the image object's * notify_timer() method as soon as possible after the given real-time * interval (in milliseconds) elapses. * * We will request only one timer at any given time. If there's still * a timer request outstanding when we make a new timer request, the * new request should REPLACE the old request. */ virtual void dispsite_set_timer(unsigned long delay_ms, class CHtmlSysImageAnimated *image) = 0; /* * Cancel the timer. Any pending timer request should be cancelled. * No further calls to notify_timer() in the image object are desired. */ virtual void dispsite_cancel_timer(class CHtmlSysImageAnimated *) = 0; }; /* ------------------------------------------------------------------------ */ /* * System sound object. This type of object allows us to play back a * sound. This interface must be subclasses for each sound type (MIDI, * WAV). This interface is not to be implemented by system code; only * the final subclasses that provide interfaces for the specific sound * types need to be implemented. */ class CHtmlSysSound: public CHtmlSysResource { public: virtual ~CHtmlSysSound() { } /* this is a sound - override the downcast method */ CHtmlSysSound *cast_sound() { return this; } /* * sounds are not cacheable, because sound resources must keep track of * where they are in the playback */ virtual int is_cacheable() const { return FALSE; } /* * Start playing the sound. When the sound is finished, invoke the * given callback (which can be null, to indicate that no callback is * to be invoked) with the given context. The 'repeat_count' parameter * to the callback indicates how many times the sound has been played. * * 'repeat' is the number of times to play back the sound; 0 indicates * that the sound should be played forever (or until cancel_sound is * called, whichever comes first). The player can ignore the repeat * count, however (this is to simplify the implementation when it's not * convenient or useful to implement repetition in the player itself). * If the repeat count is obeyed, the callback will be invoked after * all iterations have been performed, and will indicate the number of * iterations completed. If the repeat count is ignored, the callback * will be invoked after the single iteration we'll perform and will * indicate that only one iteration has been played. In this case, * caller can simply requeue the sound (if still desired) with the next * lower repeat count. * * 'vol' is the base playback volume for the track, in the range * 0..100, where 0 is silence and 100 is unattenuated (i.e., play the * track at the full volume level of the recorded data). Values * outside this range are invalid and should be pegged to the range * (i.e., treat anything below 0 as 0, and anything over 100 as 100). * This volume level is NOT a master volume for the whole computer or * whole application - it shouldn't affect any other sound levels in * the system or be used to adjust the physical speaker volume. This * parameter instead simply sets the relative playback volume of this * track. A volume level of 0 means the track should effectively be * muted; 100 means it should be played back unattenuated, at the full * native volume recorded in the sound file. Values between 0 and 100 * should be perceptually linear volume levels, akin to adjusting a * speaker volume knob between minimum and maximum volume. If the * local system can't implement per-track relative volume control, * simply ignore this parameter and play back the track at its native * volume level. (Although you might still want to treat volume 0 as * mute in this case, if possible.) * * 'fade_in' and 'fade_out' are the start-of-track and end-of-track * fade times, in milliseconds. Zero means that the track should * start/end at full volume. If a non-zero fade time is given, it * means that the volume on the track should fade in or out over the * given interval - fade_in is the interval for fading in at the start * of the track, and fade_out is the interval for fading out at the end * of the track. This is a "best effort" feature - if fades can't be * implemented on the platform, or can't be implemented for certain * audio formats, simply ignore the fade parameters and start/stop at * full volume. * * 'crossfade' indicates whether the end-of-track fade-out should be a * cross-fade or a regular fade. True means cross-fade: as soon as the * fade-out begins, call the 'done' callback, which will tell the queue * manager that it can start playing the next track in the queue * immediately, even while the fade is proceeding. False means a * regular fade-out: the 'done' callback should be invoked after the * track is fully finished (including the fade), as normal. If * cross-fades can't be implemented on the platform, ignore this * parameter and do a normal fade-out. * * Note that 'crossfade' doesn't affect the fade-in. Any cross-fade at * fade-in is controlled by the *outgoing* track - it's up to the * previous track to determine when to call the 'done' callback to * enable to next queued track to begin playing. * * When the repeat count is 0 or >1, the fade-in should be applied only * to the first iteration, and the fade-out should be applied only to * the last iteration. There should be no fade between repeated * iterations of the same track. * * Returns zero on success, non-zero on error. If a non-zero value is * returned, the callback is NOT invoked. * * The URL is provided for diagnostic purposes. If the routine * encounters an error and wants to provide details, it can use the URL * in a debugging message to indicate the object that caused the error. */ virtual int play_sound(class CHtmlSysWin *win, void (*done_func)(void *, int repeat_count), void *done_func_ctx, int repeat, const textchar_t *url, int vol, long fade_in, long fade_out, int crossfade) = 0; /* * Add an end-of-track cross-fade to this track, if it's currently * playing. This has no effect if the track isn't already playing or * has finished playing. * * 'ms' is the duration of the fade, in milliseconds. The fade should * begin 'ms' milliseconds before the end of the track; the volume * level should be gradually decreased over that interval from the * normal playback volume to silence. * * The point of this routine is to allow an *incoming* track to ask for * a crossfade as it starts. The request can't be made as part of the * play_sound() on the incoming track - not just because play_sound() * doesn't offer a way to do this, but because there's no way for * play_sound() to offer a way to do this even if it wanted to. A * cross-fade is always a feature of an outgoing track, because the way * you accomplish a cross-fade is to have the outgoing track signal * that it's finished (by calling the 'done' callback) at the start of * its fade-out, to let the next queued track begin playing. So, the * way to handle an incoming cross-fade is to queue the incoming sound, * and call this routine on the immediately preceding sound in the * queue. * * Note that there might already be a scheduled fade-out on this track, * because play_sound() can request a cross-fade as well (and because * this routine could conceivably be called more than once for a single * track). If there are multiple fade-outs for the same track, the * recommended handling is to combine them by always setting the volume * level to the MININUM of the volume levels from the competing fades. * This will ensure a monotonic fade - the volume level won't jump * around, because it will only be able to decrease over time. The * volume level might not decrease in a simple straight line (it could * be a series of linear segments, because the different fade slopes * might intersect), but this really shouldn't matter perceptually; * most listeners probably won't even notice that the fade isn't * perfectly linear as long as it's monotonic. * * If cross-fades can't be implemented on this platform, or it's not * possible to add a cross-fade to a track already being played, simply * ignore calls to this routine. */ virtual void add_crossfade(class CHtmlSysWin *win, long ms) = 0; /* * Cancel the sound. * * If 'sync' is true, the sound must be canceled, and the callback * invoked, BEFORE this routine returns. If 'sync' is false, this * routine MAY return before the sound has actually stopped, as long as * the sound is scheduled for cancellation very shortly. (Of course, * the callback should still be invoked in this case, as soon as the * sound actually stops playing.) * * (The point os 'sync' is to allow for situations where the playback * is handled by a background thread, hardware sound card buffers, or * another asynchronous player mechanism. For example, if the playback * is being handled by a background thread, when 'sync' is false this * routine could simply post a message to the playback thread telling * it to stop, then immediately return without waiting for the message * to be received. Playback might continue in this case for a few * milliseconds, until the OS gets around to scheduling the playback * thread for its next chance to run. This type of approach helps keep * the UI responsive by avoiding blocking waits for playback to catch * up with UI activity.) * * If 'sync' is true OR 'fade_out_ms' is zero, the cancellation is * abrupt - the sound should simply be cut off in mid-stream. If a * callback was registered when the sound was started, the callback * should be invoked as though the track had reached its normal ending, * as soon as the playback actually terminates. * * If 'fade_out_ms' is non-zero, it specifies a number of milliseconds * over which the sound should gradually fade out. The fade should * start immediately; the volume of the playback for this sound (and * ONLY this sound) should be reduced gradually from full volume to * silence, over the course of the specified interval. * * If a non-zero fade interval is specified, the 'fade_in_bg' flag * specifies how the callback function (if one was specified in * play_sound) should be invoked. If 'fade_in_bg' is true, it means * that the callback should be invoked immediately, before the function * returns. Otherwise, the callback should be invoked as soon as the * fade is finished. * * Note that regardless of the 'fade_in_bg' setting, the fade itself * should happen "in the background" with respect to the UI, if * possible. That is, this routine should schedule the fade to begin * immediately, and should then immediately return so that the UI * remains responsive during the fade interval. This routine should * NOT wait until after the fade is finished to return to its caller. * * (The point of setting 'fade_in_bg' to true is to allow for * cross-fade effects: since the callback is invoked immediately, the * next sound in the same queue ("layer") will be able to start playing * immediately, so the new sound will play concurrently while the old * sound fades out. Using a fade-in with the new sound will * effectively cross-fade the two sounds, creating a smooth transition * between adjacent tracks.) * * Synchronous mode and fade mode are mutually exclusive. If 'sync' is * true, the routine should simply ignore the fade parameters. * * Note that the fade setting specified here is independent of the * end-of-track fade specified in play_sound(). The fade specified in * play_sound() only applies when the track plays all the way through * to the end. The cancel_sound() fade, in contrast, specifies what * should happen immediately at the time of the cancellation, so it * effectively supersedes any play_sound() fade. In particular, if * 'fade_out_ms' in cancel_sound() is zero, the sound should be stopped * abruptly with no fade, EVEN IF play_sound() specified a fade-out. * * HANDLING PLATFORM LIMITATIONS: * * 1. If the local platform can't support concurrent playback of two or * more sounds, this routine should simply ignore fade_in_bg and always * do "foreground" fades - that is, still process the fade in the * background with respect to the UI, but don't invoke the callback * until after the fade is finished. Waiting to invoke the callback * will ensure that the queue manager doesn't attempt to start playing * another sound until the current sound is terminated. This approach * will still create a relatively smooth transition effect between * adjacent tracks by doing a fade-out and then a fade-in - not exactly * what the caller asks for when requesting a cross-fade, but a * reasonable facsimile. * * 2. If it's not possible to perform the fade in the background with * respect to the UI, the recommended fallback is to limit the fade to * a relatively short interval (e.g., one second: so if the caller * requests a 5000ms fade, limit it to 1000ms) and then perform the * fade as a blocking operation. Keeping the fade time short will * minimize the UI impact of freezing the UI during the fade, while * still smoothing out sound transitions to some degree. * * 3. If it's not possible to perform fades at all, simply ignore the * fade parameters and stop the sound abruptly. */ virtual void cancel_sound(class CHtmlSysWin *win, int sync, long fade_out_ms, int fade_in_bg) = 0; /* * Suspend playback if necessary to make the sound device available * for another sound. If the given new sound can't be played * simultaneously with this sound, suspend this sound so that the * new sound can play. (This routine is not meant to actually play * the new sound; it simply suspends playback of the current sound * so that the caller can then play the new sound.) Return true if * we did indeed suspend the sound, false if not. * * On systems where multiple sounds of any type can be played * simultaneously, this routine should simply return false. If the * system allows multiple sounds to be played as long as they're not * of the same type, this routine should check the type of the new * sound; if it's the same as the current sound, the routine should * suspend the current sound and return true, otherwise return false. * * The callback indicating that the sound is finished should NOT be * invoked by this routine, since the routine should merely pause * the current sound, not cancel it entirely. */ virtual int maybe_suspend(class CHtmlSysSound *new_sound) = 0; /* * Resume playback. If playback was previously suspended with * maybe_suspend(), this should continue playing where we left off. */ virtual void resume() = 0; }; /* * System MIDI sound object. */ class CHtmlSysSoundMidi: public CHtmlSysSound { public: virtual HTML_res_type_t get_res_type() const { return HTML_res_type_MIDI; } /* * Create a new system-specific MIDI object. This is a static * routine that must be implemented for this class in a * system-specific module. This routine should return a new * instance of the correct subclass of this class for the current * operating system. * * Note that 'filesize' is the size of the data starting at * 'seekpos' that the resource loader considers to be part of this * resource; the actual file may be larger than this value, since * other data may follow the resource in the file. * * 'url' is provided for diagnostic purposes; it should be used in * any error message that this routine generates (with * oshtml_dbg_printf). It can otherwise be ignored. * * The 'win' argument is provided so that the loader can check the * current environment for any information necessary for loading the * resource. */ static CHtmlSysResource *create_midi(const class CHtmlUrl *url, const textchar_t *filename, unsigned long seekpos, unsigned long filesize, class CHtmlSysWin *win); }; /* * System WAV sound object. */ class CHtmlSysSoundWav: public CHtmlSysSound { public: virtual HTML_res_type_t get_res_type() const { return HTML_res_type_WAV; } /* * Create a new system-specific WAV object. This is a static * routine that must be implemented for this class in a * system-specific module. This routine should return a new * instance of the correct subclass of this class for the current * operating system. * * Note that 'filesize' is the size of the data starting at * 'seekpos' that the resource loader considers to be part of this * resource; the actual file may be larger than this value, since * other data may follow the resource in the file. * * 'url' is provided for diagnostic purposes; it should be used in * any error message that this routine generates (with * oshtml_dbg_printf). It can otherwise be ignored. * * The 'win' argument is provided so that the loader can check the * current environment for any information necessary for loading the * resource. */ static CHtmlSysResource *create_wav(const class CHtmlUrl *url, const textchar_t *filename, unsigned long seekpos, unsigned long filesize, class CHtmlSysWin *win); }; /* * System MPEG 2.0 audio layer 1/2/3 sound object. This single object * is used for all three MPEG 2.0 audio layers, since all subtypes use a * common header format, which can be used to determine the appropriate * decoding algorithm. */ class CHtmlSysSoundMpeg: public CHtmlSysSound { public: virtual HTML_res_type_t get_res_type() const { return HTML_res_type_WAV; } /* * Create a new system-specific MPEG 2.0 layer 1/2/3 sound object. * This is a static routine that must be implemented for this class * in a system-specific module. This routine should return a new * instance of the correct subclass of this class for the current * operating system. * * Note that 'filesize' is the size of the data starting at * 'seekpos' that the resource loader considers to be part of this * resource; the actual file may be larger than this value, since * other data may follow the resource in the file. * * 'url' is provided for diagnostic purposes; it should be used in * any error message that this routine generates (with * oshtml_dbg_printf). It can otherwise be ignored. * * The 'win' argument is provided so that the loader can check the * current environment for any information necessary for loading the * resource. */ static CHtmlSysResource *create_mpeg(const class CHtmlUrl *url, const textchar_t *filename, unsigned long seekpos, unsigned long filesize, class CHtmlSysWin *win); }; /* * System Ogg Vorbis sound object. */ class CHtmlSysSoundOgg: public CHtmlSysSound { public: virtual HTML_res_type_t get_res_type() const { return HTML_res_type_OGG; } /* * Create a new system-specific Ogg Vorbis object. This is a static * routine that must be implemented for this class in a system-specific * module. This routine should return a new instance of the correct * subclass of this class for the current operating system. * * Note that 'filesize' is the size of the data starting at 'seekpos' * that the resource loader considers to be part of this resource; the * actual file may be larger than this value, since other data may * follow the resource in the file. * * 'url' is provided for diagnostic purposes; it should be used in any * error message that this routine generates (with oshtml_dbg_printf). * It can otherwise be ignored. * * The 'win' argument is provided so that the loader can check the * current environment for any information necessary for loading the * resource. */ static CHtmlSysResource *create_ogg(const class CHtmlUrl *url, const textchar_t *filename, unsigned long seekpos, unsigned long filesize, class CHtmlSysWin *win); }; #endif /* HTMLSYS_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmltags.cpp�������������������������������������������������������������������0000664�0000000�0000000�00000730536�12650170723�0017025�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/html/htmltags.cpp,v 1.3 1999/07/11 00:46:40 MJRoberts Exp $"; #endif /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmltags.cpp - implementation of HTML tag objects Function Notes Modified 08/26/97 MJRoberts - Creation */ #include <string.h> #include <memory.h> #include <stdlib.h> #include <stdio.h> #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef HTMLPRS_H #include "htmlprs.h" #endif #ifndef HTMLTAGS_H #include "htmltags.h" #endif #ifndef HTMLFMT_H #include "htmlfmt.h" #endif #ifndef HTMLDISP_H #include "htmldisp.h" #endif #ifndef HTMLSYS_H #include "htmlsys.h" #endif #ifndef HTMLTXAR_H #include "htmltxar.h" #endif #ifndef HTMLRC_H #include "htmlrc.h" #endif /* ------------------------------------------------------------------------ */ /* * Basic tag implementation */ CHtmlTag::~CHtmlTag() { } /* * check if my name matches a given name */ int CHtmlTag::tag_name_matches(const textchar_t *nm, size_t nmlen) const { const textchar_t *myname = get_tag_name(); return tag_names_match(myname, get_strlen(myname), nm, nmlen); } /* * check if two tag names match */ int CHtmlTag::tag_names_match(const textchar_t *nm1, size_t len1, const textchar_t *nm2, size_t len2) { return (len1 == len2 && !memicmp(nm1, nm2, len1 * sizeof(nm1[0]))); } /* process the end tag */ void CHtmlTag::process_end_tag(CHtmlParser *parser, const textchar_t *tag, size_t len) { /* process the normal end tag */ parser->end_normal_tag(tag, len); } /* parse the tag */ void CHtmlTag::on_parse(CHtmlParser *parser) { /* add myself to the current list */ parser->append_tag(this); } /* * Start a new paragraph */ void CHtmlTag::break_paragraph(CHtmlParser *parser, int isexplicit) { parser->end_paragraph(isexplicit); } /* * Set an attribute */ HTML_attrerr CHtmlTag::set_attribute(CHtmlParser *, HTML_Attrib_id_t, const textchar_t * /*val*/, size_t /*vallen*/) { /* we don't recognize any attributes by default */ return HTML_attrerr_inv_attr; } /* * Parse a color attribute setting */ HTML_attrerr CHtmlTag::set_color_attr(CHtmlParser *parser, HTML_color_t *color_var, const textchar_t *val, size_t vallen) { /* * if it starts with #, it's a color sRGB value, otherwise it must * be a name */ if (vallen > 0 && (*val == '#' || is_digit(*val))) { long num; HTML_attrerr err; /* skip leading '#' if provided */ if (*val == '#') { ++val; --vallen; } /* parse the hex number for the color */ err = set_hex_attr(&num, val, vallen); *color_var = num; return err; } else { /* * ask the parser to try to find an identifier that matches the * value */ switch(parser->attrval_to_id(val, vallen)) { case HTML_Attrib_black: *color_var = 0x000000; break; case HTML_Attrib_silver: *color_var = 0xc0c0c0; break; case HTML_Attrib_gray: *color_var = 0x808080; break; case HTML_Attrib_white: *color_var = 0xffffff; break; case HTML_Attrib_maroon: *color_var = 0x800000; break; case HTML_Attrib_red: *color_var = 0xff0000; break; case HTML_Attrib_purple: *color_var = 0x800080; break; case HTML_Attrib_fuchsia: *color_var = 0xff00ff; break; case HTML_Attrib_green: *color_var = 0x008000; break; case HTML_Attrib_lime: *color_var = 0x00ff00; break; case HTML_Attrib_olive: *color_var = 0x808000; break; case HTML_Attrib_yellow: *color_var = 0xffff00; break; case HTML_Attrib_navy: *color_var = 0x000080; break; case HTML_Attrib_blue: *color_var = 0x0000ff; break; case HTML_Attrib_teal: *color_var = 0x008080; break; case HTML_Attrib_aqua: *color_var = 0x00ffff; break; case HTML_Attrib_statusbg: *color_var = HTML_COLOR_STATUSBG; break; case HTML_Attrib_statustext: *color_var = HTML_COLOR_STATUSTEXT; break; case HTML_Attrib_link: *color_var = HTML_COLOR_LINK; break; case HTML_Attrib_alink: *color_var = HTML_COLOR_ALINK; break; case HTML_Attrib_hlink: *color_var = HTML_COLOR_HLINK; break; case HTML_Attrib_text: *color_var = HTML_COLOR_TEXT; break; case HTML_Attrib_bgcolor: *color_var = HTML_COLOR_BGCOLOR; break; case HTML_Attrib_input: *color_var = HTML_COLOR_INPUT; break; default: /* this value is not valid */ return HTML_attrerr_inv_enum; } /* if we didn't return an error already, we were successful */ return HTML_attrerr_ok; } } /* * Map a color to an appropriate system color */ HTML_color_t CHtmlTag::map_color(CHtmlSysWin *win, HTML_color_t color) { /* * if it's a special color, ask the window to map it to an RGB * value; otherwise, it's already an RGB value, so return it * unchanged */ if (html_is_color_special(color)) return win->map_system_color(color); else return color; } /* * Set a numeric attribute */ HTML_attrerr CHtmlTag::set_number_attr(long *num_var, const textchar_t *val, size_t vallen) { long acc; HTML_attrerr err; /* loop through the digits */ for (err = HTML_attrerr_ok, acc = 0 ; vallen ; ++val, --vallen) { if (is_digit(*val)) { /* accumulate one more digit */ acc *= 10; acc += ascii_digit_to_int(*val); } else { /* generate an error and return */ err = HTML_attrerr_inv_type; break; } } /* set the variable value */ *num_var = acc; /* return any error code that we set */ return err; } /* * Set a numeric attribute from a floating point value. */ HTML_attrerr CHtmlTag::set_float_attr(double *num_var, const textchar_t *val, size_t vallen) { double acc; int in_frac = FALSE; double frac_base; HTML_attrerr err; /* loop through the digits */ for (err = HTML_attrerr_ok, acc = 0.0 ; vallen ; ++val, --vallen) { if (is_digit(*val)) { /* accumulate one more digit */ if (in_frac) { acc += (double)ascii_digit_to_int(*val) / frac_base; frac_base *= 10.0; } else { acc *= 10.0; acc += (double)ascii_digit_to_int(*val); } } else if (*val == '.' && !in_frac) { in_frac = TRUE; frac_base = 10.0; } else { /* generate an error and return */ err = HTML_attrerr_inv_type; break; } } /* set the variable value */ *num_var = acc; /* return any error code that we set */ return err; } /* * Check an attribute string for a comma-delimited keyword */ int CHtmlTag::check_comma_kw(const textchar_t **val, size_t *vallen, const textchar_t *kw) { /* get the keyword length */ size_t kwlen = strlen(kw); /* scan for the keyword */ if (*vallen >= kwlen && memicmp(*val, kw, kwlen) == 0 && (*vallen == kwlen || (*val)[kwlen] == ',')) { /* we found the keyword - skip it in the input */ *val += kwlen; *vallen -= kwlen; /* skip the comma if present */ if (*vallen != 0) { *val += 1; *vallen -= 1; } /* tell the caller we found it */ return TRUE; } else { /* we didn't find it */ return FALSE; } } /* * Set a coordinate attribute. If pct is not null, we'll allow a * percentage sign, and return in *pct whether or not a percentage sign * was found. Advances the string pointer to the next comma. */ HTML_attrerr CHtmlTag::set_coord_attr(long *num_var, int *is_pct, const textchar_t **val, size_t *vallen) { const textchar_t *start; size_t startlen; HTML_attrerr err; /* find the next comma */ start = *val; startlen = *vallen; while (*vallen && **val != ',') { ++(*val); --(*vallen); } /* compute the length of the part up to the comma */ startlen -= *vallen; /* parse the number up to the comma */ if (is_pct == 0) err = set_number_attr(num_var, start, startlen); else err = set_number_or_pct_attr(num_var, is_pct, start, startlen); /* skip the comma and the whitespace after the comma */ if (*vallen && **val == ',') { ++(*val); --(*vallen); } while (*vallen && is_space(**val)) { ++(*val); --(*vallen); } /* return the result */ return err; } /* * Set a numeric attribute, which may be expressed either in absolute * terms or as a percentage. */ HTML_attrerr CHtmlTag::set_number_or_pct_attr(long *num_var, int *is_pct, const textchar_t *val, size_t vallen) { /* * if the last character is a percent sign, it's relative; * otherwise, it's absolute */ if (vallen > 0 && val[vallen - 1] == '%') { /* note that it's given as a percentage value */ *is_pct = TRUE; /* remove the percent sign from the value */ --vallen; } else { /* note that it's absolute */ *is_pct = FALSE; } /* parse what's left as an ordinary number */ return set_number_attr(num_var, val, vallen); } /* * Set a hex numeric attribute */ HTML_attrerr CHtmlTag::set_hex_attr(long *num_var, const textchar_t *val, size_t vallen) { long acc; HTML_attrerr err; /* loop through the digits */ for (err = HTML_attrerr_ok, acc = 0 ; vallen ; ++val, --vallen) { if (is_hex_digit(*val)) { /* accumulate one more digit */ acc <<= 4; acc += ascii_hex_to_int(*val); } else { /* generate an error and return */ err = HTML_attrerr_inv_type; break; } } /* set the variable value */ *num_var = acc; /* return any error code that we set */ return err; } /* * Get the next tag in format order. When formatting, we start with * the top container, then go to the first child of the container, then * to it's first child, and so on; then to the next sibling of that tag, * and so on; then to the next sibling of its container, then to its * first child, and so on. In effect, we do a depth-first traversal, * but we process the container nodes on the way down. An ordinary tag * is not a container, so we only need to worry about moving to the next * sibling or to the parent's next sibling. */ CHtmlTag *CHtmlTag::get_next_fmt_tag(CHtmlFormatter *formatter) { /* if I have a sibling, it's the next tag */ if (get_next_tag() != 0) return get_next_tag(); /* * That's the end of the contents list for my container - tell the * container we're leaving, and move on to its next sibling. If we * don't have a container, there are no more tags left to format. */ if (get_container() != 0) { CHtmlTag *nxt; CHtmlTagContainer *cont; /* * Find the container's next sibling. If the container itself * doesn't have a next sibling, find its container's next * sibling, and so on until we find a sibling or run out of * containers. */ for (cont = get_container() ; cont != 0 ; cont = cont->get_container()) { /* get the container's next sibling, and stop if it has one */ nxt = cont->get_next_tag(); if (nxt != 0) break; } /* * If there is indeed another tag to format, or the tag is * marked as "closed", tell our container that we're done * formatting it. If there isn't anything left, and the tag * hasn't been closed yet, do NOT leave the container yet -- * more document parsing may occur that adds more items to the * current container, in which case we'll still want the current * container's settings in effect when we come back to do more * formatting. Hence, only exit the container if we have more * work to do immediately, in which case we know that we'll * never add anything more to our container. Note that we need * to inform as many containers as we're actually exiting, if * we're exiting more than one level, so iterate up the * containment tree until we find a container with a next * sibling. */ for (cont = get_container() ; cont != 0 ; cont = cont->get_container()) { /* * if this one isn't closed yet, and there's not another * sibling, it's still open */ if (nxt == 0 && !cont->is_closed()) break; /* tell this one we're exiting it */ cont->format_exit(formatter); /* stop if this one has a sibling */ if (cont->get_next_tag() != 0) break; } /* return the next tag after our container */ return nxt; } /* * there's nothing after us, and we have no container, so this is * the end of the line */ return 0; } /* * get the list nesting level */ int CHtmlTag::get_list_nesting_level() const { /* if I don't have a container, nesting level is zero */ if (get_container() == 0) return 0; /* return my container's list nesting level */ return (get_container()->get_list_nesting_level()); } /* * parse a CLEAR attribute value */ HTML_attrerr CHtmlTag::set_clear_attr(HTML_Attrib_id_t *clear, CHtmlParser *parser, const textchar_t *val, size_t vallen) { HTML_Attrib_id_t val_id; /* get the attribute value, and check it for validity */ switch(val_id = parser->attrval_to_id(val, vallen)) { case HTML_Attrib_left: case HTML_Attrib_right: case HTML_Attrib_all: /* valid value - set it and return success */ *clear = val_id; return HTML_attrerr_ok; default: /* not a valid CLEAR value */ return HTML_attrerr_inv_enum; } } /* * format a CLEAR setting */ void CHtmlTag::format_clear(CHtmlFormatter *formatter, HTML_Attrib_id_t clear) { int changed; /* * If we're to move past items in one of the margins, do so */ switch(clear) { case HTML_Attrib_left: changed = formatter->break_to_clear(TRUE, FALSE); break; case HTML_Attrib_right: changed = formatter->break_to_clear(FALSE, TRUE); break; case HTML_Attrib_all: changed = formatter->break_to_clear(TRUE, TRUE); break; default: changed = FALSE; break; } /* if we added any spacing, go to a new line */ if (changed) formatter->add_disp_item_new_line(new (formatter) CHtmlDispBreak(0)); } /* * Find the end of a token in a delimited attribute list. This simply * scans a string until we find the next occurrence of a delimiter * character, or the end of the string. */ const textchar_t *CHtmlTag::find_attr_token( const textchar_t *val, size_t *vallen, const textchar_t *delims) { /* scan until we find a delimiter or run out of input text */ for ( ; *vallen != 0 ; ++val, --*vallen) { /* if this character is in the delimiter list, stop here */ if (strchr(delims, *val) != 0) return val; } /* * we ran out of string without finding the delimiter - return a * pointer to the character after the end of the string */ return val; } /* ------------------------------------------------------------------------ */ /* * Container tag implementation */ /* * Destructor - delete sublists */ CHtmlTagContainer::~CHtmlTagContainer() { /* delete my sublist */ delete_contents(); } /* * Pruning pre-deletion - pre-delete my children */ void CHtmlTagContainer::prune_pre_delete(CHtmlTextArray *arr) { CHtmlTag *cur; /* pass the notification on to my children */ for (cur = contents_first_ ; cur != 0 ; cur = cur->get_next_tag()) cur->prune_pre_delete(arr); } /* * A container tag pushes itself onto the container stack upon being * parsed */ void CHtmlTagContainer::on_parse(CHtmlParser *parser) { /* push onto parser container stack */ parser->push_container(this); } /* * Append a tag to the end of my sublist */ void CHtmlTagContainer::append_to_sublist(CHtmlTag *tag) { /* see if I have anything in my sublist yet */ if (contents_last_) { /* add this item after the last item currently in my sublist */ contents_last_->append(tag); /* it's the new sublist tail */ contents_last_ = tag; } else { /* this is now the first, last, and only item in my sublist */ contents_first_ = tag; contents_last_ = tag; } /* I'm the new item's container */ tag->set_container(this); } /* * Delete contents */ void CHtmlTagContainer::delete_contents() { CHtmlTag *tag; CHtmlTag *nxt; /* go through each item in my sublist and delete it */ for (tag = contents_first_ ; tag ; tag = nxt) { /* remember the next thing in my sublist */ nxt = tag->get_next_tag(); /* delete this tag */ delete tag; } /* there's nothing in my sublist now */ contents_first_ = 0; contents_last_ = 0; } /* * Get the next tag in format order. The next tag after formatting a * container is the first child of the container. */ CHtmlTag *CHtmlTagContainer::get_next_fmt_tag(class CHtmlFormatter *formatter) { /* * if I have a child, return it; otherwise, return the next sibling * or container's sibling (etc) as though we were an ordinary tag */ if (get_contents()) { /* return my first child */ return get_contents(); } else { /* * I have no children. Call my format_exit() method right now. * Normally, the last child would call this on the way out; since * we have no children, though, no one would otherwise call this. * Note that, as usual, we only want to call format_exit() if the * tag is finished (i.e., closed). */ if (is_closed()) format_exit(formatter); /* * get the next item using the non-container algorithm; this * will find our container's next sibling, or its container's * next sibling, and so on, until we find a container with a * sibling or run out of containers */ return CHtmlTag::get_next_fmt_tag(formatter); } } /* * Re-insert my contents into the text array. Runs through my contents * and tells each to re-insert their contents. */ void CHtmlTagContainer::reinsert_text_array(CHtmlTextArray *arr) { CHtmlTag *cur; for (cur = get_contents() ; cur != 0 ; cur = cur->get_next_tag()) cur->reinsert_text_array(arr); } /* ------------------------------------------------------------------------ */ /* * Special outer container tag */ void CHtmlTagOuter::format(CHtmlSysWin *win, CHtmlFormatter *) { /* set default color scheme */ win->set_html_bg_color(0, TRUE); win->set_html_text_color(0, TRUE); win->set_html_input_color(0, TRUE); win->set_html_link_colors(0, TRUE, 0, TRUE, 0, TRUE, 0, TRUE); } /* ------------------------------------------------------------------------ */ /* * TITLE tag */ void CHtmlTagTITLE::format(CHtmlSysWin *, CHtmlFormatter *formatter) { /* put the formatter into title mode */ formatter->begin_title(); } void CHtmlTagTITLE::format_exit(CHtmlFormatter *formatter) { /* tell the formatter this is the end of the title */ formatter->end_title(this); /* * if we have some title text, display it directly -- if we've been * pruned, our contents will have been deleted, hence we must rely * on our internal record of the title */ if (title_.get() != 0) formatter->get_win()->set_window_title(title_.get()); } /* * We can prune the contents of a TITLE tag, even when we can't prune * the TITLE tag itself */ void CHtmlTagTITLE::try_prune_contents(CHtmlTextArray *txtarr) { CHtmlTag *cur; CHtmlTag *nxt; /* delete each child */ for (cur = contents_first_ ; cur != 0 ; cur = nxt) { /* remember the next child */ nxt = cur->get_next_tag(); /* notify this child that it's about to be pruned */ cur->prune_pre_delete(txtarr); /* delete it */ delete cur; } /* forget about our former contents */ contents_first_ = 0; contents_last_ = 0; } /* ------------------------------------------------------------------------ */ /* * BLOCKQUOTE */ void CHtmlTagBLOCKQUOTE::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { long indent; /* inherit default */ CHtmlTagBlock::format(win, formatter); /* insert a line break */ break_ht_ = win->measure_text(formatter->get_font(), " ", 1, 0).y / 2; formatter->add_line_spacing(break_ht_); /* indent a few en's in the current font */ indent = win->measure_text(formatter->get_font(), "n", 1, 0).x * 5; formatter->push_margins(formatter->get_left_margin() + indent, formatter->get_right_margin() + indent); } void CHtmlTagBLOCKQUOTE::format_exit(CHtmlFormatter *formatter) { /* insert a line break */ formatter->add_line_spacing(break_ht_); /* restore enclosing margins */ formatter->pop_margins(); /* inherit default */ CHtmlTagBlock::format_exit(formatter); } /* ------------------------------------------------------------------------ */ /* * Character set change tag */ int CHtmlTagCharset::add_attrs(class CHtmlFormatter *, class CHtmlFontDesc *desc) { /* use our character set, not the default */ desc->default_charset = FALSE; desc->charset = charset_; /* indicate that we've changed the character set */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Entity character inserter */ void CHtmlTagEntityInserter::on_parse( CHtmlParser *parser, int charcode, const char *fallback) { /* translate the entity */ len_ = parser->get_sys_frame()->xlat_html4_entity( txt_, sizeof(txt_), charcode, &charset_, &has_charset_); /* if it was too long, use the fallback */ if (len_ > sizeof(txt_)) { len_ = strlen(fallback); if (len_ > sizeof(txt_)) len_ = sizeof(txt_); memcpy(txt_, fallback, len_); } /* add the text to the text array */ txtofs_ = parser->get_text_array()->append_text(txt_, len_); } void CHtmlTagEntityInserter::format(CHtmlSysWin *win, CHtmlFormatter *fmt) { /* if necessary, change character sets before inserting our text */ CHtmlSysFont *old_font = 0; if (has_charset_) { /* get the old font and its description */ CHtmlFontDesc font_desc; old_font = fmt->get_font(); old_font->get_font_desc(&font_desc); /* create a new font for the new character set */ font_desc.default_charset = FALSE; font_desc.charset = charset_; CHtmlSysFont *new_font = win->get_font(&font_desc); /* install the new font */ fmt->set_font(new_font); } /* add our text */ fmt->add_disp_item(fmt->get_disp_factory()-> new_disp_text(win, fmt->get_font(), txt_, len_, txtofs_)); /* restore the old font, if we changed it */ if (old_font != 0) fmt->set_font(old_font); } void CHtmlTagEntityInserter::prune_pre_delete(CHtmlTextArray *arr) { arr->delete_text(txtofs_, len_); } /* ------------------------------------------------------------------------ */ /* * CREDIT */ void CHtmlTagCREDIT::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { long break_ht; /* insert a line break */ break_ht = win->measure_text(formatter->get_font(), " ", 1, 0).y / 3; formatter->add_line_spacing(break_ht); /* right-align text in the credit */ old_align_ = formatter->get_blk_alignment(); formatter->set_blk_alignment(HTML_Attrib_right); /* inherit default handling to set font attributes */ CHtmlTagFontCont::format(win, formatter); /* add the emdash */ emdash_.format(win, formatter); } void CHtmlTagCREDIT::format_exit(CHtmlFormatter *formatter) { formatter->add_line_spacing(0); formatter->set_blk_alignment(old_align_); CHtmlTagFontCont::format_exit(formatter); } int CHtmlTagCREDIT::add_attrs(CHtmlFormatter *, CHtmlFontDesc *desc) { /* if italics are already set, there's nothing to do */ if (desc->italic) return FALSE; /* set italics and indicate a change */ desc->italic = TRUE; return TRUE; } /* ------------------------------------------------------------------------ */ /* * Heading tags (H1 - H6) */ void CHtmlTagHeading::on_parse(CHtmlParser *parser) { /* start a new paragraph */ break_paragraph(parser, FALSE); /* inherit default */ CHtmlTagBlock::on_parse(parser); } void CHtmlTagHeading::on_close(CHtmlParser *parser) { /* inherit default */ CHtmlTagBlock::on_close(parser); /* start a new paragraph */ break_paragraph(parser, FALSE); } /* * parse attributes in a heading tag */ HTML_attrerr CHtmlTagHeading::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { HTML_Attrib_id_t val_id; switch(attr_id) { case HTML_Attrib_align: /* look up the alignment setting */ val_id = parser->attrval_to_id(val, vallen); /* make sure it's valid - return an error if not */ switch(val_id) { case HTML_Attrib_left: case HTML_Attrib_center: case HTML_Attrib_right: case HTML_Attrib_justify: align_ = val_id; break; default: return HTML_attrerr_inv_enum; } break; default: /* return inherited default behavior */ return CHtmlTagBlock::set_attribute(parser, attr_id, val, vallen); } /* if we didn't balk already, we must have been successful */ return HTML_attrerr_ok; } /* * Add font attributes. Headings are formatted at a particular HTML * size (1-7), which depends on the particular heading subclass, in * bold, and in the text font. */ int CHtmlTagHeading::add_attrs(CHtmlFormatter *, CHtmlFontDesc *desc) { CHtmlFontDesc mydesc; /* ignore the current font descriptor, and set up our own settings */ mydesc.weight = 700; mydesc.htmlsize = get_heading_fontsize(); desc->copy_from(&mydesc); /* always report a change */ return TRUE; } void CHtmlTagHeading::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { /* inherit default */ CHtmlTagBlock::format(win, formatter); /* insert a full line break */ break_ht_ = win->measure_text(formatter->get_font(), " ", 1, 0).y; formatter->add_line_spacing(break_ht_); /* set the alignment specified for the header */ formatter->set_blk_alignment(align_); /* enter heading font */ fontcont_format(win, formatter); } void CHtmlTagHeading::format_exit(CHtmlFormatter *formatter) { /* exit heading font */ fontcont_format_exit(formatter); /* insert a full line break */ formatter->add_line_spacing(break_ht_); /* inherit default */ CHtmlTagBlock::format_exit(formatter); } /* ------------------------------------------------------------------------ */ /* * ADDRESS tag */ void CHtmlTagADDRESS::on_parse(CHtmlParser *parser) { /* start a new paragraph */ break_paragraph(parser, FALSE); /* initialize the font-container add-in */ fontcont_init(); /* inherit default */ CHtmlTagBlock::on_parse(parser); } void CHtmlTagADDRESS::on_close(CHtmlParser *parser) { /* inherit default */ CHtmlTagBlock::on_close(parser); /* start a new paragraph */ break_paragraph(parser, FALSE); } /* * set font attributes */ int CHtmlTagADDRESS::add_attrs(CHtmlFormatter *, CHtmlFontDesc *desc) { /* if ADDRESS is already set, there's nothing to do */ if (desc->pe_address) return FALSE; /* set ADDRESS and indicate a change */ desc->pe_address = TRUE; return TRUE; } /* ------------------------------------------------------------------------ */ /* * HR (horizontal rule) */ CHtmlTagHR::~CHtmlTagHR() { /* remove our reference on the image, if we have one */ if (image_ != 0) image_->remove_ref(); } void CHtmlTagHR::on_parse(CHtmlParser *parser) { /* add a paragraph break before the rule */ break_paragraph(parser, FALSE); /* inherit default handling */ CHtmlTag::on_parse(parser); /* add a paragraph break after the rule */ break_paragraph(parser, FALSE); } /* * parse attributes */ HTML_attrerr CHtmlTagHR::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { switch(attr_id) { case HTML_Attrib_align: /* allow left, center, or right alignment */ switch(align_ = parser->attrval_to_id(val, vallen)) { case HTML_Attrib_left: case HTML_Attrib_right: case HTML_Attrib_center: /* no problem */ break; default: /* invalid setting for ALIGN */ align_ = HTML_Attrib_invalid; return HTML_attrerr_inv_enum; } break; case HTML_Attrib_noshade: /* ignore the value, set shading off */ shade_ = FALSE; break; case HTML_Attrib_size: /* set height of the ruler */ return set_number_attr(&size_, val, vallen); case HTML_Attrib_width: /* set the horizontal size */ return set_number_or_pct_attr(&width_, &width_pct_, val, vallen); case HTML_Attrib_src: /* set the image source */ src_.set_url(val, vallen); break; default: /* return inherited default behavior */ return CHtmlTag::set_attribute(parser, attr_id, val, vallen); } /* if we made it this far, everything worked correctly */ return HTML_attrerr_ok; } void CHtmlTagHR::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { CHtmlDispHR *disp; /* add a full line break before */ formatter->add_line_spacing(win->measure_text( formatter->get_font(), " ", 1, 0).y); /* set the appropriate alignment in the formatter */ formatter->set_blk_alignment(align_); /* * If we have a SRC attribute with an image, and haven't loaded the * image yet, load it now. Ignore the image if graphics are * disabled. */ if (win->get_html_show_graphics()) { if (src_.get_url() != 0 && image_ == 0) { /* * try to find the image in the cache, or load it if it's * not already there */ image_ = formatter->get_res_cache()-> find_or_create(win, formatter->get_res_finder(), &src_); /* if it's not a valid image, log an error */ if (image_ == 0 || image_->get_image() == 0) oshtml_dbg_printf("<HR>: resource %s is not a valid image\n", src_.get_url()); /* count our reference to the cache object as long as we keep it */ if (image_ != 0) image_->add_ref(); } } else if (image_ != 0) { /* graphics aren't allowed - forget the image */ image_->remove_ref(); image_ = 0; } /* add a rule of the appropriate size */ disp = new (formatter) CHtmlDispHR(win, shade_, size_, width_, width_pct_, formatter->get_left_margin(), formatter->get_right_margin(), image_); formatter->add_disp_item(disp); /* add a full line break after */ formatter->add_line_spacing(win->measure_text( formatter->get_font(), " ", 1, 0).y); } /* ------------------------------------------------------------------------ */ /* * List container - superclass for UL, OL, DL */ /* * parse a list container */ void CHtmlTagListContainer::on_parse(CHtmlParser *parser) { /* inherit default to insert myself into the parser list */ CHtmlTagBlock::on_parse(parser); /* set my nesting level to one higher than my container's */ list_level_ = get_container()->get_list_nesting_level() + 1; } /* * pre-close the list - close any open list item before closing the list */ void CHtmlTagListContainer::pre_close(CHtmlParser *parser) { parser->close_tag_if_open(get_list_tag_name()); } /* * parse an attribute */ HTML_attrerr CHtmlTagListContainer::set_attribute( CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { switch(attr_id) { case HTML_Attrib_compact: /* note that we're compact */ compact_ = TRUE; return HTML_attrerr_ok; default: /* return inherited default behavior */ return CHtmlTagBlock::set_attribute(parser, attr_id, val, vallen); } } /* * add attributes - we use this to add attributes for the list header */ int CHtmlTagListContainer::add_attrs(CHtmlFormatter *, CHtmlFontDesc *desc) { /* if the font is already bold, there's nothing to do */ if (desc->weight >= 700) return FALSE; /* set bold weight and indicate a change */ desc->weight = 700; return TRUE; } /* * format the start of the list */ void CHtmlTagListContainer::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { /* inherit default */ CHtmlTagBlock::format(win, formatter); /* * Insert a new line. If this is the outermost list, insert a blank * line's worth of vertical whitespace; if it's a nested list, just * go to a new line without additional spacing. */ if (get_list_nesting_level() == 1) break_ht_ = win->measure_text(formatter->get_font(), " ", 1, 0).y / 2; else break_ht_ = 0; formatter->add_line_spacing(break_ht_); /* add attributes for the list header */ fontcont_format(win, formatter); /* haven't started the list yet */ list_started_ = FALSE; } /* * begin the first list item - list items should call this before * formatting themselves to make sure we've set up properly for entry * into the list items */ void CHtmlTagListContainer::begin_list_item(CHtmlSysWin *win, CHtmlFormatter *formatter) { /* if I've already begun the list items, there's nothing to do */ if (list_started_) return; /* note that we've started the list */ list_started_ = TRUE; /* end the list header */ fontcont_format_exit(formatter); /* make sure we're on a new line */ formatter->add_line_spacing(0); /* indent the list by appropriate amount on the left */ if (is_list_indented()) formatter->push_margins(formatter->get_left_margin() + get_left_indent(win, formatter), formatter->get_right_margin()); } /* * get the default indenting */ long CHtmlTagListContainer::get_left_indent(CHtmlSysWin *win, CHtmlFormatter *formatter) const { static const textchar_t prefix[] = "99. "; static size_t prefix_len = sizeof(prefix)/sizeof(prefix[0]) - 1; /* make enough space for a 2-digit prefix */ return win->measure_text(formatter->get_font(), prefix, prefix_len, 0).x * 2; } void CHtmlTagListContainer::format_exit(CHtmlFormatter *formatter) { /* * if we never started the list, we only need to reset the list * header font attributes */ if (!list_started_) { /* remove the list header font settings */ fontcont_format_exit(formatter); } else { /* * insert a line break with the same amount of vertical * whitespace we used at the top of the list */ formatter->add_line_spacing(break_ht_); /* restore enclosing margins */ if (is_list_indented()) formatter->pop_margins(); } /* inherit default */ CHtmlTagBlock::format_exit(formatter); } /* ------------------------------------------------------------------------ */ /* * UL - unordered list */ HTML_attrerr CHtmlTagUL::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { switch(attr_id) { case HTML_Attrib_type: /* get the bullet type */ switch(type_ = parser->attrval_to_id(val, vallen)) { case HTML_Attrib_disc: case HTML_Attrib_square: case HTML_Attrib_circle: return HTML_attrerr_ok; default: return HTML_attrerr_inv_enum; } case HTML_Attrib_plain: type_ = HTML_Attrib_plain; return HTML_attrerr_ok; case HTML_Attrib_src: src_.set_url(val, vallen); return HTML_attrerr_ok; default: /* return inherited default behavior */ return CHtmlTagListContainer:: set_attribute(parser, attr_id, val, vallen); } } /* * format the list */ void CHtmlTagUL::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { /* inherit default */ CHtmlTagListContainer::format(win, formatter); /* * figure out what type of bullet to use if a style hasn't been set * explicitly */ if (type_ == HTML_Attrib_invalid) { switch(get_list_nesting_level()) { case 1: /* first level - use a disc */ type_ = HTML_Attrib_disc; break; case 2: /* second level - use a circle */ type_ = HTML_Attrib_circle; break; default: /* anything further - use a square */ type_ = HTML_Attrib_square; break; } } } /* * set the bullet for an item */ void CHtmlTagUL::set_item_bullet(CHtmlTagLI *item, CHtmlFormatter *) { /* tell the tag to use my bullet and my source image */ item->set_default_bullet(type_, &src_); } /* * add a display item for the list header */ void CHtmlTagUL::add_bullet_disp(CHtmlSysWin *win, CHtmlFormatter *formatter, CHtmlTagLI *item, long bullet_width) { /* tell the tag to add a bullet display item */ item->add_listhdr_bullet(win, formatter, bullet_width); } /* ------------------------------------------------------------------------ */ /* * OL - unordered list */ HTML_attrerr CHtmlTagOL::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { switch(attr_id) { case HTML_Attrib_type: /* get the numbering style */ if (vallen != 1) return HTML_attrerr_inv_enum; switch(style_ = val[0]) { case '1': case 'a': case 'A': case 'i': case 'I': return HTML_attrerr_ok; default: return HTML_attrerr_inv_enum; } case HTML_Attrib_plain: style_ = 'p'; return HTML_attrerr_ok; case HTML_Attrib_start: case HTML_Attrib_seqnum: /* get the starting number */ return set_number_attr(&start_, val, vallen); case HTML_Attrib_continue: /* continue where last ordered list left off */ continue_ = TRUE; return HTML_attrerr_ok; default: /* return inherited default behavior */ return CHtmlTagListContainer:: set_attribute(parser, attr_id, val, vallen); } } /* * get the default indenting */ long CHtmlTagOL::get_left_indent(CHtmlSysWin *win, CHtmlFormatter *formatter) const { const textchar_t *prefix; /* base our indent on our numbering style */ switch (style_) { case '1': default: prefix = "99. "; break; case 'a': prefix = "aa. "; break; case 'A': prefix = "AA. "; break; case 'i': prefix = "xxxiii. "; break; case 'I': prefix = "XXXIII. "; break; } /* make enough space for a 2-digit prefix */ return win->measure_text(formatter->get_font(), prefix, get_strlen(prefix), 0).x * 2; } /* * format the item */ void CHtmlTagOL::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { /* inherit default */ CHtmlTagListContainer::format(win, formatter); /* * set my value to the starting value - if we're continuing from a * previous list, ask the formatter for the ending value of the old * list, otherwise use the starting value from the attributes (or * the default, if the attribute wasn't set) */ if (continue_) cur_value_ = formatter->get_ol_continue_seqnum(); else cur_value_ = start_; /* if a numbering style isn't set, use regular numbers by default */ if (style_ == '\0') style_ = '1'; } /* * set the bullet for an item */ void CHtmlTagOL::set_item_bullet(CHtmlTagLI *item, CHtmlFormatter *formatter) { /* tell the tag to use my current value and style */ item->set_default_value(cur_value_); item->set_default_number_style(style_); /* set my value to the next value after this tag's value */ cur_value_ = item->get_value() + 1; /* tell the formatter the last numbered list item */ formatter->set_ol_continue_seqnum(cur_value_); } /* * add a display item for the list header */ void CHtmlTagOL::add_bullet_disp(CHtmlSysWin *win, CHtmlFormatter *formatter, CHtmlTagLI *item, long bullet_width) { /* tell the tag to add a numeric list header display item */ item->add_listhdr_number(win, formatter, bullet_width); } /* ------------------------------------------------------------------------ */ /* * LI - list item */ CHtmlTagLI::~CHtmlTagLI() { /* if we have an image, remove our reference on it */ if (image_ != 0) image_->remove_ref(); } void CHtmlTagLI::on_parse(CHtmlParser *parser) { /* if we're currently in a list header, end the list header */ parser->close_tag_if_open("LH"); /* if we're currently in a list item, end that list item */ parser->close_tag_if_open("LI"); /* add this tag as normal */ CHtmlTagContainer::on_parse(parser); /* * add a space to the text array, so we find word breaks between * list items */ parser->get_text_array()->append_text_noref(" ", 1); } /* * parse an attribute for the list item when we're in an ordered list */ HTML_attrerr CHtmlTagLI::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { switch(attr_id) { case HTML_Attrib_type: /* if it's a single character, it must be a numbering style */ if (vallen == 1) { switch(style_ = val[0]) { case '1': case 'a': case 'A': case 'i': case 'I': return HTML_attrerr_ok; default: return HTML_attrerr_inv_enum; } } /* it must be a bullet type */ switch(type_ = parser->attrval_to_id(val, vallen)) { case HTML_Attrib_disc: case HTML_Attrib_square: case HTML_Attrib_circle: return HTML_attrerr_ok; default: return HTML_attrerr_inv_enum; } case HTML_Attrib_plain: type_ = HTML_Attrib_plain; return HTML_attrerr_ok; case HTML_Attrib_value: /* parse the value, and note that it's been set */ value_set_ = TRUE; return set_number_attr(&value_, val, vallen); case HTML_Attrib_src: src_.set_url(val, vallen); return HTML_attrerr_ok; default: /* return inherited default behavior */ return CHtmlTagContainer::set_attribute(parser, attr_id, val, vallen); } } /* * format the list item */ void CHtmlTagLI::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { CHtmlTagListContainer *list; long bullet_width; /* get my list container - abort if not set */ list = get_container()->get_list_container(); if (list == 0) return; /* tell the list that we're starting a new item */ list->begin_list_item(win, formatter); /* move to a new line */ formatter->add_line_spacing(0); /* set my default bullet or value, as appropriate, if not specified */ list->set_item_bullet(this, formatter); /* * make room for the bullet in the left margin by temporarily * un-indenting by half of the list indent size; this temporary * un-indenting will only last to the end of this line */ bullet_width = list->get_left_indent(win, formatter) / 2; formatter->set_temp_margins(formatter->get_left_margin() - bullet_width, formatter->get_right_margin()); /* add the appropriate bullet */ list->add_bullet_disp(win, formatter, this, bullet_width); } /* * Add a list header number display item */ void CHtmlTagLI::add_listhdr_number(CHtmlSysWin *win, CHtmlFormatter *formatter, long bullet_width) { /* * if it's plain, add a plain bullet item, otherwise add a numeric * list header item */ if (type_ == HTML_Attrib_plain || style_ == 'p') formatter->add_disp_item(new (formatter) CHtmlDispListitemBullet( win, win->get_bullet_font(formatter->get_font()), bullet_width, type_, 0)); else formatter->add_disp_item(new (formatter) CHtmlDispListitemNumber( win, formatter->get_font(), bullet_width, style_, value_)); } /* * Add a list header bullet display item */ void CHtmlTagLI::add_listhdr_bullet(CHtmlSysWin *win, CHtmlFormatter *formatter, long bullet_width) { /* * if we have an image source, and we haven't loaded the image, do * so now */ if (win->get_html_show_graphics()) { if (src_.get_url() != 0) { /* load hte image if we haven't already */ if (image_ == 0) { /* load it or find it in the cache */ image_ = formatter->get_res_cache()-> find_or_create(win, formatter->get_res_finder(), &src_); /* if it's not a valid image, log an error */ if (image_ == 0 || image_->get_image() == 0) oshtml_dbg_printf("<LI>: resource %s is not a " "valid image\n", src_.get_url()); /* add a reference to it as long as we keep the pointer */ if (image_ != 0) image_->add_ref(); } } } else if (image_ != 0) { /* graphics aren't allowed - delete the image */ image_->remove_ref(); image_ = 0; } /* add a new bullet list item */ formatter->add_disp_item(new (formatter) CHtmlDispListitemBullet( win, win->get_bullet_font(formatter->get_font()), bullet_width, type_, image_)); } /* ------------------------------------------------------------------------ */ /* * Definition list items */ /* * parse a DT (definition term) item */ void CHtmlTagDT::on_parse(CHtmlParser *parser) { /* if there's an open DD item, implicitly close it */ parser->close_tag_if_open("DD"); /* parse this tag normally */ CHtmlTagContainer::on_parse(parser); } /* * format a DT (definition term) item */ void CHtmlTagDT::format(CHtmlSysWin *, CHtmlFormatter *formatter) { /* start a new line */ formatter->add_line_spacing(0); } /* * parse a DD (definition list definition) item */ void CHtmlTagDD::on_parse(CHtmlParser *parser) { /* if there's an open DT item, implicitly close it */ parser->close_tag_if_open("DT"); /* parse this tag normally */ CHtmlTagContainer::on_parse(parser); } /* * format a DD (definition list definition) item */ void CHtmlTagDD::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { CHtmlTagListContainer *list; /* get my list container - abort if not set */ list = get_container()->get_list_container(); if (list == 0) return; /* go to a new line */ formatter->add_line_spacing(0); /* indent the definition on the left */ formatter->push_margins(formatter->get_left_margin() + list->get_left_indent(win, formatter), formatter->get_right_margin()); } /* * finish formatting a DD item */ void CHtmlTagDD::format_exit(CHtmlFormatter *formatter) { /* go to a new line */ formatter->add_line_spacing(0); /* restore margins */ formatter->pop_margins(); } /* ------------------------------------------------------------------------ */ /* * Text tag */ CHtmlTagText::CHtmlTagText(CHtmlTextArray *arr, CHtmlTextBuffer *buf) { /* allocate space in the formatter's text array */ len_ = buf->getlen(); txtofs_ = arr->append_text(buf->getbuf(), len_); } CHtmlTagText::CHtmlTagText(CHtmlTextArray *arr, const textchar_t *buf, size_t buflen) { /* allocate space in the formatter's text array */ len_ = buflen; txtofs_ = arr->append_text(buf, buflen); } CHtmlTagText::~CHtmlTagText() { } /* * pre-delete for pruning the tree - delete my text from the text array */ void CHtmlTagText::prune_pre_delete(CHtmlTextArray *arr) { /* tell the text array to delete my text */ arr->delete_text(txtofs_, len_); } /* * Format the text element */ void CHtmlTagText::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { CHtmlDisp *textobj; const textchar_t *txt; /* get the address of the memory containing the text */ txt = formatter->get_text_ptr(txtofs_); /* create a new text display item, and add it to the formatter's list */ textobj = formatter->get_disp_factory()-> new_disp_text(win, formatter->get_font(), txt, len_, txtofs_); formatter->add_disp_item(textobj); } /* * Re-insert my text into the text array */ void CHtmlTagText::reinsert_text_array(CHtmlTextArray *arr) { unsigned long old_txtofs; /* remember our original text offset so we can delete it momentarily */ old_txtofs = txtofs_; /* * put a new copy of my text into the text array, and remember the * new location */ txtofs_ = arr->append_text(arr->get_text(txtofs_), len_); /* * delete our old copy (do this *after* we insert the new copy, to * make sure that we don't delete the underlying page accidentally * by leaving it unreferenced for a moment) */ arr->delete_text(old_txtofs, len_); } /* ------------------------------------------------------------------------ */ /* * Preformatted text tag */ CHtmlTagTextPre::CHtmlTagTextPre(int tab_size, CHtmlTextArray *arr, CHtmlTextBuffer *buf) { init_text(tab_size, arr, buf->getbuf(), buf->getlen()); } CHtmlTagTextPre::CHtmlTagTextPre(int tab_size, CHtmlTextArray *arr, const textchar_t *buf, size_t buflen) { init_text(tab_size, arr, buf, buflen); } /* * initialize the text - used by the constructor */ void CHtmlTagTextPre::init_text(int tab_size, CHtmlTextArray *arr, const textchar_t *buf, size_t buflen) { int col; int target; size_t expanded_len; const textchar_t *p; size_t rem; /* * First, figure out how much space we'll need. Since we need our * text to be in a single chunk, we'll need to know how much space * to reserve in advance, then we'll fill in the space after making * the reservation. */ for (p = buf, rem = buflen, col = 0, expanded_len = 0 ; rem != 0 ; ++p, --rem) { switch(*p) { case '\t': /* tab - figure out how much space we'll need for expansion */ target = (col/tab_size + 1) * tab_size; expanded_len += target - col; col = target; break; default: /* normal character - use one column */ ++col; ++expanded_len; break; } } /* * Reserve the necessary amount of space. This will ensure that our * entire chunk of text is stored contiguously, even though we'll be * adding it to the array with multiple append_text calls. */ txtofs_ = arr->reserve_space(expanded_len); /* * Now go through the buffer and append it to the text array. For * efficiency, append in as large pieces as we can. */ for (p = buf, rem = buflen, col = 0 ; rem != 0 ; ++p, --rem) { switch(*p) { case '\t': /* tab character - insert the expansion using spaces */ { size_t sprem; /* append the chunk from the last tab to here */ if (p != buf) arr->append_text_noref(buf, p - buf); /* figure out how many spaces we need to add */ target = (col/tab_size + 1) * tab_size; /* add the spaces */ for (sprem = (size_t)(target - col) ; sprem != 0 ; ) { static const textchar_t spaces[] = " "; size_t spcur; /* * figure out how many we can insert from our static * space array - insert as many as we can, up to the * array size */ spcur = sprem; if (spcur > sizeof(spaces)/sizeof(spaces[0]) - 1) spcur = sizeof(spaces)/sizeof(spaces[0]) - 1; /* insert the spaces */ arr->append_text_noref(spaces, spcur); /* deduct the amount we just did from the total */ sprem -= spcur; } /* * remember the location of the next character - it's * the start of the next chunk after this tab */ buf = p + 1; /* note that we're at the target column now */ col = target; } break; default: /* * simply count it for now - we'll actually append it when * we get to the next tab stop or the end of the string */ ++col; break; } } /* * If there's anything left over from the last tab, insert it now */ if (p != buf) arr->append_text_noref(buf, p - buf); /* * Remember my length as the fully expanded length */ len_ = expanded_len; } /* * Format the text element */ void CHtmlTagTextPre::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { CHtmlDisp *textobj; const textchar_t *txt; /* get the address of the memory containing the text */ txt = formatter->get_text_ptr(txtofs_); /* create a new text display item, and add it to the formatter's list */ textobj = formatter->get_disp_factory()-> new_disp_text_pre(win, formatter->get_font(), txt, len_, txtofs_); formatter->add_disp_item(textobj); } /* ------------------------------------------------------------------------ */ /* * Command input tag */ CHtmlTagTextInput::CHtmlTagTextInput(CHtmlTextArray *txtarr, const textchar_t *buf, size_t len) { /* * Since the caller will manage space until the command is * committed, do not allocate space yet; however, do get the * tentative array address, so that it's clear where we are in the * text stream. */ txtofs_ = txtarr->store_text_temp(0, 0); /* save the buffer information for editing */ len_ = len; buf_ = buf; } /* * Format the tag */ void CHtmlTagTextInput::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { CHtmlDispTextInput *textobj; /* see if I'm being edited */ if (buf_ != 0) { /* * I'm being edited - use my internal buffer for the text. * Create in the system heap (by using null as the formatter * placement argument for operator new) rather than in the * formatter's heap, because the formatter's heap doesn't ever * delete items individually; when we're open for editing, we'll * want to be able to repeatedly create and delete temporary * display items, so we want to make sure they really get * deleted. */ textobj = formatter->get_disp_factory()-> new_disp_text_input(win, formatter->get_font(), buf_, len_, txtofs_); /* * add the display item to the formatter's list, but tell it * that it's replacing the current command */ formatter->add_disp_item_input(textobj, this); } else { /* no longer being edited - use the default implementation */ CHtmlTagText::format(win, formatter); } } /* * Change to a new buffer. This can be used when we resume an interrupted * editing session. */ void CHtmlTagTextInput::change_buf(CHtmlFormatterInput *fmt, const textchar_t *buf) { /* remember my new buffer */ buf_ = buf; /* store the current text in the text array */ txtofs_ = fmt->store_input_text_temp(buf_, len_); } /* * Update the length of the underlying buffer */ void CHtmlTagTextInput::setlen(CHtmlFormatterInput *fmt, size_t len) { /* update our internal length */ len_ = len; /* store the current text in the text array */ txtofs_ = fmt->store_input_text_temp(buf_, len_); } /* * Commit the text. This stores it in the text array. */ void CHtmlTagTextInput::commit(CHtmlTextArray *arr) { /* store it in the array, and note the final address */ txtofs_ = arr->append_text(buf_, len_); /* forget the editing buffer, since we're done editing now */ buf_ = 0; } /* * Re-insert my text into the text array */ void CHtmlTagTextInput::reinsert_text_array(CHtmlTextArray *arr) { /* * if I haven't been committed, do nothing, since I'm not in my * final position in the text array yet; if I've been committed, * inherit the default behavior */ if (buf_ == 0) CHtmlTagText::reinsert_text_array(arr); } /* ------------------------------------------------------------------------ */ /* * Debugger source text */ CHtmlTagTextDebugsrc::CHtmlTagTextDebugsrc(unsigned long linenum, int tab_size, CHtmlTextArray *arr, const textchar_t *buf, size_t buflen) : CHtmlTagTextPre(tab_size, arr, buf, buflen) { /* remember my line number */ linenum_ = linenum; /* * add a newline to the text array, so we find a word break between * this line and the next one */ arr->append_text_noref("\n", 1); } void CHtmlTagTextDebugsrc::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { CHtmlDisp *textobj; const textchar_t *txt; /* get the address of the memory containing the text */ txt = formatter->get_text_ptr(txtofs_); /* create a new debug source text display item */ textobj = formatter->get_disp_factory()-> new_disp_text_debug(win, formatter->get_font(), txt, len_, txtofs_, linenum_); /* add the item on a new line */ formatter->add_disp_item_new_line(textobj); } /* ------------------------------------------------------------------------ */ /* * Soft hyphen element */ void CHtmlTagSoftHyphen::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { CHtmlDisp *disp; /* create a soft hyphen display item */ disp = formatter->get_disp_factory()-> new_disp_soft_hyphen(win, formatter->get_font(), formatter->get_text_ptr(txtofs_), len_, txtofs_); /* add the item to the display list */ formatter->add_disp_item(disp); } /* ------------------------------------------------------------------------ */ /* * Non-breaking space element */ void CHtmlTagNBSP::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { CHtmlDisp *disp; /* create a non-breaking space display item */ disp = formatter->get_disp_factory()-> new_disp_nbsp(win, formatter->get_font(), formatter->get_text_ptr(txtofs_), len_, txtofs_); /* add the item to the display list */ formatter->add_disp_item(disp); } /* ------------------------------------------------------------------------ */ /* * Special explicit-width space element */ void CHtmlTagSpace::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { CHtmlDisp *disp; /* create a typographical space display item */ disp = formatter->get_disp_factory()-> new_disp_space(win, formatter->get_font(), formatter->get_text_ptr(txtofs_), len_, txtofs_, wid_, pre_); /* add the item to the display list */ formatter->add_disp_item(disp); } /* ------------------------------------------------------------------------ */ /* * <BR> tag */ CHtmlTagBR::CHtmlTagBR(CHtmlParser *) { /* assume no CLEAR attribute is provided */ clear_ = HTML_Attrib_invalid; /* assume we'll use the default spacing */ height_ = 1; } void CHtmlTagBR::on_parse(CHtmlParser *parser) { /* inherit default */ CHtmlTag::on_parse(parser); /* * add a newline to the text array, so that this shows up as a word * break, and so that it works nicely with copying to the system * clipboard */ parser->get_text_array()->append_text_noref("\n", 1); /* skip any whitespace between the <BR> and the next text */ parser->begin_skip_sp(); } /* * Format a line break */ void CHtmlTagBR::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { /* process any CLEAR setting */ format_clear(formatter, clear_); /* * If we're on a blank line, or we're explicitly adding extra * spacing, add some vertical whitespace; otherwise, just end the * current line. */ if (height_ > 1 || formatter->last_was_newline()) { int ht; /* * Add some vertical whitespace -- space by half the font height * for each height unit we're adding. */ ht = win->measure_text(formatter->get_font(), " ", 1, 0).y / 2 * height_; formatter->add_line_spacing(ht); } /* end the current line, but don't add any vertical whitespace */ formatter->add_disp_item_new_line(new (formatter) CHtmlDispBreak(0)); } /* * set attributes */ HTML_attrerr CHtmlTagBR::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { switch(attr_id) { case HTML_Attrib_clear: return set_clear_attr(&clear_, parser, val, vallen); case HTML_Attrib_height: /* read the HEIGHT setting */ return set_number_attr(&height_, val, vallen); default: /* return inherited default behavior */ return CHtmlTag::set_attribute(parser, attr_id, val, vallen); } } /* ------------------------------------------------------------------------ */ /* * <Body> tag */ CHtmlTagBODY::~CHtmlTagBODY() { /* remove our reference on the image object, if we have one */ if (bg_image_ != 0) bg_image_->remove_ref(); } HTML_attrerr CHtmlTagBODY::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { HTML_attrerr err; switch(attr_id) { case HTML_Attrib_background: /* store the background image URL */ background_.set_url(val, vallen); break; case HTML_Attrib_bgcolor: /* parse the color */ err = set_color_attr(parser, &bgcolor_, val, vallen); /* if we got a valid color, note that we want to use it*/ if (err == HTML_attrerr_ok) use_bgcolor_ = TRUE; /* return the result of parsing the color */ return err; case HTML_Attrib_text: /* parse the color */ err = set_color_attr(parser, &textcolor_, val, vallen); /* if we got a valid color, note that we want to use it */ if (err == HTML_attrerr_ok) use_textcolor_ = TRUE; /* return the result of parsing the color */ return err; case HTML_Attrib_input: /* parse the color */ err = set_color_attr(parser, &inputcolor_, val, vallen); /* if we got a valid color, note that we want to use it */ if (err == HTML_attrerr_ok) use_inputcolor_ = TRUE; /* return the result of parsing the color */ return err; case HTML_Attrib_link: /* parse the link color */ err = set_color_attr(parser, &link_color_, val, vallen); if (err == HTML_attrerr_ok) use_link_color_ = TRUE; return err; case HTML_Attrib_vlink: /* parse the vlink color */ err = set_color_attr(parser, &vlink_color_, val, vallen); if (err == HTML_attrerr_ok) use_vlink_color_ = TRUE; return err; case HTML_Attrib_alink: /* parse the link color */ err = set_color_attr(parser, &alink_color_, val, vallen); if (err == HTML_attrerr_ok) use_alink_color_ = TRUE; return err; case HTML_Attrib_hlink: /* parse the link hover color */ err = set_color_attr(parser, &hlink_color_, val, vallen); if (err == HTML_attrerr_ok) use_hlink_color_ = TRUE; return err; default: /* return inherited default behavior */ return CHtmlTag::set_attribute(parser, attr_id, val, vallen); } /* if we didn't balk already, we must have been successful */ return HTML_attrerr_ok; } void CHtmlTagBODY::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { /* * if there's a background, use it, otherwise use the background * color */ if (background_.get_url() != 0) { /* load the image if we haven't already */ if (win->get_html_show_graphics()) { if (bg_image_ == 0) { /* load it into the cache, or find an existing copy */ bg_image_ = formatter->get_res_cache() ->find_or_create(win, formatter->get_res_finder(), &background_); /* if it's not a valid image, log an error */ if (bg_image_ == 0 || bg_image_->get_image() == 0) oshtml_dbg_printf("<BODY BACKGROUND>: " "resource %s is not a valid image\n", background_.get_url()); /* add a reference to it, since we're keeping a pointer */ if (bg_image_ != 0) bg_image_->add_ref(); } } else if (bg_image_ != 0) { /* graphics aren't allowed - delete the image */ bg_image_->remove_ref(); bg_image_ = 0; } /* set the background image */ win->set_html_bg_image(bg_image_); /* * if we have an image, link it up to the display site for the * special BODY display item */ if (bg_image_ != 0 && bg_image_->get_image() != 0 && formatter->get_body_disp() != 0) { /* * Set the display site that's part of the special BODY display * item to display the new image. This will form the two-way * link (i.e., it'll set the image's display site to the BODY * item). */ formatter->get_body_disp()->set_site_image(bg_image_); } } else { /* no background image */ win->set_html_bg_image(0); } /* tell the window to use my background color, if one is set */ win->set_html_bg_color(map_color(win, bgcolor_), !use_bgcolor_); /* tell the window to use my text color, if one is set */ win->set_html_text_color(map_color(win, textcolor_), !use_textcolor_); /* tell the window to use my input color, if one is set */ win->set_html_input_color(map_color(win, inputcolor_), !use_inputcolor_); /* tell the window what to do with the link colors */ win->set_html_link_colors( map_color(win, link_color_), !use_link_color_, map_color(win, vlink_color_), !use_vlink_color_, map_color(win, alink_color_), !use_alink_color_, map_color(win, hlink_color_), !use_hlink_color_); } /* ------------------------------------------------------------------------ */ /* * Block-level tag base class */ HTML_attrerr CHtmlTagBlock::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { switch(attr_id) { case HTML_Attrib_clear: return set_clear_attr(&clear_, parser, val, vallen); case HTML_Attrib_nowrap: nowrap_ = TRUE; break; default: /* return inherited default behavior */ return CHtmlTagContainer::set_attribute(parser, attr_id, val, vallen); } /* if we didn't balk already, we must have been successful */ return HTML_attrerr_ok; } void CHtmlTagBlock::format(CHtmlSysWin *, CHtmlFormatter *formatter) { /* if we're to move past marginal items, do so */ format_clear(formatter, clear_); /* set line wrapping mode */ old_wrap_ = formatter->get_line_wrapping(); formatter->set_line_wrapping(!nowrap_); } void CHtmlTagBlock::format_exit(CHtmlFormatter *formatter) { /* restore old line wrapping state */ formatter->set_line_wrapping(old_wrap_); } /* ------------------------------------------------------------------------ */ /* * <P> (paragraph) tag */ void CHtmlTagP::init(int isexplicit, CHtmlParser *prs) { /* specify no alignment by default */ align_ = HTML_Attrib_invalid; /* remember whether the paragraph break is explicit or not */ explicit_ = isexplicit; /* * add a newline to the text array, so that this shows up as a word * break, and so that it works nicely with copying to the system * clipboard */ prs->get_text_array()->append_text_noref("\n", 1); /* presume we'll wrap lines normally */ nowrap_ = FALSE; /* presume no CLEAR setting */ clear_ = HTML_Attrib_invalid; } /* process the ending tag */ void CHtmlTagP::process_end_tag(CHtmlParser *parser, const textchar_t *, size_t) { /* the </P> tag requires special treatment in the parser */ parser->end_p_tag(); } /* set an attribute in the <P> tag */ HTML_attrerr CHtmlTagP::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { HTML_Attrib_id_t val_id; switch(attr_id) { case HTML_Attrib_clear: return set_clear_attr(&clear_, parser, val, vallen); case HTML_Attrib_align: /* look up the alignment setting */ val_id = parser->attrval_to_id(val, vallen); /* make sure it's valid - return an error if not */ switch(val_id) { case HTML_Attrib_left: case HTML_Attrib_center: case HTML_Attrib_right: case HTML_Attrib_justify: align_ = val_id; break; default: return HTML_attrerr_inv_enum; } break; case HTML_Attrib_nowrap: nowrap_ = TRUE; break; default: /* return inherited default behavior */ return CHtmlTag::set_attribute(parser, attr_id, val, vallen); } /* if we didn't balk already, we must have been successful */ return HTML_attrerr_ok; } /* * Parse a paragraph marker */ void CHtmlTagP::on_parse(CHtmlParser *parser) { /* inherit default */ CHtmlTag::on_parse(parser); /* * skip any whitespace between the <P> and the first text in the new * paragraph */ parser->begin_skip_sp(); } /* * Format a paragraph marker */ void CHtmlTagP::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { int ht; /* process any CLEAR setting */ format_clear(formatter, clear_); /* * get the height of the break - if there's an explicit paragraph * break, add in a blank line, otherwise add no additional vertical * whitespace */ if (explicit_) { /* add in the height of two lines */ ht = win->measure_text(formatter->get_font(), " ", 1, 0).y / 2; } else { /* implicit break - don't add any extra vertical space */ ht = 0; } /* tell the formatter to go to a new line with this spacing */ formatter->add_line_spacing(ht); /* * Set the formatter alignment for this block element. Note that * we waited until after breaking the line (with add_line_spacing) * to do this, since we don't want the new alignment to affect the * tail end of the last paragraph. */ formatter->set_blk_alignment(align_); /* set line wrapping mode */ formatter->set_line_wrapping(!nowrap_); } /* ------------------------------------------------------------------------ */ /* * Division tags */ HTML_attrerr CHtmlTagDIV::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { HTML_Attrib_id_t val_id; HTML_attrerr err; switch(attr_id) { case HTML_Attrib_align: /* look up the alignment setting */ val_id = parser->attrval_to_id(val, vallen); /* make sure it's valid - return an error if not */ switch(val_id) { case HTML_Attrib_left: case HTML_Attrib_center: case HTML_Attrib_right: case HTML_Attrib_justify: align_ = val_id; break; default: return HTML_attrerr_inv_enum; } break; case HTML_Attrib_hover: /* * This sets the hover attributes for the division. The general * syntax is * * <DIV HOVER=item,item,...> * * where each 'item' can be one of the following: * * LINK - indicates that when the mouse is hovering anywhere over * the division, all links in the division should highlight with * the current <body hlink> color, if set, else with the standard * system hover color. * * LINK(fgcolor,bgcolor,decoration) - same as LINK, but also * specifies the colors and decorations to be shown for links * within the division when the mouse is hovering over the * division. bgcolor and decoration are optional, and can simply * be left blank to take the defaults; a comma can be ommitted if * everything following it is left blank. * * No whitespace is allowed within the attribute value. */ const textchar_t *start; const textchar_t *p; size_t rem; /* scan until we run out of items */ for (p = val, rem = vallen ;; ) { size_t len; /* * scan for the end of the keyword - it ends at a comma or open * paren (or the end of the string, of course) */ start = p; p = find_attr_token(p, &rem, ",("); /* note the length of the keyword */ len = p - start; /* determine which keyword we have */ if (len == 4 && memicmp(start, "link", 4) == 0) { /* note that we use HOVER=LINK mode */ hover_link_ = TRUE; /* if the hover color is specified, scan it */ if (rem != 0 && *p == '(') { /* skip the paren */ ++p, --rem; /* scan for the end of the foreground color keyword */ start = p; p = find_attr_token(p, &rem, ",)"); /* if we have a non-blank color name, use it */ if ((len = p - start) != 0) { /* parse the foreground color */ err = set_color_attr(parser, &hover_fg_, start, len); /* return failure on error */ if (err != HTML_attrerr_ok) return err; /* note that we have a foreground color */ use_hover_fg_ = TRUE; } /* parse the background color if it's there */ len = 0; if (rem != 0 && *p == ',') { /* skip the comma */ ++p, --rem; /* scan for the background color */ start = p; p = find_attr_token(p, &rem, ",)"); /* note the length */ len = p - start; } /* if we have a non-blank color name, use it */ if (len != 0) { /* parse the color */ err = set_color_attr(parser, &hover_bg_, start, len); /* return failure on error */ if (err != HTML_attrerr_ok) return err; /* note that we have a background color */ use_hover_bg_ = TRUE; } /* parse the decoration if it's there */ len = 0; if (rem != 0 && *p == ',') { /* skip the comma */ ++p, --rem; /* scan for the background color */ start = p; p = find_attr_token(p, &rem, ",)"); /* note the length */ len = p - start; } /* if we found a decoration, parse it */ if (len != 0) { if (len == 9 && memicmp(start, "underline", 9) == 0) hover_underline_ = TRUE; else return HTML_attrerr_inv_enum; } /* we need to be looking at a close paren now */ if (rem == 0 || *p != ')') return HTML_attrerr_missing_delim; /* skip the paren */ ++p, --rem; } } else { /* unrecognized keyword */ return HTML_attrerr_inv_enum; } /* if we're at the end of the attribute, we're done */ if (rem == 0) break; /* if there's a comma, skip it; if there's not, it's an error */ if (*p == ',') { /* skip the comma */ ++p, --rem; } else { /* we need a comma to separate items */ return HTML_attrerr_missing_delim; } } /* done with the tag */ break; default: /* return inherited default behavior */ return CHtmlTagBlock::set_attribute(parser, attr_id, val, vallen); } /* if we didn't balk already, we must have been successful */ return HTML_attrerr_ok; } /* * Parse */ void CHtmlTagDIV::on_parse(CHtmlParser *parser) { /* note our starting text offset */ start_txtofs_ = parser->get_text_array()->append_text(0, 0); /* inherit default */ CHtmlTagDIV_base::on_parse(parser); } /* * Finish parse */ void CHtmlTagDIV::on_close(CHtmlParser *parser) { /* note our ending text offset */ end_txtofs_ = parser->get_text_array()->append_text(0, 0); /* inherit default */ CHtmlTagDIV_base::on_close(parser); } /* * post-close processing */ void CHtmlTagDIV::post_close(CHtmlParser *parser) { /* inherit default */ CHtmlTagDIV_base::post_close(parser); /* * Add a 'relax' tag so that we have something to traverse into if * we're at the very end of the format list - this ensures that we'll * be closed properly when we're formatted at the end of the list. */ parser->append_tag(new CHtmlTagRelax()); } /* * Format a DIV tag */ void CHtmlTagDIV::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { /* do division formatting with our set alignment */ div_format(formatter, align_); /* * If this division is active - specifically, if it has the HOVER * attribute - we need to create a display list item for it. */ if (hover_link_) { CHtmlDispDIV *div; CHtmlDispLinkDIV *link; /* create a display item, and add it to the formatter's list */ div = new (formatter) CHtmlDispDIV(start_txtofs_, end_txtofs_); formatter->begin_div(div); /* create the DIV-link for the division */ link = new (formatter) CHtmlDispLinkDIV(div); /* set the link's hover colors */ if (use_hover_fg_) link->set_hover_fg(map_color(win, hover_fg_)); if (use_hover_bg_) link->set_hover_bg(map_color(win, hover_bg_)); if (hover_underline_) link->set_hover_underline(); /* * add the link to the formatter list - note that we don't add it * as a full hyperlink, since it's not a real link and so doesn't * generate any contents linked to it */ formatter->add_disp_item(link); } /* inherit default */ CHtmlTagDIV_base::format(win, formatter); } /* * End the current division */ void CHtmlTagDIV::format_exit(CHtmlFormatter *formatter) { /* if we're active, we need to close out our display list presence */ if (hover_link_) { /* tell the formatter we're exiting */ formatter->end_div(); } /* inherit default */ CHtmlTagDIV_base::format_exit(formatter); } void CHtmlTagDIV::prune_pre_delete(class CHtmlTextArray *arr) { /* delete our start and end markers */ arr->delete_text(start_txtofs_, 0); arr->delete_text(end_txtofs_, 0); } /* ------------------------------------------------------------------------ */ /* * Common base class for DIV-like tags - specifically DIV and CENTER */ /* * Start a new paragraph whenever we start a new division */ void CHtmlTagDIV_base::on_parse(CHtmlParser *parser) { /* start a new paragraph */ break_paragraph(parser, FALSE); /* inherit default */ CHtmlTagBlock::on_parse(parser); } /* * Start a new paragraph whenever we end a division */ void CHtmlTagDIV_base::on_close(CHtmlParser *parser) { /* inherit default */ CHtmlTagBlock::on_close(parser); /* start a new paragraph */ break_paragraph(parser, FALSE); } /* * do formatting for a DIV or CENTER tag */ void CHtmlTagDIV_base::div_format(CHtmlFormatter *formatter, HTML_Attrib_id_t alignment) { /* remember the old alignment */ old_align_ = formatter->get_div_alignment(); /* set the new alignment */ formatter->set_div_alignment(alignment); } /* * leave a DIV or CENTER section */ void CHtmlTagDIV_base::format_exit(CHtmlFormatter *formatter) { /* make sure we finish up the current line before changing alignment */ formatter->add_line_spacing(0); /* restore the old alignment */ formatter->set_div_alignment(old_align_); } /* ------------------------------------------------------------------------ */ /* * Font container add-in class */ /* * start a font container - set the appropriate font attributes in the * formatter */ void CHtmlFontContAddin::fontcont_format(CHtmlSysWin *win, CHtmlFormatter *formatter) { CHtmlFontDesc font_desc; /* remember the current font */ old_font_ = formatter->get_font(); /* get a description of the current font */ old_font_->get_font_desc(&font_desc); /* * add my attributes to the description, and note if doing so * changed anything */ if (add_attrs(formatter, &font_desc)) { CHtmlSysFont *new_font; /* we've changed the font - create a font for the new description */ new_font = win->get_font(&font_desc); /* install the new font as the current formatter font */ formatter->set_font(new_font); } } /* * end a font container - remove the font attributes */ void CHtmlFontContAddin::fontcont_format_exit(CHtmlFormatter *fmt) { /* * restore the font in effect before we added our attributes (if we * don't have an old font, don't bother - we must be unbalanced, so * that we're closing more times than we opened) */ if (old_font_ != 0) fmt->set_font(old_font_); } /* ------------------------------------------------------------------------ */ /* * Explicit font tag */ /* * format */ void CHtmlTagFontExplicit::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { /* remember the old font */ old_font_ = formatter->get_font(); /* establish the new font */ formatter->set_font(win->get_font(&font_)); } /* * exit formatting */ void CHtmlTagFontExplicit::format_exit(CHtmlFormatter *formatter) { /* * restore the old font, if we have one (if we don't, there must be an * error in the HTML, specifically an unbalanced tag; ignore such * cases for more graceful failure) */ if (old_font_ != 0) formatter->set_font(old_font_); } /* ------------------------------------------------------------------------ */ /* * Verbatim tags */ void CHtmlTagVerbatim::on_parse(CHtmlParser *parser) { /* start with a paragraph break */ break_paragraph(parser, FALSE); /* inherit default */ CHtmlTagContainer::on_parse(parser); /* put the parser into obey-whitespace mode */ parser->obey_whitespace(TRUE, FALSE); /* if necessary, put parser into literal-tags mode */ if (!translate_tags()) { parser->obey_markups(FALSE); parser->obey_end_markups(TRUE); } } void CHtmlTagVerbatim::on_close(CHtmlParser *parser) { /* inherit default */ CHtmlTagContainer::on_close(parser); /* tell the parser to stop obeying whitespace */ parser->obey_whitespace(FALSE, TRUE); /* tell the parser to resume translating tags if we there was a lapse */ if (!translate_tags()) parser->obey_markups(TRUE); } /* * listing font - set small monospaced font */ int CHtmlTagVerbatim::add_attrs(CHtmlFormatter *formatter, CHtmlFontDesc *desc) { int newsize; int change; /* presume no change */ change = FALSE; /* calculate the new size down from the BASEFONT size */ newsize = formatter->get_font_base_size() - get_font_size_decrement(); if (newsize < 1) newsize = 1; /* if we're already at this size, there's nothing to do */ if (desc->htmlsize != newsize) change = TRUE; /* if already in monospaced font and SMALL size, there's nothing to do */ if (!desc->fixed_pitch) change = TRUE; /* set monospaced font and new size */ desc->fixed_pitch = TRUE; desc->htmlsize = newsize; /* return change indication */ return change; } void CHtmlTagVerbatim::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { /* insert a full line break */ break_ht_ = win->measure_text(formatter->get_font(), " ", 1, 0).y; formatter->add_line_spacing(break_ht_); /* do font container entry */ fontcont_format(win, formatter); } void CHtmlTagVerbatim::format_exit(CHtmlFormatter *formatter) { /* exit font container */ fontcont_format_exit(formatter); /* insert vertical whitespace below */ formatter->add_line_spacing(break_ht_); } HTML_attrerr CHtmlTagVerbatim::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t id, const textchar_t *val, size_t vallen) { switch(id) { case HTML_Attrib_width: /* accept but ignore the WIDTH setting */ return set_number_attr(&width_, val, vallen); default: /* not handled here; inherit the default */ return CHtmlTagContainer::set_attribute(parser, id, val, vallen); } } /* ------------------------------------------------------------------------ */ /* * FONT markup */ HTML_attrerr CHtmlTagFONT::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t id, const textchar_t *val, size_t vallen) { HTML_attrerr err; switch(id) { case HTML_Attrib_size: /* check for relative notation */ if (vallen > 0 && (*val == '-' || *val == '+')) { /* remember the relative setting */ relative_ = *val; /* skip the +/- indicator */ ++val; --vallen; } else relative_ = 0; /* get the number */ err = set_number_attr(&size_, val, vallen); /* check the range */ if (err == HTML_attrerr_ok && (size_ < 1 || size_ > 7)) { /* leave size as default */ size_ = 3; /* report the error */ err = HTML_attrerr_out_of_range; } /* return the error indication */ return err; case HTML_Attrib_face: face_.set(val, vallen); return HTML_attrerr_ok; case HTML_Attrib_color: use_color_ = TRUE; return set_color_attr(parser, &color_, val, vallen); case HTML_Attrib_bgcolor: use_bgcolor_ = TRUE; return set_color_attr(parser, &bgcolor_, val, vallen); default: /* we don't recognize it explicitly; inherit default handling */ return CHtmlTagFontCont::set_attribute(parser, id, val, vallen); } } int CHtmlTagFONT::add_attrs(CHtmlFormatter *formatter, CHtmlFontDesc *desc) { int change; /* presume nothing will change */ change = FALSE; /* see what to do with the size */ if (relative_) { /* if it's a non-zero relative setting, apply it */ if (size_ != 0) { int oldsize; /* remember the original size */ oldsize = desc->htmlsize; /* apply the delta */ if (relative_ == '-') desc->htmlsize -= size_; else desc->htmlsize += size_; /* ensure it's within range */ if (desc->htmlsize < 1) desc->htmlsize = 1; else if (desc->htmlsize > 7) desc->htmlsize = 7; /* note if anything's changed */ change = (oldsize != desc->htmlsize); } } else { /* see if the size is the same as it already was */ if (desc->htmlsize != size_) { /* it's changed - apply the new size and note the change */ desc->htmlsize = size_; change = TRUE; } } /* use the face if one was provided */ if (face_.get() != 0) { size_t facelen = get_strlen(face_.get()); /* if the face name list has changed, use the new one */ if (get_strlen(desc->face) != facelen || memicmp(desc->face, face_.get(), facelen * sizeof(textchar_t)) != 0) { /* limit the length */ if (facelen >= sizeof(desc->face)/sizeof(desc->face[0])) facelen = sizeof(desc->face)/sizeof(desc->face[0]) - 1; /* copy and null-terminate the value */ memcpy(desc->face, face_.get(), facelen * sizeof(textchar_t)); desc->face[facelen] = '\0'; change = TRUE; } /* note that the face was set explicitly */ desc->face_set_explicitly = TRUE; } else { /* note that the face was not set explicitly */ desc->face_set_explicitly = FALSE; } /* apply the color if one was specified */ if (use_color_ && (color_ != desc->color || desc->default_color)) { /* set the new color and note the change */ desc->color = map_color(formatter->get_win(), color_); desc->default_color = FALSE; change = TRUE; } /* apply the background color if one was specified */ if (use_bgcolor_ && (bgcolor_ != desc->bgcolor || desc->default_bgcolor)) { /* set the new color and note the change */ desc->bgcolor = map_color(formatter->get_win(), bgcolor_); desc->default_bgcolor = FALSE; change = TRUE; } /* return the change indication */ return change; } /* ------------------------------------------------------------------------ */ /* * In-line attribute tags */ /* * HiliteON and HiliteOFF base class */ void CHtmlTagHILITE::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { CHtmlFontDesc desc; /* get the formatter's current font's description */ formatter->get_font()->get_font_desc(&desc); /* apply our changes; if we make any changes, set the new font */ if (set_attr(&desc)) formatter->set_font(win->get_font(&desc)); } /* * HiliteON */ int CHtmlTagHILITEON::set_attr(CHtmlFontDesc *desc) { /* if we're already using bold type, there's nothing to do */ if (desc->weight >= 700) return FALSE; /* set the new bold weight */ desc->weight = 700; /* indicate that we made a change */ return TRUE; } /* * HiliteOFF */ int CHtmlTagHILITEOFF::set_attr(CHtmlFontDesc *desc) { /* if we're already using non-bold type, there's nothing to do */ if (desc->weight <= 400) return FALSE; /* set the new normal weight */ desc->weight = 400; /* indicate that we made a change */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Font container markups. */ /* * CITE */ int CHtmlTagCITE::add_attrs(CHtmlFormatter *, CHtmlFontDesc *desc) { /* if CITE is already set, there's nothing to do */ if (desc->pe_cite) return FALSE; /* set CITE and indicate a change */ desc->pe_cite = TRUE; return TRUE; } /* * CODE */ int CHtmlTagCODE::add_attrs(CHtmlFormatter *, CHtmlFontDesc *desc) { /* if CODE is already set, there's nothing to do */ if (desc->pe_code) return FALSE; /* set CODE and indicate a change */ desc->pe_code = TRUE; return TRUE; } /* * EM */ int CHtmlTagEM::add_attrs(CHtmlFormatter *, CHtmlFontDesc *desc) { /* if EM is already set, there's nothing to do */ if (desc->pe_em) return FALSE; /* set EM and indicate a change */ desc->pe_em = TRUE; return TRUE; } /* * KBD */ int CHtmlTagKBD::add_attrs(CHtmlFormatter *, CHtmlFontDesc *desc) { /* if KBD is already set, there's nothing to do */ if (desc->pe_kbd) return FALSE; /* set KBD and indicate a change */ desc->pe_kbd = TRUE; return TRUE; } /* * SAMP */ int CHtmlTagSAMP::add_attrs(CHtmlFormatter *, CHtmlFontDesc *desc) { /* if SAMP is already set, there's nothing to do */ if (desc->pe_samp) return FALSE; /* set SAMP and indicate a change */ desc->pe_samp = TRUE; return TRUE; } /* * STRONG */ int CHtmlTagSTRONG::add_attrs(CHtmlFormatter *, CHtmlFontDesc *desc) { /* if STRONG is already set, there's nothing to do */ if (desc->pe_strong) return FALSE; /* set STRONG and indicate a change */ desc->pe_strong = TRUE; return TRUE; } /* * BIG */ int CHtmlTagBIG::add_attrs(CHtmlFormatter *formatter, CHtmlFontDesc *desc) { int newsize; /* calculate the new size as +1 from the BASEFONT size */ newsize = formatter->get_font_base_size() + 1; if (newsize > 7) newsize = 7; /* if we're already at this size, there's nothing to do */ if (desc->htmlsize == newsize) return FALSE; /* set the new size and indicate a change */ desc->htmlsize = newsize; return TRUE; } /* * SMALL */ int CHtmlTagSMALL::add_attrs(CHtmlFormatter *formatter, CHtmlFontDesc *desc) { int newsize; /* calculate the new size as -1 from the BASEFONT size */ newsize = formatter->get_font_base_size() - 1; if (newsize < 1) newsize = 1; /* if we're already at this size, there's nothing to do */ if (desc->htmlsize == newsize) return FALSE; /* set the new size and indicate a change */ desc->htmlsize = newsize; return TRUE; } /* * DFN */ int CHtmlTagDFN::add_attrs(CHtmlFormatter *, CHtmlFontDesc *desc) { /* if DFN is already set, there's nothing to do */ if (desc->pe_dfn) return FALSE; /* set DFN and indicate a change */ desc->pe_dfn = TRUE; return TRUE; } /* * VAR */ int CHtmlTagVAR::add_attrs(CHtmlFormatter *, CHtmlFontDesc *desc) { /* if VAR is already set, there's nothing to do */ if (desc->pe_var) return FALSE; /* set VAR and indicate a change */ desc->pe_var = TRUE; return TRUE; } /* * B - bold */ int CHtmlTagB::add_attrs(CHtmlFormatter *, CHtmlFontDesc *desc) { /* if the font is already bold, there's nothing to do */ if (desc->weight >= 700) return FALSE; /* set bold weight and indicate a change */ desc->weight = 700; return TRUE; } /* * I - italic */ int CHtmlTagI::add_attrs(CHtmlFormatter *, CHtmlFontDesc *desc) { /* if I is already set, there's nothing to do */ if (desc->italic) return FALSE; /* set italics and indicate a change */ desc->italic = TRUE; return TRUE; } /* * U - underline */ int CHtmlTagU::add_attrs(CHtmlFormatter *, CHtmlFontDesc *desc) { /* if U is already set, there's nothing to do */ if (desc->underline) return FALSE; /* set U and indicate a change */ desc->underline = TRUE; return TRUE; } /* * STRIKE */ int CHtmlTagSTRIKE::add_attrs(CHtmlFormatter *, CHtmlFontDesc *desc) { /* if STRIKE is already set, there's nothing to do */ if (desc->strikeout) return FALSE; /* set STRIKE and indicate a change */ desc->strikeout = TRUE; return TRUE; } /* * TT - typewriter font */ int CHtmlTagTT::add_attrs(CHtmlFormatter *, CHtmlFontDesc *desc) { /* if TT is already set, there's nothing to do */ if (desc->fixed_pitch) return FALSE; /* set TT and indicate a change */ desc->fixed_pitch = TRUE; return TRUE; } /* * SUB - subscript */ int CHtmlTagSUB::add_attrs(CHtmlFormatter *formatter, CHtmlFontDesc *desc) { int change; /* inherit SMALL attributes */ change = CHtmlTagSMALL::add_attrs(formatter, desc); /* add the SUBSCRIPT attribute as well */ if (!desc->subscript) { change |= TRUE; desc->subscript = TRUE; } /* return whether we made a change */ return change; } /* * SUP - superscript */ int CHtmlTagSUP::add_attrs(CHtmlFormatter *formatter, CHtmlFontDesc *desc) { int change; /* inherit SMALL attributes */ change = CHtmlTagSMALL::add_attrs(formatter, desc); /* add the SUPERSCRIPT attribute as well */ if (!desc->superscript) { change |= TRUE; desc->superscript = TRUE; } /* return whether we made a change */ return change; } /* ------------------------------------------------------------------------ */ /* * BASEFONT */ HTML_attrerr CHtmlTagBASEFONT::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { HTML_attrerr err; /* see what ID we're setting */ switch(attr_id) { case HTML_Attrib_size: /* read the SIZE attribute value */ err = set_number_attr(&size_, val, vallen); /* make sure it's in range */ if (err == HTML_attrerr_ok && (size_ < 1 || size_ > 7)) { /* leave the size as the default */ size_ = 3; /* indicate an error */ err = HTML_attrerr_out_of_range; } /* return the error indication */ return err; case HTML_Attrib_face: face_.set(val, vallen); return HTML_attrerr_ok; case HTML_Attrib_color: use_color_ = TRUE; return set_color_attr(parser, &color_, val, vallen); default: /* inherit default processing */ return CHtmlTag::set_attribute(parser, attr_id, val, vallen); } } /* * on formatting the tag, set the base font in the formatter */ void CHtmlTagBASEFONT::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { CHtmlSysFont *old_font; CHtmlFontDesc desc; int delta; /* * get the current font size, and note its HTML size relative to * the base font */ old_font = formatter->get_font(); old_font->get_font_desc(&desc); delta = desc.htmlsize - formatter->get_font_base_size(); /* tell the formatter to use the new base font */ formatter->set_font_base_size(size_); /* use the face if one was provided */ if (face_.get() != 0) { size_t facelen; /* limit the length to what will fit in the font descriptor */ facelen = get_strlen(face_.get()); if (facelen >= sizeof(desc.face)/sizeof(desc.face[0])) facelen = sizeof(desc.face)/sizeof(desc.face[0]) - 1; /* copy and null-terminate the value */ memcpy(desc.face, face_.get(), facelen * sizeof(textchar_t)); desc.face[facelen] = '\0'; } /* apply the color setting if one was provided */ if (use_color_) { desc.color = map_color(win, color_); desc.default_color = FALSE; } /* update the default font to conform to the new size and description */ desc.htmlsize = size_ + delta; formatter->set_font(win->get_font(&desc)); } /* ------------------------------------------------------------------------ */ /* * IMG - images */ CHtmlTagIMG::~CHtmlTagIMG() { /* remove our reference on the image object, if we have one */ if (image_ != 0) image_->remove_ref(); /* likewise the hover/active images */ if (h_image_ != 0) h_image_->remove_ref(); if (a_image_ != 0) a_image_->remove_ref(); } /* * pre-delete for pruning the tree - delete my ALT text from the text * array */ void CHtmlTagIMG::prune_pre_delete(CHtmlTextArray *arr) { /* tell the text array to delete my text */ if (alt_.get() != 0) arr->delete_text(alt_txtofs_, get_strlen(alt_.get())); } HTML_attrerr CHtmlTagIMG::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { HTML_Attrib_id_t val_id; /* see what ID we're setting */ switch(attr_id) { case HTML_Attrib_src: src_.set_url(val, vallen); break; case HTML_Attrib_asrc: asrc_.set_url(val, vallen); break; case HTML_Attrib_hsrc: hsrc_.set_url(val, vallen); break; case HTML_Attrib_alt: /* set the text */ alt_.set(val, vallen); /* insert it into our text array */ alt_txtofs_ = parser->get_text_array()->append_text(val, vallen); break; case HTML_Attrib_border: return set_number_attr(&border_, val, vallen); case HTML_Attrib_hspace: return set_number_attr(&hspace_, val, vallen); case HTML_Attrib_vspace: return set_number_attr(&vspace_, val, vallen); case HTML_Attrib_usemap: usemap_.set_url(val, vallen); break; case HTML_Attrib_ismap: ismap_ = TRUE; break; case HTML_Attrib_width: width_set_ = TRUE; return set_number_attr(&width_, val, vallen); case HTML_Attrib_height: height_set_ = TRUE; return set_number_attr(&height_, val, vallen); case HTML_Attrib_align: /* look up the alignment setting */ val_id = parser->attrval_to_id(val, vallen); /* make sure it's valid - return an error if not */ switch(val_id) { case HTML_Attrib_left: case HTML_Attrib_right: case HTML_Attrib_top: case HTML_Attrib_middle: case HTML_Attrib_bottom: align_ = val_id; break; default: return HTML_attrerr_inv_enum; } break; default: /* inherit default processing */ return CHtmlTag::set_attribute(parser, attr_id, val, vallen); } /* success */ return HTML_attrerr_ok; } /* * format the image */ void CHtmlTagIMG::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { CHtmlDispImg *disp; /* if we haven't loaded the image yet, load it now */ if (win->get_html_show_graphics()) { /* set up the main image, which is required for all <IMG> tags */ load_image(win, formatter, &image_, &src_, "SRC", TRUE); /* set up the optional active and hover images, if present */ load_image(win, formatter, &a_image_, &asrc_, "ASRC", FALSE); load_image(win, formatter, &h_image_, &hsrc_, "HSRC", FALSE); } else { /* * if we have an image hanging around from a previous pass * through the format list, delete it, since we aren't going to * display it any more */ if (image_ != 0) { /* graphics aren't allowed - delete the image */ image_->remove_ref(); image_ = 0; } if (a_image_ != 0) { a_image_->remove_ref(); a_image_ = 0; } if (h_image_ != 0) { h_image_->remove_ref(); h_image_ = 0; } /* if we have an ALT attribute, insert its text */ if (alt_.get() != 0) formatter-> add_disp_item(formatter->get_disp_factory()-> new_disp_text(win, formatter->get_font(), alt_.get(), get_strlen(alt_.get()), alt_txtofs_)); /* * don't insert anything further - we specifically don't want to * insert an image display object */ return; } /* create the display item */ disp = formatter->get_disp_factory()-> new_disp_img(win, image_, &alt_, align_, hspace_, vspace_, width_, width_set_, height_, height_set_, border_, &usemap_, ismap_); /* if we have hover or active images, set them in the display item */ if (h_image_ != 0 || a_image_ != 0) disp->set_extra_images(a_image_, h_image_); /* * Add the display item to the formatter's display list. If the * item is to be inserted into the current line, add it as we would * any other item; if instead we're going to float it to the left or * right margin and flow the text around it, we need to add it to * the deferred display list. */ switch(align_) { case HTML_Attrib_top: case HTML_Attrib_bottom: case HTML_Attrib_middle: /* insert the item in-line */ formatter->add_disp_item(disp); break; case HTML_Attrib_left: case HTML_Attrib_right: /* * the item will float to the margin with text flowing around it * -- defer inserting it into the display list until we start a * new line */ formatter->add_disp_item_deferred(disp); break; default: /* other cases should never occur; ignore them if they do */ break; } } /* * Load an image resource associated with the tag. */ void CHtmlTagIMG::load_image(CHtmlSysWin *win, CHtmlFormatter *formatter, CHtmlResCacheObject **image, const CHtmlUrl *url, const char *attr_name, int mandatory) { /* if the image is optional and there's no URL, skip it */ if (!mandatory && (url->get_url() == 0 || url->get_url()[0] == 0)) return; /* if we don't already have an object for this image, try loading it */ if (*image == 0) { /* * find the image in the cache if possible, or load it and add it * to the cache */ *image = formatter->get_res_cache()-> find_or_create(win, formatter->get_res_finder(), url); /* if it's not a valid image, log an error */ if (*image == 0 || (*image)->get_image() == 0) oshtml_dbg_printf("<IMG %s>: resource %s is not a valid image\n", attr_name, url->get_url()); /* count our reference to the cache object as long as we keep it */ if (*image != 0) (*image)->add_ref(); } } /* ------------------------------------------------------------------------ */ /* * NOBR - don't allow line breaks within the text spanned by the * <nobr>...</nobr> sequence. */ void CHtmlTagNOBR::format(CHtmlSysWin *, CHtmlFormatter *formatter) { /* save old wrapping state, and set new state */ old_wrap_ = formatter->get_line_wrapping(); formatter->set_line_wrapping(FALSE); /* add a line-wrap tag to the display stream, so it knows about it */ formatter->add_disp_item(new (formatter) CHtmlDispNOBR(TRUE)); } void CHtmlTagNOBR::format_exit(CHtmlFormatter *formatter) { /* restore old line wrapping state */ formatter->set_line_wrapping(old_wrap_); /* add a line-wrap tag to the display stream to restore the old status */ formatter->add_disp_item(new (formatter) CHtmlDispNOBR(!old_wrap_)); } /* ------------------------------------------------------------------------ */ /* * WRAP tag */ /* * parse an attribute */ HTML_attrerr CHtmlTagWRAP::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { /* check the attribute */ switch(attr_id) { case HTML_Attrib_char: /* they want character-wrapping mode */ char_wrap_mode_ = TRUE; break; case HTML_Attrib_word: /* they want word-wrapping mode */ char_wrap_mode_ = FALSE; break; default: /* it's not one of ourse, so inherit the base class processing */ return CHtmlTag::set_attribute(parser, attr_id, val, vallen); } /* success */ return HTML_attrerr_ok; } /* * enter the tag during formatting */ void CHtmlTagWRAP::format(CHtmlSysWin *, CHtmlFormatter *formatter) { /* * Set the new wrapping mode. Note that we're an in-line mode switch, * not a container, so there's no need to remember the previous mode. */ formatter->set_char_wrap_mode(char_wrap_mode_); } /* ------------------------------------------------------------------------ */ /* * BANNER */ CHtmlTagBANNER::~CHtmlTagBANNER() { /* * if there's anything in my banner list, move it back into my * regular contents list, so that it's deleted normally */ if (banner_first_ != 0) { contents_first_ = banner_first_; banner_first_ = 0; } } /* * pre-delete on pruning the tree - notify any children, either in the * regular list or in the banner list */ void CHtmlTagBANNER::prune_pre_delete(CHtmlTextArray *arr) { CHtmlTag *cur; /* if there's anything in my banner child list, notify it */ for (cur = banner_first_ ; cur != 0 ; cur = cur->get_next_tag()) cur->prune_pre_delete(arr); /* inherit default handling */ CHtmlTagContainer::prune_pre_delete(arr); } /* * Receive notification that the banner is now obsolete. The formatter * will call this routine whenever it finds that a subsequent BANNER tag * in the same format list uses the same banner -- when this happens, * the banner involved will always be overwritten by the subsequent tag. * We'll discard our contents and skip doing any work to format the * banner on future passes through the format list. */ void CHtmlTagBANNER::notify_obsolete(CHtmlTextArray *arr) { /* restore my usual child list */ if (banner_first_ != 0) { contents_first_ = banner_first_; banner_first_ = 0; } /* remove references on any contained text items */ prune_pre_delete(arr); /* dump my children -- we'll never need to format the contents again */ delete_contents(); } void CHtmlTagBANNER::on_parse(CHtmlParser *parser) { /* * If the REMOVE attribute was set, it means that this isn't a * container tag after all -- in this case, don't add myself as a * container, but just as an ordinary tag. */ if (remove_) CHtmlTag::on_parse(parser); else CHtmlTagContainer::on_parse(parser); /* * insert a newline before the banner, so lines in the main window * before the banner don't run into the banner text for word and * line breaking purposes */ parser->get_text_array()->append_text_noref("\n", 1); /* skip whitespace at the start of the banner's body */ parser->begin_skip_sp(); } /* * close the BANNER tag */ void CHtmlTagBANNER::on_close(CHtmlParser *parser) { /* inherit default behavior, if we're a container */ if (!remove_) CHtmlTagContainer::on_close(parser); /* * insert a newline after the banner, for the same reason we insert * one before the banner */ parser->get_text_array()->append_text_noref("\n", 1); } HTML_attrerr CHtmlTagBANNER:: set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { HTML_Attrib_id_t val_id; /* see what ID we're setting */ switch(attr_id) { case HTML_Attrib_id: /* store the ID */ id_.set(val, vallen); break; case HTML_Attrib_remove: /* note that this is a REMOVE tag */ remove_ = TRUE; break; case HTML_Attrib_removeall: /* this is a REMOVEALL tag */ remove_ = TRUE; removeall_ = TRUE; case HTML_Attrib_height: /* note that the height has been explicitly set */ height_set_ = TRUE; /* check for special values */ if (parser->attrval_to_id(val, vallen) == HTML_Attrib_previous) { /* note that we're to use the existing banner height */ height_prev_ = TRUE; } else { /* set the numeric or percentage height */ return set_number_or_pct_attr(&height_, &height_pct_, val, vallen); } break; case HTML_Attrib_width: /* note that the width has been explicitly set */ width_set_ = TRUE; /* check for special values */ if (parser->attrval_to_id(val, vallen) == HTML_Attrib_previous) { /* note that we're to use the existing banner height */ width_prev_ = TRUE; } else { /* set the numeric or percentage width */ return set_number_or_pct_attr(&width_, &width_pct_, val, vallen); } break; case HTML_Attrib_border: border_ = TRUE; break; case HTML_Attrib_align: /* get the alignment setting */ val_id = parser->attrval_to_id(val, vallen); /* map it to the appropriate setting */ switch(val_id) { case HTML_Attrib_top: alignment_ = HTML_BANNERWIN_POS_TOP; break; case HTML_Attrib_bottom: alignment_ = HTML_BANNERWIN_POS_BOTTOM; break; case HTML_Attrib_left: alignment_ = HTML_BANNERWIN_POS_LEFT; break; case HTML_Attrib_right: alignment_ = HTML_BANNERWIN_POS_RIGHT; break; default: return HTML_attrerr_inv_enum; } break; default: /* inherit default processing */ return CHtmlTagContainer::set_attribute(parser, attr_id, val, vallen); } /* success */ return HTML_attrerr_ok; } void CHtmlTagBANNER::format(CHtmlSysWin *, CHtmlFormatter *formatter) { /* * If this is a <BANNER REMOVE> tag, find the existing banner and * delete it. If the banner isn't finished yet, ignore it for now. */ if (remove_) { if (!obsolete_) { /* check to see if we're removing one banner or all of them */ if (removeall_) { /* tell the formatter to remove all banners */ formatter->remove_all_banners(TRUE); } else { /* tell the formatter to remove this banner */ formatter->remove_banner(id_.get()); } /* * we don't need to apply this again in future formatting * passes, because the formatter will go back into the * format list and remove the previous banner */ obsolete_ = TRUE; } } else { /* * If I have any contents, remove them and move them into the * banner sublist instead. This will prevent the banner's * contents from showing up in my main list. If I've already * set up my banner sublist, it means I've been formatted before * and we don't need to repeat this step. */ if (banner_first_ == 0) { banner_first_ = contents_first_; contents_first_ = 0; } /* if I don't have any contents, there's nothing to do */ if (banner_first_ == 0) return; /* tell the formatter to start formatting the banner */ if (!formatter->format_banner(id_.get(), height_, height_set_, height_prev_, height_pct_, width_, width_set_, width_prev_, width_pct_, this)) { /* * That failed -- we can't format a real banner. Simply leave * the contents in the separate list, so that all of our * contents are simply ignored. (We wouldn't want our * contents showing up in our main window, so it's better to * just leave them unshown.) */ } } } void CHtmlTagBANNER::format_exit(CHtmlFormatter *formatter) { /* * This will only be called from within the subformatter. Tell our * subformatter to stop formatting, since we are no longer within * its contents. */ formatter->end_banner(); } /* ------------------------------------------------------------------------ */ /* * ABOUTBOX tag - the about box */ HTML_attrerr CHtmlTagABOUTBOX:: set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { switch(attr_id) { case HTML_Attrib_id: case HTML_Attrib_remove: case HTML_Attrib_height: case HTML_Attrib_border: /* * none of these BANNER attributes are accepted by ABOUTBOX, so * inherit the container default instead */ return CHtmlTagContainer::set_attribute(parser, attr_id, val, vallen); default: /* inherit default from BANNER */ return CHtmlTagBANNER::set_attribute(parser, attr_id, val, vallen); } } /* ------------------------------------------------------------------------ */ /* * TAB */ CHtmlTagTAB::CHtmlTagTAB(CHtmlParser *) { /* set default attribute values */ align_ = HTML_Attrib_invalid; define_ = FALSE; to_ = FALSE; dp_ = 0; use_indent_ = FALSE; use_multiple_ = FALSE; indent_ = 0; txtofs_ = 0; } /* * when pruning the tree, delete my text from the text array */ void CHtmlTagTAB::prune_pre_delete(CHtmlTextArray *arr) { arr->delete_text(txtofs_, 1); } void CHtmlTagTAB::on_parse(CHtmlParser *parser) { /* inherit default */ CHtmlTag::on_parse(parser); /* add a space to the buffer, in case we need to add it */ txtofs_ = parser->get_text_array()->append_text(" ", 1); } /* * parse a TAB attribute */ HTML_attrerr CHtmlTagTAB:: set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { HTML_Attrib_id_t val_id; /* see what ID we're setting */ switch(attr_id) { case HTML_Attrib_id: /* store the ID */ id_.set(val, vallen); define_ = TRUE; break; case HTML_Attrib_to: /* remember where we're tabbing to */ id_.set(val, vallen); to_ = TRUE; break; case HTML_Attrib_dp: /* remember the decimal point character */ if (vallen > 0) dp_ = val[0]; break; case HTML_Attrib_align: switch(val_id = parser->attrval_to_id(val, vallen)) { case HTML_Attrib_left: case HTML_Attrib_center: case HTML_Attrib_right: case HTML_Attrib_decimal: /* these are all valid */ align_ = val_id; break; default: return HTML_attrerr_inv_enum; } break; case HTML_Attrib_indent: use_indent_ = TRUE; return set_number_attr(&indent_, val, vallen); case HTML_Attrib_multiple: use_multiple_ = TRUE; return set_number_attr(&indent_, val, vallen); default: /* inherit default processing */ return CHtmlTag::set_attribute(parser, attr_id, val, vallen); } /* success */ return HTML_attrerr_ok; } /* * format a tab */ void CHtmlTagTAB::format(CHtmlSysWin *, CHtmlFormatter *formatter) { /* if this defines a tab, tell the formatter about it */ if (define_) { formatter->set_tab(id_.get(), align_, dp_); return; } /* let the formatter deal with it */ formatter->tab_to(this); } /* ------------------------------------------------------------------------ */ /* * <Q> - quote */ /* * open parsing - add my open quote to the text array */ void CHtmlTagQ::on_parse(CHtmlParser *parser) { int level; unsigned int html_open; const textchar_t *ascii_quote; /* inherit default */ CHtmlTagContainer::on_parse(parser); /* figure out what kind of quotes to use, based on our nesting level */ level = get_quote_nesting_level(); if ((level & 1) == 0) { /* * I'm the outermost quote, or the second quote in, or the * fourth quote in, and so on - use double quotes */ html_open = 8220; ascii_quote = "\""; } else { /* * I'm the first one in, or the third one in, and so on - use * single quotes */ html_open = 8216; ascii_quote = "'"; } /* set up our open quote */ openq_.on_parse(parser, html_open, ascii_quote); /* we effectively insert text, so keep adjacent source whitespace */ parser->end_skip_sp(); } /* * close parsing - add my close quote to the text array */ void CHtmlTagQ::on_close(CHtmlParser *parser) { int level; unsigned int html_close; const textchar_t *ascii_quote; /* inherit default */ CHtmlTagContainer::on_close(parser); /* figure out what kind of quotes to use, based on our nesting level */ level = get_quote_nesting_level(); if ((level & 1) == 0) { /* * I'm the outermost quote, or the second quote in, or the fourth * quote in, and so on - use double quotes */ html_close = 8221; ascii_quote = "\""; } else { /* * I'm the first one in, or the third one in, and so on - use * single quotes */ html_close = 8217; ascii_quote = "'"; } /* add my close quote */ closeq_.on_parse(parser, html_close, ascii_quote); /* we effectively insert text, so keep adjacent source whitespace */ parser->end_skip_sp(); } /* * format the tag - add my open quote to the display list */ void CHtmlTagQ::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { /* add a display item for my quote */ openq_.format(win, formatter); } /* * end formatting - add my close quote to the display list */ void CHtmlTagQ::format_exit(CHtmlFormatter *formatter) { /* add a display item for my quote */ closeq_.format(formatter->get_win(), formatter); } /* ------------------------------------------------------------------------ */ /* * <A> (anchor) */ HTML_attrerr CHtmlTagA::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { HTML_attrerr err; const textchar_t *p; size_t rem; switch(attr_id) { case HTML_Attrib_href: /* store the hyperlink reference */ href_.set_url(val, vallen); break; case HTML_Attrib_title: /* store the title */ title_.set(val, vallen); break; case HTML_Attrib_name: case HTML_Attrib_rel: case HTML_Attrib_rev: /* accept but ignore these attributes */ break; case HTML_Attrib_plain: /* set rendering style to 'plain' */ style_ = LINK_STYLE_PLAIN; break; case HTML_Attrib_hidden: /* set link style to 'hidden' */ style_ = LINK_STYLE_HIDDEN; break; case HTML_Attrib_forced: /* * Set rendering style to 'forced'. Note that this is, for now, an * UNDOCUMENTED, UNSUPPORTED extension - it is specifically for * internal use by the system itself, not for use by games. * Support for this attribute is NOT universal, so games shouldn't * use it. */ style_ = LINK_STYLE_FORCED; break; case HTML_Attrib_hover: /* * Set the hovering foreground color, and optionally the background * color. First, scan for a comma, to see if there's a background * color specified. */ rem = vallen; p = find_attr_token(val, &rem, ","); /* if there's a color, use it */ if (p - val != 0) { /* scan the foreground color */ err = set_color_attr(parser, &hover_fg_, val, p - val); /* return failure on error */ if (err != HTML_attrerr_ok) return err; /* note that we're using a foreground hover color */ use_hover_fg_ = TRUE; } /* if there's a comma, skip it */ if (rem != 0) ++p, --rem; /* scan for a comma, to see if there's a decoration flag */ val = p; p = find_attr_token(p, &rem, ","); /* if there's a background color, scan it */ if (p - val != 0) { /* scan the color */ err = set_color_attr(parser, &hover_bg_, val, p - val); /* return failure on error */ if (err != HTML_attrerr_ok) return err; /* note that we're using a background hover color */ use_hover_bg_ = TRUE; } /* if there's a comma, skip it */ if (rem != 0) ++p, --rem; /* if there's an added decoration, scan it */ if (rem != 0) { /* scan the decoration name */ if (rem == 9 && memicmp(p, "underline", 9) == 0) hover_underline_ = TRUE; else err = HTML_attrerr_inv_enum; } /* return the parsing result */ return err; case HTML_Attrib_noenter: noenter_ = TRUE; break; case HTML_Attrib_append: append_ = TRUE; break; default: /* return inherited default behavior */ return CHtmlTag::set_attribute(parser, attr_id, val, vallen); } /* if we didn't balk already, we must have been successful */ return HTML_attrerr_ok; } void CHtmlTagA::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { CHtmlDispLink *link; /* handle the font container formatting */ fontcont_format(win, formatter); /* create a link display item */ link = new (formatter) CHtmlDispLink(style_, append_, noenter_, &href_, title_.get(), title_.get() == 0 ? 0 : get_strlen(title_.get())); /* set the HOVER attributes, if they're used */ if (use_hover_fg_) link->set_hover_fg(map_color(win, hover_fg_)); if (use_hover_bg_) link->set_hover_bg(map_color(win, hover_bg_)); if (hover_underline_) link->set_hover_underline(); /* set it as the current link in the formatter */ formatter->add_disp_item_link(link); } void CHtmlTagA::format_exit(CHtmlFormatter *formatter) { /* tell the formatter that we're exiting the link */ formatter->end_link(); /* exit the font container */ fontcont_format_exit(formatter); } /* * add font container attributes */ int CHtmlTagA::add_attrs(CHtmlFormatter *formatter, CHtmlFontDesc *desc) { /* * Within a hyperlink, ignore any containing font color, because we * want to use the appropriate link color by default. So, explicitly * set the default_color_ attribute in the font descriptor: an <A> is * like an explicit <FONT COLOR> tag specifying the appropriate link * color. * * In a HIDDEN or PLAIN hyperlink, though, we DO inherit the color from * the surrounding text, since these aren't drawn as hyperlinks. */ if (href_.get_url() != 0 && get_strlen(href_.get_url()) && !desc->default_color && style_ != LINK_STYLE_PLAIN && style_ != LINK_STYLE_HIDDEN) { /* explicitly set the default color */ desc->default_color = TRUE; /* we changed the description */ return TRUE; } /* no changes to the font */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * MAP tag and subtags */ HTML_attrerr CHtmlTagMAP::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { switch(attr_id) { case HTML_Attrib_name: /* set my name */ name_.set(val, vallen); break; default: return CHtmlTagContainer::set_attribute(parser, attr_id, val, vallen); } /* no errors */ return HTML_attrerr_ok; } void CHtmlTagMAP::format(CHtmlSysWin *, CHtmlFormatter *formatter) { formatter->begin_map(name_.get(), get_strlen(name_.get())); } void CHtmlTagMAP::format_exit(CHtmlFormatter *formatter) { formatter->end_map(); } HTML_attrerr CHtmlTagAREA::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { HTML_Attrib_id_t val_id; HTML_attrerr err; int i; int pct; switch(attr_id) { case HTML_Attrib_shape: switch(val_id = parser->attrval_to_id(val, vallen)) { case HTML_Attrib_rect: case HTML_Attrib_circle: case HTML_Attrib_poly: /* these are all valid */ shape_ = val_id; break; default: return HTML_attrerr_inv_enum; } break; case HTML_Attrib_coords: switch(shape_) { case HTML_Attrib_invalid: case HTML_Attrib_rect: shape_ = HTML_Attrib_rect; for (i = 0 ; i < 4 ; ++i) { /* set the next coordinate */ err = set_coord_attr(&coords_[i].val_, &pct, &val, &vallen); if (err != HTML_attrerr_ok) return err; /* set percentage indicator */ coords_[i].pct_ = pct; } coord_cnt_ = 4; break; case HTML_Attrib_circle: for (i = 0 ; i < 3 ; ++i) { /* set the next coordinate */ err = set_coord_attr(&coords_[i].val_, &pct, &val, &vallen); if (err != HTML_attrerr_ok) return err; /* set percentage indicator */ coords_[i].pct_ = pct; } coord_cnt_ = 3; break; case HTML_Attrib_poly: for (i = 0 ; i < CHtmlTagAREA_max_coords ; ++i) { /* set the next coordinate */ err = set_coord_attr(&coords_[i].val_, &pct, &val, &vallen); if (err != HTML_attrerr_ok) return err; /* set percentage indicator */ coords_[i].pct_ = pct; /* if we've run out of coordinates, stop */ if (vallen == 0) break; } /* make sure we used everything in the list */ if (vallen != 0) return HTML_attrerr_too_many_coords; /* make sure we got an even number of coordinates */ if ((i & 1) != 0) return HTML_attrerr_odd_coords; /* store the count */ coord_cnt_ = i; break; default: /* other cases should never occur; ignore them if they do */ break; } break; case HTML_Attrib_href: href_.set_url(val, vallen); nohref_ = FALSE; break; case HTML_Attrib_nohref: nohref_ = TRUE; break; case HTML_Attrib_alt: alt_.set(val, vallen); break; case HTML_Attrib_append: append_ = TRUE; break; case HTML_Attrib_noenter: noenter_ = TRUE; break; default: return CHtmlTag::set_attribute(parser, attr_id, val, vallen); } /* success */ return HTML_attrerr_ok; } void CHtmlTagAREA::format(CHtmlSysWin *, CHtmlFormatter *formatter) { /* add the area to the formatter's current map */ formatter->add_map_area(shape_, nohref_ ? 0 : &href_, alt_.get(), alt_.get() == 0 ? 0 : get_strlen(alt_.get()), coords_, coord_cnt_, append_, noenter_); } /* ------------------------------------------------------------------------ */ /* * Column list entry for TABLE tag */ CHtmlTag_column::CHtmlTag_column() { /* set up an initial cell list */ cells_used_ = 0; cells_alloced_ = 10; cells_ = (CHtmlTag_cell **) th_malloc(cells_alloced_ * sizeof(CHtmlTag_cell *)); /* we have no width measurements yet */ min_width_ = max_width_ = 0; pix_width_ = 0; pct_width_ = 0; /* width is not yet known */ width_ = 0; } CHtmlTag_column::~CHtmlTag_column() { size_t i; CHtmlTag_cell **cell; /* delete each allocated cell */ for (i = 0, cell = cells_ ; i < cells_used_ ; ++i, ++cell) delete *cell; /* delete the cell array */ th_free(cells_); } /* * Get a cell */ CHtmlTag_cell *CHtmlTag_column::get_cell(size_t rownum) { /* if the row is allocated, return it directly */ if (rownum < cells_used_) return cells_[rownum]; /* expand the slot list if necessary to make room for the newrow */ if (rownum >= cells_alloced_) { cells_alloced_ = rownum + 5; cells_ = (CHtmlTag_cell **) th_realloc(cells_, cells_alloced_ * sizeof(CHtmlTag_cell *)); } /* allocate entries up to the given point */ while (rownum >= cells_used_) cells_[cells_used_++] = new CHtmlTag_cell(); /* return the new column */ return cells_[rownum]; } /* ------------------------------------------------------------------------ */ /* * Table - base table image mix-in */ CHtmlTableImage::~CHtmlTableImage() { /* remove our reference on the image object, if we have one */ if (bg_image_ != 0) bg_image_->remove_ref(); } /* * load the background image during formatting as needed */ void CHtmlTableImage::format_load_bg(CHtmlSysWin *win, CHtmlFormatter *fmt) { /* if we're in graphics mode, load the image */ if (win->get_html_show_graphics()) { /* if we already have an image loaded, there's nothing to do */ if (bg_image_ != 0) return; /* if we don't have an image at all, there's nothing to do */ if (background_.get_url() == 0 || background_.get_url()[0] == '\0') return; /* try loading the image */ bg_image_ = fmt->get_res_cache()-> find_or_create(win, fmt->get_res_finder(), &background_); /* if we didn't load it, log an error */ if (bg_image_ == 0 || bg_image_->get_image() == 0) oshtml_dbg_printf("<%s background>: resource %s is not a valid " "image\n", get_tag_name(), background_.get_url()); /* if we got a cache object, count our reference to it */ if (bg_image_ != 0) bg_image_->add_ref(); } else { /* we're not in graphics mode - release any image we're holding */ if (bg_image_ != 0) { /* release and forget our reference on the image */ bg_image_->remove_ref(); bg_image_ = 0; } } } /* ------------------------------------------------------------------------ */ /* * Tables - TABLE tag */ CHtmlTagTABLE::CHtmlTagTABLE(CHtmlParser *) : CHtmlTagContainer(), CHtmlTableImage() { /* no alignment assigned */ align_ = HTML_Attrib_invalid; /* no width or height specified yet */ width_set_ = height_set_ = FALSE; width_pct_ = height_pct_ = FALSE; /* default to no border - so indicate with a size of zero pixels */ border_ = 0; /* use default spacing of 2 and padding of 1 */ cellpadding_ = 1; cellspacing_ = 2; /* set up some space for the column list */ cols_used_ = 0; cols_alloced_ = 10; columns_ = (CHtmlTag_column **) th_malloc(cols_alloced_ * sizeof(CHtmlTag_column *)); /* currently on first row */ cur_rownum_ = 0; /* no caption yet */ caption_tag_ = 0; /* widths are not yet known */ min_width_ = max_width_ = 0; /* no display item yet */ disp_ = 0; /* don't have an enclosing table yet */ enclosing_table_ = 0; } CHtmlTagTABLE::~CHtmlTagTABLE() { size_t i; CHtmlTag_column **col; /* delete the columns we've allocated */ for (i = 0, col = columns_ ; i < cols_used_ ; ++i, ++col) delete *col; /* delete the column slot array itself */ th_free(columns_); } HTML_attrerr CHtmlTagTABLE::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { HTML_Attrib_id_t val_id; HTML_attrerr err; switch(attr_id) { case HTML_Attrib_align: /* see what we have */ val_id = parser->attrval_to_id(val, vallen); switch(val_id) { case HTML_Attrib_left: case HTML_Attrib_center: case HTML_Attrib_right: /* valid setting - remember it */ align_ = val_id; break; default: /* other settings are invalid */ return HTML_attrerr_inv_enum; } break; case HTML_Attrib_width: /* set the horizontal size */ width_set_ = TRUE; return set_number_or_pct_attr(&width_, &width_pct_, val, vallen); case HTML_Attrib_height: /* set the vertical size */ height_set_ = TRUE; return set_number_or_pct_attr(&height_, &height_pct_, val, vallen); case HTML_Attrib_border: /* * if there's no value, use a border size of 1; otherwise, use * the value as the border size */ if (parser->attrval_to_id(val, vallen) == HTML_Attrib_border) border_ = 1; else return set_number_attr(&border_, val, vallen); break; case HTML_Attrib_cellpadding: return set_number_attr(&cellpadding_, val, vallen); case HTML_Attrib_cellspacing: return set_number_attr(&cellspacing_, val, vallen); case HTML_Attrib_bgcolor: /* parse the color */ err = set_color_attr(parser, &bgcolor_, val, vallen); /* if we got a valid color, note that we want to use it*/ if (err == HTML_attrerr_ok) use_bgcolor_ = TRUE; /* return the result of parsing the color */ return err; case HTML_Attrib_background: /* store the background image URL */ background_.set_url(val, vallen); break; default: return CHtmlTagContainer::set_attribute(parser, attr_id, val, vallen); } /* no errors */ return HTML_attrerr_ok; } void CHtmlTagTABLE::on_parse(CHtmlParser *parser) { /* * Start a new paragraph, unless it's a floating item, in which case * we treat it as an in-line item, so there's no paragraph break. */ if (!is_floating()) break_paragraph(parser, FALSE); /* inherit default */ CHtmlTagContainer::on_parse(parser); } void CHtmlTagTABLE::on_close(CHtmlParser *parser) { /* inherit default */ CHtmlTagContainer::on_close(parser); /* * If I have a caption, tell the caption that we've finished parsing * the table. The caption will need to re-insert its text into the * text array if it's aligned below the table, because the caption * will be inserted into the display list after the table. */ if (caption_tag_ != 0) caption_tag_->on_close_table(parser); } void CHtmlTagTABLE::post_close(CHtmlParser *parser) { /* inherit default */ CHtmlTagContainer::post_close(parser); /* * append a 'relax' in case we're closing any other containers around * the table (this is necessary to prevent processing immediately * following closures on the way out when we're doing the first pass * through the table at format time) */ parser->append_tag(new CHtmlTagRelax()); /* * start a new paragraph if it's a non-floating item; we don't do this * for floating tables because we effectively treat floating tables as * in-line items */ if (!is_floating()) break_paragraph(parser, FALSE); } void CHtmlTagTABLE::pre_close(CHtmlParser *parser) { /* if there's an open cell, close it */ parser->close_tag_if_open("TH"); parser->close_tag_if_open("TD"); /* if there's an open row, close it */ parser->close_tag_if_open("TR"); /* if there's an open caption, close it */ parser->close_tag_if_open("CAPTION"); } void CHtmlTagTABLE::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { long width, max_width; long max_height; HTML_Attrib_id_t align; /* load our background image if needed */ format_load_bg(win, formatter); /* start at the first row */ cur_rownum_ = 0; /* * if our alignment is not specified, use the enclosing division * alignment; otherwise, use the specified alignment */ align = (align_ == HTML_Attrib_invalid ? formatter->get_div_alignment() : align_); /* set the block alignment to the table's alignment */ formatter->set_blk_alignment(align); /* * Figure out the maximum space available horizontally and * vertically in the window */ max_width = win->get_disp_width() - formatter->get_left_margin() - formatter->get_right_margin(); max_height = win->get_disp_height() - formatter->get_top_margin() - formatter->get_bottom_margin(); /* * If width and height are set in percentages, calculate the * available size for the percentages now. If they're not set, * default to zero, so that we end up using the minimum size that * works. */ if (width_set_) { if (width_pct_) width = (long)((double)max_width * ((double)width_ / 100.0)); else width = width_; } else width = 0; if (height_set_) { if (height_pct_) calc_height_ = (long) ((double)max_height * ((double)height_ / 100.0)); else calc_height_ = height_; } else calc_height_ = 0; /* create a TABLE display item, and remember it for later */ disp_ = new (formatter) CHtmlDispTable(win, border_, align, is_floating(), width, width_pct_, width_set_, max_width, map_color(win, bgcolor_), use_bgcolor_, bg_image_); /* * Tell the display item about the min and max widths. On pass 1, * these are not valid until after we've finished traversing the table, * because we don't have enough information to calculate these at our * format_exit time (and indeed, we wait until then to calculate the * values). However, we keep these values from the previous pass * through the table, so they're available throughout pass 2. */ disp_->set_table_size_limits(min_width_, max_width_); /* tell the formatter we're starting a table */ enclosing_table_ = formatter->begin_table(this, disp_, &enclosing_table_pos_, caption_tag_); } void CHtmlTagTABLE::format_exit(CHtmlFormatter *formatter) { size_t colnum; long extra; int pass; /* * Now that we've finished a pass through the table, we have a complete * set of metrics for the table's cells, so we can now calculate the * column metrics based on the cell metrics. * * Computing the column metrics takes two passes. On the first pass, * we figure the width contributions to column sizes for cells that * span only one column each. On the second pass, we use those * single-column metrics to find the width contributions for cells that * span multiple columns. */ for (pass = 1 ; pass <= 2 ; ++pass) { CHtmlTag *cur; /* loop through the rows within the table */ for (cur = get_contents() ; cur != 0 ; cur = cur->get_next_tag()) { CHtmlTagTR *tr; /* if this is a row, traverse its cells */ if ((tr = cur->get_as_table_row()) != 0) { CHtmlTag *chi; /* traverse the current row's cells */ for (chi = tr->get_contents() ; chi != 0 ; chi = chi->get_next_tag()) { CHtmlTagTableCell *cell; /* if it's a cell, figure its column metrics */ if ((cell = chi->get_as_table_cell()) != 0) cell->compute_column_metrics(pass); } } } } /* * We can now calculate the size limits for the table, since each * cell has made its contribution. The minimum size for the table * is the sum of the minimum sizes of the columns; the maximum size * is the sum of the maximum sizes of the columns. */ for (min_width_ = max_width_ = 0, colnum = 0 ; colnum < cols_used_ ; ++colnum) { CHtmlTag_column *col; /* get this column */ col = get_column(colnum); /* add its minimum and maximum to the aggregate min and max */ min_width_ += col->get_min_width(); max_width_ += col->get_max_width(); } /* add the decoration widths to the total (borders, padding, spacing) */ extra = calc_decor_width(); min_width_ += extra; max_width_ += extra; /* * make sure the minimum width is at least the explicitly set width, * if provided */ if (width_set_ && !width_pct_ && min_width_ < width_) min_width_ = width_; /* * tell our display item the newly calculated values - this will allow * enclosing tables to get our horizontal size requirements during the * first pass */ if (disp_ != 0) disp_->set_table_size_limits(min_width_, max_width_); /* * Tell the formatter that we're done with this table, and restore * the enclosing table as the current table under construction. * Note that we need to wait until after we've calculated the table * size limits, because ending the table will go back and reformat * it with the new known size. */ if (disp_ != 0) formatter->end_table(disp_, enclosing_table_, &enclosing_table_pos_, caption_tag_); /* restore division alignment */ formatter->set_blk_alignment(HTML_Attrib_invalid); } /* * Get a column at a given column index */ CHtmlTag_column *CHtmlTagTABLE::get_column(size_t colnum) { /* if the column is allocated, return it directly */ if (colnum < cols_used_) return columns_[colnum]; /* expand the slot list if necessary to make room for the new column */ if (colnum >= cols_alloced_) { cols_alloced_ = colnum + 5; columns_ = (CHtmlTag_column **) th_realloc(columns_, cols_alloced_ * sizeof(CHtmlTag_column *)); } /* allocate entries up to the given point */ while (colnum >= cols_used_) columns_[cols_used_++] = new CHtmlTag_column(); /* return the new column */ return columns_[colnum]; } /* * get the next tag after the end of the table */ CHtmlTag *CHtmlTagTABLE::get_post_table_tag() { CHtmlTagContainer *cont; /* * starting with the TABLE tag itself, then working up through our * containers, find the next sibling */ for (cont = this ; cont != 0 && cont->get_next_tag() == 0 ; cont = cont->get_container()) ; /* return the sibling we found (if any) */ return (cont != 0 ? cont->get_next_tag() : 0); } /* * Calculate the width of table decorations (borders, cell spacing, cell * padding) */ long CHtmlTagTABLE::calc_decor_width() { /* * calculate the overhead for decorations: a border on each side of the * table of width 'border_'; cell padding on each side of each cell; * cell spacing between each cell and to the left and right of the * table; and two pixels for the left/right borders of each cell, if * any */ return ((2 * border_) + (cols_used_ * cellpadding_ * 2) + (cols_used_ * (border_ ? 2 : 0)) + ((cols_used_ + 1) * cellspacing_)); } /* * Calculate the width of the table, based on the column contents. */ long CHtmlTagTABLE::calc_table_width(long width_min, long width_max, long win_width) { size_t colnum; int tot_pct; long non_pct_max; long content_max; /* * If our minimum width doesn't fit the available window width, then * simply use our minimum width; this will require horizontal * scrolling, but this is the smallest we can go. */ if (width_min >= win_width) return width_min; /* * calculate the maximum width of the content part (excluding * decorations) */ content_max = width_max - calc_decor_width(); /* * Our minimum size fits the window, so we can expand beyond our * minimum content size. In the simplest case, we can just check to * see if the maximum fits the window, and top out there if so. If the * maximum doesn't fit, top out at the window size instead; this will * squeeze the table into a smaller space than it would fill if left to * its own devices, but will minimize horizontal scrolling, which is * more important than maximizing the width of the table. * * If we have any percentage columns, we need to do some extra work. * For each percentage-based column, we need to check the minimum table * size that would be necessary for the column to have its desired * percentage at its maximum content size. In addition, we must check * how much space would be needed so that the columns *without* * percentage widths would take the percentage of space left over after * accounting for all of the percentage columns. * * Run through each column and check for percentage columns. */ for (tot_pct = 0, colnum = 0, non_pct_max = 0 ; colnum < cols_used_ ; ++colnum) { CHtmlTag_column *col; int cur_pct; /* get this column descriptor */ col = get_column(colnum); /* if it has a percentage width, figure its needs */ if ((cur_pct = col->get_pct_width()) != 0) { /* * if this column would take us over 100%, limit it so that the * total doesn't exceed 100% */ if (tot_pct + cur_pct > 100) { /* limit the sum of all requests to 100% */ cur_pct = 100 - tot_pct; /* * if this leaves us with 0% or less, we've overcommitted; * simply use the maximum available window width */ if (cur_pct <= 0) return win_width; } /* add this percentage into the total */ tot_pct += cur_pct; /* * Calculate how big the table has to be to accommodate our * percentage request. Don't bother if we're asking for 100%, * as this will either overcommit the table (if there are any * other non-empty columns) or provide no information (if we're * the only column); if it overcommits the table, we'll catch * this naturally in the rest of the algorithm, so no need for * a special check here. */ if (cur_pct < 100) { long implied_max; /* * calculate the implied size of the table; round up, since * we'll round down when calculating percentage during * column width layout */ implied_max = (col->get_max_width()*100 + (cur_pct - 1)) / cur_pct; /* * if this is greater than the maximum table size, increase * the required maximum table size to accommodate */ if (implied_max > content_max) { /* increase the table size */ width_max += implied_max - content_max; content_max = implied_max; } } } else { /* * this column doesn't have a percentage width, so add its * maximum width to the sum of the non-percentage column * widths; we'll use this later to make sure the part of the * table without percentage widths adds up to the leftover * percentage after counting all of the percentage-width * columns */ non_pct_max += col->get_max_width(); } } /* * If the total of the non-percentage columns is non-zero, and we've * committed all of the space with percentage columns already, then we * can't possibly satisfy the constraints, so simply make the table as * wide as the available window space. */ if (non_pct_max != 0 && tot_pct >= 100) return win_width; /* * If the total of the non-percentage columns is between 0 and 100, * calculate the table width that we'd need to accommodate the * non-percentage columns at the left-over percentage. This will * ensure that we have space for all of the non-percentage columns at * their maximum sizes, and can still have all of the percentage * columns at their correct percentages. */ if (non_pct_max != 0 && tot_pct != 0) { long implied_max; /* * calculate the implied table width so that the non-percentage * columns take up exactly the left-over percentage */ implied_max = (non_pct_max*100 + (100 - tot_pct - 1)) / (100 - tot_pct); /* * if this is greater than the maximum table size, increase the * required maximum table size */ if (implied_max > content_max) { /* increase the table size */ width_max += implied_max - content_max; content_max = implied_max; } } /* * If our maximum size fits the window, top out at the maximum size. * Otherwise, limit our size to the available window space. */ if (width_max < win_width) return width_max; else return win_width; } /* * Set column widths and horizontal offsets. Returns the final width of * the table. */ void CHtmlTagTABLE::set_column_widths(long table_x_offset, long table_width) { long content_width; size_t colnum; long x_offset; long rem; long sum_of_pix; CHtmlTag_column *col; int unconstrained_cnt; long tot_added; int tot_pct; int force_alloc; /* * calculate the content width as the table width minus the size of * the decorations */ content_width = table_width - calc_decor_width(); /* * First, calculate the amount of space we'd have left over if we were * to set every column to its minimum size. This will tell us how * much extra space there is to distribute among the columns, which * we'll do according to their size constraints and settings. */ for (rem = content_width, colnum = 0, unconstrained_cnt = 0, sum_of_pix = 0 ; colnum < cols_used_ ; ++colnum) { /* get this column descriptor */ col = get_column(colnum); /* initially set this column to its minimum width */ col->set_width(col->get_min_width()); /* deduct this one's minimum from the remaining leftover space */ rem -= col->get_min_width(); /* * If this column is unconstrained (i.e., it has no WIDTH setting * of any kind), note it. We'll want to know later if there are * any unconstrained columns, because if there's any leftover * space after we've dealt with all of the constrained columns, * we'll preferentially divide the leftover among the * unconstrained columns. * * If this column is constrained with a pixel width, count the * pixel width request in our sum of pixel width requests. */ if (col->get_pix_width() == 0 && col->get_pct_width() == 0) ++unconstrained_cnt; else if (col->get_pix_width() != 0) sum_of_pix += col->get_pix_width(); } /* * Next, allocate the percentage requests. Run through the columns * again looking for percentage width requests, and allocate space for * each one. */ for (colnum = 0, tot_added = 0, tot_pct = 0 ; colnum < cols_used_ ; ++colnum) { int cur_pct; /* get this column descriptor */ col = get_column(colnum); /* if this is a percentage-width column, apply the percentage */ if ((cur_pct = col->get_pct_width()) != 0) { long req_width; /* limit the total percentage requests to 100% */ if (tot_pct + cur_pct > 100) cur_pct = 100 - tot_pct; /* count this in the total */ tot_pct += cur_pct; /* * calculate the requested width as a percentage of the total * table width */ req_width = (cur_pct * content_width) / 100; /* * if the requested width is greater than the minimum width of * the column, apply it; ignore it if it's less than the * minimum, since we never go less than the minimum */ if (req_width > col->get_min_width()) { long added_width; /* figure out how much width we're attempting to add */ added_width = req_width - col->get_width(); /* count the total added */ tot_added += added_width; /* add the width to the column */ col->set_width(col->get_width() + added_width); } } } /* * If we added more than the extra space we had available to add, go * back and trim off the excess. */ for (force_alloc = FALSE ; tot_added > rem ; ) { long need_to_trim; long tot_trimmed; long prop_base; /* calculate how much we need to trim */ need_to_trim = tot_added - rem; /* add up the percentage requests of the eligible columns */ for (colnum = 0, prop_base = 0 ; colnum < cols_used_ ; ++colnum) { /* get this column descriptor */ col = get_column(colnum); /* * If this column is eligible for trimming, note it. It's * eligible if it has a percentage-based width and it's above * its minimum width. */ if (col->get_pct_width() != 0 && col->get_width() > col->get_min_width()) { /* add its current width to the proportion base */ prop_base += col->get_width(); } } /* run through the columns and trim space from eligible columns */ for (colnum = 0, tot_trimmed = 0 ; colnum < cols_used_ ; ++colnum) { /* get this column descriptor */ col = get_column(colnum); /* if it's eligible, trim some space from it */ if (col->get_pct_width() != 0 && col->get_width() > col->get_min_width()) { long cur_trim; /* * trim from this column in proportion to its size relative * to the total size of the columns we can trim */ cur_trim = (col->get_width() * need_to_trim) / prop_base; /* * if we're on the final forced-allocation pass, allocate * all remaining pixels to this column if possible */ if (force_alloc) cur_trim = need_to_trim - tot_trimmed; /* we can't go below the column's minimum width, though */ if (col->get_width() - cur_trim < col->get_min_width()) cur_trim = col->get_width() - col->get_min_width(); /* trim space from the column */ col->set_width(col->get_width() - cur_trim); /* count it in the total trimmed */ tot_trimmed += cur_trim; } } /* adjust the net added width to reflect the trimming we just did */ tot_added -= tot_trimmed; /* * If we didn't trim anything on this round, then we must have * reached a point where we have a last pixel or two that we can't * de-allocate proportionally due to rounding. Force allocation * of pixels on the next round. */ if (tot_trimmed == 0) force_alloc = TRUE; } /* * deduct the amount added in the percentage pass from the total * remaining space to be distributed */ rem -= tot_added; /* * If anything's left over, and we have one or more columns with * explicit pixel widths, allocate the remaining space among those * explicitly sized columns up to their width settings. */ if (rem != 0 && sum_of_pix != 0) { long total_req; long total_to_alloc; long total_alloced; /* * First, check to see if we have enough to bring every column * with an explicit pixel width up to its requested width. */ for (total_req = 0, colnum = 0 ; colnum < cols_used_ ; ++colnum) { /* get this column descriptor */ col = get_column(colnum); /* * if this column is pixel-sized, and its requested size is * greater than its current size, add the amount of additional * space it requested to the running total */ if (col->get_pix_width() != 0 && col->get_width() < col->get_pix_width()) { /* add its requested additional space to the running sum */ total_req += col->get_pix_width() - col->get_width(); } } /* if there are any requests, service them */ if (total_req != 0) { /* * if the sum of the requests exceeds the available size, * limit the amount to be allocated to the amount available; * otherwise, just allocate the full amount requested */ total_to_alloc = (total_req < rem ? total_req : rem); /* allocate the space proportionally to the requests */ for (total_alloced = 0, colnum = 0 ; colnum < cols_used_ ; ++colnum) { /* get this column descriptor */ col = get_column(colnum); /* if this is an pixel-sized column, allocate space to it */ if (col->get_pix_width() != 0) { /* * if this column isn't already at least at its * requested size, give it its share of the remaining * space */ if (col->get_width() < col->get_pix_width()) { long share; /* * Allocate from the available space * proportionally to this column's needs for * additional space. This should allocate the * space in such a way that we get equally close * to the requested size for each column. */ share = ((col->get_pix_width() - col->get_width()) * total_to_alloc) / total_req; /* add the share to the column's width */ col->set_width(col->get_width() + share); /* count it in the total we've allocated */ total_alloced += share; } } } /* * deduct the total we allocated from the remaining space to * be allocated */ rem -= total_alloced; } } /* * if there are no columns, ignore any remaining space, as there are no * columns into which to distribute the space */ if (cols_used_ == 0) rem = 0; /* * We've now applied all percentages and we've expanded each column * with a requested pixel size to the requested pixel size, or at * least as close to it as we can get. If we still have any space * remaining, divide it up. Keep going as long as we have more space * available, since we might on any given pass throw back some space * for other columns to use. */ for (force_alloc = FALSE ; rem != 0 ; ) { long max_sum; int all_at_max; int base_cnt; long space_used; /* we haven't used any space on this pass yet */ space_used = 0; /* * If there are any unconstrained columns, allocate space only to * the unconstrained columns, because this will let us keep the * constrained columns at their optimal sizes. If there are no * unconstrained columns, just divide the space among all of the * columns: if there's extra space and no unconstrained columns, * the table is over-constrained, so all we can do is add space. */ if (unconstrained_cnt != 0) { /* we have unconstrained columns - add space only to these */ base_cnt = unconstrained_cnt; } else { /* no unconstrained columns - add space to all columns */ base_cnt = cols_used_; } /* * Run through the columns to check to see if they're all at least * at their maximum sizes. If some columns are and some columns * aren't, we'll give space preferentially to those that aren't yet * at their maximum sizes. */ for (colnum = 0, all_at_max = TRUE ; colnum < cols_used_ ; ++colnum) { /* * If this column isn't at its maximum size, they're not all at * their maximum size, obviously. Only count the column if * we're going to process it (i.e., it's unconstrained, or all * columns are constrained). */ col = get_column(colnum); if (col->get_width() < col->get_max_width() && (unconstrained_cnt == 0 || (col->get_pix_width() == 0 && col->get_pct_width() == 0))) { /* note that they're not all at maximum width */ all_at_max = FALSE; /* no need to keep looking - one is enough */ break; } } /* calculate the proportionality base for distributing space */ for (colnum = 0, max_sum = 0 ; colnum < cols_used_ ; ++colnum) { /* get this column descriptor */ col = get_column(colnum); /* * If we're including this column, count it in the * proportionality base. If we have any unconstrained columns, * include only unconstrained columns; otherwise, include all * columns. */ if (unconstrained_cnt == 0 || (col->get_pix_width() == 0 && col->get_pct_width() == 0)) { /* * Include this column in the running total. If all * columns are at their maximum sizes, simply distribute * the remaining space proportionally to the maximum sizes. * Otherwise, distribute space proportionally to the * *excess* of maximum over minimum size, as this will * naturally scale all of the columns up to their maximum * sizes at equal paces. */ if (all_at_max) max_sum += col->get_max_width(); else max_sum += col->get_max_width() - col->get_min_width(); } } /* loop over the columns again and hand out the space */ for (colnum = 0 ; colnum < cols_used_ ; ++colnum) { /* get this column descriptor */ col = get_column(colnum); /* if we're including this column, add its space */ if (unconstrained_cnt == 0 || (col->get_pix_width() == 0 && col->get_pct_width() == 0)) { long added; /* * Add the space to the column. If our proportionality * base is non-zero, divide the space in proportion to our * contribution to the base. If the base is zero, simply * divide the space equally among the unconstrained * columns. */ if (force_alloc) { /* * we're forcing allocation to the earliest columns * possible, so give all of the remaining pixels to * this column */ added = rem - space_used; } else if (max_sum != 0) { /* * figure the proportion of space going to this column: * if all columns are at maximum size, distribute space * proportionally to the maximum size; otherwise, * distribute space proportionally to the excess of * maximum over minimum size */ if (all_at_max) added = (rem * col->get_max_width()) / max_sum; else added = (rem * (col->get_max_width() - col->get_min_width())) / max_sum; } else { /* there's no proportionality base, so divide evenly */ added = rem / base_cnt; } /* * if this would take us over our maximum width, and not * all of the columns are at their maximum widths yet, * limit ourselves to our maximum for now */ if (!all_at_max && col->get_width() + added > col->get_max_width()) { /* limit the addition to our maximum width */ added = col->get_max_width() - col->get_width(); /* but don't shrink */ if (added < 0) added = 0; } /* add the space to the column */ col->set_width(col->get_width() + added); /* count the added space */ space_used += added; } } /* deduct the space we just allocated from the remaining space */ rem -= space_used; /* * If we allocated no space on this round, it must mean we've * reached the point where we have a final pixel or two that we * can't allocate proportionally due to rounding. Force * allocation on the next round. */ if (space_used == 0) force_alloc = TRUE; } /* * We've now handed out all space. Run through the columns one last * time and assign the column positions. */ for (x_offset = table_x_offset + cellspacing_ + border_, colnum = 0 ; colnum < cols_used_ ; ++colnum) { /* get this column descriptor */ col = get_column(colnum); /* assign the position */ col->set_x_offset(x_offset); /* adjust the column's width for the border and cell spacing */ col->set_width(col->get_width() + 2 * cellpadding_ + (border_ ? 2 : 0)); /* * advance to the next cell offset: include the width of this cell * plus the inter-cell spacing */ x_offset += col->get_width() + cellspacing_; } } /* * Compute the final height of the table. We'll go through our rows and * compute their final heights, which will tell us the final height of * the table itself. */ void CHtmlTagTABLE::compute_table_height() { CHtmlTag *cur; size_t rowcnt; size_t rownum; int pass; /* count the number of rows in the table */ for (rowcnt = 0, cur = get_contents() ; cur != 0 ; cur = cur->get_next_tag()) { /* if it's a row, count it */ if (cur->get_as_table_row() != 0) ++rowcnt; } /* * Go through the rows and compute the maximum cell height in each row. * Cells may span multiple rows, so we must consider cells beyond a * row's own cells to determine the actual row height. * * Note that we need to do this in two passes. On the first pass, we * assign heights to all single-row cells (ROWSPAN == 1). On the * second, we allocate space from multi-row cells. We need to compute * all of the single-row cell heights before we start working on the * multi-row cells because we allocate the multi-row space in * proportion to the single-row heights. */ for (pass = 1 ; pass <= 2 ; ++pass) { /* go through the rows and compute each row's height */ for (rownum = 0, cur = get_contents() ; cur != 0 ; cur = cur->get_next_tag()) { CHtmlTagTR *tr; /* get this item as a TR tag; if it's not a TR tag, skip it */ tr = cur->get_as_table_row(); if (tr == 0) continue; /* have this row ask its cells for their height contributions */ tr->compute_max_cell_height(rownum, rowcnt, pass); /* count the row */ ++rownum; } } } /* * Set row positions. */ void CHtmlTagTABLE::set_row_positions(class CHtmlFormatter *formatter) { CHtmlTag *cur; long y_offset; long total_height; long extra_height; long total_interior_height; int star_cnt = 0; /* clear the total interior height counter */ total_interior_height = 0; /* * Go through my rows and add up the heights. With the exception of * the <CAPTION> element, every direct child tag should be a <TR> * (table row) tag. */ for (y_offset = cellspacing_ + border_, cur = get_contents() ; cur != 0 ; cur = cur->get_next_tag()) { CHtmlTagTR *tr; long height; /* get this item as a TR tag; if it's not a TR tag, skip it */ tr = cur->get_as_table_row(); if (tr == 0) continue; /* ask this row for its maximum cell height */ height = tr->get_max_cell_height(); /* count this row's interior height in the total */ total_interior_height += height; /* add in the internal padding twice (bottom and top) */ height += 2 * cellpadding_; /* * add two pixels of border (one for bottom and one for top) if * we have a border (the cell borders are always one pixel, * regardless of hte size of the table border) */ height += (border_ != 0 ? 2 : 0); /* add the bottom inter-row spacing */ height += cellspacing_; /* add this row's height into the running total */ y_offset += height; /* if this is a 'height=*' row, note that we have one */ if (tr->is_height_star()) ++star_cnt; } /* the y offset is the total height of the table plus the bottom border */ total_height = y_offset + border_; /* * If we have an explicit height set, and the explicit height is * greater than the actual height we've calculated, we'll need to * expand all of the rows proportionally to the extra height we need * to reach the explicit setting. We treat the explicit setting as * a minimum height, so if the actual table height exceeds the * explicit setting, we won't change anything. */ if (height_set_ && calc_height_ > total_height) extra_height = calc_height_ - total_height; else extra_height = 0; /* * Now go through again, and actually set all of the row positions. */ for (y_offset = cellspacing_ + border_, cur = get_contents() ; cur != 0 ; cur = cur->get_next_tag()) { CHtmlTagTR *tr; long height; long interior_height; /* get this item as a TR tag; if it's not a TR tag, skip it */ tr = cur->get_as_table_row(); if (tr == 0) continue; /* ask this row for its maximum cell height */ interior_height = tr->get_max_cell_height(); /* start with the interior height of the cell */ height = interior_height; /* add in the internal padding twice (bottom and top) */ height += 2 * cellpadding_; /* * add two pixels of border (one for bottom and one for top) if * we have a border (the cell borders are always one pixel, * regardless of the size of the table border) */ height += (border_ != 0 ? 2 : 0); /* * If there's extra height, dole it out. If we have a star row, * we're going to give all of the extra height to the star row; * otherwise, we'll apportion it among the rows. */ if (star_cnt != 0) { /* * If this is a 'height=*' row, give it a fraction of the * extra height. Otherwise, give it nothing. 'height=*' * rows soak up all of the extra space, and split it evenly * by count. */ if (tr->is_height_star()) height += extra_height / star_cnt; } else if (total_interior_height != 0) { /* * Add in this row's portion of the extra height - the * amount we add in is the total extra height times the * ratio of this row's height to the total actual height, * thus giving us a portion of the extra height proportional * to the row's contribution to the overall actual height. * Note that we count only interior heights for the * proportion. */ height += (long)((double)extra_height * ((double)interior_height / (double)total_interior_height)); } /* set the y offset of the items in the row */ tr->set_row_y_pos(formatter, this, y_offset, height); /* add the height to the current position */ y_offset += height; /* add the bottom inter-row spacing */ y_offset += cellspacing_; } /* add the bottom border size */ y_offset += border_; /* tell the formatter the overall size of hte table */ formatter->end_table_set_size(enclosing_table_, y_offset, &enclosing_table_pos_); /* set the display item's height */ disp_->set_table_height(y_offset); } /* ------------------------------------------------------------------------ */ /* * Tables - CAPTION tag */ HTML_attrerr CHtmlTagCAPTION::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { HTML_Attrib_id_t val_id; switch(attr_id) { case HTML_Attrib_align: /* see what we have */ val_id = parser->attrval_to_id(val, vallen); switch(val_id) { case HTML_Attrib_top: case HTML_Attrib_bottom: /* * This is a vertical setting. Use default horizontal * alignment, and remember the desired vertical alignment. */ valign_ = val_id; align_ = HTML_Attrib_invalid; break; case HTML_Attrib_left: case HTML_Attrib_right: case HTML_Attrib_center: /* horizontal setting - remember it */ align_ = val_id; break; default: /* other settings are invalid */ return HTML_attrerr_inv_enum; } break; case HTML_Attrib_valign: /* check the setting */ val_id = parser->attrval_to_id(val, vallen); switch(val_id) { case HTML_Attrib_top: case HTML_Attrib_bottom: /* valid setting - remember it */ valign_ = val_id; break; default: /* other settings are invalid */ return HTML_attrerr_inv_enum; } break; default: return CHtmlTagContainer::set_attribute(parser, attr_id, val, vallen); } /* no errors */ return HTML_attrerr_ok; } /* * when we encounter a CAPTION tag, close any other table structure tags * (rows and cells) that are open */ void CHtmlTagCAPTION::on_parse(CHtmlParser *parser) { /* if there's an open cell, close it */ parser->close_tag_if_open("TH"); parser->close_tag_if_open("TD"); /* if there's an open row, close it */ parser->close_tag_if_open("TR"); /* inherit default */ CHtmlTagContainer::on_parse(parser); } /* * finish parsing the CAPTION tag */ void CHtmlTagCAPTION::on_close(CHtmlParser *) { CHtmlTagTABLE *table; /* find my table container, and tell it about us */ if ((table = get_table_container()) != 0) table->set_caption_tag(this); } /* * post-close processing */ void CHtmlTagCAPTION::post_close(CHtmlParser *parser) { /* inherit default */ CHtmlTagContainer::post_close(parser); /* * add a 'relax' tag after me - this will ensure that whatever follows, * we won't traverse out of the enclosing table if we try to traverse * off the end of our sublist (such as we must do while formatting our * sublist) */ parser->append_tag(new CHtmlTagRelax()); } /* * Receive notification that we've just finished parsing the enclosing * table. If we're aligned at the bottom of the table, we must now * re-insert any text contents into the text array, because the text * will appear in the display list after the table. */ void CHtmlTagCAPTION::on_close_table(CHtmlParser *parser) { /* if I'm aligned below the table, re-insert my text contents here */ if (valign_ == HTML_Attrib_bottom) { CHtmlTag *cur; /* go through my contents, and re-insert any text */ for (cur = get_contents() ; cur != 0 ; cur = cur->get_next_tag()) cur->reinsert_text_array(parser->get_text_array()); } } /* * Format the caption's contents */ void CHtmlTagCAPTION::format_caption(CHtmlSysWin *win, CHtmlFormatter *formatter) { HTML_Attrib_id_t align; HTML_Attrib_id_t old_align; CHtmlTag *cur; CHtmlTag *last; /* set the appropriate horizontal alignment */ align = (align_ == HTML_Attrib_invalid ? HTML_Attrib_center : align_); old_align = formatter->get_blk_alignment(); formatter->set_blk_alignment(align); /* make sure we're on a new line */ formatter->add_disp_item_new_line(new (formatter) CHtmlDispBreak(0)); /* format our contents */ last = get_next_fmt_tag(formatter); for (cur = get_contents() ; cur != 0 && cur != last ; cur = cur->get_next_fmt_tag(formatter)) { /* format this tag */ cur->format(win, formatter); } /* end the line */ formatter->add_disp_item_new_line(new (formatter) CHtmlDispBreak(0)); /* restore old alignment */ formatter->set_blk_alignment(old_align); } /* ------------------------------------------------------------------------ */ /* * Tables - TR (table row) tag */ HTML_attrerr CHtmlTagTR::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { HTML_Attrib_id_t val_id; HTML_attrerr err; switch(attr_id) { case HTML_Attrib_align: /* see what we have */ val_id = parser->attrval_to_id(val, vallen); switch(val_id) { case HTML_Attrib_left: case HTML_Attrib_center: case HTML_Attrib_right: /* valid setting - remember it */ align_ = val_id; break; default: /* other settings are invalid */ return HTML_attrerr_inv_enum; } break; case HTML_Attrib_valign: /* see what we have */ val_id = parser->attrval_to_id(val, vallen); switch(val_id) { case HTML_Attrib_top: case HTML_Attrib_middle: case HTML_Attrib_bottom: /* valid setting - remember it */ valign_ = val_id; break; default: /* other settings are invalid */ return HTML_attrerr_inv_enum; } break; case HTML_Attrib_bgcolor: /* parse the color */ err = set_color_attr(parser, &bgcolor_, val, vallen); /* if we got a valid color, note that we want to use it*/ if (err == HTML_attrerr_ok) use_bgcolor_ = TRUE; /* return the result of parsing the color */ return err; case HTML_Attrib_background: /* store the background image URL */ background_.set_url(val, vallen); break; case HTML_Attrib_height: /* check for a '*' attribute */ if (vallen == 1 && *val == '*') { /* note that we have a '*' for the height */ height_star_ = TRUE; } else { /* set the height attribute */ height_set_ = TRUE; return set_number_attr(&height_, val, vallen); } break; default: return CHtmlTagContainer::set_attribute(parser, attr_id, val, vallen); } /* no errors */ return HTML_attrerr_ok; } void CHtmlTagTR::on_parse(CHtmlParser *parser) { /* close a CAPTION if it's open */ parser->close_tag_if_open("CAPTION"); /* if we're currently in a cell (TD or TH), close it */ parser->close_tag_if_open("TD"); parser->close_tag_if_open("TH"); /* if we're currently in another row, close it */ parser->close_tag_if_open("TR"); /* add this tag as normal */ CHtmlTagContainer::on_parse(parser); } void CHtmlTagTR::pre_close(CHtmlParser *parser) { /* close any open cell */ parser->close_tag_if_open("TD"); parser->close_tag_if_open("TH"); } void CHtmlTagTR::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { CHtmlTagTABLE *table; /* get my table - if I don't have a table, I can't do anything */ table = get_container()->get_table_container(); if (table == 0) return; /* load our background image if needed */ format_load_bg(win, formatter); /* get my row, by asking the table for its current row */ rownum_ = table->get_cur_rownum(); /* * reset my maximum height -- the maximum height is set at the end * of pass 2 */ max_cell_height_ = 0; /* start at the first column */ cur_colnum_ = 0; } void CHtmlTagTR::format_exit(CHtmlFormatter *) { CHtmlTagTABLE *table; /* get my table - if I don't have a table, I can't do anything */ table = get_container()->get_table_container(); if (table == 0) return; /* tell the table to move on to the next row */ table->set_cur_rownum(rownum_ + 1); } /* * Compute the maximum height of cells in this row, and add their * contributions to this row and any other rows they span. * * Note that this routine has to be called twice - call it for each row in * the table with 'pass' set to 1, then go back and call it again for each * row with 'pass' set to 2. On the first pass, we calculate the height * contribution of each cell that spans only a single row. On the second * pass, we go back and figure how to apportion the height contributions of * cells that span multiple rows. We need to figure all of the single-row * contributions first, because we allocate the multi-row contributions in * proportion to the single-row heights. */ void CHtmlTagTR::compute_max_cell_height(size_t rownum, size_t rowcnt, int pass) { /* * Loop through my cells. Ask each cell for its height, and find * the largest. */ for (CHtmlTag *cur = get_contents() ; cur != 0 ; cur = cur->get_next_tag()) { CHtmlTagTableCell *cell; CHtmlTag *row; long height; long span; long span_left; /* if this isn't a table cell, ignore it */ cell = cur->get_as_table_cell(); if (cell == 0) continue; /* * Get the number of rows we should distribute the cell's height * over. We normally distribute over the rowspan, but if the * rowspan exceeds the number of rows left in the table after * this row, we limit the rowspan to the number of rows * remaining. */ span = cell->get_rowspan(); if (span > (long)(rowcnt - rownum)) span = (long)(rowcnt - rownum); /* get our cell height */ height = cell->get_table_cell_height(); /* * If we span only one row, assign the full height to that row. * * If we span more than one row, assign the height to our several * rows in proportion to the single-cell heights of the rows. * Because we need to calculate the single-cell heights first, we * can't allocate the multi-row heights until the second pass. */ if (span <= 1) { /* we occupy a single row, so assign the height to our row */ include_cell_height(height); } else if (pass == 2) { CHtmlTagTR *tr; long cur_tot; long extra; long rem; /* * We span multiple rows, and this is the second pass. * Allocate our height to each of the rows we span. Dole out * the extra height in proportion to the current heights of the * spanned rows. So, first calculate the total heights of the * spanned rows. */ for (span_left = span, row = this, cur_tot = 0 ; row != 0 && span_left != 0 ; row = row->get_next_tag()) { /* if this is a row, get its height */ if ((tr = row->get_as_table_row()) != 0) { /* add the height to the total */ cur_tot += tr->get_max_cell_height(); /* count it */ --span_left; } } /* * We now have the total of the current heights of the spanned * rows. Calculate the excess height we need to distribute: * this is the excess of our height over the total current * height. */ extra = (height > cur_tot ? height - cur_tot : 0); /* remember the total remaining to distribute */ rem = extra; /* distribute the extra height proportionally */ for (span_left = span, row = this ; row != 0 && span_left != 0 ; row = row->get_next_tag()) { long delta; long ht; /* if this isn't a row, skip it */ if ((tr = row->get_as_table_row()) == 0) continue; /* count the row */ --span_left; /* get this row's current height */ ht = tr->get_max_cell_height(); /* * Calculate the share of the extra height to put here. If * this is the last row, distribute the entire remaining * height here, to avoid losing any pixels due to * accumulated rounding errors. Otherwise, if the total * height is non-zero, give this cell its share of the * height in proportion to its current share of the total * current height. If the total height is zero, we can't * calculate proportions, so simply dole out the extra * height in equal shares to our spanned cells. */ delta = (span_left == 0 ? rem : (cur_tot != 0 ? (extra * ht) / cur_tot : extra / span)); /* add the extra share to the row height */ tr->include_cell_height(ht + delta); /* deduct this allocation from the remainder */ rem -= delta; } } } } /* * Set the y positions of the items in this row. */ void CHtmlTagTR::set_row_y_pos(CHtmlFormatter *formatter, CHtmlTagTABLE *table, long y_offset, long height) { /* loop through my cells and set each one's vertical position */ for (CHtmlTag *cur = get_contents() ; cur != 0 ; cur = cur->get_next_tag()) { CHtmlTagTableCell *cell; CHtmlTag *nxt; long cell_height; long span_left; /* if this isn't a table cell, ignore it */ cell = cur->get_as_table_cell(); if (cell == 0) continue; /* * compute this cell's height - if the cell spans only one row, * it's the current row's height; otherwise, we need to add in * the heights of the spanned rows */ cell_height = height; for (nxt = this, span_left = cell->get_rowspan() ; span_left > 1 ; --span_left) { CHtmlTagTR *tr; /* find the next TR element */ for (tr = 0, nxt = nxt->get_next_tag() ; nxt != 0 ; nxt = nxt->get_next_tag()) { /* if this is a TR element, stop searching */ if ((tr = nxt->get_as_table_row()) != 0) break; } /* if we didn't find another row, we've spanned all we can */ if (tr == 0) break; /* * add this row's height to the total height of the cell, * including all of the spacing */ cell_height += tr->get_max_cell_height() + table->get_cellspacing() + 2 * table->get_cellpadding() + (table->get_border() != 0 ? 2 : 0); } /* set this cell's vertical position */ cell->set_cell_y_pos(formatter, y_offset, cell_height, valign_); } } /* ------------------------------------------------------------------------ */ /* * Tables - basic cell tag - base class for TH (table heading) and TD * (table cell data) */ CHtmlTagTableCell::CHtmlTagTableCell(CHtmlParser *parser) : CHtmlTagContainer(), CHtmlTableImage() { /* NOWRAP not yet specified */ nowrap_ = FALSE; /* default to spanning one row and column */ rowspan_ = colspan_ = 1; /* no alignment settings yet */ align_ = valign_ = HTML_Attrib_invalid; /* no width or height explicitly set yet */ width_set_ = height_set_ = FALSE; width_pct_ = FALSE; /* haven't set my row and column position yet */ row_col_set_ = FALSE; rownum_ = 0; colnum_ = 0; /* no display item yet */ disp_ = 0; disp_last_ = 0; /* no contents yet */ content_height_ = 0; disp_y_base_ = 0; } HTML_attrerr CHtmlTagTableCell::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { HTML_Attrib_id_t val_id; HTML_attrerr err; switch(attr_id) { case HTML_Attrib_nowrap: /* note the presence of NOWRAP */ nowrap_ = TRUE; break; case HTML_Attrib_align: /* see what we have */ val_id = parser->attrval_to_id(val, vallen); switch(val_id) { case HTML_Attrib_left: case HTML_Attrib_center: case HTML_Attrib_right: /* valid setting - remember it */ align_ = val_id; break; default: /* other settings are invalid */ return HTML_attrerr_inv_enum; } break; case HTML_Attrib_valign: /* see what we have */ val_id = parser->attrval_to_id(val, vallen); switch(val_id) { case HTML_Attrib_top: case HTML_Attrib_middle: case HTML_Attrib_bottom: /* valid setting - remember it */ valign_ = val_id; break; default: /* other settings are invalid */ return HTML_attrerr_inv_enum; } break; case HTML_Attrib_rowspan: return set_number_attr(&rowspan_, val, vallen); case HTML_Attrib_colspan: return set_number_attr(&colspan_, val, vallen); case HTML_Attrib_width: /* set the horizontal size, and note that it's been set */ width_set_ = TRUE; return set_number_or_pct_attr(&width_, &width_pct_, val, vallen); case HTML_Attrib_height: /* set the horizontal size, and note that it's been set */ height_set_ = TRUE; return set_number_attr(&height_, val, vallen); case HTML_Attrib_bgcolor: /* parse the color */ err = set_color_attr(parser, &bgcolor_, val, vallen); /* if we got a valid color, note that we want to use it*/ if (err == HTML_attrerr_ok) use_bgcolor_ = TRUE; /* return the result of parsing the color */ return err; case HTML_Attrib_background: /* store the background image URL */ background_.set_url(val, vallen); break; default: return CHtmlTagContainer::set_attribute(parser, attr_id, val, vallen); } /* no errors */ return HTML_attrerr_ok; } /* * Parse a cell tag. Since a new table tag implicitly closes the * previous cell, we will close the current TH or TD tag, if one is * open. */ void CHtmlTagTableCell::on_parse(CHtmlParser *parser) { /* close a CAPTION if it's open */ parser->close_tag_if_open("CAPTION"); /* if there's an open cell, implicitly close it */ parser->close_tag_if_open("TH"); parser->close_tag_if_open("TD"); /* * add a space between cells, so that we always find word boundaries * between cells and don't get confused into thinking a word can * span cells */ parser->get_text_array()->append_text_noref(" ", 1); /* parse this tag normally */ CHtmlTagContainer::on_parse(parser); } /* * format a table cell */ void CHtmlTagTableCell::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { CHtmlTagTABLE *table; CHtmlTagTR *row; size_t rownum; size_t colnum; long x_offset, col_width; long padding; HTML_Attrib_id_t align; int use_bgcolor; HTML_color_t bgcolor; /* * get my table and row -- if I don't have both, we can't format * this item as a table cell */ table = get_container()->get_table_container(); row = get_container()->get_table_row_container(); if (table == 0 || row == 0) return; /* load our background image if needed */ format_load_bg(win, formatter); /* * Determine my row and column position. We start with the current * row and column, and look for a block of available cells big * enough for our rowspan and colspan settings. All cells in the * block must be available. We'll keep going right until we find a * big enough block, but we'll always stay in the current row. * * Note that if we've formatted the table already, the row and * column positions will already be known, in which case we don't * have to figure them out again. Reformatting the same page will * never change the row and column positions, as these are derived * from the table structure itself, not from the on-screen layout. */ if (!row_col_set_) { for (rownum = row->get_rownum(), colnum = row->get_cur_colnum() ;; ) { size_t r, c; int ok; /* * See if the current position has enough space available. * All cells for our row and column spans must be * unoccupied, so that we don't overlap the contents of any * other cells. */ for (ok = TRUE, c = colnum ; ok && c < colnum + colspan_ ; ++c) { for (r = rownum ; ok && r < rownum + rowspan_ ; ++r) { /* see if this position is occupied */ if (table->get_cell(r, c)->is_occupied()) { /* * This position is occupied. Clear the "ok" * flag to indicate that we can't use this * position. In addition, move the proposed * column position to the next column after this * one, since we obviously can't use anything to * the left of this point - if we overlap at the * current position, we'd overlap at the next * position to the right, and so on until we're * all the way to the right of this column. */ ok = FALSE; colnum = c + 1; } } } /* if the position checked out, we can stop looking */ if (ok) break; } /* we've determined our position - remember it */ rownum_ = rownum; colnum_ = colnum; row_col_set_ = TRUE; } /* * Claim our block of cells by setting the 'occupied' flag on each * cell in our block. */ for (rownum = rownum_ ; rownum < rownum_ + rowspan_ ; ++rownum) for (colnum = colnum_ ; colnum < colnum_ + colspan_ ; ++colnum) table->get_cell(rownum, colnum)->set_occupied(); /* * tell the row to start the next column at the next column after * the last one we're using */ row->set_cur_colnum(colnum_ + colspan_); /* * Calculate our x offset and column width. The x offset is the x * offset of my column; the width is the sum of the widths of my * spanned columns. */ x_offset = table->get_column(colnum_)->get_x_offset(); for (colnum = colnum_, col_width = 0 ; colnum < colnum_ + colspan_ ; ++colnum) col_width += table->get_column(colnum)->get_width(); /* add the inter-column padding for spanned columns */ col_width += (colspan_ - 1) * table->get_cellspacing(); /* get the background color from the row, if it's not set in the cell */ if (use_bgcolor_) { /* it's set in the cell - this overrides any row setting */ use_bgcolor = TRUE; bgcolor = map_color(win, bgcolor_); } else { /* not set in the cell - use the setting from the row */ use_bgcolor = row->get_use_bgcolor(); bgcolor = row->get_bgcolor(); } /* create the display item for the cell */ disp_ = new (formatter) CHtmlDispTableCell(win, table->get_border() != 0, bgcolor, use_bgcolor, bg_image_, width_, width_pct_, width_set_); /* * Set horizontal alignment for the cell's contents. If we don't * have an explicit setting, use the row setting; if the row doesn't * have an explicit setting, use the appropriate default for this * type of cell. */ align = align_; if (align == HTML_Attrib_invalid) align = row->get_align(); if (align == HTML_Attrib_invalid) align = get_default_cell_alignment(); /* * Tell the formatter we're beginning a new cell. We must tell the * formatter our column offset and size; the formatter will ignore * this on the first pass, since we won't have the final results in * at that point, but it will use it on the second pass. Note that * we must take the internal cell padding into account, since the * offset and width include the space taken by the padding, but the * contents must fit inside the padding. */ padding = table->get_cellpadding() + (table->get_border() ? 1 : 0); formatter->begin_table_cell(disp_, x_offset, x_offset + padding, col_width - 2*padding); /* * Tell the formatter to set our alignment. Wait until after we've * already started the cell with the formatter, because our * alignment setting affects the cell's *contents*, not the cell * itself. * * Set the DIVISION alignment, not just the block-level alignment -- * we want every block in the cell to be aligned the same way, if * the table contains multiple blocks (it may, for example, contain * multiple paragraphs); set the block alignment as well, so that * the implicit first block in the cell gets this alignment. * * Remember the enclosing alignment so that we can restore it when * we're done. */ old_align_ = formatter->get_div_alignment(); formatter->set_div_alignment(align); formatter->set_blk_alignment(align); /* set the cell's width */ disp_->set_cell_width(col_width); } /* * exit formatting of a table cell */ void CHtmlTagTableCell::format_exit(CHtmlFormatter *formatter) { CHtmlDisp_wsinfo wsinfo; /* finish up any floating items that have been deferred */ formatter->pre_end_table_cell(); /* * Scan our contents for trailing whitespace, and remove any we find. * Trailing whitespace in a table cell can disturb the layout, so we * throw it away. */ wsinfo.remove_trailing_whitespace(formatter->get_win(), disp_, 0); /* * add a line break; this is not needed as an actual line break, but * we do need it to mark the end of our contents in the display list */ disp_last_ = new (formatter) CHtmlDispBreak(0); formatter->add_disp_item_new_line(disp_last_); /* restore the enclosing alignment */ formatter->set_div_alignment(old_align_); /* tell the formatter that we're done with this cell */ formatter->end_table_cell(this, disp_); } /* * Receive information on our metrics. The formatter calls this as we're * leaving each cell on pass 1. We simply store the minimum and maximum * widths of our contents for later here; we'll use this information in * compute_column_metrics() to figure our contribution to the column * layout. */ void CHtmlTagTableCell::set_table_cell_metrics(CHtmlTableMetrics *metrics) { /* remember our calculated metrics for later */ cont_min_width_ = metrics->min_width_; cont_max_width_ = metrics->max_width_; } /* * Compute our width contribution to the column or columns we occupy. This * is called from our table's format_exit() routine, when we're done with * the table - we have to wait until the end of the table to compute our * column widths because our allocations sometimes depend on the other * cells in the table. * * This must be called twice - call it for each cell in the table with * 'pass' set to 1, then go back and call it again for each cell with * 'pass' set to 2. On the first pass, we'll compute the column width * contribution for each cell that occupies a single column. On the second * pass, we'll calculate the contribution for each cell that spans multiple * columns (i.e., COLSPAN > 1). We have to wait to do the multi-column * cells until after doing all of the single-column cells because we * allocate the multi-column space in proportion to the widths calculated * for the single-column cells. */ void CHtmlTagTableCell::compute_column_metrics(int pass) { CHtmlTagTABLE *table; size_t c; long min_width; long max_width; /* get the settings from the saved metrics */ min_width = cont_min_width_; max_width = cont_max_width_; /* * If we have an explicit WIDTH setting in pixels, then use this as our * maximum rather than the actual maximum. We use this as the maximum * because an explicit preferred size setting overrides the implied * preferred size, which is the content size. */ if (width_set_ && !width_pct_) { /* limit our maximum width to the requested size */ max_width = width_; /* make sure we don't go under our minimum content width, though */ if (max_width < min_width) max_width = min_width; } /* * get my table -- if I don't have one, we can't format this item as a * table cell */ table = get_container()->get_table_container(); if (table == 0) return; /* * If we have span only one column (COLSPAN == 1), we simply contribute * our entire width to our one column. * * If we span multiple columns, we have to divide our width among our * spanned columns. Rather than distributing the width in equal parts * to our columns, dole it out in proportion to the widths the columns * would have without our contribution. To do this, we have to wait * until our second pass through the table, at which point the * contributions from other rows will be known. */ if (colspan_ <= 1) { CHtmlTag_column *colp = table->get_column(colnum_); /* we span only one column, so give our full width to it */ colp->add_cell_widths(min_width, max_width); /* if we have an explicit WIDTH setting, allocate it to the column */ if (width_set_) { /* apply the pixel or percentage width, as appropriate */ if (width_pct_) colp->add_cell_pct_width(width_); else colp->add_cell_pix_width(width_); } } else if (pass == 2) { long min_tot, max_tot, width_tot; long min_extra, max_extra, width_extra; long min_rem, max_rem, width_rem; CHtmlTag_column *colp; /* * We span multiple columns, and we're on the second pass. * Distribute our column width in proportion to the widths our * spanned columns would otherwise have, if we weren't part of the * table. First, calculate the total widths our columns have so * far. Since we're on the second pass, we will have already * calculated the widths from single-span cells in the same columns * in other rows. */ for (c = colnum_, min_tot = max_tot = width_tot = 0 ; c < colnum_ + colspan_ ; ++c) { /* sum the totals for this column */ colp = table->get_column(c); min_tot += colp->get_min_width(); max_tot += colp->get_max_width(); width_tot += (width_set_ && width_pct_ ? colp->get_pct_width() : colp->get_pix_width()); } /* calculate the excess space we have to dole out */ min_extra = (min_width > min_tot ? min_width - min_tot : 0); max_extra = (max_width > max_tot ? max_width - max_tot : 0); /* if the WIDTH attribute is set, calculate its excess as well */ width_extra = 0; if (width_set_) { /* * for percentages, dole out the entire percentage * proportionally; for pixel widths, dole out only the excess * pixel width proportionally */ if (width_pct_) width_extra = width_pct_; else width_extra = (width_ > width_tot ? width_ - width_tot : 0); } /* * keep track of how much we've actually doled out, to ensure that * we don't lose any pixels through rounding errors */ min_rem = min_extra; max_rem = max_extra; width_rem = width_extra; /* dole out our excess min and max widths proportionally */ for (c = colnum_ ; c < colnum_ + colspan_ ; ++c) { long min_cur, max_cur; long min_delta, max_delta; int is_last_col = (c == colnum_ + colspan_ - 1); /* get this column and its current metrics */ colp = table->get_column(c); min_cur = colp->get_min_width(); max_cur = colp->get_max_width(); /* * Compute the proportion of the extra widths to hand out. If * the existing total is zero, it means that the columns are * all otherwise empty (i.e., it has no COLSPAN=1 entries at * all), so dole out our space evenly in this case. In any * case, if this is the last column, to avoid losing pixels * through accumulated rounding errors, give the entire unused * balance to this column. */ min_delta = (is_last_col ? min_rem : (min_tot != 0 ? (min_extra * min_cur) / min_tot : min_extra / colspan_)); max_delta = (is_last_col ? max_rem : (max_tot != 0 ? (max_extra * max_cur) / max_tot : max_extra / colspan_)); /* add in the deltas */ colp->add_cell_widths(min_cur + min_delta, max_cur + max_delta); /* deduct these deltas from the total to be allocated */ min_rem -= min_delta; max_rem -= max_delta; /* if the width is set, hand out the extra set width as well */ if (width_set_) { long width_cur; long width_delta; /* get the appropriate current column width */ if (width_pct_) width_cur = colp->get_pct_width(); else width_cur = colp->get_pix_width(); /* calculate the proportional share to hand out */ width_delta = (is_last_col ? width_rem : (width_tot != 0 ? (width_extra * width_cur) / width_tot : width_extra / colspan_)); /* allocate the delta to the appropriate width counter */ if (width_pct_) colp->add_cell_pct_width(width_delta); else colp->add_cell_pix_width(width_cur + width_delta); /* deduct the delta from the total to be allocated */ width_rem -= width_delta; } } } } /* * Receive information on the formatted height of the cell's contents. * The formatter calls this after the cell's contents have been * formatted at the cell's proper width in pass 2. In addition, we'll * receive information on the base y position of the cell's contents, so * that we can later offset the cell's contents to their final y * positions. */ void CHtmlTagTableCell::set_table_cell_height(long height, long base_y_pos) { /* save the y offset of the cell's contents */ disp_y_base_ = base_y_pos; /* assume we'll use the actual height */ content_height_ = height; /* if the HEIGHT attribute is greater, use it instead */ if (height_set_ && height_ > content_height_) content_height_ = height_; } /* * Set the cell's vertical position */ void CHtmlTagTableCell::set_cell_y_pos(CHtmlFormatter *formatter, long row_y_offset, long row_height, HTML_Attrib_id_t row_valign) { HTML_Attrib_id_t valign; long ypos; /* use the row's valign if we don't have our own */ valign = (valign_ == HTML_Attrib_invalid ? row_valign : valign_); /* figure the position based on the alignment */ switch(valign) { case HTML_Attrib_top: /* align at the top of the row */ ypos = row_y_offset; break; case HTML_Attrib_middle: case HTML_Attrib_invalid: /* align centered in the row */ ypos = row_y_offset + (row_height - content_height_) / 2; break; case HTML_Attrib_bottom: /* align at the bottom of the row */ ypos = row_y_offset + (row_height - content_height_); break; default: /* other attributes should never occur; ignore them if they do */ break; } /* tell the formatter to set y positions for items in the cell */ formatter->set_table_cell_y_pos(disp_, disp_last_, ypos, disp_y_base_, row_y_offset); /* set my display item's position height */ disp_->set_cell_height(row_height); } /* ------------------------------------------------------------------------ */ /* * SOUND tag */ CHtmlTagSOUND::~CHtmlTagSOUND() { if (sound_ != 0) sound_->remove_ref(); } HTML_attrerr CHtmlTagSOUND::set_attribute(CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen) { int pct; HTML_attrerr ret; switch(attr_id) { case HTML_Attrib_src: src_.set_url(val, vallen); break; case HTML_Attrib_layer: /* allow foreground, background, or ambient */ switch(layer_ = parser->attrval_to_id(val, vallen)) { case HTML_Attrib_foreground: case HTML_Attrib_background: case HTML_Attrib_ambient: case HTML_Attrib_bgambient: break; default: /* invalid LAYER setting */ return HTML_attrerr_inv_enum; } break; case HTML_Attrib_alt: alt_.set(val, vallen); break; case HTML_Attrib_repeat: /* see if we have a keyword value */ has_repeat_ = TRUE; if (parser->attrval_to_id(val, vallen) == HTML_Attrib_loop) { /* loop indefinitely - set repeat_ to zero to so indicate */ repeat_ = 0; } else { /* treat it as a number */ return set_number_attr(&repeat_, val, vallen); } break; case HTML_Attrib_fadein: /* check for the CROSSFADE qualifier */ crossfade_in_ = check_comma_kw(&val, &vallen, "crossfade"); /* get the fade time */ return set_float_attr(&fadein_, val, vallen); case HTML_Attrib_fadeout: /* check for the CROSSFADE qualifier */ crossfade_out_ = check_comma_kw(&val, &vallen, "crossfade"); /* get the fade time */ return set_float_attr(&fadeout_, val, vallen); case HTML_Attrib_volume: /* get the volume */ ret = set_number_attr(&vol_, val, vallen); /* peg it to the 0..100 range */ if (vol_ < 0) vol_ = 0; if (vol_ > 100) vol_ = 100; /* return the result from the attribute parse */ return ret; case HTML_Attrib_random: return set_number_or_pct_attr(&random_, &pct, val, vallen); case HTML_Attrib_cancel: /* * see if they're cancelling a layer; if not, they're cancelling * all layers */ cancel_ = TRUE; switch(layer_ = parser->attrval_to_id(val, vallen)) { case HTML_Attrib_foreground: case HTML_Attrib_background: case HTML_Attrib_ambient: case HTML_Attrib_bgambient: /* accept the layer setting */ break; case HTML_Attrib_cancel: /* no layer setting - cancel all layers */ layer_ = HTML_Attrib_invalid; break; default: /* invalid setting */ return HTML_attrerr_inv_enum; } break; case HTML_Attrib_interrupt: /* note that we're interrupting any currently-playing sound */ interrupt_ = TRUE; break; case HTML_Attrib_sequence: /* accept REPLACE, RANDOM, and CYCLE */ switch(sequence_ = parser->attrval_to_id(val, vallen)) { case HTML_Attrib_replace: case HTML_Attrib_random: case HTML_Attrib_cycle: /* valid answer */ break; default: /* invalid answer */ return HTML_attrerr_inv_enum; } break; default: /* ignore other attributes */ break; } /* no problems */ return HTML_attrerr_ok; } /* * "Format" a sound tag. This initiates playback of the sound. */ void CHtmlTagSOUND::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { HTML_res_type_t restype; /* if the tag is obsolete, ignore it */ if (obsolete_) return; /* do nothing if sound tags aren't allowed in this kind of window */ if (!formatter->allow_sound_tags()) return; /* remember the previous sound tag */ prv_sound_ = formatter->get_last_sound_tag(); /* if this is a CANCEL operation, cancel sounds */ if (cancel_) { formatter->cancel_sound(layer_, fadeout_, crossfade_out_, FALSE); /* * this tag doesn't need to be played back again, since * everything up to this point is now obsolete */ obsolete_ = TRUE; /* * Obsolete all prior sound tags in the same layer, since * they'll never need to be played again. */ obsolete_prev_sounds(layer_); /* done */ return; } /* if we don't have a source, there's nothing to do */ if (src_.get_url() == 0) { oshtml_dbg_printf("<SOUND>: SRC must be specified\n"); return; } /* get the type of the source and check it for validity */ switch(restype = CHtmlResType::get_res_type(src_.get_url())) { case HTML_res_type_MIDI: case HTML_res_type_WAV: case HTML_res_type_MP123: case HTML_res_type_OGG: /* these are valid types for sound playback */ break; default: /* other types can't be played as sounds */ oshtml_dbg_printf("<SOUND>: invalid resource type\n"); return; } /* * If characteristics of the sound (such as its type) determine the * layer in which it must play, force the correct layer. */ if (restype == HTML_res_type_MIDI) { /* MIDI files always play in the background */ layer_ = HTML_Attrib_background; } else if (restype == HTML_res_type_MP123 || restype == HTML_res_type_OGG) { /* * MPEG audio and Ogg Vorbis audio can go in any layer - these * formats are equally suitable for background music or sound * effects in any other layer. We can't make any guesses about the * layer for these types. */ } else if (restype == HTML_res_type_WAV) { /* * WAV resources can go in the foreground or ambient layers - if * the layer is unspecified, try to guess based on whether we * have a RANDOM setting or not */ if (layer_ == HTML_Attrib_invalid) { if (random_ == 0) { /* no RANDOM setting - it's a foreground sound */ layer_ = HTML_Attrib_foreground; } else { /* RANDOM is set - it's an ambient sound */ layer_ = HTML_Attrib_ambient; } } } /* if we don't know the layer, we can't play the sound */ if (layer_ == HTML_Attrib_invalid) { oshtml_dbg_printf("<SOUND>: LAYER must be specified\n"); return; } /* if we don't have the sound loaded, load it now */ if (sound_ == 0) { /* find it in the cache or load it */ sound_ = formatter->get_res_cache()-> find_or_create(win, formatter->get_res_finder(), &src_); /* if it's not a valid image, log an error */ if (sound_ == 0 || sound_->get_sound() == 0) oshtml_dbg_printf("<SOUND>: resource %s is not a valid sound\n", src_.get_url()); /* count our reference to the cache object as long as we keep it */ if (sound_ != 0) sound_->add_ref(); } /* interrupt current sound in our layer if appropriate */ if (interrupt_) { /* * Cancel the current sound in this layer. If the * FADEIN=CROSSFADE,time option was set, do a cross-fade: do a * background fade on the old sound with the same duration as the * fade-in on the new sound. */ formatter->cancel_sound( layer_, crossfade_in_ ? fadein_ : 0.0, crossfade_in_, FALSE); /* * Obsolete all prior sound tags in the same layer, since * they'll never need to be played again. */ obsolete_prev_sounds(layer_); /* * We don't need to do the interrupt again in the future, so * turn the interrupt flag off. This is important for background * and ambient sounds, since reformatting this tag in the future * could cancel this sound itself or a sound after it. */ interrupt_ = FALSE; } /* * If this is in the AMBIENT layer, and it has a non-zero RANDOM * value, and no REPEAT value is specified, assume REPEAT=LOOP */ if (layer_ == HTML_Attrib_ambient && random_ != 0 && !has_repeat_) repeat_ = 0; /* tell the formatter to play the sound */ formatter->play_sound(this, layer_, sound_, repeat_, random_, sequence_); /* * If this is a foreground sound, make it obsolete now. Foreground * sounds are played in response to a change in game state; * reformatting the current text in the future will not cause the * game state change, hence it's not necessary to replay a * foreground sound. */ if (layer_ == HTML_Attrib_foreground) make_obsolete(); } /* * Obsolete all prior sounds in a given layer */ void CHtmlTagSOUND::obsolete_prev_sounds(HTML_Attrib_id_t layer) { CHtmlTagSOUND *cur; CHtmlTagSOUND *prv; /* * start out with the sound just before me, and work back to the * start of the list */ for (cur = this->prv_sound_ ; cur != 0 ; cur = prv) { /* remember the previous sound, in case we forget about it below */ prv = cur->prv_sound_; /* * if we're obsoleting all layers, or if this tag is in the * given layer, the tag is now obsolete */ if (layer == HTML_Attrib_invalid || layer == cur->layer_) { /* this tag is now obsolete */ cur->make_obsolete(); } /* * If we're cancelling every layer, we'll end up obsoleting * everything prior to me, so there will be no point in doing so * again in the future. Hence, we can forget about the previous * sound immediately, to truncate any future list scans (which * will keep things from slowing down as we add sounds). */ if (layer == HTML_Attrib_invalid) cur->prv_sound_ = 0; } } /* * Mark this sound as obsolete */ void CHtmlTagSOUND::make_obsolete() { /* remember that I'm obsolete */ obsolete_ = TRUE; /* I don't need to keep my cache object around any more */ if (sound_ != 0) { sound_->remove_ref(); sound_ = 0; } } /* ------------------------------------------------------------------------ */ /* * Special <?T2> tag */ /* * active on formatting */ void CHtmlTagQT2::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { /* tell the formatter we're in T3 mode */ formatter->set_t3_mode(FALSE); } /* ------------------------------------------------------------------------ */ /* * Special <?T3> tag */ /* * active on formatting */ void CHtmlTagQT3::format(CHtmlSysWin *win, CHtmlFormatter *formatter) { /* tell the formatter we're in T3 mode */ formatter->set_t3_mode(TRUE); } /* ------------------------------------------------------------------------ */ /* * Debug routines */ #ifdef TADSHTML_DEBUG #include <stdarg.h> #include <stdio.h> void CHtmlTag::debug_indent(int indent) { char buf[128]; char *p; for (indent *= 3, p = buf ; indent && p - buf + 1 < sizeof(buf) ; --indent, *p++ = ' '); *p = '\0'; oshtml_dbg_printf(buf); } void CHtmlTag::debug_printf(const char *msg, ...) const { va_list argptr; va_start(argptr, msg); oshtml_dbg_vprintf(msg, argptr); va_end(argptr); } void CHtmlTagText::debug_dump(int indent, class CHtmlTextArray *arr) { debug_dump_name(indent); if (len_) debug_printf("[%.*s]\n", len_, arr->get_text(txtofs_)); else debug_printf("[]\n"); debug_dump_next(indent, arr); } #endif /* TADSHTML_DEBUG */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmltags.h���������������������������������������������������������������������0000664�0000000�0000000�00000336132�12650170723�0016464�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/html/htmltags.h,v 1.3 1999/07/11 00:46:40 MJRoberts Exp $ */ /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmltags.h - tag classes Function Notes Modified 08/26/97 MJRoberts - Creation */ #ifndef HTMLTAGS_H #define HTMLTAGS_H #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef HTMLURL_H #include "htmlurl.h" #endif #ifndef HTMLSYS_H #include "htmlsys.h" #endif /* ------------------------------------------------------------------------ */ /* * Error codes for setting attributes (CHtmlTag::set_attribute) */ enum HTML_attrerr { HTML_attrerr_ok, /* success */ HTML_attrerr_inv_attr, /* attribute is not valid for this tag */ HTML_attrerr_inv_type, /* value is not of the appropriate type */ HTML_attrerr_inv_enum, /* value doesn't match allowed choices */ HTML_attrerr_out_of_range, /* value is out of range */ HTML_attrerr_too_many_coords, /* too many coordinates */ HTML_attrerr_odd_coords, /* odd number of coordinates in polygon */ HTML_attrerr_missing_delim /* missing delimiter */ }; /* ------------------------------------------------------------------------ */ /* * Basic tag class */ class CHtmlTag { public: CHtmlTag() { nxt_ = 0; container_ = 0; } virtual ~CHtmlTag(); /* * Process the end-tag. This is a static function invoked directly * from the parser to begin processing the </xxx> closing tag. Almost * all tags are handled the same way here, but a few tag classes need * to "override" this; for example, the </BODY> tag isn't a real * container and simply ignores closing tags, so it overrides this to * do nothing at all. * * Note that this is a static, but we can still override it as though * it were virtual, because the registration table (in htmlreg.h) * automatically associates each tag name with the proper class's * static. If this isn't overridden, then the class static will simply * "inherit" the base class static; otherwise, the class static will be * the one defined in the specific overriding class. */ static void process_end_tag(class CHtmlParser *parser, const textchar_t *tag, size_t len); /* format this tag */ virtual void format(class CHtmlSysWin *, class CHtmlFormatter *) { } /* * Determine if we can prune this tag. Certain types of tags cannot * be pruned because they have global effects. By default, we'll * return true; any tag that cannot be pruned due to global effects * should return false. */ virtual int can_prune_tag() const { return TRUE; } /* * If this tag can't be pruned (i.e., can_prune_tag() returns * false), this method should try to delete any contents of the tag * that can be safely deleted. In some cases, a container tag (such * as TITLE) can't be pruned, but the contents of the tag can be * safely removed. By default, we won't do anything here. */ virtual void try_prune_contents(class CHtmlTextArray *) { } /* * Perform any work necessary just before deleting this tag as part * of pruning a tree. Tags with children should call this method on * all of their children. Tags with text should delete their text * from the text array. */ virtual void prune_pre_delete(class CHtmlTextArray *arr) { } /* * Set an attribute value. Each tag must override this routine, * since by default we don't recognize any attributes. Returns an * HTML_attr_err error code. */ virtual HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); /* * Immediately after the parser finishes scanning the tag's * attribute list, it will create a new instance of the tag object, * then call this method to let the tag take any parse-time action * necessary. Container start tags, for example, will place * themselves on the parser tag stack. By default, we'll simply add * this tag to the current container. */ virtual void on_parse(CHtmlParser *parser); /* get my tag name - must be overridden by each subclass */ virtual const textchar_t *get_tag_name() const = 0; /* check if my tag name matches a given name */ int tag_name_matches(const textchar_t *nm, size_t nmlen) const; /* check if two tag names match */ static int tag_names_match(const textchar_t *nm1, size_t len1, const textchar_t *nm2, size_t len2); /* * Determine if a particular tag is allowed while within this * container. Each time a tag is parsed, the parser will check with * each enclosing container to make sure that a particular tag is * allowed by the container. By default, this routine returns true * to indicate that the tag is allowed. Some container tags will * want to override this; for example, the <TITLE> tag does not * allow any markups, and the <PRE> tag does not allow block * markups. */ virtual int allow_tag(CHtmlTag *) const { return TRUE; } /* * certain tags are not allowed in lists - these should ovveride * this and return false */ virtual int allowed_in_list() const { return TRUE; } /* * Get the list nesting level. Since most elements are not list * containers, this by default returns the list nesting level of my * container, or zero if I have no container. */ virtual int get_list_nesting_level() const; /* tags are arranged into lists */ CHtmlTag *get_next_tag() const { return nxt_; } /* determine if it's okay to proceed into formatting this tag */ virtual int ready_to_format() const { return TRUE; } /* * set the next tag to the given tag (used by the parser when * unlinking tags from trees) */ void set_next_tag(CHtmlTag *nxt) { nxt_ = nxt; } /* each tag has a container */ class CHtmlTagContainer *get_container() const { return container_; } /* am I a container? */ virtual int is_container_tag() const { return FALSE; } /* add a new item following this tag */ void append(CHtmlTag *tag) { nxt_ = tag; } /* set my container */ void set_container(class CHtmlTagContainer *container) { container_ = container; } /* * Get this item as a table cell. If it's not a table cell, this * routine returns null. */ virtual class CHtmlTagTableCell *get_as_table_cell() { return 0; } /* * Get this item as a table row. If it's not a table row, this * routine returns null. */ virtual class CHtmlTagTR *get_as_table_row() { return 0; } /* * Get the next tag in format order. When formatting, we start * with the top container, then go to the first child of the * container, then to it's first child, and so on; then to the next * sibling of that tag, and so on; then to the next sibling of its * container, then to its first child, and so on. In effect, we do * a depth-first traversal, but we process the container nodes on * the way down. */ virtual CHtmlTag *get_next_fmt_tag(class CHtmlFormatter *formatter); /* * Re-insert this tag's contents into the text array. This is used * when a tag's display position is different than its position in * the parsed tag list. Text must appear in the same order in the * text array that it appears in the display list, so when a tag's * display items are displayed out of order with respect to the tag, * we must use this routine to re-insert the tag's contents at the * proper point. This is used, for example, to put a table's * caption's text after the table's text when the caption is aligned * below the table. * * The default implementation does nothing, because default tags * have no contents and no text. */ virtual void reinsert_text_array(class CHtmlTextArray *) { } /* -------------------------------------------------------------------- */ /* * Debugging methods. These methods are conditionally compiled; if * debugging is turned off at compile time, these methods will not * be defined. */ HTML_IF_DEBUG(virtual void debug_dump(int indent, class CHtmlTextArray *arr) { debug_dump_name(indent); debug_dump_next(indent, arr); }) HTML_IF_DEBUG(void debug_dump_name(int indent) { debug_indent(indent); debug_printf("%s\n", get_tag_name()); debug_dump_attrib(indent); }) HTML_IF_DEBUG(void debug_dump_next(int indent, class CHtmlTextArray *arr) { if (nxt_) nxt_->debug_dump(indent, arr); } ) HTML_IF_DEBUG(virtual void debug_dump_attrib(int /*indent*/) { }) HTML_IF_DEBUG(void debug_indent(int indent);) HTML_IF_DEBUG(void debug_printf(const char *msg, ...) const;) protected: /* * Most block tags cause a paragraph break. This service routine * should be called by the on_parse() routine of each tag that * causes a paragraph break just before themselves, and in the * on_close() of container tags that cause a paragraph break after * themselves. This routine tells the parser to insert a paragraph * break into the text stream, and also has the effect of supplying * an implicit </P> tag if a <P> tag is on the stack. */ void break_paragraph(CHtmlParser *parser, int isexplicit); /* process a CLEAR attribute */ void format_clear(class CHtmlFormatter *formatter, HTML_Attrib_id_t clear); /* service routine to parse a color attribute value */ HTML_attrerr set_color_attr(class CHtmlParser *parser, HTML_color_t *color_var, const textchar_t *val, size_t vallen); /* * Map a color. If the color is a special parameterized color * values, we'll map it to the corresponding system RGB color here. * If the color is already an RGB color, we'll simply return the * value unchanged. This routine always returns a fully-resolved * RGB setting. */ HTML_color_t map_color(class CHtmlSysWin *win, HTML_color_t color); /* parse a CLEAR attribute */ HTML_attrerr set_clear_attr(HTML_Attrib_id_t *clear, class CHtmlParser *parser, const textchar_t *val, size_t vallen); /* * Service routines to parse a numeric decimal/hex attribute value */ HTML_attrerr set_number_attr(long *num_var, const textchar_t *val, size_t vallen); HTML_attrerr set_float_attr(double *num_var, const textchar_t *val, size_t vallen); HTML_attrerr set_hex_attr(long *num_var, const textchar_t *val, size_t vallen); /* check an attribute string for a keyword followed by a comma */ int check_comma_kw(const textchar_t **val, size_t *vallen, const textchar_t *kw); /* * Service routine to parse a numeric value that may be either an * absolute value or a percentage. If the last character is a * percent sign, it's a percentage, otherwise it's an absolute * value. */ HTML_attrerr set_number_or_pct_attr(long *num_var, int *is_pct, const textchar_t *val, size_t vallen); /* * Set a coordinate attribute. If pct is not null, we'll allow a * percentage sign, and return in *pct whether or not a percentage * sign was found. Advances the string pointer to the next comma. */ HTML_attrerr set_coord_attr(long *num_var, int *is_pct, const textchar_t **val, size_t *vallen); /* find the end of a token in a delimited attribute list */ const textchar_t *find_attr_token(const textchar_t *val, size_t *vallen, const textchar_t *delim_chars); private: /* next tag in my list */ CHtmlTag *nxt_; /* my container */ class CHtmlTagContainer *container_; }; /* * Each final tag subclass must define a static method that returns a * string giving the name of the tag, and must also define a static * method that returns a new instance of the class. This macro can be * used to provide these definitions. This macro also defines the * virtual tag name retrieval function. */ #define HTML_TAG_MAP_NOCONSTRUCTOR(tag_class_name, tag_name_string) \ static const textchar_t *get_tag_name_st() { return tag_name_string; } \ const textchar_t *get_tag_name() const { return tag_name_string; } \ static CHtmlTag *create_tag_instance(class CHtmlParser *prs) \ { return new tag_class_name(prs); } #define HTML_TAG_MAP(tag_class_name, tag_name_string) \ tag_class_name(class CHtmlParser *) { } \ HTML_TAG_MAP_NOCONSTRUCTOR(tag_class_name, tag_name_string) /* * Note that each final tag subclass that corresponds to a tag name * must be registered so that the parser can include the tag in its tag * name table. See htmlreg.h for information. */ /* ------------------------------------------------------------------------ */ /* * Container tag class. This subclass of the basic tag can be used as * the base class for any tag that acts as a container. */ class CHtmlTagContainer: public CHtmlTag { public: CHtmlTagContainer() { contents_first_ = 0; contents_last_ = 0; closed_ = FALSE; } ~CHtmlTagContainer(); /* am I a container? */ virtual int is_container_tag() const { return TRUE; } /* * Internal processing to exit the formatting of the container. We * must undo any formatter state changes (such as margin sizes or font * settings) that we made on entry; by default, we don't do anything, * but container subclasses that modify formatter state on entry must * undo those changes on exit. */ virtual void format_exit(class CHtmlFormatter *) { } /* perform pre-deletion work on all of my children */ void prune_pre_delete(class CHtmlTextArray *arr); /* * A container tag pushes itself onto the container stack upon * being parsed */ virtual void on_parse(CHtmlParser *parser); /* * When we parse a close tag for an open container tag, the parser * will call this routine before actually closing the tag. This * gives us a chance to do any implicit closing of other tags that * may be nested within this tag. The default doesn't do anything. */ virtual void pre_close(CHtmlParser *) { } /* * When we reach the end of a container, the parser will call on_close * on the current container to let it know that it's been closed. * Note that we don't need to pop the container off the parser stack, * as the parser will always do this automatically. Note also that * this is called while we're still the active container. */ virtual void on_close(CHtmlParser *) { /* note that the tag is closed */ closed_ = TRUE; } /* * receive notification that we've just finished closing the tag; the * immediate container is now re-established as the active container */ virtual void post_close(CHtmlParser *) { } /* determine whether the close tag has been parsed yet */ int is_closed() const { return closed_; } /* * Container tags can have sublists, containing the list of data * and markups within the container. This returns the head of the * sublist. */ CHtmlTag *get_contents() const { return contents_first_; } /* get the lats item in my sublist */ CHtmlTag *get_last_contents() const { return contents_last_; } /* * Set the first child (this is used by the parser when unlinking * tags from trees) */ void set_contents(CHtmlTag *cont) { contents_first_ = cont; } /* Add a new item at the end of my sublist */ void append_to_sublist(CHtmlTag *tag); /* Delete all of my contents */ void delete_contents(); /* * Get the next tag in format order. The next tag after formatting * a container is the first child of the container. */ virtual CHtmlTag *get_next_fmt_tag(class CHtmlFormatter *formatter); /* re-insert my contents into the text array */ virtual void reinsert_text_array(class CHtmlTextArray *arr); /* * If this item is a list container, return this item, otherwise * return the enclosing container that is a list container. Returns * null if there is no enclosing list container. */ virtual class CHtmlTagListContainer *get_list_container() { /* * default items are not list containers; return my container * cast to a list container, or null if I have no container */ if (get_container() == 0) return 0; else return get_container()->get_list_container(); } /* * If this item is a quote tag, return this item, otherwise return * the enclosing container that is a quote item. Returns null if * there is no enclosing quote container. */ virtual class CHtmlTagQ *get_quote_container() { if (get_container() == 0) return 0; else return get_container()->get_quote_container(); } /* * If this item is a table/row/cell container, return this item, * otehrwise return the enclosing container that is a table/row * container. Returns null if there is no enclosing table/row * container. */ virtual class CHtmlTagTABLE *get_table_container() { if (get_container() == 0) return 0; else return get_container()->get_table_container(); } virtual class CHtmlTagTR *get_table_row_container() { if (get_container() == 0) return 0; else return get_container()->get_table_row_container(); } virtual class CHtmlTagTableCell *get_table_cell_container() { if (get_container() == 0) return 0; else return get_container()->get_table_cell_container(); } HTML_IF_DEBUG(virtual void debug_dump(int indent, class CHtmlTextArray *arr) { debug_dump_name(indent); if (contents_first_) contents_first_->debug_dump(indent + 1, arr); debug_dump_next(indent, arr); }) protected: /* head of sublist of contents of this container */ CHtmlTag *contents_first_; CHtmlTag *contents_last_; /* flag indicating that the close tag has been encountered */ int closed_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Special "relax" tag - this tag doesn't do anything, but is sometimes * useful to pad the list. */ class CHtmlTagRelax: public CHtmlTag { public: /* this class does not correspond to a named tag */ const textchar_t *get_tag_name() const { return "<!-- Relax -->"; } /* we have no effect on formatting */ void format(class CHtmlSysWin *, class CHtmlFormatter *) { } }; /* ------------------------------------------------------------------------ */ /* * Special outermost container class. This is the container that wraps * the entire document. */ class CHtmlTagOuter: public CHtmlTagContainer { public: /* this class does not correspond to a named tag */ const textchar_t *get_tag_name() const { return "<!-- Outer Container -->"; } /* format the tag */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); }; /* ------------------------------------------------------------------------ */ /* * Special text tag class. This tag is used to wrap a block of text. */ class CHtmlTagText: public CHtmlTag { public: CHtmlTagText(class CHtmlTextArray *arr, class CHtmlTextBuffer *buf); CHtmlTagText(class CHtmlTextArray *arr, const textchar_t *buf, size_t buflen); ~CHtmlTagText(); /* delete my text from the text array when pruning the tree */ void prune_pre_delete(class CHtmlTextArray *arr); const textchar_t *get_tag_name() const { return "<!-- Text -->"; } void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); HTML_IF_DEBUG(virtual void debug_dump(int indent, class CHtmlTextArray *arr);) /* get this item's text offset */ unsigned long get_text_ofs() const { return txtofs_; } /* re-insert my text into the text array */ virtual void reinsert_text_array(class CHtmlTextArray *arr); protected: CHtmlTagText() { } /* memory containing the text */ unsigned long txtofs_; size_t len_; }; /* * Text tag subclass for preformatted text */ class CHtmlTagTextPre: public CHtmlTagText { public: CHtmlTagTextPre(int tab_size, class CHtmlTextArray *arr, class CHtmlTextBuffer *buf); CHtmlTagTextPre(int tab_size, class CHtmlTextArray *arr, const textchar_t *buf, size_t buflen); const textchar_t *get_tag_name() const { return "<!-- Preformatted Text -->"; } void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); private: /* initialize the text buffer - used by the constructors */ void init_text(int tab_size, class CHtmlTextArray *arr, const textchar_t *buf, size_t buflen); }; /* * Text tag subclass for command line input. This class behaves * largely like a normal text tag, except for some special behavior. * First, it can be edited through the UI. Second, the text in the * buffer isn't stored in the text array, but rather in a buffer managed * by the UI that's handling the input; the UI makes changes to its * buffer, and notifies the formatter of changes so that the formatter * can update the display. Once the command is completed, it can be * committed, which will store it in the text array as usual; after * being committed, the command can no longer be edited, and behaves * like a normal text tag. */ class CHtmlTagTextInput: public CHtmlTagText { public: CHtmlTagTextInput(class CHtmlTextArray *arr, const textchar_t *buf, size_t len); /* format the tag */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); /* commit the command to the text array */ void commit(class CHtmlTextArray *arr); /* * Update the length of the text in the buffer. Since the UI * manages the buffer, it must keep us apprised of changes to the * buffer's length with this function. */ void setlen(class CHtmlFormatterInput *fmt, size_t len); /* re-insert my text into the text array */ virtual void reinsert_text_array(class CHtmlTextArray *arr); /* change the underlying UI buffer */ void change_buf(CHtmlFormatterInput *fmt, const textchar_t *buf); private: /* the text pointer, when it's still in the UI's buffer */ const textchar_t *buf_; }; /* * Text tag subclass for debugger source file lines. In addition to the * normal information we store for a text item, we store the seek * location in the source file of the line; this information is used to * associate the line in memory with the debug information in the .GAM * file. * * This class is used only for the debugger. */ class CHtmlTagTextDebugsrc: public CHtmlTagTextPre { public: CHtmlTagTextDebugsrc(unsigned long linenum, int tab_size, class CHtmlTextArray *arr, const textchar_t *buf, size_t buflen); const textchar_t *get_tag_name() const { return "<!-- tdb source text -->"; } void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); private: unsigned long linenum_; }; /* ------------------------------------------------------------------------ */ /* * Special tag for a soft hyphen. This indicates a point at which we can * optionally break the line (to fit the margins) by adding a hyphen. */ class CHtmlTagSoftHyphen: public CHtmlTagText { public: CHtmlTagSoftHyphen(class CHtmlTextArray *arr) : CHtmlTagText(arr, "", 0) { } const textchar_t *get_tag_name() const { return "­"; } void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); protected: }; /* ------------------------------------------------------------------------ */ /* * Special tag for non-breaking spaces. */ class CHtmlTagNBSP: public CHtmlTagText { public: CHtmlTagNBSP(class CHtmlTextArray *arr, const textchar_t *buf) : CHtmlTagText(arr, buf, get_strlen(buf)) { /* remember my width in spaces */ wid_ = get_strlen(buf); } const textchar_t *get_tag_name() const { return " "; } void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); protected: /* our width, in ordinary space widths */ int wid_; }; /* * Special width values for spaces whose sizes depend on font attributes * other than the em size. We normally represent space widths as a number * of spaces per em (i.e., as the denominator in the fraction of an em). * Some space widths are not tied to the em size, though; for these, we * define some manifest constants that are absurdly outside the range that * we need for any of the pre-defined fractional-em spaces. */ #define HTMLTAG_SPWID_ORDINARY 10000 /* width of an ordinary space (' ') */ #define HTMLTAG_SPWID_FIGURE 10001 /* width of the digit '0' */ #define HTMLTAG_SPWID_PUNCT 10002 /* width of a period ('.') */ /* * Special tag for explicitly-sized spaces. */ class CHtmlTagSpace: public CHtmlTagText { public: /* * Construct. Note that we store a single space in the text array as * our plain text representation, unless we're a zero-width space, in * which case we store nothing at all in the text array. */ CHtmlTagSpace(class CHtmlTextArray *arr, int wid, int pre) : CHtmlTagText(arr, wid != 0 ? " " : "", wid != 0 ? 1 : 0) { /* remember my width and preformatted flag */ wid_ = wid; pre_ = pre; } const textchar_t *get_tag_name() const { return "<!-- special space -->"; } void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); protected: /* * our width, expressed as the denominator of the fractional em (or as * one of the special HTMLTAG_SPWID_xxx widths, or as zero for a * zero-width space) */ int wid_; /* are we preformatted? */ int pre_; }; /* ------------------------------------------------------------------------ */ /* * BODY tag */ /* default color settings */ const HTML_color_t HTML_bgcolor_default = 0xffffff; /* white */ const HTML_color_t HTML_textcolor_default = 0x000000; /* black */ class CHtmlTagBODY: public CHtmlTag { public: CHtmlTagBODY(class CHtmlParser *) { use_bgcolor_ = FALSE; use_textcolor_ = FALSE; use_inputcolor_ = FALSE; use_link_color_ = FALSE; use_vlink_color_ = FALSE; use_alink_color_ = FALSE; use_hlink_color_ = FALSE; bg_image_ = 0; } ~CHtmlTagBODY(); /* process the end tag */ static void process_end_tag(class CHtmlParser *, const textchar_t *, size_t) { /* * Unlike in standard HTML, our <BODY> tags aren't containers, so * we don't need or want an ending </BODY> tag. However, since * people familiar with HTML often expect <BODY> to require a * closing </BODY> tag, allow it by simply ignoring it. */ } /* BODY tags have global effect, so they can't be pruned */ virtual int can_prune_tag() const { return FALSE; } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagBODY, "BODY"); HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); private: /* URL for background image */ class CHtmlUrl background_; /* the image cache object for the background image */ class CHtmlResCacheObject *bg_image_; /* background color - ignored if use_bgcolor_ is false */ int use_bgcolor_ : 1; HTML_color_t bgcolor_; /* text color - ignored if use_textcolor_ is false */ int use_textcolor_ : 1; HTML_color_t textcolor_; /* input text color - ignored if use_inputcolor_ is false */ int use_inputcolor_ : 1; HTML_color_t inputcolor_; /* * link, vlink, alink, and hlink colors - the colors are ignored if the * corresponding use_xxx_'s are false */ int use_link_color_ : 1; HTML_color_t link_color_; int use_alink_color_ : 1; HTML_color_t alink_color_; int use_vlink_color_ : 1; HTML_color_t vlink_color_; int use_hlink_color_ : 1; HTML_color_t hlink_color_; }; /* ------------------------------------------------------------------------ */ /* * TITLE tag */ class CHtmlTagTITLE: public CHtmlTagContainer { public: HTML_TAG_MAP(CHtmlTagTITLE, "TITLE"); /* TITLE tags can't be pruned due to global effects */ virtual int can_prune_tag() const { return FALSE; } /* * prune the contents - we can always delete the contents of a * TITLE, even though we can't delete the TITLE itself */ virtual void try_prune_contents(class CHtmlTextArray *txtarr); void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); virtual void format_exit(class CHtmlFormatter *formatter); /* * set my title text - the formatter calls this when we close our * tag to inform us of the text of the title */ void set_title_text(const textchar_t *txt) { /* * Record the title only if it's non-empty. If the caller is * attempting to set an empty title string, it's probably * because we've been pruned; in this case, keep our original * title string. Note that, in the case we actually have an * empty title string, this will be harmless, because we'll * never record a non-empty string internally in the first place * in this case. */ if (txt != 0 && get_strlen(txt) != 0) title_.set(txt); } private: /* the text of our title */ CStringBuf title_; }; /* ------------------------------------------------------------------------ */ /* * Block-level container tag. The block-level tags all have some common * attributes, such as CLEAR and NOWRAP. */ class CHtmlTagBlock: public CHtmlTagContainer { public: CHtmlTagBlock() { /* set default attributes */ clear_ = HTML_Attrib_invalid; nowrap_ = FALSE; } HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void format_exit(class CHtmlFormatter *formatter); protected: /* CLEAR setting - LEFT, RIGHT, or ALL, if anything is set */ HTML_Attrib_id_t clear_; /* NOWRAP specified */ int nowrap_ : 1; /* old line wrap setting */ int old_wrap_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Paragraph tag. The paragraph tag is not a container, but simply a * marker in the text stream. */ class CHtmlTagP: public CHtmlTag { public: CHtmlTagP(CHtmlParser *prs) { init(TRUE, prs); } CHtmlTagP(int isexplicit, CHtmlParser *prs) { init(isexplicit, prs); } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagP, "P"); HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); void on_parse(class CHtmlParser *parser); void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); /* process the end tag */ static void process_end_tag(class CHtmlParser *parser, const textchar_t *, size_t); private: /* initialize */ void init(int isexplicit, CHtmlParser *prs); /* alignment setting */ HTML_Attrib_id_t align_; /* * flag indicating whether this paragraph break was inserted by an * explicit <P> tag in the source text, or implicitly by another tag * that causes a paragraph to end */ int explicit_ : 1; /* NOWRAP setting - if true, we won't wrap within the paragraph */ int nowrap_ : 1; /* CLEAR setting - LEFT, RIGHT, or ALL, if anything is set */ HTML_Attrib_id_t clear_; }; /* ------------------------------------------------------------------------ */ /* * Special internal hilite-on and hilite-off tags. These are * non-container font attribute tags; they act essentially like <B> and * </B>, but are simply in-line tags that don't contain any other tags: * they establish the new font settings until the next similar tag. */ class CHtmlTagHILITE: public CHtmlTag { public: void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); protected: /* * set my font attribute in the given font descriptor; returns true if * anything changed about the font */ virtual int set_attr(class CHtmlFontDesc *desc) = 0; }; class CHtmlTagHILITEON: public CHtmlTagHILITE { public: HTML_TAG_MAP(CHtmlTagHILITEON, "HILITEON"); protected: virtual int set_attr(class CHtmlFontDesc *desc); }; class CHtmlTagHILITEOFF: public CHtmlTagHILITE { public: HTML_TAG_MAP(CHtmlTagHILITEOFF, "HILITEOFF"); protected: virtual int set_attr(class CHtmlFontDesc *desc); }; /* ------------------------------------------------------------------------ */ /* * Division tags */ /* * basic DIV-style tag - shared by DIV and CENTER */ class CHtmlTagDIV_base: public CHtmlTagBlock { public: /* do a paragraph break on opening and closing a heading */ void on_parse(CHtmlParser *parser); void on_close(CHtmlParser *parser); /* do general DIV-style formatting with a given alignment type */ void div_format(class CHtmlFormatter *formatter, HTML_Attrib_id_t alignment); /* leave the division - restore old alignment settings */ void format_exit(class CHtmlFormatter *formatter); private: /* alignment in effect immediately before this division started */ HTML_Attrib_id_t old_align_; }; /* * DIV */ class CHtmlTagDIV: public CHtmlTagDIV_base { public: CHtmlTagDIV(CHtmlParser *) { align_ = HTML_Attrib_invalid; start_txtofs_ = 0; end_txtofs_ = 0; hover_link_ = FALSE; use_hover_fg_ = use_hover_bg_ = hover_underline_ = FALSE; } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagDIV, "DIV"); HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); void on_parse(CHtmlParser *parser); void on_close(CHtmlParser *parser); void post_close(class CHtmlParser *parser); void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void format_exit(class CHtmlFormatter *formatter); void prune_pre_delete(class CHtmlTextArray *arr); private: /* alignment setting */ HTML_Attrib_id_t align_; /* range of text offsets that this DIV covers */ unsigned long start_txtofs_; unsigned long end_txtofs_; /* is there a HOVER=LINK attribute? */ int hover_link_ : 1; /* HOVER=LINK(fgcolor,bgcolor,decoration) values */ HTML_color_t hover_fg_; HTML_color_t hover_bg_; int use_hover_fg_ : 1; int use_hover_bg_ : 1; int hover_underline_ : 1; }; /* * CENTER */ class CHtmlTagCENTER: public CHtmlTagDIV_base { public: HTML_TAG_MAP(CHtmlTagCENTER, "CENTER"); void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter) { /* do division formatting with center alignment */ div_format(formatter, HTML_Attrib_center); /* inherit default */ CHtmlTagDIV_base::format(win, formatter); } }; /* ------------------------------------------------------------------------ */ /* * Font container add-in. This class can be multiply inherited by a * tag class to provide font container attributes. Each final subclass * that inherits this must implement add_attrs() to add the appropriate * attributes for the font. */ class CHtmlFontContAddin { public: virtual ~CHtmlFontContAddin() {} protected: void fontcont_init() { old_font_ = 0; } /* * Do font formatting entry and exit. The subclass's format() and * format_exit() routines, respectively, should call these in the * course of its processing. */ void fontcont_format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void fontcont_format_exit(class CHtmlFormatter *formatter); /* * Add my attributes to a font description. Returns true if the * description was changed, false if not. */ virtual int add_attrs(class CHtmlFormatter *, class CHtmlFontDesc *) = 0; /* font in effect before we added our attributes */ class CHtmlSysFont *old_font_; }; /* ------------------------------------------------------------------------ */ class CHtmlTagADDRESS: public CHtmlTagBlock, public CHtmlFontContAddin { public: CHtmlTagADDRESS(class CHtmlParser *) { fontcont_init(); } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagADDRESS, "ADDRESS"); /* address tags are not allowed in lists */ int allowed_in_list() const { return FALSE; } /* do a paragraph break on opening and closing a heading */ void on_parse(CHtmlParser *parser); void on_close(CHtmlParser *parser); void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter) { fontcont_format(win, formatter); } void format_exit(class CHtmlFormatter *formatter) { fontcont_format_exit(formatter); } /* add font attributes */ int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; class CHtmlTagBLOCKQUOTE: public CHtmlTagBlock { public: HTML_TAG_MAP(CHtmlTagBLOCKQUOTE, "BLOCKQUOTE"); int allowed_in_list() const { return FALSE; } void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void format_exit(class CHtmlFormatter *formatter); private: /* spacing before and after quoted area - calculated in format() */ int break_ht_; }; /* * BQ - HTML 3.0 synonym for BLOCKQUOTE */ class CHtmlTagBQ: public CHtmlTagBLOCKQUOTE { public: CHtmlTagBQ(class CHtmlParser *prs) : CHtmlTagBLOCKQUOTE(prs) { } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagBQ, "BQ"); }; /* ------------------------------------------------------------------------ */ /* * Basic heading tag class */ class CHtmlTagHeading: public CHtmlTagBlock, public CHtmlFontContAddin { public: CHtmlTagHeading() { fontcont_init(); align_ = HTML_Attrib_invalid; } /* heading tags are not allowed in lists */ int allowed_in_list() const { return FALSE; } /* do a paragraph break on opening and closing a heading */ void on_parse(CHtmlParser *parser); void on_close(CHtmlParser *parser); /* parse attributes */ HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); /* format and exit formatting for these fonts */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void format_exit(class CHtmlFormatter *formatter); /* add font attributes */ int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); /* * Get my heading size. All of the heading types behave the same * way, except that they each have a distinctive size. This routine * must be overridden by each heading subclass to return the * appropriate HTML size (1-7) for the heading. */ virtual int get_heading_fontsize() = 0; private: /* * size of vertical whitespace around heading - set during * format(), used again during format_exit() */ int break_ht_; /* alignment */ HTML_Attrib_id_t align_; }; class CHtmlTagH1: public CHtmlTagHeading { public: HTML_TAG_MAP(CHtmlTagH1, "H1"); int get_heading_fontsize() { return 6; } }; class CHtmlTagH2: public CHtmlTagHeading { public: HTML_TAG_MAP(CHtmlTagH2, "H2"); int get_heading_fontsize() { return 5; } }; class CHtmlTagH3: public CHtmlTagHeading { public: HTML_TAG_MAP(CHtmlTagH3, "H3"); int get_heading_fontsize() { return 4; } }; class CHtmlTagH4: public CHtmlTagHeading { public: HTML_TAG_MAP(CHtmlTagH4, "H4"); int get_heading_fontsize() { return 3; } }; class CHtmlTagH5: public CHtmlTagHeading { public: HTML_TAG_MAP(CHtmlTagH5, "H5"); int get_heading_fontsize() { return 2; } }; class CHtmlTagH6: public CHtmlTagHeading { public: HTML_TAG_MAP(CHtmlTagH6, "H6"); int get_heading_fontsize() { return 1; } }; /* ------------------------------------------------------------------------ */ /* * Preformatted text tags. All of these tags cause entry into verbatim * mode, in which whitespace and line breaks in the source text are * obeyed. */ class CHtmlTagVerbatim: public CHtmlTagContainer, public CHtmlFontContAddin { public: CHtmlTagVerbatim() { fontcont_init(); } void on_parse(CHtmlParser *parser); void on_close(CHtmlParser *parser); /* do we translate tags within this type of preformatted block? */ virtual int translate_tags() const = 0; /* format and exit formatting for these fonts */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void format_exit(class CHtmlFormatter *formatter); /* add font attributes */ virtual int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); /* get the font size decrement for this listing type */ virtual int get_font_size_decrement() const { return 1; } HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); private: /* * size of vertical whitespace above and below - calculated during * format(), and used again during format_exit() */ int break_ht_; /* * WIDTH attribute setting; we ignore this, but read it anyway, for * compatibility with other renderers and in case we might want to * use it in the future */ long width_; }; class CHtmlTagPRE: public CHtmlTagVerbatim { public: HTML_TAG_MAP(CHtmlTagPRE, "PRE"); /* we do translate tags within PRE blocks */ int translate_tags() const { return TRUE; } }; class CHtmlTagLISTING: public CHtmlTagVerbatim { public: HTML_TAG_MAP(CHtmlTagLISTING, "LISTING"); /* we do not translate tags within LISTING blocks */ int translate_tags() const { return FALSE; } /* use a smaller font size than the default listing font */ int get_font_size_decrement() const { return 2; } }; class CHtmlTagXMP: public CHtmlTagVerbatim { public: HTML_TAG_MAP(CHtmlTagXMP, "XMP"); /* we do not translate tags within XMP blocks */ int translate_tags() const { return FALSE; } }; /* ------------------------------------------------------------------------ */ /* * List elements */ class CHtmlTagListContainer: public CHtmlTagBlock, public CHtmlFontContAddin { public: CHtmlTagListContainer() { compact_ = FALSE; list_level_ = 0; list_started_ = FALSE; fontcont_init(); } /* I'm a list container */ CHtmlTagListContainer *get_list_container() { return this; } /* return the name of the list item tag making up this list */ virtual const textchar_t *get_list_tag_name() const { return "LI"; } /* pre-close the tag - close any open list item before closing the list */ void pre_close(CHtmlParser *parser); /* certain tags are not allowed within lists */ int allow_tag(CHtmlTag *tag) const { return tag->allowed_in_list(); } /* get my list nesting level */ int get_list_nesting_level() const { return list_level_; } /* * determine if the list is indented overall; by default, lists are * indented overall, but some list subtypes (such as definition * lists) indent some items but not the overall list */ virtual int is_list_indented() const { return TRUE; } /* get the amount to indent the list */ virtual long get_left_indent(class CHtmlSysWin *win, class CHtmlFormatter *formatter) const; /* set the bullet for an item */ virtual void set_item_bullet(class CHtmlTagLI *, class CHtmlFormatter *) { } /* add the display item for the bullet for a list item */ virtual void add_bullet_disp(class CHtmlSysWin *, class CHtmlFormatter *, class CHtmlTagLI *, long /*bullet_width*/) { } /* parse the list tag */ void on_parse(CHtmlParser *parser); /* parse an attribute */ HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); /* format the tag */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void format_exit(class CHtmlFormatter *formatter); /* * begin a list item - each list item should call this before * formatting itself */ void begin_list_item(class CHtmlSysWin *win, class CHtmlFormatter *formatter); /* add font container attributes */ int add_attrs(class CHtmlFormatter *, class CHtmlFontDesc *); private: /* list nesting level */ int list_level_; /* compact flag */ int compact_ : 1; /* line spacing - set in format(), used in format_exit() */ int break_ht_; /* flag: first list item encountered */ int list_started_ : 1; }; class CHtmlTagUL: public CHtmlTagListContainer { public: CHtmlTagUL(class CHtmlParser *) { /* use default bullet type */ type_ = HTML_Attrib_invalid; } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagUL, "UL"); /* set the bullet for an item */ void set_item_bullet(class CHtmlTagLI *item, class CHtmlFormatter *formatter); /* add the bullet display item */ void add_bullet_disp(class CHtmlSysWin *win, class CHtmlFormatter *formatter, class CHtmlTagLI *item, long bullet_width); /* parse an attribute */ HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); /* format the item */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); private: /* type of bullet to use - disc, square, circle */ HTML_Attrib_id_t type_; /* source for bullet when given as an image */ CHtmlUrl src_; }; class CHtmlTagOL: public CHtmlTagListContainer { public: CHtmlTagOL(class CHtmlParser *) { /* use default bullet type */ style_ = '\0'; /* start at number 1 unless otherwise specified */ start_ = 1; /* * presume we'll start a new list, rather than continue * numbering from previous ordered list */ continue_ = FALSE; } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagOL, "OL"); /* set the bullet for an item */ void set_item_bullet(class CHtmlTagLI *item, class CHtmlFormatter *formatter); /* add the bullet display item */ void add_bullet_disp(class CHtmlSysWin *win, class CHtmlFormatter *formatter, class CHtmlTagLI *item, long bullet_width); /* parse an attribute */ HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); /* format the item */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); /* get the amount to indent the list */ virtual long get_left_indent(class CHtmlSysWin *win, class CHtmlFormatter *formatter) const; private: /* style specifier - '1', 'a', 'A', 'i', 'I' */ textchar_t style_; /* starting number */ long start_; /* current value */ long cur_value_; /* flag: continue numbering from end of previous list */ int continue_ : 1; }; class CHtmlTagDL: public CHtmlTagListContainer { public: HTML_TAG_MAP(CHtmlTagDL, "DL"); /* definition lists are made up of DD items */ const textchar_t *get_list_tag_name() const { return "DD"; } /* do not indent the overall list for a definition list */ int is_list_indented() const { return FALSE; } }; /* * DIR - treat this as a synonym for UL */ class CHtmlTagDIR: public CHtmlTagUL { public: CHtmlTagDIR(class CHtmlParser *parser) : CHtmlTagUL(parser) { } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagDIR, "DIR"); }; /* * MENU - treat this as a synonym for UL */ class CHtmlTagMENU: public CHtmlTagUL { public: CHtmlTagMENU(class CHtmlParser *parser) : CHtmlTagUL(parser) { } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagMENU, "MENU"); }; class CHtmlTagLH: public CHtmlTagContainer { public: HTML_TAG_MAP(CHtmlTagLH, "LH"); }; class CHtmlTagLI: public CHtmlTagContainer { public: CHtmlTagLI(class CHtmlParser *) { /* no value has been set yet, so use the default */ value_set_ = FALSE; /* presume we'll inherit bullet type from the list */ type_ = HTML_Attrib_invalid; /* presume we'll inherit numbering style from the list */ style_ = '\0'; /* no image yet */ image_ = 0; } ~CHtmlTagLI(); HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagLI, "LI"); /* parse the item */ void on_parse(CHtmlParser *parser); /* parse an attribute */ HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); /* format the item */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); /* * set the default bullet - if a bullet has already been specified, * leave the specified bullet as it was */ void set_default_bullet(HTML_Attrib_id_t type, const CHtmlUrl *url) { /* if we don't have a type set, use the default type for the list */ if (type_ == HTML_Attrib_invalid) type_ = type; /* if we don't have a source image, use the default for the list */ if (src_.get_url() == 0 && url != 0 && url->get_url() != 0) src_.set_url(url); } /* * set the default value - if a value has already been specified, * leave the value as it was */ void set_default_value(long value) { if (!value_set_) { value_ = value; value_set_ = TRUE; } } /* set the default number display style */ void set_default_number_style(textchar_t style) { if (style_ == '\0') style_ = style; } /* * add a numeric list header display item, using my current value * and display style */ void add_listhdr_number(class CHtmlSysWin *win, class CHtmlFormatter *formatter, long bullet_width); /* * add a bullet list header display item, using the bullet type * previously established for this item */ void add_listhdr_bullet(class CHtmlSysWin *win, class CHtmlFormatter *formatter, long bullet_width); /* get my value */ long get_value() const { return value_; } private: /* current list item value, and flag indicating it's been set */ long value_; int value_set_ : 1; /* list item style */ textchar_t style_; /* bullet type, if specified */ HTML_Attrib_id_t type_; /* source for image for use as bullet */ CHtmlUrl src_; /* image cache object for bullet image */ class CHtmlResCacheObject *image_; }; class CHtmlTagDT: public CHtmlTagContainer { public: HTML_TAG_MAP(CHtmlTagDT, "DT"); /* parse this item */ void on_parse(CHtmlParser *parser); /* format it */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); }; class CHtmlTagDD: public CHtmlTagContainer { public: HTML_TAG_MAP(CHtmlTagDD, "DD"); /* parse this item */ void on_parse(CHtmlParser *parser); /* format it */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void format_exit(class CHtmlFormatter *formatter); }; /* ------------------------------------------------------------------------ */ /* * phrase markup */ /* ------------------------------------------------------------------------ */ /* * Font containers. These items all set a font attribute on entry, and * remove the font attribute on exit. */ class CHtmlTagFontCont: public CHtmlTagContainer, public CHtmlFontContAddin { public: CHtmlTagFontCont() { fontcont_init(); } void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter) { fontcont_format(win, formatter); } void format_exit(class CHtmlFormatter *formatter) { fontcont_format_exit(formatter); } }; /* * Character set tag - this is a special font tag that the parser * generates when it needs to switch to a new character set. This * otherwise leaves the font unchanged. */ class CHtmlTagCharset: public CHtmlTagFontCont { public: CHtmlTagCharset(oshtml_charset_id_t charset) { charset_ = charset; } const textchar_t *get_tag_name() const { return "<!-- Charset -->"; } /* add font attributes */ int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); private: /* character set identifier */ oshtml_charset_id_t charset_; }; /* * Entity inserter. This is an add-in for tags like CREDIT and Q that * insert special character entities. */ class CHtmlTagEntityInserter { public: void on_parse(class CHtmlParser *parser, int charcode, const char *fallback); void prune_pre_delete(class CHtmlTextArray *arr); void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); private: /* text offset of our translation */ unsigned long txtofs_; /* expansion of our translation */ char txt_[32]; size_t len_; /* character set of translation */ oshtml_charset_id_t charset_; int has_charset_; }; /* * CREDIT - HTML 3.0 */ class CHtmlTagCREDIT: public CHtmlTagFontCont { public: HTML_TAG_MAP(CHtmlTagCREDIT, "CREDIT"); /* add my emdash while parsing */ void on_parse(class CHtmlParser *parser) { emdash_.on_parse(parser, 8212, "---"); CHtmlTagFontCont::on_parse(parser); } /* delete my text when pruning the tree */ void prune_pre_delete(class CHtmlTextArray *arr) { emdash_.prune_pre_delete(arr); CHtmlTagFontCont::prune_pre_delete(arr); } void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void format_exit(class CHtmlFormatter *formatter); /* add font attributes */ int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); private: /* our em-dash */ CHtmlTagEntityInserter emdash_; /* alignment in effect immediately before this division started */ HTML_Attrib_id_t old_align_; }; class CHtmlTagCITE: public CHtmlTagFontCont { public: HTML_TAG_MAP(CHtmlTagCITE, "CITE"); int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; class CHtmlTagCODE: public CHtmlTagFontCont { public: HTML_TAG_MAP(CHtmlTagCODE, "CODE"); int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; class CHtmlTagEM: public CHtmlTagFontCont { public: HTML_TAG_MAP(CHtmlTagEM, "EM"); int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; class CHtmlTagKBD: public CHtmlTagFontCont { public: HTML_TAG_MAP(CHtmlTagKBD, "KBD"); int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; class CHtmlTagSAMP: public CHtmlTagFontCont { public: HTML_TAG_MAP(CHtmlTagSAMP, "SAMP"); int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; class CHtmlTagSTRONG: public CHtmlTagFontCont { public: HTML_TAG_MAP(CHtmlTagSTRONG, "STRONG"); int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; class CHtmlTagBIG: public CHtmlTagFontCont { public: HTML_TAG_MAP(CHtmlTagBIG, "BIG"); int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; class CHtmlTagSMALL: public CHtmlTagFontCont { public: HTML_TAG_MAP(CHtmlTagSMALL, "SMALL"); int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; class CHtmlTagDFN: public CHtmlTagFontCont { public: HTML_TAG_MAP(CHtmlTagDFN, "DFN"); int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; class CHtmlTagVAR: public CHtmlTagFontCont { public: HTML_TAG_MAP(CHtmlTagVAR, "VAR"); int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; class CHtmlTagB: public CHtmlTagFontCont { public: HTML_TAG_MAP(CHtmlTagB, "B"); int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; class CHtmlTagI: public CHtmlTagFontCont { public: HTML_TAG_MAP(CHtmlTagI, "I"); int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; class CHtmlTagU: public CHtmlTagFontCont { public: HTML_TAG_MAP(CHtmlTagU, "U"); int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; class CHtmlTagSTRIKE: public CHtmlTagFontCont { public: HTML_TAG_MAP(CHtmlTagSTRIKE, "STRIKE"); int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; /* S is synonymous with STRIKE */ class CHtmlTagS: public CHtmlTagSTRIKE { public: CHtmlTagS(CHtmlParser *prs) : CHtmlTagSTRIKE(prs) { } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagS, "S"); }; class CHtmlTagTT: public CHtmlTagFontCont { public: HTML_TAG_MAP(CHtmlTagTT, "TT"); int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; /* * SUP - superscript - subclass this from SMALL, since we'll use the * same font as with SMALL, but set it a little differently */ class CHtmlTagSUP: public CHtmlTagSMALL { public: CHtmlTagSUP(class CHtmlParser *parser) : CHtmlTagSMALL(parser) { } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagSUP, "SUP"); int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; /* * SUB - subscript - subclass this from SMALL */ class CHtmlTagSUB: public CHtmlTagSMALL { public: CHtmlTagSUB(class CHtmlParser *parser) : CHtmlTagSMALL(parser) { } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagSUB, "SUB"); int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); }; /* * FONT tag */ class CHtmlTagFONT: public CHtmlTagFontCont { public: CHtmlTagFONT(class CHtmlParser *) { /* by default, leave the size alone */ relative_ = '+'; size_ = 0; /* by default, do not change the color */ use_color_ = FALSE; use_bgcolor_ = FALSE; } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagFONT, "FONT"); /* parse an attribute */ HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); /* add my font attributes to a font descriptor */ int add_attrs(class CHtmlFormatter *fmt, class CHtmlFontDesc *desc); private: /* font size */ long size_; /* * relative setting - if this is zero, the size is absolute; if * it's '+', the size is a positive relative setting; if it's '-', * the size is a negative relative setting */ textchar_t relative_; /* face name list */ CStringBuf face_; /* color - if use_color_ is false, color_ is ignored */ int use_color_ : 1; HTML_color_t color_; /* background color */ int use_bgcolor_ : 1; HTML_color_t bgcolor_; }; /* ------------------------------------------------------------------------ */ /* * Explicit font tag. */ class CHtmlTagFontExplicit: public CHtmlTagContainer { public: CHtmlTagFontExplicit(const class CHtmlFontDesc *font) { font_ = *font; } const textchar_t *get_tag_name() const { return "<!-- Explicit Font -->"; } void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void format_exit(class CHtmlFormatter *formatter); protected: /* our font */ class CHtmlFontDesc font_; /* original font in effect in enclosing scope */ class CHtmlSysFont *old_font_; }; /* ------------------------------------------------------------------------ */ /* * NOBR - non-breaking text. This encloses a run of pre-formatted text * where no automatic line breaking is allowed. */ class CHtmlTagNOBR: public CHtmlTagContainer { public: HTML_TAG_MAP(CHtmlTagNOBR, "NOBR"); void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void format_exit(class CHtmlFormatter *formatter); private: /* the enclosing break/nobreak status */ int old_wrap_; }; /* ------------------------------------------------------------------------ */ /* * WRAP - control the text-wrapping mode. This is an in-line * non-container tag that lets the document switch between word-wrapping * mode and character-wrapping mode. In word-wrapping mode, we break * lines only at defined word boundaries (at spaces, after hyphens, at * soft hyphens). In character-wrapping mode, we can break lines anywhere * except at explicit non-breaking boundaries (as marked by the zero-width * non-breaking space character, \uFEFF). */ class CHtmlTagWRAP: public CHtmlTag { public: CHtmlTagWRAP(CHtmlParser *) { /* assume word-wrap mode */ char_wrap_mode_ = FALSE; } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagWRAP, "WRAP"); /* parse an attribute */ HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); /* format this tag */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); private: /* our wrapping mode */ int char_wrap_mode_; }; /* ------------------------------------------------------------------------ */ /* * NOLOG - text hidden from log files. This encloses a run of text that we * want displayed but not copied to any transcript file. Since we handle * only regular display streams, this tag has no effect; we simply parse it * and otherwise ignore it. */ class CHtmlTagNOLOG: public CHtmlTagContainer { public: HTML_TAG_MAP(CHtmlTagNOLOG, "NOLOG"); }; /* * LOG - text hidden *except* in log files. This encloses a run of text * that we want displayed only in a transcript file, not in the UI. Since * we handle only regular display streams, this tag effectively hides its * contents; we simply parse it, but skip its contents during formatting. */ class CHtmlTagLOG: public CHtmlTagContainer { public: HTML_TAG_MAP(CHtmlTagLOG, "LOG"); /* * get the next tag in format order - since we hide our contents, this * is simply the next tag after us, not our first child */ virtual CHtmlTag *get_next_fmt_tag(class CHtmlFormatter *formatter) { return CHtmlTag::get_next_fmt_tag(formatter); } }; /* ------------------------------------------------------------------------ */ /* * TAB markup */ class CHtmlTagTAB: public CHtmlTag { public: CHtmlTagTAB(CHtmlParser *); HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagTAB, "TAB"); /* delete my text on deleting the tag */ void prune_pre_delete(class CHtmlTextArray *arr); /* parse the tag */ void on_parse(class CHtmlParser *parser); /* parse an attribute */ HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); /* set the MULTIPLE attribute to the specified value */ void set_multiple_val(int val) { indent_ = val; use_multiple_ = TRUE; } /* format the tab */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); /* name of the tab */ CStringBuf id_; /* true -> define my ID */ int define_ : 1; /* true -> tab to my ID */ int to_ : 1; /* alignment */ HTML_Attrib_id_t align_; /* alignment character (decimal point) */ textchar_t dp_; /* * indent amount, and flag specifying if it's to be used as direct * spacing (use_indent_) or indentation to a multiple of a given tab * size (use_multiple_) */ long indent_; int use_indent_ : 1; int use_multiple_ : 1; /* text offset of our space character */ unsigned long txtofs_; }; /* ------------------------------------------------------------------------ */ class CHtmlTagA: public CHtmlTagContainer, public CHtmlFontContAddin { public: CHtmlTagA(class CHtmlParser *prs) { fontcont_init(); /* presume we'll render with the normal link style */ style_ = LINK_STYLE_NORMAL; noenter_ = FALSE; append_ = FALSE; use_hover_fg_ = use_hover_bg_ = FALSE; hover_underline_ = FALSE; } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagA, "A"); /* don't allow <A> tags to be nested */ int allow_tag(CHtmlTag *tag) const { /* allow anything but a nested <A> */ return !tag->tag_name_matches("A", 1); } HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void format_exit(class CHtmlFormatter *formatter); /* add font-cont attributes */ virtual int add_attrs(class CHtmlFormatter *, class CHtmlFontDesc *); private: /* link information */ CHtmlUrl href_; CStringBuf title_; /* HOVER foreground and background colors */ HTML_color_t hover_fg_; HTML_color_t hover_bg_; /* link style (LINK_STYLE_xxx) */ unsigned int style_ : 2; /* true -> do not automatically enter the command */ int noenter_ : 1; /* * true -> append the href to any existing command line, rather than * clearing the previous command */ int append_ : 1; /* true -> use hover foreground/background colors */ int use_hover_fg_ : 1; int use_hover_bg_ : 1; /* * Hover decoration: true->underline, false->none. Note that this * specifies an *added* decoration - so if this is true, we add an * underline, but if it's false, we don't remove an underline if we'd * otherwise use one. */ int hover_underline_ : 1; }; class CHtmlTagQ: public CHtmlTagContainer { public: HTML_TAG_MAP(CHtmlTagQ, "Q"); /* delete my quote text when pruning the tree */ void prune_pre_delete(class CHtmlTextArray *arr) { openq_.prune_pre_delete(arr); closeq_.prune_pre_delete(arr); CHtmlTagContainer::prune_pre_delete(arr); } void on_parse(class CHtmlParser *parser); void on_close(class CHtmlParser *parser); void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void format_exit(class CHtmlFormatter *formatter); /* I'm a quote container */ CHtmlTagQ *get_quote_container() { return this; } /* get my quote nesting level */ int get_quote_nesting_level() { CHtmlTagQ *qcont; /* get my enclosing quote container */ qcont = (get_container() == 0 ? 0 : get_container()->get_quote_container()); /* I'm one level deeper than it is */ return (qcont == 0 ? 0 : qcont->get_quote_nesting_level() + 1); } private: CHtmlTagEntityInserter openq_; CHtmlTagEntityInserter closeq_; }; class CHtmlTagBR: public CHtmlTag { public: CHtmlTagBR(class CHtmlParser *prs); HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagBR, "BR"); void on_parse(class CHtmlParser *parser); void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); private: /* CLEAR setting - LEFT, RIGHT, or ALL, if anything is set */ HTML_Attrib_id_t clear_; /* height of the break */ long height_; }; class CHtmlTagHR: public CHtmlTag { public: CHtmlTagHR(class CHtmlParser *) { align_ = HTML_Attrib_center; shade_ = TRUE; size_ = 0; width_ = 100; width_pct_ = TRUE; image_ = 0; } ~CHtmlTagHR(); HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagHR, "HR"); void on_parse(CHtmlParser *parser); void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); private: /* alignment */ HTML_Attrib_id_t align_; /* shading on or off */ int shade_ : 1; /* vertical size in pixels */ long size_; /* width as a percentage or as a pixel size (depending on width_pct_) */ long width_; /* flag: true -> width is given as a percentage; false -> pixel size */ int width_pct_; /* image source, if an image is used */ CHtmlUrl src_; /* the image cache object */ class CHtmlResCacheObject *image_; }; /* ------------------------------------------------------------------------ */ /* * IMAGE tag */ class CHtmlTagIMG: public CHtmlTag { public: CHtmlTagIMG(class CHtmlParser *) { /* initialize all attributes to default values */ image_ = 0; h_image_ = 0; a_image_ = 0; border_ = 1; ismap_ = FALSE; align_ = HTML_Attrib_middle; width_ = height_ = 0; width_set_ = height_set_ = FALSE; hspace_ = vspace_ = 0; alt_txtofs_ = 0; } ~CHtmlTagIMG(); /* on pruning the tree, delete my ALT text */ void prune_pre_delete(class CHtmlTextArray *arr); HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagIMG, "IMG"); /* parse attributes */ HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); /* format the image */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); private: /* load one of our image resources */ void load_image(class CHtmlSysWin *win, class CHtmlFormatter *formatter, class CHtmlResCacheObject **image, const CHtmlUrl *url, const char *attr_name, int mandatory); /* the image cache object for the main image */ class CHtmlResCacheObject *image_; /* for hyperlinked images, the hover/active images */ class CHtmlResCacheObject *h_image_; class CHtmlResCacheObject *a_image_; /* image source */ CHtmlUrl src_; /* for hyperlinked images, the hover/active sources */ CHtmlUrl asrc_; CHtmlUrl hsrc_; /* alternate textual description */ CStringBuf alt_; /* text offset of alt_ text */ unsigned long alt_txtofs_; /* border size */ long border_; /* horizontal/vertical whitespace settings */ long hspace_; long vspace_; /* width and height settings */ long width_; long height_; /* flags indicating whether width and height were explicitly set */ int width_set_ : 1; int height_set_ : 1; /* map setting */ CHtmlUrl usemap_; /* ISMAP setting */ int ismap_ : 1; /* alignment */ HTML_Attrib_id_t align_; }; /* ------------------------------------------------------------------------ */ /* * SOUND tag */ class CHtmlTagSOUND: public CHtmlTag { public: CHtmlTagSOUND(class CHtmlParser *) { sound_ = 0; layer_ = HTML_Attrib_invalid; repeat_ = 1; has_repeat_ = FALSE; fadein_ = fadeout_ = 0.0; crossfade_in_ = crossfade_out_ = FALSE; vol_ = 100; random_ = 0; cancel_ = FALSE; interrupt_ = FALSE; sequence_ = HTML_Attrib_invalid; obsolete_ = FALSE; prv_sound_ = 0; gen_id_ = 0; } ~CHtmlTagSOUND(); HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagSOUND, "SOUND"); /* don't allow pruning SOUND tags except when obsolete */ virtual int can_prune_tag() const { return obsolete_; } /* parse attributes */ HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); /* format the sound */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); /* get/set the sound generation ID */ unsigned int get_gen_id() const { return gen_id_; } void set_gen_id(unsigned int gen_id) { gen_id_ = gen_id; } /* get the fade parameters */ double get_fade_in() { return fadein_; } int get_crossfade_in() { return crossfade_in_; } double get_fade_out() { return fadeout_; } int get_crossfade_out() { return crossfade_out_; } /* get the base volume parameter */ int get_volume() { return (int)vol_; } private: /* * Obsolete all previous sounds in the given layer (but not this * sound itself). This is called for all sounds prior to a CANCEL * tag in a given layer. */ void obsolete_prev_sounds(HTML_Attrib_id_t layer); /* mark this sound as obsolete */ void make_obsolete(); /* the resource cache object */ class CHtmlResCacheObject *sound_; /* source */ CHtmlUrl src_; /* layer (foreground, bgambient, ambient, background) */ HTML_Attrib_id_t layer_; /* alternate textual description */ CStringBuf alt_; /* number of times to repeat -- zero indicates looping */ long repeat_; /* flag indicating that REPEAT was specified */ int has_repeat_ : 1; /* time in milliseconds to fade in/out */ double fadein_; double fadeout_; /* base volume, 0-100 (0 is silence, 100 is unattenuated) */ long vol_; /* * probability of starting at any given second; zero indicates that * the RANDOM attribute is not present */ long random_; /* cancellation - true means that we're cancelling sounds in a layer */ int cancel_ : 1; /* * interrupt - true means we start immediately, stopping any sound * currently playing in the same layer */ int interrupt_ : 1; /* * Crossfades. These are true if we're doing a crossfade during * fade-in or fade-out, respectively. */ int crossfade_in_ : 1; int crossfade_out_ : 1; /* sequence code (replace, random, cycle) */ HTML_Attrib_id_t sequence_; /* * Obsolete flag. Once a sound has been removed from its queue by a * CANCEL, it's unnecessary to ever play that sound again, since * there's a tag that follows that will cancel the sound. So, we'll * set this flag whenever we cancel a sound from its queue, and * we'll ignore any sounds in the playback stream that have this * flag set. */ int obsolete_ : 1; /* * Previous sound tag in format list. We use this to keep a chain * of the sound tags in reverse order. Each time we encounter a * CANCEL tag, we can go back and obsolete all of the tags in the * chain prior to the CANCEL tag, since they'll never need to be * played again. */ CHtmlTagSOUND *prv_sound_; /* * Sound generation ID. Each time we play a sound, the formatter * marks it with its current generation ID. If we've played a sound * before on the current generation, it means that we're just * reformatting the current page and should leave the current sounds * playing, so we won't play the same sound again. */ unsigned int gen_id_; }; /* ------------------------------------------------------------------------ */ /* * MAP and subtags */ class CHtmlTagMAP: public CHtmlTagContainer { public: HTML_TAG_MAP(CHtmlTagMAP, "MAP"); HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void format_exit(class CHtmlFormatter *formatter); private: CStringBuf name_; }; class CHtmlTagAREA: public CHtmlTag { public: CHtmlTagAREA(CHtmlParser *) { /* no shape yet */ shape_ = HTML_Attrib_invalid; /* no coordinates yet */ coord_cnt_ = 0; /* NOHREF not yet seen */ nohref_ = FALSE; /* APPEND, NOENTER not yet seen */ append_ = FALSE; noenter_ = FALSE; } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagAREA, "AREA"); HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); private: /* shape - rect, circle, poly */ HTML_Attrib_id_t shape_; /* * Coordinate list - interpretation depends on the type of shape, * but we'll always store a flat list of coordinate values. */ CHtmlTagAREA_coords_t coords_[CHtmlTagAREA_max_coords]; /* number of coordinates in use */ size_t coord_cnt_; /* href of this map setting */ CHtmlUrl href_; /* flag indicating that NOHREF is specified */ int nohref_ : 1; /* alternate name */ CStringBuf alt_; /* * APPEND attribute setting - appends the HREF to the command rather * than clearing out the old command */ int append_ : 1; /* * NOENTER - allows the player to continue editing the command after * adding our HREF */ int noenter_ : 1; }; /* ------------------------------------------------------------------------ */ /* * BASEFONT */ class CHtmlTagBASEFONT: public CHtmlTag { public: CHtmlTagBASEFONT(class CHtmlParser *) { size_ = 3; use_color_ = FALSE; } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagBASEFONT, "BASEFONT"); HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); /* these tags have global effect, so don't prune them */ virtual int can_prune_tag() const { return FALSE; } private: /* SIZE setting */ long size_; /* face name list */ CStringBuf face_; /* color - if use_color_ is false, color_ is ignored */ int use_color_ : 1; HTML_color_t color_; }; /* ------------------------------------------------------------------------ */ /* * BANNER tag */ class CHtmlTagBANNER: public CHtmlTagContainer { public: HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagBANNER, "BANNER"); CHtmlTagBANNER(class CHtmlParser *) { /* initialize attributes with defaults */ remove_ = FALSE; removeall_ = FALSE; height_set_ = width_set_ = FALSE; height_pct_ = width_pct_ = FALSE; height_prev_ = width_prev_ = FALSE; border_ = FALSE; alignment_ = HTML_BANNERWIN_POS_TOP; obsolete_ = FALSE; /* if ID isn't specified, use an empty name as the default banner */ id_.set(""); /* nothing in the banner sublist yet */ banner_first_ = 0; } ~CHtmlTagBANNER(); /* pre-delete on pruning the tree */ void prune_pre_delete(class CHtmlTextArray *arr); /* only allow pruning BANNER tags when they're obsolete */ virtual int can_prune_tag() const { return obsolete_; } HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); void on_parse(class CHtmlParser *parser); void on_close(class CHtmlParser *parser); void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void format_exit(class CHtmlFormatter *formatter); /* * We can't format a banner until it's finished. (However, note * that a 'remove' element has no contents, so it's always ready to * format.) */ virtual int ready_to_format() const { return remove_ || is_closed(); } /* * Get the first tag in the banner contents. During formatting of * the banner, the banner's normal contents list is empty, so the * contents of the banner must instead be obtained with this special * call. */ class CHtmlTag *get_banner_contents() const { return banner_first_; } /* * Receive notification that the banner is now obsolete. The * formatter will call this routine whenever it finds that a * subsequent BANNER tag in the same format list uses the same * banner -- when this happens, the banner involved will always be * overwritten by the subsequent tag. We'll discard our contents * and skip doing any work to format the banner on future passes * through the format list. */ void notify_obsolete(class CHtmlTextArray *arr); /* * Special flag that identifies this tag as an about box rather than * a regular banner. Since about boxes and banners work almost the * same way, we share most of the code for the two classes, and use * this flag to let the formatter know which is which. */ virtual int is_about_box() const { return FALSE; } /* * Determine if the banner has a border */ int has_border() const { return border_; } /* * Get the alignment setting */ HTML_BannerWin_Pos_t get_alignment() const { return alignment_; } protected: /* banner identifier */ CStringBuf id_; /* * Height - if not set, we'll use the height of the contents. If * height_pct_ is set, it indicates that the height is a percentage * of the overall window height. */ long height_; int height_set_ : 1; int height_pct_; /* likewise with width */ long width_; int width_set_ : 1; int width_pct_; /* flag: keep height/width of previous instance of this banner */ int height_prev_ : 1; int width_prev_ : 1; /* REMOVE flag - set when REMOVE attribute is present */ int remove_ : 1; /* REMOVEALL flag - set when REMOVEALL attribute is present */ int removeall_ : 1; /* * obsolete flag - we'll set this immediately after we apply any * REMOVE tag, because doing so will remove previous banners not * only from the display but also from the formatting list, hence we * only need to apply one of these a single time */ int obsolete_ : 1; /* BORDER flag */ int border_ : 1; /* alignment setting */ HTML_BannerWin_Pos_t alignment_; /* * banner contents -- we move the normal contents list into this * special banner contents list when we're first formatted; this * prevents the banner contents from being formatted into my own * display list, but still lets us get at the sublist in the banner * subformatter */ CHtmlTag *banner_first_; }; /* ------------------------------------------------------------------------ */ /* * The ABOUTBOX tag is a simple subclass of BANNER. It works almost the * same as BANNER, but has a special flag to the formatter that lets the * formatter know this is actually an about box. */ class CHtmlTagABOUTBOX: public CHtmlTagBANNER { public: HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagABOUTBOX, "ABOUTBOX"); CHtmlTagABOUTBOX(class CHtmlParser *parser) : CHtmlTagBANNER(parser) { } /* set an attribute - we suppress certain BANNER attributes */ HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); /* this is the ABOUTBOX tag */ virtual int is_about_box() const { return TRUE; } }; /* ------------------------------------------------------------------------ */ /* * Tables */ /* * Table column cell list entry */ class CHtmlTag_cell { public: CHtmlTag_cell() { /* the cell is not yet occupied */ occupied_ = FALSE; } /* determine if the cell is occupied */ int is_occupied() const { return occupied_; } /* mark the cell as occupied */ void set_occupied() { occupied_ = TRUE; } private: /* flag indicating whether the cell has been occupied yet */ int occupied_ : 1; }; /* * Table column list entry */ class CHtmlTag_column { public: CHtmlTag_column(); ~CHtmlTag_column(); /* * Get the pointer to a cell at a given row (row numbers start at * zero). If the cell isn't yet allocated, this automatically * allocates the cell, so this routine always returns a valid cell * pointer. */ CHtmlTag_cell *get_cell(size_t rownum); /* * Add a cell's widths to my measurements. This is used during pass * 1 to determine the sizes needed for the column (and, in turn, for * the enclosing table). The minimum width is the narrowest that we * can make the column before we start clipping; in other words, * it's the width of the largest single item that can't be broken * across lines. The maximum width is the width of the longest * line; this is the narrowest that we can make the table before we * need to start wrapping lines. If either of the parameters to * this function are greater than the respective values we've * stored, we'll update the stored values with the new values. */ void add_cell_widths(long min_width, long max_width) { if (min_width > min_width_) min_width_ = min_width; if (max_width > max_width_) max_width_ = max_width; } /* * Add a cell's explicit pixel width to my measurements. We'll * accumulate the minimum explicit pixel width that each column wants, * and then apply this if it exceeds the width we'd otherwise choose * for the column. */ void add_cell_pix_width(long pix_width) { if (pix_width > pix_width_) pix_width_ = pix_width; } /* * Add a cell's percentage-based width to my measurements. We'll * accumulate the minimum percentage width that each column wants, * and then apply this if it exceeds the width we'd otherwise choose * for the column. */ void add_cell_pct_width(long pct_width) { if (pct_width > pct_width_) pct_width_ = pct_width; } /* get the minimum and maximum sizes for the column */ long get_min_width() const { return min_width_; } long get_max_width() const { return max_width_; } /* get the percentage width for the column */ long get_pct_width() const { return pct_width_; } /* get the explicitly set pixel width for the column */ long get_pix_width() const { return pix_width_; } /* * Set the final width of the column. This is called when the table * is formatted during pass 2, after all of the contents have been * measured. */ void set_width(long wid) { width_ = wid; } /* get the final width of the column */ long get_width() const { return width_; } /* * Get/set the horizontal offset of the column. This is the offset * from the left edge of the table, and is simply the offset of the * previous column plus the width of the previous column. */ long get_x_offset() const { return x_offset_; } void set_x_offset(long x_offset) { x_offset_ = x_offset; } private: /* cell list - array of cells in the column */ CHtmlTag_cell **cells_; /* number of cells used in the list */ size_t cells_used_; /* number of slots for cells allocated in list */ size_t cells_alloced_; /* * Minimum width of the column: this is the width of the largest * item that can't be broken across multiple lines. This is the * narrowest we can go before we start clipping items within the * column. */ long min_width_; /* * Maximum width needed for the column: this is the width of the * longest line in the column. This is the narrowest that we can go * before we start wrapping lines within the column. */ long max_width_; /* * Pixel width. When a cell's WIDTH attribute is given as a pixel * width, we'll track it here. We record the highest pixel width of * the cells in this column. */ long pix_width_; /* * Percentage width. When a cell's width is expressed as a * percentage of the table width, we'll track it here. We record * the highest of the percentage widths of the cells in this column. */ long pct_width_; /* * final width of the column - set during pass 2, after all contents * are known */ long width_; /* horizontal offset of the column from the left edge of the table */ long x_offset_; }; /* * Mix-in class for table background image management. */ class CHtmlTableImage { public: CHtmlTableImage() { /* no background color setting yet */ use_bgcolor_ = FALSE; /* no background image yet */ bg_image_ = 0; } virtual ~CHtmlTableImage(); /* load the background image during formatting as needed */ void format_load_bg(class CHtmlSysWin *win, class CHtmlFormatter *fmt); protected: virtual const textchar_t *get_tag_name() const = 0; /* our background color */ HTML_color_t bgcolor_; int use_bgcolor_ : 1; /* the URL and image cache object for our background image */ class CHtmlUrl background_; class CHtmlResCacheObject *bg_image_; }; /* * TABLE tag */ class CHtmlTagTABLE: public CHtmlTagContainer, public CHtmlTableImage { public: CHtmlTagTABLE(class CHtmlParser *); ~CHtmlTagTABLE(); HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagTABLE, "TABLE"); /* put paragraph breaks around the table */ void on_parse(class CHtmlParser *parser); void on_close(class CHtmlParser *parser); void post_close(class CHtmlParser *parser); /* before closing the table, close any open cell and row */ void pre_close(class CHtmlParser *parser); /* parse attributes */ HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); /* I'm a TABLE tag */ CHtmlTagTABLE *get_table_container() { return this; } /* * get the next format-order tag after the table - this is essentially * the same as get_next_fmt_tag(), but doesn't trigger any side effects */ CHtmlTag *get_post_table_tag(); /* get my display item */ class CHtmlDispTable *get_disp() const { return disp_; } /* is this a margin-floating item? */ int is_floating() const { /* it's a floating item if it's left- or right-aligned */ return (align_ == HTML_Attrib_left || align_ == HTML_Attrib_right); } /* get the alignment attribute */ HTML_Attrib_id_t get_align() const { return align_; } /* get/set my current row number */ size_t get_cur_rownum() const { return cur_rownum_; } void set_cur_rownum(size_t rownum) { cur_rownum_ = rownum; } /* format the table */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void format_exit(class CHtmlFormatter *formatter); /* get the number of columns */ size_t get_column_count() const { return cols_used_; } /* * Get the column at a particular column index. Allocates a new * column entry if this column is not yet allocated, so this routine * always returns a valid cell pointer. */ CHtmlTag_column *get_column(size_t colnum); /* * Get a cell at a particular row and column position. */ CHtmlTag_cell *get_cell(size_t rownum, size_t colnum) { return get_column(colnum)->get_cell(rownum); } /* * Calculate the table width based on the size constraints of the * columns. width_min and width_max are the minimum and maximum sizes * calculated for the contents, and win_width is the available space in * the window. */ long calc_table_width(long width_min, long width_max, long win_width); /* * calculate the width of the table decorations (borders, cell * spacing, cell padding) */ long calc_decor_width(); /* * Set column widths. * * x_offset is the x position of the left edge of the table. * table_width is the desired width of the table, which includes all * decorations (borders, cell spacing, cell padding). */ void set_column_widths(long x_offset, long table_width); /* * Set row positions. This distributes the vertical space in the * table according to the row sizes. Each row's size depends on the * sizes of its member cells; each row's position depends on the * sizes of the rows above it. When the table is finished after * pass 2, everything about the cells is determined except the * vertical sizes of the rows; this routine finishes the job by * determining row positions and setting everything's final vertical * location. */ void set_row_positions(class CHtmlFormatter *formatter); /* * Compute the final height of the table. We'll go through our rows * and compute their final heights. */ void compute_table_height(); /* get the cell padding and spacing settings */ long get_cellpadding() const { return cellpadding_; } long get_cellspacing() const { return cellspacing_; } /* get the border size */ long get_border() const { return border_; } /* * Set the CAPTION tag. A table may have one CAPTION tag, or no * caption at all. The CAPTION tag tells us about itself when the * parser finishes with it. */ void set_caption_tag(class CHtmlTagCAPTION *caption_tag) { caption_tag_ = caption_tag; } /* get my enclosing table */ CHtmlTagTABLE *get_enclosing_table() const { return enclosing_table_; } private: /* my display item */ class CHtmlDispTable *disp_; /* alignment */ HTML_Attrib_id_t align_; /* * width; if width_pct_ is true, width_ is a percentage of the space * between the margins, otherwise it's a pixel size */ long width_; int width_pct_; /* * flag indicating if width has been set at all; if not, it defaults * to the minimum size needed to show the table with all cells at * maximum, but no more than the greater of 100% of the available * space between the margins and the minimum table width */ int width_set_ : 1; /* height; works like width */ long height_; int height_pct_; /* flag indicating whether height has been specified */ int height_set_ : 1; /* * calculated minimum height - we calculate this based on the height * parameter value when we start formatting the table, and then use * it when we know all of the row heights to set the minimum table * height */ long calc_height_; /* border size in pixels */ long border_; /* cell spacing and padding, in pixels */ long cellspacing_; long cellpadding_; /* the enclosing table display item and position */ class CHtmlTagTABLE *enclosing_table_; CHtmlPoint enclosing_table_pos_; /* my CAPTION tag, if I have one */ class CHtmlTagCAPTION *caption_tag_; /* * Current row number. This starts at zero; each time we finish a * row, we'll increase this. */ size_t cur_rownum_; /* * Column list. The table keeps an array of column entries; each * column entry keeps track of the size metrics for the column, and * keeps a list of the cells (by row) that are actually occupied in * that column. */ CHtmlTag_column **columns_; /* number of columns used in column list */ size_t cols_used_; /* number of column slots allocated in column list */ size_t cols_alloced_; /* * Minimum and maximum widths. These aren't known until after we * complete the first pass. The minimum width is the size of the * largest single item that can't be broken across lines; the * maximum width is the width of the longest single line. */ long min_width_; long max_width_; }; class CHtmlTagCAPTION: public CHtmlTagContainer { public: CHtmlTagCAPTION(class CHtmlParser *) { /* no alignment specified yet */ valign_ = HTML_Attrib_invalid; align_ = HTML_Attrib_invalid; } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagCAPTION, "CAPTION"); /* * Get the next tag in format order. We don't format CAPTION tags * at the point they're encountered in the tag list; instead, the * table formats its caption at the appropriate point in laying out * the table. Hence, when asked for the next tag after the CAPTION * in format order, we pretend we have no contents. */ virtual CHtmlTag *get_next_fmt_tag(class CHtmlFormatter *formatter) { /* return the next tag after us, as though we had no contents */ return CHtmlTag::get_next_fmt_tag(formatter); } /* parse the tag */ void on_parse(CHtmlParser *parser); /* close parsing of the tag */ void on_close(class CHtmlParser *parser); /* after-close processing */ void post_close(class CHtmlParser *parser); /* * Format the caption's contents. Normal formatting will skip the * caption's contents; at the appropriate time, the formatter will * explicitly call this routine to format the caption. */ void format_caption(class CHtmlSysWin *win, class CHtmlFormatter *formatter); /* parse attributes */ HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); /* * Determine if the caption is at the top of the table. Returns * true if the caption is at the top, false if the caption is at the * bottom. */ int is_at_top_of_table() const { /* it's at the top if it hasn't been set explicitly to the bottom */ return (valign_ != HTML_Attrib_bottom); } /* * Receive notification that we've finished parsing the table. If * we're aligned at the bottom of the table, we'll need to re-insert * our text into the text array here, since the display items for * our contents will be in the display list after the table. */ void on_close_table(class CHtmlParser *parser); private: /* horizontal and vertical alignment */ HTML_Attrib_id_t align_; HTML_Attrib_id_t valign_; }; class CHtmlTagTR: public CHtmlTagContainer, public CHtmlTableImage { public: CHtmlTagTR(class CHtmlParser *) : CHtmlTagContainer(), CHtmlTableImage() { /* no horizontal alignment set */ align_ = HTML_Attrib_invalid; /* default to middle vertical alignment */ valign_ = HTML_Attrib_middle; /* no cells yet, so the maximum height is zero */ max_cell_height_ = 0; /* no height set yet */ height_set_ = FALSE; height_star_ = FALSE; } HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagTR, "TR"); /* * on opening and closing parsing of tag, implicitly close any open * cell within this row */ void on_parse(class CHtmlParser *parser); void pre_close(class CHtmlParser *parser); /* parse attributes */ HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); /* I'm a table row (TR) tag */ CHtmlTagTR *get_as_table_row() { return this; } CHtmlTagTR *get_table_row_container() { return this; } /* * Get my row number. The row number is fixed for all cells in the * row; this is determined by the table when we start this row. */ size_t get_rownum() const { return rownum_; } /* * Get/set the current column number. We start this at zero when we * start the row; each cell sets the column number to the next * available column when it determines where it goes. */ size_t get_cur_colnum() const { return cur_colnum_; } void set_cur_colnum(size_t colnum) { cur_colnum_ = colnum; } /* format the row */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void format_exit(class CHtmlFormatter *formatter); /* get the maximum height of any of my cells */ long get_max_cell_height() const { /* * if we have an explicit minimum height, and it's larger than * the maximum cell height, use that; otherwise use the * calculated maximum cell height */ if (height_set_ && height_ > max_cell_height_) return height_; else return max_cell_height_; } /* * Compute the maximum cell height. Adds the height contribution * from each of the cells in this row to all rows the cell spans. */ void compute_max_cell_height(size_t rownum, size_t rowcnt, int pass); /* * Include the height of a cell in the computation of the row's * maximum cell height. */ void include_cell_height(long height) { /* if this is the largest so far, remember it */ if (height > max_cell_height_) max_cell_height_ = height; } /* * Set the y offset of items in the row. We'll use this after we've * completed pass 2 to set the final vertical positions of the * contents of the row. */ void set_row_y_pos(class CHtmlFormatter *formatter, class CHtmlTagTABLE *table, long y_offset, long height); /* get my alignment setting */ HTML_Attrib_id_t get_align() const { return align_; } /* get my background color setting (and whether it's valid) */ HTML_color_t get_bgcolor() const { return bgcolor_; } int get_use_bgcolor() const { return use_bgcolor_; } /* * determine if I my height attribute is '*', which means that I * should soak up any available extra space in the vertical height * of the enclosing table */ int is_height_star() const { return height_star_; } private: /* horizontal and vertical alignment settings */ HTML_Attrib_id_t align_; HTML_Attrib_id_t valign_; /* height attribute */ long height_; unsigned int height_set_ : 1; unsigned int height_star_ : 1; /* my row number in the table */ size_t rownum_; /* current column number */ size_t cur_colnum_; /* maximum cell height found so far */ long max_cell_height_; }; /* * base class for table cells (TD and TH) */ class CHtmlTagTableCell: public CHtmlTagContainer, public CHtmlTableImage { public: CHtmlTagTableCell(class CHtmlParser *parser); /* * get this item as a table cell - I'm definitely a table cell, so * return a pointer to myself */ class CHtmlTagTableCell *get_table_cell_container() { return this; } class CHtmlTagTableCell *get_as_table_cell() { return this; } /* * Set the cell's metrics. The formatter calls this after it's * computed the minimum and maximum sizes of our contents during * pass 1 through our table. */ void set_table_cell_metrics(class CHtmlTableMetrics *metrics); /* compute the column metrics from the previously stored cell metrics */ void compute_column_metrics(int pass); /* * Set the cell's height. The formatter calls this after it's * computed the cell's formatted vertical size during pass 2. If * the content height is less than the height attribute, we'll use * the height attribute. base_y_pos is the tentative y position of * the top of the cell's contents; we'll save this position so that, * when we later set the final position of each item in the cell's * contents, we'll be able to set its offset from the final cell * base position to be the same as its original offset from the * tentative base position. */ void set_table_cell_height(long height, long base_y_pos); /* get my formatted height */ long get_table_cell_height() const { return content_height_; } /* get my ROWSPAN setting */ long get_rowspan() const { return rowspan_; } /* * Set the cell's vertical position. This is used at the end of the * second pass, after we know the vertical size and position of the * row containing this cell, to set the final positions of the items * in the cell. 'y_offset' is the offset from the top of the table * to the top of this cell's row. */ void set_cell_y_pos(class CHtmlFormatter *formatter, long y_offset, long height, HTML_Attrib_id_t row_valign); /* parse attributes */ HTML_attrerr set_attribute(class CHtmlParser *parser, HTML_Attrib_id_t attr_id, const textchar_t *val, size_t vallen); /* on starting a cell, close any previous cell that's still open */ void on_parse(class CHtmlParser *parser); /* format the cell */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); void format_exit(class CHtmlFormatter *formatter); protected: /* * Get my default horizontal alignment. This setting is used if * neither the row nor the cell has an explicit ALIGN setting. The * value differs for each type of cell, thus this method is pure * virtual. */ virtual HTML_Attrib_id_t get_default_cell_alignment() const = 0; private: /* my row and column numbers */ size_t rownum_; size_t colnum_; /* flag indicating that we've figured out my row and column position */ int row_col_set_ : 1; /* true -> don't wrap long lines */ int nowrap_ : 1; /* number of rows and columns spanned by the cell */ long rowspan_; long colspan_; /* horizontal and vertial alignment settings */ HTML_Attrib_id_t align_; HTML_Attrib_id_t valign_; /* alignment in effect immediately before this cell started */ HTML_Attrib_id_t old_align_; /* width and height attributes in pixels */ long width_; long height_; /* flag indicating that the width is a percentage of the table width */ int width_pct_; /* * Content height in pixels; this is the actual height of the * contents after formatting the cell on the second pass. */ long content_height_; /* * Tentative vertical position of the top of the cell's contents. * This is the position that the formatter puts the contents at * before the final positions are known; during the final * positioning pass, we'll use this as the base, so that we know how * far to put each item from the final vertical position of the * cell. */ long disp_y_base_; /* flags indicating whether width and height have been specified */ int width_set_ : 1; int height_set_ : 1; /* * My display item. This is only meaningful inside a single table * formatting pass; we need to keep track of it so that we can * calculate the cell's metrics when we finish formatting its * contents in pass 1, and so that we can set the vertical positions * of the cell's contents in pass 2. */ class CHtmlDispTableCell *disp_; /* * Last display item in the cell. This is used as a marker so that * we know the range of the display items contained within the cell. */ class CHtmlDisp *disp_last_; /* min/max width calculated from contents */ long cont_min_width_; long cont_max_width_; }; class CHtmlTagTH: public CHtmlTagTableCell { public: HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagTH, "TH"); CHtmlTagTH(class CHtmlParser *parser) : CHtmlTagTableCell(parser) { } protected: /* header cells are centered by default */ HTML_Attrib_id_t get_default_cell_alignment() const { return HTML_Attrib_center; } private: }; class CHtmlTagTD: public CHtmlTagTableCell { public: HTML_TAG_MAP_NOCONSTRUCTOR(CHtmlTagTD, "TD"); CHtmlTagTD(class CHtmlParser *parser) : CHtmlTagTableCell(parser) { } protected: /* data cells are aligned left by default */ HTML_Attrib_id_t get_default_cell_alignment() const { return HTML_Attrib_left; } private: }; /* ------------------------------------------------------------------------ */ /* * Special internal processing directive tag for T3 callers. This tells us * to adjust our parsing rules for T3 programs. When we see this tag, we * disable the <BANNER> tag, because that tag isn't allowed in T3. */ class CHtmlTagQT3: public CHtmlTag { public: HTML_TAG_MAP(CHtmlTagQT3, "?T3"); /* activate our special handling on formatting */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); }; /* * Special internal processing directive tag for TADS 2 callers. This * tells us to adjust our parsing rules for TADS 2 programs. */ class CHtmlTagQT2: public CHtmlTag { public: HTML_TAG_MAP(CHtmlTagQT2, "?T2"); /* activate our special handling on formatting */ void format(class CHtmlSysWin *win, class CHtmlFormatter *formatter); }; #endif /* HTMLTAGS_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmltxar.cpp�������������������������������������������������������������������0000664�0000000�0000000�00000056134�12650170723�0017040�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/html/htmltxar.cpp,v 1.2 1999/05/17 02:52:22 MJRoberts Exp $"; #endif /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmltxar.cpp - text array Function Notes Modified 09/23/97 MJRoberts - Creation */ #include <stdlib.h> #include <assert.h> #ifndef HTMLTXAR_H #include "htmltxar.h" #endif CHtmlTextArray::CHtmlTextArray() { /* allocate the initial page pointer array */ page_entries_ = 128; pages_ = (CHtmlTextArrayEntry *)th_malloc(page_entries_ * sizeof(CHtmlTextArrayEntry)); assert(pages_ != 0); /* allocate the first page */ alloc_first_page(); /* nothing consumed yet */ max_addr_ = 0; mem_in_use_ = 0; } CHtmlTextArray::~CHtmlTextArray() { size_t i; CHtmlTextArrayEntry *p; /* delete each page we've allocated */ for (i = 0, p = pages_ ; i < pages_alloced_ ; ++i, ++p) { /* free this page if it was allocated */ if (p->text_ != 0) th_free(p->text_); } /* delete the page array itself */ th_free(pages_); } /* * clear the text array - deletes all pages, but doesn't delete the page * list itself */ void CHtmlTextArray::clear() { size_t i; CHtmlTextArrayEntry *p; /* delete each page we've allocated */ for (i = 0, p = pages_ ; i < pages_alloced_ ; ++i, ++p) { /* free this page if it was allocated */ if (p->text_ != 0) { /* free the page */ th_free(p->text_); /* forget about it */ p->text_ = 0; } } /* reset everything */ pages_alloced_ = 0; pages_in_use_ = 0; max_addr_ = 0; mem_in_use_ = 0; /* allocate the first page */ alloc_first_page(); } /* * Remove a reference to a block of text previously allocated. If this * leaves the page containing the text unreferenced, we'll delete the * entire page. */ void CHtmlTextArray::delete_text(unsigned long addr, size_t len) { size_t pg; /* get the containing page */ pg = get_page(addr); /* remove a reference to the containing page */ --(pages_[pg].refs_); /* subtract the size from the space-in-use marker on the page */ pages_[pg].space_in_use_ -= len; /* deduct this space from the total memory in use */ mem_in_use_ -= len; /* * If that leaves the page unreferenced, delete it. Never delete * the active (i.e., last) page, even if it's otherwise * unreferenced, since we could create a new reference in it at any * time. */ if (pages_[pg].refs_ == 0 && pg != pages_alloced_ - 1) { /* deduct anything remaining on this page from the memory in use */ mem_in_use_ -= pages_[pg].space_in_use_; /* delete the page's memory */ th_free(pages_[pg].text_); /* forget the pointer, since it's gone now */ pages_[pg].text_ = 0; /* that's one less page in use */ --pages_in_use_; } } /* * allocate the first page */ void CHtmlTextArray::alloc_first_page() { pages_[0].text_ = (textchar_t *)th_malloc(HTML_TEXTARRAY_PAGESIZE); pages_[0].used_ = 0; pages_[0].refs_ = 0; pages_[0].space_in_use_ = 0; pages_[0].alloced_ = 0; pages_alloced_ = 1; pages_in_use_ = 1; assert(pages_[0].text_ != 0); } /* * Add the text to the array, counting the reference */ unsigned long CHtmlTextArray::append_text(const textchar_t *txt, size_t len) { unsigned long addr; size_t pg; /* add the text normally */ addr = store_text(txt, len); /* count the reference to the page containing the text */ pg = get_page(addr); ++(pages_[pg].refs_); /* return the address */ return addr; } /* * Temporarily store text without committing the space */ unsigned long CHtmlTextArray::store_text_temp(const textchar_t *txt, size_t len) { unsigned long addr; /* * Make sure the text will fit in the page in a single chunk by * reserving space for it */ addr = reserve_space(len); /* * Note the maximum address assigned, regardless of whether the * space is committed yet or not */ max_addr_ = addr + len; /* note the amount reserved on the page */ pages_[last_page()].alloced_ += len; /* copy the text into the page at the current offset */ if (len != 0) memcpy(pages_[last_page()].text_ + last_page_ofs(), txt, len); /* return the address */ return addr; } /* * Store text, committing the space */ unsigned long CHtmlTextArray::store_text(const textchar_t *txt, size_t len) { unsigned long addr; size_t pg; /* store the text */ addr = store_text_temp(txt, len); /* count the space in use on the page */ pg = get_page(addr); pages_[pg].space_in_use_ += len; /* count this space in the total memory we're using */ mem_in_use_ += len; /* * commit the storage by moving the page's free space pointer past * the text */ inc_last_page_ofs(len); /* return the address */ return addr; } /* * Reserve space for a chunk of text, ensuring that the chunk will be * stored contiguously on a single page */ unsigned long CHtmlTextArray::reserve_space(size_t len) { /* it must fit on a page */ assert(len <= HTML_TEXTARRAY_PAGESIZE); /* * We're required to keep each text block contiguous, so make sure * we have room for this entire block in the current page; if not, * we need to move on to the next page. */ if (len > HTML_TEXTARRAY_PAGESIZE - last_page_ofs()) { /* make sure we have room in the top-level page array */ if (pages_alloced_ == page_entries_) { /* allocate more entries */ page_entries_ += 128; pages_ = (CHtmlTextArrayEntry *)th_realloc(pages_, page_entries_ * sizeof(CHtmlTextArrayEntry)); assert(pages_ != 0); } /* allocate a new page */ pages_[pages_alloced_].text_ = (textchar_t *) th_malloc(HTML_TEXTARRAY_PAGESIZE); pages_[pages_alloced_].used_ = 0; pages_[pages_alloced_].refs_ = 0; pages_[pages_alloced_].alloced_ = 0; pages_[pages_alloced_].space_in_use_ = 0; ++pages_alloced_; ++pages_in_use_; } /* * Calculate the address. Treating the pages as though they * constituted a single linear array by pretending each array were * allocated directly after the previous one in memory, we'd simply * use the page location times the page size plus the page offset. * Since the pages are of a fixed size, we can later decode that * formulation to find the actual location in memory. */ return make_addr(last_page(), last_page_ofs()); } /* * Increment an offset so that it points to another valid offset */ unsigned long CHtmlTextArray::inc_ofs(unsigned long addr) const { /* get the page and page offset of this address */ size_t pg = get_page(addr); size_t ofs = get_page_ofs(addr); /* * if the address is at the end of its page, we need to move to the * next page, otherwise just go to the next offset within this page */ if (ofs >= pages_[pg].alloced_) { /* if we're at the end of the array, we can't increment */ if (pg + 1 >= pages_alloced_) return addr; /* return the first address in the next page */ return make_addr(pg + 1, 0); } else return addr + 1; } /* * Decrement an offset so that it points to another valid offset */ unsigned long CHtmlTextArray::dec_ofs(unsigned long addr) const { /* get the page and page offset of this address */ size_t pg = get_page(addr); size_t ofs = get_page_ofs(addr); /* * if we're at the start of this page, we need to move to the * previous page, otherwise just move down a character on this page */ if (ofs == 0) { /* if we're at the very beginning, return the same value */ if (addr == 0) return addr; /* return the last offset in the previous page */ return make_addr(pg - 1, pages_[pg - 1].used_ - 1); } else return addr - 1; } /* * Increment an offset by a whole character */ unsigned long CHtmlTextArray::inc_ofs_char( oshtml_charset_id_t cs, unsigned long addr) const { /* get the page and page offset of this address */ size_t pg = get_page(addr); size_t ofs = get_page_ofs(addr); /* * If we're at the end of the page, move to the start of the next page. * The start of a page is always the start of a character, since * characters can't be split across chunks (and chunks can't be split * across pages), hence we're automatically at the start of a character * if we move to the start of the next page. */ if (ofs >= pages_[pg].alloced_) { /* if we're at the end of the array, we can't increment */ if (pg + 1 >= pages_alloced_) return addr; /* return the first address in the next page */ return make_addr(pg + 1, 0); } /* get a pointer to the given character position */ size_t rem = pages_[pg].used_ - ofs; const textchar_t *p = pages_[pg].text_ + ofs; /* increment by the offset to the next whole character */ return addr + (os_next_char(cs, p, rem) - p); } /* * Decrement an offset by a whole character */ unsigned long CHtmlTextArray::dec_ofs_char( oshtml_charset_id_t cs, unsigned long addr) const { /* get the page and page offset of this address */ size_t pg = get_page(addr); size_t ofs = get_page_ofs(addr); /* * if we're at the start of this page, we need to move to the previous * page, otherwise just move down a character on this page */ if (ofs == 0) { /* if we're at the very beginning, return the same value */ if (addr == 0) return addr; /* move just past the end of the previous page */ pg -= 1; ofs = pages_[pg].used_; addr = make_addr(pg, ofs); } /* get a pointer to the starting character position */ size_t rem = pages_[pg].used_ - ofs; const textchar_t *p = pages_[pg].text_ + ofs; /* decrement by the offset to the start of the previous whole character */ return addr - (p - os_prev_char(cs, p, pages_[pg].text_)); } /* is the character at the given offset a word character? */ int CHtmlTextArray::is_word_char( oshtml_charset_id_t cs, unsigned long addr) const { /* get the page and page offset of this address */ size_t pg = get_page(addr); size_t ofs = get_page_ofs(addr); const textchar_t *p = pages_[pg].text_ + ofs; size_t rem = pages_[pg].used_ - ofs; return os_is_word_char(cs, p, rem); } /* * Determine how many characters are between two text offsets. Since * text offsets are not assigned continuously, it is possible that the * difference of the two offsets overstates the number of characters * between the two offset. */ unsigned long CHtmlTextArray::get_char_count(unsigned long startaddr, unsigned long endaddr) const { size_t startpg, endpg, curpg; size_t startofs, endofs, curofs; unsigned long siz; /* get the page and offset of the start and end addresses */ startpg = get_page(startaddr); startofs = get_page_ofs(startaddr); endpg = get_page(endaddr); endofs = get_page_ofs(endaddr); /* scan pages for the size */ for (siz = 0, curpg = startpg, curofs = startofs ;; ) { /* * if we're on the last page, get the distance from the ending * offset to the current offset, and we're done */ if (curpg >= endpg || curpg >= pages_alloced_) { siz += endofs - curofs; break; } /* add in the amount remaining on the current page */ siz += pages_[curpg].alloced_; /* move on to the beginning of the next page */ ++curpg; curofs = 0; } /* return the size we calculated */ return siz; } /* * Get a pointer to a chunk of characters starting at the given offset. * Returns a pointer to the characters, and sets *len to the number of * characters (up to a maximum of maxlen) in the chunk. This allows a * caller to traverse the possibly discontinuous array of characters. */ const textchar_t *CHtmlTextArray::get_text_chunk(unsigned long *addr, size_t *len_in_chunk, unsigned long maxlen) const { size_t pg; size_t ofs; /* get the page and offset of the given address */ pg = get_page(*addr); ofs = get_page_ofs(*addr); /* if we're past the end of this page, move on to the next */ if (ofs > pages_[pg].alloced_) { /* move to the start of the next page */ ++pg; ofs = 0; } /* for the size, use everything on the current page, up to the limit */ *len_in_chunk = pages_[pg].alloced_ - ofs; if (*len_in_chunk > maxlen) { /* limit the size */ *len_in_chunk = maxlen; /* * we'll still have text left in this page after returning this * chunk -- the next address will still be in this page */ *addr = make_addr(pg, ofs + maxlen); } else { /* * we exhausted this page with this chunk -- the next address * will be at the start of the next page */ *addr = make_addr(pg + 1, 0); } /* return the pointer to the page at the appropriate offset */ return pages_[pg].text_ + ofs; } /* * Search for text */ int CHtmlTextArray::search(const textchar_t *txt, size_t txtlen, int exact_case, int whole_word, int wrap, int dir, unsigned long startofs, unsigned long *match_start, unsigned long *match_end) { const textchar_t *cur_p; const textchar_t *cur_start; size_t cur_rem; size_t cur_pg; size_t cur_ofs; size_t start_pg; size_t start_ofs; /* * Get the system default character set. For character classification * purposes, we'll assume that everything is in this character set. * This isn't always true, since entity markups (e.g., ä) can be * in non-default character sets. But the information on the character * set is in the next layer up (the CHtmlDisp list), which we don't * have access to, so this potentially faulty assumption is the best we * can do. The potential harm is that we interpret an entity markup * character's byte encoding in terms of the system default character * set rather than its actual character set, which in turn could cause * us to misclassify the character as a word or non-word character when * in fact it's the opposite. If we make such an error for a character * at a word boundary, we could miss a word boundary (or, contrariwise, * think there's a word boundary someplace where there isn't one), * which could cause a "whole word" search to miss a match or find a * false match. Note that this won't affect characters that are part * of the search string itself, since these can really only be * expressed in terms of the system default character set, as there's * no interface mechanism for mixing character sets in this kind of * input. The potential for error is when such an entity is adjacent * to a match to a search string. */ oshtml_charset_id_t cs = os_get_default_charset(); /* get the page and offset of the starting address */ start_pg = cur_pg = get_page(startofs); start_ofs = cur_ofs = get_page_ofs(startofs); cur_rem = pages_[cur_pg].used_ - cur_ofs; cur_p = cur_start = pages_[cur_pg].text_ + cur_ofs; /* * if this page has been deleted, set the remaining amount on the page * to zero so that we know we have no text to check here */ if (pages_[cur_pg].text_ == 0) cur_rem = cur_ofs = 0; /* keep going until we run out of text */ for (;;) { size_t src_rem; const textchar_t *src_p; const textchar_t *arr_p; size_t arr_rem; size_t arr_pg; size_t arr_ofs; /* if we're going backwards, decrement first */ if (dir == -1) { /* * Move back to the previous character on this page. If * we're already at the start of the page, move to the * previous page. */ if (cur_ofs == 0) { /* go back to the last character of the previous page */ if (cur_pg == 0) { if (wrap) { /* go back to the end of the buffer */ cur_pg = pages_alloced_; } else { /* * we've reached the start of the buffer without * finding the target, and wrapping is not * allowed, so the search has failed */ return FALSE; } } /* set up at the end of the previous page */ --cur_pg; cur_p = pages_[cur_pg].text_ + pages_[cur_pg].used_; cur_rem = 0; cur_ofs = pages_[cur_pg].used_; /* if the page has been deleted, there's no text to check */ if (pages_[cur_pg].text_ == 0) cur_rem = cur_ofs = 0; } else { /* back up a character on the current page */ --cur_ofs; --cur_p; ++cur_rem; } /* check to see if we're back where we started */ if (cur_pg == start_pg && cur_ofs == start_ofs) return FALSE; } /* set up with the current array page */ arr_p = cur_p; arr_rem = cur_rem; arr_pg = cur_pg; arr_ofs = cur_ofs; /* * We may need to do the comparison in chunks, because the * current page may only hold a portion of the length of the * target string. Keep scanning on subsequent pages as * necessary. */ for (src_rem = txtlen, src_p = txt ;; ) { size_t cur_comp_len; /* determine how much we can compare on the current page */ cur_comp_len = src_rem; if (cur_comp_len > arr_rem) cur_comp_len = arr_rem; /* see if this one matches - if not, stop looking */ if ((exact_case ? memcmp(src_p, arr_p, cur_comp_len) : memicmp(src_p, arr_p, cur_comp_len)) != 0) break; /* advance our remainder pointers */ src_rem -= cur_comp_len; src_p += cur_comp_len; /* if we're out of source, we have a match */ if (src_rem == 0) { unsigned long a0 = make_addr(cur_pg, cur_ofs); unsigned long a1 = make_addr(arr_pg, arr_ofs + cur_comp_len); /* * if this is a whole-word match, make sure we start and * end on word boundaries */ if (whole_word && ((a0 != 0 && is_word_char(cs, dec_ofs_char(cs, a0))) || (a1 < get_max_addr() && is_word_char(cs, a1)))) { /* * we have a word character on one side or the other, * so it's not a match */ break; } /* return the match */ *match_start = a0; *match_end = a1; return TRUE; } /* * Advance to the next page. If there isn't another page, * we can stop searching, because we don't have enough text * left to contain the target. */ ++arr_pg; if (arr_pg >= pages_alloced_) break; /* set up at the start of the next page */ arr_p = pages_[arr_pg].text_; arr_rem = pages_[arr_pg].used_; arr_ofs = 0; /* if the page has been deleted, there's no text to check */ if (pages_[cur_pg].text_ == 0) arr_rem = 0; } /* if we're going forwards, increment after an unsuccessful match */ if (dir == 1) { /* move ahead to the next character on this page, if possible */ if (cur_rem != 0) { ++cur_ofs; ++cur_p; --cur_rem; /* check to see if we're back where we started */ if (cur_pg == start_pg && cur_ofs == start_ofs) return FALSE; } /* * If we've run out of text on the current page, move on to * the next page. */ if (cur_rem == 0) { /* move to the start of the next page */ ++cur_pg; /* if there isn't another page, we can't go any further */ if (cur_pg >= pages_alloced_) { /* * if they want us to wrap, go back to the start of * the buffer */ if (wrap) { /* go back to the start of the buffer */ cur_pg = 0; } else { /* * we've reached the end of the buffer without * finding the target, and wrapping is not * allowed, so the search has failed */ return FALSE; } } /* set up at the start of this page */ cur_p = pages_[cur_pg].text_; cur_rem = pages_[cur_pg].used_; cur_ofs = 0; /* if the page has been deleted, there's no text to check */ if (pages_[cur_pg].text_ == 0) cur_rem = cur_ofs = 0; /* check to see if we're back where we started */ if (cur_pg == start_pg && cur_ofs == start_ofs) return FALSE; } } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmltxar.h���������������������������������������������������������������������0000664�0000000�0000000�00000027306�12650170723�0016504�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/html/htmltxar.h,v 1.2 1999/05/17 02:52:22 MJRoberts Exp $ */ /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmltxar.h - text array class Function This class provides a mechanism for storing a long run of text in a linear address space. Since we'd like to be able to tell whether a particular piece of text comes before or after another piece of text in the text stream, it's useful for each piece of text to have an address in a linear address space. Since the text stream for a document can become quite large, we don't actually store the text in a single large chunk of memory. Instead, we break the text up into pages. To allow for linear addresses within these pages, we give each piece of text a "virtual" address, which we can then map into a pointer to the actual memory containing the text. To simplify code that accesses the text, we ensure that each chunk of text added to the array is stored contiguously in memory. So, once the caller has obtained a pointer to the memory containing a chunk of text, the caller can treat the pointer as a simple C++ character array pointer. Note that the addresses that we return are not necessarily contiguous; that is, if you store a block of 10 characters, and that block is assigned address 25, the next block of characters will not necessarily be stored at location 35. Therefore, only the addresses actually returned by append_text can be used to retrieve text. Therefore, "address arithmetic" is not possible with the values returned by append_text() outside of a single chunk. However, because ordering is guaranteed, comparisons are legal and reliably determine the relative storage order of two chunks. Notes Modified 09/23/97 MJRoberts - Creation */ #ifndef HTMLTXAR_H #define HTMLTXAR_H #ifndef TADSHTML_H #include "tadshtml.h" #endif const size_t HTML_TEXTARRAY_PAGESIZE = 32*1024; /* page entry */ class CHtmlTextArrayEntry { public: textchar_t *text_; /* text of the page */ size_t used_; /* amount of space used on this page */ size_t alloced_; /* amount of space reserved on the page */ size_t refs_; /* number of references to this page */ size_t space_in_use_; /* space actually in use (counting deletions) */ }; class CHtmlTextArray { public: CHtmlTextArray(); ~CHtmlTextArray(); /* clear the text array -- deletes all of the text in the array */ void clear(); /* * Determine how much memory, in bytes, the text in the array is * consuming. This measures the memory allocated to the text, hence * the granularity is the size of a page. */ unsigned long get_mem_in_use() const { return mem_in_use_; } /* * Add text to the array. We return the linear address of the text * in the buffer, which is guaranteed to be higher than that of any * previously appended text. We also guarantee that the text will * be stored in a contiguous block of memory, so that subsequent * uses of the text can treat it as a simple character array. If a * text item of length zero is appended, we won't actually store * anything, but we will return an address that is higher than that * of any text in the array. */ unsigned long append_text(const textchar_t *txt, size_t len); /* * Add text to the array without creating a reference to the text. * This should be used whenever the caller doesn't need to keep * track of the text, such as when the text is added purely to * signal a word or line break. */ void append_text_noref(const textchar_t *txt, size_t len) { store_text(txt, len); } /* * Temporarily store text in the array without actually consuming * space. Returns the address used to store the text, which may * change from the address previouly used for the same temporary * storage. */ unsigned long store_text_temp(const textchar_t *txt, size_t len); /* * Store text and commit the space. */ unsigned long store_text(const textchar_t *txt, size_t len); /* * Delete a reference to a block of text previously allocated. */ void delete_text(unsigned long addr, size_t len); /* * Reserve space for a chunk of text, ensuring that the chunk will * be stored contiguously on a single page. (It's not necessary to * call this prior to append_text, since that will ensure that the * text stored is in a single chunk. However, it is necessary to * use this if you want to call append_text several times and have * the whole group of text end up in a single page -- for this case, * call reserve_space with the sum of the sizes of the pieces of * text, then make the append_text calls.) Returns the text offset * for the start of the reserved chunk. */ unsigned long reserve_space(size_t len); /* * Get the address of a chunk of text previously allocated. Only * values returned by append_text() can be used reliably with this * call; no "pointer arithmetic" is possible on the values returned * by append_text() outside of a single chunk, other than comparison * of addresses from any chunks to determine storage order. */ textchar_t *get_text(unsigned long linear_address) const { /* make sure it's within range */ if (linear_address > max_addr_) linear_address = max_addr_; /* get the text at the given offset */ return (pages_[get_page(linear_address)].text_ + get_page_ofs(linear_address)); } /* get the highest address currently in use */ unsigned long get_max_addr() const { return max_addr_; } /* get the character at a given offset */ textchar_t get_char(unsigned long addr) const { return *get_text(addr); } /* is the character at the given offset a word character? */ int is_word_char(oshtml_charset_id_t cs, unsigned long addr) const; /* * Increment/decrement an offset by one byte, such that it points to * the next/previous valid byte offset. Note that these ignore any * underlying multi-byte structure in the character set - they just * move the offset a byte at a time, which might leave the offset in * the middle of a multi-byte character. */ unsigned long inc_ofs(unsigned long ofs) const; unsigned long dec_ofs(unsigned long ofs) const; /* * Increment/decrement an offset by one character, such that it points * to the next/previous valid offset that's also the lead byte of a * character. If the local system character set uses a multi-byte * encoding (e.g., UTF-8), this ensures that the new offset points to * the lead byte of a character, so it moves the offset by more than * one byte if necessary to skip one whole character. */ unsigned long inc_ofs_char( oshtml_charset_id_t cs, unsigned long ofs) const; unsigned long dec_ofs_char( oshtml_charset_id_t cs, unsigned long ofs) const; /* * Determine how many characters are between two text offsets. Since * text offsets are not assigned continuously, it is possible that the * difference of the two offsets overstates the number of characters * between the two offset. */ unsigned long get_char_count(unsigned long startofs, unsigned long endofs) const; /* * Get a pointer to a chunk of characters starting at the given * offset. Returns a pointer to the characters, sets *len_in_chunk * to the number of characters (up to a maximum of maxlen) in the * chunk, and advances *startofs to point to the next valid offset * after the chunk returned. This allows a caller to traverse the * possibly discontinuous array of characters by calling this * routine repeatedly to get chunks. */ const textchar_t *get_text_chunk(unsigned long *startofs, size_t *len_in_chunk, unsigned long maxlen) const; /* * Find a text string. Searches from the given starting offset to * the end of the text array. If we find the string, we'll set * *match_start and *match_end to the starting and ending offsets of * the match and return true; we'll return false if we can't find * the string. If exact_case is true, we'll match only if the case * matches, otherwise we'll ignore case. * * If dir is 1, we'll search forwards; otherwise, we'll search * backwards. If wrap is true, we'll wrap around at the end (or * start if going backwards) of the buffer to the opposite end of * the buffer and continue the search from there; we'll only fail if * we get back to the starting point and still haven't found the * string. */ int search(const textchar_t *txt, size_t txtlen, int exact_case, int whole_word, int wrap, int dir, unsigned long startofs, unsigned long *match_start, unsigned long *match_end); private: /* allocate the first page */ void alloc_first_page(); /* get the page containing an address */ size_t get_page(unsigned long addr) const { return addr / HTML_TEXTARRAY_PAGESIZE; } /* get the offset within the page containing an address */ size_t get_page_ofs(unsigned long addr) const { return addr % HTML_TEXTARRAY_PAGESIZE; } /* make an address out of a page number and offset */ unsigned long make_addr(size_t pg, size_t ofs) const { return (((unsigned long)pg * HTML_TEXTARRAY_PAGESIZE) + (unsigned long)ofs); } /* last page */ size_t last_page() const { return pages_alloced_ - 1; } /* offset of next free byte on last page */ size_t last_page_ofs() const { return pages_[last_page()].used_; } /* increase amount used on last page */ void inc_last_page_ofs(size_t len) { pages_[last_page()].used_ += len; } /* array of page pointers; the pages contain the actual text */ CHtmlTextArrayEntry *pages_; /* size of top-level page array (number of pointers allocated) */ size_t page_entries_; /* * number of pages allocated (this is actually just a high-water * mark for the number of pages *ever* allocated; pages can be * deleted when they are unreferenced, hence this doesn't represent * the number of pages actually present in memory, but just the * number of the next slot to be filled) */ size_t pages_alloced_; /* * Maximum address used. This includes both storage actually * committed and storage only temporarily used. */ unsigned long max_addr_; /* * Pages in use. This represents the actual number of pages for * which memory is currently allocated. Whenever we allocate a new * page, we increment this, and whenever we delete a page (because * it becomes unreferenced) we decrement this. */ size_t pages_in_use_; /* * Amount of memory currently in use. This keeps track of * allocations and deletions. We might actually be using more * system memory than this would indicate, because pages are not * necessarily completely full; a partial page takes up more OS * memory than this would indicate. */ unsigned long mem_in_use_; }; #endif /* HTMLTXAR_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/htmlurl.h����������������������������������������������������������������������0000664�0000000�0000000�00000001741�12650170723�0016323�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/html/htmlurl.h,v 1.2 1999/05/17 02:52:22 MJRoberts Exp $ */ /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name htmlurl.h - URL class Function Notes Modified 09/07/97 MJRoberts - Creation */ #ifndef HTMLURL_H #define HTMLURL_H #ifndef TADSHTML_H #include "tadshtml.h" #endif class CHtmlUrl { public: CHtmlUrl() { } CHtmlUrl(const textchar_t *url) { urltext.set(url); } CHtmlUrl(const textchar_t *url, size_t len) { urltext.set(url, len); } void set_url(const textchar_t *url) { urltext.set(url); } void set_url(const textchar_t *url, size_t len) { urltext.set(url, len); } void set_url(const CHtmlUrl *url) { urltext.set(url->get_url()); } const textchar_t *get_url() const { return urltext.get(); } private: CStringBuf urltext; }; #endif /* HTMLURL_H */ �������������������������������qtads-2.1.7/htmltads/htmlver.h����������������������������������������������������������������������0000664�0000000�0000000�00000003461�12650170723�0016316�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* Copyright (c) 2006 by Michael J. Roberts. All Rights Reserved. */ /* Name htmlver.h - HTML TADS version number Function HTML TADS has its own release numbering, separate from that of the underlying TADS VMs. TADS 2, TADS 3, and HTML TADS are conceptually separate components, and in practice each component is on its own independent cycle. Up until release HT-11 in September, 2006, it was our practice to identify the HTML TADS version by the versions of the underlying VMs linked into the build, but the quasi-independent release cycles made this quite confusing. To rememdy this, we've introduced the new practice of giving each HTML TADS release its own independent version number. The underlying VM versions should still be identified in the "about" box as well, but the new "HT-n" release number should be given the primary position. Something like this: HTML TADS Release HT-11 (TADS 2.5.10/3.0.11) The HTML TADS release number will henceforth be in the format "HT-n", where n is simply an integer we'll increment on each release. Ports can add a local patch version if needed, preferably with a "build" number that's simply incremented on each release (we recommend *not* resetting the build numger when the HT-n numbers changes - that could create confusion about whether "HT-11 build 5" is more or less recent than "HT-12 build 2"; it's easier to simply never reuse a build number so there's no ambiguity). The first version using this convention was numbered HT-11, since it was the 11th public release overall (as far as we can tell from our records). Notes Modified 09/08/06 MJRoberts - Creation */ #ifndef HTMLVER_H #define HTMLVER_H /* * The HTML TADS HT-n version ID string */ #define HTMLTADS_VERSION "HT-24" #endif /* HTMLVER_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/notes/�������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12650170723�0015610�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/htmltads/notes/changes.htm��������������������������������������������������������������0000664�0000000�0000000�00001000313�12650170723�0017730�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<title>Recent Changes to HTML TADS

Recent Changes to HTML TADS

This file lists the changes that have been made recently to HTML TADS. This release also contains improvements to TADS itself, which are documented separately in Recent Changes to TADS.

Note that we've tried to keep all of the documentation that accompanies this release up-to-date with these changes. This file is provided as a quick overview for experienced TADS users so that you can quickly see what has changed since the last version you installed.

Changes in Release HT-24 (Windows build Win121; TADS 2.5.16/3.1.3)

Released May 16, 2013
  • Workbench for Windows has a new setting, in the Program Arguments dialog (accessible via the Debug menu), that lets you set the file safety setting when running the program in the debugger. The default setting is "Use interpreter settings", which uses the file safety level set through the interpreter preferences dialog (accessed via the interpreter's Edit/Options menu command). The interpreter's factory setting is local read/write access, which allows reading and writing files in the directory containing the .t3 file being executed, but not in any outside folders. This is intended to protect your system against malicious or errant software when running .gam/.t3 files you download from the Internet, by preventing access to any files outside of the folder where you downloaded the game. However, this can be too restrictive if you're using TADS to write your own tools for file manipulation. The new setting lets you set less restrictive file safety levels when running your own tools, without affecting the default interpreter settings when you're running other games you've downloaded.
  • The <BLOCKQUOTE>...<CREDITS> tag incorrectly left subsequent text right-aligned; this has been corrected. In addition, the dash shown before the credit text is a typographical em-dash, if the local character set supports it, rather than a series of three hyphens as in the past. (bugdb.tads.org #0000184)
  • The "Build Web Page Package" command failed due to a naming conflict in the web builder script with a built-in function. This is now fixed. (bugdb.tads.org #0000167)
  • <IMG> tags and other tags referencing external files sometimes couldn't find files whose names contained non-ASCII characters, such as accented letters. The HTML parser internally didn't accept non-ASCII characters in external filenames, so it converted them to underscore "_" characters when searching for the files. The HTML parser now accepts non-ASCII characters and passes them through to the file system. Note that non-ASCII filenames might still be problematic on some operating systems, since the HTML parser simply uses the filenames as rendered in the local display character set, which on some systems might not always match the file system character set. When the two character sets differ, the HTML layer might not be able to find <IMG> and similar file references with non-ASCII names. It's safest to use filenames containing only ASCII characters for these resources whenever possible. (bugdb.tads.org #0000170)
  • Starting in version Windows build 116 (TADS 3.1.0), TADS Workbench didn't run under Windows 2000 or earlier versions of Windows, due to a dependency on a newer Windows feature that didn't exist until Windows XP SP1. The dependency has been removed and replaced with code that should work on Win 2K as well as newer systems, so Workbench should once again be able to run on Win 2K. (bugdb.tads.org #0000186)

Changes in Release HT-23 (Windows build Win120; TADS 2.5.16/3.1.2)

Released August 30, 2012

This release updates the TADS 2 VM, but there are no changes in the HTML TADS portion of the system.

Changes in Release HT-23 (Windows build Win119; TADS 2.5.15/3.1.2)

Released August 28, 2012
  • Workbench crashed when running a Web UI project more than once during a session. (This was an unintended consequence of the fix for bug #131 in build Win117.) (bugdb.tads.org #0000165)

Changes in Release HT-23 (Windows build Win118; TADS 2.5.15/3.1.2)

Released August 20, 2012

This release updates the T3 VM, but there are no changes in the HTML TADS portion of the system.

Changes in Release HT-23 (Windows build Win117; TADS 2.5.15/3.1.1)

Released July 14, 2012
  • The Workbench text editor on Windows now provides custom editor modes for Javascript and CSS files, with full support for syntax coloring, code folding, and auto indenting. (These file types are useful when working with the Web UI.)
  • The integrated Scintilla editor in Workbench for Windows has been updated to the latest version (3.0.2).
  • On Windows, you can now use drag-and-drop to open a file in the Workbench text editor. Simply drag a file from the Windows desktop and drop it onto Workbench, and Workbench will open the file in the text editor.
  • Workbench for Windows now displays the contents of resource folders directly in the project tree. In the past, Workbench allowed you to add a folder from the file system to the project tree, and showed the folder itself in the tree, but didn't show the folder's contents; you had to go out to the Windows desktop to view a folder's contents. This makes it easier to see the full list of resource files that are part of the project.
  • Workbench for Windows now displays library resource files (including resource directories, as described above) in the project tree. This is useful for Web UI games, since it makes it makes it easier to find and open library text resources (HTML, Javascript, CSS) in the Workbench text editor.
  • When you open a project in Workbench for Windows, Workbench now creates a project.tdbconfig file in the project directory to store your user interface settings for the project. The stored settings include things like which files are open in Workbench editor windows, window sizes and positions, past search strings, text editor option settings, and many other settings specific to the Workbench UI for the project. The new file has the same base name as the project (.t3m) file, with the .t3m suffix replaced by .tdbconfig.

    In the past, these UI settings were stored directly in the project (.t3m) file itself. There are two main reasons for moving them into a separate file. First, the old arrangement was problematic for collaboration, because the UI settings aren't really project-level settings at all; they're really per-user settings, since each user collaborating on a project might have different UI preferences. With this change, collaborators can share the .t3m file, which contains only the project-level build settings, but keep individual .tdbconfig files for their personalized UI settings. Second, the single-file setup wasn't great for use with source control systems, because Workbench updated the .t3m file virtually every time you opened the project. The constant updating tended to create numerous superfluous history updates for a .t3m file under source control. Workbench still writes a fresh copy of the .t3m file each time a project is closed, so the .t3m file's timestamp will be updated, but the contents should be left unchanged as long as you don't make any actual changes to the build settings (in which case you'd want to update the source control version of the file anyway to capture the changes in the build settings).

    Workbench still recognizes the old single-file format, but now always saves updates in the two-file format. This means that you can open old projects without having to make any manual changes. When you open an old project, Workbench automatically converts it to the new two-file format when you close the project.

  • Several bugs in the automatic code formatter in the Workbench text editor on Windows have been fixed:
    • Selecting the text of an object definition and explicitly reindenting the region (with Alt+Q) didn't work properly if a function definition immediately preceded the object. (bugdb.tads.org #0000152)
    • The body of a function defined with the "replace" or "modify" keyword, but no "function" keyword, wasn't indented properly when explicitly reindenting a region (with Alt+Q). (bugdb.tads.org #0000153)
    • The next line after a line ending in a "//" comment at the top level (outside of any function or object definition) wasn't indented properly. (bugdb.tads.org #0000154)
    • Workbench for Windows now stores filename paths in the saved project configuration using relative path notation for files that are within the project directory or its subfolders. (The project directory is the folder containing the .t3m file for the project.) In the past, Workbench stored absolute paths for many of the files referenced in a config file, which made it more difficult to move a project to a new folder or use it on a different machine with a different directory layout. (bugdb.tads.org #0000133)
    • Workbench for Windows now stores the global options file (htmltdb3.t3c) in the local Windows "Application Data" folder, to ensure that the file is accessible to each Workbench user. In the past, the options file was stored in the "Program Files" folder where Workbench is installed. This was problematic for users without Administrator privileges, since the Program Files folder is normally read-only for non-admin users. In such cases, Workbench was unable to save updates to the option settings, since it has to write to the options file to save those updates.

      The new scheme works as follows:

      • If Workbench is installed in a subfolder of the system's Program Files folder (usually C:\Program Files), the global options file is stored in the system's Application Data folder, in a corresponding subfolder. For example, if Workbench is installed in <Program Files>\TADS 3, the options file is stored in <Application Data>\TADS 3. Using a corresponding subfolder allows you to have multiple Workbench versions installed on the same machine by ensuring that each version's options file is in a separate location.
      • If Workbench isn't installed within Program Files, the options file is stored directly in the install directory (where the Workbench .exe file is located). The assumption in this case is that you've installed Workbench in a private user directory, and that you want to keep the installation self-contained by keeping everything in that single folder.
      (bugdb.tads.org #0000114)
    • When running a TADS 3 program within Workbench for Windows, the File object now uses the project directory as the default working directory. This restores the behavior that was in versions prior to TADS 3.1.0; in 3.1.0, the File object used the .t3 folder as the default directory. The .t3 folder is still the default when running the game in a normal interpreter (HTML TADS, or t3run in a DOS box). For debugging purposes under Workbench, though, it's more convenient to use the project directory as the working directory. The debugging .t3 file when working with Workbench is typically stored in a separate folder along with the other compiler output files, to keep the main project folder tidy; you usually want to keep any files you access through the File object together with the project source files instead of those compiler output files. (bugdb.tads.org #0000120)
    • In version 3.1, the HTMLT3.EXE interpreter process didn't terminate properly after the user closed the UI window of a Web UI game running in local stand-alone mode. The HTMLT3.EXE process kept running indefinitely in the background in this situation, making it necessary to manually terminate the process (using the Windows Task Manager, for example). This has been fixed. (bugdb.tads.org #0000131)
    • HTML TADS displayed a [More] prompt unnecessarily in cases where the text output just filled the available space, even when this left the window scrolled all the way down (so that responding to the [More] wouldn't have any effect on scrolling, but would only serve to clear the prompt itself). The prompt is no longer shown in cases where the window is already scrolled all the way to the bottom. (bugdb.tads.org #0000155)

    Changes in Release HT-22 (Windows build Win116; TADS 2.5.15/3.1.0)

    Released 12/21/2011
    • The Windows interpreters now send transcript output to the disk file immediately after each command, rather than buffering the output in memory. This usually isn't noticeable one way or the other, but it does matter when using an external tool that reads from the transcript file while the game is running. Some IF mapper tools do this, for example, to produce a dynamic visual map of the game while you play. The buffering in past versions meant that output text might be delayed in reaching the disk file (and thus in reaching any tools monitoring the disk file) for several turns. Output is now written immediately to the disk file, so monitoring tools should be able to keep in sync with what you see in the game window.
    • The fading parameters for <SOUND> tags are now handled a little differently for repeated tracks. In the past, fading was simply applied to each iteration of a repeated track individually, so a track with REPEAT=3 would fade in, play back, fade out, then fade in a second time, play back, fade out, and again for a third fade in, play back, fade out. This was probably undesirable for most practical purposes, since repeated sounds are most useful for constructing continuous ambient background music and effects. So the sound player now applies a fade-in only to the first iteration of a looped sound, and applies a fade-out only to the last iteration.
    • The Windows versions of Workbench and HTML TADS for builds Win111 through Win114 refused to run on older versions of Windows - specifically, 95, 98, ME, and NT 4. This was due to an "upgrade" we made to the latest version of the Microsoft C++ compiler (version 2008), which by design produces EXEs that won't run on those older systems. Starting with this build, we have returned to the older version of the Microsoft compiler to prepare the release builds. This should allow the system to run on the older OS versions again. (However, note that Windows 95 and 98 users will have to upgrade their version of Microsoft Internet Explorer to IE 5 or later in order to run Workbench. This ensures that you have a system DLL that Workbench depends on. This isn't necessary for newer Windows versions, since they come with the required DLL.)
    • In TADS 3 Workbench for Windows, starting in version 3.0.18, the Build Web Page command generated an invalid HTML file. This was actually a side effect of a bug in the TADS 3 interpreter (bugdb.tads.org #0000065). The interpreter bug has been corrected, which fixes the Build Web Page problem. (bugdb.tads.org #0000064)
    • Workbench for Windows is now a little more consistent about the word-wrapping margins when auto-formatting strings and comments (such as via the Alt-Q command). In the past, strings were filled one character short of the right margin as set in the options, and comments were filled to about three characters short. (bugdb.tads.org #0000058)
    • The syntax coloring in the Workbench editor on Windows didn't properly handle embedded << >> expressions within HTML attribute values in strings, as in "<img src='foo.jpg' width=<<width>> height=<<height>>>". The syntax colorer incorrectly treated the first >> as the end of the tag. The colorer now handles this case correctly. (bugdb.tads.org #0000072)
    • In Workbench for Windows, some of the tabbed or "wizard"-style dialogs were displayed at the wrong size on some systems, which could cut off some of the text and/or buttons in the dialogs, making them essentially unusable. The problem was due to an outdated font dependency in the dialog definitions; this has been corrected to use current Windows standards, so the dialogs should display correctly on all systems now. (bugdb.tads.org #0000087)
    • In Workbench for Windows, the debugger used to bring the game window to the foreground at the start of each new run, even when execution immediately stopped in the debugger at a breakpoint or single-step trace. This was slightly annoying because you had to manually switch back to the debugger window. The debugger window now stays in the foreground in this case. The game window still comes to the foreground when execution doesn't immediately stop in the debugger, of course.
    • A bug in Workbench for Windows caused a crash if a text file was saved with no project (.t3m file) open. This is now fixed. (bugdb.tads.org #0000092)
    • The output of Workbench's Project Search results (on Windows) contained an HTML syntax error if any of the files listed in the project window were non-existent. This didn't affect the appearance of the search results themselves, but it did log an error in the Debug Log window. This is now fixed. (bugdb.tads.org #0000097)
    • A bug in the TADS 3 coloring mode for Scintilla caused the wrong coloring if a "..." token was used within an embedded string expression, as in "<<func(a...)>>". This is fixed. (bugdb.tads.org #0000094)
    • In Workbench for Windows, the "new project" command omitted all libraries when the "Plain T3" configuration was selected. The command now includes the core system library in the new project. In most cases, even the most stripped-down build configuration will want to include the core system library, since it contains the basic program startup code and necessary support classes for the intrinsic classes. You can still manually remove these files from a new project after creating it if desired.
    • If you have a file open in the Workbench text editor, and you edit and save the same file with another application, Workbench automatically detects the change the next time you bring Workbench to the foreground, offering you the option of reloading the updated copy of the file from disk. In the past, if you chose to reload the file, the editor reset the cursor and scrolling position back to the start of the file, and also cleared any breakpoints and bookmarks in the file. Workbench now scrolls back to the same position and selects the same text range, and retains breakpoints and bookmarks.

      (Of course, if you make extensive changes to the file with another other program, the cursor and marker positions might not correspond to the same text after reloading. The editor only keeps track of these locations by line and column numbers. It doesn't attempt to analyze the differences in the file to find the same actual text that was previously selected.)

    • The Windows debugger didn't respond properly to the Terminate Program command when the game was started via a Step In or Step Over command (rather than the Go command). This has been corrected. (bugdb.tads.org #0000105)

    Changes in Release HT-21 (Windows build Win115; TADS 2.5.14/3.0.18.1)

    Released 1/29/2010
    • In releases Win113 and Win114, audio playback of compressed sound types (MP3, OGG) didn't work for resources embedded in a .t3 or .gam file. The audio generally just didn't play back when embedded, and the audio player sometimes showed a spurious error or warning. This has been corrected; an embedded version of an audio stream should once again work the same way as the stand-alone file version of the same stream. (bugdb.tads.org #0000080)

    Changes in Release HT-21 (Windows build Win114; TADS 2.5.14/3.0.18.1)

    Released 5/5/2009

    (This build has no changes specific to the Windows version or the HTML renderer - it's just an update of the TADS 2 and TADS 3 components. See their release notes for details.)

    Changes in Release HT-21 (Windows build Win113; TADS 2.5.13/3.0.18)

    Released 4/28/2009
    • HTML TADS for Windows now supports fade-in and fade-out in the <SOUND> tag. You can use this to fade in the sound at the beginning of a track, fade out the sound at the end, and fade out a track being canceled in mid-play (rather than just cutting it off abruptly). You can also do cross-fades between a track being canceled and the new track replacing it. The new features are described in detail in the Sound chapter of the HTML TADS documentation.
    • The new VOLUME attribute of the <SOUND> tag lets you control the relative playback volume of a track. The VOLUME value is from 0 to 100, where 0 is silence and 100 is the full, unattenuated level recorded in the files. This is currently only implemented in the Windows version.
    • The Windows version of TADS 3 now has better support for localizations that use MBCS (multi-byte character set) code pages. (On Windows, MBCS code pages are used for languages whose writing systems use large numbers of glyphs, such as Chinese or Japanese.) In the past, there were a number of problems with MBCS display: moving the text editor cursor through text containing double-byte characters sometimes garbled the on-screen display of the characters, as did selecting the text with the mouse, and it was possible in the text editor to delete or overwrite one byte at a time of a double-byte character. These problems have been fixed; the text editor and the interpreter now correctly handle multi-byte characters from the system default code page. (Note that the new support is specifically for the system default code page, which is the code page associated with the system localization. On XP, this can be changed via the "Language for non-Unicode programs" option.)
    • When you build a stand-alone game with the HTML interpreter for Windows, the toolbar in the game window now omits the "Load New Game" button. The corresponding menu command was excised some time ago from the main menu in stand-alone builds, but the toolbar button was still present; for consistency, the toolbar button has now been removed as well.
    • In Workbench for Windows, the editor now highlights the entire source line where an error occurred when you double-click on an error message in the compiler output window. This is designed to make it faster to find the cursor by making the location more visually apparent.
    • In Workbench for Windows, in the "Welcome" dialog, un-checking the checkbox to "show this dialog again next time" had no effect. This has been corrected.
    • In Workbench for Windows, the build option "treat warnings as errors" wasn't properly read from the saved project configuration when a project was loaded. This has been corrected. (bugdb.tads.org #0000044)
    • The HTML renderer incorrectly allowed line breaks within preformatted text (<PRE> ... </PRE>) at explicit typographical space characters (&emsp, &ensp;, etc). This has been corrected. (bugdb.tads.org #000050)
    • The HTML renderer incorrectly treated &nbsp; (non-breaking space) characters as potential line-break points when sizing a banner according to the width of its contents. This has been corrected. (bugdb.tads.org #000051)
    • In Workbench for Windows, there was a situation where the program could get stuck in a loop repeatedly showing the dialog asking you whether you want to terminate the running program before recompiling. This happened if execution was stopped in the debugger (at a breakpoint, or single-stepping) in an Exception constructor called to handle a run-time error, and you activated one of the Compile commands. The problem has been fixed.

    Changes in Release HT-20 (Windows build Win112; TADS 2.5.12/3.0.17)

    Released 9/8/2008
    • In TADS 2 Workbench on Windows, the "Compile and Run" command caused Workbench to go into an infinite loop of building the program over and over. This has been corrected. (bugdb.tads.org #0000022)
    • In the past, when TADS 2 Workbench on Windows tried to open a source file, and the file wasn't present in the main project folder, Workbench searched for the file by looking in each directory where other source files were previously found. In cases where you had several files with the same name in different directories, this sometimes loaded the wrong copy of the file. This was particularly noticeable if you created your own private copies of system files, such as adv.t or std.t - Workbench tended to find the system versions rather than your private versions, because the system include folder was usually first in Workbench's internal directory cache. Now, Workbench checks the list of "include" directories first, in the order given in the Build Settings dialog - this ensures that Workbench searches for source files in the same places that the compiler does when you build your program, which will generally ensure that Workbench finds the right version of the file. (bugdb.tads.org #0000034)
    • In text editor windows in Workbench for Windows, the text caret is now colored as the inverse of the main "Default" style background color for the window. For example, if the main background is black, the caret will be white; if the background is yellow, the caret will be bright blue (because blue is the RGB inverse of yellow). As an exception, a black caret is used if the background is a mid-level gray (because the RGB inverse of a mid-level gray is another mid-level gray, which wouldn't provide sufficient contrast). This helps ensure that the caret will be visible regardless of the background color. (In the past, the caret was always black, which made it difficult or impossible to see when using a dark color as the background.) (bugdb.tads.org #0000025)
    • The text editor in Workbench for Windows now adjusts the horizontal scrollbar limits to accommodate the width of the line containing the cursor, if the line is too wide for the current limits. Scintilla normally uses fixed limits for the horizontal scrollbar, because of the performance cost of determining the actual maximum width of all of the lines in the entire file; but its default is too narrow for very long lines of text. Workbench still doesn't scan the entire file, but it now checks the current line each time the cursor moves to ensure that the scrollbar is wide enough for that line. (bugdb.tads.org #0000029)
    • In TADS 2 Workbench for Windows, in the "Welcome" dialog that's displayed at launch, the "Open a project" button didn't work properly if you selected a .t file (rather than a project file - .tdc). The problem was that this skipped the normal source file wizard, which sets up the project configuration; without this setup work, Workbench wasn't convinced that a project had actually been loaded, so it prompted you to set up this information manually through the Build Settings dialog. This has been corrected; Workbench now runs the source file wizard when you select a .t file to load at startup. (bugdb.tads.org #0000028)

    Changes in Release HT-20 (Windows build Win111; TADS 2.5.11/3.0.17)

    Released 8/9/2008
    • In Workbench for Windows, breakpoint indicators (the little red circles in the left margin of a source file window) weren't updated properly when lines were inserted or deleted in the file above a breakpoint line. In particular, a duplicate breakpoint appeared for each actual breakpoint each time a line was added or removed earlier in the file. This has been corrected. (bugdb.tads.org #0000007)

    Changes in Release HT-19 (TADS 2.5.10/3.0.16)

    Released 4/10/2008 - Windows build Win110
    • The Windows version has been updated to the latest support libraries for the PNG and MNG formats; specifically, libpng 1.2.22, zlib 1.2.3, and mnglib 1.0.10.
    • TADS 3 Workbench for Windows didn't properly restore the "treat warnings as errors" project setting when loading a project (in other words, Workbench always forced this option to the default "on" setting every time a project was loaded). This has been fixed.
    • In Workbench on Windows, double-clicking on an error message in the Debug Log window when said window was maximized left all of the window's text up to the cursor click selected. Only the error message line itself should have been left selected; this is fixed now.
    • A bug in the TADS 3 editor mode caused incorrect auto-indenting if there was trailing whitespace after the function's open brace. This has been corrected.
    • In Workbench for Windows, the "Build Source ZIP" command sometimes omitted extension files that were directly in the "Extensions" folder set in the options - that is, files that were stored directly in this directory, rather than in a sub-folder. This happened when the only extension files in a project were in the main Extensions folder (i.e., the project didn't include any extension files that were in sub-folders of that main folder). This is now fixed.
    • The HTML renderer sometimes incorrectly calculated the width of tables and banners that contained hyperlinks. The renderer incorrectly treated a hyperlink as implying a possible link-break point for the purposes of the width calculation, even though hyperlinking doesn't by itself have this effect on layout. This didn't affect the way hyperlinks were actually laid out, but rather affected the size calculation for containers such as tables and banners. The visual effect was that banners and tables containing hyperlinked text that didn't were sometimes to narrow for their contents. This has been corrected.

    Changes in Release HT-18 (TADS 2.5.10/3.0.15.3)

    Released 11/2/2007 - Windows build Win109
    • HTML TADS for Windows sometimes "froze" when running on systems localized for East Asian languages. The problem was sporadic - it sometimes happened and sometimes didn't, essentially at random; but when it happened, the triggering conditions were (a) that the Windows default code page for non-Unicode applications was for a multi-byte character set (such as code pages 932, 936, 949, or 950), and (b) certain HTML entity (&xxx;) markups where displayed, such as the typographical quote marks (&lsquo;, &rsquo;, &ldquo;, &rdquo;). The bug was a bookkeeping error in the HTML parser in the handling of multi-byte characters; it has been corrected.
    • In the past, if the HTML source text for a table was of just the right length, it was possible for the text following the table to be rendered twice when first displayed. (This only happened during the initial layout - manually forcing a display refresh, such as by resizing the game window slightly, corrected the displayed text.) The problem was triggered by a particular arrangement of the HTML source text in the system's internal output buffers, so the output had to be of just the right length and had to occur at just the right point in the output sequence. The chances of encountering the bug in practice were therefore fairly remote. The bug has been fixed.
    • HTML TADS for Windows didn't correctly play back digital audio files (WAV, OGG) encoded with odd (non-even) sampling rates (e.g., 11025 samples per second). Depending on the playback duration, such files would play back partially correctly, then switch to bursts of white noise. This has been corrected.
    • Under certain rare conditions, Workbench on Windows failed to properly restore the docked-window configuration when loading a new project while another project was already loaded. The symptom was that one or more docked windows would show up along the wrong edge of the window - for example, a window that was docked along the left edge of the screen when the project was last used might be docked along the bottom edge instead when the project was re-opened. The exact conditions that triggered the problem were essentially random, so even if you encountered this bug, it probably only happened some of the time. The problem is now fixed.

    Changes in Release HT-17 (TADS 2.5.10/3.0.15.2)

    Released 9/13/2007 - Windows build Win108
    • The Workbench debugger on Windows now tries to be smarter about selecting the expression text for "tooltip" evaluation. (Tooltip evaluation happens when you let the mouse pointer hover over an expression in the source code for a few moments: the debugger evaluates the text under the mouse as an expression, and shows the result in a little pop-up window.) In the past, the debugger simply used the single word under the mouse as the expression text. The debugger now looks beyond the single word, as follows:
      • First, if the mouse is over a parenthesis ("()") or square bracket ("[]"), the debugger looks for the matching delimiter, and uses the entire stretch between (and including) the delimiters. Otherwise, the debugger uses the single word under the mouse.
      • Next, if the result so far starts with a "[", and the preceding character isn't an operator of some kind, the debugger adds the part before the "[", applying the first rule above to find its limits. Otherwise, if the character preceding the result so far is a ".", the debugger adds the part before the ".", using the first rule above to find its limits.
      • The debugger repeats the second rule above as long as it finds more "prefix" expressions to add.

      The new rules are convenient for index expressions ("lst[i]") and object property expressions ("obj.prop"). If you point to either of the brackets in an index expression, the debugger evaluates the entire list-and-index expression; you can still get just the index part by pointing to the part within the brackets, and just the list part by pointing to the part before the brackets. Similarly, if you point to the property name in an "obj.prop" expression, you get the whole "obj.prop" expression result; if you want just the object part, point to it.

      Note that if you select a range of text with the mouse and point to the selected range, the debugger evaluates the entire selection, just as in the past. This lets you override the default "smart" behavior to evaluate the exact expression you want, for times when the default selection rules don't give you the results you're looking for.

    • The Workbench debugger on Windows incorrectly showed "tooltip" expressions while the program was actively running. These expressions should only pop up when the program is stopped in the debugger; when the program is running, there's not a meaningful context in which to evaluate anything. This has been corrected.
    • TADS 3 Workbench for Windows didn't properly restore the "no verbose errors" project setting when loading a project (in other words, Workbench always forced this option to the default "verbose errors" setting every time a project was loaded). This has been fixed.
    • Workbench for Windows rearranged docked windows incorrectly when a running game was terminated while the main Workbench window was minimized. Docked windows were shrunk down to zero size on restoring the main window in this case. This has been corrected.
    • HTML TADS on Windows now uses the native Windows half-tone drawing mode for scaling images. This is a slightly slower drawing mode, but yields much better results when an image is drawn at an on-screen pixel size different from its native dimensions.
    • The TADS 3 editor mode in Workbench for Windows incorrectly colored "this" as a keyword; it no longer does this. (This came about because the TADS 3 coloring module is based on Scintilla's C++ coloring module: "this" is a keyword in C++, so this simply reflects some inherited code that needed further customization.)
    • The New Project Wizard in TADS 3 Workbench for Windows erroneously copied some information from a currently open project to the new project being created: bookmarks, special files, Web extra files, "feelie" files, and Notes & Miscellaneous files. This has been corrected.
    • On some Windows systems, the Workbench documentation search feature was unable to build its search index due to a permissions violation creating a temporary file. The indexer tried to create a temporary file in the root directory of the C: drive, but on some Windows systems this location is protected against writing by normal users. The indexer now uses a different temp file location that should be more reliable.
    • On some systems, the Windows Workbench documentation searcher sometimes failed to build the search index on the first search attempt due to a filename error. This has been corrected.
    • Starting in build Win107, Workbench for Windows automatically creates the object and symbol file output directories as needed when running the compiler to build the project. However, it didn't also create the output directory for the .t3 file itself. Workbench now also creates the .t3 output directory if it doesn't already exist.
    • The TADS syntax coloring mode in Workbench on Windows didn't correctly color text within a double-quoted string that contained an embedded << >> expression within an HTML tag. The syntax colorer treated the part after the first > at the end of the embedded expression as though it were the closing > of the enclosing HTML tag, whereas it should have treated that > as part of the >> sequence delimiting the embedded expression. The coloring from that point forward within the string was wrong, because the colorer's internal state was out of sync with the contents of the string. This has been corrected.
    • In the TADS 3 Library Reference Manual, hyperlink references to the class ExitLister incorrectly pointed to the object exitLister. This was due to (a) the LRM generator's use of the symbol name as the file name for a given object's reference page, and (b) the Window's file system's insensitivity to case. (The Windows file systems are generally case-preserving [they remember the case specified when a file is created, and faithfully reproduce it in the file index] but case-insensitive [two names that differ only in case are considered to refer to the same object]). The LRM generator now tweaks the filenames for object pages when necessary to ensure that each object has a distinct filename on case-insensitive file systems.

    Changes in Release HT-16 (TADS 2.5.10/3.0.15.1)

    Released 7/19/2007 - Windows build Win107
    • TADS 3 Workbench for Windows has a new option for the Project Search function that lets you search for text that spans multiple lines. To use this new option, open the File Search dialog and tick the checkbox "Collapse spaces and newlines." When this option is selected, the project searcher effectively "collapses" all of the whitespace in each file before searching it - that is, the searcher converts each series of consecutive spaces, tabs, and newline characters into a single space. The searcher then searches the file for your string or regular expression pattern. Since newlines are treated as though they were ordinary spaces, the string or pattern you're looking for will be matched even when it's split across multiple source lines. This is especially handy when searching for text within long strings in the source code, since long strings tend to be split up over several lines for readability.
    • TADS 3 Workbench for Windows has a new option setting that lets you specify the main folder where you keep your extension libraries. This option is in the main Options dialog, on the new System/Extensions page. You aren't required to keep your extensions here, but there are two benefits of doing so:
      • Workbench automatically includes this folder in the library path list. Workbench and the compiler will look for files here when they're not found within your project directory.
      • The Source ZIP packager treats this directory specially. When a project file is found directly in this directory (not in a subdirectory), the Source ZIP packager will include only the stand-alone file in the ZIP file. Normally, the packager includes the entire directory (including subdirectories) of each file in your project tree, on the assumption that any files actually included in the project are probably accompanied by one or more ancillary files, such as README files or documentation. When a file is found directly in the Extensions folder, though, Workbench takes it to be a simple, stand-alone extension file.
    • As described above, the Source ZIP packager now treats the Extensions directory specially when building your source ZIP file. When a file is found directly in the Extensions folder (rather than in a sub-folder), the Source ZIP packager assumes that the file is a stand-alone extension, without any accompanying files. The logic is that a more elaborate extension that includes documentation or other related files would warrant its own separate subfolder, so anything that's directly in the Extensions folder must not have any separate files associated with it. Any file that's in a separate subdirectory of the Extensions folder, or that's entirely outside of the Extensions folder, is considered to be part of a complex extension that includes ancillary files; in this case, the ZIP packager bundles up the entire contents of the directory containing the file.
    • TADS 3 Workbench for Windows now automatically creates the output directories for object (.t3o) and symbol (.t3s) files if they don't already exist. Workbench creates the directories as needed just before running the compiler during each build. This eliminates the need to create these directories manually when moving a project to a new machine.
    • The new <BODY> attribute INPUT lets you specify the text color for command-line input. This new attribute is parallel to the existing TEXT, BGCOLOR, LINK, VLINK, ALINK, and HLINK attributes: it takes as its value as an HTML color name or a #xxxxxx RGB value, and sets the text color for command input text throughout the document. For example, <BODY INPUT=GREEN> sets the color for input text to green. ("Input text" is text within <FONT FACE="TADS-Input"> tags.) The <BODY INPUT> attribute is useful because it lets you set a suitable contrasting text color for input text globally when you change the main window's background color with <BODY BGCOLOR=xxx>. In the past, it wasn't possible to change the input color globally - you had to use <FONT COLOR=xxx> tags on each input line instead - which sometimes left older parts of the transcript illegible after a background color change.
    • In Workbench, the editor language mode for TADS 3 incorrectly classified periods as parts of identifiers when they occurred after a series of alphanumeric characters with no intervening spaces. This caused incorrect syntax highlighting in some cases. This is now fixed.
    • The Workbench editor language mode for TADS 3 now recognizes VerbRule as a kind of object definition. In the past, VerbRule definitions didn't auto-indent properly because the editor mode thought they looked like function definitions.
    • In the TADS 3 language mode in the editor, the Alt+Q command (Edit.FillParagraph) could get stuck in an infinite loop if it was used to reformat a string with a block of text containing no spaces that was too long to fit within the margins. This has been corrected; when an unbreakable block of text within a string is too long to fit between the margins, the word wrapper now simply lets the block overflow the right margin, then breaks the line just after the block.
    • In Workbench for Windows HT-15, the auto-script QUIT-trimming feature (which optionally removes QUIT commands and/or other game-terminating event sequences from the end of an automatically captured session script) didn't function. It should now work as advertised.
    • Workbench for Windows now stores its documentation search index file in the system All Users\Application Data folder, under a TADS 2 Workbench or TADS 3 Workbench subfolder. This should ensure that Workbench is able to create the index file as needed, even on machines where the user doesn't have write permission to the Program Files installation directory. (In the past, Workbench stored the index file in the program directory; on shared computers, this sometimes didn't work due to user permissions settings.)
    • In Workbench for Windows HT-15, the Compile-and-Run command got stuck in an infinite loop of repeatedly recompiling the project. This has been corrected.
    • Workbench on Windows crashed if it was terminated (either via the File/Exit command or by closing the main Workbench window) while the project was still running in the debugger. This is now fixed.
    • In the past, when you created a new project in Workbench, the auto-script counter from any previously loaded project was propagated into the new project, so the new project's auto-scripts were numbered from whatever the last counter in the old project was rather than from 1. Workbench now resets the counter when a new project is created.
    • Starting with Workbench HT-15, the position of Find and Restore dialogs was saved with the project and restored each time the dialogs were subsequently displayed. Workbench now ensures that the restored position is within the actual physical display area. In cases where the project is saved on a machine with a large monitor and then moved to another machine with a smaller monitor, this check prevents the dialog from being hidden by appearing outside of the visible display area.
    • In the past, the flashing text caret sometimes failed to appear when bringing the interpreter window to the foreground after another application had been in the foreground. This happened under different circumstances, such as when asynchronous output (such as from a real-time fuse or daemon) occurred while the interpreter was in the background. This has been fixed.
    • On Windows, the interpreter sometimes left a few stray pixels on the screen during command-line editing when ClearType (Windows's font anti-aliasing system) was enabled. This has been fixed.
    • The TADS 3 editor mode in Workbench for Windows didn't formerly recognize the "function" keyword for function definitions when figuring the syntax indenting. This has been corrected.
    • In Workbench for Windows, using the Edit/Find command sometimes incorrectly reported "No more matches" when searching backwards in a file and more matching text was present. The editor should now properly find matching text in reverse searches.
    • In Workbench for Windows, the Edit/Find Again command incorrectly reported "No more matches" if the search was started with the cursor at the very start of the file. This has been corrected.
    • In Workbench for Windows, the Edit/Find Again command incorrectly failed to wrap the search back to the start of the file, even when the previous search had the wrapping option enabled. The wrapping option is now respected.
    • In Workbench for Windows, the Edit/Find dialog has a new scoping option that lets you search the project files, excluding libraries and extensions. The dialog now offers three scopes: Current File, which searches only within the file displayed in the active window; Project - No Libraries, which searches only within files that are part of the project tree and stored within the project directory; and Entire Project, which searches all files in the project tree, whether they're in the project directory or in separate library or extension directories.
    • The Edit/Find Again command in Workbench for Windows now respects the search-scope option from the last search conducted through the Edit/Find dialog. That is, Find Again will search through the current file, the local project files, or the entire project, according to the option settings in the last Find dialog.
    • When using the tabbed document UI in Workbench for Windows (that is, with the MDI child windows maximized), switching back and forth between the Help window and other document windows sometimes caused the Help window's contents to scroll slightly. This should no longer occur.
    • In Workbench for Windows, docked tool windows were sometimes rearranged as the debugger opened and closed windows to match preference settings when switching between design and debug modes. For example, suppose that in design mode, you had the Project window and the Scripts window docked along the left edge of the main Workbench application window, with the Projects window on top; and suppose that you set the preferences so that the Scripts window was hidden when switching to debug mode and was shown again when switching back to design mode. In this case, on returning to design mode, Workbench sometimes rearranged the windows so that the Scripts window re-appeared above the Projects window. Workbench should now restore the original window order in these situations.

    Changes in Release HT-15 (TADS 2.5.10/3.0.15)

    Released 3/8/2007 - Windows build Win106
    • TADS 3 Workbench's "Build Source ZIP Package" feature now includes external libraries in the ZIP file it builds. Each external library folder is bundled into a ZIP file of its own, and then these ZIPs are included in the overall source ZIP package. This ensures that recipients of the source package have everything they need to build the project on another machine, without having to find and download third-party libraries (or, more problematically, unpublished custom libraries created by the project's author).
    • TADS 3 Workbench for Windows now accepts folders in the "Notes & Miscellaneous" section of the project list. The main purpose of the Notes & Misc section is to specify additional files to be included in the Source ZIP package; the ability to add folders here is convenient if you want to include a whole batch of notes, since you no longer have to add them individually to the list. Folders in this section work just like they do in the Resource Files section: Workbench remembers the folder in the project list, not its contents, and retrieves the current contents of the folder each time you build a Source ZIP package. This ensures that you always get the current contents at the time of a build, so you don't have to worry about keeping the project list up to date as you add and remove files in the folder.
    • In TADS 3 Workbench for Windows, the auto-scripting system can now optionally trim QUIT commands from the end of a script file. You can configure the particular QUIT commands to trim using the Scripts page of the Project Settings dialog. Enter one or more lists of events to trim; each list is a set of one or more regular expressions, one event per line. You can enter multiple alternative lists by separating lists with blank lines. Each time you run the game, Workbench scans the resulting auto-script file for each of the QUIT sequences listed in the project settings; if it finds a match at the very end of the new auto-script file, Workbench will delete the entire matching sequence from the end of the script.
    • Workbench's integrated text editor in Windows build Win105 added a feature that wasn't documented in that release's change log: a new User Keywords syntax coloring feature. On the Syntax Coloring page of the Options dialog, for the TADS 3 language mode, new "syntax element" categories are now included for User Keywords 1, 2, and 3. These categories let you specify custom coloring for three sets of keywords of your choosing. You can specify the keywords for each category selecting the category in the Syntax Element list, then clicking the Keywords button. This opens a dialog that lets you type in the keywords to associate with the category. Whenever these keywords appear in TADS 3 files, the editor will color them according to the custom colors you select for their categories. For example, you could use this to set custom coloring for certain library macros (DefineAction, VerbRule, etc).
    • Workbench for Windows now remembers the position of the Find and Replace dialogs, and restores the last position each time one of these dialogs is redisplayed. This means that if you move the dialog so that it's not covering any of the source windows, the new position will stick - you won't have to move the dialog again each time you bring it up again. In the past, Workbench centered the dialog each time it was displayed, so you had to manually move it out of the way again on each use.
    • Workbench for Windows now keeps keyboard focus in the Find dialog after each "Find Next" operation, whether or not a match was found, and whether or not the latest result was in a different window from the previous result. In the past, focus moved to the current text editor window after an unsuccessful multi-file Find Next and each time the latest match was found in a new window; this was inconvenient when operating the dialog via the keyboard, since keyboard focus didn't reliably stay in the dialog after a Find Next.
    • In the Windows Workbench, the TADS 3 language mode's code folding had a problem in some cases folding methods. This has been corrected.
    • In the Workbench default key mapping on Windows, the keys for Edit.PopBookmark and Edit.JumpToNamedBookmark have been swapped from the previous release. Edit.PopBookmark is now Alt+J, and Edit.JumpToNamedBookmark is now Ctrl+J. The change makes the default key mapping the same as the Epsilon-style key mapping for Edit.PopBookmark, which was originally intended but was entered incorrectly in the mapping in the prior release.
    • Workbench for Windows has a new command, Edit.JumpToPreviousBookmark, which jumps backwards to the previous bookmark. This does the same thing as Edit.JumpToNextBookmark, but goes the opposite direction through the source. This command isn't assigned to any key in the standard mappings.
    • Workbench for Windows has two new commands to delete bookmarks. Edit.ClearAllBookmarks deletes all bookmarks throughout the project, while Edit.ClearFileBookmarks deletes all bookmarks in the current file. These commands aren't assigned to any keys in the standard mappings.
    • In Workbench for Windows, you can now set the color for selected text in the editor and tool windows. This is done via the Fonts page of the Options dialog - simply select the new colors in the Selected Text drop-down lists. The color selections you make here apply to tool windows, but by default they don't apply to text editor windows. This is because Scintilla normally shows selected text with the syntax-sensitive font color against a gray background, which allows most syntax coloring to remain visible even in a selected region. If you want to override this feature and use custom colors, check "Override Scintilla selection color in editor windows." Sight-impaired users who use screen readers should note that some screen readers use the font color to detect selected regions, so Scintilla's non-standard selection coloring will prevent such screen readers from detecting selections. To make Scintilla use the standard Windows selection colors, set the Selected Text colors to "Automatic" and check the "Override" box.
    • The integrated editor in Workbench for Windows didn't show the selected custom coloring for keywords in the "User Keywords 3" list. This has been corrected.
    • The TADS 3 editor mode's syntax coloring didn't properly color continuation lines of preprocessor directives (i.e., with "\" at the end of the prior line) as preprocessor directives. It also didn't indent continuation lines properly. For both purposes, the editor incorrectly treated continuation lines as regular code. This has been corrected.
    • In Workbench for Windows, some of the error message dialogs were "unowned" windows, meaning they came up with their own separate Windows taskbar entries, almost as though they were separate applications. This could be slightly annoying in that clicking on the main Workbench window when one of these message dialogs was displayed brought the Workbench window to the front, but that window wouldn't accept any input until the error dialog was dismissed. To dismiss the error dialog, you had to manually navigate back to its window, which might be hidden behind the main Workbench window by now. These dialogs are now all "owned" by the Workbench window, meaning they'll always stay in front of the Workbench window; this should eliminate the navigation problem by ensuring that the error dialogs are never hidden behind the Workbench window.
    • In the past, the HTML Interpreter didn't recognize <BASEFONT> as a tag with global effect when pruning old text to reduce memory usage. It now properly treats <BASEFONT> as a global tag, so it retains these tags when pruning. (The HTML Interpreter discards old text and HTML tags from the top of the window whenever the total amount of text in the window exceeds a user-specified memory threshold. The Interpreter discards only enough text to keep the total under the memory limit; since the usual limits are pretty large, this process is normally transparent to the user, since all of the discarded text should have scrolled off the top of the window long ago whenever this happens. When discarding old text, the HTML parser makes a point of discarding only tags which have no global effects, so that their removal doesn't affect any of the text that remains after the pruning.)
    • In the past, Workbench for Windows didn't update the project list to reflect changes to a .tl file made within the integrated editor. This has been corrected - whenever you save a .tl file that's part of the project, the project window will automatically refresh its list of the library's contents.
    • Due to a bug in Scintilla, the Stuttered Page Down command didn't work properly if the current page contained wrapped lines. This has been corrected.

    Changes in Release HT-14 (TADS 2.5.10/3.0.14)

    Released 2/9/2007 - Windows build Win105
    • Important note: if you're upgrading from a previous version of Workbench, and you want to use one of the updated standard key mappings, you'll have to manually load the new mapping file of your choice. Your previous settings are stored in the Workbench configuration file, and Workbench will respect your old settings until you explicitly load a new key file. To do that, go to the Keyboard page of the Options dialog, click the Load Key Map button, and select the *.keymap file you want to load (the standard mappings are in the Workbench program directory). Click OK in the Options dialog to save the new mappings.
    • A few new editor features have been added to Workbench for Windows. These are described more fully in the editor help file.
      • The integrated editor has been updated to the latest, just-released version of Scintilla, 1.72.
      • The Alt+/ key binding has been replaced in this version. In HT-13, Alt+/ was bound to Ext.Edit.FormatComment. This key is now used for setting a bookmark instead (see below). The old binding isn't needed any longer because Alt+Q now serves as a multi-purpose, context-sensitive reindent command: press Alt+Q while the cursor is in a TADS comment to reformat the comment; press Alt+Q while the cursor is in a TADS string to reformat the string; press it in other TADS code to reindent the code line or region; press it in a non-TADS window to reformat a text paragraph.
      • The new command Edit.CommentRegion adds or removes commenting at the start of each line in the selected region. The command is bound to the Ctrl+8 key in the standard key mappings (this key was chosen because 8 is the asterisk key, and asterisks are commonly involved in commenting in many languages). The TADS language mode handles this by adding or removing "//" at the start of each line; other modes currently ignore the command. This command is handy when you want to quickly disable some code for testing purposes, and then later re-enable it.
      • The editor now provides a "bookmarking" feature, which lets you mark locations in source files for easy return later on. This new feature is described in detail in the Workbench help, in the Advanced Editor Features section.
      • The new editor command Edit.FindDefinition (on the F6 key in the standard keyboard mappings) searches the project for the definition of the symbol at the cursor location. The editor searches for a TADS 3-style object, class, or function definition, looking in all files included in the project list. If a definition is found, Workbench opens the file containing the definition and selects the matching symbol. Since the cursor is positioned at the matching symbol in the definition, you can search for another definition of the same symbol simply by pressing F6 again - this is useful for symbols that have more than one definition, such as via 'modify' or 'replace'.
      • The TADS mode adds a new command to reformat a literal string, applying word-wrapping and auto-indenting. You can invoke this by positioning the cursor within a string and pressing Alt+Q. The editor will automatically re-flow the string to fill out lines to the current right margin, wrapping lines at word boundaries. The reformatting leaves line breaks intact when they're adjacent to explicit formatting line break codes (\n, \b, <br>, <p>, <.p>), and won't break a line in the middle of a {format code} sequence.
      • The TADS mode now distinguishes statement braces ("{" and "}") from other operators for syntax coloring purposes. This lets you set a separate color for braces.
      • The editor now supports "code folding," which lets you selectively show and hide blocks of code. The folding system detects the lexical hierarchy of the TADS code - that is, it recognizes an object definition as a block of code, a method within an object as a block, and a set of statements enclosed in braces within a method as a block. Enable code folding using the Show Folding Controls command on the Edit menu. When folding is enabled, the editor shows "+" and "-" controls in the left margin; these work like buttons in the ordinary Windows tree control, to expand or collapse a block of the code. Folding is often helpful with large object or method definitions, because it lets you hide details so you can more easily see the big-picture structure of the code.
      • The editor now maintains a "mark," which is a temporary, ad hoc bookmark, similar to the Emacs mark. The mark is set automatically whenever you set a selection mode anchor (Edit.SelectionMode, etc.), jump to the start or end of the document, or start a regular or incremental search. You can jump to the mark with the new command Edit.SwapMark, and you can select the text between the cursor and the mark with the new command Edit.SelectToMark.
      • The incremental search command can now be used to extend a selection range. If a selection mode is in effect at the start of an incremental search, and the search is successful, the selection range is extended at the end of the search to include everything between the original selection anchor and the end of the matching search text.
      • A new Options page, Wrapping & Margins, lets you control how the editor handles long lines. You can set a right margin, which controls the line width for certain commands, and you can make the editor display a visual guide at that column. You can also tell the editor to automatically word-wrap text as you enter it when you type past the margin.
      • The TADS-mode comment formatter now uses the new margin setting as the line length when doing its word-wrapping.
      • A new command, Edit.FillParagraph, auto-formats a paragraph of plain text, filling out each line to the margin width and breaking lines on word boundaries to fit.
      • When an editor window is in TADS 3 language mode, the Edit.FillParagraph command doesn't do ordinary paragraph filling, but instead does context-sensitive TADS-code reformatting, depending on where the cursor is positioned. If a region is selected, the command reindents each line in the region, as though you'd used the Ext.Edit.SyntaxIndent command. Otherwise, if the cursor is in a comment, the command reformats the comment, the same as Ext.Edit.FormatComment; and if the cursor is within a string, the command reformats the string, the same as Ext.Edit.FormatString. This means that you don't really need to bind separate keys to the Ext.Edit.FormatString and Ext.Edit.FormatComment commands - you can simply use the Edit.FillParagraph command (Alt+Q) to do whichever type is right for the context.
      • Editor windows can show the line number for each line in the margin. Use the Show Line Numbers command in the Edit menu to control this.
    • A new option (set via the Library Paths page of the Options dialog) lets you specify that library files are to be opened in read-only mode by default. When this option is set, the editor will open any file not located within your project folder as read-only. This lets you avoid accidentally editing a library file when you're browsing the file or stepping through it in the debugger. You can always make a library window writable by using the Edit.ChangeReadOnly command (which isn't assigned to a key in the default mapping). By default, this option is not set, meaning that all files are opened as writable by default.
    • The project window now recognizes a few additional resource filename extensions as indicating text files that can be opened in the integrated text editor: .log, .cmd, .c, .cpp. (When you double-click on a resource file in the project window, Workbench tries to guess whether the file in question is a text file by looking at the filename suffix. If the suffix is one typically associated with plain text files, Workbench opens the file in the internal editor; otherwise, Workbench asks the operating system to open the file using whatever application is normally associated with that type of file.)
    • A bug in Workbench for Windows sometimes occured when loading a project. The problem manifested as a Workbench crash in some cases, and in other cases as random rearrangement of some of the windows left open in the previous session. The problems were particularly likely to occur when loading a project after Workbench was already open. The bug has been fixed, so these problems should no longer occur.
    • In HT-13, the Compile-And-Run command in Workbench initiated the build correctly, but didn't start the project running after the build completed. This has been corrected.
    • The regular expression option for the project-wide search command (Find in Project Files) didn't work in the previous release - checking the regular expression box always yielded a regular expression syntax error. This has been corrected.

    Changes in Release HT-13 (TADS 2.5.10/3.0.13)

    Released (1/19/2007) - Windows build Win103
    • Workbench for Windows now has a built-in text editor. The integrated editor is Scintilla, which is a highly stable open-source text editing component designed specifically as a programmer's editor. Scintilla has the features you'd expect from a source code editor: multi-level undo, syntax coloring, regular expression searching, and much more.

      The Scintilla editor takes the place of the former read-only source code display window, so when you're stepping through code in the debugger, you're actually using a live editing window - this means you can make changes directly in the debugger window when you discover a problem or otherwise find something you want to change. (Of course, changes you make to the source code while debugging won't affect the running version of the program until you recompile.)

    • Scintilla can be programmed to understand different programming languages, to provide appropriate syntax coloring. The integrated version recognizes TADS 3 code (of course), and it also includes support for HTML and XML. In fact, Scintilla has pre-defined syntax modes for numerous other languages - C, C++, Python, Perl, Java, BASIC, Fortran, and on and on - but to avoid clutter in the Workbench UI, we've only exposed the language modes that are likely to be useful within TADS projects.
    • The TADS 3 editing mode not only provides syntax coloring, but also provides a sophisticated auto-indenter and comment formatter. The auto-indenter attempts to keep your code properly indented as you write it; whenever you press a "hot" key, the auto-indenter tries to fix up the current line to make it fit the standard indenting style. Hot keys include the return key, braces, square brackets, semicolons, and colons. The comment formatter automatically reformats a block comment when you type the "*/" sequence; it fills the comment's paragraphs for an 80-character line width and sets up a column of asterisks at the left edge.

      These auto-formatting features are designed to make your life easier - much easier - by saving you the trouble of manually fussing with the indenting as you write your source code. But they can take a little getting used to: some people find it disconcerting to have the code jumping around on its own so much. If you find that you don't like the auto-indenting scheme, you have several options for controlling it, moderating it, or eliminating it. The Indenting page in the Options dialog lets you set the auto-indent style to "syntax-based," "same as previous line," or "off." If you turn it off, the source text will be left exactly as you type it. If you set it to "same as previous line," the indenter will merely indent each new line to the same level as the previous line whenever you press return, and otherwise will leave everything alone. This page also lets you turn off the comment auto-formatter, if you find its behavior annoying. If you want to go beyond these pre-defined choices, there's one more option: you can go in and change the TADS 3 mode to make it behave exactly the way you want. We supply the source code for the TADS 3 mode - it's in tads3addin.cpp - so you can make whatever customizations you like and then recompile the plug-in DLL.

    • You can now customize the keyboard, to define your own command keys. Use the Keyboard page of the Options dialog to customize key bindings. The keyboard mapping system is particularly useful if you want to make the new built-in text editor behave like another editor you're already familiar with.
    • The language-mode features we just described are fully extensible with a plug-in system, in case you want to add support for another language you find useful within Workbench. The plug-in system uses DLLs containing COM objects - we chose that infrastructure because it lets you write these objects in virtually any programming language on Windows. We've provided a set of C++ classes that makes it relatively easy to write a plug-in in C++. If you're interested, refer to the "addins" directory for a bunch of source files that provide specifications and examples. The main file is itadsworkbench.h, which contains the specification of the plug-in interface. Also see wb_addin.h, which defines the framework classes. We've also supplied source code for the TADS 3 and HTML modes, both to serve as examples of how to build add-ins, and to make it easy to make minor tweaks to the behavior of those modes. Everything related to the add-ins is in the "addins" subdirectory of the main install directory.
    • TADS 3 Workbench for Windows has a new Project Search feature. This lets you search all of the text files in your project for a given string or regular expression - it's like the Unix "grep" command. Use the Search for Text command on the Project menu to perform a search. If you check the Regular Expression box, the search string will be interpreted as a regular expression pattern, using exactly the same syntax that the TADS 3 VM uses in rexSearch() and the like.
    • The Find dialog (for searching for text in a source file) now has a Regular Expression checkbox option. (A "regular expression" is a special kind of wildcard pattern, sort of like those "*.TXT" file searches you can do in Windows, but much more powerful.) Check this box to perform a regular expression search of a text editor window. As with Project Search, this uses the full TADS 3 regular expression syntax.
    • The Find dialog is now "modeless," meaning that you can switch back to the editor window while the Find dialog is still displayed, to move the cursor or make changes to the text.
    • A new Incremental Search command provides a more convenient way of searching for text in an editor window. Incremental search skips the dialog, so it can be run entirely via the keyboard. Unlike the regular Find dialog, incremental conducts the search continuously as you type in the search string; this saves time by letting you type in only as much of the search term as you actually need to locate the text you're looking for. Incremental searches have several options that can be selected via keystrokes within a search, including regular expression matching, whole-word matching, exact-case matching, and forward/reverse searches.
    • A new Replace command lets you search for text in an editor window and replace each occurrence with a new bit of text. Like Find, Replace can optionally search for a regular expression pattern. The dialog lets you find and replace occurrences of a search term one at a time, so that you can individually inspect and approve each substitution, and also has a button to replace all remaining occurrences without further prompting. The dialog is modeless, meaning that you can switch back to the text window while the dialog is still running, if you want to make additional edits between replacements.
    • In TADS 3 Workbench, the Project window has several new sections:
      • The Feelies section lets you list additional files to include in release packages. These files are automatically included in the ZIP Package and Windows SETUP Package builds.
      • The Web Page Extras section lets you add extra files to include in the Web page package. Any files you drop here are automatically added as links in the "Download" box on the generated Web page. This lets you add things like hints, maps, walk-throughs, and any other extra material you want to make available for download from your Web site, but which you don't want to include in the release package itself. For each file, you can set a title for its hyperlink on the generated page by right-clicking the file in the Project window and choosing "Set File Title..." from the menu. If you don't set a title for a file, its hyperlink will simply be a "friendly" version of the filename (any path or extension is removed, the first letter is capitalized, underscores are changed to spaces, and each embedded capital is preceded by a space: so misc\readMe.txt will be shown as "Read Me", for example).
      • The Notes and Miscellaneous section is a place to add files that you want to keep track of as part of the project's overall source materials, but which don't belong in any of the other Project window sections. You can use this section for things like your "to do" list, your working notes, and so on. The benefits of listing files here are (1) that you can easily open a file by double-clicking it in the list, and (2) that the "Build Source ZIP Package" command will automatically include these files.
      • The Special Files section is a little different from the other sections. Rather than containing a list of files of a particular type, it contains slots for a number of files with special purposes in certain of the release packages. Each slot is labeled with its special purpose, plus the currently assigned file, if any; you can assign a new file to a slot by dragging a file from the Windows desktop and dropping it on the slot, or by right-clicking the slot and selecting "Set File" from the pop-up menu.
    • If you go to the "Windows SETUP" page of the Build Settings dialog in TADS 3 Workbench, and you click the "Edit" button to display the SETUP options editor, you'll find that many of the options that were formerly presented have been removed. The missing options have been replaced with slots in the Special Files section (see above). The new arrangement has the advantage of being more consistent overall: these special files are really just some additional, special-purpose source files for the project, and the new arrangement recognizes this by moving them into the Project window's list, where all of the other source files for the project are managed.
    • In the Project window in TADS 3 Workbench, you can explicitly set the "Start Menu" title for any file in the Feelies list, and for certain of the files in the Special Files list. Set a file's Start Menu title by right-clicking on the file in the Project window, and selecting "Set File Title" from the menu. The title is only used in the Windows SETUP package; it's ignored for the Release ZIP package. Each Feelie item that's assigned a Start Menu title will be added to your game's Start Menu group, with the title you specify appearing in the group menu; other feelies are included in the package and will be copied to the user's hard disk at installation, but won't be listed in the Start menu.
    • In TADS 2 Workbench for Windows, the Build Settings dialog has a new page, "Files - Special." This page lets you specify (1) a Cover Art image, which (if specified) is bundled into your compiled .gam file for use by game browsers (such as TADS Game Chest); and (2) a Windows icon (*.ICO) file to use for the Windows .EXE version of your game. Note that the EXE icon was specified in past versions via the "Windows SETUP" page of the Build Settings; that older setting has been removed, so you should use the new "Special" page to set the icon from now on.
    • TADS 3 Workbench has a new set of Build commands that let you create release packages with a couple of menu clicks. These commands automate procedures that, in the past, had to be done manually using other tools, such as a ZIP program. The new packaging commands are:
      • Build Release ZIP Package: This creates a ZIP file containing the release version of your game, suitable for distributing to players or uploading to the Web (to the IF Archive, for example). The ZIP file contains a freshly compiled release build of your game (the .t3 file), plus all of the Special Files and Feelies listed in the Project window. The Resource Files listed in the Project window are automatically bundled into the compiled game. The resulting ZIP file is the "universal" edition of your game for distribution: UNZIP tools are available on virtually every platform, and the .t3 file bundled in will work on any computer with a TADS Interpreter. To distribute the game, you simply distribute the ZIP file to players. To play the game, a player just needs (1) your ZIP file, (2) an UNZIP tool for her operating system, and (3) a TADS 3 Interpreter for her OS. Almost everyone these days will already have an UNZIP tool installed, and many regular IF players will already have a TADS Interpreter. Set the output ZIP file via the "Release ZIP" page of the Build Settings dialog.
      • Build Windows SETUP Package: This command (which was included in previous Workbench versions as well, under the name "Build Windows Installer") builds an automatic SETUP program for the Windows edition of your game. The resulting SETUP program contains a freshly compiled copy of your game, compiled as a Windows executable (.EXE) program, plus all of the Special Files and Feelies listed in the Project window. The game .EXE file will automatically contain the Resource Files listed in the Project window. The resulting SETUP file is a completely stand-alone edition of your game - to distribute it, you just distribute the single installer .EXE file; to install, a player simply runs the installer. You don't need to distribute any other files, and players don't need to download or install anything else. Unlike the ZIP file edition, players don't need to have an UNZIP tool or a separate TADS Interpreter installed - the installer contains everything they need. As a result, this is by far the most convenient distribution format for players on Windows - but, of course, it has the disadvantage that only Windows users can use it. If you plan to distribute this edition, it's a good idea to distribute the ZIP edition as well - that way, Windows users get the ease of use of the SETUP edition, but people on other operating systems can still run the game. Set the output EXE file (and other options) via the "Windows SETUP" page of the Build Settings dialog.
      • Build Source ZIP Package: This creates a ZIP file containing everything that goes into building your game: the project (.t3m) file, the source files, header files, resource files, Special Files, and Feelies. This makes it easy to transfer your project to another computer, or to distribute the project to other people. Note that only "local" files are included - that is, files within the folder containing the .t3m file (or subfolders of that folder); anything else is assumed to be a third-party library, which you normally wouldn't want included since it's not part of your project's source. Set the output ZIP file via the "Source File ZIP" page of the Build Settings dialog.
      • Build Web Page: This creates a simple Web site for your game, suitable for uploading to a Web hosting service. The main page is created by filling in a template with the bibliographic information from your GameInfo.txt file (which is usually, in turn, generated from the versionInfo object defined in your game's source code), and includes a link to download the game (either as just the .t3 file or as the release ZIP package), and optionally to the Windows SETUP package and the Source ZIP package. The page will also include links for any additional files that you list in the Web Page Extras section of the Project window. Set the output directory and other options via the "Web Page" page of the Build Settings dialog. The web page builder uses a standard template to determine the layout of the page, but you can create your own custom template instead if you want to change the appearance or add special features; you can specify your custom template file using the "Custom Web Template" item in the Special Files section in the Project window.
      • Build All Packages: This builds a fresh copy of each of the packaging options: the release ZIP, the Windows SETUP, the source ZIP, and the Web Page. To be more precise, this actually builds all of the selected packages, the selections being made via the Build Settings dialog. Each package has a page in the Build Settings dialog, and each package page has a checkbox labeled "Include in 'Build All Packages'". If this box is checked, the package will be included in the build; otherwise it won't. By default, the box is checked for every package. If there are packages you don't plan to distribute, you can make the Build All Packages process run a little faster by un-checking the boxes for the unwanted packages.
    • TADS 3 Workbench for Windows can now automatically capture all input events each time you run your game, creating a "script" that you can replay on a later run of the game. Here's how this works:
      • Each time you run the game, Workbench automatically creates a script file, with a name like "Auto 5.cmd", in the Scripts subfolder of your main project folder.
      • The Scripts tool window shows the list of script files, sorted from newest to oldest.
      • To replay an entire script, right-click on the desired script in the Scripts tool window and select "Replay" from the menu.
      • To replay a script only to a certain point, first open the script in the text editor by double-clicking on the script file in the Scripts window, then right-click on the last line of the script to execute, and select "Replay to Cursor" from the menu.
      • When you replay a script, Workbench will tentatively create a new auto script for the replay session, in case you type some new commands after the replay has completed. However, if you don't end up typing any new commands, so that the new script is in the end identical to the one being replayed, Workbench will discard the redundant new script. This avoids cluttering the Script list with multiple copies of the same script if you're repeatedly playing through a script as you test a series of changes.
      • Most people work on a game one section at a time, so the scripts that Workbench captures are typically most useful when they're fresh. As you move on to focus on new parts of the game, older scripts will probably become less useful, since they exercise portions of the game that you're not currently working on. This is why Workbench sorts the Script window from newest to oldest - we figure you'll usually want the most recent scripts to be closest at hand.
      • What's more, Workbench can help keep the Scripts window tidy by automatically discarding older scripts. By default, Workbench keeps the latest 25 "Auto N" scripts: each time a new script is created, Workbench deletes any Auto scripts beyond the most recent 25. You can change the maximum number of Auto scripts to keep, or you can turn off this auto-delete feature entirely, using the Scripts page of the Options dialog. No matter what the settings, Workbench will never include a renamed script in this cleanup process - the cleanup process will only delete scripts with the "Auto N" name pattern. So, if there's a script you want to keep permanently, simply rename it to something other then "Auto" followed by a number - you can do this by right-clicking on the file in the Script window and selecting "Rename" from the menu.
      • You can disable the auto-scripting feature and change the other scripting settings using the Scripts page of the Options dialog.
    • Workbench for Windows has a number of enhanced user-interface features. Most of these should be familiar to most Windows users, since they're common in other applications:
      • Workbench now has multiple toolbars instead of a single main toolbar. The new documentation search and integrated editor features have increased the number of toolbar commands to the point where a single toolbar would have become quite large. The new arrangement creates groups of related commands, and puts each group in its own separate toolbar.
      • The real advantage of the separate toolbars is that you can now rearrange the layout of the toolbars, to suit your working style and the amount of space you have available on your monitor. You can move a toolbar by dragging on its left edge. You can also hide and show each toolbar individually - right-click on a toolbar to access a menu that lets you select which toolbars are visible. The main menu is itself a toolbar now, so you can arrange its position the same way (you can even hide it if you want).
      • When you maximize the document windows (the "MDI" windows), Workbench now displays a list of "tabs" that let you navigate among the windows. The tabs are displayed at the top of the document window area; click on a tab to bring up another window. Many people find the tabbed style of interface easier to navigate than the MDI style, since each window gets the full workspace when it's active, but the list of other windows is always close at hand. To switch to the tabbed interface, simply maximize a document window; to switch back to MDI mode, click the "restore" button at the right of the tab list.
      • Menu items now display icons that correspond to the toolbar buttons for the same commands. Many people find that this makes menus easier to navigate, by providing a visual cue to help spot the command you're looking for at a glance.
    • You can now tell Workbench to show or hide the various "tool" windows when switching between Debug and Design modes. (Workbench is in "Debug" mode when the game is running, and "Design" mode when the game isn't running.) You can specify which windows to show and hide in each mode using the new "Tool Windows" page of the Options dialog. For the most part, the tool windows are useful only in one mode or the other; for example, the Stack window is useless in Design mode, since there's no active stack to display. This new feature is designed to give you more room to work by letting you optimize the set of tool windows displayed in each mode.
    • Workbench for Windows now lets you perform a full-text search of the documentation. To run a search, type your search terms into the text box in the new Search toolbar, and click the button (or just press Return). Workbench will display a Search Results window showing a list of the pages found within the documentation containing the search terms.
    • By default, the search engine looks for pages containing all of the keywords you type; if you want to look for documents containing one of several keywords, separate the words with OR (the upper-case letters are required); for example, operator OR expression searches for pages with either keyword. If you want to exclude pages that contain a certain word, type NOT (in upper-case) before the word: operator NOT expression returns pages that contain "operator" and which don't contain "expression." By default, the search engine looks for common variations of the words you type, such as plurals and verb tenses; to look for the exact word you type, put it in quotes: "operator" will match only "operator", not "operators" or "operated" or "operating". Similarly, if you want to look for an exact phrase, put the whole phrase in quotes: "thing class" searches for the whole phrase, not just the individual words.
    • If you installed the version of the Author's Kit that includes the full documentation, the search engine will search your local copy of the documentation. This requires building a local index, which uses a few megabytes of disk space and could take a minute or so, but only needs to be done once. If you installed the smaller Author's Kit with no documentation, Workbench will run the search using the tads.org on-line search engine, which obviously requires an internet connection to perform the search. The on-line and local versions work the same way (they use the same search engine), but the local version is usually faster because it doesn't involve any network latency.
    • The Search toolbar also lets you perform a quick search of the current document window, or a quick project-wide text search. Use the drop-down arrow in the Search bar to select "Search Current File" or "Search Project Files"; after you've done this, the Search bar will search the current file or the project source code rather than the documentation. This is a little quicker than going through the normal Find dialogs. Note that unlike Doc searches, Project and Current File searches are not keyword searches - these are simple literal-text searches that simply look for the exact text you type. The OR and NOT keywords and the quoting syntax used in the Doc search don't apply here. These searches use the default Find options (case-insensitive literal text, not regular expressions). You'll have to use the normal Find dialog, accessible through the Edit menu, if you want to set non-default search options: regular expression matching, whole-word matching, or exact-case matching.
    • Workbench for Windows has some new commands on the Tools menu to work with IFIDs (Interactive Fiction Identifiers, as defined by the Treaty of Babel and used in the TADS GameInfo metadata system). First, the command "Generate IFID" randomly generates an IFID for you to use in your project. Second, the command "Read IFID From..." reads the IFID from an existing TADS game. Importantly, the Read IFID command generates the "implied" IFID for a game that was originally released without an IFID defined in its GameInfo data (or without any GameInfo data at all). This is useful because the Treaty of Babel specifies that you must use this implied IFID as the explicit IFID for any future version updates of a game previously released without an IFID - this ensures that the IF Archive and other tools will be able to recognize the new and old releases as different versions of the same game. The Babel didn't appear on the scene until mid-2006, so the many TADS games released before that time obviously don't have explicit IFIDs.
    • The Windows SETUP builder has a new feature that lets you add your own custom items to the Start Menu group for your game. For users running your SETUP version, it will be much easier to find any "feelie" items you include if you put them on the start menu - most Windows users will never look at the "C:\Program Files\My Game" directory on their own after installing your game, so they'd probably never find your feelies otherwise. You can add an item to the Start menu by including syntax like this in the list of installation settings (the "Installer Options" field in the Windows SETUP page of the Build Settings dialog):

      startmenu="My Item Title" item1.htm
      

      This shows "My Item Title" as the title in the Start Menu group. When the user selects this item, Windows opens the file "item1.htm" using whatever program is associated with that file type on the user's machine. The installer automatically includes the given file in the install set, so you don't need to list it separately with a FILE= line.

      A note on file formats: It's pretty safe to assume that everyone has software installed for viewing plain text (.txt) and HTML (.htm) files, and most Windows machines have a Portable Document Format (aka Acrobat, or .pdf) reader as well. Other formats, such as Word (.doc) or other word processor formats, are less universal, so you're much better off with HTML or PDF if you want fancy formatting.

    • The HTML formatter didn't properly align the text of the first line of an <LI> item in a numbered list (<OL>...</OL>). The text position was off by a few pixels to the left or right of the list margin. This has been corrected.
    • In the past, in Workbench for Windows, the font popup list in the Formatting page of the Debugger Options dialog sometimes showed multiple copies of a font name. This happened because the popup list showed one copy of the name for each code page (localized character set) installed for the font; some of the system fonts support numerous code pages, so a font like "Courier New" might have shown up five or six times. The dialog now displays only one copy of each font name.
    • In Workbench for Windows, when you change a #define symbol in the Project Settings dialog, Workbench reloads the list of files in the Project window to make sure that any affected libraries reflect the new #define values. Libraries (.tl files) can refer to #define symbols to determine which source files to include, so changing a #define can affect the list of included project files. In the past, this refresh operation had the side effect of forgetting all of the "file exclusions" for libraries. That is, if you had unchecked any of the checkboxes next to source files within a library in the Project tree, the checkboxes were all checked again after the refresh. Workbench now retains the checkbox settings. (Note that this only applies to files that are included both before and after the #define change. If you change a #define in such a way that the list of included files changes - say, because you change the LANGUAGE symbol from "en_us" to "fr_fr" - the checkbox settings for any old files that are no longer included in the project will be lost, and the checkboxes for any new files that weren't previously included in the project will be checked by default. Workbench can't guess at the correspondence between files in, say, the en_us library and the fr_fr library, so it can't carry over the exclusion settings when the libraries change.)
    • In certain cases, Workbench for Windows was inconsolable about finding a missing source file. If you added a source file to your project, and the source file was in a subdirectory under one of the existing library directories (the directories listed in the "Library Paths" page of the Options dialog), Workbench remembered the source file's name with a relative path - so, for example, the source file was listed in the project tree as "MyLib\mysource.t". If you later moved this source file to another directory, Workbench asked as always for your help in locating the missing file. The problem appeared when the new directory didn't have the same final path element ("MyLib" in our example). In these cases, Workbench insisted on finding a file with the exact relative path "MyLib\mysource.t" - even if you explicitly selected the new "mysource.t" location, Workbench refused to accept it, repeatedly popping up the "Missing File" dialog until you hit Cancel. This has been corrected; Workbench now automatically drops the relative path prefix in these cases and just looks for the unadorned source file name.
    • In a TADS 3 game, if you used a library (.tl) file in the game, and the library included multimedia resources, Workbench sometimes failed to find the resources when running a debugging build. This happened when the library you included was in a subdirectory of your main project directory, or a subdirectory of a directory in your global library path; in these cases, Workbench didn't properly take into account the relative path to the library's subfolder when looking for the resources. This has been corrected.
    • The Windows HTML Interpreter didn't play back Ogg Vorbis sounds properly when they were embedded as resources in the .t3/.gam file. This has been corrected.

    Changes in Release HT-12 (TADS 2.5.10/3.0.12)

    Released September 15, 2006 - Windows build Win102

    This is the TADS 3.0 General Release version.

    • Due to a bug in Workbench for Windows, the "Help/User Manuals" menu command sometimes failed to actually open the manual. The problem occurred when no other browser windows were open; if any browser windows were already open, the manual was launched properly. This has been corrected: the command should now work properly whether or not any other browser windows are open.

    Changes in Release HT-11 (TADS 2.5.10/3.0.11)

    Released September 8, 2006 - Windows build Win101

    Note: starting with this release, HTML TADS has its own release numbering, separate from that of the underlying TADS VMs. TADS 2, TADS 3, and HTML TADS are all essentially on their own separate release cycles, so the past practice of treating the HTML TADS version number as equivalent to the underlying VM version numbers was too confusing. From now on, HTML TADS releases will be identified by an "HT-n" version number. We're starting this new practice with HT-11, since this is the 11th public release. In addition, the Windows build will be identified with its own port-specific build number, so that any future bug-fix versions that only affect Windows can be differentiated.

    • The Windows HTML interpreter failed to match a parameterized font name when the name was the last of multiple font names in a <FONT FACE="x,y,z"> list. This has been corrected.

    Changes in version 2.5.10/3.0.10

    Released August 17, 2006
    • In Workbench for Windows, the debugger incorrectly allowed evaluating a new expression while the debugger didn't have control (i.e., while the game program was actively executing). This shouldn't have been allowed because the current context for evaluation changes continuously while the game is executing, and as a result attempts to evaluate expressions at such times could yield unpredictable results. In certain rare circumstances, this could have caused Workbench itself to crash. This has been fixed: expression evaluation is now correctly suspended during game execution. If the user enters a new expression (into the "watch expression" window, for example) while the game is executing, Workbench accepts the new entry, but simply leaves the value part blank until the next time the debugger gains control (by hitting a breakpoint, for example), at which point the expression is evaluated in the current context as usual.
    • In Workbench for Windows, the New Project Wizard now sets verbose error messages by default. You can change this by going to the Build menu, selecting Settings, selecting the Diagnostics tab, and un-checking the "Verbose error messages" checkbox.
    • In Workbench for Windows, if the option "Clear Debug Log when starting new build" is selected (on the Messages page of the Debugger Options dialog, accessible from the View menu), the debug log will not automatically scroll to the bottom as new messages appear. If a large number of messages are displayed during the build, this saves you the trouble of manually scrolling the window back to the top to see the first few error messages, which are almost always the ones to tackle first.

    Changes in version 2.5.9/3.0.9

    Released August 29, 2005
    • The Windows HTML interpreter recognizes the new "Default" setting for a game's presentation profile. If a game's GameInfo record uses the "Default" setting, the Windows interpreter now uses the current default theme when playing the game. The current default theme is marked as such in the Themes menu, and the player can change the default at any time using a new command in that same menu. The player can also manually set a game to use the "Default" theme by editing the game's profile on the Game Chest page. Also, if a game doesn't have a GameInfo record at all, then the interpreter uses the current default theme for the game. In the past, the interpreter used the Multimedia theme in these cases.
    • On Windows, the default display font for the "TADS-Script" pseudo-font is now Comic Sans MS (at 11 point). In the past, the default was Times New Roman; that's obviously not a script-style font, but it was chosen as the default because there aren't any script fonts that are guaranteed to be installed on all Windows systems. Comic Sans is a better default because (a) it actually is a script font, and (b) it's present on almost every Windows system, since it's installed as part of Internet Explorer. On those rare systems where Comic Sans MS isn't available, the operating system will automatically fall back on the default application font (usually Arial), so even these systems aren't any worse off with this change.
    • The Windows HTML interpreter didn't retain the bold and italic attributes of the "TADS-Input" pseudo-font when another <FONT> tag (such as one changing the size or color of the text) was nested within the <FONT> tag that selected the "TADS-Input" font. This is now fixed.
    • The Windows interpreter no longer shows anything in the status area at the bottom of the interpreter window when the mouse is hovering over a hyperlink with an explicitly empty title attribute (TITLE=""). In the past, the interpreter acted as though the TITLE attribute wasn't present at all, and displayed the hyperlink's HREF text. In many cases, the HREF text is just some internal code for the game's own use, and so isn't meant for the player's eyes. In the past, the only way to suppress the status area display of the HREF was to use a non-empty TITLE attribute instead, which supersedes the HREF text in the status area display. In some cases, though, it's useful to be able to show nothing at all in the status area for a link. You can now do that by using TITLE="" in the <A> tag.
    • The HTML interpreter didn't properly show TITLE tags when the mouse was hovering over a hyperlinked image. This now works correctly.
    • A bug in the HTML renderer resulted in an incorrect display layout when a table cell contained nothing besides a nested table, and the nested table used a background color or image. In these cases, the background of the nested table was sometimes displayed at an incorrect location on the screen. (The problem only occurred when the nested table was the only thing in its cell - if there was anything else in the cell, even a "\n" or one of the special HTML non-breaking space characters, the display was correct.) This is now fixed.
    • The HTML renderer sometimes gave a nested table too much space in its enclosing table's layout, leaving unwanted empty space in the enclosing column. This problem usually only showed up when a table contained a cell that contained only a nested table. This has been fixed.
    • A bug in the HTML interpreter caused unpredictable beahvior, including crashing the interpreter, when using animated images within a banner-API window. The bug usually manifested when the program deleted a banner window that was displaying an animated image at the time of the deletion. This has been corrected.
    • In the past, the <NOBR> tag didn't always work properly within tables. Depending on the table's surroundings, <NOBR> sections within a table sometimes caused incorrect layout of the table's contents. This is now fixed.
    • In some cases, an IMG tag with separate ASRC and/or HSRC images didn't update the display properly on a click on the image or when the mouse hovered over the image. This has been corrected.
    • On Windows, the inputDialog() function used a fixed size for the buttons it displayed, so a long name could overflow the space allowed for the button. This made the dialog confusing, since the label text was clipped to the button's display size, hiding part of the label. The dialog now takes the label length into account, and makes the buttons wider if necessary to accommodate longer labels.

    Changes in version 2.5.9/3.0.8

    Released September 12, 2004
    • The HTML interpreter's table layout rules have changed for cells that span multiple rows and/or columns. In the past, the interpreter divided up the width of a multi-column cell evenly over the columns it spanned, and divided up the height of a multi-row cell evenly over its rows. This made it difficult to achieve certain effects, because it tended to add a lot of extra space to rows and columns that would otherwise have been small. The interpreter now uses a more sophisticated algorithm described in the table layout section.
    • A bug in the HTML interpreter caused the interpreter to crash, on rare occasions, when using the Find or Find Next commands. The problem was tied to unpredictable particulars of memory allocation, so it was essentially random - it didn't depend in any obvious way on what game was being played or on what appeared on the screen. This has now been fixed.
    • In the past, clearing a banner window created through the TADS 3 banner API stopped any sounds playing in the main window. This no longer occurs.
    • <SOUND> tags are no longer allowed in banner windows created through the banner API. Such sound tags were allowed in the past, but this caused various problems, because all windows effectively shared the "sound queues" of the main window - that is, all sounds were effectively coordinated through the main window even when played from banner windows. Any <SOUND> tags that appear in banner API windows will now simply be ignored.
    • In the HTML interpreter, going back to review a page (with the "go to previous page" command) incorrectly showed the contents of any <TITLE> tag on the page as part of the page's text. This no longer happens.
    • The HTML parser is now more tolerant of missing "close tags" (HTML tags of the form </xxx>). When a close tag is found that doesn't match the latest open tag, the parser now searches for an enclosing open tag that matches; if a matching open tag is found, all of the nested tags are closed. This change generally produces results closer to what was intended, despite the error in the HTML. In particular, when a close tag is missing somewhere within a complex, nested structure like a table or a banner, the change ensures that the larger structure is still parsed correctly, even though its contents have a missing close tag.
    • A problem in the HTML interpreter occasionally caused unpredictable behavior, including crashing the interpreter, when TADS 3 banner API windows were used with the "MORE mode" style. The problem only occurred when the windows were layed out in particular ways, so it happened only rarely. This has been fixed.
    • "https:" has been added to the list of recognized internet hyperlink prefixes for the <A HREF> tag. (See the <A HREF> documentation for more details.)
    • In the Windows HTML interpreter's "Customize Theme" dialog, in the "Colors" page, the Status Line color buttons are now disabled when the "Override game colors" option is selected. When this option is selected, the Main Text color settings are used for everything, so the status line text colors can't be specified separately in this case.

    Changes in version 2.5.8/3.0.7

    Released June 12, 2004
    • The HTML interpreter's command history feature (which lets you recall recent commands to the command line, so that you can edit them and re-use them) has a couple of enhancements:
      • On Windows, the F8 key now acts the same way it does in the DOS shell: it recalls the most recent prior command that starts with what's currently on the command line. If you press the key repeatedly, it recalls in turn each prior command that starts with that prefix. This handy shortcut lets you recall a complex past command with just a few keystrokes.
      • The command history now omits a command if it's an exact duplicate of the immediately preceding command. This ensures that the history doesn't fill up with a run of "WAIT" or "AGAIN" commands, for example. Note that this only applies to the most recent previous command; so if you type an alternating sequence of commands (WAIT, AGAIN, WAIT, AGAIN, etc), for example, the entire sequence will be stored in the command history.
    • Workbench for Windows now correctly handles the various Edit menu commands (Cut, Copy, Paste, Delete, Undo), and their corresponding accelerator keys, when editing an expression or value in the "Locals" or "Watch Expression" windows. In the past, these commands were ignored.
    • The Windows HTML interpreter now understands Ctrl+Insert to mean "Copy," Shift+Insert to mean "Paste," and Shift+Delete to mean "Cut." These accelerator keys were standard in the first few versions of Windows, and while they've long since been deleted from the Windows UI guidelines, many applications still support them anyway because some long-time Windows users are accustomed to them. Note that the modern equivalents are still supported, too, of course - you can still use Ctrl+C for Copy, Ctrl+V for Paste, and Ctrl+X for Cut.

    Changes in version 2.5.8/3.0.6

    Interim build 2.5.8/3.0.6q: 5/9/2004

    • Due to a bug introduced several releases ago, the Windows HTML interpreter didn't obey the Ctrl+Left and Ctrl+Right keys, which should move the caret left and right a word at a time while editing a command line. This bug has been fixed, so the word-at-a-time keys now work again.

    Interim build 2.5.8/3.0.6p: 4/25/2004

    • In the past, the HTML interpreter didn't stop with a MORE prompt in a banner window if a long block of text was displayed, the banner was cleared, and then another long block of text was displayed. This could be seen, for example, in some "chapter list" menus in TADS 3 games, by stepping through a series of long chapters. This has been corrected.
    • The HTML interpreter now accepts .JPE as a valid suffix for JPEG image resources. The suffixes .JPG and .JPEG are also still recognized, of course.
    • The Windows HTML interpreter did not properly initialize the size of a new banner window that didn't yet have a size explicitly set or any text displayed, occasionally causing a new banner window to appear at a random size if the game didn't explicitly set the size soon after creation. This has been corrected; a new banner window is initially set to zero width and height, ensuring that it won't be visible until the game finishes setting it up properly.

    Interim build 2.5.8/3.0.6o: 3/14/2004

    • A problem with vertical spacing after a command line, caused by a change in 3.0.6n, has been corrected. In 3.0.6n, a &zwnbsp; character was added after each command line. This proved to be a problem if a "\b" sequence was displayed after a command line, because it caused two blank lines to appear in the display window (the extra line was the one containing the &zwnbsp; character). So, the &zwnbsp; has been removed. In its place, a refinement of the pre-3.0.6n mechanism is now used: after each command input line, and only after a command input line, the interpreter simply adds an extra line's worth of scrolling height to the window. This has the desired effect of scrolling up the window one line after each user input, which provides the user with immediate visual feedback that the command line has been accepted, while eliminating the undesirable side effects of both the 3.0.6n change and the pre-3.0.6n mechanism.
    • The HTML interpreter now treats multiple calls to the T3 function inputLineCancel() as significant. This is important in cases where several calls are made with the "reset" parameter set to nil, and later a call is made with "reset" set to true. In the past, calls were ignored if an interrupted input was not still pending, so only the first call after an interrupted input was obeyed. This meant that it was impossible to reset the input buffer if the input had already been canceled but not reset. This has been corrected.
    • TADS 3 Workbench for Windows now inherits the TADSLIB environment variable, if set, and passes it along to the compiler. This allows you to add directories to the library file search path globally. The library folder list you specify in the Workbench options comes before the TADSLIB environment variable in the search order; that is, the compiler will first look for a library file in each folder specified in the Workbench options, and then will turn to the folders listed in the TADSLIB environment variable.

    Interim build 2.5.8/3.0.6n: 3/6/2004

    • Workbench for Windows now remembers whether or not the source file window has been maximized (within the main work area in the Workbench window), and restores the same status when the project is reloaded in future sessions.
    • Workbench for Windows now stores the debugging image file name using a directory path relative to the project directory, if possible, even if you enter the filename with an absolute path in the build settings dialog. This makes it easier to move the project from one machine to another, or relocate it to a different folder on the same machine. (In the past, this filename was stored with a relative path in most cases, but Workbench didn't force it to a relative path if you manually entered an absolute path. Workbench now automatically changes the filename to use a relative path even if you enter an absolute path.)
    • In the past, the HTML interpreter always showed some extra blank space below the last line of text if the last line ended in some sort of line-breaking markup sequence (such as a paragraph break, or the end of a <TABLE> tag). This was intentional, to provide better feedback to the player when the player pressed the Enter key at the end of a command line, but had the undesirable side effect of leaving extra blank space when merely pausing for input. The extra space is no longer shown. If you want to show extra space at the bottom of a display when pausing, you can do so by displaying line breaks to the desired height, then displaying a "&zwnbsp;" character (a zero-width non-breaking space), which has no visual appearance of its own but forces the HTML renderer to immediately show all of the extra space preceding the character. The interpreter itself now uses this trick to achieve the effect that was formerly achieved with the now-defunct extra vertical spacing.
    • In the Windows HTML interpreter, the hyperlinks in the "About" box are now shown even when the preferences are set to hide hyperlinks. Similarly, the hyperlink to return to the Game Chest page after a game has ended is now shown as a hyperlink, regardless of the preference settings. (In the past, these controls followed the preference settings for hyperlink visibility; since they're part of the interpreter user interface, not part of the game text, it makes more sense for these controls to override the preference settings and show as hyperlinks unconditionally.)
    • The HTML renderer sometimes got the margins wrong on the initial layout if a "floating" image was at or near the start of the layout. The correct margins displayed on subsequent redraws in these cases, but the initial layout sometimes had negative margins, which left some text partially outside the main window. This has been corrected.
    • In the past, TADS 3 games compiled into Windows executables (.exe files) didn't load bundled multimedia resources (images, sounds, etc.) properly. This is now fixed.

    Interim build 2.5.8/3.0.6m: 11/15/2003

    • The HTML interpreter now supports the BACKGROUND attribute in TABLE, TH, and TD tags. This attribute sets the background image for a table or a cell within a table. If the image is larger than the bounding rectangle of the table or cell, the interpreter clips the image to the table or cell size; if the image is smaller than the bounding rectangle, the interpreter "tiles" the image, drawing as many copies of the image across the width and/or down the height of the bounding rectangle as are needed to fill the space.
    • In Workbench for Windows, if the game program was waiting for a keystroke (via inputKey() or the like), and you tried to terminate the program (by starting a new build, for example), Workbench got stuck in a loop trying to terminate the game. The game program will be terminate correctly now.
    • The Windows HTML interpreter's "mute" button on the toolbar now uses two icons, one for when sounds are on and one for when sounds are muted, showing the current status. The old icon looked too much like a status icon indicating that the sound was always mute.
    • The HTML parser now accepts closing </BODY> tags. Since the special TADS dialect of HTML doesn't treat <BODY> as a container, it's never necessary to include closing </BODY> tags, and if you do, they interpreter simply ignores them. In the past, though, the parser didn't accept </BODY> tags at all (it flagged an error), which caused some confusion among people who already know standard HTML, where <BODY> is a container.
    • In Workbench for Windows, an incompatibility with Windows ME caused Workbench to crash under certain obscure conditions. The crash was due to a Windows API problem that appears to exist only in ME, and happened when using the "new project wizard" to create a new game, and then only when the Debug Log was the only window open. This problem has been corrected.
    • The HTML interpreter had a problem that sometimes prevented a game's bundled resources from being loaded properly when several games were run in sequence in a single interpreter session. This is now fixed.
    • A bug in the HTML renderer caused whitespace characters following a <Q> start or end tag to be ignored when a character entity (an "&" sequence) immediately preceded the tag. (This is a pretty rare combination of markups, so this hardly ever showed up in practice, but it's obviously perfectly valid.) The whitespace is now properly retained.

    Interim build 2.5.8/3.0.6k: 8/17/2003

    • Workbench for Windows now automatically converts any forward-slash characters ('/') to backslashes in filenames entered into the Build Settings dialog. (In the past, using forward slashes as path delimiters was allowed, but it caused odd behavior in certain cases, because Workbench treated forward slashes as distinct from backslashes for the purposes of comparing filenames and paths.)
    • The HTML interpreter froze up if a table was displayed that contained no columns (no <TD> tags), but had a WIDTH setting for the table itself. This type of table is usually ill-formed anyway, in that the lack of any columns is probably due to missing table structure tags, but the interpreter will now gracefully accept such tables.
    • The Windows HTML interpreter is a little better at deferring redraws when clearing the screen. This eliminates flicker in some cases, especially when a banner window is being added or removed right before or after the screen is cleared.
    • The Windows HTML interpreter is better at handling MORE scrolling in very small windows. In the past, if a window was very small (around five or ten pixels high, for example), pressing the space bar could sometimes actually make the window scroll backwards. This has been corrected; even very small windows will handle MORE scrolling properly now.
    • A bug in the HTML interpreter caused occasional drawing problems when first displaying certain tables. When a table had a large "cellpadding" or "cellspacing" value, one or two rows of pixels were sometimes not drawn when the table was first shown (but did redraw properly on subsequent refreshes, such as resizing the window). This is now fixed.
    • A bug in Workbench for Windows caused a breakpoint marker (the little red circle in the left margin of a source file) to appear in the first source window when a global breakpoint existed. The spurious breakpoint marker will no longer appear.
    • A bug in Workbench for Windows caused the program to open extra file handles open each time the application was activated. This has been corrected.

    Interim build 2.5.8/3.0.6j: 8/2/2003

    • Workbench for Windows now uses different window icons for the main Workbench window and for the game window, so that it's easier to tell which is which on the Windows taskbar or Alt-Tab window selector.
    • Workbench for Windows has a new command (on the toolbar and in the Build menu) that terminates a build in progress. This is handy if you started a build accidentally and don't want to wait for it to complete. Note that interrupting a build can sometimes leave intermediate files (such as symbol and object files) incomplete, so if you interrupt a build, you might have to subsequently perform a full build to make sure everything is rebuilt properly.
    • Workbench for Windows now offers the option of automatically clearing the Debug Log window before each new build. The option is on the Messages page of the options dialog ("View" menu, "Options..." command). Some people like to start each new build with an empty Debug Log window, to eliminate any clutter from past builds.
    • TADS 3 Workbench for Windows now automatically clears the old list of Include files from the Project window when you use the "Scan for Includes" command. This ensures that the Include list contains all of the actual header files used in the project, and only those header files.
    • The "New Project" wizard in TADS 3 Workbench for Windows now offers an option to create a bare-bones project that doesn't include the standard TADS 3 adventure library (adv3). This is convenient for creating special-purpose projects that don't require the full adventure library.
    • In the Windows HTML interpreter, if the window was scrolled to the right, then resized so that the scrollbar wasn't needed any longer, then scrolled back all the way to the left, the horizontal scrollbar didn't always disappear as it should. It should now disappear reliably in these cases.
    • A bug caused Workbench for Windows to crash when compiling a game, if the compiler wrote an extremely long message to the log window. This has been corrected.
    • A bug in the HTML interpreter caused a crash when cutting and pasting long bits of text under certain circumstances. This is now fixed.
    • The HTML interpreter now uses the <BODY> tag's BGCOLOR attribute even if a <BACKGROUND> image is also set. This ensures that the background color will show through any transparent sections of the background image.
    • In the Windows HTML interpreter, right-clicking on a hyperlink will no longer follow the link; right-clicking now simply brings up normal context menu.
    • In the Windows HTML interpreter, previous page viewing is more functional and flexible. The mouse wheel now works properly, you can use the Find command, and the window is properly refreshed if you change themes while viewing a past page.
    • In Workbench for Windows, some of the keyboard accelerators (such as Ctrl+C for Copy, or Ctrl+Q for Quit) didn't work properly in the game window. This has been corrected.
    • In Workbench for Windows, the "Windows" menu wasn't kept properly updated when source windows were maximized (so that the source windows took up the entire MDI "client area"). This is now fixed.
    • A bug in the HTML interpreter occasionally left banner windows only partially redrawn; this was particularly apparent with things like hint menus. The problem has been corrected.
    • In Workbench for Windows, the expression evaluation windows now accept a few more keystrokes, to make the keyboard interface a little more convenient. The Page Up and Down keys now scroll the window a page at a time, the Home key moves to the first row, and the End key moves to the last row. In addition, in the "watch" window, when no row is selected, starting to type an expression (or pressing Enter or F2) will automatically scroll to and select the empty last row; this saves a mouse click in the common case that you just want to add a new expression to the list when no other expression is selected.

    Interim build 2.5.8/3.0.6i: 6/15/2003

    • Due to a bug in a change made in the previous interim build, MORE mode didn't work properly in the Windows HTML interpreter. MORE mode works correctly again.
    • Workbench for Windows didn't allow re-running the game if, in the course of running the game, a file selector dialog was used to navigate to a different directory. (On re-running, Workbench displayed an error dialog saying "VM Error: image file not found.") This is now fixed.
    • The Windows HTML interpreter crashed on startup if the Game Chest file, as selected in the interpreter options, didn't exist. This crash has been fixed.
    • In the Windows HTML interpreter, if you change the Game Chest file setting in the options dialog, and the new file you select doesn't already exist, the interpreter now automatically saves the current Game Chest contents to the new file. In the past, you had to copy your old file manually. If the new file you select does already exist, the interpreter will simply load the new file rather than overwrite it.

    Interim build 2.5.8/3.0.6h: 6/7/2003

    • The Windows HTML interpreter's user interface for setting display options (such as fonts and colors) has been slightly reorganized to make it more intuitive and easier to navigate. Some of the changes:
      • The term "Theme" is now used instead of "Profile"; this is more consistent with the prevailing Windows terminology.
      • The visual settings - fonts, colors, "MORE" style, media type enabling - have been separated into their own dialog, which can be accessed directly with the "Customize Theme" command on the new "Themes" menu, as well as from a toolbar button. This dialog is now called "Customize Theme," to make it more obvious that all of these visual settings are part of the current theme.
      • The main options dialog no longer has the tabs for visual settings, but it has a new tab, "Appearance." This tab lets you select the current visual theme, create new themes, and delete themes. This also has a button to open the visual settings dialog to customize the currently selected theme, and a button to reset the current theme to its "factory defaults" (but this button only works for the standard pre-defined themes, since only the built-in themes have defaults). The old dialog tab arrangement mixed the settings that are part of the visual theme and those that are simply global option settings; the new arrangement keeps all of the visual theme settings together in a separate dialog.
      • A new "Themes" menu has been added, and the theme-related items that were previously in the "View" menu have been moved to the new menu instead. This change is intended to make it more obvious which items are theme-related by grouping them on a single menu.
      • Several of the items formerly on the "View" menu (graphics, sound, music, link display mode) have been removed from the menu and added instead to the new "Media" page of the "Customize Theme" dialog. These options are all part of the theme; moving them to the dialog makes this much more obvious in the user interface. Most users hardly ever change these settings, so it seems unlikely that the extra step needed to open the dialog will noticeably inconvenience anyone.
      • The Windows HTML interpreter no longer uses the F5 key to toggle the hyperlink display mode. Some users found this keyboard shortcut to be too convenient, in it was too easy to hit the key unintentionally, resulting in an unexpected change in the link display mode; this was especially a problem for Workbench users, since the Workbench debugger uses F5 as the "go" button.
      • A new "Mute Sound" command (on the View menu and on the toolbar) lets you turn off all sound output. This doesn't actually disable playback of audio media types; it merely mutes the speakers temporarily. Playback or music and sound effects proceeds while muting is on, but with the volume control effectively turned all the way down. (In contrast, turning off sound or music on the "Media" tab in the Customize Theme dialog makes the interpreter behave as though it simply doesn't support the disabled media types. This is visible to games when they query the interpreter capabilities. Muting is not visible to games.)
    • In the Windows interpreter, the list of recent games shown in the "File" menu now shows the title (instead of the filename) of each game that you've entered into your Game Chest favorites. Recent games that aren't in your Game Chest favorites list are still listed by filename.
    • A bug in Workbench for Windows caused the "build for release" process to fail if the debug game file in the build settings was specified as going in a folder other than the main project folder (the one containing the .t3m file). (Yes, you read that right: the location of the debug game file caused problems creating the release game file.) This has been corrected; the release build should work properly now for any valid output directory settings.
    • A bug in Workbench for Windows caused a spurious error code to be displayed in the release build process for the command that's included in the build sequence to delete the temporary directory created for the intermediate files created during the release build. The error code display was itself the error; the directory removal actually worked correctly, but Workbench showed an error anyway. The spurious error has been removed (but any actual errors that occur will still be displayed, of course).
    • On Windows, a bug in the preference system caused preference settings to be saved incorrectly in certain situations. If more than one HTML TADS interpreter was running simultaneously, of if both the normal interpreter and Workbench were running simultaneously, and the games weren't all using the same profile (for example, if one game was using the standard "Multimedia" profile, and another was using "Plain Text"), then it was possible for the font and color settings of one of the profiles to be incorrectly saved over the settings for the other profile. For example, it was possible to make the Multimedia profile take on the settings of the Plain Text profile. These settings are saved in the registry, so once the wrong settings were saved, they stayed wrong until the user manually changed them back. This problem has been corrected; multiple interpreter windows won't interfere with one another (or with Workbench) any longer.
    • When multiple HTML TADS interpreters are running simultaneously, if you make changes to the option settings in one interpreter window, the other running copies of the interpreter will automatically update with the same changes.
    • Workbench for Windows now automatically selects the default "Multimedia" preferences profile on startup. In the past, the debugger simply used whatever profile was active in the game most recently played.
    • A problem in the Windows HTML interpreter caused crashes under certain very rare conditions. In particular, if a game asked the interpreter (via the systemInfo() built-in function) if it supports any of the digital audio formats, and the user had previously closed the interpreter window (so the game was in the process of being terminated, but hadn't yet exited), the crash occurred. This has been corrected.
    • Due to a bug, text-grid windows weren't being redrawn immediately after being cleared. This has been corrected.
    • In the HTML interpreter, if a banner window was initially created with a size of zero, the interpreter showed the banner with an arbitrary small height (about 20 pixels) until the program explicitly set the window's size. New windows are now correctly created with zero height instead.
    • The Author's Kit uninstaller for Windows no longer deletes your global Workbench configuration file; this is a file called "htmltdb3.t3c", located in the main Author's Kit install directory. The uninstaller leaves this file intact, because it contains option settings for Workbench, such as the font settings for source files. If you're uninstalling in preparation for installing a new version, and you install the new version in the same directory as the previous version you're uninstalling, this change will ensure that your option settings are retained. However, if you're uninstalling the Author's Kit permanently, this change means that you'll have to manually delete the "htmltdb3.t3c" file and the Author's Kit directory.
    • A problem in the interpreter caused link underlines to fail to draw properly in rare cases. In particular, in some fonts, a link that appeared as the very last thing displayed sometimes was missing its underline, at least until the window was drawn again for some reason (due to resizing, for example). Links should now draw correctly all the time.
    • In the past, the HTML interpreter didn't display typographical quotes for <Q> tags (using regular ASCII "straight" quotes instead) under certain localization conditions. For example, the Windows interpreter didn't display typographical quotes on systems localized to use a character set other than Windows US/Western Europe (code page 1252). The interpreter is now more flexible; it should now display typographical quotes on a localized machine as long as the machine has the proper character sets installed. (For Windows machines, this means that typographical quotes will be displayed regardless of the default system character set, as long as a cp1252 version of the selected font is also installed.)
    • The Windows HTML interpreter now pays attention to the command script MORE prompt mode when playing back a command script (using the "@" syntax in TADS 2, or the new REPLAY command in TADS 3). In the past, the interpreter always showed MORE prompts, even when the game requested that the prompts be skipped.

    Interim build 2.5.8/3.0.6g: 4/12/2003

    • TADS 3 Workbench for Windows now lets you set the filename of the game file (.t3) compiled for debugging. In the past, this filename was always derived from the name of the project (.t3m) file, so it couldn't be set independently. The Build Settings dialog now allows you to change the filename. Note that this means that you are now free to use your project name for the release version of the game file, as long as you choose another name for the debug version. The New Project wizard takes advantage of this change: it sets the default debug game file to reside in a .\debug subfolder of the project folder (which the wizard creates for you automatically), and it sets the filename for the release build to use the same name as the project. (In the old scheme, since the project name was always reserved for the debug build, the release build had to have some other name, which by default used the project name plus a "_rls" suffix. Most people didn't want to actually distribute the released version with the "_rls" suffix on its name, so this convention created a manual file renaming step for most people when distributing their games. The new scheme saves you this trouble by letting you use any name you want for the release file. The only restriction is that the debug and release builds must have different names from one another.)
    • The Windows HTML interpreter now draws thick table borders with diagonal joins at the corners. (In the past, the corner joins were squared, which didn't look great with border thicknesses greater than one pixel.)
    • Workbench for Windows has a couple of new text editor interface parameters. First, you can now include the code "%c" to pass the target column number to your text editor. Second, the "%n" (line number) and "%c" (column number) parameters can be qualified with an inserted "0" (hence "%0n" and "%0c") to indicate that the line and/or column number should be passed to the editor using a 0 base - you should use this qualifier if your editor expects 0 for the first line (or column), 1 for the second, and so on. The Epsilon auto-configuration has been updated to take advantage of these new features.
    • The Windows HTML interpreter has a new command-line option, -noalphablend, that lets you explicitly turn off alpha-blending support for semi-transparent images. By default, alpha blending will be used when it's supported by the underlying platform (it's supported on Windows ME, 2000, and XP). It might rarely be desirable to turn it off despite system support, such as when the system hardware is too slow with alpha blending enabled, or (more likely) if the local implementation is buggy, as it appears to be in WineX. Attention WineX users: WineX reportedly crashes when alpha blending is enabled, apparently due to a bug in the WineX graphics code. If HTML TADS crashes when you start it up on WineX, try running with the -noalphablend option.
    • The Windows HTML interpreter handled the "Escape" key incorrectly when running some games. In particular, games that read individual keystrokes received two copies of each escape key press. This has been corrected.
    • The HTML interpreter did not correctly calculate the contribution of some of the typographical spaces ("&emsp;" and the like) to the width needs of a table. This sometimes caused unnecessary line wrapping in tables containing these characters. The renderer now calculates the table widths of the typographical spaces correctly.
    • The Windows HTML interpreters are a little smoother in drawing the window when the game has a pause for computation right after displaying some text that results in scrolling. In the past, the interpreter left a little bit of the old text at the bottom of the window, which created a slightly jumpy appearance. The interpreter now updates the exposed area more quickly after scrolling, reducing the apparent flashing.

    Interim build 2.5.8/3.0.6f: 3/23/2003
    This version has no changes related to HTML TADS.

    Interim build 2.5.8/3.0.6e: 3/16/2003

    • TADS 3 Workbench for Windows now allows only project makefiles (.t3m files) to be opened as projects; source files and executable (.t3) files are no longer openable as projects. This doesn't affect TADS 2 Workbench. (Opening source and executable files in TADS 3 Workbench was an historical carry-over from TADS 2, where there isn't a formal project file, but rather a "debugger configuration" file that Workbench maintains alongside a .gam file. In TADS 3, the project makefile is an important part of each project, regardless of whether the project was created with Workbench or with the command-line compiler or other tools. You can create project files manually with a text editor, or you can let Workbench create them for you using the New Project command on the File menu.)
    • Workbench for Windows now knows how to auto-configure the Imaginate program editor. If you have Imaginate installed on your system, Workbench will include it in the list of known editors, and will automatically set up the necessary command options to invoke Imaginate when you want to edit a source file. Open the Options dialog with the View/Options menu, select the Editor tab, and click the "Auto Config" button to bring up the list of known editors.
    • In the past, TADS 3 games sometimes caused the Windows HTML TADS interpreter to crash if the interpreter window was closed while the game was waiting at a "More" prompt. This has been fixed.

    Interim build 2.5.8/3.0.6d: 2/23/2003

    • Fixed a problem that prevented TADS 3 games from using the <ABOUTBOX> tag. The HTML parser was overly zealous in disallowing <BANNER> tags in TADS 3 games, and incorrectly applied this prohibition to <ABOUTBOX> tags as well. This has been corrected.
    • The HTML interpreter didn't always properly calculate the range (or sometimes even the existence) of the horizontal scrollbar when a <TABLE> exceeded the width of the window. Tables are now properly included in figuring the range of horizontal scrolling.
    • The Windows HTML interpreter uses a slightly different color scheme than it did in the past for displaying table borders. The top and left sides of each table border are now shown in the "button face" color (as set in the Windows system color scheme); in the past they were shown in the "highlight" color. This improves upon the old color scheme in that the highlight color is the same as the default system window background color in most Windows color schemes, so these portions of the table border were effectively invisible in many cases. This change makes borders visible with typical system settings.
    • The HTML renderer's table layout algorithm has been improved to make table layout more controllable. There are two main changes, both relating to the way the renderer decides how wide to make the columns in a table. First, when the renderer is choosing the overall width of a table, it tries to ensure that the table will be wide enough that any columns specified with percentage widths can be laid out with the requested percentage. Second, when a column's width is specified in pixels, the renderer tries to match the requested width as closely as possible; in the past, the renderer used width specifications as hints but didn't attempt to match the requests exactly. These changes should have minimal impact on existing games; the old behavior didn't provide much control over column widths, so it's unlikely that any games were able to achieve particular effects that would be affected by the changes. A more detailed description of the new layout algorithm can be found in the Table Layout Rules documentation.
    • In the Windows HTML interpreter, the keyboard event processing had a problem that prevented the end-of-file indication from being returned properly to TADS intrinsics in certain cases. This problem made it impossible in such cases for the game program to detect that the user intended to terminate the game by closing the main HTML TADS application window. This has been corrected.
    • The HTML interpreter incorrectly calculated the width of banner windows under certain circumstances. In particular, if a vertical (left or right) banner contained only a table, the full width of the table wasn't properly counted to determine the natural width of the banner. This has been corrected.
    • The interpreter had a problem that caused it to crash if an animated image (an MNG) was used as the background of a BODY tag. (The crash only occurred if the window was subsequently cleared.) This has been corrected.
    • A problem in the HTML parser caused character entities ("&" sequences) to be displayed incorrectly in rare cases. The problem had to do with buffering of text output at higher layers of the system, and only showed up if an entity happened to be split up due to a buffer flush. This was most likely to occur when displaying large amounts of text at once, such as in a room description. This has been fixed; entities should display properly regardless of any higher-level buffering.

    Interim build 2.5.8/3.0.6c: 2/1/2003

    • The Windows HTML interpreter now adds an appropriate default extension when the TADS 2 askfile() function or TADS 3 inputFile() function is used to select the name of a file to write. The default extension depends on the type of file that the program requests. (In the past, the interpreter didn't add a default extension except for saved-game files. The lack of an extension was inconsistent with the behavior of askfile/inputFile on searching for an existing file to open, since the dialog by default only shows files matching the appropriate extension for the file type.) This change is limited to the Windows interpreter; it doesn't affect any other platforms.
    • A bug in the HTML interpreter occasionally caused banners created with the <BANNER> tag to ignore an explicit WIDTH attribute. The behavior was unpredictable, and varied from session to session with the same game program. This has been corrected; the WIDTH attribute should now be reliably obeyed.
    • The Windows HTML interpreter no longer accepts the <:BANNER> tag when running a TADS 3 game. TADS 3 games must use the new banner API instead of using <BANNER>tags. (The banner API is more powerful than the <BANNER> tag, and works not only on HTML interpreters but on many text-only interpreters as well. The new API cannot interoperate with the original tag-based banner mechanism, though, so to avoid confusion, the tag-based banners simply are not allowed in TADS 3 programs. If a TADS 3 program attempts to use a <BANNER> tag, the interpreter will show a message in the debug message window, if there is one, and will otherwise ignore the banner and its contents.)
    • Fixed a problem that prevented multi-byte characters (primarily used in East Asian languages) from displaying under some circumstances. (This problem was introduced in the 12/15/2002 interim build, and has now been corrected.)

    Interim build 2.5.8/3.0.6b: 12/15/2002

    • The HTML renderer now provides the game with much better control over the way lines are broken during the text layout process. In the past, the formatter used simple word wrapping. By default, the line breaking process acts the same as it used to, but several new features let the game override the default behavior. These changes are described in detail in the Line Breaking chapter of the documentation, but here's a summary:
      • A new HTML tag is now supported: <WRAP> tag, which controls the line breaking method. <WRAP WORD> selects the default mode, word-wrapping mode, which breaks lines only at word boundaries (defined as spaces and hyphens). <WRAP CHAR> selects character-wrapping mode, which breaks lines between any two characters.
      • A new markup, the "zero-width non-breaking space," written as "&zwnbsp;", lets the game specify a point at which text cannot be broken, even if the line breaking rules would otherwise allow it.
      • A new markup, the "zero-width space," written as "&zwsp;", lets the game specify a point at which the formatter can insert a line break, even if the line breaking rules would otherwise forbid it.
      • The soft hyphen markup, "&shy;", is now recognized. This lets the game specify a point at which the formatter can optionally insert a line break by hyphenating a word. If the formatter does elect to break at a soft hyphen, the soft hyphen will be displayed as a regular hyphen; otherwise, a soft hyphen is invisible.
      • Several new "typographical space" markups give the game fine control over spacing.

    Interim build 2.5.8/3.0.6a: 11/23/2002

    • Whitespace between a <BANNER> tag and the first text within the banner's body is no longer significant. In the past, if any whitespace appeared between a <BANNER> tag and the first text in the body of the banner, the banner was displayed with a leading space. Leading whitespace is now ignored, so that a banner's body is treated as starting with a new paragraph. (If a game explicitly wants to show leading whitespace in a banner, it can use the &nbsp; sequence to add a non-breaking space, which the HTML renderer will display even where ordinary whitespace is suppressed.)
    • A problem in the Windows HTML interpreter caused the main window's size and placement to be forgotten when the interpreter application was minimized and then closed from the task bar. The interpreter now correctly remembers its size as it was before being minimized.
    • A problem in the Windows interpreter caused music playback to stop whenever the interpreter discarded the oldest text in the game window. (The interpreter periodically discards the oldest text to save memory, according to the "Text Memory Limit" setting in the options dialog.) This is now fixed.
    • A problem in the Windows interpreter caused the "Profile" list (in the Profiles submenu of the Edit menu, and in the drop-down menu in the profile button on the toolbar) to be shown with no active profile selected in some cases. If the game had a Game Chest entry, and the entry's profile name setting was left completely empty, no profile was selected in the profile menu. This is now fixed; games with empty Game Chest profile settings will use the "Multimedia" profile by default, the same as a game whose Game Chest profile setting refers to any other non-existent profile name.
    • In the Windows interpreter, it is now less visually intrusive when old text is discarded to save memory. In past versions, it was obvious that the window was being refreshed at these times. The interpreter is now a little better at hiding these updates, so they should usually be less noticeable now. When discarding old text, the interpreter must recalculate the layout of all of the remaining text in the window, to account for any change in paragraph flow or other layout features that result from discarding the old text. In most cases, the new layout matches the old layout for all of the text visible in the window, since the window usually shows the most recent text, which is the least likely to be affected by discarding the oldest text; the interpreter tries to take advantage of this to make the layout recalculation invisible.
    • Fixed a problem that caused output to be hidden when the game opened a <TABLE> tag but never got around to closing it before reading a command line. This sort of thing isn't legal, but the interpreter didn't recover very gracefully from such input. The interpreter now automatically closes any <TABLE> tags left open when starting a new command line, ensuring that the HTML formatter won't get stuck waiting for a </TABLE> that the game accidentally omits.

    Changes in version 2.5.7

    Released September 22, 2002
    • The Windows interpreter now keeps the display option settings (fonts, colors, and so on) in a "Profile," which is simply a named group of saved settings. Profiles let you switch all of your display settings at once, so they're especially convenient if you like to use one display style with some games, and a different display style with other games. Here's how profiles work:
      • At any given time, there's a current profile; you can change the current profile using the "Profiles" command on the "Edit" menu (there's also a toolbar button that does the same thing). Whenever you make changes to your display settings with the Options dialog, the settings are saved with the current profile.
      • Initially, the system automatically creates two profiles for you: Multimedia, and Plain Text. The system sets up these profiles with default settings, but you can customize the settings with the Options dialog any way you like.
      • You can also create new profiles of your own, using the "Manage Profiles" command (part of the "Profiles" submenu of the "Edit" menu, and also a toolbar button). Whenever you create a new profile, its initial settings will be copied from the current profile.
      • The interpreter remembers which profile was last used with each game, and automatically switches to the same profile each time you play the game. For games you've added to your Game Chest, you can change the profile setting for each game with the "Edit" link displayed next to the game's listing on the Game Chest page. The interpreter also remembers which profile was last used with the Game Chest page itself.
      • When you load a game for the first time, the interpreter checks to see if the game includes GameInfo metadata with a "PresentationProfile" setting. If the game does include that setting, the interpreter uses the setting to select an initial profile for the game, if there's a profile with the same name. This gives the game's author a way to recommend a display style for the game.
    • The Windows HTML interpreter now allows you to set the size of each font individually in the Preferences dialog. Sometimes, two different fonts don't look quite right together when displayed at the same nominal point size; this new feature lets you adjust the individual point sizes of the fonts you're using to give a uniform visual size. On the Fonts tab of the dialog, each font now has a drop-down list that lets you specify the point size for the font. Note that this sets the "base size" for the font; whenever the game selects larger and smaller sizes via its formatting commands, the sizes are relative to this base size. In addition, if you set the "Text Size" item on the "View" menu in the interpreter to a size other than "Medium," all of the fonts will be scaled up or down from the base size you set in the preferences. Whenever the game displays a font by name, rather than using one of the fonts you select in the Preferences dialog, the interpreter will use the point size of the Main Game Font as the base size of the named font.
    • The Windows HTML interpreter has a new page of Game Chest options in the Preferences dialog.
      • You can now select the location of the Game Chest database file, GameChest.txt, which stores your list of game and web site links. The file's default location is the "My Documents" folder (in a "TADS" subfolder), but you can use this new option to set a custom location. Note that if you change this option, you'll have to manually move the GameChest.txt file to the new location you choose, if you want to use the original copy; if you don't, Game Chest will create an empty new GameChest.txt file for you. Also, you'll have to change this setting again if you re-install the Player Kit in the future.
      • You can select a custom picture (using a PNG or JPEG image file) for the background image displayed on the Game Chest page, or you can choose to use no image at all.
    • The new BODY tag attribute HLINK allows you to specify the color of hyperlinked text for "hovering," which means that the mouse cursor is positioned over the hyperlink but the mouse button isn't being held down. Note that not all systems use a special appearance for hovering over a hyperlink, and even on systems that do, the special appearance may be controllable through user preferences (it is on Windows, for example); this attribute won't have any effect in these cases.
    • The Windows HTML interpreter no longer uses full alpha blending when running on Windows 98; only simple transparency is now used on Win98. This change was made because the alpha blending support built in to the operating system has some serious flaws in Windows 98; these OS bugs made it impossible for the TADS renderer to work reliably. This change does not affect any other Windows platforms: full alpha blending is still supported on Windows ME, 2000, or XP. (Thanks to Lukas Fichtinger for point out this problem.)
    • A bug in Workbench for Windows caused Workbench to crash occasionally after performing build command steps that produced no output. This has bee corrected.
    • A bug in Workbench for Windows caused sporadic crashes when making changes to source window formatting settings from in the Options dialog. This should no longer occur.
    • Due to a bug in the combined TADS 2/3 Windows interpreter, double-clicking a saved game file from a Windows Explorer desktop window did not correctly start and restore the saved game. This is now fixed.
    • A bug in the Windows interpreter caused the game to stop responding in some cases where the game used the morePrompt() function to wait for the user to acknowledge a display. This has been corrected.
    • Due to a bug, the Windows HTML interpreter's Game Chest was unable to run a game listed in the "favorites" list when the game's directory path contained an apostrophe ("'") character. This has been fixed; Game Chest can launch a game from any valid path now.
    • The Windows HTML interpreter's Game Chest showed some accented characters incorrectly when the characters were taken from GameInfo metadata embedded in TADS 2 games, and the GameInfo files were prepared using UTF-8 enabled text editors. This has been corrected. (Note that you might need to remove and re-add Game chest entries for any games with accented characters shown incorrectly, because the Game Chest will have already stored in the wrong accented characters in its configuration file. If you remove and re-add the entries, Game Chest will interpret the characters correctly when it stores the new entries.)
    • The Windows HTML interpreter's Game Chest froze up when attempting to read GameInfo data stored in certain invalid formats (this showed up in particular when the GameInfo file was stored in the UCS-2 Unicode encoding). This has been corrected; the interpreter now tolerates invalid formats and shows a warning dialog when it detects one. (Thanks to Lukas Fichtinger for catching this.)
    • Several of the third-party libraries that the Windows interpreter uses have released updates recently, so the interpreter has been updated to use the latest releases, as of this writing: MNG is now supported through mnglib 1.0.4 and zlib 1.1.4; and Ogg Vorbis playback now uses libvorbis 1.0.
    • The HTML parser is now more tolerant of text written piecewise. In the past, if the game wrote out a partially-completed tag and then flushed the output stream or forced a redraw, the parser saw only the incomplete tag, and flagged an error. The parser now defers parsing an incomplete tag, even after an explicit buffer flush, saving the partial tag for later completion. The parser has a limit of 4k bytes for partially-flushed tags; if the source text of an incomplete tag exceeds this limit, the parser will assume the tag will never be completed and issue an error. This change will be mostly invisible to games; the only noticeable difference should be that the parser will gracefully handle unusual cases where a game flushes the output buffer explicitly in the midst of writing a tag. (Note this type of situation doesn't arise very often in normal game execution, but it can easily occur when stepping through a game with the debugger, since the debugger can be configured to flush output on every pause.)
    • The Preferences dialog enabled and disabled the Apply button on the font selection page incorrectly in some cases. This is now fixed.
    • A bug in the HTML renderer caused spurious "MORE" prompts in some cases when the game displayed a table with lots of cells. This has been fixed.

    Changes in version 2.5.6

    Released June 1, 2002
    • The new ASRC and HSRC attributes of the <IMG> tag provide a way of implementing simple roll-over buttons. Refer to Roll-over Images in the documentation for details.
    • Fonts can now specify a BGCOLOR (background color) attribute in addition to the COLOR attribute. When a font specifies a background color, the area behind any text displayed with the font is filled with the background color. The area filled with the background color is dependent on the font, but is a rectangle that is usually slightly taller than the tallest character in the font, and just wide enough to hold the displayed characters.
    • The Windows HTML interpreter now supports Ogg Vorbis compressed audio in <SOUND> tags. Ogg Vorbis is an open-source audio format similar to MPEG audio but said to have superior compressions and fidelity. The Windows interpreter uses the Xiphophorus reference implementation, version 1.0rc3, for decoding. Ogg Vorbis files are identified by resource names that end with the suffix ".ogg". An Ogg Vorbis resource can be played in any sound layer. Refer to vorbis.com and xiph.org for information on the Ogg Vorbis format, and for links encoders and other Ogg Vorbis-enabled software.
    • The Windows HTML interpreter now supports MNG (Multiple-image Network Graphics), on an experimental basis. MNG is an animated image format related to PNG, and, like PNG, is an open standard unencumbered by patents; MNG is said to be superior to animated GIF's in a number of ways. (HTML TADS doesn't support GIF images, static or animated, because of patent restrictions applying to the GIF format.) MNG is supported by several popular applications, including Jasc's Animation Shop (part of Paint Shop Pro). The MNG web site has information on the standard, including links to applications that can be used to create and manipulate MNG images. In HTML TADS, you can use an MNG file as the SRC of an <IMG> tag, an <HR> tag, or an <LI> tag. Note that MNG images can NOT be used as the main body background; only static images (PNG's and JPEG's) can be used as the body background.
    • The new system information code __SYSINFO_OGG allows a game to sense whether or not the interpreter supports the Ogg Vorbis audio format.
    • The new system information codes __SYSINFO_MNG, __SYSINFO_MNG_TRANS, and __SYSINFO_MNG_ALPHA sense whether or not the interpreter supports the MNG image format, transparency masks in MNG images, and "alpha channel" partial transparency in MNG images, respectively. In most cases, MNG transparency and alpha support will mirror PNG support for the same features.
    • The Windows HTML interpreter now supports full alpha-channel (partial transparency) rendering of PNG images on Windows 98, ME, 2000, and XP. Alpha rendering is automatic. This functionality is not available when running on Windows 95 or NT 4, because these versions of Windows do not provide support for alpha rendering in their native graphics subsystems; on Windows 95 and NT 4, images with alpha channels are rendered against a fixed background (either the default background specified in the PNG itself, or a default chosen by the interpreter if the PNG doesn't specify one) as in past versions. For PNG's with paletted alpha, on 95/NT 4, the paletted alpha is treated as simple transparency. Note: the system information code __SYSINFO_PNG_ALPHA (introduced in version 2.5.4) indicates whether or not the system supports full PNG alpha channel rendering; this allows a game to include two copies of a graphic (usually, one optimized for full alpha and the other optimized for simple transparency), displaying one or the other according to the capabilities of the interpreter.
    • The Windows HTML interpreter now includes an "Exit" command on the "File" menu. This is equivalent to closing the interpreter window. Similarly, the former "Quit" command in Workbench has been renamed to "Exit" for conformance with standard Windows user interface conventions.
    • The Windows HTML interpreter features the "Game Chest" start page. The interpreter shows the Game Chest page when it first starts, and also offers a link to the Game Chest page whenever a game ends. The Game Chest page gives you a customized list of your favorite games; you can start a game on your list by clicking its title, saving you the trouble of finding the game file on your hard disk. The Game Chest page is easy to use and self-explanatory.
    • The Windows HTML interpreter has a new "Find Text" command, which lets you search for text in the game window. This makes it easy to locate a passage that you remember seeing earlier and want to read again. To begin a search, select the menu item "Find text on current page" from the "Edit" menu; this will bring up a dialog box. Type the text you want to find and click the "Find Next" button in the dialog (or just press Enter). The interpreter will look for the text, and then highlight the first occurrence it finds. Select the "Find Next" item from the "Edit" menu to find the next occurrence of the same text, or just press the F3 function key.
    • TADS Workbench for Windows has a new feature that lets you go to a selected line number in the active source window. To use the command, select the "Go to line number" item from the "Edit" menu, or type Ctrl+G. This will show a dialog asking for a line number; type the line number and click "OK" (or press Return). The cursor will be positioned on the selected line number in the active source window.
    • In Workbench for Windows, the debugger log message window is now a "dockable" window. As with the other dockable windows, you can switch the window between document-style ("MDI") mode and docking mode by right-clicking on the window's title bar and selecting the new mode from the pop-up menu.
    • The Windows HTML interpreter now includes a toolbar, for fast access to frequently-used commands. The toolbar is optional; if you want to hide it, un-check the "Show Toolbar" menu item on the "View" menu.
    • The keyboard accelerators in the Windows interpreter have been changed slightly to better match the usual Windows conventions. "Restore game" is now on Ctrl+R, and "Open new game" is on Ctrl+O.
    • The Windows HTML interpreter now continuously displays the elapsed time since loading the current game at the right side of the status line. The timer shows elapsed time for the current session only, so if you save a game, turn off your computer, then come back to the game later, the timer will be reset when you start your new session. If you find the timer distracting, you can hide it by un-checking the "Show Timer" menu item on the "View" menu. You can also choose whether to display hours, minutes, and seconds, or just hours and minutes: right-click on the status bar, and select your desired time display format from the pop-up menu.
    • The Windows HTML interpreter now allows keyboard entry of character code 255. (This primarily affects users on machines localized outside of the US and Western Europe, since the Windows Western European character set doesn't use character code 255 for anything. In past versions, the interpreter incorrectly failed to allow keyboard entry of this particular character code.)
    • In the Windows interpreter, the parameterized font named "TADS-Input" selects the text's color, bold, and italics attributes as well as the typeface, according to the settings that the player makes in the Preferences dialog. In the past, it was not possible to override the additional attributes, even with explicit <font> tags nested within the tag that selected the "TADS-Input" face. This has been changed so that <font color=xxx>, <i>, and <b> tags nested within a <font face='TADS-Input'> tag are obeyed. (Note, however, that any color attribute specified in the same <font> tag as a face='TADS-Input' attribute is still ignored: the preference color associated with the "TADS-Input" font still overrides any other color setting made in the same tag. This isn't necessarily the most intuitive behavior, but think of it as an arbitrary choice to resolve a conflict: since "face=TADS-Input" and "color=xxx" attributes in a <font> tag both select a text color, one of them has to be ignored.)
    • The Windows interpreter now uses the TITLE attribute of an <A> tag as the "tip" text for the link. That is, if the mouse cursor hovers over a link for a few moments, the interpreter displays a small pop-up window showing the link's TITLE attribute text.
    • Hyperlinks can now be displayed with explicit font colors. In the past, a <FONT COLOR=xxx> tag within an <A HREF> tag was ignored, and the hyperlinked text always displayed in the system hyperlink text color despite the explicit color setting of the interior tag. The renderer now obeys the color setting of a <FONT> tag enclosed within an <A HREF> tag, overriding the default system color settings. Note that when the color is explicitly overridden in this fashion, the explicitly specified color is used for all states of the linked text, so the link doesn't change its visual appearance in response to the mouse position as it does with the default colors.
    • The following parameterized color names have been added:
      • link: hyperlink text
      • alink: "active" hyperlink text (on Windows, this is the color of a link while the mouse button is being held down with the mouse over the link; the meaning may vary on other systems)
      • text: the default text color
      • bgcolor: the default background color
    • The Windows HTML interpreter now waits to attach to the DirectSound system (a part of Windows that handles sound playback) until a game first requires sound playback, rather than at interpreter start-up as it did in the past. This means that the interpreter won't attempt to use the DirectSound subsystem at all when the game or games played during an interpreter session don't use any sound effects. When an application connects to the DirectSound system, other applications are sometimes unable to use DirectSound simultaneously; although HTML TADS uses a "cooperative" sharing level for its DirectSound use, some other applications require exclusive access, and so cannot use DirectSound at the same time as HTML TADS. By waiting to attach to DirectSound until a game actually needs sound playback, the interpreter avoids unnecessarily locking out other, less cooperative applications.
    • A bug in the Windows interpreter caused the interpreter to crash if a game attempted to display a JPEG image encoded in gray-scale (monochrome) format. This has been corrected.
    • The Windows interpreter now shows more immediate visual feedback (by scrolling the window down a line) after a command is entered if the command takes a perceptible amount of time to process. (This is a purely cosmetic change. In the past, the interpreter didn't show any visual change until a command was finished; this was harmless, but looked a little weird for commands that took more than a few tenths of a second to complete.)
    • The scrollbars in the main window in the Windows interpreter now track the mouse a little differently: when you click and drag the scrollbar's "thumb" to change the scroll position, the scrollbar will continue tracking the mouse as long as you hold down the mouse button, no matter how far you move the cursor away from the scrollbar. In the past, as is typical for Windows scrollbars, the thumb snapped back to its original position whenever the mouse strayed more than about the width of a scrollbar away from the scrollbar; some people (your present correspondent included) found this extremely irritating, since it's hard to keep the mouse lined up with the narrow little scrollbar area when moving the thumb any substantial distance.
    • The Windows interpreter (and Workbench) now provide "native" mouse wheel support. In the past, it was necessary to use the mouse manufacturer's extra driver software for the scroll wheel to work in HTML TADS, but this was a problem for users who don't use the extra driver software; many people must disable the extra driver software because of incompatibilities with other applications. The new native mouse wheel support means the mouse wheel will work properly on any Windows 98/Me/NT 4/2000/XP system, even if the extra driver software is disabled. (Windows 95 and NT 3.1 users must still use the extra driver software, because those versions of Windows do not have native mouse wheel support.)
    • Workbench for Windows has a new "auto configuration" dialog for text editors. When you're configuring a text editor (in the "Editor" panel of the main options dialog, reachable from the "Options" item on the "View" menu), you can click on the new "Auto Config" button to bring up the automatic configuration dialog. The new dialog lets you choose an editor from a list of pre-configured editors, plus any editors that you have installed that have registered Windows Explorer file associations for files of type ".c", ".h", ".cpp", or ".txt". If your preferred editor is included in the list, all you have to do is select your editor from the list and click the OK button; the appropriate settings will be entered automatically into the editor panel. If your favorite editor isn't on the list, please contact TADS's author to request adding your editor to the list in a future version. Sadly, text editor vendors have not yet been lining up with large cash offers for promotional consideration in the auto-config list; until they do, I'll add any editor to the list upon receipt of the relevant configuration parameters. Note: if you select a pre-configured editor (as opposed to one deduced from file associations), you might need to go the "Advanced" dialog after selecting your editor from the list to set the full path to the editor's executable file.
    • The mechanism that searches for source files in the Windows debugger has been improved slightly to eliminate a problem that occurred in some cases where a header or source file was not in the working directory at compile time. In some such cases, the debugger managed to open the source file but did not properly show breakpoint and execution pointer icons. This has been corrected.
    • The Windows TADS 3 Workbench debugger now automatically stops when the interpreter is about to throw a run-time error, allowing you to see the location in the code where the error occurs, and possibly to intervene manually. If you change the execution point and then step or go, the interpreter will not throw the error; if you leave the execution point unchanged, the interpreter will proceed to throw the run-time error as normal. In some cases, you may be able to correct the conditions that caused the error (by changing the values of variables, for example); in these cases, you might wish to avoid throwing the error by moving the execution point back a line or two in the source and retrying the operation.
    • The Windows TADS 3 Workbench allows files in the Project window to be reordered, simply by clicking on an item with the mouse and dragging it to its new location. Files can only be reordered within a folder; a file cannot be dragged to a different folder.
    • Workbench for Windows now provides the "Find" command within the Debug Log window, to search for text within the debug log text.
    • The HTML renderer now flows text around tables with ALIGN=LEFT or ALIGN=RIGHT settings. This lets a table "float" in a margin in the same way that images can, which can be useful for a variety of text effects. Note: the renderer's treatment of such tables deviates slightly from the HTML standard, in that a left- or right-aligned table is effectively treated as an in-line item rather than a block item, so no paragraph break is visually displayed at the point where the table appears in the text. For strictly compliant HTML treatment, one can place a <P> tag immediately before and immediately after a table with left or right alignment. Tables with no explicit alignment and tables that specify ALIGN=CENTER are not affected by this change.
    • The HTML interpreter did not correctly display PNG images that use a gray-scale format (one of the several PNG color storage formats); gray-scale images were displayed with random colors that changed unpredictably during redraws. This has been corrected; gray-scale images are now displayed correctly.
    • The Windows HTML interpreter now uses libpng 1.2.2 (the latest version, as of this writing, of the reference open-source PNG library) for handling PNG files. This expands the range of PNG sub-formats supported on Windows.
    • The Windows HTML interpreter is now faster at queuing digitized sound effects (the WAV, MPEG, and Ogg Vorbis formats). The interpreter's latency sensing the end of playback for digitized sounds has been reduced substantially, so loops and queues now proceed with a much smaller delay between adjacent effects.
    • A bug in the HTML renderer caused list items (specified with the <LI> tag) to be indented improperly. In the past, the bullet or number of each list item was shown at the same indent level as the rest of the list text; instead, the bullet or number should be displayed in the left margin of the first line, with the text of each line left-aligned to the right of the bullet or number. This has been corrected.
    • A bug in the HTML renderer caused unpredictable results when switching out of HTML mode (using "\H-") immediately following certain types of closing tags. This problem only appeared when an "\H-" sequence appeared immediately after an affected close tag with no other intervening text or tags. This is now fixed.
    • A bug in the Windows HTML interpreter caused some font preference settings to change by themselves when brining up the Preferences dialog. In particular, if the originally selected font was the first font in the popup list for a given font type, the font switched upon bringing up the dialog to the second item in the list. This problem no longer occurs.
    • A bug in the HTML renderer caused incorrect displays in certain cases where a <TABLE> tag was nested within a <BLOCKQUOTE> tag, and no text or markups came between the closing </TABLE> tag and the closing </BLOCKQUOTE> of the enclosing structure. Typically, some of the text immediately following the </BLOCKQUOTE> was not displayed at all or was displayed with incorrect margins. This has been corrected.
    • The Windows installer now automatically looks up the local "Program Files" path and uses it to select the default installation directory. This applies to the system installer as well as to game installers you create using Workbench. When you're creating an installer for your game, you should use only a relative path for your "Default Program Directory" setting (in the Installer Options subdialog of the Build Settings dialog); the installer will automatically insert the correct prefix for the local computer at installation time. For example, rather than typing "C:\Program Files\My Game" into the Default Program Directory text box, you should type simply "My Game"; the installer will expand this to "C:\Program Files\My Game" or "C:\Programme\My Game" or whatever is appropriate on the local system at install time.
    • A couple of bugs in the HTML renderer caused crashes in some cases when <BANNER> tags were not closed before reading text input from the keyboard. The effects of unclosed <BANNER> tags varied according to the banner's contents, but several common types of contents, including <FONT> and <TABLE> tags, sometimes caused interpreter crashes. The renderer should now be tolerant of unclosed <BANNER> tags (at the very least, it should no longer crash when confronted with such things).
    • The <CAPTION> sub-tag of a <TABLE> is now implicitly closed by a <TR>, <TD>, or </TABLE> tag. In the past, the HTML parser complained if a <CAPTION> was not closed with an explicit matching </CAPTION>; the parser now implictly closes a <CAPTION> at the end of a table or at the start of any other table structure sub-tag.
    • Tables that had explicit WIDTH settings but contained no data are now displayed at the given width. In the past, a table without any contents was shown with zero width, even if an explicit WIDTH attribute was given.
    • On Windows, the HTML interpreter did not always correctly choose fonts on systems localized for locales other than Western Europe/US. In the preferences dialog, the font selector pop-up controls sometimes showed the same font several times (possibly with suffixes on the name) when several different character set versions of the same font were installed on the system. Furthermore, the interpreter sometimes showed characters from the incorrect character set when displaying HTML entities. This has been corrected; the list of fonts should now show only one instance of each typeface name, even when several different character sets of the same typeface are installed, and the interpreter should show the correct characters for all HTML entities that can be displayed.
    • Due to a bug in HTML TADS, if the player terminated a game by closing the interpreter window while the game was specifically pausing for some amount of time (for example, to allow a sound to finish playing), the interpreter did not immediately exit, but continued waiting for the interval specified by the game. In the case of long delays, this could leave the interpreter process running for some time after it was ostensibly terminated. This was mostly harmless, but unnecessarily consumed system resources. This has been corrected.

    Changes in version 2.5.5

    • Fixed a problem in the Windows HTML interpreter that eventually stopped MIDI sounds that were intended to loop infinitely. (The problem was that the MIDI player was unnecessarily re-opening the MIDI file after each repetition; this eventually consumed all available file handles, at which point the looping stopped because the MIDI player couldn't open the file again.) This has been corrected.
    • Fixed a problem in the Windows HTML interpreter that prevented playback of very short MP3 audio files. In the past, MP3 files under about half a second in total duration did not play back. This has been corrected.
    • Fixed a problem in the HTML interpreter that sometimes caused crashes when certain font-related tags were not properly balanced (i.e., begin and end tags were not matched up correctly in the HTML). While these sorts of sequences are still invalid HTML and hence might still confuse the renderer and result in strange displays, they should no longer cause crashes.

    Changes in version 2.5.4

    • The Windows HTML interpreter now supports transparent PNG images. Transparent PNG images can be used to show non-rectangular icons and to superimpose images over backgrounds. Check your PNG creation tools for information on how to create images with transparency information. (Note that only simple transparency is supported; "alpha blending," which allows images to be partially transparent, is not supported. Images with alpha information are treated as having simple transparency: each pixel that is partially but not completely transparent is treated as completely opaque.) Note that a new systemInfo flag, SYSINFO_PNG_TRANS, has been added to test for PNG transparency support; systemInfo(SYSINFO_PNG_TRANS) will now return true under the Windows HTML interpreter.
    • In TADS Workbench on Windows, any sounds still playing when you terminate the running game are canceled. (In the past, sounds continued to play even after terminating a game, until either the sounds finished playing by themselves or you restarted the game.)
    • On Windows, a bug prevented playback of MIDI files containing certain sequences. This has been corrected (The exact conditions that caused the problem are complicated, but the bug involved long MIDI messages occurring at certain boundaries within the file.)
    • On Windows, a bug in MIDI playback caused a crash if playback failed due to an error within the MIDI file. This has been corrected.
    • On Windows, if MIDI playback was cancelled in the middle of certain types of operations (such as pitch bends), a new MIDI playback started after the cancellation sometimes "inherited" the channel settings of the interrupted file, resulting in incorrect playback. This has been corrected: all MIDI channels are now reset to default conditions at the start of each new playback.
    • For users running certain early versions of Windows 95, Workbench crashed when the "Settings..." item was selected from the "Build" menu, due to an incompatibility with a Windows system DLL. This problem apparently only occurred for Windows 95 version 4.00.1111; the problem has been corrected.

    Changes in version 2.5.3

    • The HTML interpreter's table layout algorithm has been improved slightly in the way it calculates the layout for rows within tables with explicit heights. This will be particularly noticeable in tables with HEIGHT=100%. In the past, the table layout system apportioned the extra vertical space in tables with specified heights according to the total height of the table; it now counts only the interior heights of the cells, which results in much more accurate layout when the table is intended to stretch to the limits of the window height.
    • The HTML interpreter for Windows now reformats the text on any change to the window size, vertical or horizontal. In the past, as an optimization to avoid excessive redrawing, the system only reformatted on a change to the horizontal size of the window. However, it is possible for formatting to depend on the vertical size, too; tables whose heights are set to percentages of the window height, for example, must be reformatted when the window is vertically resized. So, this optimization has been removed; changes to either the width or height of the main window will now result in reformatting.
    • When you build a stand-alone game with the HTML interpreter for Windows, the stand-alone game executable will no longer display the recent games section of the "File" menu, nor will it offer the "load new game" command. These menu items were not appropriate for stand-alone games, so they've been removed. This change doesn't affect the HTML interpreter during normal use - only when used to build a stand-alone game executable.
    • The game installer has a new option, "readme", that lets you specify a file to display to the user immediately after completing an installation. Specify an HTML or text file in this field, and the installer will open this file after the install completes successfully and the user dismisses the installation "wizard" dialog.
    • The HTML interpreter did not properly update the display in some situations where a game used repeated updates to animate a sequence of graphics. This problem affected the title sequence of Neil K. Guy's Six Stories, for example. This has been corrected.
    • The HTML interpreter for Windows crashed if you closed the application while a sound was still playing. This has been corrected.

    Changes in version 2.5.2

    • The Windows interpreter now allows you to use certain web addresses in <A HREF> links. The interpreter opens these links using the user's normal web browser, so the user must have a Windows web browser (such as Microsoft Internet Explorer, Netscape Navigator, or Netscape Communicator) installed and configured as the default browser in order to follow these links. Any properly configured Windows web browser should work. If a web browser is installed, the Windows interpreter will use it to show any link whose HREF starts with one of the following prefixes:
      • http:
      • ftp:
      • news:
      • mailto:
      • telnet:
      Note that you can use the systemInfo() function to check the interpreter to determine if these link types are supported, using the new __SYSINFO_LINKS_HTTP, __SYSINFO_LINKS_FTP, and related capability codes. Refer to the TADS 2.5.2 release notes for details.
    • The Windows HTML TADS Interpreter now keeps a list of recent games under the "File" menu. The four most recently-played games will be displayed under the file menu; to load one of these games, just select it from the menu. Similarly, the Windows Debugger keeps a list of recent game projects, under the "Recent Games" submenu of the "File" menu (the debugger uses a submenu to allow for the future addition of a "Recent Files" submenu listing recently-opened source files).
    • Workbench will now work properly with relative directory paths in all build settings. In the past, it was usually best to make these paths absolute (i.e., including a full path specification with drive letter prefix), because Workbench's working directory was always the same as where the Workbench executable was stored. This is no longer the case; Workbench now sets the working directory to the directory containing the project (".tdc") file each time a game is loaded, so all of the paths in the build settings can specified relative to the project directory. This is especially helpful if you're moving a project file between different computers with different drive letters or directory layouts, because it allows a project's build settings to be fully self-contained within the project directory.
    • The Windows debugger now lets you control flushing output to the game window. In the past, the game window frequently did not show any output that had occurred since the last user input; this sometimes made it difficult to debug display code, because the immediate effects on the game window could not be seen while stepping through the code in the debugger. A new menu command, "Flush Game Window Output" on the "View" menu, lets you explicitly flush most pending output to the game window. In addition, another menu item, "Auto Flush Game Window" (also on the "View" menu), lets you control whether or not the debugger automatically flushes the game window on entry; if this menu item is checked, the debugger will automatically flush the game window's output each time you step through a line of code or encounter a breakpoint. Note that some types of HTML structures, such as tables and banners, will not always display anything until fully formed (that is, the entire structure, including the ending tag, has been written to the output), so you will not always see all pending text displayed even when explicitly flushing output.
    • The Setup program (for installing TADS itself, as well as for installing games using the installer created by MKSETUP or through the Workbench "Compile Installer" command) now accepts bacsklashes in the "Program Folder" setting that the user enters during the install process. In the past, Setup treated backslashes as invalid characters, and simply converted them to underscores. The installer now accepts backslashes as path separators, allowing the user to place the installed software in any submenu within the Start menu.
    • The Windows debugger now responds to the left and right arrow keys while the local variable and watch windows are active. In these expression list windows, the left arrow key closes the sub-list of the current item if it's open, otherwise moves to the parent item; the right arrow key opens the sub-list of the current item if it's closed, otherwise moves to the first child, if any, or the next item, if not. These keys provide a more convenient keyboard interface to the expression list windows, and is more consistent with Windows conventions for this type of window.
    • The Windows debugger now responds to function key F2 in an expression editor window by activating the currently selected expression or value for editing, for consistency with Windows conventions.
    • The Windows interpreter is now much less intrusive when it discards old text during a long game. In the past, whenever the interpreter discarded old text to save memory, the interpreter cleared the text window then re-drew all of the text after a short delay. Since this usually happens with a frequency of every thirty to fifty turns after playing for a while, the screen clearing could be quite annoying. Starting with this version, the interpreter no longer clears the screen while it's working. In most cases, this makes text disarding almost invisible; most of the time, you'll probably notice nothing more than a brief flash of the "Working" message in the status line.
    • The interpreter now shows the active game file in the window title. In the past, the interpreter's window title only changed when a game explicitly set it using the <TITLE> tag. The title now reflects the ".gam" file name if the game doesn't specify a <TITLE> tag. This is especially helpful if you're switching among different games in the course of a single session.
    • Fixed a bug in the <TAB> tag. When <TAB MULTIPLE> was specified, with no value specified or with a value of zero for the MULTIPLE attribute, the Windows interpreter crashed. This has been corrected; an unspecified MULTIPLE value or a MULTIPLE value of zero is now treated as through MULTIPLE were set to 1.
    • Fixed a bug in the clearscreen() function. In past versions, if the game called clearscreen() after displaying some text containing an HTML tag sequence near the end, and the text did not end with a newline of some kind (a "\n" or "\b" sequence), the interpreter occasionally would display part of the HTML tag as plain text. This should no longer occur.
    • Fixed a bug in the Windows interpreter that caused a crash under certain very obscure circumstances. In particular, if you activated another application while the interpreter was running, and brought up a dialog (such as a file selector dialog) so that the dialog was directly over the interpreter's main window, then did something to dismiss the dialog that involved a mouse double-click (for example, double-clicking on a file in the file selector dialog), the interpreter would sometimes crash. This has been corrected.
    • Fixed a problem in Workbench for Windows that sometimes caused compiler error messages in the Debug Log window to be split over two lines. When this happened with the line break in the middle of the filename portion of the message, double-clicking on the message to go to the error line in the source file didn't work: Workbench couldn't read the complete filename from the log window, because the filename was broken up over two lines. This has been corrected; each error message will now always appear on a single line.
    • A bug in the Windows interpreter prevented a stand-alone game executable from starting properly if the user set the preferences so that the interpreter didn't ask for a game at startup ("ask for a game to open on starting HTML TADS" on the "Starting" page of the preferences dialog). This has been corrected.

    Changes in version 2.5.1

    • The Windows HTML TADS Interpreter now uses a fixed palette (sometimes called a "rainbow" or "color cube" palette) when the video hardware is set to display 8-bit (256-color) graphics. In past versions, the Windows interpreter dynamically set the hardware palette according to the images that were displayed, allocating the 256 available palette colors according to the order in which the images were displayed; while this optimized color fidelity for some images, it sometimes gave poor results when multiple images with very different color schemes were displayed simultaneously. The new fixed palette is designed to give reasonably good results for a wide range of images; because the palette is fixed, the order in which images are displayed will no longer affect their appearance. Note that this change only affects 256-color display modes; when the video hardware is set to display 16- or 24-bit color ("65536 colors" or "True Color" in the Display Properties control panel), the interpreter does not use a palette at all, since it can display all of an image's colors directly.
    • On the Windows interpreter, keyboard focus now stays in the main input panel more consistently. In past versions, clicking in a banner moved focus to the banner, making it necessary to click back in the main input panel to enter a command. Input focus now moves back to the main panel after any click in a banner, except when you select a range of text in the banner, in which case focus stays in the banner until you explicitly click in the main text panel or clear the selection in the banner. (Focus stays in a banner with a selection so that you can use the Ctrl+C keyboard command to copy the selection.)
    • The Windows interpreter in the past acted as though the player had responded to a "MORE" prompt whenever the timeDelay() function was used to pause the game. This no longer occurs.
    • Fixed a problem in the interpreter that omitted the display of a game's closing message in certain cases. When the interpreter's preferences were set so that the interpreter continued running after quitting the game, any message that the game attempted to display after its last line of input was not visible (for example, a closing message after a QUIT command was not shown). This has been corrected; any closing message from the game is now properly displayed.
    • On the Windows interpreter, the systemInfo() built-in function incorrectly reported that MPEG Audio was not supported. This has been corrected.
    • On the Windows interpreter, the morePrompt() built-in function incorrectly displayed the text "[More]" in the main output window, rather than using the normal "More" prompt (as selected in the preferences). This has been corrected.
    • The interpreter showed a horizontal scrollbar unnecessarily in certain cases when a game displayed tables containing long lines of text, even when the lines were wrapped within the table cells. This has been corrected.
    • Fixed a bug in the interpreter that prevented <BANNER REMOVALL> sequences from working correctly under certain conditions. This has been corrected.
    • On the Windows interpreter, if you displayed a banner with its height set to 100%, the interpreter made the banner too tall, so that it covered the status line. This has been corrected.
    • On the Windows interpreter, the "More" prompt is no longer displayed if the main text panel is very tiny. This will improve the display behavior for games that use full-window banners to achieve special effects (especially animation effects) by suppressing the unnecessary "More" prompts that this type of effect generated in the past.
    • The mechanism that discards old text during long game sessions has been improved slightly to make the text discarding more consistent. In past versions, the system sometimes waited to discard any text until the total text size was far over the limit set in the preferences, and then discarded all or almost all of the old text. The interpreter should discard a much more reasonable amount of old text and do so closer to the time the text starts exceeding the preferences limit.
    • The Windows interpreter incorrectly discarded old text from time to time even when the memory preference setting was "No Limit." This has been corrected; all text is now retained with this setting.
    • The Windows interpreter now displays the "Working" message in the status line only during really long reformatting jobs. In the past, the "Working" message was displayed during any reformatting, so the status line flashed a lot during game play, which was often distracting. The "Working" message now only shows up after a couple of tenths of seconds have elapsed during reformatting, which prevents the message from flashing on and off during quick display operations.
    • The Windows interpreter had a bug that caused various problems if a new, non-HTML-aware game was loaded while an HTML-enabled game was previously running. This has been corrected.
    • Fixed a debugger bug that caused a spurious error message ("attempting to free a locked object"), and in some cases a crash, when the game used the delete statement to delete an object which had at some point earlier been "opened" in an expression window to show its property values. This will no longer occur.
    • Fixed a problem that made it impossible to terminate the game by closing the main game window when the game was stopped at a "more" prompt while running under the debugger. In the previous version, closing the game window at a "more" prompt made the debugger lock up and stop responding; this no longer occurs.
    • Fixed a bug in the debugger that caused Workbench to crash when a game running in HTML mode caused an HTML parsing error, and the source text near the error contained a "\)" sequence (to turn off highlighting, using the traditional non-HTML formatting codes). This has been corrected.
    • Fixed another debugger problem that caused a crash when breakpoints were set in a source file containing no executable code at all.

    Changes in version 2.5.0

    Released on July 10, 1999
    • Important programming note: The Windows debugger now requires that you compile your game with the new -ds2 option. Use this option instead of the traditional -ds option. (The only difference between -ds and -ds2 is that the -ds2 option instructs the compiler to generate a new style of debugging information in your game; the Windows version of the debugger, starting with version 2.5.0, requires this new style of information, and will not work with games compiled using the -ds option.)
    • Another important programming note: On Windows, the TADS Debugger is now called "TADS Workbench." I've changed the name because the program is now more than just a debugger: TADS Workbench is an integrated environment that lets you build your program as well as debug it. Refer to the TADS Workbench help page for details on the new features.
    • The Windows HTML TADS interpreter now lets you load a new game without exiting and restarting the interpreter. You can select the "Load Game" item from the "File" menu at any time to start a new game. Loading a new game immediately ends the current game, so the interpreter displays a confirmation dialog when you select this menu item if a game is already underway.
    • The Windows HTML TADS interpreter offers new option settings for the interpreter's action when first launched. The new "Starting" tab in the options dialog has these options:
      • Ask for a game to open on starting. If this box is checked, when you launch the interpreter, it immediately displays an "Open" dialog asking you to select a game file. If you uncheck this box, the interpreter will display the main game window, but take no further action on its own; to load a game, select "Load New Game" on the "File" menu.
      • Initial game folder. This text field lets you choose the folder on your hard disk where the interpreter start the first time it displays an "Open" dialog. If you usually store your TADS game files in a particular location on your hard disk, you can enter that folder here; whenever you start HTML TADS, the interpreter will automatically initialize the "Open" dialog to start in this location, saving you the trouble of navigating to the folder each time you run the interpreter.
    • The Windows HTML TADS interpreter now offers option settings for the interpreter's action after you quit the game (normally by entering a QUIT command at the game's command prompt). You can choose this option in the "Quitting" tab of the options dialog. The options are:
      • Wait for a keystroke, then exit. This is the traditional action: after the game ends, the interpreter waits for you to press a key, then the interpreter program exits. (The interpreter waits for you to press a key, rather than exiting immediately, so that the game window doesn't disappear before you have a chance to read any messages that the game displays as it ends.)
      • Keep running. When you select this setting, the interpreter simply keeps running even after the game has ended. To exit the interpreter, close the game window (by clicking on the game window's close box, or by selecting "Exit" from the game window's system menu). This setting allows you to start another game, using the "Load Game" item on the "File" menu, after a game has finished.
    • The Windows HTML TADS interpreter now offers different option settings for the interpreter's action when you close the game window (by clicking the close box on the game window's title bar, or selecting "Close" in the game window's system menu). You can select the close action in the "Quitting" tab of the options dialog. The options are:
      • Send QUIT command to game. This is the traditional action: when you close the game window, the interpreter simply acts as though you had typed QUIT into the game.
      • Prompt before closing window and exiting. The interpreter will display a dialog box asking for confirmation that you really want to quit. If you click "Yes," the interpreter will terminate the game, bypassing any prompt that the game would normally display at this point.
      • Close window and exit without prompting. The interpreter will immediately close the game and exit when you click the game window's close box. The interpreter will not prompt for confirmation, and will also bypass any prompting that the game would normally do upon quitting.
    • The <BANNER> tag now accepts percentage values for the WIDTH and HEIGHT attributes. If you put a percent sign ("%") after the WIDTH or HEIGHT value, the size will be a percentage of the main window size. This lets you scale a banner to the size of the main window. Refer to Banner Size Attributes for full details.
    • When a game is running in HTML mode, the yorn() built-in function now displays the player's input in the input font set in the preferences. (In the past, the Interpreter did not switch to the input font for yorn() in HTML mode. The Interpreter has always switched to the input font for yorn() as well as for input() when the game was not in HTML mode, and continues to do so.)
    • "$$ABEND", the emergency abort command, now works properly in the Windows HTML TADS interpreter. This command was ignored in past versions.
    • The Windows debugger, now part of TADS Workbench, has a number of new user interface features that make it easier and more convenient to use:
      • You can now add or remove a breakpoint simply by clicking in the left margin of a source line (the gray area to the left of the text).
      • You can now reload your game file from disk without quitting and restarting the debugger. The "Reload Game File" item on the "File" menu terminates the current debugging session and reloads the game from disk. If you're making numerous small changes to your game as you run through it in the debugger, this new reload option can be convenient, since it allows you to edit the source, recompile the game, and reload it in the debugger without having to exit and restart the debugger. Note: you should not attempt to recompile your game at the same time that the game is running in the debugger, since this could result in a file sharing conflict (the compiler would attempt to overwrite the game file while it's in use by the debugger); instead, you should quit the game first. You can leave the debugger itself running during compilation, but you should be sure the debugger's title bar shows the "[game terminated]" status; this indicates that the debugger is not using the game file. After recompiling, use the "Reload Game File" command to load the game back into the debugger for another run.
      • You can now load a new game file without quitting and restarting the debugger. The "Load Game" item on the "File" menu terminates the current game (if any) and loads a new game.
      • The special debugger windows that display the stack, local variables, watch expressions, and call history now feature "docking" support, which means that these windows can be attached to an edge of the main debugger window. This new feature gives you substantially greater power to customize your display layout. Look here for more information.
      • The debugger now checks for modified source files each time the main debugger window is activated. If you edit and save a source file with another program (your text editor, for example) while the file is open in the debugger, the debugger will now offer you the option to load the updated source file when you switch back to the debugger window. The debugger will display a prompt for each source file that has been modified; you may choose to keep the old version in memory or load the updated version. Note that you'll generally only want to load the new version of a source file if you've also recompiled the game and then reloaded the game into the debugger; if you merely edit a source file, but continue debugging the old version of the compiled game, the source and compiled game would no longer be synchronized, hence you wouldn't want to load a new source for the old compiled game.
      • Right-click on the background area of the main debugger frame window (the "MDI client area") to pop up a menu of the special debugger windows.
      • The debugger now has preference settings that let you control which confirmation and notification dialogs that the debugger displays. Some people like to be prompted for confirmation when a command would have a possibly dangerous side effect, while others would prefer the debugger to assume they know what they're doing. The "Messages" tab in the debugger's "Options" dialog lets you turn many of the debugger's notifications and confirmations on or off to suit your working style.
      • When you close the debugger (by closing the main debugger window or by selecting the "Quit" item on the "File" menu), the debugger no longer waits for a keystroke before exiting. This extra pause at exit served no useful purpose for the debugger, so it has been removed.
      • The debugger now obeys accelerator keys for debugging commands (such Ctrl+F for "Find") when the debugger's main window is active, even while the game is running. (In past versions, the game's accelerator keys were active at all times while the game was running (rather than being paused in the debugger at a breakpoint or in single-step mode), even when the debugger's main window was on top. This was confusing and inconvenient. The debugger's accelerator keys now work any time the debugger's main window is active, whether the game is paused in the debugger or is running, and the game's accelerator keys are active any time the game window is active.)
    • Fixed a bug in the MKSETUP program (which builds an installer for a game). If the setup configuration file contained a FILE option (to include an extra file in the installation file set), MKSETUP got stuck in a loop adding the file over and over. This has been corrected.

    Changes in version 2.4.0

    • On Windows, in the "Options" dialog, the settings formerly in the "Command Font" panel now appear in the "Fonts" panel, and the "Command Font" panel has been removed. In addition, the "Command Font" popup list now offers a selection for "(Main Game Font)"; this allows you to use the normal game text font, but still set the style of the command input font (color, bold, italics) specially. (The separate checkbox for using the default game font has been removed, since it's redundant with the new popup entry.)
    • In version 2.3.0 on Windows, a bug in the "Options" dialog caused the interpreter to crash if the "Command Font" tab in the dialog was selected and the dialog was subsequently dismissed with the "Font" popup showing no selection. This has been corrected.
    • In the Windows version, a new "Options" dialog page allows setting the memory limit on previously-displayed text. As text scrolls off the top of the window, the interpreter keeps the text in memory (and any related information, such as graphical images) so that you can scroll the window back to review the old text. During long game sessions, this can use up an excessive amount of memory. In version 2.3.0, the interpreter started automatically limiting its memory usage to keep memory usage under control; the interpreter had a fixed limit of 128 KBytes for the text buffer. In version 2.4.0, the default limit has been reduced to 64 KBytes, but you can now control this limit with the new "Memory" options page. This new options page allows you to select a lower setting. If you frequently run low on system memory during long game sessions, try setting a lower memory limit with this option page.
    • On Windows, displaying PNG images in 256-color display modes is now much faster.
    • Fixed a bug in the pop-up evaluation system ("tooltip evaluation") for the Windows debugger. In version 2.3.0, multiple evaluation pop-ups sometimes appeared, and they didn't all disappear correctly. This should work correctly now.
    • The Windows version is now somewhat smarter about adjusting the scrolling position after the window is resized or reformatted. In particular, when the text is scrolled all the way to the bottom (as it tends to be most of the time during play), and you resize the window, the interpreter will keep the window scrolled to the bottom after the resize.
    • A bug caused the interpreter to fail to display any text after a game program displayed an empty <TITLE> tag. This has been corrected.

    Changes in version 2.3.0

    Changes of interest to both players and authors:

    • The interpreter now produces a much more usable transcript (a "script" or log file) for games that use HTML. Script files are now created as plain text files, with all of the HTML codes translated to plain text where possible or removed entirely where not; the effect is exactly the same as when the game is played through the text-only version of the TADS interpreter. In past versions, HTML codes were passed through to the script file, which made the script file very difficult to read.
    • In the Windows version of HTML TADS, the "Options" dialog has a new tab, "Command Font," that lets you set the typeface, color, and style for text entered on command lines. If you click the checkbox for "normal game font," command lines will use the same typeface as the rest of the game; if you uncheck this box, you can select the type style for commands. Non-HTML games will always obey your settings; note, however, that HTML-enabled games can override the input font, so some games may not always use the preference setting.
    • In the Windows version, when the mouse is hovering over an image, the system now displays a small pop-up label (sometimes called a "tool tip") showing the ALT text for the image. Game authors can supply text for the ALT attribute of each image to provide a textual description of the image.
    • The Windows version now limits the amount of old text that the game keeps around during a session. In the past, all of the text ever displayed during a session stayed around until the end of the session, consuming memory. HTML TADS now discards the oldest text periodically, which helps keep memory consumption under control. The system still keeps lots of old text, so players will continue to be able to scroll back through the game transcript, but the amount of old text retained is now limited to about 128k (which is, for example, enough to play all the way through The Plant once, and then restart and play about halfway through again).
    • In past versions, HTML TADS did not obey the traditional highlighting escape codes, "\(" and "\)", when not running in HTML mode (i.e., when the game didn't display the "\H+" sequence to activate HTML). This has been fixed; standard TADS games that don't activate HTML mode will now display highlighted text correctly.
    • HTML TADS did not show tab sequences ("\t") correctly for non-HTML games. Rather than skipping to a tab stop, the system simply displayed a few spaces, which in most proportional fonts did not create the right effect. This has been fixed; standard text-mode games will now show proper tabs when played with the HTML TADS interpreter.
    • Fixed a bug that caused old pages to display with an incorrect background image. In some cases, if clearscreen() was called, then a background image was displayed (with <BODY BACKGROUND=xxx>), then clearscreen() was called again, and after all of this the player used the "Previous Page" command (on the "Go" menu) to look at the old pages, the background image from the middle page could be displayed as the background image of other pages in the history, even though those pages were originally displayed without a background image. This has been corrected; old pages are now displayed with the correct background image (or lack thereof).
    • In past versions, when a banner was re-used for new contents, the old contents weren't always completely erased from the window. If the new contents took up less space than the previous contents of the banner, this left garbage on the screen. This problem has been corrected; a banner window is now completely erased when a new BANNER tag replaces the contents of an existing banner window, leaving only the new contents in the window.
    • Fixed a bug in MIDI playback that caused problems with looping MIDI sounds. Symptoms of the problem were sporadic; in some cases, the MIDI sound simply wouldn't repeat, and in other cases TADS caused a GPF. This problem has been corrected; looping MIDI sounds should now play back reliably.
    • WAV file playback occasionally "glitched," causing stuttered playback, looping playback, or random noise, or sometimes causing sounds not to play at all. These problems should now be corrected.
    • Fixed a bug that occasionally caused problems displaying vertical banners (that is, banners along the left or right edge of the main window). In some cases, if the player restored a game, and the restored game immediately displayed a left or right banner, the system incorrectly set the size of the banner so that it took up almost the entire width of the display window, leaving no space for the main text area. This has been corrected.
    • On the Windows version, the entities &Odblac; and &odblac; were reversed, so that &Odblac; displayed a lower-case "o" with double acute accent, and &odblac; displayed an upper-case "O" with double acute accent. This has been corrected.
    • The "MORE" prompt's wording on the Windows version has been changed slightly; the prompt now advises players to press "the space bar" rather "any key," because the "MORE" prompt did not in fact respond to just any key, but only certain keys. (The "MORE" prompt actually responds to a few keys besides the space bar, such as the Return key and the Page Up and Page Down keys, but the new prompt text is considerably less misleading.)

    Changes of interest to authors:

    • The Windows version now supports MPEG 2.0 audio layers II and III (usually referred to as MP2 and MP3 files, respectively). For more details refer to the sound documentation.
    • Because the Windows version supports MPEG 2.0 audio layers II and III, the systemInfo() function returns true for the new feature codes related to MPEG: __SYSINFO_MPEG_AUDIO, __SYSINFO_MPEG_AUDIO_2, and __SYSINFO_MPEG_AUDIO_3.
    • The <A> and <AREA> tags now accept two additional attributes that let you control how they enter their HREF commands. See the section in "Deviations from Standard HTML" on <A HREF> for details.
    • A new parameterized font, TADS-Input, lets the player specify the typeface, color, and style for text entered on command lines. If you're not trying to achieve a special effect of some kind during command entry, you're encouraged to use TADS-Input as the font for all commands. By default, std.t now activates this font in the commandPrompt function; you can replace this function definition with your own if you want to override this behavior. See the documentation on parameterized font names for details.
    • The treatment of whitespace following <P> and <BR> tags is now slightly different than it was in past versions. In the past, if a <P> or <BR> tag occurred at the end of a line of source code, and more text followed on the subsequent source line, HTML TADS rendered the text with a leading space on the second line. This no longer occurs; HTML TADS now omits any spaces between these tags and the subsequent non-blank text, so that the new line after a <P> or <BR> tag starts without a leading space. This new behavior is more consistent with the way that TADS handles the "\n" and "\b" sequences, and is also consistent with the way popular web browsers render similar HTML mark-ups.
    • HTML TADS now includes the normal bottom margin spacing in horizontal banners. In past versions, horizontal banners were too compressed vertically because this space wasn't included in the way the formatter calculated banner heights. (If you've written a game that compensated for this problem by adding some extra padding of your own, you might want to remove the padding in future versions of your game.)
    • The <BANNER> tag accepts a new attribute, REMOVEALL, that indicates that all banners should be removed. REMOVEALL does not take a value; when this attribute is specified, no other attribute (not even ID) is needed.
    • If the <BANNER> tag was used to replace an existing banner, and the new banner had different ALIGN or BORDER settings than those of the original banner, the new settings were ignored. This has been corrected; the new banner's settings now replace any settings that were previously in effect.
    • In past versions, alignment settings in <TD> (table cell) tags propagated to the first block within the cell, but not to any additional blocks. So, if you created a table cell which contained more than one paragraph, the first paragraph was aligned correctly, but subsequent paragraphs were aligned according to the enclosing division's block alignment. This has been corrected; any <TD> alignment setting now affects every block within the cell.

    Changes in version 2.2.6

    • The interaction between the inputkey() built-in function and the automatic "MORE" prompt has been changed slightly to correct a problem. In the past, the inputkey() function could retrieve keystrokes even when a "MORE" prompt was being displayed, which could create confusing situations when the text prompting for the keystroke had not yet been scrolled into view itself. This has been corrected; inputkey will never retrieve a keystroke until any after pending "MORE" prompt has been acknowledged.
    • Very long sequences of characters with no intervening spaces sometimes caused incorrect formatting in past versions. This happened in particular when long sequences of HTML entities appeared, such as twenty consecutive &nbsp; sequences with no intervening spaces (note that using the backslash-space sequence instead of &nbsp; produces the same result). This has been corrected.
    • A bug that occasionally crashed the HTML TADS debugger has been fixed. The crash sometimes occurred as the debugger attempted to take control when a run-time error occurred in the game.

    Changes in version 2.2.5

    • The <A> tag accepts a new attribute, PLAIN, that specifies that any text within the link should be rendered as ordinary text, rather than using the normal link attributes. Refer to <A> changes for more information.
    • In the preferences dialog, players can now select a fixed-width font, if desired, for the main game font. In the past, the main game font popup only offered proportional fonts; there was no particular reason for this restriction other than to reduce the size of the list to make it more manageable; some people didn't like the restriction, so fixed-width fonts are now included in the list as well.
    • In past versions, the HTML interpreter did not always work properly when started from a DOS command prompt. In particular, if the command line was specified using a relative path to the game file (for example: htmltads games\mygame.gam), the interpreter did not properly load resources (JPEG images, WAV sounds, etc.) bound into the .GAM file or bound into a .RSn file. This problem has been corrected; starting the HTML interpreter from a command prompt should work properly now, regardless of whether the .GAM file is in the current directory or a different directory specified with a relative or absolute path.
    • The inputkey() built-in function did not work correctly in past versions of the HTML interpreter. Although the function did wait until the user pressed a key, the function did not return the correct value for the key. This has been corrected.
    • In past versions, table cell tags did not accept WIDTH attributes with percentage values. Percentage-based cell WIDTH values are now supported.
    • Tables with empty cells (specified by a <TD> tag followed immediately by another <TD> tag or a </TD> tag) were not always formatted correctly in past versions; these sequences usually caused the entire table to be displayed at the wrong position. This problem was particularly noticeable in banners. This has been corrected; empty cells are now handled correctly.
    • A bug in the formatting of the <BANNER> tag caused a banner to be displayed in certain situations even after a <BANNER REMOVE> tag had been issued for the same ID. This has been corrected.
    • In the past, the traditional TADS non-breaking space sequence, "\ " (a backslash followed by a space), did not work correctly in the HTML TADS interpreter; in particular, multiple consecutive backslash-space sequences were displayed as a single space. This has been fixed.
    • In previous versions, graphics placed in banners were not always updated immediately after a palette change, which occasionally caused brief drawing glitches. This has been corrected.
    • If the user selected "highlight on hovering" in the link color settings in the options dialog, the interpreter did not always remove highlighting from a link positioned very close to the edge of a banner if the mouse was moved off of the link and into another banner (or the main text window). This works correctly now.
    • A bug in past versions caused a crash on exit under obscure circumstances: if the status line (or any other banner that was updated after each turn) contained a link, and the mouse was positioned over the link when the user typed "quit" and then left there, a crash sometimes occurred. This has been corrected.
    • See Recent Changes to TADS for information about several generic TADS interpreter improvements and corrections that affect the HTML interpreter as well:
      • A new parser feature lets you define new methods, dobjCheck and iobjCheck, that are always called before the verb handlers (verIoVerb and so on).
      • The HTML interpreter now supports external (native code) functions; you can place native code into a DLL and call the code from within your TADS program.
      • A parser bug, involving using AGAIN with a command directed to an actor that is no longer present, has been fixed.
      • A parser bug caused incorrect prompts to be displayed when an unknown word was entered in a command that required an additional object; this has been corrected.
      • A parser bug that caused doSynonym and ioSynonym handling to work incorrectly with overridden methods is now fixed.
      • The init function is no longer incorrectly invoked prior to initRestore when restoring a saved game during start-up (for example, when you double-clicked the saved game from Explorer).
      • Backslashes in text captured by the outcapture() built-in function are now handled correctly.
      • An incompatibility with games compiled by old (pre-2.2.0) versions of the compiler has been corrected.

    Changes in version 2.2.4

    Here's a quick index to some selected highlights in this release:

    Changes of Interest to Players:

    Changes of Interest to Game Authors:

    • TADS now offers improved support for localized character sets. In particular, TADS provides a way to use extended (non-ASCII) characters in a game while retaining game portability. This new TADS feature is supported in HTML TADS as well. Refer to the character set notes for more information.
    • The BANNER tag has been extended to allow banners to be placed at the top, bottom, left, or right edges of the window. Refer to the new banner documentation for details.
    • A new utility lets you build a custom installer for your game, without any programming. Refer to the game distribution documentation for details.
    • A game author can now optionally create multiple resources bundle files, separate from the .GAM file, for distributing a game and its resources separately without having to distribute the individual JPEG, WAV, and other files. This new mechanism allows game authors to distribute their games in multiple configurations, so that players can choose how much of a game they want to download and install. This new mechanism is described in the resource file documentation. Note that this new feature requires the TADS resource manager (tadsrsc) version 2.2.4 or higher; the resource manager accompanies the TADS compiler and tools distribution.
    • If you build a stand-alone executable version of your game based on the HTML TADS interpreter, you can now specify the desktop icon that your executable uses. You specify this with the maketrx32 command when you build your stand-alone game. Refer to the game distribution documentation for more information.
    • If you build a stand-alone executable version of your game based on the HTML TADS interpreter, you can now specify a custom filename suffix (extension) to use for saved game files created by your game. If you do this, the stand-alone game will automatically create system registry entries that associate the saved game files with your stand-alone game executable, allowing the player to start your game and restore a saved position in a single action by double-clicking on a saved game file. Refer to the game distribution documentation for more information.
    • The new TADS systemInfo() built-in function provides a way for you to determine in your game code whether the interpreter which is executing your game has certain capabilities, including whether HTML can be used to format text. This function can be especially useful if you want to customize your game for different interpreter configurations. Refer to Recent Changes to TADS for details.
    • TADS has a new parser hook that gives you more control over how the TADS parser handles unknown words in player commands. Refer to Recent Changes to TADS for full details.
    • TADS has a new language feature that lets you specify the superclass from which you want a method to inherit when you use the inherited keyword. Refer to Recent Changes to TADS for information.
    • TADS now allows you to change the object that represents the player character dynamically, using the new parserSetMe built-in function. Recent Changes to TADS has details.
    • HTML TADS now supports the <Q> tag from HTML 4.0. This tag encloses a passage in quotation marks. <Q> automatically uses typographical quotation marks if they're available in the character set. Nested <Q> tags alternate between double and single quotes.
    • The typographical character extensions have been changed for compatibility with HTML 4.0, which includes the same characters with different names than were originally assigned by TADS. The following character names have been changed to match the HTML 4.0 specification:
      • &lsq; has been changed to &lsquo;
      • &rsq; has been changed to &rsquo;
      • &ldq; has been changed to &ldquo;
      • &rdq; has been changed to &rdquo;
      • &emdash; has been changed to &mdash;
      • &endash; has been changed to &ndash;
      Note that the old character codes are still allowed, but they're no longer documented and should be considered obsolete.
    • The following new special character codes from HTML 4.0 have been added.
      • &sbquo; produces a single low-9 quote
      • &bdquo; produces a double low-9 quote
      • &lsaquo; produces a single left angle quote
      • &rsaquo; produces a single right angle quote
      • &dagger; produces a dagger
      • &Dagger; produces a double-dagger
      • &OElig; produces a capital "OE" ligature
      • &oelig; produces a small "oe" ligature
      • &Yuml; produces a capital "Y" with an umlaut (diaeresis)
      • &Scaron; produces the capital letter "S" with a caron
      • &scaron; produces the small letter "s" with a caron
      • &permil; produces a per-thousand sign
    • In addition to the special characters listed above, HTML TADS now supports all of the HTML 4.0 named character entities listed in the HTML 4.0 specifications as "symbols, mathematical symbols, and Greek letters." This list is not reproduced here because of its length, but you can refer to the HTML 4.0 specification for the full list. The only named character entities that HTML TADS does not support are the spacing and joiner entities (ensp, emsp, thinsp, zwnj, zwj), and the bidirectional markers (lrm, rlm).
    • HTML TADS now provides an additional set of named character entities to provide access to ISO Latin-2 characters. ISO Latin-2 is a character set designed for Eastern and Central European languages. Standard HTML does not yet provide named entities for these charaters, but HTML TADS extends the standard entity list to support these additional characters. Refer to the HTML TADS Latin-2 table for the complete list.
    • You can now enter hex digits in character entities by putting an "x" after the "#"; for example, "&#xF3" enters the hex value F3.
    • The Windows HTML TADS Debugger now uses MDI (Multiple Document Interface). MDI is a common Windows application style in which all of an application's windows are grouped together into a single container window. The MDI style is well suited for applications such as the Debugger that have a large number of related windows that share common user interface controls, such as toolbars and menu bars. Many users found the original Debugger user interface to be unwieldy, because the various windows weren't grouped together.
    • The Windows version of the debugger will now ask you to locate source files as it needs them, rather than requiring that all of the proper include paths be specified on the command line. Whenever the Windows debugger needs to open a source file, and the file isn't in the .GAM file directory or anywhere on the include path, the debugger will display a dialog asking you to locate the source file. In addition, the debugger saves the list of directories obtained in response to these prompts with the per-game configuration information (in the game's .TDC file), so these settings are retained in future sessions. (Note that the DOS character-mode debugger still operates as before, requiring source files to be on the include path at start-up.)
    • The Windows version of the debugger now lets you access the main debugger window's menu through the keyboard interface (using the Alt key) even while one of the other debugger windows is active.
    • The Windows version of the debugger now displays the game status (running, paused, terminated) in the main debugger window's caption.
    • In a source file window in the Windows version of the debugger, certain keystrokes are now handled slightly differently, to make the handling more consistent with other Windows programs. The Home and End keys now move the cursor to the start and end of the current line, respectively; Control+Home goes to the start of the file, and Control+End goes to the end of the file. Page Up and Page Down now move the cursor up and down a page; Control+Page Up and Control+Page Down scroll the window without moving the cursor.
    • To accomodate the editing key changes above, the Control+Home key no longer goes to the current line, but now goes to the start of the file. The Alt+Keypad * key now goes to the current line.
    • For game developers' use, the interpreter now displays diagnostic messages in the debug log window for resource loading problems. Whenever a JPEG, PNG, WAV, or MIDI resource cannot be loaded or encounters an error, the interpreter displays information about the problem in the debug log window. (Note that the debug log window is only shown if you run with -debugwin specified on the command line when invoking the interpreter. These messages won't interfere with the main game window.)
    • This version corrects a few bugs with tables:
      • Under certain circumstances, using alignment attributes in TR or TD tags caused the alignment settings to continue beyond the table.
      • If a table was the very first item in a banner, the table was invisible.
      • If a table had an explicit height specified (with a HEIGHT attribute on the TABLE tag), the height was ignored.
      • A table whose WIDTH setting exceeded the maximum width actually necessary to display the table sometimes ignored the width setting. (This only happened when the table had simple contents, such as columns that only contained single words or pictures.)
    • Some bugs involving banners are now fixed:
      • If you used multiple banners, the status line was not always updated properly when the player moved the mouse cursor over a link.
      • A text link appearing in a banner did not always use the correct color.
    • This version corrects a problem with the debugger that caused the debugger to crash sometimes when you single-stepped through code at the very end of a source file.
    • The FACE attribute of the FONT tag now accepts a set of new "parameterized" font names. The new parameterized fonts do not directly select a system font by name, but instead select a style of font that can be mapped to an appropriate installed font on each system. Because the parameterized font names are mapped at run-time on each system, parameterized names are much more portable than explicit system font names while still providing you with some flexibility in specifying different styles. In addition, some systems allow the player to select the actual system font that will be displayed for a given parameterized font, thus giving greater control over the final presentation to the player while still giving considerable control over the style to the game author. Refer to the HTML deviations documentation for full details.
    • In addition to the standard HTML color names and numerical RGB values, the HTML parser now provides a set of new "parameterized" color names. The parameterized values do not map directly to hard-coded colors, but instead use system-dependent colors that the player can change at run-time on some systems. For example, the new color names "statusbg" and "statustext" are system-defined colors for the status line background and status line text, respectively; on Windows, the player can control the values of these colors through the "Preferences" dialog. You can use the new parameterized color names in any of the tag attributes that take a color value. Refer to the HTML deviations documentation for full details.
    • The HTML status line code in adv.t, which is enabled when you compile your game with the preprocessor symbol USE_HTML_STATUS defined, now automatically senses whether the game is running in HTML mode or in standard mode, and generates an appropriate status line for the current mode. This makes it easier to write a game that will run under both HTML and standard interpreter systems, while still taking advantage of HTML features when they're available. The enhanced status line code uses the new systemInfo built-in function, described in Recent Changes to TADS, to sense whether HTML features are available at run-time. Note that, even if you customize the status line, you may want to refer to the new adv.t status line code and use similar logic in your custom implementation, to support both types of interpreters.
    • The default status line color scheme in adv.t now uses the new "statustext" and "statusbg" parameterized color attributes, which makes the status line colors controllable through the player preferences (for platforms that provide a preferences mechanism).
    • TADSERR.MSG (the TADS error message file) is now linked directly into the interpreter, so the separate message file is no longer needed when playing or distributing a game.
    • Some small documentation problems have been fixed:
      • the link from the introductory page to the debugger page is fixed
      • the debugger page is now part of the normal interpreter distribution
      • the debugger page no longer has its last few paragraphs in boldface.
    • Some cosmetic problems have been fixed:
      • The HTML TADS interpreter and debugger "About" boxes now display the TADS version number
      • The usage messages for the HTML TADS interpreter and debugger now display their correct executable names
    qtads-2.1.7/htmltads/notes/compat.htm000066400000000000000000000135111265017072300176060ustar00rootroot00000000000000HTML TADS System Compatibility Notes





    System Compatibility Notes



    If you're experiencing a problem with HTML TADS or TADS Workbench, please consult the notes below. We've found a few situations where different Windows system configurations can cause problems. The notes below explain how to fix the configuration problems we know about.

    If you can't find a solution to your problem here, you might try checking the Usenet newsgroup rec.arts.int-fiction to see if anyone else has run into the same problem, or you can go to tads.org for information on how to contact us.


    Workbench on Windows 95/98: "SHELL32.DLL:SHGetFolderPathA" Error

    If you're running TADS Workbench on Windows 95 or 98, launching Workbench might show the following error messages:

    Error Starting Program
    The HTMLDB3.EXE file is linked to missing export SHELL32.DLL:SHGetFolderPathA.
    

    If you get this error, you should be able to fix it by installing Microsoft Internet Explorer version 5 or later. The issue is that Workbench depends upon a newer version of a certain Windows system DLL than what comes with 95 and 98 by default. IE 5 and later automatically upgrade this DLL as part of their installation process, so updating the DLL is simply a matter of updating IE.

    Crash running HTML TADS

    If HTML TADS crashes, particularly when you select the "Options" item of the "Edit" menu or "Customize" on the "Themes" menu, you probably have an out-of-date version of a Windows system file called COMCTL32.DLL. If you encounter this problem, you can fix it by downloading this file from the Microsoft web site:

    After downloading the file, double-click it to run the installation program, which will install the upgraded DLL for you. Note that HTML TADS automatically checks to make sure you have the correct version installed and warns you at startup if you don't. Although you can choose to continue running if TADS displays this warning, we strongly encourage you to upgrade your COMCTL32.DLL before running HTML TADS.

    Crash using Wine (Windows emulator for Linux)

    If you're running HTML TADS in the Wine environment (a Windows emulation environment for Linux/Unix systems), you might need to use the "-noalphablend" command-line option when running htmltads.exe. Some versions of Wine have a bug that causes the program to crash immediately after startup. This has been reported in particular on the version known as WineX. If you're running on Wine, and the interpreter crashes immediately when you run it, try running it from the command line like this:

    htmltads -noalphablend
    
    Western European characters on localized (Non-English) Windows systems

    If you're running a Windows system that uses a character set other than US/Western Europe (such as Windows Eastern European or Cyrillic), please read this note.

    If you have any problems with HTML TADS displaying characters that are supposed to be from the US/Western Europe character set, but are instead displayed as characters from your system's default character set, you might need to adjust your system configuration to remove "font substitutions." Many non-US/Western Europe systems use font substitutions as a way to work around older applications that don't have proper localized character set support, but the substitutions can interfere with applications like HTML TADS that do properly support localized character sets.

    To check for font substitutions, look at your WIN.INI file (usually in your C:\WINDOWS directory). Look for a line that starts with "[fontsubstitutes]", then look for lines like this:

    Arial,0=Arial,238
    

    (The second number might be something other than 238.) If you find any lines like this, you might want to try deleting them or commenting them out (by putting a semicolon, ";", at the start of each line). You might need to reboot your system before your changes take effect.

    Windows NT users: On NT, the font substitutions are specified in the registry rather than in WIN.INI. Run REGEDIT and look for a registry key with this path:

    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes
    

    qtads-2.1.7/htmltads/notes/porting.htm000066400000000000000000001611271265017072300200140ustar00rootroot00000000000000 Notes on porting HTML TADS

    Notes on porting HTML TADS

    Recent Changes Affecting Porting

    Version 2.5.10

    • The "link" item class, CHtmlDispLink, has a new method, is_clickable_link(). This method indicates whether or not the link is clickable - that is, whether clicking on the link should be treated as a hyperlink activation or as an ordinary click on the underlying item. When is_clickable_link() returns TRUE, the platform-specific code should act as it always has. However, when is_clickable_link() returns FALSE, the UI code should treat a click on the link as though the link weren't there at all - so the click should be treated the same as a click on the underlying display item.
    • The TADS2 "osifc" interface includes new optional UI extensions. These extensions are optional because they're inherently graphical, and could not readily be implemented on text-only platforms. HTML TADS ports are generally graphical, though, so most HTML TADS versions should be able to implement these. The extensions are defined in a separate file, tads2/osifcext.h, to emphasize that they're not part of the base osifc set. If you want to implement the extensions, you should do so directly in your platform-specific code; there's no provision for these in oshtml.cpp since there's simply no generic support that the portable HTML TADS code could usefully provide for these. In addition, if you do implement these, you should enable TADS 3 VM access to the extended functionality by modifying your TADS 3 makefile to include tads3/vmbifregx.cpp instead of tads3/vmbifreg.cpp. The 'x' version includes the "extended" I/O function set that provides program access to the new interfaces.

    Version 2.5.8

    • The os_banner API introduces the new concept of a "parent" window. Each banner window can now optionally have a parent banner specified. Refer to the TADS 2 porting notes (portnote.txt in the TADS 2 distribution), and the os_banner API documentation (os_banner.htm) for details. This change requires a change to the interface ot the frame window method CHtmlSysFrame::create_banner_subwin(), and also will require changes to the banner window layout algorithm on all implementing platforms. Note that the addition of the parent window is mandatory for systems that implement the os_banner API; there is no provision for systems to support the banner API without also supporting parent window specifications.
    • The os_banner API has a new style flag, OS_BANNER_STYLE_MOREMODE. When this flag is set, the banner window must display a suitable "More" prompt, and pause for user input, when text is about to scroll out of view. In other words, this style flag should make the banner show a "More" prompt just like the main game window does. This style implies the auto-vscroll style, because that's the only way text would ever scroll out of view. The system code manages the "More" prompting in the HTML interpreters, so this style flag must be handled by the system-specific CHtmlSysWin implementation. Note that this flag will never be used except in banner API windows (in other words, it'll never be used in windows created with the <BANNER> tag).
    • The class CHtmlSysFont has a couple of new methods that provide information on the system-level font. is_fixed_pitch() returns TRUE if the font is monospaced, FALSE if it's proportionally spaced. get_em_size() returns the "em" size of the font. On many systems, the em size is a design property of each font, so it can't be inferred in a portable fashion; for systems where this information isn't stored in each font, the implementation should simply return the height of the font in pixels (i.e., translate the nominal point size of the font to pixels and return the result).
    • The method CHtmlSysImage::draw_image() has a new third parameter, specifying how to draw the image if the native size of the image doesn't exactly match the size of the rectangle. In the past, the handling was poorly specified in the interface comments, but it was intended that the routine would linearly scale the image up or down as needed to fill the target drawing rectangle. The new parameter allows the caller to specify one of three treatments for size mismatches: clip the image, stretch (scale) it, or tile it. The comments describe the three modes in more detail. The old version should have done exactly what the "stretch" mode now does.
    • The class CHtmlSysWin has a new method, draw_text_space(), that draws "typographical spaces," which is to say spaces of arbitrary width. This is used for special proportional spacing effects where it's necessary to draw what look like spaces, but not necessarily in integral multiples of the width of the ordinary space character.
    • In CHtmlSysWin::get_font(), when creating the new CHtmlSysFont object, if the font descriptor specified has the 'default_charset' member set to TRUE, then the new system font object must store the ACTUAL character set identifier in the new font object's 'desc_.charset' element. There was no such requirement in the past. This change is required so that the generic code can obtain the actual character set information from a font object, so that the generic code can pass character set ID's back to the system code.
    • There's a new function, os_next_char(), that the system code must implement. Each platform should provide a header for this function (or, alternatively, define it as a macro) in the platform's xxx/hos_xxx.h file. This function allows the system to provide support for multi-byte character sets, if multi-byte character sets are used locally. For systems that use only single-byte character sets, this is trivial to implement (it's just a pointer increment). Most systems that support multi-byte character sets at all provide OS-level functions to traverse strings with proper MBCS handling, so this should be easy to implement wherever it's needed.
    • The new CHtmlSysFrame method set_nonstop_mode() lets the system frame receive notification that the caller wants to run in non-stop mode (or not - the default is NOT non-stop mode). In non-stop mode, MORE prompting (or any local equivalent) is to be suppressed. This is used when the UI is running under automated scripting control, which means we don't want to require any user interaction. System implementations are free to ignore this, but systems that have something like a MORE mode (i.e., pausing for user input between each screen-full of text displayed) should implement this method.

    Version 2.5.7

    • Several new SYSINFO_xxx codes have been added; the os_get_sysinfo() routine should be updated for the new codes. In particular, note that SYSINFO_BANNERS should return result code 1, SYSINFO_TEXT_COLORS should return SYSINFO_TXC_RGB, SYSINFO_TEXT_HILITE should return 1, and SYSINFO_INTERP_CLASS should return SYSINFO_ICLASS_HTML. See osifc.h (in the tads 2 sources) for the definitions of these constants.
    • The new parameterized color HTML_COLOR_INPUT has been added, to represent the color of command-line text. CHtmlSysWin::map_system_color() should be updated with the new color code.
    • The new parameterized color HTML_COLOR_HLINK has been added, to represent the color of hyperlink text when the mouse cursor is hovering over the hyperlink. CHtmlSysWin::map_system_color() should be updated with the new color code.
    • The new os_banner API, introduced into "osifc" (the basic TADS portability layer) in TADS 2.5.7, is mostly handled by the portable HTML TADS code, so ports will get the new banner API without much extra platform-specific work. We've tried to keep track of specific changes that were necessary for the Windows version, and outline them below; we expect that similar changes will be needed on other systems.
    • A few interface changes were necessary in classes defined in htmlsys.h. The significant ones are described below; any not listed should be fairly self-explanatory and should be obvious when compiling.
    • The oshtml_set_frame() routine has been renamed to CHtmlSysFrame::set_frame_obj(). It works the same way; this is just a superficial name change to make the naming more consistent with the model.
    • The CHtmlSysWin routines create_banner_subwin(), remove_banner_subwin(), and create_aboutbox_subwin() have been moved to CHtmlSysFrame. They really belonged there to start with, but the stream-based design of the original <BANNER>-tag system misleadingly suggested that they were part of the main text window. In addition, these routines have been superfically renamed to use 'window' instead of 'subwin', for consistency with the new view of banners as peers of the main text window, and not mere subwindows of the main text window.
    • The CHtmlSysWin routine set_banner_size() has changed slightly. The "size is percentage" flags have been replaced with more general "size units" parameters, which expand the range of possible size settings by adding a new unit, "characters." The "pixels" unit is the same as the old percentage==FALSE, and the "percentage" unit is the same as the old percentage==TRUE. The "characters" unit is new: it specifies the size in terms of the character width or height, which is defined as the size of a "0" character in the window's default font.
    • There's an additional, more subtle change in set_banner_size(): the meaning of the "percentage" size has changed. In the past, the size was given as a percentage of the application window size; now, the size is given as a percentage of the remaining size at the time the banner is being laid out. So, suppose we have a game with two banners, the first (in layout order) top-aligned and the second bottom-aligned, and each with a percentage size of 33. In the past, the two banners and the main text window would all have the same size, one third of the total application window area. Now, the top window, being first in layout order, still gets 33% of the overall window, since the remaining size is the entire window at the time it's laid out. We next lay out the bottom banner; this one gets 33% of the remaining size, which is two-thirds of the application window size; hence, we get 33%*66% = 22% of the total application window. The main text window gets what's left, which is 46% of the total application window size.
    • CHtmlSysFrame::flush_txtbuf() should now be sure to flush buffers explicitly for any banner API windows, separately from the main game window. Fortunately, the portable code makes this pretty easy: simply run through the list of banner windows, and for each window's CHtmlSysWin object 'win', call flush_txtbuf(fmt) on the window's CHtmlFormatter object, where 'fmt' is the parameter of the same name to CHtmlSysFrame::flush_txtbuf().
    • The interface for CHtmlSysWin::create_banner_subwin() has changed to use the OS_BANNER_STYLE_xxx flags to specify the style of the new banner window. Note that this adds some new styles that weren't possible to specify before. The os_banner interface explicitly specifies that all style flags are optional, so the OS code is not required to implement any of the new style flags. However, since the HTML interpreters are generally the most full-featured interpreters around, and since the GUI platforms where the HTML interpreters run can easily support all of the new styles, it's highly desirable for the OS code to implement the new styles. The new features in particular are that scrollbars can be displayed in banner windows, and banners can be set explicitly to "auto scroll" mode, so that whenever new text is printed to a banner, the banner scrolls to show the new text.
    • CHtmlFormatter::get_text_array() is now protected, so code in other classes can no longer access this method. This change was made because formatters can no longer be assumed to provide text arrays. Any OS code that depended on being able to access the formatter's text array should instead use the similar methods exposed directly by the formatter itself. This refactoring of interfaces allows the formatter's implementation to be better hidden, allowing more flexibility in the formatter's internal design.
    • On the Windows version, I had to make some adjustments to the way the banner windows drew their borders when the banner windows became scrollable. In particular, I had to exclude the border itself from the scrollable region of the window. (This is a detail specific to the Windows OS implementation, but I mention it because other platforms could run into the same sort of thing.)
    • Note that "tab alignment" is inherently always available in any full HTML interpreter, since the portable HTML parser/renderer handles the <TAB> tag. So, for the style flag OS_BANNER_STYLE_TAB_ALIGN, you can ignore this flag on window creation, and simply set it unconditionally in CHtmlSysWin::get_banner_info().
    • The behavior method CHtmlSysWin::start_new_page() has changed slightly. In the past, this method simply cleared the entire frame, which included deleting all banner windows. This method should no longer delete banner windows unconditionally; instead, it should only delete banners created with <BANNER> tags in the main window. Most implementations will probably use the main window's formatter object's remove_all_banners() method to delete the banners, in which case they will not need any changes: the remove_all_banners() method will do the right thing automatically.
    • The Windows version maintains a "history" of screens that have been cleared. Each time CHtmlSysFrame::start_new_page() is called, the Windows implementation saves the outgoing page by "exporting" the parser object's state to a CHtmlParserState object (this is all done by invoking portable code). Users can later recall these saved history pages using a menu or toolbar command to step back through the list of old pages. The old pages are read-only, obviously. The new banner model required some changes in the way the Windows version handles this; these changes are entirely specific to the Windows implementation, but we mention them in case other platforms have similar mechanisms that will be affected in similar ways:
      • When viewing history, the Windows version now uses a separate window for the history panel, rather than showing history in the main window. When switching from the main page to viewing history, the system makes the main page window invisible (by hiding the window at the OS level) and makes the history window visible; the history window is normally kept invisible. The system then places the history window in exactly the same screen area that the main window was previously occupying. Switching back from a history page to the main window reverses this visible/invisible swap. This change allows the main window to be left intact, so that the system doesn't have to worry about saving and restoring its state with respect to command input editing and the like.
      • The history window uses a new subclass of the portable formatter object; the new subclass is CHtmlFormatterHist. The main difference between this new special formatter and the normal main window formatter is that CHtmlFormatterHist simply ignores banner windows embedded in the history. This allows paging through the history without disturbing the layout of banner windows; it's crucial to leave banner windows unaffected during history recall because of the new programmatic access to banners.
    • In the Windows interpreter, any routine that adds output to the main window is now careful to exit "history" mode and return to showing the main page. In particular, CHtmlSysFrame::display_output() always jumps to the active page if a history page is being viewed. This is desirable in case a timed event causes output while the user is viewing a history page; this change ensures that the effect of the timed event is immediately apparent to the user.

    Version 2.5.6

    • Several new parameterized system colors have been added: HTML_COLOR_LINK and HTML_COLOR_ALINK, HTML_COLOR_TEXT, HTML_COLOR_BGCOLOR. The system-specific CHtmlSysWin::map_system_color() implementation should be updated to recognize these new color codes and translate them to the appropriate colors; in most cases, these will be translated according to user preference settings.
    • The new member variable face_set_explicitly of the class CHtmlFontDesc (defined in the portable header htmlsys.h) allows you to distinguish cases where a typeface name has been explicitly selected from cases where the typeface is inherited from surrounding text. The latter case arises in situations such as when a <b> tag appears: the font descriptor is filled in with all of the information for the current text (i.e., the text surrounding the boldface text), then the boldface tag changes the weight member to select bold text. The face member is filled in with the name of the typeface from the surrounding text, but a new typeface isn't being selected - it's merely filled in because we want all of the same attributes of the surrounding text except for the weight. This new member has been added so that any system-specific code that translates parameterized font names (such as "TADS-Input") will know whether additional attributes associated with the parameterized font name, such as color and boldness, should be applied to the font descriptor. When face_set_explicitly is true, all of the attributes of a parameterized font should be selected; if this member variable is false, then only the face name should be translated, and the other attributes should be left alone. On Windows, this affects the "TADS-Input" font, because this font allows the user to select (via the "preferences" dialog) the color, bold, and italics settings for the command input font; CHtmlSysWin_win32::get_param_font() in win32/htmlw32.cpp uses this information when performing the parameterized font name translation.
    • The new member variables bgcolor and default_bgcolor of CHtmlFontDesc provide information on the font's background color, if it has one. If default_bgcolor is TRUE, then bgcolor should be ignored; otherwise, bgcolor should be used as the fill color for the bounding rectangle of text drawn with font.
    • A new, more sophisticated timer interface has been added to CHtmlSysWin. The system-specific window subclass must implement a few new pure virtual functions to provide this mechanism: CHtmlSysWin::create_timer(), CHtmlSysWin::set_timer(), CHtmlSysWin::cancel_timer(), and CHtmlSysWin::delete_timer(). Related to this new mechanism is the new class CHtmlSysTimer. The base type (defined in htmlsys.h) keeps track of the generic information associated with the new timer mechanism, and the system code is free to subclass this as needed to add system-specific information.
    • CHtmlSysWin::draw_text() should be updated to use the new background color information in CHtmlSysFont.
    • The Ogg Vorbis compressed audio format has been added to the standard set of media types. The new system-specific subclass CHtmlSysSoundOgg must be implemented to provide Ogg Vorbis playback.
    • The MNG animated image format has been added to the standard set of media types. The new system-specific subclass CHtmlSysImageMng must be implemented to provide MNG support.
    • The application frame class (CHtmlSysFrame) has a new method that the system-specific code must implement, get_formatter(), which returns the CHtmlFormatter object associated with the main HTML window. This should be trivial to implement in most cases, because the application frame must already create a formatter object and keep track of it internally.

    Architectural Overview

    First the good news: most of HTML TADS is portable code; you shouldn't need to make any changes to the portable parts in order to move HTML TADS to a new operating system. Now the bad news: it's not all portable code; some of the code is system-specific, and you'll need to re-implement this non-portable portion to get HTML TADS running on a new operating system.

    Even though you won't need to make any changes to the portable code, you'll probably find it helpful to know a little about how it works, since you'll make use of services in the portable code in the course of implementing a new system-specific implementation. This section is a brief overview of the design of HTML TADS. HTML TADS has the following major components:

    • HTML parser: this is implemented in the class CHtmlParser. The HTML parser reads HTML source code and interprets the embedded HTML command sequences (which are usually called "markups"). The parser generates a data structure in memory that represents the HTML text and commands; this data structure is called the format list. The format list contains the same information as the original HTML source code, but in a translated format that is easier for the computer to use.

      The format list is constructed from "tag" objects, which are subclasses of the class CHtmlTag.

    • Formatter: this is implemented by the class CHtmlFormatter. The formatter reads the format list (which was generated by the parser) and converts it into another in-memory data structure, the display list. Whereas the format list corresponds directly to the parsed HTML code, the display list corresponds directly to the information that will be displayed on the screen. The display list contains almost entirely items that appear on-screen, and each item in the display list has information on its size, position, and appearance. To produce the display list from the format list, the formatter must figure out where to insert line breaks, where each item will go in the display window, and all other details of how the information will appear on the screen.

      The display list is constructed from "display" objects, which are subclasses of CHtmlDisp.

    • Text array: this is implemented in the class CHtmlTextArray. The text array is a simple mechanism that stores the text in the HTML source. The text array is a specialized memory manager that has some special properties. In particular, it provides a virtual address scheme that guarantees that addresses are allocated in monotonically increasing order; the formatter exploits this feature to simplify its manipulation of the text underlying the format and display lists.
    • Resource cache: this is implemented by the class CHtmlResCache. The resource cache keeps track of resources (external binary data, such as JPEG images) that have been loaded. Its function is to minimize resource memory consumption and load time by re-using resources whenever possible. Whenever HTML TADS is about to load a resource, it first looks in the resource cache to see if the resource is present, and if so uses the original copy of it.
    • System HTML window: this is implemented by your non-portable, system-specific code. HTML TADS defines a portable interface to the system window, which the formatter uses to obtain information about the layout of the window and to display information in the window.
    • Input buffer: this is implemented by the class CHtmlInputBuf. This class is a helper for your system-specific window implementation; you don't need to use it, but it may be helpful. The input buffer handles the internal (non-user-interface) details of implementing a command line input editor. It provides services, such as selecting a range of text or inserting a character, that make it easier to implement a command line editor; if you use this class, your user interface code must handle actual user interface events and call the corresponding methods in the input buffer object. Your operating system or application class framework may have its own object or service that provides this type of functionality, in which case you probably won't need to use this object.
    • Resource implementations: the classes CHtmlJpeg and CHtmlPng implement portable operations on JPEG and PNG files, respectively. Additionally, you must implement system-specific objects that take the portable data representations of these classes and convert them into objects that your system can display.
    • Property list: the class CHtmlPropList provides an implementation of a simple property list object. You can use this object to store user preferences, if it's helpful.
    • Resource finder: this is implemented in class CHtmlResFinder. The resource finder is a mechanism that lets HTML TADS find resources stored in .GAM files through the TADS resource storage mechanism. The resource finder works with the TADS file reader to construct a map of resources stored in the .GAM file; when HTML TADS tries to load a resource based on an URL, it first looks in the resource finder's list to see if the resource can be loaded from the .GAM file.

    Steps in Porting HTML TADS

    The remainder of this document describes the steps you should follow to port HTML TADS to a new system.

    First, port the regular TADS to your system

    The first thing you need to do is port TADS to your platform. Since TADS has already been ported to most platforms, this should just be a matter of finding the correct set of files for your system, setting up a build environment, and compiling. There's a makefile or its equivalent for most platforms as well, so you shouldn't need to figure out the build commands from scratch.

    HTML TADS only uses the TADS interpreter, so you only need to build this component of the normal TADS at this point.

    Obtain the Required Third-party Libraries

    On Windows, HTML TADS depends on a number of third-party libraries to implement some of its functionality. In particular, the image format support is provided mostly by third-party libraries.

    These libraries are not required by the portable code. The Windows implementation uses them, but you don't necessarily have to on another platform. The Macintosh version, for example, doesn't use any of them, because there are better Mac-specific equivalents. We mention these libraries only because they're highly portable, so if you don't know of a better option for your system, these are probably good bets.

    If you do use these third-party libraries, it will mean that you'll need to do a little leg-work to integrate them. In the end, though, it should save you a lot of effort compared to implementing all of this functionality from scratch: these libraries are all free, of high quality, and are already highly portable.

    The libraries you might find useful are:

    • PNG (Portable Network Graphics). This library provides support for the PNG image format. You can find the PNG home page, which has links to lots of information about PNG, including C source code for the PNG library, at http://www.cdrom.com/pub/png/ . I'm currently using libpng version 0.95 (also known as 1.0 beta 5), but future versions will probably work as well.
    • ZLIB, a data-compression library. HTML TADS doesn't use ZLIB directly, but the PNG libraries described above need it. You can find the ZLIB home page at http://www.cdrom.com/pub/infozip/zlib/; this page has links to the C source code.
    • JPEG, a portable image format. There are several implementations of JPEG available, so if there's one for your platform that you're already familiar with, you should use that. The reference implementation, though, is the Independent JPEG Group's library; you can find the C source code at ftp://ftp.simtel.net/pub/simtelnet/msdos/graphics/jpegsr6b.zip (for an MSDOS ZIP file), or ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz for a Gnu-tools-friendly version. (Apart from the bundling format, both of these should contain the same source files.) Note that you should use version 6b or higher - past versions did not handle conversion of the gray-scale storage format to RGB for display, which the portable CHtmlJpeg code generally requires.
    • Libvorbis (the reference Ogg Vorbis decoder library), at www.vorbis.com.
    • Libmng (the reference MNG library), at libmng.com.
    Each of the libraries above has been widely ported; there's a good chance there's already a makefile for your platform included in the archive. If not, each includes documentation that describes how to port the library to a new platform.

    The TADS virtual OS layer

    The regular character-mode TADS has a "virtual operating system" layer of code. This is the interface that TADS uses to perform system-specific operations. The interface itself is defined portably -- there's a set of functions that TADS can call from portable code, and these functions provide the same behavior on all platforms. The implementations of these functions are different on each platform, though. These functions provide a virtual OS by providing system-specific functionality through a standardized, portable interface.

    HTML TADS: a replacement virtual OS layer

    HTML TADS is designed to look like a virtual OS layer, from TADS's perspective. TADS doesn't know much about HTML; as far as TADS is concerned, it's just generating output the same way it would on DOS or Macintosh or Unix: TADS puts together a buffer with text it wants to display, then calls the virtual OS display-text function.

    So, to port HTML TADS to a new platform, you should start with a port of TADS to that platform, then remove a number of the virtual OS function implementations that you would normally use for that platform. You remove them because HTML TADS provides new implementations for them.

    For a normal port of TADS, most of the virtual OS functions are in files with names like osdos.c or osmac.c. Some systems have more extensive OS layers than others, so some systems have all of their OS implementation in a single .c file, while others use several files. There's also a file called osgen.c, which provides some OS-specific functions that have a common implementation across a number of systems. Your system's TADS makefile should be helpful in determining which files are used on your platform.

    oshtml.c

    To determine which of the normal platform OS functions to remove from your build, look at the HTML TADS file called oshtml.c. This file contains the implementation of the HTML TADS replacements for the normal virtual OS functions. Despite its name, oshtml.c is a portable file -- it is the same on all platforms. You don't need to port this file.

    Go through the function definitions in oshtml.c; for each one, you need to remove the corresponding implementation in the normal OS layer for your system.

    htmlsys.h

    Now comes the real work. Just as TADS has a virtual OS layer, HTML TADS has its own virtual OS layer. The functionality of this layer is defined in the HTML TADS file htmlsys.h.

    TADS is written in C, but HTML TADS is written in C++. The TADS virtual OS layer used C functions; the HTML TADS virtual OS layer, on the other hand, is defined through a set of C++ classes and methods on those classes. The file htmlsys.h defines the portable interfaces to these classes. These classes have names that start with CHtmlSys: CHtmlSysFont, CHtmlSysWin, and so on.

    These classes are not typical C++ classes that define methods and member variables. Instead, these classes are designed as abstract interfaces -- the methods defined in these classes do not have corresponding implementations, but are pure virtual methods that must be overridden and defined in subclasses.

    This is where the new system-specific code for your platform needs to be written. For your system, you must define a concrete subclass of each of the CHtmlSysXxx classes. Each of these concrete subclasses must provide implementations for all of the methods in its CHtmlSysXxx abstract base class.

    The reason that the CHtmlSysXxx classes are designed as abstract interfaces is so that you can use a third-party class framework to build your version of HTML TADS, if you want. If your system (or compiler) has an application framework, there's probably a class in the framework that corresponds roughly to each of the CHtmlSysXxx interfaces. To use these interfaces with your framework, you can use multiple inheritance.

    A word on multiple inheritance: A lot of people dislike multiple inheritance, or have heard that it's a bad thing, or feel that C++'s implementation of multiple inheritance is flawed; but this is a case where it's actually quite useful and reasonably straightforward to use. Since the CHtmlSysXxx classes are abstract, and because they are stand-alone classes without any base classes, they can be "mixed in" to other classes without much chance of an inheritance conflict, and without placing any requirements on the design of the rest of your class hierarchy. In particular, you won't need to use C++'s "virtual" inheritance feature, which is probably where most people's misgivings about C++ multiple inheritance come from.

    If you're using a framework, you should find the framework class that corresponds to each of the CHtmlSysXxx interfaces. Then, you should create a subclass of your framework class that inherits from both the framework class and the CHtmlSysXxx interface. For example, suppose that your framework has a class called TWindow that defines the basic window type. (If there's a framework subclass of TWindow that would be more appropriate, such as TScrollingWindow, you should use that as the base class. You don't have to use the bare window class just because CHtmlSysWin defines the interface to a window -- make your framework usage decisions just as though the CHtmlSysXxx interfaces were not involved.) You'd make a TWindow subclass that also inherits from CHtmlSysXxx:

       class CHtmlSysWin_mac: public TWindow, public CHtmlSysWin
       {
        ...
       };
    

    Your framework will probably require that you provide implementations for some methods inherited from TWindow; you should define these as you would for any other application.

    In addition, you must provide implementations for all of the functions in CHtmlSysWin. The htmlsys.h header file provides comments that document the functions that these methods are meant to provide.

    Here's a summary of the classes in htmlsys.h that you will need to implement.

    • CHtmlSysFont: system font object. This provides an portable interface to a font.
    • CHtmlSysFrame: system application frame object. This object provides a logical container for the HTML display; this object doesn't have to correspond to anything that's actually displayed, but rather is a programmatic object that the TADS interpreter uses to perform operations involving the display. When your application starts up, you will need to create exactly one object that implements this interface. The main purpose of this object is to route the TADS interpreter engine's input and output functions to the main HTML display window.

      Pay careful attention to the comments in htmlsys.h regarding CHtmlSysFrame::set_frame_obj().

      This interface is probably the most flexible (and thus ambiguous) in terms of how you will go about implementing it, because it doesn't correspond directly to any display element. In my Windows implementation, this interface is implemented by a system-specific window object that serves as the top-level frame window. Since this frame window contains the main game HTML window, it can easily route the CHtmlSysFrame functions to the main HTML window. Depending on your implementation design, it may be more appropriate to implement this interface in something like an "Application" object, or even in a completely abstract singleton object that you create purely for this interface.

    • CHtmlSysWin: system window object. This is the interface that HTML TADS uses to control the display. You will generally create one of these objects to serve as the main game window; additionally, the formatter may create additional CHtmlSysWin objects to display banner windows.
    • CHtmlSysImageJpeg: system JPEG image resource object. You must implement interface to provide a display object for JPEG images. Note that there is a corresponding portable class, CHtmlJpeg, which handles the details of loading a JPEG image from a file; you won't need to port CHtmlJpeg. You will need to port CHtmlSysImageJpeg, which is responsible for converting the portable data representation that CHtmlJpeg uses into a suitable system-specific object that you can display on your system.
    • CHtmlSysImagePng: system PNG image object. This is the PNG image equivalent of CHtmlSysImageJpeg. The corresponding portable class, CHtmlPng, handles the details of reading a PNG file; you must implement CHtmlSysImagePng to convert the portable representation used in CHtmlPng into a system-specific display object.
    • CHtmlSysImageMng: system MNG image object. This is the MNG image class.

    Note that there also a few classes defined in htmlsys.h that you won't have to implement. Some of these are portable classes that are used simply to parameterize some of the methods of the other classes; they're defined here because their main function is to work with these classes. Others are base classes for more specialized interfaces, so don't need to be implemented directly.

    • CHtmlFontDesc: font description. This is a portable class that's used by the portable code to describe a font to the system code; this class is used to parameterize certain methods in system objects.
    • CHtmlFontMetrics: font metrics. This class is used as a portable representation of certain information about a font, and is used to parameterize system object methods.
    • CHtmlSysResource: system resource object. This object provides an interface to system-specific display objects that come from external resources, such as JPEG images. This is a base class for more specific interfaces, so you won't need to implement this interface directly.
    • CHtmlSysImage: system image resource object. This is a subclass of CHtmlSysResource that serves as the base class for the image interfaces. You won't need to implement this interface directly; it's a base class for more specific interfaces.

    System Object Creation

    Since your system code will be providing implementations of these interfaces in subclasses, the portable code has no way of knowing what the final subclasses are called. Thus, the portable code can't ever create a system object directly; instead, your system code creates all of the system objects.

    Your system code will create some system objects automatically. For example, it will create the CHtmlSysFrame object when the application starts running (the main application entrypoint is itself always system-specific), and it will create the main HTML window (which provides a CHtmlSysWin interface) during initialization as well.

    After startup, the portable code will call methods in existing system objects to create additional system objects. For example, when the formatter needs to create a new banner subwindow, it will call the main HTML window's create_banner_subwin() method; this method, which is implemented by your system code, will create an appropriate final subclass of CHtmlSysWin -- specialized for your system -- and return a pointer to it.

    The TADS Win32 Framework

    Rather than using an existing class framework to develop the Win32 version of HTML TADS, I developed my own framework. Although I designed and implemented this framework specifically for this project, I designed it to operate as a general-purpose Win32 framework. Note that I could just as well have used one of the commercially available third-party C++ frameworks for Windows (such as MFC, the class library Microsoft includes with Visual C++), but I chose to develop my own framework instead for a number of reasons; one of the main reasons is that I wanted to avoid inadvertantly introducing any framework dependencies in the portable design that might have resulted from developing HTML TADS around an existing framework.

    All of the classes with names starting with CTads (such as CTadsWin and CTadsStatusline) belong to the Windows framework.

    If you're using an existing framework to port HTML TADS to your system, you won't need to be concerned at all with the CTadsXxx classes. You don't need to port those classes, because you will simply replace them with the corresponding classes from your framework. In fact, you don't even need to replace the CTadsXxx classes exactly; if your framework is laid out differently from the CTads framework, you should follow the organization that you'd normally follow when developing an application with your framework and ignore the CTadsXxx organization. Remember, your job is to provide implementations of the abstract interfaces defined in htmlsys.h -- nothing from the CTads framework is needed by the portable code.

    If you're not using an existing class framework, you can use the CTadsXxx classes as a starting point. These classes, though, are completely Win32-specific, so they're filled with Win32 API calls. In addition, these classes are not even designed to be called from portable code, so the interfaces these classes expose are themselves closely coupled to the Win32 API; for example, many of the member variables and method parameters use Win32 system datatypes.

    html_os.h

    The normal TADS OS layer has some additional definitions in a header file, os.h. This file has some configuration information needed by the portable code, such as the native C types to use for certain abstract TADS types.

    HTML TADS has a corresponding header file, html_os.h. This file is in the portable directory, but provides platform-specific definitions. You need to edit this file to provide a set of definitions for your platform. You should add a section, enclosed in an appropriate #ifdef for your system, that includes a system-specific file that you create. For Win32, this included file is called hos_w32.h; you should create a file appropriate for your system.

    You should refer to the hos_w32.h to find the set of macros and other definitions that you need to include in your platform-specific header file. Fortunately, this file is considerably simpler than os.h from TADS; one reason is that HTML TADS uses some of the TADS OS layer directly, reducing the amount of new OS layer that needs to be built, and another reason is that C++ is somewhat more standardized than C was in the days when I started TADS.

    Note that html_os.h acts only as a "switchboard" for including the appropriate platform-specific file. Please observe this convention, since it will keep the code (both in html_os.h and in your own system-specific files) easier to read by keeping each platform's code in its own set of files, rather than concatenated together into a huge impenetrable set of #ifdef's. For the Win32 definitions, look in hos_w32.h and hos_w32.cpp in the win32 subdirectory.

    os_get_sysinfo()

    Your system code must define this function:

        int os_get_sysinfo(int code, void *param, long *result);
    
    Refer to the base TADS header file osifc.h for a full description of this function. This is one of the few TADS OS-layer functions that you must define in your system-specific code. The reason this function is part of your system-specific code rather than part of the portable HTML TADS definitions (as are most of the other TADS OS-layer functions) is that this function returns the specific capabilities of your version of HTML TADS. Since each port of HTML TADS may vary in capabilities, HTML TADS cannot at the portable level know which capabilities are implemented for each platform. For example, some ports of HTML TADS may support MIDI files but not WAV files. This function allows the system-specific code to provide the correct information.

    os_dbg_sys_msg()

    Your system code must define this function:

        void os_dbg_sys_msg(const textchar_t *msg);
    

    This routine displays internal debugging messages on the system console. You can provide an empty implementation for this function if you wish; its only function is to help you debug HTML TADS by providing a place for the system to display internal diagnostic information. When you compile the system without TADSHTML_DEBUG defined, this function is not used at all.

    Main entrypoint

    Once you've provided implementations for the CHtmlSysXxx interfaces, you're nearly done. The only thing left is that your system code has control over starting up the application.

    The main entrypoint is system-specific, because each GUI system has its own way of invoking an application and passing start-up paramters to it. There's no CHtmlSysXxx or other portable interface defined for the main entrypoint; this is a totally system-specific function, so you must provide a main entrypoint as appropriate for your operating system.

    Your main entrypoint will undoubtedly have to do some system-specific work to get the application initialized; you should follow the normal application initialization protocol for your system. Among other things, you'll want to parse the command line or read the start-up parameters, or whatever the equivalent is on your system.

    In addition, your main entrypoint is required to do a few specific things to get HTML TADS started. You can refer to the implementation of run_game() in the Windows code (in htmlw32.cpp) for an example of the start-up code. Here's an outline of the steps you need to perform:

    • Call the function CHtmlResType::add_basic_types() (which takes no arguments and returns no value). This function initializes the table of built-in media types (JPEG, PNG, MNG, MIDI, WAV, MPEG Audio, Ogg Vorbis Audio). The media type table is dynamic, so that new media types can be added at run-time; because of this, the table must be initialized with the built-in types at some point during startup. (In the future, it may be possible to add types to the table by looking for external plug-in objects in dynamic-link libraries; no system does this yet, but the table is dynamic to allow for this future enhancement.)
    • Create a parser and a formatter object. You should create a new CHtmlParser object and start it in literal mode, and you should create a new CHtmlFormatterInput object connected to the parser:

          parser = new CHtmlParser(TRUE);
          formatter = new CHtmlFormatterInput(parser);
      
    • Create your application frame object. This must be a class that inherits from CHtmlSysFrame, but the actual subclass is up to you.
    • Connect the parser to your frame object. The application frame object must remember the parser, so that its get_parser() method can return the correct object. How you do this is up to your implementation.
    • Tell the oshtml layer about the frame object. Call CHtmlSysFrame::set_frame_obj() with your frame object as the argument. (You may simply want to do this in the frame object's constructor, since there's always exactly one such object.)
    • Create the main HTML window. The CHtmlSysWin() base class requires the formatter as a parameter, so you must do this after you've created the formatter. You'll probably need to store a pointer to the main window object in your frame object.
    • Call formatter_->set_win(this, &margin_rectangle) from the constructor for your system CHtmlSysWin subclass. (You don't necessarily have to do this during the constructor, but that's probably the most convenient place for it.) The margin_rectangle is a CHtmlRect structure that you initialize with the margins in pixels to use at each edge of the window: so margin_rectangle.left is the number of pixels to leave blank along the left edge of the window, etc.
    • Determine the name of the .GAM file. You should use os_exeseek() with the executable file name and type "TGAM" as parameters, to determine if a .GAM file is embedded in the application's executable file; if so, you don't need to find a separate .GAM file. Otherwise, you will probably want to look for a start-up parameter (such as a command line argument), or prompt the user, to determine the .GAM file's name.
    • Have the formatter initialize an appctxdef structure. This structure is used to let the TADS interpreter system notify the host application (in this case, your HTML TADS implementation) of certain events, such as loading a resource from the .GAM file. The formatter is set up to receive such notifications; you must simply set up an appctxdef structure accordingly. Here's how you do this:

          appctxdef appctx;
          formatter->get_res_finder()->init_appctx(&appctx);
      
    • Set up a C-style main(int argc, char **argv) argument vector. This vector should contain at least one argument, which is the name of the application's executable file; if no other arguments are specified, TADS will try to load the game out of the executable file. Additional arguments are interpreted as though you were running the normal TADS interpreter from a command line; if you're specifying the name of the .GAM file to run, it should be the last argument.
    • Invoke os0main2() with the following parameters: argc (number of elements in argv), argv (array of pointers to argument strings), trdmain (the address of the trdmain function, which is the TADS interpreter's main entrypoint), "" (an empty string), "config.trh" (a string giving the name of the HTML TADS interpreter's configuration options file), and &appctx (the address of your appctxdef structure). This calls TADS to load and run the game. (Note that trdmain is the main TADS entrypoint; os0main2 is an intermediate routine that processes arguments.)

          os0main2(argc, argv, trdmain, "", "config.trh", &appctx);
      

      If os0main2() returns zero, it means that it successfully ran the game; a non-zero value indicates an error.

    • Close and delete your main window. (Depending on how your OS works, you may not need to do anything here, since the reason we're quitting may be that the user has already closed the main window. On Windows, the code checks to see if the main window still exists, and closes it via the Windows API if so.)
    • In the destructor for your class that implements the CHtmlSysWin interface, you should include this line:

          formatter_->unset_win();
      

      This tells the formatter that its associated window is about to be deleted; the formatter disentangles itself from the window at this point to ensure that it doesn't make any references to the window after the window has been deleted.

    • Finally, you must delete the parser and formatter objects you created. The order of operations is a little sensitive, because the parser and formatter are mutually entangled with pointers referencing one another. You should use these steps:

          formatter->release_parser();
          delete parser;
          delete formatter;
      

    Some assumptions about GUI OS architecture

    The portable HTML TADS code makes some assumptions about the architecture of GUI operating systems. These are true of most of the GUI systems I've encountered, but it's worth spelling these out in case (a) you're porting to a non-GUI system, or (b) you're porting to an unusual GUI system that diverges from the typical patterns found in Windows, Macintosh, X, etc.

    Event orientation: One of the main assumptions we make is that we're running on an event-driven system. That is, the operating system maintains an "event" or "message" queue that delivers input to the application. The application's top-level code path is a loop that reads an event/message from the queue, carries out appropriate processing to respond to the event, and loops. The OS generates events to represent user input-device actions (keystrokes, mouse movement, mouse clicks, etc) and higher-level GUI changes (window resizing and moving, redraw needed, etc).

    Event models vary considerably by operating system, so the HTML TADS portable code of necessity leaves it up to the port code to read and handle events. Instead, the portable code provides handlers that you call in the course of processing events. So, you have to provide the "glue" that interacts with the OS-level event system to read and decode events, but in many cases you can invoke a portable routine to carry out the processing necessary to respond to a given event.

    There are two main styles of event model:

    • The "message loop" model: the OS generates events and puts them in an internal queue. The application sits in a loop, reading input from this queue and carrying out the appropriate processing. Mac OS follows this model.
    • The "dispatch" model: the application initializes and registers one or more callback routines with the OS, then calls the OS to tell it to process events. This API call might not return until the application terminates. Inside this API call, the OS itself sits in a loop reading events, and dispatches to the registered application callbacks each time a relevant event arrives. The X Window System follows this model.

    (It's worth pointing out that Windows is a hybrid of the two models. On Windows, the application is responsible for reading events, as in the message loop model, but the application must also register callbacks with the operating system, and the OS can dispatch directly to the callbacks in the course of carrying out arbitrary API calls.)

    Invalidation drawing model: We assume that drawing is driven by the OS through the event model. In particular:

    • We assume that the OS keeps track of an "invalidation" region for each window - a list of areas of the window that have been marked as needing to be redrawn. There are typically two sources of invalidation: (1) the application can explicitly mark parts of the window as invalid, which it does whenever it has updated the underlying data model in such a way that the information on the screen needs to be changed to match; and (2) the OS itself can mark parts of the window as invalid, which it does whenever portions of the window are newly exposed, such as when the user resizes or moves the window, or when another window that was in front of the window has been moved or closed, etc.
    • We assume that the OS will generate "redraw" or "repaint" events. A redraw event is an event telling the application that it's time to redraw a portion of the window that has been marked as invalid. HTML TADS doesn't contain any code that initiates drawing, because we assume that the OS will initiate drawing via these events.

    On most GUI systems, the redrawing model is tightly integrated with the native event system, so HTML TADS doesn't have much choice but to leave this up to the port code. In most cases, the port code in turn simply leaves it up to the OS. If your OS doesn't have a native invalidation/redraw event model, you'll have to provide one of your own. The simplest way to do this would be to explicitly redraw the entire contents of each window every time an "input" call of some kind is performed; you'd do the drawing at the start of the call, before pausing to read input from the user. "Input" calls are times when the application will pause for user interaction, so it's a natural time to make sure everything's up to date on the display.

    In practical terms, you can usually implement "redraw" event handling by doing any necessary OS-level setup, then calling formatter_-<draw() from your CHtmlSysWin subclass. The formatter walks through its display list and calls back to your OS code to do the actual drawing.

    Window management: HTML TADS assumes that someone will be taking care of managing the window system - things like providing a UI for the user to resize and move windows, etc. We assume that this "someone" will be doing all of the necessary invalidations as part of this window management. In almost every case, it's the OS that handles these things, but some older GUIs (early PalmOS versions, for example) are rather limited and foist this stuff off on the application, in which case you'll be responsible for handling this in your porting code. But on any modern GUI, this stuff should come for free. qtads-2.1.7/htmltads/oshtml.cpp000066400000000000000000001212611265017072300164750ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/html/oshtml.cpp,v 1.2 1999/05/17 02:52:22 MJRoberts Exp $"; #endif /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name oshtml.c - interface layer from TADS to TADS-HTML front-end Function This layer takes the place of the OS layer normally used in the TADS runtime. Rather than providing an OS interface, this layer provides an interface the HTML front-end. Notes Modified 11/02/97 MJRoberts - Creation */ #include #include #include #include /* include TADS interfaces that we'll need */ #include #include #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef HTMLSYS_H #include "htmlsys.h" #endif #ifndef HTMLPRS_H #include "htmlprs.h" #endif #ifndef HTMLFMT_H #include "htmlfmt.h" #endif #ifndef HTMLTAGS_H #include "htmltags.h" #endif /* status mode flag */ static int status_mode_ = 0; /* HTML mode flag */ static int html_mode_ = 0; /* input-in-progress flag (for os_gets_timeout) */ static int gets_in_progress_ = FALSE; /* hilite mode - this flags our synthesized hilite attribute */ static int hilite_mode_ = FALSE; /* * EOF input counter. EOF means that the user has closed the UI window, * which leaves the program running without a source of user input. The * input request functions (such as keyboard and event input) return an EOF * status code in this situation, and a well-behaved program will notice * the EOF and gracefully shut itself down. Some programs ignore the EOF * status, though, in which case they'll usually get stuck in an infinite * loop repeatedly asking for input that can never arrive. Since we return * the EOF status from each such input request immediately, this sets up a * loop at 100% CPU - which is especially pernicious in this situation, * since the program no longer as a UI window (that's why we're getting at * EOF the first place) that lets the user see what's wedged and eating all * that CPU. * * To catch such errant programs, we'll count input requests that occur * after EOF; if the program makes too many such requests, we'll kill it. * Some programs legitimately continue running after EOF to finish cleanup * tasks, but a well-behaved program won't poll for input once discovering * that there's no more to be had, so repeated input calls are a sign that * the program isn't handling the EOF properly and needs to be externally * terminated. */ static int eof_input_count_ = 0; /* * Check for an EOF input loop. Each input request function should call * this any time it's about to return EOF. We'll count the request; if too * many input requests occur after EOF, we'll assume the program is * ignoring the EOF code and looping on input, so we'll kill it. */ static void check_eof_loop() { if (++eof_input_count_ > 10000) CHtmlSysFrame::kill_process(); } /* * Buffer containing score information for status line */ static textchar_t status_score_buf_[256]; /* * The system frame object. The OS code MUST create an object implementing * the CHtmlSysFrame interface during program initialization, and call * CHtmlSysFrame::set_frame_obj() to give us a reference to the object. */ CHtmlSysFrame *CHtmlSysFrame::app_frame_ = 0; /* * Enter HTML mode */ void os_start_html() { CHtmlParser *parser; CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); /* if there's no app frame, there's nothing more to do */ if (app_frame == 0) return; /* * Flush buffered text to the parser. We don't need to format it * right now, but we do need to parse it, since we're going to * change the parsing mode. */ app_frame->flush_txtbuf(FALSE, FALSE); /* note that we're in HTML mode */ html_mode_ = 1; /* turn on HTML parsing */ parser = app_frame->get_parser(); parser->obey_whitespace(FALSE, TRUE); parser->obey_markups(TRUE); } /* * exit HTML mode */ void os_end_html() { CHtmlParser *parser; CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); /* if there's no app frame, there's nothing to do */ if (app_frame == 0) return; /* flush buffered text to the parser */ app_frame->flush_txtbuf(FALSE, FALSE); /* note that we're not in HTML mode */ html_mode_ = 0; /* turn off HTML parsing */ parser = app_frame->get_parser(); parser->obey_whitespace(TRUE, TRUE); parser->obey_markups(FALSE); parser->obey_end_markups(FALSE); } /* * set non-stop mode */ void os_nonstop_mode(int flag) { CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); /* tell the app frame to set the mode */ if (app_frame != 0) app_frame->set_nonstop_mode(flag); } /* * check for user break (control-C, etc) */ int os_break() { CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); /* if there's no app frame, there can't be a break */ if (app_frame == 0) return FALSE; /* ask the window to check for a break */ return app_frame->check_break_key(); } /* * display a string in the score area in the status line */ void os_strsc(const char *p) { size_t len; /* * remember the score string - it will be used the next time we * rebuild the status line */ len = get_strlen(p) * sizeof(textchar_t); if (len > sizeof(status_score_buf_)) len = sizeof(status_score_buf_) - sizeof(status_score_buf_[0]); memcpy(status_score_buf_, p, len); status_score_buf_[len / sizeof(status_score_buf_[0])] = '\0'; } /* * internal routine to display HTML tags, regardless of our current * markup obedience mode */ static void oshtml_display_html_tags(const textchar_t *txt) { CHtmlParser *parser; int old_obey, old_obey_end; CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); /* if there's no app frame, there's nothing to do */ if (app_frame == 0) return; /* get the parser */ parser = app_frame->get_parser(); /* * Flush any buffered text to the parser. We don't need to format * it yet, but we do need to make sure it's fully parsed, since * we're may be changing the parsing mode. */ app_frame->flush_txtbuf(FALSE, FALSE); /* obey markups long enough to parse our HTML markup text */ old_obey = parser->get_obey_markups(); old_obey_end = parser->get_obey_end_markups(); parser->obey_markups(TRUE); /* display the output */ app_frame->display_output(txt, strlen(txt)); /* flush the result to the parser, since we may be changing modes */ app_frame->flush_txtbuf(FALSE, FALSE); /* restore markup obedience mode */ parser->obey_markups(old_obey); parser->obey_end_markups(old_obey_end); } /* * internal routine to set the status mode */ static void oshtml_status_mode(int flag) { static const textchar_t banner_start[] = "" ""; static const textchar_t banner_score[] = ""; static const textchar_t banner_end[] = "
    "; /* start or end the status line banner */ if (flag) { /* start the banner */ oshtml_display_html_tags(banner_start); } else { /* add the score to the banner */ oshtml_display_html_tags(banner_score); oshtml_display_html_tags(status_score_buf_); oshtml_display_html_tags(banner_end); } } /* * set the status line mode */ void os_status(int stat) { /* * If we're in HTML mode, don't do any automatic status line * generation -- let the game do the status line the way it wants, * without any predefined handling from the run-time system */ if (html_mode_) return; /* see what mode we're setting */ switch(stat) { case 0: default: /* turn off status line mode */ status_mode_ = 0; oshtml_status_mode(0); break; case 1: /* turn on status line mode */ status_mode_ = 1; oshtml_status_mode(1); break; } } /* * get the status line mode */ int os_get_status() { return status_mode_; } /* * set the score value */ void os_score(int cur, int turncount) { char buf[128]; /* build the default status line score format */ sprintf(buf, "%d/%d", cur, turncount); /* set the score string */ os_strsc(buf); } /* * read a line of input from the keyboard */ unsigned char *os_gets(unsigned char *buf, size_t bufl) { int evt; /* cancel any previous interrupted input */ os_gets_cancel(TRUE); /* call the timeout version with no timeout specified */ evt = os_gets_timeout(buf, bufl, 0, FALSE); /* check for EOF */ if (evt == OS_EVT_EOF) { /* check for an input loop at EOF */ check_eof_loop(); /* indicate EOF via a null buffer return */ return 0; } /* return the buffer */ return buf; } /* * Finish an input that we previously started. */ static void oss_gets_done() { /* if an input line is in progress, end it */ if (gets_in_progress_) { /* if we're not in HTML mode, close the input-font tag */ if (!html_mode_) oshtml_display_html_tags(""); /* the input is no longer in progress */ gets_in_progress_ = FALSE; } } /* * read a line of input from the keyboard, with optional timeout */ int os_gets_timeout(unsigned char *buf, size_t bufl, unsigned long timeout, int use_timeout) { int evt; CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); /* if we have end-of-file on the console, we can't get input */ if (CHtmlSysFrame::eof_on_console()) { /* check for an input loop at EOF */ check_eof_loop(); /* indicate EOF to the caller */ return OS_EVT_EOF; } /* * If we're in non-HTML mode, and we don't have a prior input already * in progress, establish the input font by displaying the proper * tag. */ if (!html_mode_ && !gets_in_progress_) oshtml_display_html_tags(""); /* note that we now have an input in progress */ gets_in_progress_ = TRUE; /* * get input from the app frame - return null if it returns end of * file (i.e., application terminating) */ evt = app_frame->get_input_timeout( (textchar_t *)buf, bufl, timeout, use_timeout); /* if that returned end-of-file, pass that along to our caller */ if (evt == OS_EVT_EOF) { /* check for an input loop at EOF */ check_eof_loop(); /* reutrn the EOF event */ return evt; } /* if we didn't time out, the gets is now completed */ if (evt != OS_EVT_TIMEOUT) oss_gets_done(); /* return the event code */ return evt; } /* * Cancel a previously interrupted input */ void os_gets_cancel(int reset) { CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); /* cancel the input in the app window */ if (app_frame != 0) app_frame->get_input_cancel(reset); /* do our own cleanup on the pending input */ oss_gets_done(); } /* * read a character from the keyboard */ int os_getc() { CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); /* if we have end-of-file on the console, we can't get input */ if (CHtmlSysFrame::eof_on_console()) { static int eof_cmd; /* check for an input loop on EOF */ check_eof_loop(); /* * return (0 + CMD_EOF) sequences as we're called; alternate * between the '\000' character and the CMD_EOF character */ eof_cmd = !eof_cmd; return (eof_cmd ? 0 : CMD_EOF); } /* ask the app frame to get a key */ return textchar_to_int(app_frame->wait_for_keystroke(FALSE)); } /* * Read a character from the keyboard in raw mode. Because the OS code * handles command line editing itself, we presume that os_getc() is * already returning the low-level raw keystroke information, so we'll * just return that same information. */ int os_getc_raw() { return os_getc(); } /* wait for a character to become available from the keyboard */ void os_waitc() { CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); /* tell the app frame to wait for a keystroke */ if (app_frame != 0) app_frame->wait_for_keystroke(TRUE); } /* * get an event */ int os_get_event(unsigned long timeout_in_milliseconds, int use_timeout, os_event_info_t *info) { CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); /* if we have end-of-file on the console, we can't get input */ if (CHtmlSysFrame::eof_on_console()) { /* check for an input loop at EOF */ check_eof_loop(); /* return the EOF status */ return OS_EVT_EOF; } /* ask the app frame for an event and return the result */ return app_frame->get_input_event(timeout_in_milliseconds, use_timeout, info); } /* * clear the screen */ void oscls() { CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); /* if app frame, we can't do this */ if (app_frame == 0) return; /* tell the app frame to start a new page */ app_frame->start_new_page(); } /* * flush any buffered display output */ void os_flush() { CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); /* tell the app frame to flush its buffers */ if (app_frame != 0) app_frame->flush_txtbuf(TRUE, FALSE); } /* * immediately update the display */ void os_update_display() { CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); /* * tell the app frame to flush its buffers and immediately update the * display */ if (app_frame != 0) app_frame->flush_txtbuf(TRUE, TRUE); } /* * Set text attributes. We can ignore these if we're in HTML mode, * because we can count on the caller sending us HTML markups directly. * When we're not in HTML mode, though, we need to supply the appropriate * formatting. */ void os_set_text_attr(int attrs) { /* * ignore attribute settings in HTML mode - calls must use appropriate * HTML tags instead */ if (html_mode_) return; /* check for highlighting */ if (((attrs & OS_ATTR_HILITE) != 0) != (hilite_mode_ != 0)) { /* remember the new mode */ hilite_mode_ = ((attrs & OS_ATTR_HILITE) != 0); /* add an in-line highlighting mode switch */ oshtml_display_html_tags(hilite_mode_ ? "" : ""); } } /* * Set the text color. */ void os_set_text_color(os_color_t, os_color_t) { /* we ignore this - callers must use HTML tags to set colors */ } /* * Set the screen color. */ void os_set_screen_color(os_color_t color) { /* we ignore this - callers must use HTML tags to set colors */ } /* * use plain ascii mode for the display */ void os_plain() { /* ignore this -- we can only use the HTML mode */ } /* * write a string */ void os_printz(const char *str) { /* use our base counted-length writer */ os_print(str, strlen(str)); } void os_print(const char *str, size_t len) { CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); /* check our status mode */ switch(status_mode_) { case 0: /* display the entire string normally */ break; case 1: /* skip any leading newlines */ for ( ; len != 0 && *str == '\n' ; ++str, --len) ; /* stop at the first newline after any other characters */ if (len != 0) { const char *p; size_t rem; /* scan for a newline */ for (p = str, rem = len ; rem != 0 && *p != '\n' ; ++p, --rem) ; /* if we found one, note it */ if (rem != 0 && *p == '\n') { /* switch to status mode 2 for subsequent output */ status_mode_ = 2; /* display only the part before the newline */ len = p - str; } } break; case 2: default: /* * suppress everything in status mode 2 - this is the part after * the initial line of status text, which is hidden until we * explicitly return to the main text area by switching to status * mode 0 */ return; } /* if app frame, we can't display anything */ if (app_frame == 0) return; /* display the string */ app_frame->display_output(str, len); } /* * show the MORE prompt and wait for the user to respond */ void os_more_prompt() { CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); /* if app frame, we can't do this */ if (app_frame == 0) return; /* flush the output, and make sure it's displayed */ app_frame->flush_txtbuf(TRUE, FALSE); /* display the MORE prompt */ app_frame->pause_for_more(); } /* * pause prior to exit */ void os_expause() { CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); /* if app frame, we can't do this */ if (app_frame == 0) return; /* flush the output, and make sure it's displayed */ app_frame->flush_txtbuf(TRUE, FALSE); /* display an exit message in the status line, and wait for a key */ app_frame->pause_for_exit(); } /* * printf to debug log window - map the osifc versions to our internal * versions */ void os_dbg_printf(const char *fmt, ...) { va_list argptr; va_start(argptr, fmt); oshtml_dbg_vprintf(fmt, argptr); va_end(argptr); } void os_dbg_vprintf(const char *fmt, va_list argptr) { oshtml_dbg_vprintf(fmt, argptr); } /* * printf to debug log window */ void oshtml_dbg_printf(const char *fmt, ...) { va_list argptr; va_start(argptr, fmt); oshtml_dbg_vprintf(fmt, argptr); va_end(argptr); } void oshtml_dbg_vprintf(const char *fmt, va_list argptr) { char buf[1024]; char *p; CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); /* format the message */ vsprintf(buf, fmt, argptr); /* * Remove any bold on/off sequences from the buffer. Bold sequences * are interpreted by the HTML parser as tag open/close sequences, * so they can cause weird problems. */ for (p = buf ; *p != '\0' ; ++p) { /* if it's a bold on/off sequence, convert it to a space */ if (*p == 1 || *p == 2) *p = ' '; } /* display it */ if (app_frame != 0) { app_frame->dbg_print(buf); } #ifdef TADSHTML_DEBUG else { /* no app frame - use the system debug console instead */ os_dbg_sys_msg(buf); } #endif /* TADSHTML_DEBUG */ } /* * We can ignore title settings from the high-level output formatter * layer, since we parse the title tag ourselves in the underlying HTML * layer. */ void os_set_title(const char *) { /* nothing to do */ } /* * HTML 4 character set translation. This translates to the plain ASCII * 7-bit character set. For the HTML interpreter, we only need this for * translations to log files, since we handle the display in the HTML * layer. */ void os_xlat_html4(unsigned int html4_char, char *result, size_t result_len) { /* default character to use for unknown charaters */ #define INV_CHAR "?" /* * Translation table - provides mappings for characters the ISO * Latin-1 subset of the HTML 4 character map (values 128-255). * * Characters marked "(approx)" are approximations where the actual * desired character is not available in the DOS console character * set, but a reasonable approximation is used. Characters marked * "(approx unaccented)" are accented characters that are not * available; these use the unaccented equivalent as an * approximation, since this will presumably convey more meaning * than a blank. * * Characters marked "(n/a)" have no equivalent (even approximating) * in the DOS character set, and are mapped to spaces. * * Characters marked "(not used)" are not used by HTML '&' markups. */ static const char *xlat_tbl[] = { INV_CHAR, /* 128 (not used) */ INV_CHAR, /* 129 (not used) */ "'", /* 130 - sbquo (approx) */ INV_CHAR, /* 131 (not used) */ "\"", /* 132 - bdquo (approx) */ INV_CHAR, /* 133 (not used) */ INV_CHAR, /* 134 - dagger (n/a) */ INV_CHAR, /* 135 - Dagger (n/a) */ INV_CHAR, /* 136 (not used) */ INV_CHAR, /* 137 - permil (n/a) */ INV_CHAR, /* 138 (not used) */ "<", /* 139 - lsaquo (approx) */ INV_CHAR, /* 140 - OElig (n/a) */ INV_CHAR, /* 141 (not used) */ INV_CHAR, /* 142 (not used) */ INV_CHAR, /* 143 (not used) */ INV_CHAR, /* 144 (not used) */ "'", /* 145 - lsquo (approx) */ "'", /* 146 - rsquo (approx) */ "\"", /* 147 - ldquo (approx) */ "\"", /* 148 - rdquo (approx) */ INV_CHAR, /* 149 (not used) */ "-", /* 150 - endash */ "--", /* 151 - emdash */ INV_CHAR, /* 152 (not used) */ "(tm)", /* 153 - trade (approx) */ INV_CHAR, /* 154 (not used) */ ">", /* 155 - rsaquo (approx) */ INV_CHAR, /* 156 - oelig (n/a) */ INV_CHAR, /* 157 (not used) */ INV_CHAR, /* 158 (not used) */ "Y", /* 159 - Yuml (approx unaccented) */ INV_CHAR, /* 160 (not used) */ "!", /* 161 - iexcl */ "c", /* 162 - cent */ "#", /* 163 - pound */ INV_CHAR, /* 164 - curren (n/a) */ INV_CHAR, /* 165 - yen */ "|", /* 166 - brvbar */ INV_CHAR, /* 167 - sect (n/a) */ INV_CHAR, /* 168 - uml (n/a) */ "(c)", /* 169 - copy (approx) */ INV_CHAR, /* 170 - ordf */ "<<", /* 171 - laquo */ INV_CHAR, /* 172 - not */ " ", /* 173 - shy (n/a) */ "(R)", /* 174 - reg (approx) */ INV_CHAR, /* 175 - macr (n/a) */ INV_CHAR, /* 176 - deg */ "+/-", /* 177 - plusmn */ INV_CHAR, /* 178 - sup2 */ INV_CHAR, /* 179 - sup3 (n/a) */ "'", /* 180 - acute (approx) */ INV_CHAR, /* 181 - micro */ INV_CHAR, /* 182 - para (n/a) */ INV_CHAR, /* 183 - middot */ ",", /* 184 - cedil (approx) */ INV_CHAR, /* 185 - sup1 (n/a) */ INV_CHAR, /* 186 - ordm */ ">>", /* 187 - raquo */ "1/4", /* 188 - frac14 */ "1/2", /* 189 - frac12 */ "3/4", /* 190 - frac34 (approx) */ "?", /* 191 - iquest */ "A", /* 192 - Agrave (approx unaccented) */ "A", /* 193 - Aacute (approx unaccented) */ "A", /* 194 - Acirc (approx unaccented) */ "A", /* 195 - Atilde (approx unaccented) */ "A", /* 196 - Auml */ "A", /* 197 - Aring */ "AE", /* 198 - AElig */ "C", /* 199 - Ccedil */ "E", /* 200 - Egrave (approx unaccented) */ "E", /* 201 - Eacute */ "E", /* 202 - Ecirc (approx unaccented) */ "E", /* 203 - Euml (approx unaccented) */ "I", /* 204 - Igrave (approx unaccented) */ "I", /* 205 - Iacute (approx unaccented) */ "I", /* 206 - Icirc (approx unaccented) */ "I", /* 207 - Iuml (approx unaccented) */ INV_CHAR, /* 208 - ETH (n/a) */ "N", /* 209 - Ntilde */ "O", /* 210 - Ograve (approx unaccented) */ "O", /* 211 - Oacute (approx unaccented) */ "O", /* 212 - Ocirc (approx unaccented) */ "O", /* 213 - Otilde (approx unaccented) */ "O", /* 214 - Ouml */ "x", /* 215 - times (approx) */ "O", /* 216 - Oslash (approx unaccented) */ "U", /* 217 - Ugrave (approx unaccented) */ "U", /* 218 - Uacute (approx unaccented) */ "U", /* 219 - Ucirc (approx unaccented) */ "U", /* 220 - Uuml */ "Y", /* 221 - Yacute (approx unaccented) */ INV_CHAR, /* 222 - THORN (n/a) */ INV_CHAR, /* 223 - szlig (approx) */ "a", /* 224 - agrave */ "a", /* 225 - aacute */ "a", /* 226 - acirc */ "a", /* 227 - atilde (approx unaccented) */ "a", /* 228 - auml */ "a", /* 229 - aring */ "ae", /* 230 - aelig */ "c", /* 231 - ccedil */ "e", /* 232 - egrave */ "e", /* 233 - eacute */ "e", /* 234 - ecirc */ "e", /* 235 - euml */ "i", /* 236 - igrave */ "i", /* 237 - iacute */ "i", /* 238 - icirc */ "i", /* 239 - iuml */ INV_CHAR, /* 240 - eth (n/a) */ "n", /* 241 - ntilde */ "o", /* 242 - ograve */ "o", /* 243 - oacute */ "o", /* 244 - ocirc */ "o", /* 245 - otilde (approx unaccented) */ "o", /* 246 - ouml */ INV_CHAR, /* 247 - divide */ "o", /* 248 - oslash (approx unaccented) */ "u", /* 249 - ugrave */ "u", /* 250 - uacute */ "u", /* 251 - ucirc */ "u", /* 252 - uuml */ "y", /* 253 - yacute (approx unaccented) */ INV_CHAR, /* 254 - thorn (n/a) */ "y" /* 255 - yuml */ }; /* * Map certain extended characters into our 128-255 range. If we * don't recognize the character, return the default invalid * charater value. */ if (html4_char > 255) { switch(html4_char) { case 338: html4_char = 140; break; case 339: html4_char = 156; break; case 376: html4_char = 159; break; case 352: html4_char = 154; break; case 353: html4_char = 138; break; case 8211: html4_char = 150; break; case 8212: html4_char = 151; break; case 8216: html4_char = 145; break; case 8217: html4_char = 146; break; case 8218: html4_char = 130; break; case 8220: html4_char = 147; break; case 8221: html4_char = 148; break; case 8222: html4_char = 132; break; case 8224: html4_char = 134; break; case 8225: html4_char = 135; break; case 8240: html4_char = 137; break; case 8249: html4_char = 139; break; case 8250: html4_char = 155; break; case 8482: html4_char = 153; break; default: /* unmappable character */ result[0] = INV_CHAR[0]; result[1] = '\0'; return; } } /* * if the character is in the regular ASCII zone, return it * untranslated */ if (html4_char < 128) { result[0] = (unsigned char)html4_char; result[1] = '\0'; return; } /* look up the character in our table and return the translation */ strcpy(result, xlat_tbl[html4_char - 128]); } /* ------------------------------------------------------------------------ */ /* * External banner interface */ /* * create an external banner window */ void *os_banner_create(void *parent_handle, int where, void *other_handle, int wintype, int align, int siz, int siz_units, unsigned long style) { CHtmlFormatterBannerExt *parent_fmt; CHtmlFormatterBannerExt *other_fmt; /* * the 'parent' and 'other' handles are simply CHtmlFormatterBannerExt * pointers */ parent_fmt = (CHtmlFormatterBannerExt *)parent_handle; other_fmt = (CHtmlFormatterBannerExt *)other_handle; /* * Create and return the new formatter. Simply use the pointer to the * CHtmlFormatterBannerExt object as the handle. */ return CHtmlFormatterBannerExt::create_extern_banner( parent_fmt, where, other_fmt, wintype, (HTML_BannerWin_Pos_t)align, siz, siz_units, style); } /* * delete an external banner window */ void os_banner_delete(void *banner_handle) { CHtmlFormatterBannerExt *banner_fmt; /* the banner handle is the CHtmlFormatterBannerExt object */ banner_fmt = (CHtmlFormatterBannerExt *)banner_handle; /* delete the banner */ CHtmlFormatterBannerExt::delete_extern_banner(banner_fmt); } /* * orphan an external banner window */ void os_banner_orphan(void *banner_handle) { CHtmlSysFrame *app_frame = CHtmlSysFrame::get_frame_obj(); CHtmlFormatterBannerExt *banner_fmt; /* if there's no app frame, simply delete the banner */ if (app_frame == 0) { os_banner_delete(banner_handle); return; } /* the banner handle is the CHtmlFormatterBannerExt object */ banner_fmt = (CHtmlFormatterBannerExt *)banner_handle; /* let the application frame object handle it */ app_frame->orphan_banner_window(banner_fmt); } /* * display output on a banner */ void os_banner_disp(void *banner_handle, const char *txt, size_t len) { CHtmlFormatterBannerExt *fmt; /* the banner handle is simply the CHtmlFormatterBannerExt pointer */ fmt = (CHtmlFormatterBannerExt *)banner_handle; /* ask the formatter to handle it */ fmt->add_source_text(txt, len); } /* * flush output on a banner */ void os_banner_flush(void *banner_handle) { CHtmlFormatterBannerExt *fmt; /* the banner handle is simply the CHtmlFormatterBannerExt pointer */ fmt = (CHtmlFormatterBannerExt *)banner_handle; /* ask the formatter to handle it */ fmt->flush_txtbuf(TRUE); } /* * set the banner size */ void os_banner_set_size(void *banner_handle, int siz, int siz_units, int is_advisory) { CHtmlFormatterBannerExt *fmt; /* * If this is advisory only, ignore it - we fully support * size-to-contents, which the caller is indicating they will * eventually call, so there's no need to set this estimated size. * It's actually better for us not to set the estimated size, because * doing so could redundantly resize the window, making for excessive * redrawing. */ if (is_advisory) return; /* the banner handle is simply the CHtmlFormatterBannerExt pointer */ fmt = (CHtmlFormatterBannerExt *)banner_handle; /* set the size */ fmt->set_banner_size(siz, siz_units); } /* * set the banner to the size of its current contents in one or both * dimension */ void os_banner_size_to_contents(void *banner_handle) { CHtmlFormatterBannerExt *fmt; /* the banner handle is simply the CHtmlFormatterBannerExt pointer */ fmt = (CHtmlFormatterBannerExt *)banner_handle; /* make sure we've flushed any buffered text */ fmt->flush_txtbuf(TRUE); /* size the window to its current contents */ fmt->size_to_contents(TRUE, TRUE); } /* * turn HTML mode on in the banner */ void os_banner_start_html(void *banner_handle) { CHtmlFormatterBannerExt *fmt; /* the banner handle is simply the CHtmlFormatterBannerExt pointer */ fmt = (CHtmlFormatterBannerExt *)banner_handle; /* flush buffered text */ os_banner_flush(banner_handle); /* turn on HTML parsing in the banner */ fmt->set_html_mode(TRUE); } /* * turn HTML mode off in the banner */ void os_banner_end_html(void *banner_handle) { CHtmlFormatterBannerExt *fmt; /* the banner handle is simply the CHtmlFormatterBannerExt pointer */ fmt = (CHtmlFormatterBannerExt *)banner_handle; /* flush buffered text */ os_banner_flush(banner_handle); /* turn off HTML parsing in the banner */ fmt->set_html_mode(FALSE); } /* * Set an attribute in the banner */ void os_banner_set_attr(void *banner_handle, int attrs) { CHtmlFormatterBannerExt *fmt; /* the banner handle is simply the CHtmlFormatterBannerExt pointer */ fmt = (CHtmlFormatterBannerExt *)banner_handle; /* set the attribute in the banner */ fmt->set_banner_text_attr(attrs); } /* * Set the text color in a banner, for subsequent text displays. We'll * ignore this in HTML mode, as HTML must be used instead. */ void os_banner_set_color(void *banner_handle, os_color_t fg, os_color_t bg) { CHtmlFormatterBannerExt *fmt; /* the banner handle is simply the CHtmlFormatterBannerExt pointer */ fmt = (CHtmlFormatterBannerExt *)banner_handle; /* set the color in the banner */ fmt->set_banner_text_color(fg, bg); } /* * Set the screen color in the banner. Ignore this in the HTML * interpreter: HTML formatting must be used. */ void os_banner_set_screen_color(void *banner_handle, os_color_t color) { CHtmlFormatterBannerExt *fmt; /* the banner handle is simply the CHtmlFormatterBannerExt pointer */ fmt = (CHtmlFormatterBannerExt *)banner_handle; /* set the screen color in the banner */ fmt->set_banner_screen_color(color); } /* * Clear a banner */ void os_banner_clear(void *banner_handle) { CHtmlFormatterBannerExt *fmt; /* the banner handle is simply the CHtmlFormatterBannerExt pointer */ fmt = (CHtmlFormatterBannerExt *)banner_handle; /* flush any buffered text */ os_banner_flush(banner_handle); /* clear the formatter */ fmt->clear_contents(); } /* * Get the width and height of the banner, in character cells. This is not * meaningful for HTML mode, because the HTML interpreters can use * proportional fonts and different fonts of different rendered sizes. */ int os_banner_get_charwidth(void *) { return 0; } int os_banner_get_charheight(void *) { return 0; } /* * Get information on the banner */ int os_banner_getinfo(void *banner_handle, os_banner_info_t *info) { CHtmlFormatterBannerExt *fmt; HTML_BannerWin_Pos_t pos; CHtmlSysWin *win; CHtmlPoint siz; CHtmlRect margins; /* the banner handle is simply the CHtmlFormatterBannerExt pointer */ fmt = (CHtmlFormatterBannerExt *)banner_handle; /* if there's no window, there's no information */ if ((win = fmt->get_win()) == 0) return FALSE; /* get the information from the window */ win->get_banner_info(&pos, &info->style); /* get the pixel width and height of the window */ info->pix_width = win->get_disp_width(); info->pix_height = win->get_disp_height(); /* measure the size of a "0" character in the default font */ siz = win->measure_text(fmt->get_font(), "0", 1, 0); /* get the formatter's margins */ margins = fmt->get_phys_margins(); /* * Approximate the character size of the window using the "0" size. * Don't count the formatter's margins in the row/column size, since * what we really want to know is how much text we could fit in the * window without any scrolling. */ info->rows = (info->pix_height - margins.top - margins.bottom) / siz.y; info->columns = (info->pix_width - margins.left - margins.right) / siz.x; /* * perform the trivial type conversion from HTML_BannerWin_Pos_t to * the OS_BANNER_ALIGN_xxx system (the identifiers correspond directly * between the two systems; the HTML subsystem's 'enum' is purely * cosmetic) */ info->align = (int)pos; /* * it's an HTML window, so it does its own line wrapping at the OS * level; the caller should not do line wrapping on text sent our way */ info->os_line_wrap = TRUE; /* indicate that we filled in the information */ return TRUE; } /* * Set the output coordinates in a text grid banner */ void os_banner_goto(void *banner_handle, int row, int col) { CHtmlFormatterBannerExt *fmt; /* the banner handle is simply the CHtmlFormatterBannerExt pointer */ fmt = (CHtmlFormatterBannerExt *)banner_handle; /* flush any buffered text */ os_banner_flush(banner_handle); /* move the cursor */ fmt->set_cursor_pos(row, col); } qtads-2.1.7/htmltads/tadshtml.cpp000066400000000000000000000071721265017072300170130ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/html/tadshtml.cpp,v 1.3 1999/07/11 00:46:41 MJRoberts Exp $"; #endif /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tadshtml.cpp - utility functions for TADS HTML engine Function Notes Modified 08/26/97 MJRoberts - Creation */ #include #include #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef HTMLSYS_H #include "htmlsys.h" #endif #ifndef HTML_OS_H #include "html_os.h" #endif /* ------------------------------------------------------------------------ */ /* * Text stream buffer. This buffer accumulates output for eventual * submission to the parser. */ /* * Append text to the end of the buffer */ void CHtmlTextBuffer::append(const textchar_t *txt, size_t len) { /* make sure we have enough space allocated */ if (buf_ == 0) { /* nothing allocated yet - allocate the initial buffer space */ bufalo_ = init_alloc_unit; buf_ = (textchar_t *)th_malloc(bufalo_ * sizeof(textchar_t)); /* nothing in the buffer yet - the end is the beginning */ bufend_ = buf_; } else if (getlen() + len > bufalo_) { size_t curlen; /* remember the current length, so we can reposition bufend_ later */ curlen = getlen(); /* allocate the additional space */ bufalo_ += extra_alloc_unit; buf_ = (textchar_t *)th_realloc(buf_, bufalo_ * sizeof(textchar_t)); /* update the end pointer, in case the buffer moved */ bufend_ = buf_ + curlen; } /* copy the text into the buffer */ memcpy(bufend_, txt, len * sizeof(*txt)); /* remember the new end pointer */ bufend_ += len; } /* ------------------------------------------------------------------------ */ /* * Counted-length string pointer */ textchar_t CCntlenStrPtr::nextchar() const { /* * if we have another character after the current character, return * it, otherwise return a null character */ return (textchar_t)(len_ > 1 ? *(ptr_ + 1) : 0); } /* ------------------------------------------------------------------------ */ /* * Safe strcpy */ void safe_strcpy(textchar_t *dst, size_t dstsize, const textchar_t *src, size_t srclen) { size_t copylen; /* if the destination buffer is zero-size, we can't do anything */ if (dstsize == 0) return; /* assume we'll copy the whole source */ copylen = srclen; /* limit to the available destination space less null termination */ if (copylen > dstsize - 1) copylen = dstsize - 1; /* copy the characters */ memcpy(dst, src, copylen * sizeof(src[0])); /* null-terminate */ dst[copylen] = '\0'; } /* ------------------------------------------------------------------------ */ /* * Get the rightmost instance of a substring in a string */ textchar_t *get_strrstr(const textchar_t *str, const textchar_t *sub) { const char *p; size_t slen = get_strlen(str); size_t sublen = get_strlen(sub); /* * if the substring is longer than the string, we're obviously not * going to find it */ if (sublen > slen) return 0; /* scan from the end of the string backwards */ for (p = str + slen - sublen + 1 ; p != str ; ) { /* back one character */ --p; /* check for a match */ if (memcmp(p, sub, sublen) == 0) return (char *)p; } /* not found */ return 0; } qtads-2.1.7/htmltads/tadshtml.h000066400000000000000000001123031265017072300164510ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/html/tadshtml.h,v 1.3 1999/07/11 00:46:41 MJRoberts Exp $ */ /* * Copyright (c) 1997 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tadshtml.h - definitions for TADS HTML renderer Function Notes Modified 08/23/97 MJRoberts - Creation */ #include #include #include #include #ifndef TADSHTML_H #define TADSHTML_H /* include the TADS OS header, to allow for global OS-specific definitions */ #include /* * Debug support */ //#define TADSHTML_DEBUG #ifdef TADSHTML_DEBUG #define HTML_IF_DEBUG(x) x #else #define HTML_IF_DEBUG(x) #endif /* ------------------------------------------------------------------------ */ /* * Some compilers (notably gcc >= 4.7.1) require that overloads for the * basic 'new' and 'delete' operators be declared with 'throw' clauses to * match the standard C++ library (that is, 'new' must be declared to throw * std::bad_alloc, and 'delete' must be declared to throw nothing). * Naturally, some other compilers (notably MSVC 2003) don't want the * 'throw' clauses. If your compiler wants the 'throw' declarations, * define NEW_DELETE_NEED_THROW in your makefile, otherwise omit it. Note * that some compilers (notably gcc < 4.7.1) don't care one way or the * other, so if your compiler doesn't complain, you probably don't need to * worry about this setting. */ #ifndef SYSTHROW #ifdef NEW_DELETE_NEED_THROW #define SYSTHROW(exc) exc #else #define SYSTHROW(exc) #endif #endif /* ------------------------------------------------------------------------ */ /* * Basic portable types and macros */ /* * Text character type. We define this as an explicit type to allow for * eventual migration to Unicode or another more readily * internationalizable (than plain old 'char') character representation. */ typedef char textchar_t; /* number of elements in an array */ #ifndef countof #define countof(x) (sizeof(x)/sizeof((x)[0])) #endif /* ------------------------------------------------------------------------ */ /* * Include the HTML TADS OS supplement. (We make a point of including this * here, rather than earlier, because it might depend on the basic types we * defined above.) */ #include "html_os.h" /* ------------------------------------------------------------------------ */ /* * Debugging covers for standard memory allocation routines. These * covers allow us to track allocations and deallocations so that we can * ensure that we don't leak any memory. */ /* * Debug console print routine. These print messages to a suitable * console window. (These functions are implemented in the portable * code, and call routines provided by the CHtmlSysFrame interface * object.) */ void oshtml_dbg_printf(const char *fmt, ...); void oshtml_dbg_vprintf(const char *fmt, va_list argptr); #ifdef TADSHTML_DEBUG #include void *th_malloc(size_t siz); void *th_realloc(void *oldptr, size_t siz); void th_free(void *ptr); void *operator new(size_t siz) SYSTHROW(throw (std::bad_alloc)); void *operator new[](size_t siz) SYSTHROW(throw (std::bad_alloc)); void operator delete(void *ptr) SYSTHROW(throw ()); void operator delete[](void *ptr) SYSTHROW(throw ()); /* * List all allocated memory blocks - displays heap information on * stdout. This can be called at program termination to detect unfreed * memory blocks, the existence of which could indicate a memory leak. */ void th_list_memory_blocks(); /* * List all allocated memory blocks in the external subsystem. This must * be redirected in the tads 2 or tads 3 implementation to use the memory * lister for that subsystem. */ void th_list_subsys_memory_blocks(); #else /* TADSHTML_DEBUG */ /* non-debug version - use standard library routines */ #define th_malloc(siz) malloc(siz) #define th_realloc(ptr, siz) realloc(ptr, siz) #define th_free(ptr) free(ptr) #endif /* TADSHTML_DEBUG */ /* ------------------------------------------------------------------------ */ /* * cast a textchar_t to an int - this can be used to get a "wide" * representation of a character. */ inline int textchar_to_int(textchar_t c) { /* * cast it by way of unsigned char, to ensure that we don't * sign-extend 8-bit values */ return (int)(unsigned char)c; } /* * Character classification functions. We'll use ctype.h for now, but * we will want to replace these in the Unicode future. */ inline int is_alpha(textchar_t c) { return ((unsigned char)c < 127 && isalpha(c)); } inline int is_space(textchar_t c) { return ((unsigned char)c < 127 && isspace(c)); } inline int is_newline(textchar_t c) { return (c == '\n' || c == '\r'); } inline int is_digit(textchar_t c) { return ((unsigned char)c < 127 && isdigit(c)); } inline int is_hex_digit(textchar_t c) { return ((unsigned char)c < 127 && isxdigit(c)); } inline int is_upper(textchar_t c) { return ((unsigned char)c < 127 && isupper(c)); } inline int is_lower(textchar_t c) { return ((unsigned char)c < 127 && islower(c)); } inline textchar_t to_upper(textchar_t c) { return (unsigned char)c >= 127 ? c : (textchar_t)toupper(c); } inline textchar_t to_lower(textchar_t c) { return (unsigned char)c >= 127 ? c : (textchar_t)tolower(c); } inline int ascii_digit_to_int(textchar_t c) { return c - '0'; } inline int ascii_hex_to_int(textchar_t c) { return (c >= 'a' && c <= 'f' ? c - 'a' + 10 : (c >= 'A' && c <= 'F' ? c - 'A' + 10 : ascii_digit_to_int(c))); } inline textchar_t int_to_ascii_digit(int d) { return (textchar_t)(d + '0'); } inline textchar_t int_to_hex_digit(int d, int caps) { return (textchar_t)(d < 10 ? 'd' + '0' : 'd' + (caps ? 'A' : 'a')); } /* * Get length of a null-terminated string. It's just strlen for now, but * we will want to replace this in the Unicode future. */ inline size_t get_strlen(const textchar_t *str) { return strlen(str); } /* * Copy a null-terminated string. It's just strlen for now, but we'll * want to replace this with an appropriate Unicode function in the * future. */ inline void do_strcpy(textchar_t *dst, const textchar_t *src) { strcpy(dst, src); } /* * "Safe" strcpy - always null-terminates, never overflows. */ void safe_strcpy(textchar_t *dst, size_t dstsize, const textchar_t *src, size_t srclen); inline void safe_strcpy(textchar_t *dst, size_t dstsize, const textchar_t *src) { safe_strcpy(dst, dstsize, src, get_strlen(src)); } /* * Compare two strings */ inline size_t get_strcmp(const textchar_t *a, const textchar_t *b) { return strcmp(a, b); } /* * case insensitive string comparison */ inline size_t get_stricmp(const textchar_t *a, const textchar_t *b) { return stricmp(a, b); } /* * Convert a string to a number. It's just atol for now, but we will * want to replace it for a future Unicode version. */ inline long get_atol(const textchar_t *str) { return atol(str); } /* find the rightmost instance of a string in a string */ textchar_t *get_strrstr(const textchar_t *str, const textchar_t *sub); /* * TRUE and FALSE */ #ifndef FALSE # define FALSE 0 #endif #ifndef TRUE # define TRUE 1 #endif /* ------------------------------------------------------------------------ */ /* * Flat color type -- stores an RGB value in a long. */ typedef long HTML_color_t; /* get the red/green/blue elements out of a color_t */ #define HTML_color_red(c) ((unsigned char)(((c) >> 16) & 0xff)) #define HTML_color_green(c) ((unsigned char)(((c) >> 8) & 0xff)) #define HTML_color_blue(c) ((unsigned char)((c) & 0xff)) /* make an HTML_color_t out of a red, green, and blue triplet */ #define HTML_make_color(r, g, b) \ ((((unsigned long)((r) & 0xff)) << 16) \ | (((unsigned long)((g) & 0xff)) << 8) \ | ((unsigned long)((b) & 0xff))) /* * Special parameterized color values. These color values are used to * store references to color settings that are mapped through a * system-dependent mechanism (see CHtmlSysWin::map_system_color() in * htmlsys.h) to an actual system color. The color mapping mechanism may * use a player-controlled preference mechanism to map the colors, or may * simply choose colors that are suitable for the system color scheme or * display characteristics. * * Note that we can distinguish these color values from actual RGB values * by checking the high-order byte of the 32-bit value. Actual RGB values * always have zero in the high order byte; these special values have a * non-zero value in the high-order byte. */ /* status line background and text colors */ const unsigned long HTML_COLOR_STATUSBG = 0x01000001; const unsigned long HTML_COLOR_STATUSTEXT = 0x01000002; /* link colors - regular, active (i.e., clicked) */ const unsigned long HTML_COLOR_LINK = 0x01000003; const unsigned long HTML_COLOR_ALINK = 0x01000004; /* text color */ const unsigned long HTML_COLOR_TEXT = 0x01000005; /* background color */ const unsigned long HTML_COLOR_BGCOLOR = 0x01000006; /* color of command-line input text */ const unsigned long HTML_COLOR_INPUT = 0x01000007; /* link color - hovering */ const unsigned long HTML_COLOR_HLINK = 0x01000008; /* * determine if a color is a special color; returns true if the color is a * special parameterized color value, false if it's an RGB value */ #define html_is_color_special(color) (((color) & 0xff000000) != 0) /* ------------------------------------------------------------------------ */ /* * resource types */ enum HTML_res_type_t { HTML_res_type_unknown, /* invalid resource type */ HTML_res_type_JPEG, /* JPEG image */ HTML_res_type_PNG, /* PNG image */ HTML_res_type_WAV, /* WAV audio */ HTML_res_type_MP123, /* MPEG 2.0 audio layer 1, 2, and 3 */ HTML_res_type_MIDI, /* MIDI audio */ HTML_res_type_OGG, /* Ogg Vorbis audio */ HTML_res_type_MNG /* MNG animated image */ }; /* ------------------------------------------------------------------------ */ /* * Resource loader function type. This is the interface to the loader * function for a resource type; this function loads the resource from a * file and creates a type-specific and system-dependent C++ object that * handles the resource. The C++ object is a descendant of * CHtmlSysResource (see htmlsys.h). * * Note that 'filesize' is the size of the data starting at 'seekpos' * that the resource loader considers to be part of this resource; the * actual file may be larger than this value, since other data may * follow the resource in the file. * * 'url' is provided for diagnostic purposes; it should be used in any * error message that this routine generates (with oshtml_dbg_printf). * It can otherwise be ignored. * * The 'win' argument is provided so that the loader can check the * current environment for any information necessary for loading the * resource. For example, an image loader may want to call * win->get_use_palette() to determine if the image's color map must be * mapped to a palette. */ typedef class CHtmlSysResource *(*htmlres_loader_func_t)(const class CHtmlUrl *, const textchar_t *, unsigned long, unsigned long, class CHtmlSysWin *); /* ------------------------------------------------------------------------ */ /* * Resource type sensing. We determine the type of an resource based on * the file extension in its URL. The file extension is the part of the * URL after the last period after the last slash (forward or backslash) * or colon. */ class CHtmlResType { public: /* determine the type of a resource given its URL */ static HTML_res_type_t get_res_type(const textchar_t *url); /* get the full mapping information for a resource */ static HTML_res_type_t get_res_mapping(const textchar_t *url, htmlres_loader_func_t *loader_func); /* add a resource mapping */ static void add_res_type(const textchar_t *suffix, HTML_res_type_t res_type, htmlres_loader_func_t create_res_obj); /* * Add the basic resource types - this adds mappings to the system * resource table for all of the built-in resource types (JPEG, PNG, * etc). This must be called once during initialization. */ static void add_basic_types(); /* * delete the resource table - this can be called during program * termination to ensure that the memory associated with the global * resource table is freed before the program exits */ static void delete_res_table(); private: /* resource type mapping table */ static struct ext_map_t *ext_table_; /* number of entries in the mapping table */ static size_t ext_table_cnt_; /* total number of slots (not necessarily all in use) in the table */ static size_t ext_table_size_; }; /* ------------------------------------------------------------------------ */ /* * Standard cursor types. */ enum Html_csrtype_t { HTML_CSRTYPE_ARROW, /* standard arrow cursor */ HTML_CSRTYPE_IBEAM, /* text I-beam cursor */ HTML_CSRTYPE_HAND /* hand with pointy finger ("click to select") */ }; /* ------------------------------------------------------------------------ */ /* * Geometry convenience types */ /* an x,y location */ struct CHtmlPoint { CHtmlPoint() : x(0), y(0) { } CHtmlPoint(long x, long y) : x(x), y(y) { } void set(long x, long y) { this->x = x; this->y = y; } long x; long y; }; /* bounds of a rectangle */ class CHtmlRect { public: CHtmlRect() : left(0), top(0), right(0), bottom(0) { } CHtmlRect(long left, long top, long right, long bottom) : left(left), top(top), right(right), bottom(bottom) { } void set(long left, long top, long right, long bottom) { this->left = left; this->top = top; this->right = right; this->bottom = bottom; } void set(const CHtmlRect *rect) { set(rect->left, rect->top, rect->right, rect->bottom); } void offset(long x, long y) { left += x; right += x; top += y; bottom += y; } /* see if the rectangle contains a given point */ int contains(CHtmlPoint pt) const { return (pt.x >= left && pt.x < right && pt.y >= top && pt.y < bottom); } long left; long top; long right; long bottom; }; /* ------------------------------------------------------------------------ */ /* * Types for tag COORD attributes */ /* maximum number of coordinate scalars in a coordinate list */ const int CHtmlTagAREA_max_coords = 20; /* coordinates structure */ struct CHtmlTagAREA_coords_t { /* value of this scalar in the list */ long val_; /* flag indicating it's a percentage */ int pct_; }; /* ------------------------------------------------------------------------ */ /* * Hyperlink styles. */ /* * Plain link style: show the link as though it's ordinary text, rather * than using a distinctive link appearance. */ #define LINK_STYLE_PLAIN 0 /* * Normal link style: show the link using the standard appearance, but * show it as plain text if links are globally disabled. */ #define LINK_STYLE_NORMAL 1 /* * Forced link style: show the link using the distinctive hyperlink * appearance, even if link visibility is currently disabled globally. * This can be used for special links that are to be visible and active * even when ordinary links are not being shown. */ #define LINK_STYLE_FORCED 2 /* * "Hidden" link style: show the link as ordinary text except when the * mouse is hovering over the link, in which case we'll show it as a link. */ #define LINK_STYLE_HIDDEN 3 /* ------------------------------------------------------------------------ */ /* * Text stream buffer. This buffer accumulates output for eventual * submission to the parser. */ /* initial allocation size - the buffer starts at this size */ const int init_alloc_unit = 16*1024; /* * incremental allocation size - we'll extend the buffer by this amount * each time we run out of space */ const int extra_alloc_unit = 16*1024; class CHtmlTextBuffer { public: CHtmlTextBuffer() { buf_ = 0; bufend_ = 0; bufalo_ = 0; } ~CHtmlTextBuffer() { if (buf_) { th_free(buf_); buf_ = 0; } } /* clear the buffer of all text */ void clear() { bufend_ = buf_; } /* add text to end of the buffer */ void append(const textchar_t *txt, size_t len); void append(const textchar_t *txt) { append(txt, get_strlen(txt)); } /* get the buffer pointer */ const textchar_t *getbuf() const { return buf_; } /* get the number of characters in the buffer */ size_t getlen() const { return bufend_ - buf_; } /* change the length of the buffer */ void setlen(size_t newlen) { bufend_ = buf_ + newlen; } protected: /* pointer to the buffer */ textchar_t *buf_; /* allocated size of the buffer */ size_t bufalo_; /* pointer to the next available free character position in the buffer */ textchar_t *bufend_; }; /* ------------------------------------------------------------------------ */ /* * Simple string buffer class, to simplify string storage management */ class CStringBuf { public: CStringBuf() { buf_ = 0; len_ = 0; slen_ = 0; } CStringBuf(const textchar_t *str) { init(str, get_strlen(str)); } CStringBuf(const textchar_t *str, size_t len) { init(str, len); } CStringBuf(size_t len) { alloc(len); buf_[0] = '\0'; slen_ = 0; } ~CStringBuf() { if (buf_ != 0) delete [] buf_; } void set(const CStringBuf *str) { set(str->get()); } void set(const textchar_t *str) { set(str, str == 0 ? 0 : get_strlen(str)); } void set(const textchar_t *str, size_t len) { if (len_ < len + 1) { clear(); init(str, len); } else store(str, len, 0); } textchar_t *get() const { return buf_; } size_t getlen() const { return slen_; } void setlen(size_t len) { slen_ = len; } /* * get a newly allocated copy of the string (the caller must delete * this with th_free()) */ textchar_t *get_copy() const { /* if there's a string, allocate a copy */ if (buf_ != 0) { /* allocate a new buffer */ textchar_t *s = (textchar_t *)th_malloc(get_strlen(buf_) + 1); /* copy the string into the new buffer */ strcpy(s, buf_); /* return the new buffer */ return s; } else { /* no string; return an empty buffer */ textchar_t *s = (textchar_t *)th_malloc(1); s[0] = '\0'; return s; } } void append(CStringBuf *str) { append(str->get()); } void append(const textchar_t *str) { append(str, get_strlen(str)); } void append(const textchar_t *str, size_t len) { size_t newlen; size_t oldlen = slen_; newlen = oldlen + len + 1; if (len_ < newlen) { /* reallocate the buffer to be big enough */ realloc(newlen); } /* add the new string at the end of the old one */ store(str, len, oldlen); } void append_at(size_t idx, CStringBuf *str) { append_at(idx, str->get()); } void append_at(size_t idx, const textchar_t *str) { append_at(idx, str, get_strlen(str)); } void append_at(size_t idx, const textchar_t *str, size_t len) { /* make sure we have room for the added text */ size_t newlen = idx + len + 1; if (len_ < newlen) realloc(newlen); /* store the new string starting at the given index */ store(str, len, idx); } /* * append, increasing the buffer size by the given increment (rather * than just enough to add the string) if more space is needed */ void append_inc(CStringBuf *str, size_t alloc_len) { append_inc(str->get(), alloc_len); } void append_inc(const textchar_t *str, size_t alloc_len) { append_inc(str, str == 0 ? 0 : get_strlen(str), alloc_len); } void append_inc(const textchar_t *str, size_t len, size_t alloc_inc) { size_t newlen; size_t oldlen = slen_; /* determine the amount of space we need to add the new string */ newlen = oldlen + len + 1; if (len_ < newlen) { /* * increase the space by the increment, if the increment is * larger than the string we're adding */ if (len_ + alloc_inc > newlen) newlen = len_ + alloc_inc; /* reallocate the buffer to be big enough */ realloc(newlen); } /* add the new string at the end of the old one */ store(str, len, oldlen); } /* prepend */ void prepend(CStringBuf *str) { prepend(str->get()); } void prepend(const textchar_t *str) { prepend(str, get_strlen(str)); } void prepend(const textchar_t *str, size_t len) { size_t newlen; size_t oldlen = slen_; /* ensure we have space for the added text */ newlen = oldlen + len + 1; if (len_ < newlen) realloc(newlen); /* move the existing text over to make room */ memmove(buf_ + len, buf_, (slen_ + 1)*sizeof(textchar_t)); /* copy in the new text */ memcpy(buf_, str, len*sizeof(textchar_t)); /* adjust the length */ slen_ += len; } void prepend_inc(CStringBuf *str, size_t alloc_inc) { prepend_inc(str->get(), alloc_inc); } void prepend_inc(const textchar_t *str, size_t alloc_inc) { prepend_inc(str, str == 0 ? 0 : get_strlen(str), alloc_inc); } void prepend_inc(const textchar_t *str, size_t len, size_t alloc_inc) { size_t newlen; size_t oldlen = slen_; /* determine the amount of space we need to add the new string */ newlen = oldlen + len + 1; if (len_ < newlen) { /* * increase the space by the increment, if the increment is * larger than the string we're adding */ if (len_ + alloc_inc > newlen) newlen = len_ + alloc_inc; /* reallocate the buffer to be big enough */ realloc(newlen); } /* prepend the string */ prepend(str, len); } void clear() { len_ = 0; if (buf_ != 0) delete [] buf_; buf_ = 0; slen_ = 0; } /* ensure that our buffer is at least as large as requested */ void ensure_size(size_t min_len) { /* if we're smaller than the requested size, reallocate */ if (len_ < min_len + 1) realloc(min_len + 1); } /* ensure that we can add the given size */ void ensure_added_size(size_t added, size_t inc) { /* make sure we have space for the current plus new contents */ size_t cur = slen_; if (cur + added + 1 > len_) { /* add at least 'inc', up to 'added' */ size_t newlen = cur + (inc > added ? inc : added); /* realloc */ ensure_size(newlen); } } /* * copy a string into the buffer (replacing the current contents), * quoting any XML markup characters */ char *xmlify(const textchar_t *str) { return xmlify(str, str != 0 ? get_strlen(str) : 0); } char *xmlify(const textchar_t *str, size_t len) { const textchar_t *src; textchar_t *dst; size_t dstlen, rem; /* count the length with markup expansion */ for (src = str, rem = len, dstlen = 0 ; rem != 0 ; ++src, --rem) { /* if it's a markup character, count its expanded length */ switch (*src) { case '&': /* & */ dstlen += 5; break; case '>': case '<': /* < > */ dstlen += 4; break; case '"': case '\'': /* " ' */ dstlen +=5; break; default: /* anything else is copied verbatim */ dstlen += 1; } } /* allocate space */ ensure_size(dstlen); /* copy and quote */ for (src = str, dst = buf_, rem = len ; rem != 0 ; ++src, --rem) { /* if it's a markup character, count its expanded length */ switch (*src) { case '&': do_strcpy(dst, "&"); dst += 5; break; case '>': do_strcpy(dst, ">"); dst += 3; break; case '<': do_strcpy(dst, "<"); dst += 3; break; case '"': do_strcpy(dst, """); dst += 5; break; case '\'': do_strcpy(dst, "'"); dst +=5; break; default: *dst++ = *src; break; } } /* null-terminate it */ *dst = '\0'; slen_ = dst - buf_; /* return the buffer pointer */ return buf_; } protected: void init(const textchar_t *str, size_t len) { alloc(len + 1); store(str, len, 0); } void alloc(size_t len) { len_ = len; buf_ = new textchar_t[len_]; buf_[0] = 0; } void realloc(size_t newlen) { textchar_t *newbuf; /* allocate a new buffer */ newbuf = new textchar_t[newlen]; /* if we already had a bufer, copy its contents to the new buffer */ if (buf_ != 0) { /* copy the old buffer contents into the new buffer */ memcpy(newbuf, buf_, len_ * sizeof(textchar_t)); /* drop the old buffer */ delete [] buf_; } else newbuf[0] = '\0'; /* remember the new buffer and size */ buf_ = newbuf; len_ = newlen; } void store(const textchar_t *str, size_t len, size_t offset) { memcpy(buf_ + offset, str, len * sizeof(textchar_t)); buf_[offset + len] = 0; slen_ = offset + len; } /* the string buffer */ textchar_t *buf_; /* allocated length of the string buffer */ size_t len_; /* stored length of the string in the buffer */ size_t slen_; }; /* ------------------------------------------------------------------------ */ /* * service class to save position in a counted-length string */ class CCntlenStrPtrSaver { public: void save(const textchar_t *ptr, size_t len) { ptr_ = ptr; len_ = len; } void restore(const textchar_t **ptr, size_t *len) { *ptr = ptr_; *len = len_; } private: const textchar_t *ptr_; size_t len_; }; /* * Counted-length string pointer. * * For the most part, this class operates in bytes; some methods * specifically operate on multi-byte characters using the underlying * OS-level support. This design is compatible with any MBCS's that * represent their ASCII subset as single-byte characters, which is the * case for most widely-used MBCS's. */ class CCntlenStrPtr { public: CCntlenStrPtr() { set(0, 0); } CCntlenStrPtr(const textchar_t *ptr, size_t len) { set(ptr, len); } CCntlenStrPtr(const CCntlenStrPtr *p) { set(p); } /* set up to point to a buffer */ void set(const textchar_t *ptr, size_t len) { ptr_ = ptr; len_ = len; } void set(const CCntlenStrPtr *p) { set(p->ptr_, p->len_); } /* get the length and the character at the current position */ textchar_t curchar() const { return *ptr_; } textchar_t nextchar() const; size_t getlen() const { return len_; } /* get a pointer to the text from this point forward */ const textchar_t *gettext() const { return ptr_; } /* increment and decrement the buffer pointer */ void inc() { ++ptr_; --len_; } void inc(size_t amt) { ptr_ += amt; len_ -= amt; } void dec() { --ptr_; ++len_; } void dec(size_t amt) { ptr_ -= amt; len_ += amt; } /* * Increment the buffer pointer by a whole character. This will * properly handle any multi-byte characters using OS-level support. */ void inc_char(oshtml_charset_id_t charset) { /* * ask the OS to traverse the string to the next character (this * will properly handle any multi-byte characters, according to the * local OS conventions) */ const textchar_t *nxt = os_next_char(charset, ptr_, len_); /* adjust the length for the pointer change */ len_ -= (nxt - ptr_); /* set the pointer to the next character */ ptr_ = nxt; } /* * get the number of bytes that the current character uses, taking into * account multi-byte character sets */ size_t char_bytes(oshtml_charset_id_t charset) { /* * find the next character, and return the difference of the * pointer to the next character and the pointer to the current * character */ return os_next_char(charset, ptr_, len_) - ptr_; } /* skip a newline sequence */ void skip_newline() { /* remember the first character */ textchar_t c = curchar(); /* skip the first character */ inc(); /* if the second character is the other or (\n|\r), skip it, too */ if (len_ != 0 && ((c == '\n' && curchar() == '\r') || (c == '\r' && curchar() == '\n'))) inc(); } /* save/restore position */ void save_position(CCntlenStrPtrSaver *saver) { saver->save(ptr_, len_); } void restore_position(CCntlenStrPtrSaver *saver) { saver->restore(&ptr_, &len_); } private: const textchar_t *ptr_; size_t len_; }; /* ------------------------------------------------------------------------ */ /* * Generic iterator callback object. This is an abstract interface that * can be implemented as needed to provide callback functions in contexts * where we need to iterate over a set of elements. */ class CIterCallback { public: virtual ~CIterCallback() {} /* invoke the callback with the given parameter */ virtual void invoke(void *arg) = 0; }; /* ------------------------------------------------------------------------ */ /* * A simple array list type. We keep an underlying array of void* object * pointers, automatically expanding the underlying array as needed to * accomodate new elements. */ class CHArrayList { public: CHArrayList() { init(16, 16); } CHArrayList(size_t init_cnt, size_t inc_siz) { init(init_cnt, inc_siz); } ~CHArrayList() { /* delete our underlying array */ th_free(arr_); } /* clear the entire list */ void clear() { cnt_ = 0; } /* truncate to the given number of elements */ void trunc(size_t cnt) { /* reduce (but don't expand) to the given count */ if (cnt < cnt_) cnt_ = cnt; } /* get the number of elements in the array */ size_t get_count() const { return cnt_; } /* get the element at the given index (no error checking) */ void *get_ele(size_t idx) const { return arr_[idx]; } /* find an element's index; returns -1 if not found */ int find_ele(void *ele) const { size_t i; void **p; /* scan for the element */ for (i = 0, p = arr_ ; i < cnt_ ; ++i, ++p) { /* if this is the element, return the index */ if (*p == ele) return (int)i; } /* didn't find it */ return -1; } /* add a new element */ void add_ele(void *ele) { /* expand the array if necessary */ if (cnt_ >= alloc_) { /* figure the new size */ alloc_ += inc_siz_; /* allocate at the new size */ arr_ = (void **)th_realloc(arr_, alloc_ * sizeof(arr_[0])); } /* add the new element */ arr_[cnt_++] = ele; } /* remove one element by value; returns true if found, false if not */ int remove_ele(void *ele) { size_t i; void **p; /* scan for the element */ for (i = 0, p = arr_ ; i < cnt_ ; ++i, ++p) { /* if this is the element, remove it */ if (*p == ele) { /* remove the element at this index */ remove_ele(i); /* indicate that we found the element */ return TRUE; } } /* we didn't find the element */ return FALSE; } /* remove the element at the given index */ void remove_ele(size_t idx) { void **p; /* move each following element down one slot */ for (p = arr_ + idx, ++idx ; idx < cnt_ ; ++idx, ++p) *p = *(p + 1); /* reduce the in-use count */ --cnt_; } /* * Safely iterate over all elements of the list. The callback can do * anything it wants, including inserting and deleting elements in this * list. We make a snapshot of the list before starting, so we * guarantee that the callback is invoked once for each object in the * list when this is called, regardless of any edits to the list later * on. */ void iterate(class CIterCallback *cb) { void **lst; void **p; size_t cnt; /* if there are no elements, there's nothing to do */ if (cnt_ == 0) return; /* make a copy of the list */ lst = (void **)th_malloc(cnt_ * sizeof(lst[0])); cnt = cnt_; /* copy the list */ memcpy(lst, arr_, cnt_ * sizeof(lst[0])); /* iterate over our safe copy of the list */ for (p = lst ; cnt != 0 ; --cnt, ++p) { /* invoke the callback on this element */ cb->invoke(*p); } /* delete our copy of the list */ th_free(lst); } protected: /* initialize */ void init(size_t init_cnt, size_t inc_siz) { /* allocate the array */ arr_ = (void **)th_malloc(init_cnt * sizeof(arr_[0])); /* remember the current size */ alloc_ = init_cnt; /* no elements are currently in use */ cnt_ = 0; /* remember the increment size */ inc_siz_ = inc_siz; } /* our array of elements */ void **arr_; /* number of elements allocated */ size_t alloc_; /* number of elements currently in use */ size_t cnt_; /* increment size */ size_t inc_siz_; }; /* ------------------------------------------------------------------------ */ /* * Debugger support. These definitions apply only to TDB * implementations. */ /* * Source line status codes. Each source line can have a combination of * these status codes (for example, a line can be current and have a * breakpoint). * * For the convenience of our Scintilla implementation on Windows, we put * these in ascending Z order for display purposes. That is, the icons for * the higher-numbered status codes are to be drawn in front of the icons * for the lower-numbered codes, when these status bits are rendered as * margin icons in a source-file display window. (If any other platforms * should find this ordering inconvenient for some reason - for example, if * they have their own native controls that would be better served by a * different ordering - then we could farm these definitions out to the * OS-layer code instead. At the moment, the ordering doesn't seem to * matter to anyone except Scintilla, so for the time being we'll just * define them here in the order convenient for Scintilla, to save ports * the trouble of having to define these.) Also for the convenience of the * ports, we leave a few bits at the bottom of the range open for * port-specific use. */ const unsigned int HTMLTDB_STAT_BP = 0x08; /* line has a breakpoint */ const unsigned int HTMLTDB_STAT_BP_COND = 0x10; /* bp has a condition */ const unsigned int HTMLTDB_STAT_BP_DIS = 0x20; /* breakpoint is disabled */ const unsigned int HTMLTDB_STAT_CTXLINE = 0x40; /* current context line */ const unsigned int HTMLTDB_STAT_CURLINE = 0x80; /* current line */ #endif /* TADSHTML_H */ qtads-2.1.7/htmltads/tadsrtyp.cpp000066400000000000000000000142161265017072300170420ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/html/tadsrtyp.cpp,v 1.2 1999/05/17 02:52:22 MJRoberts Exp $"; #endif /* * Copyright (c) 1998 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tadsrtyp.cpp - TADS Resource Type implementation Function Notes Modified 11/17/98 MJRoberts - Creation */ #include #include #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef HTMLSYS_H #include "htmlsys.h" #endif /* ------------------------------------------------------------------------ */ /* * Resource table entry. This table is used to load resources based on * the resource type that appears as the resource URL suffix. */ struct ext_map_t { /* * The resource suffix ("extension") - this is a string of up to * eight characters that appears at the end of an URL after a * period; this identifies the type of the resource. */ textchar_t ext[9]; /* * Resource type ID. This is the internal content type identifier * that we use for the resource. */ HTML_res_type_t res_type; /* * Loader. This is a function that we invoke to load the resource * from the file and create the C++ object that we use to manipulate * the resource. */ htmlres_loader_func_t create_res_obj; }; /* * the static resource table, the number of entries it contains, and the * total number of slots in the table */ ext_map_t *CHtmlResType::ext_table_ = 0; size_t CHtmlResType::ext_table_cnt_ = 0; size_t CHtmlResType::ext_table_size_ = 0; /* ------------------------------------------------------------------------ */ /* * Add the basic set of built-in resource type mappings */ void CHtmlResType::add_basic_types() { add_res_type("JPG", HTML_res_type_JPEG, &CHtmlSysImageJpeg::create_jpeg); add_res_type("JPE", HTML_res_type_JPEG, &CHtmlSysImageJpeg::create_jpeg); add_res_type("JPEG", HTML_res_type_JPEG, &CHtmlSysImageJpeg::create_jpeg); add_res_type("PNG", HTML_res_type_PNG, &CHtmlSysImagePng::create_png); add_res_type("MNG", HTML_res_type_MNG, &CHtmlSysImageMng::create_mng); add_res_type("WAV", HTML_res_type_WAV, &CHtmlSysSoundWav::create_wav); add_res_type("MID", HTML_res_type_MIDI, &CHtmlSysSoundMidi::create_midi); add_res_type("MIDI", HTML_res_type_MIDI, &CHtmlSysSoundMidi::create_midi); add_res_type("MPG", HTML_res_type_MP123,&CHtmlSysSoundMpeg::create_mpeg); add_res_type("MP2", HTML_res_type_MP123,&CHtmlSysSoundMpeg::create_mpeg); add_res_type("MP3", HTML_res_type_MP123,&CHtmlSysSoundMpeg::create_mpeg); add_res_type("OGG", HTML_res_type_OGG, &CHtmlSysSoundOgg::create_ogg); } /* ------------------------------------------------------------------------ */ /* * Add a resource type */ void CHtmlResType::add_res_type(const textchar_t *suffix, HTML_res_type_t res_type, htmlres_loader_func_t create_res_obj) { ext_map_t *entry; /* * if we don't have a mapping table yet, allocate one; if we have * one but it's full, expand it */ if (ext_table_cnt_ == 0) { /* * no table yet - allocate a table with space for several * initial entries */ ext_table_size_ = 10; ext_table_ = (ext_map_t *)th_malloc(ext_table_size_ * sizeof(ext_table_[0])); } else if (ext_table_cnt_ == ext_table_size_) { /* the table is full - expand it a bit */ ext_table_size_ += 10; ext_table_ = (ext_map_t *)th_realloc(ext_table_, ext_table_size_ * sizeof(ext_table_[0])); } /* add this entry */ entry = &ext_table_[ext_table_cnt_]; do_strcpy(entry->ext, suffix); entry->res_type = res_type; entry->create_res_obj = create_res_obj; /* consume the slot */ ++ext_table_cnt_; } /* ------------------------------------------------------------------------ */ /* * Determine the type of a resource given its URL */ HTML_res_type_t CHtmlResType::get_res_type(const textchar_t *url) { return get_res_mapping(url, 0); } /* * Get the mapping information for a resource */ HTML_res_type_t CHtmlResType:: get_res_mapping(const textchar_t *url, htmlres_loader_func_t *loader_func) { const textchar_t *p; const textchar_t *last_dot; const ext_map_t *pmap; size_t i; /* if there's no URL, there's no mapping */ if (url == 0) return HTML_res_type_unknown; /* * Look for the last dot that follows the last path separator * character (colon, slash, or backslash) */ for (p = url, last_dot = 0 ; *p ; ++p) { switch(*p) { case '/': case '\\': case ':': /* it's a path separator, so the last dot is invalid */ last_dot = 0; break; case '.': /* this is the last dot we've seen so far */ last_dot = p; break; } } /* if we didn't find a dot, we can't determine the resource type */ if (last_dot == 0) return HTML_res_type_unknown; /* skip the dot and find the extension */ ++last_dot; /* look up the extension in our table */ for (pmap = ext_table_, i = 0 ; i < ext_table_cnt_ ; ++pmap, ++i) { /* see if this one matches */ if (get_stricmp(last_dot, pmap->ext) == 0) { /* if they wanted to know about the loader, return it */ if (loader_func != 0) *loader_func = pmap->create_res_obj; /* found a match */ return pmap->res_type; } } /* we didn't find a match */ return HTML_res_type_unknown; } /* * Delete the resource table */ void CHtmlResType::delete_res_table() { if (ext_table_ != 0) { th_free(ext_table_); ext_table_ = 0; ext_table_cnt_ = ext_table_size_ = 0; } } qtads-2.1.7/qtads-logo.png000066400000000000000000000467321265017072300154340ustar00rootroot00000000000000PNG  IHDRP/sRGBbKGD pHYs  tIME 28p IDATxwdW}-v8rWӄ3 1gc}k_5`[h9wtLXՕ뤽Z5kuT9Zrrrrrrrrrrr~6r_ ϩC ~Կ@^&@ȿj He/j+|[<2l by{kҲKH j٠,{d9+`,JY2Zx`+lDj9HԲO פ>,B+ PnG+ H NMŶ0pK#EۢJm_0_9 FrVd BE@/Ee)SeE<`X j[־t"MF\0x|^ri)0|.-TJ:0BL!+8^6PUR*<'8& 28 S,J/yy.6s`}hqvJuPpNX WUb*svlfB")Ca`7T74vJ5)BH)+ gs*63<_[󹐦k g=L IVފM*Ǖ:#’± q c՚@D"6x}7] o_Q /K! PfZMS5WWx+\׶l64CJ)IRsqƤT!L&6s';SL2k["+jjoZѽv㛒um.4M SJ )8ӭ\khԷ2ɤ_z&~ \ %a,6y۲]Mܕzc>:Nac?~}bSضE*y\bBh($Z0Ran-ݻr;u7iѤO]%M&t"]7UEMWU;Pܚ&zzXqrّ3GSLڲ ^]!oжaϾ""8QRzRi`8BPJEbA8t@Սm~c^m]ꚭO~?cf1J)!V@cgڰlM[;Hov]'0xGXȦlJpw T(65BpzS7(TcRt/BJ%i}Ws0 1F?j n 1f? ; A}抍k;_7YҼm_{C}PJivT;nQMTuCr~#O`Y"O-зik>Nm?4ӧ4ݠjCVn'icކ RJ]pH5|asұv<כϥ%T&W:zZص_.{/Sɋ)yYJA4]'f@]w qs(ZFǹ(c H߲MBd۞&Cxld5ױU U릯.ZS"\U?'Ņgb Y|O>˿Ӛz\l*U{s*kMt]kAVĵٱA)FfC&IcUk[yuC# ץ"M]ݠ"HCY_0 ##ڪ;n9A)(c?lijgɒRʲ$eDUM"0;,_tV/nQ[M,KreWȊEj;wv ѽs >${7m̥bTlf󇞛?社U/4k;^ }85? 2Jу>{?5&oX@(eFMlj躎v9nrqv؁8YKѕw&ckuC7oܵ>sh0`s9{JT<6WQ]uj]k]\Ӷb])Bc\Uo#^V+^KB  :J2@'\29g/P%?ލ"iY-)Ȭg:t %8GG* E5MͫN>d |bÞۅbx}#gO={`sscܑf<ڗOm=M&iKo?j[nyD 3'._J/ mU5V6ssbg2kZUBr3ع&Ϝ#螮ĥA=XYi]AUNgﵒ$@5Ct#ѹjM-1sgVո,}YVEt˹zpx{Rz."N :T6VYY{QHង׃ Z*p@ 3pGuCum]ROhizx}Tk=?>q'cd-#p8-OXߴuwMCn~yU,XQY0=J/.:oO;eq)f$bSS[BX[aF2\bBpAc AJŔ$c\ǂ]JǦ'׬KߩMR v.))"0e)d@8Bk[tJ$܅t.\?AZۍ`.YKɤ>X5Pc\u3)҉EE(U$T05EY4;\Yf %AD]fkoW $|2!Yq".&䄕/e*KK2- u55[Wv5?>Ar$4MVL塹=#|.^EYѹP“ޕ'S##cZulhS+M/Qi>ھU;2thùۡhԗ'=GSŠC9@]ye+Whm&ϵ,U`!`*-˧6ulp[<>rc~st;WlMqM{87w‘C􇳉HX?ӊ:?xM+•5+pVs`baJnt Cj0uN(sީ)7BbR+0堔ip]$HHA:9SY;?ٿix \ %c==KʹdW{y-SW;'G.Ӎ{n4flvq4>OY+W߼?] A]Ǧb8=n8f aJJ+R&{Gv<8??8;35|CƯ]-[f ( ?mͷݵ}{|PS49?ǸhM 흙 [)ePe]SPөxlyK)d$B(iE=}Uk_}5S. ?ڹzY oפ&4݀fFYQ)JJυ<0*8&rMj^Kʎh]C%3KG?=uڜcY gO={6zږ;}~K2Suw˛ᵭX8WԵSX9b}b%YZMmm2 W[:e>Ṥ̇HM)J 1Ǡ& M0 C>J) VDJd,1 =8 PN^%MJ@C>c iqx eYD.U%%k*>;pUa\ӴUۻnljtlWo~|pUUOms#vzn9[ dҕR(F&Òһ U&$3X 1rA䋟ek?R S'+Ү-R- Vw͝7sϞhmmX}1N)Ex7վr59}~dnRs3F)E 4{Bh[e]9NYaI_DzQ哿~VshGZ;Aع, Xr̴L|&P2-;ˉ#dffviW/#܎6.e>5 <}$Buv.&mCӴ.HsdheܰKJ05w6v4|q#09ǁ@?mJN8R " gHlfz!XS1|h)͆#/$R*BRJJL?!4R %{[bT64^)oy|n!63ぇX߆MUܶ7>urαzN|)B+R!7Z>G}BO& d7&,8ʸBV^|u_hJ@3UZ2xҗ:LDmK&E|f\x!:jZ XL8@sT˥`/@ `0'_ {ݲgʭ43 o^޶c^>y*BȥScC{/;᡽&v7nQ`^.C@wmJaP7˟/+ ud뚦db kYȥQ"OPFq bS*aD)Au94²,ض ۶!Jqpm= MQ/8zS(P9x4!>0]B! R>S1%eX$-Gy*@ Wfl5-]Z.;h̎^Śwmg{χO<;nCH[ RӋq @,хIoߟt%ڹjMoE2KΕq6TY9Ÿnl꯬_ۊģj6?6T3^MAZT>58OJ0ZW Dz׮ >zVTzm+o~?'<#Ї+JV7P7HPT~1%AʘuL]FfB@)LPZ f' @Ň!TPpp28#r%UBlۆeYp"J"ekH)|)П2W)_`ZAuRmRxYz| ?&!=lmUwo[雟3NX&4wJ 1w"E|AX'*~~(3ի t_`}sRd)$''O8V7LMSK`k8UDc{{>̥*13EJ}}Ր#7ɍՔ%>"]۶c3c'gf. !x{M=fkW*ARA3E,e}Q@2 !uld"aCJ 4,E0@mʅrPnʍCzb)pC H.8Ypa>4W?JTK3_@j'ar+=_LaeT3!]xB5:: ^=wͭtݐB`ߏP(p(,dt-` P,D/;fR9GznNJ)K9w ̄v>T2Emq.F0}u9c}sf'ƮOwGP ql}a4-4ߊHUMAB(b \>~Qƹj+#6=#O80}mR`]JJN))X )eЊKo~&)} Ş -i_\f Ț`:1}~bk\rֲr 8Dn ŠAzP2ϱ)e* +uQ:=σg; v %(,RB+,bU(R' RHr1 q(F@(s RL _5dC{g.T˟g{bE D)G]xP5Ѻ^Jab\Zi:hwd=-88@[6J`Bxڅ~:;9r)V߻R+rmߏ{ 3uyث~w\BX|ra!1?9N/; C# PiyYϵH95ae3i:< T^_͂[.#JBs<3XΩE*: +F:l+O\+ %,RP ^N VxGZJ'A0YI@斑۷׵\<=w\:%fIuo܊U|>Bvn4wj[f]IlCE"hwiqRjK4;4Cw6KL ¡Cd7}ֵ l`#YJ9r}@m|fv]lz*93zΧS}oDWJ+Ogw}O;モմŦƧ,L]I㫷t !rv|'bS([f}_N,Y /)%14#r6GJI@ IDAT,eܠ[s9 <*Z*!A!<σu %! s`񾼌AX8Y[̱tMi:aӥN$n@fx@ܼf]kkZO(7p4''#Co`]}(MZV^x3Jǥ^VPHŅp=|J$l%E ǗrςSsz'GGݧ7vT5v]:yڝc}[nr=>EJY2ٯ~ο{zn_F&&G/ ;2s otƳ$/KuE`#?jZdd PIB'D٩|&p]= +/'q.˕,,ރpPaEgMtgI7RJRb0MRFJb@ B](,"@WPp]uYU9t?`gRv˶' AYo~b,z wP "gG"kr3^ZNEW}i:xj@iJy`3MJސR8d >L.J\^eؖ&,J`U5,˂BR8@( ~gN]D.<>}"'2] QQ>6Du]hXWlio|ӧ^pfvp*'7ܥ um]-;ځ\,]nTYIC/RZRxN@U߄ɋg["i""U Z:K4Lt%z}j!-|.Co駱 ,gj:z.3: ."+ʠƄ}ݨU SK?r+NStUu mBxOc=-<Ƕr\Bm[ZC!zv"u]PJ<^O{l2Y%gEt4U v4Xb\ sbKEU5gS)-iK+ބsFé?okZ6޻eg'M_:G, owgݦhEMmMgy _hUxHN=΍%g3O]4|f)8?9w,eUi#J 8f,؂T<+gOqK+慥(T0 J(T3DnJEt̰iN/Ζ1(7ؾbUK{2He`\Oc#FB_&ѵf=9{LpQF/̈p< #+—V{R7,)˨.¥\̥DBa9?EJ)*#b܁r-0k_reϦ!;?F{h<4M'g?iGYcgw^>q;=#(/FF&#{=|Ýnqw-g&#mFSgOdlb(0_.8F+TD++HEU5>}r)BP̴<$gljW02rPT*77@%<Ȍ0M %NVAآL\ycG{7؜yi B!_0/GT ,/aVڹz9>4XN$Rx⽜"J)<PZ0+PR4k %)ẂR&tӄ?/r}b'tӏw^3aܲ] UV{܌|L..;1+gOH"W* ĸrm7<,Z1Qx6hB'L#o 2r2 e}HAy;•?|uvMC3& hK])!PJO`~ 8r -wc>;&V.S\XYl2PJ2B!k>.7B !8?'ccyM<1Lx;[HtccCRzR+j|g !m}bok46vt#;`Vw_D3Yͧ##W&/luHB 7='BEu k_]Yuܹ9oI`yѤ^QP(?h_QS{R2FƭȦ|IO=׊4Y>B(ҡ$Y@qH\ q^7tF%`Q!(()*ZMSK-oCcw~'?B, \ul\6t̜>!82jr*8? PUDLY9 G3<6o J)aYeera~*Zrg#b[ݻ~S3峙LmK}?G,c[Wjlkkoko Y=;?]Y.ދv?KFZ"dĄOSJ-ֵuʁ]\cEBJI R-m!3NUʞwJP eG͎UF~|;~03:*-LMN+NBsv Azb)0:!DI;/]S7߬lR1))0 (օn߾dsPJ2pTRʑsM7Vb{CPWmz;Z:ڢ.IbzXJiu"i r`检T :i(qYU>*Pի|V?Wk7}eM[VKg剧=/;yWx'ӉTMyKܴkɤ4n`xYn%)$i6c`_߂KBvrg+E]K[9(\Ʊm%\*O.+Bi;;[wԮ۹I:[Z߽a zބŹ5xp~ԉ\*5 VK bni B*LsKr>P@( Kt3ln]ݟ{}RlNգ))2 B !\ײt:9VT3/U75&Ab ""+6m *)dƵ >f B5r+=Tu,9.-Cx0PFyMcjǭz5}1X[Uge>S047tʪΆ`Et*JgҹL">7UՆ,2 @o !]y[(XpKRHL2yhaj#:9)Y,]iM[+#kWLJ.b~jvmDA)P^]WyOse޾l*^,*jj?c3gb3ޏVÅE󪈡9, IsuA+-Hi律`$uY9OĦ'PJIXVl~rؕ 璱cۚ|>d[fƴ;ԶmO\ u9ixx3g:88*U&tE`4MV%Cs+UPe82B8O O98]q!\JrBXY1 xᮁhEOECSW\}ӫzų==3z%o粌!҅kq7޻~Vmؾ3*'Uc."L(9mXZrH6 t^0Ʈcr !L 5뢆#յ~ʹi¡5;kK)ʝ{нe|^Ό^MeM{͟ۺ.fS|%2OᨦM-"ɠt}U`)E:y+4t`]K# ԷFw#kn~URRh9812 UmF&YIjWuE繎mei~%-+%q/5 \H4LfO{vi?~qZиqofڲA7R1L4i}盼ywd?5tɉE@E(y~aG9s֯3x{jWnR !lտc'q`nmN${QlV|߲&0v/xiʭ;!hoXav>I,җBS]-w:ڿ0Ӈl6T^;юVV0BJR75Asđ̥g])B3 y{CO<ӯ~M_*,}BxT.c/~C 3DjMu]C?.eηn;HmK[$Oit4q+-.zNѱ}B\&XI].Gr;L/GD.5<N69ώ>״0յzHUuͿɣdAfӅYy Hg̬ hHDbzG]=G)%t]GךQ~ϝkY8s']Uvt&WaT*31ridpq;s% [)}69c׽wL3'Ǿ5fg3]N+m10d6\vetrdxܲ 1y߯?,s9OǕLJ4w#UZ7lRc:s kp"=5$҂Rqзn޸EuߺBk;˧O=ģ?X[ɢBuMqq,K>CtÀa#E"VTP YUʬkf PHأى D*4Qq&Ο&!~3o^.j>_0Tj u ]o j&u"Iۃ'&lB0MpPH>?QR"=5N(!$#L"( X-!^5܊&:y+05y¡>sϏ .l/yq_Y^@|jfbx_c[)%t{,o{4Nb.;rg;7WF̄ &3;/IeO}b"0woe6ZS4ttlX_UHW-\w \.'.عG}#\/+g2 T,vvOLs]Q*[aJ(؄R/ 6g 3%UayJ!e J7$ BTlj˥N* fӟ!MיRJJIQoRƅL"1X[\-Gn$hHES?a1MT IRJ)3}҉N>ыɂ[Qj Եs\ B #DSIK%]\D6 T75-̌9yrԾbӓV+<_~1Y Kqsxpe0\Qݡu0;;|{I4MۄP \V̍œ+DMI%^=ԣ#g?_vTz@喭vvIl*C_C>A }C> ( dQ.nM˲˒M[RdI=gpR{9;!C=9!x@, RFRZs3.\fҾV,..ޛMfa)c,}/lxnӥm:Bm"ArB,Bp^PbY~riRX}P)g޿K])GRɇ{9t[!1&Tp&&`; :7G~c)$kO"`,lݿuc׵7R\^J\*&NLuq>fDت<륵rR[5̀87/}MlBmɥ9wSwoVVVV=תjEcm+_ LO^s-kZX~N]Z}yn@dXP$;-$RXTE{I6dY _[?]?؉$S:N:$Ei ˫\Q>y+' JE!FY,x൸$B"r C.lBI)K˶N$!cb0ΘB%IJ9c,F\]^RV﷢>,z;L2,@*Ӈ ܢ!kI !1!xRuk[KVv2mƦs_F݄ mQ{'N~x.78t~L&ȗ^.HPf굺(ϸVX"T6Uo ]B 2 ! sLI+XZQ 3|T~kkP[6-QbPY k ƞ)Gg#LM^=49_Nj*ˋ|UTŽ19{ ?rcO9{Nayn{\!D?4g1^_? I>kW xPlfDЈ(dXӀh$[ K@P8.VhǕfBcqlh;ci4 YZ3̨ fE|5{S᳐!PP;? FƞCVg$-S_]Ww aF%}$72JiTJbmTn"-#d *ww 8 :|?|ezqܹ=ga.?ĄXL69v:j)*B1&gě\u,!}8&J:[F`6 Z>h#C{_(h%:GĔKfkjJGf) Buk .29EVu`e~Kgs`?£NӸ9?W'A]k5utkڭV <_F~uXmk[&2D;.>}PYnT/L!@$5 X iWYҀ% @F{ȡ{ELd L ưun^h újR/;d졍, 3NϽO&u͹ۿ{qZTlV+|)OE#'XMU4*귾 g΂^x姯X]F XHK;)a cTPYM QJiu84]5CM2M]f:2t1e˞Y;3x\Boo`Bd:s<̽ο|7v/h~}dž{czоr*ܾ*peipĢ7<"88psR)[-ny/_ 4'Bu,ZX"M 7n<*Ac&n`fZhzQNgG2}ra5x^-~sMG,+ I%Rt_nCG>9N0P0Dm"P) ^@94E:;HB1?L֙\s PmUdԀz&Zvzr U1k(Ѓ؅.1( k =22tN֬A$\Gn ,Ď7h 6Hsvj Cb2n3P,a{C;i zF'I^b?nYg,foL.JR>XR7bE*${l&8*2<(rr ?@^ R{MyT' yM%c^B;ˢ' Q9 ` 8I᠝- iHF)) _JGKJAhK2AzL6F% K[SL{.cyT.\zUU2>Ο@lfqf1w*Hqj6 Ĕ`[Rfb4Ŗ˳^fC,9XKs&zg}.='<8p7zg=ewY<==-_Ww.|{+flց < 4뱹5uuqҋJ[?U&&W'0w}JhX쟽 ,R rUJLm+\< ;.7;eQ&~)3e9_,,wyG(6V_xx 9y;eJV~e?F.G?6#P@X[Sa]+>p[Bh<wɺm#~gPOA޲*-g)L\ҘCǒ sk"  igyDpW*esY*ҵwɛ"1xϯ7ۆG1 ؔ͊Ql~emeXTXd+-,)k#U/M~|Y[ [KC{~2WS8FV6eEWsiAcg_]s-וd_v ߁ |M+m-*vty!:D# /omc/RӠUHW7+^۸E6ad]oN5`4wN~K|E:ܵ.Dύ\+R{QwZ[k~(P {~P Q; G[f y摜.޷3<%c>wޏ@SN |krk4yI4uC<ڵwu9/ŭ +vU1` ҉#yhc)]`!5,9Kd)+˙H gV;~̄.3G!|F RӭƯFu$$ohkkR ~ W,P[|̊*+٩|[\')9qwCͦ  ϝ^iAW iy*(0spEC];!G ~6#fƼ Xrw31aR(]xherYa =IY[;ewv zNqa6 `[g+[ !)Z!ZH*Q8OCCڵ3nnoz,L6!tcaA!پڮw@̪*GPq>O˯/-=تc m"r0q;^V6+mZ^ W!-_3z!jWKmᶻœsa}Jdh)@93^8VF?X:0V#pjAZvЛOrLsuBz$|E YJ $K `763Y2GF xs`4!t ɉKx$ۼmB.W>_(**J%\*{J. x\7VF>}/ lsǣ_ݝYC{khrt`AUD(@m |l?nOhgEqx.z+X ޕ6:6n(@Ć ȁ=^n:qZiLk%-UpRb %b{bRXln*" r$9Iە}Xm㨟Ѣ8e4|D}C7lڴS(̫n V( (*v1iœ9,6`_ *K)PJ ٫*۽kz $C9 ``c|!tV²yZ َL_>ODɽ<,Pչ=WiXBjj -G~{1{ RXИnJ6c9HCۺp]bŏrR^I(| 0oy=6#ϏHn7x$xӏRڰ)is<_>O:bBT);O]fϯf`'=|f[F&GK0ݎ:PH}ĚmFP3\-\5Itߺ8y[4< jZ53*BwFX(Lj#H+CpC2p؛*Xx9vLNBڋ$~WѿW2姌A K':%=7TJp}M+\ʙf9%-)ቶb] &lKcK8:: %BLBggfM"M\] u}Wy2C7˖4u?BY+PԴLM|Y۴)\t_>OXʲT V)AɃ8x'}v~?ʡ9ںi/1@%~(o r wzvNf8OfdfF~|g֗p\ ]S)N9@1tᇕgUlmIٖK)+58g)1˗`%sF׭zB!{N{j#p^%?mN('TOQYI݁904ev'xw ·jf ao>\?j9⣦sjQe}IZjktuy XX^%x#@iT߼Bo%9{wвbv6df!ߕce ȏr}Y$,ɦk(ًZ`x=p{LbN *zm їSV~prb(eeM f6=~hڹ?0[^2B}.\4ԕG+ό_>lWҮ0 NG>7ydנu՗S1Qr/Trf`Ӭlz $ԅ桐apzr#0yVENRq?atQt B;x*<}&'&+k~(׫ߙjQ>tQq=BYRb[}GpYR|_F:_'+3c48sdx8dn~Q2|gq`-pCGkf7l k>`_uQ_8ܲɀ+[rm|mx YJ%\^<so*uunh C&~gCH'/ tct@!WʝwvH}rfE3:19Z"|6_GT]~{JgkozQvaX@xJ.&dI-M2 a|1=}LI$>'OՐE閩 dzg M2Q+N' j~7s}|/ -pG!U ~L'"H{Le.Ge;?-F-b"*7ɯ?rR&%( ȡ0ԃgNc2rqiIg\uho EB(wa^Y=}D0Ws!"?ǃB3]k%^gzE+ɂTmⱦOCLmw@o3LZ}!paYm8%K)ˉьq^_{k^ޕq/!%D0rYx-{%g Ĥ ďȱ9?ggZNz8P%@h ύ۹>@ˑ0b?8TPp|5eoR:F,*4ӏ&"u~ǘ2%ev?#?1 rT'5[O`B,;%:8_\Q.xyʘ؏_ȋg3iֱz>#'viȵ]aiOq< XezUQ&2@},K:=~+vNb*z6}D_fvngׯ Gn/a>{$F x.ᡄac.Л >y_/}Uyy91 MW T @)76d[<8)$pf[84`*sy .8`LZ;(_ؿϫ)rI & TgZ5.}=yg@yg?, xه~:JX9<@ES @{BJ/t~Gj6T_ Dbm1µς Fkrlf/KN@LLNIct@|*;@==s}TE>|9C,e`"Cfg *ցS`Wmj#S݃6dCrM:2 hF8BEQcbkc-8uL_}D%a<i9r15Ӣ=I]2;0\2}-Uշ(ϝ/gN)ϟ>MT[?k/NHA96x*U?|ˮLђq(=m6 zn>ڃCx6iCw;XN)I5{K<Ԫ5KV{m x^X9o۩Z 9 CݽG[#xv/> W9'ccr@?xqQ |`F+MXZeYdg3Sc\d!> [-z+*Fz%v̏T2]ScwGקw?/GGgpR@+We57sz㺹|T5E博IU4T:LỚ7x/RPIKU sy^?xv' Q¦1o$ss޸|hss:m'%R_!؏.!0i d|xksN܈e%+@KCߒϜ|C)yxh?pEn G7%M ܺW;zCYzw|G8h=φ!>x^럦!T_4ɹp'$ =hr\@c_1aN׭F|@β0#z;?HTi 11o ~It 8쫮Lc~Cya^ݗSz:л)ǖҮ%rd3)&sm<%Pykq?|oV )i&\Hptvi"x"Je~*\';Z_P~R 6:zoLu57k eso<TB!2z|`;62V26 ?Q+_z}B`œc#([ z|7kXٍa3a]K۱UkHX'elYZR*p'??Q[kew$9{w{vH"YJ))bj⣦bR {Y4x{eZT}{c1"".E@ֽ#n I"qoƧQL̂SaaXQ?p=p]q8އ/ G%sK -yc213 H3{(0 (#r)g~|$& ʘxX^>5#Vv8Z* )|bn-cy`6uj,0iġAmN0-o]2¼#e JF^w`1>Y)_6eSf/ :nbLGT / %,PBg{ߚ>O/M?0?znܧ2a,̜< K RҠN$\֞yNe2eO"eyj`ocώyNharȟ14oȑځ6ޱӀGL8 ϦSf/\c _M֮N{/[FLblc~S`sr*a-v%b]#N><ý"JwxH$ H:v?lzfφmS3•LoMcSs9+鉤pNn^|0wItޅz+eGOR޽N?4yŏ{t D0 Y4iZM r;GSs45b҉'NJ8u9xH\:&xW]z69P~3Z\}M*!ܵ[0/:0 >(Q#: 70ʛmU)G^P%Px&ӷ3eޮU czu7{v#G[X:64ѲIBɅxXj:A{CG\\~(ifO|sm?IKZO̼y 5k' e;2еx.&w!#Q@ T_8[Rx8xf[e]W7枻[6 B6;C t@C -Mi:yLv ~43r6v3â7w::CI1p/wx@'*1O|DŽ%|E㩮/H m܎tho#h6㈒Nvr7k/3AMANȍk.Isb(rJRPT#*52>#ͺ`@4hK+/  x4X@ًErs*t>)QY>M#.r"xx_ Az,<|\pcm |@P#* 4N902HTi0>#/LE2 FAgݽad' 3N;xܗɈY| h\{"=ڊGƢqN-‘/"_;犺Wrdp`k^6moxs3wwT$ ) Lc.ωG\4^Azi/1l,Cg8*^mȕpNz[+>Z~ioͽг4025^Ghl쵹 W\8m8|9|?& t.!0`Ȍ, ߲/Rn9B?BjﯽyHv4MG( O?'mPVRN< M䶾a\C f8%vOcf׶$ow,G-x~lk b4f  s5*4Dxsb|F0,Y4 RLG}TA2N:UYjGp` svt|c(dfLFk8%mTFl?خ_UJE뱰.+F[2ꦧJ55\G۲_ZGm< L=^`gb)h.FtbPwt NI,b\CRVzu&OxyMj)1B8;/4 ‚PE"XHTm=# hoL{vجƚ–o>Ňȣd!3"-삸Ȟ[VN DzZ+K)9=Vi(\c>{~;H)b*Eߤ=|xuX uQ$YL*^ gqh/ 6#)7Āh;W!YGSHs:,#ѦŹGO<|#֞;0&L{>8`O;{5WGLJu%]ZMFMP9j<3".gd>YdLAس/_J!eS BM!6x 2S̕di()xy!kN|#gilWy.k:J"A+Bw6zɣ">RVm*wl>lz?zp]3c=m6q`$/a,G- DػA_oE.VX@ 񍟡lf0ta"tB8= ;,Μ<8 6ߘ |'Oyg8*zk<u?єO+gi+Uqq~X}ŷA0-o Mc_ݔ\+Mz.0ӅNpד;W"j=Z IDAT'IHnv_U^{#X?{P\.82#a ݆ >ŜaVлE:HveHׅ:nӀ"?%@- \fBHmj HԜ\1 ?f 8r14`a]̹u޵G)2UzBe$^oY#s g,u%9qlcs>bvh ZOfi8{w(n6vظ?Q[=v9:!gN邝/6=DtnvB≒ߊd5- a]q0^7\/+6x;&D)D$j t<G't* vq]ݺhD)rcO=aOO$\\¡H7~ĵ;I}t^5[,\ۦ_)^;UzsХ1Fuʒh A(!)~LlT#/f@ӷi6L^ǡ>W,D(v]"\y\Vgػs(s__pI؃#ǎ\m}x밣kD=\OZ*L%c̴%X#ڴ]`״ublt(s/y 9l#xCˉ3'O4]l\uy2X&j5{1 N G֓hM<8ZIt4VoR >TLhR#JT0Fx#HTn!|xr R M5^Bv%< R ֹK8o='EQOmdf M7'1k(D3/ edtI7/׷0BFXsEvLg!_{\Igq=‘1k/McA\u(-G̹Ҝmŗc+7C_!(VRl_ d8pyi Z#H52;Ӡ-WtGOJwAv?+/v6YU[Aã0~c~ҙ MQк-Ǝ \U4TKy='pmBώ:5r]`Ϝh* d ""ԟymX ㅹ/E֝~N֔\ï-y[ʼn)kR:8F24'o9\<4~]ٱy%~]S9jLk=vʷW[w uL VljsQW}ر`ȹĝ? ewxLK#8`5?>^u9 HPv|0=4o9I:gMu2 ?eaL(!ͨABa3˶*@63/s/Kv[,Ԙ z㼇wy܉^BuhQL+ơ4եو4H`w9[z,I}׺ WSip$f ٲiO0_#3mwm{{\O=~mŒMJU6FJ\*,`{ pT{Aa!\&*ytҳ 2 Xpt6Peq@h Ϡ4N93JeÍAIndKaq[4ʭ~q{.!ƭ^u)$=4𑖗J)7Յ!]!KK߼o4bdP0?-o];s|9_a}wroVp~`PeZy滯J:w$G;p{{{G1)0n%-C &>pɜyGɦݴn7ߵ A3v^'g|\ˉ xρ\{mP㙤9Ugg%4T-1e8WJMvȧ/,N␇$Ѻ+Pw<{4#-,f1mrh f~X Hs;ij" 씷Nfq?zV>y[=& [Ýh1s;z̥3O(tLY| I-\}'^d" ڶ29֯$m ޛj+NͅY7:bPFG}+(uз0n, nH! ؘ&?fHya I1# 2ܔ185Qo^ꓑiǎ* mmq9y~X0f%М@Fo1CRs)&w8y؟ ̓~^Ov{w=vTeF&eBbX}5pl)<*dAȁEf;TW2ƀ:c)q`:Q=^ܺu  _@@LR>XaG(8 е$L&g| vfDơKyoE8\c3E>3Ȥg!,l [=ɏcҋh-<>C7Ѷ78-'~9P.C5n.-1Sʀ%{6{_d$W33X >y̓~QmTz΅ffL1\y^>ȵtA4=1_%˷]mNJlvGš} SBrXR",(F&04޵ݛȶR#f%N ?xݵ؆q I7/SHO)6zφEo V:87Lڻ#Sf^^ e:,xLle2O `O兑A0x?7Uk֔)xvxD΍x0H#Ӕ%Fs r᧙X4ƺ817بxQFbd>VVF ]ba Lc]Μv?/2(Ҵuł&Ǭ'[A'ofxGC9mwgxi‡?7iv?5ݐ!N昐qYT L ,+)W)7 MHiwd*˪w.OMfc#5 #|KfDxIFpY#͐McEqʥ)954$o?*//}M>$Ξ 2=;_!}G۝`?Gp{1wb$F5!i iM/=f̌ȏ~Sm d1Isdf380xa^sV53_Tݺ,#?izY1ǎɻv^9-Ѡp8^Zt)ðOLgU`8=܂QVr{wӟyʰ|y$lbFlJ:hLȞkV /vhVcNze 1Vtj02ad5>z׌ )p]F":kwʩ *9qH(`a#4.+@?& hkg$/i pyL(;[/!f.,>*,=˞ŷ^K'{v.-m3j\x|b|xsEzI(68]/kJ)2:H,Ӏ֦inKx̚Yh;-uwl!ys[ϔk'.|2H 7 @49@Aј:FT/Mp#H$͵.0. J QLQ&DcJ6#tŒ ZD 6(z BۃlwmzGϜ-JD5N෰MwQѳهK7=9GOݷMx4QWiYJ^`c%=q %KM*Aaޝdڞ0鿵5K6۴\(TQxDc|Q6A>t 95g@w Fň_FGNQŠJbL!y&k%A\ov2'➝NN8sq[0 -I<ŴIp52>&g/ k]1.)7owۛo"#<|=O4N&# PmCَKۺ)#D} r7.M]MCԍh|^ ŒkhA;\ei,*L&d;=#0W|{K0dnd TUt#:AAt ?RQ8["%*Ő) ӧ{JC,6F,t p5 >V)b:!z0g!ύȹ"G !-mpC_[͵]Tڪ&TN/޼-`i3hb'z_:-CP"v8q#^Q&넅!ˤ0+Q\ʤlK0=3/IOr(p)H)U .tMȡaene.=`@Bl F\*n/`~gaOvTEؚؔo1I^m.ȝ&n;@?DP,q5>%SͰǸ;B+ L3Oa`L7mN ӆ[%FJ6XhTIϼJNN09A݌.4xi&"ϞZ#wG037> Sp s߮ոAno# ]ؕ]e2[ W3eY rDz'OOęy+V%_"+iKs}eОѲ!l;Ds~eaJbʱd\_X*x$NUkg簯8c`icäҋ&4+%tj\n߰/%Zri_˻Oy;8u/ 5>X7n=:,+H`{cJn* yu)虮hI+Czϊ%\]a$c ٙM\8|[fדMN.󕢜@4O DsJ *(S2ьѡуw{=7ν/ s(AN8RǮ&<_"iNŽ:4q77!N8/#ͤENymKl^T0{7+ҒDq~n'7`VnE>WC*.(J^e99йC2t9pҗ*k8(Ɗ&/žCYNFOw8oG㘺zeK4~!/7 Y<0f !ׇ"Wu3&fL$ 0:Ṻ2.X^|~ 1ۑ+^xO_Pt_5Y|ifK.pz%B/Pݰ -}Aؚ(ZkZ--|zd߹@o \@A_I3vw r#B]!~3T@ctwpE#R'0ųjs3.wOf `%/Eu;EW@%DCd$/xG\&ꆩR\W,ϫ| m6i;?!d]sm3矮mlngK'"}Cnݺ|~H9KlJs2c-eeʷ0? zp>1y_|E8EB^)lX~k{m?MGÝ:g Ĝv,.BXIuKO7JbN$FXeNw˝ IDATlyqrx[hrey߾BpZ-_|.=7sxЊaNW߯Cw;'WKA ޭ;M:ע دA/XxW6`!otɉSijgo]_^-bBg'  9YsrSl$O;̥3\"MNa<\=w2rzE#{1œKDu`d\*s-19=ۥ+7n^#7n)I-ǒ-ILH+ |>cFS@9[*ލzqJkGim'J}/xTQAY՘z9!:T?:Z)O47`?ٰd;F#M z_# "_ʍ Ԋ) FCsh Cmv}ثɵ;A"2"e񊷮;+V7RpLyZ="8mVf~ >QL(3?padUlU70a)-U)~~n IS=JPfi+]3R9ljvAι;? hO % ) bXOkj[qJ$Jak#n@ٌ>)lv ro* cLғla+L<=?aR xm4y K>';N9*[S矪v-80m]&f"1$ sX@%uL.QZ $jH>~Z(2ohQd E btM eqv@ K/؟Waz,j <˭56@]KdYl|ik~=empk绪'گo3^ n`C!$&B7´Ra5m)W{kVx}[ҭrJ!{58_`E%(G/-ir4<2K={{O 7ִ:|q x9vW<{ 'T7_A7`6~kE?ٻK^bıJØ[?}r?$^ ZSJf2 ^k^6U%c\r9o"*jZBhXroNe+{uIMjC Mlx|<=%tšI30m_[Pn *:cp"vc]<nn{wL*ށ[?ö"Yha l1nl6R dS-ݪJL}]:K07}Z{ ed`n\ϸyxαk))WL/l[_(%A62 Wczp+1 YnmW#Wc:Exn;ػɴ~}Mn;_SAX[]6JAzP̦BaT. hb+XtÙ{=e/r۵|U#<p?cw7MtX^m`^R{etv ^b?ӯ')th{p$5LW qD?knC?9ps7/ jȸC22α:ʺyv>zTUX=ߛ&mZtW:Eڍݩ[D-Krw|F͂k .c7GD[}=DPFݴkkD Jłp+~hf #: F7 ϫ.iX5 pz*1#0 z1 tF9Wt|'ּ5N;6_\Tn&YAޝs%[{i8Keteuhr1ujd32hr͇o+L#>Ex;s]*ErTp1@77 "ZڲZ1tKXw1L49_[y O.x8چVK1 8<7:-̄|j\y-L©7s9mѻ[ʾ;bWuTc@&, *W2g4;Jjx疗 Λy3wj.;Jn'}a uAf =gX"|:"/R7uy A Pg`PVwX0]*"T05o4&vtizK6#ZteF.iy r}? X<x- 7t2nx]*{vͅs}r~%szܲ*j-R>@מ}qᒸKpei[!\"0gx8$3yE+vc56 &AnNgcwPs;+qfeI+a󘀏DO-5j_Ƀi!ǥV`wY $8p1Tu?t7.`VxtN"U|h2.YE ]%:P:(7?V)"нsbQJZl=H!Akn[C"'l !< F!E8VWK ӧM09i0E0z{ ;Ruɥg ' {agC&3-_) p t/]Sp@_=Ss&;r6Ip#̶pjn, zPiílR Fٸ.YFbH9vM'B3A=`M6Ƒ M McB͏JrRR7s󰧝/8v<!A35}8xT?9 |ζˁ'|<"<$L~xAo' 0/<[Ǡ$uJOQ`X]L!mcfum-29 `4h P8%ᚦS([?@PH\k%#K%^~6 t= ]3.i38,g S'xbڙe.9'b66& {;ŽDzjDŽ8D|יč/Fr !$lQIRGL#(E%( ,&!g)dpIG.' Z-*btɹp,_yKA ЏחBr2Oť> A\vyDSܑܺf>˩Sh6$Wg/fzmytgfg{b$?5 q9?uPX_}ސ<$uμ|C.b+|Wmz=4k5LrH¼$ .9X:dG!OiZt$T?kyj\fj<ϊ"#3V=*PI"R&0+Ys؄c;'-ɑ=G,=L*慜W/dNmn>Mc>B%NsI{jתqPq u͔*mYLjpnz^V rr̍%Ÿr~ZTR'b\4Pi f  ߮nV/LH]4_lQk.& ^@~ @gLjly+<:"e7.ݡܱLzǙm]ݡ9}mvjM{5R71aOs\(|Ԃl-188L0.ݽBV⿎uĄ f<*د&P8ңClf_"ym\yX;2h:AAgd̎`A3hl//J9Ѓ1].s%)fe #옲~;N* LjEx^t{Y̵Asjzoa[k!HS&O ʫ,@xou8{^D&."Ht>8/>@N ;q_H.laP3-|ys鱈,+:ALZ@ ՞0\DtIH.&iC/nΆ@o4eEowce{}+@ ~(C6ݭ/%WQj=J.(fsK& 8tE|_-;zƁ_deK71"IoH YpV)E4CR ~C% ^{y2%SYeWГ0` ц$VW>}s}{Rnu_^%ogiw866)";Eo\Cv%zSO&-DZ #& \ۆ1x:yV `R7Ke̋1 +<.O`+eL?T񧽻> J/VeOE(*XAlkpy!*4A Dbgڔ7^|o!SL_tM |Kfxdvp9lB`U L -k6nvH44@#3b|>%Uh4aj0+@L4H$_.a_@툼SHEӁ 6wBqIT_M{]<]Mv(s}p_b>Rgv,x@.߈MRY@~p7+ Sf'e<-Db=JpSA)>t Q zg\$+n$^SEдOSi1=P9Fc+h$*Ƕeń;rnE`͋m^ P=Vnx1^]۹+nUYk;R»+Vzl-K`Y c!g.dY.Gc;ISq‰-|)[xJ$.5V]o,`BL &`Sdx~ZwF W=гl q~=3~l?lG%_5۞Ǽ:WsɻM==m<|}tn0Yʜ])vCAZ/3][1]&߄G r&a8eB 3Ņ˟ ʽ%<'^}{-䩽tP6a Ʈ[zf9wBC ӧG0fa5Ӧ&5]a_/hu)+  =_|I,L`F#RxϘS/>frT8$9&Wx6C*4 xYpPx+5<F0(ge?` A10l}t@b*ZO<T u3CX-!8\VPJ#comS n/n_o/qal4> @~>j4< q_x, i4y>9sth/b[xA6xהͶcX 2XH]'hiK$`Ay%ɻ/LC魳Ϸ%Q9}"!W{Ks'pBh<Rвo]+@JLAӔ(!Sq4+~𳁄)A)+ds IDATJ0T?fyG8p:Toڽu.R/HA̫.9+muriLgCu"tѹ8@MQdpNG1!nD,fROFկchtn>̕D} Lu@΁[pj QK .!m:*6qW)iy ]Wpo1xU (%:t79e!m r*~+ܞ5wY]}!>9 /bi QP> eMh菏J 54ԃM%zˬ:bsLj|UYpP~O;?B)CSx), E:c5c|uZd].CVbF:<:KDKP, Ff} Boie;_ C0"C\vAϒYd\CoqbTLZCZ}/LF #@Z.nqU"@`H'tG?!SD9)rRL1[_DTHw%nclueQ,s./]55?ut oY#gj&"ss(Á%z['-|˩0/ѿ_LNrk^fs`nGk=ܶ*pC%=Ӎ?@Dg&br8Ga$Ɠ$d 2\hZL2fdZnc }Cr~ h 'i7D\ݕF@__/Ȳ͍%ԹM.[6ZGj !:*wG)1 G<@ӟgyyW OzT*crԓGma 2 X,8 m q( 8[Q=r=I4,UY*d^x_;(KCTN 7ٛ7r2W^G_AJ-֬5 ;Ղ]pO1bΗ[NPw~ypa&uNy<<"u)}p!\m-4J1Ҁ 8XAȠ#E@&:3\i(~YЦ 3:9a7E!*JK2zq6D "8R ۩)2 *4eOT8}d=2V+ЙM `لeS 8ۚP g?_g_?z׌*@ "0F⨍M6)H+@!ۂOgY⽏$_[(XϗhC BW ET)|on(ds_x.G(x7'DfX{h9 O]SĜڃ W׋lj22a2 6N)B8P4LR:jL"N0In!6?*f1{Ŕ^#l3D+Nv#ﴘS8hbrbx5д$ަow,T ?|})uS?R#fH;n-/97aY9w21@B[3þ)`̊f,)'Ph3|6iG;=@O[(tyÕ+X;G%qgA1sSjv^/`D|8Q뾌v^"+sy&'$i'\\|Cd|mE`EO ) =|5W>[v4G>׿]wrGKrJ$u@e5P 5Ww4xĥ`gOS|`IKqx;ryo# _TLTK^E&N{svQ11#Q7=s" S+yP GݓlMG^ҳxwׁFϸ|qqP7W/ L7 \֗ARRpz`fFd~](1fp+ЭSw@W:YGƆaY~@_Пat2t[8nh]PߎCDUrٛ/\S}xdbE{yuӏknz/  %h(b[-/KIrSk_lM;{T2,a)"?O%Ⱦ"w1:N| t}"|K/5`dŨc;+HjnӇ'^ /^yԛas0/;q `M |o%3#}Pa9fv0lT32ewD*/I@<y wH)X~Xm= cs\Q m v: f{l#k/ .Ad_\%pETP|'9V{tt/A@ {u`ەPnf y[*ضLLT< 5D;}*xKg_3O|;i w>7= KBf?k[fPB)X -t+qم AC`Cv^.!W?KChLys:15'h/Sv,^uS /=:st/ t8gɘߓ8_vob٬ll^_څ@/T“Wl??v}?HhŬ@;a$ŧ)\X-/I2Wm?I&O89 Es ~jL9zv=rߥG_=:r ؼ?`CW"-Msm|>>k/ʾgyB=:\E؁ <+N=r}bb P%l $f  LWJ0Sŋ?Kø _?=x5g |*@eGx,_4ZQ\i3v~CkP snTzMEzR4 !<-1J)3F8о,L|2Xq{i<'f/DeMWᓽ&(1u[NM=& JZ#-pOz_>l ܹ;7p'1ee"H_A,G;z'JRX:ь'}ktP4_B\!in]6Wc*ЛǫMP8`ҩƧ:!;bHp@L'~y)mL_s,'3S`"h׾ށ]͆YG n@rqwA\Y)@cX1]x4 I_j^xi3OյeqNT{R|w"!OYV6I5,Eh]Wsoep~}b[(a":PØ~T?Fb.SUfgv_O=xױQ}. s!]hGhN? 7,I=(,eP+'/"gǦMb^):VW4d4]}\h#owcLUVbD/I?\ةrݳW1@F^zk ~4}qS93>';Yz^5yR Sjm~~DB-?LGzrr0"Ԙvg+gF}f0)c S_zW$۞d<~7sYoYh'TϋW7Xa63 5v[Xs!9 `iEdJ퀁EҴ{8:lHP$h14i ]Mi64F3l*TRڷoBr_>Jj p0?)r+^?ޟtJsO~m`h,|*سE4J ʹ0.ѿ+ Н(J8U4Kbki'b[N/i':{u";{rj {]۪hҭzq}vyS)(@H/l]zo|0ӿл ր`q-mq^`-@ vrF 5C: N;-?ഞ¸-3zp9h)~ny}>6G]K\Pɲ;0)VF@N4{N+h&3el<㥷ZVw;D\<ñ]e翷tCf|ro$Qlkyyt{rn_]c`< ixkkyDWN+LH2 S]i]7J~ף軽yБFV@+H_\[ :$" 8~y$zp]Gg}k'B6hą'A)F~ʽL[t3ͧ|qQH(6M&(2:aM УV0vG&nh6psMGɱ{/v4.Tb{h2Xd|T,wW=ւ|[޻P¹g߽oзGƦ6T @O}/_Y3܆Ylcњ'psl_3n0 Q zOhJev+}%y .eco;bp9|sΉgqzP:Qb&&E?Ciߏ|M^.z2-1bl\ sub·Ӎe٩{/?;όǠݛ#[4q2kzV' {$@Qc\&I'4hJ#?z 4MLs6r@ 8gݭrsUڛHト*eaٓO~c|=CO; (04-J#Q`~@`_;Ã?.]Kh>ӝ.`Go1zL:ObHxip(WR{;BOL*j$UO|ۿ;K;t{&0Y@pG+;fL_rGC+b<{wQ|U%e!y*bP: WU!~:ҴWj"4Ylg [#8sr^Nw/,@/Γv۳s \!2"aS&lF SǻY_*̟'i,J _뚄L/ɁwyCo.4#Lb}F~饅JWF]Oztm{y'ረC Kx >RD'cTpMrJGCD p|jz׳0 6!*oJE@a'm5(EGq\\:G=ѯ]۞2o㾄ipe%eMZ׀Tf+סZÕFjv?y0唂u&B,}˸MN IoN!׳wDcV3%TOJ@/Jr\|_zw`ooC#_:#az >3:DN9Z >q?ҩ_(% ?&0CJCս6LsVSlԾP|ľӵgǙݔnĮmT$&w<[ bV s/ ]??oFD U|?EwʲyYlO_'kaci_s.FԁLǖ"d /P ig3xtv CFOQ}Rz9@ ̾{)*0]uT˶×p)m' ;/sqWJq NK؎2n+?r9)n=@:QFh=?D3EOGaH 1fhl:Pȋ農tU;212:&@'I{4[בwj:]qhݨo_춰*\F`?qPMDzL֜[5wWz+Ur8| \| IDAT0$NZ2$EPF@ӽt7%Wĺ22`ȶUѫs1՞ӓɤLQ'ddp]m8b75@qkEf)ܥ, ۵2=x {xx` }FծL41ό ;J}N1 A_ҖXnBИl[EN%|ӫq:3psM:݃A!ׇ٫qCO0xbV+*3z4'ʧ z/>[VRtS{\\5S"8:>סG$/3p;0^%]0N"fBHW_'N]/Բ` h0V%WɅk/45eb8c?T+GnaMY fj;1%ՅXr;zxݶ}=?l^ra\3W]-& zUf &3* 8}C@ !X*5:N_9DGΕs24p338`N}4[el_OJWWoww -FX?8 e+"'ڌ{[4mwMpY/{}{ sq3hނ-rvo{zn?n @,Z1ÕJY;^E Oq~μa_q4& .vSwahr5ά$%0@m=:ՃWmun*cuUʍTrB;QCF`lG??ѧ{T0xe;AFU ʘ A x߅q{2{˱2gY¿}!@p<]¨a/i6 vydi6$W"n}[r-u-eα(mSdvMA "G52m7`S_T?;Co䠂_$=/'/̛7= L۞_s,U߅;I` ڿe 7!C"PC1هZl>j_0YIG'nCiMS) J9o8o58D/%5c^kё8a ,me^4o>v$pacoQ~e!Xe)[j(7%eM;w9å׳Ip^[%/R5{u rcJo2&4apY@'s)Oǵ\u o_tsIq~csޱ7 8IIry@cAvOP~pzҋXtQ Z!7ox?EpFQ4Ynv9H]G:Jڄ*v(yO>uAΞbOy#ݱ 7g B&*ʸȧI& `Caxcp* \%*9Mqϱ|X``}E^W#|>k-ڳIQB{uVh**D'` طгy݌wpP}'0ܓ$6q9eK Xldozw9+捇g͡q(,e 49_l,Aɑ=\fY&e Ȫ^hc.DrRs~%źJ+3Ed罞] ,so<9gEre*O(@U 0Utˆ~ ß &X{xy6};r+i~ ;FFi6DT4K6#\^bJJhS Ze,[vs'?GxTU~|u(GͽS >@/ ff`],l;Zʓt2t>kX{eqR1>Tޮ$X6I y]xwv[z~X:a~K3 Kp-3/xݙk^󳑾0^d9>.v^SkulKF٠y93oj?|/kw3Ei?\oD8ׯb>A\} G 5% L^F{ڴ8Ǻ~/e$QlA2-|B(8{,w~%h9͆\zp_YѮطt8{' {{Odfv@$ƭ2$|| %ҺyԸ&" YM"3aWK]pEhİ3g'w)۫o3s`G//)v ޖT!Unz_~sw_<ߋ>/8$a80j܃wUcg{Lweb1`48taͫs! {unz)B]%kHCٸpfSq.` UM7͂Yl;SuX/Gk87Ow܉)뉿V <=Ռy(9&8 ]bnyJ2\%R :k}8E/"/eLI$v-vce%. M~touX?/X-=oJ㰀L\eg&FT /4o0cw74:G!xC9' ihǹCfwU.A!w fj=;*yx-{ );3c O &?x໩?eT1?3xTXz^_Qpp 4 2ҫ^ڴw vƠvO.@!wN㯧i]k ;^ F譊xKh2AKW*/D~ćW=aP"pWd"*hr<΍/'@śX.`OcG˦\BKS[*ji3<ك a"5s}2~HR(#23IPWS_?tfOYz}VÎ#e.BK*~BrK9 50LaA-؎,2W0zW/=Rɤ$~x %_$ l?tz8n;UI9͖8=^Kb`Y_sd*Ɨm:Pd. wv9Yi( @{CɷUg{bEE7LEesx>l{~"p<)sGOJE7NNͻٷr=PC6=9s7~e?+~)vHb~`x%q}̝oPSfN缞;\)ne2v4۶W$7_mMJ~_5\Ľl?ßk'9N )c9l;͖sKڣĔp&fc9bV̳{+ بg/?>~ѕϾ1-x.:8boanW3:(/G{U2gO|s{WMŹo Wmݼn6;~3 2N4F!c^T Z&龗7FGӦOV|y3A<jA7t s/g./QhoP}G7?7lf/N}bw/ѯ<(8STMW F89Mbm[KNs=3 qN:@^7sG;JU*"Xx!05McX .^~驥'%u3|S?#}oϤȾޜ=߫i6;P9ѽQ{%6q >VW ޼W੕X//WKf"^|tRÏÀb% I `kx™w^TO u|NB W&6vF]&JЏ<`')SٱhN{< N7>|_4W7Zz +"Hނ^O~2'fj^ Hd"L282dɰE0'3uY|n-pC3`8թcO7_^H=1@7=:2%hEjhkU\xХpj*ku\^eO x bÀޞRӯRʛRjiv˿9[ן) " @8.s\-콚fufѱ;Lb|+'8mA)ڪWѵ\ʋDл^BNY(I0<729|`joPO :Np \W '*/l}cڼW-p2~^"BEvb(Ol e-Gr4;6cfhh\ Ъ'0%nHjg,DuJeYDV=vJlA#S:>qS8[A.,E J`92a@52³WK/~qIrk| v2o@CA(`2^N&5&V־&ѫsK5fbКcxaA̧̝c+;Z)U\kg*}ISMıq&tl!-)!L,x{mI򏽺ry0A  :vJz?,]~ dpӮw-700ۚ}j{檃f7f֬G~;,{%c9/БWBpF 10! Vi$j zrR) f~˕_-pC2z7f!24"пO[p4'IOh~j.zBZmj>1h~`7zQu~ukAAy`M6"oh~"o8OczA8h~h~ _4yQqãåuxCCq^F1}kh~-yBYnC>h~tcFxS-#rJbƢţãäuFFoYA.yh~ m5FFipXY./tH[ǠǞƞŠġUHll5Mc|jHH sysdnCi??bʠyϫ̦ɢŜşĠYJydH3~jh~)uBYtJJ&|x[nAAHśbյ״Ӱϫ˦ǠěĞMMvZE+wh~l6OgMM'|șmU]I"CȨhѵӵյִӯϪʤŝOOmU=%qh~)uC[OO!s̨sRqW,q'/ĭd˳qδwϴ|ѴӴմԯΩRRhORR!r}̱r?7t}Ǧ$TZ_ífȱnͳuϳ{ѳĴTT;~h~'qKrTT!rl_0"2PQRRSZbǰkWWrD$oh~7|OeWW q]R9zeN²<ƶLĵMóMNNNOPYg{M(th~!n9XrYYh~ZóMEyDtmͽAοH̾I˽IʻIȹIǸIŶJôJ\oR5~jh~+vM\\h~Q˽I˽GepdzJȴKɵJʷIʸH˺HͼGξFοEj^h~h~>e{s^W k|ʸHʷIɵJ~g-UUUSTSRRvaae>h~h~7|_aP9svTSU-p^'ɩzCbddeeeefclA!lh~1x[cIPuueedzEe+ƆX/ánFa`^\ZYU^getAsĺrK$oh~*sRoue>lwiZ\^mB^0yPi,JKLNOPRhpTTTT]M{ųxQhh-XONLg'YzRty?TPSUVXboj^e!}ӜgRx:h$]]]h&^jf#k}XVUSp-`&lwS#Y{=oWY[]ę_mqj$O츊췉尀ӞhĎTq-fmSNwvę_][Wx6f-PfH"9z;ѵ\_ŚaȜdɢjxoƬo&ⳀՔ￑ミx3[j'qXpo:m̡fȜdŚaY>\,0t:ֻǠkȝdȜdÚarvRJ̞˝ʜɛȚǙƘŗzx-̵U'qjArl6q{[^ÚaȜdÔVCt>̪|ֻ[XV|tи4鿋ӧҦѥФϣ΢Π͟ΛY?|tOpu]LQTXEz?_0ZNZM~wyʙW͚Rۯۯڮ٭ج׫֪թ͞č@չww@|nIrAxF|I~Ir5j3Q4& e0DZůsB~yԸ͓D͓DΕHף[tÉҟ޲޲ݱ~͓Dh~+sY}zyQg[c8}\3sS)eDZ;x[q1cnb@k?d9{[0nM!eEE^bI)hM1gK0iK/oO1lO1aG,[C%Q;?ew؇|ݐ( @ ,'"uih~"^j|IK3vRd&{8ֿIۺGu5\#pN2 $/'///+($!}4wXpN`!nBxMo8L~W<3333.)%'};vTsnkqVoTs9t~bMsS977750+&,|nh~h~#p,w'բ3:mK::::-h~wrBϲ_r>4J]oR8h~e9cĥBs_L3~)v9NaxuA5gmc8XXƞţ¢Ft_G2}*v>[F;mvW.Y[ˣ˧ǟƠáI5/zC\uI1tyMHxƠdյֳЬʤě~MpV>+w8TiM4yʝsPw2êaδxдӴֳŭQ}gJ2}0{UQ:}˭rEcOSW`ưkͳvT>^TE||_"iSn)ĵMNOOQwXrD3zIaXCyrO+nȷ2HH;H˼IȹI\`C-x>[\On;Hȸ1w½E¨R¨RêQëPĬOo~>;_WlhêQCy•e2ߴ_hijkycsD4zXnKqjad5]'ƩPLLMPgdYyQ4zGg=UMzC[ d'ZSUX_s`ÎU}Ao-]p0\ĵj9[XJi,o2àr[×_ǜcnʶJ￑ミ금ڥqCbnZ~͢gǜcPv7r8Sάǝdǜc`}O䷃˝ɜȚƙŗJм\4zHvV`Z^Qy>S:+ gʭSuз͝Y׫֩ըӦҥDu>rBzHMs8A/ p:̸wHҡ]ҜR߲pўݰݱޯnÚOsy^\ZAa8kMg5](|V$W}ɞxݣVݢSiݤUYBzDrtqQ&kJ tR(yQ-zQ(h[:ߐۻR~c;zW-nN%^>V5 llziiiiiZsnpYg:}\1rQ'];E*<XEmDi=_4nM"aA~/z?tT|\/yU&ԾI:v~lR/mM0a2q5 6/zcR"{V2T8`Xrߎ#p*!J9TA]F&gZFj\Hvqc{}rʁio{ܽcŀ(0 '!"v0dhEQ=o]5ͧ{@ÔTٽJÛk-wMW\785230*(<_zy~wW\'i4r2vCoy{U>772+.E7wxh~_h~f2uDVsQ?<1h~S^1N]åCY>;D\oD5fkU># ~Tě~ŢF~fRFEQyGXoi[,LҬѭʤŝJ_ȺJƷKôLrau[KZsYZkò@ëOĭPƯODZNlfK^ngŮMțh?klliz|XNeXple>d)حNPTkme'P[MF[Tf)w<ǽ`]ƚb§ԡkڧsɔ[Gs1ªuE͡gř`w8|>Ϋ}ȜdlwT̞ʜȚƘ̝ailnl|kWYEp8QǫPȨ㻂ۯ٭׫Ӧ̜Y~MlIqCo8e/7`쬚|Ψޯnۨ`tÈ}ͧi`_wUgZmM"xV+Z8|S+ٸɓVe9wW,bBa; l] vjjjhtybi<{Z0hE:_TxW@k>yV*W#!'uEoW-qP1_1W˸FgTzW/B}f|i#dg=^hLgn\vs{~猺ǠXu(  1+0=uyasag﷐V[|ReeG=:3?Dp^h~vLǖyoxW=h~3Szś|j\\ZfUZj^x1åaӵӰqac`m^xʚUOSjie~c%%ȴMʸKo{kfyi]sL쳈e_d_pAy?ϺYaðU_į^ȞhG̷XȜdΦo˝ƗϟeXQByG{n˩~գ޲޷qk[;zV(])wfFЪǎge8hEa; }xt^Jf:}V"T[gn\#e;7tle#%|ه퓼+x5qtads-2.1.7/qtads.pro000066400000000000000000000313251265017072300145020ustar00rootroot00000000000000QT += core gui network QT_CONFIG -= no-pkg-config contains(QT_MAJOR_VERSION, 5):QT += widgets TEMPLATE = app CONFIG += silent warn_off VERSION = 2.1.7 static:DEFINES += STATIC_QT android { CONFIG += mobility MOBILITY = } # Mac OS application and file icons. macx { ICON = QTads.icns OtherIcons.files = QTadsGameFile.icns OtherIcons.path = Contents/Resources QMAKE_BUNDLE_DATA += OtherIcons } # MS Windows executable resource. win32 { RC_FILE += qtads.rc } # Static OS X builds against Qt4 need to explicitly include the text codec plugins. macx:static:contains(QT_MAJOR_VERSION, 4) { QTPLUGIN += qcncodecs qjpcodecs qtwcodecs qkrcodecs } macx { QMAKE_INFO_PLIST = Info.plist QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.7 CONFIG += link_pkgconfig PKGCONFIG += SDL2_mixer LIBS += -lSDL_sound QMAKE_CFLAGS += -std=gnu11 -fvisibility=hidden QMAKE_CXXFLAGS += -fvisibility=hidden QMAKE_LFLAGS += -dead_strip } else:!android { CONFIG += link_pkgconfig PKGCONFIG += sdl # Normally we would use pkg-config for SDL_mixer too, but it has to appear # in the linker flags before SDL_sound, which lacks pkg-config support, or # else we crash. LIBS += -lSDL_mixer -lSDL_sound } win32 { *-g++* { QMAKE_CFLAGS += -march=i686 -mtune=generic QMAKE_CXXFLAGS += -march=i686 -mtune=generic QMAKE_CFLAGS_RELEASE += -fomit-frame-pointer QMAKE_CXXFLAGS_RELEASE += -fomit-frame-pointer # Dead code stripping (requires patched binutils). QMAKE_CFLAGS += -fdata-sections -ffunction-sections QMAKE_CXXFLAGS += -fdata-sections -ffunction-sections QMAKE_LFLAGS += -Wl,--gc-sections # Don't dead-strip the resource section (it contains the icon, # version strings, etc.) We use a linker script to do that. QMAKE_LFLAGS += $$PWD/w32_linkscript } # We don't really need libmad and libmodplug, but my w32 SDL_mixer # in my mingw-cross-env build environment does. LIBS += -lmad -lmodplug -lvorbisfile -lvorbis -logg # So that we can use _stat64(). This means the minimum version # of Windows needed to run the application is Windows XP SP2. DEFINES += __MSVCRT_VERSION__=0x0601 } RESOURCES += resources.qrc # We use warn_off to allow only default warnings, not to supress them all. QMAKE_CXXFLAGS_WARN_OFF = QMAKE_CFLAGS_WARN_OFF = *-g++* { # The code does a lot of type punning, which breaks ANSI/ISO strict # aliasing rules. QMAKE_CXXFLAGS += -fno-strict-aliasing QMAKE_CFLAGS += -fno-strict-aliasing # Avoid a flood of "unused parameter" warnings. QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter QMAKE_CFLAGS_WARN_ON += -Wno-unused-parameter } # Where to find the portable Tads sources. T2DIR = tads2 T3DIR = tads3 HTDIR = htmltads DEFINES += \ QT_NO_CAST_FROM_ASCII \ QT_NO_CAST_TO_ASCII \ TROLLTECH_QT \ _M_QT \ T3_COMPILING_FOR_HTML \ VM_FLAT_POOL \ USE_HTML \ TC_TARGET_T3 #CONFIG(release, debug|release) { # DEFINES += VMGLOB_VARS #} else { # DEFINES += VMGLOB_PARAM TADSHTML_DEBUG T3_DEBUG NEW_DELETE_NEED_THROW #} DEFINES += VMGLOB_VARS macx|win32 { DEFINES += OS_NO_TYPES_DEFINED TARGET = QTads } else { TARGET = qtads } # These macros override some default buffer-sizes the T3VM uses for UNDO # operations. QTads runs on systems with a lot of RAM (compared to DOS), so we # could increase these buffers. Normally, the player would only be able to # UNDO turns in a T3 game only 10 times or so. With larger UNDO-buffers, more # turns are possible. See tads3/vmparam.h for an explanation. Note that # VM_UNDO_MAX_SAVEPTS can't be larger than 255. # # This, however, incurs quite a bit of overhead, slowing game response down # over time, which is why we disable this for now. #DEFINES += \ # VM_STACK_SIZE=65536 \ # VM_STACK_RESERVE=1024 \ # VM_UNDO_MAX_RECORDS=65536 \ # VM_UNDO_MAX_SAVEPTS=255 INCLUDEPATH += src $$T2DIR $$T3DIR $$HTDIR win32:INCLUDEPATH += $$T2DIR/msdos DEPENDPATH += src $$T2DIR $$T3DIR $$HTDIR OBJECTS_DIR = obj MOC_DIR = tmp UI_DIR = tmp # DISTFILES += AUTHORS COPYING ChangeLog INSTALL NEWS README SOURCE_README TODO # Extra files to include in the distribution tar.gz archive (by doing a # "make dist"). # DISTFILES += \ # $$T2DIR/LICENSE.TXT \ # $$T2DIR/tadsver.htm \ # $$T2DIR/portnote.txt \ # $$T3DIR/portnote.htm \ # $$HTDIR/LICENSE.TXT \ # $$HTDIR/notes/porting.htm FORMS += \ src/confdialog.ui \ src/gameinfodialog.ui \ src/aboutqtadsdialog.ui # QTads headers HEADERS += \ src/osqt.h \ src/hos_qt.h \ src/h_qt.h \ src/oswin.h \ src/hos_w32.h \ src/missing.h \ src/globals.h \ src/sysfont.h \ src/sysframe.h \ src/syswinaboutbox.h \ src/syswingroup.h \ src/syswin.h \ src/syswininput.h \ src/sysimagejpeg.h \ src/sysimagepng.h \ src/sysimagemng.h \ src/syssoundogg.h \ src/syssoundwav.h \ src/syssoundmpeg.h \ src/syssoundmidi.h \ src/qtadsimage.h \ src/qtadssound.h \ src/dispwidget.h \ src/dispwidgetinput.h \ src/qtadshostifc.h \ src/qtadstimer.h \ src/confdialog.h \ src/settings.h \ src/gameinfodialog.h \ src/kcolorbutton.h \ src/aboutqtadsdialog.h \ src/config.h # QTads sources. SOURCES += \ src/oemqt.c \ src/osqt.cc \ src/hos_qt.cc \ src/globals.cc \ src/sysframe.cc \ src/syswingroup.cc \ src/syswin.cc \ src/syswinaboutbox.cc \ src/syswininput.cc \ src/sysimage.cc \ src/syssound.cc \ src/missing.cc \ src/qtadsimage.cc \ src/qtadssound.cc \ src/main.cc \ src/dispwidget.cc \ src/dispwidgetinput.cc \ src/confdialog.cc \ src/settings.cc \ src/gameinfodialog.cc \ src/kcolorbutton.cc \ src/aboutqtadsdialog.cc unix:SOURCES += \ $$T2DIR/ostzposix.c win32:SOURCES += \ $$T2DIR/msdos/ostzw32.c # Portable Tads headers. We simply include every header from the Tads # directories. It's sub-optimal, but the safest solution. # Tads 2 headers. HEADERS += $$PWD/$$T2DIR/*.h # Tads 3 headers. HEADERS += $$PWD/$$T3DIR/*.h # HTML TADS headers. HEADERS += $$PWD/$$HTDIR/*.h # Portable Tads sources. We always know which are needed. # HTML Tads sources. SOURCES += \ $$HTDIR/tadshtml.cpp \ $$HTDIR/tadsrtyp.cpp \ $$HTDIR/htmlprs.cpp \ $$HTDIR/htmltags.cpp \ $$HTDIR/htmlfmt.cpp \ $$HTDIR/htmldisp.cpp \ $$HTDIR/htmlsys.cpp \ $$HTDIR/htmltxar.cpp \ $$HTDIR/htmlinp.cpp \ $$HTDIR/htmlrc.cpp \ $$HTDIR/htmlrf.cpp \ $$HTDIR/htmlhash.cpp \ $$HTDIR/oshtml.cpp \ $$HTDIR/htmlsnd.cpp # Tads2 sources. SOURCES += \ $$T2DIR/argize.c \ $$T2DIR/ler.c \ $$T2DIR/mcm.c \ $$T2DIR/mcs.c \ $$T2DIR/mch.c \ $$T2DIR/obj.c \ $$T2DIR/cmd.c \ $$T2DIR/errmsg.c \ $$T2DIR/fioxor.c \ $$T2DIR/oserr.c \ $$T2DIR/runstat.c \ $$T2DIR/fio.c \ $$T2DIR/getstr.c \ $$T2DIR/cmap.c \ $$T2DIR/askf_os.c \ $$T2DIR/indlg_os.c \ $$T2DIR/osifc.c \ $$T2DIR/dat.c \ $$T2DIR/lst.c \ $$T2DIR/run.c \ $$T2DIR/out.c \ $$T2DIR/voc.c \ $$T2DIR/bif.c \ $$T2DIR/suprun.c \ $$T2DIR/regex.c \ $$T2DIR/vocab.c \ $$T2DIR/execmd.c \ $$T2DIR/ply.c \ $$T2DIR/qas.c \ $$T2DIR/trd.c \ $$T2DIR/dbgtr.c \ $$T2DIR/linfdum.c \ $$T2DIR/osrestad.c \ $$T2DIR/bifgdum.c \ $$T2DIR/output.c \ $$T2DIR/osstzprs.c \ $$T2DIR/osnoui.c # Tads3 sources. SOURCES += \ $$T3DIR/derived/vmuni_cs.cpp \ $$T3DIR/askf_os3.cpp \ $$T3DIR/charmap.cpp \ $$T3DIR/gameinfo.cpp \ $$T3DIR/indlg_os3.cpp \ $$T3DIR/md5.cpp \ $$T3DIR/resfind.cpp \ $$T3DIR/resload.cpp \ $$T3DIR/resnoexe.cpp \ $$T3DIR/sha2.cpp \ $$T3DIR/std.cpp \ $$T3DIR/tcerr.cpp \ $$T3DIR/tcerrmsg.cpp \ $$T3DIR/tcgen.cpp \ $$T3DIR/tcglob.cpp \ $$T3DIR/tcmain.cpp \ $$T3DIR/tcprs.cpp \ $$T3DIR/tcprs_rt.cpp \ $$T3DIR/tcprsnf.cpp \ $$T3DIR/tcprsnl.cpp \ $$T3DIR/tcprsstm.cpp \ $$T3DIR/tcsrc.cpp \ $$T3DIR/tct3.cpp \ $$T3DIR/tct3_d.cpp \ $$T3DIR/tct3nl.cpp \ $$T3DIR/tct3stm.cpp \ $$T3DIR/tct3unas.cpp \ $$T3DIR/tctok.cpp \ $$T3DIR/utf8.cpp \ $$T3DIR/vmanonfn.cpp \ $$T3DIR/vmbif.cpp \ $$T3DIR/vmbifl.cpp \ $$T3DIR/vmbifregx.cpp \ $$T3DIR/vmbift3.cpp \ $$T3DIR/vmbiftad.cpp \ $$T3DIR/vmbiftio.cpp \ $$T3DIR/vmbiftix.cpp \ $$T3DIR/vmbignum.cpp \ $$T3DIR/vmbignumlib.cpp \ $$T3DIR/vmbt3_nd.cpp \ $$T3DIR/vmbytarr.cpp \ $$T3DIR/vmcfgfl.cpp \ $$T3DIR/vmcoll.cpp \ $$T3DIR/vmconhmp.cpp \ $$T3DIR/vmconhtm.cpp \ $$T3DIR/vmconsol.cpp \ $$T3DIR/vmcrc.cpp \ $$T3DIR/vmcset.cpp \ $$T3DIR/vmdate.cpp \ $$T3DIR/vmdict.cpp \ $$T3DIR/vmdynfunc.cpp \ $$T3DIR/vmerr.cpp \ $$T3DIR/vmerrmsg.cpp \ $$T3DIR/vmfile.cpp \ $$T3DIR/vmfilnam.cpp \ $$T3DIR/vmfilobj.cpp \ $$T3DIR/vmfref.cpp \ $$T3DIR/vmfunc.cpp \ $$T3DIR/vmglob.cpp \ $$T3DIR/vmgram.cpp \ $$T3DIR/vmhash.cpp \ $$T3DIR/vmimage.cpp \ $$T3DIR/vmimg_nd.cpp \ $$T3DIR/vmini_nd.cpp \ $$T3DIR/vminit.cpp \ $$T3DIR/vminitfl.cpp \ $$T3DIR/vmintcls.cpp \ $$T3DIR/vmisaac.cpp \ $$T3DIR/vmiter.cpp \ $$T3DIR/vmlog.cpp \ $$T3DIR/vmlookup.cpp \ $$T3DIR/vmlst.cpp \ $$T3DIR/vmmain.cpp \ $$T3DIR/vmmcreg.cpp \ $$T3DIR/vmmeta.cpp \ $$T3DIR/vmnetfillcl.cpp \ $$T3DIR/vmobj.cpp \ $$T3DIR/vmop.cpp \ $$T3DIR/vmpack.cpp \ $$T3DIR/vmpat.cpp \ $$T3DIR/vmpool.cpp \ $$T3DIR/vmpoolfl.cpp \ $$T3DIR/vmregex.cpp \ $$T3DIR/vmrun.cpp \ $$T3DIR/vmrunsym.cpp \ $$T3DIR/vmsa.cpp \ $$T3DIR/vmsave.cpp \ $$T3DIR/vmsort.cpp \ $$T3DIR/vmsortv.cpp \ $$T3DIR/vmsrcf.cpp \ $$T3DIR/vmstack.cpp \ $$T3DIR/vmstr.cpp \ $$T3DIR/vmstrbuf.cpp \ $$T3DIR/vmstrcmp.cpp \ $$T3DIR/vmtmpfil.cpp \ $$T3DIR/vmtobj.cpp \ $$T3DIR/vmtype.cpp \ $$T3DIR/vmtypedh.cpp \ $$T3DIR/vmtz.cpp \ $$T3DIR/vmtzobj.cpp \ $$T3DIR/vmundo.cpp \ $$T3DIR/vmvec.cpp android:OTHER_FILES += \ android/AndroidManifest.xml \ android/res/values-et/strings.xml \ android/res/values-fa/strings.xml \ android/res/values-id/strings.xml \ android/res/drawable-ldpi/icon.png \ android/res/values-pt-rBR/strings.xml \ android/res/values-rs/strings.xml \ android/res/values-fr/strings.xml \ android/res/values-el/strings.xml \ android/res/values-it/strings.xml \ android/res/values-zh-rCN/strings.xml \ android/res/values-ro/strings.xml \ android/res/layout/splash.xml \ android/res/drawable/logo.png \ android/res/drawable/icon.png \ android/res/values-es/strings.xml \ android/res/values-ja/strings.xml \ android/res/values-de/strings.xml \ android/res/values-nb/strings.xml \ android/res/drawable-mdpi/icon.png \ android/res/values-ru/strings.xml \ android/res/values-ms/strings.xml \ android/res/values/strings.xml \ android/res/values/libs.xml \ android/res/values-pl/strings.xml \ android/res/drawable-hdpi/icon.png \ android/res/values-nl/strings.xml \ android/res/values-zh-rTW/strings.xml \ android/version.xml \ android/src/org/kde/necessitas/ministro/IMinistro.aidl \ android/src/org/kde/necessitas/ministro/IMinistroCallback.aidl \ android/src/org/kde/necessitas/origo/QtApplication.java \ android/src/org/kde/necessitas/origo/QtActivity.java \ android/AndroidManifest.xml \ android/res/values-et/strings.xml \ android/res/values-fa/strings.xml \ android/res/values-id/strings.xml \ android/res/drawable-ldpi/icon.png \ android/res/values-pt-rBR/strings.xml \ android/res/values-rs/strings.xml \ android/res/values-fr/strings.xml \ android/res/values-el/strings.xml \ android/res/values-it/strings.xml \ android/res/values-zh-rCN/strings.xml \ android/res/values-ro/strings.xml \ android/res/layout/splash.xml \ android/res/drawable/logo.png \ android/res/drawable/icon.png \ android/res/values-es/strings.xml \ android/res/values-ja/strings.xml \ android/res/values-de/strings.xml \ android/res/values-nb/strings.xml \ android/res/drawable-mdpi/icon.png \ android/res/values-ru/strings.xml \ android/res/values-ms/strings.xml \ android/res/values/strings.xml \ android/res/values/libs.xml \ android/res/values-pl/strings.xml \ android/res/drawable-hdpi/icon.png \ android/res/values-nl/strings.xml \ android/res/values-zh-rTW/strings.xml \ android/version.xml \ android/src/org/kde/necessitas/ministro/IMinistro.aidl \ android/src/org/kde/necessitas/ministro/IMinistroCallback.aidl \ android/src/org/kde/necessitas/origo/QtApplication.java \ android/src/org/kde/necessitas/origo/QtActivity.java \ android/AndroidManifest.xml qtads-2.1.7/qtads.rc000066400000000000000000000016051265017072300143040ustar00rootroot00000000000000# if defined(UNDER_CE) # include # else # include # endif #include "src/globals.h" IDI_ICON1 ICON DISCARDABLE "qtads.ico" VS_VERSION_INFO VERSIONINFO FILEVERSION W32_RC_FILEVERSION PRODUCTVERSION W32_RC_PRODUCTVERSION FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS VS_FF_DEBUG #else FILEFLAGS 0x0L #endif FILEOS VOS__WINDOWS32 FILETYPE VFT_APP FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Nikos Chantziaras" VALUE "FileDescription", "QTads - A TADS Interpreter" VALUE "FileVersion", QTADS_VERSION VALUE "ProductVersion", QTADS_VERSION VALUE "LegalCopyright", "Copyright 2013, Michael J. Roberts; 2013, Nikos Chantziaras" VALUE "OriginalFilename", "QTads.exe" VALUE "ProductName", "QTads" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END qtads-2.1.7/qtads_48x48.png000066400000000000000000000116631265017072300153500ustar00rootroot00000000000000PNG  IHDR00WgAMA asRGB cHRMz&u0`:pQ<bKGD pHYsHHFk>IDAThޭy]W}?oZRk$vx Y<.S!E S5$8!Te(j 2Ԑ!)&$@3!ƌd/#[VjKo~Ι?^LTݺ{=os_詉>· DBUPRV Y>Oײ@f}qqaiw~S7ڨyiٿL<^XNs.ѐ9OGئ 3,\2mjd_O<]a.FoG-=rH*pa?4+{~fnWz G6O(ރ?!aff0A#WSۀCJt>rlfuz>x>R23;S[ߵ_܈g? <QJbǎAOva&@^ցrJ(%9s MϢM* 5x# wF %tMDi508JRHJ1`=3÷?z[+c0S@)ɖcLs¯](%m GƱtH6iH% e77q|$2>6zx&n9ȩ겫ˆT@>!&ژ$z(XmvvS\#.KboPҽ ²MsoPJIҩ$$For-u ;R\6K*°RJ)tLz=w~[ENe 鷐^ 5^6^%{װJCHH% B* ]Dz [֟|oo,8?y?j Lq{!_UoLrĭ˙Y !$ϗئ5ݗu Ha_ h5_5~۰c>v #6Nx_qnY2][wns۰q]JZyMVN!_9v sadۨ@6vMW/@bl:7%ϼG5rJv/_x,{cQd6;aN<>sp*%4Ź)'Y;Iq~NN$3N8Gg^vPӲtX~(Md~y,/#.9TLdyv)]k(J.&ـ@)$/M-2c$i6GaM|# dckF}/+h4GqI9(-1t ^>v8#zy4J3Xj6LT7%7SoT0 }0t WQTL6qǽ=` rC[D,0gg,΂i`D"(X OB?Y|ɓ@nG\h"(xZ&*^@~ 3N|z}  ?+Pp4n>+3ӬDɀ[v&ڧƓD dX2};SϯAA =+p 4x/ޕjt-(V47>ϑ7˼gO'$ p1\C0CRC 9@۶՗ik&ָ-D|dv 8[j!5q(u Sߊ)i6s؆6K"͉3gYy0^N/Qz ➝9ptj߱*wbFeyqx~|m7H.ZьNܕI6o*+OF>Ru+}X>|4 ihƳsܕ}-}17طyR}rFZ\n$6#Bp:-KۂS?PuD+YJIq#j&ji  q]FodN¹)`bxd nD3(%M%ܙ!@Ѝh~VVOD /@5hD!% p O ^.NSY:$KsX?Elҙ! 6]G7`դPex%|3KOJV:km_v`8 d>teP%|wߩ[ɵ--'ksQ-u:dsC o!r $SY+J 8j5"a u1=*FYO>kްW& StLXhhD P*/*qrqwp`! GQ3(qkʹQuՑz-v& F 4:Q7­BDҊl,c"6):n[w*۸';8F< ('PAÅi7)橔TʋL e ${ʰ8 wΰA|#cz*daaa)AxzJ_@h[/*^,o]y.rri[)R)v r dsC:B[ +! [ӅW\pɿ)mn$8J}\OhE\,O4 H$jZRqZL@cadCfw+&ߔD@3P+>z5G:\*L[\MnˏW{dl8ۇ\V+KH)rAvztLPdltvE(;IJgf *>Ï {}ЪU/OP)u j[c'u|NץmS̟g0OjҜ<'=>XI4]!f)AHصw$TzU}rHV^`Ll6O  hutIME %+IDATxżw%Wy}ΩafzȞ4DX$[6`{b]kydADl(K#iF#MιopQ{FXq>ꪚ:zI%;l=fuzR&JX]%ӿyi_yXș3ʿdVg~ :bAP0ݓKnp `JhFLT$ s@BP*XJEKhR9UբPh1?!z1|SPL$K"ڐ̠^HnU1cR-Ju>@-!i&\"*  &T  D*(`ɏsd\$DnC}0799(ݕ{nzվy(f|2w Dk*Hԃ"f/^<0|}&dm_suw$HA]$cM:Ȉ"ybo}UΙ=[^?g>Ue%/dfO+5ܭDWdvU$RU;ba v@1EEDTUUW\Vi[TLQ#1*5QФ)F8M泮~gb`9_t6E:wHNeT=' ]+S+t4M n%@kh.&xtJD:3,FF}xPà,R,tm)zzzMU A"1  aN@@YZc1VGP@x{)5CP9*_SGZ]K&&mV> t z~U;}lz/E/Jn8/BīHH1PT%,Di&^EEs ]R.tdimI#_@QYR>Ϫ{Uu$T*U  QF=ACW'e?%4OLMgos-{TD@#!jBFO9s;嚕FG ڱ\:QUDTX;_slA PLTī*缳e\k:r[~6c=~#Ho "5_ĨHHAL_ETD}8aY{)똰yzZ7+k"%,Q#A7P((JEFq3 ,Qy'%oƟ{٫~z[_Aq+'vu>V:QC5;\"ZbJswqW?yNas"HnD$W8逓s浳s ) v0tJ IEg;TA=BhsN)ty>Ca?JWy)ǻ)qNq.硇gj~F6̵Vr9@rC4U$(0UmM(#9*m@3pGS GDaD$I]7_]c?Sūϥ{o"9ᝣ^o[׼/~ :G;4\DL0Cψ1g#PCdxxQQ.DbD4 PU=^sӯ{?CeR/${ũ's jAPy>v+^-W}}WSM~ ҊU.izB;e$ngF;1P=P.9?iqO`6U?헇,NN֬:& A$~d !rG;7KcKU!B (F=ZLjs鷏rGV=/%?xKp%dD}OE}o.񩈤d@ Y"v"GZuP,_~$?pTRBv܏݅ftGM}=e>uٓ`%m &(oZo8B*J_*[+l/c U$ީ,E86OzrՉ'l;|{v^3.F%ALn|w(ZZx #Տj˟}jzF XZ $[jbLy׮~s>cGv)>8/}NQÿ9V!%좺7blp% BfS.oJ+C/gtRWOƔTrU1.mGRڬ b"rC|G88pE`m cejEOo|u)س̷\k<8FZ,ZتQ%Y94DVK64Nr=Hi3MRI5NRi5M=IP/uq7=0XhFdYL^TK[9"*FaثAX(^ *Z\5kj&IlNBrY Ӷb%_B@>pTtxޢ)2=.2,dױA/j&_%IG_<(P7l{7?k~<|N-b0kG;k*[99;9/d#^U z6$mvq5)_ ]%ݲe+ħ!\9wT j"At 9@AY=:,qWCKCZӇH 7=5!dKOȉU<|K&ͼ%Ds'\u=ޒ$q};?|Aq89*8wϼC7̳`gctQAŹc ` AVVK.<M3|z$r J$U[طΤ.z Beb.jzTd&&hX݅O={q9U,S^1XTBdL/ vKTwVqqІ\vիyO5ݼmQ,ֆT%xNoH KA,Bȡa6ЌKcuIes,6{5Y}Cޑ"edae}c ߟe#Lj)ҘEJ#)\}&2,-/q mH)"C`,fCY\`zqEWWDdN?/oz7E R7PUdY, IByԎS)1[~Aռ M㜣nTr,M736|ub(FD#xȑ\,]CwPT*]?ƒ3;,+:>3ԛ qN.qGS{7j_ yOWIm]8UЉj2R8g@PI8N gKŔ>8NȌU7 E6 E|'⥣939;,r}(oP _|Cf 8yp]";Fzxy[x[ٻb(QʼnKsf]"LXvmF}rp{%KD3HV#Qaǖ \Fe %t¢@,=& Of }A3S"JEeӪ󐃖WR4)xl?4C5K0Kx-C)Bh9Ͻp;>o6yBP, 8y#0"" T >8{;zsc8?0,({/zPG}&AyzXvWΎv?7r3`QWdՇHa+P)Dݩ:NoA'd}ST6>u+{g2P-qػsM2:֥#RjG-b,bgL=̉29'29~i@06dӖ}sغm7Ãlظ^"+;;!b04q wx Z4u^4 P "KZV􄗞nCy#_7UYxɂ-[^2^NZ[dr0yF@A۵ f&C:9-qҭe߅W˖ d'al P{撰V8 8 *]+tc'jUU| *6]Ȳq.vLoZq>9:.޼!7wșjr]&Z6=M]TTю0 *))YYfăz;8q'h5W4˔*>sxғ، B]_1MkSX}TpnSAAeƋ^}t)b 0hM@e t]C#34Qd3Lw.dFCC{l iy\  kw\',Jk.+]Y[i)m NqR_jո+K'i^{pkW_T-K,V20fB1B-{.v]QlPIK\("&mv'`Hj{6oe-UΠի21qTzzEo# èiO@J:{3fMD5B=K"Z}k+P&N7[/)ri k~Z.-0q򨌏֓'KQ#c5 nDuۙcYeБ5r)Jק5lŸĐ hp)m_ QB*UA%^yՃ,M M:O:iR0,b‚ Y{FmQ%Ǝűpaj%diB>ٺLm{ٰ0,P(.KIXsIGA gaZu91yld,F{Akw`5xs{cEXCdC)b"~4ΑeD= "BDkO|n9qnfOpFFXB&=snf^{"iH&8VmYk4<7R.EH-ƶhC'I*h,:Hs տ(ZQ,ygBK`Bb D q %eq"&BU eM'35~C̜<9/mc HU(b4KȲ Mb$>Ui>ӜBGgjTQ啺8 desjG>PIltBb U+PDZDB1DspũU.Ap-Ƀ,T2i7{6sٴl-t C ݪ>=.Kb',s?'O/jEcLiUֈD{H v{4??:we<+gxH_K`U4!PW\4KGXI{f%KBXse3.گ*.9h/'Y=yNH֐X $ #˴^_% u`B ^,*F]XZ钠=CbՏ|(nz!P.p,sԑe!-W,mi_%,ϕI6Y\6"hV'^ڏOs'!A@eq,MdnnɉcazjEkߎ1y_OO)x7Z J7%`^߶?U!#`yqYyΑe,ũSU%IH[kebQp+5'N٭k1tڈyq8SSc2?7, 8‚QfݼCDvȃGLϜ ֊z-!b7£Czp΋˜,7xv!Q*ƒa$P.8|z.Y&kωkϫV0\jFB׺u}K~8/?M0:P,坧 8RH'=|֤1=ĭvV-LN`|KKZMZ"9܋ݼ-[vP# # N3T&iCzBY%lu cYZm,b'lxMR;\CcD#tǞ:ݎ?K뫄aHPJM[DCM Y*i겔vrZOΞ s#n- f*b>̕@faJmlb~ԁ}֡ wfΝ2T iwCL^ ZU{ moa=,#sY^rxfx[P *&"M&Ə33=AO# %vH)y瑇s)n57Mo>46ǥ{6oXURq$x$((] ;QG5UZO>ݲ65Vk PRhtuuI&ǙfNĚeTtwƍ[Q6na-Z(Ġ׾Ҧzbjh3V٪__Wy6[>| O;}[ݏJTxplswoV%OHd cK1&`qa\eIE o`Ya(TGP4M5sᲄf7 "ƄjK7,^g1PEJG>?fV{'Y羼y_$=[GzIݚ䄘REF)|90!AqcL:( s077#KK t?*arOfppaDqN3bҴ;^3 (c }a^lԋ^4v,/[Jkmq`܌3?=@|s~k}FivP͖"Ta?W/*H %,O}B!}a&6no&^w-YKba z5,HPFݜ6b&1I[s$$@BDTV\g~r^wrk.ma{|_d9[7!Z@c:o&4 A^bMz{:d/e9$M,_-`lKX@XAlN#փC+2d5$m\:@k頴kGY5ɯ Tkڐ^s)[_r/kQ(lVjK6o^ [ʿ ;P bIZsj^TN+kwNBU,ACX@T= "t*&z>%tPZHKx5S1ag@G<f6r;ް07_ӥ'~VFkOGN5) ).U>/@P#,  }Q/A{*@gA ګiz>F8I8zcüLݩذ+ "n}:󞅥TV +1n<7lk?/@ 7} @Go?hoGM q]5UQuZ'SaqD0aq@Qi0 ZV," iMqĵڮaةr-&F-)3"`1FV4N+LV3YZMY\N5uOX%lꝌK?dogq~DϾU릂 8O"? T^!(h.=d|S_c3z;}Ww{~s9|ewLsŇh. qi,Yc+oLX˜<˖:%˔v_J_JX\i1I 8 !]=Wf.`.?MϾ=| 0|&OؠA]g_ZM kZw2)zr-eg'R݅ڊ/l7F1>c 5b v!louo/ T| =֫ 2FTi6ZՆLV3Y&Qb+4Rnp*gVv2GfdaiYC'u~ak#tQ L_|[מ}>{>=\Gg}ZZ#$S/i,ל/g~a9;ejQz'k؍T<ۤIe,) hwX*j=nJ}uYguVz덦fčz=As=;>s˿g~/P]Gr31l3ıN|Fkʼnp1&E-t]j9cgXR(Py -ebeҁ~izG܈%ɜ(`0k,/ե/&G?;~b =ZtKk^G1Z 09Oc_:x{=[ BWF9ilݨ-j˓:4v_N/\)IENDB`qtads-2.1.7/resources.qrc000066400000000000000000000002471265017072300153640ustar00rootroot00000000000000 qtads-logo.png qtads_48x48.png qtads_72x72.png qtads-2.1.7/share/000077500000000000000000000000001265017072300137425ustar00rootroot00000000000000qtads-2.1.7/share/applications/000077500000000000000000000000001265017072300164305ustar00rootroot00000000000000qtads-2.1.7/share/applications/qtads.desktop000066400000000000000000000003461265017072300211420ustar00rootroot00000000000000[Desktop Entry] Name=QTads GenericName=TADS interpreter Comment=Multimedia interpreter for TADS games Exec=qtads %f Icon=qtads Terminal=false Type=Application MimeType=application/x-tads;application/x-t3vm-image; Categories=Game; qtads-2.1.7/share/icons/000077500000000000000000000000001265017072300150555ustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/000077500000000000000000000000001265017072300165145ustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/128x128/000077500000000000000000000000001265017072300174515ustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/128x128/mimetypes/000077500000000000000000000000001265017072300214655ustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/128x128/mimetypes/application-x-t3vm-image.png000066400000000000000000000251401265017072300267140ustar00rootroot00000000000000PNG  IHDR>a pHYsnu>tIME (WbKGD)IDATx}{yW@/4@$ adb̙cT0B0J X=FxKc;"j͛7 @q.Q!q _;Ą9C/6ݎ=Wlfu@{Kb˄o0Kcb 1xdƍfD=sRaC.#0 0-0w\PNŘ.e0;6l`6 ec~z-CdNx{{ h0ܹ|M;b,vi4k, *iy&.Lv``H "6fQڶmA}^a̙pBs`ׯ_?k/x&803qLhCЌ&L4OELytnH~#NTm, S`h4m2gn iݺuvg$G3%Kh#uZ hfoBœN:fT Nj!?ۼG$bW(;]*tL?/ S {1>X<L0cƌOߎk׮6 >l!w7q[O}=x˖-5_& ׼o:K:3 AA%h&@I .袣ѱ3!~7!H8Qn_MJ_'~fNk_MY&ajj[*G %uǰXd5D2R4^K~H3x)/; hѢ;c?/EK* @"*e a lܸ11 AOR4Մ&ߴi ??>Rܦ]`f>k_~FML)6mH^ 35c!EpM(*:`ٲeSNt!<3b>xU)RO$&y*Y7ZE}Qq?0 >}_LC lXzaW HaܱABn;i$(wsoFځPhӂ Ln'%ܰa;ILJY=|avUWQRtAV}y!5y&.Y2H$ Ri`8H;v!jS̔R x'x28ƁMxo' dciwCÆjs|0@]E$}׮]fVX}Ox*oſ[~=njpI}ڒ ~^ !0-s*ǝE Rvo3 *p=s.?39 6}Bd)Fl8}OAA&jL_ִh!:'C* nV=Cctxҏ6c DNV +66YBHvYl1::lix S=C( !sڬYM _1OKg7l;+|2W&}aXbJ^foНܻv͟IwlSz ) ̅iY̕},1).L `NՉq#taYkX=L jW'yftmFũH~w۠s ^W߳~xZ[3Ps%?ʏ.Љ0~s0Oy|}[b< y=~Tjt᤾_Lޔ"& W\A_̅ C(DeIgA|%C;yo y1fvQnU~C Q/:Y  h],z/@:۠=CEkSD#O%j%&AyP*$l J2tbOhdA'* SRVbF[#Ev~0{o@O|~pS#Đx7:{I_LP{XgYLHܴٓiRQ}A<~pBmE:cyB8O7/c:_njzRÒ>/e =T;D?3f7Doꌘa0v-RClR}{QR߉kGxyۘÅSz}>x/U'~2UcHtRaXK8 %0_X>EjY~YrS^2.t]g4- u{ke*0p|^eIg>x UfiDod+%LlDm|s!qS,v;"c',RC Ȉ_y !iO@vߥIޠKS%=_0$ Tךw Bl_(|z~ReW &,W#p< Y5cK-Aj&:JAL*+b8, Bm,\nBFq?*܄&BHI(-Ձ"S䪉RR6 C&i jōOh"E"Ya<,hXcTVx Ӻ^3@1xX>s%~|*@Sء|O2ț hH:jԩ#PR_2'`;<ƞ>g1TQ#FiCòu Nvm3lT:X8)_S:p35 @R^h*V(̌V iMgqOJK5tU$Qb&!pp]P+GJ{]=WHE!@S8?&P#ڣNA68I$Еkmȓ|@k)Ƞ 0j@A0 7mtPpd nHag }RE`QDaO t}nE{e` dWJ8c'>`?0aAQJS0()+Wm#a dctk(bPu\T1ٍn\i@&:=(Ph'e]L\@s5"!F `'RެYɨW`h"Y( nPbG`jHz%}5 ~< ~||lN+1,TP=mA-@ LjTLr$-eX@YPj"Eޟ2;չdQyb!:Fĵ/!/2blMǵ upH(ֿ .jm6Z '2xDo|͏1Gw{ $zDt՗ЭZN7]{ CmWId_l) Q FuDwYw-4Qxa580IbDuEa9} СLF/C| *,E & |a,c0 Snsm-,OPcc tFZOA;Dg)ã~-S_8̺}'h +@/  hG4=e\״cE!Y!(|s|tx??L)d#齉hGLta})y_Nj3Aa<ECˍQGhǂ*_.OQȑ ,a$: j_! %Ltۍ+Fؔч\>70 /P>4ڍ[<[сI:/Onl3ѣCô>#4CeM%mOp8MQ.bUd^vaޢN¾Yw 2 #x>p.vt +h4|xIHlY!d4$llMf(kyg@ hW q|@P̈́Rq4_l1[+ 76p̦7g?DGr;.,lл~dPJF ]Em02Gg\<~V8kt{ 鋟}ܹ#lyU@Pd{%g\\yօwuӦr)fL(90IأmFb(wipp#tl>!-MM2(>U@6}=otnXSb)]0hS㞘nMaSemM]mUɗ "+@* @t81_:cN,킉14Ar `ÐnSi~$$ҦjK?fHU{ 'PBx`@w6ӮZE 3@1?c6fLFY(ܶ6\FdPXa9y5apƤS =$YǶlb4` 3!s6:`E; ,v!|Ҟee=G&2A, /At0:8 `H?l&v?h#2,}vsIN7u9 0*f40He^` m&n\ER}ӊkϞPyj]"53s |2p}~0rt( ۮT{L`]@L !: 6<.#ٌNGQA#3(1+v/v(XPit駛߀ tzU0!UWI&TowyybBB (=?@d eY}zt<j*3=+ $e NB$hAAm!\H8T".n̨ڮ$5eK[3?0$ӛNMb]9<6JV`G@XKn(>F ^#Հ@# E#8GY|˺:3RE?0u,c5ѐ7I̻4`"z.xߟX^I#Esi,4G3 rc^%$I~O-M6: r%xJw X.M1Z9`&8keEᚎW^i)2wm:ڡ`!>f#({@ BWQ kB"v?Y.,0 ޠ4B'w.oPA :Sb:ĚX9:%dưRmpq fG|mkeE:zDTSʲY=rS?DՍcYޑA"ƵD] 6YtJ&##1ms@6VL4SBS]2dё<'Q탿U6diිG7@T4WɸW -x5vE}I' ؜gm# vBgsbJMQC J"DN;4 BuyG" ̕mƱվH3hg@,mx#hK.=ML^B"4@y!h[\^*.֞.>7$'Z:S0.t8D9e]L֨ |M1hiޢ+6F'i'EHN&\QOJ-8֭sm64LCw ,H C[9e/רm &S]p[!x8#0*a$/[۱#O)dοjV( NцV aB 7ye-FOځ@ڰaC ⊰P8HӁJۛ Ӎ,U@C FOq cRtv/JA($D!^#~s :PP(B'67-u2Q[mah- !וԧa$ ʀ%={ל׹ _Q(bCr̂"AjBmG?mL_TڀbpfuMBɠ(l @G\q %. 3Z1@t˕K; i܋ '$0GZ]E4tAv ^B1>Rݒ'  Fܦ<1`H>YHD*mŒODц{$+=6viig"hQD=kA .D"!8El'U̸T3\?az=P,\jGjwv>mgaC GEM/N;qw_^>g rՓ;fkV, tI)͊ejjlB[86I΋1bj\`&ipǏN>|p;z.j(MWFv42,l{JR]f} /PL(jO<7c*b_~|cc3L§Jgꫵr10-B\6R2rKro_O(5r5WA\2H< JvmGF Woō3# 3zlgi{E?ֲo^&zI?^EW.'z7|A.jlc:5Ɨ bvmuTO2#, T@!$Y)w᷃! 3xDŊ'_go{^Bz$+72P)X-vtt[9Lbc^ e5(=ٗtx8bA#00mdH|v7 3RK@q`-[vwAr:&ST3tUd[$Gpj]"aYQ{8=Hvt1"5L̵^k"x@B.RiG\@ ^Hz} >; ?$F-zk[!L\h'aEA9TN@ٟ [/7qGḴ׿} e hYHMk?XY?|$9 PQƍA\ `Fgh_S?E:MI]_`bdI䓉NAL,E%9_x>p*Z'XQSurNW'*k6?d y U9W2$6B,G`@|⧨=Grl1,+riɧՋc0Y %b0J'sM1 l|C ~TvO ò{X\l}_{pl컚VNSTONstmf&x<6~'9&&ygtmpA4uN]i㊉q0擝BF@ z=]O_kDc]P ZSG1a%<P Ѵ YX&|4Xz?;J:^wj2({c`y *Nm##kw? Mߵ)j^]EꞞ믱]ۻTʚL>X~wuݺ[wnݭLFٺIENDB`qtads-2.1.7/share/icons/hicolor/128x128/mimetypes/application-x-tads.png000077700000000000000000000000001265017072300331142application-x-t3vm-image.pngustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/16x16/000077500000000000000000000000001265017072300173015ustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/16x16/mimetypes/000077500000000000000000000000001265017072300213155ustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/16x16/mimetypes/application-x-t3vm-image.png000066400000000000000000000020301265017072300265350ustar00rootroot00000000000000PNG  IHDR(-SgAMA asRGB cHRMz&u0`:pQ<PLTExx''yyèھ۴uïʟqěnkzgvdram]iZeeeW^xJJ[ TVVUTT\xSS`TQOtt෷ϢUlauzz䮮֭1QS_TouuО[qkNmpp㥥؜J-V`{ݾkkٴ̶ffӭaaܖxxx¤\\ȜVVz))JJu##pn pWWJdItRNS8v r?1N wW }?????????7'S___iXū& pHYsHHFk>IDATO+j*pJ2JvqqSN>rXJ.Rrٔܶy$O$I$ܻf$I$)%)` ׀&XtYμW C5x~['q*|Vo$9ۭ)\4|ڜywIJ$Id$[*/Ừ%tEXtdate:create2012-04-13T20:56:30+03:002T%tEXtdate:modify2005-02-07T02:44:02+02:00kIENDB`qtads-2.1.7/share/icons/hicolor/16x16/mimetypes/application-x-tads.png000077700000000000000000000000001265017072300327442application-x-t3vm-image.pngustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/256x256/000077500000000000000000000000001265017072300174555ustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/256x256/apps/000077500000000000000000000000001265017072300204205ustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/256x256/apps/qtads.png000066400000000000000000001133251265017072300222470ustar00rootroot00000000000000PNG  IHDR\rf pHYsnu>tIME &bKGDbIDATx,U&|nUuy^zI)G[,r^c0`~X1i_6.eka`lc %+KO9=<9ΡܺUuTO7fS{+}|'s[cklٺWEeK ^]H|;VP Q~7(m0E0.I B5.=7~bS#%MȖ[CcKo#)\#zz1l ~*QTm>PT=2T @K)~L@ hj/#: t񡗾*<[|b [,]3%ƕ#Z:z(0Ljn(SaݤZה TK85sbe=)a-y( LFi(1 ]7.@_NzrYV(Ζhu7U_& !%ŗ_[?,dk{y谯W zO4 zHc@Z1ˁHDN-. \K&DtPO}URlLB(ԈF [ #hJ1p ɿz?lo -xuaERXqBAߘiw@n8 [MpFiR71iߓ$O9Fo{4t(t1j ruߧ@GIg< l}}$ jO?xxٓ[QVS2٫՛(*. (I^&Uw1.&&PŚmԃf뮶 v~zA)?Į%ƪ6rQUP[.=UMN$m %ؗu> @4A[S!DC0 W%OS5pG{&7ݹudDw+J?N~;+6 Cg fغ: @5:%661=쯨UȝCa$h}*CF6.4|c7#?nv}!_z֟g?[&aW6P٢`2>skh\Ϝ=ss5`c]laJ/h`GAapBf c#o!;R/r?i׷SI cĴgy58-td#t'`uc"Pٹ98slտgֻznHn{[`w>'S)2j,&|p;%\}dفovJ ?|6;|_*U/k |t]5ڿFse[Yc|^um;=ϽBLQWh뷖~ùtś44?LWueu? 'xME,h72ɿ恟\2?Vu}-.xH5=fTX-I.Äw{Ou89Y6iS\쪸Xk|j?As3.c3/ ĩBة?4_-pOl𛩥hnj.,[};U|'L}LMOW >lF콙 n~#Y*hk[m+]7[>W0'h?i?%=eG\Lj~+go#_=Kg24,@oz=HQ뛹La@LCR Z~=?%~-_f/c-+%_ ixؽkB['6ھݘ9B3 2Mـ[Vtp$ܽ3OP_]#P%-9d^_ܳ=gȶ W޸Pm>|mr 7*E#6p (/Թws+~ЃZ~+U)ʒJk%Uױ޹\*uY BU+µ}~sœd1_a>TةIHDC^rx?, ~\+~'{W=:b+9:ˉ{_Hʓ<{ Ȳ*@&͏ߧϨ _)lkh( OL\ly~81:~ˌ/J^m -MӣO?F-5q,@A L} f.mQ*pe|S WjE+&T򨭠mm1A ta|WN U 6;nM%Ą^7X`]ڃ G{=ޫdӯL(*st Ev`bTpLX|Lml>l  5?,%f:IӃ19<Ο: z1Xɀo?&oB!hn*O (Yv߹<]_C$g Q +"cDZFwZ0V6 kwE w7qB ga˳GVy^׽)o$Ic:YA^ tqp""<Ż)CWi=*=W*V]SOxqسk,^9-WXLjRH*8,Ub~d PrhP۵oEvߺ4,Ւ|J*&yT oKwf|07}yS|h}(dҩ5GYTb-h$s B=p15ںM 76мdoUe+nCmS<@[qvßޔ >?omgGGYFE%ՂĦ(={с}CH !Zwz? W(QhU~4;[s?`-RKqy]zdlA"~׺F-7^/^a./}}߯A$ֶ\c/<7%zn솗^}Y3 CRJI"g߰6l|vQTHgY̍p5|*}mX̤ԩ?zM8R4}RiO < =o?}٤rN(a4x 5Bߠ{Zjoo}װds9'j`#)S3?=?lHӬjZ|g Il90wѺK/} CQ:5(G]a+gi=YiР )`nʁa,-%̀Nn{΃RcϠXj$CT9`D2!: 86nFV9 Hګl9rA8t``#YЁ׀N8)Նprwj0.!!|K Ù؄d?C$<(tcC xKX;MUZЦ~c|Jxg]fϾ74Nv,N?Ks1}4 LϾ!әlHA˞{o!@TǴ|x2~`3'>M,gMIXZDϭ7GA{1}RREAͿ $!b&/}6^_g'&&9|`+6'DuovQ歋pR} qQp{jm;Gb ' lch!RuPӗY˫&;_kelonYa«ճ:w7կ{DBqMap " ۛaHjg 8eBJ>4C_S!/0k* (Z_sC@&@u0'mtsn_߀k5=30v*1c l6AAhjoX/pǵBDCW B" ˔wr׆85c*]};4|3 |f˳ʿuO4]>+ꃔceiH1$O6(-b:w,7F! 2߀mj8G%p_+fg9Qa34 Û'NA*1߻sP^ M-,xNG{gffqr@01Ӓ9'PDP6FlNA$:5k pO6e"j=Q;RօwpH}#wwD_Wu'W^;vU {Kp7[J;; ѯqC_O;»D:!|9Uآ&q"]ؐb[3cpP1$|~1 ab±bh&99w81Mjׁp,RpqP|}&Wx?N<Ωt|:w8za^1۩\Yu \W$50WNw=˅V~z]P\|{2ù Vu .wCje!A"M;`t^tV}l@ SLG}3l>0wϮ }>i`T _XlG;D܀g8U qَC, n!<:  dAS30gh~'/_^5uh`eJ[ q!m(A&p-/zi(&ρݵ|yȞssp8WHWn6@ u{c㓰H <\턉\;U;\s-6hccq"5A>B!(l~HLA7j0h@ wlÆꭘy'B];~_`Gy/"8ެgט_/-Ҙ]ᡓ:Mpm!W^j8a UhOkѺÂBwW;u^o.d3S@鿦6)"{?\yaoky:Ș@6_GY1 y2;=tJ {>E{kN&/4%Bb]4_q-.@ vRG:Y``ׯNpaހ!;#=3"\]{⨰04#hRbft, *D$'vt|@@C8MNA&, l'%Y%P}#To'd]Q ^(%z1VT,{YX>*ˋ2^/K'wrx}va^IHs: l+;Spc @eʶ1DXQSeRBxgrF/뉑Sp­wDo%wAf s=_*y8{307< ôE޾生UQ=t"ǵ*\s²M-;cټ`p/һ?.:xC@l,̦ymDDy>i8?vxnݎaᅲ}nq5/4"v9_;9P: #'`09~'CK BV3MkE{oq=Pݤ'|\vBbMSkfOѧr(ܙNL1a5NXEּνjo0]]ڟfT?xr9w?zju|"e@?ǖ s:dW7Oe$ǯ$&V [8?iwgfK} .Sg7n(죈^pl>Uи'&SЩ7#i/ Hd~"e(<¦±m~ n; BBs㾛τk*5;ү>$ËWCyv?X I>GmG)ȁ jP(2Mf=w ^qMq>| a}itjn玂B(5Ӕ@e8/$`O"L{`87(ܣvZ-EBAo~6 4ñTt];nSD#-D A=T|0lN!g-!Mf>u[uoO=3\Kyx}<|ѳ#-XȭiwAAbQ [\}rWg;y s/DRޤ>7iIyLsMZ>3—oK 1vxe-ϝ~5]T;󙚹w_b pS^;7'xXĚ N;>IxoaIpf)|NN_e<́A/oe,>_9&oHyK*'~$;7 iBw>~[篽h K`9qɳ07 &t <7 S߸8@LW4Xn@sG8GDD{w ފLmfS"7*2/2 !|(@ T*Cn޳Lbv ܧHM.?6 g&,Ji4C<)͟@_ٳQFWyɞPϻ!u iyO{GW e3E|΋sƀsșU[[r"ffbOOx=HE 4)DPC;!_LsOy,ʨ}n84~'dk`@/\ÁGu/0Ͻ*>q|G2\щ8dF_c14x3" `[_]?j -Ue7㟩"gID&Oý)H΢2Jsz璛-qo5{T-Gշs_]; _n<|*!w jg?oWkp'Ff \b_1أ< L TSEΫbFͮ` j(RA^T =F i CL@z?¾;n2w>``#3`gTMupjM # 8 踋gz]axlrN{{W]UT.{ 9}*gspf:{ڽOUhtv>AE0v < WWh ;ao9 +p? "QH_y9Pه 3 wX|i-O o?qER]\?s,tvSsA#\oK ϼȅRc3!< uDfC_[F/$]{SijvfnIj K;+  i{9|;2CCfu}ﶛÉ3!5^vP5/FhvN 2'Vh6K0kP$Flޏpk=*r+Gvt^LEv~-Zomv{ ׫Π ?t>m1'NK@: 9;gT-+.[Vpʟo>L-TCCv *ٱ٪牦н{[ԽF T/Lxfga/X{:xM;P`pfX!0hUPQkQ4ƥ)Lu8՟C@3jΕ^}$լkFPDGt{{;s1?̠v }Vi; :ДEX+k#~뒡ұk݅ J3=gXٗg j!nU4Qd|(B0_H0`ncaRH{͏Wy'~m'<e{ nK(q/ <}rϼDEzpk:d2M3m=ACMC&;X ?x{|j% ]#Aho W̴5s4KSJ?>kUXHgar!YQHG48^7x},:ˈ!0qƠ)y ^(8.ljtbMQIƪbk{%[CM+po,j)QP= /_zrtn{K6) ' ۔pq*eJ+M%%T"K6͖ /`/s_8橏ލQ}{vRi-[F\ F-U +VXDjrĀ:}8B]ږܶ)Bo$k8p*N7ZC NPy,߮gv|Ff櫞cKX#MH+^ZQ2V9]d֖fxS0 Bc>Somw">U-P#1h%&%Lh4s/T?heM<8=L%`4iSmM%E]4HO(้>Up[ҵ3uQ?!g|zde ,if\p& @˶sҶTr"iWC^s'񩇟 Z׳~w6̂ ]u1b #&^4TW8PM"A3kWj1ND ֻ]IR}yC 3}\,ؕ)6h +O鸒 <{bfO"ۖ/р >dveϾuvvq:Z~]!䏵sda[, S Ӡ/Ldt7_W4e޵~?}͓Ư ZxyGo8#)^\h㚸cO!@i"Nd<;+9Of&M-b{;6GĬ; 2߳L-l$JjӲ* xf|x3O@lOyz~aH+'K.YhqR#_Z+-~{ g4G`G'+nSs˟Jo+#m}p^D8 G=]W~VRK8*,8 )0%s 4c˼gmɖ׽D#;:xE˳0IQ7*Hd{HE",c"ZG L9K`M Ko WOZHe`O o5( %sJErbQ$U=D~ |H_}w?bO Nvwn_~P0`1Q( 3$dw8 NbMCad*xa'YxfRG ɳ@fO'%6m b@`ɄKKq"Rb (J-F[7\!L= {/8*E5/OӠͱ&jj] fP<]+ zׁw#vGyNFBLB;ag?cEŞ@φ!9<;V^mS\Lށ~y]I<=c "љ(u7mi[=ѥ⇪J4gICջ-CB (%mcWcgw\0W Xo.7=;6ˁ#ر;]:N>3N=/z=w@OL7mSY%GKi?V "=Zv߰kIqyįI(s(w ڪLy 6 g tը+sЬ`SSKu<{:dpϻjwݽ 0B8ywhyfL<+vms b :$L] EZ2+A myI7@.#=m--vA_$8 td6l委-;NDdN,5M'jg_P=|XZC𶽭t{ )!9)aT2|fCP#W ~'- ?xV:~qx@ ɴB_{v&1Þ*\.[w3Uj؅ay.oZcv;;J/},'yhH[w,5M˵Lxwg?bb'IͿo?q.xTu3ƽ;Ҁ\}-1J,"gFF+ ˼ym ~6װow±eci1vwRV| qfS 1E0ԨcSp%"R]q L?r8 m_LDݬk 4Pg6kR* !]J\%50k Y E ̍^8`&6S飞;[?Ӛ+E@Z?ij- SCͨ/=y PX^EFmΖ]JjJ@׼Glw tNDǻS%MrILADC)u]y K?ܲms'Vji',wZT.B&W9b߷.S8po  !X=8:w53Yż[Ou8wzZ=xl@\kp擕S]7x!:^'n~)۝g}?dsk? 8vlI/쓏X8~ǧ>D@jqV]c;#P|eBxdiJߪ&+ 9lV@;@}։ z♩g< b}뭪!ӗ#2I뻲i}pxuO?_o,` 3-z[-$h>'A͌A)ʪɸ RvcJh}K7\>*YhΚ>ERٗ-挳!fY?j5€@.N_)8GV9ɨ`xfj{oB<.7 }vw_^,pe8{gm+P=Uws' -Q>e`@|YoDYux; t `C8쬓x. >PBET`eL@.MX:X#Z''bj!OT0 Ը)x)p0 ,{"=D{;-9/;~3+UV;o9 Ss<]W#Xu 4M5vTL#hM-7]Ӱb:̀Hs+ux/}~k?m0W>8?\s7*lO;:Y0bnpeNw P'pbq5Cxxs՜sy2;&V Qog]OlozkP$f`CD :Ë3us s?(%}5]NQ ҋ+܂ug09?Ss01; sp_:z~=p6V@\W:⋁/ g >jqg^/?9we[J 6v(t<vQcH:)9s Pp)J#bB% l^˕g!fs:t o۾m s Lz!GN¿y[/{@y4E9cRcxKh?B Pn.X۔Z,?hM@ }\ PLI;3l/>ERA Xj psa;B`mcnqv.&Z`{{SjJ'!ѷot.ށ+BL#QO2/$p_=|~д V̟8f'p,9gWH"3ʱz9tlH3` 9|F&&7ν30|{k,7Cpֽ]]N1?yn@wW+Of`%[Kfӊ"PRCocnaa շհ;z@ӘO9vvMݩ 6w"sd Ao:[YLB>q?<?}"utL1vJHg}[_+Nـ(1󋧽  :Z`t\"L L% ir2sDtR $m.ZTgD[a3@$8% W@ 2'@Բk bGz=NCWGzӞ{ a eliIrW8!m_7p-U= 7a.pȭi_ZqJ{okwf/;飼4ǩ`qEuq/ t&AoTT:\r>A()w 8ˆpC-S~D2_W 8E~׳ݝp@ݱi-q5?Z&FOJՄ/2X]B)wtؕ&X;*ǟKft̩yx k*^WWq{o?zh`~E 'TrR{Ɵ)X'bG7 Y8Tq2JaD&=>~@l/=p R* RD UjC(8L8ր+$Q{{^@n >/Zy Eh({ۢp3{Nh Q1S53:ֲKNz4|o|Ix>e0\&# SQ58a (v}0. Pr"~BYa,)(>NF^s* W4dPD4?_S7iZ[~N8zrtXn7¥*~?£ls>L4r '2/CJU]jsm n c#R%7_W5V48ǁh?|Nh$A^d@ɶSiK-4J2JE27&;Ifs~=xvx<͑u;}p{[aH>e&)۶T4,JcBT8,QĹE!"sdT{+ JBD;\om43O/!(mvv5pn8}B>cku{X)iпZ5e1vhyKeB-ј<=9~[` kly/O5pT@S] |Z&| U9:K"m)2vrqRJϏ=?,NByKM;/#Dp ߯#u:r$`yq`_ˁP#ػzA~gIi 9ѪqVYkA3.T-{lpPԓhJ"ظ5{\_}Y{uͲ'h @=+OJ ]TMZ&Nl&u/-(ܸnd֋{=c dfOК| \KzC zi8 *]6_ě"P:T_F\ȧT Qe#4)e?R`O Nr*k}"x.`HB"=ό2xUAJK#` 5:N+JXA[wmGEDϲ>siʤFqG +g!ĭrvjZ]P`V$ HY3]DVo9۽8;1_݅Q9;s^c+HkgToi/ո!L/%ɞ}mnW&%T6G+ 爫/ ì^9045EH3\\ҵo~"sf>eؗv9jxO8\ ‚_+l] uB!o\qٗ=583BWa Vyw+hgUryrqo]{߂NB=VkvՎWM.hcPCsR0nX{Q]9%^OS)ń\8[ l-TQ:5Tُ8IM<`ߌZAoA[LF!MkȨ=j{6. ڵ0KZ -gHU.'mb%tZ1JBX f⢫AAjH U8=i]x2 zn}['֘뵍@E x)c*WvsHŲd ^c$tP@[ z( )`+א>Tx6`cv!55з6ջ?᮷me3IkDz31xU4/>e`tm"(@ ? H?y9|T\D)b^e|zLq,03@#SdUn@*B4 |so.S=3yt E`Co={3&iTz?o>9E@'e˜q 6XIU|*#PrJ _i?Myy CP"W1v-qkZ:^6A6`/f &/@cG_^ R/)\@z۷v-Z MM"~u=$\ %$2ArCH}_8T:JyKW]pⱨ5 p Bm74 VAI5 c\9馽~(JLɍgWc:Ve8@*6qBK`)baZ&"uϨ:]c?$nc?Uq$r9F0utFVP)XN=g7Lh7dUTTtNwཪOvX 2p`cH Se:ϭ`dG:`TbUTG#2LՔ _f rvob2Wp|zT|Y0 ң/@F$ ;UIeХο @Jd_!gsIYdCCHAup g1 xꃓp)IͬY;b ;~-˦^)Da>}vhᝁWa/z ZX$S%.1̔siyBHJډ͕aRc@Qsɠ*>]-UK vvNC. zryrB˿ 䵀.TXT:D39ް&Y^i[K +{β1Tɟ1_H]\^ho!\.#njɉ!. &RTZz}* 5ɕA_ )/ycurLuXFXM顊?'\Q0X++V$Fǟs^\+)٦ ];D [w^|FBѺQq8ԹI>50?3@vPM @ dɑPR\ 0Ʊvc\psncoxT@p s'c\!^iVgXC('uW;#.37$A~ |q^osO\v{j~ɞ}ґUBsc:_ v}wl~=ʬM u $( 7黻r d frFL* }rY-TL0Z"{敀nwvHiS ]@vZ

    zpnsS'ϯl<\3۽::@Ӵײ`qB)(dbPwuL73]r"dBO~ TɇzI0UhW(>e66V)(>n!@_nDEt*YX~p:Om6:iPG=`v(_gk4  =8໺$Oku &|89%ޢ´棹^,r@+Σl0;7G;ʵ9@:_N!.2DQv8PSĖ'%u<tv~?߲O`52էL{}zjkuk=ZZ;8Ȼ9{{[qv^h2# CͰR4Y1)q&-pT$jH2)S(UcBS'%ʚv*xe\(lLWk;.5˄ E ؠ-.&Oe!g'8}h|*Gc>_!R"٤HWL :BAB\m-`}c=re 0l.9 T +!4*0: '&9Syd٘ |zԼD7 df߀"6jkvᶕXS tu4d^+":heG 9q|ٜ- w-hEᚤA;?y$3xuO4 *I,sb.^E*'+g97oH4  aBf, oƚ'`r$NC4zO6.FC-^F8\eg6ܓ,xUy89MO,FbR\$Zxܳ̾%t/[̦ĞrO~)ͮXbo'Lk* z[]ڡX)^[ECSgEÏsk>U{QI5RyRuw`fOҝ=?[*&ٺ<'USisKfЮaᏀJ@'j O[A6!mAK m!&TJ'.wQl['fǿF<>))U{0ж33y!@#j. ǘ{$['Njm(j>a3feEYZDT2M1L=|Hp^p;xO|_\y[k! \XVu4+ `#iWPՆ,\' ms&Klm*%}WVO:.jD&'%Y,`#DwTaGeMo;${(+e zX sFĕ)O7*ovXOO\b1G[W9uTܚ(@&fX!0)se!@k,;U@2EF;,1̀`܍ Eq>6bog ۶MbBPRx7U>w{F1Ygе>Rw/_낡|nkun@ Ze[}=L3d UQ!lX2"N/f$.af޵bxٓ}.+MJ frG`9jixKZ^v3?!nF@yB u$/_ E&@{*f4am.+fz6fwo@nUŊw}PX&cQ U8b:5 BԪIlY@gg3_ۯzI]0}̀/xFЯpf.BwCWG+6mT`Z T~?αPNAuFT2.q4d fP2?*YWRlg]6<̓ŭmmkJzGl'Xpξl (E柭ALNdαٙUy0܆.nÂf=v]l-7+,l8׷۪׶v.-ZէPp)j Eۄ ޑI-0(8ֺGvGnSTCfū¯0TjИ :Eq,D Cl\ _Kx9ZS\5rCe:t"r6`N.T, m/2dP%` @Fyɋ+>? # h"o7hawOHYr&W >8h=>h h&aVh0 0)$cu ε-&}>9oFT;fl 9~O$mzLnmCַ!'fk"Gϼ<\re`,)@oܶ"Ul o >WY_o":0E]OJ1%L6{G9է\!G Pg>>`Ow5}@R'P(ۧ"Fvڎ:ZJuU_SeK0=j<*}x(5b oZ 0WLMn`tEQXqUJ^˼i_+|5lec@TfXCa_rY83um5bءSOsh5ԗ"Ǣ=Tt7> y;\t@u/9N&OWu.ٙI^ ד0=5paͅL&rBA6EUȦ!3|5-uw=끎"4b:Ma:OW^ Z#ym;D0X́ if@@t'2C{voY0H Ouh%^>3SS #P~ $%AMV+Fdf&^خZ?؋虯WW$ؘON<컗H4ʪCf6H,Ň|4 rYy,n7dq>?k>npp| DөgFdj>fg UyVٖf&l\PZ4ǔ*B&\La>+:vW_4&O {2jB@lH3 &C*;}t˶bOqV_6`vzbzH4:|B^w=؊qڱ{W%UbVy]g>: F`&m > o{'? nId' ARbۤ,*r` ^֯|N?on_Q2gIN]a0> 5ko$o}+ # ,}ͬo zM +7,C*b>wo*Tffm{?r͎JeV]݇J$v`Զ9zM_,KK"\t ,&$4&i[2Pϋ@1yFo343 S +PukfR+o>Ⱥb_ eHOO~D 0sgϡ- !D|C[8Pf[-A[Հoi{Mr!ND򔮬]েƿTJ3;{JxݧDQbM.z|w;QVwZ1*|Z O\[Ax#_o^~ڳ8ؗv=#{v1#:%U`H)˞մ,oR6eqM=VXȀ_̌zs vr׹Akk<=k(a ?UǢPXf0#VU MN}GuU^H ^ {) PSm[$R7b@Ke={2**J9=e@gb1t~~{-5#e(|U녾bJجYxgӜ+s'GEܚOaߚ7B[o&'8o#9>J6NEyƊDXt01%vQQ%711mut5#-/Ǫ_:wX3М3u3կ1uԢ"9f,̌G|9̗{݇n ]x)L/d9hl0۶n.a>X}G}K&rOB>݃r!FCgb|@p._UfCO->5c`OW m6y5:(]ʙAkZ"09_ysa?C?u7_ZS؅wa ,;&~+JYpՑ~Ry+ʏO7S8 !, =Zf=?˩;3}BM^\\56 ! A&@i|W}k?_|fҶ0.[zǾ󙇺wOxvEg,-'PR{x%Gfu.Y_@~+wѧF77bhƁ:,\wQgCכ .䋌qT- :;bt d_P}2,5J8EYNDAO-x\Sά?`3W˥US:fם{蓉ƅwtv \sxAP[F3h"cl3̬@&ubapgcY{'+qh |Ksu)/;-[j0*e'Erw P3uD$aDW(̸.\6L?330iwm흕{B﫹cuWMgDzTS$͞c$ƆϼroPF~›ջ/wy;\Gab:mѠCm{=ag8痓}g;3.EA #Ms\s:(2.qwWl X|'g3"oNA6z巖m2L=xN+01xv$,'c2{Z|Wjf&M/Nq~! ,ģ~wߡ++NY$w R~Z.2M+d\-Vf/ +ƫȫ_nbٜ* +Ous/<޾./B|w/ByOeyr6շ~{ZbPA~WíEkc#}GG\I. &hoSUKK[j nq RBMCpލyEߑKo=~هYVPYvrIUvaGgR0+IG T^uy}:q[2/mR- 0?DEQ#U;]&7N2G877IffM}]f|zZ__@ <&,Y~nT~CzS| l:.FmTUj3bIr+(-v3ZNaj\k`sS{omDJVF91׍*HQ6寻*a+$zR5Ը>;u9qo~t{Mڟ;5;G$="wuE(~*hT df*V=56}6>ss6}jzmh@[[{_ēUQQdJș?[>>!9yv%:QD~*Lm=s ;tߐX!2@V(PDSa ~D}ɰg -֨v AȀP[`|+/QUVו^]- X?}{Fk=;5~[{/%r.g|  ~M (Oݥ x'̞K8pS+'&v: J8DbkrtzsfoeS;l Vn2c\ 9"C ̗IOgxy?E>%@iIR|FfNpu pykj/-W//8&l _tm =Pڷ=|9@;e㰀nkV{kϠJ3=K j&@QibЅ7?w=VS[6~Ď8.[ix`d&}M%uB|| n_(Y|$ ƥhshttaBMF8ڼͬ8diJq5~N@Z&Npo_Z^5'ID:4?9j|ET^pMonMѥe?_'6맩\##% ޓ6F1mv:|v|=]wK{k[;t`e`ڤWHZ _sz@ ѳ[+0S_xPw|0 Hq4ɑi8 4M- Q{$.Pc-u‹+ UQë 6 iX&=R6;yaz,TՉ,187Ԋ( ?>Qß} xϞ݋3"7Z(LmCNTV_J,s 3n[@2#ړy4е~>}fjR9=8́Νr&:ٮc}G=W:[SWxFȋ7~Ś'{vT ɡOgS85: {[\7\|hٴ^O]jL80 IiIŧ>=?m w!wV>r#:a_%Bz'5U'fμԻ'BM-߉vu v8~d>θ.S@Vg!*c~{<V > y6jV@g68oQ(Ĥ/5 =z F]5YU'p\x ȟ55.OLmuz1(;GL{\Wƞ1}ZG/U]ֵbfR8ϟm_87M|u&e7HpT_7JGMa[B!NbKEBSsLO1ʎk;v:z;;;c ,%1AM@o0{eOֵd:u'?5:ޱ`_i b<6: G|wǟG3@oj{Sӛ@8m!B͵?ҷ$0;d鳦}>5aRy 䃀̸.6n |չ6z5jcщYX=J|V p w2) eG>w շl}Tw'?ԵSr/7nEFoyfȭb];&ΤYZ3zZkOB ]N6kWvOj}~'ڷ=nH鿺^;v3J"&so "jmx]Ud%&@KuM::F˶z,pUᔋ3[Z^s˥?S 8ƞQ r;dk~(ًp>Wҏ*_qG>l֧z% 3W20̆ZmmUl)WRqW o:總R9 6``_ao=|_[ºWNJܥ1y[O$P~@0E\/`N@G nn=6n0mp͎`_*fs-|j2 ~2\cJ4pJxy0/; 6? UfcNе('AlYtWgiva;Yrt9ޖf^8}s{V(SAM\"S+BrG>_uow!Vd! 9w޾kb "1Ϗo{x,7^\l9)Y*;9&sLcïY!_ԨiRU' =KoPd۳#pscl@wg\lp釪u<Sb}69ǀTs13Sf/YgG2]?ccWgk^A& o- F݈ ښl?Ggg0R0Խ6y o-6W~!3aje[x\~׳ڧU" )}]1 HNQ)lٺa%bn' +;P4ޓVǂ9Гf_Y)3TqKxL)G|c Q^I` )ȳϯl`\},Rp3Z` `Z+~Fݙ6|O7nx !7@90Ÿ^Unx`l+_{(4Avjnl1;H-{s jEY*Z+n[i>rkfWĐCm~ul~-tv85N?gjWᱩy=@ha| 5D.ZE[9_4Foمs".^~dܖDu`>5%~rJ@XU$PO@xn6 س9j Lgbۗ| X;qV1ghx Ap{rYf!_d[Np!\G 8gPͦ@_Bk NNSpCk0 uj GS/sյ:is[3G*Sd~nA_ ?u<'1O?n۾}״pO.)<1ZcО9[xB{G2fxnM^^ )5;.k%EM i=~qvKx}h{>z ߵ{Ȁboii^j@ju7[i7f$.̶:N]H [C.f-϶@]L;?F57uޮE8+?@0$4[ ǯ D/Pθ[[]^4d`_< z~6@p20i{"m_cs>or?2w  a[ÿS;AT{%H߇7%s^0q}jܕUr__~kJvl gʟ XT 1̖[D#9WRx4&/֚cZ=ޮv5k}*7pҢp uVX-h~{s \376k;jt!lZ zn5ƮL;! ' n{?+ | 1Ω/Iyk3:`vz#273%`o-P8;?|ff;p@&P#Nr7aÛ7̖1uy;̶H/!Xl ]e8Oׯk]v=M3s_@^@]@gu`g7k-Mg:kfsTAX`6Xlh)xԗO/$UzA6}{b3o,p lΝqfI1JAH!D>E_tUçe7Q칓C˿zSիhg=!\ v|zMh}/ÿ~6 tCx2ʚO@f /1e ט)fo# (Qo..`j. QS9)(bkL~]asH?O}tɯm*ɽY=lS`1K1Νqf0lu=4FXL8;S *ILP!)FpSt8T? H\8;>~O?&z:lF-nz;Q.@1zv1:ׄ98923I|i4n,&prQbk|b 73=f<U=0Z|QhvNoD-5:ȗ~(V8x9g`:Aa"1y዆EN<*N g۳= &m|!E'Otoƛl/2mlulln0Pc<KQGp"`|.aA>fb)0ض:b=l(Uኙ >zfg?WKۥ-ߨ5y;6G7lDFe;PfnNr>CaL Bqz⛉sgK[߬nLam#M F yM@Aߴkz8 nX` "1OLڨ7/"O/\2 D x_.0cbL䓓K׿7x1kD}np͞ ;5- |@ijn9 Bs#[q7?᝜׹ a 9پ9BlAB[;Lm_`Ϗnnԟ$<얍%p+?qICFوg}}M;Ǵ?ƴfO()QL*/ҫjK,@m&201䀟Og !/qo`''309(&fs#O'Թ%dOa6ͅ}>05|l\4FL`W56#L".EFWJDCLmT-9&to<؂i` p_z.ȍ5LFjnl [5^A=0woX4Sv3faa64pI~ȇwĶ1htD"qKϳ%󈺴IaT@} LENf<;笙x < ->vb IdlirűSxj˾7n @!t mu0k<.Pyf^(cfMDU4LA:zUTPs<?ԧζ6,ʮsg+ͷ1/> %cc)~6qޖo,+^v,FQ Fpdif I/,*ݰD1`TZu!y9aw}\eޒ v8 ޗ͙]OSszbO%cRf4r ƞ[`9i'̐>(i#1046H M :jI s l7%Ky 3s3Ņzjge܂Xx  G8mΝqSgf"8Ub_bF>wtܯEwjѦ}{}1$ 3!jxyI?Z!>+'Zht>??q}~"sly*q|ԯ->VV%+w}8I}{={߯E[oGۮǻvcBZ"KJ_w,_e\ 鄞;]H+$I[N+?`[a$[nc!sM`J v:j ^_8E4sC1%yZ0M#0`k` hH ϖB&9O3cz65TH/^f*fH^|l*;[`cmB%.|GZ_Fښ߷7:b|~&z~"ݪB@`r_ǨAPx&(L)^ȶ)1s)s ܩ|j"co=yA84ذl>^g@(MFyCo#CZwE1ҴPm܄w!թӍbP,sɹO>ܼѓBo[c#)xv&IENDB`qtads-2.1.7/share/icons/hicolor/32x32/000077500000000000000000000000001265017072300172755ustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/32x32/mimetypes/000077500000000000000000000000001265017072300213115ustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/32x32/mimetypes/application-x-t3vm-image.png000066400000000000000000000035001265017072300265340ustar00rootroot00000000000000PNG  IHDR szzgAMA asRGB cHRMz&u0`:pQ< pHYsHHFk>bKGD5IDATXW[lTUνGRJJ*ńP@G iDII|h&6Ĉ-j!mvJߔNt\:gmjb Nc^{[ulppEOش D"g:T*Uvu]u߹sS`墂Wqq,;`i$=|<@B' /e7j----..3dpW:;;`޽{C0h7L!ljj(''>q+f&3m4bO5!N{YSr~o𗗻˥(jܿ|Hc^=z^;àbj>6K.Fа756ZW(4m&_8u fS%f3+0p:;;KoNm8w|b&K?;/RE!dY>tZ}iff2"[PٳUR`_?Vy\i4R|*`$xt %B``!HPtC}B7 w~>r7Pϳw\tRV`G * V\oS{8FޝM$p)MiŅF`Ka2 (# Pr-NnY)|J $/lBX6{%H,;,)@+81-ҺR޼G1yi)k4REŀP' N@rRC (=Ee%ʹ"+4RЀʍ2l ӡQ ˇhkEIl3)SCl 4a<'oqR,+ ЙvK֮ #h@*&6ڶm|Fp|^ 0 z)DÝ)"T凾Twt4k?ڒHWq0.Ȫx [G~ݚ D2൯x\lf9X=uU>W`aNKaw%tEXtdate:create2012-04-13T20:56:30+03:002T%tEXtdate:modify2005-02-07T02:44:02+02:00kIENDB`qtads-2.1.7/share/icons/hicolor/32x32/mimetypes/application-x-tads.png000077700000000000000000000000001265017072300327402application-x-t3vm-image.pngustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/48x48/000077500000000000000000000000001265017072300173135ustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/48x48/apps/000077500000000000000000000000001265017072300202565ustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/48x48/apps/qtads.png000066400000000000000000000116631265017072300221070ustar00rootroot00000000000000PNG  IHDR00WgAMA asRGB cHRMz&u0`:pQ<bKGD pHYsHHFk>IDAThޭy]W}?oZRk$vx Y<.S!E S5$8!Te(j 2Ԑ!)&$@3!ƌd/#[VjKo~Ι?^LTݺ{=os_詉>· DBUPRV Y>Oײ@f}qqaiw~S7ڨyiٿL<^XNs.ѐ9OGئ 3,\2mjd_O<]a.FoG-=rH*pa?4+{~fnWz G6O(ރ?!aff0A#WSۀCJt>rlfuz>x>R23;S[ߵ_܈g? <QJbǎAOva&@^ցrJ(%9s MϢM* 5x# wF %tMDi508JRHJ1`=3÷?z[+c0S@)ɖcLs¯](%m GƱtH6iH% e77q|$2>6zx&n9ȩ겫ˆT@>!&ژ$z(XmvvS\#.KboPҽ ²MsoPJIҩ$$For-u ;R\6K*°RJ)tLz=w~[ENe 鷐^ 5^6^%{װJCHH% B* ]Dz [֟|oo,8?y?j Lq{!_UoLrĭ˙Y !$ϗئ5ݗu Ha_ h5_5~۰c>v #6Nx_qnY2][wns۰q]JZyMVN!_9v sadۨ@6vMW/@bl:7%ϼG5rJv/_x,{cQd6;aN<>sp*%4Ź)'Y;Iq~NN$3N8Gg^vPӲtX~(Md~y,/#.9TLdyv)]k(J.&ـ@)$/M-2c$i6GaM|# dckF}/+h4GqI9(-1t ^>v8#zy4J3Xj6LT7%7SoT0 }0t WQTL6qǽ=` rC[D,0gg,΂i`D"(X OB?Y|ɓ@nG\h"(xZ&*^@~ 3N|z}  ?+Pp4n>+3ӬDɀ[v&ڧƓD dX2};SϯAA =+p 4x/ޕjt-(V47>ϑ7˼gO'$ p1\C0CRC 9@۶՗ik&ָ-D|dv 8[j!5q(u Sߊ)i6s؆6K"͉3gYy0^N/Qz ➝9ptj߱*wbFeyqx~|m7H.ZьNܕI6o*+OF>Ru+}X>|4 ihƳsܕ}-}17طyR}rFZ\n$6#Bp:-KۂS?PuD+YJIq#j&ji  q]FodN¹)`bxd nD3(%M%ܙ!@Ѝh~VVOD /@5hD!% p O ^.NSY:$KsX?Elҙ! 6]G7`դPex%|3KOJV:km_v`8 d>teP%|wߩ[ɵ--'ksQ-u:dsC o!r $SY+J 8j5"a u1=*FYO>kްW& StLXhhD P*/*qrqwp`! GQ3(qkʹQuՑz-v& F 4:Q7­BDҊl,c"6):n[w*۸';8F< ('PAÅi7)橔TʋL e ${ʰ8 wΰA|#cz*daaa)AxzJ_@h[/*^,o]y.rri[)R)v r dsC:B[ +! [ӅW\pɿ)mn$8J}\OhE\,O4 H$jZRqZL@cadCfw+&ߔD@3P+>z5G:\*L[\MnˏW{dl8ۇ\V+KH)rAvztLPdltvE(;IJgf *>Ï {}ЪU/OP)u j[c'u|NץmS̟g0OjҜ<'=>XI4]!f)AHصw$TzU}rHV^`Ll6O  hubKGD IDAThZkh>3+Ve,y9+N])qӐ&+$m!ɏ@i)\BK4Z\SJI"pa-ٖeY%[o[ӻ;3=ݹ㻳wVRa3~|uK>h4;;K6n(Xg< 0%_Jܼy֭['℟57f N,}<.acy?e`@󍹹9J0J1r{,j裎_l)^o<,[DbY#` CuD77;j!~0|ށ r0nݺE0Yw/>Mǚ(`R3GPi],Jʹ_}J`#>}8Ơ "nX 4x|TVVF3[D0]b{tc~~^3M3o# |AW)Wz?¥w^6ϼI'@g:Bf8R[GEȌHt "9x] H8Zxm@U7,b$_PCfqM $Z!וp\:d/$EU@U^;ǞpA  ɟm+Ky0W\:` 9YĀ~~?MBo(` k| P`R,hAqf.ȧ@ BN!se\ǀT@[.3,(:3$kD,OPӨL8z\`: TPi m޼s Na;dJ G]-"|_ @ iXn2T̟.]>fQd_K ~ cE-z-߹J)bmx%*,"'<.]p&ٽ +YN}Lq9 ؁3%zjY fcΝccctR8fc |fƠ}HdQ<Ns'ΞV>3DYW;^ —X% !%iٿS UE2U<* Gk84aȐcK&a?t?x!nudr?ƿaxPm%tEXtdate:create2012-04-13T20:56:30+03:002T%tEXtdate:modify2005-02-07T02:44:02+02:00kIENDB`qtads-2.1.7/share/icons/hicolor/48x48/mimetypes/application-x-tads.png000077700000000000000000000000001265017072300327562application-x-t3vm-image.pngustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/72x72/000077500000000000000000000000001265017072300173055ustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/72x72/apps/000077500000000000000000000000001265017072300202505ustar00rootroot00000000000000qtads-2.1.7/share/icons/hicolor/72x72/apps/qtads.png000066400000000000000000000260731265017072300221020ustar00rootroot00000000000000PNG  IHDRHHUGbKGD pHYsnu>tIME %+IDATxżw%Wy}ΩafzȞ4DX$[6`{b]kydADl(K#iF#MιopQ{FXq>ꪚ:zI%;l=fuzR&JX]%ӿyi_yXș3ʿdVg~ :bAP0ݓKnp `JhFLT$ s@BP*XJEKhR9UբPh1?!z1|SPL$K"ڐ̠^HnU1cR-Ju>@-!i&\"*  &T  D*(`ɏsd\$DnC}0799(ݕ{nzվy(f|2w Dk*Hԃ"f/^<0|}&dm_suw$HA]$cM:Ȉ"ybo}UΙ=[^?g>Ue%/dfO+5ܭDWdvU$RU;ba v@1EEDTUUW\Vi[TLQ#1*5QФ)F8M泮~gb`9_t6E:wHNeT=' ]+S+t4M n%@kh.&xtJD:3,FF}xPà,R,tm)zzzMU A"1  aN@@YZc1VGP@x{)5CP9*_SGZ]K&&mV> t z~U;}lz/E/Jn8/BīHH1PT%,Di&^EEs ]R.tdimI#_@QYR>Ϫ{Uu$T*U  QF=ACW'e?%4OLMgos-{TD@#!jBFO9s;嚕FG ڱ\:QUDTX;_slA PLTī*缳e\k:r[~6c=~#Ho "5_ĨHHAL_ETD}8aY{)똰yzZ7+k"%,Q#A7P((JEFq3 ,Qy'%oƟ{٫~z[_Aq+'vu>V:QC5;\"ZbJswqW?yNas"HnD$W8逓s浳s ) v0tJ IEg;TA=BhsN)ty>Ca?JWy)ǻ)qNq.硇gj~F6̵Vr9@rC4U$(0UmM(#9*m@3pGS GDaD$I]7_]c?Sūϥ{o"9ᝣ^o[׼/~ :G;4\DL0Cψ1g#PCdxxQQ.DbD4 PU=^sӯ{?CeR/${ũ's jAPy>v+^-W}}WSM~ ҊU.izB;e$ngF;1P=P.9?iqO`6U?헇,NN֬:& A$~d !rG;7KcKU!B (F=ZLjs鷏rGV=/%?xKp%dD}OE}o.񩈤d@ Y"v"GZuP,_~$?pTRBv܏݅ftGM}=e>uٓ`%m &(oZo8B*J_*[+l/c U$ީ,E86OzrՉ'l;|{v^3.F%ALn|w(ZZx #Տj˟}jzF XZ $[jbLy׮~s>cGv)>8/}NQÿ9V!%좺7blp% BfS.oJ+C/gtRWOƔTrU1.mGRڬ b"rC|G88pE`m cejEOo|u)س̷\k<8FZ,ZتQ%Y94DVK64Nr=Hi3MRI5NRi5M=IP/uq7=0XhFdYL^TK[9"*FaثAX(^ *Z\5kj&IlNBrY Ӷb%_B@>pTtxޢ)2=.2,dױA/j&_%IG_<(P7l{7?k~<|N-b0kG;k*[99;9/d#^U z6$mvq5)_ ]%ݲe+ħ!\9wT j"At 9@AY=:,qWCKCZӇH 7=5!dKOȉU<|K&ͼ%Ds'\u=ޒ$q};?|Aq89*8wϼC7̳`gctQAŹc ` AVVK.<M3|z$r J$U[طΤ.z Beb.jzTd&&hX݅O={q9U,S^1XTBdL/ vKTwVqqІ\vիyO5ݼmQ,ֆT%xNoH KA,Bȡa6ЌKcuIes,6{5Y}Cޑ"edae}c ߟe#Lj)ҘEJ#)\}&2,-/q mH)"C`,fCY\`zqEWWDdN?/oz7E R7PUdY, IByԎS)1[~Aռ M㜣nTr,M736|ub(FD#xȑ\,]CwPT*]?ƒ3;,+:>3ԛ qN.qGS{7j_ yOWIm]8UЉj2R8g@PI8N gKŔ>8NȌU7 E6 E|'⥣939;,r}(oP _|Cf 8yp]";Fzxy[x[ٻb(QʼnKsf]"LXvmF}rp{%KD3HV#Qaǖ \Fe %t¢@,=& Of }A3S"JEeӪ󐃖WR4)xl?4C5K0Kx-C)Bh9Ͻp;>o6yBP, 8y#0"" T >8{;zsc8?0,({/zPG}&AyzXvWΎv?7r3`QWdՇHa+P)Dݩ:NoA'd}ST6>u+{g2P-qػsM2:֥#RjG-b,bgL=̉29'29~i@06dӖ}sغm7Ãlظ^"+;;!b04q wx Z4u^4 P "KZV􄗞nCy#_7UYxɂ-[^2^NZ[dr0yF@A۵ f&C:9-qҭe߅W˖ d'al P{撰V8 8 *]+tc'jUU| *6]Ȳq.vLoZq>9:.޼!7wșjr]&Z6=M]TTю0 *))YYfăz;8q'h5W4˔*>sxғ، B]_1MkSX}TpnSAAeƋ^}t)b 0hM@e t]C#34Qd3Lw.dFCC{l iy\  kw\',Jk.+]Y[i)m NqR_jո+K'i^{pkW_T-K,V20fB1B-{.v]QlPIK\("&mv'`Hj{6oe-UΠի21qTzzEo# èiO@J:{3fMD5B=K"Z}k+P&N7[/)ri k~Z.-0q򨌏֓'KQ#c5 nDuۙcYeБ5r)Jק5lŸĐ hp)m_ QB*UA%^yՃ,M M:O:iR0,b‚ Y{FmQ%Ǝűpaj%diB>ٺLm{ٰ0,P(.KIXsIGA gaZu91yld,F{Akw`5xs{cEXCdC)b"~4ΑeD= "BDkO|n9qnfOpFFXB&=snf^{"iH&8VmYk4<7R.EH-ƶhC'I*h,:Hs տ(ZQ,ygBK`Bb D q %eq"&BU eM'35~C̜<9/mc HU(b4KȲ Mb$>Ui>ӜBGgjTQ啺8 desjG>PIltBb U+PDZDB1DspũU.Ap-Ƀ,T2i7{6sٴl-t C ݪ>=.Kb',s?'O/jEcLiUֈD{H v{4??:we<+gxH_K`U4!PW\4KGXI{f%KBXse3.گ*.9h/'Y=yNH֐X $ #˴^_% u`B ^,*F]XZ钠=CbՏ|(nz!P.p,sԑe!-W,mi_%,ϕI6Y\6"hV'^ڏOs'!A@eq,MdnnɉcazjEkߎ1y_OO)x7Z J7%`^߶?U!#`yqYyΑe,ũSU%IH[kebQp+5'N٭k1tڈyq8SSc2?7, 8‚QfݼCDvȃGLϜ ֊z-!b7£Czp΋˜,7xv!Q*ƒa$P.8|z.Y&kωkϫV0\jFB׺u}K~8/?M0:P,坧 8RH'=|֤1=ĭvV-LN`|KKZMZ"9܋ݼ-[vP# # N3T&iCzBY%lu cYZm,b'lxMR;\CcD#tǞ:ݎ?K뫄aHPJM[DCM Y*i겔vrZOΞ s#n- f*b>̕@faJmlb~ԁ}֡ wfΝ2T iwCL^ ZU{ moa=,#sY^rxfx[P *&"M&Ə33=AO# %vH)y瑇s)n57Mo>46ǥ{6oXURq$x$((] ;QG5UZO>ݲ65Vk PRhtuuI&ǙfNĚeTtwƍ[Q6na-Z(Ġ׾Ҧzbjh3V٪__Wy6[>| O;}[ݏJTxplswoV%OHd cK1&`qa\eIE o`Ya(TGP4M5sᲄf7 "ƄjK7,^g1PEJG>?fV{'Y羼y_$=[GzIݚ䄘REF)|90!AqcL:( s077#KK t?*arOfppaDqN3bҴ;^3 (c }a^lԋ^4v,/[Jkmq`܌3?=@|s~k}FivP͖"Ta?W/*H %,O}B!}a&6no&^w-YKba z5,HPFݜ6b&1I[s$$@BDTV\g~r^wrk.ma{|_d9[7!Z@c:o&4 A^bMz{:d/e9$M,_-`lKX@XAlN#փC+2d5$m\:@k頴kGY5ɯ Tkڐ^s)[_r/kQ(lVjK6o^ [ʿ ;P bIZsj^TN+kwNBU,ACX@T= "t*&z>%tPZHKx5S1ag@G<f6r;ް07_ӥ'~VFkOGN5) ).U>/@P#,  }Q/A{*@gA ګiz>F8I8zcüLݩذ+ "n}:󞅥TV +1n<7lk?/@ 7} @Go?hoGM q]5UQuZ'SaqD0aq@Qi0 ZV," iMqĵڮaةr-&F-)3"`1FV4N+LV3YZMY\N5uOX%lꝌK?dogq~DϾU릂 8O"? T^!(h.=d|S_c3z;}Ww{~s9|ewLsŇh. qi,Yc+oLX˜<˖:%˔v_J_JX\i1I 8 !]=Wf.`.?MϾ=| 0|&OؠA]g_ZM kZw2)zr-eg'R݅ڊ/l7F1>c 5b v!louo/ T| =֫ 2FTi6ZՆLV3Y&Qb+4Rnp*gVv2GfdaiYC'u~ak#tQ L_|[מ}>{>=\Gg}ZZ#$S/i,ל/g~a9;ejQz'k؍T<ۤIe,) hwX*j=nJ}uYguVz덦fčz=As=;>s˿g~/P]Gr31l3ıN|Fkʼnp1&E-t]j9cgXR(Py -ebeҁ~izG܈%ɜ(`0k,/ե/&G?;~b =ZtKk^G1Z 09Oc_:x{=[ BWF9ilݨ-j˓:4v_N/\)IENDB`qtads-2.1.7/share/man/000077500000000000000000000000001265017072300145155ustar00rootroot00000000000000qtads-2.1.7/share/man/man6/000077500000000000000000000000001265017072300153565ustar00rootroot00000000000000qtads-2.1.7/share/man/man6/qtads.6000066400000000000000000000032661265017072300165700ustar00rootroot00000000000000.\" Process this file with .\" groff -man -Tascii qtads.6 .TH QTADS 6 "April 2010" " " "User Manuals" .SH NAME QTads \- Multimedia interpreter for TADS games .SH SYNOPSIS .B qtads [ .I game-file .B ] .SH DESCRIPTION .B QTads is an interpreter that can run games written in TADS. TADS is a programming language written by Michael J.\ Roberts, designed to implement text-adventure games (Interactive Fiction), similar to those developed by Infocom in 1980-1990, as well as other companies (like Legend Entertainment, Level 9, etc). If you liked games like "Zork", "Adventure", "Trinity", or "Eric the Unready", you'll feel right at home. .PP This version of QTads supports both TADS versions in use today; traditional TADS 2 as well as the new TADS 3 format. It runs on a variety of systems, including MS Windows and every Unix system for which the Qt library is available, including Linux and Mac OSX. .SH OPTIONS With no arguments, QTads will by default ask you for a filename, unless this prompting has been disabled in the configuration dialog. .PP The optional .I filename can be used to start a game immediately. .SH LICENSE QTads is a Free program, dual-licensed under the GNU General Public License (GPL) as well as the HTML TADS Freeware Source Code License. See the file COPYING (included in the package of the program), or the "Help->About" menu for details. .SH AUTHORS This program is written and maintained by Nikos Chantziaras . .SH ACKNOWLEDGEMENTS The reference implementation of the TADS VM was done by Mike Roberts. Several people helped making QTads a better program. See the file CREDITS or the "Help->About" menu for a list of names. .SH "SEE ALSO" .BR frotz (6), .BR inform (1) qtads-2.1.7/share/mime/000077500000000000000000000000001265017072300146715ustar00rootroot00000000000000qtads-2.1.7/share/mime/packages/000077500000000000000000000000001265017072300164475ustar00rootroot00000000000000qtads-2.1.7/share/mime/packages/qtads.xml000066400000000000000000000011521265017072300203040ustar00rootroot00000000000000 TADS 2 Game TADS 3 Executable qtads-2.1.7/src/000077500000000000000000000000001265017072300134275ustar00rootroot00000000000000qtads-2.1.7/src/aboutqtadsdialog.cc000066400000000000000000000036451265017072300172750ustar00rootroot00000000000000#include "aboutqtadsdialog.h" #include "ui_aboutqtadsdialog.h" #include "globals.h" #include "trd.h" #include "vmvsn.h" #include "htmlver.h" AboutQtadsDialog::AboutQtadsDialog(QWidget *parent) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), ui(new Ui::AboutQtadsDialog) { ui->setupUi(this); #ifdef Q_OS_MAC // On Mac OS X, the dialog should not have any buttons. ui->buttonBox->setStandardButtons(QDialogButtonBox::NoButton); #else // Show a "Close" button everywhere else. ui->buttonBox->setStandardButtons(QDialogButtonBox::Close); #endif // Construct a string holding all version info. QString str; str += QString::fromLatin1("

"); str += tr("QTads version:") + QString::fromLatin1("") + QString::fromLatin1(QTADS_VERSION) + QString::fromLatin1("
") + tr("HTML TADS version:") + QString::fromLatin1("\t") + QString::fromLatin1(HTMLTADS_VERSION) + QString::fromLatin1("
") + tr("TADS 2 virtual machine:") + QString::fromLatin1("\t") + QString::fromLatin1(TADS_RUNTIME_VERSION) + QString::fromLatin1("
") + tr("TADS 3 virtual machine:") + QString::fromLatin1("\t") + QString::fromLatin1(T3VM_VSN_STRING) + QString::fromLatin1(" (") + QString::fromLatin1(T3VM_IDENTIFICATION) + QString::fromLatin1(")
") + tr("Qt build version:") + QString::fromLatin1("") + QString::fromLatin1(QT_VERSION_STR) + QString::fromLatin1("
") + tr("Qt runtime version:") + QString::fromLatin1("") + QString::fromLatin1(qVersion()) + QString::fromLatin1("
"); ui->versionInfoLabel->setText(str); } AboutQtadsDialog::~AboutQtadsDialog() { delete ui; } qtads-2.1.7/src/aboutqtadsdialog.h000066400000000000000000000005601265017072300171300ustar00rootroot00000000000000#ifndef ABOUTQTADSDIALOG_H #define ABOUTQTADSDIALOG_H #include #include "config.h" namespace Ui { class AboutQtadsDialog; } class AboutQtadsDialog: public QDialog { Q_OBJECT public: explicit AboutQtadsDialog(QWidget *parent = 0); ~AboutQtadsDialog() override; private: Ui::AboutQtadsDialog *ui; }; #endif // ABOUTQTADSDIALOG_H qtads-2.1.7/src/aboutqtadsdialog.ui000066400000000000000000000203711265017072300173200ustar00rootroot00000000000000 AboutQtadsDialog About QTads 0 About 0 0 <h3><a href="http://qtads.sourceforge.net"><img src=":/qtads-logo.png" /></a> <br>A Multimedia TADS Interpreter</h3> <p> TADS Copyright © 2013 Michael J. Roberts<br> QTads Copyright © 2013 Nikos Chantziaras </p> <p> QTads comes with NO WARRANTY, to the extent permitted by law. You may redistribute copies of QTads under certain terms and conditions. Click on the &ldquo;License&rdquo; tab for more information. </p> Qt::AlignCenter true true Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse Thanks To true (All entries are listed alphabetically by first name.) Thanks fly out to: Adam Thornton Adrien Beau Andrew Plotkin Alan Mead Beni Serfaty C.E.J. Pacian Chris Odhner Christophe Antoine Curt Siffert Daniel Schepler Denis Pokataev Emily Boegheim Eric Eve Eric Forgeot Erik Temple Greg Boettcher Guilherme De Sousa Hugo Landau Jim Aikin Joe Mason Kevin Boone Kodath duMatri Kris Kennaway Krister Fundin Leif Huhn Marc Simpson Ming Hua Nicolas Lécureuil NR Turner Ornithopter Petter Sjölund Poster Rémi Verschelde Rune Berg Samuel Verschelde Stefan Müller tetzank Thom Brown Tomas Blaha Tony Houlbrooke Xenius for the ideas, source code and bug reports. Also thanks to everyone who donated to this project. And of course, Mike Roberts; if there would be no Mike, there would be no Tads! Qt::TextBrowserInteraction Version true 20 Qt::TextBrowserInteraction Contact <p>For suggestions, issues, queries, bug reports, or any form of feedback, please contact:</p> <p>&nbsp;&nbsp;<a href="mailto:Nikos Chantziaras <realnc@gmail.com>">Nikos Chantziaras &lt;realnc@gmail.com&gt;</a></p> <p>For up to date information about QTads, you can visit its <a href="http://qtads.sourceforge.net">homepage</a>.</p> true true Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse Begware <p>QTads is Free and Open Source Software and hence you are free to use, copy and even modify it. It is also &ldquo;Begware&rdquo;, meaning that you can get it at no cost, but we beg you to make a donation if you think the software is worth it.</p> <center><p> <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=2S3Y8SMHHHUQL">Donate through PayPal</a> </p> <p>Thank you for supporting this project!</p></center> true true Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse License <p>You can redistribute and/or modify QTads either under the terms of the <a href="http://qtads.sourceforge.net/LICENSE.TXT">HTML TADS Freeware Source Code License</a>, or under the terms of the <a href="http://qtads.sourceforge.net/COPYING">GNU General Public License</a> as published by the Free Software Foundation (either version 2 of the license, or, at your option, any later version).</p> true true Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse buttonBox accepted() AboutQtadsDialog accept() 222 273 222 138 buttonBox rejected() AboutQtadsDialog reject() 222 273 222 138 qtads-2.1.7/src/confdialog.cc000066400000000000000000000423421265017072300160500ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include #include #include #include #include "confdialog.h" #include "ui_confdialog.h" #include "globals.h" #include "settings.h" #include "sysframe.h" #include "syswingroup.h" ConfDialog::ConfDialog( CHtmlSysWinGroupQt* parent ) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), ui(new Ui::ConfDialog) { ui->setupUi(this); Settings* sett = qFrame->settings(); sett->loadFromDisk(); #ifdef Q_OS_MAC // On the Mac, make the color selection buttons smaller so that they // become square instead of round. QSize macSize(48, 24); ui->mainBgColorButton->setFixedSize(macSize); ui->mainTextColorButton->setFixedSize(macSize); ui->bannerBgColorButton->setFixedSize(macSize); ui->bannerTextColorButton->setFixedSize(macSize); ui->inputColorButton->setFixedSize(macSize); ui->linkUnvisitedColorButton->setFixedSize(macSize); ui->linkHoveringColorButton->setFixedSize(macSize); ui->linkClickedColorButton->setFixedSize(macSize); #endif ui->allowGraphicsCheckBox->setChecked(sett->enableGraphics); ui->allowSoundEffectsCheckBox->setChecked(sett->enableSoundEffects); ui->allowMusicCheckBox->setChecked(sett->enableMusic); ui->allowLinksCheckBox->setChecked(sett->enableLinks); ui->smoothScalingCheckBox->setChecked(sett->useSmoothScaling); ui->mainBgColorButton->setColor(sett->mainBgColor); ui->mainTextColorButton->setColor(sett->mainTextColor); ui->bannerBgColorButton->setColor(sett->bannerBgColor); ui->bannerTextColorButton->setColor(sett->bannerTextColor); ui->inputColorButton->setColor(sett->inputColor); ui->linkUnvisitedColorButton->setColor(sett->unvisitedLinkColor); ui->linkHoveringColorButton->setColor(sett->hoveringLinkColor); ui->linkClickedColorButton->setColor(sett->clickedLinkColor); ui->underlineLinksCheckBox->setChecked(sett->underlineLinks); ui->highlightLinksCheckBox->setChecked(sett->highlightLinks); ui->mainFontSizeSpinBox->setValue(sett->mainFont.pointSize()); ui->fixedFontSizeSpinBox->setValue(sett->fixedFont.pointSize()); ui->serifFontSizeSpinBox->setValue(sett->serifFont.pointSize()); ui->sansFontSizeSpinBox->setValue(sett->sansFont.pointSize()); ui->scriptFontSizeSpinBox->setValue(sett->scriptFont.pointSize()); ui->writerFontSizeSpinBox->setValue(sett->writerFont.pointSize()); if (sett->useMainFontForInput) { ui->inputFontSizeSpinBox->setValue(sett->mainFont.pointSize()); ui->inputFontSizeSpinBox->setEnabled(false); } else { ui->inputFontSizeSpinBox->setValue(sett->inputFont.pointSize()); } ui->mainFontBox->setCurrentFont(sett->mainFont); ui->fixedFontBox->setCurrentFont(sett->fixedFont); ui->serifFontBox->setCurrentFont(sett->serifFont); ui->sansFontBox->setCurrentFont(sett->sansFont); ui->scriptFontBox->setCurrentFont(sett->scriptFont); ui->writerFontBox->setCurrentFont(sett->writerFont); if (sett->useMainFontForInput) { ui->inputFontBox->setCurrentFont(sett->mainFont); ui->inputFontBox->setEnabled(false); } else { ui->inputFontBox->setCurrentFont(sett->inputFont); } ui->useMainFontCheckBox->setChecked(sett->useMainFontForInput); connect(ui->useMainFontCheckBox, SIGNAL(toggled(bool)), ui->inputFontBox, SLOT(setDisabled(bool))); connect(ui->useMainFontCheckBox, SIGNAL(toggled(bool)), ui->inputFontSizeSpinBox, SLOT(setDisabled(bool))); ui->inputFontItalicCheckBox->setChecked(sett->inputFont.italic()); ui->inputFontBoldCheckBox->setChecked(sett->inputFont.bold()); switch (sett->ioSafetyLevelRead) { case 0: ui->safetyRead0RadioButton->setChecked(true); break; case 2: ui->safetyRead2RadioButton->setChecked(true); break; case 4: ui->safetyRead4RadioButton->setChecked(true); break; default: ui->safetyRead2RadioButton->setChecked(true); break; } switch (sett->ioSafetyLevelWrite) { case 0: ui->safetyWrite0RadioButton->setChecked(true); break; case 2: ui->safetyWrite2RadioButton->setChecked(true); break; case 4: ui->safetyWrite4RadioButton->setChecked(true); break; default: ui->safetyWrite2RadioButton->setChecked(true); break; } const QList& aliases = QTextCodec::availableCodecs(); QList codecs; for (int i = 0; i < aliases.size(); ++i) { const QByteArray codecName = QTextCodec::codecForName(aliases.at(i))->name(); // Only allow some of the possible sets, otherwise we would get a big // list with most of the encodings being irrelevant. The only Unicode // encoding we allow is UTF-8, since it's a single-byte character set // and therefore can be used by TADS 2 games (though I'm not aware of // any that actually use UTF-8.) if (codecName == "UTF-8" or codecName.startsWith("windows-") or codecName.startsWith("ISO-") or codecName.startsWith("KOI8-") or codecName.startsWith("IBM") or codecName.startsWith("EUC-") or codecName.startsWith("jisx020") or codecName.startsWith("cp949")) { codecs.append(codecName); } } qSort(codecs); for (int i = 0; i < codecs.size(); ++i) { if (ui->encodingComboBox->findText(QString::fromLatin1(codecs.at(i))) == -1) { ui->encodingComboBox->addItem(QString::fromLatin1(codecs.at(i))); } } ui->encodingComboBox->setCurrentIndex(ui->encodingComboBox->findText(QString::fromLatin1(sett->tads2Encoding))); QString txt(QKeySequence(Qt::CTRL).toString(QKeySequence::NativeText)); if (txt.endsWith(QChar::fromLatin1('+'))) { txt.truncate(txt.length() - 1); } ui->pasteOnDblClkCheckBox->setText(tr("%1 + double-click pastes current word").arg(txt)); ui->pasteOnDblClkCheckBox->setChecked(sett->pasteOnDblClk); ui->softScrollCheckBox->setChecked(sett->softScrolling); ui->askForGameFileCheckBox->setChecked(sett->askForGameFile); ui->confirmRestartCheckBox->setChecked(sett->confirmRestartGame); ui->confirmQuitCheckBox->setChecked(sett->confirmQuitGame); switch (sett->updateFreq) { case Settings::UpdateOnEveryStart: ui->updateOnStartRadioButton->setChecked(true); break; case Settings::UpdateDaily: ui->updateDailyRadioButton->setChecked(true); break; case Settings::UpdateWeekly: ui->updateWeeklyRadioButton->setChecked(true); break; default: ui->updateNeverRadioButton->setChecked(true); break; } #ifdef Q_OS_MAC // On Mac OS X, the dialog should not have any buttons, and settings // changes should apply instantly. this->fMakeInstantApply(); ui->buttonBox->setStandardButtons(QDialogButtonBox::NoButton); #else QDialogButtonBox::ButtonLayout layoutPolicy = QDialogButtonBox::ButtonLayout(ui->buttonBox->style()->styleHint(QStyle::SH_DialogButtonLayout)); if (layoutPolicy == QDialogButtonBox::GnomeLayout) { // On Gnome (and other Gtk-based environments, like XFCE), we follow // Gnome standards. We only provide a "Close" button and settings // changes should apply instantly. this->fMakeInstantApply(); ui->buttonBox->setStandardButtons(QDialogButtonBox::Close); } else { // Assume KDE/MS Windows standards. No instant apply, and use OK/Apply/Cancel // buttons. ui->buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel); connect(ui->buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(fApplySettings())); connect(this, SIGNAL(accepted()), this, SLOT(fApplySettings())); } #endif } ConfDialog::~ConfDialog() { delete ui; } void ConfDialog::changeEvent(QEvent *e) { QDialog::changeEvent(e); switch (e->type()) { case QEvent::LanguageChange: ui->retranslateUi(this); break; default: break; } } void ConfDialog::fMakeInstantApply() { connect(ui->mainFontBox, SIGNAL(currentFontChanged(QFont)), this, SLOT(fApplySettings())); connect(ui->fixedFontBox, SIGNAL(currentFontChanged(QFont)), this, SLOT(fApplySettings())); connect(ui->serifFontBox, SIGNAL(currentFontChanged(QFont)), this, SLOT(fApplySettings())); connect(ui->sansFontBox, SIGNAL(currentFontChanged(QFont)), this, SLOT(fApplySettings())); connect(ui->scriptFontBox, SIGNAL(currentFontChanged(QFont)), this, SLOT(fApplySettings())); connect(ui->writerFontBox, SIGNAL(currentFontChanged(QFont)), this, SLOT(fApplySettings())); connect(ui->inputFontBox, SIGNAL(currentFontChanged(QFont)), this, SLOT(fApplySettings())); connect(ui->useMainFontCheckBox, SIGNAL(toggled(bool)), this, SLOT(fApplySettings())); connect(ui->mainFontSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(fApplySettings())); connect(ui->fixedFontSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(fApplySettings())); connect(ui->serifFontSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(fApplySettings())); connect(ui->sansFontSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(fApplySettings())); connect(ui->scriptFontSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(fApplySettings())); connect(ui->writerFontSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(fApplySettings())); connect(ui->inputFontSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(fApplySettings())); connect(ui->inputFontItalicCheckBox, SIGNAL(toggled(bool)), this, SLOT(fApplySettings())); connect(ui->inputFontBoldCheckBox, SIGNAL(toggled(bool)), this, SLOT(fApplySettings())); connect(ui->underlineLinksCheckBox, SIGNAL(toggled(bool)), this, SLOT(fApplySettings())); connect(ui->highlightLinksCheckBox, SIGNAL(toggled(bool)), this, SLOT(fApplySettings())); connect(ui->allowGraphicsCheckBox, SIGNAL(toggled(bool)), this, SLOT(fApplySettings())); connect(ui->allowSoundEffectsCheckBox, SIGNAL(toggled(bool)), this, SLOT(fApplySettings())); connect(ui->allowMusicCheckBox, SIGNAL(toggled(bool)), this, SLOT(fApplySettings())); connect(ui->allowLinksCheckBox, SIGNAL(toggled(bool)), this, SLOT(fApplySettings())); connect(ui->smoothScalingCheckBox, SIGNAL(toggled(bool)), this, SLOT(fApplySettings())); connect(ui->mainTextColorButton, SIGNAL(changed(QColor)), this, SLOT(fApplySettings())); connect(ui->mainBgColorButton, SIGNAL(changed(QColor)), this, SLOT(fApplySettings())); connect(ui->bannerTextColorButton, SIGNAL(changed(QColor)), this, SLOT(fApplySettings())); connect(ui->bannerBgColorButton, SIGNAL(changed(QColor)), this, SLOT(fApplySettings())); connect(ui->inputColorButton, SIGNAL(changed(QColor)), this, SLOT(fApplySettings())); connect(ui->linkUnvisitedColorButton, SIGNAL(changed(QColor)), this, SLOT(fApplySettings())); connect(ui->linkHoveringColorButton, SIGNAL(changed(QColor)), this, SLOT(fApplySettings())); connect(ui->linkClickedColorButton, SIGNAL(changed(QColor)), this, SLOT(fApplySettings())); // Because these are radio buttons, we connect the clicked() instead of // the toggled() signal. The toggled() signal would result in the slot // getting called twice, once for the button that gets unchecked and once // for the button that gets checked. The clicked() signal is only emitted // when a button gets checked. connect(ui->safetyRead0RadioButton, SIGNAL(clicked()), this, SLOT(fApplySettings())); connect(ui->safetyRead2RadioButton, SIGNAL(clicked()), this, SLOT(fApplySettings())); connect(ui->safetyRead4RadioButton, SIGNAL(clicked()), this, SLOT(fApplySettings())); connect(ui->safetyWrite0RadioButton, SIGNAL(clicked()), this, SLOT(fApplySettings())); connect(ui->safetyWrite2RadioButton, SIGNAL(clicked()), this, SLOT(fApplySettings())); connect(ui->safetyWrite4RadioButton, SIGNAL(clicked()), this, SLOT(fApplySettings())); connect(ui->updateOnStartRadioButton, SIGNAL(clicked()), this, SLOT(fApplySettings())); connect(ui->updateDailyRadioButton, SIGNAL(clicked()), this, SLOT(fApplySettings())); connect(ui->updateWeeklyRadioButton, SIGNAL(clicked()), this, SLOT(fApplySettings())); connect(ui->updateNeverRadioButton, SIGNAL(clicked()), this, SLOT(fApplySettings())); connect(ui->encodingComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(fApplySettings())); connect(ui->pasteOnDblClkCheckBox, SIGNAL(toggled(bool)), this, SLOT(fApplySettings())); connect(ui->softScrollCheckBox, SIGNAL(toggled(bool)), this, SLOT(fApplySettings())); connect(ui->askForGameFileCheckBox, SIGNAL(toggled(bool)), this, SLOT(fApplySettings())); connect(ui->confirmRestartCheckBox, SIGNAL(toggled(bool)), this, SLOT(fApplySettings())); connect(ui->confirmQuitCheckBox, SIGNAL(toggled(bool)), this, SLOT(fApplySettings())); } void ConfDialog::fApplySettings() { if (ui->useMainFontCheckBox->isChecked()) { ui->inputFontBox->setCurrentFont(ui->mainFontBox->currentFont()); ui->inputFontSizeSpinBox->setValue(ui->mainFontSizeSpinBox->value()); } Settings* sett = qFrame->settings(); sett->enableGraphics = ui->allowGraphicsCheckBox->isChecked(); sett->enableSoundEffects = ui->allowSoundEffectsCheckBox->isChecked(); sett->enableMusic = ui->allowMusicCheckBox->isChecked(); sett->enableLinks = ui->allowLinksCheckBox->isChecked(); sett->useSmoothScaling = ui->smoothScalingCheckBox->isChecked(); sett->mainBgColor = ui->mainBgColorButton->color(); sett->mainTextColor = ui->mainTextColorButton->color(); sett->bannerBgColor = ui->bannerBgColorButton->color(); sett->bannerTextColor = ui->bannerTextColorButton->color(); sett->inputColor = ui->inputColorButton->color(); sett->unvisitedLinkColor = ui->linkUnvisitedColorButton->color(); sett->hoveringLinkColor = ui->linkHoveringColorButton->color(); sett->clickedLinkColor = ui->linkClickedColorButton->color(); sett->underlineLinks = ui->underlineLinksCheckBox->isChecked(); sett->highlightLinks = ui->highlightLinksCheckBox->isChecked(); sett->mainFont = ui->mainFontBox->currentFont(); sett->fixedFont = ui->fixedFontBox->currentFont(); sett->serifFont = ui->serifFontBox->currentFont(); sett->sansFont = ui->sansFontBox->currentFont(); sett->scriptFont = ui->scriptFontBox->currentFont(); sett->writerFont = ui->writerFontBox->currentFont(); sett->inputFont = ui->inputFontBox->currentFont(); sett->inputFont.setBold(ui->inputFontBoldCheckBox->isChecked()); sett->inputFont.setItalic(ui->inputFontItalicCheckBox->isChecked()); sett->mainFont.setPointSize(ui->mainFontSizeSpinBox->value()); sett->fixedFont.setPointSize(ui->fixedFontSizeSpinBox->value()); sett->serifFont.setPointSize(ui->serifFontSizeSpinBox->value()); sett->sansFont.setPointSize(ui->sansFontSizeSpinBox->value()); sett->scriptFont.setPointSize(ui->scriptFontSizeSpinBox->value()); sett->writerFont.setPointSize(ui->writerFontSizeSpinBox->value()); sett->inputFont.setPointSize(ui->inputFontSizeSpinBox->value()); sett->useMainFontForInput = ui->useMainFontCheckBox->isChecked(); if (ui->safetyRead0RadioButton->isChecked()) { sett->ioSafetyLevelRead = 0; } else if (ui->safetyRead2RadioButton->isChecked()) { sett->ioSafetyLevelRead = 2; } else if (ui->safetyRead4RadioButton->isChecked()) { sett->ioSafetyLevelRead = 4; } if (ui->safetyWrite0RadioButton->isChecked()) { sett->ioSafetyLevelWrite = 0; } else if (ui->safetyWrite2RadioButton->isChecked()) { sett->ioSafetyLevelWrite = 2; } else if (ui->safetyWrite4RadioButton->isChecked()) { sett->ioSafetyLevelWrite = 4; } sett->tads2Encoding = ui->encodingComboBox->currentText().toLatin1(); sett->pasteOnDblClk = ui->pasteOnDblClkCheckBox->isChecked(); sett->softScrolling = ui->softScrollCheckBox->isChecked(); sett->askForGameFile = ui->askForGameFileCheckBox->isChecked(); sett->confirmRestartGame = ui->confirmRestartCheckBox->isChecked(); sett->confirmQuitGame = ui->confirmQuitCheckBox->isChecked(); if (ui->updateOnStartRadioButton->isChecked()) { sett->updateFreq = Settings::UpdateOnEveryStart; } else if (ui->updateDailyRadioButton->isChecked()) { sett->updateFreq = Settings::UpdateDaily; } else if (ui->updateWeeklyRadioButton->isChecked()) { sett->updateFreq = Settings::UpdateWeekly; } else { sett->updateFreq = Settings::UpdateNever; } // Notify the application that preferences have changed. qFrame->notifyPreferencesChange(sett); sett->saveToDisk(); } qtads-2.1.7/src/confdialog.h000066400000000000000000000025041265017072300157060ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef CONFDIALOG_H #define CONFDIALOG_H #include #include "config.h" namespace Ui { class ConfDialog; } class ConfDialog: public QDialog { Q_OBJECT public: ConfDialog( class CHtmlSysWinGroupQt* parent = 0 ); ~ConfDialog() override; protected: void changeEvent( QEvent* e ) override; private: Ui::ConfDialog* ui; // Makes the dialog's controls apply instantly when they change. void fMakeInstantApply(); private slots: void fApplySettings(); }; #endif qtads-2.1.7/src/confdialog.ui000066400000000000000000000731441265017072300161040ustar00rootroot00000000000000 ConfDialog 0 Fonts QFormLayout::ExpandingFieldsGrow &Main Game Font mainFontBox 0 0 pt 6 72 &Fixed-width Font fixedFontBox 0 0 QFontComboBox::MonospacedFonts pt 6 72 &Serifed Font serifFontBox 0 0 pt 6 72 Sa&ns-Serif Font sansFontBox 0 0 pt 6 72 S&cript Font scriptFontBox 0 0 pt 6 72 &Typewriter Font writerFontBox 0 0 QFontComboBox::MonospacedFonts pt 6 72 Input Font Use Main Game Font for Input 0 0 pt 6 72 &Italic &Bold Colors Central Window Qt::AlignHCenter|Qt::AlignTop &Text mainTextColorButton 0 0 &Background mainBgColorButton 0 0 &Input inputColorButton 0 0 Banner Windows Qt::AlignHCenter|Qt::AlignTop T&ext bannerTextColorButton 0 0 B&ackground bannerBgColorButton 0 0 Hyperlinks &Underline Links &Highlight when hovering Qt::AlignHCenter|Qt::AlignTop U&nvisited linkUnvisitedColorButton 0 0 H&overing linkHoveringColorButton 0 0 &Clicked linkClickedColorButton 0 0 Media You can prevent games from displaying graphics or playing music and sound effects by clearing these checkboxes. If you turn off graphics and/or sounds, multimedia games will still work, but they'll be limited to displaying text. true Allow &Graphics Allow &Sound Effects Allow Background &Music Allow &Hyperlinks Qt::Horizontal Use Smooth Image Scaling File Safety The file safety setting applies only to explicit file operations by the game. It does not affect saving or restoring games or logging a transcript to a file. true Read Access No read access Read from game directory Read from anywhere Write Access No write access Write to game directory Write to anywhere Updates <html><head/><body><p>The online update check will never download or install updates. It will only inform you about available updates. The automatic update check is only performed by the application at startup; no background services of any kind will be installed on your computer.</p><p>Choose how often the application should check for available updates:</p></body></html> true On every application start Once per day Once per week Never Misc The majority of TADS 2 games don't use Unicode to display characters not covered by the ASCII character set. They also don't tell interpreters what encoding they actually use. Here you can select the character set that should be used for TADS 2 games. Note that this setting only takes effect on newly added text; already existing text will not be converted. true TADS 2 Encoding encodingComboBox Qt::Horizontal Paste on double-click Soft Scrolling Ask for a game file at startup Ask for confirmation when restarting a game Ask for confirmation when quitting a game KColorButton QPushButton
kcolorbutton.h
buttonBox accepted() ConfDialog accept() 256 304 157 274 buttonBox rejected() ConfDialog reject() 328 304 286 274
qtads-2.1.7/src/config.h000066400000000000000000000003111265017072300150400ustar00rootroot00000000000000#ifndef CONFIG_H #define CONFIG_H /* We use the 'override' keyword from C++-11. We define it as an empty * macro on older compilers. */ #if __cplusplus < 201103L #define override #endif #endif qtads-2.1.7/src/dispwidget.cc000066400000000000000000000357051265017072300161130ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include #include #include #include #include #include #include "htmlattr.h" #include "htmlfmt.h" #include "htmldisp.h" #include "dispwidget.h" #include "settings.h" #include "syswininput.h" DisplayWidget* DisplayWidget::curSelWidget = 0; DisplayWidget::DisplayWidget( CHtmlSysWinQt* parent, CHtmlFormatter* formatter ) : QWidget(parent), fHoverLink(0), fClickedLink(0), fHasSelection(false), inSelectMode(false), parentSysWin(parent), formatter(formatter) { this->setForegroundRole(QPalette::Text); this->setBackgroundRole(QPalette::Base); // Enable mouse tracking, since we need to change the mouse cursor shape // when hovering over hyperlinks. this->setMouseTracking(true); } DisplayWidget::~DisplayWidget() { // If we currently have an active selection, make sure no one tries to // access it anymore. if (DisplayWidget::curSelWidget == this) { qWinGroup->enableCopyAction(false); DisplayWidget::curSelWidget = 0; } } void DisplayWidget::fInvalidateLinkTracking() { // If we're tracking links (hover/click), forget about them. if (this->fClickedLink != 0) { this->fClickedLink->set_clicked(this->parentSysWin, CHtmlDispLink_none); this->fClickedLink = 0; } if (this->fHoverLink != 0) { this->fHoverLink->set_clicked(this->parentSysWin, CHtmlDispLink_none); this->fHoverLink = 0; } this->unsetCursor(); qWinGroup->statusBar()->setUpdatesEnabled(false); qWinGroup->statusBar()->clearMessage(); qWinGroup->statusBar()->setUpdatesEnabled(true); } QString DisplayWidget::fMySelectedText() { unsigned long startOfs, endOfs; this->formatter->get_sel_range(&startOfs, &endOfs); if (startOfs == endOfs) { // There's nothing selected. return QString(); } // Figure out how much space we need. This is a worst-case size. unsigned long len = this->formatter->get_chars_in_ofs_range(startOfs, endOfs); // Get the text in the internal format. CStringBuf buf(len); this->formatter->extract_text(&buf, startOfs, endOfs); // Return only the useful part of the buffer (which is null-terminated). return QString::fromUtf8(buf.get()); } void DisplayWidget::fHandleDoubleOrTripleClick( QMouseEvent* e, bool tripleClick ) { // Get the offsets of the current word or line, depending on whether this // is a double or triple click. unsigned long start, end, offs; offs = this->formatter->find_textofs_by_pos(CHtmlPoint(e->x(), e->y())); if (tripleClick) { this->formatter->get_line_limits(&start, &end, offs); } else { this->formatter->get_word_limits(&start, &end, offs); } // If this is a Ctrl+double-click, and pasting is enabled in the settings, // paste the word into the current line input. Otherwise, just select the // word. if (qFrame->settings()->pasteOnDblClk and not tripleClick and (QApplication::keyboardModifiers() & Qt::ControlModifier)) { CStringBuf strBuf; this->formatter->extract_text(&strBuf, start, end); QString txt(QString::fromUtf8(strBuf.get()).trimmed()); if (not txt.isEmpty()) { qFrame->gameWindow()->insertText(txt + QChar::fromLatin1(' ')); } } else if (~QApplication::keyboardModifiers() & Qt::ControlModifier) { this->formatter->set_sel_range(start, end); fSyncClipboard(); qWinGroup->enableCopyAction(true); this->inSelectMode = true; this->fHasSelection = true; } } void DisplayWidget::fSyncClipboard() { const QString txt(fMySelectedText()); if (txt.isEmpty() or not QApplication::clipboard()->supportsSelection()) { return; } QApplication::clipboard()->setText(txt, QClipboard::Selection); } void DisplayWidget::paintEvent( QPaintEvent* e ) { //qDebug() << Q_FUNC_INFO << "called"; //qDebug() << "repainting" << e->rect(); const QRect& qRect = e->region().boundingRect(); CHtmlRect cRect(qRect.left(), qRect.top(), qRect.left() + qRect.width(), qRect.top() + qRect.height()); this->formatter->draw(&cRect, false, 0); } void DisplayWidget::mouseMoveEvent( QMouseEvent* e ) { // Avoid registering a triple click directly after the mouse was moved. fLastDoubleClick = QTime(); if (e->buttons() & Qt::LeftButton) { // If we're tracking a selection, update the selection range. if (this->inSelectMode) { this->formatter->set_sel_range(CHtmlPoint(this->fSelectOrigin.x(), this->fSelectOrigin.y()), CHtmlPoint(e->pos().x(), e->pos().y()), 0, 0); fSyncClipboard(); return; } // We're not tracking a selection, but the mouse is inside of one. // If there's enough distance since the start of the drag, start a // drag event containing the selected text. if (this->fHasSelection and (e->pos() - this->fDragStartPos).manhattanLength() > QApplication::startDragDistance()) { QDrag* drag = new QDrag(this); QMimeData* mime = new QMimeData; mime->setText(this->fMySelectedText()); drag->setMimeData(mime); drag->exec(Qt::CopyAction); this->fInvalidateLinkTracking(); return; } } // This wasn't a selection event. Just update link tracking. this->updateLinkTracking(e->pos()); } void DisplayWidget::leaveEvent( QEvent* ) { this->fInvalidateLinkTracking(); } void DisplayWidget::mousePressEvent( QMouseEvent* e ) { if (e->button() != Qt::LeftButton) { e->ignore(); return; } if (this->fHoverLink == 0) { // We're not hover-tracking a link. Start selection mode if we're not // already in that mode. if (this->inSelectMode) { return; } // If there was a double-click previously and not too much time has // passed, then this is a triple-click. if (fLastDoubleClick.isValid() and fLastDoubleClick.msecsTo(QTime::currentTime()) <= QApplication::doubleClickInterval()) { fHandleDoubleOrTripleClick(e, true); return; } // We're not tracking a selection, but if we have a selection and the // mouse was inside it, then prepare for a drag start operation. unsigned long selStart, selEnd; this->formatter->get_sel_range(&selStart, &selEnd); unsigned long mousePos = this->formatter->find_textofs_by_pos(CHtmlPoint(e->pos().x(), e->pos().y())); if (mousePos > selStart and mousePos < selEnd) { this->fDragStartPos = e->pos(); return; } this->inSelectMode = true; this->fSelectOrigin = e->pos(); // Just in case we had a selection previously, clear it. this->clearSelection(); // If another widget also has an active selection, clear it. if (curSelWidget != 0 and curSelWidget != this) { curSelWidget->clearSelection(); } // Remember that we're the widget with an active selection. curSelWidget = this; qWinGroup->enableCopyAction(true); return; } // We're hover-tracking a link. Click-track it if it's clickable. if (this->fHoverLink->is_clickable_link() and qFrame->settings()->enableLinks) { // Draw all of the items involved in the link in the hilited state. this->fHoverLink->set_clicked(this->parentSysWin, CHtmlDispLink_clicked); this->fClickedLink = this->fHoverLink; } } void DisplayWidget::mouseReleaseEvent( QMouseEvent* e ) { #if QT_VERSION < 0x040700 #define MiddleButton MidButton #endif if (e->button() == Qt::MiddleButton and QApplication::clipboard()->supportsSelection()) { qFrame->gameWindow()->insertText(QApplication::clipboard()->text(QClipboard::Selection)); return; } if (e->button() != Qt::LeftButton) { e->ignore(); return; } if (this->inSelectMode) { // Releasing the button ends selection mode. this->inSelectMode = false; // If the selection is empty, there would be nothing to copy. if (this->fMySelectedText().isEmpty()) { qWinGroup->enableCopyAction(false); } else { this->fHasSelection = true; } return; } // We're not tracking a selection but if we do have selected text, clear // it. if (this->fHasSelection) { this->clearSelection(); } if (this->fClickedLink == 0) { // We're not click-tracking a link; there's nothing else to do here. return; } // If we're still hovering over the clicked link, process it. if (this->fClickedLink == this->fHoverLink) { const textchar_t* cmd = this->fClickedLink->href_.get_url(); qFrame->gameWindow()->processCommand(cmd, strlen(cmd), this->fClickedLink->get_append(), not this->fClickedLink->get_noenter(), OS_CMD_NONE); // Put it back in hovering mode. if (qFrame->settings()->highlightLinks) { this->fClickedLink->set_clicked(this->parentSysWin, CHtmlDispLink_hover); } // Otherwise, if we're hovering over another link, put that one in hover mode. } else if (this->fHoverLink != 0) { this->fHoverLink->set_clicked(this->parentSysWin, CHtmlDispLink_hover); } // Stop click-tracking it. this->fClickedLink = 0; } void DisplayWidget::mouseDoubleClickEvent( QMouseEvent* e ) { if (e->button() != Qt::LeftButton) { e->ignore(); return; } // Note the time this occurred, since we need to detect triple-clicks. fLastDoubleClick = QTime::currentTime(); fHandleDoubleOrTripleClick(e, false); } void DisplayWidget::clearSelection() { // Clear the selection range in the formatter by setting both ends // of the range to the maximum text offset in the formatter's // display list. this->fHasSelection = false; unsigned long start = this->formatter->get_text_ofs_max(); this->formatter->set_sel_range(start, start); // If we're the widget with the active selection, also disable the copy // action, since clearing the selection means there's nothing to copy. if (DisplayWidget::curSelWidget == this) { qWinGroup->enableCopyAction(false); } } QString DisplayWidget::selectedText() { if (DisplayWidget::curSelWidget == 0) { // There's no active selection. return QString(); } return DisplayWidget::curSelWidget->fMySelectedText(); } void DisplayWidget::updateLinkTracking( const QPoint& mousePos ) { // Get the display object containing the position. CHtmlPoint docPos; // If specified mouse position is invalid, map it from the current global // position. if (mousePos.isNull()) { const QPoint pos(this->mapFromGlobal(QCursor::pos())); docPos.set(pos.x(), pos.y()); } else { docPos.set(mousePos.x(), mousePos.y()); } CHtmlDisp* disp = this->formatter->find_by_pos(docPos, true); // If there's nothing, no need to continue. if (disp == 0) { // If we were tracking anything, forget about it. if (this->fHoverLink != 0) { this->fHoverLink->set_clicked(this->parentSysWin, CHtmlDispLink_none); this->fHoverLink = 0; this->unsetCursor(); qWinGroup->statusBar()->setUpdatesEnabled(false); qWinGroup->statusBar()->clearMessage(); qWinGroup->statusBar()->setUpdatesEnabled(true); } return; } // It could be a link. if (qFrame->settings()->enableLinks) { CHtmlDispLink* link = disp->get_link(this->formatter, docPos.x, docPos.y); // If we're already tracking a hover over this link, we don't need to // do anything else. if (link == this->fHoverLink) { return; } // If we're tracking a hover and it's a new link, track this one and // forget about the previous one. if (link != this->fHoverLink) { if (this->fHoverLink != 0) { this->fHoverLink->set_clicked(this->parentSysWin, CHtmlDispLink_none); } this->fHoverLink = link; } // If it's a clickable link, also change the mouse cursor shape and // hovering color. if (link != 0 and link->is_clickable_link()) { this->setCursor(Qt::PointingHandCursor); // Only change its color if we're not click-tracking another link. if (qFrame->settings()->highlightLinks and this->fClickedLink == 0) { link->set_clicked(this->parentSysWin, CHtmlDispLink_hover); } } // If we found something that has ALT text, show it in the status bar. if (disp->get_alt_text() != 0 and strlen(disp->get_alt_text()) > 0) { qWinGroup->statusBar()->setUpdatesEnabled(false); qWinGroup->statusBar()->showMessage(QString::fromUtf8(disp->get_alt_text())); qWinGroup->statusBar()->setUpdatesEnabled(true); return; } // It could be a clickable link without any ALT text. In that case, show // its contents. if (link != 0 and link->is_clickable_link()) { qWinGroup->statusBar()->setUpdatesEnabled(false); qWinGroup->statusBar()->showMessage(QString::fromUtf8(link->href_.get_url())); qWinGroup->statusBar()->setUpdatesEnabled(true); return; } } // We don't know what it was. Clear status bar message, reset cursor shape // and forget about any link we were tracking. qWinGroup->statusBar()->setUpdatesEnabled(false); qWinGroup->statusBar()->clearMessage(); qWinGroup->statusBar()->setUpdatesEnabled(true); this->unsetCursor(); if (this->fHoverLink != 0) { this->fHoverLink->set_clicked(this->parentSysWin, CHtmlDispLink_none); this->fHoverLink = 0; } } qtads-2.1.7/src/dispwidget.h000066400000000000000000000071171265017072300157510ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef DISPWIDGET_H #define DISPWIDGET_H #include #include #include #include "config.h" /* The widget where CHtmlSysWin* performs actual paint operations. It also * handles mouse events. */ class DisplayWidget: public QWidget { Q_OBJECT private: // We track the current link the mouse is currently hovering over and the // link over which the mouse button has been pressed but not released yet. class CHtmlDispLink* fHoverLink; class CHtmlDispLink* fClickedLink; // Origin of current selection range. QPoint fSelectOrigin; // Do we currently have a selection? bool fHasSelection; // Drag start position. QPoint fDragStartPos; // Time of last double-click event. QTime fLastDoubleClick; // Stop tracking links. void fInvalidateLinkTracking(); // Returns the text currently selected in this window. QString fMySelectedText(); void fHandleDoubleOrTripleClick( QMouseEvent* e, bool tripleClick ); void fSyncClipboard(); protected: // Are we in text selection mode? bool inSelectMode; // The window we're embeded in. class CHtmlSysWinQt* parentSysWin; // Our parent's formatter, for easy access. class CHtmlFormatter* formatter; // Holds the widget that currently has an active selection range, or null // if there's no active selection. static DisplayWidget* curSelWidget; void paintEvent( QPaintEvent* e ) override; void mouseMoveEvent( QMouseEvent* e ) override; void leaveEvent( QEvent* e ) override; void mousePressEvent( QMouseEvent* e ) override; void mouseReleaseEvent( QMouseEvent* e ) override; void mouseDoubleClickEvent( QMouseEvent* e ); public: DisplayWidget( class CHtmlSysWinQt* parent, class CHtmlFormatter* formatter ); ~DisplayWidget() override; // When our parent's notify_clear_contents() is called, we need to know // about it so we can perform link tracking invalidation. void notifyClearContents() { // When clearing contents, the display items are already gone. Set them // Null so we won't try to access them. this->fClickedLink = this->fHoverLink = 0; this->fInvalidateLinkTracking(); } // Clear the selection range. virtual void clearSelection(); // Get the text contained in the currently active selection. Returns a // null string is there's currently no display widget with an active // selection. static QString selectedText(); // Update link tracking for specified mouse position. If the specified // position isNull(), it will be autodetected. // // TODO: What happens with multi-pointer systems? void updateLinkTracking( const QPoint& pos ); }; #endif qtads-2.1.7/src/dispwidgetinput.cc000066400000000000000000000165071265017072300171720ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include #include #include #include #include "htmlfmt.h" #include "htmltags.h" #include "htmlinp.h" #include "htmldisp.h" #include "settings.h" #include "syswingroup.h" #include "syswin.h" #include "dispwidgetinput.h" DisplayWidgetInput::DisplayWidgetInput(CHtmlSysWinQt* parent, CHtmlFormatter* formatter, CHtmlInputBuf* tadsBuffer ) : DisplayWidget(parent, formatter), fCursorPos(0, 0), fLastCursorPos(0, 0), fCursorVisible(false), fBlinkVisible(false), fBlinkTimer(new QTimer(this)), fInpTag(0), fTadsBuffer(tadsBuffer) { connect(this->fBlinkTimer, SIGNAL(timeout()), this, SLOT(fBlinkCursor())); this->resetCursorBlinking(); // Our initial height is the height of the current input font. this->fHeight = QFontMetrics(qFrame->settings()->inputFont).height(); // We need to check whether the application lost focus. connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)), SLOT(fHandleFocusChange(QWidget*,QWidget*))); } void DisplayWidgetInput::paintEvent( QPaintEvent* e ) { //qDebug() << Q_FUNC_INFO << "called"; DisplayWidget::paintEvent(e); QPainter painter(this); if (this->fCursorVisible and this->fBlinkVisible) { painter.drawLine(this->fCursorPos.x(), this->fCursorPos.y(), this->fCursorPos.x(), this->fCursorPos.y() + this->fHeight); } } void DisplayWidgetInput::mousePressEvent( QMouseEvent* e ) { DisplayWidget::mousePressEvent(e); if (not this->fInpTag or not e->isAccepted()) { return; } // We have a current input tag. If the mouse press was inside it, update // the input caret position. unsigned long offs = formatter->find_textofs_by_pos(CHtmlPoint(e->x(), e->y())); if (offs >= this->fInpTag->get_text_ofs()) { fTadsBuffer->set_caret(offs - this->fInpTag->get_text_ofs()); this->updateCursorPos(formatter, false, true); } } void DisplayWidgetInput::mouseMoveEvent( QMouseEvent* e ) { DisplayWidget::mouseMoveEvent(e); if (not this->inSelectMode or (~e->buttons() & Qt::LeftButton) or not fInpTag or not fTadsBuffer) { return; } // If we have a selection and it extends into the input tag, sync the // selection range with the tag. unsigned long selStart, selEnd; this->formatter->get_sel_range(&selStart, &selEnd); if (selEnd < fInpTag->get_text_ofs()) { // The selection doesn't extend into our input tag. return; } // Figure out where to put the caret. unsigned long offs = formatter->find_textofs_by_pos(CHtmlPoint(e->x(), e->y())); size_t caretPos = 0; if (offs > fInpTag->get_text_ofs()) { caretPos = offs - fInpTag->get_text_ofs(); } // If there's no selection, just update the caret position. Otherwise, // also sync the input tag's selection with the formatter's. if (selStart == selEnd) { fTadsBuffer->set_caret(caretPos); } else { unsigned long inpSelStart; if (selStart > fInpTag->get_text_ofs()) { inpSelStart = selStart - fInpTag->get_text_ofs(); } else { inpSelStart = 0; } unsigned long inpSelEnd = selEnd - fInpTag->get_text_ofs(); fTadsBuffer->set_sel_range(inpSelStart, inpSelEnd, caretPos); } this->updateCursorPos(formatter, true, false); } void DisplayWidgetInput::fBlinkCursor() { this->fBlinkVisible = not this->fBlinkVisible; this->update(this->fCursorPos.x() - 5, this->fCursorPos.y() - 5, this->fCursorPos.x() + 5, this->fCursorPos.y() + this->fHeight + 5); } void DisplayWidgetInput::fHandleFocusChange( QWidget* old, QWidget* now ) { if (now == 0) { // The application window lost focus. Disable cursor blinking. this->fBlinkTimer->stop(); #ifdef Q_OS_MAC // On the Mac, when applications lose focus the cursor must be disabled. if (this->fBlinkVisible) { this->fBlinkCursor(); } #else // On all other systems we assume the cursor must stay visible. if (not this->fBlinkVisible) { this->fBlinkCursor(); } #endif } else if (old == 0 and now != 0) { // The application window gained focus. Reset cursor blinking. this->resetCursorBlinking(); } } void DisplayWidgetInput::updateCursorPos( CHtmlFormatter* formatter , bool keepSelection, bool updateFormatterSelection ) { // Ignore the call if there's currently no active tag. if (this->fInpTag == 0) { return; } // Reset the blink timer. if (this->fBlinkTimer->isActive()) { this->fBlinkTimer->start(); } // Blink-out first to ensure the cursor won't stay visible at the previous // position after we move it. if (this->fBlinkVisible) { this->fBlinkCursor(); } const CHtmlPoint& cursorPos = formatter->get_text_pos(this->fInpTag->get_text_ofs() + this->fTadsBuffer->get_caret()); this->fCursorPos = QPoint(cursorPos.x, cursorPos.y); // If there's another window with an active selection, moving the cursor // must clear it, unless we've been explicitly told otherwise. if (not keepSelection and DisplayWidget::curSelWidget and DisplayWidget::curSelWidget != this) { DisplayWidget::curSelWidget->clearSelection(); } // Update the selection range in the formatter. If there's actually any // text selected, then mark us as the active selection widget and enable // the "Copy" action. if (updateFormatterSelection) { size_t start, end, caret; unsigned long inp_txt_ofs = this->fInpTag->get_text_ofs(); this->fTadsBuffer->get_sel_range(&start, &end, &caret); if (start != end) { formatter->set_sel_range(start + inp_txt_ofs, end + inp_txt_ofs); DisplayWidget::curSelWidget = this; qWinGroup->enableCopyAction(true); } } // Blink-in. if (not this->fBlinkVisible) { this->fBlinkCursor(); } } void DisplayWidgetInput::resetCursorBlinking() { // Start the timer unless cursor blinking is disabled. if (QApplication::cursorFlashTime() > 1) { this->fBlinkTimer->start(QApplication::cursorFlashTime() / 2); } } void DisplayWidgetInput::clearSelection() { DisplayWidget::clearSelection(); if (this->fTadsBuffer->has_sel_range()) { this->fTadsBuffer->set_sel_range(0, 0, this->fTadsBuffer->get_caret()); } } qtads-2.1.7/src/dispwidgetinput.h000066400000000000000000000060511265017072300170250ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef DISPWIDGETINPUT_H #define DISPWIDGETINPUT_H #include "dispwidget.h" #include "config.h" class DisplayWidgetInput: public DisplayWidget { Q_OBJECT private: // Position of the text cursor. QPoint fCursorPos; // Height of the text cursor in pixels. unsigned fHeight; // Last position of the text cursor. QPoint fLastCursorPos; // Is the text cursor visible? bool fCursorVisible; // Text cursor blink visibility. Changed by a timer to show/hide the // cursor at specific intervals if fCursorVisible is true. bool fBlinkVisible; // Text cursor blink timer. class QTimer* fBlinkTimer; // Current input tag, if there is one (null if there isn't.) class CHtmlTagTextInput* fInpTag; // The TADS input buffer we're working with. class CHtmlInputBuf* fTadsBuffer; private slots: // Called by the timer to blink the text cursor. void fBlinkCursor(); // We need to know when the application loses focus entirely so that we // can disable keyboard cursor blinking when we lose focus. void fHandleFocusChange( QWidget* old, QWidget* now ); protected: void paintEvent( QPaintEvent* e ) override; void mousePressEvent( QMouseEvent* e ) override; void mouseMoveEvent( QMouseEvent* e ) override; public: DisplayWidgetInput( class CHtmlSysWinQt* parent, class CHtmlFormatter* formatter, CHtmlInputBuf* tadsBuffer ); // Set the height of the text cursor in pixels. void setCursorHeight( unsigned height ) { this->fHeight = height; } // Show/hide the text cursor. void setCursorVisible( bool visible ) { this->fCursorVisible = visible; } bool isCursorVisible() { return this->fCursorVisible; } void updateCursorPos(class CHtmlFormatter* formatter, bool keepSelection, bool updateFormatterSelection ); // Reset cursor blink timer. This will read the blinking rate from the // desktop environment and ajust the blink timer as needed. void resetCursorBlinking(); // Set the current input tag. void setInputTag( CHtmlTagTextInput* inputTag ) { this->fInpTag = inputTag; } void clearSelection() override; }; #endif qtads-2.1.7/src/gameinfodialog.cc000066400000000000000000000231311265017072300167030ustar00rootroot00000000000000#include "gameinfodialog.h" #include "ui_gameinfodialog.h" #include #include #include #include "globals.h" #include "settings.h" #include "sysframe.h" #include "syswininput.h" #include "htmlrf.h" #include "htmlfmt.h" void QTadsGameInfoEnum::tads_enum_game_info( const char* name, const char* val ) { const QString& valStr = QString::fromUtf8(val); const QString& nameStr = QString::fromUtf8(name).toLower(); const QString& htmlValStr = #if QT_VERSION < 0x050000 Qt::escape(valStr); #else valStr.toHtmlEscaped(); #endif if (nameStr == QString::fromLatin1("name")) { this->gameName = QString::fromLatin1("
") + htmlValStr + QString::fromLatin1("

"); this->plainGameName = valStr; } else if (nameStr == QString::fromLatin1("headline")) { this->headline = QString::fromLatin1("

") + htmlValStr + QString::fromLatin1("

"); } else if (nameStr == QString::fromLatin1("byline")) { this->byLine = QString::fromLatin1("

") + htmlValStr + QString::fromLatin1("

"); } else if (nameStr == QString::fromLatin1("htmlbyline")) { this->htmlByLine = QString::fromLatin1("

") + valStr + QString::fromLatin1("

"); } else if (nameStr == QString::fromLatin1("authoremail")) { this->email = valStr; } else if (nameStr == QString::fromLatin1("desc")) { this->desc = htmlValStr; this->desc.replace(QString::fromLatin1("\\n"), QString::fromLatin1("

")); } else if (nameStr == QString::fromLatin1("htmldesc")) { this->htmlDesc = valStr; } else if (nameStr == QString::fromLatin1("version")) { this->version = valStr; } else if (nameStr == QString::fromLatin1("firstpublished")) { this->published = valStr; } else if (nameStr == QString::fromLatin1("releasedate")) { this->date = valStr; } else if (nameStr == QString::fromLatin1("language")) { this->lang = valStr; } else if (nameStr == QString::fromLatin1("series")) { this->series = valStr; } else if (nameStr == QString::fromLatin1("seriesnumber")) { this->seriesNumber = valStr; } else if (nameStr == QString::fromLatin1("genre")) { this->genre = valStr; } else if (nameStr == QString::fromLatin1("forgiveness")) { this->forgiveness = valStr; } else if (nameStr == QString::fromLatin1("licensetype")) { this->license = valStr; } else if (nameStr == QString::fromLatin1("copyingrules")) { this->copyRules = valStr; } else if (nameStr == QString::fromLatin1("ifid")) { this->ifid = valStr; } } static void insertTableRow( QTableWidget* table, const QString& text1, const QString& text2 ) { table->insertRow(table->rowCount()); QTableWidgetItem* item = new QTableWidgetItem(text1); item->setFlags(Qt::ItemIsEnabled); table->setItem(table->rowCount() - 1, 0, item); item = new QTableWidgetItem(text2); item->setFlags(Qt::ItemIsEnabled); table->setItem(table->rowCount() - 1, 1, item); } static QImage loadCoverArtImage() { CHtmlResFinder* resFinder = qFrame->gameWindow()->get_formatter()->get_res_finder(); // Look for a cover art resource. We try four different resource names. // The first two (PNG and JPG with a ".system/" prefix) are defined in the // current cover art standard. The other two were defined in the older // standard which did not use a prefix. QByteArray coverArtResName; bool coverArtFound = false; const char coverArtPaths[][21] = {".system/CoverArt.png", ".system/CoverArt.jpg", "CoverArt.png", "CoverArt.jpg"}; for (int i = 0; i < 4 and not coverArtFound; ++i) { coverArtResName = coverArtPaths[i]; if (resFinder->resfile_exists(coverArtResName.constData(), coverArtResName.length())) { coverArtFound = true; } } if (not coverArtFound) { return QImage(); } CStringBuf strBuf; unsigned long offset; unsigned long size; resFinder->get_file_info(&strBuf, coverArtResName.constData(), coverArtResName.length(), &offset, &size); // Check if the file exists and is readable. QFileInfo inf(fnameToQStr(strBuf.get())); if (not inf.exists() or not inf.isReadable()) { qWarning() << "ERROR:" << inf.filePath() << "doesn't exist or is unreadable"; return QImage(); } // Open the file and seek to the specified position. QFile file(inf.filePath()); if (not file.open(QIODevice::ReadOnly)) { qWarning() << "ERROR: Can't open file" << inf.filePath(); return QImage(); } if (not file.seek(offset)) { qWarning() << "ERROR: Can't seek in file" << inf.filePath(); file.close(); return QImage(); } // Load the image data. const QByteArray& data(file.read(size)); file.close(); if (data.isEmpty() or static_cast(data.size()) < size) { qWarning() << "ERROR: Could not read" << size << "bytes from file" << inf.filePath(); return QImage(); } QImage image; if (not image.loadFromData(data)) { qWarning() << "ERROR: Could not parse image data"; return QImage(); } // If we got here, all went well. Return the image scaled to a // 200 pixels width if it's too large. Otherwise, return it as-is. if (image.width() > 200) { Qt::TransformationMode mode = qFrame->settings()->useSmoothScaling ? Qt::SmoothTransformation : Qt::FastTransformation; return image.scaledToWidth(200, mode); } else { return image; } } GameInfoDialog::GameInfoDialog( const QByteArray& fname, QWidget* parent ) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), ui(new Ui::GameInfoDialog) { ui->setupUi(this); CTadsGameInfo info; QTadsGameInfoEnum cb; // Try to load the cover art. If there is one, insert it into the text // browser as the "CoverArt" resource. const QImage& image = loadCoverArtImage(); if (not image.isNull()) { ui->description->document()->addResource(QTextDocument::ImageResource, QUrl(QString::fromLatin1("CoverArt")), image); this->resize(this->width(), this->height() + image.height()); } info.read_from_file(fname.constData()); info.enum_values(&cb); // Fill out the description. QString tmp; if (not image.isNull()) { tmp += QString::fromLatin1("

"); } tmp += cb.gameName + cb.headline + (cb.htmlByLine.isEmpty() ? cb.byLine : cb.htmlByLine) + (cb.htmlDesc.isEmpty() ? cb.desc : cb.htmlDesc); ui->description->setHtml(tmp); // Fill out the table. ui->table->setColumnCount(2); if (not cb.genre.isEmpty()) { insertTableRow(ui->table, tr("Genre"), cb.genre); } if (not cb.version.isEmpty()) { insertTableRow(ui->table, tr("Version"), cb.version); } if (not cb.forgiveness.isEmpty()) { insertTableRow(ui->table, tr("Forgiveness"), cb.forgiveness); } if (not cb.series.isEmpty()) { insertTableRow(ui->table, tr("Series"), cb.series); } if (not cb.seriesNumber.isEmpty()) { insertTableRow(ui->table, tr("Series Number"), cb.seriesNumber); } if (not cb.date.isEmpty()) { insertTableRow(ui->table, tr("Date"), cb.date); } if (not cb.published.isEmpty()) { insertTableRow(ui->table, tr("First Published"), cb.published); } if (not cb.email.isEmpty()) { insertTableRow(ui->table, tr("Author email"), cb.email); } if (not cb.lang.isEmpty()) { insertTableRow(ui->table, tr("Language"), cb.lang); } if (not cb.license.isEmpty()) { insertTableRow(ui->table, tr("License Type"), cb.license); } if (not cb.copyRules.isEmpty()) { insertTableRow(ui->table, tr("Copying Rules"), cb.copyRules); } if (not cb.ifid.isEmpty()) { insertTableRow(ui->table, tr("IFID"), cb.ifid); } ui->table->resizeColumnsToContents(); ui->table->resizeRowsToContents(); ui->table->setShowGrid(false); // There's no point in having the tableview widget be any higher than the // sum of all rows. This will also make sure that the widget is not shown // at all in case we have zero rows. int topMargin; int bottomMargin; ui->table->getContentsMargins(0, &topMargin, 0, &bottomMargin); int maxHeight = ui->table->rowCount() * ui->table->rowHeight(0); if (maxHeight > 0) { // Only add the margins to the maximum height if we're going to show // the table at all. maxHeight += topMargin + bottomMargin; if (maxHeight < ui->table->minimumSizeHint().height()) { // Do not make it smaller than the minimum size hint, otherwise we'll // have a messed-up scrollbar. maxHeight = ui->table->minimumSizeHint().height(); } } ui->table->setMaximumHeight(maxHeight); } GameInfoDialog::~GameInfoDialog() { delete ui; } bool GameInfoDialog::gameHasMetaInfo( const QByteArray& fname ) { CTadsGameInfo info; return info.read_from_file(fname.constData()); } QTadsGameInfoEnum GameInfoDialog::getMetaInfo( const QByteArray& fname ) { CTadsGameInfo info; QTadsGameInfoEnum cb; info.read_from_file(fname.constData()); info.enum_values(&cb); return cb; } qtads-2.1.7/src/gameinfodialog.h000066400000000000000000000024551265017072300165530ustar00rootroot00000000000000#ifndef GAMEINFODIALOG_H #define GAMEINFODIALOG_H #include #include "gameinfo.h" #include "config.h" /* Implementation of the game information enumerator callback interface. * See tads3/gameinfo.h for details. */ class QTadsGameInfoEnum: public CTadsGameInfo_enum { public: QString gameName; QString plainGameName; // Game name but without any HTML markup. QString headline; QString byLine; QString htmlByLine; QString email; QString desc; QString htmlDesc; QString version; QString published; QString date; QString lang; QString series; QString seriesNumber; QString genre; QString forgiveness; QString license; QString copyRules; QString ifid; void tads_enum_game_info( const char* name, const char* val ) override; }; namespace Ui { class GameInfoDialog; } class GameInfoDialog: public QDialog { Q_OBJECT public: explicit GameInfoDialog( const QByteArray& fname, QWidget* parent = 0 ); ~GameInfoDialog() override; // Checks whether a game file contains any embedded meta information. static bool gameHasMetaInfo( const QByteArray& fname ); static QTadsGameInfoEnum getMetaInfo( const QByteArray& fname ); private: Ui::GameInfoDialog *ui; }; #endif // GAMEINFODIALOG_H qtads-2.1.7/src/gameinfodialog.ui000066400000000000000000000075071265017072300167440ustar00rootroot00000000000000 GameInfoDialog 0 0 500 395 Qt::ScrollBarAlwaysOn <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'DejaVu Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <table border="0" style="-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;"> <tr> <td style="border: none;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></td></tr></table></body></html> true Qt::ScrollBarAlwaysOn false false QAbstractItemView::ScrollPerPixel QAbstractItemView::ScrollPerPixel false false QDialogButtonBox::Close buttonBox accepted() GameInfoDialog accept() 248 254 157 274 buttonBox rejected() GameInfoDialog reject() 316 260 286 274 qtads-2.1.7/src/globals.cc000066400000000000000000000001141265017072300153550ustar00rootroot00000000000000class CHtmlSysFrameQt* qFrame = 0; class CHtmlSysWinGroupQt* qWinGroup = 0; qtads-2.1.7/src/globals.h000066400000000000000000000015621265017072300152270ustar00rootroot00000000000000#ifndef GLOBALS_H #define GLOBALS_H /* QTads version string. */ #define QTADS_VERSION "2.1.7" /* MS Windows executable resource versions. */ #define W32_RC_FILEVERSION 2,1,7,00 #define W32_RC_PRODUCTVERSION 2,1,7,00 #ifdef __cplusplus /* Works like qApp, but contains the global CHtmlSysFrameQt object instead. If * this variable is 0, it means that no such object has been created yet. * * qApp and qFrame actually both point to the same object (the global * QApplication instance), but qFrame is provided simply to avoid casting the * global qApp object into a CHtmlSysFrameQt when we need to use it as such. */ extern class CHtmlSysFrameQt* qFrame; /* The global CHtmlSysWinGroupQt object. 0 if none exists. Like qApp/qFrame, * this is a singleton object and it's handy to have a global pointer to it. */ extern class CHtmlSysWinGroupQt* qWinGroup; #endif #endif qtads-2.1.7/src/h_qt.h000066400000000000000000000021431265017072300145330ustar00rootroot00000000000000#ifndef H_QT_H #define H_QT_H #ifdef __cplusplus } #endif #include #ifdef __cplusplus extern "C" { #endif #if Q_BYTE_ORDER == Q_BIG_ENDIAN # include "h_ppc.h" #elif QT_POINTER_SIZE == 4 # include "h_ix86.h" #else /* round a size to worst-case alignment boundary */ #define osrndsz(s) (((s)+3) & ~3) /* round a pointer to worst-case alignment boundary */ #define osrndpt(p) ((uchar *)((((ulong)(p)) + 3) & ~3)) /* read unaligned portable unsigned 2-byte value, returning int */ #define osrp2(p) ((int)*(quint16 *)(p)) /* read unaligned portable signed 2-byte value, returning int */ #define osrp2s(p) ((int)*(qint16 *)(p)) /* write int to unaligned portable 2-byte value */ #define oswp2(p, i) (*(quint16 *)(p)=(quint16)(i)) #define oswp2s(p, i) oswp2(p, i) /* read unaligned portable 4-byte value, returning long */ #define osrp4(p) (*(quint32 *)(p)) /* read unaligned portable 4-byte value, returning signed long */ #define osrp4s(p) (*(qint32 *)(p)) /* write long to unaligned portable 4-byte value */ #define oswp4(p, l) (*(qint32 *)(p)=(l)) #define oswp4s(p, l) oswp4(p, l) #endif #endif qtads-2.1.7/src/hos_qt.cc000066400000000000000000000056071265017072300152430ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include "hos_qt.h" #include #include "vmuni.h" #ifdef TADSHTML_DEBUG void os_dbg_sys_msg( const textchar_t* msg ) { qDebug() << msg; } #endif oshtml_charset_id_t os_get_default_charset() { // We always assume UTF-8, so the value we return here doesn't actually // represent anything. return 0; } /* Get the next character in a string. */ textchar_t* os_next_char( oshtml_charset_id_t /*id*/, const textchar_t* p, size_t len ) { if (p == 0) { return 0; } // We always use UTF-8, so we only need to figure out how many bytes to // skip by looking at the first byte. We don't need to actually iterate // over them. if ((0x80 & *p) == 0x00) { p += 1; } else if ((0xE0 & *p) == 0xC0) { p += 2; } else if ((0xF0 & *p) == 0xE0) { p += 3; } else if ((0xF8 & *p) == 0xF0) { p += 4; } else { qWarning() << Q_FUNC_INFO << "corrupt UTF-8 sequence"; p += 1; } return const_cast(p); } /* Get the previous character in a string. */ textchar_t* os_prev_char( oshtml_charset_id_t /*id*/, const textchar_t* p, const textchar_t* pstart ) { if (p == 0) { return 0; } // Move back one byte. --p; // Skip continuation bytes. Make sure we don't go past the beginning. // We need to iterate over every byte since it's not possible to determine // the length of the character without actually looking at the first byte. while (p != pstart and (*p & 0xC0) == 0x80) { --p; } return const_cast(p); } /* Determine if the character at the given string position is a word * character - i.e., a character that's part of a written word. */ int os_is_word_char( oshtml_charset_id_t id, const textchar_t* p, size_t len ) { if (p == 0) { return false; } const QString& c = QString::fromUtf8(p, os_next_char(id, p, len) - p); if (c.isEmpty()) { return false; } return t3_is_alpha(c.at(0).unicode()); } /* Get the current system time. */ os_timer_t os_get_time() { return os_get_sys_clock_ms(); } qtads-2.1.7/src/hos_qt.h000066400000000000000000000210661265017072300151020ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ /* This file should be included ONLY by html_os.h, which serves as the * switchboard for OS header inclusion. Do not include this file directly from * from any other file. */ #ifndef TADSHTML_H #include "tadshtml.h" #endif #ifndef HOS_QT_H #define HOS_QT_H /* ------------------------------------------------------------------------ */ /* * System debugging console messages */ /* * Write a debug message to the system console. This is only used when * TADSHTML_DEBUG is defined, and even then is only used to display * internal debugging messages. The system code can provide an empty * implementation, if desired, and should not even need to include a * definition when TADSHTML_DEBUG isn't defined when the system is * compiled. */ #ifdef TADSHTML_DEBUG void os_dbg_sys_msg( const textchar_t* msg ); #endif /* ------------------------------------------------------------------------ */ /* * Character set identifier type. The font descriptor uses this type to * contain a description of the character set to use in selecting a * font. On systems that use code pages or a similar mechanism, this * should contain an identifier for the desired code page. * * On Windows, we use the xxx_CHARSET identifiers to select font code * pages. Other systems may use a font ID or font family ID, code page * number, script ID, or other system-specific type; the correct type to * use depends on how the system chooses the character set to display * when drawing text. */ // Qt always uses Unicode, so this is pretty much a dummy definition. typedef unsigned int oshtml_charset_id_t; /* get the system default character set */ oshtml_charset_id_t os_get_default_charset(); /* ------------------------------------------------------------------------ */ /* * Advance a character string pointer to the next character in the string. * The character string is in the character set identified by 'id'. * * For systems that use plain ASCII or other single-byte character sets, * this can simply always return (p+1). * * For systems that use or can use multi-byte character sets, this must * advance 'p' by the number of bytes taken up by the character to which * 'p' points. On some systems, single-byte and double-byte characters * can be mixed within the same character set; in such cases, this * function must look at the actual character that 'p' points to and * figure out how big that specific character is. * * Note that this function takes a 'const' pointer and returns a non-const * pointer to the same string, so the pointer passed is stripped of its * const-ness. This isn't great, but (short of templates) is the only * convenient way to allow both const and non-const uses of this function. * Callers should take care not to misuse the const removal. (It's not * like this is a security breach or anything; the caller can always use * an ordinary type-cast if they really want to remove the const-ness.) */ textchar_t* os_next_char( oshtml_charset_id_t /*id*/, const textchar_t* p, size_t /*len*/ ); /* * Decrement a character string pointer to point to the previous character * in the string. The character set is identified by 'id'. 'p' is the * current character pointer, and 'pstart' is a pointer to the start of the * string. * * The return value should be a pointer to the character before the * character 'p' points to, but never before the start of the string. Note * that 'p' might actually point just past the end of the string, so no * assumptions should be made about what 'p' currently points to. However, * it *is* safe to assume that 'p' points to a valid character *boundary* - * that is, 'p' must never be at a byte position in the middle of a * multi-byte character but should always be at a byte that starts a * character OR at the byte just after the end of the string... */ textchar_t* os_prev_char( oshtml_charset_id_t /*id*/, const textchar_t* p, const textchar_t* pstart ); /* * Determine if the character at the given string position is a word * character - i.e., a character that's part of a written word. The exact * set of included characters is up to the platform, since this is for UI * purposes, for things like selecting a word at a time in displayed text. * Word characters usually include alphabetic characters, digits, and * hyphens. * * 'p' is a pointer to the character of interest (if it's a multi-byte * character, 'p' points to the first byte of the character), and 'len' is * the number of bytes after 'p' in the overall string (which might contain * additional characters following the character of interest, so 'len' * isn't necessary the number of bytes in the single character at 'p'). */ int os_is_word_char( oshtml_charset_id_t id, const textchar_t *p, size_t len ); /* ------------------------------------------------------------------------ */ /* * System timer. The system timer is used to measure short time * differences. The timer should provide resolution as high as * possible; in practice, we'll use it to measure intervals of about a * second, so a precision of about a tenth of a second is adequate. * Since we only use the timer to measure intervals, the timer's zero * point is unimportant (i.e., it doesn't need to relate to any * particular time zone). */ /* system timer datatype */ typedef long os_timer_t; /* get the current system time value */ os_timer_t os_get_time(); /* * Get the number of timer units per second. For Windows and Unix, the * timer indicates milliseconds, so there are 1000 units per second. */ // Qt emulates a resolution of 1 ms even on systems with a lower resolution. #define OS_TIMER_UNITS_PER_SECOND 1000 /* ------------------------------------------------------------------------ */ /* * Huge memory block manipulation - for use with blocks of memory over * 64k on 16-bit machines. For 32-bit (and larger) architectures, huge * blocks are the same as normal blocks, so these macros have trivial * implementations. */ /* * Huge pointer type. For a given datatype, this should construct an * appropriate type declaration for a huge pointer to that datatype. On * win32 and Unix, this just returns a plain pointer to the type. */ #define OS_HUGEPTR(type) type * /* * Allocate a huge (>64k) buffer. On win32/Unix, we can use the normal * memory allocator; on 16-bit systems, this would have to make a * special OS call to do the allocation. * * Note that we use th_malloc(), not the plain malloc(). th_malloc() is * defined in the portable HTML TADS code; it provides a debug version * of malloc that we can use to track memory allocations to ensure there * are no memory leaks. All 32-bit (and larger) platforms should use * this same definition for os_alloc_huge(). */ #define os_alloc_huge(siz) th_malloc(siz) /* * Increment a "huge" pointer by a given amount. On win32/Unix, * ordinary pointers are huge, so we just do the normal arithmetic. On * 16-bit systems, this may have to do some extra work. */ #define os_add_huge(ptr, amt) ((ptr) + (amt)) /* * Free a huge pointer. On systems that run Qt, we use the standard * memory allocator. * * Note that we use th_free(), not the plain free(); see the explanation * of the use of th_malloc() above. */ #define os_free_huge(ptr) th_free(ptr) /* * Memory alignment: align a size to the worst-case alignment * requirement for this system. For most systems, 4-byte alignment is * adequate, but some systems (such as 64-bit hardware) may have a * larger worst-case alignment requirement. */ // We assume 8-byte alignment instead of 4-byte, since we need to support // 64-bit compilers. #define os_align_size(siz) (((siz) + 7) & ~7) #endif /* HOS_QT_H */ qtads-2.1.7/src/hos_w32.h000066400000000000000000000001411265017072300150600ustar00rootroot00000000000000/* This file is empty and just needs to exist or else compilation on MS Windows * will fail. */ qtads-2.1.7/src/kcolorbutton.cc000066400000000000000000000150621265017072300164670ustar00rootroot00000000000000/* This file is part of the KDE libraries Copyright (C) 1997 Martin Jones (mjones@kde.org) Copyright (C) 1999 Cristian Tibirna (ctibirna@kde.org) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kcolorbutton.h" #include #include #include #include #include #include #include #include #include #include KColorButton::KColorButton( QWidget *parent ) : QPushButton( parent ) , d( new KColorButtonPrivate(this) ) { d->m_bdefaultColor = false; d->m_defaultColor = QColor(); setAcceptDrops( true); // 2000-10-15 (putzer): fixes broken keyboard usage connect (this, SIGNAL(clicked()), this, SLOT(_k_chooseColor())); } KColorButton::KColorButton( const QColor &c, QWidget *parent ) : QPushButton( parent ) , d( new KColorButtonPrivate(this) ) { d->col = c; d->m_bdefaultColor = false; d->m_defaultColor = QColor(); setAcceptDrops( true); // 2000-10-15 (putzer): fixes broken keyboard usage connect (this, SIGNAL(clicked()), this, SLOT(_k_chooseColor())); } KColorButton::KColorButton( const QColor &c, const QColor &defaultColor, QWidget *parent ) : QPushButton( parent ) , d( new KColorButtonPrivate(this) ) { d->col = c; d->m_bdefaultColor = true; d->m_defaultColor = defaultColor; setAcceptDrops( true); // 2000-10-15 (putzer): fixes broken keyboard usage connect (this, SIGNAL(clicked()), this, SLOT(_k_chooseColor())); } KColorButton::~KColorButton() { delete d; } QColor KColorButton::color() const { return d->col; } void KColorButton::setColor( const QColor &c ) { if ( d->col != c ) { d->col = c; repaint(); emit changed( d->col ); } } QColor KColorButton::defaultColor() const { return d->m_defaultColor; } void KColorButton::setDefaultColor( const QColor &c ) { d->m_bdefaultColor = c.isValid(); d->m_defaultColor = c; } void KColorButton::KColorButtonPrivate::initStyleOption(QStyleOptionButton* opt) const { opt->initFrom(q); opt->state |= q->isDown() ? QStyle::State_Sunken : QStyle::State_Raised; opt->features = QStyleOptionButton::None; if (q->isDefault()) opt->features |= QStyleOptionButton::DefaultButton; opt->text.clear(); opt->icon = QIcon(); } void KColorButton::paintEvent( QPaintEvent* ) { QPainter painter(this); //First, we need to draw the bevel. QStyleOptionButton butOpt; d->initStyleOption(&butOpt); style()->drawControl( QStyle::CE_PushButtonBevel, &butOpt, &painter, this ); //OK, now we can muck around with drawing out pretty little color box //First, sort out where it goes QRect labelRect = style()->subElementRect( QStyle::SE_PushButtonContents, &butOpt, this ); int shift = style()->pixelMetric( QStyle::PM_ButtonMargin ) / 2; labelRect.adjust(shift, shift, -shift, -shift); int x, y, w, h; labelRect.getRect(&x, &y, &w, &h); if (isChecked() || isDown()) { x += style()->pixelMetric( QStyle::PM_ButtonShiftHorizontal ); y += style()->pixelMetric( QStyle::PM_ButtonShiftVertical ); } QColor fillCol = isEnabled() ? d->col : palette().color(backgroundRole()); qDrawShadePanel( &painter, x, y, w, h, palette(), true, 1, NULL); if ( fillCol.isValid() ) painter.fillRect( x+1, y+1, w-2, h-2, fillCol ); if ( hasFocus() ) { QRect focusRect = style()->subElementRect( QStyle::SE_PushButtonFocusRect, &butOpt, this ); QStyleOptionFocusRect focusOpt; focusOpt.init(this); focusOpt.rect = focusRect; focusOpt.backgroundColor = palette().background().color(); style()->drawPrimitive( QStyle::PE_FrameFocusRect, &focusOpt, &painter, this ); } } QSize KColorButton::sizeHint() const { QStyleOptionButton opt; d->initStyleOption(&opt); return style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(40, 15), this). expandedTo(QApplication::globalStrut()); } QSize KColorButton::minimumSizeHint() const { QStyleOptionButton opt; d->initStyleOption(&opt); return style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(3, 3), this). expandedTo(QApplication::globalStrut()); } void KColorButton::dragEnterEvent( QDragEnterEvent *event) { event->setAccepted(qvariant_cast(event->mimeData()->colorData()).isValid() && isEnabled()); } void KColorButton::dropEvent( QDropEvent *event) { const QColor& c = qvariant_cast(event->mimeData()->colorData()); if (c.isValid()) { setColor(c); } } void KColorButton::keyPressEvent( QKeyEvent *e ) { if (e->matches(QKeySequence::Copy)) { QMimeData *mime=new QMimeData; mime->setColorData(color()); QApplication::clipboard()->setMimeData( mime, QClipboard::Clipboard ); } else if (e->matches(QKeySequence::Paste)) { const QColor& color = qvariant_cast(QApplication::clipboard()->mimeData(QClipboard::Clipboard)->colorData()); setColor( color ); } else QPushButton::keyPressEvent( e ); } void KColorButton::mousePressEvent( QMouseEvent *e) { d->mPos = e->pos(); QPushButton::mousePressEvent(e); } void KColorButton::mouseMoveEvent( QMouseEvent *e) { QPushButton::mouseMoveEvent(e); if( (e->buttons() & Qt::LeftButton) && (e->pos()-d->mPos).manhattanLength() > QApplication::startDragDistance() ) { QDrag* drag = new QDrag(this); QMimeData* mime = new QMimeData; mime->setColorData(color()); drag->setMimeData(mime); drag->exec(Qt::CopyAction); setDown(false); } } void KColorButton::KColorButtonPrivate::_k_chooseColor() { QColor c = q->color(); if ( m_bdefaultColor ) { c = QColorDialog::getColor(m_defaultColor, q); if( c.isValid() ) { q->setColor( c ); } else { q->setColor( m_defaultColor ); } } else { c = QColorDialog::getColor(c, q); if(c.isValid()) { q->setColor( c ); } } } qtads-2.1.7/src/kcolorbutton.h000066400000000000000000000063631265017072300163350ustar00rootroot00000000000000/* This file is part of the KDE libraries Copyright (C) 1997 Martin Jones (mjones@kde.org) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KCOLORBUTTON_H #define KCOLORBUTTON_H #include class KColorButtonPrivate; /** * @short A pushbutton to display or allow user selection of a color. * * This widget can be used to display or allow user selection of a color. * * @see KColorDialog * * \image html kcolorbutton.png "KDE Color Button" */ class KColorButton: public QPushButton { Q_OBJECT Q_PROPERTY( QColor color READ color WRITE setColor USER true ) Q_PROPERTY( QColor defaultColor READ defaultColor WRITE setDefaultColor ) public: /** * Creates a color button. */ explicit KColorButton( QWidget *parent = 0 ); /** * Creates a color button with an initial color @p c. */ explicit KColorButton( const QColor &c, QWidget *parent = 0 ); /** * Creates a color button with an initial color @p c and default color @p defaultColor. */ KColorButton( const QColor &c, const QColor &defaultColor, QWidget *parent = 0 ); virtual ~KColorButton(); /** * Returns the currently chosen color. */ QColor color() const; /** * Sets the current color to @p c. */ void setColor( const QColor &c ); /** * Returns the default color or an invalid color * if no default color is set. */ QColor defaultColor() const; /** * Sets the default color to @p c. */ void setDefaultColor( const QColor &c ); QSize sizeHint() const; QSize minimumSizeHint() const; Q_SIGNALS: /** * Emitted when the color of the widget * is changed, either with setColor() or via user selection. */ void changed( const QColor &newColor ); protected: virtual void paintEvent( QPaintEvent *pe ); virtual void dragEnterEvent( QDragEnterEvent *); virtual void dropEvent( QDropEvent *); virtual void mousePressEvent( QMouseEvent *e ); virtual void mouseMoveEvent( QMouseEvent *e); virtual void keyPressEvent( QKeyEvent *e ); private: class KColorButtonPrivate { public: KColorButtonPrivate(KColorButton *q): q(q) {} void _k_chooseColor(); KColorButton *q; QColor m_defaultColor; bool m_bdefaultColor : 1; bool dragFlag : 1; QColor col; QPoint mPos; void initStyleOption(QStyleOptionButton* opt) const; }; KColorButtonPrivate * const d; Q_PRIVATE_SLOT( d, void _k_chooseColor() ) }; #endif qtads-2.1.7/src/main.cc000066400000000000000000000063701265017072300146700ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include #include #include #include "settings.h" #include "sysframe.h" #include "qtadssound.h" // Static Qt4 builds on OS X need the text codec plugins. #if defined(STATIC_QT) and defined(Q_OS_MAC) and QT_VERSION < 0x050000 #include Q_IMPORT_PLUGIN(qcncodecs) Q_IMPORT_PLUGIN(qjpcodecs) Q_IMPORT_PLUGIN(qtwcodecs) Q_IMPORT_PLUGIN(qkrcodecs) #endif // On some platforms, SDL redefines main in order to provide a // platform-specific main() implementation. However, Qt handles this too, // so things can get weird. We need to make sure main is not redefined so // that Qt can find our own implementation and SDL will not try to do // platform-specific initialization work (like launching the Cocoa event-loop // or setting up the application menu on OS X, or redirecting stdout and stderr // to text files on Windows), which would break things. #ifdef main # undef main #endif int main( int argc, char** argv ) { CHtmlResType::add_basic_types(); CHtmlSysFrameQt* app = new CHtmlSysFrameQt(argc, argv, "QTads", "2.0", "Nikos Chantziaras", "qtads.sourceforge.net"); // Filename of the game to run. QString gameFileName; const QStringList& args = app->arguments(); if (args.size() == 2) { if (QFile::exists(args.at(1))) { gameFileName = args.at(1); } else if (QFile::exists(args.at(1) + QString::fromLatin1(".gam"))) { gameFileName = args.at(1) + QString::fromLatin1(".gam"); } else if (QFile::exists(args.at(1) + QString::fromLatin1(".t3"))) { gameFileName = args.at(1) + QString::fromLatin1(".t3"); } else { qWarning() << "File" << args.at(1) << "not found."; } } if (gameFileName.isEmpty() and app->settings()->askForGameFile) { gameFileName = QFileDialog::getOpenFileName(0, QObject::tr("Choose the TADS game you wish to run"), QString::fromLatin1(""), QObject::tr("TADS Games") + QString::fromLatin1("(*.gam *.Gam *.GAM *.t3 *.T3)")); } if (not initSound()) { delete app; return 1; } QMetaObject::invokeMethod(app, "entryPoint", Qt::QueuedConnection, Q_ARG(QString, gameFileName)); int ret = app->exec(); delete app; quitSound(); return ret; } qtads-2.1.7/src/missing.cc000066400000000000000000000045221265017072300154120ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include #include #include #include #include #include #include "missing.h" #include "globals.h" #include "settings.h" #include "sysframe.h" #ifndef _WIN32 int memicmp( const char* s1, const char* s2, size_t len ) { char* x1 = new char[len]; char* x2 = new char[len]; for (size_t i = 0; i < len; ++i) { x1[i] = std::tolower(s1[i]); x2[i] = std::tolower(s2[i]); } int ret = std::memcmp(x1, x2, len); delete[] x1; delete[] x2; return ret; } #endif int stricmp( const char* s1, const char* s2 ) { if (qFrame->tads3()) { return QString::localeAwareCompare(QString::fromUtf8(s1).toLower(), QString::fromUtf8(s2).toLower()); } // TADS 2 does not use UTF-8; use the encoding from our settings. QTextCodec* codec = QTextCodec::codecForName(qFrame->settings()->tads2Encoding); return QString::localeAwareCompare(codec->toUnicode(s1).toLower(), codec->toUnicode(s2).toLower()); } int strnicmp( const char* s1, const char* s2, size_t n ) { QString qs1; QString qs2; if (qFrame->tads3()) { qs1 = QString::fromUtf8(s1); qs2 = QString::fromUtf8(s2); } else { // TADS 2 does not use UTF-8; use the encoding from our settings. QTextCodec* codec = QTextCodec::codecForName(qFrame->settings()->tads2Encoding); qs1 = codec->toUnicode(s1); qs2 = codec->toUnicode(s2); } qs1.truncate(n); qs2.truncate(n); return QString::compare(qs1.toLower(), qs2.toLower()); } qtads-2.1.7/src/missing.h000066400000000000000000000033401265017072300152510ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ /* This file provides some non-standard functions that are considered * standard by the portable Tads code and thus aren't in the OS-layer. */ #ifndef MISSING_H #define MISSING_H #include /* For size_t. */ #ifdef __cplusplus extern "C" { #endif /* Counted-length case-insensitive string comparison. * * This function always compares 'len' characters, regardless if a '\0' * character has been detected. * * This function is already available on MS Windows. */ #ifndef _WIN32 int memicmp( const char* s1, const char* s2, size_t len ); #endif /* Case-insensitive string comparison. */ int stricmp( const char* s1, const char* s2 ); /* Length-limited case-insensitive string comparison. * * This function compares at most 'n' characters, or until a '\0' * character has been detected. */ int strnicmp( const char* s1, const char* s2, size_t n ); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* MISSING_H */ qtads-2.1.7/src/oemqt.c000066400000000000000000000023001265017072300147130ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ /* ChangeLog: * 2002/12/17 Niko - Creation */ #include "std.h" #include "globals.h" char G_tads_oem_app_name[] = "QTads "QTADS_VERSION; char G_tads_oem_dbg_name[] = ""; /* No debugger for Qt (yet). */ char G_tads_oem_display_mode[] = "multimedia"; char G_tads_oem_author[] = "QTads copyright (C) 2013 by Nikos Chantziaras \n"; int G_tads_oem_copyright_prefix = TRUE; qtads-2.1.7/src/osnetwin.h000066400000000000000000000001701265017072300154440ustar00rootroot00000000000000/* This gets included by the base code when building on (or cross * compiling to) MS Windows. */ #include "osnetdum.h" qtads-2.1.7/src/osqt.cc000066400000000000000000001317031265017072300147310ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ /* Qt-specific Tads OS functions. * * This file should only contain Tads OS specific functions. That doesn't mean * that you can't use C++ code inside the functions; you can use any C++ * feature you want, as long as the function headers are compatible with the * prototypes in "osifc.h". The only exception are static helper functions * that are clearly marked as such. */ #include #if QT_VERSION < 0x050000 #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "os.h" #include "osifcext.h" #include "globals.h" #include "settings.h" #include "sysframe.h" #include "syswininput.h" /* Translates a local filename to a unicode QString. */ QString fnameToQStr( const char* fname ) { Q_ASSERT(qFrame != 0); if (qFrame->tads3()) return QString::fromUtf8(fname); return QFile::decodeName(fname); } /* Translates a unicode QString filename to a local filename. */ QByteArray qStrToFname( const QString& fnameStr ) { if (qFrame->tads3()) return fnameStr.toUtf8(); return QFile::encodeName(fnameStr); } /* -------------------------------------------------------------------- * Basic file I/O interface. */ static osfildef* createQFile( const char* fname, QFile::OpenMode mode ) { Q_ASSERT(fname != 0); QFile* file = new QFile(fnameToQStr(fname)); if (not file->open(mode)) { delete file; return 0; } return file; } /* Open text file for reading. */ osfildef* osfoprt( const char* fname, os_filetype_t ) { return createQFile(fname, QFile::ReadOnly | QFile::Text); } /* Open text file for writing. */ osfildef* osfopwt( const char* fname, os_filetype_t ) { return createQFile(fname, QFile::WriteOnly | QFile::Truncate | QFile::Text); } /* Open text file for reading and writing, keeping existing contents. */ osfildef* osfoprwt( const char* fname, os_filetype_t ) { return createQFile(fname, QFile::ReadWrite | QFile::Text); } /* Open text file for reading and writing, truncating existing contents. */ osfildef* osfoprwtt( const char* fname, os_filetype_t ) { return createQFile(fname, QFile::ReadWrite | QFile::Truncate | QFile::Text); } /* Open binary file for writing. */ osfildef* osfopwb( const char* fname, os_filetype_t ) { return createQFile(fname, QFile::WriteOnly | QFile::Truncate); } /* Open binary file for reading. */ osfildef* osfoprb( const char* fname, os_filetype_t ) { return createQFile(fname, QFile::ReadOnly); } /* Open binary file for reading and writing, keeping any existing contents. */ osfildef* osfoprwb( const char* fname, os_filetype_t ) { return createQFile(fname, QFile::ReadWrite); } /* Open binary file for reading and writing, truncating existing contents. */ osfildef* osfoprwtb( const char* fname, os_filetype_t ) { return createQFile(fname, QFile::ReadWrite | QFile::Truncate); } /* Duplicate a file handle. */ osfildef* osfdup( osfildef* orig, const char* mode ) { Q_ASSERT(orig != 0); Q_ASSERT(mode != 0); Q_ASSERT(mode[0] != '\0'); QFile::OpenMode qMode = 0; size_t i = 0; if (mode[i] == 'r') { ++i; if (mode[i] == '+') { qMode = QFile::ReadWrite; ++i; } else { qMode = QFile::ReadOnly; } } else if (mode[0] == 'w') { ++i; qMode = QFile::WriteOnly; } else { return 0; } if (mode[i] == 't' or mode[i] == 's' or mode[i] == '\0') { qMode |= QFile::Text; } else if (mode[i] == 'b') { // Binary mode is the default; there's no explicit flag for it. } else { return 0; } QFile* file = new QFile(0); if (not file->open(orig->handle(), qMode)) { delete file; return 0; } return file; } /* Get a line of text from a text file. */ char* osfgets( char* buf, size_t len, osfildef* fp ) { Q_ASSERT(buf != 0); Q_ASSERT(fp != 0); if (fp->readLine(buf, len) != static_cast(len)) { return 0; } return buf; } /* Write a line of text to a text file. */ int osfputs( const char* buf, osfildef* fp ) { if (fp->write(buf, qstrlen(buf)) < 1) return EOF; return 1; } /* Write a null-terminated string to a text file. */ void os_fprintz( osfildef* fp, const char* str ) { Q_ASSERT(fp != 0); Q_ASSERT(str != 0); fp->write(str, qstrlen(str)); } /* Print a counted-length string (which might not be null-terminated) * to a file. */ void os_fprint( osfildef* fp, const char* str, size_t len ) { Q_ASSERT(fp != 0); Q_ASSERT(str != 0); fp->write(str, len); } /* Write bytes to file. */ int osfwb( osfildef* fp, const void* buf, int bufl ) { Q_ASSERT(fp != 0); Q_ASSERT(buf != 0); if (fp->write(static_cast(buf), bufl) < 1) return 1; return 0; } /* Flush buffered writes to a file. */ int osfflush( osfildef* fp ) { Q_ASSERT(fp != 0); if (not fp->flush()) return EOF; return 0; } /* Get a character from a file. */ int osfgetc( osfildef* fp ) { Q_ASSERT(fp != 0); char c; if (not fp->getChar(&c)) return EOF; return c; } /* Read bytes from file. */ int osfrb( osfildef* fp, void* buf, int bufl ) { Q_ASSERT(fp != 0); Q_ASSERT(buf != 0); return fp->read(static_cast(buf), bufl) != bufl; } /* Read bytes from file and return the number of bytes read. */ size_t osfrbc( osfildef* fp, void* buf, size_t bufl ) { Q_ASSERT(fp != 0); Q_ASSERT(buf != 0); int bytesRead = fp->read(static_cast(buf), bufl); if (bytesRead < 0) return 0; return bytesRead; } /* Get the current seek location in the file. */ long osfpos( osfildef* fp ) { Q_ASSERT(fp != 0); return fp->pos(); } /* Seek to a location in the file. */ int osfseek( osfildef* fp, long pos, int mode ) { Q_ASSERT(fp != 0); if (mode == OSFSK_CUR) pos += fp->pos(); else if (mode == OSFSK_END) pos += fp->size(); return not fp->seek(pos); } /* Close a file. */ void osfcls( osfildef* fp ) { delete fp; } /* Delete a file. */ int osfdel( const char* fname ) { return not QFile::remove(fnameToQStr(fname)); } /* Rename a file. */ int os_rename_file( const char* oldname, const char* newname ) { return QFile::rename(fnameToQStr(oldname), fnameToQStr(newname)); } /* Access a file - determine if the file exists. */ int osfacc( const char* fname ) { const QFileInfo info(fnameToQStr(fname)); // Since exists() returns false for dangling symlinks, we need // to explicitly check whether it's a symlink or not. if (not info.exists() and not info.isSymLink()) { return 1; } return 0; } // On Windows, we need to enable NTFS permission lookups. #ifdef Q_OS_WIN extern Q_CORE_EXPORT int qt_ntfs_permission_lookup; #endif int os_file_stat( const char* fname, int follow_links, os_file_stat_t* s ) { Q_ASSERT(fname != 0); Q_ASSERT(s != 0); const QString& fnameStr = fnameToQStr(fname); #ifdef Q_OS_WIN struct __stat64 info; // Get a UTF-16 version of the filename and NULL-terminate it. wchar_t* wFname = new wchar_t[fnameStr.length() + 1]; wFname[fnameStr.toWCharArray(wFname)] = L'\0'; // Get the file information. int statRet = _wstat64(wFname, &info); delete[] wFname; if (statRet != 0) return FALSE; /* translate the status fields */ s->sizelo = (uint32_t)(info.st_size & 0xFFFFFFFF); s->sizehi = (uint32_t)(info.st_size >> 32); s->cre_time = (os_time_t)info.st_ctime; s->mod_time = (os_time_t)info.st_mtime; s->acc_time = (os_time_t)info.st_atime; s->mode = info.st_mode; #else struct stat buf; int statRet; if (follow_links) statRet = stat(QFile::encodeName(fnameStr).constData(), &buf); else statRet = lstat(QFile::encodeName(fnameStr).constData(), &buf); if (statRet != 0) return false; s->sizelo = (uint32_t)(buf.st_size & 0xFFFFFFFF); s->sizehi = sizeof(buf.st_size) > 4 ? (uint32_t)((buf.st_size >> 32) & 0xFFFFFFFF) : 0; s->cre_time = buf.st_ctime; s->mod_time = buf.st_mtime; s->acc_time = buf.st_atime; s->mode = buf.st_mode; #endif s->attrs = 0; // QFileInfo::exists() cannot be trusted due to it's weird symlink // handling. if (osfacc(fname) != 0) { return false; } QFileInfo inf(fnameStr); bool isLink = inf.isSymLink(); #ifdef Q_OS_WIN // Don't treat shortcut files as symlinks. if (isLink and (QString::compare(inf.suffix(), QLatin1String("lnk"), Qt::CaseInsensitive) == 0)) { isLink = false; } #endif if (follow_links and isLink) { if (inf.symLinkTarget().isEmpty()) { return false; } inf.setFile(inf.symLinkTarget()); } if (inf.isHidden()) { s->attrs |= OSFATTR_HIDDEN; } #ifdef Q_OS_WIN // Enable NTFS permissions. ++qt_ntfs_permission_lookup; #endif if (inf.isReadable()) { s->attrs |= OSFATTR_READ; } if (inf.isWritable()) { s->attrs |= OSFATTR_WRITE; } #ifdef Q_OS_WIN // Disable NTFS permissions. --qt_ntfs_permission_lookup; #endif return true; } /* Manually resolve a symbolic link. */ int os_resolve_symlink( const char* fname, char* target, size_t target_size ) { const QByteArray& str = qStrToFname(QFileInfo(fnameToQStr(fname)).symLinkTarget()); if (str.isEmpty() or str.size() >= static_cast(target_size)) { return false; } qstrcpy(target, str.constData()); return true; } /* Get a list of root directories. */ size_t os_get_root_dirs( char* buf, size_t buflen ) { const QFileInfoList& rootList = QDir::drives(); // Paranoia. if (rootList.size() == 0) { return 0; } QByteArray str; for (int i = 0; i < rootList.size(); ++i) { str += rootList.at(i).path().toLatin1(); // Every path needs to be NULL-terminated. str += '\0'; } // The whole result must end with two NULL bytes. str += '\0'; if (buf != 0 and static_cast(buflen) >= str.size()) { memcpy(buf, str.constData(), str.size()); } return str.size(); } int os_open_dir( const char* dirname, osdirhdl_t* handle ) { QDirIterator* d = new QDirIterator(fnameToQStr(dirname), QDir::Dirs | QDir::Files | QDir::Hidden | QDir::System); if (d->next().isEmpty()) { // We can't read anything. Don't know why, don't care. return false; } *handle = d; return true; } int os_read_dir( osdirhdl_t handle, char* fname, size_t fname_size ) { const QByteArray& str = qStrToFname(handle->fileName()); if (str.isEmpty() or str.size() >= static_cast(fname_size)) { return false; } qstrcpy(fname, str.constData()); handle->next(); return true; } void os_close_dir( osdirhdl_t handle ) { delete handle; } /* Get a file's mode/type. This returns the same information as * the 'mode' member of os_file_stat_t from os_file_stat(), so we * simply call that routine and return the value. */ int osfmode( const char* fname, int follow_links, unsigned long* mode, unsigned long* attr ) { os_file_stat_t s; int ok; if ((ok = os_file_stat(fname, follow_links, &s)) != false) { if (mode != NULL) *mode = s.mode; if (attr != NULL) *attr = s.attrs; } return ok; } /* Determine if the given filename refers to a special file. */ os_specfile_t os_is_special_file( const char* fname ) { // We also check for "./" and "../" instead of just "." and // "..". (We use OSPATHCHAR instead of '/' though.) const char selfWithSep[3] = {'.', OSPATHCHAR, '\0'}; const char parentWithSep[4] = {'.', '.', OSPATHCHAR, '\0'}; if ((strcmp(fname, ".") == 0) or (strcmp(fname, selfWithSep) == 0)) return OS_SPECFILE_SELF; if ((strcmp(fname, "..") == 0) or (strcmp(fname, parentWithSep) == 0)) return OS_SPECFILE_PARENT; return OS_SPECFILE_NONE; } // -------------------------------------------------------------------- /* Convert string to all-lowercase. */ char* os_strlwr( char* s ) { Q_ASSERT(s != 0); Q_ASSERT(std::strlen(s) >= std::strlen(QString::fromUtf8(s).toLower().toUtf8())); return std::strcpy(s, QString::fromUtf8(s).toLower().toUtf8()); } /* -------------------------------------------------------------------- * Special file and directory locations. */ /* Seek to the resource file embedded in the current executable file. * * We don't support this (and probably never will.) */ osfildef* os_exeseek( const char*, const char* ) { return 0; } /* Get the full filename (including directory path) to the executable * file. */ int os_get_exe_filename( char* buf, size_t buflen, const char* ) { QFileInfo inf(QApplication::applicationFilePath()); if (not inf.exists() or not inf.isReadable()) { return false; } const QByteArray& encodedFilename = qStrToFname(inf.filePath()); if (encodedFilename.length() + 1 > static_cast(buflen)) { // The result would not fit in the buffer. return false; } qstrcpy(buf, encodedFilename.constData()); return true; } /* Get a special directory path. */ void os_get_special_path( char* buf, size_t buflen, const char* /*argv0*/, int id ) { Q_ASSERT(buf != 0); Q_ASSERT(buflen > 0); switch (id) { case OS_GSP_T3_RES: case OS_GSP_T3_INC: case OS_GSP_T3_LIB: case OS_GSP_T3_USER_LIBS: // We can safely ignore those. They're needed only by the compiler. // OS_GSP_T3_RES is only needed by the base code implementation of // charmap.cc (tads3/charmap.cpp) which we don't use. // FIXME: We do use tads3/charmap.cpp, so this needs to be handled. return; case OS_GSP_T3_APP_DATA: case OS_GSP_LOGFILE: { const QString& dirStr = #if QT_VERSION < 0x050000 QDesktopServices::storageLocation(QDesktopServices::DataLocation); #else QStandardPaths::writableLocation(QStandardPaths::DataLocation); #endif QDir dir(dirStr); // Create the directory if it doesn't exist. if (not dir.exists() and not dir.mkpath(dirStr)) { // TODO: Error dialog. qWarning() << "Could not create directory path:" << dirStr; Q_ASSERT(qStrToFname(QDir::tempPath()).size() < static_cast(buflen)); strncpy(buf, qStrToFname(QDir::tempPath()).constData(), buflen); return; } Q_ASSERT(qStrToFname(dirStr).size() < static_cast(buflen)); strncpy(buf, qStrToFname(dirStr).constData(), buflen); buf[buflen - 1] = '\0'; break; } default: // We didn't recognize the specified id. That means the base code // added a new value for it that we don't know about. // TODO: Error dialog. qWarning("Unknown id in os_get_special_path()"); } } /* -------------------------------------------------------------------- */ /* Look for a file in the standard locations: current directory, program * directory, PATH. * * FIXME: We only look in the current directory, whatever that might be. */ #if 0 int os_locate( const char* fname, int /*flen*/, const char* /*arg0*/, char* buf, size_t bufsiz ) { //qDebug() << Q_FUNC_INFO << "\n Looking for:" << fname; Q_ASSERT(fname != 0); Q_ASSERT(buf != 0); const QFileInfo& fileInfo = QFileInfo(fnameToQStr(fname)); const QByteArray& result = qStrToFname(fileInfo.absoluteFilePath()); if (bufsiz > result.length() and QFile::exists(fileInfo.absoluteFilePath())) { strcpy(buf, result.constData()); return true; } // Not found or buffer not big enough. return false; } #endif /* -------------------------------------------------------------------- */ /* Create and open a temporary file. */ osfildef* os_create_tempfile( const char* fname, char* buf ) { if (fname != 0 and fname[0] != '\0') { // A filename has been specified; use it. return createQFile(fname, QFile::ReadWrite | QFile::Truncate); } Q_ASSERT(buf != 0); // No filename was given; create a temporary file. buf[0] = '\0'; QTemporaryFile* file = new QTemporaryFile(QString::fromLatin1("/qtads_XXXXXX")); if (not file->open()) { delete file; return 0; } return file; } /* Delete a temporary file created with os_create_tempfile(). */ int osfdel_temp( const char* fname ) { Q_ASSERT(fname != 0); if (fname[0] == '\0' or QFile::remove(fnameToQStr(fname))) { // If fname was empty, it has been already deleted automatically by // osfcls(). If fname was not empty, QFile::remove has taken care of // deleting the file. return 0; } // QFile::remove() failed. return 1; } /* This isn't actually used by the basecode. */ #if 0 void os_get_tmp_path( char* buf ) { } #endif /* Generate a name for a temporary file. */ int os_gen_temp_filename( char* buf, size_t buflen ) { QTemporaryFile tmpfile(QDir::tempPath() + QString::fromLatin1("/qtads_XXXXXX")); if (not tmpfile.open()) { return false; } // Don't automatically delete the file from disk. This is safer, // since another process could create a file with the same name // before our caller gets the chance to re-create the file. tmpfile.setAutoRemove(false); const QByteArray& data = qStrToFname(tmpfile.fileName()); if (data.length() >= static_cast(buflen)) { // 'buf' isn't big enough to hold the result, including the // terminating '\0'. return false; } qstrcpy(buf, data.constData()); return true; } /* -------------------------------------------------------------------- * Basic directory/folder management routines. */ /* Create a directory. */ int os_mkdir( const char* dir, int create_parents ) { if (create_parents) { return QDir().mkpath(fnameToQStr(dir)); } return QDir().mkdir(fnameToQStr(dir)); } /* Remove a directory. */ int os_rmdir( const char* dir ) { return QDir().rmdir(fnameToQStr(dir)); } /* -------------------------------------------------------------------- * Filename manipulation routines. */ /* Apply a default extension to a filename, if it doesn't already have one. */ void os_defext( char* fn, const char* ext ) { Q_ASSERT(fn != 0); Q_ASSERT(ext != 0); if (QFileInfo(fnameToQStr(fn)).suffix().isEmpty()) { os_addext(fn, ext); } } /* Unconditionally add an extention to a filename. * * TODO: Find out if there are systems that don't use the dot as the extension * separator. (Only systems supported by Qt of course.) */ void os_addext( char* fn, const char* ext ) { Q_ASSERT(fn != 0); Q_ASSERT(ext != 0); std::strcat(fn, "."); std::strcat(fn, ext); } /* Remove the extension from a filename. */ void os_remext( char* fn ) { Q_ASSERT(fn != 0); QFileInfo file(fnameToQStr(fn)); if (file.suffix().isEmpty()) { return; } std::strcpy(fn, qStrToFname(file.completeBaseName())); } /* Get a pointer to the root name portion of a filename. * * Note that Qt's native path separator character is '/'. It doesn't matter on * what OS we're running. * * FIXME: This only works for UTF-8 (T3 only). */ char* os_get_root_name( const char* buf ) { Q_ASSERT(buf != 0); const char* p = buf; for (p += std::strlen(buf) - 1; p > buf and *p != '/'; --p) ; if (p != buf) { ++p; } return const_cast(p); } /* Build a full path name, given a path and a filename. */ void os_build_full_path( char* fullpathbuf, size_t fullpathbuflen, const char* path, const char* filename ) { Q_ASSERT(fullpathbuf != 0); Q_ASSERT(path != 0); Q_ASSERT(filename != 0); qstrncpy(fullpathbuf, qStrToFname(QFileInfo(QDir(fnameToQStr(path)), fnameToQStr(filename)).filePath()), fullpathbuflen); } void os_combine_paths( char* fullpathbuf, size_t fullpathbuflen, const char* path, const char* filename ) { Q_ASSERT(fullpathbuf != 0); Q_ASSERT(path != 0); Q_ASSERT(filename != 0); bool filenameIsDot = false; bool filenameIsDotDot = false; if (qstrcmp(filename, "..") == 0 or qstrcmp(filename, "../") == 0) filenameIsDotDot = true; else if (qstrcmp(filename, ".") == 0 or qstrcmp(filename, "./") == 0) { filenameIsDot = true; } if (filenameIsDot or filenameIsDotDot) { os_build_full_path(fullpathbuf, fullpathbuflen, path, ""); if (qstrlen(fullpathbuf) + 3 <= fullpathbuflen) { strcat(fullpathbuf, filenameIsDot ? "." : ".."); } } else { os_build_full_path(fullpathbuf, fullpathbuflen, path, filename); } } /* Extract the path from a filename. */ void os_get_path_name( char* pathbuf, size_t pathbuflen, const char* fname ) { Q_ASSERT(pathbuf != 0); Q_ASSERT(fname != 0); qstrncpy(pathbuf, qStrToFname(QFileInfo(fnameToQStr(fname)).path()).constData(), pathbuflen); } /* Convert a relative URL into a relative filename path. */ void os_cvt_url_dir( char* result_buf, size_t result_buf_size, const char* src_url ) { Q_ASSERT(result_buf != 0); Q_ASSERT(src_url != 0); QString result(fnameToQStr(src_url)); qstrncpy(result_buf, qStrToFname(result).constData(), result_buf_size); } /* Determine whether a filename specifies an absolute or relative path. */ int os_is_file_absolute( const char* fname ) { Q_ASSERT(fname != 0); return QFileInfo(fnameToQStr(fname)).isAbsolute(); } /* Get the absolute, fully qualified filename for a file. */ int os_get_abs_filename( char* result_buf, size_t result_buf_size, const char* filename ) { Q_ASSERT(result_buf != 0); const QByteArray& data = qStrToFname(QFileInfo(fnameToQStr(filename)).absoluteFilePath()); qstrncpy(result_buf, data.constData(), result_buf_size); if (data.length() >= static_cast(result_buf_size)) { // Result didn't fit in 'result_buf'. return false; } return true; } // This is only used by the HTML TADS debugger, which we don't include // in our build. #if 0 int os_get_rel_path( char* result_buf, size_t result_buf_size, const char* basepath, const char* filename) { Q_ASSERT(result_buf != 0); Q_ASSERT(basepath != 0); Q_ASSERT(filename != 0); QDir baseDir(fnameToQStr(basepath)); const QString& result = baseDir.relativeFilePath(fnameToQStr(filename)); if (result.isEmpty()) { qstrncpy(result_buf, filename, result_buf_size); return false; } qstrncpy(result_buf, qStrToFname(result), result_buf_size); return true; } #endif /* Determine if the given file is in the given directory. */ #if 1 /* TODO: We use the version from the DOS/Windows implementation for now. * I'll do the Qt-specific implementation later. */ static void canonicalize_path( char* path ) { // We canonicalize only the path, in case the file doesn't actually exist. // QFileInfo::canonicalFilePath() doesn't work for non-existent files. QFileInfo info(fnameToQStr(path)); QString cleanPath; if (info.isDir()) { cleanPath = info.filePath(); } else { cleanPath = info.path(); } QByteArray canonPath(qStrToFname(QDir(cleanPath).canonicalPath())); // Append the filename if we previously stripped it. if (not info.isDir()) { QString cleanFilename(fnameToQStr(path)); int i = cleanFilename.length(); while (cleanFilename[i] != QChar::fromLatin1('/') and i > 0) { --i; } canonPath.append(qStrToFname(cleanFilename.mid(i))); } qstrncpy(path, canonPath.constData(), OSFNMAX); } int os_is_file_in_dir( const char* filename, const char* path, int include_subdirs, int match_self ) { char filename_buf[OSFNMAX], path_buf[OSFNMAX]; size_t flen, plen; /* absolute-ize the filename, if necessary */ if (!os_is_file_absolute(filename)) { os_get_abs_filename(filename_buf, sizeof(filename_buf), filename); filename = filename_buf; } /* absolute-ize the path, if necessary */ if (!os_is_file_absolute(path)) { os_get_abs_filename(path_buf, sizeof(path_buf), path); path = path_buf; } /* * canonicalize the paths, to remove .. and . elements - this will make * it possible to directly compare the path strings */ safe_strcpy(filename_buf, sizeof(filename_buf), filename); canonicalize_path(filename_buf); filename = filename_buf; safe_strcpy(path_buf, sizeof(path_buf), path); canonicalize_path(path_buf); path = path_buf; /* get the length of the filename and the length of the path */ flen = strlen(filename); plen = strlen(path); /* if the path ends in a separator character, ignore that */ if (plen > 0 && (path[plen-1] == '\\' || path[plen-1] == '/')) --plen; /* * Check that the filename has 'path' as its path prefix. First, check * that the leading substring of the filename matches 'path', ignoring * case. Note that we need the filename to be at least two characters * longer than the path: it must have a path separator after the path * name, and at least one character for a filename past that. */ if (flen < plen + 2 || memicmp(filename, path, plen) != 0) return FALSE; /* * Okay, 'path' is the leading substring of 'filename'; next make sure * that this prefix actually ends at a path separator character in the * filename. (This is necessary so that we don't confuse "c:\a\b.txt" * as matching "c:\abc\d.txt" - if we only matched the "c:\a" prefix, * we'd miss the fact that the file is actually in directory "c:\abc", * not "c:\a".) */ if (filename[plen] != '\\' && filename[plen] != '/') return FALSE; /* * We're good on the path prefix - we definitely have a file that's * within the 'path' directory or one of its subdirectories. If we're * allowed to match on subdirectories, we already have our answer * (true). If we're not allowed to match subdirectories, we still have * one more check, which is that the rest of the filename is free of * path separator charactres. If it is, we have a file that's directly * in the 'path' directory; otherwise it's in a subdirectory of 'path' * and thus isn't a match. */ if (include_subdirs) { /* * filename is in the 'path' directory or one of its * subdirectories, and we're allowed to match on subdirectories, so * we have a match */ return TRUE; } else { const char *p; /* * We're not allowed to match subdirectories, so scan the rest of * the filename for path separators. If we find any, the file is * in a subdirectory of 'path' rather than directly in 'path' * itself, so it's not a match. If we don't find any separators, * we have a file directly in 'path', so it's a match. */ for (p = filename + plen + 1 ; *p != '\0' && *p != '/' && *p != '\\' ; ++p) ; /* * if we reached the end of the string without finding a path * separator character, it's a match */ return (*p == '\0'); } } #else int os_is_file_in_dir( const char* filename, const char* path, int include_subdirs ) { Q_ASSERT(filename != 0); Q_ASSERT(path != 0); Q_ASSERT(filename[0] != '\0'); Q_ASSERT(filename[qstrlen(filename) - 1] != '/'); QFileInfo fileInf(fnameToQStr(filename)); const QString& pathStr = QFileInfo(fnameToQStr(path)).canonicalFilePath(); // If the filename is absolute but the file doesn't exist, we know // that we're not going to find it anywhere, so report failure. if (fileInf.isAbsolute() and not fileInf.exists()) { return false; } const QString& fnameStr = fileInf.filePath(); // Look in 'path' first, before recursing its subdirectories. bool found; if (fnameStr.startsWith(pathStr) and fileInf.exists()) found = true; else found = false; // If we already found the file in 'path', or we're not searching in // subdirectories, report the result now; in both cases, we don't need // to recurse subdirectories. if (found or not include_subdirs) { return found; } // We didn't find the file and need to recurse all subdirectories. // Iterate over every subdirectory and look for the file in each one. We // only need to iterate directories, not regular files, and we omit the // "." and ".." directory entries. We do follow symbolic links; it's OK // to do so, since QDirIterator will detect loops. QDirIterator it(pathStr, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks); while (it.hasNext() and not found) { if (fnameStr.startsWith(QDir(it.next()).canonicalPath()) and fileInf.exists()) { found = true; } } return found; } #endif // -------------------------------------------------------------------- /* Get a suitable seed for a random number generator. */ void os_rand( long* val ) { Q_ASSERT(val != 0); static bool initialized = false; if (not initialized) { qsrand(QDateTime::currentDateTime().toTime_t()); initialized = true; } *val = qrand(); } /* Generate random bytes for use in seeding a PRNG (pseudo-random number * generator). */ void os_gen_rand_bytes( unsigned char* buf, size_t len ) { #ifdef Q_OS_UNIX QFile rdev(QString::fromLatin1("/dev/urandom")); if (rdev.open(QFile::ReadOnly)) { qint64 bytesRead = rdev.read(reinterpret_cast(buf), len); if (bytesRead >= static_cast(len)) return; if (bytesRead > 0) { // For whatever reason, we couldn't read the requested amount of // bytes from the device. We'll fill the rest with the generic // generator below. buf += bytesRead; len -= bytesRead; } } #endif static bool initialized = false; if (not initialized) { qsrand(QDateTime::currentDateTime().toTime_t()); initialized = true; } for (size_t i = 0; i < len; ++i) { buf[i] = qrand() % 256; } } /* -------------------------------------------------------------------- * Allocating sprintf and vsprintf. */ // FIXME: Implement this. //int //os_asprintf( char** bufptr, const char* fmt, ... ) //{} // FIXME: Implement this. //int //os_vasprintf( char** bufptr, const char* fmt, va_list ap ) //{} /* -------------------------------------------------------------------- */ /* Set busy cursor. * * This made sense with a 386 back in the day, where loading a T2 game needed * some time. On today's computers this takes milliseconds, so it doesn't make * sense to provide a "busy cursor". */ void os_csr_busy( int /*flag*/ ) { /* if (flag) { QApplication::setOverrideCursor(Qt::WaitCursor); } else { QApplication::restoreOverrideCursor(); } */ } /* -------------------------------------------------------------------- * User Input Routines. */ /* Ask the user for a filename, using a system-dependent dialog or * other mechanism. */ int os_askfile( const char* prompt, char* fname_buf, int fname_buf_len, int prompt_type, os_filetype_t file_type ) { Q_ASSERT(prompt_type == OS_AFP_SAVE or prompt_type == OS_AFP_OPEN); Q_ASSERT(prompt != 0); Q_ASSERT(fname_buf != 0); QString filter; QString ext; switch (file_type) { case OSFTGAME: filter = QObject::tr("TADS 2 Games") + QString::fromLatin1(" (*.gam *.Gam *.GAM)"); break; case OSFTSAVE: filter = QObject::tr("TADS 2 Saved Games") + QString::fromLatin1(" (*.sav *.Sav *.SAV)"); break; case OSFTLOG: filter = QObject::tr("Game Transcripts") + QString::fromLatin1(" (*.txt *.Txt *.TXT)"); break; case OSFTT3IMG: Q_ASSERT(qFrame->tads3()); filter = QObject::tr("TADS 3 Games") + QString::fromLatin1(" (*.t3 *.T3)"); break; case OSFTT3SAV: Q_ASSERT(qFrame->tads3()); filter = QObject::tr("TADS 3 Saved Games") + QString::fromLatin1(" (*.t3v *.T3v *.T3V)"); ext = QString::fromLatin1("t3v"); break; } // Always provide an "All Files" filter. if (not filter.isEmpty()) { filter.append(QString::fromLatin1(";;")); filter.append(QObject::tr("All Files") + QString::fromLatin1(" (*)")); } QString promptStr; if (qFrame->tads3()) { promptStr = QString::fromUtf8(prompt); } else { // TADS 2 does not use UTF-8; use the encoding from our settings for the // prompt message. QTextCodec* codec = QTextCodec::codecForName(qFrame->settings()->tads2Encoding); promptStr = codec->toUnicode(prompt); } QString filename; if (prompt_type == OS_AFP_OPEN) { filename = QFileDialog::getOpenFileName(qFrame->gameWindow(), promptStr, QDir::currentPath(), filter); } else { filename = QFileDialog::getSaveFileName(qFrame->gameWindow(), promptStr, QDir::currentPath(), filter); } if (filename.isEmpty()) { // User cancelled. return OS_AFE_CANCEL; } const QByteArray& result = qStrToFname(filename); if (fname_buf_len <= result.length()) { return OS_AFE_FAILURE; } strcpy(fname_buf, result.constData()); if (not ext.isEmpty()) { // Since `ext' is not empty, an extension should be // appended (if none exists already). os_defext(fname_buf, qStrToFname(ext).constData()); fname_buf[fname_buf_len - 1] = '\0'; } return OS_AFE_SUCCESS; } // -------------------------------------------------------------------- /* Ask for input through a dialog. */ int os_input_dialog( int icon_id, const char* prompt, int standard_button_set, const char** buttons, int button_count, int default_index, int cancel_index ) { Q_ASSERT(prompt != 0); Q_ASSERT(icon_id == OS_INDLG_ICON_NONE or icon_id == OS_INDLG_ICON_WARNING or icon_id == OS_INDLG_ICON_INFO or icon_id == OS_INDLG_ICON_QUESTION or icon_id == OS_INDLG_ICON_ERROR); Q_ASSERT(standard_button_set == 0 or standard_button_set == OS_INDLG_OK or standard_button_set == OS_INDLG_OKCANCEL or standard_button_set == OS_INDLG_YESNO or standard_button_set == OS_INDLG_YESNOCANCEL); QMessageBox dialog(qWinGroup); // We'll use that if we're running a T2 game. QTextCodec* t2Codec = QTextCodec::codecForName(qFrame->settings()->tads2Encoding); dialog.setText(qFrame->tads3() ? QString::fromUtf8(prompt) : t2Codec->toUnicode(prompt)); switch (icon_id) { case OS_INDLG_ICON_NONE: dialog.setIcon(QMessageBox::NoIcon); break; case OS_INDLG_ICON_WARNING: dialog.setIcon(QMessageBox::Warning); break; case OS_INDLG_ICON_INFO: dialog.setIcon(QMessageBox::Information); break; case OS_INDLG_ICON_QUESTION: dialog.setIcon(QMessageBox::Question); break; case OS_INDLG_ICON_ERROR: dialog.setIcon(QMessageBox::Critical); break; } QList buttonList; if (standard_button_set != 0) { switch (standard_button_set) { case OS_INDLG_OK: buttonList.append(dialog.addButton(QMessageBox::Ok)); break; case OS_INDLG_OKCANCEL: buttonList.append(dialog.addButton(QMessageBox::Ok)); buttonList.append(dialog.addButton(QMessageBox::Cancel)); break; case OS_INDLG_YESNO: buttonList.append(dialog.addButton(QMessageBox::Yes)); buttonList.append(dialog.addButton(QMessageBox::No)); break; case OS_INDLG_YESNOCANCEL: buttonList.append(dialog.addButton(QMessageBox::Yes)); buttonList.append(dialog.addButton(QMessageBox::No)); buttonList.append(dialog.addButton(QMessageBox::Cancel)); break; default: qWarning("os_input_dialog: unrecognized button set"); } } else for (int i = 0; i < button_count; ++i) { Q_ASSERT(buttons[i] != 0); const QString& buttonText = qFrame->tads3() ? QString::fromUtf8(buttons[i]) : t2Codec->toUnicode(buttons[i]); buttonList.append(dialog.addButton(buttonText, QMessageBox::AcceptRole)); } if (default_index != 0) { dialog.setDefaultButton(buttonList[default_index - 1]); } if (cancel_index != 0) { dialog.setEscapeButton(buttonList[default_index - 1]); } // We append a space to the window title to avoid the "<2>" that would // otherwise be appended automatically by some window managers (like KDE.) dialog.setWindowTitle(qWinGroup->windowTitle() + QChar::fromLatin1(' ')); dialog.exec(); QAbstractButton* result = dialog.clickedButton(); if (result == 0) { return cancel_index; } return buttonList.indexOf(static_cast(result)) + 1; } /* -------------------------------------------------------------------- * Time-functions. */ /* Higher-precision time (nanosecond precision). */ void os_time_ns( os_time_t* seconds, long* nanoseconds ) { const QDateTime& curTime = QDateTime::currentDateTime(); *seconds = curTime.toTime_t(); *nanoseconds = curTime.time().msec() * 1000000; } /* Get the current system high-precision timer. */ long os_get_sys_clock_ms( void ) { static QTime zeroPoint(QTime::currentTime()); static long lastRet = -1; static unsigned long wraps = 0; long ret = zeroPoint.elapsed(); if (ret < lastRet) { // Timer has wrapped to zero. This only happens when 24 hours have // passed since this function has been called for the first time. It's // unlikely that someone will run the interpreter for 24 hours, but // still... // // Note that the code *will* break if we're running for more than // 11.767.033 years, 251 days, 20 hours and 24 minutes. :P ++wraps; } lastRet = ret; return ret + (wraps * 86400000L); } /* Sleep for a while. */ void os_sleep_ms( long ms ) { if (not qFrame->gameRunning() or ms < 1) { return; } QEventLoop idleLoop; QTimer timer; timer.setSingleShot(true); QObject::connect(&timer, SIGNAL(timeout()), &idleLoop, SLOT(quit())); QObject::connect(qFrame, SIGNAL(gameQuitting()), &idleLoop, SLOT(quit())); timer.start(ms); idleLoop.exec(); } /* Set a file's type information. * * TODO: Find out if this can be empty on all systems Qt supports. */ void os_settype( const char*, os_filetype_t ) { } /* -------------------------------------------------------------------- */ /* Get filename from startup parameter, if possible. * * TODO: Find out what this is supposed to do. */ int os_paramfile( char* /*buf*/ ) { return false; } /* Terminate the program and exit with the given exit status. */ void os_term( int /*status*/ ) { qDebug() << Q_FUNC_INFO; } /* Initialize the time zone. * * TODO: Find out if this can be empty on all systems Qt supports. */ void os_tzset( void ) { } /* Set the default saved-game extension. * * We don't need to implement this since this routine is intended to be * invoked only if the interpreter is running as a stand-alone game, * and this isn't possible in QTads. */ void os_set_save_ext( const char* ) { } /* -------------------------------------------------------------------- */ /* Generate the name of the character set mapping table for Unicode * characters to and from the given local character set. * * We use UTF-8 for everything, which should work on all platforms. */ void os_get_charmap( char* mapname, int charmap_id ) { //qDebug() << Q_FUNC_INFO; Q_ASSERT(qFrame->tads3()); switch(charmap_id) { case OS_CHARMAP_DISPLAY: case OS_CHARMAP_FILENAME: case OS_CHARMAP_FILECONTENTS: strcpy(mapname, "utf-8"); break; default: qWarning("os_get_charmap() got an unknown charmap id"); strcpy(mapname, "utf-8"); } } /* Generate a filename for a character-set mapping file. */ void os_gen_charmap_filename( char* filename, char* internal_id, char* /*argv0*/ ) { qDebug() << Q_FUNC_INFO; Q_ASSERT(filename != 0); strncpy(filename, qStrToFname(QString::fromLatin1(internal_id) + QString::fromLatin1(".tcp")).constData(), OSFNMAX); filename[OSFNMAX - 1] = '\0'; } /* Receive notification that a character mapping file has been loaded. * * We simply switch the codec that QString uses to convert to and from * char* and QCString. */ void os_advise_load_charmap( char* /*id*/, char* /*ldesc*/, char* /*sysinfo*/ ) { qDebug() << Q_FUNC_INFO; //QTextCodec::setCodecForCStrings(QTextCodec::codecForName(sysinfo)); } /* -------------------------------------------------------------------- */ /* Get system information. */ int os_get_sysinfo( int code, void* /*param*/, long* result ) { Q_ASSERT(result != 0); switch(code) { case SYSINFO_HTML: case SYSINFO_JPEG: case SYSINFO_PNG: case SYSINFO_LINKS_HTTP: case SYSINFO_LINKS_FTP: case SYSINFO_LINKS_NEWS: case SYSINFO_LINKS_MAILTO: case SYSINFO_LINKS_TELNET: case SYSINFO_PNG_TRANS: case SYSINFO_PNG_ALPHA: case SYSINFO_OGG: case SYSINFO_MNG: case SYSINFO_MNG_TRANS: case SYSINFO_MNG_ALPHA: case SYSINFO_TEXT_HILITE: case SYSINFO_BANNERS: *result = 1; break; case SYSINFO_WAV: case SYSINFO_MIDI: case SYSINFO_WAV_MIDI_OVL: case SYSINFO_WAV_OVL: case SYSINFO_MPEG: case SYSINFO_MPEG1: case SYSINFO_MPEG2: case SYSINFO_MPEG3: #ifndef Q_OS_ANDROID *result = 1; #else *result = 0; #endif break; case SYSINFO_AUDIO_FADE: case SYSINFO_AUDIO_CROSSFADE: // We support fades and crossfades for everything except MIDI. *result = SYSINFO_AUDIOFADE_MPEG | SYSINFO_AUDIOFADE_OGG | SYSINFO_AUDIOFADE_WAV; break; case SYSINFO_PREF_IMAGES: if (qFrame->settings()->enableGraphics) { *result = 1; } else { *result = 0; } break; case SYSINFO_PREF_SOUNDS: if (qFrame->settings()->enableSoundEffects) { *result = 1; } else { *result = 0; } break; case SYSINFO_PREF_MUSIC: if (qFrame->settings()->enableMusic) { *result = 1; } else { *result = 0; } break; case SYSINFO_PREF_LINKS: if (qFrame->settings()->enableLinks) { *result = 1; } else { *result = 0; } break; case SYSINFO_TEXT_COLORS: *result = SYSINFO_TXC_RGB; break; case SYSINFO_INTERP_CLASS: *result = SYSINFO_ICLASS_HTML; break; default: // We didn't recognize the code, which means that this // QTads version is too old. qWarning("Game specified an unknown os_get_sysinfo() code."); return false; } // We recognized the code. return true; } /* -------------------------------------------------------------------- */ /* Open a popup menu window. */ // FIXME: Just a dummy implementation for now. int os_show_popup_menu( int /*default_pos*/, int /*x*/, int /*y*/, const char* /*txt*/, size_t /*txtlen*/, union os_event_info_t* /*evt*/ ) { if (qFrame->gameRunning()) { return OSPOP_FAIL; } return OSPOP_EOF; } /* Enable/disable a System Menu Command event in os_get_event(). */ // FIXME: Just a dummy implementation for now. void os_enable_cmd_event( int /*id*/, unsigned int /*status*/ ) { } void os_init_ui_after_load( class CVmBifTable* /*bif_table*/, class CVmMetaTable* /*meta_table*/) { } qtads-2.1.7/src/osqt.h000066400000000000000000000241621265017072300145730ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ /* Qt-specific Tads OS functions. * * This file *must not* contain C++ code, since it gets included from * the portable Tads C code. */ #ifndef OSQT_H #define OSQT_H /* The system headers should never be included from inside an extern "C" block. * However, we are included from tads2/os.h from inside such a block ourselves, * so everything we include will be extern "C" too. We need to reverse this or * some compilers will bark (Sun C++ 5.9 on Linux, for example.) */ #ifdef __cplusplus } #endif #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* Define CPU_IS_BIGENDIAN to the appropriate value for this platform. * Needed by tads3/sha2.cpp. */ #if Q_BYTE_ORDER == Q_BIG_ENDIAN #define CPU_IS_BIGENDIAN 1 #else #define CPU_IS_BIGENDIAN 0 #endif /* Most systems have typedefs for ushort, uint and ulong. If not, the * qtads.pro project file should be modified to define OS_NO_TYPES_DEFINED. */ #ifndef OS_NO_TYPES_DEFINED #define OS_UINT_DEFINED #ifndef Q_OS_ANDROID #define OS_USHORT_DEFINED #define OS_ULONG_DEFINED #endif #endif #define OSNOUI_OMIT_OS_FPRINTZ #define OSNOUI_OMIT_OS_FPRINT #define OSNOUI_OMIT_OS_CVT_URL_DIR #define OSNOUI_OMIT_TEMPFILE #define fname_memcmp memcmp /* TODO: Implement threads. */ #define OS_DECLARATIVE_TLS #define OS_DECL_TLS(t, v) t v /* We don't support the Atari 2600. */ #include "osbigmem.h" /* Provide some non-standard functions (memicmp(), etc). */ #include "missing.h" /* Our Tads OEM identifier. */ #define TADS_OEM_NAME "Nikos Chantziaras " #define NO_T2_COPYRIGHT_NOTICE /* We assume that the C-compiler is mostly ANSI compatible. */ #define OSANSI /* Special function qualifier needed for certain types of callback functions. * This is for old 16-bit systems; we don't need it and define it to nothing. */ #define OS_LOADDS /* Unices don't suffer the near/far pointers brain damage (thank God) so we * make this a do-nothing macro. */ #define osfar_t /* This is used to explicitly discard computed values (some compilers would * otherwise give a warning like "computed value not used" in some cases). * Casting to void should work on every ANSI-Compiler. */ #define DISCARD (void) /* Copies a struct into another. ANSI C allows the assignment operator to be * used with structs. */ #define OSCPYSTRUCT(x,y) ((x)=(y)) /* Link error messages into the application. */ #define ERR_LINK_MESSAGES /* System identifier and system descriptive name. We also state "Windows" * since we compile and run just fine under MS Windows. */ #define OS_SYSTEM_NAME "Qt" #define OS_SYSTEM_LDESC "Qt (Unix/MacOSX/MS-Windows)" /* Program Exit Codes. */ #define OSEXSUCC 0 /* Successful completion. */ #define OSEXFAIL 1 /* Failure. */ /* Theoretical maximum osmalloc() size. Unix systems have at least a 32-bit * memory space. Even on 64-bit systems, 2^32 is a good value, so we don't * bother trying to find out an exact value. */ #define OSMALMAX 0xffffffffL /* Maximum length of a filename. */ #define OSFNMAX 255 #ifndef OSPATHALT /* Other path separator characters. */ #define OSPATHALT "" #endif #ifndef OSPATHURL /* Path separator characters for URL conversions. */ #define OSPATHURL "/" #endif #ifndef OSPATHCHAR /* Normal path separator character. */ #define OSPATHCHAR '/' #endif /* Directory separator for PATH-style environment variables. */ #define OSPATHSEP ':' /* String giving the special path representing the current * working directory. */ #define OSPATHPWD "." #ifndef OS_NEWLINE_SEQ /* ASCII string giving the local newline sequence to write on output. */ #define OS_NEWLINE_SEQ "\n" #endif /* File handle structure for osfxxx functions. */ #ifdef __cplusplus typedef class QFile osfildef; #else typedef struct QFile osfildef; #endif /* The maximum width of a line of text. We ignore this, but the base code * needs it defined. * * Note: this value must fit in a single byte, so the maximum is 255. */ #define OS_MAXWIDTH 255 /* Disable the Tads swap file; computers have plenty of RAM these days. */ #define OS_DEFAULT_SWAP_ENABLED 0 /* TADS 2 macro/function configuration. Modern configurations always use the * no-macro versions, so these definitions should always be set as shown * below. */ #define OS_MCM_NO_MACRO #define ERR_NO_MACRO /* Not really needed; just a dummy. */ #define OS_TR_USAGE "usage: qtads [options] file" /* These values are used for the "mode" parameter of osfseek() to * indicate where to seek in the file. */ #define OSFSK_SET 0 /* Set position relative to the start of the file. */ #define OSFSK_CUR 1 /* Set position relative to the current file position. */ #define OSFSK_END 2 /* Set position relative to the end of the file. */ /* File modes. On Windows, we do the Microsoft thing. Everywhere else, we * assume POSIX stat(2). */ #ifdef Q_OS_WIN #define OSFMODE_FILE _S_IFREG #define OSFMODE_DIR _S_IFDIR #define OSFMODE_BLK 0 #define OSFMODE_CHAR _S_IFCHR #define OSFMODE_PIPE _S_IFIFO #define OSFMODE_SOCKET 0 #define OSFMODE_LINK 0 #else #define OSFMODE_FILE S_IFREG #define OSFMODE_DIR S_IFDIR #define OSFMODE_CHAR S_IFCHR #define OSFMODE_BLK S_IFBLK #define OSFMODE_PIPE S_IFIFO #define OSFMODE_LINK S_IFLNK #define OSFMODE_SOCKET S_IFSOCK #endif /* File attribute bits. */ #define OSFATTR_HIDDEN 0x0001 #define OSFATTR_SYSTEM 0x0002 #define OSFATTR_READ 0x0004 #define OSFATTR_WRITE 0x0008 #ifdef __cplusplus typedef class QDirIterator* osdirhdl_t; #else typedef struct QDirIterator* osdirhdl_t; #endif /* 64-bit time_t. Only Windows supports this. */ #ifdef Q_OS_WIN #define os_time_t __time64_t #endif /* ============= Functions follow ================ */ #ifdef __cplusplus } // extern "C" /* Helper routine. Translates a local filename to a unicode QString. In * Tads 3, we use UTF-8 for everything. In Tads 2, we need to use the * local character set for filenames. */ QString fnameToQStr( const char* fname ); /* Helper routine. Translates a unicode QString filename to a local * filename. */ QByteArray qStrToFname( const QString& fnameStr ); extern "C" { #endif /* 64-bit replacements for routines. Only Windows supports this. */ #ifdef Q_OS_WIN #define os_gmtime _gmtime64 #define os_localtime _localtime64 #define os_time _time64 #endif /* Allocate a block of memory of the given size in bytes. */ #define osmalloc malloc /* Free memory previously allocated with osmalloc(). */ #define osfree free /* Reallocate memory previously allocated with osmalloc() or osrealloc(), * changing the block's size to the given number of bytes. */ #define osrealloc realloc /* Open text file for reading. */ osfildef* osfoprt( const char* fname, os_filetype_t ); /* Open text file for writing. */ osfildef* osfopwt( const char* fname, os_filetype_t ); /* Open text file for reading and writing, keeping the file's existing * contents if the file already exists or creating a new file if no * such file exists. */ osfildef* osfoprwt( const char* fname, os_filetype_t ); /* Open text file for reading/writing. If the file already exists, * truncate the existing contents. Create a new file if it doesn't * already exist. */ osfildef* osfoprwtt( const char* fname, os_filetype_t ); /* Open binary file for writing. */ osfildef* osfopwb( const char* fname, os_filetype_t ); /* Open source file for reading - use the appropriate text or binary * mode. */ #define osfoprs osfoprt /* Open binary file for reading. */ osfildef* osfoprb( const char* fname, os_filetype_t ); /* Open binary file for reading/writing. If the file already exists, * keep the existing contents. Create a new file if it doesn't already * exist. */ osfildef* osfoprwb( const char* fname, os_filetype_t ); /* Open binary file for reading/writing. If the file already exists, * truncate the existing contents. Create a new file if it doesn't * already exist. */ osfildef* osfoprwtb( const char* fname, os_filetype_t ); /* Get a line of text from a text file. */ char* osfgets( char* buf, size_t len, osfildef* fp ); /* Write a line of text to a text file. */ int osfputs( const char* buf, osfildef* fp ); /* Write bytes to file. */ int osfwb( osfildef* fp, const void* buf, int bufl ); /* Flush buffered writes to a file. */ int osfflush( osfildef* fp ); /* Get a character from a file. */ int osfgetc( osfildef* fp ); /* Read bytes from file. */ int osfrb( osfildef* fp, void* buf, int bufl ); /* Read bytes from file and return the number of bytes read. */ size_t osfrbc( osfildef* fp, void* buf, size_t bufl ); /* Get the current seek location in the file. */ //#define osfpos ftell long osfpos( osfildef* fp ); /* Seek to a location in the file. */ int osfseek( osfildef* fp, long pos, int mode ); /* Close a file. */ void osfcls( osfildef* fp ); /* Delete a file. */ int osfdel( const char* fname ); /* Rename a file. */ int os_rename_file( const char* oldname, const char* newname ); /* Access a file - determine if the file exists. */ int osfacc( const char* fname ); /* Get a file's mode. */ int osfmode( const char* fname, int follow_links, unsigned long* mode, unsigned long* attr ); /* Allocating sprintf and vsprintf. */ #define os_asprintf asprintf #define os_vasprintf vasprintf #endif /* OSQT_H */ qtads-2.1.7/src/oswin.h000066400000000000000000000001411265017072300147330ustar00rootroot00000000000000/* This file is empty and just needs to exist or else compilation on MS Windows * will fail. */ qtads-2.1.7/src/qtadshostifc.h000066400000000000000000000174141265017072300163030ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef QTADSHOSTIFC_H #define QTADSHOSTIFC_H #include #include "vmhost.h" #include "resload.h" #include "appctx.h" #include "config.h" /* Host application interface. This provides a bridge between the T3 VM host * interface (class CVmHostIfc) and the TADS 2 application context (struct * appctxdef) mechanism. */ class QTadsHostIfc: public CVmHostIfc { private: appctxdef* fAppctx; int fIoSafetyRead; int fIoSafetyWrite; CResLoader* fCmapResLoader; public: QTadsHostIfc( struct appctxdef* appctx ) : fAppctx(appctx), fIoSafetyRead(VM_IO_SAFETY_READWRITE_CUR), fIoSafetyWrite(VM_IO_SAFETY_READWRITE_CUR) { // TODO: Use the directory where charmap files are stored. this->fCmapResLoader = new CResLoader("./"); } ~QTadsHostIfc() override { delete this->fCmapResLoader; } // // CVmHostIfc interface implementation. // int get_io_safety_read() override { if (this->fAppctx != 0 and this->fAppctx->get_io_safety_level != 0) { // Ask the app context to handle it. int readLvl; (*this->fAppctx->get_io_safety_level)(this->fAppctx->io_safety_level_ctx, &readLvl, 0); return readLvl; } else { // The app context doesn't care - use our own level memory */ return this->fIoSafetyRead; } } int get_io_safety_write() override { if (this->fAppctx != 0 and this->fAppctx->get_io_safety_level != 0) { // Ask the app context to handle it. int writeLvl; (*this->fAppctx->get_io_safety_level)(this->fAppctx->io_safety_level_ctx, 0, &writeLvl); return writeLvl; } else { // The app context doesn't care - use our own level memory */ return this->fIoSafetyWrite; } } void set_io_safety( int readLvl, int writeLvl ) override { if (this->fAppctx != 0 and this->fAppctx->set_io_safety_level != 0) { // Let the app context handle it. (*this->fAppctx->set_io_safety_level)(this->fAppctx->io_safety_level_ctx, readLvl, writeLvl); } else { // The app doesn't care - set our own level memory. this->fIoSafetyRead = readLvl; this->fIoSafetyWrite = writeLvl; } } // FIXME: Implement void get_net_safety( int* client_level, int* server_level ) override { if (client_level != 0) *client_level = 0; if (server_level != 0) *server_level = 0; } // FIXME: Implement void set_net_safety( int /*client_level*/, int /*server_level*/ ) override { } class CResLoader* get_sys_res_loader() override { return this->fCmapResLoader; } void set_image_name( const char* fname ) override { // Pass it through the app context if possible. if (this->fAppctx != 0 and this->fAppctx->set_game_name != 0) { (*this->fAppctx->set_game_name)(this->fAppctx->set_game_name_ctx, fname); } } void set_res_dir( const char* fname ) override { // Pass it through the app context if possible. if (this->fAppctx != 0 and this->fAppctx->set_res_dir != 0) { (*this->fAppctx->set_res_dir)(this->fAppctx->set_res_dir_ctx, fname); } } int add_resfile( const char* fname ) override { // Pass it through the app context if possible. if (this->fAppctx != 0 and this->fAppctx->add_resfile != 0) { return (*this->fAppctx->add_resfile)(this->fAppctx->add_resfile_ctx, fname); } else { return 0; } } // We suport add_resfile() if the application context does. int can_add_resfiles() override { // If the add_resfile function is defined in the application context, // we support adding resource files. return (this->fAppctx != 0 and this->fAppctx->add_resfile != 0); } void add_resource( unsigned long ofs, unsigned long siz, const char* res_name, size_t res_name_len, int fileno ) override { // Pass it through the app context if possible. if (this->fAppctx != 0 and this->fAppctx->add_resource != 0) { (*this->fAppctx->add_resource)(this->fAppctx->add_resource_ctx, ofs, siz, res_name, res_name_len, fileno); } } void add_resource( const char* fname, size_t fname_len, const char* res_name, size_t res_name_len) override { // Pass it through the app context if possible. if (this->fAppctx != 0 and this->fAppctx->add_resource_link != 0) { (*this->fAppctx->add_resource_link) (this->fAppctx->add_resource_link_ctx, fname, fname_len, res_name, res_name_len); } } const char* get_res_path() override { // Get the path from the app context if possible. return (this->fAppctx != 0 ? this->fAppctx->ext_res_path : 0); } // Determine if a resource exists. int resfile_exists( const char* res_name, size_t res_name_len ) override { // Let the application context handle it if possible; if not, just // return false, since we can't otherwise provide resource operations. if (this->fAppctx != 0 and this->fAppctx->resfile_exists != 0) { return (*this->fAppctx->resfile_exists)(this->fAppctx->resfile_exists_ctx, res_name, res_name_len); } else { return false; } } osfildef* find_resource( const char* res_name, size_t res_name_len, unsigned long* res_size ) override { // Let the application context handle it; if we don't have an // application context, we don't provide resource operation, so simply // return failure. if (this->fAppctx != 0 and this->fAppctx->find_resource != 0) { return (*this->fAppctx->find_resource)(this->fAppctx->find_resource_ctx, res_name, res_name_len, res_size); } else { return 0; } } vmhost_gin_t get_image_name( char* buf, size_t buflen ) override { // Let the application context handle it if possible; otherwise, return // false, since we can't otherwise ask for an image name. if (this->fAppctx != 0 and this->fAppctx->get_game_name != 0) { // Ask the host system to get a name. int ret = (*this->fAppctx->get_game_name)(this->fAppctx->get_game_name_ctx, buf, buflen); // If that failed, the user must have chosen to cancel; otherwise, // we were successful. return (ret ? VMHOST_GIN_SUCCESS : VMHOST_GIN_CANCEL); } else { // We can't ask for a name. return VMHOST_GIN_IGNORED; } } // Get a special file system path. void get_special_file_path( char* buf, size_t buflen, int id ) override { return os_get_special_path(buf, buflen, 0, id); } }; #endif qtads-2.1.7/src/qtadsimage.cc000066400000000000000000000054601265017072300160620ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include #include "syswin.h" #include "qtadsimage.h" #include "settings.h" void QTadsImage::drawFromPaintEvent( class CHtmlSysWin* win, class CHtmlRect* pos, htmlimg_draw_mode_t mode ) { QPainter painter(static_cast(win)->widget()); if (mode == HTMLIMG_DRAW_CLIP) { // Clip mode. Only draw the part of the image that would fit. If the // image is smaller than pos, adjust the drawing area to avoid scaling. int targetWidth; int targetHeight; if (this->width() > pos->right - pos->left) { targetWidth = pos->right - pos->left; } else { targetWidth = this->width(); } if (this->height() > pos->bottom - pos->top) { targetHeight = pos->bottom - pos->top; } else { targetHeight = this->height(); } painter.drawImage(pos->left, pos->top, *this, 0, 0, targetWidth, targetHeight); return; } if (mode == HTMLIMG_DRAW_STRETCH) { // If the image doesn't fit exactly, scale it. Use the "smooth" // transformation mode (which uses a bilinear filter) if enabled in // the settings. Qt::TransformationMode mode = qFrame->settings()->useSmoothScaling ? Qt::SmoothTransformation : Qt::FastTransformation; if (this->width() != pos->right - pos->left or this->height() != pos->bottom - pos->top) { painter.drawImage(QPoint(pos->left, pos->top), this->scaled(pos->right - pos->left, pos->bottom - pos->top, Qt::IgnoreAspectRatio, mode)); } else { painter.drawImage(QPoint(pos->left, pos->top), *this); } return; } // If we get here, 'mode' must have been HTMLIMG_DRAW_TILE. Q_ASSERT(mode == HTMLIMG_DRAW_TILE); QPixmap pix(QPixmap::fromImage(*this)); painter.drawTiledPixmap(pos->left, pos->top, pos->right - pos->left, pos->bottom - pos->top, pix); } qtads-2.1.7/src/qtadsimage.h000066400000000000000000000030351265017072300157200ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef QTADSIMAGE_H #define QTADSIMAGE_H #include #include "htmlsys.h" /* We handle all types of images the same way, so we implement that handling * in this class and derive the various CHtmlSysImage* classes from this one. */ class QTadsImage: public QImage { public: QTadsImage() { } QTadsImage( const QImage& qImg ) : QImage(qImg) { } // A call to this method is only allowed to happen from inside // QTadsDisplayWidget::paintEvent(). This always happens indirectly // through CHtmlFormatter::draw(), which QTadsDisplayWidget::painEvent() is // using to repaint the window. void drawFromPaintEvent( CHtmlSysWin* win, class CHtmlRect* pos, htmlimg_draw_mode_t mode ); }; #endif qtads-2.1.7/src/qtadssound.cc000066400000000000000000000430141265017072300161250ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include #include #include #ifndef Q_OS_ANDROID #include #include #endif #include "qtadssound.h" #include "globals.h" #include "sysframe.h" #include "settings.h" #include "syssoundwav.h" #include "syssoundogg.h" #include "syssoundmpeg.h" #include "syssoundmidi.h" bool initSound() { #ifndef Q_OS_ANDROID if (SDL_Init(SDL_INIT_AUDIO) != 0) { qWarning("Unable to initialize sound system: %s", SDL_GetError()); return false; } // Initialize SDL_sound. Sound_Init(); // This will preload the needed codecs now instead of constantly loading // and unloading them each time a sound is played/stopped. This is only // available in SDL_Mixer 1.2.10 and newer. #if (MIX_MAJOR_VERSION > 1) \ || ((MIX_MAJOR_VERSION == 1) && (MIX_MINOR_VERSION > 2)) \ || ((MIX_MAJOR_VERSION == 1) && (MIX_MINOR_VERSION == 2) && (MIX_PATCHLEVEL > 9)) int sdlFormats = MIX_INIT_OGG; if (Mix_Init((sdlFormats & sdlFormats) != sdlFormats)) { qWarning("Unable to load Ogg Vorbis support: %s", Mix_GetError()); return false; } #endif if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) != 0) { qWarning("Unable to initialize audio mixer: %s", Mix_GetError()); return false; } Mix_AllocateChannels(16); Mix_ChannelFinished(QTadsSound::callback); Mix_HookMusicFinished(CHtmlSysSoundMidiQt::callback); /* int numtimesopened, frequency, channels; Uint16 format; numtimesopened=Mix_QuerySpec(&frequency, &format, &channels); if(numtimesopened) { printf("Mix_QuerySpec: %s\n",Mix_GetError()); } else { char *format_str="Unknown"; switch(format) { case AUDIO_U8: format_str="U8"; break; case AUDIO_S8: format_str="S8"; break; case AUDIO_U16LSB: format_str="U16LSB"; break; case AUDIO_S16LSB: format_str="S16LSB"; break; case AUDIO_U16MSB: format_str="U16MSB"; break; case AUDIO_S16MSB: format_str="S16MSB"; break; } qDebug("opened=%d times frequency=%dHz format=%s channels=%d", numtimesopened, frequency, format_str, channels); } */ /* int max = Mix_GetNumChunkDecoders(); for(int i = 0; i < max; ++i) { qDebug() << "Sample chunk decoder" << i << "is for" << Mix_GetChunkDecoder(i); } max = Mix_GetNumMusicDecoders(); for(int i = 0; i < max; ++i) { qDebug() << "Sample chunk decoder" << i << "is for" << Mix_GetMusicDecoder(i); } */ return true; #else return false; #endif } void quitSound() { #ifndef Q_OS_ANDROID Mix_ChannelFinished(0); Mix_HookMusicFinished(0); // Close the audio device as many times as it was opened. // We disable this for now since it results in a crash in some systems. // It looks like a clash between SDL_mixer and SDL_sound. /* int opened = Mix_QuerySpec(0, 0, 0); for (int i = 0; i < opened; ++i) { Mix_CloseAudio(); } */ Sound_Quit(); SDL_Quit(); #endif } #ifndef Q_OS_ANDROID QList QTadsSound::fObjList; QTadsSound::QTadsSound( QObject* parent, Mix_Chunk* chunk, SoundType type ) : QObject(parent), fChunk(chunk), fChannel(-1), fType(type), fPlaying(false), fFadeOut(0), fCrossFade(false), fFadeOutTimer(new QTimer(0)), fDone_func(0), fDone_func_ctx(0), fRepeats(0), fRepeatsWanted(1) { // FIXME: Calculate sound length in a safer way. this->fLength = (this->fChunk->alen * 8) / (2 * 16 * 44.1); // Pretend that the sound is 30ms shorter than it really is in order to // compensate for wacky OS timers (Windows and low-Hz systems). if (this->fLength > 30) { this->fLength -= 30; } else { this->fLength = 0; } //qDebug() << "Sound length:" << this->fLength; connect(this, SIGNAL(readyToLoop()), SLOT(fDoLoop())); connect(this, SIGNAL(readyToFadeOut()), SLOT(fPrepareFadeOut())); connect(this->fFadeOutTimer, SIGNAL(timeout()), this, SLOT(fDoFadeOut())); connect(this, SIGNAL(destroyed()), SLOT(fDeleteTimer())); // Make sure the timer only calls our fade-out slot *once* and then stops. this->fFadeOutTimer->setSingleShot(true); } QTadsSound::~QTadsSound() { //qDebug() << Q_FUNC_INFO; this->fRepeatsWanted = -1; // If our chunk is still playing, halt it. if (Mix_GetChunk(this->fChannel) == this->fChunk and Mix_Playing(this->fChannel) == 1) { Mix_HaltChannel(this->fChannel); } // Wait till it finished before deleting it. if (Mix_GetChunk(this->fChannel) == this->fChunk) { while (Mix_Playing(this->fChannel) != 0) { SDL_Delay(10); } } // Free the raw audio data buffer if SDL_mixer didn't allocate it itself. if (not this->fChunk->allocated) { free(this->fChunk->abuf); } Mix_FreeChunk(this->fChunk); } void QTadsSound::fDoFadeOut() { Q_ASSERT(Mix_GetChunk(this->fChannel) == this->fChunk and Mix_Playing(this->fChannel) == 1); // If we need to do a crossfade, call the TADS callback now. if (this->fCrossFade and this->fDone_func) { this->fDone_func(this->fDone_func_ctx, this->fRepeats); // Make sure our SDL callback won't call the TADS callback a second // time. this->fDone_func = 0; this->fDone_func_ctx = 0; } Mix_FadeOutChannel(this->fChannel, this->fFadeOut); } void QTadsSound::fDoLoop() { // When playing again, we can't assume that we can use the same channel. this->fChannel = Mix_PlayChannel(-1, this->fChunk, 0); this->fTimePos.start(); ++this->fRepeats; // If this is the last iteration and we have a fade-out, set the fade-out // timer. if (this->fFadeOut > 0 and (this->fRepeatsWanted == -1 or this->fRepeats == this->fRepeatsWanted)) { this->fFadeOutTimer->start(this->fLength - this->fFadeOut); } } void QTadsSound::fPrepareFadeOut() { this->fFadeOutTimer->start(this->fLength - this->fFadeOut); } void QTadsSound::fDeleteTimer() { delete this->fFadeOutTimer; } void QTadsSound::callback( int channel ) { QTadsSound* mObj = 0; int index = 0; // Find the object that uses the specified channel. for (int i = 0; i < fObjList.size() and mObj == 0; ++i) { if (fObjList.at(i)->fChannel == channel) { mObj = fObjList.at(i); index = i; } } if (mObj == 0) { return; } // If it's an infinite loop sound, or it has not reached the wanted repeat // count yet, play again. if ((mObj->fRepeatsWanted == 0) or (mObj->fRepeats < mObj->fRepeatsWanted)) { mObj->emitReadyToLoop(); return; } // Remove the object from the list. Since it can be included several // times, only remove the instance we associated to the channel we're // handling. fObjList.removeAt(index); // Sound has repeated enough times, or it has been halted. In either case, // we need to invoke the TADS callback, if there is one. mObj->fPlaying = false; if (mObj->fDone_func) { mObj->fDone_func(mObj->fDone_func_ctx, mObj->fRepeats); } } int QTadsSound::startPlaying( void (*done_func)(void*, int repeat_count), void* done_func_ctx, int repeat, int vol, int fadeIn, int fadeOut, bool crossFade ) { // Check if user disabled digital sound. if (not qFrame->settings()->enableSoundEffects) { return 1; } Q_ASSERT(not this->fPlaying); // Adjust volume if it exceeds min/max levels. if (vol < 0) { vol = 0; } else if (vol > 100) { vol = 100; } // Convert the TADS volume level semantics [0..100] to SDL volume // semantics [0..MIX_MAX_VOLUME]. vol = (vol * MIX_MAX_VOLUME) / 100; // Set the volume level. Mix_VolumeChunk(this->fChunk, vol); this->fRepeatsWanted = repeat; if (fadeIn > 0) { this->fChannel = Mix_FadeInChannel(-1, this->fChunk, 0, fadeIn); } else { this->fChannel = Mix_PlayChannel(-1, this->fChunk, 0); } if (this->fChannel == -1) { qWarning() << "ERROR:" << Mix_GetError(); Mix_SetError(""); return 1; } else { this->fTimePos.start(); this->fPlaying = true; this->fCrossFade = crossFade; this->fRepeats = 1; QTadsSound::fObjList.append(this); this->fDone_func = done_func; this->fDone_func_ctx = done_func_ctx; if (fadeOut > 0) { this->fFadeOut = fadeOut; if (repeat == 1) { // The sound should only be played once. We need to set the // fade-out timer here since otherwise the sound won't get a // chance to fade-out. emit readyToFadeOut(); } } } return 0; } void QTadsSound::cancelPlaying( bool sync, int fadeOut, bool fadeOutInBg ) { if (not this->fPlaying) { return; } this->fRepeatsWanted = -1; if (not sync and fadeOut > 0) { if (fadeOutInBg and this->fDone_func) { // We need to do fade-out in the background; call the TADS callback // now. this->fDone_func(this->fDone_func_ctx, this->fRepeats); // Make sure our SDL callback won't call the TADS callback a second // time. this->fDone_func = 0; this->fDone_func_ctx = 0; } Mix_FadeOutChannel(this->fChannel, fadeOut); } else { Mix_HaltChannel(this->fChannel); } if (sync and Mix_GetChunk(this->fChannel) == this->fChunk) { // The operation needs to be synchronous; wait for the sound to finish. while (Mix_Playing(this->fChannel) != 0) { SDL_Delay(10); } } } void QTadsSound::addCrossFade( int ms ) { this->fCrossFade = true; this->fFadeOut = ms; if (this->fPlaying) { this->fRepeatsWanted = -1; int timeFromNow = this->fLength - this->fFadeOut - this->fTimePos.elapsed(); if (timeFromNow < 1) { timeFromNow = 1; this->fFadeOut = this->fLength - this->fTimePos.elapsed(); } this->fFadeOutTimer->start(timeFromNow); } } #endif CHtmlSysSound* QTadsSound::createSound( const CHtmlUrl* /*url*/, const textchar_t* filename, unsigned long seekpos, unsigned long filesize, CHtmlSysWin*, SoundType type ) #ifndef Q_OS_ANDROID { //qDebug() << "Loading sound from" << filename << "offset:" << seekpos << "size:" << filesize // << "url:" << url->get_url(); // Check if the file exists and is readable. QFileInfo inf(fnameToQStr(filename)); if (not inf.exists() or not inf.isReadable()) { qWarning() << "ERROR:" << inf.filePath() << "doesn't exist or is unreadable"; return 0; } // Open the file and seek to the specified position. QFile file(inf.filePath()); if (not file.open(QIODevice::ReadOnly)) { qWarning() << "ERROR: Can't open file" << inf.filePath(); return 0; } if (not file.seek(seekpos)) { qWarning() << "ERROR: Can't seek in file" << inf.filePath(); file.close(); return 0; } QByteArray data(file.read(filesize)); file.close(); if (data.isEmpty() or static_cast(data.size()) < filesize) { qWarning() << "ERROR: Could not read" << filesize << "bytes from file" << inf.filePath(); return 0; } // Create the RWops through which the data will be read later. SDL_RWops* rw = SDL_RWFromConstMem(data.constData(), data.size()); if (rw == 0) { qWarning() << "ERROR:" << SDL_GetError(); SDL_ClearError(); return 0; } // The chunk that will hold the final, decoded sound. Mix_Chunk* chunk; // If it's an MP3 or WAV, we'll decode it with SDL_sound. For Ogg Vorbis // we use SDL_mixer. The reason is that SDL_mixer plays WAV at wrong // speeds if they're not 11, 22 or 44kHz (like 48kHz or 32kHz) and crashes // sometimes with MP3s. SDL_sound can't cope well with Ogg Vorbis that // have more then two channels. if (type == MPEG or type == WAV) { if (rw == 0) { qWarning() << "ERROR:" << SDL_GetError(); SDL_ClearError(); return 0; } Sound_AudioInfo wantedFormat; wantedFormat.channels = 2; wantedFormat.rate = 44100; wantedFormat.format = MIX_DEFAULT_FORMAT; // Note that we use a large buffer size to speed-up decoding of large // MP3s on Windows; the decoding will take extremely long with small // buffer sizes. Sound_Sample* sample = Sound_NewSample(rw, type == WAV ? "WAV" : "MP3", &wantedFormat, #ifdef Q_OS_WIN 6291456 #else 131072 #endif ); if (sample == 0) { qWarning() << "ERROR:" << Sound_GetError(); Sound_ClearError(); return 0; } Sound_DecodeAll(sample); if (sample->flags & SOUND_SAMPLEFLAG_ERROR) { // We don't abort since some of these errors can be non-fatal. // Unfortunately, there's no way to tell :-/ qWarning() << "WARNING:" << Sound_GetError(); Sound_ClearError(); } Uint8* buf = static_cast(malloc(sample->buffer_size)); memcpy(buf, sample->buffer, sample->buffer_size); Sound_FreeSample(sample); chunk = Mix_QuickLoad_RAW(buf, sample->buffer_size); if (chunk == 0) { qWarning() << "ERROR:" << Mix_GetError(); Mix_SetError(""); free(buf); return 0; } } else { Q_ASSERT(type == OGG); chunk = Mix_LoadWAV_RW(rw, true); if (chunk == 0) { qWarning() << "ERROR:" << Mix_GetError(); Mix_SetError(""); return 0; } } // Alternative way of decoding MPEG, utilizing SMPEG directly. It sucks, // since SMPEG, for the piece of crap it is, tends to play MP3s at double // speed (chipmunks ahoy...) #if 0 if (type == MPEG) { // The sound is an mp3. We'll decode it into an SDL_Mixer chunk using // SMPEG. SMPEG* smpeg = SMPEG_new_data(data.data(), data.size(), 0, 0); // Set the format we want the decoded raw data to have. SDL_AudioSpec spec; SMPEG_wantedSpec(smpeg, &spec); spec.channels = 2; spec.format = MIX_DEFAULT_FORMAT; spec.freq = 44100; SMPEG_actualSpec(smpeg, &spec); // We decode the mpeg stream in steps of 8192kB each and increase the // size of the output buffer after each step. size_t bufSize = 8192; Uint8* buf = static_cast(malloc(bufSize)); // Needs to be set to digital silence because SMPEG is mixing source // and destination. memset(buf, 0, bufSize); // Prepare for decoding and decode the first bunch of data. SMPEG_play(smpeg); int bytesWritten = SMPEG_playAudio(smpeg, buf, 8192); // Decode the rest. Increase buffer as needed. while (bytesWritten > 0 and bytesWritten <= 8192) { bufSize += 8192; buf = static_cast(realloc(buf, bufSize)); memset(buf + (bufSize - 8192), 0, 8192); bytesWritten = SMPEG_playAudio(smpeg, buf + (bufSize - 8192), 8192); } // Done with decoding. SMPEG_stop(smpeg); if (SMPEG_error(smpeg) != 0) { qWarning() << "ERROR: cannot decode sound data:" << SMPEG_error(smpeg); free(buf); SMPEG_delete(smpeg); return 0; } SMPEG_delete(smpeg); // Adjust final buffer size to fit the decoded data exactly. buf = static_cast(realloc(buf, (bufSize - 8192) + bytesWritten)); chunk = Mix_QuickLoad_RAW(buf, (bufSize - 8192) + bytesWritten); } #endif // We have all the data we need; create the sound object. It is // *important* not to pass the CHtmlSysWin object as the parent in the // constructor; doing so would result in Qt deleting the sound object when // the parent object gets destroyed. Therefore, we simply pass 0 to make // the sound object parentless. CHtmlSysSound* sound = NULL; switch (type) { case WAV: //qDebug() << "Sound type: WAV"; sound = new CHtmlSysSoundWavQt(0, chunk, WAV); break; case OGG: //qDebug() << "Sound type: OGG"; sound = new CHtmlSysSoundOggQt(0, chunk, OGG); break; case MPEG: //qDebug() << "Sound type: MPEG"; sound = new CHtmlSysSoundMpegQt(0, chunk, MPEG); break; } return sound; } #else { return 0; } #endif qtads-2.1.7/src/qtadssound.h000066400000000000000000000071501265017072300157700ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef QTADSSOUND_H #define QTADSSOUND_H #include #include #include "tadshtml.h" #include "config.h" bool initSound(); void quitSound(); /* Provides the common code for all three types of digitized sound (WAV, * Ogg Vorbis and MP3). */ class QTadsSound: public QObject { Q_OBJECT public: enum SoundType { WAV, OGG, MPEG }; private: #ifndef Q_OS_ANDROID struct Mix_Chunk* fChunk; int fChannel; SoundType fType; bool fPlaying; int fFadeOut; bool fCrossFade; class QTimer* fFadeOutTimer; QTime fTimePos; // TADS callback to invoke on stop. void (*fDone_func)(void*, int repeat_count); // CTX to pass to the TADS callback. void* fDone_func_ctx; // How many times we repeated the sound. int fRepeats; // How many times should we repeat the sound. // 0 means repeat forever. int fRepeatsWanted; // Total length of the sound in milliseconds. unsigned fLength; // All QTadsMediaObjects that currently exist. We need this in order to // implement the SDL_Mixer callback (which in turn needs to call the TADS // callback) that is invoked after a channel has stopped playing. That // callback has to be a static member (C++ methods can't be C callbacks), // and since there's no 'this' pointer in static member functions, it needs // to invoke the TADS callback based on the channel number. static QList fObjList; // We can't call SDL_mixer functions from inside an SDL_mixer callback, so // we use the following method: when the sound stops and the callback gets // called, we don't play it again (if it's looped) from inside the callback // but emit a signal which connects to a slot which plays the sound one // more time. void emitReadyToLoop() { emit readyToLoop(); } void emitReadyToFadeOut() { emit readyToFadeOut(); } private slots: void fDoFadeOut(); void fDoLoop(); void fPrepareFadeOut(); void fDeleteTimer(); signals: void readyToLoop(); void readyToFadeOut(); public: QTadsSound( QObject* parent, struct Mix_Chunk* chunk, SoundType type ); ~QTadsSound() override; #endif public: // The SDL_Mixer callback for when a sound finished playing. static void callback( int channel ); int startPlaying( void (*done_func)(void*, int repeat_count), void* done_func_ctx, int repeat, int vol, int fadeIn, int fadeOut, bool crossFade ); void cancelPlaying( bool sync, int fadeOut, bool fadeOutInBg ); void addCrossFade( int ms ); static class CHtmlSysSound* createSound( const class CHtmlUrl* url, const textchar_t* filename, unsigned long seekpos, unsigned long filesize, class CHtmlSysWin* win, SoundType type ); }; #endif qtads-2.1.7/src/qtadstimer.h000066400000000000000000000033441265017072300157610ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef QTADSTIMER_H #define QTADSTIMER_H #include #include "htmlsys.h" /* Tads HTML layer class whose interface needs to be implemented by the * interpreter. * * See htmltads/htmlsys.h and htmltads/notes/porting.htm for information * about this class. */ class QTadsTimer: public QTimer, public CHtmlSysTimer { Q_OBJECT public slots: // We connect the timeout() signal to this slot. void trigger() { // If we have a callback, call it. if (this->func_ != 0) { this->invoke_callback(); } } public: QTadsTimer( void (*func)(void*), void* ctx, QObject* parent = 0 ) : QTimer(parent), CHtmlSysTimer(func, ctx) { connect(this, SIGNAL(timeout()), this, SLOT(trigger())); } // We bring this into public scope since we need to evaluate the callback // pointer in order to unregister the timer. using CHtmlSysTimer::func_; }; #endif qtads-2.1.7/src/settings.cc000066400000000000000000000236501265017072300156040ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include #include #include #include "settings.h" #include "syswingroup.h" #include "globals.h" void Settings::loadFromDisk() { QSettings sett; sett.beginGroup(QString::fromLatin1("media")); this->enableGraphics = sett.value(QString::fromLatin1("graphics"), true).toBool(); #ifndef Q_OS_ANDROID this->enableSoundEffects = sett.value(QString::fromLatin1("sounds"), true).toBool(); this->enableMusic = sett.value(QString::fromLatin1("music"), true).toBool(); #else this->enableSoundEffects = sett.value(QString::fromLatin1("sounds"), false).toBool(); this->enableMusic = sett.value(QString::fromLatin1("music"), false).toBool(); #endif this->enableLinks = sett.value(QString::fromLatin1("links"), true).toBool(); this->useSmoothScaling = sett.value(QString::fromLatin1("smoothImageScaling"), true).toBool(); sett.endGroup(); sett.beginGroup(QString::fromLatin1("colors")); this->mainBgColor = sett.value(QString::fromLatin1("mainbg"), QColor(Qt::white)).value(); this->mainTextColor = sett.value(QString::fromLatin1("maintext"), QColor(Qt::black)).value(); this->bannerBgColor = sett.value(QString::fromLatin1("bannerbg"), QColor(Qt::lightGray)).value(); this->bannerTextColor = sett.value(QString::fromLatin1("bannertext"), QColor(Qt::black)).value(); this->inputColor = sett.value(QString::fromLatin1("input"), QColor(70, 70, 70)).value(); this->underlineLinks = sett.value(QString::fromLatin1("underlinelinks"), false).toBool(); this->highlightLinks = sett.value(QString::fromLatin1("highlightlinks"), true).toBool(); this->unvisitedLinkColor = sett.value(QString::fromLatin1("unvisitedlinks"), QColor(Qt::blue)).value(); this->hoveringLinkColor = sett.value(QString::fromLatin1("hoveringlinks"), QColor(Qt::red)).value(); this->clickedLinkColor = sett.value(QString::fromLatin1("clickedlinks"), QColor(Qt::cyan)).value(); sett.endGroup(); #ifdef Q_OS_MAC const QString& DEFAULT_SERIF = QString::fromLatin1("Georgia,15"); const QString& DEFAULT_SANS = QString::fromLatin1("Helvetica,15"); const QString& DEFAULT_MONO = QString::fromLatin1("Andale Mono,15"); const QString& DEFAULT_SCRIPT = QString::fromLatin1("Apple Chancery,17"); #else #ifdef Q_OS_WIN const QString& DEFAULT_SERIF = QString::fromLatin1("Times New Roman,12"); const QString& DEFAULT_SANS = QString::fromLatin1("Verdana,12"); const QString& DEFAULT_MONO = QString::fromLatin1("Courier New,12"); const QString& DEFAULT_SCRIPT = QString::fromLatin1("Comic Sans MS,12"); #else #ifdef Q_OS_ANDROID const QString& DEFAULT_SERIF = QString::fromLatin1("Droid Serif"); const QString& DEFAULT_SANS = QString::fromLatin1("Droid Sans"); const QString& DEFAULT_MONO = QString::fromLatin1("Droid Sans Mono"); const QString& DEFAULT_SCRIPT = QString::fromLatin1("Droid Serif"); #else const QString& DEFAULT_SERIF = QString::fromLatin1("serif"); const QString& DEFAULT_SANS = QString::fromLatin1("sans-serif"); const QString& DEFAULT_MONO = QString::fromLatin1("monospace"); const QString& DEFAULT_SCRIPT = QString::fromLatin1("cursive"); #endif #endif #endif sett.beginGroup(QString::fromLatin1("fonts")); this->mainFont.fromString(sett.value(QString::fromLatin1("main"), DEFAULT_SERIF).toString()); this->fixedFont.fromString(sett.value(QString::fromLatin1("fixed"), DEFAULT_MONO).toString()); this->serifFont.fromString(sett.value(QString::fromLatin1("serif"), DEFAULT_SERIF).toString()); this->sansFont.fromString(sett.value(QString::fromLatin1("sans"), DEFAULT_SANS).toString()); this->scriptFont.fromString(sett.value(QString::fromLatin1("script"), DEFAULT_SCRIPT).toString()); this->writerFont.fromString(sett.value(QString::fromLatin1("typewriter"), DEFAULT_MONO).toString()); this->inputFont.fromString(sett.value(QString::fromLatin1("input"), DEFAULT_SERIF).toString()); this->useMainFontForInput = sett.value(QString::fromLatin1("useMainFontForInput"), true).toBool(); sett.endGroup(); sett.beginGroup(QString::fromLatin1("misc")); this->ioSafetyLevelRead = sett.value(QString::fromLatin1("ioSafetyLevelRead"), 2).toInt(); this->ioSafetyLevelWrite = sett.value(QString::fromLatin1("ioSafetyLevelWrite"), 2).toInt(); this->tads2Encoding = sett.value(QString::fromLatin1("tads2encoding"), QByteArray("windows-1252")).toByteArray(); this->pasteOnDblClk = sett.value(QString::fromLatin1("pasteondoubleclick"), true).toBool(); this->softScrolling = sett.value(QString::fromLatin1("softscrolling"), true).toBool(); this->askForGameFile = sett.value(QString::fromLatin1("askforfileatstart"), false).toBool(); this->confirmRestartGame = sett.value(QString::fromLatin1("confirmrestartgame"), true).toBool(); this->confirmQuitGame = sett.value(QString::fromLatin1("confirmquitgame"), true).toBool(); this->lastFileOpenDir = sett.value(QString::fromLatin1("lastFileOpenDir"), QString::fromLatin1("")).toString(); sett.endGroup(); sett.beginGroup(QString::fromLatin1("recent")); this->recentGamesList = sett.value(QString::fromLatin1("games"), QStringList()).toStringList(); Q_ASSERT(this->recentGamesList.size() <= this->recentGamesCapacity); // Remove any files that don't exist or aren't readable. for (int i = 0; i < this->recentGamesList.size(); ++i) { QFileInfo file(this->recentGamesList.at(i)); if (not file.exists() or not (file.isFile() or file.isSymLink()) or not file.isReadable()) { this->recentGamesList.removeAt(i); --i; } } sett.endGroup(); this->appSize = sett.value(QString::fromLatin1("geometry/size"), QSize(740, 540)).toSize(); this->lastUpdateDate = sett.value(QString::fromLatin1("update/lastupdatedate"), QDate()).toDate(); this->updateFreq = static_cast( sett.value(QString::fromLatin1("update/updatefreq"), UpdateDaily).toInt() ); } void Settings::saveToDisk() { QSettings sett; sett.beginGroup(QString::fromLatin1("media")); sett.setValue(QString::fromLatin1("graphics"), this->enableGraphics); sett.setValue(QString::fromLatin1("sounds"), this->enableSoundEffects); sett.setValue(QString::fromLatin1("music"), this->enableMusic); sett.setValue(QString::fromLatin1("links"), this->enableLinks); sett.setValue(QString::fromLatin1("smoothImageScaling"), this->useSmoothScaling); sett.endGroup(); sett.beginGroup(QString::fromLatin1("colors")); sett.setValue(QString::fromLatin1("mainbg"), this->mainBgColor); sett.setValue(QString::fromLatin1("maintext"), this->mainTextColor); sett.setValue(QString::fromLatin1("bannerbg"), this->bannerBgColor); sett.setValue(QString::fromLatin1("bannertext"), this->bannerTextColor); sett.setValue(QString::fromLatin1("input"), this->inputColor); sett.setValue(QString::fromLatin1("underlinelinks"), this->underlineLinks); sett.setValue(QString::fromLatin1("highlightlinks"), this->highlightLinks); sett.setValue(QString::fromLatin1("unvisitedlinks"), this->unvisitedLinkColor); sett.setValue(QString::fromLatin1("hoveringlinks"), this->hoveringLinkColor); sett.setValue(QString::fromLatin1("clickedlinks"), this->clickedLinkColor); sett.endGroup(); sett.beginGroup(QString::fromLatin1("fonts")); sett.setValue(QString::fromLatin1("main"), this->mainFont.toString()); sett.setValue(QString::fromLatin1("fixed"), this->fixedFont.toString()); sett.setValue(QString::fromLatin1("serif"), this->serifFont.toString()); sett.setValue(QString::fromLatin1("sans"), this->sansFont.toString()); sett.setValue(QString::fromLatin1("script"), this->scriptFont.toString()); sett.setValue(QString::fromLatin1("typewriter"), this->writerFont.toString()); sett.setValue(QString::fromLatin1("input"), this->inputFont.toString()); sett.setValue(QString::fromLatin1("useMainFontForInput"), this->useMainFontForInput); sett.endGroup(); sett.beginGroup(QString::fromLatin1("misc")); sett.setValue(QString::fromLatin1("ioSafetyLevelRead"), this->ioSafetyLevelRead); sett.setValue(QString::fromLatin1("ioSafetyLevelWrite"), this->ioSafetyLevelWrite); sett.setValue(QString::fromLatin1("tads2encoding"), this->tads2Encoding); sett.setValue(QString::fromLatin1("pasteondoubleclick"), this->pasteOnDblClk); sett.setValue(QString::fromLatin1("softscrolling"), this->softScrolling); sett.setValue(QString::fromLatin1("askforfileatstart"), this->askForGameFile); sett.setValue(QString::fromLatin1("confirmrestartgame"), this->confirmRestartGame); sett.setValue(QString::fromLatin1("confirmquitgame"), this->confirmQuitGame); sett.setValue(QString::fromLatin1("lastFileOpenDir"), this->lastFileOpenDir); sett.endGroup(); sett.beginGroup(QString::fromLatin1("recent")); sett.setValue(QString::fromLatin1("games"), this->recentGamesList); sett.endGroup(); sett.setValue(QString::fromLatin1("geometry/size"), qWinGroup->size()); sett.setValue(QString::fromLatin1("update/lastupdatedate"), this->lastUpdateDate); sett.setValue(QString::fromLatin1("update/updatefreq"), this->updateFreq); sett.sync(); } qtads-2.1.7/src/settings.h000066400000000000000000000040721265017072300154430ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef SETTINGS_H #define SETTINGS_H #include #include "sysfont.h" class Settings { public: void loadFromDisk(); void saveToDisk(); bool enableGraphics; bool enableSoundEffects; bool enableMusic; bool enableLinks; bool useSmoothScaling; QColor mainTextColor; QColor mainBgColor; QColor bannerTextColor; QColor bannerBgColor; QColor inputColor; bool underlineLinks; bool highlightLinks; QColor unvisitedLinkColor; QColor hoveringLinkColor; QColor clickedLinkColor; CHtmlSysFontQt mainFont; CHtmlSysFontQt fixedFont; CHtmlSysFontQt serifFont; CHtmlSysFontQt sansFont; CHtmlSysFontQt scriptFont; CHtmlSysFontQt writerFont; CHtmlSysFontQt inputFont; bool useMainFontForInput; int ioSafetyLevelRead; int ioSafetyLevelWrite; QByteArray tads2Encoding; bool pasteOnDblClk; bool softScrolling; bool askForGameFile; bool confirmRestartGame; bool confirmQuitGame; QString lastFileOpenDir; QStringList recentGamesList; static const int recentGamesCapacity = 10; QSize appSize; QDate lastUpdateDate; enum UpdateFreq { UpdateOnEveryStart, UpdateDaily, UpdateWeekly, UpdateNever } updateFreq; }; #endif qtads-2.1.7/src/sysfont.h000066400000000000000000000064121265017072300153100ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef SYSFONT_H #define SYSFONT_H #include #include #include #include "htmlsys.h" #include "config.h" /* Tads HTML layer class whose interface needs to be implemented by the * interpreter. * * See htmltads/htmlsys.h and htmltads/notes/porting.htm for information * about this class. */ class CHtmlSysFontQt: public QFont, public CHtmlSysFont { private: QColor fColor; QColor fBgColor; public: // When color() is a valid color (QColor::isValid()) it should be used as // the foreground color when drawing text in this font. const QColor& color() const { return this->fColor; } HTML_color_t htmlColor() const { return HTML_make_color(this->fColor.red(), this->fColor.green(), this->fColor.blue()); } void color( HTML_color_t color ) { this->fColor = QColor(HTML_color_red(color), HTML_color_green(color), HTML_color_blue(color)); } // When bgColor() is a valid color (QColor::isValid()) it should be used as // the background color when drawing text in this font. const QColor& bgColor() const { return this->fBgColor; } HTML_color_t htmlBgColor() const { return HTML_make_color(this->fBgColor.red(), this->fBgColor.green(), this->fBgColor.blue()); } void bgColor( HTML_color_t color ) { this->fBgColor = QColor(HTML_color_red(color), HTML_color_green(color), HTML_color_blue(color)); } bool operator ==( const CHtmlSysFontQt& f ) const { return QFont::operator ==(f) and this->fColor == f.fColor and this->fBgColor == f.fBgColor; } CHtmlSysFontQt& operator =( const QFont& f ) { QFont::operator =(f); return *this; } // Set the font descriptor. void set_font_desc( const CHtmlFontDesc* src ) { desc_.copy_from(src); // Clear the explicit-face-name flag, since this is important only // when looking up a font. desc_.face_set_explicitly = false; } // // CHtmlSysFont interface implementation. // void get_font_metrics( CHtmlFontMetrics* m ) override { //qDebug() << Q_FUNC_INFO << "called"; QFontMetrics tmp(*this); m->ascender_height = tmp.ascent(); m->descender_height = tmp.descent(); m->total_height = tmp.height(); } int is_fixed_pitch() override { return QFontInfo(*this).fixedPitch(); } int get_em_size() override { return QFontInfo(*this).pixelSize(); } }; #endif qtads-2.1.7/src/sysframe.cc000066400000000000000000001123511265017072300155720ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include #include #include #include #include #include #include #include #include #include "qtadshostifc.h" #include "settings.h" #include "syswinaboutbox.h" #include "syswininput.h" #include "gameinfodialog.h" #include "qtadssound.h" #include "htmlprs.h" #include "htmlfmt.h" #include "htmlrf.h" #include "htmltxar.h" #include "vmmaincn.h" void CHtmlSysFrameQt::fCtxGetIOSafetyLevel( void*, int* read, int* write ) { Q_ASSERT(qFrame != 0); if (read != 0) { *read = qFrame->fSettings->ioSafetyLevelRead; } if (write != 0) { *write = qFrame->fSettings->ioSafetyLevelWrite; } } CHtmlSysFrameQt::CHtmlSysFrameQt( int& argc, char* argv[], const char* appName, const char* appVersion, const char* orgName, const char* orgDomain ) : QApplication(argc, argv), fGameWin(0), fGameRunning(false), fTads3(true), fReformatPending(false), fNonStopMode(false) { //qDebug() << Q_FUNC_INFO; Q_ASSERT(qFrame == 0); this->setApplicationName(QString::fromLatin1(appName)); this->setApplicationVersion(QString::fromLatin1(appVersion)); this->setOrganizationName(QString::fromLatin1(orgName)); this->setOrganizationDomain(QString::fromLatin1(orgDomain)); // Load our persistent settings. this->fSettings = new Settings; this->fSettings->loadFromDisk(); // Initialize the input color with the user-configured one. The game is // free to change the input color later on. const QColor& tmpCol = this->fSettings->inputColor; this->fInputColor = HTML_make_color(tmpCol.red(), tmpCol.green(), tmpCol.blue()); this->fParser = 0; this->fFormatter = 0; // Clear the TADS appctx; all unused fields must be 0. memset(&this->fAppctx, 0, sizeof(this->fAppctx)); this->fAppctx.get_io_safety_level = CHtmlSysFrameQt::fCtxGetIOSafetyLevel; // Create the TADS host and client application interfaces. this->fHostifc = new QTadsHostIfc(&this->fAppctx); this->fClientifc = new CVmMainClientConsole; // Set our global pointer. qFrame = this; // Create our main application window. this->fMainWin = new CHtmlSysWinGroupQt; this->fMainWin->setWindowTitle(QString::fromLatin1(appName)); this->fMainWin->updateRecentGames(); // Automatically quit the application when the last window has closed. connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit())); // We're the main HTML TADS frame object. CHtmlSysFrame::set_frame_obj(this); // Set application window icon, unless we're on OS X where the bundle // icon is used. #ifndef Q_OS_MAC this->setWindowIcon(QIcon(QString::fromLatin1(":/qtads_48x48.png"))); #endif } CHtmlSysFrameQt::~CHtmlSysFrameQt() { //qDebug() << Q_FUNC_INFO; Q_ASSERT(qFrame != 0); // Delete the "about this game" box. this->fMainWin->deleteAboutBox(); // We're no longer the main frame object. CHtmlSysFrame::set_frame_obj(0); // Save our persistent settings. this->fSettings->saveToDisk(); // We're being destroyed, so our global pointer is no longer valid. qFrame = 0; // Delete HTML banners, orphaned banners and main game window. while (not this->fBannerList.isEmpty()) { delete this->fBannerList.takeLast(); } while (not this->fOrhpanBannerList.isEmpty()) { os_banner_delete(this->fOrhpanBannerList.takeLast()); } if (this->fGameWin != 0) { delete this->fGameWin; } // Release the parser and delete our parser and formatter. if (this->fFormatter != 0) { this->fFormatter->release_parser(); } if (this->fParser != 0) { delete this->fParser; } if (this->fFormatter != 0) { delete this->fFormatter; } // Delete cached fonts. while (not this->fFontList.isEmpty()) { delete this->fFontList.takeLast(); } // Delete our TADS application interfaces and settings. delete this->fClientifc; delete this->fHostifc; delete this->fSettings; delete this->fMainWin; } void CHtmlSysFrameQt::fRunGame() { if (this->fNextGame.isEmpty()) { // Nothing to run. return; } while (not this->fNextGame.isEmpty()) { QFileInfo finfo(QFileInfo(this->fNextGame).absoluteFilePath()); this->fNextGame.clear(); // Change to the game file's directory. QDir::setCurrent(finfo.absolutePath()); // Run the appropriate TADS VM. int vmType = vm_get_game_type(qStrToFname(finfo.absoluteFilePath()).constData(), 0, 0, 0, 0); if (vmType == VM_GGT_TADS2 or vmType == VM_GGT_TADS3) { // Delete all HTML and orphaned banners. while (not this->fOrhpanBannerList.isEmpty()) { os_banner_delete(this->fOrhpanBannerList.takeLast()); } while (not this->fBannerList.isEmpty()) { delete this->fBannerList.takeLast(); } // Delete the current main HTML game window. if (this->fGameWin != 0) { delete this->fGameWin; } // Delete current HTML parser and main game window formatter. if (this->fFormatter != 0) { this->fFormatter->release_parser(); } if (this->fParser != 0) { delete this->fParser; } if (this->fFormatter != 0) { delete this->fFormatter; } // Delete cached fonts. while (not this->fFontList.isEmpty()) { delete this->fFontList.takeLast(); } // Recreate them. this->fParser = new CHtmlParser(true); this->fFormatter = new CHtmlFormatterInput(this->fParser); // Tell the resource finder about our appctx. this->fFormatter->get_res_finder()->init_appctx(&this->fAppctx); this->fGameWin = new CHtmlSysWinInputQt(this->fFormatter, qWinGroup->centralWidget()); this->fGameWin->resize(qWinGroup->centralWidget()->size()); this->fGameWin->show(); this->fGameWin->setFocus(); // Set the application's window title to contain the filename of // the game we're running or the game's name as at appears in the // gameinfo. The game is free to change that later on. const QString& titleStr = GameInfoDialog::getMetaInfo(qStrToFname(finfo.absoluteFilePath())).plainGameName; if (titleStr.simplified().isEmpty()) { // The game doesn't provide a game name. Just use the filename. #ifdef Q_OS_MAC // Just use the filename on OS X. Seems to be the norm there. qWinGroup->setWindowTitle(finfo.fileName()); #else // On all other systems, also append the application name. qWinGroup->setWindowTitle(finfo.fileName() + QString::fromLatin1(" - ") + qFrame->applicationName()); #endif } else { qWinGroup->setWindowTitle(titleStr.trimmed()); } // Add the game file to our "recent games" list. QStringList& gamesList = this->fSettings->recentGamesList; int recentIdx = gamesList.indexOf(finfo.absoluteFilePath()); if (recentIdx > 0) { // It's already in the list and it's not the first item. Make // it the first item so that it becomes the most recent entry. gamesList.move(recentIdx, 0); } else if (recentIdx < 0) { // We didn't find it in the list by absoluteFilePath(). Try to // find it by canonicalFilePath() instead. This way, we avoid // listing the same game twice if the user opened it through a // different path (through a symlink that leads to the same // file, for instance.) bool found = false; const QString& canonPath = finfo.canonicalFilePath(); for (recentIdx = 0; recentIdx < gamesList.size() and not found; ++recentIdx) { if (QFileInfo(gamesList.at(recentIdx)).canonicalFilePath() == canonPath) { found = true; } } if (found) { gamesList.move(recentIdx - 1, 0); } else { // It's not in the list. Prepend it as the most recent item // and, if the list is full, delete the oldest one. if (gamesList.size() >= this->fSettings->recentGamesCapacity) { gamesList.removeLast(); } gamesList.prepend(finfo.absoluteFilePath()); } } this->fMainWin->updateRecentGames(); this->fSettings->saveToDisk(); qWinGroup->updatePasteAction(); // Run the appropriate VM. this->fGameRunning = true; this->fGameFile = finfo.absoluteFilePath(); emit gameStarting(); if (vmType == VM_GGT_TADS2) { this->fRunT2Game(finfo.absoluteFilePath()); } else { this->fRunT3Game(finfo.absoluteFilePath()); } this->fGameRunning = false; this->fGameFile.clear(); emit gameHasQuit(); // Flush any pending output and cancel all sounds and animations. this->flush_txtbuf(true, false); this->fFormatter->cancel_sound(HTML_Attrib_invalid, 0.0, false, false); this->fFormatter->cancel_playback(); // Display a "game has ended" message. We use HTML, so Make sure // the parser is in markup mode. this->fParser->obey_markups(true); QString endMsg(QString::fromLatin1("


(The game has ended.)

")); this->display_output(endMsg.toUtf8().constData(), endMsg.length()); this->flush_txtbuf(true, false); } else { QMessageBox::critical(this->fMainWin, tr("Open Game"), finfo.fileName() + tr(" is not a TADS game file.")); } } // Reset application window title. qWinGroup->setWindowTitle(qFrame->applicationName()); } void CHtmlSysFrameQt::fRunT2Game( const QString& fname ) { // We'll be loading a T2 game. this->fTads3 = false; // T2 requires argc/argv style arguments. char argv0[] = "qtads"; char* argv1 = new char[qStrToFname(fname).size() + 1]; strcpy(argv1, qStrToFname(fname).constData()); char* argv[2] = {argv0, argv1}; // We always use .sav as the extension for T2 save files. char savExt[] = "sav"; // Start the T2 VM. trdmain(2, argv, &this->fAppctx, savExt); delete[] argv1; } void CHtmlSysFrameQt::fRunT3Game( const QString& fname ) { // vm_run_image_params doesn't copy the filename string but stores // the pointer directly. We therefore need to hold a reference to // the data so that it won't go out of scope. const QByteArray& fnameData = qStrToFname(fname); vm_run_image_params params(this->fClientifc, this->fHostifc, fnameData.constData()); this->fTads3 = true; vm_run_image(¶ms); } #ifdef Q_OS_MAC #include bool CHtmlSysFrameQt::event( QEvent* e ) { // We only handle the FileOpen event. if (e->type() != QEvent::FileOpen) { return QApplication::event(e); } return qWinGroup->handleFileOpenEvent(static_cast(e)); } #endif void CHtmlSysFrameQt::entryPoint( QString gameFileName ) { // Restore the application's size. this->fMainWin->resize(this->fSettings->appSize); this->fMainWin->show(); // Do an online update check. int daysRequired; switch (this->fSettings->updateFreq) { case Settings::UpdateOnEveryStart: daysRequired = 0; break; case Settings::UpdateDaily: daysRequired = 1; break; case Settings::UpdateWeekly: daysRequired = 7; break; default: daysRequired = -1; } if (not this->fSettings->lastUpdateDate.isValid()) { // Force update check. daysRequired = 0; } int daysPassed = this->fSettings->lastUpdateDate.daysTo(QDate::currentDate()); if (daysPassed >= daysRequired and daysRequired > -1) { this->fMainWin->checkForUpdates(); } // If a game file was specified, try to run it. if (not gameFileName.isEmpty()) { this->setNextGame(gameFileName); } } #if QT_VERSION < 0x040700 static int qtRuntimeVersion() { const QList verList(QByteArray(qVersion()).split('.')); if (verList.size() < 3) { // Something isn't right. The Qt version string should have // at least three fields. return 0; } bool ok; int major = verList.at(0).toInt(&ok); if (not ok) { return 0; } int minor = verList.at(1).toInt(&ok); if (not ok) { return 0; } int patch = verList.at(2).toInt(&ok); if (not ok) { return 0; } return QT_VERSION_CHECK(major, minor, patch); } #endif CHtmlSysFontQt* CHtmlSysFrameQt::createFont( const CHtmlFontDesc* font_desc ) { //qDebug() << Q_FUNC_INFO; Q_ASSERT(font_desc != 0); CHtmlFontDesc newFontDesc = *font_desc; CHtmlSysFontQt newFont; QFont::StyleStrategy strat; #if QT_VERSION >= 0x040700 // We're building with a recent enough Qt; use ForceIntegerMetrics directly. strat = QFont::StyleStrategy(QFont::PreferOutline | QFont::PreferQuality | QFont::ForceIntegerMetrics); #else // We're building with a Qt version that does not offer ForceIntegerMetrics. // If we're running on a recent enough Qt, use the ForceIntegerMetrics enum // value directly. if (qtRuntimeVersion() >= 0x040700) { strat = QFont::StyleStrategy(QFont::PreferOutline | QFont::PreferQuality | 0x0400); } else { strat = QFont::StyleStrategy(QFont::PreferOutline | QFont::PreferQuality); } #endif newFont.setStyleStrategy(strat); // Use the weight they provided (we may change this if a weight modifier is // specified). if (newFontDesc.weight < 400) { newFont.setWeight(QFont::Light); } else if (newFontDesc.weight < 700) { newFont.setWeight(QFont::Normal); } else if (newFontDesc.weight < 900) { newFont.setWeight(QFont::Bold); } else { newFont.setWeight(QFont::Black); } // Apply the specific font attributes. newFont.setItalic(newFontDesc.italic); newFont.setUnderline(newFontDesc.underline); newFont.setStrikeOut(newFontDesc.strikeout); // Assume that we'll use the base point size of the default text font (the // main proportional font) as the basis of the size. If we find a specific // named parameterized font name, we'll change to use the size as specified // in the player preferences for that parameterized font; but if we have a // particular name given, the player has no way to directly specify the // base size for that font, so the best we can do is use the default text // font size for guidance. int base_point_size = this->fSettings->mainFont.pointSize(); // System font name that is to be used. QString fontName; // If a face name is listed, try to find the given face in the system. // Note that we wait until after setting all of the basic attributes (in // particular, weight, color, and styles) before dealing with the face // name; this is to allow parameterized face names to override the basic // attributes. For example, the "TADS-Input" font allows the player to // specify color, bold, and italic styles in addition to the font name. if (newFontDesc.face[0] != 0) { // The face name field can contain multiple face names separated by // commas. We split them into a list and try each one individualy. bool matchFound = false; const QStringList& strList = QString(QString::fromLatin1(newFontDesc.face)) .split(QChar::fromLatin1(','), QString::SkipEmptyParts); for (int i = 0; i < strList.size() and not matchFound; ++i) { const QString& s = strList.at(i).simplified().toLower(); if (s == QString::fromLatin1(HTMLFONT_TADS_SERIF).toLower()) { fontName = this->fSettings->serifFont.family(); base_point_size = this->fSettings->serifFont.pointSize(); matchFound = true; } else if (s == QString::fromLatin1(HTMLFONT_TADS_SANS).toLower()) { fontName = this->fSettings->sansFont.family(); base_point_size = this->fSettings->sansFont.pointSize(); matchFound = true; } else if (s == QString::fromLatin1(HTMLFONT_TADS_SCRIPT).toLower()) { fontName = this->fSettings->scriptFont.family(); base_point_size = this->fSettings->scriptFont.pointSize(); matchFound = true; } else if (s == QString::fromLatin1(HTMLFONT_TADS_TYPEWRITER).toLower()) { fontName = this->fSettings->writerFont.family(); base_point_size = this->fSettings->writerFont.pointSize(); matchFound = true; } else if (s == QString::fromLatin1(HTMLFONT_TADS_INPUT).toLower()) { fontName = this->fSettings->inputFont.family(); base_point_size = this->fSettings->inputFont.pointSize(); if (newFontDesc.face_set_explicitly) { newFont.setBold(this->fSettings->inputFont.bold()); newFont.setItalic(this->fSettings->inputFont.italic()); newFontDesc.color = HTML_COLOR_INPUT; newFont.color(HTML_COLOR_INPUT); } else if (newFontDesc.default_color) { newFontDesc.color = HTML_COLOR_INPUT; newFont.color(HTML_COLOR_INPUT); } matchFound = true; } else if (s == QString::fromLatin1("qtads-grid")) { // "qtads-grid" is an internal face name; it means we should // return a font suitable for a text grid banner. fontName = this->fSettings->fixedFont.family(); base_point_size = this->fSettings->fixedFont.pointSize(); matchFound = true; } else { newFont.setFamily(s); if (newFont.exactMatch()) { matchFound = true; fontName = s; } } } // If we didn't find a match, use the main game font as set by // the user. if (not matchFound) { fontName = this->fSettings->mainFont.family(); } // Apply characteristics only if the face wasn't specified. } else { // See if fixed-pitch is desired. if (newFontDesc.fixed_pitch) { // Use prefered monospaced font. fontName = this->fSettings->fixedFont.family(); base_point_size = this->fSettings->fixedFont.pointSize(); } else { // Use prefered proportional font. fontName = this->fSettings->mainFont.family(); base_point_size = this->fSettings->mainFont.pointSize(); } // See if serifs are desired for a variable-pitch font. if (not newFontDesc.serif and not newFontDesc.fixed_pitch) { fontName = this->fSettings->serifFont.family(); base_point_size = this->fSettings->serifFont.pointSize(); } // See if emphasis (EM) is desired - render italic if so. if (newFontDesc.pe_em) { newFont.setItalic(true); } // See if strong emphasis (STRONG) is desired - render bold if so. if (newFontDesc.pe_strong) { newFontDesc.weight = 700; newFont.setWeight(QFont::Bold); } // If we're in an address block, render in italics. if (newFontDesc.pe_address) { newFont.setItalic(true); } // See if this is a defining instance (DFN) - render in italics. if (newFontDesc.pe_dfn) { newFont.setItalic(true); } // See if this is sample code (SAMP), keyboard code (KBD), or a // variable (VAR) - render these in a monospaced roman font if so. if (newFontDesc.pe_samp or newFontDesc.pe_kbd or newFontDesc.pe_var) { // Render KBD in bold. if (newFontDesc.pe_kbd) { newFont.setWeight(QFont::Bold); } fontName = this->fSettings->fixedFont.family(); base_point_size = this->fSettings->fixedFont.pointSize(); } // See if this is a citation (CITE) - render in italics if so. if (newFontDesc.pe_cite) { newFont.setItalic(true); } } newFont.setFamily(fontName); // Note the HTML SIZE parameter requested - if this is zero, it indicates // that we want to use a specific point size instead. int htmlsize = newFontDesc.htmlsize; // If a valid HTML size is specified, map it to a point size. int pointsize; if (htmlsize >= 1 and htmlsize <= 7) { static const int size_pct[] = { 60, 80, 100, 120, 150, 200, 300 }; // An HTML size is specified -- if a BIG or SMALL attribute is present, // bump the HTML size by 1 in the appropriate direction, if we're not // already at a limit. if (newFontDesc.pe_big and htmlsize < 7) { ++htmlsize; } else if (newFontDesc.pe_small && htmlsize > 1) { --htmlsize; } // Adjust for the HTML SIZE setting. There are 7 possible size // settings, numbered 1 through 7. Adjust the font size by applying a // scaling factor for the font sizes. Our size factor table is in // percentages, so multiply by the size factor, add in 50 so that we // round properly, and divide by 100 to get the new size. pointsize = ((base_point_size * size_pct[htmlsize - 1]) + 50) / 100; } else { // There's no HTML size - use the explicit point size provided. pointsize = newFontDesc.pointsize; } newFont.setPointSize(pointsize > 0 ? pointsize : base_point_size); if (not newFontDesc.default_color) { newFont.color(newFontDesc.color); } if (not newFontDesc.default_bgcolor) { newFont.bgColor(newFontDesc.bgcolor); } // Check whether a matching font is already in our cache. for (int i = 0; i < this->fFontList.size(); ++i) { if (*this->fFontList.at(i) == newFont) { return this->fFontList[i]; } } //qDebug() << "Font not found in cache; creating new font:" << newFont // << "\nFonts so far:" << this->fFontList.size() + 1; // There was no match in our cache. Create a new font and store it in our // cache. CHtmlSysFontQt* font = new CHtmlSysFontQt(newFont); font->set_font_desc(&newFontDesc); this->fFontList.append(font); return font; } void CHtmlSysFrameQt::adjustBannerSizes() { if (this->fGameWin == 0) { return; } // Start with the main game window. Its area can never exceed the // application's central frame. QRect siz(qWinGroup->centralWidget()->rect()); this->fGameWin->calcChildBannerSizes(siz); } void CHtmlSysFrameQt::reformatBanners( bool showStatus, bool freezeDisplay, bool resetSounds ) { if (this->fGameWin == 0) { return; } // Recalculate the banner layout, in case any of the underlying units (such // as the default font size) changed. this->adjustBannerSizes(); // Always reformat the main panel window. this->fGameWin->doReformat(showStatus, freezeDisplay, resetSounds); // Reformat each banner window. for (int i = 0; i < this->fBannerList.size(); ++i) { this->fBannerList.at(i)->doReformat(showStatus, freezeDisplay, false); } } void CHtmlSysFrameQt::pruneParseTree() { static int checkCount = 0; // If there's a reformat pending, perform it. if (this->fReformatPending) { this->fReformatPending = false; this->reformatBanners(true, true, false); } // Skip this entirely most of the time; only check every so often, so that // we don't waste a lot of time doing this too frequently. ++checkCount; if (checkCount < 10) { return; } checkCount = 0; // Check to see if we're consuming too much memory - if not, there's // nothing we need to do here. if (this->fParser->get_text_array()->get_mem_in_use() < 65536) { return; } // Perform the pruning and reformat all banners. this->fParser->prune_tree(65536 / 2); this->reformatBanners(false, true, false); } void CHtmlSysFrameQt::notifyPreferencesChange( const Settings* sett ) { // Bail out if we currently don't have an active formatter. if (this->fFormatter == 0) { return; } // If digital sounds are now turned off, cancel sound playback in the // effects layers if (not sett->enableSoundEffects) { this->fFormatter->cancel_sound(HTML_Attrib_ambient, 0.0, false, false); this->fFormatter->cancel_sound(HTML_Attrib_bgambient, 0.0, false, false); this->fFormatter->cancel_sound(HTML_Attrib_foreground, 0.0, false, false); } // If background music is now turned off, cancel playback in the music layer. if (not sett->enableMusic) { this->fFormatter->cancel_sound(HTML_Attrib_background, 0.0, false, false); } // Links in the main game window are not invalidated for some reason, so we // invalidate them manually here. const QRect& widgetRect = this->fGameWin->widget()->visibleRegion().boundingRect(); CHtmlRect documentRect(widgetRect.x(), widgetRect.y(), widgetRect.x() + widgetRect.width(), widgetRect.y() + widgetRect.height()); this->fFormatter->inval_links_on_screen(&documentRect); // Reformat everything so that changes in fonts/colors/etc become visible // immediately. qFrame->reformatBanners(true, true, false); // Change the text cursor's height according to the new input font's height. qFrame->gameWindow()->setCursorHeight(QFontMetrics(sett->inputFont).height()); } void CHtmlSysFrame::kill_process() { quitSound(); delete qFrame; ::exit(0); } int CHtmlSysFrame::eof_on_console() { return qWinGroup->wantsToQuit(); } void CHtmlSysFrameQt::flush_txtbuf( int fmt, int immediate_redraw ) { // Flush and clear the buffer. this->fParser->parse(&this->fBuffer, qWinGroup); this->fBuffer.clear(); // If desired, run the parsed source through the formatter and display it. if (fmt) { this->fGameWin->do_formatting(false, false, false); } // Also flush all banner windows. for (int i = 0; i < this->fBannerList.size(); ++i) { this->fBannerList.at(i)->get_formatter()->flush_txtbuf(fmt); } // If desired, immediately update the display. if (immediate_redraw) { this->fMainWin->centralWidget()->update(); } } void CHtmlSysFrameQt::start_new_page() { //qDebug() << Q_FUNC_INFO; // Don't bother if the game is quitting. if (not this->fGameRunning) { return; } // Flush any pending output. this->flush_txtbuf(true, false); // Cancel all animations. this->fFormatter->cancel_playback(); // Tell the parser to clear the page. this->fParser->clear_page(); // Remove all banners. The formatter will do the right thing and only // remove banners that have not been created programmatically (like those // created with tags.) this->fFormatter->remove_all_banners(false); // Notify the main game window that we're clearing the page. this->fGameWin->notify_clear_contents(); // Reformat the window for the new blank page. this->reformatBanners(false, false, true); } void CHtmlSysFrameQt::set_nonstop_mode( int flag ) { //qDebug() << Q_FUNC_INFO; this->fNonStopMode = flag; } void CHtmlSysFrameQt::display_output( const textchar_t *buf, size_t len ) { //qDebug() << Q_FUNC_INFO; // Just add the new text to our buffer. Append it as-is if we're running // a TADS 3 game, since it's already UTF-8 encoded. if (this->fTads3) { this->fBuffer.append(buf, len); } else { // TADS 2 does not use UTF-8; use the encoding from our settings. QTextCodec* codec = QTextCodec::codecForName(this->fSettings->tads2Encoding); this->fBuffer.append(codec->toUnicode(buf, len).toUtf8().constData()); } } int CHtmlSysFrameQt::check_break_key() { //qDebug() << Q_FUNC_INFO; // TODO: We don't check for any such shortcut yet. return false; } int CHtmlSysFrameQt::get_input( textchar_t* buf, size_t bufsiz ) { //qDebug() << Q_FUNC_INFO; if (this->get_input_timeout(buf, bufsiz, 0, false) == OS_EVT_EOF) { return false; } return true; } int CHtmlSysFrameQt::get_input_timeout( textchar_t* buf, size_t buflen, unsigned long timeout, int use_timeout ) { //qDebug() << Q_FUNC_INFO; // Flush and prune before input. this->flush_txtbuf(true, false); this->pruneParseTree(); if (use_timeout) { bool timedOut = false; this->fGameWin->getInput(buf, buflen, timeout, true, &timedOut); if (timedOut) { return OS_EVT_TIMEOUT; } } else { this->fGameWin->getInput(buf, buflen); } // Return EOF if we're quitting the game. if (not this->fGameRunning) { return OS_EVT_EOF; } return OS_EVT_LINE; } void CHtmlSysFrameQt::get_input_cancel( int reset ) { //qDebug() << Q_FUNC_INFO; this->fGameWin->cancelInput(reset); } int CHtmlSysFrameQt::get_input_event( unsigned long timeout, int use_timeout, os_event_info_t* info ) { //qDebug() << Q_FUNC_INFO << "use_timeout:" << use_timeout; // Flush and prune before input. this->flush_txtbuf(true, false); this->pruneParseTree(); // Get the input. bool timedOut = false; int res = this->fGameWin->getKeypress(timeout, use_timeout, &timedOut); // Return EOF if we're quitting the game. if (not this->fGameRunning) { return OS_EVT_EOF; } // If the timeout expired, tell the caller. if (use_timeout and timedOut) { return OS_EVT_TIMEOUT; } if (res == -2) { // It was an HREF event (user clicked a hyperlink). Get the last // pending HREF event. // For TADS 3, we use the result as-is; it's already in UTF-8. For TADS 2, // we will need to use the prefered encoding. if (this->fTads3) { strncpy(info->href, this->fGameWin->pendingHrefEvent().toUtf8().constData(), sizeof(info->href) - 1); } else { QTextCodec* codec = QTextCodec::codecForName(this->fSettings->tads2Encoding); strncpy(info->href, codec->fromUnicode(this->fGameWin->pendingHrefEvent()).constData(), sizeof(info->href) - 1); } info->href[sizeof(info->href) - 1] = '\0'; return OS_EVT_HREF; } else if (res == 0) { // It was an extended character; call again to get the // extended code. info->key[0] = 0; info->key[1] = this->fGameWin->getKeypress(); } else { // A normal character. Return it as is. info->key[0] = res; info->key[1] = 0; } // Tell the caller it was a key-event. return OS_EVT_KEY; } // FIXME: OS_EVT_HREF isn't handled and shouldn't be allowed here. textchar_t CHtmlSysFrameQt::wait_for_keystroke( int pause_only ) { //qDebug() << Q_FUNC_INFO; static int pendingCmd = 0; int ret; // If we have a pending keystroke command from out last call, return it // now. if (pendingCmd != 0) { ret = pendingCmd; pendingCmd = 0; //qDebug() << ret; return ret; } QLabel moreText(pause_only ? tr("*** MORE *** [press a key to continue]") : tr("Please press a key")); // Display a permanent QLabel instead of a temporary message. This allows // other status bar messages (like when hovering over hyperlinks) to // temporary remove the MORE text instead of replacing it. moreText.setFrameStyle(QFrame::NoFrame | QFrame::Plain); moreText.setLineWidth(0); moreText.setContentsMargins(0, 0, 0, 0); qWinGroup->statusBar()->setUpdatesEnabled(false); qWinGroup->statusBar()->addWidget(&moreText); qWinGroup->statusBar()->setUpdatesEnabled(true); // Get the input. os_event_info_t info; ret = this->get_input_event(0, false, &info); // Remove the status message. qWinGroup->statusBar()->setUpdatesEnabled(false); qWinGroup->statusBar()->removeWidget(&moreText); qWinGroup->statusBar()->setUpdatesEnabled(true); if (ret == OS_EVT_EOF) { pendingCmd = CMD_EOF; return 0; } if (ret == OS_EVT_KEY and info.key[0] == 0) { // It was an extended character. Prepare to return it on our next // call. pendingCmd = info.key[1]; return 0; } return info.key[0]; } void CHtmlSysFrameQt::pause_for_exit() { qDebug() << Q_FUNC_INFO; // Just wait for a keystroke and discard it. this->wait_for_keystroke(true); } void CHtmlSysFrameQt::pause_for_more() { //qDebug() << Q_FUNC_INFO; this->wait_for_keystroke(true); } void CHtmlSysFrameQt::dbg_print( const char* /*msg*/ ) { //qDebug() << "HTML TADS Debug message:" << msg; } CHtmlSysWin* CHtmlSysFrameQt::create_banner_window( CHtmlSysWin* parent, HTML_BannerWin_Type_t window_type, CHtmlFormatter* formatter, int where, CHtmlSysWin* other, HTML_BannerWin_Pos_t pos, unsigned long style ) { //qDebug() << Q_FUNC_INFO; //return 0; //qDebug() << "Creating new banner. parent:" << parent << "type:" << window_type << "where:" << where // << "other:" << other << "pos:" << pos << "style:" << style; // Create the banner window. CHtmlSysWinQt* banner = new CHtmlSysWinQt(formatter, qWinGroup->centralWidget()); CHtmlSysWinQt* castParent = static_cast(parent); CHtmlSysWinQt* castOther = static_cast(other); // Don't allow MORE mode in text grids. if (window_type == HTML_BANNERWIN_TEXTGRID) { style &= ~OS_BANNER_STYLE_MOREMODE; } // MORE mode implies auto vscroll. if (style & OS_BANNER_STYLE_MOREMODE) { style |= OS_BANNER_STYLE_AUTO_VSCROLL; } // If no parent was specified, it means that it's a child of the main // game window. if (parent == 0) { parent = castParent = this->fGameWin; } // If BEFORE or AFTER is requested but 'other' isn't a child of the // parent, we must behave as if OS_BANNER_LAST were specified. if (where == OS_BANNER_BEFORE or where == OS_BANNER_AFTER) { Q_ASSERT(other != 0); if (castOther->parentBanner() != parent) { where = OS_BANNER_LAST; } } // Add the banner and store it in our list. castParent->addBanner(banner, window_type, where, castOther, pos, style); this->fBannerList.append(banner); return banner; } void CHtmlSysFrameQt::orphan_banner_window( CHtmlFormatterBannerExt* banner ) { //qDebug() << Q_FUNC_INFO; this->fOrhpanBannerList.append(banner); } CHtmlSysWin* CHtmlSysFrameQt::create_aboutbox_window( CHtmlFormatter* formatter ) { //qDebug() << Q_FUNC_INFO; return this->fMainWin->createAboutBox(formatter); } void CHtmlSysFrameQt::remove_banner_window( CHtmlSysWin* win ) { //qDebug() << Q_FUNC_INFO; // If this is the "about this game" box, ask the main window to delete it. if (win == this->fMainWin->aboutBox()) { this->fMainWin->deleteAboutBox(); return; } CHtmlSysWinQt* castWin = static_cast(win); // Before deleting it, remove it from our list and give keyboard focus to // its parent, if it has one. this->fBannerList.removeAll(castWin); if (castWin->parentBanner() != 0) { castWin->parentBanner()->setFocus(); } // Delete it and recalculate the banner layout. delete win; this->adjustBannerSizes(); } int CHtmlSysFrameQt::get_exe_resource( const textchar_t* /*resname*/, size_t /*resnamelen*/, textchar_t* /*fname_buf*/, size_t /*fname_buf_len*/, unsigned long* /*seek_pos*/, unsigned long* /*siz*/ ) { //qDebug() << Q_FUNC_INFO; //qDebug() << "resname:" << resname << "fname_buf:" << fname_buf << "seek_pos:" << seek_pos; return false; } qtads-2.1.7/src/sysframe.h000066400000000000000000000214721265017072300154370ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef SYSFRAME_H #define SYSFRAME_H #include #include "htmlsys.h" #include "config.h" /* Tads HTML layer class whose interface needs to be implemented by the * interpreter. * * See htmltads/htmlsys.h and htmltads/notes/porting.htm for information * about this class. */ class CHtmlSysFrameQt: public QApplication, public CHtmlSysFrame { Q_OBJECT private: // Preferences (fonts, colors, etc.) class Settings* fSettings; // Tads2 application container context. appctxdef fAppctx; // Tads3 host and client services interfaces. class CVmHostIfc* fHostifc; class CVmMainClientConsole* fClientifc; class CHtmlTextBuffer fBuffer; // Main window. class CHtmlSysWinGroupQt* fMainWin; // Main HTML window. class CHtmlSysWinInputQt* fGameWin; // Tads HTML parser. class CHtmlParser* fParser; // Tads HTML formatter for the main game window. class CHtmlFormatterInput* fFormatter; // List of banners. QList fBannerList; // List of orphaned banners. QList fOrhpanBannerList; // Fonts we created. We keep a list of every font we created since we're // responsible for deleting them when they're no longer needed. QList fFontList; // Are we currently executing a game? bool fGameRunning; // Filename of the game we're currently executing. QString fGameFile; // Is the game we're currently running (if we're running one) a Tads 3 // game? bool fTads3; // The game we should try to run after the current one ends. QString fNextGame; // Is there a reformat pending? bool fReformatPending; // Current input font color. HTML_color_t fInputColor; // Are we in non-stop mode? bool fNonStopMode; // Run the game file contained in fNextGame. void fRunGame(); void fRunT2Game( const QString& fname ); void fRunT3Game( const QString& fname ); #ifdef Q_OS_MAC protected: // On the Mac, dropping a file on our application icon will generate a // FileOpen event, so we override this to be able to handle it. bool event( QEvent* ) override; #endif // appctx callback for getting the current I/O safety level. static void fCtxGetIOSafetyLevel( void*, int* read, int* write ); signals: // Emitted just prior to starting a game. The game has not started yet // when this is emitted. void gameStarting(); // Emitted prior to quitting a game. The game has not quit yet when this // is emitted. void gameQuitting(); // Emitted after quiting a game. The game has already quit when this is // emitted. void gameHasQuit(); public slots: // Replacement for main(). We need this so that we can start the Tads VM // after the QApplication main event loop has started. void entryPoint( QString gameFileName ); public: CHtmlSysFrameQt( int& argc, char* argv[], const char* appName, const char* appVersion, const char* orgName, const char* orgDomain ); ~CHtmlSysFrameQt() override; class Settings* settings() { return this->fSettings; } class CHtmlSysWinInputQt* gameWindow() { return this->fGameWin; } const QList& bannerList() { return this->fBannerList; } HTML_color_t inputColor() { return this->fInputColor; } void inputColor( HTML_color_t color ) { this->fInputColor = color; } CHtmlSysFontQt* createFont( const CHtmlFontDesc* font_desc ); bool gameRunning() { return this->fGameRunning; } const QString& gameFile() { return this->fGameFile; } void setGameRunning( bool f ) { this->fGameRunning = f; if (f == false) { emit gameQuitting(); } } void setNextGame( const QString& fname ) { this->fNextGame = fname; // If no game is currently executing, run it now. Otherwise, end the // current game. if (not this->fGameRunning) { this->fRunGame(); } else { this->setGameRunning(false); } } bool tads3() { return this->fTads3; } bool nonStopMode() { return this->fNonStopMode; } // Recalculate and adjust the sizes of all HTML banners. void adjustBannerSizes(); // Reformat all HTML banners. void reformatBanners( bool showStatus, bool freezeDisplay, bool resetSounds ); // Schedule a reformat. void scheduleReformat() { this->fReformatPending = true; } // Prune the main window's parse tree, if we're using too much memory. // This should be called before getting user input; we'll check to see how // much memory the parse tree is taking up, and cut it down a bit if it's // too big. // TODO: Implement configurable max memory size. void pruneParseTree(); // Notify the application that preferences have changed. void notifyPreferencesChange( const class Settings* sett ); // Advance the event loop. void advanceEventLoop( QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents ) { // Guard against re-entrancy. static bool working = false; if (working) { return; } working = true; // DeferredDelete events need to be dispatched manually, since we don't // return to the main event loop while a game is running. #ifndef Q_OS_MAC // On OS X, this causes CPU utilization to go through the roof. Not // sure why. Disable this for now on OS X until further information // is available on this. this->sendPostedEvents(0, QEvent::DeferredDelete); #endif this->sendPostedEvents(); this->processEvents(flags); this->sendPostedEvents(); working = false; } // Advance the event loop with a timeout. void advanceEventLoop( QEventLoop::ProcessEventsFlags flags, int maxtime ) { // Guard against re-entrancy. static bool working = false; if (working) { return; } working = true; #ifndef Q_OS_MAC this->sendPostedEvents(0, QEvent::DeferredDelete); #endif this->sendPostedEvents(); this->processEvents(flags, maxtime); this->sendPostedEvents(); working = false; } // // CHtmlSysFrame interface implementation. // void flush_txtbuf( int fmt, int immediate_redraw ) override; class CHtmlParser* get_parser() override { return this->fParser; } void start_new_page() override; void set_nonstop_mode( int flag ) override; void display_output( const textchar_t* buf, size_t len ) override; int check_break_key() override; int get_input( textchar_t* buf, size_t bufsiz ) override; int get_input_timeout( textchar_t* buf, size_t buflen, unsigned long timeout, int use_timeout ) override; void get_input_cancel( int reset ) override; int get_input_event( unsigned long ms, int use_timeout, os_event_info_t* info ) override; textchar_t wait_for_keystroke( int pause_only ) override; void pause_for_exit() override; void pause_for_more() override; void dbg_print( const char* msg ) override; class CHtmlSysWin* create_banner_window( class CHtmlSysWin* parent, HTML_BannerWin_Type_t window_type, class CHtmlFormatter* formatter, int where, class CHtmlSysWin* other, HTML_BannerWin_Pos_t pos, unsigned long style ) override; void orphan_banner_window( class CHtmlFormatterBannerExt* banner ) override; CHtmlSysWin* create_aboutbox_window( class CHtmlFormatter* formatter ) override; void remove_banner_window( CHtmlSysWin* win ) override; int get_exe_resource( const textchar_t* resname, size_t resnamelen, textchar_t* fname_buf, size_t fname_buf_len, unsigned long* seek_pos, unsigned long* siz ) override; }; #endif qtads-2.1.7/src/sysimage.cc000066400000000000000000000117151265017072300155640ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include #include #include "qtadsimage.h" #include "sysimagejpeg.h" #include "sysimagepng.h" #include "sysimagemng.h" /* Helper routine. Loads any type of image from the specified offset inside * the given file and returns it. Has the same semantics as the various * CHtmlSysImage*::create_*() routines. The image type is specified in * 'imageType'. It has the same format as the list returned by * QImageReader::supportedImageFormats() (like "JPG", "PNG", etc.) */ static CHtmlSysResource* createImageFromFile( const CHtmlUrl* /*url*/, const textchar_t* filename, unsigned long seekpos, unsigned long filesize, CHtmlSysWin* /*win*/, const QString& imageType ) { //qDebug() << "Loading" << imageType << "image from" << filename << "at offset" << seekpos // << "with size" << filesize << "url:" << url->get_url(); // Check if the file exists and is readable. QFileInfo inf(fnameToQStr(filename)); if (not inf.exists() or not inf.isReadable()) { qWarning() << "ERROR:" << inf.filePath() << "doesn't exist or is unreadable"; return 0; } // Open the file and seek to the specified position. QFile file(inf.filePath()); if (not file.open(QIODevice::ReadOnly)) { qWarning() << "ERROR: Can't open file" << inf.filePath(); return 0; } if (not file.seek(seekpos)) { qWarning() << "ERROR: Can't seek in file" << inf.filePath(); file.close(); return 0; } CHtmlSysResource* image = NULL; // Better get an error at compile-time using static_cast rather than an // abort at runtime using dynamic_cast. QTadsImage* cast = NULL; CHtmlSysImageMngQt* mngCast = NULL; // Create an object of the appropriate class for the specified image type. // Also cast the object to a QTadsImage so we can loadFromData() later on. if (imageType == QString::fromLatin1("JPG") or imageType == QString::fromLatin1("JPEG")) { image = new CHtmlSysImageJpegQt; cast = static_cast(static_cast(image)); } else if (imageType == QString::fromLatin1("PNG")) { image = new CHtmlSysImagePngQt; cast = static_cast(static_cast(image)); } else if (imageType == QString::fromLatin1("MNG")) { image = new CHtmlSysImageMngQt; mngCast = static_cast(image); } else { qWarning() << "ERROR: Unknown image type" << imageType; file.close(); return NULL; } // Load the image data. const QByteArray& data(file.read(filesize)); file.close(); if (data.isEmpty() or static_cast(data.size()) < filesize) { qWarning() << "ERROR: Could not read" << filesize << "bytes from file" << inf.filePath(); delete image; return 0; } if (imageType == QString::fromLatin1("MNG")) { QBuffer* buf = new QBuffer(mngCast); buf->setData(data); buf->open(QBuffer::ReadOnly); mngCast->setFormat("MNG"); mngCast->setDevice(buf); mngCast->start(); } else if (not cast->loadFromData(data, imageType.toLatin1())) { qWarning() << "ERROR: Could not parse image data"; delete image; return 0; } return image; } CHtmlSysResource* CHtmlSysImageJpeg::create_jpeg( const CHtmlUrl* url, const textchar_t* filename, unsigned long seekpos, unsigned long filesize, CHtmlSysWin* win ) { return ::createImageFromFile(url, filename, seekpos, filesize, win, QString::fromLatin1("JPG")); } CHtmlSysResource* CHtmlSysImagePng::create_png( const CHtmlUrl* url, const textchar_t* filename, unsigned long seekpos, unsigned long filesize, CHtmlSysWin* win ) { return ::createImageFromFile(url, filename, seekpos, filesize, win, QString::fromLatin1("PNG")); } CHtmlSysResource* CHtmlSysImageMng::create_mng( const CHtmlUrl* url, const textchar_t* filename, unsigned long seekpos, unsigned long filesize, CHtmlSysWin* win ) { return ::createImageFromFile(url, filename, seekpos, filesize, win, QString::fromLatin1("MNG")); } qtads-2.1.7/src/sysimagejpeg.h000066400000000000000000000031751265017072300162750ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef SYSIMAGEJPEG_H #define SYSIMAGEJPEG_H #include "qtadsimage.h" #include "config.h" /* Tads HTML layer class whose interface needs to be implemented by the * interpreter. * * See htmltads/htmlsys.h and htmltads/notes/porting.htm for information * about this class. */ class CHtmlSysImageJpegQt: public QTadsImage, public CHtmlSysImageJpeg { public: // // CHtmlSysImageJpeg interface implementation. // void draw_image( CHtmlSysWin* win, CHtmlRect* pos, htmlimg_draw_mode_t mode ) override { QTadsImage::drawFromPaintEvent(win, pos, mode); } unsigned long get_width() const override { return QTadsImage::width(); } unsigned long get_height() const override { return QTadsImage::height(); } int map_palette( CHtmlSysWin*, int ) override { return false; } }; #endif qtads-2.1.7/src/sysimagemng.h000066400000000000000000000051001265017072300161170ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef SYSIMAGEMNG_H #define SYSIMAGEMNG_H #include #include #include "qtadsimage.h" #include "config.h" /* Tads HTML layer class whose interface needs to be implemented by the * interpreter. * * See htmltads/htmlsys.h and htmltads/notes/porting.htm for information * about this class. */ class CHtmlSysImageMngQt: public QMovie, public CHtmlSysImageMng { Q_OBJECT private: CHtmlSysImageDisplaySite* fDispSite; private slots: void updateDisplay( const QRect& rect ) { if (this->fDispSite != 0) { this->fDispSite->dispsite_inval(rect.x(), rect.y(), rect.width(), rect.height()); } } public: CHtmlSysImageMngQt() : fDispSite(0) { connect(this, SIGNAL(updated(QRect)), this, SLOT(updateDisplay(QRect))); } // // CHtmlSysImageMng interface implementation. // void set_display_site ( CHtmlSysImageDisplaySite* dispSite ) override { this->fDispSite = dispSite; } void cancel_playback() override { this->stop(); } void pause_playback() override { this->setPaused(true); } void resume_playback() override { this->setPaused(false); } void draw_image( CHtmlSysWin* win, CHtmlRect* pos, htmlimg_draw_mode_t mode ) override { QTadsImage(this->currentImage()).drawFromPaintEvent(win, pos, mode); } unsigned long get_width() const override { return this->frameRect().width(); } unsigned long get_height() const override { return this->frameRect().height(); } int map_palette( CHtmlSysWin*, int ) override { return false; } void notify_timer() override { qDebug() << Q_FUNC_INFO; } void notify_image_change( int, int, int, int ) override { qDebug() << Q_FUNC_INFO; } }; #endif qtads-2.1.7/src/sysimagepng.h000066400000000000000000000031701265017072300161270ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef SYSIMAGEPNG_H #define SYSIMAGEPNG_H #include "qtadsimage.h" #include "config.h" /* Tads HTML layer class whose interface needs to be implemented by the * interpreter. * * See htmltads/htmlsys.h and htmltads/notes/porting.htm for information * about this class. */ class CHtmlSysImagePngQt: public QTadsImage, public CHtmlSysImagePng { public: // // CHtmlSysImagePng interface implementation. // void draw_image( CHtmlSysWin* win, CHtmlRect* pos, htmlimg_draw_mode_t mode ) override { QTadsImage::drawFromPaintEvent(win, pos, mode); } unsigned long get_width() const override { return QTadsImage::width(); } unsigned long get_height() const override { return QTadsImage::height(); } int map_palette( CHtmlSysWin*, int ) override { return false; } }; #endif qtads-2.1.7/src/syssound.cc000066400000000000000000000234731265017072300156360ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include #ifndef Q_OS_ANDROID #include #endif #include "globals.h" #include "sysframe.h" #include "settings.h" #include "syssoundmidi.h" #include "syssoundwav.h" #include "syssoundogg.h" #include "syssoundmpeg.h" #ifndef Q_OS_ANDROID /* -------------------------------------------------------------------- * CHtmlSysSoundMidiQt */ CHtmlSysSoundMidiQt* CHtmlSysSoundMidiQt::fActiveMidi = 0; CHtmlSysSoundMidiQt::CHtmlSysSoundMidiQt( SDL_RWops* music ) : fRWops(music), fPlaying(false), fDone_func(0), fDone_func_ctx(0), fRepeats(0), fRepeatsWanted(1) { #if SDL_VERSION_ATLEAST(1,3,0) this->fMusic = Mix_LoadMUS_RW(this->fRWops, false); #else this->fMusic = Mix_LoadMUS_RW(this->fRWops); #endif } CHtmlSysSoundMidiQt::~CHtmlSysSoundMidiQt() { this->fRepeatsWanted = -1; if (CHtmlSysSoundMidiQt::fActiveMidi == this) { Mix_HaltMusic(); CHtmlSysSoundMidiQt::callback(); CHtmlSysSoundMidiQt::fActiveMidi = 0; } Mix_FreeMusic(this->fMusic); SDL_FreeRW(this->fRWops); } void CHtmlSysSoundMidiQt::callback() { //qDebug() << Q_FUNC_INFO; if (CHtmlSysSoundMidiQt::fActiveMidi == 0) { // No music is playing. Nothing to do. return; } CHtmlSysSoundMidiQt* curMidi = CHtmlSysSoundMidiQt::fActiveMidi; // If it's an infinite loop sound, or it has not reached the wanted repeat // count yet, play again. if ((curMidi->fRepeatsWanted == 0) or (curMidi->fRepeats < curMidi->fRepeatsWanted)) { Mix_PlayMusic(curMidi->fMusic, 0); ++curMidi->fRepeats; return; } CHtmlSysSoundMidiQt::fActiveMidi = 0; // Sound has repeated enough times, or it has been halted. In either case, // we need to invoke the TADS callback, if there is one. if (curMidi->fDone_func) { //qDebug() << "Invoking callback - repeats:" << curMidi->fRepeats; curMidi->fDone_func(curMidi->fDone_func_ctx, curMidi->fRepeats); } curMidi->fPlaying = false; } #endif int CHtmlSysSoundMidiQt::play_sound( CHtmlSysWin*, void (*done_func)(void*, int repeat_count), void* done_func_ctx, int repeat, const textchar_t* /*url*/, int vol, long /*fade_in*/, long /*fade_out*/, int /*crossfade*/ ) #ifndef Q_OS_ANDROID { //qDebug() << "play_sound url:" << url << "repeat:" << repeat; // Check if the user disabled background music. if (not qFrame->settings()->enableMusic) { return 1; } if (CHtmlSysSoundMidiQt::fActiveMidi != 0) { // Only one MIDI sound can be active at a time. return 1; } // Adjust volume if it exceeds min/max levels. if (vol < 0) { vol = 0; } else if (vol > 100) { vol = 100; } // Convert the TADS volume level semantics [0..100] to SDL volume // semantics [0..MIX_MAX_VOLUME]. vol = (vol * MIX_MAX_VOLUME) / 100; // Set the volume level. Mix_VolumeMusic(vol); this->fRepeatsWanted = repeat; if (Mix_PlayMusic(this->fMusic, 0) == -1) { qWarning() << "ERROR: Can't play MIDI:" << Mix_GetError(); return 1; } this->fPlaying = true; this->fRepeats = 1; this->fDone_func = done_func; this->fDone_func_ctx = done_func_ctx; CHtmlSysSoundMidiQt::fActiveMidi = this; return 0; } #else { return 1; } #endif void CHtmlSysSoundMidiQt::cancel_sound( CHtmlSysWin*, int, long, int ) #ifndef Q_OS_ANDROID { //qDebug() << Q_FUNC_INFO; if (fActiveMidi == this) { this->fRepeatsWanted = -1; Mix_HaltMusic(); CHtmlSysSoundMidiQt::callback(); CHtmlSysSoundMidiQt::fActiveMidi = 0; this->fPlaying = false; } } #else { } #endif CHtmlSysResource* CHtmlSysSoundMidi::create_midi( const CHtmlUrl* /*url*/, const textchar_t* filename, unsigned long seekpos, unsigned long filesize, CHtmlSysWin* ) #ifndef Q_OS_ANDROID { //qDebug() << "Loading sound from" << filename << "offset:" << seekpos << "size:" << filesize // << "url:" << url->get_url(); // Check if the file exists and is readable. QFileInfo inf(fnameToQStr(filename)); if (not inf.exists() or not inf.isReadable()) { qWarning() << "ERROR:" << inf.filePath() << "doesn't exist or is unreadable"; return 0; } // Open the file and seek to the specified position. QFile file(inf.filePath()); if (not file.open(QIODevice::ReadOnly)) { qWarning() << "ERROR: Can't open file" << inf.filePath(); return 0; } if (not file.seek(seekpos)) { qWarning() << "ERROR: Can't seek in file" << inf.filePath(); file.close(); return 0; } QByteArray data(file.read(filesize)); file.close(); if (data.isEmpty() or static_cast(data.size()) < filesize) { qWarning() << "ERROR: Could not read" << filesize << "bytes from file" << inf.filePath(); return 0; } return new CHtmlSysSoundMidiQt(SDL_RWFromConstMem(data.constData(), data.size())); } #else { return 0; } #endif /* -------------------------------------------------------------------- * CHtmlSysSoundWavQt */ int CHtmlSysSoundWavQt::play_sound( CHtmlSysWin*, void (*done_func)(void*, int repeat_count), void* done_func_ctx, int repeat, const textchar_t* /*url*/, int vol, long fade_in, long fade_out, int crossfade ) #ifndef Q_OS_ANDROID { //qDebug() << "play_sound url:" << url << "repeat:" << repeat; return this->startPlaying(done_func, done_func_ctx, repeat, vol, fade_in, fade_out, crossfade); } #else { return 1; } #endif void CHtmlSysSoundWavQt::add_crossfade( CHtmlSysWin*, long ms ) #ifndef Q_OS_ANDROID { this->addCrossFade(ms); } #else { } #endif void CHtmlSysSoundWavQt::cancel_sound( CHtmlSysWin*, int sync, long fade_out_ms, int fade_in_bg ) #ifndef Q_OS_ANDROID { //qDebug() << Q_FUNC_INFO; this->cancelPlaying(sync, fade_out_ms, fade_in_bg); } #else { } #endif void CHtmlSysSoundWavQt::resume() { //qDebug() << Q_FUNC_INFO; } /* -------------------------------------------------------------------- * CHtmlSysSoundOggQt */ int CHtmlSysSoundOggQt::play_sound( CHtmlSysWin*, void (*done_func)(void*, int repeat_count), void* done_func_ctx, int repeat, const textchar_t*, int vol, long fade_in, long fade_out, int crossfade ) { //qDebug() << "play_sound url:" << url << "repeat:" << repeat; #ifndef Q_OS_ANDROID return this->startPlaying(done_func, done_func_ctx, repeat, vol, fade_in, fade_out, crossfade); #else return 1; #endif } void CHtmlSysSoundOggQt::add_crossfade( CHtmlSysWin*, long ms ) { #ifndef Q_OS_ANDROID this->addCrossFade(ms); #endif } void CHtmlSysSoundOggQt::cancel_sound( CHtmlSysWin*, int sync, long fade_out_ms, int fade_in_bg ) { //qDebug() << Q_FUNC_INFO; #ifndef Q_OS_ANDROID this->cancelPlaying(sync, fade_out_ms, fade_in_bg); #endif } void CHtmlSysSoundOggQt::resume() { //qDebug() << Q_FUNC_INFO; } /* -------------------------------------------------------------------- * CHtmlSysSoundMpegQt */ int CHtmlSysSoundMpegQt::play_sound( CHtmlSysWin*, void (*done_func)(void*, int repeat_count), void* done_func_ctx, int repeat, const textchar_t* /*url*/, int vol, long fade_in, long fade_out, int crossfade ) { //qDebug() << "play_sound url:" << url << "repeat:" << repeat; #ifndef Q_OS_ANDROID return this->startPlaying(done_func, done_func_ctx, repeat, vol, fade_in, fade_out, crossfade); #else return 1; #endif } void CHtmlSysSoundMpegQt::add_crossfade( CHtmlSysWin*, long ms ) { #ifndef Q_OS_ANDROID this->addCrossFade(ms); #endif } void CHtmlSysSoundMpegQt::cancel_sound( CHtmlSysWin*, int sync, long fade_out_ms, int fade_in_bg ) { //qDebug() << Q_FUNC_INFO; #ifndef Q_OS_ANDROID this->cancelPlaying(sync, fade_out_ms, fade_in_bg); #endif } void CHtmlSysSoundMpegQt::resume() { //qDebug() << Q_FUNC_INFO; } CHtmlSysResource* CHtmlSysSoundWav::create_wav( const CHtmlUrl* url, const textchar_t* filename, unsigned long seekpos, unsigned long filesize, CHtmlSysWin* win ) { #ifndef Q_OS_ANDROID return QTadsSound::createSound(url, filename, seekpos, filesize, win, QTadsSound::WAV); #else return 0; #endif } CHtmlSysResource* CHtmlSysSoundMpeg::create_mpeg( const CHtmlUrl* url, const textchar_t* filename, unsigned long seekpos, unsigned long filesize, CHtmlSysWin* win ) { #ifndef Q_OS_ANDROID return QTadsSound::createSound(url, filename, seekpos, filesize, win, QTadsSound::MPEG); #else return 0; #endif } CHtmlSysResource* CHtmlSysSoundOgg::create_ogg( const CHtmlUrl* url, const textchar_t* filename, unsigned long seekpos, unsigned long filesize, CHtmlSysWin* win ) { #ifndef Q_OS_ANDROID return QTadsSound::createSound(url, filename, seekpos, filesize, win, QTadsSound::OGG); #else return 0; #endif } qtads-2.1.7/src/syssoundmidi.h000066400000000000000000000064561265017072300163450ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef SYSSOUNDMIDI_H #define SYSSOUNDMIDI_H #include #include "htmlsys.h" #include "config.h" /* Tads HTML layer class whose interface needs to be implemented by the * interpreter. * * See htmltads/htmlsys.h and htmltads/notes/porting.htm for information * about this class. */ class CHtmlSysSoundMidiQt: public CHtmlSysSoundMidi { #ifndef Q_OS_ANDROID private: struct SDL_RWops* fRWops; Mix_Music* fMusic; bool fPlaying; // TADS callback to invoke on stop. void (*fDone_func)(void*, int repeat_count); // CTX to pass to the TADS callback. void* fDone_func_ctx; // How many times we repeated the sound. int fRepeats; // How many times should we repeat the sound. // 0 means repeat forever. int fRepeatsWanted; // Currently playing MIDI object. The callback needs this. static CHtmlSysSoundMidiQt* fActiveMidi; #endif public: #ifndef Q_OS_ANDROID CHtmlSysSoundMidiQt( struct SDL_RWops* music ); ~CHtmlSysSoundMidiQt() override; // SDL_Mixer callback. static void callback(); #endif // // CHtmlSysSoundMidi interface implementation. // int play_sound( CHtmlSysWin* win, void (*done_func)(void*, int repeat_count), void* done_func_ctx, int repeat, const textchar_t* url, int vol, long fade_in, long fade_out, int crossfade ) override; void add_crossfade( CHtmlSysWin*, long ) override { qDebug() << Q_FUNC_INFO; } void cancel_sound( CHtmlSysWin* win, int sync, long fade_out_ms, int fade_in_bg ) override; int maybe_suspend( CHtmlSysSound* ) override // MIDI is exclusive - we can only play one MIDI sound at a time. However, // the current HTML TADS model only allows MIDI to play in one layer (the // background layer) anyway, so we should never find ourselves wanting to // play a MIDI sound while another MIDI sound is already active. So, we'll // just ignore this request entirely; the result will be that the new // foreground sound will be unable to play. // // If at some point in the future the HTML TADS model changes to allow // multiple layers of MIDI sounds, this part of this routine will have to // have a real implementation. { return false; } void resume() override // We never suspend MIDI, so there's nothing to do. (See the notes in // maybe_suspend() - if the model changes to require that MIDI suspension // be implemented, we would have to provide a real implementation here.) { } }; #endif qtads-2.1.7/src/syssoundmpeg.h000066400000000000000000000040251265017072300163410ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef SYSSOUNDMPEG_H #define SYSSOUNDMPEG_H #include "qtadssound.h" #include "htmlsys.h" #include "config.h" /* Tads HTML layer class whose interface needs to be implemented by the * interpreter. * * See htmltads/htmlsys.h and htmltads/notes/porting.htm for information * about this class. */ class CHtmlSysSoundMpegQt: public QTadsSound, public CHtmlSysSoundMpeg { Q_OBJECT public: #ifndef Q_OS_ANDROID CHtmlSysSoundMpegQt( QObject* parent, Mix_Chunk* chunk, SoundType type ) : QTadsSound(parent, chunk, type) { } #endif // // CHtmlSysSoundMpeg interface implementation. // int play_sound( CHtmlSysWin* win, void (*done_func)(void*, int repeat_count), void* done_func_ctx, int repeat, const textchar_t* url, int vol, long fade_in, long fade_out, int crossfade ) override; void add_crossfade( CHtmlSysWin* win, long ms ) override; void cancel_sound( CHtmlSysWin* win, int sync, long fade_out_ms, int fade_in_bg ) override; int maybe_suspend( CHtmlSysSound* ) override // We always return false since we have no limitation regarding the amount // of sounds we can play simultaneously. { return false; } void resume() override; }; #endif qtads-2.1.7/src/syssoundogg.h000066400000000000000000000040171265017072300161660ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef SYSSOUNDOGG_H #define SYSSOUNDOGG_H #include "qtadssound.h" #include "htmlsys.h" #include "config.h" /* Tads HTML layer class whose interface needs to be implemented by the * interpreter. * * See htmltads/htmlsys.h and htmltads/notes/porting.htm for information * about this class. */ class CHtmlSysSoundOggQt: public QTadsSound, public CHtmlSysSoundOgg { Q_OBJECT public: #ifndef Q_OS_ANDROID CHtmlSysSoundOggQt( QObject* parent, Mix_Chunk* chunk, SoundType type ) : QTadsSound(parent, chunk, type) { } #endif // // CHtmlSysSoundOgg interface implementation. // int play_sound( CHtmlSysWin* win, void (*done_func)(void*, int repeat_count), void* done_func_ctx, int repeat, const textchar_t* url, int vol, long fade_in, long fade_out, int crossfade ) override; void add_crossfade( CHtmlSysWin* win, long ms ) override; void cancel_sound( CHtmlSysWin* win, int sync, long fade_out_ms, int fade_in_bg ) override; int maybe_suspend( CHtmlSysSound* ) override // We always return false since we have no limitation regarding the amount // of sounds we can play simultaneously. { return false; } void resume() override; }; #endif qtads-2.1.7/src/syssoundwav.h000066400000000000000000000037731265017072300162170ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef SYSSOUNDWAV_H #define SYSSOUNDWAV_H #include "qtadssound.h" #include "htmlsys.h" /* Tads HTML layer class whose interface needs to be implemented by the * interpreter. * * See htmltads/htmlsys.h and htmltads/notes/porting.htm for information * about this class. */ class CHtmlSysSoundWavQt: public QTadsSound, public CHtmlSysSoundWav { Q_OBJECT public: #ifndef Q_OS_ANDROID CHtmlSysSoundWavQt( QObject* parent, Mix_Chunk* chunk, SoundType type ) : QTadsSound(parent, chunk, type) { } #endif // // CHtmlSysSoundWav interface implementation. // int play_sound( CHtmlSysWin* win, void (*done_func)(void*, int repeat_count), void* done_func_ctx, int repeat, const textchar_t* url, int vol, long fade_in, long fade_out, int crossfade ) override; void add_crossfade( CHtmlSysWin* win, long ms ) override; void cancel_sound( CHtmlSysWin* win, int sync, long fade_out_ms, int fade_in_bg ) override; int maybe_suspend( CHtmlSysSound* ) override // We always return false since we have no limitation regarding the amount // of sounds we can play simultaneously. { return false; } void resume() override; }; #endif qtads-2.1.7/src/syswin.cc000066400000000000000000001171741265017072300153050ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include #include #include #include #include #include "dispwidget.h" #include "qtadstimer.h" #include "settings.h" #include "syswininput.h" #include "sysimagejpeg.h" #include "sysimagepng.h" #include "htmlfmt.h" #include "htmlrc.h" CHtmlSysWinQt::CHtmlSysWinQt( CHtmlFormatter* formatter, QWidget* parent ) : QScrollArea(parent), CHtmlSysWin(formatter), fBannerPos(HTML_BANNERWIN_POS_TOP), fBannerStyleModeMode(true), fBannerStyleAutoVScroll(true), fBannerStyleGrid(false), fBannerStyleBorder(0), fBorderLine(qWinGroup->centralWidget()), fDontReformat(0), fInPagePauseMode(false), fParentBanner(0), fBgImage(0), lastInputHeight(0), margins(8, 2, 8, 2), bannerSize(0), bannerSizeUnits(HTML_BANNERWIN_UNITS_PIX) { this->dispWidget = new DisplayWidget(this, formatter); this->setWidget(this->dispWidget); this->formatter_->set_win(this, &margins); this->viewport()->setForegroundRole(QPalette::Text); this->viewport()->setBackgroundRole(QPalette::Base); this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); this->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); this->setFrameStyle(QFrame::NoFrame | QFrame::Plain); this->setLineWidth(0); this->setContentsMargins(0, 0, 0, 0); this->fBorderLine.setFrameStyle(QFrame::Box | QFrame::Plain); QPalette p(this->fBorderLine.palette()); p.setColor(QPalette::WindowText, Qt::darkGray); this->fBorderLine.setPalette(p); p = this->palette(); p.setColor(QPalette::Base, qFrame->settings()->bannerBgColor); p.setColor(QPalette::Text, qFrame->settings()->bannerTextColor); this->setPalette(p); this->setAttribute(Qt::WA_InputMethodEnabled); // TEMP: Just to make the area of this widget visibly more apparent. //this->viewport()->setBackgroundRole(QPalette::ToolTipBase); } CHtmlSysWinQt::~CHtmlSysWinQt() { //qDebug() << Q_FUNC_INFO; // Remove my reference on the background image if I have one. if (this->fBgImage != 0) { this->fBgImage->remove_ref(); } // Don't allow the formatter to reference us anymore, since // we're about to be deleted. this->formatter_->unset_win(); // Remove ourselves from our parent banner's child list. if (this->fParentBanner != 0) { this->fParentBanner->fChildBanners.removeAll(this); } // Remove all references to us from our child banners. for (int i = 0; i < this->fChildBanners.size(); ++i) { Q_ASSERT(this->fChildBanners.at(i)->fParentBanner == this); this->fChildBanners.at(i)->fParentBanner = 0; } } void CHtmlSysWinQt::fSetupPainterForFont( QPainter& painter, bool hilite, CHtmlSysFont* font ) { const CHtmlSysFontQt& fontCast = *static_cast(font); painter.setFont(fontCast); if (fontCast.use_font_color()) { // The font has its own color; use it. HTML_color_t color = fontCast.get_font_color(); painter.setPen(QColor(HTML_color_red(color), HTML_color_green(color), HTML_color_blue(color))); } else if (font->get_font_color() == static_cast(HTML_COLOR_INPUT)) { painter.setPen(qFrame->inputColor()); } if (hilite) { painter.setBackgroundMode(Qt::OpaqueMode); painter.setBackground(QApplication::palette().highlight()); painter.setPen(QApplication::palette().color(QPalette::HighlightedText)); } if (fontCast.use_font_bgcolor()) { painter.setBackgroundMode(Qt::OpaqueMode); HTML_color_t color = fontCast.get_font_bgcolor(); painter.setBackground(QColor(HTML_color_red(color), HTML_color_green(color), HTML_color_blue(color))); } } void CHtmlSysWinQt::keyPressEvent( QKeyEvent* event ) { qFrame->gameWindow()->keyPressEvent(event); } void CHtmlSysWinQt::inputMethodEvent( QInputMethodEvent* e ) { qFrame->gameWindow()->inputMethodEvent(e); } void CHtmlSysWinQt::scrollContentsBy(int dx, int dy) { QScrollArea::scrollContentsBy(dx, dy); this->dispWidget->updateLinkTracking(QPoint()); // If we reached the bottom, clear page-pause mode. if (this->fInPagePauseMode and this->verticalScrollBar()->value() == this->verticalScrollBar()->maximum()) { this->fInPagePauseMode = false; qFrame->gameWindow()->removeFromPagePauseQueue(this); } } void CHtmlSysWinQt::wheelEvent( QWheelEvent* e ) { // Only allow the event if the banner has a vertical scrollbar. Banners // without one are not supposed to be scrollable. if (this == qFrame->gameWindow() or this->fBannerStyleVScroll) { QScrollArea::wheelEvent(e); } else { e->ignore(); } } void CHtmlSysWinQt::resizeEvent( QResizeEvent* e ) { QScrollArea::resizeEvent(e); // Make sure our display widget is at least the same size as us, so that // it gets mouse events from the entire area of the window. this->dispWidget->setMinimumSize(e->size()); // If we reached the bottom, clear page-pause mode. if (this->fInPagePauseMode and this->verticalScrollBar()->value() == this->verticalScrollBar()->maximum()) { this->fInPagePauseMode = false; qFrame->gameWindow()->removeFromPagePauseQueue(this); } } void CHtmlSysWinQt::mousePressEvent( QMouseEvent* e ) { // This is a work-around for KDE's Oxygen style window grabbing. Oxygen // allows window grabbing by clicking on any "empty" area of an // application. An area is considered empty, if mouse press events reach // QMainWindow. We don't want our game window to have draggable areas // just because we don't handle mouse press events. So we accept the event // here so that KDE knows not to allow window dragging. e->accept(); } void CHtmlSysWinQt::calcChildBannerSizes( QRect& parentSize ) { //qDebug() << Q_FUNC_INFO; ++this->fDontReformat; QRect newSize; QRect oldSize; // Get our current size. oldSize = this->geometry(); // Start off assuming we'll take the entire parent area. If we're a // top-level window, we'll leave it exactly like that. If we're a banner, // we'll be aligned on three sides with the parent area, since we always // carve out a chunk by splitting the parent area; we'll adjust the fourth // side according to our banner size and alignment specifications. newSize = parentSize; // If we're a banner window, carve out our own area from the parent window; // otherwise, we must be a top-level window, so take the entire available // space for ourselves. if (this != qFrame->gameWindow()) { QFrame& borderLine = this->fBorderLine; CHtmlRect margins = this->formatter_->get_phys_margins(); // Should be draw a border? bool drawBorder = this->fBannerStyleBorder; if (this->bannerSize == 0) { // This is a zero-size banner. Don't use a border or margins. // A zero-size banner really should be a zero-size banner. drawBorder = false; margins.set(0, 0, 0, 0); } // Calculate our current on-screen size. int wid = oldSize.width(); int ht = oldSize.height(); // Convert the width units to pixels. switch (this->bannerSizeUnits) { case HTML_BANNERWIN_UNITS_PIX: // Pixels - use the stored width directly. wid = this->bannerSize; ht = this->bannerSize; // Add space for the border if needed. if (drawBorder) { ++wid; ++ht; } break; case HTML_BANNERWIN_UNITS_CHARS: // Character cells - calculate the size in terms of the width of a // "0" character in the window's default font. wid = this->bannerSize * measure_text(get_default_font(), "0", 1, 0).x; ht = this->bannerSize * measure_text(get_default_font(), "0", 1, 0).y; // Add space for the border if needed. if (drawBorder) { ++wid; ++ht; } // Add the margins. wid += margins.left + margins.right; ht += margins.top + margins.bottom; break; case HTML_BANNERWIN_UNITS_PCT: // Percentage - calculate the width as a percentage of the parent // size. wid = (this->bannerSize * parentSize.width()) / 100; ht = (this->bannerSize * parentSize.height()) / 100; break; } // Make sure that the banner doesn't exceed the available area. if (wid > parentSize.width()) { wid = parentSize.width(); } if (ht > parentSize.height()) { ht = parentSize.height(); } // Position the banner according to our alignment type. switch(this->fBannerPos) { case HTML_BANNERWIN_POS_TOP: if (drawBorder) { // Adjust for the border by taking the space for it out of // the banner. newSize.setBottom(newSize.top() + ht - 2); // Position the border window at the bottom of our area. borderLine.setGeometry(newSize.left(), newSize.top() + ht - 1, newSize.left() + newSize.width(), 1); } else { // Align the banner at the top of the window. newSize.setBottom(newSize.top() + ht - 1); } // Take the space out of the top of the parent window. parentSize.setTop(parentSize.top() + ht); break; case HTML_BANNERWIN_POS_BOTTOM: if (drawBorder) { // Adjust for the border by taking the space for it out of // the banner. newSize.setTop(newSize.top() + newSize.height() - ht + 2); // Position the border window at the top of our area. borderLine.setGeometry(newSize.left(), newSize.top() - 1, newSize.left() + newSize.width(), 1); } else { // Align the banner at the bottom of the window. newSize.setTop(newSize.top() + newSize.height() - ht + 1); } // Take the space out of the bottom of the parent area. parentSize.setBottom((parentSize.top() + parentSize.height()) - ht); break; case HTML_BANNERWIN_POS_LEFT: if (drawBorder) { // Adjust for the border by taking the space for it out of // the banner. newSize.setRight(newSize.left() + wid - 2); // Position the border window at the right of our area. borderLine.setGeometry(newSize.left() + width(), newSize.top(), 1, newSize.height()); } else { // Align the banner at the left of the window. newSize.setRight(newSize.left() + wid - 1); } // Take the space from the left of the parent window. parentSize.setLeft(parentSize.left() + wid); break; case HTML_BANNERWIN_POS_RIGHT: if (drawBorder) { // Adjust for the border by taking the space for it out of // the banner. newSize.setLeft(newSize.left() + newSize.width() - wid + 2); // Position the border window at the left of our area. borderLine.setGeometry(newSize.left() - 1, newSize.top(), 1, newSize.height()); } else { // Align the banner at the right of the window. newSize.setLeft(newSize.left() + newSize.width() - wid + 1); } // Take the space from the right of the parent window. parentSize.setRight(parentSize.left() + parentSize.width() - wid); break; } if (drawBorder) { borderLine.show(); } else { borderLine.hide(); } } // Now that we know our own full area, lay out our banner children. This // will update newSize to reflect our actual size after deducting space for // our children. for (int i = 0; i < this->fChildBanners.size(); ++i) { this->fChildBanners.at(i)->calcChildBannerSizes(newSize); } // Set our new window position if it differs from the old one. if (newSize != oldSize) { this->setGeometry(newSize); // Resizing the display widget by the height we gained or lost during // the resize avoids cutting-off the bottom of the output. I've no // idea why; the problem probably lies elsewhere, but for now, this // seems to fix it. this->dispWidget->resize(this->dispWidget->width(), this->dispWidget->height() + (newSize.height() - oldSize.height())); // Since we changed size, we will need a reformat. //if (this != qFrame->gameWindow()) //qDebug() << newSize.width(); //if (newSize.width() != oldSize.width()) qFrame->scheduleReformat(); } //qFrame->gameWindow()->verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum); --this->fDontReformat; } void CHtmlSysWinQt::doReformat( int showStatus, int freezeDisplay, int resetSounds ) { // Forget any tracking links. this->dispWidget->notifyClearContents(); // Restart the formatter. this->formatter_->start_at_top(resetSounds); // Format the window contents. this->do_formatting(showStatus, not freezeDisplay, freezeDisplay); // Reset last seen position. Substract the top margin when doing this, // since for scrolling purposes we need to take the whole area into // account, not only the position where actual content starts. this->lastInputHeight = this->formatter_->get_max_y_pos() - this->formatter_->get_phys_margins().top; } void CHtmlSysWinQt::addBanner( CHtmlSysWinQt* banner, HTML_BannerWin_Type_t type, int where, CHtmlSysWinQt* other, HTML_BannerWin_Pos_t pos, unsigned long style ) { Q_ASSERT(banner->fParentBanner == 0); // Enable/disable the scrollbars, according to what was requested. if (style & OS_BANNER_STYLE_VSCROLL) { banner->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); banner->fBannerStyleVScroll = true; } else { banner->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); banner->fBannerStyleVScroll = false; } if (style & OS_BANNER_STYLE_HSCROLL) { banner->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); banner->fBannerStyleHScroll = true; } else { banner->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); banner->fBannerStyleHScroll = true; } banner->fBannerStyleGrid = type & HTML_BANNERWIN_TEXTGRID; banner->fParentBanner = this; banner->fBannerPos = pos; banner->fBannerWhere = where; banner->fBannerStyleModeMode = style & OS_BANNER_STYLE_MOREMODE; banner->fBannerStyleAutoVScroll = style & OS_BANNER_STYLE_AUTO_VSCROLL; banner->fBannerStyleBorder = style & OS_BANNER_STYLE_BORDER; banner->setGeometry(this->geometry()); // Qt will not update the viewport() geometry unless the widget is shown // first. banner->show(); banner->hide(); // Add the banner to our child list. Q_ASSERT(not this->fChildBanners.contains(banner)); switch (where) { case OS_BANNER_FIRST: this->fChildBanners.prepend(banner); break; case OS_BANNER_LAST: this->fChildBanners.append(banner); break; case OS_BANNER_BEFORE: Q_ASSERT(this->fChildBanners.contains(other)); this->fChildBanners.insert(this->fChildBanners.indexOf(other), banner); break; default: Q_ASSERT(where == OS_BANNER_AFTER); Q_ASSERT(this->fChildBanners.contains(other)); this->fChildBanners.insert(this->fChildBanners.indexOf(other) + 1, banner); } } extern void os_sleep_ms( long ms ); void CHtmlSysWinQt::scrollDown( bool force, bool justOneLine ) { QScrollBar* bar = this->verticalScrollBar(); // If we're very small, or shouldn't scroll, or there's nothing to scroll, // ignore the call. if (this->width() < 10 or this->height() < 10 or not this->fBannerStyleAutoVScroll or bar->maximum() == bar->minimum()) { return; } // Simply scroll directly to the bottom if more-mode is disabled for this // banner or the game is in non-stop mode. if (not this->fBannerStyleModeMode or qFrame->nonStopMode()) { if (qFrame->settings()->softScrolling) { while (bar->value() < bar->maximum()) { bar->setValue(bar->value() + bar->singleStep()); os_sleep_ms(5); } } else { bar->triggerAction(QAbstractSlider::SliderToMaximum); } return; } if (this->fInPagePauseMode and not force) { return; } if (this->fInPagePauseMode) { if (justOneLine) { bar->triggerAction(QAbstractSlider::SliderSingleStepAdd); } else if (qFrame->settings()->softScrolling) { int target = bar->value() + bar->pageStep() - bar->singleStep() * 2; while (bar->value() < target and bar->value() < bar->maximum()) { bar->setValue(bar->value() + bar->singleStep()); os_sleep_ms(5); } } else { bar->setValue(bar->value() + bar->pageStep() - bar->singleStep() * 2); } } else if (qFrame->settings()->softScrolling) { while (bar->value() < this->lastInputHeight and bar->value() < bar->maximum()) { bar->setValue(bar->value() + bar->singleStep()); os_sleep_ms(5); } } else { bar->setValue(this->lastInputHeight); } // If we didn't arrive at the bottom, enable page-pause. if (bar->value() < bar->maximum()) { qFrame->gameWindow()->addToPagePauseQueue(this); this->fInPagePauseMode = true; // Note our new position. Make sure that next time, we scroll down by // *almost* a page; we leave two lines of context. this->lastInputHeight += bar->pageStep() - bar->singleStep() * 2; return; } this->fInPagePauseMode = false; return; } void CHtmlSysWinQt::notify_clear_contents() { //qDebug() << Q_FUNC_INFO; // Tell our display widget about it. this->dispWidget->notifyClearContents(); // Reset our last-seen position (for page-pause handling) back to the top // and clear page-pause mode. this->lastInputHeight = 0; this->fInPagePauseMode = false; qFrame->gameWindow()->removeFromPagePauseQueue(this); } int CHtmlSysWinQt::close_window( int force ) { qDebug() << Q_FUNC_INFO; return force ? true : false; } CHtmlPoint CHtmlSysWinQt::measure_text( CHtmlSysFont* font, const textchar_t* str, size_t len, int* ascent ) { //qDebug() << Q_FUNC_INFO; const QFontMetrics& tmpMetr = QFontMetrics(*static_cast(font)); if (ascent != 0) { *ascent = tmpMetr.ascent(); } // We don't return the actual width of the text, but the distance to where // subsequent text should be drawn. This is really what our caller needs // to know, otherwise letters will start jumping left and right when // selecting text or moving the text cursor. return CHtmlPoint(tmpMetr.width(QString::fromUtf8(str, len)), tmpMetr.height()); } size_t CHtmlSysWinQt::get_max_chars_in_width( CHtmlSysFont* font, const textchar_t* str, size_t len, long wid ) { // We bisect the results of measure_text() for the maximum amount of // characters that fit. int begin = 1; int end = len; int mid = (begin + end) / 2; while (end >= begin) { mid = (begin + end) / 2; // We might have landed inside a UTF-8 continuation byte. In that case, // adjust mid until we have a complete UTF character. while ((str[mid] & 0xC0) == 0x80 and mid > begin and mid < end) { ++mid; } long bestFit = this->measure_text(font, str, mid, 0).x; if (bestFit > wid) { end = mid - 1; } else if (bestFit < wid) { begin = mid + 1; } else { return mid; } } // We didn't find an exact match, which means one less than the result we // got will fit. --mid; // If that got us inside a UTF-8 sequence, skip to the character's first // byte. while (mid > 0 and (str[mid] & 0xC0) == 0x80) { --mid; } return mid > 0 ? mid : 0; } void CHtmlSysWinQt::draw_text( int hilite, long x, long y, CHtmlSysFont* font, const textchar_t* str, size_t len ) { if (len == 0) { // Huh? return; } if ((0x80 & *str) != 0x00 and (0xE0 & *str) != 0xC0 and (0xF0 & *str) != 0xE0 and (0xF8 & *str) != 0xF0) { //qDebug() << "ERROR"; ++str; --len; } if (len == 0) { return; } if (this->fBannerStyleGrid) { //qDebug() << QString::fromUtf8(str, len); const QString qStr(QString::fromUtf8(str, len)); //qDebug() << qStr; if (qStr.startsWith(QString::fromUtf8("α"))) { //qDebug() << len; //abort(); //printf("%.*s\n", (int)len, str); } } QPainter painter(this->dispWidget); this->fSetupPainterForFont(painter, hilite, font); painter.drawText(x, y + painter.fontMetrics().ascent(), QString::fromUtf8(str, len)); } void CHtmlSysWinQt::draw_text_space( int hilite, long x, long y, CHtmlSysFont* font, long wid ) { QPainter painter(this->dispWidget); this->fSetupPainterForFont(painter, hilite, font); // Construct a string of spaces that's at least 'width' pixels wide. QString str(QChar::fromLatin1(' ')); const QFontMetrics& metr = painter.fontMetrics(); while (metr.width(str) < wid) { str.append(QChar::fromLatin1(' ')); } painter.drawText(x, y, wid, metr.height(), Qt::AlignLeft, str); } void CHtmlSysWinQt::draw_bullet( int hilite, long x, long y, CHtmlSysFont* font, HTML_SysWin_Bullet_t style ) { int unicode; switch (style) { case HTML_SYSWIN_BULLET_SQUARE: unicode = 0x25AA; break; case HTML_SYSWIN_BULLET_CIRCLE: unicode = 0x25CB; break; case HTML_SYSWIN_BULLET_DISC: unicode = 0x2022; break; default: Q_ASSERT(style == HTML_SYSWIN_BULLET_PLAIN); // Nothing to draw. return; } const QByteArray& utf8 = QString(QChar(unicode)).toUtf8(); this->draw_text(hilite, x, y, font, utf8.constData(), utf8.size()); } void CHtmlSysWinQt::draw_hrule( const CHtmlRect* pos, int shade ) { //qDebug() << Q_FUNC_INFO; Q_ASSERT(pos != 0); QPainter painter(this->dispWidget); if (shade) { if (pos->bottom - pos->top > 2) { qDrawShadePanel(&painter, pos->left, pos->top, pos->right - pos->left, pos->bottom - pos->top, this->palette(), true, 1, 0); } else { qDrawShadeLine(&painter, pos->left, pos->top, pos->right, pos->top, this->palette(), true, 1, 0); } } else { painter.fillRect(pos->left, pos->top, pos->right - pos->left, pos->bottom - pos->top, QApplication::palette().color(QPalette::Dark)); } } void CHtmlSysWinQt::draw_table_border( const CHtmlRect* pos, int width, int cell ) { //qDebug() << Q_FUNC_INFO; QPainter pnt(this->dispWidget); QPalette pal; // Use Midlight for Light to closer match Windows HTML TADS appearance. pal.setColor(QPalette::Light, QApplication::palette().color(QPalette::Midlight)); qDrawShadePanel(&pnt, pos->left, pos->top, pos->right - pos->left, pos->bottom - pos->top, pal, cell, width); } void CHtmlSysWinQt::draw_table_bkg( const CHtmlRect* pos, HTML_color_t bgcolor ) { //qDebug() << Q_FUNC_INFO; QPainter painter(this->dispWidget); int red = HTML_color_red(bgcolor); int green = HTML_color_green(bgcolor); int blue = HTML_color_blue(bgcolor); painter.fillRect(pos->left, pos->top, pos->right - pos->left, pos->bottom - pos->top, QColor(red, green, blue)); } void CHtmlSysWinQt::draw_dbgsrc_icon( const CHtmlRect*, unsigned int ) { qDebug() << Q_FUNC_INFO; } int CHtmlSysWinQt::do_formatting( int /*show_status*/, int update_win, int freeze_display ) { //qDebug() << Q_FUNC_INFO; if (this->fDontReformat != 0) { qWarning() << "Reentrancy detected in" << Q_FUNC_INFO << "\nThis should not happen."; return false; } // If desired, freeze display updating while we're working. if (freeze_display) { this->formatter_->freeze_display(true); } // Redraw the window before we start, so that something happens immediately // and the window doesn't look frozen. if (update_win) { qFrame->advanceEventLoop(); } // Get the window area in document coordinates, so we'll know when we've // formatted past the bottom of the current display area. Note that we'll // ignore the presence or absence of a horizontal scrollbar, since it could // come or go while we're formatting; by assuming that it won't be there, // we'll be maximally conservative about redrawing the whole area. //unsigned long winBottom = qMax(static_cast(this->height()), this->formatter_->get_max_y_pos()); // We don't have enough formatting done yet to draw the window. bool drawn = false; // Reformat everything, drawing as soon as possible, if desired. /* if (update_win) { while (formatter_->more_to_do() and not drawn) { // Format another line. this->formatter_->do_formatting(); // If we have enough content to do so, redraw the window; we're not // really done with the formatting yet, but at least we'll update // the window as soon as we can, so the user isn't staring at a // blank window longer than necessary. if (this->formatter_->get_max_y_pos() >= winBottom) { // If the formatter's updating is frozen, invalidate the window; // the formatter won't have been invalidating it as it goes. if (freeze_display) { this->viewport()->update(); } // Let the event loop redraw the window. qFrame->advanceEventLoop(); // We've now drawn it. drawn = true; } } } */ // After drawing, keep going until we've formatted everything. while (this->formatter_->more_to_do()) { this->formatter_->do_formatting(); } // Unfreeze the display if we froze it. if (freeze_display) { this->formatter_->freeze_display(false); } if (this->fBannerStyleGrid) { this->dispWidget->resize(this->viewport()->size()); } else { long newWidth; if (this->formatter_->get_outer_max_line_width() > this->viewport()->width()) { newWidth = this->formatter_->get_outer_max_line_width(); } else { newWidth = this->viewport()->width(); } this->dispWidget->resize(newWidth, this->formatter_->get_max_y_pos()); } // If we didn't do any drawing, and we updated the window coming in, // invalidate the window - the initial redraw will have validated // everything, but we've just made it all invalid again and didn't get // around to drawing it. Do the same thing if we froze the display and we // didn't do any updating. if ((update_win and not drawn) or freeze_display) { this->dispWidget->update(); } // Make sure we don't lose any link we were previously tracking. this->dispWidget->updateLinkTracking(QPoint()); // Return an indication of whether we did any updating. return drawn; } CHtmlSysFont* CHtmlSysWinQt::get_default_font() { //qDebug() << Q_FUNC_INFO; CHtmlFontDesc desc; qFrame->settings()->mainFont.get_font_desc(&desc); desc.htmlsize = 3; return get_font(&desc); } void CHtmlSysWinQt::register_timer_func( void (*timer_func)(void*), void* func_ctx ) { QTadsTimer* timer = new QTadsTimer(timer_func, func_ctx, this); timer->setSingleShot(false); timer->set_repeating(true); timer->set_active(true); timer->start(1000); this->fTimerList.append(timer); } void CHtmlSysWinQt::unregister_timer_func( void (*timer_func)( void * ), void* ) { for (int i = 0; i < this->fTimerList.size(); ++i) { if (this->fTimerList.at(i)->func_ == timer_func) { delete this->fTimerList.takeAt(i); return; } } } CHtmlSysTimer* CHtmlSysWinQt::create_timer( void (*timer_func)( void* ), void *func_ctx ) { qDebug() << Q_FUNC_INFO; return new QTadsTimer(timer_func, func_ctx, this); } void CHtmlSysWinQt::set_timer( CHtmlSysTimer* timer, long interval_ms, int repeat ) { qDebug() << Q_FUNC_INFO; static_cast(timer)->setSingleShot(not repeat); static_cast(timer)->set_repeating(repeat); static_cast(timer)->start(interval_ms); } void CHtmlSysWinQt::cancel_timer( CHtmlSysTimer* timer ) { qDebug() << Q_FUNC_INFO; static_cast(timer)->stop(); } void CHtmlSysWinQt::delete_timer( CHtmlSysTimer* timer ) { qDebug() << Q_FUNC_INFO; delete timer; } void CHtmlSysWinQt::fmt_adjust_hscroll() { //qDebug() << Q_FUNC_INFO; if (this->fBannerStyleGrid) { this->dispWidget->resize(this->viewport()->width(), this->dispWidget->height()); return; } if (this->formatter_->get_outer_max_line_width() != this->dispWidget->width()) { long newWidth; if (this->formatter_->get_outer_max_line_width() > this->viewport()->width()) { newWidth = this->formatter_->get_outer_max_line_width(); } else { newWidth = this->viewport()->width(); } this->dispWidget->resize(newWidth, this->dispWidget->height()); } } void CHtmlSysWinQt::fmt_adjust_vscroll() { //qDebug() << Q_FUNC_INFO; // Get the target height. int targetHt = static_cast(this->formatter_->get_max_y_pos()); if (targetHt == this->dispWidget->height()) { // No change in height, so nothing to do. return; } if (this->fBannerStyleGrid) { // Text grid banners never scroll in any direction, so they only need // to be the same size as the viewport. this->dispWidget->resize(this->dispWidget->width(), this->viewport()->height()); } else { this->dispWidget->resize(this->dispWidget->width(), targetHt); this->scrollDown(false, false); } } void CHtmlSysWinQt::inval_doc_coords( const CHtmlRect* area ) { //qDebug() << Q_FUNC_INFO; //qDebug() << "Invalidating area" << area->left << area->top << area->right << area->bottom; long width = area->right == HTMLSYSWIN_MAX_RIGHT ? this->dispWidget->width() - area->left : area->right - area->left; long height = area->bottom == HTMLSYSWIN_MAX_BOTTOM ? this->dispWidget->height() - area->top : area->bottom - area->top; this->dispWidget->update(QRect(area->left, area->top, width, height)); } void CHtmlSysWinQt::advise_clearing_disp_list() { //qDebug() << Q_FUNC_INFO; this->dispWidget->notifyClearContents(); } void CHtmlSysWinQt::scroll_to_doc_coords( const CHtmlRect* ) { qDebug() << Q_FUNC_INFO; //qFrame->advanceEventLoop(); } void CHtmlSysWinQt::get_scroll_doc_coords( CHtmlRect* ) { qDebug() << Q_FUNC_INFO; } void CHtmlSysWinQt::set_window_title( const textchar_t* title ) { //qDebug() << Q_FUNC_INFO; qWinGroup->setWindowTitle(QString::fromUtf8(title)); } void CHtmlSysWinQt::set_html_bg_color( HTML_color_t color, int use_default ) { //qDebug() << Q_FUNC_INFO; // If we have an active background image, ignore the call. if (this->fBgImage != 0) { return; } if (use_default) { QPalette p(this->palette()); if (this->fBannerStyleGrid) { p.setColor(QPalette::Base, Qt::black); } else { p.setColor(QPalette::Base, qFrame->settings()->mainBgColor); } this->setPalette(p); return; } color = this->map_system_color(color); QPalette p(this->palette()); p.setColor(QPalette::Base, QColor(HTML_color_red(color), HTML_color_green(color), HTML_color_blue(color))); this->setPalette(p); } void CHtmlSysWinQt::set_html_text_color( HTML_color_t color, int use_default ) { //qDebug() << Q_FUNC_INFO; if (use_default) { QPalette p(this->palette()); if (this->fBannerStyleGrid) { p.setColor(QPalette::Text, Qt::lightGray); } else { p.setColor(QPalette::Text, qFrame->settings()->mainTextColor); } this->setPalette(p); return; } color = this->map_system_color(color); QPalette p(this->palette()); p.setColor(QPalette::Text, QColor(HTML_color_red(color), HTML_color_green(color), HTML_color_blue(color))); this->setPalette(p); } void CHtmlSysWinQt::set_html_link_colors( HTML_color_t, int, HTML_color_t, int, HTML_color_t, int, HTML_color_t, int ) { //qDebug() << Q_FUNC_INFO; } HTML_color_t CHtmlSysWinQt::map_system_color( HTML_color_t color ) { //qDebug() << Q_FUNC_INFO; // If it's just an RGB value, return it unchanged. if (not html_is_color_special(color)) { return color; } QColor col; switch (color) { case HTML_COLOR_STATUSBG: col = qFrame->settings()->bannerBgColor; break; case HTML_COLOR_STATUSTEXT: col = qFrame->settings()->bannerTextColor; break; case HTML_COLOR_LINK: col = qFrame->settings()->unvisitedLinkColor; break; case HTML_COLOR_ALINK: col = qFrame->settings()->clickedLinkColor; break; case HTML_COLOR_TEXT: col = qFrame->settings()->mainTextColor; break; case HTML_COLOR_BGCOLOR: col = qFrame->settings()->mainBgColor; break; case HTML_COLOR_INPUT: col = qFrame->settings()->inputColor; break; case HTML_COLOR_HLINK: col = qFrame->settings()->hoveringLinkColor; break; default: // Return black for everything else. return 0; } return HTML_make_color(col.red(), col.green(), col.blue()); } HTML_color_t CHtmlSysWinQt::get_html_link_color() const { //qDebug() << Q_FUNC_INFO; const QColor& col = qFrame->settings()->unvisitedLinkColor; return HTML_make_color(col.red(), col.green(), col.blue()); } HTML_color_t CHtmlSysWinQt::get_html_alink_color() const { //qDebug() << Q_FUNC_INFO; const QColor& col = qFrame->settings()->clickedLinkColor; return HTML_make_color(col.red(), col.green(), col.blue()); } HTML_color_t CHtmlSysWinQt::get_html_vlink_color() const { //qDebug() << Q_FUNC_INFO; // Visited links aren't really implemented by the HTML base code. return this->get_html_link_color(); } HTML_color_t CHtmlSysWinQt::get_html_hlink_color() const { //qDebug() << Q_FUNC_INFO; const QColor& col = qFrame->settings()->hoveringLinkColor; return HTML_make_color(col.red(), col.green(), col.blue()); } int CHtmlSysWinQt::get_html_link_underline() const { //qDebug() << Q_FUNC_INFO; return qFrame->settings()->underlineLinks; } int CHtmlSysWinQt::get_html_show_links() const { //qDebug() << Q_FUNC_INFO; return qFrame->settings()->enableLinks; } int CHtmlSysWinQt::get_html_show_graphics() const { //qDebug() << Q_FUNC_INFO; return qFrame->settings()->enableGraphics; } void CHtmlSysWinQt::set_html_bg_image( CHtmlResCacheObject* image ) { //qDebug() << Q_FUNC_INFO; if (image == 0 or image->get_image() == 0) { // No image specified; forget the current image if we have one and // restore the default background color. if (this->fBgImage != 0) { this->fBgImage->remove_ref(); this->fBgImage = 0; this->set_html_bg_color(0, true); } return; } QTadsImage* castImg; switch (image->get_image()->get_res_type()) { case HTML_res_type_JPEG: castImg = static_cast(image->get_image()); break; case HTML_res_type_PNG: castImg = static_cast(image->get_image()); break; case HTML_res_type_MNG: // FIXME: Handle MNG backgrounds. //castImg = static_cast(image->get_image()); return; default: qWarning() << Q_FUNC_INFO << "Unknown resource type"; castImg = reinterpret_cast(image->get_image()); } QPalette p(this->palette()); p.setBrush(QPalette::Base, *castImg); this->setPalette(p); image->add_ref(); this->fBgImage = image; } void CHtmlSysWinQt::inval_html_bg_image( unsigned int, unsigned int, unsigned int, unsigned int ) { //qDebug() << Q_FUNC_INFO; } void CHtmlSysWinQt::set_banner_size( long width, HTML_BannerWin_Units_t width_units, int use_width, long height, HTML_BannerWin_Units_t height_units, int use_height ) { //qDebug() << Q_FUNC_INFO; if (this == qFrame->gameWindow()) { // We're the main game window. Ignore the call. return; } if (this->fBannerPos == HTML_BANNERWIN_POS_TOP or this->fBannerPos == HTML_BANNERWIN_POS_BOTTOM) { if (not use_height) { return; } this->bannerSize = height; this->bannerSizeUnits = height_units; } else { Q_ASSERT(this->fBannerPos == HTML_BANNERWIN_POS_LEFT or this->fBannerPos == HTML_BANNERWIN_POS_RIGHT); if (not use_width) { return; } this->bannerSize = width; this->bannerSizeUnits = width_units; } qFrame->adjustBannerSizes(); if (not this->isVisible()) { this->show(); } return; } void CHtmlSysWinQt::set_banner_info( HTML_BannerWin_Pos_t pos, unsigned long style ) { //qDebug() << Q_FUNC_INFO; this->fBannerStyleModeMode = style & OS_BANNER_STYLE_MOREMODE; this->fBannerStyleAutoVScroll = (style & OS_BANNER_STYLE_AUTO_VSCROLL) or this->fBannerStyleModeMode; this->fBannerStyleBorder = style & OS_BANNER_STYLE_BORDER; this->fBannerPos = pos; // FIXME: ignore the scrollbar changes (for now, anyway) } void CHtmlSysWinQt::get_banner_info( HTML_BannerWin_Pos_t* pos, unsigned long* style ) { //qDebug() << Q_FUNC_INFO; Q_ASSERT(pos != 0); Q_ASSERT(style != 0); *pos = this->fBannerPos; // Set the style flags. *style = 0; if (this->fBannerStyleModeMode) { *style |= OS_BANNER_STYLE_MOREMODE; } if (this->fBannerStyleAutoVScroll or this->fBannerStyleModeMode) { *style |= OS_BANNER_STYLE_AUTO_VSCROLL; } if (this->fBannerStyleBorder) { *style |= OS_BANNER_STYLE_BORDER; } // We provide full HTML interpretation, so we support . *style |= OS_BANNER_STYLE_TAB_ALIGN; } qtads-2.1.7/src/syswin.h000066400000000000000000000232471265017072300151440ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef SYSWIN_H #define SYSWIN_H #include #include #include #include "globals.h" #include "sysfont.h" #include "sysframe.h" #include "syswingroup.h" #include "config.h" /* Tads HTML layer class whose interface needs to be implemented by the * interpreter. * * See htmltads/htmlsys.h and htmltads/notes/porting.htm for information * about this class. */ class CHtmlSysWinQt: public QScrollArea, public CHtmlSysWin { Q_OBJECT private: // If we're a banner, what is our HTML_BannerWin_Pos_t, placement and // style flags. // // Note: To determine whether we're a banner, we don't use a flag but // simply check whether "this == qFrame->gameWindow()"; if we're the game // window, we can't be a banner. HTML_BannerWin_Pos_t fBannerPos; int fBannerWhere; bool fBannerStyleModeMode; bool fBannerStyleVScroll; bool fBannerStyleAutoVScroll; bool fBannerStyleHScroll; bool fBannerStyleGrid; bool fBannerStyleBorder; // Our border, if we have one. QFrame fBorderLine; // Guard against re-entrancy for do_formatting(). int fDontReformat; // Are we currently in page-pause mode? bool fInPagePauseMode; // Our parent banner, if any. This is 0 if we don't have one. CHtmlSysWinQt* fParentBanner; // Our child banners, if any. QList fChildBanners; // List of timers with registered callbacks. QList fTimerList; // Our banner background image. class CHtmlResCacheObject* fBgImage; void fSetupPainterForFont( QPainter& painter, bool hilite, CHtmlSysFont* font ); protected: // The content height at the time of the last user input. When the // formatter is producing a long run of output, we pause between screens to // make sure the user has had a chance to see all of the text before we // scroll it away. This member records the position of the last input, so // we can be sure not to add more than another screenfull without getting // more input. int lastInputHeight; // Margins. CHtmlRect margins; // Banner size information, if we are a banner. This is either our width // or height, according to our alignment type (vertical or horizontal). long bannerSize; HTML_BannerWin_Units_t bannerSizeUnits; // Our display widget. class DisplayWidget* dispWidget; void keyPressEvent( QKeyEvent* event ) override; void inputMethodEvent( QInputMethodEvent* e ) override; void scrollContentsBy(int dx, int dy) override; void wheelEvent( QWheelEvent* e ) override; void resizeEvent( QResizeEvent* e ) override; void mousePressEvent( QMouseEvent* e ) override; public: CHtmlSysWinQt( class CHtmlFormatter* formatter, QWidget* parent ); ~CHtmlSysWinQt() override; // Returns our display widget. class DisplayWidget* displayWidget() const { return this->dispWidget; } // Calculate and adjust the sizes of our child banners. On entry, // 'parentSize' contains the size of the full parent window area; on // return, it is updated to indicate the final size of the parent banner's // area after deducting the space carved out for children. void calcChildBannerSizes( QRect& parentSize ); // Do a complete reformat. void doReformat( int showStatus, int freezeDisplay, int resetSounds ); void addBanner( CHtmlSysWinQt* banner, HTML_BannerWin_Type_t type, int where, CHtmlSysWinQt* other, HTML_BannerWin_Pos_t pos, unsigned long style ); // Our parent banner, if there is one. CHtmlSysWinQt* parentBanner() const { return this->fParentBanner; } // Scroll down by a page while keeping track of when to page-pause. The // 'force' flag controls whether we should scroll even while waiting for // input in page-pause mode. void scrollDown( bool force, bool justOneLine ); // // CHtmlSysWin interface implementation. // CHtmlSysWinGroup* get_win_group() override { return qWinGroup; } void notify_clear_contents() override; int close_window( int force ) override; long get_disp_width() override { return this->viewport()->width() - 3; } long get_disp_height() override { return this->viewport()->height(); } long get_pix_per_inch() override { return QApplication::desktop()->logicalDpiX(); } CHtmlPoint measure_text( class CHtmlSysFont* font, const textchar_t* str, size_t len, int *ascent ) override; CHtmlPoint measure_dbgsrc_icon() override { return CHtmlPoint(); } size_t get_max_chars_in_width( CHtmlSysFont* font, const textchar_t* str, size_t len, long wid ) override; void draw_text( int hilite, long x, long y, CHtmlSysFont* font, const textchar_t* str, size_t len ) override; void draw_text_space( int hilite, long x, long y, CHtmlSysFont* font, long wid ) override; void draw_bullet( int hilite, long x, long y, CHtmlSysFont* font, HTML_SysWin_Bullet_t style ) override; void draw_hrule( const CHtmlRect* pos, int shade ) override; void draw_table_border( const CHtmlRect* pos, int width, int cell ) override; void draw_table_bkg( const CHtmlRect* pos, HTML_color_t bgcolor ) override; void draw_dbgsrc_icon( const CHtmlRect* pos, unsigned int stat ) override; int do_formatting( int show_status, int update_win, int freeze_display ) override; // We don't deal with palettes, so this is a no-op. void recalc_palette() override { } // We don't deal with palettes, so always return false. int get_use_palette() override { return false; } CHtmlSysFont* get_default_font() override; CHtmlSysFont* get_font( const CHtmlFontDesc* font_desc ) override { if (this->fBannerStyleGrid) { // We're a text grid banner; use our internal font face, // "qtads-grid", which will result in createFont() using the fixed // width font configured in the user settings. CHtmlFontDesc newDesc(*font_desc); newDesc.copy_from(font_desc); strcpy(newDesc.face, "qtads-grid"); return qFrame->createFont(&newDesc); } return qFrame->createFont(font_desc); } CHtmlSysFont* get_bullet_font( CHtmlSysFont* current_font ) override { return current_font; } void register_timer_func( void (*timer_func)( void* ), void* func_ctx ) override; void unregister_timer_func( void (*timer_func)( void * ), void* func_ctx ) override; CHtmlSysTimer* create_timer( void (*timer_func)( void* ), void *func_ctx ) override; void set_timer( CHtmlSysTimer* timer, long interval_ms, int repeat ) override; void cancel_timer( CHtmlSysTimer* timer ) override; void delete_timer( CHtmlSysTimer* timer ) override; void fmt_adjust_hscroll() override; void fmt_adjust_vscroll() override; void inval_doc_coords( const CHtmlRect* area ) override; void advise_clearing_disp_list() override; void scroll_to_doc_coords( const CHtmlRect* pos ) override; void get_scroll_doc_coords( CHtmlRect* pos ) override; void set_window_title( const textchar_t* title ) override; void set_html_bg_color( HTML_color_t color, int use_default ) override; void set_html_text_color( HTML_color_t color, int use_default ) override; void set_html_input_color( HTML_color_t, int ) override // We don't provide input, so we don't deal with this. { } void set_html_link_colors( HTML_color_t link_color, int link_use_default, HTML_color_t vlink_color, int vlink_use_default, HTML_color_t alink_color, int alink_use_default, HTML_color_t hlink_color, int hlink_use_default ) override; HTML_color_t map_system_color( HTML_color_t color ) override; HTML_color_t get_html_link_color() const override; HTML_color_t get_html_alink_color() const override; HTML_color_t get_html_vlink_color() const override; HTML_color_t get_html_hlink_color() const override; int get_html_link_underline() const override; int get_html_show_links() const override; int get_html_show_graphics() const override; void set_html_bg_image( class CHtmlResCacheObject* image ) override; void inval_html_bg_image( unsigned int x, unsigned int y, unsigned int wid, unsigned int ht ) override; void set_banner_size( long width, HTML_BannerWin_Units_t width_units, int use_width, long height, HTML_BannerWin_Units_t height_units, int use_height ) override; void set_banner_info( HTML_BannerWin_Pos_t pos, unsigned long style ) override; void get_banner_info( HTML_BannerWin_Pos_t* pos, unsigned long* style ) override; }; #endif qtads-2.1.7/src/syswinaboutbox.cc000066400000000000000000000040461265017072300170420ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include "htmlfmt.h" #include "syswinaboutbox.h" #include "dispwidget.h" CHtmlSysWinAboutBoxQt::CHtmlSysWinAboutBoxQt( class CHtmlFormatter* formatter, QWidget* parent ) : CHtmlSysWinQt(formatter, parent) { this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); this->setMinimumSize(200,140); } void CHtmlSysWinAboutBoxQt::resizeEvent( QResizeEvent* e ) { this->formatter_->start_at_top(false); this->do_formatting(true, false, true); QScrollArea::resizeEvent(e); } QSize CHtmlSysWinAboutBoxQt::sizeHint() const { // Ensure that we're always large enough to show the whole contents of the // "about" content. return this->dispWidget->size(); } void CHtmlSysWinAboutBoxQt::set_banner_size( long width, HTML_BannerWin_Units_t, int, long height, HTML_BannerWin_Units_t height_units, int ) { this->bannerSize = height; this->bannerSizeUnits = height_units; this->dispWidget->resize(width + this->margins.left + this->margins.right, height + this->margins.top + this->margins.bottom); QRect rec(this->geometry()); this->calcChildBannerSizes(rec); this->adjustSize(); } qtads-2.1.7/src/syswinaboutbox.h000066400000000000000000000040751265017072300167060ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef SYSWINABOUTBOX_H #define SYSWINABOUTBOX_H #include "syswin.h" #include "config.h" /* We need special handling for the "About this game" box. */ class CHtmlSysWinAboutBoxQt: public CHtmlSysWinQt { Q_OBJECT protected: void keyPressEvent( QKeyEvent* e ) override // It shouldn't be possible to do game input from the about box, so we // bypass the inherited input handling and revert to the default. { QScrollArea::keyPressEvent(e); } void resizeEvent( QResizeEvent* e ) override; QSize sizeHint() const override; public: CHtmlSysWinAboutBoxQt( class CHtmlFormatter* formatter, QWidget* parent ); // We have scrollbars always disabled, so we can report our own // width/height rather than our viewport's. We need to do that because // the formatter needs to know our size before we become visible, and our // viewport only reports a valid size after show() is called. long get_disp_width() override { return this->width(); } long get_disp_height() override { return this->height(); } void set_banner_size( long width, HTML_BannerWin_Units_t width_units, int use_width, long height, HTML_BannerWin_Units_t height_units, int use_height ) override; }; #endif qtads-2.1.7/src/syswingroup.cc000066400000000000000000000665231265017072300163630ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "syswininput.h" #include "syswinaboutbox.h" #include "confdialog.h" #include "settings.h" #include "gameinfodialog.h" #include "aboutqtadsdialog.h" #include "dispwidget.h" void QTadsFrame::resizeEvent( QResizeEvent* ) { qFrame->reformatBanners(true, true, false); } void QTadsFrame::dragEnterEvent( QDragEnterEvent* e ) { CHtmlSysWinInputQt* gameWindow = qFrame->gameWindow(); // Only accept text. URLs are handled by the main window instead. if (e->mimeData()->hasText() and not e->mimeData()->hasUrls() and (gameWindow and gameWindow->acceptsText())) { e->acceptProposedAction(); } } void QTadsFrame::dropEvent( QDropEvent* e ) { qFrame->gameWindow()->insertText(e->mimeData()->text()); } CHtmlSysWinGroupQt::CHtmlSysWinGroupQt() : fConfDialog(0), fGameInfoDialog(0), fAboutBoxDialog(0), fAboutBox(0), fAboutQtadsDialog(0), fNetManager(0), fHttpRedirectCount(0), fWantsToQuit(false), fSilentIfNoUpdates(false) { //qDebug() << Q_FUNC_INFO << "called"; Q_ASSERT(qWinGroup == 0); // Make sure we can drag&drop (files in our case) into the main window. this->setAcceptDrops(true); // We make our menu bar parentless so it will be shared by all our windows // in Mac OS X. QMenuBar* menuBar = new QMenuBar(0); // "Game" menu. QMenu* menu = menuBar->addMenu(tr("&Game")); QAction* act = new QAction(tr("&Open") + QString::fromLatin1("..."), this); #if QT_VERSION >= 0x040600 act->setIcon(QIcon::fromTheme(QString::fromLatin1("document-open"))); #endif act->setShortcuts(QKeySequence::Open); menu->addAction(act); connect(act, SIGNAL(triggered()), this, SLOT(fOpenNewGame())); act = new QAction(tr("Open &Recent"), this); #if QT_VERSION >= 0x040600 act->setIcon(QIcon::fromTheme(QString::fromLatin1("document-open-recent"))); #endif this->fRecentGamesMenu = new QMenu(this); act->setMenu(this->fRecentGamesMenu); menu->addAction(act); connect(this->fRecentGamesMenu, SIGNAL(triggered(QAction*)), this, SLOT(fRecentGameTriggered(QAction*))); this->fRestartCurrentGameAction = new QAction(tr("Re&start"), this); #if QT_VERSION >= 0x040600 this->fRestartCurrentGameAction->setIcon(QIcon::fromTheme(QString::fromLatin1("view-refresh"))); #endif this->fRestartCurrentGameAction->setShortcut(QKeySequence(QString::fromLatin1("Ctrl+R"))); menu->addAction(this->fRestartCurrentGameAction); this->fRestartCurrentGameAction->setEnabled(false); connect(this->fRestartCurrentGameAction, SIGNAL(triggered()), this, SLOT(fRestartCurrentGame())); this->fEndCurrentGameAction = new QAction(tr("Qui&t"), this); this->fEndCurrentGameAction->setMenuRole(QAction::NoRole); #if QT_VERSION >= 0x040600 this->fEndCurrentGameAction->setIcon(QIcon::fromTheme(QString::fromLatin1("process-stop"))); #endif this->fEndCurrentGameAction->setShortcuts(QKeySequence::Close); menu->addAction(this->fEndCurrentGameAction); this->fEndCurrentGameAction->setEnabled(false); connect(this->fEndCurrentGameAction, SIGNAL(triggered()), this, SLOT(fEndCurrentGame())); menu->addSeparator(); this->fAboutGameAction = new QAction(tr("&About This Game"), this); this->fAboutGameAction->setMenuRole(QAction::NoRole); this->fAboutGameAction->setEnabled(false); menu->addAction(this->fAboutGameAction); connect(this->fAboutGameAction, SIGNAL(triggered()), this, SLOT(fShowAboutGame())); this->fGameInfoAction = new QAction(tr("View Metadata"), this); #if QT_VERSION >= 0x040600 this->fGameInfoAction->setIcon(QIcon::fromTheme(QString::fromLatin1("document-properties"))); #endif menu->addAction(this->fGameInfoAction); this->fGameInfoAction->setEnabled(false); connect(this->fGameInfoAction, SIGNAL(triggered()), this, SLOT(fShowGameInfoDialog())); menu->addSeparator(); act = new QAction(tr("&Quit QTads"), this); act->setMenuRole(QAction::QuitRole); #if QT_VERSION >= 0x040600 act->setIcon(QIcon::fromTheme(QString::fromLatin1("application-exit"))); act->setShortcuts(QKeySequence::Quit); #endif menu->addAction(act); connect(act, SIGNAL(triggered()), this, SLOT(close())); // "Edit" menu. menu = menuBar->addMenu(tr("&Edit")); this->fCopyAction = new QAction(tr("&Copy"), this); #if QT_VERSION >= 0x040600 this->fCopyAction->setIcon(QIcon::fromTheme(QString::fromLatin1("edit-copy"))); this->fCopyAction->setShortcuts(QKeySequence::Copy); #endif this->fCopyAction->setEnabled(false); menu->addAction(this->fCopyAction); connect(this->fCopyAction, SIGNAL(triggered()), SLOT(copyToClipboard())); this->fPasteAction = new QAction(tr("&Paste"), this); #if QT_VERSION >= 0x040600 this->fPasteAction->setIcon(QIcon::fromTheme(QString::fromLatin1("edit-paste"))); this->fPasteAction->setShortcuts(QKeySequence::Paste); #endif this->fPasteAction->setDisabled(true); menu->addAction(this->fPasteAction); connect(this->fPasteAction, SIGNAL(triggered()), SLOT(pasteFromClipboard())); connect(QApplication::clipboard(), SIGNAL(dataChanged()), SLOT(updatePasteAction())); menu->addSeparator(); act = new QAction(tr("&Preferences..."), this); #if QT_VERSION >= 0x040600 act->setIcon(QIcon::fromTheme(QString::fromLatin1("configure"))); act->setShortcuts(QKeySequence::Preferences); #endif menu->addAction(act); connect(act, SIGNAL(triggered()), this, SLOT(fShowConfDialog())); // "Help" menu. menu = menuBar->addMenu(tr("&Help")); this->fAboutQtadsAction = new QAction(tr("A&bout QTads"), this); #if QT_VERSION >= 0x040600 this->fAboutQtadsAction->setIcon(QIcon::fromTheme(QString::fromLatin1("help-about"))); #endif menu->addAction(this->fAboutQtadsAction); connect(this->fAboutQtadsAction, SIGNAL(triggered()), this, SLOT(fShowAboutQtads())); act = new QAction(tr("&Check for Updates"), this); act->setMenuRole(QAction::ApplicationSpecificRole); menu->addAction(act); connect(act, SIGNAL(triggered()), this, SLOT(fCheckForUpdates())); this->setMenuBar(menuBar); // Create a default status bar. this->statusBar(); // Set up our central widget. this->fFrame = new QTadsFrame(this); this->fFrame->setFrameStyle(QFrame::NoFrame | QFrame::Plain); this->fFrame->setLineWidth(0); this->fFrame->setContentsMargins(0,0,0,0); this->setCentralWidget(this->fFrame); // Use a sane minimum size; by default Qt would allow us to be resized // to almost zero. this->setMinimumSize(240, 180); // Receive notification when a game is about to quit/start so we can // enable/disable related actions. connect(qFrame, SIGNAL(gameQuitting()), this, SLOT(fNotifyGameQuitting())); connect(qFrame, SIGNAL(gameHasQuit()), this, SLOT(fNotifyGameQuitting())); connect(qFrame, SIGNAL(gameStarting()), this, SLOT(fNotifyGameStarting())); qWinGroup = this; } CHtmlSysWinGroupQt::~CHtmlSysWinGroupQt() { Q_ASSERT(qWinGroup != 0); qWinGroup = 0; } bool CHtmlSysWinGroupQt::fAskQuitGameDialog() { if (not qFrame->settings()->confirmQuitGame or not qFrame->gameRunning()) { return true; } QMessageBox* msgBox = new QMessageBox(QMessageBox::Question, tr("Quit Current Game") + QString::fromLatin1(" - ") + qFrame->applicationName(), tr("If you didn't save the current game, all progress will" " be lost. Do you wish to quit the game?"), QMessageBox::Yes | QMessageBox::Cancel, this); msgBox->setAttribute(Qt::WA_DeleteOnClose); msgBox->setDefaultButton(QMessageBox::Cancel); #ifdef Q_OS_MAC msgBox->setIconPixmap(QPixmap(QString::fromLatin1(":/qtads_72x72.png"))); // This presents the dialog as a sheet in OS X. msgBox->setWindowModality(Qt::WindowModal); #endif if (msgBox->exec() == QMessageBox::Yes) { return true; } return false; } bool CHtmlSysWinGroupQt::fAskRestartGameDialog() { if (not qFrame->settings()->confirmRestartGame or not qFrame->gameRunning()) { return true; } QMessageBox* msgBox = new QMessageBox(QMessageBox::Question, tr("Restart Current Game") + QString::fromLatin1(" - ") + qFrame->applicationName(), tr("If you didn't save the current game, all progress will be lost." " Do you wish to restart the game?"), QMessageBox::Yes | QMessageBox::Cancel, this); msgBox->setAttribute(Qt::WA_DeleteOnClose); msgBox->setDefaultButton(QMessageBox::Cancel); #ifdef Q_OS_MAC msgBox->setIconPixmap(QPixmap(QString::fromLatin1(":/qtads_72x72.png"))); // This presents the dialog as a sheet in OS X. msgBox->setWindowModality(Qt::WindowModal); #endif if (msgBox->exec() == QMessageBox::Yes) { return true; } return false; } static QNetworkReply* sendNetRequest( QNetworkAccessManager* nam, const QUrl& url ) { QByteArray userAgent = "QTads/"; userAgent += QByteArray(QTADS_VERSION).replace(' ', '_'); userAgent += " Qt/"; userAgent += qVersion(); QNetworkRequest req(url); req.setRawHeader("User-Agent", userAgent); req.setRawHeader("Connection", "close"); return nam->get(req); } void CHtmlSysWinGroupQt::fCheckForUpdates() { // If there's already an update check in progress, don't do anything. if (this->fNetManager != 0) { return; } this->fNetManager = new QNetworkAccessManager(this); connect(this->fNetManager, SIGNAL(finished(QNetworkReply*)), SLOT(fReplyFinished(QNetworkReply*))); this->fHttpRedirectCount = 0; this->fReply = sendNetRequest(this->fNetManager, QUrl(QString::fromLatin1("http://qtads.sourceforge.net/currentversion"))); } static void showUpdateErrorMsg( const QString& detailedText ) { QMessageBox* msg = new QMessageBox( QMessageBox::Critical, QObject::tr("Check for Updates - Error"), QObject::tr("It was not possible to retrieve update information. Please try again later," " as the problem might be temporary. If the problem persists, feel free to" " contact the author.") ); msg->setAttribute(Qt::WA_DeleteOnClose); msg->setDetailedText(detailedText); msg->show(); } void CHtmlSysWinGroupQt::fReplyFinished( QNetworkReply* reply ) { if (reply->error() != QNetworkReply::NoError) { showUpdateErrorMsg(this->fReply->errorString()); this->fNetManager->deleteLater(); this->fReply->deleteLater(); this->fNetManager = 0; return; } // If we get an HTTP redirect, retry the request with the new URL. if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 302) { ++this->fHttpRedirectCount; // If we got more than 5 redirects by now, something's wrong. Abort. if (this->fHttpRedirectCount > 5) { showUpdateErrorMsg(tr("Too many HTTP redirects")); this->fNetManager->deleteLater(); this->fReply->deleteLater(); this->fNetManager = 0; return; } QUrl newUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); if (newUrl.isRelative()) { newUrl = reply->url().resolved(newUrl); } reply->deleteLater(); this->fReply = sendNetRequest(this->fNetManager, newUrl); return; } // If we get here, then anything else than an HTTP 200 status is an error. if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { QString errMsg = tr("Expected HTTP status code 200, got:\n"); errMsg += reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString(); QString httpReason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); if (not httpReason.isEmpty()) { errMsg += QString::fromLatin1(" (") + httpReason + QString::fromLatin1(")"); } showUpdateErrorMsg(errMsg); this->fNetManager->deleteLater(); this->fReply->deleteLater(); this->fNetManager = 0; return; } // Convert current version to hex. QString str(QString::fromLatin1(QTADS_VERSION)); // If this is a git snapshot, strip the " git". if (str.endsWith(QString::fromLatin1(" git"), Qt::CaseInsensitive)) { str.chop(4); } QStringList strList = str.split(QChar::fromLatin1('.')); int curVersion = QT_VERSION_CHECK(strList.at(0).toInt(), strList.at(1).toInt(), strList.at(2).toInt()); // Do the same with the retrieved version. str = QString::fromUtf8(reply->readLine(10)); int newVersion = 0; if (str.length() > 3) { // Chop the newline at the end, if there is one. if (str.endsWith(QChar::fromLatin1('\n'))) { str.chop(1); } strList = str.split(QChar::fromLatin1('.')); newVersion = QT_VERSION_CHECK(strList.at(0).toInt(), strList.at(1).toInt(), strList.at(2).toInt()); } QMessageBox* msgBox = new QMessageBox(this); msgBox->setAttribute(Qt::WA_DeleteOnClose); msgBox->setTextFormat(Qt::RichText); msgBox->setWindowTitle(tr("Check for Updates")); if (newVersion > curVersion) { // There's a new version available. Retrieve the rest of the remote // file. For security, provide a sane upper limit of characters to // read. QString text; while (reply->canReadLine() and text.length() < 2500) { text.append(QString::fromUtf8(reply->readLine(100))); } // Remove that last newline. if (text.endsWith(QString::fromLatin1("\n"))) { text.truncate(text.length() - 1); } if (text.length() > 2) { msgBox->setDetailedText(text); } #ifdef Q_OS_MAC msgBox->setIconPixmap(QPixmap(QString::fromLatin1(":/qtads_72x72.png"))); #else msgBox->setIcon(QMessageBox::Question); #endif msgBox->setText(tr("A newer version of QTads is available. Do you want to visit the download page?")); msgBox->setInformativeText(tr("Note that this is only a check for new versions. Nothing will be downloaded" " or installed automatically.")); msgBox->setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox->setDefaultButton(QMessageBox::Yes); if (msgBox->exec() == QMessageBox::Yes) { QDesktopServices::openUrl(QUrl(QString::fromLatin1("http://qtads.sourceforge.net/downloads.shtml"))); } } else if (not this->fSilentIfNoUpdates) { #ifdef Q_OS_MAC msgBox->setIconPixmap(QPixmap(QString::fromLatin1(":/qtads_72x72.png"))); #else msgBox->setIcon(QMessageBox::Information); #endif msgBox->setText(tr("This version of QTads is up to date.")); msgBox->exec(); } this->fSilentIfNoUpdates = false; this->fNetManager->deleteLater(); this->fReply->deleteLater(); this->fNetManager = 0; qFrame->settings()->lastUpdateDate = QDate::currentDate(); } void CHtmlSysWinGroupQt::fShowGameInfoDialog() { // If the dialog is already open, simply activate and raise it. if (this->fGameInfoDialog != 0) { this->fGameInfoDialog->activateWindow(); this->fGameInfoDialog->raise(); return; } this->fGameInfoDialog = new GameInfoDialog(qStrToFname(qFrame->gameFile()), this); this->fGameInfoDialog->setWindowTitle(tr("Game Information")); connect(fGameInfoDialog, SIGNAL(finished(int)), SLOT(fHideGameInfoDialog())); connect(fGameInfoDialog, SIGNAL(finished(int)), SLOT(fActivateWindow())); this->fGameInfoDialog->show(); } void CHtmlSysWinGroupQt::fHideGameInfoDialog() { if (this->fGameInfoDialog != 0) { this->fGameInfoDialog->deleteLater(); this->fGameInfoDialog = 0; } } void CHtmlSysWinGroupQt::fShowConfDialog() { // If the dialog is already open, simply activate and raise it. if (this->fConfDialog != 0) { this->fConfDialog->activateWindow(); this->fConfDialog->raise(); return; } this->fConfDialog = new ConfDialog(this); this->fConfDialog->setWindowTitle(tr("QTads Preferences")); connect(this->fConfDialog, SIGNAL(finished(int)), this, SLOT(fHideConfDialog())); #ifdef Q_OS_MAC // There's a bug in Qt for OS X that results in a visual glitch with // QFontComboBox widgets inside QFormLayouts. Making the dialog 4 pixels // higher fixes it. // // See: http://bugreports.qt.nokia.com/browse/QTBUG-10460 this->fConfDialog->layout()->activate(); this->fConfDialog->setMinimumHeight(this->fConfDialog->minimumHeight() + 4); #endif connect(fConfDialog, SIGNAL(finished(int)), SLOT(fActivateWindow())); this->fConfDialog->show(); } void CHtmlSysWinGroupQt::fHideConfDialog() { if (this->fConfDialog != 0) { this->fConfDialog->deleteLater(); this->fConfDialog = 0; } } void CHtmlSysWinGroupQt::fShowAboutGame() { if (this->fAboutBox == 0) { return; } if (this->fAboutBoxDialog != 0 and this->fAboutBoxDialog->isVisible()) { this->fAboutBoxDialog->activateWindow(); this->fAboutBoxDialog->raise(); return; } if (this->fAboutBoxDialog == 0) { this->fAboutBoxDialog = new QDialog(this, Qt::WindowTitleHint | Qt::WindowSystemMenuHint); this->fAboutBoxDialog->setWindowTitle(tr("About This Game")); this->fAboutBox->setParent(this->fAboutBoxDialog); QVBoxLayout* layout = new QVBoxLayout(this->fAboutBoxDialog); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(this->fAboutBox); connect(this->fAboutBoxDialog, SIGNAL(finished(int)), SLOT(fHideAboutGame())); } this->fAboutBoxDialog->resize(this->fAboutBox->size()); connect(fAboutBoxDialog, SIGNAL(finished(int)), SLOT(fActivateWindow())); this->fAboutBoxDialog->show(); } void CHtmlSysWinGroupQt::fHideAboutGame() { // Destroy the dialog, but not the about box HTML banner. We reparent // the banner so it won't be automatically deleted by its parent. this->fAboutBox->setParent(0); this->fAboutBoxDialog->hide(); this->fAboutBoxDialog->deleteLater(); this->fAboutBoxDialog = 0; } void CHtmlSysWinGroupQt::fShowAboutQtads() { // If the dialog is already open, simply activate and raise it. if (this->fAboutQtadsDialog != 0) { this->fAboutQtadsDialog->activateWindow(); this->fAboutQtadsDialog->raise(); return; } this->fAboutQtadsDialog = new AboutQtadsDialog(this); connect(this->fAboutQtadsDialog, SIGNAL(finished(int)), this, SLOT(fHideAboutQtads())); #ifdef Q_OS_MAC // Similar bug to the config dialog one. Again, 4 pixels higher fixes it. this->fAboutQtadsDialog->layout()->activate(); this->fAboutQtadsDialog->setMinimumHeight(this->fAboutQtadsDialog->minimumHeight() + 4); #endif connect(fAboutQtadsDialog, SIGNAL(finished(int)), SLOT(fActivateWindow())); this->fAboutQtadsDialog->show(); } void CHtmlSysWinGroupQt::fHideAboutQtads() { if (this->fAboutQtadsDialog != 0) { this->fAboutQtadsDialog->deleteLater(); this->fAboutQtadsDialog = 0; } } void CHtmlSysWinGroupQt::fOpenNewGame() { const QString& fname = QFileDialog::getOpenFileName(0, tr("Choose the TADS game you wish to run"), qFrame->settings()->lastFileOpenDir, tr("TADS Games") + QString::fromLatin1(" (*.gam *.Gam *.GAM *.t3 *.T3)")); this->activateWindow(); if (not fname.isEmpty()) { qFrame->settings()->lastFileOpenDir = QFileInfo(fname).absolutePath(); qFrame->setNextGame(fname); } } void CHtmlSysWinGroupQt::fRecentGameTriggered( QAction* action ) { if (this->fAskQuitGameDialog()) { qFrame->setNextGame(action->statusTip()); } } void CHtmlSysWinGroupQt::fEndCurrentGame() { if (this->fAskQuitGameDialog()) { qFrame->setGameRunning(false); } } void CHtmlSysWinGroupQt::fRestartCurrentGame() { if (this->fAskRestartGameDialog()) { qFrame->setNextGame(qFrame->gameFile()); } } void CHtmlSysWinGroupQt::fNotifyGameQuitting() { this->fGameInfoAction->setEnabled(false); this->fRestartCurrentGameAction->setEnabled(false); this->fEndCurrentGameAction->setEnabled(false); } void CHtmlSysWinGroupQt::fNotifyGameStarting() { this->fHideGameInfoDialog(); this->fGameInfoAction->setEnabled(GameInfoDialog::gameHasMetaInfo(qStrToFname(qFrame->gameFile()))); this->fRestartCurrentGameAction->setEnabled(true); this->fEndCurrentGameAction->setEnabled(true); } void CHtmlSysWinGroupQt::closeEvent( QCloseEvent* e ) { if (not qFrame->gameRunning()) { return; } QMessageBox* msgBox = new QMessageBox(QMessageBox::Question, tr("Quit QTads"), tr("A game is currently running. Abandon the game and quit the interpreter?"), QMessageBox::Yes | QMessageBox::Cancel, this); msgBox->setAttribute(Qt::WA_DeleteOnClose); msgBox->setDefaultButton(QMessageBox::Cancel); #ifdef Q_OS_MAC msgBox->setIconPixmap(QPixmap(QString::fromLatin1(":/qtads_72x72.png"))); // This presents the dialog as a sheet in OS X. msgBox->setWindowModality(Qt::WindowModal); #endif if (not qFrame->settings()->confirmQuitGame or msgBox->exec() == QMessageBox::Yes) { qFrame->setGameRunning(false); // Make sure the VM knows that we're closing. this->fWantsToQuit = true; e->accept(); } else { e->ignore(); } } void CHtmlSysWinGroupQt::dragEnterEvent( QDragEnterEvent* e ) { // Only accept the event if there is exactly one URL which points to a // local file. if (e->mimeData()->hasUrls() and e->mimeData()->urls().size() == 1 and not e->mimeData()->urls().at(0).toLocalFile().isEmpty()) { e->acceptProposedAction(); } } void CHtmlSysWinGroupQt::dropEvent( QDropEvent* e ) { e->acceptProposedAction(); this->fGameFileFromDropEvent = e->mimeData()->urls().at(0).toLocalFile(); QTimer::singleShot(100, this, SLOT(fRunDropEventFile())); } void CHtmlSysWinGroupQt::copyToClipboard() { const QString& selectedText = DisplayWidget::selectedText(); if (not selectedText.isEmpty()) { QApplication::clipboard()->setText(selectedText); } } void CHtmlSysWinGroupQt::pasteFromClipboard() { qFrame->gameWindow()->insertText(QApplication::clipboard()->text()); } void CHtmlSysWinGroupQt::fRunDropEventFile() { if (this->fAskQuitGameDialog()) { qFrame->setNextGame(this->fGameFileFromDropEvent); } } void CHtmlSysWinGroupQt::updatePasteAction() { this->fPasteAction->setDisabled(QApplication::clipboard()->text().isEmpty() or not qFrame->gameWindow()); } CHtmlSysWinAboutBoxQt* CHtmlSysWinGroupQt::createAboutBox( class CHtmlFormatter* formatter ) { // If there's already an "about" box, destroy it first. if (this->fAboutBoxDialog != 0) { this->fHideAboutGame(); } else { this->fAboutGameAction->setEnabled(true); } // We will reparent the banner when we show the actual dialog. this->fAboutBox = new CHtmlSysWinAboutBoxQt(formatter, 0); // Only set the width to something comfortable. The height will be // calculated later when set_banner_size() is called on the about box. this->fAboutBox->resize(500, 0); return this->fAboutBox; } void CHtmlSysWinGroupQt::deleteAboutBox() { if (this->fAboutBox == 0) { return; } if (this->fAboutBoxDialog != 0) { this->fHideAboutGame(); } delete this->fAboutBox; this->fAboutBox = 0; this->fAboutGameAction->setEnabled(false); } void CHtmlSysWinGroupQt::updateRecentGames() { // We simply clear the menu of all items and re-populate it. this->fRecentGamesMenu->clear(); // If the list is empty, disable the menu. if (qFrame->settings()->recentGamesList.isEmpty()) { this->fRecentGamesMenu->setEnabled(false); return; } // The list is not empty. If the menu was disabled, enable it. if (not this->fRecentGamesMenu->isEnabled()) { this->fRecentGamesMenu->setEnabled(true); } // Populate it. const QStringList& list = qFrame->settings()->recentGamesList; for (int i = 0; i < list.size(); ++i) { QString gameName = GameInfoDialog::getMetaInfo(qStrToFname(list.at(i))).plainGameName; if (gameName.isEmpty()) { gameName = QFileInfo(list.at(i)).fileName(); } gameName = gameName.replace(QString::fromLatin1("&"), QString::fromLatin1("&&")); QAction* act = this->fRecentGamesMenu->addAction(gameName); // Elide the text in case it's too long. act->setText(QFontMetrics(act->font()).elidedText(gameName, Qt::ElideRight, 300)); act->setStatusTip(QString(list.at(i))); } } void CHtmlSysWinGroupQt::checkForUpdates() { this->fSilentIfNoUpdates = true; this->fCheckForUpdates(); } void CHtmlSysWinGroupQt::enableCopyAction( bool f ) { this->fCopyAction->setEnabled(f); } #ifdef Q_OS_MAC bool CHtmlSysWinGroupQt::handleFileOpenEvent( class QFileOpenEvent* e ) { if (e->file().isEmpty()) { e->ignore(); return false; } e->accept(); this->fGameFileFromDropEvent = e->file(); QTimer::singleShot(100, this, SLOT(fRunDropEventFile())); return true; } #endif oshtml_charset_id_t CHtmlSysWinGroupQt::get_default_win_charset() const { //qDebug() << Q_FUNC_INFO << "called"; return 0; } size_t CHtmlSysWinGroupQt::xlat_html4_entity( textchar_t* result, size_t result_size, unsigned int charval, oshtml_charset_id_t*, int* changed_charset ) { //qDebug() << Q_FUNC_INFO << "called"; Q_ASSERT(result != 0); // HTML4 entities are Unicode characters, which means the QChar(uint) ctor // will do the right thing. QString s = QString(QChar(charval)); strcpy(result, s.toUtf8()); if (changed_charset != 0) { *changed_charset = false; } return s.toUtf8().length(); } qtads-2.1.7/src/syswingroup.h000066400000000000000000000115001265017072300162060ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef SYSWINGROUP_H #define SYSWINGROUP_H #include #include #include #include "htmlsys.h" #include "config.h" class QTadsFrame: public QFrame { Q_OBJECT protected: void resizeEvent( QResizeEvent* e ) override; void dragEnterEvent( QDragEnterEvent* e ) override; void dropEvent( QDropEvent* e ) override; public: QTadsFrame( QWidget* parent ) : QFrame(parent) { this->setAcceptDrops(true); } }; /* Tads HTML layer class whose interface needs to be implemented by the * interpreter. * * See htmltads/htmlsys.h and htmltads/notes/porting.htm for information * about this class. */ class CHtmlSysWinGroupQt: public QMainWindow, public CHtmlSysWinGroup { Q_OBJECT private: class ConfDialog* fConfDialog; class GameInfoDialog* fGameInfoDialog; QTadsFrame* fFrame; class QDialog* fAboutBoxDialog; class CHtmlSysWinAboutBoxQt* fAboutBox; class AboutQtadsDialog* fAboutQtadsDialog; class QMenu* fRecentGamesMenu; class QAction* fAboutGameAction; class QAction* fEndCurrentGameAction; class QAction* fRestartCurrentGameAction; class QAction* fGameInfoAction; class QAction* fAboutQtadsAction; class QAction* fCopyAction; class QAction* fPasteAction; class QNetworkAccessManager* fNetManager; class QNetworkReply* fReply; unsigned int fHttpRedirectCount; QString fGameFileFromDropEvent; // Are we trying to quit the application? bool fWantsToQuit; // If this is set, we won't bother the user with a dialog if the // update checker finds no available updates. bool fSilentIfNoUpdates; bool fAskQuitGameDialog(); bool fAskRestartGameDialog(); private slots: void fCheckForUpdates(); void fReplyFinished( QNetworkReply* reply ); void fShowGameInfoDialog(); void fHideGameInfoDialog(); void fShowConfDialog(); void fHideConfDialog(); void fShowAboutGame(); void fHideAboutGame(); void fShowAboutQtads(); void fHideAboutQtads(); void fOpenNewGame(); void fRecentGameTriggered( QAction* action ); void fEndCurrentGame(); void fRestartCurrentGame(); void fNotifyGameQuitting(); void fNotifyGameStarting(); void fRunDropEventFile(); void fActivateWindow() { this->activateWindow(); } protected: void closeEvent( QCloseEvent* e ) override; void dragEnterEvent( QDragEnterEvent* e ) override; void dropEvent( QDropEvent* e ) override; public slots: // Copy the current selection to the clipboard. No action is performed if // there's no selection in any of the game windows. void copyToClipboard(); // Paste the current contents of the clipboard into the input window's // editor. Does nothing if the input window is not currently in line input // mode. void pasteFromClipboard(); // Enable or disable the paste action according to whether there's text // in the clipboard and a game window currently exists. void updatePasteAction(); public: CHtmlSysWinGroupQt(); ~CHtmlSysWinGroupQt() override; CHtmlSysWinAboutBoxQt* createAboutBox( class CHtmlFormatter* formatter ); void deleteAboutBox(); CHtmlSysWinAboutBoxQt* aboutBox() { return this->fAboutBox; } void updateRecentGames(); void checkForUpdates(); bool wantsToQuit() const { return this->fWantsToQuit; } void enableCopyAction( bool f ); #ifdef Q_OS_MAC // Handler for FileOpen events. They only occur in OS X. bool handleFileOpenEvent( class QFileOpenEvent* e ); #endif // // CHtmlSysWinGroup interface implementation. // oshtml_charset_id_t get_default_win_charset() const override; size_t xlat_html4_entity( textchar_t* result, size_t result_size, unsigned int charval, oshtml_charset_id_t* charset, int* changed_charset ) override; }; #endif qtads-2.1.7/src/syswininput.cc000066400000000000000000000641171265017072300163630ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include #include #include #include #include #include #include #include #include #include #include "settings.h" #include "dispwidgetinput.h" #include "syswininput.h" #include "htmlfmt.h" #include "htmlinp.h" #include "htmltags.h" #include "htmlprs.h" CHtmlSysWinInputQt::CHtmlSysWinInputQt( CHtmlFormatter* formatter, QWidget* parent ) : CHtmlSysWinQt(formatter, parent), fInputMode(NoInput), fInputReady(false), fRestoreFromCancel(false), fLastKeyEvent(Qt::Key_Any), fTag(0) { this->fInputBuffer = new textchar_t[1024]; this->fInputBufferSize = 1024; this->fTadsBuffer = new CHtmlInputBuf(this->fInputBuffer, 1024, 100); this->fTadsBuffer->set_utf8_mode(true); // Replace the default display widget with an input display widget. this->formatter_->unset_win(); delete this->dispWidget; this->dispWidget = new DisplayWidgetInput(this, formatter, this->fTadsBuffer); this->fCastDispWidget = static_cast(this->dispWidget); this->setWidget(this->dispWidget); this->formatter_->set_win(this, &margins); QPalette p(this->palette()); p.setColor(QPalette::Base, qFrame->settings()->mainBgColor); p.setColor(QPalette::Text, qFrame->settings()->mainTextColor); this->setPalette(p); } CHtmlSysWinInputQt::~CHtmlSysWinInputQt() { delete this->fTadsBuffer; delete[] this->fInputBuffer; } void CHtmlSysWinInputQt::fStartKeypressInput() { this->fInputReady = false; this->fInputMode = SingleKeyInput; this->fHrefEvent.clear(); } void CHtmlSysWinInputQt::fProcessPagePauseQueue() { if (this->fPagePauseQueue.isEmpty()) { return; } QLabel moreText(tr("*** MORE *** [press space or enter to continue]")); // Display a permanent QLabel instead of a temporary message. This allows // other status bar messages (like when hovering over hyperlinks) to // temporary remove the MORE text instead of replacing it. moreText.setFrameStyle(QFrame::NoFrame | QFrame::Plain); moreText.setLineWidth(0); moreText.setContentsMargins(0, 0, 0, 0); qWinGroup->statusBar()->setUpdatesEnabled(false); qWinGroup->statusBar()->addWidget(&moreText); qWinGroup->statusBar()->setUpdatesEnabled(true); this->fInputMode = PagePauseInput; while (this->fInputMode == PagePauseInput and qFrame->gameRunning()) { qFrame->advanceEventLoop(QEventLoop::WaitForMoreEvents | QEventLoop::AllEvents); } qWinGroup->statusBar()->setUpdatesEnabled(false); qWinGroup->statusBar()->removeWidget(&moreText); qWinGroup->statusBar()->setUpdatesEnabled(true); } void CHtmlSysWinInputQt::fUpdateInputFormatter() { if (this->fTag == 0) { return; } this->fTag->setlen(static_cast(this->formatter_), this->fTadsBuffer->getlen()); if (this->fTag->ready_to_format()) { this->fTag->format(static_cast(this), this->formatter_); this->verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum); } this->fCastDispWidget->updateCursorPos(this->formatter_, false, true); } void CHtmlSysWinInputQt::setCursorHeight( unsigned height ) { this->fCastDispWidget->setCursorHeight(height); this->fCastDispWidget->updateCursorPos(this->formatter_, true, true); } void CHtmlSysWinInputQt::processCommand( const textchar_t* cmd, size_t len, int append, int enter, int os_cmd_id ) { if (not qFrame->gameRunning()) { return; } // If the command starts with "http:", "ftp:", "news:" "mailto:", or // "telnet:", try to open it in the external application that handles it. if (strnicmp(cmd, "http:", 5) == 0 || strnicmp(cmd, "ftp:", 4) == 0) { // Parse http and ftp URLs in strict mode. QDesktopServices::openUrl(QUrl::fromEncoded(cmd, QUrl::StrictMode)); return; } if (strnicmp(cmd, "news:", 5) == 0 || strnicmp(cmd, "mailto:", 7) == 0 || strnicmp(cmd, "telnet:", 7) == 0) { // Parse news, mailto and telnet URLs in tolerant mode. QDesktopServices::openUrl(QUrl::fromEncoded(cmd, QUrl::TolerantMode)); return; } // If we're not currently accepting input, ignore this. if (this->fInputMode == NoInput or this->fInputMode == PagePauseInput) { return; } // If we're waiting for a single key-press event and the command isn't some // sort of special OS_CMD command, it's an HREF event. if (this->fInputMode == SingleKeyInput and os_cmd_id == OS_CMD_NONE) { // If the HREF string is empty, use a single space so that we know that // an HREF event actually occured. if (cmd[0] == '\0') { this->fHrefEvent = QChar::fromLatin1(' '); } else { this->fHrefEvent = QString::fromUtf8(cmd); } return; } // If we're not in APPEND mode, clear out the current command; otherwise, // make sure we're at the end of the current text. if (!append) { this->fTadsBuffer->del_line(); } else { this->fTadsBuffer->end_of_line(false); } // Add the command string. this->fTadsBuffer->add_string(cmd, len, true); this->fTag->setlen(static_cast(this->formatter_), this->fTadsBuffer->getlen()); if (this->fTag->ready_to_format()) { this->fTag->format(static_cast(this), this->formatter_); } this->fCastDispWidget->updateCursorPos(this->formatter_, false, true); // If 'enter' is true, indicate that we've finished reading the command, so // that getInput() will return the new command as its result; otherwise, // let the player continue editing this command. if (enter) { this->fInputReady = true; this->fInputMode = NoInput; emit inputReady(); } } void CHtmlSysWinInputQt::resizeEvent( QResizeEvent* event ) { CHtmlSysWinQt::resizeEvent(event); if (this->fCastDispWidget->isCursorVisible()) { this->fCastDispWidget->updateCursorPos(this->formatter_, true, true); } } void CHtmlSysWinInputQt::keyPressEvent( QKeyEvent* e ) { //qDebug() << Q_FUNC_INFO; //qDebug() << "Key pressed:" << hex << e->key(); if (this->fInputMode == NoInput or not qFrame->gameRunning()) { QScrollArea::keyPressEvent(e); return; } if (this->fInputMode == SingleKeyInput) { this->singleKeyPressEvent(e); return; } if (this->fInputMode == PagePauseInput) { if (e->key() == Qt::Key_Space) { // Scroll down by a page. this->fPagePauseQueue.head()->scrollDown(true, false); #if QT_VERSION >= 0x040500 } else if (e->matches(QKeySequence::InsertParagraphSeparator)) { #else } else if (e->key() == Qt::Key_Enter or e->key() == Qt::Key_Return) { #endif // Scroll down by a line. this->fPagePauseQueue.head()->scrollDown(true, true); } return; } if (e->matches(QKeySequence::MoveToStartOfLine) or e->matches(QKeySequence::MoveToStartOfBlock)) { this->fTadsBuffer->start_of_line(false); this->fCastDispWidget->clearSelection(); } else if (e->matches(QKeySequence::MoveToEndOfLine) or e->matches(QKeySequence::MoveToEndOfBlock)) { this->fTadsBuffer->end_of_line(false); this->fCastDispWidget->clearSelection(); #if QT_VERSION >= 0x040500 } else if (e->matches(QKeySequence::InsertParagraphSeparator)) { #else } else if (e->key() == Qt::Key_Enter or e->key() == Qt::Key_Return) { #endif this->fInputReady = true; this->fInputMode = NoInput; this->fTadsBuffer->add_hist(); this->fCastDispWidget->clearSelection(); emit inputReady(); return; } else if (e->matches(QKeySequence::Delete)) { this->fTadsBuffer->del_right(); this->fCastDispWidget->clearSelection(); } else if (e->matches(QKeySequence::DeleteEndOfWord)) { this->fTadsBuffer->move_right(true, true); this->fTadsBuffer->del_selection(); this->fCastDispWidget->clearSelection(); } else if (e->matches(QKeySequence::DeleteStartOfWord)) { this->fTadsBuffer->move_left(true, true); this->fTadsBuffer->del_selection(); this->fCastDispWidget->clearSelection(); } else if (e->matches(QKeySequence::MoveToPreviousChar)) { this->fTadsBuffer->move_left(false, false); this->fCastDispWidget->clearSelection(); } else if (e->matches(QKeySequence::MoveToNextChar)) { this->fTadsBuffer->move_right(false, false); this->fCastDispWidget->clearSelection(); } else if (e->matches(QKeySequence::MoveToPreviousWord)) { this->fTadsBuffer->move_left(false, true); this->fCastDispWidget->clearSelection(); } else if (e->matches(QKeySequence::MoveToNextWord)) { this->fTadsBuffer->move_right(false, true); this->fCastDispWidget->clearSelection(); } else if (e->matches(QKeySequence::MoveToPreviousLine)) { this->fTadsBuffer->select_prev_hist(); this->fCastDispWidget->clearSelection(); } else if (e->matches(QKeySequence::MoveToNextLine)) { this->fTadsBuffer->select_next_hist(); this->fCastDispWidget->clearSelection(); } else if (e->matches(QKeySequence::Find)) { this->fTadsBuffer->select_prev_hist_prefix(); this->fCastDispWidget->clearSelection(); } else if (e->matches(QKeySequence::SelectPreviousChar)) { if (not this->fTadsBuffer->has_sel_range()) { this->fCastDispWidget->clearSelection(); } this->fTadsBuffer->move_left(true, false); } else if (e->matches(QKeySequence::SelectNextChar)) { if (not this->fTadsBuffer->has_sel_range()) { this->fCastDispWidget->clearSelection(); } this->fTadsBuffer->move_right(true, false); } else if (e->matches(QKeySequence::SelectPreviousWord)) { if (not this->fTadsBuffer->has_sel_range()) { this->fCastDispWidget->clearSelection(); } this->fTadsBuffer->move_left(true, true); } else if (e->matches(QKeySequence::SelectNextWord)) { if (not this->fTadsBuffer->has_sel_range()) { this->fCastDispWidget->clearSelection(); } this->fTadsBuffer->move_right(true, true); } else if (e->matches(QKeySequence::SelectStartOfLine) or e->matches(QKeySequence::SelectStartOfBlock)) { if (not this->fTadsBuffer->has_sel_range()) { this->fCastDispWidget->clearSelection(); } this->fTadsBuffer->start_of_line(true); } else if (e->matches(QKeySequence::SelectEndOfLine) or e->matches(QKeySequence::SelectEndOfBlock)) { if (not this->fTadsBuffer->has_sel_range()) { this->fCastDispWidget->clearSelection(); } this->fTadsBuffer->end_of_line(true); } else if (e->matches(QKeySequence::SelectAll)) { if (not this->fTadsBuffer->has_sel_range()) { this->fCastDispWidget->clearSelection(); } this->fTadsBuffer->start_of_line(false); this->fTadsBuffer->end_of_line(true); } else if (e->matches(QKeySequence::Undo)) { if (not this->fTadsBuffer->has_sel_range()) { this->fCastDispWidget->clearSelection(); } this->fTadsBuffer->undo(); } else if (e->key() == Qt::Key_Backspace) { this->fTadsBuffer->backspace(); this->fCastDispWidget->clearSelection(); } else { QString strToAdd = e->text(); if (strToAdd.isEmpty() or not strToAdd.at(0).isPrint()) { QScrollArea::keyPressEvent(e); return; } this->insertText(strToAdd); } this->fUpdateInputFormatter(); } void CHtmlSysWinInputQt::inputMethodEvent( QInputMethodEvent* e ) { if (this->fInputMode == NoInput or not qFrame->gameRunning() or this->fInputMode == PagePauseInput or e->commitString().isEmpty()) { QScrollArea::inputMethodEvent(e); return; } if (this->fInputMode == SingleKeyInput) { this->fLastKeyEvent = static_cast(0); this->fLastKeyText = 0; // If the keypress doesn't correspond to exactly one character, ignore // it. if (e->commitString().size() != 1) { QScrollArea::inputMethodEvent(e); return; } this->fLastKeyText = e->commitString().at(0); this->fInputMode = NoInput; this->fInputReady = true; emit inputReady(); return; } this->insertText(e->commitString()); this->fUpdateInputFormatter(); } void CHtmlSysWinInputQt::singleKeyPressEvent( QKeyEvent* event ) { //qDebug() << Q_FUNC_INFO; Q_ASSERT(this->fInputMode == SingleKeyInput); this->fLastKeyEvent = static_cast(0); this->fLastKeyText = 0; switch (event->key()) { case 0: case Qt::Key_unknown: QScrollArea::keyPressEvent(event); return; case Qt::Key_Escape: case Qt::Key_Tab: case Qt::Key_Backspace: case Qt::Key_Return: case Qt::Key_Enter: case Qt::Key_Delete: case Qt::Key_Home: case Qt::Key_End: case Qt::Key_Left: case Qt::Key_Up: case Qt::Key_Right: case Qt::Key_Down: case Qt::Key_PageUp: case Qt::Key_PageDown: case Qt::Key_F1: case Qt::Key_F2: case Qt::Key_F3: case Qt::Key_F4: case Qt::Key_F5: case Qt::Key_F6: case Qt::Key_F7: case Qt::Key_F8: case Qt::Key_F9: case Qt::Key_F10: this->fLastKeyEvent = static_cast(event->key()); break; default: // If the keypress doesn't correspond to exactly one character, ignore // it. if (event->text().size() != 1) { QScrollArea::keyPressEvent(event); return; } this->fLastKeyText = event->text().at(0); } this->fInputMode = NoInput; this->fInputReady = true; emit inputReady(); } void CHtmlSysWinInputQt::getInput( textchar_t* buf, size_t buflen, unsigned long timeout, bool useTimeout, bool* timedOut ) { //qDebug() << Q_FUNC_INFO; Q_ASSERT(buf != 0); CHtmlFormatterInput* formatter = static_cast(this->formatter_); bool resuming = this->fTag != 0; // Correct any ill-formed HTML prior to input. formatter->prepare_for_input(); if (resuming) { // We're resuming; reuse our existing input tag with the new buffer. this->fTag->change_buf(formatter, this->fTadsBuffer->getbuf()); this->fTag->format(static_cast(this), this->formatter_); // We treat canceled inputs with reset=false as if they were resumes. // The difference is that in that case, we need to restore the cursor. if (this->fRestoreFromCancel) { this->fCastDispWidget->setCursorVisible(true); this->fCastDispWidget->updateCursorPos(formatter, false, true); this->fRestoreFromCancel = false; } } else { // Since we're not resuming, make sure that we've formatted all // available input and tell the formatter to begin a new input. while (formatter->more_to_do()) { formatter->do_formatting(); } this->fTadsBuffer->setbuf(this->fInputBuffer, buflen > this->fInputBufferSize ? this->fInputBufferSize - 1 : buflen - 1, 0); CHtmlTagTextInput* tag = formatter->begin_input(this->fTadsBuffer->getbuf(), 0); this->fTag = tag; this->fCastDispWidget->setInputTag(tag); if (tag->ready_to_format()) { tag->format(this, this->formatter_); } this->fTadsBuffer->show_caret(); this->fCastDispWidget->setCursorVisible(true); this->fCastDispWidget->updateCursorPos(formatter, false, true); this->fTadsBuffer->set_sel_range(0, 0, 0); } // If we have banners waiting in page-pause mode, process them first. this->fProcessPagePauseQueue(); this->fInputReady = false; this->fInputMode = NormalInput; // Reset the MORE prompt position to this point, since the user has seen // everything up to here. this->lastInputHeight = this->formatter_->get_max_y_pos(); if (useTimeout) { QEventLoop idleLoop; QTimer timer; timer.setSingleShot(true); connect(&timer, SIGNAL(timeout()), &idleLoop, SLOT(quit())); connect(qFrame, SIGNAL(gameQuitting()), &idleLoop, SLOT(quit())); connect(this, SIGNAL(inputReady()), &idleLoop, SLOT(quit())); timer.start(timeout); idleLoop.exec(); if (timedOut != 0 and not this->fInputReady and qFrame->gameRunning()) { *timedOut = true; this->fInputMode = NoInput; return; } } else while (qFrame->gameRunning() and not this->fInputReady) { qFrame->advanceEventLoop(QEventLoop::WaitForMoreEvents | QEventLoop::AllEvents); } // We're finished with input. this->cancelInput(true); // If input exceeds the buffer size, make sure we don't overflow. int len = this->fTadsBuffer->getlen() > buflen ? buflen : this->fTadsBuffer->getlen(); // For TADS 3, we use the result as-is; it's already in UTF-8. For TADS 2, // we will need to use the prefered encoding. if (qFrame->tads3()) { strncpy(buf, this->fTadsBuffer->getbuf(), len); } else { QTextCodec* codec = QTextCodec::codecForName(qFrame->settings()->tads2Encoding); strncpy(buf, codec->fromUnicode(QString::fromUtf8(this->fTadsBuffer->getbuf(), this->fTadsBuffer->getlen())).constData(), len); } buf[len] = '\0'; } void CHtmlSysWinInputQt::cancelInput( bool reset ) { if (this->fTag == 0) { // There's nothing to cancel. return; } // Remember if we are at the bottom of the output. bool wasAtBottom = this->verticalScrollBar()->value() == this->verticalScrollBar()->maximum(); this->fTadsBuffer->hide_caret(); this->fCastDispWidget->setCursorVisible(false); static_cast(this->formatter_)->end_input(); // Add the line-break after the command. if (qFrame->get_parser()->get_obey_markups()) { // we're in parsed mode, so write our sequence as HTML qFrame->display_output("
", 4); } else { // we're in literal mode, so write out a literal newline qFrame->display_output("\n", 1); } // Tell the formatter to add an extra line's worth of spacing, to ensure // that we have some immediate visual feedback (in the form of scrolling // the window up a line) when the user presses the Enter key. this->formatter_->add_line_to_disp_height(); // Flush the newline, and update the window immediately, in case the // operation takes a while to complete. if (wasAtBottom) { qFrame->flush_txtbuf(true, true); this->verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum); qFrame->advanceEventLoop(); } // Done with the tag. if (reset) { this->fTag = 0; this->fCastDispWidget->setInputTag(0); this->fRestoreFromCancel = false; } else { this->fRestoreFromCancel = true; } } int CHtmlSysWinInputQt::getKeypress( unsigned long timeout, bool useTimeout, bool* timedOut ) { //qDebug() << Q_FUNC_INFO; // If 'done' is false, it means that we have been previously called and // returned 0, so this time we should return the extended key-code we // stored last time in 'extKey'. static bool done = true; static int extKey; if (not done) { // We have a pending return from our last call. Prepare to do a // normal read on our next call and return the pending result. done = true; return extKey; } // Prepare the formatter for input and format all remaining lines. CHtmlFormatterInput* formatter = static_cast(this->formatter_); formatter->prepare_for_input(); while (formatter->more_to_do()) { formatter->do_formatting(); } //this->scrollDown(); // If we have banners waiting in page-pause mode, process them first. this->fProcessPagePauseQueue(); // Clear any pending HREF event. this->fHrefEvent.clear(); extKey = 0; this->fStartKeypressInput(); // Reset the MORE prompt position to this point, since the user has seen // everything up to here. this->lastInputHeight = this->formatter_->get_max_y_pos(); if (useTimeout) { QEventLoop idleLoop; QTimer timer; timer.setSingleShot(true); connect(&timer, SIGNAL(timeout()), &idleLoop, SLOT(quit())); connect(qFrame, SIGNAL(gameQuitting()), &idleLoop, SLOT(quit())); connect(this, SIGNAL(inputReady()), &idleLoop, SLOT(quit())); timer.start(timeout); idleLoop.exec(); } else while (not this->fInputReady and this->fHrefEvent.isEmpty() and qFrame->gameRunning()) { qFrame->advanceEventLoop(QEventLoop::WaitForMoreEvents | QEventLoop::AllEvents); } if (not qFrame->gameRunning()) { // Game is quitting. return -3; } // If there was an HREF event, tell the caller. if (not this->fHrefEvent.isEmpty()) { return -2; } // If we're using a timeout and it expired, tell the caller. if (useTimeout and qFrame->gameRunning() and not this->fInputReady) { Q_ASSERT(timedOut != 0); *timedOut = true; return -1; } if (this->fLastKeyEvent != 0) { switch (this->fLastKeyEvent) { case Qt::Key_Escape: return 27; // A Tab is not an extended character, but Tads requires that // it is handled as one. case Qt::Key_Tab: extKey = CMD_TAB; break; case Qt::Key_Return: case Qt::Key_Enter: return 13; case Qt::Key_Down: extKey = CMD_DOWN; break; case Qt::Key_Up: extKey = CMD_UP; break; case Qt::Key_Left: extKey = CMD_LEFT; break; case Qt::Key_Right: extKey = CMD_RIGHT; break; case Qt::Key_Home: extKey = CMD_HOME; break; case Qt::Key_Backspace: return 8; case Qt::Key_F1: extKey = CMD_F1; break; case Qt::Key_F2: extKey = CMD_F2; break; case Qt::Key_F3: extKey = CMD_F3; break; case Qt::Key_F4: extKey = CMD_F4; break; case Qt::Key_F5: extKey = CMD_F5; break; case Qt::Key_F6: extKey = CMD_F6; break; case Qt::Key_F7: extKey = CMD_F7; break; case Qt::Key_F8: extKey = CMD_F8; break; case Qt::Key_F9: extKey = CMD_F9; break; case Qt::Key_F10: extKey = CMD_F10; break; case Qt::Key_Delete: extKey = CMD_DEL; break; case Qt::Key_PageDown: extKey = CMD_PGDN; break; case Qt::Key_PageUp: extKey = CMD_PGUP; break; case Qt::Key_End: extKey = CMD_END; break; default: // If we got here, something went wrong. Just report a // space. qWarning() << Q_FUNC_INFO << "unrecognized key event in switch:" << hex << this->fLastKeyEvent; return ' '; } } else { // It's a textual key press. return this->fLastKeyText.unicode(); } // Prepare to return the extended key-code on // our next call. done = false; return 0; } void CHtmlSysWinInputQt::addToPagePauseQueue( CHtmlSysWinQt* banner ) { if (not this->fPagePauseQueue.contains(banner)) { this->fPagePauseQueue.enqueue(banner); } } void CHtmlSysWinInputQt::removeFromPagePauseQueue( CHtmlSysWinQt* banner ) { if (this->fPagePauseQueue.isEmpty() or not this->fPagePauseQueue.contains(banner)) { return; } this->fPagePauseQueue.removeOne(banner); if (this->fPagePauseQueue.isEmpty() and this->fInputMode == PagePauseInput) { this->fInputMode = NoInput; } } void CHtmlSysWinInputQt::insertText( QString str ) { if (fInputMode != NormalInput or str.isEmpty() or not qFrame->gameRunning()) { return; } // Replace tabs and newlines with spaces. for (int i = 0; i < str.length(); ++i) { if (str.at(i) == QChar::fromLatin1('\t') or str.at(i) == QChar::fromLatin1('\n')) { str[i] = QChar::fromLatin1(' '); } } const QByteArray& utfTxt = str.toUtf8(); // Try to add it to the input buffer. Do not allow truncation; it // won't work with UTF-8 strings and we'll crash if it doesn't fit. if (not this->fTadsBuffer->add_string(utfTxt.constData(), utfTxt.length(), false)) { // It didn't fit. Try to add the largest part of it that fits. int i = str.length(); QString subStr; QByteArray subUtf; do { --i; subStr = str.left(i); subUtf = subStr.toUtf8(); } while (not this->fTadsBuffer->add_string(subUtf.constData(), subUtf.length(), false) and i > 1); } this->fCastDispWidget->clearSelection(); this->fUpdateInputFormatter(); } void CHtmlSysWinInputQt::set_html_input_color(HTML_color_t clr, int use_default) { //qDebug() << Q_FUNC_INFO; if (use_default) { const QColor& def = qFrame->settings()->inputColor; qFrame->inputColor(HTML_make_color(def.red(), def.green(), def.blue())); } else { qFrame->inputColor(this->map_system_color(clr)); } } qtads-2.1.7/src/syswininput.h000066400000000000000000000131521265017072300162160ustar00rootroot00000000000000/* Copyright (C) 2013 Nikos Chantziaras. * * This file is part of the QTads program. This program is free software; you * can redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; either version * 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #ifndef SYSWININPUT_H #define SYSWININPUT_H #include #include "syswin.h" #include "config.h" /* An input-capable CHtmlSysWinQt. */ class CHtmlSysWinInputQt: public CHtmlSysWinQt { Q_OBJECT friend class CHtmlSysWinQt; private: // These values specify the exact input-mode we are in. enum InputMode { // We aren't in input-mode. NoInput, // Return-terminated input. NormalInput, // Single keypress input. SingleKeyInput, // We are waiting for a response to continue scrolling. PagePauseInput }; // The input-mode we are currently in. InputMode fInputMode; // Queue of banners who are currently in page-pause mode. QQueue fPagePauseQueue; // Our display widget casted for easier access. class DisplayWidgetInput* fCastDispWidget; // We have a finished user input. bool fInputReady; // cancelInput(false) was called, meaning that next time getInput() is // called, we'll need to provide the same input contents from the previous // session, even though we're not actually resuming. bool fRestoreFromCancel; // In single keypress input mode, these store the last pressed key. Only // one of fLastKeyEvent and fLastKeyText can be valid. // // fLastKeyEvent is used in cases where the user pressed a non-text key, // like backspace, space, enter, the up-arrow button, etc. In that case, // fLastKeyEvent contains that key press in form of a Qt::Key and // fLastKeyText will be a null QChar. // // If the user pressed a text key (for example "C", "8" or "!"), then // fLastKeyEvent will be zero and fLastKeyText will contain the character // that corresponds to the pressed key. Qt::Key fLastKeyEvent; QChar fLastKeyText; // Pending HREF event, if any. QString fHrefEvent; // The input tag we use to communicate with the base code. class CHtmlTagTextInput* fTag; // Our command input buffer. class CHtmlInputBuf* fTadsBuffer; textchar_t* fInputBuffer; size_t fInputBufferSize; void fStartKeypressInput(); void fProcessPagePauseQueue(); void fUpdateInputFormatter(); protected: void resizeEvent( QResizeEvent* event ) override; void keyPressEvent( QKeyEvent* e ) override; void inputMethodEvent( QInputMethodEvent* e ) override; void singleKeyPressEvent( QKeyEvent* event ); signals: // Emitted when an input operation has finished successfully. void inputReady(); public: CHtmlSysWinInputQt( class CHtmlFormatter* formatter, QWidget* parent ); ~CHtmlSysWinInputQt() override; // Change the height of the text cursor. void setCursorHeight( unsigned height ); void processCommand( const textchar_t* cmd, size_t len, int append, int enter, int os_cmd_id ); // Read a line of input. void getInput( textchar_t* buf, size_t buflen, unsigned long timeout = 0, bool useTimeout = false, bool* timedOut = 0 ); // Cancel an interrupted input. See CHtmlSysFrame::get_input_cancel(). void cancelInput( bool reset ); // Uses os_getc_raw() semantics, but with a timeout. // // If 'timeout' is 0 or negative, then the routine behaves exactly like // os_getc_raw(). If 'timeout' is positive, then we only wait for a key // for 'timeout' milliseconds. If the operation times out before a key has // been pressed, we return 0 and set 'timedOut' to true. If a key is // pressed before the timeout is reached, we return the same as // os_getc_raw() and set 'timedOut' to false. // // If an HREF events happens while we're waiting for input, -1 is returned. // The caller should use the pendingHrefEvent() method to get the HREF // event in this case. int getKeypress( unsigned long timeout = 0, bool useTimeout = false, bool* timedOut = 0 ); // Add a banner to the queue of banners that are in page-pause mode. void addToPagePauseQueue( CHtmlSysWinQt* banner ); void removeFromPagePauseQueue( CHtmlSysWinQt* banner ); // Return the currently pending HREF event (is there is one.) This method // will clear the event, so subsequent calls will return an empty string. QString pendingHrefEvent() { QString ret(this->fHrefEvent); this->fHrefEvent.clear(); return ret; } // Insert text into the current input editor position. Does nothing if line // input mode is not currently active. void insertText( QString str ); // Do we currently accept text insertion? bool acceptsText() { return this->fInputMode == NormalInput; } // // CHtmlSysWin interface implementation. // void set_html_input_color( HTML_color_t clr, int use_default ) override; }; #endif qtads-2.1.7/tads2/000077500000000000000000000000001265017072300136555ustar00rootroot00000000000000qtads-2.1.7/tads2/appctx.h000066400000000000000000000253701265017072300153340ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/appctx.h,v 1.2 1999/05/17 02:52:14 MJRoberts Exp $ */ /* * Copyright (c) 1997, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name appctx.h - Application container context definitions Function Defines structures related to the TADS runtime application Notes Modified 9/05/98 CNebel - Spliced off from trd.h. */ #ifndef APPCTX_H #define APPCTX_H #ifdef __cplusplus extern "C" { #endif /* * Application container context. The TADS run-time is a subsystem that * can be invoked from different types of applications; in fact, even * when only the standard stand-alone run-time is considered, multiple * application containers must be supported because of differences * between operating systems. The application container context is an * optional mechanism that the main application can use to provide * structured interaction between itself and the TADS run-time subsystem. * * The function pointers contained herein are intended to allow the * run-time subsystem to call the host system to notify it of certain * events, or obtain optional services from the host system. Any of * these function pointers can be null, in which case the run-time * subsystem will skip calling them. * * Note that each function has an associated callback context. This * allows the host system to recover any necessary context information * when the callback is invoked. */ typedef struct appctxdef appctxdef; struct appctxdef { /* * Get the .GAM file name. The run-time will call this only if it * can't find a game file to load through some other means first. * The run-time determines the game file first by looking at the * command line, then by checking to see if a .GAM file is attached * to the executable. If none of these checks yields a game, the * run-time will call this routine to see if the host system wants * to provide a game. This routine can be implemented on a GUI * system, for example, to display a dialog prompting the user to * select a game file to open. A trivial implementation of this * routine (that merely returns false) is okay. * * This routine should return true (any non-zero value) if it * provides the name of a file to open, false (zero) if not. */ int (*get_game_name)(void *appctxdat, char *buf, size_t buflen); void *get_game_name_ctx; /* * Set the .GAM file name. When the run-time determines the name of * the file it will use to read the game, it calls this routine. * The host system should note the game filename if it will need to * access the game file itself (for example, to load resources). */ void (*set_game_name)(void *appctxdat, const char *fname); void *set_game_name_ctx; /* * Set the root path for individual resources. By default, we use the * directory containing the game file, but this can be used to override * that. */ void (*set_res_dir)(void *appctxdat, const char *fname); void *set_res_dir_ctx; /* * Set the resource map address in the game file. If the .GAM * reader encounters a resource map in the file, it calls this * routine with the seek offset of the first resource. Each * resource's address is given as an offset from this point. * * fileno is the file number assigned by the host system in * add_resfile. File number zero is always the .GAM file. */ void (*set_resmap_seek)(void *appctxdat, unsigned long seekpos, int fileno); void *set_resmap_seek_ctx; /* * Add a resource entry. The 'ofs' entry is the byte offset of the * start of the resource, relative to the seek position previously * set with set_resmap_seek. 'siz' is the size of the resource in * bytes; the resource is stored as contiguous bytes starting at the * given offset for the given size. Note that resources may be * added before the resource map seek position is set, so the host * system must simply store the resource information for later use. * The 'fileno' is zero for the .GAM file, or the number assigned by * the host system in add_resfile for other resource files. */ void (*add_resource)(void *appctxdat, unsigned long ofs, unsigned long siz, const char *nm, size_t nmlen, int fileno); void *add_resource_ctx; /* * Add a resource link entry. 'fname' and 'fnamelen' give the name of * a local file containing the resource data; 'resname' and * 'resnamelen' give the name of the resource as it appears within the * compiled game file. This creates a link from a .GAM resource name * to a local filename, where the actual binary resource data reside, * so that we can retrieve a resource by .GAM resource name without * actually copying the data into the .GAM file. This is used mostly * for debugging purposes: it allows the compiler to skip the step of * copying the resource data into the .GAM file, but still allows the * game to load resources by .GAM resource name, to create a testing * environment that's consistent with the full build version (where the * resources would actually be copied). */ void (*add_resource_link)(void *appctxdat, const char *fname, size_t fnamelen, const char *resname, size_t resnamelen); void *add_resource_link_ctx; /* * Add a resource path. 'path' is a string giving a directory prefix * in local system notation. * * This adds a directory to the list of directories that we'll search * when we're looking for an individual resource as an external file * (such as a ".jpg" image file or ".ogg" sound file). This can be * called zero or more times; each call adds another directory to * search after any previous directories. We'll always search the * default directory first (this is the directory containing the game * file); then we'll search directories added with this call in the * order in which the directories were added. */ void (*add_res_path)(void *appctxdat, const char *path, size_t len); void *add_res_path_ctx; /* * Find a resource entry. If the resource can be found, this must * return an osfildef* handle to the resource, with its seek position * set to the first byte of the resource data, and set *res_size to * the size in bytes of the resource data in the file. If the * resource cannot be found, returns null. */ osfildef *(*find_resource)(void *appctxdat, const char *resname, size_t resnamelen, unsigned long *res_size); void *find_resource_ctx; /* * Add a resource file. The return value is a non-zero file number * assigned by the host system; we'll use this number in subsequent * calls to add_resource to add the resources from this file. * * After calling this routine to add the file, we'll parse the file * and add any resources using add_resource. */ int (*add_resfile)(void *appctxdat, const char *fname); void *add_resfile_ctx; /* * Determine if a resource exists. Returns true if the resource can * be loaded, false if not. The resource name is in the standard * URL-style format. */ int (*resfile_exists)(void *appctxdat, const char *res_name, size_t res_name_len); void *resfile_exists_ctx; /* * Resource file path. If we should look for resource files in a * different location than the .GAM file, the host system can set * this to a path that we should use to look for resource files. If * it's null, we'll look in the directory that contains the .GAM * file. Note that if the path is provided, it must be set up with * a trailing path separator character, so that we can directly * append a name to this path to form a valid fully-qualified * filename. */ const char *ext_res_path; /* * File safety level get/set. During initialization, we'll call the * host system to tell it the file safety level selected by the user on * the command line; if the host system is saving preference * information, it should temporarily override its saved preferences * and use the command line setting (and it may, if appropriate, want * to save the command line setting as the saved preference setting, * depending on how it handles preferences). During execution, any * time the game tries to open a file (using the fopen built-in * function), we'll call the host system to ask it for the current * setting, and use this new setting rather than the original command * line setting. * * Refer to bif.c for information on the meanings of the file safety * levels. */ void (*set_io_safety_level)(void *ctx, int read, int write); void (*get_io_safety_level)(void *ctx, int *read, int *write); void *io_safety_level_ctx; /* * Network safety level get/set. This is analogous to the file safety * level scheme, but controls access to network resources. There are * two components to the network safety setting: client and server. * The client component controls the game's ability to open network * connections to access information on remote machines, such as * opening http connections to access web sites. The server component * controls the game's ability to create servers of its own and accept * incoming connections. Each component can be set to one of the * following: * *. 0 = no restrictions (least "safety"): all network access granted *. 1 = 'localhost' access only *. 2 = no network access * * This only applies to the TADS 3 VM. TADS 2 doesn't support any * network features, so this doesn't apply. */ void (*set_net_safety_level)(void *ctx, int client_level, int srv_level); void (*get_net_safety_level)(void *ctx, int *client_level, int *srv_level); void *net_safety_level_ctx; /* * Name of run-time application for usage messages. If this is * null, the default run-time application name will be used for * usage messages. */ const char *usage_app_name; }; #ifdef __cplusplus } #endif #endif qtads-2.1.7/tads2/argize.c000066400000000000000000000106451265017072300153100ustar00rootroot00000000000000/* * Copyright (c) 1998 by Christopher Nebel. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name: argize.c Function: Break a string into argc/argv-style arguments. Notes: none Modified: <2>07/04/98 CNebel - Fixed so that args wouldn't pick up trailing whitespace. <1>07/04/98 CNebel - Created based on older Mac-specific argize source */ #include "argize.h" #include #include /* * Return what argc would be if this string were fed to argize. */ int countargs(const char *cmdline) { const char *p = cmdline; int state = 0; int argc = 0; while (*p) { switch (state) { case 0: /* skip leading white space */ while (isspace((unsigned char)*p)) p++; if (!*p) break; /* If there's nothing left, get out. */ state = 1; /* fall through */; case 1: /* begin arg */ argc++; state = 2; /* fall through */ case 2: /* collect chars for current arg */ if (isspace((unsigned char)*p)) p++, state = 0; /* Terminate this arg */ else if (*p == '"') p++, state = 3; /* Open quote. */ else if (*p == '\\') p++, state = 4; /* Escape sequence. */ else p++; /* Just keep going. */ break; case 3: /* collect chars inside quotes. */ switch (*p) { case '"' : p++; state = 2; break; /* Close quote */ case '\\': p++; state = 5; break; /* Escape sequence. */ default: p++; break; } break; case 4: /* escaped character outside quotes. */ p++; state = 2; break; case 5: /* escaped character inside quotes. */ p++; state = 3; break; } } return argc; } /* * Break a string into argc/argv arguments, removing quotes in * the process. Returns 0 on success, 1 if there were too many * arguments. */ int argize(char *cmdline, int *const argc, char *argv[], const size_t argvlen) { char *p, *q; int state; p = q = cmdline; *argc = 0; state = 0; while (*p) { switch (state) { case 0: /* skip leading white space */ while (isspace((unsigned char)*p)) p++, q++; if (!*p) break; /* If there's nothing left, get out. */ state = 1; /* fall through */; case 1: /* begin arg */ if ((size_t)*argc < argvlen) argv[(*argc)++] = q; else return 1; state = 2; /* fall through */ case 2: /* collect chars for current arg */ if (isspace((unsigned char)*p)) *q++ = *p++ = '\0', state = 0; /* Terminate this arg */ else if (*p == '"') p++, state = 3; /* Open quote. */ else if (*p == '\\') p++, state = 4; /* Escape sequence. */ else *q++ = *p++; /* Just keep going. */ break; case 3: /* collect chars inside quotes. */ switch (*p) { case '"' : p++; state = 2; break; /* Close quote.*/ case '\\': p++; state = 5; break; /* Escape sequence. */ default: *q++ = *p++; break; } break; case 4: /* escaped character outside quotes. */ *q++ = *p++; state = 2; break; case 5: /* escaped character inside quotes. */ *q++ = *p++; state = 3; break; } } *q = '\0'; return 0; } qtads-2.1.7/tads2/argize.h000066400000000000000000000015041265017072300153070ustar00rootroot00000000000000/* * Copyright (c) 1998 by Christopher Nebel. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name argize.h Function Break a string into argc/argv-style arguments. Notes none Modified 07/04/98 CNebel - Created based on older Mac-specific argize source. */ #ifndef _ARGIZE_ #define _ARGIZE_ #include /* * Return what argc would be if this string were fed to argize. */ int countargs(const char *cmdline); /* * Break a string into argc/argv arguments, removing quotes in * the process. Returns 0 on success, 1 if there were too many * arguments to fit into argv. */ int argize(char *cmdline, int * const argc, char *argv[], const size_t argvlen); #endif qtads-2.1.7/tads2/askf_os.c000066400000000000000000000025631265017072300154540ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name askf_os.c - dialog-based implementation of tio_askfile Function Implements tio_askfile() using the OS-layer os_askfile() routine. Notes Only one of askf_tx.c or askf_os.c should be included in a given executable. For a text-only version, include askf_tx. For a version where os_askfile() provides a file dialog, use askf_os instead. We provide a choice of tio_askfile() implementations in the portable code (rather than only through the OS code) so that we can call the formatted text output routines in this version. An OS-layer implementation could not call the formatted output routines (it would have to call os_printf directly), which would result in poor prompt formatting any time a prompt exceeded a single line of text. Modified 09/27/99 MJRoberts - Creation */ #include "os.h" #include "std.h" #include "tio.h" /* * formatted text-only file prompt */ int tio_askfile(const char *prompt, char *reply, int replen, int prompt_type, os_filetype_t file_type) { /* let the OS layer handle it */ return os_askfile(prompt, reply, replen, prompt_type, file_type); } qtads-2.1.7/tads2/bif.c000066400000000000000000003471721265017072300145770ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/BIF.C,v 1.4 1999/07/11 00:46:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name bif.c - built-in function implementation Function Implements built-in functions for TADS Notes None Modified 04/11/99 CNebel - fix signed/unsigned mismatches 12/16/92 MJRoberts - add TADS/Graphic functions 12/26/91 MJRoberts - creation */ #include #include #include #include #include #include #include "os.h" #include "std.h" #include "bif.h" #include "tio.h" #include "run.h" #include "voc.h" #include "fio.h" #include "dbg.h" #include "prp.h" #include "lst.h" #include "trd.h" #include "regex.h" #include "res.h" #include "cmap.h" /* yorn - yes or no */ void bifyon(bifcxdef *ctx, int argc) { char rsp[128]; char *p; runsdef val; char yesbuf[64]; char nobuf[64]; re_context rectx; int match_yes; int match_no; bifcntargs(ctx, 0, argc); /* check for proper argument count */ /* load the "yes" and "no" reply patterns */ if (os_get_str_rsc(RESID_YORN_YES, yesbuf, sizeof(yesbuf))) strcpy(yesbuf, "[Yy].*"); if (os_get_str_rsc(RESID_YORN_NO, nobuf, sizeof(nobuf))) strcpy(nobuf, "[Nn].*"); /* if we're in HTML mode, switch to input font */ if (tio_is_html_mode()) tioputs(ctx->bifcxtio, ""); /* ensure the prompt is displayed */ tioflushn(ctx->bifcxtio, 0); /* reset count of lines since the last keyboard input */ tioreset(ctx->bifcxtio); /* read a line of text */ if (tiogets(ctx->bifcxtio, (char *)0, rsp, (int)sizeof(rsp))) runsig(ctx->bifcxrun, ERR_RUNQUIT); /* if we're in HTML mode, close the input font tag */ if (tio_is_html_mode()) tioputs(ctx->bifcxtio, ""); /* scan off leading spaces */ for (p = rsp ; t_isspace(*p) ; ++p) ; /* set up our regex context */ re_init(&rectx, ctx->bifcxerr); /* check for a "yes" response */ match_yes = re_compile_and_match(&rectx, yesbuf, strlen(yesbuf), p, strlen(p)); /* check for a "no" response */ match_no = re_compile_and_match(&rectx, nobuf, strlen(nobuf), p, strlen(p)); /* check the result */ if (match_yes == (int)strlen(p)) val.runsv.runsvnum = 1; else if (match_no == (int)strlen(p)) val.runsv.runsvnum = 0; else val.runsv.runsvnum = -1; /* delete our regex context */ re_delete(&rectx); /* push the result */ runpush(ctx->bifcxrun, DAT_NUMBER, &val); } /* setfuse */ void bifsfs(bifcxdef *ctx, int argc) { objnum func; uint tm; runsdef val; voccxdef *voc = ctx->bifcxrun->runcxvoc; bifcntargs(ctx, 3, argc); /* check for proper argument count */ func = runpopfn(ctx->bifcxrun); tm = runpopnum(ctx->bifcxrun); runpop(ctx->bifcxrun, &val); /* limitation: don't allow string or list for value */ if (val.runstyp == DAT_LIST || val.runstyp == DAT_SSTRING) runsig(ctx->bifcxrun, ERR_FUSEVAL); vocsetfd(voc, voc->voccxfus, func, (prpnum)0, tm, &val, ERR_MANYFUS); } /* remfuse */ void bifrfs(bifcxdef *ctx, int argc) { objnum func; runsdef val; voccxdef *voc = ctx->bifcxrun->runcxvoc; bifcntargs(ctx, 2, argc); func = runpopfn(ctx->bifcxrun); runpop(ctx->bifcxrun, &val); vocremfd(voc, voc->voccxfus, func, (prpnum)0, &val, ERR_NOFUSE); } /* setdaemon */ void bifsdm(bifcxdef *ctx, int argc) { objnum func; runsdef val; voccxdef *voc = ctx->bifcxrun->runcxvoc; bifcntargs(ctx, 2, argc); /* check for proper argument count */ func = runpopfn(ctx->bifcxrun); runpop(ctx->bifcxrun, &val); /* limitation: don't allow string or list for value */ if (val.runstyp == DAT_LIST || val.runstyp == DAT_SSTRING) runsig(ctx->bifcxrun, ERR_FUSEVAL); vocsetfd(voc, voc->voccxdmn, func, (prpnum)0, 0, &val, ERR_MANYDMN); } /* remdaemon */ void bifrdm(bifcxdef *ctx, int argc) { objnum func; runsdef val; voccxdef *voc = ctx->bifcxrun->runcxvoc; bifcntargs(ctx, 2, argc); func = runpopfn(ctx->bifcxrun); runpop(ctx->bifcxrun, &val); vocremfd(voc, voc->voccxdmn, func, (prpnum)0, &val, ERR_NODMN); } /* incturn */ void bifinc(bifcxdef *ctx, int argc) { int turncnt; if (argc == 1) { /* get the number of turns to skip */ turncnt = runpopnum(ctx->bifcxrun); if (turncnt < 1) runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "incturn"); } else { /* no arguments -> increment by one turn */ bifcntargs(ctx, 0, argc); turncnt = 1; } /* skip the given number of turns */ vocturn(ctx->bifcxrun->runcxvoc, turncnt, TRUE); } /* skipturn */ void bifskt(bifcxdef *ctx, int argc) { int turncnt; bifcntargs(ctx, 1, argc); turncnt = runpopnum(ctx->bifcxrun); if (turncnt < 1) runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "skipturn"); vocturn(ctx->bifcxrun->runcxvoc, turncnt, FALSE); } /* quit */ void bifqui(bifcxdef *ctx, int argc) { /* check for proper argument count */ bifcntargs(ctx, 0, argc); /* flush output buffer, and signal the end of the game */ tioflush(ctx->bifcxtio); errsig(ctx->bifcxerr, ERR_RUNQUIT); } /* internal function to convert a TADS string into a C-string */ static void bifcstr(bifcxdef *ctx, char *buf, size_t bufsiz, uchar *str) { size_t srcrem; size_t dstrem; uchar *src; char *dst; /* get the length and text portion of the string */ srcrem = osrp2(str) - 2; str += 2; /* scan the string, and convert escapes */ for (src = str, dst = buf, dstrem = bufsiz ; srcrem != 0 && dstrem != 0 ; ++src, --srcrem) { /* if we have an escape sequence, convert it */ if (*src == '\\') { /* skip the backslash in the input */ ++src; --srcrem; /* if there's nothing left, store the backslash */ if (srcrem == 0) { /* store the backslash */ *dst++ = '\\'; --dstrem; /* there's nothing left to scan */ break; } /* see what the second half of the escape sequence is */ switch(*src) { case 'n': /* store a C-style newline character */ *dst++ = '\n'; --dstrem; break; case 't': /* store a C-style tab */ *dst++ = '\t'; --dstrem; break; case '(': case ')': /* entirely omit the highlighting sequences */ break; default: /* store everything else unchanged */ *dst++ = *src; --dstrem; break; } } else { /* copy this character unchanged */ *dst++ = *src; --dstrem; } } /* if the buffer wasn't big enough, signal an error */ if (dstrem == 0) runsig(ctx->bifcxrun, ERR_BIFCSTR); /* null-terminate the result string */ *dst = '\0'; } /* save */ void bifsav(bifcxdef *ctx, int argc) { uchar *fn; char buf[OSFNMAX]; int err; runsdef val; bifcntargs(ctx, 1, argc); fn = runpopstr(ctx->bifcxrun); bifcstr(ctx, buf, (size_t)sizeof(buf), fn); os_defext(buf, ctx->bifcxsavext != 0 ? ctx->bifcxsavext : "sav"); err = fiosav(ctx->bifcxrun->runcxvoc, buf, ctx->bifcxrun->runcxgamename); runpush(ctx->bifcxrun, runclog(err), &val); } /* restore */ void bifrso(bifcxdef *ctx, int argc) { uchar *fn; char buf[OSFNMAX]; int err; voccxdef *vctx = ctx->bifcxrun->runcxvoc; bifcntargs(ctx, 1, argc); /* check for special restore(nil) - restore game given as parameter */ if (runtostyp(ctx->bifcxrun) == DAT_NIL) { /* get filename from startup parameter, if any */ if (!os_paramfile(buf)) { /* no startup parameter */ runpnum(ctx->bifcxrun, FIORSO_NO_PARAM_FILE); return; } } else { /* get string parameter - it's the filename */ fn = runpopstr(ctx->bifcxrun); bifcstr(ctx, buf, (size_t)sizeof(buf), fn); os_defext(buf, ctx->bifcxsavext != 0 ? ctx->bifcxsavext : "sav"); } /* try restoring the file */ err = fiorso(vctx, buf); /* blow away all undo records */ objulose(vctx->voccxundo); /* return the result code from fiorso */ runpnum(ctx->bifcxrun, err); /* note that the rest of the command line is to be ignored */ vctx->voccxflg |= VOCCXFCLEAR; } /* logging */ void biflog(bifcxdef *ctx, int argc) { char buf[OSFNMAX]; uchar *str; bifcntargs(ctx, 1, argc); if (runtostyp(ctx->bifcxrun) == DAT_NIL) { rundisc(ctx->bifcxrun); tiologcls(ctx->bifcxtio); } else { str = runpopstr(ctx->bifcxrun); bifcstr(ctx, buf, (size_t)sizeof(buf), str); tiologopn(ctx->bifcxtio, buf); } } /* restart */ void bifres(bifcxdef *ctx, int argc) { voccxdef *vctx = ctx->bifcxrun->runcxvoc; objnum fn; if (argc == 2) fn = runpopfn(ctx->bifcxrun); /* get function if present */ else { bifcntargs(ctx, 0, argc); /* check for proper argument count */ fn = MCMONINV; /* no function was specified */ } objulose(vctx->voccxundo); /* blow away all undo records */ vocrevert(vctx); /* revert all objects to original state */ vocdmnclr(vctx); /* clear out fuses/deamons/notifiers */ /* restore the original "Me" object */ vctx->voccxme = vctx->voccxme_init; /* call preinit if necessary (call it before invoking the user callback) */ if (vctx->voccxpreinit != MCMONINV) runfn(ctx->bifcxrun, vctx->voccxpreinit, 0); /* * If a restart function was provided, call it. Note that we left * the argument for the function on the stack, so there's no need to * re-push it! */ if (fn != MCMONINV) runfn(ctx->bifcxrun, fn, 1); /* restart the game */ errsig(ctx->bifcxerr, ERR_RUNRESTART); } /* input - get a line of input from the keyboard */ void bifinp(bifcxdef *ctx, int argc) { char inbuf[128]; /* check for proper argument count */ bifcntargs(ctx, 0, argc); /* make sure the prompt is displayed */ tioflushn(ctx->bifcxtio, 0); /* reset count of lines since the last keyboard input */ tioreset(ctx->bifcxtio); /* read a line of text */ if (tiogets(ctx->bifcxtio, (char *)0, inbuf, (int)sizeof(inbuf))) runsig(ctx->bifcxrun, ERR_RUNQUIT); /* push the string, converting escapes */ runpushcstr(ctx->bifcxrun, inbuf, strlen(inbuf), 0); } /* notify */ void bifnfy(bifcxdef *ctx, int argc) { objnum objn; prpnum prp; uint tm; voccxdef *voc = ctx->bifcxrun->runcxvoc; bifcntargs(ctx, 3, argc); /* check for proper argument count */ objn = runpopobj(ctx->bifcxrun); prp = runpopprp(ctx->bifcxrun); tm = runpopnum(ctx->bifcxrun); /* a time of zero means every turn */ if (tm == 0) tm = VOCDTIM_EACH_TURN; vocsetfd(voc, voc->voccxalm, objn, prp, tm, (runsdef *)0, ERR_MANYNFY); } /* unnotify */ void bifunn(bifcxdef *ctx, int argc) { objnum objn; prpnum prop; voccxdef *voc = ctx->bifcxrun->runcxvoc; bifcntargs(ctx, 2, argc); objn = runpopobj(ctx->bifcxrun); prop = runpopprp(ctx->bifcxrun); vocremfd(voc, voc->voccxalm, objn, prop, (runsdef *)0, ERR_NONFY); } /* trace on/off */ void biftrc(bifcxdef *ctx, int argc) { runsdef val; int n; int flag; if (argc == 2) { /* get the type indicator and the on/off status */ n = runpopnum(ctx->bifcxrun); flag = runpoplog(ctx->bifcxrun); /* see what type of debugging they want to turn on or off */ switch(n) { case 1: /* turn on parser tracing */ if (flag) ctx->bifcxrun->runcxvoc->voccxflg |= VOCCXFDBG; else ctx->bifcxrun->runcxvoc->voccxflg &= ~VOCCXFDBG; break; default: /* ignore other requests */ runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "debugTrace"); } } else { /* break into debugger; return whether debugger is present */ bifcntargs(ctx, 0, argc); runpush(ctx->bifcxrun, runclog(dbgstart(ctx->bifcxrun->runcxdbg)), &val); } } /* say */ void bifsay(bifcxdef *ctx, int argc) { uchar *str; long num; char numbuf[30]; if (argc != 2) bifcntargs(ctx, 1, argc); switch(runtostyp(ctx->bifcxrun)) { case DAT_NUMBER: num = runpopnum(ctx->bifcxrun); sprintf(numbuf, "%ld", num); tioputs(ctx->bifcxtio, numbuf); break; case DAT_SSTRING: str = runpopstr(ctx->bifcxrun); outfmt(ctx->bifcxtio, str); break; case DAT_NIL: (void)runpoplog(ctx->bifcxrun); break; default: runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "say"); } } /* car */ void bifcar(bifcxdef *ctx, int argc) { uchar *lstp; uint lstsiz; runsdef val; bifcntargs(ctx, 1, argc); bifchkarg(ctx, DAT_LIST); lstp = runpoplst(ctx->bifcxrun); /* get list's size, and point to its data string */ lstsiz = osrp2(lstp) - 2; lstp += 2; /* push first element if one is present, otherwise push nil */ if (lstsiz) runpbuf(ctx->bifcxrun, *lstp, lstp+1); else runpush(ctx->bifcxrun, DAT_NIL, &val); } /* cdr */ void bifcdr(bifcxdef *ctx, int argc) { uchar *lstp; uint siz; uint lstsiz; runsdef val; runsdef stkval; bifcntargs(ctx, 1, argc); bifchkarg(ctx, DAT_LIST); lstp = runpoplst(ctx->bifcxrun); stkval.runstyp = DAT_LIST; stkval.runsv.runsvstr = lstp; /* get list's size, and point to its data string */ lstsiz = osrp2(lstp) - 2; lstp += 2; if (lstsiz != 0) { /* deduct size of first element from size of list */ siz = datsiz(*lstp, lstp+1) + 1; lstsiz -= siz; /* add in the size prefix for our new list size */ lstsiz += 2; /* allocate space for new list containing rest of list */ runhres1(ctx->bifcxrun, lstsiz, 1, &stkval); lstp = stkval.runsv.runsvstr + siz + 2; /* write out size followed by list value string */ oswp2(ctx->bifcxrun->runcxhp, lstsiz); memcpy(ctx->bifcxrun->runcxhp+2, lstp, (size_t)(lstsiz-2)); val.runsv.runsvstr = ctx->bifcxrun->runcxhp; val.runstyp = DAT_LIST; ctx->bifcxrun->runcxhp += lstsiz; runrepush(ctx->bifcxrun, &val); } else runpush(ctx->bifcxrun, DAT_NIL, &val); /* empty list - cdr is nil */ } /* caps */ void bifcap(bifcxdef *ctx, int argc) { bifcntargs(ctx, 0, argc); tiocaps(ctx->bifxtio); /* set output driver next-char-capitalized flag */ } /* nocaps */ void bifnoc(bifcxdef *ctx, int argc) { bifcntargs(ctx, 0, argc); tionocaps(ctx->bifxtio); /* set next-not-capitalized flag */ } /* length */ void biflen(bifcxdef *ctx, int argc) { uchar *p; runsdef val; long len; int l; bifcntargs(ctx, 1, argc); switch(runtostyp(ctx->bifcxrun)) { case DAT_SSTRING: p = (uchar *)runpopstr(ctx->bifcxrun); len = osrp2(p) - 2; break; case DAT_LIST: p = runpoplst(ctx->bifcxrun); l = osrp2(p) - 2; p += 2; /* count all elements in list */ for (len = 0 ; l ; ++len) { int cursiz; /* get size of this element, and move past it */ cursiz = datsiz(*p, p+1) + 1; l -= cursiz; p += cursiz; } break; default: runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "length"); } val.runsv.runsvnum = len; runpush(ctx->bifcxrun, DAT_NUMBER, &val); } /* find */ void biffnd(bifcxdef *ctx, int argc) { uchar *p1, *p2; int len1, len2; int outv; runsdef val; int typ; int siz; bifcntargs(ctx, 2, argc); switch(runtostyp(ctx->bifcxrun)) { case DAT_SSTRING: p1 = runpopstr(ctx->bifcxrun); len1 = osrp2(p1) - 2; p1 += 2; p2 = runpopstr(ctx->bifcxrun); len2 = osrp2(p2) - 2; p2 += 2; /* look for p2 within p1 */ for (typ = DAT_NIL, outv = 1 ; len1 >= len2 ; ++p1, --len1, ++outv) { if (!memcmp(p1, p2, (size_t)len2)) { typ = DAT_NUMBER; /* use number in outv after all */ break; /* that's it - we've found it */ } } break; case DAT_LIST: p1 = runpoplst(ctx->bifcxrun); len1 = osrp2(p1) - 2; p1 += 2; /* get second item: any old datatype */ runpop(ctx->bifcxrun, &val); for (typ = DAT_NIL, outv = 1 ; len1 ; ++outv, p1 += siz, len1 -= siz) { siz = datsiz(*p1, p1 + 1) + 1; /* get size of this element */ if (val.runstyp != *p1) continue; /* types don't match */ switch(val.runstyp) { case DAT_NUMBER: if (val.runsv.runsvnum != osrp4s(p1 + 1)) continue; break; case DAT_SSTRING: case DAT_LIST: if (osrp2(p1 + 1) != osrp2(val.runsv.runsvstr) || memcmp(p1 + 3, val.runsv.runsvstr + 2, (size_t)(osrp2(p1 + 1) - 2))) continue; break; case DAT_PROPNUM: if (osrp2(p1 + 1) != val.runsv.runsvprp) continue; break; case DAT_OBJECT: case DAT_FNADDR: if (osrp2(p1 + 1) != val.runsv.runsvobj) continue; break; default: break; } /* if we got here, it means we found a match */ typ = DAT_NUMBER; /* use the value in outv */ break; /* that's it - we've found it */ } break; default: runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "find"); } /* push the value given by typ and outv */ val.runsv.runsvnum = outv; runpush(ctx->bifcxrun, typ, &val); } /* setit - set current 'it' */ void bifsit(bifcxdef *ctx, int argc) { objnum obj; int typ; voccxdef *vcx = ctx->bifcxrun->runcxvoc; /* check for extended version that allows setting him/her */ if (argc == 2) { if (runtostyp(ctx->bifcxrun) == DAT_NIL) { rundisc(ctx->bifcxrun); /* discard the nil */ obj = MCMONINV; /* use invalid object */ } else obj = runpopobj(ctx->bifcxrun); /* get the object */ typ = runpopnum(ctx->bifcxrun); /* get the code */ vcx->voccxthc = 0; /* clear the 'them' list */ switch(typ) { case 0: /* set "it" */ vcx->voccxit = obj; break; case 1: /* set "him" */ vcx->voccxhim = obj; break; case 2: /* set "her" */ vcx->voccxher = obj; break; } return; } /* "setit classic" has one argument only */ bifcntargs(ctx, 1, argc); /* check to see if we're setting 'it' or 'them' */ if (runtostyp(ctx->bifcxrun) == DAT_LIST) { uchar *lst; uint siz; int cnt; lst = runpoplst(ctx->bifcxrun); siz = osrp2(lst); lst += 2; siz -= 2; for (cnt = 0 ; siz ; ) { /* if this is an object, add to 'them' list (otherwise ignore) */ if (*lst == DAT_OBJECT) vcx->voccxthm[cnt++] = osrp2(lst+1); lstadv(&lst, &siz); } vcx->voccxthc = cnt; vcx->voccxit = MCMONINV; } else { /* set 'it', and delete 'them' list */ if (runtostyp(ctx->bifcxrun) == DAT_NIL) { vcx->voccxit = MCMONINV; rundisc(ctx->bifcxrun); } else vcx->voccxit = runpopobj(ctx->bifcxrun); vcx->voccxthc = 0; } } /* randomize - seed random number generator */ void bifsrn(bifcxdef *ctx, int argc) { bifcntargs(ctx, 0, argc); os_rand(&ctx->bifcxrnd); ctx->bifcxrndset = TRUE; } /* rand - get a random number */ void bifrnd(bifcxdef *ctx, int argc) { unsigned long result, max, randseed; int tmp; runsdef val; /* get argument - number giving upper bound of generated number */ bifcntargs(ctx, 1, argc); bifchkarg(ctx, DAT_NUMBER); max = runpopnum(ctx->bifcxrun); /* if the max is zero, just return zero */ if (max == 0) { runpnum(ctx->bifcxrun, 0); return; } /* * If the random number generator has been seeded by a call to * randomize(), use the new, improved random number generator. If * not, use the old random number generator to ensure that the same * sequence of numbers is generated as always (to prevent breaking * existing test scripts based on the old sequence). */ if (!ctx->bifcxrndset) { /* compute the next number in sequence, using old cheesy generator */ randseed = ctx->bifcxrnd; randseed *= 1033; randseed += 5; tmp = randseed / 16384; randseed %= 16384; result = tmp / 7; /* adjust the result to be in the requested range */ result = ( randseed % max ) + 1; /* save the new seed value, and return the value */ ctx->bifcxrnd = randseed; val.runsv.runsvnum = result; runpush(ctx->bifcxrun, DAT_NUMBER, &val); } else { #define BIF_RAND_M ((ulong)2147483647) #define BIF_RAND_Q ((ulong)127773) #define BIF_RAND_A ((ulong)16807) #define BIF_RAND_R ((ulong)2836) long lo, hi, test; lo = ctx->bifcxrnd / BIF_RAND_Q; hi = ctx->bifcxrnd % BIF_RAND_Q; test = BIF_RAND_A*lo - BIF_RAND_R*hi; ctx->bifcxrnd = test; if (test > 0) ctx->bifcxrnd = test; else ctx->bifcxrnd = test + BIF_RAND_M; runpnum(ctx->bifcxrun, (((ulong)ctx->bifcxrnd) % max) + 1); } } /* * case-insensitive substring matching */ static char *bif_stristr(const char *s1, const char *s2) { size_t s1len; size_t s2len; /* scan for a match */ for (s1len = strlen(s1), s2len = strlen(s2) ; s1len >= s2len ; ++s1, --s1len) { /* if this is a match, return this substring */ if (memicmp(s1, s2, s2len) == 0) return (char *)s1; } return 0; } /* * askfile flags */ #define BIF_ASKF_EXT_RET 1 /* extended return codes */ /* * askfile */ void bifask(bifcxdef *ctx, int argc) { uchar *prompt; char buf[OSFNMAX + 2]; char pbuf[128]; int err; int prompt_type; int file_type; ulong flags; /* make sure we have an acceptable number of arguments */ if (argc != 1 && argc != 3 && argc != 4) runsig(ctx->bifcxrun, ERR_BIFARGC); /* get the first argument - the prompt string */ prompt = runpopstr(ctx->bifcxrun); bifcstr(ctx, pbuf, (size_t)sizeof(pbuf), prompt); /* presume we will have no flags */ flags = 0; /* if we have the prompt type and file type parameters, get them */ if (argc >= 3) { /* get the prompt-type and the file-type arguments */ prompt_type = (int)runpopnum(ctx->bifcxrun); file_type = (int)runpopnum(ctx->bifcxrun); /* if we have a fourth argument, it's the flags */ if (argc == 4) flags = runpopnum(ctx->bifcxrun); } else { static const char *save_strs[] = { "save", "write", 0 }; static const char *game_strs[] = { "restore", "game", 0 }; const char **sp; /* * No prompt type or file type were specified. Try to infer the * dialog type and file type from the text of the prompt. (This * is mostly to support older games, in particular those based * on older versions of adv.t, since newer games should always * provide explicit values for the file type and dialog type. * We are thus inferring the types based on the prompt strings * that older adv.t's used when calling askfile.) * * If the prompt contains any substring such as "save" or * "write", specify that we're saving; otherwise, assume that * we're opening an existing file for reading. * * If the prompt contains the substrings "restore" AND "game", * assume that we're opening a game file; otherwise, don't make * any assumptions, and use the "unknown" file type. */ /* presume we're going to open a saved-game file */ prompt_type = OS_AFP_OPEN; file_type = OSFTSAVE; /* look for any one of the "save" substrings */ for (sp = save_strs ; *sp != 0 ; ++sp) { /* check to see if this substring matches */ if (bif_stristr(pbuf, *sp)) { /* found it - use the "save" prompt */ prompt_type = OS_AFP_SAVE; /* no need to look any further */ break; } } /* * look for *all* of the "restore game" strings - if we fail to * find any of them, be conservative and make no assumptions * about the file type */ for (sp = game_strs ; *sp != 0 ; ++sp) { if (bif_stristr(pbuf, *sp) == 0) { /* * this one doesn't match - don't make assumptions about * the file type */ file_type = OSFTUNK; /* no need to look any further */ break; } } /* check for a transcript */ if (file_type == OSFTUNK && prompt_type == OS_AFP_SAVE && bif_stristr(pbuf, "script") != 0) { /* looks like a log file */ file_type = OSFTLOG; } } /* ask for a file */ err = tio_askfile(pbuf, buf, (int)sizeof(buf), prompt_type, file_type); /* * if the caller requested extended return codes, return a list * containing the return code as the first element and, if * successful, the string as the second element */ if ((flags & BIF_ASKF_EXT_RET) != 0) { ushort len; runsdef val; uchar *p; /* * Allocate space for the starter list - if we have a string to * return, just allocate space for the number element for now; * otherwise, allocate space for the number plus a nil second * element (one byte). */ len = 2 + (1 + 4); if (err != OS_AFE_SUCCESS) ++len; /* allocate the space */ runhres(ctx->bifcxrun, len, 0); /* set up our list pointer */ val.runstyp = DAT_LIST; val.runsv.runsvstr = p = ctx->bifcxrun->runcxhp; /* write the length prefix */ oswp2(p, len); p += 2; /* write the return code as the first element */ *p++ = DAT_NUMBER; oswp4s(p, err); p += 4; /* write the 'nil' second element if there's an error */ if (err != OS_AFE_SUCCESS) *p++ = DAT_NIL; /* commit the list's memory */ ctx->bifcxrun->runcxhp = p; /* push the list */ runrepush(ctx->bifcxrun, &val); /* if we were successful, add the string to the list */ if (err == OS_AFE_SUCCESS) { runsdef val2; /* push the string value, converting to our string format */ runpushcstr(ctx->bifcxrun, buf, strlen(buf), 1); /* add it to the list already on the stack */ runpop(ctx->bifcxrun, &val2); runpop(ctx->bifcxrun, &val); runadd(ctx->bifcxrun, &val, &val2, 2); /* re-push the result */ runrepush(ctx->bifcxrun, &val); } } else { /* * use the traditional return codes - if askfile failed, return * nil; otherwise, return the filename */ if (err) runpnil(ctx->bifcxrun); else runpushcstr(ctx->bifcxrun, buf, strlen(buf), 0); } } /* setscore */ void bifssc(bifcxdef *ctx, int argc) { int s1, s2; /* optional new way - string argument */ if (argc == 1 && runtostyp(ctx->bifcxrun) == DAT_SSTRING) { char buf[80]; uchar *p; p = runpopstr(ctx->bifcxrun); bifcstr(ctx, buf, (size_t)sizeof(buf), p); tiostrsc(ctx->bifcxtio, buf); } else { /* old way - two numeric arguments (displays: x/y) */ bifcntargs(ctx, 2, argc); s1 = runpopnum(ctx->bifcxrun); s2 = runpopnum(ctx->bifcxrun); tioscore(ctx->bifcxtio, s1, s2); } } /* substr */ void bifsub(bifcxdef *ctx, int argc) { uchar *p; int ofs; int asklen; int outlen; int len; bifcntargs(ctx, 3, argc); /* get the string argument */ bifchkarg(ctx, DAT_SSTRING); p = runpopstr(ctx->bifcxrun); len = osrp2(p) - 2; p += 2; /* get the offset argument */ bifchkarg(ctx, DAT_NUMBER); ofs = runpopnum(ctx->bifcxrun); if (ofs < 1) runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "substr"); /* get the length argument */ bifchkarg(ctx, DAT_NUMBER); asklen = runpopnum(ctx->bifcxrun); if (asklen < 0) runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "substr"); --ofs; /* convert offset to a zero bias (user provided 1-bias) */ p += ofs; /* advance string pointer by offset */ if (ofs >= len) outlen = 0; /* offset is past end of string */ else if (asklen > len - ofs) outlen = len - ofs; /* just use rest of string */ else outlen = asklen; /* requested length can be provided */ runpstr(ctx->bifcxrun, (char *)p, outlen, 3); } /* cvtstr - convert value to a string */ void bifcvs(bifcxdef *ctx, int argc) { char *p; int len; char buf[30]; bifcntargs(ctx, 1, argc); switch(runtostyp(ctx->bifcxrun)) { case DAT_NIL: p = "nil"; len = 3; (void)runpoplog(ctx->bifcxrun); break; case DAT_TRUE: p = "true"; len = 4; (void)runpoplog(ctx->bifcxrun); break; case DAT_NUMBER: sprintf(buf, "%ld", runpopnum(ctx->bifcxrun)); p = buf; len = strlen(buf); break; case DAT_SSTRING: /* leave the string value on the stack unchanged */ return; default: /* throw the RUNEXITOBJ error */ runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "cvtstr"); } runpstr(ctx->bifcxrun, p, len, 0); } /* cvtnum - convert a value to a number */ void bifcvn(bifcxdef *ctx, int argc) { runsdef val; uchar *p; int len; int typ; long acc; int neg; bifcntargs(ctx, 1, argc); p = runpopstr(ctx->bifcxrun); len = osrp2(p) - 2; p += 2; if (len == 3 && !memcmp(p, "nil", (size_t)3)) typ = DAT_NIL; else if (len == 4 && !memcmp(p, "true", (size_t)4)) typ = DAT_TRUE; else { typ = DAT_NUMBER; for ( ; len != 0 && t_isspace(*p) ; ++p, --len) ; if (len != 0 && *p == '-') { neg = TRUE; for (++p, --len ; len != 0 && t_isspace(*p) ; ++p, --len) ; } else neg = FALSE; /* accumulate the number digit by digit */ for (acc = 0 ; len != 0 && isdigit(*p) ; ++p, --len) acc = (acc << 3) + (acc << 1) + ((*p) - '0'); if (neg) acc = -acc; val.runsv.runsvnum = acc; } runpush(ctx->bifcxrun, typ, &val); } /* general string conversion function */ static void bifcvtstr(bifcxdef *ctx, void (*cvtfn)(uchar *, int), int argc) { uchar *p; int len; runsdef val; runsdef stkval; bifcntargs(ctx, 1, argc); bifchkarg(ctx, DAT_SSTRING); p = runpopstr(ctx->bifcxrun); stkval.runstyp = DAT_SSTRING; stkval.runsv.runsvstr = p; len = osrp2(p); /* allocate space in heap for the string and convert */ runhres1(ctx->bifcxrun, len, 1, &stkval); p = stkval.runsv.runsvstr; memcpy(ctx->bifcxrun->runcxhp, p, (size_t)len); (*cvtfn)(ctx->bifcxrun->runcxhp + 2, len - 2); val.runsv.runsvstr = ctx->bifcxrun->runcxhp; val.runstyp = DAT_SSTRING; ctx->bifcxrun->runcxhp += len; runrepush(ctx->bifcxrun, &val); } /* routine to convert a counted-length string to uppercase */ static void bifstrupr(uchar *str, int len) { for ( ; len ; --len, ++str) { if (*str == '\\' && len > 1) --len, ++str; else if (islower(*str)) *str = toupper(*str); } } /* upper */ void bifupr(bifcxdef *ctx, int argc) { bifcvtstr(ctx, bifstrupr, argc); } /* convert a counted-length string to lowercase */ static void bifstrlwr(uchar *str, int len) { for ( ; len ; --len, ++str) { if (*str == '\\' && len > 1) --len, ++str; else if (isupper(*str)) *str = tolower(*str); } } /* lower */ void biflwr(bifcxdef *ctx, int argc) { bifcvtstr(ctx, bifstrlwr, argc); } /* internal check to determine if object is of a class */ int bifinh(voccxdef *voc, vocidef *v, objnum cls) { int i; objnum *sc; if (!v) return(FALSE); for (i = v->vocinsc, sc = v->vocisc ; i ; ++sc, --i) { if (*sc == cls || bifinh(voc, vocinh(voc, *sc), cls)) return(TRUE); } return(FALSE); } /* isclass(obj, cls) */ void bifisc(bifcxdef *ctx, int argc) { objnum obj; objnum cls; runsdef val; voccxdef *voc = ctx->bifcxrun->runcxvoc; bifcntargs(ctx, 2, argc); /* if checking for nil, return nil */ if (runtostyp(ctx->bifcxrun) == DAT_NIL) { rundisc(ctx->bifcxrun); rundisc(ctx->bifcxrun); runpnil(ctx->bifcxrun); return; } /* get the arguments: object, class */ obj = runpopobj(ctx->bifcxrun); cls = runpopobj(ctx->bifcxrun); /* return the result from bifinh() */ runpush(ctx->bifcxrun, runclog(bifinh(voc, vocinh(voc, obj), cls)), &val); } /* firstsc(obj) - get the first superclass of an object */ void bif1sc(bifcxdef *ctx, int argc) { objnum obj; objnum sc; bifcntargs(ctx, 1, argc); obj = runpopobj(ctx->bifcxrun); sc = objget1sc(ctx->bifcxrun->runcxmem, obj); runpobj(ctx->bifcxrun, sc); } /* firstobj */ void biffob(bifcxdef *ctx, int argc) { vocidef ***vpg; vocidef **v; objnum obj; int i; int j; objnum cls; voccxdef *voc = ctx->bifcxrun->runcxvoc; /* get class to search for, if one is specified */ if (argc == 0) cls = MCMONINV; else if (argc == 1) cls = runpopobj(ctx->bifcxrun); else runsig(ctx->bifcxrun, ERR_BIFARGC); for (vpg = voc->voccxinh, i = 0 ; i < VOCINHMAX ; ++vpg, ++i) { if (!*vpg) continue; for (v = *vpg, obj = (i << 8), j = 0 ; j < 256 ; ++v, ++obj, ++j) { if (!*v || ((*v)->vociflg & VOCIFCLASS) || (cls != MCMONINV && !bifinh(voc, *v, cls))) continue; /* this is an object we can use - push it */ runpobj(ctx->bifcxrun, obj); return; } } /* no objects found at all - return nil */ runpnil(ctx->bifcxrun); } /* nextobj */ void bifnob(bifcxdef *ctx, int argc) { objnum prv; vocidef ***vpg; vocidef **v; objnum obj; int i; int j; objnum cls; voccxdef *voc = ctx->bifcxrun->runcxvoc; /* get last position in search */ prv = runpopobj(ctx->bifcxrun); /* get class to search for, if one is specified */ if (argc == 1) cls = MCMONINV; else if (argc == 2) cls = runpopobj(ctx->bifcxrun); else runsig(ctx->bifcxrun, ERR_BIFARGC); /* start at previous object plus 1 */ i = (prv >> 8); vpg = voc->voccxinh + i; j = (prv & 255); obj = prv; v = (*vpg) + j; for (;;) { ++j; ++obj; ++v; if (j == 256) { j = 0; ++i; ++vpg; if (!*vpg) { obj += 255; j += 255; continue; } v = (*vpg); } if (i >= VOCINHMAX) { runpnil(ctx->bifcxrun); return; } if (!*v || ((*v)->vociflg & VOCIFCLASS) || (cls != MCMONINV && !bifinh(voc, *v, cls))) continue; /* this is an object we can use - push it */ runpobj(ctx->bifcxrun, obj); return; } } /* setversion */ void bifsvn(bifcxdef *ctx, int argc) { bifcntargs(ctx, 1, argc); (void)runpopstr(ctx->bifcxrun); /* note - setversion doesn't do anything in v2; uses timestamp instead */ } /* getarg */ void bifarg(bifcxdef *ctx, int argc) { int argnum; bifcntargs(ctx, 1, argc); bifchkarg(ctx, DAT_NUMBER); /* get and verify argument number */ argnum = runpopnum(ctx->bifcxrun); if (argnum < 1) runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "getarg"); runrepush(ctx->bifcxrun, ctx->bifcxrun->runcxbp - argnum - 1); } /* datatype */ void biftyp(bifcxdef *ctx, int argc) { runsdef val; bifcntargs(ctx, 1, argc); /* get whatever it is, and push the type */ runpop(ctx->bifcxrun, &val); val.runsv.runsvnum = val.runstyp; /* new value is the datatype */ runpush(ctx->bifcxrun, DAT_NUMBER, &val); } /* undo */ void bifund(bifcxdef *ctx, int argc) { objucxdef *ucx = ctx->bifcxrun->runcxvoc->voccxundo; mcmcxdef *mcx = ctx->bifcxrun->runcxmem; errcxdef *ec = ctx->bifcxerr; int err; int undone; runsdef val; bifcntargs(ctx, 0, argc); /* no arguments */ ERRBEGIN(ec) if (ucx) { objundo(mcx, ucx); /* try to undo to previous savepoint */ undone = TRUE; /* looks like we succeeded */ } else undone = FALSE; /* no undo context; can't undo */ ERRCATCH(ec, err) if (err == ERR_NOUNDO || err == ERR_ICUNDO) undone = FALSE; else errrse(ec); /* don't know how to handle other errors */ ERREND(ec) /* return a value indicating whether the undo operation succeeded */ runpush(ctx->bifcxrun, runclog(undone), &val); /* note that the rest of the command line is to be ignored */ ctx->bifcxrun->runcxvoc->voccxflg |= VOCCXFCLEAR; } /* flags for defined() function */ #define BIFDEF_DEFINED_ANY 1 #define BIFDEF_DEFINED_DIRECTLY 2 #define BIFDEF_DEFINED_INHERITS 3 #define BIFDEF_DEFINED_GET_CLASS 4 /* defined */ void bifdef(bifcxdef *ctx, int argc) { prpnum prpn; objnum objn; uint ofs; runsdef val; objnum def_objn; int flag; /* get object and property arguments */ objn = runpopobj(ctx->bifcxrun); prpn = runpopprp(ctx->bifcxrun); /* if there's a flag argument, get it as well */ if (argc == 3) { /* get the flag */ flag = (int)runpopnum(ctx->bifcxrun); } else { /* check the argument count */ bifcntargs(ctx, 2, argc); /* use the default flag value (DEFINES_OR_INHERITS) */ flag = BIFDEF_DEFINED_ANY; } /* get the offset of the property and the defining object */ ofs = objgetap(ctx->bifcxrun->runcxmem, objn, prpn, &def_objn, FALSE); /* determine the type of information they want */ switch(flag) { case BIFDEF_DEFINED_ANY: /* if the property is defined, return true, else return nil */ runpush(ctx->bifcxrun, runclog(ofs != 0), &val); break; case BIFDEF_DEFINED_DIRECTLY: /* if the property is defined directly by the object, return true */ runpush(ctx->bifcxrun, runclog(ofs != 0 && def_objn == objn), &val); break; case BIFDEF_DEFINED_INHERITS: /* if the property is inherited, return true */ runpush(ctx->bifcxrun, runclog(ofs != 0 && def_objn != objn), &val); break; case BIFDEF_DEFINED_GET_CLASS: /* if it's defined, return the defining object, otherwise nil */ if (ofs == 0) runpnil(ctx->bifcxrun); else runpobj(ctx->bifcxrun, def_objn); break; default: /* invalid flag value */ runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "defined"); } } /* proptype */ void bifpty(bifcxdef *ctx, int argc) { prpnum prpn; objnum objn; uint ofs; runsdef val; objnum orn; objdef *objptr; prpdef *propptr; bifcntargs(ctx, 2, argc); /* get offset of obj.prop */ objn = runpopobj(ctx->bifcxrun); prpn = runpopprp(ctx->bifcxrun); ofs = objgetap(ctx->bifcxrun->runcxmem, objn, prpn, &orn, FALSE); if (ofs) { /* lock the object, read the prpdef, and unlock it */ objptr = (objdef *)mcmlck(ctx->bifcxrun->runcxmem, (mcmon)orn); propptr = objofsp(objptr, ofs); val.runsv.runsvnum = prptype(propptr); mcmunlck(ctx->bifcxrun->runcxmem, (mcmon)orn); } else { /* property is not defined by object - indicate that type is nil */ val.runsv.runsvnum = DAT_NIL; } /* special case: DAT_DEMAND -> DAT_LIST (for contents properties) */ if (val.runsv.runsvnum == DAT_DEMAND) val.runsv.runsvnum = DAT_LIST; /* return the property type as a number */ runpush(ctx->bifcxrun, DAT_NUMBER, &val); } /* outhide */ void bifoph(bifcxdef *ctx, int argc) { runsdef val; int hidden, output_occurred; bifcntargs(ctx, 1, argc); outstat(&hidden, &output_occurred); if (runtostyp(ctx->bifcxrun) == DAT_TRUE) { /* throw away the flag */ rundisc(ctx->bifcxrun); /* figure out appropriate return value */ if (!hidden) val.runsv.runsvnum = 0; else if (!output_occurred) val.runsv.runsvnum = 1; else val.runsv.runsvnum = 2; runpush(ctx->bifcxrun, DAT_NUMBER, &val); /* actually hide the output, resetting count flag */ outhide(); } else if (runtostyp(ctx->bifcxrun) == DAT_NIL) { /* throw away the flag */ rundisc(ctx->bifcxrun); /* show output, returning status */ runpush(ctx->bifcxrun, runclog(outshow()), &val); } else if (runtostyp(ctx->bifcxrun) == DAT_NUMBER) { int n = runpopnum(ctx->bifcxrun); if (n == 0) { /* output was not hidden - show output and return status */ runpush(ctx->bifcxrun, runclog(outshow()), &val); } else if (n == 1) { /* * Output was hidden, but no output had occurred yet. * Leave output hidden and return whether any output has * occurred. */ runpush(ctx->bifcxrun, runclog(output_occurred), &val); } else if (n == 2) { /* * Output was hidden, and output had already occurred. If * more output has occurred, return true, else return nil. * In either case, set the output_occurred flag back to * true, since it was true before the outhide(true). */ runpush(ctx->bifcxrun, runclog(output_occurred), &val); outsethidden(); } else errsig1(ctx->bifcxerr, ERR_INVVBIF, ERRTSTR, "outhide"); } else errsig(ctx->bifcxerr, ERR_REQNUM); } /* put a numeric value in a list */ static uchar *bifputnum(uchar *lstp, uint val) { *lstp++ = DAT_NUMBER; oswp4s(lstp, (long)val); return(lstp + 4); } /* gettime */ void biftim(bifcxdef *ctx, int argc) { time_t timer; struct tm *tblock; uchar ret[80]; uchar *p; runsdef val; int typ; if (argc == 1) { /* get the time type */ typ = (int)runpopnum(ctx->bifcxrun); } else { /* make sure no arguments are specified */ bifcntargs(ctx, 0, argc); /* use the default time type */ typ = 1; } switch(typ) { case 1: /* * default information format - list format with current system * time and date */ /* make sure the time zone is set up properly */ os_tzset(); /* get the local time information */ timer = time(NULL); tblock = localtime(&timer); /* adjust values for return format */ tblock->tm_year += 1900; tblock->tm_mon++; tblock->tm_wday++; tblock->tm_yday++; /* build return list value */ oswp2(ret, 47); p = ret + 2; p = bifputnum(p, tblock->tm_year); p = bifputnum(p, tblock->tm_mon); p = bifputnum(p, tblock->tm_mday); p = bifputnum(p, tblock->tm_wday); p = bifputnum(p, tblock->tm_yday); p = bifputnum(p, tblock->tm_hour); p = bifputnum(p, tblock->tm_min); p = bifputnum(p, tblock->tm_sec); *p++ = DAT_NUMBER; oswp4s(p, (long)timer); val.runstyp = DAT_LIST; val.runsv.runsvstr = ret; runpush(ctx->bifcxrun, DAT_LIST, &val); break; case 2: /* * High-precision system timer value - returns the system time * in milliseconds, relative to an arbitrary zero point */ runpnum(ctx->bifcxrun, os_get_sys_clock_ms()); break; default: /* other types are invalid */ runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "gettime"); break; } } /* getfuse */ void bifgfu(bifcxdef *ctx, int argc) { vocddef *daem; objnum func; runsdef val; runcxdef *rcx = ctx->bifcxrun; int slots; prpnum prop; voccxdef *vcx = ctx->bifcxrun->runcxvoc; bifcntargs(ctx, 2, argc); if (runtostyp(rcx) == DAT_FNADDR) { /* check on a setfuse()-style fuse: get fnaddr, parm */ func = runpopfn(rcx); runpop(rcx, &val); for (slots = vcx->voccxfuc, daem = vcx->voccxfus ; slots ; ++daem, --slots) { if (daem->vocdfn == func && daem->vocdarg.runstyp == val.runstyp && !memcmp(&val.runsv, &daem->vocdarg.runsv, (size_t)datsiz(val.runstyp, &val.runsv))) goto ret_num; } } else { /* check on a notify()-style fuse: get object, &message */ func = runpopobj(rcx); prop = runpopprp(rcx); for (slots = vcx->voccxalc, daem = vcx->voccxalm ; slots ; ++daem, --slots) { if (daem->vocdfn == func && daem->vocdprp == prop) goto ret_num; } } /* didn't find anything - return nil */ runpush(rcx, DAT_NIL, &val); return; ret_num: /* return current daem->vocdtim */ runpnum(rcx, (long)daem->vocdtim); return; } /* runfuses */ void bifruf(bifcxdef *ctx, int argc) { int ret; runsdef val; bifcntargs(ctx, 0, argc); ret = exefuse(ctx->bifcxrun->runcxvoc, TRUE); runpush(ctx->bifcxrun, runclog(ret), &val); } /* rundaemons */ void bifrud(bifcxdef *ctx, int argc) { bifcntargs(ctx, 0, argc); exedaem(ctx->bifcxrun->runcxvoc); } /* intersect */ void bifsct(bifcxdef *bifctx, int argc) { runcxdef *ctx = bifctx->bifcxrun; uchar *l1; uchar *l2; uchar *l3; uint siz1; uint siz2; uint siz3; uchar *p; uint l; uint dsz1; uint dsz2; runsdef val; runsdef stk1, stk2; bifcntargs(bifctx, 2, argc); l1 = runpoplst(ctx); siz1 = osrp2(l1); l2 = runpoplst(ctx); siz2 = osrp2(l2); /* make sure the first list is smaller - if not, switch them */ if (siz1 > siz2) l3 = l1, l1 = l2, l2 = l3, siz3 = siz1, siz1 = siz2, siz2 = siz3; /* size of result is at most size of smaller list (which is now siz1) */ stk1.runstyp = stk2.runstyp = DAT_LIST; stk1.runsv.runsvstr = l1; stk2.runsv.runsvstr = l2; runhres2(ctx, siz1, 2, &stk1, &stk2); l1 = stk1.runsv.runsvstr; l2 = stk2.runsv.runsvstr; l3 = ctx->runcxhp + 2; /* go through list1, and copy each element that is found in list2 */ for (l1 += 2, l2 += 2, siz1 -= 2, siz2 -= 2 ; siz1 ; lstadv(&l1, &siz1)) { dsz1 = datsiz(*l1, l1 + 1) + 1; for (l = siz2, p = l2 ; l ; lstadv(&p, &l)) { dsz2 = datsiz(*p, p + 1) + 1; #ifndef AMIGA if (dsz1 == dsz2 && !memcmp(l1, p, (size_t)dsz1)) #else /* AMIGA */ if (!memcmp(l1, p, (size_t)dsz1) && (dsz1 == dsz2) ) #endif /* AMIGA */ { memcpy(l3, p, (size_t)dsz1); l3 += dsz1; break; } } } /* set up return value, take it out of the heap, and push value */ val.runsv.runsvstr = ctx->runcxhp; val.runstyp = DAT_LIST; oswp2(ctx->runcxhp, (uint)(l3 - ctx->runcxhp)); ctx->runcxhp = l3; runrepush(ctx, &val); } /* * Portable keystroke mappings. We map the extended key codes to these * strings, so that the TADS code can access arrow keys and the like. */ static char *ext_key_names[] = { "[up]", /* CMD_UP - 1 */ "[down]", /* CMD_DOWN - 2 */ "[right]", /* CMD_RIGHT - 3 */ "[left]", /* CMD_LEFT - 4 */ "[end]", /* CMD_END - 5 */ "[home]", /* CMD_HOME - 6 */ "[del-eol]", /* CMD_DEOL - 7 */ "[del-line]", /* CMD_KILL - 8 */ "[del]", /* CMD_DEL - 9 */ "[scroll]", /* CMD_SCR - 10 */ "[page up]", /* CMD_PGUP - 11 */ "[page down]", /* CMD_PGDN - 12 */ "[top]", /* CMD_TOP - 13 */ "[bottom]", /* CMD_BOT - 14 */ "[f1]", /* CMD_F1 - 15 */ "[f2]", /* CMD_F2 - 16 */ "[f3]", /* CMD_F3 - 17 */ "[f4]", /* CMD_F4 - 18 */ "[f5]", /* CMD_F5 - 19 */ "[f6]", /* CMD_F6 - 20 */ "[f7]", /* CMD_F7 - 21 */ "[f8]", /* CMD_F8 - 22 */ "[f9]", /* CMD_F9 - 23 */ "[f10]", /* CMD_F10 - 24 */ "[?]", /* invalid key - CMD_CHOME - 25 */ "[tab]", /* CMD_TAB - 26 */ "[?]", /* invalid key - shift-F2 - 27 */ "[?]", /* not used (obsolete) - 28 */ "[word-left]", /* CMD_WORD_LEFT - 29 */ "[word-right]", /* CMD_WORD_RIGHT - 30 */ "[del-word]", /* CMD_WORDKILL - 31 */ "[eof]", /* CMD_EOF - 32 */ "[break]" /* CMD_BREAK - 33 */ }; /* * Get the name of a keystroke. Pass in the one or two characters * returned by os_getc(), and we'll fill in the buffer with the * inputkey() name of the keystroke. Returns true if the key was valid, * false if not. 'c' is the first character returned by os_getc() for * the keystroke; if 'c' is zero, then 'extc' is the character returned * by the second call to os_getc() to get the CMD_xxx code for the * keystroke. * * The name buffer should be 20 characters long - this will ensure that * any name will fit. * * For ordinary, printable characters, we'll simply return the * character; the letter 'a', for example, is returned as the string "a". * * For extended keys, we'll look up the CMD_xxx code and return the name * of the command, enclosed in square brackets; see the ext_key_names * table for the mappings. The left-arrow cursor key, for example, * returns "[left]". * * For control characters, we'll generate a name like "[ctrl-a]", except * for the following characters: * *. ascii 10 returns "\n" *. ascii 13 returns "\n" *. ascii 9 returns "\t" *. ascii 8 returns "[bksp]" */ static int get_ext_key_name(char *namebuf, int c, int extc) { /* if it's a control character, translate it */ if (c >= 1 && c <= 27) { switch(c) { case 10: case 13: /* return '\n' for LF and CR characters */ strcpy(namebuf, "\\n"); return TRUE; case 9: /* return '\t' for TAB characters */ strcpy(namebuf, "\\t"); return TRUE; case 8: /* return '[bksp]' for backspace characters */ strcpy(namebuf, "[bksp]"); return TRUE; case 27: /* return '[esc]' for the escape key */ strcpy(namebuf, "[esc]"); return TRUE; default: /* return '[ctrl-X]' for other control characters */ strcpy(namebuf, "[ctrl-X]"); namebuf[6] = (char)(c + 'a' - 1); return TRUE; } } /* if it's any other non-extended key, return it as-is */ if (c != 0) { namebuf[0] = c; namebuf[1] = '\0'; return TRUE; } /* if it's in the key name array, use the array entry */ if (extc >= 1 && extc <= (int)(sizeof(ext_key_names)/sizeof(ext_key_names[0]))) { /* use the array name */ strcpy(namebuf, ext_key_names[extc - 1]); return TRUE; } /* if it's in the ALT key range, generate an ALT key name */ if (extc >= CMD_ALT && extc <= CMD_ALT + 25) { /* generate an ALT key name */ strcpy(namebuf, "[alt-X]"); namebuf[5] = (char)(extc - CMD_ALT + 'a'); return TRUE; } /* it's not a valid key - use '[?]' as the name */ strcpy(namebuf, "[?]"); return FALSE; } /* inputkey */ void bifink(bifcxdef *ctx, int argc) { int c; int extc; char str[20]; size_t len; bifcntargs(ctx, 0, argc); tioflushn(ctx->bifcxtio, 0); /* get a key */ c = os_getc_raw(); /* if it's extended, get the second part of the extended sequence */ extc = (c == 0 ? os_getc_raw() : 0); /* map the extended key name */ get_ext_key_name(str, c, extc); /* get the length of the name */ len = strlen(str); /* reset the [more] counter */ outreset(); /* return the string, translating escapes */ runpstr(ctx->bifcxrun, str, len, 0); } /* get direct/indirect object word list */ void bifwrd(bifcxdef *ctx, int argc) { int ob; vocoldef *v; uchar buf[128]; uchar *dst; uchar *src; uint len; runsdef val; bifcntargs(ctx, 1, argc); /* figure out what word list to get */ ob = runpopnum(ctx->bifcxrun); switch(ob) { case 1: v = ctx->bifcxrun->runcxvoc->voccxdobj; break; case 2: v = ctx->bifcxrun->runcxvoc->voccxiobj; break; default: runpnil(ctx->bifcxrun); return; } /* now build a list of strings from the words, if there are any */ if (v != 0 && voclistlen(v) != 0 && v->vocolfst != 0 && v->vocollst != 0) { for (dst = buf + 2, src = (uchar *)v->vocolfst ; src <= (uchar *)v->vocollst ; src += len + 1) { *dst++ = DAT_SSTRING; len = strlen((char *)src); oswp2(dst, len + 2); strcpy((char *)dst + 2, (char *)src); dst += len + 2; } } else dst = buf + 2; /* finish setting up the list length and return it */ len = dst - buf; oswp2(buf, len); val.runsv.runsvstr = buf; val.runstyp = DAT_LIST; runpush(ctx->bifcxrun, DAT_LIST, &val); } /* add a vocabulary word to an object */ void bifadw(bifcxdef *ctx, int argc) { uchar *wrd; objnum objn; prpnum prpn; vocidef *voci; int classflg; voccxdef *voc = ctx->bifcxrun->runcxvoc; bifcntargs(ctx, 3, argc); /* get the arguments */ objn = runpopobj(ctx->bifcxrun); prpn = runpopprp(ctx->bifcxrun); wrd = runpopstr(ctx->bifcxrun); /* make sure the property is a valid part of speech property */ if (!prpisvoc(prpn)) runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "addword"); /* get the vocidef for the object, and see if it's a class object */ voci = vocinh(voc, objn); classflg = VOCFNEW; if (voci->vociflg & VOCIFCLASS) classflg |= VOCFCLASS; /* add the word */ vocadd(voc, prpn, objn, classflg, (char *)wrd); /* generate undo for the operation */ vocdusave_addwrd(voc, objn, prpn, classflg, (char *)wrd); } /* delete a vocabulary word from an object */ void bifdlw(bifcxdef *ctx, int argc) { uchar *wrd; objnum objn; prpnum prpn; voccxdef *voc = ctx->bifcxrun->runcxvoc; bifcntargs(ctx, 3, argc); /* get the arguments */ objn = runpopobj(ctx->bifcxrun); prpn = runpopprp(ctx->bifcxrun); wrd = runpopstr(ctx->bifcxrun); /* make sure the property is a valid part of speech property */ if (!prpisvoc(prpn)) runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "delword"); /* delete the word */ vocdel1(voc, objn, (char *)wrd, prpn, FALSE, FALSE, TRUE); } /* callback context for word list builder */ struct bifgtw_cb_ctx { uchar *p; int typ; }; /* callback for word list builder */ static void bifgtw_cb(void *ctx0, vocdef *voc, vocwdef *vocw) { struct bifgtw_cb_ctx *ctx = (struct bifgtw_cb_ctx *)ctx0; /* ignore deleted objects */ if (vocw->vocwflg & VOCFDEL) return; /* ignore objects of the inappropriate type */ if (vocw->vocwtyp != ctx->typ) return; /* the datatype is string */ *ctx->p = DAT_SSTRING; /* copy the first word */ memcpy(ctx->p + 3, voc->voctxt, (size_t)voc->voclen); /* if there are two words, add a space and the second word */ if (voc->vocln2) { *(ctx->p + 3 + voc->voclen) = ' '; memcpy(ctx->p + 4 + voc->voclen, voc->voctxt + voc->voclen, (size_t)voc->vocln2); oswp2(ctx->p + 1, voc->voclen + voc->vocln2 + 3); ctx->p += voc->voclen + voc->vocln2 + 4; } else { oswp2(ctx->p + 1, voc->voclen+2); ctx->p += voc->voclen + 3; } } /* get the list of words for an object for a particular part of speech */ void bifgtw(bifcxdef *ctx, int argc) { objnum objn; prpnum prpn; voccxdef *voc = ctx->bifcxrun->runcxvoc; int cnt; int siz; runsdef val; struct bifgtw_cb_ctx fnctx; bifcntargs(ctx, 2, argc); /* get the arguments */ objn = runpopobj(ctx->bifcxrun); prpn = runpopprp(ctx->bifcxrun); /* make sure the property is a valid part of speech property */ if (!prpisvoc(prpn)) runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "delword"); /* get the size of the list we'll need to build */ voc_count(voc, objn, prpn, &cnt, &siz); /* * calculate how much space it will take to make a list out of all * these words: 2 bytes for the list length header; plus, for each * entry, 1 byte for the type header, 2 bytes for the string size * header, and possibly one extra byte for the two-word separator -- * a total of 4 bytes extra per word. */ siz += 2 + 4*cnt; /* reserve the space */ runhres(ctx->bifcxrun, siz, 0); /* set up our callback context, and build the list */ fnctx.p = ctx->bifcxrun->runcxhp + 2; fnctx.typ = prpn; voc_iterate(voc, objn, bifgtw_cb, &fnctx); /* set up the return value */ val.runstyp = DAT_LIST; val.runsv.runsvstr = ctx->bifcxrun->runcxhp; /* write the list length, and advance past the space we used */ oswp2(ctx->bifcxrun->runcxhp, fnctx.p - ctx->bifcxrun->runcxhp); ctx->bifcxrun->runcxhp = fnctx.p; /* return the list */ runrepush(ctx->bifcxrun, &val); } /* verbinfo service routine - add an object to the output list */ static uchar *bifvin_putprpn(uchar *p, prpnum prpn) { *p++ = DAT_PROPNUM; oswp2(p, prpn); return p + 2; } /* verbinfo */ void bifvin(bifcxdef *ctx, int argc) { objnum verb; objnum prep; uchar tplbuf[VOCTPL2SIZ]; int newstyle; /* get the verb */ verb = runpopobj(ctx->bifcxrun); /* check for the presence of a preposition */ if (argc == 1) { /* no preposition */ prep = MCMONINV; } else { /* the second argument is the preposition */ bifcntargs(ctx, 2, argc); prep = runpopobj(ctx->bifcxrun); } /* look up the template */ if (voctplfnd(ctx->bifcxrun->runcxvoc, verb, prep, tplbuf, &newstyle)) { prpnum prp_do, prp_verdo, prp_io, prp_verio; int flg_dis_do; ushort siz; uchar *p; runsdef val; /* get the information from the template */ prp_do = voctpldo(tplbuf); prp_verdo = voctplvd(tplbuf); prp_io = voctplio(tplbuf); prp_verio = voctplvi(tplbuf); flg_dis_do = (voctplflg(tplbuf) & VOCTPLFLG_DOBJ_FIRST) != 0; /* * figure space for the return value: if there's a prep, three * property pointers plus a boolean, otherwise just two property * pointers */ siz = 2 + 2*(2+1); if (prep != MCMONINV) siz += (2+1) + 1; /* reserve the space */ runhres(ctx->bifcxrun, siz, 0); /* build the output list */ p = ctx->bifcxrun->runcxhp; oswp2(p, siz); p += 2; p = bifvin_putprpn(p, prp_verdo); if (prep == MCMONINV) { p = bifvin_putprpn(p, prp_do); } else { p = bifvin_putprpn(p, prp_verio); p = bifvin_putprpn(p, prp_io); *p++ = runclog(flg_dis_do); } /* build the return value */ val.runstyp = DAT_LIST; val.runsv.runsvstr = ctx->bifcxrun->runcxhp; /* consume the space */ ctx->bifcxrun->runcxhp += siz; /* return the list */ runrepush(ctx->bifcxrun, &val); } else { /* no template for this verb - return nil */ runpnil(ctx->bifcxrun); } } /* clearscreen */ void bifcls(bifcxdef *ctx, int argc) { /* this takes no arguments */ bifcntargs(ctx, 0, argc); /* flush any pending output */ tioflushn(ctx->bifcxtio, 0); /* clear the screen */ oscls(); } /* * File operations */ /* * fopen(file, mode). * * Operations are allowed only if they conform to the current I/O safety * level. The safety level can be set by the user on the command line * when running the game, and some implementations may allow the setting * to be saved as a preference. The possible levels are: * *. 0 - minimum safety - read and write in any directory *. 1 - read in any directory, write in current directory *. 2 - read/write access in current directory only *. 3 - read-only access in current directory only *. 4 - maximum safety - no file I/O allowed * * When operations are allowed only in the current directory, the * operations will fail if the filename contains any sort of path * specifier (for example, on Unix, any file that contains a '/' is * considered to have a path specifier, and will always fail if * operations are only allowed in the current directory). */ void biffopen(bifcxdef *ctx, int argc) { char fname[OSFNMAX]; uchar *p; uchar *mode; int modelen; int fnum; osfildef *fp; int bin_mode = TRUE; /* flag: mode is binary (rather than text) */ int rw_mode = FALSE; /* flag: both read and write are allowed */ char main_mode; /* 'r' for read, 'w' for write */ int in_same_dir; /* flag: file is in current directory */ appctxdef *appctx; bifcntargs(ctx, 2, argc); /* get the filename */ p = runpopstr(ctx->bifcxrun); bifcstr(ctx, fname, (size_t)sizeof(fname), p); /* * If it's a relative path, combine it with the game file path to form * the absolute path. This ensures that relative paths are always * relative to the original working directory if the OS-level working * directory has changed. */ if (!os_is_file_absolute(fname)) { /* combine the game file path with the relative filename */ char newname[OSFNMAX]; os_build_full_path(newname, sizeof(newname), ctx->bifcxrun->runcxgamepath, fname); /* replace the original filename with the full path */ strcpy(fname, newname); } /* get the mode string */ mode = runpopstr(ctx->bifcxrun); modelen = osrp2(mode) - 2; mode += 2; if (modelen < 1) goto bad_mode; /* allocate a filenum for the file */ for (fnum = 0 ; fnum < BIFFILMAX ; ++fnum) { if (ctx->bifcxfile[fnum].fp == 0) break; } if (fnum == BIFFILMAX) { /* return nil to indicate failure */ runpnil(ctx->bifcxrun); return; } /* parse the main mode */ switch(*mode) { case 'w': case 'W': main_mode = 'w'; break; case 'r': case 'R': main_mode = 'r'; break; default: goto bad_mode; } /* skip the main mode, and check for a '+' flag */ ++mode; --modelen; if (modelen > 0 && *mode == '+') { /* note the read/write mode */ rw_mode = TRUE; /* skip the speciifer */ ++mode; --modelen; } /* check for a binary/text specifier */ if (modelen > 0) { switch(*mode) { case 'b': case 'B': bin_mode = TRUE; break; case 't': case 'T': bin_mode = FALSE; break; default: goto bad_mode; } /* skip the binary/text specifier */ ++mode; --modelen; } /* it's an error if there's anything left unparsed */ if (modelen > 0) goto bad_mode; /* * If we have a host application context, and it provides a file * safety level callback function, ask the host system for its * current file safety level, which overrides our current setting. */ appctx = ctx->bifcxappctx; if (appctx != 0 && appctx->get_io_safety_level != 0) { /* * ask the host system for the current level, and override any * setting we previously had */ (*appctx->get_io_safety_level)( appctx->io_safety_level_ctx, &ctx->bifcxsafetyr, &ctx->bifcxsafetyw); } /* * Check to see if the file is in the current working directory - if * not, we may have to disallow the operation based on safety level * settings. */ in_same_dir = os_is_file_in_dir( fname, ctx->bifcxrun->runcxgamepath, TRUE, FALSE); /* check file safety settings */ switch(main_mode) { case 'w': /* * writing - we must be at a safety level no higher than 2 * (read/write current directory) to write at all, and we must be * level 0 to write a file that's not in the current directory */ if (ctx->bifcxsafetyw > 2 || (!in_same_dir && ctx->bifcxsafetyw > 0)) { /* this operation is not allowed - return failure */ runpnil(ctx->bifcxrun); return; } break; case 'r': /* * reading - we must be at a safety level no higher than 3 (read * current directory) to read at all, and we must be at safety * level 1 (read any directory) or lower to read a file that's not * in the current directory */ if (ctx->bifcxsafetyr > 3 || (!in_same_dir && ctx->bifcxsafetyr > 1)) { /* this operation is not allowed - return failure */ runpnil(ctx->bifcxrun); return; } break; default: /* * fail the operation, as a code maintenance measure to make * sure that we add appropriate cases to this switch (even if * merely to allow the operation unconditionally) in the event * that more modes are added in the future */ goto bad_mode; } /* try opening the file */ switch(main_mode) { case 'w': /* check for binary vs text mode */ if (bin_mode) { /* * binary mode -- allow read/write or just writing, but in * either case truncate the file if it already exists, and * create a new file if it doesn't exist */ if (rw_mode) fp = osfoprwtb(fname, OSFTDATA); else fp = osfopwb(fname, OSFTDATA); } else { /* text mode - don't allow read/write on a text file */ if (rw_mode) goto bad_mode; /* open the file */ fp = osfopwt(fname, OSFTTEXT); } break; case 'r': /* check for binary vs text mode */ if (bin_mode) { /* * Binary mode -- allow read/write or just reading; leave * any existing file intact. */ if (rw_mode) { /* open for reading and writing, keeping existing data */ fp = osfoprwb(fname, OSFTDATA); } else { /* open for read-only */ fp = osfoprb(fname, OSFTDATA); } } else { /* text mode -- only allow reading */ if (rw_mode) goto bad_mode; /* open the file */ fp = osfoprt(fname, OSFTTEXT); } break; default: goto bad_mode; } /* if we couldn't open it, return nil */ if (fp == 0) { runpnil(ctx->bifcxrun); return; } /* store the flags */ ctx->bifcxfile[fnum].flags = 0; if (bin_mode) ctx->bifcxfile[fnum].flags |= BIFFIL_F_BINARY; /* remember the file handle */ ctx->bifcxfile[fnum].fp = fp; /* return the file number (i.e., the slot number) */ runpnum(ctx->bifcxrun, (long)fnum); return; /* come here on a mode error */ bad_mode: runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "fopen"); } /* service routine for file routines - get and validate a file number */ static osfildef *bif_get_file(bifcxdef *ctx, int *fnump, int *bin_modep) { long fnum; /* get the file number and make sure it's valid */ fnum = runpopnum(ctx->bifcxrun); if (fnum < 0 || fnum >= BIFFILMAX || ctx->bifcxfile[fnum].fp == 0) runsig(ctx->bifcxrun, ERR_BADFILE); /* put the validated file number, if the caller wants it */ if (fnump != 0) *fnump = (int)fnum; /* set the binary-mode flag, if the caller wants it */ if (bin_modep != 0) *bin_modep = ((ctx->bifcxfile[fnum].flags & BIFFIL_F_BINARY) != 0); /* return the file array pointer */ return ctx->bifcxfile[fnum].fp; } void biffclose(bifcxdef *ctx, int argc) { int fnum; osfildef *fp; /* get the file number */ bifcntargs(ctx, 1, argc); fp = bif_get_file(ctx, &fnum, 0); /* close the file and release the slot */ osfcls(fp); ctx->bifcxfile[fnum].fp = 0; } void bifftell(bifcxdef *ctx, int argc) { osfildef *fp; /* get the file number */ bifcntargs(ctx, 1, argc); fp = bif_get_file(ctx, (int *)0, 0); /* return the seek position */ runpnum(ctx->bifcxrun, osfpos(fp)); } void biffseek(bifcxdef *ctx, int argc) { osfildef *fp; long pos; /* get the file pointer */ bifcntargs(ctx, 2, argc); fp = bif_get_file(ctx, (int *)0, 0); /* get the seek position, and seek there */ pos = runpopnum(ctx->bifcxrun); osfseek(fp, pos, OSFSK_SET); } void biffseekeof(bifcxdef *ctx, int argc) { osfildef *fp; /* get the file pointer */ bifcntargs(ctx, 1, argc); fp = bif_get_file(ctx, (int *)0, 0); /* seek to the end */ osfseek(fp, 0L, OSFSK_END); } void biffwrite(bifcxdef *ctx, int argc) { osfildef *fp; char typ; char buf[32]; runsdef val; int bin_mode; /* get the file */ bifcntargs(ctx, 2, argc); fp = bif_get_file(ctx, (int *)0, &bin_mode); /* get the value to write */ runpop(ctx->bifcxrun, &val); typ = val.runstyp; if (bin_mode) { /* put a byte indicating the type */ if (osfwb(fp, &typ, 1)) goto ret_error; /* see what type of data we want to put */ switch(typ) { case DAT_NUMBER: oswp4s(buf, val.runsv.runsvnum); if (osfwb(fp, buf, 4)) goto ret_error; break; case DAT_SSTRING: /* write the string, including the length prefix */ if (osfwb(fp, val.runsv.runsvstr, osrp2(val.runsv.runsvstr))) goto ret_error; break; case DAT_TRUE: /* all we need for this is the type prefix */ break; default: /* other types are not acceptable */ runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "fwrite"); } } else { uint rem; uchar *p; switch(typ) { case DAT_SSTRING: /* * Copy and translate the string to our buffer, in pieces if * the size of the string exceeds that of our buffer. If we * encounter any escape codes, translate them. */ rem = osrp2(val.runsv.runsvstr) - 2; p = val.runsv.runsvstr + 2; while (rem > 0) { uchar *dst; uchar buf[256]; /* fill up the buffer */ for (dst = buf ; rem != 0 && (size_t)(dst - buf) < sizeof(buf) - 1 ; ++p, --rem) { /* if we have an escape character, translate it */ if (*p == '\\' && rem > 1) { /* skip the opening slash */ ++p; --rem; /* translate it */ switch(*p) { case 'n': *dst++ = '\n'; break; case 't': *dst++ = '\t'; break; default: *dst++ = *p; break; } } else { /* copy this character directly */ *dst++ = *p; } } /* null-terminate the buffer */ *dst = '\0'; /* write it out */ if (osfputs((char *)buf, fp) == EOF) goto ret_error; } /* done */ break; default: /* other types are not allowed */ runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "fwrite"); } } /* success */ runpnil(ctx->bifcxrun); return; ret_error: val.runstyp = DAT_TRUE; runpush(ctx->bifcxrun, DAT_TRUE, &val); } void biffread(bifcxdef *ctx, int argc) { osfildef *fp; char typ; char buf[32]; runsdef val; ushort len; int bin_mode; /* get the file pointer */ bifcntargs(ctx, 1, argc); fp = bif_get_file(ctx, (int *)0, &bin_mode); if (bin_mode) { /* binary file - read the type byte */ if (osfrb(fp, &typ, 1)) goto ret_error; /* read the data according to the type */ switch(typ) { case DAT_NUMBER: if (osfrb(fp, buf, 4)) goto ret_error; runpnum(ctx->bifcxrun, osrp4s(buf)); break; case DAT_SSTRING: /* get the size */ if (osfrb(fp, buf, 2)) goto ret_error; len = osrp2(buf); /* reserve space */ runhres(ctx->bifcxrun, len, 0); /* read the string into the reserved space */ if (osfrb(fp, ctx->bifcxrun->runcxhp + 2, len - 2)) goto ret_error; /* set up the string */ oswp2(ctx->bifcxrun->runcxhp, len); val.runstyp = DAT_SSTRING; val.runsv.runsvstr = ctx->bifcxrun->runcxhp; /* consume the space */ ctx->bifcxrun->runcxhp += len; /* push the value */ runrepush(ctx->bifcxrun, &val); break; case DAT_TRUE: val.runstyp = DAT_TRUE; runpush(ctx->bifcxrun, DAT_TRUE, &val); break; default: goto ret_error; } } else { uchar buf[257]; uchar *dst; uchar *src; uint len; uint res_total; int found_nl; /* * reserve some space in the heap - we'll initially reserve * space for twice our buffer, in case every single character * needs to be expanded into an escape sequence */ res_total = sizeof(buf) * 2; runhres(ctx->bifcxrun, res_total, 0); /* set up our output value */ val.runstyp = DAT_SSTRING; val.runsv.runsvstr = ctx->bifcxrun->runcxhp; dst = ctx->bifcxrun->runcxhp + 2; /* keep going until we find a newline or run out of data */ for (found_nl = FALSE ; !found_nl ; ) { /* text-mode - read the result into our buffer */ if (!osfgets((char *)buf, sizeof(buf) - 1, fp)) { /* * if we found a newline, return what we have; * otherwise, return an error */ if (found_nl) break; else goto ret_error; } /* * make sure it's null-terminated, in case the buffer was * full */ buf[256] = '\0'; /* translate into the heap area we've reserved */ for (src = buf ; *src != '\0' ; ++src, ++dst) { /* determine if we need translations */ switch(*src) { case '\n': case '\r': /* translate to a newline sequence */ *dst++ = '\\'; *dst = 'n'; /* note that we've found our newline */ found_nl = TRUE; break; case '\t': /* translate to a tab sequence */ *dst++ = '\\'; *dst = 't'; break; case '\\': /* expand to a double-backslash sequence */ *dst++ = '\\'; *dst = '\\'; break; default: /* leave other characters intact */ *dst = *src; break; } } /* * If we didn't find the newline, we'll need more space. * This is a bit tricky, because the space we've already set * up may move if we compact the heap while asking for more * space. So, remember our current length, reserve another * buffer-full of space, and set everything up at the new * output location if necessary. */ if (!found_nl) { /* reserve another buffer-full (double for expansion) */ res_total += sizeof(buf) * 2; /* note our current offset */ len = dst - val.runsv.runsvstr; oswp2(val.runsv.runsvstr, len); /* ask for the space */ runhres(ctx->bifcxrun, res_total, 0); /* * Since we were at the top of the heap before, we * should still be at the top of the heap. If not, * we'll have to copy from our old location to the new * top of the heap. */ if (val.runsv.runsvstr != ctx->bifcxrun->runcxhp) { /* copy our existing text to our new location */ memmove(ctx->bifcxrun->runcxhp, val.runsv.runsvstr, len); /* fix up our pointer */ val.runsv.runsvstr = ctx->bifcxrun->runcxhp; } /* re-establish our output pointer at our new location */ dst = val.runsv.runsvstr + len; } } /* finish setting up the string */ len = dst - val.runsv.runsvstr; oswp2(val.runsv.runsvstr, len); /* consume the space */ ctx->bifcxrun->runcxhp += len; /* push the value */ runrepush(ctx->bifcxrun, &val); } /* success - we've already pushed the return value */ return; ret_error: runpnil(ctx->bifcxrun); } void bifcapture(bifcxdef *ctx, int argc) { mcmcxdef *mcx = ctx->bifcxrun->runcxmem; mcmon obj; uint siz; uint ofs; uchar *p; /* get the capture on/off flag */ bifcntargs(ctx, 1, argc); switch(runtostyp(ctx->bifcxrun)) { case DAT_TRUE: /* turn on capturing */ tiocapture(ctx->bifcxtio, mcx, TRUE); /* * The return value is a status code used to restore the * original status on the bracketing call to turn off output. * The only status necessary is the current output size. */ siz = tiocapturesize(ctx->bifcxtio); runpnum(ctx->bifcxrun, (long)siz); break; case DAT_NUMBER: /* get the original offset */ ofs = runpopnum(ctx->bifcxrun); /* get the capture object and size */ obj = tiogetcapture(ctx->bifcxtio); siz = tiocapturesize(ctx->bifcxtio); if (obj == MCMONINV) { runpnil(ctx->bifcxrun); return; } /* turn off capturing and reset the buffer on the outermost call */ if (ofs == 0) { tiocapture(ctx->bifcxtio, mcx, FALSE); tioclrcapture(ctx->bifcxtio); } /* lock the object */ p = mcmlck(mcx, obj); /* include only the part that happened after the matching call */ p += ofs; siz = (ofs > siz) ? 0 : siz - ofs; ERRBEGIN(ctx->bifcxerr) /* push the string onto the stack */ runpstr(ctx->bifcxrun, (char *)p, siz, 0); ERRCLEAN(ctx->bifcxerr) /* done with the object - unlock it */ mcmunlck(mcx, obj); ERRENDCLN(ctx->bifcxerr) /* done with the object - unlock it */ mcmunlck(mcx, obj); break; default: runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "outcapture"); } } /* * systemInfo */ void bifsysinfo(bifcxdef *ctx, int argc) { runsdef val; int id; long result; /* see what we have */ switch(id = (int)runpopnum(ctx->bifcxrun)) { case SYSINFO_SYSINFO: /* systemInfo call is supported in this version - return true */ bifcntargs(ctx, 1, argc); val.runstyp = DAT_TRUE; runpush(ctx->bifcxrun, DAT_TRUE, &val); break; case SYSINFO_VERSION: /* get the run-time version string */ bifcntargs(ctx, 1, argc); runpushcstr(ctx->bifcxrun, TADS_RUNTIME_VERSION, strlen(TADS_RUNTIME_VERSION), 0); break; case SYSINFO_OS_NAME: /* get the operating system name */ bifcntargs(ctx, 1, argc); runpushcstr(ctx->bifcxrun, OS_SYSTEM_NAME, strlen(OS_SYSTEM_NAME), 0); break; case SYSINFO_HTML: case SYSINFO_JPEG: case SYSINFO_PNG: case SYSINFO_WAV: case SYSINFO_MIDI: case SYSINFO_WAV_MIDI_OVL: case SYSINFO_WAV_OVL: case SYSINFO_PREF_IMAGES: case SYSINFO_PREF_SOUNDS: case SYSINFO_PREF_MUSIC: case SYSINFO_PREF_LINKS: case SYSINFO_MPEG: case SYSINFO_MPEG1: case SYSINFO_MPEG2: case SYSINFO_MPEG3: case SYSINFO_LINKS_HTTP: case SYSINFO_LINKS_FTP: case SYSINFO_LINKS_NEWS: case SYSINFO_LINKS_MAILTO: case SYSINFO_LINKS_TELNET: case SYSINFO_PNG_TRANS: case SYSINFO_PNG_ALPHA: case SYSINFO_OGG: case SYSINFO_MNG: case SYSINFO_MNG_TRANS: case SYSINFO_MNG_ALPHA: case SYSINFO_TEXT_HILITE: case SYSINFO_INTERP_CLASS: /* * these information types are all handled by the OS layer, and * take no additional arguments */ bifcntargs(ctx, 1, argc); if (os_get_sysinfo(id, 0, &result)) { /* we got a valid result - return it */ runpnum(ctx->bifcxrun, result); } else { /* the code was unknown - return nil */ runpnil(ctx->bifcxrun); } break; case SYSINFO_HTML_MODE: /* ask the output formatter for its current HTML setting */ bifcntargs(ctx, 1, argc); val.runstyp = runclog(tio_is_html_mode()); runpush(ctx->bifcxrun, val.runstyp, &val); break; case SYSINFO_TEXT_COLORS: /* * Text colors are only supported in full HTML interpreters. If * this is an HTML interpreter, ask the underlying OS layer about * color support; otherwise, colors are not available, since we * don't handle colors in our text-only HTML subset. * * Colors are NOT supported in the HTML mini-parser in text-only * interpreters in TADS 2. So, even if we're running in HTML * mode, if this is a text-only interpreter, we can't display text * colors. */ bifcntargs(ctx, 1, argc); if (os_get_sysinfo(SYSINFO_HTML, 0, &result) && result != 0) { /* * we're in HTML mode, so ask the underlying HTML OS * implementation for its level of text color support */ if (os_get_sysinfo(id, 0, &result)) { /* push the OS-level result */ runpnum(ctx->bifcxrun, result); } else { /* the OS code doesn't recognize it; assume no support */ runpnum(ctx->bifcxrun, SYSINFO_TXC_NONE); } } else { /* we're a text-only interpreter - no color support */ runpnum(ctx->bifcxrun, SYSINFO_TXC_NONE); } break; case SYSINFO_BANNERS: /* TADS 2 does not offer banner support */ bifcntargs(ctx, 1, argc); runpnum(ctx->bifcxrun, 0); break; default: /* * Other codes fail harmlessly with a nil return value. Pop all * remaining arguments and return nil. */ for ( ; argc > 1 ; --argc) rundisc(ctx->bifcxrun); runpnil(ctx->bifcxrun); break; } } /* * morePrompt - display the more prompt and wait for the user to respond */ void bifmore(bifcxdef *ctx, int argc) { /* this function takes no arguments */ bifcntargs(ctx, 0, argc); /* display the MORE prompt */ tioflushn(ctx->bifcxtio, 1); out_more_prompt(); } /* * parserSetMe */ void bifsetme(bifcxdef *ctx, int argc) { objnum new_me; /* this function takes one argument */ bifcntargs(ctx, 1, argc); /* get the new "Me" object */ new_me = runpopobj(ctx->bifcxrun); /* "Me" cannot be nil */ if (new_me == MCMONINV) runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "parserSetMe"); /* set the current "Me" object in the parser */ voc_set_me(ctx->bifcxrun->runcxvoc, new_me); } /* * parserGetMe */ void bifgetme(bifcxdef *ctx, int argc) { /* this function takes no arguments */ bifcntargs(ctx, 0, argc); /* return the current Me object */ runpobj(ctx->bifcxrun, ctx->bifcxrun->runcxvoc->voccxme); } /* * reSearch */ void bifresearch(bifcxdef *ctx, int argc) { uchar *patstr; size_t patlen; uchar *searchstr; size_t searchlen; int result_len; int match_ofs; /* this function takes two parameters: pattern, string */ bifcntargs(ctx, 2, argc); /* get the pattern string */ patstr = runpopstr(ctx->bifcxrun); patlen = osrp2(patstr) - 2; patstr += 2; /* get the search string */ searchstr = runpopstr(ctx->bifcxrun); searchlen = osrp2(searchstr) - 2; searchstr += 2; /* search for the pattern in the string */ match_ofs = re_compile_and_search(&ctx->bifcxregex, (char *)patstr, patlen, (char *)searchstr, searchlen, &result_len); /* * if we didn't match, return nil; otherwise, return a list with the * match offset and length */ if (match_ofs < 0) { /* no match - return nil */ runpnil(ctx->bifcxrun); } else { ushort listsiz; runsdef val; uchar *p; /* * build a list consisting of two numbers and a string: two * bytes for the list header, then two elements at (one byte for * the datatype header, four bytes for the number), then the * string element with (one byte for the datatype, two bytes for * the string length prefix, and the bytes of the string) */ listsiz = 2 + (1+4)*2 + (1 + 2 + (ushort)(result_len)); /* allocate the space */ runhres(ctx->bifcxrun, listsiz, 0); /* set up the list stack item */ val.runstyp = DAT_LIST; p = val.runsv.runsvstr = ctx->bifcxrun->runcxhp; /* set the list's length */ oswp2(p, listsiz); p += 2; /* * Add the offset element. For consistency with TADS * conventions, use 1 as the offset of the first character in * the string - this makes it easy to use the offset value with * substr(). */ *p++ = DAT_NUMBER; oswp4s(p, match_ofs + 1); p += 4; /* add the length element */ *p++ = DAT_NUMBER; oswp4s(p, result_len); p += 4; /* add the result string */ *p++ = DAT_SSTRING; oswp2(p, result_len + 2); p += 2; memcpy(p, ctx->bifcxregex.strbuf + match_ofs, result_len); /* reserve the space in the heap */ ctx->bifcxrun->runcxhp += listsiz; /* return the list */ runrepush(ctx->bifcxrun, &val); } } /* reGetGroup */ void bifregroup(bifcxdef *ctx, int argc) { int grp; size_t len; re_group_register *reg; ushort hplen; runsdef val; uchar *p; long numval; /* this function takes one parameter: the group number to retrieve */ bifcntargs(ctx, 1, argc); /* get the group number */ grp = (int)runpopnum(ctx->bifcxrun); /* make sure it's within range */ if (grp < 1 || grp > RE_GROUP_REG_CNT) runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "reGetGroup"); /* adjust from a 1-bias to an array index */ --grp; /* if the group was never set, return nil */ if (grp >= ctx->bifcxregex.cur_group) { runpnil(ctx->bifcxrun); return; } /* get the register */ reg = &ctx->bifcxregex.regs[grp]; /* if the group wasn't set, return nil */ if (reg->start_ofs == 0 || reg->end_ofs == 0) { runpnil(ctx->bifcxrun); return; } /* calculate the length of the string in this group */ len = reg->end_ofs - reg->start_ofs; /* * reserve the necessary heap space: two bytes for the list length * prefix, two number elements (one byte each for the type, four * bytes each for the value), and the string element (one byte for * the type, two bytes for the length prefix, plus the string * itself). */ hplen = (ushort)(2 + 2*(1+4) + (1 + 2 + len)); runhres(ctx->bifcxrun, hplen, 0); /* set up the stack value */ val.runstyp = DAT_LIST; p = val.runsv.runsvstr = ctx->bifcxrun->runcxhp; /* put in the list length prefix */ oswp2(p, hplen); p += 2; /* add the starting character position of the group - adjust to 1-bias */ *p++ = DAT_NUMBER; numval = (long)(reg->start_ofs - ctx->bifcxregex.strbuf) + 1; oswp4s(p, numval); p += 4; /* add the length of the group */ *p++ = DAT_NUMBER; numval = (long)(reg->end_ofs - reg->start_ofs); oswp4s(p, numval); p += 4; /* set up the string */ *p++ = DAT_SSTRING; oswp2(p, len+2); p += 2; memcpy(p, reg->start_ofs, len); /* consume the heap space */ ctx->bifcxrun->runcxhp += hplen; /* push the result */ runrepush(ctx->bifcxrun, &val); } /* * inputevent */ void bifinpevt(bifcxdef *ctx, int argc) { unsigned long timeout; int use_timeout; os_event_info_t info; int evt; uchar *p; ushort lstsiz; runsdef val; size_t paramlen; char keyname[20]; /* check for a timeout value */ if (argc == 0) { /* there's no timeout */ use_timeout = FALSE; timeout = 0; } else if (argc >= 1) { /* get the timeout value */ use_timeout = TRUE; timeout = (unsigned long)runpopnum(ctx->bifcxrun); } /* ensure we don't have too many arguments */ if (argc > 1) runsig(ctx->bifcxrun, ERR_BIFARGC); /* flush any pending output */ tioflushn(ctx->bifcxtio, 0); /* reset count of lines since keyboard input */ tioreset(ctx->bifcxtio); /* ask the OS code for an event */ evt = os_get_event(timeout, use_timeout, &info); /* * the list always minimally needs two bytes of length prefix plus a * number with the event code (one byte for the type, four bytes for * the value) */ lstsiz = 2 + (1 + 4); /* figure out how much space we'll need based on the event type */ switch(evt) { case OS_EVT_KEY: /* * we need space for a string with one or two bytes (depending * on whether or not we have an extended key code) - 1 byte for * type code, 2 for length prefix, and 1 or 2 for the string's * contents */ paramlen = (info.key[0] == 0 ? 2 : 1); /* map the extended key */ get_ext_key_name(keyname, info.key[0], info.key[1]); /* determine the length we need for the string */ paramlen = strlen(keyname); /* add it into the list */ lstsiz += 1 + 2 + paramlen; break; case OS_EVT_HREF: /* * we need space for the href string - 1 byte for type code, 2 * for length prefix, plus the string's contents */ paramlen = strlen(info.href); lstsiz += 1 + 2 + (ushort)paramlen; break; default: /* other event types have no extra data */ break; } /* allocate space for the list */ runhres(ctx->bifcxrun, lstsiz, 0); /* set up the stack value */ val.runstyp = DAT_LIST; p = val.runsv.runsvstr = ctx->bifcxrun->runcxhp; /* set up the list length prefix */ oswp2(p, lstsiz); p += 2; /* set up the event type element */ *p++ = DAT_NUMBER; oswp4s(p, evt); p += 4; /* add the event parameters, if any */ switch(evt) { case OS_EVT_KEY: /* set up the string for the key */ *p++ = DAT_SSTRING; oswp2(p, paramlen + 2); p += 2; /* add the characters to the string */ memcpy(p, keyname, paramlen); p += paramlen; break; case OS_EVT_HREF: /* add the string for the href */ *p++ = DAT_SSTRING; oswp2(p, paramlen + 2); memcpy(p + 2, info.href, paramlen); break; } /* consume the heap space */ ctx->bifcxrun->runcxhp += lstsiz; /* push the result */ runrepush(ctx->bifcxrun, &val); } /* timeDelay */ void bifdelay(bifcxdef *ctx, int argc) { long delay; /* ensure we have the right number of arguments */ bifcntargs(ctx, 1, argc); /* flush any pending output */ tioflushn(ctx->bifcxtio, 0); /* get the delay time */ delay = runpopnum(ctx->bifcxrun); /* let the system perform the delay */ os_sleep_ms(delay); } /* setOutputFilter */ void bifsetoutfilter(bifcxdef *ctx, int argc) { /* ensure we have the right number of arguments */ bifcntargs(ctx, 1, argc); /* see what we have */ switch(runtostyp(ctx->bifcxrun)) { case DAT_NIL: /* remove the current filter */ out_set_filter(MCMONINV); /* discard the argument */ rundisc(ctx->bifcxrun); break; case DAT_FNADDR: /* set the filter to the given function */ out_set_filter(runpopfn(ctx->bifcxrun)); break; default: /* anything else is invalid */ runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "setOutputFilter"); } } /* * Get an optional object argument. If the next argument is not an * object value, or we're out of arguments, we'll return MCMONINV. * Otherwise, we'll pop the object value and return it, decrementing the * remaining argument counter provided. */ static objnum bif_get_optional_obj_arg(bifcxdef *ctx, int *rem_argc) { /* if we're out of arguments, there's no object value */ if (*rem_argc == 0) return MCMONINV; /* * if the next argument is not an object or nil, we're out of object * arguments */ if (runtostyp(ctx->bifcxrun) != DAT_OBJECT && runtostyp(ctx->bifcxrun) != DAT_NIL) return MCMONINV; /* we have an object - remove it from the remaining argument count */ --(*rem_argc); /* pop and return the object value */ return runpopobjnil(ctx->bifcxrun); } /* * execCommand flag values */ #define EC_HIDE_SUCCESS 0x00000001 #define EC_HIDE_ERROR 0x00000002 #define EC_SKIP_VALIDDO 0x00000004 #define EC_SKIP_VALIDIO 0x00000008 /* * execCommand - execute a recursive command */ void bifexec(bifcxdef *ctx, int argc) { objnum actor; objnum verb; objnum dobj; objnum prep; objnum iobj; int err; uint capture_start; uint capture_end; objnum capture_obj; ulong flags; int hide_any; int rem_argc; /* * Check for the correct argument count. The first two arguments * are required; additional arguments are optional. */ if (argc < 2 || argc > 6) runsig(ctx->bifcxrun, ERR_BIFARGC); /* pop the arguments - actor, verb, dobj, prep, iobj */ actor = runpopobjnil(ctx->bifcxrun); verb = runpopobjnil(ctx->bifcxrun); /* * The other object arguments are optional. If we run into a * numeric argument, it's the flags value, in which case we're out * of objects. */ rem_argc = argc - 2; dobj = bif_get_optional_obj_arg(ctx, &rem_argc); prep = bif_get_optional_obj_arg(ctx, &rem_argc); iobj = bif_get_optional_obj_arg(ctx, &rem_argc); /* if we have a flags argument, pop it */ if (rem_argc > 0) { /* the last argument is the flags - pop the numeric value */ flags = runpopnum(ctx->bifcxrun); /* remove it from the remaining argument counter */ --rem_argc; } else { /* no flags specified - use zero by default */ flags = 0; } /* * make sure we don't have any arguments left - if we do, then it * means that we got an incorrect type and skipped an argument when * we were trying to sense the meanings of the arguments from their * types */ if (rem_argc != 0) runsig1(ctx->bifcxrun, ERR_INVTBIF, ERRTSTR, "execCommand"); /* if we're hiding any output, start output capture */ hide_any = ((flags & (EC_HIDE_SUCCESS | EC_HIDE_ERROR)) != 0); if (hide_any) { /* start capturing */ tiocapture(ctx->bifcxtio, ctx->bifcxrun->runcxmem, TRUE); /* note the current output position */ capture_start = tiocapturesize(ctx->bifcxtio); } /* execute the command */ err = execmd_recurs(ctx->bifcxrun->runcxvoc, actor, verb, dobj, prep, iobj, (flags & EC_SKIP_VALIDDO) == 0, (flags & EC_SKIP_VALIDIO) == 0); /* if we're hiding any output, end hiding */ if (hide_any) { uchar *p; int hide; /* get the capture buffer size */ capture_end = tiocapturesize(ctx->bifcxtio); /* turn off capture if it wasn't already on when we started */ if (capture_start == 0) tiocapture(ctx->bifcxtio, ctx->bifcxrun->runcxmem, FALSE); /* determine whether we're hiding or showing the result */ if (err == 0) hide = ((flags & EC_HIDE_SUCCESS) != 0); else hide = ((flags & EC_HIDE_ERROR) != 0); /* show or hide the result, as appropriate */ if (hide) { /* * We're hiding this result, so do not display the string. * If there's an enclosing capture, remove the string from * the enclosing capture. */ if (capture_start != 0) tiopopcapture(ctx->bifcxtio, capture_start); } else { /* * We're showing the text. If we're in an enclosing * capture, do nothing - simply leave the string in the * enclosing capture buffer; otherwise, actually display it */ if (capture_start == 0) { /* lock the capture object */ capture_obj = tiogetcapture(ctx->bifcxtio); p = mcmlck(ctx->bifcxrun->runcxmem, capture_obj); ERRBEGIN(ctx->bifcxerr) { /* display the string */ outformatlen((char *)p + capture_start, capture_end - capture_start); } ERRCLEAN(ctx->bifcxerr) { /* unlock the capture object before signalling out */ mcmunlck(ctx->bifcxrun->runcxmem, capture_obj); } ERRENDCLN(ctx->bifcxerr); /* unlock the capture object */ mcmunlck(ctx->bifcxrun->runcxmem, capture_obj); } } /* clear the capture buffer if it wasn't on when we started */ if (capture_start == 0) tioclrcapture(ctx->bifcxtio); } /* push the result code */ runpnum(ctx->bifcxrun, err); } /* * parserGetObj - get one of the objects associated with the command */ void bifgetobj(bifcxdef *ctx, int argc) { int id; objnum obj; voccxdef *voc = ctx->bifcxrun->runcxvoc; /* check the argument count */ bifcntargs(ctx, 1, argc); /* get the argument */ id = (int)runpopnum(ctx->bifcxrun); /* get the appropriate object */ switch(id) { case 1: /* get the current actor */ obj = voc->voccxactor; /* if there's no current actor, use the current 'me' by default */ if (obj == MCMONINV) obj = voc->voccxme; /* done */ break; case 2: /* verb */ obj = voc->voccxverb; break; case 3: /* direct object */ obj = (voc->voccxdobj == 0 ? MCMONINV : voc->voccxdobj->vocolobj); break; case 4: /* preposition */ obj = voc->voccxprep; break; case 5: /* indirect object */ obj = (voc->voccxiobj == 0 ? MCMONINV : voc->voccxiobj->vocolobj); break; case 6: /* "it" */ obj = voc->voccxit; break; case 7: /* "him" */ obj = voc->voccxhim; break; case 8: /* "her" */ obj = voc->voccxher; break; case 9: /* them */ voc_push_objlist(voc, voc->voccxthm, voc->voccxthc); /* * return directly, since we've already pushed the result (it's * a list, not an object) */ return; default: /* invalid argument */ runsig1(ctx->bifcxrun, ERR_INVVBIF, ERRTSTR, "parserGetObj"); break; } /* return the object */ runpobj(ctx->bifcxrun, obj); } /* * parseNounList - parse a noun list. Call like this: * * parserParseNounList(wordlist, typelist, starting_index, complain, * multi, check_actor); */ void bifparsenl(bifcxdef *ctx, int argc) { /* check the argument count */ bifcntargs(ctx, 6, argc); /* call the parser */ voc_parse_np(ctx->bifcxrun->runcxvoc); } /* * parserTokenize - given a string, produce a list of tokens. Returns * nil on error, or a list of token strings. * * parserTokenize(commandString); */ void bifprstok(bifcxdef *ctx, int argc) { /* check arguments */ bifcntargs(ctx, 1, argc); /* call the parser */ voc_parse_tok(ctx->bifcxrun->runcxvoc); } /* * parserGetTokTypes - given a list of tokens (represented as strings), * get a corresponding list of token types. * * parserGetTokTypes(tokenList); */ void bifprstoktyp(bifcxdef *ctx, int argc) { /* check arguments */ bifcntargs(ctx, 1, argc); /* call the parser */ voc_parse_types(ctx->bifcxrun->runcxvoc); } /* * parserDictLookup - given a list of tokens and their types, produce a * list of all of the objects that match all of the words. * * parserDictLookup(tokenList, typeList); */ void bifprsdict(bifcxdef *ctx, int argc) { /* check arguments */ bifcntargs(ctx, 2, argc); /* call the parser */ voc_parse_dict_lookup(ctx->bifcxrun->runcxvoc); } /* * parserResolveObjects - resolve an object list of the sort returned by * parseNounList. Validates and disambiguates the objects. * * parserResolveObjects(actor, verb, prep, otherobj, usageType, * verprop, tokenList, objList, silent) */ void bifprsrslv(bifcxdef *ctx, int argc) { /* check arguments */ bifcntargs(ctx, 9, argc); /* call the parser */ voc_parse_disambig(ctx->bifcxrun->runcxvoc); } /* * parserReplaceCommand - replace the current command line with a new * string. Aborts the current command. */ void bifprsrplcmd(bifcxdef *ctx, int argc) { /* check arguments */ bifcntargs(ctx, 1, argc); /* call the parser */ voc_parse_replace_cmd(ctx->bifcxrun->runcxvoc); } /* * exitobj - throw a RUNEXITOBJ error */ void bifexitobj(bifcxdef *ctx, int argc) { /* no arguments are allowed */ bifcntargs(ctx, 0, argc); /* throw the RUNEXITOBJ error */ errsig(ctx->bifcxerr, ERR_RUNEXITOBJ); } /* * Standard system button labels for bifinpdlg() */ #define BIFINPDLG_LBL_OK 1 #define BIFINPDLG_LBL_CANCEL 2 #define BIFINPDLG_LBL_YES 3 #define BIFINPDLG_LBL_NO 4 /* * inputdialog */ void bifinpdlg(bifcxdef *ctx, int argc) { uchar *p; char prompt[256]; char lblbuf[256]; char *labels[10]; char *dst; char *xp; uint len; size_t bcnt; int default_resp, cancel_resp; int resp; int std_btns; int icon_id; /* check for proper arguments */ bifcntargs(ctx, 5, argc); /* get the icon number */ icon_id = runpopnum(ctx->bifcxrun); /* get the prompt string */ p = runpopstr(ctx->bifcxrun); bifcstr(ctx, prompt, (size_t)sizeof(prompt), p); /* translate from internal to local characters */ for (xp = prompt ; *xp != '\0' ; xp++) *xp = (char)cmap_i2n(*xp); /* check for a standard button set selection */ if (runtostyp(ctx->bifcxrun) == DAT_NUMBER) { /* get the standard button set ID */ std_btns = runpopnum(ctx->bifcxrun); /* there are no actual buttons */ bcnt = 0; } else { /* we're not using standard buttons */ std_btns = 0; /* get the response string list */ p = runpoplst(ctx->bifcxrun); len = osrp2(p); p += 2; /* build our internal button list */ for (bcnt = 0, dst = lblbuf ; len != 0 ; lstadv(&p, &len)) { /* see what we have */ if (*p == DAT_SSTRING) { /* it's a label string - convert to a C string */ bifcstr(ctx, dst, sizeof(lblbuf) - (dst - lblbuf), p + 1); /* translate from internal to local characters */ for (xp = dst ; *xp != '\0' ; xp++) *xp = (char)cmap_i2n(*xp); /* set this button to point to the converted text */ labels[bcnt++] = dst; /* move past this label in the button buffer */ dst += strlen(dst) + 1; } else if (*p == DAT_NUMBER) { int id; int resid; /* it's a standard system label ID - get the ID */ id = (int)osrp4s(p + 1); /* translate it to the appropriate string resource */ switch(id) { case BIFINPDLG_LBL_OK: resid = RESID_BTN_OK; break; case BIFINPDLG_LBL_CANCEL: resid = RESID_BTN_CANCEL; break; case BIFINPDLG_LBL_YES: resid = RESID_BTN_YES; break; case BIFINPDLG_LBL_NO: resid = RESID_BTN_NO; break; default: resid = 0; break; } /* * if we got a valid resource ID, load the resource; * otherwise, skip this button */ if (resid != 0 && !os_get_str_rsc(resid, dst, sizeof(lblbuf) - (dst - lblbuf))) { /* set this button to point to the converted text */ labels[bcnt++] = dst; /* move past this label in the button buffer */ dst += strlen(dst) + 1; } } /* if we have exhausted our label array, stop now */ if (bcnt >= sizeof(labels)/sizeof(labels[0]) || dst >= lblbuf + sizeof(lblbuf)) break; } } /* get the default response */ if (runtostyp(ctx->bifcxrun) == DAT_NIL) { rundisc(ctx->bifcxrun); default_resp = 0; } else default_resp = runpopnum(ctx->bifcxrun); /* get the cancel response */ if (runtostyp(ctx->bifcxrun) == DAT_NIL) { rundisc(ctx->bifcxrun); cancel_resp = 0; } else cancel_resp = runpopnum(ctx->bifcxrun); /* flush output before showing the dialog */ tioflushn(ctx->bifcxtio, 0); /* show the dialog */ resp = tio_input_dialog(icon_id, prompt, std_btns, (const char **)labels, bcnt, default_resp, cancel_resp); /* return the result */ runpnum(ctx->bifcxrun, resp); } /* * Determine if a resource exists */ void bifresexists(bifcxdef *ctx, int argc) { uchar *p; char resname[OSFNMAX]; appctxdef *appctx; int found; runsdef val; /* check for proper arguments */ bifcntargs(ctx, 1, argc); /* get the resource name string */ p = runpopstr(ctx->bifcxrun); bifcstr(ctx, resname, (size_t)sizeof(resname), p); /* * if we have a host application context, and it provides a resource * finder function, ask the resource finder if the resource is * available; otherwise, report that the resource is not loadable, * since we must not be running a version of the interpreter that * supports external resource loading */ appctx = ctx->bifcxappctx; found = (appctx != 0 && appctx->resfile_exists != 0 && (*appctx->resfile_exists)(appctx->resfile_exists_ctx, resname, strlen(resname))); /* push the result */ runpush(ctx->bifcxrun, runclog(found), &val); } qtads-2.1.7/tads2/bif.h000066400000000000000000000254111265017072300145710ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/BIF.H,v 1.3 1999/07/11 00:46:29 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name bif.h - built-in functions interface Function interface to run-time intrinsic function implementation Notes None Modified 12/16/92 MJRoberts - add TADS/Graphic functions 12/26/91 MJRoberts - creation */ #ifndef BIF_INCLUDED #define BIF_INCLUDED #ifndef OS_INCLUDED # include "os.h" #endif #ifndef ERR_INCLUDED # include "err.h" #endif #ifndef RUN_INCLUDED # include "run.h" #endif #ifndef TIO_INCLUDED # include "tio.h" #endif #ifndef REGEX_H #include "regex.h" #endif #ifdef __cplusplus extern "C" { #endif /* forward definitions */ struct vocidef; struct voccxdef; /* maximum number of file handles available */ #define BIFFILMAX 10 /* file contexts for the built-in file handling functions */ struct biffildef { osfildef *fp; /* underyling system file handle */ uint flags; /* flags */ #define BIFFIL_F_BINARY 0x01 /* file is binary */ }; typedef struct biffildef biffildef; /* built-in execution context */ struct bifcxdef { errcxdef *bifcxerr; /* error-handling context */ runcxdef *bifcxrun; /* code execution context */ tiocxdef *bifcxtio; /* text I/O context */ long bifcxrnd; /* random number seed */ int bifcxseed1; /* first seed for new generator */ int bifcxseed2; /* second seed for new generator */ int bifcxseed3; /* third seed for new generator */ int bifcxrndset; /* randomize() has been called */ biffildef bifcxfile[BIFFILMAX]; /* file handles for fopen, etc */ int bifcxsafetyr; /* file I/O safety level - read */ int bifcxsafetyw; /* file I/O safety level - write */ char *bifcxsavext; /* saved game extension (null by default) */ struct appctxdef *bifcxappctx; /* host application context */ re_context bifcxregex; /* regular expression searching context */ }; typedef struct bifcxdef bifcxdef; /* * argument list checking routines - can be disabled for faster * run-time */ /* check for proper number of arguments */ /* void bifcntargs(bifcxdef *ctx, int argcnt) */ /* check that next argument has proper type */ /* void bifchkarg(bifcxdef *ctx, dattyp typ); */ #ifdef RUNFAST # define bifcntargs(ctx, parmcnt, argcnt) # define bifchkarg(ctx, typ) #else /* RUNFAST */ # define bifcntargs(ctx, parmcnt, argcnt) \ (parmcnt == argcnt ? DISCARD 0 : \ (runsig(ctx->bifcxrun, ERR_BIFARGC), DISCARD 0)) # define bifchkarg(ctx, typ) \ (runtostyp(ctx->bifcxrun) == typ ? DISCARD 0 : \ (runsig(ctx->bifcxrun, ERR_INVTBIF), DISCARD 0)) #endif /* RUNFAST */ /* determine if one object is a subclass of another */ int bifinh(struct voccxdef *voc, struct vocidef *v, objnum cls); /* enumerate the built-in functions */ void bifyon(bifcxdef *ctx, int argc); /* yorn - yes or no */ void bifsfs(bifcxdef *ctx, int argc); /* setfuse */ void bifrfs(bifcxdef *ctx, int argc); /* remfuse */ void bifsdm(bifcxdef *ctx, int argc); /* setdaemon */ void bifrdm(bifcxdef *ctx, int argc); /* remdaemon */ void bifinc(bifcxdef *ctx, int argc); /* incturn */ void bifqui(bifcxdef *ctx, int argc); /* quit */ void bifsav(bifcxdef *ctx, int argc); /* save */ void bifrso(bifcxdef *ctx, int argc); /* restore */ void biflog(bifcxdef *ctx, int argc); /* logging */ void bifres(bifcxdef *ctx, int argc); /* restart */ void bifinp(bifcxdef *ctx, int argc); /* input - get line from kb */ void bifnfy(bifcxdef *ctx, int argc); /* notify */ void bifunn(bifcxdef *ctx, int argc); /* unnotify */ void biftrc(bifcxdef *ctx, int argc); /* trace on/off */ void bifsay(bifcxdef *ctx, int argc); /* say */ void bifcar(bifcxdef *ctx, int argc); /* car */ void bifcdr(bifcxdef *ctx, int argc); /* cdr */ void bifcap(bifcxdef *ctx, int argc); /* caps */ void biflen(bifcxdef *ctx, int argc); /* length */ void biffnd(bifcxdef *ctx, int argc); /* find */ void bifsit(bifcxdef *ctx, int argc); /* setit - set current 'it' */ void bifsrn(bifcxdef *ctx, int argc); /* randomize: seed rand */ void bifrnd(bifcxdef *ctx, int argc); /* rand - get a random number */ void bifask(bifcxdef *ctx, int argc); /* askfile */ void bifssc(bifcxdef *ctx, int argc); /* setscore */ void bifsub(bifcxdef *ctx, int argc); /* substr */ void bifcvs(bifcxdef *ctx, int argc); /* cvtstr: convert to string */ void bifcvn(bifcxdef *ctx, int argc); /* cvtnum: convert to number */ void bifupr(bifcxdef *ctx, int argc); /* upper */ void biflwr(bifcxdef *ctx, int argc); /* lower */ void biffob(bifcxdef *ctx, int argc); /* firstobj */ void bifnob(bifcxdef *ctx, int argc); /* nextobj */ void bifsvn(bifcxdef *ctx, int argc); /* setversion */ void bifarg(bifcxdef *ctx, int argc); /* getarg */ void biftyp(bifcxdef *ctx, int argc); /* datatype */ void bifisc(bifcxdef *ctx, int argc); /* isclass */ void bifund(bifcxdef *ctx, int argc); /* undo */ void bifdef(bifcxdef *ctx, int argc); /* defined */ void bifpty(bifcxdef *ctx, int argc); /* proptype */ void bifoph(bifcxdef *ctx, int argc); /* outhide */ void bifgfu(bifcxdef *ctx, int argc); /* getfuse */ void bifruf(bifcxdef *ctx, int argc); /* runfuses */ void bifrud(bifcxdef *ctx, int argc); /* rundaemons */ void biftim(bifcxdef *ctx, int argc); /* gettime */ void bifsct(bifcxdef *ctx, int argc); /* intersect */ void bifink(bifcxdef *ctx, int argc); /* inputkey */ void bifwrd(bifcxdef *ctx, int argc); /* objwords */ void bifadw(bifcxdef *ctx, int argc); /* addword */ void bifdlw(bifcxdef *ctx, int argc); /* delword */ void bifgtw(bifcxdef *ctx, int argc); /* getwords */ void bifnoc(bifcxdef *ctx, int argc); /* nocaps */ void bifskt(bifcxdef *ctx, int argc); /* skipturn */ void bifcls(bifcxdef *ctx, int argc); /* clearscreen */ void bif1sc(bifcxdef *ctx, int argc); /* firstsc */ void bifvin(bifcxdef *ctx, int argc); /* verbinfo */ void bifcapture(bifcxdef *ctx, int argc); /* outcapture */ void biffopen(bifcxdef *ctx, int argc); /* fopen */ void biffclose(bifcxdef *ctx, int argc); /* fclose */ void biffwrite(bifcxdef *ctx, int argc); /* fwrite */ void biffread(bifcxdef *ctx, int argc); /* fread */ void biffseek(bifcxdef *ctx, int argc); /* fseek */ void biffseekeof(bifcxdef *ctx, int argc); /* fseekeof */ void bifftell(bifcxdef *ctx, int argc); /* ftell */ void bifsysinfo(bifcxdef *ctx, int argc); /* systemInfo */ void bifmore(bifcxdef *ctx, int argc); /* morePrompt */ void bifsetme(bifcxdef *ctx, int argc); /* parserSetMe */ void bifgetme(bifcxdef *ctx, int argc); /* parserGetMe */ void bifresearch(bifcxdef *ctx, int argc); /* reSearch */ void bifregroup(bifcxdef *ctx, int argc); /* reGetGroup */ void bifinpevt(bifcxdef *ctx, int argc); /* inputevent */ void bifdelay(bifcxdef *ctx, int argc); /* timeDelay */ void bifsetoutfilter(bifcxdef *ctx, int argc); /* setOutputFilter */ void bifexec(bifcxdef *ctx, int argc); /* execCommand */ void bifgetobj(bifcxdef *ctx, int argc); /* parserGetObj */ void bifparsenl(bifcxdef *ctx, int argc); /* parserParseNounList */ void bifprstok(bifcxdef *ctx, int argc); /* parserTokenize */ void bifprstoktyp(bifcxdef *ctx, int argc); /* parserGetTokTypes */ void bifprsdict(bifcxdef *ctx, int argc); /* parserDictLookup */ void bifprsrslv(bifcxdef *ctx, int argc); /* parserResolveObjects */ void bifprsrplcmd(bifcxdef *ctx, int argc); /* parserReplaceCommand */ void bifexitobj(bifcxdef *ctx, int argc); /* exitobj */ void bifinpdlg(bifcxdef *ctx, int argc); /* inputdialog */ void bifresexists(bifcxdef *ctx, int argc); /* resourceExists */ /* * TADS/graphic functions - these are present in the text system, but * don't do anything. */ void bifgrp(bifcxdef *ctx, int argc); /* g_readpic: read picture */ void bifgsp(bifcxdef *ctx, int argc); /* g_showpic: show picture */ void bifgsh(bifcxdef *ctx, int argc); /* g_sethot: set hot list */ void bifgin(bifcxdef *ctx, int argc); /* g_inventory */ void bifgco(bifcxdef *ctx, int argc); /* g_compass */ void bifgov(bifcxdef *ctx, int argc); /* g_overlay */ void bifgmd(bifcxdef *ctx, int argc); /* g_mode */ void bifgmu(bifcxdef *ctx, int argc); /* g_music */ void bifgpa(bifcxdef *ctx, int argc); /* g_pause */ void bifgef(bifcxdef *ctx, int argc); /* g_effect */ void bifgsn(bifcxdef *ctx, int argc); /* g_sound */ #ifdef __cplusplus } #endif #endif /* BIF_INCLUDED */ qtads-2.1.7/tads2/bifgdum.c000066400000000000000000000015731265017072300154440ustar00rootroot00000000000000/* * Copyright (c) 1993, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name bifgdum.c - stubs for built-in TADS/Graphic functions Function provides dummy entrypoints for built-in functions specific to TADS/Graphic Notes None Modified 01/11/93 MJRoberts - creation */ #include "os.h" #include "bif.h" void bifgrp(bifcxdef *ctx, int argc) {} void bifgsp(bifcxdef *ctx, int argc) {} void bifgsh(bifcxdef *ctx, int argc) {} void bifgin(bifcxdef *ctx, int argc) {} void bifgco(bifcxdef *ctx, int argc) {} void bifgov(bifcxdef *ctx, int argc) {} void bifgmd(bifcxdef *ctx, int argc) {} void bifgmu(bifcxdef *ctx, int argc) {} void bifgpa(bifcxdef *ctx, int argc) {} void bifgef(bifcxdef *ctx, int argc) {} void bifgsn(bifcxdef *ctx, int argc) {} qtads-2.1.7/tads2/cmap.c000066400000000000000000000221741265017072300147470ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/cmap.c,v 1.2 1999/05/17 02:52:14 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name cmap.c - character mapping implementation Function Notes Modified 05/31/98 MJRoberts - Creation */ #include #include "os.h" #include "cmap.h" #include "err.h" #include "tio.h" /* ------------------------------------------------------------------------ */ /* * Global variables for character mapping tables */ unsigned char G_cmap_input[256]; unsigned char G_cmap_output[256]; char G_cmap_id[5]; char G_cmap_ldesc[CMAP_LDESC_MAX_LEN + 1]; /* * static variables */ /* * flag: true -> a character set has been explicitly loaded, so we * should ignore any game character set setting */ static int S_cmap_loaded; /* ------------------------------------------------------------------------ */ /* * Initialize the default character mappings */ void cmap_init_default(void) { size_t i; /* initialize the input table */ for (i = 0 ; i < sizeof(G_cmap_input)/sizeof(G_cmap_input[0]) ; ++i) G_cmap_input[i] = (unsigned char)i; /* initialize the output table */ for (i = 0 ; i < sizeof(G_cmap_output)/sizeof(G_cmap_output[0]) ; ++i) G_cmap_output[i] = (unsigned char)i; /* we have a null ID */ memset(G_cmap_id, 0, sizeof(G_cmap_id)); /* indicate that it's the default */ strcpy(G_cmap_ldesc, "(native/no mapping)"); /* note that we have no character set loaded */ S_cmap_loaded = FALSE; } /* ------------------------------------------------------------------------ */ /* * Internal routine to load a character map from a file */ static int cmap_load_internal(char *filename) { osfildef *fp; static char sig1[] = CMAP_SIG_S100; char buf[256]; uchar lenbuf[2]; size_t len; int sysblk; /* if there's no mapping file, use the default mapping */ if (filename == 0) { /* initialize with the default mapping */ cmap_init_default(); /* return success */ return 0; } /* open the file */ fp = osfoprb(filename, OSFTCMAP); if (fp == 0) return 1; /* check the signature */ if (osfrb(fp, buf, sizeof(sig1)) || memcmp(buf, sig1, sizeof(sig1)) != 0) { osfcls(fp); return 2; } /* load the ID */ G_cmap_id[4] = '\0'; if (osfrb(fp, G_cmap_id, 4)) { osfcls(fp); return 3; } /* load the long description */ if (osfrb(fp, lenbuf, 2) || (len = osrp2(lenbuf)) > sizeof(G_cmap_ldesc) || osfrb(fp, G_cmap_ldesc, len)) { osfcls(fp); return 4; } /* load the two tables - input, then output */ if (osfrb(fp, G_cmap_input, sizeof(G_cmap_input)) || osfrb(fp, G_cmap_output, sizeof(G_cmap_output))) { osfcls(fp); return 5; } /* read the next section header */ if (osfrb(fp, buf, 4)) { osfcls(fp); return 6; } /* if it's "SYSI", read the system information string */ if (!memcmp(buf, "SYSI", 4)) { /* read the length prefix, then the string */ if (osfrb(fp, lenbuf, 2) || (len = osrp2(lenbuf)) > sizeof(buf) || osfrb(fp, buf, len)) { osfcls(fp); return 7; } /* we have a system information block */ sysblk = TRUE; } else { /* there's no system information block */ sysblk = FALSE; } /* * call the OS code, so that it can do any system-dependent * initialization for the new character mapping */ os_advise_load_charmap(G_cmap_id, G_cmap_ldesc, sysblk ? buf : ""); /* read the next section header */ if (sysblk && osfrb(fp, buf, 4)) { osfcls(fp); return 8; } /* see if we have an entity list */ if (!memcmp(buf, "ENTY", 4)) { /* read the entities */ for (;;) { size_t len; unsigned int cval; char expansion[CMAP_MAX_ENTITY_EXPANSION]; /* read the next item's length and character value */ if (osfrb(fp, buf, 4)) { osfcls(fp); return 9; } /* decode the values */ len = osrp2(buf); cval = osrp2(buf+2); /* if we've reached the zero marker, we're done */ if (len == 0 && cval == 0) break; /* read the string */ if (len > CMAP_MAX_ENTITY_EXPANSION || osfrb(fp, expansion, len)) { osfcls(fp); return 10; } /* tell the output code about the expansion */ tio_set_html_expansion(cval, expansion, len); } } /* * ignore anything else we find - if the file format is updated to * include extra information in the future, and this old code tries * to load an updated file, we'll just ignore the new information, * which should always be placed after the "SYSI" block (if present) * to ensure compatibility with past versions (such as this code) */ /* no problems - close the file and return success */ osfcls(fp); return 0; } /* ------------------------------------------------------------------------ */ /* * Explicitly load a character set from a file. This character set * mapping will override any implicit character set mapping that we read * from a game file. This should be called when the player explicitly * loads a character set (via a command line option or similar action). */ int cmap_load(char *filename) { int err; /* try loading the file */ if ((err = cmap_load_internal(filename)) != 0) return err; /* * note that we've explicitly loaded a character set, if they named * a character set (if not, this simply establishes the default * setting, so we haven't explicitly loaded anything) */ if (filename != 0) S_cmap_loaded = TRUE; /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Explicitly override any game character set and use no character set * instead. */ void cmap_override(void) { /* apply the default mapping */ cmap_init_default(); /* * pretend we have a character map loaded, so that we don't try to * load another one if the game specifies a character set */ S_cmap_loaded = TRUE; } /* ------------------------------------------------------------------------ */ /* * Set the game's internal character set. This is called when a game is * loaded, and the game specifies a character set. */ void cmap_set_game_charset(errcxdef *ec, char *internal_id, char *internal_ldesc, char *argv0) { char filename[OSFNMAX]; /* * If a character set is already explicitly loaded, ignore the * game's character set - the player asked us to use a particular * mapping, so ignore what the game wants. (This will probably * result in incorrect display of non-ASCII character values, but * the player is most likely to use this to avoid errors when an * appropriate mapping file for the game is not available. In this * case, the player informs us by setting the option that he or she * knows and accepts that the game will not look exactly right.) */ if (S_cmap_loaded) return; /* * ask the operating system to name the mapping file -- this routine * will determine, if possible, the current native character set, * and apply a system-specific naming convention to tell us what * mapping file we should open */ os_gen_charmap_filename(filename, internal_id, argv0); /* try loading the mapping file */ if (cmap_load_internal(filename)) errsig2(ec, ERR_CHRNOFILE, ERRTSTR, errstr(ec, filename, strlen(filename)), ERRTSTR, errstr(ec, internal_ldesc, strlen(internal_ldesc))); /* * We were successful - the game's internal character set is now * mapped to the current native character set. Even though we * loaded an ldesc from the mapping file, forget that and store the * internal ldesc that the game specified. The reason we do this is * that it's possible that the player will dynamically switch native * character sets in the future, at which point we'll need to * re-load the mapping table, which could raise an error if a * mapping file for the new character set isn't available. So, we * may need to provide the same explanation later that we needed to * provide here. Save the game's character set ldesc for that * eventuality, since it describes exactly what the *game* wanted. */ strcpy(G_cmap_ldesc, internal_ldesc); } qtads-2.1.7/tads2/cmap.h000066400000000000000000000067461265017072300147630ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/cmap.h,v 1.2 1999/05/17 02:52:14 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name cmap.h - character mapping definitions Function Notes Modified 05/31/98 MJRoberts - Creation */ #ifndef CMAP_H #define CMAP_H struct errcxdef; /* ------------------------------------------------------------------------ */ /* * Initialize the default character mappings. If no mapping file is to * be read, this function will establish identify mappings that leave * characters untranslated. */ void cmap_init_default(void); /* * Load a character map file. Returns zero on success, non-zero on * failure. If filename is null, we'll use the default mapping. */ int cmap_load(char *filename); /* * Turn off character translation. This overrides any game character * set that we find and simply uses the default translation. */ void cmap_override(void); /* * Set the game's internal character set. This should be called when a * game is loaded, and the game specifies an internal character set. If * there is no character map file explicitly loaded, we will attempt to * load a character mapping file that maps this character set to the * current native character set. Signals an error on failure. This * routine will succeed (without doing anything) if a character set has * already been explicitly loaded, since an explicitly-loaded character * set overrides the automatic character set selection that we attempt * when loading a game. * * argv0 must be provided so that we know where to look for our mapping * file on systems where mapping files are stored in the same directory * as the TADS executables. */ void cmap_set_game_charset(struct errcxdef *errctx, char *internal_id, char *internal_ldesc, char *argv0); /* ------------------------------------------------------------------------ */ /* * Mapping macros */ /* map a native character (read externally) into an internal character */ #define cmap_n2i(c) (G_cmap_input[(unsigned char)(c)]) /* map an internal character into a native character (for display) */ #define cmap_i2n(c) (G_cmap_output[(unsigned char)(c)]) /* ------------------------------------------------------------------------ */ /* * Global character mapping tables. The character map is established at * start-up. */ /* * input-mapping table - for native character 'n', cmap_input[n] yields * the internal character code */ extern unsigned char G_cmap_input[256]; /* * output-mapping table - for internal character 'n', cmap_output[n] * yields the output character code */ extern unsigned char G_cmap_output[256]; /* the ID of the loaded character set */ extern char G_cmap_id[5]; /* the full name (for display purposes) of the loaded character set */ #define CMAP_LDESC_MAX_LEN 40 extern char G_cmap_ldesc[CMAP_LDESC_MAX_LEN + 1]; /* * Maximum expansion for an HTML entity mapping */ #define CMAP_MAX_ENTITY_EXPANSION 50 /* ------------------------------------------------------------------------ */ /* * Signatures for character map files. The signature is stored at the * beginning of the file. */ /* single-byte character map version 1.0.0 */ #define CMAP_SIG_S100 "TADS2 charmap S100\n\r\01a" #endif /* CMAP_H */ qtads-2.1.7/tads2/cmd.c000066400000000000000000000034741265017072300145740ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/CMD.C,v 1.2 1999/05/17 02:52:11 MJRoberts Exp $"; #endif /* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name cmd.c - command line option reader Function service functions for interpreting command line options Notes none Modified 04/04/92 MJRoberts - creation */ #include "os.h" #include "std.h" #include "err.h" #include "cmd.h" /* get a toggle argument */ int cmdtog(errcxdef *ec, int prv, char *argp, int ofs, void (*usagefn)(errcxdef *)) { switch(argp[ofs + 1]) { case '+': return(TRUE); case '-': return(FALSE); case '\0': return(!prv); default: /* invalid - display usage if we have a callback for it */ if (usagefn != 0) (*usagefn)(ec); NOTREACHEDV(int); return 0; } } /* get an argument to a switch */ char *cmdarg(errcxdef *ec, char ***argpp, int *ip, int argc, int ofs, void (*usagefn)(errcxdef *)) { char *ret; /* * check to see if the argument is appended directly to the option; * if not, look at the next string */ ret = (**argpp) + ofs + 1; if (*ret == '\0') { /* * it's not part of this string - get the argument from the next * string in the vector */ ++(*ip); ++(*argpp); ret = (*ip >= argc ? 0 : **argpp); } /* * if we didn't find the argument, it's an error - display usage if * we have a valid usage callback */ if ((ret == 0 || *ret == 0) && usagefn != 0) (*usagefn)(ec); return ret; } qtads-2.1.7/tads2/cmd.h000066400000000000000000000041201265017072300145660ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/CMD.H,v 1.2 1999/05/17 02:52:11 MJRoberts Exp $ */ /* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name cmd.h - interface to command line option service routines Function defines command line option service routine interfaces Notes none Modified 04/04/92 MJRoberts - creation */ #ifndef CMD_INCLUDED #define CMD_INCLUDED /* * Get argument to an option. Option can be rammed up against option * letter(s) with no space, or can be separated by a space. argp is a * pointer to the pointer to the current position in the argv[] array; * ip is a pointer to the index in the argv[] array. Both *argpp and * *ip are incremented if the next word must be read. argc is the total * number of arguments. ofs gives the number of characters (NOT * including the '-') in this option flag; most options will have ofs==1 * since they are of the form '-x'. usagefn is a function to call if * the parsing fails; it is not expected to return, but should signal an * error instead. */ char *cmdarg(struct errcxdef *ec, char ***argpp, int *ip, int argc, int ofs, void (*usagefn)(struct errcxdef*)); /* * Read a toggle argument. prv is the previous value (prior to this * switch) of the parameter (TRUE or FALSE). argp is a pointer to the * current argument word. ofs is the length of this option flag, NOT * including the '-'; most options have ofs==1 since they are of the * form '-x'. If the option is followed by '+', the value returned is * TRUE; if it's followed by '-', the value is FALSE; if followed by * nothing, the option is the logical inverse of the previous value. If * it's followed by any other character, we call the usage callback, * which is not expected to return, but should signal an error. */ int cmdtog(struct errcxdef *ec, int prv, char *argp, int ofs, void (*usagefn)(struct errcxdef*)); #endif /* CMD_INCLUDED */ qtads-2.1.7/tads2/dat.c000066400000000000000000000027401265017072300145740ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/DAT.C,v 1.2 1999/05/17 02:52:11 MJRoberts Exp $"; #endif /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name dat.c - datatype manipulation routines Function Functions to operate on TADS run-time datatypes Notes Datatypes are portable, hence the hard-coded values for data sizes. Modified 08/13/91 MJRoberts - creation */ #include "os.h" #include "std.h" #include "dat.h" #include "lst.h" #include "prp.h" #include "obj.h" #include "voc.h" /* return size of a data value */ uint datsiz(dattyp typ, void *val) { switch(typ) { case DAT_NUMBER: return(4); /* numbers are in 4-byte lsb-first format */ case DAT_OBJECT: return(2); /* object numbers are in 2-byte lsb-first format */ case DAT_SSTRING: case DAT_DSTRING: case DAT_LIST: return(osrp2((char *)val)); case DAT_NIL: case DAT_TRUE: return(0); case DAT_PROPNUM: case DAT_SYN: case DAT_FNADDR: case DAT_REDIR: return(2); case DAT_TPL: /* template is counted array of 10-byte entries, plus length byte */ return(1 + ((*(uchar *)val) * VOCTPLSIZ)); case DAT_TPL2: return(1 + ((*(uchar *)val) * VOCTPL2SIZ)); default: return(0); } } qtads-2.1.7/tads2/dat.h000066400000000000000000000030431265017072300145760ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/DAT.H,v 1.2 1999/05/17 02:52:11 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name dat.h - datatype definitions Function Defines datatypes for TADS Notes None Modified 08/11/91 MJRoberts - creation */ #ifndef DAT_INCLUDED #define DAT_INCLUDED #ifndef STD_INCLUDED #include "std.h" #endif /* a type of a piece of data */ typedef int dattyp; /* datatype numbers */ #define DAT_NUMBER 1 #define DAT_OBJECT 2 #define DAT_SSTRING 3 #define DAT_BASEPTR 4 #define DAT_NIL 5 /* nil, as in FALSE or empty list */ #define DAT_CODE 6 #define DAT_LIST 7 #define DAT_TRUE 8 /* inverse of nil */ #define DAT_DSTRING 9 #define DAT_FNADDR 10 /* a function address */ #define DAT_TPL 11 /* template list pointer */ #define DAT_PROPNUM 13 /* a property number */ #define DAT_DEMAND 14 /* special flag: use callback to set on use */ #define DAT_SYN 15 /* synonym to indicated property value */ #define DAT_REDIR 16 /* redirection to different object */ #define DAT_TPL2 17 /* new-style template */ /* determine the size of a piece of data */ uint datsiz(dattyp typ, void *valptr); #endif /* DAT_INCLUDED */ qtads-2.1.7/tads2/dbg.h000066400000000000000000000605761265017072300146000ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/DBG.H,v 1.3 1999/07/11 00:46:29 MJRoberts Exp $ */ /* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name dbg.h - debug interface Function Debugger interface definitions Notes The Debugger implementation is split into two parts: the "engine", which implements the parts of the debugger that are independent of user interface; and the UI. The UI is in a separate module so that several user interfaces can be provided, and a particular one chosen at link time. The UI section should contain nothing that is generic, but only the parts specific to that user interface. Modified 04/11/99 CNebel - Move extern C. 03/28/92 MJRoberts - creation */ #ifndef DBG_INCLUDED #define DBG_INCLUDED #ifndef OBJ_INCLUDED #include "obj.h" #endif #ifndef PRP_INCLUDED #include "prp.h" #endif #ifdef __cplusplus extern "C" { #endif /* forward declarations */ struct bifcxdef; struct toksdef; struct toktdef; struct tokcxdef; /* stack frame record */ struct dbgfdef { struct runsdef *dbgfbp; /* base pointer of frame */ objnum dbgfself; /* 'self' object (MCMONINV for functions) */ objnum dbgftarg; /* actual target object */ prpnum dbgfprop; /* property being evalutated */ int dbgfargc; /* number of arguments */ int dbgfbif; /* set to built-in function number if in built-in */ uint dbgffr; /* offset in object of local frame symbol table */ uint dbgflin; /* OPCLINE operand of latest line */ }; typedef struct dbgfdef dbgfdef; /* max number of frames to store in debug frame memory */ #define DBGMAXFRAME 100 /* maximum number of breakpoints set concurrently */ #define DBGBPMAX 50 /* breakpoint structure */ struct dbgbpdef { objnum dbgbpself; /* the "self" object for the breakpoint */ objnum dbgbptarg; /* actual target object for the breakpoint */ uint dbgbpofs; /* offset in object of the breakpoint */ uint dbgbpflg; /* breakpoint flags */ # define DBGBPFUSED 0x01 /* breakpoint has been set */ # define DBGBPFNAME 0x02 /* name of address has been stored */ # define DBGBPFCOND 0x04 /* breakpoint has a condition attached */ # define DBGBPFDISA 0x08 /* breakpoint is disabled */ # define DBGBPFCONDNAME 0x10 /* condition name string has been stored */ uint dbgbpnam; /* offset of address name within dbgcxnam buffer */ uint dbgbpcondnam; /* offset of condition string within buffer */ objnum dbgbpcond; /* object containing compiled condition for bp */ }; typedef struct dbgbpdef dbgbpdef; /* maximum number of watch expressions set concurrently */ #define DBGWXMAX 30 /* watch expression structure */ struct dbgwxdef { objnum dbgwxobj; /* object containing compiled expression */ objnum dbgwxself; /* 'self' for the expression */ uint dbgwxnam; /* offset of expression text within dbgcxnam buffer */ uint dbgwxflg; /* flags for this watch expression slot */ # define DBGWXFUSED 0x01 /* watch slot is in use */ # define DBGWXFNAME 0x02 /* name of watch has been stored */ }; typedef struct dbgwxdef dbgwxdef; /* amount of space for bp names (original address strings from user) */ #define DBGCXNAMSIZ 2048 /* debug context */ struct dbgcxdef { struct tiocxdef *dbgcxtio; /* text i/o context */ struct tokthdef *dbgcxtab; /* symbol table */ struct mcmcxdef *dbgcxmem; /* memory cache manager context */ struct errcxdef *dbgcxerr; /* error handling context */ struct lindef *dbgcxlin; /* chain of line sources */ int dbgcxfcn; /* number of frames in use */ int dbgcxdep; /* actual depth (if overflow frame buffer) */ int dbgcxfid; /* source file serial number */ dbgfdef dbgcxfrm[DBGMAXFRAME]; /* stack frames */ int dbgcxflg; /* flags for debug session */ # define DBGCXFSS 0x01 /* single-stepping source lines */ # define DBGCXFSO 0x02 /* stepping over a function/method call */ # define DBGCXFOK 0x04 /* debugger is linked in */ # define DBGCXFIND 0x08 /* in debugger - suppress stack trace on err */ # define DBGCXFGBP 0x10 /* global breakpoints in effect */ # define DBGCXFTRC 0x20 /* call tracing activated */ # define DBGCXFLIN2 0x40 /* new-style line records (line numbers) */ int dbgcxsof; /* frame depth at step-over time */ dbgbpdef dbgcxbp[DBGBPMAX]; /* breakpoints */ dbgwxdef dbgcxwx[DBGWXMAX]; /* watch expressions */ struct prscxdef *dbgcxprs; /* parsing context */ struct runcxdef *dbgcxrun; /* execution context */ uint dbgcxnamf; /* next free byte of dbgcxnam buffer */ uint dbgcxnams; /* size of dbgcxnam buffer */ char *dbgcxnam; /* space for bp address names */ char *dbgcxhstp; /* call history buffer */ uint dbgcxhstl; /* history buffer length */ uint dbgcxhstf; /* offset of next free byte of history */ /* * This member is for the use of the user interface code. If the * user interface implementation needs to store additional context, * it can allocate a structure of its own (it should probably do * this in dbguini()) and store a pointer to that structure here. * Since the user interface entrypoints always have the debugger * context passed as a parameter, the user interface code can * recover its extra context information by following this pointer * and casting it to its private structure type. The TADS code * won't do anything with this pointer except initialize it to null * when initializing the debugger context. */ void *dbgcxui; }; typedef struct dbgcxdef dbgcxdef; /* ======================================================================== */ /* * Compiler interface. These routines are called by the compiler to * inform the debug record generator about important events as * compilation proceeds. */ /* * Tell the current line source that we're compiling an executable * line, and tell it the object number and offset of the code within the * object. */ void dbgclin(struct tokcxdef *tokctx, objnum objn, uint ofs); /* size of information given to line source via lincmpinf method */ #define DBGLINFSIZ 4 /* ======================================================================== */ /* * Run-time interface. These routines are called by the run-time * system to apprise the debugger of important events during execution. */ /* * Determine if the debugger is present. Returns true if so, false if * not. This should return false for any stand-alone version of the * executable that isn't linked with the debugger. If this returns * true, dbgucmd() must not have a trivial implementation -- dbgucmd() * must at least let the user quit out of the game. * * This can be switched at either link time or compile time. If DBG_OFF * is defined, we'll force this to return false; otherwise, we'll let * the program define the appropriate implementation through the linker. */ #ifdef DBG_OFF #define dbgpresent() (FALSE) #else int dbgpresent(); #endif /* add a debug tracing record */ /* void dbgenter(dbgcxdef *ctx, runsdef *bp, objnum self, objnum target, prpnum prop, int binum, int argc); */ /* tell debugger where the current line's local frame table is located */ /* void dbgframe(dbgcxdef *ctx, uint ofsfr, ofslin); */ /* * Single-step interrupt: the run-time has reached a new source line. * ofs is the offset from the start of the object of the line record, * and p is the current execution pointer. *p can be changed upon * return, in which case the run-time will continue from the new * position; however, the new address must be within the same function * or method as it was originally. */ /* void dbgssi(dbgcxdef *ctx, uint ofs, int instr, int err, uchar *noreg *p); */ /* pop debug trace level */ /* void dbgleave(dbgcxdef *ctx, int exittype); */ #define DBGEXRET 0 /* return with no value */ #define DBGEXVAL 1 /* return with a value */ #define DBGEXPASS 2 /* use 'pass' to exit a function */ /* dump the stack into text output */ /* void dbgdump(dbgcxdef *ctx); */ /* reset debug stack (throw away entire contents) */ /* void dbgrst(dbgcxdef *ctx); */ /* activate debugger if possible; returns TRUE if no debugger is present */ int dbgstart(dbgcxdef *ctx); /* add a string to the history buffer */ void dbgaddhist(dbgcxdef *ctx, char *buf, int bufl); /* * Find a base pointer, given the object+offset of the frame. If the * frame is not active, this routine signals ERR_INACTFR; otherwise, the * bp value for the frame is returned. */ struct runsdef *dbgfrfind(dbgcxdef *ctx, objnum frobj, uint frofs); /* ======================================================================== */ /* * User Interface Support routines. These routines are called by the * user interface layer to get information from the debugger and perform * debugging operations. */ /* get a symbol name; returns length of name */ int dbgnam(dbgcxdef *ctx, char *outbuf, int typ, int val); /* * Get information about current line. It is assumed that the caller * knows the size of the line information . */ void dbglget(dbgcxdef *ctx, uchar *buf); /* * Get information about a line in an enclosing stack frame. Level 0 is * the current line, level 1 is the first enclosing frame, and so on. * Returns 0 on success, non-zero if the frame level is invalid. */ int dbglgetlvl(dbgcxdef *ctx, uchar *buf, int level); /* * Set a breakpoint by symbolic address: "function" or * "object.property". The string may contain whitespace characters * around each symbol; it must be null-terminated. If an error occurs, * the error number is returned. bpnum returns with the breakpoint * number if err == 0. If the condition string is given (and is not an * empty string), the condition is compiled in the scope of the * breakpoint and attached as the breakpoint condition. */ int dbgbpset(dbgcxdef *ctx, char *addr, int *bpnum); /* * Set a breakpoint at an object + offset location. If 'toggle' is * true, and there's already a breakpoint at the given location, we'll * clear the breakpoint; in this case, *did_set will return false to * indicate that an existing breakpoint was cleared rather than a new * breakpoint created. *did_set will return true if a new breakpoint * was set. */ int dbgbpat(dbgcxdef *ctx, objnum objn, objnum self, uint ofs, int *bpnum, char *bpname, int toggle, char *condition, int *did_set); /* * Set a breakpoint at an object + offset location, optionally with a * condition, using an existing breakpoint slot. If the slot is already * in use, we'll return an error. */ int dbgbpatid(dbgcxdef *ctx, int bpnum, objnum target, objnum self, uint ofs, char *bpname, int toggle, char *cond, int *did_set); /* * Determine if there's a breakpoint at a given code location. Fills in * *bpnum with the breakpoint identifier and returns true if a * breakpoint is found at the given location; returns false if there are * no breakpoints matching the description. */ int dbgisbp(dbgcxdef *ctx, objnum target, objnum self, uint ofs, int *bpnum); /* * Determine if the given breakpoint is enabled */ int dbgisbpena(dbgcxdef *ctx, int bpnum); /* * Delete a breakpoint by breakpoint number (as returned from * dbgbpset). Returns error number, or 0 for success. */ int dbgbpdel(dbgcxdef *ctx, int bpnum); /* disable or enable a breakpoint, by breakpoint number; returns error num */ int dbgbpdis(dbgcxdef *ctx, int bpnum, int disable); /* * Set a new condition for the given breakpoint. Replaces any existing * condition. If an error occurs, we'll leave the old condition as it * was and return a non-zero error code; on success, we'll update the * condition and return zero. */ int dbgbpsetcond(dbgcxdef *ctx, int bpnum, char *cond); /* list breakpoints, using user callback to do display */ void dbgbplist(dbgcxdef *ctx, void (*dispfn)(void *ctx, const char *str, int len), void *dispctx); /* enumerate breakpoints */ void dbgbpenum(dbgcxdef *ctx, void (*cbfunc)(void *cbctx, int bpnum, const char *desc, const char *cond, int disabled), void *cbctx); /* call callback with lindef data for each breakpoint currently set */ void dbgbpeach(dbgcxdef *ctx, void (*fn)(void *, int, uchar *, uint), void *fnctx); /* * Get information on a specific breakpoint. Returns zero on success, * non-zero on failure. */ int dbgbpgetinfo(dbgcxdef *ctx, int bpnum, char *descbuf, size_t descbuflen, char *condbuf, size_t condbuflen); /* * Evaluate an expression (a text string to be parsed) at a particular * stack context level; returns error number. Invokes the callback * function repeatedly to display the value string, and ends the display * with a newline. If showtype is true, we'll include a type name * prefix, otherwise we'll simply display the value. */ int dbgeval(dbgcxdef *ctx, char *expr, void (*dispfn)(void *dispctx, const char *str, int strl), void *dispctx, int level, int showtype); /* * Evaluate an expression, extended version. For aggregate values * (objects, lists), we'll invoke a callback function for each value * contained by the aggregate value, passing the callback the name and * relationship of the subitem. The relationship is simply the operator * that should be used to join the parent expression and the subitem * name to form the full subitem expression; for objects, it's ".", and * for lists it's null (because for lists the subitem names will include * brackets). 'speculative' is passed to dbgcompile; see the comments * there for information on the purpose of this flag. */ int dbgevalext(dbgcxdef *ctx, char *expr, void (*dispfn)(void *dispctx, const char *str, int strl), void *dispctx, int level, int showtype, dattyp *dat, void (*aggcb)(void *aggctx, const char *subname, int subnamelen, const char *relationship), void *aggctx, int speculative); /* * enumerate local variables at a given stack context level by calling * the given function once for each local variable */ void dbgenumlcl(dbgcxdef *ctx, int level, void (*func)(void *ctx, const char *lclnam, size_t lclnamlen), void *cbctx); /* * Compile an expression in a given frame context. Returns an error * number. Allocates a new object to contain the compiled code, and * returns the object number in *objn; the caller is responsible for * freeing the object when done with it. * * If 'speculative' is set to true, we'll prohibit the expression from * making any assignments or calling any methods or functions. This * mode can be used to try compiling an expression that the user could * conceivably be interested in but has not expressly evaluated; for * example, this can be used to implement "tooltip evaluation," where * the debugger automatically shows a little pop-up window with the * expression under the mouse cursor if the mouse cursor is left * hovering over some text for a few moments. In such cases, since the * user hasn't explicitly requested evaluation, it would be bad to make * any changes to game state, hence the prohibition of assignments or * calls. */ int dbgcompile(dbgcxdef *ctx, char *expr, dbgfdef *fr, objnum *objn, int speculative); /* display a stack traceback through a user callback */ void dbgstktr(dbgcxdef *ctx, void (*dispfn)(void *dispctx, const char *str, int strl), void *dispctx, int level, int toponly, int include_markers); /* format a display of where execution is stopped into a buffer */ void dbgwhere(dbgcxdef *ctx, char *buf); /* set a watch expression; returns error or 0 for success */ int dbgwxset(dbgcxdef *ctx, char *expr, int *wxnum, int level); /* delete a watch expression */ int dbgwxdel(dbgcxdef *ctx, int wxnum); /* update all watch expressions */ void dbgwxupd(dbgcxdef *ctx, void (*dispfn)(void *dispctx, const char *txt, int len), void *dispctx); /* switch to a new active lindef */ void dbgswitch(struct lindef **linp, struct lindef *newlin); /* ======================================================================== */ /* * User Interface Routines. The routines are called by the debugger * to perform user interaction. */ /* * Debugger user interface initialization, phase one. TADS calls this * routine during startup, before reading the .GAM file, to let the user * interface perform any initialization it requires before the .GAM file * is loaded. */ void dbguini(dbgcxdef *ctx, const char *game_filename); /* * Debugger user interface initialization, phase two. TADS calls this * routine during startup, after read the .GAM file. The debugger user * interface code can perform any required initialization that depends * on the .GAM file having been read. */ void dbguini2(dbgcxdef *ctx); /* * Determine if the debugger can resume from a run-time error. This * reflects the capabilities of the user interface of the debugger. In * particular, if the UI provides a way to change the instruction * pointer, then the debugger can resume from an error, since the user * can always move past the run-time error and continue execution. If * the UI doesn't let the user change the instruction pointer, resuming * from an error won't work, since the program will keep hitting the * same error and re-entering the debugger. If this returns false, the * run-time will trap to the debugger on an error, but will simply abort * the current command when the debugger returns. If this returns true, * the run-time will trap to the debugger on an error with the * instruction pointer set back to the start of the line containing the * error, and will thus re-try the same line of code when the debugger * returns, unless the debugger explicitly moves the instruction pointer * before returning. */ int dbgu_err_resume(dbgcxdef *ctx); /* * Find a source file. origname is the name of the source file as it * appears in the game's debugging information; this routine should * figure out where the file actually is, and put the fully-qualified * path to the file in fullname. The debugger calls this after it * exhausts all of its other methods of finding a source file (such as * searching the include path). * * Return true if the source file should be considered valid, false if * not. Most implementations will simply return true if the file was * found, false if not; however, this approach will cause the debugger * to terminate with an error at start-up if the user hasn't set up the * debugger's include path correctly before running the debugger. Some * implementations, in particular GUI implementations, may wish to wait * to find a file until the file is actually needed, rather than pester * the user with file search dialogs repeatedly at start-up. * * must_find_file specifies how to respond if we can't find the file. * If must_find_file is true, we should always return false if we can't * find the file. If must_find_file is false, however, we can * optionally return true even if we can't find the file. Doing so * indicates that the debugger UI will defer locating the file until it * is actually needed. * * If this routine returns true without actually finding the file, it * should set fullname[0] to '\0' to indicate that fullname doesn't * contain a valid filename. */ int dbgu_find_src(const char *origname, int origlen, char *fullname, size_t full_len, int must_find_file); /* * Debugger user interface main command loop. If err is non-zero, the * debugger was entered because a run-time error occurred; otherwise, if * bphit is non-zero, it's the number of the breakpoint that was * encountered; otherwise, the debugger was entered through a * single-step of some kind. exec_ofs is the byte offset within the * target object of the next instruction to be executed. This can be * changed upon return, in which case execution will continue from the * new offset, but the offset must be within the same method of the same * object (or within the same function) as it was upon entry. */ void dbgucmd(dbgcxdef *ctx, int bphit, int err, unsigned int *exec_ofs); /* * Debugger UI - quitting game. The runtime calls this routine just * before the play loop is about to terminate after the game code has * called the "quit" built-in function. If the debugger wants, it can * take control here (just as with dbgucmd()) for as long as it wants. * If the debugger wants to restart the game, it should call bifrst(). * If this routine returns without signalling a RUN_RESTART error, TADS * will terminate. If a RUN_RESTART error is signalled, TADS will * resume the play loop. */ void dbguquitting(dbgcxdef *ctx); /* * debugger user interface termination - this routine is called when the * debugger is about to terminate, so that the user interface can close * itself down (close windows, release memory, etc) */ void dbguterm(dbgcxdef *ctx); /* * Debugger user interface: display an error. This is called mainly so * that the debugger can display an error using special output * formatting if the error occurs while debugging. */ void dbguerr(dbgcxdef *ctx, int errnum, char *msg); /* turn hidden output tracing on/off */ void trchid(void); void trcsho(void); /* ======================================================================== */ /* * optional debugger macros - these compile to nothing when compiling a * version for use without the debugger */ #ifdef DBG_OFF # define dbgenter(ctx, bp, self, target, prop, binum, argc) # define dbgleave(ctx, exittype) # define dbgdump(ctx) # define dbgrst(ctx) ((void)0) # define dbgframe(ctx, frofs, linofs) # define dbgssi(ctx, ofs, instr, err, p) #else /* DBG_OFF */ # define dbgenter(ctx, bp, self, target, prop, binum, argc) \ dbgent(ctx, bp, self, target, prop, binum, argc) # define dbgleave(ctx, exittype) dbglv(ctx, exittype) # define dbgdump(ctx) dbgds(ctx) # define dbgrst(ctx) ((ctx)->dbgcxfcn = (ctx)->dbgcxdep = 0) # define dbgframe(ctx, frofs, linofs) \ (((ctx)->dbgcxfrm[(ctx)->dbgcxfcn - 1].dbgffr = (frofs)), \ ((ctx)->dbgcxfrm[(ctx)->dbgcxfcn - 1].dbgflin = (linofs))) # define dbgssi(ctx, ofs, instr, err, p) dbgss(ctx, ofs, instr, err, p) #endif /* DBG_OFF */ /* ======================================================================== */ /* private internal routines */ void dbgent(dbgcxdef *ctx, struct runsdef *bp, objnum self, objnum target, prpnum prop, int binum, int argc); void dbglv(dbgcxdef *ctx, int exittype); void dbgds(dbgcxdef *ctx); void dbgss(dbgcxdef *ctx, uint ofs, int instr, int err, uchar *noreg *p); void dbgpval(struct dbgcxdef *ctx, struct runsdef *val, void (*dispfn)(void *, const char *, int), void *dispctx, int showtype); int dbgtabsea(struct toktdef *tab, char *name, int namel, int hash, struct toksdef *ret); #ifdef __cplusplus } #endif #endif /* DBG_INCLUDED */ qtads-2.1.7/tads2/dbgtr.c000066400000000000000000000042071265017072300151260ustar00rootroot00000000000000/* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name dbgtr.c - Debugging functions for run-time Function Provides dummy entrypoints for various debugger functions for run-time. Notes Eliminates a number of time- and space-consuming functions from TR. Also defines a couple of TOKTH entrypoints since there will be no need for a symbol table when debugging is not enabled. Modified 12/18/92 MJRoberts - creation */ #include #include "os.h" #include "std.h" #include "tok.h" #include "dbg.h" /* indicate that the debugger is not present */ int dbgpresent() { return FALSE; } static void dummy_add(toktdef *tab, char *nam, int namel, int typ, int val, int hash) {} static int dummy_sea(toktdef *tab, char *nam, int namel, int hash, toksdef *ret) { return(0); } static void dummy_set(toktdef *tab, toksdef *sym) {} static void dummy_each(toktdef *tab, void (*fn)(void *, toksdef *), void *fnctx) {} uint tokhsh(char *nam) { return(0); } /* dummy symbol table entrypoints */ void tokthini(errcxdef *ec, mcmcxdef *mctx, toktdef *symtab1) { tokthdef *symtab = (tokthdef *)symtab1; /* convert to correct type */ CLRSTRUCT(*symtab); symtab1->toktfadd = dummy_add; symtab1->toktfsea = dummy_sea; symtab1->toktfset = dummy_set; symtab1->toktfeach = dummy_each; symtab1->tokterr = ec; symtab->tokthmem = mctx; } /* dummy debugger entrypoints */ void dbgent(dbgcxdef *ctx, struct runsdef *bp, objnum self, objnum target, prpnum prop, int binum, int argc) { } void dbglv(dbgcxdef *ctx, int exittype) { } int dbgnam(dbgcxdef *ctx, char *outbuf, int typ, int val) { memcpy(outbuf, "", (size_t)17); return(17); } void dbgds(dbgcxdef *ctx) { VARUSED(ctx); } int dbgu_err_resume(dbgcxdef *ctx) { VARUSED(ctx); return FALSE; } void dbguquitting(dbgcxdef *ctx) { VARUSED(ctx); } /* void dbglget() {} void dbgclin() {} void dbgstktr() {} */ qtads-2.1.7/tads2/emt.h000066400000000000000000000126201265017072300146140ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/EMT.H,v 1.2 1999/05/17 02:52:11 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name emt.h - emitter definitions Function Defines interface to the code emitter Notes None Modified 09/23/91 MJRoberts - creation */ #ifndef EMT_INCLUDED #define EMT_INCLUDED #ifndef STD_INCLUDED # include "std.h" #endif #ifndef ERR_INCLUDED # include "err.h" #endif #ifndef MCM_INCLUDED # include "mcm.h" #endif #ifndef OBJ_INCLUDED # include "obj.h" #endif #ifndef TOK_INCLUDED #include "tok.h" #endif /* temporary labels/forward reference fixups */ struct emtldef { ushort emtllnk; /* link: first/next forward reference */ ushort emtlofs; /* offset of label/forward reference to fix up */ uchar emtlflg; /* flags */ # define EMTLFSET 1 /* label has been set - no longer forward */ }; typedef struct emtldef emtldef; /* end of link list */ #define EMTLLNKEND ((ushort)~0) /* emitter context */ struct emtcxdef { errcxdef *emtcxerr; /* error handling context */ mcmcxdef *emtcxmem; /* cache manager context */ mcmon emtcxobj; /* object code is being written to */ ushort emtcxofs; /* offset of next byte of code to be written */ uchar *emtcxptr; /* pointer to object's memory */ int emtcxlcnt; /* size of emtcxlbl array */ ushort emtcxlfre; /* first free label/forward reference */ emtldef emtcxlbl[1]; /* array of labels/forward reference fixups */ objnum emtcxfrob; /* debug frame object, if any */ }; typedef struct emtcxdef emtcxdef; /* pre-emit list element structure */ struct emtledef { uchar emtletyp; long emtleval; }; typedef struct emtledef emtledef; /* pre-emit list definition structure */ struct emtlidef { int emtlicnt; /* number of elements in the list */ emtledef emtliele[1]; /* array of list elements */ }; typedef struct emtlidef emtlidef; /* emit a value, given as a token */ void emtval(emtcxdef *ctx, struct tokdef *tok, uchar *base); /* emit an operand-less opcode */ /* void emtop(emtcxdef *ctx, uchar op); */ #define emtop(ctx,op) \ (emtres(ctx, (ushort)1), \ (void)(*((ctx)->emtcxptr + (ctx)->emtcxofs++) = (op))) /* emit a single byte */ /* void emtbyte(emtcxdef *ctx, uchar b); */ #define emtbyte(ctx,b) emtop(ctx,b) /* emit bytes from memory */ /* void emtmem(emtcxdef *ctx, void *ptr, size_t siz); */ #define emtmem(ctx,ptr,siz) \ (emtres(ctx, (ushort)(siz)),\ memcpy((ctx)->emtcxptr + (ctx)->emtcxofs,ptr,(size_t)(siz)),\ (void)((ctx)->emtcxofs += (siz))) /* emit an 2-byte unsigned integer value */ /* void emtint2(emtcxdef *ctx, int i); */ #define emtint2(ctx,i) \ (emtres(ctx, (ushort)2), oswp2((ctx)->emtcxptr + (ctx)->emtcxofs, i), \ (ctx)->emtcxofs+=2) /* emit a 2-byte signed integer value */ /* void emtint2s(emtcxdef *ctx, int i); */ #define emtint2s(ctx,i) \ (emtres(ctx, (ushort)2), oswp2s((ctx)->emtcxptr + (ctx)->emtcxofs, i), \ (ctx)->emtcxofs+=2) /* emit a 2-byte integer at a particular offset in the code */ #define emtint2at(ctx,i,ofs) \ (oswp2((ctx)->emtcxptr + (ofs), i)) /* emit a signed 2-byte integer at a particular offset in the code */ #define emtint2sat(ctx,i,ofs) \ (oswp2s((ctx)->emtcxptr + (ofs), i)) /* read a 2-byte integer previously emitted */ #define emtint2from(ctx, ofs) \ (osrp2((ctx)->emtcxptr + (ofs))) /* emit a 4-byte integer value */ /* void emtint4(emtcxdef *ctx, long l); */ #define emtint4(ctx,l) \ (emtres(ctx, (ushort)4), oswp4s((ctx)->emtcxptr + (ctx)->emtcxofs, l), \ (ctx)->emtcxofs+=4) /* set up temporary labels in code generation context */ void emtlini(emtcxdef *ctx); /* get a temporary code label */ uint emtglbl(emtcxdef *ctx); /* get a temporary code label set to the current code location */ uint emtgslbl(emtcxdef *ctx); /* * Set a temporary label to the current position, releasing it if * desired. If the label is released, *lab is set to an invalid label * value (EMTLLNKEND) so that the caller can detect (for error-handling * purposes) whether the label has been freed. */ void emtslbl(emtcxdef *ctx, noreg uint *lab, int release); /* * Release a temporary label for re-use. *lab is set to EMTLLNKEND so * that the caller can detect (for error-handling purposes) whether the * label has been freed. */ void emtdlbl(emtcxdef *ctx, noreg uint *lab); /* * Clear label, ignoring errors (used for error cleanup). *lab is set * to EMTLLNKEND to flag that it's been freed. */ void emtclbl(emtcxdef *ctx, noreg uint *lab); /* return TRUE if a label has been set, FALSE if not set */ /* int emtqset(emtcxdef *ctx, uint lab); */ #define emtqset(ctx,lab) (((ctx)->emtcxlbl[lab].emtlflg & EMTLFSET) != 0) /* emit a jump to a temporary label */ void emtjmp(emtcxdef *ctx, uchar op, uint lab); /* reserve space for code */ void emtres(emtcxdef *ctx, ushort bytes); /* emit a list value */ void emtlst(emtcxdef *ctx, uint ofs, uchar *base); /* incremental addition to object size when we run out of space */ #define EMTINC 256 #endif /* EMT_INCLUDED */ qtads-2.1.7/tads2/err.h000066400000000000000000000554561265017072300146350ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/ERR.H,v 1.3 1999/07/11 00:46:29 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name err.h - error handling definitions Function Defines error handling mechanism Notes This package defines a set of macros that allows code to raise and handle exceptions. A macro is provided which signals an error, which does a non-local goto to the innermost enclosing exception handler. A set of macros sets up exception handling code. To catch exceptions that occur inside a block of code (i.e., in the code or in any subroutines called by the code), begin the block with ERRBEGIN. At the end of the protected code, place the exception handler, which starts with ERRCATCH. At the end of the exception handler, place ERREND. If no exception occurs, execution goes through the protected code, then resumes at the code following the ERREND. The exception handler can signal another error, which will cause the next enclosing frame to catch the error. Alternatively, if the exception handler doesn't signal an error or return, execution continues at the code following the ERREND. Exceptions that are signalled during exception handling will be caught by the next enclosing frame, unless the exception handler code is itself protected by another ERRBEGIN-ERREND block. To signal an error, use errsig(). To use a string argument in a signalled error, cover the string with errstr(ctx, str, len); for example: errsig1(ctx, ERR_XYZ, ERRTSTR, errstr(ctx, buf, strlen(buf))); This copies the string into a buffer that is unaffected by stack resetting during error signalling. Modified 12/30/92 MJRoberts - use new central library ler.h 09/14/92 MJRoberts - add errlog2 08/15/91 MJRoberts - creation */ #ifndef ERR_INCLUDED #define ERR_INCLUDED #include "os.h" #ifndef STD_INCLUDED #include "std.h" #endif #include "ler.h" /* * for compatility with old facility-free mechanism, signal with * facility "TADS" */ #define errsig(ctx, err) errsigf(ctx, "TADS", err) #define errsig1(c, e, t, a) errsigf1(c,"TADS",e,t,a) #define errsig2(c, e, t1, a1, t2, a2) errsigf2(c,"TADS",e,t1,a1,t2,a2) #define errlog(c, e) errlogf(c, "TADS", e) #define errlog1(c, e, t, a) errlogf1(c,"TADS",e,t,a) #define errlog2(c, e, t1, a1, t2, a2) errlogf2(c,"TADS",e,t1,a1,t2,a2) /*---------------------------- TADS Error Codes ----------------------------*/ /* memory/cache manager errors */ #define ERR_NOMEM 1 /* out of memory */ #define ERR_FSEEK 2 /* error seeking in file */ #define ERR_FREAD 3 /* error reading from file */ #define ERR_NOPAGE 4 /* no more page slots */ #define ERR_REALCK 5 /* attempting to reallocate a locked object */ #define ERR_SWAPBIG 6 /* swapfile limit reached - out of virtual memory */ #define ERR_FWRITE 7 /* error writing file */ #define ERR_SWAPPG 8 /* exceeded swap page table limit */ #define ERR_CLIUSE 9 /* requested client object number already in use */ #define ERR_CLIFULL 10 /* client mapping table is full */ #define ERR_NOMEM1 11 /* swapping/garbage collection failed to find enuf */ #define ERR_NOMEM2 12 /* no memory to resize (expand) an object */ #define ERR_OPSWAP 13 /* unable to open swap file */ #define ERR_NOHDR 14 /* can't get a new object header */ #define ERR_NOLOAD 15 /* mcm cannot find object to load (internal error) */ #define ERR_LCKFRE 16 /* attempting to free a locked object (internal) */ #define ERR_INVOBJ 17 /* invalid object */ #define ERR_BIGOBJ 18 /* object too big - exceeds memory allocation limit */ /* lexical analysis errors */ #define ERR_INVTOK 100 /* invalid token */ #define ERR_STREOF 101 /* end of file while scanning string */ #define ERR_TRUNC 102 /* symbol too long - truncated */ #define ERR_NOLCLSY 103 /* no space in local symbol table */ #define ERR_PRPDIR 104 /* invalid preprocessor (#) directive */ #define ERR_INCNOFN 105 /* no filename in #include directive */ #define ERR_INCSYN 106 /* invalid #include syntax */ #define ERR_INCSEAR 107 /* can't find included file */ #define ERR_INCMTCH 108 /* no matching delimiter in #include filename */ #define ERR_MANYSYM 109 /* out of space for symbol table */ #define ERR_LONGLIN 110 /* line too long */ #define ERR_INCRPT 111 /* header file already included - ignored */ #define ERR_PRAGMA 112 /* unknown pragma (ignored) */ #define ERR_BADPELSE 113 /* unexpected #else */ #define ERR_BADENDIF 114 /* unexpected #endif */ #define ERR_BADELIF 115 /* unexpected #elif */ #define ERR_MANYPIF 116 /* #if nesting too deep */ #define ERR_DEFREDEF 117 /* #define symbol already defined */ #define ERR_PUNDEF 118 /* #undef symbol not defined */ #define ERR_NOENDIF 119 /* missing #endif */ #define ERR_MACNEST 120 /* macros nested too deeply */ #define ERR_BADISDEF 121 /* invalid argument for defined() operator */ #define ERR_PIF_NA 122 /* #if is not implemented */ #define ERR_PELIF_NA 123 /* #elif is not implemented */ #define ERR_P_ERROR 124 /* error directive: %s */ #define ERR_LONG_FILE_MACRO 125 /* __FILE__ expansion too long */ #define ERR_LONG_LINE_MACRO 126 /* __LINE__ expansion too long */ /* undo errors */ #define ERR_UNDOVF 200 /* operation is too big for undo log */ #define ERR_NOUNDO 201 /* no more undo information */ #define ERR_ICUNDO 202 /* incomplete undo (no previous savepoint) */ /* parser errors */ #define ERR_REQTOK 300 /* expected token (arg 1) - found something else */ #define ERR_REQSYM 301 /* expected symbol */ #define ERR_REQPRP 302 /* expected a property name */ #define ERR_REQOPN 303 /* expected operand */ #define ERR_REQARG 304 /* expected comma or closing paren (arg list) */ #define ERR_NONODE 305 /* no space for new parse node */ #define ERR_REQOBJ 306 /* epxected object name */ #define ERR_REQEXT 307 /* redefining symbol as external function */ #define ERR_REQFCN 308 /* redefining symbol as function */ #define ERR_NOCLASS 309 /* can't use CLASS with function/external function */ #define ERR_REQUNO 310 /* required unary operator */ #define ERR_REQBIN 311 /* required binary operator */ #define ERR_INVBIN 312 /* invalid binary operator */ #define ERR_INVASI 313 /* invalid assignment */ #define ERR_REQVAR 314 /* required variable name */ #define ERR_LCLSYN 315 /* required comma or semicolon in local list */ #define ERR_REQRBR 316 /* required right brace (eof before end of group) */ #define ERR_BADBRK 317 /* 'break' without 'while' */ #define ERR_BADCNT 318 /* 'continue' without 'while' */ #define ERR_BADELS 319 /* 'else' without 'if' */ #define ERR_WEQASI 320 /* warning: possible use of '=' where ':=' intended */ #define ERR_EOF 321 /* unexpected end of file */ #define ERR_SYNTAX 322 /* general syntax error */ #define ERR_INVOP 323 /* invalid operand type */ #define ERR_NOMEMLC 324 /* no memory for new local symbol table */ #define ERR_NOMEMAR 325 /* no memory for argument symbol table */ #define ERR_FREDEF 326 /* redefining a function which is already defined */ #define ERR_NOSW 327 /* 'case' or 'default' and not in switch block */ #define ERR_SWRQCN 328 /* constant required in switch case value */ #define ERR_REQLBL 329 /* label required for 'goto' */ #define ERR_NOGOTO 330 /* 'goto' label never defined */ #define ERR_MANYSC 331 /* too many superclasses for object */ #define ERR_OREDEF 332 /* redefining symbol as object */ #define ERR_PREDEF 333 /* property being redefined in object */ #define ERR_BADPVL 334 /* invalid property value */ #define ERR_BADVOC 335 /* bad vocabulary property value */ #define ERR_BADTPL 336 /* bad template property value (need sstring) */ #define ERR_LONGTPL 337 /* template base property name too long */ #define ERR_MANYTPL 338 /* too many templates (internal compiler limit) */ #define ERR_BADCMPD 339 /* bad value for compound word (sstring required) */ #define ERR_BADFMT 340 /* bad value for format string (sstring needed) */ #define ERR_BADSYN 341 /* invalid value for synonym (sstring required) */ #define ERR_UNDFSYM 342 /* undefined symbol */ #define ERR_BADSPEC 343 /* bad special word */ #define ERR_NOSELF 344 /* "self" not valid in this context */ #define ERR_STREND 345 /* warning: possible unterminated string */ #define ERR_MODRPLX 346 /* modify/replace not allowed with external func */ #define ERR_MODFCN 347 /* modify not allowed with function */ #define ERR_MODFWD 348 /* modify/replace not allowed with forward func */ #define ERR_MODOBJ 349 /* modify can only be used with a defined object */ #define ERR_RPLSPEC 350 /* warning - replacing specialWords */ #define ERR_SPECNIL 351 /* nil only allowed with modify specialWords */ #define ERR_BADLCL 353 /* 'local' statement must precede executable code */ #define ERR_IMPPROP 354 /* implied verifier '%s' is not a property */ #define ERR_BADTPLF 355 /* invalid command template flag */ #define ERR_NOTPLFLG 356 /* flags are not allowed with old file format */ #define ERR_AMBIGBIN 357 /* warning: operator '%s' could be binary */ #define ERR_PIA 358 /* warning: possibly incorrect assignment */ #define ERR_BADSPECEXPR 359 /* invalid speculation evaluation */ /* code generation errors */ #define ERR_OBJOVF 400 /* object cannot grow any bigger - code too big */ #define ERR_NOLBL 401 /* no more temporary labels/fixups */ #define ERR_LBNOSET 402 /* (internal error) label never set */ #define ERR_INVLSTE 403 /* invalid datatype for list element */ #define ERR_MANYDBG 404 /* too many debugger line records (internal limit) */ /* vocabulary setup errors */ #define ERR_VOCINUS 450 /* vocabulary being redefined for object */ #define ERR_VOCMNPG 451 /* too many vocwdef pages (internal limit) */ #define ERR_VOCREVB 452 /* redefining same verb */ #define ERR_VOCREVB2 453 /* redefining same verb - two arguments */ /* set-up errors */ #define ERR_LOCNOBJ 500 /* location of object %s is not an object */ #define ERR_CNTNLST 501 /* contents of object %s is not list */ #define ERR_SUPOVF 502 /* overflow trying to build contents list */ #define ERR_RQOBJNF 503 /* required object %s not found */ #define ERR_WRNONF 504 /* warning - object %s not found */ #define ERR_MANYBIF 505 /* too many built-in functions (internal error) */ /* fio errors */ #define ERR_OPWGAM 600 /* unable to open game for writing */ #define ERR_WRTGAM 601 /* error writing to game file */ #define ERR_FIOMSC 602 /* too many sc's for writing in fiowrt */ #define ERR_UNDEFF 603 /* undefined function */ #define ERR_UNDEFO 604 /* undefined object */ #define ERR_UNDEF 605 /* undefined symbols found */ #define ERR_OPRGAM 606 /* unable to open game for reading */ #define ERR_RDGAM 607 /* error reading game file */ #define ERR_BADHDR 608 /* file has invalid header - not TADS game file */ #define ERR_UNKRSC 609 /* unknown resource type in .gam file */ #define ERR_UNKOTYP 610 /* unknown object type in OBJ resource */ #define ERR_BADVSN 611 /* file saved by different incompatible version */ #define ERR_LDGAM 612 /* error loading object on demand */ #define ERR_LDBIG 613 /* object too big for load region (prob. internal) */ #define ERR_UNXEXT 614 /* did not expect external function */ #define ERR_WRTVSN 615 /* compiler cannot write the requested version */ #define ERR_VNOCTAB 616 /* format version cannot be used with -ctab */ #define ERR_BADHDRRSC 617 /* invalid resource file header in file %s */ #define ERR_RDRSC 618 /* error reading resource file "xxx" */ /* character mapping errors */ #define ERR_CHRNOFILE 700 /* unable to load character mapping file */ /* user interrupt */ #define ERR_USRINT 990 /* user requested cancel of current operation */ /* run-time errors */ #define ERR_STKOVF 1001 /* stack overflow */ #define ERR_HPOVF 1002 /* heap overflow */ #define ERR_REQNUM 1003 /* numeric value required */ #define ERR_STKUND 1004 /* stack underflow */ #define ERR_REQLOG 1005 /* logical value required */ #define ERR_INVCMP 1006 /* invalid datatypes for magnitude comparison */ #define ERR_REQSTR 1007 /* string value required */ #define ERR_INVADD 1008 /* invalid datatypes for '+' operator */ #define ERR_INVSUB 1009 /* invalid datatypes for binary '-' operator */ #define ERR_REQVOB 1010 /* require object value */ #define ERR_REQVFN 1011 /* required function pointer */ #define ERR_REQVPR 1012 /* required property number value */ /* non-error conditions: run-time EXIT, ABORT, ASKIO, ASKDO */ #define ERR_RUNEXIT 1013 /* 'exit' statement executed */ #define ERR_RUNABRT 1014 /* 'abort' statement executed */ #define ERR_RUNASKD 1015 /* 'askdo' statement executed */ #define ERR_RUNASKI 1016 /* 'askio' executed; int arg 1 is prep */ #define ERR_RUNQUIT 1017 /* 'quit' executed */ #define ERR_RUNRESTART 1018 /* 'reset' executed */ #define ERR_RUNEXITOBJ 1019 /* 'exitobj' executed */ #define ERR_REQVLS 1020 /* list value required */ #define ERR_LOWINX 1021 /* index value too low (must be >= 1) */ #define ERR_HIGHINX 1022 /* index value too high (must be <= length(list)) */ #define ERR_INVTBIF 1023 /* invalid type for built-in function */ #define ERR_INVVBIF 1024 /* invalid value for built-in function */ #define ERR_BIFARGC 1025 /* wrong number of arguments to built-in */ #define ERR_ARGC 1026 /* wrong number of arguments to user function */ #define ERR_FUSEVAL 1027 /* string/list not allowed for fuse/daemon arg */ #define ERR_BADSETF 1028 /* internal error in setfuse/setdaemon/notify */ #define ERR_MANYFUS 1029 /* too many fuses */ #define ERR_MANYDMN 1030 /* too many daemons */ #define ERR_MANYNFY 1031 /* too many notifiers */ #define ERR_NOFUSE 1032 /* fuse not found in remfuse */ #define ERR_NODMN 1033 /* daemon not found in remdaemon */ #define ERR_NONFY 1034 /* notifier not found in unnotify */ #define ERR_BADREMF 1035 /* internal error in remfuse/remdaemon/unnotify */ #define ERR_DMDLOOP 1036 /* load-on-demand loop: property not being set */ #define ERR_UNDFOBJ 1037 /* undefined object in vocabulary tree */ #define ERR_BIFCSTR 1038 /* c-string conversion overflows buffer */ #define ERR_INVOPC 1039 /* invalid opcode */ #define ERR_RUNNOBJ 1040 /* runtime error: property taken of non-object */ #define ERR_EXTLOAD 1041 /* unable to load external function "%s" */ #define ERR_EXTRUN 1042 /* error executing external function "%s" */ #define ERR_CIRCSYN 1043 /* circular synonym */ #define ERR_DIVZERO 1044 /* divide by zero */ #define ERR_BADDEL 1045 /* can only delete objects created with "new" */ #define ERR_BADNEWSC 1046 /* superclass for "new" cannot be a new object */ #define ERR_VOCSTK 1047 /* insufficient space in parser stack */ #define ERR_BADFILE 1048 /* invalid file handle */ #define ERR_RUNEXITPRECMD 1049 /* exited from preCommand */ /* run-time parser errors */ #define ERR_PRS_SENT_UNK 1200 /* sentence structure not recognized */ #define ERR_PRS_VERDO_FAIL 1201 /* verDoVerb failed */ #define ERR_PRS_VERIO_FAIL 1202 /* verIoVerb failed */ #define ERR_PRS_NO_VERDO 1203 /* no verDoVerb for direct object */ #define ERR_PRS_NO_VERIO 1204 /* no verIoVerb for direct object */ #define ERR_PRS_VAL_DO_FAIL 1205 /* direct object validation failed */ #define ERR_PRS_VAL_IO_FAIL 1206 /* indirect object validation failed */ /* compiler/runtime/debugger driver errors */ #define ERR_USAGE 1500 /* invalid usage */ #define ERR_OPNINP 1501 /* error opening input file */ #define ERR_NODBG 1502 /* game not compiled for debugging */ #define ERR_ERRFIL 1503 /* unable to open error capture file */ #define ERR_PRSCXSIZ 1504 /* parse pool + local size too large */ #define ERR_STKSIZE 1505 /* stack size too large */ #define ERR_OPNSTRFIL 1506 /* error opening string capture file */ #define ERR_INVCMAP 1507 /* invalid character map file */ /* debugger errors */ #define ERR_BPSYM 2000 /* symbol not found for breakpoint */ #define ERR_BPPROP 2002 /* breakpoint symbol is not a property */ #define ERR_BPFUNC 2003 /* breakpoint symbol is not a function */ #define ERR_BPNOPRP 2004 /* property is not defined for object */ #define ERR_BPPRPNC 2005 /* property is not code */ #define ERR_BPSET 2006 /* breakpoint already set at this location */ #define ERR_BPNOTLN 2007 /* breakpoint is not at a line (OPCLINE instr) */ #define ERR_MANYBP 2008 /* too many breakpoints */ #define ERR_BPNSET 2009 /* breakpoint to be deleted was not set */ #define ERR_DBGMNSY 2010 /* too many symbols in debug expression (int lim) */ #define ERR_NOSOURC 2011 /* unable to find source file %s */ #define ERR_WTCHLCL 2012 /* illegal to assign to local in watch expr */ #define ERR_INACTFR 2013 /* inactive frame (expression value not available) */ #define ERR_MANYWX 2014 /* too many watch expressions */ #define ERR_WXNSET 2015 /* watchpoint not set */ #define ERR_EXTRTXT 2016 /* extraneous text at end of command */ #define ERR_BPOBJ 2017 /* breakpoint symbol is not an object */ #define ERR_DBGINACT 2018 /* debugger is not active */ #define ERR_BPINUSE 2019 /* breakpoint is already used */ #define ERR_RTBADSPECEXPR 2020 /* invalid speculative expression */ #define ERR_NEEDLIN2 2021 /* -ds2 information not found - must recompile */ /* usage error messages */ #define ERR_TCUS1 3000 /* first tc usage message */ #define ERR_TCUSL 3024 /* last tc usage message */ #define ERR_TCTGUS1 3030 /* first tc toggle message */ #define ERR_TCTGUSL 3032 #define ERR_TCZUS1 3040 /* first tc -Z suboptions usage message */ #define ERR_TCZUSL 3041 #define ERR_TC1US1 3050 /* first tc -1 suboptions usage message */ #define ERR_TC1USL 3058 #define ERR_TCMUS1 3070 /* first tc -m suboptions usage message */ #define ERR_TCMUSL 3076 #define ERR_TCVUS1 3080 /* first -v suboption usage message */ #define ERR_TCVUSL 3082 #define ERR_TRUSPARM 3099 #define ERR_TRUS1 3100 /* first tr usage message */ #define ERR_TRUSL 3117 #define ERR_TRUSFT1 3118 /* first tr "footer" message */ #define ERR_TRUSFTL 3119 /* last tr "footer" message */ #define ERR_TRSUS1 3150 /* first tr -s suboptions usage message */ #define ERR_TRSUSL 3157 #define ERR_TDBUSPARM 3199 #define ERR_TDBUS1 3200 /* first tdb usage message */ #define ERR_TDBUSL 3214 /* last tdb usage message */ /* TR 16-bit MSDOS-specific usage messages */ #define ERR_TRUS_DOS_1 3300 #define ERR_TRUS_DOS_L 3300 /* TR 32-bit MSDOS console mode usage messages */ #define ERR_TRUS_DOS32_1 3310 #define ERR_TRUS_DOS32_L 3312 /* TADS/Graphic errors */ #define ERR_GNOFIL 4001 /* can't find graphics file %s */ #define ERR_GNORM 4002 /* can't find room %s */ #define ERR_GNOOBJ 4003 /* can't find hot spot object %s */ #define ERR_GNOICN 4004 /* can't find icon object %s */ /* * Special error flag - this is returned from execmd() when preparseCmd * returns a command list. This indicates to voc1cmd that it should try * the command over again, using the words in the new list. */ #define ERR_PREPRSCMDREDO 30000 /* preparseCmd returned a list */ #define ERR_PREPRSCMDCAN 30001 /* preparseCmd returned 'nil' to cancel */ #endif /* ERR_INCLUDED */ qtads-2.1.7/tads2/errmsg.c000066400000000000000000001012751265017072300153260ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/ERRMSG.C,v 1.3 1999/07/11 00:46:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name errmsg.c - error message text Function provides error message text for TADS2 error handlers Notes none Modified 11/26/92 JEras - send .MSG output to named file 11/15/92 JEras - portable .MSG format 04/05/92 MJRoberts - creation */ #include "os.h" #include "err.h" #include "mch.h" #ifdef ERR_LINK_MESSAGES /* * Define some messages, if they're not otherwise defined. On some * systems, the os-dependent headers define non-standard default sizes * for some of the memory settings. Whenever an OS header does this, it * should define the corresponding usage message string. We'll define * any that aren't already defined in the OS headers to provide default * values here. */ #ifndef TCD_HEAPSIZ_MSG #define TCD_HEAPSIZ_MSG " -mh size heap size (default 1024 bytes)" #endif #ifndef TCD_POOLSIZ_MSG #define TCD_POOLSIZ_MSG \ " -mp size parse node pool size (default 6144 bytes)" #endif #ifndef TCD_LCLSIZ_MSG #define TCD_LCLSIZ_MSG \ " -ml size local symbol table size (default 4096 bytes)" #endif #ifndef TCD_STKSIZ_MSG #define TCD_STKSIZ_MSG " -ms size stack size (default 50 elements)" #endif #ifndef TCD_LABSIZ_MSG #define TCD_LABSIZ_MSG \ " -mg size goto label table size (default 1024 bytes)" #endif #ifndef TRD_HEAPSIZ_MSG #define TRD_HEAPSIZ_MSG " -mh size heap size (default 2048 bytes)" #endif #ifndef TRD_STKSIZ_MSG #define TRD_STKSIZ_MSG " -ms size stack size (default 200 elements)" #endif #ifndef TRD_UNDOSIZ_MSG #define TRD_UNDOSIZ_MSG \ " -u size set undo to size (0 to disable; default 16384)" #endif #ifndef TDD_HEAPSIZ_MSG #define TDD_HEAPSIZ_MSG " -mh size heap size (default 2048 bytes)" #endif #ifndef TDD_STKSIZ_MSG #define TDD_STKSIZ_MSG " -ms size stack size (default 200 elements)" #endif #ifndef TDD_UNDOSIZ_MSG #define TDD_UNDOSIZ_MSG \ " -u size set undo to size (0 to disable; default 16384)" #endif #ifndef TDD_POOLSIZ_MSG #define TDD_POOLSIZ_MSG " -mp size parse pool size (default 2048 bytes)" #endif /* * The error list */ static errmdef errlist[] = { { ERR_NOMEM, "fatal: out of memory" }, { ERR_FSEEK, "fatal: error seeking in file" }, { ERR_FREAD, "fatal: error reading from file" }, { ERR_NOPAGE, "fatal: no more page slots" }, { ERR_REALCK, "attempting to reallocate a locked object" }, { ERR_SWAPBIG, "fatal: swapfile limit reached - out of virtual memory" }, { ERR_FWRITE, "fatal: error writing file" }, { ERR_SWAPPG, "fatal: exceeded swap page table limit" }, { ERR_CLIUSE, "requested client object number already in use" }, { ERR_CLIFULL, "fatal: client mapping table is full" }, { ERR_NOMEM1, "fatal: no memory, even after swapping/garbage collection" }, { ERR_NOMEM2, "fatal: no memory to resize (expand) an object" }, { ERR_OPSWAP, "unable to open swap file" }, { ERR_NOHDR, "can't get a new object header" }, { ERR_NOLOAD, "mcm cannot find object to load (internal error)" }, { ERR_LCKFRE, "attempting to free a locked object (internal error)" }, { ERR_INVOBJ, "invalid object (object has been deleted)" }, { ERR_BIGOBJ, "object too big - exceeds maximum object allocation size" }, { ERR_INVTOK, "invalid token" }, { ERR_STREOF, "end of file while scanning string" }, { ERR_TRUNC, "warning: symbol too long - truncated to \"%s\"" }, { ERR_NOLCLSY, "fatal: no space in local symbol table" }, { ERR_PRPDIR, "invalid preprocessor (#) directive" }, { ERR_INCNOFN, "no filename in #include directive" }, { ERR_INCSYN, "invalid #include syntax" }, { ERR_INCSEAR, "can't find included file \"%s\"" }, { ERR_INCMTCH, "no matching delimiter in #include filename" }, { ERR_MANYSYM, "fatal: out of space for symbol table expansion" }, { ERR_LONGLIN, "input line is too long" }, { ERR_INCRPT, "warning: file \"%s\" already included; #include ignored" }, { ERR_PRAGMA, "unknown pragma (ignored)" }, { ERR_BADPELSE, "unexpected #else" }, { ERR_BADENDIF, "unexpected #endif" }, { ERR_BADELIF, "unexpected #elif" }, { ERR_MANYPIF, "#if nesting too deep" }, { ERR_DEFREDEF, "#define symbol already defined -- redefinition ignored" }, { ERR_PUNDEF, "warning: symbol not defined in #undef" }, { ERR_NOENDIF, "missing #endif" }, { ERR_MACNEST, "macros nested too deeply" }, { ERR_BADISDEF, "invalid argument for defined() preprocessor operator" }, { ERR_PIF_NA, "#if is not implemented" }, { ERR_PELIF_NA, "#elif is not implemented" }, { ERR_P_ERROR, "Error directive: %s" }, { ERR_LONG_FILE_MACRO, "internal error: __FILE__ expansion too long" }, { ERR_LONG_LINE_MACRO, "internal error: __LINE__ expansion too long" }, { ERR_UNDOVF, "operation is too big for undo log" }, { ERR_NOUNDO, "no more undo information" }, { ERR_ICUNDO, "incomplete undo (no previous savepoint)" }, { ERR_REQTOK, "expected %s" }, { ERR_REQSYM, "expected a symbol" }, { ERR_REQPRP, "expected a property name" }, { ERR_REQOPN, "expected an operand" }, { ERR_REQARG, "expected a comma or closing paren (in arg list)" }, { ERR_NONODE, "fatal: no space for new parse node (use -mp option to increase the limit)" }, { ERR_REQOBJ, "expected object name" }, { ERR_REQEXT, "redefining symbol as external function" }, { ERR_REQFCN, "redefining symbol as function" }, { ERR_NOCLASS, "can't use \"class\" with function/external function" }, { ERR_REQUNO, "unary operator required" }, { ERR_REQBIN, "binary operator required" }, { ERR_INVBIN, "invalid binary operator" }, { ERR_INVASI, "invalid assignment" }, { ERR_REQVAR, "variable name required" }, { ERR_LCLSYN, "comma or semicolon required in local list" }, { ERR_REQRBR, "right brace required (eof before end of group)" }, { ERR_BADBRK, "'break' without 'while'" }, { ERR_BADCNT, "'continue' without 'while'" }, { ERR_BADELS, "'else' without 'if'" }, { ERR_WEQASI, "warning: possible use of '=' where ':=' intended" }, { ERR_EOF, "unexpected end of file" }, { ERR_SYNTAX, "general syntax error" }, { ERR_INVOP, "invalid operand type" }, { ERR_NOMEMLC, "fatal: can't expand local symbol table" }, { ERR_NOMEMAR, "fatal: can't expand argument symbol table" }, { ERR_FREDEF, "redefining a function which is already defined" }, { ERR_NOSW, "'case' or 'default' not in switch block" }, { ERR_SWRQCN, "constant required in switch case value" }, { ERR_REQLBL, "label required for 'goto'" }, { ERR_NOGOTO, "'goto' label never defined" }, { ERR_MANYSC, "too many superclasses for object" }, { ERR_OREDEF, "redefining symbol as object" }, { ERR_PREDEF, "property being redefined in object" }, { ERR_BADPVL, "invalid property value" }, { ERR_BADVOC, "invalid vocabulary property value" }, { ERR_BADTPL, "invalid template property value (need string)" }, { ERR_LONGTPL, "template base property name too long" }, { ERR_MANYTPL, "too many templates (internal compiler limit)" }, { ERR_BADCMPD, "invalid value for compound word (string required)" }, { ERR_BADFMT, "invalid value for format string (string required)" }, { ERR_BADSYN, "invalid value for synonym (string required)"}, { ERR_UNDFSYM, "undefined symbol" }, { ERR_BADSPEC, "invalid value for specialWords list (string required)" }, { ERR_NOSELF, "\"self\" is not valid in this context" }, { ERR_STREND, "warning: possible unterminated string" }, { ERR_MODRPLX, "'replace'/'modify' not allowed with external function" }, { ERR_MODFCN, "'modify' not allowed with function; ignored" }, { ERR_MODFWD, "'modify'/'replace' ignored with forward declaration" }, { ERR_MODOBJ, "'modify' can only be used with a defined object" }, { ERR_RPLSPEC, "warning: replacing specialWords (please use 'replace')" }, { ERR_SPECNIL, "nil allowed only with modify specialWords" }, { ERR_BADLCL, "'local' is only allowed at the beginning of a block" }, { ERR_IMPPROP,"implied verifier '%s' is not a property" }, { ERR_BADTPLF,"invalid command template flag" }, { ERR_NOTPLFLG,"flags are not allowed with old file formats" }, { ERR_AMBIGBIN,"warning: operator '%s' interpreted as unary in list" }, { ERR_PIA,"warning: possibly incorrect assignment" }, { ERR_BADSPECEXPR, "speculative evaluation failed" }, { ERR_OBJOVF, "object cannot grow any bigger - code too big" }, { ERR_NOLBL, "no more temporary labels/fixups (internal compiler limit)"}, { ERR_LBNOSET, "(internal error) label never set" }, { ERR_INVLSTE, "invalid datatype for list element" }, { ERR_MANYDBG, "fatal: too many debugger source line records (internal limit)" }, { ERR_VOCINUS, "vocabulary being redefined for object" }, { ERR_VOCMNPG, "fatal: too many vocabulary word relations (internal limit)" }, { ERR_VOCREVB, "warning: same verb '%s' defined for two objects" }, { ERR_VOCREVB2, "warning: same verb '%s %s' defined for two objects" }, { ERR_LOCNOBJ, "warning: location of object \"%s\" is not an object" }, { ERR_CNTNLST, "warning: contents of object \"%s\" is not list" }, { ERR_SUPOVF, "overflow trying to build contents list" }, { ERR_RQOBJNF, "required object \"%s\" not found" }, { ERR_WRNONF, "warning: object \"%s\" not found" }, { ERR_MANYBIF, "too many built-in functions (internal error)" }, { ERR_OPWGAM, "unable to open game for writing" }, { ERR_WRTGAM, "error writing to game file" }, { ERR_FIOMSC, "too many sc's for writing in fiowrt" }, { ERR_UNDEFF, "undefined function \"%s\"" }, { ERR_UNDEFO, "undefined object \"%s\"" }, { ERR_UNDEF, "undefined symbols found" }, { ERR_OPRGAM, "unable to open game for reading" }, { ERR_RDGAM, "error reading game file" }, { ERR_BADHDR, "file has invalid header - not a TADS game file" }, { ERR_UNKRSC, "unknown resource type in .gam file" }, { ERR_UNKOTYP, "unknown object type in OBJ resource" }, { ERR_BADVSN, "file saved by different (incompatible) version" }, { ERR_LDGAM, "error loading object on demand" }, { ERR_LDBIG, "object too big for load region (internal error)" }, { ERR_UNXEXT, "did not expect external function" }, { ERR_WRTVSN, "compiler cannot write this file version" }, { ERR_VNOCTAB, "this file version cannot be used with a -ctab setting" }, { ERR_BADHDRRSC, "resource file \"%s\" has an invalid header" }, { ERR_RDRSC, "error reading resource file \"%s\"" }, { ERR_CHRNOFILE, "character table file \"%s\" not found for internal character set \"%s\""}, { ERR_USRINT, "user requested cancel of current operation" }, { ERR_STKOVF, "stack overflow" }, { ERR_HPOVF, "heap overflow" }, { ERR_REQNUM, "numeric value required" }, { ERR_STKUND, "stack underflow" }, { ERR_REQLOG, "logical value required" }, { ERR_INVCMP, "invalid datatypes for magnitude comparison" }, { ERR_REQSTR, "string value required" }, { ERR_INVADD, "invalid datatypes for binary '+' operator" }, { ERR_INVSUB, "invalid datatypes for binary '-' operator" }, { ERR_REQVOB, "object value required" }, { ERR_REQVFN, "function pointer required" }, { ERR_REQVPR, "property pointer value required" }, { ERR_RUNEXIT, "'exit' statement executed" }, { ERR_RUNABRT, "'abort' statement executed" }, { ERR_RUNASKD, "'askdo' statement executed" }, { ERR_RUNASKI, "'askio' executed; preposition is object #%d" }, { ERR_RUNQUIT, "'quit' executed" }, { ERR_RUNRESTART, "'reset' executed" }, { ERR_RUNEXITOBJ, "'exitobj' executed" }, { ERR_REQVLS, "list value required" }, { ERR_LOWINX, "index value too low (must be at least 1)" }, { ERR_HIGHINX,"index value too high (must be at most length(list))" }, { ERR_INVTBIF,"invalid type for built-in function" }, { ERR_INVVBIF,"invalid value for built-in function \"%s\"" }, { ERR_BIFARGC,"wrong number of arguments to built-in" }, { ERR_ARGC, "wrong number of arguments to user function \"%s\"" }, { ERR_FUSEVAL,"string/list not allowed for fuse/daemon arg" }, { ERR_BADSETF,"internal error in setfuse/setdaemon/notify" }, { ERR_MANYFUS,"too many fuses" }, { ERR_MANYDMN,"too many daemons" }, { ERR_MANYNFY,"too many notifiers" }, { ERR_NOFUSE, "fuse not found in remfuse" }, { ERR_NODMN, "daemon not found in remdaemon" }, { ERR_NONFY, "notifier not found in unnotify" }, { ERR_BADREMF,"internal error in remfuse/remdaemon/unnotify" }, { ERR_DMDLOOP,"load-on-demand loop: property not being set (internal)" }, { ERR_UNDFOBJ,"undefined object \"%s\" in class list"}, { ERR_BIFCSTR,"c-string conversion overflows buffer (internal limit)" }, { ERR_INVOPC, "invalid opcode (internal error)" }, { ERR_RUNNOBJ,"property evaluated for non-existent object" }, { ERR_EXTLOAD,"unable to load external function \"%s\"" }, { ERR_EXTRUN, "external functions are obsolete (attempted to invoke \"%s\")" }, { ERR_CIRCSYN,"circular synonym" }, { ERR_DIVZERO,"divide by zero" }, { ERR_BADDEL, "only objects allocated with 'new' can be deleted" }, { ERR_BADNEWSC,"invalid superclass for 'new'" }, { ERR_VOCSTK, "fatal: out of space in parser stack" }, { ERR_BADFILE, "invalid file handle" }, { ERR_PRS_SENT_UNK, "unrecognized sentence structure" }, { ERR_PRS_VERDO_FAIL, "direct object validation failed" }, { ERR_PRS_VERIO_FAIL, "indirect object validation failed" }, { ERR_PRS_NO_VERDO, "no direct object validation method defined" }, { ERR_PRS_NO_VERIO, "no indirect object validation method defined" }, { ERR_PRS_VAL_DO_FAIL, "direct object validation failed" }, { ERR_PRS_VAL_IO_FAIL, "indirect object validation failed" }, { ERR_USAGE, "invalid command-line usage" }, { ERR_OPNINP, "error opening input file" }, { ERR_NODBG, "game not compiled for debugging - recompile with -ds or -ds2 \ option (check your debugger instructions to determine which option to \ use with your version of the debugger)" }, { ERR_ERRFIL, "unable to create error logging file" }, { ERR_PRSCXSIZ, "parse pool + local sizes too large - total cannot exceed %u" }, { ERR_STKSIZE, "stack size too large - cannot exceed %u" }, { ERR_OPNSTRFIL, "error creating string capture file" }, { ERR_INVCMAP, "character map file not found or invalid" }, { ERR_BPSYM, "error setting breakpoint: unknown symbol" }, { ERR_BPPROP, "error setting breakpoint: symbol is not a property" }, { ERR_BPFUNC, "error setting breakpoint: symbol is not a function" }, { ERR_BPNOPRP, "error setting breakpoint: property not defined in object" }, { ERR_BPPRPNC, "error setting breakpoint: property is not code" }, { ERR_BPSET, "error: breakpoint is already set at this location" }, { ERR_BPNOTLN, "error setting breakpoint: breakpoint not at line" }, { ERR_MANYBP, "too many breakpoints" }, { ERR_BPNSET, "breakpoint was not set" }, { ERR_DBGMNSY, "too many symbols in eval expression (internal limit)" }, { ERR_NOSOURC, "unable to find source file \"%s\"" }, { ERR_WTCHLCL, "assignment to local is illegal in watch expression" }, { ERR_INACTFR, "inactive frame (expression value not available)" }, { ERR_MANYWX, "too many watch expressions" }, { ERR_WXNSET, "watch expression not set" }, { ERR_EXTRTXT, "extraneous text after end of command" }, { ERR_BPOBJ, "error setting breakpoint: symbol is not an object" }, { ERR_DBGINACT, "debugger is inactive (program running)" }, { ERR_BPINUSE, "breakpoint is already used" }, { ERR_RTBADSPECEXPR, "speculative evaluation failed (runtime)" }, { ERR_NEEDLIN2, "New-style debugging records required. Recompile game with -ds2 option." }, /* compiler usage messages */ { ERR_TCUS1, OS_TC_USAGE }, { ERR_TCUS1 + 1, "options:" }, { ERR_TCUS1 + 2, " -C (-)[toggle] use C-style =, == operators"}, { ERR_TCUS1 + 3, " -case (+)[toggle] turn case sensitivity on(+) or off(-)" }, { ERR_TCUS1 + 4, " -ctab file use character mapping table file" }, { ERR_TCUS1 + 5, " -D sym=val define preprocessor symbol" }, { ERR_TCUS1 + 6, " -ds (-)[toggle] generate source debugging information" }, { ERR_TCUS1 + 7, " -ds2 (-)[toggle] generate new-style debug information" }, { ERR_TCUS1 + 8, " -e file capture errors/warnings in file" }, { ERR_TCUS1 + 9, " -Fs file capture strings in file" }, { ERR_TCUS1 + 10, " -fv a|b|c|* .GAM file version to generate" }, { ERR_TCUS1 + 11," -i path add path to include file search list" }, { ERR_TCUS1 + 12," -l file load precompiled header file" }, { ERR_TCUS1 + 13," -m memory suboptions (use -m? to list)" }, { ERR_TCUS1 + 14, " -o file write game to file (-o- for no output)" }, { ERR_TCUS1 + 15, " -p (-)[toggle] pause for key after compilation" }, { ERR_TCUS1 + 16, " -s (-)[toggle] show statistics after compilation" }, { ERR_TCUS1 + 17, " -tf file use file for swapping (default: TADSSWAP.DAT)" }, { ERR_TCUS1 + 18, " -ts size maximum swapfile size (default: unlimited)" }, { ERR_TCUS1 + 19, #if OS_DEFAULT_SWAP_ENABLED " -t (+)[toggle] swapping" }, #else " -t (-)[toggle] swapping" }, #endif { ERR_TCUS1 + 20, " -U sym undefine preprocessor symbol" }, { ERR_TCUS1 + 21, " -v arg set warning level (-v? for details)" }, { ERR_TCUS1 + 22, " -w file precompile headers to file" }, { ERR_TCUS1 + 23, " -Z code generation options (use -Z? to list)" }, { ERR_TCUSL, " -1 v1 compatibility options (use -1? to list)" }, { ERR_TCTGUS1, "" }, { ERR_TCTGUS1 + 1, "toggle options: add + to enable, - to disable, nothing to toggle" }, { ERR_TCTGUSL, "(default is shown in parentheses before [toggle] above)"}, { ERR_TCZUS1, "-Z (code generation) suboptions:" }, { ERR_TCZUSL, " -Za (+)[toggle] run-time user function argument checking" }, { ERR_TC1US1, "-1 (TADS version 1 compatibility) suboptions:" }, { ERR_TC1US1 + 1, " -1a (+)[toggle] run-time user function argument checking" }, { ERR_TC1US1 + 2, " (same as -Za)" }, { ERR_TC1US1 + 3, " -1k (-)[toggle] disable new keywords" }, { ERR_TC1US1 + 4, " -1e (-)[toggle] ignore ';' after 'if {...}'" }, { ERR_TC1US1 + 5, " -1d new replace 'do' keyword with 'new'" }, { ERR_TC1USL - 2, "" }, { ERR_TC1USL - 1, "With no suboption, -1 acts like a toggle option for all" }, { ERR_TC1USL, "toggle-type v1 compatibility options" }, { ERR_TCMUS1, "-m (memory) suboptions:" }, { ERR_TCMUS1 + 1, " -m size limit in-memory cache to size (in bytes)" }, { ERR_TCMUS1 + 2, TCD_LABSIZ_MSG }, { ERR_TCMUS1 + 3, TCD_HEAPSIZ_MSG }, { ERR_TCMUS1 + 4, TCD_LCLSIZ_MSG }, { ERR_TCMUS1 + 5, TCD_POOLSIZ_MSG }, { ERR_TCMUSL, TCD_STKSIZ_MSG }, { ERR_TCVUS1, "-v (warning verbosity level) suboptions:" }, { ERR_TCVUS1 + 1, " -v level set warning verbosity to level (default is 0)" }, { ERR_TCVUSL, " -v-abin disable ambiguous binary operator warnings (+abin enables)" }, /* runtime usage */ { ERR_TRUSPARM, "usage: %s [options] file" }, { ERR_TRUS1, OS_TR_USAGE }, { ERR_TRUS1 + 1, "options:" }, { ERR_TRUS1 + 2, " -ctab file use character mapping table file" }, { ERR_TRUS1 + 3, " -ctab- do not use any character mapping file" }, { ERR_TRUS1 + 4, " -i file read commands from file" }, { ERR_TRUS1 + 5, " -o file write commands to file" }, { ERR_TRUS1 + 6, " -l file log all output to file" }, { ERR_TRUS1 + 7, " -m size maximum cache size (in bytes)" }, { ERR_TRUS1 + 8, TRD_HEAPSIZ_MSG }, { ERR_TRUS1 + 9, TRD_STKSIZ_MSG }, { ERR_TRUS1 + 10, " -p [toggle] pause after play" }, { ERR_TRUS1 + 11, " -r savefile restore saved game position from savefile" }, { ERR_TRUS1 + 12, " -s level set I/O safety level (-s? for help)" }, { ERR_TRUS1 + 13, " -tf file use file for swapping (default: TADSSWAP.DAT)" }, { ERR_TRUS1 + 14, " -ts size maximum swapfile size (default: unlimited)" }, { ERR_TRUS1 + 15, " -tp (-)[toggle] preload all objects" }, { ERR_TRUS1 + 16, #if OS_DEFAULT_SWAP_ENABLED " -t- disable swapping (enabled by default)" }, #else " -t+ enable swapping (disabled by default)" }, #endif { ERR_TRUS1 + 17, TRD_UNDOSIZ_MSG }, { ERR_TRUSFT1, "" }, { ERR_TRUSFT1 + 1, "toggle options: add + to enable, - to disable, nothing to toggle" }, /* tr security suboptions help */ { ERR_TRSUS1, "-s (file I/O safety level) suboptions:" }, { ERR_TRSUS1 + 1, " -s0 minimum safety - read and write in any directory" }, { ERR_TRSUS1 + 2, " -s1 read in any directory, write in current directory only" }, { ERR_TRSUS1 + 3, " -s2 (default) read and write in current directory only" }, { ERR_TRSUS1 + 4, " -s3 read-only access in current directory only" }, { ERR_TRSUS1 + 5, " -s4 maximum safety - no file I/O allowed" }, { ERR_TRSUS1 + 6, "(These options apply only to explicit file operations by the game,"}, { ERR_TRSUSL, "not to saving and restoring games.)" }, /* debugger usage */ { ERR_TDBUSPARM, "usage: %s [options] file" }, { ERR_TDBUS1, OS_TDB_USAGE }, { ERR_TDBUS1 + 1, "options:"}, { ERR_TDBUS1 + 2, " -ctab file use character mapping table file" }, { ERR_TDBUS1 + 3, " -ctab- do not use any character table file" }, { ERR_TDBUS1 + 4, " -i path add path to source file search list" }, { ERR_TDBUS1 + 5, " -m size maximum cache size (in bytes)" }, { ERR_TDBUS1 + 6, TDD_HEAPSIZ_MSG }, { ERR_TDBUS1 + 7, TDD_POOLSIZ_MSG }, { ERR_TDBUS1 + 8, TDD_STKSIZ_MSG }, { ERR_TDBUS1 + 9, " -r savefile restore saved game position from savefile" }, { ERR_TDBUS1 + 10," -s level set I/O safety level (-s? for help)" }, { ERR_TDBUS1 + 11, " -tf file use file for swapping (default: TADSSWAP.DAT)" }, { ERR_TDBUS1 + 12, " -ts size maximum swapfile size (default: unlimited)" }, { ERR_TDBUS1 + 13, #if OS_DEFAULT_SWAP_ENABLED " -t- disable swapping (enabled by default)" }, #else " -t+ enable swapping (disabled by default)" }, #endif { ERR_TDBUSL, TDD_UNDOSIZ_MSG }, /* DOS 16-bit TR supplemental usage messages */ { ERR_TRUS_DOS_1, " -plain use plain text console output (no BIOS calls)" }, /* DOS 32-bit TR32 supplemental usage messages */ { ERR_TRUS_DOS32_1, " -plain use plain text console output (no BIOS calls)" }, { ERR_TRUS_DOS32_1 + 1, " -kbfix95 use special Windows 95 keyboard handling (use only if" }, { ERR_TRUS_DOS32_1 + 2, " you have problems with keyboard layout switching)" }, { ERR_GNOFIL, "warning: graphics resource file \"%s\" not found" }, { ERR_GNORM, "can't find room \"%s\" (it's used in the resource file)" }, { ERR_GNOOBJ, "can't find object \"%s\" (it's used in a hot spot resource)" }, { ERR_GNOICN, "can't find object \"%s\" (it's used in an icon resource)"} }; void errmsg(errcxdef *ctx, char *outbuf, uint outbufl, uint err) { uint last; uint first; uint cur; VARUSED(outbufl); /* run a binary search for the indicated error */ first = 0; last = (sizeof(errlist) / sizeof(errlist[0])) - 1; for (;;) { if (first > last) { strcpy(outbuf, "unknown error"); return; } cur = first + (last - first)/2; if (errlist[cur].errmerr == err) { strcpy(outbuf, errlist[cur].errmtxt); return; } else if (errlist[cur].errmerr < err) first = (cur == first ? first + 1 : cur); else last = (cur == last ? last - 1 : cur); } } void errini(errcxdef *ctx, osfildef *fp) { VARUSED(ctx); VARUSED(fp); } #else /* ERR_LINK_MESSAGES */ void errmsg(errcxdef *ctx, char *outbuf, uint outbufl, uint err) { uint last; uint first; uint cur; errmfdef *errlist = ctx->errcxseek; int l; if (!errlist) { strcpy(outbuf, "unknown: no error message file"); return; } /* run a binary search for the indicated error */ first = 0; last = ctx->errcxsksz - 1; for (;;) { if (first > last) { strcpy(outbuf, "error not found"); return; } cur = first + (last - first)/2; if (errlist[cur].errmfnum == err) { osfseek(ctx->errcxfp, errlist[cur].errmfseek + ctx->errcxbase, OSFSK_SET); osfgets(outbuf, outbufl, ctx->errcxfp); l = strlen(outbuf); while (l && (outbuf[l-1] == '\n' || outbuf[l-1] == '\r')) --l; outbuf[l] = '\0'; return; } else if (errlist[cur].errmfnum < err) first = (cur == first ? first + 1 : cur); else last = (cur == last ? last - 1 : cur); } } void errini(errcxdef *ctx, osfildef *fp) { uint siz; uint i; uchar buf[6]; ctx->errcxseek = (errmfdef *)0; ctx->errcxsksz = 0; ctx->errcxbase = 0; if (!fp || ctx->errcxfp) return; /* read number of messages, and allocate space for descriptors */ (void)osfrb(fp, buf, 2); ctx->errcxsksz = osrp2(buf); siz = ctx->errcxsksz * sizeof(errmdef); ctx->errcxseek = (errmfdef *)mchalo(ctx, siz, "error messages"); for (i = 0; i < ctx->errcxsksz; ++i) { (void)osfrb(fp, buf, 6); ctx->errcxseek[i].errmfnum = osrp2(buf); ctx->errcxseek[i].errmfseek = osrp4(buf+2); } ctx->errcxfp = fp; } #endif /* ERR_LINK_MESSAGES */ #ifdef ERR_BUILD_FILE /* build error message file to named file or TADSERR.MSG in current dir */ int main(int argc, char *argv[]) { osfildef *fp; uint i; uint siz; ulong pos; char *nameout; uchar buf[6]; if (argc > 2) { printf("usage: tadserr [msg-file]\n"); os_term(1); } nameout = (argc == 2) ? argv[1] : "tadserr.msg"; fp = osfopwb(nameout, OSFTERRS); if (!fp) { printf("error: can't open %s\n", nameout); os_term(OSEXFAIL); } /* write number of messages */ siz = sizeof(errlist) / sizeof(errlist[0]); oswp2(buf, siz); (void)osfwb(fp, buf, 2); /* write message headers */ for (i = 0, pos = 2 + siz * 6 ; i < siz ; ++i) { oswp2(buf, errlist[i].errmerr); oswp4(buf+2, pos); (void)osfwb(fp, buf, 6); pos += strlen(errlist[i].errmtxt) + 1; } /* write messages */ for (i = 0 ; i < siz ; ++i) { uint msglen; /* only write the message if its length is non-zero */ msglen = strlen(errlist[i].errmtxt); if (msglen) (void)osfwb(fp, errlist[i].errmtxt, msglen); (void)osfwb(fp, "\n", 1); } osfcls(fp); return 0; } #endif /* ERR_BUILD_FILE */ #ifdef ERR_BUILD_DOC #include /* build error message file in current directory */ int main(int argc, char *argv[]) { osfildef *fp; uint i; uint siz; osfildef *fpin; char *namein; char buf[128]; char buf2[80]; unsigned inmsg; char *src; char *dst; int found_end = 0; long prvpos; int inquote; if (argc != 2) { printf("usage: tadsmdoc tex-file\n"); os_term(OSEXFAIL); } namein = argv[1]; rename(namein, "$DOC$.TMP"); if (!(fpin = osfoprt("$DOC$.TMP", OSFTTEXT))) printf("warning: old doc file not found - creating %s\n", namein); fp = osfopwt(namein, OSFTTEXT); if (!fp) { printf("error: can't create %s\n", namein); rename("$DOC$.TMP", namein); os_term(OSEXFAIL); } /* seek first message, copying original input file up to that point */ if (fpin != 0) { for (;;) { if (!fgets(buf, sizeof(buf), fpin)) { fpin = 0; break; } if (!memcmp(buf, "% message #", 11)) { inmsg = atoi(buf + 11); break; } else if (!memcmp(buf, "% end messages", 14)) { found_end = 1; break; } else fputs(buf, fp); } } /* write messages */ siz = sizeof(errlist) / sizeof(errlist[0]); for (i = 0 ; i < siz ; ++i) { if (errlist[i].errmerr == ERR_TCUS1) break; /* wait until we're at least at current message in input */ while (fpin != 0 && !found_end && inmsg < errlist[i].errmerr) { prvpos = ftell(fpin); if (!fgets(buf, sizeof(buf), fpin)) fpin = 0; else if (!memcmp(buf, "% message #", 11)) { inmsg = atoi(buf + 11); if (inmsg < errlist[i].errmerr) fputs(buf, fp); } else if (!memcmp(buf, "% end messages", 14)) found_end = 1; else fputs(buf, fp); } /* discard next line of input if it's the current message */ if (fpin != 0 && inmsg == errlist[i].errmerr) fgets(buf, sizeof(buf), fpin); /* add the current message to output file */ sprintf(buf2, "%% message #%d\n", errlist[i].errmerr); fputs(buf2, fp); sprintf(buf2, "\\tadsmessage{%d}{", errlist[i].errmerr); dst = buf2 + strlen(buf2); inquote = 0; for (src = errlist[i].errmtxt ; *src ; ++src) { if (*src == '%') { switch(*++src) { case 'd': memcpy(dst, "{\\it n}", 8); dst += 7; break; case 's': memcpy(dst, "{\\it xxx}", 10); dst += 9; break; case 't': memcpy(dst, "{\\it token}", 12); dst += 11; break; } } else if (*src == '"') { if (inquote) { *dst++ = '\''; *dst++ = '\''; } else { *dst++ = '`'; *dst++ = '`'; } inquote = !inquote; } else if (*src == '\'') { if (inquote || (src > errlist[i].errmtxt && isalpha(*(src-1)) && isalpha(*(src+1)))) { *dst++ = *src; inquote = 0; } else { *dst++ = '`'; inquote = 1; } } else if (*src == '#') { *dst++ = '\\'; *dst++ = *src; } else *dst++ = *src; } *dst++ = '}'; *dst++ = '\n'; *dst++ = '\0'; fputs(buf2, fp); /* go back to previous position if appropriate */ if (fpin != 0 && errlist[i].errmerr != inmsg) fseek(fpin, prvpos, 0); } /* copy the remainder up to the end of file marker */ while (fpin != 0 && !found_end) { if (!fgets(buf, sizeof(buf), fpin)) fpin = 0; else if (!memcmp(buf, "% end messages", 14)) found_end = 1; else fputs(buf, fp); } /* write out end of file marker, and copy remainder of input file */ fputs("% end messages\n", fp); while (fpin != 0) { if (!fgets(buf, sizeof(buf), fpin)) fpin = 0; else fputs(buf, fp); } osfcls(fp); if (fpin != 0) fclose(fpin); remove("$DOC$.TMP"); return 0; } #endif /* ERR_BUILD_DOC */ qtads-2.1.7/tads2/execmd.c000066400000000000000000003646321265017072300153040ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/EXECMD.C,v 1.5 1999/07/11 00:46:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1987, 1990 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name execmd - TADS Interpreter Execute user Command Function Executes a user command after it has been parsed Notes TADS 2.0 version This module contains the implementation of the entire "turn" sequence, which is: preCommand(actor, verb, dobj-list, prep, iobj) verb.verbAction(actor, do, prep, io) actor.actorAction( verb, do, prep, io ) actor.location.roomAction( actor, verb, do, prep, io ) if ( io ) { io.iobjCheck(actor, verb, dobj, prep) if (io does not define verIo directly) io.iobjGen(actor, verb, dobj, prep) do.dobjCheck(actor, verb, iobj, prep) if (do does not define do directly) do.dobjGen(actor, verb, iobj, prep) io.verIo( actor, do ) if ( noOutput ) { do.verDo( actor, io ) if ( noOutput ) io.io( actor, do ) } } else if ( do ) { do.dobjCheck(actor, verb, nil, nil) if (do does not define do directly) do.dobjGen(actor, verb, nil, nil) do.verDo( actor ) if ( noOutput )do.do( actor ) } else { verb.action( actor ) } postAction(actor, verb, dobj, prep, iobj, error_code) daemons fuses endCommand(actor, verb, dobj-list, prep, iobj, error_code) If an 'exit' or 'exitobj' is encountered, we skip straight to the daemons. If an abort is encountered, we skip to endCommand. If askio, or askdo is encountered, we skip everything remaining. Under any of these exit scenarios, we return success to our caller. This module also contains code to set and remove fuses and daemons, since they are part of the player turn sequence. Returns 0 for success, other for failure. Modified 03/25/92 MJRoberts - TADS 2.0 08/13/91 MJRoberts - add him/her support 11/30/90 MJRoberts - moved main execmd loop here from vocab, moved fuses/daemon stuff to fuses.c 04/23/90 MJRoberts - clear alarms (notify's) in clrdaemons() 07/07/89 MJRoberts - add fuse/daemon context value 06/28/89 MJRoberts - default message if objects don't handle the verb 11/06/88 MJRoberts - provide error messages in setfuse, setdaemon, etc. 11/06/88 MJRoberts - be careful not to send doX message on ask?o 11/05/88 MJRoberts - save tpldef with "again" 10/30/88 MJRoberts - new "version 6" parser interface 12/28/87 MJRoberts - created */ #include #include #include #include #include "os.h" #include "err.h" #include "voc.h" #include "tio.h" #include "mch.h" #include "mcm.h" #include "obj.h" #include "prp.h" #include "run.h" #include "lst.h" #include "bif.h" /* allocate and initialize a fuse/daemon/notifier array */ void vocinialo(voccxdef *ctx, vocddef **what, int cnt) { vocddef *p; *what = (vocddef *)mchalo(ctx->voccxerr, (cnt * sizeof(vocddef)), "vocinialo"); /* set all object/function entries to MCMONINV to indicate not-in-use */ for (p = *what ; cnt ; ++p, --cnt) p->vocdfn = MCMONINV; } /* internal service routine to clear one set of fuses/deamons/alerters */ static void vocdmn1clr(vocddef *dmn, uint cnt) { for ( ; cnt ; --cnt, ++dmn) dmn->vocdfn = MCMONINV; } /* delete all fuses/daemons/alerters */ void vocdmnclr(voccxdef *ctx) { vocdmn1clr(ctx->voccxfus, ctx->voccxfuc); vocdmn1clr(ctx->voccxdmn, ctx->voccxdmc); vocdmn1clr(ctx->voccxalm, ctx->voccxalc); } /* save undo information for a daemon/fuse/notifier */ static void vocdusav(voccxdef *ctx, vocddef *what) { uchar *p; objucxdef *uc = ctx->voccxundo; ushort siz = sizeof(what) + sizeof(*what) + 1; /* if we don't need to save undo, quit now */ if (uc == 0 || !objuok(uc)) return; /* reserve space for our record */ p = objures(uc, OBJUCLI, siz); /* set up our undo record */ *p = VOC_UNDO_DAEMON; memcpy(p + 1, &what, (size_t)sizeof(what)); memcpy(p + 1 + sizeof(what), what, (size_t)sizeof(*what)); uc->objucxhead += siz; } /* apply undo information for a daemon/fuse/notifier */ void vocdundo(void *ctx0, uchar *data) { voccxdef *ctx = (voccxdef *)ctx0; vocddef *daemon; objnum objn; ushort siz; ushort wrdsiz; uchar *p; int sccnt; objnum sc; int len1, len2; prpnum prp; int flags; uchar *wrd; switch(*data) { case VOC_UNDO_DAEMON: memcpy(&daemon, data + 1, (size_t)sizeof(daemon)); memcpy(daemon, data + 1 + sizeof(daemon), (size_t)sizeof(*daemon)); break; case VOC_UNDO_NEWOBJ: /* get the object number */ objn = osrp2(data + 1); /* delete the object's inheritance and vocabulary records */ vocdel(ctx, objn); vocidel(ctx, objn); /* delete the object */ mcmfre(ctx->voccxmem, (mcmon)objn); break; case VOC_UNDO_DELOBJ: /* get the object's number and size */ objn = osrp2(data + 1); siz = osrp2(data + 3); wrdsiz = osrp2(data + 5); /* allocate the object with its original number */ p = mcmalonum(ctx->voccxmem, siz, (mcmon)objn); /* copy the contents back to the object */ memcpy(p, data + 7, (size_t)siz); /* get its superclass if it has one */ sccnt = objnsc(p); if (sccnt) sc = osrp2(objsc(p)); /* unlock the object, and create its inheritance records */ mcmunlck(ctx->voccxmem, (mcmon)objn); vociadd(ctx, objn, MCMONINV, sccnt, &sc, VOCIFNEW | VOCIFVOC); /* restore the words as well */ data += 7 + siz; while (wrdsiz) { /* get the lengths from the buffer */ len1 = osrp2(data + 2); len2 = osrp2(data + 4); /* add the word */ vocadd2(ctx, data[0], objn, data[1], data+6, len1, data+6+len1, len2); /* remove this object from the word size */ wrdsiz -= 6 + len1 + len2; data += 6 + len1 + len2; } break; case VOC_UNDO_ADDVOC: case VOC_UNDO_DELVOC: flags = *(data + 1); prp = *(data + 2); objn = osrp2(data + 3); wrd = data + 5; if (*data == VOC_UNDO_ADDVOC) vocdel1(ctx, objn, (char *)wrd, prp, FALSE, FALSE, FALSE); else vocadd(ctx, prp, objn, flags, (char *)wrd); break; case VOC_UNDO_SETME: ctx->voccxme = osrp2(data + 1); break; } } /* determine size of one of our client undo records */ ushort OS_LOADDS vocdusz(void *ctx0, uchar *data) { VARUSED(ctx0); switch(*data) { case VOC_UNDO_DAEMON: /* it's the size of the structures, plus one for the header */ return (ushort)((sizeof(vocddef *) + sizeof(vocddef)) + 1); case VOC_UNDO_NEWOBJ: /* 2 bytes for the objnum plus 1 for the header */ return 2 + 1; case VOC_UNDO_DELOBJ: /* * 1 (header) + 2 (objnum) + 2 (size) + 2 (word size) + object * data size + word size */ return osrp2(data+3) + osrp2(data+5) + 1+2+2+2; case VOC_UNDO_ADDVOC: case VOC_UNDO_DELVOC: /* 1 (header) + 2 (objnum) + 1 (flags) + 1 (type) + word size */ return osrp2(data + 5) + 5; default: return 0; } } /* save undo for object creation */ void vocdusave_newobj(voccxdef *ctx, objnum objn) { objucxdef *uc = ctx->voccxundo; uchar *p; p = objures(uc, OBJUCLI, 3); *p = VOC_UNDO_NEWOBJ; oswp2(p+1, objn); uc->objucxhead += 3; } /* save undo information for a change in the "Me" object */ void vocdusave_me(voccxdef *ctx, objnum old_me) { uchar *p; objucxdef *uc = ctx->voccxundo; /* if we don't need to save undo, there's nothing to do */ if (uc == 0 || !objuok(uc)) return; /* reserve space for our record */ p = objures(uc, OBJUCLI, 3); *p = VOC_UNDO_SETME; oswp2(p+1, old_me); /* absorb the space */ uc->objucxhead += 3; } /* callback context structure */ struct delobj_cb_ctx { uchar *p; }; /* * Iteration callback to write vocabulary words for an object being * deleted to an undo stream, so that they can be restored if the * deletion is undone. */ static void delobj_cb(void *ctx0, vocdef *voc, vocwdef *vocw) { struct delobj_cb_ctx *ctx = (struct delobj_cb_ctx *)ctx0; uchar *p = ctx->p; /* write this object's header to the stream */ p[0] = vocw->vocwtyp; p[1] = vocw->vocwflg; oswp2(p+2, voc->voclen); oswp2(p+4, voc->vocln2); /* write the words as well */ memcpy(p+6, voc->voctxt, (size_t)(voc->voclen + voc->vocln2)); /* advance the pointer */ ctx->p += 6 + voc->voclen + voc->vocln2; } /* save undo for object deletion */ void vocdusave_delobj(voccxdef *ctx, objnum objn) { objucxdef *uc = ctx->voccxundo; uchar *p; uchar *objp; uint siz; int wrdsiz; int wrdcnt; struct delobj_cb_ctx fnctx; /* figure out how much we need to save */ objp = mcmlck(ctx->voccxmem, (mcmon)objn); siz = objfree(objp); /* figure the word size */ voc_count(ctx, objn, 0, &wrdcnt, &wrdsiz); /* * we need to store an additional 6 bytes (2-length1, 2-length2, * 1-type, 1-flags) for each word */ wrdsiz += wrdcnt*6; /* set up the undo header */ p = objures(uc, OBJUCLI, (ushort)(7 + siz + wrdsiz)); *p = VOC_UNDO_DELOBJ; oswp2(p+1, objn); oswp2(p+3, siz); oswp2(p+5, wrdsiz); /* save the object's data */ memcpy(p+7, objp, (size_t)siz); /* write the words */ fnctx.p = p+7 + siz; voc_iterate(ctx, objn, delobj_cb, &fnctx); /* unlock the object and advance the undo pointer */ mcmunlck(ctx->voccxmem, (mcmon)objn); uc->objucxhead += 7 + siz + wrdsiz; } /* save undo for word creation */ void vocdusave_addwrd(voccxdef *ctx, objnum objn, prpnum typ, int flags, char *wrd) { ushort wrdsiz; uchar *p; objucxdef *uc = ctx->voccxundo; /* figure out how much space we need, and reserve it */ wrdsiz = osrp2(wrd); p = objures(uc, OBJUCLI, (ushort)(5 + wrdsiz)); *p = VOC_UNDO_ADDVOC; *(p+1) = flags; *(p+2) = (uchar)typ; oswp2(p+3, objn); memcpy(p+5, wrd, (size_t)wrdsiz); uc->objucxhead += 5 + wrdsiz; } /* save undo for word deletion */ void vocdusave_delwrd(voccxdef *ctx, objnum objn, prpnum typ, int flags, char *wrd) { ushort wrdsiz; uchar *p; objucxdef *uc = ctx->voccxundo; /* figure out how much space we need, and reserve it */ wrdsiz = osrp2(wrd); p = objures(uc, OBJUCLI, (ushort)(5 + wrdsiz)); *p = VOC_UNDO_DELVOC; *(p+1) = flags; *(p+2) = (uchar)typ; oswp2(p+3, objn); memcpy(p+5, wrd, (size_t)wrdsiz); uc->objucxhead += 5 + wrdsiz; } /* set a fuse/daemon/notifier */ void vocsetfd(voccxdef *ctx, vocddef *what, objnum func, prpnum prop, uint tm, runsdef *val, int err) { int slots; if (what == ctx->voccxdmn) slots = ctx->voccxdmc; else if (what == ctx->voccxalm) slots = ctx->voccxalc; else if (what == ctx->voccxfus) slots = ctx->voccxfuc; else errsig(ctx->voccxerr, ERR_BADSETF); /* find a free slot, and set up our fuse/daemon */ for ( ; slots ; ++what, --slots) { if (what->vocdfn == MCMONINV) { /* save an undo record for this slot before changing */ vocdusav(ctx, what); /* record the information */ what->vocdfn = func; if (val != 0) OSCPYSTRUCT(what->vocdarg, *val); what->vocdprp = prop; what->vocdtim = tm; /* * the fuse/notifier/daemon is set - no need to look further * for an open slot */ return; } } /* we didn't find an open slot - signal the appropriate error */ errsig(ctx->voccxerr, err); } /* remove a fuse/daemon/notifier */ void vocremfd(voccxdef *ctx, vocddef *what, objnum func, prpnum prop, runsdef *val, int err) { int slots; if (what == ctx->voccxdmn) slots = ctx->voccxdmc; else if (what == ctx->voccxalm) slots = ctx->voccxalc; else if (what == ctx->voccxfus) slots = ctx->voccxfuc; else errsig(ctx->voccxerr, ERR_BADREMF); /* find the slot with this same fuse/daemon/notifier, and remove it */ for ( ; slots ; ++what, --slots) { if (what->vocdfn == func && what->vocdprp == prop && (!val || (val->runstyp == what->vocdarg.runstyp && !memcmp(&val->runsv, &what->vocdarg.runsv, (size_t)datsiz(val->runstyp, &val->runsv))))) { /* save an undo record for this slot before changing */ vocdusav(ctx, what); what->vocdfn = MCMONINV; return; } } /* errsig(ctx->voccxerr, err); <<>> */ } /* * Count one or more turns - burn all fuses down by the given number of * turns. Execute any fuses that expire within the given interval, but * not any that expire at the end of the last turn counted here. (If * incrementing by one turn only, no fuses will be executed.) If the * do_fuses flag is false, fuses are simply deleted if they burn down * within the interval. */ void vocturn(voccxdef *ctx, int turncnt, int do_fuses) { vocddef *p; int i; int do_exe; while (turncnt--) { /* presume we won't find anything to execute */ do_exe = FALSE; /* go through notifiers, looking for fuse-type notifiers */ for (i = ctx->voccxalc, p = ctx->voccxalm ; i ; ++p, --i) { if (p->vocdfn != MCMONINV && p->vocdtim != VOCDTIM_EACH_TURN && p->vocdtim != 0) { /* save an undo record for this slot before changing */ vocdusav(ctx, p); if (--(p->vocdtim) == 0) do_exe = TRUE; } } /* now go through the fuses */ for (i = ctx->voccxfuc, p = ctx->voccxfus ; i ; ++p, --i) { if (p->vocdfn != MCMONINV && p->vocdtim != 0) { /* save an undo record for this slot before changing */ vocdusav(ctx, p); if (--(p->vocdtim) == 0) do_exe = TRUE; } } /* * if we'll be doing more, and anything burned down, run * current fuses before going on to the next turn */ if ((!do_fuses || turncnt) && do_exe) exefuse(ctx, do_fuses); } } /* * display a default error message for a verb/dobj/iobj combo. * The message is "I don't know how to " if * the dobj is present, and "I don't know how to anything * " if the iobj is present. Such a message * is displayed when the objects in the command don't handle the verb * (i.e., don't have any methods for verification of the verb: they * lack verDo or verIo). */ static void exeperr(voccxdef *ctx, objnum verb, objnum dobj, objnum prep, objnum iobj) { if (ctx->voccxper2 != MCMONINV) { runpobj(ctx->voccxrun, iobj); runpobj(ctx->voccxrun, prep); runpobj(ctx->voccxrun, dobj); runpobj(ctx->voccxrun, verb); runfn(ctx->voccxrun, ctx->voccxper2, 4); return; } vocerr(ctx, VOCERR(110), "I don't know how to "); runppr(ctx->voccxrun, verb, PRP_SDESC, 0); if (dobj != MCMONINV) { vocerr(ctx, VOCERR(111), " "); runppr(ctx->voccxrun, dobj, PRP_THEDESC, 0); } else { vocerr(ctx, VOCERR(112), " anything "); if (prep != MCMONINV) runppr(ctx->voccxrun, prep, PRP_SDESC, 0); else vocerr(ctx, VOCERR(113), "to"); vocerr(ctx, VOCERR(114), " "); runppr(ctx->voccxrun, iobj, PRP_THEDESC, 0); } vocerr(ctx, VOCERR(115), "."); } /* * Execute daemons */ void exedaem(voccxdef *ctx) { runcxdef *rcx = ctx->voccxrun; vocddef *daemon; int i; runsdef val; int err; for (i = ctx->voccxdmc, daemon = ctx->voccxdmn ; i ; ++daemon, --i) { if (daemon->vocdfn != MCMONINV) { objnum thisd = daemon->vocdfn; ERRBEGIN(ctx->voccxerr) OSCPYSTRUCT(val, daemon->vocdarg); runpush(rcx, val.runstyp, &val); runfn(rcx, thisd, 1); ERRCATCH(ctx->voccxerr, err) if (err != ERR_RUNEXIT && err != ERR_RUNEXITOBJ) errrse(ctx->voccxerr); ERREND(ctx->voccxerr) } } for (i = ctx->voccxalc, daemon = ctx->voccxalm ; i ; ++daemon, --i) { if (daemon->vocdfn != MCMONINV && daemon->vocdtim == VOCDTIM_EACH_TURN) { ERRBEGIN(ctx->voccxerr) runppr(rcx, daemon->vocdfn, daemon->vocdprp, 0); ERRCATCH(ctx->voccxerr, err) if (err != ERR_RUNEXIT && err != ERR_RUNEXITOBJ) errrse(ctx->voccxerr); ERREND(ctx->voccxerr) } } } /* * Execute any pending fuses. Return TRUE if any fuses were executed, * FALSE otherwise. */ int exefuse(voccxdef *ctx, int do_run) { runcxdef *rcx = ctx->voccxrun; vocddef *daemon; int i; int found = FALSE; runsdef val; int err; /* first, execute any expired function-based fuses */ for (i = ctx->voccxfuc, daemon = ctx->voccxfus ; i ; ++daemon, --i) { if (daemon->vocdfn != MCMONINV && daemon->vocdtim == 0) { objnum thisf = daemon->vocdfn; found = TRUE; ERRBEGIN(ctx->voccxerr) /* save an undo record for this slot before changing */ vocdusav(ctx, daemon); /* remove the fuse prior to running */ daemon->vocdfn = MCMONINV; if (do_run) { OSCPYSTRUCT(val, daemon->vocdarg); runpush(rcx, val.runstyp, &val); runfn(rcx, thisf, 1); } ERRCATCH(ctx->voccxerr, err) if (err != ERR_RUNEXIT && err != ERR_RUNEXITOBJ) errrse(ctx->voccxerr); ERREND(ctx->voccxerr) } } /* next, execute any expired method-based notifier fuses */ for (i = ctx->voccxalc, daemon = ctx->voccxalm ; i ; ++daemon, --i) { if (daemon->vocdfn != MCMONINV && daemon->vocdtim == 0) { objnum thisa = daemon->vocdfn; found = TRUE; ERRBEGIN(ctx->voccxerr) /* save an undo record for this slot before changing */ vocdusav(ctx, daemon); /* delete it prior to running it */ daemon->vocdfn = MCMONINV; if (do_run) runppr(rcx, thisa, daemon->vocdprp, 0); ERRCATCH(ctx->voccxerr, err) if (err != ERR_RUNEXIT && err != ERR_RUNEXITOBJ) errrse(ctx->voccxerr); ERREND(ctx->voccxerr) } } /* return true if we found any expired fuses */ return found; } /* ------------------------------------------------------------------------ */ /* * Find the action routine template for a verb. Fills in *tplofs with * the offset of the template property within the verb object, and fills * in actofs with the offset of the "action" property within the verb * object. Sets *tplofs to zero if there's no template, and sets * *actofs to zero if there's no action routine. */ static void exe_get_tpl(voccxdef *ctx, objnum verb, uint *tplofs, uint *actofs) { /* look up the new-style template first */ *tplofs = objgetap(ctx->voccxmem, verb, PRP_TPL2, (objnum *)0, FALSE); /* if there's no new-style template, look up the old-style template */ if (*tplofs == 0) *tplofs = objgetap(ctx->voccxmem, verb, PRP_TPL, (objnum *)0, FALSE); /* also look to see if the verb has an Action method */ *actofs = objgetap(ctx->voccxmem, verb, PRP_ACTION, (objnum *)0, FALSE); } /* ------------------------------------------------------------------------ */ /* * Execute fuses and daemons. Returns zero on success, or ERR_ABORT if * 'abort' was thrown during execution. */ int exe_fuses_and_daemons(voccxdef *ctx, int err, int do_fuses, objnum actor, objnum verb, vocoldef *dobj_list, int dobj_cnt, objnum prep, objnum iobj) { int err2; /* presume no error */ err2 = 0; /* execute fuses and daemons if desired - trap any errors that occur */ if (do_fuses) { ERRBEGIN(ctx->voccxerr) { /* execute daemons */ exedaem(ctx); /* execute fuses */ (void)exefuse(ctx, TRUE); } ERRCATCH(ctx->voccxerr, err2) { /* * if 'abort' was invoked, ignore it, since it's now had the * desired effect of skipping any remaining fuses and * daemons; resignal any other error */ if (err2 != ERR_RUNABRT) errrse(ctx->voccxerr); /* replace any previous error with the new error code */ err = err2; } ERREND(ctx->voccxerr); } /* execute endCommand if it's defined */ if (ctx->voccxendcmd != MCMONINV) { /* push the arguments */ runpnum(ctx->voccxrun, err); runpobj(ctx->voccxrun, iobj); runpobj(ctx->voccxrun, prep); voc_push_vocoldef_list(ctx, dobj_list, dobj_cnt); runpobj(ctx->voccxrun, verb); runpobj(ctx->voccxrun, actor); /* call endCommand */ runfn(ctx->voccxrun, ctx->voccxendcmd, 6); } /* return the error status */ return err; } /* ------------------------------------------------------------------------ */ /* * execute iobjGen/dobjGen methods, if appropriate */ static int exegen(voccxdef *ctx, objnum obj, prpnum genprop, prpnum verprop, prpnum actprop) { int hasgen; /* has xobjGen property */ objnum genobj; /* object with xobjGen property */ int hasver; /* has verXoVerb property */ objnum verobj; /* object with verXoVerb property */ int hasact; /* has xoVerb property */ objnum actobj; /* object with xoVerb property */ /* ignore it if there's no object here */ if (obj == MCMONINV) return(FALSE); /* look up the xobjGen property, and ignore if not present */ hasgen = objgetap(ctx->voccxmem, obj, genprop, &genobj, FALSE); if (!hasgen) return(FALSE); /* look up the verXoVerb and xoVerb properties */ hasver = objgetap(ctx->voccxmem, obj, verprop, &verobj, FALSE); hasact = objgetap(ctx->voccxmem, obj, actprop, &actobj, FALSE); /* ignore if verXoVerb or xoVerb "overrides" xobjGen */ if ((hasver && !bifinh(ctx, vocinh(ctx, genobj), verobj)) || (hasact && !bifinh(ctx, vocinh(ctx, genobj), actobj))) return FALSE; /* all conditions are met - execute dobjGen */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Save "again" information for a direct or indirect object */ static void exe_save_again_obj(vocoldef *againv, const vocoldef *objv, char **bufp) { /* if there's an object, save it */ if (objv != 0) { /* copy the object information structure */ memcpy(againv, objv, sizeof(*againv)); /* copy the original command words to the "again" buffer */ if (objv->vocolfst != 0 && objv->vocollst != 0) { size_t copylen; /* * Compute the length of the entire list. The words are * arranged consecutively in the buffer, separated by null * bytes, so we must copy everything from the first word to * the start of the last word, plus the length of the last * word, plus the last word's trailing null byte. */ copylen = objv->vocollst - objv->vocolfst + strlen(objv->vocollst) + 1; /* copy the text */ memcpy(*bufp, objv->vocolfst, copylen); /* * set the new structure to point into the copy, not the * original */ againv->vocolfst = *bufp; againv->vocollst = *bufp + (objv->vocollst - objv->vocolfst); /* skip past the space we've consumed in the buffer */ *bufp += copylen; } } else { /* there's nothing to save - just set the object ID to invalid */ againv->vocolobj = MCMONINV; } } /* * Restore an "again" object previously saved. Note that we must copy * the saved data to our 2-element arrays so that we can insert a * terminating element after each restored element - other code * occasionally expects these structures to be stored in the standard * object list array format. Returns a pointer to the restored object * list, which is the same as the first argument. */ static vocoldef *exe_restore_again_obj(vocoldef again_array[2], const vocoldef *saved_obj) { /* copy the saved object into the first array element */ memcpy(&again_array[0], saved_obj, sizeof(again_array[0])); /* clear the second element to indicate the end of the object list */ again_array[1].vocolobj = MCMONINV; again_array[1].vocolflg = 0; /* return a pointer to the first array element */ return &again_array[0]; } /* ------------------------------------------------------------------------ */ /* * Execute a single command. 'recursive' indicates whether the routine * is being called for normal command processing or as a recursive call * from within the game; if this flag is true, we'll bypass certain * operations that are only appropriate for normal direct player * commands: we won't remember the command for "again" processing, we * won't do end-of-turn processing, and we won't reset the system stack * before each function invocation. */ static int exe1cmd(voccxdef *ctx, objnum actor, objnum verb, vocoldef *dobjv, objnum *prepptr, vocoldef *iobjv, int endturn, uchar *tpl, int newstyle, int recursive, int validate_dobj, int validate_iobj, vocoldef *dobj_list, int cur_dobj_idx, int dobj_cnt, int show_multi_prefix, int multi_flags) { objnum loc; int err; runcxdef *rcx = ctx->voccxrun; objnum prep = *prepptr; objnum dobj = (dobjv != 0 ? dobjv->vocolobj : MCMONINV); objnum iobj = (iobjv != 0 ? iobjv->vocolobj : MCMONINV); int tplflags; int dobj_first; objnum old_tio_actor; vocoldef *old_ctx_dobj; vocoldef *old_ctx_iobj; objnum old_verb; objnum old_actor; objnum old_prep; int do_fuses; int do_postact; vocoldef again_dobj[2]; vocoldef again_iobj[2]; /* presume no error will occur */ err = 0; /* * Presume we'll run fuses and daemons if this is the end of the * turn. We only do fuses and daemons once per command, even if the * command contains multiple objects; 'endturn' will be true only * when this is the last object of the command. */ do_fuses = endturn; /* presume we will call postAction */ do_postact = TRUE; /* remember the original tio-level actor setting */ old_tio_actor = tiogetactor(ctx->voccxtio); /* remember the original command settings (in case this is recursive) */ old_actor = ctx->voccxactor; old_verb = ctx->voccxverb; old_prep = ctx->voccxprep; old_ctx_dobj = ctx->voccxdobj; old_ctx_iobj = ctx->voccxiobj; /* the default actor is Me */ if (actor == MCMONINV) actor = ctx->voccxme; /* if command is "again", get information from previous command */ if (verb == ctx->voccxvag) { /* it's "again" - repeat the last command */ actor = ctx->voccxlsa; verb = ctx->voccxlsv; dobj = ctx->voccxlsd.vocolobj; iobj = ctx->voccxlsi.vocolobj; prep = ctx->voccxlsp; tpl = ctx->voccxlst; newstyle = ctx->voccxlssty; /* * If we have a direct or indirect object, restore the full * object information structure pointers (in particular, this * restores the word lists). */ if (dobj != MCMONINV) dobjv = exe_restore_again_obj(again_dobj, &ctx->voccxlsd); if (iobj != MCMONINV) iobjv = exe_restore_again_obj(again_iobj, &ctx->voccxlsi); /* * make sure the command is repeatable: there must have been a * verb, and the objects specified must still be accessible */ if (verb == MCMONINV) { /* * if the last command was lost due to an object deletion, * show the message "you can't repeat that command"; * otherwise, show the message "there's no command to * repeat" */ if ((ctx->voccxflg & VOCCXAGAINDEL) != 0) vocerr(ctx, VOCERR(27), "You can't repeat that command."); else vocerr(ctx, VOCERR(26), "There's no command to repeat."); /* flush the output and return failure */ tioflush(ctx->voccxtio); return 0; } else if ((dobj != MCMONINV && !vocchkaccess(ctx, dobj, PRP_VALIDDO, 0, actor, verb)) || (iobj != MCMONINV && !vocchkaccess(ctx, iobj, PRP_VALIDIO, 0, actor, verb)) || !vocchkaccess(ctx, actor, PRP_VALIDACTOR, 0, actor, verb)) { vocerr(ctx, VOCERR(27), "You can't repeat that command."); tioflush(ctx->voccxtio); return(0); } } else { /* verify the direct object if present */ if (validate_dobj && dobj != MCMONINV && !vocchkaccess(ctx, dobj, PRP_VALIDDO, 0, actor, verb)) { /* generate an appropriate message */ if (vocchkvis(ctx, dobj, actor)) { /* it's visible but not accessible */ vocnoreach(ctx, &dobj, 1, actor, verb, prep, PRP_DODEFAULT, FALSE, 0, 0, 1); } else { /* it's not even visible */ if (recursive) vocerr(ctx, VOCERR(39), "You don't see that here."); else vocerr(ctx, VOCERR(38), "You don't see that here any more."); } /* indicate the error */ return ERR_PRS_VAL_DO_FAIL; } /* verify the indirect object if present */ if (validate_iobj && iobj != MCMONINV && !vocchkaccess(ctx, iobj, PRP_VALIDIO, 0, actor, verb)) { /* generate the error message */ if (vocchkvis(ctx, iobj, actor)) { /* it's visible but not accessible */ vocnoreach(ctx, &iobj, 1, actor, verb, prep, PRP_IODEFAULT, FALSE, 0, 0, 1); } else { /* it's not even visible */ if (recursive) vocerr(ctx, VOCERR(39), "You don't see that here."); else vocerr(ctx, VOCERR(38), "You don't see that here any more."); } /* indicate the error */ return ERR_PRS_VAL_IO_FAIL; } /* * save the command, unless this is a recursive call from the * game, so that we can repeat this command if the next is * "again" */ if (!recursive) { char *dst; /* save the command parameters */ ctx->voccxlsa = actor; ctx->voccxlsv = verb; ctx->voccxlsp = prep; ctx->voccxlssty = newstyle; if (tpl != 0) memcpy(ctx->voccxlst, tpl, (size_t)(newstyle ? VOCTPL2SIZ : VOCTPLSIZ)); /* set up to write into the "again" word buffer */ dst = ctx->voccxagainbuf; /* save the direct object information */ exe_save_again_obj(&ctx->voccxlsd, dobjv, &dst); /* save the indirect object information */ exe_save_again_obj(&ctx->voccxlsi, iobjv, &dst); /* * clear the flag indicating that "again" was lost due to * object deletion, because we obviously have a valid * "again" at this point */ ctx->voccxflg &= ~VOCCXAGAINDEL; } } /* remember the flags */ tplflags = (tpl != 0 && newstyle ? voctplflg(tpl) : 0); dobj_first = (tplflags & VOCTPLFLG_DOBJ_FIRST); /* set up actor for tio subsystem - format strings need to know */ tiosetactor(ctx->voccxtio, actor); /* store current dobj and iobj vocoldef's for later reference */ ctx->voccxdobj = dobjv; ctx->voccxiobj = iobjv; /* store the rest of the current command objects for reference */ ctx->voccxactor = actor; ctx->voccxverb = verb; ctx->voccxprep = prep; ERRBEGIN(ctx->voccxerr) /* reset the run-time context if this is a top-level call */ if (!recursive) runrst(rcx); /* * if this is the first object, invoke the game's preCommand * function, passing the list of all of the direct objects */ if (cur_dobj_idx == 0 && ctx->voccxprecmd != MCMONINV) { /* push the arguments: actor, verb, dobj-list, prep, iobj */ runpobj(rcx, iobj); runpobj(rcx, prep); voc_push_vocoldef_list(ctx, dobj_list, dobj_cnt); runpobj(rcx, verb); runpobj(rcx, actor); /* catch errors specially for preCommand */ ERRBEGIN(ctx->voccxerr) { /* invoke preCommand */ runfn(rcx, ctx->voccxprecmd, 5); } ERRCATCH(ctx->voccxerr, err) { /* * if the error was 'exit', translate it to EXITPRECMD so * that we handle the outer loop correctly (exiting from * preCommand skips execution for all subsequent objects, * but doesn't skip fuses and daemons) */ if (err == ERR_RUNEXIT) errsig(ctx->voccxerr, ERR_RUNEXITPRECMD); /* no special handling - just resignal the error */ errrse(ctx->voccxerr); } ERREND(ctx->voccxerr); } /* show the pre-object prefix if the caller instructed us to do so */ voc_multi_prefix(ctx, dobj, show_multi_prefix, multi_flags, cur_dobj_idx, dobj_cnt); /* * check to see if the verb has verbAction defined - if so, invoke * the method */ if (objgetap(ctx->voccxmem, verb, PRP_VERBACTION, (objnum *)0, FALSE)) { /* call verb.verbAction(actor, dobj, prep, iobj) */ runpobj(rcx, iobj); runpobj(rcx, prep); runpobj(rcx, dobj); runpobj(rcx, actor); runppr(rcx, verb, PRP_VERBACTION, 4); } /* invoke cmdActor.actorAction(verb, dobj, prep, iobj) */ runpobj(rcx, iobj); runpobj(rcx, prep); runpobj(rcx, dobj); runpobj(rcx, verb); runppr(rcx, actor, PRP_ACTORACTION, 4); /* reset the run-time context if this is a top-level call */ if (!recursive) runrst(rcx); /* invoke actor.location.roomAction(actor, verb, dobj, prep, iobj) */ runppr(rcx, actor, PRP_LOCATION, 0); if (runtostyp(rcx) == DAT_OBJECT) { loc = runpopobj(rcx); /* reset the run-time context if this is a top-level call */ if (!recursive) runrst(rcx); /* call roomAction */ runpobj(rcx, iobj); runpobj(rcx, prep); runpobj(rcx, dobj); runpobj(rcx, verb); runpobj(rcx, actor); runppr(rcx, loc, PRP_ROOMACTION, 5); } else { /* the location isn't an object, so discard it */ rundisc(rcx); } /* if there's an indirect object, execute iobjCheck */ if (iobj != MCMONINV) { /* reset the run-time context if this is a top-level call */ if (!recursive) runrst(rcx); /* invoke iobjCheck */ runpobj(rcx, prep); runpobj(rcx, dobj); runpobj(rcx, verb); runpobj(rcx, actor); runppr(rcx, iobj, PRP_IOBJCHECK, 4); } /* * If there's an indirect object, and the indirect object doesn't * directly define io, call iobj.iobjGen(actor, verb, dobj, * prep) */ if (iobj != MCMONINV && exegen(ctx, iobj, PRP_IOBJGEN, voctplvi(tpl), voctplio(tpl))) { /* reset the run-time context if this is a top-level call */ if (!recursive) runrst(rcx); /* invoke iobjGen */ runpobj(rcx, prep); runpobj(rcx, dobj); runpobj(rcx, verb); runpobj(rcx, actor); runppr(rcx, iobj, PRP_IOBJGEN, 4); } /* if there's an direct object, execute dobjCheck */ if (dobj != MCMONINV) { /* reset the run-time context if this is a top-level call */ if (!recursive) runrst(rcx); /* invoke dobjCheck */ runpobj(rcx, prep); runpobj(rcx, iobj); runpobj(rcx, verb); runpobj(rcx, actor); runppr(rcx, dobj, PRP_DOBJCHECK, 4); } /* * If there's a direct object, and the direct object doesn't * directly define do, call dobj.dobjGen(actor, verb, iobj, * prep) */ if (dobj != MCMONINV && exegen(ctx, dobj, PRP_DOBJGEN, voctplvd(tpl), voctpldo(tpl))) { /* reset the run-time context if this is a top-level call */ if (!recursive) runrst(rcx); /* invoke dobjGen */ runpobj(rcx, prep); runpobj(rcx, iobj); runpobj(rcx, verb); runpobj(rcx, actor); runppr(rcx, dobj, PRP_DOBJGEN, 4); } /* reset the hidden-text flag */ tiohide(ctx->voccxtio); tioshow(ctx->voccxtio); /* * Now do what needs to be done, depending on the sentence structure: * * No objects ==> cmdVerb.action( cmdActor ) * * Direct object only ==> cmdDobj.verDo( actor ) *. cmdDobj.do( actor ) * * Indirect + direct ==> cmdDobj.verDo( actor, cmdIobj ) *. cmdIobj.verIo( actor, cmdDobj ) *. cmdIobj.io( actor, cmdDobj ) */ if (dobj == MCMONINV) { /* reset the stack for top-level calls */ if (!recursive) runrst(rcx); /* invoke verb.action */ runpobj(rcx, actor); runppr(rcx, verb, PRP_ACTION, 1); } else if (iobj == MCMONINV) { if (!objgetap(ctx->voccxmem, dobj, voctplvd(tpl), (objnum *)0, FALSE)) { /* display the error */ exeperr(ctx, verb, dobj, MCMONINV, MCMONINV); /* note that verDoVerb failed */ err = ERR_PRS_NO_VERDO; /* we're done with this command */ goto skipToFuses; } /* reset the stack for top-level calls */ if (!recursive) runrst(rcx); /* invoke dobj.verDoVerb */ runpobj(rcx, actor); runppr(rcx, dobj, voctplvd(tpl), 1); /* check for an error message from verDoVerb */ if (!tioshow(ctx->voccxtio)) { /* reset the stack for top-level calls */ if (!recursive) runrst(rcx); /* dobj.verDoVerb displayed no output - process dobj.doVerb */ runpobj(rcx, actor); runppr(rcx, dobj, voctpldo(tpl), 1); } else { /* note that verDoVerb failed */ err = ERR_PRS_VERDO_FAIL; } } else { /* check to see if the verDoVerb and verIoVerb methods exist */ if (!objgetap(ctx->voccxmem, dobj, voctplvd(tpl), (objnum *)0, FALSE)) { /* no verDoVerb method - show a default message */ exeperr(ctx, verb, dobj, MCMONINV, MCMONINV); /* note the error */ err = ERR_PRS_NO_VERDO; /* skip to the end of the turn */ goto skipToFuses; } else if (!objgetap(ctx->voccxmem, iobj, voctplvi(tpl), (objnum *)0, FALSE)) { /* no verIoVerb method - show a default mesage */ exeperr(ctx, verb, MCMONINV, prep, iobj); /* note the error */ err = ERR_PRS_NO_VERIO; /* skip to the end of the turn */ goto skipToFuses; } /* reset the stack for top-level calls */ if (!recursive) runrst(rcx); /* call verDoVerb(actor [,iobj]) */ if (!dobj_first) runpobj(rcx, iobj); runpobj(rcx, actor); runppr(rcx, dobj, voctplvd(tpl), (dobj_first ? 1 : 2)); /* check for error output from verDoVerb */ if (!tioshow(ctx->voccxtio)) { /* reset the stack for top-level calls */ if (!recursive) runrst(rcx); /* no error from verDoVerb - call verIoVerb(actor [,dobj]) */ if (dobj_first) runpobj(rcx, dobj); runpobj(rcx, actor); runppr(rcx, iobj, voctplvi(tpl), (dobj_first ? 2 : 1)); /* check for error output from verIoVerb */ if (!tioshow(ctx->voccxtio)) { /* reset the stack for top-level calls */ if (!recursive) runrst(rcx); /* no error from verDoVerb or verIoVerb - call ioVerb */ runpobj(rcx, dobj); runpobj(rcx, actor); runppr(rcx, iobj, voctplio(tpl), 2); } else { /* note the error */ err = ERR_PRS_VERIO_FAIL; } } else { /* note the error */ err = ERR_PRS_VERDO_FAIL; } } skipToFuses: ERRCATCH(ctx->voccxerr, err) { /* if askIo was invoked, get the preposition from the error stack */ if (err == ERR_RUNASKI) *prepptr = errargint(0); /* * If we executed 'abort', we'll skip straight to endCommand. * * If we executed askDo or askIo, we won't execute anything * more, because the command is being interrupted. * * If 'exit' or 'exitobj' was executed, proceed through * postAction and subsequent steps. * * If any error occurred other than 'exit' or 'exitobj' being * invoked, resignal the error. * * We don't need to do anything more at this point if 'exit' was * invoked, because 'exit' merely skips to the end-of-turn * phase, which is where we'll go next from here. * * If 'exitobj' was invoked, we don't want to return an error at * all, since we just want to skip the remainder of the normal * processing for the current object and proceed to the next * object (in a command with multiple direct objects). */ if (err == ERR_RUNABRT) { /* * aborting - we're going to call postAction, but we're not * going to execute fuses and daemons */ do_fuses = FALSE; endturn = TRUE; } else if (err == ERR_RUNASKD || err == ERR_RUNASKI) { /* we're going to skip all end-of-turn action */ do_fuses = FALSE; do_postact = FALSE; endturn = FALSE; } else if (err == ERR_RUNEXIT) { /* * Proceed with the remainder of the processing for this * turn, but retain the error code to return to our caller, * so they know that the rest of the turn is to be skipped. * * In addition, set 'do_fuses' to true, since we want to go * directly to the fuse and daemon processing for this turn, * regardless of whether any other objects are present * (because we'll skip any remaining objects). */ endturn = TRUE; do_fuses = TRUE; } else if (err == ERR_RUNEXITPRECMD) { /* * exited from preCommand - end the turn, but do not run the * postAction routine */ do_fuses = TRUE; do_postact = FALSE; endturn = TRUE; } else if (err == ERR_RUNEXITOBJ) { /* * Proceed with the remainder of processing for this turn - * we want to proceed to the next object, if any, and * process it as normal. We don't need to update 'endturn' * or 'do_fuses', since we want to do all of those in the * normal fashion. */ } else { /* * We can't handle any other errors. Restore the enclosing * command context, and resignal the error. */ /* restore the previous tio actor setting */ tiosetactor(ctx->voccxtio, old_tio_actor); /* restore the original context iobj and dobj settings */ ctx->voccxdobj = old_ctx_dobj; ctx->voccxiobj = old_ctx_iobj; /* restore the original context command objects */ ctx->voccxactor = old_actor; ctx->voccxverb = old_verb; ctx->voccxprep = old_prep; /* resignal the error */ errrse(ctx->voccxerr); } } ERREND(ctx->voccxerr); /* * If desired, call postAction(actor, verb, dobj, prep, iobj, * error_status). */ if (do_postact && ctx->voccxpostact != MCMONINV) { int err2; ERRBEGIN(ctx->voccxerr) { /* push the arguments */ runpnum(rcx, err); runpobj(rcx, iobj); runpobj(rcx, prep); runpobj(rcx, dobj); runpobj(rcx, verb); runpobj(rcx, actor); /* invoke postAction */ runfn(rcx, ctx->voccxpostact, 6); } ERRCATCH(ctx->voccxerr, err2) { /* remember the new error condition */ err = err2; /* if we're aborting, skip fuses and daemons */ if (err == ERR_RUNABRT) { endturn = TRUE; do_fuses = FALSE; } } ERREND(ctx->voccxerr); } /* restore the original context iobj and dobj settings */ ctx->voccxdobj = old_ctx_dobj; ctx->voccxiobj = old_ctx_iobj; /* restore the original context command objects */ ctx->voccxverb = old_verb; ctx->voccxprep = old_prep; /* reset the stack for top-level calls */ if (!recursive) runrst(rcx); /* * If this is the end of the turn, execute fuses and daemons. Skip * fuses on recursive calls, since we want to count them as part of * the enclosing turn. */ if (endturn && !recursive) { /* catch errors so that we can restore the actor globals */ ERRBEGIN(ctx->voccxerr) { /* run fuses, daemons, and endCommand */ err = exe_fuses_and_daemons(ctx, err, do_fuses, actor, verb, dobj_list, dobj_cnt, prep, iobj); } ERRCLEAN(ctx->voccxerr) { /* restore the previous actor globals */ ctx->voccxactor = old_actor; tiosetactor(ctx->voccxtio, old_tio_actor); } ERRENDCLN(ctx->voccxerr); } /* restore the previous actor globals */ ctx->voccxactor = old_actor; tiosetactor(ctx->voccxtio, old_tio_actor); /* success */ return err; } /* * saveit stores the current direct object list in 'it' or 'them'. */ static void exesaveit(voccxdef *ctx, vocoldef *dolist) { int cnt; int i; int dbg = ctx->voccxflg & VOCCXFDBG; runcxdef *rcx = ctx->voccxrun; cnt = voclistlen(dolist); if (cnt == 1) { /* * check to make sure they're not referring to a number or a * string; if so, it doesn't make any sense to save it */ if (dolist[0].vocolflg == VOCS_STR || dolist[0].vocolflg == VOCS_NUM) { /* * As of 2.5.11, don't clear 'it' on a number or string; * rather, just leave it as it was from the prior command. * Players will almost never expect a number or string to have * anything to do with pronoun antecedents, and in fact some * players reported finding it confusing to have the antecedant * implied by the second-most-recent command disappear when the * most recent command used a number of string. */ #if 0 /* save a nil 'it' */ ctx->voccxit = MCMONINV; if (dbg) tioputs(ctx->voccxtio, ".. setting 'it' to nil (strObj/numObj)\\n"); #endif /* we're done */ return; } /* save 'it' */ ctx->voccxit = dolist[0].vocolobj; ctx->voccxthc = 0; if (dbg) { tioputs(ctx->voccxtio, ".. setting it: "); runppr(rcx, ctx->voccxit, PRP_SDESC, 0); tioputs(ctx->voccxtio, "\\n"); } /* set "him" if appropriate */ runppr(rcx, ctx->voccxit, PRP_ISHIM, 0); if (runtostyp(rcx) == DAT_TRUE) { ctx->voccxhim = ctx->voccxit; if (dbg) tioputs(ctx->voccxtio, "... [setting \"him\" to same object]\\n"); } rundisc(rcx); /* set "her" if appropriate */ runppr(rcx, ctx->voccxit, PRP_ISHER, 0); if (runtostyp(rcx) == DAT_TRUE) { ctx->voccxher = ctx->voccxit; if (dbg) tioputs(ctx->voccxtio, "... [setting \"her\" to same object]\\n"); } rundisc(rcx); } else if (cnt > 1) { ctx->voccxthc = cnt; ctx->voccxit = MCMONINV; if (dbg) tioputs(ctx->voccxtio, ".. setting \"them\": ["); for (i = 0 ; i < cnt ; ++i) { ctx->voccxthm[i] = dolist[i].vocolobj; if (dbg) { runppr(rcx, dolist[i].vocolobj, PRP_SDESC, 0); tioputs(ctx->voccxtio, i+1 < cnt ? ", " : "]\\n"); } } } } /* display a multiple-object prefix */ void voc_multi_prefix(voccxdef *ctx, objnum objn, int show_prefix, int multi_flags, int cur_index, int count) { runcxdef *rcx = ctx->voccxrun; /* if the object is invalid, ignore it */ if (objn == MCMONINV) return; /* * if there's a prefixdesc method defined, call it rather than the * older multisdesc (or even older sdesc) approach */ if (objgetap(ctx->voccxmem, objn, PRP_PREFIXDESC, (objnum *)0, FALSE) != 0) { runsdef val; /* push the word flags */ runpnum(rcx, multi_flags); /* * push the object count and the current index (adjusted to a * 1-based value) */ runpnum(rcx, count); runpnum(rcx, cur_index + 1); /* push the 'show' flag */ val.runstyp = runclog(show_prefix); runpush(rcx, val.runstyp, &val); /* call the method */ runppr(rcx, objn, PRP_PREFIXDESC, 4); /* we're done */ return; } /* * if we're not showing the prefix, don't use the multisdesc/sdesc * display */ if (!show_prefix) return; /* * use multisdesc if defined (for compatibility with older games, * use sdesc if multisdesc doesn't exist for this object) */ if (objgetap(ctx->voccxmem, objn, PRP_MULTISDESC, (objnum *)0, FALSE) == 0) { /* there's no multisdesc defined - use the plain sdesc */ runppr(rcx, objn, PRP_SDESC, 0); } else { /* multisdesc is defined - use it */ runppr(rcx, objn, PRP_MULTISDESC, 0); } /* show the colon */ vocerr_info(ctx, VOCERR(120), ": "); } /* execute command for each object in direct object list */ static int exeloop(voccxdef *ctx, objnum actor, objnum verb, vocoldef *dolist, objnum *prep, vocoldef *iobj, int multi_flags, uchar *tpl, int newstyle) { runcxdef *rcx = ctx->voccxrun; int err; int i; int dobj_cnt; int exec_cnt; vocoldef *dobj; /* * count the direct objects; we'll iterate over the direct objects, * so we execute the command once per direct object */ exec_cnt = dobj_cnt = (dolist != 0 ? voclistlen(dolist) : 0); /* * if there are no direct objects, we still must execute the command * once */ if (exec_cnt < 1) exec_cnt = 1; /* * If we have multiple direct objects, or we're using "all" with * just one direct object, check with the verb to see if multiple * words are acceptable: call verb.rejectMultiDobj, and see what it * returns; if it returns true, don't allow multiple words, and * expect that rejectMultiDobj displayed an error message. * Otherwise, proceed. */ if (((multi_flags & VOCS_ALL) != 0 || dobj_cnt > 1) && dolist && dolist[0].vocolobj != MCMONINV) { int typ; ERRBEGIN(ctx->voccxerr) runrst(rcx); if (!prep || *prep == MCMONINV) runpnil(rcx); else runpobj(rcx, *prep); runppr(rcx, verb, PRP_REJECTMDO, 1); typ = runtostyp(rcx); rundisc(rcx); ERRCATCH(ctx->voccxerr, err) if (err == ERR_RUNEXIT || err == ERR_RUNEXITOBJ || err == ERR_RUNABRT) return err; else errrse(ctx->voccxerr); ERREND(ctx->voccxerr) /* if they returned 'true', don't bother continuing */ if (typ == DAT_TRUE) return 0; } /* * execute the command the required number of times */ for (i = 0 ; i < exec_cnt ; ++i) { int show_multi_prefix; /* get the current direct object, if we have one */ dobj = (dolist != 0 ? &dolist[i] : 0); /* * If we have a number or string, set the current one in * numObj/strObj */ if (dolist != 0) { if (dolist[i].vocolflg == VOCS_STR) { /* it's a string - set strObj.value */ vocsetobj(ctx, ctx->voccxstr, DAT_SSTRING, dolist[i].vocolfst + 1, &dolist[i], &dolist[i]); } else if (dolist[i].vocolflg == VOCS_NUM) { long v1, v2; /* it's a number - set numObj.value */ v1 = atol(dolist[i].vocolfst); oswp4s(&v2, v1); vocsetobj(ctx, ctx->voccxnum, DAT_NUMBER, &v2, &dolist[i], &dolist[i]); } } /* * For cases where we have a bunch of direct objects (or even * one when "all" was used), we want to preface the output from * each iteration with the name of the object we're acting on * currently. In other cases, there is no prefix. */ show_multi_prefix = ((multi_flags != 0 || dobj_cnt > 1) && dobj != 0); /* * Execute the command for this object. For every object except * the first, re-validate the direct and indirect objects. * There's no need to re-validate the objects on the first * object in a command, because that will already have been done * during object resolution. */ err = exe1cmd(ctx, actor, verb, dobj, prep, iobj, (i + 1 == exec_cnt), tpl, newstyle, FALSE, i != 0, i != 0, dolist, i, dobj_cnt, show_multi_prefix, multi_flags); /* check the error - ignore any verification failures */ switch(err) { case ERR_PRS_VERDO_FAIL: case ERR_PRS_VERIO_FAIL: case ERR_PRS_NO_VERDO: case ERR_PRS_NO_VERIO: case ERR_RUNEXITOBJ: case ERR_RUNEXIT: /* ignore the error and continue */ err = 0; break; case ERR_RUNEXITPRECMD: /* * exited from preCommand - skip execution of subsequent * objects, but return success */ return 0; case 0: /* no error; continue */ break; default: /* anything else stops this command */ return err; } /* flush output */ tioflush(ctx->voccxtio); } /* success */ return 0; } /* * Execute a command recursively. Game code can call this routine * (indirectly through a built-in function) to execute a command, using * all of the same steps that would be applied for the command if the * player had typed it. */ int execmd_recurs(voccxdef *ctx, objnum actor, objnum verb, objnum dobj, objnum prep, objnum iobj, int validate_dobj, int validate_iobj) { int err; int newstyle; uchar tpl[VOCTPL2SIZ]; vocoldef dobjv; vocoldef iobjv; voccxdef ctx_copy; runsdef *orig_sp; runsdef *orig_bp; /* * Save the stack and base pointers as they are on entry. Since * exe1cmd() is being called recursively, it won't automatically clear * the stack after it's done as it would at the top level; this means * that an aborted frame can be left on the stack if we throw an * 'exit' or 'abort' in the course of executing the command. To make * sure we don't leave any aborted frames on the stack before * returning to our caller, we simply need to restore the stack and * frame pointers on the way out as they were on the way in. */ orig_sp = ctx->voccxrun->runcxsp; orig_bp = ctx->voccxrun->runcxbp; /* make a copy of the voc context, so that changes aren't permanent */ ctx_copy = *ctx; ctx = &ctx_copy; /* * there are no unknown words in the recursive command, since the * command was prepared directly from resolved objects */ ctx->voccxunknown = 0; /* set up the vocoldef structure for the direct object, if present */ if (dobj != MCMONINV) { dobjv.vocolobj = dobj; dobjv.vocolfst = dobjv.vocollst = ""; dobjv.vocolflg = 0; } /* set up the vocoldef structure for the indirect object, if present */ if (iobj != MCMONINV) { iobjv.vocolobj = iobj; iobjv.vocolfst = iobjv.vocollst = ""; iobjv.vocolflg = 0; } /* figure out which template we need, based on the objects provided */ if (dobj == MCMONINV) { uint actofs; uint tplofs; /* * No objects were provided - use the verb's "action" method. * Make sure that there is in fact an "action" method. */ exe_get_tpl(ctx, verb, &tplofs, &actofs); if (actofs != 0) { /* execute the "action" method */ err = exe1cmd(ctx, actor, verb, 0, &prep, 0, FALSE, 0, FALSE, TRUE, validate_dobj, validate_iobj, 0, 0, 0, FALSE, 0); } else { /* indicate that the sentence structure wasn't understood */ err = ERR_PRS_SENT_UNK; } } else if (iobj == MCMONINV) { /* * No indirect object was provided, but a direct object is * present - use the one-object template. First, look up the * template. */ if (voctplfnd(ctx, verb, MCMONINV, tpl, &newstyle)) { /* execute the command */ err = exe1cmd(ctx, actor, verb, &dobjv, &prep, 0, FALSE, tpl, newstyle, TRUE, validate_dobj, validate_iobj, &dobjv, 0, 1, FALSE, 0); } else { /* indicate that the sentence structure wasn't understood */ err = ERR_PRS_SENT_UNK; } } else { /* * Both a direct and indirect object were provided - find the * two-object template for the given preposition. */ if (voctplfnd(ctx, verb, prep, tpl, &newstyle)) { /* execute the command */ err = exe1cmd(ctx, actor, verb, &dobjv, &prep, &iobjv, FALSE, tpl, newstyle, TRUE, validate_dobj, validate_iobj, &dobjv, 0, 1, FALSE, 0); } else { /* indicate that the sentence structure wasn't understood */ err = ERR_PRS_SENT_UNK; } } /* * if the error was EXITPRECMD, change it to EXIT - EXITPRECMD is a * special flag indicating that we exited from a preCommand * function, which is different than normal exiting internally but * not to the game */ if (err == ERR_RUNEXITPRECMD) err = ERR_RUNEXIT; /* * restore the original stack and base pointers, to ensure that we * don't leave any aborted frames on the stack */ ctx->voccxrun->runcxsp = orig_sp; ctx->voccxrun->runcxbp = orig_bp; /* return the result code */ return err; } /* * Check for ALL, ANY, or THEM in the list - use multi-mode if found, * even if we have only one object. Returns a combination of any of the * VOCS_ALL, VOCS_ANY, or VOCS_THEM flags that we find. */ static int check_for_multi(vocoldef *dolist) { int dolen; int i; int result; /* presume we won't find any flags */ result = 0; /* * scan the list for ALL, ANY, or THEM flags, combining any such * flags we find into the result */ dolen = voclistlen(dolist); for (i = 0 ; i < dolen ; ++i) result |= (dolist[i].vocolflg & (VOCS_ALL | VOCS_ANY | VOCS_THEM)); /* return the result */ return result; } /* ------------------------------------------------------------------------ */ /* * Try running the preparseCmd user function. Returns 0 if the * function doesn't exist or returns 'true', ERR_PREPRSCMDCAN if it * returns 'nil' (and thus wants to cancel the command), and * ERR_PREPRSCMDREDO if it returns a list (and thus wants to redo the * command). */ int try_preparse_cmd(voccxdef *ctx, char **cmd, int wrdcnt, uchar **preparse_list) { uchar listbuf[VOCBUFSIZ + 2 + 3*VOCBUFSIZ]; int i; uchar *p; size_t len; runsdef val; int typ; int err; /* if there's no preparseCmd, keep processing */ if (ctx->voccxppc == MCMONINV) return 0; /* build a list of the words */ for (p = listbuf + 2, i = 0 ; i < wrdcnt ; ++i) { char *src; int add_quote; /* check for strings - they require special handling */ if (cmd[i][0] == '"') { /* * it's a string - what follows is a run-time style string, * with a length prefix followed by the text of the string */ len = osrp2(cmd[i] + 1) - 2; src = cmd[i] + 3; /* add quotes to the result */ add_quote = TRUE; } else { /* ordinary word - copy directly */ src = (char *)cmd[i]; /* it's a null-terminated string */ len = strlen(src); /* don't add quotes to the result */ add_quote = FALSE; } /* write the type prefix */ *p++ = DAT_SSTRING; /* write the length prefix */ oswp2(p, len + 2 + (add_quote ? 2 : 0)); p += 2; /* add an open quote if necessary */ if (add_quote) *p++ = '"'; /* copy the text */ memcpy(p, src, len); p += len; /* add the closing quote if necessary */ if (add_quote) *p++ = '"'; } /* set the length of the whole list */ len = p - listbuf; oswp2(listbuf, len); /* push the list as the argument, and call the user's preparseCmd */ val.runstyp = DAT_LIST; val.runsv.runsvstr = listbuf; runpush(ctx->voccxrun, DAT_LIST, &val); /* presume that no error will occur */ err = 0; /* catch errors that occur within preparseCmd */ ERRBEGIN(ctx->voccxerr) { /* call preparseCmd */ runfn(ctx->voccxrun, ctx->voccxppc, 1); } ERRCATCH(ctx->voccxerr, err) { /* * if it's abort/exit/exitobj, just return it; for any other * errors, just re-throw the same error */ switch(err) { case ERR_RUNABRT: case ERR_RUNEXIT: case ERR_RUNEXITOBJ: /* simply return these errors to the caller */ break; default: /* re-throw anything else */ errrse(ctx->voccxerr); } } ERREND(ctx->voccxerr); /* if an error occurred, return the error code */ if (err != 0) return err; /* get the result */ typ = runtostyp(ctx->voccxrun); /* if they returned a list, it's a new command to execute */ if (typ == DAT_LIST) { /* get the list and give it to the caller */ *preparse_list = runpoplst(ctx->voccxrun); /* * indicate that the command is to be reparsed with the new word * list */ return ERR_PREPRSCMDREDO; } /* for any other type, we don't need the value, so discard it */ rundisc(ctx->voccxrun); /* if the result is nil, don't process this command further */ if (typ == DAT_NIL) return ERR_PREPRSCMDCAN; else return 0; } /* ------------------------------------------------------------------------ */ /* * Call parseAskobjIndirect */ static void voc_askobj_indirect(voccxdef *ctx, vocoldef *dolist, objnum actor, objnum verb, objnum prep) { int cnt; int i; size_t len; uchar *lstp; /* * Generate the direct object list argument. This argument is a * list of lists. For each noun phrase, we generate one sublist in * the main list. Each sublist itself consists of three * sub-sublists: first, a list of strings giving the words in the * noun phrase; second, a list of the objects matching the noun * phrase; third, a list of the flags for the matching objects. * * So, if the player typed "put red box and blue ball", we might * generate a list something like this: * * [ [ ['red', 'box'], [redBox1, redBox2], [0, 0] ], [ ['blue', * 'ball'], [blueBall], [0, 0] ] ] */ /* * First, figure out how much space we need for this list of lists * of lists. Scan the direct object list for distinct noun phrases * - we need one sublist for each distinct noun phrase. */ cnt = voclistlen(dolist); for (len = 0, i = 0 ; i < cnt ; ) { char *p; size_t curlen; int j; /* * we need the sublist type prefix (one byte) plus the sublist * length prefix (two bytes), plus the type and length prefixes * (one plus two bytes) for each of the three sub-sublist */ len += (1+2) + 3*(1+2); /* * we need space to store the strings for the words in this noun * phrase */ for (p = dolist[i].vocolfst ; p != 0 && p <= dolist[i].vocollst ; p += curlen + 1) { /* * add in the space needed for this string element in the * sub-sublist - we need one byte for the type prefix, two * bytes for the length prefix, and the bytes for the string * itself */ curlen = strlen(p); len += (1+2) + curlen; } /* * scan each object for this same noun phrase (i.e., for which * the vocabulary words are the same) */ for (j = i ; j < cnt && dolist[j].vocolfst == dolist[i].vocolfst ; ++j) { /* * Add in space for this object in the sub-sublist for the * current noun phrase. If this object is nil, we need only * one byte for the type; otherwise, we need one byte for * the type prefix plus two bytes for the object ID. */ if (dolist[i].vocolobj == MCMONINV) len += 1; else len += (1 + 2); /* * Add in space for the flags sub-sublist for the current * object. We need one byte for the type and four for the * integer value. */ len += (1 + 4); } /* skip to the next distinct noun phrase */ i = j; } /* allocate the list */ lstp = voc_push_list_siz(ctx, len); /* * Go through our object array again, and this time actually build * the list. */ for (i = 0 ; i < cnt ; ) { char *p; uchar *subp; uchar *subsubp; size_t curlen; int j; /* start the sublist with the type prefix */ *lstp++ = DAT_LIST; /* leave a placeholder for our length prefix */ subp = lstp; lstp += 2; /* start the sub-sublist with the word strings */ *lstp++ = DAT_LIST; subsubp = lstp; lstp += 2; /* store the word strings in the sub-sublist */ for (p = dolist[i].vocolfst ; p != 0 && p <= dolist[i].vocollst ; p += curlen + 1) { /* get this string's length */ curlen = strlen(p); /* store the type and length prefixes */ *lstp++ = DAT_SSTRING; oswp2(lstp, curlen + 2); lstp += 2; /* store the string */ memcpy(lstp, p, curlen); lstp += curlen; } /* fix up the string sub-sublist length */ oswp2(subsubp, lstp - subsubp); /* start the second sub-sublist, for the objects */ *lstp++ = DAT_LIST; subsubp = lstp; lstp += 2; /* write each object */ for (j = i ; j < cnt && dolist[j].vocolfst == dolist[i].vocolfst ; ++j) { /* * if this object isn't nil, write it to the sub-sublist; * otherwise, just put nil in the sub-sublist */ if (dolist[j].vocolobj != MCMONINV) { *lstp++ = DAT_OBJECT; oswp2(lstp, dolist[j].vocolobj); lstp += 2; } else { /* no object - just store nil */ *lstp++ = DAT_NIL; } } /* fix up the object sub-sublist length */ oswp2(subsubp, lstp - subsubp); /* start the third sub-sublist, for the flags */ *lstp++ = DAT_LIST; subsubp = lstp; lstp += 2; /* write each object's flags */ for (j = i ; j < cnt && dolist[j].vocolfst == dolist[i].vocolfst ; ++j) { /* write the flags */ *lstp++ = DAT_NUMBER; oswp4s(lstp, dolist[j].vocolflg); lstp += 4; } /* fix up the flag sub-sublist length */ oswp2(subsubp, lstp - subsubp); /* skip to the start of the next distinct noun phrase */ i = j; /* fix up the sublist length */ oswp2(subp, lstp - subp); } /* push the prep, verb, and actor arguments */ runpobj(ctx->voccxrun, prep); runpobj(ctx->voccxrun, verb); runpobj(ctx->voccxrun, (objnum)(actor == MCMONINV ? ctx->voccxme : actor)); /* call the function */ runfn(ctx->voccxrun, ctx->voccxpask3, 4); } /* ------------------------------------------------------------------------ */ /* * execmd() - executes a user's command given the verb's verb and * preposition words, a list of nouns to be used as indirect objects, * and a list to be used for direct objects. The globals cmdActor and * cmdPrep should already be set. This routine tries to find a template * for the verb which matches the player's command. If no template * matches, we try (using default objects and, if that fails, requests * to the player for objects) to fill in any missing information in the * player's command. If that still fails, we will say we don't * understand the sentence and leave it at that. */ int execmd(voccxdef *ctx, objnum actor, objnum prep, char *vverb, char *vprep, vocoldef *dolist, vocoldef *iolist, char **cmd, int *typelist, char *cmdbuf, int wrdcnt, uchar **preparse_list, int *next_word) { objnum verb; objnum iobj; int multi_flags = 0; vocwdef *n; int cnt; vocoldef *newnoun; int next; char *exenewcmd; char *donewcmd; char *ionewcmd; char *exenewbuf; char *donewbuf; char *ionewbuf; char **exenewlist; char **donewlist; char **ionewlist; int *exenewtype; int *donewtype; int *ionewtype; vocoldef *dolist1; vocoldef *iolist1; uchar tpl[VOCTPL2SIZ]; int foundtpl; /* used to determine success of tpl searches */ runcxdef *rcx = ctx->voccxrun; uint tplofs; /* offset of template object */ uint actofs; /* offset of 'action' property */ int askflags; /* flag for what we need to ask user */ int newstyle; /* flag indicating new-style template definitions */ int tplflags; int err; uchar *save_sp; /* run preparseCmd */ switch(try_preparse_cmd(ctx, cmd, wrdcnt, preparse_list)) { case 0: /* proceed with the command */ break; case ERR_PREPRSCMDCAN: /* command cancelled */ return 0; case ERR_RUNEXIT: case ERR_RUNABRT: case ERR_RUNEXITOBJ: /* abort/exit/exitobj - treat this the same as command cancellation */ return 0; case ERR_PREPRSCMDREDO: /* redo the command - so indicate to the caller */ return ERR_PREPRSCMDREDO; } /* look up the verb based on the verb and verb-prep */ n = vocffw(ctx, vverb, (int)strlen(vverb), vprep, (vprep ? (int)strlen(vprep) : 0), PRP_VERB, (vocseadef *)0); /* if we didn't find a verb template, we can't process the sentence */ if (n == 0) { /* try parseUnknownVerb, and show an error if that doesn't handle it */ if (try_unknown_verb(ctx, actor, cmd, typelist, wrdcnt, next_word, TRUE, VOCERR(18), "I don't understand that sentence.")) { /* they handled it successfully - end the command with success */ return 0; } else { /* * parseUnknownVerb failed or aborted - end the command with * an error */ return 1; } } /* get the deepverb object */ verb = n->vocwobj; /* default actor is "Me" */ if (actor == MCMONINV) actor = ctx->voccxme; /* set a savepoint, if we're keeping undo information */ if (ctx->voccxundo) objusav(ctx->voccxundo); /* * Check that the room will allow this command -- it may not * due to darkness or other ailment. We can find out with the * roomCheck(verb) message, sent to the meobj. */ { int t; /* call roomCheck */ runrst(rcx); runpobj(rcx, verb); runppr(rcx, ctx->voccxme, PRP_ROOMCHECK, 1); t = runpoplog(rcx); /* if they returned nil, stop the command, but indicate success */ if (!t) return 0; } /* look for a new-style template first, then the old-style template */ exe_get_tpl(ctx, verb, &tplofs, &actofs); /* make sure we found a verb */ if (tplofs == 0 && actofs == 0 && verb != ctx->voccxvag) { /* try parseUnknownVerb, and show an error if that doesn't handle it */ if (try_unknown_verb(ctx, actor, cmd, typelist, wrdcnt, next_word, TRUE, VOCERR(23), "internal error: verb has no action, doAction, or ioAction")) return 0; else return 1; } /* * Check to see if we have an "all" - if we do, we'll need to * display the direct object's name even if only one direct object * comes of it. */ multi_flags = check_for_multi(dolist); /* * set up dobj word list in case objwords is used in doDefault (the * game may want to check for "all" and disallow it, for example) */ ctx->voccxdobj = dolist; /* set up our stack allocations, which we may need from now on */ voc_enter(ctx, &save_sp); VOC_STK_ARRAY(ctx, char, donewcmd, VOCBUFSIZ); VOC_STK_ARRAY(ctx, char, ionewcmd, VOCBUFSIZ); VOC_STK_ARRAY(ctx, char, donewbuf, 2*VOCBUFSIZ); VOC_STK_ARRAY(ctx, char, ionewbuf, 2*VOCBUFSIZ); VOC_STK_ARRAY(ctx, char *, donewlist, VOCBUFSIZ); VOC_STK_ARRAY(ctx, char *, ionewlist, VOCBUFSIZ); VOC_MAX_ARRAY(ctx, int, donewtype); VOC_MAX_ARRAY(ctx, int, ionewtype); VOC_MAX_ARRAY(ctx, vocoldef, dolist1); VOC_MAX_ARRAY(ctx, vocoldef, iolist1); /* keep going until we're done with the sentence */ for ( ;; ) { askflags = err = 0; ERRBEGIN(ctx->voccxerr) /* * Now see what kind of sentence we have. If we have no * objects and an action, use the action. If we have a direct * object and a doAction, use the doAction. If we have an * indirect object and an ioAction with a matching preposition, * use the ioAction. If we have an indirect object and no * matching ioAction, complain. If we have a direct object and * no doAction or ioAction, complain. If we have fewer objects * than we really want, ask the user for more of them. */ if (voclistlen(dolist) == 0 && voclistlen(iolist) == 0) { if (actofs || verb == ctx->voccxvag) { if ((err = exeloop(ctx, actor, verb, (vocoldef *)0, &prep, (vocoldef *)0, multi_flags, (uchar *)0, 0)) != 0) goto exit_error; } else { /* * The player has not specified any objects, but the * verb seems to require one. See if there's a unique * default. */ runrst(rcx); runpnil(rcx); runpobj(rcx, prep); runpobj(rcx, actor); runppr(rcx, verb, PRP_DODEFAULT, 3); if (runtostyp(rcx) == DAT_LIST) { uchar *l = runpoplst(rcx); uint lstsiz; objnum defobj; int objcnt; objnum newprep; runsdef val; objnum o; /* push list back on stack, to keep in heap */ val.runsv.runsvstr = l; val.runstyp = DAT_LIST; runrepush(rcx, &val); /* get list size out of list */ lstsiz = osrp2(l) - 2; l += 2; /* find default preposition for verb, if any */ runppr(rcx, verb, PRP_PREPDEFAULT, 0); if (runtostyp(rcx) == DAT_OBJECT) newprep = runpopobj(rcx); else { newprep = MCMONINV; rundisc(rcx); } if (!voctplfnd(ctx, verb, newprep, tpl, &newstyle)) { for (objcnt = 0 ; lstsiz && objcnt < 2 ; lstadv(&l, &lstsiz)) { if (*l == DAT_OBJECT) { ++objcnt; defobj = osrp2(l + 1); } } } else { int dobj_first; /* * Get the template flags. If we must * disambiguate the direct object first for this * verb, do so now. */ tplflags = (newstyle ? voctplflg(tpl) : 0); dobj_first = (tplflags & VOCTPLFLG_DOBJ_FIRST); for (objcnt = 0 ; lstsiz && objcnt < 2 ; lstadv(&l, &lstsiz)) { if (*l == DAT_OBJECT) { o = osrp2(l + 1); if (!objgetap(ctx->voccxmem, o, voctplvd(tpl), (objnum *)0, FALSE)) continue; tiohide(ctx->voccxtio); if (newprep != MCMONINV && !dobj_first) runpnil(rcx); runpobj(rcx, actor); runppr(rcx, o, voctplvd(tpl), ((newprep != MCMONINV && !dobj_first) ? 2 : 1)); if (!tioshow(ctx->voccxtio)) { ++objcnt; defobj = o; } } } /* no longer need list in heap, so discard it */ rundisc(rcx); /* use default object if there's exactly one */ if (objcnt == 1) { dolist[0].vocolobj = defobj; dolist[0].vocolflg = 0; dolist[0].vocolfst = dolist[0].vocollst = 0; dolist[1].vocolobj = MCMONINV; dolist[1].vocolflg = 0; dolist[1].vocolfst = dolist[1].vocollst = 0; runrst(rcx); if (ctx->voccxpdef2 != MCMONINV) { runpnil(rcx); runpobj(rcx, defobj); runpobj(rcx, verb); runpobj(rcx, actor); runfn(rcx, ctx->voccxpdef2, 4); } else if (ctx->voccxpdef != MCMONINV) { runpnil(rcx); runpobj(rcx, defobj); runfn(rcx, ctx->voccxpdef, 2); } else { /* tell the player what we're doing */ vocerr_info(ctx, VOCERR(130), "("); runppr(rcx, defobj, PRP_THEDESC, 0); vocerr_info(ctx, VOCERR(131), ")"); tioflush(ctx->voccxtio); } err = -2; /* "continue" */ goto exit_error; } } } else rundisc(rcx); /* * No unique default; ask the player for a direct * object, and try the command again if he is kind * enough to provide one. */ askflags = ERR_RUNASKD; } } else if (voclistlen(iolist) == 0) { /* direct object(s), but no indirect object -- find doAction */ if (voctplfnd(ctx, verb, MCMONINV, tpl, &newstyle)) { /* disambiguate the direct object list, now that we can */ if (vocdisambig(ctx, dolist1, dolist, PRP_DODEFAULT, PRP_VALIDDO, voctplvd(tpl), cmd, MCMONINV, actor, verb, prep, cmdbuf, FALSE)) { err = -1; goto exit_error; } iobj = MCMONINV; /* * save the disambiguated direct object list, in case * we hit an askio in the course of processing it */ memcpy(dolist, dolist1, (size_t)(voclistlen(dolist1) + 1)*sizeof(dolist[0])); /* re-check for multi-mode */ if (multi_flags == 0) multi_flags = check_for_multi(dolist1); /* save it/them/him/her, and execute the command */ exesaveit(ctx, dolist1); if ((err = exeloop(ctx, actor, verb, dolist1, &prep, (vocoldef *)0, multi_flags, tpl, newstyle)) != 0) goto exit_error; } else { /* no doAction - we'll need to find an indirect object */ runrst(rcx); runppr(rcx, verb, PRP_PREPDEFAULT, 0); if (runtostyp(rcx) != DAT_OBJECT) { /* discard the result */ rundisc(rcx); /* call parseUnknownVerb to handle it */ if (try_unknown_verb(ctx, actor, cmd, typelist, wrdcnt, next_word, TRUE, VOCERR(24), "I don't recognize that sentence.")) { /* handled - end the command successfully */ err = 0; } else { /* not handled - indicate failure */ err = -1; } goto exit_error; } prep = runpopobj(rcx); runrst(rcx); runpobj(rcx, prep); runpobj(rcx, actor); runppr(rcx, verb, PRP_IODEFAULT, 2); if (runtostyp(rcx) == DAT_LIST) { uchar *l = runpoplst(rcx); uint lstsiz; objnum defobj; int objcnt; runsdef val; objnum o; /* push list back on stack, to keep in heap */ val.runsv.runsvstr = l; val.runstyp = DAT_LIST; runrepush(rcx, &val); /* get list size out of list */ lstsiz = osrp2(l) - 2; l += 2; if (!voctplfnd(ctx, verb, prep, tpl, &newstyle)) { for (objcnt = 0 ; lstsiz && objcnt < 2 ; lstadv(&l, &lstsiz)) { if (*l == DAT_OBJECT) { objcnt++; defobj = osrp2(l + 1); } } } else { int dobj_first; /* * Get the template flags. If we must * disambiguate the direct object first for this * verb, do so now. */ tplflags = (newstyle ? voctplflg(tpl) : 0); dobj_first = (tplflags & VOCTPLFLG_DOBJ_FIRST); if (dobj_first) { if (vocdisambig(ctx, dolist1, dolist, PRP_DODEFAULT, PRP_VALIDDO, voctplvd(tpl), cmd, MCMONINV, actor, verb, prep, cmdbuf, FALSE)) { err = -1; goto exit_error; } /* only one direct object is allowed here */ if (voclistlen(dolist1) > 1) { vocerr(ctx, VOCERR(28), "You can't use multiple objects with this command."); err = -1; goto exit_error; } /* save the object in the original list */ memcpy(dolist, dolist1, (size_t)(2 * sizeof(dolist[0]))); } for (objcnt = 0 ; lstsiz && objcnt < 2 ; lstadv(&l, &lstsiz)) { if (*l == DAT_OBJECT) { o = osrp2(l + 1); if (!objgetap(ctx->voccxmem, o, voctplvi(tpl), (objnum *)0, FALSE)) continue; tiohide(ctx->voccxtio); if (dobj_first) runpobj(rcx, dolist[0].vocolobj); runpobj(rcx, actor); runppr(rcx, o, voctplvi(tpl), (dobj_first ? 2 : 1)); if (!tioshow(ctx->voccxtio)) { objcnt++; defobj = o; } } } } /* no longer need list in heap, so discard it */ rundisc(rcx); /* if there's exactly one default object, use it */ if (objcnt == 1) { iolist[0].vocolobj = defobj; iolist[0].vocolflg = 0; iolist[0].vocolfst = iolist[0].vocollst = 0; iolist[1].vocolobj = MCMONINV; iolist[1].vocolflg = 0; iolist[1].vocolfst = iolist[1].vocollst = 0; /* tell the user what we're assuming */ runrst(rcx); if (ctx->voccxpdef2 != MCMONINV) { runpobj(rcx, prep); runpobj(rcx, defobj); runpobj(rcx, verb); runpobj(rcx, actor); runfn(rcx, ctx->voccxpdef2, 4); } else if (ctx->voccxpdef != MCMONINV) { runpobj(rcx, prep); runpobj(rcx, defobj); runfn(rcx, ctx->voccxpdef, 2); } else { vocerr_info(ctx, VOCERR(130), "("); runppr(rcx, prep, PRP_SDESC, 0); vocerr_info(ctx, VOCERR(132), " "); runppr(rcx, defobj, PRP_THEDESC, 0); vocerr_info(ctx, VOCERR(131), ")"); } tioflush(ctx->voccxtio); err = -2; /* "continue" */ goto exit_error; } } else rundisc(rcx); /* * We didn't get a unique default indirect object, so * we should ask the player for an indirct object, and * repeat the command should he provide one. */ askflags = ERR_RUNASKI; } } else { objnum otherobj; /* find the template for this verb/prep combination */ if (!voctplfnd(ctx, verb, prep, tpl, &newstyle)) { vocoldef *np1; /* * If we could have used the preposition in the first noun * phrase rather than in the verb, and this would have * yielded a valid verb phrase, the error is "I don't see * any here". * * Otherwise, it's a verb phrasing error. In this case, * call parseUnknownVerb to handle the error; the default * error is "I don't recognize that sentence". */ np1 = dolist[0].vocolfst < iolist[0].vocolfst ? dolist : iolist; if ((np1->vocolflg & VOCS_TRIMPREP) != 0) { char namebuf[VOCBUFSIZ]; /* * it's a trimmed prep phrase, so we actually have an * unmatched object - report the error */ voc_make_obj_name_from_list( ctx, namebuf, cmd, np1->vocolfst, np1->vocolhlst); vocerr(ctx, VOCERR(9), "I don't see any %s here.", namebuf); /* terminate the command with an error */ err = -1; } else if (try_unknown_verb(ctx, actor, cmd, typelist, wrdcnt, next_word, TRUE, VOCERR(24), "I don't recognize that sentence.")) { /* they handled it - terminate command successfully */ err = 0; } else { /* that failed - terminate the command with an error */ err = -1; } /* terminate the command */ goto exit_error; } /* * We have both direct and indirect objects. If we don't * yet have the direct object, go ask for it */ if (voclistlen(dolist) == 0) { askflags = ERR_RUNASKD; goto exit_error; } /* get the flags (if old-style, flags are always zero) */ tplflags = (newstyle ? voctplflg(tpl) : 0); /* * the "other" object (dobj if doing iobj, iobj if doing * dobj) is not known when the first object is disambiguated */ otherobj = MCMONINV; /* disambiguate the objects in the proper order */ if (tplflags & VOCTPLFLG_DOBJ_FIRST) { /* disambiguate the direct object list */ if (vocdisambig(ctx, dolist1, dolist, PRP_DODEFAULT, PRP_VALIDDO, voctplvd(tpl), cmd, otherobj, actor, verb, prep, cmdbuf, FALSE)) { err = -1; goto exit_error; } /* * only one direct object is allowed if it's * disambiguated first */ if (voclistlen(dolist1) > 1) { vocerr(ctx, VOCERR(28), "You can't use multiple objects with this command."); err = -1; goto exit_error; } /* the other object is now known for iboj disambiguation */ otherobj = dolist1[0].vocolobj; } /* disambiguate the indirect object list */ if (vocdisambig(ctx, iolist1, iolist, PRP_IODEFAULT, PRP_VALIDIO, voctplvi(tpl), cmd, otherobj, actor, verb, prep, cmdbuf, FALSE)) { err = -1; goto exit_error; } /* only one indirect object is allowed */ if (voclistlen(iolist1) > 1) { vocerr(ctx, VOCERR(25), "You can't use multiple indirect objects."); err = -1; goto exit_error; } otherobj = iobj = iolist1[0].vocolobj; /* * disambiguate the direct object list if we haven't * already done so (we might have disambiguated it first due * to the DisambigDobjFirst flag being set in the template) */ if (!(tplflags & VOCTPLFLG_DOBJ_FIRST) && vocdisambig(ctx, dolist1, dolist, PRP_DODEFAULT, PRP_VALIDDO, voctplvd(tpl), cmd, otherobj, actor, verb, prep, cmdbuf, FALSE)) { err = -1; goto exit_error; } /* re-check for multi-mode */ if (multi_flags == 0) multi_flags = check_for_multi(dolist1); /* save it/them/him/her, and execute the command */ exesaveit(ctx, dolist1); if ((err = exeloop(ctx, actor, verb, dolist1, &prep, iolist1, multi_flags, tpl, newstyle)) != 0) goto exit_error; } exit_error: ; ERRCATCH(ctx->voccxerr, err) if (err == ERR_RUNASKI) prep = errargint(0); if (err != ERR_RUNASKD && err != ERR_RUNASKI) errrse(ctx->voccxerr); ERREND(ctx->voccxerr) switch(err) { case 0: break; case ERR_RUNABRT: /* "abort" executed - return the ABORT code */ VOC_RETVAL(ctx, save_sp, err); case ERR_RUNEXIT: /* * "exit" executed - terminate the command, but return * success, since we want to process any additional commands */ VOC_RETVAL(ctx, save_sp, 0); case ERR_RUNEXITOBJ: /* * "exitobj" executed - indicate success, since this merely * indicates that the game decided it was done processing an * object early */ VOC_RETVAL(ctx, save_sp, 0); case ERR_RUNASKI: case ERR_RUNASKD: askflags = err; break; case -2: /* special code: continue with main loop */ continue; case -1: /* special code: return an error */ default: VOC_RETVAL(ctx, save_sp, 1); } /* * If we got this far, we probably want more information. The * askflags can tell us what to do from here. */ if (askflags) { int old_unknown; int exenewpos; /* * if we had unknown words, don't ask for more information * at this point; simply give up and report the unknown word */ if (ctx->voccxunknown != 0) { VOC_RETVAL(ctx, save_sp, 1); } /* find new template indicated by the additional object */ foundtpl = voctplfnd(ctx, verb, prep, tpl, &newstyle); tplflags = (newstyle ? voctplflg(tpl) : 0); /* find a default object of the type requested */ runrst(rcx); if (askflags == ERR_RUNASKD) runpnil(rcx); runpobj(rcx, prep); runpobj(rcx, actor); runppr(rcx, verb, (prpnum)(askflags == ERR_RUNASKD ? PRP_DODEFAULT : PRP_IODEFAULT), (askflags == ERR_RUNASKD ? 3 : 2)); /* * If we got a list back from ?oDefault, and we have a new * template for the command, process the list normally with * the object verification routine for this template. If we * end up with exactly one object, we will assume it is the * object to be used; otherwise, make no assumption and ask * the user for guidance. */ if (runtostyp(rcx) == DAT_LIST && foundtpl) { uchar *l = runpoplst(rcx); uint lstsiz; int objcnt; objnum defobj; objnum o; runsdef val; /* push list back on stack, to keep it in the heap */ val.runsv.runsvstr = l; val.runstyp = DAT_LIST; runrepush(rcx, &val); /* get list size out of list */ lstsiz = osrp2(l) - 2; l += 2; for (objcnt = 0 ; lstsiz && objcnt < 2 ; lstadv(&l, &lstsiz)) { if (*l == DAT_OBJECT) { prpnum verprop; int argc = 1; o = osrp2(l + 1); verprop = (askflags == ERR_RUNASKD ? voctplvd(tpl) : voctplvi(tpl)); if (!objgetap(ctx->voccxmem, o, verprop, (objnum *)0, FALSE)) continue; tiohide(ctx->voccxtio); /* * In the unlikely event that we have an * indirect object but no direct object, push * the iobj. This can happen when the player * types a sentence such as "verb prep iobj". */ if (voclistlen(iolist) != 0 && askflags == ERR_RUNASKD && !(tplflags & VOCTPLFLG_DOBJ_FIRST)) { /* push the indirect object */ runpobj(rcx, iolist[0].vocolobj); /* note the second argument */ argc = 2; } /* * If this is a disambigDobjFirst verb, and * we're validating an indirect object list, * then we must push the direct object argument * to the indirect object validation routine. */ if (askflags == ERR_RUNASKI && (tplflags & VOCTPLFLG_DOBJ_FIRST) != 0) { /* push the diret object */ runpobj(rcx, dolist[0].vocolobj); /* note the second argument */ argc = 2; } /* push the actor and call the verXoVerb routine */ runpobj(rcx, actor); runppr(rcx, o, verprop, argc); if (!tioshow(ctx->voccxtio)) { ++objcnt; defobj = o; } } } /* no longer need list in heap, so discard it */ rundisc(rcx); /* if we found exactly one object, it's the default */ if (objcnt == 1) { if (askflags == ERR_RUNASKD) { dolist[0].vocolobj = defobj; dolist[0].vocolflg = 0; dolist[0].vocolfst = dolist[0].vocollst = 0; dolist[1].vocolobj = MCMONINV; dolist[1].vocolflg = 0; dolist[1].vocolfst = dolist[1].vocollst = 0; } else { iolist[0].vocolobj = defobj; iolist[0].vocolflg = 0; iolist[0].vocolfst = iolist[0].vocollst = 0; iolist[1].vocolobj = MCMONINV; iolist[1].vocolflg = 0; iolist[1].vocolfst = iolist[1].vocollst = 0; } /* tell the user what we're assuming */ if (ctx->voccxpdef2 != MCMONINV) { if (askflags == ERR_RUNASKI) runpobj(rcx, prep); else runpnil(rcx); runpobj(rcx, defobj); runpobj(rcx, verb); runpobj(rcx, actor); runfn(rcx, ctx->voccxpdef2, 4); } else if (ctx->voccxpdef != MCMONINV) { if (askflags == ERR_RUNASKI) runpobj(rcx, prep); else runpnil(rcx); runpobj(rcx, defobj); runfn(rcx, ctx->voccxpdef, 2); } else { vocerr_info(ctx, VOCERR(130), "("); if (askflags == ERR_RUNASKI) { runppr(rcx, prep, PRP_SDESC, 0); vocerr_info(ctx, VOCERR(132), " "); } runppr(rcx, defobj, PRP_THEDESC, 0); vocerr_info(ctx, VOCERR(131), ")"); } tioflush(ctx->voccxtio); continue; /* try the command again */ } } else rundisc(rcx); /* make sure output capturing is off for the prompt */ tiocapture(ctx->voccxtio, (mcmcxdef *)0, FALSE); tioclrcapture(ctx->voccxtio); /* * If we're asking for an indirect object, and we have a * list of direct objects, and parseAskobjIndirect is * defined, call it. Otherwise, if there's a * parseAskobjActor routine, call it. Otherwise, if there's * a parseAskobj routine, use that. Finally, if none of * those are defined, generate the default phrasing. */ if (ctx->voccxpask3 != MCMONINV && askflags == ERR_RUNASKI && voclistlen(dolist) != 0) { /* call parseAskobjIndirect */ voc_askobj_indirect(ctx, dolist, actor, verb, prep); } else if (ctx->voccxpask2 != MCMONINV) { if (askflags == ERR_RUNASKI) runpobj(ctx->voccxrun, prep); runpobj(ctx->voccxrun, verb); runpobj(ctx->voccxrun, (objnum)(actor == MCMONINV ? ctx->voccxme : actor)); runfn(ctx->voccxrun, ctx->voccxpask2, askflags == ERR_RUNASKI ? 3 : 2); } else if (ctx->voccxpask != MCMONINV) { if (askflags == ERR_RUNASKI) runpobj(ctx->voccxrun, prep); runpobj(ctx->voccxrun, verb); runfn(ctx->voccxrun, ctx->voccxpask, askflags == ERR_RUNASKI ? 2 : 1); } else { /* * Phrase the question: askDo: "What do you want * to ?" askIo: "What do you want * to it ?" If the actor is Me, leave the * actor out of it. */ if (actor != MCMONINV && actor != ctx->voccxme) { vocerr_info(ctx, VOCERR(148), "What do you want "); runppr(rcx, actor, PRP_THEDESC, 0); vocerr_info(ctx, VOCERR(149), " to "); } else { /* no actor - don't mention one */ vocerr_info(ctx, VOCERR(140), "What do you want to "); } /* add the verb */ runppr(rcx, verb, PRP_SDESC, 0); /* * add an appropriate pronoun for the direct object, * and the preposition, if we're asking for an indirect * object */ if (askflags == ERR_RUNASKI) { int i; int cnt; int distinct; char *lastfst; /* * If possible, tailor the pronoun to the situation * rather than using "it"; if we have multiple * objects, use "them", and if we have agreement * with the possible single objects about "him" or * "her", use that. Otherwise, use "it". If "all" * was specified for any word, automatically assume * multiple distinct objects were specified. */ cnt = voclistlen(dolist); for (distinct = 0, i = 0, lastfst = 0 ; i < cnt ; ++i) { /* if the first word is different here, note it */ if (lastfst != dolist[i].vocolfst) { /* this is a different word - count it */ ++distinct; lastfst = dolist[i].vocolfst; } /* always assume multiple distinct objects on "all" */ if (dolist[i].vocolflg & VOCS_ALL) { distinct = 2; break; } } /* * If we have multiple words, use "them"; * otherwise, see if we can find agreement about * using "him" or "her". */ if (distinct > 1) { /* multiple words specified by user - use "them" */ vocerr_info(ctx, VOCERR(144), " them "); } else { int is_him; int is_her; int is_them; /* run through the objects and check him/her */ for (i = 0 ; i < cnt ; ++i) { int him1, her1, them1; /* if it's special (number, string), use "it" */ if (dolist[i].vocolobj == MCMONINV) { him1 = FALSE; her1 = FALSE; them1 = FALSE; } else { /* check for "him" */ runppr(rcx, dolist[i].vocolobj, PRP_ISHIM, 0); him1 = (runtostyp(rcx) == DAT_TRUE); rundisc(rcx); /* check for "her" */ runppr(rcx, dolist[i].vocolobj, PRP_ISHER, 0); her1 = (runtostyp(rcx) == DAT_TRUE); rundisc(rcx); /* check for "them" */ runppr(rcx, dolist[i].vocolobj, PRP_ISTHEM, 0); them1 = (runtostyp(rcx) == DAT_TRUE); rundisc(rcx); } /* * if this is the first object, it * definitely agrees; otherwise, keep going * only if it agrees with what we found on * the last pass */ if (i == 0) { is_him = him1; is_her = her1; is_them = them1; } else { /* turn off either that is no longer true */ if (!him1) is_him = FALSE; if (!her1) is_her = FALSE; if (!them1) is_them = FALSE; } /* if all are false, stop now */ if (!is_him && !is_her && !is_them) break; } /* * If we could agree on "him", "her", or "them", * use that pronoun; otherwise, use "it". If we * found both "him" and "her" are acceptable for * all objects, use "them". */ if ((is_him && is_her) || is_them) vocerr_info(ctx, VOCERR(147), " them "); else if (is_him) vocerr_info(ctx, VOCERR(145), " him "); else if (is_her) vocerr_info(ctx, VOCERR(146), " her "); else vocerr_info(ctx, VOCERR(141), " it "); } /* finish off the question with the prep and a "?" */ if (prep != MCMONINV) runppr(rcx, prep, PRP_SDESC, 0); else vocerr_info(ctx, VOCERR(142), "to"); } vocerr_info(ctx, VOCERR(143), "?"); } tioflush(ctx->voccxtio); /* * Get a new command line. If the player gives us * something that looks like a noun list, and nothing more, * he anwered our question; otherwise, he's typing a new * command, so we must return to the caller with the reparse * flag set. */ if (askflags == ERR_RUNASKD) { exenewbuf = donewbuf; exenewcmd = donewcmd; exenewlist = donewlist; exenewtype = donewtype; } else { exenewbuf = ionewbuf; exenewcmd = ionewcmd; exenewlist = ionewlist; exenewtype = ionewtype; } /* read the new command */ if (vocread(ctx, actor, verb, exenewcmd, VOCBUFSIZ, askflags == ERR_RUNASKD ? 3 : 4) == VOCREAD_REDO) { /* * we got an input line, but we want to treat it as a brand * new command line - copy the new text to the command * buffer, set the 'redo' flag, and give up */ strcpy(cmdbuf, exenewcmd); ctx->voccxredo = TRUE; VOC_RETVAL(ctx, save_sp, 1); } if (!(cnt = voctok(ctx, exenewcmd, exenewbuf, exenewlist, TRUE, FALSE, TRUE))) { runrst(rcx); runfn(rcx, ctx->voccxprd, 0); VOC_RETVAL(ctx, save_sp, 1); } if (cnt < 0) { ctx->voccxunknown = 0; VOC_RETVAL(ctx, save_sp, 1); } /* * Save the unknown word count while getting types, and set * the count to a non-zero value - this will force the type * checker to generate an error on an unknown word. This * removes a little control from the game (since * parseUnknownXobj won't be called), but there's not much * else we can do here. */ old_unknown = ctx->voccxunknown; ctx->voccxunknown = 1; /* get the types */ exenewlist[cnt] = 0; if (vocgtyp(ctx, exenewlist, exenewtype, cmdbuf)) { /* * clear the unknown word count so that we fail with * this error rather than trying to deal with unknown * words */ ctx->voccxunknown = 0; /* return failure */ VOC_RETVAL(ctx, save_sp, 1); } /* restore the unknown word count */ ctx->voccxunknown = old_unknown; /* start at the first word */ exenewpos = 0; /* * if we're asking for an indirect object, and the first * word is a preposition, and matches the preposition that * we supplied to precede the indirect object, skip the * preposition */ if (askflags == ERR_RUNASKI && prep != MCMONINV && (exenewtype[0] & VOCT_PREP) != 0) { vocwdef *vp; /* get the preposition */ vp = vocffw(ctx, exenewlist[0], (int)strlen(exenewlist[0]), (char *)0, 0, PRP_PREP, (vocseadef *)0); if (vp != 0 && vp->vocwobj == prep) ++exenewpos; } /* check for a noun */ newnoun = (askflags == ERR_RUNASKD ? dolist : iolist); cnt = vocchknoun(ctx, exenewlist, exenewtype, exenewpos, &next, newnoun, FALSE); if (cnt < 0) { VOC_RETVAL(ctx, save_sp, 1); } /* invalid syntax */ if (cnt == 0 || (exenewlist[next] && !vocspec(exenewlist[next], VOCW_THEN) && *exenewlist[next] != '\0')) { strcpy(cmdbuf, exenewcmd); ctx->voccxredo = TRUE; VOC_RETVAL(ctx, save_sp, 1); } /* re-check the 'multi' flags */ multi_flags = check_for_multi(newnoun); /* give it another go by going back to the top of the loop */ } else { /* normal exit flags - return success */ VOC_RETVAL(ctx, save_sp, 0); } } } qtads-2.1.7/tads2/fio.c000066400000000000000000001622761265017072300146140ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/FIO.C,v 1.4 1999/07/11 00:46:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name fio.c - file i/o functions Function file i/o: read game, write game, save game, restore game Notes none Modified 04/11/99 CNebel - Include appctx.h, not trd.h. 10/23/97 CNebel - removed now-obsolete Think C hack. 04/02/92 MJRoberts - creation */ #include #include #include "os.h" #include "std.h" #include "appctx.h" #include "mch.h" #include "mcm.h" #include "mcl.h" #include "tok.h" #include "obj.h" #include "voc.h" #include "fio.h" #include "dat.h" #include "prs.h" #include "linf.h" #include "cmap.h" /* compare a resource string */ /* int fioisrsc(uchar *filbuf, char *refnam); */ #define fioisrsc(filbuf, refnam) \ (((filbuf)[0] == strlen(refnam)) && \ !memcmp(filbuf+1, refnam, (size_t)((filbuf)[0]))) /* callback to load an object on demand */ void OS_LOADDS fioldobj(void *ctx0, mclhd handle, uchar *ptr, ushort siz) { fiolcxdef *ctx = (fiolcxdef *)ctx0; ulong seekpos = (ulong)handle; osfildef *fp = ctx->fiolcxfp; char buf[7]; errcxdef *ec = ctx->fiolcxerr; uint rdsiz; /* figure out what type of object is to be loaded */ osfseek(fp, seekpos + ctx->fiolcxst, OSFSK_SET); if (osfrb(fp, buf, 7)) errsig(ec, ERR_LDGAM); switch(buf[0]) { case TOKSTFUNC: rdsiz = osrp2(buf + 3); break; case TOKSTOBJ: rdsiz = osrp2(buf + 5); break; case TOKSTFWDOBJ: case TOKSTFWDFN: default: errsig(ec, ERR_UNKOTYP); } if (siz < rdsiz) errsig(ec, ERR_LDBIG); if (osfrb(fp, ptr, rdsiz)) errsig(ec, ERR_LDGAM); if (ctx->fiolcxflg & FIOFCRYPT) fioxor(ptr, rdsiz, ctx->fiolcxseed, ctx->fiolcxinc); } /* shut down load-on-demand subsystem (close load file) */ void fiorcls(fiolcxdef *ctx) { if (ctx != 0 && ctx->fiolcxfp != 0) { /* close the file */ osfcls(ctx->fiolcxfp); /* forget the file object */ ctx->fiolcxfp = 0; } } /* * Read an HTMLRES resource map */ static void fiordhtml(errcxdef *ec, osfildef *fp, appctxdef *appctx, int resfileno, const char *resfilename) { uchar buf[256]; /* * resource map - if the host system is interested, tell it about it */ if (appctx != 0) { ulong entry_cnt; ulong i; /* read the index table header */ if (osfrb(fp, buf, 8)) errsig1(ec, ERR_RDRSC, ERRTSTR, errstr(ec, resfilename, strlen(resfilename))); /* get the number of entries in the table */ entry_cnt = osrp4(buf); /* read the index entries */ for (i = 0 ; i < entry_cnt ; ++i) { ulong res_ofs; ulong res_siz; ushort res_namsiz; /* read this entry */ if (osfrb(fp, buf, 10)) errsig1(ec, ERR_RDRSC, ERRTSTR, errstr(ec, resfilename, strlen(resfilename))); /* get the entry header */ res_ofs = osrp4(buf); res_siz = osrp4(buf + 4); res_namsiz = osrp2(buf + 8); /* read this entry's name */ if (osfrb(fp, buf, res_namsiz)) errsig1(ec, ERR_RDRSC, ERRTSTR, errstr(ec, resfilename, strlen(resfilename))); /* tell the host system about this entry */ if (appctx->add_resource) (*appctx->add_resource)(appctx->add_resource_ctx, res_ofs, res_siz, (char *)buf, (size_t)res_namsiz, resfileno); } /* tell the host system where the resources start */ if (appctx->set_resmap_seek != 0) { long pos = osfpos(fp); (*appctx->set_resmap_seek)(appctx->set_resmap_seek_ctx, pos, resfileno); } } } /* * Read an external resource file. This is a limited version of the * general file reader that can only read resource files, not full game * files. */ static void fiordrscext(errcxdef *ec, osfildef *fp, appctxdef *appctx, int resfileno, char *resfilename) { uchar buf[TOKNAMMAX + 50]; unsigned long endpos; unsigned long startofs; /* note the starting offset */ startofs = osfpos(fp); /* check file and version headers, and get flags and timestamp */ if (osfrb(fp, buf, (int)(sizeof(FIOFILHDR) + sizeof(FIOVSNHDR) + 2))) errsig1(ec, ERR_RDRSC, ERRTSTR, errstr(ec, resfilename, strlen(resfilename))); if (memcmp(buf, FIOFILHDRRSC, (size_t)sizeof(FIOFILHDRRSC))) errsig1(ec, ERR_BADHDRRSC, ERRTSTR, errstr(ec, resfilename, strlen(resfilename))); if (memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR, (size_t)sizeof(FIOVSNHDR)) && memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR2, (size_t)sizeof(FIOVSNHDR2)) && memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR3, (size_t)sizeof(FIOVSNHDR3))) errsig(ec, ERR_BADVSN); if (osfrb(fp, buf, (size_t)26)) errsig1(ec, ERR_RDRSC, ERRTSTR, errstr(ec, resfilename, strlen(resfilename))); /* now read resources from the file */ for (;;) { /* read resource type and next-resource pointer */ if (osfrb(fp, buf, 1) || osfrb(fp, buf + 1, (int)(buf[0] + 4))) errsig1(ec, ERR_RDRSC, ERRTSTR, errstr(ec, resfilename, strlen(resfilename))); endpos = osrp4(buf + 1 + buf[0]); /* check the resource type */ if (fioisrsc(buf, "HTMLRES")) { /* read the HTML resource map */ fiordhtml(ec, fp, appctx, resfileno, resfilename); /* * skip the resources - they're entirely for the host * application's use */ osfseek(fp, endpos + startofs, OSFSK_SET); } else if (fioisrsc(buf, "$EOF")) { /* we're done reading the file */ break; } else errsig(ec, ERR_UNKRSC); } } /* * read a game from a binary file * * flags: * &1 ==> run preinit * &2 ==> preload objects */ static void fiord1(mcmcxdef *mctx, voccxdef *vctx, tokcxdef *tctx, osfildef *fp, const char *fname, fiolcxdef *setupctx, ulong startofs, objnum *preinit, uint *flagp, tokpdef *path, uchar **fmtsp, uint *fmtlp, uint *pcntptr, int flags, appctxdef *appctx, char *argv0) { int i; int siz; uchar buf[TOKNAMMAX + 50]; errcxdef *ec = vctx->voccxerr; ulong endpos; int obj; ulong curpos; runxdef *ex; ulong eof_reset = 0; /* reset here at EOF if non-zero */ #if 0 // XFCNs are obsolete int xfcns_done = FALSE; /* already loaded XFCNs */ #endif ulong xfcn_pos = 0; /* location of XFCN's if preloadable */ uint xor_seed = 17; /* seed value for fioxor */ uint xor_inc = 29; /* increment value for fioxor */ /* set up loader callback context */ setupctx->fiolcxfp = fp; setupctx->fiolcxerr = ec; setupctx->fiolcxst = startofs; setupctx->fiolcxseed = xor_seed; setupctx->fiolcxinc = xor_inc; /* check file and version headers, and get flags and timestamp */ if (osfrb(fp, buf, (int)(sizeof(FIOFILHDR) + sizeof(FIOVSNHDR) + 2))) errsig(ec, ERR_RDGAM); if (memcmp(buf, FIOFILHDR, (size_t)sizeof(FIOFILHDR))) errsig(ec, ERR_BADHDR); if (memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR, (size_t)sizeof(FIOVSNHDR)) && memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR2, (size_t)sizeof(FIOVSNHDR2)) && memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR3, (size_t)sizeof(FIOVSNHDR3))) errsig(ec, ERR_BADVSN); if (osfrb(fp, vctx->voccxtim, (size_t)26)) errsig(ec, ERR_RDGAM); /* * if the game wasn't compiled with 2.2 or later, make a note, * because we need to ignore certain property flags (due to a bug in * the old compiler) */ if (memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR2, (size_t)sizeof(FIOVSNHDR2)) == 0 || memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR3, (size_t)sizeof(FIOVSNHDR3)) == 0) mctx->mcmcxflg |= MCMCXF_NO_PRP_DEL; setupctx->fiolcxflg = *flagp = osrp2(buf + sizeof(FIOFILHDR) + sizeof(FIOVSNHDR)); /* now read resources from the file */ for (;;) { /* read resource type and next-resource pointer */ if (osfrb(fp, buf, 1) || osfrb(fp, buf + 1, (int)(buf[0] + 4))) errsig(ec, ERR_RDGAM); endpos = osrp4(buf + 1 + buf[0]); if (fioisrsc(buf, "OBJ")) { /* skip regular objects if fast-load records are included */ if (*flagp & FIOFFAST) { osfseek(fp, endpos + startofs, OSFSK_SET); continue; } curpos = osfpos(fp) - startofs; while (curpos != endpos) { /* read type and object number */ if (osfrb(fp, buf, 3)) errsig(ec, ERR_RDGAM); obj = osrp2(buf+1); switch(buf[0]) { case TOKSTFUNC: case TOKSTOBJ: if (osfrb(fp, buf + 3, 4)) errsig(ec, ERR_RDGAM); mcmrsrv(mctx, (ushort)osrp2(buf + 3), (mcmon)obj, (mclhd)curpos); curpos += osrp2(buf + 5) + 7; /* load object if preloading */ if (flags & 2) { (void)mcmlck(mctx, (mcmon)obj); mcmunlck(mctx, (mcmon)obj); } /* seek past this object */ osfseek(fp, curpos + startofs, OSFSK_SET); break; case TOKSTFWDOBJ: case TOKSTFWDFN: { ushort siz; uchar *p; if (osfrb(fp, buf+3, 2)) errsig(ec, ERR_RDGAM); siz = osrp2(buf+3); p = mcmalonum(mctx, siz, (mcmon)obj); if (osfrb(fp, p, siz)) errsig(ec, ERR_RDGAM); mcmunlck(mctx, (mcmon)obj); curpos += 5 + siz; break; } case TOKSTEXTERN: if (!vctx->voccxrun->runcxext) errsig(ec, ERR_UNXEXT); ex = &vctx->voccxrun->runcxext[obj]; if (osfrb(fp, buf + 3, 1) || osfrb(fp, ex->runxnam, (int)buf[3])) errsig(ec, ERR_RDGAM); ex->runxnam[buf[3]] = '\0'; curpos += buf[3] + 4; break; default: errsig(ec, ERR_UNKOTYP); } } } else if (fioisrsc(buf, "FST")) { uchar *p; uchar *bufp; ulong siz; if (!(*flagp & FIOFFAST)) { osfseek(fp, endpos + startofs, OSFSK_SET); continue; } curpos = osfpos(fp) - startofs; siz = endpos - curpos; if (siz && siz < OSMALMAX && (bufp = p = (uchar *)osmalloc((size_t)siz)) != 0) { uchar *p1; ulong siz2; uint sizcur; for (p1 = p, siz2 = siz ; siz2 ; siz2 -= sizcur, p1 += sizcur) { sizcur = (siz2 > (uint)0xffff ? (uint)0xffff : siz2); if (osfrb(fp, p1, sizcur)) errsig(ec, ERR_RDGAM); } while (siz) { obj = osrp2(p + 1); switch(*p) { case TOKSTFUNC: case TOKSTOBJ: mcmrsrv(mctx, (ushort)osrp2(p + 3), (mcmon)obj, (mclhd)osrp4(p + 7)); p += 11; siz -= 11; /* preload object if desired */ if (flags & 2) { (void)mcmlck(mctx, (mcmon)obj); mcmunlck(mctx, (mcmon)obj); } break; case TOKSTEXTERN: if (!vctx->voccxrun->runcxext) errsig(ec, ERR_UNXEXT); ex = &vctx->voccxrun->runcxext[obj]; memcpy(ex->runxnam, p + 4, (size_t)p[3]); ex->runxnam[p[3]] = '\0'; siz -= p[3] + 4; p += p[3] + 4; break; default: errsig(ec, ERR_UNKOTYP); } } /* done with temporary block; free it */ osfree(bufp); osfseek(fp, endpos + startofs, OSFSK_SET); } else { while (curpos != endpos) { if (osfrb(fp, buf, 3)) errsig(ec, ERR_RDGAM); obj = osrp2(buf + 1); switch(buf[0]) { case TOKSTFUNC: case TOKSTOBJ: if (osfrb(fp, buf + 3, 8)) errsig(ec, ERR_RDGAM); mcmrsrv(mctx, (ushort)osrp2(buf + 3), (mcmon)obj, (mclhd)osrp4(buf + 7)); curpos += 11; /* preload object if desired */ if (flags & 2) { (void)mcmlck(mctx, (mcmon)obj); mcmunlck(mctx, (mcmon)obj); osfseek(fp, curpos + startofs, OSFSK_SET); } break; case TOKSTEXTERN: if (!vctx->voccxrun->runcxext) errsig(ec, ERR_UNXEXT); ex = &vctx->voccxrun->runcxext[obj]; if (osfrb(fp, buf + 3, 1) || osfrb(fp, ex->runxnam, (int)buf[3])) errsig(ec, ERR_RDGAM); ex->runxnam[buf[3]] = '\0'; curpos += buf[3] + 4; break; default: errsig(ec, ERR_UNKOTYP); } } } /* if we can preload xfcn's, do so now */ if (xfcn_pos) { eof_reset = endpos; /* remember to return here when done */ osfseek(fp, xfcn_pos, OSFSK_SET); /* go to xfcn's */ } } else if (fioisrsc(buf, "XFCN")) { if (!vctx->voccxrun->runcxext) errsig(ec, ERR_UNXEXT); /* read length and name of resource */ if (osfrb(fp, buf, 3) || osfrb(fp, buf + 3, (int)buf[2])) errsig(ec, ERR_RDGAM); siz = osrp2(buf); #if 0 /* * external functions are now obsolete - do not load */ /* look for an external function with the same name */ for (i = vctx->voccxrun->runcxexc, ex = vctx->voccxrun->runcxext ; i ; ++ex, --i) { j = strlen(ex->runxnam); if (j == buf[2] && !memcmp(buf + 3, ex->runxnam, (size_t)j)) break; } /* if we found an external function of this name, load it */ if (i && !xfcns_done) { /* load the function */ ex->runxptr = os_exfld(fp, (unsigned)siz); } else { /* this XFCN isn't used; don't bother loading it */ osfseek(fp, endpos + startofs, OSFSK_SET); } #else /* external functions are obsolete; simply skip the data */ osfseek(fp, endpos + startofs, OSFSK_SET); #endif } else if (fioisrsc(buf, "HTMLRES")) { /* read the resources */ fiordhtml(ec, fp, appctx, 0, fname); /* * skip the resources - they're entirely for the host * application's use */ osfseek(fp, endpos + startofs, OSFSK_SET); } else if (fioisrsc(buf, "INH")) { uchar *p; uchar *bufp; ulong siz; /* do it in a single file read, if we can, for speed */ curpos = osfpos(fp) - startofs; siz = endpos - curpos; if (siz && siz < OSMALMAX && (bufp = p = (uchar *)osmalloc((size_t)siz)) != 0) { uchar *p1; ulong siz2; uint sizcur; for (p1 = p, siz2 = siz ; siz2 ; siz2 -= sizcur, p1 += sizcur) { sizcur = (siz2 > (uint)0xffff ? (uint)0xffff : siz2); if (osfrb(fp, p1, sizcur)) errsig(ec, ERR_RDGAM); } while (siz) { i = osrp2(p + 7); obj = osrp2(p + 1); vociadd(vctx, (objnum)obj, (objnum)osrp2(p+3), i, (objnum *)(p + 9), p[0] | VOCIFXLAT); vocinh(vctx, obj)->vociilc = osrp2(p + 5); p += 9 + (2 * i); siz -= 9 + (2 * i); } /* done with temporary block; free it */ osfree(bufp); } else { while (curpos != endpos) { if (osfrb(fp, buf, 9)) errsig(ec, ERR_RDGAM); i = osrp2(buf + 7); /* get number of superclasses */ obj = osrp2(buf + 1); /* get object number */ if (i && osfrb(fp, buf + 9, 2 * i)) errsig(ec, ERR_RDGAM); vociadd(vctx, (objnum)obj, (objnum)osrp2(buf+3), i, (objnum *)(buf + 9), buf[0] | VOCIFXLAT); vocinh(vctx, obj)->vociilc = osrp2(buf + 5); curpos += 9 + (2 * i); } } } else if (fioisrsc(buf, "REQ")) { curpos = osfpos(fp) - startofs; siz = endpos - curpos; if (osfrb(fp, buf, (uint)siz)) errsig(ec, ERR_RDGAM); vctx->voccxme = vctx->voccxme_init = osrp2(buf); vctx->voccxvtk = osrp2(buf+2); vctx->voccxstr = osrp2(buf+4); vctx->voccxnum = osrp2(buf+6); vctx->voccxprd = osrp2(buf+8); vctx->voccxvag = osrp2(buf+10); vctx->voccxini = osrp2(buf+12); vctx->voccxpre = osrp2(buf+14); vctx->voccxper = osrp2(buf+16); /* if we have a cmdPrompt function, read it */ if (siz >= 20) vctx->voccxprom = osrp2(buf + 18); else vctx->voccxprom = MCMONINV; /* if we have the NLS functions, read them */ if (siz >= 26) { vctx->voccxpdis = osrp2(buf + 20); vctx->voccxper2 = osrp2(buf + 22); vctx->voccxpdef = osrp2(buf + 24); } else { /* the new NLS functions aren't defined in this file */ vctx->voccxpdis = MCMONINV; vctx->voccxper2 = MCMONINV; vctx->voccxpdef = MCMONINV; } /* test for parseAskobj separately, as it was added later */ if (siz >= 28) vctx->voccxpask = osrp2(buf + 26); else vctx->voccxpask = MCMONINV; /* test for preparseCmd separately - it's another late comer */ if (siz >= 30) vctx->voccxppc = osrp2(buf + 28); else vctx->voccxppc = MCMONINV; /* check for parseAskobjActor separately - another late comer */ if (siz >= 32) vctx->voccxpask2 = osrp2(buf + 30); else vctx->voccxpask2 = MCMONINV; /* if we have parseErrorParam, read it as well */ if (siz >= 34) { vctx->voccxperp = osrp2(buf + 32); } else { /* parseErrorParam isn't defined in this file */ vctx->voccxperp = MCMONINV; } /* * if we have commandAfterRead and initRestore, read them as * well */ if (siz >= 38) { vctx->voccxpostprom = osrp2(buf + 34); vctx->voccxinitrestore = osrp2(buf + 36); } else { /* these new functions aren't defined in this game */ vctx->voccxpostprom = MCMONINV; vctx->voccxinitrestore = MCMONINV; } /* check for and read parseUnknownVerb, parseNounPhrase */ if (siz >= 42) { vctx->voccxpuv = osrp2(buf + 38); vctx->voccxpnp = osrp2(buf + 40); } else { vctx->voccxpuv = MCMONINV; vctx->voccxpnp = MCMONINV; } /* check for postAction, endCommand */ if (siz >= 48) { vctx->voccxpostact = osrp2(buf + 42); vctx->voccxendcmd = osrp2(buf + 44); vctx->voccxprecmd = osrp2(buf + 46); } else { vctx->voccxpostact = MCMONINV; vctx->voccxendcmd = MCMONINV; vctx->voccxprecmd = MCMONINV; } /* check for parseAskobjIndirect */ if (siz >= 50) vctx->voccxpask3 = osrp2(buf + 48); else vctx->voccxpask3 = MCMONINV; /* check for preparseExt and parseDefaultExt */ if (siz >= 54) { vctx->voccxpre2 = osrp2(buf + 50); vctx->voccxpdef2 = osrp2(buf + 52); } else { vctx->voccxpre2 = MCMONINV; vctx->voccxpdef2 = MCMONINV; } } else if (fioisrsc(buf, "VOC")) { uchar *p; uchar *bufp; ulong siz; int len1; int len2; /* do it in a single file read, if we can, for speed */ curpos = osfpos(fp) - startofs; siz = endpos - curpos; if (siz && siz < OSMALMAX && (bufp = p = (uchar *)osmalloc((size_t)siz)) != 0) { uchar *p1; ulong siz2; uint sizcur; for (p1 = p, siz2 = siz ; siz2 ; siz2 -= sizcur, p1 += sizcur) { sizcur = (siz2 > (uint)0xffff ? (uint)0xffff : siz2); if (osfrb(fp, p1, sizcur)) errsig(ec, ERR_RDGAM); } while (siz) { len1 = osrp2(p); len2 = osrp2(p + 2); if (*flagp & FIOFCRYPT) fioxor(p + 10, (uint)(len1 + len2), xor_seed, xor_inc); vocadd2(vctx, (prpnum)osrp2(p+4), (objnum)osrp2(p+6), osrp2(p+8), p + 10, len1, (len2 ? p + 10 + len1 : (uchar*)0), len2); p += 10 + len1 + len2; siz -= 10 + len1 + len2; } /* done with the temporary block; free it up */ osfree(bufp); } else { /* can't do it in one file read; do it the slow way */ while (curpos != endpos) { if (osfrb(fp, buf, 10) || osfrb(fp, buf + 10, (len1 = osrp2(buf)) + (len2 = osrp2(buf + 2)))) errsig(ec, ERR_RDGAM); if (*flagp & FIOFCRYPT) fioxor(buf + 10, (uint)(len1 + len2), xor_seed, xor_inc); vocadd2(vctx, (prpnum)osrp2(buf+4), (objnum)osrp2(buf+6), osrp2(buf+8), buf + 10, len1, (len2 ? buf + 10 + len1 : (uchar*)0), len2); curpos += 10 + len1 + len2; } } } else if (fioisrsc(buf, "FMTSTR")) { uchar *fmts; uint fmtl; if (osfrb(fp, buf, 2)) errsig(ec, ERR_RDGAM); fmtl = osrp2(buf); fmts = mchalo(vctx->voccxerr, fmtl, "fiord1"); if (osfrb(fp, fmts, fmtl)) errsig(ec, ERR_RDGAM); if (*flagp & FIOFCRYPT) fioxor(fmts, fmtl, xor_seed, xor_inc); tiosetfmt(vctx->voccxtio, vctx->voccxrun, fmts, fmtl); if (fmtsp) *fmtsp = fmts; if (fmtlp) *fmtlp = fmtl; } else if (fioisrsc(buf, "CMPD")) { if (osfrb(fp, buf, 2)) errsig(ec, ERR_RDGAM); vctx->voccxcpl = osrp2(buf); vctx->voccxcpp = (char *)mchalo(vctx->voccxerr, vctx->voccxcpl, "fiord1"); if (osfrb(fp, vctx->voccxcpp, (uint)vctx->voccxcpl)) errsig(ec, ERR_RDGAM); if (*flagp & FIOFCRYPT) fioxor((uchar *)vctx->voccxcpp, (uint)vctx->voccxcpl, xor_seed, xor_inc); } else if (fioisrsc(buf, "SPECWORD")) { if (osfrb(fp, buf, 2)) errsig(ec, ERR_RDGAM); vctx->voccxspl = osrp2(buf); vctx->voccxspp = (char *)mchalo(vctx->voccxerr, vctx->voccxspl, "fiord1"); if (osfrb(fp, vctx->voccxspp, (uint)vctx->voccxspl)) errsig(ec, ERR_RDGAM); if (*flagp & FIOFCRYPT) fioxor((uchar *)vctx->voccxspp, (uint)vctx->voccxspl, xor_seed, xor_inc); } else if (fioisrsc(buf, "SYMTAB")) { tokthdef *symtab; /* if there's no debugger context, don't bother with this */ if (!vctx->voccxrun->runcxdbg) { osfseek(fp, endpos + startofs, OSFSK_SET); continue; } if (!(symtab = vctx->voccxrun->runcxdbg->dbgcxtab)) { symtab = (tokthdef *)mchalo(ec, sizeof(tokthdef), "fiord:symtab"); tokthini(ec, mctx, (toktdef *)symtab); vctx->voccxrun->runcxdbg->dbgcxtab = symtab; } /* read symbols until we find a zero-length symbol */ for (;;) { int hash; if (osfrb(fp, buf, 4)) errsig(ec, ERR_RDGAM); if (buf[0] == 0) break; if (osfrb(fp, buf + 4, (int)buf[0])) errsig(ec, ERR_RDGAM); buf[4 + buf[0]] = '\0'; hash = tokhsh((char *)buf + 4); (*symtab->tokthsc.toktfadd)((toktdef *)symtab, (char *)buf + 4, (int)buf[0], (int)buf[1], osrp2(buf + 2), hash); } } else if (fioisrsc(buf, "SRC")) { /* skip source file id's if there's no debugger context */ if (vctx->voccxrun->runcxdbg == 0) { osfseek(fp, endpos + startofs, OSFSK_SET); continue; } while ((osfpos(fp) - startofs) != endpos) { /* the only thing we know how to read is linfdef's */ if (linfload(fp, vctx->voccxrun->runcxdbg, ec, path)) errsig(ec, ERR_RDGAM); } } else if (fioisrsc(buf, "SRC2")) { /* * this is simply a marker indicating that we have new-style * (line-number-based) source debugging information in the * file -- set the new-style debug info flag */ if (vctx->voccxrun->runcxdbg != 0) vctx->voccxrun->runcxdbg->dbgcxflg |= DBGCXFLIN2; /* the contents are empty - skip the block */ osfseek(fp, endpos + startofs, OSFSK_SET); } else if (fioisrsc(buf, "PREINIT")) { if (osfrb(fp, buf, 2)) errsig(ec, ERR_RDGAM); *preinit = osrp2(buf); } else if (fioisrsc(buf, "ERRMSG")) { errini(ec, fp); osfseek(fp, endpos + startofs, OSFSK_SET); } else if (fioisrsc(buf, "EXTCNT")) { uchar *p; ushort len; ulong siz; curpos = osfpos(fp) - startofs; siz = endpos - curpos; if (osfrb(fp, buf, 2)) errsig(ec, ERR_RDGAM); i = osrp2(buf); len = i * sizeof(runxdef); p = mchalo(ec, len, "fiord:runxdef"); memset(p, 0, (size_t)len); vctx->voccxrun->runcxext = (runxdef *)p; vctx->voccxrun->runcxexc = i; /* see if start-of-XFCN information is present */ if (siz >= 6) { /* get location of first XFCN, and seek there */ if (osfrb(fp, buf, 4)) errsig(ec, ERR_RDGAM); xfcn_pos = osrp4(buf); } /* seek past this resource */ osfseek(fp, endpos + startofs, OSFSK_SET); } else if (fioisrsc(buf, "PRPCNT")) { if (osfrb(fp, buf, 2)) errsig(ec, ERR_RDGAM); if (pcntptr) *pcntptr = osrp2(buf); } else if (fioisrsc(buf, "TADSPP") && tctx != 0) { tok_read_defines(tctx, fp, ec); } else if (fioisrsc(buf, "XSI")) { if (osfrb(fp, buf, 2)) errsig(ec, ERR_RDGAM); setupctx->fiolcxseed = xor_seed = buf[0]; setupctx->fiolcxinc = xor_inc = buf[1]; osfseek(fp, endpos + startofs, OSFSK_SET); } else if (fioisrsc(buf, "CHRSET")) { size_t len; /* read the character set ID and LDESC */ if (osfrb(fp, buf, 6) || (len = osrp2(buf+4)) > CMAP_LDESC_MAX_LEN || osfrb(fp, buf+6, len)) errsig(ec, ERR_RDGAM); /* establish this character set mapping */ buf[4] = '\0'; cmap_set_game_charset(ec, (char *)buf, (char *)buf + 6, argv0); } else if (fioisrsc(buf, "$EOF")) { if (eof_reset) { osfseek(fp, eof_reset, OSFSK_SET); /* back after EXTCNT */ eof_reset = 0; /* really done at next EOF */ #if 0 // XFCNs are obsolete xfcns_done = TRUE; /* don't do XFCNs again */ #endif } else break; } else errsig(ec, ERR_UNKRSC); } } /* read binary file */ void fiord(mcmcxdef *mctx, voccxdef *vctx, tokcxdef *tctx, char *fname, char *exename, fiolcxdef *setupctx, objnum *preinit, uint *flagp, tokpdef *path, uchar **fmtsp, uint *fmtlp, uint *pcntptr, int flags, struct appctxdef *appctx, char *argv0) { osfildef *fp; ulong startofs; char *display_fname; /* presume there will be no need to run preinit */ *preinit = MCMONINV; /* * get the display filename - use the real filename if one is * provided, otherwise use the name of the executable file itself */ display_fname = (fname != 0 ? fname : exename); /* save the filename in G_os_gamename */ if (display_fname != 0) { size_t copylen; /* limit the copy to the buffer size */ if ((copylen = strlen(display_fname)) > sizeof(G_os_gamename) - 1) copylen = sizeof(G_os_gamename) - 1; /* save it */ memcpy(G_os_gamename, display_fname, copylen); G_os_gamename[copylen] = '\0'; } else G_os_gamename[0] = '\0'; /* open the file and read and check file header */ fp = (fname != 0 ? osfoprb(fname, OSFTGAME) : os_exeseek(exename, "TGAM")); if (fp == 0) errsig(vctx->voccxerr, ERR_OPRGAM); /* * we've identified the .GAM file source - tell the host system * about it, if it's interested */ if (appctx != 0 && appctx->set_game_name != 0) (*appctx->set_game_name)(appctx->set_game_name_ctx, display_fname); /* remember starting location in file */ startofs = osfpos(fp); ERRBEGIN(vctx->voccxerr) /* * Read the game file. Note that the .GAM file always has resource * file number zero. */ fiord1(mctx, vctx, tctx, fp, display_fname, setupctx, startofs, preinit, flagp, path, fmtsp, fmtlp, pcntptr, flags, appctx, argv0); /* * If the host system accepts additional resource files, look for * additional resource files. These are files in the same directory * as the .GAM file, with the .GAM suffix replaced by suffixes from *. RS0 to .RS9. */ if (appctx != 0 && appctx->add_resfile != 0) { char suffix_lc[4]; char suffix_uc[4]; int i; char *base_name; /* use the game or executable filename, as appropriate */ base_name = display_fname; /* build the initial suffixes - try both upper- and lower-case */ suffix_uc[0] = 'R'; suffix_uc[1] = 'S'; suffix_uc[3] = '\0'; suffix_lc[0] = 'r'; suffix_lc[1] = 's'; suffix_lc[3] = '\0'; /* loop through each possible suffix (.RS0 through .RS9) */ for (i = 0 ; i < 9 ; ++i) { char resname[OSFNMAX]; osfildef *fpres; int resfileno; /* * Build the next resource filename. If there's an explicit * resource path, use it, otherwise use the same directory * that contains the .GAM file. */ if (appctx->ext_res_path != 0) { /* * There's an explicit resource path - append the root * (filename-only, minus path) portion of the .GAM file * name to the resource path. */ os_build_full_path(resname, sizeof(resname), appctx->ext_res_path, os_get_root_name(base_name)); } else { /* * there's no resource path - use the entire .GAM * filename, including directory, so that we look in the * same directory that contains the .GAM file */ if (base_name != 0) strcpy(resname, base_name); else resname[0] = '\0'; } /* add the current extension (replacing any current extension) */ os_remext(resname); suffix_lc[2] = suffix_uc[2] = '0' + i; os_addext(resname, suffix_lc); /* try opening the file */ fpres = osfoprb(resname, OSFTGAME); /* if that didn't work, try the upper-case name */ if (fpres == 0) { /* replace the suffix with the upper-case version */ os_remext(resname); os_addext(resname, suffix_uc); /* try again with the new name */ fpres = osfoprb(resname, OSFTGAME); } /* if we opened it successfully, read it */ if (fpres != 0) { /* tell the host system about it */ resfileno = (*appctx->add_resfile) (appctx->add_resfile_ctx, resname); /* read the file */ fiordrscext(vctx->voccxerr, fpres, appctx, resfileno, resname); /* we're done with the file, so close it */ osfcls(fpres); } } } ERRCLEAN(vctx->voccxerr) /* if an error occurs during read, clean up by closing the file */ osfcls(fp); ERRENDCLN(vctx->voccxerr); } /* save game header */ #define FIOSAVHDR "TADS2 save\012\015\032" /* save game header prefix - .GAM file information */ #define FIOSAVHDR_PREFIX "TADS2 save/g\012\015\032" /* * Saved game format version string - note that the length of the * version string must be fixed, so when this is updated, it must be * updated to another string of the same length. This should be updated * whenever a change is made to the format that can't be otherwise * detected from the data stream in the saved game file. */ #define FIOSAVVSN "v2.2.1" /* old saved game format version strings */ #define FIOSAVVSN1 "v2.2.0" /* read fuse/daemon/alarm record */ static int fiorfda(osfildef *fp, vocddef *p, uint cnt) { vocddef *q; uint i; uchar buf[14]; /* start by clearing out entire record */ for (i = 0, q = p ; i < cnt ; ++q, ++i) q->vocdfn = MCMONINV; /* now restore all the records from the file */ for (;;) { /* read a record, and quit if it's the last one */ if (osfrb(fp, buf, 13)) return(TRUE); if ((i = osrp2(buf)) == 0xffff) return(FALSE); /* restore this record */ q = p + i; q->vocdfn = osrp2(buf+2); q->vocdarg.runstyp = buf[4]; switch(buf[4]) { case DAT_NUMBER: q->vocdarg.runsv.runsvnum = osrp4s(buf+5); break; case DAT_OBJECT: case DAT_FNADDR: q->vocdarg.runsv.runsvobj = osrp2(buf+5); break; case DAT_PROPNUM: q->vocdarg.runsv.runsvprp = osrp2(buf+5); break; } q->vocdprp = osrp2(buf+9); q->vocdtim = osrp2(buf+11); } } /* * Look in a saved game file to determine if it has information on which * GAM file created it. If the GAM file information is available, this * routine returns true and stores the game file name in the given * buffer; if the information isn't available, we'll return false. */ int fiorso_getgame(char *saved_file, char *fnamebuf, size_t buflen) { osfildef *fp; uint namelen; char buf[sizeof(FIOSAVHDR_PREFIX) + 2]; /* open the input file */ if (!(fp = osfoprb(saved_file, OSFTSAVE))) return FALSE; /* read the prefix header and check */ if (osfrb(fp, buf, (int)(sizeof(FIOSAVHDR_PREFIX) + 2)) || memcmp(buf, FIOSAVHDR_PREFIX, sizeof(FIOSAVHDR_PREFIX)) != 0) { /* * there's no game file information - close the file and * indicate that we have no information */ osfcls(fp); return FALSE; } /* get the length of the filename */ namelen = osrp2(buf + sizeof(FIOSAVHDR_PREFIX)); if (namelen > buflen - 1) namelen = buflen - 1; /* read the filename */ if (osfrb(fp, fnamebuf, namelen)) { osfcls(fp); return FALSE; } /* null-terminate the string */ fnamebuf[namelen] = '\0'; /* done with the file */ osfcls(fp); /* indicate that we found the information */ return TRUE; } /* restore game: returns TRUE on failure */ int fiorso(voccxdef *vctx, char *fname) { osfildef *fp; objnum obj; uchar *p; uchar *mut; uint mutsiz; uint oldmutsiz; int propcnt; mcmcxdef *mctx = vctx->voccxmem; uchar buf[sizeof(FIOSAVHDR) + sizeof(FIOSAVVSN)]; ushort newsiz; int err = FALSE; char timestamp[26]; int version = 0; /* version ID - 0 = current version */ int result; /* presume success */ result = FIORSO_SUCCESS; /* open the input file */ if (!(fp = osfoprb(fname, OSFTSAVE))) return FIORSO_FILE_NOT_FOUND; /* check for a prefix header - if it's there, skip it */ if (!osfrb(fp, buf, (int)(sizeof(FIOSAVHDR_PREFIX) + 2)) && memcmp(buf, FIOSAVHDR_PREFIX, sizeof(FIOSAVHDR_PREFIX)) == 0) { ulong skip_len; /* * The prefix header is present - skip it. The 2-byte value * following the header is the length of the prefix data block * (not including the header), so simply skip the additional * number of bytes specified. */ skip_len = (ulong)osrp2(buf + sizeof(FIOSAVHDR_PREFIX)); osfseek(fp, skip_len, OSFSK_CUR); } else { /* * there's no prefix header - seek back to the start of the file * and read the standard header information */ osfseek(fp, 0, OSFSK_SET); } /* read headers and check */ if (osfrb(fp, buf, (int)(sizeof(FIOSAVHDR) + sizeof(FIOSAVVSN))) || memcmp(buf, FIOSAVHDR, (size_t)sizeof(FIOSAVHDR))) { /* it's not a saved game file */ result = FIORSO_NOT_SAVE_FILE; goto ret_error; } /* check the version string */ if (memcmp(buf + sizeof(FIOSAVHDR), FIOSAVVSN, (size_t)sizeof(FIOSAVVSN)) == 0) { /* it's the current version */ version = 0; } else if (memcmp(buf + sizeof(FIOSAVHDR), FIOSAVVSN1, (size_t)sizeof(FIOSAVVSN1)) == 0) { /* it's old version #1 */ version = 1; } else { /* * this isn't a recognized version - the file must have been * saved by a newer version of the system, so we can't assume we * will be able to parse the format */ result = FIORSO_BAD_FMT_VSN; goto ret_error; } /* * Read timestamp and check - the game must have been saved by the * same .GAM file that we are now running, because the .SAV file is * written entirely in terms of the contents of the .GAM file; any * change in the .GAM file invalidates the .SAV file. */ if (osfrb(fp, timestamp, 26) || memcmp(timestamp, vctx->voccxtim, (size_t)26)) { result = FIORSO_BAD_GAME_VSN; goto ret_error; } /* first revert every object to original (post-compilation) state */ vocrevert(vctx); /* * the most common error from here on is simply a file read error, * so presume that this is what will happen; if we are successful or * encounter a different error, we'll change the status at that * point */ result = FIORSO_READ_ERROR; /* go through file and load changed objects */ for (;;) { /* get the header */ if (osfrb(fp, buf, 7)) goto ret_error; /* get the object number from the header, and stop if we're done */ obj = osrp2(buf+1); if (obj == MCMONINV) break; /* if the object was dynamically allocated, recreate it */ if (buf[0] == 1) { int sccnt; objnum sc; /* create the object */ mutsiz = osrp2(buf + 3); p = mcmalonum(mctx, (ushort)mutsiz, (mcmon)obj); /* read the object's contents */ if (osfrb(fp, p, mutsiz)) goto ret_error; /* get the superclass data (at most one superclass) */ sccnt = objnsc(p); if (sccnt) sc = osrp2(objsc(p)); /* create inheritance records for the object */ vociadd(vctx, obj, MCMONINV, sccnt, &sc, VOCIFNEW | VOCIFVOC); #if 0 { int wrdcnt; /* read the object's vocabulary and add it back */ if (osfrb(fp, buf, 2)) goto ret_error; wrdcnt = osrp2(buf); while (wrdcnt--) { int len1; int len2; char wrd[80]; /* read the header */ if (osfrb(fp, buf, 6)) goto ret_error; len1 = osrp2(buf+2); len2 = osrp2(buf+4); /* read the word text */ if (osfrb(fp, wrd, len1 + len2)) goto ret_error; /* add the word */ vocadd2(vctx, buf[0], obj, buf[1], wrd, len1, wrd + len1, len2); } } #endif } else { /* get the remaining data from the header */ propcnt = osrp2(buf + 3); mutsiz = osrp2(buf + 5); /* expand object if it's not big enough for mutsiz */ p = mcmlck(mctx, (mcmon)obj); oldmutsiz = mcmobjsiz(mctx, (mcmon)obj) - objrst(p); if (oldmutsiz < mutsiz) { newsiz = mutsiz - oldmutsiz; p = (uchar *)objexp(mctx, obj, &newsiz); } /* reset statistics, and read mutable part from file */ mut = p + objrst(p); objsnp(p, propcnt); objsfree(p, mutsiz + objrst(p)); if (osfrb(fp, mut, mutsiz)) err = TRUE; /* reset ignore flags as needed */ objsetign(mctx, obj); } /* touch and unlock the object */ mcmtch(mctx, (mcmon)obj); mcmunlck(mctx, (mcmon)obj); if (err) goto ret_error; } /* read fuses/daemons/alarms */ if (fiorfda(fp, vctx->voccxdmn, vctx->voccxdmc) || fiorfda(fp, vctx->voccxfus, vctx->voccxfuc) || fiorfda(fp, vctx->voccxalm, vctx->voccxalc)) goto ret_error; /* read the dynamically added and deleted vocabulary */ for (;;) { int len1; int len2; char wrd[80]; int flags; int typ; /* read the header */ if (osfrb(fp, buf, 8)) goto ret_error; typ = buf[0]; flags = buf[1]; len1 = osrp2(buf+2); len2 = osrp2(buf+4); obj = osrp2(buf+6); /* check to see if this is the end marker */ if (obj == MCMONINV) break; /* read the word text */ if (osfrb(fp, wrd+2, len1)) goto ret_error; if (len2) { wrd[len1 + 2] = ' '; if (osfrb(fp, &wrd[len1 + 3], len2)) goto ret_error; oswp2(wrd, len1 + len2 + 3); } else oswp2(wrd, len1 + 2); /* add or delete the word as appropriate */ if (flags & VOCFDEL) vocdel1(vctx, obj, (char *)wrd, (prpnum)typ, FALSE, FALSE, FALSE); else vocadd2(vctx, buf[0], obj, buf[1], (uchar *)wrd+2, len1, (uchar *)wrd+len1, len2); } /* * the following was added in save format version "v2.2.1", so skip * it if the save version is older than that */ if (version != 1) { /* read the current "Me" object */ if (osfrb(fp, buf, 2)) goto ret_error; vctx->voccxme = osrp2(buf); } /* done - close file and return success indication */ osfcls(fp); return FIORSO_SUCCESS; /* come here on failure - close file and return error indication */ ret_error: osfcls(fp); return result; } /* write fuse/daemon/alarm block */ static int fiowfda(osfildef *fp, vocddef *p, uint cnt) { uchar buf[14]; uint i; for (i = 0 ; i < cnt ; ++i, ++p) { if (p->vocdfn == MCMONINV) continue; /* not set - ignore */ oswp2(buf, i); /* element in array to be set */ oswp2(buf+2, p->vocdfn); /* object number for function/target */ buf[4] = p->vocdarg.runstyp; /* type of argument */ switch(buf[4]) { case DAT_NUMBER: oswp4s(buf+5, p->vocdarg.runsv.runsvnum); break; case DAT_OBJECT: case DAT_FNADDR: oswp2(buf+5, p->vocdarg.runsv.runsvobj); break; case DAT_PROPNUM: oswp2(buf+5, p->vocdarg.runsv.runsvprp); break; } oswp2(buf+9, p->vocdprp); oswp2(buf+11, p->vocdtim); /* write this record to file */ if (osfwb(fp, buf, 13)) return(TRUE); } /* write end record - -1 for array element number */ oswp2(buf, 0xffff); return(osfwb(fp, buf, 13)); } /* context for vocabulary saver callback function */ struct fiosav_cb_ctx { int err; osfildef *fp; }; #ifdef NEVER /* * callback for vocabulary saver - called by voc_iterate for each word * defined for a particular object, allowing us to write all the words * attached to a dynamically allocated object to the save file */ static void fiosav_cb(struct fiosav_cb_ctx *ctx, vocdef *voc, vocwdef *vocw) { char buf[10]; /* write the part of speech, flags, and word lengths */ buf[0] = vocw->vocwtyp; buf[1] = vocw->vocwflg; oswp2(buf+2, voc->voclen); oswp2(buf+4, voc->vocln2); if (osfwb(ctx->fp, buf, 6)) ctx->err = TRUE; /* write the words */ if (osfwb(ctx->fp, voc->voctxt, voc->voclen + voc->vocln2)) ctx->err = TRUE; } #endif /* * Callback for vocabulary saver - called by voc_iterate for every * word. We'll write the word if it was dynamically added or deleted, * so that we can restore that status when the game is restored. */ static void fiosav_voc_cb(void *ctx0, vocdef *voc, vocwdef *vocw) { struct fiosav_cb_ctx *ctx = (struct fiosav_cb_ctx *)ctx0; char buf[10]; /* if the word was dynamically allocated or deleted, save it */ if ((vocw->vocwflg & VOCFNEW) || (vocw->vocwflg & VOCFDEL)) { /* write the header information */ buf[0] = vocw->vocwtyp; buf[1] = vocw->vocwflg; oswp2(buf+2, voc->voclen); oswp2(buf+4, voc->vocln2); oswp2(buf+6, vocw->vocwobj); if (osfwb(ctx->fp, buf, 8)) ctx->err = TRUE; /* write the words */ if (osfwb(ctx->fp, voc->voctxt, voc->voclen + voc->vocln2)) ctx->err = TRUE; } } /* save game; returns TRUE on failure */ int fiosav(voccxdef *vctx, char *fname, char *game_fname) { osfildef *fp; vocidef ***vpg; vocidef **v; int i; int j; objnum obj; uchar *p; uchar *mut; uint mutsiz; int propcnt; mcmcxdef *mctx = vctx->voccxmem; uchar buf[8]; int err = FALSE; struct fiosav_cb_ctx fnctx; /* open the output file */ if ((fp = osfopwb(fname, OSFTSAVE)) == 0) return TRUE; /* * If we have game file information, save the game file information * with the saved game file. This lets the player start the * run-time and restore the game by specifying only the saved game * file. */ if (game_fname != 0) { size_t len; /* write the prefix header */ len = strlen(game_fname); oswp2(buf, len); if (osfwb(fp, FIOSAVHDR_PREFIX, (int)sizeof(FIOSAVHDR_PREFIX)) || osfwb(fp, buf, 2) || osfwb(fp, game_fname, (int)len)) goto ret_error; } /* write save game header and timestamp */ if (osfwb(fp, FIOSAVHDR, (int)sizeof(FIOSAVHDR)) || osfwb(fp, FIOSAVVSN, (int)sizeof(FIOSAVVSN)) || osfwb(fp, vctx->voccxtim, 26)) goto ret_error; /* go through each object, and write if it's been changed */ for (vpg = vctx->voccxinh, i = 0 ; i < VOCINHMAX ; ++vpg, ++i) { if (!*vpg) continue; for (v = *vpg, obj = (i << 8), j = 0 ; j < 256 ; ++v, ++obj, ++j) { if (*v != 0) { /* write object if it's dirty */ if (mcmobjdirty(mctx, (mcmon)obj)) { p = mcmlck(mctx, (mcmon)obj); mut = p + objrst(p); propcnt = objnprop(p); mutsiz = objfree(p) - objrst(p); if ((objflg(p) & OBJFINDEX) != 0) mutsiz += propcnt * 4; /* * If the object was dynamically allocated, write * the whole object. Otherwise, write just the * mutable part. */ if ((*v)->vociflg & VOCIFNEW) { /* indicate that the object is dynamic */ buf[0] = 1; oswp2(buf + 1, obj); /* write the entire object */ mutsiz = objfree(p); oswp2(buf + 3, mutsiz); if (osfwb(fp, buf, 7) || osfwb(fp, p, mutsiz)) err = TRUE; #ifdef NEVER { int wrdcnt; /* count the words, and write the count */ voc_count(vctx, obj, 0, &wrdcnt, (int *)0); oswp2(buf, wrdcnt); if (osfwb(fp, buf, 2)) err = TRUE; /* write the words */ fnctx.err = 0; fnctx.fp = fp; voc_iterate(vctx, obj, fiosav_cb, &fnctx); if (fnctx.err != 0) err = TRUE; } #endif } else if (mutsiz) { /* write number of properties, size of mut, and mut */ buf[0] = 0; /* indicate that the object is static */ oswp2(buf + 1, obj); oswp2(buf + 3, propcnt); oswp2(buf + 5, mutsiz); if (osfwb(fp, buf, 7) || osfwb(fp, mut, mutsiz)) err = TRUE; } mcmunlck(mctx, (mcmon)obj); if (err != 0) goto ret_error; } } } } /* write end-of-objects indication */ buf[0] = 0; oswp2(buf + 1, MCMONINV); oswp4(buf + 3, 0); if (osfwb(fp, buf, 7)) goto ret_error; /* write fuses/daemons/alarms */ if (fiowfda(fp, vctx->voccxdmn, vctx->voccxdmc) || fiowfda(fp, vctx->voccxfus, vctx->voccxfuc) || fiowfda(fp, vctx->voccxalm, vctx->voccxalc)) goto ret_error; /* write run-time vocabulary additions and deletions */ fnctx.fp = fp; fnctx.err = 0; voc_iterate(vctx, MCMONINV, fiosav_voc_cb, &fnctx); if (fnctx.err) goto ret_error; /* write end marker for vocabulary additions and deletions */ oswp2(buf+6, MCMONINV); if (osfwb(fp, buf, 8)) goto ret_error; /* write the current "Me" object */ oswp2(buf, vctx->voccxme); if (osfwb(fp, buf, 2)) goto ret_error; /* done - close file and return success indication */ osfcls(fp); os_settype(fname, OSFTSAVE); return FALSE; /* come here on failure - close file and return error indication */ ret_error: osfcls(fp); return TRUE; } qtads-2.1.7/tads2/fio.h000066400000000000000000000115621265017072300146100ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/FIO.H,v 1.3 1999/07/11 00:46:29 MJRoberts Exp $ */ /* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name fio.h - file i/o interface Function fiel i/o functions - write game, read game, save game, restore game Notes none Modified 04/02/92 MJRoberts - creation */ #ifndef FIO_INCLUDED #define FIO_INCLUDED #ifndef MCM_INCLUDED #include "mcm.h" #endif #ifndef OS_INCLUDED #include "os.h" #endif #ifndef OBJ_INCLUDED #include "obj.h" #endif #ifndef TOK_INCLUDED #include "tok.h" #endif /* forward declarations */ struct voccxdef; /* load-on-demand context (passed in by mcm in load callback) */ typedef struct fiolcxdef fiolcxdef; struct fiolcxdef { osfildef *fiolcxfp; /* file pointer of load file */ errcxdef *fiolcxerr; /* error handling context */ ulong fiolcxst; /* starting offset in file */ uint fiolcxflg; /* flags from original load file */ uint fiolcxseed; /* fioxor seed */ uint fiolcxinc; /* fioxor increment */ }; /* write game to binary file */ void fiowrt(struct mcmcxdef *mctx, struct voccxdef *vctx, struct tokcxdef *tokctx, struct tokthdef *tab, uchar *fmts, uint fmtl, char *fname, uint flags, objnum preinit, int extc, uint prpcnt, char *filever); /* flag values for use with fiowrt */ #define FIOFSYM 0x01 /* include symbol table in output file */ #define FIOFLIN 0x02 /* include source file tracking information */ #define FIOFPRE 0x04 /* preinit needs to be run after reading game */ #define FIOFCRYPT 0x08 /* "encrypt" objects prior to writing them */ #define FIOFBIN 0x10 /* writing precompiled header */ #define FIOFFAST 0x20 /* fast-load records are in file */ #define FIOFCASE 0x40 /* case folding was turned on in original compile */ #define FIOFLIN2 0x80 /* new-style line records */ /* read game from binary file; sets up loader callback context */ void fiord(struct mcmcxdef *mctx, struct voccxdef *vctx, struct tokcxdef *tctx, char *fname, char *exename, struct fiolcxdef *setupctx, objnum *preinit, uint *flagp, struct tokpdef *path, uchar **fmtsp, uint *fmtlp, uint *pcntptr, int flags, struct appctxdef *appctx, char *argv0); /* shut down load-on-demand subsystem, close load file */ void fiorcls(fiolcxdef *ctx); /* loader callback - load an object on demand */ void OS_LOADDS fioldobj(void *ctx, mclhd handle, uchar *ptr, ushort siz); /* * Save a game - returns TRUE on failure. We'll save the file to * 'fname'. 'game_fname' is the name of the game file; if this is not * null, we'll save it to the saved game file so that the player can * later start the game by specifying only the saved game file to the * run-time. 'game_fname' can be null, in which case we'll omit the * game file information. */ int fiosav(struct voccxdef *vctx, char *fname, char *game_fname); /* * fiorso() result codes */ #define FIORSO_SUCCESS 0 /* success */ #define FIORSO_FILE_NOT_FOUND 1 /* file not found */ #define FIORSO_NOT_SAVE_FILE 2 /* not a saved game file */ #define FIORSO_BAD_FMT_VSN 3 /* incompatible file format version */ #define FIORSO_BAD_GAME_VSN 4 /* file saved by another game or version */ #define FIORSO_READ_ERROR 5 /* error reading from the file */ #define FIORSO_NO_PARAM_FILE 6 /* no parameter file (for restore(nil)) */ /* restore a game - returns TRUE on failure */ int fiorso(struct voccxdef *vctx, char *fname); /* * Look in a saved game file to determine if it has information on which * GAM file created it. If the GAM file information is available, this * routine returns true and stores the game file name in the given * buffer; if the information isn't available, we'll return false. */ int fiorso_getgame(char *saved_file, char *buf, size_t buflen); /* encrypt/decrypt an object */ void fioxor(uchar *p, uint siz, uint seed, uint inc); /* strings stored in binary game file for identification and validation */ /* file header string */ #define FIOFILHDR "TADS2 bin\012\015\032" /* resource file header string */ #define FIOFILHDRRSC "TADS2 rsc\012\015\032" /* CURRENT file format version string */ #define FIOVSNHDR "v2.2.0" /* other file format versions that can be READ by this version */ #define FIOVSNHDR2 "v2.0.0" #define FIOVSNHDR3 "v2.0.1" #endif /* FIO_INCLUDED */ qtads-2.1.7/tads2/fioxor.c000066400000000000000000000021701265017072300153270ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/FIOXOR.C,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $"; #endif /* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name fioxor.c - file i/o encryption (XOR) routine Function Encrypts/decrypts a block of memory using a simple function that generates a stream of pseudo-random characters and xors them with a data block. Used to hide the data within a game object. This is a terribly insecure code, intended only to slow down would-be prying eyes. Fortunately, it isn't obvious what the plaintext is, so the simplicity of the encryption algorithm should be offset somewhat by the obscurity of the unencrypted data. Notes None Modified 03/13/93 MJRoberts - parameterize encryption scheme 04/16/92 MJRoberts - creation */ #include "os.h" #include "std.h" #include "fio.h" void fioxor(uchar *p, uint siz, uint seed, uint inc) { for ( ; siz ; seed += inc, --siz) *p++ ^= (uchar)seed; } qtads-2.1.7/tads2/getstr.c000066400000000000000000000116631265017072300153400ustar00rootroot00000000000000/* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name getstr - get a string from the player Function Reads a string from the player, doing all the necessary formatting and logging. Notes This routine flushes output before getting the string. The caller should display any desired prompt prior to calling getstring(). Never call os_gets() directly; use getstring() instead, since it logs the string to the log file if one is open. Modified 09/05/92 MJRoberts - add buf length parameter to getstring 04/07/91 JEras - log '>' on prompt; disable moremode before prompt 03/10/91 MJRoberts - created (separated from vocab.c) */ #include #include "os.h" #include "tio.h" #include "cmap.h" #include "run.h" /* * Global variable with the current command logging file. If this is * not null, we'll log each command that we read to this file. */ osfildef *cmdfile; /* * External global with the current script input file. If this is * non-null, we'll read commands from this file rather than from the * keyboard. */ extern osfildef *scrfp; /* * External global indicating script echo status. If we're reading from * a script input file (i.e., scrfp is non-null), and this variable is * true, it indicates that we're in "quiet" mode reading the script, so * we will not echo commands that we read from the script file to the * display. */ extern int scrquiet; /* * getstring reads a string from the keyboard, doing all necessary * output flushing. Prompting is to be done by the caller. This * routine should be called instead of os_gets. */ int getstring(char *prompt, char *buf, int bufl) { char *result; int savemoremode; int retval; /* show prompt if one was given and flush output */ savemoremode = setmore(0); if (prompt != 0) { /* display the prompt text */ outformat(prompt); /* make sure it shows up in the log file as well */ out_logfile_print(prompt, FALSE); } outflushn(0); outreset(); /* read from the command input file if we have one */ if (scrfp != 0) { int quiet = scrquiet; /* try reading from command input file */ if ((result = qasgets(buf, bufl)) == 0) { /* * End of command input file; return to reading the * keyboard. If we didn't already show the prompt, show it * now. * * Note that qasgets() will have closed the script file * before returning eof, so we won't directly read the * command here but instead handle it later when we check to * see if we need to read from the keyboard. */ if (quiet && prompt != 0) outformat(prompt); outflushn(0); outreset(); /* * Guarantee that moremode is turned back on. (moremode can * be turned off for one of two reasons: we're printing the * prompt, or we're reading from a script with no pauses. * In either case, moremode should be turned back on at this * point. -CDN) */ savemoremode = 1; /* turn off NONSTOP mode now that we're done with the script */ os_nonstop_mode(FALSE); } /* success */ retval = 0; } /* if we don't have a script file, read from the keyboard */ if (scrfp == 0) { /* update the status line */ runstat(); /* read a line from the keyboard */ result = (char *)os_gets((uchar *)buf, bufl); /* * if the result is null, we're at eof, so return a non-zero * value; otherwise, we successfully read a command, so return * zero */ retval = (result == 0); } /* restore the original "more" mode */ setmore(savemoremode); /* check the result */ if (retval != 0) { /* we got an error reading the command - return the error */ return retval; } else { char *p; /* * we got a command, or at least a partial command (if we timed * out, we may still have a partial line in the buffer) - write * the input line to the log and/or command files, as * appropriate */ out_logfile_print(buf, TRUE); if (cmdfile != 0) { os_fprintz(cmdfile, ">"); os_fprintz(cmdfile, buf); os_fprintz(cmdfile, "\n"); } /* translate the input to the internal character set */ for (p = buf ; *p != '\0' ; ++p) *p = cmap_n2i(*p); /* success */ return retval; } } qtads-2.1.7/tads2/h_ix86.h000066400000000000000000000036321265017072300151370ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/H_IX86.H,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name h_ix86.h - hardware definitions for Intel x86. Function These definitions are for 16-bit and 32-bit Intel CPUs. (Note: for 64-bit Intel hardware, use h_ix86_64.h instead. Don't use this file for non-Intel platforms, as it assumes Intel byte order and type sizes.) Notes Modified 10/17/98 MJRoberts - Creation */ #ifndef H_IX86_H #define H_IX86_H /* round a size to worst-case alignment boundary */ #define osrndsz(s) (((s)+3) & ~3) /* round a pointer to worst-case alignment boundary */ #define osrndpt(p) ((uchar *)((((unsigned long)(p)) + 3) & ~3)) /* read unaligned portable unsigned 2-byte value, returning int */ #define osrp2(p) ((int)*(unsigned short *)(p)) /* read unaligned portable signed 2-byte value, returning int */ #define osrp2s(p) ((int)*(short *)(p)) /* write int to unaligned portable 2-byte value */ #define oswp2(p, i) (*(unsigned short *)(p)=(unsigned short)(i)) #define oswp2s(p, i) oswp2(p, i) /* read unaligned portable 4-byte value, returning unsigned long */ #define osrp4(p) (*(unsigned long *)(p)) #define osrp4s(p) (*(long *)(p)) /* write long to unaligned portable 4-byte value */ #define oswp4(p, l) (*(long *)(p)=(l)) #define oswp4s(p, l) oswp4(p, l) /* read float from portable 4-byte buffer (IEEE 754-2008 format) */ float osrpfloat(const void *p); /* write float to portable 4-byte buffer (IEEE 754-2008 format) */ void oswpfloat(void *p, float f); /* write double to portable 8-byte buffer (IEEE 754-2008 format) */ double osrpdouble(const void *p); /* read double from portable 8-byte buffer (IEEE 754-2008 format) */ void oswpdouble(void *p, double d); #endif /* H_IX86_H */ qtads-2.1.7/tads2/h_ix86_64.h000066400000000000000000000067751265017072300154630ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/H_IX86.H,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2008 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name h_ix86_64.h - hardware definitions for x86-64 (64-bit x86) platforms Function These definitions are designed ONLY for 64-bit x86 CPUs. Note that these probably will NOT work on 16- and 32-bit x86 hardware. Notes IMPORTANT! See the note below about the typedef for hix_int32. Modified 03/25/08 MJRoberts - created (from h_ix86.h) */ #ifndef H_IX86_64_H #define H_IX86_64_H #ifdef HAVE_CONFIG_H #include #endif /* ------------------------------------------------------------------------ */ /* * Define local covers for the 32-bit and 16-bit integer types. * * For some of our operations, we need to translate between the local * native types and the portable types. The portable types are explicitly * defined with exact byte sizes and byte orderings. One way to do these * translations is to read and write the portable values one byte at a * time, and do some arithmetic to carry out the translation to the native * types. However, there's a more efficient approach: x86 CPUs happen to * have native types that match our portable types in size and byte order, * so the efficient way to do the translations is simply to use the correct * native type that corresponds to each portable type. * * That's the purpose of these definitions - since the portable types are * of specific sizes, we need to map the native type that exactly matches * each portable type. * * This is complicated by a *second* level of mapping, which is from C * types (short, int, long) to native types (WORD, DWORD, QWORD): this * mapping can vary by compiler. * * So, our typedefs here are designed to give us the correct mapping from C * types to portable types, by taking into account the way our compiler * maps from C to native. */ #if SIZEOF_INT == 4 typedef int hix_int32; typedef unsigned int hix_uint32; #else #error SIZEOF_INT must be defined in your makefile - \ set SIZEOF_INT to the number of bytes in an 'int' for your compiler #endif #if SIZEOF_SHORT == 2 typedef short hix_int16; typedef unsigned short hix_uint16; #else #error SIZEOF_SHORT must be defined in your makefile - \ set SIZEOF_SHORT to the number of bytes in a 'short' for your compiler #endif /* ------------------------------------------------------------------------ */ /* round a size to worst-case alignment boundary */ #define osrndsz(s) (((s)+3) & ~3) /* round a pointer to worst-case alignment boundary */ #define osrndpt(p) ((uchar *)((((ulong)(p)) + 3) & ~3)) /* read unaligned portable unsigned 2-byte value, returning int */ #define osrp2(p) ((int)*(hix_uint16 *)(p)) /* read unaligned portable signed 2-byte value, returning int */ #define osrp2s(p) ((int)*(hix_int16 *)(p)) /* write int to unaligned portable 2-byte value */ #define oswp2(p, i) (*(hix_uint16 *)(p)=(hix_uint16)(i)) #define oswp2s(p, i) oswp2(p, i) /* read unaligned portable 4-byte value, returning unsigned long */ #define osrp4(p) (*(hix_uint32 *)(p)) /* read unaligned portable 4-byte value, returning signed long */ #define osrp4s(p) (*(hix_int32 *)(p)) /* write unsigned/signed long to unaligned portable 4-byte value */ #define oswp4(p, l) (*(hix_int32 *)(p)=(l)) #define oswp4s(p, l) oswp4(p, l) #endif /* H_IX86_64_H */ qtads-2.1.7/tads2/h_ppc.h000066400000000000000000000056731265017072300151320ustar00rootroot00000000000000/* Copyright (c) 1992, 2004 by Michael J. Roberts. All Rights Reserved. */ /* Name h_ppc.h - TADS hardware abstraction definitions for PowerPC processor Function This file provides some machine-dependent routines that allow the TadsVM to run on big-endian CPUs (PowerPC on a Mac running OS X, for example). These definitions are for 32-bit machines only. These probably will NOT work on 64-bit hardware. Notes Created by Nikos Chantziaras. The arithmetics used in this code are stolen from Iain Merrick's HyperTADS interpreter. The definitions provided here are actually quite generic; they are perfectly suitable for little-endian CPUs, like Intel x86 compatibles. Anyway, they shouldn't be used for Intel machines, as this generic version of the routines is somewhat slower than the ones written specifically for x86 CPUs, as Intel compatibles provide hardware support for these routines; on Intel compatibles, h_ix86.h should be used instead. Note that Iain Merrick's original file assigns the copyright to Mike Roberts, so that's what I'm going to do here too. Modified 05/19/04 MJRoberts - added to main TADS source tree 05/17/04 Nikos Chantziaras - created */ #ifndef H_PPC_H #define H_PPC_H /* * Round a size up to worst-case alignment boundary. */ #define osrndsz(s) (((s) + 3) & ~3) /* * Round a pointer up to worst-case alignment boundary. */ #define osrndpt(p) ((unsigned char*)((((unsigned long)(p)) + 3) & ~3)) /* * Service macros for osrp2 etc. */ #define osc2u(p,i) ((unsigned short)(((unsigned char*)(p))[i])) #define osc2l(p,i) ((unsigned long)(((unsigned char*)(p))[i])) #define osc2s(p,i) ((short)(((signed char*)(p))[i])) #define osc2sl(p,i) ((long)(((signed char*)(p))[i])) /* * Read an unaligned portable unsigned 2-byte value, returning an int * value. */ #define osrp2(p) (osc2u(p,0) + (osc2u(p,1) << 8)) /* * Read an unaligned portable signed 2-byte value, returning int. */ #define osrp2s(p) (((short)osc2u(p,0)) + (osc2s(p,1) << 8)) /* * Write int to unaligned portable 2-byte value. */ #define oswp2(p,i) \ ((((unsigned char*)(p))[1] = (i) >> 8), \ (((unsigned char*)(p))[0] = (i) & 255)) #define oswp2s(p,i) oswp2(p,i) /* * Read an unaligned portable 4-byte value, returning long. */ #define osrp4s(p) \ (((long)osc2l(p,0)) \ + ((long)(osc2l(p,1)) << 8) \ + ((long)(osc2l(p,2) << 16)) \ + (osc2sl(p,3) << 24)) /* * Read an unaligned portable 4-byte value, returning unsigned long. */ #define osrp4(p) \ ((osc2l(p,0)) \ + ((osc2l(p,1)) << 8) \ + ((osc2l(p,2) << 16)) \ + (osc2l(p,3) << 24)) /* * Write a long to an unaligned portable 4-byte value. */ #define oswp4(p,i) \ ((((unsigned char*)(p))[0] = (i)), \ (((unsigned char*)(p))[1] = (i) >> 8), \ (((unsigned char*)(p))[2] = (i) >> 16, \ (((unsigned char*)(p))[3] = (i) >> 24))) #define oswp4s(p,i) oswp4(p,i) #endif /* H_PPC_H */ qtads-2.1.7/tads2/indlg_os.c000066400000000000000000000032001265017072300156120ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name indlg_tx.c - input dialog - formatted text-only version Function Implements the input dialog function using formatted text Notes Only one of indlg_tx or indlg_os should be included in a given executable. The choice depends on whether a system-level dialog is available or not. If no system-level dialog is available, include indlg_tx, which provides an implementation using formatted text. If a system-level dialog is available, use indlg_os, which calls os_input_dialog(). This file exists in the portable layer, rather than in the OS layer, so that we can provide an implementation using formatted text. An OS-layer implementation could not provide formatted text. Modified 09/27/99 MJRoberts - Creation */ #include #include #include "std.h" #include "os.h" #include "tio.h" /* ------------------------------------------------------------------------ */ /* * Text-mode os_input_dialog implementation */ int tio_input_dialog(int icon_id, const char *prompt, int standard_button_set, const char **buttons, int button_count, int default_index, int cancel_index) { /* call the OS implementation */ return os_input_dialog(icon_id, prompt, standard_button_set, buttons, button_count, default_index, cancel_index); } qtads-2.1.7/tads2/ler.c000066400000000000000000000107361265017072300146120ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/LER.C,v 1.3 1999/07/11 00:46:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name ler.c - library error handling functions Function Functions for error signalling and recovery Notes None Modified 12/30/92 MJRoberts - created from TADS err.c 10/02/91 MJRoberts - creation */ #include #include "os.h" #include "ler.h" /* format an error message, sprintf-style, using an erradef array */ int errfmt(char *outbuf, int outbufl, char *fmt, int argc, erradef *argv) { int outlen = 0; int argi = 0; int len; char buf[20]; char *p; char fmtchar; while (*fmt != '\0' && outbufl > 1) { switch(*fmt) { case '\\': ++fmt; len = 1; switch(*fmt) { case '\0': --fmt; break; case '\n': p = "\n"; break; case '\t': p = "\t"; break; default: p = fmt; break; } break; case '%': ++fmt; fmtchar = *fmt; if (argi >= argc) fmtchar = 1; /* too many - ignore it */ switch(fmtchar) { case '\0': --fmt; len = 0; break; case '%': p = "%"; len = 1; break; case 'd': sprintf(buf, "%d", argv[argi].erraint); len = strlen(buf); p = buf; break; case 'u': sprintf(buf, "%u", argv[argi].erraint); len = strlen(buf); p = buf; break; case 's': p = argv[argi].errastr; len = strlen(p); break; default: p = ""; len = 0; --argi; break; } ++argi; break; default: p = fmt; len = 1; break; } /* copy output that was set up above */ if (len != 0) { if (outbufl >= len) { memcpy(outbuf, p, (size_t)len); outbufl -= len; outbuf += len; } else if (outbufl > 1) { memcpy(outbuf, p, (size_t)outbufl - 1); outbufl = 1; } outlen += len; } ++fmt; } /* add a null terminator */ if (outbufl != 0) *outbuf++ = '\0'; /* return the length */ return outlen; } #if defined(DEBUG) && !defined(ERR_NO_MACRO) void errjmp(jmp_buf buf, int e) { longjmp(buf, e); } #endif /* DEBUG */ #ifdef ERR_NO_MACRO /* base error signal function */ void errsign(errcxdef *ctx, int e, char *facility) { strncpy(ctx->errcxptr->errfac, facility, ERRFACMAX); ctx->errcxptr->errfac[ERRFACMAX] = '\0'; ctx->errcxofs = 0; longjmp(ctx->errcxptr->errbuf, e); } /* signal an error with no arguments */ void errsigf(errcxdef *ctx, char *facility, int e) { errargc(ctx, 0); errsign(ctx, e, facility); } /* enter a string argument */ char *errstr(errcxdef *ctx, const char *str, int len) { char *ret = &ctx->errcxbuf[ctx->errcxofs]; memcpy(ret, str, (size_t)len); ret[len] = '\0'; ctx->errcxofs += len + 1; return(ret); } /* resignal current error */ void errrse1(errcxdef *ctx, errdef *fr) { errargc(ctx, fr->erraac); memcpy(ctx->errcxptr->erraav, fr->erraav, (size_t)(fr->erraac * sizeof(erradef))); errsign(ctx, fr->errcode, fr->errfac); } /* log an error: base function */ void errlogn(errcxdef *ctx, int err, char *facility) { ctx->errcxofs = 0; (*ctx->errcxlog)(ctx->errcxlgc, facility, err, ctx->errcxptr->erraac, ctx->errcxptr->erraav); } /* log an error with no arguments */ void errlogf(errcxdef *ctx, char *facility, int err) { errargc(ctx, 0); errlogn(ctx, err, facility); } #endif /* ERR_NO_MACRO */ qtads-2.1.7/tads2/ler.h000066400000000000000000000262011265017072300146110ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/LER.H,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name ler.h - library error handling definitions Function Defines error handling mechanism Notes All of the functions and macros in here are named ERRxxx because this file was based on the TADS err.h, which used the ERRxxx naming convention, and it would be a lot of trouble to change. This package defines a set of macros that allows code to raise and handle exceptions. A macro is provided which signals an error, which does a non-local goto to the innermost enclosing exception handler. A set of macros sets up exception handling code. To catch exceptions that occur inside a block of code (i.e., in the code or in any subroutines called by the code), begin the block with ERRBEGIN. At the end of the protected code, place the exception handler, which starts with ERRCATCH. At the end of the exception handler, place ERREND. If no exception occurs, execution goes through the protected code, then resumes at the code following the ERREND. The exception handler can signal another error, which will cause the next enclosing frame to catch the error. Alternatively, if the exception handler doesn't signal an error or return, execution continues at the code following the ERREND. Exceptions that are signalled during exception handling will be caught by the next enclosing frame, unless the exception handler code is itself protected by another ERRBEGIN-ERREND block. To signal an error, use errsig(). To use a string argument in a signalled error, cover the string with errstr(ctx, str, len); for example: errsig1(ctx, ERR_XYZ, ERRTSTR, errstr(ctx, buf, strlen(buf))); This copies the string into a buffer that is unaffected by stack resetting during error signalling. Modified 10/23/97 CNebel - fixed warnings from Metrowerks C compiler. 12/30/92 MJRoberts - created from TADS err.h 09/14/92 MJRoberts - add errlog2 08/15/91 MJRoberts - creation */ #ifndef LER_INCLUDED #define LER_INCLUDED #include "os.h" #ifndef LIB_INCLUDED #include "lib.h" #endif #include #include #ifdef __cplusplus extern "C" { #endif /* maximum length of a facility identifier */ #define ERRFACMAX 6 union erradef { int erraint; /* integer argument */ char *errastr; /* text string argument */ }; typedef union erradef erradef; struct errdef { struct errdef *errprv; /* previous error frame */ int errcode; /* error code of exception being handled */ char errfac[ERRFACMAX+1]; /* facility of current error */ erradef erraav[10]; /* parameters for error */ int erraac; /* count of parameters in argc */ jmp_buf errbuf; /* jump buffer for current error frame */ }; typedef struct errdef errdef; #define ERRBUFSIZ 512 /* seek location record for an error message by number */ struct errmfdef { uint errmfnum; /* error number */ ulong errmfseek; /* seek location of this message */ }; typedef struct errmfdef errmfdef; struct errcxdef { errdef *errcxptr; /* current error frame */ void (*errcxlog)(void *, char *fac, int err, int argc, erradef *); /* error logging callback function */ void *errcxlgc; /* context for error logging callback */ int errcxofs; /* offset in argument buffer */ char errcxbuf[ERRBUFSIZ]; /* space for argument strings */ osfildef *errcxfp; /* message file, if one is being used */ errmfdef *errcxseek; /* seek locations of messages in file */ uint errcxsksz; /* size of errcxseek array */ ulong errcxbase; /* offset in physical file of logical error file */ struct appctxdef *errcxappctx; /* host application context */ }; typedef struct errcxdef errcxdef; /* begin protected code */ #define ERRBEGIN(ctx) \ { \ errdef fr_; \ if ((fr_.errcode = setjmp(fr_.errbuf)) == 0) \ { \ fr_.errprv = (ctx)->errcxptr; \ (ctx)->errcxptr = &fr_; /* end protected code, begin error handler */ #define ERRCATCH(ctx, e) \ assert(1==1 && (ctx)->errcxptr != fr_.errprv); \ (ctx)->errcxptr = fr_.errprv; \ } \ else \ { \ assert(2==2 && (ctx)->errcxptr != fr_.errprv); \ (e) = fr_.errcode; \ (ctx)->errcxptr = fr_.errprv; /* retrieve argument (int, string) in current error frame */ #define errargint(argnum) (fr_.erraav[argnum].erraint) #define errargstr(argnum) (fr_.erraav[argnum].errastr) #define ERREND(ctx) \ } \ } /* end protected code, begin cleanup (no handling; just cleaning up) */ #define ERRCLEAN(ctx) \ assert((ctx)->errcxptr != fr_.errprv); \ (ctx)->errcxptr = fr_.errprv; \ } \ else \ { \ assert((ctx)->errcxptr != fr_.errprv); \ (ctx)->errcxptr = fr_.errprv; #define ERRENDCLN(ctx) \ errrse(ctx); \ } \ } /* argument types for errors with arguments */ #define ERRTINT erraint #define ERRTSTR errastr /* set argument count in error frame */ #define errargc(ctx,cnt) ((ctx)->errcxptr->erraac=(cnt)) /* enter string argument; returns pointer to argument used in errargv */ #ifdef ERR_NO_MACRO char *errstr(errcxdef *ctx, const char *str, int len); #else /* ERR_NO_MACRO */ #define errstr(ctx,str,len) \ ((memcpy(&(ctx)->errcxbuf[(ctx)->errcxofs],str,(size_t)len), \ (ctx)->errcxofs += (len), \ (ctx)->errcxbuf[(ctx)->errcxofs++] = '\0'), \ &(ctx)->errcxbuf[(ctx)->errcxofs-(len)-1]) #endif /* ERR_NO_MACRO */ /* set argument in error frame argument vector */ #define errargv(ctx,index,typ,arg) \ ((ctx)->errcxptr->erraav[index].typ=(arg)) /* signal an error with argument count already set */ #ifdef ERR_NO_MACRO void errsign(errcxdef *ctx, int e, char *facility); #else /* ERR_NO_MACRO */ # ifdef DEBUG void errjmp(jmp_buf buf, int e); # define errsign(ctx, e, fac) \ (strncpy((ctx)->errcxptr->errfac, fac, ERRFACMAX),\ (ctx)->errcxptr->errfac[ERRFACMAX]='\0',\ (ctx)->errcxofs=0, errjmp((ctx)->errcxptr->errbuf, e)) # else /* DEBUG */ # define errsign(ctx, e, fac) \ (strncpy((ctx)->errcxptr->errfac, fac, ERRFACMAX),\ (ctx)->errcxptr->errfac[ERRFACMAX]='\0',\ (ctx)->errcxofs=0, longjmp((ctx)->errcxptr->errbuf, e)) # endif /* DEBUG */ #endif /* ERR_NO_MACRO */ /* signal an error with no arguments */ #ifdef ERR_NO_MACRO void errsigf(errcxdef *ctx, char *facility, int err); #else /* ERR_NO_MACRO */ #define errsigf(ctx, fac, e) (errargc(ctx,0),errsign(ctx,e,fac)) #endif /* ERR_NO_MACRO */ /* signal an error with one argument */ #define errsigf1(ctx, fac, e, typ1, arg1) \ (errargv(ctx,0,typ1,arg1),errargc(ctx,1),errsign(ctx,e,fac)) /* signal an error with two arguments */ #define errsigf2(ctx, fac, e, typ1, arg1, typ2, arg2) \ (errargv(ctx,0,typ1,arg1), errargv(ctx,1,typ2,arg2), \ errargc(ctx,2), errsign(ctx,e,fac)) /* resignal the current error - only usable within exception handlers */ #ifdef ERR_NO_MACRO void errrse1(errcxdef *ctx, errdef *fr); # define errrse(ctx) errrse1(ctx, &fr_) #else /* ERR_NO_MACRO */ /* void errrse(errcxdef *ctx); */ # define errrse(ctx) \ (errargc(ctx, fr_.erraac),\ memcpy((ctx)->errcxptr->erraav, fr_.erraav, \ (size_t)(fr_.erraac*sizeof(erradef))),\ errsign(ctx, fr_.errcode, fr_.errfac)) #endif /* ERR_NO_MACRO */ /* * For use in an error handler (ERRCATCH..ERREND) only: Copy the * parameters from the error currently being handled to the enclosing * frame. This is useful when "keeping" an error being handled - i.e., * the arguments will continue to be used outside of the * ERRCATCH..ERREND code. */ /* void errkeepargs(errcxdef *ctx); */ #define errkeepargs(ctx) errcopyargs(ctx, &fr_) /* * copy the parameters for an error from another frame into the current * frame - this can be used when we want to be able to display an error * that occurred in an inner frame within code that is protected by a * new enclosing error frame */ /* void errcopyargs(errcxdef *ctx, errdef *fr); */ #define errcopyargs(ctx, fr) \ (errargc((ctx), (fr)->erraac), \ memcpy((ctx)->errcxptr->erraav, (fr)->erraav, \ (size_t)((fr)->erraac*sizeof(erradef)))) /* log error that's been caught, using arguments already caught */ #define errclog(ctx) \ ((*(ctx)->errcxlog)((ctx)->errcxlgc,fr_.errfac,fr_.errcode,\ fr_.erraac,fr_.erraav)) /* log an error that's been set up but not signalled yet */ #define errprelog(ctx, err) \ ((*(ctx)->errcxlog)((ctx)->errcxlgc,(ctx)->errcxptr->errfac,\ err,(ctx)->errcxptr->erraac,\ (ctx)->errcxptr->erraav)) /* log an error (no signalling, just reporting) */ #ifdef ERR_NO_MACRO void errlogn(errcxdef *ctx, int err, char *facility); #else /* ERR_NO_MACRO */ #define errlogn(ctx,err,fac) \ ((ctx)->errcxofs=0,\ (*(ctx)->errcxlog)((ctx)->errcxlgc,fac,err,(ctx)->errcxptr->erraac,\ (ctx)->errcxptr->erraav)) #endif /* ERR_NO_MACRO */ /* log an error with no arguments */ #ifdef ERR_NO_MACRO void errlogf(errcxdef *ctx, char *facility, int err); #else /* ERR_NO_MACRO */ /* void errlogf(errcxdef *ctx, char *facility, int err); */ #define errlogf(ctx,fac,err) (errargc(ctx,0),errlogn(ctx,err,fac)) #endif /* ERR_NO_MACRO */ /* log an error with one argument */ #define errlogf1(ctx, fac, e, typ1, arg1) \ (errargv(ctx,0,typ1,arg1),errargc(ctx,1),errlogn(ctx,e,fac)) /* log an error with two arguments */ #define errlogf2(ctx, fac, e, typ1, arg1, typ2, arg2) \ (errargv(ctx,0,typ1,arg1),errargv(ctx,1,typ2,arg2),\ errargc(ctx,2),errlogn(ctx,e,fac)) /* * Format an error message, sprintf-style, using arguments in an * erradef array (which is passed to the error-logging callback). * Returns the length of the output string, even if the actual * output string was truncated because the outbuf was too short. * (If called with outbufl == 0, nothing will be written out, but * the size of the buffer needed, minus the terminating null byte, * will be computed and returned.) */ int errfmt(char *outbuf, int outbufl, char *fmt, int argc, erradef *argv); /* get the text of an error */ void errmsg(errcxdef *ctx, char *outbuf, uint outbufl, uint err); /* initialize error subsystem, opening error message file if necessary */ void errini(errcxdef *ctx, osfildef *fp); /* allocate and initialize error context, free error context */ errcxdef *lerini(); void lerfre(errcxdef *ctx); /* error message structure - number + text */ typedef struct errmdef errmdef; struct errmdef { uint errmerr; /* error number */ char *errmtxt; /* text of error message */ }; #ifdef __cplusplus } #endif #endif /* ERR_INCLUDED */ qtads-2.1.7/tads2/lib.h000066400000000000000000000067601265017072300146050ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/LIB.H,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name lib.h - standard definitions Function Various standard definitions Notes None Modified 01/02/93 SMcAdams - add bit operations 12/30/92 MJRoberts - converted to lib.h (from TADS std.h) 08/03/91 MJRoberts - creation */ #ifndef LIB_INCLUDED #define LIB_INCLUDED #include #include #include #ifndef OS_INCLUDED # include "os.h" #endif /* short-hand for various types */ #ifndef OS_UCHAR_DEFINED typedef unsigned char uchar; #endif #ifndef OS_USHORT_DEFINED typedef unsigned short ushort; #endif #ifndef OS_UINT_DEFINED typedef unsigned int uint; #endif #ifndef OS_ULONG_DEFINED typedef unsigned long ulong; #endif /* some alternative types */ typedef unsigned char ub1; typedef signed char sb1; typedef char b1; typedef unsigned int ub2; typedef signed int sb2; typedef int b2; typedef unsigned long ub4; typedef signed long sb4; typedef long b4; typedef int eword; /* maximum/minimum portable values for various types */ #define UB4MAXVAL 0xffffffffUL #define UB2MAXVAL 0xffffU #define UB1MAXVAL 0xffU #define SB4MAXVAL 0x7fffffffL #define SB2MAXVAL 0x7fff #define SB1MAXVAL 0x7f #define SB4MINVAL (-(0x7fffffff)-1) #define SB2MINVAL (-(0x7fff)-1) #define SB1MINVAL (-(0x7f)-1) /* clear a struture */ #define CLRSTRUCT(x) memset(&(x), 0, (size_t)sizeof(x)) #define CPSTRUCT(dst,src) memcpy(&(dst), &(src), (size_t)sizeof(dst)) /* TRUE and FALSE */ #ifndef TRUE # define TRUE 1 #endif /* TRUE */ #ifndef FALSE # define FALSE 0 #endif /* FALSE */ /* bitwise operations */ #define bit(va, bt) ((va) & (bt)) #define bis(va, bt) ((va) |= (bt)) #define bic(va, bt) ((va) &= ~(bt)) /* * noreg/NOREG - use for variables changed in error-protected code that * are used in error handling code. Use 'noreg' on the declaration like * a storage class qualifier. Use 'NOREG' as an argument call, passing * the addresses of all variables declared noreg. For non-ANSI * compilers, a routine osnoreg(/o_ void *arg0, ... _o/) must be * defined. */ #ifdef OSANSI # define noreg volatile # define NOREG(arglist) #else /* OSANSI */ # define noreg # define NOREG(arglist) osnoreg arglist ; #endif /* OSANSI */ /* * Linting directives. You can define these before including this file * if you have a fussy compiler. */ #ifdef LINT # ifndef NOTREACHED # define NOTREACHED return # endif # ifndef NOTREACHEDV # define NOTREACHEDV(t) return((t)0) # endif # ifndef VARUSED # define VARUSED(v) varused(v) # endif void varused(); #else /* LINT */ # ifndef NOTREACHED # define NOTREACHED # endif # ifndef NOTREACHEDV # define NOTREACHEDV(t) # endif # ifndef VARUSED # define VARUSED(v) # endif #endif /* LINT */ /* conditionally compile code if debugging is enabled */ #ifdef DEBUG # define IF_DEBUG(x) x #else /* DEBUG */ # define IF_DEBUG(x) #endif /* DEBUG */ #ifndef offsetof # define offsetof(s_name, m_name) (size_t)&(((s_name *)0)->m_name) #endif /* offsetof */ /* * Define our own version of isspace(), so that we don't try to interpret * anything outside of the normal ASCII set as spaces. */ #define t_isspace(c) \ (((unsigned char)(c)) <= 127 && isspace((unsigned char)(c))) #endif /* LIB_INCLUDED */ qtads-2.1.7/tads2/lin.h000066400000000000000000000136011265017072300146110ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/LIN.H,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name lin.h - line source definitions Function Definitions for the basic line source class Notes A line source is a mechanism for reading source text. The tokenizer reads its input from a line source. This is the basic class definition; individual line sources will define the functions and class data needed. Modified 08/14/91 MJRoberts - creation */ #ifndef LIN_INCLUDED #define LIN_INCLUDED #ifndef LIB_INCLUDED #include "lib.h" #endif #ifndef OS_INCLUDED #include "os.h" #endif #ifndef OBJ_INCLUDED #include "obj.h" #endif #ifdef __cplusplus extern "C" { #endif /* line source superclass structure */ typedef struct lindef lindef; struct lindef { int (*lingetp)(lindef *lin); /* get next line */ void (*linclsp)(lindef *lin); /* close line source */ void (*linppos)(lindef *lin, char *buf, uint buflen); /* write printable rep of position to buf (for error reporting) */ void (*linglop)(lindef *lin, uchar *buf); /* generate a line record for an OPCLINE instruction */ int (*linwrtp)(lindef *lin, osfildef *fp); /* write line source information to binary file; TRUE ==> error */ void (*lincmpp)(lindef *lin, uchar *buf); /* give location of compiled code for current line to line source */ void (*linactp)(lindef *lin); /* activate for dbg */ void (*lindisp)(lindef *lin); /* disactivate */ void (*lintellp)(lindef *lin, uchar *pos); /* get position */ void (*linseekp)(lindef *lin, uchar *pos); /* seek */ int (*linreadp)(lindef *lin, uchar *buf, uint siz); /* fread */ void (*linpaddp)(lindef *lin, uchar *pos, long delta); /* add an offset to a position value */ int (*linqtopp)(lindef *lin, uchar *pos); /* at top? */ int (*lingetsp)(lindef *lin, uchar *buf, uint siz); /* get line */ void (*linnamp)(lindef *lin, char *buf); /* get source name */ void (*linfindp)(lindef *lin, char *buf, objnum *objp, uint *ofsp); /* find nearest line record */ void (*lingotop)(lindef *lin, int where); /* seek global */ long (*linofsp)(lindef *lin); /* byte offset in line source */ void (*linrenp)(lindef *lin, objnum oldnum, objnum newnum); /* renumber an object (for "modify") */ void (*lindelp)(lindef *lin, objnum objn); /* delete an object (for "replace") */ ulong (*linlnump)(lindef *lin); /* get the current line number */ # define LINGOTOP OSFSK_SET /* go to top of line source */ # define LINGOEND OSFSK_END /* go to end of line source */ lindef *linpar; /* parent of current line source */ lindef *linnxt; /* next line in line source chain */ int linid; /* serial number of line source (for debugger) */ char *linbuf; /* pointer to current line */ ushort linflg; /* flags */ # define LINFEOF 0x01 /* line source is at end of file */ # define LINFMORE 0x02 /* there's more to the line than linlen */ # define LINFDBG 0x04 /* debug record already generated for line */ # define LINFNOINC 0x08 /* ignore # directives from this line source */ # define LINFCMODE 0x10 /* line source is parsed in C-mode */ ushort linlen; /* length of the line */ ushort linlln; /* length of line record generated by lingloc */ }; /* * Maximum allowed value for linlln, in bytes. This allows subsystems * that need to maintain local copies of seek locations to know how big * an area to allocate for them. */ #define LINLLNMAX 20 /* macros to cover calls to functions */ #define linget(lin) ((*((lindef *)(lin))->lingetp)((lindef *)(lin))) #define lincls(lin) ((*((lindef *)(lin))->linclsp)((lindef *)(lin))) #define linppos(lin, buf, buflen) \ ((*((lindef *)(lin))->linppos)((lindef *)(lin), buf, buflen)) #define linglop(lin, buf) ((*((lindef *)(lin))->linglop)(lin, buf)) #define linwrt(lin, fp) ((*((lindef *)(lin))->linwrtp)(lin, fp)) #define lincmpinf(lin, buf) ((*((lindef *)(lin))->lincmpp)(lin, buf)) #define linactiv(lin) ((*((lindef *)(lin))->linactp)(lin)) #define lindisact(lin) ((*((lindef *)(lin))->lindisp)(lin)) #define lintell(lin, pos) ((*((lindef *)(lin))->lintellp)(lin, pos)) #define linseek(lin, pos) ((*((lindef *)(lin))->linseekp)(lin, pos)) #define linread(lin, buf, siz) ((*((lindef *)(lin))->linreadp)(lin, buf, siz)) #define linpadd(lin, pos, delta) \ ((*((lindef *)(lin))->linpaddp)(lin, pos, delta)) #define linqtop(lin, pos) ((*((lindef *)(lin))->linqtopp)(lin, pos)) #define lingets(lin, buf, siz) ((*((lindef *)(lin))->lingetsp)(lin, buf, siz)) #define linnam(lin, buf) ((*((lindef *)(lin))->linnamp)(lin, buf)) #define linlnum(lin) ((*((lindef *)(lin))->linlnump)(lin)) #define linfind(lin, buf, objp, ofsp) \ ((*((lindef *)(lin))->linfindp)(lin, buf, objp, ofsp)) #define lingoto(lin, where) ((*((lindef *)(lin))->lingotop)(lin, where)) #define linofs(lin) ((*((lindef *)(lin))->linofsp)(lin)) #define linrenum(lin, oldnum, newnum) \ ((*((lindef *)(lin))->linrenp)(lin, oldnum, newnum)) #define lindelnum(lin, objn) ((*((lindef *)(lin))->lindelp)(lin, objn)) #ifdef __cplusplus } #endif #endif /* LIN_INCLUDED */ qtads-2.1.7/tads2/linf.h000066400000000000000000000117031265017072300147600ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/LINF.H,v 1.3 1999/07/11 00:46:30 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name linf.h - line source File Function Defines the file line source Notes The file line source reads lines from an OS file. Modified 08/14/91 MJRoberts - creation */ #ifndef LINF_INCLUDED #define LINF_INCLUDED #include #ifndef STD_INCLUDED #include "std.h" #endif #ifndef OS_INCLUDED #include "os.h" #endif #ifndef LIN_INCLUDED #include "lin.h" #endif #ifndef OBJ_INCLUDED #include "obj.h" #endif #ifndef MCM_INCLUDED #include "mcm.h" #endif #ifndef TOK_INCLUDED #include "tok.h" #endif #ifdef __cplusplus extern "C" { #endif /* maximum number of pages of debugging records we can keep */ #define LINFPGMAX 128 /* * executable line information structure: this record relates one * executable line to the object containing the p-code, and the offset * in the object of the p-code for the start of the line */ struct linfinfo { /* * OPCLINE data (file seek position or line number, depending on how * the game was compiled: -ds -> file seek offset, -ds2 -> line * number) */ ulong fpos; /* object number */ objnum objn; /* offset from start of code */ uint ofs; }; /* * file line source */ struct linfdef { lindef linflin; /* superclass data */ osfildef *linffp; /* file pointer for this line source */ char linfbuf[100]; /* buffer for the line contents */ int linfbufnxt; /* offset in buffer of start of next line */ int linfnxtlen; /* length of data after linfbufnxt */ ulong linfnum; /* current line number */ ulong linfseek; /* seek position of current line */ mcmcxdef *linfmem; /* memory manager context */ mcmon linfpg[LINFPGMAX]; /* pages for debugging records */ ulong linfcrec; /* number of debugger records written so far */ char linfnam[1]; /* name of file being read */ }; typedef struct linfdef linfdef; /* initialize a file line source, opening the file for the line source */ linfdef *linfini(mcmcxdef *mctx, errcxdef *errctx, char *filename, int flen, struct tokpdef *path, int must_find_file, int new_line_records); /* initialize a pre-allocated linfdef, skipping debugger page setup */ void linfini2(mcmcxdef *mctx, linfdef *linf, char *filename, int flen, osfildef *fp, int new_line_records); /* get next line from line source */ int linfget(lindef *lin); /* generate printable rep of current position in source (for errors) */ void linfppos(lindef *lin, char *buf, uint bufl); /* close line source */ void linfcls(lindef *lin); /* generate source-line debug instruction operand */ void linfglop(lindef *lin, uchar *buf); /* generate new-style source-line debug instructino operand */ void linfglop2(lindef *lin, uchar *buf); /* save line source to binary (.gam) file */ int linfwrt(lindef *lin, osfildef *fp); /* load a file-line-source from binary (.gam) file */ int linfload(osfildef *fp, struct dbgcxdef *dbgctx, errcxdef *ec, struct tokpdef *path); /* add a debugger line record for the current line */ void linfcmp(lindef *lin, uchar *buf); /* find nearest line record to a file seek location */ void linffind(lindef *lin, char *buf, objnum *objp, uint *ofsp); /* activate line source for debugging */ void linfact(lindef *lin); /* disactivate line source */ void linfdis(lindef *lin); /* get current seek position */ void linftell(lindef *lin, uchar *pos); /* seek */ void linfseek(lindef *lin, uchar *pos); /* read */ int linfread(lindef *lin, uchar *buf, uint siz); /* add a signed delta to a seek positon */ void linfpadd(lindef *lin, uchar *pos, long delta); /* query whether at top of file */ int linfqtop(lindef *lin, uchar *pos); /* read one line at current seek position */ int linfgets(lindef *lin, uchar *buf, uint bufsiz); /* get name of line source */ void linfnam(lindef *lin, char *buf); /* get the current line number */ ulong linflnum(lindef *lin); /* go to top or bottom */ void linfgoto(lindef *lin, int where); /* return the current offset in the line source */ long linfofs(lindef *lin); /* renumber an object */ void linfren(lindef *lin, objnum oldnum, objnum newnum); /* delete an object */ void linfdelnum(lindef *lin, objnum objn); /* copy line records to an array of linfinfo structures */ void linf_copy_linerecs(linfdef *linf, struct linfinfo *info); /* debugging echo */ #ifdef DEBUG # define LINFDEBUG(x) x #else /* DEBUG */ # define LINFDEBUG(x) #endif /* DEBUG */ #ifdef __cplusplus } #endif #endif /* LINF_INCLUDED */ qtads-2.1.7/tads2/linfdum.c000066400000000000000000000034141265017072300154610ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/LINFDUM.C,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $"; #endif /* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name linfdum.c - dummy implementation of line source loader Function Implements a line source loader that just reads a file line source from a .gam file and ignores the information therein. Used to link the run-time if debugging functions are not desired (because the rest of the linf implementation will be unnecessary in this case). Notes None Modified 04/11/92 MJRoberts - creation */ #include #include #include #include #include "os.h" #include "std.h" #include "err.h" #include "mch.h" #include "linf.h" #include "dbg.h" #include "tok.h" /* read and ignore a file-line-source from binary (.gam) file */ int linfload(osfildef *fp, dbgcxdef *dbgctx, errcxdef *ec, tokpdef *path) { uchar buf[UCHAR_MAX + 6]; uint pgcnt; ulong reccnt; VARUSED(ec); VARUSED(dbgctx); VARUSED(path); /* read the source's description from the file */ if (osfrb(fp, buf, 6) || osfrb(fp, buf + 6, (int)buf[1])) return(TRUE); /* skip the pages of debugging line records */ reccnt = osrp4(buf + 2); if (!reccnt) return(FALSE); /* no debug records at all */ pgcnt = 1 + ((reccnt - 1) >> 10); /* figure number of pages */ while (pgcnt--) { if (osfseek(fp, (1024 * DBGLINFSIZ), OSFSK_CUR)) return(TRUE); } /* do nothing with this information - just return success */ return(FALSE); } qtads-2.1.7/tads2/los.h000066400000000000000000000000241265017072300146170ustar00rootroot00000000000000/* * empty */ qtads-2.1.7/tads2/lst.c000066400000000000000000000013271265017072300146260ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/LST.C,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $"; #endif /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name lst.c - list manipulation routines Function Routines to manipulate TADS run-time lists Notes None Modified 08/13/91 MJRoberts - creation */ #include #include "os.h" #include "lst.h" #include "dat.h" void lstadv(uchar **lstp, uint *sizp) { uint siz; siz = datsiz(**lstp, (*lstp)+1) + 1; assert(siz <= *sizp); *lstp += siz; *sizp -= siz; } qtads-2.1.7/tads2/lst.h000066400000000000000000000015511265017072300146320ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/LST.H,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name lst.h - list definitions Function Run-time list definitions Notes A TADS run-time list is essentially a packed counted array. The first thing in a list is a ushort, which specifies the number of elements in the list. The list elements are then packed into the list immediately following. Modified 08/13/91 MJRoberts - creation */ #ifndef LST_INCLUDED #define LST_INCLUDED #ifndef DAT_INCLUDED #include "dat.h" #endif /* advance a list pointer/size pair to the next element of a list */ void lstadv(uchar **lstp, uint *sizp); #endif /* LST_INCLUDED */ qtads-2.1.7/tads2/ltk.h000066400000000000000000000047441265017072300146310ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/LTK.H,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $ */ /* * Copyright (c) 1993 by Steve McAdams. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name ltk.h - Library porting Tool Kit definitions Function Notes These are generic definitions which should be applicable to any system. The implementation will be in a file call LTKxxx, where 'xxx' is the os that the toolkit is written for. Modified 02/11/93 SMcAdams - Creation */ #ifndef LTK #define LTK #include /* * ltkini - allocate and INItialize ltk context. 'heapsiz' is the * requested size for the local heap. Returns 0 if the request cannot be * satisfied. */ void ltkini(unsigned short heapsiz); /* * ltkfre - FREe ltk context. */ void ltkfre(); /* * ltk_dlg - DiaLoG. Present user with informational dialog message. * 'title' specifies the title to use in the dialog box, 'msg' is the * text message, which may contain printf-style formatting. * printf-style arguments must be passed in also, if the message * requires them. */ void ltk_dlg(char *title, char *msg, ...); /* * ltk_errlog - Error logging function for LER routines. */ void ltk_errlog(void *ctx, char *fac, int errno, int agrc, union erradef *argv); /* * ltk_alloc - ALLOCate permanent global memory. Returns 0 if the * request cannot be satisfied. */ void *ltk_alloc(size_t siz); /* ltk_realloc - reallocate memory; analogous to realloc() */ void *ltk_realloc(void *ptr, size_t siz); /* * ltk_sigalloc - ALLOCate permanent global memory, signals error on * failure. */ void *ltk_sigalloc(struct errcxdef *errcx, size_t siz); /* * ltk_free - FREE memory allocated using ltk_alloc. */ void ltk_free(void *ptr); /* * ltk_suballoc - SUB-ALLOCate memory from user heap. Returns 0 if the * request cannot be satisfied. */ void *ltk_suballoc(size_t siz); /* * ltk_sigsuballoc - SUB-ALLOCate memory from user heap, signals error * on failure. */ void *ltk_sigsuballoc(struct errcxdef *errcx, size_t siz); /* * ltk_subfree - SUBsegment FREE. Frees memory allocated by * ltk_suballoc. */ void ltk_subfree(void *ptr); /* * ltk_beep - BEEP the user. */ void ltk_beep(void); /* * ltk_beg_wait - signal that the user needs to wait. */ void ltk_beg_wait(void); /* * ltk_end_wait - end the waiting period . */ void ltk_end_wait(void); #endif /* LTK */ qtads-2.1.7/tads2/mch.c000066400000000000000000000017701265017072300145750ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/MCH.C,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $"; #endif /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name mch.c - memory cache manager: low-level heap manager Function Low-level heap management functions Notes This is a cover for malloc that uses proper error signalling conventions Modified 08/20/91 MJRoberts - creation */ #include "os.h" #include "std.h" #include "mch.h" #include "err.h" /* global to keep track of all allocations */ IF_DEBUG(ulong mchtotmem;) uchar *mchalo(errcxdef *ctx, size_t siz, char *comment) { uchar *ret; VARUSED(comment); IF_DEBUG(mchtotmem += siz;) ret = (uchar *)osmalloc(siz); if (ret) return(ret); else { errsig(ctx, ERR_NOMEM); NOTREACHEDV(uchar *); return 0; } } qtads-2.1.7/tads2/mch.h000066400000000000000000000023661265017072300146040ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/MCH.H,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name mch.h - memory cache heap manager Function Low-level heap management functions Notes This is the low-level heap manager, which maintains a list of non-relocatable, non-swappable blocks of memory. The cache manager uses the heap manager for its basic storage needs. Modified 08/03/91 MJRoberts - creation */ #ifndef MCH_INCLUDED #define MCH_INCLUDED #include #ifndef STD_INCLUDED #include "std.h" #endif #ifndef ERR_INCLUDED #include "err.h" #endif /* * Allocate a block of memory; returns pointer to the block. * An out-of-memory error is signalled if insufficient memory * is available. The comment is for debugging purposes only. */ uchar *mchalo(errcxdef *ctx, size_t siz, char *comment); /* allocate a structure */ #define MCHNEW(errctx, typ, comment) \ ((typ *)mchalo(errctx, sizeof(typ), comment)) /* free a block of memory */ /* void mchfre(uchar *ptr); */ #define mchfre(ptr) (osfree(ptr)) #endif /* MCH_INCLUDED */ qtads-2.1.7/tads2/mcl.h000066400000000000000000000026431265017072300146060ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/MCL.H,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name mcl.h - memory cache loader Function Object loader definitions Notes The memory cache object loader is a subsystem used by the memory cache manager to load objects from a file. The cache manager obtains a loader context during initialization, then refers to objects through loader handles. All loading is actually accomplished through client callbacks. There is no general loader implementation. The "load handle" is defined here suitably for an fseek() location; it is assumed that clients will store the fseek() location of an object when setting up the load map, then use information stored at this fseek address to complete the load on demand. Modified 08/03/91 MJRoberts - creation */ #ifndef MCL_INCLUDED #define MCL_INCLUDED #ifndef STD_INCLUDED # include "std.h" #endif #ifndef ERR_INCLUDED # include "err.h" #endif /* loader context */ typedef struct mclcxdef mclcxdef; struct mclcxdef { errcxdef *mclcxerr; /* error handling context */ }; /* loader handle */ typedef ulong mclhd; /* essentially a seek address */ #endif /* MCL_INCLUDED */ qtads-2.1.7/tads2/mcm.c000066400000000000000000001123761265017072300146070ustar00rootroot00000000000000/* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name mcm.c - memory cache manager Function Manages the high-level memory cache Notes The memory cache manager is a subsystem providing high-level object cache management. Each object in a cache can be swapped out to disk or relocated when it is not locked, allowing the cache manager to keep memory unfragmented and overcommitted. Modified 08/03/91 MJRoberts - creation */ #include #include #include #include "os.h" #include "mcm.h" #include "mch.h" #include "err.h" /* get an unused object cache entry, allocating a new page if needed */ static mcmodef *mcmoal(mcmcx1def *ctx, mcmon *objnum); /* split a (previously free) block into two pieces */ static void mcmsplt(mcmcx1def *ctx, mcmon n, ushort siz); /* unlink an object from a doubly-linked list */ static void mcmunl(mcmcx1def *ctx, mcmon n, mcmon *lst); /* initialize a cache, return cache context */ /* find free block: find a block from the free pool to satisfy a request */ static mcmodef *mcmffb(mcmcx1def *ctx, ushort siz, mcmon *nump); /* add page pagenum, initializing entries after firstunu to unused */ static void mcmadpg(mcmcx1def *ctx, uint pagenum, mcmon firstunu); /* link an object into a doubly-linked list at the head of the list */ static void mcmlnkhd(mcmcx1def *ctx, mcmon *lst, mcmon n); /* try to allocate a new chunk from the heap */ static uchar *mcmhalo(mcmcx1def *ctx); /* relocate blocks in a heap */ static uchar *mcmreloc(mcmcx1def *ctx, uchar *start, uchar *end); /* find next free heap block */ static uchar *mcmffh(mcmcx1def *ctx, uchar *p); #ifdef NEVER /* update entry to account for a block relocation */ static void mcmmove(mcmcx1def *ctx, mcmodef *obj, uchar *newaddr); #else /* NEVER */ #define mcmmove(ctx, o, new) ((o)->mcmoptr = (new)) #endif /* NEVER */ /* consolidate two contiguous free blocks into a single block */ static void mcmconsol(mcmcx1def *ctx, uchar *p); /* collect garbage in all heaps */ static void mcmgarb(mcmcx1def *ctx); /* make some room by swapping or discarding objects */ static int mcmswap(mcmcx1def *ctx, ushort siz); /* toss out an object; returns TRUE if successful */ static int mcmtoss(mcmcx1def *ctx, mcmon objnum); /* next heap block, given a heap block (points to header) */ /* uchar *mcmhnxt(mcmcx1def *ctx, uchar *p) */ #define mcmnxh(ctx, p) \ ((p) + osrndsz(sizeof(mcmon)) + mcmgobje(ctx, *(mcmon*)(p))->mcmosiz) #ifdef DEBUG # define MCMCLICTX(ctx) assert(*(((ulong *)ctx) - 1) == 0x02020202) # define MCMGLBCTX(ctx) assert(*(((ulong *)ctx) - 1) == 0x01010101) #else /* DEBUG */ # define MCMCLICTX(ctx) # define MCMGLBCTX(ctx) #endif /* DEBUG */ /* initialize a new client context */ mcmcxdef *mcmcini(mcmcx1def *globalctx, uint pages, void (*loadfn)(void *, mclhd, uchar *, ushort), void *loadctx, void (*revertfn)(void *, mcmon), void *revertctx) { mcmcxdef *ret; ushort siz; siz = sizeof(mcmcxdef) + sizeof(mcmon *) * (pages - 1); IF_DEBUG(siz += sizeof(ulong)); ret = (mcmcxdef *)mchalo(globalctx->mcmcxerr, siz, "mcm client context"); IF_DEBUG((*(ulong *)ret = 0x02020202, ret = (mcmcxdef *)((uchar *)ret + sizeof(ulong)))); ret->mcmcxmsz = pages; ret->mcmcxgl = globalctx; ret->mcmcxldf = loadfn; ret->mcmcxldc = loadctx; ret->mcmcxrvf = revertfn; ret->mcmcxrvc = revertctx; ret->mcmcxflg = 0; memset(ret->mcmcxmtb, 0, (size_t)(pages * sizeof(mcmon *))); return(ret); } /* uninitialize a client context */ void mcmcterm(mcmcxdef *ctx) { /* delete the context memory */ mchfre(ctx); } /* initialize a new global context */ mcmcx1def *mcmini(ulong max, uint pages, ulong swapsize, osfildef *swapfp, char *swapfilename, errcxdef *errctx) { mcmcx1def *ctx; /* newly-allocated cache manager context */ uchar *noreg chunk;/* 1st chunk of memory managed by this cache mgr */ mcmodef *obj; /* pointer to a cache object entry */ ushort siz; /* size of current thing being allocated */ ushort rem; /* bytes remaining in chunk */ int err; NOREG((&chunk)) /* make sure 'max' is big enough - must be at least one chunk */ if (max < (ulong)MCMCHUNK) max = (ulong)MCMCHUNK; /* allocate space for control structures from low-level heap */ rem = MCMCHUNK; IF_DEBUG(rem += sizeof(long)); chunk = mchalo(errctx, rem, "mcmini"); IF_DEBUG((*(ulong *)chunk = 0x01010101, chunk += sizeof(ulong), rem -= sizeof(ulong))); ctx = (mcmcx1def *)chunk; /* put context at start of chunk */ /* initialize swapper; clean up if it fails */ ERRBEGIN(errctx) mcsini(&ctx->mcmcxswc, ctx, swapsize, swapfp, swapfilename, errctx); ERRCATCH(errctx, err) mcsclose(&ctx->mcmcxswc); mchfre(chunk); errsig(errctx, err); ERREND(errctx) chunk += sizeof(mcmcx1def); /* rest of chunk is after context */ rem -= sizeof(mcmcx1def); /* remove from remaining size counter */ /* allocate the page table (an array of pointers to pages) */ ctx->mcmcxtab = (mcmodef **)chunk; /* put at bottom of chunk */ siz = pages * sizeof(mcmodef *); /* calcuate size of table */ memset(ctx->mcmcxtab, 0, (size_t)siz); /* clear entire table */ chunk += siz; /* reflect size of table */ rem -= siz; /* take it out of the remaining count */ /* here we begin normal heap marking with object references */ ctx->mcmcxhpch = (mcmhdef *)chunk; /* set start of heap chain */ chunk += sizeof(mcmhdef); rem -= sizeof(mcmhdef); ctx->mcmcxhpch->mcmhnxt = (mcmhdef *)0; /* no next heap in chain yet */ /* allocate the first page */ *(mcmon *)chunk = 0; /* set object number header in chunk */ chunk += osrndsz(sizeof(mcmon)); rem -= osrndsz(sizeof(mcmon)); ctx->mcmcxtab[0] = (mcmodef *)chunk; /* put at bottom of chunk */ memset(ctx->mcmcxtab[0], 0, (size_t)MCMPAGESIZE); chunk += MCMPAGESIZE; /* reflect size of page */ rem -= MCMPAGESIZE; /* take it out of the remainder */ /* set up the first page with an entry for itself */ obj = mcmgobje(ctx, (mcmon)0); /* point to first page entry */ obj->mcmoflg = MCMOFPRES | MCMOFNODISC | MCMOFPAGE | MCMOFNOSWAP; obj->mcmoptr = (uchar *)ctx->mcmcxtab[0]; obj->mcmosiz = MCMPAGESIZE; /* set up the rest of the context */ ctx->mcmcxlru = ctx->mcmcxmru = MCMONINV; /* no mru/lru list yet */ ctx->mcmcxmax = max - (ulong)MCMCHUNK; ctx->mcmcxpage = 1; /* next page slot to be allocated will be #1 */ ctx->mcmcxpgmx = pages; /* max number of pages we can allocate */ ctx->mcmcxerr = errctx; ctx->mcmcxcsw = mcmcswf; /* set up the free list with the remainder of the chunk */ ctx->mcmcxfre = 1; /* we've allocated object 0; obj 1 is free space */ obj = mcmgobje(ctx, ctx->mcmcxfre); /* point to free object entry */ obj->mcmonxt = obj->mcmoprv = MCMONINV; /* end of free list */ obj->mcmoflg = MCMOFFREE; /* mark the free block as such */ *(mcmon *)chunk = ctx->mcmcxfre; /* set free list header */ chunk += osrndsz(sizeof(mcmon)); rem -= osrndsz(sizeof(mcmon)); obj->mcmoptr = chunk; /* rest of chunk */ obj->mcmosiz = rem - osrndsz(sizeof(mcmon)); /* remaining size in chunk */ /* set flag for end of chunk (invalid object header) */ *((mcmon *)(chunk + rem - osrndsz(sizeof(mcmon)))) = MCMONINV; /* set up the unused entry list with the remaining headers in the page */ mcmadpg(ctx, 0, 2); return(ctx); } /* * Uninitialize the cache manager. Frees the memory allocated for the * cache, including the context structure itself. */ void mcmterm(mcmcx1def *ctx) { mcmhdef *cur, *nxt; /* * Free each chunk in the cache block list, *except* the last one. The * last one is special: it's actually the first chunk allocated, since * we build the list in reverse order, and the first chunk pointer * points into the middle of the actual allocation block, since we * sub-allocated the context structure itself and the page table out of * that memory. */ for (cur = ctx->mcmcxhpch ; cur != 0 && cur->mcmhnxt != 0 ; cur = nxt) { /* remember the next chunk, and delete this one */ nxt = cur->mcmhnxt; mchfre(cur); } /* * As described above, the last chunk in the list is the first * allocated, and it points into the middle of the actual allocated * memory block. Luckily, we do have a handy pointer to the start of * the memory block, namely the context pointer - it's the first thing * allocated out of the block, so it's the same as the block pointer. * Freeing the context frees this last/first chunk. */ mchfre(ctx); } /* * Allocate a new object, returning a pointer to its memory. The new * object is locked upon return. The object number for the new object * is returned at *nump. */ static uchar *mcmalo1(mcmcx1def *ctx, ushort siz, mcmon *nump) { mcmon n; mcmodef *o; uchar *chunk; MCMGLBCTX(ctx); /* round size to appropriate multiple */ siz = osrndsz(siz); /* if it's bigger than the chunk size, we can't allocate it */ if (siz > MCMCHUNK) errsig(ctx->mcmcxerr, ERR_BIGOBJ); startover: /* look in the free block chain for a fit to the request */ o = mcmffb(ctx, siz, &n); if (n != MCMONINV) { mcmsplt(ctx, n, siz); /* split the block if necessary */ mcmgobje(ctx, n)->mcmoflg = MCMOFNODISC | MCMOFLOCK | MCMOFPRES; mcmgobje(ctx, n)->mcmolcnt = 1; /* one locker so far */ *nump = n; return(o->mcmoptr); } /* nothing found; we must get space out of the heap if possible */ chunk = mcmhalo(ctx); /* get space from heap */ if (!chunk) goto error; /* can't get any more space from heap */ o = mcmoal(ctx, &n); /* set up cache entry for free space */ if (n == MCMONINV) { mcmhdef *chunk_hdr = ((mcmhdef *)chunk) - 1; ctx->mcmcxhpch = chunk_hdr->mcmhnxt; mchfre(chunk_hdr); goto error; /* any error means we can't allocate the memory */ } *(mcmon *)chunk = n; /* set object header */ chunk += osrndsz(sizeof(mcmon)); o->mcmoptr = chunk; o->mcmosiz = MCMCHUNK - osrndsz(sizeof(mcmon)); o->mcmoflg = MCMOFFREE; mcmlnkhd(ctx, &ctx->mcmcxfre, n); goto startover; /* try again, now that we have some memory */ error: *nump = MCMONINV; return((uchar *)0); } static void mcmcliexp(mcmcxdef *cctx, mcmon clinum) { /* add global number to client mapping table at client number */ if (cctx->mcmcxmtb[clinum >> 8] == (mcmon *)0) { mcmcx1def *ctx = cctx->mcmcxgl; int i; mcmon *p; /* this page is not allocated - allocate it */ p = (mcmon *)mchalo(ctx->mcmcxerr, (256 * sizeof(mcmon)), "client mapping page"); cctx->mcmcxmtb[clinum >> 8] = p; for (i = 0 ; i < 256 ; ++i) *p++ = MCMONINV; } } /* high-level allocate: try, collect garbage, then try again */ uchar *mcmalo0(mcmcxdef *cctx, ushort siz, mcmon *nump, mcmon clinum, int noclitrans) { uchar *ret; mcmcx1def *ctx = cctx->mcmcxgl; /* global context */ mcmon glb; /* global object number allocated */ MCMCLICTX(cctx); MCMGLBCTX(ctx); /* try once */ if ((ret = mcmalo1(ctx, siz, &glb)) != 0) goto done; /* collect some garbage */ mcmgarb(ctx); /* try swapping until we get the memory or have nothing left to swap */ for ( ;; ) { /* try again */ if ((ret = mcmalo1(ctx, siz, &glb)) != 0) goto done; /* nothing left to swap? */ if (!mcmswap(ctx, siz)) break; /* try yet again */ if ((ret = mcmalo1(ctx, siz, &glb)) != 0) goto done; /* collect garbage once again */ mcmgarb(ctx); } /* try again */ if ((ret = mcmalo1(ctx, siz, &glb)) != 0) goto done; /* we have no other way of getting more memory, so signal an error */ errsig(ctx->mcmcxerr, ERR_NOMEM1); NOTREACHEDV(uchar *); done: if (noclitrans) { *nump = glb; return(ret); } /* we have an object - generate client number */ if (clinum == MCMONINV) { /* find a free number */ mcmon **p; uint i; mcmon j; mcmon *q; int found = FALSE; int unused = -1; for (i = 0, p = cctx->mcmcxmtb ; i < cctx->mcmcxmsz ; ++i, ++p) { if (*p) { for (j = 0, q = *p ; j < 256 ; ++j, ++q) { if (*q == MCMONINV) { found = TRUE; break; } } } else if (unused == -1) unused = i; /* note an unused page mapping table */ if (found) break; } if (found) clinum = (i << 8) + j; else if (unused != -1) clinum = (unused << 8); else errsig(ctx->mcmcxerr, ERR_CLIFULL); } /* expand client mapping table if necessary */ mcmcliexp(cctx, clinum); /* make sure the entry isn't already in use */ if (mcmc2g(cctx, clinum) != MCMONINV) errsig(ctx->mcmcxerr, ERR_CLIUSE); cctx->mcmcxmtb[clinum >> 8][clinum & 255] = glb; if (nump) *nump = clinum; return(ret); } /* reserve space for an object at a client object number */ void mcmrsrv(mcmcxdef *cctx, ushort siz, mcmon clinum, mclhd loadhd) { mcmcx1def *ctx = cctx->mcmcxgl; /* global context */ mcmon glb; /* global object number allocated */ mcmodef *o; MCMCLICTX(cctx); MCMGLBCTX(ctx); o = mcmoal(ctx, &glb); /* get a new object header */ if (!o) errsig(ctx->mcmcxerr, ERR_NOHDR); /* can't get a new header */ o->mcmoldh = loadhd; o->mcmoflg = 0; o->mcmosiz = siz; mcmcliexp(cctx, clinum); if (mcmc2g(cctx, clinum) != MCMONINV) errsig(ctx->mcmcxerr, ERR_CLIUSE); cctx->mcmcxmtb[clinum >> 8][clinum & 255] = glb; } /* resize an existing object */ uchar *mcmrealo(mcmcxdef *cctx, mcmon cliobj, ushort newsize) { mcmcx1def *ctx = cctx->mcmcxgl; /* global context */ mcmon obj = mcmc2g(cctx, cliobj); mcmodef *o = mcmgobje(ctx, obj); mcmon nxt; mcmodef *nxto; uchar *p; int local_lock; MCMCLICTX(cctx); MCMGLBCTX(ctx); newsize = osrndsz(newsize); /* make sure the object is locked, and note if we locked it */ if ((local_lock = !(o->mcmoflg & MCMOFLOCK)) != 0) (void)mcmlck(cctx, cliobj); ERRBEGIN(ctx->mcmcxerr) if (newsize < o->mcmosiz) mcmsplt(ctx, obj, newsize); /* smaller; just split block */ else { /* see if there's a free block after this block */ p = o->mcmoptr; nxt = *(mcmon *)(p + o->mcmosiz); nxto = (nxt == MCMONINV) ? (mcmodef *)0 : mcmgobje(ctx, nxt); if (nxto && ((nxto->mcmoflg & MCMOFFREE) && nxto->mcmosiz >= newsize - o->mcmosiz)) { /* sanity check - make sure heap and page table agree */ assert(nxto->mcmoptr == p + o->mcmosiz + osrndsz(sizeof(mcmon))); /* annex the free block */ o->mcmosiz += nxto->mcmosiz + osrndsz(sizeof(mcmon)); /* move the free block to the unused list */ mcmunl(ctx, nxt, &ctx->mcmcxfre); nxto->mcmonxt = ctx->mcmcxunu; ctx->mcmcxunu = nxt; nxto->mcmoflg = 0; /* split the newly grown block if necessary */ mcmsplt(ctx, obj, newsize); } else { /* can't annex; allocate new memory and copy */ if (o->mcmolcnt != 1) /* if anyone else has a lock... */ errsig(ctx->mcmcxerr, ERR_REALCK); /* we can't move it */ p = mcmalo0(cctx, newsize, &nxt, MCMONINV, TRUE); if (nxt == MCMONINV) errsig(ctx->mcmcxerr, ERR_NOMEM2); memcpy(p, o->mcmoptr, (size_t)o->mcmosiz); /* adjust the object entries */ nxto = mcmgobje(ctx, nxt); /* get pointer to new entry */ newsize = nxto->mcmosiz; /* get actual size of new block */ nxto->mcmoptr = o->mcmoptr; /* copy current block info to new */ nxto->mcmosiz = o->mcmosiz; o->mcmoptr = p; /* copy new block info to original entry */ o->mcmosiz = newsize; /* now fix up the heap pointers, and free the temp object */ *(mcmon *)(p - osrndsz(sizeof(mcmon))) = obj; *(mcmon *)(nxto->mcmoptr - osrndsz(sizeof(mcmon))) = nxt; mcmgunlck(ctx, nxt); mcmgfre(ctx, nxt); } } ERRCLEAN(ctx->mcmcxerr) /* release our lock, if we had to obtain one */ if (local_lock) mcmunlck(cctx, cliobj); ERRENDCLN(ctx->mcmcxerr) /* return the address of the object */ return(o->mcmoptr); } /* * Free an object by GLOBAL number: move object to free list. */ void mcmgfre(mcmcx1def *ctx, mcmon obj) { mcmodef *o = mcmgobje(ctx, obj); MCMGLBCTX(ctx); /* signal an error if the object is locked */ if (o->mcmolcnt) errsig(ctx->mcmcxerr, ERR_LCKFRE); /* take out of LRU chain if it's in the chain */ if (o->mcmoflg & MCMOFLRU) mcmunl(ctx, obj, &ctx->mcmcxlru); /* put it in the free list */ mcmlnkhd(ctx, &ctx->mcmcxfre, obj); o->mcmoflg = MCMOFFREE; } /* * load and lock an object that has been swapped out or discarded */ uchar *mcmload(mcmcxdef *cctx, mcmon cnum) { mcmcx1def *ctx = cctx->mcmcxgl; mcmodef *o = mcmobje(cctx, cnum); mcmodef *newdef; mcmon newn; mcmon num = mcmc2g(cctx, cnum); MCMCLICTX(cctx); MCMGLBCTX(ctx); /* we first need to obtain some memory for this object */ (void)mcmalo0(cctx, o->mcmosiz, &newn, MCMONINV, TRUE); newdef = mcmgobje(ctx, newn); /* use memory block from our new object */ o->mcmoptr = newdef->mcmoptr; o->mcmosiz = newdef->mcmosiz; /* load or swap the object in */ ERRBEGIN(ctx->mcmcxerr) if (o->mcmoflg & (MCMOFNODISC | MCMOFDIRTY)) mcsin(&ctx->mcmcxswc, o->mcmoswh, o->mcmoptr, o->mcmosiz); else if (cctx->mcmcxldf) (*cctx->mcmcxldf)(cctx->mcmcxldc, o->mcmoldh, o->mcmoptr, o->mcmosiz); else errsig(ctx->mcmcxerr, ERR_NOLOAD); ERRCLEAN(ctx->mcmcxerr) mcmgunlck(ctx, newn); /* unlock the object */ mcmgfre(ctx, newn); /* don't need new memory after all */ ERRENDCLN(ctx->mcmcxerr) /* unuse the new cache entry we obtained (we just wanted the memory) */ /* @@@ */ *(mcmon *)(o->mcmoptr - osrndsz(sizeof(mcmon))) = num; /* set obj# */ newdef->mcmoflg = 0; /* mark new block as unused */ newdef->mcmonxt = ctx->mcmcxunu; /* link to unused chain */ ctx->mcmcxunu = newn; /* set flags in the newly loaded object and return */ o->mcmoflg |= MCMOFPRES | MCMOFLOCK; /* object is now present in memory */ o->mcmoflg &= ~MCMOFDIRTY; /* not written since last swapped in */ o->mcmoflg |= MCMOFNODISC; /* don't discard once it's been to swap file */ o->mcmolcnt = 1; /* one locker so far */ /* if the object is to be reverted upon loading, revert it now */ if (o->mcmoflg & MCMOFREVRT) { (*cctx->mcmcxrvf)(cctx->mcmcxrvc, cnum); o->mcmoflg &= ~MCMOFREVRT; } return(o->mcmoptr); } /* * Allocate a new object header. This doesn't allocate an object, just * the header for one. */ static mcmodef *mcmoal(mcmcx1def *ctx, mcmon *nump) { mcmodef *ret; uint pagenum; MCMGLBCTX(ctx); /* look first in list of unused headers */ startover: if (ctx->mcmcxunu != MCMONINV) { /* we have something in the unused list; return it */ *nump = ctx->mcmcxunu; ret = mcmgobje(ctx, *nump); ctx->mcmcxunu = ret->mcmonxt; ret->mcmoswh = MCSSEGINV; return(ret); } /* * No unused entries: we must create a new page. To do so, we * simply allocate memory for a new page. Allocate the memory * ourselves, to avoid deadlocking with the allocator (which can * try to get a new entry to satisfy our request for memory). */ if (ctx->mcmcxpage == ctx->mcmcxpgmx) goto error; /* no more pages */ pagenum = ctx->mcmcxpage++; /* get a new page slot */ ctx->mcmcxtab[pagenum] = (mcmodef *)mchalo(ctx->mcmcxerr, MCMPAGESIZE, "mcmoal"); mcmadpg(ctx, pagenum, MCMONINV); goto startover; error: *nump = MCMONINV; return((mcmodef *)0); } /* find free block: find a block from the free pool to satisfy allocation */ static mcmodef *mcmffb(mcmcx1def *ctx, ushort siz, mcmon *nump) { mcmon n; mcmodef *o; mcmon minn; mcmodef *mino; ushort min; MCMGLBCTX(ctx); for (minn = MCMONINV, mino = 0, n = ctx->mcmcxfre ; n != MCMONINV ; n = o->mcmonxt) { o = mcmgobje(ctx, n); if (o->mcmosiz == siz) { /* found exact match - use it immediately */ minn = n; min = siz; mino = o; break; } else if (o->mcmosiz > siz) { /* found something at least as big; is it smallest yet? */ if (minn == MCMONINV || o->mcmosiz < min) { /* yes, best fit so far, use it; but keep looking */ minn = n; mino = o; min = o->mcmosiz; } } } /* if we found something, remove from the free list */ if (minn != MCMONINV) { mcmunl(ctx, minn, &ctx->mcmcxfre); mino->mcmoflg &= ~MCMOFFREE; mino->mcmoswh = MCSSEGINV; } *nump = minn; return mino; } /* * unlink an object header from one of the doubly-linked lists */ static void mcmunl(mcmcx1def *ctx, mcmon n, mcmon *lst) { mcmodef *o = mcmgobje(ctx, n); mcmodef *nxt; mcmodef *prv; MCMGLBCTX(ctx); /* see if this is LRU chain - must deal with MRU pointer if so */ if (lst == &ctx->mcmcxlru) { /* if it's at MRU, set MRU pointer to previous object in list */ if (ctx->mcmcxmru == n) { ctx->mcmcxmru = o->mcmoprv; /* set MRU to previous in chain */ if (ctx->mcmcxmru != MCMONINV) /* set nxt for new MRU */ mcmgobje(ctx, ctx->mcmcxmru)->mcmonxt = MCMONINV; else ctx->mcmcxlru = MCMONINV; /* nothing in list; clear LRU */ } o->mcmoflg &= ~MCMOFLRU; } nxt = o->mcmonxt == MCMONINV ? (mcmodef *)0 : mcmgobje(ctx, o->mcmonxt); prv = o->mcmoprv == MCMONINV ? (mcmodef *)0 : mcmgobje(ctx, o->mcmoprv); /* set back link for next object, if there is a next object */ if (nxt) nxt->mcmoprv = o->mcmoprv; /* set forward link for previous object, or head if no previous object */ if (prv) prv->mcmonxt = o->mcmonxt; else *lst = o->mcmonxt; o->mcmonxt = o->mcmoprv = MCMONINV; } /* link an item to the head of a doubly-linked list */ static void mcmlnkhd(mcmcx1def *ctx, mcmon *lst, mcmon n) { MCMGLBCTX(ctx); if (*lst != MCMONINV) mcmgobje(ctx, *lst)->mcmoprv = n; mcmgobje(ctx, n)->mcmonxt = *lst; /* next is previous head of list */ *lst = n; /* make object new head of list */ mcmgobje(ctx, n)->mcmoprv = MCMONINV; /* there is no previous entry */ } /* add page pagenum, initializing entries after firstunu to unused */ static void mcmadpg(mcmcx1def *ctx, uint pagenum, mcmon firstunu) { mcmon unu; mcmodef *obj; mcmon lastunu; MCMGLBCTX(ctx); unu = (firstunu == MCMONINV ? pagenum * MCMPAGECNT : firstunu); ctx->mcmcxunu = unu; lastunu = (pagenum * MCMPAGECNT) + MCMPAGECNT - 1; for (obj = mcmgobje(ctx, unu) ; unu < lastunu ; ++obj) obj->mcmonxt = ++unu; obj->mcmonxt = MCMONINV; } /* * split a previously-free block into two chunks, adding the remainder * back into the free list, if there's enough left over */ static void mcmsplt(mcmcx1def *ctx, mcmon n, ushort siz) { mcmodef *o = mcmgobje(ctx, n); mcmon newn; mcmodef *newp; MCMGLBCTX(ctx); if (o->mcmosiz < siz + MCMSPLIT) return; /* don't split; we're done */ newp = mcmoal(ctx, &newn); if (newn == MCMONINV) return; /* ignore error - just skip split */ /* set up the new entry, and link into free list */ *(mcmon *)(o->mcmoptr + siz) = newn; newp->mcmoptr = o->mcmoptr + siz + osrndsz(sizeof(mcmon)); newp->mcmosiz = o->mcmosiz - siz - osrndsz(sizeof(mcmon)); newp->mcmoflg = MCMOFFREE; mcmlnkhd(ctx, &ctx->mcmcxfre, newn); o->mcmosiz = siz; /* size of new object is now exactly as request */ } /* allocate a new chunk from the heap if possible */ static uchar *mcmhalo(mcmcx1def *ctx) { uchar *chunk; int err; # define size (MCMCHUNK + sizeof(mcmhdef) + 2*osrndsz(sizeof(mcmon))) VARUSED(err); MCMGLBCTX(ctx); if (ctx->mcmcxmax < MCMCHUNK) return((uchar *)0); ERRBEGIN(ctx->mcmcxerr) chunk = mchalo(ctx->mcmcxerr, size, "mcmhalo"); ERRCATCH(ctx->mcmcxerr, err) ctx->mcmcxmax = 0; /* remember we can't allocate anything more */ return((uchar *)0); /* return no memory */ ERREND(ctx->mcmcxerr) ctx->mcmcxmax -= MCMCHUNK; /* link into heap chain */ ((mcmhdef *)chunk)->mcmhnxt = ctx->mcmcxhpch; ctx->mcmcxhpch = (mcmhdef *)chunk; /*@@@@*/ *(mcmon *)(chunk + osrndsz(sizeof(mcmhdef) + MCMCHUNK)) = MCMONINV; return(chunk + sizeof(mcmhdef)); # undef size } /* "use" an object - move to most-recent position in LRU chain */ void mcmuse(mcmcx1def *ctx, mcmon obj) { mcmodef *o = mcmgobje(ctx, obj); MCMGLBCTX(ctx); if (ctx->mcmcxmru == obj) return; /* already MRU; nothing to do */ /* remove from LRU chain if it's in it */ if (o->mcmoflg & MCMOFLRU) mcmunl(ctx, obj, &ctx->mcmcxlru); /* set forward pointer of last block, if there is one */ if (ctx->mcmcxmru != MCMONINV) mcmgobje(ctx, ctx->mcmcxmru)->mcmonxt = obj; o->mcmoprv = ctx->mcmcxmru; /* point back to previous MRU */ o->mcmonxt = MCMONINV; /* nothing in list after this one */ ctx->mcmcxmru = obj; /* point MRU to new block */ /* if there's nothing in the chain at all, set LRU to this block, too */ if (ctx->mcmcxlru == MCMONINV) ctx->mcmcxlru = obj; /* note that object is in LRU chain */ o->mcmoflg |= MCMOFLRU; } /* find next free block in a heap, starting with pointer */ static uchar *mcmffh(mcmcx1def *ctx, uchar *p) { mcmodef *o; MCMGLBCTX(ctx); while (*(mcmon *)p != MCMONINV) { o = mcmgobje(ctx, *(mcmon *)p); assert(o->mcmoptr == p + osrndsz(sizeof(mcmon))); if (o->mcmoflg & MCMOFFREE) return(p); p += osrndsz(sizeof(mcmon)) + o->mcmosiz; /* move on to next chunk */ } return((uchar *)0); /* no more free blocks in heap */ } #ifdef NEVER static void mcmmove(mcmcx1def *ctx, mcmodef *o, uchar *newpage) { mcmodef **page; MCMGLBCTX(ctx); /* see if we need to update page table (we do if moving a page) */ if (o->mcmoflg & MCMOFPAGE) { for (page = ctx->mcmcxtab ; *page ; ++page) { if (*page == (mcmodef *)(o->mcmoptr)) { *page = (mcmodef *)newpag; break; } } if (!*page) printf("\n*** internal error - relocating page\n"); } o->mcmoptr = newpage; } #endif /* NEVER */ /* relocate blocks from p to (but not including) q */ static uchar *mcmreloc(mcmcx1def *ctx, uchar *p, uchar *q) { mcmodef *o; ushort dist; mcmon objnum; MCMGLBCTX(ctx); objnum = *(mcmon *)p; /* get number of free block being bubbled up */ o = mcmgobje(ctx, objnum); /* get pointer to free object */ assert(o->mcmoptr == p + osrndsz(sizeof(mcmon))); dist = osrndsz(sizeof(mcmon)) + o->mcmosiz; /* compute distance to move */ mcmmove(ctx, o, q - dist + osrndsz(sizeof(mcmon))); /* move obj to top */ memmove(p, p+dist, (size_t)(q - p - o->mcmosiz)); /* move memory */ /* update cache entries for the blocks we moved */ while (p != q - dist) { mcmmove(ctx, mcmgobje(ctx, *(mcmon *)p), p + osrndsz(sizeof(mcmon))); p = mcmnxh(ctx, p); } *(mcmon *)(q - dist) = objnum; /* set bubbled num */ return(q - dist); /* return new location of bubbled block */ } /* consolidate the two (free) blocks starting at p into one block */ static void mcmconsol(mcmcx1def *ctx, uchar *p) { uchar *q; mcmodef *obj1, *obj2; MCMGLBCTX(ctx); q = mcmnxh(ctx, p); obj1 = mcmgobje(ctx, *(mcmon *)p); obj2 = mcmgobje(ctx, *(mcmon *)q); assert(obj1->mcmoptr == p + osrndsz(sizeof(mcmon))); assert(obj2->mcmoptr == q + osrndsz(sizeof(mcmon))); obj1->mcmosiz += osrndsz(sizeof(mcmon)) + obj2->mcmosiz; mcmunl(ctx, *(mcmon *)q, &ctx->mcmcxfre); /* add second object entry to unused list */ obj2->mcmonxt = ctx->mcmcxunu; ctx->mcmcxunu = *(mcmon *)q; obj2->mcmoflg = 0; } /* attempt to compact all heaps by consolidating free space */ static void mcmgarb(mcmcx1def *ctx) { mcmhdef *h; uchar *p; uchar *q; uchar *nxt; ushort flags; MCMGLBCTX(ctx); for (h = ctx->mcmcxhpch ; h ; h = h->mcmhnxt) { p = (uchar *)(h+1); /* get pointer to actual heap */ p = mcmffh(ctx, p); /* get first free block in heap */ if (!p) continue; /* can't do anything - no free blocks */ nxt = mcmnxh(ctx, p); /* remember immediate next block */ for (q=p ;; ) { q = mcmnxh(ctx, q); /* find next chunk in heap */ if (*(mcmon *)q == MCMONINV) break; /* reached end of heap */ assert(mcmgobje(ctx, *(mcmon *)q)->mcmoptr == q + osrndsz(sizeof(mcmon))); flags = mcmgobje(ctx, *(mcmon *)q)->mcmoflg; /* get flags */ /* if the block is locked, p can't be relocated */ if (flags & MCMOFLOCK) { p = mcmffh(ctx, q); /* find next free block after p */ q = p; if (p) continue; /* try again; start with next free block */ else break; /* no more free blocks - done with heap */ } /* if the block is free, we can relocate between p and q */ if (flags & MCMOFFREE) { if (q != nxt) p = mcmreloc(ctx, p, q); /* relocate */ mcmconsol(ctx, p); /* consolidate two free blocks */ /* resume looking, starting with consolidated block */ nxt = mcmnxh(ctx, p); q = p; continue; } } } } /* toss out a particular object */ static int mcmtoss(mcmcx1def *ctx, mcmon n) { mcmodef *o = mcmgobje(ctx, n); mcmodef *newp; mcmon newn; MCMGLBCTX(ctx); /* make a new block for the free space */ newp = mcmoal(ctx, &newn); if (newn == MCMONINV) return(FALSE); /* ignore the error, but can't toss it out */ /* write object to swap file if not discardable */ if (o->mcmoflg & (MCMOFNODISC | MCMOFDIRTY)) { mcsseg old_swap_seg; /* * If this object was last loaded out of the load file, rather * than the swap file, don't attempt to find it in the swap file * -- so note by setting the old swap segment parameter to null. */ if (!(o->mcmoflg & MCMOFNODISC)) old_swap_seg = o->mcmoswh; else old_swap_seg = MCSSEGINV; o->mcmoswh = mcsout(&ctx->mcmcxswc, (uint)n, o->mcmoptr, o->mcmosiz, old_swap_seg, o->mcmoflg & MCMOFDIRTY); } /* give the object's space to the newly created block */ newp->mcmoptr = o->mcmoptr; newp->mcmosiz = o->mcmosiz; newp->mcmoflg = MCMOFFREE; /*@@@*/ *(mcmon *)(o->mcmoptr - osrndsz(sizeof(mcmon))) = newn; mcmlnkhd(ctx, &ctx->mcmcxfre, newn); o->mcmoflg &= ~MCMOFPRES; /* object is no longer in memory */ mcmunl(ctx, n, &ctx->mcmcxlru); /* remove from LRU list */ return(TRUE); /* successful, so return TRUE */ } /* swap or discard to make room for siz; return 0 if nothing swapped */ static int mcmswap(mcmcx1def *ctx, ushort siz) { mcmon n; mcmodef *o; mcmon nxt; int pass; /* pass 1: swap one piece big enough */ /* pass 2: swap enough pieces to add up to right size */ ushort tot; MCMGLBCTX(ctx); for (pass = 1, tot = 0 ; pass < 3 && tot < siz ; ++pass) { for (n = ctx->mcmcxlru ; n != MCMONINV && tot < siz ; n = nxt) { o = mcmgobje(ctx, n); nxt = o->mcmonxt; /* get next now, as we may unlink */ if (!(o->mcmoflg & (MCMOFLOCK | MCMOFNOSWAP | MCMOFPAGE)) && (pass == 2 || o->mcmosiz >= siz)) { /* toss out, and add into size if successful */ if (mcmtoss(ctx, n)) tot += o->mcmosiz; } } } /* if we managed to remove anything, return TRUE, otherwise FALSE */ return(tot != 0); } /* compute size of cache */ ulong mcmcsiz(mcmcxdef *cctx) { mcmcx1def *ctx = cctx->mcmcxgl; mcmhdef *p; ulong tot; MCMCLICTX(cctx); MCMGLBCTX(ctx); /* count number of heaps, adding in chunk size for each */ for (tot = 0, p = ctx->mcmcxhpch ; p ; p = p->mcmhnxt) tot += MCMCHUNK; return(tot); } #ifdef MCM_NO_MACRO /* routines that can be either macros or functions */ uchar *mcmlck(mcmcxdef *ctx, mcmon objnum) { mcmodef *o = mcmobje(ctx, objnum); if ((o->mcmoflg & MCMOFFREE) != 0 || mcmc2g(ctx, objnum) == MCMONINV) { errsig(ctx->mcmcxgl->mcmcxerr, ERR_INVOBJ); return 0; } else if (o->mcmoflg & MCMOFPRES) { o->mcmoflg |= MCMOFLOCK; ++(o->mcmolcnt); return(o->mcmoptr); } else return(mcmload(ctx, objnum)); } void mcmunlck(mcmcxdef *ctx, mcmon obj) { mcmodef *o = mcmobje(ctx, obj); if (o->mcmoflg & MCMOFLOCK) { if (!(--(o->mcmolcnt))) { o->mcmoflg &= ~MCMOFLOCK; mcmuse(ctx->mcmcxgl, mcmc2g(ctx, obj)); } } } void mcmgunlck(mcmcx1def *ctx, mcmon obj) { mcmodef *o = mcmgobje(ctx, obj); if (o->mcmoflg & MCMOFLOCK) { if (!(--(o->mcmolcnt))) { o->mcmoflg &= ~MCMOFLOCK; mcmuse(ctx, obj); } } } #endif /* MCM_NO_MACRO */ /* * Change an object's swap file handle. This routine will only be * called for an object that is either present or swapped out (i.e., an * object with a valid mcsseg number in its swap state). */ void mcmcswf(mcmcx1def *ctx, mcmon objn, mcsseg swapn, mcsseg oldswapn) { mcmodef *o = mcmgobje(ctx, objn); MCMGLBCTX(ctx); /* * Reset the swap number only if the object is swapped out and its * swap file number matches the old one, or the object is currently * present (in which case the swap file number is irrelevant and can * be replaced). */ if (((o->mcmoflg & (MCMOFDIRTY | MCMOFNODISC)) && o->mcmoswh == oldswapn) || (o->mcmoflg & MCMOFPRES)) o->mcmoswh = swapn; } void mcmfre(mcmcxdef *ctx, mcmon obj) { /* free the actual object */ mcmgfre(ctx->mcmcxgl, mcmc2g(ctx, obj)); /* unmap the client object number */ mcmc2g(ctx, obj) = MCMONINV; } qtads-2.1.7/tads2/mcm.h000066400000000000000000000400241265017072300146020ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/MCM.H,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name mcm.h - memory cache manager Function Definitions for memory cache manager Notes None Modified 08/03/91 MJRoberts - creation */ #ifndef MCM_INCLUDED #define MCM_INCLUDED #ifndef OS_INCLUDED # include "os.h" #endif #ifndef STD_INCLUDED # include "std.h" #endif #ifndef MCS_INCLUDED # include "mcs.h" #endif #ifndef MCL_INCLUDED # include "mcl.h" #endif #include #ifdef __cplusplus extern "C" { #endif /* if the os layer doesn't want long mcm macros, we don't either */ #ifdef OS_MCM_NO_MACRO # define MCM_NO_MACRO #endif /* * mcmon - cache object number. Each object in the cache is referenced * by an object number, which is a number of this type. */ typedef ushort mcmon; /* invalid object number - used to indicate N/A or no object */ #define MCMONINV ((mcmon)~0) /* invalid page number */ #define MCMPINV ((uint)~0) /* * mcmodef - cache object definition. Each cache object is managed by * this structure. Pointers for a doubly-linked list are provided; * these are used to maintain a recent-use list for objects in memory, * and to maintain a free list for cache object entries not in use. A * set of flag bits is provided, as is the size of the object and its * location (which is a memory pointer for objects in memory, a swap * file handle for swapped-out objects, or a load-file handle for * objects that are unloaded). */ typedef struct mcmodef mcmodef; struct mcmodef { uchar *mcmoptr; /* memory pointer (if object is present) */ union { mcsseg mcmolocs; /* swap segment handle */ mclhd mcmolocl; /* load file object handle */ } mcmoloc; /* object's external location */ # define mcmoswh mcmoloc.mcmolocs # define mcmoldh mcmoloc.mcmolocl mcmon mcmonxt; /* next object in this list */ mcmon mcmoprv; /* previous object in this list */ ushort mcmoflg; /* flags for this cache object */ # define MCMOFDIRTY 0x01 /* object has been written */ # define MCMOFNODISC 0x02 /* not in load file (can't be discarded) */ # define MCMOFLOCK 0x04 /* object is locked */ # define MCMOFPRES 0x08 /* object is present in memory */ # define MCMOFLRU 0x10 /* object is in LRU chain */ # define MCMOFPAGE 0x20 /* object is a cache manager page */ # define MCMOFNOSWAP 0x40 /* object cannot be swapped out */ # define MCMOFFREE 0x80 /* entry refers to a free block of memory */ # define MCMOFREVRT 0x100 /* call revert callback upon loading */ uchar mcmolcnt; /* lock count */ ushort mcmosiz; /* size of the object */ }; /* heap header - allocate one of these in each heap */ typedef struct mcmhdef mcmhdef; struct mcmhdef { mcmhdef *mcmhnxt; /* next heap in this chain */ }; /* GLOBAL cache manager context: tracks cache manager state */ typedef struct mcmcx1def mcmcx1def; struct mcmcx1def { mcmodef **mcmcxtab; /* page table for the cache */ errcxdef *mcmcxerr; /* error handling context */ mcmhdef *mcmcxhpch; /* heap chain pointer */ mcscxdef mcmcxswc; /* swap manager context */ mclcxdef mcmcxldc; /* loader context */ ulong mcmcxmax; /* maximum amount of actual heap we can ever alloc */ mcmon mcmcxlru; /* least recently used object still in memory */ mcmon mcmcxmru; /* most recently used object */ mcmon mcmcxfre; /* head of free list */ mcmon mcmcxunu; /* head of unused list */ ushort mcmcxpage; /* last page table slot used */ ushort mcmcxpgmx; /* maximum number of pages we can allocate */ void (*mcmcxcsw)(mcmcx1def *, mcmon, mcsseg, mcsseg); /* change swap handle in object to new swap handle */ }; /* CLIENT cache manager context: used by client to request mcm services */ typedef struct mcmcxdef mcmcxdef; struct mcmcxdef { mcmcx1def *mcmcxgl; /* global cache manager context */ uint mcmcxflg; /* flags */ uint mcmcxmsz; /* maximum size of mapping table */ void (*mcmcxldf)(void *ctx, mclhd handle, uchar *ptr, ushort siz); /* callback to load objects */ void *mcmcxldc; /* context for load callback */ void (*mcmcxrvf)(void *ctx, mcmon objn); /* revert object */ void *mcmcxrvc; /* context for revert callback */ mcmon *mcmcxmtb[1]; /* mapping table */ }; /* context flags */ #define MCMCXF_NO_PRP_DEL 0x0001 /* PRPFDEL is invalid in this game file */ /* convert from a client object number to a global object number */ /* mcmon mcmc2g(mcmcxdef *ctx, mcmon objn); */ #define mcmc2g(ctx, objn) ((ctx)->mcmcxmtb[(objn)>>8][(objn)&255]) /* * FREE LIST: this is a list, headed by context->mcmcxfre and chained * forward and back by mcmonxt and mcmoprv, consisting of free memory * blocks. These refer to blocks in the heap that are not used by any * client objects. */ /* * UNUSED LIST: this is a list, headed by context->mcmcxunu and chained * forward by mcmonxt (not back, because it's never necessary to take * anything out of the list except at the head, nor to search the list * backwards), of unused cache object entries. These entries are not * associated with any client object or with any heap memory. This list * is used to get a new cache object header, and deleted cache objects * are placed onto this list. */ /* * LRU LIST: this is a list of in-memory blocks in ascending order of * recency of use by the client. Each time a client unlocks a block, * the block is moved to the most recent position in the list (the end * of the list). To make it fast to add a new object, we keep a pointer * to the end of the list as well as to the beginning. The start of the * list is at context->mcmcxlru, and is the least recently unlocked * block still in memory. The end of the list is at context->mcmcxmru, * and is the most recently unlocked block. */ /* * initialize the cache manager, returning a context for cache manager * operations; a null pointer is returned if insufficient heap memory is * available for initialization. The 'max' argument specifies the * maximum amount of actual low-level heap memory that the cache manager * can ever allocate on behalf of this context (of course, it can * overcommit the heap through swapping). If 'max' is less than the * size of a single heap allocation, it is adjusted upwards to that * minimum. */ mcmcx1def *mcmini(ulong max, uint pages, ulong swapsize, osfildef *swapfp, char *swapfilename, errcxdef *errctx); /* terminate the cache manager - frees the structure and all cache memory */ void mcmterm(mcmcx1def *ctx); /* allocate a client context */ mcmcxdef *mcmcini(mcmcx1def *globalctx, uint pages, void (*loadfn)(void *, mclhd, uchar *, ushort), void *loadctx, void (*revertfn)(void *, mcmon), void *revertctx); /* terminate a client context - frees the structure memory */ void mcmcterm(mcmcxdef *ctx); /* * Lock a cache object, bringing it into memory if necessary. Returns * a pointer to the memory containing the object. A null pointer is * returned in case of error. The object remains fixed in memory at the * returned location until unlocked. Locks are stacked; that is, if * an object is locked twice in succession, it needs to be unlocked * twice in succession before it is actually unlocked. */ #ifdef MCM_NO_MACRO uchar *mcmlck(mcmcxdef *ctx, mcmon objnum); #else /* MCM_NO_MACRO */ /* uchar *mcmlck(mcmcxdef *ctx, mcmon objnum); */ #define mcmlck(ctx,num) \ ((mcmobje(ctx,num)->mcmoflg & MCMOFPRES ) ? \ ((mcmobje(ctx,num)->mcmoflg|=MCMOFLOCK), \ ++(mcmobje(ctx,num)->mcmolcnt), mcmobje(ctx,num)->mcmoptr) \ : mcmload(ctx,num)) #endif /* MCM_NO_MACRO */ /* * Unlock a cache object, allowing it to be moved and swapped. * Unlocking an object moves it to the end (i.e., most recently used) * position on the LRU chain, making it the least favorable to swap out * or discard. This happens at unlock time (rather than lock time) * because presumably the client has been using the object the entire * time it was locked. For this reason, and to keep memory unfragmented * as much as possible, objects should not be kept locked except when * actually in use. Note that locks nest; if an object is locked three * times without an intervening unlock, it must be unlocked three times * in a row. An object can be unlocked even if it's not locked; doing * so has no effect. */ #ifdef MCM_NO_MACRO void mcmunlck(mcmcxdef *ctx, mcmon objnum); #else /* MCM_NO_MACRO */ /* void mcmunlck(mcmcxdef *ctx, mcmon objnum); */ #define mcmunlck(ctx,obj) \ ((mcmobje(ctx,obj)->mcmoflg & MCMOFLOCK) ? \ (--(mcmobje(ctx,obj)->mcmolcnt) ? (void)0 : \ ((mcmobje(ctx,obj)->mcmoflg&=(~MCMOFLOCK)), \ mcmuse((ctx)->mcmcxgl,mcmc2g(ctx,obj)))) : (void)0) #endif /* MCM_NO_MACRO */ /* * Allocate a new cache object. The new object is locked upon return. * A pointer to the memory for the new object is returned, and * the object number is returned at *nump. A null pointer is returned * if the object cannot be allocated. */ /* uchar *mcmalo(mcmcxdef *ctx, ushort siz, mcmon *nump); */ #define mcmalo(ctx, siz, nump) mcmalo0(ctx, siz, nump, MCMONINV, FALSE) /* * Reserve space for an object, giving it a particular client object * number. This doesn't actually allocate any space for the object, but * just sets it up so that it can be loaded by the client when it's * needed. */ void mcmrsrv(mcmcxdef *ctx, ushort siz, mcmon clinum, mclhd loadhd); /* * Allocate a new cache object, and associate it with a particular * client object number. An error is signalled if the client object * number is already in use. */ /* uchar *mcmalonum(mcmcxdef *ctx, ushort siz, mcmon num); */ #define mcmalonum(ctx, siz, num) mcmalo0(ctx, siz, (mcmon *)0, num, FALSE) /* * Reallocate an existing object. The object's size is increased * or reduced according to newsize. The object is locked if it is * not already, and the address of the object's memory is returned. * Note that the object can move when reallocated, even if it was * locked before the call. */ uchar *mcmrealo(mcmcxdef *ctx, mcmon objnum, ushort newsize); /* * Touch a cache object (rendering it dirty). When an object is * written, the client must touch it to ensure that the version in * memory is not discarded. The cache manager attempts to optimize * activity by not writing objects that can be reconstructed from the * load or swap file. Touching the object informs the cache manager * that the object is different from any version it has in a swap or * load file. */ /* void mcmtch(mcmcxdef *ctx, mcmon objnum); */ #define mcmtch(ctx,obj) \ (mcmobje(ctx,obj)->mcmoflg |= MCMOFDIRTY) /* was: (mcmobje(ctx,obj)->mcmoflg |= (MCMOFDIRTY | MCMOFNODISC)) */ /* get size of a cache manager object - object need not be locked */ /* ushort mcmobjsiz(mcmcxdef *ctx, mcmon objn); */ #define mcmobjsiz(ctx, objn) (mcmobje(ctx, objn)->mcmosiz) /* determine if object has ever been touched */ /* int mcmobjdirty(mcmcxdef *ctx, mcmon objn); */ #define mcmobjdirty(ctx, objn) \ (mcmobje(ctx, objn)->mcmoflg & (MCMOFDIRTY | MCMOFNODISC)) /* get object's memory pointer - object must be locked for valid result */ /* uchar *mcmobjptr(mcmcxdef *ctx, mcmon objn); */ #define mcmobjptr(ctx, objn) (mcmobje(ctx, objn)->mcmoptr) /* * Free an object. The memory occupied by the object is discarded, and * the object may no longer be referenced. */ void mcmfre(mcmcxdef *ctx, mcmon obj); /* * "Revert" an object - convert it back to original state. This * routine just invokes a client callback to do the actual reversion * work. The callback is called immediately if the object is already * present in memory, but is deferred until the object is loaded/swapped * in if the object is not in memory. */ /* void mcmrevert(mcmcxdef *ctx, mcmon objn); */ #define mcmrevert(ctx, objn) \ ((mcmobje(ctx, objn)->mcmoflg & MCMOFPRES) ? \ ((*(ctx)->mcmcxrvf)((ctx)->mcmcxrvc, objn), DISCARD 0) \ : DISCARD (mcmobje(ctx, objn)->mcmoflg |= MCMOFREVRT)) /* get current size of object cache */ ulong mcmcsiz(mcmcxdef *ctx); /* change an object's swap handle (used by swapper) */ /* void mcmcsw(mcmcx1def *ctx, ushort objn, mcsseg swapn, mcsseg oldswn); */ #define mcmcsw(ctx, objn, swapn, oldswapn) \ ((*(ctx)->mcmcxcsw)(ctx, objn, swapn, oldswapn)) /* ------------------------------- PRIVATE ------------------------------- */ /* Unlock an object by its global handle */ #ifdef MCM_NO_MACRO void mcmgunlck(mcmcx1def *ctx, mcmon objnum); #else /* MCM_NO_MACRO */ /* void mcmgunlck(mcmcx1def *ctx, mcmon objnum); */ #define mcmgunlck(ctx,obj) \ ((mcmgobje(ctx,obj)->mcmoflg & MCMOFLOCK) ? \ (--(mcmgobje(ctx,obj)->mcmolcnt) ? (void)0 : \ ((mcmgobje(ctx,obj)->mcmoflg&=(~MCMOFLOCK)), mcmuse(ctx,obj))) : \ (void)0) #endif /* MCM_NO_MACRO */ /* real memory allocator; clients use cover macros */ uchar *mcmalo0(mcmcxdef *ctx, ushort siz, mcmon *nump, mcmon clinum, int noclitrans); /* free an object by global object number */ void mcmgfre(mcmcx1def *ctx, mcmon obj); /* "use" an object (move to most-recent position in LRU chain) */ void mcmuse(mcmcx1def *ctx, mcmon n); /* * Load or swap in a cache object which is currently unloaded, locking * it before returning. Returns a pointer to the memory containing * the object, or a null pointer in case of error. The object * remains fixed in memory at the returned location until unlocked. */ uchar *mcmload(mcmcxdef *ctx, mcmon objnum); /* * Size of each chunk of memory we'll request from the heap manager. * To cut down on wasted memory from the heap manager, we'll always make * our requests in this large size, then sub-allocate out of these * chunks. */ #define MCMCHUNK 32768 /* * number of cache entries in a page - make this a power of 2 to keep * the arithmetic to find a cache object entry simple */ #define MCMPAGECNT 256 /* * size of a page, in bytes */ #define MCMPAGESIZE (MCMPAGECNT * sizeof(mcmodef)) /* * When allocating memory, and we find a free block satisfying the * request, we will split the free block if doing so would result in * enough space in the second block. MCMSPLIT specifies the minimum * size left over that will allow a split to occur. */ #define MCMSPLIT 64 /* get an object cache entyr given a GLOBAL object number */ #define mcmgobje(ctx,num) (&((ctx)->mcmcxtab[(num)>>8][(num)&255])) /* get an object cache entry given a CLIENT object number */ /* mcmodef *mcmobje(mcmcxdef *ctx, mcmon objnum) */ #define mcmobje(ctx,num) mcmgobje((ctx)->mcmcxgl,mcmc2g(ctx,num)) /* allocate a block that will be locked for its entire lifetime */ void *mcmptralo(mcmcxdef *ctx, ushort siz); /* free a block allocated with mcmptralo */ void mcmptrfre(mcmcxdef *ctx, void *block); /* change an object's swap handle */ void mcmcswf(mcmcx1def *ctx, mcmon objn, mcsseg swapn, mcsseg oldswapn); #ifdef __cplusplus } #endif #endif /* MCM_INCLUDED */ qtads-2.1.7/tads2/mcs.c000066400000000000000000000247101265017072300146070ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/MCS.C,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $"; #endif /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name mcs.c - memory cache swapping manager Function Implements swapping functions for cache manager Notes None Modified 08/04/91 MJRoberts - creation */ #include #include #include "os.h" #include "std.h" #include "err.h" #include "mcs.h" #include "mch.h" #include "mcm.h" /* initialize swapper: allocate memory for swap page table */ void mcsini(mcscxdef *ctx, mcmcx1def *gmemctx, ulong maxsiz, osfildef *fp, char *swapfilename, errcxdef *errctx) { uchar *p; ctx->mcscxtab = (mcsdsdef **)0; /* anticipate failure */ /* allocate space from the low-level heap for page table and one page */ p = mchalo(errctx, ((MCSPAGETAB * sizeof(mcsdsdef *)) + (MCSPAGECNT * sizeof(mcsdsdef))), "mcsini"); /* set up the context with pointers to this chunk */ ctx->mcscxtab = (mcsdsdef **)p; memset(p, 0, (size_t)(MCSPAGETAB * sizeof(mcsdsdef *))); p += MCSPAGETAB * sizeof(mcsdsdef *); ctx->mcscxtab[0] = (mcsdsdef *)p; /* set up the rest of the context */ ctx->mcscxtop = (ulong)0; ctx->mcscxmax = maxsiz; ctx->mcscxmsg = 0; ctx->mcscxfp = fp; ctx->mcscxerr = errctx; ctx->mcscxmem = gmemctx; /* * store the swap filename - make a copy so that the caller doesn't * have to retain the original copy (in case it's on the stack) */ if (swapfilename != 0) { ctx->mcscxfname = (char *)mchalo(errctx, (strlen(swapfilename)+1), "mcsini"); strcpy(ctx->mcscxfname, swapfilename); } else ctx->mcscxfname = 0; } /* close the swapper */ void mcsclose(mcscxdef *ctx) { if (ctx->mcscxtab) mchfre(ctx->mcscxtab); } /* * Attempt to compact the swap file when it grows too big. The segment * descriptors are always allocated in increasing seek location within * the swap file. To compress the file, make each descriptor's * allocated size equal its used size for each in-use segment, and leave * free segments at their allocated sizes. */ static void mcscompact(mcscxdef *ctx) { char buf[512]; ulong max; mcsseg cur_in; mcsseg cur_out; mcsdsdef *desc_in; mcsdsdef *desc_out; uint siz; uint rdsiz; ulong ptr_in; ulong ptr_out; max = 0; /* start at offset zero within file */ for (cur_in = cur_out = 0 ; cur_in < ctx->mcscxmsg ; ++cur_in) { desc_in = mcsdsc(ctx, cur_in); /* * If the present descriptor's address is wrong, and the swap * segment is in use, move the swap segment. If it's not in * use, we don't need to move it, because we're going to throw * away the segment entirely. */ if (desc_in->mcsdsptr != max && (desc_in->mcsdsflg & MCSDSFINUSE)) { /* ptr_in is the old location, ptr_out is the new location */ ptr_in = desc_in->mcsdsptr; ptr_out = max; /* copy through our buffer */ for (siz = desc_in->mcsdsosz ; siz ; siz -= rdsiz) { /* size is whole buffer, or last piece if smaller */ rdsiz = (siz > sizeof(buf) ? sizeof(buf) : siz); /* seek to old location and get the piece */ osfseek(ctx->mcscxfp, ptr_in, OSFSK_SET); (void)osfrb(ctx->mcscxfp, buf, (size_t)rdsiz); /* seek to new location and write the piece */ osfseek(ctx->mcscxfp, ptr_out, OSFSK_SET); (void)osfwb(ctx->mcscxfp, buf, (size_t)rdsiz); /* adjust the pointers by the size copied */ ptr_in += rdsiz; ptr_out += rdsiz; } } /* adjust object descriptor to reflect new location */ desc_in->mcsdsptr = max; /* * Make current object's size exact if it's in use. If it's * not in use, delete the segment altogether. */ if (desc_in->mcsdsflg & MCSDSFINUSE) { desc_in->mcsdssiz = desc_in->mcsdsosz; max += desc_in->mcsdssiz; /* copy descriptor to correct position to close any holes */ if (cur_out != cur_in) { desc_out = mcsdsc(ctx, cur_out); OSCPYSTRUCT(*desc_out, *desc_in); /* we need to renumber the corresponding object as well */ mcmcsw(ctx->mcscxmem, (mcmon)desc_in->mcsdsobj, cur_out, cur_in); } /* we actually wrote this one, so move output pointer */ ++cur_out; } else { /* * We need to renumber the corresponding object so that it * knows there is no swap segment for it any more. */ mcmcsw(ctx->mcscxmem, (mcmon)desc_in->mcsdsobj, MCSSEGINV, cur_in); } } /* * Adjust the top of the file for our new size, and add the savings * into the available space counter. Also, adjust the total handle * count to reflect any descriptors that we've deleted. */ ctx->mcscxmax += (ctx->mcscxtop - max); ctx->mcscxtop = max; ctx->mcscxmsg = cur_out; } /* swap an object out to the swap file */ mcsseg mcsout(mcscxdef *ctx, uint objid, uchar *ptr, ushort siz, mcsseg oldseg, int dirty) { mcsdsdef *desc; mcsdsdef **pagep; uint i; uint j; mcsseg min; mcsseg cur; ushort minsiz; IF_DEBUG(printf("<< mcsout: objid=%d, ptr=%lx, siz=%u, oldseg=%u >>\n", objid, (unsigned long)ptr, siz, oldseg)); /* see if old segment can be reused */ if (oldseg != MCSSEGINV) { desc = mcsdsc(ctx, oldseg); if (!(desc->mcsdsflg & MCSDSFINUSE) /* if old seg is not in use */ && desc->mcsdsobj == objid /* and it has same object */ && desc->mcsdssiz >= siz /* and it's still big enough */ && !dirty) /* and the object in memory hasn't been changed */ { /* we can reuse the old segment without rewriting it */ desc->mcsdsflg |= MCSDSFINUSE; /* mark segment as in use */ return(oldseg); } } /* look for the smallest unused segment big enough for this object */ for (cur = 0, min = MCSSEGINV, i = 0, pagep = ctx->mcscxtab ; cur < ctx->mcscxmsg && i < MCSPAGETAB && *pagep ; ++pagep, ++i) { for (j = 0, desc = *pagep ; cur < ctx->mcscxmsg && j < MCSPAGECNT ; ++desc, ++j, ++cur) { if (!(desc->mcsdsflg & MCSDSFINUSE) && desc->mcsdssiz >= siz && (min == MCSSEGINV || desc->mcsdssiz < minsiz)) { min = cur; minsiz = desc->mcsdssiz; if (minsiz == siz) break; /* exact match - we're done */ } } /* quit if we found an exact match */ if (min != MCSSEGINV && minsiz == siz) break; } /* if we found nothing, allocate a new segment if possible */ if (min == MCSSEGINV) { if (siz > ctx->mcscxmax) { /* swap file is too big; compact it and try again */ mcscompact(ctx); if (siz > ctx->mcscxmax) errsig(ctx->mcscxerr, ERR_SWAPBIG); } min = ctx->mcscxmsg; if ((min >> 8) >= MCSPAGETAB) /* exceeded pages in page table? */ errsig(ctx->mcscxerr, ERR_SWAPPG); if (!ctx->mcscxtab[min >> 8]) /* haven't allocate page yet? */ { ctx->mcscxtab[min >> 8] = (mcsdsdef *)mchalo(ctx->mcscxerr, (MCSPAGECNT * sizeof(mcsdsdef)), "mcsout"); } /* set up new descriptor */ desc = mcsdsc(ctx, min); desc->mcsdsptr = ctx->mcscxtop; desc->mcsdssiz = siz; desc->mcsdsobj = objid; /* write out the segment */ mcswrt(ctx, desc, ptr, siz); desc->mcsdsflg = MCSDSFINUSE; /* update context information to account for new segment */ ctx->mcscxtop += siz; /* add to top seek offset in file */ ctx->mcscxmax -= siz; /* take size out of quota */ ctx->mcscxmsg++; /* increment last segment allocated */ return(min); } else { desc = mcsdsc(ctx, min); desc->mcsdsobj = objid; mcswrt(ctx, desc, ptr, siz); desc->mcsdsflg |= MCSDSFINUSE; return(min); } } void mcsin(mcscxdef *ctx, mcsseg seg, uchar *ptr, ushort siz) { mcsdsdef *desc = mcsdsc(ctx, seg); IF_DEBUG(printf("<< mcsin: seg=%u, ptr=%lx, siz=%d, objid=%u >>\n", seg, (unsigned long)ptr, siz, desc->mcsdsobj)); assert(seg < ctx->mcscxmsg); /* can only swap in as much as we wrote */ if (desc->mcsdsosz < siz) siz = desc->mcsdsosz; /* seek to and read the segment */ if (osfseek(ctx->mcscxfp, desc->mcsdsptr, OSFSK_SET)) errsig(ctx->mcscxerr, ERR_FSEEK); if (osfrb(ctx->mcscxfp, ptr, (size_t)siz)) errsig(ctx->mcscxerr, ERR_FREAD); desc->mcsdsflg &= ~MCSDSFINUSE; /* segment no longer in use */ } void mcswrt(mcscxdef *ctx, mcsdsdef *desc, uchar *buf, ushort bufl) { int tries; desc->mcsdsosz = bufl; for (tries = 0 ; tries < 2 ; ++tries) { /* attempt to write the object to the swap file */ if (osfseek(ctx->mcscxfp, desc->mcsdsptr, OSFSK_SET)) errsig(ctx->mcscxerr, ERR_FSEEK); if (!osfwb(ctx->mcscxfp, buf, (size_t)bufl)) return; /* couldn't write it; compact the swap file */ mcscompact(ctx); } /* couldn't write to swap file, even after compacting it */ errsig(ctx->mcscxerr, ERR_FWRITE); } qtads-2.1.7/tads2/mcs.h000066400000000000000000000076661265017072300146270ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/MCS.H,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name mcs.h - memory cache swap manager Function Definitions for memory cache swap manager Notes The cache swap manager provides swap file services to the memory cache manager. The cache manager calls the swap manager to write objects to the swap file and read in previously swapped-out objects. Modified 08/03/91 MJRoberts - creation */ #ifndef MCS_INCLUDED #define MCS_INCLUDED #ifndef STD_INCLUDED # include "std.h" #endif #ifndef ERR_INCLUDED # include "err.h" #endif /* swap segment descriptor */ typedef struct mcsdsdef mcsdsdef; struct mcsdsdef { ulong mcsdsptr; /* seek pointer in swap file */ ushort mcsdssiz; /* size of this swap segment */ ushort mcsdsosz; /* size of object written to segment */ uint mcsdsobj; /* client object ID */ ushort mcsdsflg; /* flags */ # define MCSDSFINUSE 0x01 /* segment is in use */ }; /* * mcsseg - swap segment handle. All swap-file segments are addressed * through this handle type. */ typedef ushort mcsseg; /* swap manager context */ typedef struct mcscxdef mcscxdef; struct mcscxdef { osfildef *mcscxfp; /* swap file handle */ char *mcscxfname; /* name of swap file */ errcxdef *mcscxerr; /* error handling context */ ulong mcscxtop; /* top of swap file allocated so far */ ulong mcscxmax; /* maximum size of swap file we're allowed */ mcsdsdef **mcscxtab; /* swap descriptor page table */ mcsseg mcscxmsg; /* maximum segment allocated so far */ struct mcmcx1def *mcscxmem; /* memory manager context */ }; #define MCSSEGINV ((mcsseg)~0) /* invalid segment ID - error indicator */ /* initialize swapper - returns 0 for success, other for error */ void mcsini(struct mcscxdef *ctx, struct mcmcx1def *gmemctx, ulong maxsiz, osfildef *fp, char *swapfilename, struct errcxdef *errctx); /* close swapper (release memory areas) */ void mcsclose(struct mcscxdef *ctx); /* * Swap an object out. The caller specifies the location and size of * the object, as well as a unique handle (arbitrary, up to the caller; * the only requirement is that it be unique among all caller objects * and always the same for a particular caller's object) and the * previous swap handle if the object ever had one. If the object is * not dirty (it hasn't been written since being swapped in), and the * swap manager hasn't reused the swap slot, the swap manager doesn't * need to write the memory, since it already has a copy on disk; * instead, it can just mark the slot as back in use. If the caller * doesn't wish to take advantage of this optimization, always pass in * dirty == TRUE, which will force a write regardless of the object ID. */ mcsseg mcsout(struct mcscxdef *ctx, uint objid, uchar *objptr, ushort objsize, mcsseg oldswapseg, int dirty); /* Swap an object in */ void mcsin(struct mcscxdef *ctx, mcsseg swapseg, uchar *objptr, ushort size); /* number of page pointers in page table (max number of pages) */ #define MCSPAGETAB 256 /* number of swap descriptors in a page */ #define MCSPAGECNT 256 /* find swap descriptor corresponding to swap segment number */ #define mcsdsc(ctx,seg) (&(ctx)->mcscxtab[(seg)>>8][(seg)&255]) /* write out a swap segment */ void mcswrt(mcscxdef *ctx, mcsdsdef *desc, uchar *buf, ushort bufl); #endif /* MCS_INCLUDED */ qtads-2.1.7/tads2/msdos/000077500000000000000000000000001265017072300150025ustar00rootroot00000000000000qtads-2.1.7/tads2/msdos/ostzw32.c000066400000000000000000000154631265017072300165120ustar00rootroot00000000000000/* * Copyright (c) 1987, 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name ostzw32.c - osifc timezone routines for Win32 Function Notes Modified 08/05/12 MJRoberts - Creation */ #include #include #include #include #include #include #include #include "std.h" #include "os.h" extern void safe_strcpy(char *dst, size_t dstlen, const char *src); /* list of mappings from Windows zone names to Olson zone names */ typedef struct win_zone_map_t win_zone_map_t; struct win_zone_map_t { const WCHAR *win_name; /* Windows zone name ("Pacific Standard Time") */ const char *tz_name; /* zoneinfo (Olson) name ("America/Los_Angeles") */ const char *st_abbr; /* standard time abbreviation ("PST") */ const char *dst_abbr; /* daylight time abbreviation ("PDT") */ }; static const win_zone_map_t S_win_zone_map[] = { /* * include the mapping file we generate from the Unicode CLDR file * windowsZones.xml - this contains a list of { "win name", "olson name" } * constant data definitions that will populate this array for us */ #include "windowsZones.h" }; /* find a Windows zone name in the zone map list */ static const win_zone_map_t *win_to_zoneinfo(const wchar_t *win_name) { /* search the list for the given name */ const win_zone_map_t *z; for (z = S_win_zone_map ; z->win_name != 0 ; ++z) { if (wcscmp(z->win_name, win_name) == 0) return z; } /* didn't find it */ return 0; } /* * Generate a timezone abbreviation from a windows zone name. This is an * approximation at best; we use this as a last resort when we can't find * the zone in our zone list. We'll simply pull out the initials of the * words in the zone name, unless it already looks like an abbreviation, in * which case we'll just return it as-is. */ static void gen_tz_abbr(char *abbr, size_t abbrlen, const wchar_t *name) { /* if the name doesn't have any spaces, assume it's already abbreviated */ if (wcschr(name, ' ') == 0) { /* no spaces, so assume it's already abbreviation and just copy it */ for ( ; abbrlen > 1 && *name != 0 ; --abbrlen) *abbr++ = (char)*name++; } else { /* pull out the first letter of each word */ int sp = TRUE; for ( ; abbrlen > 1 && *name != 0 ; ++name) { if (*name == ' ') sp = TRUE; else if (sp) { *abbr++ = (char)*name; sp = FALSE; } } } if (abbrlen > 0) *abbr = '\0'; } /* * Get the zoneinfo (Olson) timezone name for the local machine/process. */ int os_get_zoneinfo_key(char *name, size_t namelen) { TIME_ZONE_INFORMATION tz; const win_zone_map_t *z; /* get the Windows time zone information */ if (GetTimeZoneInformation(&tz) != TIME_ZONE_ID_INVALID && (z = win_to_zoneinfo(tz.StandardName)) != 0) { /* success - return the zoneinfo name mapping */ safe_strcpy(name, namelen, z->tz_name); return TRUE; } /* there's no time zone information from Windows; return failure */ return FALSE; } /* * Get the current settings for the local time zone */ int os_get_timezone_info(struct os_tzinfo_t *info) { TIME_ZONE_INFORMATION tz; int mode; /* initialize all fields to zero */ memset(info, 0, sizeof(*info)); /* get the Windows time zone information */ if ((mode = GetTimeZoneInformation(&tz)) != TIME_ZONE_ID_INVALID) { const win_zone_map_t *z; /* check to see if standard/daylight change rules are present */ if (mode != TIME_ZONE_ID_UNKNOWN && tz.StandardDate.wMonth != 0) { /* * We have both daylight and standard time. Set the GMT offset * for each; Windows gives us to this as minutes west of GMT, * and we want seconds east, so negate it and multiply by 60 * seconds per minutes. */ info->std_ofs = -(tz.Bias + tz.StandardBias) * 60; info->dst_ofs = -(tz.Bias + tz.DaylightBias) * 60; /* check whether we're on standard or daylight right now */ info->is_dst = (mode == TIME_ZONE_ID_DAYLIGHT); } else { /* there's no daylight rule, so we're always on standard time */ info->std_ofs = info->dst_ofs = -tz.Bias * 60; info->is_dst = FALSE; } /* * If we have recurring standard/daylight rules, set the rules. * Recurring rules are indicated by wYear == 0; a non-zero wYear * means that the rule occurs on that exact date only. We have no * way to represent once-only rules, so we ignore them and indicate * that no rules are available. */ if (mode != TIME_ZONE_ID_UNKNOWN && tz.StandardDate.wMonth != 0 && tz.StandardDate.wYear == 0 && tz.DaylightDate.wYear == 0) { /* set 'dst_start' to the DaylightDate rule */ info->dst_start.month = tz.DaylightDate.wMonth; info->dst_start.week = tz.DaylightDate.wDay; info->dst_start.day = tz.DaylightDate.wDayOfWeek + 1; info->dst_start.time = tz.DaylightDate.wHour*60*60 + tz.DaylightDate.wMinute*60; /* set 'dst_end' to the StandardTime rule */ info->dst_end.month = tz.StandardDate.wMonth; info->dst_end.week = tz.StandardDate.wDay; info->dst_end.day = tz.StandardDate.wDayOfWeek + 1; info->dst_end.time = tz.StandardDate.wHour*60*60 + tz.StandardDate.wMinute*60; } /* look up the windows zone in our list to get the abbreviations */ if ((z = win_to_zoneinfo(tz.StandardName)) != 0) { /* found it - return the mapped abbreviations */ safe_strcpy(info->std_abbr, sizeof(info->std_abbr), z->st_abbr); safe_strcpy(info->dst_abbr, sizeof(info->dst_abbr), z->dst_abbr); } else { /* * we couldn't find a mapping; generate the abbreviation from * the windows zone name by pulling out the initials (for * example, "Pacific Standard Time" -> "PST") */ gen_tz_abbr(info->std_abbr, sizeof(info->std_abbr), tz.StandardName); gen_tz_abbr(info->dst_abbr, sizeof(info->dst_abbr), tz.DaylightName); } /* success */ return TRUE; } else { /* failed */ return FALSE; } } qtads-2.1.7/tads2/msdos/windowsZones.h000066400000000000000000000155431265017072300176740ustar00rootroot00000000000000/* * GENERATED FILE - DO NOT EDIT * * This file was generated by the TADS 3 zoneinfo * database process (tads3/tz/tz.t) from the Unicode * CLDR source file windowsZones.xml. To regenerate, * run tz.t with the latest windowsZones.xml. * * See http://cldr.unicode.org for CLDR updates. * * (This file is designed to be #included within an * appropriate C struct initializer.) */ { L"AUS Central Standard Time", "Australia/Darwin", "CST", "CST" }, { L"AUS Eastern Standard Time", "Australia/Sydney", "EST", "EST" }, { L"Afghanistan Standard Time", "Asia/Kabul", "AFT", "" }, { L"Alaskan Standard Time", "America/Anchorage", "AKST", "AKDT" }, { L"Arab Standard Time", "Asia/Riyadh", "AST", "" }, { L"Arabian Standard Time", "Asia/Dubai", "GST", "" }, { L"Arabic Standard Time", "Asia/Baghdad", "AST", "ADT" }, { L"Argentina Standard Time", "America/Argentina/Buenos_Aires", "ART", "ARST" }, { L"Atlantic Standard Time", "America/Halifax", "AST", "ADT" }, { L"Azerbaijan Standard Time", "Asia/Baku", "AZT", "AZST" }, { L"Azores Standard Time", "Atlantic/Azores", "AZOT", "AZOST" }, { L"Bangladesh Standard Time", "Asia/Dhaka", "BDT", "BDST" }, { L"Canada Central Standard Time", "America/Regina", "CST", "MDT" }, { L"Cape Verde Standard Time", "Atlantic/Cape_Verde", "CVT", "CVST" }, { L"Caucasus Standard Time", "Asia/Yerevan", "AMT", "AMST" }, { L"Cen. Australia Standard Time", "Australia/Adelaide", "CST", "CST" }, { L"Central America Standard Time", "America/Guatemala", "CST", "CDT" }, { L"Central Asia Standard Time", "Asia/Almaty", "ALMT", "ALMST" }, { L"Central Brazilian Standard Time", "America/Cuiaba", "AMT", "AMST" }, { L"Central Europe Standard Time", "Europe/Budapest", "CET", "CEST" }, { L"Central European Standard Time", "Europe/Warsaw", "CET", "CEST" }, { L"Central Pacific Standard Time", "Pacific/Guadalcanal", "SBT", "" }, { L"Central Standard Time", "America/Chicago", "CST", "CDT" }, { L"China Standard Time", "Asia/Shanghai", "CST", "CDT" }, { L"Dateline Standard Time", "Etc/GMT+12", "GMT+12", "" }, { L"E. Africa Standard Time", "Africa/Nairobi", "EAT", "" }, { L"E. Australia Standard Time", "Australia/Brisbane", "EST", "EST" }, { L"E. Europe Standard Time", "Europe/Minsk", "FET", "EEST" }, { L"E. South America Standard Time", "America/Sao_Paulo", "BRT", "BRST" }, { L"Eastern Standard Time", "America/New_York", "EST", "EDT" }, { L"Egypt Standard Time", "Africa/Cairo", "EET", "EEST" }, { L"Ekaterinburg Standard Time", "Asia/Yekaterinburg", "YEKT", "YEKST" }, { L"FLE Standard Time", "Europe/Kiev", "EET", "EEST" }, { L"Fiji Standard Time", "Pacific/Fiji", "FJT", "FJST" }, { L"GMT Standard Time", "Europe/London", "GMT", "BST" }, { L"GTB Standard Time", "Europe/Istanbul", "EET", "EEST" }, { L"Georgian Standard Time", "Asia/Tbilisi", "GET", "GEST" }, { L"Greenland Standard Time", "America/Godthab", "WGT", "WGST" }, { L"Greenwich Standard Time", "Atlantic/Reykjavik", "GMT", "ISST" }, { L"Hawaiian Standard Time", "Pacific/Honolulu", "HST", "HDT" }, { L"India Standard Time", "Asia/Kolkata", "IST", "IST" }, { L"Iran Standard Time", "Asia/Tehran", "IRST", "IRDT" }, { L"Israel Standard Time", "Asia/Jerusalem", "IST", "IDT" }, { L"Jordan Standard Time", "Asia/Amman", "EET", "EEST" }, { L"Kamchatka Standard Time", "Asia/Kamchatka", "PETT", "PETST" }, { L"Korea Standard Time", "Asia/Seoul", "KST", "KDT" }, { L"Magadan Standard Time", "Asia/Magadan", "MAGT", "MAGST" }, { L"Mauritius Standard Time", "Indian/Mauritius", "MUT", "MUST" }, { L"Mexico Standard Time", "America/Mexico_City", "CST", "CDT" }, { L"Middle East Standard Time", "Asia/Beirut", "EET", "EEST" }, { L"Montevideo Standard Time", "America/Montevideo", "UYT", "UYST" }, { L"Morocco Standard Time", "Africa/Casablanca", "WET", "WEST" }, { L"Mountain Standard Time", "America/Denver", "MST", "MDT" }, { L"Mountain Standard Time (Mexico)", "America/Chihuahua", "MST", "MDT" }, { L"Myanmar Standard Time", "Asia/Rangoon", "MMT", "" }, { L"N. Central Asia Standard Time", "Asia/Novosibirsk", "NOVT", "NOVST" }, { L"Namibia Standard Time", "Africa/Windhoek", "WAT", "WAST" }, { L"Nepal Standard Time", "Asia/Kathmandu", "NPT", "" }, { L"New Zealand Standard Time", "Pacific/Auckland", "NZST", "NZDT" }, { L"Newfoundland Standard Time", "America/St_Johns", "NST", "NDT" }, { L"North Asia East Standard Time", "Asia/Irkutsk", "IRKT", "IRKST" }, { L"North Asia Standard Time", "Asia/Krasnoyarsk", "KRAT", "KRAST" }, { L"Pacific SA Standard Time", "America/Santiago", "CLT", "CLST" }, { L"Pacific Standard Time", "America/Los_Angeles", "PST", "PDT" }, { L"Pacific Standard Time (Mexico)", "America/Santa_Isabel", "PST", "PDT" }, { L"Pakistan Standard Time", "Asia/Karachi", "PKT", "PKST" }, { L"Paraguay Standard Time", "America/Asuncion", "PYT", "PYST" }, { L"Romance Standard Time", "Europe/Paris", "CET", "CEST" }, { L"Russian Standard Time", "Europe/Moscow", "MSK", "MSD" }, { L"SA Eastern Standard Time", "America/Cayenne", "GFT", "" }, { L"SA Pacific Standard Time", "America/Bogota", "COT", "COST" }, { L"SA Western Standard Time", "America/La_Paz", "BOT", "BOST" }, { L"SE Asia Standard Time", "Asia/Bangkok", "ICT", "" }, { L"Samoa Standard Time", "Pacific/Apia", "WST", "WSDT" }, { L"Singapore Standard Time", "Asia/Singapore", "SGT", "MALST" }, { L"South Africa Standard Time", "Africa/Johannesburg", "SAST", "SAST" }, { L"Sri Lanka Standard Time", "Asia/Colombo", "IST", "IST" }, { L"Syria Standard Time", "Asia/Damascus", "EET", "EEST" }, { L"Taipei Standard Time", "Asia/Taipei", "CST", "CDT" }, { L"Tasmania Standard Time", "Australia/Hobart", "EST", "EST" }, { L"Tokyo Standard Time", "Asia/Tokyo", "JST", "JDT" }, { L"Tonga Standard Time", "Pacific/Tongatapu", "TOT", "TOST" }, { L"US Eastern Standard Time", "America/Indiana/Indianapolis", "EST", "EDT" }, { L"US Mountain Standard Time", "America/Phoenix", "MST", "MDT" }, { L"UTC", "Etc/GMT", "GMT", "" }, { L"UTC+12", "Etc/GMT-12", "GMT-12", "" }, { L"UTC-02", "Etc/GMT+2", "GMT+2", "" }, { L"UTC-11", "Etc/GMT+11", "GMT+11", "" }, { L"Ulaanbaatar Standard Time", "Asia/Ulaanbaatar", "ULAT", "ULAST" }, { L"Venezuela Standard Time", "America/Caracas", "VET", "" }, { L"Vladivostok Standard Time", "Asia/Vladivostok", "VLAT", "VLAST" }, { L"W. Australia Standard Time", "Australia/Perth", "WST", "WST" }, { L"W. Central Africa Standard Time", "Africa/Lagos", "WAT", "" }, { L"W. Europe Standard Time", "Europe/Berlin", "CET", "CEST" }, { L"West Asia Standard Time", "Asia/Tashkent", "UZT", "UZST" }, { L"West Pacific Standard Time", "Pacific/Port_Moresby", "PGT", "" }, { L"Yakutsk Standard Time", "Asia/Yakutsk", "YAKT", "YAKST" } qtads-2.1.7/tads2/obj.c000066400000000000000000001055551265017072300146060ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/OBJ.C,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $"; #endif /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name obj.c - object manipulation Function Operations on TADS objects Notes A TADS object is stored in a single cache object. The object is laid out in memory as follows: { ushort workspace - workspace in which object is defined ushort flags - object flags ushort sc_count - number of superclasses ushort prop_used - number of entries actually used ushort free_offset - offset of next free byte in prop area objnum sclist[sc_count] - array of superclass object numbers prpdef proplist[prop_max] - array of properties of the object } The number of superclasses is defined when the object is compiled, and does not change at runtime. Properties can be added and replaced at runtime; the object's cache object may need to be resized from time to time to accomdate added or expanded property values. Objects are stored in a portable format. Each ushort (including the objnum) in the object header is stored as a 2-byte array, least significant byte first; the array is stored with no padding for alignment. Each property is defined by a prpdef structure (see prp.h). The meaning of propval varies by datatype. For DAT_NUMBER, the value is the actual number in question. For DAT_OBJECT, it is the object number of the object. For DAT_NIL and DAT_TRUE, there is no value. For DAT_SSTRING, DAT_CODE, DAT_LIST, and DAT_DSTRING, the value is stored immediately following the prpdef, and takes up the number of bytes given in the prpsize field. Modified 08/11/91 MJRoberts - creation */ #include "os.h" #include "obj.h" #include "prp.h" #include "dat.h" #include "mch.h" #include "mcm.h" #include "err.h" /* * Get a property WITHOUT INHERITANCE. The offset of the property's * prpdef is returned. An offset of zero means the property wasn't * found. */ uint objgetp(mcmcxdef *mctx, objnum objn, prpnum prop, dattyp *typptr) { objdef *objptr; prpdef *p; int cnt; uint retval; /* property offset, if we find it */ uint ignprop; /* ignored property - use if real property isn't found */ uchar pbuf[2]; /* property number in portable format */ uchar *indp; uchar *indbase; int last; int first; int cur; oswp2(pbuf, prop); /* get property number in portable foramt */ objptr = (objdef *)mcmlck(mctx, objn); /* get a lock on the object */ ignprop = 0; /* assume we won't find ignored property */ cnt = objnprop(objptr); /* get number of properties defined */ retval = 0; /* presume failure */ if (objflg(objptr) & OBJFINDEX) { /* there's an index -> do a binary search through the index */ indbase = (uchar *)objpfre(objptr); /* find index */ first = 0; last = cnt - 1; for (;;) { if (first > last) break; /* crossed over -> not found */ cur = first + (last - first)/2; /* split the difference */ indp = indbase + cur*4; /* get pointer to this entry */ if (indp[0] == pbuf[0] && indp[1] == pbuf[1]) { retval = osrp2(indp + 2); break; } else if (indp[0] < pbuf[0] || (indp[0] == pbuf[0] && indp[1] < pbuf[1])) first = (cur == first ? first + 1 : cur); else last = (cur == last ? last - 1 : cur); } /* ignore ignored and deleted properties if possible */ while (retval && ((prpflg(objptr + retval) & PRPFIGN) != 0 || ((prpflg(objptr + retval) & PRPFDEL) != 0 && (mctx->mcmcxflg & MCMCXF_NO_PRP_DEL) == 0)) && cur < cnt && indp[0] == indp[4] && indp[1] == indp[5]) { indp += 4; retval = osrp2(indp + 2); } if (retval && osrp2(objptr + retval) != prop) assert(FALSE); } else { /* there's no index -> do sequential search through properties */ for (p = objprp(objptr) ; cnt ; p = objpnxt(p), --cnt) { /* if this is the property, and it's not being ignored, use it */ if (*(uchar *)p == pbuf[0] && *(((uchar *)p) + 1) == pbuf[1]) { if (prpflg(p) & PRPFIGN) /* this is ignored */ ignprop = objpofs(objptr, p); /* ... make a note of it */ else if ((prpflg(p) & PRPFDEL) != 0 /* it's deleted */ && (mctx->mcmcxflg & MCMCXF_NO_PRP_DEL) == 0) /* simply skip it */ ; else { retval = objpofs(objptr, p); /* this is the one */ break; /* we're done */ } } } } if (!retval) retval = ignprop; /* use ignored value if nothing else */ if (retval && typptr) *typptr = prptype(objofsp(objptr, retval)); mcmunlck(mctx, objn); /* done with object, so unlock it */ return(retval); } /* get the offset of the end of a property in an object */ uint objgetp_end(mcmcxdef *ctx, objnum objn, prpnum prop) { objdef *objp; prpdef *propptr; uint ofs; uint valsiz; /* get the start of the object */ ofs = objgetp(ctx, objn, prop, 0); if (ofs == 0) return 0; /* get the object */ objp = mcmlck(ctx, (mcmon)objn); /* get the property */ propptr = objofsp(objp, ofs); /* get the data size */ valsiz = prpsize(propptr); /* done with the object */ mcmunlck(ctx, (mcmon)objn); /* * return the ending offset - it's the starting offset plus the * property header size plus the size of the property data */ return ofs + PRPHDRSIZ + valsiz; } /* determine whether an object is a descendant of another object */ static int objisd(mcmcxdef *ctx, objdef *objptr, objnum parentnum) { uchar *sc; int cnt; for (sc = objsc(objptr), cnt = objnsc(objptr) ; cnt ; sc += 2, --cnt) { int cursc = osrp2(sc); int ret; objdef *curptr; if (cursc == parentnum) return(TRUE); curptr = (objdef *)mcmlck(ctx, (mcmon)cursc); ret = objisd(ctx, curptr, parentnum); mcmunlck(ctx, (mcmon)cursc); if (ret) return(TRUE); } return(FALSE); } /* * Get a property of an object, either from the object or from a * superclass (inherited). If the inh flag is TRUE, we do not look at * all in the object itself, but restrict our search to inherited * properties only. We return the byte offset of the prpdef within the * object in which the prpdef is found; the superclass object itself is * NOT locked upon return, but we will NOT unlock the object passed in * (in other words, all object locking status is the same as it was on * entry). If the offset is zero, the property was not found. * * This is an internal helper routine - it's not meant to be called * except by objgetap(). */ static uint objgetap0(mcmcxdef *ctx, noreg objnum obj, prpnum prop, objnum *orn, int inh, dattyp *ortyp) { uchar *sc; ushort sccnt; ushort psav; dattyp typsav = DAT_NIL; objnum osavn = MCMONINV; uchar *o1; objnum o1n; ushort poff; int found; uint retval; dattyp typ; uchar sclist[100]; /* up to 50 superclasses */ objdef *objptr; NOREG((&obj)) /* see if the property is in the current object first */ if (!inh && (retval = objgetp(ctx, obj, prop, &typ)) != 0) { /* * tell the caller which object this came from, if the caller * wants to know */ if (orn != 0) *orn = obj; /* if the caller wants to know the type, return it */ if (ortyp != 0) *ortyp = typ; /* return the property offset */ return retval; } /* lock the object, cache its superclass list, and unlock it */ objptr = (objdef *)mcmlck(ctx, (mcmon)obj); sccnt = objnsc(objptr); memcpy(sclist, objsc(objptr), (size_t)(sccnt << 1)); sc = sclist; mcmunlck(ctx, (mcmon)obj); /* try to inherit the property */ for (found = FALSE ; sccnt != 0 ; sc += 2, --sccnt) { /* recursively look up the property in this superclass */ poff = objgetap0(ctx, (objnum)osrp2(sc), prop, &o1n, FALSE, &typ); /* if we found the property, remember it */ if (poff != 0) { int isdesc; /* if we have a previous object, determine lineage */ if (found) { o1 = mcmlck(ctx, o1n); isdesc = objisd(ctx, o1, osavn); mcmunlck(ctx, o1n); } /* * if we don't already have a property, or the new object * is a descendant of the previously found object (meaning * that the new object's property should override the * previously found object's property), use this new * property */ if (!found || isdesc) { psav = poff; osavn = o1n; typsav = typ; found = TRUE; } } } /* set return pointer and return the offset of what we found */ if (orn != 0) *orn = osavn; /* return the object type if the caller wanted it */ if (ortyp != 0) *ortyp = typsav; /* return the offset of the property if we found one, or zero if not */ return (found ? psav : 0); } /* * Get a property of an object, either from the object or from a * superclass (inherited). If the inh flag is TRUE, we do not look at * all in the object itself, but restrict our search to inherited * properties only. We return the byte offset of the prpdef within the * object in which the prpdef is found; the superclass object itself is * NOT locked upon return, but we will NOT unlock the object passed in * (in other words, all object locking status is the same as it was on * entry). If the offset is zero, the property was not found. */ uint objgetap(mcmcxdef *ctx, noreg objnum obj, prpnum prop, objnum *ornp, int inh) { uint retval; dattyp typ; objnum orn; /* * even if the caller doesn't care about the original object number, * we do, so provide our own location to store it if necessary */ if (ornp == 0) ornp = &orn; /* keep going until we've finished translating synonyms */ for (;;) { /* look up the property */ retval = objgetap0(ctx, obj, prop, ornp, inh, &typ); /* * If we found something (i.e., retval != 0), check to see if we * have a synonym; if so, synonym translation is required */ if (retval != 0 && typ == DAT_SYN) { prpnum prvprop; objdef *objptr; prpdef *p; /* * Translation is required - get new property and try again. * First, remember the original property, so we can make * sure we're not going to loop (at least, not in this one * synonym definition). */ prvprop = prop; objptr = (objdef *)mcmlck(ctx, (mcmon)*ornp); p = objofsp(objptr, retval); prop = osrp2(prpvalp(p)); mcmunlck(ctx, (mcmon)*ornp); /* check for direct circularity */ if (prop == prvprop) errsig(ctx->mcmcxgl->mcmcxerr, ERR_CIRCSYN); /* go back for another try with the new property */ continue; } /* we don't have to perform a translation; return the result */ return retval; } } /* * Expand an object by a requested size, and return a pointer to the * object's location. The object will be unlocked and relocked by this * call. The new size is written to the *siz argument. */ objdef *objexp(mcmcxdef *ctx, objnum obj, ushort *siz) { ushort oldsiz; uchar *p; oldsiz = mcmobjsiz(ctx, (mcmon)obj); p = mcmrealo(ctx, (mcmon)obj, (ushort)(oldsiz + *siz)); *siz = mcmobjsiz(ctx, (mcmon)obj) - oldsiz; return((objdef *)p); } /* * Delete a property in an object. Note that we never actually remove * anything marked as an original property, but just mark it 'ignore'. * This way, it's easy to restore the entire original state of the * objects, simply by deleting everything not marked original and * clearing the 'ignore' flag on the remaining properties. If * 'mark_only' is true, we'll only mark the property as deleted without * actually reclaiming the space; this is necessary when deleting a * method when other methods may follow, since p-code is not entirely * self-relative and thus can't always be relocated within an object. */ void objdelp(mcmcxdef *mctx, objnum objn, prpnum prop, int mark_only) { objdef *objptr; uint pofs; prpdef *p; prpdef *nxt; size_t movsiz; pofs = objgetp(mctx, objn, prop, (dattyp *)0); /* try to find property */ if (!pofs) return; /* not defined - nothing to delete */ objptr = (objdef *)mcmlck(mctx, objn); /* get lock on object */ p = objofsp(objptr, pofs); /* get actual prpdef pointer */ nxt = objpnxt(p); /* find next prpdef after this one */ /* if this is original, just mark 'ignore' */ if (prpflg(p) & PRPFORG) { prpflg(p) |= PRPFIGN; /* mark this as overridden */ } else if (mark_only) { prpflg(p) |= PRPFDEL; /* mark as deleted without removing space */ } else { /* move prpdef's after current one down over current one */ movsiz = (uchar *)objptr + objfree(objptr) - (uchar *)nxt; memmove(p, nxt, movsiz); objsnp(objptr, objnprop(objptr)-1); objsfree(objptr, objfree(objptr) - (((uchar *)nxt) - ((uchar *)p))); } /* tell cache manager this object has been changed, and unlock it */ mcmtch(mctx, objn); mcmunlck(mctx, objn); } /* * Set a property of an object to a new value, overwriting the original * value (if any); the object must be unlocked coming in. If an undo * context is provided, an undo record is written; if the undo context * pointer is null, no undo information is kept. */ void objsetp(mcmcxdef *ctx, objnum objn, prpnum prop, dattyp typ, void *val, objucxdef *undoctx) { objdef *objptr; prpdef *p; uint pofs; uint siz; ushort newsiz; int indexed; int prop_was_set; /* get a lock on the object */ objptr = (objdef *)mcmlck(ctx, objn); indexed = objflg(objptr) & OBJFINDEX; /* catch any errors so we can unlock the object */ ERRBEGIN(ctx->mcmcxgl->mcmcxerr) { /* get the previous value of the property, if any */ pofs = objgetp(ctx, objn, prop, (dattyp *)0); p = objofsp(objptr, pofs); prop_was_set = (p != 0); /* start the undo record if we are keeping undo information */ if (undoctx && objuok(undoctx)) { uchar *up; uchar cmd; if (p) { if (prpflg(p) & PRPFORG) { cmd = OBJUOVR; /* override original */ p = (prpdef *)0; /* pretend it doesn't even exist */ } else cmd = OBJUCHG; /* change property */ } else cmd = OBJUADD; /* prop didn't exist - adding it */ /* write header, reserve space, and get a pointer to the space */ up = objures(undoctx, cmd, (ushort)(sizeof(mcmon) + sizeof(prpnum) + (p ? PRPHDRSIZ + prpsize(p) : 0))); /* write the object and property numbers */ memcpy(up, &objn, (size_t)sizeof(objn)); up += sizeof(mcmon); memcpy(up, &prop, (size_t)sizeof(prop)); up += sizeof(prop); /* if there's existing data, write it */ if (p) { memcpy(up, p, (size_t)(PRPHDRSIZ + prpsize(p))); up += PRPHDRSIZ + prpsize(p); } /* update the undo context's head offset for the new value */ undoctx->objucxhead = up - undoctx->objucxbuf; } /* get the size of the data */ siz = datsiz(typ, val); /* * If the property is already set, and the new data fits, use the * existing slot. However, do not use existing slot if it's * in the non-mutable portion of the object. */ if (!p || (uint)prpsize(p) < siz || pofs < (uint)objrst(objptr)) { uint avail; /* delete any existing value */ if (prop_was_set) objdelp(ctx, objn, prop, FALSE); /* get the top of the property area */ p = objpfre(objptr); /* make sure there's room at the top */ avail = mcmobjsiz(ctx, (mcmon)objn) - objfree(objptr); if (avail < siz + PRPHDRSIZ) { newsiz = 64 + ((objfree(objptr) + siz + PRPHDRSIZ) - mcmobjsiz(ctx, (mcmon)objn)); objptr = objexp(ctx, objn, &newsiz); p = objpfre(objptr); /* reset pointer if object moved */ /* NOTE! Index (if present) is now invalid! */ } prpsetsize(p, siz); /* set the new property size */ prpsetprop(p, prop); /* ... and property id */ prpflg(p) = 0; /* no property flags yet */ objsnp(objptr, objnprop(objptr) + 1); /* one more prop */ objsfree(objptr, objfree(objptr) + siz + PRPHDRSIZ); } /* copy the new data to top of object's free space */ prptype(p) = typ; if (siz != 0) memcpy(prpvalp(p), val, (size_t)siz); } ERRCLEAN(ctx->mcmcxgl->mcmcxerr) { mcmunlck(ctx, objn); /* unlock the object */ } ERRENDCLN(ctx->mcmcxgl->mcmcxerr) /* dirty the object, and release lock on object before return */ mcmtch(ctx, objn); /* mark the object as changed */ mcmunlck(ctx, objn); /* unlock it */ /* if necessary, rebuild the property index */ if (indexed) objindx(ctx, objn); } /* set an undo savepoint */ void objusav(objucxdef *undoctx) { /* the only thing in this record is the OBJUSAV header */ objures(undoctx, OBJUSAV, (ushort)0); } /* reserve space in an undo buffer, and write header */ uchar *objures(objucxdef *undoctx, uchar cmd, ushort siz) { ushort prv; uchar *p; /* adjust size to include header information */ siz += 1 + sizeof(ushort); /* make sure there's enough room overall for the record */ if (siz > undoctx->objucxsiz) errsig(undoctx->objucxerr, ERR_UNDOVF); /* if there's no information, reset buffers */ if (undoctx->objucxhead == undoctx->objucxprv) { undoctx->objucxhead = undoctx->objucxprv = undoctx->objucxtail = 0; undoctx->objucxtop = 0; goto done; } /* if tail is below head, we can use to top of entire buffer */ if (undoctx->objucxtail < undoctx->objucxhead) { /* if there's enough space left after head, we're done */ if (undoctx->objucxsiz - undoctx->objucxhead >= siz) goto done; /* insufficient space: wrap head down to bottom of buffer */ undoctx->objucxtop = undoctx->objucxprv; /* last was top */ undoctx->objucxhead = 0; } /* head is below tail: delete records until we have enough room */ while (undoctx->objucxtail - undoctx->objucxhead < siz) { objutadv(undoctx); /* if the tail wrapped, advancing won't do any more good */ if (undoctx->objucxtail <= undoctx->objucxhead) { /* if there's enough room at the top, we're done */ if (undoctx->objucxsiz - undoctx->objucxhead >= siz) goto done; /* still not enough room; wrap the head this time */ undoctx->objucxtop = undoctx->objucxprv; /* last was top */ undoctx->objucxhead = 0; } } done: /* save back-link, and set objucxprv pointer to the new record */ prv = undoctx->objucxprv; undoctx->objucxprv = undoctx->objucxhead; /* write the header: command byte, back-link to previous record */ p = &undoctx->objucxbuf[undoctx->objucxhead]; *p++ = cmd; memcpy(p, &prv, sizeof(prv)); /* advance the head pointer past the header */ undoctx->objucxhead += 1 + sizeof(prv); /* set the high-water mark if we've exceeded the old one */ if (undoctx->objucxprv > undoctx->objucxtop) undoctx->objucxtop = undoctx->objucxprv; /* return the reserved space */ return &undoctx->objucxbuf[undoctx->objucxhead]; } /* advance the undo tail pointer over the record it points to */ void objutadv(objucxdef *undoctx) { uchar *p; ushort siz; uchar pr[PRPHDRSIZ]; /* space for a property header */ uchar cmd; /* if we're at the most recently written record, flush buffer */ if (undoctx->objucxtail == undoctx->objucxprv) { undoctx->objucxtail = 0; undoctx->objucxprv = 0; undoctx->objucxhead = 0; undoctx->objucxtop = 0; } /* if we've reached high water mark, wrap back to bottom */ if (undoctx->objucxtail == undoctx->objucxtop) { undoctx->objucxtail = 0; return; } /* determine size by inspecting current record */ p = undoctx->objucxbuf + undoctx->objucxtail; siz = 1 + sizeof(ushort); /* basic header size */ cmd = *p++; p += sizeof(ushort); /* skip the previous pointer */ switch(cmd) { case OBJUCHG: /* change: property header (added below) plus data value */ memcpy(pr, p + sizeof(mcmon) + sizeof(prpnum), (size_t)PRPHDRSIZ); siz += PRPHDRSIZ + prpsize(pr); /* FALLTHROUGH */ case OBJUADD: case OBJUOVR: /* add/override: property header only */ siz += sizeof(mcmon) + sizeof(prpnum); break; case OBJUCLI: siz += (*undoctx->objucxcsz)(undoctx->objucxccx, p); break; case OBJUSAV: break; } undoctx->objucxtail += siz; } /* undo one undo record, and remove it from the undo list */ void obj1undo(mcmcxdef *mctx, objucxdef *undoctx) { uchar *p; prpnum prop; objnum objn; uchar cmd; uchar pr[PRPHDRSIZ]; /* space for property header */ ushort prv; ushort pofs; objdef *objptr; int indexed; /* if there's no more undo, signal an error */ if (undoctx->objucxprv == undoctx->objucxhead) errsig(undoctx->objucxerr, ERR_NOUNDO); /* move back to previous record */ undoctx->objucxhead = undoctx->objucxprv; p = &undoctx->objucxbuf[undoctx->objucxprv]; /* get command, and set undocxprv to previous record */ cmd = *p++; memcpy(&prv, p, sizeof(prv)); p += sizeof(prv); /* if we're at the tail, no more undo; otherwise, use back link */ if (undoctx->objucxprv == undoctx->objucxtail) undoctx->objucxprv = undoctx->objucxhead; else undoctx->objucxprv = prv; if (cmd == OBJUSAV) return; /* savepointer marker - nothing to do */ /* get object/property information for property-changing undo */ if (cmd != OBJUCLI) { memcpy(&objn, p, (size_t)sizeof(objn)); p += sizeof(objn); memcpy(&prop, p, (size_t)sizeof(prop)); p += sizeof(prop); objptr = mcmlck(mctx, objn); indexed = (objflg(objptr) & OBJFINDEX); mcmunlck(mctx, objn); } switch(cmd) { case OBJUADD: objdelp(mctx, objn, prop, FALSE); if (indexed) objindx(mctx, objn); break; case OBJUOVR: objdelp(mctx, objn, prop, FALSE); /* delete the non-original value */ pofs = objgetp(mctx, objn, prop, (dattyp *)0); /* get ignored prop */ objptr = (objdef *)mcmlck(mctx, objn); /* lock the object */ prpflg(objofsp(objptr, pofs)) &= ~PRPFIGN; /* no longer ignored */ mcmunlck(mctx, objn); /* unlock the object */ break; case OBJUCHG: memcpy(pr, p, (size_t)PRPHDRSIZ); p += PRPHDRSIZ; objsetp(mctx, objn, prop, prptype(pr), (void *)p, (objucxdef *)0); break; case OBJUCLI: (*undoctx->objucxcun)(undoctx->objucxccx, p); break; } } /* * Determine if it's ok to add undo records - returns TRUE if a * savepoint has been stored in the undo log, FALSE if not. */ int objuok(objucxdef *undoctx) { ushort prv; /* see if there's any more undo information */ if (undoctx->objucxprv == undoctx->objucxhead) return(FALSE); /* look for most recent savepoint marker */ for (prv = undoctx->objucxprv ;; ) { if (undoctx->objucxbuf[prv] == OBJUSAV) return(TRUE); /* found a savepoint - can add undo */ /* if we've reached the tail, there are no more undo records */ if (prv == undoctx->objucxtail) return(FALSE); /* no savepoints - can't add undo */ /* get previous record */ memcpy(&prv, &undoctx->objucxbuf[prv+1], sizeof(prv)); } } /* * Undo back to the most recent savepoint. If there is no savepoint in * the undo list, NOTHING will be undone. This prevents reaching an * inconsistent state in which some, but not all, of the operations * between two savepoints are undone: either all operations between two * savepoints will be undone, or none will. */ void objundo(mcmcxdef *mctx, objucxdef *undoctx) { ushort prv; ushort sav; /* see if there's any more undo information */ if (undoctx->objucxprv == undoctx->objucxhead) errsig(undoctx->objucxerr, ERR_NOUNDO); /* look for most recent savepoint marker */ for (prv = undoctx->objucxprv ;; ) { if (undoctx->objucxbuf[prv] == OBJUSAV) { sav = prv; break; } /* if we've reached the tail, there are no more undo records */ if (prv == undoctx->objucxtail) errsig(undoctx->objucxerr, ERR_ICUNDO); /* get previous record */ memcpy(&prv, &undoctx->objucxbuf[prv+1], sizeof(prv)); } /* now undo everything until we get to the savepoint */ do { obj1undo(mctx, undoctx); } while (undoctx->objucxhead != sav); } /* initialize undo context */ objucxdef *objuini(mcmcxdef *ctx, ushort siz, void (*undocb)(void *, uchar *), ushort (*sizecb)(void *, uchar *), void *callctx) { objucxdef *ret; long totsiz; /* force size into valid range */ totsiz = (long)siz + sizeof(objucxdef) - 1; if (totsiz > 0xff00) siz = 0xff00 - sizeof(objucxdef) + 1; ret = (objucxdef *)mchalo(ctx->mcmcxgl->mcmcxerr, (sizeof(objucxdef) + siz - 1), "objuini"); ret->objucxmem = ctx; ret->objucxerr = ctx->mcmcxgl->mcmcxerr; ret->objucxsiz = siz; ret->objucxhead = ret->objucxprv = ret->objucxtail = ret->objucxtop = 0; /* set client callback functions */ ret->objucxcun = undocb; /* callback to apply client undo */ ret->objucxcsz = sizecb; /* callback to get size of client undo */ ret->objucxccx = callctx; /* context for client callback functions */ return(ret); } /* discard all undo records */ void objulose(objucxdef *ctx) { if (ctx) ctx->objucxhead = ctx->objucxprv = ctx->objucxtail = ctx->objucxtop = 0; } /* uninitialize the undo context - release allocated memory */ void objuterm(objucxdef *uctx) { /* free the undo memory block */ mchfre(uctx); } /* revert object to original (post-compilation) values */ void objrevert(void *ctx0, mcmon objn) { mcmcxdef *mctx = (mcmcxdef *)ctx0; uchar *p; prpdef *pr; int cnt; int indexed; p = mcmlck(mctx, objn); pr = objprp(p); indexed = objflg(p) & OBJFINDEX; /* restore original settings */ objsfree(p, objrst(p)); objsnp(p, objstat(p)); /* go through original properties and remove 'ignore' flag if set */ for (cnt = objnprop(p) ; cnt ; pr = objpnxt(pr), --cnt) prpflg(pr) &= ~PRPFIGN; /* touch object and unlock it */ mcmtch(mctx, objn); mcmunlck(mctx, objn); /* if it's indexed, rebuild the index */ if (indexed) objindx(mctx, objn); } /* set 'ignore' flag for original properties set in mutable part */ void objsetign(mcmcxdef *mctx, objnum objn) { objdef *objptr; prpdef *mut; prpdef *p; int statcnt; int cnt; int indexed; prpdef *p1; objptr = (objdef *)mcmlck(mctx, (mcmon)objn); p1 = objprp(objptr); indexed = objflg(objptr) & OBJFINDEX; /* go through mutables, and set ignore on duplicates in non-mutables */ for (mut = (prpdef *)(objptr + objrst(objptr)), cnt = objnprop(objptr) - objstat(objptr) ; cnt ; mut = objpnxt(mut), --cnt) { for (p = p1, statcnt = objstat(objptr) ; statcnt ; p = objpnxt(p), --statcnt) { /* if this static prop matches a mutable prop, ignore it */ if (prpprop(p) == prpprop(mut)) { prpflg(p) |= PRPFIGN; break; } } } mcmtch(mctx, (mcmon)objn); mcmunlck(mctx, (mcmon)objn); if (indexed) objindx(mctx, objn); } /* * Build or rebuild a property index for an object. */ void objindx(mcmcxdef *mctx, objnum objn) { uint newsiz; uint avail; objdef *objptr; uint cnt; prpdef *p; uchar *indp; uchar *indbase; uint icnt; uint first; uint last; uint cur; objptr = (objdef *)mcmlck(mctx, objn); /* get object pointer */ cnt = objnprop(objptr); /* get number of properties */ p = objprp(objptr); /* get pointer to properties (or old index) */ newsiz = 2 + 4*cnt; /* figure size needed for the index */ avail = mcmobjsiz(mctx, objn) - objfree(objptr); /* insert space for the index; expand the object if necessary */ if (avail < newsiz) { ushort need; newsiz += 10*4; /* add some extra space for later */ need = newsiz - avail; /* compute amount of space needed */ objptr = objexp(mctx, objn, &need); p = objprp(objptr); } /* now build the index */ indbase = objpfre(objptr); for (icnt = 0 ; cnt ; p = objpnxt(p), --cnt, ++icnt) { uint ofs = (uchar *)p - (uchar *)objptr; if (icnt) { /* figure out where to insert this property */ first = 0; last = icnt - 1; for (;;) { if (first > last) break; cur = first + (last - first)/2; indp = indbase + cur*4; if (indp[0] == p[0] && indp[1] == p[1]) break; else if (indp[0] < p[0] || (indp[0] == p[0] && indp[1] < p[1])) first = (cur == first ? first + 1 : cur); else last = (cur == last ? last - 1 : cur); } /* make sure we're positioned just before insertion point */ while (cur < icnt && (indp[0] <= p[0] || (indp[0] == p[0] && indp[1] <= p[1]))) { indp += 4; ++cur; } /* move elements above if any */ if (cur < icnt) memmove(indp + 4, indp, (size_t)((icnt - cur) * 4)); } else indp = indbase; /* insert property into index */ indp[0] = p[0]; indp[1] = p[1]; oswp2(indp+2, ofs); } /* set the index flag, and dirty and free the object */ objsflg(objptr, objflg(objptr) | OBJFINDEX); mcmtch(mctx, (mcmon)objn); mcmunlck(mctx, (mcmon)objn); } /* allocate and initialize an object */ objdef *objnew(mcmcxdef *mctx, int sccnt, ushort propspace, objnum *objnptr, int classflg) { objdef *o; mcmon objn; /* allocate cache object */ o = (objdef *)mcmalo(mctx, (ushort)(OBJDEFSIZ + sccnt * 2 + propspace), &objn); /* set up object descriptor for the new object */ objini(mctx, sccnt, (objnum)objn, classflg); *objnptr = (objnum)objn; return(o); } /* initialize an already allocated object */ void objini(mcmcxdef *mctx, int sccnt, objnum objn, int classflg) { objdef *o; uint flags = 0; /* get a lock on the object */ o = (objdef *)mcmlck(mctx, objn); memset(o, 0, (size_t)10); objsnsc(o, sccnt); objsfree(o, ((uchar *)objsc(o) + 2*sccnt) - (uchar *)o); /* set up flags */ if (classflg) flags |= OBJFCLASS; objsflg(o, flags); /* tell cache manager that this object has been modified */ mcmtch(mctx, objn); mcmunlck(mctx, objn); } /* * Get the first superclass of an object. If it doesn't have any * superclasses, return invalid. */ objnum objget1sc(mcmcxdef *ctx, objnum objn) { objdef *p; objnum retval; /* lock the object */ p = mcmlck(ctx, (mcmon)objn); /* get the first superclass if it has any */ if (objnsc(p) == 0) retval = MCMONINV; else retval = osrp2(objsc(p)); /* unlock the object and return the superclass value */ mcmunlck(ctx, (mcmon)objn); return retval; } qtads-2.1.7/tads2/obj.h000066400000000000000000000422701265017072300146050ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/OBJ.H,v 1.3 1999/07/11 00:46:30 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name obj.h - object definitions Function Defines interface for object handling routines Notes An object is divided into two parts: a "static" part and a "dynamic" part. The static part contains all of the properties originally defined in the object; none of these properties can be deleted, but they can have a flag set to indicate that they are to be ignored, which effectively deletes them. The dynamic part changes at run-time, with properties deleted and changed as requested. The static part is kept around at all times to allow objects to revert back to their original state with very little work. For TADS games, this allows objects to be restored to their pre-play state after game play, without requiring a separate snapshot of the objects. In addition, it allows object state to be saved in a very compact format, because only the object header and dynamic portion need to be written; the static portion, which may contain a large amount of code, need not be saved when saving object state. Modified 08/11/91 MJRoberts - creation */ #ifndef OBJ_INCLUDED #define OBJ_INCLUDED #ifndef PRP_INCLUDED #include "prp.h" #endif #ifndef DAT_INCLUDED #include "dat.h" #endif #ifndef MCM_INCLUDED #include "mcm.h" #endif #ifndef ERR_INCLUDED #include "err.h" #endif #ifdef __cplusplus extern "C" { #endif /* object number */ typedef ushort objnum; /* * For non-class objects, we'll leave some space free in the object so * that a few properties can be added without having to resize the * object. Class objects will probably never have anything added, so * there's no need for extra space. */ #define OBJEXTRA 64 /* object structure */ /* struct objdef { ushort objws; /o workspace object was defined in o/ ushort objflg; /o object flags o/ ushort objnsc; /o number of superclasses o/ ushort objnprop; /o number of properties defined in object o/ ushort objfree; /o offset of next free byte in property area o/ ushort objrst; /o reset point - offset of end of 'static' part o/ ushort objstat; /o number of 'static' properties o/ objnum objsc[1]; /o superclasses o/ }; typedef struct objdef objdef; */ # define OBJFCLS 0x01 /* object is a class */ /* The object structure is actually laid out portably, using unaligned 2-byte arrays, stored least significant byte first, for each ushort (including the objnum array for the superclasses). The actual entries are at these offsets on all machines: objws 0 objflg 2 objnsc 4 objnprop 6 objfree 8 objrst 10 objstat 12 objsc[0] 14 objsc[1] 16 etc If the OBJFINDEX flag is set, the object has a property index. The index occurs after the last superclass (so it's where the property data would go if there were no index), and the property data follows. Each index entry consists of a pair of two-byte entries: the first is the property number, and the second is its offset within the object. For performance reasons, an index is only built on a class object -- whenever a property is changed within an object, the entire index must be rebuilt, because the locations of many properties within the object can be changed by a single property change in the object. The index is ordered by property number, so it can be searched using a binary search. Furthermore, "ignored" properties are excluded from the index; only the active instance of a particular property is stored. The index must be maintained by all routines that can change property information: setp, delp, revert, etc. Preceding the index table is a two-byte entry that gives the offset of the properties. Since the properties immediately follow the index, this can be used to deduce how large a space is available for the index itself. */ typedef uchar objdef; #define OBJDEFSIZ 14 /* "sizeof(objdef)" - size of object header w/o sc's */ /* object flags */ #define OBJFCLASS 1 /* object is a class */ #define OBJFINDEX 2 /* object has a property index */ #define OBJFMOD 4 /* object has been modified by a newer definition */ /* undo context */ struct objucxdef { mcmcxdef *objucxmem; /* cache manager context */ errcxdef *objucxerr; /* error context */ ushort objucxsiz; /* size of the undo buffer */ ushort objucxhead; /* head (position of next write) */ ushort objucxtail; /* tail (position of oldest record) */ ushort objucxprv; /* previous head pointer */ ushort objucxtop; /* highest head value written */ void (*objucxcun)(void *ctx, uchar *data); /* apply a client undo record */ ushort (*objucxcsz)(void *ctx, uchar *data); /* get size of a client undo record */ void *objucxccx; /* client undo context */ uchar objucxbuf[1]; /* undo buffer */ }; typedef struct objucxdef objucxdef; /* * Undo records are kept in a circular buffer allocated as part of an * undo context. Offsets within the buffer are kept for the head, tail, * and previous head records. The head always points to the byte at * which the next undo record will be written. The previous head points * to the most recently written undo record; it contains a back link to * the undo record before that, and so forth back through the entire * chain. (These reverse links are necessary because undo records vary * in size depending on the data contained within.) The tail points to * the oldest undo record that's still in the buffer. Conceptually, the * head is always "above" the tail in the buffer; since the buffer is * circular, the tail may have a higher address, but this just means * that the buffer wraps around at the top. When the head bumps into * the tail (i.e., the head address is physically below or equal to the * tail address, and the head is then advanced so that its address * becomes higher than the tail's), the tail is advanced by discarding * as many of the least recent undo records as necessary to make room * for the new head position. When the head and the previous head point * to the same place, we have no undo records in the buffer. */ /* * The first byte of an undo record specifies what action is to be * undone. If a property was added, it is undone merely by deleting the * property. If a property was changed, it is undone by setting the * property back to its old value. An additional special flag indicates * a "savepoint." Normally, all changes back to a savepoint will be * undone. */ #define OBJUADD 1 /* a property was added (undo by deleting) */ #define OBJUCHG 2 /* a property was changed (change back to old value) */ #define OBJUSAV 3 /* savepoint marker (no property information) */ #define OBJUOVR 4 /* override original property (set orig to IGNORE) */ #define OBJUCLI 5 /* client undo record (any client data) */ /* * After the control byte (OBJUxxx), the object number, property * number, datatype, and data value will follow; some or all of these * may be omitted, depending on the control byte. */ /* get object flags */ #define objflg(o) ((ushort)osrp2(((char *)(o)) + 2)) /* get object flags */ #define objsflg(o, val) oswp2(((char *)(o)) + 2, val) /* given an object pointer, get a pointer to the first prpdef */ /* prpdef *objprp(objdef *objptr); */ #define objprp(o) ((prpdef *)(objsc(o) + 2*objnsc(o))) /* given an object pointer, get number of properties in the prpdef */ /* int objnprop(objdef *objptr); */ #define objnprop(o) ((ushort)osrp2(((char *)(o)) + 6)) /* set number of properties */ /* void objsnp(objdef *objptr, int newnum); */ #define objsnp(o,n) oswp2(((char *)(o)) + 6, n) /* given an object pointer, get offset of free space */ /* int objfree(objdef *objptr); */ #define objfree(o) ((ushort)osrp2(((char *)(o)) + 8)) /* set free space pointer */ /* void objsfree(objdef *objptr, int newfree); */ #define objsfree(o,n) oswp2(((char *)(o)) + 8, n) /* get number of static properties */ /* ushort objstat(objdef *objptr); */ #define objstat(o) ((ushort)osrp2(((char *)(o)) + 10)) /* set number of static properties */ /* void objsetst(objdef *objptr, int newstat); */ #define objsetst(o,n) oswp2(((char *)(o)) + 10, n) /* get reset size (size of static properties) */ /* ushort objrst(objdef *objptr); */ #define objrst(o) ((ushort)osrp2(((char *)(o)) + 12)) /* set reset size */ /* void objsetrst(objdef *objptr, uint newrst); */ #define objsetrst(o,n) oswp2(((char *)(o)) + 12, n) /* given an object pointer, get first superclass pointer */ /* uchar *objsc(objdef *objptr); */ #define objsc(o) (((uchar *)(o)) + OBJDEFSIZ) /* given an object pointer, get number of superclasses */ /* int objnsc(objdef *objptr); */ #define objnsc(o) ((ushort)osrp2(((char *)(o)) + 4)) /* set number of superclasses */ /* void objsnsc(objdef *objptr, int num); */ #define objsnsc(o,n) oswp2(((char *)(o)) + 4, n) /* given a prpdef, get the next prpdef */ /* prpdef *objpnxt(prpdef *p); */ #define objpnxt(p) \ ((prpdef *)(((uchar *)(p)) + PRPHDRSIZ + prpsize(p))) /* get pointer to free prpdef */ /* prpdef *objpfre(objdef *objptr); */ #define objpfre(o) ((prpdef *)(((uchar *)(o)) + objfree(o))) /* given a prpdef and an object pointer, compute the prpdef offset */ /* uint objpofs(objdef *objptr, prpdef *propptr); */ #define objpofs(o,p) ((uint)((p) ? (((uchar *)(p)) - ((uchar *)(o))) : 0)) /* given an object pointer and a property offset, get prpdef pointer */ /* prpdef *objofsp(objdef *objptr, uint propofs); */ #define objofsp(o,ofs) ((prpdef *)((ofs) ? (((uchar *)(o)) + (ofs)) : 0)) /* * Get the first superclass of an object. If it doesn't have any * superclasses, return invalid. */ objnum objget1sc(mcmcxdef *ctx, objnum objn); /* * Get an object's property WITHOUT INHERITANCE. If the object has the * indicated property set, the byte OFFSET of the prpdef within the * object is returned. The offset will remain valid until any type of * operation that sets a property in the object (such as objdelp, * objsetp, or an undo operation). An offset of zero means that the * property was not set in the object. */ uint objgetp(mcmcxdef *ctx, objnum objn, prpnum prop, dattyp *typptr); /* * Get the *ending* offset of the given property's value, without any * inheritance. Returns the byte offset one past the end of the * property's data. */ uint objgetp_end(mcmcxdef *ctx, objnum objn, prpnum prop); /* * Get a property of an object, either from the object or from a * superclass (inherited). If the inh flag is TRUE, we do not look * at all in the object itself, but restrict our search to inherited * properties only. We return the byte OFFSET of the prpdef within * the object in which the prpdef is found; the superclass object * itself is NOT locked upon return, but we will NOT unlock the * object passed in. If the offset is zero, the property was not * found. The offset returned is valid until any operation that * sets a property in the object (such as objdelp, objsetp, or an * undo operation). */ uint objgetap(mcmcxdef *ctx, noreg objnum objn, prpnum prop, objnum *orn, int inh); /* * expand an object by a requested amount, returning a pointer to the * object's new location if it must be moved. The object will be * unlocked and relocked by this call. On return, the actual amount * of space ADDED to the object will be returned. */ objdef *objexp(mcmcxdef *ctx, objnum obj, ushort *siz); /* * Set an object's property, deleting the original value of the * property if it existed. If an undo context is provided, write an * undo record for the change; if the undo context pointer is null, no * undo information is retained. */ void objsetp(mcmcxdef *ctx, objnum obj, prpnum prop, dattyp typ, void *val, objucxdef *undoctx); /* * Delete a property. If mark_only is true, we'll only mark the * property as deleted without actually reclaiming its space; this is * necessary when removing a code property (type DAT_CODE) any time * other code properties may follow, because p-code is not entirely * self-relative and thus can't always be relocated within an object. */ void objdelp(mcmcxdef *mctx, objnum objn, prpnum prop, int mark_only); /* * Set up for emitting code into an object. Writes a property header * of type 'code', and returns the offset of the next free byte in the * object. Call objendemt when done. The datatype argument is * provided so that list generation can be done through the same * mechanism, since parser lists must be converted to run-time * lists via the code generator. */ uint objemt(mcmcxdef *ctx, objnum objn, prpnum prop, dattyp typ); /* done emitting code into property, finish setting object info */ void objendemt(mcmcxdef *ctx, objnum objn, prpnum prop, uint endofs); /* * Determine if undo records should be kept. Undo records should be * kept only if a savepoint is present in the undo log. If no savepoint * is present, adding undo records would be useless, since it will not * be possible to apply the undo information. */ int objuok(objucxdef *undoctx); /* * Reserve space in an undo buffer, deleting old records as needed. * Returns a pointer to the reserved space. */ uchar *objures(objucxdef *undoctx, uchar cmd, ushort siz); /* advance the tail pointer in an undo buffer over the record it points to */ void objutadv(objucxdef *undoctx); /* apply one undo record, and remove it from undo list */ void obj1undo(mcmcxdef *mctx, objucxdef *undoctx); /* * Undo back to the most recent savepoint. If there is no savepoint in * the undo list, NOTHING will be undone. This prevents reaching an * inconsistent state in which some, but not all, of the operations * between two savepoints are undone: either all operations between two * savepoints will be undone, or none will. */ void objundo(mcmcxdef *mctx, objucxdef *undoctx); /* set an undo savepoint */ void objusav(objucxdef *undoctx); /* initialize undo context */ objucxdef *objuini(mcmcxdef *memctx, ushort undosiz, void (*undocb)(void *ctx, uchar *data), ushort (*sizecb)(void *ctx, uchar *data), void *callctx); /* free the undo context - releases memory allocated by objuini() */ void objuterm(objucxdef *undoctx); /* discard all undo context (for times such as restarting) */ void objulose(objucxdef *undoctx); /* * Allocate and initialize a new object. The caller specifies the * number of superclasses to be allocated, and the amount of space (in * bytes) for the object's property data. The caller must fill in the * superclass array. Upon return, the object is allocated and locked, * and is initialized with no properties. A pointer to the object's * memory is returned, and *objnptr receives the object number. */ objdef *objnew(mcmcxdef *mctx, int sccnt, ushort propspace, objnum *objnptr, int classflg); /* initialize an already allocated object */ void objini(mcmcxdef *mctx, int sccnt, objnum objn, int classflg); /* * Add space for additional superclasses to an object. The object can * already have some properties set (if it doesn't, it can just be * reinitialized). */ void objaddsc(mcmcxdef *mctx, int sccnt, objnum objn); /* * Delete an object's properties and superclasses. The 'mindel' * parameter specifies the minimum property number to be deleted. * Properties below this are considered "system" properties that are not * to be deleted. This could be used by a development environment to * store the source for an object as a special system property in the * object; when the object is recompiled, all of the object's properties * and superclasses must be deleted except the source property, which is * retained even after recompilation. */ void objclr(mcmcxdef *mctx, objnum objn, prpnum mindel); /* Build or rebuild an object's property index */ void objindx(mcmcxdef *mctx, objnum objn); /* set up just-compiled object: mark static part and original properties */ void objcomp(mcmcxdef *mctx, objnum objn, int for_debug); /* revert an object to original post-compilation state */ void objrevert(void *mctx, mcmon objn); /* reset 'ignore' flags for a newly reconstructed object */ void objsetign(mcmcxdef *mctx, objnum objn); #ifdef __cplusplus } #endif #endif /* OBJ_INCLUDED */ qtads-2.1.7/tads2/oem.h000066400000000000000000000073461265017072300146200ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/OEMPROTO.H,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $ */ /* * Copyright (c) 1996, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name oem.h - "original equipment manufacturer" identification Function This file identifies the person who created this version of TADS. If you're porting TADS to a new machine, or you're customizing something, THIS MEANS YOU! The OEM name defined herein is displayed in the version banner of each executable to make it easy to identify the source of a particular version of the system. This file also specifies the OEM version number. This version number identifies the revision level of a particular base version of the TADS source code as released by a particular OEM. Notes EACH PERSON WHO PRODUCES A VERSION OF TADS SHOULD SET THE OEM IDENTIFIER IN THIS FILE TO A UNIQUE VALUE. The OEM identifier is intended to reduce the confusion that could result from different people producing versions of the system. If you produce a version of TADS, and someone has a question about it, it will be possible to tell from the OEM identifier on the person's executable whether it's your version or a version someone else produced. We recommend that you use your email address or full name as your OEM identifier, since this is the best way to ensure that you don't use an ID that someone else is using. Note that, if you release multiple versions of a particular release of the base TADS source code (for example, if you find a fix a port-specific bug in your version, and the fixed version is based on the same base TADS source code as the original release), you should update the OEM version number as well. This should start at zero for the first release that you make of a particular version of the base TADS source code, and should be incremented each time you release a new revision based on the same portable code. This version number shows up as the fourth part of the version number displayed in each executable's banner. Modified 10/05/96 MJRoberts - Creation */ #ifndef OEM_H #define OEM_H #ifndef OS_H #include "os.h" #endif /* * OEM name - this should uniquely identify your version, to * distinguish it from versions produced by other people; you should use * your name or email address to ensure uniqueness. If the name is * already defined (some people like to define it in the makefile), we * won't redefine it here. */ #ifndef TADS_OEM_NAME $$$ #define your own TADS_OEM_NAME here, similar to this: /* #define TADS_OEM_NAME "Mike Roberts" */ /* Please keep the size under 256 bytes */ /* Don't use "Mike Roberts" unless your name happens to be Mike Roberts. */ #endif /* * NOTE - if you redistribute this source file, please leave the dollar * signs intact above so that the next person who gets a copy gets a * compiler error and thereby realizes that they need to update this. */ /* * OEM version level - increment for each new release based on the same * original source code version. Note that this is a string. Some * people prefer to define the OEM patch level in their makefiles, so we * won't attempt to redefine it if it's already been defined. */ #ifndef TADS_OEM_VERSION # define TADS_OEM_VERSION "0" #endif /* * Some external strings that must be defined for the run-time banner. * Please refer to oem.c for the definitions of these strings. */ extern char G_tads_oem_app_name[]; extern char G_tads_oem_dbg_name[]; extern char G_tads_oem_display_mode[]; extern char G_tads_oem_author[]; extern int G_tads_oem_copyright_prefix; #endif /* OEM_H */ qtads-2.1.7/tads2/opc.h000066400000000000000000000232131265017072300146100ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/OPC.H,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name opc.h - opcode definitions Function Defines opcodes Notes Lifted largely from the old TADS, since the basic run-time interpreter's operation is essentially the same. Modified 09/23/91 MJRoberts - creation */ #ifndef OPC_INCLUDED #define OPC_INCLUDED #define OPCPUSHNUM 1 /* push a constant numeric value */ #define OPCPUSHOBJ 2 /* push an object */ #define OPCNEG 3 /* unary negation */ #define OPCNOT 4 /* logical negation */ #define OPCADD 5 /* addition/list concatenation */ #define OPCSUB 6 /* subtraction/list difference */ #define OPCMUL 7 /* multiplication */ #define OPCDIV 8 /* division */ #define OPCAND 9 /* logical AND */ #define OPCOR 10 /* logical OR */ #define OPCEQ 11 /* equality */ #define OPCNE 12 /* inequality */ #define OPCGT 13 /* greater than */ #define OPCGE 14 /* greater or equal */ #define OPCLT 15 /* less than */ #define OPCLE 16 /* less or equal */ #define OPCCALL 17 /* call a function */ #define OPCGETP 18 /* get property */ #define OPCGETPDATA 19 /* get a property, allowing only data values */ #define OPCGETLCL 20 /* get a local variable's value */ #define OPCPTRGETPDATA 21 /* get property via pointer; only allow data */ #define OPCRETURN 22 /* return without a value */ #define OPCRETVAL 23 /* return a value */ #define OPCENTER 24 /* enter a function */ #define OPCDISCARD 25 /* discard top of stack */ #define OPCJMP 26 /* unconditional jump */ #define OPCJF 27 /* jump if false */ #define OPCPUSHSELF 28 /* push current object */ #define OPCSAY 29 /* implicit printout for doublequote strings */ #define OPCBUILTIN 30 /* call a built-in function */ #define OPCPUSHSTR 31 /* push a string */ #define OPCPUSHLST 32 /* push a list */ #define OPCPUSHNIL 33 /* push the NIL value */ #define OPCPUSHTRUE 34 /* push the TRUE value */ #define OPCPUSHFN 35 /* push the address of a function */ #define OPCGETPSELFDATA 36 /* push property of self; only allow data */ #define OPCPTRCALL 38 /* call function pointed to by top of stack */ #define OPCPTRINH 39 /* inherit pointer to property (stack=prop) */ #define OPCPTRGETP 40 /* get property by pointer (stack=obj,prop) */ #define OPCPASS 41 /* pass to inherited handler */ #define OPCEXIT 42 /* exit turn, but continue with fuses/daemons */ #define OPCABORT 43 /* abort turn, skipping fuses/daemons */ #define OPCASKDO 44 /* ask for a direct object */ #define OPCASKIO 45 /* ask for indirect object and set preposition */ /* explicit superclass inheritance opcodes */ #define OPCEXPINH 46 /* "inherited ." */ #define OPCEXPINHPTR 47 /* "inherited ." */ /* * Special opcodes for peephole optimization. These are essentially * pairs of operations that occur frequently so have been collapsed into * a single instruction. */ #define OPCCALLD 48 /* call function and discard value */ #define OPCGETPD 49 /* evaluate property and discard any value */ #define OPCBUILTIND 50 /* call built-in function and discard value */ #define OPCJE 51 /* jump if equal */ #define OPCJNE 52 /* jump if not equal */ #define OPCJGT 53 /* jump if greater than */ #define OPCJGE 54 /* jump if greater or equal */ #define OPCJLT 55 /* jump if less than */ #define OPCJLE 56 /* jump if less or equal */ #define OPCJNAND 57 /* jump if not AND */ #define OPCJNOR 58 /* jump if not OR */ #define OPCJT 59 /* jump if true */ #define OPCGETPSELF 60 /* get property of the 'self' object */ #define OPCGETPSLFD 61 /* get property of 'self' and discard result */ #define OPCGETPOBJ 62 /* get property of a given object */ /* note: differs from GETP in that object is */ /* encoded into the instruction */ #define OPCGETPOBJD 63 /* get property of an object and discard result */ #define OPCINDEX 64 /* get an indexed entry from a list */ #define OPCPUSHPN 67 /* push a property number */ #define OPCJST 68 /* jump and save top-of-stack if true */ #define OPCJSF 69 /* jump and save top-of-stack if false */ #define OPCJMPD 70 /* discard stack and then jump unconditionally */ #define OPCINHERIT 71 /* inherit a property from superclass */ #define OPCCALLEXT 72 /* call external function */ #define OPCDBGRET 73 /* return to debugger (no stack frame leaving) */ #define OPCCONS 74 /* construct list from top two stack elements */ #define OPCSWITCH 75 /* switch statement */ #define OPCARGC 76 /* get argument count */ #define OPCCHKARGC 77 /* check actual arguments against formal count */ #define OPCLINE 78 /* line record */ #define OPCFRAME 79 /* local variable frame record */ #define OPCBP 80 /* breakpoint - replaces an OPCLINE instruction */ #define OPCGETDBLCL 81 /* get debugger local */ #define OPCGETPPTRSELF 82 /* get property pointer from self */ #define OPCMOD 83 /* modulo */ #define OPCBAND 84 /* binary AND */ #define OPCBOR 85 /* binary OR */ #define OPCXOR 86 /* binary XOR */ #define OPCBNOT 87 /* binary negation */ #define OPCSHL 88 /* bit shift left */ #define OPCSHR 89 /* bit shift right */ #define OPCNEW 90 /* create new object */ #define OPCDELETE 91 /* delete object */ /* ----- opcodes 192 and above are reserved for assignment operations ----- */ /* ASSIGNMENT OPERATIONS When (opcode & 0xc0 == 0xc0), we have an assignment operation. (Note that this means that opcodes from 0xc0 up are all reserved for assignment operations.) The low six bits of the opcode specify exactly what kind of operation is to be performed: bits 0-1: specifies destination type: 00 2-byte operand is local number 01 2-byte operand is property to set in obj at tos 10 tos is index, [sp-1] is list to be indexed and set 11 tos is property pointer, [sp-1] is object bits 2-4: specifies assignment operation: 000 := (direct assignment) 001 += (add tos to destination) 010 -= (subtract tos from destination) 011 *= (multiply destination by tos) 100 /= (divide destination by tos) 101 ++ (increment tos) 110 -- (decrement tos) 111 *reserved* bit 5: specifies what to do with value computed by assignment 0 leave on stack (implies pre increment/decrement) 1 discard (implies post increment/decrement) */ #define OPCASI_MASK 0xc0 /* assignment instruction */ #define OPCASIDEST_MASK 0x03 /* mask to get destination field */ #define OPCASILCL 0x00 /* assign to a local */ #define OPCASIPRP 0x01 /* assign to an object.property */ #define OPCASIIND 0x02 /* assign to an element of a list */ #define OPCASIPRPPTR 0x03 /* assign property via pointer */ #define OPCASITYP_MASK 0x1c /* mask to get assignment type field */ #define OPCASIDIR 0x00 /* direct assignment */ #define OPCASIADD 0x04 /* assign and add */ #define OPCASISUB 0x08 /* assign and subtract */ #define OPCASIMUL 0x0c /* assign and multiply */ #define OPCASIDIV 0x10 /* assign and divide */ #define OPCASIINC 0x14 /* increment */ #define OPCASIDEC 0x18 /* decrement */ #define OPCASIEXT 0x1c /* other - extension flag */ /* extended assignment flags - next byte when OPCASIEXT is used */ #define OPCASIMOD 1 /* modulo and assign */ #define OPCASIBAND 2 /* binary AND and assign */ #define OPCASIBOR 3 /* binary OR and assign */ #define OPCASIXOR 4 /* binary XOR and assign */ #define OPCASISHL 5 /* shift left and assign */ #define OPCASISHR 6 /* shift right and assign */ #define OPCASIPRE_MASK 0x20 /* mask for pre/post field */ #define OPCASIPOST 0x00 /* increment after push */ #define OPCASIPRE 0x20 /* increment before push */ /* some composite opcodes for convenience */ #define OPCSETLCL (OPCASI_MASK | OPCASILCL | OPCASIDIR) #endif /* OPC_INCLUDED */ qtads-2.1.7/tads2/os.h000066400000000000000000000140701265017072300144510ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/OS.H,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name os.h - operating system definitions Function Definitions that vary by operating system Notes Do NOT put your system-specific definitions in this file, EXCEPT for your system-specific #include lines. We want to avoid piling up a huge tree of #ifdef's, since it's almost impossible to decipher such code. Instead, we want to isolate all of the code for each platform in its own header file -- this way, it should be easy to figure out what code is compiled on each system. So, when porting this code to a new platform, you should first create an osxxx.h file for your platform (for example, on Windows, the file is oswin.h), then add to this file an ifdef'd include for your file, something like this: #ifdef _FROBNIX #include "osfrobnix.h" #endif These should generally be the ONLY lines in this file that pertain to your system. Everything else for your system should be defined in your osxxx.h file. Also note that some definitions belong in your *hardware* file, rather than your operating system file. Since many types of hardware have several operating systems, and many operating systems run on more than one type of hardware, definitions that pertain to a particular type of hardware should be isolated in a separate file. So, if you're adding a new hardware platform as well as (or instead of) a new operating system, you should create a new h_xxx.h file (in the "hardware" source subdirectory), and add an include line like this: #ifdef _M_BANANA_3000 #include "h_b3000.h" #endif Note that you may have to adjust your makefile's CFLAGS so that the proper hardware and software configuration is selected via -D options (or your local equivalent). Modified 10/17/98 MJRoberts - creation (from TADS 2 os.h, los.h, etc) */ #ifndef OS_INCLUDED #define OS_INCLUDED /* * For C++ files, define externals with C linkage */ #ifdef __cplusplus extern "C" { #endif /* ------------------------------------------------------------------------ */ /* * Include the appropriate hardware-specific header. */ /* * Intel x86 processors - 32-bit */ #ifdef _M_IX86 #include "h_ix86.h" #endif /* * Intel x86 processors - 64-bit */ #ifdef _M_IX86_64 #include "h_ix86_64.h" #endif /* * PowerPC CPU's */ #ifdef _M_PPC #include "h_ppc.h" #endif /* * Qt. This isn't *truly* a hardware platform, but it looks like one to * us, because it provides its own hardware virtualization scheme. Rather * than trying to figure out what sort of physical hardware we're * targeting, we can simply define our hardware virtualization macros and * functions in terms of Qt's hardware virtualization APIs, and let Qt take * care of providing the target-specific implementation of those APIs. */ #ifdef _M_QT #include "h_qt.h" #endif /* add others here */ /* ------------------------------------------------------------------------ */ /* * Include the portable OS interface type definitions. These types can * be used within OS-specific headers, so this type definitions header * must be included before any of the OS-specific headers. */ #include "osifctyp.h" /* ------------------------------------------------------------------------ */ /* * Include the appropriate OS-specific header. We switch on system type * here to avoid a big pile of ifdef's for each system scattered among * all of the headers, and instead just select one big file for each * system-specific definitions. */ #ifdef _WIN32 # include "oswin.h" #endif #ifdef __MSDOS__ # ifdef T_WIN32 /* Windows-specific definitions are in oswin.h */ # include "oswin.h" # else # ifdef MSOS2 /* OS/2-specific definnitions are in osos2.h */ # include "osos2.h" # else /* DOS-specific definitions are in osdos.h */ # include "osdos.h" # endif # endif #endif #ifdef MAC_OS /* macintosh definitions (Mac OS <=9) are in osmac.h */ #include "osmac.h" #endif #ifdef MAC_OS_X /* macintosh OS X definitions are in osmacosx.h */ #include "osmacosx.h" #endif #ifdef UNIX /* unix definitions are in osunixt.h */ #include "osunixt.h" #endif #ifdef ATARI /* Atari ST definitions are in osatari.h */ #include "osatari.h" #endif #ifdef GLK /* glk definitions are in os_glk.h */ #include "os_glk.h" #endif #ifdef __BEOS__ #include "osbeos.h" #endif #ifdef TROLLTECH_QT /* Qt-specific definitions are in osqt.h */ #include "osqt.h" #endif #ifdef FROBTADS /* * FrobTADS definitions are in osfrobtads.h. (FrobTADS isn't really an OS, * but from our perspective it looks like one, since it implements the * various osifc entrypoints. FrobTADS further virtualizes access to * several Curses-style terminal i/o APIs, but we don't care about that; we * just care that it provides our osifc implementations.) */ #include "osfrobtads.h" #endif /* **************** add other systems here **************** */ /* * Done with C linkage section (osifc.h has its own) */ #ifdef __cplusplus } #endif /* ------------------------------------------------------------------------ */ /* * Include the generic interface definitions for routines that must be * implemented separately on each platform. * * Note that we include this file *after* including the system-specific * osxxx.h header -- this allows definitions in the osxxx.h header to * override certain defaults in osifc.h by #defining symbols to indicate * to osifc.h that it should not include the defaults. Refer to osifc.h * for details of such overridable definitions. */ #include "osifc.h" /* ------------------------------------------------------------------------ */ /* * If the system "long description" (for the banner) isn't defined, * make it the same as the platform ID string. */ #ifndef OS_SYSTEM_LDESC # define OS_SYSTEM_LDESC OS_SYSTEM_NAME #endif #endif /* OS_INCLUDED */ qtads-2.1.7/tads2/osbigmem.h000066400000000000000000000045711265017072300156370ustar00rootroot00000000000000/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name osbigmem.h - define "big memory" defaults for the TADS 2 compiler/ interpreter/debugger initialization parameters Function This is a common header that osxxx.h headers can include to define the maximum values for the memory configuration parameters for the TADS 2 tools. Many modern systems have sufficient memory that there's no reason not to choose the largest values for these parameters. Notes Modified 12/04/01 MJRoberts - Creation */ #ifndef OSBIGMEM_H #define OSBIGMEM_H /* * Define the largest sizes for the memory configuration parameters */ #define TCD_SETTINGS_DEFINED #define TCD_POOLSIZ (24 * 1024) #define TCD_LCLSIZ (16 * 1024) #define TCD_HEAPSIZ 65535 #define TCD_STKSIZ 512 #define TCD_LABSIZ 8192 #define TRD_SETTINGS_DEFINED #define TRD_HEAPSIZ 65535 #define TRD_STKSIZ 512 #define TRD_UNDOSIZ 60000 /* WARNING: above 65535 will cause crashes */ #define TDD_SETTINGS_DEFINED #define TDD_HEAPSIZ 65535 #define TDD_STKSIZ 512 #define TDD_UNDOSIZ 60000 /* WARNING: above 65535 will cause crashes */ #define TDD_POOLSIZ (24 * 1024) #define TDD_LCLSIZ 0 /* * define usage strings for the new defaults */ #define TCD_HEAPSIZ_MSG " -mh size heap size (default 65535 bytes)" #define TCD_POOLSIZ_MSG \ " -mp size parse node pool size (default 24576 bytes)" #define TCD_LCLSIZ_MSG \ " -ml size local symbol table size (default 16384 bytes)" #define TCD_STKSIZ_MSG " -ms size stack size (default 512 elements)" #define TCD_LABSIZ_MSG \ " -mg size goto label table size (default 8192 bytes)" #define TRD_HEAPSIZ_MSG " -mh size heap size (default 65535 bytes)" #define TRD_STKSIZ_MSG " -ms size stack size (default 512 elements)" #define TRD_UNDOSIZ_MSG \ " -u size set undo to size (0 to disable; default 60000)" #define TDD_HEAPSIZ_MSG " -mh size heap size (default 65535 bytes)" #define TDD_STKSIZ_MSG " -ms size stack size (default 512 elements)" #define TDD_UNDOSIZ_MSG \ " -u size set undo to size (0 to disable; default 60000)" #define TDD_POOLSIZ_MSG \ " -mp size parse pool size (default 24576 bytes)" #endif /* OSBIGMEM_H */ qtads-2.1.7/tads2/oserr.c000066400000000000000000000015271265017072300151600ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/OSERR.C,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $"; #endif /* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name oserr.c - find error message file Function Looks in executable's directory for TADSERR.MSG, and opens it Notes None Modified 04/04/99 CNebel - Improve const-ness. 04/24/93 JEras - use new os_locate() to find tadserr.msg 04/27/92 MJRoberts - creation */ #include #include "os.h" osfildef *oserrop(const char *arg0) { char buf[128]; if ( !os_locate("tadserr.msg", 11, arg0, buf, sizeof(buf)) ) return((osfildef *)0); return(osfoprb(buf, OSFTERRS)); } qtads-2.1.7/tads2/osgen.h000066400000000000000000000306701265017072300151470ustar00rootroot00000000000000/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name osgen.h - definitions for the osgen subsystem Function Notes Modified 05/19/02 MJRoberts - Creation */ #ifndef OSGEN_H #define OSGEN_H /* ------------------------------------------------------------------------ */ /* * osgen support routines. These routines are implemented by the * semi-portable osgen.c layer for use by the underlying osxxx code. */ /* * Initialize the osgen scrollback buffer. Allocate the given amount of * space in bytes for scrollback. */ void osssbini(unsigned int size); /* * Delete the scrollback buffer. This should be called during program * termination to release memory allocated by osssbini(). */ void osssbdel(void); /* * The size, in character cells, of the ENTIRE SCREEN (or, at least, the * entire part of the screen available for our use). Note that these * variables reflect the size of the entire screen - these are NOT * adjusted for status lines, banners, or anything else. * * The system-specific "oss" code MUST initialize these variables during * startup, and MUST then keep them up to date whenever the screen size * changes (for example, if we're running in a terminal window, and the * user resizes the window, these must be updated to reflect the new * window size). IN ADDITION, whenever the display size changes after * start-up, the "oss" code MUST call osssb_on_resize_screen() to notify * the osgen layer of the change. * * IMPORTANT: In the past, the "oss" code was responsible for initializing * and maintaining the global variables G_os_linewidth and * G_os_pagelength. The "oss" code need not do this any more - in fact, * the OS code MUST STOP setting G_os_pagelength and G_os_linewidth. * Change these variables instead. */ extern int G_oss_screen_width; extern int G_oss_screen_height; /* * the "oss" code MUST call this routine whenever G_os_screen_width or * G_os_screen_height change (except during initialization) */ void osssb_on_resize_screen(void); #ifdef RUNTIME /* * Redraw the screen if necessary. The oss code MUST call this routine * whenever it's going to pause for a timed wait, or stop to wait for user * input. Specifically, this should be called from the OS implementation * of the following routines: * * os_getc() *. os_getc_raw() *. os_waitc() *. os_get_event() *. os_askfile(); *. os_input_dialog() *. os_sleep_ms() *. os_yield(); */ void osssb_redraw_if_needed(void); /* * Put the cursor at the default position. The oss code CAN optionally * call this at the start of routines that pause for input to put the * cursor at the default position in the main window. Depending on how the * oss code implements text drawing, the osgen code can sometimes leave the * cursor in a banner window rather than in the main window after drawing. * This routine will ensure that the cursor is in the main window at the * end of the main window's text. */ void osssb_cursor_to_default_pos(void); /* * Determine if stdin is at end-of-file. If possible, this should return * the stdin status even if we've never attempted to read stdin. * * For most Unix systems, this can return feof(stdin). With some C * libraries, however, this doesn't work until after an attempt has already * been made to read from stdin, so we provide this cover to allow flexible * implementation on different systems. On Windows, for example, it's * necessary to use _feof(_fileno(stdin)), because the higher-level feof() * doesn't recognize EOF until the handle has been read from. */ int oss_eof_on_stdin(void); #else /* RUNTIME */ /* in non-RUNTIME mode, we don't use osssb_redraw_if_needed at all */ #define osssb_redraw_if_needed() /* in non-RUNTIME mode, we don't need to reposition the cursor */ #define osssb_cursor_to_default_pos() #endif /* RUNTIME */ /* ------------------------------------------------------------------------ */ /* * ossxxx implementation for osgen. These routines must be defined by the * OS-specific subsystem. */ /* * Get extended system information. This checks to see if the ossxxx * implementation handles the system information code. If the osxxx layer * doesn't want to handle the code, it can return false so that the osgen * layer will provide a default interpretation. * * Currently, the following codes should be handled by the oss layer: * * SYSINFO_TEXT_COLORS - return the level of color support on this * platform. Because the ossxxx color interface that osgen uses is limited * to the ANSI colors, this should normally return one of the ANSI support * levels if color is supported, or the unsupported level if color isn't * supported. Note that if the platform uses a fixed color scheme that * cannot be controlled via the ossxxx routines, this should usually return * the "highlighting only" or "parameterized colors" level. * * SYSINFO_TEXT_HILITE - indicate whether or not highlighted text is * rendered in a distinctive appearance. */ int oss_get_sysinfo(int code, void *param, long *result); /* * Translate a portable color specification to an oss-style color code. * This returns a color code suitable for use in ossclr(), ossdsp(), and * the other ossxxx() routines defined here that take color codes as * parameters; this color code is opaque to the caller. * * The purpose of this routine is to translate from the portable interface * to color to the hardware-specific or OS-specific color coding used in * the ossxxx layer, so that a caller given a color specified via the * portable interface can send the appropriate ossxxx color code to the * lower-level routines. * * 'fg', 'bg', and 'screen_color' are OSGEN_COLOR_xxx values. 'attrs' is a * combination of OS_ATTR_xxx values (defined in osifc.h). This routine * should combine the colors specified by the combination of these values * to yield an appropriate color code for use in the ossxxx routines. * * Note that the 'bg' color can be OSGEN_COLOR_TRANSPARENT. If this is the * case, the 'screen_color' value should be used to determine the * background color, if possible. 'fg' should never be transparent. If * 'bg' is not transparent, then screen color is usually ignored. * * COLOR SUPPORT IS OPTIONAL, but this routine must be implemented if * osgen.c is to be used. If the platform does NOT support explicit color * settings, this routine should still return an appropriate code that can * be used with ossclr, etc. For example, the Unix oss implementation, at * the time of this writing, used an internal color scheme to select from a * fixed set of parameterized colors (normal, highlighted, reverse), so the * Unix implementation could simply return its appropriate internal code, * largely ignoring the requested colors. Note, however, that a platform * with only parameterized colors might still want to inspect the 'fg' and * 'bg' colors for the OSGEN_COLOR_xxx parameterized colors * (OSGEN_COLOR_TEXT, OSGEN_COLOR_STATUSBG, etc), and return its internal * code corresponding to the selected parameter color when possible. */ int ossgetcolor(int fg, int bg, int attrs, int screen_color); /* * Translate a key event from "raw" mode to processed mode. This takes an * event of type OS_EVT_KEY, and converts the keystroke encoded in the * event from the raw keystroke to a CMD_xxx code, if appropriate. * * In effect, this routine converts the event returned from os_get_event() * to the corresponding command code that would be returned from os_getc(). * * This routine is defined at the "oss" level because the osgen layer is * semi-portable, and thus needs to be aloof from the details of key * mappings, which vary by system. */ void oss_raw_key_to_cmd(os_event_info_t *evt); /* * Clear an area of the screen. The color code is opaque to callers; it is * meaningful only to the ossxxx implementation. */ void ossclr(int top, int left, int bottom, int right, int color); /* * Display text at a particular location in a particular color. */ void ossdsp(int line, int column, int color, const char *msg); /* * Move the cursor */ void ossloc(int line, int column); /* * Scroll a region of the screen UP one line. */ void ossscu(int top_line, int left_column, int bottom_line, int right_column, int blank_color); /* * Scroll a region of the screen DOWN one line. */ void ossscr(int top_line, int left_column, int bottom_line, int right_column, int blank_color); /* ------------------------------------------------------------------------ */ /* * osgen attribute names - these are simply substitutes for the regular * osifc attribute names; they map directly to the corresponding osifc * attributes. */ #define OSGEN_ATTR_BOLD OS_ATTR_BOLD /* ------------------------------------------------------------------------ */ /* * osgen colors. We don't use full RGB values in the osgen * implementation, but use a simpler color scheme involving the * parameterized colors (from the osifc layer), plus the eight ANSI colors * (black, white, red, green, blue, yellow, cyan, magenta), each in a * "low-intensity" and "high-intensity" version. * * If an underlying platform supports some colors but not all of the ANSI * colors defined here, it should simply map each OSGEN_COLOR_xxx color to * the closest available system color. * * Some systems use color intensity to render the "hilite" ("bold") * attribute. On these systems, it's obviously not possible for the * caller to select both boldness and the full range of colors * independently, because that would leave no way to render bold text for * the brighter half of the range of colors. Such systems should simply * treat the high-intensity and low-intensity version of a given color as * equivalent, and select the high- or low-intensity version according to * the boldness attribute. For reference, here's the correspondence * between the low- and high-intensity versions of the colors: * * low <-> high *. black <-> gray *. maroon <-> red *. green <-> lime *. navy <-> blue *. teal <-> cyan *. purple <-> magenta *. olive <-> yellow *. silver <-> white * * The comment for each absolute color gives the approximate RGB value we * expect for the color (given as hex triplets on 00-FF intensity scale). */ /* the parameterized colors, mapped to single-byte values for osgen's use */ #define OSGEN_COLOR_TRANSPARENT ((char)((OS_COLOR_P_TRANSPARENT) >> 24)) #define OSGEN_COLOR_TEXT ((char)((OS_COLOR_P_TEXT) >> 24)) #define OSGEN_COLOR_TEXTBG ((char)((OS_COLOR_P_TEXTBG) >> 24)) #define OSGEN_COLOR_STATUSLINE ((char)((OS_COLOR_P_STATUSLINE) >> 24)) #define OSGEN_COLOR_STATUSBG ((char)((OS_COLOR_P_STATUSBG) >> 24)) #define OSGEN_COLOR_INPUT ((char)((OS_COLOR_P_INPUT) >> 24)) /* "low-intensity" versions of the ANSI colors */ #define OSGEN_COLOR_BLACK 0x70 /* 00 00 00 */ #define OSGEN_COLOR_MAROON 0x71 /* 80 00 00 */ #define OSGEN_COLOR_GREEN 0x72 /* 00 80 00 */ #define OSGEN_COLOR_NAVY 0x73 /* 00 00 80 */ #define OSGEN_COLOR_TEAL 0x74 /* 00 80 80 */ #define OSGEN_COLOR_PURPLE 0x75 /* 80 00 80 */ #define OSGEN_COLOR_OLIVE 0x76 /* 80 80 00 */ #define OSGEN_COLOR_SILVER 0x77 /* C0 C0 C0 */ /* "high-intensity" versions of the ANSI colors */ #define OSGEN_COLOR_GRAY 0x78 /* 80 80 80 */ #define OSGEN_COLOR_RED 0x79 /* FF 00 00 */ #define OSGEN_COLOR_LIME 0x7A /* 00 FF 00 */ #define OSGEN_COLOR_BLUE 0x7B /* 00 00 FF */ #define OSGEN_COLOR_CYAN 0x7C /* 00 FF FF */ #define OSGEN_COLOR_MAGENTA 0x7D /* FF 00 FF */ #define OSGEN_COLOR_YELLOW 0x7E /* FF FF 00 */ #define OSGEN_COLOR_WHITE 0x7F /* FF FF FF */ #endif /* OSGEN_H */ qtads-2.1.7/tads2/osifc.c000066400000000000000000000030401265017072300151210ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name osifc.c - operating system interface - portable definitions Function This module contains portable code for the TADS OS interface. This code is portable - it doesn't need to be modified for any platform, and it should be linked into the executables on all platforms. Notes Modified 09/27/99 MJRoberts - Creation */ #include "std.h" #include "os.h" /* ------------------------------------------------------------------------ */ /* * Global variables */ /* * Page length and line width of the display area. We default to a 24x80 * display area; the OS code should change these values during startup to * reflect the actual display area size, if it can be determined. * * The portable code can use these values to determine the screen size for * layout purposes (such as word-wrapping and "more" prompt insertions). */ int G_os_pagelength = 24; int G_os_linewidth = 80; /* * MORE mode flag */ int G_os_moremode = TRUE; /* * Name of the loaded game file, if applicable. The application should set * this during start-up (or wherever else is appropriate). For the * interpreter, this is the .gam or .t3 file being executed. For * executables other than interpreters, this should simply be left empty. */ char G_os_gamename[OSFNMAX]; qtads-2.1.7/tads2/osifc.h000066400000000000000000007402641265017072300151460ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/osifc.h,v 1.3 1999/07/11 00:46:34 MJRoberts Exp $ */ /* * Copyright (c) 1997, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name osifc.h - portable interfaces to OS-specific functions Function This file defines interfaces to certain functions that must be called from portable code, but which must have system-specific implementations. Notes DO NOT MODIFY THIS FILE WITH PLATFORM-SPECIFIC DEFINITIONS. If you wish to add definitions for your platform, add them to the osxxx.h file specific to your platform. Note that your osxxx.h file is always included *before* this file, so items in your osxxx.h file are already defined by the time this file is seen by the compiler. To port TADS to a new platform, you should go through this entire file and provide a definition for each documented macro, typedef, and function, and you should provide an implementation for each function prototype. Each prototype provides a portable interface, so it is the same on all platforms, but each platform requires its own custom implementation. Put your definitions in your osxxx.h header file; put your function implementations in your osxxx.c file or files. You should not change any macro or typedef that is actually #define'd in this file. Those definitions are part of the portable interface, not part of the platform-specific implementation. Certain functions are merely documented here, but no prototypes are provided. For these functions, most platforms use #define macros to implement the functions in terms of standard C library functions or OS API functions; we do not provide prototypes for these functions so that the OS implementation can call the C library or OS API functions directly through a macro, avoiding an unnecessary extra function call. However, if you must provide an implementation for these functions, you can provide your own prototypes for them in your osxxx.h header file. SEE ALSO osifctyp.h, which contains definitions for some of the interface datatypes. Modified 04/04/99 CNebel - Improve const-ness; use new appctx.h header. 12/05/97 MJRoberts - Creation */ #ifndef OSIFC_H #define OSIFC_H #include #include #include "appctx.h" #ifdef __cplusplus extern "C" { #endif /* ------------------------------------------------------------------------ */ /* * A note on character sets: * * Except where noted, all character strings passed to and from the * osxxx functions defined herein use the local operating system * representation. On a Windows machine localized to Eastern Europe, * for example, the character strings passed to and from the osxxx * functions would use single-byte characters in the Windows code page * 1250 representation. * * Callers that use multiple character sets must implement mappings to * and from the local character set when calling the osxxx functions. * The osxxx implementations are thus free to ignore any issues related * to character set conversion or mapping. * * The osxxx implementations are specifically not permitted to use * double-byte Unicode as the native character set, nor any other * character set where a null byte could appear as part of a non-null * character. In particular, callers may assume that null-terminated * strings passed to and from the osxxx functions contain no embedded * null bytes. Multi-byte character sets (i.e., character sets with * mixed single-byte and double-byte characters) may be used as long as * a null byte is never part of any multi-byte character, since this * would guarantee that a null byte could always be taken as a null * character without knowledge of the encoding or context. */ /* ------------------------------------------------------------------------ */ /* * "Far" Pointers. Most platforms can ignore this. For platforms with * mixed-mode addressing models, where pointers of different sizes can * be used within a single program and hence some pointers require * qualification to indicate that they use a non-default addressing * model, the keyword OSFAR should be defined to the appropriate * compiler-specific extension keyword. * * If you don't know what I'm talking about here, you should just ignore * it, because your platform probably doesn't have anything this * sinister. As of this writing, this applies only to MS-DOS, and then * only to 16-bit implementations that must interact with other 16-bit * programs via dynamic linking or other mechanisms. */ /* ------------------------------------------------------------------------ */ /* * ANSI C99 exact-size integer types. * * C99 defines a set of integer types with exact bit sizes, named intXX_t * for a signed integer with XX bits, and uintXX_t for unsigned. XX can be * 8, 16, 32, or 64. TADS uses the 16- and 32-bit sizes, so each platform * is responsible for defining the following types: * *. int16_t - a signed integer type storing EXACTLY 16 bits *. uint16_t - an unsigned integer type storing EXACTLY 16 bits *. int32_t - a signed integer type storing EXACTLY 32 bits *. uint32_t - an unsigned integer type storing EXACTLY 32 bits * * Many modern compilers provide definitions for these types via the * standard header stdint.h. Where stdint.h is provided, the platform code * can merely #include . * * For compilers where stdint.h isn't available, you must provide suitable * typedefs. Note that the types must be defined with the exact bit sizes * specified; it's not sufficient to use a bigger type, because we depend * in some cases on overflow and sign extension behavior at the specific * bit size. */ /* ------------------------------------------------------------------------ */ /* * Thread-local storage (TLS). * * When TADS is compiled with threading support, it requires some variables * to be "thread-local". This means that the variables have global scope * (so they're not stored in "auto" variables on the stack), but each * thread has a private copy of each such variable. * * Nearly all systems that support threads also support thread-local * storage. Like threading support itself, though, TLS support is at * present implemented only in non-portable OS APIs rather than standard C * language features. TLS is a requirement if TADS is compiled with * threading, but it's not needed for non-threaded builds. TADS only * requires threading at present (version 3.1) for its network features; * since these features are optional, systems that don't have threading and * TLS support will simply need to disable the network features, which will * allow all of the threading and TLS definitions in osifc to be omitted. * * There appear to be two common styles of TLS programming models. The * first provides non-standard compiler syntax for declarative creation of * thread-local variables. The Microsoft (on Windows) and Gnu compilers * (on Linux and Unix) do this: they provide custom storage class modifiers * for declaring thread locals (__declspec(thread) for MSVC, __thread for * gcc). Compilers that support declarative thread locals handle the * implementation details through code generation, so the program merely * needs to add the special TLS storage class qualifier to an otherwise * ordinary global variable declaration, and then can access the thread * local as though it were an ordinary global. * * The second programming model is via explicit OS API calls to create, * initialize, and access thread locals. pthreads provides such an API, as * does Win32. In fact, when you use the declarative syntax with MSVC or * gcc, the compiler generates the appropriate API calls, but the details * are transparent to the program; in contrast, when using pthreads * directly, the program must actively call the relevant APIs. * * It's probably the case that every system that has compiler-level support * for declarative thread local creation also has procedural APIs, so the * simplest way to abstract the platform differences would be to do * everything in terms of APIs. However, it seems likely that compilers * with declarative syntax might be able to generate more efficient code, * since optimizers always benefit from declarative information. So we'd * like to use declarative syntax whenever it's available, but fall back on * explicit API calls when it's not. So our programming model is a union * of the two styles: * * 1. For each thread local, declare the thread local: *. OS_DECL_TLS(char *, my_local); * * 2. At main program startup (for the main thread only), initialize each * thread local: *. os_tls_create(my_local); * * 3. Never get or set the value of a thread local directly; instead, use * the get/set functions: *. char *x = os_tls_get(char *, my_local); *. os_tls_set(my_local, "hello"); * * One key feature of this implementation is that each thread local is * stored as a (void *) value. We do it this way to allow a simple direct * mapping to the pthreads APIs, since that's going to be the most common * non-declarative implementation. This means that a thread local variable * can contain any pointer type, but *only* a pointer type. The standard * pattern for dealing with anything more ocmplex is the same as in * pthreads: gather up the data into a structure, malloc() an instance of * that structure at entry to each thread (including the main thread), and * os_tls_set() the variable to contain a pointer to that structure. From * then on, use os_tls_set(my_struct *, my_local)->member to access the * member variables in the structure. And finally, each thread must delete * the structure at thread exit. */ /* * * Declare a thread local. * * - For compilers that support declarative TLS variables, the local OS * headers should use the compiler support by #defining OS_DECL_TLS to the * appropriate local declarative keyword. * * - For systems without declarative TLS support but with TLS APIs, the * global declared by this macro actually stores the slot ID (what pthreads * calls the "key") for the variable. This macro should therefore expand * to a declaration of the appropriate API type for a slot ID; for example, * on pthreads, #define OS_DECL_TLS(t, v) pthread_key_t v. * * - For builds with no thread support, simply #define this to declare the * variable as an ordinary global: #define OS_DECL_TLS(t, v) t v. */ /* #define OS_DECL_TLS(typ, varname) __thread typ varname */ /* * For API-based systems without declarative support in the compiler, the * main program startup code must explicitly create a slot for each thread- * local variable by calling os_tls_create(). The API returns a slot ID, * which is shared among threads and therefore can be stored in an ordinary * global variable. OS_DECL_TLS will have declared the global variable * name in this case as an ordinary global of the slot ID type. The * os_tls_create() macro should therefore expand to a call to the slot * creation API, storing the new slot ID in the global. * * Correspondingly, before the main thread exits, it should delete each * slot it created, b calling os_tls_delete(). * * For declarative systems, there's no action required here, so these * macros can be defined to empty. */ /* #define os_tls_create(varname) pthread_key_create(&varname, NULL) */ /* #define os_tls_delete(varname) pthread_key_delete(varname) */ /* * On API-based systems, each access to get or set the thread local * requires an API call, using the slot ID stored in the actual global to * get the per-thread instance of the variable's storage. *. #define os_tls_get(typ, varname) ((typ)pthread_getspecific(varname)) *. #define os_tls_set(varname, val) pthread_setspecific(varname, val) * * On declarative systems, the global variable itself is the thread local, * so get/set can be implemented as direct access to the variable. *. #define os_tls_get(typ, varname) varname *. #define os_tls_set(varname, val) varname = (val) */ /* * Common TLS definitions - declarative thread locals * * For systems with declarative TLS support in the compiler, the OS header * can #define OS_DECLARATIVE_TLS to pick up suitable definitions for the * os_tls_xxx() macros. The OS header must separately define OS_DECL_TLS * as appropriate for the local system. */ #ifdef OS_DECLARATIVE_TLS #define os_tls_create(varname) #define os_tls_delete(varname) #define os_tls_get(typ, varname) varname #define os_tls_set(varname, val) varname = (val) #endif /* * Common TLS definitions - pthreads * * For pthreads systems without declarative TLS support in the compiler, * the OS header can simply #define OS_PTHREAD_TLS to pick up the standard * definitions below. */ #ifdef OS_PTHREAD_TLS #include #define OS_DECL_TLS(typ, varname) pthread_key_t varname #define os_tls_create(varname) pthread_key_create(&varname, NULL) #define os_tls_delete(varname) pthread_key_delete(varname) #define os_tls_get(typ, varname) ((typ)pthread_getspecific(varname)) #define os_tls_set(varname, val) pthread_setspecific(varname, val) #endif /* ------------------------------------------------------------------------ */ /* * definitions. * * os_time() should act like Unix time(), returning the number of seconds * elapsed since January 1, 1970 at midnight UTC. * * The original Unix package defined time_t as a 32-bit signed * int, and many subsequent C compilers on other platforms followed suit. * A signed 32-bit time_t has the well-known year-2038 problem; some later * C compilers tried to improve matters by using an unsigned 32-bit time_t * instead, but for many purposes this is even worse since it can't * represent any date before 1/1/1970. *Most* modern compilers solve the * problem once and for all (for 300 billion years in either direction of * 1/1/1970, anyway - enough to represent literally all of eternity in most * current cosmological models) by defining time_t as a signed 64-bit int. * But some compilers stubbornly stick to the old 32-bit time_t even in * newer versions, for the sake of compatibility with older code that might * be lax about mixing time_t's with ordinary int's. E.g., MSVC2003 does * this. Fortunately, some of these compilers (such as MSVC2003 again) * also define a parallel, transitional set of 64-bit time functions that * you can use by replacing all references to the standard time_t and * related names with the corresponding 64-bit names. * * We'd really like to use a 64-bit time_t wherever we can - the TADS * release cycle can be a bit slow, and we don't want 2038 to sneak up on * us and catch us unawares. So for those compilers that offer a choice of * 32 or 64 bits, we'd like to select the 64 bit version. To facilitate * this, we define covers here for the time.h types and functions that we * use. On platforms where the regular time_t is already 64 bits, or where * there's no 64-bit option at all, you can simply do nothing - the * defaults defined here use the standard time_t typedef and functions, so * that's what you'll get if you don't define these in the OS-specific * headers for your platform. For compilers that provide both a 32-bit * time_t and a 64-bit other_time_t, the OS headers should #define these * macros in terms of those compiler-specific 64-bit names. */ #ifndef os_time_t # define os_time_t time_t # define os_gmtime(t) gmtime(t) # define os_localtime(t) localtime(t) # define os_time(t) time(t) #endif /* * Initialize the time zone. This routine is meant to take care of any * work that needs to be done prior to calling localtime() and other * time-zone-dependent routines in the run-time library. For DOS and * Windows, we need to call the run-time library routine tzset() to set up * the time zone from the environment; most systems shouldn't need to do * anything in this routine. It's sufficient to call this once during the * process lifetime, since it's meant to perform static initialization that * lasts as long as the process is running. */ #ifndef os_tzset void os_tzset(void); #endif /* * Higher-precision time. This retrieves the same time information as * os_time() (i.e., the elapsed time since the standard Unix Epoch, January * 1, 1970 at midnight UTC), but retrieves it with the highest precision * available on the local system, up to nanosecond precision. If less * precision is available, that's fine; just return the time to the best * precision available, but expressed in terms of the number of * nanoseconds. For example, if you can retrieve milliseconds, you can * convert that to nanoseconds by multiplying by 1,000,000. * * On return, fills in '*seconds' with the number of whole seconds since * the Epoch, and fills in '*nanoseconds' with the fractional portion, * expressed in nanosceconds. Note that '*nanoseconds' is merely the * fractional portion of the time, so 0 <= *nanoseconds < 1000000000. */ void os_time_ns(os_time_t *seconds, long *nanoseconds); /* * Get the local time zone name, as a location name in the IANA zoneinfo * database. For example, locations using US Pacific Time should return * "America/Los_Angeles". * * Returns true if successful, false if not. If the local operating system * doesn't have a way to obtain this information, or if it's not available * in the local machine's configuration, this returns false. * * The zoneinfo database is also known as the Olson or TZ (timezone) * database; it's widely used on Unix systems as the definitive source of * local time zone settings. See http://www.iana.org/time-zones for more * information. * * On many Unix systems, the TZ environment variable contains the zoneinfo * zone name when its first character is ':'. Windows uses a proprietary * list of time zone names that can be mapped to zoneinfo names via a * hand-coded list (such a list is maintained in the Unicode CLDR; our * Windows implementation uses the CLDR list to generate the mapping). * MacOS X uses zoneinfo keys directly; /etc/localtime is a link to the * zoneinfo file for the local zone as set via the system preferences. * * os_tzset() must be invoked at some point before this routine is called. */ int os_get_zoneinfo_key(char *buf, size_t buflen); /* * Get a description of the local time zone. Fills in '*info' with the * available information. Returns true on success, false on failure. * * See osstzprs.h/.c for a portable implementation of a parser for * POSIX-style TZ strings. That can serve as a full implementation of this * function for systems that use the POSIX TZ environment variable syntax * to specify the timezone. (That routine simply parses a string from any * source, so it can be used to parse the TZ syntax even on systems where * the string comes from somewhere other than the TZ environment variable.) * * os_tzset() must be invoked at some point before this routine is called. * * The following two structures are used for the return information: * * os_tzrule_t - Timezone Rule structure. This describes a rule for an * annual transition between daylight savings time and standard time in a * time zone. Most timezones that have recurring standard/daylight changes * require two of these rules, one for switching to daylight time in the * spring and one for switching to standard time in the fall. * * os_tzinfo_t - Timezone Information structure. This describes a * timezone's clock settings, name(s), and rules for recurring annual * changes between standard time and daylight time, if applicable. */ struct os_tzrule_t { /* * Day of year, 1-365, NEVER counting Feb 29; set to 0 if not used. * Corresponds to the "J" format in Unix TZ strings. (Called "Julian * day" in the POSIX docs, thus the "J", even though it's a bit of a * misnomer.)(Because of the invariance of the mapping from J-number to * date, this is just an obtuse way of specifying a month/day date. * But even so, we'll let the OS layer relay this back to us in * J-number format and count on the portable caller to work out the * date, rather than foisting that work on each platform * implementation.) */ int jday; /* * Day of year, 1-366, counting Feb 29 on leap years; set to 0 if not * used; ignored if 'jday' is nonzero. This corresponds to the Julian * day sans "J" in TZ strings (almost - that TZ format uses 0-365 as * its range, so bump it up by one when parsing a TZ string). This * format is even more obtuse than the J-day format, in that it doesn't * even have an invariant month/day mapping (not after day 59, anyway - * day 60 is either February 29 or March 1, depending on the leapness * of the year, and every day after that is similarly conditional). As * far as I can tell, no one uses this option, so I'm not sure why it * exists. The zoneinfo source format doesn't have a way to represent * it, which says to me that no one has ever used it in a statutory DST * start/end date definition in the whole history of time zones around * the world, since the whole history of time zones around the world is * exactly what the zoneinfo database captures in exhaustive and * painstaking detail. If anyone had ever used it in defining a time * zone, zoneinfo would have an option for it. My guess is that it's a * fossilized bug from some early C RTL that's been retained out of an * abundance of caution vis-a-vis compatibility, and was entirely * replaced in practice by the J-number format as soon as someone * noticed the fiddly leap year behavior. But for the sake of * completeness... */ int yday; /* * The month (1-12), week of the month, and day of the week (1-7 for * Sunday to Saturday). Week 1 is the first week in which 'day' * occurs, week 2 is the second, etc.; week 5 is the last occurrence of * 'day' in the month. These fields are used for "second Sunday in * March" types of rules. Set these to zero if they're not used; * they're ignored in any case if 'jday' or 'yday' are non-zero. */ int month; int week; int day; /* time of day, in seconds after midnight (e.g., 2AM is 120 == 2*60*60) */ int time; }; struct os_tzinfo_t { /* * The local offset from GMT, in seconds, for standard time and * daylight time in this zone. These values are positive for zones * east of GMT and negative for zones west: New York standard time * (EST) is 5 hours west of GMT, so its offset is -5*60*60. * * Set both of these fields (if possible) regardless of whether * standard or daylight time is currently in effect in the zone. The * caller will select which offset to use based on the start/end rules, * or based on the 'is_dst' flag if no rules are available. * * If it's only possible to determine the current wall clock offset, be * it standard or daylight time, and it's not possible to determine the * time difference between the two, simply set both of these to the * current offset. This information isn't available from the standard * C library, and many OS APIs also lack it. */ int32_t std_ofs; int32_t dst_ofs; /* * The abbreviations for the local zone's standard time and daylight * time, respectively, when displaying date/time values. E.g., "EST" * and "EDT" for US Eastern Time. If the zone doesn't observe daylight * time (it's on standard time year round), set dst_abbr to an empty * string. * * As with std_ofs and dst_ofs, you can set both of these to the same * string if it's only possible to determine the one that's currently * in effect. */ char std_abbr[16]; char dst_abbr[16]; /* * The ongoing rules for switching between daylight and standard time * in this zone, if available. 'dst_start' is the date when daylight * savings starts, 'dst_end' is the date when standard time resumes. * Set all fields to 0 if the start/stop dates aren't available, or the * zone is on standard time year round. */ struct os_tzrule_t dst_start; struct os_tzrule_t dst_end; /* * True -> the zone is CURRENTLY on daylight savings time; false means * it's currently on standard time. * * This is only used if the start/end rules aren't specified. In the * absence of start/end rules, there's no way to know when the current * standard/daylight phase ends, so we'll have to assume that the * current mode is in effect permanently. In this case, the caller * will use only be able to use the offset and abbreviation for the * current mode and will have to ignore the other one. */ int is_dst; }; int os_get_timezone_info(struct os_tzinfo_t *info); /* * Get the current system high-precision timer. This function returns a * value giving the wall-clock ("real") time in milliseconds, relative to * any arbitrary zero point. It doesn't matter what this value is relative * to -- the only important thing is that the values returned by two * different calls should differ by the number of actual milliseconds that * have elapsed between the two calls. This might be the number of * milliseconds since the computer was booted, since the current user * logged in, since midnight of the previous night, since the program * started running, since 1-1-1970, etc - it doesn't matter what the epoch * is, so the implementation can use whatever's convenient on the local * system. * * True millisecond precision isn't required. Each implementation should * simply use the best precision available on the system. If your system * doesn't have any kind of high-precision clock, you can simply use the * time() function and multiply the result by 1000 (but see the note below * about exceeding 32-bit precision). * * However, it *is* required that the return value be in *units* of * milliseconds, even if your system clock doesn't have that much * precision; so on a system that uses its own internal clock units, this * routine must multiply the clock units by the appropriate factor to yield * milliseconds for the return value. * * It is also required that the values returned by this function be * monotonically increasing. In other words, each subsequent call must * return a value that is equal to or greater than the value returned from * the last call. On some systems, you must be careful of two special * situations. * * First, the system clock may "roll over" to zero at some point; for * example, on some systems, the internal clock is reset to zero at * midnight every night. If this happens, you should make sure that you * apply a bias after a roll-over to make sure that the value returned from * this return continues to increase despite the reset of the system clock. * * Second, a 32-bit signed number can only hold about twenty-three days * worth of milliseconds. While it seems unlikely that a TADS game would * run for 23 days without a break, it's certainly reasonable to expect * that the computer itself may run this long without being rebooted. So, * if your system uses some large type (a 64-bit number, for example) for * its high-precision timer, you may want to store a zero point the very * first time this function is called, and then always subtract this zero * point from the large value returned by the system clock. If you're * using time(0)*1000, you should use this technique, since the result of * time(0)*1000 will almost certainly not fit in 32 bits in most cases. */ long os_get_sys_clock_ms(void); /* ------------------------------------------------------------------------ */ /* * Hardware Configuration. Define the following functions appropriately * for your hardware. For efficiency, these functions should be defined * as macros if possible. * * Note that these hardware definitions are independent of the OS, at * least to the extent that your OS can run on multiple types of * hardware. So, rather than combining these definitions into your * osxxx.h header file, we recommend that you put these definitions in a * separate h_yyy.h header file, which can be configured into os.h with * an appropriate "_M_yyy" preprocessor symbol. Refer to os.h for * details of configuring the hardware include file. */ /* * Round a size up to worst-case alignment boundary. For example, on a * platform where the largest type must be aligned on a 4-byte boundary, * this should round the value up to the next higher mutliple of 4 and * return the result. */ /* size_t osrndsz(size_t siz); */ /* * Round a pointer up to worst-case alignment boundary. */ /* void *osrndpt(void *ptr); */ /* * Read an unaligned portable unsigned 2-byte value, returning an int * value. The portable representation has the least significant byte * first, so the value 0x1234 is represented as the byte 0x34, followed * by the byte 0x12. * * The source value must be treated as unsigned, but the result is * signed. This is significant on 32- and 64-bit platforms, because it * means that the source value should never be sign-extended to 32-bits. * For example, if the source value is 0xffff, the result is 65535, not * -1. */ /* int osrp2(unsigned char *p); */ /* * Read an unaligned portable signed 2-byte value, returning int. This * differs from osrp2() in that this function treats the source value as * signed, and returns a signed result; hence, on 32- and 64-bit * platforms, the result must be sign-extended to the int size. For * example, if the source value is 0xffff, the result is -1. */ /* int osrp2s(unsigned char *p); */ /* * Write unsigned int to unaligned portable 2-byte value. The portable * representation stores the low-order byte first in memory, so * oswp2(0x1234) should result in storing a byte value 0x34 in the first * byte, and 0x12 in the second byte. */ /* void oswp2(unsigned char *p, unsigned int i); */ /* * Write signed int to unaligned portable 2-byte value. Negative values * must be stored in two's complement notation. E.g., -1 is stored as * FF.FF, -32768 is stored as 00.80 (little-endian). * * Virtually all modern hardware uses two's complement notation as the * native representation, which makes this routine a trivial synonym of * osrp2() (i.e., #define oswp2s(p,i) oswp2(p,i)). We distinguish the * signed version on the extremely off chance that TADS is ever ported to * wacky hardware with a different representation for negative integers * (one's complement, sign bit, etc). */ /* void oswp2s(unsigned char *p, int i); */ /* * Read an unaligned unsigned portable 4-byte value, returning long. The * underlying value should be considered signed, and the result is signed. * The portable representation stores the bytes starting with the least * significant: the value 0x12345678 is stored with 0x78 in the first byte, * 0x56 in the second byte, 0x34 in the third byte, and 0x12 in the fourth * byte. */ /* unsigned long osrp4(unsigned char *p); */ /* * Read an unaligned signed portable 4-byte value, returning long. */ /* long osrp4s(unsigned char *p); */ /* * Write an unsigned long to an unaligned portable 4-byte value. The * portable representation stores the low-order byte first in memory, so * 0x12345678 is written to memory as 0x78, 0x56, 0x34, 0x12. */ /* void oswp4(unsigned char *p, unsigned long l); */ /* * Write a signed long, using little-endian byte order and two's complement * notation for negative numbers. This is a trivial synonym for oswp4() * for all platforms with native two's complement arithmetic (which is * virtually all modern platforms). See oswp2s() for more discussion. */ /* void oswp4s(unsigned char *p, long l); */ /* * For convenience and readability, the 1-byte integer (signed and * unsigned) equivalents of the above. */ #define osrp1(p) (*(unsigned char *)(p)) #define osrp1s(p) (*(signed char *)(p)) #define oswp1(p, b) (*(unsigned char *)(p) = (b)) #define oswp1s(p, b) (*(signed char *)(p) = (b)) /* ------------------------------------------------------------------------ */ /* * varargs va_copy() extension. * * On some compilers, va_list is a reference type. This means that if a * va_list value is passed to a function that uses va_arg() to step through * the referenced arguments, the caller's copy of the va_list might be * updated on return. This is problematic in cases where the caller needs * to use the va_list again in another function call, since the va_list is * no longer pointing to the first argument for the second call. C99 has a * solution in the form of the va_copy() macro. Unfortunately, this isn't * typically available in pre-C99 compilers, and isn't standard in *any* * C++ version. We thus virtualize it here in a macro. * * os_va_copy() has identical semantics to C99 va_copy(). A matching call * to os_va_copy_end() must be made for each call to os_va_copy() before * the calling function returns; this has identical semantics to C99 * va_end(). * * Because our semantics are identical to the C99 version, we provide a * default definition here for compilers that define va_copy(). Platform * headers must provide suitable definitions only if their compilers don't * have va_copy(). We also provide a definition for GCC compilers that * define the private __va_copy macro, which also has the same semantics. */ #ifdef va_copy # define os_va_copy(dst, src) va_copy(dst, src) # define os_va_copy_end(dst) va_end(dst) #else # if defined(__GNUC__) && defined(__va_copy) # define os_va_copy(dst, src) __va_copy(dst, src) # define os_va_copy_end(dst) va_end(dst) # endif #endif /* ------------------------------------------------------------------------ */ /* * Platform Identifiers. You must define the following macros in your * osxxx.h header file: * * OS_SYSTEM_NAME - a string giving the system identifier. This string * must contain only characters that are valid in a TADS identifier: * letters, numbers, and underscores; and must start with a letter or * underscore. For example, on MS-DOS, this string is "MSDOS". * * OS_SYSTEM_LDESC - a string giving the system descriptive name. This * is used in messages displayed to the user. For example, on MS-DOS, * this string is "MS-DOS". */ /* ------------------------------------------------------------------------ */ /* * Message Linking Configuration. You should #define ERR_LINK_MESSAGES * in your osxxx.h header file if you want error messages linked into * the application. Leave this symbol undefined if you want an external * message file. */ /* ------------------------------------------------------------------------ */ /* * Program Exit Codes. These values are used for the argument to exit() * to conform to local conventions. Define the following values in your * OS-specific header: * * OSEXSUCC - successful completion. Usually defined to 0. *. OSEXFAIL - failure. Usually defined to 1. */ /* ------------------------------------------------------------------------ */ /* * Basic memory management interface. These functions are merely * documented here, but no prototypes are defined, because most * platforms #define macros for these functions and types, mapping them * to malloc or other system interfaces. */ /* * Theoretical maximum osmalloc() size. This may be less than the * capacity of the argument to osmalloc() on some systems. For example, * on segmented architectures (such as 16-bit x86), memory is divided into * segments, so a single memory allocation can allocate only a subset of * the total addressable memory in the system. This value thus specifies * the maximum amount of memory that can be allocated in one chunk. * * Note that this is an architectural maximum for the hardware and * operating system. It doesn't have anything to do with the total amount * of memory actually available at run-time. * * #define OSMALMAX to a constant long value with theoretical maximum * osmalloc() argument value. For a platform with a flat (unsegmented) * 32-bit memory space, this is usually 0xffffffff; for 16-bit platforms, * this is usually 0xffff. */ /* #define OSMALMAX 0xffffffff */ /* * Allocate a block of memory of the given size in bytes. The actual * allocation may be larger, but may be no smaller. The block returned * should be worst-case aligned (i.e., suitably aligned for any type). * Return null if the given amount of memory is not available. */ /* void *osmalloc(size_t siz); */ /* * Free memory previously allocated with osmalloc(). */ /* void osfree(void *block); */ /* * Reallocate memory previously allocated with osmalloc() or * osrealloc(), changing the block's size to the given number of bytes. * If necessary, a new block at a different address can be allocated, in * which case the data from the original block is copied (the lesser of * the old block size and the new size is copied) to the new block, and * the original block is freed. If the new size is less than the old * size, this need not do anything at all, since the returned block can * be larger than the new requested size. If the block cannot be * enlarged to the requested size, return null. */ /* void *osrealloc(void *block, size_t siz); */ /* ------------------------------------------------------------------------ */ /* * Basic file I/O interface. These functions are merely documented here, * but no prototypes are defined, because most platforms #define macros for * these functions and types, mapping them to stdio or other system I/O * interfaces. * * When writing a file, writes might or might not be buffered in * application memory; this is up to the OS implementation, which can * perform buffering according to local conventions and what's most * efficient. However, it shouldn't make any difference to the caller * whether writes are buffered or not - the OS implementation must take * care that any buffering is invisible to the app. (Porters: note that * the basic C stdio package has the proper behavior here, so you'll get * the correct semantics if you use a simple stdio implementation.) * * Write buffering might be visible to *other* apps, though. In * particular, another process might not see data written to a file (with * osfwb(), os_fprint(), etc) immediately, since the write functions might * hold the written bytes in an internal memory buffer rather than sending * them to the OS. Any internal buffers are guaranteed to be flushed to * the OS upon calling osfcls() or osfflush(). Note that it's never * *necessary* to call osfflush(), because buffered data will always be * flushed on closing the file with osfcls(). However, if you want other * apps to be able to see updates immediately, you can use osfflush() to * ensure that buffers are flushed to a file before you close it. * * You can also use osfflush() to check for buffered write errors. When * you use osfwb() or other write functions to write data, they will return * a success indication even if the data was only copied into a buffer. * This means that a write that appeared to succeed might actually fail * later, when the buffer is flushed. The only way to know for sure is to * explicitly flush buffers using osfflush(), and check the result code. * If the original write function and a subsequent osfflush() *both* return * success indications, then the write has definitely succeeded. */ /* * Define the following values in your OS header to indicate local * file/path syntax conventions: * * OSFNMAX - integer indicating maximum length of a filename * * OSPATHCHAR - character giving the normal path separator character *. OSPATHALT - string giving other path separator characters *. OSPATHURL - string giving path separator characters for URL conversions *. OSPATHSEP - directory separator for PATH-style environment variables *. OSPATHPWD - string giving the special path representing the current *. working directory; for Unix or Windows, this is "." * * OSPATHURL is a little different: this specifies the characters that * should be converted to URL-style separators when converting a path from * local notation to URL notation. This is usually the same as the union * of OSPATHCHAR and OSPATHALT, but need not be; for example, on DOS, the * colon (':') is a path separator for most purposes, but is NOT a path * character for URL conversions. */ /* * Define the type osfildef as the appropriate file handle structure for * your osfxxx functions. This type is always used as a pointer, but * the value is always obtained from an osfopxxx call, and is never * synthesized by portable code, so you can use essentially any type * here that you want. * * For platforms that use C stdio functions to implement the osfxxx * functions, osfildef can simply be defined as FILE. */ /* typedef FILE osfildef; */ /* * File types. * * These are symbols of the form OSFTxxxx defining various content types, * somewhat aking to MIME types. These were mainly designed for the old * Mac OS (versions up to OS 9), where the file system stored a type tag * with each file's metadata. The type tags were used for things like * filtering file selector dialogs and setting file-to-app associations in * the desktop shell. * * Our OSFTxxx symbols are abstract file types that we define, for types * used within the TADS family of applications. They give us a common, * cross-platform reference point for each type we use. Each port where * file types are meaningful then maps our abstract type IDs to the * corresponding port-specific type IDs. In practice, this has never been * used anywhere other than the old Mac OS ports; in fact, it's not even * used in the modern Mac OS (OS X and later), since Apple decided to stop * fighting the tide and start using filename suffixes for this sort of * tagging, like everyone else always has. * * For the list of file types, see osifctyp.h */ /* * Local newline convention. * * Because of the pernicious NIH ("Not Invented Here") cultures of the * major technology vendors, basically every platform out there has its own * unique way of expressing newlines in text files. Unix uses LF (ASCII * 10); Mac uses CR (ASCII 13); DOS and Windows use CR-LF pairs. In the * past there were heaven-only-knows how many other conventions in use, but * fortunately these three have the market pretty well locked up at this * point. But we do still have to worry about these three. * * Our strategy on input is to be open to just about anything whenever * possible. So, when we're reading something that we believe to be a text * file, we'll treat all of these as line endings: CR, LF, CR-LF, and * LF-CR. It's pretty safe to do this; if we have a CR and LF occurring * adjacently, it's almost certain that they're intended to be taken * together as a single newline sequence. Likewise, if there's a lone CR * or LF, it's rare for it to mean anything other than a newline. * * On output, though, we can't be as loose. The problem is that other * applications on our big three platforms *don't* tend to aim for the same * flexibility we do on input: other apps usually expect exactly the local * conventions on input, and don't always work well if they don't get it. * So it's important that when we're writing a text file, we write newlines * in the local convention. This means that we sometimes need to know what * the local convention actually is. That's where this definition comes * in. * * Each port must define OS_NEWLINE_SEQ as an ASCII string giving the local * newline sequence to write on output. For example, DOS defines it as * "\r\n" (CR-LF). Always define it as a STRING (not a character * constant), even if it's only one character long. * * (Note that some compilers use wacky mappings for \r and \n. Some older * Mac compilers, for example, defined \n as CR and \r as LF, because of * the Mac convention where newline is represented as CR in a text file. * If there's any such variability on your platform, you can always use the * octal codes to be unambiguous: \012 for LF and \015 for CR.) */ /* #define OS_NEWLINE_SEQ "\r\n" */ /* * Open text file for reading. This opens the file with read-only access; * we're not allowed to write to the file using this handle. Returns NULL * on error. * * A text file differs from a binary file in that some systems perform * translations to map between C conventions and local file system * conventions; for example, on DOS, the stdio library maps the DOS CR-LF * newline convention to the C-style '\n' newline format. On many systems * (Unix, for example), there is no distinction between text and binary * files. * * On systems that support file sharing and locking, this should open the * file in "shared read" mode - this means that other processes are allowed * to simultaneously read from the file, but no other processs should be * allowed to write to the file as long as we have it open. If another * process already has the file open with write access, this routine should * return failure, since we can't take away the write privileges the other * process already has and thus we can't guarantee that other processes * won't write to the file while we have it open. */ /* osfildef *osfoprt(const char *fname, os_filetype_t typ); */ /* * Open a text file for "volatile" reading: we open the file with read-only * access, and we explicitly accept instability in the file's contents due * to other processes simultaneously writing to the file. On systems that * support file sharing and locking, the file should be opened in "deny * none" mode, meaning that other processes can simultaneously open the * file for reading and/or writing even while have the file open. */ /* osfildef *osfoprtv(const char *fname, os_filetype_t typ); */ /* * Open text file for writing; returns NULL on error. If the file already * exists, this truncates the file to zero length, deleting any existing * contents. */ /* osfildef *osfopwt(const char *fname, os_filetype_t typ); */ /* * Open text file for reading and writing, keeping the file's existing * contents if the file already exists or creating a new file if no such * file exists. Returns NULL on error. */ /* osfildef *osfoprwt(const char *fname, os_filetype_t typ); */ /* * Open text file for reading/writing. If the file already exists, * truncate the existing contents to zero length. Create a new file if it * doesn't already exist. Return null on error. */ /* osfildef *osfoprwtt(const char *fname, os_filetype_t typ); */ /* * Open binary file for writing; returns NULL on error. If the file * exists, this truncates the existing contents to zero length. */ /* osfildef *osfopwb(const char *fname, os_filetype_t typ); */ /* * Open source file for reading - use the appropriate text or binary * mode. */ /* osfildef *osfoprs(const char *fname, os_filetype_t typ); */ /* * Open binary file for reading; returns NULL on error. */ /* osfildef *osfoprb(const char *fname, os_filetype_t typ); */ /* * Open binary file for 'volatile' reading; returns NULL on error. * ("Volatile" means that we'll accept writes from other processes while * reading, so the file should be opened in "deny none" mode or the * equivalent, to the extent that the local system supports file sharing * modes.) */ /* osfildef *osfoprbv(const char *fname, os_filetype_t typ); */ /* * Open binary file for random-access reading/writing. If the file already * exists, keep the existing contents; if the file doesn't already exist, * create a new empty file. * * The caller is allowed to perform any mixture of read and write * operations on the returned file handle, and can seek around in the file * to read and write at random locations. * * If the local file system supports file sharing or locking controls, this * should generally open the file in something equivalent to "exclusive * write, shared read" mode ("deny write" in DENY terms), so that other * processes can't modify the file at the same time we're modifying it (but * it doesn't bother us to have other processes reading from the file while * we're working on it, as long as they don't mind that we could change * things on the fly). It's not absolutely necessary to assert these * locking semantics, but if there's an option to do so this is preferred. * Stricter semantics (such as "exclusive" or "deny all" mode) are better * than less strict semantics. Less strict semantics are dicey, because in * that case the caller has no way of knowing that another process could be * modifying the file at the same time, and no way (through osifc) of * coordinating that activity. If less strict semantics are implemented, * the caller will basically be relying on luck to avoid corruptions due to * writing by other processes. * * Return null on error. */ /* osfildef *osfoprwb(const char *fname, os_filetype_t typ); */ /* * Open binary file for random-access reading/writing. If the file already * exists, truncate the existing contents (i.e., delete the contents of the * file, resetting it to a zero-length file). Create a new file if it * doesn't already exist. The caller is allowed to perform any mixture of * read and write operations on the returned handle, and can seek around in * the file to read and write at random locations. * * The same comments regarding sharing/locking modes for osfoprwb() apply * here as well. * * Return null on error. */ /* osfildef *osfoprwtb(const char *fname, os_filetype_t typ); */ /* * Duplicate a file handle. Returns a new osfildef* handle that accesses * the same open file as an existing osfildef* handle. The new handle is * independent of the original handle, with its own seek position, * buffering, etc. The new handle and the original handle must each be * closed separately when the caller is done with them (closing one doesn't * close the other). The effect should be roughly the same as the Unix * dup() function. * * On success, returns a new, non-null osfildef* handle duplicating the * original handle. Returns null on failure. * * 'mode' is a simplified stdio fopen() mode string. The first * character(s) indicate the access type: "r" for read access, "w" for * write access, or "r+" for read/write access. Note that "w+" mode is * specifically not defined, since the fopen() handling of "w+" is to * truncate any existing file contents, which is not desirable when * duplicating a handle. The access type can optionally be followed by "t" * for text mode, "s" for source file mode, or "b" for binary mode, with * the same meanings as for the various osfop*() functions. The default is * 't' for text mode if none of these are specified. * * If the osfop*() functions are implemented in terms of stdio FILE* * objects, this can be implemented as fdopen(dup(fileno(orig)), mode), or * using equivalents if the local stdio library uses different names for * these functions. Note that "s" (source file format) isn't a stdio mode, * so implementations must translate it to the appropriate "t" or "b" mode. * (For that matter, "t" and "b" modes aren't universally supported either, * so some implementations may have to translate these, or more likely * simply remove them, as most platforms don't distinguish text and binary * modes anyway.) */ osfildef *osfdup(osfildef *orig, const char *mode); /* * Set a file's type information. This is primarily for implementations on * Mac OS 9 and earlier, where the file system keeps file-type metadata * separate from the filename. On such systems, this can be used to set * the type metadata after a file is created. The system should map the * os_filetype_t values to the actual metadata values on the local system. * On most systems, there's no such thing as file-type metadata, in which * case this function should simply be stubbed out with an empty function. */ void os_settype(const char *f, os_filetype_t typ); /* open the error message file for reading */ osfildef *oserrop(const char *arg0); /* * Get a line of text from a text file. Uses fgets semantics. */ /* char *osfgets(char *buf, size_t len, osfildef *fp); */ /* * Write a line of text to a text file. Uses fputs semantics. */ /* void osfputs(const char *buf, osfildef *fp); */ /* * Write to a text file. os_fprintz() takes a null-terminated string, * while os_fprint() takes an explicit separate length argument that might * not end with a null terminator. */ void os_fprintz(osfildef *fp, const char *str); void os_fprint(osfildef *fp, const char *str, size_t len); /* * Write bytes to file. Return 0 on success, non-zero on error. */ /* int osfwb(osfildef *fp, const void *buf, int bufl); */ /* * Flush buffered writes to a file. This ensures that any bytes written to * the file (with osfwb(), os_fprint(), etc) are actually sent out to the * operating system, rather than being buffered in application memory for * later writing. * * Note that this routine only guarantees that we write through to the * operating system. This does *not* guarantee that the data will actually * be committed to the underlying physical storage device. Such a * guarantee is hard to come by in general, since most modern systems use * multiple levels of software and hardware buffering - the OS might buffer * some data in system memory, and the physical disk drive might itself * buffer data in its own internal cache. This routine thus isn't good * enough, for example, to protect transactional data that needs to survive * a power failure or a serious system crash. What this routine *does* * ensure is that buffered data are written through to the OS; in * particular, this ensures that another process that's reading from the * same file will see all updates we've made up to this point. * * Returns 0 on success, non-zero on error. Errors can occur for any * reason that they'd occur on an ordinary write - a full disk, a hardware * failure, etc. */ /* int osfflush(osfildef *fp); */ /* * Read a character from a file. Provides the same semantics as fgetc(). */ /* int osfgetc(osfildef *fp); */ /* * Read bytes from file. Return 0 on success, non-zero on error. */ /* int osfrb(osfildef *fp, void *buf, int bufl); */ /* * Read bytes from file and return the number of bytes read. 0 * indicates that no bytes could be read. */ /* size_t osfrbc(osfildef *fp, void *buf, size_t bufl); */ /* * Get the current seek location in the file. The first byte of the * file has seek position 0. */ /* long osfpos(osfildef *fp); */ /* * Seek to a location in the file. The first byte of the file has seek * position 0. Returns zero on success, non-zero on error. * * The following constants must be defined in your OS-specific header; * these values are used for the "mode" parameter to indicate where to * seek in the file: * * OSFSK_SET - set position relative to the start of the file *. OSFSK_CUR - set position relative to the current file position *. OSFSK_END - set position relative to the end of the file */ /* int osfseek(osfildef *fp, long pos, int mode); */ /* * Close a file. * * If the OS implementation uses buffered writes, this routine guarantees * that any buffered data are flushed to the underlying file. So, it's not * necessary to call osfflush() before calling this routine. However, * since this function doesn't return any error indication, a caller could * use osfflush() first to check for errors on any final buffered writes. */ /* void osfcls(osfildef *fp); */ /* * Delete a file. Returns zero on success, non-zero on error. */ /* int osfdel(const char *fname); */ /* * Rename/move a file. This should apply the usual C rename() behavior. * Renames the old file to the new name, which may be in a new directory * location if supported on the local system; moves across devices, * volumes, file systems, etc may or may not be supported according to the * local system's rules. If the new file already exists, results are * undefined. Returns true on success, false on failure. */ /* int os_rename_file(const char *oldname, const char *newname); */ /* ------------------------------------------------------------------------ */ /* * File "stat()" information - mode, size, time stamps */ /* * Test access to a file - i.e., determine if the file exists. Returns * zero if the file exists, non-zero if not. (The semantics may seem * backwards, but this is consistent with the conventions used by most of * the other osfxxx calls: zero indicates success, non-zero indicates an * error. If the file exists, "accessing" it was successful, so osfacc * returns zero; if the file doesn't exist, accessing it gets an error, * hence a non-zero return code.) */ /* int osfacc(const char *fname) */ /* * Get a file's mode and attribute flags. This retrieves information on * the given file equivalent to the st_mode member of the 'struct stat' * data returned by the Unix stat() family of functions, as well as some * extra system-specific attributes. On success, fills in *mode (if mode * is non-null) with the mode information as a bitwise combination of * OSFMODE_xxx values, fills in *attr (if attr is non-null) with a * combination of OSFATTR_xxx attribute flags, and returns true; on * failure, simply returns false. Failure can occur if the file doesn't * exist, can't be accessed due to permissions, etc. * * Note that 'mode' and/or 'attr' can be null if the caller doesn't need * that information. Implementations must check these parameters for null * pointers and skip returning the corresponding information if null. * * If the file in 'fname' is a symbolic link, the behavior depends upon * 'follow_links'. If 'follow_links' is true, the function should resolve * the link reference (and if that points to another link, the function * resolves that link as well, and so on) and return information on the * object the link points to. Otherwise, the function returns information * on the link itself. This only applies for symbolic links (not for hard * links), and only if the underlying OS and file system support this * distinction; if the OS transparently resolves links and doesn't allow * retrieving information about the link itself, 'follow_links' can be * ignored. Likewise, hard links (on systems that support them) are * generally indistinguishable from regular files, so this function isn't * expected to do anything special with them. * * The '*mode' value returned is a bitwise combination of OSFMODE_xxx flag. * Many of the flags are mutually exclusive; for example, "file" and * "directory" should never be combined. It's also possible for '*mode' to * be zero for a valid file; this means that the file is of some special * type on the local system that doesn't fit any of the OSFMODE_xxx types. * (If any ports do encounter such cases, we can add OSFMODE_xxx types to * accommodate new types. The list below isn't meant to be final; it's * just what we've encountered so far on the platforms where TADS has * already been ported.) * * The OSFMODE_xxx values are left for the OS to define so that they can be * mapped directly to the OS API's equivalent constants, if desired. This * makes the routine easy to write, since you can simply set *mode directly * to the mode information the OS returns from its stat() or equivalent. * However, note that these MUST be defined as bit flags - that is, each * value must be exactly a power of 2. Windows and Unix-like systems * follow this practice, as do most "stat()" functions in C run-time * libraries, so this usually works automatically if you map these * constants to OS or C library values. However, if a port defines its own * values for these, take care that they're all powers of 2. * * Obviously, a given OS might not have all of the file types listed here. * If any OSFMODE_xxx values aren't applicable on the local OS, you can * simply define them as zero since they'll never be returned. * * Notes on attribute flags: * * OSFATTR_HIDDEN means that the file is conventionally hidden by default * in user interface views or listings, but is still fully accessible to * the user. Hidden files are also usually excluded by default from * wildcard patterns in commands ("rm *.*"). On Unix, a hidden file is one * whose name starts with "."; on Windows, it's a file with the HIDDEN bit * in its file attributes. On systems where this concept exists, the user * can still manipulate these files as normal by naming them explicitly, * and can typically make them appear in UI views or directory listings via * a preference setting or command flag (e.g., "ls -a" on Unix). The * "hidden" flag is explicitly NOT a security or permissions mechanism, and * it doesn't protect the file against intentional access by a user; it's * merely a convenience designed to reduce clutter by excluding files * maintained by the OS or by an application (such as preference files, * indices, caches, etc) from casual folder browsing, where a user is * typically only concerned with her own document files. On systems where * there's no such naming convention or attribute metadata, this flag will * never appear. * * OSFATTR_SYSTEM is similar to 'hidden', but means that the file is * specially marked as an operating system file. This is mostly a * DOS/Windows concept, where it corresponds to the SYSTEM bit in the file * attributes; this flag will probably never appear on other systems. The * distinction between 'system' and 'hidden' is somewhat murky even on * Windows; most 'system' file are also marked as 'hidden', and in * practical terms in the user interface, 'system' files are treated the * same as 'hidden'. * * OSFATTR_READ means that the file is readable by this process. * * OSFATTR_WRITE means that the file is writable by this process. */ /* int osfmode(const char *fname, int follow_links, */ /* unsigned long *mode, unsigned long *attr); */ /* file mode/type constants */ /* #define OSFMODE_FILE - regular file */ /* #define OSFMODE_DIR - directory */ /* #define OSFMODE_BLK - block-mode device */ /* #define OSFMODE_CHAR - character-mode device */ /* #define OSFMODE_PIPE - pipe/FIFO/other character-oriented IPC */ /* #define OSFMODE_SOCKET - network socket */ /* #define OSFMODE_LINK - symbolic link */ /* file attribute constants */ /* #define OSFATTR_HIDDEN - hidden file */ /* #define OSFATTR_SYSTEM - system file */ /* #define OSFATTR_READ - the file is readable by this process */ /* #define OSFATTR_WRITE - the file is writable by this process */ /* * Get stat() information. This fills in the portable os_file_stat * structure with the requested file information. Returns true on success, * false on failure (file not found, permissions error, etc). * * 'follow_links' has the same meaning as for osfmode(). */ typedef struct os_file_stat_t os_file_stat_t; int os_file_stat(const char *fname, int follow_links, os_file_stat_t *s); struct os_file_stat_t { /* * Size of the file, in bytes. For platforms lacking 64-bit types, we * split this into high and low 32-bit portions. Platforms where the * native stat() or equivalent only returns a 32-bit file size can * simply set sizehi to zero, since sizelo can hold the entire size * value. */ uint32_t sizelo; uint32_t sizehi; /* * Creation time, modification time, and last access time. If the file * system doesn't keep information on one or more of these, use * (os_time_t)0 to indicate that the timestamp isn't available. It's * fine to return any subset of these. Per the standard C stat(), * these should be expressed as seconds after the Unix Epoch. */ os_time_t cre_time; os_time_t mod_time; os_time_t acc_time; /* file mode, using the same flags as returned from osfmode() */ unsigned long mode; /* file attributes, using the same flags as returned from osfmode() */ unsigned long attrs; }; /* * Manually resolve a symbolic link. If the local OS and file system * support symbolic links, and the given filename is a symbolic link (in * which case osfmode(fname, FALSE, &m, &a) will set OSFMODE_LINK in the * mode bits), this fills in 'target' with the name of the link target * (i.e., the object that the link in 'fname' points to). This should * return a fully qualified file system path. Returns true on success, * false on failure. * * This should only resolve a single level of indirection. If the link * target of 'fname' is itself a link to a second target, this should only * resolve the single reference from 'fname' to its direct direct. Callers * that wish to resolve the final target of a chain of link references must * iterate until the returned path doesn't refer to a link. */ int os_resolve_symlink(const char *fname, char *target, size_t target_size); /* ------------------------------------------------------------------------ */ /* * Get a list of root directories. If 'buf' is non-null, fills in 'buf' * with a list of strings giving the root directories for the local, * file-oriented devices on the system. The strings are each null * terminated and are arranged consecutively in the buffer, with an extra * null terminator after the last string to mark the end of the list. * * The return value is the length of the buffer required to hold the * results. If the caller's buffer is null or is too short, the routine * should return the full length required, and leaves the contents of the * buffer undefined; the caller shouldn't expect any contents to be filled * in if the return value is greater than buflen. Both 'buflen' and the * return value include the null terminators, including the extra null * terminator at the end of the list. If an error occurs, or the system * has no concept of a root directory, returns zero. * * Each result string should be expressed using the syntax for the root * directory on a device. For example, on Windows, "C:\" represents the * root directory on the C: drive. * * "Local" means a device is mounted locally, as opposed to being merely * visible on the network via some remote node syntax; e.g., on Windows * this wouldn't include any UNC-style \\SERVER\SHARE names, and on VMS it * excludes any SERVER:: nodes. It's up to each system how to treat * virtual local devices, i.e., those that look synctactically like local * devices but are actually mounted network devices, such as Windows mapped * network drives; we recommend including them if it would take extra work * to filter them out, and excluding them if it would take extra work to * include them. "File-oriented" means that the returned devices are * accessed via file systems, not as character devices or raw block * devices; so this would exclude /dev/xxx devices on Unix and things like * CON: and LPT1: on Windows. * * Examples ("." represents a null byte): * * Windows: C:\.D:\.E:\.. * * Unix example: /.. */ size_t os_get_root_dirs(char *buf, size_t buflen); /* ------------------------------------------------------------------------ */ /* * Open a directory. This begins an enumeration of a directory's contents. * 'dirname' is a relative or absolute path to a directory. On success, * returns true, and 'handle' is set to a port-defined handle value that's * used in subsequent calls to os_read_dir() and os_close_dir(). Returns * false on failure. * * If the routine succeeds, the caller must eventually call os_close_dir() * to release the resources associated with the handle. */ /* typedef osdirhdl_t; */ int os_open_dir(const char *dirname, /*OUT*/osdirhdl_t *handle); /* * Read the next file in a directory. 'handle' is a handle value obtained * from a call to os_open_dir(). On success, returns true and fills in * 'fname' with the next filename; the handle is also internally updated so * that the next call to this function will retrieve the next file, and so * on until all files have been retrieved. If an error occurs, or there * are no more files in the directory, returns false. * * The filename returned is the root filename only, without the path. The * caller can build the full path by calling os_build_full_path() or * os_combine_paths() with the original directory name and the returned * filename as parameters. * * This routine lists all objects in the directory that are visible to the * corresponding native API, and is non-recursive. The listing should thus * include subdirectory objects, but not the contents of subdirectories. * Implementations are encouraged to simply return all objects returned * from the corresponding native directory scan API; there's no need to do * any filtering, except perhaps in cases where it's difficult or * impossible to represent an object in terms of the osifc APIs (e.g., it * might be reasonable to exclude files without names). System relative * links, such as the Unix/DOS "." and "..", specifically should be * included in the listing. For unusual objects that don't fit into the * os_file_stat() taxonomy or that otherwise might create confusion for a * caller, err on the side of full disclosure (i.e., just return everything * unfiltered); if necessary, we can extend the os_file_stat() taxonomy or * add new osifc APIs to create a portable abstraction to handle whatever * is unusual or potentially confusing about the native object. For * example, Unix implementations should feel free to return symbolic link * objects, including dangling links, since we have the portable * os_resolve_symlink() that lets the caller examine the meaning of the * link object. */ int os_read_dir(osdirhdl_t handle, char *fname, size_t fname_size); /* * Close a directory handle. This releases the resources associated with a * directory search started with os_open_dir(). Every successful call to * os_open_dir() must have a matching call to os_close_dir(). As usual for * open/close protocols, the handle is invalid after calling this function, * so no more calls to os_read_dir() may be made with the handle. */ void os_close_dir(osdirhdl_t handle); /* ------------------------------------------------------------------------ */ /* * NB - this routine is DEPRECATED as of TADS 2.5.16/3.1.1. Callers should * use os_open_dir(), os_read_dir(), os_close_dir() instead. * * Find the first file matching a given pattern. The returned context * pointer is a pointer to whatever system-dependent context structure is * needed to continue the search with the next file, and is opaque to the * caller. The caller must pass the context pointer to the next-file * routine. The caller can optionally cancel a search by calling the * close-search routine with the context pointer. If the return value is * null, it indicates that no matching files were found. If a file was * found, outbuf will be filled in with its name, and isdir will be set to * true if the match is a directory, false if it's a file. If pattern is * null, all files in the given directory should be returned; otherwise, * pattern is a string containing '*' and '?' as wildcard characters, but * not containing any directory separators, and all files in the given * directory matching the pattern should be returned. * * Important: because this routine may allocate memory for the returned * context structure, the caller must either call os_find_next_file until * that routine returns null, or call os_find_close() to cancel the search, * to ensure that the os code has a chance to release the allocated memory. * * 'outbuf' should be set on output to the name of the matching file, * without any path information. * * 'outpathbuf' should be set on output to full path of the matching file. * If possible, 'outpathbuf' should use the same relative or absolute * notation that the search criteria used on input. For example, if dir = * "resfiles", and the file found is "MyPic.jpg", outpathbuf should be set * to "resfiles/MyPic.jpg" (or appropriate syntax for the local platform). * Similarly, if dir = "/home/tads/resfiles", outpath buf should be * "/home/tads/resfiles/MyPic.jpg". The result should always conform to * correct local conventions, which may require some amount of manipulation * of the filename; for example, on the Mac, if dir = "resfiles", the * result should be ":resfiles:MyPic.jpg" (note the added leading colon) to * conform to Macintosh relative path notation. * * Note that 'outpathbuf' may be null, in which case the caller is not * interested in the full path information. */ /* * Note the following possible ways this function may be called: * * dir = "", pattern = filename - in this case, pattern is the name of a * file or directory in the current directory. filename *might* be a * relative path specified by the user (on a command line, for example); * for instance, on Unix, it could be something like "resfiles/jpegs". * * dir = path, pattern = filname - same as above, but this time the * filename or directory pattern is relative to the given path, rather * than to the current directory. For example, we could have dir = * "/games/mygame" and pattern = "resfiles/jpegs". * * dir = path, pattern = 0 (NULL) - this should search for all files in * the given path. The path might be absolute or it might be relative. * * dir = path, pattern = "*" - this should have the same result as when * pattern = 0. * * dir = path, pattern = "*.ext" - this should search for all files in * the given path whose names end with ".ext". * * dir = path, pattern = "abc*" - this should search for all files in * the given path whose names start with "abc". * * All of these combinations are possible because callers, for * portability, must generally not manipulate filenames directly; * instead, callers obtain paths and search strings from external * sources, such as from the user, and present them to this routine with * minimal manipulation. */ void *os_find_first_file(const char *dir, char *outbuf, size_t outbufsiz, int *isdir, char *outpathbuf, size_t outpathbufsiz); /* * Implementation notes for porting os_find_first_file: * * The algorithm for this routine should go something like this: * * - If 'path' is null, create a variable real_path and initialize it * with the current directory. Otherwise, copy path to real_path. * * - If 'pattern' contains any directory separators ("/" on Unix, for * example), change real_path so that it reflects the additional leading * subdirectories in the path in 'pattern', and remove the leading path * information from 'pattern'. For example, on Unix, if real_path * starts out as "./subdir", and pattern is "resfiles/jpegs", change * real_path to "./subdir/resfiles", and change pattern to "jpegs". * Take care to add and remove path separators as needed to keep the * path strings well-formed. * * - Begin a search using appropriate OS API's for all files in * real_path. * * - Check each file found. Skip any files that don't match 'pattern', * treating "*" as a wildcard that matches any string of zero or more * characters, and "?" as a wildcard that matches any single character * (or matches nothing at the end of a string). For example: * *. "*" matches anything *. "abc?" matches "abc", "abcd", "abce", "abcf", but not "abcde" *. "abc???" matches "abc", "abcd", "abcde", "abcdef", but not "abcdefg" *. "?xyz" matches "wxyz", "axyz", but not "xyz" or "abcxyz" * * - Return the first file that matches, if any, by filling in 'outbuf' * and 'isdir' with appropriate information. Before returning, allocate * a context structure (which is entirely for your own use, and opaque * to the caller) and fill it in with the information necessary for * os_find_next_file to get the next matching file. If no file matches, * return null. */ /* * Find the next matching file, continuing a search started with * os_find_first_file(). Returns null if no more files were found, in * which case the search will have been automatically closed (i.e., * there's no need to call os_find_close() after this routine returns * null). Returns a non-null context pointer, which is to be passed to * this function again to get the next file, if a file was found. * * 'outbuf' and 'outpathbuf' are filled in with the filename (without * path) and full path (relative or absolute, as appropriate), * respectively, in the same manner as they do for os_find_first_file(). * * Implementation note: if os_find_first_file() allocated memory for the * search context, this routine must free the memory if it returs null, * because this indicates that the search is finished and the caller * need not call os_find_close(). */ void *os_find_next_file(void *ctx, char *outbuf, size_t outbufsiz, int *isdir, char *outpathbuf, size_t outpathbufsiz); /* * Cancel a search. The context pointer returned by the last call to * os_find_first_file() or os_find_next_file() is the parameter. There * is no need to call this function if find-first or find-next returned * null, since they will have automatically closed the search. * * Implementation note: if os_find_first_file() allocated memory for the * search context, this routine should release the memory. */ void os_find_close(void *ctx); /* * Special filename classification */ enum os_specfile_t { /* not a special file */ OS_SPECFILE_NONE, /* * current directory link - this is a file like the "." file on Unix * or DOS, which is a special link that simply refers to itself */ OS_SPECFILE_SELF, /* * parent directory link - this is a file like the ".." file on Unix * or DOS, which is a special link that refers to the parent * directory */ OS_SPECFILE_PARENT }; /* * Determine if the given filename refers to a special file. Returns the * appropriate enum value if so, or OS_SPECFILE_NONE if not. The given * filename must be a root name - it must not contain a path prefix. The * purpose here is to classify the results from os_find_first_file() and * os_find_next_file() to identify the special relative links, so callers * can avoid infinite recursion when traversing a directory tree. */ enum os_specfile_t os_is_special_file(const char *fname); /* ------------------------------------------------------------------------ */ /* * Convert string to all-lowercase. */ char *os_strlwr(char *s); /* ------------------------------------------------------------------------ */ /* * Character classifications for quote characters. os_squote() returns * true if its argument is any type of single-quote character; * os_dquote() returns true if its argument is any type of double-quote * character; and os_qmatch(a, b) returns true if a and b are matching * open- and close-quote characters. * * These functions allow systems with extended character codes with * weird quote characters (such as the Mac) to match the weird * characters, so that users can use the extended quotes in input. * * These are usually implemented as macros. The most common * implementation simply returns true for the standard ASCII quote * characters: * * #define os_squote(c) ((c) == '\'') *. #define os_dquote(c) ((c) == '"') *. #define os_qmatch(a, b) ((a) == (b)) * * These functions take int arguments to allow for the possibility of * Unicode input. */ /* int os_squote(int c); */ /* int os_dquote(int c); */ /* int os_qmatch(int a, int b); */ /* ------------------------------------------------------------------------ */ /* * Special file and directory locations */ /* * Get the full filename (including directory path) to the executable * file, given the argv[0] parameter passed into the main program. This * fills in the buffer with a null-terminated string that can be used in * osfoprb(), for example, to open the executable file. * * Returns non-zero on success. If it's not possible to determine the * name of the executable file, returns zero. * * Some operating systems might not provide access to the executable file * information, so non-trivial implementation of this routine is optional; * if the necessary information is not available, simply implement this to * return zero. If the information is not available, callers should offer * gracefully degraded functionality if possible. */ int os_get_exe_filename(char *buf, size_t buflen, const char *argv0); /* * Get a special directory path. Returns the selected path, in a format * suitable for use with os_build_full_path(). The main program's argv[0] * parameter is provided so that the system code can choose to make the * special paths relative to the program install directory, but this is * entirely up to the system implementation, so the argv[0] parameter can * be ignored if it is not needed. * * The 'id' parameter selects which special path is requested; this is one * of the constants defined below. If the id is not understood, there is * no way of signalling an error to the caller; this routine can fail with * an assert() in such cases, because it indicates that the OS layer code * is out of date with respect to the calling code. * * This routine can be implemented using one of the strategies below, or a * combination of these. These are merely suggestions, though, and systems * are free to ignore these and implement this routine using whatever * scheme is the best fit to local conventions. * * - Relative to argv[0]. Some systems use this approach because it keeps * all of the TADS files together in a single install directory tree, and * doesn't require any extra configuration information to find the install * directory. Since we base the path name on the executable that's * actually running, we don't need any environment variables or parameter * files or registry entries to know where to look for related files. * * - Environment variables or local equivalent. On some systems, it is * conventional to set some form of global system parameter (environment * variables on Unix, for example) for this sort of install configuration * data. In these cases, this routine can look up the appropriate * configuration variables in the system environment. * * - Hard-coded paths. Some systems have universal conventions for the * installation configuration of compiler-like tools, so the paths to our * component files can be hard-coded based on these conventions. * * - Hard-coded default paths with environment variable overrides. Let the * user set environment variables if they want, but use the standard system * paths as hard-coded defaults if the variables aren't set. This is often * the best choice; users who expect the standard system conventions won't * have to fuss with any manual settings or even be aware of them, while * users who need custom settings aren't stuck with the defaults. */ void os_get_special_path(char *buf, size_t buflen, const char *argv0, int id); /* * TADS 3 system resource path. This path is used to load system * resources, such as character mapping files and error message files. */ #define OS_GSP_T3_RES 1 /* * TADS 3 compiler - system headers. This is the #include path for the * header files included with the compiler. */ #define OS_GSP_T3_INC 2 /* * TADS 3 compiler - system library source code. This is the path to the * library source files that the compiler includes in every compilation by * default (such as _main.t). */ #define OS_GSP_T3_LIB 3 /* * TADS 3 compiler - user library path list. This is a list of directory * paths, separated by the OSPATHSEP character, that should be searched for * user library files. The TADS 3 compiler uses this as an additional set * of locations to search after the list of "-Fs" options and before the * OS_GSP_T3_LIB directory. * * This path list is intended for the user's use, so no default value is * needed. The value should be user-configurable using local conventions; * on Unix, for example, this might be handled with an environment * variable. */ #define OS_GSP_T3_USER_LIBS 4 /* * TADS 3 interpreter - application data path. This is the directory where * we should store things like option settings: data that we want to store * in a file, global to all games. Depending on local system conventions, * this can be a global shared directory for all users, or can be a * user-specific directory. */ #define OS_GSP_T3_APP_DATA 5 /* * TADS 3 interpreter - system configuration files. This is used for files * that affect all games, and generally all users on the system, so it * should be in a central location. On Windows, for example, we simply * store these files in the install directory containing the intepreter * binary. */ #define OS_GSP_T3_SYSCONFIG 6 /* * System log files. This is the directory for system-level status, debug, * and error logging files. (Note that we're NOT talking about in-game * transcript logging per the SCRIPT command. SCRIPT logs are usually sent * to files selected by the user via a save-file dialog, so these don't * need a special location.) */ #define OS_GSP_LOGFILE 7 /* * Seek to the resource file embedded in the current executable file, * given the main program's argv[0]. * * On platforms where the executable file format allows additional * information to be attached to an executable, this function can be used * to find the extra information within the executable. * * The 'typ' argument gives a resource type to find. This is an arbitrary * string that the caller uses to identify what type of object to find. * The "TGAM" type, for example, is used by convention to indicate a TADS * compiled GAM file. */ osfildef *os_exeseek(const char *argv0, const char *typ); /* ------------------------------------------------------------------------ */ /* * Load a string resource. Given a string ID number, load the string * into the given buffer. * * Returns zero on success, non-zero if an error occurs (for example, * the buffer is too small, or the requested resource isn't present). * * Whenever possible, implementations should use an operating system * mechanism for loading the string from a user-modifiable resource * store; this will make localization of these strings easier, since the * resource store can be modified without the need to recompile the * application. For example, on the Macintosh, the normal system string * resource mechanism should be used to load the string from the * application's resource fork. * * When no operating system mechanism exists, the resources can be * stored as an array of strings in a static variable; this isn't ideal, * because it makes it much more difficult to localize the application. * * Resource ID's are application-defined. For example, for TADS 2, * "res.h" defines the resource ID's. */ int os_get_str_rsc(int id, char *buf, size_t buflen); /* ------------------------------------------------------------------------ */ /* * Look for a file in the "standard locations": current directory, program * directory, PATH-like environment variables, etc. The actual standard * locations are specific to each platform; the implementation is free to * use whatever conventions are appropriate to the local system. On * systems that have something like Unix environment variables, it might be * desirable to define a TADS-specific variable (TADSPATH, for example) * that provides a list of directories to search for TADS-related files. * * On return, fill in 'buf' with the full filename of the located copy of * the file (if a copy was indeed found), in a format suitable for use with * the osfopxxx() functions; in other words, after this function returns, * the caller should be able to pass the contents of 'buf' to an osfopxxx() * function to open the located file. * * Returns true (non-zero) if a copy of the file was located, false (zero) * if the file could not be found in any of the standard locations. */ int os_locate(const char *fname, int flen, const char *arg0, char *buf, size_t bufsiz); /* ------------------------------------------------------------------------ */ /* * Create and open a temporary file. The file must be opened to allow * both reading and writing, and must be in "binary" mode rather than * "text" mode, if the system makes such a distinction. Returns null on * failure. * * If 'fname' is non-null, then this routine should create and open a file * with the given name. When 'fname' is non-null, this routine does NOT * need to store anything in 'buf'. Note that the routine shouldn't try * to put the file in a special directory or anything like that; just open * the file with the name exactly as given. * * If 'fname' is null, this routine must choose a file name and fill in * 'buf' with the chosen name; if possible, the file should be in the * conventional location for temporary files on this system, and should be * unique (i.e., it shouldn't be the same as any existing file). The * filename stored in 'buf' is opaque to the caller, and cannot be used by * the caller except to pass to osfdel_temp(). On some systems, it may * not be possible to determine the actual filename of a temporary file; * in such cases, the implementation may simply store an empty string in * the buffer. (The only way the filename would be unavailable is if the * implementation uses a system API that creates a temporary file, and * that API doesn't return the name of the created temporary file. In * such cases, we don't need the name; the only reason we need the name is * so we can pass it to osfdel_temp() later, but since the system is going * to delete the file automatically, osfdel_temp() doesn't need to do * anything and thus doesn't need the name.) * * After the caller is done with the file, it should close the file (using * osfcls() as normal), then the caller MUST call osfdel_temp() to delete * the temporary file. * * This interface is intended to take advantage of systems that have * automatic support for temporary files, while allowing implementation on * systems that don't have any special temp file support. On systems that * do have automatic delete-on-close support, this routine should use that * system-level support, because it helps ensure that temp files will be * deleted even if the caller fails to call osfdel_temp() due to a * programming error or due to a process or system crash. On systems that * don't have any automatic delete-on-close support, this routine can * simply use the same underlying system API that osfoprwbt() normally * uses (although this routine must also generate a name for the temp file * when the caller doesn't supply one). * * This routine can be implemented using ANSI library functions as * follows: if 'fname' is non-null, return fopen(fname,"w+b"); otherwise, * set buf[0] to '\0' and return tmpfile(). */ osfildef *os_create_tempfile(const char *fname, char *buf); /* * Delete a temporary file - this is used to delete a file created with * os_create_tempfile(). For most platforms, this can simply be defined * the same way as osfdel(). For platforms where the operating system or * file manager will automatically delete a file opened as a temporary * file, this routine should do nothing at all, since the system will take * care of deleting the temp file. * * Callers are REQUIRED to call this routine after closing a file opened * with os_create_tempfile(). When os_create_tempfile() is called with a * non-null 'fname' argument, the same value should be passed as 'fname' to * this function. When os_create_tempfile() is called with a null 'fname' * argument, then the buffer passed in the 'buf' argument to * os_create_tempfile() must be passed as the 'fname' argument here. In * other words, if the caller explicitly names the temporary file to be * opened in os_create_tempfile(), then that same filename must be passed * here to delete the named file; if the caller lets os_create_tempfile() * generate a filename, then the generated filename must be passed to this * routine. * * If os_create_tempfile() is implemented using ANSI library functions as * described above, then this routine can also be implemented with ANSI * library calls as follows: if 'fname' is non-null and fname[0] != '\0', * then call remove(fname); otherwise do nothing. */ int osfdel_temp(const char *fname); /* * Get the temporary file path. This should fill in the buffer with a * path prefix (suitable for strcat'ing a filename onto) for a good * directory for a temporary file, such as the swap file. */ void os_get_tmp_path(char *buf); /* * Generate a name for a temporary file. This constructs a random file * path in the system temp directory that isn't already used by an existing * file. * * On systems with long filenames, this can be implemented by selecting a * GUID-strength random name (such as 32 random hex digits) with a decent * random number generator. That's long enough that the odds of a * collision are essentially zero. On systems that only support short * filenames, the odds of a collision are non-zero, so the routine should * actually check that the chosen filename doesn't exist. * * Optionally, before returning, this routine *may* create (and close) an * empty placeholder file to "reserve" the chosen filename. This isn't * required, and on systems with long filenames it's usually not necessary * because of the negligible chance of a collision. On systems with short * filenames, a placeholder can be useful to prevent a subsequent call to * this routine, or a separate process, from using the same filename before * the caller has had a chance to use the returned name to create the * actual temp file. * * Returns true on success, false on failure. This can fail if there's no * system temporary directory defined, or the temp directory is so full of * other files that we can't find an unused filename. */ int os_gen_temp_filename(char *buf, size_t buflen); /* ------------------------------------------------------------------------ */ /* * Basic directory/folder management routines */ /* * Switch to a new working directory. * * This is meant to behave similarly to the Unix concept of a working * directory, in that it sets the base directory assumed for subsequent * file operations (e.g., the osfopxx() functions, osfdel(), etc - anything * that takes a filename or directory name as an argument). The working * directory applies to filenames specified with relative paths in the * local system notation. File operations on filenames specified with * absolute paths, of course, ignore the working directory. */ void os_set_pwd(const char *dir); /* * Switch the working directory to the directory containing the given * file. Generally, this routine should only need to parse the filename * enough to determine the part that's the directory path, then use * os_set_pwd() to switch to that directory. */ void os_set_pwd_file(const char *filename); /* * Create a directory. This creates a new directory/folder with the given * name, which may be given as a relative or absolute path. Returns true * on success, false on failure. * * If 'create_parents' is true, and the directory has mulitiple path * elements, this routine should create each enclosing parent that doesn't * already exist. For example, if the path is specified as "a/b/c", and * there exists a folder "a" in the working directory, but "a" is empty, * this should first create "b" and then create "c". If an error occurs * creating any parent, the routine should simply stop and return failure. * (Optionally, the routine may attempt to behave atomically by undoing any * parent folder creations it accomplished before failing on a nested * folder, but this isn't required. To reduce the chances of a failure * midway through the operation, the routine might want to scan the * filename before starting to ensure that it contains only valid * characters, since an invalid character is the most likely reason for a * failure part of the way through.) * * We recommend making the routine flexible in terms of the notation it * accepts; e.g., on Unix, "/dir/sub/folder" and "/dir/sub/folder/" should * be considered equivalent. */ int os_mkdir(const char *dir, int create_parents); /* * Remove a directory. Returns true on success, false on failure. * * If the directory isn't already empty, this routine fails. That is, the * routine does NOT recursively delete the contents of a non-empty * directory. It's up to the caller to delete any contents before removing * the directory, if that's the caller's intention. (Note to implementors: * most native OS APIs to remove directories fail by default if the * directory isn't empty, so it's usually safe to implement this simply by * calling the native API. However, if your system's version of this API * can remove a non-empty directory, you MUST add an extra test before * removing the directory to ensure it's empty, and return failure if it's * not. For the purposes of this test, "empty" should of course ignore any * special objects that are automatically or implicitly present in all * directories, such as the Unix "." and ".." relative links.) */ int os_rmdir(const char *dir); /* ------------------------------------------------------------------------ */ /* * Filename manipulation routines */ /* apply a default extension to a filename, if it doesn't already have one */ void os_defext(char *fname, const char *ext); /* unconditionally add an extention to a filename */ void os_addext(char *fname, const char *ext); /* remove the extension from a filename */ void os_remext(char *fname); /* * Compare two file names/paths for syntactic equivalence. Returns true if * the names are equivalent names according to the local file system's * syntax conventions, false if not. This does a syntax-only comparison of * the paths, without looking anything up in the file system. This means * that a false return doesn't guarantee that the paths don't point to the * same file. * * This routine DOES make the following equivalences: * * - if the local file system is insensitive to case, the names are * compared ignoring case * * - meaningless path separator difference are ignored: on Unix, "a/b" == * "a//b" == "a/b/"; on Windows, "a/b" == "a\\b" * * - relative links that are strictly structural or syntactic are applied; * for example, on Unix or Windows, "a/./b" == "a/b" = "a/b/c/..". This * only applies for special relative links that can be resolved without * looking anything up in the file system. * * This DOES NOT do the following: * * - it doesn't apply working directories/volums to relative paths * * - it doesn't follow symbolic links in the file system */ int os_file_names_equal(const char *a, const char *b); /* * Get a pointer to the root name portion of a filename. This is the part * of the filename after any path or directory prefix. For example, on * Unix, given the string "/home/mjr/deep.gam", this function should return * a pointer to the 'd' in "deep.gam". If the filename doesn't appear to * have a path prefix, it should simply return the argument unchanged. * * IMPORTANT: the returned pointer MUST point into the original 'buf' * string, and the contents of that buffer must NOT be modified. The * return value must point into the same buffer because there are no * allowances for the alternatives. In particular, (a) you can't return a * pointer to newly allocated memory, because callers won't free it, so * doing so would cause a memory leak; and (b) you can't return a pointer * to an internal static buffer, because callers might call this function * more than once and still rely on a value returned on an older call, * which would be invalid if a static buffer could be overwritten on each * call. For these reasons, it's required that the return value point to a * position within the original string passed in 'buf'. */ char *os_get_root_name(const char *buf); /* * Determine whether a filename specifies an absolute or relative path. * This is used to analyze filenames provided by the user (for example, * in a #include directive, or on a command line) to determine if the * filename can be considered relative or absolute. This can be used, * for example, to determine whether to search a directory path for a * file; if a given filename is absolute, a path search makes no sense. * A filename that doesn't specify an absolute path can be combined with * a path using os_build_full_path(). * * Returns true if the filename specifies an absolute path, false if * not. */ int os_is_file_absolute(const char *fname); /* * Extract the path from a filename. Fills in pathbuf with the path * portion of the filename. If the filename has no path, the pathbuf * should be set appropriately for the current directory (on Unix or DOS, * for example, it can be set to an empty string). * * The result can end with a path separator character or not, depending on * local OS conventions. Paths extracted with this function can only be * used with os_build_full_path(), so the conventions should match that * function's. * * Unix examples: * *. /home/mjr/deep.gam -> /home/mjr *. games/deep.gam -> games *. deep.gam -> [empty string] * * Mac examples: * * :home:mjr:deep.gam -> :home:mjr *. Hard Disk:games:deep.gam -> Hard Disk:games *. Hard Disk:deep.gam -> Hard Disk: *. deep.gam -> [empty string] * * VMS examples: * *. SYS$DISK:[mjr.games]deep.gam -> SYS$DISK:[mjr.games] *. SYS$DISK:[mjr.games] -> SYS$DISK:[mjr] *. deep.gam -> [empty string] * * Note in the last example that we've retained the trailing colon in the * path, whereas we didn't in the others; although the others could also * retain the trailing colon, it's required only for the last case. The * last case requires the colon because it would otherwise be impossible to * determine whether "Hard Disk" was a local subdirectory or a volume name. * */ void os_get_path_name(char *pathbuf, size_t pathbuflen, const char *fname); /* * Build a full path name, given a path and a filename. The path may have * been specified by the user, or may have been extracted from another file * via os_get_path_name(). This routine must take care to add path * separators as needed, but also must take care not to add too many path * separators. * * This routine should reformat the path into canonical format to the * extent possible purely through syntactic analysis. For example, special * relative links, such as Unix "." and "..", should be resolved; for * example, combining "a/./b/c" with ".." on Unix should yield "a/b". * However, symbolic links that require looking up names in the file system * should NOT be resolved. We don't want to perform any actual file system * lookups because might want to construct hypothetical paths that don't * necessarily relate to files on the local system. * * Note that relative path names may require special care on some * platforms. In particular, if the source path is relative, the result * should also be relative. For example, on the Macintosh, a path of * "games" and a filename "deep.gam" should yield ":games:deep.gam" - note * the addition of the leading colon to make the result path relative. * * Note also that the 'filename' argument is not only allowed to be an * ordinary file, possibly qualified with a relative path, but is also * allowed to be a subdirectory. The result in this case should be a path * that can be used as the 'path' argument to a subsequent call to * os_build_full_path; this allows a path to be built in multiple steps by * descending into subdirectories one at a time. * * Unix examples: * *. /home/mjr + deep.gam -> /home/mjr/deep.gam" *. /home/mjr + .. -> /home *. /home/mjr + ../deep.gam -> /home/deep.gam *. /home/mjr/ + deep.gam -> /home/mjr/deep.gam" *. games + deep.gam -> games/deep.gam" *. games/ + deep.gam -> games/deep.gam" *. /home/mjr + games/deep.gam -> /home/mjr/games/deep.gam" *. games + scifi/deep.gam -> games/scifi/deep.gam" *. /home/mjr + games -> /home/mjr/games" * * Mac examples: * *. Hard Disk: + deep.gam -> Hard Disk:deep.gam *. :games: + deep.gam -> :games:deep.gam *. :games:deep + ::test.gam -> :games:test.gam *. games + deep.gam -> :games:deep.gam *. Hard Disk: + :games:deep.gam -> Hard Disk:games:deep.gam *. games + :scifi:deep.gam -> :games:scifi:deep.gam *. Hard Disk: + games -> Hard Disk:games *. Hard Disk:games + scifi -> Hard Disk:games:scifi *. Hard Disk:games:scifi + deep.gam -> Hard Disk:games:scifi:deep.gam *. Hard Disk:games + :scifi:deep.gam -> Hard Disk:games:scifi:deep.gam * * VMS examples: * *. [home.mjr] + deep.gam -> [home.mjr]deep.gam *. [home.mjr] + [-]deep.gam -> [home]deep.gam *. mjr.dir + deep.gam -> [.mjr]deep.gam *. [home]mjr.dir + deep.gam -> [home.mjr]deep.gam *. [home] + [.mjr]deep.gam -> [home.mjr]deep.gam */ void os_build_full_path(char *fullpathbuf, size_t fullpathbuflen, const char *path, const char *filename); /* * Combine a path and a filename to form a full path to the file. This is * *almost* the same as os_build_full_path(), but if the 'filename' element * is a special relative link, such as Unix '.' or '..', this preserves * that special link in the final name. * * Unix examples: * *. /home/mjr + deep.gam -> /home/mjr/deep.gam *. /home/mjr + . -> /home/mjr/. *. /home/mjr + .. -> /home/mjr/.. * * Mac examples: * *. Hard Disk:games + deep.gam -> HardDisk:games:deep.gam *. Hard Disk:games + :: -> HardDisk:games:: * * VMS exmaples: * *. [home.mjr] + deep.gam -> [home.mjr]deep.gam *. [home.mjr] + [-] -> [home.mjr.-] */ void os_combine_paths(char *fullpathbuf, size_t pathbuflen, const char *path, const char *filename); /* * Get the absolute, fully qualified filename for a file. This fills in * 'result_buf' with the absolute path to the given file, taking into * account the current working directory and any other implied environment * information that affects the way the file system would resolve the given * file name to a specific file on disk if we opened the file now using * this name. * * The returned path should be in absolute path form, meaning that it's * independent of the current working directory or any other environment * settings. That is, this path should still refer to the same file even * if the working directory changes. * * Note that it's valid to get the absolute path for a file that doesn't * exist, or for a path with directory components that don't exist. For * example, a caller might generate the absolute path for a file that it's * about to create, or a hypothetical filename for path comparison * purposes. The function should succeed even if the file or any path * components don't exist. If the file is in relative format, and any path * elements don't exist but are syntactically well-formed, the result * should be the path obtained from syntactically combining the working * directory with the relative path. * * On many systems, a given file might be reachable through more than one * absolute path. For example, on Unix it might be possible to reach a * file through symbolic links to the file itself or to parent directories, * or hard links to the file. It's up to the implementation to determine * which path to use in such cases. * * On success, returns true. If it's not possible to resolve the file name * to an absolute path, the routine copies the original filename to the * result buffer exactly as given, and returns false. */ int os_get_abs_filename(char *result_buf, size_t result_buf_size, const char *filename); /* * Get the relative version of the given filename path 'filename', relative * to the given base directory 'basepath'. Both paths must be given in * absolute format. * * Returns true on success, false if it's not possible to rewrite the path * in relative terms. For example, on Windows, it's not possible to * express a path on the "D:" drive as relative to a base path on the "C:" * drive, since each drive letter has an independent root folder; there's * no namespace entity enclosing a drive letter's root folder. On * Unix-like systems where the entire namespace has a single hierarchical * root, it should always be possible to express any path relative to any * other. * * The result should be a relative path that can be combined with * 'basepath' using os_build_full_path() to reconstruct a path that * identifies the same file as the original 'filename' (it's not important * that this procedure would result in the identical string - it just has * to point to the same file). If it's not possible to express the * filename relative to the base path, fill in 'result_buf' with the * original filename and return false. * * Windows examples: * *. c:\mjr\games | c:\mjr\games\deep.gam -> deep.gam *. c:\mjr\games | c:\mjr\games\tads\deep.gam -> tads\deep.gam *. c:\mjr\games | c:\mjr\tads\deep.gam -> ..\tads\deep.gam *. c:\mjr\games | d:\deep.gam -> d:\deep.gam (and return false) * * Mac OS examples: * *. Mac HD:mjr:games | Mac HD:mjr:games:deep.gam -> deep.gam *. Mac HD:mjr:games | Mac HD:mjr:games:tads:deep.gam -> :tads:deep.gam *. Mac HD:mjr:games | Ext Disk:deep.gam -> Ext Disk:deep.gam (return false) * * VMS examples: * *. SYS$:[mjr.games] | SYS$:[mjr.games]deep.gam -> deep.gam *. SYS$:[mjr.games] | SYS$:[mjr.games.tads]deep.gam -> [.tads]deep.gam *. SYS$:[mjr.games] | SYS$:[mjr.tads]deep.gam -> [-.tads]deep.gam *. SYS$:[mjr.games] | DISK$:[mjr]deep.gam -> DISK$[mjr]deep.gam (ret false) */ int os_get_rel_path(char *result_buf, size_t result_buf_size, const char *basepath, const char *filename); /* * Determine if the given file is in the given directory. Returns true if * so, false if not. 'filename' is a relative or absolute file name; * 'path' is a relative or absolute directory path, such as one returned * from os_get_path_name(). * * If 'include_subdirs' is true, the function returns true if the file is * either directly in the directory 'path', OR it's in any subdirectory of * 'path'. If 'include_subdirs' is false, the function returns true only * if the file is directly in the given directory. * * If 'match_self' is true, the function returns true if 'filename' and * 'path' are the same directory; otherwise it returns false in this case. * * This routine is allowed to return "false negatives" - that is, it can * claim that the file isn't in the given directory even when it actually * is. The reason is that it's not always possible to determine for sure * that there's not some way for a given file path to end up in the given * directory. In contrast, a positive return must be reliable. * * If possible, this routine should fully resolve the names through the * file system to determine the path relationship, rather than merely * analyzing the text superficially. This can be important because many * systems have multiple ways to reach a given file, such as via symbolic * links on Unix; analyzing the syntax alone wouldn't reveal these multiple * pathways. * * SECURITY NOTE: If possible, implementations should fully resolve all * symbolic links, relative paths (e.g., Unix ".."), etc, before rendering * judgment. One important application for this routine is to determine if * a file is in a sandbox directory, to enforce security restrictions that * prevent a program from accessing files outside of a designated folder. * If the implementation fails to resolve symbolic links or relative paths, * a malicious program or user could bypass the security restriction by, * for example, creating a symbolic link within the sandbox directory that * points to the root folder. Implementations can avoid this loophole by * converting the file and directory names to absolute paths and resolving * all symbolic links and relative notation before comparing the paths. */ int os_is_file_in_dir(const char *filename, const char *path, int include_subdirs, int match_self); /* ------------------------------------------------------------------------ */ /* * Convert an OS filename path to URL-style format. This isn't a true URL * conversion; rather, it simply expresses a filename in Unix-style * notation, as a series of path elements separated by '/' characters. * Unlike true URLs, we don't use % encoding or a scheme prefix (file://, * etc). * * The result path never ends in a trailing '/', unless the entire result * path is "/". This is for consistency; even if the source path ends with * a local path separator, the result doesn't. * * If the local file system syntax uses '/' characters as ordinary filename * characters, these must be replaced with some other suitable character in * the result, since otherwise they'd be taken as path separators when the * URL is parsed. If possible, the substitution should be reversible with * respect to os_cvt_dir_url(), so that the same URL read back in on this * same platform will produce the same original filename. One particular * suggestion is that if the local system uses '/' to delimit what would be * a filename extension on other platforms, replace '/' with '.', since * this will provide reversibility as well as a good mapping if the URL is * read back in on another platform. * * The local equivalents of "." and "..", if they exist, are converted to * "." and ".." in the URL notation. * * Examples: * *. Windows: images\rooms\startroom.jpg -> images/rooms/startroom.jpg *. Windows: ..\startroom.jpg -> ../startroom.jpg *. Mac: :images:rooms:startroom.jpg -> images/rooms/startroom.jpg *. Mac: ::startroom.jpg -> ../startroom.jpg *. VMS: [.images.rooms]startroom.jpg -> images/rooms/startroom.jpg *. VMS: [-.images]startroom.jpg -> ../images/startroom.jpg *. Unix: images/rooms/startroom.jpg -> images/rooms/startroom.jpg *. Unix: ../images/startroom.jpg -> ../images/startroom.jpg * * If the local name is an absolute path in the local file system (e.g., * Unix /file, Windows C:\file), translate as follows. If the local * operating system uses a volume or device designator (Windows C:, VMS * SYS$DISK:, etc), make the first element of the path the exact local * syntax for the device designator: /C:/ on Windows, /SYS$DISK:/ on VMS, * etc. Include the local syntax for the device prefix. For a system like * Unix with a unified file system root ("/"), simply start with the root * directory. Examples: * *. Windows: C:\games\deep.gam -> /C:/games/deep.gam *. Windows: C:games\deep.gam -> /C:./games/deep.gam *. Windows: \\SERVER\DISK\games\deep.gam -> /\\SERVER/DISK/games/deep.gam *. Mac OS 9: Hard Disk:games:deep.gam -> /Hard Disk:/games/deep.gam *. VMS: SYS$DISK:[games]deep.gam -> /SYS$DISK:/games/deep.gam *. Unix: /games/deep.gam -> /games/deep.gam * * Rationale: it's effectively impossible to create a truly portable * representation of an absolute path. Operating systems are too different * in the way they represent root paths, and even if that were solvable, a * root path is essentially unusable across machines anyway because it * creates a dependency on the contents of a particular machine's disk. So * if we're called upon to translate an absolute path, we can forget about * trying to be truly portable and instead focus on round-trip fidelity - * i.e., making sure that applying os_cvt_url_dir() to our result recovers * the exact original path, assuming it's done on the same operating * system. The approach outlined above should achieve round-trip fidelity * when a local path is converted to a URL and back on the same machine, * since the local URL-to-path converter should recognize its own special * type of local absolute path prefix. It also produces reasonable results * on other platforms - see the os_cvt_url_dir() comments below for * examples of the decoding results for absolute paths moved to new * platforms. The result when a device-rooted absolute path is encoded on * one machine and then decoded on another will generally be a local path * with a root on the default device/volume and an outermost directory with * a name based on the original machine's device/volume name. This * obviously won't reproduce the exact original path, but since that's * impossible anyway, this is probably as good an approximation as we can * create. * * Character sets: the input could be in local or UTF-8 character sets. * The implementation shouldn't care, though - just treat bytes in the * range 0-127 as plain ASCII, and everything else as opaque. I.e., do not * quote or otherwise modify characters outside the 0-127 range. */ void os_cvt_dir_url(char *result_buf, size_t result_buf_size, const char *src_path); /* * Convert a URL-style path into a filename path expressed in the local * file system's syntax. Fills in result_buf with a file path, constructed * using the local file system syntax, that corresponds to the path in * src_url expressed in URL-style syntax. Examples: * * images/rooms/startroom.jpg -> *. Windows -> images\rooms\startroom.jpg *. Mac OS 9 -> :images:rooms:startroom.jpg *. VMS -> [.images.rooms]startroom.jpg * * The source format isn't a true URL; it's simply a series of path * elements separated by '/' characters. Unlike true URLs, our input * format doesn't use % encoding and doesn't have a scheme (file://, etc). * (Any % in the source is treated as an ordinary character and left as-is, * even if it looks like a %XX sequence. Anything that looks like a scheme * prefix is left as-is, with any // treated as path separators. * * images/file%20name.jpg -> *. Windows -> images\file%20name.jpg * * file://images/file.jpg -> *. Windows -> file_\\images\file.jpg * * Any characters in the path that are invalid in the local file system * naming rules are converted to "_", unless "_" is itself invalid, in * which case they're converted to "X". One exception is that if '/' is a * valid local filename character (rather than a path separator as it is on * Unix and Windows), it can be used as the replacement for the character * that os_cvt_dir_url uses as its replacement for '/', so that this * substitution is reversible when a URL is generated and then read back in * on this same platform. * * images/file:name.jpg -> *. Windows -> images\file_name.jpg *. Mac OS 9 -> :images:file_name.jpg *. Unix -> images/file:name.jpg * * The path elements "." and ".." are specifically defined as having their * Unix meanings: "." is an alias for the preceding path element, or the * working directory if it's the first element, and ".." is an alias for * the parent of the preceding element. When these appear as path * elements, this routine translates them to the appropriate local * conventions. "." may be translated simply by removing it from the path, * since it reiterates the previous path element. ".." may be translated * by removing the previous element - HOWEVER, if ".." appears as the first * element, it has to be retained and translated to the equivalent local * notation, since it will have to be applied later, when the result_buf * path is actually used to open a file, at which point it will combined * with the working directory or another base path. * *. /images/../file.jpg -> [Windows] file.jpg *. ../images/file.jpg -> *. Windows -> ..\images\file.jpg *. Mac OS 9 -> ::images:file.jpg *. VMS -> [-.images]file.jpg * * If the URL path is absolute (starts with a '/'), the routine inspects * the path to see if it was created by the same OS, according to the local * rules for converting absolute paths in os_cvt_dir_url() (see). If so, * we reverse the encoding done there. If it doesn't appear that the name * was created by the same operating system - that is, if reversing the * encoding doesn't produce a valid local filename - then we create a local * absolute path as follows. If the local system uses device/volume * designators, we start with the current working device/volume or some * other suitable default volume. We then add the first element of the * path, if any, as the root directory name, applying the usual "_" or "X" * substitution for any characters that aren't allowed in local names. The * rest of the path is handled in the usual fashion. * *. /images/file.jpg -> *. Windows -> \images\file.jpg *. Unix -> /images/file.jpg * *. /c:/images/file.jpg -> *. Windows -> c:\images\file.jpg *. Unix -> /c:/images/file.jpg *. VMS -> SYS$DISK:[c__.images]file.jpg * *. /Hard Disk:/images/file.jpg -> *. Windows -> \Hard Disk_\images\file.jpg *. Unix -> SYS$DISK:[Hard_Disk_.images]file.jpg * * Note how the device/volume prefix becomes the top-level directory when * moving a path across machines. It's simply not possible to reconstruct * the exact original path in such cases, since device/volume syntax rules * have little in common across systems. But this seems like a good * approximation in that (a) it produces a valid local path, and (b) it * gives the user a reasonable basis for creating a set of folders to mimic * the original source system, if they want to use that approach to port * the data rather than just changing the paths internally in the source * material. * * Character sets: use the same rules as for os_cvt_dir_url(). */ void os_cvt_url_dir(char *result_buf, size_t result_buf_size, const char *src_url); /* ------------------------------------------------------------------------ */ /* * Get a suitable seed for a random number generator; should use the system * clock or some other source of an unpredictable and changing seed value. */ void os_rand(long *val); /* * Generate random bytes for use in seeding a PRNG (pseudo-random number * generator). This is an extended version of os_rand() for PRNGs that use * large seed vectors containing many bytes, rather than the simple 32-bit * seed that os_rand() assumes. * * As with os_rand(), this function isn't meant to be used directly as a * random number source for ongoing use - instead, this is intended mostly * for seeding a PRNG, which will then be used as the primary source of * random numbers. The big problem with using this routine directly as a * randomness source is that some implementations might rely heavily on * environmental randomness, such as the real-time clock or OS usage * statistics. Such sources tend to provide reasonable entropy from one * run to the next, but not within a single session, as the underlying data * sources don't change rapidly enough. * * Ideally, this routine should generate *truly* random bytes obtained from * hardware sources. Not all systems can provide that, though, so true * randomness isn't guaranteed. Here are the suggested implementation * options, in descending order of desirability: * * 1. Use a hardware source of true randomness, such as a /dev/rand type * of device. However, note that this call should return reasonably * quickly, so always use a non-blocking source. Some Unix /dev/rand * devices, for example, can block indefinitely to allow sufficient entropy * to accumulate. * * 2. Use a cryptographic random number source provided by the OS. Some * systems provide this as an API service. If going this route, be sure * that the OS generator is itself "seeded" with some kind of true * randomness source, as it defeats the whole purpose if you always return * a fixed pseudo-random sequence each time the program runs. * * 3. Use whatever true random sources are available locally to seed a * software pseudo-random number generator, then generate bytes from your * PRNG. Some commonly available sources of true randomness are a * high-resolution timer, the system clock, the current process ID, * logged-in user ID, environment variables, uninitialized pages of memory, * the IP address; each of these sources might give you a few bits of * entropy at best, so the best bet is to use an ensemble. You could, for * example, concatenate a bunch of this type of information together and * calculate an MD5 or SHA1 hash to mix the bits more thoroughly. For the * PRNG, use a cryptographic generator. (You could use the ISAAC generator * from TADS 3, as that's a crypto PRNG, but it's probably better to use a * different generator here since TADS 3 is going to turn around and use * this function's output to seed ISAAC - seeding one ISAAC instance with * another ISAAC's output seems likely to magnify any weaknesses in the * ISAAC algorithm.) Note that this option is basically the DIY version of * option 2. Option 2 is better because the OS probably has access to * better sources of true randomness than an application does. */ void os_gen_rand_bytes(unsigned char *buf, size_t len); /* ------------------------------------------------------------------------ */ /* * Display routines. * * Our display model is a simple stdio-style character stream. * * In addition, we provide an optional "status line," which is a * non-scrolling area where a line of text can be displayed. If the status * line is supported, text should only be displayed in this area when * os_status() is used to enter status-line mode (mode 1); while in status * line mode, text is written to the status line area, otherwise (mode 0) * it's written to the normal main text area. The status line is normally * shown in a different color to set it off from the rest of the text. * * The OS layer can provide its own formatting (word wrapping in * particular) if it wants, in which case it should also provide pagination * using os_more_prompt(). */ /* * OS_MAXWIDTH - the maximum width of a line of text. Most platforms use * 135 for this, but you can use more or less as appropriate. If you use * OS-level line wrapping, then the true width of a text line is * irrelevant, and the portable code will use this merely for setting its * internal buffer sizes. * * This must be defined in the os_xxx.h header file for each platform. */ /*#define OS_MAXWIDTH 135 - example only: define for real in os_xxx.h header*/ /* * Print a string on the console. These routines come in two varieties: * * os_printz - write a NULL-TERMINATED string *. os_print - write a COUNTED-LENGTH string, which may not end with a null * * These two routines are identical except that os_printz() takes a string * which is terminated by a null byte, and os_print() instead takes an * explicit length, and a string that may not end with a null byte. * * os_printz(str) may be implemented as simply os_print(str, strlen(str)). * * The string is written in one of three ways, depending on the status mode * set by os_status(): * * status mode == 0 -> write to main text window *. status mode == 1 -> write to status line *. anything else -> do not display the text at all * * Implementations are free to omit any status line support, in which case * they should simply suppress all output when the status mode is anything * other than zero. * * The following special characters must be recognized in the displayed * text: * * '\n' - newline: end the current line and move the cursor to the start of * the next line. If the status line is supported, and the current status * mode is 1 (i.e., displaying in the status line), then two special rules * apply to newline handling: newlines preceding any other text should be * ignored, and a newline following any other text should set the status * mode to 2, so that all subsequent output is suppressed until the status * mode is changed with an explicit call by the client program to * os_status(). * * '\r' - carriage return: end the current line and move the cursor back to * the beginning of the current line. Subsequent output is expected to * overwrite the text previously on this same line. The implementation * may, if desired, IMMEDIATELY clear the previous text when the '\r' is * written, rather than waiting for subsequent text to be displayed. * * All other characters may be assumed to be ordinary printing characters. * The routine need not check for any other special characters. * */ void os_printz(const char *str); void os_print(const char *str, size_t len); /* * Print to the debugger console. These routines are for interactive * debugger builds only: they display the given text to a separate window * within the debugger UI (separate from the main game command window) * where the debugger displays status information specific to the debugging * session (such as compiler/build output, breakpoint status messages, * etc). For example, TADS Workbench on Windows displays these messages in * its "Debug Log" window. * * These routines only need to be implemented for interactive debugger * builds, such as TADS Workbench on Windows. These can be omitted for * regular interpreter builds. */ void os_dbg_printf(const char *fmt, ...); void os_dbg_vprintf(const char *fmt, va_list args); /* * Allocating sprintf and vsprintf. These work like the regular C library * sprintf and vsprintf funtions, but they allocate a return buffer that's * big enough to hold the result, rather than formatting into a caller's * buffer. This protects against buffer overruns and ensures that the * result isn't truncated. * * On return, '*bufptr' is filled in with a pointer to a buffer allocated * with osmalloc(). This buffer contains the formatted string result. The * caller is responsible for freeing the buffer by calling osfree(). * *bufptr can be null on return if an error occurs. * * The return value is the number of bytes written to the allocated buffer, * not including the null terminator. If an error occurs, the return value * is -1 and *bufptr is undefined. * * Many modern C libraries provide equivalents of these, usually called * asprintf() and vasprintf(), respectively. */ /* int os_asprintf(char **bufptr, const char *fmt, ...); */ /* int os_vasprintf(char **bufptr, const char *fmt, va_list ap); */ /* * Set the status line mode. There are three possible settings: * * 0 -> main text mode. In this mode, all subsequent text written with * os_print() and os_printz() is to be displayed to the main text area. * This is the normal mode that should be in effect initially. This mode * stays in effect until an explicit call to os_status(). * * 1 -> statusline mode. In this mode, text written with os_print() and * os_printz() is written to the status line, which is usually rendered as * a one-line area across the top of the terminal screen or application * window. In statusline mode, leading newlines ('\n' characters) are to * be ignored, and any newline following any other character must change * the mode to 2, as though os_status(2) had been called. * * 2 -> suppress mode. In this mode, all text written with os_print() and * os_printz() must simply be ignored, and not displayed at all. This mode * stays in effect until an explicit call to os_status(). */ void os_status(int stat); /* get the status line mode */ int os_get_status(); /* * Set the score value. This displays the given score and turn counts on * the status line. In most cases, these values are displayed at the right * edge of the status line, in the format "score/turns", but the format is * up to the implementation to determine. In most cases, this can simply * be implemented as follows: * *. void os_score(int score, int turncount) *. { *. char buf[40]; *. sprintf(buf, "%d/%d", score, turncount); *. os_strsc(buf); *. } */ void os_score(int score, int turncount); /* display a string in the score area in the status line */ void os_strsc(const char *p); /* clear the screen */ void oscls(void); /* redraw the screen */ void os_redraw(void); /* flush any buffered display output */ void os_flush(void); /* * Update the display - process any pending drawing immediately. This * only needs to be implemented for operating systems that use * event-driven drawing based on window invalidations; the Windows and * Macintosh GUI's both use this method for drawing window contents. * * The purpose of this routine is to refresh the display prior to a * potentially long-running computation, to avoid the appearance that the * application is frozen during the computation delay. * * Platforms that don't need to process events in the main thread in order * to draw their window contents do not need to do anything here. In * particular, text-mode implementations generally don't need to implement * this routine. * * This routine doesn't absolutely need a non-empty implementation on any * platform, but it will provide better visual feedback if implemented for * those platforms that do use event-driven drawing. */ void os_update_display(); /* ------------------------------------------------------------------------ */ /* * Set text attributes. Text subsequently displayed through os_print() and * os_printz() are to be displayed with the given attributes. * * 'attr' is a (bitwise-OR'd) combination of OS_ATTR_xxx values. A value * of zero indicates normal text, with no extra attributes. */ void os_set_text_attr(int attr); /* attribute code: bold-face */ #define OS_ATTR_BOLD 0x0001 /* attribute code: italic */ #define OS_ATTR_ITALIC 0x0002 /* * Abstract attribute codes. Each platform can choose a custom rendering * for these by #defining them before this point, in the OS-specific header * (osdos.h, osmac.h, etc). We provide *default* definitions in case the * platform doesn't define these. * * For compatibility with past versions, we treat HILITE, EM, and BOLD as * equivalent. Platforms that can display multiple kinds of text * attributes (boldface and italic, say) should feel free to use more * conventional HTML mappings, such as EM->italic and STRONG->bold. */ /* * "Highlighted" text, as appropriate to the local platform. On most * text-mode platforms, the only kind of rendering variation possible is a * brighter or intensified color. If actual bold-face is available, that * can be used instead. This is the attribute used for text enclosed in a * TADS2 "\( \)" sequence. */ #ifndef OS_ATTR_HILITE # define OS_ATTR_HILITE OS_ATTR_BOLD #endif /* HTML attribute - by default, map this to bold-face */ #ifndef OS_ATTR_EM # define OS_ATTR_EM OS_ATTR_BOLD #endif /* HTML attribute - by default, this has no effect */ #ifndef OS_ATTR_STRONG # define OS_ATTR_STRONG 0 #endif /* ------------------------------------------------------------------------ */ /* * Colors. * * There are two ways of encoding a color. First, a specific color can be * specified as an RGB (red-green-blue) value, with discreet levels for * each component's intensity, ranging from 0 to 255. Second, a color can * be "parameterized," which doesn't specify a color in absolute terms but * rather specified one of a number of pre-defined *types* of colors; * these pre-defined types can be chosen by the OS implementation, or, on * some systems, selected by the user via a preferences mechanism. * * The os_color_t type encodes a color in 32 bits. The high-order 8 bits * of a color value give the parameterized color identifier, or are set to * zero to indicate an RGB color. An RGB color is encoded in the * low-order 24 bits, via the following formula: * * (R << 16) + (G << 8) + B * * R specifies the intensity of the red component of the color, G green, * and B blue. Each of R, G, and B must be in the range 0-255. */ typedef unsigned long os_color_t; /* encode an R, G, B triplet into an os_color_t value */ #define os_rgb_color(r, g, b) (((r) << 16) + ((g) << 8) + (b)) /* * Determine if a color is given as an RGB value or as a parameterized * color value. Returns true if the color is given as a parameterized * color (one of the OS_COLOR_xxx values), false if it's given as an * absolute RGB value. */ #define os_color_is_param(color) (((color) & 0xFF000000) != 0) /* get the red/green/blue components of an os_color_t value */ #define os_color_get_r(color) ((int)(((color) >> 16) & 0xFF)) #define os_color_get_g(color) ((int)(((color) >> 8) & 0xFF)) #define os_color_get_b(color) ((int)((color) & 0xFF)) /* * Parameterized color codes. These are os_color_t values that indicate * colors by type, rather than by absolute RGB values. */ /* * "transparent" - applicable to backgrounds only, this specifies that the * current screen background color should be used */ #define OS_COLOR_P_TRANSPARENT ((os_color_t)0x01000000) /* "normal text" color (as set via user preferences, if applicable) */ #define OS_COLOR_P_TEXT ((os_color_t)0x02000000) /* normal text background color (from user preferences) */ #define OS_COLOR_P_TEXTBG ((os_color_t)0x03000000) /* "status line" text color (as set via user preferences, if applicable) */ #define OS_COLOR_P_STATUSLINE ((os_color_t)0x04000000) /* status line background color (from user preferences) */ #define OS_COLOR_P_STATUSBG ((os_color_t)0x05000000) /* input text color (as set via user preferences, if applicable) */ #define OS_COLOR_P_INPUT ((os_color_t)0x06000000) /* * Set the text foreground and background colors. This sets the text * color for subsequent os_printf() and os_vprintf() calls. * * The background color can be OS_COLOR_TRANSPARENT, in which case the * background color is "inherited" from the current screen background. * Note that if the platform is capable of keeping old text for * "scrollback," then the transparency should be a permanent attribute of * the character - in other words, it should not be mapped to the current * screen color in the scrollback buffer, because doing so would keep the * current screen color even if the screen color changes in the future. * * Text color support is optional. If the platform doesn't support text * colors, this can simply do nothing. If the platform supports text * colors, but the requested color or attributes cannot be displayed, the * implementation should use the best available approximation. */ void os_set_text_color(os_color_t fg, os_color_t bg); /* * Set the screen background color. This sets the text color for the * background of the screen. If possible, this should immediately redraw * the main text area with this background color. The color is given as an * OS_COLOR_xxx value. * * If the platform is capable of redisplaying the existing text, then any * existing text that was originally displayed with 'transparent' * background color should be redisplayed with the new screen background * color. In other words, the 'transparent' background color of previously * drawn text should be a permanent attribute of the character - the color * should not be mapped on display to the then-current background color, * because doing so would lose the transparency and thus retain the old * screen color on a screen color change. */ void os_set_screen_color(os_color_t color); /* ------------------------------------------------------------------------ */ /* * os_plain() - Use plain ascii mode for the display. If possible and * necessary, turn off any text formatting effects, such as cursor * positioning, highlighting, or coloring. If this routine is called, * the terminal should be treated as a simple text stream; users might * wish to use this mode for things like text-to-speech converters. * * Purely graphical implementations that cannot offer a textual mode * (such as Mac OS or Windows) can ignore this setting. * * If this routine is to be called, it must be called BEFORE os_init(). * The implementation should set a flag so that os_init() will know to * set up the terminal for plain text output. */ #ifndef os_plain /* * some platforms (e.g. Mac OS) define this to be a null macro, so don't * define a prototype in those cases */ void os_plain(void); #endif /* * Set the game title. The output layer calls this routine when a game * sets its title (via an HTML tag, for example). If it's * convenient to do so, the OS layer can use this string to set a window * caption, or whatever else makes sense on each system. Most * character-mode implementations will provide an empty implementation, * since there's not usually any standard way to show the current * application title on a character-mode display. */ void os_set_title(const char *title); /* * Show the system-specific MORE prompt, and wait for the user to respond. * Before returning, remove the MORE prompt from the screen. * * This routine is only used and only needs to be implemented when the OS * layer takes responsibility for pagination; this will be the case on * most systems that use proportionally-spaced (variable-pitch) fonts or * variable-sized windows, since on such platforms the OS layer must do * most of the formatting work, leaving the standard output layer unable * to guess where pagination should occur. * * If the portable output formatter handles the MORE prompt, which is the * usual case for character-mode or terminal-style implementations, this * routine is not used and you don't need to provide an implementation. * Note that HTML TADS provides an implementation of this routine, because * the HTML renderer handles line breaking and thus must handle * pagination. */ void os_more_prompt(); /* * Interpreter Class Configuration. * * If this is a TEXT-ONLY interpreter: DO NOT define USE_HTML. * * If this is a MULTIMEDIA (HTML TADS) intepreter: #define USE_HTML * * (This really should be called something like OS_USE_HTML - the USE_ name * is for historical reasons. This purpose of this macro is to configure * the tads 2 VM-level output formatter's line breaking and MORE mode * behavior. In HTML mode, the VM-level formatter knows that it's feeding * its output to a page layout engine, so the VM-level output is just a * text stream. In plain-text mode, the VM formatter *is* the page layout * engine, so it needs to do all of the word wrapping and MORE prompting * itself. The tads 3 output layer does NOT use this macro for its * equivalent configuration, but instead has different .cpp files for the * different modes, and you simply link in the one for the configuration * you want.) */ /* #define USE_HTML */ /* * Enter HTML mode. This is only used when the run-time is compiled * with the USE_HTML flag defined. This call instructs the renderer * that HTML sequences should be parsed; until this call is made, the * renderer should not interpret output as HTML. Non-HTML * implementations do not need to define this routine, since the * run-time will not call it if USE_HTML is not defined. */ void os_start_html(void); /* exit HTML mode */ void os_end_html(void); /* * Global variables with the height and width (in character cells - rows * and columns) of the main text display area into which os_printf * displays. The height and width are given in text lines and character * columns, respectively. The portable code can use these values to * format text for display via os_printf(); for example, the caller can * use the width to determine where to put line breaks. * * These values are only needed for systems where os_printf() doesn't * perform its own word-wrap formatting. On systems such as the Mac, * where os_printf() performs word wrapping, these sizes aren't really * important because the portable code doesn't need to perform any real * formatting. * * These variables reflect the size of the "main text area," which is the * area of the screen excluding the status line and any "banner" windows * (as created with the os_banner_xxx() interfaces). * * The OS code must initialize these variables during start-up, and must * adjust them whenever the display size is changed by user action or * other external events (for example, if we're running inside a terminal * window, and the user resizes the window, the OS code must recalculate * the layout and adjust these accordingly). */ extern int G_os_pagelength; extern int G_os_linewidth; /* * Global flag that tells the output formatter whether to count lines * that it's displaying against the total on the screen so far. If this * variable is true, lines are counted, and the screen is paused with a * [More] message when it's full. When not in MORE mode, lines aren't * counted. This variable should be set to false when displaying text * that doesn't count against the current page, such as status line * information. * * This flag should not be modified by OS code. Instead, the output * formatter will set this flag according to its current state; the OS * code can use this flag to determine whether or not to display a MORE * prompt during os_printf()-type operations. Note that this flag is * normally interesting to the OS code only when the OS code itself is * handling the MORE prompt. */ extern int G_os_moremode; /* * Global buffer containing the name of the byte-code file (the "game * file") loaded into the VM. This is used only where applicable, which * generally means in TADS Interpreter builds. In other application * builds, this is simply left empty. The application is responsible for * setting this during start-up (or wherever else the byte-code filename * becomes known or changes). */ extern char G_os_gamename[OSFNMAX]; /* * Set non-stop mode. This tells the OS layer that it should disable any * MORE prompting it would normally do. * * This routine is needed only when the OS layer handles MORE prompting; on * character-mode platforms, where the prompting is handled in the portable * console layer, this can be a dummy implementation. */ void os_nonstop_mode(int flag); /* * Update progress display with current info, if appropriate. This can * be used to provide a status display during compilation. Most * command-line implementations will just ignore this notification; this * can be used for GUI compiler implementations to provide regular * display updates during compilation to show the progress so far. */ /* void os_progress(const char *fname, unsigned long linenum); */ /* * Set busy cursor. If 'flag' is true, provide a visual representation * that the system or application is busy doing work. If 'flag' is * false, remove any visual "busy" indication and show normal status. * * We provide a prototype here if your osxxx.h header file does not * #define a macro for os_csr_busy. On many systems, this function has * no effect at all, so the osxxx.h header file simply #define's it to * do an empty macro. */ #ifndef os_csr_busy void os_csr_busy(int flag); #endif /* ------------------------------------------------------------------------ */ /* * User Input Routines */ /* * Ask the user for a filename, using a system-dependent dialog or other * mechanism. Returns one of the OS_AFE_xxx status codes (see below). * * prompt_type is the type of prompt to provide -- this is one of the * OS_AFP_xxx codes (see below). The OS implementation doesn't need to * pay any attention to this parameter, but it can be used if desired to * determine the type of dialog to present if the system provides * different types of dialogs for different types of operations. * * file_type is one of the OSFTxxx codes for system file type. The OS * implementation is free to ignore this information, but can use it to * filter the list of files displayed if desired; this can also be used * to apply a default suffix on systems that use suffixes to indicate * file type. If OSFTUNK is specified, it means that no filtering * should be performed, and no default suffix should be applied. */ int os_askfile(const char *prompt, char *fname_buf, int fname_buf_len, int prompt_type, os_filetype_t file_type); /* * os_askfile status codes */ /* success */ #define OS_AFE_SUCCESS 0 /* * Generic failure - this is largely provided for compatibility with * past versions, in which only zero and non-zero error codes were * meaningful; since TRUE is defined as 1 on most platforms, we assume * that 1 is probably the generic non-zero error code that most OS * implementations have traditionally used. In addition, this can be * used to indicate any other error for which there is no more specific * error code. */ #define OS_AFE_FAILURE 1 /* user cancelled */ #define OS_AFE_CANCEL 2 /* * os_askfile prompt types * * Important note: do not change these values when porting TADS. These * values can be used by games, so they must be the same on all * platforms. */ #define OS_AFP_OPEN 1 /* choose an existing file to open for reading */ #define OS_AFP_SAVE 2 /* choose a filename for saving to a file */ /* * Read a string of input. Fills in the buffer with a null-terminated * string containing a line of text read from the standard input. The * returned string should NOT contain a trailing newline sequence. On * success, returns 'buf'; on failure, including end of file, returns a * null pointer. */ unsigned char *os_gets(unsigned char *buf, size_t bufl); /* * Read a string of input with an optional timeout. This behaves like * os_gets(), in that it allows the user to edit a line of text (ideally * using the same editing keys that os_gets() does), showing the line of * text under construction during editing. This routine differs from * os_gets() in that it returns if the given timeout interval expires * before the user presses Return (or the local equivalent). * * If the user presses Return before the timeout expires, we store the * command line in the given buffer, just as os_gets() would, and we return * OS_EVT_LINE. We also update the display in the same manner that * os_gets() would, by moving the cursor to a new line and scrolling the * displayed text as needed. * * If a timeout occurs before the user presses Return, we store the command * line so far in the given buffer, statically store the cursor position, * insert mode, buffer text, and anything else relevant to the editing * state, and we return OS_EVT_TIMEOUT. * * If the implementation does not support the timeout operation, this * routine should simply return OS_EVT_NOTIMEOUT immediately when called; * the routine should not allow the user to perform any editing if the * timeout is not supported. Callers must use the ordinary os_gets() * routine, which has no timeout capabilities, if the timeout is not * supported. * * When we return OS_EVT_TIMEOUT, the caller is responsible for doing one * of two things. * * The first possibility is that the caller performs some work that doesn't * require any display operations (in other words, the caller doesn't * invoke os_printf, os_getc, or anything else that would update the * display), and then calls os_gets_timeout() again. In this case, we will * use the editing state that we statically stored before we returned * OS_EVT_TIMEOUT to continue editing where we left off. This allows the * caller to perform some computation in the middle of user command editing * without interrupting the user - the extra computation is transparent to * the user, because we act as though we were still in the midst of the * original editing. * * The second possibility is that the caller wants to update the display. * In this case, the caller must call os_gets_cancel() BEFORE making any * display changes. Then, the caller must do any post-input work of its * own, such as updating the display mode (for example, closing HTML font * tags that were opened at the start of the input). The caller is now * free to do any display work it wants. * * If we have information stored from a previous call that was interrupted * by a timeout, and os_gets_cancel(TRUE) was never called, we will resume * editing where we left off when the cancelled call returned; this means * that we'll restore the cursor position, insertion state, and anything * else relevant. Note that if os_gets_cancel(FALSE) was called, we must * re-display the command line under construction, but if os_gets_cancel() * was never called, we will not have to make any changes to the display at * all. * * Note that when resuming an interrupted editing session (interrupted via * os_gets_cancel()), the caller must re-display the prompt prior to * invoking this routine. * * Note that we can return OS_EVT_EOF in addition to the other codes * mentioned above. OS_EVT_EOF indicates that an error occurred reading, * which usually indicates that the application is being terminated or that * some hardware error occurred reading the keyboard. * * If 'use_timeout' is false, the timeout should be ignored. Without a * timeout, the function behaves the same as os_gets(), except that it will * resume editing of a previously-interrupted command line if appropriate. * (This difference is why the timeout is optional: a caller might not need * a timeout, but might still want to resume a previous input that did time * out, in which case the caller would invoke this routine with * use_timeout==FALSE. The regular os_gets() would not satisfy this need, * because it cannot resume an interrupted input.) * * Note that a zero timeout has the same meaning as for os_get_event(): if * input is available IMMEDIATELY, return the input, otherwise return * immediately with the OS_EVT_TIMEOUT result code. */ int os_gets_timeout(unsigned char *buf, size_t bufl, unsigned long timeout_in_milliseconds, int use_timeout); /* * Cancel an interrupted editing session. This MUST be called if any * output is to be displayed after a call to os_gets_timeout() returns * OS_EVT_TIMEOUT. * * 'reset' indicates whether or not we will forget the input state saved * by os_gets_timeout() when it last returned. If 'reset' is true, we'll * clear the input state, so that the next call to os_gets_timeout() will * start with an empty input buffer. If 'reset' is false, we will retain * the previous input state, if any; this means that the next call to * os_gets_timeout() will re-display the same input buffer that was under * construction when it last returned. * * This routine need not be called if os_gets_timeout() is to be called * again with no other output operations between the previous * os_gets_timeout() call and the next one. * * Note that this routine needs only a trivial implementation when * os_gets_timeout() is not supported (i.e., the function always returns * OS_EVT_NOTIMEOUT). */ void os_gets_cancel(int reset); /* * Read a character from the keyboard. For extended keystrokes, this * function returns zero, and then returns the CMD_xxx code for the * extended keystroke on the next call. For example, if the user presses * the up-arrow key, the first call to os_getc() should return 0, and the * next call should return CMD_UP. Refer to the CMD_xxx codes below. * * os_getc() should return a high-level, translated command code for * command editing. This means that, where a functional interpretation of * a key and the raw key-cap interpretation both exist as CMD_xxx codes, * the functional interpretation should be returned. For example, on Unix, * Ctrl-E is conventionally used in command editing to move to the end of * the line, following Emacs key bindings. Hence, os_getc() should return * CMD_END for this keystroke, rather than a single 0x05 character (ASCII * Ctrl-E), because CMD_END is the high-level command code for the * operation. * * The translation ability of this function allows for system-dependent key * mappings to functional meanings. */ int os_getc(void); /* * Read a character from the keyboard, following the same protocol as * os_getc() for CMD_xxx codes (i.e., when an extended keystroke is * encountered, os_getc_raw() returns zero, then returns the CMD_xxx code * on the subsequent call). * * This function differs from os_getc() in that this function returns the * low-level, untranslated key code whenever possible. This means that, * when a functional interpretation of a key and the raw key-cap * interpretation both exist as CMD_xxx codes, this function returns the * key-cap interpretation. For the Unix Ctrl-E example in the comments * describing os_getc() above, this function should return 5 (the ASCII * code for Ctrl-E), because the ASCII control character interpretation is * the low-level key code. * * This function should return all control keys using their ASCII control * codes, whenever possible. Similarly, this function should return ASCII * 27 for the Escape key, if possible. * * For keys for which there is no portable ASCII representation, this * should return the CMD_xxx sequence. So, this function acts exactly the * same as os_getc() for arrow keys, function keys, and other special keys * that have no ASCII representation. This function returns a * non-translated version ONLY when an ASCII representation exists - in * practice, this means that this function and os_getc() vary only for CTRL * keys and Escape. */ int os_getc_raw(void); /* wait for a character to become available from the keyboard */ void os_waitc(void); /* * Constants for os_getc() when returning commands. When used for * command line editing, special keys (arrows, END, etc.) should cause * os_getc() to return 0, and return the appropriate CMD_ value on the * NEXT call. Hence, os_getc() must keep the appropriate information * around statically for the next call when a command key is issued. * * The comments indicate which CMD_xxx codes are "translated" codes and * which are "raw"; the difference is that, when a particular keystroke * could be interpreted as two different CMD_xxx codes, one translated * and the other raw, os_getc() should always return the translated * version of the key, and os_getc_raw() should return the raw version. */ #define CMD_UP 1 /* move up/up arrow (translated) */ #define CMD_DOWN 2 /* move down/down arrow (translated) */ #define CMD_RIGHT 3 /* move right/right arrow (translated) */ #define CMD_LEFT 4 /* move left/left arrow (translated) */ #define CMD_END 5 /* move cursor to end of line (translated) */ #define CMD_HOME 6 /* move cursor to start of line (translated) */ #define CMD_DEOL 7 /* delete to end of line (translated) */ #define CMD_KILL 8 /* delete entire line (translated) */ #define CMD_DEL 9 /* delete current character (translated) */ #define CMD_SCR 10 /* toggle scrollback mode (translated) */ #define CMD_PGUP 11 /* page up (translated) */ #define CMD_PGDN 12 /* page down (translated) */ #define CMD_TOP 13 /* top of file (translated) */ #define CMD_BOT 14 /* bottom of file (translated) */ #define CMD_F1 15 /* function key F1 (raw) */ #define CMD_F2 16 /* function key F2 (raw) */ #define CMD_F3 17 /* function key F3 (raw) */ #define CMD_F4 18 /* function key F4 (raw) */ #define CMD_F5 19 /* function key F5 (raw) */ #define CMD_F6 20 /* function key F6 (raw) */ #define CMD_F7 21 /* function key F7 (raw) */ #define CMD_F8 22 /* function key F8 (raw) */ #define CMD_F9 23 /* function key F9 (raw) */ #define CMD_F10 24 /* function key F10 (raw) */ #define CMD_CHOME 25 /* control-home (raw) */ #define CMD_TAB 26 /* tab (translated) */ #define CMD_SF2 27 /* shift-F2 (raw) */ /* not used (obsolete) - 28 */ #define CMD_WORD_LEFT 29 /* word left (ctrl-left on dos) (translated) */ #define CMD_WORD_RIGHT 30 /* word right (ctrl-right on dos) (translated) */ #define CMD_WORDKILL 31 /* delete word right (translated) */ #define CMD_EOF 32 /* end-of-file (raw) */ #define CMD_BREAK 33 /* break (Ctrl-C or local equivalent) (translated) */ #define CMD_INS 34 /* insert key (raw) */ /* * ALT-keys - add alphabetical code to CMD_ALT: ALT-A == CMD_ALT + 0, * ALT-B == CMD_ALT + 1, ALT-C == CMD_ALT + 2, etc * * These keys are all raw (untranslated). */ #define CMD_ALT 128 /* start of ALT keys */ /* ------------------------------------------------------------------------ */ /* * Event information structure for os_get_event. The appropriate union * member should be filled in, depending on the type of event that * occurs. */ union os_event_info_t { /* * OS_EVT_KEY - this returns the one or two characters of the * keystroke. If the key is an extended key, so that os_getc() would * return a two-character sequence for the keystroke, the first * character should be zero and the second the extended key code. * Otherwise, the first character should simply be the ASCII key code. * * The key code here is the "raw" keycode, equivalent to the codes * returned by os_getc_raw(). Note in particular that this means that * CTRL and Escape keys are presented as one-byte ASCII control * characters, not as two-byte CMD_xxx sequences. * * For multi-byte character sets (Shift-JIS, for example), note that * os_get_event() must NOT return a complete two-byte character here. * The two bytes here are exclusively used to represent special * CMD_xxx keys (such as arrow keys and function keys). For a * keystroke that is represented in a multi-byte character set using * more than one byte, os_get_event() must return a series of events. * Return an ordinary OS_EVT_KEY for the first byte of the sequence, * putting the byte in key[0]; then, return the second byte as a * separate OS_EVT_KEY as the next event; and so on for any additional * bytes. This will allow callers that are not multibyte-aware to * treat multi-byte characters as though they were sequences of * one-byte characters. */ int key[2]; /* * OS_EVT_HREF - this returns the text of the HREF as a * null-terminated string. */ char href[256]; /* command ID (for OS_EVT_COMMAND) */ int cmd_id; }; typedef union os_event_info_t os_event_info_t; /* * Event types for os_get_event */ /* invalid/no event */ #define OS_EVT_NONE 0x0000 /* OS_EVT_KEY - user typed a key on the keyboard */ #define OS_EVT_KEY 0x0001 /* OS_EVT_TIMEOUT - no event occurred before the timeout elapsed */ #define OS_EVT_TIMEOUT 0x0002 /* * OS_EVT_HREF - user clicked on a <A HREF> link. This only applies to * the HTML-enabled run-time. */ #define OS_EVT_HREF 0x0003 /* * OS_EVT_NOTIMEOUT - caller requested a timeout, but timeout is not * supported by this version of the run-time */ #define OS_EVT_NOTIMEOUT 0x0004 /* * OS_EVT_EOF - an error occurred reading the event. This generally * means that the application is quitting or we can no longer read from * the keyboard or terminal. */ #define OS_EVT_EOF 0x0005 /* * OS_EVT_LINE - user entered a line of text on the keyboard. This event * is not returned from os_get_event(), but rather from os_gets_timeout(). */ #define OS_EVT_LINE 0x0006 /* * Get an input event. The event types are shown above. If use_timeout is * false, this routine should simply wait until one of the events it * recognizes occurs, then return the appropriate information on the event. * If use_timeout is true, this routine should return OS_EVT_TIMEOUT after * the given number of milliseconds elapses if no event occurs first. * * This function is not obligated to obey the timeout. If a timeout is * specified and it is not possible to obey the timeout, the function * should simply return OS_EVT_NOTIMEOUT. The trivial implementation thus * checks for a timeout, returns an error if specified, and otherwise * simply waits for the user to press a key. * * A timeout value of 0 does *not* mean that there's no timeout (i.e., it * doesn't mean we should wait indefinitely) - that's specified by passing * FALSE for use_timeout. A zero timeout also doesn't meant that the * function should unconditionally return OS_EVT_TIMEOUT. Instead, a zero * timeout specifically means that IF an event is available IMMEDIATELY, * without blocking the thread, we should return that event; otherwise we * should immediately return a timeout event. */ int os_get_event(unsigned long timeout_in_milliseconds, int use_timeout, os_event_info_t *info); /* ------------------------------------------------------------------------ */ /* * Extended os_get_event() codes. * * THESE ARE NOT USED in the basic osifc implementation - these are only * used if the interpreter supports the "extended" interface defined in * osifcext.h. */ /* * System menu command event. Some interpreters (such as HTML TADS for * Windows) provide menu commands for common system-level game verbs - * SAVE, RESTORE, UNDO, QUIT. By default, these commands are returned as * literal command strings ("save", "restore", etc) via os_gets(), as * though the user had typed the commands at the keyboard. The extended OS * interface allows the program to receive these commands via * os_get_event(). When a command is enabled for os_get_event() via the * extended OS interface, and the player selects the command via a menu (or * toolbar button, etc) during a call to os_get_event(), os_get_event() * returns this event code, with the menu ID stored in the cmd_id field of * the event structure. */ #define OS_EVT_COMMAND 0x0100 /* command IDs for OS_EVT_COMMAND */ #define OS_CMD_NONE 0x0000 /* invalid command ID, for internal use */ #define OS_CMD_SAVE 0x0001 /* save game */ #define OS_CMD_RESTORE 0x0002 /* restore game */ #define OS_CMD_UNDO 0x0003 /* undo last turn */ #define OS_CMD_QUIT 0x0004 /* quit game */ #define OS_CMD_CLOSE 0x0005 /* close the game window */ #define OS_CMD_HELP 0x0006 /* show game help */ /* highest command ID used in this version of the interface */ #define OS_CMD_LAST 0x0006 /* ------------------------------------------------------------------------ */ /* * Ask for input through a dialog. * * 'prompt' is a text string to display as a prompting message. For * graphical systems, this message should be displayed in the dialog; * for text systems, this should be displayed on the terminal after a * newline. * * 'standard_button_set' is one of the OS_INDLG_xxx values defined * below, or zero. If this value is zero, no standard button set is to * be used; the custom set of buttons defined in 'buttons' is to be used * instead. If this value is non-zero, the appropriate set of standard * buttons, with labels translated to the local language if possible, is * to be used. * * 'buttons' is an array of strings to use as button labels. * 'button_count' gives the number of entries in the 'buttons' array. * 'buttons' and 'button_count' are ignored if 'standard_button_set' is * non-zero, since a standard set of buttons is used instead. If * 'buttons' and 'button_count' are to be used, each entry contains the * label of a button to show. */ /* * An ampersand ('&') character in a label string indicates that the * next character after the '&' is to be used as the short-cut key for * the button, if supported. The '&' should NOT be displayed in the * string; instead, the character should be highlighted according to * local system conventions. On Windows, for example, the short-cut * character should be shown underlined; on a text display, the response * might be shown with the short-cut character enclosed in parentheses. * If there is no local convention for displaying a short-cut character, * then the '&' should simply be removed from the displayed text. * * The short-cut key specified by each '&' character should be used in * processing responses. If the user presses the key corresponding to a * button's short-cut, the effect should be the same as if the user * clicked the button with the mouse. If local system conventions don't * allow for short-cut keys, any short-cut keys can be ignored. * * 'default_index' is the 1-based index of the button to use as the * default. If this value is zero, there is no default response. If * the user performs the appropriate system-specific action to select * the default response for the dialog, this is the response that is to * be selected. On Windows, for example, pressing the "Return" key * should select this item. * * 'cancel_index' is the 1-based index of the button to use as the * cancel response. If this value is zero, there is no cancel response. * This is the response to be used if the user cancels the dialog using * the appropriate system-specific action. On Windows, for example, * pressing the "Escape" key should select this item. */ /* * icon_id is one of the OS_INDLG_ICON_xxx values defined below. If * possible, an appropriate icon should be displayed in the dialog. * This can be ignored in text mode, and also in GUI mode if there is no * appropriate system icon. * * The return value is the 1-based index of the response selected. If * an error occurs, return 0. */ int os_input_dialog(int icon_id, const char *prompt, int standard_button_set, const char **buttons, int button_count, int default_index, int cancel_index); /* * Standard button set ID's */ /* OK */ #define OS_INDLG_OK 1 /* OK, Cancel */ #define OS_INDLG_OKCANCEL 2 /* Yes, No */ #define OS_INDLG_YESNO 3 /* Yes, No, Cancel */ #define OS_INDLG_YESNOCANCEL 4 /* * Dialog icons */ /* no icon */ #define OS_INDLG_ICON_NONE 0 /* warning */ #define OS_INDLG_ICON_WARNING 1 /* information */ #define OS_INDLG_ICON_INFO 2 /* question */ #define OS_INDLG_ICON_QUESTION 3 /* error */ #define OS_INDLG_ICON_ERROR 4 /* ------------------------------------------------------------------------ */ /* * OS main entrypoint */ int os0main(int oargc, char **oargv, int (*mainfn)(int, char **, char *), const char *before, const char *config); /* * new-style OS main entrypoint - takes an application container context */ int os0main2(int oargc, char **oargv, int (*mainfn)(int, char **, struct appctxdef *, char *), const char *before, const char *config, struct appctxdef *appctx); /* * OBSOLETE - Get filename from startup parameter, if possible; returns * true and fills in the buffer with the parameter filename on success, * false if no parameter file could be found. * * (This was used until TADS 2.2.5 for the benefit of the Mac interpreter, * and interpreters on systems with similar desktop shells, to allow the * user to launch the terp by double-clicking on a saved game file. The * terp would read the launch parameters, discover that a saved game had * been used to invoke it, and would then stash away the saved game info * for later retrieval from this function. This functionality was replaced * in 2.2.5 with a command-line parameter: the terp now uses the desktop * launch data to synthesize a suitable argv[] vectro to pass to os0main() * or os0main2(). This function should now simply be stubbed out - it * should simply return FALSE.) */ int os_paramfile(char *buf); /* * Initialize. This should be called during program startup to * initialize the OS layer and check OS-specific command-line arguments. * * If 'prompt' and 'buf' are non-null, and there are no arguments on the * given command line, the OS code can use the prompt to ask the user to * supply a filename, then store the filename in 'buf' and set up * argc/argv to give a one-argument command string. (This mechanism for * prompting for a filename is obsolescent, and is retained for * compatibility with a small number of existing implementations only; * new implementations should ignore this mechanism and leave the * argc/argv values unchanged.) */ int os_init(int *argc, char *argv[], const char *prompt, char *buf, int bufsiz); /* * Termination functions. There are three main termination functions, * described individually below; here's a brief overview of the * relationship among the functions. The important thing to realize is * that these functions have completely independent purposes; they should * never call one another, and they should never do any of the work that's * intended for the others. * * os_uninit() is meant to undo the effects of os_init(). On many * systems, os_init() has some global effect, such as setting the terminal * to some special input or output mode. os_uninit's purpose is to undo * these global effects, returning the terminal mode (and whatever else) * to the conditions they were in at program startup. Portable callers * are meant to call this routine at some point before terminating if they * ever called os_init(). Note that this routine DOES NOT terminate the * program - it should simply undo anything that os_init() did and return, * to let the caller do any additional termination work of its own. * * os_expause() optionally pauses before termination, to allow the user to * acknowledge any text the program displays just before exiting. This * doesn't have to do anything at all, but it's useful on systems where * program termination will do something drastic like close the window: * without a pause, the user wouldn't have a chance to read any text the * program displayed after the last interactive input, because the window * would abruptly disappear moments after the text was displayed. For * systems where termination doesn't have such drastic effects, there's no * need to do anything in this routine. Because it's up to this routine * whether or not to pause, this routine must display a prompt if it's * going to pause for user input - the portable caller obviously can't do * so, because the caller doesn't know if the routine is going to pause or * not (so if the caller displayed a prompt assuming the routine would * pause, the prompt would show up even on systems where there actually is * no pause, which would be confusing). This routine DOES NOT terminate * the program; it simply pauses if necessary to allow the user to * acknowledge the last bit of text the program displayed, then returns to * allow the caller to carry on with its own termination work. * * os_term() is meant to perform the same function as the C standard * library routine exit(): this actually terminates the program, exiting * to the operating system. This routine is not meant to return to its * caller, because it's supposed to exit the program directly. For many * systems, this routine can simply call exit(); the portable code calls * this routine instead of calling exit() directly, because some systems * have their own OS-specific way of terminating rather than using exit(). * This routine MUST NOT undo the effects of os_init(), because callers * might not have ever called os_init(); callers are required to call * os_uninit() if they ever called os_init(), before calling os_term(), so * this routine can simply assume that any global modes set by os_init() * have already been undone by the time this is called. */ /* * Uninitialize. This is called prior to progam termination to reverse * the effect of any changes made in os_init(). For example, if * os_init() put the terminal in raw mode, this should restore the * previous terminal mode. This routine should not terminate the * program (so don't call exit() here) - the caller might have more * processing to perform after this routine returns. */ void os_uninit(void); /* * Pause prior to exit, if desired. This is meant to be called by * portable code just before the program is to be terminated; it can be * implemented to show a prompt and wait for user acknowledgment before * proceeding. This is useful for implementations that are using * something like a character-mode terminal window running on a graphical * operating system: this gives the implementation a chance to pause * before exiting, so that the window doesn't just disappear * unceremoniously. * * This is allowed to do nothing at all. For regular character-mode * systems, this routine usually doesn't do anything, because when the * program exits, the terminal will simply return to the OS command * prompt; none of the text displayed just before the program exited will * be lost, so there's no need for any interactive pause. Likewise, for * graphical systems where the window will remain open, even after the * program exits, until the user explicitly closes the window, there's no * need to do anything here. * * If this is implemented to pause, then this routine MUST show some kind * of prompt to let the user know we're waiting. In the simple case of a * text-mode terminal window on a graphical OS, this should simply print * out some prompt text ("Press a key to exit...") and then wait for the * user to acknowledge the prompt (by pressing a key, for example). For * graphical systems, the prompt could be placed in the window's title * bar, or status-bar, or wherever is appropriate for the OS. */ void os_expause(void); /* * Terminate. This should exit the program with the given exit status. * In general, this should be equivalent to the standard C library * exit() function, but we define this interface to allow the OS code to * do any necessary pre-termination cleanup. */ void os_term(int status); /* * Install/uninstall the break handler. If possible, the OS code should * set (if 'install' is true) or clear (if 'install' is false) a signal * handler for keyboard break signals (control-C, etc, depending on * local convention). The OS code should set its own handler routine, * which should note that a break occurred with an internal flag; the * portable code uses os_break() from time to time to poll this flag. */ void os_instbrk(int install); /* * Check for user break ("control-C", etc) - returns true if a break is * pending, false if not. If this returns true, it should "consume" the * pending break (probably by simply clearing the OS code's internal * break-pending flag). */ int os_break(void); /* * Sleep for a given interval. This should simply pause for the given * number of milliseconds, then return. On multi-tasking systems, this * should use a system API to suspend the current process for the desired * delay; on single-tasking systems, this can simply sit in a wait loop * until the desired interval has elapsed. */ void os_sleep_ms(long delay_in_milliseconds); /* * Yield CPU; returns TRUE if user requested an interrupt (a "control-C" * type of operation to abort the entire program), FALSE to continue. * Portable code should call this routine from time to time during lengthy * computations that don't involve any UI operations; if practical, this * routine should be invoked roughly every 10 to 100 milliseconds. * * The purpose of this routine is to support "cooperative multitasking" * systems, such as pre-X MacOS, where it's necessary for each running * program to call the operating system explicitly in order to yield the * CPU from time to time to other concurrently running programs. On * cooperative multitasking systems, a program can only lose control of * the CPU by making specific system calls, usually related to GUI events; * a program can never lose control of the CPU asynchronously. So, a * program that performs lengthy computations without any UI interaction * can cause the rest of the system to freeze up until the computations * are finished; but if a compute-intensive program explicitly yields the * CPU from time to time, it allows other programs to remain responsive. * Yielding the CPU at least every 100 milliseconds or so will generally * allow the UI to remain responsive; yielding more frequently than every * 10 ms or so will probably start adding noticeable overhead. * * On single-tasking systems (such as MS-DOS), there's only one program * running at a time, so there's no need to yield the CPU; on virtually * every modern system, the OS automatically schedules CPU time without * the running programs having any say in the matter, so again there's no * need for a program to yield the CPU. For systems where this routine * isn't needed, the system header should simply #define os_yield to * something like "((void)0)" - this will allow the compiler to completely * ignore calls to this routine for systems where they aren't needed. * * Note that this routine is NOT meant to provide scheduling hinting to * modern systems with true multitasking, so a trivial implementation is * fine for any modern system. */ #ifndef os_yield int os_yield(void); #endif /* * Set the default saved-game extension. This routine will NOT be called * when we're using the standard saved game extension; this routine will be * invoked only if we're running as a stand-alone game, and the game author * specified a non-standard saved-game extension when creating the * stand-alone game. * * This routine is not required if the system does not use the standard, * semi-portable os0.c implementation. Even if the system uses the * standard os0.c implementation, it can provide an empty routine here if * the system code doesn't need to do anything special with this * information. * * The extension is specified as a null-terminated string. The extension * does NOT include the leading period. */ void os_set_save_ext(const char *ext); /* * Get the saved game extension previously set with os_set_save_ext(). * Returns null if no custom extension has been set. */ const char *os_get_save_ext(); /* ------------------------------------------------------------------------*/ /* * Translate a character from the HTML 4 Unicode character set to the * current character set used for display. Takes an HTML 4 character * code and returns the appropriate local character code. * * The result buffer should be filled in with a null-terminated string * that should be used to represent the character. Multi-character * results are possible, which may be useful for certain approximations * (such as using "(c)" for the copyright symbol). * * Note that we only define this prototype if this symbol isn't already * defined as a macro, which may be the case on some platforms. * Alternatively, if the function is already defined (for example, as an * inline function), the defining code can define OS_XLAT_HTML4_DEFINED, * in which case we'll also omit this prototype. * * Important: this routine provides the *default* mapping that is used * when no external character mapping file is present, and for any named * entities not defined in the mapping file. Any entities in the * mapping file, if used, will override this routine. * * A trivial implementation of this routine (that simply returns a * one-character result consisting of the original input character, * truncated to eight bits if necessary) can be used if you want to * require an external mapping file to be used for any game that * includes HTML character entities. The DOS version implements this * routine so that games will still look reasonable when played with no * mapping file present, but other systems are not required to do this. */ #ifndef os_xlat_html4 # ifndef OS_XLAT_HTML4_DEFINED void os_xlat_html4(unsigned int html4_char, char *result, size_t result_buf_len); # endif #endif /* * Generate a filename for a character-set mapping file. This function * should determine the current native character set in use, if * possible, then generate a filename, according to system-specific * conventions, that we should attempt to load to get a mapping between * the current native character set and the internal character set * identified by 'internal_id'. * * The internal character set ID is a string of up to 4 characters. * * On DOS, the native character set is a DOS code page. DOS code pages * are identified by 3- or 4-digit identifiers; for example, code page * 437 is the default US ASCII DOS code page. We generate the * character-set mapping filename by appending the internal character * set identifier to the DOS code page number, then appending ".TCP" to * the result. So, to map between ISO Latin-1 (internal ID = "La1") and * DOS code page 437, we would generate the filename "437La1.TCP". * * Note that this function should do only two things. First, determine * the current native character set that's in use. Second, generate a * filename based on the current native code page and the internal ID. * This function is NOT responsible for figuring out the mapping or * anything like that -- it's simply where we generate the correct * filename based on local convention. * * 'filename' is a buffer of at least OSFNMAX characters. * * 'argv0' is the executable filename from the original command line. * This parameter is provided so that the system code can look for * mapping files in the original TADS executables directory, if desired. */ void os_gen_charmap_filename(char *filename, char *internal_id, char *argv0); /* * Receive notification that a character mapping file has been loaded. * The caller doesn't require this routine to do anything at all; this * is purely for the system-dependent code's use so that it can take * care of any initialization that it must do after the caller has * loaded a charater mapping file. 'id' is the character set ID, and * 'ldesc' is the display name of the character set. 'sysinfo' is the * extra system information string that is stored in the mapping file; * the interpretation of this information is up to this routine. * * For reference, the Windows version uses the extra information as a * code page identifier, and chooses its default font character set to * match the code page. On DOS, the run-time requires the player to * activate an appropriate code page using a DOS command (MODE CON CP * SELECT) prior to starting the run-time, so this routine doesn't do * anything at all on DOS. */ void os_advise_load_charmap(char *id, char *ldesc, char *sysinfo); /* * Generate the name of the character set mapping table for Unicode * characters to and from the given local character set. Fills in the * buffer with the implementation-dependent name of the desired * character set map. See below for the character set ID codes. * * For example, on Windows, the implementation would obtain the * appropriate active code page (which is simply a Windows character set * identifier number) from the operating system, and build the name of * the Unicode mapping file for that code page, such as "CP1252". On * Macintosh, the implementation would look up the current script system * and return the name of the Unicode mapping for that script system, * such as "ROMAN" or "CENTEURO". * * If it is not possible to determine the specific character set that is * in use, this function should return "asc7dflt" (ASCII 7-bit default) * as the character set identifier on an ASCII system, or an appropriate * base character set name on a non-ASCII system. "asc7dflt" is the * generic character set mapping for plain ASCII characters. * * The given buffer must be at least 32 bytes long; the implementation * must limit the result it stores to 32 bytes. (We use a fixed-size * buffer in this interface for simplicity, and because there seems no * need for greater flexibility in the interface; a character set name * doesn't carry very much information so shouldn't need to be very * long. Note that this function doesn't generate a filename, but * simply a mapping name; in practice, a map name will be used to * construct a mapping file name.) * * Because this function obtains the Unicode mapping name, there is no * need to specify the internal character set to be used: the internal * character set is Unicode. */ /* * Implementation note: when porting this routine, the convention that * you use to name your mapping files is up to you. You should simply * choose a convention for this implementation, and then use the same * convention for packaging the mapping files for your OS release. In * most cases, the best convention is to use the names that the Unicode * consortium uses in their published cross-mapping listings, since * these listings can be used as the basis of the mapping files that you * include with your release. For example, on Windows, the convention * is to use the code page number to construct the map name, as in * CP1252 or CP1250. */ void os_get_charmap(char *mapname, int charmap_id); /* * Character map for the display (i.e., for the user interface). This * is the character set which is used for input read from the keyboard, * and for output displayed on the monitor or terminal. */ #define OS_CHARMAP_DISPLAY 1 /* * Character map for mapping filename strings. This should identify the * character set currently in use on the local system for filenames * (i.e., for strings identifying objects in the local file system), * providing a suitable name for use in opening a mapping file. * * On many platforms, the entire system uses only one character set at * the OS level, so the file system character set is the same as the * display character set. Some systems define a particular character * set for file system use, though, because different users might be * running applications on terminals that display different character * sets. */ #define OS_CHARMAP_FILENAME 2 /* * Default character map for file contents. On most systems, this will * be the same as display. On some systems, it won't be possible to * know in general what character set files use; in fact, this isn't * possible anywhere, since a file might have been copied without * translation from a different type of computer using a different * character set. So, this isn't meant to provide a reliable choice of * character set for any arbitrary file; it's simply meant to be a good * guess that most files on this system are likely to use. */ #define OS_CHARMAP_FILECONTENTS 3 /* * Default character map for the command line. This is the maping we use * to interpret command line arguments passed to our main() or equivalent. * On most systems, this will be the same as the display character set. */ #define OS_CHARMAP_CMDLINE 4 /* ------------------------------------------------------------------------ */ /* * External Banner Interface. This interface provides the ability to * divide the display window into multiple sub-windows, each with its own * independent contents. * * To determine where a new banner is displayed, we look at the banners as * a tree, rooted at the "main window," the special banner that the system * automatically creates initially for the main game text. We start by * allocating the entire display (or the entire application window, if * we're running on a GUI system) to the main window. We then traverse * the tree, starting with the root window's children. For each child * window, we allocate space for the child out of the parent window's * area, according to the child's alignment and size settings, and deduct * this space from the parent window's size. We then lay out the children * of the child. * * For each banner window, we take its requested space out of the parent * window's area by starting at the edge of the parent window rectangle as * indicated by the banner's alignment, and taking the requested `width * (for a left/right banner) or height (for a top/bottom banner), limiting * to the available width/height in the parent window's space. Give the * banner the full extent of the parent's space in its other dimension (so * a left/right banner gets the full height of the parent space, and a * top/bottom banner gets the full width). * * Note that the layout proceeds exclusively down the tree (i.e., from the * root to children to grandchildren, and so on). It *appears* that a * child affects its parent, because of the deduction step: a child * acquires screen space by carving out a chunk of its parent. The right * way to think about this, though, is that the parent's full area is the * union of the parent window and all of its children; when viewed this * way, the parent's full area is fully determined the instant the parent * is laid out, and never changes as its children are laid out. Note in * particular that a child can never make a parent larger; the only thing * a child can do to a parent is carve out a chunk of the parent for * itself, which doesn't affect the boundaries of the union of the parent * plus its children. * * Note also that if the banner has a border, and the implementation * actually draws borders, the border must be drawn for the *full* area of * the banner, as defined above. For example, suppose we have two * borders: banner A is a child of the main window, is top-aligned, and * has a border. Banner B is a child of banner A, right-aligned, with no * border. Obviously, without considering banner B, banner A's space runs * across the entire width of the main window, so its border (at the * bottom of its area) runs across the entire width of the main window. * Banner B carves out some space from A's right side for itself, so * banner A's actual on-screen area runs from the left edge of the main * window to banner B's left edge. However, even though banner A itself * no longer runs the full width of the main window, banner A's *full* * area - that is, the union of banner A's on-screen area and all of its * children's full areas - does still run the entire width of the main * window, hence banner A's border must still run the full width of the * main window. The simple way of looking at this is that a banner's * border is always to be drawn exactly the same way, regardless of * whether or not the banner has children - simply draw the banner as it * would be drawn if the banner had no children. * * Each time a banner is added or removed, we must recalculate the layout * of the remaining banners and main text area. The os_banner_xxx() * implementation is responsible for this layout refiguring. * * The entire external banner window interface is optional, although the * functions must at least be defined as dummies to avoid linker errors * when building. If a platform doesn't implement this feature, * os_banner_create() should simply return null, and the other routines * can do nothing. */ /* * Create a banner window. 'info' gives the desired parameters for the new * banner. * * Note that certain requested parameter settings might or might not be * respected, depending on the capabilities of the platform and user * preferences. os_banner_getinfo() can be used after creation to * determine which parameter settings are actually used in the new banner. * * 'parent' gives the parent of this banner; this is the banner handle of * another banner window, or null. If 'parent' is null, then the new * banner is a child of the main window, which the system creates * automatically at startup and which contains the main input/output * transcript. The new banner's on-screen area is carved out of the * parent's space, according to the alignment and size settings of the new * window, so this determines how the window is laid out on the screen. * * 'where' is OS_BANNER_FIRST to make the new window the first child of its * parent; OS_BANNER_LAST to make it the last child of its parent; * OS_BANNER_BEFORE to insert it immediately before the existing banner * identified by handle in 'other'; or OS_BANNER_AFTER to insert * immediately after 'other'. When BEFORE or AFTER is used, 'other' must * be another child of the same parent; if it is not, the routine should * act as though 'where' were given as OS_BANNER_LAST. * * 'other' is a banner handle for an existing banner window. This is used * to specify the relative position among children of the new banner's * parent, if 'where' is either OS_BANNER_BEFORE or OS_BANNER_AFTER. If * 'where' is OS_BANNER_FIRST or OS_BANNER_LAST, 'other' is ignored. * * 'wintype' is the type of the window. This is one of the * OS_BANNER_TYPE_xxx codes indicating what kind of window is desired. * * 'align' is the banner's alignment, given as an OS_BANNER_ALIGN_xxx * value. Top/bottom banners are horizontal: they run across the full * width of the existing main text area. Left/right banners are vertical: * they run down the full height of the existing main text area. * * 'siz' is the requested size of the new banner. The meaning of 'siz' * depends on the value of 'siz_units', which can be OS_BANNER_SIZE_PCT to * set the size as a percentage of the REMAINING space, or * OS_BANNER_SIZE_ABS to set an absolute size in the "natural" units of the * window. The natural units vary by window type: for text and text grid * windows, this is in rows/columns of '0' characters in the default font * for the window. Note that when OS_BANNER_SIZE_ABS is used in a text or * text grid window, the OS implementation MUST add the space needed for * margins and borders when determining the actual pixel size of the * window; in other words, the window should be large enough that it can * actually display the given number or rows or columns. * * The size is interpreted as a width or height according to the window's * orientation. For a TOP or BOTTOM banner, the size is the height; for a * LEFT or RIGHT banner, the size is the width. A banner has only one * dimension's size given, since the other dimension's size is determined * automatically by the layout rules. * * Note that the window's size can be changed later using * banner_size_to_contents() or banner_set_size(). * * 'style' is a combination of OS_BANNER_STYLE_xxx flags - see below. The * style flags give the REQUESTED style for the banner, which might or * might not be respected, depending on the platform's capabilities, user * preferences, and other factors. os_banner_getinfo() can be used to * determine which style flags are actually used. * * Returns the "handle" to the new banner window, which is an opaque value * that is used in subsequent os_banner_xxx calls to operate on the window. * Returns null if the window cannot be created. An implementation is not * required to support this functionality at all, and can subset it if it * does support it (for example, an implementation could support only * top/bottom-aligned banners, but not left/right-aligned), so callers must * be prepared for this routine to return null. */ void *os_banner_create(void *parent, int where, void *other, int wintype, int align, int siz, int siz_units, unsigned long style); /* * insertion positions */ #define OS_BANNER_FIRST 1 #define OS_BANNER_LAST 2 #define OS_BANNER_BEFORE 3 #define OS_BANNER_AFTER 4 /* * banner types */ /* * Normal text stream window. This is a text stream that behaves * essentially like the main text window: text is displayed to this * through os_banner_disp(), always in a stream-like fashion by adding new * text to the end of any exiting text. * * Systems that use proportional fonts should usually simply use the same * font they use by default in the main text window. However, note that * the OS_BANNER_STYLE_TAB_ALIGN style flag might imply that a fixed-pitch * font should be used even when proportional fonts are available, because * a fixed-pitch font will allow the calling code to rely on using spaces * to align text within the window. */ #define OS_BANNER_TYPE_TEXT 1 /* * "Text grid" window. This type of window is similar to an normal text * window (OS_BANNER_TYPE_TEXT), but is guaranteed to arrange its text in * a regular grid of character cells, all of the same size. This means * that the output position can be moved to an arbitrary point within the * window at any time, so the calling program can precisely control the * layout of the text in the window. * * Because the output position can be moved to arbitrary positions in the * window, it is possible to overwrite text previously displayed. When * this happens, the old text is completely obliterated by the new text, * leaving no trace of the overwritten text. * * In order to guarantee that character cells are all the same size, this * type of window does not allow any text attributes. The implementation * should simply ignore any attempts to change text attributes in this * type of window. However, colors can be used to the same degree they * can be used in an ordinary text window. * * To guarantee the regular spacing of character cells, all * implementations must use fixed-pitch fonts for these windows. This * applies even to platforms where proportional fonts are available. */ #define OS_BANNER_TYPE_TEXTGRID 2 /* * banner alignment types */ #define OS_BANNER_ALIGN_TOP 0 #define OS_BANNER_ALIGN_BOTTOM 1 #define OS_BANNER_ALIGN_LEFT 2 #define OS_BANNER_ALIGN_RIGHT 3 /* * size units */ #define OS_BANNER_SIZE_PCT 1 #define OS_BANNER_SIZE_ABS 2 /* * banner style flags */ /* * The banner has a visible border; this indicates that a line is to be * drawn to separate the banner from the adjacent window or windows * "inside" the banner. So, a top-aligned banner will have its border * drawn along its bottom edge; a left-aligned banner will show a border * along its right edge; and so forth. * * Note that character-mode platforms generally do NOT respect the border * style, since doing so takes up too much screen space. */ #define OS_BANNER_STYLE_BORDER 0x00000001 /* * The banner has a vertical/horizontal scrollbar. Character-mode * platforms generally do not support scrollbars. */ #define OS_BANNER_STYLE_VSCROLL 0x00000002 #define OS_BANNER_STYLE_HSCROLL 0x00000004 /* * Automatically scroll the banner vertically/horizontally whenever new * text is displayed in the window. In other words, whenever * os_banner_disp() is called, scroll the window so that the text that the * new cursor position after the new text is displayed is visible in the * window. * * Note that this style is independent of the presence of scrollbars. * Even if there are no scrollbars, we can still scroll the window's * contents programmatically. * * Implementations can, if desired, keep an internal buffer of the * window's contents, so that the contents can be recalled via the * scrollbars if the text displayed in the banner exceeds the space * available in the banner's window on the screen. If the implementation * does keep such a buffer, we recommend the following method for managing * this buffer. If the AUTO_VSCROLL flag is not set, then the banner's * contents should be truncated at the bottom when the contents overflow * the buffer; that is, once the banner's internal buffer is full, any new * text that the calling program attempts to add to the banner should * simply be discarded. If the AUTO_VSCROLL flag is set, then the OLDEST * text should be discarded instead, so that the most recent text is * always retained. */ #define OS_BANNER_STYLE_AUTO_VSCROLL 0x00000008 #define OS_BANNER_STYLE_AUTO_HSCROLL 0x00000010 /* * Tab-based alignment is required/supported. On creation, this is a hint * to the implementation that is sometimes necessary to determine what * kind of font to use in the new window, for non-HTML platforms. If this * flag is set on creation, the caller is indicating that it wants to use * <TAB> tags to align text in the window. * * Character-mode implementations that use a single font with fixed pitch * can simply ignore this. These implementations ALWAYS have a working * <TAB> capability, because the portable output formatter provides <TAB> * interpretation for a fixed-pitch window. * * Full HTML TADS implementations can also ignore this. HTML TADS * implementations always have full <TAB> support via the HTML * parser/renderer. * * Text-only implementations on GUI platforms (i.e., implementations that * are not based on the HTML parser/renderer engine in HTML TADS, but * which run on GUI platforms with proportionally-spaced text) should use * this flag to determine the font to display. If this flag is NOT set, * then the caller doesn't care about <TAB>, and the implementation is * free to use a proportionally-spaced font in the window if desired. * * When retrieving information on an existing banner, this flag indicates * that <TAB> alignment is actually supported on the window. */ #define OS_BANNER_STYLE_TAB_ALIGN 0x00000020 /* * Use "MORE" mode in this window. By default, a banner window should * happily allow text to overflow the vertical limits of the window; the * only special thing that should happen on overflow is that the window * should be srolled down to show the latest text, if the auto-vscroll * style is set. With this flag, though, a banner window acts just like * the main text window: when the window fills up vertically, we show a * MORE prompt (using appropriate system conventions), and wait for the * user to indicate that they're ready to see more text. On most systems, * the user acknowledges a MORE prompt by pressing a key or scrolling with * the mouse, but it's up to the system implementor to decide what's * appropriate for the system. * * Note that MORE mode in ANY banner window should generally override all * other user input focus. In other words, if the game in the main window * would like to read a keystroke from the user, but one of the banner * windows is pausing with a MORE prompt, any keyboard input should be * directed to the banner paused at the MORE prompt, not to the main * window; the main window should not receive any key events until the MORE * prompt has been removed. * * This style requires the auto-vscroll style. Implementations should * assume auto-vscroll when this style is set. This style can be ignored * with text grid windows. */ #define OS_BANNER_STYLE_MOREMODE 0x00000040 /* * This banner is a horizontal/vertical "strut" for sizing purposes. This * means that the banner's content size is taken into account when figuring * the content size of its *parent* banner. If the banner has the same * orientation as the parent, its content size is added to its parent's * internal content size to determine the parent's overall content size. * If the banner's orientation is orthogonal to the parent's, then the * parent's overall content size is the larger of the parent's internal * content size and this banner's content size. */ #define OS_BANNER_STYLE_HSTRUT 0x00000080 #define OS_BANNER_STYLE_VSTRUT 0x00000100 /* * Delete a banner. This removes the banner from the display, which * requires recalculating the entire screen's layout to reallocate this * banner's space to other windows. When this routine returns, the banner * handle is invalid and can no longer be used in any os_banner_xxx * function calls. * * If the banner has children, the children will no longer be displayed, * but will remain valid in memory until deleted. A child window's * display area always comes out of its parent's space, so once the parent * is gone, a child has no way to acquire any display space; resizing the * child won't help, since it simply has no way to obtain any screen space * once its parent has been deleted. Even though the window's children * will become invisible, their banner handles will remain valid; the * caller is responsible for explicitly deleting the children even after * deleting their parent. */ void os_banner_delete(void *banner_handle); /* * "Orphan" a banner. This tells the osifc implementation that the caller * wishes to sever all of its ties with the banner (as part of program * termination, for example), but that the calling program does not * actually require that the banner's on-screen display be immediately * removed. * * The osifc implementation can do one of two things: * * 1. Simply call os_banner_delete(). If the osifc implementation * doesn't want to do anything extra with the banner, it can simply delete * the banner, since the caller has no more use for it. * * 2. Take ownership of the banner. If the osifc implementation wishes * to continue displaying the final screen configuration after a program * has terminated, it can simply take over the banner and leave it on the * screen. The osifc subsystem must eventually delete the banner itself * if it takes this routine; for example, if the osifc subsystem allows * another client program to be loaded into the same window after a * previous program has terminated, it would want to delete any orphaned * banners from the previous program when loading a new program. */ void os_banner_orphan(void *banner_handle); /* * Banner information structure. This is filled in by the system-specific * implementation in os_banner_getinfo(). */ struct os_banner_info_t { /* alignment */ int align; /* style flags - these indicate the style flags actually in use */ unsigned long style; /* * Actual on-screen size of the banner, in rows and columns. If the * banner is displayed in a proportional font or can display multiple * fonts of different sizes, this is approximated by the number of "0" * characters in the window's default font that will fit in the * window's display area. */ int rows; int columns; /* * Actual on-screen size of the banner in pixels. This is meaningful * only for full HTML interpreter; for text-only interpreters, these * are always set to zero. * * Note that even if we're running on a GUI operating system, these * aren't meaningful unless this is a full HTML interpreter. Text-only * interpreters should always set these to zero, even on GUI OS's. */ int pix_width; int pix_height; /* * OS line wrapping flag. If this is set, the window uses OS-level * line wrapping because the window uses a proportional font, so the * caller does not need to (and should not) perform line breaking in * text displayed in the window. * * Note that OS line wrapping is a PERMANENT feature of the window. * Callers can note this information once and expect it to remain * fixed through the window's lifetime. */ int os_line_wrap; }; typedef struct os_banner_info_t os_banner_info_t; /* * Get information on the banner - fills in the information structure with * the banner's current settings. Note that this should indicate the * ACTUAL properties of the banner, not the requested properties; this * allows callers to determine how the banner is actually displayed, which * depends upon the platform's capabilities and user preferences. * * Returns true if the information was successfully obtained, false if * not. This can return false if the underlying OS window has already * been closed by a user action, for example. */ int os_banner_getinfo(void *banner_handle, os_banner_info_t *info); /* * Get the character width/height of the banner, for layout purposes. This * gives the size of the banner in character cells. * * These are not meaningful when the underlying window uses a proportional * font or varying fonts of different sizes. When the size of text varies * in the window, the OS layer is responsible for word-wrapping and other * layout, in which case these simply return zero. * * Note that these routines might appear to be redundant with the 'rows' * and 'columns' information returned from os_banner_getinfo(), but these * have two important distinctions. First, these routines return only the * width and height information, so they can be implemented with less * overhead than os_banner_getinfo(); this is important because formatters * might need to call these routines frequently while formatting text. * Second, these routines are not required to return an approximation for * windows using proportional fonts, as os_banner_getinfo() does; these can * simply return zero when a proportional font is in use. */ int os_banner_get_charwidth(void *banner_handle); int os_banner_get_charheight(void *banner_handle); /* clear the contents of a banner */ void os_banner_clear(void *banner_handle); /* * Display output on a banner. Writes the output to the window on the * display at the current output position. * * The following special characters should be recognized and handled: * * '\n' - newline; move output position to the start of the next line. * * '\r' - move output position to start of current line; subsequent text * overwrites any text previously displayed on the current line. It is * permissible to delete the old text immediately on seeing the '\r', * rather than waiting for additional text to actually overwrite it. * * All other characters should simply be displayed as ordinary printing * text characters. Note that tab characters should not be passed to this * routine, but if they are, they can simply be treated as ordinary spaces * if desired. Other control characters (backspace, escape, etc) should * never be passed to this routine; the implementation is free to ignore * any control characters not listed above. * * If any text displayed here overflows the current boundaries of the * window on the screen, the text MUST be "clipped" to the current window * boundaries; in other words, anything this routine tries to display * outside of the window's on-screen rectangle must not actually be shown * on the screen. * * Text overflowing the display boundaries MUST also be retained in an * internal buffer. This internal buffer can be limited to the actual * maximum display size of the terminal screen or application window, if * desired. It is necessary to retain clipped text, because this allows a * window to be expanded to the size of its contents AFTER the contents * have already been displayed. * * If the banner does its own line wrapping, it must indicate this via the * os_line_wrap flag in the os_banner_getinfo() return data. If the * banner doesn't indicate this flag, then it must not do any line * wrapping at all, even if the caller attempts to write text beyond the * right edge of the window - any text overflowing the width of the window * must simply be clipped. * * Text grid banners must ALWAYS clip - these banners should never perform * any line wrapping. */ void os_banner_disp(void *banner_handle, const char *txt, size_t len); /* * Set the text attributes in a banner, for subsequent text displays. * 'attr' is a (bitwise-OR'd) combination of OS_ATTR_xxx values. */ void os_banner_set_attr(void *banner_handle, int attr); /* * Set the text color in a banner, for subsequent text displays. The 'fg' * and 'bg' colors are given as RGB or parameterized colors; see the * definition of os_color_t for details. * * If the underlying renderer is HTML-enabled, then this should not be * used; the appropriate HTML code should simply be displayed to the * banner instead. */ void os_banner_set_color(void *banner_handle, os_color_t fg, os_color_t bg); /* * Set the screen color in the banner - this is analogous to the screen * color in the main text area. * * If the underlying renderer is HTML-enabled, then this should not be * used; the HTML <BODY> tag should be used instead. */ void os_banner_set_screen_color(void *banner_handle, os_color_t color); /* flush output on a banner */ void os_banner_flush(void *banner_handle); /* * Set the banner's size. The size has the same meaning as in * os_banner_create(). * * 'is_advisory' indicates whether the sizing is required or advisory only. * If this flag is false, then the size should be set as requested. If * this flag is true, it means that the caller intends to call * os_banner_size_to_contents() at some point, and that the size being set * now is for advisory purposes only. Platforms that support * size-to-contents may simply ignore advisory sizing requests, although * they might want to ensure that they have sufficient off-screen buffer * space to keep track of the requested size of display, so that the * information the caller displays in preparation for calling * size-to-contents will be retained. Platforms that do not support * size-to-contents should set the requested size even when 'is_advisory' * is true. */ void os_banner_set_size(void *banner_handle, int siz, int siz_units, int is_advisory); /* * Set the banner to the size of its current contents. This can be used * to set the banner's size after some text (or other material) has been * displayed to the banner, so that the size can be set according to the * banner's actual space requirements. * * This changes the banner's "requested size" to match the current size. * Subsequent calls to os_banner_getinfo() will thus indicate a requested * size according to the size set here. */ void os_banner_size_to_contents(void *banner_handle); /* * Turn HTML mode on/off in the banner window. If the underlying renderer * doesn't support HTML, these have no effect. */ void os_banner_start_html(void *banner_handle); void os_banner_end_html(void *banner_handle); /* * Set the output coordinates in a text grid window. The grid window is * arranged into character cells numbered from row zero, column zero for * the upper left cell. This function can only be used if the window was * created with type OS_BANNER_TYPE_TEXTGRID; the request should simply be * ignored by other window types. * * Moving the output position has no immediate effect on the display, and * does not itself affect the "content size" for the purposes of * os_banner_size_to_contents(). This simply sets the coordinates where * any subsequent text is displayed. */ void os_banner_goto(void *banner_handle, int row, int col); /* ------------------------------------------------------------------------ */ /* * Get system information. 'code' is a SYSINFO_xxx code, which * specifies what type of information to get. The 'param' argument's * meaning depends on which code is selected. 'result' is a pointer to * an integer that is to be filled in with the result value. If the * code is not known, this function should return FALSE. If the code is * known, the function should fill in *result and return TRUE. */ int os_get_sysinfo(int code, void *param, long *result); /* determine if systemInfo is supported - os_get_sysinfo never gets this */ #define SYSINFO_SYSINFO 1 /* get interpreter version number - os_get_sysinfo never gets this */ #define SYSINFO_VERSION 2 /* get operating system name - os_get_sysinfo never gets this */ #define SYSINFO_OS_NAME 3 /* * Can the system process HTML directives? returns 1 if so, 0 if not. * Note that if this returns false, then all of the codes below from * JPEG to LINKS are implicitly false as well, since TADS can only use * images, sounds, and links through HTML. */ #define SYSINFO_HTML 4 /* can the system display JPEG's? 1 if yes, 0 if no */ #define SYSINFO_JPEG 5 /* can the system display PNG's? 1 if yes, 0 if no */ #define SYSINFO_PNG 6 /* can the system play WAV's? 1 if yes, 0 if no */ #define SYSINFO_WAV 7 /* can the system play MIDI's? 1 if yes, 0 if no */ #define SYSINFO_MIDI 8 /* can the system play MIDI and WAV's simultaneously? yes=1, no=0 */ #define SYSINFO_WAV_MIDI_OVL 9 /* can the system play multiple WAV's simultaneously? yes=1, no=0 */ #define SYSINFO_WAV_OVL 10 /* * GENERAL NOTES ON PREFERENCE SETTINGS: * * Several of the selectors below refer to the preference settings. We're * talking about user-settable options to control various aspects of the * interpreter. The conventional GUI for this kind of thing is a dialog * box reachable through a menu command named something like "Options" or * "Preferences". A couple of general notes about these: * * 1. The entire existence of a preferences mechanism is optional - * interpreter writers aren't required to implement anything along these * lines. In some cases the local platforms might not have any suitable * conventions for a preferences UI (e.g., character-mode console * applications), and in other cases the terp developer might just want to * omit a prefs mechanism because of the work involved to implement it, or * to keep the UI simpler. * * 2. If a given SYSINFO_PREF_xxx selector refers to a preference item * that's not implemented in the local interpreter, the terp should simply * return a suitable default result. For example, if the interpreter * doesn't have a preference item to allow the user to turn sounds off, the * selector SYSINFO_PREF_SOUNDS should return 1 to indicate that the user * has not in fact turned off sounds (because there's no way to do so). * * 3. The various SYSINFO_PREF_xxx selectors are purely queries - they're * NOT a mechanism for enforcing the preferences. For example, if the * interpreter provides a "Sounds On/Off" option, it's up to the terp to * enforce it the Off setting by ignoring any sound playback requests. The * game isn't under any obligation to query any of the preferences or to * alter its behavior based on their settings - you should expect that the * game will go on trying to play sounds even when "Sounds Off" is selected * in the preferences. The purpose of these SYSINFO selectors is to let * the game determine the current presentation status, but *only if it * cares*. For example, the game might determine whether or not sounds are * actually being heard just before playing a sound effect that's important * to the progress of the game, so that it can provide a visual alternative * if the effect won't be heard. */ /* * Get image preference setting - 1 = images can be displayed, 0 = images * are not being displayed because the user turned off images in the * preferences. This is, of course, irrelevant if images can't be * displayed at all. * * See the general notes on preferences queries above. */ #define SYSINFO_PREF_IMAGES 11 /* * Get digitized sound effect (WAV) preference setting - 1 = sounds can be * played, 0 = sounds are not being played because the user turned off * sounds in the preferences. * * See the general notes on preferences queries above. */ #define SYSINFO_PREF_SOUNDS 12 /* * Get music (MIDI) preference setting - 1 = music can be played, 0 = music * is not being played because the user turned off music in the * preferences. * * See the general notes on preferences queries above. */ #define SYSINFO_PREF_MUSIC 13 /* * Get link display preference setting - 0 = links are not being displayed * because the user set a preference item that suppresses all links (which * doesn't actually hide them, but merely displays them and otherwise * treats them as ordinary text). 1 = links are to be displayed normally. * 2 = links can be displayed temporarily by the user by pressing a key or * some similar action, but aren't being displayed at all times. * * See the general note on preferences queries above. */ #define SYSINFO_PREF_LINKS 14 /* can the system play MPEG sounds of any kind? */ #define SYSINFO_MPEG 15 /* can the system play MPEG audio 2.0 layer I/II/III sounds? */ #define SYSINFO_MPEG1 16 #define SYSINFO_MPEG2 17 #define SYSINFO_MPEG3 18 /* * is the system *currently* in HTML mode? os_get_sysinfo never gets * this code, since the portable output layer keeps track of this */ #define SYSINFO_HTML_MODE 19 /* * Does the system allow following external URL links of the various * types? These return true if the system is capable of following these * types of hypertext links, false if not. Note that, if the system is * capable of following these links, these should return true regardless * of any current mode settings; in particular, these should not be * sensitive to the current HTML mode or the current link display mode, * since the question is not whether a link now displayed can be * followed by the user, but rather whether the system has the * capability to follow these types of links at all. */ #define SYSINFO_LINKS_HTTP 20 #define SYSINFO_LINKS_FTP 21 #define SYSINFO_LINKS_NEWS 22 #define SYSINFO_LINKS_MAILTO 23 #define SYSINFO_LINKS_TELNET 24 /* is PNG transparency supported? */ #define SYSINFO_PNG_TRANS 25 /* is PNG alpha blending supported? */ #define SYSINFO_PNG_ALPHA 26 /* is the Ogg Vorbis audio format supported? */ #define SYSINFO_OGG 27 /* can the system display MNG's? */ #define SYSINFO_MNG 28 /* can the system display MNG's with transparency? */ #define SYSINFO_MNG_TRANS 29 /* can the system display MNG's with alpha blending? */ #define SYSINFO_MNG_ALPHA 30 /* can we display highlighted text in its own appearance? */ #define SYSINFO_TEXT_HILITE 31 /* * Can we display text colors? This returns a SYSINFO_TXC_xxx code * indicating the level of color support. * * The os_xxx interfaces don't presently support anything beyond the ANSI * colors; however, HTML-enabled interpreters generally support full RGB * colors, so we call this out as a separate level. */ #define SYSINFO_TEXT_COLORS 32 /* no text color support */ #define SYSINFO_TXC_NONE 0 /* parameterized color names only (OS_COLOR_P_TEXT, etc) */ #define SYSINFO_TXC_PARAM 1 /* * we support only the basic ANSI colors, foreground control only (white, * black, blue, red, green, yellow, cyan, magenta) */ #define SYSINFO_TXC_ANSI_FG 2 /* ANSI colors, foreground and background */ #define SYSINFO_TXC_ANSI_FGBG 3 /* full RGB support */ #define SYSINFO_TXC_RGB 4 /* are the os_banner_xxx() interfaces supported? */ #define SYSINFO_BANNERS 33 /* Interpreter Class - this returns one of the SYSINFO_ICLASS_xxx codes */ #define SYSINFO_INTERP_CLASS 34 /* * Interpreter class: Character-mode Text-Only. Interpreters of this class * use a single, fixed-pitch font to display all text, and use the * text-only HTML subset, and cannot display graphics. */ #define SYSINFO_ICLASS_TEXT 1 /* * Interpreter class: Text-Only GUI. Interpreters of this class are * traditional text-only interpreters running on graphical operating * systems. These interpreters might use multiple fonts (for example, they * might display highlighted text in boldface), and might use * proportionally-spaced text for some windows. These interpreters support * the text-only HTML subset, and cannot display graphics. * * Text-only GUI interpreters act essentially the same as character-mode * text-only interpreters, from the perspective of the client program. */ #define SYSINFO_ICLASS_TEXTGUI 2 /* * Interpreter class: HTML. Interpreters of this class can display * graphics and sounds, can display multiple fonts and font sizes, can use * proportional fonts, and support the full HTML TADS markup language for * formatting. */ #define SYSINFO_ICLASS_HTML 3 /* * Audio fade information. * * SYSINFO_AUDIO_FADE: basic fade-in and fade-out support. Interpreters * that don't support audio fade at all should return 0. Interpreters that * support fades should return a bitwise OR'd combination of * SYSINFO_AUDIOFADE_xxx flags below indicating which formats can be used * with fades. * * SYSINFO_AUDIO_CROSSFADE: cross-fades are supported (i.e., simultaneous * play of overlapping tracks, one fading out while the other fades in). * If cross-fades aren't supported, return 0. If they're supported, return * a combination of SYSINFO_AUDIOFADE_xxx flags indicating which formats * can be used with cross-fades. */ #define SYSINFO_AUDIO_FADE 35 #define SYSINFO_AUDIO_CROSSFADE 36 /* * Specific audio fading features. These are bit flags that can be * combined to indicate the fading capabilities of the interpreter. */ #define SYSINFO_AUDIOFADE_MPEG 0x0001 /* supported for MPEG audio */ #define SYSINFO_AUDIOFADE_OGG 0x0002 /* supported for Ogg Vorbis */ #define SYSINFO_AUDIOFADE_WAV 0x0004 /* supported for WAV */ #define SYSINFO_AUDIOFADE_MIDI 0x0008 /* supported for MIDI */ /* ------------------------------------------------------------------------ */ /* * Integer division operators. For any compiler that follows ANSI C * rules, no definitions are required for these routine, since the * standard definitions below will work properly. However, if your * compiler does not follow ANSI standards with respect to integer * division of negative numbers, you must provide implementations of * these routines that produce the correct results. * * Division must "truncate towards zero," which means that any * fractional part is dropped from the result. If the result is * positive, the result must be the largest integer less than the * algebraic result: 11/3 yields 3. If the result is negative, the * result must be the smallest integer less than the result: (-11)/3 * yields -3. * * The remainder must obey the relationship (a/b)*b + a%b == a, for any * integers a and b (b != 0). * * If your compiler does not obey the ANSI rules for the division * operators, make the following changes in your osxxx.h file * * - define the symbol OS_NON_ANSI_DIVIDE in the osxxx.h file * * - either define your own macros for os_divide_long() and * os_remainder_long(), or put actual prototypes for these functions * into your osxxx.h file and write appropriate implementations of these * functions in one of your osxxx.c or .cpp files. */ /* long os_divide_long(long a, long b); // returns (a/b) with ANSI rules */ /* long os_remainder_long(long a, long b); // returns (a%b) with ANSI rules */ /* standard definitions for any ANSI compiler */ #ifndef OS_NON_ANSI_DIVIDE #define os_divide_long(a, b) ((a) / (b)) #define os_remainder_long(a, b) ((a) % (b)) #endif /* ------------------------------------------------------------------------ */ /* * Special "switch" statement optimization flags. These definitions are * OPTIONAL - if a platform doesn't provide these definitions, suitable * code that's fully portable will be used. * * On some compilers, the performance of a "switch" statement can be * improved by fully populating the switch with all possible "case" * values. When the compiler can safely assume that every possible "case" * value is specifically called out in the switch, the compiler can * generate somewhat faster code by omitting any range check for the * controlling expression of the switch: a range check is unnecessary * because the compiler knows that the value can never be outside the * "case" table. * * This type of optimization doesn't apply to all compilers. When a given * platform's compiler can be coerced to produce faster "switch" * statements, though, there might be some benefit in defining these * symbols according to local platform rules. * * First, #define OS_FILL_OUT_CASE_TABLES if you want this type of switch * optimization at all. This symbol is merely a flag, so it doesn't need * a value - #defining it is enough to activate the special code. If you * don't define this symbol, then the code that cares about this will * simply generate ordinary switches, leaving holes in the case table and * using "default:" to cover them. If the platform's osxxx.h header does * #define OS_FILL_OUT_CASE_TABLES, then the portable code will know to * fill out case tables with all possible alternatives where it's possible * and potentially beneficial to do so. * * Second, if you #define OS_FILL_OUT_CASE_TABLES, you MUST ALSO #define * OS_IMPOSSIBLE_DEFAULT_CASE. The value for this symbol must be some * code to insert into a "switch" statement at the point where a * "default:" case would normally go. On some compilers, special * extensions allow the program to explicitly indicate within a switch * that all possible cases are covered, so that the compiler doesn't have * to be relied upon to infer this for itself (which would be impossible * for it to do in many cases anyway). You can use an empty definition * for this symbol if your compiler doesn't have any special construct for * indicating a fully-populated case table. * * Note that *most* switch statements in portable code won't care one way * or the other about these definitions. There's a time/space trade-off * in fully populating a switch's case table, so only the most * time-critical code will bother trying. */ /* ------------------------------------------------------------------------ */ /* * TADS 2 swapping configuration. Define OS_DEFAULT_SWAP_ENABLED to 0 * if you want swapping turned off, 1 if you want it turned on. If we * haven't defined a default swapping mode yet, turn swapping on by * default. */ #ifndef OS_DEFAULT_SWAP_ENABLED # define OS_DEFAULT_SWAP_ENABLED 1 #endif /* * If the system "long description" (for the banner) isn't defined, make * it the same as the platform ID string. */ #ifndef OS_SYSTEM_LDESC # define OS_SYSTEM_LDESC OS_SYSTEM_NAME #endif /* * TADS 2 Usage Lines * * If the "usage" lines (i.e., the descriptive lines of text describing * how to run the various programs) haven't been otherwise defined, * define defaults for them here. Some platforms use names other than * tc, tr, and tdb for the tools (for example, on Unix they're usually * tadsc, tadsr, and tadsdb), so the usage lines should be adjusted * accordingly; simply define them earlier than this point (in this file * or in one of the files included by this file, such as osunixt.h) for * non-default text. */ #ifndef OS_TC_USAGE # define OS_TC_USAGE "usage: tc [options] file" #endif #ifndef OS_TR_USAGE # define OS_TR_USAGE "usage: tr [options] file" #endif #ifndef OS_TDB_USAGE # define OS_TDB_USAGE "usage: tdb [options] file" #endif /* * Ports can define a special TDB startup message, which is displayed * after the copyright/version banner. If it's not defined at this * point, define it to an empty string. */ #ifndef OS_TDB_STARTUP_MSG # define OS_TDB_STARTUP_MSG "" #endif /* * If a system patch sub-level isn't defined, define it here as zero. * The patch sub-level is used on some systems where a number of ports * are derived from a base port (for example, a large number of ports * are based on the generic Unix port). For platforms like the Mac, * where the porting work applies only to that one platform, this * sub-level isn't meaningful. */ #ifndef OS_SYSTEM_PATCHSUBLVL # define OS_SYSTEM_PATCHSUBLVL "0" #endif #ifdef __cplusplus } #endif #endif /* OSIFC_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads2/osifcext.h������������������������������������������������������������������������0000664�0000000�0000000�00000014061�12650170723�0015654�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2005 by Michael J. Roberts. All Rights Reserved. */ /* Name osifcext.h - tads OS interface extensions Function This header defines some extensions to the portable interfaces to the OS, beyond the interfaces provided in osifc.h. These functions are optional extensions, for use in tads 3: platforms generally only need to implement these functions if the tads 3 tads-io-ext function set is used. Notes Modified 03/02/05 MJRoberts - Creation */ #ifndef OSIFCEXT_H #define OSIFCEXT_H /* if we're in C++ mode, explicitly specify C-style linkage */ #ifdef __cplusplus extern "C" { #endif /* ------------------------------------------------------------------------ */ /* * Open a popup menu window. The new window should be opened at the given * x,y coordinates (given in pixels, relative to the top left of the main * game window), and should be drawn in a style consistent with local * system conventions for context menus. If 'default_pos' is true, ignore * the x,y coordinates and place the menu at a suitable default position, * according to local conventions; for example, on Windows, the default * position for a context menu is at the current mouse position. The given * HTML text is to be used as the contents of the new window. The window * should be sized to fit the contents. * * The window should have essentially the same lifetime as a context menu * would have. That is, if the user clicks the mouse outside of the popup, * the popup should be automatically closed. If the user clicks on a * hyperlink in the popup, the hyperlink should fire its event as normal, * and the popup window should be closed. * * If we successfully show the menu, and the user clicks on a hyperlink * item from the menu, we'll return OSPOP_HREF, and we'll fill in *evt with * the event information for the hyperlink selection. If we successfully * show the menu, but the user clicks outside the menu or otherwise cancels * the menu without making a selection, we'll return OSPOP_CANCEL. If we * can't show the menu, we'll return OSPOP_FAIL. If the application is * shut down while we're showing the menu (because the user closes the app, * for example, or because the system is shutting down) we'll return * OSPOP_EOF. */ int os_show_popup_menu(int default_pos, int x, int y, const char *txt, size_t txtlen, union os_event_info_t *evt); #define OSPOP_FAIL 0 /* can't show popup menu */ #define OSPOP_HREF 1 /* user clicked on a hyperlink in the menu */ #define OSPOP_CANCEL 2 /* user canceled without making a selection */ #define OSPOP_EOF 3 /* application is closing */ /* close the extern "C" if we're in C++ mode */ #ifdef __cplusplus } #endif /* ------------------------------------------------------------------------ */ /* * Enable/disable a System Menu Command event in os_get_event(). * * Some interpreters provide menu commands for the common "system" commands * that most games support, such as SAVE, RESTORE, UNDO, and QUIT. The * older interpreters didn't have any support for a menu interface, so the * standard handling of these menu commands is a bit of a hack: we simply * write the literal text of the command ("save", "restore", etc) into the * command-line buffer, as though the player had actually typed the * command, and then return the command line from os_gets(). This works * fine as far as it goes, but it has one big restriction: there's no way * to get these system command events from os_get_event(). * * This function gives os_get_event() access to the system menu commands. * The system commands can be enabled individually; this is important * because it allows the GUI to show the commands as appropriately enabled * or disabled in menus and toolbars and whatever other widgets are used. * Once a command is enabled, then whenever we're blocked in a UI read in * os_get_event(), the command will be shown as enabled in the visual UI; * if the user then selects the command, it will be returned from * os_get_event() via an OS_EVT_COMMAND event. * * 'id' is an OS_CMD_xxx value identifying which event is to be enabled or * disabled. Once set, an event's status will remain as given until the * next call to this routine with the same ID. * * 'status' is a combination of OS_CMDSTAT_xxx flags specifying the event's * status. By default, all events are *disabled* for os_get_event(), and * all events are *enabled* for os_gets(). You can control the * os_get_event() and os_gets() status of the event separately. If you * don't include an OS_CMDSTAT_EVT_xxx bit in the flags, the current * os_get_event() status is left unchanged; likewise, if you don't include * an OS_CMDSTAT_GETS_xxx bit, the os_gets() status is left unchanged. */ void os_enable_cmd_event(int id, unsigned int status); /* ------------------------------------------------------------------------ */ /* * Command status codes for os_enable_cmd_event. * * Note that if the ENA and DIS bit for the same type are both set, the ENA * takes precedence. * * (OS implementors: note that each ENA/DIS pair consists of a "select" bit * and an "enable" bit. If the "select" bit for a pair is missing, then * you don't need to update the corresponding status bit. If the "select" * bit is present, then simply set the corresponding status bit to the * "enable" bit value.) */ /* enable/disable the event for os_get_event() */ #define OS_CMDSTAT_EVT_ENA (OSCS_EVT_SEL | OSCS_EVT_ON) #define OS_CMDSTAT_EVT_DIS (OSCS_EVT_SEL | 0x0000) /* enable/disable the event for os_gets() */ #define OS_CMDSTAT_GETS_ENA (OSCS_GETS_SEL | OSCS_GETS_ON) #define OS_CMDSTAT_GETS_DIS (OSCS_GETS_SEL | 0x0000) /* internal use: SELECT and ON bits for OS_CMDSTAT_xxx_yyy */ #define OSCS_EVT_SEL 0x0001 #define OSCS_EVT_ON 0x0002 #define OSCS_GTS_SEL 0x0004 #define OSCS_GTS_ON 0x0008 #endif /* OSIFCEXT_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads2/osifctyp.h������������������������������������������������������������������������0000664�0000000�0000000�00000004515�12650170723�0015673�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 1997, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name osifctyp.h - TADS OS interface datatype definitions Function Defines certain datatypes used in the TADS operating system interface Notes DO NOT MODIFY THIS FILE WITH PLATFORM-SPECIFIC DEFINITIONS. This file defines types that are part of the portable interface; these types do not vary by platform. These types are defined separately from osifc.h so that they can be #include'd before the OS-specific header. osifc.h is included after the OS-specific header. */ /* ------------------------------------------------------------------------ */ /* * File types. These type codes are used when opening or creating a * file, so that the OS routine can set appropriate file system metadata * to describe or find the file type. * * The type os_filetype_t is defined for documentary purposes; it's * always just an int. */ typedef int os_filetype_t; #define OSFTGAME 0 /* a game data file (.gam) */ #define OSFTSAVE 1 /* a saved game (.sav) */ #define OSFTLOG 2 /* a transcript (log) file */ #define OSFTSWAP 3 /* swap file */ #define OSFTDATA 4 /* user data file (used with the TADS fopen() call) */ #define OSFTCMD 5 /* QA command/log file */ #define OSFTERRS 6 /* error message file */ #define OSFTTEXT 7 /* text file - used for source files */ #define OSFTBIN 8 /* binary file of unknown type - resources, etc */ #define OSFTCMAP 9 /* character mapping file */ #define OSFTPREF 10 /* preferences file */ #define OSFTUNK 11 /* unknown - as a filter, matches any file type */ #define OSFTT3IMG 12 /* T3 image file (.t3 - formerly .t3x) */ #define OSFTT3OBJ 13 /* T3 object file (.t3o) */ #define OSFTT3SYM 14 /* T3 symbol export file (.t3s) */ #define OSFTT3SAV 15 /* T3 saved state file (.t3v) */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads2/osnoui.c��������������������������������������������������������������������������0000664�0000000�0000000�00000272422�12650170723�0015346�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/OSNOUI.C,v 1.3 1999/07/11 00:46:30 MJRoberts Exp $"; #endif /* * Copyright (c) 1997, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name osnoui.c - general-purpose implementations of OS routines with no UI Function This file provides implementations for certain OS routines that have no UI component and can be implemented in general for a range of operating systems. Notes Modified 04/11/99 CNebel - Improve const-ness; fix C++ errors. 11/02/97 MJRoberts - Creation */ #include <stdio.h> #ifdef MSDOS #include <io.h> #endif #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <ctype.h> #include <time.h> #include <sys/stat.h> #ifdef T_WIN32 #include <Windows.h> #include <direct.h> #endif #include "os.h" #include "run.h" /* ------------------------------------------------------------------------ */ /* * Safe strcpy */ void safe_strcpyl(char *dst, size_t dstlen, const char *src, size_t srclen) { size_t copylen; /* do nothing if there's no output buffer */ if (dst == 0 || dstlen == 0) return; /* do nothing if the source and destination buffers are the same */ if (dst == src) return; /* use an empty string if given a null string */ if (src == 0) src = ""; /* * figure the copy length - use the smaller of the actual string size * or the available buffer size, minus one for the null terminator */ copylen = srclen; if (copylen > dstlen - 1) copylen = dstlen - 1; /* copy the string (or as much as we can) */ memcpy(dst, src, copylen); /* null-terminate it */ dst[copylen] = '\0'; } void safe_strcpy(char *dst, size_t dstlen, const char *src) { safe_strcpyl(dst, dstlen, src, src != 0 ? strlen(src) : 0); } /* ------------------------------------------------------------------------ */ #define ispathchar(c) \ ((c) == OSPATHCHAR || ((c) != 0 && strchr(OSPATHALT, c) != 0)) /* * Ports with MS-DOS-like file systems (Atari ST, OS/2, Macintosh, and, * of course, MS-DOS itself) can use the os_defext and os_remext * routines below by defining USE_DOSEXT. Unix and VMS filenames will * also be parsed correctly by these implementations, but untranslated * VMS logical names may not be. */ #ifdef USE_DOSEXT /* * os_defext(fn, ext) should append the default extension ext to the * filename in fn. It is assumed that the buffer at fn is big enough to * hold the added characters in the extension. The result should be * null-terminated. When an extension is already present in the * filename at fn, no action should be taken. On systems without an * analogue of extensions, this routine should do nothing. */ #ifndef OSNOUI_OMIT_OS_DEFEXT void os_defext(char *fn, const char *ext) { char *p; /* * Scan backwards from the end of the string, looking for the last * dot ('.') in the filename. Stop if we encounter a path separator * character of some kind, because that means we reached the start * of the filename without encountering a period. */ p = fn + strlen(fn); while (p > fn) { /* on to the previous character */ p--; /* * if it's a period, return without doing anything - this * filename already has an extension, so don't apply a default */ if (*p == '.') return; /* * if this is a path separator character, we're no longer in the * filename, so stop looking for a period */ if (ispathchar(*p)) break; } /* we didn't find an extension - add the dot and the extension */ strcat(fn, "."); strcat(fn, ext); } #endif /* * Add an extension, even if the filename currently has one */ #ifndef OSNOUI_OMIT_OS_ADDEXT void os_addext(char *fn, const char *ext) { if (strlen(fn) + 1 + strlen(ext) + 1 < OSFNMAX) { strcat(fn, "."); strcat(fn, ext); } } #endif /* * os_remext(fn) removes the extension from fn, if present. The buffer * at fn should be modified in place. If no extension is present, no * action should be taken. For systems without an analogue of * extensions, this routine should do nothing. */ #ifndef OSNOUI_OMIT_OS_REMEXT void os_remext(char *fn) { char *p; /* scan backwards from the end of the string, looking for a dot */ p = fn + strlen(fn); while ( p>fn ) { /* move to the previous character */ p--; /* if it's a period, we've found the extension */ if ( *p=='.' ) { /* terminate the string here to remove the extension */ *p = '\0'; /* we're done */ return; } /* * if this is a path separator, there's no extension, so we can * stop looking */ if (ispathchar(*p)) return; } } #endif /* * Get a pointer to the root name portion of a filename. Note that this * implementation is included in the ifdef USE_DOSEXT section, since it * seems safe to assume that if a platform has filenames that are * sufficiently DOS-like for the extension parsing routines, the same * will be true of path parsing. */ #ifndef OSNOUI_OMIT_OS_GET_ROOTNAME char *os_get_root_name(const char *buf) { const char *rootname; #ifdef MSDOS const char *orig = buf; #endif /* scan the name for path separators */ for (rootname = buf ; *buf != '\0' ; ++buf) { /* if this is a path separator, remember it */ if (ispathchar(*buf)) { #ifdef MSDOS /* * if this is the last character in the name, and it's beyond * the second character, assume it's a device name colon rather * than a path separator colon */ if (*buf == ':' && buf > orig + 1 && buf[1] == '\0') continue; #endif /* * It's a path separators - for now, assume the root will * start at the next character after this separator. If we * find another separator later, we'll forget about this one * and use the later one instead. */ rootname = buf + 1; } } /* * Return the last root name candidate that we found. (Cast it to * non-const for the caller's convenience: *we're* not allowed to * modify this buffer, but the caller is certainly free to pass in a * writable buffer, and they're free to write to it after we return.) */ return (char *)rootname; } #endif /* * Extract the path from a filename */ #ifndef OSNOUI_OMIT_OS_GET_PATH_NAME void os_get_path_name(char *pathbuf, size_t pathbuflen, const char *fname) { const char *lastsep; const char *p; size_t len; int root_path; #ifdef MSDOS /* * Special case for DOS/Windows: If we have a bare drive letter, or an * explicit current directory path on a drive letter (X:.), there is no * parent directory. Our normal algorithm won't catch this because of * our special handling that adds the working directory to a bare drive * letter (turning "X:" into "X:."). */ if (isalpha(fname[0]) && fname[1] == ':' && (fname[2] == '\0' || (fname[2] == '.' && fname[3] == '\0'))) { if (pathbuflen > 0) pathbuf[0] = '\0'; return; } #endif /* find the last separator in the filename */ for (p = fname, lastsep = fname ; *p != '\0' ; ++p) { /* * If it's a path separator character, remember it as the last one * we've found so far. However, don't count it if it's the last * separator - i.e., if only more path separators follow. */ if (ispathchar(*p)) { const char *q; /* skip any immediately adjacent path separators */ for (q = p + 1 ; *q != '\0' && ispathchar(*q) ; ++q) ; /* if we found more following, *p is the last separator */ if (*q != '\0') lastsep = p; } } /* get the length of the prefix, not including the separator */ len = lastsep - fname; /* * Normally, we don't include the last path separator in the path; for * example, on Unix, the path of "/a/b/c" is "/a/b", not "/a/b/". * However, on Unix/DOS-like file systems, a root path *does* require * the last path separator: the path of "/a" is "/", not an empty * string. So, we need to check to see if the file is in a root path, * and if so, include the final path separator character in the path. */ for (p = fname, root_path = TRUE ; p != lastsep ; ++p) { /* * if this is NOT a path separator character, we don't have all * path separator characters before the filename, so we don't have * a root path */ if (!ispathchar(*p)) { /* note that we don't have a root path */ root_path = FALSE; /* no need to look any further */ break; } } /* if we have a root path, keep the final path separator in the path */ if (root_path && ispathchar(fname[len])) ++len; #ifdef MSDOS /* * On DOS, we have a special case: if the path is of the form "x:\", * where "x" is any letter, then we have a root filename and we want to * include the backslash. */ if (lastsep == fname + 2 && isalpha(fname[0]) && fname[1] == ':' && fname[2] == '\\') { /* we have an absolute path - use the full "x:\" sequence */ len = 3; /* if this was the entire path name, we have no parent */ if (fname[3] == '\0') len = 0; } /* if the path is of the form X:, keep the trailing colon */ if (len == 1 && *lastsep == ':') len = 2; #endif /* make sure it fits in our buffer (with a null terminator) */ if (len > pathbuflen - 1) len = pathbuflen - 1; /* copy it and null-terminate it */ memcpy(pathbuf, fname, len); pathbuf[len] = '\0'; #ifdef MSDOS /* * Another DOS special case: if the path is of the form "X:", with no * directory element, it refers to the current working directory on * drive X: (DOS/Win keep a working directory per drive letter). To * make this more explicit, change it to "X:.", so that we actually * have the name of a directory. */ if (lastsep == fname + 1 && pathbuf[1] == ':' && pathbuflen >= 4) { pathbuf[2] = '.'; pathbuf[3] = '\0'; } #endif } #endif /* * Canonicalize a path: remove ".." and "." relative elements */ #if !defined(OSNOUI_OMIT_OS_BUILD_FULL_PATH) && !defined(OSNOUI_OMIT_OS_COMBINE_PATHS) void canonicalize_path(char *path) { char *orig = path; char *p; char *ele, *prvele; char *trimpt = 0; int dotcnt = 0; /* * First, skip the root element. This is unremovable. For Unix, the * root element is simply a leading slash (or series of slashes). For * DOS/Win, we can also have drive letters and UNC paths (as in * \\MACHINE\ROOT). */ #ifdef MSDOS /* check for a UNC path or drive letter */ if (path[0] == '\\' && path[1] == '\\') { /* skip to the \ after the machine name */ for (path += 2 ; *path != '\0' && *path != '\\' && *path != '/' ; ++path) ; /* * further skip to the slash after that, as the drive name is part * of the root as well */ if (*path != '\0') { /* find the slash */ for (++path ; *path != '\0' && *path != '\\' && *path != '/' ; ++path) ; /* we can trim this slash if the path ends here */ trimpt = path; } } else if (path[1] == ':' && isalpha(path[0])) { /* skip the drive letter */ path += 2; } #endif /* skip leading slashes */ for ( ; ispathchar(*path) ; ++path) ; /* * if we didn't choose a different trim starting point, trim from after * the root slashes */ if (trimpt == 0) trimpt = path; /* scan elements */ for (p = ele = path, prvele = 0 ; *ele != '\0' ; ) { size_t ele_len; /* find the end of the current element */ for ( ; *p != '\0' && !ispathchar(*p) ; ++p) ; ele_len = p - ele; /* skip adjacent path separators */ for ( ; ispathchar(*p) ; ++p) ; /* check for special elements */ if (ele_len == 1 && ele[0] == '.') { /* '.' -> current directory; simply remove this element */ ++dotcnt; memmove(ele, p, strlen(p) + 1); /* revisit this element */ p = ele; } else if (ele_len == 2 && ele[0] == '.' && ele[1] == '.') { /* * '..' -> parent directory; remove this element and the * previous element. If there is no previous element, or the * previous element is a DOS drive letter or root path slash, * we can't remove it. Leaving a '..' at the root level will * result in an invalid path, but it would change the meaning * to remove it, so leave it intact. * * There's also a special case if the previous element is * another '..'. If so, it means this was an unremovable '..', * so we can't do any combining - we need to keep this '..' * AND the previous '..' to retain the meaning. */ ++dotcnt; if (prvele == 0 || ispathchar(*prvele) || (prvele[0] == '.' && prvele[1] == '.' && ispathchar(prvele[2])) #ifdef MSDOS || (prvele == path && path[1] == ':' && isalpha(path[0])) #endif ) prvele = 0; /* remove the previous element */ if (prvele != 0) { /* remove the element */ memmove(prvele, p, strlen(p) + 1); /* * start the scan over from the beginning; this is a little * inefficient, but it's the easiest way to ensure * accuracy, since we'd otherwise have to back up to find * the previous element, which can be tricky */ p = ele = path; prvele = 0; } else { /* * no previous element, so keep the .. as it is and move on * to the next element */ prvele = ele; ele = p; } } else { /* ordinary element - move on to the next element */ prvele = ele; ele = p; } } /* if we left any trailing slashes, remove them */ for (p = trimpt + strlen(trimpt) ; p > trimpt && ispathchar(*(p-1)) ; *--p = '\0') ; /* * If that left us with an empty string, and we had one or more "." or * ".." elements, the "." and ".." elements must have canceled out the * other elements. In this case return "." as the result. */ if (orig[0] == '\0' && dotcnt != 0) { orig[0] = '.'; orig[1] = '\0'; } } #endif /* * General path builder for os_build_full_path() and os_combine_paths(). * The two versions do the same work, except that the former canonicalizes * the result (resolving "." and ".." in the last element, for example), * while the latter just builds the combined path literally. */ #if !defined(OSNOUI_OMIT_OS_BUILD_FULL_PATH) && !defined(OSNOUI_OMIT_OS_COMBINE_PATHS) static void build_path(char *fullpathbuf, size_t fullpathbuflen, const char *path, const char *filename, int canon) { size_t plen, flen; int add_sep; /* presume we'll copy the entire path */ plen = strlen(path); #ifdef MSDOS /* * On DOS, there's a special case involving root paths without drive * letters. If the filename starts with a slash, but doesn't look like * a UNC-style name (\\MACHINE\NAME), it's an absolute path on a * relative drive. This means that we need to copy the drive letter or * UNC prefix from 'path' and add the root path from 'filename'. */ if ((filename[0] == '\\' || filename[0] == '/') && !(filename[1] == '\\' && filename[1] == '/')) { const char *p; /* * 'filename' is a root path without a drive letter. Determine if * 'path' starts with a drive letter or a UNC path. */ if (isalpha(path[0]) && path[1] == ':') { /* drive letter */ plen = 2; } else if ((path[0] == '\\' || path[0] == '/') && (path[1] == '\\' || path[1] == '/')) { /* * UNC-style name - copy the whole \\MACHINE\PATH part. Look * for the first path separator after the machine name... */ for (p = path + 2 ; *p != '\0' && *p != '\\' && *p != '/' ; ++p) ; /* ...now look for the next path separator after that */ if (*p != '\0') for (++p ; *p != '\0' && *p != '\\' && *p != '/' ; ++p) ; /* copy everything up to but not including the second separator */ plen = p - path; } else { /* * we have a root path on the filename side, but no drive * letter or UNC prefix on the path side, so there's nothing to * add to the filename */ plen = 0; } } /* * There's a second special case for DOS. If the filename has a drive * letter with a relative path (e.g., "d:asdf" - no leading backslash), * we can't apply any of the path. This sort of path isn't absolute, * in that it depends upon the working directory on the drive, but it's * also not relative, in that it does specify a full directory rather * than a path fragment to add to a root path. */ if (isalpha(filename[0]) && filename[1] == ':' && filename[2] != '\\' && filename[2] != '/') { /* the file has a drive letter - we can't combine it with the path */ plen = 0; } #endif /* if the filename is an absolute path already, leave it as is */ if (os_is_file_absolute(filename)) plen = 0; /* * Note whether we need to add a separator. If the path prefix ends in * a separator, don't add another; otherwise, add the standard system * separator character. * * Don't add a separator if the path is completely empty, since this * simply means that we want to use the current directory. */ add_sep = (plen != 0 && path[plen] == '\0' && !ispathchar(path[plen-1])); /* copy the path to the full path buffer, limiting to the buffer length */ if (plen > fullpathbuflen - 1) plen = fullpathbuflen - 1; memcpy(fullpathbuf, path, plen); /* add the path separator if necessary (and if there's room) */ if (add_sep && plen + 2 < fullpathbuflen) fullpathbuf[plen++] = OSPATHCHAR; /* add the filename after the path, if there's room */ flen = strlen(filename); if (flen > fullpathbuflen - plen - 1) flen = fullpathbuflen - plen - 1; memcpy(fullpathbuf + plen, filename, flen); /* add a null terminator */ fullpathbuf[plen + flen] = '\0'; /* if desired, canonicalize the result */ if (canon) canonicalize_path(fullpathbuf); } #endif /* * Build a full path out of path and filename components, returning the * result in canonical form (e.g., with '.' and '..' resolved). */ #ifndef OSNOUI_OMIT_OS_BUILD_FULL_PATH void os_build_full_path(char *fullpathbuf, size_t fullpathbuflen, const char *path, const char *filename) { /* build the combined path, and canonicalize the result */ build_path(fullpathbuf, fullpathbuflen, path, filename, TRUE); } #endif /* * Build a combined path, returning the literal combination without * resolving any relative links. */ #ifndef OSNOUI_OMIT_OS_COMBINE_PATHS void os_combine_paths(char *fullpathbuf, size_t fullpathbuflen, const char *path, const char *filename) { /* build the path, without any canonicalization */ build_path(fullpathbuf, fullpathbuflen, path, filename, FALSE); } #endif /* * Determine if a path is absolute or relative. If the path starts with * a path separator character, we consider it absolute, otherwise we * consider it relative. * * Note that, on DOS, an absolute path can also follow a drive letter. * So, if the path contains a letter followed by a colon, we'll consider * the path to be absolute. */ #ifndef OSNOUI_OMIT_IS_FILE_ABSOLUTE int os_is_file_absolute(const char *fname) { #ifdef MSDOS /* * on DOS, a file is absolute if it starts with a drive letter followed * by a path separator */ if (isalpha(fname[0]) && fname[1] == ':' && (fname[2] == '/' || fname[2] == '\\')) return TRUE; /* * it's also absolute if it starts with a UNC-style path * (\\MACHINE\DRIVE) */ if ((fname[0] == '\\' || fname[0] == '/') && (fname[1] == '\\' || fname[1] == '/')) { const char *p; /* make sure we have another \ sometime later */ for (p = fname + 2 ; *p != '\0' && *p != '/' && *p != '\\' ; ++p) ; if (*p != '\0') return TRUE; } #else /* MSDOS */ /* if the name starts with a path separator, it's absolute */ if (ispathchar(fname[0])) return TRUE; #endif /* the path is relative */ return FALSE; } #endif /* * path letter/substring comparison; define this according to whether the * local file system is case-sensitive (e.g., Unix) or not (DOS, Windows) */ #if defined(MSDOS) # define pathmemcmp(a, b, len) memicmp(a, b, len) # define pathchareq(a, b) (toupper(a) == toupper(b)) #endif #ifndef pathchareq # define pathmemcmp(a, b, len) memcmp(a, b, len) # define pathchareq(a, b) ((a) == (b)) #endif #ifdef MSDOS /* * Parse a volume name in an absolute path. DOS/Windows volume names are * simply one-letter drive names - A:, C:, etc. If there's a drive letter * name, returns the next character after the ':'; if there's not a drive * letter, returns null. */ static const char *oss_parse_volume(const char *path) { if (isalpha(path[0]) && path[1] == ':') return path + 2; else return 0; } #endif /* * Get the relative form of a path name. Given a base path and a filename, * get the name of the file with its path expressed relative to the base * path, if possible. Both paths must be in absolute format. * * For DOS-like systems with drive letters, if the filename and base path * are on different drives, there's no way to express the filename in * relative terms, so we'll return it unchanged. */ #ifndef OSNOUI_OMIT_OS_GET_REL_PATH int os_get_rel_path(char *result, size_t result_len, const char *basepath, const char *filename) { const char *fp; const char *bp; const char *fsep; const char *bsep; size_t rem; char *rp; #if defined(MSDOS) /* * DOS and some other systems (e.g., VMS) use drive letter, volume, or * device prefixes, and each drive/volume/device is effectively its own * separate namespace with its own root directory. There's typically * no syntax on these systems to refer to a container entity above the * volume level, or the parent of a volume root, which means there's no * way to write a filename in relative terms when it's on a different * volume from the base path. * * So: start by checking for a drive/volume prefix. If both paths have * one (and they both should if the system uses them at all, as both * paths are supposed to be handed to us in absolute format), make sure * they match; if not, we can't form a relative name. */ bp = oss_parse_volume(basepath); fp = oss_parse_volume(filename); if (bp != 0 && fp != 0 && (bp - basepath != fp - filename || pathmemcmp(basepath, filename, bp - basepath) != 0)) { /* we have volumes, and they don't match - we can't relativize it */ strncpy(result, filename, result_len - 1); result[result_len - 1] = '\0'; return FALSE; } #endif /* * Find the common path prefix. We can lop off any leading elements * that the two paths have in common. */ for (fp = filename, bp = basepath, fsep = 0, bsep = 0 ; pathchareq(*fp, *bp) || (ispathchar(*fp) && ispathchar(*bp)) ; ++fp, ++bp) { /* * if this is a separator character, note it - we want to keep * track of the last separator in the common portion, since this is * the end of the common path prefix */ if (ispathchar(*fp) || *fp == '\0') { fsep = fp; bsep = bp; } /* stop at the end of the strings */ if (*fp == '\0' || *bp == '\0') break; } /* if we didn't find any separators, we can't relativize the paths */ if (bsep == 0 || fsep == 0) { /* nothing in common - return the filename unchanged */ strncpy(result, filename, result_len - 1); result[result_len - 1] = '\0'; return FALSE; } /* * If we reached the end of the base path string, and we're at a path * separator in the filename string, then the entire base path prefix * is in common to both names. */ if (*bp == '\0' && (ispathchar(*fp) || *fp == '\0')) { fsep = fp; bsep = bp; } /* if we're at a path separator in the base path, skip it */ if (*bsep != '\0') ++bsep; /* if we're at a path separator in the filename, skip it */ if (*fsep != '\0') ++fsep; /* * Everything up to fsep and bsep can be dropped, because it's a common * directory path prefix. We must now add the relative adjustment * portion: add a ".." directory for each remaining directory in the * base path, since we must move from the base path up to the common * ancestor; then add the rest of the filename path. */ /* * first, set up to copy into the result buffer - leave space for null * termination */ rp = result; rem = result_len - 1; /* add a ".." for each remaining directory in the base path string */ while (*bsep != '\0') { /* add ".." and a path separator */ if (rem > 3) { *rp++ = '.'; *rp++ = '.'; *rp++ = OSPATHCHAR; rem -= 3; } else { /* no more room - give up */ rem = 0; break; } /* scan to the next path separator */ for ( ; *bsep != '\0' && !ispathchar(*bsep) ; ++bsep) ; /* if this is a path separator, skip it */ if (*bsep != '\0') ++bsep; } /* * Copy the remainder of the filename, or as much as will fit, and * ensure that the result is properly null-terminated */ strncpy(rp, fsep, rem); rp[rem] = '\0'; /* if the result is empty, return "." to represent the current dir */ if (result[0] == '\0') strcpy(rp, "."); /* success */ return TRUE; } #endif #endif /* USE_DOSEXT */ /* ------------------------------------------------------------------------ */ /* * A port can define USE_TIMERAND if it wishes to randomize from the * system clock. This should be usable by most ports. */ #ifdef USE_TIMERAND # include <time.h> void os_rand(long *seed) { time_t t; time( &t ); *seed = (long)t; } #endif /* USE_TIMERAND */ #ifdef USE_PATHSEARCH /* search a path specified in the environment for a tads file */ static int pathfind(const char *fname, int flen, const char *pathvar, char *buf, size_t bufsiz) { char *e; if ( (e = getenv(pathvar)) == 0 ) return(0); for ( ;; ) { char *sep; size_t len; if ( (sep = strchr(e, OSPATHSEP)) != 0 ) { len = (size_t)(sep-e); if (!len) continue; } else { len = strlen(e); if (!len) break; } memcpy(buf, e, len); if (!ispathchar(buf[len-1])) buf[len++] = OSPATHCHAR; memcpy(buf+len, fname, flen); buf[len+flen] = 0; if (osfacc(buf) == 0) return(1); if (!sep) break; e = sep+1; } return(0); } #endif /* USE_PATHSEARCH */ /* * Look for a tads-related file in the standard locations and, if the * search is successful, store the result file name in the given buffer. * Return 1 if the file was located, 0 if not. * * Search the following areas for the file: current directory, program * directory (as derived from argv[0]), and the TADS path. */ int os_locate(const char *fname, int flen, const char *arg0, char *buf, size_t bufsiz) { /* Check the current directory */ if (osfacc(fname) == 0) { memcpy(buf, fname, flen); buf[flen] = 0; return(1); } /* Check the program directory */ if (arg0 && *arg0) { const char *p; /* find the end of the directory name of argv[0] */ for (p = arg0 + strlen(arg0); p > arg0 && !ispathchar(*(p-1)); --p) ; /* don't bother if there's no directory on argv[0] */ if (p > arg0) { size_t len = (size_t)(p - arg0); memcpy(buf, arg0, len); memcpy(buf+len, fname, flen); buf[len+flen] = 0; if (osfacc(buf) == 0) return(1); } } #ifdef USE_PATHSEARCH /* Check TADS path */ if ( pathfind(fname, flen, "TADS", buf, bufsiz) ) return(1); #endif /* USE_PATHSEARCH */ return(0); } /* ------------------------------------------------------------------------ */ /* * Define the version of memcmp to use for comparing filename path elements * for equality. For case-sensitive file systems, use memcmp(); for * systems that ignore case, use memicmp(). */ #if defined(MSDOS) || defined(T_WIN32) || defined(DJGPP) || defined(MSOS2) # define fname_memcmp memicmp #endif #if defined(UNIX) # define fname_memcmp memcmp #endif /* * Get the next earlier element of a path */ static const char *prev_path_ele(const char *start, const char *p, size_t *ele_len) { int cancel = 0; const char *dotdot = 0; /* if we're at the start of the string, there are no more elements */ if (p == start) return 0; /* keep going until we find a suitable element */ for (;;) { const char *endp; /* * If we've reached the start of the string, it means that we have * ".."'s that canceled out every earlier element of the string. * If the cancel count is non-zero, it means that we have one or * more ".."'s that are significant (in that they cancel out * relative elements before the start of the string). If the * cancel count is zero, it means that we've exactly canceled out * all remaining elements in the string. */ if (p == start) { *ele_len = (dotdot != 0 ? 2 : 0); return dotdot; } /* * back up through any adjacent path separators before the current * element, so that we're pointing to the first separator after the * previous element */ for ( ; p != start && ispathchar(*(p-1)) ; --p) ; /* * If we're at the start of the string, this is an absolute path. * Treat it specially by returning a zero-length initial element. */ if (p == start) { *ele_len = 0; return p; } /* note where the current element ends */ endp = p; /* now back up to the path separator before this element */ for ( ; p != start && !ispathchar(*(p-1)) ; --p) ; /* * if this is ".", skip it, since this simply means that this * element matches the same folder as the previous element */ if (endp - p == 1 && p[0] == '.') continue; /* * if this is "..", it cancels out the preceding non-relative * element; up the cancel count and keep searching */ if (endp - p == 2 && p[0] == '.' && p[1] == '.') { /* up the cancel count */ ++cancel; /* if this is the first ".." we've encountered, note it */ if (dotdot == 0) dotdot = p; /* keep searching */ continue; } /* * This is an ordinary path element, not a relative "." or ".." * link. If we have a non-zero cancel count, we're still working * on canceling out elements from ".."'s we found later in the * string. */ if (cancel != 0) { /* this absorbs one level of cancellation */ --cancel; /* * if that's the last cancellation, we've absorbed all ".." * effects, so the last ".." we found is no longer significant */ if (cancel == 0) dotdot = 0; /* keep searching */ continue; } /* this item isn't canceled out by a "..", so it's our winner */ *ele_len = endp - p; return p; } } /* * Compare two paths for equality */ int os_file_names_equal(const char *a, const char *b) { /* start at the end of each name and work backwards */ const char *pa = a + strlen(a), *pb = b + strlen(b); /* keep going until we reach the start of one or the other path */ for (;;) { size_t lena, lenb; /* get the next earlier element of each path */ pa = prev_path_ele(a, pa, &lena); pb = prev_path_ele(b, pb, &lenb); /* if one or the other ran out, we're done */ if (pa == 0 || pb == 0) { /* the paths match if they ran out at the same time */ return pa == pb; } /* if the two elements don't match, return unequal */ if (lena != lenb || fname_memcmp(pa, pb, lena) != 0) return FALSE; } } /* ------------------------------------------------------------------------ */ /* * ISAAC random number generator. This is a small, fast, cryptographic * quality random number generator that we use internally for some generic * purposes: * * - for os_gen_temp_filename(), we use it to generate a GUID-style random * filename * * - for our generic implementation of os_gen_rand_bytes(), we use it as * the source of the random bytes */ /* * include ISAAC if we're using our generic temporary filename generator * with long filenames, or we're using our generic os_gen_rand_bytes() */ #if !defined(OSNOUI_OMIT_TEMPFILE) && (defined(T_WIN32) || !defined(MSDOS)) #define INCLUDE_ISAAC #endif #ifdef USE_GENRAND #define INCLUDE_ISAAC #endif #ifdef INCLUDE_ISAAC /* * ISAAC random number generator implementation, for generating * GUID-strength random temporary filenames */ #define ISAAC_RANDSIZL (8) #define ISAAC_RANDSIZ (1<<ISAAC_RANDSIZL) static struct isaacctx { /* RNG context */ unsigned long cnt; unsigned long rsl[ISAAC_RANDSIZ]; unsigned long mem[ISAAC_RANDSIZ]; unsigned long a; unsigned long b; unsigned long c; } *S_isaacctx; #define _isaac_rand(r) \ ((r)->cnt-- == 0 ? \ (isaac_gen_group(r), (r)->cnt=ISAAC_RANDSIZ-1, (r)->rsl[(r)->cnt]) : \ (r)->rsl[(r)->cnt]) #define isaac_rand() _isaac_rand(S_isaacctx) #define isaac_ind(mm,x) ((mm)[(x>>2)&(ISAAC_RANDSIZ-1)]) #define isaac_step(mix,a,b,mm,m,m2,r,x) \ { \ x = *m; \ a = ((a^(mix)) + *(m2++)) & 0xffffffff; \ *(m++) = y = (isaac_ind(mm,x) + a + b) & 0xffffffff; \ *(r++) = b = (isaac_ind(mm,y>>ISAAC_RANDSIZL) + x) & 0xffffffff; \ } #define isaac_mix(a,b,c,d,e,f,g,h) \ { \ a^=b<<11; d+=a; b+=c; \ b^=c>>2; e+=b; c+=d; \ c^=d<<8; f+=c; d+=e; \ d^=e>>16; g+=d; e+=f; \ e^=f<<10; h+=e; f+=g; \ f^=g>>4; a+=f; g+=h; \ g^=h<<8; b+=g; h+=a; \ h^=a>>9; c+=h; a+=b; \ } /* generate the group of numbers */ static void isaac_gen_group(struct isaacctx *ctx) { unsigned long a; unsigned long b; unsigned long x; unsigned long y; unsigned long *m; unsigned long *mm; unsigned long *m2; unsigned long *r; unsigned long *mend; mm = ctx->mem; r = ctx->rsl; a = ctx->a; b = (ctx->b + (++ctx->c)) & 0xffffffff; for (m = mm, mend = m2 = m + (ISAAC_RANDSIZ/2) ; m<mend ; ) { isaac_step(a<<13, a, b, mm, m, m2, r, x); isaac_step(a>>6, a, b, mm, m, m2, r, x); isaac_step(a<<2, a, b, mm, m, m2, r, x); isaac_step(a>>16, a, b, mm, m, m2, r, x); } for (m2 = mm; m2<mend; ) { isaac_step(a<<13, a, b, mm, m, m2, r, x); isaac_step(a>>6, a, b, mm, m, m2, r, x); isaac_step(a<<2, a, b, mm, m, m2, r, x); isaac_step(a>>16, a, b, mm, m, m2, r, x); } ctx->b = b; ctx->a = a; } static void isaac_init(unsigned long *rsl) { static int inited = FALSE; int i; unsigned long a; unsigned long b; unsigned long c; unsigned long d; unsigned long e; unsigned long f; unsigned long g; unsigned long h; unsigned long *m; unsigned long *r; struct isaacctx *ctx; /* allocate the context if we don't already have it set up */ if ((ctx = S_isaacctx) == 0) ctx = S_isaacctx = (struct isaacctx *)malloc(sizeof(struct isaacctx)); /* * If we're already initialized, AND the caller isn't re-seeding with * explicit data, we're done. */ if (inited && rsl == 0) return; inited = TRUE; ctx->a = ctx->b = ctx->c = 0; m = ctx->mem; r = ctx->rsl; a = b = c = d = e = f = g = h = 0x9e3779b9; /* the golden ratio */ /* scramble the initial settings */ for (i = 0 ; i < 4 ; ++i) { isaac_mix(a, b, c, d, e, f, g, h); } /* * if they sent in explicit initialization bytes, use them; otherwise * seed the generator with truly random bytes from the system */ if (rsl != 0) memcpy(ctx->rsl, rsl, sizeof(ctx->rsl)); else os_gen_rand_bytes((unsigned char *)ctx->rsl, sizeof(ctx->rsl)); /* initialize using the contents of ctx->rsl[] as the seed */ for (i = 0 ; i < ISAAC_RANDSIZ ; i += 8) { a += r[i]; b += r[i+1]; c += r[i+2]; d += r[i+3]; e += r[i+4]; f += r[i+5]; g += r[i+6]; h += r[i+7]; isaac_mix(a, b, c, d, e, f, g, h); m[i] = a; m[i+1] = b; m[i+2] = c; m[i+3] = d; m[i+4] = e; m[i+5] = f; m[i+6] = g; m[i+7] = h; } /* do a second pass to make all of the seed affect all of m */ for (i = 0 ; i < ISAAC_RANDSIZ ; i += 8) { a += m[i]; b += m[i+1]; c += m[i+2]; d += m[i+3]; e += m[i+4]; f += m[i+5]; g += m[i+6]; h += m[i+7]; isaac_mix(a, b, c, d, e, f, g, h); m[i] = a; m[i+1] = b; m[i+2] = c; m[i+3] = d; m[i+4] = e; m[i+5] = f; m[i+6] = g; m[i+7] = h; } /* fill in the first set of results */ isaac_gen_group(ctx); /* prepare to use the first set of results */ ctx->cnt = ISAAC_RANDSIZ; } #endif /* INCLUDE_ISAAC */ /* ------------------------------------------------------------------------ */ /* * Generic implementation of os_gen_rand_bytes(). This can be used when * the operating system doesn't have a native source of true randomness, * but prefereably only as a last resort - see below for why. * * This generator uses ISAAC to generate the bytes, seeded by the system * time. This algorithm isn't nearly as good as using a native OS-level * randomness source, since any decent OS API will have access to * considerably more sources of true entropy. In a portable setting, we * have very little in the way of true randomness available. The most * reliable portable source of randomness is the system clock; if it has * resolution in the millisecond range, this gives us roughly 10-12 bits of * real entropy if we assume that the user is running the program manually, * in that there should be no pattern to the exact program start time - * even a series of back-to-back runs would be pretty random in the part of * the time below about 1 second. We *might* also be able to get a little * randomness from the memory environment, such as the current stack * pointer, the state of the malloc() allocator as revealed by the address * of a newly allocated block, the contents of uninitialized stack * variables and uninitialized malloc blocks, and the contents of the * system registers as saved by setjmp. These memory settings are not * entirely likely to vary much from one run to the next: most modern OSes * virtualize the process environment to such an extent that each fresh run * will start with exactly the same initial memory environment, including * the stack address and malloc heap configuration. * * We make the most of these limited entropy sources by using them to seed * an ISAAC RNG, then generating the returned random bytes via ISAAC. * ISAAC's design as a cryptographic RNG means that it thoroughly mixes up * the meager set of random bits we hand it, so the bytes returned will * statistically look nice and random. However, don't be fooled into * thinking that this magnifies the basic entropy we have as inputs - it * doesn't. If we only have 10-12 bits of entropy from the timer, and * everything else is static, our byte sequence will represent 10-12 bits * of entropy scattered around through a large set of mathematically (and * deterministically) derived bits. The danger is that the "birthday * problem" dictates that with 12 bits of variation from one run to the * next, we'd have a good chance of seeing a repeat of the *exact same byte * sequence* within about 100 runs. This is why it's so much better to * customize this routine using a native OS mechanism whenever possible. */ #ifdef USE_GENRAND void os_gen_rand_bytes(unsigned char *buf, size_t buflen) { union { unsigned long r[ISAAC_RANDSIZ]; struct { unsigned long r1[15]; jmp_buf env; } o; } r; void *p, *q; /* * Seed ISAAC with what little entropy we have access to in a generic * cross-platform implementation: * * - the current wall-clock time *. - the high-precision (millisecond) system timer *. - the current stack pointer *. - an arbitrary heap pointer obtained from malloc() *. - whatever garbage is in the random heap pointer from malloc() *. - whatever random garbage is in the rest of our stack buffer 'r' *. - the contents of system registers from 'setjmp' * * The millisecond timer is by far the most reliable source of real * entropy we have available. The wall clock time doesn't vary quickly * enough to produce more than a few bits of entropy from run to run; * all of the memory factors could be absolutely deterministic from run * to run, depending on how thoroughly the OS virtualizes the process * environment at the start of a run. For example, some systems clear * memory pages allocated to a process, as a security measure to * prevent old data from one process from becoming readable by another * process. Some systems virtualize the memory space such that the * program is always loaded at the same virtual address, always has its * stack at the same virtual address, etc. * * Note that we try to add some variability to our malloc heap probing, * first by making the allocation size vary according to the low bits * of the system millisecond timer, then by doing a second allocation * to take into account the effect of the randomized size of the first * allocation. This should avoid getting the exact same results every * time we run, even if the OS arranges for the heap to have exactly * the same initial layout with every run, since our second malloc will * have initial conditions that vary according to the size our first * malloc. This arguably doesn't introduce a lot of additional real * entropy, since we're already using the system timer directly in the * calculation anyway: in a sufficiently predictable enough heap * environment, our two malloc() calls will yield the same results for * a given timer value, so we're effectively adding f(timer) for some * deterministic function f(), which is the same in terms of additional * real entropy as just adding the timer again, which is the same as * adding nothing. */ r.r[0] = (unsigned long)time(0); r.r[1] = (unsigned long)os_get_sys_clock_ms(); r.r[2] = (unsigned long)buf; r.r[3] = (unsigned long)&buf; p = malloc((size_t)(os_get_sys_clock_ms() & 1023) + 17); r.r[4] = (unsigned long)p; r.r[5] = *(unsigned long *)p; r.r[6] = *((unsigned long *)p + 10); q = malloc(((size_t)p & 1023) + 19); r.r[7] = (unsigned long)p; r.r[8] = *(unsigned long *)p; r.r[9] = *((unsigned long *)p + 10); setjmp(r.o.env); free(p); free(q); /* initialize isaac with our seed data */ isaac_init(r.r); /* generate random bytes from isaac to fill the buffer */ while (buflen > 0) { unsigned long n; size_t copylen; /* generate a number */ n = isaac_rand(); /* copy it into the buffer */ copylen = buflen < sizeof(n) ? buflen : sizeof(n); memcpy(buf, &n, copylen); /* advance our buffer pointer */ buf += copylen; buflen -= copylen; } } #endif /* USE_GENRAND */ /* ------------------------------------------------------------------------ */ /* * Temporary files * * This default implementation is layered on the normal osifc file APIs, so * our temp files are nothing special: they're just ordinary files that we * put in a special directory (the temp path), and which we count on our * caller to delete before the app terminates (which assumes that the app * terminates normally, AND that our portable caller correctly keeps track * of every temp file it creates and explicitly deletes it). * * On systems that have native temp file support, you might want to use the * native API instead, since native temp file APIs often have the useful * distinction that they'll automatically delete any outstanding temp files * on application termination, even on crashy exits. To remove these * default implementations from the build, #define OSNOUI_OMIT_TEMPFILE * in your makefile. */ #ifndef OSNOUI_OMIT_TEMPFILE /* * Create and open a temporary file */ osfildef *os_create_tempfile(const char *swapname, char *buf) { osfildef *fp; const char *fname; /* if a name wasn't provided, generate a name */ if (swapname == 0) { /* generate a name for the file */ os_gen_temp_filename(buf, OSFNMAX); /* use the generated name */ fname = buf; } else { /* use the name they passed in */ fname = swapname; } /* open the file in binary write/truncate mode */ fp = osfoprwtb(fname, OSFTSWAP); /* set the file's type in the OS, if necessary */ os_settype(fname, OSFTSWAP); /* return the file pointer */ return fp; } /* * Delete a temporary file. Since our generic implementation of * os_create_tempfile() simply uses osfoprwtb() to open the file, a * temporary file's handle is not any different from any other file * handle - in particular, the OS doesn't automatically delete the file * when closed. Hence, we need to delete the file explicitly here. */ int osfdel_temp(const char *fname) { /* delete the file using the normal mechanism */ return osfdel(fname); } #if defined(MSDOS) && !defined(T_WIN32) #define SHORT_FILENAMES #endif #ifndef SHORT_FILENAMES /* * Generate a name for a temporary file. This is the long filename * version, suitable only for platforms that can handle filenames of at * least 45 characters in just the root name portion. For systems with * short filenames (e.g., MS-DOS, this must use a different algorithm - see * the MSDOS section below for a fairly portable "8.3" implementation. */ int os_gen_temp_filename(char *buf, size_t buflen) { char tmpdir[OSFNMAX], fname[50]; /* get the system temporary directory */ os_get_tmp_path(tmpdir); /* seed ISAAC with random data from the system */ isaac_init(0); /* * Generate a GUID-strength random filename. ISAAC is a cryptographic * quality RNG, so the chances of collisions with other filenames * should be effectively zero. */ sprintf(fname, "TADS-%08lx-%08lx-%08lx-%08lx.tmp", isaac_rand(), isaac_rand(), isaac_rand(), isaac_rand()); /* build the full path */ os_build_full_path(buf, buflen, tmpdir, fname); /* success */ return TRUE; } #endif #endif /* OSNOUI_OMIT_TEMPFILE */ /* ------------------------------------------------------------------------ */ /* * print a null-terminated string to osfildef* file */ #ifndef OSNOUI_OMIT_OS_FPRINTZ void os_fprintz(osfildef *fp, const char *str) { fprintf(fp, "%s", str); } #endif /* * print a counted-length string (which might not be null-terminated) to a * file */ #ifndef OSNOUI_OMIT_OS_FPRINT void os_fprint(osfildef *fp, const char *str, size_t len) { fprintf(fp, "%.*s", (int)len, str); } #endif /* ------------------------------------------------------------------------ */ #ifdef T_WIN32 /* * Windows implementation - get the temporary file path. */ void os_get_tmp_path(char *buf) { GetTempPath(OSFNMAX, buf); } #endif #if defined(MSDOS) && !defined(T_WIN32) /* * MS-DOS implementation - Get the temporary file path. Tries getting * the values of various environment variables that are typically used * to define the location for temporary files. */ void os_get_tmp_path(char *buf) { static char *vars[] = { "TEMPDIR", "TMPDIR", "TEMP", "TMP", 0 }; char **varp; /* look for an environment variable from our list */ for (varp = vars ; *varp ; ++varp) { char *val; /* get this variable's value */ val = getenv(*varp); if (val) { size_t len; /* use this value */ strcpy(buf, val); /* add a backslash if necessary */ if ((len = strlen(buf)) != 0 && buf[len-1] != '/' && buf[len-1] != '\\') { buf[len] = '\\'; buf[len+1] = '\0'; } /* use this value */ return; } } /* didn't find anything - leave the prefix empty */ buf[0] = '\0'; } /* * Generate a name for a temporary file. This implementation is suitable * for MS-DOS and other platforms with short filenames - for the simpler * and more generic long-filename version, see above. On systems where * filenames are limited to short names, we can't pack enough randomness * into a filename to guarantee uniqueness by virtue of randomness alone, * so we cope by actually checking for an existing file for each random * name we roll up, trying again if our selected name is in use. */ int os_gen_temp_filename(char *buf, size_t buflen) { osfildef *fp; int attempt; size_t len; time_t timer; int pass; /* * Fail if our buffer is smaller than OSFNMAX. os_get_tmp_path() * assumes an OSFNMAX-sized buffer, so we'll have problems if the * passed-in buffer is shorter than that. */ if (buflen < OSFNMAX) return FALSE; /* * Try a few times with the temporary file path; if we can't find an * available filename there, try again with the working directory. */ for (pass = 1 ; pass <= 2 ; ++pass) { /* get the directory path */ if (pass == 1) { /* first pass - use the system temp directory */ os_get_tmp_path(buf); len = strlen(buf); } else { /* * second pass - we couldn't find any free names in the system * temp directory, so try the working directory */ buf[0] = '\0'; len = 0; } /* get the current time, as a basis for a unique identifier */ time(&timer); /* try until we find a non-existent filename */ for (attempt = 0 ; attempt < 100 ; ++attempt) { /* generate a name based on time and try number */ sprintf(buf + len, "SW%06ld.%03d", (long)timer % 999999, attempt); /* check to see if a file by this name already exists */ if (osfacc(buf)) { /* the file doesn't exist - try creating it */ fp = osfoprwtb(buf, OSFTSWAP); /* if that succeeded, return this file */ if (fp != 0) { /* set the file's type in the OS, if necessary */ os_settype(buf, OSFTSWAP); /* close the file - it's just an empty placeholder */ osfcls(fp); /* return success */ return TRUE; } } } } /* we couldn't find a free filename - return failure */ return FALSE; } #endif /* MSDOS */ #ifdef USE_NULLSTYPE void os_settype(const char *f, os_filetype_t t) { /* nothing needs to be done on this system */ } #endif /* USE_NULLSTYPE */ /* ------------------------------------------------------------------------ */ /* * URL/local path conversion */ #if defined(TURBO) || defined(DJGPP) || defined(MICROSOFT) || defined(MSOS2) /* length-protected character adder for os_cvt_xxx */ static void cvtaddchar(char **dst, size_t *rem, char c) { if (*rem > 1) { **dst = c; *dst += 1; *rem -= 1; } } static void cvtaddchars(char **dst, size_t *rem, const char *src, size_t len) { for ( ; len > 0 ; --len) cvtaddchar(dst, rem, *src++); } #endif /* * Convert an OS filename path to a relative URL */ void os_cvt_dir_url(char *result_buf, size_t result_buf_size, const char *src_path) { char *dst = result_buf; const char *src = src_path; size_t rem = result_buf_size; #if defined(TURBO) || defined(DJGPP) || defined(MICROSOFT) || defined(MSOS2) /* * If there's a DOS/Windows drive letter, start with the drive letter * and leading '\', if present, as a separate path element. If it's a * UNC-style path, add the UNC \\MACHINE\SHARE as the first element. * * In either case, we'll leave the source pointer positioned at the * rest of the path after the drive root or UNC share, which means * we're pointing to the relative portion of the path that follows. * The normal algorithm will simply convert this to a relative URL that * will be tacked on to the absolute root URL portion that we generate * here, so we'll have the correct overall format. */ if (isalpha(src[0]) && src[1] == ':') { /* start with /X: */ cvtaddchar(&dst, &rem, '/'); cvtaddchars(&dst, &rem, src, 2); src += 2; /* * if it's just "X:" and not "X:\", translate it to "X:." to make * it explicit that we're talking about the working directory on X: */ if (*src != '\\' && *src != '/') cvtaddchars(&dst, &rem, "./", 2); } else if ((src[0] == '\\' || src[0] == '/') && (src[1] == '\\' || src[1] == '/')) { const char *p; /* * UNC-style path. Find the next separator to get the end of the * machine name. */ for (p = src + 2 ; *p != '\0' && *p != '/' && *p != '\\' ; ++p) ; /* start with /\\MACHINE */ cvtaddchar(&dst, &rem, '/'); cvtaddchars(&dst, &rem, src, p - src); /* skip to the path separator */ src = p; } #endif /* DOS */ /* * Run through the source buffer, copying characters to the output * buffer. If we encounter a path separator character, replace it with * a forward slash. */ for ( ; *src != '\0' && rem > 1 ; ++dst, ++src, --rem) { /* * If this is a local path separator character, replace it with the * URL-style path separator character. Otherwise, copy it * unchanged. */ if (strchr(OSPATHURL, *src) != 0) { /* add the URL-style path separator instead of the local one */ *dst = '/'; } else { /* add the character unchanged */ *dst = *src; } } /* remove any trailing separators (unless the whole path is "/") */ while (dst > result_buf + 1 && *(dst-1) == '/') --dst; /* add a null terminator */ *dst = '\0'; } /* * Convert a relative URL to a relative file system path name */ #ifndef OSNOUI_OMIT_OS_CVT_URL_DIR void os_cvt_url_dir(char *result_buf, size_t result_buf_size, const char *src_url) { char *dst = result_buf; const char *src = src_url; size_t rem = result_buf_size; /* * check for an absolute path */ #if defined(TURBO) || defined(DJGPP) || defined(MICROSOFT) || defined(MSOS2) if (*src == '/') { const char *p; int is_drive, is_unc = FALSE; /* we have an absolute path - find the end of the first element */ for (p = ++src ; *p != '\0' && *p != '/' ; ++p) ; /* check to see if it looks like a drive letter */ is_drive = (isalpha(src[0]) && src[1] == ':' && (p - src == 2 || (p - src == 3 && src[2] == '.'))); /* check to see if it looks like a UNC-style path */ is_unc = (src[0] == '\\' && src[1] == '\\'); /* * if it's a drive letter or UNC path, it's a valid Windows root * path element - copy it exactly, then decode the rest of the path * as a simple relative path relative to this root */ if (is_drive || is_unc) { /* it's a drive letter or drive root path - copy it exactly */ cvtaddchars(&dst, &rem, src, p - src); /* * if it's an X:. path, remove the . and the following path * separator */ if (is_drive && p - src == 3 && src[2] == '.') { /* undo the '.' */ --dst; ++rem; /* skip the '/' if present */ if (*p == '/') ++p; } /* skip to the '/' */ src = p; } else { /* * It's not a valid DOS root element, so make this a * non-drive-letter root path, converting the first element as * a directory name. */ cvtaddchar(&dst, &rem, '\\'); } } else if (isalpha(src[0]) && src[1] == ':') { /* * As a special case, assume that a path starting with "X:" (where * X is any letter) is a Windows/DOS drive letter prefix. This * doesn't fit our new (as of Jan 2012) rules for converting paths * to URLs, but it's what older versions did, so it provides * compatibility. There's a small price for this compatibility, * which is that it's possible in principle for a Unix relative * path to look the same way - you could have a Unix directory * called "c:", so "c:/dir" would be a valid relative path. But * it's extremely uncommon for Unix users to use colons in * directory names for a couple of reasons; one is that it creates * interop problems because practically every other file system * treats ':' as a special syntax element, and the other is that * ':' is conventionally used on Unix itself as a delimiter in path * lists, so while it isn't formally a special character in file * names, it's effectively a special character. */ cvtaddchars(&dst, &rem, src, 2); src += 2; } #endif /* * Run through the source buffer, copying characters to the output * buffer. If we encounter a '/', convert it to a path separator * character. */ for ( ; *src != '\0' && rem > 1 ; ++src, ++dst, --rem) { /* * replace slashes with path separators; expand '%' sequences; copy * all other characters unchanged */ if (*src == '/') { /* change '/' to the local path separator */ *dst = OSPATHCHAR; } else if ((unsigned char)*src < 32 #if defined(TURBO) || defined(DJGPP) || defined(MICROSOFT) || defined(MSOS2) || strchr("*+?=[]\\&|\":<>", *src) != 0 #endif ) { *dst = '_'; } else { /* copy this character unchanged */ *dst = *src; } } /* add a null terminator and we're done */ *dst = '\0'; } #endif /* ------------------------------------------------------------------------ */ /* * Service routine for searching - build the full output path name. (This * is used in the various DOS/Windows builds.) */ #if defined(TURBO) || defined(DJGPP) || defined(MICROSOFT) || defined(MSOS2) static void oss_build_outpathbuf(char *outpathbuf, size_t outpathbufsiz, const char *path, const char *fname) { /* if there's a full path buffer, build the full path */ if (outpathbuf != 0) { size_t lp; size_t lf; /* copy the path prefix */ lp = strlen(path); if (lp > outpathbufsiz - 1) lp = outpathbufsiz - 1; memcpy(outpathbuf, path, lp); /* add the filename if there's any room */ lf = strlen(fname); if (lf > outpathbufsiz - lp - 1) lf = outpathbufsiz - lp - 1; memcpy(outpathbuf + lp, fname, lf); /* null-terminate the result */ outpathbuf[lp + lf] = '\0'; } } #endif /* TURBO || DJGPP || MICROSOFT || MSOS2 */ /* ------------------------------------------------------------------------ */ /* * Borland C implementation of directory functions */ #if defined(TURBO) || defined(DJGPP) #include <dir.h> #include <dos.h> struct oss_find_ctx_t { struct ffblk ff; int found; }; int os_open_dir(const char *dir, osdirhdl_t *hdl) { char *pat; size_t patsiz; struct oss_find_ctx_t *ctx; /* create a copy of the string with "\*.*" appended */ patsiz = strlen(dir) + 32; pat = (char *)malloc(patsiz); os_build_full_path(pat, patsiz, dir, "*.*"); if (pat == 0) return FALSE; /* allocate a search block structure */ ctx = (struct oss_find_ctx_t *)malloc(sizeof(struct oss_find_ctx_t)); if (ctx == 0) { free(pat); return FALSE; } /* begin the search */ ctx->found = (findfirst(pat, &ctx->ff, FA_DIREC) == 0); /* done with the pattern buffer */ free(pat); /* return the context as the search handle and indicate success */ *hdl = ctx; return TRUE; } int os_read_dir(osdirhdl_t hdl, char *buf, size_t buflen) { /* cast the handle to our private structure */ struct oss_find_ctx_t *ctx = (struct oss_find_ctx_t *)hdl; /* if we're out of files, return failure */ if (!ctx->found) return FALSE; /* return the current file */ safe_strcpy(buf, buflen, ctx->ff.ff_name); /* move on to the next file */ ctx->found = (findnext(&ctx->ff) == 0); /* success */ return TRUE; } void os_close_dir(osdirhdl_t hdl) { /* free the context structure */ free(hdl); } #if 0 // the os_find_xxx family of functions has been DEPRECATED /* * search context structure - DEPRECATED */ struct oss_find_ctx_t { /* C library find-file block */ struct ffblk ff; /* original search path prefix (we'll allocate more to fit the string) */ char path[1]; }; /* * find first matching file - DEPRECATED */ void *os_find_first_file(const char *dir, const char *pattern, char *outbuf, size_t outbufsiz, int *isdir, char *outpathbuf, size_t outpathbufsiz) { struct oss_find_ctx_t *ctx; char realpat[OSFNMAX]; size_t l; size_t path_len; const char *lastsep; const char *p; /* * construct the filename pattern from the directory and pattern * segments, using "*" as the default wildcard pattern if no * explicit pattern was provided */ strcpy(realpat, dir); if ((l = strlen(realpat)) != 0 && realpat[l - 1] != '\\') realpat[l++] = '\\'; if (pattern == 0) strcpy(realpat + l, "*.*"); else strcpy(realpat + l, pattern); /* find the last separator in the original path */ for (p = realpat, lastsep = 0 ; *p != '\0' ; ++p) { /* if this is a separator, remember it */ if (*p == '\\' || *p == '/' || *p == ':') lastsep = p; } /* * if we found a separator, the path prefix is everything up to and * including the separator; otherwise, there's no path prefix */ if (lastsep != 0) { /* the length of the path includes the separator */ path_len = lastsep + 1 - realpat; } else { /* there's no path prefix - everything is in the current directory */ path_len = 0; } /* allocate a context */ ctx = (struct oss_find_ctx_t *)malloc(sizeof(struct oss_find_ctx_t) + path_len); /* copy the path to the context */ memcpy(ctx->path, realpat, path_len); ctx->path[path_len] = '\0'; /* call DOS to search for a matching file */ if (findfirst(realpat, &ctx->ff, FA_DIREC)) { /* no dice - delete the context and return failure */ free(ctx); return 0; } /* skip files with the HIDDEN or SYSTEM attributes */ while ((ctx->ff.ff_attrib & (FA_HIDDEN | FA_SYSTEM)) != 0) { /* skip to the next file */ if (findnext(&ctx->ff)) { /* no more files - delete the context and give up */ free(ctx); return 0; } } /* copy the filename into the caller's buffer */ l = strlen(ctx->ff.ff_name); if (l > outbufsiz - 1) l = outbufsiz - 1; memcpy(outbuf, ctx->ff.ff_name, l); outbuf[l] = '\0'; /* build the full path, if desired */ oss_build_outpathbuf(outpathbuf, outpathbufsiz, ctx->path, ctx->ff.ff_name); /* return the directory indication */ *isdir = (ctx->ff.ff_attrib & FA_DIREC) != 0; /* * return the context - it will be freed later when find-next * reaches the last file or find-close is called to cancel the * search */ return ctx; } /* find the next file - DEPRECATED */ void *os_find_next_file(void *ctx0, char *outbuf, size_t outbufsiz, int *isdir, char *outpathbuf, size_t outpathbufsiz) { struct oss_find_ctx_t *ctx = (struct oss_find_ctx_t *)ctx0; size_t l; /* if the search has already ended, do nothing */ if (ctx == 0) return 0; /* keep going until we find a non-hidden, non-system file */ do { /* try searching for the next file */ if (findnext(&ctx->ff)) { /* no more files - delete the context and give up */ free(ctx); /* return null to indicate that the search is finished */ return 0; } } while ((ctx->ff.ff_attrib & (FA_HIDDEN | FA_SYSTEM)) != 0); /* return the name */ l = strlen(ctx->ff.ff_name); if (l > outbufsiz - 1) l = outbufsiz - 1; memcpy(outbuf, ctx->ff.ff_name, l); outbuf[l] = '\0'; /* return the directory indication */ *isdir = (ctx->ff.ff_attrib & FA_DIREC) != 0; /* build the full path, if desired */ oss_build_outpathbuf(outpathbuf, outpathbufsiz, ctx->path, ctx->ff.ff_name); /* * indicate that the search was successful by returning the non-null * context pointer -- the context has been updated for the new * position in the search, so we just return the same context * structure that we've been using all along */ return ctx; } /* cancel a search - DEPRECATED */ void os_find_close(void *ctx0) { struct oss_find_ctx_t *ctx = (struct oss_find_ctx_t *)ctx0; /* if the search was already cancelled, do nothing */ if (ctx == 0) return; /* delete the search context */ free(ctx); } #endif /* DEPRECATED */ /* * Time-zone initialization */ void os_tzset() { /* let the run-time library initialize the time zone */ tzset(); } /* ------------------------------------------------------------------------ */ /* * Get file attributes */ unsigned long oss_get_file_attrs(const char *fname) { struct ffblk ff; unsigned long attrs = 0; /* look up the file */ if (!findfirst(fname, &ff, FA_DIREC)) { /* translate the HIDDEN and SYSTEM bits */ if ((ff.ff_attrib & FA_HIDDEN) != 0) attrs |= OSFATTR_HIDDEN; if ((ff.ff_attrib & FA_SYSTEM) != 0) attrs |= OSFATTR_SYSTEM; /* if the RDONLY bit is set, it's readable, otherwise read/write */ attrs |= OSFATTR_READ; if ((ff.ff_attrib & FA_RDONLY) == 0) attrs |= OSFATTR_WRITE; } /* return the attributes */ return attrs; } /* * Get file status information */ int os_file_stat(const char *fname, int follow_links, os_file_stat_t *s) { struct stat info; struct ffblk ff; /* get the file information */ if (stat(fname, &info)) { /* * stat() doesn't work for devices; try running through osfmode(), * which does special checks for reserved device names */ if (osfmode(fname, follow_links, &s->mode, &s->attrs)) { /* devices have no sizes or timestamps */ s->sizehi = 0; s->sizelo = 0; s->cre_time = 0; s->mod_time = 0; s->acc_time = 0; /* success */ return TRUE; } /* failed */ return FALSE; } /* translate the status fields */ if (sizeof(info.st_size) <= 4) { s->sizelo = info.st_size; s->sizehi = 0; } else { s->sizelo = (uint32_t)(info.st_size & 0xFFFFFFFF); s->sizehi = (uint32_t)(info.st_size >> 32); } s->cre_time = info.st_ctime; s->mod_time = info.st_mtime; s->acc_time = info.st_atime; s->mode = info.st_mode; /* get the DOS mode information */ s->attrs = oss_get_file_attrs(fname); /* success */ return TRUE; } /* * Resolve a symbolic link */ int os_resolve_symlink(const char *fname, char *target, size_t target_size) { /* DOS doesn't support symbolic links */ return FALSE; } #endif /* TURBO || DJGPP */ /* ------------------------------------------------------------------------ */ /* * Microsoft C implementation of directory functions */ #ifdef MICROSOFT struct oss_find_ctx_t { /* _findfirst/_findnext search handle */ long handle; /* return data from _findfirst/_findnext */ struct _finddata_t data; /* does 'data' contain a valid file description? */ int found; }; int os_open_dir(const char *dir, osdirhdl_t *hdl) { char *pat; size_t patsiz; struct oss_find_ctx_t *ctx; /* create a copy of the string with "\*" appended */ patsiz = strlen(dir) + 32; pat = (char *)malloc(patsiz); os_build_full_path(pat, patsiz, dir, "*"); if (pat == 0) return FALSE; /* allocate a search block structure */ ctx = (struct oss_find_ctx_t *)malloc(sizeof(struct oss_find_ctx_t)); if (ctx == 0) { free(pat); return FALSE; } /* begin the search */ ctx->handle = _findfirst(pat, &ctx->data); /* done with the pattern string */ free(pat); /* if the search failed, free resources and return failure */ if (ctx->handle == -1L) { free(ctx); return FALSE; } /* return the context as the search handle and indicate success */ ctx->found = TRUE; *hdl = ctx; return TRUE; } int os_read_dir(osdirhdl_t hdl, char *buf, size_t buflen) { /* cast the handle to our private structure */ struct oss_find_ctx_t *ctx = (struct oss_find_ctx_t *)hdl; /* if we're out of files, return failure */ if (!ctx->found) return FALSE; /* return the current file */ safe_strcpy(buf, buflen, ctx->data.name); /* move on to the next file */ ctx->found = (_findnext(ctx->handle, &ctx->data) == 0); /* success */ return TRUE; } void os_close_dir(osdirhdl_t hdl) { /* cast the handle to our private structure */ struct oss_find_ctx_t *ctx = (struct oss_find_ctx_t *)hdl; /* close the system search handle */ _findclose(ctx->handle); /* free the context structure */ free(hdl); } #if 0 // the os_find_xxx family of functions has been DEPRECATED typedef struct { /* search handle */ long handle; /* found data structure */ struct _finddata_t data; /* full original search path */ char path[1]; } ossfcx; /* * find first matching file */ void *os_find_first_file(const char *dir, const char *pattern, char *outbuf, size_t outbufsiz, int *isdir, char *outpathbuf, size_t outpathbufsiz) { ossfcx *ctx; char realpat[OSFNMAX]; size_t l; size_t path_len; const char *lastsep; const char *p; /* * construct the filename pattern from the directory and pattern * segments, using "*" as the default wildcard pattern if no * explicit pattern was provided */ strcpy(realpat, dir); if ((l = strlen(realpat)) != 0 && realpat[l - 1] != '\\') realpat[l++] = '\\'; if (pattern == 0) strcpy(realpat + l, "*"); else strcpy(realpat + l, pattern); /* find the last separator in the original path */ for (p = realpat, lastsep = 0 ; *p != '\0' ; ++p) { /* if this is a separator, remember it */ if (*p == '\\' || *p == '/' || *p == ':') lastsep = p; } /* * if we found a separator, the path prefix is everything up to and * including the separator; otherwise, there's no path prefix */ if (lastsep != 0) { /* the length of the path includes the separator */ path_len = lastsep + 1 - realpat; } else { /* there's no path prefix - everything is in the current directory */ path_len = 0; } /* allocate a context */ ctx = (ossfcx *)malloc(sizeof(ossfcx) + path_len); /* copy the path to the context */ memcpy(ctx->path, realpat, path_len); ctx->path[path_len] = '\0'; /* call DOS to search for a matching file */ ctx->handle = _findfirst(realpat, &ctx->data); if (ctx->handle == -1L) { /* no dice - delete the context and return failure */ free(ctx); return 0; } /* skip files with HIDDEN or SYSTEM attributes */ while ((ctx->data.attrib & (_A_HIDDEN | _A_SYSTEM)) != 0) { /* skip this file */ if (_findnext(ctx->handle, &ctx->data) != 0) { /* no more files - close up shop and return failure */ _findclose(ctx->handle); free(ctx); return 0; } } /* return the name */ l = strlen(ctx->data.name); if (l > outbufsiz - 1) l = outbufsiz - 1; memcpy(outbuf, ctx->data.name, l); outbuf[l] = '\0'; /* return the directory indication */ *isdir = (ctx->data.attrib & _A_SUBDIR) != 0; /* build the full path, if desired */ oss_build_outpathbuf(outpathbuf, outpathbufsiz, ctx->path, ctx->data.name); /* * return the context - it will be freed later when find-next * reaches the last file or find-close is called to cancel the * search */ return ctx; } /* find the next file */ void *os_find_next_file(void *ctx0, char *outbuf, size_t outbufsiz, int *isdir, char *outpathbuf, size_t outpathbufsiz) { ossfcx *ctx = (ossfcx *)ctx0; size_t l; /* if the search has already ended, do nothing */ if (ctx == 0) return 0; /* keep going until we find a non-system, non-hidden file */ do { /* try searching for the next file */ if (_findnext(ctx->handle, &ctx->data) != 0) { /* no more files - close the search and delete the context */ _findclose(ctx->handle); free(ctx); /* return null to indicate that the search is finished */ return 0; } } while ((ctx->data.attrib & (_A_HIDDEN | _A_SYSTEM)) != 0); /* return the name */ l = strlen(ctx->data.name); if (l > outbufsiz - 1) l = outbufsiz - 1; memcpy(outbuf, ctx->data.name, l); outbuf[l] = '\0'; /* return the directory indication */ *isdir = (ctx->data.attrib & _A_SUBDIR) != 0; /* build the full path, if desired */ oss_build_outpathbuf(outpathbuf, outpathbufsiz, ctx->path, ctx->data.name); /* * indicate that the search was successful by returning the non-null * context pointer -- the context has been updated for the new * position in the search, so we just return the same context * structure that we've been using all along */ return ctx; } /* cancel a search */ void os_find_close(void *ctx0) { ossfcx *ctx = (ossfcx *)ctx0; /* if the search was already cancelled, do nothing */ if (ctx == 0) return; /* close the search and delete the context structure */ _findclose(ctx->handle); free(ctx); } #endif /* DEPCRECATED */ /* * Time-zone initialization */ void os_tzset() { /* let the run-time library initialize the time zone */ tzset(); } /* ------------------------------------------------------------------------ */ /* * Get the DOS attributes for a file */ unsigned long oss_get_file_attrs(const char *fname) { unsigned long attrs = 0; struct _finddata_t ff; /* get the DOS attribute flags */ ff.attrib = 0; #ifdef T_WIN32 ff.attrib = GetFileAttributes(fname); #else { long h = _findfirst(fname, &ff); if (h != -1L) _findclose(h); } #endif /* translate the HIDDEN and SYSTEM bits */ if ((ff.attrib & _A_HIDDEN) != 0) attrs |= OSFATTR_HIDDEN; if ((ff.attrib & _A_SYSTEM) != 0) attrs |= OSFATTR_SYSTEM; /* if the RDONLY bit is set, it's readable, otherwise read/write */ attrs |= OSFATTR_READ; if ((ff.attrib & _A_RDONLY) == 0) attrs |= OSFATTR_WRITE; #ifdef T_WIN32 /* * On Windows, attempt to do a full security manager check to determine * our actual access rights to the file. If we fail to get the * security info, we'll give up and return the simple RDONLY * determination we just made above. In most cases, failure to check * the security information will simply be because the file has no ACL, * which means that anyone can access it, hence our tentative result * from the RDONLY attribute is correct after all. */ { /* * get the file's DACL and owner/group security info; first, ask * how much space we need to allocate for the returned information */ DWORD len = 0; SECURITY_INFORMATION info = (SECURITY_INFORMATION)( OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION); GetFileSecurity(fname, info, 0, 0, &len); if (len != 0) { /* allocate a buffer for the security info */ SECURITY_DESCRIPTOR *sd = (SECURITY_DESCRIPTOR *)malloc(len); if (sd != 0) { /* now actually retrieve the security info into our buffer */ if (GetFileSecurity(fname, info, sd, len, &len)) { HANDLE ttok; /* impersonate myself for security purposes */ ImpersonateSelf(SecurityImpersonation); /* * get the security token for the current thread, which * is the context in which the caller will presumably * eventually attempt to access the file */ if (OpenThreadToken( GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &ttok)) { GENERIC_MAPPING genmap = { FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS }; PRIVILEGE_SET privs; DWORD privlen = sizeof(privs); BOOL granted = FALSE; DWORD desired; DWORD allowed; /* test read access */ desired = GENERIC_READ; MapGenericMask(&desired, &genmap); if (AccessCheck(sd, ttok, desired, &genmap, &privs, &privlen, &allowed, &granted)) { /* clear the read bit if reading isn't allowed */ if (allowed == 0) attrs &= ~OSFATTR_READ; } /* test write access */ desired = GENERIC_WRITE; MapGenericMask(&desired, &genmap); if (AccessCheck(sd, ttok, desired, &genmap, &privs, &privlen, &allowed, &granted)) { /* clear the read bit if reading isn't allowed */ if (allowed == 0) attrs &= ~OSFATTR_WRITE; } /* done with the thread token */ CloseHandle(ttok); } /* terminate our security self-impersonation */ RevertToSelf(); } /* free the allocated security info buffer */ free(sd); } } } #endif /* T_WIN32 */ /* return the attributes */ return attrs; } /* * Get file status information */ int os_file_stat(const char *fname, int follow_links, os_file_stat_t *s) { struct __stat64 info; // $$$ we should support Windows symlinks and junction points /* get the file information */ if (_stat64(fname, &info)) return FALSE; /* translate the status fields */ s->sizelo = (uint32_t)(info.st_size & 0xFFFFFFFF); s->sizehi = (uint32_t)(info.st_size >> 32); s->cre_time = (os_time_t)info.st_ctime; s->mod_time = (os_time_t)info.st_mtime; s->acc_time = (os_time_t)info.st_atime; s->mode = info.st_mode; /* get the attributes */ s->attrs = oss_get_file_attrs(fname); /* success */ return TRUE; } /* * Resolve a symbolic link */ int os_resolve_symlink(const char *fname, char *target, size_t target_size) { /* * We don't currently support symbolic links. (We should change this * to support Windows symlinks and junction points. */ //$$$ return FALSE; } #endif /* MICROSOFT */ /* ------------------------------------------------------------------------ */ /* * Create a directory */ #ifdef MSDOS #ifdef TURBO #define _mkdir(dir) mkdir(dir) #endif #ifdef DJGPP #define _mkdir(dir) mkdir(dir, S_IWUSR) #endif int os_mkdir(const char *dir, int create_parents) { /* * if we're to create the intermediate parent folders, and the path * contains multiple elements, recursively create the parent * directories first */ if (create_parents && (strchr(dir, ':') != 0 || strchr(dir, '/') != 0 || strchr(dir, '\\') != 0)) { char par[OSFNMAX]; /* extract the parent path */ os_get_path_name(par, sizeof(par), dir); /* if the parent doesn't already exist, create it recursively */ if (par[0] != '\0' && osfacc(par) && !os_mkdir(par, TRUE)) return FALSE; } /* create the directory */ return _mkdir(dir) == 0; } #endif /* ------------------------------------------------------------------------ */ /* * Remove a directory */ #ifdef MSDOS #ifdef TURBO #define _rmdir(dir) rmdir(dir) #endif #ifdef DJGPP #define _rmdir(dir) rmdir(dir) #endif int os_rmdir(const char *dir) { return _rmdir(dir) == 0; } #endif /* ------------------------------------------------------------------------ */ /* * OS/2 implementation of directory functions */ #ifdef MSOS2 /* C library context structure used for file searches */ #ifdef __32BIT__ # define DosFindFirst(a,b,c,d,e,f) DosFindFirst(a,b,c,d,e,f,FIL_STANDARD) typedef FILEFINDBUF3 oss_c_ffcx; typedef ULONG count_t; #else /* !__32BIT__ */ # define DosFindFirst(a,b,c,d,e,f) DosFindFirst(a,b,c,d,e,f,0L) typedef FILEFINDBUF oss_c_ffcx; typedef USHORT count_t; #endif /* __32BIT__ */ struct oss_find_ctx_t { oss_c_ffcx ff; int found; }; int os_open_dir(const char *dir, osdirhdl_t *hdl) { char *pat; size_t patsiz; struct oss_find_ctx_t *ctx; HDIR hdir = HDIR_SYSTEM; count_t scnt = 1; /* create a copy of the string with "\*.*" appended */ patsiz = strlen(dir) + 32; pat = (char *)malloc(patsiz); os_build_full_path(pat, patsiz, dir, "*.*"); if (pat == 0) return FALSE; /* allocate a search block structure */ ctx = (struct oss_find_ctx_t *)malloc(sizeof(struct oss_find_ctx_t)); if (ctx == 0) { free(pat); return FALSE; } /* begin the search */ ctx->found = (DosFindFirst(pat, &hdir, FILE_DIRECTORY | FILE_NORMAL, &ctx->ff, sizeof(ctx->ff), &scnt) == 0); /* done with the pattern buffer */ free(pat); /* return the context as the search handle, and indicate success */ *hdl = ctx; return TRUE; } int os_read_dir(osdirhdl_t hdl, char *buf, size_t buflen) { count_t scnt = 1; /* cast the handle to our private structure */ struct oss_find_ctx_t *ctx = (oss_find_ctx_t *)hdl; /* if we're out of files, return failure */ if (!ctx->found) return FALSE; /* return the current file */ safe_strcpyl(buf, buflen, ctx->ff.achName, ctx->ff.cchName); /* move on to the next file */ ctx->found = (DosFindNext( HDIR_SYSTEM, &ctx->ff, sizeof(ctx->ff), &scnt) == 0); /* success */ return TRUE; } void os_close_dir(osdirhdl_t hdl) { /* free the context structure */ free(hdl); } #if 0 // the os_find_xxx family of functions has been DEPRECATED typedef struct { /* C library context structure */ oss_c_ffcx ff; /* original search path */ char path[1]; } ossfcx; void *os_find_first_file(const char *dir, const char *pattern, char *outbuf, size_t outbufsiz, int *isdir, char *outpathbuf, size_t outpathbufsiz) { ossfcx *ctx; char realpat[OSFNMAX]; size_t l; HDIR hdir = HDIR_SYSTEM; count_t scnt = 1; size_t path_len; const char *lastsep; const char *p; /* * construct the filename pattern from the directory and pattern * segments, using "*" as the default wildcard pattern if no * explicit pattern was provided */ strcpy(realpat, dir); if ((l = strlen(realpat)) != 0 && realpat[l - 1] != '\\') realpat[l++] = '\\'; if (pattern == 0) strcpy(realpat + l, "*"); else strcpy(realpat + l, pattern); /* find the last separator in the original path */ for (p = realpat, lastsep = 0 ; *p != '\0' ; ++p) { /* if this is a separator, remember it */ if (*p == '\\' || *p == '/' || *p == ':') lastsep = p; } /* * if we found a separator, the path prefix is everything up to and * including the separator; otherwise, there's no path prefix */ if (lastsep != 0) { /* the length of the path includes the separator */ path_len = lastsep + 1 - realpat; } else { /* there's no path prefix - everything is in the current directory */ path_len = 0; } /* allocate a context */ ctx = (ossfcx *)malloc(sizeof(ossfcx) + path_len); /* call DOS to search for a matching file */ if (DosFindFirst(realpat, &hdir, FILE_DIRECTORY | FILE_NORMAL, &ctx->ff, sizeof(ctx->ff), &scnt)) { /* no dice - delete the context and return failure */ free(ctx); return 0; } /* if it's a HIDDEN or SYSTEM file, skip it */ while ((ctx->ff.attrFile & (FILE_HIDDEN | FILE_SYSTEM)) != 0) { /* skip to the next file */ if (DosFindNext(HDIR_SYSTEM, &ctx->ff, sizeof(ctx->ff), &scnt)) { /* no more files - delete the context and give up */ free(ctx); return 0; } } /* copy the path to the context */ memcpy(ctx->path, realpat, path_len); ctx->path[path_len] = '\0'; /* return the name */ l = ctx->ff.cchName; if (l > outbufsiz - 1) l = outbufsiz - 1; memcpy(outbuf, ctx->ff.achName, l); outbuf[l] = '\0'; /* return the directory indication */ *isdir = (ctx->ff.attrFile & FILE_DIRECTORY) != 0; /* build the full path, if desired */ oss_build_outpathbuf(outpathbuf, outpathbufsiz, ctx->path, ctx->ff.data.name); /* * return the context - it will be freed later when find-next * reaches the last file or find-close is called to cancel the * search */ return ctx; } /* find the next file */ void *os_find_next_file(void *ctx0, char *outbuf, size_t outbufsiz, int *isdir, char *outpathbuf, size_t outpathbufsiz) { ossfcx *ctx = (ossfcx *)ctx0; size_t l; /* if the search has already ended, do nothing */ if (ctx == 0) return 0; /* keep going until we find a non-system, non-hidden file */ do { /* try searching for the next file */ if (DosFindNext(HDIR_SYSTEM, &ctx->ff, sizeof(ctx->ff), &scnt)) { /* no more files - delete the context and give up */ free(ctx); /* return null to indicate that the search is finished */ return 0; } } while ((ctx->ff.attrFile & (FILE_HIDDEN | FILE_SYSTEM)) != 0); /* return the name */ l = ctx->ff.cchName; if (l > outbufsiz - 1) l = outbufsiz - 1; memcpy(outbuf, ctx->ff.achName, l); outbuf[l] = '\0'; /* return the directory indication */ *isdir = (ctx->ff.attrFile & FILE_DIRECTORY) != 0; /* build the full path, if desired */ oss_build_outpathbuf(outpathbuf, outpathbufsiz, ctx->ff.path, ctx->ff.data.name); /* * indicate that the search was successful by returning the non-null * context pointer -- the context has been updated for the new * position in the search, so we just return the same context * structure that we've been using all along */ return ctx; } /* cancel a search */ void os_find_close(void *ctx0) { ossfcx *ctx = (ossfcx *)ctx0; /* if the search was already cancelled, do nothing */ if (ctx == 0) return; /* delete the context structure */ free(ctx); } #endif /* DEPRECATED */ #endif /* MSOS2 */ #ifdef MSDOS /* * Check for a special filename */ enum os_specfile_t os_is_special_file(const char *fname) { /* check for '.' */ if (strcmp(fname, ".") == 0) return OS_SPECFILE_SELF; /* check for '..' */ if (strcmp(fname, "..") == 0) return OS_SPECFILE_PARENT; /* not a special file */ return OS_SPECFILE_NONE; } #endif /* MSDOS */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads2/osrestad.c������������������������������������������������������������������������0000664�0000000�0000000�00000006312�12650170723�0015647�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/OSRESTAD.C,v 1.1 1999/07/11 00:46:30 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name osrestad.c - Resource String default implementation for TADS 2 Function Implements os_get_str_rsc() using a set of static strings, compiled in to the application, for English versions of the TADS 2 resources. This file is specific to three things: - TADS 2. If the OS functions are used by another application, this implementation must be replaced with one that defines the strings for that application rather than for TADS 2. The actual resource ID's and their meanings are specific to the host application. - English. To translate an application built with this implementation, this file must be replaced with one that defines translated versions of the strings. - Generic operating system. The definitions here could be inappropriate for operating systems where strong local conventions exist. In particular, the dialog button labels might not conform to local conventions. When porting TADS to an operating system whose conventions differ from the ones used here, this file must be replaced. Note that, whenever possible, an OS-level resource mechanism should be used instead of this default implementation to begin with. Notes Modified 06/25/99 MJRoberts - Creation */ #include <string.h> #include "os.h" #include "res.h" /* * The strings, as a static array. * * To translate a version of TADS built using this file, make a new copy * of this file, translate the strings, and modify the makefile to build * a new version of the executable with your translated version of this * file. */ static const char *S_res_strings[] = { /* * OK/Cancel/Yes/No button labels. We provide a shortcut for every * label, to make the buttons work well in character mode. */ "&OK", /* RESID_BTN_OK = 1 */ "&Cancel", /* RESID_BTN_CANCEL = 2 */ "&Yes", /* RESID_BTN_YES = 3 */ "&No", /* RESID_BTN_NO = 4 */ /* reply strings for yorn() */ "[Yy].*", /* RESID_YORN_YES */ "[Nn].*" /* RESID_YORN_NO */ }; /* * Load a string resource */ int os_get_str_rsc(int id, char *buf, size_t buflen) { /* * make sure the index is in range of our array - note that the * index values start at 1, but our array starts at 0, so adjust * accordingly */ if (id < 0 || id >= (int)sizeof(S_res_strings)/sizeof(S_res_strings[0])) return 1; /* make sure the buffer is large enough */ if (buflen < strlen(S_res_strings[id-1]) + 1) return 2; /* copy the string */ strcpy(buf, S_res_strings[id-1]); /* return success */ return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads2/osstzprs.c������������������������������������������������������������������������0000664�0000000�0000000�00000023213�12650170723�0015731�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2012 by Michael J. Roberts. All Rights Reserved. */ /* Name osstzprs.c - portable POSIX-style "TZ" variable parser Function This routine parses a POSIX-style "TZ" variable, filling in an os_tzinfo_t structure for os_get_timezone_info(). Notes Modified 02/13/12 MJRoberts - Creation */ #include <stdlib.h> #include <ctype.h> #include <string.h> #include "os.h" #include "osstzprs.h" #include "lib.h" /* ------------------------------------------------------------------------ */ /* * Internal service routines */ /* * parse a time zone name; returns the end of the name; optionally copies * the string to an output buffer */ static const char *parse_zone_name(char *dst, size_t dstlen, const char *p, size_t *len) { /* if there's a pointer, scan the string */ if (p != 0) { /* skip alphabetics */ for ( ; *len != 0 && isalpha(*p) ; ++p, --*len) { /* * copy to the output if there's room for this character plus a * null terminator */ if (dstlen > 1) *dst++ = *p; } } /* null-terminate the output if there's room */ if (dstlen > 0) *dst = '\0'; /* return the final pointer */ return p; } /* parse digits */ static const char *parse_digits(int *val, const char *p, size_t *len) { for (*val = 0 ; *len != 0 && isdigit(*p) ; ++p, --*len) { *val *= 10; *val += *p - '0'; } return p; } /* * Parse an offset value - [+-]hh[:mm[:ss]]; returns a pointer to the next * character after the parsed offset value, and fills in '*secs' with the * number of seconds represented by the offset value. */ static const char *parse_offset(int *secs, const char *p, size_t *len) { int sign = 1, hh = 0, mm = 0, ss = 0; /* check for a sign (which must be followed by a digit) */ if (*len >= 2 && *p == '+' && isdigit(p[1])) ++p, --*len; else if (*len >= 2 && *p == '-' && isdigit(p[1])) sign = -1, ++p, --*len; /* if we don't have a digit, it's not a number */ if (*len == 0 || !isdigit(*p)) return p; /* parse the hours */ p = parse_digits(&hh, p, len); /* check for minutes */ if (*len >= 3 && *p == ':' && isdigit(p[1]) && isdigit(p[2])) { /* parse the minutes */ ++p, --*len; p = parse_digits(&mm, p, len); /* check for seconds */ if (*len >= 3 && *p == ':' && isdigit(p[1]) && isdigit(p[2])) { ++p, --*len; p = parse_digits(&ss, p, len); } } /* compute the full time in seconds, and apply the sign */ *secs = sign*(hh*60*60 + mm*60 + ss); /* return the updated pointer */ return p; } /* * Parse a DST start/end rule */ static const char *parse_dst_rule(struct os_tzrule_t *rule, const char *p, size_t *len) { /* remember the starting point - we'll return this on failure */ const char *start = p; /* check the date portion */ if (*len >= 2 && (*p == 'J' || *p == 'j') && isdigit(p[1])) { /* Julian day, not counting February 29 */ ++p, --*len; p = parse_digits(&rule->jday, p, len); } else if (*len >= 1 && isdigit(*p)) { /* day of year, counting February 29 */ p = parse_digits(&rule->yday, p, len); /* TZ uses range 0-365 for this format, but yday uses 1-366 */ rule->yday += 1; } else if (*len >= 2 && *p == 'M' && isdigit(p[1])) { /* month.week.day format - start with the month */ ++p, --*len; p = parse_digits(&rule->month, p, len); if (rule->month < 1 || rule->month > 12) return start; /* check for the required '.' and a following digit */ if (*len < 2 || *p != '.' || !isdigit(p[1])) return start; /* parse the week */ ++p, --*len; p = parse_digits(&rule->week, p, len); if (rule->week < 1 || rule->week > 5) return start; /* check for the required '.' and a following digit */ if (*len < 2 || *p != '.' || !isdigit(p[1])) return start; /* parse the day of the week; adjust from 0-6 to our 1-7 range */ ++p, --*len; p = parse_digits(&rule->day, p, len); rule->day += 1; if (rule->day < 1 || rule->day > 7) return start; } else { /* not a valid DST start/end format */ return p; } /* we got the date; check for a time portion */ if (*len >= 2 && *p == '/' && isdigit(p[1])) { /* parse the time; this has the same parsing rules as an offset */ ++p, --*len; p = parse_offset(&rule->time, p, len); } else { /* the default is 2:00 AM */ rule->time = 2*60*60; } /* success - return the updated pointer */ return p; } /* ------------------------------------------------------------------------ */ /* * Parse a "TZ" environment variable string */ int oss_parse_posix_tz(struct os_tzinfo_t *info, const char *tz, size_t len, int west_is_positive) { const char *p; const char *std_name = 0, *dst_name = 0; size_t std_name_len = 0, dst_name_len = 0; int std_ofs = 0, dst_ofs = 0; struct os_tzrule_t dst_start, dst_end; /* clear the daylight savings start/end dates */ memset(&dst_start, 0, sizeof(dst_start)); memset(&dst_end, 0, sizeof(dst_end)); /* if there's no TZ string, return failure */ if (tz == 0) return FALSE; /* skip leading spaces, just in case; if that leaves nothing, fail */ for ( ; len != 0 && isspace(*tz) ; ++tz, --len) ; if (len == 0) return FALSE; /* parse the standard time name; if that's empty, fail */ std_name = tz; tz = parse_zone_name(0, 0, tz, &len); if ((std_name_len = tz - std_name) == 0) return FALSE; /* parse the offset value */ p = tz; if ((tz = parse_offset(&std_ofs, tz, &len)) == p) return FALSE; /* if we're using west-positive notation, convert to os_tzinfo_t specs */ if (west_is_positive) std_ofs = -std_ofs; /* if we're not done, check for a daylight zone */ p = tz; if (len != 0 && (tz = parse_zone_name(0, 0, tz, &len)) != p) { /* found a daylight zone name - note it */ dst_name = p; dst_name_len = tz - p; /* * check for an optional offset value; if it's not present, * daylight time is implicitly one hour ahead of standard time */ p = tz; if ((tz = parse_offset(&dst_ofs, tz, &len)) == p) dst_ofs = std_ofs + 60*60; else if (west_is_positive) dst_ofs = -dst_ofs; } else { /* there's no daylight zone, so just repeat the standard offset */ dst_ofs = std_ofs; } /* check for the daylight time start/end dates */ if (len != 0 && *tz == ',') { /* parse the start date */ p = ++tz; --len; if ((tz = parse_dst_rule(&dst_start, tz, &len)) == p) return FALSE; /* if there's a start date, there has to be an end date */ if (len == 0 || *tz != ',') return FALSE; /* parse the end date */ p = ++tz; --len; if ((tz = parse_dst_rule(&dst_end, tz, &len)) == p) return FALSE; } /* we need to be done now, other than trailing spaces */ for ( ; len != 0 && isspace(*tz) ; ++tz) ; if (len != 0) return FALSE; /* clear the result structure */ memset(info, 0, sizeof(*info)); /* set the offsets */ info->std_ofs = std_ofs; info->dst_ofs = dst_ofs; /* copy the abbreviation strings */ parse_zone_name(info->std_abbr, sizeof(info->std_abbr), std_name, &std_name_len); parse_zone_name(info->dst_abbr, sizeof(info->dst_abbr), dst_name, &dst_name_len); /* copy the daylight start/end rules */ memcpy(&info->dst_start, &dst_start, sizeof(info->dst_start)); memcpy(&info->dst_end, &dst_end, sizeof(info->dst_end)); /* success */ return TRUE; } #ifdef TEST static void show_dst(const struct os_tzrule_t *rule) { if (rule->jday != 0) printf(", jday %d at %d", rule->jday, rule->time); else if (rule->yday != 0) printf(", yday %d at %d", rule->yday, rule->time); else if (rule->month != 0) printf(", M%d W%d D%d at %d", rule->month, rule->week, rule->day, rule->time); } int main(int argc, char **argv) { static const char *t[] = { "EST5", "EST5,", "EST5,M4.1.0", "EST5,M4.1.0,M10.5.0", "EST5EDT", "EST+5EDT", "EST5EDT4", "PST8", "PST8PDT", "PST8PDT7", "PST+8PDT+7", "PST8PDT6:30", "HST10", "CET-1", "CET-1CEST", "CET-1CEST-2", "GMTO0:1GST-0:59", "GMTO0:0:1GST-1:59:59", "PST8PDT,M3.5.0,M10.1.0", "PST8PDT,M4.1.0/2:30,M10.5.0/2:30", "PST8PDT,J91/3,275/0", "PST8PDT,J91/3,275/0x", 0 }; int i; struct os_tzinfo_t info; for (i = 0 ; t[i] != 0 ; ++i) { printf("%s -> ", t[i]); if (oss_parse_posix_tz(&info, t[i], strlen(t[i]), TRUE)) { printf("'%s' %d, '%s' %d", info.std_abbr, info.std_ofs, info.dst_abbr, info.dst_ofs); show_dst(&info.dst_start); show_dst(&info.dst_end); printf("\n"); } else { printf("invalid\n"); } } } #endif /* TEST */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads2/osstzprs.h������������������������������������������������������������������������0000664�0000000�0000000�00000004762�12650170723�0015746�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2012 by Michael J. Roberts. All Rights Reserved. */ /* Name osstzprs.h - portable POSIX-style "TZ" variable parser Function This routine parses a POSIX-style "TZ" variable, filling in an os_tzinfo_t structure for os_get_timezone_info(). This is a portable routine designed as a helper for implementing os_get_timezone_info() on platforms where POSIX TZ strings can be used. Many Unix platforms use this convention. We parse the following formats: EST5 - time zone with standard time only, no daylight time EST5EDT - standard plus daylight time, with implied 1:00 daylight offset EST5EDT4 - standard plus daylight time, with explicit daylight offset EST5:00 - offset can include minutes EST5:00:00 - ...and seconds EST5EDT,M3.2.0,M11.1.0 - adds rules for daylight time start/stop dates This isn't a full drop-in replacement for os_get_timezone_info(), since (a) we don't want to assume that the Unix getenv() API is the way (or the only way) to get the string to parse, and (b) some platforms that use the TZ format might also accept other formats or have other ways of setting the timezone. For Unix-type platforms platforms that don't need to check for other syntax, this should be all you need to do to implement os_get_timezone_info(): #include "osstzprs.h" int os_get_timezone_info(struct os_tzinfo_t *info) { const char *tz = getenv("TZ"); return tz != 0 ? oss_parse_posix_tz(info, tz, strlen(tz), TRUE) : FALSE; } Notes Modified 02/13/12 MJRoberts - Creation */ #ifndef OSSTZPRS_H #define OSSTZPRS_H #include "os.h" #ifdef __cplusplus extern "C" { #endif /* * Parse a string using the syntax of the POSIX "TZ" environment variable. * If successful, fills in *info and returns true; if the string isn't in * one of the recognized formats, returns false. * * 'west_is_positive' says whether UTC offsets are positive or negative to * the west. The POSIX TZ convention is that west is positive, as in * EST5EDT (New York time, 5 hours west of UTC), so this should be set to * TRUE if the source material actually comes from a TZ variable. The * reason we offer this option at all is that the zoneinfo database uses * the opposite convention, where New York would be EST-5EDT. */ int oss_parse_posix_tz(struct os_tzinfo_t *info, const char *tz, size_t len, int west_is_positive); #ifdef __cplusplus } #endif #endif /* OSSTZPRS_H */ ��������������qtads-2.1.7/tads2/ostzposix.c�����������������������������������������������������������������������0000664�0000000�0000000�00000024720�12650170723�0016110�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include <ctype.h> #include <string.h> #include <time.h> #include "os.h" #include "lib.h" #include "osstzprs.h" extern void safe_strcpy(char *dst, size_t dstlen, const char *src); /* Get the local time zone name, as a zoneinfo database key. Many * modern Unix systems use zoneinfo keys as they native timezone * setting, but there are several different ways of storing this on a * per-process and system-wide basis. We'll try looking for the common * mechanisms. */ int os_get_zoneinfo_key( char *buf, size_t buflen ) { /* First, try the TZ environment variable. This is used on nearly * all Unix-alikes for a per-process timezone setting, although * it will only contain a zoneinfo key in newer versions. There * are several possible formats for specifying a zoneinfo key: * * TZ=/usr/share/zoneinfo/America/Los_Angeles * - A full absolute path name to a tzinfo file. We'll sense * this by looking for "/zoneinfo/" in the string, and if we * find it, we'll return the portion after /zoneinfo/. * * TZ=America/Los_Angeles * - Just the zoneinfo key, without a path. If we find a string * that contains all alphabetics, undersores, and slashes, and * has at least one internal slash but doesn't start with a * slash, we probably have a zoneinfo key. We'll see if we * can find a matching file in the usual zoneinfo database * locations: /etc/zoneinfo, /usr/share/zoneinfo; if we can, * we'll return the key name, otherwise we'll assume this * isn't actually a zoneinfo key but just happens to look like * one in terms of format. * * TZ=:America/Los_Angeles * TZ=:/etc/zoneinfo/America/Los_Angeles * - POSIX systems generally use the ":" prefix to signify that * this is a zoneinfo path rather than the old-style "EST5EDT" * type of self-contained zone description. If we see a colon * prefix with a relative path (properly formed in terms of * its character content), we'll simply assume this is a * zoneinfo key without even checking for an existing file, * since there's not much else it could be. If we see an * absolute path, we'll search it for /zoneinfo/ and return * the portion after this, again without checking for an * existing file. */ const char *tz = getenv("TZ"); if (tz != 0 && tz[0] != '\0') { /* check that the string is formatted like a zoneinfo key */ #define tzcharok(c) (isalpha(c) != 0 || (c) == '/' || (c) == '_') int fmt_ok = TRUE; const char *p; fmt_ok &= (tz[0] == ':' || tzcharok(tz[0])); for (p = tz + 1 ; *p != '\0' ; ++p) fmt_ok &= tzcharok(*p); /* proceed only if it has the right format */ if (fmt_ok) { /* check for a leading ':', per POSIX */ if (tz[0] == ':') { /* yes, we have a leading ':', so it's almost certainly * a zoneinfo key; if it's an absolute path, find the * part after /zoneinfo/ */ if (tz[1] == '/') { /* absolute form - look for /zoneinfo/ */ const char *z = strstr(tz, "/zoneinfo/"); if (z != 0) { /* found it - return the part after /zoneinfo/ */ safe_strcpy(buf, buflen, z + 10); return TRUE; } } else { /* relative path - return as-is minus the colon */ safe_strcpy(buf, buflen, tz + 1); return TRUE; } } else { /* There's no colon, so it *might* be a zoneinfo key. * If it's an absolute path containing /zoneinfo/, it's * a solid bet. If it's a relative path, look to see * if we can find a file in one of the usual zoneinfo * database locations. */ if (tz[0] == '/') { /* absolute path - check for /zoneinfo/ */ const char *z = strstr(tz, "/zoneinfo/"); if (z != 0) { /* found it - return the part after /zoneinfo/ */ safe_strcpy(buf, buflen, z + 10); return TRUE; } } else { /* relative path - look for a tzinfo file in the * usual locations */ static const char *dirs[] = { "/etc/zoneinfo", "/usr/share/zoneinfo", 0 }; const char **dir; for (dir = dirs ; *dir != 0 ; ++dir) { /* build this full path */ char fbuf[OSFNMAX]; os_build_full_path(fbuf, sizeof(fbuf), *dir, tz); /* check for a file at this location */ if (!osfacc(fbuf)) { /* got it - looks like a good zoneinfo key */ safe_strcpy(buf, buflen, tz); return TRUE; } } } } } } /* No luck with TZ, so try the system-wide settings next. * * If a file called /etc/timezone exists, it's usually a one-line * text file containing the zoneinfo key. Read and return its * contents. */ FILE *fp; if ((fp = fopen("/etc/timezone", "r")) != 0) { /* read the one-liner */ char lbuf[256]; int ok = FALSE; if (fgets(lbuf, sizeof(lbuf), fp) != 0) { /* strip any trailing newline */ size_t l = strlen(lbuf); if (l != 0 && lbuf[l-1] == '\n') lbuf[l-1] = '\0'; /* if it's in absolute format, return the part after * /zoneinfo/; otherwise just return the string */ if (lbuf[0] == '/') { /* absoltue path - find /zoneinfo/ */ const char *z = strstr(lbuf, "/zoneinfo/"); if (z != 0) { safe_strcpy(buf, buflen, z + 10); ok = TRUE; } } else { /* relative notation - return it as-is */ safe_strcpy(buf, buflen, lbuf); ok = TRUE; } } /* we're done with the file */ fclose(fp); /* if we got our result, return success */ if (ok) return TRUE; } /* If /etc/sysconfig/clock exists, read it and look for a line * starting with ZONE=. This contains the zoneinfo key. */ if ((fp = fopen("/etc/sysconfig/clock", "r")) != 0) { /* scan the file for ZONE=... */ int ok = FALSE; for (;;) { /* read the next line */ char lbuf[256]; if (fgets(lbuf, sizeof(lbuf), fp) == 0) break; /* skip leading spaces */ const char *p; for (p = lbuf ; isspace(*p) ; ++p) ; /* check for ZONE */ if (memicmp(p, "zone", 4) != 0) continue; /* skip spaces after ZONE */ for (p += 4 ; isspace(*p) ; ++p) ; /* check for '=' */ if (*p != '=') continue; /* skip spaces after the '=' */ for (++p ; isspace(*p) ; ++p) ; /* if it's in absolute form, look for /zoneinfo/ */ if (*p == '/') { const char *z = strstr(p, "/zoneinfo/"); if (z != 0) { safe_strcpy(buf, buflen, z + 10); ok = TRUE; } } else { /* relative notation - it's the zoneinfo key */ safe_strcpy(buf, buflen, p); ok = TRUE; } /* that's our ZONE line, so we're done scanning the file */ break; } /* done with the file */ fclose(fp); /* if we got our result, return success */ if (ok) return TRUE; } /* If /etc/localtime is a symbolic link, the linked file is the * actual zoneinfo file. Resolve the link and return the portion * of the path after "/zoneinfo/". */ static const char *elt = "/etc/localtime"; unsigned long mode; char linkbuf[OSFNMAX]; const char *zi; if (osfmode(elt, FALSE, &mode, NULL) && (mode & OSFMODE_LINK) != 0 && os_resolve_symlink(elt, linkbuf, sizeof(linkbuf)) && (zi = strstr(linkbuf, "/zoneinfo/")) != 0) { /* it's a link containing /zoneinfo/, so return the portion * after /zoneinfo/ */ safe_strcpy(buf, buflen, zi + 10); return TRUE; } /* well, we're out of ideas - return failure */ return FALSE; } /* Get timezone information. This can be used as a fallback if the a * zoneinfo key isn't available, or if the caller doesn't use zoneinfo * data. */ int os_get_timezone_info( struct os_tzinfo_t *info ) { /* try parsing environment variable TZ as a POSIX timezone string */ const char *tz = getenv("TZ"); if (tz != 0 && oss_parse_posix_tz(info, tz, strlen(tz), TRUE)) return TRUE; /* fall back on localtime() - that'll at least give us the current * timezone name and GMT offset in most cases */ time_t timer = time(0); const struct tm *tm = localtime(&timer); if (tm != 0) { memset(info, 0, sizeof(*info)); info->is_dst = tm->tm_isdst; if (tm->tm_isdst) { info->dst_ofs = tm->tm_gmtoff; safe_strcpy(info->dst_abbr, sizeof(info->dst_abbr), tm->tm_zone); } else { info->std_ofs = tm->tm_gmtoff; safe_strcpy(info->std_abbr, sizeof(info->std_abbr), tm->tm_zone); } return TRUE; } /* no information is available */ return FALSE; } ������������������������������������������������qtads-2.1.7/tads2/out.c�����������������������������������������������������������������������������0000664�0000000�0000000�00000001450�12650170723�0014630�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/OUT.C,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $"; #endif /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name out.c - output formatter Function Formats output text: word wrap, etc. Notes None Modified 10/27/91 MJRoberts - creation */ #include <string.h> #include "os.h" #include "tio.h" /* * write out a runtime length-prefixed string */ void outfmt(tiocxdef *ctx, uchar *txt) { uint len; VARUSED(ctx); /* read the length prefix */ len = osrp2(txt) - 2; txt += 2; /* write out the string */ tioputslen(ctx, (char *)txt, len); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads2/output.c��������������������������������������������������������������������������0000664�0000000�0000000�00000336617�12650170723�0015401�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/Output.c,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $"; #endif /* * Copyright (c) 1987, 1988 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name output - TADS Interpreter and Compiler formatted Output routines Function Provides formatted output support. Text that is sent through outformat() is displayed with word-wrap so that words fill the line but are not broken across lines. Other routines allow flushing the output buffer, displaying blank lines, and logging output to a file as well as to the display. Notes We use the global variables G_os_pagelength and G_os_linewidth, defined in the OS code, to determine the size of the display area and to determine the number of lines to display between page pauses (MORE prompts). Line breaking and MORE prompting can be handled by the OS code if desired; the USE_MORE macro controls this setting. Returns None Modified 04/05/92 MJRoberts - TADS 2.0 changes 03/29/92 MJRoberts - fix unfound formatstring handling 08/01/91 MJRoberts - no more mode when debugger is running 07/18/91 MJRoberts - improve t_outline [more] behavior 07/01/91 MJRoberts - Mac porting changes 06/05/91 MJRoberts - add format string support 03/27/91 MJRoberts - debugger enhancements 03/10/91 MJRoberts - integrate John's qa-scripter mods 04/24/90 MJRoberts - add \^ (equivalent to calling "caps()" intrinsic) 04/16/89 MJRoberts - add outcformat for compressed strings 10/30/88 MJRoberts - add outhide() and outshow() functions 10/29/88 MJRoberts - break lines on hyphens 12/22/87 MJRoberts - created */ #include <stdio.h> #include <ctype.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include "os.h" #include "std.h" #include "run.h" #include "voc.h" #include "tio.h" #include "mcm.h" #include "dbg.h" #include "cmap.h" /* * use our own isxxx - anything outside the US ASCII range is not reliably * classifiable by the normal C isxxx routines */ #define outissp(c) (((uchar)(c)) <= 127 && isspace((uchar)(c))) #define outisal(c) (((uchar)(c)) <= 127 && isalpha((uchar)(c))) #define outisdg(c) (((uchar)(c)) <= 127 && isdigit((uchar)(c))) #define outisup(c) (((uchar)(c)) <= 127 && isupper((uchar)(c))) #define outislo(c) (((uchar)(c)) <= 127 && islower((uchar)(c))) /* * Turn on formatter-level MORE mode, EXCEPT under any of the following * conditions: * * - this is a MAC OS port *. - this is an HTML TADS interpreter *. - USE_OS_LINEWRAP is defined * * Formatter-level MORE mode and formatter-level line wrapping go together; * you can't have one without the other. So, if USE_OS_LINEWRAP is * defined, we must also use OS-level MORE mode, which means we don't want * formatter-level MORE mode. * * For historical reasons, we check specifically for MAC_OS. This was the * first platform for which OS-level MORE mode and OS-level line wrapping * were invented; at the time, we foolishly failed to anticipate that more * platforms might eventually come along with the same needs, so we coded a * test for MAC_OS rather than some more abstract marker. For * compatibility, we retain this specific test. * * USE_OS_LINEWRAP is intended as the more abstract marker we should * originally have used. A port should #define USE_OS_LINEWRAP in its * system-specific os_xxx.h header to turn on OS-level line wrapping and * OS-level MORE mode. Ports should avoid adding new #ifndef tests for * specific platforms here; we've only retained the MAC_OS test because we * don't want to break the existing MAC_OS port. */ #ifndef MAC_OS # ifndef USE_HTML # ifndef USE_OS_LINEWRAP # define USE_MORE /* activate formatter-level more-mode */ # endif /* USE_OS_LINEWRAP */ # endif /* USE_HTML */ #endif /* MAC_OS */ /* * In HTML mode, don't use MORE mode. Note that we explicitly turn MORE * mode OFF, even though we won't have turned it on above, because it might * have been turned on by an os_xxx.h header. This is here for historical * reasons; in particular, some of the HTML interpreter builds include * headers that were originally written for the normal builds for those * same platforms, and those original headers explicitly #define USE_MORE * somewhere. So, to be absolutely sure we get it right here, we have to * explicitly turn off USE_MORE when compiling for HTML mode. */ #ifdef USE_HTML # ifdef USE_MORE # undef USE_MORE # endif #endif /* * QSPACE is the special character for a quoted space (internally, the * sequence "\ " (backslash-space) is converted to QSPACE). It must not * be any printable character. The value here may need to be changed in * the extremely unlikely event that TADS is ever ported to an EBCDIC * machine. */ #define QSPACE 26 /* * QTAB is a special hard tab character indicator. We use this when we * need to generate a hard tab to send to the underlying output layer * (in particular, we use this to send hard tabs to the HTML formatter * when we're in HTML mode). */ #define QTAB 25 /* maximum width of the display */ #define MAXWIDTH OS_MAXWIDTH /* ------------------------------------------------------------------------ */ /* * Globals and statics. These should really be moved into a context * structure, so that the output formatter subsystem could be shared * among multiple clients. For now, there's no practical problem using * statics, because we only need a single output subsystem at one time. */ /* current script (command input) file */ extern osfildef *scrfp; /* * This should be TRUE if the output should have two spaces after a * period (or other such punctuation. It should generally be TRUE for * fixed-width fonts, and FALSE for proportional fonts. */ static int doublespace = 1; /* * Log file handle and name. If we're copying output to a log file, * these will tell us about the file. */ osfildef *logfp; static char logfname[OSFNMAX]; /* flag indicating whether output has occurred since last check */ static uchar outcnt; /* flag indicating whether hidden output has occurred */ static uchar hidout; /* flag indicating whether to show (TRUE) or hide (FALSE) output */ static uchar outflag; /* flag indicating whether output is hidden for debugging purposes */ int dbghid; /* * Current recursion level in formatter invocation */ static int G_recurse = 0; /* active stream in current recursion level */ static struct out_stream_info *G_cur_stream; /* watchpoint mode flag */ static uchar outwxflag; /* * User filter function. When this function is set, we'll invoke this * function for each string that's displayed through the output * formatter. */ static objnum G_user_filter = MCMONINV; /* ------------------------------------------------------------------------ */ /* * Hack to run with TADS 2.0 with minimal reworking. Rather than using * an allocated output layer context, store our subsystem context * information in some statics. This is less clean than using a real * context, but doesn't create any practical problems as we don't need * to share the output formatter subsystem among multiple simultaneous * callers. */ static runcxdef *runctx; /* execution context */ static uchar *fmsbase; /* format string area base */ static uchar *fmstop; /* format string area top */ static objnum cmdActor; /* current actor */ /* forward declarations of static functions */ static void outstring_stream(struct out_stream_info *stream, char *s); static void outchar_noxlat_stream(struct out_stream_info *stream, char c); static char out_parse_entity(char *outbuf, size_t outbuf_size, char **sp, size_t *slenp); /* ------------------------------------------------------------------------ */ /* * HTML lexical analysis mode */ #define HTML_MODE_NORMAL 0 /* normal text, not in a tag */ #define HTML_MODE_TAG 1 /* parsing inside a tag */ #define HTML_MODE_SQUOTE 2 /* in a single-quoted string in a tag */ #define HTML_MODE_DQUOTE 3 /* in a double-quoted string in a tag */ /* * HTML parsing mode flag for <BR> tags. We defer these until we've * read the full tag in order to obey an HEIGHT attribute we find. When * we encounter a <BR>, we figure out whether we think we'll need a * flush or a blank line; if we find a HEIGHT attribute, we may change * this opinion. */ #define HTML_DEFER_BR_NONE 0 /* no pending <BR> */ #define HTML_DEFER_BR_FLUSH 1 /* only need an outflush() */ #define HTML_DEFER_BR_BLANK 2 /* need an outblank() */ /* * If we're compiling for an HTML-enabled underlying output subsystem, * we want to call the underlying OS layer when switching in and out of * HTML mode. If the underlying system doesn't process HTML, we don't * need to let it know anything about HTML mode. */ #ifdef USE_HTML # define out_start_html(stream) os_start_html() # define out_end_html(stream) os_end_html() #else # define out_start_html(stream) # define out_end_html(stream) #endif /* ------------------------------------------------------------------------ */ /* * Output formatter stream state structure. This structure encapsulates * the state of an individual output stream. */ typedef struct out_stream_info out_stream_info; struct out_stream_info { /* low-level display routine (va_list version) */ void (*do_print)(out_stream_info *stream, const char *str); /* current line position and output column */ uchar linepos; uchar linecol; /* number of lines on the screen (since last MORE prompt) */ int linecnt; /* output buffer */ char linebuf[MAXWIDTH]; /* * attribute buffer - we keep one attribute entry for each character in * the line buffer */ int attrbuf[MAXWIDTH]; /* current attribute for text we're buffering into linebuf */ int cur_attr; /* last attribute we wrote to the osifc layer */ int os_attr; /* CAPS mode - next character output is converted to upper-case */ uchar capsflag; /* NOCAPS mode - next character output is converted to lower-case */ uchar nocapsflag; /* ALLCAPS mode - all characters output are converted to upper-case */ uchar allcapsflag; /* capture information */ mcmcxdef *capture_ctx; /* memory context to use for capturing */ mcmon capture_obj; /* object holding captured output */ uint capture_ofs; /* write offset in capture object */ int capturing; /* true -> we are capturing output */ /* "preview" state for line flushing */ int preview; /* flag indicating that we just flushed a new line */ int just_did_nl; /* this output stream uses "MORE" mode */ int use_more_mode; /* * This output stream uses OS-level line wrapping - if this is set, * the output formatter will not insert a newline at the end of a * line that it's flushing for word wrapping, but will instead let * the underlying OS display layer handle the wrapping. */ int os_line_wrap; /* * Flag indicating that the underlying output system wants to * receive its output as HTML. * * If this is true, we'll pass through HTML to the underlying output * system, and in addition generate HTML sequences for certain * TADS-native escapes (for example, we'll convert the "\n" sequence * to a <BR> sequence). * * If this is false, we'll do just the opposite: we'll remove HTML * from the output stream and convert it into normal text sequences. */ int html_target; /* * Flag indicating that the target uses plain text. If this flag is * set, we won't add the OS escape codes for highlighted characters. */ int plain_text_target; /* * Flag indicating that the caller is displaying HTML. We always * start off in text mode; the client can switch to HTML mode by * displaying a special escape sequence, and can switch back to text * mode by displaying a separate special escape sequence. */ int html_mode; /* current lexical analysis mode */ unsigned int html_mode_flag; /* <BR> defer mode */ unsigned int html_defer_br; /* * HTML "ignore" mode - we suppress all output when parsing the * contents of a <TITLE> or <ABOUTBOX> tag */ int html_in_ignore; /* * HTML <TITLE> mode - when we're in this mode, we're gathering the * title (i.e., we're inside a <TITLE> tag's contents). We'll copy * characters to the title buffer rather than the normal output * buffer, and then call os_set_title() when we reach the * tag. */ int html_in_title; /* buffer for the title */ char html_title_buf[256]; /* pointer to next available character in title buffer */ char *html_title_ptr; /* quoting level */ int html_quote_level; /* PRE nesting level */ int html_pre_level; /* * Parsing mode flag for ALT attributes. If we're parsing a tag * that allows ALT, such as IMG or SOUND, we'll set this flag, then * insert the ALT text if we encounter it during parsing. */ int html_allow_alt; }; /* * Default output converter. This is the output converter for the * standard display. Functions in the public interface that do not * specify an output converter will use this converter by default. */ static out_stream_info G_std_disp; /* * Log file converter. This is the output converter for a log file. * Whenever we open a log file, we'll initialize this converter; as we * display text to the main display, we'll also copy it to the log file. * * We maintain an entire separate conversion context for the log file, * so that we can perform a different set of conversions on it. We may * want, for example, to pass HTML text through to the OS display * subsystem (this is the case for the HTML-enabled interpreter), but * we'll still want to convert log file output to text. By keeping a * separate display context for the log file, we can format output to * the log file using an entirely different style than we do for the * display. */ static out_stream_info G_log_disp; /* ------------------------------------------------------------------------ */ /* * low-level output handlers for the standard display and log file */ /* standard display printer */ static void do_std_print(out_stream_info *stream, const char *str) { VARUSED(stream); /* display the text through the OS layer */ os_printz(str); } /* log file printer */ static void do_log_print(out_stream_info *stream, const char *str) { VARUSED(stream); /* display to the log file */ if (logfp != 0 && G_os_moremode) { os_fprintz(logfp, str); osfflush(logfp); } } /* ------------------------------------------------------------------------ */ /* * initialize a generic output formatter state structure */ static void out_state_init(out_stream_info *stream) { /* start out at the first column */ stream->linepos = 0; stream->linecol = 0; stream->linebuf[0] = '\0'; /* set normal text attributes */ stream->cur_attr = 0; stream->os_attr = 0; /* start out at the first line */ stream->linecnt = 0; /* we're not in either "caps", "nocaps", or "allcaps" mode yet */ stream->capsflag = stream->nocapsflag = stream->allcapsflag = FALSE; /* we're not capturing yet */ stream->capturing = FALSE; stream->capture_obj = MCMONINV; /* we aren't previewing a line yet */ stream->preview = 0; /* we haven't flushed a new line yet */ stream->just_did_nl = FALSE; /* presume this stream does not use "MORE" mode */ stream->use_more_mode = FALSE; /* presume this stream uses formatter-level line wrapping */ stream->os_line_wrap = FALSE; /* assume that the underlying system is not HTML-enabled */ stream->html_target = FALSE; /* presume this target accepts OS highlighting sequences */ stream->plain_text_target = FALSE; /* start out in text mode */ stream->html_mode = FALSE; /* start out in "normal" lexical state */ stream->html_mode_flag = HTML_MODE_NORMAL; /* not in an ignored tag yet */ stream->html_in_ignore = FALSE; /* not in title mode yet */ stream->html_in_title = FALSE; /* not yet deferring line breaks */ stream->html_defer_br = HTML_DEFER_BR_NONE; /* not yet in quotes */ stream->html_quote_level = 0; /* not yet in a PRE block */ stream->html_pre_level = 0; /* not in an ALT tag yet */ stream->html_allow_alt = FALSE; } /* ------------------------------------------------------------------------ */ /* * initialize a standard display stream */ static void out_init_std(out_stream_info *stream) { /* there's no user output filter function yet */ out_set_filter(MCMONINV); /* initialize the basic stream state */ out_state_init(stream); /* set up the low-level output routine */ G_std_disp.do_print = do_std_print; #ifdef USE_MORE /* * We're compiled for MORE mode, and we're not compiling for an * underlying HTML formatting layer, so use MORE mode for the * standard display stream. */ stream->use_more_mode = TRUE; #else /* * We're compiled for OS-layer (or HTML-layer) MORE handling. For * this case, use OS-layer (or HTML-layer) line wrapping as well. */ stream->os_line_wrap = TRUE; #endif #ifdef USE_HTML /* * if we're compiled for HTML mode, set the standard output stream * so that it knows it has an HTML target - this will ensure that * HTML tags are passed through to the underlying stream, and that * we generate HTML equivalents for our own control sequences */ stream->html_target = TRUE; #endif } /* * initialize a standard log file stream */ static void out_init_log(out_stream_info *stream) { /* initialize the basic stream state */ out_state_init(stream); /* set up the low-level output routine */ stream->do_print = do_log_print; /* use plain text in the log file stream */ stream->plain_text_target = TRUE; } /* ------------------------------------------------------------------------ */ /* * table of '&' character name sequences */ struct amp_tbl_t { /* entity name */ const char *cname; /* HTML Unicode character value */ uint html_cval; /* native character set expansion */ char *expan; }; /* * HTML entity mapping table. When we're in non-HTML mode, we keep our * own expansion table so that we can map HTML entity names into the * local character set. * * The entries in this table must be in sorted order (by HTML entity * name), because we use a binary search to find an entity name in the * table. */ static struct amp_tbl_t amp_tbl[] = { { "AElig", 198, 0 }, { "Aacute", 193, 0 }, { "Abreve", 258, 0 }, { "Acirc", 194, 0 }, { "Agrave", 192, 0 }, { "Alpha", 913, 0 }, { "Aogon", 260, 0 }, { "Aring", 197, 0 }, { "Atilde", 195, 0 }, { "Auml", 196, 0 }, { "Beta", 914, 0 }, { "Cacute", 262, 0 }, { "Ccaron", 268, 0 }, { "Ccedil", 199, 0 }, { "Chi", 935, 0 }, { "Dagger", 8225, 0 }, { "Dcaron", 270, 0 }, { "Delta", 916, 0 }, { "Dstrok", 272, 0 }, { "ETH", 208, 0 }, { "Eacute", 201, 0 }, { "Ecaron", 282, 0 }, { "Ecirc", 202, 0 }, { "Egrave", 200, 0 }, { "Eogon", 280, 0 }, { "Epsilon", 917, 0 }, { "Eta", 919, 0 }, { "Euml", 203, 0 }, { "Gamma", 915, 0 }, { "Iacute", 205, 0 }, { "Icirc", 206, 0 }, { "Igrave", 204, 0 }, { "Iota", 921, 0 }, { "Iuml", 207, 0 }, { "Kappa", 922, 0 }, { "Lacute", 313, 0 }, { "Lambda", 923, 0 }, { "Lcaron", 317, 0 }, { "Lstrok", 321, 0 }, { "Mu", 924, 0 }, { "Nacute", 323, 0 }, { "Ncaron", 327, 0 }, { "Ntilde", 209, 0 }, { "Nu", 925, 0 }, { "OElig", 338, 0 }, { "Oacute", 211, 0 }, { "Ocirc", 212, 0 }, { "Odblac", 336, 0 }, { "Ograve", 210, 0 }, { "Omega", 937, 0 }, { "Omicron", 927, 0 }, { "Oslash", 216, 0 }, { "Otilde", 213, 0 }, { "Ouml", 214, 0 }, { "Phi", 934, 0 }, { "Pi", 928, 0 }, { "Prime", 8243, 0 }, { "Psi", 936, 0 }, { "Racute", 340, 0 }, { "Rcaron", 344, 0 }, { "Rho", 929, 0 }, { "Sacute", 346, 0 }, { "Scaron", 352, 0 }, { "Scedil", 350, 0 }, { "Sigma", 931, 0 }, { "THORN", 222, 0 }, { "Tau", 932, 0 }, { "Tcaron", 356, 0 }, { "Tcedil", 354, 0 }, { "Theta", 920, 0 }, { "Uacute", 218, 0 }, { "Ucirc", 219, 0 }, { "Udblac", 368, 0 }, { "Ugrave", 217, 0 }, { "Upsilon", 933, 0 }, { "Uring", 366, 0 }, { "Uuml", 220, 0 }, { "Xi", 926, 0 }, { "Yacute", 221, 0 }, { "Yuml", 376, 0 }, { "Zacute", 377, 0 }, { "Zcaron", 381, 0 }, { "Zdot", 379, 0 }, { "Zeta", 918, 0 }, { "aacute", 225, 0 }, { "abreve", 259, 0 }, { "acirc", 226, 0 }, { "acute", 180, 0 }, { "aelig", 230, 0 }, { "agrave", 224, 0 }, { "alefsym", 8501, 0 }, { "alpha", 945, 0 }, { "amp", '&', 0 }, { "and", 8743, 0 }, { "ang", 8736, 0 }, { "aogon", 261, 0 }, { "aring", 229, 0 }, { "asymp", 8776, 0 }, { "atilde", 227, 0 }, { "auml", 228, 0 }, { "bdquo", 8222, 0 }, { "beta", 946, 0 }, { "breve", 728, 0 }, { "brvbar", 166, 0 }, { "bull", 8226, 0 }, { "cacute", 263, 0 }, { "cap", 8745, 0 }, { "caron", 711, 0 }, { "ccaron", 269, 0 }, { "ccedil", 231, 0 }, { "cedil", 184, 0 }, { "cent", 162, 0 }, { "chi", 967, 0 }, { "circ", 710, 0 }, { "clubs", 9827, 0 }, { "cong", 8773, 0 }, { "copy", 169, 0 }, { "crarr", 8629, 0 }, { "cup", 8746, 0 }, { "curren", 164, 0 }, { "dArr", 8659, 0 }, { "dagger", 8224, 0 }, { "darr", 8595, 0 }, { "dblac", 733, 0 }, { "dcaron", 271, 0 }, { "deg", 176, 0 }, { "delta", 948, 0 }, { "diams", 9830, 0 }, { "divide", 247, 0 }, { "dot", 729, 0 }, { "dstrok", 273, 0 }, { "eacute", 233, 0 }, { "ecaron", 283, 0 }, { "ecirc", 234, 0 }, { "egrave", 232, 0 }, { "emdash", 8212, 0 }, { "empty", 8709, 0 }, { "endash", 8211, 0 }, { "eogon", 281, 0 }, { "epsilon", 949, 0 }, { "equiv", 8801, 0 }, { "eta", 951, 0 }, { "eth", 240, 0 }, { "euml", 235, 0 }, { "exist", 8707, 0 }, { "fnof", 402, 0 }, { "forall", 8704, 0 }, { "frac12", 189, 0 }, { "frac14", 188, 0 }, { "frac34", 190, 0 }, { "frasl", 8260, 0 }, { "gamma", 947, 0 }, { "ge", 8805, 0 }, { "gt", '>', 0 }, { "hArr", 8660, 0 }, { "harr", 8596, 0 }, { "hearts", 9829, 0 }, { "hellip", 8230, 0 }, { "iacute", 237, 0 }, { "icirc", 238, 0 }, { "iexcl", 161, 0 }, { "igrave", 236, 0 }, { "image", 8465, 0 }, { "infin", 8734, 0 }, { "int", 8747, 0 }, { "iota", 953, 0 }, { "iquest", 191, 0 }, { "isin", 8712, 0 }, { "iuml", 239, 0 }, { "kappa", 954, 0 }, { "lArr", 8656, 0 }, { "lacute", 314, 0 }, { "lambda", 955, 0 }, { "lang", 9001, 0 }, { "laquo", 171, 0 }, { "larr", 8592, 0 }, { "lcaron", 318, 0 }, { "lceil", 8968, 0 }, { "ldq", 8220, 0 }, { "ldquo", 8220, 0 }, { "le", 8804, 0 }, { "lfloor", 8970, 0 }, { "lowast", 8727, 0 }, { "loz", 9674, 0 }, { "lsaquo", 8249, 0 }, { "lsq", 8216, 0 }, { "lsquo", 8216, 0 }, { "lstrok", 322, 0 }, { "lt", '<', 0 }, { "macr", 175, 0 }, { "mdash", 8212, 0 }, { "micro", 181, 0 }, { "middot", 183, 0 }, { "minus", 8722, 0 }, { "mu", 956, 0 }, { "nabla", 8711, 0 }, { "nacute", 324, 0 }, { "nbsp", QSPACE, 0 }, { "ncaron", 328, 0 }, { "ndash", 8211, 0 }, { "ne", 8800, 0 }, { "ni", 8715, 0 }, { "not", 172, 0 }, { "notin", 8713, 0 }, { "nsub", 8836, 0 }, { "ntilde", 241, 0 }, { "nu", 957, 0 }, { "oacute", 243, 0 }, { "ocirc", 244, 0 }, { "odblac", 337, 0 }, { "oelig", 339, 0 }, { "ogon", 731, 0 }, { "ograve", 242, 0 }, { "oline", 8254, 0 }, { "omega", 969, 0 }, { "omicron", 959, 0 }, { "oplus", 8853, 0 }, { "or", 8744, 0 }, { "ordf", 170, 0 }, { "ordm", 186, 0 }, { "oslash", 248, 0 }, { "otilde", 245, 0 }, { "otimes", 8855, 0 }, { "ouml", 246, 0 }, { "para", 182, 0 }, { "part", 8706, 0 }, { "permil", 8240, 0 }, { "perp", 8869, 0 }, { "phi", 966, 0 }, { "pi", 960, 0 }, { "piv", 982, 0 }, { "plusmn", 177, 0 }, { "pound", 163, 0 }, { "prime", 8242, 0 }, { "prod", 8719, 0 }, { "prop", 8733, 0 }, { "psi", 968, 0 }, { "quot", '"', 0 }, { "rArr", 8658, 0 }, { "racute", 341, 0 }, { "radic", 8730, 0 }, { "rang", 9002, 0 }, { "raquo", 187, 0 }, { "rarr", 8594, 0 }, { "rcaron", 345, 0 }, { "rceil", 8969, 0 }, { "rdq", 8221, 0 }, { "rdquo", 8221, 0 }, { "real", 8476, 0 }, { "reg", 174, 0 }, { "rfloor", 8971, 0 }, { "rho", 961, 0 }, { "rsaquo", 8250, 0 }, { "rsq", 8217, 0 }, { "rsquo", 8217, 0 }, { "sacute", 347, 0 }, { "sbquo", 8218, 0 }, { "scaron", 353, 0 }, { "scedil", 351, 0 }, { "sdot", 8901, 0 }, { "sect", 167, 0 }, { "shy", 173, 0 }, { "sigma", 963, 0 }, { "sigmaf", 962, 0 }, { "sim", 8764, 0 }, { "spades", 9824, 0 }, { "sub", 8834, 0 }, { "sube", 8838, 0 }, { "sum", 8721, 0 }, { "sup", 8835, 0 }, { "sup1", 185, 0 }, { "sup2", 178, 0 }, { "sup3", 179, 0 }, { "supe", 8839, 0 }, { "szlig", 223, 0 }, { "tau", 964, 0 }, { "tcaron", 357, 0 }, { "tcedil", 355, 0 }, { "there4", 8756, 0 }, { "theta", 952, 0 }, { "thetasym", 977, 0 }, { "thorn", 254, 0 }, { "thorn", 254, 0 }, { "tilde", 732, 0 }, { "times", 215, 0 }, { "trade", 8482, 0 }, { "uArr", 8657, 0 }, { "uacute", 250, 0 }, { "uarr", 8593, 0 }, { "ucirc", 251, 0 }, { "udblac", 369, 0 }, { "ugrave", 249, 0 }, { "uml", 168, 0 }, { "upsih", 978, 0 }, { "upsilon", 965, 0 }, { "uring", 367, 0 }, { "uuml", 252, 0 }, { "weierp", 8472, 0 }, { "xi", 958, 0 }, { "yacute", 253, 0 }, { "yen", 165, 0 }, { "yuml", 255, 0 }, { "zacute", 378, 0 }, { "zcaron", 382, 0 }, { "zdot", 380, 0 }, { "zeta", 950, 0 } }; /* ------------------------------------------------------------------------ */ /* * turn on CAPS mode for a stream */ static void outcaps_stream(out_stream_info *stream) { /* turn on CAPS mode */ stream->capsflag = TRUE; /* turn off NOCAPS and ALLCAPS mode */ stream->nocapsflag = FALSE; stream->allcapsflag = FALSE; } /* * turn on NOCAPS mode for a stream */ static void outnocaps_stream(out_stream_info *stream) { /* turn on NOCAPS mode */ stream->nocapsflag = TRUE; /* turn off CAPS and ALLCAPS mode */ stream->capsflag = FALSE; stream->allcapsflag = FALSE; } /* * turn on or off ALLCAPS mode for a stream */ static void outallcaps_stream(out_stream_info *stream, int all_caps) { /* set the ALLCAPS flag */ stream->allcapsflag = all_caps; /* clear the CAPS and NOCAPS flags */ stream->capsflag = FALSE; stream->nocapsflag = FALSE; } /* ------------------------------------------------------------------------ */ /* * write a string to a stream */ static void stream_print(out_stream_info *stream, char *str) { /* call the stream's do_print method */ (*stream->do_print)(stream, str); } /* * Write out a line */ static void t_outline(out_stream_info *stream, int nl, const char *txt, const int *attr) { extern int scrquiet; /* * Check the "script quiet" mode - this indicates that we're reading * a script and not echoing output to the display. If this mode is * on, and we're writing to the display, suppress this write. If * the mode is off, or we're writing to another stream (such as the * log file), show the output as normal. */ if (!scrquiet || stream != &G_std_disp) { size_t i; char buf[MAXWIDTH]; char *dst; /* * Check to see if we've reached the end of the screen, and if * so run the MORE prompt. Note that we don't make this check * at all if USE_MORE is undefined, since this means that the OS * layer code is taking responsibility for pagination issues. * We also don't display a MORE prompt when reading from a * script file. * * Note that we suppress the MORE prompt if nl == 0, since this * is used to flush a partial line of text without starting a * new line (for example, when displaying a prompt where the * input will appear on the same line following the prompt). * * Skip the MORE prompt if this stream doesn't use it. */ if (stream->use_more_mode && scrfp == 0 && G_os_moremode && nl != 0 && nl != 4 && stream->linecnt++ >= G_os_pagelength) { /* display the MORE prompt */ out_more_prompt(); } /* * Display the text. Run through the text in pieces; each time the * attributes change, set attributes at the osifc level. */ for (i = 0, dst = buf ; txt[i] != '\0' ; ++i) { /* if the attribute is changing, notify osifc */ if (attr != 0 && attr[i] != stream->os_attr) { /* flush the preceding text */ if (dst != buf) { *dst = '\0'; stream_print(stream, buf); } /* set the new attribute */ os_set_text_attr(attr[i]); /* remember this as the last OS attribute */ stream->os_attr = attr[i]; /* start with a fresh buffer */ dst = buf; } /* buffer this character */ *dst++ = txt[i]; } /* flush the last chunk of text */ if (dst != buf) { *dst = '\0'; stream_print(stream, buf); } } } /* ------------------------------------------------------------------------ */ /* * Flush the current line to the display. The 'nl' argument specifies * what kind of flushing to do: * * 0: flush the current line but do not start a new line; more text will * follow on the current line. This is used, for example, to flush text * after displaying a prompt and before waiting for user input. * * 1: flush the line and start a new line. * * 2: flush the line as though starting a new line, but don't add an * actual newline character to the output, since the underlying OS * display code will handle this. Instead, add a space after the line. * (This differs from mode 0 in that mode 0 shouldn't add anything at * all after the line.) * * 3: "preview" mode. Flush the line, but do not start a new line, and * retain the current text in the buffer. This is used for systems that * handle the line wrapping in the underlying system code to flush a * partially filled line that will need to be flushed again later. * * 4: same as mode 0, but used for internal buffer flushes only. Do not * involve the underlying OS layer in this type of flush - simply flush * our buffers with no separation. */ /* flush a given output stream */ static void outflushn_stream(out_stream_info *stream, int nl) { int i; /* null-terminate the current output line buffer */ stream->linebuf[stream->linepos] = '\0'; /* note the position of the last character to display */ i = stream->linepos - 1; /* if we're adding anything, remove trailing spaces */ if (nl != 0 && nl != 4) { /* look for last non-space character */ for ( ; i >= 0 && outissp(stream->linebuf[i]) ; --i) ; } /* check the output mode */ if (nl == 3) { /* * this is the special "preview" mode -- only display the part * that we haven't already previewed for this same line */ if (i + 1 > stream->preview) { /* write out the line */ t_outline(stream, 0, &stream->linebuf[stream->preview], &stream->attrbuf[stream->preview]); /* skip past the part we wrote */ stream->preview += strlen(&stream->linebuf[stream->preview]); } } else { char *suffix; /* extra text to add after the flushed text */ int countnl = 0; /* true if line counts for [more] paging */ /* null-terminate the buffer at the current position */ stream->linebuf[++i] = '\0'; /* check the mode */ switch(nl) { case 0: case 3: case 4: /* no newline - just flush out what we have with no suffix */ suffix = 0; break; case 1: /* * Add a newline. If there's nothing in the current line, * or we just wrote out a newline, do not add an extra * newline. Keep all newlines in PRE mode. */ if (stream->linecol != 0 || !stream->just_did_nl || stream->html_pre_level != 0) { /* add a newline after the text */ suffix = "\n"; /* count the line in the page size */ countnl = 1; } else { /* don't add a newline */ suffix = 0; } break; case 2: /* * we're going to depend on the underlying OS output layer * to do line breaking, so don't add a newline, but do add a * space, so that the underlying OS layer knows we have a * word break here */ suffix = " "; break; } /* * display the line, as long as we have something buffered to * display; even if we don't, display it if our column is * non-zero and we didn't just do a newline, since this must * mean that we've flushed a partial line and are just now doing * the newline */ if (stream->linebuf[stream->preview] != '\0' || (stream->linecol != 0 && !stream->just_did_nl) || stream->html_pre_level > 0) { /* write it out */ t_outline(stream, countnl, &stream->linebuf[stream->preview], &stream->attrbuf[stream->preview]); /* write the suffix, if any */ if (suffix != 0) t_outline(stream, 0, suffix, 0); } /* generate an HTML line break if necessary */ if (nl == 1 && stream->html_mode && stream->html_target) t_outline(stream, 0, "
", 0); if (nl == 0) { /* we're not displaying a newline, so flush what we have */ os_flush(); } else { /* we displayed a newline, so reset the column position */ stream->linecol = 0; } /* reset the line output buffer position */ stream->linepos = stream->preview = 0; /* * If we just output a newline, note it. If we didn't just * output a newline, but we did write out anything else, note * that we're no longer at the start of a line on the underlying * output device. */ if (nl == 1) stream->just_did_nl = TRUE; else if (stream->linebuf[stream->preview] != '\0') stream->just_did_nl = FALSE; } /* * If the osifc-level attributes don't match the current attributes, * bring the osifc layer up to date. This is necessary in cases where * we set attributes immediately before asking for input - we * essentially need to flush the attributes without flushing any text. */ if (stream->cur_attr != stream->os_attr) { /* set the osifc attributes */ os_set_text_attr(stream->cur_attr); /* remember the new attributes as the current osifc attributes */ stream->os_attr = stream->cur_attr; } } /* ------------------------------------------------------------------------ */ /* * Determine if we're showing output. Returns true if output should be * displayed, false if it should be suppressed. We'll note the output * for hidden display accounting as needed. */ static int out_is_hidden() { /* check the output flag */ if (!outflag) { /* trace the hidden output if desired */ if (dbghid && !hidout) trchid(); /* note the hidden output */ hidout = 1; /* * unless we're showing hidden text in the debugger, we're * suppressing output, so return true */ if (!dbghid) return TRUE; } /* we're not suppressing output */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Display a blank line to the given stream */ static void outblank_stream(out_stream_info *stream) { /* flush the stream */ outflushn_stream(stream, 1); /* if generating for an HTML display target, add an HTML line break */ if (stream->html_mode && stream->html_target) outstring_stream(stream, "
"); /* write out the newline */ t_outline(stream, 1, "\n", 0); } /* ------------------------------------------------------------------------ */ /* * Generate a tab for a "\t" sequence in the game text. * * Standard (non-HTML) version: we'll generate enough spaces to take us * to the next tab stop. * * HTML version: if we're in native HTML mode, we'll just generate a * ; if we're not in HTML mode, we'll generate a hard * tab character, which the HTML formatter will interpret as a . */ static void outtab_stream(out_stream_info *stream) { /* check to see what the underlying system is expecting */ if (stream->html_target) { /* the underlying system is HTML - check for HTML mode */ if (stream->html_mode) { /* we're in HTML mode, so use the HTML tag */ outstring_stream(stream, ""); } else { /* we're not in HTML mode, so generate a hard tab character */ outchar_noxlat_stream(stream, QTAB); } } else { int maxcol; /* * We're not in HTML mode - expand the tab with spaces. Figure * the maximum column: if we're doing our own line wrapping, never * go beyond the actual display width. */ maxcol = (stream->os_line_wrap ? OS_MAXWIDTH : G_os_linewidth); /* add the spaces */ do { stream->attrbuf[stream->linepos] = stream->cur_attr; stream->linebuf[stream->linepos++] = ' '; ++(stream->linecol); } while (((stream->linecol + 1) & 3) != 0 && stream->linecol < maxcol); } } /* ------------------------------------------------------------------------ */ /* * Flush a line */ static void out_flushline(out_stream_info *stream, int padding) { /* * check to see if we're using the underlying display layer's line * wrapping */ if (stream->os_line_wrap) { /* * In the HTML version, we don't need the normal *MORE* * processing, since the HTML layer will handle that. * Furthermore, we don't need to provide actual newline breaks * -- that happens after the HTML is parsed, so we don't have * enough information here to figure out actual line breaks. * So, we'll just flush out our buffer whenever it fills up, and * suppress newlines. * * Similarly, if we have OS-level MORE processing, don't try to * figure out where the line breaks go -- just flush our buffer * without a trailing newline whenever the buffer is full, and * let the OS layer worry about formatting lines and paragraphs. * * If we're using padding, use mode 2. If we don't want padding * (which is the case if we completely fill up the buffer * without finding any word breaks), write out in mode 0, which * just flushes the buffer exactly like it is. */ outflushn_stream(stream, padding ? 2 : 4); } else { /* * Normal mode - we process the *MORE* prompt ourselves, and we * are responsible for figuring out where the actual line breaks * go. Use outflush() to generate an actual newline whenever we * flush out our buffer. */ outflushn_stream(stream, 1); } } /* ------------------------------------------------------------------------ */ /* * Write a character to an output stream without translation */ static void outchar_noxlat_stream(out_stream_info *stream, char c) { int i; int qspace; /* check for the special quoted space character */ if (c == QSPACE) { /* it's a quoted space - note it and convert it to a regular space */ qspace = 1; c = ' '; } else if (c == QTAB) { /* it's a hard tab - convert it to an ordinary tab */ c = '\t'; qspace = 0; } else { /* translate any whitespace character to a regular space character */ if (outissp(c)) c = ' '; /* it's not a quoted space */ qspace = 0; } /* check for the caps/nocaps flags */ if ((stream->capsflag || stream->allcapsflag) && outisal(c)) { /* capsflag is set, so capitalize this character */ if (outislo(c)) c = toupper(c); /* okay, we've capitalized something; clear flag */ stream->capsflag = 0; } else if (stream->nocapsflag && outisal(c)) { /* nocapsflag is set, so minisculize this character */ if (outisup(c)) c = tolower(c); /* clear the flag now that we've done the job */ stream->nocapsflag = 0; } /* if in capture mode, simply capture the character */ if (stream->capturing) { uchar *p; /* if we have a valid capture object, copy to it */ if (stream->capture_obj != MCMONINV) { /* lock the object holding the captured text */ p = mcmlck(stream->capture_ctx, stream->capture_obj); /* make sure the capture object is big enough */ if (mcmobjsiz(stream->capture_ctx, stream->capture_obj) <= stream->capture_ofs) { /* expand the object by another 256 bytes */ p = mcmrealo(stream->capture_ctx, stream->capture_obj, (ushort)(stream->capture_ofs + 256)); } /* add this character */ *(p + stream->capture_ofs++) = c; /* unlock the capture object */ mcmtch(stream->capture_ctx, stream->capture_obj); mcmunlck(stream->capture_ctx, stream->capture_obj); } /* * we're done - we don't want to actually display the character * while capturing */ return; } /* add the character to out output buffer, flushing as needed */ if (stream->linecol + 1 < G_os_linewidth) { /* * there's room for this character, so add it to the buffer */ /* ignore non-quoted space at start of line outside of PRE */ if (outissp(c) && c != '\t' && stream->linecol == 0 && !qspace && stream->html_pre_level == 0) return; /* is this a non-quoted space not at the start of the line? */ if (outissp(c) && c != '\t' && stream->linecol != 0 && !qspace && stream->html_pre_level == 0) { int pos1 = stream->linepos - 1; char p = stream->linebuf[pos1]; /* check previous character */ /* ignore repeated spaces - collapse into a single space */ if (outissp(p)) return; /* * Certain punctuation requires a double space: a period, a * question mark, an exclamation mark, or a colon; or any of * these characters followed by any number of single and/or * double quotes. First, scan back to before any quotes, if * are on one now, then check the preceding character; if * it's one of the punctuation marks requiring a double * space, add this space a second time. (In addition to * scanning back past quotes, scan past parentheses, * brackets, and braces.) Don't double the spacing if we're * not in the normal doublespace mode; some people may * prefer single spacing after punctuation, so we make this * a run-time option. */ if (doublespace) { /* find the previous relevant punctuation character */ while (pos1 && (p == '"' || p == '\'' || p == ')' || p == ']' || p == '}')) { p = stream->linebuf[--pos1]; } if ( p == '.' || p == '?' || p == '!' || p == ':' ) { /* a double-space is required after this character */ stream->attrbuf[stream->linepos] = stream->cur_attr; stream->linebuf[stream->linepos++] = c; ++(stream->linecol); } } } /* add this character to the buffer */ stream->attrbuf[stream->linepos] = stream->cur_attr; stream->linebuf[stream->linepos++] = c; /* advance the output column position */ ++(stream->linecol); return; } /* * The line would overflow if this character were added. Find the * most recent word break, and output the line up to the previous * word. Note that if we're trying to output a space, we'll just * add it to the line buffer. If the last character of the line * buffer is already a space, we won't do anything right now. */ if (outissp(c) && c != '\t' && !qspace) { /* this is a space, so we're at a word break */ if (stream->linebuf[stream->linepos - 1] != ' ') { stream->attrbuf[stream->linepos] = stream->cur_attr; stream->linebuf[stream->linepos++] = ' '; } return; } /* * Find the most recent word break: look for a space or dash, starting * at the end of the line. * * If we're about to write a hyphen, we want to skip all contiguous * hyphens, because we want to keep them together as a single * punctuation mark; then keep going in the normal manner, which will * keep the hyphens plus the word they're attached to together as a * single unit. If spaces precede the sequence of hyphens, include * the prior word as well. */ i = stream->linepos - 1; if (c == '-') { /* skip any contiguous hyphens at the end of the line */ for ( ; i >= 0 && stream->linebuf[i] == '-' ; --i) ; /* skip any spaces preceding the sequence of hyphens */ for ( ; i >= 0 && outissp(stream->linebuf[i]) ; --i) ; } /* * Now find the preceding space. If we're doing our own wrapping * (i.e., we're not using OS line wrapping), then look for the * nearest hyphen as well. */ for ( ; i >= 0 && !outissp(stream->linebuf[i]) && !(!stream->os_line_wrap && stream->linebuf[i] == '-') ; --i) ; /* check to see if we found a good place to break */ if (i < 0) { /* * we didn't find any good place to break - flush the entire * line as-is, breaking arbitrarily in the middle of a word */ out_flushline(stream, FALSE); /* * we've completely cleared out the line buffer, so reset all of * the line buffer counters */ stream->linepos = 0; stream->linecol = 0; stream->linebuf[0] = '\0'; } else { char brkchar; char tmpbuf[MAXWIDTH]; int tmpattr[MAXWIDTH]; size_t tmpcnt; /* remember the word-break character */ brkchar = stream->linebuf[i]; /* null-terminate the line buffer */ stream->linebuf[stream->linepos] = '\0'; /* the next line starts after the break - save a copy */ tmpcnt = strlen(&stream->linebuf[i+1]); memcpy(tmpbuf, &stream->linebuf[i+1], tmpcnt + 1); memcpy(tmpattr, &stream->attrbuf[i+1], tmpcnt * sizeof(tmpattr[0])); /* * terminate the buffer at the space or after the hyphen, * depending on where we broke */ if (outissp(brkchar)) stream->linebuf[i] = '\0'; else stream->linebuf[i+1] = '\0'; /* write out everything up to the word break */ out_flushline(stream, TRUE); /* copy the next line into line buffer */ memcpy(stream->linebuf, tmpbuf, tmpcnt + 1); memcpy(stream->attrbuf, tmpattr, tmpcnt * sizeof(tmpattr[0])); stream->linepos = tmpcnt; /* * figure what column we're now in - count all of the printable * characters in the new line */ for (stream->linecol = 0, i = 0 ; i < stream->linepos ; ++i) { /* if it's printable, count it */ if (((unsigned char)stream->linebuf[i]) >= 26) ++(stream->linecol); } } /* add the new character to buffer */ stream->attrbuf[stream->linepos] = stream->cur_attr; stream->linebuf[stream->linepos++] = c; /* advance the column counter */ ++(stream->linecol); } /* ------------------------------------------------------------------------ */ /* * Write out a character, translating to the local system character set * from the game's internal character set. */ static void outchar_stream(out_stream_info *stream, char c) { outchar_noxlat_stream(stream, cmap_i2n(c)); } /* * write out a string, translating to the local system character set */ static void outstring_stream(out_stream_info *stream, char *s) { /* write out each character in the string */ for ( ; *s ; ++s) outchar_stream(stream, *s); } /* * write out a string without translation */ static void outstring_noxlat_stream(out_stream_info *stream, char *s) { for ( ; *s ; ++s) outchar_noxlat_stream(stream, *s); } /* ------------------------------------------------------------------------ */ /* * Write out an HTML character value, translating to the local character * set. */ static void outchar_html_stream(out_stream_info *stream, unsigned int htmlchar) { struct amp_tbl_t *ampptr; /* * search for a mapping entry for this entity, in case it's defined * in an external mapping file */ for (ampptr = amp_tbl ; ampptr < amp_tbl + sizeof(amp_tbl)/sizeof(amp_tbl[0]) ; ++ampptr) { /* if this is the one, stop looking */ if (ampptr->html_cval == htmlchar) break; } /* * If we found a mapping table entry, and the entry has an expansion * from the external character mapping table file, use the external * expansion; otherwise, use the default expansion. */ if (ampptr >= amp_tbl + sizeof(amp_tbl)/sizeof(amp_tbl[0]) || ampptr->expan == 0) { char xlat_buf[50]; /* * there's no external mapping table file expansion -- use the * default OS mapping routine */ os_xlat_html4(htmlchar, xlat_buf, sizeof(xlat_buf)); outstring_noxlat_stream(stream, xlat_buf); } else { /* * use the explicit mapping from the mapping table file */ outstring_noxlat_stream(stream, ampptr->expan); } } /* ------------------------------------------------------------------------ */ /* * Enter a recursion level. Returns TRUE if the caller should proceed * with the operation, FALSE if not. * * If we're making a recursive call, thereby re-entering the formatter, * and this stream is not the same as the enclosing stream, we want to * ignore this call and suppress any output to this stream, so we'll * return FALSE. */ static int out_push_stream(out_stream_info *stream) { /* * if we're already in the formatter, and the new stream doesn't * match the enclosing recursion level's stream, tell the caller to * abort the operation */ if (G_recurse != 0 && G_cur_stream != stream) return FALSE; /* note the active stream */ G_cur_stream = stream; /* count the entry */ ++G_recurse; /* tell the caller to proceed */ return TRUE; } /* * Leave a recursion level */ static void out_pop_stream() { /* count the exit */ --G_recurse; } /* ------------------------------------------------------------------------ */ /* * nextout() returns the next character in a string, and updates the * string pointer and remaining length. Returns zero if no more * characters are available in the string. */ /* static char nextout(char **s, uint *len); */ #define nextout(s, len) ((char)(*(len) == 0 ? 0 : (--(*(len)), *((*(s))++)))) /* ------------------------------------------------------------------------ */ /* * Get the next character, writing the previous character to the given * output stream if it's not null. */ static char nextout_copy(char **s, size_t *slen, char prv, out_stream_info *stream) { /* if there's a stream, write the previous character to the stream */ if (stream != 0) outchar_stream(stream, prv); /* return the next character */ return nextout(s, slen); } /* ------------------------------------------------------------------------ */ /* * Read an HTML tag, for our primitive mini-parser. If 'stream' is not * null, we'll copy each character we read to the output stream. Returns * the next character after the tag name. */ static char read_tag(char *dst, size_t dstlen, int *is_end_tag, char **s, size_t *slen, out_stream_info *stream) { char c; /* skip the opening '<' */ c = nextout_copy(s, slen, '<', stream); /* skip spaces */ while (outissp(c)) c = nextout_copy(s, slen, c, stream); /* note if this is a closing tag */ if (c == '/' || c == '\\') { /* it's an end tag - note it and skip the slash */ *is_end_tag = TRUE; c = nextout_copy(s, slen, c, stream); /* skip yet more spaces */ while (outissp(c)) c = nextout_copy(s, slen, c, stream); } else *is_end_tag = FALSE; /* * find the end of the tag name - the tag continues to the next space, * '>', or end of line */ for ( ; c != '\0' && !outissp(c) && c != '>' ; c = nextout_copy(s, slen, c, stream)) { /* add this to the tag buffer if it fits */ if (dstlen > 1) { *dst++ = c; --dstlen; } } /* null-terminate the tag name */ if (dstlen > 0) *dst = '\0'; /* return the next character */ return c; } /* ------------------------------------------------------------------------ */ /* * display a string of a given length to a given stream */ static int outformatlen_stream(out_stream_info *stream, char *s, size_t slen) { char c; int done = 0; char fmsbuf[40]; /* space for constructing translation string */ uint fmslen; char *f = 0; char *f1; int infmt = 0; /* * This routine can recurse because of format strings ("%xxx%" * sequences). When we recurse, we want to ensure that the * recursion is directed to the original stream only. So, note the * current stream statically in case we re-enter the formatter. */ if (!out_push_stream(stream)) return 0; /* get the first character */ c = nextout(&s, &slen); /* if we have anything to show, show it */ while (c != '\0') { /* check if we're collecting translation string */ if (infmt) { /* * if the string is too long for our buffer, or we've come * across a backslash (illegal in a format string), or we've * come across an HTML-significant character ('&' or '<') in * HTML mode, we must have a stray percent sign; dump the * whole string so far and act as though we have no format * string */ if (c == '\\' || f == &fmsbuf[sizeof(fmsbuf)] || (stream->html_mode && (c == '<' || c == '&'))) { outchar_stream(stream, '%'); for (f1 = fmsbuf ; f1 < f ; ++f1) outchar_stream(stream, *f1); infmt = 0; /* process this character again */ continue; } else if (c == '%' && f == fmsbuf) /* double percent sign? */ { outchar_stream(stream, '%'); /* send out a single '%' */ infmt = 0; /* no longer processing translation string */ } else if (c == '%') /* found end of string? translate it if so */ { uchar *fms; int initcap = FALSE; int allcaps = FALSE; char fmsbuf_srch[sizeof(fmsbuf)]; /* null-terminate the string */ *f = '\0'; /* check for an init cap */ if (outisup(fmsbuf[0])) { /* * note the initial capital, so that we follow the * original capitalization in the substituted string */ initcap = TRUE; /* * if the second letter is capitalized as well, * capitalize the entire substituted string */ if (fmsbuf[1] != '\0' && outisup(fmsbuf[1])) { /* use all caps */ allcaps = TRUE; } } /* convert the entire string to lower case for searching */ strcpy(fmsbuf_srch, fmsbuf); os_strlwr(fmsbuf_srch); /* find the string in the format string table */ fmslen = strlen(fmsbuf_srch); for (fms = fmsbase ; fms < fmstop ; ) { uint propnum; uint len; /* get the information on this entry */ propnum = osrp2(fms); len = osrp2(fms + 2) - 2; /* check for a match */ if (len == fmslen && !memcmp(fms + 4, fmsbuf_srch, (size_t)len)) { int old_all_caps; /* note the current ALLCAPS mode */ old_all_caps = stream->allcapsflag; /* * we have a match - set the appropriate * capitalization mode */ if (allcaps) outallcaps_stream(stream, TRUE); else if (initcap) outcaps_stream(stream); /* * evaluate the associated property to generate * the substitution text */ runppr(runctx, cmdActor, (prpnum)propnum, 0); /* turn off ALLCAPS mode */ outallcaps_stream(stream, old_all_caps); /* no need to look any further */ break; } /* move on to next formatstring if not yet found */ fms += len + 4; } /* if we can't find it, dump the format string as-is */ if (fms == fmstop) { outchar_stream(stream, '%'); for (f1 = fmsbuf ; f1 < f ; ++f1) outchar_stream(stream, *f1); outchar_stream(stream, '%'); } /* no longer reading format string */ infmt = 0; } else { /* copy this character of the format string */ *f++ = c; } /* move on to the next character and continue scanning */ c = nextout(&s, &slen); continue; } /* * If we're parsing HTML here, and we're inside a tag, skip * characters until we reach the end of the tag. */ if (stream->html_mode_flag != HTML_MODE_NORMAL) { switch(stream->html_mode_flag) { case HTML_MODE_TAG: /* * keep skipping up to the closing '>', but note when we * enter any quoted section */ switch(c) { case '>': /* we've reached the end of the tag */ stream->html_mode_flag = HTML_MODE_NORMAL; /* if we have a deferred
, process it now */ switch(stream->html_defer_br) { case HTML_DEFER_BR_NONE: /* no deferred
*/ break; case HTML_DEFER_BR_FLUSH: outflushn_stream(stream, 1); break; case HTML_DEFER_BR_BLANK: outblank_stream(stream); break; } /* no more deferred
pending */ stream->html_defer_br = HTML_DEFER_BR_NONE; /* no more ALT attribute allowed */ stream->html_allow_alt = FALSE; break; case '"': /* enter a double-quoted string */ stream->html_mode_flag = HTML_MODE_DQUOTE; break; case '\'': /* enter a single-quoted string */ stream->html_mode_flag = HTML_MODE_SQUOTE; break; default: /* if it's alphabetic, note the attribute name */ if (outisal(c)) { char attrname[128]; char attrval[256]; char *dst; /* gather up the attribute name */ for (dst = attrname ; dst + 1 < attrname + sizeof(attrname) ; ) { /* store this character */ *dst++ = c; /* get the next character */ c = nextout(&s, &slen); /* if it's not alphanumeric, stop scanning */ if (!outisal(c) && !outisdg(c)) break; } /* null-terminate the result */ *dst++ = '\0'; /* gather the value if present */ if (c == '=') { char qu; /* skip the '=' */ c = nextout(&s, &slen); /* if we have a quote, so note */ if (c == '"' || c == '\'') { /* remember the quote */ qu = c; /* skip it */ c = nextout(&s, &slen); } else { /* no quote */ qu = 0; } /* read the value */ for (dst = attrval ; dst + 1 < attrval + sizeof(attrval) ; ) { /* store this character */ *dst++ = c; /* read the next one */ c = nextout(&s, &slen); if (c == '\0') { /* * we've reached the end of the * string, and we're still inside * this attribute - abandon the * attribute but note that we're * inside a quoted string if * necessary */ if (qu == '"') stream->html_mode_flag = HTML_MODE_DQUOTE; else if (qu == '\'') stream->html_mode_flag = HTML_MODE_SQUOTE; else stream->html_mode_flag = HTML_MODE_TAG; /* stop scanning the string */ break; } /* * if we're looking for a quote, check * for the closing quote; otherwise, * check for alphanumerics */ if (qu != 0) { /* if this is our quote, stop scanning */ if (c == qu) break; } else { /* if it's non-alphanumeric, we're done */ if (!outisal(c) && !outisdg(c)) break; } } /* skip the closing quote, if necessary */ if (qu != 0 && c == qu) c = nextout(&s, &slen); /* null-terminate the value string */ *dst = '\0'; } else { /* no value */ attrval[0] = '\0'; } /* * see if we recognize it, and it's meaningful * in the context of the current tag */ if (!stricmp(attrname, "height") && stream->html_defer_br != HTML_DEFER_BR_NONE) { int ht; /* * If the height is zero, always treat this * as a non-blanking flush. If it's one, * treat it as we originally planned to. If * it's greater than one, add n blank lines. */ ht = atoi(attrval); if (ht == 0) { /* always use non-blanking flush */ stream->html_defer_br = HTML_DEFER_BR_FLUSH; } else if (ht == 1) { /* keep original setting */ } else { for ( ; ht > 0 ; --ht) outblank_stream(stream); } } else if (!stricmp(attrname, "alt") && !stream->html_in_ignore && stream->html_allow_alt) { /* write out the ALT string */ outstring_stream(stream, attrval); } /* * since we already read the next character, * simply loop back immediately */ continue; } break; } break; case HTML_MODE_DQUOTE: /* if we've reached the closing quote, return to tag state */ if (c == '"') stream->html_mode_flag = HTML_MODE_TAG; break; case HTML_MODE_SQUOTE: /* if we've reached the closing quote, return to tag state */ if (c == '\'') stream->html_mode_flag = HTML_MODE_TAG; break; } /* * move on to the next character, and start over with the * new character */ c = nextout(&s, &slen); continue; } /* * If we're in a title, and this isn't the start of a new tag, * skip the character - we suppress all regular text output * inside a ... sequence. */ if (stream->html_in_ignore && c != '<') { /* check for entities */ char cbuf[50]; if (c == '&') { /* translate the entity */ c = out_parse_entity(cbuf, sizeof(cbuf), &s, &slen); } else { /* it's an ordinary character - copy it out literally */ cbuf[0] = c; cbuf[1] = '\0'; /* get the next character */ c = nextout(&s, &slen); } /* * if we're gathering a title, and there's room in the title * buffer for more (always leaving room for a null * terminator), add this to the title buffer */ if (stream->html_in_title) { char *cbp; for (cbp = cbuf ; *cbp != '\0' ; ++cbp) { /* if there's room, add it */ if (stream->html_title_ptr + 1 < stream->html_title_buf + sizeof(stream->html_title_buf)) *stream->html_title_ptr++ = *cbp; } } /* don't display anything in an ignore section */ continue; } if ( c == '%' ) /* translation string? */ { infmt = 1; f = fmsbuf; } else if ( c == '\\' ) /* special escape code? */ { c = nextout(&s, &slen); if (stream->capturing && c != '^' && c != 'v' && c != '\0') { outchar_stream(stream, '\\'); outchar_stream(stream, c); /* keep the \- and also put out the next two chars */ if (c == '-') { outchar_stream(stream, nextout(&s, &slen)); outchar_stream(stream, nextout(&s, &slen)); } } else { switch(c) { case 'H': /* HTML mode entry */ /* turn on HTML mode in the renderer */ switch(c = nextout(&s, &slen)) { case '-': /* if we have an HTML target, notify it */ if (stream->html_target) { /* flush its stream */ outflushn_stream(stream, 0); /* tell the OS layer to switch to normal mode */ out_end_html(stream); } /* switch to normal mode */ stream->html_mode = FALSE; break; case '+': default: /* if we have an HTML target, notify it */ if (stream->html_target) { /* flush the underlying stream */ outflushn_stream(stream, 0); /* tell the OS layer to switch to HTML mode */ out_start_html(stream); } /* switch to HTML mode */ stream->html_mode = TRUE; /* * if the character wasn't a "+", it's not part * of the "\H" sequence, so display it normally */ if (c != '+' && c != 0) outchar_stream(stream, c); break; } /* this sequence doesn't result in any actual output */ break; case 'n': /* newline? */ outflushn_stream(stream, 1); /* yes, output line */ break; case 't': /* tab? */ outtab_stream(stream); break; case 'b': /* blank line? */ outblank_stream(stream); break; case '\0': /* line ends here? */ done = 1; break; case ' ': /* quoted space */ if (stream->html_target && stream->html_mode) { /* * we're generating for an HTML target and we're * in HTML mode - generate the HTML non-breaking * space */ outstring_stream(stream, " "); } else { /* * we're not in HTML mode - generate our * internal quoted space character */ outchar_stream(stream, QSPACE); } break; case '^': /* capitalize next character */ stream->capsflag = 1; stream->nocapsflag = 0; break; case 'v': stream->nocapsflag = 1; stream->capsflag = 0; break; case '(': /* generate HTML if in the appropriate mode */ if (stream->html_mode && stream->html_target) { /* send HTML to the renderer */ outstring_stream(stream, ""); } else { /* turn on the 'hilite' attribute */ stream->cur_attr |= OS_ATTR_HILITE; } break; case ')': /* generate HTML if in the appropriate mode */ if (stream->html_mode && stream->html_target) { /* send HTML to the renderer */ outstring_stream(stream, ""); } else { /* turn off the 'hilite' attribute */ stream->cur_attr &= ~OS_ATTR_HILITE; } break; case '-': outchar_stream(stream, nextout(&s, &slen)); outchar_stream(stream, nextout(&s, &slen)); break; default: /* just pass invalid escapes as-is */ outchar_stream(stream, c); break; } } } else if (!stream->html_target && stream->html_mode && (c == '<' || c == '&')) { /* * We're in HTML mode, but the underlying target does not * accept HTML sequences. It appears we're at the start of * an "&" entity or a tag sequence, so parse it, remove it, * and replace it (if possible) with a text-only equivalent. */ if (c == '<') { /* read the tag */ char tagbuf[50]; int is_end_tag; c = read_tag(tagbuf, sizeof(tagbuf), &is_end_tag, &s, &slen, 0); /* * Check to see if we recognize the tag. We only * recognize a few simple tags that map easily to * character mode. */ if (!stricmp(tagbuf, "br")) { /* * line break - if there's anything buffered up, * just flush the current line, otherwise write out * a blank line */ if (stream->html_in_ignore) /* suppress breaks in ignore mode */; else if (stream->linepos != 0) stream->html_defer_br = HTML_DEFER_BR_FLUSH; else stream->html_defer_br = HTML_DEFER_BR_BLANK; } else if (!stricmp(tagbuf, "b") || !stricmp(tagbuf, "i") || !stricmp(tagbuf, "em") || !stricmp(tagbuf, "strong")) { int attr; /* choose the attribute flag */ switch (tagbuf[0]) { case 'b': case 'B': attr = OS_ATTR_BOLD; break; case 'i': case 'I': attr = OS_ATTR_ITALIC; break; case 'e': case 'E': attr = OS_ATTR_EM; break; case 's': case 'S': attr = OS_ATTR_STRONG; break; } /* bold on/off - send out appropriate os-layer code */ if (stream->html_in_ignore) { /* suppress any change in 'ignore' mode */ } else if (!is_end_tag) { /* turn on the selected attribute */ stream->cur_attr |= attr; } else { /* turn off the selected attribute */ stream->cur_attr &= ~attr; } } else if (!stricmp(tagbuf, "p")) { /* paragraph - send out a blank line */ if (!stream->html_in_ignore) outblank_stream(stream); } else if (!stricmp(tagbuf, "tab")) { /* tab - send out a \t */ if (!stream->html_in_ignore) outtab_stream(stream); } else if (!stricmp(tagbuf, "img") || !stricmp(tagbuf, "sound")) { /* IMG and SOUND - allow ALT attributes */ stream->html_allow_alt = TRUE; } else if (!stricmp(tagbuf, "hr")) { int rem; if (!stream->html_in_ignore) { /* start a new line */ outflushn_stream(stream, 1); /* write out underscores to the display width */ for (rem = G_os_linewidth - 1 ; rem > 0 ; ) { char dashbuf[100]; int cur; /* do as much as we can on this pass */ cur = rem; if ((size_t)cur > sizeof(dashbuf) - 1) cur = sizeof(dashbuf) - 1; /* do a buffer-full of dashes */ memset(dashbuf, '_', cur); dashbuf[cur] = '\0'; outstring_stream(stream, dashbuf); /* deduct this from the total */ rem -= cur; } /* put a blank line after the underscores */ outblank_stream(stream); } } else if (!stricmp(tagbuf, "q")) { unsigned int htmlchar; if (!stream->html_in_ignore) { /* if it's an open quote, increment the level */ if (!is_end_tag) ++(stream->html_quote_level); /* add the open quote */ htmlchar = (!is_end_tag ? ((stream->html_quote_level & 1) == 1 ? 8220 : 8216) : ((stream->html_quote_level & 1) == 1 ? 8221 : 8217)); /* * write out the HTML character, translated to * the local character set */ outchar_html_stream(stream, htmlchar); /* if it's a close quote, decrement the level */ if (is_end_tag) --(stream->html_quote_level); } } else if (!stricmp(tagbuf, "title")) { /* * Turn ignore mode on or off as appropriate, and * turn on or off title mode as well. */ if (is_end_tag) { /* * note that we're leaving an ignore section and * a title section */ --(stream->html_in_ignore); --(stream->html_in_title); /* * if we're no longer in a title, call the OS * layer to tell it the title string, in case it * wants to change the window title or otherwise * make use of the title */ if (stream->html_in_title == 0) { /* null-terminate the title string */ *stream->html_title_ptr = '\0'; /* tell the OS about the title */ os_set_title(stream->html_title_buf); } } else { /* * if we aren't already in a title, set up to * capture the title into the title buffer */ if (!stream->html_in_title) stream->html_title_ptr = stream->html_title_buf; /* * note that we're in a title and in an ignore * section, since nothing within gets displayed */ ++(stream->html_in_ignore); ++(stream->html_in_title); } } else if (!stricmp(tagbuf, "aboutbox")) { /* turn ignore mode on or off as appropriate */ if (is_end_tag) --(stream->html_in_ignore); else ++(stream->html_in_ignore); } else if (!stricmp(tagbuf, "pre")) { /* count the nesting level if starting PRE mode */ if (!is_end_tag) stream->html_pre_level += 1; /* surround the PRE block with line breaks */ outblank_stream(stream); /* count the nesting level if ending PRE mode */ if (is_end_tag && stream->html_pre_level != 0) stream->html_pre_level -= 1; } /* suppress everything up to the next '>' */ stream->html_mode_flag = HTML_MODE_TAG; /* * continue with the current character; since we're in * html tag mode, we'll skip everything until we get to * the closing '>' */ continue; } else if (c == '&') { /* parse it */ char xlat_buf[50]; c = out_parse_entity(xlat_buf, sizeof(xlat_buf), &s, &slen); /* write it out (we've already translated it) */ outstring_noxlat_stream(stream, xlat_buf); /* proceed with the next character */ continue; } } else if (stream->html_target && stream->html_mode && c == '<') { /* * We're in HTML mode, and we have an underlying HTML target. * We don't need to do much HTML interpretation at this level. * However, we do need to keep track of when we're in a PRE * block, so that we can pass whitespaces and newlines through * to the underlying HTML engine without filtering when we're * in preformatted text. */ char tagbuf[50]; int is_end_tag; c = read_tag(tagbuf, sizeof(tagbuf), &is_end_tag, &s, &slen, stream); /* check for special tags */ if (!stricmp(tagbuf, "pre")) { /* count the nesting level */ if (!is_end_tag) stream->html_pre_level += 1; else if (is_end_tag && stream->html_pre_level != 0) stream->html_pre_level -= 1; } /* copy the last character after the tag to the stream */ if (c != 0) outchar_stream(stream, c); } else { /* normal character */ outchar_stream(stream, c); } /* move on to the next character, unless we're finished */ if (done) c = '\0'; else c = nextout(&s, &slen); } /* if we ended up inside what looked like a format string, dump string */ if (infmt) { outchar_stream(stream, '%'); for (f1 = fmsbuf ; f1 < f ; ++f1) outchar_stream(stream, *f1); } /* exit a recursion level */ out_pop_stream(); /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Parse an HTML entity markup */ static char out_parse_entity(char *outbuf, size_t outbuf_size, char **sp, size_t *slenp) { char ampbuf[10]; char *dst; char *orig_s; size_t orig_slen; const struct amp_tbl_t *ampptr; size_t lo, hi, cur; char c; /* * remember where the part after the '&' begins, so we can come back * here later if necessary */ orig_s = *sp; orig_slen = *slenp; /* get the character after the ampersand */ c = nextout(sp, slenp); /* if it's numeric, parse the number */ if (c == '#') { uint val; /* skip the '#' */ c = nextout(sp, slenp); /* check for hex */ if (c == 'x' || c == 'X') { /* skip the 'x' */ c = nextout(sp, slenp); /* read the hex number */ for (val = 0 ; isxdigit((uchar)c) ; c = nextout(sp, slenp)) { /* accumulate the current digit into the value */ val *= 16; if (outisdg(c)) val += c - '0'; else if (c >= 'a' && c <= 'f') val += c - 'a' + 10; else val += c - 'A' + 10; } } else { /* read the number */ for (val = 0 ; outisdg(c) ; c = nextout(sp, slenp)) { /* accumulate the current digit into the value */ val *= 10; val += c - '0'; } } /* if we found a ';' at the end, skip it */ if (c == ';') c = nextout(sp, slenp); /* translate the character into the output buffer */ os_xlat_html4(val, outbuf, outbuf_size); /* we're done with this character */ return c; } /* * Parse the sequence after the '&'. Parse up to the closing * semicolon, or any non-alphanumeric, or until we fill up the buffer. */ for (dst = ampbuf ; c != '\0' && (outisdg(c) || outisal(c)) && dst < ampbuf + sizeof(ampbuf) - 1 ; *dst++ = c, c = nextout(sp, slenp)) ; /* null-terminate the name */ *dst = '\0'; /* do a binary search for the name */ lo = 0; hi = sizeof(amp_tbl)/sizeof(amp_tbl[0]) - 1; for (;;) { int diff; /* if we've converged, look no further */ if (lo > hi || lo >= sizeof(amp_tbl)/sizeof(amp_tbl[0])) { ampptr = 0; break; } /* split the difference */ cur = lo + (hi - lo)/2; ampptr = &_tbl[cur]; /* see where we are relative to the target item */ diff = strcmp(ampptr->cname, ampbuf); if (diff == 0) { /* this is it */ break; } else if (diff > 0) { /* make sure we don't go off the end */ if (cur == hi && cur == 0) { /* we've failed to find it */ ampptr = 0; break; } /* this one is too high - check the lower half */ hi = (cur == hi ? hi - 1 : cur); } else { /* this one is too low - check the upper half */ lo = (cur == lo ? lo + 1 : cur); } } /* skip to the appropriate next character */ if (c == ';') { /* name ended with semicolon - skip the semicolon */ c = nextout(sp, slenp); } else if (ampptr != 0) { int skipcnt; /* found the name - skip its exact length */ skipcnt = strlen(ampptr->cname); for (*sp = orig_s, *slenp = orig_slen ; skipcnt != 0 ; c = nextout(sp, slenp), --skipcnt) ; } /* if we found the entry, write out the character */ if (ampptr != 0) { /* * if this one has an external mapping table entry, use the mapping * table entry; otherwise, use the default OS routine mapping */ if (ampptr->expan != 0) { /* * we have an explicit expansion from the mapping table file - * use it */ size_t copylen = strlen(ampptr->expan); if (copylen > outbuf_size - 1) copylen = outbuf_size - 1; memcpy(outbuf, ampptr->expan, copylen); outbuf[copylen] = '\0'; } else { /* * there's no mapping table expansion - use the default OS code * expansion */ os_xlat_html4(ampptr->html_cval, outbuf, outbuf_size); } } else { /* * didn't find it - output the '&' literally, then back up and * output the entire sequence following */ *sp = orig_s; *slenp = orig_slen; c = nextout(sp, slenp); /* fill in the '&' return value */ outbuf[0] = '&'; outbuf[1] = '\0'; } /* return the next character */ return c; } /* ------------------------------------------------------------------------ */ /* * Initialize the output formatter */ void out_init() { /* not yet hiding output */ outflag = 1; outcnt = 0; hidout = 0; /* initialize the standard display stream */ out_init_std(&G_std_disp); /* initialize the log file stream */ out_init_log(&G_log_disp); } /* ------------------------------------------------------------------------ */ /* * initialize the property translation table */ void tiosetfmt(tiocxdef *ctx, runcxdef *rctx, uchar *fbase, uint flen) { VARUSED(ctx); fmsbase = fbase; fmstop = fbase + flen; runctx = rctx; } /* ------------------------------------------------------------------------ */ /* * Map an HTML entity to a local character value. The character table * reader will call this routine during initialization if it finds HTML * entities in the mapping table file. We'll remember these mappings * for use in translating HTML entities to the local character set. * * Note that the standard run-time can only display a single character * set, so every HTML entity that we display must be mapped to the * single active native character set. */ void tio_set_html_expansion(unsigned int html_char_val, const char *expansion, size_t expansion_len) { struct amp_tbl_t *p; /* find the character value */ for (p = amp_tbl ; p < amp_tbl + sizeof(amp_tbl)/sizeof(amp_tbl[0]) ; ++p) { /* if this is the one, store it */ if (p->html_cval == html_char_val) { /* allocate space for it */ p->expan = (char *)osmalloc(expansion_len + 1); /* save it */ memcpy(p->expan, expansion, expansion_len); p->expan[expansion_len] = '\0'; /* no need to look any further */ return; } } } /* ------------------------------------------------------------------------ */ /* * Write out a c-style (null-terminated) string. */ int outformat(char *s) { return outformatlen(s, strlen(s)); } /* ------------------------------------------------------------------------ */ /* * This routine sends out a string, one character at a time (via outchar). * Escape codes ('\n', and so forth) are handled here. */ int outformatlen(char *s, uint slen) { char c; uint orig_slen; char *orig_s; int ret; int called_filter; /* presume we'll return success */ ret = 0; /* presume we won't call the filter function */ called_filter = FALSE; /* if there's a user filter function to invoke, call it */ if (G_user_filter != MCMONINV) { /* push the string */ runpstr(runctx, s, slen, 1); /* call the filter */ runfn(runctx, G_user_filter, 1); /* * note that we called the filter, so that we'll remove the * result of the filter from the stack before we return */ called_filter = TRUE; /* if the result is a string, use it in place of the original text */ if (runtostyp(runctx) == DAT_SSTRING) { runsdef val; uchar *p; /* pop the value */ runpop(runctx, &val); /* * get the text from the string, and use it as a replacement * for the original string */ p = val.runsv.runsvstr; slen = osrp2(p) - 2; s = (char *)(p + 2); /* * push the string back onto the stack - this will ensure * that the string stays referenced while we're working, so * that the garbage collector won't delete it */ runrepush(runctx, &val); } } /* remember the original string, before we scan the first character */ orig_s = s; orig_slen = slen; /* get the first character to display */ c = nextout(&s, &slen); /* if the string is non-empty, note that we've displayed something */ if (c != 0) outcnt = 1; /* check to see if we're hiding output */ if (out_is_hidden()) goto done; /* if the debugger is showing watchpoints, suppress all output */ if (outwxflag) goto done; /* display the string */ ret = outformatlen_stream(&G_std_disp, orig_s, orig_slen); /* if there's a log file, write to the log file as well */ if (logfp != 0) { outformatlen_stream(&G_log_disp, orig_s, orig_slen); osfflush(logfp); } done: /* if we called the filter, remove the result from the stack */ if (called_filter) rundisc(runctx); /* return the result from displaying to the screen */ return ret; } /* ------------------------------------------------------------------------ */ /* * Display a blank line */ void outblank() { /* note that we've displayed something */ outcnt = 1; /* check to see if we're hiding output */ if (out_is_hidden()) return; /* generate the newline to the standard display */ outblank_stream(&G_std_disp); /* if we're logging, generate the newline to the log file as well */ if (logfp != 0) { outblank_stream(&G_log_disp); osfflush(logfp); } } /* ------------------------------------------------------------------------ */ /* * outcaps() - sets an internal flag which makes the next letter output * a capital, whether it came in that way or not. Set the same state in * both formatters (standard and log). */ void outcaps(void) { outcaps_stream(&G_std_disp); outcaps_stream(&G_log_disp); } /* * outnocaps() - sets the next letter to a miniscule, whether it came in * that way or not. */ void outnocaps(void) { outnocaps_stream(&G_std_disp); outnocaps_stream(&G_log_disp); } /* ------------------------------------------------------------------------ */ /* * Open a log file */ int tiologopn(tiocxdef *ctx, char *fn) { /* if there's an old log file, close it */ if (tiologcls(ctx)) return 1; /* save the filename for later */ strcpy(logfname, fn); /* open the new file */ logfp = osfopwt(fn, OSFTLOG); /* * Reset the log file's output formatter state, since we're opening * a new file. */ out_init_log(&G_log_disp); /* * Set the log file's HTML source mode flag to the same value as is * currently being used in the main display stream, so that it will * interpret source markups the same way that the display stream is * going to. */ G_log_disp.html_mode = G_std_disp.html_mode; /* return 0 on success, non-zero on failure */ return (logfp == 0); } /* * Close the log file */ int tiologcls(tiocxdef *ctx) { /* if we have a file, close it */ if (logfp != 0) { /* close the handle */ osfcls(logfp); /* set the system file type to "log file" */ os_settype(logfname, OSFTLOG); /* forget about our log file handle */ logfp = 0; } /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Write text explicitly to the log file. This can be used to add * special text (such as prompt text) that would normally be suppressed * from the log file. When more mode is turned off, we don't * automatically copy text to the log file; any text that the caller * knows should be in the log file during times when more mode is turned * off can be explicitly added with this function. * * If nl is true, we'll add a newline at the end of this text. The * caller should not include any newlines in the text being displayed * here. */ void out_logfile_print(char *txt, int nl) { /* if there's no log file, there's nothing to do */ if (logfp == 0) return; /* add the text */ os_fprintz(logfp, txt); /* add a newline if desired */ if (nl) { /* add a normal newline */ os_fprintz(logfp, "\n"); /* if the logfile is an html target, write an HTML line break */ if (G_log_disp.html_target && G_log_disp.html_mode) os_fprintz(logfp, "
\n"); } /* flush the output */ osfflush(logfp); } /* ------------------------------------------------------------------------ */ /* * Set the current MORE mode */ int setmore(int state) { int oldstate = G_os_moremode; G_os_moremode = state; return oldstate; } /* ------------------------------------------------------------------------ */ /* * Run the MORE prompt. If the output layer takes responsibility for * pagination issues (i.e., USE_MORE is defined), we'll simply display * the prompt and wait for input. Otherwise, the OS layer controls the * MORE prompt, so we'll call the OS-layer function to display the * prompt. */ void out_more_prompt() { #ifdef USE_MORE /* * USE_MORE defined - we take responsibility for pagination. Show * our default MORE prompt and wait for a keystroke. */ int done; int next_page; /* display the "MORE" prompt */ os_printz("[More]"); os_flush(); /* wait for an acceptable keystroke */ for (done = FALSE ; !done ; ) { os_event_info_t evt; /* get an event */ switch(os_get_event(0, FALSE, &evt)) { case OS_EVT_KEY: switch(evt.key[0]) { case ' ': /* stop waiting, show one page */ done = TRUE; next_page = TRUE; break; case '\r': case '\n': /* stop waiting, show one line */ done = TRUE; next_page = FALSE; break; default: /* ignore any other keystrokes */ break; } break; case OS_EVT_EOF: /* end of file - there's nothing to wait for now */ done = TRUE; next_page = TRUE; /* don't use more prompts any more, as the user can't respond */ G_os_moremode = FALSE; break; default: /* ignore other events */ break; } } /* * Remove the prompt from the screen by backing up and overwriting * it with spaces. (Note that this assumes that we're running in * some kind of terminal or character mode with a fixed-pitch font; * if that's not the case, the OS layer should be taking * responsibility for pagination anyway, so this code shouldn't be * in use in the first place.) */ os_printz("\r \r"); /* * if they pressed the space key, it means that we should show an * entire new page, so reset the line count to zero; otherwise, * we'll want to display another MORE prompt at the very next line, * so leave the line count alone */ if (next_page) G_std_disp.linecnt = 0; #else /* USE_MORE */ /* * USE_MORE is undefined - this means that the OS layer is taking * all responsibility for pagination. We must ask the OS layer to * display the MORE prompt, because we can't make any assumptions * about what the prompt looks like. */ os_more_prompt(); #endif /* USE_MORE */ } /* ------------------------------------------------------------------------ */ /* * reset output */ void outreset(void) { G_std_disp.linecnt = 0; } /* ------------------------------------------------------------------------ */ /* * Determine if HTML mode is active. Returns true if so, false if not. * Note that this merely indicates whether an "\H+" sequence is * currently active -- this will return true after an "\H+" sequence, * even on text-only interpreters. */ int tio_is_html_mode() { /* return the current HTML mode flag for the standard display stream */ return G_std_disp.html_mode; } /* ------------------------------------------------------------------------ */ /* * Capture routines. Capture affects only the standard display output * stream; there's no need to capture information redundantly in the log * file stream. */ /* * Begin/end capturing */ void tiocapture(tiocxdef *tioctx, mcmcxdef *memctx, int flag) { if (flag) { /* create a new object if necessary */ if (G_std_disp.capture_obj == MCMONINV) { mcmalo(memctx, 256, &G_std_disp.capture_obj); mcmunlck(memctx, G_std_disp.capture_obj); } /* remember the memory context */ G_std_disp.capture_ctx = memctx; } /* * remember capture status in the standard output stream as well as * the log stream */ G_std_disp.capturing = flag; G_log_disp.capturing = flag; } /* clear all captured output */ void tioclrcapture(tiocxdef *tioctx) { G_std_disp.capture_ofs = 0; } /* clear captured output back to a given size */ void tiopopcapture(tiocxdef *tioctx, uint orig_size) { G_std_disp.capture_ofs = orig_size; } /* get the object handle of the captured output */ mcmon tiogetcapture(tiocxdef *ctx) { return G_std_disp.capture_obj; } /* get the amount of text captured */ uint tiocapturesize(tiocxdef *ctx) { return G_std_disp.capture_ofs; } /* ------------------------------------------------------------------------ */ /* * set the current actor */ void tiosetactor(tiocxdef *ctx, objnum actor) { VARUSED(ctx); cmdActor = actor; } /* * get the current actor */ objnum tiogetactor(tiocxdef *ctx) { VARUSED(ctx); return cmdActor; } /* ------------------------------------------------------------------------ */ /* * Flush the output line. We'll write to both the standard display and * the log file, as needed. */ void outflushn(int nl) { /* flush the display stream */ outflushn_stream(&G_std_disp, nl); /* flush the log stream, if we have an open log file */ if (logfp != 0) { outflushn_stream(&G_log_disp, nl); osfflush(logfp); } } /* * flush the current line, and start a new line */ void outflush(void) { /* use the common flushing routine in mode 1 (regular newline) */ outflushn(1); } /* ------------------------------------------------------------------------ */ /* * Hidden text routines */ /* * outhide - hide output in the standard display stream */ void outhide(void) { outflag = 0; outcnt = 0; hidout = 0; } /* * Check output status. Indicate whether output is currently hidden, * and whether any hidden output has occurred. */ void outstat(int *hidden, int *output_occurred) { *hidden = !outflag; *output_occurred = outcnt; } /* set the flag to indicate that output has occurred */ void outsethidden(void) { outcnt = 1; hidout = 1; } /* * outshow() - turns output back on, and returns TRUE (1) if any output * has occurred since the last outshow(), FALSE (0) otherwise. */ int outshow(void) { /* turn output back on */ outflag = 1; /* if we're debugging, note the end of hidden output */ if (dbghid && hidout) { hidout = 0; trcsho(); } /* return the flag indicating whether hidden output occurred */ return outcnt; } /* ------------------------------------------------------------------------ */ /* * start/end watchpoint evaluation - suppress all dstring output */ void outwx(int flag) { outwxflag = flag; } /* ------------------------------------------------------------------------ */ /* * Set the user filter function. Setting this to MCMONINV clears the * filter. */ void out_set_filter(objnum filter_fn) { /* remember the filter function */ G_user_filter = filter_fn; } /* ------------------------------------------------------------------------ */ /* * Set the double-space mode */ void out_set_doublespace(int dbl) { /* remember the new setting */ doublespace = dbl; } qtads-2.1.7/tads2/ply.c000066400000000000000000000240551265017072300146330ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/PLY.C,v 1.3 1999/05/29 15:51:02 MJRoberts Exp $"; #endif /* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name ply.c - play game Function executes a game, starting with the 'init' function Notes none Modified 04/04/92 MJRoberts - creation */ #include "os.h" #include "std.h" #include "run.h" #include "voc.h" #include "err.h" #include "obj.h" #include "fio.h" #include #include "ply.h" /* * Play the game */ void plygo(runcxdef *run, voccxdef *voc, tiocxdef *tio, objnum preinit, char *restore_fname) { int err; errcxdef *ec = run->runcxerr; char filbuf[128]; int first_time; int noreg inited = FALSE; NOREG((&inited)); first_time = TRUE; /* * Write out the special HTML sequence, in case we're on an HTML * system. This tells the HTML parser to use the parsing rules for * TADS 2 callers. */ outformat("\\H+\\H-"); startover: if (!inited) { /* use Me as the format-string actor for preinit and init */ tiosetactor(voc->voccxtio, voc->voccxme); /* * Run preinit, if it hasn't been run yet. Note that we only * do this the first time through. If we come back here via the * restart function, preinit will already have been run in the * restart function itself, so we don't need to run it again. */ if (first_time) { /* make a note that we've been through here once already */ first_time = FALSE; /* remember the preinit function for later use in restarting */ voc->voccxpreinit = preinit; /* run the preinit() function */ ERRBEGIN(ec) { /* reset the interpreter */ runrst(run); /* reset the parser */ voc_stk_ini(voc, (uint)VOC_STACK_SIZE); /* run preinit */ if (preinit != MCMONINV) runfn(run, preinit, 0); } ERRCATCH(ec, err) { /* if they restarted, go back and start over */ if (err == ERR_RUNRESTART) goto startover; /* resignal the error */ errrse(ec); } ERREND(ec); } /* * Run the "init" function. Do NOT run init if we're restoring * a game directly from the command line AND there's an * initRestore function defined. */ if (restore_fname == 0 || voc->voccxinitrestore == MCMONINV) { ERRBEGIN(ec) { /* reset the interpreter */ runrst(run); /* reset the parser */ voc_stk_ini(voc, (uint)VOC_STACK_SIZE); /* run init */ runfn(run, (objnum)voc->voccxini, 0); } ERRCATCH(ec, err) { /* if they restarted, go back and start over */ if (err == ERR_RUNRESTART) goto startover; /* resignal the error */ errrse(ec); } ERREND(ec); } } /* next time through, we'll need to run init again */ inited = FALSE; /* * check for startup parameter file to restore - if there's a * system-specific parameter file specified, pretend that it was * specified as the restore file */ if (os_paramfile(filbuf)) restore_fname = filbuf; /* check for a file to restore */ if (restore_fname != 0) { /* * Check to see if the game file supports the initRestore * function. If so, call it to restore the game. If not, * restore the game directly. */ if (voc->voccxinitrestore != MCMONINV) { char restore_buf[OSFNMAX*2]; char *src; char *dst; /* convert any backslashes to double backslashes */ for (src = restore_fname, dst = restore_buf ; *src != '\0' && dst + 2 < restore_buf + sizeof(restore_buf) ; ++src) { switch(*src) { case '\\': /* it's a backslash - double it */ *dst++ = '\\'; *dst++ = '\\'; break; default: /* copy the character as-is */ *dst++ = *src; } } /* * all the game's initRestore function with the name of * saved game file to restore as the argument */ /* reset the interpreter */ runrst(run); /* reset the parser */ voc_stk_ini(voc, (uint)VOC_STACK_SIZE); /* push the game file name and run initRestore */ runpstr(run, restore_buf, dst - restore_buf, 0); runfn(run, (objnum)voc->voccxinitrestore, 1); } else { /* restore the game */ os_printz("\n\n[Restoring saved game]\n\n"); err = fiorso(voc, restore_fname); if (err) { char buf[60 + OSFNMAX]; sprintf(buf, "\n\nError: unable to restore file \"%s\"\n\n", restore_fname); os_printz(buf); } } /* forget the saved game name, in case we restore */ restore_fname = 0; } /* clear out the redo command buffer */ voc->voccxredobuf[0] = '\0'; /* read and execute commands */ for (;;) { char buf[128]; err = 0; ERRBEGIN(ec) /* read a new command if there's nothing to redo */ if (!voc->voccxredo) { /* reset hidden output so we're showing output */ tioshow(tio); tioflush(tio); /* clear the interpreter stack */ runrst(run); /* read a command */ vocread(voc, MCMONINV, MCMONINV, buf, (int)sizeof(buf), 0); /* special qa checking */ if (buf[0] == '@') { int quiet = FALSE; char *p; p = buf + 1; if (*p == '@') { /* turn off MORE mode */ setmore(0); /* set NONSTOP mode in the OS layer */ os_nonstop_mode(TRUE); /* skip the extra '@' */ ++p; } else if (*p == '!') { quiet = TRUE; ++p; } while (*p != '\0' && t_isspace(*p)) ++p; if (*p != '\0') { /* open the named file */ qasopn(p, quiet); } else { char fname[256]; /* no file was named - ask the user to select a file */ if (tio_askfile("Read script file:", fname, sizeof(fname), OS_AFP_OPEN, OSFTCMD) == 0) qasopn(fname, quiet); } goto end_loop; } } /* * If there's redo in the redo buffer, use it now. If the * buffer is empty and the redo flag is set, we'll just * re-execute whatever's in our internal buffer. */ if (voc->voccxredo && voc->voccxredobuf[0] != '\0') { /* copy the redo buffer into our internal buffer */ strcpy(buf, voc->voccxredobuf); /* we've consumed it now, so clear it out */ voc->voccxredobuf[0] = '\0'; } /* we've now consumed the redo */ voc->voccxredo = FALSE; /* clear any pending break that's queued up */ (void)os_break(); /* execute the command */ (void)voccmd(voc, buf, (uint)sizeof(buf)); end_loop: ERRCATCH(ec, err) { if (err != ERR_RUNQUIT && err != ERR_RUNRESTART && !(err == ERR_RUNABRT && voc->voccxredo)) errclog(ec); } ERREND(ec); /* on interrupt, undo last command (which was partially executed) */ if (err == ERR_USRINT && voc->voccxundo) { ERRBEGIN(ec) objundo(voc->voccxmem, voc->voccxundo); ERRCATCH(ec, err) if (err != ERR_NOUNDO && err != ERR_ICUNDO) errrse(ec); ERREND(ec) } /* if they want to quit, we're done */ if (err == ERR_RUNQUIT) break; else if (err == ERR_RUNRESTART) goto startover; } /* * If we're quitting, give the debugger one last chance at taking * control. If it just returns, we can go ahead and terminate, but * if it wants it can restart the game by calling bifrst() as * normal. */ ERRBEGIN(ec) { /* clear anything in the debugger stack trace */ run->runcxdbg->dbgcxfcn = 0; run->runcxdbg->dbgcxdep = 0; /* tell the debugger the game has exited */ dbguquitting(run->runcxdbg); } ERRCATCH(ec, err) { switch(err) { case ERR_RUNRESTART: /* they restarted the game - re-enter the play loop */ goto startover; case ERR_RUNQUIT: /* quitting - proceed to return as normal */ break; default: /* resignal any other error */ errrse(ec); } } ERREND(ec); } qtads-2.1.7/tads2/ply.h000066400000000000000000000012001265017072300146230ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/PLY.H,v 1.2 1999/05/17 02:52:13 MJRoberts Exp $ */ /* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name ply.h - definitions for game play loop Function definitions for game play loop Notes none Modified 04/11/92 MJRoberts - creation */ #ifndef PLY_INCLUDED #define PLY_INCLUDED void plygo(struct runcxdef *run, struct voccxdef *voc, struct tiocxdef *tio, objnum preinit, char *restore_fname); #endif /* PLY_INCLUDED */ qtads-2.1.7/tads2/prp.h000066400000000000000000000164761265017072300146450ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/PRP.H,v 1.2 1999/05/17 02:52:13 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name prp.h - property definitions Function Defines the structure of properties Notes A property structure must be binary-portable, because properties are stored in objects, which must be binary-portable. Hence, the internal structure of a property header is not a C structure, but a portable sequence of bytes. Multi-byte quantities are stored in Intel format. property number - 2 bytes property datatype - 1 byte property size - 2 bytes property flags - 1 byte This header is followed immediately by the property value. For convenience, a set of macros is defined to provide access to the fields of a property header. Modified 12/16/92 MJRoberts - TADS/Graphic properties 08/11/91 MJRoberts - creation */ #ifndef PRP_INCLUDED #define PRP_INCLUDED #ifndef DAT_INCLUDED #include "dat.h" #endif /* a property number, used to look up all properties */ typedef ushort prpnum; /* a property definition, used to associate data with properties */ /* typedef struct prpdef prpdef; struct prpdef { prpnum prpprop; /o property number of this property o/ dattyp prptype; /o datatype of the property value o/ ushort prpsize; /o size of the property's data o/ ushort prpflg; /o flags for the property o/ uchar prpval[1]; /o value of the property (if applicable) o/ }; */ typedef uchar prpdef; /* prpdef is just an array of bytes */ #define PRPHDRSIZ 6 /* "sizeof(prpdef)" - size of property header */ /* Macros to provide access to property header entries */ #define prpprop(p) osrp2((uchar *)(p)) #define prptype(p) (*(((uchar *)(p)) + 2)) #define prpsize(p) osrp2((((uchar *)(p)) + 3)) #define prpflg(p) (*(((uchar *)(p)) + 5)) #define prpvalp(p) (((uchar *)(p)) + 6) #define prpsetprop(p,n) oswp2((uchar *)(p), n) #define prpsetsize(p,s) oswp2((((uchar *)(p)) + 3), s) /* property flag values */ #define PRPFORG 0x01 /* property is original startup value */ #define PRPFIGN 0x02 /* ignore this prop (has been changed) */ #define PRPFDEL 0x04 /* property has been permanently deleted */ /* * invalid property number - this number will never be used as an actual * property, so it can be used to signify the lack of a valid property */ #define PRP_INVALID 0 /* certain property types are special, and are reserved here */ #define PRP_DOACTION 1 /* doAction property */ /* vocabulary properties - keep these contiguous, and must start at 2 */ #define PRP_VERB 2 /* verb vocabulary property */ #define PRP_NOUN 3 /* noun vocabulary property */ #define PRP_ADJ 4 /* adjective vocabulary property */ #define PRP_PREP 5 /* preposition vocabulary property */ #define PRP_ARTICLE 6 /* article vocabulary property */ #define PRP_PLURAL 7 /* plural vocabulary property */ /* determine if a property is a vocab property */ /* int prpisvoc(prpnum p); */ #define prpisvoc(p) ((p) >= PRP_VERB && (p) <= PRP_PLURAL) /* more properties... */ #define PRP_SDESC 8 #define PRP_THEDESC 9 #define PRP_DODEFAULT 10 #define PRP_IODEFAULT 11 #define PRP_IOACTION 12 #define PRP_LOCATION 13 #define PRP_VALUE 14 #define PRP_ROOMACTION 15 #define PRP_ACTORACTION 16 #define PRP_CONTENTS 17 #define PRP_TPL 18 /* special built-in TEMPLATE structure */ #define PRP_PREPDEFAULT 19 #define PRP_VERACTOR 20 #define PRP_VALIDDO 21 #define PRP_VALIDIO 22 #define PRP_LOOKAROUND 23 #define PRP_ROOMCHECK 24 #define PRP_STATUSLINE 25 #define PRP_LOCOK 26 #define PRP_ISVIS 27 #define PRP_NOREACH 28 #define PRP_ISHIM 29 #define PRP_ISHER 30 #define PRP_ACTION 31 /* action method */ #define PRP_VALDOLIST 32 /* validDoList */ #define PRP_VALIOLIST 33 /* validIoList */ #define PRP_IOBJGEN 34 /* iobjGen */ #define PRP_DOBJGEN 35 /* dobjGen */ #define PRP_NILPREP 36 /* nilPrep */ #define PRP_REJECTMDO 37 /* rejectMultiDobj */ #define PRP_MOVEINTO 38 /* moveInto */ #define PRP_CONSTRUCT 39 /* construct */ #define PRP_DESTRUCT 40 /* destruct */ #define PRP_VALIDACTOR 41 /* validActor */ #define PRP_PREFACTOR 42 /* preferredActor */ #define PRP_ISEQUIV 43 /* isEquivalent */ #define PRP_ADESC 44 #define PRP_MULTISDESC 45 #define PRP_TPL2 46 /* new-style built-in TEMPLATE structure */ #define PRP_ANYVALUE 47 /* anyvalue(n) - value to use for '#' with ANY */ #define PRP_NEWNUMOBJ 48 /* newnumbered(n) - create new numbered object */ #define PRP_UNKNOWN 49 /* internal property for unknown words */ #define PRP_PARSEUNKNOWNDOBJ 50 /* parseUnknownDobj */ #define PRP_PARSEUNKNOWNIOBJ 51 /* parseUnknownIobj */ #define PRP_DOBJCHECK 52 /* dobjCheck */ #define PRP_IOBJCHECK 53 /* iobjCheck */ #define PRP_VERBACTION 54 /* verbAction */ #define PRP_DISAMBIGDO 55 /* disambigDobj */ #define PRP_DISAMBIGIO 56 /* disambigIobj */ #define PRP_PREFIXDESC 57 /* prefixdesc */ #define PRP_ISTHEM 58 /* isThem */ /* properties used by TADS/Graphic */ #define PRP_GP_PIC 100 /* gp_picture */ #define PRP_GP_NAME 101 /* gp_name */ #define PRP_GP_DEFVERB 102 /* gp_defverb */ #define PRP_GP_ACTIVE 103 /* gp_active */ #define PRP_GP_HOTLIST 104 /* gp_hotlist */ #define PRP_GP_ICON 105 /* gp_icon */ #define PRP_GP_DEFVERB2 106 /* gp_defverb2 */ #define PRP_GP_DEFPREP 107 /* gp_defprep */ #define PRP_GP_HOTID 108 /* gp_hotid */ #define PRP_GP_OVERLAY 109 /* gp_overlay */ #define PRP_GP_HOTX 110 /* gp_hotx */ #define PRP_GP_HOTY 111 /* gp_hoty */ /* highest reserved property number - must match last one above */ #define PRP_LASTRSV 111 #endif /* PRP_INCLUDED */ qtads-2.1.7/tads2/prs.h000066400000000000000000000216511265017072300146370ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/PRS.H,v 1.3 1999/07/11 00:46:30 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name prs.h - parser definitions Function Definitions for the parser Notes None Modified 08/30/91 MJRoberts - creation */ #ifndef PRS_INCLUDED #define PRS_INCLUDED #ifndef ERR_INCLUDED #include "err.h" #endif #ifndef TOK_INCLUDED #include "tok.h" #endif #ifndef PRP_INCLUDED #include "prp.h" #endif #ifndef EMT_INCLUDED #include "emt.h" #endif #ifndef VOC_INCLUDED #include "voc.h" #endif /* expression parse tree node */ typedef struct prsndef prsndef; struct prsndef { int prsntyp; /* type of this node */ int prsnnlf; /* number of leaves on this node */ union { tokdef prsnvt; /* leaf node: token at leaf */ prsndef *prsnvn[1]; /* non-leaf: one or more subnodes */ } prsnv; }; /* * Case table: the case table is a linked list of arrays of case * records, each of which contains a value (in the form of a tokdef) and * a label number. When additional case table entries are needed, * another array is added to the list, providing essentially unlimited * cases in a switch. Note that this is the parser's internal record of * a case table as it's being parsed, and is converted to a different * representation during code generation. */ #define PRSCTSIZE 50 /* number of case entries in one case array */ typedef struct prsctdef prsctdef; struct prsctdef { prsctdef *prsctnxt; /* next array in case table */ struct { tokdef prscttok; /* value of this label */ uint prsctofs; /* code position for this label */ } prsctcase[PRSCTSIZE]; /* cases */ }; /* case table control block */ struct prscsdef { struct prsctdef *prscstab; /* first case table */ uint prscscnt; /* number of cases */ uint prscsdflt; /* offset of 'default' code */ }; typedef struct prscsdef prscsdef; /* parsing context */ struct prscxdef { errcxdef *prscxerr; /* error handling context */ tokcxdef *prscxtok; /* lexical analysis context */ toktdef *prscxstab; /* table to which to add symbols */ toktdef *prscxgtab; /* goto (label) symbol table */ mcmcxdef *prscxmem; /* memory handling context */ emtcxdef *prscxemt; /* emitter context */ voccxdef *prscxvoc; /* vocabulary context */ char *prscxcpp; /* pointer to compound word memory area */ uint prscxcpf; /* offset of next free byte of compound words */ size_t prscxcps; /* size of compound word memory area */ uchar *prscxfsp; /* pointer to format string area */ uint prscxfsf; /* offset of next free byte of format strings */ size_t prscxfss; /* size of format string area */ char *prscxspp; /* pointer to special word area */ uint prscxspf; /* offset of next free byte of special words */ size_t prscxsps; /* size of special word area */ ushort prscxflg; /* parse flags */ # define PRSCXFLIN 0x01 /* debug mode: generate inline line records */ # define PRSCXFLCL 0x02 /* debug mode: generate inline local records */ # define PRSCXFLST 0x04 /* parsing a list element - no indexing */ # define PRSCXFARC 0x08 /* check argument counts in user fns */ # define PRSCXFV1E 0x10 /* v1 'else' compat - ignore ';' after '}' */ # define PRSCXFWTCH 0x20 /* compiling "watch" expression */ # define PRSCXFFUNC 0x40 /* compiling function - no 'self' object */ # define PRSCXFTPL1 0x80 /* use old-style templates */ # define PRSCXFLIN2 0x100 /* generate new-style line records */ uint prscxprp; /* maximum property number allocated so far */ uint prscxext; /* count of external functions so far */ uchar *prscxnode; /* next available node structure */ uint prscxsofs; /* starting offset of current string */ ushort prscxslen; /* length of current string */ ushort prscxnsiz; /* bytes in node pool */ ushort prscxnrem; /* remaining bytes in node pool */ uchar *prscxnrst; /* value for resetting prscxnode */ ushort prscxrrst; /* value for resetting prscxnrem */ uchar *prscxplcl; /* pool for local variables */ ushort prscxslcl; /* size of local pool */ ushort prscxextc; /* count of external functions */ osfildef *prscxstrfile; /* string capture file */ uchar prscxpool[1]; /* pool for node allocation */ }; typedef struct prscxdef prscxdef; /* parse function or object definition */ void prscode(prscxdef *ctx, int markcomp); /* parse a statement (or compound statement) */ void prsstm(prscxdef *ctx, uint brk, uint cont, int parms, int locals, uint entofs, prscsdef *swctl, uint curfr); /* delete 'goto' labels belonging to current code block */ void prsdelgoto(prscxdef *ctx); /* parse an expression and generate code for it */ void prsxgen(prscxdef *ctx); /* * Parse an expression and generate code for it, using speculative * rules: we prohibit assignments, method calls, and function calls (in * short, we prohibit anything that could change game state). This is * meant to support the debugger's speculative evaluation mode (see * dbgcompile() for information). Throws an error if a prohibited * operation is found. */ void prsxgen_spec(prscxdef *ctx); /* * parse and generate an expression, checking for a possibly incorrect * assignment if in C operator mode */ void prsxgen_pia(prscxdef *ctx); /* string accumulation routines */ ushort prsxsst(prscxdef *ctx); void prsxsad(prscxdef *ctx, char *p, ushort len); void prsxsend(prscxdef *ctx); /* determine if a type is valid for logical operators */ #define prsvlog(typ) \ ((typ)==TOKTNUMBER || (typ)==TOKTNIL || (typ)==TOKTTRUE) /* convert a value to a logical type, if it's a number */ #define prs2log(typ, val) \ ((typ)==TOKTNUMBER ? ((val) ? TOKTTRUE : TOKTNIL) : (typ)) /* reset node pool */ /* void prsrstn(prscxdef *ctx); */ #define prsrstn(ctx) \ ((ctx)->prscxnode = (ctx)->prscxnrst, \ (ctx)->prscxnrem = (ctx)->prscxrrst) /* maximum number of superclasses for a single object */ #define PRSMAXSC 64 /* amount of space to allocate for a new object */ #define PRSOBJSIZ 256 /* add a symbol to the symbol table */ void prsdef(prscxdef *ctx, tokdef *tok, int typ); /* define a symbol as an object or forward-reference object */ void prsdefobj(prscxdef *ctx, tokdef *tok, int typ); /* * Require a property identifier, returning the property number. The * token containing the property identifier is removed from the input * stream. */ prpnum prsrqpr(prscxdef *ctx); /* signal a "missing required token" error, finding token name */ void prssigreq(prscxdef *ctx, int t); /* generate code from a parse tree */ void prsgexp(prscxdef *ctx, prsndef *n); /* generate code for initializer */ void prsgini(prscxdef *ctx, prsndef *node, uint curfr); /* check for and skip a required token */ void prsreq(prscxdef *ctx, int t); /* get next token, require it to be a particular value, then skip it */ void prsnreq(prscxdef *ctx, int t); /* START parsing an expression - resets node pool */ prsndef *prsexpr(prscxdef *ctx); /* allocate space in the parse node area */ uchar *prsbalo(prscxdef *ctx, uint siz); /* build a quad operator node */ prsndef *prsnew4(prscxdef *ctx, int t, prsndef *n1, prsndef *n2, prsndef *n3, prsndef *n4); /* build a tertiary operator node */ prsndef *prsnew3(prscxdef *ctx, int t, prsndef *n1, prsndef *n2, prsndef *n3); /* build a binary operator node */ prsndef *prsnew2(prscxdef *ctx, int t, prsndef *n1, prsndef *n2); /* build a unary operator node */ prsndef *prsnew1(prscxdef *ctx, int t, prsndef *n); /* build a new value node */ prsndef *prsnew0(prscxdef *ctx, tokdef *tokp); /* start parsing an initializer expression (resets node pool) */ prsndef *prsxini(prscxdef *ctx); #endif /* PRS_INCLUDED */ qtads-2.1.7/tads2/qas.c000066400000000000000000000066421265017072300146150ustar00rootroot00000000000000/* * Copyright (c) 1991, 2002 Michael J. Roberts. All rights reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name qas - qa scripter Function Allows TADS to read part or all of the commands from a session from a file. Notes Some operating systems (e.g., Mac) obtain user input in ways that don't involve the command line. For these systems to work properly, the os_xxx routines that invoke other input methods must be "qa scripter aware"; for example, the Mac os_askfile() routine must put the filename it gets back in the command log file, or must read directly from the command log file, or both. Modified 03/10/91 MJRoberts - created */ #include #include #include "os.h" #include "run.h" #include "tio.h" /* * Globals for the script reader */ osfildef *scrfp = (osfildef *)0; /* script file */ int scrquiet = 0; /* flag: true ==> script is NOT shown as read */ /* * open script file */ int qasopn(char *scrnam, int quiet) { if (scrfp) return 1; /* already reading from script */ if ((scrfp = osfoprt(scrnam, OSFTCMD)) == 0) return 1; scrquiet = quiet; return 0; } /* * close script file */ void qasclose() { /* only close the script file if there's one open */ if (scrfp) { osfcls(scrfp); scrfp = 0; /* no more script file */ scrquiet = 0; } } /* * Read the next line from the script file (this is essentially the * script-redirected os_gets). Only lines starting with '>' are * considered script input lines; all other lines are comments, and are * ignored. */ char *qasgets(char *buf, int bufl) { /* shouldn't be here at all if there's no script file */ if (scrfp == 0) return 0; /* update status line */ runstat(); /* keep going until we find something we like */ for (;;) { char c; /* * Read the next character of input. If it's not a newline, * there's more on the same line, so read the rest and see what * to do. */ c = osfgetc(scrfp); if (c != '\n' && c != '\r') { /* read the rest of the line */ if (!osfgets(buf, bufl, scrfp)) { /* end of file: close the script and return eof */ qasclose(); return 0; } /* if the line started with '>', strip '\n' and return line */ if (c == '>') { int l; /* remove the trailing newline */ if ((l = strlen(buf)) > 0 && (buf[l-1] == '\n' || buf[l-1] == '\r')) buf[l-1] = 0; /* * if we're not in quiet mode, echo the command to the * display */ if (!scrquiet) outformat(buf); /* flush the current line without adding any blank lines */ outflushn(1); /* return the command */ return buf; } } else if (c == EOF ) { /* end of file - close the script and return eof */ qasclose(); return 0; } } } qtads-2.1.7/tads2/regex.c000066400000000000000000001464231265017072300151450ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/regex.c,v 1.3 1999/07/11 00:46:34 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name regex.c - Regular Expression Parser and Recognizer for TADS Function Parses and recognizes regular expressions Notes Regular expression syntax: abc|def either abc or def (abc) abc abc+ abc, abcc, abccc, ... abc* ab, abc, abcc, ... abc? ab or abc . any single character abc$ abc at the end of the line ^abc abc at the beginning of the line %^abc literally ^abc [abcx-z] matches a, b, c, x, y, or z [^abcx-z] matches any character except a, b, c, x, y, or z [^]-q] matches any character except ], -, or q Note that using ']' or '-' in a character range expression requires special ordering. If ']' is to be used, it must be the first character after the '^', if present, within the brackets. If '-' is to be used, it must be the first character after the '^' and/or ']', if present. '%' is used to escape the special characters: | . ( ) * ? + ^ $ % [ (We use '%' rather than a backslash because it's less trouble to enter in a TADS string -- a backslash needs to be quoted with another backslash, which is error-prone and hard to read. '%' doesn't need any special quoting in a TADS string, which makes it a lot more readable.) In addition, '%' is used to introduce additional special sequences: %1 text matching first parenthesized expression %9 text matching ninth parenthesized experssion %< matches at the beginning of a word only %> matches at the end of a word only %w matches any word character %W matches any non-word character %b matches at any word boundary (beginning or end of word) %B matches except at a word boundary For the word matching sequences, a word is any sequence of letters and numbers. Modified 09/06/98 MJRoberts - Creation */ #include #include #include #include #include "std.h" #include "mch.h" #include "ler.h" #include "regex.h" /* ------------------------------------------------------------------------ */ /* * A "machine" (i.e., a finite state automaton) is a set of state * transition tuples. A tuple has three elements: the state ID, the ID * of the state that we transition to, and the condition for the * transition. The condition is simply the character that we must match * to make the transition, or a special distinguished symbol "epsilon," * which refers to a transition with no input character consumed. * * The primitive elements of our machines guarantee that we never have * more than two transitions out of a particular state, so we can * denormalize the representation of a state by storing the two possible * tuples for that state in a single combined tuple. This has the * performance advantage that we can use the state ID as an index into * an array of state tuples. * * A particular machine always has a single initial and single final * (successful) state, so we can define a machine by its initial and * final state ID's. */ /* the special symbol value for "epsilon" */ #define RE_EPSILON '\001' /* the special symbol value for a wildcard character */ #define RE_WILDCARD '\002' /* special symbol values for beginning and end of text */ #define RE_TEXT_BEGIN '\003' #define RE_TEXT_END '\004' /* special symbol values for start and end of a word */ #define RE_WORD_BEGIN '\005' #define RE_WORD_END '\006' /* special symbols for word-char and non-word-char */ #define RE_WORD_CHAR '\007' #define RE_NON_WORD_CHAR '\010' /* special symbols for word-boundary and non-word-boundary */ #define RE_WORD_BOUNDARY '\011' #define RE_NON_WORD_BOUNDARY '\012' /* special symbol for a character range/exclusion range */ #define RE_RANGE '\013' #define RE_RANGE_EXCL '\014' /* a range of special symbol values for group matchers */ #define RE_GROUP_MATCH_0 '\015' #define RE_GROUP_MATCH_9 (RE_GROUP_MATCH_0 + 9) /* ------------------------------------------------------------------------ */ /* * A machine description. Machines are fully described by their initial * and final state ID's. */ typedef struct { /* the machine's initial state */ re_state_id init; /* the machine's final state */ re_state_id final; } re_machine; /* ------------------------------------------------------------------------ */ /* * Initialize the context. The memory for the context structure itself * is allocated and maintained by the caller. */ void re_init(re_context *ctx, errcxdef *errctx) { /* save the error context */ ctx->errctx = errctx; /* no tuple array yet */ ctx->tuple_arr = 0; ctx->tuples_alloc = 0; /* clear states */ ctx->next_state = RE_STATE_FIRST_VALID; /* clear groups */ ctx->cur_group = 0; /* no string buffer yet */ ctx->strbuf = 0; } /* ------------------------------------------------------------------------ */ /* * Reset compiler - clears states and tuples */ static void re_reset(re_context *ctx) { int i; /* delete any range tables we've allocated */ for (i = 0 ; i < ctx->next_state ; ++i) { if (ctx->tuple_arr[i].char_range != 0) { mchfre(ctx->tuple_arr[i].char_range); ctx->tuple_arr[i].char_range = 0; } } /* clear states */ ctx->next_state = RE_STATE_FIRST_VALID; /* clear groups */ ctx->cur_group = 0; } /* ------------------------------------------------------------------------ */ /* * Delete the context - frees structures associated with the context. * Does NOT free the memory used by the context structure itself. */ void re_delete(re_context *ctx) { /* reset state */ re_reset(ctx); /* if we've allocated an array, delete it */ if (ctx->tuple_arr != 0) { mchfre(ctx->tuple_arr); ctx->tuple_arr = 0; } /* if we allocated a string buffer, delete it */ if (ctx->strbuf != 0) { mchfre(ctx->strbuf); ctx->strbuf = 0; } } /* ------------------------------------------------------------------------ */ /* * Allocate a new state ID */ static re_state_id re_alloc_state(re_context *ctx) { /* * If we don't have enough room for another state, expand the array */ if (ctx->next_state >= ctx->tuples_alloc) { uint new_alloc; /* bump the size by a bit */ new_alloc = ctx->tuples_alloc + 100; /* allocate or expand the array */ if (ctx->tuples_alloc == 0) { /* allocate the initial memory block */ ctx->tuple_arr = (re_tuple *)mchalo(ctx->errctx, (new_alloc * sizeof(re_tuple)), "regex"); } else { re_tuple *ptr; /* allocate a new memory block */ ptr = (re_tuple *)mchalo(ctx->errctx, (new_alloc * sizeof(re_tuple)), "regex"); /* copy the old memory to the new memory */ memcpy(ptr, ctx->tuple_arr, ctx->tuples_alloc * sizeof(re_tuple)); /* free the old block */ mchfre(ctx->tuple_arr); /* use the new block */ ctx->tuple_arr = ptr; } /* remember the new allocation size */ ctx->tuples_alloc = new_alloc; } /* initialize the next state */ ctx->tuple_arr[ctx->next_state].next_state_1 = RE_STATE_INVALID; ctx->tuple_arr[ctx->next_state].next_state_2 = RE_STATE_INVALID; ctx->tuple_arr[ctx->next_state].ch = RE_EPSILON; ctx->tuple_arr[ctx->next_state].flags = 0; ctx->tuple_arr[ctx->next_state].char_range = 0; /* return the new state's ID */ return ctx->next_state++; } /* ------------------------------------------------------------------------ */ /* * Set a transition from a state to a given destination state. */ static void re_set_trans(re_context *ctx, re_state_id id, re_state_id dest_id, char ch) { re_tuple *tuple; /* * get the tuple containing the transitions for this state ID - the * state ID is the index of the state's transition tuple in the * array */ tuple = &ctx->tuple_arr[id]; /* * If the first state pointer hasn't been set yet, set it to the new * destination. Otherwise, set the second state pointer. * * Only set the character on setting the first state. When setting * the second state, we must assume that the character for the state * has already been set, since any given state can have only one * character setting. */ if (tuple->next_state_1 == RE_STATE_INVALID) { /* * set the character ID, unless the state has been marked with a * special flag which indicates that the character value has * another meaning (in particular, a group marker) */ if (!(tuple->flags & (RE_STATE_GROUP_BEGIN | RE_STATE_GROUP_END))) tuple->ch = ch; /* set the first transition */ tuple->next_state_1 = dest_id; } else { /* set only the second transition state - don't set the character */ tuple->next_state_2 = dest_id; } } /* ------------------------------------------------------------------------ */ /* * Initialize a new machine, giving it an initial and final state */ static void re_init_machine(re_context *ctx, re_machine *machine) { machine->init = re_alloc_state(ctx); machine->final = re_alloc_state(ctx); } /* * Build a character recognizer */ static void re_build_char(re_context *ctx, re_machine *machine, char ch) { /* initialize our new machine */ re_init_machine(ctx, machine); /* allocate a transition tuple for the new state */ re_set_trans(ctx, machine->init, machine->final, ch); } /* * Build a character range recognizer. 'range' is a 256-bit (32-byte) * bit vector. */ static void re_build_char_range(re_context *ctx, re_machine *machine, unsigned char *range, int exclusion) { unsigned char *range_copy; /* initialize our new machine */ re_init_machine(ctx, machine); /* allocate a transition table for the new state */ re_set_trans(ctx, machine->init, machine->final, (char)(exclusion ? RE_RANGE_EXCL : RE_RANGE)); /* allocate a copy of the range bit vector */ range_copy = (unsigned char *)mchalo(ctx->errctx, 32, "regex range"); /* copy the caller's range */ memcpy(range_copy, range, 32); /* store it in the tuple */ ctx->tuple_arr[machine->init].char_range = range_copy; } /* * Build a group recognizer. This is almost the same as a character * recognizer, but matches a previous group rather than a literal * character. */ static void re_build_group_matcher(re_context *ctx, re_machine *machine, int group_num) { /* initialize our new machine */ re_init_machine(ctx, machine); /* * Allocate a transition tuple for the new state, using the group ID * as the character code. Store the special code for a group * recognizer rather than the normal literal character code. */ re_set_trans(ctx, machine->init, machine->final, (char)(group_num + RE_GROUP_MATCH_0)); } /* * Build a concatenation recognizer */ static void re_build_concat(re_context *ctx, re_machine *new_machine, re_machine *lhs, re_machine *rhs) { /* initialize the new machine */ re_init_machine(ctx, new_machine); /* * set up an epsilon transition from the new machine's initial state * to the first submachine's initial state */ re_set_trans(ctx, new_machine->init, lhs->init, RE_EPSILON); /* * Set up an epsilon transition from the first submachine's final * state to the second submachine's initial state */ re_set_trans(ctx, lhs->final, rhs->init, RE_EPSILON); /* * Set up an epsilon transition from the second submachine's final * state to our new machine's final state */ re_set_trans(ctx, rhs->final, new_machine->final, RE_EPSILON); } /* * Build a group machine. sub_machine contains the machine that * expresses the group's contents; we'll fill in new_machine with a * newly-created machine that encloses and marks the group. */ static void re_build_group(re_context *ctx, re_machine *new_machine, re_machine *sub_machine, int group_id) { /* initialize the container machine */ re_init_machine(ctx, new_machine); /* * set up an epsilon transition from the new machine's initial state * into the initial state of the group, and another transition from * the group's final state into the container's final state */ re_set_trans(ctx, new_machine->init, sub_machine->init, RE_EPSILON); re_set_trans(ctx, sub_machine->final, new_machine->final, RE_EPSILON); /* * Mark the initial and final states of the group machine as being * group markers. */ ctx->tuple_arr[new_machine->init].flags |= RE_STATE_GROUP_BEGIN; ctx->tuple_arr[new_machine->final].flags |= RE_STATE_GROUP_END; /* store the group ID in the 'ch' member of the start and end states */ ctx->tuple_arr[new_machine->init].ch = group_id; ctx->tuple_arr[new_machine->final].ch = group_id; } /* * Build an alternation recognizer */ static void re_build_alter(re_context *ctx, re_machine *new_machine, re_machine *lhs, re_machine *rhs) { /* initialize the new machine */ re_init_machine(ctx, new_machine); /* * Set up an epsilon transition from our new machine's initial state * to the initial state of each submachine */ re_set_trans(ctx, new_machine->init, lhs->init, RE_EPSILON); re_set_trans(ctx, new_machine->init, rhs->init, RE_EPSILON); /* * Set up an epsilon transition from the final state of each * submachine to our final state */ re_set_trans(ctx, lhs->final, new_machine->final, RE_EPSILON); re_set_trans(ctx, rhs->final, new_machine->final, RE_EPSILON); } /* * Build a closure recognizer */ static void re_build_closure(re_context *ctx, re_machine *new_machine, re_machine *sub, char specifier) { /* initialize the new machine */ re_init_machine(ctx, new_machine); /* * set up an epsilon transition from our initial state to the * submachine's initial state, and from the submachine's final state * to our final state */ re_set_trans(ctx, new_machine->init, sub->init, RE_EPSILON); re_set_trans(ctx, sub->final, new_machine->final, RE_EPSILON); /* * If this is an unbounded closure ('*' or '+', but not '?'), set up * the loop transition that takes us from the new machine's final * state back to its initial state. We don't do this on the * zero-or-one closure, because we can only match the expression * once. */ if (specifier != '?') re_set_trans(ctx, sub->final, sub->init, RE_EPSILON); /* * If this is a zero-or-one closure or a zero-or-more closure, set * up an epsilon transition from our initial state to our final * state, since we can skip the entire subexpression. We don't do * this on the one-or-more closure, because we can't skip the * subexpression in this case. */ if (specifier != '+') re_set_trans(ctx, new_machine->init, new_machine->final, RE_EPSILON); } /* * Build a null machine */ static void re_build_null_machine(re_context *ctx, re_machine *machine) { machine->init = machine->final = RE_STATE_INVALID; } /* ------------------------------------------------------------------------ */ /* * Determine if a machine is null */ static int re_is_machine_null(re_context *ctx, re_machine *machine) { return (machine->init == RE_STATE_INVALID); } /* ------------------------------------------------------------------------ */ /* * Concatenate the second machine onto the first machine, replacing the * first machine with the resulting machine. If the first machine is a * null machine (created with re_build_null_machine), we'll simply copy * the second machine into the first. */ static void re_concat_onto(re_context *ctx, re_machine *dest, re_machine *rhs) { /* check for a null destination machine */ if (re_is_machine_null(ctx, dest)) { /* * the first machine is null - simply copy the second machine * onto the first unchanged */ *dest = *rhs; } else { re_machine new_machine; /* build the concatenated machine */ re_build_concat(ctx, &new_machine, dest, rhs); /* copy the concatenated machine onto the first machine */ *dest = new_machine; } } /* * Alternate the second machine onto the first machine, replacing the * first machine with the resulting machine. If the first machine is a * null machine, this simply replaces the first machine with the second * machine. If the second machine is null, this simply leaves the first * machine unchanged. */ static void re_alternate_onto(re_context *ctx, re_machine *dest, re_machine *rhs) { /* check to see if the first machine is null */ if (re_is_machine_null(ctx, dest)) { /* * the first machine is null - simply copy the second machine * onto the first */ *dest = *rhs; } else { /* * if the second machine is null, don't do anything; otherwise, * build the alternation */ if (!re_is_machine_null(ctx, rhs)) { re_machine new_machine; /* build the alternation */ re_build_alter(ctx, &new_machine, dest, rhs); /* replace the first machine with the alternation */ *dest = new_machine; } } } /* ------------------------------------------------------------------------ */ /* * Set a bit in a bit vector. */ #define re_set_bit(set, bit) \ (((unsigned char *)(set))[(bit) >> 3] |= (1 << ((bit) & 7))) /* * Test a bit in a bit vector */ #define re_is_bit_set(set, bit) \ ((((unsigned char *)(set))[(bit) >> 3] & (1 << ((bit) & 7))) != 0) /* ------------------------------------------------------------------------ */ /* * Compile an expression */ static re_status_t re_compile(re_context *ctx, const char *expr, size_t exprlen, re_machine *result_machine) { re_machine cur_machine; re_machine alter_machine; re_machine new_machine; size_t group_stack_level; struct { re_machine old_cur; re_machine old_alter; int group_id; } group_stack[50]; /* reset everything */ re_reset(ctx); /* start out with no current machine and no alternate machine */ re_build_null_machine(ctx, &cur_machine); re_build_null_machine(ctx, &alter_machine); /* nothing on the stack yet */ group_stack_level = 0; /* loop until we run out of expression to parse */ for ( ; exprlen != 0 ; ++expr, --exprlen) { switch(*expr) { case '^': /* * beginning of line - if we're not at the beginning of the * current expression (i.e., we already have some * concatentations accumulated), treat it as an ordinary * character */ if (!re_is_machine_null(ctx, &cur_machine)) goto normal_char; /* build a new start-of-text recognizer */ re_build_char(ctx, &new_machine, RE_TEXT_BEGIN); /* * concatenate it onto the string - note that this can't * have any postfix operators */ re_concat_onto(ctx, &cur_machine, &new_machine); break; case '$': /* * End of line specifier - if there's anything left after * the '$' other than a close parens or alternation * specifier, great it as a normal character */ if (exprlen > 1 && (*(expr+1) != ')' && *(expr+1) != '|')) goto normal_char; /* build a new end-of-text recognizer */ re_build_char(ctx, &new_machine, RE_TEXT_END); /* * concatenate it onto the string - note that this can't * have any postfix operators */ re_concat_onto(ctx, &cur_machine, &new_machine); break; case '(': /* * Add a nesting level. Push the current machine and * alternate machines onto the group stack, and clear * everything out for the new group. */ if (group_stack_level > sizeof(group_stack)/sizeof(group_stack[0])) { /* we cannot proceed - return an error */ return RE_STATUS_GROUP_NESTING_TOO_DEEP; } /* save the current state on the stack */ group_stack[group_stack_level].old_cur = cur_machine; group_stack[group_stack_level].old_alter = alter_machine; /* * Assign the group a group ID - groups are numbered in * order of their opening (left) parentheses, so we want to * assign a group number now. We won't actually need to * know the group number until we get to the matching close * paren, but we need to assign it now, so store it in the * group stack. */ group_stack[group_stack_level].group_id = ctx->cur_group; /* consume the group number */ ctx->cur_group++; /* push the level */ ++group_stack_level; /* start the new group with empty machines */ re_build_null_machine(ctx, &cur_machine); re_build_null_machine(ctx, &alter_machine); break; case ')': /* if there's nothing on the stack, ignore this */ if (group_stack_level == 0) break; /* take a level off the stack */ --group_stack_level; /* * Remove a nesting level. If we have a pending alternate * expression, build the alternation expression. This will * leave the entire group expression in alter_machine, * regardless of whether an alternation was in progress or * not. */ re_alternate_onto(ctx, &alter_machine, &cur_machine); /* * Create a group machine that encloses the group and marks * it with a group number. We assigned the group number * when we parsed the open paren, so read that group number * from the stack. * * Note that this will leave 'new_machine' with the entire * group machine. */ re_build_group(ctx, &new_machine, &alter_machine, group_stack[group_stack_level].group_id); /* * Pop the stack - restore the alternation and current * machines that were in progress before the group started. */ cur_machine = group_stack[group_stack_level].old_cur; alter_machine = group_stack[group_stack_level].old_alter; /* * Check the group expression (in new_machine) for postfix * expressions */ goto apply_postfix; case '|': /* * Start a new alternation. This ends the current * alternation; if we have a previous pending alternate, * build an alternation machine out of the previous * alternate and the current machine and move that to the * alternate; otherwise, simply move the current machine to * the pending alternate. */ re_alternate_onto(ctx, &alter_machine, &cur_machine); /* * the alternation starts out with a blank slate, so null * out the current machine */ re_build_null_machine(ctx, &cur_machine); break; case '%': /* * quoted character - skip the quote mark and see what we * have */ ++expr; --exprlen; /* check to see if we're at the end of the expression */ if (exprlen == 0) { /* * end of the string - ignore it, but undo the extra * increment of the expression index so that we exit the * enclosing loop properly */ --expr; ++exprlen; break; } /* see what we have */ switch(*expr) { case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* group match - build a new literal group recognizer */ re_build_group_matcher(ctx, &new_machine, (int)(*expr - '1')); /* apply any postfix expression to the group recognizer */ goto apply_postfix; case '<': /* build a beginning-of-word recognizer */ re_build_char(ctx, &new_machine, RE_WORD_BEGIN); /* it can't be postfixed - just concatenate it */ re_concat_onto(ctx, &cur_machine, &new_machine); break; case '>': /* build an end-of-word recognizer */ re_build_char(ctx, &new_machine, RE_WORD_END); /* it can't be postfixed - just concatenate it */ re_concat_onto(ctx, &cur_machine, &new_machine); break; case 'w': /* word character */ re_build_char(ctx, &new_machine, RE_WORD_CHAR); goto apply_postfix; case 'W': /* non-word character */ re_build_char(ctx, &new_machine, RE_NON_WORD_CHAR); goto apply_postfix; case 'b': /* word boundary */ re_build_char(ctx, &new_machine, RE_WORD_BOUNDARY); /* it can't be postfixed */ re_concat_onto(ctx, &cur_machine, &new_machine); break; case 'B': /* not a word boundary */ re_build_char(ctx, &new_machine, RE_NON_WORD_BOUNDARY); /* it can't be postfixed */ re_concat_onto(ctx, &cur_machine, &new_machine); break; default: /* build a new literal character recognizer */ re_build_char(ctx, &new_machine, *expr); /* apply any postfix expression to the character */ goto apply_postfix; } break; case '.': /* * wildcard character - build a single character recognizer * for the special wildcard symbol, then go check it for a * postfix operator */ re_build_char(ctx, &new_machine, RE_WILDCARD); goto apply_postfix; break; case '[': /* range expression */ { int is_exclusive = FALSE; unsigned char set[32]; /* clear out the set of characters in the range */ memset(set, 0, sizeof(set)); /* first, skip the open bracket */ ++expr; --exprlen; /* check to see if starts with the exclusion character */ if (exprlen != 0 && *expr == '^') { /* skip the exclusion specifier */ ++expr; --exprlen; /* note it */ is_exclusive = TRUE; } /* * if the first character is a ']', include it in the * range */ if (exprlen != 0 && *expr == ']') { re_set_bit(set, (int)']'); ++expr; --exprlen; } /* * if the next character is a '-', include it in the * range */ if (exprlen != 0 && *expr == '-') { re_set_bit(set, (int)'-'); ++expr; --exprlen; } /* scan the character set */ while (exprlen != 0 && *expr != ']') { int ch; /* note this character */ ch = (int)(unsigned char)*expr; /* set it */ re_set_bit(set, ch); /* skip this character of the expression */ ++expr; --exprlen; /* check for a range */ if (exprlen != 0 && *expr == '-') { int ch2; /* skip the '-' */ ++expr; --exprlen; if (exprlen != 0) { /* get the other end of the range */ ch2 = (int)(unsigned char)*expr; /* skip the second character */ ++expr; --exprlen; /* if the range is reversed, swap it */ if (ch > ch2) { int tmp = ch; ch = ch2; ch2 = tmp; } /* fill in the range */ for ( ; ch <= ch2 ; ++ch) re_set_bit(set, ch); } } } /* create a character range machine */ re_build_char_range(ctx, &new_machine, set, is_exclusive); /* apply any postfix operator */ goto apply_postfix; } break; default: normal_char: /* * it's an ordinary character - build a single character * recognizer machine, and then concatenate it onto any * existing machine */ re_build_char(ctx, &new_machine, *expr); apply_postfix: /* * Check for a postfix operator, and apply it to the machine * in 'new_machine' if present. In any case, concatenate * the 'new_machine' (modified by a postix operator or not) * to the current machien. */ if (exprlen > 1) { switch(*(expr+1)) { case '*': case '+': case '?': /* * We have a postfix closure operator. Build a new * closure machine out of 'new_machine'. */ { re_machine closure_machine; /* move onto the closure operator */ ++expr; --exprlen; /* build the closure machine */ re_build_closure(ctx, &closure_machine, &new_machine, *expr); /* replace the original machine with the closure */ new_machine = closure_machine; /* * skip any redundant closure symbols, keeping * only the first one we saw */ while (exprlen > 1 && (*(expr+1) == '?' || *(expr+1) == '+' || *(expr+1) == '*')) { ++expr; --exprlen; } } break; default: /* no postfix operator */ break; } } /* * Concatenate the new machine onto the current machine * under construction. */ re_concat_onto(ctx, &cur_machine, &new_machine); break; } } /* complete any pending alternation */ re_alternate_onto(ctx, &alter_machine, &cur_machine); /* store the resulting machine in the caller's machine descriptor */ *result_machine = alter_machine; /* no errors encountered */ return RE_STATUS_SUCCESS; } /* ------------------------------------------------------------------------ */ /* * Pattern recognizer */ /* * Note a group position if appropriate */ static void re_note_group(re_context *ctx, re_group_register *regs, re_state_id id, const char *p) { int group_index; /* * Check to see if this is a valid state and it's a group marker - * if not, there's nothing to do */ if (id == RE_STATE_INVALID || !(ctx->tuple_arr[id].flags & (RE_STATE_GROUP_BEGIN | RE_STATE_GROUP_END)) || (group_index = (int)ctx->tuple_arr[id].ch) >= RE_GROUP_REG_CNT) return; /* * It's a valid group marker - note the appropriate register value */ if ((ctx->tuple_arr[id].flags & RE_STATE_GROUP_BEGIN) != 0) regs[group_index].start_ofs = p; else regs[group_index].end_ofs = p; } /* * Determine if a character is part of a word. We consider letters and * numbers to be word characters. */ static int re_is_word_char(char c) { return (isalpha((unsigned char)c) || isdigit((unsigned char)c)); } /* * Match a string to a compiled expression. Returns the length of the * match if successful, or -1 if no match was found. */ static int re_match(re_context *ctx, const char *entire_str, const char *str, size_t origlen, const re_machine *machine, re_group_register *regs) { re_state_id cur_state; const char *p; size_t curlen; /* start at the machine's initial state */ cur_state = machine->init; /* start at the beginning of the string */ p = str; curlen = origlen; /* note any group involved in the initial state */ re_note_group(ctx, regs, cur_state, p); /* * if we're starting in the final state, immediately return success * with a zero-length match */ if (cur_state == machine->final) { /* return success with a zero-length match */ return 0; } /* run the machine */ for (;;) { re_tuple *tuple; /* get the tuple for this state */ tuple = &ctx->tuple_arr[cur_state]; /* if this is a group state, adjust the group registers */ re_note_group(ctx, regs, cur_state, p); /* see what kind of state we're in */ if (!(tuple->flags & (RE_STATE_GROUP_BEGIN | RE_STATE_GROUP_END)) && tuple->ch != RE_EPSILON) { /* * This is a character or group recognizer state. If we * match the character or group, continue on to the next * state; otherwise, return failure. */ switch(tuple->ch) { case RE_GROUP_MATCH_0: case RE_GROUP_MATCH_0 + 1: case RE_GROUP_MATCH_0 + 2: case RE_GROUP_MATCH_0 + 3: case RE_GROUP_MATCH_0 + 4: case RE_GROUP_MATCH_0 + 5: case RE_GROUP_MATCH_0 + 6: case RE_GROUP_MATCH_0 + 7: case RE_GROUP_MATCH_0 + 8: case RE_GROUP_MATCH_0 + 9: { int group_num; re_group_register *group_reg; size_t reg_len; /* it's a group - get the group number */ group_num = tuple->ch - RE_GROUP_MATCH_0; group_reg = ®s[group_num]; /* * if this register isn't defined, there's nothing * to match, so fail */ if (group_reg->start_ofs == 0 || group_reg->end_ofs == 0) return -1; /* calculate the length of the register value */ reg_len = group_reg->end_ofs - group_reg->start_ofs; /* if we don't have enough left to match, it fails */ if (curlen < reg_len) return -1; /* if the string doesn't match exactly, we fail */ if (memcmp(p, group_reg->start_ofs, reg_len) != 0) return -1; /* * It matches exactly - skip the entire length of * the register in the source string */ p += reg_len; curlen -= reg_len; } break; case RE_TEXT_BEGIN: /* * Match only the exact beginning of the string - if * we're anywhere else, this isn't a match. If this * succeeds, we don't skip any characters. */ if (p != entire_str) return -1; break; case RE_TEXT_END: /* * Match only the exact end of the string - if we're * anywhere else, this isn't a match. Don't skip any * characters on success. */ if (curlen != 0) return -1; break; case RE_WORD_BEGIN: /* * if the previous character is a word character, we're * not at the beginning of a word */ if (p != entire_str && re_is_word_char(*(p-1))) return -1; /* * if we're at the end of the string, or the current * character isn't the start of a word, we're not at the * beginning of a word */ if (curlen == 0 || !re_is_word_char(*p)) return -1; break; case RE_WORD_END: /* * if the current character is a word character, we're not * at the end of a word */ if (curlen != 0 && re_is_word_char(*p)) return -1; /* * if we're at the beginning of the string, or the * previous character is not a word character, we're not * at the end of a word */ if (p == entire_str || !re_is_word_char(*(p-1))) return -1; break; case RE_WORD_CHAR: /* if it's not a word character, it's a failure */ if (curlen == 0 || !re_is_word_char(*p)) return -1; /* skip this character of input */ ++p; --curlen; break; case RE_NON_WORD_CHAR: /* if it's a word character, it's a failure */ if (curlen == 0 || re_is_word_char(*p)) return -1; /* skip the input */ ++p; --curlen; break; case RE_WORD_BOUNDARY: case RE_NON_WORD_BOUNDARY: { int prev_is_word; int next_is_word; int boundary; /* * Determine if the previous character is a word * character -- if we're at the beginning of the * string, it's obviously not, otherwise check its * classification */ prev_is_word = (p != entire_str && re_is_word_char(*(p-1))); /* make the same check for the current character */ next_is_word = (curlen != 0 && re_is_word_char(*p)); /* * Determine if this is a boundary - it is if the * two states are different */ boundary = ((prev_is_word != 0) ^ (next_is_word != 0)); /* * make sure it matches what was desired, and return * failure if not */ if ((tuple->ch == RE_WORD_BOUNDARY && !boundary) || (tuple->ch == RE_NON_WORD_BOUNDARY && boundary)) return -1; } break; case RE_WILDCARD: /* make sure we have a character to match */ if (curlen == 0) return -1; /* skip this character */ ++p; --curlen; break; case RE_RANGE: case RE_RANGE_EXCL: { int match; /* make sure we have a character to match */ if (curlen == 0) return -1; /* see if we match */ match = re_is_bit_set(tuple->char_range, (int)(unsigned char)*p); /* make sure we got what we wanted */ if ((tuple->ch == RE_RANGE && !match) || (tuple->ch == RE_RANGE_EXCL && match)) return -1; /* skip this character of the input */ ++p; --curlen; } break; default: /* make sure we have an exact match */ if (curlen == 0 || tuple->ch != *p) return -1; /* skip this character of the input */ ++p; --curlen; break; } /* * if we got this far, we were successful - move on to the * next state */ cur_state = tuple->next_state_1; } else if (tuple->next_state_2 == RE_STATE_INVALID) { /* * We have only one transition, so this state is entirely * deterministic. Simply move on to the next state. */ cur_state = tuple->next_state_1; } else { re_machine sub_machine; re_group_register regs1[RE_GROUP_REG_CNT]; re_group_register regs2[RE_GROUP_REG_CNT]; int ret1; int ret2; /* * This state has two possible transitions, and we don't * know which one to take. So, try both, see which one * works better, and return the result. Try the first * transition first. Note that each separate attempt must * use a separate copy of the registers. */ memcpy(regs1, regs, sizeof(regs1)); sub_machine.init = tuple->next_state_1; sub_machine.final = machine->final; ret1 = re_match(ctx, entire_str, p, curlen, &sub_machine, regs1); /* * Now try the second transition */ memcpy(regs2, regs, sizeof(regs2)); sub_machine.init = tuple->next_state_2; sub_machine.final = machine->final; ret2 = re_match(ctx, entire_str, p, curlen, &sub_machine, regs2); /* * If they both failed, the whole thing failed. Otherwise, * return the longer of the two, plus the length we * ourselves matched previously. Note that we return the * register set from the winning match. */ if (ret1 < 0 && ret2 < 0) { /* they both failed */ return -1; } else if (ret1 > ret2) { /* use the first register set and result length */ memcpy(regs, regs1, sizeof(regs1)); return ret1 + (p - str); } else { /* use the second register set and result length */ memcpy(regs, regs2, sizeof(regs2)); return ret2 + (p - str); } } /* * If we're in the final state, return success */ if (cur_state == machine->final) { /* finish off any group involved in the final state */ re_note_group(ctx, regs, cur_state, p); /* return the length we matched */ return p - str; } } } /* ------------------------------------------------------------------------ */ /* * Search for a regular expression within a string. Returns -1 if the * string cannot be found, otherwise returns the offset from the start * of the string to be searched of the start of the first match for the * pattern. */ static int re_search(re_context *ctx, const char *str, size_t len, const re_machine *machine, re_group_register *regs, int *result_len) { int ofs; /* * Starting at the first character in the string, search for the * pattern at each subsequent character until we either find the * pattern or run out of string to test. */ for (ofs = 0 ; ofs < (int)len ; ++ofs) { int matchlen; /* check for a match */ matchlen = re_match(ctx, str, str + ofs, len - ofs, machine, regs); if (matchlen >= 0) { /* we found a match here - return the length and offset */ *result_len = matchlen; return ofs; } } /* we didn't find a match */ return -1; } /* ------------------------------------------------------------------------ */ /* * Make a copy of a search string in our private buffer. */ static void re_save_search_str(re_context *ctx, const char *str, size_t len) { /* if the string is empty, this is easy */ if (len == 0) { /* nothing to store - just save the length and return */ ctx->curlen = 0; return; } /* if the current buffer isn't big enough, allocate a new one */ if (ctx->strbuf == 0 || ctx->strbufsiz < len) { /* * free any previous buffer - its contents are no longer * important, since we're about to overwrite it with a new * string */ if (ctx->strbuf != 0) mchfre(ctx->strbuf); /* * allocate a new buffer; round up to the next 256-byte * increment to make sure we're not constantly reallocating to * random sizes */ ctx->strbufsiz = ((len + 255) & ~255); /* allocate it */ ctx->strbuf = (char *)mchalo(ctx->errctx, ctx->strbufsiz, "regex str"); } /* copy the string */ memcpy(ctx->strbuf, str, len); /* save the length */ ctx->curlen = len; } /* ------------------------------------------------------------------------ */ /* * Compile an expression and search for a match within the given string. * Returns the offset of the match, or -1 if no match was found. */ int re_compile_and_search(re_context *ctx, const char *pattern, size_t patlen, const char *searchstr, size_t searchlen, int *result_len) { re_machine machine; /* compile the expression - return failure if we get an error */ if (re_compile(ctx, pattern, patlen, &machine) != RE_STATUS_SUCCESS) return -1; /* save the search string in our internal buffer */ re_save_search_str(ctx, searchstr, searchlen); /* clear the group registers */ memset(ctx->regs, 0, sizeof(ctx->regs)); /* * search for the pattern in our copy of the string - use the copy * so that the group registers stay valid even if the caller * deallocates the original string after we return */ return re_search(ctx, ctx->strbuf, ctx->curlen, &machine, ctx->regs, result_len); } /* ------------------------------------------------------------------------ */ /* * Compile an expression and check for a match. Returns the length of * the match if we found a match, -1 if we found no match. This is not * a search function; we merely match the leading substring of the given * string to the given pattern. */ int re_compile_and_match(re_context *ctx, const char *pattern, size_t patlen, const char *searchstr, size_t searchlen) { re_machine machine; /* compile the expression - return failure if we get an error */ if (re_compile(ctx, pattern, patlen, &machine) != RE_STATUS_SUCCESS) return FALSE; /* save the search string in our internal buffer */ re_save_search_str(ctx, searchstr, searchlen); /* clear the group registers */ memset(ctx->regs, 0, sizeof(ctx->regs)); /* match the string */ return re_match(ctx, ctx->strbuf, ctx->strbuf, ctx->curlen, &machine, ctx->regs); } qtads-2.1.7/tads2/regex.h000066400000000000000000000115041265017072300151410ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/regex.h,v 1.3 1999/07/11 00:46:34 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name regex.h - regular expression parser for TADS Function Notes Modified 04/11/99 CNebel - Fix warnings. 10/07/98 MJRoberts - Creation */ #ifndef REGEX_H #define REGEX_H #include /* state ID */ typedef int re_state_id; /* invalid state ID - used to mark null machines */ #define RE_STATE_INVALID ((re_state_id)-1) /* first valid state ID */ #define RE_STATE_FIRST_VALID ((re_state_id)0) /* ------------------------------------------------------------------------ */ /* * Group register structure. Each register keeps track of the starting * and ending offset of the group's text. */ typedef struct { const char *start_ofs; const char *end_ofs; } re_group_register; /* number of group registers we keep */ #define RE_GROUP_REG_CNT 10 /* ------------------------------------------------------------------------ */ /* * Denormalized state transition tuple. Each tuple represents the * complete set of transitions out of a particular state. A particular * state can have one character transition, or two epsilon transitions. * Note that we don't need to store the state ID in the tuple, because * the state ID is the index of the tuple in an array of state tuples. */ typedef struct { /* the character we must match to transition to the target state */ char ch; /* the target states */ re_state_id next_state_1; re_state_id next_state_2; /* character range match table, if used */ unsigned char *char_range; /* flags */ unsigned char flags; } re_tuple; /* * Tuple flags */ /* this state is the start of a group - the 'ch' value is the group ID */ #define RE_STATE_GROUP_BEGIN 0x02 /* this state is the end of a group - 'ch' is the group ID */ #define RE_STATE_GROUP_END 0x04 /* ------------------------------------------------------------------------ */ /* * Regular expression compilation context structure. This tracks the * state of the compilation and stores the resources associated with the * compiled expression. */ typedef struct { /* error context */ errcxdef *errctx; /* next available state ID */ re_state_id next_state; /* * The array of transition tuples. We'll allocate this array and * expand it as necessary. */ re_tuple *tuple_arr; /* number of transition tuples allocated in the array */ int tuples_alloc; /* current group ID */ int cur_group; /* group registers */ re_group_register regs[RE_GROUP_REG_CNT]; /* * Buffer for retaining a copy of the last string we scanned. We * retain our own copy of each string, and point the group registers * into this copy rather than the caller's original string -- this * ensures that the group registers remain valid even after the * caller has deallocated the original string. */ char *strbuf; /* length of the string currently in the buffer */ size_t curlen; /* size of the buffer allocated to strbuf */ size_t strbufsiz; } re_context; /* ------------------------------------------------------------------------ */ /* * Status codes */ typedef enum { /* success */ RE_STATUS_SUCCESS = 0, /* compilation error - group nesting too deep */ RE_STATUS_GROUP_NESTING_TOO_DEEP } re_status_t; /* ------------------------------------------------------------------------ */ /* * Initialize the context. The memory for the context structure itself * must be allocated and maintained by the caller. */ void re_init(re_context *ctx, errcxdef *errctx); /* * Delete the context - frees structures associated with the context. * Does NOT free the memory used by the context structure itself. */ void re_delete(re_context *ctx); /* * Compile an expression and search for a match within the given string. * Returns the offset of the match, or -1 if no match was found. */ int re_compile_and_search(re_context *ctx, const char *pattern, size_t patlen, const char *searchstr, size_t searchlen, int *result_len); /* * Compile an expression and check for a match. Returns the length of * the match if we found a match, -1 if we found no match. This is not * a search function; we merely match the leading substring of the given * string to the given pattern. */ int re_compile_and_match(re_context *ctx, const char *pattern, size_t patlen, const char *searchstr, size_t searchlen); #endif /* REGEX_H */ qtads-2.1.7/tads2/res.h000066400000000000000000000041641265017072300146240ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/RES.H,v 1.1 1999/07/11 00:46:30 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name res.h - string resource definitions for TADS Function Defines the ID's for string resources. These strings must be loaded when needed with os_get_str_rsc(), since the specific mechanism by which these strings are loaded varies by operating system. We assign the resources sequential numbers starting at 1, to make it easier to map the resource loader to an operating system mechanism where such a mechanism exists. Notes Modified 06/25/99 MJRoberts - Creation */ #ifndef _RES_H_ #define _RES_H_ /* * Dialog buttons. These provide the text for standard buttons in * dialogs created with os_input_dialog(). * * These labels can use "&" to indicate a shortcut letter, per the * normal os_input_dialog() interface; for example, if the Yes button * label is "&Yes", the button has the shortcut letter "Y". * * The text of these buttons may vary by system, since these should * conform to local conventions where there are local conventions. In * addition, of course, these strings will vary by language. */ /* OK and Cancel buttons */ #define RESID_BTN_OK 1 #define RESID_BTN_CANCEL 2 /* "Yes" and "No" buttons */ #define RESID_BTN_YES 3 #define RESID_BTN_NO 4 /* * Reply strings for the yorn() built-in function. These strings are * regular expressions as matched by the regex.h functions. For * English, for example, the "yes" string would be "[Yy].*" and the "no" * string would be "[Nn].*". For German, it might be desirable to * accept both "Ja" and "Yes", so the "Yes" string might be "[JjYy].*". * * It's not necessary in these patterns to consider leading spaces, * since the yorn() function will skip any leading spaces before * performing the pattern match. */ #define RESID_YORN_YES 5 #define RESID_YORN_NO 6 #endif /* _RES_H_ */ qtads-2.1.7/tads2/run.c000066400000000000000000002250231265017072300146310ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/RUN.C,v 1.2 1999/05/17 02:52:13 MJRoberts Exp $"; #endif /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name run.c - code execution Function Executes p-code Notes Due to the highly mobile memory architecture of this version of TADS, objects and data within objects can move at certain junctures. At these times, pointers to object data become invalid, and it's necessary to re-establish those pointers. Two functions are provided to facilitate this. runcpsav() is called prior to an operation that may move object data; it returns the offset within the object and property of the code being executed, and unlocks the object. runcprst() is called after such an operation; it relocks the object, and returns a pointer to the property data. Note that the special property number zero is used for functions; this indicates that no prpdef structuring is done on the object, but that its actual data start at offset 0. Modified 04/11/99 CNebel - Fix warnings. 10/20/91 MJRoberts - creation */ #include #include #include "os.h" #include "run.h" #include "dbg.h" #include "lst.h" #include "obj.h" #include "voc.h" #include "sup.h" /* forward declarations */ struct bifcxdef; /* * Create a new object */ static void run_new(runcxdef *ctx, uchar *noreg *codepp, objnum callobj, prpnum callprop) { objnum sc; objnum objn; objdef *objp; int sccnt; vocidef *voci; /* get the superclass (nil means no superclass) */ if (runtostyp(ctx) == DAT_NIL) sccnt = 0; else { /* get the superclass */ sc = runpopobj(ctx); sccnt = 1; /* make sure it's not a dynamically-allocated object */ voci = vocinh(ctx->runcxvoc, sc); if (voci->vociflg & VOCIFNEW) runsig(ctx, ERR_BADNEWSC); } /* create a new object and set its superclass */ objp = objnew(ctx->runcxmem, sccnt, 64, &objn, FALSE); if (sccnt) oswp2(objsc(objp), sc); /* save undo for the object creation */ vocdusave_newobj(ctx->runcxvoc, objn); /* touch and unlock the object */ mcmtch(ctx->runcxmem, (mcmon)objn); mcmunlck(ctx->runcxmem, (mcmon)objn); /* add a vocabulary inheritance record for the new object */ vociadd(ctx->runcxvoc, objn, MCMONINV, sccnt, &sc, VOCIFNEW | VOCIFVOC); /* set up its vocabulary, inheriting from the class */ if (sccnt) supivoc1((struct supcxdef *)0, ctx->runcxvoc, vocinh(ctx->runcxvoc, objn), objn, TRUE, VOCFNEW); /* run the constructor */ runpprop(ctx, codepp, callobj, callprop, objn, PRP_CONSTRUCT, FALSE, 0, objn); #ifdef NEVER /* * add it to its location's contents list by calling * newobj.moveInto(newobj.location) */ runppr(ctx, objn, PRP_LOCATION, 0); if (runtostyp(ctx) == DAT_OBJECT) runppr(ctx, objn, PRP_MOVEINTO, 1); else rundisc(ctx); #endif /* return the new object */ runpobj(ctx, objn); } /* * Delete an object */ static void run_delete(runcxdef *ctx, uchar *noreg *codepp, objnum callobj, prpnum callprop) { objnum objn; vocidef *voci; int i; voccxdef *vctx = ctx->runcxvoc; /* get the object to be deleted */ objn = runpopobj(ctx); /* make sure it was allocated with "new" */ voci = vocinh(vctx, objn); if (voci == 0 || !(voci->vociflg & VOCIFNEW)) runsig(ctx, ERR_BADDEL); /* run the destructor */ runpprop(ctx, codepp, callobj, callprop, objn, PRP_DESTRUCT, FALSE, 0, objn); #ifdef NEVER /* remove it from its location, if any, by using moveInto(nil) */ runpnil(ctx); runppr(ctx, objn, PRP_MOVEINTO, 1); #endif /* save undo for the object deletion */ vocdusave_delobj(vctx, objn); /* delete the object's inheritance and vocabulary records */ vocdel(vctx, objn); vocidel(vctx, objn); /* forget 'it' if the deleted object is 'it' (or 'them', etc) */ if (vctx->voccxit == objn) vctx->voccxit = MCMONINV; if (vctx->voccxhim == objn) vctx->voccxhim = MCMONINV; if (vctx->voccxher == objn) vctx->voccxher = MCMONINV; for (i = 0 ; i < vctx->voccxthc ; ++i) { if (vctx->voccxthm[i] == objn) { /* forget the entire 'them' list when deleting from it */ vctx->voccxthc = 0; break; } } /* forget the 'again' statistics if necessary */ if (vctx->voccxlsd.vocolobj == objn || vctx->voccxlsi.vocolobj == objn || vctx->voccxlsa == objn || vctx->voccxlsv == objn || vctx->voccxlsp == objn) { /* forget the verb */ vctx->voccxlsv = MCMONINV; /* * note in the flags why we lost the "again" verb, for better * error reporting if the player tries to type "again" */ vctx->voccxflg |= VOCCXAGAINDEL; } /* delete the memory manager object */ mcmfre(ctx->runcxmem, (mcmon)objn); } /* * invoke a function */ void runfn(runcxdef *ctx, noreg objnum objn, int argc) { uchar *fn; int err; NOREG((&objn)) /* get a lock on the object */ fn = mcmlck(ctx->runcxmem, objn); /* catch any errors, so we can unlock the object */ ERRBEGIN(ctx->runcxerr) /* execute the object */ runexe(ctx, fn, MCMONINV, objn, (prpnum)0, argc); /* in case of error, unlock the object and resignal the error */ ERRCATCH(ctx->runcxerr, err) mcmunlck(ctx->runcxmem, objn); /* release the lock on the object */ if (err < ERR_RUNEXIT || err > ERR_RUNEXITOBJ) dbgdump(ctx->runcxdbg); /* dump the stack */ errrse(ctx->runcxerr); ERREND(ctx->runcxerr) /* we're done with the object, so unlock it */ mcmunlck(ctx->runcxmem, objn); } /* * compress the heap - remove unreferenced items */ void runhcmp(runcxdef *ctx, uint siz, uint below, runsdef *val1, runsdef *val2, runsdef *val3) { uchar *hp = ctx->runcxheap; uchar *htop = ctx->runcxhp; runsdef *stop = ctx->runcxsp + below; runsdef *stk = ctx->runcxstk; runsdef *sp; uchar *dst = hp; uchar *hnxt; int ref; /* go through heap, finding references on stack */ for ( ; hp < htop ; hp = hnxt) { hnxt = hp + osrp2(hp); /* remember next heap element */ for (ref = FALSE, sp = stk ; sp < stop ; ++sp) { switch(sp->runstyp) { case DAT_SSTRING: case DAT_LIST: if (sp->runsv.runsvstr == hp) /* reference to this item? */ { ref = TRUE; /* this heap item is referenced */ sp->runsv.runsvstr = dst; /* reflect imminent move */ } break; default: /* other types do not refer to the heap */ break; } } /* check the explicitly referenced value pointers as well */ #define CHECK_VAL(val) \ if (val && val->runsv.runsvstr == hp) \ ref = TRUE, val->runsv.runsvstr = dst; CHECK_VAL(val1); CHECK_VAL(val2); CHECK_VAL(val3); #undef CHECK_VAL /* if referenced, copy it to dst and advance dst */ if (ref) { if (hp != dst) memmove(dst, hp, (size_t)osrp2(hp)); dst += osrp2(dst); } } /* set heap pointer based on shuffled heap */ ctx->runcxhp = dst; /* check for space requested, and signal error if not available */ if ((uint)(ctx->runcxhtop - ctx->runcxhp) < siz) runsig(ctx, ERR_HPOVF); } /* * push a value onto the stack that's already been allocated in heap */ void runrepush(runcxdef *ctx, runsdef *val) { /* check for stack overflow */ runstkovf(ctx); OSCPYSTRUCT(*(ctx->runcxsp), *val); /* increment stack pointer */ ++(ctx->runcxsp); } /* push a counted-length string onto the stack */ void runpstr(runcxdef *ctx, char *str, int len, int sav) { runsdef val; /* allocate space and set up new string */ runhres(ctx, len+2, sav); oswp2(ctx->runcxhp, len+2); memcpy(ctx->runcxhp+2, str, (size_t)len); /* push return value */ val.runsv.runsvstr = ctx->runcxhp; val.runstyp = DAT_SSTRING; ctx->runcxhp += len + 2; runrepush(ctx, &val); } /* push a C-style string, converting escape codes */ void runpushcstr(runcxdef *ctx, char *str, size_t len, int sav) { char *p; char *dst; size_t need; runsdef val; /* determine how much space we'll need after converting escapes */ for (p = str, need = len ; p < str + len ; ++p) { switch(*p) { case '\\': case '\n': case '\r': case '\t': /* these characters need to be escaped */ ++need; break; default: break; } } /* reserve space */ runhres(ctx, need + 2, sav); /* set up the length prefix */ oswp2(ctx->runcxhp, need + 2); /* copy the string, expanding escapes */ for (p = str, dst = (char *)ctx->runcxhp + 2 ; p < str + len ; ++p) { switch(*p) { case '\\': *dst++ = '\\'; *dst++ = '\\'; break; case '\n': case '\r': *dst++ = '\\'; *dst++ = 'n'; break; case '\t': *dst++ = '\\'; *dst++ = '\t'; break; default: *dst++ = *p; break; } } /* push the return value */ val.runsv.runsvstr = ctx->runcxhp; val.runstyp = DAT_SSTRING; ctx->runcxhp += need + 2; runrepush(ctx, &val); } /* push a value onto the stack */ void runpush(runcxdef *ctx, dattyp typ, runsdef *val) { int len; /* check for stack overflow */ runstkovf(ctx); OSCPYSTRUCT(*(ctx->runcxsp), *val); ctx->runcxsp->runstyp = typ; /* variable-length data must be copied into the heap */ if (typ == DAT_SSTRING || typ == DAT_LIST) { len = osrp2(val->runsv.runsvstr); runhres(ctx, len, 0); /* reserve space in heap */ memcpy(ctx->runcxhp, val->runsv.runsvstr, (size_t)len); ctx->runcxsp->runsv.runsvstr = ctx->runcxhp; ctx->runcxhp += len; } /* increment stack pointer */ ++(ctx->runcxsp); } /* push a number onto the stack */ void runpnum(runcxdef *ctx, long num) { runsdef val; val.runsv.runsvnum = num; runpush(ctx, DAT_NUMBER, &val); } /* push an object onto the stack (or nil if obj is MCMONINV) */ void runpobj(runcxdef *ctx, objnum obj) { runsdef val; if (obj == MCMONINV) runpnil(ctx); else { val.runsv.runsvobj = obj; runpush(ctx, DAT_OBJECT, &val); } } /* push nil */ void runpnil(runcxdef *ctx) { runsdef val; runpush(ctx, DAT_NIL, &val); } /* copy datatype + value from a runsdef into a buffer (such as list) */ static void runputbuf(uchar *dstp, runsdef *val) { *dstp++ = val->runstyp; switch(val->runstyp) { case DAT_LIST: case DAT_SSTRING: memcpy(dstp, val->runsv.runsvstr, (size_t)osrp2(val->runsv.runsvstr)); break; case DAT_NUMBER: oswp4s(dstp, val->runsv.runsvnum); break; case DAT_PROPNUM: oswp2(dstp, val->runsv.runsvprp); break; case DAT_OBJECT: case DAT_FNADDR: oswp2(dstp, val->runsv.runsvobj); break; } } /* push a value from a buffer (list, property, etc) onto stack */ void runpbuf(runcxdef *ctx, int typ, void *valp) { runsdef val; switch(typ) { case DAT_NUMBER: val.runsv.runsvnum = osrp4s(valp); break; case DAT_OBJECT: case DAT_FNADDR: val.runsv.runsvobj = osrp2(valp); break; case DAT_PROPNUM: val.runsv.runsvprp = osrp2(valp); break; case DAT_SSTRING: case DAT_LIST: val.runsv.runsvstr = (uchar *)valp; break; case DAT_NIL: case DAT_TRUE: break; } runpush(ctx, typ, &val); } /* compare items at top of stack for equality; TRUE->equal, FALSE->unequal */ int runeq(runcxdef *ctx) { runsdef val1, val2; /* get values, and see if they have identical type; not equal if not */ runpop(ctx, &val1); runpop(ctx, &val2); if (val1.runstyp != val2.runstyp) return(FALSE); /* types match, so check values */ switch(val1.runstyp) { case DAT_NUMBER: return(val1.runsv.runsvnum == val2.runsv.runsvnum); case DAT_SSTRING: case DAT_LIST: return(osrp2(val1.runsv.runsvstr) == osrp2(val2.runsv.runsvstr) && !memcmp(val1.runsv.runsvstr, val2.runsv.runsvstr, (size_t)osrp2(val1.runsv.runsvstr))); case DAT_PROPNUM: return(val1.runsv.runsvprp == val2.runsv.runsvprp); case DAT_OBJECT: case DAT_FNADDR: return(val1.runsv.runsvobj == val2.runsv.runsvobj); default: return(TRUE); } } /* compare magnitudes of numbers/strings at top of stack; strcmp-like value */ int runmcmp(runcxdef *ctx) { if (runtostyp(ctx) == DAT_NUMBER) { long num2 = runpopnum(ctx); long num1 = runpopnum(ctx); if (num1 > num2) return(1); else if (num1 < num2) return(-1); else return(0); } else if (runtostyp(ctx) == DAT_SSTRING) { uchar *str2 = runpopstr(ctx); uchar *str1 = runpopstr(ctx); uint len1 = osrp2(str1) - 2; uint len2 = osrp2(str2) - 2; str1 += 2; str2 += 2; while (len1 && len2) { if (*str1 < *str2) return(-1); /* character from 1 is greater */ else if (*str1 > *str2) return(1); /* char from 1 is less */ ++str1; ++str2; --len1; --len2; } if (len1) return(1); /* match up to len2, but string 1 is longer */ else if (len2) return(-1); /* match up to len1, but str2 is longer */ else return(0); /* strings are identical */ } else { runsig(ctx, ERR_INVCMP); } return 0; } /* determine size of a runsdef item */ int runsiz(runsdef *item) { switch(item->runstyp) { case DAT_NUMBER: return(4); case DAT_SSTRING: case DAT_LIST: return(osrp2(item->runsv.runsvstr)); case DAT_PROPNUM: case DAT_OBJECT: case DAT_FNADDR: return(2); default: return(0); } } /* find a sublist within a list */ uchar *runfind(uchar *lst, runsdef *item) { uint len; uint curlen; for (len = osrp2(lst) - 2, lst += 2 ; len ; lst += curlen, len -= curlen) { if (*lst == item->runstyp) { switch(*lst) { case DAT_LIST: case DAT_SSTRING: if (osrp2(lst+1) == osrp2(item->runsv.runsvstr) && !memcmp(lst+1, item->runsv.runsvstr, (size_t)osrp2(lst+1))) return(lst); break; case DAT_NUMBER: if (osrp4s(lst+1) == item->runsv.runsvnum) return(lst); break; case DAT_TRUE: case DAT_NIL: return(lst); case DAT_OBJECT: case DAT_FNADDR: if (osrp2(lst+1) == item->runsv.runsvobj) return(lst); break; case DAT_PROPNUM: if (osrp2(lst+1) == item->runsv.runsvprp) return(lst); break; } } curlen = datsiz(*lst, lst+1) + 1; } return((uchar *)0); } /* add values */ void runadd(runcxdef *ctx, runsdef *val, runsdef *val2, uint below) { if (val->runstyp == DAT_LIST) { int len1 = osrp2(val->runsv.runsvstr); int len2 = runsiz(val2); int newlen; /* if concatenating a list, take out length + datatype from 2nd */ if (val2->runstyp == DAT_LIST) newlen = len1 + len2 - 2; /* leave out second list len */ else newlen = len1 + len2 + 1; /* add in datatype header */ /* get space in heap, copy first list, and set new length */ runhres2(ctx, newlen, below, val, val2); memcpy(ctx->runcxhp, val->runsv.runsvstr, (size_t)len1); oswp2(ctx->runcxhp, newlen); /* append the new element or list of elements */ if (val2->runstyp == DAT_LIST) memcpy(ctx->runcxhp + len1, val2->runsv.runsvstr + 2, (size_t)(len2 - 2)); else runputbuf(ctx->runcxhp + len1, val2); /* set up return value and update heap pointer */ val->runsv.runsvstr = ctx->runcxhp; ctx->runcxhp += newlen; } else if (val->runstyp==DAT_SSTRING && val2->runstyp==DAT_SSTRING) { int len1 = osrp2(val->runsv.runsvstr); int len2 = osrp2(val2->runsv.runsvstr); /* reserve space, and concatenate the two strings */ runhres2(ctx, len1 + len2 - 2, below, val, val2); memcpy(ctx->runcxhp, val->runsv.runsvstr, (size_t)len1); memcpy(ctx->runcxhp + len1, val2->runsv.runsvstr + 2, (size_t)len2 - 2); /* set length to sum of two lengths, minus 2nd length word */ oswp2(ctx->runcxhp, len1 + len2 - 2); val->runsv.runsvstr = ctx->runcxhp; ctx->runcxhp += len1 + len2 - 2; } else if (val->runstyp == DAT_NUMBER && val2->runstyp == DAT_NUMBER) val->runsv.runsvnum += val2->runsv.runsvnum; else runsig(ctx, ERR_INVADD); } /* returns TRUE if value changed */ int runsub(runcxdef *ctx, runsdef *val, runsdef *val2, uint below) { if (val->runstyp == DAT_LIST) { uchar *sublist; int subsize; int listsize; int part1sz; if (val2->runstyp == DAT_LIST) { uchar *p1; uchar *p2; uint rem1; uint rem2; uchar *dst; /* reserve space for another copy of first list */ listsize = runsiz(val); runhres2(ctx, listsize, below, val, val2); dst = ctx->runcxhp + 2; /* get pointer to first list */ p1 = val->runsv.runsvstr; rem1 = osrp2(p1) - 2; p1 += 2; /* * loop through left list, copying elements to output if * not in the right list */ for ( ; rem1 ; lstadv(&p1, &rem1)) { int found = FALSE; /* find current element of first list in second list */ p2 = val2->runsv.runsvstr; rem2 = osrp2(p2) - 2; p2 += 2; for ( ; rem2 ; lstadv(&p2, &rem2)) { if (*p1 == *p2) { int siz1 = datsiz(*p1, p1+1); int siz2 = datsiz(*p2, p2+1); if (siz1 == siz2 && (siz1 == 0 || !memcmp(p1+1, p2+1, (size_t)siz1))) { found = TRUE; break; } } } /* if this element wasn't found, copy to output list */ if (!found) { uint siz; *dst++ = *p1; if ((siz = datsiz(*p1, p1+1)) != 0) { memcpy(dst, p1+1, siz); dst += siz; } } } /* we've built the list; write size and we're done */ oswp2(ctx->runcxhp, dst - ctx->runcxhp); val->runsv.runsvstr = ctx->runcxhp; ctx->runcxhp = dst; } else if ((sublist = runfind(val->runsv.runsvstr, val2)) != 0) { subsize = datsiz(*sublist, sublist + 1) + 1; listsize = runsiz(val); part1sz = sublist - (uchar *)val->runsv.runsvstr; runhres2(ctx, listsize - subsize, below, val, val2); memcpy(ctx->runcxhp, val->runsv.runsvstr, (size_t)part1sz); memcpy(ctx->runcxhp + part1sz, sublist + subsize, (size_t)(listsize - subsize - part1sz)); oswp2(ctx->runcxhp, listsize - subsize); val->runsv.runsvstr = ctx->runcxhp; ctx->runcxhp += listsize - subsize; } else { return(FALSE); /* no change - value can be re-pushed */ } } else if (val->runstyp == DAT_NUMBER && val2->runstyp == DAT_NUMBER) val->runsv.runsvnum -= val2->runsv.runsvnum; else runsig(ctx, ERR_INVSUB); return(TRUE); /* value has changed; must be pushed anew */ } /* return code pointer offset */ static uint runcpsav(runcxdef *ctx, uchar *noreg *cp, objnum obj, prpnum prop) { uint ofs; VARUSED(prop); /* get offset from start of object */ ofs = *cp - mcmobjptr(ctx->runcxmem, (mcmon)obj); /* clear the pointer so the caller knows the object is unlocked */ *cp = 0; /* unlock the object, and return the derived offset */ mcmunlck(ctx->runcxmem, (mcmon)obj); return(ofs); } /* restore code pointer based on object.property */ uchar *runcprst(runcxdef *ctx, uint ofs, objnum obj, prpnum prop) { uchar *ptr; VARUSED(prop); /* lock object, and get pointer based on offset */ ptr = mcmlck(ctx->runcxmem, (mcmon)obj) + ofs; return(ptr); } /* get offset of an element within a list */ static uint runindofs(runcxdef *ctx, uint indx, uchar *lstp) { uint lstsiz; uchar *orgp = lstp; /* verify that index is in range */ if (indx <= 0) runsig(ctx, ERR_LOWINX); /* get list's size, and point to its data string */ lstsiz = osrp2(lstp) - 2; lstp += 2; /* skip the first indx-1 elements */ for (--indx ; indx && lstsiz ; --indx) lstadv(&lstp, &lstsiz); /* if we ran out of list, the index is out of range */ if (!lstsiz) runsig(ctx, ERR_HIGHINX); /* return the offset */ return((uint)(lstp - orgp)); } /* push an indexed element of a list; index is tos, list is next on stack */ static void runpind(runcxdef *ctx, uint indx, uchar *lstp) { uchar *ele; runsdef val; /* find the element we want to push */ ele = lstp + runindofs(ctx, indx, lstp); /* reserve space first, in case lstp gets moved around */ val.runstyp = DAT_LIST; val.runsv.runsvstr = lstp; runhres1(ctx, datsiz(*ele, ele + 1), 0, &val); if (val.runsv.runsvstr != lstp) ele = val.runsv.runsvstr + runindofs(ctx, indx, val.runsv.runsvstr); /* push the operand */ runpbuf(ctx, *ele, ele+1); } /* * Check a property to ensure that it's a data property. Throws an * error if the property contains a method. This is used for debugger * speculative evaluation to ensure that we don't call any methods from * within speculative expressions. */ static void runcheckpropdata(runcxdef *ctx, objnum obj, prpnum prop) { uint pofs; objnum target; objdef *objptr; prpdef *prpptr; int typ; /* if the object is invalid, it's an error */ if (obj == MCMONINV) errsig(ctx->runcxerr, ERR_REQVOB); /* get the property */ pofs = objgetap(ctx->runcxmem, obj, prop, &target, FALSE); /* if there's no property, it's okay - it will just return nil */ if (pofs == 0) return; /* get the object */ objptr = mcmlck(ctx->runcxmem, target); /* get the property */ prpptr = (prpdef *)(((uchar *)objptr) + pofs); typ = prptype(prpptr); /* we're done with the object's memory now */ mcmunlck(ctx->runcxmem, target); /* check the type */ switch(typ) { case DAT_CODE: case DAT_DSTRING: /* * we can't call code or evaluate (i.e., print) double-quoted * strings during speculative evaluation */ errsig(ctx->runcxerr, ERR_RTBADSPECEXPR); default: /* other types do not involve method calls, so they're okay */ break; } } /* push an object's property */ void runpprop(runcxdef *ctx, uchar *noreg *codepp, objnum callobj, prpnum callprop, noreg objnum obj, prpnum prop, int inh, int argc, objnum self) { uint pofs; uint saveofs; objdef *objptr; prpdef *prpptr; uchar *val; int typ; runsdef sval; objnum target; int times_through = 0; int err; objnum otherobj; NOREG((&obj, &codepp)); if (obj == MCMONINV) runsig(ctx, ERR_RUNNOBJ); startover: pofs = objgetap(ctx->runcxmem, obj, prop, &target, inh); /* if nothing was found, push nil */ if (!pofs) { runpush(ctx, DAT_NIL, &sval); return; } /* found a property; get the prpdef, and the value and type of data */ objptr = mcmlck(ctx->runcxmem, target); ERRBEGIN(ctx->runcxerr) /* catch errors so we can unlock object */ prpptr = (prpdef *)(((uchar *)objptr) + pofs); val = prpvalp(prpptr); typ = prptype(prpptr); /* determine what to do based on property type */ switch(typ) { case DAT_CODE: /* save caller's code offset - caller's object may move */ if (codepp) saveofs = runcpsav(ctx, codepp, callobj, callprop); /* execute the code */ runexe(ctx, val, self, target, prop, argc); /* restore caller's code pointer in case object moved */ if (codepp) *codepp = runcprst(ctx, saveofs, callobj, callprop); break; case DAT_REDIR: otherobj = osrp2(val); break; case DAT_DSTRING: outfmt(ctx->runcxtio, val); break; case DAT_DEMAND: break; default: runpbuf(ctx, typ, val); break; } /* we're done - unlock the object */ mcmunlck(ctx->runcxmem, target); /* if it's redirected, redirect it now */ if (typ == DAT_REDIR) { runpprop(ctx, codepp, callobj, callprop, otherobj, prop, FALSE, argc, otherobj); } /* if an error occurs, unlock the object, and resignal the error */ ERRCATCH(ctx->runcxerr, err) mcmunlck(ctx->runcxmem, target); if (err < ERR_RUNEXIT || err > ERR_RUNEXITOBJ) dbgdump(ctx->runcxdbg); /* dump the stack */ errrse(ctx->runcxerr); ERREND(ctx->runcxerr) /* apply special handling for set-on-first-use data */ if (typ == DAT_DEMAND) { /* * if we've already done this, the property isn't being set by * the callback, so we'll never get out of this loop - abort if * so */ if (++times_through != 1) runsig(ctx, ERR_DMDLOOP); /* save caller's code offset - caller's object may move */ if (codepp) saveofs = runcpsav(ctx, codepp, callobj, callprop); /* invoke the callback to set the property on demand */ (*ctx->runcxdmd)(ctx->runcxdmc, obj, prop); /* restore caller's code pointer */ if (codepp) *codepp = runcprst(ctx, saveofs, callobj, callprop); /* try again now that it's been set up */ goto startover; } } /* ======================================================================== */ /* * user exit callbacks */ static int runuftyp(runuxdef *ctx) { return(runtostyp(ctx->runuxctx)); } static long runufnpo(runuxdef *ctx) { return(runpopnum(ctx->runuxctx)); } static uchar *runufspo(runuxdef *ctx) { return(runpopstr(ctx->runuxctx)); } static void runufdsc(runuxdef *ctx) { rundisc(ctx->runuxctx); } static void runufnpu(runuxdef *ctx, long num) { runpnum(ctx->runuxctx, num); } static void runufspu(runuxdef *ctx, uchar *str) { runsdef val; val.runstyp = DAT_SSTRING; val.runsv.runsvstr = str - 2; runrepush(ctx->runuxctx, &val); } static void runufcspu(runuxdef *ctx, char *str) { runpstr(ctx->runuxctx, str, (int)strlen(str), ctx->runuxargc); } static uchar *runufsal(runuxdef *ctx, int len) { uchar *ret; len += 2; runhres(ctx->runuxctx, len, ctx->runuxargc); ret = ctx->runuxctx->runcxhp; oswp2(ret, len); ret += 2; ctx->runuxctx->runcxhp += len; return(ret); } static void runuflpu(runuxdef *ctx, int typ) { runsdef val; val.runstyp = typ; runrepush(ctx->runuxctx, &val); } /* convert an osrp2 value to a signed short value */ #define runrp2s(p) ((short)(ushort)osrp2(p)) /* ======================================================================== */ /* * execute p-code */ void runexe(runcxdef *ctx, uchar *p0, objnum self, objnum target, prpnum targprop, int argc) { uchar *noreg p = p0; uchar opc; /* opcode we're currently working on */ runsdef val; /* stack element (for pushing) */ runsdef val2; /* another one (for popping in two-op instructions) */ uint ofs; /* offset in code of current execution */ prpnum prop; /* property number, when needed */ objnum obj; /* object number, when needed */ runsdef *noreg rstsp; /* sp to reset to on DISCARD instructions */ uchar *lstp; /* list pointer */ int nargc; /* argument count of called function */ runsdef *valp; runsdef *stkval; int i; int brkchk; #ifndef DBG_OFF int err; #endif NOREG((&rstp, &p)); /* save entry SP - this is reset point until ENTER */ rstsp = ctx->runcxsp; #ifndef DBG_OFF /* * For the debugger's sake, set up an error frame so that we catch * any errors thrown during p-code execution within this function. * If an error occurs, and the debugger is present, we'll set the * instruction pointer back to the start of the line that caused the * error and enter the debugger with the error indication. If the * debugger isn't present, we'll simply re-throw the error. This * entire block can be compiled out of the execution engine when * linking a stand-alone (non-debug) version of the run-time. */ resume_from_error: ERRBEGIN(ctx->runcxerr) #endif /* DBG_OFF */ for (brkchk = 0 ;; ++brkchk) { /* check for break - signal if user has hit break */ if (brkchk == 1000) { brkchk = 0; if (os_break()) runsig(ctx, ERR_USRINT); } opc = *p++; switch(opc) { case OPCPUSHNUM: val.runsv.runsvnum = osrp4s(p); runpush(ctx, DAT_NUMBER, &val); p += 4; break; case OPCPUSHOBJ: val.runsv.runsvobj = osrp2(p); runpush(ctx, DAT_OBJECT, &val); p += 2; break; case OPCPUSHSELF: val.runsv.runsvobj = self; runpush(ctx, DAT_OBJECT, &val); break; case OPCPUSHSTR: val.runsv.runsvstr = p; runpush(ctx, DAT_SSTRING, &val); p += osrp2(p); /* skip past string */ break; case OPCPUSHLST: val.runsv.runsvstr = p; runpush(ctx, DAT_LIST, &val); p += osrp2(p); /* skip past list */ break; case OPCPUSHNIL: runpush(ctx, DAT_NIL, &val); break; case OPCPUSHTRUE: runpush(ctx, DAT_TRUE, &val); break; case OPCPUSHFN: val.runsv.runsvobj = osrp2(p); runpush(ctx, DAT_FNADDR, &val); p += 2; break; case OPCPUSHPN: val.runsv.runsvprp = osrp2(p); runpush(ctx, DAT_PROPNUM, &val); p += 2; break; case OPCNEG: val.runstyp = DAT_NUMBER; val.runsv.runsvnum = -runpopnum(ctx); runrepush(ctx, &val); break; case OPCBNOT: val.runstyp = DAT_NUMBER; val.runsv.runsvnum = ~runpopnum(ctx); runrepush(ctx, &val); break; case OPCNOT: if (runtoslog(ctx)) runpush(ctx, runclog(!runpoplog(ctx)), &val); else runpush(ctx, runclog(runpopnum(ctx)), &val); break; case OPCADD: runpop(ctx, &val2); /* right op is pushed last -> popped 1st */ runpop(ctx, &val); runadd(ctx, &val, &val2, 2); runrepush(ctx, &val); break; case OPCSUB: runpop(ctx, &val2); /* right op is pushed last -> popped 1st */ runpop(ctx, &val); (void)runsub(ctx, &val, &val2, 2); runrepush(ctx, &val); break; case OPCMUL: val.runstyp = DAT_NUMBER; val.runsv.runsvnum = runpopnum(ctx); val.runsv.runsvnum *= runpopnum(ctx); runrepush(ctx, &val); break; case OPCBAND: val.runstyp = DAT_NUMBER; val.runsv.runsvnum = runpopnum(ctx); val.runsv.runsvnum &= runpopnum(ctx); runrepush(ctx, &val); break; case OPCBOR: val.runstyp = DAT_NUMBER; val.runsv.runsvnum = runpopnum(ctx); val.runsv.runsvnum |= runpopnum(ctx); runrepush(ctx, &val); break; case OPCSHL: val.runstyp = DAT_NUMBER; val.runsv.runsvnum = runpopnum(ctx); val.runsv.runsvnum = runpopnum(ctx) << val.runsv.runsvnum; runrepush(ctx, &val); break; case OPCSHR: val.runstyp = DAT_NUMBER; val.runsv.runsvnum = runpopnum(ctx); val.runsv.runsvnum = runpopnum(ctx) >> val.runsv.runsvnum; runrepush(ctx, &val); break; case OPCXOR: /* allow logical ^ logical or number ^ number */ if (runtoslog(ctx)) { int a, b; /* logicals - return a logical value */ a = runpoplog(ctx); b = runpoplog(ctx); val.runstyp = runclog(a ^ b); } else { /* numeric value - return binary xor */ val.runstyp = DAT_NUMBER; val.runsv.runsvnum = runpopnum(ctx); val.runsv.runsvnum ^= runpopnum(ctx); } runrepush(ctx, &val); break; case OPCDIV: val.runsv.runsvnum = runpopnum(ctx); if (val.runsv.runsvnum == 0) runsig(ctx, ERR_DIVZERO); val.runsv.runsvnum = runpopnum(ctx) / val.runsv.runsvnum; val.runstyp = DAT_NUMBER; runrepush(ctx, &val); break; case OPCMOD: val.runsv.runsvnum = runpopnum(ctx); if (val.runsv.runsvnum == 0) runsig(ctx, ERR_DIVZERO); val.runsv.runsvnum = runpopnum(ctx) % val.runsv.runsvnum; val.runstyp = DAT_NUMBER; runrepush(ctx, &val); break; #ifdef NEVER case OPCAND: if (runtostyp(ctx) == DAT_LIST) runlstisect(ctx); else runpush(ctx, runclog(runpoplog(ctx) && runpoplog(ctx)), &val); break; case OPCOR: runpush(ctx, runclog(runpoplog(ctx) || runpoplog(ctx)), &val); break; #endif /* NEVER */ case OPCEQ: runpush(ctx, runclog(runeq(ctx)), &val); break; case OPCNE: runpush(ctx, runclog(!runeq(ctx)), &val); break; case OPCLT: runpush(ctx, runclog(runmcmp(ctx) < 0), &val); break; case OPCLE: runpush(ctx, runclog(runmcmp(ctx) <= 0), &val); break; case OPCGT: runpush(ctx, runclog(runmcmp(ctx) > 0), &val); break; case OPCGE: runpush(ctx, runclog(runmcmp(ctx) >= 0), &val); break; case OPCCALL: { objnum o; /* get the argument count */ nargc = *p++; /* ensure we have enough values to pass as arguments */ runcheckargc(ctx, &nargc); /* object could move--save offset to restore 'p' after call */ o = osrp2(p); ofs = runcpsav(ctx, &p, target, targprop); /* execute the function */ runfn(ctx, o, nargc); /* restore code pointer in case target object moved */ p = runcprst(ctx, ofs, target, targprop) + 2; break; } case OPCGETP: nargc = *p++; runcheckargc(ctx, &nargc); prop = osrp2(p); p += 2; obj = runpopobj(ctx); runpprop(ctx, &p, target, targprop, obj, prop, FALSE, nargc, obj); break; case OPCGETPDATA: prop = osrp2(p); p += 2; obj = runpopobj(ctx); runcheckpropdata(ctx, obj, prop); runpprop(ctx, &p, target, targprop, obj, prop, FALSE, 0, obj); break; case OPCGETDBLCL: #ifdef DBG_OFF /* non-debug mode - this will always throw an error */ dbgfrfind(ctx->runcxdbg, 0, 0); #else /* debug mode - look up the local in the stack frame */ { objnum frobj; uint frofs; runsdef *otherbp; frobj = osrp2(p); frofs = osrp2(p + 2); otherbp = dbgfrfind(ctx->runcxdbg, frobj, frofs); runrepush(ctx, otherbp + runrp2s(p + 4) - 1); p += 6; } break; #endif case OPCGETLCL: runrepush(ctx, ctx->runcxbp + runrp2s(p) - 1); p += 2; break; case OPCRETURN: runleave(ctx, argc /* was: osrp2(p) */); dbgleave(ctx->runcxdbg, DBGEXRET); goto done; case OPCRETVAL: /* if there's nothing on the stack, return nil */ if (runtostyp(ctx) != DAT_BASEPTR) runpop(ctx, &val); else val.runstyp = DAT_NIL; runleave(ctx, argc /* was: osrp2(p) */); runrepush(ctx, &val); dbgleave(ctx->runcxdbg, DBGEXVAL); goto done; case OPCENTER: /* push old base pointer and set up new one */ ctx->runcxsp = rstsp; val.runsv.runsvstr = (uchar *)ctx->runcxbp; runpush(ctx, DAT_BASEPTR, &val); ctx->runcxbp = ctx->runcxsp; /* add a trace record */ dbgenter(ctx->runcxdbg, ctx->runcxbp, self, target, targprop, 0, argc); /* initialize locals to nil */ for (i = osrp2(p) ; i ; --i) runpush(ctx, DAT_NIL, &val); p += 2; /* skip the local count operand */ /* save stack pointer - reset sp to this value on DISCARD */ rstsp = ctx->runcxsp; break; case OPCDISCARD: ctx->runcxsp = rstsp; break; case OPCSWITCH: { int i; int tostyp; int match, typmatch; runpop(ctx, &val); tostyp = val.runstyp; switch(tostyp) { case DAT_SSTRING: tostyp = OPCPUSHSTR; break; case DAT_LIST: tostyp = OPCPUSHLST; break; case DAT_PROPNUM: tostyp = OPCPUSHPN; break; case DAT_FNADDR: tostyp = OPCPUSHFN; break; case DAT_TRUE: tostyp = OPCPUSHTRUE; break; case DAT_NIL: tostyp = OPCPUSHNIL; break; } p += osrp2(p); /* find the switch table */ i = osrp2(p); /* get number of cases */ /* look for a matching case */ for (match = FALSE ; i && !match ; --i) { p += 2; /* skip previous jump/size word */ typmatch = (*p == tostyp); switch(*p++) { case OPCPUSHNUM: match = (typmatch && val.runsv.runsvnum == osrp4s(p)); p += 4; break; case OPCPUSHLST: case OPCPUSHSTR: match = (typmatch && osrp2(val.runsv.runsvstr) == osrp2(p) && !memcmp(val.runsv.runsvstr, p, (size_t)osrp2(p))); p += runrp2s(p); break; case OPCPUSHPN: match = (typmatch && val.runsv.runsvprp == osrp2(p)); p += 2; break; case OPCPUSHOBJ: case OPCPUSHFN: match = (typmatch && val.runsv.runsvobj == osrp2(p)); p += 2; break; case OPCPUSHSELF: match = (typmatch && val.runsv.runsvobj == self); break; case OPCPUSHTRUE: case OPCPUSHNIL: match = typmatch; break; } } if (!match) p += 2; /* if default, skip to default case */ p += runrp2s(p); /* wherever we left off, p points to jump */ break; } case OPCJMP: p += runrp2s(p); break; case OPCJT: if (runtoslog(ctx)) p += (runpoplog(ctx) ? runrp2s(p) : 2); else p += (runpopnum(ctx) != 0 ? runrp2s(p) : 2); break; case OPCJF: if (runtoslog(ctx)) p += ((!runpoplog(ctx)) ? runrp2s(p) : 2); else if (runtostyp(ctx) == DAT_NUMBER) p += ((runpopnum(ctx) == 0) ? runrp2s(p) : 2); else /* consider any other type to be true */ { rundisc(ctx); /* throw away the item considered to be true */ p += 2; } break; case OPCSAY: outfmt(ctx->runcxtio, p); p += osrp2(p); /* skip past string */ break; case OPCBUILTIN: { int binum; runsdef *stkp; nargc = *p++; runcheckargc(ctx, &nargc); binum = osrp2(p); ofs = runcpsav(ctx, &p, target, targprop); stkp = ctx->runcxsp - nargc; dbgenter(ctx->runcxdbg, ctx->runcxsp + 1, MCMONINV, MCMONINV, (prpnum)0, binum, nargc); (*ctx->runcxbi[binum])((struct bifcxdef *)ctx->runcxbcx, nargc); dbgleave(ctx->runcxdbg, ctx->runcxsp != stkp ? DBGEXVAL : DBGEXRET); p = runcprst(ctx, ofs, target, targprop); p += 2; break; } case OPCPTRCALL: nargc = *p++; runcheckargc(ctx, &nargc); ofs = runcpsav(ctx, &p, target, targprop); runfn(ctx, runpopfn(ctx), nargc); p = runcprst(ctx, ofs, target, targprop); break; case OPCINHERIT: nargc = *p++; runcheckargc(ctx, &nargc); prop = osrp2(p); p += 2; runpprop(ctx, &p, target, targprop, target, prop, TRUE, nargc, self); break; case OPCPTRINH: nargc = *p++; runcheckargc(ctx, &nargc); prop = runpopprp(ctx); runpprop(ctx, &p, target, targprop, target, prop, TRUE, nargc, self); break; case OPCPTRGETP: nargc = *p++; runcheckargc(ctx, &nargc); prop = runpopprp(ctx); obj = runpopobj(ctx); runpprop(ctx, &p, target, targprop, obj, prop, FALSE, nargc, obj); break; case OPCPTRGETPDATA: prop = runpopprp(ctx); obj = runpopobj(ctx); runcheckpropdata(ctx, obj, prop); runpprop(ctx, &p, target, targprop, obj, prop, FALSE, 0, obj); break; case OPCEXPINH: /* inheritance from explicit superclass */ nargc = *p++; runcheckargc(ctx, &nargc); prop = osrp2(p); obj = osrp2(p + 2); p += 4; /* * Evaluate the property of the given object, but keeping * the same 'self' as is currently in effect. Note that the * 'inherit' flag is FALSE in this call, even though we're * inheriting, because the opcode explicitly specifies the * object we want to inherit from. */ runpprop(ctx, &p, target, targprop, obj, prop, FALSE, nargc, self); break; case OPCEXPINHPTR: nargc = *p++; runcheckargc(ctx, &nargc); prop = runpopprp(ctx); obj = osrp2(p); p += 2; runpprop(ctx, &p, target, targprop, obj, prop, FALSE, nargc, self); break; case OPCPASS: prop = osrp2(p); runleave(ctx, 0); dbgleave(ctx->runcxdbg, DBGEXPASS); runpprop(ctx, &p, target, targprop, target, prop, TRUE, argc, self); goto done; case OPCEXIT: errsig(ctx->runcxerr, ERR_RUNEXIT); /* NOTREACHED */ case OPCABORT: errsig(ctx->runcxerr, ERR_RUNABRT); /* NOTREACHED */ case OPCASKDO: errsig(ctx->runcxerr, ERR_RUNASKD); /* NOTREACHED */ case OPCASKIO: errsig1(ctx->runcxerr, ERR_RUNASKI, ERRTINT, osrp2(p)); /* NOTREACHED */ case OPCJE: p += (runeq(ctx) ? runrp2s(p) : 2); break; case OPCJNE: p += (!runeq(ctx) ? runrp2s(p) : 2); break; case OPCJGT: p += (runmcmp(ctx) > 0 ? runrp2s(p) : 2); break; case OPCJGE: p += (runmcmp(ctx) >= 0 ? runrp2s(p) : 2); break; case OPCJLT: p += (runmcmp(ctx) < 0 ? runrp2s(p) : 2); break; case OPCJLE: p += (runmcmp(ctx) <= 0 ? runrp2s(p) : 2); break; case OPCJNAND: p += (!(runpoplog(ctx) && runpoplog(ctx)) ? runrp2s(p) : 2); break; case OPCJNOR: p += (!(runpoplog(ctx) || runpoplog(ctx)) ? runrp2s(p) : 2); break; case OPCGETPSELF: nargc = *p++; runcheckargc(ctx, &nargc); prop = osrp2(p); p += 2; runpprop(ctx, &p, target, targprop, self, prop, FALSE, nargc, self); break; case OPCGETPSELFDATA: prop = osrp2(p); p += 2; runcheckpropdata(ctx, self, prop); runpprop(ctx, &p, target, targprop, self, prop, FALSE, 0, self); break; case OPCGETPPTRSELF: nargc = *p++; runcheckargc(ctx, &nargc); prop = runpopprp(ctx); runpprop(ctx, &p, target, targprop, self, prop, FALSE, nargc, self); break; case OPCGETPOBJ: nargc = *p++; runcheckargc(ctx, &nargc); obj = osrp2(p); prop = osrp2(p+2); p += 4; runpprop(ctx, &p, target, targprop, obj, prop, FALSE, nargc, obj); break; case OPCINDEX: i = runpopnum(ctx); /* get index */ lstp = runpoplst(ctx); /* get the list */ runpind(ctx, i, lstp); break; case OPCJST: if (runtostyp(ctx) == DAT_TRUE) p += runrp2s(p); else { (void)runpoplog(ctx); p += 2; } break; case OPCJSF: if (runtostyp(ctx) == DAT_NIL || (runtostyp(ctx) == DAT_NUMBER && (ctx->runcxsp - 1)->runsv.runsvnum == 0)) p += runrp2s(p); else { runpop(ctx, &val); p += 2; } break; case OPCCALLEXT: { #if 0 // external functions are now obsolete static runufdef uf = { runuftyp, runufnpo, runufspo, runufdsc, runufnpu, runufspu, runufcspu, runufsal, runuflpu }; int fn; runxdef *ex; runuxdef ux; /* set up callback context */ ux.runuxctx = ctx; ux.runuxvec = &uf; ux.runuxargc = *p++; fn = osrp2(p); p += 2; ex = &ctx->runcxext[fn]; if (!ex->runxptr) { if ((ex->runxptr = os_exfil(ex->runxnam)) == 0) runsig1(ctx, ERR_EXTLOAD, ERRTSTR, ex->runxnam); } if (os_excall(ex->runxptr, &ux)) runsig1(ctx, ERR_EXTRUN, ERRTSTR, ex->runxnam); #else /* external functions are obsolete - throw an error */ runxdef *ex; p += 1; ex = &ctx->runcxext[osrp2(p)]; p += 2; runsig1(ctx, ERR_EXTRUN, ERRTSTR, ex->runxnam); #endif } break; case OPCDBGRET: goto done; case OPCCONS: { uint totsiz; uint oldsiz; uint tot; uint cursiz; runsdef lstend; tot = i = osrp2(p); /* get # of items to build into list */ p += 2; /* reserve space for initial list (w/length word only) */ runhres(ctx, 2, 0); /* * Set up value to point to output list, making room * for length prefix. Remember size-so-far separately. */ lstend.runstyp = DAT_LIST; lstend.runsv.runsvstr = ctx->runcxhp; ctx->runcxhp += 2; totsiz = 2; while (i--) { runpop(ctx, &val); /* get next value off stack */ cursiz = runsiz(&val); /* * Set up to allocate space. Before doing so, make * sure the list under construction is valid, to * ensure that it stays around after garbage * collection. */ oldsiz = totsiz; totsiz += cursiz + 1; oswp2(lstend.runsv.runsvstr, oldsiz); ctx->runcxhp = lstend.runsv.runsvstr + oldsiz; runhres2(ctx, cursiz + 1, tot - i, &val, &lstend); /* write this item to the list */ runputbuf(lstend.runsv.runsvstr + oldsiz, &val); } oswp2(lstend.runsv.runsvstr, totsiz); ctx->runcxhp = lstend.runsv.runsvstr + totsiz; runrepush(ctx, &lstend); } break; case OPCARGC: val.runsv.runsvnum = argc; runpush(ctx, DAT_NUMBER, &val); break; case OPCCHKARGC: if ((*p & 0x80) ? argc < (*p & 0x7f) : argc != *p) { char namebuf[128]; size_t namelen; /* * debugger is present - look up the name of the current * function or method, so that we can report it in the * error message */ if (targprop == 0) { /* we're in a function */ namelen = dbgnam(ctx->runcxdbg, namebuf, TOKSTFUNC, target); } else { /* we're in an object.method */ namelen = dbgnam(ctx->runcxdbg, namebuf, TOKSTOBJ, target); namebuf[namelen++] = '.'; namelen += dbgnam(ctx->runcxdbg, namebuf + namelen, TOKSTPROP, targprop); } namebuf[namelen] = '\0'; runsig1(ctx, ERR_ARGC, ERRTSTR, namebuf); } ++p; break; case OPCLINE: case OPCBP: { uchar *ptr = mcmobjptr(ctx->runcxmem, (mcmon)target); uint ofs; uchar instr; /* set up the debugger frame record for this line */ dbgframe(ctx->runcxdbg, osrp2(p+1), p - ptr); /* remember the instruction */ instr = *(p-1); /* remember the offset of the line record */ ctx->runcxlofs = ofs = (p + 2 - ptr); /* skip to the next instruction */ p += *p; /* let the debugger take over, if it wants to */ dbgssi(ctx->runcxdbg, ofs, instr, 0, &p); break; } case OPCFRAME: /* this is a frame record - just jump past it */ p += osrp2(p); break; case OPCASI_MASK | OPCASIDIR | OPCASILCL: runpop(ctx, &val); OSCPYSTRUCT(*(ctx->runcxbp + runrp2s(p) - 1), val); stkval = &val; p += 2; goto no_assign; case OPCASI_MASK | OPCASIDIR | OPCASIPRP: obj = runpopobj(ctx); prop = osrp2(p); p += 2; runpop(ctx, &val); stkval = valp = &val; goto assign_property; case OPCASI_MASK | OPCASIDIR | OPCASIPRPPTR: prop = runpopprp(ctx); obj = runpopobj(ctx); runpop(ctx, &val); stkval = valp = &val; goto assign_property; case OPCNEW: run_new(ctx, &p, target, targprop); break; case OPCDELETE: run_delete(ctx, &p, target, targprop); break; default: if ((opc & OPCASI_MASK) == OPCASI_MASK) { runsdef val3; int asityp; int asiext; int lclnum; valp = &val; stkval = &val; asityp = (opc & OPCASITYP_MASK); if (asityp == OPCASIEXT) asiext = *p++; /* get list element/property number if needed */ switch(opc & OPCASIDEST_MASK) { case OPCASIPRP: obj = runpopobj(ctx); prop = osrp2(p); p += 2; break; case OPCASIPRPPTR: prop = runpopprp(ctx); obj = runpopobj(ctx); break; case OPCASIIND: i = runpopnum(ctx); lstp = runpoplst(ctx); break; case OPCASILCL: lclnum = runrp2s(p); p += 2; break; } if (asityp != OPCASIDIR) { /* we have an = operator - get lval, modify, & set */ switch(opc & OPCASIDEST_MASK) { case OPCASILCL: OSCPYSTRUCT(val, *(ctx->runcxbp + lclnum - 1)); break; case OPCASIPRP: case OPCASIPRPPTR: runpprop(ctx, &p, target, targprop, obj, prop, FALSE, 0, obj); runpop(ctx, &val); break; case OPCASIIND: runpind(ctx, i, lstp); runpop(ctx, &val); break; } /* if saving pre-inc/dec value, get the value now */ if ((opc & OPCASIPRE_MASK) == OPCASIPOST) { OSCPYSTRUCT(val3, val); stkval = &val3; } } /* get rvalue, except for inc/dec operations */ if (asityp != OPCASIINC && asityp != OPCASIDEC) runpop(ctx, &val2); /* now apply operation to lvalue using rvalue */ switch(asityp) { case OPCASIADD: if ((opc & OPCASIIND) != 0) { runsdef val4; /* * we're adding to an indexed value out of a list - * we need to make sure the list is protected from * garbage collection, so push it back on the stack * while we're working */ val4.runstyp = DAT_LIST; val4.runsv.runsvstr = lstp; runrepush(ctx, &val4); /* carry out the addition */ runadd(ctx, &val, &val2, 2); /* * in case the list got moved during garbage * collection, retrieve it from the stack */ lstp = runpoplst(ctx); } else { /* no list indexing - just carry out the addition */ runadd(ctx, &val, &val2, 2); } break; case OPCASISUB: if ((opc & OPCASIIND) != 0) { runsdef val4; int result; /* as with adding, protect the list from GC */ val4.runstyp = DAT_LIST; val4.runsv.runsvstr = lstp; runrepush(ctx, &val4); /* carry out the subtraction and note the result */ result = runsub(ctx, &val, &val2, 2); /* recover the list pointer */ lstp = runpoplst(ctx); /* check to see if we have an assignment */ if (!result) goto no_assign; } else { /* no list indexing - just do the subtraction */ if (!runsub(ctx, &val, &val2, 2)) goto no_assign; } break; case OPCASIMUL: if (val.runstyp != DAT_NUMBER || val2.runstyp != DAT_NUMBER) runsig(ctx, ERR_REQNUM); val.runsv.runsvnum *= val2.runsv.runsvnum; break; case OPCASIDIV: if (val.runstyp != DAT_NUMBER || val2.runstyp != DAT_NUMBER) runsig(ctx, ERR_REQNUM); if (val2.runsv.runsvnum == 0) runsig(ctx, ERR_DIVZERO); val.runsv.runsvnum /= val2.runsv.runsvnum; break; case OPCASIINC: if (val.runstyp != DAT_NUMBER) runsig(ctx, ERR_REQNUM); ++(val.runsv.runsvnum); break; case OPCASIDEC: if (val.runstyp != DAT_NUMBER) runsig(ctx, ERR_REQNUM); --(val.runsv.runsvnum); break; case OPCASIDIR: valp = stkval = &val2; break; case OPCASIEXT: switch (asiext) { case OPCASIMOD: if (val.runstyp != DAT_NUMBER || val2.runstyp != DAT_NUMBER) runsig(ctx, ERR_REQNUM); if (val2.runsv.runsvnum == 0) runsig(ctx, ERR_DIVZERO); val.runsv.runsvnum %= val2.runsv.runsvnum; break; case OPCASIBAND: if ((val.runstyp == DAT_TRUE || val.runstyp == DAT_NIL) && (val2.runstyp == DAT_TRUE || val2.runstyp == DAT_NIL)) { int a, b; a = (val.runstyp == DAT_TRUE ? 1 : 0); b = (val2.runstyp == DAT_TRUE ? 1 : 0); val.runstyp = runclog(a && b); } else if (val.runstyp == DAT_NUMBER && val2.runstyp == DAT_NUMBER) val.runsv.runsvnum &= val2.runsv.runsvnum; else runsig(ctx, ERR_REQNUM); break; case OPCASIBOR: if ((val.runstyp == DAT_TRUE || val.runstyp == DAT_NIL) && (val2.runstyp == DAT_TRUE || val2.runstyp == DAT_NIL)) { int a, b; a = (val.runstyp == DAT_TRUE ? 1 : 0); b = (val2.runstyp == DAT_TRUE ? 1 : 0); val.runstyp = runclog(a || b); } else if (val.runstyp == DAT_NUMBER && val2.runstyp == DAT_NUMBER) val.runsv.runsvnum |= val2.runsv.runsvnum; else runsig(ctx, ERR_REQNUM); break; case OPCASIXOR: if ((val.runstyp == DAT_TRUE || val.runstyp == DAT_NIL) && (val2.runstyp == DAT_TRUE || val2.runstyp == DAT_NIL)) { int a, b; a = (val.runstyp == DAT_TRUE ? 1 : 0); b = (val2.runstyp == DAT_TRUE ? 1 : 0); val.runstyp = runclog(a ^ b); } else if (val.runstyp == DAT_NUMBER && val2.runstyp == DAT_NUMBER) val.runsv.runsvnum ^= val2.runsv.runsvnum; else runsig(ctx, ERR_REQNUM); break; case OPCASISHL: if (val.runstyp != DAT_NUMBER || val2.runstyp != DAT_NUMBER) runsig(ctx, ERR_REQNUM); val.runsv.runsvnum <<= val2.runsv.runsvnum; break; case OPCASISHR: if (val.runstyp != DAT_NUMBER || val2.runstyp != DAT_NUMBER) runsig(ctx, ERR_REQNUM); val.runsv.runsvnum >>= val2.runsv.runsvnum; break; default: runsig(ctx, ERR_INVOPC); } break; default: runsig(ctx, ERR_INVOPC); } /* write the rvalue at *valp to the lvalue */ switch(opc & OPCASIDEST_MASK) { case OPCASILCL: OSCPYSTRUCT(*(ctx->runcxbp + lclnum - 1), *valp); break; case OPCASIPRP: case OPCASIPRPPTR: assign_property: { void *valbuf; uchar outbuf[4]; switch(valp->runstyp) { case DAT_LIST: case DAT_SSTRING: valbuf = valp->runsv.runsvstr; break; case DAT_NUMBER: valbuf = outbuf; oswp4s(outbuf, valp->runsv.runsvnum); break; case DAT_OBJECT: case DAT_FNADDR: valbuf = outbuf; oswp2(outbuf, valp->runsv.runsvobj); break; case DAT_PROPNUM: valbuf = outbuf; oswp2(outbuf, valp->runsv.runsvprp); break; default: valbuf = &valp->runsv; break; } ofs = runcpsav(ctx, &p, target, targprop); objsetp(ctx->runcxmem, obj, prop, valp->runstyp, valbuf, ctx->runcxundo); p = runcprst(ctx, ofs, target, targprop); break; } case OPCASIIND: { uint newtot; uint newsiz; uint remsiz; uint delsiz; uchar *delp; uchar *remp; /* compute sizes and pointers to various parts */ ofs = runindofs(ctx, i, lstp); delp = lstp + ofs; /* ptr to item to replace */ delsiz = datsiz(*delp, delp + 1); /* size of *delp */ remsiz = osrp2(lstp) - ofs - delsiz - 1; newsiz = runsiz(valp); /* size of new item */ newtot = osrp2(lstp) + newsiz - delsiz; /* new tot */ /* reserve space for the new list & copy first part */ { runsdef val3; /* make sure lstp stays valid before and after */ val3.runstyp = DAT_LIST; val3.runsv.runsvstr = lstp; runhres3(ctx, newtot, 3, &val, &val2, &val3); /* update all of the pointers within lstp */ lstp = val3.runsv.runsvstr; delp = lstp + ofs; remp = lstp + ofs + delsiz + 1; } memcpy(ctx->runcxhp + 2, lstp + 2, (size_t)(ofs - 2)); /* set size of new list */ oswp2(ctx->runcxhp, newtot); /* copy new item into buffer */ runputbuf(ctx->runcxhp + ofs, valp); /* copy remainder and update heap pointer */ memcpy(ctx->runcxhp + ofs + newsiz + 1, remp, (size_t)remsiz); val.runstyp = DAT_LIST; val.runsv.runsvstr = ctx->runcxhp; stkval = &val; ctx->runcxhp += newtot; break; } } no_assign: /* skip assignment - operation didn't change value */ if (*p == OPCDISCARD) { /* next assignment is DISCARD - deal with it now */ ++p; ctx->runcxsp = rstsp; } else runrepush(ctx, stkval); } else errsig(ctx->runcxerr, ERR_INVOPC); } } /* * come here to return - don't use 'return' directly, since that * would not properly exit the error frame */ done: ; #ifndef DBG_OFF /* * Come here to catch any errors that occur during execution of this * p-code */ ERRCATCH(ctx->runcxerr, err) { /* * if the debugger isn't present, or we're already in the * debugger, or if the debugger can't resume from errors, or if * we're not in user code (in which case the debugger can't * resume from this error even if it normally could resume from * an error), simply re-signal the error */ if (!dbgpresent() || ctx->runcxdbg->dbgcxfcn == 0 || !dbgu_err_resume(ctx->runcxdbg) || (ctx->runcxdbg->dbgcxflg & DBGCXFIND) != 0) errrse(ctx->runcxerr); /* check the error code */ switch(err) { case ERR_RUNEXIT: case ERR_RUNABRT: case ERR_RUNASKD: case ERR_RUNASKI: case ERR_RUNQUIT: case ERR_RUNRESTART: case ERR_RUNEXITOBJ: /* don't trap these errors - resignal it immediately */ errrse(ctx->runcxerr); default: /* trap other errors to the debugger */ break; } /* if the object was unlocked, re-lock it */ if (p == 0) mcmlck(ctx->runcxmem, target); /* set up after the last OPCLINE instruction */ p = mcmobjptr(ctx->runcxmem, (mcmon)target) + ctx->runcxlofs - 2; p += *p; /* * Keep the current error's arguments around for handling * outside of this handler, since we'll need them in dbgssi. */ errkeepargs(ctx->runcxerr); /* enter the debugger with the error code */ dbgssi(ctx->runcxdbg, ctx->runcxlofs, OPCLINE, err, &p); /* check the error again */ switch (err) { case ERR_ARGC: /* we can't continue from this - simply return */ break; default: /* resume execution */ goto resume_from_error; } } ERREND(ctx->runcxerr); #endif /* DBG_OFF */ } /* * Signal a run-time error. This function first calls the debugger * single-step function to allow the debugger to trap the error, then * signals the error as usual when the debugger returns. */ void runsign(runcxdef *ctx, int err) { /* * If the debugger isn't capable of resuming from a run-time error, * trap to the debugger now so that the user can see what happened. * Do not trap to the debugger here if the debugger can resume from * an error; instead, we'll trap in the p-code loop, since we'll be * able to resume execution from the point of the error. * * Note that we can't resume from an error when there's no stack * frame, so we'll trap to the debugger here in that case. */ if (ctx->runcxdbg->dbgcxfcn == 0 || !dbgu_err_resume(ctx->runcxdbg)) dbgssi(ctx->runcxdbg, ctx->runcxlofs, OPCLINE, err, 0); /* signal the error */ errsign(ctx->runcxerr, err, "TADS"); } qtads-2.1.7/tads2/run.h000066400000000000000000000347561265017072300146510ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/RUN.H,v 1.2 1999/05/17 02:52:13 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name run.h - definitions for code execution Function Definitions for code execution Notes The preprocessor symbol RUNFAST can be defined if run-time checking of stack overflow, stack underflow, and other unusual but potentially dangerous conditions is to be turned off. This will result in somewhat faster run-time performance, but run-time errors could be disastrous. Modified 10/20/91 MJRoberts - creation */ #ifndef RUN_INCLUDED #define RUN_INCLUDED #include "os.h" #ifndef STD_INCLUDED # include "std.h" #endif #ifndef OBJ_INCLUDED # include "obj.h" #endif #ifndef PRP_INCLUDED # include "prp.h" #endif #ifndef OPC_INCLUDED # include "opc.h" #endif #ifndef TIO_INCLUDED # include "tio.h" #endif #ifndef DBG_INCLUDED # include "dbg.h" #endif #ifndef TOK_INCLUDED # include "tok.h" #endif #ifdef __cplusplus extern "C" { #endif /* forward declarations */ struct bifcxdef; /* stack element - the stack is an array of these structures */ struct runsdef { uchar runstyp; /* type of element */ union { long runsvnum; /* numeric value */ objnum runsvobj; /* object value */ prpnum runsvprp; /* property number value */ uchar *runsvstr; /* string/list value */ } runsv; }; typedef struct runsdef runsdef; /* external function control structure */ struct runxdef { char runxnam[TOKNAMMAX + 1]; /* name of external function */ int (*runxptr)(void *); /* pointer to memory containing code */ }; typedef struct runxdef runxdef; /* external function context structure - passed to user exits */ struct runuxdef { struct runcxdef osfar_t *runuxctx; /* run-time context */ struct runufdef osfar_t *runuxvec; /* vector of functions */ int runuxargc; /* count of arguments to function */ }; typedef struct runuxdef runuxdef; /* external function callback vector */ struct runufdef { int (osfar_t *runuftyp)(runuxdef *); /* type of top of stack */ long (osfar_t *runufnpo)(runuxdef *); /* pop a number */ uchar *(osfar_t *runufspo)(runuxdef *); /* pop a string */ void (osfar_t *runufdsc)(runuxdef *); /* discard item at top of stack */ void (osfar_t *runufnpu)(runuxdef *, long); /* push a number */ void (osfar_t *runufspu)(runuxdef *, uchar *); /* push alloc'd string */ void (osfar_t *runufcspu)(runuxdef *, char *); /* push a C-string */ uchar *(osfar_t *runufsal)(runuxdef *, int); /* allocate a new string */ void (osfar_t *runuflpu)(runuxdef *, int);/* push DAT_TRUE or DAT_NIL */ }; typedef struct runufdef runufdef; /* execution context */ struct runcxdef { errcxdef *runcxerr; /* error management context */ mcmcxdef *runcxmem; /* cache manager context for object references */ runsdef *runcxstk; /* base of interpreter stack */ runsdef *runcxstop; /* top of stack */ runsdef *runcxsp; /* current stack pointer (stack grows upwards) */ runsdef *runcxbp; /* base pointer */ uchar *runcxheap; /* run-time variable-length object heap */ uchar *runcxhp; /* current heap pointer */ uchar *runcxhtop; /* top of heap */ objucxdef *runcxundo; /* undo context */ tiocxdef *runcxtio; /* text I/O context */ void *runcxbcx; /* context for built-in callback functions */ void (**runcxbi)(struct bifcxdef *ctx, int argc); /* built-in functions */ struct dbgcxdef *runcxdbg; /* debugger context */ struct voccxdef *runcxvoc; /* player command parser context */ void (*runcxdmd)(void *ctx, objnum obj, prpnum prp); /* demand-loader callback function */ void *runcxdmc; /* demand-loader callback context */ runxdef *runcxext; /* external function array */ int runcxexc; /* count of external functions */ uint runcxlofs; /* offset of last line record encountered */ char *runcxgamename; /* name of the .GAM file */ char *runcxgamepath; /* absolute directory path of .GAM file */ }; typedef struct runcxdef runcxdef; /* execute a function, given the function object number */ void runfn(runcxdef *ctx, noreg objnum objn, int argc); /* * Execute p-code given a pointer to the code. p is the actual pointer * to the first byte of code to be executed. self is the object to be * used for the special 'self' pseudo-object, and target is the object * whose data are actually being executed. targprop is the property being * executed; 0 is used for functions. */ void runexe(runcxdef *ctx, uchar *p, objnum self, objnum target, prpnum targprop, int argc); /* push a value onto the stack */ void runpush(runcxdef *ctx, dattyp typ, runsdef *val); /* push a value onto the stack that's already in the heap */ void runrepush(runcxdef *ctx, runsdef *val); /* push a number onto the stack */ void runpnum(runcxdef *ctx, long val); /* push an object onto the stack */ void runpobj(runcxdef *ctx, objnum obj); /* push nil */ void runpnil(runcxdef *ctx); /* push a value onto the stack from a buffer (propdef, list) */ void runpbuf(runcxdef *ctx, int typ, void *val); /* push a counted-length string onto the stack */ void runpstr(runcxdef *ctx, char *str, int len, int sav); /* * Push a C-style string onto the stack, converting escape codes. If * the character contains backslashes, newline, or tab characters, we'll * convert these characters to their escaped equivalent. */ void runpushcstr(runcxdef *ctx, char *str, size_t len, int sav); /* * Push a property onto the stack. codepp is a pointer to the caller's * code pointer, which will be updated if necessary; callobj and * callofsp are the object and starting offset within the object of the * code being executed by the caller, which are needed to update * *codepp. Property 0 is used if a function is being executed. obj * and prop are the object and property number whose value is to be * pushed. If 'inh' is TRUE, it means that only a property inherited * by 'obj' is to be considered; this is used for "pass"/"inherited" * operations, with the current target object given as 'obj'. */ void runpprop(runcxdef *ctx, uchar *noreg *codepp, objnum callobj, prpnum callprop, noreg objnum obj, prpnum prop, int inh, int argc, objnum self); /* top level runpprop, when caller is not executing in an object */ /* void runppr(runcxdef *ctx, objnum obj, prpnum prp, int argc); */ #define runppr(ctx, obj, prp, argc) \ runpprop(ctx, (uchar **)0, (objnum)0, (prpnum)0, obj, prp, FALSE, argc, obj) /* discard top element on stack */ /* void rundisc(runcxdef *ctx); */ #define rundisc(ctx) (runstkund(ctx), (--((ctx)->runcxsp))) /* pop the top element on the stack */ /* void runpop(runcxdef *ctx, runsdef *val); */ #define runpop(ctx, v) \ (runstkund(ctx), memcpy(v, (--((ctx)->runcxsp)), (size_t)sizeof(runsdef))) /* pop a numeric value, signalling an error if not a number */ /* long runpopnum(runcxdef *ctx); */ #define runpopnum(ctx) \ (runstkund(ctx), ((--((ctx)->runcxsp))->runstyp!=DAT_NUMBER ? \ (runsig(ctx,ERR_REQNUM), (long)0) : \ ((ctx)->runcxsp->runsv.runsvnum))) /* pop an object, signalling an error if not an object */ /* objnum runpopobj(runcxdef *ctx); */ #define runpopobj(ctx) \ (runstkund(ctx), ((--(ctx)->runcxsp))->runstyp!=DAT_OBJECT ? \ (runsig(ctx,ERR_REQVOB), (objnum)0) : \ ((ctx)->runcxsp->runsv.runsvobj)) /* pop an object or nil - returns MCMONINV if the value is nil */ #define runpopobjnil(ctx) \ (runstkund(ctx), ((--(ctx)->runcxsp))->runstyp==DAT_OBJECT ? \ ((ctx)->runcxsp->runsv.runsvobj) : \ ((ctx)->runcxsp->runstyp==DAT_NIL ? MCMONINV : \ (runsig(ctx,ERR_REQVOB), (objnum)0))) /* pop a list, signalling an error if not a list */ /* uchar *runpoplst(runcxdef *ctx); */ #define runpoplst(ctx) \ (runstkund(ctx), ((--(ctx)->runcxsp))->runstyp!=DAT_LIST ? \ (runsig(ctx,ERR_REQVLS), (uchar *)0) : \ (uchar *)((ctx)->runcxsp->runsv.runsvstr)) /* pop a property number, signalling an error if not a property number */ /* prpnum runpopprp(runcxdef *ctx); */ #define runpopprp(ctx) \ (runstkund(ctx), ((--(ctx)->runcxsp))->runstyp!=DAT_PROPNUM ? \ (runsig(ctx,ERR_REQVPR), (prpnum)0) : \ ((ctx)->runcxsp->runsv.runsvprp)) /* pop function pointer */ /* objnum runpopfn(runcxdef *ctx); */ #define runpopfn(ctx) \ ((objnum)(runstkund(ctx), ((--(ctx)->runcxsp))->runstyp!=DAT_FNADDR ? \ (runsig(ctx,ERR_REQVFN), (objnum)0) : \ ((ctx)->runcxsp->runsv.runsvobj))) /* pop a string value */ /* char *runpopstr(runcxdef *ctx); */ #define runpopstr(ctx) \ (runstkund(ctx), ((--((ctx)->runcxsp))->runstyp!=DAT_SSTRING ? \ (runsig(ctx,ERR_REQSTR), (uchar *)0) : \ ((ctx)->runcxsp->runsv.runsvstr))) /* pop a logical value - TRUE for DAT_TRUE, FALSE for DAT_NIL */ /* int runpoplog(runcxdef *ctx); */ #define runpoplog(ctx) \ ((--((ctx)->runcxsp))->runstyp==DAT_TRUE ? TRUE : \ (ctx)->runcxsp->runstyp==DAT_NIL ? FALSE : \ (runsig(ctx, ERR_REQLOG), 0)) /* get type of top of stack */ /* int runtostyp(runcxdef *ctx); */ #define runtostyp(ctx) (((ctx)->runcxsp - 1)->runstyp) /* determine if top of stack is logical value (returns TRUE if so) */ /* int runtoslog(runcxdef *ctx); */ #define runtoslog(ctx) \ (runtostyp(ctx) == DAT_TRUE || runtostyp(ctx) == DAT_NIL) /* convert C logical to TADS logical (TRUE->DAT_TRUE, FALSE->DAT_NIL) */ /* int runclog(int log); */ #define runclog(l) ((l) ? DAT_TRUE : DAT_NIL) /* compare magnitudes of numbers/strings on top of stack; strcmp-like value */ int runmcmp(runcxdef *ctx); /* TRUE if items at top of stack are equal, FALSE otherwise */ int runeq(runcxdef *ctx); /* check for stack underflow */ /* void runstkund(runcxdef *ctx); */ /* check for stack overflow */ /* void runstkovf(runcxdef *ctx); */ /* * Check to ensure we have enough arguments to pass to a function or method * call - this simply ensures we have enough data in the current frame. * This is important because the called function will be able to write to * our frame. If we don't have enough arguments, we'll push enough 'nil' * values to meet the need. */ #define runcheckargc(ctx, nargc) \ while ((ctx)->runcxsp - (ctx)->runcxbp < *(nargc)) \ runpnil(ctx) #ifdef RUNFAST # define runstkovf(ctx) (DISCARD 0) # define runstkund(ctx) (DISCARD 0) #else /* RUNFAST */ # define runstkovf(ctx) \ ((ctx)->runcxsp >= (ctx)->runcxstop ? (runsig(ctx, ERR_STKOVF), \ DISCARD 0) : DISCARD 0) # define runstkund(ctx) \ ((ctx)->runcxsp == (ctx)->runcxstk ? runsig(ctx, ERR_STKUND), \ DISCARD 0 : DISCARD 0) #endif /* RUNFAST */ /* reserve space in heap, collecting garbage if necessary */ /* void runhres(runcxdef *ctx, uint siz, uint below); */ #define runhres(ctx, siz, below) \ ((uint)((ctx)->runcxhtop - (ctx)->runcxhp) > (uint)(siz) ? DISCARD 0 : \ (runhcmp(ctx, siz, below, (runsdef *)0, (runsdef *)0, (runsdef *)0),\ DISCARD 0)) /* reserve space, with various amounts of saving */ #define runhres1(ctx, siz, below, val1) \ ((uint)((ctx)->runcxhtop - (ctx)->runcxhp) > (uint)(siz) ? DISCARD 0 : \ (runhcmp(ctx, siz, below, val1, (runsdef *)0, (runsdef *)0), DISCARD 0)) #define runhres2(ctx, siz, below, val1, val2) \ ((uint)((ctx)->runcxhtop - (ctx)->runcxhp) > (uint)(siz) ? DISCARD 0 : \ (runhcmp(ctx, siz, below, val1, val2, (runsdef *)0), DISCARD 0)) #define runhres3(ctx, siz, below, val1, val2, val3) \ ((uint)((ctx)->runcxhtop - (ctx)->runcxhp) > (uint)(siz) ? DISCARD 0 : \ (runhcmp(ctx, siz, below, val1, val2, val3), DISCARD 0)) /* garbage collect heap, making sure 'siz' bytes are available afterwards */ void runhcmp(runcxdef *ctx, uint siz, uint below, runsdef *val1, runsdef *val2, runsdef *val3); /* determine size of a data item */ int runsiz(runsdef *item); /* find a sublist within a list, returning pointer to sublist or NULL */ uchar *runfind(uchar *list, runsdef *item); /* add two runsdef values, returning result in *val */ void runadd(runcxdef *ctx, runsdef *val, runsdef *val2, uint below); /* * subtract val2 from val, returning result in *val; return TRUE if * value changed, FALSE otherwise (this is returned when subtracting * something from a list that isn't in the list) */ int runsub(runcxdef *ctx, runsdef *val, runsdef *val2, uint below); /* restore code pointer from object.property + offset */ uchar *runcprst(runcxdef *ctx, uint ofs, objnum obj, prpnum prop); /* leave a stack frame, removing arguments */ /* void runleave(runcxdef *ctx, uint parms); */ #define runleave(ctx, parms) \ (((ctx)->runcxsp = (ctx)->runcxbp), \ ((ctx)->runcxbp = (runsdef *)((--((ctx)->runcxsp))->runsv.runsvstr)), \ ((ctx)->runcxsp -= (parms))) /* reset run-time: throw away entire stack and heap */ /* void runrst(runcxdef *ctx); */ #define runrst(ctx) (((ctx)->runcxsp = (ctx)->runcxstk), \ ((ctx)->runcxhp = (ctx)->runcxheap), \ dbgrst(ctx->runcxdbg)) /* set up runtime status line display */ void runistat(struct voccxdef *vctx, struct runcxdef *rctx, struct tiocxdef *tctx); /* signal a run-time error - allows debugger trapping */ void runsign(runcxdef *ctx, int err); /* sign a run-time error with zero arguments */ #define runsig(ctx, err) (errargc((ctx)->runcxerr,0),runsign(ctx,err)) /* signal a run-time error with one argument */ #define runsig1(ctx, err, typ, arg) \ (errargv((ctx)->runcxerr,0,typ,arg),errargc((ctx)->runcxerr,1),\ runsign(ctx,err)) /* draw status line */ void runstat(void); /* initialize output status */ void runistat(struct voccxdef *vctx, struct runcxdef *rctx, struct tiocxdef *tctx); #ifdef __cplusplus } #endif #endif /* RUN_INCLUDED */ qtads-2.1.7/tads2/runstat.c000066400000000000000000000034001265017072300155160ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/RUNSTAT.C,v 1.2 1999/05/17 02:52:13 MJRoberts Exp $"; #endif /* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name runstat.c - tads 1 compatible runstat() Function generates status line Notes none Modified 04/04/92 MJRoberts - creation */ #include "os.h" #include "std.h" #include "mcm.h" #include "obj.h" #include "run.h" #include "tio.h" #include "voc.h" #include "dat.h" static runcxdef *runctx; static voccxdef *vocctx; static tiocxdef *tioctx; void runstat(void) { objnum locobj; int savemoremode; /* get the location of the Me object */ runppr(runctx, vocctx->voccxme, PRP_LOCATION, 0); /* if that's no an object, there's nothing we can do */ if (runtostyp(runctx) != DAT_OBJECT) { rundisc(runctx); return; } /* get Me.location */ locobj = runpopobj(runctx); /* flush any pending output */ outflushn(0); /* switch to output display mode 1 (status line) */ os_status(1); /* turn off MORE mode */ savemoremode = setmore(0); /* call the statusLine method of the current room */ runppr(runctx, locobj, PRP_STATUSLINE, 0); /* if we're in the status line, make sure the line gets flushed */ if (os_get_status() != 0) tioputs(tioctx, "\\n"); outflushn(0); /* restore the previous MORE mode */ setmore(savemoremode); /* switch to output display mode 0 (main text area) */ os_status(0); } void runistat(voccxdef *vctx, runcxdef *rctx, tiocxdef *tctx) { runctx = rctx; vocctx = vctx; tioctx = tctx; } qtads-2.1.7/tads2/std.h000066400000000000000000000012701265017072300146200ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/STD.H,v 1.2 1999/05/17 02:52:13 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name std.h - standard definitions Function Various standard definitions Notes None Modified 12/30/92 MJRoberts - adjusted to use lib.h 08/03/91 MJRoberts - creation */ #ifndef STD_INCLUDED #define STD_INCLUDED /* pick up definitions from central library header */ #ifndef LIB_INCLUDED # include "lib.h" #endif #ifndef OS_INCLUDED # include "os.h" #endif #endif /* STD_INCLUDED */ qtads-2.1.7/tads2/sup.h000066400000000000000000000054271265017072300146450ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/SUP.H,v 1.2 1999/05/17 02:52:13 MJRoberts Exp $ */ /* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name sup.h - definitions for post-compilation setup Function does post-compilation setup, such as setting up contents lists Notes none Modified 03/28/92 MJRoberts - creation */ #ifndef SUP_INCLUDED #define SUP_INCLUDED #ifndef TOK_INCLUDED #include "tok.h" #endif #ifndef MCM_INCLUDED #include "mcm.h" #endif #ifndef OBJ_INCLUDED #include "obj.h" #endif #ifndef PRP_INCLUDED #include "prp.h" #endif #ifndef VOC_INCLUDED #include "voc.h" #endif /* setup context */ struct supcxdef { errcxdef *supcxerr; mcmcxdef *supcxmem; /* memory manager client context */ voccxdef *supcxvoc; /* player command parsing context */ tokthdef *supcxtab; /* top-level symbol table */ runcxdef *supcxrun; /* execution context */ uchar *supcxbuf; /* space for building a list */ ushort supcxlen; /* size of buffer */ }; typedef struct supcxdef supcxdef; /* set up contents list for one object for demand-on-load */ void supcont(void *ctx, objnum obj, prpnum prp); /* set up inherited vocabulary (called before executing game) */ void supivoc(supcxdef *ctx); /* find required objects/functions */ void supfind(errcxdef *ctx, tokthdef *tab, voccxdef *voc, objnum *preinit, int warnlevel, int casefold); /* set up reserved words */ void suprsrv(supcxdef *sup, void (*bif[])(struct bifcxdef *, int), toktdef *tab, int fncntmax, int v1compat, char *new_do, int casefold); /* set up built-in functions without symbol table (for run-time) */ void supbif(supcxdef *sup, void (*bif[])(struct bifcxdef *, int), int bifsiz); /* log an undefined-object error */ void sup_log_undefobj(mcmcxdef *mctx, errcxdef *ec, int err, char *sym_name, int sym_name_len, objnum objn); /* set up inherited vocabulary for a particular object */ void supivoc1(supcxdef *sup, voccxdef *ctx, vocidef *v, objnum target, int inh_from_obj, int flags); /* get name of an object out of symbol table */ void supgnam(char *buf, tokthdef *tab, objnum objn); /* table of built-in functions */ typedef struct supbidef supbidef; struct supbidef { char *supbinam; /* name of function */ void (*supbifn)(struct bifcxdef *, int); /* C routine to call */ }; /* external definition for special token table */ extern tokldef supsctab[]; #endif /* SUP_INCLUDED */ qtads-2.1.7/tads2/suprun.c000066400000000000000000000300761265017072300153630ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/SUPRUN.C,v 1.3 1999/07/11 00:46:30 MJRoberts Exp $"; #endif /* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name suprun.c - setup functions for run-time Function This module implements the set-up functions needed at run-time Notes Separated from sup.c to avoid having to link functions needed only in the compiler into the runtime. Modified 12/16/92 MJRoberts - add TADS/Graphic extensions 04/11/92 MJRoberts - creation */ #include #include #include #include "os.h" #include "std.h" #include "obj.h" #include "prp.h" #include "dat.h" #include "tok.h" #include "mcm.h" #include "mch.h" #include "sup.h" #include "bif.h" supbidef osfar_t supbitab[] = { { "say", bifsay }, { "car", bifcar }, { "cdr", bifcdr }, { "length", biflen }, { "randomize", bifsrn }, { "rand", bifrnd }, { "substr", bifsub }, { "cvtstr", bifcvs }, { "cvtnum", bifcvn }, { "upper", bifupr }, { "lower", biflwr }, { "caps", bifcap }, { "find", biffnd }, { "getarg", bifarg }, { "datatype", biftyp }, { "setdaemon", bifsdm }, { "setfuse", bifsfs }, { "setversion", bifsvn }, { "notify", bifnfy }, { "unnotify", bifunn }, { "yorn", bifyon }, { "remfuse", bifrfs }, { "remdaemon", bifrdm }, { "incturn", bifinc }, { "quit", bifqui }, { "save", bifsav }, { "restore", bifrso }, { "logging", biflog }, { "input", bifinp }, { "setit", bifsit }, { "askfile", bifask }, { "setscore", bifssc }, { "firstobj", biffob }, { "nextobj", bifnob }, { "isclass", bifisc }, { "restart", bifres }, { "debugTrace", biftrc }, { "undo", bifund }, { "defined", bifdef }, { "proptype", bifpty }, { "outhide", bifoph }, { "runfuses", bifruf }, { "rundaemons", bifrud }, { "gettime", biftim }, { "getfuse", bifgfu }, { "intersect", bifsct }, { "inputkey", bifink }, { "objwords", bifwrd }, { "addword", bifadw }, { "delword", bifdlw }, { "getwords", bifgtw }, { "nocaps", bifnoc }, { "skipturn", bifskt }, { "clearscreen", bifcls }, { "firstsc", bif1sc }, { "verbinfo", bifvin }, { "fopen", biffopen }, { "fclose", biffclose }, { "fwrite", biffwrite }, { "fread", biffread }, { "fseek", biffseek }, { "fseekeof", biffseekeof }, { "ftell", bifftell }, { "outcapture", bifcapture }, { "systemInfo", bifsysinfo }, { "morePrompt", bifmore }, { "parserSetMe", bifsetme }, { "parserGetMe", bifgetme }, { "reSearch", bifresearch }, { "reGetGroup", bifregroup }, { "inputevent", bifinpevt }, { "timeDelay", bifdelay }, { "setOutputFilter", bifsetoutfilter }, { "execCommand", bifexec }, { "parserGetObj", bifgetobj }, { "parseNounList", bifparsenl }, { "parserTokenize", bifprstok }, { "parserGetTokTypes", bifprstoktyp }, { "parserDictLookup", bifprsdict }, { "parserResolveObjects", bifprsrslv }, { "parserReplaceCommand", bifprsrplcmd }, { "exitobj", bifexitobj }, { "inputdialog", bifinpdlg }, { "resourceExists", bifresexists }, /* * To accommodate systemInfo, we've removed g_readpic. This has been * present for a while and didn't do anything, since tads/g was never * released. We can therefore use its function slot without requiring * a format change. */ /* { "g_readpic", bifgrp }, */ /* likewise these */ /* { "g_showpic", bifgsp }, */ /* { "g_sethot", bifgsh }, */ /* { "g_inventory", bifgin }, */ /* * more tads/g functions - these are kept around mostly as * placeholders in case we want to use these slots in the future */ /* { "g_compass", bifgco }, - removed for reSearch */ /* { "g_overlay", bifgov }, - removed for reGetGroup */ /* { "g_mode", bifgmd }, - removed for inputevent */ /* { "g_music", bifgmu }, - removed for timeDelay */ /* { "g_pause", bifgpa }, - removed for setOutputFilter */ /* { "g_effect", bifgef }, - removed for execCommand */ /* { "g_sound", bifgsn }, - removed for parserGetObj */ /* a few more placeholder slots for future expansion */ /* { "__reserved_func_0", bifgsn }, - removed for parseNounList */ /* { "__reserved_func_2", bifgsn }, - removed for parserTokenize */ /* { "__reserved_func_3", bifgsn }, - removed for parserGetTokTypes */ /* { "__reserved_func_4", bifgsn }, - removed for parserDictLookup */ /* { "__reserved_func_5", bifgsn }, - removed for parserResolveObjects */ /* { "__reserved_func_6", bifgsn }, - removed for parserReplaceCommand */ /* { "__reserved_func_7", bifgsn }, - removed for exitobj */ /* { "__reserved_func_8", bifgsn }, - removed for inputdialog */ /* { "__reserved_func_9", bifgsn }, - removed for resourceExists */ /* more slots added in 2.3.1 */ { "__reserved_func_10", bifgsn }, { "__reserved_func_11", bifgsn }, { "__reserved_func_12", bifgsn }, { "__reserved_func_13", bifgsn }, { "__reserved_func_14", bifgsn }, { "__reserved_func_15", bifgsn }, { "__reserved_func_16", bifgsn }, { "__reserved_func_17", bifgsn }, { "__reserved_func_18", bifgsn }, { "__reserved_func_19", bifgsn }, { 0, 0 } }; /* set up built-in functions array without symbol table (for run-time) */ void supbif(supcxdef *sup, void (*bif[])(bifcxdef*, int), int bifsiz) { supbidef *p; int i; for (p = supbitab, i = 0 ; p->supbinam ; ++i, ++p) { if (i >= bifsiz) errsig(sup->supcxerr, ERR_MANYBIF); bif[i] = p->supbifn; } } /* set up contents property for load-on-demand */ void supcont(void *ctx0, objnum obj, prpnum prp) { supcxdef *ctx = (supcxdef *)ctx0; vocidef ***vpg; vocidef **v; voccxdef *voc = ctx->supcxvoc; int i; int j; int len = 2; objnum chi; objnum loc; /* be sure the buffer is allocated */ if (!ctx->supcxbuf) { ctx->supcxlen = 512; ctx->supcxbuf = mchalo(ctx->supcxerr, ctx->supcxlen, "supcont"); } assert(prp == PRP_CONTENTS); /* the only thing that makes sense */ for (vpg = voc->voccxinh, i = 0 ; i < VOCINHMAX ; ++vpg, ++i) { if (!*vpg) continue; /* no entries on this page */ for (v = *vpg, chi = (i << 8), j = 0 ; j < 256 ; ++v, ++chi, ++j) { /* if there's no record at this location, skip it */ if (!*v) continue; /* inherit the location if it hasn't been set to any value */ if ((*v)->vociloc == MCMONINV && !((*v)->vociflg & VOCIFLOCNIL)) loc = (*v)->vociilc; else loc = (*v)->vociloc; /* if this object is in the indicated location, add it */ if (loc == obj && !((*v)->vociflg & VOCIFCLASS)) { /* see if we have room in list buffer; expand buffer if not */ if (len + 3 > ctx->supcxlen) { uchar *newbuf; /* allocate a new buffer */ newbuf = mchalo(ctx->supcxerr, (len + 512), "supcont"); /* copy the old buffer's contents into the new buffer */ memcpy(newbuf, ctx->supcxbuf, ctx->supcxlen); /* remember the new buffer length */ ctx->supcxlen = len + 512; /* free the old buffer */ mchfre(ctx->supcxbuf); /* remember the new buffer */ ctx->supcxbuf = newbuf; /* sanity check for integer overflow */ if (len + 3 > ctx->supcxlen) errsig(ctx->supcxmem->mcmcxgl->mcmcxerr, ERR_SUPOVF); } ctx->supcxbuf[len] = DAT_OBJECT; oswp2(ctx->supcxbuf + len + 1, chi); len += 3; } } } oswp2(ctx->supcxbuf, len); objsetp(ctx->supcxmem, obj, prp, DAT_LIST, ctx->supcxbuf, ctx->supcxrun->runcxundo); } static void supiwrds(voccxdef *ctx, objnum sc, objnum target, int flags) { int i; vocdef *v; vocdef **vp; vocwdef *vw; /* go through each hash value looking for superclass object */ for (i = VOCHASHSIZ, vp = ctx->voccxhsh ; i != 0 ; ++vp, --i) { /* go through all words in this hash chain */ for (v = *vp ; v != 0 ; v = v->vocnxt) { /* go through all vocwdef's defined for this word */ for (vw = vocwget(ctx, v->vocwlst) ; vw ; vw = vocwget(ctx, vw->vocwnxt)) { /* add word to target if it's defined for this superclass */ if (vw->vocwobj == sc) vocadd2(ctx, vw->vocwtyp, target, VOCFINH + flags, v->voctxt, v->voclen, (v->vocln2 ? v->voctxt + v->voclen : (uchar *)0), v->vocln2); } } } } /* set up inherited vocabulary for a particular object */ void supivoc1(supcxdef *sup, voccxdef *ctx, vocidef *v, objnum target, int inh_from_obj, int flags) { objnum *sc; int numsc; vocidef *scv; for (numsc = v->vocinsc, sc = v->vocisc ; numsc ; ++sc, --numsc) { scv = vocinh(ctx, *sc); if (scv) { /* inherit from its superclasses first */ supivoc1(sup, ctx, scv, target, FALSE, flags); /* if it's a class object, we can inherit from it */ if (scv->vociflg & VOCIFCLASS) { /* inherit location, if we haven't already done so */ if (v->vociilc == MCMONINV) { if (scv->vociloc == MCMONINV) v->vociilc = scv->vociilc; else v->vociilc = scv->vociloc; } } /* * inherit from superclass if it's a class, or if we're * supposed to inherit from any object */ if (inh_from_obj || (scv->vociflg & VOCIFCLASS)) { /* inherit vocabulary if this superclass has any words */ if (scv->vociflg & VOCIFVOC) supiwrds(ctx, *sc, target, flags); } } else { char buf[TOKNAMMAX + 1]; /* get the symbol's name */ supgnam(buf, sup->supcxtab, *sc); /* log an error with the symbol's name and location of first use */ sup_log_undefobj(ctx->voccxmem, ctx->voccxerr, ERR_UNDFOBJ, buf, (int)strlen(buf), *sc); } } } void sup_log_undefobj(mcmcxdef *mctx, errcxdef *ec, int err, char *nm, int nmlen, objnum objn) { uchar *p; size_t len; /* * if the object has any superclasses defined, what must have * happened is that we encountered an error in the course of * defining the object; the object is partially defined, hence it * won't show up in our records of defined objects, yet it really * shouldn't count as an undefined object; simply suppress the * message in this case */ if (objget1sc(mctx, objn) != MCMONINV) return; /* get the object - it contains the location where it was defined */ p = mcmlck(mctx, (mcmon)objn); /* skip the object header */ p += OBJDEFSIZ; /* get the length of the name */ len = strlen((char *)p); #ifdef OS_ERRLINE len += strlen((char *)p + len + 1); #endif /* log the error */ errlog2(ec, err, ERRTSTR, errstr(ec, nm, nmlen), ERRTSTR, errstr(ec, (char *)p, len)); /* done with the object */ mcmunlck(mctx, (mcmon)objn); } qtads-2.1.7/tads2/tadsexit.h000066400000000000000000000152601265017072300156570ustar00rootroot00000000000000/* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tadsexit.h - user exit definitions for TADS version 2 Function Defines the interface for user exit functions Notes User exits need a way of getting to certain internal TADS functions. These functions are provided as a vector of function pointers in a structure passed to the user exit: the tadsuxdef. Modified 04/30/92 MJRoberts - creation */ #ifndef TADSEXIT_INCLUDED # define TADSEXIT_INCLUDED /* * On MS-DOS, user exits are compiled in "tiny" model (suitable for * making into a .COM file), but since TADS is a large-model program, * data and functions provided by TADS must be referenced with "far" * pointers. This keyword is not accepted by compilers on most other * systems, so we'll define the keyword to nothing if we're not on * MS-DOS. Be sure use the compiler option -DMSDOS when building on * MS-DOS! */ #ifndef osfar_t # ifdef MSDOS # define osfar_t far # else # define osfar_t # endif /* MSDOS */ #endif /* osfar_t */ /* * TADS datatypes usable by user-exit functions. These types are * returned by tads_tostyp, and used in tads_push for the 'type' * argument. */ #define TADS_NUMBER 1 /* a number (long integer) */ #define TADS_SSTRING 3 /* constant (single-quoted) string */ #define TADS_NIL 5 /* nil (false, no value) */ #define TADS_TRUE 8 /* true */ /* #define TADS_RSTRING 12 */ /* OBSOLETE - DO NOT USE */ /* * "String descriptor" type. Use this type to hold the return value of * the tads_popstr() function. Note that a string descriptor is NOT a * pointer to a C-style null-terminated string; instead, you must use * the tads_strlen() and tads_strptr() functions to get the length and * text pointer from a descriptor. */ typedef char osfar_t *tads_strdesc; /* * tadsufdef: run-time system callback function vector. */ struct tadsufdef { /* type of top of stack */ int (osfar_t *tadsuftyp)(void osfar_t *); /* pop a number */ long (osfar_t *tadsufnpo)(void osfar_t *); /* pop a string */ tads_strdesc (osfar_t *tadsufspo)(void osfar_t *); /* discard top item of stack */ void (osfar_t *tadsufdsc)(void osfar_t *); /* push a number */ void (osfar_t *tadsufnpu)(void osfar_t *, long); /* push TADS string (allocated with tads_stralo) */ void (osfar_t *tadsufspu)(void osfar_t *, unsigned char osfar_t *); /* push a C-style null-terminated string */ void (osfar_t *tadsufcspu)(void osfar_t *, char osfar_t *); /* allocate new string */ char osfar_t *(osfar_t *tadsufsal)(void osfar_t *, int); /* push a logical value */ void (osfar_t *tadsuflpu)(void osfar_t *, int); }; typedef struct tadsufdef tadsufdef; /* * tadsuxdef: context argument passed into user exit function. Used * to identify callback routines. */ struct tadsuxdef { struct tadscbdef osfar_t *tadsuxcb; /* callback context */ tadsufdef osfar_t *tadsuxuf; /* vector of functions */ int tadsuxac; /* argument count */ }; typedef struct tadsuxdef tadsuxdef; /* internal service macros */ #define tads_vec(ctx) ((ctx)->tadsuxuf) #define tads_c2u(p,i) ((unsigned int)(((unsigned char osfar_t *)(p))[i])) /* * These macros provide easy access to the TADS access functions. All * of the callback macros require the TADS function vector (passed into * the user exit as its argument) as the first argument. */ /* * return the datatype (TADS_xxx) of the value on top of the stack * (i.e., the next value that will be returned by tads_pop) */ #define tads_tostyp(ctx) ((*tads_vec(ctx)->tadsuftyp)(ctx)) /* * Pop a number off stack (use when the item on top of the stack is a * number, TADS_NUMBER). Returns a signed long. */ #define tads_popnum(ctx) ((*tads_vec(ctx)->tadsufnpo)(ctx)) /* * Pop a string off the stack. The pointer returned is to a string * descriptor. You will need to use the tads_strlen() and tads_strptr() * functions to get the length of the string and a pointer to its * buffer -- do not attempt to use the returned pointer directly as * a string. */ #define tads_popstr(ctx) ((*tads_vec(ctx)->tadsufspo)(ctx)) /* * Get the length of a string retrieved with tads_popstr(). */ #define tads_strlen(ctx, str) (tads_c2u(str, 0) + (tads_c2u(str, 1) << 8) - 2) /* * Get a pointer to the string buffer from a value retrieved with * tads_popstr(). The value returned points to the actual text of * the string. Note that this text will NOT be null-terminated; * you must use tads_strlen() to determine the length of the string. */ #define tads_strptr(ctx, str) ((char osfar_t *)((str) + 2)) /* * pop next item off stack and discard it (use to remove TADS_TRUE and * TADS_NIL values from the stack) */ #define tads_pop(ctx) ((*tads_vec(ctx)->tadsufdsc)(ctx)) /* * Push a number onto the stack. */ #define tads_pushnum(ctx, num) ((*tads_vec(ctx)->tadsufnpu)(ctx, (long)(num))) /* * Push a C-style string onto the stack. The string must be a normal * C-style null-terminated string. It should not have been allocated * with tads_stralo(). */ #define tads_pushcstr(ctx, cstr) ((*tads_vec(ctx)->tadsufcspu)(ctx, str)) /* * Allocate space for a string. The string written to the buffer * returned does NOT need to be null-terminated, but it can be; the null * byte will be ignored if it is present. The length you allocate will * be the length of the string, regardless of the presence of any null * bytes. When you allocate a string with this function, push it with * the tads_pushastr() function. Do NOT push this string with the * tads_pushcstr() function, which is only for C-style strings. This * function returns a pointer to the buffer you should use for the * string. */ #define tads_stralo(ctx, len) ((*tads_vec(ctx)->tadsufsal)(ctx, (int)(len))) /* * Push a string allocated with tads_stralo(). */ #define tads_pushastr(ctx, str) ((*tads_vec(ctx)->tadsufspu)(ctx, str)) /* Push a logical value (true or nil) onto stack */ #define tads_pushtrue(ctx) ((*tads_vec(ctx)->tadsuflpu)(ctx, TADS_TRUE)) #define tads_pushnil(ctx) ((*tads_vec(ctx)->tadsuflpu)(ctx, TADS_NIL)) /* get number of arguments to this function */ #define tads_argc(ctx) ((ctx)->tadsuxac) #endif /* TADSEXIT_INCLUDED */ qtads-2.1.7/tads2/tcd.h000066400000000000000000000014771265017072300146110ustar00rootroot00000000000000/* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcd.h - Header for TADS 2 compiler main Function Main entrypoint for TADS 2 compiler Notes None Modified 08/23/98 CNebel - creation */ #ifndef TCD_INCLUDED #define TCD_INCLUDED /* Define default memory sizes if no one else has. */ #ifndef TCD_POOLSIZ # define TCD_POOLSIZ (6 * 1024) #endif #ifndef TCD_LCLSIZ # define TCD_LCLSIZ 2048 #endif #ifndef TCD_HEAPSIZ # define TCD_HEAPSIZ 4096 #endif #ifndef TCD_STKSIZ # define TCD_STKSIZ 50 #endif #ifndef TCD_LABSIZ # define TCD_LABSIZ 1024 #endif /* Main entry point for compiler. */ int tcdmain(int argc, char **argv, char *save_ext); #endif qtads-2.1.7/tads2/tcg.h000066400000000000000000000013051265017072300146020ustar00rootroot00000000000000/* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcg.h - stub for tcg routines Function Declaration of TADS/Graphic functions. Notes These routines do nothing; they are merely to allow linking the normal TADS/Compiler without having to have separate drivers for the graphical and non-graphical versions. */ #ifndef TCG_H #define TCG_H #include "os.h" #include "err.h" #include "prs.h" /* tcgcomp - graphical phase of compilation: do nothing in text version */ void tcgcomp(errcxdef *ec, prscxdef *pctx, const char *infile); #endif qtads-2.1.7/tads2/tio.h000066400000000000000000000151411265017072300146230ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/TIO.H,v 1.2 1999/05/17 02:52:13 MJRoberts Exp $ */ /* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tio.h - text I/O interface Function Formatted text input and output interface definition Notes None Modified 04/11/99 CNebel - Move extern C. 09/05/92 MJRoberts - add length parameter to getstring() 02/16/92 MJRoberts - creation */ #ifndef TIO_INCLUDED #define TIO_INCLUDED #include #ifndef ERR_INCLUDED # include "err.h" #endif #ifndef OBJ_INCLUDED # include "obj.h" #endif #ifdef __cplusplus extern "C" { #endif /* forward decls */ struct runcxdef; /* text i/o context */ struct tiocxdef { errcxdef *tiocxerr; /* error handling context */ }; typedef struct tiocxdef tiocxdef; /* * Initialize the output formatter subsystem. This must be called once * at startup. */ void out_init(void); /* redirect all tioxxx routines to TADS v1.x outxxx equivalents */ #define tioflushn(ctx, nl) outflushn(nl) #define tioflush(ctx) outflush() #define tioblank(ctx) outblank() #define tioreset(ctx) outreset() #define tiogets(ctx, prompt, str, siz) getstring(prompt, str, siz) #define tioputs(ctx, str) outformat(str) #define tioputslen(ctx, str, len) outformatlen(str, len) #define tiocaps(ctx) outcaps() #define tionocaps(ctx) outnocaps() #define tioshow(ctx) outshow() #define tiohide(ctx) outhide() #define tioscore(ctx, s1, s2) os_score(s1, s2) #define tiostrsc(ctx, s) os_strsc(s) /* set up format strings in output subsystem */ void tiosetfmt(tiocxdef *ctx, struct runcxdef *rctx, uchar *fmtbase, uint fmtlen); /* tell tio subsystem the current actor */ void tiosetactor(struct tiocxdef *ctx, objnum actor); /* get the current tio subsystem actor */ objnum tiogetactor(struct tiocxdef *ctx); /* turn output capture on/off */ void tiocapture(struct tiocxdef *tioctx, struct mcmcxdef *memctx, int flag); /* get the capture object handle */ mcmon tiogetcapture(struct tiocxdef *ctx); /* get the amount of text captured */ uint tiocapturesize(struct tiocxdef *ctx); /* format a length-prefixed (runtime-style) string to the display */ void outfmt(tiocxdef *ctx, uchar *txt); /* format a null-terminated (C-style) string to the display */ int outformat(char *s); /* format a counted-length string, which may not be null-terminated */ int outformatlen(char *s, uint len); /* flush output, with specified newline mode */ void outflushn(int nl); /* flush output */ void outflush(void); /* reset output state */ void outreset(void); /* * Get a string from the keyboard. Returns non-zero if an error occurs * (in particular, if no more input is available from the keyboard), * zero on success. */ int getstring(char *prompt, char *buf, int bufl); /* set capitalize-next-character mode on/off */ void outcaps(void); void outnocaps(void); /* open/close output log file */ int tiologopn(tiocxdef *ctx, char *fn); int tiologcls(tiocxdef *ctx); /* * Write text explicitly to the log file. This can be used to add * special text (such as prompt text) that would normally be suppressed * from the log file. When more mode is turned off, we don't * automatically copy text to the log file; any text that the caller * knows should be in the log file during times when more mode is turned * off can be explicitly added with this function. * * If nl is true, we'll add a newline at the end of this text. The * caller should not include any newlines in the text being displayed * here. */ void out_logfile_print(char *txt, int nl); /* * Check output status. Indicate whether output is currently hidden, * and whether any hidden output has occurred. */ void outstat(int *hidden, int *output_occurred); /* hide/show output */ void outhide(void); int outshow(void); /* set the flag to indicate that output has occurred */ void outsethidden(void); /* write a blank line */ void outblank(void); /* start/end watchpoint evaluation */ void outwx(int flag); /* Begin/end capturing */ void tiocapture(tiocxdef *tioctx, mcmcxdef *memctx, int flag); /* clear all captured output */ void tioclrcapture(tiocxdef *tioctx); /* * clear captured output back to a given point -- this can be used to * remove captured output in an inner capture from an enclosing capture */ void tiopopcapture(tiocxdef *tioctx, uint orig_size); /* get the object handle of the captured output */ mcmon tiogetcapture(tiocxdef *ctx); /* get the amount of text captured */ uint tiocapturesize(tiocxdef *ctx); /* turn MORE mode on or off */ int setmore(int state); /* explicitly activate the "MORE" prompt */ void out_more_prompt(); /* * QA controller functions */ int qasopn(char *scrnam, int quiet); void qasclose(void); char *qasgets(char *buf, int bufl); /* * Set an HTML entity expansion. This is called during initialization * when we read a character mapping table that includes HTML entity * expansions. The HTML run-time uses its own expansion mechanism, so * it will ignore this information. The standard character-mode TADS * run-time, however, uses this information to map HTML entities to the * local character set. */ void tio_set_html_expansion(unsigned int html_char_val, const char *expansion, size_t expansion_len); /* check for HTML mode - returns true if an "\H+" sequence is active */ int tio_is_html_mode(); /* set the user output filter function */ void out_set_filter(objnum filter_fn); /* set the double-space mode */ void out_set_doublespace(int dbl); /* * Ask for a filename, using a system-defined dialog (via os_askfile) if * possible. Uses the same interface as os_askfile(), which we will * call directly for graphical implementations. We'll use formatted * text for text-only implementations. */ int tio_askfile(const char *prompt, char *fname_buf, int fname_buf_len, int prompt_type, os_filetype_t file_type); /* * Display a dialog, using a system-defined dialog (via os_input_dialog) * if possible. Uses the same interface as os_input_dialog(), which we * will call directly for graphical implementations. We'll use * formatted text for text-only implementations. */ int tio_input_dialog(int icon_id, const char *prompt, int standard_button_set, const char **buttons, int button_count, int default_index, int cancel_index); #ifdef __cplusplus } #endif #endif /* TIO_INCLUDED */ qtads-2.1.7/tads2/tok.h000066400000000000000000000500521265017072300146250ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/TOK.H,v 1.3 1999/07/11 00:46:30 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tok.h - tokenizer definitions Function Definitions for tokenizer Notes None Modified 08/14/91 MJRoberts - creation */ #ifndef TOK_INCLUDED #define TOK_INCLUDED #ifndef STD_INCLUDED #include "std.h" #endif #ifndef LIN_INCLUDED #include "lin.h" #endif #ifndef MCH_INCLUDED #include "mch.h" #endif #ifndef ERR_INCLUDED #include "err.h" #endif #ifndef MCM_INCLUDED #include "mcm.h" #endif /* number of entries in hash table - must be power of 2 */ #define TOKHASHSIZE 256 /* symbol types */ #define TOKSTUNK 0 /* unknown symbol, not yet defined */ #define TOKSTFUNC 1 /* function; value is object number */ #define TOKSTOBJ 2 /* object; value is object number */ #define TOKSTPROP 3 /* property; value is property number */ #define TOKSTLOCAL 4 /* a local variable or formal parameter */ #define TOKSTSELF 5 /* the pseudo-object "self" */ #define TOKSTBIFN 6 /* a built-in function */ #define TOKSTFWDOBJ 7 /* forward-referenced object */ #define TOKSTFWDFN 8 /* forward-referenced object */ #define TOKSTINHERIT 9 /* the pseudo-object "inherited" */ #define TOKSTEXTERN 10 /* an external function */ #define TOKSTKW 11 /* keyword; value is token number */ #define TOKSTLABEL 12 /* statement label */ #define TOKSTARGC 13 /* 'argcount' pseudo-variable */ #define TOKSTPROPSPEC 14 /* speculative evaluation property */ /* token types */ #define TOKTEOF 1 /* binary operators - keep these together (see prsbopl[] in prs.c) */ #define TOKTPLUS 2 #define TOKTMINUS 3 #define TOKTDIV 4 #define TOKTTIMES 5 #define TOKTNOT 6 /* ! or "not" */ #define TOKTEQ 7 #define TOKTNE 8 #define TOKTGT 9 #define TOKTGE 10 #define TOKTLT 11 #define TOKTLE 12 #define TOKTMOD 13 #define TOKTBAND 14 #define TOKTBOR 15 #define TOKTXOR 16 #define TOKTSHL 17 #define TOKTSHR 18 #define TOKTTILDE 30 /* * special 'dot' replacement for speculative evaluation mode -- this is * strictly for marking parse tree nodes, and has the same meaning in a * parse tree node as a regular TOKTDOT, but generates code that can't * call methods */ #define TOKTDOTSPEC 31 /* special node marker for explicit superclass inheritance nodes */ #define TOKTEXPINH 32 #define TOKTLPAR 50 /* ( */ #define TOKTRPAR 51 /* ) */ #define TOKTCOLON 52 #define TOKTDSTRING 53 /* string in double quotes */ #define TOKTSSTRING 54 /* string in single quotes */ #define TOKTNUMBER 55 #define TOKTSYMBOL 56 #define TOKTINVALID 57 /* invalid lexical token */ #define TOKTLBRACK 58 /* [ */ #define TOKTRBRACK 59 /* ] */ #define TOKTLBRACE 60 /* { */ #define TOKTRBRACE 61 /* } */ #define TOKTSEM 62 /* ; */ #define TOKTCOMMA 63 #define TOKTDOT 64 /* . */ #define TOKTOR 65 /* | or "if" */ #define TOKTAND 66 /* & or "and" */ #define TOKTIF 67 /* keywords */ #define TOKTELSE 68 #define TOKTWHILE 69 #define TOKTFUNCTION 70 #define TOKTRETURN 71 #define TOKTLOCAL 72 #define TOKTOBJECT 73 #define TOKTBREAK 74 #define TOKTCONTINUE 75 #define TOKTLIST 76 /* a list */ #define TOKTNIL 77 #define TOKTTRUE 78 #define TOKTPASS 79 #define TOKTCLASS 80 #define TOKTEXIT 81 #define TOKTABORT 82 #define TOKTASKDO 83 #define TOKTASKIO 84 #define TOKTPOUND 85 /* # */ #define TOKTQUESTION 86 /* ? */ #define TOKTCOMPOUND 87 #define TOKTIOSYN 88 #define TOKTDOSYN 89 #define TOKTEXTERN 90 #define TOKTFORMAT 91 #define TOKTDO 92 #define TOKTFOR 93 #define TOKTNEW 94 #define TOKTDELETE 95 /* assignment operators - keep these together */ #define TOKTINC 150 /* ++ */ #define TOKTPOSTINC 151 /* MUST BE TOKTINC + 1 */ #define TOKTDEC 152 /* -- */ #define TOKTPOSTDEC 153 /* MUST BE TOKTDEC + 1 */ #define TOKTPLEQ 154 /* += */ #define TOKTMINEQ 155 /* -= */ #define TOKTDIVEQ 156 /* /= */ #define TOKTTIMEQ 157 /* *= */ #define TOKTASSIGN 158 /* simple assignment */ #define TOKTMODEQ 159 /* %= (mod and assign) operator */ #define TOKTBANDEQ 160 /* &= */ #define TOKTBOREQ 161 /* |= */ #define TOKTXOREQ 162 /* ^= (xor and assign) */ #define TOKTSHLEQ 163 /* <<= (shift left and assign) */ #define TOKTSHREQ 164 /* >>= (shift right and assign */ #define TOKTSWITCH 200 #define TOKTCASE 201 #define TOKTDEFAULT 202 #define TOKTGOTO 203 #define TOKTELLIPSIS 204 /* ... */ #define TOKTSPECIAL 205 /* "specialWords" */ #define TOKTREPLACE 206 /* replace */ #define TOKTMODIFY 207 /* modify */ #define TOKTEQEQ 208 /* the '==' operator */ #define TOKTPOINTER 209 /* the -> operator */ /* the longest a symbol name can be */ #define TOKNAMMAX 39 /* symbol table entry */ typedef struct toksdef toksdef; struct toksdef { uchar tokstyp; /* type of the symbol */ uchar tokshsh; /* hash value of symbol */ ushort toksval; /* value of the symbol (depends on type) */ ushort toksfr; /* frame offset of symbol (for debugger) */ uchar tokslen; /* length of the symbol's name */ char toksnam[TOKNAMMAX]; /* name of symbol */ }; /* symbol table entry without 'name' portion - for allocation purposes */ struct toks1def { uchar tokstyp; uchar tokshsh; ushort toksval; ushort toksfr; uchar tokslen; char toksnam[1]; }; typedef struct toks1def toks1def; /* generic symbol table object - other symbol tables are subclasses */ typedef struct toktdef toktdef; struct toktdef { void (*toktfadd)(toktdef *tab, char *name, int namel, int typ, int val, int hash); /* add symbol */ int (*toktfsea)(toktdef *tab, char *name, int namel, int hash, toksdef *ret); /* search symbol table */ void (*toktfset)(toktdef *tab, toksdef *sym); /* update val & typ of symbol to those in *sym */ void (*toktfeach)(toktdef *tab, void (*fn)(void *ctx, toksdef *sym), void *fnctx); /* call fn for each sym */ toktdef *toktnxt; /* next symbol table to be searched */ errcxdef *tokterr; /* error handling context */ }; /* maximum number of pools (TOKTSIZE bytes each) for symbols */ #define TOKPOOLMAX 128 /* pointer to a symbol in a hashed symbol table */ struct tokthpdef { mcmon tokthpobj; /* cache manager object number of page */ uint tokthpofs; /* offset within page of this symbol */ }; typedef struct tokthpdef tokthpdef; /* extended symbol entry in a hashed symbol table */ struct tokshdef { tokthpdef tokshnxt; /* pointer to next symbol in the table */ toksdef tokshsc; /* superclass - normal symbol entry */ }; typedef struct tokshdef tokshdef; /* hashing symbol table (subclass of generic symbol table) */ struct tokthdef { toktdef tokthsc; /* generic symbol table superclass data */ mcmcxdef *tokthmem; /* memory manager context */ tokthpdef tokthhsh[TOKHASHSIZE]; /* hash table */ uint tokthpcnt; /* number of memory pools for toksdef's */ mcmon tokthpool[TOKPOOLMAX]; /* memory pools for toksdef's */ uint tokthfinal[TOKPOOLMAX]; /* actual sizes of these pools */ uchar *tokthcpool; /* current pool pointer */ ushort tokthsize; /* remaining size of top memory pool */ ushort tokthofs; /* allocation offset in top memory pool */ }; typedef struct tokthdef tokthdef; /* size of toksdef pools to allocate for hashed symbol tables */ #define TOKTHSIZE 4096 /* * Linear cache-object-embedded symbol table. This type of symbol * table is used for frame parameter/local variable lists. It is best * for small tables, because it isn't broken up into hash buckets, so it * is searched linearly. As a result, it's small enough to be embedded * in code. */ struct toktldef { toktdef toktlsc; /* generic symbol table superclass data */ uchar *toktlptr; /* base of linear symbol table */ uchar *toktlnxt; /* next free byte in table */ uint toktlcnt; /* number of objects in the table */ uint toktlsiz; /* bytes remaining in the table */ }; typedef struct toktldef toktldef; struct tokdef { int toktyp; /* type of the token */ int toklen; /* length of token text, if a symbolic token */ long tokval; /* numeric value, if applicable */ ushort tokofs; uint tokhash; /* token hash value, if a symbolic token */ char toknam[TOKNAMMAX+1]; /* text of token, if a symbolic token */ toksdef toksym; /* symbol from table matching token */ }; typedef struct tokdef tokdef; /* special character sequence */ #define TOKSCMAX 3 /* maximum length of a special char sequence */ typedef struct tokscdef tokscdef; struct tokscdef { tokscdef *tokscnxt; /* next sequence with same first character */ int toksctyp; /* token type corresponding to sequence */ int toksclen; /* length of the sequence */ char tokscstr[TOKSCMAX+1]; /* the sequence itself */ }; /* * Compare a special character sequence - for efficiency, define * something special for the maximum length available (TOKSCMAX). * Note that the first character will always be equal, or the * string wouldn't even get to the point of being tested by this * macro. */ #if TOKSCMAX == 3 # define toksceq(str1, str2, len1, len2) \ ((len2) >= (len1) \ && ((len1) == 1 \ || ((str1)[1] == (str2)[1] \ && ((len1) == 2 \ || (str1)[2] == (str2)[2])))) #endif /* TOKSCMAX == 3 */ #ifndef toksceq # define toksceq(str1, str2, len) (!memcmp(str1, str2, (size_t)(len))) #endif /* toksceq */ /* special character sequence list table entry */ struct tokldef { int tokltyp; /* token type corresponding to sequence */ char toklstr[TOKSCMAX+1]; /* the text of the sequence */ }; typedef struct tokldef tokldef; /* include path structure */ typedef struct tokpdef tokpdef; struct tokpdef { tokpdef *tokpnxt; /* next path in list */ int tokplen; /* length of directory name */ char tokpdir[1]; /* directory to search */ }; /* #define symbol structure */ typedef struct tokdfdef tokdfdef; struct tokdfdef { tokdfdef *nxt; /* next symbol in the same hash chain */ char *nm; /* name of the symbol */ int len; /* length of the symbol */ int explen; /* length of the expansion */ char expan[1]; /* expansion buffer */ }; /* #define hash table information */ #define TOKDFHSHSIZ 64 #define TOKDFHSHMASK 63 /* maximum #if nesting */ #define TOKIFNEST 64 /* #if state */ #define TOKIF_IF_YES 1 /* processing a true #if/#ifdef block */ #define TOKIF_IF_NO 2 /* processing a false #if/#ifdef block */ #define TOKIF_ELSE_YES 3 /* processing a true #else part */ #define TOKIF_ELSE_NO 4 /* processing a false #else part */ /* maximum macro expansion nesting */ #define TOKMACNEST 20 /* lexical analysis context */ struct tokcxdef { errcxdef *tokcxerr; /* error handling context */ mcmcxdef *tokcxmem; /* cache manager context */ struct dbgcxdef *tokcxdbg; /* debugger context */ lindef *tokcxlin; /* line source */ tokpdef *tokcxinc; /* head of include path list */ toktdef *tokcxstab; /* current head of symbol table chain */ void *tokcxscx; /* context for string storage callback functions */ ushort (*tokcxsst)(void *ctx); /* start storing a string; return offset of string's storage */ void (*tokcxsad)(void *ctx, char *str, ushort len); /* add characters to a string */ void (*tokcxsend)(void *ctx); /* finish storing string */ char *tokcxmsav[TOKMACNEST]; /* saved positions for macro expansion */ ushort tokcxmsvl[TOKMACNEST]; /* saved lengths for macro expansion */ int tokcxmlvl; /* macro nesting level */ int tokcxflg; /* flags */ # define TOKCXFINMAC 0x01 /* doing <> macro expansion */ # define TOKCXCASEFOLD 0x02 /* fold upper and lower case */ # define TOKCXFCMODE 0x04 /* parse using C operators */ # define TOKCXF_EMBED_PAREN_PRE 0x08 /* embedded expr - did '(' */ # define TOKCXF_EMBED_PAREN_AFT 0x10 /* embedded expr - must do ')' */ # define TOKCXFLIN2 0x20 /* new-style line records */ tokdef tokcxcur; /* current token */ char *tokcxbuf; /* buffer for long lines */ ushort tokcxbsz; /* size of long line buffer */ char *tokcxptr; /* pointer into line source */ ushort tokcxlen; /* length of text in buffer */ uchar tokcxinx[256]; /* special character indices */ tokdfdef *tokcxdf[TOKDFHSHSIZ]; /* hash table for #define symbols */ int tokcxifcnt; /* number of #endif's we expect to find */ char tokcxif[TOKIFNEST]; /* #if state for each nesting level */ int tokcxifcur; /* current #if state, obeying nesting */ struct linfdef *tokcxhdr; /* list of previously included headers */ tokscdef *tokcxsc[1]; /* special character table */ }; typedef struct tokcxdef tokcxdef; /* allocate and initialize a lexical analysis context */ tokcxdef *tokcxini(errcxdef *errctx, mcmcxdef *mctx, tokldef *sctab); /* add an include path to a token handling context */ void tokaddinc(tokcxdef *ctx, char *path, int pathlen); /* compute the hash value of a string */ uint tokhsh(char *nam); /* * Fold case of a token if we're in case-insensitive mode. This should * be called any time a token is constructed artificially; it need not * be used the token is read through the tokenizer, because the * tokenizer will always adjust a token as needed before returning it. */ void tok_case_fold(tokcxdef *ctx, tokdef *tok); /* initialize a hashed symbol table */ void tokthini(errcxdef *errctx, mcmcxdef *memctx, toktdef *toktab1); /* add a symbol to a hashed symbol table */ void tokthadd(toktdef *toktab, char *name, int namel, int typ, int val, int hash); /* update a symbol in a hashed symbol table */ void tokthset(toktdef *toktab, toksdef *sym); /* search a hashed symbol table for a symbol */ int tokthsea(toktdef *tab, char *name, int namel, int hash, toksdef *ret); /* call a function for each symbol in a hashed symbol table */ void toktheach(toktdef *tab, void (*cb)(void *ctx, toksdef *sym), void *ctx); /* find a symbol given type and value */ int tokthfind(toktdef *tab, int typ, uint val, toksdef *sym); /* initialize a linear symbol table */ void toktlini(errcxdef *errctx, toktldef *toktab, uchar *mem, uint siz); /* add a symbol to a linear symbol table */ void toktladd(toktdef *toktab, char *name, int namel, int typ, int val, int hash); /* search a linear symbol table */ int toktlsea(toktdef *tab, char *name, int namel, int hash, toksdef *ret); /* update a symbol in a linear symbol table */ void toktlset(toktdef *toktab, toksdef *sym); /* call a function for each symbol in a local symbol table */ void toktleach(toktdef *tab, void (*cb)(void *ctx, toksdef *sym), void *ctx); /* delete all symbols from a linear table */ void toktldel(toktldef *tab); /* get next token, removing it from input stream */ int toknext(tokcxdef *ctx); /* general function to get/peek at next token */ int tokget1(tokcxdef *ctx, tokdef *tok, int consume); /* add a symbol to the #define symbol table */ void tok_add_define(tokcxdef *ctx, char *sym, int len, char *expan, int explen); /* * add a symbol to the #define symbol table, folding case if we're * operating in case-insensitive mode */ void tok_add_define_cvtcase(tokcxdef *ctx, char *sym, int len, char *expan, int explen); /* add a symbol to the #define symbol table as a number */ void tok_add_define_num_cvtcase(tokcxdef *ctx, char *sym, int len, int num); /* undefine a #define symbol */ void tok_del_define(tokcxdef *ctx, char *sym, int len); /* read/write preprocessor symbols from/to a file */ void tok_read_defines(tokcxdef *ctx, osfildef *fp, errcxdef *ec); /* write preprocessor state to a file */ void tok_write_defines(tokcxdef *ctx, osfildef *fp, errcxdef *ec); /* determine if a char is a valid non-initial character in a symbol name */ #define TOKISSYM(c) \ (isalpha((uchar)(c)) || isdigit((uchar)(c)) || (c)=='_' || (c)=='$') /* numeric conversion and checking macros */ #define TOKISHEX(c) \ (isdigit((uchar)(c))||((c)>='a'&&(c)<='f')||((c)>='A'&&(c)<='F')) #define TOKISOCT(c) \ (isdigit((uchar)(c))&&!((c)=='8'||(c)=='9')) #define TOKHEX2INT(c) \ (isdigit((uchar)c)?(c)-'0':((c)>='a'?(c)-'a'+10:(c)-'A'+10)) #define TOKOCT2INT(c) ((c)-'0') #define TOKDEC2INT(c) ((c)-'0') #endif /* TOK_INCLUDED */ qtads-2.1.7/tads2/trd.c000066400000000000000000000603101265017072300146120ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/TRD.C,v 1.3 1999/05/29 15:51:02 MJRoberts Exp $"; #endif /* * Copyright (c) 1992, 2000 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name trd.c - tads2 run-time driver Function reads a binary file and executes it Notes none Modified 04/11/99 CNebel - Use new headers. 04/04/92 MJRoberts - creation */ #include #include #include "os.h" #include "std.h" #include "trd.h" #include "err.h" #include "mch.h" #include "obj.h" #include "run.h" #include "voc.h" #include "bif.h" #include "dbg.h" #include "sup.h" #include "cmd.h" #include "fio.h" #include "oem.h" #include "ply.h" #include "cmap.h" #include "regex.h" /* dummy setup function */ void supgnam(char *buf, tokthdef *tab, objnum sc) { strcpy(buf, "???"); } /* dummy file read functions */ void tok_read_defines(tokcxdef *tctx, osfildef *fp, errcxdef *ec) { errsig(ec, ERR_UNKRSC); } /* dummy debugger functions */ void trchid(void) {} void trcsho(void) {} struct runsdef *dbgfrfind(dbgcxdef *ctx, objnum frobj, uint frofs) { VARUSED(frobj); VARUSED(frofs); errsig(ctx->dbgcxerr, ERR_INACTFR); return 0; } void dbgss(struct dbgcxdef *ctx, uint ofs, int instr, int err, uchar *noreg *p) { VARUSED(ctx); VARUSED(ofs); VARUSED(instr); VARUSED(err); VARUSED(p); } int dbgstart(struct dbgcxdef *ctx) { VARUSED(ctx); return TRUE; } /* printf-style formatting */ static void trdptf(const char *fmt, ...) { char buf[256]; va_list va; /* format the string */ va_start(va, fmt); vsprintf(buf, fmt, va); va_end(va); /* print the formatted buffer */ os_printz(buf); } /* * display a range of usage messages */ static void trdusage_show_range(errcxdef *ec, int msg_first, int msg_last) { int i; char buf[128]; for (i = msg_first ; i <= msg_last ; ++i) { errmsg(ec, buf, (uint)sizeof(buf), i); trdptf("%s\n", buf); } } /* * display a range of usage messages, then throw the usage error */ static void trdusage_range(errcxdef *ec, int msg_first, int msg_last) { /* show the message range */ trdusage_show_range(ec, msg_first, msg_last); /* signal the usage error */ errsig(ec, ERR_USAGE); } /* * display general run-time usage information */ static void trdusage(errcxdef *ec) { int first; /* * if we have an app display name, display it instead of the * hard-coded text in the message identifying the app */ first = ERR_TRUS1; if (ec->errcxappctx != 0 && ec->errcxappctx->usage_app_name != 0) { char buf[128]; char buf2[128]; erradef argv[1]; /* get the parameterized usage message */ errmsg(ec, buf, (uint)sizeof(buf), ERR_TRUSPARM); /* format in the application name */ argv[0].errastr = (char *)ec->errcxappctx->usage_app_name; errfmt(buf2, (int)sizeof(buf2), buf, 1, argv); /* display it */ trdptf("%s\n", buf2); /* start at the next message */ ++first; } /* display the main option list messages */ trdusage_show_range(ec, first, ERR_TRUSL); /* display the OS-specific option messages, if any */ trdusage_show_range(ec, ERR_TRUS_OS_FIRST, ERR_TRUS_OS_LAST); /* display the usage footer messages */ trdusage_range(ec, ERR_TRUSFT1, ERR_TRUSFTL); } /* * display -s suboptions */ static void trdusage_s(errcxdef *ec) { trdusage_range(ec, ERR_TRSUS1, ERR_TRSUSL); } static void trdmain1(errcxdef *ec, int argc, char *argv[], appctxdef *appctx, char *save_ext) { osfildef *swapfp = (osfildef *)0; runcxdef runctx; bifcxdef bifctx; voccxdef vocctx; void (*bif[100])(struct bifcxdef *, int); mcmcxdef *mctx = 0; mcmcx1def *globalctx = 0; dbgcxdef dbg; supcxdef supctx; char *swapname = 0; char swapbuf[OSFNMAX]; char **argp; char *arg; char *infile; char infile_abs[OSFNMAX]; /* fully-qualified input file name */ char infile_path[OSFNMAX]; /* absolute path to input file */ char *exefile; /* try with executable file if no infile */ ulong swapsize = 0xffffffffL; /* allow unlimited swap space */ int swapena = OS_DEFAULT_SWAP_ENABLED; /* swapping enabled? */ int i; int pause = FALSE; /* pause after finishing game */ fiolcxdef fiolctx; noreg int loadopen = FALSE; char inbuf[OSFNMAX]; ulong cachelimit = 0xffffffff; ushort undosiz = TRD_UNDOSIZ; /* default undo context size 16k */ objucxdef *undoptr = 0; uint flags; /* flags used to write the file we're reading */ objnum preinit; /* preinit object, if we need to execute it */ uint heapsiz = TRD_HEAPSIZ; uint stksiz = TRD_STKSIZ; runsdef *mystack; uchar *myheap; extern osfildef *cmdfile; /* hacky v1 qa interface - command log fp */ extern osfildef *logfp; /* hacky v1 qa interface - output log fp */ int preload = FALSE; /* TRUE => preload all objects */ ulong totsize; extern voccxdef *main_voc_ctx; int safety_read, safety_write; /* file I/O safety level */ char *restore_file = 0; /* .SAV file to restore */ char *charmap = 0; /* character map file */ int charmap_none; /* explicitly do not use a character set */ int doublespace = TRUE; /* formatter double-space setting */ NOREG((&loadopen)) /* initialize the output formatter */ out_init(); /* set safety level to 2 by default - read any/write current dir only */ safety_read = safety_write = 2; /* no -ctab- yet */ charmap_none = FALSE; /* parse arguments */ for (i = 1, argp = argv + 1 ; i < argc ; ++argp, ++i) { arg = *argp; if (*arg == '-') { switch(*(arg+1)) { case 'c': if (!strcmp(arg+1, "ctab")) { /* get the character mapping table */ charmap = cmdarg(ec, &argp, &i, argc, 4, trdusage); } else if (!strcmp(arg+1, "ctab-")) { /* use the default mapping */ charmap_none = TRUE; } else trdusage(ec); break; case 'r': /* restore a game */ restore_file = cmdarg(ec, &argp, &i, argc, 1, trdusage); break; case 'i': qasopn(cmdarg(ec, &argp, &i, argc, 1, trdusage), TRUE); break; case 'o': cmdfile = osfopwt(cmdarg(ec, &argp, &i, argc, 1, trdusage), OSFTCMD); break; case 'l': logfp = osfopwt(cmdarg(ec, &argp, &i, argc, 1, trdusage), OSFTCMD); break; case 'p': if (!stricmp(arg, "-plain")) { os_plain(); break; } pause = cmdtog(ec, pause, arg, 1, trdusage); break; case 'd': if (!strnicmp(arg, "-double", 7)) { /* get the argument value */ doublespace = cmdtog(ec, doublespace, arg, 6, trdusage); /* set the double-space mode in the formatter */ out_set_doublespace(doublespace); break; } break; case 's': { char *p; /* get the option */ p = cmdarg(ec, &argp, &i, argc, 1, trdusage); /* if they're asking for help, display detailed usage */ if (*p == '?') trdusage_s(ec); /* get the safety level from the argument */ safety_read = *p - '0'; safety_write = (*(p+1) != '\0' ? *(p+1) - '0' : safety_read); /* range-check the values */ if (safety_read < 0 || safety_read > 4 || safety_write < 0 || safety_write > 4) trdusage_s(ec); /* tell the host system about the setting */ if (appctx != 0 && appctx->set_io_safety_level != 0) (*appctx->set_io_safety_level) (appctx->io_safety_level_ctx, safety_read, safety_write); } break; case 'm': switch(*(arg + 2)) { case 's': stksiz = atoi(cmdarg(ec, &argp, &i, argc, 2, trdusage)); break; case 'h': heapsiz = atoi(cmdarg(ec, &argp, &i, argc, 2, trdusage)); break; default: cachelimit = atol(cmdarg(ec, &argp, &i, argc, 1, trdusage)); break; } break; case 't': /* swap file options: -tf file, -ts size, -t- (no swap) */ switch(*(arg+2)) { case 'f': swapname = cmdarg(ec, &argp, &i, argc, 2, trdusage); break; case 's': swapsize = atol(cmdarg(ec, &argp, &i, argc, 2, trdusage)); break; case 'p': preload = cmdtog(ec, preload, arg, 2, trdusage); break; default: swapena = cmdtog(ec, swapena, arg, 1, trdusage); break; } break; case 'u': undosiz = atoi(cmdarg(ec, &argp, &i, argc, 1, trdusage)); break; default: trdusage(ec); } } else break; } /* presume we won't take the .gam from the application executable */ exefile = 0; /* get input name argument, and make sure it's the last argument */ if (i == argc) { osfildef *fp; ulong curpos; ulong endpos; int use_exe; /* * There's no input name argument, so we need to find the game * to play some other way. First, check to see if we have a * game to restore, and if so whether it has the .GAM name * encoded into it. Next, look to see if there's a game * attached to the executable file; if so, use it. If not, see * if the host system wants to provide a name through its * callback. */ /* presume we won't find a game attached to the executable file */ infile = 0; use_exe = FALSE; /* * see if we have a saved game to restore, and it specifies the * GAM file that saved it */ if (restore_file != 0) { /* try getting the game name from the restore file */ if (fiorso_getgame(restore_file, inbuf, sizeof(inbuf))) { /* got it - use this file */ infile = inbuf; } } /* * it that didn't work, try to read from os-dependent part of * program being executed */ if (infile == 0) { /* try opening the executable file */ exefile = (argv && argv[0] ? argv[0] : "TRX"); fp = os_exeseek(exefile, "TGAM"); if (fp != 0) { /* see if there's a game file attached to the executable */ curpos = osfpos(fp); osfseek(fp, 0L, OSFSK_END); endpos = osfpos(fp); osfcls(fp); /* if we found it, use it */ if (endpos != curpos) use_exe = TRUE; } } /* * if we didn't find a game in the executable, try the host * system callback */ if (infile == 0 && !use_exe) { /* * ask the host system callback what to do - if we don't * have a host system callback, or the callback */ if (appctx != 0 && appctx->get_game_name != 0) { /* call the host system callback */ if ((*appctx->get_game_name)(appctx->get_game_name_ctx, inbuf, sizeof(inbuf))) { /* the host system provided a name - use it */ infile = inbuf; } else { /* * the host didn't provide a name - simply display a * message indicating that no game file has been * chosen, and return */ trdptf("\n"); trdptf("(No game has been selected.)\n"); return; } } else { /* * we've run out of ways to get a filename - give the * user the usage message and quit */ trdusage(ec); } } } else { infile = *argp; if (i + 1 != argc) trdusage(ec); #ifndef OS_HATES_EXTENSIONS /* * If original name exists, use it; otherwise, try adding .GAM. * Note that this code is ifdef'd so that platforms that don't * use filename extensions in the manner conventional for DOS * and Unix won't use this code. */ if (osfacc(infile)) { strcpy(inbuf, infile); os_defext(inbuf, "gam"); infile = inbuf; } #endif /* !defined(OS_HATES_EXTENSIONS) */ } /* open up the swap file */ if (swapena && swapsize) { swapfp = os_create_tempfile(swapname, swapbuf); if (swapname == 0) swapname = swapbuf; if (swapfp == 0) errsig(ec, ERR_OPSWAP); } /* load the character map */ if (charmap_none) cmap_override(); else if (cmap_load(charmap)) errsig(ec, ERR_INVCMAP); ERRBEGIN(ec) /* initialize cache manager context */ globalctx = mcmini(cachelimit, 128, swapsize, swapfp, swapname, ec); mctx = mcmcini(globalctx, 128, fioldobj, &fiolctx, objrevert, (void *)0); mctx->mcmcxrvc = mctx; /* set up an undo context */ if (undosiz) undoptr = objuini(mctx, undosiz, vocdundo, vocdusz, &vocctx); else undoptr = (objucxdef *)0; /* set up vocabulary context */ vocini(&vocctx, ec, mctx, &runctx, undoptr, 100, 100, 200); /* * save a pointer to the voc context globally, so that certain * external routines (such as Unix-style signal handlers) can reach * it */ main_voc_ctx = &vocctx; /* allocate stack and heap */ totsize = (ulong)stksiz * (ulong)sizeof(runsdef); if (totsize != (size_t)totsize) errsig1(ec, ERR_STKSIZE, ERRTINT, (uint)(65535/sizeof(runsdef))); mystack = (runsdef *)mchalo(ec, (size_t)totsize, "runtime stack"); myheap = mchalo(ec, heapsiz, "runtime heap"); /* get the absolute path for the input file */ if (infile != 0) os_get_abs_filename(infile_abs, sizeof(infile_abs), infile); else if (exefile != 0) os_get_abs_filename(infile_abs, sizeof(infile_abs), exefile); else infile_abs[0] = '\0'; os_get_path_name(infile_path, sizeof(infile_path), infile_abs); /* set up execution context */ runctx.runcxerr = ec; runctx.runcxmem = mctx; runctx.runcxstk = mystack; runctx.runcxstop = &mystack[stksiz]; runctx.runcxsp = mystack; runctx.runcxbp = mystack; runctx.runcxheap = myheap; runctx.runcxhp = myheap; runctx.runcxhtop = &myheap[heapsiz]; runctx.runcxundo = undoptr; runctx.runcxbcx = &bifctx; runctx.runcxbi = bif; runctx.runcxtio = (tiocxdef *)0; runctx.runcxdbg = &dbg; runctx.runcxvoc = &vocctx; runctx.runcxdmd = supcont; runctx.runcxdmc = &supctx; runctx.runcxext = 0; runctx.runcxgamename = infile; runctx.runcxgamepath = infile_path; /* set up setup context */ supctx.supcxerr = ec; supctx.supcxmem = mctx; supctx.supcxtab = (tokthdef *)0; supctx.supcxbuf = (uchar *)0; supctx.supcxlen = 0; supctx.supcxvoc = &vocctx; supctx.supcxrun = &runctx; /* set up debug context */ dbg.dbgcxtio = (tiocxdef *)0; dbg.dbgcxmem = mctx; dbg.dbgcxerr = ec; dbg.dbgcxtab = (tokthdef *)0; dbg.dbgcxfcn = 0; dbg.dbgcxdep = 0; dbg.dbgcxflg = 0; dbg.dbgcxlin = (lindef *)0; /* no line sources yet */ /* set up built-in function context */ CLRSTRUCT(bifctx); bifctx.bifcxerr = ec; bifctx.bifcxrun = &runctx; bifctx.bifcxtio = (tiocxdef *)0; bifctx.bifcxrnd = 0; bifctx.bifcxrndset = FALSE; bifctx.bifcxappctx = appctx; bifctx.bifcxsafetyr = safety_read; bifctx.bifcxsafetyw = safety_write; bifctx.bifcxsavext = save_ext; /* initialize the regular expression parser context */ re_init(&bifctx.bifcxregex, ec); /* add the built-in functions, keywords, etc */ supbif(&supctx, bif, (int)(sizeof(bif)/sizeof(bif[0]))); /* set up status line hack */ runistat(&vocctx, &runctx, (tiocxdef *)0); /* turn on the "busy" cursor before loading */ os_csr_busy(TRUE); /* read the game from the binary file */ fiord(mctx, &vocctx, (struct tokcxdef *)0, infile, exefile, &fiolctx, &preinit, &flags, (struct tokpdef *)0, (uchar **)0, (uint *)0, (uint *)0, (preload ? 2 : 0), appctx, argv[0]); loadopen = TRUE; /* turn off the "busy" cursor */ os_csr_busy(FALSE); /* play the game */ plygo(&runctx, &vocctx, (tiocxdef *)0, preinit, restore_file); /* close load file */ fiorcls(&fiolctx); if (pause) { trdptf("[press a key to exit]"); os_waitc(); trdptf("\n"); } /* close and delete swapfile, if one was opened */ trd_close_swapfile(&runctx); /* make sure the script file is closed, if we have one */ qasclose(); ERRCLEAN(ec) /* close and delete swapfile, if one was opened */ trd_close_swapfile(&runctx); /* close the load file if one was opened */ if (loadopen) fiorcls(&fiolctx); /* vocctx is going out of scope - forget the global reference to it */ main_voc_ctx = 0; /* delete the voc context */ vocterm(&vocctx); /* delete the undo context */ if (undoptr != 0) objuterm(undoptr); /* release the object cache structures */ if (mctx != 0) mcmcterm(mctx); if (globalctx != 0) mcmterm(globalctx); ERRENDCLN(ec) /* vocctx is going out of scope - forget the global reference to it */ main_voc_ctx = 0; /* delete the voc contxt */ vocterm(&vocctx); /* delete the undo context */ if (undoptr != 0) objuterm(undoptr); /* release the object cache structures */ mcmcterm(mctx); mcmterm(globalctx); } /* * If the OS configuration so desires, use a less technical format for * run-time error messages by leaving out the numeric error code. Note * that we'll only do this if the error messages are linked directly * into the run-time, since we need the numeric code as a last resort * when the error message may not be present. */ #ifdef OS_SKIP_ERROR_CODES # ifdef ERR_LINK_MESSAGES # define TRDLOGERR_PREFIX "\n[An error has occurred within TADS: " # endif #endif /* * If we didn't define a different error prefix format, use the default * format with the numeric error code. */ #ifndef TRDLOGERR_PREFIX # define TRDLOGERR_PREFIX "\n[%s-%d: " #endif /* log an error */ static void trdlogerr(void *ctx0, char *fac, int err, int argc, erradef *argv) { errcxdef *ctx = (errcxdef *)ctx0; char buf[256]; char msg[256]; /* display the prefix message to the console and log file */ sprintf(buf, TRDLOGERR_PREFIX, fac, err); trdptf("%s", buf); out_logfile_print(buf, FALSE); /* display the error message text to the console and log file */ errmsg(ctx, msg, (uint)sizeof(msg), err); errfmt(buf, (int)sizeof(buf), msg, argc, argv); trdptf("%s]\n", buf); out_logfile_print(buf, FALSE); out_logfile_print("]", TRUE); } /* * close and delete the swap file */ void trd_close_swapfile(runcxdef *runctx) { extern voccxdef *main_voc_ctx; mcmcxdef *mctx; mcmcx1def *globalctx; mcscxdef *mcsctx; /* if no run context was supplied, find it from the main voc context */ if (runctx == 0) { /* if there is no main voc context, we're out of luck */ if (main_voc_ctx == 0) return; /* get the run context */ runctx = main_voc_ctx->voccxrun; } /* get the other relevant contexts */ mctx = runctx->runcxmem; globalctx = mctx->mcmcxgl; mcsctx = &globalctx->mcmcxswc; /* if we have a swap file open, close it */ if (mcsctx->mcscxfp != 0) { /* close the file */ osfcls(mcsctx->mcscxfp); /* forget about the file, so we don't try to close it again */ mcsctx->mcscxfp = (osfildef *)0; } /* if we have a filename, delete the file */ if (mcsctx->mcscxfname != 0) { /* delete the file */ osfdel_temp(mcsctx->mcscxfname); /* forget the filename, so we don't try to delete the file again */ mchfre(mcsctx->mcscxfname); mcsctx->mcscxfname = 0; } } /* main - called by os main after setting up arguments */ int trdmain(int argc, char *argv[], appctxdef *appctx, char *save_ext) { errcxdef errctx; int err; osfildef *fp; errctx.errcxlog = trdlogerr; errctx.errcxlgc = &errctx; errctx.errcxfp = (osfildef *)0; errctx.errcxofs = 0; errctx.errcxappctx = appctx; fp = oserrop(argv[0]); errini(&errctx, fp); /* copyright-date-string */ #ifndef NO_T2_COPYRIGHT_NOTICE trdptf("%s - A %s TADS %s Interpreter.\n", G_tads_oem_app_name, G_tads_oem_display_mode, TADS_RUNTIME_VERSION); trdptf("%sopyright (c) 1993, 2012 by Michael J. Roberts.\n", G_tads_oem_copyright_prefix ? "TADS c" : "C"); trdptf("%s\n", G_tads_oem_author); #endif ERRBEGIN(&errctx) trdmain1(&errctx, argc, argv, appctx, save_ext); ERRCATCH(&errctx, err) /* * log the error, unless it's usage (in which case we logged it * already) or we're simply quitting the game */ if (err != ERR_USAGE && err != ERR_RUNQUIT) errclog(&errctx); /* close the error file */ if (errctx.errcxfp != 0) osfcls(errctx.errcxfp); /* pause before exiting if the OS desires it */ os_expause(); /* return failure unless we're simply quitting the game */ return (err == ERR_RUNQUIT ? OSEXSUCC : OSEXFAIL); ERREND(&errctx) /* close the error file if we opened it */ if (errctx.errcxfp != 0) osfcls(errctx.errcxfp); /* successful completion */ return(OSEXSUCC); } qtads-2.1.7/tads2/trd.h000066400000000000000000000045601265017072300146240ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/trd.h,v 1.6 1999/07/11 00:46:35 MJRoberts Exp $ */ /* * Copyright (c) 1997, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name trd.h - TADS runtime application definitions Function Defines structures and functions related to the TADS runtime application Notes Modified 04/11/99 CNebel - Move appctx definition to its own header. 11/25/97 MJRoberts - Creation */ #ifndef TRD_H #define TRD_H #include "os.h" #include "appctx.h" #ifdef __cplusplus extern "C" { #endif /* forward-declare structure types */ struct runcxdef; /* * Run-time version number */ #define TADS_RUNTIME_VERSION "2.5.17" /* * Main run-time subsystem entrypoint. Runs the game specified in the * argument vector; does not return until the game terminates. The * application container context is optional; pass null if no context is * required. */ int trdmain(int argc, char **argv, appctxdef *appctx, char *save_ext); /* * Main debugger subsystem entrypoint. Works like trdmain(), but starts * the game under the debugger. */ int tddmain(int argc, char **argv, appctxdef *appctx, char *save_ext); /* * close and delete the swap file */ void trd_close_swapfile(struct runcxdef *runctx); #ifdef __cplusplus } #endif /* * Define default memory sizes if no one else has. */ #ifndef TRD_HEAPSIZ # define TRD_HEAPSIZ 4096 #endif #ifndef TRD_STKSIZ # define TRD_STKSIZ 200 #endif #ifndef TRD_UNDOSIZ # define TRD_UNDOSIZ (16 * 1024) #endif #ifndef TDD_HEAPSIZ # define TDD_HEAPSIZ 4096 #endif #ifndef TDD_STKSIZ # define TDD_STKSIZ 200 #endif #ifndef TDD_UNDOSIZ # define TDD_UNDOSIZ (16 * 1024) #endif #ifndef TDD_POOLSIZ # define TDD_POOLSIZ (2 * 1024) #endif #ifndef TDD_LCLSIZ # define TDD_LCLSIZ 0 #endif /* * If the OS headers haven't defined any system-specific option usage * messages, set up a dummy list. The usage display routine will show * messages starting from the lower number up to and including the higher * number; by default we'll make the ending number lower than the starting * number so that we don't display any messages at all. */ #ifndef ERR_TRUS_OS_FIRST # define ERR_TRUS_OS_FIRST 100 # define ERR_TRUS_OS_LAST 99 #endif #endif /* TRD_H */ qtads-2.1.7/tads2/voc.c000066400000000000000000000671351265017072300146240ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/VOC.C,v 1.4 1999/07/11 00:46:31 MJRoberts Exp $"; #endif /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name voc.c - vocabulary functions Function Player command parser vocabulary word functions Notes None Modified 04/11/99 CNebel - Fix signing errors. 11/07/91 MJRoberts - creation */ #include #include #include "os.h" #include "std.h" #include "mch.h" #include "mcm.h" #include "voc.h" #include "prp.h" #include "obj.h" #include "err.h" /* * Main vocabulary context. This can be saved globally if desired, so * that routines that don't have any other access to it (such as * Unix-style signal handlers) can reach it. */ voccxdef *main_voc_ctx = 0; #ifdef VOCW_IN_CACHE vocwdef *vocwget(voccxdef *ctx, uint idx) { uint pg; if (idx == VOCCXW_NONE) return 0; /* get the page we need */ pg = idx/VOCWPGSIZ; /* if it's not locked, lock it */ if (pg != ctx->voccxwplck) { /* unlock the old page */ if (ctx->voccxwplck != MCMONINV) mcmunlck(ctx->voccxmem, ctx->voccxwp[ctx->voccxwplck]); /* lock the new page */ ctx->voccxwpgptr = (vocwdef *)mcmlck(ctx->voccxmem, ctx->voccxwp[pg]); ctx->voccxwplck = pg; } /* return the entry on that page */ return ctx->voccxwpgptr + (idx % VOCWPGSIZ); } #endif /*VOCW_IN_CACHE */ /* hash value is based on first 6 characters only to allow match-in-6 */ uint vochsh(uchar *t, int len) { uint ret = 0; if (len > 6) len = 6; for ( ; len ; --len, ++t) ret = (ret + (uint)(vocisupper(*t) ? tolower(*t) : *t)) & (VOCHASHSIZ - 1); return(ret); } /* copy vocabulary word, and convert to lower case */ static void voccpy(uchar *dst, uchar *src, int len) { for ( ; len ; --len, ++dst, ++src) *dst = vocisupper(*src) ? tolower(*src) : *src; } /* allocate and set up a new vocwdef record, linking into a vocdef's list */ static void vocwset(voccxdef *ctx, vocdef *v, prpnum p, objnum objn, int classflg) { vocwdef *vw; uint inx; vocwdef *vw2; /* * look through the vocdef list to see if there's an existing entry * with the DELETED marker -- if so, simply undelete it */ for (inx = v->vocwlst, vw = vocwget(ctx, inx) ; vw ; inx = vw->vocwnxt, vw = vocwget(ctx, inx)) { /* if this entry was deleted, and otherwise matches, undelete it */ if ((vw->vocwflg & VOCFDEL) && vw->vocwobj == objn && vw->vocwtyp == p) { /* * Remove the deleted flag. We will otherwise leave the * flags unchanged, since the VOCFDEL flag applies only to * statically allocated objects, and hence the original * flags should take precedence over any run-time flags. */ vw->vocwflg &= ~VOCFDEL; /* we're done */ return; } } /* make sure the word+object+type record isn't already defined */ for (inx = v->vocwlst, vw = vocwget(ctx, inx) ; vw ; inx = vw->vocwnxt, vw = vocwget(ctx, inx)) { if (vw->vocwobj == objn && vw->vocwtyp == p && (vw->vocwflg & VOCFCLASS) == (classflg & VOCFCLASS)) { /* it matches - don't add a redundant record */ return; } } /* look in the free list for an available vocwdef */ if (ctx->voccxwfre != VOCCXW_NONE) { inx = ctx->voccxwfre; vw = vocwget(ctx, inx); /* get the free vocwdef */ ctx->voccxwfre = vw->vocwnxt; /* unlink from free list */ } else { /* allocate another page of vocwdef's if necssary */ if ((ctx->voccxwalocnt % VOCWPGSIZ) == 0) { int pg = ctx->voccxwalocnt / VOCWPGSIZ; /* make sure we haven't exceeded the available page count */ if (pg >= VOCWPGMAX) errsig(ctx->voccxerr, ERR_VOCMNPG); /* allocate on the new page */ #ifdef VOCW_IN_CACHE mcmalo(ctx->voccxmem, (ushort)(VOCWPGSIZ * sizeof(vocwdef)), &ctx->voccxwp[pg]); mcmunlck(ctx->voccxmem, ctx->voccxwp[pg]); #else ctx->voccxwp[pg] = (vocwdef *)mchalo(ctx->voccxerr, (VOCWPGSIZ * sizeof(vocwdef)), "vocwset"); #endif } /* get the next entry, and increment count of used entries */ inx = ctx->voccxwalocnt++; vw = vocwget(ctx, inx); } /* link the new vocwdef into the vocdef's relation list */ vw->vocwnxt = v->vocwlst; v->vocwlst = inx; /* set up the new vocwdef */ vw->vocwtyp = (uchar)p; vw->vocwobj = objn; vw->vocwflg = classflg; /* * Scan the list and make sure we're not adding a redundant verb. * Don't bother with the warning if this is a class. */ if (p == PRP_VERB && (ctx->voccxflg & VOCCXFVWARN) && (vw->vocwflg & VOCFCLASS) == 0) { for (vw2 = vocwget(ctx, v->vocwlst) ; vw2 ; vw2 = vocwget(ctx, vw2->vocwnxt)) { /* * if this is a different object, and it's not a class, and * it's defined as a verb, warn about it */ if (vw2 != vw && (vw2->vocwflg & VOCFCLASS) == 0 && vw2->vocwtyp == PRP_VERB) { if (v->vocln2 != 0) errlog2(ctx->voccxerr, ERR_VOCREVB, ERRTSTR, errstr(ctx->voccxerr, (char *)v->voctxt, v->voclen), ERRTSTR, errstr(ctx->voccxerr, (char *)v->voctxt + v->voclen, v->vocln2)); else errlog1(ctx->voccxerr, ERR_VOCREVB, ERRTSTR, errstr(ctx->voccxerr, (char *)v->voctxt, v->voclen)); break; } } } } /* set up a vocdef record, and link into hash table */ static void vocset(voccxdef *ctx, vocdef *v, prpnum p, objnum objn, int classflg, uchar *wrdtxt, int len, uchar *wrd2, int len2) { uint hshval = vochsh(wrdtxt, len); v->vocnxt = ctx->voccxhsh[hshval]; ctx->voccxhsh[hshval] = v; v->voclen = len; v->vocln2 = len2; voccpy(v->voctxt, wrdtxt, len); if (wrd2) voccpy(v->voctxt + len, wrd2, len2); /* allocate and initialize a vocwdef for the object */ vocwset(ctx, v, p, objn, classflg); } /* internal addword - already parsed into two words and have lengths */ void vocadd2(voccxdef *ctx, prpnum p, objnum objn, int classflg, uchar *wrdtxt, int len, uchar *wrd2, int len2) { vocdef *v; vocdef *prv; uint need; uint hshval; /* if the word is null, ignore it entirely */ if (len == 0 && len2 == 0) return; /* look for a vocdef entry with the same word text */ hshval = vochsh(wrdtxt, len); for (v = ctx->voccxhsh[hshval] ; v ; v = v->vocnxt) { /* if it matches on both words, use this entry */ if (v->voclen == len && !memcmp(wrdtxt, v->voctxt, (size_t)len) && ((!wrd2 && v->vocln2 == 0) || (v->vocln2 == len2 && !memcmp(wrd2, v->voctxt + len, (size_t)len2)))) { vocwset(ctx, v, p, objn, classflg); return; } } /* look for a free vocdef entry of the same size */ for (prv = (vocdef *)0, v = ctx->voccxfre ; v ; prv = v, v = v->vocnxt) if (v->voclen == len + len2) break; if (v) { /* we found something - unlink from free list */ if (prv) prv->vocnxt = v->vocnxt; else ctx->voccxfre = v->vocnxt; /* reuse the entry */ v->vocwlst = VOCCXW_NONE; vocset(ctx, v, p, objn, classflg, wrdtxt, len, wrd2, len2); return; } /* didn't find an existing vocdef; allocate a new one */ need = sizeof(vocdef) + len + len2 - 1; if (ctx->voccxrem < need) { /* not enough space in current page; allocate a new one */ ctx->voccxpool = mchalo(ctx->voccxerr, VOCPGSIZ, "vocadd2"); ctx->voccxrem = VOCPGSIZ; } /* use top of current pool, and update pool pointer and size */ v = (vocdef *)ctx->voccxpool; need = osrndsz(need); ctx->voccxpool += need; if (ctx->voccxrem > need) ctx->voccxrem -= need; else ctx->voccxrem = 0; /* set up new vocdef */ v->vocwlst = VOCCXW_NONE; vocset(ctx, v, p, objn, classflg, wrdtxt, len, wrd2, len2); } static void voc_parse_words(char **wrdtxt, int *len, char **wrd2, int *len2) { /* get length and pointer to actual text */ *len = osrp2(*wrdtxt) - 2; *wrdtxt += 2; /* see if there's a second word - look for a space */ for (*wrd2 = *wrdtxt, *len2 = *len ; *len2 && !vocisspace(**wrd2) ; ++*wrd2, --*len2) ; if (*len2) { *len -= *len2; while (*len2 && vocisspace(**wrd2)) ++*wrd2, --*len2; } else { /* no space ==> no second word */ *wrd2 = (char *)0; } } void vocadd(voccxdef *ctx, prpnum p, objnum objn, int classflg, char *wrdtxt) { int len; char *wrd2; int len2; voc_parse_words(&wrdtxt, &len, &wrd2, &len2); vocadd2(ctx, p, objn, classflg, (uchar *)wrdtxt, len, (uchar *)wrd2, len2); } /* make sure we have a page table entry for an object, allocating one if not */ void vocialo(voccxdef *ctx, objnum obj) { if (!ctx->voccxinh[obj >> 8]) { ctx->voccxinh[obj >> 8] = (vocidef **)mchalo(ctx->voccxerr, (256 * sizeof(vocidef *)), "vocialo"); memset(ctx->voccxinh[obj >> 8], 0, (size_t)(256 * sizeof(vocidef *))); } } /* add an inheritance/location record */ void vociadd(voccxdef *ctx, objnum obj, objnum loc, int numsc, objnum *sc, int flags) { vocidef *v; vocidef *min; vocidef *prv; vocidef *minprv; /* make sure we have a page table entry for this object */ vocialo(ctx, obj); /* look in free list for an entry that's big enough */ for (prv = (vocidef *)0, min = (vocidef *)0, v = ctx->voccxifr ; v ; prv = v, v = v->vocinxt) { if (v->vocinsc == numsc) { min = v; minprv = prv; break; } else if (v->vocinsc > numsc) { if (!min || v->vocinsc < min->vocinsc) { min = v; minprv = prv; } } } if (!min) { uint need; /* nothing in free list; allocate a new entry */ need = osrndsz(sizeof(vocidef) + (numsc - 1)*sizeof(objnum)); if (ctx->voccxilst + need >= VOCISIZ) { /* nothing left on current page; allocate a new page */ ctx->voccxip[++(ctx->voccxiplst)] = mchalo(ctx->voccxerr, VOCISIZ, "vociadd"); ctx->voccxilst = 0; } /* allocate space out of current page */ v = (vocidef *)(ctx->voccxip[ctx->voccxiplst] + ctx->voccxilst); ctx->voccxilst += need; } else { /* unlink from chain and use */ v = min; if (minprv) minprv->vocinxt = v->vocinxt; else ctx->voccxifr = v->vocinxt; } /* set up the entry */ if (vocinh(ctx, obj) != (vocidef *)0) errsig(ctx->voccxerr, ERR_VOCINUS); v->vociloc = loc; v->vociilc = MCMONINV; v->vociflg = (flags & ~VOCIFXLAT); v->vocinsc = numsc; if (numsc) { if (flags & VOCIFXLAT) { int i; for (i = 0 ; i < numsc ; ++i) v->vocisc[i] = osrp2(&sc[i]); } else memcpy(v->vocisc, sc, (size_t)(numsc * sizeof(objnum))); } vocinh(ctx, obj) = v; /* set page table entry */ } /* revert all objects to original state, using inheritance records */ void vocrevert(voccxdef *vctx) { vocidef ***vpg; vocidef **v; int i; int j; objnum obj; /* * Go through the inheritance records. Delete each dynamically * allocated object, and revert each static object to its original * load state. */ for (vpg = vctx->voccxinh, i = 0 ; i < VOCINHMAX ; ++vpg, ++i) { if (!*vpg) continue; for (v = *vpg, obj = (i << 8), j = 0 ; j < 256 ; ++v, ++obj, ++j) { if (*v) { /* if the object was dynamically allocated, delete it */ if ((*v)->vociflg & VOCIFNEW) { /* delete vocabulary and inheritance data for the object */ vocidel(vctx, obj); vocdel(vctx, obj); /* delete the object */ mcmfre(vctx->voccxmem, (mcmon)obj); } else { /* revert the object */ mcmrevert(vctx->voccxmem, (mcmon)obj); } } } } /* * Revert the vocabulary list: delete all newly added words, and * undelete all original words marked as deleted. */ vocdel1(vctx, MCMONINV, (char *)0, 0, TRUE, TRUE, FALSE); } /* initialize voc context */ void vocini(voccxdef *vocctx, errcxdef *errctx, mcmcxdef *memctx, runcxdef *runctx, objucxdef *undoctx, int fuses, int daemons, int notifiers) { CLRSTRUCT(*vocctx); vocctx->voccxerr = errctx; vocctx->voccxiplst = (uint)-1; vocctx->voccxilst = VOCISIZ; vocctx->voccxmem = memctx; vocctx->voccxrun = runctx; vocctx->voccxundo = undoctx; vocctx->voccxme = vocctx->voccxme_init = vocctx->voccxvtk = vocctx->voccxstr = vocctx->voccxnum = vocctx->voccxit = vocctx->voccxhim = vocctx->voccxprd = vocctx->voccxpre = vocctx->voccxpre2 = vocctx->voccxppc = vocctx->voccxlsv = vocctx->voccxpreinit = vocctx->voccxper = vocctx->voccxprom = vocctx->voccxpostprom = vocctx->voccxpdis = vocctx->voccxper2 = vocctx->voccxperp = vocctx->voccxpdef = vocctx->voccxpdef2 = vocctx->voccxpask = vocctx->voccxpask2 = vocctx->voccxpask3 = vocctx->voccxinitrestore = vocctx->voccxpuv = vocctx->voccxpnp = vocctx->voccxpostact = vocctx->voccxendcmd = vocctx->voccxher = MCMONINV; vocctx->voccxthc = 0; #ifdef VOCW_IN_CACHE vocctx->voccxwplck = MCMONINV; #endif vocctx->voccxactor = MCMONINV; vocctx->voccxverb = MCMONINV; vocctx->voccxprep = MCMONINV; vocctx->voccxdobj = 0; vocctx->voccxiobj = 0; vocctx->voccxunknown = 0; vocctx->voccxlastunk = 0; vocctx->voc_stk_ptr = 0; vocctx->voc_stk_cur = 0; vocctx->voc_stk_end = 0; /* allocate fuses, daemons, notifiers */ vocinialo(vocctx, &vocctx->voccxfus, (vocctx->voccxfuc = fuses)); vocinialo(vocctx, &vocctx->voccxdmn, (vocctx->voccxdmc = daemons)); vocinialo(vocctx, &vocctx->voccxalm, (vocctx->voccxalc = notifiers)); /* no entries in vocwdef free list yet */ vocctx->voccxwfre = VOCCXW_NONE; } /* uninitialize the voc context */ void vocterm(voccxdef *ctx) { /* delete the fuses, daemons, and notifiers */ voctermfree(ctx->voccxfus); voctermfree(ctx->voccxdmn); voctermfree(ctx->voccxalm); /* delete the private stack */ if (ctx->voc_stk_ptr != 0) mchfre(ctx->voc_stk_ptr); } /* clean up the vocab context */ void voctermfree(vocddef *what) { if (what != 0) mchfre(what); } /* * Iterate through all words for a particular object, calling a * function with each vocwdef found. If objn == MCMONINV, we'll call * the callback for every word. */ void voc_iterate(voccxdef *ctx, objnum objn, void (*fn)(void *, vocdef *, vocwdef *), void *fnctx) { int i; vocdef *v; vocdef **vp; vocwdef *vw; uint idx; /* go through each hash value looking for matching words */ for (i = VOCHASHSIZ, vp = ctx->voccxhsh ; i ; ++vp, --i) { /* go through all words in this hash chain */ for (v = *vp ; v ; v = v->vocnxt) { /* go through each object relation for this word */ for (idx = v->vocwlst, vw = vocwget(ctx, idx) ; vw ; idx = vw->vocwnxt, vw = vocwget(ctx, idx)) { /* * if this word is for this object, call the callback */ if (objn == MCMONINV || vw->vocwobj == objn) (*fn)(fnctx, v, vw); } } } } /* callback context for voc_count */ struct voc_count_ctx { int cnt; int siz; prpnum prp; }; /* callback for voc_count */ static void voc_count_cb(void *ctx0, vocdef *voc, vocwdef *vocw) { struct voc_count_ctx *ctx = (struct voc_count_ctx *)ctx0; VARUSED(vocw); /* * If it matches the property (or we want all properties), count * it. Don't count deleted objects. */ if ((ctx->prp == 0 || ctx->prp == vocw->vocwtyp) && !(vocw->vocwflg & VOCFDEL)) { /* count the word */ ctx->cnt++; /* count the size */ ctx->siz += voc->voclen + voc->vocln2; } } /* * Get the number and size of words defined for an object. The size * returns the total byte count from all the words involved. Do not * include deleted words in the count. */ void voc_count(voccxdef *ctx, objnum objn, prpnum prp, int *cnt, int *siz) { struct voc_count_ctx fnctx; /* set up the context with zero initial counts */ fnctx.cnt = 0; fnctx.siz = 0; fnctx.prp = prp; /* iterate over all words for the object with our callback */ voc_iterate(ctx, objn, voc_count_cb, &fnctx); /* return the data */ if (cnt) *cnt = fnctx.cnt; if (siz) *siz = fnctx.siz; } /* * Delete a particular word associated with an object, or all words if * the word pointer is null. If the word isn't marked as added at * runtime (i.e., the VOCFNEW flag is not set for the word), the word is * simply marked as deleted, rather than being actually deleted. * However, if the 'really_delete' flag is set, the word is actually * deleted. If the 'revert' flag is true, this routine deletes _every_ * dynamically created word, and undeletes all dynamically deleted words * that were in the original vocabulary. */ void vocdel1(voccxdef *ctx, objnum objn, char *wrd1, prpnum prp, int really_delete, int revert, int keep_undo) { int i; vocdef *v; vocdef *prv; vocdef *nxt; vocdef **vp; vocwdef *vw; vocwdef *prvw; vocwdef *nxtw; uint nxtidx; uint idx; int deleted_vocdef; char *wrd2; int len1, len2; int do_del; char *orgwrd; /* parse the word if provided */ orgwrd = wrd1; if (wrd1) voc_parse_words(&wrd1, &len1, &wrd2, &len2); /* go through each hash value looking for matching words */ for (i = VOCHASHSIZ, vp = ctx->voccxhsh ; i ; ++vp, --i) { /* go through all words in this hash chain */ for (prv = (vocdef *)0, v = *vp ; v ; v = nxt) { /* remember next word in hash chain */ nxt = v->vocnxt; /* if this word doesn't match, skip it */ if (wrd1) { /* compare the first word */ if (v->voclen != len1 || memicmp((char *)v->voctxt, wrd1, (size_t)len1)) { prv = v; continue; } /* if there's a second word, compare it as well */ if (wrd2 && (v->vocln2 != len2 || memicmp((char *)v->voctxt + len1, wrd2, (size_t)len2))) { prv = v; continue; } } /* presume we're not going to delete this vocdef */ deleted_vocdef = FALSE; /* go through all object relations for this word */ for (prvw = 0, idx = v->vocwlst, vw = vocwget(ctx, idx) ; vw ; vw = nxtw, idx = nxtidx) { /* remember next word in relation list */ nxtidx = vw->vocwnxt; nxtw = vocwget(ctx, nxtidx); /* * figure out whether to delete this word, based on the * caller's specified operating mode */ if (revert) { /* if reverting, delete all new words */ do_del = (vw->vocwflg & VOCFNEW); /* also, remove the DELETED flag if present */ vw->vocwflg &= ~VOCFDEL; } else { /* * delete the word if the object number matches, * AND either we're not searching for a specific * vocabulary word (in which case wrd1 will be null) * or the word matches the vocabulary word we're * seeking (in which case the part of speech must * match) */ do_del = (vw->vocwobj == objn && (wrd1 == 0 || vw->vocwtyp == prp)); /* * if we're not in really_delete mode, and the word * matches and it wasn't dynamically added at * run-time, simply mark it as deleted -- this will * allow it to be undeleted if the game is reverted * to RESTART conditions */ if (do_del && !really_delete && !(vw->vocwflg & VOCFNEW)) { /* geneate undo for the operation */ if (keep_undo && orgwrd) vocdusave_delwrd(ctx, objn, prp, vw->vocwflg, orgwrd); /* just mark the word for deletion, but keep vw */ vw->vocwflg |= VOCFDEL; do_del = FALSE; } } /* now delete the structure if we decided we should */ if (do_del) { /* geneate undo for the operation */ if (keep_undo && orgwrd) vocdusave_delwrd(ctx, objn, prp, vw->vocwflg, orgwrd); /* unlink this vocwdef from the vocdef's list */ if (prvw) prvw->vocwnxt = vw->vocwnxt; else v->vocwlst = vw->vocwnxt; /* link the vocwdef into the vocwdef free list */ vw->vocwnxt = ctx->voccxwfre; ctx->voccxwfre = idx; /* * if there's nothing left in the vocdef's list, * delete the entire vocdef as well */ if (v->vocwlst == VOCCXW_NONE) { if (prv) prv->vocnxt = v->vocnxt; else *vp = v->vocnxt; /* link into free chain */ v->vocnxt = ctx->voccxfre; ctx->voccxfre = v; /* note that it's been deleted */ deleted_vocdef = TRUE; } } else { /* we're not deleting the word, so move prvw forward */ prvw = vw; } } /* if we didn't delete this vocdef, move prv forward onto it */ if (!deleted_vocdef) prv = v; } } } /* delete all vocabulary for an object */ void vocdel(voccxdef *ctx, objnum objn) { vocdel1(ctx, objn, (char *)0, (prpnum)0, TRUE, FALSE, FALSE); } /* delete object inheritance records for a particular object */ void vocidel(voccxdef *ctx, objnum obj) { vocidef *v; /* get entry out of page table, and clear page table slot */ v = vocinh(ctx, obj); vocinh(ctx, obj) = (vocidef *)0; /* link into free list */ if (v) { v->vocinxt = ctx->voccxifr; ctx->voccxifr = v; } } /* * Find template matching a verb+object+prep combination; return TRUE * if a suitable template is found, FALSE otherwise. */ int voctplfnd(voccxdef *ctx, objnum verb_in, objnum prep, uchar *tplout, int *newstyle) { uchar *tplptr; uchar *thistpl; int found; int tplcnt; uint tplofs; objnum verb; /* look for a new-style template first */ tplofs = objgetap(ctx->voccxmem, verb_in, PRP_TPL2, &verb, FALSE); if (tplofs) { /* flag the presence of the new-style template */ *newstyle = TRUE; } else { /* no new-style template - look for old version */ tplofs = objgetap(ctx->voccxmem, verb_in, PRP_TPL, &verb, FALSE); *newstyle = FALSE; } /* inherit templates until we run out of them */ for (;;) { /* if we found something already, use it */ if (tplofs) { size_t siz; /* figure the size of this template style */ siz = (*newstyle ? VOCTPL2SIZ : VOCTPLSIZ); /* lock the verb object, and get the property value pointer */ tplptr = mcmlck(ctx->voccxmem, verb); thistpl = prpvalp(tplptr + tplofs); /* first byte is number of templates in array */ tplcnt = *thistpl++; /* look for a template that matches the preposition object */ for (found = FALSE ; tplcnt ; thistpl += siz, --tplcnt) { if (voctplpr(thistpl) == prep) { found = TRUE; break; } } /* unlock the object and return the value if we found one */ mcmunlck(ctx->voccxmem, verb); if (found) { memcpy(tplout, thistpl, siz); return(TRUE); } } /* try inheriting a template (new-style, then old-style) */ tplofs = objgetap(ctx->voccxmem, verb_in, PRP_TPL2, &verb, TRUE); if (tplofs) *newstyle = TRUE; else { tplofs = objgetap(ctx->voccxmem, verb_in, PRP_TPL, &verb, TRUE); *newstyle = FALSE; } /* return not-found if we couldn't inherit it */ if (!tplofs) return FALSE; /* use the newly found verb */ verb_in = verb; } } /* * Set the "Me" object */ void voc_set_me(voccxdef *ctx, objnum new_me) { /* save undo for the change */ vocdusave_me(ctx, ctx->voccxme); /* set the new "Me" object */ ctx->voccxme = new_me; } qtads-2.1.7/tads2/voc.h000066400000000000000000001010441265017072300146150ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/TADS2/VOC.H,v 1.4 1999/07/11 00:46:31 MJRoberts Exp $ */ /* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name voc.h - vocabulary definitions Function Defines TADS vocabulary (player command parser) functionality Notes None Modified 11/07/91 MJRoberts - creation */ #ifndef VOC_INCLUDED #define VOC_INCLUDED #ifndef ERR_INCLUDED #include "err.h" #endif #ifndef OBJ_INCLUDED #include "obj.h" #endif #ifndef PRP_INCLUDED #include "prp.h" #endif #ifndef TIO_INCLUDED #include "tio.h" #endif #ifndef RUN_INCLUDED #include "run.h" #endif #include /* * Cover macro for parser errors. Any parser error should be covered * with this macro for documentation and search purposes. (The macro * doesn't do anything - this is just something to search for when we're * trying to enumerate parser error codes.) */ #define VOCERR(errcode) errcode /* maximum number of objects matching an ambiguous word */ #define VOCMAXAMBIG 200 /* size of input buffer */ #define VOCBUFSIZ 128 /* * Vocabulary relation structure - this structure relates a vocabulary * word to an object and part of speech. A list of these structures is * attached to each vocabulary word structure to provide the word's * meanings. */ typedef struct vocwdef vocwdef; struct vocwdef { uint vocwnxt; /* index of next vocwdef attached to the same word */ objnum vocwobj; /* object associated with the word */ uchar vocwtyp; /* property associated with the word (part of speech) */ uchar vocwflg; /* flags for the word */ #define VOCFCLASS 1 /* word is for a class object */ #define VOCFINH 2 /* word is inherited from a superclass */ #define VOCFNEW 4 /* word was added at run-time */ #define VOCFDEL 8 /* word has been deleted */ }; /* vocabulary word structure */ typedef struct vocdef vocdef; struct vocdef { vocdef *vocnxt; /* next word at same hash value */ uchar voclen; /* length of the word */ uchar vocln2; /* length of second word (0 if no second word) */ uint vocwlst; /* head of list of vocwdef's attached to the word */ uchar voctxt[1]; /* text of the word */ }; /* vocabulary inheritance cell */ typedef struct vocidef vocidef; struct vocidef { uchar vocinsc; /* # of superclasses (gives size of record) */ union { struct { uchar vociusflg; /* flags for entry */ #define VOCIFCLASS 1 /* entry refers to a class object (loc records only) */ #define VOCIFVOC 2 /* entry has vocabulary words defined */ #define VOCIFXLAT 4 /* superclasses must be translated from portable fmt */ #define VOCIFLOCNIL 8 /* location is explicitly set to nil */ #define VOCIFNEW 16 /* object was allocated at run-time with "new" */ objnum vociusloc; /* location of the object */ objnum vociusilc; /* inherited location */ objnum vociussc[1]; /* array of superclasses */ } vocius; vocidef *vociunxt; } vociu; # define vociflg vociu.vocius.vociusflg # define vociloc vociu.vocius.vociusloc # define vociilc vociu.vocius.vociusilc # define vocisc vociu.vocius.vociussc # define vocinxt vociu.vociunxt }; /* size of a page in a vocabulary pool */ #define VOCPGSIZ 8192 /* number of bytes in an inheritance cell page */ #define VOCISIZ 8192 /* maximum number of inheritance pages */ #define VOCIPGMAX 32 /* maximum number of inheritance pages (256 objects per page) */ #define VOCINHMAX 128 /* size of vocabulary hash table */ #define VOCHASHSIZ 256 /* size of a template structure */ #define VOCTPLSIZ 10 /* new-style template structure */ #define VOCTPL2SIZ 16 /* * vocwdef's are fixed in size. They're allocated in a set of arrays * (the voccxwp member of the voc context has the list of arrays). Each * array is of a fixed number of vocwdef entries; a maximum number of * vocwdef arrays is possible. */ #define VOCWPGSIZ 2000 /* number of vocwdef's per array */ #define VOCWPGMAX 16 /* maximum number of vocwdef arrays */ /* * To find a vocwdef entry given its index, divide the index by the * number of entries per array to find the array number, and use the * remainder to find the index within that array. */ /*#define VOCW_IN_CACHE*/ #ifdef VOCW_IN_CACHE vocwdef *vocwget(struct voccxdef *ctx, uint idx); #else #define vocwget(ctx, idx) \ ((idx) == VOCCXW_NONE ? (vocwdef *)0 : \ ((ctx)->voccxwp[(idx)/VOCWPGSIZ] + ((idx) % VOCWPGSIZ))) #endif /* * Special values for vocdtim - these values indicate that the daemon * does not have a normal turn-based expiration time. */ #define VOCDTIM_EACH_TURN 0xffff /* the daemon fires every turn */ /* daemon/fuse/alarm slot */ struct vocddef { objnum vocdfn; /* object number of function to be invoked */ runsdef vocdarg; /* argument for daemon/fuse function */ prpnum vocdprp; /* property number (used only for alarms) */ uint vocdtim; /* time for fuses/alarms (0xffff -> each-turn alarm) */ }; typedef struct vocddef vocddef; /* vocabulary object list entry */ struct vocoldef { objnum vocolobj; /* object matching the word */ char *vocolfst; /* first word in cmd[] that identified object */ char *vocollst; /* last word in cmd[] that identified object */ char *vocolhlst; /* hypothetical last word, if we trimmed a prep */ int vocolflg; /* special flags (ALL, etc) */ }; typedef struct vocoldef vocoldef; /* vocabulary context */ struct voccxdef { errcxdef *voccxerr; /* error handling context */ tiocxdef *voccxtio; /* text i/o context */ runcxdef *voccxrun; /* execution context */ mcmcxdef *voccxmem; /* memory manager context */ objucxdef *voccxundo; /* undo context */ uchar *voccxpool; /* next free byte in vocdef pool */ vocdef *voccxfre; /* head of vocdef free list */ char *voccxcpp; /* pointer to compound word area */ int voccxcpl; /* length of compound word area */ char *voccxspp; /* pointer to special word area */ int voccxspl; /* length of special word area */ uint voccxrem; /* number of bytes remaining in vocdef pool */ vocidef **voccxinh[VOCINHMAX]; /* vocidef page table: 256 per page */ uchar *voccxip[VOCIPGMAX]; /* inheritance cell pool */ vocidef *voccxifr; /* head of inheritance cell free list */ uint voccxiplst; /* last inheritance cell page allocated */ uint voccxilst; /* next unused byte in last inheritance page */ int voccxredo; /* flag: redo command in buffer */ /* * redo buffer - if voccxredo is set, and this buffer is not empty, * we'll redo the command in this buffer rather than the one in our * internal stack buffer */ char voccxredobuf[VOCBUFSIZ]; /* * "again" buffer - when we save the last command for repeating via * the "again" command, we'll save the direct and indirect object * words here, so that they can be recovered if "again" is used */ char voccxagainbuf[VOCBUFSIZ]; vocdef *voccxhsh[VOCHASHSIZ]; /* hash table */ #ifdef VOCW_IN_CACHE mcmon voccxwp[VOCWPGMAX]; /* list of pages of vocab records */ mcmon voccxwplck; /* locked page of vocab records */ vocwdef *voccxwpgptr; /* pointer to currently locked page */ #else vocwdef *voccxwp[VOCWPGMAX]; /* vocabulary word pool */ #endif uint voccxwalocnt; /* number of vocwdef's used so far */ uint voccxwfre; /* index of first vocwdef in free list */ #define VOCCXW_NONE ((uint)(-1)) /* index value indicating end of list */ vocddef *voccxdmn; /* array of daemon slots */ uint voccxdmc; /* number of slots in daemon array */ vocddef *voccxfus; /* array of fuse slots */ uint voccxfuc; /* number of slots in fuse array */ vocddef *voccxalm; /* array of alarm slots */ uint voccxalc; /* number of slots in alarm array */ char voccxtim[26]; /* game's timestamp (asctime value) */ objnum voccxvtk; /* object number of "take" deepverb */ objnum voccxme; /* object number of "Me" actor */ objnum voccxme_init; /* initial setting of "Me" */ objnum voccxstr; /* object number of "strObj" */ objnum voccxnum; /* object number of "numObj" */ objnum voccxit; /* last "it" value */ objnum voccxhim; /* last "him" value */ objnum voccxher; /* last "her" value */ objnum voccxthc; /* count of items in "them" list */ objnum voccxthm[VOCMAXAMBIG]; /* list of items in "them" */ objnum voccxprd; /* "pardon" function object number */ objnum voccxpre; /* "preparse" function object number */ objnum voccxppc; /* "preparseCmd" function object number */ objnum voccxpre2; /* "preparseExt" function object number */ objnum voccxvag; /* "again" verb object */ objnum voccxini; /* "init" function */ objnum voccxper; /* "parseError" function object number */ objnum voccxprom; /* "cmdPrompt" function object number */ objnum voccxpostprom; /* "cmdPostPrompt" function object number */ objnum voccxpdis; /* parseDisambig function */ objnum voccxper2; /* parseError2 function */ objnum voccxperp; /* parseErrorParam function */ objnum voccxpdef; /* parseDefault function */ objnum voccxpdef2; /* parseDefaultExt function */ objnum voccxpask; /* parseAskobj function */ objnum voccxpask2; /* parseAskobjActor function */ objnum voccxpask3; /* parseAskobjIndirect function */ objnum voccxinitrestore; /* "initRestore" function object number */ objnum voccxpuv; /* parseUnknownVerb function object number */ objnum voccxpnp; /* parseNounPhrase function object number */ objnum voccxpostact; /* postAction function object number */ objnum voccxprecmd; /* preCommand function object number */ objnum voccxendcmd; /* endCommand function object number */ /* current command word list values */ vocoldef *voccxdobj; /* current direct object word list */ vocoldef *voccxiobj; /* current indirect object word list */ /* current command objects */ objnum voccxactor; /* current actor */ objnum voccxverb; /* current command deepverb */ objnum voccxprep; /* current command preposition */ /* previous command values - used by "again" */ objnum voccxlsa; /* previous actor */ objnum voccxlsv; /* previous verb */ vocoldef voccxlsd; /* previous direct object */ vocoldef voccxlsi; /* previous indirect object */ objnum voccxlsp; /* preposition */ int voccxlssty; /* style (new/old) of last template */ uchar voccxlst[VOCTPL2SIZ]; /* template */ objnum voccxpreinit; /* preinit function */ /* special flags */ uchar voccxflg; #define VOCCXFCLEAR 1 /* ignore remainder of command line (restore) */ #define VOCCXFVWARN 2 /* generate redundant verb warnings */ #define VOCCXFDBG 4 /* debug mode: show parsing information */ #define VOCCXAGAINDEL 8 /* "again" lost due to object deletion */ /* number of remaining unresolved unknown words in the command */ int voccxunknown; /* total number of unresolved words in the last command */ int voccxlastunk; /* parser stack area */ uchar *voc_stk_ptr; uchar *voc_stk_cur; uchar *voc_stk_end; }; typedef struct voccxdef voccxdef; /* allocate and push a list, returning a pointer to the list's memory */ uchar *voc_push_list_siz(voccxdef *ctx, uint lstsiz); /* push a list of objects from a vocoldef array */ void voc_push_vocoldef_list(voccxdef *ctx, vocoldef *objlist, int cnt); /* push a list of objects from an objnum array */ void voc_push_objlist(voccxdef *ctx, objnum objlist[], int cnt); /* change the player character ("Me") object */ void voc_set_me(voccxdef *ctx, objnum new_me); /* add a vocabulary word */ void vocadd(voccxdef *ctx, prpnum p, objnum objn, int classflag, char *wrdval); /* internal addword - must already be split into two words and lengths */ void vocadd2(voccxdef *ctx, prpnum p, objnum objn, int classflg, uchar *wrd1, int len1, uchar *wrd2, int len2); /* delete vocabulary for a given object */ void vocdel(voccxdef *ctx, objnum objn); /* lower-level vocabulary deletion routine */ void vocdel1(voccxdef *ctx, objnum objn, char *wrd, prpnum prp, int really_delete, int revert, int keep_undo); /* delete all inherited vocabulary */ void vocdelinh(voccxdef *ctx); /* allocate space for an inheritance record if needed */ void vocialo(voccxdef *ctx, objnum obj); /* add an inheritance/location record */ void vociadd(voccxdef *ctx, objnum obj, objnum loc, int numsc, objnum *sc, int flags); /* delete inheritance records for an object */ void vocidel(voccxdef *ctx, objnum chi); /* renumber an object's inheritance records - used for 'modify' */ void vociren(voccxdef *ctx, objnum oldnum, objnum newnum); /* caller-provided context structure for vocffw/vocfnw searches */ typedef struct vocseadef vocseadef; struct vocseadef { vocdef *v; vocwdef *vw; uchar *wrd1; int len1; uchar *wrd2; int len2; }; /* find first word matching a given word */ vocwdef *vocffw(voccxdef *ctx, char *wrd, int len, char *wrd2, int len2, int p, vocseadef *search_ctx); /* find next word */ vocwdef *vocfnw(voccxdef *voccx, vocseadef *search_ctx); /* read a line of input text */ int vocread(voccxdef *ctx, objnum actor, objnum verb, char *buf, int bufl, int type); #define VOCREAD_OK 0 #define VOCREAD_REDO 1 /* compute size of a vocoldef list */ int voclistlen(vocoldef *lst); /* tokenize an input buffer */ int voctok(voccxdef *ctx, char *cmd, char *outbuf, char **wrd, int lower, int cvt_ones, int show_errors); /* get types for a word list */ int vocgtyp(voccxdef *ctx, char **cmd, int *types, char *orgbuf); /* execute a player command */ int voccmd(voccxdef *ctx, char *cmd, uint cmdlen); /* disambiguator */ int vocdisambig(voccxdef *ctx, vocoldef *outlist, vocoldef *inlist, prpnum defprop, prpnum accprop, prpnum verprop, char *cmd[], objnum otherobj, objnum cmdActor, objnum cmdVerb, objnum cmdPrep, char *cmdbuf, int silent); /* display a multiple-object prefix */ void voc_multi_prefix(voccxdef *ctx, objnum objn, int show_prefix, int multi_flags, int cur_index, int count); /* low-level executor */ int execmd(voccxdef *ctx, objnum actor, objnum prep, char *vverb, char *vprep, vocoldef *dolist, vocoldef *iolist, char **cmd, int *typelist, char *cmdbuf, int wrdcnt, uchar **preparse_list, int *next_start); /* recursive command execution */ int execmd_recurs(voccxdef *ctx, objnum actor, objnum verb, objnum dobj, objnum prep, objnum iobj, int validate_dobj, int validate_iobj); /* try running preparseCmd user function */ int try_preparse_cmd(voccxdef *ctx, char **cmd, int wrdcnt, uchar **preparse_list); /* * Handle an unknown verb or sentence structure. We'll call this when * we encounter a sentence where we don't know the verb word, or we * don't know the combination of verb and verb preposition, or we don't * recognize the sentence structure (for example, an indirect object is * present, but we don't have a template defined using an indirect * object for the verb). * * 'wrdcnt' is the number of words in the cmd[] array. If wrdcnt is * zero, we'll automatically count the array entries, with the end of * the array indicated by a null pointer entry. * * If do_fuses is true, we'll execute the fuses and daemons if the * function exists and doesn't throw an ABORT error, or any other * run-time error other than EXIT. * * This function calls the game-defined function parseUnknownVerb, if it * exists. If the function doesn't exist, we'll simply display the * given error message, using the normal parseError mechanism. The * function should use "abort" or "exit" if it wants to cancel further * processing of the command. * * We'll return true if the function exists and executes successfully, * in which case normal processing should continue with any remaining * command on the command line. We'll return false if the function * doesn't exist or throws an error other than EXIT, in which case the * remainder of the command should be aborted. */ int try_unknown_verb(voccxdef *ctx, objnum actor, char **cmd, int *typelist, int wrdcnt, int *next_start, int do_fuses, int err, char *msg, ...); /* find a template */ int voctplfnd(voccxdef *ctx, objnum verb_in, objnum prep, uchar *tplout, int *newstyle); /* build a printable name for an object from the words in a command list */ void voc_make_obj_name(voccxdef *ctx, char *namebuf, char *cmd[], int firstwrd, int lastwrd); void voc_make_obj_name_from_list(voccxdef *ctx, char *namebuf, char *cmd[], char *firstwrd, char *lastwrd); /* * check noun - determines whether the next set of words is a valid noun * phrase. No complaint is issued if not; this check is generally made * to figure out what type of sentence we're dealing with. This is * simple; we just call vocgobj() with the complaint flag turned off. */ /* int vocchknoun(voccxdef *ctx, char **cmd, int *typelist, int cur, int *next, vocoldef *nounlist, int chkact); */ #define vocchknoun(ctx, cmd, typelist, cur, next, nounlist, chkact) \ vocgobj(ctx, cmd, typelist, cur, next, FALSE, nounlist, TRUE, chkact, 0) #define vocchknoun2(ctx, cmd, typlst, cur, next, nounlist, chkact, nomatch) \ vocgobj(ctx, cmd, typlst, cur, next, FALSE, nounlist, TRUE, chkact, nomatch) /* * get noun - reads an object list. We simply call vocgobj() with the * complaint and multiple-noun flags turned on. */ /* int vocgetnoun(voccxdef *ctx, char **cmd, int *typelist, int cur, int *next, vocoldef *nounlist); */ #define vocgetnoun(ctx, cmd, typelist, cur, next, nounlist) \ vocgobj(ctx, cmd, typelist, cur, next, TRUE, nounlist, TRUE, FALSE, 0) /* get object */ int vocgobj(voccxdef *ctx, char **cmd, int *typelist, int cur, int *next, int complain, vocoldef *nounlist, int multi, int chkact, int *nomatch); /* tokenize a string - TADS program code interface */ void voc_parse_tok(voccxdef *ctx); /* get token types - TADS program code interface */ void voc_parse_types(voccxdef *ctx); /* get objects matching all of the given words - TADS program code interface */ void voc_parse_dict_lookup(voccxdef *ctx); /* parse a noun list - TADS program code interface */ void voc_parse_np(voccxdef *ctx); /* disambiguate a noun list - TADS program code interface */ void voc_parse_disambig(voccxdef *ctx); /* replace the current command - TADS program code interface */ void voc_parse_replace_cmd(voccxdef *ctx); /* check access to an object */ int vocchkaccess(voccxdef *ctx, objnum obj, prpnum verprop, int seqno, objnum actor, objnum verb); /* check to see if an object is visible */ int vocchkvis(voccxdef *ctx, objnum obj, objnum cmdActor); /* display an appropriate message for an unreachable object */ void vocnoreach(voccxdef *ctx, objnum *list1, int cnt, objnum actor, objnum verb, objnum prep, prpnum defprop, int show_multi_prefix, int multi_flags, int multi_base_index, int multi_total_count); /* set {numObj | strObj}.value, as appropriate */ void vocsetobj(voccxdef *ctx, objnum obj, dattyp typ, void *val, vocoldef *inobj, vocoldef *outobj); /* macros to read values out of templates */ #define voctplpr(tpl) ((objnum)osrp2(((uchar *)tpl))) /* preposition */ #define voctplvi(tpl) ((prpnum)osrp2(((uchar *)tpl) + 2)) /* verIoVerb */ #define voctplio(tpl) ((prpnum)osrp2(((uchar *)tpl) + 4)) /* ioVerb */ #define voctplvd(tpl) ((prpnum)osrp2(((uchar *)tpl) + 6)) /* verDoVerb */ #define voctpldo(tpl) ((prpnum)osrp2(((uchar *)tpl) + 8)) /* doVerb */ #define voctplflg(tpl) (*(((uchar *)tpl) + 10)) /* flags */ /* flag values for the voctplflg */ #define VOCTPLFLG_DOBJ_FIRST 0x01 /* disambiguate direct object first */ /* word type flags */ #define VOCT_ARTICLE 1 #define VOCT_ADJ 2 #define VOCT_NOUN 4 #define VOCT_PREP 8 #define VOCT_VERB 16 #define VOCT_SPEC 32 /* special words - "of", ",", ".", etc. */ #define VOCT_PLURAL 64 #define VOCT_UNKNOWN 128 /* word is unknown */ /* special type flags */ #define VOCS_ALL 1 /* "all" */ #define VOCS_EXCEPT 2 /* "except" */ #define VOCS_IT 4 /* "it" */ #define VOCS_THEM 8 /* "them" */ #define VOCS_NUM 16 /* a number */ #define VOCS_COUNT 32 /* a number being used as a count */ #define VOCS_PLURAL 64 /* plural */ #define VOCS_ANY 128 /* "any" */ #define VOCS_HIM 256 /* "him" */ #define VOCS_HER 512 /* "her" */ #define VOCS_STR 1024 /* a quoted string */ #define VOCS_UNKNOWN 2048 /* noun phrase contains an unknown word */ #define VOCS_ENDADJ 4096 /* word matched adjective at end of phrase */ #define VOCS_TRUNC 8192 /* truncated match - word is leading substring */ #define VOCS_TRIMPREP 16384 /* trimmed prep phrase: assumed it was for verb */ /* special internally-defined one-character word flags */ #define VOCW_AND ',' #define VOCW_THEN '.' #define VOCW_OF 'O' #define VOCW_ALL 'A' #define VOCW_BOTH 'B' #define VOCW_IT 'I' #define VOCW_HIM 'M' #define VOCW_ONE 'N' #define VOCW_ONES 'P' #define VOCW_HER 'R' #define VOCW_THEM 'T' #define VOCW_BUT 'X' #define VOCW_ANY 'Y' /* structure for special internal word table */ struct vocspdef { char *vocspin; char vocspout; }; typedef struct vocspdef vocspdef; /* check if a word is a special word - true if word is given special word */ /* int vocspec(char *wordptr, int speccode); */ #define vocspec(w, s) (*(w) == (s)) /* * Set a fuse/daemon/notifier. */ void vocsetfd(voccxdef *ctx, vocddef *what, objnum func, prpnum prop, uint tm, runsdef *val, int err); /* remove a fuse/daemon/notifier */ void vocremfd(voccxdef *ctx, vocddef *what, objnum func, prpnum prop, runsdef *val, int err); /* count a turn (down all fuse/notifier timers) */ void vocturn(voccxdef *ctx, int turncnt, int do_fuses); /* initialize voc context */ void vocini(voccxdef *vocctx, errcxdef *errctx, mcmcxdef *memctx, runcxdef *runctx, objucxdef *undoctx, int fuses, int daemons, int notifiers); /* clean up the voc context - frees memory allocated by vocini() */ void vocterm(voccxdef *vocctx); /* allocate/free fuse/daemon/notifier array for voc ctx initialization */ void vocinialo(voccxdef *ctx, vocddef **what, int cnt); void voctermfree(vocddef *what); /* get a vocidef given an object number */ /* vocidef *vocinh(voccxdef *ctx, objnum obj); */ #define vocinh(ctx, obj) ((ctx)->voccxinh[(obj) >> 8][(obj) & 255]) /* revert all objects back to original state, using inheritance records */ void vocrevert(voccxdef *ctx); /* clear all fuses/daemons/notifiers (useful for restarting) */ void vocdmnclr(voccxdef *ctx); /* display a parser error message */ void vocerr(voccxdef *ctx, int err, char *f, ...); /* * display a parser informational error message - this will display the * message whether or not we're suppressing messages due to unknown * words, and should be used when providing information, such as objects * we're assuming by default */ void vocerr_info(voccxdef *ctx, int err, char *f, ...); /* client undo callback - undoes a daemon/fuse/notifier */ void vocdundo(void *ctx, uchar *data); /* client undo size figuring callback - return size of client undo record */ ushort OS_LOADDS vocdusz(void *ctx, uchar *data); /* save undo for object creation */ void vocdusave_newobj(voccxdef *ctx, objnum objn); /* save undo for adding a word */ void vocdusave_addwrd(voccxdef *ctx, objnum objn, prpnum typ, int flags, char *wrd); /* save undo for deleting a word */ void vocdusave_delwrd(voccxdef *ctx, objnum objn, prpnum typ, int flags, char *wrd); /* save undo for object deletion */ void vocdusave_delobj(voccxdef *ctx, objnum objn); /* save undo for changing the "Me" object */ void vocdusave_me(voccxdef *ctx, objnum old_me); /* compute vocabulary word hash value */ uint vochsh(uchar *t, int len); /* TADS versions of isalpha, isspace, isdigit, etc */ #define vocisupper(c) ((uchar)(c) <= 127 && isupper((uchar)(c))) #define vocislower(c) ((uchar)(c) <= 127 && islower((uchar)(c))) #define vocisalpha(c) ((uchar)(c) > 127 || isalpha((uchar)(c))) #define vocisspace(c) ((uchar)(c) <= 127 && isspace((uchar)(c))) #define vocisdigit(c) ((uchar)(c) <= 127 && isdigit((uchar)(c))) /* * Undo types for voc subsystem */ #define VOC_UNDO_DAEMON 1 /* fuse/daemon status change */ #define VOC_UNDO_NEWOBJ 2 /* object creation */ #define VOC_UNDO_DELOBJ 3 /* object deletion */ #define VOC_UNDO_ADDVOC 4 /* add vocabulary to an object */ #define VOC_UNDO_DELVOC 5 /* delete vocabulary from an object */ #define VOC_UNDO_SETME 6 /* set the "Me" object */ /* * Our own stack. We need to allocate some fairly large structures * (for the disambiguation lists, mostly) in a stack-like fashion, and * we don't want to consume vast quantities of the real stack, because * some machines have relatively restrictive limitations on stack usage. * To provide some elbow room, we'll use a stack-like structure of our * own: we'll allocate out of this structure as needed, and whenever we * leave a C stack frame, we'll also leave our own stack frame. */ /* re-initialize the stack, allocating space for it if needed */ void voc_stk_ini(voccxdef *ctx, uint siz); /* enter a stack frame, marking our current position */ #define voc_enter(ctx, marker) (*(marker) = (ctx)->voc_stk_cur) /* leave a stack frame, restoring the entry position */ #define voc_leave(ctx, marker) ((ctx)->voc_stk_cur = marker) /* return a value */ #define VOC_RETVAL(ctx, marker, retval) \ voc_leave(ctx, marker); return retval /* allocate space from the stack */ void *voc_stk_alo(voccxdef *ctx, uint siz); /* allocation cover macros */ #define VOC_STK_ARRAY(ctx, typ, var, cnt) \ (var = (typ *)voc_stk_alo(ctx, (uint)((cnt) * sizeof(typ)))) #define VOC_MAX_ARRAY(ctx, typ, var) \ VOC_STK_ARRAY(ctx, typ, var, VOCMAXAMBIG) /* * Stack size for the vocab stack. We'll scale our stack needs based * on the size of the vocoldef structure, since this is the most common * item to be allocated on the vocab stack. We'll also scale based on * the defined VOCMAXAMBIG parameter, since it is the number of elements * usually allocated. The actual amount of space needed depends on how * the functions in vocab.c and execmd.c work, so this parameter may * need to be adjusted for changes to the player command parser. */ #define VOC_STACK_SIZE (16 * VOCMAXAMBIG * sizeof(vocoldef)) /* * Execute all fuses and daemons, then execute the endCommand user * function. Returns zero on success, or ERR_ABORT if 'abort' was * thrown during execution. This is a convenient cover single function * to do all end-of-turn processing; this calls exefuse() and exedaem() * as needed, trapping any 'abort' or 'exit' errors that occur. * * If 'do_fuses' is true, we'll run fuses and daemons. Otherwise, */ int exe_fuses_and_daemons(voccxdef *ctx, int err, int do_fuses, objnum actor, objnum verb, vocoldef *dobj_list, int do_cnt, objnum prep, objnum iobj); /* * Execute any pending fuses. Return TRUE if any fuses were executed, * FALSE otherwise. */ int exefuse(voccxdef *ctx, int do_run); /* * Execute daemons */ void exedaem(voccxdef *ctx); /* * Get the number and size of words defined for an object. The size * returns the total byte count from all the words involved. Do not * include deleted words in the count. */ void voc_count(voccxdef *ctx, objnum objn, prpnum prp, int *cnt, int *siz); /* * Iterate through all words for a particular object, calling a * function with each vocwdef found. If objn == MCMONINV, we'll call * the callback for every word. */ void voc_iterate(voccxdef *ctx, objnum objn, void (*fn)(void *, vocdef *, vocwdef *), void *fnctx); /* ------------------------------------------------------------------------ */ /* * disambiguation status codes - used for disambigDobj and disambigIobj * methods in the deepverb */ /* continue with disambiguation process (using possibly updated list) */ #define VOC_DISAMBIG_CONT 1 /* done - the list is fully resolved; return with (possibly updated) list */ #define VOC_DISAMBIG_DONE 2 /* error - abort the command */ #define VOC_DISAMBIG_ERROR 3 /* parse string returned in second element of list as interactive response */ #define VOC_DISAMBIG_PARSE_RESP 4 /* already asked for an interactive response, but didn't read it yet */ #define VOC_DISAMBIG_PROMPTED 5 /* ------------------------------------------------------------------------ */ /* * parseNounPhrase status codes */ /* parse error occurred */ #define VOC_PNP_ERROR 1 /* use built-in default parser */ #define VOC_PNP_DEFAULT 2 /* successful parse */ #define VOC_PNP_SUCCESS 3 /* ------------------------------------------------------------------------ */ /* * parserResolveObjects usage codes */ #define VOC_PRO_RESOLVE_DOBJ 1 /* direct object */ #define VOC_PRO_RESOLVE_IOBJ 2 /* indirect object */ #define VOC_PRO_RESOLVE_ACTOR 3 /* actor */ #endif /* VOC_INCLUDED */ qtads-2.1.7/tads2/vocab.c000066400000000000000000010505751265017072300151300ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/vocab.c,v 1.5 1999/07/11 00:46:35 MJRoberts Exp $"; #endif /* * Copyright (c) 1987-1992 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vocab - TADS run-time player command parser Function Player command parser Notes This version of the parser is for TADS 2.0. Returns 0 for success, 1 for failure Modified 04/11/99 CNebel - Fix C++ errors. 03/11/92 MJRoberts - TADS 2.0 11/20/91 MJRoberts - fix isVisible operation 11/02/91 MJRoberts - fix strObj.value problem 08/13/91 MJRoberts - add him/her support 08/12/91 MJRoberts - make strObj.value an RSTRING (preprsfn arg, too) 08/08/91 MJRoberts - add preprsfn (preparse function) 04/12/91 MJRoberts - check abortcmd: if true, skip rest of cmd line 03/10/91 MJRoberts - moved getstring() to getstr.c for modularity, pick up John's qa-scripter mods 06/28/89 MJRoberts - call rtreset() before pardonfn invocations 11/04/88 MJRoberts - fix "it" and "them" 10/30/88 MJRoberts - new "version 6" game/parser interface 12/27/87 MJRoberts - created */ #include #include #include #include #include #include "os.h" #include "err.h" #include "voc.h" #include "tio.h" #include "mcm.h" #include "obj.h" #include "prp.h" #include "run.h" #include "lst.h" static char *type_names[] = { "article", "adj", "noun", "prep", "verb", "special", "plural", "unknown" }; /* array of flag values for words by part of speech */ static int voctype[] = { 0, 0, VOCT_VERB, VOCT_NOUN, VOCT_ADJ, VOCT_PREP, VOCT_ARTICLE }; /* ------------------------------------------------------------------------ */ /* * Allocate and push a list, given the number of bytes needed for the * elements of the list. */ uchar *voc_push_list_siz(voccxdef *ctx, uint lstsiz) { runcxdef *rcx = ctx->voccxrun; runsdef val; uchar *lstp; /* add in the size needed for the list's length prefix */ lstsiz += 2; /* allocate space in the heap */ runhres(rcx, lstsiz, 0); /* set up a stack value to push */ val.runstyp = DAT_LIST; val.runsv.runsvstr = lstp = ctx->voccxrun->runcxhp; /* set up the list's length prefix */ oswp2(lstp, lstsiz); lstp += 2; /* commit the space in the heap */ rcx->runcxhp += lstsiz; /* push the list value (repush, since we can use the original copy) */ runrepush(rcx, &val); /* return the list element pointer */ return lstp; } /* * Allocate and push a list. Returns a pointer to the space for the * list's first element in the heap. */ static uchar *voc_push_list(voccxdef *ctx, int ele_count, int ele_size) { uint lstsiz; /* * Figure the list size - we need space for the given number of * elements of the given size; in addition, each element requires * one byte of overhead for its type prefix. */ lstsiz = (uint)(ele_count * (1 + ele_size)); /* allocate and return the list */ return voc_push_list_siz(ctx, lstsiz); } /* * Push a list of numbers */ static void voc_push_numlist(voccxdef *ctx, uint numlist[], int cnt) { int i; uchar *lstp; /* allocate space for the list of numbers */ lstp = voc_push_list(ctx, cnt, 4); /* enter the list elements */ for (i = 0 ; i < cnt ; ++i) { /* add the type prefix */ *lstp++ = DAT_NUMBER; /* add the value */ oswp4(lstp, numlist[i]); lstp += 4; } } /* * Push a list of object ID's obtained from a vocoldef array */ void voc_push_vocoldef_list(voccxdef *ctx, vocoldef *objlist, int cnt) { int i; uchar *lstp; uint lstsiz; /* * count the size - we need 3 bytes per object (1 for type plus 2 * for the value), and 1 byte per nil */ for (lstsiz = 0, i = 0 ; i < cnt ; ++i) lstsiz += (objlist[i].vocolobj == MCMONINV ? 1 : 3); /* allocate space for the list */ lstp = voc_push_list_siz(ctx, lstsiz); /* enter the list elements */ for (i = 0 ; i < cnt ; ++i) { if (objlist[i].vocolobj == MCMONINV) { /* store the nil */ *lstp++ = DAT_NIL; } else { /* add the type prefix */ *lstp++ = DAT_OBJECT; /* add the value */ oswp2(lstp, objlist[i].vocolobj); lstp += 2; } } } /* * Push a list of object ID's */ void voc_push_objlist(voccxdef *ctx, objnum objlist[], int cnt) { int i; uchar *lstp; uint lstsiz; /* * count the size - we need 3 bytes per object (1 for type plus 2 * for the value), and 1 byte per nil */ for (lstsiz = 0, i = 0 ; i < cnt ; ++i) lstsiz += (objlist[i] == MCMONINV ? 1 : 3); /* allocate space for the list */ lstp = voc_push_list_siz(ctx, lstsiz); /* enter the list elements */ for (i = 0 ; i < cnt ; ++i) { if (objlist[i] == MCMONINV) { /* store the nil */ *lstp++ = DAT_NIL; } else { /* add the type prefix */ *lstp++ = DAT_OBJECT; /* add the value */ oswp2(lstp, objlist[i]); lstp += 2; } } } /* * Push a list of strings, where the strings are stored in memory, one * after the other, each string separated from the next with a null * byte. The list is bounded by firstwrd and lastwrd, inclusive of * both. */ static void voc_push_strlist(voccxdef *ctx, char *firstwrd, char *lastwrd) { size_t curlen; char *p; uint lstsiz; uchar *lstp; /* * Determine how much space we need for the word list. For each * entry, we need one byte for the type prefix, two bytes for the * length prefix, and the bytes of the string itself. */ for (lstsiz = 0, p = firstwrd ; p != 0 && p <= lastwrd ; p += curlen + 1) { curlen = strlen(p); lstsiz += curlen + (1+2); } /* allocate space for the word list */ lstp = voc_push_list_siz(ctx, lstsiz); /* enter the list elements */ for (p = firstwrd ; p != 0 && p <= lastwrd ; p += curlen + 1) { /* add the type prefix */ *lstp++ = DAT_SSTRING; /* add the length prefix for this string */ curlen = strlen(p); oswp2(lstp, curlen + 2); lstp += 2; /* add this string */ memcpy(lstp, p, curlen); lstp += curlen; } } /* * Push a list of strings, taking the strings from an array. */ static void voc_push_strlist_arr(voccxdef *ctx, char *wordlist[], int cnt) { int i; char **p; uint lstsiz; uchar *lstp; /* * Add up the lengths of the strings in the array. For each * element, we need space for the string's bytes, plus two bytes for * the length prefix, plus one byte for the type prefix. */ for (lstsiz = 0, p = wordlist, i = 0 ; i < cnt ; ++i, ++p) lstsiz += strlen(*p) + 3; /* allocate space for the list */ lstp = voc_push_list_siz(ctx, lstsiz); /* enter the list elements */ for (p = wordlist, i = 0 ; i < cnt ; ++i, ++p) { size_t curlen; /* add the type prefix */ *lstp++ = DAT_SSTRING; /* add the length prefix for this string */ curlen = strlen(*p); oswp2(lstp, curlen + 2); lstp += 2; /* add this string */ memcpy(lstp, *p, curlen); lstp += curlen; } } /* * Push a list of strings, taking the strings from an array that was * prepared by the parser tokenizer. This is almost the same as pushing * a regular string array, with the difference that we must recognize * the special format that the tokenizer uses to store string tokens. */ static void voc_push_toklist(voccxdef *ctx, char *wordlist[], int cnt) { int i; char **p; uint lstsiz; uchar *lstp; size_t cur_len; /* * Add up the lengths of the strings in the array. For each * element, we need space for the string's bytes, plus two bytes for * the length prefix, plus one byte for the type prefix. */ for (lstsiz = 0, p = wordlist, i = 0 ; i < cnt ; ++i, ++p) { /* * get the length of the current token - check what kind of * token we have, since we must sense the length of different * token types in different ways */ if (**p == '"') { /* * It's a string token - the string follows with a two-byte * length prefix; add two bytes for the open and close quote * characters that we'll add to the output string. Note * that we must deduct two bytes from the prefix length, * because the prefix includes the size of the prefix * itself, which we're not copying and will account for * separately in the result string. */ cur_len = osrp2(*p + 1) - 2 + 2; } else { /* for anything else, it's just a null-terminated string */ cur_len = strlen(*p); } /* add the current length to the total so far */ lstsiz += cur_len + 3; } /* allocate space for the list */ lstp = voc_push_list_siz(ctx, lstsiz); /* enter the list elements */ for (p = wordlist, i = 0 ; i < cnt ; ++i, ++p) { char *cur_ptr; size_t copy_len; /* add the type prefix */ *lstp++ = DAT_SSTRING; /* get the information for the string based on the type */ if (**p == '"') { /* * it's a string - use the length prefix (deducting two * bytes for the prefix itself, which we're not copying) */ copy_len = osrp2(*p + 1) - 2; /* add space in the result for the open and close quotes */ cur_len = copy_len + 2; /* the string itself follows the length prefix and '"' flag */ cur_ptr = *p + 3; } else { /* for anything else, it's just a null-terminated string */ cur_len = copy_len = strlen(*p); cur_ptr = *p; } /* write the length prefix for this string */ oswp2(lstp, cur_len + 2); lstp += 2; /* add the open quote if this is a quoted string */ if (**p == '"') *lstp++ = '"'; /* add this string */ memcpy(lstp, cur_ptr, copy_len); lstp += copy_len; /* add the close quote if it's a quoted string */ if (**p == '"') *lstp++ = '"'; } } /* ------------------------------------------------------------------------ */ /* * Read a command from the keyboard, doing all necessary output flushing * and prompting. */ int vocread(voccxdef *ctx, objnum actor, objnum verb, char *buf, int bufl, int type) { char *prompt; int ret; /* presume we'll return success */ ret = VOCREAD_OK; /* make sure output capturing is off */ tiocapture(ctx->voccxtio, (mcmcxdef *)0, FALSE); tioclrcapture(ctx->voccxtio); /* * Clear out the command buffer. This is important for the * timeout-based command reader, since it will take what's in the * buffer as the initial contents of the command line; this lets us * remember any partial line that the player entered before a * timeout interrupted their typing and redisplay the original * partial line on the next command line. Initially, there's no * partial line, so clear it out. */ buf[0] = '\0'; /* show the game-defined prompt, if appropriate */ if (ctx->voccxprom != MCMONINV) { runpnum(ctx->voccxrun, (long)type); runfn(ctx->voccxrun, ctx->voccxprom, 1); tioflushn(ctx->voccxtio, 0); prompt = ""; } else { /* there's no game-defined prompt - use our default */ tioblank(tio); prompt = ">"; } /* get a line of input */ if (tiogets(ctx->voccxtio, prompt, buf, bufl)) errsig(ctx->voccxerr, ERR_RUNQUIT); /* abort immediately if we see the special panic command */ if (!strcmp(buf, "$$ABEND")) { /* make sure any script file is closed */ qasclose(); /* use the OS-level termination */ os_term(OSEXFAIL); /* if that returned, signal a quit */ errsig(ctx->voccxerr, ERR_RUNQUIT); } /* call the post-prompt function if defined */ if (ctx->voccxpostprom != MCMONINV) { runpnum(ctx->voccxrun, (long)type); runfn(ctx->voccxrun, ctx->voccxpostprom, 1); } /* * If this isn't a type "0" input, and preparseExt() is defined, call * it. Don't call preparseExt() for type "0" inputs, since these will * be handled via the conventional preparse(). */ if (ctx->voccxpre2 != MCMONINV && type != 0) { uchar *s; size_t len; /* push the arguments - actor, verb, str, type */ runpnum(ctx->voccxrun, (long)type); runpstr(ctx->voccxrun, buf, (int)strlen(buf), 0); runpobj(ctx->voccxrun, verb); runpobj(ctx->voccxrun, actor); /* call preparseExt() */ runfn(ctx->voccxrun, ctx->voccxpre2, 4); /* check the result */ switch(runtostyp(ctx->voccxrun)) { case DAT_SSTRING: /* * They returned a string. Replace the input buffer we read * with the new string. Pop the new string and scan its length * prefix. */ s = runpopstr(ctx->voccxrun); len = osrp2(s) - 2; s += 2; /* * limit the size we copy to our buffer length (leaving space * for null termination) */ if (len > (size_t)bufl - 1) len = bufl - 1; /* copy the new command string into our buffer */ memcpy(buf, s, len); /* null-terminate the result */ buf[len] = '\0'; /* proceed as normal with the new string */ break; case DAT_TRUE: /* * they simply want to keep the current string as it is - * proceed as normal */ break; case DAT_NIL: /* * They want to skip the special interpretation of the input * and proceed directly to treating the input as a brand new * command. The caller will have to take care of the details; * we need only indicate this to the caller through our "redo" * result code. */ ret = VOCREAD_REDO; break; } } /* return our result */ return ret; } /* * Compare a pair of words, truncated to six characters or the * length of the first word, whichever is longer. (The first word is * the user's entry, the second is the reference word in the dictionary.) * Returns TRUE if the words match, FALSE otherwise. */ static int voceq(uchar *s1, uint l1, uchar *s2, uint l2) { uint i; if (l1 == 0 && l2 == 0) return(TRUE); /* both NULL - a match */ if (l1 == 0 || l2 == 0) return(FALSE); /* one NULL only - not a match */ if (l1 >= 6 && l2 >= l1) l2 = l1; if (l1 != l2) return(FALSE); /* ==> not equal */ for (i = 0 ; i < l1 ; i++) if (*s1++ != *s2++) return(FALSE); return(TRUE); /* strings match */ } /* find the next word in a search */ vocwdef *vocfnw(voccxdef *voccx, vocseadef *search_ctx) { vocdef *v, *vf; vocwdef *vw, *vwf; vocdef *c = search_ctx->v; int first; /* continue with current word's vocwdef list if anything is left */ first = TRUE; vw = vocwget(voccx, search_ctx->vw->vocwnxt); /* keep going until we run out of hash chain entries or find a match */ for (v = c, vf = 0 ; v != 0 && vf == 0 ; v = v->vocnxt, first = FALSE) { /* if this word matches, look at the objects in its list */ if (first || (voceq(search_ctx->wrd1, search_ctx->len1, v->voctxt, v->voclen) && voceq(search_ctx->wrd2, search_ctx->len2, v->voctxt + v->voclen, v->vocln2))) { /* * on the first time through, vw has already been set up * with the next vocwdef in the current list; on subsequent * times through the loop, start at the head of the current * word's list */ if (!first) vw = vocwget(voccx, v->vocwlst); /* search the list from vw forward */ for ( ; vw ; vw = vocwget(voccx, vw->vocwnxt)) { if (search_ctx->vw->vocwtyp == vw->vocwtyp && !(vw->vocwflg & VOCFCLASS) && !(vw->vocwflg & VOCFDEL)) { /* * remember the first vocdef that we found, and * remember this, the first matching vocwdef, then * stop scanning */ vf = v; vwf = vw; break; } } } } /* return the first vocwdef in this word's list */ search_ctx->v = vf; search_ctx->vw = (vf ? vwf : 0); return(search_ctx->vw); } /* find the first vocdef matching a set of words */ vocwdef *vocffw(voccxdef *ctx, char *wrd, int len, char *wrd2, int len2, int p, vocseadef *search_ctx) { uint hshval; vocdef *v, *vf; vocwdef *vw, *vwf; /* get the word's hash value */ hshval = vochsh((uchar *)wrd, len); /* scan the hash list until we run out of entries, or find a match */ for (v = ctx->voccxhsh[hshval], vf = 0 ; v != 0 && vf == 0 ; v = v->vocnxt) { /* if this word matches, look at the objects in its list */ if (voceq((uchar *)wrd, len, v->voctxt, v->voclen) && voceq((uchar *)wrd2, len2, v->voctxt + v->voclen, v->vocln2)) { /* look for a suitable object in the vocwdef list */ for (vw = vocwget(ctx, v->vocwlst) ; vw ; vw = vocwget(ctx, vw->vocwnxt)) { if (vw->vocwtyp == p && !(vw->vocwflg & VOCFCLASS) && !(vw->vocwflg & VOCFDEL)) { /* * remember the first vocdef that we found, and * remember this, the first matching vocwdef; then * stop scanning, since we have a match */ vf = v; vwf = vw; break; } } } } /* set up the caller-provided search structure for next time */ vw = (vf != 0 ? vwf : 0); if (search_ctx) { /* save the search position */ search_ctx->v = vf; search_ctx->vw = vw; /* save the search criteria */ search_ctx->wrd1 = (uchar *)wrd; search_ctx->len1 = len; search_ctx->wrd2 = (uchar *)wrd2; search_ctx->len2 = len2; } /* return the match */ return vw; } /* ------------------------------------------------------------------------ */ /* * vocerr_va information structure. This is initialized in the call to * vocerr_va_prep(), and must then be passed to vocerr_va(). */ struct vocerr_va_info { /* parseError/parseErrorParam result */ char user_msg[400]; /* the sprintf-style format string to display */ char *fmt; /* * Pointer to the output buffer to use to format the string 'fmt' with * its arguments, using vsprintf. The prep function will set this up * to point to user_msg[]. */ char *outp; /* size of the output buffer, in bytes */ size_t outsiz; }; /* * General parser error formatter - preparation. This must be called to * initialize the context before the message can be displayed with * vocerr_va(). */ static void vocerr_va_prep(voccxdef *ctx, struct vocerr_va_info *info, int err, char *f, va_list argptr) { /* * presume that we'll use the given format string, instead of one * provided by the program */ info->fmt = f; /* use the output buffer from the info structure */ info->outp = info->user_msg; info->outsiz = sizeof(info->user_msg); /* * if the user has a parseError or parseErrorParam function, see if it * provides a msg */ if (ctx->voccxper != MCMONINV || ctx->voccxperp != MCMONINV) { runcxdef *rcx = ctx->voccxrun; dattyp typ; size_t len; int argc; /* start off with the two arguments that are always present */ argc = 2; /* * if we're calling parseErrorParam, and we have additional * arguments, push them as well */ if (ctx->voccxperp != MCMONINV) { enum typ_t { ARGBUF_STR, ARGBUF_INT, ARGBUF_CHAR }; struct argbuf_t { enum typ_t typ; union { char *strval; int intval; char charval; } val; }; struct argbuf_t args[5]; struct argbuf_t *argp; char *p; /* * Retrieve the arguments by examining the format string. We * must buffer up the arguments before pushing them, because * we need to push them in reverse order (last to first); so, * we must scan all arguments before we push the first one. */ for (p = f, argp = args ; *p != '\0' ; ++p) { /* check if this is a parameter */ if (*p == '%') { /* find out what type it is */ switch(*++p) { case 's': /* string - save the char pointer */ argp->val.strval = va_arg(argptr, char *); argp->typ = ARGBUF_STR; /* consume an argument slot */ ++argp; break; case 'd': /* integer - save the integer */ argp->val.intval = va_arg(argptr, int); argp->typ = ARGBUF_INT; /* consume an argument slot */ ++argp; break; case 'c': /* character */ argp->val.charval = (char)va_arg(argptr, int); argp->typ = ARGBUF_CHAR; /* consume an argument slot */ ++argp; break; default: /* * ignore other types (there shouldn't be any * other types anyway) */ break; } } } /* * Push the arguments - keep looping until we get back to the * first argument slot */ while (argp != args) { /* move to the next argument, working backwards */ --argp; /* push this argument */ switch(argp->typ) { case ARGBUF_STR: /* push the string value */ runpstr(rcx, argp->val.strval, (int)strlen(argp->val.strval), 0); break; case ARGBUF_INT: /* push the number value */ runpnum(rcx, argp->val.intval); break; case ARGBUF_CHAR: /* push the character as a one-character string */ runpstr(rcx, &argp->val.charval, 1, 0); break; } /* count the argument */ ++argc; } } /* push standard arguments: error code and default message */ runpstr(rcx, f, (int)strlen(f), 0); /* 2nd arg: default msg */ runpnum(rcx, (long)err); /* 1st arg: error number */ /* invoke parseErrorParam if it's defined, otherwise parseError */ runfn(rcx, (objnum)(ctx->voccxperp == MCMONINV ? ctx->voccxper : ctx->voccxperp), argc); /* see what the function returned */ typ = runtostyp(rcx); if (typ == DAT_SSTRING) { char *p; /* * they returned a string - use it as the error message * instead of the default message */ p = (char *)runpopstr(rcx); len = osrp2(p) - 2; p += 2; if (len > sizeof(info->user_msg) - 1) len = sizeof(info->user_msg) - 1; memcpy(info->user_msg, p, len); info->user_msg[len] = '\0'; /* use the returned string as the message to display */ info->fmt = info->user_msg; /* use the remainder of the buffer for the final formatting */ info->outp = info->user_msg + len + 1; info->outsiz = sizeof(info->user_msg) - len - 1; } else { /* ignore other return values */ rundisc(rcx); } } } /* * General parser error formatter. * * Before calling this routine, callers MUST invoke vocerr_va_prep() to * prepare the information structure. Because both this routine and the * prep routine need to look at the varargs list ('argptr'), the caller * must call va_start/va_end around the prep call, and then AGAIN on this * call. va_start/va_end must be used twice to ensure that the argptr is * property re-initialized for the call to this routine. */ static void vocerr_va(voccxdef *ctx, struct vocerr_va_info *info, int err, char *f, va_list argptr) { char *buf; /* turn on output */ (void)tioshow(ctx->voccxtio); /* build the string to display */ if (os_vasprintf(&buf, info->fmt, argptr) >= 0) { /* display it */ tioputs(ctx->voccxtio, buf); /* free the buffer */ osfree(buf); } } /* ------------------------------------------------------------------------ */ /* * display a parser informational message */ void vocerr_info(voccxdef *ctx, int err, char *f, ...) { va_list argptr; struct vocerr_va_info info; /* prepare to format the message */ va_start(argptr, f); vocerr_va_prep(ctx, &info, err, f, argptr); va_end(argptr); /* call the general vocerr formatter */ va_start(argptr, f); vocerr_va(ctx, &info, err, f, argptr); va_end(argptr); } /* * display a parser error */ void vocerr(voccxdef *ctx, int err, char *f, ...) { va_list argptr; struct vocerr_va_info info; /* * If the unknown word flag is set, suppress this error, because * we're going to be trying the whole parsing from the beginning * again anyway. */ if (ctx->voccxunknown > 0) return; /* prepare to format the message */ va_start(argptr, f); vocerr_va_prep(ctx, &info, err, f, argptr); va_end(argptr); /* call the general vocerr formatter */ va_start(argptr, f); vocerr_va(ctx, &info, err, f, argptr); va_end(argptr); } /* * Handle an unknown verb or sentence structure. We'll call this when * we encounter a sentence where we don't know the verb word, or we * don't know the combination of verb and verb preposition, or we don't * recognize the sentence structure (for example, an indirect object is * present, but we don't have a template defined using an indirect * object for the verb). * * This function calls the game-defined function parseUnknownVerb, if it * exists. If the function doesn't exist, we'll simply display the * given error message, using the normal parseError mechanism. The * function should use "abort" or "exit" if it wants to cancel further * processing of the command. * * We'll return true if the function exists, in which case normal * processing should continue with any remaining command on the command * line. We'll return false if the function doesn't exist, in which * case the remainder of the command should be aborted. * * 'wrdcnt' is the number of words in the cmd[] array. If wrdcnt is * zero, we'll automatically count the array entries, with the end of * the array indicated by a null pointer entry. * * 'next_start' is a variable that we may fill in with the index of the * next word in the command to be parsed. If the user function * indicates the number of words it consumes, we'll use 'next_start' to * communicate this back to the caller, so that the caller can resume * parsing after the part parsed by the function. */ int try_unknown_verb(voccxdef *ctx, objnum actor, char **cmd, int *typelist, int wrdcnt, int *next_start, int do_fuses, int vocerr_err, char *vocerr_msg, ...) { int show_msg; va_list argptr; /* presume we won't show the message */ show_msg = FALSE; /* determine the word count if the caller passed in zero */ if (wrdcnt == 0) { /* count the words before the terminating null entry */ for ( ; cmd[wrdcnt] != 0 ; ++wrdcnt) ; } /* if parseUnknownVerb exists, call it */ if (ctx->voccxpuv != MCMONINV) { int err; int i; int do_fuses; /* no error has occurred yet */ err = 0; /* presume we will run the fuses */ do_fuses = TRUE; /* push the error number argument */ runpnum(ctx->voccxrun, (long)vocerr_err); /* push a list of the word types */ voc_push_numlist(ctx, (uint *)typelist, wrdcnt); /* push a list of the words */ voc_push_toklist(ctx, cmd, wrdcnt); /* use "Me" as the default actor */ if (actor == MCMONINV) actor = ctx->voccxme; /* push the actor argument */ runpobj(ctx->voccxrun, actor); /* catch any errors that occur while calling the function */ ERRBEGIN(ctx->voccxerr) { /* invoke the function */ runfn(ctx->voccxrun, ctx->voccxpuv, 4); /* get the return value */ switch(runtostyp(ctx->voccxrun)) { case DAT_TRUE: /* the command was handled */ rundisc(ctx->voccxrun); /* consume the entire command */ *next_start = wrdcnt; /* * since the command has now been handled, forget about * any unknown words */ ctx->voccxunknown = 0; break; case DAT_NUMBER: /* * The command was handled, and the function indicated * the number of words it wants to skip. Communicate * this information back to the caller in *next_start. * Since the routine returns the 1-based index of the * next entry, we must subtract one to get the number of * words actually consumed. */ *next_start = runpopnum(ctx->voccxrun); if (*next_start > 0) --(*next_start); /* make sure the value is in range */ if (*next_start < 0) *next_start = 0; else if (*next_start > wrdcnt) *next_start = wrdcnt; /* * forget about any unknown words in the list up to the * next word */ for (i = 0 ; i < *next_start ; ++i) { /* if this word was unknown, forget about that now */ if ((typelist[i] & VOCT_UNKNOWN) != 0 && ctx->voccxunknown > 0) --(ctx->voccxunknown); } break; default: /* treat anything else like nil */ case DAT_NIL: /* nil - command not handled; show the message */ rundisc(ctx->voccxrun); show_msg = TRUE; break; } } ERRCATCH(ctx->voccxerr, err) { /* check the error */ switch(err) { case ERR_RUNEXIT: case ERR_RUNEXITOBJ: /* * Exit or exitobj was executed - skip to the fuses. * Forget about any unknown words, since we've finished * processing this command and we don't want to allow * "oops" processing. */ ctx->voccxunknown = 0; break; case ERR_RUNABRT: /* * abort was executed - skip to the end of the command, * but do not execute the fuses */ do_fuses = FALSE; /* * Since we're aborting the command, ignore any * remaining unknown words - we're skipping out of the * command entirely, so we don't care that there were * unknown words in the command. */ ctx->voccxunknown = 0; break; default: /* re-throw any other errors */ errrse(ctx->voccxerr); } } ERREND(ctx->voccxerr); /* if we're not showing the message, process fuses and daemons */ if (!show_msg) { /* execute fuses and daemons */ if (exe_fuses_and_daemons(ctx, err, do_fuses, actor, MCMONINV, 0, 0, MCMONINV, MCMONINV) != 0) { /* * aborted from fuses and daemons - return false to tell * the caller not to execute anything left on the * command line */ return FALSE; } /* indicate that the game code successfully handled the command */ return TRUE; } } /* * If we made it here, it means we're showing the default message. * If we have unknown words, suppress the message so that we show * the unknown word error instead after returning. */ if (ctx->voccxunknown == 0) { struct vocerr_va_info info; /* prepare to format the message */ va_start(argptr, vocerr_msg); vocerr_va_prep(ctx, &info, vocerr_err, vocerr_msg, argptr); va_end(argptr); /* format the mesage */ va_start(argptr, vocerr_msg); vocerr_va(ctx, &info, vocerr_err, vocerr_msg, argptr); va_end(argptr); } /* indicate that the remainder of the command should be aborted */ return FALSE; } /* determine if a tokenized word is a special internal word flag */ /* int vocisspec(char *wrd); */ #define vocisspec(wrd) \ (vocisupper(*wrd) || (!vocisalpha(*wrd) && *wrd != '\'' && *wrd != '-')) static vocspdef vocsptab[] = { { "of", VOCW_OF }, { "and", VOCW_AND }, { "then", VOCW_THEN }, { "all", VOCW_ALL }, { "everyt", VOCW_ALL }, { "both", VOCW_BOTH }, { "but", VOCW_BUT }, { "except", VOCW_BUT }, { "one", VOCW_ONE }, { "ones", VOCW_ONES }, { "it", VOCW_IT }, { "them", VOCW_THEM }, { "him", VOCW_HIM }, { "her", VOCW_HER }, { "any", VOCW_ANY }, { "either", VOCW_ANY }, { 0, 0 } }; /* test a word to see if it's a particular special word */ static int voc_check_special(voccxdef *ctx, char *wrd, int checktyp) { /* search the user or built-in special table, as appropriate */ if (ctx->voccxspp) { char *p; char *endp; char typ; int len; int wrdlen = strlen((char *)wrd); for (p = ctx->voccxspp, endp = p + ctx->voccxspl ; p < endp ; ) { typ = *p++; len = *p++; /* if this word matches in type and text, we have a match */ if (typ == checktyp && len == wrdlen && !memcmp(p, wrd, (size_t)len)) return TRUE; /* no match - keep going */ p += len; } } else { vocspdef *x; for (x = vocsptab ; x->vocspin ; ++x) { /* if it matches in type and text, we have a match */ if (x->vocspout == checktyp && !strncmp((char *)wrd, x->vocspin, (size_t)6)) return TRUE; } } /* didn't find a match for the text and type */ return FALSE; } /* tokenize a command line - returns number of words in command */ int voctok(voccxdef *ctx, char *cmd, char *outbuf, char **wrd, int lower, int cvt_ones, int show_errors) { int i; vocspdef *x; int l; char *p; char *w; uint len; for (i = 0 ;; ) { while (vocisspace(*cmd)) cmd++; if (!*cmd) { wrd[i] = outbuf; *outbuf = '\0'; return(i); } wrd[i++] = outbuf; if (vocisalpha(*cmd) || *cmd == '-') { while(vocisalpha(*cmd) || vocisdigit(*cmd) || *cmd=='\'' || *cmd=='-') { *outbuf++ = (vocisupper(*cmd) && lower) ? tolower(*cmd) : *cmd; ++cmd; } /* * Check for a special case: abbreviations that end in a * period. For example, "Mr. Patrick J. Wayne." We wish * to absorb the period after "Mr" and the one after "J" * into the respective words; we detect this condition by * actually trying to find a word in the dictionary that * has the period. */ w = wrd[i-1]; len = outbuf - w; if (*cmd == '.') { *outbuf++ = *cmd++; /* add the period to the word */ *outbuf = '\0'; /* null-terminate it */ ++len; if (!vocffw(ctx, (char *)w, len, 0, 0, PRP_NOUN, (vocseadef *)0) && !vocffw(ctx, (char *)w, len, 0, 0, PRP_ADJ, (vocseadef *)0)) { /* no word with period in dictionary - remove period */ --outbuf; --cmd; --len; } } /* null-terminate the buffer */ *outbuf = '\0'; /* find compound words and glue them together */ for (p = ctx->voccxcpp, l = ctx->voccxcpl ; l ; ) { uint l1 = osrp2(p); char *p2 = p + l1; /* get second word */ uint l2 = osrp2(p2); char *p3 = p2 + l2; /* get compound word */ uint l3 = osrp2(p3); if (i > 1 && len == (l2 - 2) && !memcmp(w, p2 + 2, (size_t)len) && strlen((char *)wrd[i-2]) == (l1 - 2) && !memcmp(wrd[i-2], p + 2, (size_t)(l1 - 2))) { memcpy(wrd[i-2], p3 + 2, (size_t)(l3 - 2)); *(wrd[i-2] + l3 - 2) = '\0'; --i; break; } /* move on to the next word */ l -= l1 + l2 + l3; p = p3 + l3; } /* * Find any special keywords, and set to appropriate flag * char. Note that we no longer convert "of" in this * fashion; "of" is now handled separately in order to * facilitate its use as an ordinary preposition. */ if (ctx->voccxspp) { char *p; char *endp; char typ; int len; int wrdlen = strlen((char *)wrd[i-1]); for (p = ctx->voccxspp, endp = p + ctx->voccxspl ; p < endp ; ) { typ = *p++; len = *p++; if (len == wrdlen && !memcmp(p, wrd[i-1], (size_t)len) && (cvt_ones || (typ != VOCW_ONE && typ != VOCW_ONES)) && typ != VOCW_OF) { *wrd[i-1] = typ; *(wrd[i-1] + 1) = '\0'; break; } p += len; } } else { for (x = vocsptab ; x->vocspin ; ++x) { if (!strncmp((char *)wrd[i-1], (char *)x->vocspin, (size_t)6) && (cvt_ones || (x->vocspout != VOCW_ONE && x->vocspout != VOCW_ONES)) && x->vocspout != VOCW_OF) { *wrd[i-1] = x->vocspout; *(wrd[i-1] + 1) = '\0'; break; } } } /* make sure the output pointer is fixed up to the right spot */ outbuf = wrd[i-1]; outbuf += strlen((char *)outbuf); } else if (vocisdigit( *cmd )) { while(vocisdigit(*cmd) || vocisalpha(*cmd) || *cmd == '\'' || *cmd == '-') *outbuf++ = *cmd++; } else switch( *cmd ) { case '.': case '!': case '?': case ';': *outbuf++ = VOCW_THEN; ++cmd; break; case ',': case ':': *outbuf++ = VOCW_AND; ++cmd; break; case '"': case '\'': { char *lenptr; char quote = *cmd++; /* * remember that this is a quoted string (it doesn't * matter whether they're actually using single or * double quotes - in either case, we use '"' as the * flag to indicate that it's a quote string) */ *outbuf++ = '"'; /* make room for the length prefix */ lenptr = outbuf; outbuf += 2; /* copy up to the matching close quote */ while (*cmd && *cmd != quote) { char c; /* get this character */ c = *cmd++; /* escape the character if necessary */ switch(c) { case '\\': *outbuf++ = '\\'; break; } /* copy this character */ *outbuf++ = c; } oswp2(lenptr, ((int)(outbuf - lenptr))); if (*cmd == quote) cmd++; break; } default: /* display an error if appropriate */ if (show_errors) { int hmode = tio_is_html_mode(); /* * if we're in HTML mode, switch out momentarily, so that * we show the character literally, even if it's a * markup-significant character (such as '<' or '&') */ if (hmode) tioputs(ctx->voccxtio, "\\H-"); /* show the message */ vocerr(ctx, VOCERR(1), "I don't understand the punctuation \"%c\".", *cmd); /* restore HTML mode if appropriate */ if (hmode) tioputs(ctx->voccxtio, "\\H+"); } /* return failure */ return -1; } /* null-terminate the result */ *outbuf++ = '\0'; } } /* ------------------------------------------------------------------------ */ /* * Look up a word's type. If 'of_is_spec' is true, we'll treat OF as * being of type special if it's not otherwise defined. */ static int voc_lookup_type(voccxdef *ctx, char *p, int len, int of_is_spec) { int t; /* check for a special word */ if (vocisspec(p)) { /* it's a special word - this is its type */ t = VOCT_SPEC; } else { vocwdef *vw; vocdef *v; /* * Now check the various entries of this word to get the word * type flag bits. The Noun and Adjective flags can be set for * any word which matches this word in the first six letters (or * more if more were provided by the player), but the Plural * flag can only be set if the plural word matches exactly. * Note that this pass only matches the first word in two-word * verbs; the second word is considered later during the * semantic analysis. */ for (t = 0, v = ctx->voccxhsh[vochsh((uchar *)p, len)] ; v != 0 ; v = v->vocnxt) { /* if this hash chain entry matches, add it to our types */ if (voceq((uchar *)p, len, v->voctxt, v->voclen)) { /* we have a match - look through relation list for word */ for (vw = vocwget(ctx, v->vocwlst) ; vw != 0 ; vw = vocwget(ctx, vw->vocwnxt)) { /* skip this word if it's been deleted */ if (vw->vocwflg & VOCFDEL) continue; /* we need a special check for plurals */ if (vw->vocwtyp == PRP_PLURAL) { /* plurals must be exact (non-truncated) match */ if (len == v->voclen) { /* plurals also count as nouns */ t |= (VOCT_NOUN | VOCT_PLURAL); } } else { /* add this type bit to our type value */ t |= voctype[vw->vocwtyp]; } } } } } /* check for "of" if the caller wants us to */ if (of_is_spec && t == 0 && voc_check_special(ctx, p, VOCW_OF)) t = VOCT_SPEC; /* return the type */ return t; } /* ------------------------------------------------------------------------ */ /* * Display an unknown word error, and read a new command, allowing the * user to respond with the special OOPS command to correct the unknown * word. Returns a pointer to the start of the replacement text if the * player entered a correction via OOPS, or a null pointer if the player * simply entered a new command. */ static char *voc_read_oops(voccxdef *ctx, char *oopsbuf, size_t oopsbuflen, const char *unknown_word) { char *p; /* display the error */ vocerr(ctx, VOCERR(2), "I don't know the word \"%s\".", unknown_word); /* read a new command */ if (vocread(ctx, MCMONINV, MCMONINV, oopsbuf, (int)oopsbuflen, 1) == VOCREAD_REDO) { /* * we've already decided it's not an OOPS input - return null to * indicate to the caller that we have a new command */ return 0; } /* lower-case the string */ for (p = oopsbuf ; *p != '\0' ; ++p) *p = (vocisupper(*p) ? tolower(*p) : *p); /* skip leading spaces */ for (p = oopsbuf ; vocisspace(*p) ; ++p) ; /* * See if they are saying "oops". Allow "oops" or simply "o", * followed by either a space or a comma. */ if ((strlen(p) > 5 && memcmp(p, "oops ", 5) == 0) || (strlen(p) > 5 && memcmp(p, "oops,", 5) == 0)) { /* we found "OOPS" - move to the next character */ p += 5; } else if ((strlen(p) > 2 && memcmp(p, "o ", 2) == 0) || (strlen(p) > 2 && memcmp(p, "o,", 2) == 0)) { /* we found "O" - move to the next character */ p += 2; } else { /* * we didn't find any form of "OOPS" response - return null to * indicate to the caller that the player entered a new command */ return 0; } /* skip spaces before the replacement text */ for ( ; vocisspace(*p) ; ++p) ; /* return a pointer to the start of the replacement text */ return p; } /* ------------------------------------------------------------------------ */ /* * figure out what parts of speech are associated with each * word in a tokenized command list */ int vocgtyp(voccxdef *ctx, char *cmd[], int types[], char *orgbuf) { int cur; int t; char *p; int len; int unknown_count = 0; startover: if (ctx->voccxflg & VOCCXFDBG) tioputs(ctx->vocxtio, ". Checking words:\\n"); for (cur = 0 ; cmd[cur] ; ++cur) { /* get the word */ p = cmd[cur]; len = strlen(p); /* look it up */ t = voc_lookup_type(ctx, p, len, FALSE); /* see if the word was found */ if (t == 0 && !voc_check_special(ctx, p, VOCW_OF)) { /* * We didn't find the word. For now, set its type to * "unknown". */ t = VOCT_UNKNOWN; /* * If the unknown word count is already non-zero, it means * that we've tried to let the game resolve this word using * the parseUnknownDobj/Iobj mechanism, but it wasn't able * to do so, thus we've come back here to use the normal * "oops" processing instead. * * Don't generate a message until we get to the first * unknown word from the original list that we weren't able * to resolve. We may have been able to handle one or more * of the original list of unknown words (through * parseNounPhrase or other means), so we don't want to * generate a message for any words we ended up handling. * The number we resolved is the last full unknown count * minus the remaining unknown count. */ if (ctx->voccxunknown != 0 && unknown_count >= ctx->voccxlastunk - ctx->voccxunknown) { char oopsbuf[VOCBUFSIZ]; char *p1; /* * we can try using the parseUnknownDobj/Iobj again * after this, so clear the unknown word count for now */ ctx->voccxunknown = 0; /* display an error, and ask for a new command */ p1 = voc_read_oops(ctx, oopsbuf, sizeof(oopsbuf), p); /* if they responded with replacement text, apply it */ if (p1 != 0) { char redobuf[200]; char *q; int i; int wc; char **w; char *outp; /* * copy words from the original string, replacing * the unknown word with what follows the "oops" in * the new command */ for (outp = redobuf, i = 0, w = cmd ; *w != 0 ; ++i, ++w) { /* see what we have */ if (i == cur) { /* * We've reached the word to be replaced. * Ignore the original token, and replace it * with the word or words from the OOPS * command */ for (q = p1, len = 0 ; *q != '\0' && *q != '.' && *q != ',' && *q != '?' && *q != '!' ; ++q, ++len) ; memcpy(outp, p1, (size_t)len); outp += len; } else if (**w == '"') { char *strp; char *p2; char qu; int rem; /* * It's a string - add a quote mark, then * copy the string as indicated by the * length prefix, then add another quote * mark. Get the length by reading the * length prefix following the quote mark, * and get a pointer to the text of the * string, which immediately follows the * length prefix. */ len = osrp2(*w + 1) - 2; strp = *w + 3; /* * We need to figure out what kind of quote * mark to use. If the string contains any * embedded double quotes, use single quotes * to delimit the string; otherwise, use * double quotes. Presume we'll use double * quotes as the delimiter, then scan the * string for embedded double quotes. */ for (qu = '"', p2 = strp, rem = len ; rem != 0 ; --rem, ++p2) { /* * if this is an embedded double quote, * use single quotes to delimite the * string */ if (*p2 == '"') { /* use single quotes as delimiters */ qu = '\''; /* no need to look any further */ break; } } /* add the open quote */ *outp++ = qu; /* copy the string */ memcpy(outp, strp, len); outp += len; /* add the close quote */ *outp++ = qu; } else { /* * it's an ordinary token - copy the * null-terminated string for the token from * the original command list */ len = strlen(*w); memcpy(outp, *w, (size_t)len); outp += len; } /* add a space between words */ *outp++ = ' '; } /* terminate the new string */ *outp = '\0'; /* try tokenizing the string */ *(cmd[0]) = '\0'; if ((wc = voctok(ctx, redobuf, cmd[0], cmd, FALSE, FALSE, TRUE)) <= 0) return 1; cmd[wc] = 0; /* start over with the typing */ goto startover; } else { /* * They didn't start the command with "oops", so * this must be a brand new command. Replace the * original command with the new command. */ strcpy(orgbuf, oopsbuf); /* * forget we had an unknown word so that we're sure * to start over with a new command */ ctx->voccxunknown = 0; /* * set the "redo" flag to start over with the new * command */ ctx->voccxredo = 1; /* * return an error to indicate the current command * has been aborted */ return 1; } } else { /* * We've now encountered an unknown word, and we're * going to defer resolution. Remember this; we'll * count the unknown word in the context when we return * (do so only locally for now, since we may encounter * more unknown words before we return, in which case we * want to know that this is still the first pass). */ ++unknown_count; } } /* display if in debug mode */ if (ctx->voccxflg & VOCCXFDBG) { char buf[128]; size_t i; char *p; int cnt; (void)tioshow(ctx->voccxtio); sprintf(buf, "... %s (", cmd[cur]); p = buf + strlen(buf); cnt = 0; for (i = 0 ; i < sizeof(type_names)/sizeof(type_names[0]) ; ++i) { if (t & (1 << i)) { if (cnt) *p++ = ','; strcpy(p, type_names[i]); p += strlen(p); ++cnt; } } *p++ = ')'; *p++ = '\\'; *p++ = 'n'; *p = '\0'; tioputs(ctx->voccxtio, buf); } types[cur] = t; /* record type of this word */ } /* if we found any unknown words, note this in our context */ ctx->voccxunknown = unknown_count; ctx->voccxlastunk = unknown_count; /* successful acquisition of types */ return 0; } /* * intersect - takes two lists and puts the intersection of them into * the first list. */ static int vocisect(objnum *list1, objnum *list2) { int i, j, k; for (i = k = 0 ; list1[i] != MCMONINV ; ++i) { for (j = 0 ; list2[j] != MCMONINV ; ++j) { if (list1[i] == list2[j]) { list1[k++] = list1[i]; break; } } } list1[k] = MCMONINV; return(k); } /* * Intersect lists, including parallel flags lists. The flags from the * two lists for any matching object are OR'd together. */ static int vocisect_flags(objnum *list1, uint *flags1, objnum *list2, uint *flags2) { int i, j, k; for (i = k = 0 ; list1[i] != MCMONINV ; ++i) { for (j = 0 ; list2[j] != MCMONINV ; ++j) { if (list1[i] == list2[j]) { list1[k] = list1[i]; flags1[k] = flags1[i] | flags2[j]; ++k; break; } } } list1[k] = MCMONINV; return(k); } /* * get obj list: build a list of the objects that are associated with a * given word of player input. */ static int vocgol(voccxdef *ctx, objnum *list, uint *flags, char **wrdlst, int *typlst, int first, int cur, int last, int ofword) { char *wrd; int typ; vocwdef *v; int cnt; int len; vocseadef search_ctx; int try_plural; int try_noun_before_num; int try_endadj; int trying_endadj; int wrdtyp; /* get the current word and its type */ wrd = wrdlst[cur]; typ = typlst[cur]; /* get the length of the word */ len = strlen(wrd); /* * Get word type: figure out the correct part of speech, given by * context, for a given word. If it could count as only a * noun/plural or only an adjective, we use that. If it could count * as either a noun/plural or an adjective, we will treat it as a * noun/plural if it is the last word in the name or the last word * before "of", otherwise as an adjective. * * If the word is unknown, treat it as a noun or adjective - treat * it as part of the current noun phrase. One unknown word renders * the whole noun phrase unknown. */ try_plural = (typ & VOCT_PLURAL); /* presume we won't retry this word as an adjective */ try_endadj = FALSE; /* presume we won't retry this as a noun before a number */ try_noun_before_num = FALSE; /* we're not yet trying with adjective-at-end */ trying_endadj = FALSE; /* check to see what parts of speech are defined for this word */ if ((typ & (VOCT_NOUN | VOCT_PLURAL)) && (typ & VOCT_ADJ)) { /* * This can be either an adjective or a plural/noun. If this is * the last word in the noun phrase, treat it as a noun/plural if * possible. Otherwise, treat it as an adjective. */ if (cur + 1 == last || cur == ofword - 1) { /* * This is the last word in the entire phrase, or the last word * before an 'of' (which makes it the last word of its * subphrase). Treat it as a noun if possible, otherwise as a * plural */ wrdtyp = ((typ & VOCT_NOUN) ? PRP_NOUN : PRP_PLURAL); /* * If this can be an adjective, too, make a note to come back * and try it again as an adjective. We prefer not to end a * noun phrase with an adjective, but we allow it, since it's * often convenient to abbreviate a noun phrase to just the * adjectives (as in TAKE RED, where there's only one object * nearby to which RED applies). */ if ((typ & VOCT_ADJ) != 0) try_endadj = TRUE; } else if ((cur + 2 == last || cur == ofword - 2) && vocisdigit(wrdlst[cur+1][0])) { /* * This is the second-to-last word, and the last word is * numeric. In this case, try this word as BOTH a noun and an * adjective. Try it as an adjective first, but make a note to * go back and try it again as a noun. */ wrdtyp = PRP_ADJ; try_noun_before_num = TRUE; } else { /* * This isn't the last word, so it can only be an adjective. * Look at it only as an adjective. */ wrdtyp = PRP_ADJ; } } else if (typ & VOCT_NOUN) wrdtyp = PRP_NOUN; else if (typ & VOCT_UNKNOWN) wrdtyp = PRP_UNKNOWN; else { /* it's just an adjective */ wrdtyp = PRP_ADJ; /* * if this is the last word in the phrase, flag it as an ending * adjective */ if (cur + 1 == last || cur == ofword - 1) trying_endadj = TRUE; } /* display debugger information if appropriate */ if (ctx->voccxflg & VOCCXFDBG) { char buf[128]; sprintf(buf, "... %s (treating as %s%s)\\n", wrd, (wrdtyp == PRP_ADJ ? "adjective" : wrdtyp == PRP_NOUN ? "noun" : wrdtyp == PRP_INVALID ? "unknown" : "plural"), (wrdtyp == PRP_NOUN && try_plural ? " + plural" : "")); tioputs(ctx->vocxtio, buf); } /* if this is an unknown word, it doesn't have any objects */ if (wrdtyp == PRP_UNKNOWN) { list[0] = MCMONINV; return 0; } /* we have nothing in the list yet */ cnt = 0; add_words: for (v = vocffw(ctx, wrd, len, (char *)0, 0, wrdtyp, &search_ctx) ; v != 0 ; v = vocfnw(ctx, &search_ctx)) { int i; /* add the matching object to the output list */ list[cnt] = v->vocwobj; /* clear the flags */ flags[cnt] = 0; /* set the PLURAL flag if this is the plural vocabulary usage */ if (wrdtyp == PRP_PLURAL) flags[cnt] |= VOCS_PLURAL; /* set the ADJECTIVE AT END flag if appropriate */ if (wrdtyp == PRP_ADJ && trying_endadj) flags[cnt] |= VOCS_ENDADJ; /* * if this is not an exact match for the word, but is merely a * long-enough leading substring, flag it as truncated */ if (len < search_ctx.v->voclen) flags[cnt] |= VOCS_TRUNC; /* count the additional word in the list */ ++cnt; /* * if this object is already in the list with the same flags, * don't add it again */ for (i = 0 ; i < cnt - 1 ; ++i) { /* check for an identical entry */ if (list[i] == list[cnt-1] && flags[i] == flags[cnt-1]) { /* take it back out of the list */ --cnt; /* no need to continue looking for the duplicate */ break; } } /* make sure we haven't overflowed the list */ if (cnt >= VOCMAXAMBIG) { vocerr(ctx, VOCERR(3), "The word \"%s\" refers to too many objects.", wrd); list[0] = MCMONINV; return -1; } } /* * if we want to go back and try the word again as a noun before a * number (as in "button 5"), do so now */ if (try_noun_before_num && wrdtyp == PRP_ADJ) { /* change the word type to noun */ wrdtyp = PRP_NOUN; /* don't try this again */ try_noun_before_num = FALSE; /* add the words for the noun usage */ goto add_words; } /* * if we're interpreting the word as a noun, and the word can be a * plural, add in the plural interpretation as well */ if (try_plural && wrdtyp != PRP_PLURAL) { /* change the word type to plural */ wrdtyp = PRP_PLURAL; /* don't try plurals again */ try_plural = FALSE; /* add the words for the plural usage */ goto add_words; } /* * if this was the last word in the phrase, and it could have been * an adjective, try it again as an adjective */ if (try_endadj && wrdtyp != PRP_ADJ) { /* change the word type to adjective */ wrdtyp = PRP_ADJ; /* note that we're retrying as an adjective */ trying_endadj = TRUE; /* don't try this again */ try_endadj = FALSE; /* add the words for the adjective usage */ goto add_words; } /* * If we're interpreting the word as an adjective, and it's * numeric, include objects with "#" in their adjective list -- * these objects allow arbitrary numbers as adjectives. Don't do * this if there's only the one word. */ if (vocisdigit(wrd[0]) && wrdtyp == PRP_ADJ && first + 1 != last) { wrd = "#"; len = 1; goto add_words; } list[cnt] = MCMONINV; return cnt; } /* * Add the user-defined word for "of" to a buffer. If no such word is * defined by the user (with the specialWords construct), add "of". */ static void vocaddof(voccxdef *ctx, char *buf) { if (ctx->voccxspp) { size_t len = ctx->voccxspp[1]; size_t oldlen = strlen(buf); memcpy(buf + oldlen, ctx->voccxspp + 2, len); buf[len + oldlen] = '\0'; } else strcat(buf, "of"); } /* ------------------------------------------------------------------------ */ /* * Call the parseNounPhrase user function, if defined, to attempt to * parse a noun phrase. * * Returns VOC_PNP_ERROR if the hook function indicates that an error * occurred; PNP_DEFAULT if the hook function told us to use the default * list; or PNP_SUCCESS to indicate that the hook function provided a * list to use. */ static int voc_pnp_hook(voccxdef *ctx, char *cmd[], int typelist[], int cur, int *next, int complain, vocoldef *out_nounlist, int *out_nouncount, int chkact, int *no_match) { runcxdef *rcx = ctx->voccxrun; runsdef val; int wordcnt; char **cmdp; int outcnt; vocoldef *outp; int i; /* if parseNounPhrase isn't defined, use the default handling */ if (ctx->voccxpnp == MCMONINV) return VOC_PNP_DEFAULT; /* push the actor-check flag */ val.runstyp = (chkact ? DAT_TRUE : DAT_NIL); runpush(rcx, val.runstyp, &val); /* push the complain flag */ val.runstyp = (complain ? DAT_TRUE : DAT_NIL); runpush(rcx, val.runstyp, &val); /* push the current index (adjusted to 1-based user convention) */ runpnum(rcx, cur + 1); /* count the entries in the command list */ for (wordcnt = 0, cmdp = cmd ; *cmdp != 0 && **cmdp != '\0' ; ++wordcnt, ++cmdp) ; /* push the type list */ voc_push_numlist(ctx, (uint *)typelist, wordcnt); /* push the command word list */ voc_push_strlist_arr(ctx, cmd, wordcnt); /* call the method */ runfn(rcx, ctx->voccxpnp, 5); /* check the return value */ if (runtostyp(rcx) == DAT_NUMBER) { /* return the status code directly from the hook function */ return (int)runpopnum(rcx); } else if (runtostyp(rcx) == DAT_LIST) { uchar *lstp; uint lstsiz; /* pop the list */ lstp = runpoplst(rcx); /* read and skip the size prefix */ lstsiz = osrp2(lstp); lstsiz -= 2; lstp += 2; /* the first element should be the next index */ if (lstsiz > 1 && *lstp == DAT_NUMBER) { /* set the 'next' pointer, adjusting to 0-based indexing */ *next = osrp4(lstp+1) - 1; /* * If 'next' is out of range, force it into range. We can't * go backwards (so 'next' must always be at least 'cur'), * and we can't go past the null element at the end of the * list. */ if (*next < cur) *next = cur; else if (*next > wordcnt) *next = wordcnt; /* skip the list entry */ lstadv(&lstp, &lstsiz); } else { /* ignore the list and use the default parsing */ return VOC_PNP_DEFAULT; } /* read the list entries and store them in the output array */ for (outcnt = 0, outp = out_nounlist ; lstsiz > 0 ; ) { /* make sure we have room for another entry */ if (outcnt >= VOCMAXAMBIG - 1) break; /* get the next list entry, and store it in the output array */ if (*lstp == DAT_NIL) { /* set the list entry */ outp->vocolobj = MCMONINV; /* skip the entry */ lstadv(&lstp, &lstsiz); } else if (*lstp == DAT_OBJECT) { /* set the list entry */ outp->vocolobj = osrp2(lstp+1); /* skip the list entry */ lstadv(&lstp, &lstsiz); } else { /* ignore other types in the list */ lstadv(&lstp, &lstsiz); continue; } /* check for a flag entry */ if (lstsiz > 0 && *lstp == DAT_NUMBER) { /* set the flags */ outp->vocolflg = (int)osrp4(lstp+1); /* skip the number */ lstadv(&lstp, &lstsiz); } else { /* no flags were specified - use the default */ outp->vocolflg = 0; } /* set the word list boundaries */ outp->vocolfst = cmd[cur]; outp->vocollst = cmd[*next - 1]; /* count the entry */ ++outp; ++outcnt; } /* terminate the list */ outp->vocolobj = MCMONINV; outp->vocolflg = 0; /* set the output count */ *out_nouncount = outcnt; /* * set "no_match" appropriately -- set "no_match" true if we're * returning an empty list and we parsed one or more words */ if (no_match != 0) *no_match = (outcnt == 0 && *next > cur); /* * Adjust the unknown word count in the context. If the routine * parsed any unknown words, decrement the unknown word count in * the context by the number of unknown words parsed, since * these have now been dealt with. If the return list contains * any objects flagged as having unknown words, add the count of * such objects back into the context, since we must still * resolve these at disambiguation time. */ for (i = cur ; i < *next ; ++i) { /* if this parsed word was unknown, remove it from the count */ if ((typelist[i] & VOCT_UNKNOWN) != 0) --(ctx->voccxunknown); } for (i = 0, outp = out_nounlist ; i < outcnt ; ++i) { /* if this object has the unknown flag, count it */ if ((outp->vocolflg & VOCS_UNKNOWN) != 0) ++(ctx->voccxunknown); } /* indicate that the hook provided a list */ return VOC_PNP_SUCCESS; } else { /* * ignore any other return value - consider others equivalent to * DEFAULT */ rundisc(rcx); return VOC_PNP_DEFAULT; } } /* ------------------------------------------------------------------------ */ /* * Build an object name from the words in a command */ void voc_make_obj_name(voccxdef *ctx, char *namebuf, char *cmd[], int firstwrd, int lastwrd) { int i; /* run through the range of words, and add them to the buffer */ for (i = firstwrd, namebuf[0] = '\0' ; i < lastwrd ; ++i) { if (voc_check_special(ctx, cmd[i], VOCW_OF)) vocaddof(ctx, namebuf); else strcat(namebuf, cmd[i]); if (cmd[i][strlen(cmd[i])-1] == '.' && i + 1 < lastwrd) strcat(namebuf, "\\"); if (i + 1 < lastwrd) strcat(namebuf, " "); } } /* * Make an object name from a list entry */ void voc_make_obj_name_from_list(voccxdef *ctx, char *namebuf, char *cmd[], char *firstwrd, char *lastwrd) { int i, i1, i2; /* find the cmd indices */ for (i = i1 = i2 = 0 ; cmd[i] != 0 && *cmd[i] != 0 ; ++i) { if (cmd[i] == firstwrd) i1 = i; if (cmd[i] == lastwrd) i2 = i + 1; } /* build the name */ voc_make_obj_name(ctx, namebuf, cmd, i1, i2); } /* ------------------------------------------------------------------------ */ /* * get 1 obj - attempts to figure out the limits of a single noun * phrase. Aside from dealing with special words here ("all", "it", * "them", string objects, numeric objects), we will accept a basic noun * phrase of the form [article][adjective*][noun]["of" [noun-phrase]]. * (Note that this is not actually recursive; only one "of" can occur in * a noun phrase.) If successful, we will construct a list of all * objects that have all the adjectives and nouns in the noun phrase. * Note that plurals are treated basically like nouns, except that we * will flag them so that the disambiguator knows to include all objects * that work with the plural. * * Note that we also allow the special constructs "all [of] * " and "both [of] "; these are treated * identically to normal plurals. * * If no_match is not null, we'll set it to true if we found valid * syntax but no matching objects, false otherwise. */ static int vocg1o(voccxdef *ctx, char *cmd[], int typelist[], int cur, int *next, int complain, vocoldef *nounlist, int chkact, int *no_match) { int l1; int firstwrd; int i; int ofword = -1; int hypothetical_last = -1; int trim_flags = 0; int outcnt = 0; objnum *list1; uint *flags1; objnum *list2; uint *flags2; char *namebuf; int has_any = FALSE; uchar *save_sp; int found_plural; int unknown_count; int trying_count = FALSE; int retry_with_count; voc_enter(ctx, &save_sp); VOC_MAX_ARRAY(ctx, objnum, list1); VOC_MAX_ARRAY(ctx, uint, flags1); VOC_MAX_ARRAY(ctx, objnum, list2); VOC_MAX_ARRAY(ctx, uint, flags2); VOC_STK_ARRAY(ctx, char, namebuf, VOCBUFSIZ); /* presume we'll find a match */ if (no_match != 0) *no_match = FALSE; /* start at the first word */ *next = cur; /* if we're at the end of the command, return no objects in list */ if (cur == -1 || !cmd[cur]) { VOC_RETVAL(ctx, save_sp, 0); } /* show trace message if in debug mode */ if (ctx->voccxflg & VOCCXFDBG) tioputs(ctx->vocxtio, chkact ? ". Checking for actor\\n" : ". Reading noun phrase\\n"); /* try the user parseNounPhrase hook */ switch(voc_pnp_hook(ctx, cmd, typelist, cur, next, complain, nounlist, &outcnt, chkact, no_match)) { case VOC_PNP_DEFAULT: /* continue on to the default processing */ break; case VOC_PNP_ERROR: default: /* return an error */ VOC_RETVAL(ctx, save_sp, -1); case VOC_PNP_SUCCESS: /* use their list */ VOC_RETVAL(ctx, save_sp, outcnt); } /* check for a quoted string */ if (*cmd[cur] == '"') { /* can't use a quoted string as an actor */ if (chkact) { VOC_RETVAL(ctx, save_sp, 0); } if (ctx->voccxflg & VOCCXFDBG) tioputs(ctx->vocxtio, "... found quoted string\\n"); nounlist[outcnt].vocolobj = MCMONINV; nounlist[outcnt].vocolflg = VOCS_STR; nounlist[outcnt].vocolfst = nounlist[outcnt].vocollst = cmd[cur]; *next = ++cur; ++outcnt; VOC_RETVAL(ctx, save_sp, outcnt); } /* check for ALL/ANY/BOTH/EITHER [OF] contruction */ if ((vocspec(cmd[cur], VOCW_ALL) || vocspec(cmd[cur], VOCW_BOTH) || vocspec(cmd[cur], VOCW_ANY)) && cmd[cur+1] != (char *)0) { int nxt; int n = cur+1; int has_of; /* can't use ALL as an actor */ if (chkact) { VOC_RETVAL(ctx, save_sp, 0); } /* remember whether we have "any" or "either" */ has_any = vocspec(cmd[cur], VOCW_ANY); /* check for optional 'of' */ if (voc_check_special(ctx, cmd[n], VOCW_OF)) { if (ctx->voccxflg & VOCCXFDBG) tioputs(ctx->vocxtio, "... found ALL/ANY/BOTH/EITHER OF\\n"); has_of = TRUE; n++; if (!cmd[n]) { char *p; int ver; if (vocspec(cmd[cur], VOCW_ALL)) { ver = VOCERR(4); p = "I think you left something out after \"all of\"."; } else if (vocspec(cmd[cur], VOCW_ANY)) { ver = VOCERR(29); p = "I think you left something out after \"any of\"."; } else { ver = VOCERR(5); p = "There's something missing after \"both of\"."; } vocerr(ctx, ver, p); VOC_RETVAL(ctx, save_sp, -1); } } else has_of = FALSE; nxt = n; if (typelist[n] & VOCT_ARTICLE) ++n; /* skip leading article */ for ( ;; ) { if (!cmd[n]) break; if (voc_check_special(ctx, cmd[n], VOCW_OF)) { ++n; if (!cmd[n]) { vocerr(ctx, VOCERR(6), "I expected a noun after \"of\"."); VOC_RETVAL(ctx, save_sp, -1); } if (*cmd[n] & VOCT_ARTICLE) ++n; } else if (typelist[n] & (VOCT_ADJ | VOCT_NOUN)) ++n; else break; } /* * Accept the ALL if the last word is a plural. Accept the ANY * if either we don't have an OF (ANY NOUN is okay even without * a plural), or if we have OF and a plural. (More simply put, * accept the ALL or ANY if the last word is a plural, or if we * have ANY but not OF). */ if (n > cur && ((typelist[n-1] & VOCT_PLURAL) || (has_any && !has_of))) { if (ctx->voccxflg & VOCCXFDBG) tioputs(ctx->vocxtio, "... found ALL/ANY/BOTH/EITHER + noun phrase\\n"); cur = nxt; } } if (vocspec(cmd[cur], VOCW_ALL) && !has_any) { /* can't use ALL as an actor */ if (chkact) { VOC_RETVAL(ctx, save_sp, 0); } if (ctx->voccxflg & VOCCXFDBG) tioputs(ctx->vocxtio, "... found ALL\\n"); nounlist[outcnt].vocolobj = MCMONINV; nounlist[outcnt].vocolflg = VOCS_ALL; nounlist[outcnt].vocolfst = nounlist[outcnt].vocollst = cmd[cur]; ++outcnt; ++cur; if (cmd[cur] && vocspec(cmd[cur], VOCW_BUT)) { int cnt; int i; vocoldef *xlist; uchar *inner_save_sp; if (ctx->voccxflg & VOCCXFDBG) tioputs(ctx->vocxtio, "... found ALL EXCEPT\\n"); voc_enter(ctx, &inner_save_sp); VOC_MAX_ARRAY(ctx, vocoldef, xlist); cur++; cnt = vocgobj(ctx, cmd, typelist, cur, next, complain, xlist, 1, chkact, 0); if (cnt < 0) { /* * An error occurred - return it. Note that, since * we're returning from the entire function, we're * popping the save_sp frame, NOT the inner_save_sp * frame -- the inner frame is nested within the save_sp * frame, and we want to pop the entire way out since * we're exiting the entire function. */ VOC_RETVAL(ctx, save_sp, cnt); } cur = *next; for (i = 0 ; i < cnt ; ++i) { OSCPYSTRUCT(nounlist[outcnt], xlist[i]); nounlist[outcnt].vocolflg |= VOCS_EXCEPT; ++outcnt; } voc_leave(ctx, inner_save_sp); } *next = cur; nounlist[outcnt].vocolobj = MCMONINV; nounlist[outcnt].vocolflg = 0; VOC_RETVAL(ctx, save_sp, outcnt); } switch(*cmd[cur]) { case VOCW_IT: nounlist[outcnt].vocolflg = VOCS_IT; goto do_special; case VOCW_THEM: nounlist[outcnt].vocolflg = VOCS_THEM; goto do_special; case VOCW_HIM: nounlist[outcnt].vocolflg = VOCS_HIM; goto do_special; case VOCW_HER: nounlist[outcnt].vocolflg = VOCS_HER; /* FALLTHRU */ do_special: if (ctx->voccxflg & VOCCXFDBG) tioputs(ctx->vocxtio, "... found pronoun\\n"); *next = cur + 1; nounlist[outcnt].vocolobj = MCMONINV; nounlist[outcnt].vocolfst = nounlist[outcnt].vocollst = cmd[cur]; ++outcnt; VOC_RETVAL(ctx, save_sp, outcnt); default: break; } if (((typelist[cur] & (VOCT_ARTICLE | VOCT_ADJ | VOCT_NOUN | VOCT_UNKNOWN)) == 0) && !vocisdigit(*cmd[cur])) { VOC_RETVAL(ctx, save_sp, 0); } if (typelist[cur] & VOCT_ARTICLE) { ++cur; if (cmd[cur] == (char *)0 || ((typelist[cur] & (VOCT_ADJ | VOCT_NOUN | VOCT_UNKNOWN)) == 0 && !vocisdigit(*cmd[cur]))) { vocerr(ctx, VOCERR(7), "An article must be followed by a noun."); *next = cur; VOC_RETVAL(ctx, save_sp, -1); } } /* start at the current word */ firstwrd = cur; /* scan words for inclusion in this noun phrase */ for (found_plural = FALSE, unknown_count = 0, l1 = 0 ; ; ) { if (cmd[cur] == (char *)0) break; if (typelist[cur] & VOCT_ADJ) ++cur; else if (typelist[cur] & VOCT_UNKNOWN) { /* * Remember that we found an unknown word, but include it in * the noun phrase - this will render the entire noun phrase * unknown, but we'll resolve that later. */ ++unknown_count; ++cur; } else if (typelist[cur] & VOCT_NOUN) { ++cur; if (cmd[cur] == (char *)0) break; if (vocisdigit(*cmd[cur])) ++cur; if (cmd[cur] == (char *)0) break; if (!voc_check_special(ctx, cmd[cur], VOCW_OF)) break; } else if (vocisdigit(*cmd[cur])) ++cur; else if (voc_check_special(ctx, cmd[cur], VOCW_OF)) { ++cur; if (ofword != -1) { /* there's already one 'of' - we must be done */ --cur; break; } ofword = cur-1; if (typelist[cur] & VOCT_ARTICLE) /* allow article after "of" */ ++cur; } else break; /* note whether we found anything that might be a plural */ if (cmd[cur] != 0 && (typelist[cur] & VOCT_PLURAL) != 0) found_plural = TRUE; } try_again: /* * build a printable string consisting of the words in the noun * phrase, for displaying error messages */ voc_make_obj_name(ctx, namebuf, cmd, firstwrd, cur); /* remember the next word after this noun phrase */ *next = cur; /* * If we have any unknown words, we won't be able to match any * objects for the noun phrase. Return with one entry in the list, * but use an invalid object and mark the object as containing * unknown words. */ if (unknown_count > 0) { /* * Add one unknown object for each unknown word. This lets us * communicate the number of unknown words that we found to the * disambiguator, which will later attempt to resolve the * reference. Each object we add is the same; they're here only * for the word count. */ for ( ; unknown_count > 0 ; --unknown_count) { nounlist[outcnt].vocolobj = MCMONINV; nounlist[outcnt].vocolflg = VOCS_UNKNOWN; nounlist[outcnt].vocolfst = cmd[firstwrd]; nounlist[outcnt].vocollst = cmd[cur-1]; ++outcnt; } nounlist[outcnt].vocolobj = MCMONINV; nounlist[outcnt].vocolflg = 0; VOC_RETVAL(ctx, save_sp, outcnt); } /* get the list of objects that match the first word */ l1 = vocgol(ctx, list1, flags1, cmd, typelist, firstwrd, firstwrd, cur, ofword); /* * Allow retrying with a count plus a plural if the first word is a * number, and we have something plural in the list. Only treat "1" * this way if more words follow in the noun phrase. */ retry_with_count = ((vocisdigit(*cmd[firstwrd]) && found_plural) || (vocisdigit(*cmd[firstwrd]) && cur != firstwrd+1 && atoi(cmd[firstwrd]) == 1)); /* see if we found anything on the first word */ if (l1 <= 0) { if (chkact) { VOC_RETVAL(ctx, save_sp, 0); } if (vocisdigit(*cmd[firstwrd])) { if (retry_with_count) { /* interpret it as a count plus a plural */ trying_count = TRUE; /* don't try this again */ retry_with_count = FALSE; } else { /* not a plural - take the number as the entire noun phrase */ nounlist[outcnt].vocolobj = MCMONINV; nounlist[outcnt].vocolflg = VOCS_NUM; nounlist[outcnt].vocolfst = nounlist[outcnt].vocollst = cmd[firstwrd]; *next = firstwrd + 1; ++outcnt; nounlist[outcnt].vocolobj = MCMONINV; nounlist[outcnt].vocolflg = 0; VOC_RETVAL(ctx, save_sp, outcnt); } } else { /* * display a message if we didn't already (if vocgol * returned less than zero, it already displayed its own * error message) */ if (l1 == 0) vocerr(ctx, VOCERR(9), "I don't see any %s here.", namebuf); /* return failure */ VOC_RETVAL(ctx, save_sp, -1); } } retry_exclude_first: for (i = firstwrd + 1 ; i < cur ; ++i) { int l2; if (voc_check_special(ctx, cmd[i], VOCW_OF) || (typelist[i] & VOCT_ARTICLE)) continue; /* get the list for this new object */ l2 = vocgol(ctx, list2, flags2, cmd, typelist, firstwrd, i, cur, ofword); /* if that failed and displayed an error, return failure */ if (l2 < 0) { /* return failure */ VOC_RETVAL(ctx, save_sp, -1); } /* * Intersect the last list with the new list. If the previous * list didn't have anything in it, it must mean that the word * list started with a number, in which case we're trying to * interpret this as a count plus a plural. So, don't intersect * the list if there was nothing in the first list. */ if (l1 == 0) { /* just copy the new list */ l1 = l2; memcpy(list1, list2, (size_t)((l1+1) * sizeof(list1[0]))); memcpy(flags1, flags2, (size_t)(l1 * sizeof(flags1[0]))); } else { /* intersect the two lists */ l1 = vocisect_flags(list1, flags1, list2, flags2); } /* * If there's nothing in the list, it means that there's no * object that defines all of these words. */ if (l1 == 0) { if (ctx->voccxflg & VOCCXFDBG) tioputs(ctx->vocxtio, "... can't find any objects matching these words\\n"); /* * If there's an "of", remove the "of" and everything that * follows, and go back and reprocess the part up to the * "of" -- treat it as a sentence that has two objects, with * "of" as the preposition introducing the indirect object. */ if (ofword != -1) { if (ctx->voccxflg & VOCCXFDBG) tioputs(ctx->vocxtio, "... dropping the part after OF and retrying\\n"); /* * drop the part from 'of' on - scan only from firstwrd * to the word before 'of' */ hypothetical_last = cur; trim_flags |= VOCS_TRIMPREP; cur = ofword; /* forget that we have an 'of' phrase at all */ ofword = -1; /* retry with the new, shortened phrase */ goto try_again; } /* * Try again with the count + plural interpretation, if * possible */ if (retry_with_count) { if (ctx->voccxflg & VOCCXFDBG) tioputs(ctx->vocxtio, "... treating the number as a count and retrying\\n"); /* we've exhausted our retries */ retry_with_count = FALSE; trying_count = TRUE; /* go try it */ goto retry_exclude_first; } /* * If one of the words will work as a preposition, and we * took it as an adjective, go back and try the word again * as a preposition. */ for (i = cur - 1; i > firstwrd ; --i) { if (typelist[i] & VOCT_PREP) { if (ctx->voccxflg & VOCCXFDBG) tioputs(ctx->vocxtio, "... changing word to prep and retrying\\n"); hypothetical_last = cur; trim_flags |= VOCS_TRIMPREP; cur = i; goto try_again; } } /* if just checking actor, don't display an error */ if (chkact) { VOC_RETVAL(ctx, save_sp, 0); } /* * tell the player about it unless supressing complaints, * and return an error */ if (complain) vocerr(ctx, VOCERR(9), "I don't see any %s here.", namebuf); if (no_match != 0) *no_match = TRUE; VOC_RETVAL(ctx, save_sp, 0); } } /* * We have one or more objects, so make a note of how we found * them. */ if (ctx->voccxflg & VOCCXFDBG) tioputs(ctx->vocxtio, "... found objects matching vocabulary:\\n"); /* store the list of objects that match all of our words */ for (i = 0 ; i < l1 ; ++i) { if (ctx->voccxflg & VOCCXFDBG) { tioputs(ctx->voccxtio, "..... "); runppr(ctx->voccxrun, list1[i], PRP_SDESC, 0); tioflushn(ctx->voccxtio, 1); } nounlist[outcnt].vocolfst = cmd[firstwrd]; nounlist[outcnt].vocollst = cmd[cur-1]; nounlist[outcnt].vocolflg = flags1[i] | (trying_count ? VOCS_COUNT : 0) | trim_flags; if (trim_flags) nounlist[outcnt].vocolhlst = cmd[hypothetical_last - 1]; if (has_any) nounlist[outcnt].vocolflg |= VOCS_ANY; nounlist[outcnt++].vocolobj = list1[i]; if (outcnt > VOCMAXAMBIG) { vocerr(ctx, VOCERR(10), "You're referring to too many objects with \"%s\".", namebuf); VOC_RETVAL(ctx, save_sp, -2); } } /* * If we have a one-word noun phrase, and the word is a number, add * the number object into the list of objects we're considering, * even though we found an object that matches. We'll probably * easily disambiguate this later, but we need to keep open the * possibility that they're just referring to a number rather than a * numbered adjective for now. */ if (firstwrd + 1 == cur && vocisdigit(*cmd[firstwrd])) { /* add just the number as an object */ nounlist[outcnt].vocolobj = ctx->voccxnum; nounlist[outcnt].vocolflg = VOCS_NUM; nounlist[outcnt].vocolfst = nounlist[outcnt].vocollst = cmd[firstwrd]; ++outcnt; } /* terminate the list */ nounlist[outcnt].vocolobj = MCMONINV; nounlist[outcnt].vocolflg = 0; /* return the number of objects in our match list */ VOC_RETVAL(ctx, save_sp, outcnt); } /* * get obj - gets one or more noun lists (a flag, "multi", says whether * we should allow multiple lists). We use vocg1o() to read noun lists * one at a time, and keep going (if "multi" is true) as long as there * are more "and " clauses. * * If no_match is not null, we'll set it to true if the syntax was okay * but we didn't find any match for the list of words, false otherwise. */ int vocgobj(voccxdef *ctx, char *cmd[], int typelist[], int cur, int *next, int complain, vocoldef *nounlist, int multi, int chkact, int *no_match) { int cnt; int outcnt = 0; int i; int again = FALSE; int lastcur; vocoldef *tmplist; uchar *save_sp; voc_enter(ctx, &save_sp); VOC_MAX_ARRAY(ctx, vocoldef, tmplist); for ( ;; ) { /* try getting a single object */ cnt = vocg1o(ctx, cmd, typelist, cur, next, complain, tmplist, chkact, no_match); /* if we encountered a syntax error, return failure */ if (cnt < 0) { VOC_RETVAL(ctx, save_sp, cnt); } /* if we got any objects, store them in our output list */ if (cnt > 0) { for (i = 0 ; i < cnt ; ++i) { OSCPYSTRUCT(nounlist[outcnt], tmplist[i]); if (++outcnt > VOCMAXAMBIG) { vocerr(ctx, VOCERR(11), "You're referring to too many objects."); VOC_RETVAL(ctx, save_sp, -1); } } } /* if we didn't find any objects, stop looking */ if (cnt == 0) { if (again) *next = lastcur; break; } /* * if the caller only wanted a single object (or is getting an * actor, in which case they implicitly want only a single * object), stop looking for additional noun phrases */ if (!multi || chkact) break; /* skip past the previous noun phrase */ cur = *next; /* * if we're looking at a noun phrase separator ("and" or a * comma), get the next noun phrase; otherwise, we're done */ if (cur != -1 && cmd[cur] != 0 && vocspec(cmd[cur], VOCW_AND)) { lastcur = cur; while (cmd[cur] && vocspec(cmd[cur], VOCW_AND)) ++cur; again = TRUE; if (complain) complain = 2; } else { /* end of line, or not at a separator - we're done */ break; } } /* terminate the list and return the number of objects we found */ nounlist[outcnt].vocolobj = MCMONINV; nounlist[outcnt].vocolflg = 0; VOC_RETVAL(ctx, save_sp, outcnt); } /* ------------------------------------------------------------------------ */ /* * TADS program code interface - tokenize a string. Returns a list of * strings, with each string giving a token in the command. */ void voc_parse_tok(voccxdef *ctx) { uchar *save_sp; runcxdef *rcx = ctx->voccxrun; char **cmd; char *inbuf; char *outbuf; uchar *p; uint len; int cnt; /* enter our stack frame */ voc_enter(ctx, &save_sp); /* get the string argument */ p = runpopstr(rcx); /* get and skip the length prefix */ len = osrp2(p) - 2; p += 2; /* * Allocate space for the original string, and space for the token * pointers and the tokenized string buffer. We could potentially * have one token per character in the original string, and we could * potentially need one extra null terminator for each character in * the original string; allocate accordingly. */ VOC_STK_ARRAY(ctx, char, inbuf, len + 1); VOC_STK_ARRAY(ctx, char, outbuf, len*2); VOC_STK_ARRAY(ctx, char *, cmd, len*2); /* copy the string into our buffer, and null-terminate it */ memcpy(inbuf, p, len); inbuf[len] = '\0'; /* tokenize the string */ cnt = voctok(ctx, inbuf, outbuf, cmd, TRUE, FALSE, FALSE); /* check the result */ if (cnt < 0) { /* negative count - it's an error, so return nil */ runpnil(rcx); } else { /* push the return list */ voc_push_toklist(ctx, cmd, cnt); } /* leave our stack frame */ voc_leave(ctx, save_sp); } /* ------------------------------------------------------------------------ */ /* * TADS program code interface - get the list of types for a list words. * The words are simply strings of the type returned from the tokenizer. * The return value is a list of types, with each entry in the return * list giving the types of the corresponding entry in the word list. */ void voc_parse_types(voccxdef *ctx) { runcxdef *rcx = ctx->voccxrun; uchar *wrdp; uint wrdsiz; uchar *typp; uchar *lstp; uint lstsiz; int wrdcnt; /* get the list of words from the stack */ wrdp = runpoplst(rcx); /* read and skip the size prefix */ wrdsiz = osrp2(wrdp) - 2; wrdp += 2; /* scan the list and count the number of string entries */ for (wrdcnt = 0, lstp = wrdp, lstsiz = wrdsiz ; lstsiz != 0 ; lstadv(&lstp, &lstsiz)) { /* if this is a string, count it */ if (*lstp == DAT_SSTRING) ++wrdcnt; } /* allocate the return list - one number entry per word */ typp = voc_push_list(ctx, wrdcnt, 4); /* look up each word and set the corresponding element in the type list */ for (lstp = wrdp, lstsiz = wrdsiz ; lstsiz != 0 ; lstadv(&lstp, &lstsiz)) { /* if this is a string, look it up in the dictionary */ if (*lstp == DAT_SSTRING) { char buf[256]; int curtyp; uint len; /* make sure it fits in our buffer */ len = osrp2(lstp+1) - 2; if (len < sizeof(buf)) { /* extract the word into our buffer */ memcpy(buf, lstp+3, len); /* null-terminate it */ buf[len] = '\0'; /* get the type */ curtyp = voc_lookup_type(ctx, buf, len, TRUE); /* if they didn't find a type at all, set it to UNKNOWN */ if (curtyp == 0) curtyp = VOCT_UNKNOWN; } else { /* the string is too big - just mark it as unknown */ curtyp = VOCT_UNKNOWN; } /* add this type to the return list */ *typp++ = DAT_NUMBER; oswp4s(typp, curtyp); typp += 4; } } } /* ------------------------------------------------------------------------ */ /* * Parse a noun phrase from TADS program code. Takes a list of words * and a list of types from the stack, uses the standard noun phrase * parser, and returns a list of matching objects. The object list is * not disambiguated, but merely reflects all matching objects. The * entire standard parsing algorithm applies, including parseNounPhrase * invocation if appropriate. */ void voc_parse_np(voccxdef *ctx) { runcxdef *rcx = ctx->voccxrun; vocoldef *objlist; uchar *save_sp; uchar *wordp; uint wordsiz; uchar *typp; uint typsiz; int cnt; char **wordarr; int wordcnt; char *wordchararr; uint wordcharsiz; int *typarr; int complain; int chkact; int multi; int no_match; int next; int cur; uchar *lstp; uint lstsiz; char *p; int i; int old_unknown, old_lastunk; /* allocate stack arrays */ voc_enter(ctx, &save_sp); VOC_MAX_ARRAY(ctx, vocoldef, objlist); /* * Save the original context unknown values, since we don't want to * affect the context information in this game-initiated call, then * clear the unknown word count for the duration of the call. */ old_unknown = ctx->voccxunknown; old_lastunk = ctx->voccxlastunk; ctx->voccxunknown = ctx->voccxlastunk = 0; /* get the list of words, and read its length prefix */ wordp = runpoplst(rcx); wordsiz = osrp2(wordp) - 2; wordp += 2; /* get the list of types, and read its length prefix */ typp = runpoplst(rcx); typsiz = osrp2(typp) - 2; typp += 2; /* get the starting index (adjusting for zero-based indexing) */ cur = runpopnum(rcx) - 1; next = cur; /* get the flag arguments */ complain = runpoplog(rcx); multi = runpoplog(rcx); chkact = runpoplog(rcx); /* count the words in the word list */ for (wordcnt = 0, lstp = wordp, wordcharsiz = 0, lstsiz = wordsiz ; lstsiz != 0 ; lstadv(&lstp, &lstsiz)) { /* count the word */ ++wordcnt; /* * count the space needed for the word - count the bytes of the * string plus a null terminator */ if (*lstp == DAT_SSTRING) wordcharsiz += osrp2(lstp+1) + 1; } /* allocate space for the word arrays */ VOC_STK_ARRAY(ctx, char, wordchararr, wordcharsiz); VOC_STK_ARRAY(ctx, char *, wordarr, wordcnt+1); VOC_STK_ARRAY(ctx, int, typarr, wordcnt+1); /* build the word array */ for (i = 0, p = wordchararr, lstp = wordp, lstsiz = wordsiz ; lstsiz != 0 ; lstadv(&lstp, &lstsiz)) { /* add the word to the word array */ if (*lstp == DAT_SSTRING) { uint len; /* add this entry to the word array */ wordarr[i] = p; /* copy the word to the character array and null-terminate it */ len = osrp2(lstp + 1) - 2; memcpy(p, lstp + 3, len); p[len] = '\0'; /* move the write pointer past this entry */ p += len + 1; /* bump the index */ ++i; } } /* store an empty last entry */ wordarr[i] = 0; /* build the type array */ for (i = 0, lstp = typp, lstsiz = typsiz ; lstsiz != 0 && i < wordcnt ; lstadv(&lstp, &lstsiz)) { /* add this entry to the type array */ if (*lstp == DAT_NUMBER) { /* set the entry */ typarr[i] = (int)osrp4(lstp + 1); /* bump the index */ ++i; } } /* parse the noun phrase */ cnt = vocgobj(ctx, wordarr, typarr, cur, &next, complain, objlist, multi, chkact, &no_match); /* restore context unknown values */ ctx->voccxunknown = old_unknown; ctx->voccxlastunk = old_lastunk; /* check the return value */ if (cnt < 0) { /* syntax error; return nil to indicate an error */ runpnil(rcx); } else if (cnt == 0) { /* * No objects found. Return a list consisting only of the next * index. If the next index is equal to the starting index, * this will tell the caller that no noun phrase is * syntactically present; otherwise, it will tell the caller * that a noun phrase is present but there are no matching * objects. * * Note that we must increment the returned element index to * conform with the 1-based index values that the game function * uses. */ ++next; voc_push_numlist(ctx, (uint *)&next, 1); } else { /* * We found one or more objects. Return a list whose first * element is the index of the next word to be parsed, and whose * remaining elements are sublists. Each sublist contains a * match for one noun phrase; for each AND adding another noun * phrase, there's another sublist. Each sublist contains the * index of the first word of its noun phrase, the index of the * last word of its noun phrase, and then the objects. For each * object, there is a pair of entries: the object itself, and * the flags for the object. * * First, figure out how much space we need by scanning the * return list. */ for (lstsiz = 0, i = 0 ; i < cnt ; ) { int j; /* * count the entries in this sublist by looking for the next * entry whose starting word is different */ for (j = i ; j < cnt && objlist[j].vocolfst == objlist[i].vocolfst ; ++j) { /* * for this entry, we need space for the object (1 + 2 * for an object, or just 1 for nil) and flags (1 + 4) */ if (objlist[j].vocolobj == MCMONINV) lstsiz += 1; else lstsiz += 3; lstsiz += 5; } /* * For this sublist, we need space for the first index (type * prefix + number = 1 + 4 = 5) and the last index (5). * We've already counted space for the objects in the list. * Finally, we need space for the list type and length * prefixes (1 + 2) for the sublist itself. */ lstsiz += (5 + 5) + 3; /* skip to the next element */ i = j; } /* * finally, we need space for the first element of the list, * which is the index of the next word to be parsed (1+4) */ lstsiz += 5; /* allocate space for the list */ lstp = voc_push_list_siz(ctx, lstsiz); /* * store the first element - the index of the next word to parse * (adjusting to 1-based indexing) */ *lstp++ = DAT_NUMBER; oswp4s(lstp, next + 1); lstp += 4; /* build the list */ for (i = 0 ; i < cnt ; ) { uchar *sublstp; int j; int firstidx; int lastidx; /* store the list type prefix */ *lstp++ = DAT_LIST; /* * remember where the length prefix is, then skip it - we'll * come back and fill it in when we're done with the sublist */ sublstp = lstp; lstp += 2; /* search the array to find the indices of the boundary words */ for (j = 0 ; j < wordcnt ; ++j) { /* if this is the first word, remember the index */ if (wordarr[j] == objlist[i].vocolfst) firstidx = j; /* if this is the last word, remember the index */ if (wordarr[j] == objlist[i].vocollst) { /* remember the index */ lastidx = j; /* * we can stop looking now, since the words are * always in order (so the first index will never be * after the last index) */ break; } } /* add the first and last index, adjusting to 1-based indexing */ *lstp++ = DAT_NUMBER; oswp4s(lstp, firstidx + 1); lstp += 4; *lstp++ = DAT_NUMBER; oswp4s(lstp, lastidx + 1); lstp += 4; /* add the objects */ for (j = i ; j < cnt && objlist[j].vocolfst == objlist[i].vocolfst ; ++j) { /* add this object */ if (objlist[j].vocolobj == MCMONINV) { /* just store a nil */ *lstp++ = DAT_NIL; } else { /* store the object */ *lstp++ = DAT_OBJECT; oswp2(lstp, objlist[j].vocolobj); lstp += 2; } /* add the flags */ *lstp++ = DAT_NUMBER; oswp4s(lstp, objlist[i].vocolflg); lstp += 4; } /* fix up the sublist's length prefix */ oswp2(sublstp, lstp - sublstp); /* move on to the next sublist */ i = j; } } /* exit the stack frame */ voc_leave(ctx, save_sp); } /* ------------------------------------------------------------------------ */ /* * TADS program code interface - given a list of words and a list of * their types, produce a list of objects that match all of the words. */ void voc_parse_dict_lookup(voccxdef *ctx) { uchar *save_sp; runcxdef *rcx = ctx->voccxrun; uchar *wrdp; uint wrdsiz; uchar *typp; uint typsiz; objnum *list1; objnum *list2; int cnt1; int cnt2; /* enter our stack frame and allocate stack arrays */ voc_enter(ctx, &save_sp); VOC_MAX_ARRAY(ctx, objnum, list1); VOC_MAX_ARRAY(ctx, objnum, list2); /* get the word list, and read and skip its size prefix */ wrdp = runpoplst(rcx); wrdsiz = osrp2(wrdp) - 2; wrdp += 2; /* get the type list, and read and skip its size prefix */ typp = runpoplst(rcx); typsiz = osrp2(typp) - 2; typp += 2; /* nothing in the main list yet */ cnt1 = 0; /* go through the word list */ while (wrdsiz > 0) { int curtyp; int type_prop; char *curword; uint curwordlen; char *curword2; uint curwordlen2; vocwdef *v; char *p; uint len; vocseadef search_ctx; /* if this entry is a string, consider it */ if (*wrdp == DAT_SSTRING) { /* get the current word's text string */ curword = (char *)(wrdp + 3); curwordlen = osrp2(wrdp+1) - 2; /* check for an embedded space */ for (p = curword, len = curwordlen ; len != 0 && !t_isspace(*p) ; ++p, --len) ; if (len != 0) { /* get the second word */ curword2 = p + 1; curwordlen2 = len - 1; /* truncate the first word accordingly */ curwordlen -= len; } else { /* no embedded space -> no second word */ curword2 = 0; curwordlen2 = 0; } /* presume we won't find a valid type property */ type_prop = PRP_INVALID; /* * get this type entry, if there's another entry in the * list, and it's of the appropriate type */ if (typsiz > 0 && *typp == DAT_NUMBER) { /* * Figure out what type property we'll be using. We'll * consider only one meaning for each word, and we'll * arbitrarily pick one if the type code has more than * one type, because we expect the caller to provide * exactly one type per word. */ int i; struct typemap_t { int flag; int prop; }; static struct typemap_t typemap[] = { { VOCT_ARTICLE, PRP_ARTICLE }, { VOCT_ADJ, PRP_ADJ }, { VOCT_NOUN, PRP_NOUN }, { VOCT_PREP, PRP_PREP }, { VOCT_VERB, PRP_VERB }, { VOCT_PLURAL, PRP_PLURAL } }; struct typemap_t *mapp; /* get the type */ curtyp = (int)osrp4(typp+1); /* search for a type */ for (mapp = typemap, i = sizeof(typemap)/sizeof(typemap[0]) ; i != 0 ; ++mapp, --i) { /* if this flag is set, use this type property */ if ((curtyp & mapp->flag) != 0) { /* use this one */ type_prop = mapp->prop; break; } } } /* nothing in the new list yet */ cnt2 = 0; /* scan for matching words */ for (v = vocffw(ctx, curword, curwordlen, curword2, curwordlen2, type_prop, &search_ctx) ; v != 0 ; v = vocfnw(ctx, &search_ctx)) { int i; /* make sure we have room in our list */ if (cnt2 >= VOCMAXAMBIG - 1) break; /* make sure that this entry isn't already in our list */ for (i = 0 ; i < cnt2 ; ++i) { /* if this entry matches, stop looking */ if (list2[i] == v->vocwobj) break; } /* if it's not already in the list, add it now */ if (i == cnt2) { /* add it to our list */ list2[cnt2++] = v->vocwobj; } } /* terminate the list */ list2[cnt2] = MCMONINV; /* * if there's nothing in the first list, simply copy this * into the first list; otherwise, intersect the two lists */ if (cnt1 == 0) { /* this is the first list -> copy it into the main list */ memcpy(list1, list2, (cnt2+1)*sizeof(list2[0])); cnt1 = cnt2; } else { /* intersect the two lists */ cnt1 = vocisect(list1, list2); } /* * if there's nothing in the result list now, there's no * need to look any further, because further intersection * will yield nothing */ if (cnt1 == 0) break; } /* advance the word list */ lstadv(&wrdp, &wrdsiz); /* if there's anything left in the type list, advance it as well */ if (typsiz > 0) lstadv(&typp, &typsiz); } /* push the result list */ voc_push_objlist(ctx, list1, cnt1); /* exit our stack frame */ voc_leave(ctx, save_sp); } /* ------------------------------------------------------------------------ */ /* * TADS program code interface - disambiguate a noun list. We take the * kind of complex object list returned by voc_parse_np(), and produce a * fully-resolved list of objects. */ void voc_parse_disambig(voccxdef *ctx) { voccxdef ctx_copy; uchar *save_sp; runcxdef *rcx = ctx->voccxrun; vocoldef *inlist; vocoldef *outlist; int objcnt; char **cmd; char *cmdbuf; char *oopsbuf; objnum actor; objnum verb; objnum prep; objnum otherobj; prpnum defprop; prpnum accprop; prpnum verprop; uchar *tokp; uint toksiz; uchar *objp; uint objsiz; uchar *lstp; uint lstsiz; int tokcnt; char *p; int i; int silent; int err; int unknown_count; /* allocate our stack arrays */ voc_enter(ctx, &save_sp); VOC_MAX_ARRAY(ctx, vocoldef, outlist); VOC_STK_ARRAY(ctx, char, cmdbuf, VOCBUFSIZ); VOC_STK_ARRAY(ctx, char, oopsbuf, VOCBUFSIZ); /* get the actor, verb, prep, and otherobj arguments */ actor = runpopobj(rcx); verb = runpopobj(rcx); prep = runpopobjnil(rcx); otherobj = runpopobjnil(rcx); /* * get the usage parameter, and use it to select the appropriate * defprop and accprop */ switch(runpopnum(rcx)) { case VOC_PRO_RESOLVE_DOBJ: default: defprop = PRP_DODEFAULT; accprop = PRP_VALIDDO; break; case VOC_PRO_RESOLVE_IOBJ: defprop = PRP_IODEFAULT; accprop = PRP_VALIDIO; break; case VOC_PRO_RESOLVE_ACTOR: defprop = PRP_DODEFAULT; accprop = PRP_VALIDACTOR; break; } /* get the verprop argument */ verprop = runpopprp(rcx); /* pop the token list, and read and skip the length prefix */ tokp = runpoplst(rcx); toksiz = osrp2(tokp) - 2; tokp += 2; /* pop the object list, and read and skip the length prefix */ objp = runpoplst(rcx); objsiz = osrp2(objp) - 2; objp += 2; /* pop the "silent" flag */ silent = runpoplog(rcx); /* count the tokens */ for (lstp = tokp, lstsiz = toksiz, tokcnt = 0 ; lstsiz != 0 ; lstadv(&lstp, &lstsiz)) { /* if this is a string, count it */ if (*lstp == DAT_SSTRING) ++tokcnt; } /* allocate stack space for the command pointers and buffer */ VOC_STK_ARRAY(ctx, char *, cmd, tokcnt + 1); /* extract the tokens into our pointer buffer */ for (lstp = tokp, lstsiz = toksiz, i = 0, p = cmdbuf ; lstsiz != 0 ; lstadv(&lstp, &lstsiz)) { /* if this is a string, count and size it */ if (*lstp == DAT_SSTRING) { uint len; /* store a pointer to the word in the command buffer */ cmd[i++] = p; /* copy the token into the command buffer and null-terminate it */ len = osrp2(lstp + 1) - 2; memcpy(p, lstp + 3, len); p[len] = '\0'; /* move the buffer pointer past this word */ p += len + 1; } } /* store a null at the end of the command pointer list */ cmd[i] = 0; /* * The object list is provided in the same format as the list * returned by voc_parse_np(), but the leading index number is * optional. We don't need the leading index for anything, so if * it's there, simply skip it so that we can start with the first * sublist. */ if (objsiz > 0 && *objp == DAT_NUMBER) lstadv(&objp, &objsiz); /* * Count the objects in the object list, so that we can figure out * how much we need to allocate for the input object list. */ for (lstp = objp, lstsiz = objsiz, objcnt = 0 ; lstsiz != 0 ; lstadv(&lstp, &lstsiz)) { /* if this is a sublist, parse it */ if (*lstp == DAT_LIST) { uchar *subp; uint subsiz; /* get the sublist pointer, and read and skip the size prefix */ subp = lstp + 1; subsiz = osrp2(subp) - 2; subp += 2; /* scan the sublist */ while (subsiz > 0) { /* if this is an object, count it */ if (*subp == DAT_OBJECT || *subp == DAT_NIL) ++objcnt; /* skip this element */ lstadv(&subp, &subsiz); } } } /* allocate space for the input list */ VOC_STK_ARRAY(ctx, vocoldef, inlist, objcnt + 1); /* we don't have any unknown words yet */ unknown_count = 0; /* parse the list, filling in the input array */ for (lstp = objp, lstsiz = objsiz, i = 0 ; lstsiz != 0 ; lstadv(&lstp, &lstsiz)) { /* if this is a sublist, parse it */ if (*lstp == DAT_LIST) { uchar *subp; uint subsiz; int firstwrd, lastwrd; /* get the sublist pointer, and read and skip the size prefix */ subp = lstp + 1; subsiz = osrp2(subp) - 2; subp += 2; /* in case we don't find token indices, clear them */ firstwrd = 0; lastwrd = 0; /* * the first two elements of the list are the token indices * of the first and last words of this object's noun phrase */ if (subsiz > 0 && *subp == DAT_NUMBER) { /* read the first index, adjusting to zero-based indexing */ firstwrd = (int)osrp4(subp+1) - 1; /* make sure it's in range */ if (firstwrd < 0) firstwrd = 0; else if (firstwrd > tokcnt) firstwrd = tokcnt; /* skip it */ lstadv(&subp, &subsiz); } if (subsiz > 0 && *subp == DAT_NUMBER) { /* read the last index, adjusting to zero-based indexing */ lastwrd = (int)osrp4(subp+1) - 1; /* make sure it's in range */ if (lastwrd < firstwrd) lastwrd = firstwrd; else if (lastwrd > tokcnt) lastwrd = tokcnt; /* skip it */ lstadv(&subp, &subsiz); } /* scan the sublist */ while (subsiz > 0) { /* if this is an object, store it */ if (*subp == DAT_OBJECT || *subp == DAT_NIL) { /* store the object */ if (*subp == DAT_OBJECT) inlist[i].vocolobj = osrp2(subp+1); else inlist[i].vocolobj = MCMONINV; /* set the first and last word pointers */ inlist[i].vocolfst = cmd[firstwrd]; inlist[i].vocollst = cmd[lastwrd]; /* skip the object */ lstadv(&subp, &subsiz); /* check for flags */ if (subsiz > 0 && *subp == DAT_NUMBER) { /* get the flags value */ inlist[i].vocolflg = (int)osrp4(subp+1); /* skip the number in the list */ lstadv(&subp, &subsiz); } else { /* clear the flags */ inlist[i].vocolflg = 0; } /* if an unknown word was involved, note it */ if ((inlist[i].vocolflg & VOCS_UNKNOWN) != 0) ++unknown_count; /* consume the element */ ++i; } else { /* skip this element */ lstadv(&subp, &subsiz); } } } } /* terminate the list */ inlist[i].vocolobj = MCMONINV; inlist[i].vocolflg = 0; /* * make a copy of our context, so the disambiguation can't make any * global changes */ memcpy(&ctx_copy, ctx, sizeof(ctx_copy)); /* * Count the unknown words and set the count in the context. This * will allow us to determine after we call the resolver whether the * resolution process cleared up the unknown words (via * parseUnknownDobj/Iobj). */ ctx_copy.voccxunknown = ctx_copy.voccxlastunk = unknown_count; /* disambiguate the noun list */ err = vocdisambig(&ctx_copy, outlist, inlist, defprop, accprop, verprop, cmd, otherobj, actor, verb, prep, cmdbuf, silent); /* * If the error was VOCERR(2) - unknown word - check the input list * to see if it contained any unknown words. If it does, and we're * not in "silent" mode, flag the error and then give the user a * chance to use "oops" to correct the problem. If we're in silent * mode, don't display an error and don't allow interactive * correction via "oops." * * It is possible that the unknown word is not in the input list, * but in the user's response to an interactive disambiguation * query. This is why we must check to see if the unknown word is * in the original input list or not. */ if (err == VOCERR(2) && ctx_copy.voccxunknown != 0 && !silent) { char *unk; int unk_idx; char *rpl_text; /* * forget we have unknown words, since we're going to handle * them now */ ctx_copy.voccxunknown = 0; /* * find the unknown word - look up each word until we find one * that's not in the dictionary */ for (i = 0, unk = 0 ; cmd[i] != 0 ; ++i) { int t; /* * get this word's type - if the word has no type, it's an * unknown word */ t = voc_lookup_type(ctx, cmd[i], strlen(cmd[i]), TRUE); if (t == 0) { /* this is it - note it and stop searching */ unk_idx = i; unk = cmd[i]; break; } } /* * if we didn't find any unknown words, assume the first word * was unknown */ if (unk == 0) { unk_idx = 0; unk = cmd[0]; } /* display an error, and read a new command */ rpl_text = voc_read_oops(&ctx_copy, oopsbuf, VOCBUFSIZ, unk); /* * if they didn't respond with "oops," treat the response as a * brand new command to replace the current command */ if (rpl_text == 0) { /* * it's a replacement command - set the redo flag to * indicate that we should process the replacement command */ ctx_copy.voccxredo = TRUE; /* copy the response into the command buffer */ strcpy(cmdbuf, oopsbuf); } else { /* indicate the correction via the result code */ err = VOCERR(45); /* * Build the new command string. The new command string * consists of all of the tokens up to the unknown token, * then the replacement text, then all of the remaining * tokens. */ for (p = cmdbuf, i = 0 ; cmd[i] != 0 ; ++i) { size_t needed; /* figure the size needs for this token */ if (i == unk_idx) { /* we need to insert the replacement text */ needed = strlen(rpl_text); } else { /* we need to insert this token string */ needed = strlen(cmd[i]); } /* * if more tokens follow, we need a space after the * replacement text to separate it from what follows */ if (cmd[i+1] != 0 && needed != 0) needed += 1; /* leave room for null termination */ needed += 1; /* if we don't have room for this token, stop now */ if (needed > (size_t)(VOCBUFSIZ - (p - cmdbuf))) break; /* * if we've reached the unknown token, insert the * replacement text; otherwise, insert this token */ if (i == unk_idx) { /* insert the replacement text */ strcpy(p, rpl_text); } else if (*cmd[i] == '"') { char *p1; char qu; /* * Scan the quoted string for embedded double quotes * - if it has any, use single quotes as the * delimiter; otherwise, use double quotes as the * delimiter. Note that we ignore the first and * last characters in the string, since these are * always the delimiting double quotes in the * original token text. */ for (qu = '"', p1 = cmd[i] + 1 ; *p1 != '\0' && *(p1 + 1) != '\0' ; ++p1) { /* check for an embedded double quote */ if (*p1 == '"') { /* switch to single quotes as delimiters */ qu = '\''; /* no need to look any further */ break; } } /* add the open quote */ *p++ = qu; /* * add the text, leaving out the first and last * characters (which are the original quotes) */ if (strlen(cmd[i]) > 2) { memcpy(p, cmd[i] + 1, strlen(cmd[i]) - 2); p += strlen(cmd[i]) - 2; } /* add the closing quote */ *p++ = qu; /* null-terminate here so we don't skip any further */ *p = '\0'; } else { /* copy this word */ strcpy(p, cmd[i]); } /* move past this token */ p += strlen(p); /* add a space if another token follows */ if (cmd[i+1] != 0) *p++ = ' '; } /* null-terminate the replacement text */ *p = '\0'; } } /* * Count the objects. An object list is returned only on success or * VOCERR(44), which indicates that the list is still ambiguous. */ if (err == 0 || err == VOCERR(44)) { /* count the objects in the list */ for (i = 0 ; outlist[i].vocolobj != MCMONINV ; ++i) ; objcnt = i; } else { /* there's nothing in the list */ objcnt = 0; } /* figure out how much space we need for the objects */ lstsiz = (1+2) * objcnt; /* add space for the first element, which contains the status code */ lstsiz += (1 + 4); /* if there's a new command string, we'll store it, so make room */ if (ctx_copy.voccxredo || err == VOCERR(45)) { /* * add space for the type prefix (1), length prefix (2), and the * string bytes (with no null terminator, of course) */ lstsiz += (1 + 2 + strlen(cmdbuf)); /* * if we're retrying due to the redo flag, always return the * RETRY error code, regardless of what caused us to retry the * command */ if (ctx_copy.voccxredo) err = VOCERR(43); } /* push a list with space for the objects */ lstp = voc_push_list_siz(ctx, lstsiz); /* store the status code in the first element */ *lstp++ = DAT_NUMBER; oswp4s(lstp, err); lstp += 4; /* store the remainder of the list */ if (err == 0 || err == VOCERR(44)) { /* fill in the list with the objects */ for (i = 0 ; i < objcnt ; ++i) { /* set this element */ *lstp++ = DAT_OBJECT; oswp2(lstp, outlist[i].vocolobj); lstp += 2; } } else if (ctx_copy.voccxredo || err == VOCERR(45)) { uint len; /* there's a new command - return it as the second element */ *lstp++ = DAT_SSTRING; /* store the length */ len = strlen(cmdbuf); oswp2(lstp, len + 2); lstp += 2; /* store the string */ memcpy(lstp, cmdbuf, len); } /* leave the stack frame */ voc_leave(ctx, save_sp); } /* ------------------------------------------------------------------------ */ /* * TADS program code interface - replace the current command line with a * new string, aborting the current command. */ void voc_parse_replace_cmd(voccxdef *ctx) { runcxdef *rcx = ctx->voccxrun; uchar *p; uint len; /* get the string */ p = runpopstr(rcx); /* read and skip the length prefix */ len = osrp2(p) - 2; p += 2; /* make sure it fits in the redo buffer - truncate it if necessary */ if (len + 1 > VOCBUFSIZ) len = VOCBUFSIZ - 1; /* copy the string and null-terminate it */ memcpy(ctx->voccxredobuf, p, len); ctx->voccxredobuf[len] = '\0'; /* set the "redo" flag so that we execute what's in the buffer */ ctx->voccxredo = TRUE; /* abort the current command so that we start anew with the replacement */ errsig(ctx->voccxerr, ERR_RUNABRT); } /* ------------------------------------------------------------------------ */ /* * This routine gets an actor, which is just a single object reference at * the beginning of a sentence. We return 0 if we fail to find an actor; * since this can be either a harmless or troublesome condition, we must * return additional information. The method used to return back ERROR/OK * is to set *next != cur if there is an error, *next == cur if not. So, * getting back (objdef*)0 means that you should check *next. If the return * value is nonzero, then that object is the actor. */ static objnum vocgetactor(voccxdef *ctx, char *cmd[], int typelist[], int cur, int *next, char *cmdbuf) { int l; vocoldef *nounlist; vocoldef *actlist; int cnt; uchar *save_sp; prpnum valprop, verprop; voc_enter(ctx, &save_sp); VOC_MAX_ARRAY(ctx, vocoldef, nounlist); VOC_MAX_ARRAY(ctx, vocoldef, actlist); *next = cur; /* assume no error will occur */ cnt = vocchknoun(ctx, cmd, typelist, cur, next, nounlist, TRUE); if (cnt > 0 && *next != -1 && cmd[*next] && vocspec(cmd[*next], VOCW_AND)) { int have_unknown; /* make a note as to whether the list contains an unknown word */ have_unknown = ((nounlist[0].vocolflg & VOCS_UNKNOWN) != 0); /* * If validActor is defined for any of the actors, use it; * otherwise, for compatibility with past versions, use the * takeVerb disambiguation mechanism. If we have a pronoun, we * can't decide yet how to do this, so presume that we'll use * the new mechanism and switch later if necessary. * * If we have don't have a valid object (which will be the case * for a pronoun), we can't decide until we get into the * disambiguation process, so presume we'll use validActor for * now. */ verprop = PRP_VERACTOR; if (nounlist[0].vocolobj == MCMONINV || objgetap(ctx->voccxmem, nounlist[0].vocolobj, PRP_VALIDACTOR, (objnum *)0, FALSE)) valprop = PRP_VALIDACTOR; else valprop = PRP_VALIDDO; /* disambiguate it using the selected properties */ if (vocdisambig(ctx, actlist, nounlist, PRP_DODEFAULT, valprop, verprop, cmd, MCMONINV, ctx->voccxme, ctx->voccxvtk, MCMONINV, cmdbuf, FALSE)) { /* * if we have an unknown word in the list, assume for the * moment that this isn't an actor phrase after all, but a * verb */ if (have_unknown) { /* indicate that we didn't find a noun phrase syntactically */ *next = cur; } /* return no actor */ VOC_RETVAL(ctx, save_sp, MCMONINV); } if ((l = voclistlen(actlist)) > 1) { vocerr(ctx, VOCERR(12), "You can only speak to one person at a time."); *next = cur + 1; /* error flag - return invalid but next!=cur */ VOC_RETVAL(ctx, save_sp, MCMONINV); } else if (l == 0) return(MCMONINV); if (cmd[*next] && vocspec(cmd[*next], VOCW_AND)) { ++(*next); VOC_RETVAL(ctx, save_sp, actlist[0].vocolobj); } } if (cnt < 0) { /* error - make *next != cur */ *next = cur + 1; } else *next = cur; /* no error condition, but nothing found */ VOC_RETVAL(ctx, save_sp, MCMONINV); /* so return invalid and *next == cur */ } /* figure out how many objects are in an object list */ int voclistlen(vocoldef *lst) { int i; for (i = 0 ; lst->vocolobj != MCMONINV || lst->vocolflg != 0 ; ++lst, ++i) ; return(i); } /* * check access - evaluates cmdVerb.verprop(actor, obj, seqno), and * returns whatever it returns. The seqno parameter is used for special * cases, such as "ask", when the validation routine wishes to return * "true" on the first object and "nil" on all subsequent objects which * correspond to a particular noun phrase. We expect to be called with * seqno==0 on the first object, non-zero on others; we will pass * seqno==1 on the first object to the validation property, higher on * subsequent objects, to maintain consistency with the TADS language * convention of indexing from 1 up (as seen by the user in indexing * functions). Note that if we're checking an actor, we'll just call * obj.validActor() for the object itself (not the verb). */ int vocchkaccess(voccxdef *ctx, objnum obj, prpnum verprop, int seqno, objnum cmdActor, objnum cmdVerb) { /* * special case: the special "string" and "number" objects are * always accessible */ if (obj == ctx->voccxstr || obj == ctx->voccxnum) return TRUE; /* * If the access method is validActor, make sure the object in fact * has a validActor method defined; if it doesn't, we must be * running a game from before validActor's invention, so use the old * ValidXo mechanism instead. */ if (verprop == PRP_VALIDACTOR) { /* checking an actor - check to see if ValidActor is defined */ if (objgetap(ctx->voccxmem, obj, PRP_VALIDACTOR, (objnum *)0, FALSE)) { /* ValidActor is present - call ValidActor in the object itself */ runppr(ctx->voccxrun, obj, verprop, 0); /* return the result */ return runpoplog(ctx->voccxrun); } else { /* there's no ValidActor - call ValidXo in the "take" verb */ cmdVerb = ctx->voccxvtk; verprop = PRP_VALIDDO; } } /* call ValidXo in the verb */ runpnum(ctx->voccxrun, (long)(seqno + 1)); runpobj(ctx->voccxrun, obj); runpobj(ctx->voccxrun, (objnum)(cmdActor == MCMONINV ? ctx->voccxme : cmdActor)); runppr(ctx->voccxrun, cmdVerb, verprop, 3); /* return the result */ return runpoplog(ctx->voccxrun); } /* ask game if object is visible to the actor */ int vocchkvis(voccxdef *ctx, objnum obj, objnum cmdActor) { runpobj(ctx->voccxrun, (objnum)(cmdActor == MCMONINV ? ctx->voccxme : cmdActor)); runppr(ctx->voccxrun, obj, (prpnum)PRP_ISVIS, 1); return(runpoplog(ctx->voccxrun)); } /* set {numObj | strObj}.value, as appropriate */ void vocsetobj(voccxdef *ctx, objnum obj, dattyp typ, void *val, vocoldef *inobj, vocoldef *outobj) { *outobj = *inobj; outobj->vocolobj = obj; objsetp(ctx->voccxmem, obj, PRP_VALUE, typ, val, ctx->voccxundo); } /* set up a vocoldef */ static void vocout(vocoldef *outobj, objnum obj, int flg, char *fst, char *lst) { outobj->vocolobj = obj; outobj->vocolflg = flg; outobj->vocolfst = fst; outobj->vocollst = lst; } /* * Generate an appropriate error message saying that the objects in the * command are visible, but can't be used with the command for some * reason. Use the cantReach method of the verb (the new way), or if * there is no cantReach in the verb, of each object in the list. */ void vocnoreach(voccxdef *ctx, objnum *list1, int cnt, objnum actor, objnum verb, objnum prep, prpnum defprop, int show_multi_prefix, int multi_flags, int multi_base_index, int multi_total_count) { /* see if the verb has a cantReach method - use it if so */ if (objgetap(ctx->voccxmem, verb, PRP_NOREACH, (objnum *)0, FALSE)) { /* push arguments: (actor, dolist, iolist, prep) */ runpobj(ctx->voccxrun, prep); if (defprop == PRP_DODEFAULT) { runpnil(ctx->voccxrun); voc_push_objlist(ctx, list1, cnt); } else { voc_push_objlist(ctx, list1, cnt); runpnil(ctx->voccxrun); } runpobj(ctx->voccxrun, actor); /* invoke the method in the verb */ runppr(ctx->voccxrun, verb, PRP_NOREACH, 4); } else { int i; /* use the old way - call obj.cantReach() for each object */ for (i = 0 ; i < cnt ; ++i) { /* * display this object's name if there's more than one, so * that the player can tell to which object this message * applies */ voc_multi_prefix(ctx, list1[i], show_multi_prefix, multi_flags, multi_base_index + i, multi_total_count); /* call cantReach on the object */ runpobj(ctx->voccxrun, (objnum)(actor == MCMONINV ? ctx->voccxme : actor)); runppr(ctx->voccxrun, list1[i], (prpnum)PRP_NOREACH, 1); tioflush(ctx->voccxtio); } } } /* * Get the specialWords string for a given special word entry. Returns * the first string if multiple strings are defined for the entry. */ static void voc_get_spec_str(voccxdef *ctx, char vocw_id, char *buf, size_t buflen, const char *default_name) { int found; /* presume we won't find it */ found = FALSE; /* if there's a special word list, search it for this entry */ if (ctx->voccxspp != 0) { char *p; char *endp; size_t len; /* find appropriate user-defined word in specialWords list */ for (p = ctx->voccxspp, endp = p + ctx->voccxspl ; p < endp ; ) { /* if this one matches, get its first word */ if (*p++ == vocw_id) { /* note that we found it */ found = TRUE; /* * get the length, and limit it to the buffer size, * leaving room for null termination */ len = *p++; if (len + 1 > buflen) len = buflen - 1; /* copy it and null-terminate the string */ memcpy(buf, p, len); buf[len] = '\0'; /* we found it - no need to look any further */ break; } /* * move on to the next one - skip the length prefix plus the * length */ p += *p + 1; } } /* if we didn't find it, use the default */ if (!found) { strncpy(buf, default_name, (size_t)buflen); buf[buflen - 1] = '\0'; } } /* set it/him/her */ static int vocsetit(voccxdef *ctx, objnum obj, int accprop, objnum actor, objnum verb, objnum prep, vocoldef *outobj, char *default_name, char vocw_id, prpnum defprop, int silent) { if (obj == MCMONINV || !vocchkaccess(ctx, obj, (prpnum)accprop, 0, actor, verb)) { char nambuf[40]; /* get the display name for this specialWords entry */ voc_get_spec_str(ctx, vocw_id, nambuf, sizeof(nambuf), default_name); /* show the error if appropriate */ if (!silent) { /* use 'noreach' if possible, otherwise use a default message */ if (obj == MCMONINV) vocerr(ctx, VOCERR(13), "I don't know what you're referring to with '%s'.", nambuf); else vocnoreach(ctx, &obj, 1, actor, verb, prep, defprop, FALSE, 0, 0, 1); } /* indicate that the antecedent is inaccessible */ return VOCERR(13); } /* set the object */ vocout(outobj, obj, 0, default_name, default_name); return 0; } /* * Get a new numbered object, given a number. This is used for objects * that define '#' as one of their adjectives; we call the object, * asking it to create an object with a particular number. The object * can return nil, in which case we'll reject the command. */ static objnum voc_new_num_obj(voccxdef *ctx, objnum objn, objnum actor, objnum verb, long num, int plural) { /* push the number - if we need a plural object, use nil instead */ if (plural) runpnil(ctx->voccxrun); else runpnum(ctx->voccxrun, num); /* push the other arguments and call the method */ runpobj(ctx->voccxrun, verb); runpobj(ctx->voccxrun, actor); runppr(ctx->voccxrun, objn, PRP_NEWNUMOBJ, 3); /* if it was rejected, return an invalid object, else return the object */ if (runtostyp(ctx->voccxrun) == DAT_NIL) { rundisc(ctx->voccxrun); return MCMONINV; } else return runpopobj(ctx->voccxrun); } /* check if an object defines the special adjective '#' */ static int has_gen_num_adj(voccxdef *ctx, objnum objn) { vocwdef *v; vocseadef search_ctx; /* scan the list of objects defined '#' as an adjective */ for (v = vocffw(ctx, "#", 1, (char *)0, 0, PRP_ADJ, &search_ctx) ; v ; v = vocfnw(ctx, &search_ctx)) { /* if this is the object, return positive indication */ if (v->vocwobj == objn) return TRUE; } /* didn't find it */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Call the deepverb's disambigDobj or disambigIobj method to perform * game-controlled disambiguation. */ static int voc_disambig_hook(voccxdef *ctx, objnum verb, objnum actor, objnum prep, objnum otherobj, prpnum accprop, prpnum verprop, objnum *objlist, uint *flags, int *objcount, char *firstwrd, char *lastwrd, int num_wanted, int is_ambig, char *resp, int silent) { runcxdef *rcx = ctx->voccxrun; prpnum call_prop; runsdef val; uchar *lstp; uint lstsiz; int ret; int i; /* check for actor disambiguation */ if (verprop == PRP_VERACTOR) { /* do nothing on actor disambiguation */ return VOC_DISAMBIG_CONT; } /* figure out whether this is a dobj method or an iobj method */ call_prop = (accprop == PRP_VALIDDO ? PRP_DISAMBIGDO : PRP_DISAMBIGIO); /* if the method isn't defined, we can skip this entirely */ if (objgetap(ctx->voccxmem, verb, call_prop, (objnum *)0, FALSE) == 0) return VOC_DISAMBIG_CONT; /* push the "silent" flag */ val.runstyp = (silent ? DAT_TRUE : DAT_NIL); runpush(rcx, val.runstyp, &val); /* push the "is_ambiguous" flag */ val.runstyp = (is_ambig ? DAT_TRUE : DAT_NIL); runpush(rcx, val.runstyp, &val); /* push the "numWanted" count */ runpnum(rcx, num_wanted); /* push the flag list */ voc_push_numlist(ctx, flags, *objcount); /* push the object list */ voc_push_objlist(ctx, objlist, *objcount); /* push the word list */ voc_push_strlist(ctx, firstwrd, lastwrd); /* push the verification property */ val.runstyp = DAT_PROPNUM; val.runsv.runsvprp = verprop; runpush(rcx, DAT_PROPNUM, &val); /* push the other object */ runpobj(rcx, otherobj); /* push the preposition and the actor objects */ runpobj(rcx, prep); runpobj(rcx, actor); /* call the method */ runppr(rcx, verb, call_prop, 10); /* check the return value */ switch(runtostyp(rcx)) { case DAT_LIST: /* get the list */ lstp = runpoplst(rcx); /* read the list size prefix */ lstsiz = osrp2(lstp) - 2; lstp += 2; /* check for the status code */ if (lstsiz > 0 && *lstp == DAT_NUMBER) { /* get the status code */ ret = osrp4s(lstp+1); /* skip the element */ lstadv(&lstp, &lstsiz); } else { /* there's no status code - assume CONTINUE */ ret = VOC_DISAMBIG_CONT; } /* check for a PARSE_RESP return */ if (ret == VOC_DISAMBIG_PARSE_RESP) { /* the second element is the string */ if (*lstp == DAT_SSTRING) { uint len; /* get the length, and limit it to our buffer size */ len = osrp2(lstp+1) - 2; if (len > VOCBUFSIZ - 1) len = VOCBUFSIZ - 1; /* copy the string into the caller's buffer */ memcpy(resp, lstp+3, len); resp[len] = '\0'; } else { /* there's no string - ignore it */ ret = VOC_DISAMBIG_CONT; } } else { /* store the object list in the caller's list */ for (i = 0 ; lstsiz > 0 && i < VOCMAXAMBIG-1 ; ++i) { /* get this object */ if (*lstp == DAT_OBJECT) objlist[i] = osrp2(lstp+1); else objlist[i] = MCMONINV; /* skip the list entry */ lstadv(&lstp, &lstsiz); /* check for flags */ if (lstsiz > 0 && *lstp == DAT_NUMBER) { /* store the flags */ flags[i] = (int)osrp4(lstp+1); /* skip the flags elements */ lstadv(&lstp, &lstsiz); } else { /* no flags - use zero by default */ flags[i] = 0; } } /* store a terminator at the end of the list */ objlist[i] = MCMONINV; flags[i] = 0; /* store the output count for the caller */ *objcount = i; } /* return the result */ return ret; case DAT_NUMBER: /* get the status code */ ret = runpopnum(rcx); /* ignore raw PARSE_RESP codes, since they need to return a string */ if (ret == VOC_DISAMBIG_PARSE_RESP) ret = VOC_DISAMBIG_CONT; /* return the status */ return ret; default: /* treat anything else as CONTINUE */ rundisc(rcx); return VOC_DISAMBIG_CONT; } } /* ------------------------------------------------------------------------ */ /* * Prune a list of matches by keeping only the matches without the given * flag value, if we have a mix of entries with and without the flag. * This is a service routine for voc_prune_matches. * * The flag indicates a lower quality of matching, so this routine can * be used to reduce ambiguity by keeping only the best-quality matches * when matches of mixed quality are present. */ static int voc_remove_objs_with_flag(voccxdef *ctx, objnum *list, uint *flags, int cnt, int flag_to_remove) { int i; int flag_cnt; int special_cnt; /* first, count the number of objects with the flag */ for (i = 0, flag_cnt = special_cnt = 0 ; i < cnt ; ++i) { /* if this object exhibits the flag, count it */ if ((flags[i] & flag_to_remove) != 0) ++flag_cnt; /* if it's numObj or strObj, count it separately */ if (list[i] == ctx->voccxnum || list[i] == ctx->voccxstr) ++special_cnt; } /* * If all of the objects didn't have the flag, omit the ones that * did, so that we reduce the ambiguity to those without the flag. * Don't include the special objects (numObj and strObj) in the * count, since they will never have any of these flags set. */ if (flag_cnt != 0 && flag_cnt < cnt - special_cnt) { int dst; /* * Remove the flagged objects. Note that we can make this * adjustment to the arrays in place, because they can only * shrink - there's no need to make an extra temporary copy. */ for (i = 0, dst = 0 ; i < cnt ; ++i) { /* * If this one doesn't have the flag, keep it. Always keep * the special objects (numObj and strObj). */ if ((flags[i] & flag_to_remove) == 0 || list[i] == ctx->voccxnum || list[i] == ctx->voccxstr) { /* copy this one to the output location */ list[dst] = list[i]; flags[dst] = flags[i]; /* count the new element of the output */ ++dst; } } /* set the updated count */ cnt = dst; list[cnt] = MCMONINV; } /* return the new count */ return cnt; } /* * Prune a list of matches by keeping only the best matches when matches * of different qualities are present. * * If we have a mix of objects matching noun phrases that end in * adjectives and phrases ending in nouns with the same words, remove * those elements that end in adjectives, keeping only the better * matches that end in nouns. * * If we have a mix of objects where the words match exactly, and others * where the words are only leading substrings of longer dictionary * words, keep only the exact matches. * * Returns the number of elements in the result list. */ static int voc_prune_matches(voccxdef *ctx, objnum *list, uint *flags, int cnt) { /* remove matches that end with an adjective */ cnt = voc_remove_objs_with_flag(ctx, list, flags, cnt, VOCS_ENDADJ); /* remove matches that use truncated words */ cnt = voc_remove_objs_with_flag(ctx, list, flags, cnt, VOCS_TRUNC); /* return the new list size */ return cnt; } /* ------------------------------------------------------------------------ */ /* * Count indistinguishable items. * * If 'keep_all' is true, we'll keep all of the items, whether or not * some are indistinguishable from one another. If 'keep_all' is false, * we'll keep only one item from each set of indistinguishable items. */ static int voc_count_diff(voccxdef *ctx, objnum *list, uint *flags, int *cnt, int keep_all) { int i; int diff_cnt; /* * Presume all items will be distinguishable from one another. As * we scan the list for indistinguishable items, we'll decrement * this count each time we find an item that can't be distinguished * from another item. */ diff_cnt = *cnt; /* * Look for indistinguishable items. * * An object is distinguishable if it doesn't have the special * property marking it as one of a group of equivalent objects * (PRP_ISEQUIV), or if it has the property but there is no object * following it in the list which has the same immediate superclass. * * Note that we want to keep the duplicates if we're looking for * plurals, because the player is explicitly referring to all * matching objects. */ for (i = 0 ; i < *cnt ; ++i) { /* * check to see if this object might have indistinguishable * duplicates - it must be marked with isEquiv for this to be * possible */ runppr(ctx->voccxrun, list[i], PRP_ISEQUIV, 0); if (runpoplog(ctx->voccxrun)) { int j; int dst; objnum sc; /* get the superclass, if possible */ sc = objget1sc(ctx->voccxmem, list[i]); if (sc == MCMONINV) continue; /* * run through the remainder of the list, and remove any * duplicates of this item */ for (j = i + 1, dst = i + 1 ; j < *cnt ; ++j) { /* * see if it matches our object - if not, keep it in the * list by copying it to our destination position */ if (objget1sc(ctx->voccxmem, list[j]) != sc) { /* it's distinguishable - keep it */ list[dst] = list[j]; flags[dst++] = flags[j]; } else { /* * This item is indistinguishable from the list[i]. * First, reduce the count of different items. */ --diff_cnt; /* * Keep this object only if we're keeping all * redundant indistinguishable items. */ if (keep_all) { /* keep all items -> keep this item */ list[dst] = list[j]; flags[dst++] = flags[j]; } } } /* adjust the count to reflect the updated list */ *cnt = dst; /* add a terminator */ list[dst] = MCMONINV; flags[dst] = 0; } } /* return the number of distinguishable items */ return diff_cnt; } /* ------------------------------------------------------------------------ */ /* * vocdisambig - determines which nouns in a noun list apply. When this * is called, we must know the verb that we are processing, so we delay * disambiguation until quite late in the parsing of a sentence, opting * to keep all relevant information around until such time as we can * meaningfully disambiguate. * * This routine resolves any "all [except...]", "it", and "them" * references. We determine if all of the objects listed are accessible * (via verb.validDo, verb.validIo). We finally try to determine which * nouns apply when there are ambiguous nouns by using do.verDo * and io.verIo. */ int vocdisambig(voccxdef *ctx, vocoldef *outlist, vocoldef *inlist, prpnum defprop, prpnum accprop, prpnum verprop, char *cmd[], objnum otherobj, objnum cmdActor, objnum cmdVerb, objnum cmdPrep, char *cmdbuf, int silent) { int inpos; int outpos; int listlen = voclistlen(inlist); int noreach = FALSE; prpnum listprop; uchar *save_sp; int old_unknown, old_lastunk; int err; int still_ambig; voc_enter(ctx, &save_sp); ERRBEGIN(ctx->voccxerr) /* presume we will not leave any ambiguity in the result */ still_ambig = FALSE; /* loop through all of the objects in the input list */ for (inpos = outpos = 0 ; inpos < listlen ; ++inpos) { /* * reset the stack to our entrypoint value, since our stack * variables are all temporary for a single iteration */ voc_leave(ctx, save_sp); voc_enter(ctx, &save_sp); if (inlist[inpos].vocolflg == VOCS_STR) { vocsetobj(ctx, ctx->voccxstr, DAT_SSTRING, inlist[inpos].vocolfst + 1, &inlist[inpos], &outlist[outpos]); ++outpos; } else if (inlist[inpos].vocolflg == VOCS_NUM) { long v1; char vbuf[4]; v1 = atol(inlist[inpos].vocolfst); oswp4s(vbuf, v1); vocsetobj(ctx, ctx->voccxnum, DAT_NUMBER, vbuf, &inlist[inpos], &outlist[outpos]); ++outpos; } else if (inlist[inpos].vocolflg == VOCS_IT || (inlist[inpos].vocolflg == VOCS_THEM && ctx->voccxthc == 0)) { err = vocsetit(ctx, ctx->voccxit, accprop, cmdActor, cmdVerb, cmdPrep, &outlist[outpos], inlist[inpos].vocolflg == VOCS_IT ? "it" : "them", (char)(inlist[inpos].vocolflg == VOCS_IT ? VOCW_IT : VOCW_THEM), defprop, silent); if (err != 0) goto done; ++outpos; } else if (inlist[inpos].vocolflg == VOCS_HIM) { err = vocsetit(ctx, ctx->voccxhim, accprop, cmdActor, cmdVerb, cmdPrep, &outlist[outpos], "him", VOCW_HIM, defprop, silent); if (err != 0) goto done; ++outpos; } else if (inlist[inpos].vocolflg == VOCS_HER) { err = vocsetit(ctx, ctx->voccxher, accprop, cmdActor, cmdVerb, cmdPrep, &outlist[outpos], "her", VOCW_HER, defprop, silent); if (err != 0) goto done; ++outpos; } else if (inlist[inpos].vocolflg == VOCS_THEM) { int i; int thempos = outpos; static char them_name[] = "them"; for (i = 0 ; i < ctx->voccxthc ; ++i) { if (outpos >= VOCMAXAMBIG) { if (!silent) vocerr(ctx, VOCERR(11), "You're referring to too many objects."); err = VOCERR(11); goto done; } /* add object only if it's still accessible */ if (vocchkaccess(ctx, ctx->voccxthm[i], accprop, 0, cmdActor, cmdVerb)) { /* it's still accessible - add it to the list */ vocout(&outlist[outpos++], ctx->voccxthm[i], VOCS_THEM, them_name, them_name); } else { /* it's not accessible - complain about it */ vocnoreach(ctx, &ctx->voccxthm[i], 1, cmdActor, cmdVerb, cmdPrep, defprop, TRUE, VOCS_THEM, i, ctx->voccxthc); tioflush(ctx->voccxtio); } } /* make sure we found at least one acceptable object */ if (outpos == thempos) { if (!silent) vocerr(ctx, VOCERR(14), "I don't know what you're referring to."); err = VOCERR(14); goto done; } } else if (inlist[inpos].vocolflg == VOCS_ALL) { uchar *l; int exccnt = 0; int allpos = outpos; int k; uint len; static char all_name[] = "all"; vocoldef *exclist; vocoldef *exclist2; VOC_MAX_ARRAY(ctx, vocoldef, exclist); VOC_MAX_ARRAY(ctx, vocoldef, exclist2); if (defprop != PRP_IODEFAULT) runpobj(ctx->voccxrun, otherobj); runpobj(ctx->voccxrun, cmdPrep); runpobj(ctx->voccxrun, cmdActor); runppr(ctx->voccxrun, cmdVerb, defprop, defprop == PRP_DODEFAULT ? 3 : 2); if (runtostyp(ctx->voccxrun) == DAT_LIST) { l = runpoplst(ctx->voccxrun); len = osrp2(l) - 2; l += 2; while (len) { /* add list element to output if it's an object */ if (*l == DAT_OBJECT) vocout(&outlist[outpos++], (objnum)osrp2(l+1), 0, all_name, all_name); /* move on to next list element */ lstadv(&l, &len); } vocout(&outlist[outpos], MCMONINV, 0, (char *)0, (char *)0); } else rundisc(ctx->voccxrun); /* discard non-list value */ /* if we didn't get anything, complain about it and quit */ if (outpos <= allpos) { if (!silent) vocerr(ctx, VOCERR(15), "I don't see what you're referring to."); err = VOCERR(15); goto done; } /* remove any items in "except" list */ while (inlist[inpos + 1].vocolflg & VOCS_EXCEPT) { OSCPYSTRUCT(exclist[exccnt], inlist[++inpos]); exclist[exccnt++].vocolflg &= ~VOCS_EXCEPT; } exclist[exccnt].vocolobj = MCMONINV; exclist[exccnt].vocolflg = 0; /* disambiguate "except" list */ if (exccnt) { err = vocdisambig(ctx, exclist2, exclist, defprop, accprop, verprop, cmd, otherobj, cmdActor, cmdVerb, cmdPrep, cmdbuf, silent); if (err != 0) goto done; exccnt = voclistlen(exclist2); for (k = 0 ; k < exccnt ; ++k) { int i; for (i = allpos ; i < outpos ; ++i) { if (outlist[i].vocolobj == exclist2[k].vocolobj) { int j; for (j = i ; j < outpos ; ++j) outlist[j].vocolobj = outlist[j+1].vocolobj; --i; --outpos; if (outpos <= allpos) { if (!silent) vocerr(ctx, VOCERR(15), "I don't see what you're referring to."); err = VOCERR(15); goto done; } } } } } } else /* we have a (possibly ambiguous) noun */ { int lpos = inpos; int i = 0; int cnt; char *p; int cnt2, cnt3; int trying_again; int user_count = 0; objnum *cantreach_list; int unknown_count; int use_all_objs; objnum *list1; uint *flags1; objnum *list2; uint *flags2; objnum *list3; uint *flags3; char *usrobj; uchar *lstbuf; char *newobj; char *disnewbuf; char *disbuffer; char **diswordlist; int *distypelist; vocoldef *disnounlist; int dst; VOC_MAX_ARRAY(ctx, objnum, list1); VOC_MAX_ARRAY(ctx, objnum, list2); VOC_MAX_ARRAY(ctx, objnum, list3); VOC_MAX_ARRAY(ctx, uint, flags1); VOC_MAX_ARRAY(ctx, uint, flags2); VOC_MAX_ARRAY(ctx, uint, flags3); VOC_MAX_ARRAY(ctx, vocoldef, disnounlist); VOC_STK_ARRAY(ctx, char, disnewbuf, VOCBUFSIZ); VOC_STK_ARRAY(ctx, char, disbuffer, 2*VOCBUFSIZ); VOC_STK_ARRAY(ctx, char *, diswordlist, VOCBUFSIZ); VOC_STK_ARRAY(ctx, int, distypelist, VOCBUFSIZ); VOC_STK_ARRAY(ctx, char, usrobj, VOCBUFSIZ); VOC_STK_ARRAY(ctx, char, newobj, VOCBUFSIZ); VOC_STK_ARRAY(ctx, uchar, lstbuf, 2 + VOCMAXAMBIG*3); /* presume we won't resolve any unknown words */ unknown_count = 0; /* * Presume that we won't use all the objects that match * these words, since we normally want to try to find a * single, unambiguous match for a given singular noun * phrase. Under certain circumstances, we'll want to keep * all of the words that match the noun phrase, in which * case we'll set this flag accordingly. */ use_all_objs = FALSE; /* * go through the objects matching the current noun phrase * and add them into our list */ while (inlist[lpos].vocolfst == inlist[inpos].vocolfst && lpos < listlen) { /* add this object to the list of nouns */ list1[i] = inlist[lpos].vocolobj; /* * note whether this object matched a plural, whether it * matched adjective-at-end usage, and whether it * matched a truncated dictionary word */ flags1[i] = inlist[lpos].vocolflg & (VOCS_PLURAL | VOCS_ANY | VOCS_COUNT | VOCS_ENDADJ | VOCS_TRUNC); /* if this is a valid object, count it */ if (list1[i] != MCMONINV) ++i; /* if there's a user count, note it */ if ((inlist[lpos].vocolflg & VOCS_COUNT) != 0) user_count = atoi(inlist[lpos].vocolfst); /* if an unknown word was involved, note it */ if ((inlist[lpos].vocolflg & VOCS_UNKNOWN) != 0) ++unknown_count; /* move on to the next entry */ ++lpos; } /* terminate the list */ list1[i] = MCMONINV; cnt = i; /* * If this noun phrase contained an unknown word, check to * see if the verb defines the parseUnknownXobj() method. * If so, call the method and check the result. */ if (unknown_count > 0) { prpnum prp; /* * figure out which method to call - use * parseUnknownDobj if we're disambiguating the direct * object, parseUnknownIobj for the indirect object */ prp = (defprop == PRP_DODEFAULT ? PRP_PARSEUNKNOWNDOBJ : PRP_PARSEUNKNOWNIOBJ); /* check if the verb defines this method */ if (objgetap(ctx->voccxmem, cmdVerb, prp, (objnum *)0, FALSE)) { uchar *lstp; uint lstlen; /* trace the event for debugging */ if (ctx->voccxflg & VOCCXFDBG) tioputs(ctx->voccxtio, "... unknown word: calling " "parseUnknownXobj\\n"); /* push the list of words in the noun phrase */ voc_push_strlist(ctx, inlist[inpos].vocolfst, inlist[inpos].vocollst); /* push the other arguments */ runpobj(ctx->voccxrun, otherobj); runpobj(ctx->voccxrun, cmdPrep); runpobj(ctx->voccxrun, cmdActor); /* call the method */ runppr(ctx->voccxrun, cmdVerb, prp, 4); /* see what they returned */ switch(runtostyp(ctx->voccxrun)) { case DAT_OBJECT: /* * use the object they returned as the match for * the noun phrase */ list1[cnt++] = runpopobj(ctx->voccxrun); /* terminate the new list */ list1[cnt] = MCMONINV; break; case DAT_LIST: /* * use the list of objects they returned as the * match for the noun phrase */ lstp = runpoplst(ctx->voccxrun); /* get the length of the list */ lstlen = osrp2(lstp) - 2; lstp += 2; /* run through the list's elements */ while (lstlen != 0) { /* if this is an object, add it */ if (*lstp == DAT_OBJECT && i < VOCMAXAMBIG) list1[cnt++] = osrp2(lstp+1); /* move on to the next element */ lstadv(&lstp, &lstlen); } /* * Note that we want to use all of these objects * without disambiguation, since the game code * has explicitly said that this is the list * that matches the given noun phrase. */ use_all_objs = TRUE; /* terminate the new list */ list1[cnt] = MCMONINV; break; case DAT_TRUE: /* * A 'true' return value indicates that the * parseUnknownXobj routine has fully handled * the command. They don't want anything more * to be done with these words. Simply remove * the unknown words and continue with any other * words in the list. */ rundisc(ctx->voccxrun); /* we're done with this input phrase */ continue; default: /* * For anything else, use the default mechanism. * Simply return an error; since the "unknown * word" flag is set, we'll reparse the * sentence, this time rejecting unknown words * from the outset. * * Return error 2, since that's the generic "I * don't know the word..." error code. */ rundisc(ctx->voccxrun); err = VOCERR(2); goto done; } /* * If we made it this far, it means that they've * resolved the object for us, so we can consider * the previously unknown words to be known now. */ ctx->voccxunknown -= unknown_count; } else { /* trace the event for debugging */ if (ctx->voccxflg & VOCCXFDBG) tioputs(ctx->voccxtio, "... unknown word: no parseUnknownXobj - " "restarting parsing\\n"); /* * The verb doesn't define this method, so we should * use the traditional method; simply return * failure, and we'll reparse the sentence to reject * the unknown word in the usual fashion. Return * error 2, since that's the generic "I don't know * the word..." error code. */ err = VOCERR(2); goto done; } } /* * Use a new method to cut down on the time it will take to * iterate through the verprop's on all of those words. * We'll call the verb's validXoList method - it should * return a list containing all of the valid objects for the * verb (it's sort of a Fourier transform of validDo). * We'll intersect that list with the list we're about to * disambiguate, which should provide a list of objects that * are already qualified, in that validDo should return true * for every one of them. * * The calling sequence is: * verb.validXoList(actor, prep, otherobj) * * For reverse compatibility, if the return value is nil, * we use the old algorithm and consider all objects * that match the vocabulary. The return value must be * a list to be considered. * * If disambiguating the actor, skip this phase, since * we don't have a verb yet. */ if (accprop != PRP_VALIDACTOR && cnt != 0) { if (defprop == PRP_DODEFAULT) listprop = PRP_VALDOLIST; else listprop = PRP_VALIOLIST; /* push the arguments: the actor, prep, and other object */ runpobj(ctx->voccxrun, otherobj); runpobj(ctx->voccxrun, cmdPrep); runpobj(ctx->voccxrun, cmdActor); runppr(ctx->voccxrun, cmdVerb, listprop, 3); if (runtostyp(ctx->voccxrun) == DAT_LIST) { uchar *l; uint len; int kept_numobj; /* presume we won't keep numObj */ kept_numobj = FALSE; /* read the list length prefix, and skip it */ l = runpoplst(ctx->voccxrun); len = osrp2(l) - 2; l += 2; /* * For each element of the return value, see if * it's in list1. If so, copy the object into * list2, unless it's already in list2. */ for (cnt2 = 0 ; len != 0 ; ) { if (*l == DAT_OBJECT) { objnum o = osrp2(l+1); for (i = 0 ; i < cnt ; ++i) { if (list1[i] == o) { int j; /* check to see if o is already in list2 */ for (j = 0 ; j < cnt2 ; ++j) if (list2[j] == o) break; /* if o is not in list2 yet, add it */ if (j == cnt2) { /* add it */ list2[cnt2] = o; flags2[cnt2] = flags1[i]; ++cnt2; /* * if it's numObj, note that * we've already included it in * the output list, so that we * don't add it again later */ if (o == ctx->voccxnum) kept_numobj = TRUE; } break; } } } /* move on to next element */ lstadv(&l, &len); } /* * If the original list included numObj, keep it in * the accessible list for now - we consider numObj * to be always accessible. The noun phrase matcher * will include numObj whenever the player enters a * single number as a noun phrase, even when the * number matches an object. Note that we can skip * this special step if we already kept numObj in * the valid list. */ if (!kept_numobj) { /* search the original list for numObj */ for (i = 0 ; i < cnt ; ++i) { /* if this original entry is numObj, keep it */ if (list1[i] == ctx->voccxnum) { /* keep it in the accessible list */ list2[cnt2++] = ctx->voccxnum; /* no need to look any further */ break; } } } /* copy list2 into list1 */ memcpy(list1, list2, (size_t)(cnt2 * sizeof(list1[0]))); memcpy(flags1, flags2, (size_t)cnt2 * sizeof(flags1[0])); cnt = cnt2; list1[cnt] = MCMONINV; } else rundisc(ctx->voccxrun); } /* * Determine accessibility and visibility. First, limit * list1 to those objects that are visible OR accessible, * and limit list3 to those objects that are visible. */ for (cnt = cnt3 = i = 0 ; list1[i] != MCMONINV ; ++i) { int is_vis; int is_acc; /* determine if the object is visible */ is_vis = vocchkvis(ctx, list1[i], cmdActor); /* determine if it's accessible */ is_acc = vocchkaccess(ctx, list1[i], accprop, i, cmdActor, cmdVerb); /* keep items that are visible OR accessible in list1 */ if (is_acc || is_vis) { list1[cnt] = list1[i]; flags1[cnt] = flags1[i]; ++cnt; } /* * put items that are visible (regardless of whether or * not they're accessible) in list3 */ if (is_vis) { list3[cnt3] = list1[i]; flags3[cnt3] = flags1[i]; ++cnt3; } } /* * If some of our accessible objects matched with an * adjective at the end of the noun phrase, and others * didn't (i.e., the others matched with a noun or plural at * the end of the noun phrase), eliminate the ones that * matched with an adjective at the end. Ending a noun * phrase with an adjective is really a kind of short-hand; * if we have matches for both the full name version (with a * noun at the end) and a short-hand version, we want to * discard the short-hand version so that we don't treat it * as ambiguous with the long-name version. Likewise, if we * have some exact matches and some truncations, keep only * the exact matches. */ cnt = voc_prune_matches(ctx, list1, flags1, cnt); cnt3 = voc_prune_matches(ctx, list3, flags3, cnt3); /* * Now, reduce list1 to objects that are accessible. The * reason for this multi-step process is to ensure that we * prune the list with respect to every object in scope * (visible or accessible for the verb), so that we get the * most sensible pruning behavior. This is more sensible * than pruning by accessibility only, because sometimes we * may have objects that are visible but are not accessible; * as far as the player is concerned, the visible objects * are part of the current location, so the player should be * able to refer to them regardless of whether they're * accessible. */ for (dst = 0, i = 0 ; i < cnt ; ++i) { /* check this object for accessibility */ if (vocchkaccess(ctx, list1[i], accprop, i, cmdActor, cmdVerb)) { /* keep it in the final list */ list1[dst] = list1[i]; flags1[dst] = flags1[i]; /* count the new list entry */ ++dst; } } /* terminate list1 */ cnt = dst; list1[dst] = MCMONINV; /* * Go through the list of accessible objects, and perform * the sensible-object (verXoVerb) check on each. Copy each * sensible object to list2. */ for (i = 0, cnt2 = 0 ; i < cnt ; ++i) { /* run it by the appropriate sensible-object check */ if (accprop == PRP_VALIDACTOR) { /* run it through preferredActor */ runppr(ctx->voccxrun, list1[i], PRP_PREFACTOR, 0); if (runpoplog(ctx->voccxrun)) { list2[cnt2] = list1[i]; flags2[cnt2] = flags1[i]; ++cnt2; } } else { /* run it through verXoVerb */ tiohide(ctx->voccxtio); if (otherobj != MCMONINV) runpobj(ctx->voccxrun, otherobj); runpobj(ctx->voccxrun, cmdActor); runppr(ctx->voccxrun, list1[i], verprop, (otherobj != MCMONINV ? 2 : 1)); /* * If that didn't result in a message, this object * passed the tougher test of ver?oX, so include it * in list2. */ if (!tioshow(ctx->voccxtio)) { list2[cnt2] = list1[i]; flags2[cnt2] = flags1[i]; ++cnt2; } } } /* * Construct a string consisting of the words the user typed * to reference this object, in case we need to complain. */ usrobj[0] = '\0'; if (inlist[inpos].vocolfst != 0 && inlist[inpos].vocollst != 0) { for (p = inlist[inpos].vocolfst ; p <= inlist[inpos].vocollst ; p += strlen(p) + 1) { /* add a space if we have a prior word */ if (usrobj[0] != '\0') { /* quote the space if the last word ended with '.' */ if (p[strlen(p)-1] == '.') strcat(usrobj, "\\"); /* add the space */ strcat(usrobj, " "); } /* add the current word, or "of" if it's "of" */ if (voc_check_special(ctx, p, VOCW_OF)) vocaddof(ctx, usrobj); else strcat(usrobj, p); } } /* * If there's nothing in the YES list, and we have just a * single number as our word, act as though they are talking * about the number itself, rather than one of the objects * that happened to use the number -- none of those objects * make any sense, it seems, so fall back on the number. * * Note that we may also have only numObj in the YES list, * because the noun phrase parser normally adds numObj when * the player types a noun phrase consisting only of a * number. Do the same thing in this case -- just return * the number object. */ if ((cnt2 == 0 || (cnt2 == 1 && list2[0] == ctx->voccxnum)) && inlist[inpos].vocolfst != 0 && inlist[inpos].vocolfst == inlist[inpos].vocollst && vocisdigit(*inlist[inpos].vocolfst)) { long v1; char vbuf[4]; v1 = atol(inlist[inpos].vocolfst); oswp4s(vbuf, v1); vocsetobj(ctx, ctx->voccxnum, DAT_NUMBER, vbuf, &inlist[inpos], &outlist[outpos]); outlist[outpos].vocolflg = VOCS_NUM; ++outpos; /* skip all objects that matched the number */ for ( ; inlist[inpos+1].vocolobj != MCMONINV && inlist[inpos+1].vocolfst == inlist[inpos].vocolfst ; ++inpos) ; continue; } /* * Check if we found anything in either the YES (list2) or * MAYBE (list1) lists. If there's nothing in either list, * complain and return. */ if (cnt2 == 0 && cnt == 0) { /* * We have nothing sensible, and nothing even * accessible. If there's anything merely visible, * complain about those items. */ if (cnt3 != 0) { /* there are visible items - complain about them */ cnt = cnt3; cantreach_list = list3; noreach = TRUE; /* give the cantReach message, even for multiple objects */ goto noreach1; } else { /* * explain that there's nothing visible or * accessible matching the noun phrase, and abort * the command with an error */ if (!silent) vocerr(ctx, VOCERR(9), "I don't see any %s here.", usrobj); err = VOCERR(9); goto done; } } /* * If anything passed the stronger test (objects passing are * in list2), use this as our proposed resolution for the * noun phrase. If nothing passed the stronger test (i.e., * list2 is empty), simply keep the list of accessible * objects in list1. */ if (cnt2 != 0) { /* * we have items passing the stronger test -- copy the * stronger list (list2) to list1 */ cnt = cnt2; memcpy(list1, list2, (size_t)(cnt2 * sizeof(list1[0]))); memcpy(flags1, flags2, (size_t)(cnt2 * sizeof(flags1[0]))); } /* * Check for redundant objects in the list. If the same * object appears multiple times in the list, remove the * extra occurrences. Sometimes, a game can inadvertantly * define the same vocabulary word several times for the * same object, because of the parser's leniency with * matching leading substrings of 6 characters or longer. * To avoid unnecessary "which x do you mean..." errors, * simply discard any duplicates in the list. */ for (dst = 0, i = 0 ; i < cnt ; ++i) { int dup; int j; /* presume we won't find a duplicate of this object */ dup = FALSE; /* * look for duplicates of this object in the remainder * of the list */ for (j = i + 1 ; j < cnt ; ++j) { /* check for a duplicate */ if (list1[i] == list1[j]) { /* note that this object has a duplicate */ dup = TRUE; /* we don't need to look any further */ break; } } /* * if this object has no duplicate, retain it in the * output list */ if (!dup) { /* copy the element to the output */ list1[dst] = list1[i]; flags1[dst] = flags1[i]; /* count the output */ ++dst; } } /* update the count to the new list's size */ cnt = dst; list1[cnt] = MCMONINV; /* * If we have more than one object in the list, and numObj * is still in the list, remove numObj - we don't want to * consider numObj to be considered ambiguous with another * object when the other object passes access and validation * tests. */ if (cnt > 1) { /* scan the list for numObj */ for (i = 0, dst = 0 ; i < cnt ; ++i) { /* if this isn't numObj, keep this element */ if (list1[i] != ctx->voccxnum) list1[dst++] = list1[i]; } /* update the final count */ cnt = dst; list1[cnt] = MCMONINV; } /* * Check for a generic numeric adjective ('#' in the * adjective list for the object) in each object. If we * find it, we need to make sure there's a number in the * name of the object. */ for (i = 0 ; i < cnt ; ++i) { if (has_gen_num_adj(ctx, list1[i])) { /* * If they specified a count, create the specified * number of objects. Otherwise, if the object is * plural, they mean to use all of the objects, so a * numeric adjective isn't required -- set the * numeric adjective property in the object to nil * to so indicate. Otherwise, look for the number, * and set the numeric adjective property * accordingly. */ if ((flags1[i] & (VOCS_ANY | VOCS_COUNT)) != 0) { int n = (user_count ? user_count : 1); int j; objnum objn = list1[i]; /* * They specified a count, so we want to create * n-1 copies of the numbered object. Make room * for the n-1 new copies of this object by * shifting any elements that follow up n-1 * slots. */ if (i + 1 != cnt && n > 1) { memmove(&list1[i + n - 1], &list1[i], (cnt - i) * sizeof(list1[i])); memmove(&flags1[i + n - 1], &flags1[i], (cnt - i) * sizeof(flags1[i])); } /* create n copies of this object */ for (j = 0 ; j < n ; ++j) { long l; /* * Generate a number for the new object, * asking the object to tell us what value * to use for an "any". */ runpnum(ctx->voccxrun, (long)(j + 1)); runppr(ctx->voccxrun, objn, PRP_ANYVALUE, 1); l = runpopnum(ctx->voccxrun); /* try creating the new object */ list1[i+j] = voc_new_num_obj(ctx, objn, cmdActor, cmdVerb, l, FALSE); if (list1[i+j] == MCMONINV) { err = VOCERR(40); goto done; } } } else if ((flags1[i] & VOCS_PLURAL) != 0) { /* * get the plural object by asking for the * numbered object with a nil number parameter */ list1[i] = voc_new_num_obj(ctx, list1[i], cmdActor, cmdVerb, (long)0, TRUE); if (list1[i] == MCMONINV) { err = VOCERR(40); goto done; } } else { char *p; int found; /* * No plural, no "any" - we just want to create * one numbered object, using the number that * the player must have specified. Make sure * the player did, in fact, specify a number. */ for (found = FALSE, p = inlist[inpos].vocolfst ; p != 0 && p <= inlist[inpos].vocollst ; p += strlen(p) + 1) { /* did we find it? */ if (vocisdigit(*p)) { long l; /* get the number */ l = atol(p); /* create the object with this number */ list1[i] = voc_new_num_obj(ctx, list1[i], cmdActor, cmdVerb, l, FALSE); if (list1[i] == MCMONINV) { err = VOCERR(40); goto done; } /* the command looks to be valid */ found = TRUE; break; } } /* if we didn't find it, stop now */ if (!found) { if (!silent) vocerr(ctx, VOCERR(160), "You'll have to be more specific about which %s you mean.", usrobj); err = VOCERR(160); goto done; } } } } /* * We still have an ambiguous word - ask the user which of * the possible objects they meant to use */ trying_again = FALSE; for (;;) { int wrdcnt; int next; uchar *p; int cleared_noun; int diff_cnt; int stat; int num_wanted; int is_ambig; int all_plural; /* * check for usage - determine if we have singular * definite, singular indefinite, counted, or plural * usage */ if ((flags1[0] & (VOCS_PLURAL | VOCS_ANY | VOCS_COUNT)) != 0) { int i; /* * loop through the objects to AND together the * flags from all of the objects; we only care about * the plural flags (PLURAL, ANY, and COUNT), so * start out with only those, then AND off any that * aren't in all of the objects */ for (all_plural = VOCS_PLURAL | VOCS_ANY | VOCS_COUNT, i = 0 ; i < cnt ; ++i) { /* AND out this object's flags */ all_plural &= flags1[i]; /* * if we've ANDed down to zero, there's no need * to look any further */ if (!all_plural) break; } } else { /* * it looks like we want just a single object - * clear the various plural flags */ all_plural = 0; } /* * Count the distinguishable items. * * If we're looking for a single object, don't keep * duplicate indistinguishable items (i.e., keep only * one item from each set of mutually indistinguishable * items), since we could equally well use any single * one of those items. If we're looking for multiple * objects, keep all of the items, since the user is * referring to all of them. */ diff_cnt = voc_count_diff(ctx, list1, flags1, &cnt, all_plural != 0 || use_all_objs); /* * Determine how many objects we'd like to find. If we * have a count specified, we'd like to find the given * number of objects. If we have "ANY" specified, we * just want to pick one object arbitrarily. If we have * all plurals, we can keep all of the objects. If the * 'use_all_objs' flag is true, it means that we can use * everything in the list. */ if (use_all_objs) { /* we want to use all of the objects */ num_wanted = cnt; is_ambig = FALSE; } else if ((all_plural & VOCS_COUNT) != 0) { /* * we have a count - we want exactly the given * number of objects, but we can pick an arbitrary * subset, so it's not ambiguous even if we have too * many at the moment */ num_wanted = user_count; is_ambig = FALSE; } else if ((all_plural & VOCS_ANY) != 0) { /* * they specified "any", so we want exactly one, but * we can pick one arbitrarily, so there's no * ambiguity */ num_wanted = 1; is_ambig = FALSE; } else if (all_plural != 0) { /* * we have a simple plural, so we can use all of the * provided objects without ambiguity */ num_wanted = cnt; is_ambig = FALSE; } else { /* * it's a singular, definite usage, so we want * exactly one item; if we have more than one in our * list, it's ambiguous */ num_wanted = 1; is_ambig = (cnt != 1); } /* call the disambiguation hook */ stat = voc_disambig_hook(ctx, cmdVerb, cmdActor, cmdPrep, otherobj, accprop, verprop, list1, flags1, &cnt, inlist[inpos].vocolfst, inlist[inpos].vocollst, num_wanted, is_ambig, disnewbuf, silent); /* check the status */ if (stat == VOC_DISAMBIG_DONE) { /* that's it - copy the result */ for (i = 0 ; i < cnt ; ++i) vocout(&outlist[outpos++], list1[i], flags1[i], inlist[inpos].vocolfst, inlist[inpos].vocollst); /* we're done */ break; } else if (stat == VOC_DISAMBIG_CONT) { /* * Continue with the new list (which is the same as * the old list, if it wasn't actually updated by * the hook routine) - proceed with remaining * processing, but using the new list. * * Because the list has been updated, we must once * again count the number of distinguishable items, * since that may have changed. */ diff_cnt = voc_count_diff(ctx, list1, flags1, &cnt, TRUE); } else if (stat == VOC_DISAMBIG_PARSE_RESP || stat == VOC_DISAMBIG_PROMPTED) { /* * The status indicates one of the following: * * - the hook prompted for more information and read * a response from the player, but decided not to * parse it; we will continue with the current list, * and parse the player's response as provided by * the hook. * * - the hook prompted for more information, but * left the reading to us. We'll proceed with the * current list and read a response as normal, but * without displaying another prompt. * * In any case, just continue processing; we'll take * appropriate action on the prompting and reading * when we reach those steps. */ } else { /* anything else is an error */ err = VOCERR(41); goto done; } /* * If we found only one word, or a plural/ANY, we are * finished. If we found a count, use that count if * possible. */ if (cnt == 1 || all_plural || use_all_objs) { int flags; /* keep only one of the objects if ANY was used */ if ((all_plural & VOCS_COUNT) != 0) { if (user_count > cnt) { if (!silent) vocerr(ctx, VOCERR(30), "I only see %d of those.", cnt); err = VOCERR(30); goto done; } cnt = user_count; flags = VOCS_ALL; } else if ((all_plural & VOCS_ANY) != 0) { cnt = 1; flags = VOCS_ALL; } else flags = 0; /* put the list */ for (i = 0 ; i < cnt ; ++i) vocout(&outlist[outpos++], list1[i], flags, inlist[inpos].vocolfst, inlist[inpos].vocollst); /* we're done */ break; } /* make sure output capturing is off */ tiocapture(ctx->voccxtio, (mcmcxdef *)0, FALSE); tioclrcapture(ctx->voccxtio); /* * if we're in "silent" mode, we can't ask the player * for help, so return an error */ if (silent) { /* * We can't disambiguate the list. Fill in the * return list with what's left, which is still * ambiguous, and note that we need to return an * error code indicating that the list remains * ambiguous. */ for (i = 0 ; i < cnt && outpos < VOCMAXAMBIG ; ++i) vocout(&outlist[outpos++], list1[i], 0, inlist[inpos].vocolfst, inlist[inpos].vocollst); /* note that we have ambiguity remaining */ still_ambig = TRUE; /* we're done with this sublist */ break; } /* * We need to prompt for more information interactively. * Figure out how we're going to display the prompt. * * - If the disambigXobj hook status (stat) indicates * that the hook already displayed a prompt of its own, * we don't need to add anything here. * * - Otherwise, if there's a parseDisambig function * defined in the game, call it to display the prompt. * * - Otherwise, display our default prompt. */ if (stat == VOC_DISAMBIG_PARSE_RESP || stat == VOC_DISAMBIG_PROMPTED) { /* * the disambigXobj hook already asked for a * response, so don't display any prompt of our own */ } else if (ctx->voccxpdis != MCMONINV) { uint l; /* * There's a parseDisambig function defined in the * game - call it to display the prompt, passing the * list of possible objects and the player's * original noun phrase text as parameters. */ for (i = 0, p = lstbuf+2 ; i < cnt ; ++i, p += 2) { *p++ = DAT_OBJECT; oswp2(p, list1[i]); } l = p - lstbuf; oswp2(lstbuf, l); runpbuf(ctx->voccxrun, DAT_LIST, lstbuf); runpstr(ctx->voccxrun, usrobj, (int)strlen(usrobj), 1); runfn(ctx->voccxrun, ctx->voccxpdis, 2); } else { /* display "again" message, if necessary */ if (trying_again) vocerr_info(ctx, VOCERR(100), "Let's try it again: "); /* ask the user about it */ vocerr_info(ctx, VOCERR(101), "Which %s do you mean, ", usrobj); for (i = 0 ; i < cnt ; ) { int eqcnt; int j; objnum sc; /* * See if we have multiple instances of an * identical object. All such instances should * be grouped together (this was done above), so * we can just count the number of consecutive * equivalent objects. */ eqcnt = 1; runppr(ctx->voccxrun, list1[i], PRP_ISEQUIV, 0); if (runpoplog(ctx->voccxrun)) { /* get the superclass, if possible */ sc = objget1sc(ctx->voccxmem, list1[i]); if (sc != MCMONINV) { /* count equivalent objects that follow */ for (j = i + 1 ; j < cnt ; ++j) { if (objget1sc(ctx->voccxmem, list1[j]) == sc) ++eqcnt; else break; } } } /* * Display this object's name. If we have only * one such object, display its thedesc, * otherwise display its adesc. */ runppr(ctx->voccxrun, list1[i], (prpnum)(eqcnt == 1 ? PRP_THEDESC : PRP_ADESC), 0); /* display the separator as appropriate */ if (i + 1 < diff_cnt) vocerr_info(ctx, VOCERR(102), ", "); if (i + 2 == diff_cnt) vocerr_info(ctx, VOCERR(103), "or "); /* skip all equivalent items */ i += eqcnt; } vocerr_info(ctx, VOCERR(104), "?"); } /* * Read the response. If the disambigXobj hook already * read the response, we don't need to read anything * more. */ if (stat != VOC_DISAMBIG_PARSE_RESP && vocread(ctx, cmdActor, cmdVerb, disnewbuf, (int)VOCBUFSIZ, 2) == VOCREAD_REDO) { /* they want to treat the input as a new command */ strcpy(cmdbuf, disnewbuf); ctx->voccxunknown = 0; ctx->voccxredo = TRUE; err = VOCERR(43); goto done; } /* * parse the response */ /* tokenize the list */ wrdcnt = voctok(ctx, disnewbuf, disbuffer, diswordlist, TRUE, TRUE, TRUE); if (wrdcnt == 0) { /* empty response - run pardon() function and abort */ runfn(ctx->voccxrun, ctx->voccxprd, 0); err = VOCERR(42); goto done; } if (wrdcnt < 0) { /* return the generic punctuation error */ err = VOCERR(1); goto done; } /* * Before we tokenize the sentence, remember the current * unknown word count, then momentarily set the count to * zero. This will cause the tokenizer to absorb any * unknown words; if there are any unknown words, the * tokenizer will parse them and set the unknown count. * If we find any unknown words in the input, we'll * simply treat the input as an entirely new command. */ old_unknown = ctx->voccxunknown; old_lastunk = ctx->voccxlastunk; ctx->voccxunknown = 0; /* clear our internal type list */ memset(distypelist, 0, VOCBUFSIZ * sizeof(distypelist[0])); /* tokenize the sentence */ diswordlist[wrdcnt] = 0; if (vocgtyp(ctx, diswordlist, distypelist, cmdbuf) || ctx->voccxunknown != 0) { /* * there's an unknown word or other problem - retry * the input as an entirely new command */ strcpy(cmdbuf, disnewbuf); ctx->voccxunknown = 0; ctx->voccxredo = TRUE; err = VOCERR(2); goto done; } /* restore the original unknown word count */ ctx->voccxunknown = old_unknown; ctx->voccxlastunk = old_lastunk; /* * Find the last word that can be an adj and/or a noun. * If it can be either (i.e., both bits are set), clear * the noun bit and make it just an adjective. This is * because we're asking for an adjective for clarification, * and we most likely want it to be an adjective in this * context; if the noun bit is set, too, the object lister * will think it must be a noun, being the last word. */ for (i = 0 ; i < wrdcnt ; ++i) { if (!(distypelist[i] & (VOCT_ADJ | VOCT_NOUN | VOCT_ARTICLE))) break; } if (i && (distypelist[i-1] & VOCT_ADJ) && (distypelist[i-1] & VOCT_NOUN)) { /* * Note that we're clearing the noun flag. If * we're unsuccessful in finding the object with the * noun flag cleared, we'll put the noun flag back * in and give it another try (by adding VOCT_NOUN * back into distypelist[cleared_noun], and coming * back to the label below). */ cleared_noun = i-1; distypelist[i-1] &= ~VOCT_NOUN; } else cleared_noun = -1; try_current_flags: /* start with the first word */ if (vocspec(diswordlist[0], VOCW_ALL) || vocspec(diswordlist[0], VOCW_BOTH)) { char *nam; static char all_name[] = "all"; static char both_name[] = "both"; if (vocspec(diswordlist[0], VOCW_ALL)) nam = all_name; else nam = both_name; for (i = 0 ; i < cnt ; ++i) vocout(&outlist[outpos++], list1[i], 0, nam, nam); if (noreach) { cantreach_list = list1; goto noreach1; } break; } else if (vocspec(diswordlist[0], VOCW_ANY)) { static char *anynm = "any"; /* choose the first object arbitrarily */ vocout(&outlist[outpos++], list1[i], VOCS_ALL, anynm, anynm); break; } else { /* check for a word matching the phrase */ cnt2 = vocchknoun(ctx, diswordlist, distypelist, 0, &next, disnounlist, FALSE); if (cnt2 > 0) { /* * if that didn't consume the entire phrase, or * at least up to "one" or "ones" or a period, * disallow it, since they must be entering * something more complicated */ if (diswordlist[next] != 0 && !vocspec(diswordlist[next], VOCW_ONE) && !vocspec(diswordlist[next], VOCW_ONES) && !vocspec(diswordlist[next], VOCW_THEN)) { cnt2 = 0; } } else if (cnt2 < 0) { /* * There was a syntax error in the phrase. * vocchknoun() will have displayed a message in * this case, so we're done parsing this command. */ err = VOCERR(45); goto done; } /* proceed only if we got a valid phrase */ if (cnt2 > 0) { int cnt3; int newcnt; /* build the list of matches for the new phrase */ for (i = 0, newcnt = 0 ; i < cnt2 ; ++i) { int j; int found; /* * make sure this object isn't already in * our list - we want each object only once */ for (j = 0, found = FALSE ; j < newcnt ; ++j) { /* if this is in the list, note it */ if (list2[j] == disnounlist[i].vocolobj) { found = TRUE; break; } } /* * add it to our list only if it wasn't * already there */ if (!found) { /* copy the object ID */ list2[newcnt] = disnounlist[i].vocolobj; /* copy the flags that we care about */ flags2[newcnt] = disnounlist[i].vocolflg & (VOCS_PLURAL | VOCS_ANY | VOCS_COUNT); /* count the entry */ ++newcnt; } } /* terminate the list */ list2[newcnt] = MCMONINV; /* intersect the new list with the old list */ newcnt = vocisect(list2, list1); /* count the noun phrases in the new list */ for (i = cnt3 = 0 ; i < cnt2 ; ++i) { /* we have one more noun phrase */ ++cnt3; /* if we have a noun phrase, skip matching objs */ if (disnounlist[i].vocolfst != 0) { int j; /* skip objects matching this noun phrase */ for (j = i + 1 ; disnounlist[i].vocolfst == disnounlist[j].vocolfst ; ++j) ; i = j - 1; } } /* * If the count of items in the intersection of * the original list and the typed-in list is no * bigger than the number of items specified in * the typed-in list, we've successfully * disambiguated the object, because the user's * new list matches only one object for each set * of words the user typed. */ if (newcnt && (newcnt <= cnt3 || (diswordlist[next] && vocspec(diswordlist[next], VOCW_ONES)))) { static char one_name[] = "ones"; for (i = 0 ; i < cnt ; ++i) vocout(&outlist[outpos++], list2[i], 0, one_name, one_name); if (noreach) { cnt = newcnt; cantreach_list = list2; noreach1: if (accprop == PRP_VALIDACTOR) { /* for actors, show a special message */ vocerr(ctx, VOCERR(31), "You can't talk to that."); } else { /* use the normal no-reach message */ vocnoreach(ctx, cantreach_list, cnt, cmdActor, cmdVerb, cmdPrep, defprop, cnt > 1, 0, 0, cnt); } err = VOCERR(31); goto done; } break; } else if (newcnt == 0) { /* * If we cleared the noun, maybe we actually * need to treat the word as a noun, so add * the noun flag back in and give it another * go. If we didn't clear the noun, there's * nothing left to try, so explain that we * don't see any such object and give up. */ if (cleared_noun != -1) { distypelist[cleared_noun] |= VOCT_NOUN; cleared_noun = -1; goto try_current_flags; } /* find the first object with a noun phrase */ for (i = 0 ; i < cnt2 ; ++i) { /* if we have a noun phrase, stop scanning */ if (disnounlist[i].vocolfst != 0) break; } /* * if we found a noun phrase, build a string * out of the words used; otherwise, just * use "such" */ if (i != cnt2) { char *p; char *last; /* clear the word buffer */ newobj[0] = '\0'; /* build a string out of the words */ p = disnounlist[i].vocolfst; last = disnounlist[i].vocollst; for ( ; p <= last ; p += strlen(p) + 1) { /* * If this is a special word, we * probably can't construct a * sensible sentence - special words * are special parts of speech that * will look weird if inserted into * our constructed noun phrase. In * these cases, turn the entire * thing into "I don't see any * *such* object" rather than trying * to make do with pronouns or other * special words. */ if (vocisspec(p)) { /* * replace the entire adjective * phrase with "such" */ strcpy(newobj, "such"); /* * stop here - don't add any * more, since "such" is the * whole thing */ break; } /* add a space if we have a prior word */ if (newobj[0] != '\0') strcat(newobj, " "); /* add this word */ strcat(newobj, p); } } else { /* no noun phrase found */ strcpy(newobj, "such"); } /* didn't find anything - complain and give up */ vocerr(ctx, VOCERR(16), "You don't see any %s %s here.", newobj, usrobj); err = VOCERR(16); goto done; } /* * If we get here, it means that we have still * more than one object per noun phrase typed in * the latest sentence. Limit the list to the * intersection (by copying list2 to list1), and * try again. */ memcpy(list1, list2, (size_t)((newcnt + 1) * sizeof(list1[0]))); cnt = newcnt; trying_again = TRUE; } else { /* * We didn't find a noun phrase, so it's probably a * new command. However, check first to see if we * were making a trial run with the noun flag * cleared: if so, go back and make another pass * with the noun flag added back in to see if that * works any better. */ if (cleared_noun != -1) { distypelist[cleared_noun] |= VOCT_NOUN; cleared_noun = -1; goto try_current_flags; } /* retry as an entire new command */ strcpy(cmdbuf, disnewbuf); ctx->voccxunknown = 0; ctx->voccxredo = TRUE; err = VOCERR(43); goto done; } } } inpos = lpos - 1; } } /* terminate the output list */ vocout(&outlist[outpos], MCMONINV, 0, (char *)0, (char *)0); /* * If we still have ambiguous objects, so indicate. This can only * happen when we operate in "silent" mode, because only then can we * give up without fully resolving a list. */ if (still_ambig) err = VOCERR(44); /* no error */ err = 0; done: ERRCLEAN(ctx->voccxerr) { /* * reset the stack before we return, in case the caller handles * the error without aborting the command */ voc_leave(ctx, save_sp); } ERRENDCLN(ctx->voccxerr); /* return success */ VOC_RETVAL(ctx, save_sp, err); } /* vocready - see if at end of command, execute & return TRUE if so */ static int vocready(voccxdef *ctx, char *cmd[], int *typelist, int cur, objnum cmdActor, objnum cmdPrep, char *vverb, char *vprep, vocoldef *dolist, vocoldef *iolist, int *errp, char *cmdbuf, int first_word, uchar **preparse_list, int *next_start) { if (cur != -1 && (cmd[cur] == (char *)0 || vocspec(cmd[cur], VOCW_AND) || vocspec(cmd[cur], VOCW_THEN))) { if (ctx->voccxflg & VOCCXFDBG) { char buf[128]; sprintf(buf, ". executing verb: %s %s\\n", vverb, vprep ? vprep : ""); tioputs(ctx->vocxtio, buf); } *errp = execmd(ctx, cmdActor, cmdPrep, vverb, vprep, dolist, iolist, &cmd[first_word], &typelist[first_word],cmdbuf, cur - first_word, preparse_list, next_start); return(TRUE); } return(FALSE); } /* execute a single command */ static int voc1cmd(voccxdef *ctx, char *cmd[], char *cmdbuf, objnum *cmdActorp, int first) { int cur; int next; objnum o; vocwdef *v; char *vverb; int vvlen; char *vprep; int cnt; int err; vocoldef *dolist; vocoldef *iolist; int *typelist; objnum cmdActor = *cmdActorp; objnum cmdPrep; int swapObj; /* TRUE -> swap dobj and iobj */ int again; int first_word; uchar *preparse_list; int next_start; struct { int active; int cur; char **cmd; char *cmdbuf; } preparseCmd_stat; char **newcmd; char *origcmdbuf; char *newcmdbuf; uchar *save_sp; int no_match; int retval; voc_enter(ctx, &save_sp); VOC_MAX_ARRAY(ctx, vocoldef, dolist); VOC_MAX_ARRAY(ctx, vocoldef, iolist); VOC_STK_ARRAY(ctx, int, typelist, VOCBUFSIZ); VOC_STK_ARRAY(ctx, char *, newcmd, VOCBUFSIZ); VOC_STK_ARRAY(ctx, char, newcmdbuf, VOCBUFSIZ); /* save the original command buf in case we need to redo the command */ origcmdbuf = cmdbuf; /* clear out the type list */ memset(typelist, 0, VOCBUFSIZ*sizeof(typelist[0])); /* get the types of the words in the command */ if (vocgtyp(ctx, cmd, typelist, cmdbuf)) { retval = 1; goto done; } /* start off at the first word */ cur = next = first_word = 0; /* * Presume we will be in control of the next word - when execmd() or * another routine we call decides where the command ends, it will * fill in a new value here. When this value is non-zero, it will * tell us where the next sentence start is relative to the previous * sentence start. */ next_start = 0; /* we don't have a preparseCmd result yet */ preparseCmd_stat.active = FALSE; /* keep going until we run out of work to do */ for (again = FALSE, err = 0 ; ; again = TRUE) { /* * if preparseCmd sent us back a list, parse that list as a new * command */ if (err == ERR_PREPRSCMDREDO) { uchar *src; size_t len; size_t curlen; char *dst; int cnt; /* don't allow a preparseCmd to loop */ if (preparseCmd_stat.active) { vocerr(ctx, VOCERR(34), "Internal game error: preparseCmd loop"); retval = 1; goto done; } /* save our status prior to processing the preparseCmd list */ preparseCmd_stat.active = TRUE; preparseCmd_stat.cur = cur; preparseCmd_stat.cmd = cmd; preparseCmd_stat.cmdbuf = cmdbuf; /* set up with the new command */ cmd = newcmd; cmdbuf = newcmdbuf; cur = 0; /* break up the list into the new command buffer */ src = preparse_list; len = osrp2(src) - 2; for (src += 2, dst = cmdbuf, cnt = 0 ; len ; ) { /* make sure the next element is a string */ if (*src != DAT_SSTRING) { vocerr(ctx, VOCERR(32), "Internal game error: preparseCmd returned an invalid list"); retval = 1; goto done; } /* get the string */ ++src; curlen = osrp2(src) - 2; src += 2; /* make sure it will fit in the buffer */ if (dst + curlen + 1 >= cmdbuf + VOCBUFSIZ) { vocerr(ctx, VOCERR(33), "Internal game error: preparseCmd command too long"); retval = 1; goto done; } /* store the word */ cmd[cnt++] = dst; memcpy(dst, src, curlen); dst[curlen] = '\0'; /* move on to the next word */ len -= 3 + curlen; src += curlen; dst += curlen + 1; } /* enter a null last word */ cmd[cnt] = 0; /* generate the type list for the new list */ if (vocgtyp(ctx, cmd, typelist, cmdbuf)) { /* return an error */ retval = 1; goto done; } /* * this is not a new command - it's just further processing * of the current command */ again = FALSE; /* clear the error */ err = 0; } /* initialize locals */ cmdPrep = MCMONINV; /* assume no preposition */ swapObj = FALSE; /* assume no object swapping */ dolist[0].vocolobj = iolist[0].vocolobj = MCMONINV; dolist[0].vocolflg = iolist[0].vocolflg = 0; /* check error return from vocready (which returns from execmd) */ if (err) { /* return the error */ retval = err; goto done; } skip_leading_stuff: /* * If someone updated the sentence start point, jump there. The * sentence start is relative to the previous sentence start. */ if (next_start != 0) cur = first_word + next_start; /* clear next_start, so we can tell if someone updates it */ next_start = 0; /* skip any leading THEN's and AND's */ while (cmd[cur] && (vocspec(cmd[cur], VOCW_THEN) || vocspec(cmd[cur], VOCW_AND))) ++cur; /* see if there's anything left to parse */ if (cmd[cur] == 0) { /* * if we've been off doing preparseCmd work, return to the * original command list */ if (preparseCmd_stat.active) { /* restore the original status */ cur = preparseCmd_stat.cur; cmd = preparseCmd_stat.cmd; cmdbuf = preparseCmd_stat.cmdbuf; preparseCmd_stat.active = FALSE; /* get the type list for the original list again */ if (vocgtyp(ctx, cmd, typelist, cmdbuf)) { /* return the error */ retval = 1; goto done; } /* try again */ goto skip_leading_stuff; } else { /* nothing to pop - we must be done */ retval = 0; goto done; } } /* * display a blank line if this is not the first command on this * command line, so that we visually separate the results of the * new command from the results of the previous command */ if (again) outformat("\\b"); /* tioblank(ctx->voccxtio); */ { /* look for an explicit actor in the command */ if ((o = vocgetactor(ctx, cmd, typelist, cur, &next, cmdbuf)) != MCMONINV) { /* skip the actor noun phrase in the input */ cur = next; /* remember the actor internally */ cmdActor = *cmdActorp = o; /* set the actor in the context */ ctx->voccxactor = o; } /* if the actor parsing failed, return an error */ if (cur != next) { /* error getting actor */ retval = 1; goto done; } } /* remember where the sentence starts */ first_word = cur; /* make sure we have a verb */ if ((cmd[cur] == (char *)0) || !(typelist[cur] & VOCT_VERB)) { /* unknown verb - handle it with parseUnknownVerb if possible */ if (!try_unknown_verb(ctx, cmdActor, &cmd[cur], &typelist[cur], 0, &next_start, TRUE, VOCERR(17), "There's no verb in that sentence!")) { /* error - abort the command */ retval = 1; goto done; } else { /* go back for more */ continue; } } vverb = cmd[cur++]; /* this is the verb */ vvlen = strlen(vverb); /* remember length of verb */ vprep = 0; /* assume no verb-preposition */ /* execute if the command is just a verb */ if (vocready(ctx, cmd, typelist, cur, cmdActor, cmdPrep, vverb, vprep, dolist, iolist, &err, cmdbuf, first_word, &preparse_list, &next_start)) continue; /* * If the next word is a preposition, and it makes sense to be * aggregated with this verb, use it as such. */ if (typelist[cur] & VOCT_PREP) { if (vocffw(ctx, vverb, vvlen, cmd[cur], (int)strlen(cmd[cur]), PRP_VERB, (vocseadef *)0)) { vprep = cmd[cur++]; if (vocready(ctx, cmd, typelist, cur, cmdActor, cmdPrep, vverb, vprep, dolist, iolist, &err, cmdbuf, first_word, &preparse_list, &next_start)) continue; } else { /* * If we have a preposition which can NOT be aggregated * with the verb, take command of this form: "verb prep * iobj dobj". Note that we do *not* do this if the * word is also a noun, or it's an adjective and a noun * (possibly separated by one or more adjectives) * follows. */ if ((v = vocffw(ctx, cmd[cur], (int)strlen(cmd[cur]), (char *)0, 0, PRP_PREP, (vocseadef *)0)) != 0) { int swap_ok; /* if it can be an adjective, check further */ if (typelist[cur] & VOCT_NOUN) { /* don't allow the swap */ swap_ok = FALSE; } else if (typelist[cur] & VOCT_ADJ) { int i; /* look for a noun, possibly preceded by adj's */ for (i = cur + 1 ; cmd[i] && (typelist[i] & VOCT_ADJ) && !(typelist[i] & VOCT_NOUN) ; ++i) ; swap_ok = (!cmd[i] || !(typelist[i] & VOCT_NOUN)); } else { /* we can definitely allow this swap */ swap_ok = TRUE; } if (swap_ok) { cmdPrep = v->vocwobj; swapObj = TRUE; ++cur; } } } } retry_swap: /* get the direct object if there is one */ if ((cnt = vocchknoun2(ctx, cmd, typelist, cur, &next, dolist, FALSE, &no_match)) > 0) { /* we found a noun phrase matching one or more objects */ cur = next; } else if (no_match) { /* * we found a valid noun phrase, but we didn't find any * objects that matched the words -- get the noun again, * this time showing the error */ vocgetnoun(ctx, cmd, typelist, cur, &next, dolist); /* return the error */ retval = 1; goto done; } else if (cnt < 0) { /* invalid syntax - return failure */ retval = 1; goto done; } else { /* * If we thought we were going to get a two-object * sentence, and we got a zero-object sentence, and it looks * like the word we took as a preposition is also an * adjective or noun, go back and treat it as such. */ if (swapObj && ((typelist[cur-1] & VOCT_NOUN) || (typelist[cur-1] & VOCT_ADJ))) { --cur; swapObj = FALSE; cmdPrep = MCMONINV; goto retry_swap; } bad_sentence: /* find the last word */ while (cmd[cur]) ++cur; /* try running the sentence through preparseCmd */ err = try_preparse_cmd(ctx, &cmd[first_word], cur - first_word, &preparse_list); switch(err) { case 0: /* preparseCmd didn't do anything - the sentence fails */ if (!try_unknown_verb(ctx, cmdActor, &cmd[first_word], &typelist[first_word], 0, &next_start, TRUE, VOCERR(18), "I don't understand that sentence.")) { /* error - abort the command */ retval = 1; goto done; } else { /* success - go back for more */ continue; } case ERR_PREPRSCMDCAN: /* they cancelled - we're done with the sentence */ retval = 0; goto done; case ERR_PREPRSCMDREDO: /* reparse with the new sentence */ continue; } } /* see if we want to execute the command now */ if (vocready(ctx, cmd, typelist, cur, cmdActor, cmdPrep, vverb, vprep, swapObj ? iolist : dolist, swapObj ? dolist : iolist, &err, cmdbuf, first_word, &preparse_list, &next_start)) continue; /* * Check for an indirect object, which may or may not be preceded * by a preposition. (Note that the lack of a preposition implies * that the object we already found is the indirect object, and the * next object is the direct object. It also implies a preposition * of "to.") */ if (cmdPrep == MCMONINV && (typelist[cur] & VOCT_PREP)) { char *p1 = cmd[cur++]; /* if this is the end of the sentence, add the prep to the verb */ if (cmd[cur] == (char *)0 || vocspec(cmd[cur], VOCW_AND) || vocspec(cmd[cur], VOCW_THEN)) { if (vocffw(ctx, vverb, vvlen, p1, (int)strlen(p1), PRP_VERB, (vocseadef *)0) && !vprep) vprep = p1; else { /* call parseUnknownVerb */ if (!try_unknown_verb(ctx, cmdActor, &cmd[first_word], &typelist[first_word], 0, &next_start, TRUE, VOCERR(19), "There are words after your command I couldn't use.")) { /* error - abandon the command */ retval = 1; goto done; } else { /* go back for more */ continue; } } if ((err = execmd(ctx, cmdActor, cmdPrep, vverb, vprep, dolist, iolist, &cmd[first_word], &typelist[first_word], cmdbuf, cur - first_word, &preparse_list, &next_start)) != 0) { retval = 1; goto done; } continue; } /* * If we have no verb preposition already, and we have * another prep-capable word following this prep-capable * word, and this preposition aggregates with the verb, take * it as a sentence of the form "pry box open with crowbar" * (where the current word is "with"). We also must have at * least one more word after that, since there will have to * be an indirect object. */ if (cmd[cur] && (typelist[cur] & VOCT_PREP) && cmd[cur+1] && vprep == 0 && vocffw(ctx, vverb, vvlen, p1, (int)strlen(p1), PRP_VERB, (vocseadef *)0)) { /* * check to make sure that the next word, which we're * about to take for a prep (the "with" in "pry box open * with crowbar") is actually not part of an object name * - if it is, use it as the object name rather than as * the prep */ if (vocgobj(ctx, cmd, typelist, cur, &next, FALSE, iolist, FALSE, FALSE, 0) <= 0) { /* aggregate the first preposition into the verb */ vprep = p1; /* use the current word as the object-introducing prep */ p1 = cmd[cur++]; } } /* try for an indirect object */ cnt = vocgobj(ctx, cmd, typelist, cur, &next, TRUE, iolist, TRUE, FALSE, &no_match); if (cnt > 0) { cur = next; v = vocffw(ctx, p1, (int)strlen(p1), (char *)0, 0, PRP_PREP, (vocseadef *)0); if (v == (vocwdef *)0) { /* let parseUnknownVerb handle it */ if (!try_unknown_verb(ctx, cmdActor, &cmd[first_word], &typelist[first_word], 0, &next_start, TRUE, VOCERR(20), "I don't know how to use the word \"%s\" like that.", p1)) { /* error - abort the command */ retval = 1; goto done; } else { /* go on to the next command */ continue; } } cmdPrep = v->vocwobj; if (vocready(ctx, cmd, typelist, cur, cmdActor, cmdPrep, vverb, vprep, dolist, iolist, &err, cmdbuf, first_word, &preparse_list, &next_start)) continue; else if ((typelist[cur] & VOCT_PREP) && vocffw(ctx, vverb, vvlen, cmd[cur], (int)strlen(cmd[cur]), PRP_VERB, (vocseadef *)0) && !vprep) { vprep = cmd[cur++]; if (vocready(ctx, cmd, typelist, cur, cmdActor, cmdPrep, vverb, vprep, dolist, iolist, &err, cmdbuf, first_word, &preparse_list, &next_start)) continue; else { /* let parseUnknownVerb handle it */ if (!try_unknown_verb(ctx, cmdActor, &cmd[first_word], &typelist[first_word], 0, &next_start, TRUE, VOCERR(19), "There are words after your command I couldn't use.")) { /* error - abandon the command */ retval = 1; goto done; } else { /* go on to the next command */ continue; } } } else { /* * If the latter object phrase is flagged with the * "trimmed preposition" flag, meaning that we could * have used the preposition in the noun phrase but * assumed instead it was part of the verb, reverse * this assumption now: add the preposition back to the * noun phrase and explain that there's no such thing * present. * * Otherwise, we simply have an unknown verb phrasing, * so let parseUnknownVerb handle it. */ vocoldef *np1 = (dolist[0].vocolflg & VOCS_TRIMPREP) != 0 ? &dolist[0] : (iolist[0].vocolflg & VOCS_TRIMPREP) != 0 ? &iolist[0] : 0; if (np1 != 0) { char namebuf[VOCBUFSIZ]; /* show the name, adding the prep back in */ voc_make_obj_name_from_list( ctx, namebuf, cmd, np1->vocolfst, np1->vocolhlst); vocerr(ctx, VOCERR(9), "I don't see any %s here.", namebuf); /* error - abort */ retval = 1; goto done; } else if (!try_unknown_verb( ctx, cmdActor, &cmd[first_word], &typelist[first_word], 0, &next_start, TRUE, VOCERR(19), "There are words after your command that I couldn't use.")) { /* error - abort */ retval = 1; goto done; } else { /* continue with the next command */ continue; } } } else if (cnt < 0) { /* * the noun phrase syntax was invalid - we've already * displayed an error about it, so simply return failure */ retval = 1; goto done; } else if (no_match) { /* * we found a valid noun phrase, but we didn't find any * matching objects - we've already generated an error, * so simply return failure */ retval = 1; goto done; } else { goto bad_sentence; } } else if ((cnt = vocchknoun(ctx, cmd, typelist, cur, &next, iolist, FALSE)) > 0) { /* look for prep at end of command */ cur = next; if (cmd[cur]) { if ((typelist[cur] & VOCT_PREP) && vocffw(ctx, vverb, vvlen, cmd[cur], (int)strlen(cmd[cur]), PRP_VERB, (vocseadef *)0) && !vprep) { vprep = cmd[cur++]; } } /* the command should definitely be done now */ if (cmd[cur] != 0) { /* let parseUnknownVerb handle it */ if (!try_unknown_verb(ctx, cmdActor, &cmd[first_word], &typelist[first_word], 0, &next_start, TRUE, VOCERR(21), "There appear to be extra words after your command.")) { /* error - stop the command */ retval = 1; goto done; } else { /* go on to the next command */ continue; } } /* * If we don't have a preposition yet, we need to find the * verb's default. If the verb object has a nilPrep * property defined, use that prep object; otherwise, look * up the word "to" and use that. */ if (cmdPrep == MCMONINV && (v = vocffw(ctx, vverb, vvlen, vprep, (vprep ? (int)strlen(vprep) : 0), PRP_VERB, (vocseadef *)0)) != 0) { runppr(ctx->voccxrun, v->vocwobj, PRP_NILPREP, 0); if (runtostyp(ctx->voccxrun) == DAT_OBJECT) cmdPrep = runpopobj(ctx->voccxrun); else rundisc(ctx->voccxrun); } /* if we didn't find anything with nilPrep, find "to" */ if (cmdPrep == MCMONINV) { v = vocffw(ctx, "to", 2, (char *)0, 0, PRP_PREP, (vocseadef *)0); if (v) cmdPrep = v->vocwobj; } /* execute the command */ err = execmd(ctx, cmdActor, cmdPrep, vverb, vprep, iolist, dolist, &cmd[first_word], &typelist[first_word], cmdbuf, cur - first_word, &preparse_list, &next_start); continue; } else if (cnt < 0) { retval = 1; goto done; } else { goto bad_sentence; } } done: /* copy back the command if we need to redo */ if (ctx->voccxredo && cmdbuf != origcmdbuf) strcpy(origcmdbuf, cmdbuf); /* return the status */ VOC_RETVAL(ctx, save_sp, retval); } /* execute a player command */ int voccmd(voccxdef *ctx, char *cmd, uint cmdlen) { int wrdcnt; int cur; int next; char *buffer; char **wordlist; objnum cmdActor; int first; /* * Make sure the stack is set up, resetting the stack on entry. Note * that this makes this routine non-reentrant - recursively calling * this routine will wipe out the enclosing caller's stack. */ voc_stk_ini(ctx, (uint)VOC_STACK_SIZE); /* allocate our stack arrays */ VOC_STK_ARRAY(ctx, char, buffer, 2*VOCBUFSIZ); VOC_STK_ARRAY(ctx, char *, wordlist, VOCBUFSIZ); /* until further notice, the actor for formatStrings is Me */ tiosetactor(ctx->voccxtio, ctx->voccxme); /* clear the 'ignore' flag */ ctx->voccxflg &= ~VOCCXFCLEAR; /* send to game function 'preparse' */ if (ctx->voccxpre != MCMONINV) { int typ; uchar *s; size_t len; int err; /* push the argument string */ runpstr(ctx->voccxrun, cmd, (int)strlen(cmd), 0); /* presume no error will occur */ err = 0; /* catch errors that occur during preparse() */ ERRBEGIN(ctx->voccxerr) { /* call preparse() */ runfn(ctx->voccxrun, ctx->voccxpre, 1); } ERRCATCH(ctx->voccxerr, err) { /* see what we have */ switch(err) { case ERR_RUNEXIT: case ERR_RUNEXITOBJ: case ERR_RUNABRT: /* * if we encountered abort, exit, or exitobj, treat it * the same as a nil result code - simply cancel the * entire current command */ break; default: /* re-throw any other error */ errrse(ctx->voccxerr); } } ERREND(ctx->voccxerr); /* if an error occurred, abort the command */ if (err != 0) return 0; /* check the return value's type */ typ = runtostyp(ctx->voccxrun); if (typ == DAT_SSTRING) { /* * It's a string - replace the command with the new string. * Pop the new string and scan its length prefix. */ s = runpopstr(ctx->voccxrun); len = osrp2(s) - 2; s += 2; /* * limit the size of the command to our buffer length, * leaving space for null termination */ if (len > cmdlen - 1) len = cmdlen - 1; /* copy the new command string into our command buffer */ memcpy(cmd, s, len); /* null-terminate the command buffer */ cmd[len] = '\0'; } else { /* the value isn't important for any other type */ rundisc(ctx->voccxrun); /* if they returned nil, we're done processing the command */ if (typ == DAT_NIL) return 0; } } /* break up into individual words */ if ((wrdcnt = voctok(ctx, cmd, buffer, wordlist, TRUE, FALSE, TRUE)) > 0) { /* skip any leading "and" and "then" separators */ for (cur = 0 ; cur < wrdcnt ; ++cur) { /* if this isn't "and" or "then", we're done scanning */ if (!vocspec(wordlist[cur], VOCW_THEN) && !vocspec(wordlist[cur], VOCW_AND)) break; } } /* * if we found no words, or we found only useless leading "and" and * "then" separators, run the "pardon" function to tell the player * that we didn't find any command on the line */ if (wrdcnt == 0 || (wrdcnt > 0 && cur >= wrdcnt)) { runfn(ctx->voccxrun, ctx->voccxprd, 0); return 0; } /* * if we got an error tokenizing the word list, return - we've * already displayed an error message, so there's nothing more for * us to do here */ if (wrdcnt < 0) return 0; /* process the commands on the line */ for (first = TRUE, cmdActor = ctx->voccxactor = MCMONINV ; cur < wrdcnt ; ++cur, first = FALSE) { /* presume we won't find an unknown word */ ctx->voccxunknown = 0; /* find the THEN that ends the command, if there is one */ for (next = cur ; cur < wrdcnt && !vocspec(wordlist[cur], VOCW_THEN) ; ++cur) ; wordlist[cur] = (char *)0; /* process until we run out of work to do */ for (;;) { /* try processing the command */ if (voc1cmd(ctx, &wordlist[next], cmd, &cmdActor, first)) { /* * If the unknown word flag is set, try the command * again from the beginning. This flag means that we * tried using parseUnknownDobj/Iobj to resolve the * unknown word, but that failed and we need to try * again with the normal "oops" processing. */ if (ctx->voccxunknown > 0) continue; /* return an error */ return 1; } /* success - we're done with the command */ break; } /* if the rest of the command is to be ignored, we're done */ if (ctx->voccxflg & VOCCXFCLEAR) return 0; /* scan past any separating AND's and THEN's */ while (cur + 1 < wrdcnt && (vocspec(wordlist[cur+1], VOCW_THEN) || vocspec(wordlist[cur+1], VOCW_AND))) ++cur; /* * if another command follows, add a blank line to separate the * results from the previous command and those from the next * command */ if (cur + 1 < wrdcnt) outformat("\\b"); } /* done */ return 0; } /* * Off-stack stack management */ /* allocate/reset the stack */ void voc_stk_ini(voccxdef *ctx, uint siz) { /* allocate it if it's not already allocated */ if (ctx->voc_stk_ptr == 0) { ctx->voc_stk_ptr = mchalo(ctx->voccxerr, siz, "voc_stk_ini"); ctx->voc_stk_end = ctx->voc_stk_ptr + siz; } /* reset the stack */ ctx->voc_stk_cur = ctx->voc_stk_ptr; } /* allocate space from the off-stack stack */ void *voc_stk_alo(voccxdef *ctx, uint siz) { void *ret; /* round size up to allocation units */ siz = osrndsz(siz); /* if there's not space, signal an error */ if (ctx->voc_stk_cur + siz > ctx->voc_stk_end) errsig(ctx->voccxerr, ERR_VOCSTK); /* save the return pointer */ ret = ctx->voc_stk_cur; /* consume the space */ ctx->voc_stk_cur += siz; /*#define SHOW_HI*/ #ifdef SHOW_HI { static uint maxsiz; if (ctx->voc_stk_cur - ctx->voc_stk_ptr > maxsiz) { char buf[20]; maxsiz = ctx->voc_stk_cur - ctx->voc_stk_ptr; sprintf(buf, "%u\n", maxsiz); os_printz(buf); } } #endif /* return the space */ return ret; } qtads-2.1.7/tads3/000077500000000000000000000000001265017072300136565ustar00rootroot00000000000000qtads-2.1.7/tads3/README.TXT000066400000000000000000000065631265017072300152260ustar00rootroot00000000000000Text Adventure Development System - TADS 3 Source Code Distribution Copyright (c) 1999, 2003 by Michael J. Roberts. All Rights Reserved. This is the C++ source code for TADS 3. In order to help make TADS available on a wide range of computer systems, we're distributing this source code. TADS 3 and this source code are copyrighted works, so please read the license information below if you're going to use these files. We want to keep TADS consistent in features and functionality across all of the different computers it works on, so we ask that you do not make any changes to this code beyond what is necessary to make TADS work on your type of computer system. In addition, the author retains the rights to TADS and any derivative works based on this source code. If your main interest in TADS is for writing or playing a game, you probably don't need this source code, since executable versions of TADS are available for a number of platforms. This source code is meant primarily for people who want to port TADS to new systems, or track down and fix problems with the system code itself. ------------------------------------------------------------------------------ HOW TO PORT TADS 3 TO A NEW PLATFORM To use this code, you will need a C++ compiler, and you may have to do some C or C++ coding to customize TADS to your system, in order to build a working version of TADS from this source code. This code depends upon the TADS 2 Operating System Interface (osifc) layer, which is not included with this distribution. You can obtain the TADS 2 source code, including TADS 2 osifc implementations for many operating systems, via FTP from ftp://ftp.ifarchive.org/if-archive/programming/tads2/source (Note that ftp.ifarchive.org is the new home, effective August 2001, of the former ftp.gmd.de archive.) TADS 3 uses the identical osifc implementation that TADS 2 uses. This means that if TADS 2 has been ported to your operating system, you will probably not have to write any new code to port TADS 3 - all you'll have to do is create a makefile (or your local equivalent) and build TADS 3 using the same osifc implementation files that you used for TADS 2. If TADS 2 has not already been ported to your system, you must first write the code necessary to implement the osifc layer for your platform. You don't need to port all of TADS 2, but you must port at least the osifc code. Refer to the file PORTNOTE.TXT in the TADS 2 source code distribution for information on how to do this. ------------------------------------------------------------------------------ CONTACTING THE AUTHOR For contact information, please visit www.tads.org. If you port this software to a new platform, I'd appreciate hearing about any changes you had to make to the portable code to make TADS work on your system. My goal is for the portable part of the code to compile and run everywhere without any changes; all of the port-specific code should be isolated to the "osifc" layer. Any time the portable part of the code proves to be less than portable, I consider it a bug and will try to fix it. ------------------------------------------------------------------------------ COPYRIGHT INFORMATION TADS 3 is "freeware," which means that it's copyrighted software that the author makes available free of charge but subject to certain conditions. Please see the accompanying license file LICENSE.TXT for license information. qtads-2.1.7/tads3/askf_os3.cpp000066400000000000000000000052001265017072300160670ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name askf_os.cpp - OS-dialog-based implementation of askfile Function Implements file dialog using the OS-provided file dialog Notes Only one of askf_tx.c or askf_os.c should be included in a given executable. For a text-only version, include askf_tx. For a version where os_askfile() provides a file dialog, use askf_os instead. We provide a choice of tio_askfile() implementations in the portable code (rather than only through the OS code) so that we can call the formatted text output routines in this version. An OS-layer implementation could not call the formatted output routines (it would have to call os_printf directly), which would result in poor prompt formatting any time a prompt exceeded a single line of text. Modified 09/27/99 MJRoberts - Creation */ #include "os.h" #include "t3std.h" #include "vmglob.h" #include "vmconsol.h" #include "charmap.h" /* * formatted text-only file prompt */ int CVmConsole::askfile(VMG_ const char *prompt, size_t prompt_len, char *reply, size_t replen, int dialog_type, os_filetype_t file_type, int /*bypass_script*/) { int result; size_t prompt_ui_len; char prompt_ui[256]; char fname[OSFNMAX]; /* flush any pending output */ flush(vmg_ VM_NL_INPUT); /* translate the prompt to the local UI character set */ prompt_ui_len = G_cmap_to_ui->map_utf8(prompt_ui, sizeof(prompt_ui) - 1, prompt, prompt_len, 0); /* null-terminate the translated prompt */ prompt_ui[prompt_ui_len] = '\0'; /* let the system handle it */ result = os_askfile(prompt, fname, sizeof(fname), dialog_type, file_type); /* if we were successful, translate the filename to UTF-8 */ if (result == OS_AFE_SUCCESS) { char *dst; size_t dstlen; /* * The result string is in the local UI character set, so we * must translate it to UTF-8 for the caller. Note that we * reserve one byte of the result buffer for the null * terminator. */ dst = reply; dstlen = replen - 1; G_cmap_from_ui->map(&dst, &dstlen, fname, strlen(fname)); /* null-terminate the result */ *dst = '\0'; } /* return the result */ return result; } qtads-2.1.7/tads3/charmap.cpp000066400000000000000000002417511265017072300160070ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/charmap.cpp,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name charmap.cpp - character mapper Function Notes Modified 10/17/98 MJRoberts - Creation */ #include #include #include "t3std.h" #include "os.h" #include "utf8.h" #include "resload.h" #include "charmap.h" #include "vmdatasrc.h" /* ------------------------------------------------------------------------ */ /* * Basic Mapper Class */ /* * match a name prefix (for the various Latin-X synonym names) */ static int prefixeq(const char *table_name, size_t digit_ofs, const char *prefix) { return (digit_ofs == strlen(prefix) && memicmp(table_name, prefix, digit_ofs) == 0); } /* * Open a mapping file, applying synonyms for character set names */ osfildef *CCharmap::open_map_file_syn(class CResLoader *res_loader, const char *table_name, charmap_type_t *map_type) { osfildef *fp; /* try opening the file/resource with the exact name given */ if ((fp = open_map_file(res_loader, table_name, map_type)) != 0) return fp; /* * We didn't find a file with the exact name given. Try various * synonyms for the Latin-X character sets. */ const char *p; for (p = table_name + strlen(table_name) ; p > table_name && is_digit(*(p-1)) ; --p) ; size_t ofs = p - table_name; int num = atoi(p); int found_suffix = (ofs != strlen(p) && num != 0); if (found_suffix) { if (prefixeq(table_name, ofs, "latin") || prefixeq(table_name, ofs, "latin-") || prefixeq(table_name, ofs, "iso") || prefixeq(table_name, ofs, "iso-") || prefixeq(table_name, ofs, "8859-") || prefixeq(table_name, ofs, "iso8859-") || prefixeq(table_name, ofs, "iso-8859-") || prefixeq(table_name, ofs, "iso_8859-") || prefixeq(table_name, ofs, "iso_8859_") || prefixeq(table_name, ofs, "l")) { /* rebuild the name as "isoX" */ char buf[25]; sprintf(buf, "iso%d", num); /* try loading it */ if ((fp = open_map_file(res_loader, buf, map_type)) != 0) return fp; } } /* * if that didn't work, and the whole string looks like just a number, * try putting "cp" in front of it - i.e., allow loading cp1252 as just * "1252" */ if (found_suffix && p == table_name) { /* it's all digits - try making it "cpXXXX" */ char buf[25]; sprintf(buf, "cp%d", num); if ((fp = open_map_file(res_loader, buf, map_type)) != 0) return fp; } /* * if we still don't have a file, check for "winXXXX" and "dosXXXX", as * synonyms for cpXXXX */ if (found_suffix && (prefixeq(table_name, ofs, "win") || prefixeq(table_name, ofs, "win-") || prefixeq(table_name, ofs, "windows") || prefixeq(table_name, ofs, "windows-") || prefixeq(table_name, ofs, "dos") || prefixeq(table_name, ofs, "dos-"))) { /* try making it "cpXXXX" */ char buf[25]; sprintf(buf, "cp%d", num); if ((fp = open_map_file(res_loader, buf, map_type)) != 0) return fp; } /* we couldn't find an alternative name; give up */ return 0; } /* * Open and characterize a mapping file */ osfildef *CCharmap::open_map_file(class CResLoader *res_loader, const char *table_name, charmap_type_t *map_type) { osfildef *fp; char respath[100]; ulong startpos; uchar buf[256]; uint entry_cnt; int found_single; int found_double; /* * Generate the full resource path - character mapping resource paths * always start with "charmap/" followed by the table name, plus the * ".tcm" extension. We use lower-case names for the mapping files, so * explicitly convert to lower, in case we're on a case-sensitive file * system like Unix. */ t3sprintf(respath, sizeof(respath), "charmap/%s.tcm", table_name); t3strlwr(respath); /* open the file for the character set */ fp = res_loader->open_res_file(respath, "charmap/cmaplib", "CLIB"); /* if we couldn't open the mapping file, return failure */ if (fp == 0) return 0; /* note the initial seek position */ startpos = osfpos(fp); /* read the header and the local-to-unicode header */ if (osfrb(fp, buf, 6)) goto fail; /* get the number of entries from the local-to-unicode header */ entry_cnt = osrp2(buf + 4); /* * Scan the entries to determine if we have single-byte, * double-byte, or both. */ found_single = found_double = FALSE; while (entry_cnt > 0) { size_t cur; const uchar *p; /* read up to a buffer-full or the remaining size */ cur = sizeof(buf)/4; if (cur > entry_cnt) cur = entry_cnt; /* read it */ if (osfrb(fp, buf, cur*4)) goto fail; /* deduct the amount we just read from the amount remaining */ entry_cnt -= cur; /* scan the entries */ for (p = buf ; cur > 0 ; --cur, p += 4) { /* * Note whether this is a single-byte or double-byte entry. * If the high-order byte is non-zero, it's a double-byte * entry; otherwise, it's a single-byte entry. * * Note that we read the UINT2 at (p+2), because that's the * local character-set code point in this tuple. */ if (((uint)osrp2(p + 2)) > 0xFF) found_double = TRUE; else found_single = TRUE; } /* * if we've found both single- and double-byte characters so * far, there's no need to look any further, since we know * everything about the file now */ if (found_single && found_double) break; } /* * create the appropriate mapper, depending on whether we found * single, double, or mixed characters */ if (found_single && found_double) { /* it's mixed */ *map_type = CHARMAP_TYPE_MB; } else if (found_double) { /* it's all double-byte */ *map_type = CHARMAP_TYPE_DB; } else if (found_single) { /* it's all single-byte */ *map_type = CHARMAP_TYPE_SB; } else { /* no mappings found at all - presume it's a single-byte mapper */ *map_type = CHARMAP_TYPE_SB; } /* seek back to the start of the table */ osfseek(fp, startpos, OSFSK_SET); /* return the file pointer */ return fp; fail: /* close the file and return failure */ osfcls(fp); return 0; } /* ------------------------------------------------------------------------ */ /* * Special built-in mapper to 7-bit ASCII. This is available as a last * resort when no external mapping file can be found. */ /* * create a plain ascii translator */ CCharmapToLocalASCII::CCharmapToLocalASCII() { unsigned char *dst; wchar_t *exp_dst; size_t siz; size_t exp_siz; struct ascii_map_t { wchar_t uni; char asc[5]; }; ascii_map_t *p; static ascii_map_t ascii_mapping[] = { /* regular ASCII characters */ { 1, { 1 } }, { 2, { 2 } }, { 3, { 3 } }, { 4, { 4 } }, { 5, { 5 } }, { 6, { 6 } }, { 7, { 7 } }, { 8, { 8 } }, { 9, { 9 } }, { 10, { 10 } }, { 11, { 11 } }, { 12, { 12 } }, { 13, { 13 } }, { 14, { 14 } }, { 15, { 15 } }, { 16, { 16 } }, { 17, { 17 } }, { 18, { 18 } }, { 19, { 19 } }, { 20, { 20 } }, { 21, { 21 } }, { 22, { 22 } }, { 23, { 23 } }, { 24, { 24 } }, { 25, { 25 } }, { 26, { 26 } }, { 27, { 27 } }, { 28, { 28 } }, { 29, { 29 } }, { 30, { 30 } }, { 31, { 31 } }, { 32, { 32 } }, { 33, { 33 } }, { 34, { 34 } }, { 35, { 35 } }, { 36, { 36 } }, { 37, { 37 } }, { 38, { 38 } }, { 39, { 39 } }, { 40, { 40 } }, { 41, { 41 } }, { 42, { 42 } }, { 43, { 43 } }, { 44, { 44 } }, { 45, { 45 } }, { 46, { 46 } }, { 47, { 47 } }, { 48, { 48 } }, { 49, { 49 } }, { 50, { 50 } }, { 51, { 51 } }, { 52, { 52 } }, { 53, { 53 } }, { 54, { 54 } }, { 55, { 55 } }, { 56, { 56 } }, { 57, { 57 } }, { 58, { 58 } }, { 59, { 59 } }, { 60, { 60 } }, { 61, { 61 } }, { 62, { 62 } }, { 63, { 63 } }, { 64, { 64 } }, { 65, { 65 } }, { 66, { 66 } }, { 67, { 67 } }, { 68, { 68 } }, { 69, { 69 } }, { 70, { 70 } }, { 71, { 71 } }, { 72, { 72 } }, { 73, { 73 } }, { 74, { 74 } }, { 75, { 75 } }, { 76, { 76 } }, { 77, { 77 } }, { 78, { 78 } }, { 79, { 79 } }, { 80, { 80 } }, { 81, { 81 } }, { 82, { 82 } }, { 83, { 83 } }, { 84, { 84 } }, { 85, { 85 } }, { 86, { 86 } }, { 87, { 87 } }, { 88, { 88 } }, { 89, { 89 } }, { 90, { 90 } }, { 91, { 91 } }, { 92, { 92 } }, { 93, { 93 } }, { 94, { 94 } }, { 95, { 95 } }, { 96, { 96 } }, { 97, { 97 } }, { 98, { 98 } }, { 99, { 99 } }, { 100, { 100 } }, { 101, { 101 } }, { 102, { 102 } }, { 103, { 103 } }, { 104, { 104 } }, { 105, { 105 } }, { 106, { 106 } }, { 107, { 107 } }, { 108, { 108 } }, { 109, { 109 } }, { 110, { 110 } }, { 111, { 111 } }, { 112, { 112 } }, { 113, { 113 } }, { 114, { 114 } }, { 115, { 115 } }, { 116, { 116 } }, { 117, { 117 } }, { 118, { 118 } }, { 119, { 119 } }, { 120, { 120 } }, { 121, { 121 } }, { 122, { 122 } }, { 123, { 123 } }, { 124, { 124 } }, { 125, { 125 } }, { 126, { 126 } }, { 127, { 127 } }, /* Latin-1 accented characters and symbols */ { 353, "s" }, { 352, "S" }, { 8218, "\'" }, { 8222, "\"" }, { 8249, "<" }, { 338, "OE" }, { 8216, "\'" }, { 8217, "\'" }, { 8220, "\"" }, { 8221, "\"" }, { 8211, "-" }, { 8212, "--" }, { 8482, "(tm)" }, { 8250, ">" }, { 339, "oe" }, { 376, "Y" }, { 162, "c" }, { 163, "L" }, { 165, "Y" }, { 166, "|" }, { 169, "(c)" }, { 170, "a" }, { 173, " " }, { 174, "(R)" }, { 175, "-" }, { 177, "+/-" }, { 178, "2" }, { 179, "3" }, { 180, "\'" }, { 181, "u" }, { 182, "P" }, { 183, "*" }, { 184, "," }, { 185, "1" }, { 186, "o" }, { 171, "<<" }, { 187, ">>" }, { 188, "1/4" }, { 189, "1/2" }, { 190, "3/4" }, { 192, "A" }, { 193, "A" }, { 194, "A" }, { 195, "A" }, { 196, "A" }, { 197, "A" }, { 198, "AE" }, { 199, "C" }, { 200, "E" }, { 201, "E" }, { 202, "E" }, { 203, "E" }, { 204, "I" }, { 205, "I" }, { 206, "I" }, { 207, "I" }, { 209, "N" }, { 210, "O" }, { 211, "O" }, { 212, "O" }, { 213, "O" }, { 214, "O" }, { 215, "x" }, { 216, "O" }, { 217, "U" }, { 218, "U" }, { 219, "U" }, { 220, "U" }, { 221, "Y" }, { 223, "ss" }, { 224, "a" }, { 225, "a" }, { 226, "a" }, { 227, "a" }, { 228, "a" }, { 229, "a" }, { 230, "ae" }, { 231, "c" }, { 232, "e" }, { 233, "e" }, { 234, "e" }, { 235, "e" }, { 236, "i" }, { 237, "i" }, { 238, "i" }, { 239, "i" }, { 241, "n" }, { 242, "o" }, { 243, "o" }, { 244, "o" }, { 245, "o" }, { 246, "o" }, { 247, "/" }, { 248, "o" }, { 249, "u" }, { 250, "u" }, { 251, "u" }, { 252, "u" }, { 253, "y" }, { 255, "y" }, { 710, "^" }, { 732, "~" }, /* math symbols */ { 402, "f" }, /* other symbols */ { 8226, "*" }, /* arrows */ { 8592, "<-" }, { 8594, "->" }, /* several capital Greek letters look a lot like Roman letters */ { 913, "A" }, { 914, "B" }, { 918, "Z" }, { 919, "H" }, { 921, "I" }, { 922, "K" }, { 924, "M" }, { 925, "N" }, { 927, "O" }, { 929, "P" }, { 932, "T" }, { 933, "Y" }, { 935, "X" }, /* Latin-2 accented characters */ { 260, "A" }, { 321, "L" }, { 317, "L" }, { 346, "S" }, { 350, "S" }, { 356, "T" }, { 377, "Z" }, { 381, "Z" }, { 379, "Z" }, { 261, "a" }, { 731, "o" }, { 322, "l" }, { 318, "l" }, { 347, "s" }, { 351, "s" }, { 357, "t" }, { 378, "z" }, { 733, "\"" }, { 382, "z" }, { 380, "z" }, { 340, "R" }, { 258, "A" }, { 313, "L" }, { 262, "C" }, { 268, "C" }, { 280, "E" }, { 282, "E" }, { 270, "D" }, { 272, "D" }, { 323, "N" }, { 327, "N" }, { 336, "O" }, { 344, "R" }, { 366, "U" }, { 368, "U" }, { 354, "T" }, { 341, "r" }, { 259, "a" }, { 314, "l" }, { 263, "c" }, { 269, "c" }, { 281, "e" }, { 283, "e" }, { 271, "d" }, { 273, "d" }, { 324, "n" }, { 328, "n" }, { 337, "o" }, { 345, "r" }, { 367, "u" }, { 369, "u" }, { 355, "t" }, { 0, { 0 } } }; /* determine how much space we'll need in the translation array */ for (p = ascii_mapping, siz = 0, exp_siz = 0 ; p->uni != 0 ; ++p) { /* we need space for this mapping string, plus a length prefix byte */ siz += strlen(p->asc) + 1; /* * if this is a multi-character expansion, count it in the * expansion array size */ if (strlen(p->asc) > 1) exp_siz += strlen(p->asc) + 1; } /* add in space for the default entry */ siz += 2; /* allocate the translation array */ xlat_array_ = (unsigned char *)t3malloc(siz); /* * allocate the expansion array; allocate one extra entry for the null * mapping at index zero */ exp_array_ = (wchar_t *)t3malloc((exp_siz + 1) * sizeof(wchar_t)); /* * start at element 1 of the expansion array (element zero is reserved * to indicate the null mapping) */ dst = xlat_array_; exp_dst = exp_array_ + 1; /* * Add the zeroeth entry, which serves as the default mapping for * characters that aren't otherwise mappable. */ set_mapping(0, 0); *dst++ = 1; *dst++ = '?'; /* set up the arrays */ for (p = ascii_mapping ; p->uni != 0 ; ++p) { size_t len; /* set the mapping's offset in the translation array */ set_mapping(p->uni, dst - xlat_array_); /* get the length of this mapping */ len = strlen(p->asc); /* set this mapping's length */ *dst++ = (unsigned char)len; /* copy the mapping */ memcpy(dst, p->asc, len); /* move past the mapping in the translation array */ dst += len; /* add the expansion mapping if necessary */ if (len > 1) { size_t i; /* add an expansion mapping */ set_exp_mapping(p->uni, exp_dst - exp_array_); /* set the length prefix */ *exp_dst++ = (wchar_t)len; /* add the mapping */ for (i = 0 ; i < len ; ++i) *exp_dst++ = (wchar_t)p->asc[i]; } } } /* ------------------------------------------------------------------------ */ /* * Special built-in mapper to ISO-8859-1. Because of the widespread use * of this character set, we make this mapping available even when no * external mapping file is available. */ /* * create an 8859-1 mapper */ CCharmapToLocal8859_1::CCharmapToLocal8859_1() { unsigned char *dst; size_t siz; wchar_t c; /* * Determine how much space we'll need in the translation array - we * need one byte for each character, plus one byte for the length of * each character. We also need two bytes for the default entry. */ siz = 256 + 256 + 2; /* allocate the mapping */ xlat_array_ = (unsigned char *)t3malloc(siz); /* start at the start of the array */ dst = xlat_array_; /* * Add the zeroeth entry, which serves as the default mapping for * characters that aren't otherwise mappable. */ set_mapping(0, 0); *dst++ = 1; *dst++ = '?'; /* * Set up the mappings - this is easy because each Unicode code point * from 0 to 255 maps to the same ISO 8859-1 code point. */ for (c = 0 ; c < 256 ; ++c) { /* set the mapping's offset in the translation array */ set_mapping(c, dst - xlat_array_); /* store the length (always 1) and translated character */ *dst++ = 1; *dst++ = (unsigned char)c; } } /* ------------------------------------------------------------------------ */ /* * Character mapping for Unicode to Local */ /* * create the translator */ CCharmapToLocal::CCharmapToLocal() { /* no mapping sub-tables yet */ memset(map_, 0, sizeof(map_)); memset(exp_map_, 0, sizeof(exp_map_)); /* no translation or expansion arrays yet */ xlat_array_ = 0; exp_array_ = 0; } /* * delete the translator */ CCharmapToLocal::~CCharmapToLocal() { size_t i; /* delete the translation array */ if (xlat_array_ != 0) t3free(xlat_array_); /* delete the expansion array */ if (exp_array_ != 0) t3free(exp_array_); /* delete any mapping tables we've allocated */ for (i = 0 ; i < sizeof(map_)/sizeof(map_[0]) ; ++i) { /* delete this mapping if allocated */ if (map_[i] != 0) t3free(map_[i]); } /* delete any expansion mapping tables */ for (i = 0 ; i < sizeof(exp_map_)/sizeof(exp_map_[0]) ; ++i) { /* delete this expansion mapping if allocated */ if (exp_map_[i] != 0) t3free(exp_map_[i]); } } /* * Set a mapping */ void CCharmapToLocal::set_mapping(wchar_t unicode_char, unsigned int xlat_offset) { int master_idx; /* get the master table index for this unicode character */ master_idx = (int)((unicode_char >> 8) & 0xff); /* if there's no sub-table here yet, create one */ if (map_[master_idx] == 0) { int i; /* allocate it */ map_[master_idx] = (unsigned int *)t3malloc(256 * sizeof(unsigned int)); /* * Set each entry to the default character, so that it will * produce valid results if no mapping is ever specified for the * character. The default character is always at offset zero in * the translation array. */ for (i = 0 ; i < 256 ; ++i) map_[master_idx][i] = 0; } /* set the mapping for the character's entry in the sub-table */ map_[master_idx][unicode_char & 0xff] = xlat_offset; } /* * Set an expansion mapping */ void CCharmapToLocal::set_exp_mapping(wchar_t unicode_char, unsigned int exp_offset) { int master_idx; /* get the master table index for this unicode character */ master_idx = (int)((unicode_char >> 8) & 0xff); /* if there's no sub-table here yet, create one */ if (exp_map_[master_idx] == 0) { int i; /* allocate it */ exp_map_[master_idx] = (unsigned int *)t3malloc(256 * sizeof(unsigned int)); /* * Set each entry to the default character, so that it will produce * valid results if no mapping is ever specified for the character. * The default character is always at offset zero in the expansion * array. */ for (i = 0 ; i < 256 ; ++i) exp_map_[master_idx][i] = 0; } /* set the mapping for the character's entry in the sub-table */ exp_map_[master_idx][unicode_char & 0xff] = exp_offset; } /* * Map a UTF-8 string of known byte length to the local character set */ size_t CCharmapToLocal::map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const { utf8_ptr src_start; size_t cur_total; char *srcend; /* remember where we started */ src_start = src; /* compute where the source buffer ends */ srcend = src.getptr() + src_byte_len; /* copy characters until we reach the end of the source string */ for (cur_total = 0 ; src.getptr() < srcend ; src.inc()) { char mapbuf[10]; size_t maplen = sizeof(mapbuf); char *mapp = mapbuf; /* map this character */ maplen = map(src.getch(), &mapp, &maplen); /* determine how to store the character */ if (dest == 0) { /* we're just counting */ } else if (dest_len >= maplen) { /* we have room for it - add it in */ memcpy(dest, mapbuf, maplen); /* advance past it */ dest += maplen; dest_len -= maplen; } else { /* there's no more room - stop now */ break; } /* add this into the total */ cur_total += maplen; } /* if the caller wants to know how much space we used, tell them */ if (src_bytes_used != 0) *src_bytes_used = src.getptr() - src_start.getptr(); /* return the total length of the result */ return cur_total; } /* * Map a null-terminated UTF-8 string to the local character set */ size_t CCharmapToLocal::map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const { size_t cur_total; /* copy characters until we find the terminating null */ for (cur_total = 0 ; src.getch() != 0 ; src.inc()) { /* * map this character into the output, if it will fit, but in * any case count the space it needs in the output */ cur_total += map(src.getch(), &dest, &dest_len); } /* * add a null terminator if there's room, but don't count it in the * result length */ map(0, &dest, &dest_len); /* return the total length of the result */ return cur_total; } /* * Map a string, allocating a new buffer if the caller doesn't provide one. */ size_t CCharmapToLocal::map_utf8_alo( char **buf, const char *src, size_t srclen) const { /* figure out how much space we need */ size_t buflen = map_utf8(0, 0, src, srclen, 0); /* allocate the buffer, adding space for null termination */ *buf = (char *)t3malloc(buflen + 1); /* if that failed, return null */ if (buf == 0) return 0; /* do the mapping */ buflen = map_utf8(*buf, buflen, src, srclen, 0); /* fill in the null terminator */ (*buf)[buflen] = '\0'; /* return the mapped length */ return buflen; } /* * Map a null-terminated UTF-8 string to the local character set, escaping * characters that aren't part of the local character set. */ size_t CCharmapToLocal::map_utf8z_esc( char *dest, size_t dest_len, utf8_ptr src, size_t (*esc_fn)(wchar_t, char **, size_t *)) const { size_t cur_total; /* copy characters until we find the terminating null */ for (cur_total = 0 ; src.getch() != 0 ; src.inc()) { wchar_t ch = src.getch(); /* if this character is mappable, map it; otherwise, escape it */ if (is_mappable(src.getch())) { /* map the character */ cur_total += map(ch, &dest, &dest_len); } else { /* we can't map it, so let the escape callback handle it */ cur_total += (*esc_fn)(ch, &dest, &dest_len); } } /* * add a null terminator if there's room, but don't count it in the * result length */ map(0, &dest, &dest_len); /* return the total length of the result */ return cur_total; } /* * Escape callback for map_utf8z_esc() - prepares source-code-style * 'backslash' escape sequences for unmappable characters. */ size_t CCharmapToLocal::source_esc_cb(wchar_t ch, char **dest, size_t *len) { char buf[7]; size_t copylen; /* prepare our own representation */ sprintf(buf, "\\u%04x", (unsigned int)ch); /* copy the whole thing if possible, but limit to the available space */ copylen = 6; if (copylen > *len) copylen = *len; /* copy the bytes */ memcpy(*dest, buf, copylen); /* advance the buffer pointers */ *dest += copylen; *len -= copylen; /* return the full space needed */ return 6; } /* * Map to UTF8 */ size_t CCharmapToLocal::map_utf8(char *dest, size_t dest_len, const char *src, size_t src_byte_len, size_t *src_bytes_used) const { utf8_ptr src_ptr; /* set up the source UTF-8 pointer */ src_ptr.set((char *)src); /* map it and return the result */ return map_utf8(dest, dest_len, src_ptr, src_byte_len, src_bytes_used); } /* * Create a mapper and load a mapping file */ CCharmapToLocal *CCharmapToLocal::load(CResLoader *res_loader, const char *table_name) { osfildef *fp; CCharmapToLocal *mapper; charmap_type_t map_type; /* if they want a trivial UTF-8 translator, return one */ if (name_is_utf8_synonym(table_name)) return new CCharmapToLocalUTF8(); /* if they want a Unicode 16-bit encoding, return one */ if (name_is_ucs2le_synonym(table_name)) return new CCharmapToLocalUcs2Little(); if (name_is_ucs2be_synonym(table_name)) return new CCharmapToLocalUcs2Big(); /* presume failure */ mapper = 0; /* open and characterize the mapping file */ fp = open_map_file_syn(res_loader, table_name, &map_type); /* if we didn't find a map file, check for built-in mappings */ if (fp == 0) { /* if they want a plain ASCII translator, return a default one */ if (name_is_ascii_synonym(table_name)) return new CCharmapToLocalASCII(); /* if they want a plain ISO-8859-1 translator, return a default one */ if (name_is_8859_1_synonym(table_name)) return new CCharmapToLocal8859_1(); /* no map file - return failure */ return 0; } /* create an appropriate mapper */ switch(map_type) { case CHARMAP_TYPE_SB: /* create a single-byte mapper */ mapper = new CCharmapToLocalSB(); break; case CHARMAP_TYPE_DB: /* create a double-byte mapper */ mapper = new CCharmapToLocalDB(); break; case CHARMAP_TYPE_MB: /* create a mixed multi-byte mapper */ mapper = new CCharmapToLocalMB(); break; default: /* other mapper types are currently unknown */ break; } /* if we successfully created a mapper, tell it to load the table */ if (mapper != 0) { /* load the table */ mapper->load_table(fp); } /* close the file */ osfcls(fp); /* return the mapper, if any */ return mapper; } /* * Load the character set translation table */ void CCharmapToLocal::load_table(osfildef *fp) { ulong startpos; ulong ofs; uchar buf[256]; uint cnt; ulong xbytes; ulong xchars; uint next_ofs; /* note the initial seek position */ startpos = osfpos(fp); /* read the first entry, which gives the offset of the to-local table */ if (osfrb(fp, buf, 4)) return; ofs = t3rp4u(buf); /* seek to the to-local table */ osfseek(fp, startpos + ofs, OSFSK_SET); /* read the number of entries and number of bytes needed */ if (osfrb(fp, buf, 6)) return; cnt = osrp2(buf); xbytes = t3rp4u(buf + 2); /* * Allocate space for the translation table. Note that we cannot * handle translation tables bigger than the maximum allowed in a * single allocation unit on the operating system. */ if (xbytes > OSMALMAX) return; xlat_array_ = (unsigned char *)t3malloc(xbytes); if (xlat_array_ == 0) return; /* * Read each mapping */ for (next_ofs = 0 ; cnt > 0 ; --cnt) { wchar_t codept; uint xlen; /* read the code point and translation length */ if (osfrb(fp, buf, 3)) return; /* decode the code point and translation length */ codept = osrp2(buf); xlen = (unsigned int)buf[2]; /* assign the mapping */ set_mapping(codept, next_ofs); /* store the translation length */ xlat_array_[next_ofs++] = buf[2]; /* read the translation bytes */ if (osfrb(fp, xlat_array_ + next_ofs, xlen)) return; /* skip past the translation bytes we've read */ next_ofs += xlen; } /* * Next, read the expansions, if present. * * If we find the $EOF marker, it means it's an old-format file without * the separate expansion definitions. Otherwise, we'll have the * expansion entry count and the aggregate number of unicode characters * in all of the expansions. */ if (osfrb(fp, buf, 6) || memcmp(buf, "$EOF", 4) == 0) return; /* decode the expansion entry count and aggregate length */ cnt = osrp2(buf); xchars = t3rp4u(buf + 2); /* * add one entry so that we can leave index zero unused, to indicate * unmapped characters */ ++xchars; /* add one array slot per entry, for the length prefix slots */ xchars += cnt; /* allocate space for the expansions */ exp_array_ = (wchar_t *)t3malloc(xchars * sizeof(wchar_t)); if (exp_array_ == 0) return; /* * read the mappings; start loading them at index 1, since we want to * leave index 0 unused so that it can indicate unused mappings */ for (next_ofs = 1 ; cnt > 0 ; --cnt) { wchar_t codept; uint xlen; size_t i; /* read this entry's unicode value and expansion character length */ if (osfrb(fp, buf, 3)) return; /* decode the code point and expansion length */ codept = osrp2(buf); xlen = (uint)buf[2]; /* assign the expansion mapping */ set_exp_mapping(codept, next_ofs); /* set the length prefix */ exp_array_[next_ofs++] = (wchar_t)xlen; /* read and store the expansion characters */ for (i = 0 ; i < xlen ; ++i) { /* read this translation */ if (osfrb(fp, buf, 2)) return; /* decode and store this translation */ exp_array_[next_ofs++] = osrp2(buf); } } } /* * Write to a file */ int CCharmapToLocal::write_file(CVmDataSource *fp, const char *buf, size_t bufl) { utf8_ptr p; /* set up to read from the buffer */ p.set((char *)buf); /* map and write one buffer-full at a time */ while (bufl > 0) { char conv_buf[256]; size_t conv_len; size_t used_src_len; /* map as much as we can fit into our buffer */ conv_len = map_utf8(conv_buf, sizeof(conv_buf), p, bufl, &used_src_len); /* write out this chunk */ if (fp->write(conv_buf, conv_len)) return 1; /* advance past this chunk in the input */ p.set(p.getptr() + used_src_len); bufl -= used_src_len; } /* no errors */ return 0; } /* ------------------------------------------------------------------------ */ /* * Character mapper - trivial UTF8-to-UTF8 conversion */ /* * map a character */ size_t CCharmapToLocalUTF8::map(wchar_t unicode_char, char **output_ptr, size_t *output_len) const { /* get the character size */ size_t map_len = utf8_ptr::s_wchar_size(unicode_char); /* if we don't have room for one more character, abort */ if (*output_len < map_len) { *output_len = 0; return map_len; } /* store the mapping */ utf8_ptr::s_putch(*output_ptr, unicode_char); /* increment the pointer by the number of characters we copied */ *output_ptr += map_len; /* adjust the remaining output length */ *output_len -= map_len; /* return the size of the result */ return map_len; } /* * Map a UTF-8 string of known byte length */ size_t CCharmapToLocalUTF8::map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const { /* * if they didn't give us a destination buffer, tell them how much * space is needed for the copy - this is identical to the length of * the source string since we make no changes to it */ if (dest == 0) { if (src_bytes_used != 0) *src_bytes_used = 0; return src_byte_len; } /* assume we'll be able to copy the whole string */ size_t copy_len = src_byte_len; /* if there's not enough space for the whole string, limit it */ if (copy_len > dest_len) { /* copy no more than the output buffer length */ copy_len = dest_len; /* * If this splits a character into two pieces, back up until we're * at a character boundary - that is, the first byte of the next * chunk is a non-continuation character. */ while (copy_len > 0 && utf8_ptr::s_is_continuation(src.getptr() + copy_len)) --copy_len; /* * If that left us with zero bytes, just do the partial copy to * ensure we don't get stuck in a zero-at-a-time infinite loop. */ if (copy_len == 0) copy_len = dest_len; } /* if we have an output buffer, copy the data */ if (dest != 0) memcpy(dest, src.getptr(), copy_len); /* set the amount we copied, if the caller is interested */ if (src_bytes_used != 0) *src_bytes_used = copy_len; /* return the number of bytes we put in the destination buffer */ return copy_len; } /* * Map a null-terminated UTF-8 string */ size_t CCharmapToLocalUTF8::map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const { /* get the source length */ size_t src_len = strlen(src.getptr()); /* copy the bytes */ map_utf8(dest, dest_len, src, src_len, 0); /* * if there's room for the null terminator (which takes up just one * byte in UTF-8), add it */ if (dest_len > src_len) *(dest + src_len) = '\0'; /* * return the amount of space needed to copy the whole string -- * this is identical to the source length, since we don't make any * changes to it */ return src_len; } /* ------------------------------------------------------------------------ */ /* * Character mapper - Unicode to Single-byte */ /* * map a character */ size_t CCharmapToLocalSB::map(wchar_t unicode_char, char **output_ptr, size_t *output_len) const { /* get the mapping */ size_t map_len; const unsigned char *mapping = get_xlation(unicode_char, &map_len); /* if we don't have room for one more character, abort */ if (*output_len < map_len) { *output_len = 0; return map_len; } /* copy the mapping */ memcpy(*output_ptr, mapping, map_len); /* increment the pointer by the number of characters we copied */ *output_ptr += map_len; /* adjust the remaining output length */ *output_len -= map_len; /* return the size of the result */ return map_len; } /* * Map a UTF-8 string of known byte length to the local character set */ size_t CCharmapToLocalSB::map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const { /* remember where we started */ utf8_ptr src_start = src; /* compute where the source buffer ends */ char *srcend = src.getptr() + src_byte_len; /* copy characters until we reach the end of the source string */ size_t cur_total; for (cur_total = 0 ; src.getptr() < srcend ; src.inc()) { const unsigned char *mapping; size_t map_len; /* get the mapping for this character */ mapping = get_xlation(src.getch(), &map_len); /* * if we have room, add it; otherwise, zero the output length * remaining so we don't try to add anything more */ if (dest == 0) { /* we're just counting */ } else if (map_len <= dest_len) { /* add the sequence */ memcpy(dest, mapping, map_len); /* adjust the output pointer and length remaining */ dest += map_len; dest_len -= map_len; } else { /* it doesn't fit - stop now */ break; } /* count the length in the total */ cur_total += map_len; } /* if the caller wants to know how much space we used, tell them */ if (src_bytes_used != 0) *src_bytes_used = src.getptr() - src_start.getptr(); /* return the total length of the result */ return cur_total; } /* * Map a null-terminated UTF-8 string to the local character set */ size_t CCharmapToLocalSB::map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const { size_t cur_total; /* copy characters until we find the terminating null */ for (cur_total = 0 ; src.getch() != 0 ; src.inc()) { const unsigned char *mapping; size_t map_len; /* get the mapping for this character */ mapping = get_xlation(src.getch(), &map_len); /* * if we have room, add it; otherwise, zero the output length * remaining so we don't try to add anything more */ if (map_len <= dest_len) { /* add the sequence */ memcpy(dest, mapping, map_len); /* adjust the output pointer and length remaining */ dest += map_len; dest_len -= map_len; } else { /* it doesn't fit - zero the output length remaining */ dest_len = 0; } /* count the length in the total */ cur_total += map_len; } /* * add a null terminator, if there's room, but don't count it in the * output length */ if (dest_len > 0) *dest = '\0'; /* return the total length of the result */ return cur_total; } /* ------------------------------------------------------------------------ */ /* * Character mapper - Unicode to 16-bit Wide Unicode local character set */ /* * map a character */ size_t CCharmapToLocalWideUnicode::map(wchar_t unicode_char, char **output_ptr, size_t *output_len) const { /* if we don't have room for another wchar_t, abort */ if (*output_len < sizeof(wchar_t)) { *output_len = 0; return sizeof(wchar_t); } /* * Set the wide character to the unicode value, with no translation * - unicode is the same everywhere. * * Note that the need to perform this trivial translation for this * character set is a secondary reason that this routine is virtual * (the primary reason is to handle the default ASCII translation). */ **(wchar_t **)output_ptr = unicode_char; /* increment the pointer by the size of a wide character */ ++(*(wchar_t **)output_ptr); /* return the size of the result */ return sizeof(wchar_t); } /* * Map a UTF-8 string of known byte length to the local character set */ size_t CCharmapToLocalWideUnicode:: map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const { utf8_ptr src_start; size_t cur_total; char *srcend; wchar_t *destw; /* remember where we started */ src_start = src; /* compute where the source buffer ends */ srcend = src.getptr() + src_byte_len; /* set up a wchar_t output pointer for convenience */ destw = (wchar_t *)dest; /* copy characters until we reach the end of the source string */ for (cur_total = 0 ; src.getptr() < srcend ; src.inc()) { /* * if we have room, add it; otherwise, zero the output length * remaining so we don't try to add anything more */ if (dest == 0) { /* we're just counting - don't store anything */ } else if (dest_len >= sizeof(wchar_t)) { /* add the sequence */ *destw++ = src.getch(); /* adjust the length remaining */ dest_len -= sizeof(wchar_t); } else { /* it doesn't fit - stop now */ break; } /* count the length in the total */ cur_total += sizeof(wchar_t); } /* if the caller wants to know how much space we used, tell them */ if (src_bytes_used != 0) *src_bytes_used = src.getptr() - src_start.getptr(); /* return the total length of the result */ return cur_total; } /* * Map a null-terminated UTF-8 string to the local character set */ size_t CCharmapToLocalWideUnicode:: map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const { size_t cur_total; wchar_t *destw; /* set up a wchar_t output pointer for convenience */ destw = (wchar_t *)dest; /* copy characters until we find the terminating null */ for (cur_total = 0 ; src.getch() != 0 ; src.inc()) { /* * if we have room, add it; otherwise, zero the output length * remaining so we don't try to add anything more */ if (dest_len >= sizeof(wchar_t)) { /* add the sequence */ *destw++ = src.getch(); /* adjust the length remaining */ dest_len -= sizeof(wchar_t); } else { /* it doesn't fit - zero the output length remaining */ dest_len = 0; } /* count the length in the total */ cur_total += sizeof(wchar_t); } /* * if there's room for a null terminator character (not byte - we need * to add an entire wide character), add it, but don't count it in the * return length */ if (dest_len >= sizeof(wchar_t)) *destw = '\0'; /* return the total length of the result */ return cur_total; } /* ------------------------------------------------------------------------ */ /* * Character mapper for 16-bit Wide Unicode, big-endian. Stores the * characters in big-endian UCS-2 representation. */ size_t CCharmapToLocalUcs2Big::map(wchar_t unicode_char, char **output_ptr, size_t *output_len) const { /* * If we don't have room for another byte pair, abort. Note that we * really do want to store exactly two bytes, not sizeof(anything), * since we're storing to the UCS-2 file format, which encodes each * character in two bytes. */ if (*output_len < 2) { *output_len = 0; return 2; } /* * Store the big-endian 16-bit value with no translation - unicode * is the same everywhere. * * Note that the need to perform this trivial translation for this * character set is a secondary reason that this routine is virtual * (the primary reason is to handle the default ASCII translation). * * Store the high-order 8 bits in the first byte, and the low-order * 8 bits in the second byte. */ **output_ptr = ((unicode_char >> 8) & 0xff); *(*output_ptr + 1) = (unicode_char & 0xff); /* skip two bytes in the output */ *output_ptr += 2; *output_len -= 2; /* return the size of the result */ return 2; } /* * Map a UTF-8 string of known byte length to the local character set */ size_t CCharmapToLocalUcs2Big:: map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const { utf8_ptr src_start; size_t cur_total; char *srcend; /* remember where we started */ src_start = src; /* compute where the source buffer ends */ srcend = src.getptr() + src_byte_len; /* copy characters until we reach the end of the source string */ for (cur_total = 0 ; src.getptr() < srcend ; src.inc()) { /* * if we have room, add it; otherwise, zero the output length * remaining so we don't try to add anything more */ if (dest == 0) { /* we're not storing anything */ } else if (dest_len >= 2) { wchar_t unicode_char; /* get the current character */ unicode_char = src.getch(); /* add the sequence */ *dest++ = ((unicode_char >> 8) & 0xff); *dest++ = (unicode_char & 0xff); /* adjust the length remaining */ dest_len -= 2; } else { /* it doesn't fit - stop now */ break; } /* count the length in the total */ cur_total += 2; } /* if the caller wants to know how much space we used, tell them */ if (src_bytes_used != 0) *src_bytes_used = src.getptr() - src_start.getptr(); /* return the total length of the result */ return cur_total; } /* * Map a null-terminated UTF-8 string to the local character set */ size_t CCharmapToLocalUcs2Big:: map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const { size_t cur_total; /* copy characters until we find the terminating null */ for (cur_total = 0 ; src.getch() != 0 ; src.inc()) { /* * if we have room, add it; otherwise, zero the output length * remaining so we don't try to add anything more */ if (dest_len >= 2) { wchar_t unicode_char; /* get the current character */ unicode_char = src.getch(); /* add the sequence */ *dest++ = ((unicode_char >> 8) & 0xff); *dest++ = (unicode_char & 0xff); /* adjust the length remaining */ dest_len -= 2; } else { /* it doesn't fit - zero the output length remaining */ dest_len = 0; } /* count the length in the total */ cur_total += 2; } /* return the total length of the result */ return cur_total; } /* ------------------------------------------------------------------------ */ /* * Character mapper for 16-bit Wide Unicode, little-endian. Stores the * characters in little-endian UCS-2 representation. */ size_t CCharmapToLocalUcs2Little::map(wchar_t unicode_char, char **output_ptr, size_t *output_len) const { /* * If we don't have room for another byte pair, abort. Note that we * really do want to store exactly two bytes, not sizeof(anything), * since we're storing to the UCS-2 file format, which encodes each * character in two bytes. */ if (*output_len < 2) { *output_len = 0; return 2; } /* * Store the little-endian 16-bit value with no translation - * unicode is the same everywhere. * * Note that the need to perform this trivial translation for this * character set is a secondary reason that this routine is virtual * (the primary reason is to handle the default ASCII translation). * * Store the low-order 8 bits in the first byte, and the high-order * 8 bits in the second byte. */ **output_ptr = (unicode_char & 0xff); *(*output_ptr + 1) = ((unicode_char >> 8) & 0xff); /* skip two bytes in the output */ *output_ptr += 2; *output_len -= 2; /* return the size of the result */ return 2; } /* * Map a UTF-8 string of known byte length to the local character set */ size_t CCharmapToLocalUcs2Little:: map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const { utf8_ptr src_start; size_t cur_total; char *srcend; /* remember where we started */ src_start = src; /* compute where the source buffer ends */ srcend = src.getptr() + src_byte_len; /* copy characters until we reach the end of the source string */ for (cur_total = 0 ; src.getptr() < srcend ; src.inc()) { /* * if we have room, add it; otherwise, zero the output length * remaining so we don't try to add anything more */ if (dest == 0) { /* we're just counting - don't store anything */ } else if (dest_len >= 2) { wchar_t unicode_char; /* get the current character */ unicode_char = src.getch(); /* add the sequence */ *dest++ = (unicode_char & 0xff); *dest++ = ((unicode_char >> 8) & 0xff); /* adjust the length remaining */ dest_len -= 2; } else { /* it doesn't fit - stop now */ break; } /* count the length in the total */ cur_total += 2; } /* if the caller wants to know how much space we used, tell them */ if (src_bytes_used != 0) *src_bytes_used = src.getptr() - src_start.getptr(); /* return the total length of the result */ return cur_total; } /* * Map a null-terminated UTF-8 string to the local character set */ size_t CCharmapToLocalUcs2Little:: map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const { size_t cur_total; /* copy characters until we find the terminating null */ for (cur_total = 0 ; src.getch() != 0 ; src.inc()) { /* * if we have room, add it; otherwise, zero the output length * remaining so we don't try to add anything more */ if (dest_len >= 2) { wchar_t unicode_char; /* get the current character */ unicode_char = src.getch(); /* add the sequence */ *dest++ = (unicode_char & 0xff); *dest++ = ((unicode_char >> 8) & 0xff); /* adjust the length remaining */ dest_len -= 2; } else { /* it doesn't fit - zero the output length remaining */ dest_len = 0; } /* count the length in the total */ cur_total += 2; } /* * if there's room for a null terminator character (which takes two * bytes in UCS-2), add it, but don't count it in the return length */ if (dest_len >= 2) { *dest++ = '\0'; *dest++ = '\0'; } /* return the total length of the result */ return cur_total; } /* ------------------------------------------------------------------------ */ /* * Character mapper - local to UTF-8 */ /* * create an appropriate mapping object for the given mapping file */ CCharmapToUni *CCharmapToUni::load(class CResLoader *res_loader, const char *table_name) { osfildef *fp; CCharmapToUni *mapper; charmap_type_t map_type; /* if they want a trivial UTF-8 translator, return one */ if (name_is_utf8_synonym(table_name)) return new CCharmapToUniUTF8(); /* if they want a 16-bit Unicode mapping, return one */ if (name_is_ucs2le_synonym(table_name)) return new CCharmapToUniUcs2Little(); if (name_is_ucs2be_synonym(table_name)) return new CCharmapToUniUcs2Big(); /* presume failure */ mapper = 0; /* open and characterize the mapping file */ fp = open_map_file_syn(res_loader, table_name, &map_type); /* check to make sure we opened a file */ if (fp == 0) { /* * if there was no file, and they want a plain ASCII translator, * return a default ASCII translator */ if (name_is_ascii_synonym(table_name)) return new CCharmapToUniASCII(); /* if they want an ISO-8859-1 translator, return a default one */ if (name_is_8859_1_synonym(table_name)) return new CCharmapToUni8859_1(); /* return failure */ return 0; } /* create an appropriate mapper */ switch(map_type) { case CHARMAP_TYPE_SB: /* create a single-byte mapper */ mapper = new CCharmapToUniSB(); break; case CHARMAP_TYPE_DB: /* create a double-byte mapper */ mapper = new CCharmapToUniDB(); break; case CHARMAP_TYPE_MB: /* create a mixed multi-byte mapper */ mapper = new CCharmapToUniMB(); break; default: /* other mapper types are currently unknown */ break; } /* if we successfully created a mapper, tell it to load the table */ if (mapper != 0) { /* load the table */ mapper->load_table(fp); } /* close the file */ osfcls(fp); /* return the mapper, if any */ return mapper; } /* * load a mapping table */ void CCharmapToUni::load_table(osfildef *fp) { uchar buf[256]; uint entry_cnt; /* read the header and the local table header */ if (osfrb(fp, buf, 6)) return; /* get the local table size from the local table header */ entry_cnt = osrp2(buf + 4); /* read the mappings */ while (entry_cnt > 0) { size_t cur; const uchar *p; /* figure out how many entries we can read this time */ cur = sizeof(buf)/4; if (cur > entry_cnt) cur = entry_cnt; /* read the entries */ if (osfrb(fp, buf, cur*4)) return; /* deduct this number from the remaining count */ entry_cnt -= cur; /* scan the entries */ for (p = buf ; cur > 0 ; p += 4, --cur) { /* map this entry */ set_mapping(osrp2(p), osrp2(p+2)); } } } /* * Map a null-terminated string into a buffer */ size_t CCharmapToUni::map_str(char *outbuf, size_t outbuflen, const char *input_str) { /* map the string to the output buffer */ size_t output_len = map(&outbuf, &outbuflen, input_str, strlen(input_str)); /* if there's space remaining in the output buffer, add a null byte */ if (outbuflen != 0) *outbuf = '\0'; /* return the number of bytes needed for the conversion */ return output_len; } /* * Map a counted-length string into a buffer */ size_t CCharmapToUni::map_str(char *outbuf, size_t outbuflen, const char *input_str, size_t input_len) { /* map the string to the output buffer and return the result */ return map(&outbuf, &outbuflen, input_str, input_len); } /* * Map a null-terminated string, allocating space for the result */ size_t CCharmapToUni::map_str_alo(char **outbuf, const char *input_str) { /* figure the space needed */ size_t outlen = map_str(0, 0, input_str); /* allocate the buffer */ *outbuf = (char *)t3malloc(outlen + 1); /* map the sring */ return map_str(*outbuf, outlen, input_str); } /* * Validate a buffer of utf-8 characters */ void CCharmapToUni::validate(char *buf, size_t len) { for ( ; len != 0 ; ++buf, --len) { /* check the type of the character */ char c = *buf; if ((c & 0x80) == 0) { /* 0..127 are one-byte characters, so this is valid */ } else if ((c & 0xC0) == 0x80) { /* * This byte has the pattern 10xxxxxx, which makes it a * continuation byte. Since we didn't just come from a * multi-byte intro byte, this is invalid. Change this byte to * '?'. */ *buf = '?'; } else if ((c & 0xE0) == 0xC0) { /* * This byte has the pattern 110xxxxx, which makes it the first * byte of a two-byte character sequence. The next byte must * have the pattern 10xxxxxx - if not, mark the current * character as invalid, since it's not part of a valid * sequence, and deal with the next byte separately. */ if (len > 1 && (*(buf+1) & 0xC0) == 0x80) { /* we have a valid two-byte sequence - skip it */ ++buf; --len; } else { /* * the next byte isn't a continuation, so the current byte * is invalid */ *buf = '?'; } } else { /* * This byte has the pattern 111xxxxx, which makes it the first * byte of a three-byte sequence. The next two bytes must be * marked as continuation bytes. */ if (len > 2 && (*(buf+1) & 0xC0) == 0x80 && (*(buf+2) & 0xC0) == 0x80) { /* we have a valid three-byte sequence - skip it */ buf += 2; len -= 2; } else { /* this is not a valid three-byte sequence */ *buf = '?'; } } } } /* ------------------------------------------------------------------------ */ /* * Basic single-byte character set to UTF-8 mapper */ /* * read from a single-byte file and translate to UTF-8 */ size_t CCharmapToUniSB_basic::read_file(CVmDataSource *fp, char *buf, size_t bufl) { size_t inlen; /* * Compute how much to read from the file. The input file is * composed of single-byte characters, so only read up to one third * of the buffer length; this will ensure that we can always fit * what we read into the caller's buffer. */ inlen = bufl / 3; /* in any case, we can't read more than our own buffer size */ if (inlen > sizeof(inbuf_)) inlen = sizeof(inbuf_); /* read from the file */ inlen = fp->readc(inbuf_, inlen); /* * Map data to the caller's buffer, and return the result. We're * certain that the data will fit in the caller's buffer: we're * mapping only a third as many characters as we have bytes * available, and each character can take up at most three bytes, * hence the worst case is that we fill the buffer completely. * * On the other hand, we may only fill the buffer to a third of its * capacity, but this is okay too, since we're not required to give * the caller everything they asked for. */ return map(&buf, &bufl, inbuf_, inlen); } /* ------------------------------------------------------------------------ */ /* * Plain ASCII local to UTF-8 mapper */ /* * map a string from the single-byte local character set to UTF-8 */ size_t CCharmapToUniASCII::map(char **outp, size_t *outlen, const char *inp, size_t inlen) const { size_t tot_outlen; /* we haven't written any characters to the output buffer yet */ tot_outlen = 0; /* scan each character (character == byte) in the input string */ for ( ; inlen > 0 ; --inlen, ++inp) { wchar_t uni; size_t csiz; /* * map any character outside of the 7-bit range to U+FFFD, the * Unicode REPLACEMENT CHARACTER, which is the standard way to * represent characters that can't be mapped from an incoming * character set */ if (((unsigned char)*inp) > 127) uni = 0xfffd; else uni = ((wchar_t)(unsigned char)*inp); /* get the size of this character */ csiz = utf8_ptr::s_wchar_size(uni); /* add it to the total output length */ tot_outlen += csiz; /* if there's room, add it to our output buffer */ if (*outlen >= csiz) { /* write it out */ *outp += utf8_ptr::s_putch(*outp, uni); /* deduct it from the remaining output length */ *outlen -= csiz; } else { /* there's no room - set the remaining output length to zero */ *outlen = 0; } } /* return the total output length */ return tot_outlen; } /* ------------------------------------------------------------------------ */ /* * Single-byte mapped local to UTF-8 mapper */ /* * map a string from the single-byte local character set to UTF-8 */ size_t CCharmapToUniSB::map(char **outp, size_t *outlen, const char *inp, size_t inlen) const { size_t tot_outlen; /* we haven't written any characters to the output buffer yet */ tot_outlen = 0; /* scan each character (character == byte) in the input string */ for ( ; inlen > 0 ; --inlen, ++inp) { wchar_t uni; size_t csiz; /* get the unicode mapping for this character */ uni = map_[(unsigned char)*inp]; /* get the size of this character */ csiz = utf8_ptr::s_wchar_size(uni); /* add it to the total output lenght */ tot_outlen += csiz; /* if there's room, add it to our output buffer */ if (*outlen >= csiz) { /* write it out */ *outp += utf8_ptr::s_putch(*outp, uni); /* deduct it from the remaining output length */ *outlen -= csiz; } else { /* there's no room - set the remaining output length to zero */ *outlen = 0; } } /* return the total output length */ return tot_outlen; } /* ------------------------------------------------------------------------ */ /* * Trivial UTF8-to-UTF8 input mapper */ /* * map a string */ size_t CCharmapToUniUTF8::map2(char **outp, size_t *outlen, const char *inp, size_t inlen, size_t *partial_len) const { size_t copy_len; /* * Make sure we copy only whole characters, by truncating the string * to a length that includes only whole characters. */ copy_len = utf8_ptr::s_trunc(inp, inlen); /* * note the length of any partial characters at the end of the buffer * for the caller - this is simply the difference between the original * length and the truncated copy length, since the truncation length * is simply the length excluding the partial last character bytes */ *partial_len = inlen - copy_len; /* limit the copying to what will fit in the output buffer */ if (copy_len > *outlen) { /* don't copy more than will fit, and don't copy partial characters */ copy_len = utf8_ptr::s_trunc(inp, *outlen); /* we don't have enough room, so set the output size to zero */ *outlen = 0; } else { /* we have room, so decrement the output size by the copy size */ *outlen -= copy_len; } /* copy the data, if we have an output buffer */ if (outp != 0) { /* copy the bytes */ memcpy(*outp, inp, copy_len); /* validate that the bytes we copied are well-formed UTF-8 */ validate(*outp, copy_len); /* advance the output pointer past the copied data */ *outp += copy_len; } /* * return the total input length -- the total output length is * always identical to the input length, because we don't change * anything */ return inlen; } /* * read a file */ size_t CCharmapToUniUTF8::read_file(CVmDataSource *fp, char *buf, size_t bufl) { size_t read_len; char *last_start; size_t last_got_len; size_t last_need_len; /* * Read directly from the file, up the buffer size minus two bytes. * We want to leave two extra bytes so that we can read any extra * continuation bytes for the last character, in order to ensure * that we always read whole characters; in the worst case, the last * character could be three bytes long, in which case we'd need to * read two extra bytes. * * If the available buffer size is less than three bytes, just read * the number of bytes they asked for and don't bother trying to * keep continuation sequences intact. */ if (bufl < 3) { read_len = fp->readc(buf, bufl); validate(buf, read_len); return read_len; } /* * read up to the buffer size, less two bytes for possible * continuation bytes */ read_len = fp->readc(buf, bufl - 2); /* * if we didn't satisfy the entire request, we're at the end of the * file, so there's no point in trying to finish off any * continuation sequences - in this case, just return what we have */ if (read_len < bufl - 2) { validate(buf, read_len); return read_len; } /* * Check the last byte we read to see if there's another byte or two * following. * * If the last byte is a continuation byte, this is a bit trickier. * We must back up to the preceding lead byte to figure out what we * have in this case. */ last_start = &buf[read_len - 1]; last_got_len = 1; if (utf8_ptr::s_is_continuation(last_start)) { /* * if we only read one byte, simply return the one byte - we * started in the middle of a sequence, so there's no way we can * read a complete sequence */ if (read_len == 1) { validate(buf, read_len); return read_len; } /* back up to the byte we're continuing from */ --last_start; ++last_got_len; /* * if this is another continuation byte, we've reached the maximum * byte length of three for a single character, so there's no way * we could need to read anything more */ if (utf8_ptr::s_is_continuation(last_start)) { validate(buf, read_len); return read_len; } } /* * Okay: we have last_start pointing to the start of the last * character, and last_got_len the number of bytes we actually have for * that last character. If the needed length differs from the length * we actually have, we need to read more. */ last_need_len = utf8_ptr::s_charsize(*last_start); if (last_need_len > last_got_len) { /* * we need more than we actually read, so read the remaining * characters */ read_len += fp->readc(buf + read_len, last_need_len - last_got_len); } /* validate the buffer - ensure that it's well-formed UTF-8 */ validate(buf, read_len); /* return the length we read */ return read_len; } /* ------------------------------------------------------------------------ */ /* * Basic UCS-2 to UTF-8 mapper */ /* * Read from a file, translating to UTF-8 encoding */ size_t CCharmapToUniUcs2::read_file(CVmDataSource *fp, char *buf, size_t bufl) { size_t inlen; /* * Compute how much to read from the file. The input file is composed * of two-byte characters, so only read up to two thirds of the buffer * length; this will ensure that we can always fit what we read into * the caller's buffer. * * Note that we divide by three first, then double the result, to * ensure that we read an even number of bytes. Each UCS-2 character * is represented in exactly two bytes, so we must always read pairs of * bytes to be sure we're reading whole characters. */ inlen = bufl / 3; inlen *= 2; /* in any case, we can't read more than our own buffer size */ if (inlen > sizeof(inbuf_)) inlen = sizeof(inbuf_); /* read from the file */ inlen = fp->readc(inbuf_, inlen); /* * Map data to the caller's buffer, and return the result. We're * certain that the data will fit in the caller's buffer: we're * mapping only a third as many characters as we have bytes * available, and each character can take up at most three bytes, * hence the worst case is that we fill the buffer completely. * * On the other hand, we may only fill the buffer to a third of its * capacity, but this is okay too, since we're not required to give * the caller everything they asked for. */ return map(&buf, &bufl, inbuf_, inlen); } /* ------------------------------------------------------------------------ */ /* * UCS-2 little-endian to UTF-8 mapper */ /* * map a string */ size_t CCharmapToUniUcs2Little::map(char **outp, size_t *outlen, const char *inp, size_t inlen) const { size_t tot_outlen; /* we haven't written any characters to the output buffer yet */ tot_outlen = 0; /* scan each character (character == byte pair) in the input string */ for ( ; inlen > 1 ; inlen -= 2, inp += 2) { wchar_t uni; size_t csiz; /* * read the little-endian two-byte value - no mapping is * required, since UCS-2 uses the same code point assignments as * UTF-8 */ uni = ((wchar_t)(unsigned char)*inp) + (((wchar_t)(unsigned char)*(inp + 1)) << 8); /* get the size of this character */ csiz = utf8_ptr::s_wchar_size(uni); /* add it to the total output lenght */ tot_outlen += csiz; /* if there's room, add it to our output buffer */ if (*outlen >= csiz) { /* write it out */ *outp += utf8_ptr::s_putch(*outp, uni); /* deduct it from the remaining output length */ *outlen -= csiz; } else { /* there's no room - set the remaining output length to zero */ *outlen = 0; } } /* return the total output length */ return tot_outlen; } /* ------------------------------------------------------------------------ */ /* * UCS-2 big-endian to UTF-8 mapper */ /* * map a string */ size_t CCharmapToUniUcs2Big::map(char **outp, size_t *outlen, const char *inp, size_t inlen) const { size_t tot_outlen; /* we haven't written any characters to the output buffer yet */ tot_outlen = 0; /* scan each character (character == byte pair) in the input string */ for ( ; inlen > 1 ; inlen -= 2, inp += 2) { wchar_t uni; size_t csiz; /* * read the big-endian two-byte value - no mapping is required, * since UCS-2 uses the same code point assignments as UTF-8 */ uni = (((wchar_t)(unsigned char)*inp) << 8) + ((wchar_t)(unsigned char)*(inp + 1)); /* get the size of this character */ csiz = utf8_ptr::s_wchar_size(uni); /* add it to the total output lenght */ tot_outlen += csiz; /* if there's room, add it to our output buffer */ if (*outlen >= csiz) { /* write it out */ *outp += utf8_ptr::s_putch(*outp, uni); /* deduct it from the remaining output length */ *outlen -= csiz; } else { /* there's no room - set the remaining output length to zero */ *outlen = 0; } } /* return the total output length */ return tot_outlen; } /* ------------------------------------------------------------------------ */ /* * Multi-byte character set translation to Unicode */ /* * construct the mapper */ CCharmapToUniMB::CCharmapToUniMB() { int i; cmap_mb_entry *p; /* clear out the mapping table */ for (i = 0, p = map_ ; i < 256 ; ++i, ++p) { /* assume this lead byte won't have a sub-table */ p->sub = 0; /* * we don't have a mapping for this lead byte yet, so use U+FFFD * (the Unicode REPLACEMENT CHARACTER) as the default mapping in * case we never assign it any other mapping */ p->ch = 0xFFFD; } } /* * delete the mapper */ CCharmapToUniMB::~CCharmapToUniMB() { int i; cmap_mb_entry *p; /* delete all of our sub-tables */ for (i = 0, p = map_ ; i < 256 ; ++i, ++p) { /* if this sub-table was allocated, delete it */ if (p->sub != 0) t3free(p->sub); } } /* * Set a mapping */ void CCharmapToUniMB::set_mapping(wchar_t uni_code_pt, wchar_t local_code_pt) { /* * Check to see if it's a one-byte or two-byte mapping. If the local * code point is in the range 0-255, it's a one-byte character; * otherwise, it's a two-byte character. */ if (local_code_pt <= 255) { /* it's a single-byte character, so simply set the mapping */ map_[(unsigned char)local_code_pt].ch = uni_code_pt; } else { cmap_mb_entry *entp; wchar_t *subp; /* * Get the mapping table entry for the lead byte. The lead byte of * the local code point is given by the high-order byte of the * local code point. (Note that this doesn't have anything to do * with the endian-ness of the local platform. The generic Unicode * mapping tables are specifically designed this way, independently * of endian-ness.) */ entp = &map_[(unsigned char)((local_code_pt >> 8) & 0xff)]; /* * It's a two-byte character. The high-order byte is the lead * byte, and the low-order byte is the trailing byte of the * two-byte sequence. * * If we haven't previously set up a sub-table for the lead byte, * do so now. */ if ((subp = entp->sub) == 0) { size_t i; wchar_t *p; /* allocate a new sub-mapping table for the lead byte */ subp = entp->sub = (wchar_t *)t3malloc(256 * sizeof(wchar_t)); /* initialize each entry to U+FFFD, in case we never map them */ for (i = 256, p = subp ; i != 0 ; --i, *p++ = 0xFFFD) ; } /* set the mapping in the sub-table for the second byte */ subp[(unsigned char)(local_code_pt & 0xff)] = uni_code_pt; } } /* * map a string, providing partial character info */ size_t CCharmapToUniMB::map2(char **output_ptr, size_t *output_buf_len, const char *input_ptr, size_t input_len, size_t *partial_len) const { size_t needed_out_len; /* presume we won't have a partial last character */ *partial_len = 0; /* we haven't found anything to store in the output yet */ needed_out_len = 0; /* keep going until we've mapped each character */ while (input_len != 0) { unsigned char c; const cmap_mb_entry *entp; wchar_t wc; size_t wlen; /* get the lead byte of the next input character */ c = *input_ptr; /* get the primary mapping table entry for the lead byte */ entp = &map_[c]; /* check for a one-byte or two-byte mapping */ if (entp->sub == 0) { /* it's a one-byte character - get the mapping */ wc = entp->ch; /* skip the single byte of input */ ++input_ptr; --input_len; } else { /* * it's a two-byte character lead byte - make sure we have a * complete input character */ if (input_len < 2) { /* we have an incomplete last character - tell the caller */ *partial_len = 1; /* we're done mapping it */ break; } /* get the second byte of the sequence */ c = input_ptr[1]; /* get the translation from the sub-table */ wc = entp->sub[c]; /* skip the two-byte sequence */ input_ptr += 2; input_len -= 2; } /* we have the translation - note its stored UTF-8 byte size */ wlen = utf8_ptr::s_wchar_size(wc); /* check for room to store the output character */ if (wlen > *output_buf_len) { /* * there's no room to store this character - zero out the * output buffer length so that we know not to try storing * anything else in the buffer */ *output_buf_len = 0; } else { /* there's room - store it */ wlen = utf8_ptr::s_putch(*output_ptr, wc); /* consume output buffer space */ *output_ptr += wlen; *output_buf_len -= wlen; } /* count the needed length, whether we stored it or not */ needed_out_len += wlen; } /* return the required output length */ return needed_out_len; } /* * read from a multi-byte input file, translating to UTF-8 */ size_t CCharmapToUniMB::read_file(CVmDataSource *fp, char *buf, size_t bufl) { size_t inlen; size_t outlen; size_t partial; /* * Compute how much to read from the file. The input file is composed * of one-byte or two-byte characters, so only read up to one-third of * the caller's buffer length; this will ensure that in the worst case * we can always fit what we read into the caller's buffer. (The worst * case is that the input is entirely single-byte local characters that * translate into three-byte UTF-8 characters.) */ inlen = bufl / 3; /* in any case, we can't read more than our own buffer size */ if (inlen >= sizeof(inbuf_)) inlen = sizeof(inbuf_); /* read raw bytes from the file */ inlen = fp->readc(inbuf_, inlen); /* * Map data to the caller's buffer. Note if we have a partial * character at the end of the buffer (i.e., the last byte of the * buffer is a lead byte that requires a second byte to make up a * complete two-byte local character), so that we can read an * additional byte to complete the two-byte final character if * necessary. */ outlen = map2(&buf, &bufl, inbuf_, inlen, &partial); /* * if we have a partial trailing character, read the other half of the * final character */ if (partial != 0) { /* move the lead byte to the start of our buffer */ inbuf_[0] = inbuf_[inlen - 1]; /* read the extra byte to form a complete character */ inlen = 1 + fp->readc(inbuf_ + 1, 1); /* if we got the second byte, map the complete final character */ if (inlen == 2) outlen += map(&buf, &bufl, inbuf_, inlen); } /* return the result length */ return outlen; } qtads-2.1.7/tads3/charmap.h000066400000000000000000001440551265017072300154530ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/charmap.h,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name charmap.h - character-set mapper Function Provides mappings between 16-bit Unicode and single-byte, multi-byte, and double-byte character sets. Notes Modified 10/17/98 MJRoberts - Creation */ #ifndef CHARMAP_H #define CHARMAP_H #include #include #include #include "utf8.h" #include "os.h" #include "t3std.h" /* ------------------------------------------------------------------------ */ /* * Mapping Types. This enum provides a characterization of a local * character set (as defined in a mapping file). */ enum charmap_type_t { /* * Single-byte character set - each character is represented with a * single 8-bit byte. */ CHARMAP_TYPE_SB, /* * Double-byte character set - each character is represented with * exactly two 8-bit bytes. In each byte pair, the first byte is * taken as the high-order byte, so a text input stream consisting * of the bytes 0x12, 0x34, 0x56, 0x78 would be interpreted as the * two 16-bit code point values 0x1234, 0x5678. */ CHARMAP_TYPE_DB, /* * Mixed multi-byte - each character is represented by either one or * two 8-bit bytes. Each two-byte character starts with a byte that * is only used in two-byte characters; each one-byte character * consists of a single byte that is not used as the first byte of * any two-byte character. In each two-byte character, the first * byte is taken as the high-order byte. * * For example, assuming that 0x00-0x7F are defined as single-byte * characters, and 0x8000-0xFFFF are defined as double-byte * characters, the byte sequence 0x12, 0x81, 0xAB, 0x82, 0xCD, 0x34 * would be taken as the character sequence 0x12, 0x81AB, 0x82CD, * 0x34. */ CHARMAP_TYPE_MB }; /* ------------------------------------------------------------------------ */ /* * Basic character mapper class. */ class CCharmap { public: /* add a reference */ void add_ref() { ++ref_cnt_; } /* release a reference; delete on removing the last reference */ void release_ref() { /* count the unreference */ --ref_cnt_; /* if that leaves no references, delete me */ if (ref_cnt_ == 0) delete this; } protected: CCharmap() { /* start out with one reference, for the initial creator */ ref_cnt_ = 1; } virtual ~CCharmap() { } /* * Open and characterize a mapping file. Returns the osfildef * pointer if the file was successfully opened and parsed, or null * if not. Sets *map_type to indicate the type of mapping contained * in the file. */ static osfildef *open_map_file(class CResLoader *res_loader, const char *table_name, charmap_type_t *map_type); /* * Open and characterize a map file, checking name synonyms */ static osfildef *open_map_file_syn(class CResLoader *res_loader, const char *table_name, charmap_type_t *map_type); /* check a name to see if it matches one of the names for ASCII */ static int name_is_ascii_synonym(const char *table_name) { /* accept any of the various synonyms for ASCII */ return (stricmp(table_name, "us-ascii") == 0 || stricmp(table_name, "asc7dflt") == 0 || stricmp(table_name, "ascii") == 0 || stricmp(table_name, "iso646-us") == 0 || stricmp(table_name, "iso-ir-6") == 0 || stricmp(table_name, "cp367") == 0 || stricmp(table_name, "us") == 0); } /* check for a utf-8 synonym */ static int name_is_utf8_synonym(const char *table_name) { return (stricmp(table_name, "utf-8") == 0 || stricmp(table_name, "utf8") == 0); } /* check for ucs2-le synonyms */ static int name_is_ucs2le_synonym(const char *table_name) { return (stricmp(table_name, "utf-16le") == 0 || stricmp(table_name, "utf16le") == 0 || stricmp(table_name, "utf_16le") == 0 || stricmp(table_name, "unicodel") == 0 || stricmp(table_name, "unicode-l") == 0 || stricmp(table_name, "unicode-le") == 0 || stricmp(table_name, "ucs-2le") == 0 || stricmp(table_name, "ucs2le") == 0); } /* check for ucs2-be synonyms */ static int name_is_ucs2be_synonym(const char *table_name) { return (stricmp(table_name, "utf-16be") == 0 || stricmp(table_name, "utf16be") == 0 || stricmp(table_name, "utf_16be") == 0 || stricmp(table_name, "unicodeb") == 0 || stricmp(table_name, "unicode-b") == 0 || stricmp(table_name, "unicode-be") == 0 || stricmp(table_name, "ucs-2be") == 0 || stricmp(table_name, "ucs2be") == 0); } /* check a name to see if it matches one of the names for ISO 8859-1 */ static int name_is_8859_1_synonym(const char *table_name) { /* accept any of the various names for ISO 8859-1 */ return (stricmp(table_name, "iso-8859-1") == 0 || stricmp(table_name, "iso_8859-1") == 0 || stricmp(table_name, "iso_8859_1") == 0 || stricmp(table_name, "iso8859-1") == 0 || stricmp(table_name, "iso8859_1") == 0 || stricmp(table_name, "8859-1") == 0 || stricmp(table_name, "8859_1") == 0 || stricmp(table_name, "iso-ir-100") == 0 || stricmp(table_name, "latin1") == 0 || stricmp(table_name, "latin-1") == 0 || stricmp(table_name, "l1") == 0 || stricmp(table_name, "iso1") == 0 || stricmp(table_name, "cp819") == 0); } /* reference count */ unsigned int ref_cnt_; }; /* ------------------------------------------------------------------------ */ /* * Base character mapper class for mapping from a local character set to * UTF-8. This is an abstract interface that must be implemented for * different classes of character sets. */ class CCharmapToUni: public CCharmap { public: /* initialize */ CCharmapToUni() { } /* * Create a mapping object for a given character table. We'll read * enough of the character table to determine the appropriate * concrete subclass to instantiate, then create an object, load the * table into the object, and return the object. The caller is * responsible for deleting the object when finished with it. * * Returns null if the mapping file cannot be loaded. */ static CCharmapToUni *load(class CResLoader *res_loader, const char *table_name); /* * Validate a UTF-8 buffer. If we find any ill-formed byte sequences, * we'll convert the errant bytes to '?' characters. */ static void validate(char *buf, size_t len); /* * Determine if the given byte sequence forms a complete character in * the local character set. Returns true if so, false if not. 'len' * must be at least 1. */ virtual int is_complete_char(const char *p, size_t len) const = 0; /* * Map one character. Advances the input pointer and length past the * character. If there's a complete character at 'p', maps the * character and returns true; if additional bytes are needed to form a * complete character, leaves 'p' unchanged and returns false. */ virtual int mapchar(wchar_t &ch, const char *&p, size_t &len) = 0; /* * Convert a string from the local character set to Unicode. * Returns the byte length of the output. If the output buffer is * too small to store the result, we will return the size of the * full result, but we won't write past the end of the buffer. * * We'll advance *output_ptr by the number of bytes we write. * * If we store anything, we'll decrement *output_buf_len by the * number of bytes we store; if we don't have enough room, we'll set * *output_buf_len to zero. * * input_ptr is a pointer to the input string; input_len is the * length in bytes of the input string. */ virtual size_t map(char **output_ptr, size_t *output_buf_len, const char *input_ptr, size_t input_len) const = 0; /* * Convert a string from the local character set to Unicode. * * This works the same way as map(), but additionally provides * information on the consumption of source bytes by filling in * partial_len with the number of bytes at the end of the source * buffer that are not mappable because they do not form complete * characters in the source character set. Since we scan all input * regardless of whether there's space to store the resulting output, * this will reflect the same number of bytes no matter what the * output buffer length. */ virtual size_t map2(char **output_ptr, size_t *output_buf_len, const char *input_ptr, size_t input_len, size_t *partial_len) const = 0; /* * Map a null-terminated string into a buffer; returns the number of * bytes of the buffer actually needed to store the string. If the * entire string couldn't be mapped, this will return a number * greater than or equal to the output buffer size, but we will not * write beyond the end of the buffer. * * If there's space, the result will be null-terminated; however, * the null terminator byte will not be included in the result * length. If the return value exactly equals outbuflen, it means * that the string exactly fills the buffer, hence there isn't space * for a null terminator. */ size_t map_str(char *outbuf, size_t outbuflen, const char *input_str); /* * Map a counted-length string into a buffer; returns the number of * bytes actually needed to store the string. If the entire string * couldn't be mapped, returns the number of bytes actually needed, but * will only map up to the available size limit. Does not * null-terminate the result. */ size_t map_str(char *outbuf, size_t outbuflen, const char *input_str, size_t input_len); /* * Map a string, allocating space * * The caller is responsible for freeing the returned string with * t3free() when done with it. */ size_t map_str_alo(char **outbuf, const char *input_str); /* * Read characters from a file into a buffer, translating the * characters to UTF-8. Returns the number of bytes copied into the * buffer; returns zero on end of file. The buffer must be at least * three bytes long to ensure that at least one character can be read * from the file (the longest UTF-8 character takes up three bytes), * since it would otherwise not be possible to distinguish reaching * the end of the file from simply being unable to fit even one * character into the buffer. * * The file can be opened in text or binary mode; we don't pay any * attention to newline sequences, so the mode is not relevant to us. * * This routine may read fewer than the desired number of bytes. Upon * return, the file's seek position should be set to the next byte of * the file after the last character copied into the output buffer. * * 'read_limit' is the maximum number of bytes we're allowed to read * from the underlying file. If this is zero, then the read size is * unlimited. */ virtual size_t read_file(class CVmDataSource *fp, char *buf, size_t bufl) = 0; protected: /* delete the mapping */ virtual ~CCharmapToUni() { } /* load the mapping table from the file */ void load_table(osfildef *fp); /* * Set a mapping. uni_code_pt is the unicode code point, and * local_code_pt is the code point in the local character set. */ virtual void set_mapping(wchar_t uni_code_pt, wchar_t local_code_pt) = 0; }; /* ------------------------------------------------------------------------ */ /* * Base character mapper class for mapping from Unicode UTF-8 to a local * character set. This is an abstract interface that must be separately * implemented for different classes of character sets. * * Each mapping object maintains a table of mapping tables. The master * table contains an array of up to 256 sub-tables. The top 8 bits of * the unicode character value give the index in the master table. Each * entry in the master table is a pointer to a sub-table, or a null * pointer if there are no mappings for characters in the range for that * sub-table. * * For example, unicode characters 0x0000 through 0x007f are mapped * through the table obtained by getting the pointer at index 0 from the * master table. Unicode characters 0x0200 through 0x02ff are in the * table at master table index 2. * * If a master table index entry is empty (i.e., the pointer in the * master table at that index is null), it means that all of the * characters in the range for that master index map to the default * character. Otherwise, we index into the sub-table using the * low-order 8 bits of the Unicode character code to find the character * mapping giving the local character set code for the Unicode value. * * Each entry in the mapping table is the offset of the translation of * the character within the translation array. The translation array is * an array of bytes. The first byte of each entry is the length in * bytes of the entry (not including the length byte), followed by the * bytes of the entry. * * The first entry in the translation array is always the default * character, which is the mapping we use for characters with no other * valid mapping. */ class CCharmapToLocal: public CCharmap { public: /* initialize */ CCharmapToLocal(); /* create a mapper and load the mapping from a file */ static CCharmapToLocal *load(class CResLoader *res_loader, const char *table_name); /* * Convert a character from Unicode to the local character set. * Stores the character's byte or bytes at the given pointer, and * increments the pointer to point to the next byte after the * character. * * Returns the byte length of the output. If the output buffer is * not long enough to store the result, we simply return the size of * the result without storing anything. * * If we actually store anything, we'll decrement *output_buf_len by * the number of bytes we stored; if we don't have room to store * anything, we'll set *output_buf_len to zero. */ virtual size_t map(wchar_t unicode_char, char **output_ptr, size_t *output_buf_len) const = 0; /* * Simple single-character mapper - returns the byte length of the * local character equivalent of the unicode character, which is * written into the buffer. If the buffer isn't big enough, we'll * still return the length, but won't write anything to the buffer. */ size_t map_char(wchar_t unicode_char, char *buf, size_t buflen) { /* map the character */ return map(unicode_char, &buf, &buflen); } /* * Convert a UTF-8 string with a given byte length to the local * character set. * * Returns the byte length of the result. If the result is too long * to fit in the output buffer, we'll return the number of bytes we * actually were able to store (we'll store as much as we can, and * stop when we run out of space). We'll indicate in * *src_bytes_used how many bytes of the source we were able to map. * * If the output buffer is null, we will store nothing, but simply * determine how much space it would take to store the entire string. * * This base class provides an implementation of this method that is * suitable for all subclasses, but the method is defined as virtual * so that subclasses can override it with a more tailored (and thus * more efficient) implementation. The general-purpose base-class * implementation must call the virtual function map() for each * character mapped. */ virtual size_t map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const; /* * map to local - alternative interface using character buffers (rather * than UTF8 pointers) */ size_t map_utf8(char *dest, size_t dest_len, const char *src, size_t src_byte_len, size_t *src_bytes_used) const; /* * Map to local, allocating a new buffer. Fills in 'buf' with a * pointer to the new buffer, which we allocate via t3malloc() with * enough space for the mapped string plus a null terminator. Maps the * string into the buffer and adds a null byte. Returns the byte * length of the mapped string (not including the null byte). * * The caller is responsible for freeing the returned string with * t3free() when done with it. */ size_t map_utf8_alo(char **buf, const char *src, size_t srclen) const; /* * Convert a null-terminated UTF-8 string to the local character set. * * Returns the byte length of the result. If the result is too long * to fit in the output buffer, we'll return the size without storing * the entire string (we'll store as much as we can, and stop when we * run out of space, but continue counting the length needed; call * with a destination buffer length of zero to simply determine how * much space is needed for the result). * * The length returned does NOT include the null terminator. However, * if there's room, we will null-terminate the result string. So, if * the caller wants the result to be null terminated, it should make * sure that the buffer contains one byte more than the space reported * as necessary to store the result. */ virtual size_t map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const; /* * Convert a null-terminated UTF-8 string to the local character set, * filling in an 'escape' sequence for unknown characters. For each * unknown character, we'll invoke the given callback to get the * 'escaped' representation. Use &CCharmapToLocal::source_esc_cb, for * example, to map using source-code-style escape sequences. * * The callback takes the unmappable character, a pointer to the output * buffer, and a pointer to the length remaining. It should fill in * the buffer with the escaped sequence (up to the remaining length * limit), and adjust the buffer pointer and length for the space * consumed. The return value is the full length required for the * complete escape sequence, even if there's not enough space in the * buffer to hold that many characters. */ virtual size_t map_utf8z_esc(char *dest, size_t dest_len, utf8_ptr src, size_t (*esc_fn)(wchar_t, char **, size_t *)) const; /* * ready-made callback for map_utf8z_esc() - map to unicode 'backslash' * escape sequences ('\u1234'), as we'd use in tads source code */ static size_t source_esc_cb(wchar_t ch, char **dest, size_t *len); /* * Write data to a file, converting from UTF-8 to the local character * set. Returns zero on success, non-zero if an error occurs writing * the data. */ int write_file(class CVmDataSource *fp, const char *buf, size_t bufl); /* * determine if the given Unicode character has a mapping to the local * character set */ virtual int is_mappable(wchar_t unicode_char) const { /* * By default, it's mappable if it has a non-default mapping in * the translation table. The default mapping is always at offset * zero in the translation table. */ return (get_mapping(unicode_char) != 0); } /* * Get the display expansion for a unicode character. This returns a * pointer to an array of wchar_t characters, and fills in the length * variable. Returns null if there's no expansion. * * An "expansion" is a list of two or more unicode characters that * should be substituted for the given unicode character when the * character is displayed. Display expansions are normally used for * visual approximations when the local character set doesn't contain * an exact match for the unicode character; for example, an ASCII * mapping might use the expansion "(c)" to represent the copyright * circled-C symbol, or the two-character sequence "AE" to represent * the AE ligature. */ const wchar_t *get_expansion(wchar_t unicode_char, size_t *len) { size_t ofs; const wchar_t *map; /* get the mapping offset in the expansion array */ ofs = get_exp_mapping(unicode_char); /* if the mapping offset is zero, it means there's no mapping */ if (ofs == 0) { /* indicate that there's no mapping by returning null */ *len = 0; return 0; } /* get the mapping pointer */ map = get_exp_ptr(ofs); /* read the length and skip it */ *len = (size_t)*map++; /* return the pointer to the first character of the expansion */ return map; } protected: /* delete the mapping */ virtual ~CCharmapToLocal(); /* given a Unicode character, get the mapping for the character */ unsigned int get_mapping(wchar_t unicode_char) const { unsigned int *subtable; /* get the mapping table */ subtable = get_sub_table(unicode_char); /* * If there is no subtable, return the default character, which is * always at offset zero in the translation array; otherwise, use * the low-order 8 bits of the character code as the index into * the subtable and return the value we find there */ if (subtable == 0) return 0; else return subtable[unicode_char & 0xff]; } /* given a Unicode character, get the expansion for the character */ unsigned int get_exp_mapping(wchar_t unicode_char) const { unsigned int *subtable; /* get the mapping table */ subtable = get_exp_sub_table(unicode_char); /* * if there's no subtable, return zero to indicate there's no * expansion; otherwise, return the entry from the subtable */ return (subtable == 0 ? 0 : subtable[unicode_char & 0xff]); } /* * Get a pointer to the sequence of bytes in the translation array at * a given offset */ const unsigned char *get_xlat_ptr(unsigned int ofs) const { return &xlat_array_[ofs]; } /* * Get a pointer to the translation of a character and the length in * bytes of the translation */ const unsigned char *get_xlation(wchar_t unicode_char, size_t *map_len) const { const unsigned char *map; /* get the translation offset */ map = get_xlat_ptr(get_mapping(unicode_char)); /* read the length and skip it in the table */ *map_len = (size_t)*map++; /* return the mapped byte sequence */ return map; } /* * get a pointer to the sequence of wchar_t values in the expansion * array at a given offset */ const wchar_t *get_exp_ptr(unsigned int ofs) const { return &exp_array_[ofs]; } /* load the mapping table from a file */ void load_table(osfildef *fp); /* * Given a Unicode character, get the sub-table for the character, * or null if there is no sub-table for this character. */ unsigned int *get_sub_table(wchar_t unicode_char) const { /* * use the high-order 8 bits of the unicode character as the * index into the master table */ return map_[(unicode_char >> 8) & 0xff]; } /* * Given a Unicode character, get the expansion sub-table for the * character. or null if there is no sub-table for the character. */ unsigned int *get_exp_sub_table(wchar_t unicode_char) const { /* * use the high-order 8 bits of the unicode character as the index * into the master table */ return exp_map_[(unicode_char >> 8) & 0xff]; } /* * Set a mapping. This allocates a new sub-table if necessary, and * stores the local character mapping in the table. */ void set_mapping(wchar_t unicode_char, unsigned int xlat_offset); /* set an expansion mapping */ void set_exp_mapping(wchar_t unicode_char, unsigned int exp_offset); /* * The master mapping table list. Each entry points to the * sub-array that contains the mapping for the 256 characters whose * high-order 8 bits give the index into this table. Each entry of * the subarray is the offset within the xlat_array_ byte array of * the first byte of the translation for the unicode character. */ unsigned int *map_[256]; /* * The master expansion mapping list. This works just like map_, but * points to exp_array_ entries for unicode display expansions. */ unsigned int *exp_map_[256]; /* * The translation array. This is an array of bytes containing the * translations. map_[high_8_bits][low_8_bits] contains the offset * within this array of the translation of the character with the * given code ((high_8_bits << 8) + low_8_bits). The first byte at * this offset is the length in bytes of the translation, not * counting the length byte. The remaining bytes are the bytes of * the translation for the character. */ unsigned char *xlat_array_; /* size of the translation array */ size_t xlat_array_size_; /* * The expansion array. This is an array of unicode characters * containing the expansions for displaying unicode characters. This * works just like xlat_array_: each entry in expmap_ is an index into * this array, which gives the starting point in the array of the run * of entries for the expansion of that character. The first character * of a run is a length prefix giving the number of characters in the * expansion. */ wchar_t *exp_array_; }; /* ======================================================================== */ /* * Local character set - to - Unicode UTF-8 mappers */ /* ------------------------------------------------------------------------ */ /* * Trival UTF8-to-UTF8 mapper - performs no conversions. This can be * used when reading from an external data source that is itself in * UTF-8 format; since this is identical to the format we use * internally, no mapping is required. */ class CCharmapToUniUTF8: public CCharmapToUni { public: /* read from a file */ virtual size_t read_file(class CVmDataSource *fp, char *buf, size_t bufl); /* determine if a byte sequence forms a complete character */ virtual int is_complete_char(const char *p, size_t len) const { /* * For UTF-8, we can infer the byte length of a character from the * first byte of the sequence. If the given length is at least the * inferred byte length, we have a complete character. */ return (len >= utf8_ptr::s_charsize(*p)); } /* map one character */ virtual int mapchar(wchar_t &ch, const char *&p, size_t &len) { /* make sure we have a complete character */ size_t clen = utf8_ptr::s_charsize(*p); if (len >= clen) { /* read the character */ ch = utf8_ptr::s_getch(p); /* advance the input pointers */ p += clen; len -= clen; /* success */ return TRUE; } else { /* incomplete character */ return FALSE; } } /* map a string */ size_t map(char **output_ptr, size_t *output_buf_len, const char *input_ptr, size_t input_len) const { size_t partial_len; /* * do the full mapping, discarding the partial last character byte * length information */ return map2(output_ptr, output_buf_len, input_ptr, input_len, &partial_len); } /* map a string, providing partial character info */ virtual size_t map2(char **output_ptr, size_t *output_buf_len, const char *input_ptr, size_t input_len, size_t *partial_len) const; protected: /* we don't need a mapping table - ignore any that is set */ virtual void set_mapping(wchar_t, wchar_t) { } }; /* ------------------------------------------------------------------------ */ /* * Character mapper base class for UCS-2 to UTF-8. We will subclass * this mapper for big-endian and little-endian UCS-2 representations, * but both mappers are essentially the same in that only format * translation is required, since UCS-2 and UTF-8 use the same code * point mapping (i.e., Unicode). */ class CCharmapToUniUcs2: public CCharmapToUni { public: /* read from a file */ virtual size_t read_file(class CVmDataSource *fp, char *buf, size_t bufl); /* determine if a byte sequence forms a complete character */ virtual int is_complete_char(const char *, size_t len) const { /* every character in UCS-2 requires two bytes */ return (len >= 2); } /* map a string, providing partial character info */ virtual size_t map2(char **output_ptr, size_t *output_buf_len, const char *input_ptr, size_t input_len, size_t *partial_len) const { /* * if the input length is odd, there's one byte of partial * character information at the end of the buffer; otherwise * everything is valid */ *partial_len = (input_len & 1); /* perform the usual mapping */ return map(output_ptr, output_buf_len, input_ptr, input_len); } protected: /* * there's no mapping table for UCS-2 translations, so we don't need * to do anything with mappings */ virtual void set_mapping(wchar_t, wchar_t) { } /* temporary buffer for reading files */ char inbuf_[512]; }; /* ------------------------------------------------------------------------ */ /* * Character mapper for UCS-2 little-endian to UTF-8 */ class CCharmapToUniUcs2Little: public CCharmapToUniUcs2 { public: /* map a string */ size_t map(char **output_ptr, size_t *output_buf_len, const char *input_ptr, size_t input_len) const; /* map one character */ virtual int mapchar(wchar_t &ch, const char *&p, size_t &len) { if (len >= 2) { /* map the character */ ch = (unsigned char)*p++; ch |= ((unsigned char)*p++) << 8; /* deduct it from the length */ len -= 2; /* success */ return TRUE; } else return FALSE; } }; /* ------------------------------------------------------------------------ */ /* * Character mapper for UCS-2 big-endian to UTF-8 */ class CCharmapToUniUcs2Big: public CCharmapToUniUcs2 { public: /* map a string */ size_t map(char **output_ptr, size_t *output_buf_len, const char *input_ptr, size_t input_len) const; /* map one character */ virtual int mapchar(wchar_t &ch, const char *&p, size_t &len) { if (len >= 2) { /* map the character */ ch = ((unsigned char)*p++) << 8; ch |= (unsigned char)*p++; /* deduct it from the length */ len -= 2; /* success */ return TRUE; } else return FALSE; } }; /* ------------------------------------------------------------------------ */ /* * Basic character mapper for single-byte character sets to UTF-8 */ class CCharmapToUniSB_basic: public CCharmapToUni { public: /* read from a single-byte input file, translating to UTF-8 */ virtual size_t read_file(class CVmDataSource *fp, char *buf, size_t bufl); /* determine if a byte sequence forms a complete character */ virtual int is_complete_char(const char *, size_t) const { /* * every character in a single-byte set requires just one byte; * since 'len' is required to be at least one, there's no way we * can't have a complete character */ return TRUE; } /* map a string, providing partial character info */ virtual size_t map2(char **output_ptr, size_t *output_buf_len, const char *input_ptr, size_t input_len, size_t *partial_len) const { /* * for all single-byte character sets, one byte == one character, * so it's impossible to have partial characters */ *partial_len = 0; /* perform the normal mapping */ return map(output_ptr, output_buf_len, input_ptr, input_len); } protected: /* temporary buffer for reading files */ char inbuf_[512]; }; /* ------------------------------------------------------------------------ */ /* * Character mapper for plain ASCII to UTF-8 */ class CCharmapToUniASCII: public CCharmapToUniSB_basic { public: /* map a string */ size_t map(char **output_ptr, size_t *output_buf_len, const char *input_ptr, size_t input_len) const; /* map one character */ virtual int mapchar(wchar_t &ch, const char *&p, size_t &len) { if (len >= 1) { /* map the character */ ch = (unsigned char)*p++; /* substitude U+FFFD for any invalid character */ if (ch > 127) ch = 0xFFFD; /* deduct it from the length */ len -= 1; /* success */ return TRUE; } else return FALSE; } protected: /* * there's no map for the ASCII translation, so we can ignore * mapping calls */ void set_mapping(wchar_t, wchar_t) { } }; /* ------------------------------------------------------------------------ */ /* * Character mapper for single-byte character sets to UTF-8. */ class CCharmapToUniSB: public CCharmapToUniSB_basic { public: CCharmapToUniSB() { int i; /* initialize the mapping table to all U+FFFD */ for (i = 0 ; i < 256 ; ++i) map_[i] = 0xFFFD; } /* map a string */ size_t map(char **output_ptr, size_t *output_buf_len, const char *input_ptr, size_t input_len) const; /* map one character */ virtual int mapchar(wchar_t &ch, const char *&p, size_t &len) { if (len >= 1) { /* map the character */ ch = map_[(unsigned char)*p++]; /* deduct it from the length */ len -= 1; /* success */ return TRUE; } else return FALSE; } protected: /* set a mapping */ void set_mapping(wchar_t uni_code_pt, wchar_t local_code_pt) { /* * set the mapping, ignoring characters outside of our 8-bit * range */ if (((unsigned int)local_code_pt) < 256) map_[local_code_pt] = uni_code_pt; } private: /* * our mapping table - since the source character set is * single-byte, we need only store a wchar_t for each of the * possible 256 source characters */ wchar_t map_[256]; }; /* ------------------------------------------------------------------------ */ /* * Character mapper for mixed multi-byte character sets to UTF-8. This * maps from local character sets that use a mixture of one-byte and * two-byte sequences to represent characters. */ /* * Primary-byte mapping table entry. This gives us mapping instructions * for each leading byte of a character sequence. * * Each character is represented by a one-byte or two-byte sequence. This * mapper assumes a context-free mapping, hence for each character * represented by a single byte, that single byte unambiguously indicates * that character, and hence is never the first byte of a two-byte * sequence. For each character represented by a two-byte sequence, the * first byte of the sequence can only be part of two-byte sequences, hence * whenever we see that first byte we'll know for sure we have a two-byte * character. * * Each mapping here is for a first byte. If the byte is a single-byte * character, then the 'sub' pointer is null and the 'ch' entry gives the * Unicode code point for the character. If the byte is the lead byte of * one or more two-byte characters, then the 'sub' pointer is non-null and * 'ch' is ignored. */ struct cmap_mb_entry { /* * The sub-mapping table. This is a pointer to a table of the Unicode * code points of the two-byte sequences that start with this byte. * Each entry in the array is a Unicode code point, and the array is * indexed by the second byte of the two-byte sequence. If this * pointer is null, then this lead byte is a single-byte character. * * Note that this pointer, if non-null, always points to a 256-element * array. This array can thus be indexed directly with any unsigned * 8-bit byte value without any range checking. */ wchar_t *sub; /* * The Unicode code point of this character, if this primary byte is a * one-byte character. */ wchar_t ch; }; /* * The multi-byte-to-UTF8 mapper */ class CCharmapToUniMB: public CCharmapToUni { public: CCharmapToUniMB(); /* delete the table */ virtual ~CCharmapToUniMB(); /* determine if a byte sequence forms a complete character */ virtual int is_complete_char(const char *p, size_t len) const { /* * Check the first byte to see if this is a leading byte or a * stand-alone single byte. */ if (map_[(unsigned char)*p].sub == 0) { /* * it's a stand-alone byte, so the character length is one; * 'len' is required to be at least 1, so we definitely have a * complete character */ return TRUE; } else { /* it's a lead byte, so the character length is two */ return (len >= 2); } } /* map one character */ virtual int mapchar(wchar_t &ch, const char *&p, size_t &len) { /* get the first byte's translation entry */ cmap_mb_entry e = map_[(unsigned char)*p]; /* check if we have a single-byte or double-byte character */ if (e.sub != 0) { /* double-byte character */ if (len >= 2) { /* get the second character mapping */ ch = e.sub[(unsigned char)*++p]; /* skip the two bytes */ p += 1; len -= 2; /* success */ return TRUE; } } else { /* single-byte character */ if (len >= 1) { /* get the mapping */ ch = e.ch; /* skip the character */ p += 1; len -= 1; /* success */ return TRUE; } } /* if we made it this far, we don't have a complete character */ return FALSE; } /* read from a multi-byte input file, translating to UTF-8 */ virtual size_t read_file(class CVmDataSource *fp, char *buf, size_t bufl); /* map a string */ size_t map(char **output_ptr, size_t *output_buf_len, const char *input_ptr, size_t input_len) const { size_t partial_len; /* * do the full mapping, discarding the partial last character byte * length information */ return map2(output_ptr, output_buf_len, input_ptr, input_len, &partial_len); } /* map a string, providing partial character info */ virtual size_t map2(char **output_ptr, size_t *output_buf_len, const char *input_ptr, size_t input_len, size_t *partial_len) const; protected: /* set a mapping */ void set_mapping(wchar_t uni_code_pt, wchar_t local_code_pt); private: /* the primary-byte mapping table */ cmap_mb_entry map_[256]; /* temporary buffer for reading files */ char inbuf_[512]; }; /* ------------------------------------------------------------------------ */ /* * Character mapper for double-byte character sets to UTF-8. This maps * from local character sets that use a two-byte sequence to represent each * local character. * * For now, this is a trivial subclass of the multi-byte mapper; that * mapper handles the more general case where each character can have a * different byte length, so it readily handles the case where every * character is two bytes. If there's ever any demand for it, a * special-case subclass to handle double-byte character sets specifically * could provide slight efficiency gains, since it wouldn't have to check * each lead byte to determine the character sequence length. I don't * expect this will be needed, though: there just aren't many fixed * double-byte character sets in use (apart from the UCS2 sets, which we * already have special handling for), and there's little chance of new * ones arising in the future since Unicode is rapidly making the whole * idea of vendor character sets obsolete. */ class CCharmapToUniDB: public CCharmapToUniMB { public: }; /* ------------------------------------------------------------------------ */ /* * Character mapper for plain ISO-8859-1 to UTF-8 */ class CCharmapToUni8859_1: public CCharmapToUniSB { public: /* creation */ CCharmapToUni8859_1() { wchar_t i; /* * Initialize our mapping table. Each 8859-1 code point maps to * the same code point in Unicode, so this is a trivial * translation. */ for (i = 0 ; i < 256 ; ++i) set_mapping(i, i); } }; /* ======================================================================== */ /* * Unicode UTF-8 - to - local character set mappers */ /* ------------------------------------------------------------------------ */ /* * Trivial character mapper for UTF8-to-UTF8 conversions. This can be * used when writing external data in UTF8 format; since this is the * same format we use internally, no conversion is required. */ class CCharmapToLocalUTF8: public CCharmapToLocal { public: /* map a string */ virtual size_t map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const; /* map a null-terminated string */ virtual size_t map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const; /* map a character */ size_t map(wchar_t unicode_char, char **output_ptr, size_t *output_len) const; /* * determine if the given Unicode character has a mapping to the local * character set */ virtual int is_mappable(wchar_t unicode_char) const { /* every character can be mapped UTF8-to-UTF8, obviously */ return TRUE; } }; /* ------------------------------------------------------------------------ */ /* * Character mapper for single-byte character sets. Each character in * the local (output) character set is represented by a single byte. */ class CCharmapToLocalSB: public CCharmapToLocal { public: /* map a string */ virtual size_t map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const; /* map a null-terminated string */ virtual size_t map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const; /* map a character */ size_t map(wchar_t unicode_char, char **output_ptr, size_t *output_len) const; }; /* ------------------------------------------------------------------------ */ /* * Mixed multi-byte mapper. Each local character is represented by a * sequence of one or more bytes. * * This class is a trivial subclass of CCharmapToLocalSB. The single-byte * base class already does everything we need to do, because it is designed * to cope with mappings that involve expansions that represent a single * Unicode character with a sequence of local characters (for example, * "(c)" for the copyright symbol). */ class CCharmapToLocalMB: public CCharmapToLocalSB { public: }; /* * Double-byte mapper. Each local character is represented by exactly two * bytes. This class is a trivial subclass of CCharmapToLocalMB, because * the multi-byte mapper already handles the more general case of local * character representations that use varying byte lengths; there is no * particular efficiency gain to be had by creating a separate special-case * class for double-byte character sets. */ class CCharmapToLocalDB: public CCharmapToLocalMB { public: }; /* ------------------------------------------------------------------------ */ /* * Character mapper for mapping to local default 7-bit ASCII. This * mapper is has a built-in character set translation so that we can * always create one without having to find an external mapping file. */ class CCharmapToLocalASCII: public CCharmapToLocalSB { public: CCharmapToLocalASCII(); }; /* * Character mapper for mapping to local ISO-8859-1. This mapper has a * built-in character set translation so that we can always create one * even without an external mapping file. */ class CCharmapToLocal8859_1: public CCharmapToLocalSB { public: CCharmapToLocal8859_1(); }; /* ------------------------------------------------------------------------ */ /* * Character mapper for 16-bit Wide Unicode local character set. Stores * characters in the correct local wchar_t representation. Assumes that * the pointer is wchar_t-aligned. * * This is a trival translation. Because we're mapping from Unicode to * Unicode, the only thing we're changing is the encoding format - the * character code is simply copied without any translation, since * Unicode is the same everywhere. */ class CCharmapToLocalWideUnicode: public CCharmapToLocal { public: /* map a string */ virtual size_t map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const; /* map a null-terminated string */ virtual size_t map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const; /* map a character */ size_t map(wchar_t unicode_char, char **output_ptr, size_t *output_len) const; /* * determine if the given Unicode character has a mapping to the local * character set */ virtual int is_mappable(wchar_t unicode_char) const { /* every character can be mapped UTF8-to-UCS2 */ return TRUE; } }; /* ------------------------------------------------------------------------ */ /* * Character mapper for 16-bit Wide Unicode, big-endian. Stores the * characters in big-endian UCS-2 representation. */ class CCharmapToLocalUcs2Big: public CCharmapToLocal { public: /* map a string */ virtual size_t map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const; /* map a null-terminated string */ virtual size_t map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const; /* map a character */ size_t map(wchar_t unicode_char, char **output_ptr, size_t *output_len) const; }; /* ------------------------------------------------------------------------ */ /* * Character mapper for 16-bit Wide Unicode, little-endian. Stores the * characters in little-endian UCS-2 representation. */ class CCharmapToLocalUcs2Little: public CCharmapToLocal { public: /* map a string */ virtual size_t map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const; /* map a null-terminated string */ virtual size_t map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const; /* map a character */ size_t map(wchar_t unicode_char, char **output_ptr, size_t *output_len) const; }; #endif /* CHARMAP_H */ qtads-2.1.7/tads3/derived/000077500000000000000000000000001265017072300153005ustar00rootroot00000000000000qtads-2.1.7/tads3/derived/vmuni_cs.cpp000066400000000000000000167314771265017072300176610ustar00rootroot00000000000000/* Copyright (c) 1999, 2012 Michael J. Roberts */ /* * TADS 3 Case Conversion Table * * THIS IS A GENERATED FILE. DO NOT EDIT - any changes will * be lost on the next build. * * This file is mechanically derived from the Unicode * character database listing. */ #include #include "vmuni.h" static const unsigned char chartype[32768] = { (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0001 | 0000 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0003 | 0002 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0005 | 0004 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0007 | 0006 */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_OTHER, /* 0009 | 0008 */ (T3_CTYPE_VSPAC << 4) | T3_CTYPE_VSPAC, /* 000b | 000a */ (T3_CTYPE_VSPAC << 4) | T3_CTYPE_SPACE, /* 000d | 000c */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 000f | 000e */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0011 | 0010 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0013 | 0012 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0015 | 0014 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0017 | 0016 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0019 | 0018 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 001b | 001a */ (T3_CTYPE_VSPAC << 4) | T3_CTYPE_VSPAC, /* 001d | 001c */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_VSPAC, /* 001f | 001e */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_SPACE, /* 0021 EXCLAMATION MARK | 0020 SPACE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0023 NUMBER SIGN | 0022 QUOTATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 0025 PERCENT SIGN | 0024 DOLLAR SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0027 APOSTROPHE | 0026 AMPERSAND */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0029 RIGHT PARENTHESIS | 0028 LEFT PARENTHESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 002b PLUS SIGN | 002a ASTERISK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 002d HYPHEN-MINUS | 002c COMMA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 002f SOLIDUS | 002e FULL STOP */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0031 DIGIT ONE | 0030 DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0033 DIGIT THREE | 0032 DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0035 DIGIT FIVE | 0034 DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0037 DIGIT SEVEN | 0036 DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0039 DIGIT NINE | 0038 DIGIT EIGHT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 003b SEMICOLON | 003a COLON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 003d EQUALS SIGN | 003c LESS-THAN SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 003f QUESTION MARK | 003e GREATER-THAN SIGN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_PUNCT, /* 0041 LATIN CAPITAL LETTER A | 0040 COMMERCIAL AT */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0043 LATIN CAPITAL LETTER C | 0042 LATIN CAPITAL LETTER B */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0045 LATIN CAPITAL LETTER E | 0044 LATIN CAPITAL LETTER D */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0047 LATIN CAPITAL LETTER G | 0046 LATIN CAPITAL LETTER F */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0049 LATIN CAPITAL LETTER I | 0048 LATIN CAPITAL LETTER H */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 004b LATIN CAPITAL LETTER K | 004a LATIN CAPITAL LETTER J */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 004d LATIN CAPITAL LETTER M | 004c LATIN CAPITAL LETTER L */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 004f LATIN CAPITAL LETTER O | 004e LATIN CAPITAL LETTER N */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0051 LATIN CAPITAL LETTER Q | 0050 LATIN CAPITAL LETTER P */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0053 LATIN CAPITAL LETTER S | 0052 LATIN CAPITAL LETTER R */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0055 LATIN CAPITAL LETTER U | 0054 LATIN CAPITAL LETTER T */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0057 LATIN CAPITAL LETTER W | 0056 LATIN CAPITAL LETTER V */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0059 LATIN CAPITAL LETTER Y | 0058 LATIN CAPITAL LETTER X */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_UPPER, /* 005b LEFT SQUARE BRACKET | 005a LATIN CAPITAL LETTER Z */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 005d RIGHT SQUARE BRACKET | 005c REVERSE SOLIDUS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 005f LOW LINE | 005e CIRCUMFLEX ACCENT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_OTHER, /* 0061 LATIN SMALL LETTER A | 0060 GRAVE ACCENT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0063 LATIN SMALL LETTER C | 0062 LATIN SMALL LETTER B */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0065 LATIN SMALL LETTER E | 0064 LATIN SMALL LETTER D */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0067 LATIN SMALL LETTER G | 0066 LATIN SMALL LETTER F */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0069 LATIN SMALL LETTER I | 0068 LATIN SMALL LETTER H */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 006b LATIN SMALL LETTER K | 006a LATIN SMALL LETTER J */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 006d LATIN SMALL LETTER M | 006c LATIN SMALL LETTER L */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 006f LATIN SMALL LETTER O | 006e LATIN SMALL LETTER N */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0071 LATIN SMALL LETTER Q | 0070 LATIN SMALL LETTER P */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0073 LATIN SMALL LETTER S | 0072 LATIN SMALL LETTER R */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0075 LATIN SMALL LETTER U | 0074 LATIN SMALL LETTER T */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0077 LATIN SMALL LETTER W | 0076 LATIN SMALL LETTER V */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0079 LATIN SMALL LETTER Y | 0078 LATIN SMALL LETTER X */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_LOWER, /* 007b LEFT CURLY BRACKET | 007a LATIN SMALL LETTER Z */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 007d RIGHT CURLY BRACKET | 007c VERTICAL LINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 007f | 007e TILDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0081 | 0080 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0083 | 0082 */ (T3_CTYPE_VSPAC << 4) | T3_CTYPE_OTHER, /* 0085 | 0084 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0087 | 0086 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0089 | 0088 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 008b | 008a */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 008d | 008c */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 008f | 008e */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0091 | 0090 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0093 | 0092 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0095 | 0094 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0097 | 0096 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0099 | 0098 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 009b | 009a */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 009d | 009c */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 009f | 009e */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_SPACE, /* 00a1 INVERTED EXCLAMATION MARK | 00a0 NO-BREAK SPACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00a3 POUND SIGN | 00a2 CENT SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00a5 YEN SIGN | 00a4 CURRENCY SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 00a7 SECTION SIGN | 00a6 BROKEN BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00a9 COPYRIGHT SIGN | 00a8 DIAERESIS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 00ab LEFT-POINTING DOUBLE ANGLE QUOTATION MA | 00aa FEMININE ORDINAL INDICATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00ad SOFT HYPHEN | 00ac NOT SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00af MACRON | 00ae REGISTERED SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00b1 PLUS-MINUS SIGN | 00b0 DEGREE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00b3 SUPERSCRIPT THREE | 00b2 SUPERSCRIPT TWO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_OTHER, /* 00b5 MICRO SIGN | 00b4 ACUTE ACCENT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 00b7 MIDDLE DOT | 00b6 PILCROW SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00b9 SUPERSCRIPT ONE | 00b8 CEDILLA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 00bb RIGHT-POINTING DOUBLE ANGLE QUOTATION M | 00ba MASCULINE ORDINAL INDICATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00bd VULGAR FRACTION ONE HALF | 00bc VULGAR FRACTION ONE QUARTER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 00bf INVERTED QUESTION MARK | 00be VULGAR FRACTION THREE QUARTERS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00c1 LATIN CAPITAL LETTER A WITH ACUTE | 00c0 LATIN CAPITAL LETTER A WITH GRAVE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00c3 LATIN CAPITAL LETTER A WITH TILDE | 00c2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00c5 LATIN CAPITAL LETTER A WITH RING ABOVE | 00c4 LATIN CAPITAL LETTER A WITH DIAERESIS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00c7 LATIN CAPITAL LETTER C WITH CEDILLA | 00c6 LATIN CAPITAL LETTER AE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00c9 LATIN CAPITAL LETTER E WITH ACUTE | 00c8 LATIN CAPITAL LETTER E WITH GRAVE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00cb LATIN CAPITAL LETTER E WITH DIAERESIS | 00ca LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00cd LATIN CAPITAL LETTER I WITH ACUTE | 00cc LATIN CAPITAL LETTER I WITH GRAVE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00cf LATIN CAPITAL LETTER I WITH DIAERESIS | 00ce LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00d1 LATIN CAPITAL LETTER N WITH TILDE | 00d0 LATIN CAPITAL LETTER ETH */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00d3 LATIN CAPITAL LETTER O WITH ACUTE | 00d2 LATIN CAPITAL LETTER O WITH GRAVE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00d5 LATIN CAPITAL LETTER O WITH TILDE | 00d4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UPPER, /* 00d7 MULTIPLICATION SIGN | 00d6 LATIN CAPITAL LETTER O WITH DIAERESIS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00d9 LATIN CAPITAL LETTER U WITH GRAVE | 00d8 LATIN CAPITAL LETTER O WITH STROKE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00db LATIN CAPITAL LETTER U WITH CIRCUMFLEX | 00da LATIN CAPITAL LETTER U WITH ACUTE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00dd LATIN CAPITAL LETTER Y WITH ACUTE | 00dc LATIN CAPITAL LETTER U WITH DIAERESIS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 00df LATIN SMALL LETTER SHARP S | 00de LATIN CAPITAL LETTER THORN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00e1 LATIN SMALL LETTER A WITH ACUTE | 00e0 LATIN SMALL LETTER A WITH GRAVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00e3 LATIN SMALL LETTER A WITH TILDE | 00e2 LATIN SMALL LETTER A WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00e5 LATIN SMALL LETTER A WITH RING ABOVE | 00e4 LATIN SMALL LETTER A WITH DIAERESIS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00e7 LATIN SMALL LETTER C WITH CEDILLA | 00e6 LATIN SMALL LETTER AE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00e9 LATIN SMALL LETTER E WITH ACUTE | 00e8 LATIN SMALL LETTER E WITH GRAVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00eb LATIN SMALL LETTER E WITH DIAERESIS | 00ea LATIN SMALL LETTER E WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00ed LATIN SMALL LETTER I WITH ACUTE | 00ec LATIN SMALL LETTER I WITH GRAVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00ef LATIN SMALL LETTER I WITH DIAERESIS | 00ee LATIN SMALL LETTER I WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00f1 LATIN SMALL LETTER N WITH TILDE | 00f0 LATIN SMALL LETTER ETH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00f3 LATIN SMALL LETTER O WITH ACUTE | 00f2 LATIN SMALL LETTER O WITH GRAVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00f5 LATIN SMALL LETTER O WITH TILDE | 00f4 LATIN SMALL LETTER O WITH CIRCUMFLEX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_LOWER, /* 00f7 DIVISION SIGN | 00f6 LATIN SMALL LETTER O WITH DIAERESIS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00f9 LATIN SMALL LETTER U WITH GRAVE | 00f8 LATIN SMALL LETTER O WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00fb LATIN SMALL LETTER U WITH CIRCUMFLEX | 00fa LATIN SMALL LETTER U WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00fd LATIN SMALL LETTER Y WITH ACUTE | 00fc LATIN SMALL LETTER U WITH DIAERESIS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00ff LATIN SMALL LETTER Y WITH DIAERESIS | 00fe LATIN SMALL LETTER THORN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0101 LATIN SMALL LETTER A WITH MACRON | 0100 LATIN CAPITAL LETTER A WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0103 LATIN SMALL LETTER A WITH BREVE | 0102 LATIN CAPITAL LETTER A WITH BREVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0105 LATIN SMALL LETTER A WITH OGONEK | 0104 LATIN CAPITAL LETTER A WITH OGONEK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0107 LATIN SMALL LETTER C WITH ACUTE | 0106 LATIN CAPITAL LETTER C WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0109 LATIN SMALL LETTER C WITH CIRCUMFLEX | 0108 LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 010b LATIN SMALL LETTER C WITH DOT ABOVE | 010a LATIN CAPITAL LETTER C WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 010d LATIN SMALL LETTER C WITH CARON | 010c LATIN CAPITAL LETTER C WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 010f LATIN SMALL LETTER D WITH CARON | 010e LATIN CAPITAL LETTER D WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0111 LATIN SMALL LETTER D WITH STROKE | 0110 LATIN CAPITAL LETTER D WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0113 LATIN SMALL LETTER E WITH MACRON | 0112 LATIN CAPITAL LETTER E WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0115 LATIN SMALL LETTER E WITH BREVE | 0114 LATIN CAPITAL LETTER E WITH BREVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0117 LATIN SMALL LETTER E WITH DOT ABOVE | 0116 LATIN CAPITAL LETTER E WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0119 LATIN SMALL LETTER E WITH OGONEK | 0118 LATIN CAPITAL LETTER E WITH OGONEK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 011b LATIN SMALL LETTER E WITH CARON | 011a LATIN CAPITAL LETTER E WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 011d LATIN SMALL LETTER G WITH CIRCUMFLEX | 011c LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 011f LATIN SMALL LETTER G WITH BREVE | 011e LATIN CAPITAL LETTER G WITH BREVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0121 LATIN SMALL LETTER G WITH DOT ABOVE | 0120 LATIN CAPITAL LETTER G WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0123 LATIN SMALL LETTER G WITH CEDILLA | 0122 LATIN CAPITAL LETTER G WITH CEDILLA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0125 LATIN SMALL LETTER H WITH CIRCUMFLEX | 0124 LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0127 LATIN SMALL LETTER H WITH STROKE | 0126 LATIN CAPITAL LETTER H WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0129 LATIN SMALL LETTER I WITH TILDE | 0128 LATIN CAPITAL LETTER I WITH TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 012b LATIN SMALL LETTER I WITH MACRON | 012a LATIN CAPITAL LETTER I WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 012d LATIN SMALL LETTER I WITH BREVE | 012c LATIN CAPITAL LETTER I WITH BREVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 012f LATIN SMALL LETTER I WITH OGONEK | 012e LATIN CAPITAL LETTER I WITH OGONEK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0131 LATIN SMALL LETTER DOTLESS I | 0130 LATIN CAPITAL LETTER I WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0133 LATIN SMALL LIGATURE IJ | 0132 LATIN CAPITAL LIGATURE IJ */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0135 LATIN SMALL LETTER J WITH CIRCUMFLEX | 0134 LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0137 LATIN SMALL LETTER K WITH CEDILLA | 0136 LATIN CAPITAL LETTER K WITH CEDILLA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0139 LATIN CAPITAL LETTER L WITH ACUTE | 0138 LATIN SMALL LETTER KRA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 013b LATIN CAPITAL LETTER L WITH CEDILLA | 013a LATIN SMALL LETTER L WITH ACUTE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 013d LATIN CAPITAL LETTER L WITH CARON | 013c LATIN SMALL LETTER L WITH CEDILLA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 013f LATIN CAPITAL LETTER L WITH MIDDLE DOT | 013e LATIN SMALL LETTER L WITH CARON */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0141 LATIN CAPITAL LETTER L WITH STROKE | 0140 LATIN SMALL LETTER L WITH MIDDLE DOT */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0143 LATIN CAPITAL LETTER N WITH ACUTE | 0142 LATIN SMALL LETTER L WITH STROKE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0145 LATIN CAPITAL LETTER N WITH CEDILLA | 0144 LATIN SMALL LETTER N WITH ACUTE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0147 LATIN CAPITAL LETTER N WITH CARON | 0146 LATIN SMALL LETTER N WITH CEDILLA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0149 LATIN SMALL LETTER N PRECEDED BY APOSTR | 0148 LATIN SMALL LETTER N WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 014b LATIN SMALL LETTER ENG | 014a LATIN CAPITAL LETTER ENG */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 014d LATIN SMALL LETTER O WITH MACRON | 014c LATIN CAPITAL LETTER O WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 014f LATIN SMALL LETTER O WITH BREVE | 014e LATIN CAPITAL LETTER O WITH BREVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0151 LATIN SMALL LETTER O WITH DOUBLE ACUTE | 0150 LATIN CAPITAL LETTER O WITH DOUBLE ACUT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0153 LATIN SMALL LIGATURE OE | 0152 LATIN CAPITAL LIGATURE OE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0155 LATIN SMALL LETTER R WITH ACUTE | 0154 LATIN CAPITAL LETTER R WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0157 LATIN SMALL LETTER R WITH CEDILLA | 0156 LATIN CAPITAL LETTER R WITH CEDILLA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0159 LATIN SMALL LETTER R WITH CARON | 0158 LATIN CAPITAL LETTER R WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 015b LATIN SMALL LETTER S WITH ACUTE | 015a LATIN CAPITAL LETTER S WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 015d LATIN SMALL LETTER S WITH CIRCUMFLEX | 015c LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 015f LATIN SMALL LETTER S WITH CEDILLA | 015e LATIN CAPITAL LETTER S WITH CEDILLA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0161 LATIN SMALL LETTER S WITH CARON | 0160 LATIN CAPITAL LETTER S WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0163 LATIN SMALL LETTER T WITH CEDILLA | 0162 LATIN CAPITAL LETTER T WITH CEDILLA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0165 LATIN SMALL LETTER T WITH CARON | 0164 LATIN CAPITAL LETTER T WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0167 LATIN SMALL LETTER T WITH STROKE | 0166 LATIN CAPITAL LETTER T WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0169 LATIN SMALL LETTER U WITH TILDE | 0168 LATIN CAPITAL LETTER U WITH TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 016b LATIN SMALL LETTER U WITH MACRON | 016a LATIN CAPITAL LETTER U WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 016d LATIN SMALL LETTER U WITH BREVE | 016c LATIN CAPITAL LETTER U WITH BREVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 016f LATIN SMALL LETTER U WITH RING ABOVE | 016e LATIN CAPITAL LETTER U WITH RING ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0171 LATIN SMALL LETTER U WITH DOUBLE ACUTE | 0170 LATIN CAPITAL LETTER U WITH DOUBLE ACUT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0173 LATIN SMALL LETTER U WITH OGONEK | 0172 LATIN CAPITAL LETTER U WITH OGONEK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0175 LATIN SMALL LETTER W WITH CIRCUMFLEX | 0174 LATIN CAPITAL LETTER W WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0177 LATIN SMALL LETTER Y WITH CIRCUMFLEX | 0176 LATIN CAPITAL LETTER Y WITH CIRCUMFLEX */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0179 LATIN CAPITAL LETTER Z WITH ACUTE | 0178 LATIN CAPITAL LETTER Y WITH DIAERESIS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 017b LATIN CAPITAL LETTER Z WITH DOT ABOVE | 017a LATIN SMALL LETTER Z WITH ACUTE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 017d LATIN CAPITAL LETTER Z WITH CARON | 017c LATIN SMALL LETTER Z WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 017f LATIN SMALL LETTER LONG S | 017e LATIN SMALL LETTER Z WITH CARON */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0181 LATIN CAPITAL LETTER B WITH HOOK | 0180 LATIN SMALL LETTER B WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0183 LATIN SMALL LETTER B WITH TOPBAR | 0182 LATIN CAPITAL LETTER B WITH TOPBAR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0185 LATIN SMALL LETTER TONE SIX | 0184 LATIN CAPITAL LETTER TONE SIX */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0187 LATIN CAPITAL LETTER C WITH HOOK | 0186 LATIN CAPITAL LETTER OPEN O */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0189 LATIN CAPITAL LETTER AFRICAN D | 0188 LATIN SMALL LETTER C WITH HOOK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 018b LATIN CAPITAL LETTER D WITH TOPBAR | 018a LATIN CAPITAL LETTER D WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 018d LATIN SMALL LETTER TURNED DELTA | 018c LATIN SMALL LETTER D WITH TOPBAR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 018f LATIN CAPITAL LETTER SCHWA | 018e LATIN CAPITAL LETTER REVERSED E */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0191 LATIN CAPITAL LETTER F WITH HOOK | 0190 LATIN CAPITAL LETTER OPEN E */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0193 LATIN CAPITAL LETTER G WITH HOOK | 0192 LATIN SMALL LETTER F WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0195 LATIN SMALL LETTER HV | 0194 LATIN CAPITAL LETTER GAMMA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0197 LATIN CAPITAL LETTER I WITH STROKE | 0196 LATIN CAPITAL LETTER IOTA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0199 LATIN SMALL LETTER K WITH HOOK | 0198 LATIN CAPITAL LETTER K WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 019b LATIN SMALL LETTER LAMBDA WITH STROKE | 019a LATIN SMALL LETTER L WITH BAR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 019d LATIN CAPITAL LETTER N WITH LEFT HOOK | 019c LATIN CAPITAL LETTER TURNED M */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 019f LATIN CAPITAL LETTER O WITH MIDDLE TILD | 019e LATIN SMALL LETTER N WITH LONG RIGHT LE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01a1 LATIN SMALL LETTER O WITH HORN | 01a0 LATIN CAPITAL LETTER O WITH HORN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01a3 LATIN SMALL LETTER OI | 01a2 LATIN CAPITAL LETTER OI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01a5 LATIN SMALL LETTER P WITH HOOK | 01a4 LATIN CAPITAL LETTER P WITH HOOK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 01a7 LATIN CAPITAL LETTER TONE TWO | 01a6 LATIN LETTER YR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01a9 LATIN CAPITAL LETTER ESH | 01a8 LATIN SMALL LETTER TONE TWO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 01ab LATIN SMALL LETTER T WITH PALATAL HOOK | 01aa LATIN LETTER REVERSED ESH LOOP */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01ad LATIN SMALL LETTER T WITH HOOK | 01ac LATIN CAPITAL LETTER T WITH HOOK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 01af LATIN CAPITAL LETTER U WITH HORN | 01ae LATIN CAPITAL LETTER T WITH RETROFLEX H */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01b1 LATIN CAPITAL LETTER UPSILON | 01b0 LATIN SMALL LETTER U WITH HORN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 01b3 LATIN CAPITAL LETTER Y WITH HOOK | 01b2 LATIN CAPITAL LETTER V WITH HOOK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01b5 LATIN CAPITAL LETTER Z WITH STROKE | 01b4 LATIN SMALL LETTER Y WITH HOOK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01b7 LATIN CAPITAL LETTER EZH | 01b6 LATIN SMALL LETTER Z WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01b9 LATIN SMALL LETTER EZH REVERSED | 01b8 LATIN CAPITAL LETTER EZH REVERSED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_LOWER, /* 01bb LATIN LETTER TWO WITH STROKE | 01ba LATIN SMALL LETTER EZH WITH TAIL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01bd LATIN SMALL LETTER TONE FIVE | 01bc LATIN CAPITAL LETTER TONE FIVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 01bf LATIN LETTER WYNN | 01be LATIN LETTER INVERTED GLOTTAL STOP WITH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 01c1 LATIN LETTER LATERAL CLICK | 01c0 LATIN LETTER DENTAL CLICK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 01c3 LATIN LETTER RETROFLEX CLICK | 01c2 LATIN LETTER ALVEOLAR CLICK */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_UPPER, /* 01c5 LATIN CAPITAL LETTER D WITH SMALL LETTE | 01c4 LATIN CAPITAL LETTER DZ WITH CARON */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01c7 LATIN CAPITAL LETTER LJ | 01c6 LATIN SMALL LETTER DZ WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_TITLE, /* 01c9 LATIN SMALL LETTER LJ | 01c8 LATIN CAPITAL LETTER L WITH SMALL LETTE */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_UPPER, /* 01cb LATIN CAPITAL LETTER N WITH SMALL LETTE | 01ca LATIN CAPITAL LETTER NJ */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01cd LATIN CAPITAL LETTER A WITH CARON | 01cc LATIN SMALL LETTER NJ */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01cf LATIN CAPITAL LETTER I WITH CARON | 01ce LATIN SMALL LETTER A WITH CARON */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01d1 LATIN CAPITAL LETTER O WITH CARON | 01d0 LATIN SMALL LETTER I WITH CARON */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01d3 LATIN CAPITAL LETTER U WITH CARON | 01d2 LATIN SMALL LETTER O WITH CARON */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01d5 LATIN CAPITAL LETTER U WITH DIAERESIS A | 01d4 LATIN SMALL LETTER U WITH CARON */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01d7 LATIN CAPITAL LETTER U WITH DIAERESIS A | 01d6 LATIN SMALL LETTER U WITH DIAERESIS AND */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01d9 LATIN CAPITAL LETTER U WITH DIAERESIS A | 01d8 LATIN SMALL LETTER U WITH DIAERESIS AND */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01db LATIN CAPITAL LETTER U WITH DIAERESIS A | 01da LATIN SMALL LETTER U WITH DIAERESIS AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 01dd LATIN SMALL LETTER TURNED E | 01dc LATIN SMALL LETTER U WITH DIAERESIS AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01df LATIN SMALL LETTER A WITH DIAERESIS AND | 01de LATIN CAPITAL LETTER A WITH DIAERESIS A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01e1 LATIN SMALL LETTER A WITH DOT ABOVE AND | 01e0 LATIN CAPITAL LETTER A WITH DOT ABOVE A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01e3 LATIN SMALL LETTER AE WITH MACRON | 01e2 LATIN CAPITAL LETTER AE WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01e5 LATIN SMALL LETTER G WITH STROKE | 01e4 LATIN CAPITAL LETTER G WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01e7 LATIN SMALL LETTER G WITH CARON | 01e6 LATIN CAPITAL LETTER G WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01e9 LATIN SMALL LETTER K WITH CARON | 01e8 LATIN CAPITAL LETTER K WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01eb LATIN SMALL LETTER O WITH OGONEK | 01ea LATIN CAPITAL LETTER O WITH OGONEK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01ed LATIN SMALL LETTER O WITH OGONEK AND MA | 01ec LATIN CAPITAL LETTER O WITH OGONEK AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01ef LATIN SMALL LETTER EZH WITH CARON | 01ee LATIN CAPITAL LETTER EZH WITH CARON */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01f1 LATIN CAPITAL LETTER DZ | 01f0 LATIN SMALL LETTER J WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_TITLE, /* 01f3 LATIN SMALL LETTER DZ | 01f2 LATIN CAPITAL LETTER D WITH SMALL LETTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01f5 LATIN SMALL LETTER G WITH ACUTE | 01f4 LATIN CAPITAL LETTER G WITH ACUTE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 01f7 LATIN CAPITAL LETTER WYNN | 01f6 LATIN CAPITAL LETTER HWAIR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01f9 LATIN SMALL LETTER N WITH GRAVE | 01f8 LATIN CAPITAL LETTER N WITH GRAVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01fb LATIN SMALL LETTER A WITH RING ABOVE AN | 01fa LATIN CAPITAL LETTER A WITH RING ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01fd LATIN SMALL LETTER AE WITH ACUTE | 01fc LATIN CAPITAL LETTER AE WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01ff LATIN SMALL LETTER O WITH STROKE AND AC | 01fe LATIN CAPITAL LETTER O WITH STROKE AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0201 LATIN SMALL LETTER A WITH DOUBLE GRAVE | 0200 LATIN CAPITAL LETTER A WITH DOUBLE GRAV */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0203 LATIN SMALL LETTER A WITH INVERTED BREV | 0202 LATIN CAPITAL LETTER A WITH INVERTED BR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0205 LATIN SMALL LETTER E WITH DOUBLE GRAVE | 0204 LATIN CAPITAL LETTER E WITH DOUBLE GRAV */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0207 LATIN SMALL LETTER E WITH INVERTED BREV | 0206 LATIN CAPITAL LETTER E WITH INVERTED BR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0209 LATIN SMALL LETTER I WITH DOUBLE GRAVE | 0208 LATIN CAPITAL LETTER I WITH DOUBLE GRAV */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 020b LATIN SMALL LETTER I WITH INVERTED BREV | 020a LATIN CAPITAL LETTER I WITH INVERTED BR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 020d LATIN SMALL LETTER O WITH DOUBLE GRAVE | 020c LATIN CAPITAL LETTER O WITH DOUBLE GRAV */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 020f LATIN SMALL LETTER O WITH INVERTED BREV | 020e LATIN CAPITAL LETTER O WITH INVERTED BR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0211 LATIN SMALL LETTER R WITH DOUBLE GRAVE | 0210 LATIN CAPITAL LETTER R WITH DOUBLE GRAV */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0213 LATIN SMALL LETTER R WITH INVERTED BREV | 0212 LATIN CAPITAL LETTER R WITH INVERTED BR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0215 LATIN SMALL LETTER U WITH DOUBLE GRAVE | 0214 LATIN CAPITAL LETTER U WITH DOUBLE GRAV */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0217 LATIN SMALL LETTER U WITH INVERTED BREV | 0216 LATIN CAPITAL LETTER U WITH INVERTED BR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0219 LATIN SMALL LETTER S WITH COMMA BELOW | 0218 LATIN CAPITAL LETTER S WITH COMMA BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 021b LATIN SMALL LETTER T WITH COMMA BELOW | 021a LATIN CAPITAL LETTER T WITH COMMA BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 021d LATIN SMALL LETTER YOGH | 021c LATIN CAPITAL LETTER YOGH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 021f LATIN SMALL LETTER H WITH CARON | 021e LATIN CAPITAL LETTER H WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0221 LATIN SMALL LETTER D WITH CURL | 0220 LATIN CAPITAL LETTER N WITH LONG RIGHT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0223 LATIN SMALL LETTER OU | 0222 LATIN CAPITAL LETTER OU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0225 LATIN SMALL LETTER Z WITH HOOK | 0224 LATIN CAPITAL LETTER Z WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0227 LATIN SMALL LETTER A WITH DOT ABOVE | 0226 LATIN CAPITAL LETTER A WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0229 LATIN SMALL LETTER E WITH CEDILLA | 0228 LATIN CAPITAL LETTER E WITH CEDILLA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 022b LATIN SMALL LETTER O WITH DIAERESIS AND | 022a LATIN CAPITAL LETTER O WITH DIAERESIS A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 022d LATIN SMALL LETTER O WITH TILDE AND MAC | 022c LATIN CAPITAL LETTER O WITH TILDE AND M */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 022f LATIN SMALL LETTER O WITH DOT ABOVE | 022e LATIN CAPITAL LETTER O WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0231 LATIN SMALL LETTER O WITH DOT ABOVE AND | 0230 LATIN CAPITAL LETTER O WITH DOT ABOVE A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0233 LATIN SMALL LETTER Y WITH MACRON | 0232 LATIN CAPITAL LETTER Y WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0235 LATIN SMALL LETTER N WITH CURL | 0234 LATIN SMALL LETTER L WITH CURL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0237 LATIN SMALL LETTER DOTLESS J | 0236 LATIN SMALL LETTER T WITH CURL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0239 LATIN SMALL LETTER QP DIGRAPH | 0238 LATIN SMALL LETTER DB DIGRAPH */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 023b LATIN CAPITAL LETTER C WITH STROKE | 023a LATIN CAPITAL LETTER A WITH STROKE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 023d LATIN CAPITAL LETTER L WITH BAR | 023c LATIN SMALL LETTER C WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 023f LATIN SMALL LETTER S WITH SWASH TAIL | 023e LATIN CAPITAL LETTER T WITH DIAGONAL ST */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0241 LATIN CAPITAL LETTER GLOTTAL STOP | 0240 LATIN SMALL LETTER Z WITH SWASH TAIL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0243 LATIN CAPITAL LETTER B WITH STROKE | 0242 LATIN SMALL LETTER GLOTTAL STOP */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0245 LATIN CAPITAL LETTER TURNED V | 0244 LATIN CAPITAL LETTER U BAR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0247 LATIN SMALL LETTER E WITH STROKE | 0246 LATIN CAPITAL LETTER E WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0249 LATIN SMALL LETTER J WITH STROKE | 0248 LATIN CAPITAL LETTER J WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 024b LATIN SMALL LETTER Q WITH HOOK TAIL | 024a LATIN CAPITAL LETTER SMALL Q WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 024d LATIN SMALL LETTER R WITH STROKE | 024c LATIN CAPITAL LETTER R WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 024f LATIN SMALL LETTER Y WITH STROKE | 024e LATIN CAPITAL LETTER Y WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0251 LATIN SMALL LETTER ALPHA | 0250 LATIN SMALL LETTER TURNED A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0253 LATIN SMALL LETTER B WITH HOOK | 0252 LATIN SMALL LETTER TURNED ALPHA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0255 LATIN SMALL LETTER C WITH CURL | 0254 LATIN SMALL LETTER OPEN O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0257 LATIN SMALL LETTER D WITH HOOK | 0256 LATIN SMALL LETTER D WITH TAIL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0259 LATIN SMALL LETTER SCHWA | 0258 LATIN SMALL LETTER REVERSED E */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 025b LATIN SMALL LETTER OPEN E | 025a LATIN SMALL LETTER SCHWA WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 025d LATIN SMALL LETTER REVERSED OPEN E WITH | 025c LATIN SMALL LETTER REVERSED OPEN E */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 025f LATIN SMALL LETTER DOTLESS J WITH STROK | 025e LATIN SMALL LETTER CLOSED REVERSED OPEN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0261 LATIN SMALL LETTER SCRIPT G | 0260 LATIN SMALL LETTER G WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0263 LATIN SMALL LETTER GAMMA | 0262 LATIN LETTER SMALL CAPITAL G */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0265 LATIN SMALL LETTER TURNED H | 0264 LATIN SMALL LETTER RAMS HORN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0267 LATIN SMALL LETTER HENG WITH HOOK | 0266 LATIN SMALL LETTER H WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0269 LATIN SMALL LETTER IOTA | 0268 LATIN SMALL LETTER I WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 026b LATIN SMALL LETTER L WITH MIDDLE TILDE | 026a LATIN LETTER SMALL CAPITAL I */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 026d LATIN SMALL LETTER L WITH RETROFLEX HOO | 026c LATIN SMALL LETTER L WITH BELT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 026f LATIN SMALL LETTER TURNED M | 026e LATIN SMALL LETTER LEZH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0271 LATIN SMALL LETTER M WITH HOOK | 0270 LATIN SMALL LETTER TURNED M WITH LONG L */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0273 LATIN SMALL LETTER N WITH RETROFLEX HOO | 0272 LATIN SMALL LETTER N WITH LEFT HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0275 LATIN SMALL LETTER BARRED O | 0274 LATIN LETTER SMALL CAPITAL N */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0277 LATIN SMALL LETTER CLOSED OMEGA | 0276 LATIN LETTER SMALL CAPITAL OE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0279 LATIN SMALL LETTER TURNED R | 0278 LATIN SMALL LETTER PHI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 027b LATIN SMALL LETTER TURNED R WITH HOOK | 027a LATIN SMALL LETTER TURNED R WITH LONG L */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 027d LATIN SMALL LETTER R WITH TAIL | 027c LATIN SMALL LETTER R WITH LONG LEG */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 027f LATIN SMALL LETTER REVERSED R WITH FISH | 027e LATIN SMALL LETTER R WITH FISHHOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0281 LATIN LETTER SMALL CAPITAL INVERTED R | 0280 LATIN LETTER SMALL CAPITAL R */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0283 LATIN SMALL LETTER ESH | 0282 LATIN SMALL LETTER S WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0285 LATIN SMALL LETTER SQUAT REVERSED ESH | 0284 LATIN SMALL LETTER DOTLESS J WITH STROK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0287 LATIN SMALL LETTER TURNED T | 0286 LATIN SMALL LETTER ESH WITH CURL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0289 LATIN SMALL LETTER U BAR | 0288 LATIN SMALL LETTER T WITH RETROFLEX HOO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 028b LATIN SMALL LETTER V WITH HOOK | 028a LATIN SMALL LETTER UPSILON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 028d LATIN SMALL LETTER TURNED W | 028c LATIN SMALL LETTER TURNED V */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 028f LATIN LETTER SMALL CAPITAL Y | 028e LATIN SMALL LETTER TURNED Y */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0291 LATIN SMALL LETTER Z WITH CURL | 0290 LATIN SMALL LETTER Z WITH RETROFLEX HOO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0293 LATIN SMALL LETTER EZH WITH CURL | 0292 LATIN SMALL LETTER EZH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_ALPHA, /* 0295 LATIN LETTER PHARYNGEAL VOICED FRICATIV | 0294 LATIN LETTER GLOTTAL STOP */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0297 LATIN LETTER STRETCHED C | 0296 LATIN LETTER INVERTED GLOTTAL STOP */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0299 LATIN LETTER SMALL CAPITAL B | 0298 LATIN LETTER BILABIAL CLICK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 029b LATIN LETTER SMALL CAPITAL G WITH HOOK | 029a LATIN SMALL LETTER CLOSED OPEN E */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 029d LATIN SMALL LETTER J WITH CROSSED-TAIL | 029c LATIN LETTER SMALL CAPITAL H */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 029f LATIN LETTER SMALL CAPITAL L | 029e LATIN SMALL LETTER TURNED K */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 02a1 LATIN LETTER GLOTTAL STOP WITH STROKE | 02a0 LATIN SMALL LETTER Q WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 02a3 LATIN SMALL LETTER DZ DIGRAPH | 02a2 LATIN LETTER REVERSED GLOTTAL STOP WITH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 02a5 LATIN SMALL LETTER DZ DIGRAPH WITH CURL | 02a4 LATIN SMALL LETTER DEZH DIGRAPH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 02a7 LATIN SMALL LETTER TESH DIGRAPH | 02a6 LATIN SMALL LETTER TS DIGRAPH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 02a9 LATIN SMALL LETTER FENG DIGRAPH | 02a8 LATIN SMALL LETTER TC DIGRAPH WITH CURL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 02ab LATIN SMALL LETTER LZ DIGRAPH | 02aa LATIN SMALL LETTER LS DIGRAPH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 02ad LATIN LETTER BIDENTAL PERCUSSIVE | 02ac LATIN LETTER BILABIAL PERCUSSIVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 02af LATIN SMALL LETTER TURNED H WITH FISHHO | 02ae LATIN SMALL LETTER TURNED H WITH FISHHO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02b1 MODIFIER LETTER SMALL H WITH HOOK | 02b0 MODIFIER LETTER SMALL H */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02b3 MODIFIER LETTER SMALL R | 02b2 MODIFIER LETTER SMALL J */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02b5 MODIFIER LETTER SMALL TURNED R WITH HOO | 02b4 MODIFIER LETTER SMALL TURNED R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02b7 MODIFIER LETTER SMALL W | 02b6 MODIFIER LETTER SMALL CAPITAL INVERTED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02b9 MODIFIER LETTER PRIME | 02b8 MODIFIER LETTER SMALL Y */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02bb MODIFIER LETTER TURNED COMMA | 02ba MODIFIER LETTER DOUBLE PRIME */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02bd MODIFIER LETTER REVERSED COMMA | 02bc MODIFIER LETTER APOSTROPHE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02bf MODIFIER LETTER LEFT HALF RING | 02be MODIFIER LETTER RIGHT HALF RING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02c1 MODIFIER LETTER REVERSED GLOTTAL STOP | 02c0 MODIFIER LETTER GLOTTAL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02c3 MODIFIER LETTER RIGHT ARROWHEAD | 02c2 MODIFIER LETTER LEFT ARROWHEAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02c5 MODIFIER LETTER DOWN ARROWHEAD | 02c4 MODIFIER LETTER UP ARROWHEAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02c7 CARON | 02c6 MODIFIER LETTER CIRCUMFLEX ACCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02c9 MODIFIER LETTER MACRON | 02c8 MODIFIER LETTER VERTICAL LINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02cb MODIFIER LETTER GRAVE ACCENT | 02ca MODIFIER LETTER ACUTE ACCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02cd MODIFIER LETTER LOW MACRON | 02cc MODIFIER LETTER LOW VERTICAL LINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02cf MODIFIER LETTER LOW ACUTE ACCENT | 02ce MODIFIER LETTER LOW GRAVE ACCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02d1 MODIFIER LETTER HALF TRIANGULAR COLON | 02d0 MODIFIER LETTER TRIANGULAR COLON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02d3 MODIFIER LETTER CENTRED LEFT HALF RING | 02d2 MODIFIER LETTER CENTRED RIGHT HALF RING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02d5 MODIFIER LETTER DOWN TACK | 02d4 MODIFIER LETTER UP TACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02d7 MODIFIER LETTER MINUS SIGN | 02d6 MODIFIER LETTER PLUS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02d9 DOT ABOVE | 02d8 BREVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02db OGONEK | 02da RING ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02dd DOUBLE ACUTE ACCENT | 02dc SMALL TILDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02df MODIFIER LETTER CROSS ACCENT | 02de MODIFIER LETTER RHOTIC HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02e1 MODIFIER LETTER SMALL L | 02e0 MODIFIER LETTER SMALL GAMMA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02e3 MODIFIER LETTER SMALL X | 02e2 MODIFIER LETTER SMALL S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02e5 MODIFIER LETTER EXTRA-HIGH TONE BAR | 02e4 MODIFIER LETTER SMALL REVERSED GLOTTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02e7 MODIFIER LETTER MID TONE BAR | 02e6 MODIFIER LETTER HIGH TONE BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02e9 MODIFIER LETTER EXTRA-LOW TONE BAR | 02e8 MODIFIER LETTER LOW TONE BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02eb MODIFIER LETTER YANG DEPARTING TONE MAR | 02ea MODIFIER LETTER YIN DEPARTING TONE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02ed MODIFIER LETTER UNASPIRATED | 02ec MODIFIER LETTER VOICING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02ef MODIFIER LETTER LOW DOWN ARROWHEAD | 02ee MODIFIER LETTER DOUBLE APOSTROPHE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02f1 MODIFIER LETTER LOW LEFT ARROWHEAD | 02f0 MODIFIER LETTER LOW UP ARROWHEAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02f3 MODIFIER LETTER LOW RING | 02f2 MODIFIER LETTER LOW RIGHT ARROWHEAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02f5 MODIFIER LETTER MIDDLE DOUBLE GRAVE ACC | 02f4 MODIFIER LETTER MIDDLE GRAVE ACCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02f7 MODIFIER LETTER LOW TILDE | 02f6 MODIFIER LETTER MIDDLE DOUBLE ACUTE ACC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02f9 MODIFIER LETTER BEGIN HIGH TONE | 02f8 MODIFIER LETTER RAISED COLON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02fb MODIFIER LETTER BEGIN LOW TONE | 02fa MODIFIER LETTER END HIGH TONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02fd MODIFIER LETTER SHELF | 02fc MODIFIER LETTER END LOW TONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02ff MODIFIER LETTER LOW LEFT ARROW | 02fe MODIFIER LETTER OPEN SHELF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0301 COMBINING ACUTE ACCENT | 0300 COMBINING GRAVE ACCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0303 COMBINING TILDE | 0302 COMBINING CIRCUMFLEX ACCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0305 COMBINING OVERLINE | 0304 COMBINING MACRON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0307 COMBINING DOT ABOVE | 0306 COMBINING BREVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0309 COMBINING HOOK ABOVE | 0308 COMBINING DIAERESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 030b COMBINING DOUBLE ACUTE ACCENT | 030a COMBINING RING ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 030d COMBINING VERTICAL LINE ABOVE | 030c COMBINING CARON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 030f COMBINING DOUBLE GRAVE ACCENT | 030e COMBINING DOUBLE VERTICAL LINE ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0311 COMBINING INVERTED BREVE | 0310 COMBINING CANDRABINDU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0313 COMBINING COMMA ABOVE | 0312 COMBINING TURNED COMMA ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0315 COMBINING COMMA ABOVE RIGHT | 0314 COMBINING REVERSED COMMA ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0317 COMBINING ACUTE ACCENT BELOW | 0316 COMBINING GRAVE ACCENT BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0319 COMBINING RIGHT TACK BELOW | 0318 COMBINING LEFT TACK BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 031b COMBINING HORN | 031a COMBINING LEFT ANGLE ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 031d COMBINING UP TACK BELOW | 031c COMBINING LEFT HALF RING BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 031f COMBINING PLUS SIGN BELOW | 031e COMBINING DOWN TACK BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0321 COMBINING PALATALIZED HOOK BELOW | 0320 COMBINING MINUS SIGN BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0323 COMBINING DOT BELOW | 0322 COMBINING RETROFLEX HOOK BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0325 COMBINING RING BELOW | 0324 COMBINING DIAERESIS BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0327 COMBINING CEDILLA | 0326 COMBINING COMMA BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0329 COMBINING VERTICAL LINE BELOW | 0328 COMBINING OGONEK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 032b COMBINING INVERTED DOUBLE ARCH BELOW | 032a COMBINING BRIDGE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 032d COMBINING CIRCUMFLEX ACCENT BELOW | 032c COMBINING CARON BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 032f COMBINING INVERTED BREVE BELOW | 032e COMBINING BREVE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0331 COMBINING MACRON BELOW | 0330 COMBINING TILDE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0333 COMBINING DOUBLE LOW LINE | 0332 COMBINING LOW LINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0335 COMBINING SHORT STROKE OVERLAY | 0334 COMBINING TILDE OVERLAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0337 COMBINING SHORT SOLIDUS OVERLAY | 0336 COMBINING LONG STROKE OVERLAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0339 COMBINING RIGHT HALF RING BELOW | 0338 COMBINING LONG SOLIDUS OVERLAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 033b COMBINING SQUARE BELOW | 033a COMBINING INVERTED BRIDGE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 033d COMBINING X ABOVE | 033c COMBINING SEAGULL BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 033f COMBINING DOUBLE OVERLINE | 033e COMBINING VERTICAL TILDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0341 COMBINING ACUTE TONE MARK | 0340 COMBINING GRAVE TONE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0343 COMBINING GREEK KORONIS | 0342 COMBINING GREEK PERISPOMENI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0345 COMBINING GREEK YPOGEGRAMMENI | 0344 COMBINING GREEK DIALYTIKA TONOS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0347 COMBINING EQUALS SIGN BELOW | 0346 COMBINING BRIDGE ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0349 COMBINING LEFT ANGLE BELOW | 0348 COMBINING DOUBLE VERTICAL LINE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 034b COMBINING HOMOTHETIC ABOVE | 034a COMBINING NOT TILDE ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 034d COMBINING LEFT RIGHT ARROW BELOW | 034c COMBINING ALMOST EQUAL TO ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 034f COMBINING GRAPHEME JOINER | 034e COMBINING UPWARDS ARROW BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0351 COMBINING LEFT HALF RING ABOVE | 0350 COMBINING RIGHT ARROWHEAD ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0353 COMBINING X BELOW | 0352 COMBINING FERMATA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0355 COMBINING RIGHT ARROWHEAD BELOW | 0354 COMBINING LEFT ARROWHEAD BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0357 COMBINING RIGHT HALF RING ABOVE | 0356 COMBINING RIGHT ARROWHEAD AND UP ARROWH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0359 COMBINING ASTERISK BELOW | 0358 COMBINING DOT ABOVE RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 035b COMBINING ZIGZAG ABOVE | 035a COMBINING DOUBLE RING BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 035d COMBINING DOUBLE BREVE | 035c COMBINING DOUBLE BREVE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 035f COMBINING DOUBLE MACRON BELOW | 035e COMBINING DOUBLE MACRON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0361 COMBINING DOUBLE INVERTED BREVE | 0360 COMBINING DOUBLE TILDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0363 COMBINING LATIN SMALL LETTER A | 0362 COMBINING DOUBLE RIGHTWARDS ARROW BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0365 COMBINING LATIN SMALL LETTER I | 0364 COMBINING LATIN SMALL LETTER E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0367 COMBINING LATIN SMALL LETTER U | 0366 COMBINING LATIN SMALL LETTER O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0369 COMBINING LATIN SMALL LETTER D | 0368 COMBINING LATIN SMALL LETTER C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 036b COMBINING LATIN SMALL LETTER M | 036a COMBINING LATIN SMALL LETTER H */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 036d COMBINING LATIN SMALL LETTER T | 036c COMBINING LATIN SMALL LETTER R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 036f COMBINING LATIN SMALL LETTER X | 036e COMBINING LATIN SMALL LETTER V */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0371 GREEK SMALL LETTER HETA | 0370 GREEK CAPITAL LETTER HETA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0373 GREEK SMALL LETTER ARCHAIC SAMPI | 0372 GREEK CAPITAL LETTER ARCHAIC SAMPI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0375 GREEK LOWER NUMERAL SIGN | 0374 GREEK NUMERAL SIGN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0377 GREEK SMALL LETTER PAMPHYLIAN DIGAMMA | 0376 GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0379 (null) | 0378 (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_OTHER, /* 037b GREEK SMALL REVERSED LUNATE SIGMA SYMBO | 037a GREEK YPOGEGRAMMENI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 037d GREEK SMALL REVERSED DOTTED LUNATE SIGM | 037c GREEK SMALL DOTTED LUNATE SIGMA SYMBOL */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 037f (null) | 037e GREEK QUESTION MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0381 (null) | 0380 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0383 (null) | 0382 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0385 GREEK DIALYTIKA TONOS | 0384 GREEK TONOS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_UPPER, /* 0387 GREEK ANO TELEIA | 0386 GREEK CAPITAL LETTER ALPHA WITH TONOS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0389 GREEK CAPITAL LETTER ETA WITH TONOS | 0388 GREEK CAPITAL LETTER EPSILON WITH TONOS */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UPPER, /* 038b (null) | 038a GREEK CAPITAL LETTER IOTA WITH TONOS */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UPPER, /* 038d (null) | 038c GREEK CAPITAL LETTER OMICRON WITH TONOS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 038f GREEK CAPITAL LETTER OMEGA WITH TONOS | 038e GREEK CAPITAL LETTER UPSILON WITH TONOS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0391 GREEK CAPITAL LETTER ALPHA | 0390 GREEK SMALL LETTER IOTA WITH DIALYTIKA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0393 GREEK CAPITAL LETTER GAMMA | 0392 GREEK CAPITAL LETTER BETA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0395 GREEK CAPITAL LETTER EPSILON | 0394 GREEK CAPITAL LETTER DELTA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0397 GREEK CAPITAL LETTER ETA | 0396 GREEK CAPITAL LETTER ZETA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0399 GREEK CAPITAL LETTER IOTA | 0398 GREEK CAPITAL LETTER THETA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 039b GREEK CAPITAL LETTER LAMDA | 039a GREEK CAPITAL LETTER KAPPA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 039d GREEK CAPITAL LETTER NU | 039c GREEK CAPITAL LETTER MU */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 039f GREEK CAPITAL LETTER OMICRON | 039e GREEK CAPITAL LETTER XI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 03a1 GREEK CAPITAL LETTER RHO | 03a0 GREEK CAPITAL LETTER PI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UNDEF, /* 03a3 GREEK CAPITAL LETTER SIGMA | 03a2 (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 03a5 GREEK CAPITAL LETTER UPSILON | 03a4 GREEK CAPITAL LETTER TAU */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 03a7 GREEK CAPITAL LETTER CHI | 03a6 GREEK CAPITAL LETTER PHI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 03a9 GREEK CAPITAL LETTER OMEGA | 03a8 GREEK CAPITAL LETTER PSI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 03ab GREEK CAPITAL LETTER UPSILON WITH DIALY | 03aa GREEK CAPITAL LETTER IOTA WITH DIALYTIK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03ad GREEK SMALL LETTER EPSILON WITH TONOS | 03ac GREEK SMALL LETTER ALPHA WITH TONOS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03af GREEK SMALL LETTER IOTA WITH TONOS | 03ae GREEK SMALL LETTER ETA WITH TONOS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03b1 GREEK SMALL LETTER ALPHA | 03b0 GREEK SMALL LETTER UPSILON WITH DIALYTI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03b3 GREEK SMALL LETTER GAMMA | 03b2 GREEK SMALL LETTER BETA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03b5 GREEK SMALL LETTER EPSILON | 03b4 GREEK SMALL LETTER DELTA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03b7 GREEK SMALL LETTER ETA | 03b6 GREEK SMALL LETTER ZETA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03b9 GREEK SMALL LETTER IOTA | 03b8 GREEK SMALL LETTER THETA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03bb GREEK SMALL LETTER LAMDA | 03ba GREEK SMALL LETTER KAPPA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03bd GREEK SMALL LETTER NU | 03bc GREEK SMALL LETTER MU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03bf GREEK SMALL LETTER OMICRON | 03be GREEK SMALL LETTER XI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03c1 GREEK SMALL LETTER RHO | 03c0 GREEK SMALL LETTER PI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03c3 GREEK SMALL LETTER SIGMA | 03c2 GREEK SMALL LETTER FINAL SIGMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03c5 GREEK SMALL LETTER UPSILON | 03c4 GREEK SMALL LETTER TAU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03c7 GREEK SMALL LETTER CHI | 03c6 GREEK SMALL LETTER PHI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03c9 GREEK SMALL LETTER OMEGA | 03c8 GREEK SMALL LETTER PSI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03cb GREEK SMALL LETTER UPSILON WITH DIALYTI | 03ca GREEK SMALL LETTER IOTA WITH DIALYTIKA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03cd GREEK SMALL LETTER UPSILON WITH TONOS | 03cc GREEK SMALL LETTER OMICRON WITH TONOS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 03cf GREEK CAPITAL KAI SYMBOL | 03ce GREEK SMALL LETTER OMEGA WITH TONOS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03d1 GREEK THETA SYMBOL | 03d0 GREEK BETA SYMBOL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 03d3 GREEK UPSILON WITH ACUTE AND HOOK SYMBO | 03d2 GREEK UPSILON WITH HOOK SYMBOL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03d5 GREEK PHI SYMBOL | 03d4 GREEK UPSILON WITH DIAERESIS AND HOOK S */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03d7 GREEK KAI SYMBOL | 03d6 GREEK PI SYMBOL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03d9 GREEK SMALL LETTER ARCHAIC KOPPA | 03d8 GREEK LETTER ARCHAIC KOPPA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03db GREEK SMALL LETTER STIGMA | 03da GREEK LETTER STIGMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03dd GREEK SMALL LETTER DIGAMMA | 03dc GREEK LETTER DIGAMMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03df GREEK SMALL LETTER KOPPA | 03de GREEK LETTER KOPPA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03e1 GREEK SMALL LETTER SAMPI | 03e0 GREEK LETTER SAMPI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03e3 COPTIC SMALL LETTER SHEI | 03e2 COPTIC CAPITAL LETTER SHEI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03e5 COPTIC SMALL LETTER FEI | 03e4 COPTIC CAPITAL LETTER FEI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03e7 COPTIC SMALL LETTER KHEI | 03e6 COPTIC CAPITAL LETTER KHEI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03e9 COPTIC SMALL LETTER HORI | 03e8 COPTIC CAPITAL LETTER HORI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03eb COPTIC SMALL LETTER GANGIA | 03ea COPTIC CAPITAL LETTER GANGIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03ed COPTIC SMALL LETTER SHIMA | 03ec COPTIC CAPITAL LETTER SHIMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03ef COPTIC SMALL LETTER DEI | 03ee COPTIC CAPITAL LETTER DEI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03f1 GREEK RHO SYMBOL | 03f0 GREEK KAPPA SYMBOL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03f3 GREEK LETTER YOT | 03f2 GREEK LUNATE SIGMA SYMBOL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03f5 GREEK LUNATE EPSILON SYMBOL | 03f4 GREEK CAPITAL THETA SYMBOL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_OTHER, /* 03f7 GREEK CAPITAL LETTER SHO | 03f6 GREEK REVERSED LUNATE EPSILON SYMBOL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 03f9 GREEK CAPITAL LUNATE SIGMA SYMBOL | 03f8 GREEK SMALL LETTER SHO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03fb GREEK SMALL LETTER SAN | 03fa GREEK CAPITAL LETTER SAN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 03fd GREEK CAPITAL REVERSED LUNATE SIGMA SYM | 03fc GREEK RHO WITH STROKE SYMBOL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 03ff GREEK CAPITAL REVERSED DOTTED LUNATE SI | 03fe GREEK CAPITAL DOTTED LUNATE SIGMA SYMBO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0401 CYRILLIC CAPITAL LETTER IO | 0400 CYRILLIC CAPITAL LETTER IE WITH GRAVE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0403 CYRILLIC CAPITAL LETTER GJE | 0402 CYRILLIC CAPITAL LETTER DJE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0405 CYRILLIC CAPITAL LETTER DZE | 0404 CYRILLIC CAPITAL LETTER UKRAINIAN IE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0407 CYRILLIC CAPITAL LETTER YI | 0406 CYRILLIC CAPITAL LETTER BYELORUSSIAN-UK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0409 CYRILLIC CAPITAL LETTER LJE | 0408 CYRILLIC CAPITAL LETTER JE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 040b CYRILLIC CAPITAL LETTER TSHE | 040a CYRILLIC CAPITAL LETTER NJE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 040d CYRILLIC CAPITAL LETTER I WITH GRAVE | 040c CYRILLIC CAPITAL LETTER KJE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 040f CYRILLIC CAPITAL LETTER DZHE | 040e CYRILLIC CAPITAL LETTER SHORT U */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0411 CYRILLIC CAPITAL LETTER BE | 0410 CYRILLIC CAPITAL LETTER A */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0413 CYRILLIC CAPITAL LETTER GHE | 0412 CYRILLIC CAPITAL LETTER VE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0415 CYRILLIC CAPITAL LETTER IE | 0414 CYRILLIC CAPITAL LETTER DE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0417 CYRILLIC CAPITAL LETTER ZE | 0416 CYRILLIC CAPITAL LETTER ZHE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0419 CYRILLIC CAPITAL LETTER SHORT I | 0418 CYRILLIC CAPITAL LETTER I */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 041b CYRILLIC CAPITAL LETTER EL | 041a CYRILLIC CAPITAL LETTER KA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 041d CYRILLIC CAPITAL LETTER EN | 041c CYRILLIC CAPITAL LETTER EM */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 041f CYRILLIC CAPITAL LETTER PE | 041e CYRILLIC CAPITAL LETTER O */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0421 CYRILLIC CAPITAL LETTER ES | 0420 CYRILLIC CAPITAL LETTER ER */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0423 CYRILLIC CAPITAL LETTER U | 0422 CYRILLIC CAPITAL LETTER TE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0425 CYRILLIC CAPITAL LETTER HA | 0424 CYRILLIC CAPITAL LETTER EF */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0427 CYRILLIC CAPITAL LETTER CHE | 0426 CYRILLIC CAPITAL LETTER TSE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0429 CYRILLIC CAPITAL LETTER SHCHA | 0428 CYRILLIC CAPITAL LETTER SHA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 042b CYRILLIC CAPITAL LETTER YERU | 042a CYRILLIC CAPITAL LETTER HARD SIGN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 042d CYRILLIC CAPITAL LETTER E | 042c CYRILLIC CAPITAL LETTER SOFT SIGN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 042f CYRILLIC CAPITAL LETTER YA | 042e CYRILLIC CAPITAL LETTER YU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0431 CYRILLIC SMALL LETTER BE | 0430 CYRILLIC SMALL LETTER A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0433 CYRILLIC SMALL LETTER GHE | 0432 CYRILLIC SMALL LETTER VE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0435 CYRILLIC SMALL LETTER IE | 0434 CYRILLIC SMALL LETTER DE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0437 CYRILLIC SMALL LETTER ZE | 0436 CYRILLIC SMALL LETTER ZHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0439 CYRILLIC SMALL LETTER SHORT I | 0438 CYRILLIC SMALL LETTER I */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 043b CYRILLIC SMALL LETTER EL | 043a CYRILLIC SMALL LETTER KA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 043d CYRILLIC SMALL LETTER EN | 043c CYRILLIC SMALL LETTER EM */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 043f CYRILLIC SMALL LETTER PE | 043e CYRILLIC SMALL LETTER O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0441 CYRILLIC SMALL LETTER ES | 0440 CYRILLIC SMALL LETTER ER */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0443 CYRILLIC SMALL LETTER U | 0442 CYRILLIC SMALL LETTER TE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0445 CYRILLIC SMALL LETTER HA | 0444 CYRILLIC SMALL LETTER EF */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0447 CYRILLIC SMALL LETTER CHE | 0446 CYRILLIC SMALL LETTER TSE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0449 CYRILLIC SMALL LETTER SHCHA | 0448 CYRILLIC SMALL LETTER SHA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 044b CYRILLIC SMALL LETTER YERU | 044a CYRILLIC SMALL LETTER HARD SIGN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 044d CYRILLIC SMALL LETTER E | 044c CYRILLIC SMALL LETTER SOFT SIGN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 044f CYRILLIC SMALL LETTER YA | 044e CYRILLIC SMALL LETTER YU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0451 CYRILLIC SMALL LETTER IO | 0450 CYRILLIC SMALL LETTER IE WITH GRAVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0453 CYRILLIC SMALL LETTER GJE | 0452 CYRILLIC SMALL LETTER DJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0455 CYRILLIC SMALL LETTER DZE | 0454 CYRILLIC SMALL LETTER UKRAINIAN IE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0457 CYRILLIC SMALL LETTER YI | 0456 CYRILLIC SMALL LETTER BYELORUSSIAN-UKRA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0459 CYRILLIC SMALL LETTER LJE | 0458 CYRILLIC SMALL LETTER JE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 045b CYRILLIC SMALL LETTER TSHE | 045a CYRILLIC SMALL LETTER NJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 045d CYRILLIC SMALL LETTER I WITH GRAVE | 045c CYRILLIC SMALL LETTER KJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 045f CYRILLIC SMALL LETTER DZHE | 045e CYRILLIC SMALL LETTER SHORT U */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0461 CYRILLIC SMALL LETTER OMEGA | 0460 CYRILLIC CAPITAL LETTER OMEGA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0463 CYRILLIC SMALL LETTER YAT | 0462 CYRILLIC CAPITAL LETTER YAT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0465 CYRILLIC SMALL LETTER IOTIFIED E | 0464 CYRILLIC CAPITAL LETTER IOTIFIED E */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0467 CYRILLIC SMALL LETTER LITTLE YUS | 0466 CYRILLIC CAPITAL LETTER LITTLE YUS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0469 CYRILLIC SMALL LETTER IOTIFIED LITTLE Y | 0468 CYRILLIC CAPITAL LETTER IOTIFIED LITTLE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 046b CYRILLIC SMALL LETTER BIG YUS | 046a CYRILLIC CAPITAL LETTER BIG YUS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 046d CYRILLIC SMALL LETTER IOTIFIED BIG YUS | 046c CYRILLIC CAPITAL LETTER IOTIFIED BIG YU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 046f CYRILLIC SMALL LETTER KSI | 046e CYRILLIC CAPITAL LETTER KSI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0471 CYRILLIC SMALL LETTER PSI | 0470 CYRILLIC CAPITAL LETTER PSI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0473 CYRILLIC SMALL LETTER FITA | 0472 CYRILLIC CAPITAL LETTER FITA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0475 CYRILLIC SMALL LETTER IZHITSA | 0474 CYRILLIC CAPITAL LETTER IZHITSA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0477 CYRILLIC SMALL LETTER IZHITSA WITH DOUB | 0476 CYRILLIC CAPITAL LETTER IZHITSA WITH DO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0479 CYRILLIC SMALL LETTER UK | 0478 CYRILLIC CAPITAL LETTER UK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 047b CYRILLIC SMALL LETTER ROUND OMEGA | 047a CYRILLIC CAPITAL LETTER ROUND OMEGA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 047d CYRILLIC SMALL LETTER OMEGA WITH TITLO | 047c CYRILLIC CAPITAL LETTER OMEGA WITH TITL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 047f CYRILLIC SMALL LETTER OT | 047e CYRILLIC CAPITAL LETTER OT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0481 CYRILLIC SMALL LETTER KOPPA | 0480 CYRILLIC CAPITAL LETTER KOPPA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0483 COMBINING CYRILLIC TITLO | 0482 CYRILLIC THOUSANDS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0485 COMBINING CYRILLIC DASIA PNEUMATA | 0484 COMBINING CYRILLIC PALATALIZATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0487 COMBINING CYRILLIC POKRYTIE | 0486 COMBINING CYRILLIC PSILI PNEUMATA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0489 COMBINING CYRILLIC MILLIONS SIGN | 0488 COMBINING CYRILLIC HUNDRED THOUSANDS SI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 048b CYRILLIC SMALL LETTER SHORT I WITH TAIL | 048a CYRILLIC CAPITAL LETTER SHORT I WITH TA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 048d CYRILLIC SMALL LETTER SEMISOFT SIGN | 048c CYRILLIC CAPITAL LETTER SEMISOFT SIGN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 048f CYRILLIC SMALL LETTER ER WITH TICK | 048e CYRILLIC CAPITAL LETTER ER WITH TICK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0491 CYRILLIC SMALL LETTER GHE WITH UPTURN | 0490 CYRILLIC CAPITAL LETTER GHE WITH UPTURN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0493 CYRILLIC SMALL LETTER GHE WITH STROKE | 0492 CYRILLIC CAPITAL LETTER GHE WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0495 CYRILLIC SMALL LETTER GHE WITH MIDDLE H | 0494 CYRILLIC CAPITAL LETTER GHE WITH MIDDLE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0497 CYRILLIC SMALL LETTER ZHE WITH DESCENDE | 0496 CYRILLIC CAPITAL LETTER ZHE WITH DESCEN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0499 CYRILLIC SMALL LETTER ZE WITH DESCENDER | 0498 CYRILLIC CAPITAL LETTER ZE WITH DESCEND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 049b CYRILLIC SMALL LETTER KA WITH DESCENDER | 049a CYRILLIC CAPITAL LETTER KA WITH DESCEND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 049d CYRILLIC SMALL LETTER KA WITH VERTICAL | 049c CYRILLIC CAPITAL LETTER KA WITH VERTICA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 049f CYRILLIC SMALL LETTER KA WITH STROKE | 049e CYRILLIC CAPITAL LETTER KA WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04a1 CYRILLIC SMALL LETTER BASHKIR KA | 04a0 CYRILLIC CAPITAL LETTER BASHKIR KA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04a3 CYRILLIC SMALL LETTER EN WITH DESCENDER | 04a2 CYRILLIC CAPITAL LETTER EN WITH DESCEND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04a5 CYRILLIC SMALL LIGATURE EN GHE | 04a4 CYRILLIC CAPITAL LIGATURE EN GHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04a7 CYRILLIC SMALL LETTER PE WITH MIDDLE HO | 04a6 CYRILLIC CAPITAL LETTER PE WITH MIDDLE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04a9 CYRILLIC SMALL LETTER ABKHASIAN HA | 04a8 CYRILLIC CAPITAL LETTER ABKHASIAN HA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04ab CYRILLIC SMALL LETTER ES WITH DESCENDER | 04aa CYRILLIC CAPITAL LETTER ES WITH DESCEND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04ad CYRILLIC SMALL LETTER TE WITH DESCENDER | 04ac CYRILLIC CAPITAL LETTER TE WITH DESCEND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04af CYRILLIC SMALL LETTER STRAIGHT U | 04ae CYRILLIC CAPITAL LETTER STRAIGHT U */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04b1 CYRILLIC SMALL LETTER STRAIGHT U WITH S | 04b0 CYRILLIC CAPITAL LETTER STRAIGHT U WITH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04b3 CYRILLIC SMALL LETTER HA WITH DESCENDER | 04b2 CYRILLIC CAPITAL LETTER HA WITH DESCEND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04b5 CYRILLIC SMALL LIGATURE TE TSE | 04b4 CYRILLIC CAPITAL LIGATURE TE TSE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04b7 CYRILLIC SMALL LETTER CHE WITH DESCENDE | 04b6 CYRILLIC CAPITAL LETTER CHE WITH DESCEN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04b9 CYRILLIC SMALL LETTER CHE WITH VERTICAL | 04b8 CYRILLIC CAPITAL LETTER CHE WITH VERTIC */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04bb CYRILLIC SMALL LETTER SHHA | 04ba CYRILLIC CAPITAL LETTER SHHA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04bd CYRILLIC SMALL LETTER ABKHASIAN CHE | 04bc CYRILLIC CAPITAL LETTER ABKHASIAN CHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04bf CYRILLIC SMALL LETTER ABKHASIAN CHE WIT | 04be CYRILLIC CAPITAL LETTER ABKHASIAN CHE W */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 04c1 CYRILLIC CAPITAL LETTER ZHE WITH BREVE | 04c0 CYRILLIC LETTER PALOCHKA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 04c3 CYRILLIC CAPITAL LETTER KA WITH HOOK | 04c2 CYRILLIC SMALL LETTER ZHE WITH BREVE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 04c5 CYRILLIC CAPITAL LETTER EL WITH TAIL | 04c4 CYRILLIC SMALL LETTER KA WITH HOOK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 04c7 CYRILLIC CAPITAL LETTER EN WITH HOOK | 04c6 CYRILLIC SMALL LETTER EL WITH TAIL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 04c9 CYRILLIC CAPITAL LETTER EN WITH TAIL | 04c8 CYRILLIC SMALL LETTER EN WITH HOOK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 04cb CYRILLIC CAPITAL LETTER KHAKASSIAN CHE | 04ca CYRILLIC SMALL LETTER EN WITH TAIL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 04cd CYRILLIC CAPITAL LETTER EM WITH TAIL | 04cc CYRILLIC SMALL LETTER KHAKASSIAN CHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 04cf CYRILLIC SMALL LETTER PALOCHKA | 04ce CYRILLIC SMALL LETTER EM WITH TAIL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04d1 CYRILLIC SMALL LETTER A WITH BREVE | 04d0 CYRILLIC CAPITAL LETTER A WITH BREVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04d3 CYRILLIC SMALL LETTER A WITH DIAERESIS | 04d2 CYRILLIC CAPITAL LETTER A WITH DIAERESI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04d5 CYRILLIC SMALL LIGATURE A IE | 04d4 CYRILLIC CAPITAL LIGATURE A IE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04d7 CYRILLIC SMALL LETTER IE WITH BREVE | 04d6 CYRILLIC CAPITAL LETTER IE WITH BREVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04d9 CYRILLIC SMALL LETTER SCHWA | 04d8 CYRILLIC CAPITAL LETTER SCHWA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04db CYRILLIC SMALL LETTER SCHWA WITH DIAERE | 04da CYRILLIC CAPITAL LETTER SCHWA WITH DIAE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04dd CYRILLIC SMALL LETTER ZHE WITH DIAERESI | 04dc CYRILLIC CAPITAL LETTER ZHE WITH DIAERE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04df CYRILLIC SMALL LETTER ZE WITH DIAERESIS | 04de CYRILLIC CAPITAL LETTER ZE WITH DIAERES */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04e1 CYRILLIC SMALL LETTER ABKHASIAN DZE | 04e0 CYRILLIC CAPITAL LETTER ABKHASIAN DZE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04e3 CYRILLIC SMALL LETTER I WITH MACRON | 04e2 CYRILLIC CAPITAL LETTER I WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04e5 CYRILLIC SMALL LETTER I WITH DIAERESIS | 04e4 CYRILLIC CAPITAL LETTER I WITH DIAERESI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04e7 CYRILLIC SMALL LETTER O WITH DIAERESIS | 04e6 CYRILLIC CAPITAL LETTER O WITH DIAERESI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04e9 CYRILLIC SMALL LETTER BARRED O | 04e8 CYRILLIC CAPITAL LETTER BARRED O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04eb CYRILLIC SMALL LETTER BARRED O WITH DIA | 04ea CYRILLIC CAPITAL LETTER BARRED O WITH D */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04ed CYRILLIC SMALL LETTER E WITH DIAERESIS | 04ec CYRILLIC CAPITAL LETTER E WITH DIAERESI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04ef CYRILLIC SMALL LETTER U WITH MACRON | 04ee CYRILLIC CAPITAL LETTER U WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04f1 CYRILLIC SMALL LETTER U WITH DIAERESIS | 04f0 CYRILLIC CAPITAL LETTER U WITH DIAERESI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04f3 CYRILLIC SMALL LETTER U WITH DOUBLE ACU | 04f2 CYRILLIC CAPITAL LETTER U WITH DOUBLE A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04f5 CYRILLIC SMALL LETTER CHE WITH DIAERESI | 04f4 CYRILLIC CAPITAL LETTER CHE WITH DIAERE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04f7 CYRILLIC SMALL LETTER GHE WITH DESCENDE | 04f6 CYRILLIC CAPITAL LETTER GHE WITH DESCEN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04f9 CYRILLIC SMALL LETTER YERU WITH DIAERES | 04f8 CYRILLIC CAPITAL LETTER YERU WITH DIAER */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04fb CYRILLIC SMALL LETTER GHE WITH STROKE A | 04fa CYRILLIC CAPITAL LETTER GHE WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04fd CYRILLIC SMALL LETTER HA WITH HOOK | 04fc CYRILLIC CAPITAL LETTER HA WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04ff CYRILLIC SMALL LETTER HA WITH STROKE | 04fe CYRILLIC CAPITAL LETTER HA WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0501 CYRILLIC SMALL LETTER KOMI DE | 0500 CYRILLIC CAPITAL LETTER KOMI DE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0503 CYRILLIC SMALL LETTER KOMI DJE | 0502 CYRILLIC CAPITAL LETTER KOMI DJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0505 CYRILLIC SMALL LETTER KOMI ZJE | 0504 CYRILLIC CAPITAL LETTER KOMI ZJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0507 CYRILLIC SMALL LETTER KOMI DZJE | 0506 CYRILLIC CAPITAL LETTER KOMI DZJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0509 CYRILLIC SMALL LETTER KOMI LJE | 0508 CYRILLIC CAPITAL LETTER KOMI LJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 050b CYRILLIC SMALL LETTER KOMI NJE | 050a CYRILLIC CAPITAL LETTER KOMI NJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 050d CYRILLIC SMALL LETTER KOMI SJE | 050c CYRILLIC CAPITAL LETTER KOMI SJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 050f CYRILLIC SMALL LETTER KOMI TJE | 050e CYRILLIC CAPITAL LETTER KOMI TJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0511 CYRILLIC SMALL LETTER REVERSED ZE | 0510 CYRILLIC CAPITAL LETTER REVERSED ZE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0513 CYRILLIC SMALL LETTER EL WITH HOOK | 0512 CYRILLIC CAPITAL LETTER EL WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0515 CYRILLIC SMALL LETTER LHA | 0514 CYRILLIC CAPITAL LETTER LHA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0517 CYRILLIC SMALL LETTER RHA | 0516 CYRILLIC CAPITAL LETTER RHA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0519 CYRILLIC SMALL LETTER YAE | 0518 CYRILLIC CAPITAL LETTER YAE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 051b CYRILLIC SMALL LETTER QA | 051a CYRILLIC CAPITAL LETTER QA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 051d CYRILLIC SMALL LETTER WE | 051c CYRILLIC CAPITAL LETTER WE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 051f CYRILLIC SMALL LETTER ALEUT KA | 051e CYRILLIC CAPITAL LETTER ALEUT KA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0521 CYRILLIC SMALL LETTER EL WITH MIDDLE HO | 0520 CYRILLIC CAPITAL LETTER EL WITH MIDDLE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0523 CYRILLIC SMALL LETTER EN WITH MIDDLE HO | 0522 CYRILLIC CAPITAL LETTER EN WITH MIDDLE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0525 CYRILLIC SMALL LETTER PE WITH DESCENDER | 0524 CYRILLIC CAPITAL LETTER PE WITH DESCEND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0527 CYRILLIC SMALL LETTER SHHA WITH DESCEND | 0526 CYRILLIC CAPITAL LETTER SHHA WITH DESCE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0529 (null) | 0528 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 052b (null) | 052a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 052d (null) | 052c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 052f (null) | 052e (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UNDEF, /* 0531 ARMENIAN CAPITAL LETTER AYB | 0530 (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0533 ARMENIAN CAPITAL LETTER GIM | 0532 ARMENIAN CAPITAL LETTER BEN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0535 ARMENIAN CAPITAL LETTER ECH | 0534 ARMENIAN CAPITAL LETTER DA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0537 ARMENIAN CAPITAL LETTER EH | 0536 ARMENIAN CAPITAL LETTER ZA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0539 ARMENIAN CAPITAL LETTER TO | 0538 ARMENIAN CAPITAL LETTER ET */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 053b ARMENIAN CAPITAL LETTER INI | 053a ARMENIAN CAPITAL LETTER ZHE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 053d ARMENIAN CAPITAL LETTER XEH | 053c ARMENIAN CAPITAL LETTER LIWN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 053f ARMENIAN CAPITAL LETTER KEN | 053e ARMENIAN CAPITAL LETTER CA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0541 ARMENIAN CAPITAL LETTER JA | 0540 ARMENIAN CAPITAL LETTER HO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0543 ARMENIAN CAPITAL LETTER CHEH | 0542 ARMENIAN CAPITAL LETTER GHAD */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0545 ARMENIAN CAPITAL LETTER YI | 0544 ARMENIAN CAPITAL LETTER MEN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0547 ARMENIAN CAPITAL LETTER SHA | 0546 ARMENIAN CAPITAL LETTER NOW */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0549 ARMENIAN CAPITAL LETTER CHA | 0548 ARMENIAN CAPITAL LETTER VO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 054b ARMENIAN CAPITAL LETTER JHEH | 054a ARMENIAN CAPITAL LETTER PEH */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 054d ARMENIAN CAPITAL LETTER SEH | 054c ARMENIAN CAPITAL LETTER RA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 054f ARMENIAN CAPITAL LETTER TIWN | 054e ARMENIAN CAPITAL LETTER VEW */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0551 ARMENIAN CAPITAL LETTER CO | 0550 ARMENIAN CAPITAL LETTER REH */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0553 ARMENIAN CAPITAL LETTER PIWR | 0552 ARMENIAN CAPITAL LETTER YIWN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0555 ARMENIAN CAPITAL LETTER OH | 0554 ARMENIAN CAPITAL LETTER KEH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UPPER, /* 0557 (null) | 0556 ARMENIAN CAPITAL LETTER FEH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0559 ARMENIAN MODIFIER LETTER LEFT HALF RING | 0558 (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 055b ARMENIAN EMPHASIS MARK | 055a ARMENIAN APOSTROPHE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 055d ARMENIAN COMMA | 055c ARMENIAN EXCLAMATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 055f ARMENIAN ABBREVIATION MARK | 055e ARMENIAN QUESTION MARK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UNDEF, /* 0561 ARMENIAN SMALL LETTER AYB | 0560 (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0563 ARMENIAN SMALL LETTER GIM | 0562 ARMENIAN SMALL LETTER BEN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0565 ARMENIAN SMALL LETTER ECH | 0564 ARMENIAN SMALL LETTER DA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0567 ARMENIAN SMALL LETTER EH | 0566 ARMENIAN SMALL LETTER ZA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0569 ARMENIAN SMALL LETTER TO | 0568 ARMENIAN SMALL LETTER ET */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 056b ARMENIAN SMALL LETTER INI | 056a ARMENIAN SMALL LETTER ZHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 056d ARMENIAN SMALL LETTER XEH | 056c ARMENIAN SMALL LETTER LIWN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 056f ARMENIAN SMALL LETTER KEN | 056e ARMENIAN SMALL LETTER CA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0571 ARMENIAN SMALL LETTER JA | 0570 ARMENIAN SMALL LETTER HO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0573 ARMENIAN SMALL LETTER CHEH | 0572 ARMENIAN SMALL LETTER GHAD */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0575 ARMENIAN SMALL LETTER YI | 0574 ARMENIAN SMALL LETTER MEN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0577 ARMENIAN SMALL LETTER SHA | 0576 ARMENIAN SMALL LETTER NOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0579 ARMENIAN SMALL LETTER CHA | 0578 ARMENIAN SMALL LETTER VO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 057b ARMENIAN SMALL LETTER JHEH | 057a ARMENIAN SMALL LETTER PEH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 057d ARMENIAN SMALL LETTER SEH | 057c ARMENIAN SMALL LETTER RA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 057f ARMENIAN SMALL LETTER TIWN | 057e ARMENIAN SMALL LETTER VEW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0581 ARMENIAN SMALL LETTER CO | 0580 ARMENIAN SMALL LETTER REH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0583 ARMENIAN SMALL LETTER PIWR | 0582 ARMENIAN SMALL LETTER YIWN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0585 ARMENIAN SMALL LETTER OH | 0584 ARMENIAN SMALL LETTER KEH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0587 ARMENIAN SMALL LIGATURE ECH YIWN | 0586 ARMENIAN SMALL LETTER FEH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_UNDEF, /* 0589 ARMENIAN FULL STOP | 0588 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 058b (null) | 058a ARMENIAN HYPHEN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 058d (null) | 058c (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 058f ARMENIAN DRAM SIGN | 058e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0591 HEBREW ACCENT ETNAHTA | 0590 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0593 HEBREW ACCENT SHALSHELET | 0592 HEBREW ACCENT SEGOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0595 HEBREW ACCENT ZAQEF GADOL | 0594 HEBREW ACCENT ZAQEF QATAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0597 HEBREW ACCENT REVIA | 0596 HEBREW ACCENT TIPEHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0599 HEBREW ACCENT PASHTA | 0598 HEBREW ACCENT ZARQA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 059b HEBREW ACCENT TEVIR | 059a HEBREW ACCENT YETIV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 059d HEBREW ACCENT GERESH MUQDAM | 059c HEBREW ACCENT GERESH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 059f HEBREW ACCENT QARNEY PARA | 059e HEBREW ACCENT GERSHAYIM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05a1 HEBREW ACCENT PAZER | 05a0 HEBREW ACCENT TELISHA GEDOLA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05a3 HEBREW ACCENT MUNAH | 05a2 HEBREW ACCENT ATNAH HAFUKH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05a5 HEBREW ACCENT MERKHA | 05a4 HEBREW ACCENT MAHAPAKH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05a7 HEBREW ACCENT DARGA | 05a6 HEBREW ACCENT MERKHA KEFULA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05a9 HEBREW ACCENT TELISHA QETANA | 05a8 HEBREW ACCENT QADMA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05ab HEBREW ACCENT OLE | 05aa HEBREW ACCENT YERAH BEN YOMO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05ad HEBREW ACCENT DEHI | 05ac HEBREW ACCENT ILUY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05af HEBREW MARK MASORA CIRCLE | 05ae HEBREW ACCENT ZINOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05b1 HEBREW POINT HATAF SEGOL | 05b0 HEBREW POINT SHEVA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05b3 HEBREW POINT HATAF QAMATS | 05b2 HEBREW POINT HATAF PATAH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05b5 HEBREW POINT TSERE | 05b4 HEBREW POINT HIRIQ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05b7 HEBREW POINT PATAH | 05b6 HEBREW POINT SEGOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05b9 HEBREW POINT HOLAM | 05b8 HEBREW POINT QAMATS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05bb HEBREW POINT QUBUTS | 05ba HEBREW POINT HOLAM HASER FOR VAV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05bd HEBREW POINT METEG | 05bc HEBREW POINT DAGESH OR MAPIQ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 05bf HEBREW POINT RAFE | 05be HEBREW PUNCTUATION MAQAF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 05c1 HEBREW POINT SHIN DOT | 05c0 HEBREW PUNCTUATION PASEQ */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 05c3 HEBREW PUNCTUATION SOF PASUQ | 05c2 HEBREW POINT SIN DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05c5 HEBREW MARK LOWER DOT | 05c4 HEBREW MARK UPPER DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 05c7 HEBREW POINT QAMATS QATAN | 05c6 HEBREW PUNCTUATION NUN HAFUKHA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05c9 (null) | 05c8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05cb (null) | 05ca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05cd (null) | 05cc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05cf (null) | 05ce (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05d1 HEBREW LETTER BET | 05d0 HEBREW LETTER ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05d3 HEBREW LETTER DALET | 05d2 HEBREW LETTER GIMEL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05d5 HEBREW LETTER VAV | 05d4 HEBREW LETTER HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05d7 HEBREW LETTER HET | 05d6 HEBREW LETTER ZAYIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05d9 HEBREW LETTER YOD | 05d8 HEBREW LETTER TET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05db HEBREW LETTER KAF | 05da HEBREW LETTER FINAL KAF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05dd HEBREW LETTER FINAL MEM | 05dc HEBREW LETTER LAMED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05df HEBREW LETTER FINAL NUN | 05de HEBREW LETTER MEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05e1 HEBREW LETTER SAMEKH | 05e0 HEBREW LETTER NUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05e3 HEBREW LETTER FINAL PE | 05e2 HEBREW LETTER AYIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05e5 HEBREW LETTER FINAL TSADI | 05e4 HEBREW LETTER PE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05e7 HEBREW LETTER QOF | 05e6 HEBREW LETTER TSADI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05e9 HEBREW LETTER SHIN | 05e8 HEBREW LETTER RESH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 05eb (null) | 05ea HEBREW LETTER TAV */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05ed (null) | 05ec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05ef (null) | 05ee (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05f1 HEBREW LIGATURE YIDDISH VAV YOD | 05f0 HEBREW LIGATURE YIDDISH DOUBLE VAV */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 05f3 HEBREW PUNCTUATION GERESH | 05f2 HEBREW LIGATURE YIDDISH DOUBLE YOD */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 05f5 (null) | 05f4 HEBREW PUNCTUATION GERSHAYIM */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05f7 (null) | 05f6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05f9 (null) | 05f8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05fb (null) | 05fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05fd (null) | 05fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05ff (null) | 05fe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0601 ARABIC SIGN SANAH | 0600 ARABIC NUMBER SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0603 ARABIC SIGN SAFHA | 0602 ARABIC FOOTNOTE MARKER */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0605 (null) | 0604 ARABIC SIGN SAMVAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0607 ARABIC-INDIC FOURTH ROOT | 0606 ARABIC-INDIC CUBE ROOT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 0609 ARABIC-INDIC PER MILLE SIGN | 0608 ARABIC RAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 060b AFGHANI SIGN | 060a ARABIC-INDIC PER TEN THOUSAND SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 060d ARABIC DATE SEPARATOR | 060c ARABIC COMMA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 060f ARABIC SIGN MISRA | 060e ARABIC POETIC VERSE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0611 ARABIC SIGN ALAYHE ASSALLAM | 0610 ARABIC SIGN SALLALLAHOU ALAYHE WASSALLA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0613 ARABIC SIGN RADI ALLAHOU ANHU | 0612 ARABIC SIGN RAHMATULLAH ALAYHE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0615 ARABIC SMALL HIGH TAH | 0614 ARABIC SIGN TAKHALLUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0617 ARABIC SMALL HIGH ZAIN | 0616 ARABIC SMALL HIGH LIGATURE ALEF WITH LA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0619 ARABIC SMALL DAMMA | 0618 ARABIC SMALL FATHA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 061b ARABIC SEMICOLON | 061a ARABIC SMALL KASRA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 061d (null) | 061c (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 061f ARABIC QUESTION MARK | 061e ARABIC TRIPLE DOT PUNCTUATION MARK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0621 ARABIC LETTER HAMZA | 0620 ARABIC LETTER KASHMIRI YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0623 ARABIC LETTER ALEF WITH HAMZA ABOVE | 0622 ARABIC LETTER ALEF WITH MADDA ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0625 ARABIC LETTER ALEF WITH HAMZA BELOW | 0624 ARABIC LETTER WAW WITH HAMZA ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0627 ARABIC LETTER ALEF | 0626 ARABIC LETTER YEH WITH HAMZA ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0629 ARABIC LETTER TEH MARBUTA | 0628 ARABIC LETTER BEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 062b ARABIC LETTER THEH | 062a ARABIC LETTER TEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 062d ARABIC LETTER HAH | 062c ARABIC LETTER JEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 062f ARABIC LETTER DAL | 062e ARABIC LETTER KHAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0631 ARABIC LETTER REH | 0630 ARABIC LETTER THAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0633 ARABIC LETTER SEEN | 0632 ARABIC LETTER ZAIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0635 ARABIC LETTER SAD | 0634 ARABIC LETTER SHEEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0637 ARABIC LETTER TAH | 0636 ARABIC LETTER DAD */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0639 ARABIC LETTER AIN | 0638 ARABIC LETTER ZAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 063b ARABIC LETTER KEHEH WITH TWO DOTS ABOVE | 063a ARABIC LETTER GHAIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 063d ARABIC LETTER FARSI YEH WITH INVERTED V | 063c ARABIC LETTER KEHEH WITH THREE DOTS BEL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 063f ARABIC LETTER FARSI YEH WITH THREE DOTS | 063e ARABIC LETTER FARSI YEH WITH TWO DOTS A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 0641 ARABIC LETTER FEH | 0640 ARABIC TATWEEL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0643 ARABIC LETTER KAF | 0642 ARABIC LETTER QAF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0645 ARABIC LETTER MEEM | 0644 ARABIC LETTER LAM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0647 ARABIC LETTER HEH | 0646 ARABIC LETTER NOON */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0649 ARABIC LETTER ALEF MAKSURA | 0648 ARABIC LETTER WAW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 064b ARABIC FATHATAN | 064a ARABIC LETTER YEH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 064d ARABIC KASRATAN | 064c ARABIC DAMMATAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 064f ARABIC DAMMA | 064e ARABIC FATHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0651 ARABIC SHADDA | 0650 ARABIC KASRA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0653 ARABIC MADDAH ABOVE | 0652 ARABIC SUKUN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0655 ARABIC HAMZA BELOW | 0654 ARABIC HAMZA ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0657 ARABIC INVERTED DAMMA | 0656 ARABIC SUBSCRIPT ALEF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0659 ARABIC ZWARAKAY | 0658 ARABIC MARK NOON GHUNNA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 065b ARABIC VOWEL SIGN INVERTED SMALL V ABOV | 065a ARABIC VOWEL SIGN SMALL V ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 065d ARABIC REVERSED DAMMA | 065c ARABIC VOWEL SIGN DOT BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 065f ARABIC WAVY HAMZA BELOW | 065e ARABIC FATHA WITH TWO DOTS */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0661 ARABIC-INDIC DIGIT ONE | 0660 ARABIC-INDIC DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0663 ARABIC-INDIC DIGIT THREE | 0662 ARABIC-INDIC DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0665 ARABIC-INDIC DIGIT FIVE | 0664 ARABIC-INDIC DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0667 ARABIC-INDIC DIGIT SEVEN | 0666 ARABIC-INDIC DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0669 ARABIC-INDIC DIGIT NINE | 0668 ARABIC-INDIC DIGIT EIGHT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 066b ARABIC DECIMAL SEPARATOR | 066a ARABIC PERCENT SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 066d ARABIC FIVE POINTED STAR | 066c ARABIC THOUSANDS SEPARATOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 066f ARABIC LETTER DOTLESS QAF | 066e ARABIC LETTER DOTLESS BEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 0671 ARABIC LETTER ALEF WASLA | 0670 ARABIC LETTER SUPERSCRIPT ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0673 ARABIC LETTER ALEF WITH WAVY HAMZA BELO | 0672 ARABIC LETTER ALEF WITH WAVY HAMZA ABOV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0675 ARABIC LETTER HIGH HAMZA ALEF | 0674 ARABIC LETTER HIGH HAMZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0677 ARABIC LETTER U WITH HAMZA ABOVE | 0676 ARABIC LETTER HIGH HAMZA WAW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0679 ARABIC LETTER TTEH | 0678 ARABIC LETTER HIGH HAMZA YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 067b ARABIC LETTER BEEH | 067a ARABIC LETTER TTEHEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 067d ARABIC LETTER TEH WITH THREE DOTS ABOVE | 067c ARABIC LETTER TEH WITH RING */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 067f ARABIC LETTER TEHEH | 067e ARABIC LETTER PEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0681 ARABIC LETTER HAH WITH HAMZA ABOVE | 0680 ARABIC LETTER BEHEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0683 ARABIC LETTER NYEH | 0682 ARABIC LETTER HAH WITH TWO DOTS VERTICA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0685 ARABIC LETTER HAH WITH THREE DOTS ABOVE | 0684 ARABIC LETTER DYEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0687 ARABIC LETTER TCHEHEH | 0686 ARABIC LETTER TCHEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0689 ARABIC LETTER DAL WITH RING | 0688 ARABIC LETTER DDAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 068b ARABIC LETTER DAL WITH DOT BELOW AND SM | 068a ARABIC LETTER DAL WITH DOT BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 068d ARABIC LETTER DDAHAL | 068c ARABIC LETTER DAHAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 068f ARABIC LETTER DAL WITH THREE DOTS ABOVE | 068e ARABIC LETTER DUL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0691 ARABIC LETTER RREH | 0690 ARABIC LETTER DAL WITH FOUR DOTS ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0693 ARABIC LETTER REH WITH RING | 0692 ARABIC LETTER REH WITH SMALL V */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0695 ARABIC LETTER REH WITH SMALL V BELOW | 0694 ARABIC LETTER REH WITH DOT BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0697 ARABIC LETTER REH WITH TWO DOTS ABOVE | 0696 ARABIC LETTER REH WITH DOT BELOW AND DO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0699 ARABIC LETTER REH WITH FOUR DOTS ABOVE | 0698 ARABIC LETTER JEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 069b ARABIC LETTER SEEN WITH THREE DOTS BELO | 069a ARABIC LETTER SEEN WITH DOT BELOW AND D */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 069d ARABIC LETTER SAD WITH TWO DOTS BELOW | 069c ARABIC LETTER SEEN WITH THREE DOTS BELO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 069f ARABIC LETTER TAH WITH THREE DOTS ABOVE | 069e ARABIC LETTER SAD WITH THREE DOTS ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06a1 ARABIC LETTER DOTLESS FEH | 06a0 ARABIC LETTER AIN WITH THREE DOTS ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06a3 ARABIC LETTER FEH WITH DOT BELOW | 06a2 ARABIC LETTER FEH WITH DOT MOVED BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06a5 ARABIC LETTER FEH WITH THREE DOTS BELOW | 06a4 ARABIC LETTER VEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06a7 ARABIC LETTER QAF WITH DOT ABOVE | 06a6 ARABIC LETTER PEHEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06a9 ARABIC LETTER KEHEH | 06a8 ARABIC LETTER QAF WITH THREE DOTS ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06ab ARABIC LETTER KAF WITH RING | 06aa ARABIC LETTER SWASH KAF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06ad ARABIC LETTER NG | 06ac ARABIC LETTER KAF WITH DOT ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06af ARABIC LETTER GAF | 06ae ARABIC LETTER KAF WITH THREE DOTS BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06b1 ARABIC LETTER NGOEH | 06b0 ARABIC LETTER GAF WITH RING */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06b3 ARABIC LETTER GUEH | 06b2 ARABIC LETTER GAF WITH TWO DOTS BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06b5 ARABIC LETTER LAM WITH SMALL V | 06b4 ARABIC LETTER GAF WITH THREE DOTS ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06b7 ARABIC LETTER LAM WITH THREE DOTS ABOVE | 06b6 ARABIC LETTER LAM WITH DOT ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06b9 ARABIC LETTER NOON WITH DOT BELOW | 06b8 ARABIC LETTER LAM WITH THREE DOTS BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06bb ARABIC LETTER RNOON | 06ba ARABIC LETTER NOON GHUNNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06bd ARABIC LETTER NOON WITH THREE DOTS ABOV | 06bc ARABIC LETTER NOON WITH RING */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06bf ARABIC LETTER TCHEH WITH DOT ABOVE | 06be ARABIC LETTER HEH DOACHASHMEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06c1 ARABIC LETTER HEH GOAL | 06c0 ARABIC LETTER HEH WITH YEH ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06c3 ARABIC LETTER TEH MARBUTA GOAL | 06c2 ARABIC LETTER HEH GOAL WITH HAMZA ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06c5 ARABIC LETTER KIRGHIZ OE | 06c4 ARABIC LETTER WAW WITH RING */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06c7 ARABIC LETTER U | 06c6 ARABIC LETTER OE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06c9 ARABIC LETTER KIRGHIZ YU | 06c8 ARABIC LETTER YU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06cb ARABIC LETTER VE | 06ca ARABIC LETTER WAW WITH TWO DOTS ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06cd ARABIC LETTER YEH WITH TAIL | 06cc ARABIC LETTER FARSI YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06cf ARABIC LETTER WAW WITH DOT ABOVE | 06ce ARABIC LETTER YEH WITH SMALL V */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06d1 ARABIC LETTER YEH WITH THREE DOTS BELOW | 06d0 ARABIC LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06d3 ARABIC LETTER YEH BARREE WITH HAMZA ABO | 06d2 ARABIC LETTER YEH BARREE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_PUNCT, /* 06d5 ARABIC LETTER AE | 06d4 ARABIC FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06d7 ARABIC SMALL HIGH LIGATURE QAF WITH LAM | 06d6 ARABIC SMALL HIGH LIGATURE SAD WITH LAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06d9 ARABIC SMALL HIGH LAM ALEF | 06d8 ARABIC SMALL HIGH MEEM INITIAL FORM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06db ARABIC SMALL HIGH THREE DOTS | 06da ARABIC SMALL HIGH JEEM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06dd ARABIC END OF AYAH | 06dc ARABIC SMALL HIGH SEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06df ARABIC SMALL HIGH ROUNDED ZERO | 06de ARABIC START OF RUB EL HIZB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06e1 ARABIC SMALL HIGH DOTLESS HEAD OF KHAH | 06e0 ARABIC SMALL HIGH UPRIGHT RECTANGULAR Z */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06e3 ARABIC SMALL LOW SEEN | 06e2 ARABIC SMALL HIGH MEEM ISOLATED FORM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06e5 ARABIC SMALL WAW | 06e4 ARABIC SMALL HIGH MADDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06e7 ARABIC SMALL HIGH YEH | 06e6 ARABIC SMALL YEH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06e9 ARABIC PLACE OF SAJDAH | 06e8 ARABIC SMALL HIGH NOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06eb ARABIC EMPTY CENTRE HIGH STOP | 06ea ARABIC EMPTY CENTRE LOW STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06ed ARABIC SMALL LOW MEEM | 06ec ARABIC ROUNDED HIGH STOP WITH FILLED CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06ef ARABIC LETTER REH WITH INVERTED V | 06ee ARABIC LETTER DAL WITH INVERTED V */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 06f1 EXTENDED ARABIC-INDIC DIGIT ONE | 06f0 EXTENDED ARABIC-INDIC DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 06f3 EXTENDED ARABIC-INDIC DIGIT THREE | 06f2 EXTENDED ARABIC-INDIC DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 06f5 EXTENDED ARABIC-INDIC DIGIT FIVE | 06f4 EXTENDED ARABIC-INDIC DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 06f7 EXTENDED ARABIC-INDIC DIGIT SEVEN | 06f6 EXTENDED ARABIC-INDIC DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 06f9 EXTENDED ARABIC-INDIC DIGIT NINE | 06f8 EXTENDED ARABIC-INDIC DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06fb ARABIC LETTER DAD WITH DOT BELOW | 06fa ARABIC LETTER SHEEN WITH DOT BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 06fd ARABIC SIGN SINDHI AMPERSAND | 06fc ARABIC LETTER GHAIN WITH DOT BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 06ff ARABIC LETTER HEH WITH INVERTED V | 06fe ARABIC SIGN SINDHI POSTPOSITION MEN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0701 SYRIAC SUPRALINEAR FULL STOP | 0700 SYRIAC END OF PARAGRAPH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0703 SYRIAC SUPRALINEAR COLON | 0702 SYRIAC SUBLINEAR FULL STOP */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0705 SYRIAC HORIZONTAL COLON | 0704 SYRIAC SUBLINEAR COLON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0707 SYRIAC COLON SKEWED RIGHT | 0706 SYRIAC COLON SKEWED LEFT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0709 SYRIAC SUBLINEAR COLON SKEWED RIGHT | 0708 SYRIAC SUPRALINEAR COLON SKEWED LEFT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 070b SYRIAC HARKLEAN OBELUS | 070a SYRIAC CONTRACTION */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 070d SYRIAC HARKLEAN ASTERISCUS | 070c SYRIAC HARKLEAN METOBELUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 070f SYRIAC ABBREVIATION MARK | 070e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 0711 SYRIAC LETTER SUPERSCRIPT ALAPH | 0710 SYRIAC LETTER ALAPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0713 SYRIAC LETTER GAMAL | 0712 SYRIAC LETTER BETH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0715 SYRIAC LETTER DALATH | 0714 SYRIAC LETTER GAMAL GARSHUNI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0717 SYRIAC LETTER HE | 0716 SYRIAC LETTER DOTLESS DALATH RISH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0719 SYRIAC LETTER ZAIN | 0718 SYRIAC LETTER WAW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 071b SYRIAC LETTER TETH | 071a SYRIAC LETTER HETH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 071d SYRIAC LETTER YUDH | 071c SYRIAC LETTER TETH GARSHUNI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 071f SYRIAC LETTER KAPH | 071e SYRIAC LETTER YUDH HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0721 SYRIAC LETTER MIM | 0720 SYRIAC LETTER LAMADH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0723 SYRIAC LETTER SEMKATH | 0722 SYRIAC LETTER NUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0725 SYRIAC LETTER E | 0724 SYRIAC LETTER FINAL SEMKATH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0727 SYRIAC LETTER REVERSED PE | 0726 SYRIAC LETTER PE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0729 SYRIAC LETTER QAPH | 0728 SYRIAC LETTER SADHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 072b SYRIAC LETTER SHIN | 072a SYRIAC LETTER RISH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 072d SYRIAC LETTER PERSIAN BHETH | 072c SYRIAC LETTER TAW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 072f SYRIAC LETTER PERSIAN DHALATH | 072e SYRIAC LETTER PERSIAN GHAMAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0731 SYRIAC PTHAHA BELOW | 0730 SYRIAC PTHAHA ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0733 SYRIAC ZQAPHA ABOVE | 0732 SYRIAC PTHAHA DOTTED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0735 SYRIAC ZQAPHA DOTTED | 0734 SYRIAC ZQAPHA BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0737 SYRIAC RBASA BELOW | 0736 SYRIAC RBASA ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0739 SYRIAC DOTTED ZLAMA ANGULAR | 0738 SYRIAC DOTTED ZLAMA HORIZONTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 073b SYRIAC HBASA BELOW | 073a SYRIAC HBASA ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 073d SYRIAC ESASA ABOVE | 073c SYRIAC HBASA-ESASA DOTTED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 073f SYRIAC RWAHA | 073e SYRIAC ESASA BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0741 SYRIAC QUSHSHAYA | 0740 SYRIAC FEMININE DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0743 SYRIAC TWO VERTICAL DOTS ABOVE | 0742 SYRIAC RUKKAKHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0745 SYRIAC THREE DOTS ABOVE | 0744 SYRIAC TWO VERTICAL DOTS BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0747 SYRIAC OBLIQUE LINE ABOVE | 0746 SYRIAC THREE DOTS BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0749 SYRIAC MUSIC | 0748 SYRIAC OBLIQUE LINE BELOW */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 074b (null) | 074a SYRIAC BARREKH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 074d SYRIAC LETTER SOGDIAN ZHAIN | 074c (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 074f SYRIAC LETTER SOGDIAN FE | 074e SYRIAC LETTER SOGDIAN KHAPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0751 ARABIC LETTER BEH WITH DOT BELOW AND TH | 0750 ARABIC LETTER BEH WITH THREE DOTS HORIZ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0753 ARABIC LETTER BEH WITH THREE DOTS POINT | 0752 ARABIC LETTER BEH WITH THREE DOTS POINT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0755 ARABIC LETTER BEH WITH INVERTED SMALL V | 0754 ARABIC LETTER BEH WITH TWO DOTS BELOW A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0757 ARABIC LETTER HAH WITH TWO DOTS ABOVE | 0756 ARABIC LETTER BEH WITH SMALL V */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0759 ARABIC LETTER DAL WITH TWO DOTS VERTICA | 0758 ARABIC LETTER HAH WITH THREE DOTS POINT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 075b ARABIC LETTER REH WITH STROKE | 075a ARABIC LETTER DAL WITH INVERTED SMALL V */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 075d ARABIC LETTER AIN WITH TWO DOTS ABOVE | 075c ARABIC LETTER SEEN WITH FOUR DOTS ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 075f ARABIC LETTER AIN WITH TWO DOTS VERTICA | 075e ARABIC LETTER AIN WITH THREE DOTS POINT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0761 ARABIC LETTER FEH WITH THREE DOTS POINT | 0760 ARABIC LETTER FEH WITH TWO DOTS BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0763 ARABIC LETTER KEHEH WITH THREE DOTS ABO | 0762 ARABIC LETTER KEHEH WITH DOT ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0765 ARABIC LETTER MEEM WITH DOT ABOVE | 0764 ARABIC LETTER KEHEH WITH THREE DOTS POI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0767 ARABIC LETTER NOON WITH TWO DOTS BELOW | 0766 ARABIC LETTER MEEM WITH DOT BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0769 ARABIC LETTER NOON WITH SMALL V | 0768 ARABIC LETTER NOON WITH SMALL TAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 076b ARABIC LETTER REH WITH TWO DOTS VERTICA | 076a ARABIC LETTER LAM WITH BAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 076d ARABIC LETTER SEEN WITH TWO DOTS VERTIC | 076c ARABIC LETTER REH WITH HAMZA ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 076f ARABIC LETTER HAH WITH SMALL ARABIC LET | 076e ARABIC LETTER HAH WITH SMALL ARABIC LET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0771 ARABIC LETTER REH WITH SMALL ARABIC LET | 0770 ARABIC LETTER SEEN WITH SMALL ARABIC LE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0773 ARABIC LETTER ALEF WITH EXTENDED ARABIC | 0772 ARABIC LETTER HAH WITH SMALL ARABIC LET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0775 ARABIC LETTER FARSI YEH WITH EXTENDED A | 0774 ARABIC LETTER ALEF WITH EXTENDED ARABIC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0777 ARABIC LETTER FARSI YEH WITH EXTENDED A | 0776 ARABIC LETTER FARSI YEH WITH EXTENDED A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0779 ARABIC LETTER WAW WITH EXTENDED ARABIC- | 0778 ARABIC LETTER WAW WITH EXTENDED ARABIC- */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 077b ARABIC LETTER YEH BARREE WITH EXTENDED | 077a ARABIC LETTER YEH BARREE WITH EXTENDED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 077d ARABIC LETTER SEEN WITH EXTENDED ARABIC | 077c ARABIC LETTER HAH WITH EXTENDED ARABIC- */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 077f ARABIC LETTER KAF WITH TWO DOTS ABOVE | 077e ARABIC LETTER SEEN WITH INVERTED V */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0781 THAANA LETTER SHAVIYANI | 0780 THAANA LETTER HAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0783 THAANA LETTER RAA | 0782 THAANA LETTER NOONU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0785 THAANA LETTER LHAVIYANI | 0784 THAANA LETTER BAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0787 THAANA LETTER ALIFU | 0786 THAANA LETTER KAAFU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0789 THAANA LETTER MEEMU | 0788 THAANA LETTER VAAVU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 078b THAANA LETTER DHAALU | 078a THAANA LETTER FAAFU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 078d THAANA LETTER LAAMU | 078c THAANA LETTER THAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 078f THAANA LETTER GNAVIYANI | 078e THAANA LETTER GAAFU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0791 THAANA LETTER DAVIYANI | 0790 THAANA LETTER SEENU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0793 THAANA LETTER TAVIYANI | 0792 THAANA LETTER ZAVIYANI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0795 THAANA LETTER PAVIYANI | 0794 THAANA LETTER YAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0797 THAANA LETTER CHAVIYANI | 0796 THAANA LETTER JAVIYANI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0799 THAANA LETTER HHAA | 0798 THAANA LETTER TTAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 079b THAANA LETTER THAALU | 079a THAANA LETTER KHAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 079d THAANA LETTER SHEENU | 079c THAANA LETTER ZAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 079f THAANA LETTER DAADHU | 079e THAANA LETTER SAADHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07a1 THAANA LETTER ZO | 07a0 THAANA LETTER TO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07a3 THAANA LETTER GHAINU | 07a2 THAANA LETTER AINU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07a5 THAANA LETTER WAAVU | 07a4 THAANA LETTER QAAFU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07a7 THAANA AABAAFILI | 07a6 THAANA ABAFILI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07a9 THAANA EEBEEFILI | 07a8 THAANA IBIFILI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07ab THAANA OOBOOFILI | 07aa THAANA UBUFILI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07ad THAANA EYBEYFILI | 07ac THAANA EBEFILI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07af THAANA OABOAFILI | 07ae THAANA OBOFILI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 07b1 THAANA LETTER NAA | 07b0 THAANA SUKUN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07b3 (null) | 07b2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07b5 (null) | 07b4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07b7 (null) | 07b6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07b9 (null) | 07b8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07bb (null) | 07ba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07bd (null) | 07bc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07bf (null) | 07be (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 07c1 NKO DIGIT ONE | 07c0 NKO DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 07c3 NKO DIGIT THREE | 07c2 NKO DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 07c5 NKO DIGIT FIVE | 07c4 NKO DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 07c7 NKO DIGIT SEVEN | 07c6 NKO DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 07c9 NKO DIGIT NINE | 07c8 NKO DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07cb NKO LETTER EE | 07ca NKO LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07cd NKO LETTER E | 07cc NKO LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07cf NKO LETTER OO | 07ce NKO LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07d1 NKO LETTER DAGBASINNA | 07d0 NKO LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07d3 NKO LETTER BA | 07d2 NKO LETTER N */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07d5 NKO LETTER TA | 07d4 NKO LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07d7 NKO LETTER CHA | 07d6 NKO LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07d9 NKO LETTER RA | 07d8 NKO LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07db NKO LETTER SA | 07da NKO LETTER RRA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07dd NKO LETTER FA | 07dc NKO LETTER GBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07df NKO LETTER LA | 07de NKO LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07e1 NKO LETTER MA | 07e0 NKO LETTER NA WOLOSO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07e3 NKO LETTER NA | 07e2 NKO LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07e5 NKO LETTER WA | 07e4 NKO LETTER HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07e7 NKO LETTER NYA WOLOSO | 07e6 NKO LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07e9 NKO LETTER JONA CHA | 07e8 NKO LETTER JONA JA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 07eb NKO COMBINING SHORT HIGH TONE | 07ea NKO LETTER JONA RA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07ed NKO COMBINING SHORT RISING TONE | 07ec NKO COMBINING SHORT LOW TONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07ef NKO COMBINING LONG HIGH TONE | 07ee NKO COMBINING LONG DESCENDING TONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07f1 NKO COMBINING LONG RISING TONE | 07f0 NKO COMBINING LONG LOW TONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07f3 NKO COMBINING DOUBLE DOT ABOVE | 07f2 NKO COMBINING NASALIZATION MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07f5 NKO LOW TONE APOSTROPHE | 07f4 NKO HIGH TONE APOSTROPHE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 07f7 NKO SYMBOL GBAKURUNEN | 07f6 NKO SYMBOL OO DENNEN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 07f9 NKO EXCLAMATION MARK | 07f8 NKO COMMA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 07fb (null) | 07fa NKO LAJANYALAN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07fd (null) | 07fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07ff (null) | 07fe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0801 SAMARITAN LETTER BIT | 0800 SAMARITAN LETTER ALAF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0803 SAMARITAN LETTER DALAT | 0802 SAMARITAN LETTER GAMAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0805 SAMARITAN LETTER BAA | 0804 SAMARITAN LETTER IY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0807 SAMARITAN LETTER IT | 0806 SAMARITAN LETTER ZEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0809 SAMARITAN LETTER YUT | 0808 SAMARITAN LETTER TIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 080b SAMARITAN LETTER LABAT | 080a SAMARITAN LETTER KAAF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 080d SAMARITAN LETTER NUN | 080c SAMARITAN LETTER MIM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 080f SAMARITAN LETTER IN | 080e SAMARITAN LETTER SINGAAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0811 SAMARITAN LETTER TSAADIY | 0810 SAMARITAN LETTER FI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0813 SAMARITAN LETTER RISH | 0812 SAMARITAN LETTER QUF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0815 SAMARITAN LETTER TAAF | 0814 SAMARITAN LETTER SHAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0817 SAMARITAN MARK IN-ALAF | 0816 SAMARITAN MARK IN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0819 SAMARITAN MARK DAGESH | 0818 SAMARITAN MARK OCCLUSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 081b SAMARITAN MARK EPENTHETIC YUT | 081a SAMARITAN MODIFIER LETTER EPENTHETIC YU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 081d SAMARITAN VOWEL SIGN E | 081c SAMARITAN VOWEL SIGN LONG E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 081f SAMARITAN VOWEL SIGN LONG AA | 081e SAMARITAN VOWEL SIGN OVERLONG AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0821 SAMARITAN VOWEL SIGN OVERLONG A | 0820 SAMARITAN VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0823 SAMARITAN VOWEL SIGN A | 0822 SAMARITAN VOWEL SIGN LONG A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0825 SAMARITAN VOWEL SIGN SHORT A | 0824 SAMARITAN MODIFIER LETTER SHORT A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0827 SAMARITAN VOWEL SIGN U | 0826 SAMARITAN VOWEL SIGN LONG U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0829 SAMARITAN VOWEL SIGN LONG I | 0828 SAMARITAN MODIFIER LETTER I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 082b SAMARITAN VOWEL SIGN O | 082a SAMARITAN VOWEL SIGN I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 082d SAMARITAN MARK NEQUDAA | 082c SAMARITAN VOWEL SIGN SUKUN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 082f (null) | 082e (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0831 SAMARITAN PUNCTUATION AFSAAQ | 0830 SAMARITAN PUNCTUATION NEQUDAA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0833 SAMARITAN PUNCTUATION BAU | 0832 SAMARITAN PUNCTUATION ANGED */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0835 SAMARITAN PUNCTUATION SHIYYAALAA | 0834 SAMARITAN PUNCTUATION ATMAAU */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0837 SAMARITAN PUNCTUATION MELODIC QITSA | 0836 SAMARITAN ABBREVIATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0839 SAMARITAN PUNCTUATION QITSA | 0838 SAMARITAN PUNCTUATION ZIQAA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 083b SAMARITAN PUNCTUATION TURU | 083a SAMARITAN PUNCTUATION ZAEF */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 083d SAMARITAN PUNCTUATION SOF MASHFAAT | 083c SAMARITAN PUNCTUATION ARKAANU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 083f (null) | 083e SAMARITAN PUNCTUATION ANNAAU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0841 MANDAIC LETTER AB | 0840 MANDAIC LETTER HALQA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0843 MANDAIC LETTER AD | 0842 MANDAIC LETTER AG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0845 MANDAIC LETTER USHENNA | 0844 MANDAIC LETTER AH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0847 MANDAIC LETTER IT | 0846 MANDAIC LETTER AZ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0849 MANDAIC LETTER AKSA | 0848 MANDAIC LETTER ATT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 084b MANDAIC LETTER AL | 084a MANDAIC LETTER AK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 084d MANDAIC LETTER AN | 084c MANDAIC LETTER AM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 084f MANDAIC LETTER IN | 084e MANDAIC LETTER AS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0851 MANDAIC LETTER ASZ | 0850 MANDAIC LETTER AP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0853 MANDAIC LETTER AR | 0852 MANDAIC LETTER AQ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0855 MANDAIC LETTER AT | 0854 MANDAIC LETTER ASH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0857 MANDAIC LETTER KAD | 0856 MANDAIC LETTER DUSHENNA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 0859 MANDAIC AFFRICATION MARK | 0858 MANDAIC LETTER AIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 085b MANDAIC GEMINATION MARK | 085a MANDAIC VOCALIZATION MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 085d (null) | 085c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 085f (null) | 085e MANDAIC PUNCTUATION */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0861 (null) | 0860 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0863 (null) | 0862 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0865 (null) | 0864 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0867 (null) | 0866 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0869 (null) | 0868 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 086b (null) | 086a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 086d (null) | 086c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 086f (null) | 086e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0871 (null) | 0870 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0873 (null) | 0872 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0875 (null) | 0874 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0877 (null) | 0876 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0879 (null) | 0878 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 087b (null) | 087a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 087d (null) | 087c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 087f (null) | 087e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0881 (null) | 0880 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0883 (null) | 0882 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0885 (null) | 0884 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0887 (null) | 0886 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0889 (null) | 0888 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 088b (null) | 088a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 088d (null) | 088c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 088f (null) | 088e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0891 (null) | 0890 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0893 (null) | 0892 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0895 (null) | 0894 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0897 (null) | 0896 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0899 (null) | 0898 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 089b (null) | 089a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 089d (null) | 089c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 089f (null) | 089e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 08a1 (null) | 08a0 ARABIC LETTER BEH WITH SMALL V BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 08a3 ARABIC LETTER TAH WITH TWO DOTS ABOVE | 08a2 ARABIC LETTER JEEM WITH TWO DOTS ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 08a5 ARABIC LETTER QAF WITH DOT BELOW | 08a4 ARABIC LETTER FEH WITH DOT BELOW AND TH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 08a7 ARABIC LETTER MEEM WITH THREE DOTS ABOV | 08a6 ARABIC LETTER LAM WITH DOUBLE BAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 08a9 ARABIC LETTER YEH WITH TWO DOTS BELOW A | 08a8 ARABIC LETTER YEH WITH TWO DOTS BELOW A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 08ab ARABIC LETTER WAW WITH DOT WITHIN | 08aa ARABIC LETTER REH WITH LOOP */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 08ad (null) | 08ac ARABIC LETTER ROHINGYA YEH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08af (null) | 08ae (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08b1 (null) | 08b0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08b3 (null) | 08b2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08b5 (null) | 08b4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08b7 (null) | 08b6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08b9 (null) | 08b8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08bb (null) | 08ba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08bd (null) | 08bc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08bf (null) | 08be (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08c1 (null) | 08c0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08c3 (null) | 08c2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08c5 (null) | 08c4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08c7 (null) | 08c6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08c9 (null) | 08c8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08cb (null) | 08ca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08cd (null) | 08cc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08cf (null) | 08ce (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08d1 (null) | 08d0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08d3 (null) | 08d2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08d5 (null) | 08d4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08d7 (null) | 08d6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08d9 (null) | 08d8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08db (null) | 08da (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08dd (null) | 08dc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08df (null) | 08de (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08e1 (null) | 08e0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08e3 (null) | 08e2 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08e5 ARABIC CURLY DAMMA | 08e4 ARABIC CURLY FATHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08e7 ARABIC CURLY FATHATAN | 08e6 ARABIC CURLY KASRA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08e9 ARABIC CURLY KASRATAN | 08e8 ARABIC CURLY DAMMATAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08eb ARABIC TONE TWO DOTS ABOVE | 08ea ARABIC TONE ONE DOT ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08ed ARABIC TONE ONE DOT BELOW | 08ec ARABIC TONE LOOP ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08ef ARABIC TONE LOOP BELOW | 08ee ARABIC TONE TWO DOTS BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08f1 ARABIC OPEN DAMMATAN | 08f0 ARABIC OPEN FATHATAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08f3 ARABIC SMALL HIGH WAW | 08f2 ARABIC OPEN KASRATAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08f5 ARABIC FATHA WITH DOT ABOVE | 08f4 ARABIC FATHA WITH RING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08f7 ARABIC LEFT ARROWHEAD ABOVE | 08f6 ARABIC KASRA WITH DOT BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08f9 ARABIC LEFT ARROWHEAD BELOW | 08f8 ARABIC RIGHT ARROWHEAD ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08fb ARABIC DOUBLE RIGHT ARROWHEAD ABOVE | 08fa ARABIC RIGHT ARROWHEAD BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08fd ARABIC RIGHT ARROWHEAD ABOVE WITH DOT | 08fc ARABIC DOUBLE RIGHT ARROWHEAD ABOVE WIT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 08ff (null) | 08fe ARABIC DAMMA WITH DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0901 DEVANAGARI SIGN CANDRABINDU | 0900 DEVANAGARI SIGN INVERTED CANDRABINDU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0903 DEVANAGARI SIGN VISARGA | 0902 DEVANAGARI SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0905 DEVANAGARI LETTER A | 0904 DEVANAGARI LETTER SHORT A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0907 DEVANAGARI LETTER I | 0906 DEVANAGARI LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0909 DEVANAGARI LETTER U | 0908 DEVANAGARI LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 090b DEVANAGARI LETTER VOCALIC R | 090a DEVANAGARI LETTER UU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 090d DEVANAGARI LETTER CANDRA E | 090c DEVANAGARI LETTER VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 090f DEVANAGARI LETTER E | 090e DEVANAGARI LETTER SHORT E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0911 DEVANAGARI LETTER CANDRA O | 0910 DEVANAGARI LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0913 DEVANAGARI LETTER O | 0912 DEVANAGARI LETTER SHORT O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0915 DEVANAGARI LETTER KA | 0914 DEVANAGARI LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0917 DEVANAGARI LETTER GA | 0916 DEVANAGARI LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0919 DEVANAGARI LETTER NGA | 0918 DEVANAGARI LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 091b DEVANAGARI LETTER CHA | 091a DEVANAGARI LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 091d DEVANAGARI LETTER JHA | 091c DEVANAGARI LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 091f DEVANAGARI LETTER TTA | 091e DEVANAGARI LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0921 DEVANAGARI LETTER DDA | 0920 DEVANAGARI LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0923 DEVANAGARI LETTER NNA | 0922 DEVANAGARI LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0925 DEVANAGARI LETTER THA | 0924 DEVANAGARI LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0927 DEVANAGARI LETTER DHA | 0926 DEVANAGARI LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0929 DEVANAGARI LETTER NNNA | 0928 DEVANAGARI LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 092b DEVANAGARI LETTER PHA | 092a DEVANAGARI LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 092d DEVANAGARI LETTER BHA | 092c DEVANAGARI LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 092f DEVANAGARI LETTER YA | 092e DEVANAGARI LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0931 DEVANAGARI LETTER RRA | 0930 DEVANAGARI LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0933 DEVANAGARI LETTER LLA | 0932 DEVANAGARI LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0935 DEVANAGARI LETTER VA | 0934 DEVANAGARI LETTER LLLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0937 DEVANAGARI LETTER SSA | 0936 DEVANAGARI LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0939 DEVANAGARI LETTER HA | 0938 DEVANAGARI LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 093b DEVANAGARI VOWEL SIGN OOE | 093a DEVANAGARI VOWEL SIGN OE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 093d DEVANAGARI SIGN AVAGRAHA | 093c DEVANAGARI SIGN NUKTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 093f DEVANAGARI VOWEL SIGN I | 093e DEVANAGARI VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0941 DEVANAGARI VOWEL SIGN U | 0940 DEVANAGARI VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0943 DEVANAGARI VOWEL SIGN VOCALIC R | 0942 DEVANAGARI VOWEL SIGN UU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0945 DEVANAGARI VOWEL SIGN CANDRA E | 0944 DEVANAGARI VOWEL SIGN VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0947 DEVANAGARI VOWEL SIGN E | 0946 DEVANAGARI VOWEL SIGN SHORT E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0949 DEVANAGARI VOWEL SIGN CANDRA O | 0948 DEVANAGARI VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 094b DEVANAGARI VOWEL SIGN O | 094a DEVANAGARI VOWEL SIGN SHORT O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 094d DEVANAGARI SIGN VIRAMA | 094c DEVANAGARI VOWEL SIGN AU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 094f DEVANAGARI VOWEL SIGN AW | 094e DEVANAGARI VOWEL SIGN PRISHTHAMATRA E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 0951 DEVANAGARI STRESS SIGN UDATTA | 0950 DEVANAGARI OM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0953 DEVANAGARI GRAVE ACCENT | 0952 DEVANAGARI STRESS SIGN ANUDATTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0955 DEVANAGARI VOWEL SIGN CANDRA LONG E | 0954 DEVANAGARI ACUTE ACCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0957 DEVANAGARI VOWEL SIGN UUE | 0956 DEVANAGARI VOWEL SIGN UE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0959 DEVANAGARI LETTER KHHA | 0958 DEVANAGARI LETTER QA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 095b DEVANAGARI LETTER ZA | 095a DEVANAGARI LETTER GHHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 095d DEVANAGARI LETTER RHA | 095c DEVANAGARI LETTER DDDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 095f DEVANAGARI LETTER YYA | 095e DEVANAGARI LETTER FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0961 DEVANAGARI LETTER VOCALIC LL | 0960 DEVANAGARI LETTER VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0963 DEVANAGARI VOWEL SIGN VOCALIC LL | 0962 DEVANAGARI VOWEL SIGN VOCALIC L */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0965 DEVANAGARI DOUBLE DANDA | 0964 DEVANAGARI DANDA */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0967 DEVANAGARI DIGIT ONE | 0966 DEVANAGARI DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0969 DEVANAGARI DIGIT THREE | 0968 DEVANAGARI DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 096b DEVANAGARI DIGIT FIVE | 096a DEVANAGARI DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 096d DEVANAGARI DIGIT SEVEN | 096c DEVANAGARI DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 096f DEVANAGARI DIGIT NINE | 096e DEVANAGARI DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 0971 DEVANAGARI SIGN HIGH SPACING DOT | 0970 DEVANAGARI ABBREVIATION SIGN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0973 DEVANAGARI LETTER OE | 0972 DEVANAGARI LETTER CANDRA A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0975 DEVANAGARI LETTER AW | 0974 DEVANAGARI LETTER OOE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0977 DEVANAGARI LETTER UUE | 0976 DEVANAGARI LETTER UE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0979 DEVANAGARI LETTER ZHA | 0978 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 097b DEVANAGARI LETTER GGA | 097a DEVANAGARI LETTER HEAVY YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 097d DEVANAGARI LETTER GLOTTAL STOP | 097c DEVANAGARI LETTER JJA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 097f DEVANAGARI LETTER BBA | 097e DEVANAGARI LETTER DDDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0981 BENGALI SIGN CANDRABINDU | 0980 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0983 BENGALI SIGN VISARGA | 0982 BENGALI SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0985 BENGALI LETTER A | 0984 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0987 BENGALI LETTER I | 0986 BENGALI LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0989 BENGALI LETTER U | 0988 BENGALI LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 098b BENGALI LETTER VOCALIC R | 098a BENGALI LETTER UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 098d (null) | 098c BENGALI LETTER VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 098f BENGALI LETTER E | 098e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0991 (null) | 0990 BENGALI LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0993 BENGALI LETTER O | 0992 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0995 BENGALI LETTER KA | 0994 BENGALI LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0997 BENGALI LETTER GA | 0996 BENGALI LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0999 BENGALI LETTER NGA | 0998 BENGALI LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 099b BENGALI LETTER CHA | 099a BENGALI LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 099d BENGALI LETTER JHA | 099c BENGALI LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 099f BENGALI LETTER TTA | 099e BENGALI LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09a1 BENGALI LETTER DDA | 09a0 BENGALI LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09a3 BENGALI LETTER NNA | 09a2 BENGALI LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09a5 BENGALI LETTER THA | 09a4 BENGALI LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09a7 BENGALI LETTER DHA | 09a6 BENGALI LETTER DA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 09a9 (null) | 09a8 BENGALI LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09ab BENGALI LETTER PHA | 09aa BENGALI LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09ad BENGALI LETTER BHA | 09ac BENGALI LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09af BENGALI LETTER YA | 09ae BENGALI LETTER MA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 09b1 (null) | 09b0 BENGALI LETTER RA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 09b3 (null) | 09b2 BENGALI LETTER LA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09b5 (null) | 09b4 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09b7 BENGALI LETTER SSA | 09b6 BENGALI LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09b9 BENGALI LETTER HA | 09b8 BENGALI LETTER SA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09bb (null) | 09ba (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 09bd BENGALI SIGN AVAGRAHA | 09bc BENGALI SIGN NUKTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09bf BENGALI VOWEL SIGN I | 09be BENGALI VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09c1 BENGALI VOWEL SIGN U | 09c0 BENGALI VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09c3 BENGALI VOWEL SIGN VOCALIC R | 09c2 BENGALI VOWEL SIGN UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 09c5 (null) | 09c4 BENGALI VOWEL SIGN VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 09c7 BENGALI VOWEL SIGN E | 09c6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 09c9 (null) | 09c8 BENGALI VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 09cb BENGALI VOWEL SIGN O | 09ca (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09cd BENGALI SIGN VIRAMA | 09cc BENGALI VOWEL SIGN AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 09cf (null) | 09ce BENGALI LETTER KHANDA TA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09d1 (null) | 09d0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09d3 (null) | 09d2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09d5 (null) | 09d4 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 09d7 BENGALI AU LENGTH MARK | 09d6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09d9 (null) | 09d8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09db (null) | 09da (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09dd BENGALI LETTER RHA | 09dc BENGALI LETTER RRA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 09df BENGALI LETTER YYA | 09de (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09e1 BENGALI LETTER VOCALIC LL | 09e0 BENGALI LETTER VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09e3 BENGALI VOWEL SIGN VOCALIC LL | 09e2 BENGALI VOWEL SIGN VOCALIC L */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09e5 (null) | 09e4 (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 09e7 BENGALI DIGIT ONE | 09e6 BENGALI DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 09e9 BENGALI DIGIT THREE | 09e8 BENGALI DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 09eb BENGALI DIGIT FIVE | 09ea BENGALI DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 09ed BENGALI DIGIT SEVEN | 09ec BENGALI DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 09ef BENGALI DIGIT NINE | 09ee BENGALI DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09f1 BENGALI LETTER RA WITH LOWER DIAGONAL | 09f0 BENGALI LETTER RA WITH MIDDLE DIAGONAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09f3 BENGALI RUPEE SIGN | 09f2 BENGALI RUPEE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09f5 BENGALI CURRENCY NUMERATOR TWO | 09f4 BENGALI CURRENCY NUMERATOR ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09f7 BENGALI CURRENCY NUMERATOR FOUR | 09f6 BENGALI CURRENCY NUMERATOR THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09f9 BENGALI CURRENCY DENOMINATOR SIXTEEN | 09f8 BENGALI CURRENCY NUMERATOR ONE LESS THA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09fb BENGALI GANDA MARK | 09fa BENGALI ISSHAR */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09fd (null) | 09fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09ff (null) | 09fe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0a01 GURMUKHI SIGN ADAK BINDI | 0a00 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0a03 GURMUKHI SIGN VISARGA | 0a02 GURMUKHI SIGN BINDI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0a05 GURMUKHI LETTER A | 0a04 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a07 GURMUKHI LETTER I | 0a06 GURMUKHI LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a09 GURMUKHI LETTER U | 0a08 GURMUKHI LETTER II */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0a0b (null) | 0a0a GURMUKHI LETTER UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a0d (null) | 0a0c (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0a0f GURMUKHI LETTER EE | 0a0e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0a11 (null) | 0a10 GURMUKHI LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0a13 GURMUKHI LETTER OO | 0a12 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a15 GURMUKHI LETTER KA | 0a14 GURMUKHI LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a17 GURMUKHI LETTER GA | 0a16 GURMUKHI LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a19 GURMUKHI LETTER NGA | 0a18 GURMUKHI LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a1b GURMUKHI LETTER CHA | 0a1a GURMUKHI LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a1d GURMUKHI LETTER JHA | 0a1c GURMUKHI LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a1f GURMUKHI LETTER TTA | 0a1e GURMUKHI LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a21 GURMUKHI LETTER DDA | 0a20 GURMUKHI LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a23 GURMUKHI LETTER NNA | 0a22 GURMUKHI LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a25 GURMUKHI LETTER THA | 0a24 GURMUKHI LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a27 GURMUKHI LETTER DHA | 0a26 GURMUKHI LETTER DA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0a29 (null) | 0a28 GURMUKHI LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a2b GURMUKHI LETTER PHA | 0a2a GURMUKHI LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a2d GURMUKHI LETTER BHA | 0a2c GURMUKHI LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a2f GURMUKHI LETTER YA | 0a2e GURMUKHI LETTER MA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0a31 (null) | 0a30 GURMUKHI LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a33 GURMUKHI LETTER LLA | 0a32 GURMUKHI LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0a35 GURMUKHI LETTER VA | 0a34 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0a37 (null) | 0a36 GURMUKHI LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a39 GURMUKHI LETTER HA | 0a38 GURMUKHI LETTER SA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a3b (null) | 0a3a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0a3d (null) | 0a3c GURMUKHI SIGN NUKTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0a3f GURMUKHI VOWEL SIGN I | 0a3e GURMUKHI VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0a41 GURMUKHI VOWEL SIGN U | 0a40 GURMUKHI VOWEL SIGN II */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0a43 (null) | 0a42 GURMUKHI VOWEL SIGN UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a45 (null) | 0a44 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0a47 GURMUKHI VOWEL SIGN EE | 0a46 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0a49 (null) | 0a48 GURMUKHI VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0a4b GURMUKHI VOWEL SIGN OO | 0a4a (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0a4d GURMUKHI SIGN VIRAMA | 0a4c GURMUKHI VOWEL SIGN AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a4f (null) | 0a4e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0a51 GURMUKHI SIGN UDAAT | 0a50 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a53 (null) | 0a52 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a55 (null) | 0a54 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a57 (null) | 0a56 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0a59 GURMUKHI LETTER KHHA | 0a58 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a5b GURMUKHI LETTER ZA | 0a5a GURMUKHI LETTER GHHA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0a5d (null) | 0a5c GURMUKHI LETTER RRA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0a5f (null) | 0a5e GURMUKHI LETTER FA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a61 (null) | 0a60 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a63 (null) | 0a62 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a65 (null) | 0a64 (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0a67 GURMUKHI DIGIT ONE | 0a66 GURMUKHI DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0a69 GURMUKHI DIGIT THREE | 0a68 GURMUKHI DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0a6b GURMUKHI DIGIT FIVE | 0a6a GURMUKHI DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0a6d GURMUKHI DIGIT SEVEN | 0a6c GURMUKHI DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0a6f GURMUKHI DIGIT NINE | 0a6e GURMUKHI DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0a71 GURMUKHI ADDAK | 0a70 GURMUKHI TIPPI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a73 GURMUKHI URA | 0a72 GURMUKHI IRI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 0a75 GURMUKHI SIGN YAKASH | 0a74 GURMUKHI EK ONKAR */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a77 (null) | 0a76 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a79 (null) | 0a78 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a7b (null) | 0a7a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a7d (null) | 0a7c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a7f (null) | 0a7e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0a81 GUJARATI SIGN CANDRABINDU | 0a80 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0a83 GUJARATI SIGN VISARGA | 0a82 GUJARATI SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0a85 GUJARATI LETTER A | 0a84 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a87 GUJARATI LETTER I | 0a86 GUJARATI LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a89 GUJARATI LETTER U | 0a88 GUJARATI LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a8b GUJARATI LETTER VOCALIC R | 0a8a GUJARATI LETTER UU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a8d GUJARATI VOWEL CANDRA E | 0a8c GUJARATI LETTER VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0a8f GUJARATI LETTER E | 0a8e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a91 GUJARATI VOWEL CANDRA O | 0a90 GUJARATI LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0a93 GUJARATI LETTER O | 0a92 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a95 GUJARATI LETTER KA | 0a94 GUJARATI LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a97 GUJARATI LETTER GA | 0a96 GUJARATI LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a99 GUJARATI LETTER NGA | 0a98 GUJARATI LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a9b GUJARATI LETTER CHA | 0a9a GUJARATI LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a9d GUJARATI LETTER JHA | 0a9c GUJARATI LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a9f GUJARATI LETTER TTA | 0a9e GUJARATI LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0aa1 GUJARATI LETTER DDA | 0aa0 GUJARATI LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0aa3 GUJARATI LETTER NNA | 0aa2 GUJARATI LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0aa5 GUJARATI LETTER THA | 0aa4 GUJARATI LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0aa7 GUJARATI LETTER DHA | 0aa6 GUJARATI LETTER DA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0aa9 (null) | 0aa8 GUJARATI LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0aab GUJARATI LETTER PHA | 0aaa GUJARATI LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0aad GUJARATI LETTER BHA | 0aac GUJARATI LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0aaf GUJARATI LETTER YA | 0aae GUJARATI LETTER MA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0ab1 (null) | 0ab0 GUJARATI LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ab3 GUJARATI LETTER LLA | 0ab2 GUJARATI LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0ab5 GUJARATI LETTER VA | 0ab4 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ab7 GUJARATI LETTER SSA | 0ab6 GUJARATI LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ab9 GUJARATI LETTER HA | 0ab8 GUJARATI LETTER SA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0abb (null) | 0aba (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 0abd GUJARATI SIGN AVAGRAHA | 0abc GUJARATI SIGN NUKTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0abf GUJARATI VOWEL SIGN I | 0abe GUJARATI VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ac1 GUJARATI VOWEL SIGN U | 0ac0 GUJARATI VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ac3 GUJARATI VOWEL SIGN VOCALIC R | 0ac2 GUJARATI VOWEL SIGN UU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ac5 GUJARATI VOWEL SIGN CANDRA E | 0ac4 GUJARATI VOWEL SIGN VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0ac7 GUJARATI VOWEL SIGN E | 0ac6 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ac9 GUJARATI VOWEL SIGN CANDRA O | 0ac8 GUJARATI VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0acb GUJARATI VOWEL SIGN O | 0aca (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0acd GUJARATI SIGN VIRAMA | 0acc GUJARATI VOWEL SIGN AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0acf (null) | 0ace (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0ad1 (null) | 0ad0 GUJARATI OM */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ad3 (null) | 0ad2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ad5 (null) | 0ad4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ad7 (null) | 0ad6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ad9 (null) | 0ad8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0adb (null) | 0ada (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0add (null) | 0adc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0adf (null) | 0ade (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ae1 GUJARATI LETTER VOCALIC LL | 0ae0 GUJARATI LETTER VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ae3 GUJARATI VOWEL SIGN VOCALIC LL | 0ae2 GUJARATI VOWEL SIGN VOCALIC L */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ae5 (null) | 0ae4 (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ae7 GUJARATI DIGIT ONE | 0ae6 GUJARATI DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ae9 GUJARATI DIGIT THREE | 0ae8 GUJARATI DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0aeb GUJARATI DIGIT FIVE | 0aea GUJARATI DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0aed GUJARATI DIGIT SEVEN | 0aec GUJARATI DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0aef GUJARATI DIGIT NINE | 0aee GUJARATI DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 0af1 GUJARATI RUPEE SIGN | 0af0 GUJARATI ABBREVIATION SIGN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0af3 (null) | 0af2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0af5 (null) | 0af4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0af7 (null) | 0af6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0af9 (null) | 0af8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0afb (null) | 0afa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0afd (null) | 0afc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0aff (null) | 0afe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0b01 ORIYA SIGN CANDRABINDU | 0b00 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b03 ORIYA SIGN VISARGA | 0b02 ORIYA SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0b05 ORIYA LETTER A | 0b04 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b07 ORIYA LETTER I | 0b06 ORIYA LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b09 ORIYA LETTER U | 0b08 ORIYA LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b0b ORIYA LETTER VOCALIC R | 0b0a ORIYA LETTER UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0b0d (null) | 0b0c ORIYA LETTER VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0b0f ORIYA LETTER E | 0b0e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0b11 (null) | 0b10 ORIYA LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0b13 ORIYA LETTER O | 0b12 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b15 ORIYA LETTER KA | 0b14 ORIYA LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b17 ORIYA LETTER GA | 0b16 ORIYA LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b19 ORIYA LETTER NGA | 0b18 ORIYA LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b1b ORIYA LETTER CHA | 0b1a ORIYA LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b1d ORIYA LETTER JHA | 0b1c ORIYA LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b1f ORIYA LETTER TTA | 0b1e ORIYA LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b21 ORIYA LETTER DDA | 0b20 ORIYA LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b23 ORIYA LETTER NNA | 0b22 ORIYA LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b25 ORIYA LETTER THA | 0b24 ORIYA LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b27 ORIYA LETTER DHA | 0b26 ORIYA LETTER DA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0b29 (null) | 0b28 ORIYA LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b2b ORIYA LETTER PHA | 0b2a ORIYA LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b2d ORIYA LETTER BHA | 0b2c ORIYA LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b2f ORIYA LETTER YA | 0b2e ORIYA LETTER MA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0b31 (null) | 0b30 ORIYA LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b33 ORIYA LETTER LLA | 0b32 ORIYA LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0b35 ORIYA LETTER VA | 0b34 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b37 ORIYA LETTER SSA | 0b36 ORIYA LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b39 ORIYA LETTER HA | 0b38 ORIYA LETTER SA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b3b (null) | 0b3a (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 0b3d ORIYA SIGN AVAGRAHA | 0b3c ORIYA SIGN NUKTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b3f ORIYA VOWEL SIGN I | 0b3e ORIYA VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b41 ORIYA VOWEL SIGN U | 0b40 ORIYA VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b43 ORIYA VOWEL SIGN VOCALIC R | 0b42 ORIYA VOWEL SIGN UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0b45 (null) | 0b44 ORIYA VOWEL SIGN VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0b47 ORIYA VOWEL SIGN E | 0b46 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0b49 (null) | 0b48 ORIYA VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0b4b ORIYA VOWEL SIGN O | 0b4a (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b4d ORIYA SIGN VIRAMA | 0b4c ORIYA VOWEL SIGN AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b4f (null) | 0b4e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b51 (null) | 0b50 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b53 (null) | 0b52 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b55 (null) | 0b54 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b57 ORIYA AU LENGTH MARK | 0b56 ORIYA AI LENGTH MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b59 (null) | 0b58 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b5b (null) | 0b5a (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b5d ORIYA LETTER RHA | 0b5c ORIYA LETTER RRA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0b5f ORIYA LETTER YYA | 0b5e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b61 ORIYA LETTER VOCALIC LL | 0b60 ORIYA LETTER VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b63 ORIYA VOWEL SIGN VOCALIC LL | 0b62 ORIYA VOWEL SIGN VOCALIC L */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b65 (null) | 0b64 (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0b67 ORIYA DIGIT ONE | 0b66 ORIYA DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0b69 ORIYA DIGIT THREE | 0b68 ORIYA DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0b6b ORIYA DIGIT FIVE | 0b6a ORIYA DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0b6d ORIYA DIGIT SEVEN | 0b6c ORIYA DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0b6f ORIYA DIGIT NINE | 0b6e ORIYA DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 0b71 ORIYA LETTER WA | 0b70 ORIYA ISSHAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b73 ORIYA FRACTION ONE HALF | 0b72 ORIYA FRACTION ONE QUARTER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b75 ORIYA FRACTION ONE SIXTEENTH | 0b74 ORIYA FRACTION THREE QUARTERS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b77 ORIYA FRACTION THREE SIXTEENTHS | 0b76 ORIYA FRACTION ONE EIGHTH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b79 (null) | 0b78 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b7b (null) | 0b7a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b7d (null) | 0b7c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b7f (null) | 0b7e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b81 (null) | 0b80 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 0b83 TAMIL SIGN VISARGA | 0b82 TAMIL SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0b85 TAMIL LETTER A | 0b84 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b87 TAMIL LETTER I | 0b86 TAMIL LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b89 TAMIL LETTER U | 0b88 TAMIL LETTER II */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0b8b (null) | 0b8a TAMIL LETTER UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b8d (null) | 0b8c (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b8f TAMIL LETTER EE | 0b8e TAMIL LETTER E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0b91 (null) | 0b90 TAMIL LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b93 TAMIL LETTER OO | 0b92 TAMIL LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b95 TAMIL LETTER KA | 0b94 TAMIL LETTER AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b97 (null) | 0b96 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0b99 TAMIL LETTER NGA | 0b98 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0b9b (null) | 0b9a TAMIL LETTER CA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0b9d (null) | 0b9c TAMIL LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b9f TAMIL LETTER TTA | 0b9e TAMIL LETTER NYA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ba1 (null) | 0ba0 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0ba3 TAMIL LETTER NNA | 0ba2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0ba5 (null) | 0ba4 TAMIL LETTER TA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ba7 (null) | 0ba6 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ba9 TAMIL LETTER NNNA | 0ba8 TAMIL LETTER NA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0bab (null) | 0baa TAMIL LETTER PA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bad (null) | 0bac (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0baf TAMIL LETTER YA | 0bae TAMIL LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0bb1 TAMIL LETTER RRA | 0bb0 TAMIL LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0bb3 TAMIL LETTER LLA | 0bb2 TAMIL LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0bb5 TAMIL LETTER VA | 0bb4 TAMIL LETTER LLLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0bb7 TAMIL LETTER SSA | 0bb6 TAMIL LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0bb9 TAMIL LETTER HA | 0bb8 TAMIL LETTER SA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bbb (null) | 0bba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bbd (null) | 0bbc (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bbf TAMIL VOWEL SIGN I | 0bbe TAMIL VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bc1 TAMIL VOWEL SIGN U | 0bc0 TAMIL VOWEL SIGN II */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0bc3 (null) | 0bc2 TAMIL VOWEL SIGN UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bc5 (null) | 0bc4 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bc7 TAMIL VOWEL SIGN EE | 0bc6 TAMIL VOWEL SIGN E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0bc9 (null) | 0bc8 TAMIL VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bcb TAMIL VOWEL SIGN OO | 0bca TAMIL VOWEL SIGN O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bcd TAMIL SIGN VIRAMA | 0bcc TAMIL VOWEL SIGN AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bcf (null) | 0bce (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0bd1 (null) | 0bd0 TAMIL OM */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bd3 (null) | 0bd2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bd5 (null) | 0bd4 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0bd7 TAMIL AU LENGTH MARK | 0bd6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bd9 (null) | 0bd8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bdb (null) | 0bda (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bdd (null) | 0bdc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bdf (null) | 0bde (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0be1 (null) | 0be0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0be3 (null) | 0be2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0be5 (null) | 0be4 (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0be7 TAMIL DIGIT ONE | 0be6 TAMIL DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0be9 TAMIL DIGIT THREE | 0be8 TAMIL DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0beb TAMIL DIGIT FIVE | 0bea TAMIL DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0bed TAMIL DIGIT SEVEN | 0bec TAMIL DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0bef TAMIL DIGIT NINE | 0bee TAMIL DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bf1 TAMIL NUMBER ONE HUNDRED | 0bf0 TAMIL NUMBER TEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bf3 TAMIL DAY SIGN | 0bf2 TAMIL NUMBER ONE THOUSAND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bf5 TAMIL YEAR SIGN | 0bf4 TAMIL MONTH SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bf7 TAMIL CREDIT SIGN | 0bf6 TAMIL DEBIT SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bf9 TAMIL RUPEE SIGN | 0bf8 TAMIL AS ABOVE SIGN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0bfb (null) | 0bfa TAMIL NUMBER SIGN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bfd (null) | 0bfc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bff (null) | 0bfe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0c01 TELUGU SIGN CANDRABINDU | 0c00 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c03 TELUGU SIGN VISARGA | 0c02 TELUGU SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0c05 TELUGU LETTER A | 0c04 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c07 TELUGU LETTER I | 0c06 TELUGU LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c09 TELUGU LETTER U | 0c08 TELUGU LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c0b TELUGU LETTER VOCALIC R | 0c0a TELUGU LETTER UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0c0d (null) | 0c0c TELUGU LETTER VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c0f TELUGU LETTER EE | 0c0e TELUGU LETTER E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0c11 (null) | 0c10 TELUGU LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c13 TELUGU LETTER OO | 0c12 TELUGU LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c15 TELUGU LETTER KA | 0c14 TELUGU LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c17 TELUGU LETTER GA | 0c16 TELUGU LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c19 TELUGU LETTER NGA | 0c18 TELUGU LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c1b TELUGU LETTER CHA | 0c1a TELUGU LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c1d TELUGU LETTER JHA | 0c1c TELUGU LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c1f TELUGU LETTER TTA | 0c1e TELUGU LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c21 TELUGU LETTER DDA | 0c20 TELUGU LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c23 TELUGU LETTER NNA | 0c22 TELUGU LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c25 TELUGU LETTER THA | 0c24 TELUGU LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c27 TELUGU LETTER DHA | 0c26 TELUGU LETTER DA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0c29 (null) | 0c28 TELUGU LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c2b TELUGU LETTER PHA | 0c2a TELUGU LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c2d TELUGU LETTER BHA | 0c2c TELUGU LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c2f TELUGU LETTER YA | 0c2e TELUGU LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c31 TELUGU LETTER RRA | 0c30 TELUGU LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c33 TELUGU LETTER LLA | 0c32 TELUGU LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0c35 TELUGU LETTER VA | 0c34 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c37 TELUGU LETTER SSA | 0c36 TELUGU LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c39 TELUGU LETTER HA | 0c38 TELUGU LETTER SA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c3b (null) | 0c3a (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0c3d TELUGU SIGN AVAGRAHA | 0c3c (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c3f TELUGU VOWEL SIGN I | 0c3e TELUGU VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c41 TELUGU VOWEL SIGN U | 0c40 TELUGU VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c43 TELUGU VOWEL SIGN VOCALIC R | 0c42 TELUGU VOWEL SIGN UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0c45 (null) | 0c44 TELUGU VOWEL SIGN VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c47 TELUGU VOWEL SIGN EE | 0c46 TELUGU VOWEL SIGN E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0c49 (null) | 0c48 TELUGU VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c4b TELUGU VOWEL SIGN OO | 0c4a TELUGU VOWEL SIGN O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c4d TELUGU SIGN VIRAMA | 0c4c TELUGU VOWEL SIGN AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c4f (null) | 0c4e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c51 (null) | 0c50 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c53 (null) | 0c52 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0c55 TELUGU LENGTH MARK | 0c54 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0c57 (null) | 0c56 TELUGU AI LENGTH MARK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c59 TELUGU LETTER DZA | 0c58 TELUGU LETTER TSA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c5b (null) | 0c5a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c5d (null) | 0c5c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c5f (null) | 0c5e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c61 TELUGU LETTER VOCALIC LL | 0c60 TELUGU LETTER VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c63 TELUGU VOWEL SIGN VOCALIC LL | 0c62 TELUGU VOWEL SIGN VOCALIC L */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c65 (null) | 0c64 (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0c67 TELUGU DIGIT ONE | 0c66 TELUGU DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0c69 TELUGU DIGIT THREE | 0c68 TELUGU DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0c6b TELUGU DIGIT FIVE | 0c6a TELUGU DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0c6d TELUGU DIGIT SEVEN | 0c6c TELUGU DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0c6f TELUGU DIGIT NINE | 0c6e TELUGU DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c71 (null) | 0c70 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c73 (null) | 0c72 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c75 (null) | 0c74 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c77 (null) | 0c76 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c79 TELUGU FRACTION DIGIT ONE FOR ODD POWER | 0c78 TELUGU FRACTION DIGIT ZERO FOR ODD POWE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c7b TELUGU FRACTION DIGIT THREE FOR ODD POW | 0c7a TELUGU FRACTION DIGIT TWO FOR ODD POWER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c7d TELUGU FRACTION DIGIT TWO FOR EVEN POWE | 0c7c TELUGU FRACTION DIGIT ONE FOR EVEN POWE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c7f TELUGU SIGN TUUMU | 0c7e TELUGU FRACTION DIGIT THREE FOR EVEN PO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c81 (null) | 0c80 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c83 KANNADA SIGN VISARGA | 0c82 KANNADA SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0c85 KANNADA LETTER A | 0c84 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c87 KANNADA LETTER I | 0c86 KANNADA LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c89 KANNADA LETTER U | 0c88 KANNADA LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c8b KANNADA LETTER VOCALIC R | 0c8a KANNADA LETTER UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0c8d (null) | 0c8c KANNADA LETTER VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c8f KANNADA LETTER EE | 0c8e KANNADA LETTER E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0c91 (null) | 0c90 KANNADA LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c93 KANNADA LETTER OO | 0c92 KANNADA LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c95 KANNADA LETTER KA | 0c94 KANNADA LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c97 KANNADA LETTER GA | 0c96 KANNADA LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c99 KANNADA LETTER NGA | 0c98 KANNADA LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c9b KANNADA LETTER CHA | 0c9a KANNADA LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c9d KANNADA LETTER JHA | 0c9c KANNADA LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c9f KANNADA LETTER TTA | 0c9e KANNADA LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ca1 KANNADA LETTER DDA | 0ca0 KANNADA LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ca3 KANNADA LETTER NNA | 0ca2 KANNADA LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ca5 KANNADA LETTER THA | 0ca4 KANNADA LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ca7 KANNADA LETTER DHA | 0ca6 KANNADA LETTER DA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0ca9 (null) | 0ca8 KANNADA LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0cab KANNADA LETTER PHA | 0caa KANNADA LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0cad KANNADA LETTER BHA | 0cac KANNADA LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0caf KANNADA LETTER YA | 0cae KANNADA LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0cb1 KANNADA LETTER RRA | 0cb0 KANNADA LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0cb3 KANNADA LETTER LLA | 0cb2 KANNADA LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0cb5 KANNADA LETTER VA | 0cb4 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0cb7 KANNADA LETTER SSA | 0cb6 KANNADA LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0cb9 KANNADA LETTER HA | 0cb8 KANNADA LETTER SA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cbb (null) | 0cba (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 0cbd KANNADA SIGN AVAGRAHA | 0cbc KANNADA SIGN NUKTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0cbf KANNADA VOWEL SIGN I | 0cbe KANNADA VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0cc1 KANNADA VOWEL SIGN U | 0cc0 KANNADA VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0cc3 KANNADA VOWEL SIGN VOCALIC R | 0cc2 KANNADA VOWEL SIGN UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0cc5 (null) | 0cc4 KANNADA VOWEL SIGN VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0cc7 KANNADA VOWEL SIGN EE | 0cc6 KANNADA VOWEL SIGN E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0cc9 (null) | 0cc8 KANNADA VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ccb KANNADA VOWEL SIGN OO | 0cca KANNADA VOWEL SIGN O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ccd KANNADA SIGN VIRAMA | 0ccc KANNADA VOWEL SIGN AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ccf (null) | 0cce (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cd1 (null) | 0cd0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cd3 (null) | 0cd2 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0cd5 KANNADA LENGTH MARK | 0cd4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0cd7 (null) | 0cd6 KANNADA AI LENGTH MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cd9 (null) | 0cd8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cdb (null) | 0cda (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cdd (null) | 0cdc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0cdf (null) | 0cde KANNADA LETTER FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ce1 KANNADA LETTER VOCALIC LL | 0ce0 KANNADA LETTER VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ce3 KANNADA VOWEL SIGN VOCALIC LL | 0ce2 KANNADA VOWEL SIGN VOCALIC L */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ce5 (null) | 0ce4 (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ce7 KANNADA DIGIT ONE | 0ce6 KANNADA DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ce9 KANNADA DIGIT THREE | 0ce8 KANNADA DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ceb KANNADA DIGIT FIVE | 0cea KANNADA DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ced KANNADA DIGIT SEVEN | 0cec KANNADA DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0cef KANNADA DIGIT NINE | 0cee KANNADA DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0cf1 KANNADA SIGN JIHVAMULIYA | 0cf0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0cf3 (null) | 0cf2 KANNADA SIGN UPADHMANIYA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cf5 (null) | 0cf4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cf7 (null) | 0cf6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cf9 (null) | 0cf8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cfb (null) | 0cfa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cfd (null) | 0cfc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cff (null) | 0cfe (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d01 (null) | 0d00 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d03 MALAYALAM SIGN VISARGA | 0d02 MALAYALAM SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0d05 MALAYALAM LETTER A | 0d04 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d07 MALAYALAM LETTER I | 0d06 MALAYALAM LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d09 MALAYALAM LETTER U | 0d08 MALAYALAM LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d0b MALAYALAM LETTER VOCALIC R | 0d0a MALAYALAM LETTER UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0d0d (null) | 0d0c MALAYALAM LETTER VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d0f MALAYALAM LETTER EE | 0d0e MALAYALAM LETTER E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0d11 (null) | 0d10 MALAYALAM LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d13 MALAYALAM LETTER OO | 0d12 MALAYALAM LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d15 MALAYALAM LETTER KA | 0d14 MALAYALAM LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d17 MALAYALAM LETTER GA | 0d16 MALAYALAM LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d19 MALAYALAM LETTER NGA | 0d18 MALAYALAM LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d1b MALAYALAM LETTER CHA | 0d1a MALAYALAM LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d1d MALAYALAM LETTER JHA | 0d1c MALAYALAM LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d1f MALAYALAM LETTER TTA | 0d1e MALAYALAM LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d21 MALAYALAM LETTER DDA | 0d20 MALAYALAM LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d23 MALAYALAM LETTER NNA | 0d22 MALAYALAM LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d25 MALAYALAM LETTER THA | 0d24 MALAYALAM LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d27 MALAYALAM LETTER DHA | 0d26 MALAYALAM LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d29 MALAYALAM LETTER NNNA | 0d28 MALAYALAM LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d2b MALAYALAM LETTER PHA | 0d2a MALAYALAM LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d2d MALAYALAM LETTER BHA | 0d2c MALAYALAM LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d2f MALAYALAM LETTER YA | 0d2e MALAYALAM LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d31 MALAYALAM LETTER RRA | 0d30 MALAYALAM LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d33 MALAYALAM LETTER LLA | 0d32 MALAYALAM LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d35 MALAYALAM LETTER VA | 0d34 MALAYALAM LETTER LLLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d37 MALAYALAM LETTER SSA | 0d36 MALAYALAM LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d39 MALAYALAM LETTER HA | 0d38 MALAYALAM LETTER SA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0d3b (null) | 0d3a MALAYALAM LETTER TTTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0d3d MALAYALAM SIGN AVAGRAHA | 0d3c (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d3f MALAYALAM VOWEL SIGN I | 0d3e MALAYALAM VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d41 MALAYALAM VOWEL SIGN U | 0d40 MALAYALAM VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d43 MALAYALAM VOWEL SIGN VOCALIC R | 0d42 MALAYALAM VOWEL SIGN UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0d45 (null) | 0d44 MALAYALAM VOWEL SIGN VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d47 MALAYALAM VOWEL SIGN EE | 0d46 MALAYALAM VOWEL SIGN E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0d49 (null) | 0d48 MALAYALAM VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d4b MALAYALAM VOWEL SIGN OO | 0d4a MALAYALAM VOWEL SIGN O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d4d MALAYALAM SIGN VIRAMA | 0d4c MALAYALAM VOWEL SIGN AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0d4f (null) | 0d4e MALAYALAM LETTER DOT REPH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d51 (null) | 0d50 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d53 (null) | 0d52 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d55 (null) | 0d54 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0d57 MALAYALAM AU LENGTH MARK | 0d56 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d59 (null) | 0d58 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d5b (null) | 0d5a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d5d (null) | 0d5c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d5f (null) | 0d5e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d61 MALAYALAM LETTER VOCALIC LL | 0d60 MALAYALAM LETTER VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d63 MALAYALAM VOWEL SIGN VOCALIC LL | 0d62 MALAYALAM VOWEL SIGN VOCALIC L */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d65 (null) | 0d64 (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0d67 MALAYALAM DIGIT ONE | 0d66 MALAYALAM DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0d69 MALAYALAM DIGIT THREE | 0d68 MALAYALAM DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0d6b MALAYALAM DIGIT FIVE | 0d6a MALAYALAM DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0d6d MALAYALAM DIGIT SEVEN | 0d6c MALAYALAM DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0d6f MALAYALAM DIGIT NINE | 0d6e MALAYALAM DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d71 MALAYALAM NUMBER ONE HUNDRED | 0d70 MALAYALAM NUMBER TEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d73 MALAYALAM FRACTION ONE QUARTER | 0d72 MALAYALAM NUMBER ONE THOUSAND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d75 MALAYALAM FRACTION THREE QUARTERS | 0d74 MALAYALAM FRACTION ONE HALF */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d77 (null) | 0d76 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0d79 MALAYALAM DATE MARK | 0d78 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d7b MALAYALAM LETTER CHILLU N | 0d7a MALAYALAM LETTER CHILLU NN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d7d MALAYALAM LETTER CHILLU L | 0d7c MALAYALAM LETTER CHILLU RR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d7f MALAYALAM LETTER CHILLU K | 0d7e MALAYALAM LETTER CHILLU LL */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d81 (null) | 0d80 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d83 SINHALA SIGN VISARGAYA | 0d82 SINHALA SIGN ANUSVARAYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0d85 SINHALA LETTER AYANNA | 0d84 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d87 SINHALA LETTER AEYANNA | 0d86 SINHALA LETTER AAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d89 SINHALA LETTER IYANNA | 0d88 SINHALA LETTER AEEYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d8b SINHALA LETTER UYANNA | 0d8a SINHALA LETTER IIYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d8d SINHALA LETTER IRUYANNA | 0d8c SINHALA LETTER UUYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d8f SINHALA LETTER ILUYANNA | 0d8e SINHALA LETTER IRUUYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d91 SINHALA LETTER EYANNA | 0d90 SINHALA LETTER ILUUYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d93 SINHALA LETTER AIYANNA | 0d92 SINHALA LETTER EEYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d95 SINHALA LETTER OOYANNA | 0d94 SINHALA LETTER OYANNA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0d97 (null) | 0d96 SINHALA LETTER AUYANNA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d99 (null) | 0d98 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d9b SINHALA LETTER MAHAAPRAANA KAYANNA | 0d9a SINHALA LETTER ALPAPRAANA KAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d9d SINHALA LETTER MAHAAPRAANA GAYANNA | 0d9c SINHALA LETTER ALPAPRAANA GAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d9f SINHALA LETTER SANYAKA GAYANNA | 0d9e SINHALA LETTER KANTAJA NAASIKYAYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0da1 SINHALA LETTER MAHAAPRAANA CAYANNA | 0da0 SINHALA LETTER ALPAPRAANA CAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0da3 SINHALA LETTER MAHAAPRAANA JAYANNA | 0da2 SINHALA LETTER ALPAPRAANA JAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0da5 SINHALA LETTER TAALUJA SANYOOGA NAAKSIK | 0da4 SINHALA LETTER TAALUJA NAASIKYAYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0da7 SINHALA LETTER ALPAPRAANA TTAYANNA | 0da6 SINHALA LETTER SANYAKA JAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0da9 SINHALA LETTER ALPAPRAANA DDAYANNA | 0da8 SINHALA LETTER MAHAAPRAANA TTAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0dab SINHALA LETTER MUURDHAJA NAYANNA | 0daa SINHALA LETTER MAHAAPRAANA DDAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0dad SINHALA LETTER ALPAPRAANA TAYANNA | 0dac SINHALA LETTER SANYAKA DDAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0daf SINHALA LETTER ALPAPRAANA DAYANNA | 0dae SINHALA LETTER MAHAAPRAANA TAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0db1 SINHALA LETTER DANTAJA NAYANNA | 0db0 SINHALA LETTER MAHAAPRAANA DAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0db3 SINHALA LETTER SANYAKA DAYANNA | 0db2 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0db5 SINHALA LETTER MAHAAPRAANA PAYANNA | 0db4 SINHALA LETTER ALPAPRAANA PAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0db7 SINHALA LETTER MAHAAPRAANA BAYANNA | 0db6 SINHALA LETTER ALPAPRAANA BAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0db9 SINHALA LETTER AMBA BAYANNA | 0db8 SINHALA LETTER MAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0dbb SINHALA LETTER RAYANNA | 0dba SINHALA LETTER YAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0dbd SINHALA LETTER DANTAJA LAYANNA | 0dbc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0dbf (null) | 0dbe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0dc1 SINHALA LETTER TAALUJA SAYANNA | 0dc0 SINHALA LETTER VAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0dc3 SINHALA LETTER DANTAJA SAYANNA | 0dc2 SINHALA LETTER MUURDHAJA SAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0dc5 SINHALA LETTER MUURDHAJA LAYANNA | 0dc4 SINHALA LETTER HAYANNA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0dc7 (null) | 0dc6 SINHALA LETTER FAYANNA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0dc9 (null) | 0dc8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0dcb (null) | 0dca SINHALA SIGN AL-LAKUNA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0dcd (null) | 0dcc (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0dcf SINHALA VOWEL SIGN AELA-PILLA | 0dce (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0dd1 SINHALA VOWEL SIGN DIGA AEDA-PILLA | 0dd0 SINHALA VOWEL SIGN KETTI AEDA-PILLA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0dd3 SINHALA VOWEL SIGN DIGA IS-PILLA | 0dd2 SINHALA VOWEL SIGN KETTI IS-PILLA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0dd5 (null) | 0dd4 SINHALA VOWEL SIGN KETTI PAA-PILLA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0dd7 (null) | 0dd6 SINHALA VOWEL SIGN DIGA PAA-PILLA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0dd9 SINHALA VOWEL SIGN KOMBUVA | 0dd8 SINHALA VOWEL SIGN GAETTA-PILLA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ddb SINHALA VOWEL SIGN KOMBU DEKA | 0dda SINHALA VOWEL SIGN DIGA KOMBUVA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ddd SINHALA VOWEL SIGN KOMBUVA HAA DIGA AEL | 0ddc SINHALA VOWEL SIGN KOMBUVA HAA AELA-PIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ddf SINHALA VOWEL SIGN GAYANUKITTA | 0dde SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0de1 (null) | 0de0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0de3 (null) | 0de2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0de5 (null) | 0de4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0de7 (null) | 0de6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0de9 (null) | 0de8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0deb (null) | 0dea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ded (null) | 0dec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0def (null) | 0dee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0df1 (null) | 0df0 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0df3 SINHALA VOWEL SIGN DIGA GAYANUKITTA | 0df2 SINHALA VOWEL SIGN DIGA GAETTA-PILLA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 0df5 (null) | 0df4 SINHALA PUNCTUATION KUNDDALIYA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0df7 (null) | 0df6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0df9 (null) | 0df8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0dfb (null) | 0dfa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0dfd (null) | 0dfc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0dff (null) | 0dfe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0e01 THAI CHARACTER KO KAI | 0e00 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e03 THAI CHARACTER KHO KHUAT | 0e02 THAI CHARACTER KHO KHAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e05 THAI CHARACTER KHO KHON | 0e04 THAI CHARACTER KHO KHWAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e07 THAI CHARACTER NGO NGU | 0e06 THAI CHARACTER KHO RAKHANG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e09 THAI CHARACTER CHO CHING | 0e08 THAI CHARACTER CHO CHAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e0b THAI CHARACTER SO SO | 0e0a THAI CHARACTER CHO CHANG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e0d THAI CHARACTER YO YING | 0e0c THAI CHARACTER CHO CHOE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e0f THAI CHARACTER TO PATAK | 0e0e THAI CHARACTER DO CHADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e11 THAI CHARACTER THO NANGMONTHO | 0e10 THAI CHARACTER THO THAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e13 THAI CHARACTER NO NEN | 0e12 THAI CHARACTER THO PHUTHAO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e15 THAI CHARACTER TO TAO | 0e14 THAI CHARACTER DO DEK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e17 THAI CHARACTER THO THAHAN | 0e16 THAI CHARACTER THO THUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e19 THAI CHARACTER NO NU | 0e18 THAI CHARACTER THO THONG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e1b THAI CHARACTER PO PLA | 0e1a THAI CHARACTER BO BAIMAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e1d THAI CHARACTER FO FA | 0e1c THAI CHARACTER PHO PHUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e1f THAI CHARACTER FO FAN | 0e1e THAI CHARACTER PHO PHAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e21 THAI CHARACTER MO MA | 0e20 THAI CHARACTER PHO SAMPHAO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e23 THAI CHARACTER RO RUA | 0e22 THAI CHARACTER YO YAK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e25 THAI CHARACTER LO LING | 0e24 THAI CHARACTER RU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e27 THAI CHARACTER WO WAEN | 0e26 THAI CHARACTER LU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e29 THAI CHARACTER SO RUSI | 0e28 THAI CHARACTER SO SALA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e2b THAI CHARACTER HO HIP | 0e2a THAI CHARACTER SO SUA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e2d THAI CHARACTER O ANG | 0e2c THAI CHARACTER LO CHULA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e2f THAI CHARACTER PAIYANNOI | 0e2e THAI CHARACTER HO NOKHUK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 0e31 THAI CHARACTER MAI HAN-AKAT | 0e30 THAI CHARACTER SARA A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e33 THAI CHARACTER SARA AM | 0e32 THAI CHARACTER SARA AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0e35 THAI CHARACTER SARA II | 0e34 THAI CHARACTER SARA I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0e37 THAI CHARACTER SARA UEE | 0e36 THAI CHARACTER SARA UE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0e39 THAI CHARACTER SARA UU | 0e38 THAI CHARACTER SARA U */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0e3b (null) | 0e3a THAI CHARACTER PHINTHU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e3d (null) | 0e3c (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0e3f THAI CURRENCY SYMBOL BAHT | 0e3e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e41 THAI CHARACTER SARA AE | 0e40 THAI CHARACTER SARA E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e43 THAI CHARACTER SARA AI MAIMUAN | 0e42 THAI CHARACTER SARA O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e45 THAI CHARACTER LAKKHANGYAO | 0e44 THAI CHARACTER SARA AI MAIMALAI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0e47 THAI CHARACTER MAITAIKHU | 0e46 THAI CHARACTER MAIYAMOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0e49 THAI CHARACTER MAI THO | 0e48 THAI CHARACTER MAI EK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0e4b THAI CHARACTER MAI CHATTAWA | 0e4a THAI CHARACTER MAI TRI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0e4d THAI CHARACTER NIKHAHIT | 0e4c THAI CHARACTER THANTHAKHAT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 0e4f THAI CHARACTER FONGMAN | 0e4e THAI CHARACTER YAMAKKAN */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0e51 THAI DIGIT ONE | 0e50 THAI DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0e53 THAI DIGIT THREE | 0e52 THAI DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0e55 THAI DIGIT FIVE | 0e54 THAI DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0e57 THAI DIGIT SEVEN | 0e56 THAI DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0e59 THAI DIGIT NINE | 0e58 THAI DIGIT EIGHT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0e5b THAI CHARACTER KHOMUT | 0e5a THAI CHARACTER ANGKHANKHU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e5d (null) | 0e5c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e5f (null) | 0e5e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e61 (null) | 0e60 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e63 (null) | 0e62 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e65 (null) | 0e64 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e67 (null) | 0e66 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e69 (null) | 0e68 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e6b (null) | 0e6a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e6d (null) | 0e6c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e6f (null) | 0e6e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e71 (null) | 0e70 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e73 (null) | 0e72 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e75 (null) | 0e74 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e77 (null) | 0e76 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e79 (null) | 0e78 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e7b (null) | 0e7a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e7d (null) | 0e7c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e7f (null) | 0e7e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0e81 LAO LETTER KO | 0e80 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0e83 (null) | 0e82 LAO LETTER KHO SUNG */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0e85 (null) | 0e84 LAO LETTER KHO TAM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0e87 LAO LETTER NGO | 0e86 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0e89 (null) | 0e88 LAO LETTER CO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0e8b (null) | 0e8a LAO LETTER SO TAM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0e8d LAO LETTER NYO | 0e8c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e8f (null) | 0e8e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e91 (null) | 0e90 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e93 (null) | 0e92 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e95 LAO LETTER TO | 0e94 LAO LETTER DO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e97 LAO LETTER THO TAM | 0e96 LAO LETTER THO SUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0e99 LAO LETTER NO | 0e98 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e9b LAO LETTER PO | 0e9a LAO LETTER BO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e9d LAO LETTER FO TAM | 0e9c LAO LETTER PHO SUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e9f LAO LETTER FO SUNG | 0e9e LAO LETTER PHO TAM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0ea1 LAO LETTER MO | 0ea0 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ea3 LAO LETTER LO LING | 0ea2 LAO LETTER YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0ea5 LAO LETTER LO LOOT | 0ea4 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0ea7 LAO LETTER WO | 0ea6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ea9 (null) | 0ea8 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0eab LAO LETTER HO SUNG | 0eaa LAO LETTER SO SUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0ead LAO LETTER O | 0eac (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0eaf LAO ELLIPSIS | 0eae LAO LETTER HO TAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 0eb1 LAO VOWEL SIGN MAI KAN | 0eb0 LAO VOWEL SIGN A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0eb3 LAO VOWEL SIGN AM | 0eb2 LAO VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0eb5 LAO VOWEL SIGN II | 0eb4 LAO VOWEL SIGN I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0eb7 LAO VOWEL SIGN YY | 0eb6 LAO VOWEL SIGN Y */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0eb9 LAO VOWEL SIGN UU | 0eb8 LAO VOWEL SIGN U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0ebb LAO VOWEL SIGN MAI KON | 0eba (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 0ebd LAO SEMIVOWEL SIGN NYO | 0ebc LAO SEMIVOWEL SIGN LO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ebf (null) | 0ebe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ec1 LAO VOWEL SIGN EI | 0ec0 LAO VOWEL SIGN E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ec3 LAO VOWEL SIGN AY | 0ec2 LAO VOWEL SIGN O */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0ec5 (null) | 0ec4 LAO VOWEL SIGN AI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0ec7 (null) | 0ec6 LAO KO LA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ec9 LAO TONE MAI THO | 0ec8 LAO TONE MAI EK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ecb LAO TONE MAI CATAWA | 0eca LAO TONE MAI TI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ecd LAO NIGGAHITA | 0ecc LAO CANCELLATION MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ecf (null) | 0ece (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ed1 LAO DIGIT ONE | 0ed0 LAO DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ed3 LAO DIGIT THREE | 0ed2 LAO DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ed5 LAO DIGIT FIVE | 0ed4 LAO DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ed7 LAO DIGIT SEVEN | 0ed6 LAO DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ed9 LAO DIGIT NINE | 0ed8 LAO DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0edb (null) | 0eda (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0edd LAO HO MO | 0edc LAO HO NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0edf LAO LETTER KHMU NYO | 0ede LAO LETTER KHMU GO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ee1 (null) | 0ee0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ee3 (null) | 0ee2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ee5 (null) | 0ee4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ee7 (null) | 0ee6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ee9 (null) | 0ee8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0eeb (null) | 0eea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0eed (null) | 0eec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0eef (null) | 0eee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ef1 (null) | 0ef0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ef3 (null) | 0ef2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ef5 (null) | 0ef4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ef7 (null) | 0ef6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ef9 (null) | 0ef8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0efb (null) | 0efa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0efd (null) | 0efc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0eff (null) | 0efe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 0f01 TIBETAN MARK GTER YIG MGO TRUNCATED A | 0f00 TIBETAN SYLLABLE OM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f03 TIBETAN MARK GTER YIG MGO -UM GTER TSHE | 0f02 TIBETAN MARK GTER YIG MGO -UM RNAM BCAD */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f05 TIBETAN MARK CLOSING YIG MGO SGAB MA | 0f04 TIBETAN MARK INITIAL YIG MGO MDUN MA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f07 TIBETAN MARK YIG MGO TSHEG SHAD MA | 0f06 TIBETAN MARK CARET YIG MGO PHUR SHAD MA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f09 TIBETAN MARK BSKUR YIG MGO | 0f08 TIBETAN MARK SBRUL SHAD */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f0b TIBETAN MARK INTERSYLLABIC TSHEG | 0f0a TIBETAN MARK BKA- SHOG YIG MGO */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f0d TIBETAN MARK SHAD | 0f0c TIBETAN MARK DELIMITER TSHEG BSTAR */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f0f TIBETAN MARK TSHEG SHAD | 0f0e TIBETAN MARK NYIS SHAD */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f11 TIBETAN MARK RIN CHEN SPUNGS SHAD | 0f10 TIBETAN MARK NYIS TSHEG SHAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 0f13 TIBETAN MARK CARET -DZUD RTAGS ME LONG | 0f12 TIBETAN MARK RGYA GRAM SHAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 0f15 TIBETAN LOGOTYPE SIGN CHAD RTAGS | 0f14 TIBETAN MARK GTER TSHEG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f17 TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CH | 0f16 TIBETAN LOGOTYPE SIGN LHAG RTAGS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f19 TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS | 0f18 TIBETAN ASTROLOGICAL SIGN -KHYUD PA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f1b TIBETAN SIGN RDEL DKAR GNYIS | 0f1a TIBETAN SIGN RDEL DKAR GCIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f1d TIBETAN SIGN RDEL NAG GCIG | 0f1c TIBETAN SIGN RDEL DKAR GSUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f1f TIBETAN SIGN RDEL DKAR RDEL NAG | 0f1e TIBETAN SIGN RDEL NAG GNYIS */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0f21 TIBETAN DIGIT ONE | 0f20 TIBETAN DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0f23 TIBETAN DIGIT THREE | 0f22 TIBETAN DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0f25 TIBETAN DIGIT FIVE | 0f24 TIBETAN DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0f27 TIBETAN DIGIT SEVEN | 0f26 TIBETAN DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0f29 TIBETAN DIGIT NINE | 0f28 TIBETAN DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f2b TIBETAN DIGIT HALF TWO | 0f2a TIBETAN DIGIT HALF ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f2d TIBETAN DIGIT HALF FOUR | 0f2c TIBETAN DIGIT HALF THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f2f TIBETAN DIGIT HALF SIX | 0f2e TIBETAN DIGIT HALF FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f31 TIBETAN DIGIT HALF EIGHT | 0f30 TIBETAN DIGIT HALF SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f33 TIBETAN DIGIT HALF ZERO | 0f32 TIBETAN DIGIT HALF NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f35 TIBETAN MARK NGAS BZUNG NYI ZLA | 0f34 TIBETAN MARK BSDUS RTAGS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f37 TIBETAN MARK NGAS BZUNG SGOR RTAGS | 0f36 TIBETAN MARK CARET -DZUD RTAGS BZHI MIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f39 TIBETAN MARK TSA -PHRU | 0f38 TIBETAN MARK CHE MGO */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f3b TIBETAN MARK GUG RTAGS GYAS | 0f3a TIBETAN MARK GUG RTAGS GYON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f3d TIBETAN MARK ANG KHANG GYAS | 0f3c TIBETAN MARK ANG KHANG GYON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f3f TIBETAN SIGN MAR TSHES | 0f3e TIBETAN SIGN YAR TSHES */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f41 TIBETAN LETTER KHA | 0f40 TIBETAN LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f43 TIBETAN LETTER GHA | 0f42 TIBETAN LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f45 TIBETAN LETTER CA | 0f44 TIBETAN LETTER NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f47 TIBETAN LETTER JA | 0f46 TIBETAN LETTER CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0f49 TIBETAN LETTER NYA | 0f48 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f4b TIBETAN LETTER TTHA | 0f4a TIBETAN LETTER TTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f4d TIBETAN LETTER DDHA | 0f4c TIBETAN LETTER DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f4f TIBETAN LETTER TA | 0f4e TIBETAN LETTER NNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f51 TIBETAN LETTER DA | 0f50 TIBETAN LETTER THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f53 TIBETAN LETTER NA | 0f52 TIBETAN LETTER DHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f55 TIBETAN LETTER PHA | 0f54 TIBETAN LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f57 TIBETAN LETTER BHA | 0f56 TIBETAN LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f59 TIBETAN LETTER TSA | 0f58 TIBETAN LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f5b TIBETAN LETTER DZA | 0f5a TIBETAN LETTER TSHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f5d TIBETAN LETTER WA | 0f5c TIBETAN LETTER DZHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f5f TIBETAN LETTER ZA | 0f5e TIBETAN LETTER ZHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f61 TIBETAN LETTER YA | 0f60 TIBETAN LETTER -A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f63 TIBETAN LETTER LA | 0f62 TIBETAN LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f65 TIBETAN LETTER SSA | 0f64 TIBETAN LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f67 TIBETAN LETTER HA | 0f66 TIBETAN LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f69 TIBETAN LETTER KSSA | 0f68 TIBETAN LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f6b TIBETAN LETTER KKA | 0f6a TIBETAN LETTER FIXED-FORM RA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0f6d (null) | 0f6c TIBETAN LETTER RRA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0f6f (null) | 0f6e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0f71 TIBETAN VOWEL SIGN AA | 0f70 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f73 TIBETAN VOWEL SIGN II | 0f72 TIBETAN VOWEL SIGN I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f75 TIBETAN VOWEL SIGN UU | 0f74 TIBETAN VOWEL SIGN U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f77 TIBETAN VOWEL SIGN VOCALIC RR | 0f76 TIBETAN VOWEL SIGN VOCALIC R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f79 TIBETAN VOWEL SIGN VOCALIC LL | 0f78 TIBETAN VOWEL SIGN VOCALIC L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f7b TIBETAN VOWEL SIGN EE | 0f7a TIBETAN VOWEL SIGN E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f7d TIBETAN VOWEL SIGN OO | 0f7c TIBETAN VOWEL SIGN O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f7f TIBETAN SIGN RNAM BCAD | 0f7e TIBETAN SIGN RJES SU NGA RO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f81 TIBETAN VOWEL SIGN REVERSED II | 0f80 TIBETAN VOWEL SIGN REVERSED I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f83 TIBETAN SIGN SNA LDAN | 0f82 TIBETAN SIGN NYI ZLA NAA DA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 0f85 TIBETAN MARK PALUTA | 0f84 TIBETAN MARK HALANTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f87 TIBETAN SIGN YANG RTAGS | 0f86 TIBETAN SIGN LCI RTAGS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f89 TIBETAN SIGN MCHU CAN | 0f88 TIBETAN SIGN LCE TSA CAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f8b TIBETAN SIGN GRU MED RGYINGS | 0f8a TIBETAN SIGN GRU CAN RGYINGS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 0f8d TIBETAN SUBJOINED SIGN LCE TSA CAN | 0f8c TIBETAN SIGN INVERTED MCHU CAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f8f TIBETAN SUBJOINED SIGN INVERTED MCHU CA | 0f8e TIBETAN SUBJOINED SIGN MCHU CAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f91 TIBETAN SUBJOINED LETTER KHA | 0f90 TIBETAN SUBJOINED LETTER KA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f93 TIBETAN SUBJOINED LETTER GHA | 0f92 TIBETAN SUBJOINED LETTER GA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f95 TIBETAN SUBJOINED LETTER CA | 0f94 TIBETAN SUBJOINED LETTER NGA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f97 TIBETAN SUBJOINED LETTER JA | 0f96 TIBETAN SUBJOINED LETTER CHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0f99 TIBETAN SUBJOINED LETTER NYA | 0f98 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f9b TIBETAN SUBJOINED LETTER TTHA | 0f9a TIBETAN SUBJOINED LETTER TTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f9d TIBETAN SUBJOINED LETTER DDHA | 0f9c TIBETAN SUBJOINED LETTER DDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f9f TIBETAN SUBJOINED LETTER TA | 0f9e TIBETAN SUBJOINED LETTER NNA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fa1 TIBETAN SUBJOINED LETTER DA | 0fa0 TIBETAN SUBJOINED LETTER THA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fa3 TIBETAN SUBJOINED LETTER NA | 0fa2 TIBETAN SUBJOINED LETTER DHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fa5 TIBETAN SUBJOINED LETTER PHA | 0fa4 TIBETAN SUBJOINED LETTER PA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fa7 TIBETAN SUBJOINED LETTER BHA | 0fa6 TIBETAN SUBJOINED LETTER BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fa9 TIBETAN SUBJOINED LETTER TSA | 0fa8 TIBETAN SUBJOINED LETTER MA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fab TIBETAN SUBJOINED LETTER DZA | 0faa TIBETAN SUBJOINED LETTER TSHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fad TIBETAN SUBJOINED LETTER WA | 0fac TIBETAN SUBJOINED LETTER DZHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0faf TIBETAN SUBJOINED LETTER ZA | 0fae TIBETAN SUBJOINED LETTER ZHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fb1 TIBETAN SUBJOINED LETTER YA | 0fb0 TIBETAN SUBJOINED LETTER -A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fb3 TIBETAN SUBJOINED LETTER LA | 0fb2 TIBETAN SUBJOINED LETTER RA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fb5 TIBETAN SUBJOINED LETTER SSA | 0fb4 TIBETAN SUBJOINED LETTER SHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fb7 TIBETAN SUBJOINED LETTER HA | 0fb6 TIBETAN SUBJOINED LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fb9 TIBETAN SUBJOINED LETTER KSSA | 0fb8 TIBETAN SUBJOINED LETTER A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fbb TIBETAN SUBJOINED LETTER FIXED-FORM YA | 0fba TIBETAN SUBJOINED LETTER FIXED-FORM WA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0fbd (null) | 0fbc TIBETAN SUBJOINED LETTER FIXED-FORM RA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fbf TIBETAN KU RU KHA BZHI MIG CAN | 0fbe TIBETAN KU RU KHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fc1 TIBETAN CANTILLATION SIGN LIGHT BEAT | 0fc0 TIBETAN CANTILLATION SIGN HEAVY BEAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fc3 TIBETAN CANTILLATION SIGN SBUB -CHAL | 0fc2 TIBETAN CANTILLATION SIGN CANG TE-U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fc5 TIBETAN SYMBOL RDO RJE | 0fc4 TIBETAN SYMBOL DRIL BU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fc7 TIBETAN SYMBOL RDO RJE RGYA GRAM | 0fc6 TIBETAN SYMBOL PADMA GDAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fc9 TIBETAN SYMBOL NOR BU | 0fc8 TIBETAN SYMBOL PHUR PA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fcb TIBETAN SYMBOL NOR BU GSUM -KHYIL | 0fca TIBETAN SYMBOL NOR BU NYIS -KHYIL */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0fcd (null) | 0fcc TIBETAN SYMBOL NOR BU BZHI -KHYIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fcf TIBETAN SIGN RDEL NAG GSUM | 0fce TIBETAN SIGN RDEL NAG RDEL DKAR */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0fd1 TIBETAN MARK MNYAM YIG GI MGO RGYAN | 0fd0 TIBETAN MARK BSKA- SHOG GI MGO RGYAN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0fd3 TIBETAN MARK INITIAL BRDA RNYING YIG MG | 0fd2 TIBETAN MARK NYIS TSHEG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 0fd5 RIGHT-FACING SVASTI SIGN | 0fd4 TIBETAN MARK CLOSING BRDA RNYING YIG MG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fd7 RIGHT-FACING SVASTI SIGN WITH DOTS | 0fd6 LEFT-FACING SVASTI SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 0fd9 TIBETAN MARK LEADING MCHAN RTAGS | 0fd8 LEFT-FACING SVASTI SIGN WITH DOTS */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 0fdb (null) | 0fda TIBETAN MARK TRAILING MCHAN RTAGS */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fdd (null) | 0fdc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fdf (null) | 0fde (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fe1 (null) | 0fe0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fe3 (null) | 0fe2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fe5 (null) | 0fe4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fe7 (null) | 0fe6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fe9 (null) | 0fe8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0feb (null) | 0fea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fed (null) | 0fec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fef (null) | 0fee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ff1 (null) | 0ff0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ff3 (null) | 0ff2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ff5 (null) | 0ff4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ff7 (null) | 0ff6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ff9 (null) | 0ff8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ffb (null) | 0ffa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ffd (null) | 0ffc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fff (null) | 0ffe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1001 MYANMAR LETTER KHA | 1000 MYANMAR LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1003 MYANMAR LETTER GHA | 1002 MYANMAR LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1005 MYANMAR LETTER CA | 1004 MYANMAR LETTER NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1007 MYANMAR LETTER JA | 1006 MYANMAR LETTER CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1009 MYANMAR LETTER NYA | 1008 MYANMAR LETTER JHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 100b MYANMAR LETTER TTA | 100a MYANMAR LETTER NNYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 100d MYANMAR LETTER DDA | 100c MYANMAR LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 100f MYANMAR LETTER NNA | 100e MYANMAR LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1011 MYANMAR LETTER THA | 1010 MYANMAR LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1013 MYANMAR LETTER DHA | 1012 MYANMAR LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1015 MYANMAR LETTER PA | 1014 MYANMAR LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1017 MYANMAR LETTER BA | 1016 MYANMAR LETTER PHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1019 MYANMAR LETTER MA | 1018 MYANMAR LETTER BHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 101b MYANMAR LETTER RA | 101a MYANMAR LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 101d MYANMAR LETTER WA | 101c MYANMAR LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 101f MYANMAR LETTER HA | 101e MYANMAR LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1021 MYANMAR LETTER A | 1020 MYANMAR LETTER LLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1023 MYANMAR LETTER I | 1022 MYANMAR LETTER SHAN A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1025 MYANMAR LETTER U | 1024 MYANMAR LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1027 MYANMAR LETTER E | 1026 MYANMAR LETTER UU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1029 MYANMAR LETTER O | 1028 MYANMAR LETTER MON E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 102b MYANMAR VOWEL SIGN TALL AA | 102a MYANMAR LETTER AU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 102d MYANMAR VOWEL SIGN I | 102c MYANMAR VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 102f MYANMAR VOWEL SIGN U | 102e MYANMAR VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1031 MYANMAR VOWEL SIGN E | 1030 MYANMAR VOWEL SIGN UU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1033 MYANMAR VOWEL SIGN MON II | 1032 MYANMAR VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1035 MYANMAR VOWEL SIGN E ABOVE | 1034 MYANMAR VOWEL SIGN MON O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1037 MYANMAR SIGN DOT BELOW | 1036 MYANMAR SIGN ANUSVARA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1039 MYANMAR SIGN VIRAMA | 1038 MYANMAR SIGN VISARGA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 103b MYANMAR CONSONANT SIGN MEDIAL YA | 103a MYANMAR SIGN ASAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 103d MYANMAR CONSONANT SIGN MEDIAL WA | 103c MYANMAR CONSONANT SIGN MEDIAL RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 103f MYANMAR LETTER GREAT SA | 103e MYANMAR CONSONANT SIGN MEDIAL HA */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1041 MYANMAR DIGIT ONE | 1040 MYANMAR DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1043 MYANMAR DIGIT THREE | 1042 MYANMAR DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1045 MYANMAR DIGIT FIVE | 1044 MYANMAR DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1047 MYANMAR DIGIT SEVEN | 1046 MYANMAR DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1049 MYANMAR DIGIT NINE | 1048 MYANMAR DIGIT EIGHT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 104b MYANMAR SIGN SECTION | 104a MYANMAR SIGN LITTLE SECTION */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 104d MYANMAR SYMBOL COMPLETED | 104c MYANMAR SYMBOL LOCATIVE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 104f MYANMAR SYMBOL GENITIVE | 104e MYANMAR SYMBOL AFOREMENTIONED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1051 MYANMAR LETTER SSA | 1050 MYANMAR LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1053 MYANMAR LETTER VOCALIC RR | 1052 MYANMAR LETTER VOCALIC R */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1055 MYANMAR LETTER VOCALIC LL | 1054 MYANMAR LETTER VOCALIC L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1057 MYANMAR VOWEL SIGN VOCALIC RR | 1056 MYANMAR VOWEL SIGN VOCALIC R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1059 MYANMAR VOWEL SIGN VOCALIC LL | 1058 MYANMAR VOWEL SIGN VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 105b MYANMAR LETTER MON JHA | 105a MYANMAR LETTER MON NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 105d MYANMAR LETTER MON BBE | 105c MYANMAR LETTER MON BBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 105f MYANMAR CONSONANT SIGN MON MEDIAL MA | 105e MYANMAR CONSONANT SIGN MON MEDIAL NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 1061 MYANMAR LETTER SGAW KAREN SHA | 1060 MYANMAR CONSONANT SIGN MON MEDIAL LA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1063 MYANMAR TONE MARK SGAW KAREN HATHI | 1062 MYANMAR VOWEL SIGN SGAW KAREN EU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 1065 MYANMAR LETTER WESTERN PWO KAREN THA | 1064 MYANMAR TONE MARK SGAW KAREN KE PHO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 1067 MYANMAR VOWEL SIGN WESTERN PWO KAREN EU | 1066 MYANMAR LETTER WESTERN PWO KAREN PWA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1069 MYANMAR SIGN WESTERN PWO KAREN TONE-1 | 1068 MYANMAR VOWEL SIGN WESTERN PWO KAREN UE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 106b MYANMAR SIGN WESTERN PWO KAREN TONE-3 | 106a MYANMAR SIGN WESTERN PWO KAREN TONE-2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 106d MYANMAR SIGN WESTERN PWO KAREN TONE-5 | 106c MYANMAR SIGN WESTERN PWO KAREN TONE-4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 106f MYANMAR LETTER EASTERN PWO KAREN YWA | 106e MYANMAR LETTER EASTERN PWO KAREN NNA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 1071 MYANMAR VOWEL SIGN GEBA KAREN I | 1070 MYANMAR LETTER EASTERN PWO KAREN GHWA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1073 MYANMAR VOWEL SIGN KAYAH U | 1072 MYANMAR VOWEL SIGN KAYAH OE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 1075 MYANMAR LETTER SHAN KA | 1074 MYANMAR VOWEL SIGN KAYAH EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1077 MYANMAR LETTER SHAN GA | 1076 MYANMAR LETTER SHAN KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1079 MYANMAR LETTER SHAN ZA | 1078 MYANMAR LETTER SHAN CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 107b MYANMAR LETTER SHAN DA | 107a MYANMAR LETTER SHAN NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 107d MYANMAR LETTER SHAN PHA | 107c MYANMAR LETTER SHAN NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 107f MYANMAR LETTER SHAN BA | 107e MYANMAR LETTER SHAN FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1081 MYANMAR LETTER SHAN HA | 1080 MYANMAR LETTER SHAN THA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1083 MYANMAR VOWEL SIGN SHAN AA | 1082 MYANMAR CONSONANT SIGN SHAN MEDIAL WA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1085 MYANMAR VOWEL SIGN SHAN E ABOVE | 1084 MYANMAR VOWEL SIGN SHAN E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1087 MYANMAR SIGN SHAN TONE-2 | 1086 MYANMAR VOWEL SIGN SHAN FINAL Y */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1089 MYANMAR SIGN SHAN TONE-5 | 1088 MYANMAR SIGN SHAN TONE-3 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 108b MYANMAR SIGN SHAN COUNCIL TONE-2 | 108a MYANMAR SIGN SHAN TONE-6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 108d MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE | 108c MYANMAR SIGN SHAN COUNCIL TONE-3 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 108f MYANMAR SIGN RUMAI PALAUNG TONE-5 | 108e MYANMAR LETTER RUMAI PALAUNG FA */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1091 MYANMAR SHAN DIGIT ONE | 1090 MYANMAR SHAN DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1093 MYANMAR SHAN DIGIT THREE | 1092 MYANMAR SHAN DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1095 MYANMAR SHAN DIGIT FIVE | 1094 MYANMAR SHAN DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1097 MYANMAR SHAN DIGIT SEVEN | 1096 MYANMAR SHAN DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1099 MYANMAR SHAN DIGIT NINE | 1098 MYANMAR SHAN DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 109b MYANMAR SIGN KHAMTI TONE-3 | 109a MYANMAR SIGN KHAMTI TONE-1 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 109d MYANMAR VOWEL SIGN AITON AI | 109c MYANMAR VOWEL SIGN AITON A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 109f MYANMAR SYMBOL SHAN EXCLAMATION | 109e MYANMAR SYMBOL SHAN ONE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10a1 GEORGIAN CAPITAL LETTER BAN | 10a0 GEORGIAN CAPITAL LETTER AN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10a3 GEORGIAN CAPITAL LETTER DON | 10a2 GEORGIAN CAPITAL LETTER GAN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10a5 GEORGIAN CAPITAL LETTER VIN | 10a4 GEORGIAN CAPITAL LETTER EN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10a7 GEORGIAN CAPITAL LETTER TAN | 10a6 GEORGIAN CAPITAL LETTER ZEN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10a9 GEORGIAN CAPITAL LETTER KAN | 10a8 GEORGIAN CAPITAL LETTER IN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10ab GEORGIAN CAPITAL LETTER MAN | 10aa GEORGIAN CAPITAL LETTER LAS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10ad GEORGIAN CAPITAL LETTER ON | 10ac GEORGIAN CAPITAL LETTER NAR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10af GEORGIAN CAPITAL LETTER ZHAR | 10ae GEORGIAN CAPITAL LETTER PAR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10b1 GEORGIAN CAPITAL LETTER SAN | 10b0 GEORGIAN CAPITAL LETTER RAE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10b3 GEORGIAN CAPITAL LETTER UN | 10b2 GEORGIAN CAPITAL LETTER TAR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10b5 GEORGIAN CAPITAL LETTER KHAR | 10b4 GEORGIAN CAPITAL LETTER PHAR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10b7 GEORGIAN CAPITAL LETTER QAR | 10b6 GEORGIAN CAPITAL LETTER GHAN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10b9 GEORGIAN CAPITAL LETTER CHIN | 10b8 GEORGIAN CAPITAL LETTER SHIN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10bb GEORGIAN CAPITAL LETTER JIL | 10ba GEORGIAN CAPITAL LETTER CAN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10bd GEORGIAN CAPITAL LETTER CHAR | 10bc GEORGIAN CAPITAL LETTER CIL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10bf GEORGIAN CAPITAL LETTER JHAN | 10be GEORGIAN CAPITAL LETTER XAN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10c1 GEORGIAN CAPITAL LETTER HE | 10c0 GEORGIAN CAPITAL LETTER HAE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10c3 GEORGIAN CAPITAL LETTER WE | 10c2 GEORGIAN CAPITAL LETTER HIE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10c5 GEORGIAN CAPITAL LETTER HOE | 10c4 GEORGIAN CAPITAL LETTER HAR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UNDEF, /* 10c7 GEORGIAN CAPITAL LETTER YN | 10c6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 10c9 (null) | 10c8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 10cb (null) | 10ca (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UNDEF, /* 10cd GEORGIAN CAPITAL LETTER AEN | 10cc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 10cf (null) | 10ce (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10d1 GEORGIAN LETTER BAN | 10d0 GEORGIAN LETTER AN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10d3 GEORGIAN LETTER DON | 10d2 GEORGIAN LETTER GAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10d5 GEORGIAN LETTER VIN | 10d4 GEORGIAN LETTER EN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10d7 GEORGIAN LETTER TAN | 10d6 GEORGIAN LETTER ZEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10d9 GEORGIAN LETTER KAN | 10d8 GEORGIAN LETTER IN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10db GEORGIAN LETTER MAN | 10da GEORGIAN LETTER LAS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10dd GEORGIAN LETTER ON | 10dc GEORGIAN LETTER NAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10df GEORGIAN LETTER ZHAR | 10de GEORGIAN LETTER PAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10e1 GEORGIAN LETTER SAN | 10e0 GEORGIAN LETTER RAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10e3 GEORGIAN LETTER UN | 10e2 GEORGIAN LETTER TAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10e5 GEORGIAN LETTER KHAR | 10e4 GEORGIAN LETTER PHAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10e7 GEORGIAN LETTER QAR | 10e6 GEORGIAN LETTER GHAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10e9 GEORGIAN LETTER CHIN | 10e8 GEORGIAN LETTER SHIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10eb GEORGIAN LETTER JIL | 10ea GEORGIAN LETTER CAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10ed GEORGIAN LETTER CHAR | 10ec GEORGIAN LETTER CIL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10ef GEORGIAN LETTER JHAN | 10ee GEORGIAN LETTER XAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10f1 GEORGIAN LETTER HE | 10f0 GEORGIAN LETTER HAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10f3 GEORGIAN LETTER WE | 10f2 GEORGIAN LETTER HIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10f5 GEORGIAN LETTER HOE | 10f4 GEORGIAN LETTER HAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10f7 GEORGIAN LETTER YN | 10f6 GEORGIAN LETTER FI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10f9 GEORGIAN LETTER TURNED GAN | 10f8 GEORGIAN LETTER ELIFI */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 10fb GEORGIAN PARAGRAPH SEPARATOR | 10fa GEORGIAN LETTER AIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 10fd GEORGIAN LETTER AEN | 10fc MODIFIER LETTER GEORGIAN NAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10ff GEORGIAN LETTER LABIAL SIGN | 10fe GEORGIAN LETTER HARD SIGN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1101 HANGUL CHOSEONG SSANGKIYEOK | 1100 HANGUL CHOSEONG KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1103 HANGUL CHOSEONG TIKEUT | 1102 HANGUL CHOSEONG NIEUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1105 HANGUL CHOSEONG RIEUL | 1104 HANGUL CHOSEONG SSANGTIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1107 HANGUL CHOSEONG PIEUP | 1106 HANGUL CHOSEONG MIEUM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1109 HANGUL CHOSEONG SIOS | 1108 HANGUL CHOSEONG SSANGPIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 110b HANGUL CHOSEONG IEUNG | 110a HANGUL CHOSEONG SSANGSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 110d HANGUL CHOSEONG SSANGCIEUC | 110c HANGUL CHOSEONG CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 110f HANGUL CHOSEONG KHIEUKH | 110e HANGUL CHOSEONG CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1111 HANGUL CHOSEONG PHIEUPH | 1110 HANGUL CHOSEONG THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1113 HANGUL CHOSEONG NIEUN-KIYEOK | 1112 HANGUL CHOSEONG HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1115 HANGUL CHOSEONG NIEUN-TIKEUT | 1114 HANGUL CHOSEONG SSANGNIEUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1117 HANGUL CHOSEONG TIKEUT-KIYEOK | 1116 HANGUL CHOSEONG NIEUN-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1119 HANGUL CHOSEONG SSANGRIEUL | 1118 HANGUL CHOSEONG RIEUL-NIEUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 111b HANGUL CHOSEONG KAPYEOUNRIEUL | 111a HANGUL CHOSEONG RIEUL-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 111d HANGUL CHOSEONG KAPYEOUNMIEUM | 111c HANGUL CHOSEONG MIEUM-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 111f HANGUL CHOSEONG PIEUP-NIEUN | 111e HANGUL CHOSEONG PIEUP-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1121 HANGUL CHOSEONG PIEUP-SIOS | 1120 HANGUL CHOSEONG PIEUP-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1123 HANGUL CHOSEONG PIEUP-SIOS-TIKEUT | 1122 HANGUL CHOSEONG PIEUP-SIOS-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1125 HANGUL CHOSEONG PIEUP-SSANGSIOS | 1124 HANGUL CHOSEONG PIEUP-SIOS-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1127 HANGUL CHOSEONG PIEUP-CIEUC | 1126 HANGUL CHOSEONG PIEUP-SIOS-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1129 HANGUL CHOSEONG PIEUP-THIEUTH | 1128 HANGUL CHOSEONG PIEUP-CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 112b HANGUL CHOSEONG KAPYEOUNPIEUP | 112a HANGUL CHOSEONG PIEUP-PHIEUPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 112d HANGUL CHOSEONG SIOS-KIYEOK | 112c HANGUL CHOSEONG KAPYEOUNSSANGPIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 112f HANGUL CHOSEONG SIOS-TIKEUT | 112e HANGUL CHOSEONG SIOS-NIEUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1131 HANGUL CHOSEONG SIOS-MIEUM | 1130 HANGUL CHOSEONG SIOS-RIEUL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1133 HANGUL CHOSEONG SIOS-PIEUP-KIYEOK | 1132 HANGUL CHOSEONG SIOS-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1135 HANGUL CHOSEONG SIOS-IEUNG | 1134 HANGUL CHOSEONG SIOS-SSANGSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1137 HANGUL CHOSEONG SIOS-CHIEUCH | 1136 HANGUL CHOSEONG SIOS-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1139 HANGUL CHOSEONG SIOS-THIEUTH | 1138 HANGUL CHOSEONG SIOS-KHIEUKH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 113b HANGUL CHOSEONG SIOS-HIEUH | 113a HANGUL CHOSEONG SIOS-PHIEUPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 113d HANGUL CHOSEONG CHITUEUMSSANGSIOS | 113c HANGUL CHOSEONG CHITUEUMSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 113f HANGUL CHOSEONG CEONGCHIEUMSSANGSIOS | 113e HANGUL CHOSEONG CEONGCHIEUMSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1141 HANGUL CHOSEONG IEUNG-KIYEOK | 1140 HANGUL CHOSEONG PANSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1143 HANGUL CHOSEONG IEUNG-MIEUM | 1142 HANGUL CHOSEONG IEUNG-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1145 HANGUL CHOSEONG IEUNG-SIOS | 1144 HANGUL CHOSEONG IEUNG-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1147 HANGUL CHOSEONG SSANGIEUNG | 1146 HANGUL CHOSEONG IEUNG-PANSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1149 HANGUL CHOSEONG IEUNG-CHIEUCH | 1148 HANGUL CHOSEONG IEUNG-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 114b HANGUL CHOSEONG IEUNG-PHIEUPH | 114a HANGUL CHOSEONG IEUNG-THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 114d HANGUL CHOSEONG CIEUC-IEUNG | 114c HANGUL CHOSEONG YESIEUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 114f HANGUL CHOSEONG CHITUEUMSSANGCIEUC | 114e HANGUL CHOSEONG CHITUEUMCIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1151 HANGUL CHOSEONG CEONGCHIEUMSSANGCIEUC | 1150 HANGUL CHOSEONG CEONGCHIEUMCIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1153 HANGUL CHOSEONG CHIEUCH-HIEUH | 1152 HANGUL CHOSEONG CHIEUCH-KHIEUKH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1155 HANGUL CHOSEONG CEONGCHIEUMCHIEUCH | 1154 HANGUL CHOSEONG CHITUEUMCHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1157 HANGUL CHOSEONG KAPYEOUNPHIEUPH | 1156 HANGUL CHOSEONG PHIEUPH-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1159 HANGUL CHOSEONG YEORINHIEUH | 1158 HANGUL CHOSEONG SSANGHIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 115b HANGUL CHOSEONG NIEUN-SIOS | 115a HANGUL CHOSEONG KIYEOK-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 115d HANGUL CHOSEONG NIEUN-HIEUH | 115c HANGUL CHOSEONG NIEUN-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 115f HANGUL CHOSEONG FILLER | 115e HANGUL CHOSEONG TIKEUT-RIEUL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1161 HANGUL JUNGSEONG A | 1160 HANGUL JUNGSEONG FILLER */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1163 HANGUL JUNGSEONG YA | 1162 HANGUL JUNGSEONG AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1165 HANGUL JUNGSEONG EO | 1164 HANGUL JUNGSEONG YAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1167 HANGUL JUNGSEONG YEO | 1166 HANGUL JUNGSEONG E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1169 HANGUL JUNGSEONG O | 1168 HANGUL JUNGSEONG YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 116b HANGUL JUNGSEONG WAE | 116a HANGUL JUNGSEONG WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 116d HANGUL JUNGSEONG YO | 116c HANGUL JUNGSEONG OE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 116f HANGUL JUNGSEONG WEO | 116e HANGUL JUNGSEONG U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1171 HANGUL JUNGSEONG WI | 1170 HANGUL JUNGSEONG WE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1173 HANGUL JUNGSEONG EU | 1172 HANGUL JUNGSEONG YU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1175 HANGUL JUNGSEONG I | 1174 HANGUL JUNGSEONG YI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1177 HANGUL JUNGSEONG A-U | 1176 HANGUL JUNGSEONG A-O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1179 HANGUL JUNGSEONG YA-YO | 1178 HANGUL JUNGSEONG YA-O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 117b HANGUL JUNGSEONG EO-U | 117a HANGUL JUNGSEONG EO-O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 117d HANGUL JUNGSEONG YEO-O | 117c HANGUL JUNGSEONG EO-EU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 117f HANGUL JUNGSEONG O-EO | 117e HANGUL JUNGSEONG YEO-U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1181 HANGUL JUNGSEONG O-YE | 1180 HANGUL JUNGSEONG O-E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1183 HANGUL JUNGSEONG O-U | 1182 HANGUL JUNGSEONG O-O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1185 HANGUL JUNGSEONG YO-YAE | 1184 HANGUL JUNGSEONG YO-YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1187 HANGUL JUNGSEONG YO-O | 1186 HANGUL JUNGSEONG YO-YEO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1189 HANGUL JUNGSEONG U-A | 1188 HANGUL JUNGSEONG YO-I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 118b HANGUL JUNGSEONG U-EO-EU | 118a HANGUL JUNGSEONG U-AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 118d HANGUL JUNGSEONG U-U | 118c HANGUL JUNGSEONG U-YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 118f HANGUL JUNGSEONG YU-EO | 118e HANGUL JUNGSEONG YU-A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1191 HANGUL JUNGSEONG YU-YEO | 1190 HANGUL JUNGSEONG YU-E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1193 HANGUL JUNGSEONG YU-U | 1192 HANGUL JUNGSEONG YU-YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1195 HANGUL JUNGSEONG EU-U | 1194 HANGUL JUNGSEONG YU-I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1197 HANGUL JUNGSEONG YI-U | 1196 HANGUL JUNGSEONG EU-EU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1199 HANGUL JUNGSEONG I-YA | 1198 HANGUL JUNGSEONG I-A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 119b HANGUL JUNGSEONG I-U | 119a HANGUL JUNGSEONG I-O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 119d HANGUL JUNGSEONG I-ARAEA | 119c HANGUL JUNGSEONG I-EU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 119f HANGUL JUNGSEONG ARAEA-EO | 119e HANGUL JUNGSEONG ARAEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11a1 HANGUL JUNGSEONG ARAEA-I | 11a0 HANGUL JUNGSEONG ARAEA-U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11a3 HANGUL JUNGSEONG A-EU | 11a2 HANGUL JUNGSEONG SSANGARAEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11a5 HANGUL JUNGSEONG YEO-YA | 11a4 HANGUL JUNGSEONG YA-U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11a7 HANGUL JUNGSEONG O-YAE | 11a6 HANGUL JUNGSEONG O-YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11a9 HANGUL JONGSEONG SSANGKIYEOK | 11a8 HANGUL JONGSEONG KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11ab HANGUL JONGSEONG NIEUN | 11aa HANGUL JONGSEONG KIYEOK-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11ad HANGUL JONGSEONG NIEUN-HIEUH | 11ac HANGUL JONGSEONG NIEUN-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11af HANGUL JONGSEONG RIEUL | 11ae HANGUL JONGSEONG TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11b1 HANGUL JONGSEONG RIEUL-MIEUM | 11b0 HANGUL JONGSEONG RIEUL-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11b3 HANGUL JONGSEONG RIEUL-SIOS | 11b2 HANGUL JONGSEONG RIEUL-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11b5 HANGUL JONGSEONG RIEUL-PHIEUPH | 11b4 HANGUL JONGSEONG RIEUL-THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11b7 HANGUL JONGSEONG MIEUM | 11b6 HANGUL JONGSEONG RIEUL-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11b9 HANGUL JONGSEONG PIEUP-SIOS | 11b8 HANGUL JONGSEONG PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11bb HANGUL JONGSEONG SSANGSIOS | 11ba HANGUL JONGSEONG SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11bd HANGUL JONGSEONG CIEUC | 11bc HANGUL JONGSEONG IEUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11bf HANGUL JONGSEONG KHIEUKH | 11be HANGUL JONGSEONG CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11c1 HANGUL JONGSEONG PHIEUPH | 11c0 HANGUL JONGSEONG THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11c3 HANGUL JONGSEONG KIYEOK-RIEUL | 11c2 HANGUL JONGSEONG HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11c5 HANGUL JONGSEONG NIEUN-KIYEOK | 11c4 HANGUL JONGSEONG KIYEOK-SIOS-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11c7 HANGUL JONGSEONG NIEUN-SIOS | 11c6 HANGUL JONGSEONG NIEUN-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11c9 HANGUL JONGSEONG NIEUN-THIEUTH | 11c8 HANGUL JONGSEONG NIEUN-PANSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11cb HANGUL JONGSEONG TIKEUT-RIEUL | 11ca HANGUL JONGSEONG TIKEUT-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11cd HANGUL JONGSEONG RIEUL-NIEUN | 11cc HANGUL JONGSEONG RIEUL-KIYEOK-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11cf HANGUL JONGSEONG RIEUL-TIKEUT-HIEUH | 11ce HANGUL JONGSEONG RIEUL-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11d1 HANGUL JONGSEONG RIEUL-MIEUM-KIYEOK | 11d0 HANGUL JONGSEONG SSANGRIEUL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11d3 HANGUL JONGSEONG RIEUL-PIEUP-SIOS | 11d2 HANGUL JONGSEONG RIEUL-MIEUM-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11d5 HANGUL JONGSEONG RIEUL-KAPYEOUNPIEUP | 11d4 HANGUL JONGSEONG RIEUL-PIEUP-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11d7 HANGUL JONGSEONG RIEUL-PANSIOS | 11d6 HANGUL JONGSEONG RIEUL-SSANGSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11d9 HANGUL JONGSEONG RIEUL-YEORINHIEUH | 11d8 HANGUL JONGSEONG RIEUL-KHIEUKH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11db HANGUL JONGSEONG MIEUM-RIEUL | 11da HANGUL JONGSEONG MIEUM-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11dd HANGUL JONGSEONG MIEUM-SIOS | 11dc HANGUL JONGSEONG MIEUM-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11df HANGUL JONGSEONG MIEUM-PANSIOS | 11de HANGUL JONGSEONG MIEUM-SSANGSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11e1 HANGUL JONGSEONG MIEUM-HIEUH | 11e0 HANGUL JONGSEONG MIEUM-CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11e3 HANGUL JONGSEONG PIEUP-RIEUL | 11e2 HANGUL JONGSEONG KAPYEOUNMIEUM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11e5 HANGUL JONGSEONG PIEUP-HIEUH | 11e4 HANGUL JONGSEONG PIEUP-PHIEUPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11e7 HANGUL JONGSEONG SIOS-KIYEOK | 11e6 HANGUL JONGSEONG KAPYEOUNPIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11e9 HANGUL JONGSEONG SIOS-RIEUL | 11e8 HANGUL JONGSEONG SIOS-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11eb HANGUL JONGSEONG PANSIOS | 11ea HANGUL JONGSEONG SIOS-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11ed HANGUL JONGSEONG IEUNG-SSANGKIYEOK | 11ec HANGUL JONGSEONG IEUNG-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11ef HANGUL JONGSEONG IEUNG-KHIEUKH | 11ee HANGUL JONGSEONG SSANGIEUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11f1 HANGUL JONGSEONG YESIEUNG-SIOS | 11f0 HANGUL JONGSEONG YESIEUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11f3 HANGUL JONGSEONG PHIEUPH-PIEUP | 11f2 HANGUL JONGSEONG YESIEUNG-PANSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11f5 HANGUL JONGSEONG HIEUH-NIEUN | 11f4 HANGUL JONGSEONG KAPYEOUNPHIEUPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11f7 HANGUL JONGSEONG HIEUH-MIEUM | 11f6 HANGUL JONGSEONG HIEUH-RIEUL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11f9 HANGUL JONGSEONG YEORINHIEUH | 11f8 HANGUL JONGSEONG HIEUH-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11fb HANGUL JONGSEONG KIYEOK-PIEUP | 11fa HANGUL JONGSEONG KIYEOK-NIEUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11fd HANGUL JONGSEONG KIYEOK-KHIEUKH | 11fc HANGUL JONGSEONG KIYEOK-CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11ff HANGUL JONGSEONG SSANGNIEUN | 11fe HANGUL JONGSEONG KIYEOK-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1201 ETHIOPIC SYLLABLE HU | 1200 ETHIOPIC SYLLABLE HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1203 ETHIOPIC SYLLABLE HAA | 1202 ETHIOPIC SYLLABLE HI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1205 ETHIOPIC SYLLABLE HE | 1204 ETHIOPIC SYLLABLE HEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1207 ETHIOPIC SYLLABLE HOA | 1206 ETHIOPIC SYLLABLE HO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1209 ETHIOPIC SYLLABLE LU | 1208 ETHIOPIC SYLLABLE LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 120b ETHIOPIC SYLLABLE LAA | 120a ETHIOPIC SYLLABLE LI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 120d ETHIOPIC SYLLABLE LE | 120c ETHIOPIC SYLLABLE LEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 120f ETHIOPIC SYLLABLE LWA | 120e ETHIOPIC SYLLABLE LO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1211 ETHIOPIC SYLLABLE HHU | 1210 ETHIOPIC SYLLABLE HHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1213 ETHIOPIC SYLLABLE HHAA | 1212 ETHIOPIC SYLLABLE HHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1215 ETHIOPIC SYLLABLE HHE | 1214 ETHIOPIC SYLLABLE HHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1217 ETHIOPIC SYLLABLE HHWA | 1216 ETHIOPIC SYLLABLE HHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1219 ETHIOPIC SYLLABLE MU | 1218 ETHIOPIC SYLLABLE MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 121b ETHIOPIC SYLLABLE MAA | 121a ETHIOPIC SYLLABLE MI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 121d ETHIOPIC SYLLABLE ME | 121c ETHIOPIC SYLLABLE MEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 121f ETHIOPIC SYLLABLE MWA | 121e ETHIOPIC SYLLABLE MO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1221 ETHIOPIC SYLLABLE SZU | 1220 ETHIOPIC SYLLABLE SZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1223 ETHIOPIC SYLLABLE SZAA | 1222 ETHIOPIC SYLLABLE SZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1225 ETHIOPIC SYLLABLE SZE | 1224 ETHIOPIC SYLLABLE SZEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1227 ETHIOPIC SYLLABLE SZWA | 1226 ETHIOPIC SYLLABLE SZO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1229 ETHIOPIC SYLLABLE RU | 1228 ETHIOPIC SYLLABLE RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 122b ETHIOPIC SYLLABLE RAA | 122a ETHIOPIC SYLLABLE RI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 122d ETHIOPIC SYLLABLE RE | 122c ETHIOPIC SYLLABLE REE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 122f ETHIOPIC SYLLABLE RWA | 122e ETHIOPIC SYLLABLE RO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1231 ETHIOPIC SYLLABLE SU | 1230 ETHIOPIC SYLLABLE SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1233 ETHIOPIC SYLLABLE SAA | 1232 ETHIOPIC SYLLABLE SI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1235 ETHIOPIC SYLLABLE SE | 1234 ETHIOPIC SYLLABLE SEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1237 ETHIOPIC SYLLABLE SWA | 1236 ETHIOPIC SYLLABLE SO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1239 ETHIOPIC SYLLABLE SHU | 1238 ETHIOPIC SYLLABLE SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 123b ETHIOPIC SYLLABLE SHAA | 123a ETHIOPIC SYLLABLE SHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 123d ETHIOPIC SYLLABLE SHE | 123c ETHIOPIC SYLLABLE SHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 123f ETHIOPIC SYLLABLE SHWA | 123e ETHIOPIC SYLLABLE SHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1241 ETHIOPIC SYLLABLE QU | 1240 ETHIOPIC SYLLABLE QA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1243 ETHIOPIC SYLLABLE QAA | 1242 ETHIOPIC SYLLABLE QI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1245 ETHIOPIC SYLLABLE QE | 1244 ETHIOPIC SYLLABLE QEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1247 ETHIOPIC SYLLABLE QOA | 1246 ETHIOPIC SYLLABLE QO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 1249 (null) | 1248 ETHIOPIC SYLLABLE QWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 124b ETHIOPIC SYLLABLE QWAA | 124a ETHIOPIC SYLLABLE QWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 124d ETHIOPIC SYLLABLE QWE | 124c ETHIOPIC SYLLABLE QWEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 124f (null) | 124e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1251 ETHIOPIC SYLLABLE QHU | 1250 ETHIOPIC SYLLABLE QHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1253 ETHIOPIC SYLLABLE QHAA | 1252 ETHIOPIC SYLLABLE QHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1255 ETHIOPIC SYLLABLE QHE | 1254 ETHIOPIC SYLLABLE QHEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 1257 (null) | 1256 ETHIOPIC SYLLABLE QHO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 1259 (null) | 1258 ETHIOPIC SYLLABLE QHWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 125b ETHIOPIC SYLLABLE QHWAA | 125a ETHIOPIC SYLLABLE QHWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 125d ETHIOPIC SYLLABLE QHWE | 125c ETHIOPIC SYLLABLE QHWEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 125f (null) | 125e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1261 ETHIOPIC SYLLABLE BU | 1260 ETHIOPIC SYLLABLE BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1263 ETHIOPIC SYLLABLE BAA | 1262 ETHIOPIC SYLLABLE BI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1265 ETHIOPIC SYLLABLE BE | 1264 ETHIOPIC SYLLABLE BEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1267 ETHIOPIC SYLLABLE BWA | 1266 ETHIOPIC SYLLABLE BO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1269 ETHIOPIC SYLLABLE VU | 1268 ETHIOPIC SYLLABLE VA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 126b ETHIOPIC SYLLABLE VAA | 126a ETHIOPIC SYLLABLE VI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 126d ETHIOPIC SYLLABLE VE | 126c ETHIOPIC SYLLABLE VEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 126f ETHIOPIC SYLLABLE VWA | 126e ETHIOPIC SYLLABLE VO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1271 ETHIOPIC SYLLABLE TU | 1270 ETHIOPIC SYLLABLE TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1273 ETHIOPIC SYLLABLE TAA | 1272 ETHIOPIC SYLLABLE TI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1275 ETHIOPIC SYLLABLE TE | 1274 ETHIOPIC SYLLABLE TEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1277 ETHIOPIC SYLLABLE TWA | 1276 ETHIOPIC SYLLABLE TO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1279 ETHIOPIC SYLLABLE CU | 1278 ETHIOPIC SYLLABLE CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 127b ETHIOPIC SYLLABLE CAA | 127a ETHIOPIC SYLLABLE CI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 127d ETHIOPIC SYLLABLE CE | 127c ETHIOPIC SYLLABLE CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 127f ETHIOPIC SYLLABLE CWA | 127e ETHIOPIC SYLLABLE CO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1281 ETHIOPIC SYLLABLE XU | 1280 ETHIOPIC SYLLABLE XA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1283 ETHIOPIC SYLLABLE XAA | 1282 ETHIOPIC SYLLABLE XI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1285 ETHIOPIC SYLLABLE XE | 1284 ETHIOPIC SYLLABLE XEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1287 ETHIOPIC SYLLABLE XOA | 1286 ETHIOPIC SYLLABLE XO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 1289 (null) | 1288 ETHIOPIC SYLLABLE XWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 128b ETHIOPIC SYLLABLE XWAA | 128a ETHIOPIC SYLLABLE XWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 128d ETHIOPIC SYLLABLE XWE | 128c ETHIOPIC SYLLABLE XWEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 128f (null) | 128e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1291 ETHIOPIC SYLLABLE NU | 1290 ETHIOPIC SYLLABLE NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1293 ETHIOPIC SYLLABLE NAA | 1292 ETHIOPIC SYLLABLE NI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1295 ETHIOPIC SYLLABLE NE | 1294 ETHIOPIC SYLLABLE NEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1297 ETHIOPIC SYLLABLE NWA | 1296 ETHIOPIC SYLLABLE NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1299 ETHIOPIC SYLLABLE NYU | 1298 ETHIOPIC SYLLABLE NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 129b ETHIOPIC SYLLABLE NYAA | 129a ETHIOPIC SYLLABLE NYI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 129d ETHIOPIC SYLLABLE NYE | 129c ETHIOPIC SYLLABLE NYEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 129f ETHIOPIC SYLLABLE NYWA | 129e ETHIOPIC SYLLABLE NYO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12a1 ETHIOPIC SYLLABLE GLOTTAL U | 12a0 ETHIOPIC SYLLABLE GLOTTAL A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12a3 ETHIOPIC SYLLABLE GLOTTAL AA | 12a2 ETHIOPIC SYLLABLE GLOTTAL I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12a5 ETHIOPIC SYLLABLE GLOTTAL E | 12a4 ETHIOPIC SYLLABLE GLOTTAL EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12a7 ETHIOPIC SYLLABLE GLOTTAL WA | 12a6 ETHIOPIC SYLLABLE GLOTTAL O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12a9 ETHIOPIC SYLLABLE KU | 12a8 ETHIOPIC SYLLABLE KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12ab ETHIOPIC SYLLABLE KAA | 12aa ETHIOPIC SYLLABLE KI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12ad ETHIOPIC SYLLABLE KE | 12ac ETHIOPIC SYLLABLE KEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12af ETHIOPIC SYLLABLE KOA | 12ae ETHIOPIC SYLLABLE KO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 12b1 (null) | 12b0 ETHIOPIC SYLLABLE KWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12b3 ETHIOPIC SYLLABLE KWAA | 12b2 ETHIOPIC SYLLABLE KWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12b5 ETHIOPIC SYLLABLE KWE | 12b4 ETHIOPIC SYLLABLE KWEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 12b7 (null) | 12b6 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12b9 ETHIOPIC SYLLABLE KXU | 12b8 ETHIOPIC SYLLABLE KXA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12bb ETHIOPIC SYLLABLE KXAA | 12ba ETHIOPIC SYLLABLE KXI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12bd ETHIOPIC SYLLABLE KXE | 12bc ETHIOPIC SYLLABLE KXEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 12bf (null) | 12be ETHIOPIC SYLLABLE KXO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 12c1 (null) | 12c0 ETHIOPIC SYLLABLE KXWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12c3 ETHIOPIC SYLLABLE KXWAA | 12c2 ETHIOPIC SYLLABLE KXWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12c5 ETHIOPIC SYLLABLE KXWE | 12c4 ETHIOPIC SYLLABLE KXWEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 12c7 (null) | 12c6 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12c9 ETHIOPIC SYLLABLE WU | 12c8 ETHIOPIC SYLLABLE WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12cb ETHIOPIC SYLLABLE WAA | 12ca ETHIOPIC SYLLABLE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12cd ETHIOPIC SYLLABLE WE | 12cc ETHIOPIC SYLLABLE WEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12cf ETHIOPIC SYLLABLE WOA | 12ce ETHIOPIC SYLLABLE WO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12d1 ETHIOPIC SYLLABLE PHARYNGEAL U | 12d0 ETHIOPIC SYLLABLE PHARYNGEAL A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12d3 ETHIOPIC SYLLABLE PHARYNGEAL AA | 12d2 ETHIOPIC SYLLABLE PHARYNGEAL I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12d5 ETHIOPIC SYLLABLE PHARYNGEAL E | 12d4 ETHIOPIC SYLLABLE PHARYNGEAL EE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 12d7 (null) | 12d6 ETHIOPIC SYLLABLE PHARYNGEAL O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12d9 ETHIOPIC SYLLABLE ZU | 12d8 ETHIOPIC SYLLABLE ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12db ETHIOPIC SYLLABLE ZAA | 12da ETHIOPIC SYLLABLE ZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12dd ETHIOPIC SYLLABLE ZE | 12dc ETHIOPIC SYLLABLE ZEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12df ETHIOPIC SYLLABLE ZWA | 12de ETHIOPIC SYLLABLE ZO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12e1 ETHIOPIC SYLLABLE ZHU | 12e0 ETHIOPIC SYLLABLE ZHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12e3 ETHIOPIC SYLLABLE ZHAA | 12e2 ETHIOPIC SYLLABLE ZHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12e5 ETHIOPIC SYLLABLE ZHE | 12e4 ETHIOPIC SYLLABLE ZHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12e7 ETHIOPIC SYLLABLE ZHWA | 12e6 ETHIOPIC SYLLABLE ZHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12e9 ETHIOPIC SYLLABLE YU | 12e8 ETHIOPIC SYLLABLE YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12eb ETHIOPIC SYLLABLE YAA | 12ea ETHIOPIC SYLLABLE YI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12ed ETHIOPIC SYLLABLE YE | 12ec ETHIOPIC SYLLABLE YEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12ef ETHIOPIC SYLLABLE YOA | 12ee ETHIOPIC SYLLABLE YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12f1 ETHIOPIC SYLLABLE DU | 12f0 ETHIOPIC SYLLABLE DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12f3 ETHIOPIC SYLLABLE DAA | 12f2 ETHIOPIC SYLLABLE DI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12f5 ETHIOPIC SYLLABLE DE | 12f4 ETHIOPIC SYLLABLE DEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12f7 ETHIOPIC SYLLABLE DWA | 12f6 ETHIOPIC SYLLABLE DO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12f9 ETHIOPIC SYLLABLE DDU | 12f8 ETHIOPIC SYLLABLE DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12fb ETHIOPIC SYLLABLE DDAA | 12fa ETHIOPIC SYLLABLE DDI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12fd ETHIOPIC SYLLABLE DDE | 12fc ETHIOPIC SYLLABLE DDEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12ff ETHIOPIC SYLLABLE DDWA | 12fe ETHIOPIC SYLLABLE DDO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1301 ETHIOPIC SYLLABLE JU | 1300 ETHIOPIC SYLLABLE JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1303 ETHIOPIC SYLLABLE JAA | 1302 ETHIOPIC SYLLABLE JI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1305 ETHIOPIC SYLLABLE JE | 1304 ETHIOPIC SYLLABLE JEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1307 ETHIOPIC SYLLABLE JWA | 1306 ETHIOPIC SYLLABLE JO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1309 ETHIOPIC SYLLABLE GU | 1308 ETHIOPIC SYLLABLE GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 130b ETHIOPIC SYLLABLE GAA | 130a ETHIOPIC SYLLABLE GI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 130d ETHIOPIC SYLLABLE GE | 130c ETHIOPIC SYLLABLE GEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 130f ETHIOPIC SYLLABLE GOA | 130e ETHIOPIC SYLLABLE GO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 1311 (null) | 1310 ETHIOPIC SYLLABLE GWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1313 ETHIOPIC SYLLABLE GWAA | 1312 ETHIOPIC SYLLABLE GWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1315 ETHIOPIC SYLLABLE GWE | 1314 ETHIOPIC SYLLABLE GWEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1317 (null) | 1316 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1319 ETHIOPIC SYLLABLE GGU | 1318 ETHIOPIC SYLLABLE GGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 131b ETHIOPIC SYLLABLE GGAA | 131a ETHIOPIC SYLLABLE GGI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 131d ETHIOPIC SYLLABLE GGE | 131c ETHIOPIC SYLLABLE GGEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 131f ETHIOPIC SYLLABLE GGWAA | 131e ETHIOPIC SYLLABLE GGO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1321 ETHIOPIC SYLLABLE THU | 1320 ETHIOPIC SYLLABLE THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1323 ETHIOPIC SYLLABLE THAA | 1322 ETHIOPIC SYLLABLE THI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1325 ETHIOPIC SYLLABLE THE | 1324 ETHIOPIC SYLLABLE THEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1327 ETHIOPIC SYLLABLE THWA | 1326 ETHIOPIC SYLLABLE THO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1329 ETHIOPIC SYLLABLE CHU | 1328 ETHIOPIC SYLLABLE CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 132b ETHIOPIC SYLLABLE CHAA | 132a ETHIOPIC SYLLABLE CHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 132d ETHIOPIC SYLLABLE CHE | 132c ETHIOPIC SYLLABLE CHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 132f ETHIOPIC SYLLABLE CHWA | 132e ETHIOPIC SYLLABLE CHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1331 ETHIOPIC SYLLABLE PHU | 1330 ETHIOPIC SYLLABLE PHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1333 ETHIOPIC SYLLABLE PHAA | 1332 ETHIOPIC SYLLABLE PHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1335 ETHIOPIC SYLLABLE PHE | 1334 ETHIOPIC SYLLABLE PHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1337 ETHIOPIC SYLLABLE PHWA | 1336 ETHIOPIC SYLLABLE PHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1339 ETHIOPIC SYLLABLE TSU | 1338 ETHIOPIC SYLLABLE TSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 133b ETHIOPIC SYLLABLE TSAA | 133a ETHIOPIC SYLLABLE TSI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 133d ETHIOPIC SYLLABLE TSE | 133c ETHIOPIC SYLLABLE TSEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 133f ETHIOPIC SYLLABLE TSWA | 133e ETHIOPIC SYLLABLE TSO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1341 ETHIOPIC SYLLABLE TZU | 1340 ETHIOPIC SYLLABLE TZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1343 ETHIOPIC SYLLABLE TZAA | 1342 ETHIOPIC SYLLABLE TZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1345 ETHIOPIC SYLLABLE TZE | 1344 ETHIOPIC SYLLABLE TZEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1347 ETHIOPIC SYLLABLE TZOA | 1346 ETHIOPIC SYLLABLE TZO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1349 ETHIOPIC SYLLABLE FU | 1348 ETHIOPIC SYLLABLE FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 134b ETHIOPIC SYLLABLE FAA | 134a ETHIOPIC SYLLABLE FI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 134d ETHIOPIC SYLLABLE FE | 134c ETHIOPIC SYLLABLE FEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 134f ETHIOPIC SYLLABLE FWA | 134e ETHIOPIC SYLLABLE FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1351 ETHIOPIC SYLLABLE PU | 1350 ETHIOPIC SYLLABLE PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1353 ETHIOPIC SYLLABLE PAA | 1352 ETHIOPIC SYLLABLE PI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1355 ETHIOPIC SYLLABLE PE | 1354 ETHIOPIC SYLLABLE PEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1357 ETHIOPIC SYLLABLE PWA | 1356 ETHIOPIC SYLLABLE PO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1359 ETHIOPIC SYLLABLE MYA | 1358 ETHIOPIC SYLLABLE RYA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 135b (null) | 135a ETHIOPIC SYLLABLE FYA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 135d ETHIOPIC COMBINING GEMINATION AND VOWEL | 135c (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 135f ETHIOPIC COMBINING GEMINATION MARK | 135e ETHIOPIC COMBINING VOWEL LENGTH MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1361 ETHIOPIC WORDSPACE | 1360 ETHIOPIC SECTION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1363 ETHIOPIC COMMA | 1362 ETHIOPIC FULL STOP */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1365 ETHIOPIC COLON | 1364 ETHIOPIC SEMICOLON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1367 ETHIOPIC QUESTION MARK | 1366 ETHIOPIC PREFACE COLON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 1369 ETHIOPIC DIGIT ONE | 1368 ETHIOPIC PARAGRAPH SEPARATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 136b ETHIOPIC DIGIT THREE | 136a ETHIOPIC DIGIT TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 136d ETHIOPIC DIGIT FIVE | 136c ETHIOPIC DIGIT FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 136f ETHIOPIC DIGIT SEVEN | 136e ETHIOPIC DIGIT SIX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1371 ETHIOPIC DIGIT NINE | 1370 ETHIOPIC DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1373 ETHIOPIC NUMBER TWENTY | 1372 ETHIOPIC NUMBER TEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1375 ETHIOPIC NUMBER FORTY | 1374 ETHIOPIC NUMBER THIRTY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1377 ETHIOPIC NUMBER SIXTY | 1376 ETHIOPIC NUMBER FIFTY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1379 ETHIOPIC NUMBER EIGHTY | 1378 ETHIOPIC NUMBER SEVENTY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 137b ETHIOPIC NUMBER HUNDRED | 137a ETHIOPIC NUMBER NINETY */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 137d (null) | 137c ETHIOPIC NUMBER TEN THOUSAND */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 137f (null) | 137e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1381 ETHIOPIC SYLLABLE MWI | 1380 ETHIOPIC SYLLABLE SEBATBEIT MWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1383 ETHIOPIC SYLLABLE MWE | 1382 ETHIOPIC SYLLABLE MWEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1385 ETHIOPIC SYLLABLE BWI | 1384 ETHIOPIC SYLLABLE SEBATBEIT BWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1387 ETHIOPIC SYLLABLE BWE | 1386 ETHIOPIC SYLLABLE BWEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1389 ETHIOPIC SYLLABLE FWI | 1388 ETHIOPIC SYLLABLE SEBATBEIT FWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 138b ETHIOPIC SYLLABLE FWE | 138a ETHIOPIC SYLLABLE FWEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 138d ETHIOPIC SYLLABLE PWI | 138c ETHIOPIC SYLLABLE SEBATBEIT PWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 138f ETHIOPIC SYLLABLE PWE | 138e ETHIOPIC SYLLABLE PWEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1391 ETHIOPIC TONAL MARK DERET | 1390 ETHIOPIC TONAL MARK YIZET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1393 ETHIOPIC TONAL MARK SHORT RIKRIK | 1392 ETHIOPIC TONAL MARK RIKRIK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1395 ETHIOPIC TONAL MARK KENAT | 1394 ETHIOPIC TONAL MARK DIFAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1397 ETHIOPIC TONAL MARK HIDET | 1396 ETHIOPIC TONAL MARK CHIRET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1399 ETHIOPIC TONAL MARK KURT | 1398 ETHIOPIC TONAL MARK DERET-HIDET */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 139b (null) | 139a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 139d (null) | 139c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 139f (null) | 139e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13a1 CHEROKEE LETTER E | 13a0 CHEROKEE LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13a3 CHEROKEE LETTER O | 13a2 CHEROKEE LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13a5 CHEROKEE LETTER V | 13a4 CHEROKEE LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13a7 CHEROKEE LETTER KA | 13a6 CHEROKEE LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13a9 CHEROKEE LETTER GI | 13a8 CHEROKEE LETTER GE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13ab CHEROKEE LETTER GU | 13aa CHEROKEE LETTER GO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13ad CHEROKEE LETTER HA | 13ac CHEROKEE LETTER GV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13af CHEROKEE LETTER HI | 13ae CHEROKEE LETTER HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13b1 CHEROKEE LETTER HU | 13b0 CHEROKEE LETTER HO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13b3 CHEROKEE LETTER LA | 13b2 CHEROKEE LETTER HV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13b5 CHEROKEE LETTER LI | 13b4 CHEROKEE LETTER LE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13b7 CHEROKEE LETTER LU | 13b6 CHEROKEE LETTER LO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13b9 CHEROKEE LETTER MA | 13b8 CHEROKEE LETTER LV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13bb CHEROKEE LETTER MI | 13ba CHEROKEE LETTER ME */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13bd CHEROKEE LETTER MU | 13bc CHEROKEE LETTER MO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13bf CHEROKEE LETTER HNA | 13be CHEROKEE LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13c1 CHEROKEE LETTER NE | 13c0 CHEROKEE LETTER NAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13c3 CHEROKEE LETTER NO | 13c2 CHEROKEE LETTER NI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13c5 CHEROKEE LETTER NV | 13c4 CHEROKEE LETTER NU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13c7 CHEROKEE LETTER QUE | 13c6 CHEROKEE LETTER QUA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13c9 CHEROKEE LETTER QUO | 13c8 CHEROKEE LETTER QUI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13cb CHEROKEE LETTER QUV | 13ca CHEROKEE LETTER QUU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13cd CHEROKEE LETTER S | 13cc CHEROKEE LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13cf CHEROKEE LETTER SI | 13ce CHEROKEE LETTER SE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13d1 CHEROKEE LETTER SU | 13d0 CHEROKEE LETTER SO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13d3 CHEROKEE LETTER DA | 13d2 CHEROKEE LETTER SV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13d5 CHEROKEE LETTER DE | 13d4 CHEROKEE LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13d7 CHEROKEE LETTER DI | 13d6 CHEROKEE LETTER TE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13d9 CHEROKEE LETTER DO | 13d8 CHEROKEE LETTER TI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13db CHEROKEE LETTER DV | 13da CHEROKEE LETTER DU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13dd CHEROKEE LETTER TLA | 13dc CHEROKEE LETTER DLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13df CHEROKEE LETTER TLI | 13de CHEROKEE LETTER TLE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13e1 CHEROKEE LETTER TLU | 13e0 CHEROKEE LETTER TLO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13e3 CHEROKEE LETTER TSA | 13e2 CHEROKEE LETTER TLV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13e5 CHEROKEE LETTER TSI | 13e4 CHEROKEE LETTER TSE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13e7 CHEROKEE LETTER TSU | 13e6 CHEROKEE LETTER TSO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13e9 CHEROKEE LETTER WA | 13e8 CHEROKEE LETTER TSV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13eb CHEROKEE LETTER WI | 13ea CHEROKEE LETTER WE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13ed CHEROKEE LETTER WU | 13ec CHEROKEE LETTER WO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13ef CHEROKEE LETTER YA | 13ee CHEROKEE LETTER WV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13f1 CHEROKEE LETTER YI | 13f0 CHEROKEE LETTER YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13f3 CHEROKEE LETTER YU | 13f2 CHEROKEE LETTER YO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 13f5 (null) | 13f4 CHEROKEE LETTER YV */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 13f7 (null) | 13f6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 13f9 (null) | 13f8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 13fb (null) | 13fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 13fd (null) | 13fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 13ff (null) | 13fe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_PUNCT, /* 1401 CANADIAN SYLLABICS E | 1400 CANADIAN SYLLABICS HYPHEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1403 CANADIAN SYLLABICS I | 1402 CANADIAN SYLLABICS AAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1405 CANADIAN SYLLABICS O | 1404 CANADIAN SYLLABICS II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1407 CANADIAN SYLLABICS Y-CREE OO | 1406 CANADIAN SYLLABICS OO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1409 CANADIAN SYLLABICS CARRIER I | 1408 CANADIAN SYLLABICS CARRIER EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 140b CANADIAN SYLLABICS AA | 140a CANADIAN SYLLABICS A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 140d CANADIAN SYLLABICS WEST-CREE WE | 140c CANADIAN SYLLABICS WE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 140f CANADIAN SYLLABICS WEST-CREE WI | 140e CANADIAN SYLLABICS WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1411 CANADIAN SYLLABICS WEST-CREE WII | 1410 CANADIAN SYLLABICS WII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1413 CANADIAN SYLLABICS WEST-CREE WO | 1412 CANADIAN SYLLABICS WO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1415 CANADIAN SYLLABICS WEST-CREE WOO | 1414 CANADIAN SYLLABICS WOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1417 CANADIAN SYLLABICS WA | 1416 CANADIAN SYLLABICS NASKAPI WOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1419 CANADIAN SYLLABICS WAA | 1418 CANADIAN SYLLABICS WEST-CREE WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 141b CANADIAN SYLLABICS NASKAPI WAA | 141a CANADIAN SYLLABICS WEST-CREE WAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 141d CANADIAN SYLLABICS Y-CREE W | 141c CANADIAN SYLLABICS AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 141f CANADIAN SYLLABICS FINAL ACUTE | 141e CANADIAN SYLLABICS GLOTTAL STOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1421 CANADIAN SYLLABICS FINAL BOTTOM HALF RI | 1420 CANADIAN SYLLABICS FINAL GRAVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1423 CANADIAN SYLLABICS FINAL RIGHT HALF RIN | 1422 CANADIAN SYLLABICS FINAL TOP HALF RING */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1425 CANADIAN SYLLABICS FINAL DOUBLE ACUTE | 1424 CANADIAN SYLLABICS FINAL RING */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1427 CANADIAN SYLLABICS FINAL MIDDLE DOT | 1426 CANADIAN SYLLABICS FINAL DOUBLE SHORT V */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1429 CANADIAN SYLLABICS FINAL PLUS | 1428 CANADIAN SYLLABICS FINAL SHORT HORIZONT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 142b CANADIAN SYLLABICS EN | 142a CANADIAN SYLLABICS FINAL DOWN TACK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 142d CANADIAN SYLLABICS ON | 142c CANADIAN SYLLABICS IN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 142f CANADIAN SYLLABICS PE | 142e CANADIAN SYLLABICS AN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1431 CANADIAN SYLLABICS PI | 1430 CANADIAN SYLLABICS PAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1433 CANADIAN SYLLABICS PO | 1432 CANADIAN SYLLABICS PII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1435 CANADIAN SYLLABICS Y-CREE POO | 1434 CANADIAN SYLLABICS POO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1437 CANADIAN SYLLABICS CARRIER HI | 1436 CANADIAN SYLLABICS CARRIER HEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1439 CANADIAN SYLLABICS PAA | 1438 CANADIAN SYLLABICS PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 143b CANADIAN SYLLABICS WEST-CREE PWE | 143a CANADIAN SYLLABICS PWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 143d CANADIAN SYLLABICS WEST-CREE PWI | 143c CANADIAN SYLLABICS PWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 143f CANADIAN SYLLABICS WEST-CREE PWII | 143e CANADIAN SYLLABICS PWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1441 CANADIAN SYLLABICS WEST-CREE PWO | 1440 CANADIAN SYLLABICS PWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1443 CANADIAN SYLLABICS WEST-CREE PWOO | 1442 CANADIAN SYLLABICS PWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1445 CANADIAN SYLLABICS WEST-CREE PWA | 1444 CANADIAN SYLLABICS PWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1447 CANADIAN SYLLABICS WEST-CREE PWAA | 1446 CANADIAN SYLLABICS PWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1449 CANADIAN SYLLABICS P | 1448 CANADIAN SYLLABICS Y-CREE PWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 144b CANADIAN SYLLABICS CARRIER H | 144a CANADIAN SYLLABICS WEST-CREE P */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 144d CANADIAN SYLLABICS TAAI | 144c CANADIAN SYLLABICS TE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 144f CANADIAN SYLLABICS TII | 144e CANADIAN SYLLABICS TI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1451 CANADIAN SYLLABICS TOO | 1450 CANADIAN SYLLABICS TO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1453 CANADIAN SYLLABICS CARRIER DEE | 1452 CANADIAN SYLLABICS Y-CREE TOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1455 CANADIAN SYLLABICS TA | 1454 CANADIAN SYLLABICS CARRIER DI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1457 CANADIAN SYLLABICS TWE | 1456 CANADIAN SYLLABICS TAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1459 CANADIAN SYLLABICS TWI | 1458 CANADIAN SYLLABICS WEST-CREE TWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 145b CANADIAN SYLLABICS TWII | 145a CANADIAN SYLLABICS WEST-CREE TWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 145d CANADIAN SYLLABICS TWO | 145c CANADIAN SYLLABICS WEST-CREE TWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 145f CANADIAN SYLLABICS TWOO | 145e CANADIAN SYLLABICS WEST-CREE TWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1461 CANADIAN SYLLABICS TWA | 1460 CANADIAN SYLLABICS WEST-CREE TWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1463 CANADIAN SYLLABICS TWAA | 1462 CANADIAN SYLLABICS WEST-CREE TWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1465 CANADIAN SYLLABICS NASKAPI TWAA | 1464 CANADIAN SYLLABICS WEST-CREE TWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1467 CANADIAN SYLLABICS TTE | 1466 CANADIAN SYLLABICS T */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1469 CANADIAN SYLLABICS TTO | 1468 CANADIAN SYLLABICS TTI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 146b CANADIAN SYLLABICS KE | 146a CANADIAN SYLLABICS TTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 146d CANADIAN SYLLABICS KI | 146c CANADIAN SYLLABICS KAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 146f CANADIAN SYLLABICS KO | 146e CANADIAN SYLLABICS KII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1471 CANADIAN SYLLABICS Y-CREE KOO | 1470 CANADIAN SYLLABICS KOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1473 CANADIAN SYLLABICS KAA | 1472 CANADIAN SYLLABICS KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1475 CANADIAN SYLLABICS WEST-CREE KWE | 1474 CANADIAN SYLLABICS KWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1477 CANADIAN SYLLABICS WEST-CREE KWI | 1476 CANADIAN SYLLABICS KWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1479 CANADIAN SYLLABICS WEST-CREE KWII | 1478 CANADIAN SYLLABICS KWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 147b CANADIAN SYLLABICS WEST-CREE KWO | 147a CANADIAN SYLLABICS KWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 147d CANADIAN SYLLABICS WEST-CREE KWOO | 147c CANADIAN SYLLABICS KWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 147f CANADIAN SYLLABICS WEST-CREE KWA | 147e CANADIAN SYLLABICS KWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1481 CANADIAN SYLLABICS WEST-CREE KWAA | 1480 CANADIAN SYLLABICS KWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1483 CANADIAN SYLLABICS K | 1482 CANADIAN SYLLABICS NASKAPI KWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1485 CANADIAN SYLLABICS SOUTH-SLAVEY KEH | 1484 CANADIAN SYLLABICS KW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1487 CANADIAN SYLLABICS SOUTH-SLAVEY KOH | 1486 CANADIAN SYLLABICS SOUTH-SLAVEY KIH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1489 CANADIAN SYLLABICS CE | 1488 CANADIAN SYLLABICS SOUTH-SLAVEY KAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 148b CANADIAN SYLLABICS CI | 148a CANADIAN SYLLABICS CAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 148d CANADIAN SYLLABICS CO | 148c CANADIAN SYLLABICS CII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 148f CANADIAN SYLLABICS Y-CREE COO | 148e CANADIAN SYLLABICS COO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1491 CANADIAN SYLLABICS CAA | 1490 CANADIAN SYLLABICS CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1493 CANADIAN SYLLABICS WEST-CREE CWE | 1492 CANADIAN SYLLABICS CWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1495 CANADIAN SYLLABICS WEST-CREE CWI | 1494 CANADIAN SYLLABICS CWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1497 CANADIAN SYLLABICS WEST-CREE CWII | 1496 CANADIAN SYLLABICS CWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1499 CANADIAN SYLLABICS WEST-CREE CWO | 1498 CANADIAN SYLLABICS CWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 149b CANADIAN SYLLABICS WEST-CREE CWOO | 149a CANADIAN SYLLABICS CWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 149d CANADIAN SYLLABICS WEST-CREE CWA | 149c CANADIAN SYLLABICS CWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 149f CANADIAN SYLLABICS WEST-CREE CWAA | 149e CANADIAN SYLLABICS CWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14a1 CANADIAN SYLLABICS C | 14a0 CANADIAN SYLLABICS NASKAPI CWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14a3 CANADIAN SYLLABICS ME | 14a2 CANADIAN SYLLABICS SAYISI TH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14a5 CANADIAN SYLLABICS MI | 14a4 CANADIAN SYLLABICS MAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14a7 CANADIAN SYLLABICS MO | 14a6 CANADIAN SYLLABICS MII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14a9 CANADIAN SYLLABICS Y-CREE MOO | 14a8 CANADIAN SYLLABICS MOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14ab CANADIAN SYLLABICS MAA | 14aa CANADIAN SYLLABICS MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14ad CANADIAN SYLLABICS WEST-CREE MWE | 14ac CANADIAN SYLLABICS MWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14af CANADIAN SYLLABICS WEST-CREE MWI | 14ae CANADIAN SYLLABICS MWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14b1 CANADIAN SYLLABICS WEST-CREE MWII | 14b0 CANADIAN SYLLABICS MWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14b3 CANADIAN SYLLABICS WEST-CREE MWO | 14b2 CANADIAN SYLLABICS MWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14b5 CANADIAN SYLLABICS WEST-CREE MWOO | 14b4 CANADIAN SYLLABICS MWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14b7 CANADIAN SYLLABICS WEST-CREE MWA | 14b6 CANADIAN SYLLABICS MWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14b9 CANADIAN SYLLABICS WEST-CREE MWAA | 14b8 CANADIAN SYLLABICS MWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14bb CANADIAN SYLLABICS M | 14ba CANADIAN SYLLABICS NASKAPI MWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14bd CANADIAN SYLLABICS MH | 14bc CANADIAN SYLLABICS WEST-CREE M */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14bf CANADIAN SYLLABICS SAYISI M | 14be CANADIAN SYLLABICS ATHAPASCAN M */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14c1 CANADIAN SYLLABICS NAAI | 14c0 CANADIAN SYLLABICS NE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14c3 CANADIAN SYLLABICS NII | 14c2 CANADIAN SYLLABICS NI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14c5 CANADIAN SYLLABICS NOO | 14c4 CANADIAN SYLLABICS NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14c7 CANADIAN SYLLABICS NA | 14c6 CANADIAN SYLLABICS Y-CREE NOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14c9 CANADIAN SYLLABICS NWE | 14c8 CANADIAN SYLLABICS NAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14cb CANADIAN SYLLABICS NWA | 14ca CANADIAN SYLLABICS WEST-CREE NWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14cd CANADIAN SYLLABICS NWAA | 14cc CANADIAN SYLLABICS WEST-CREE NWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14cf CANADIAN SYLLABICS NASKAPI NWAA | 14ce CANADIAN SYLLABICS WEST-CREE NWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14d1 CANADIAN SYLLABICS CARRIER NG | 14d0 CANADIAN SYLLABICS N */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14d3 CANADIAN SYLLABICS LE | 14d2 CANADIAN SYLLABICS NH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14d5 CANADIAN SYLLABICS LI | 14d4 CANADIAN SYLLABICS LAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14d7 CANADIAN SYLLABICS LO | 14d6 CANADIAN SYLLABICS LII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14d9 CANADIAN SYLLABICS Y-CREE LOO | 14d8 CANADIAN SYLLABICS LOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14db CANADIAN SYLLABICS LAA | 14da CANADIAN SYLLABICS LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14dd CANADIAN SYLLABICS WEST-CREE LWE | 14dc CANADIAN SYLLABICS LWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14df CANADIAN SYLLABICS WEST-CREE LWI | 14de CANADIAN SYLLABICS LWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14e1 CANADIAN SYLLABICS WEST-CREE LWII | 14e0 CANADIAN SYLLABICS LWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14e3 CANADIAN SYLLABICS WEST-CREE LWO | 14e2 CANADIAN SYLLABICS LWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14e5 CANADIAN SYLLABICS WEST-CREE LWOO | 14e4 CANADIAN SYLLABICS LWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14e7 CANADIAN SYLLABICS WEST-CREE LWA | 14e6 CANADIAN SYLLABICS LWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14e9 CANADIAN SYLLABICS WEST-CREE LWAA | 14e8 CANADIAN SYLLABICS LWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14eb CANADIAN SYLLABICS WEST-CREE L | 14ea CANADIAN SYLLABICS L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14ed CANADIAN SYLLABICS SE | 14ec CANADIAN SYLLABICS MEDIAL L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14ef CANADIAN SYLLABICS SI | 14ee CANADIAN SYLLABICS SAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14f1 CANADIAN SYLLABICS SO | 14f0 CANADIAN SYLLABICS SII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14f3 CANADIAN SYLLABICS Y-CREE SOO | 14f2 CANADIAN SYLLABICS SOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14f5 CANADIAN SYLLABICS SAA | 14f4 CANADIAN SYLLABICS SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14f7 CANADIAN SYLLABICS WEST-CREE SWE | 14f6 CANADIAN SYLLABICS SWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14f9 CANADIAN SYLLABICS WEST-CREE SWI | 14f8 CANADIAN SYLLABICS SWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14fb CANADIAN SYLLABICS WEST-CREE SWII | 14fa CANADIAN SYLLABICS SWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14fd CANADIAN SYLLABICS WEST-CREE SWO | 14fc CANADIAN SYLLABICS SWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14ff CANADIAN SYLLABICS WEST-CREE SWOO | 14fe CANADIAN SYLLABICS SWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1501 CANADIAN SYLLABICS WEST-CREE SWA | 1500 CANADIAN SYLLABICS SWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1503 CANADIAN SYLLABICS WEST-CREE SWAA | 1502 CANADIAN SYLLABICS SWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1505 CANADIAN SYLLABICS S | 1504 CANADIAN SYLLABICS NASKAPI SWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1507 CANADIAN SYLLABICS SW | 1506 CANADIAN SYLLABICS ATHAPASCAN S */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1509 CANADIAN SYLLABICS MOOSE-CREE SK | 1508 CANADIAN SYLLABICS BLACKFOOT S */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 150b CANADIAN SYLLABICS NASKAPI S-W | 150a CANADIAN SYLLABICS NASKAPI SKW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 150d CANADIAN SYLLABICS NASKAPI STWA | 150c CANADIAN SYLLABICS NASKAPI SPWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 150f CANADIAN SYLLABICS NASKAPI SCWA | 150e CANADIAN SYLLABICS NASKAPI SKWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1511 CANADIAN SYLLABICS SHI | 1510 CANADIAN SYLLABICS SHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1513 CANADIAN SYLLABICS SHO | 1512 CANADIAN SYLLABICS SHII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1515 CANADIAN SYLLABICS SHA | 1514 CANADIAN SYLLABICS SHOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1517 CANADIAN SYLLABICS SHWE | 1516 CANADIAN SYLLABICS SHAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1519 CANADIAN SYLLABICS SHWI | 1518 CANADIAN SYLLABICS WEST-CREE SHWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 151b CANADIAN SYLLABICS SHWII | 151a CANADIAN SYLLABICS WEST-CREE SHWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 151d CANADIAN SYLLABICS SHWO | 151c CANADIAN SYLLABICS WEST-CREE SHWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 151f CANADIAN SYLLABICS SHWOO | 151e CANADIAN SYLLABICS WEST-CREE SHWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1521 CANADIAN SYLLABICS SHWA | 1520 CANADIAN SYLLABICS WEST-CREE SHWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1523 CANADIAN SYLLABICS SHWAA | 1522 CANADIAN SYLLABICS WEST-CREE SHWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1525 CANADIAN SYLLABICS SH | 1524 CANADIAN SYLLABICS WEST-CREE SHWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1527 CANADIAN SYLLABICS YAAI | 1526 CANADIAN SYLLABICS YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1529 CANADIAN SYLLABICS YII | 1528 CANADIAN SYLLABICS YI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 152b CANADIAN SYLLABICS YOO | 152a CANADIAN SYLLABICS YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 152d CANADIAN SYLLABICS YA | 152c CANADIAN SYLLABICS Y-CREE YOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 152f CANADIAN SYLLABICS YWE | 152e CANADIAN SYLLABICS YAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1531 CANADIAN SYLLABICS YWI | 1530 CANADIAN SYLLABICS WEST-CREE YWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1533 CANADIAN SYLLABICS YWII | 1532 CANADIAN SYLLABICS WEST-CREE YWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1535 CANADIAN SYLLABICS YWO | 1534 CANADIAN SYLLABICS WEST-CREE YWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1537 CANADIAN SYLLABICS YWOO | 1536 CANADIAN SYLLABICS WEST-CREE YWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1539 CANADIAN SYLLABICS YWA | 1538 CANADIAN SYLLABICS WEST-CREE YWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 153b CANADIAN SYLLABICS YWAA | 153a CANADIAN SYLLABICS WEST-CREE YWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 153d CANADIAN SYLLABICS NASKAPI YWAA | 153c CANADIAN SYLLABICS WEST-CREE YWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 153f CANADIAN SYLLABICS BIBLE-CREE Y | 153e CANADIAN SYLLABICS Y */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1541 CANADIAN SYLLABICS SAYISI YI | 1540 CANADIAN SYLLABICS WEST-CREE Y */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1543 CANADIAN SYLLABICS R-CREE RE | 1542 CANADIAN SYLLABICS RE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1545 CANADIAN SYLLABICS RAAI | 1544 CANADIAN SYLLABICS WEST-CREE LE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1547 CANADIAN SYLLABICS RII | 1546 CANADIAN SYLLABICS RI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1549 CANADIAN SYLLABICS ROO | 1548 CANADIAN SYLLABICS RO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 154b CANADIAN SYLLABICS RA | 154a CANADIAN SYLLABICS WEST-CREE LO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 154d CANADIAN SYLLABICS WEST-CREE LA | 154c CANADIAN SYLLABICS RAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 154f CANADIAN SYLLABICS WEST-CREE RWAA | 154e CANADIAN SYLLABICS RWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1551 CANADIAN SYLLABICS WEST-CREE R | 1550 CANADIAN SYLLABICS R */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1553 CANADIAN SYLLABICS FE | 1552 CANADIAN SYLLABICS MEDIAL R */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1555 CANADIAN SYLLABICS FI | 1554 CANADIAN SYLLABICS FAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1557 CANADIAN SYLLABICS FO | 1556 CANADIAN SYLLABICS FII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1559 CANADIAN SYLLABICS FA | 1558 CANADIAN SYLLABICS FOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 155b CANADIAN SYLLABICS FWAA | 155a CANADIAN SYLLABICS FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 155d CANADIAN SYLLABICS F | 155c CANADIAN SYLLABICS WEST-CREE FWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 155f CANADIAN SYLLABICS N-CREE THE | 155e CANADIAN SYLLABICS THE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1561 CANADIAN SYLLABICS N-CREE THI | 1560 CANADIAN SYLLABICS THI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1563 CANADIAN SYLLABICS N-CREE THII | 1562 CANADIAN SYLLABICS THII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1565 CANADIAN SYLLABICS THOO | 1564 CANADIAN SYLLABICS THO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1567 CANADIAN SYLLABICS THAA | 1566 CANADIAN SYLLABICS THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1569 CANADIAN SYLLABICS WEST-CREE THWAA | 1568 CANADIAN SYLLABICS THWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 156b CANADIAN SYLLABICS TTHE | 156a CANADIAN SYLLABICS TH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 156d CANADIAN SYLLABICS TTHO | 156c CANADIAN SYLLABICS TTHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 156f CANADIAN SYLLABICS TTH | 156e CANADIAN SYLLABICS TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1571 CANADIAN SYLLABICS TYI | 1570 CANADIAN SYLLABICS TYE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1573 CANADIAN SYLLABICS TYA | 1572 CANADIAN SYLLABICS TYO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1575 CANADIAN SYLLABICS NUNAVIK HI | 1574 CANADIAN SYLLABICS NUNAVIK HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1577 CANADIAN SYLLABICS NUNAVIK HO | 1576 CANADIAN SYLLABICS NUNAVIK HII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1579 CANADIAN SYLLABICS NUNAVIK HA | 1578 CANADIAN SYLLABICS NUNAVIK HOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 157b CANADIAN SYLLABICS NUNAVIK H | 157a CANADIAN SYLLABICS NUNAVIK HAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 157d CANADIAN SYLLABICS HK | 157c CANADIAN SYLLABICS NUNAVUT H */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 157f CANADIAN SYLLABICS QI | 157e CANADIAN SYLLABICS QAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1581 CANADIAN SYLLABICS QO | 1580 CANADIAN SYLLABICS QII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1583 CANADIAN SYLLABICS QA | 1582 CANADIAN SYLLABICS QOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1585 CANADIAN SYLLABICS Q | 1584 CANADIAN SYLLABICS QAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1587 CANADIAN SYLLABICS TLHI | 1586 CANADIAN SYLLABICS TLHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1589 CANADIAN SYLLABICS TLHA | 1588 CANADIAN SYLLABICS TLHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 158b CANADIAN SYLLABICS WEST-CREE RI | 158a CANADIAN SYLLABICS WEST-CREE RE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 158d CANADIAN SYLLABICS WEST-CREE RA | 158c CANADIAN SYLLABICS WEST-CREE RO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 158f CANADIAN SYLLABICS NGI | 158e CANADIAN SYLLABICS NGAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1591 CANADIAN SYLLABICS NGO | 1590 CANADIAN SYLLABICS NGII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1593 CANADIAN SYLLABICS NGA | 1592 CANADIAN SYLLABICS NGOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1595 CANADIAN SYLLABICS NG | 1594 CANADIAN SYLLABICS NGAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1597 CANADIAN SYLLABICS SAYISI SHE | 1596 CANADIAN SYLLABICS NNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1599 CANADIAN SYLLABICS SAYISI SHO | 1598 CANADIAN SYLLABICS SAYISI SHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 159b CANADIAN SYLLABICS WOODS-CREE THE | 159a CANADIAN SYLLABICS SAYISI SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 159d CANADIAN SYLLABICS WOODS-CREE THO | 159c CANADIAN SYLLABICS WOODS-CREE THI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 159f CANADIAN SYLLABICS WOODS-CREE TH | 159e CANADIAN SYLLABICS WOODS-CREE THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15a1 CANADIAN SYLLABICS LHII | 15a0 CANADIAN SYLLABICS LHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15a3 CANADIAN SYLLABICS LHOO | 15a2 CANADIAN SYLLABICS LHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15a5 CANADIAN SYLLABICS LHAA | 15a4 CANADIAN SYLLABICS LHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15a7 CANADIAN SYLLABICS TH-CREE THE | 15a6 CANADIAN SYLLABICS LH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15a9 CANADIAN SYLLABICS TH-CREE THII | 15a8 CANADIAN SYLLABICS TH-CREE THI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15ab CANADIAN SYLLABICS TH-CREE THOO | 15aa CANADIAN SYLLABICS TH-CREE THO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15ad CANADIAN SYLLABICS TH-CREE THAA | 15ac CANADIAN SYLLABICS TH-CREE THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15af CANADIAN SYLLABICS AIVILIK B | 15ae CANADIAN SYLLABICS TH-CREE TH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15b1 CANADIAN SYLLABICS BLACKFOOT I | 15b0 CANADIAN SYLLABICS BLACKFOOT E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15b3 CANADIAN SYLLABICS BLACKFOOT A | 15b2 CANADIAN SYLLABICS BLACKFOOT O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15b5 CANADIAN SYLLABICS BLACKFOOT WI | 15b4 CANADIAN SYLLABICS BLACKFOOT WE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15b7 CANADIAN SYLLABICS BLACKFOOT WA | 15b6 CANADIAN SYLLABICS BLACKFOOT WO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15b9 CANADIAN SYLLABICS BLACKFOOT NI | 15b8 CANADIAN SYLLABICS BLACKFOOT NE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15bb CANADIAN SYLLABICS BLACKFOOT NA | 15ba CANADIAN SYLLABICS BLACKFOOT NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15bd CANADIAN SYLLABICS BLACKFOOT KI | 15bc CANADIAN SYLLABICS BLACKFOOT KE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15bf CANADIAN SYLLABICS BLACKFOOT KA | 15be CANADIAN SYLLABICS BLACKFOOT KO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15c1 CANADIAN SYLLABICS SAYISI HI | 15c0 CANADIAN SYLLABICS SAYISI HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15c3 CANADIAN SYLLABICS SAYISI HA | 15c2 CANADIAN SYLLABICS SAYISI HO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15c5 CANADIAN SYLLABICS CARRIER GHO | 15c4 CANADIAN SYLLABICS CARRIER GHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15c7 CANADIAN SYLLABICS CARRIER GHEE | 15c6 CANADIAN SYLLABICS CARRIER GHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15c9 CANADIAN SYLLABICS CARRIER GHA | 15c8 CANADIAN SYLLABICS CARRIER GHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15cb CANADIAN SYLLABICS CARRIER RO | 15ca CANADIAN SYLLABICS CARRIER RU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15cd CANADIAN SYLLABICS CARRIER REE | 15cc CANADIAN SYLLABICS CARRIER RE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15cf CANADIAN SYLLABICS CARRIER RA | 15ce CANADIAN SYLLABICS CARRIER RI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15d1 CANADIAN SYLLABICS CARRIER WO | 15d0 CANADIAN SYLLABICS CARRIER WU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15d3 CANADIAN SYLLABICS CARRIER WEE | 15d2 CANADIAN SYLLABICS CARRIER WE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15d5 CANADIAN SYLLABICS CARRIER WA | 15d4 CANADIAN SYLLABICS CARRIER WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15d7 CANADIAN SYLLABICS CARRIER HWO | 15d6 CANADIAN SYLLABICS CARRIER HWU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15d9 CANADIAN SYLLABICS CARRIER HWEE | 15d8 CANADIAN SYLLABICS CARRIER HWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15db CANADIAN SYLLABICS CARRIER HWA | 15da CANADIAN SYLLABICS CARRIER HWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15dd CANADIAN SYLLABICS CARRIER THO | 15dc CANADIAN SYLLABICS CARRIER THU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15df CANADIAN SYLLABICS CARRIER THEE | 15de CANADIAN SYLLABICS CARRIER THE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15e1 CANADIAN SYLLABICS CARRIER THA | 15e0 CANADIAN SYLLABICS CARRIER THI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15e3 CANADIAN SYLLABICS CARRIER TTO | 15e2 CANADIAN SYLLABICS CARRIER TTU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15e5 CANADIAN SYLLABICS CARRIER TTEE | 15e4 CANADIAN SYLLABICS CARRIER TTE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15e7 CANADIAN SYLLABICS CARRIER TTA | 15e6 CANADIAN SYLLABICS CARRIER TTI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15e9 CANADIAN SYLLABICS CARRIER PO | 15e8 CANADIAN SYLLABICS CARRIER PU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15eb CANADIAN SYLLABICS CARRIER PEE | 15ea CANADIAN SYLLABICS CARRIER PE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15ed CANADIAN SYLLABICS CARRIER PA | 15ec CANADIAN SYLLABICS CARRIER PI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15ef CANADIAN SYLLABICS CARRIER GU | 15ee CANADIAN SYLLABICS CARRIER P */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15f1 CANADIAN SYLLABICS CARRIER GE | 15f0 CANADIAN SYLLABICS CARRIER GO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15f3 CANADIAN SYLLABICS CARRIER GI | 15f2 CANADIAN SYLLABICS CARRIER GEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15f5 CANADIAN SYLLABICS CARRIER KHU | 15f4 CANADIAN SYLLABICS CARRIER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15f7 CANADIAN SYLLABICS CARRIER KHE | 15f6 CANADIAN SYLLABICS CARRIER KHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15f9 CANADIAN SYLLABICS CARRIER KHI | 15f8 CANADIAN SYLLABICS CARRIER KHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15fb CANADIAN SYLLABICS CARRIER KKU | 15fa CANADIAN SYLLABICS CARRIER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15fd CANADIAN SYLLABICS CARRIER KKE | 15fc CANADIAN SYLLABICS CARRIER KKO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15ff CANADIAN SYLLABICS CARRIER KKI | 15fe CANADIAN SYLLABICS CARRIER KKEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1601 CANADIAN SYLLABICS CARRIER KK | 1600 CANADIAN SYLLABICS CARRIER KKA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1603 CANADIAN SYLLABICS CARRIER NO | 1602 CANADIAN SYLLABICS CARRIER NU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1605 CANADIAN SYLLABICS CARRIER NEE | 1604 CANADIAN SYLLABICS CARRIER NE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1607 CANADIAN SYLLABICS CARRIER NA | 1606 CANADIAN SYLLABICS CARRIER NI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1609 CANADIAN SYLLABICS CARRIER MO | 1608 CANADIAN SYLLABICS CARRIER MU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 160b CANADIAN SYLLABICS CARRIER MEE | 160a CANADIAN SYLLABICS CARRIER ME */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 160d CANADIAN SYLLABICS CARRIER MA | 160c CANADIAN SYLLABICS CARRIER MI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 160f CANADIAN SYLLABICS CARRIER YO | 160e CANADIAN SYLLABICS CARRIER YU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1611 CANADIAN SYLLABICS CARRIER YEE | 1610 CANADIAN SYLLABICS CARRIER YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1613 CANADIAN SYLLABICS CARRIER YA | 1612 CANADIAN SYLLABICS CARRIER YI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1615 CANADIAN SYLLABICS SAYISI JU | 1614 CANADIAN SYLLABICS CARRIER JU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1617 CANADIAN SYLLABICS CARRIER JE | 1616 CANADIAN SYLLABICS CARRIER JO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1619 CANADIAN SYLLABICS CARRIER JI | 1618 CANADIAN SYLLABICS CARRIER JEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 161b CANADIAN SYLLABICS CARRIER JA | 161a CANADIAN SYLLABICS SAYISI JI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 161d CANADIAN SYLLABICS CARRIER JJO | 161c CANADIAN SYLLABICS CARRIER JJU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 161f CANADIAN SYLLABICS CARRIER JJEE | 161e CANADIAN SYLLABICS CARRIER JJE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1621 CANADIAN SYLLABICS CARRIER JJA | 1620 CANADIAN SYLLABICS CARRIER JJI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1623 CANADIAN SYLLABICS CARRIER LO | 1622 CANADIAN SYLLABICS CARRIER LU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1625 CANADIAN SYLLABICS CARRIER LEE | 1624 CANADIAN SYLLABICS CARRIER LE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1627 CANADIAN SYLLABICS CARRIER LA | 1626 CANADIAN SYLLABICS CARRIER LI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1629 CANADIAN SYLLABICS CARRIER DLO | 1628 CANADIAN SYLLABICS CARRIER DLU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 162b CANADIAN SYLLABICS CARRIER DLEE | 162a CANADIAN SYLLABICS CARRIER DLE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 162d CANADIAN SYLLABICS CARRIER DLA | 162c CANADIAN SYLLABICS CARRIER DLI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 162f CANADIAN SYLLABICS CARRIER LHO | 162e CANADIAN SYLLABICS CARRIER LHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1631 CANADIAN SYLLABICS CARRIER LHEE | 1630 CANADIAN SYLLABICS CARRIER LHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1633 CANADIAN SYLLABICS CARRIER LHA | 1632 CANADIAN SYLLABICS CARRIER LHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1635 CANADIAN SYLLABICS CARRIER TLHO | 1634 CANADIAN SYLLABICS CARRIER TLHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1637 CANADIAN SYLLABICS CARRIER TLHEE | 1636 CANADIAN SYLLABICS CARRIER TLHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1639 CANADIAN SYLLABICS CARRIER TLHA | 1638 CANADIAN SYLLABICS CARRIER TLHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 163b CANADIAN SYLLABICS CARRIER TLO | 163a CANADIAN SYLLABICS CARRIER TLU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 163d CANADIAN SYLLABICS CARRIER TLEE | 163c CANADIAN SYLLABICS CARRIER TLE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 163f CANADIAN SYLLABICS CARRIER TLA | 163e CANADIAN SYLLABICS CARRIER TLI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1641 CANADIAN SYLLABICS CARRIER ZO | 1640 CANADIAN SYLLABICS CARRIER ZU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1643 CANADIAN SYLLABICS CARRIER ZEE | 1642 CANADIAN SYLLABICS CARRIER ZE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1645 CANADIAN SYLLABICS CARRIER ZA | 1644 CANADIAN SYLLABICS CARRIER ZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1647 CANADIAN SYLLABICS CARRIER INITIAL Z | 1646 CANADIAN SYLLABICS CARRIER Z */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1649 CANADIAN SYLLABICS CARRIER DZO | 1648 CANADIAN SYLLABICS CARRIER DZU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 164b CANADIAN SYLLABICS CARRIER DZEE | 164a CANADIAN SYLLABICS CARRIER DZE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 164d CANADIAN SYLLABICS CARRIER DZA | 164c CANADIAN SYLLABICS CARRIER DZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 164f CANADIAN SYLLABICS CARRIER SO | 164e CANADIAN SYLLABICS CARRIER SU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1651 CANADIAN SYLLABICS CARRIER SEE | 1650 CANADIAN SYLLABICS CARRIER SE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1653 CANADIAN SYLLABICS CARRIER SA | 1652 CANADIAN SYLLABICS CARRIER SI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1655 CANADIAN SYLLABICS CARRIER SHO | 1654 CANADIAN SYLLABICS CARRIER SHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1657 CANADIAN SYLLABICS CARRIER SHEE | 1656 CANADIAN SYLLABICS CARRIER SHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1659 CANADIAN SYLLABICS CARRIER SHA | 1658 CANADIAN SYLLABICS CARRIER SHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 165b CANADIAN SYLLABICS CARRIER TSU | 165a CANADIAN SYLLABICS CARRIER SH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 165d CANADIAN SYLLABICS CARRIER TSE | 165c CANADIAN SYLLABICS CARRIER TSO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 165f CANADIAN SYLLABICS CARRIER TSI | 165e CANADIAN SYLLABICS CARRIER TSEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1661 CANADIAN SYLLABICS CARRIER CHU | 1660 CANADIAN SYLLABICS CARRIER TSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1663 CANADIAN SYLLABICS CARRIER CHE | 1662 CANADIAN SYLLABICS CARRIER CHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1665 CANADIAN SYLLABICS CARRIER CHI | 1664 CANADIAN SYLLABICS CARRIER CHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1667 CANADIAN SYLLABICS CARRIER TTSU | 1666 CANADIAN SYLLABICS CARRIER CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1669 CANADIAN SYLLABICS CARRIER TTSE | 1668 CANADIAN SYLLABICS CARRIER TTSO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 166b CANADIAN SYLLABICS CARRIER TTSI | 166a CANADIAN SYLLABICS CARRIER TTSEE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 166d CANADIAN SYLLABICS CHI SIGN | 166c CANADIAN SYLLABICS CARRIER TTSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_PUNCT, /* 166f CANADIAN SYLLABICS QAI | 166e CANADIAN SYLLABICS FULL STOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1671 CANADIAN SYLLABICS NNGI | 1670 CANADIAN SYLLABICS NGAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1673 CANADIAN SYLLABICS NNGO | 1672 CANADIAN SYLLABICS NNGII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1675 CANADIAN SYLLABICS NNGA | 1674 CANADIAN SYLLABICS NNGOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1677 CANADIAN SYLLABICS WOODS-CREE THWEE | 1676 CANADIAN SYLLABICS NNGAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1679 CANADIAN SYLLABICS WOODS-CREE THWII | 1678 CANADIAN SYLLABICS WOODS-CREE THWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 167b CANADIAN SYLLABICS WOODS-CREE THWOO | 167a CANADIAN SYLLABICS WOODS-CREE THWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 167d CANADIAN SYLLABICS WOODS-CREE THWAA | 167c CANADIAN SYLLABICS WOODS-CREE THWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 167f CANADIAN SYLLABICS BLACKFOOT W | 167e CANADIAN SYLLABICS WOODS-CREE FINAL TH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_SPACE, /* 1681 OGHAM LETTER BEITH | 1680 OGHAM SPACE MARK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1683 OGHAM LETTER FEARN | 1682 OGHAM LETTER LUIS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1685 OGHAM LETTER NION | 1684 OGHAM LETTER SAIL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1687 OGHAM LETTER DAIR | 1686 OGHAM LETTER UATH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1689 OGHAM LETTER COLL | 1688 OGHAM LETTER TINNE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 168b OGHAM LETTER MUIN | 168a OGHAM LETTER CEIRT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 168d OGHAM LETTER NGEADAL | 168c OGHAM LETTER GORT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 168f OGHAM LETTER RUIS | 168e OGHAM LETTER STRAIF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1691 OGHAM LETTER ONN | 1690 OGHAM LETTER AILM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1693 OGHAM LETTER EADHADH | 1692 OGHAM LETTER UR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1695 OGHAM LETTER EABHADH | 1694 OGHAM LETTER IODHADH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1697 OGHAM LETTER UILLEANN | 1696 OGHAM LETTER OR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1699 OGHAM LETTER EAMHANCHOLL | 1698 OGHAM LETTER IFIN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 169b OGHAM FEATHER MARK | 169a OGHAM LETTER PEITH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 169d (null) | 169c OGHAM REVERSED FEATHER MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 169f (null) | 169e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16a1 RUNIC LETTER V | 16a0 RUNIC LETTER FEHU FEOH FE F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16a3 RUNIC LETTER YR | 16a2 RUNIC LETTER URUZ UR U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16a5 RUNIC LETTER W | 16a4 RUNIC LETTER Y */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16a7 RUNIC LETTER ETH | 16a6 RUNIC LETTER THURISAZ THURS THORN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16a9 RUNIC LETTER OS O | 16a8 RUNIC LETTER ANSUZ A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16ab RUNIC LETTER AESC | 16aa RUNIC LETTER AC A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16ad RUNIC LETTER SHORT-TWIG-OSS O | 16ac RUNIC LETTER LONG-BRANCH-OSS O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16af RUNIC LETTER OE | 16ae RUNIC LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16b1 RUNIC LETTER RAIDO RAD REID R | 16b0 RUNIC LETTER ON */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16b3 RUNIC LETTER CEN | 16b2 RUNIC LETTER KAUNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16b5 RUNIC LETTER G | 16b4 RUNIC LETTER KAUN K */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16b7 RUNIC LETTER GEBO GYFU G | 16b6 RUNIC LETTER ENG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16b9 RUNIC LETTER WUNJO WYNN W | 16b8 RUNIC LETTER GAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16bb RUNIC LETTER HAEGL H | 16ba RUNIC LETTER HAGLAZ H */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16bd RUNIC LETTER SHORT-TWIG-HAGALL H | 16bc RUNIC LETTER LONG-BRANCH-HAGALL H */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16bf RUNIC LETTER SHORT-TWIG-NAUD N | 16be RUNIC LETTER NAUDIZ NYD NAUD N */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16c1 RUNIC LETTER ISAZ IS ISS I | 16c0 RUNIC LETTER DOTTED-N */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16c3 RUNIC LETTER JERAN J | 16c2 RUNIC LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16c5 RUNIC LETTER LONG-BRANCH-AR AE | 16c4 RUNIC LETTER GER */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16c7 RUNIC LETTER IWAZ EOH | 16c6 RUNIC LETTER SHORT-TWIG-AR A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16c9 RUNIC LETTER ALGIZ EOLHX | 16c8 RUNIC LETTER PERTHO PEORTH P */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16cb RUNIC LETTER SIGEL LONG-BRANCH-SOL S | 16ca RUNIC LETTER SOWILO S */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16cd RUNIC LETTER C | 16cc RUNIC LETTER SHORT-TWIG-SOL S */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16cf RUNIC LETTER TIWAZ TIR TYR T | 16ce RUNIC LETTER Z */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16d1 RUNIC LETTER D | 16d0 RUNIC LETTER SHORT-TWIG-TYR T */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16d3 RUNIC LETTER SHORT-TWIG-BJARKAN B | 16d2 RUNIC LETTER BERKANAN BEORC BJARKAN B */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16d5 RUNIC LETTER OPEN-P | 16d4 RUNIC LETTER DOTTED-P */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16d7 RUNIC LETTER MANNAZ MAN M | 16d6 RUNIC LETTER EHWAZ EH E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16d9 RUNIC LETTER SHORT-TWIG-MADR M | 16d8 RUNIC LETTER LONG-BRANCH-MADR M */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16db RUNIC LETTER DOTTED-L | 16da RUNIC LETTER LAUKAZ LAGU LOGR L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16dd RUNIC LETTER ING | 16dc RUNIC LETTER INGWAZ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16df RUNIC LETTER OTHALAN ETHEL O | 16de RUNIC LETTER DAGAZ DAEG D */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16e1 RUNIC LETTER IOR | 16e0 RUNIC LETTER EAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16e3 RUNIC LETTER CALC | 16e2 RUNIC LETTER CWEORTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16e5 RUNIC LETTER STAN | 16e4 RUNIC LETTER CEALC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16e7 RUNIC LETTER SHORT-TWIG-YR | 16e6 RUNIC LETTER LONG-BRANCH-YR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16e9 RUNIC LETTER Q | 16e8 RUNIC LETTER ICELANDIC-YR */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 16eb RUNIC SINGLE PUNCTUATION | 16ea RUNIC LETTER X */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 16ed RUNIC CROSS PUNCTUATION | 16ec RUNIC MULTIPLE PUNCTUATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 16ef RUNIC TVIMADUR SYMBOL | 16ee RUNIC ARLAUG SYMBOL */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 16f1 (null) | 16f0 RUNIC BELGTHOR SYMBOL */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 16f3 (null) | 16f2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 16f5 (null) | 16f4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 16f7 (null) | 16f6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 16f9 (null) | 16f8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 16fb (null) | 16fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 16fd (null) | 16fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 16ff (null) | 16fe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1701 TAGALOG LETTER I | 1700 TAGALOG LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1703 TAGALOG LETTER KA | 1702 TAGALOG LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1705 TAGALOG LETTER NGA | 1704 TAGALOG LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1707 TAGALOG LETTER DA | 1706 TAGALOG LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1709 TAGALOG LETTER PA | 1708 TAGALOG LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 170b TAGALOG LETTER MA | 170a TAGALOG LETTER BA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 170d (null) | 170c TAGALOG LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 170f TAGALOG LETTER WA | 170e TAGALOG LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1711 TAGALOG LETTER HA | 1710 TAGALOG LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1713 TAGALOG VOWEL SIGN U | 1712 TAGALOG VOWEL SIGN I */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 1715 (null) | 1714 TAGALOG SIGN VIRAMA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1717 (null) | 1716 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1719 (null) | 1718 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 171b (null) | 171a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 171d (null) | 171c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 171f (null) | 171e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1721 HANUNOO LETTER I | 1720 HANUNOO LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1723 HANUNOO LETTER KA | 1722 HANUNOO LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1725 HANUNOO LETTER NGA | 1724 HANUNOO LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1727 HANUNOO LETTER DA | 1726 HANUNOO LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1729 HANUNOO LETTER PA | 1728 HANUNOO LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 172b HANUNOO LETTER MA | 172a HANUNOO LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 172d HANUNOO LETTER RA | 172c HANUNOO LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 172f HANUNOO LETTER WA | 172e HANUNOO LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1731 HANUNOO LETTER HA | 1730 HANUNOO LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1733 HANUNOO VOWEL SIGN U | 1732 HANUNOO VOWEL SIGN I */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 1735 PHILIPPINE SINGLE PUNCTUATION | 1734 HANUNOO SIGN PAMUDPOD */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 1737 (null) | 1736 PHILIPPINE DOUBLE PUNCTUATION */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1739 (null) | 1738 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 173b (null) | 173a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 173d (null) | 173c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 173f (null) | 173e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1741 BUHID LETTER I | 1740 BUHID LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1743 BUHID LETTER KA | 1742 BUHID LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1745 BUHID LETTER NGA | 1744 BUHID LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1747 BUHID LETTER DA | 1746 BUHID LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1749 BUHID LETTER PA | 1748 BUHID LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 174b BUHID LETTER MA | 174a BUHID LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 174d BUHID LETTER RA | 174c BUHID LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 174f BUHID LETTER WA | 174e BUHID LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1751 BUHID LETTER HA | 1750 BUHID LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1753 BUHID VOWEL SIGN U | 1752 BUHID VOWEL SIGN I */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1755 (null) | 1754 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1757 (null) | 1756 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1759 (null) | 1758 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 175b (null) | 175a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 175d (null) | 175c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 175f (null) | 175e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1761 TAGBANWA LETTER I | 1760 TAGBANWA LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1763 TAGBANWA LETTER KA | 1762 TAGBANWA LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1765 TAGBANWA LETTER NGA | 1764 TAGBANWA LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1767 TAGBANWA LETTER DA | 1766 TAGBANWA LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1769 TAGBANWA LETTER PA | 1768 TAGBANWA LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 176b TAGBANWA LETTER MA | 176a TAGBANWA LETTER BA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 176d (null) | 176c TAGBANWA LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 176f TAGBANWA LETTER WA | 176e TAGBANWA LETTER LA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 1771 (null) | 1770 TAGBANWA LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1773 TAGBANWA VOWEL SIGN U | 1772 TAGBANWA VOWEL SIGN I */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1775 (null) | 1774 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1777 (null) | 1776 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1779 (null) | 1778 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 177b (null) | 177a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 177d (null) | 177c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 177f (null) | 177e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1781 KHMER LETTER KHA | 1780 KHMER LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1783 KHMER LETTER KHO | 1782 KHMER LETTER KO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1785 KHMER LETTER CA | 1784 KHMER LETTER NGO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1787 KHMER LETTER CO | 1786 KHMER LETTER CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1789 KHMER LETTER NYO | 1788 KHMER LETTER CHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 178b KHMER LETTER TTHA | 178a KHMER LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 178d KHMER LETTER TTHO | 178c KHMER LETTER DO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 178f KHMER LETTER TA | 178e KHMER LETTER NNO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1791 KHMER LETTER TO | 1790 KHMER LETTER THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1793 KHMER LETTER NO | 1792 KHMER LETTER THO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1795 KHMER LETTER PHA | 1794 KHMER LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1797 KHMER LETTER PHO | 1796 KHMER LETTER PO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1799 KHMER LETTER YO | 1798 KHMER LETTER MO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 179b KHMER LETTER LO | 179a KHMER LETTER RO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 179d KHMER LETTER SHA | 179c KHMER LETTER VO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 179f KHMER LETTER SA | 179e KHMER LETTER SSO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17a1 KHMER LETTER LA | 17a0 KHMER LETTER HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17a3 KHMER INDEPENDENT VOWEL QAQ | 17a2 KHMER LETTER QA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17a5 KHMER INDEPENDENT VOWEL QI | 17a4 KHMER INDEPENDENT VOWEL QAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17a7 KHMER INDEPENDENT VOWEL QU | 17a6 KHMER INDEPENDENT VOWEL QII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17a9 KHMER INDEPENDENT VOWEL QUU | 17a8 KHMER INDEPENDENT VOWEL QUK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17ab KHMER INDEPENDENT VOWEL RY | 17aa KHMER INDEPENDENT VOWEL QUUV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17ad KHMER INDEPENDENT VOWEL LY | 17ac KHMER INDEPENDENT VOWEL RYY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17af KHMER INDEPENDENT VOWEL QE | 17ae KHMER INDEPENDENT VOWEL LYY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17b1 KHMER INDEPENDENT VOWEL QOO TYPE ONE | 17b0 KHMER INDEPENDENT VOWEL QAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17b3 KHMER INDEPENDENT VOWEL QAU | 17b2 KHMER INDEPENDENT VOWEL QOO TYPE TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17b5 KHMER VOWEL INHERENT AA | 17b4 KHMER VOWEL INHERENT AQ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17b7 KHMER VOWEL SIGN I | 17b6 KHMER VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17b9 KHMER VOWEL SIGN Y | 17b8 KHMER VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17bb KHMER VOWEL SIGN U | 17ba KHMER VOWEL SIGN YY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17bd KHMER VOWEL SIGN UA | 17bc KHMER VOWEL SIGN UU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17bf KHMER VOWEL SIGN YA | 17be KHMER VOWEL SIGN OE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17c1 KHMER VOWEL SIGN E | 17c0 KHMER VOWEL SIGN IE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17c3 KHMER VOWEL SIGN AI | 17c2 KHMER VOWEL SIGN AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17c5 KHMER VOWEL SIGN AU | 17c4 KHMER VOWEL SIGN OO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17c7 KHMER SIGN REAHMUK | 17c6 KHMER SIGN NIKAHIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17c9 KHMER SIGN MUUSIKATOAN | 17c8 KHMER SIGN YUUKALEAPINTU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17cb KHMER SIGN BANTOC | 17ca KHMER SIGN TRIISAP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17cd KHMER SIGN TOANDAKHIAT | 17cc KHMER SIGN ROBAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17cf KHMER SIGN AHSDA | 17ce KHMER SIGN KAKABAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17d1 KHMER SIGN VIRIAM | 17d0 KHMER SIGN SAMYOK SANNYA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17d3 KHMER SIGN BATHAMASAT | 17d2 KHMER SIGN COENG */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 17d5 KHMER SIGN BARIYOOSAN | 17d4 KHMER SIGN KHAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 17d7 KHMER SIGN LEK TOO | 17d6 KHMER SIGN CAMNUC PII KUUH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 17d9 KHMER SIGN PHNAEK MUAN | 17d8 KHMER SIGN BEYYAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 17db KHMER CURRENCY SYMBOL RIEL | 17da KHMER SIGN KOOMUUT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 17dd KHMER SIGN ATTHACAN | 17dc KHMER SIGN AVAKRAHASANYA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 17df (null) | 17de (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 17e1 KHMER DIGIT ONE | 17e0 KHMER DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 17e3 KHMER DIGIT THREE | 17e2 KHMER DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 17e5 KHMER DIGIT FIVE | 17e4 KHMER DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 17e7 KHMER DIGIT SEVEN | 17e6 KHMER DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 17e9 KHMER DIGIT NINE | 17e8 KHMER DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 17eb (null) | 17ea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 17ed (null) | 17ec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 17ef (null) | 17ee (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17f1 KHMER SYMBOL LEK ATTAK MUOY | 17f0 KHMER SYMBOL LEK ATTAK SON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17f3 KHMER SYMBOL LEK ATTAK BEI | 17f2 KHMER SYMBOL LEK ATTAK PII */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17f5 KHMER SYMBOL LEK ATTAK PRAM | 17f4 KHMER SYMBOL LEK ATTAK BUON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17f7 KHMER SYMBOL LEK ATTAK PRAM-PII | 17f6 KHMER SYMBOL LEK ATTAK PRAM-MUOY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17f9 KHMER SYMBOL LEK ATTAK PRAM-BUON | 17f8 KHMER SYMBOL LEK ATTAK PRAM-BEI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 17fb (null) | 17fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 17fd (null) | 17fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 17ff (null) | 17fe (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1801 MONGOLIAN ELLIPSIS | 1800 MONGOLIAN BIRGA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1803 MONGOLIAN FULL STOP | 1802 MONGOLIAN COMMA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1805 MONGOLIAN FOUR DOTS | 1804 MONGOLIAN COLON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1807 MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER | 1806 MONGOLIAN TODO SOFT HYPHEN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1809 MONGOLIAN MANCHU FULL STOP | 1808 MONGOLIAN MANCHU COMMA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 180b MONGOLIAN FREE VARIATION SELECTOR ONE | 180a MONGOLIAN NIRUGU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 180d MONGOLIAN FREE VARIATION SELECTOR THREE | 180c MONGOLIAN FREE VARIATION SELECTOR TWO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_SPACE, /* 180f (null) | 180e MONGOLIAN VOWEL SEPARATOR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1811 MONGOLIAN DIGIT ONE | 1810 MONGOLIAN DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1813 MONGOLIAN DIGIT THREE | 1812 MONGOLIAN DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1815 MONGOLIAN DIGIT FIVE | 1814 MONGOLIAN DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1817 MONGOLIAN DIGIT SEVEN | 1816 MONGOLIAN DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1819 MONGOLIAN DIGIT NINE | 1818 MONGOLIAN DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 181b (null) | 181a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 181d (null) | 181c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 181f (null) | 181e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1821 MONGOLIAN LETTER E | 1820 MONGOLIAN LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1823 MONGOLIAN LETTER O | 1822 MONGOLIAN LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1825 MONGOLIAN LETTER OE | 1824 MONGOLIAN LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1827 MONGOLIAN LETTER EE | 1826 MONGOLIAN LETTER UE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1829 MONGOLIAN LETTER ANG | 1828 MONGOLIAN LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 182b MONGOLIAN LETTER PA | 182a MONGOLIAN LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 182d MONGOLIAN LETTER GA | 182c MONGOLIAN LETTER QA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 182f MONGOLIAN LETTER LA | 182e MONGOLIAN LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1831 MONGOLIAN LETTER SHA | 1830 MONGOLIAN LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1833 MONGOLIAN LETTER DA | 1832 MONGOLIAN LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1835 MONGOLIAN LETTER JA | 1834 MONGOLIAN LETTER CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1837 MONGOLIAN LETTER RA | 1836 MONGOLIAN LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1839 MONGOLIAN LETTER FA | 1838 MONGOLIAN LETTER WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 183b MONGOLIAN LETTER KHA | 183a MONGOLIAN LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 183d MONGOLIAN LETTER ZA | 183c MONGOLIAN LETTER TSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 183f MONGOLIAN LETTER ZRA | 183e MONGOLIAN LETTER HAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1841 MONGOLIAN LETTER ZHI | 1840 MONGOLIAN LETTER LHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 1843 MONGOLIAN LETTER TODO LONG VOWEL SIGN | 1842 MONGOLIAN LETTER CHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1845 MONGOLIAN LETTER TODO I | 1844 MONGOLIAN LETTER TODO E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1847 MONGOLIAN LETTER TODO U | 1846 MONGOLIAN LETTER TODO O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1849 MONGOLIAN LETTER TODO UE | 1848 MONGOLIAN LETTER TODO OE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 184b MONGOLIAN LETTER TODO BA | 184a MONGOLIAN LETTER TODO ANG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 184d MONGOLIAN LETTER TODO QA | 184c MONGOLIAN LETTER TODO PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 184f MONGOLIAN LETTER TODO MA | 184e MONGOLIAN LETTER TODO GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1851 MONGOLIAN LETTER TODO DA | 1850 MONGOLIAN LETTER TODO TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1853 MONGOLIAN LETTER TODO JA | 1852 MONGOLIAN LETTER TODO CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1855 MONGOLIAN LETTER TODO YA | 1854 MONGOLIAN LETTER TODO TSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1857 MONGOLIAN LETTER TODO KA | 1856 MONGOLIAN LETTER TODO WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1859 MONGOLIAN LETTER TODO HAA | 1858 MONGOLIAN LETTER TODO GAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 185b MONGOLIAN LETTER TODO NIA | 185a MONGOLIAN LETTER TODO JIA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 185d MONGOLIAN LETTER SIBE E | 185c MONGOLIAN LETTER TODO DZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 185f MONGOLIAN LETTER SIBE IY | 185e MONGOLIAN LETTER SIBE I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1861 MONGOLIAN LETTER SIBE U | 1860 MONGOLIAN LETTER SIBE UE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1863 MONGOLIAN LETTER SIBE KA | 1862 MONGOLIAN LETTER SIBE ANG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1865 MONGOLIAN LETTER SIBE HA | 1864 MONGOLIAN LETTER SIBE GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1867 MONGOLIAN LETTER SIBE SHA | 1866 MONGOLIAN LETTER SIBE PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1869 MONGOLIAN LETTER SIBE DA | 1868 MONGOLIAN LETTER SIBE TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 186b MONGOLIAN LETTER SIBE FA | 186a MONGOLIAN LETTER SIBE JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 186d MONGOLIAN LETTER SIBE HAA | 186c MONGOLIAN LETTER SIBE GAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 186f MONGOLIAN LETTER SIBE ZA | 186e MONGOLIAN LETTER SIBE TSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1871 MONGOLIAN LETTER SIBE CHA | 1870 MONGOLIAN LETTER SIBE RAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1873 MONGOLIAN LETTER MANCHU I | 1872 MONGOLIAN LETTER SIBE ZHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1875 MONGOLIAN LETTER MANCHU RA | 1874 MONGOLIAN LETTER MANCHU KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1877 MONGOLIAN LETTER MANCHU ZHA | 1876 MONGOLIAN LETTER MANCHU FA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1879 (null) | 1878 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 187b (null) | 187a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 187d (null) | 187c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 187f (null) | 187e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1881 MONGOLIAN LETTER ALI GALI VISARGA ONE | 1880 MONGOLIAN LETTER ALI GALI ANUSVARA ONE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1883 MONGOLIAN LETTER ALI GALI UBADAMA | 1882 MONGOLIAN LETTER ALI GALI DAMARU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1885 MONGOLIAN LETTER ALI GALI BALUDA | 1884 MONGOLIAN LETTER ALI GALI INVERTED UBAD */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1887 MONGOLIAN LETTER ALI GALI A | 1886 MONGOLIAN LETTER ALI GALI THREE BALUDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1889 MONGOLIAN LETTER ALI GALI KA | 1888 MONGOLIAN LETTER ALI GALI I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 188b MONGOLIAN LETTER ALI GALI CA | 188a MONGOLIAN LETTER ALI GALI NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 188d MONGOLIAN LETTER ALI GALI TTHA | 188c MONGOLIAN LETTER ALI GALI TTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 188f MONGOLIAN LETTER ALI GALI NNA | 188e MONGOLIAN LETTER ALI GALI DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1891 MONGOLIAN LETTER ALI GALI DA | 1890 MONGOLIAN LETTER ALI GALI TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1893 MONGOLIAN LETTER ALI GALI PHA | 1892 MONGOLIAN LETTER ALI GALI PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1895 MONGOLIAN LETTER ALI GALI ZHA | 1894 MONGOLIAN LETTER ALI GALI SSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1897 MONGOLIAN LETTER ALI GALI AH | 1896 MONGOLIAN LETTER ALI GALI ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1899 MONGOLIAN LETTER TODO ALI GALI ZHA | 1898 MONGOLIAN LETTER TODO ALI GALI TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 189b MONGOLIAN LETTER MANCHU ALI GALI NGA | 189a MONGOLIAN LETTER MANCHU ALI GALI GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 189d MONGOLIAN LETTER MANCHU ALI GALI JHA | 189c MONGOLIAN LETTER MANCHU ALI GALI CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 189f MONGOLIAN LETTER MANCHU ALI GALI DDHA | 189e MONGOLIAN LETTER MANCHU ALI GALI TTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18a1 MONGOLIAN LETTER MANCHU ALI GALI DHA | 18a0 MONGOLIAN LETTER MANCHU ALI GALI TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18a3 MONGOLIAN LETTER MANCHU ALI GALI CYA | 18a2 MONGOLIAN LETTER MANCHU ALI GALI SSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18a5 MONGOLIAN LETTER MANCHU ALI GALI ZA | 18a4 MONGOLIAN LETTER MANCHU ALI GALI ZHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18a7 MONGOLIAN LETTER ALI GALI HALF YA | 18a6 MONGOLIAN LETTER ALI GALI HALF U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 18a9 MONGOLIAN LETTER ALI GALI DAGALGA | 18a8 MONGOLIAN LETTER MANCHU ALI GALI BHA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 18ab (null) | 18aa MONGOLIAN LETTER MANCHU ALI GALI LHA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 18ad (null) | 18ac (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 18af (null) | 18ae (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18b1 CANADIAN SYLLABICS AY | 18b0 CANADIAN SYLLABICS OY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18b3 CANADIAN SYLLABICS WAY | 18b2 CANADIAN SYLLABICS AAY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18b5 CANADIAN SYLLABICS PAY | 18b4 CANADIAN SYLLABICS POY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18b7 CANADIAN SYLLABICS TAY | 18b6 CANADIAN SYLLABICS PWOY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18b9 CANADIAN SYLLABICS KWAY | 18b8 CANADIAN SYLLABICS KAY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18bb CANADIAN SYLLABICS NOY | 18ba CANADIAN SYLLABICS MAY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18bd CANADIAN SYLLABICS LAY | 18bc CANADIAN SYLLABICS NAY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18bf CANADIAN SYLLABICS SAY | 18be CANADIAN SYLLABICS SOY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18c1 CANADIAN SYLLABICS SHAY | 18c0 CANADIAN SYLLABICS SHOY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18c3 CANADIAN SYLLABICS YOY | 18c2 CANADIAN SYLLABICS SHWOY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18c5 CANADIAN SYLLABICS RAY | 18c4 CANADIAN SYLLABICS YAY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18c7 CANADIAN SYLLABICS OJIBWAY NWI | 18c6 CANADIAN SYLLABICS NWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18c9 CANADIAN SYLLABICS OJIBWAY NWII | 18c8 CANADIAN SYLLABICS NWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18cb CANADIAN SYLLABICS OJIBWAY NWO | 18ca CANADIAN SYLLABICS NWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18cd CANADIAN SYLLABICS OJIBWAY NWOO | 18cc CANADIAN SYLLABICS NWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18cf CANADIAN SYLLABICS RWI | 18ce CANADIAN SYLLABICS RWEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18d1 CANADIAN SYLLABICS RWO | 18d0 CANADIAN SYLLABICS RWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18d3 CANADIAN SYLLABICS RWA | 18d2 CANADIAN SYLLABICS RWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18d5 CANADIAN SYLLABICS OJIBWAY T | 18d4 CANADIAN SYLLABICS OJIBWAY P */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18d7 CANADIAN SYLLABICS OJIBWAY C | 18d6 CANADIAN SYLLABICS OJIBWAY K */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18d9 CANADIAN SYLLABICS OJIBWAY N | 18d8 CANADIAN SYLLABICS OJIBWAY M */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18db CANADIAN SYLLABICS OJIBWAY SH | 18da CANADIAN SYLLABICS OJIBWAY S */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18dd CANADIAN SYLLABICS WESTERN W | 18dc CANADIAN SYLLABICS EASTERN W */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18df CANADIAN SYLLABICS FINAL RAISED DOT | 18de CANADIAN SYLLABICS FINAL SMALL RING */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18e1 CANADIAN SYLLABICS WEST-CREE LOO | 18e0 CANADIAN SYLLABICS R-CREE RWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18e3 CANADIAN SYLLABICS THWE | 18e2 CANADIAN SYLLABICS WEST-CREE LAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18e5 CANADIAN SYLLABICS TTHWE | 18e4 CANADIAN SYLLABICS THWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18e7 CANADIAN SYLLABICS TTHAA | 18e6 CANADIAN SYLLABICS TTHOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18e9 CANADIAN SYLLABICS TLHOO | 18e8 CANADIAN SYLLABICS TLHWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18eb CANADIAN SYLLABICS SAYISI SHOO | 18ea CANADIAN SYLLABICS SAYISI SHWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18ed CANADIAN SYLLABICS CARRIER GWU | 18ec CANADIAN SYLLABICS SAYISI HOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18ef CANADIAN SYLLABICS CARRIER GAA | 18ee CANADIAN SYLLABICS CARRIER DENE GEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18f1 CANADIAN SYLLABICS SAYISI JUU | 18f0 CANADIAN SYLLABICS CARRIER GWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18f3 CANADIAN SYLLABICS BEAVER DENE L | 18f2 CANADIAN SYLLABICS CARRIER JWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18f5 CANADIAN SYLLABICS CARRIER DENTAL S | 18f4 CANADIAN SYLLABICS BEAVER DENE R */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 18f7 (null) | 18f6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 18f9 (null) | 18f8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 18fb (null) | 18fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 18fd (null) | 18fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 18ff (null) | 18fe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1901 LIMBU LETTER KA | 1900 LIMBU VOWEL-CARRIER LETTER */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1903 LIMBU LETTER GA | 1902 LIMBU LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1905 LIMBU LETTER NGA | 1904 LIMBU LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1907 LIMBU LETTER CHA | 1906 LIMBU LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1909 LIMBU LETTER JHA | 1908 LIMBU LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 190b LIMBU LETTER TA | 190a LIMBU LETTER YAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 190d LIMBU LETTER DA | 190c LIMBU LETTER THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 190f LIMBU LETTER NA | 190e LIMBU LETTER DHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1911 LIMBU LETTER PHA | 1910 LIMBU LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1913 LIMBU LETTER BHA | 1912 LIMBU LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1915 LIMBU LETTER YA | 1914 LIMBU LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1917 LIMBU LETTER LA | 1916 LIMBU LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1919 LIMBU LETTER SHA | 1918 LIMBU LETTER WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 191b LIMBU LETTER SA | 191a LIMBU LETTER SSA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 191d (null) | 191c LIMBU LETTER HA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 191f (null) | 191e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1921 LIMBU VOWEL SIGN I | 1920 LIMBU VOWEL SIGN A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1923 LIMBU VOWEL SIGN EE | 1922 LIMBU VOWEL SIGN U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1925 LIMBU VOWEL SIGN OO | 1924 LIMBU VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1927 LIMBU VOWEL SIGN E | 1926 LIMBU VOWEL SIGN AU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1929 LIMBU SUBJOINED LETTER YA | 1928 LIMBU VOWEL SIGN O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 192b LIMBU SUBJOINED LETTER WA | 192a LIMBU SUBJOINED LETTER RA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 192d (null) | 192c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 192f (null) | 192e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1931 LIMBU SMALL LETTER NGA | 1930 LIMBU SMALL LETTER KA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1933 LIMBU SMALL LETTER TA | 1932 LIMBU SMALL LETTER ANUSVARA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1935 LIMBU SMALL LETTER PA | 1934 LIMBU SMALL LETTER NA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1937 LIMBU SMALL LETTER RA | 1936 LIMBU SMALL LETTER MA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1939 LIMBU SIGN MUKPHRENG | 1938 LIMBU SMALL LETTER LA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 193b LIMBU SIGN SA-I | 193a LIMBU SIGN KEMPHRENG */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 193d (null) | 193c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 193f (null) | 193e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 1941 (null) | 1940 LIMBU SIGN LOO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1943 (null) | 1942 (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1945 LIMBU QUESTION MARK | 1944 LIMBU EXCLAMATION MARK */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1947 LIMBU DIGIT ONE | 1946 LIMBU DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1949 LIMBU DIGIT THREE | 1948 LIMBU DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 194b LIMBU DIGIT FIVE | 194a LIMBU DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 194d LIMBU DIGIT SEVEN | 194c LIMBU DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 194f LIMBU DIGIT NINE | 194e LIMBU DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1951 TAI LE LETTER XA | 1950 TAI LE LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1953 TAI LE LETTER TSA | 1952 TAI LE LETTER NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1955 TAI LE LETTER YA | 1954 TAI LE LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1957 TAI LE LETTER THA | 1956 TAI LE LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1959 TAI LE LETTER PA | 1958 TAI LE LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 195b TAI LE LETTER MA | 195a TAI LE LETTER PHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 195d TAI LE LETTER VA | 195c TAI LE LETTER FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 195f TAI LE LETTER QA | 195e TAI LE LETTER HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1961 TAI LE LETTER TSHA | 1960 TAI LE LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1963 TAI LE LETTER A | 1962 TAI LE LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1965 TAI LE LETTER EE | 1964 TAI LE LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1967 TAI LE LETTER U | 1966 TAI LE LETTER EH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1969 TAI LE LETTER O | 1968 TAI LE LETTER OO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 196b TAI LE LETTER E | 196a TAI LE LETTER UE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 196d TAI LE LETTER AI | 196c TAI LE LETTER AUE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 196f (null) | 196e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1971 TAI LE LETTER TONE-3 | 1970 TAI LE LETTER TONE-2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1973 TAI LE LETTER TONE-5 | 1972 TAI LE LETTER TONE-4 */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 1975 (null) | 1974 TAI LE LETTER TONE-6 */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1977 (null) | 1976 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1979 (null) | 1978 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 197b (null) | 197a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 197d (null) | 197c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 197f (null) | 197e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1981 NEW TAI LUE LETTER LOW QA | 1980 NEW TAI LUE LETTER HIGH QA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1983 NEW TAI LUE LETTER HIGH XA | 1982 NEW TAI LUE LETTER HIGH KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1985 NEW TAI LUE LETTER LOW KA | 1984 NEW TAI LUE LETTER HIGH NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1987 NEW TAI LUE LETTER LOW NGA | 1986 NEW TAI LUE LETTER LOW XA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1989 NEW TAI LUE LETTER HIGH SA | 1988 NEW TAI LUE LETTER HIGH TSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 198b NEW TAI LUE LETTER LOW TSA | 198a NEW TAI LUE LETTER HIGH YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 198d NEW TAI LUE LETTER LOW YA | 198c NEW TAI LUE LETTER LOW SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 198f NEW TAI LUE LETTER HIGH THA | 198e NEW TAI LUE LETTER HIGH TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1991 NEW TAI LUE LETTER LOW TA | 1990 NEW TAI LUE LETTER HIGH NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1993 NEW TAI LUE LETTER LOW NA | 1992 NEW TAI LUE LETTER LOW THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1995 NEW TAI LUE LETTER HIGH PHA | 1994 NEW TAI LUE LETTER HIGH PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1997 NEW TAI LUE LETTER LOW PA | 1996 NEW TAI LUE LETTER HIGH MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1999 NEW TAI LUE LETTER LOW MA | 1998 NEW TAI LUE LETTER LOW PHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 199b NEW TAI LUE LETTER HIGH VA | 199a NEW TAI LUE LETTER HIGH FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 199d NEW TAI LUE LETTER LOW FA | 199c NEW TAI LUE LETTER HIGH LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 199f NEW TAI LUE LETTER LOW LA | 199e NEW TAI LUE LETTER LOW VA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19a1 NEW TAI LUE LETTER HIGH DA | 19a0 NEW TAI LUE LETTER HIGH HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19a3 NEW TAI LUE LETTER LOW HA | 19a2 NEW TAI LUE LETTER HIGH BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19a5 NEW TAI LUE LETTER LOW BA | 19a4 NEW TAI LUE LETTER LOW DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19a7 NEW TAI LUE LETTER HIGH XVA | 19a6 NEW TAI LUE LETTER HIGH KVA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19a9 NEW TAI LUE LETTER LOW XVA | 19a8 NEW TAI LUE LETTER LOW KVA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19ab NEW TAI LUE LETTER LOW SUA | 19aa NEW TAI LUE LETTER HIGH SUA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 19ad (null) | 19ac (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 19af (null) | 19ae (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19b1 NEW TAI LUE VOWEL SIGN AA | 19b0 NEW TAI LUE VOWEL SIGN VOWEL SHORTENER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19b3 NEW TAI LUE VOWEL SIGN U | 19b2 NEW TAI LUE VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19b5 NEW TAI LUE VOWEL SIGN E | 19b4 NEW TAI LUE VOWEL SIGN UU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19b7 NEW TAI LUE VOWEL SIGN O | 19b6 NEW TAI LUE VOWEL SIGN AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19b9 NEW TAI LUE VOWEL SIGN UE | 19b8 NEW TAI LUE VOWEL SIGN OA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19bb NEW TAI LUE VOWEL SIGN AAY | 19ba NEW TAI LUE VOWEL SIGN AY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19bd NEW TAI LUE VOWEL SIGN OY | 19bc NEW TAI LUE VOWEL SIGN UY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19bf NEW TAI LUE VOWEL SIGN UEY | 19be NEW TAI LUE VOWEL SIGN OAY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 19c1 NEW TAI LUE LETTER FINAL V | 19c0 NEW TAI LUE VOWEL SIGN IY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19c3 NEW TAI LUE LETTER FINAL N | 19c2 NEW TAI LUE LETTER FINAL NG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19c5 NEW TAI LUE LETTER FINAL K | 19c4 NEW TAI LUE LETTER FINAL M */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19c7 NEW TAI LUE LETTER FINAL B | 19c6 NEW TAI LUE LETTER FINAL D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19c9 NEW TAI LUE TONE MARK-2 | 19c8 NEW TAI LUE TONE MARK-1 */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 19cb (null) | 19ca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 19cd (null) | 19cc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 19cf (null) | 19ce (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 19d1 NEW TAI LUE DIGIT ONE | 19d0 NEW TAI LUE DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 19d3 NEW TAI LUE DIGIT THREE | 19d2 NEW TAI LUE DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 19d5 NEW TAI LUE DIGIT FIVE | 19d4 NEW TAI LUE DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 19d7 NEW TAI LUE DIGIT SEVEN | 19d6 NEW TAI LUE DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 19d9 NEW TAI LUE DIGIT NINE | 19d8 NEW TAI LUE DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 19db (null) | 19da NEW TAI LUE THAM DIGIT ONE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 19dd (null) | 19dc (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19df NEW TAI LUE SIGN LAEV | 19de NEW TAI LUE SIGN LAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19e1 KHMER SYMBOL MUOY KOET | 19e0 KHMER SYMBOL PATHAMASAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19e3 KHMER SYMBOL BEI KOET | 19e2 KHMER SYMBOL PII KOET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19e5 KHMER SYMBOL PRAM KOET | 19e4 KHMER SYMBOL BUON KOET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19e7 KHMER SYMBOL PRAM-PII KOET | 19e6 KHMER SYMBOL PRAM-MUOY KOET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19e9 KHMER SYMBOL PRAM-BUON KOET | 19e8 KHMER SYMBOL PRAM-BEI KOET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19eb KHMER SYMBOL DAP-MUOY KOET | 19ea KHMER SYMBOL DAP KOET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19ed KHMER SYMBOL DAP-BEI KOET | 19ec KHMER SYMBOL DAP-PII KOET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19ef KHMER SYMBOL DAP-PRAM KOET | 19ee KHMER SYMBOL DAP-BUON KOET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19f1 KHMER SYMBOL MUOY ROC | 19f0 KHMER SYMBOL TUTEYASAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19f3 KHMER SYMBOL BEI ROC | 19f2 KHMER SYMBOL PII ROC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19f5 KHMER SYMBOL PRAM ROC | 19f4 KHMER SYMBOL BUON ROC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19f7 KHMER SYMBOL PRAM-PII ROC | 19f6 KHMER SYMBOL PRAM-MUOY ROC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19f9 KHMER SYMBOL PRAM-BUON ROC | 19f8 KHMER SYMBOL PRAM-BEI ROC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19fb KHMER SYMBOL DAP-MUOY ROC | 19fa KHMER SYMBOL DAP ROC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19fd KHMER SYMBOL DAP-BEI ROC | 19fc KHMER SYMBOL DAP-PII ROC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19ff KHMER SYMBOL DAP-PRAM ROC | 19fe KHMER SYMBOL DAP-BUON ROC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a01 BUGINESE LETTER GA | 1a00 BUGINESE LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a03 BUGINESE LETTER NGKA | 1a02 BUGINESE LETTER NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a05 BUGINESE LETTER BA | 1a04 BUGINESE LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a07 BUGINESE LETTER MPA | 1a06 BUGINESE LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a09 BUGINESE LETTER DA | 1a08 BUGINESE LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a0b BUGINESE LETTER NRA | 1a0a BUGINESE LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a0d BUGINESE LETTER JA | 1a0c BUGINESE LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a0f BUGINESE LETTER NYCA | 1a0e BUGINESE LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a11 BUGINESE LETTER RA | 1a10 BUGINESE LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a13 BUGINESE LETTER VA | 1a12 BUGINESE LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a15 BUGINESE LETTER A | 1a14 BUGINESE LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 1a17 BUGINESE VOWEL SIGN I | 1a16 BUGINESE LETTER HA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a19 BUGINESE VOWEL SIGN E | 1a18 BUGINESE VOWEL SIGN U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a1b BUGINESE VOWEL SIGN AE | 1a1a BUGINESE VOWEL SIGN O */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1a1d (null) | 1a1c (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1a1f BUGINESE END OF SECTION | 1a1e BUGINESE PALLAWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a21 TAI THAM LETTER HIGH KHA | 1a20 TAI THAM LETTER HIGH KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a23 TAI THAM LETTER LOW KA | 1a22 TAI THAM LETTER HIGH KXA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a25 TAI THAM LETTER LOW KHA | 1a24 TAI THAM LETTER LOW KXA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a27 TAI THAM LETTER HIGH CA | 1a26 TAI THAM LETTER NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a29 TAI THAM LETTER LOW CA | 1a28 TAI THAM LETTER HIGH CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a2b TAI THAM LETTER LOW CHA | 1a2a TAI THAM LETTER LOW SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a2d TAI THAM LETTER RATA | 1a2c TAI THAM LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a2f TAI THAM LETTER DA | 1a2e TAI THAM LETTER HIGH RATHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a31 TAI THAM LETTER RANA | 1a30 TAI THAM LETTER LOW RATHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a33 TAI THAM LETTER HIGH THA | 1a32 TAI THAM LETTER HIGH TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a35 TAI THAM LETTER LOW THA | 1a34 TAI THAM LETTER LOW TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a37 TAI THAM LETTER BA | 1a36 TAI THAM LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a39 TAI THAM LETTER HIGH PHA | 1a38 TAI THAM LETTER HIGH PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a3b TAI THAM LETTER LOW PA | 1a3a TAI THAM LETTER HIGH FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a3d TAI THAM LETTER LOW PHA | 1a3c TAI THAM LETTER LOW FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a3f TAI THAM LETTER LOW YA | 1a3e TAI THAM LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a41 TAI THAM LETTER RA | 1a40 TAI THAM LETTER HIGH YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a43 TAI THAM LETTER LA | 1a42 TAI THAM LETTER RUE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a45 TAI THAM LETTER WA | 1a44 TAI THAM LETTER LUE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a47 TAI THAM LETTER HIGH SSA | 1a46 TAI THAM LETTER HIGH SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a49 TAI THAM LETTER HIGH HA | 1a48 TAI THAM LETTER HIGH SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a4b TAI THAM LETTER A | 1a4a TAI THAM LETTER LLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a4d TAI THAM LETTER I | 1a4c TAI THAM LETTER LOW HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a4f TAI THAM LETTER U | 1a4e TAI THAM LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a51 TAI THAM LETTER EE | 1a50 TAI THAM LETTER UU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a53 TAI THAM LETTER LAE | 1a52 TAI THAM LETTER OO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 1a55 TAI THAM CONSONANT SIGN MEDIAL RA | 1a54 TAI THAM LETTER GREAT SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a57 TAI THAM CONSONANT SIGN LA TANG LAI | 1a56 TAI THAM CONSONANT SIGN MEDIAL LA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a59 TAI THAM CONSONANT SIGN FINAL NGA | 1a58 TAI THAM SIGN MAI KANG LAI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a5b TAI THAM CONSONANT SIGN HIGH RATHA OR L | 1a5a TAI THAM CONSONANT SIGN LOW PA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a5d TAI THAM CONSONANT SIGN BA | 1a5c TAI THAM CONSONANT SIGN MA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 1a5f (null) | 1a5e TAI THAM CONSONANT SIGN SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a61 TAI THAM VOWEL SIGN A | 1a60 TAI THAM SIGN SAKOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a63 TAI THAM VOWEL SIGN AA | 1a62 TAI THAM VOWEL SIGN MAI SAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a65 TAI THAM VOWEL SIGN I | 1a64 TAI THAM VOWEL SIGN TALL AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a67 TAI THAM VOWEL SIGN UE | 1a66 TAI THAM VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a69 TAI THAM VOWEL SIGN U | 1a68 TAI THAM VOWEL SIGN UUE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a6b TAI THAM VOWEL SIGN O | 1a6a TAI THAM VOWEL SIGN UU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a6d TAI THAM VOWEL SIGN OY | 1a6c TAI THAM VOWEL SIGN OA BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a6f TAI THAM VOWEL SIGN AE | 1a6e TAI THAM VOWEL SIGN E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a71 TAI THAM VOWEL SIGN AI | 1a70 TAI THAM VOWEL SIGN OO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a73 TAI THAM VOWEL SIGN OA ABOVE | 1a72 TAI THAM VOWEL SIGN THAM AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a75 TAI THAM SIGN TONE-1 | 1a74 TAI THAM SIGN MAI KANG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a77 TAI THAM SIGN KHUEN TONE-3 | 1a76 TAI THAM SIGN TONE-2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a79 TAI THAM SIGN KHUEN TONE-5 | 1a78 TAI THAM SIGN KHUEN TONE-4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a7b TAI THAM SIGN MAI SAM | 1a7a TAI THAM SIGN RA HAAM */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 1a7d (null) | 1a7c TAI THAM SIGN KHUEN-LUE KARAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 1a7f TAI THAM COMBINING CRYPTOGRAMMIC DOT | 1a7e (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a81 TAI THAM HORA DIGIT ONE | 1a80 TAI THAM HORA DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a83 TAI THAM HORA DIGIT THREE | 1a82 TAI THAM HORA DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a85 TAI THAM HORA DIGIT FIVE | 1a84 TAI THAM HORA DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a87 TAI THAM HORA DIGIT SEVEN | 1a86 TAI THAM HORA DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a89 TAI THAM HORA DIGIT NINE | 1a88 TAI THAM HORA DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1a8b (null) | 1a8a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1a8d (null) | 1a8c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1a8f (null) | 1a8e (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a91 TAI THAM THAM DIGIT ONE | 1a90 TAI THAM THAM DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a93 TAI THAM THAM DIGIT THREE | 1a92 TAI THAM THAM DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a95 TAI THAM THAM DIGIT FIVE | 1a94 TAI THAM THAM DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a97 TAI THAM THAM DIGIT SEVEN | 1a96 TAI THAM THAM DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a99 TAI THAM THAM DIGIT NINE | 1a98 TAI THAM THAM DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1a9b (null) | 1a9a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1a9d (null) | 1a9c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1a9f (null) | 1a9e (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1aa1 TAI THAM SIGN WIANGWAAK | 1aa0 TAI THAM SIGN WIANG */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1aa3 TAI THAM SIGN KEOW | 1aa2 TAI THAM SIGN SAWAN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1aa5 TAI THAM SIGN DOKMAI | 1aa4 TAI THAM SIGN HOY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 1aa7 TAI THAM SIGN MAI YAMOK | 1aa6 TAI THAM SIGN REVERSED ROTATED RANA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1aa9 TAI THAM SIGN KAANKUU | 1aa8 TAI THAM SIGN KAAN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1aab TAI THAM SIGN SATKAANKUU | 1aaa TAI THAM SIGN SATKAAN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1aad TAI THAM SIGN CAANG | 1aac TAI THAM SIGN HANG */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1aaf (null) | 1aae (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ab1 (null) | 1ab0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ab3 (null) | 1ab2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ab5 (null) | 1ab4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ab7 (null) | 1ab6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ab9 (null) | 1ab8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1abb (null) | 1aba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1abd (null) | 1abc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1abf (null) | 1abe (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ac1 (null) | 1ac0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ac3 (null) | 1ac2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ac5 (null) | 1ac4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ac7 (null) | 1ac6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ac9 (null) | 1ac8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1acb (null) | 1aca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1acd (null) | 1acc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1acf (null) | 1ace (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ad1 (null) | 1ad0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ad3 (null) | 1ad2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ad5 (null) | 1ad4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ad7 (null) | 1ad6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ad9 (null) | 1ad8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1adb (null) | 1ada (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1add (null) | 1adc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1adf (null) | 1ade (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ae1 (null) | 1ae0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ae3 (null) | 1ae2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ae5 (null) | 1ae4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ae7 (null) | 1ae6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ae9 (null) | 1ae8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1aeb (null) | 1aea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1aed (null) | 1aec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1aef (null) | 1aee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1af1 (null) | 1af0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1af3 (null) | 1af2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1af5 (null) | 1af4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1af7 (null) | 1af6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1af9 (null) | 1af8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1afb (null) | 1afa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1afd (null) | 1afc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1aff (null) | 1afe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b01 BALINESE SIGN ULU CANDRA | 1b00 BALINESE SIGN ULU RICEM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b03 BALINESE SIGN SURANG | 1b02 BALINESE SIGN CECEK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 1b05 BALINESE LETTER AKARA | 1b04 BALINESE SIGN BISAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b07 BALINESE LETTER IKARA | 1b06 BALINESE LETTER AKARA TEDUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b09 BALINESE LETTER UKARA | 1b08 BALINESE LETTER IKARA TEDUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b0b BALINESE LETTER RA REPA | 1b0a BALINESE LETTER UKARA TEDUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b0d BALINESE LETTER LA LENGA | 1b0c BALINESE LETTER RA REPA TEDUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b0f BALINESE LETTER EKARA | 1b0e BALINESE LETTER LA LENGA TEDUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b11 BALINESE LETTER OKARA | 1b10 BALINESE LETTER AIKARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b13 BALINESE LETTER KA | 1b12 BALINESE LETTER OKARA TEDUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b15 BALINESE LETTER GA | 1b14 BALINESE LETTER KA MAHAPRANA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b17 BALINESE LETTER NGA | 1b16 BALINESE LETTER GA GORA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b19 BALINESE LETTER CA LACA | 1b18 BALINESE LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b1b BALINESE LETTER JA JERA | 1b1a BALINESE LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b1d BALINESE LETTER TA LATIK | 1b1c BALINESE LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b1f BALINESE LETTER DA MURDA ALPAPRANA | 1b1e BALINESE LETTER TA MURDA MAHAPRANA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b21 BALINESE LETTER NA RAMBAT | 1b20 BALINESE LETTER DA MURDA MAHAPRANA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b23 BALINESE LETTER TA TAWA | 1b22 BALINESE LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b25 BALINESE LETTER DA MADU | 1b24 BALINESE LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b27 BALINESE LETTER PA | 1b26 BALINESE LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b29 BALINESE LETTER BA | 1b28 BALINESE LETTER PA KAPAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b2b BALINESE LETTER MA | 1b2a BALINESE LETTER BA KEMBANG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b2d BALINESE LETTER RA | 1b2c BALINESE LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b2f BALINESE LETTER WA | 1b2e BALINESE LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b31 BALINESE LETTER SA SAPA | 1b30 BALINESE LETTER SA SAGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b33 BALINESE LETTER HA | 1b32 BALINESE LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b35 BALINESE VOWEL SIGN TEDUNG | 1b34 BALINESE SIGN REREKAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b37 BALINESE VOWEL SIGN ULU SARI | 1b36 BALINESE VOWEL SIGN ULU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b39 BALINESE VOWEL SIGN SUKU ILUT | 1b38 BALINESE VOWEL SIGN SUKU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b3b BALINESE VOWEL SIGN RA REPA TEDUNG | 1b3a BALINESE VOWEL SIGN RA REPA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b3d BALINESE VOWEL SIGN LA LENGA TEDUNG | 1b3c BALINESE VOWEL SIGN LA LENGA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b3f BALINESE VOWEL SIGN TALING REPA | 1b3e BALINESE VOWEL SIGN TALING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b41 BALINESE VOWEL SIGN TALING REPA TEDUNG | 1b40 BALINESE VOWEL SIGN TALING TEDUNG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b43 BALINESE VOWEL SIGN PEPET TEDUNG | 1b42 BALINESE VOWEL SIGN PEPET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 1b45 BALINESE LETTER KAF SASAK | 1b44 BALINESE ADEG ADEG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b47 BALINESE LETTER TZIR SASAK | 1b46 BALINESE LETTER KHOT SASAK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b49 BALINESE LETTER VE SASAK | 1b48 BALINESE LETTER EF SASAK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b4b BALINESE LETTER ASYURA SASAK | 1b4a BALINESE LETTER ZAL SASAK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1b4d (null) | 1b4c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1b4f (null) | 1b4e (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1b51 BALINESE DIGIT ONE | 1b50 BALINESE DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1b53 BALINESE DIGIT THREE | 1b52 BALINESE DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1b55 BALINESE DIGIT FIVE | 1b54 BALINESE DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1b57 BALINESE DIGIT SEVEN | 1b56 BALINESE DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1b59 BALINESE DIGIT NINE | 1b58 BALINESE DIGIT EIGHT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1b5b BALINESE PAMADA | 1b5a BALINESE PANTI */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1b5d BALINESE CARIK PAMUNGKAH | 1b5c BALINESE WINDU */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1b5f BALINESE CARIK PAREREN | 1b5e BALINESE CARIK SIKI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 1b61 BALINESE MUSICAL SYMBOL DONG | 1b60 BALINESE PAMENENG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b63 BALINESE MUSICAL SYMBOL DUNG | 1b62 BALINESE MUSICAL SYMBOL DENG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b65 BALINESE MUSICAL SYMBOL DANG SURANG | 1b64 BALINESE MUSICAL SYMBOL DANG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b67 BALINESE MUSICAL SYMBOL DAENG | 1b66 BALINESE MUSICAL SYMBOL DING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b69 BALINESE MUSICAL SYMBOL DAING | 1b68 BALINESE MUSICAL SYMBOL DEUNG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b6b BALINESE MUSICAL SYMBOL COMBINING TEGEH | 1b6a BALINESE MUSICAL SYMBOL DANG GEDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b6d BALINESE MUSICAL SYMBOL COMBINING KEMPU | 1b6c BALINESE MUSICAL SYMBOL COMBINING ENDEP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b6f BALINESE MUSICAL SYMBOL COMBINING JEGOG | 1b6e BALINESE MUSICAL SYMBOL COMBINING KEMPL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b71 BALINESE MUSICAL SYMBOL COMBINING KEMPL | 1b70 BALINESE MUSICAL SYMBOL COMBINING KEMPU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b73 BALINESE MUSICAL SYMBOL COMBINING GONG | 1b72 BALINESE MUSICAL SYMBOL COMBINING BENDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b75 BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN | 1b74 BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b77 BALINESE MUSICAL SYMBOL RIGHT-HAND CLOS | 1b76 BALINESE MUSICAL SYMBOL RIGHT-HAND CLOS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b79 BALINESE MUSICAL SYMBOL LEFT-HAND OPEN | 1b78 BALINESE MUSICAL SYMBOL LEFT-HAND OPEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b7b BALINESE MUSICAL SYMBOL LEFT-HAND CLOSE | 1b7a BALINESE MUSICAL SYMBOL LEFT-HAND CLOSE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 1b7d (null) | 1b7c BALINESE MUSICAL SYMBOL LEFT-HAND OPEN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1b7f (null) | 1b7e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b81 SUNDANESE SIGN PANGLAYAR | 1b80 SUNDANESE SIGN PANYECEK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 1b83 SUNDANESE LETTER A | 1b82 SUNDANESE SIGN PANGWISAD */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b85 SUNDANESE LETTER U | 1b84 SUNDANESE LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b87 SUNDANESE LETTER O | 1b86 SUNDANESE LETTER AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b89 SUNDANESE LETTER EU | 1b88 SUNDANESE LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b8b SUNDANESE LETTER QA | 1b8a SUNDANESE LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b8d SUNDANESE LETTER NGA | 1b8c SUNDANESE LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b8f SUNDANESE LETTER JA | 1b8e SUNDANESE LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b91 SUNDANESE LETTER NYA | 1b90 SUNDANESE LETTER ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b93 SUNDANESE LETTER DA | 1b92 SUNDANESE LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b95 SUNDANESE LETTER PA | 1b94 SUNDANESE LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b97 SUNDANESE LETTER VA | 1b96 SUNDANESE LETTER FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b99 SUNDANESE LETTER MA | 1b98 SUNDANESE LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b9b SUNDANESE LETTER RA | 1b9a SUNDANESE LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b9d SUNDANESE LETTER WA | 1b9c SUNDANESE LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b9f SUNDANESE LETTER XA | 1b9e SUNDANESE LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 1ba1 SUNDANESE CONSONANT SIGN PAMINGKAL | 1ba0 SUNDANESE LETTER HA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ba3 SUNDANESE CONSONANT SIGN PANYIKU | 1ba2 SUNDANESE CONSONANT SIGN PANYAKRA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ba5 SUNDANESE VOWEL SIGN PANYUKU | 1ba4 SUNDANESE VOWEL SIGN PANGHULU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ba7 SUNDANESE VOWEL SIGN PANOLONG | 1ba6 SUNDANESE VOWEL SIGN PANAELAENG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ba9 SUNDANESE VOWEL SIGN PANEULEUNG | 1ba8 SUNDANESE VOWEL SIGN PAMEPET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1bab SUNDANESE SIGN VIRAMA | 1baa SUNDANESE SIGN PAMAAEH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1bad SUNDANESE CONSONANT SIGN PASANGAN WA | 1bac SUNDANESE CONSONANT SIGN PASANGAN MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1baf SUNDANESE LETTER SYA | 1bae SUNDANESE LETTER KHA */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1bb1 SUNDANESE DIGIT ONE | 1bb0 SUNDANESE DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1bb3 SUNDANESE DIGIT THREE | 1bb2 SUNDANESE DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1bb5 SUNDANESE DIGIT FIVE | 1bb4 SUNDANESE DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1bb7 SUNDANESE DIGIT SEVEN | 1bb6 SUNDANESE DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1bb9 SUNDANESE DIGIT NINE | 1bb8 SUNDANESE DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bbb SUNDANESE LETTER REU | 1bba SUNDANESE AVAGRAHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bbd SUNDANESE LETTER BHA | 1bbc SUNDANESE LETTER LEU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bbf SUNDANESE LETTER FINAL M | 1bbe SUNDANESE LETTER FINAL K */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bc1 BATAK LETTER SIMALUNGUN A | 1bc0 BATAK LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bc3 BATAK LETTER SIMALUNGUN HA | 1bc2 BATAK LETTER HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bc5 BATAK LETTER BA | 1bc4 BATAK LETTER MANDAILING HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bc7 BATAK LETTER PA | 1bc6 BATAK LETTER KARO BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bc9 BATAK LETTER NA | 1bc8 BATAK LETTER SIMALUNGUN PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bcb BATAK LETTER WA | 1bca BATAK LETTER MANDAILING NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bcd BATAK LETTER PAKPAK WA | 1bcc BATAK LETTER SIMALUNGUN WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bcf BATAK LETTER SIMALUNGUN GA | 1bce BATAK LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bd1 BATAK LETTER DA | 1bd0 BATAK LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bd3 BATAK LETTER SIMALUNGUN RA | 1bd2 BATAK LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bd5 BATAK LETTER SIMALUNGUN MA | 1bd4 BATAK LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bd7 BATAK LETTER NORTHERN TA | 1bd6 BATAK LETTER SOUTHERN TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bd9 BATAK LETTER SIMALUNGUN SA | 1bd8 BATAK LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bdb BATAK LETTER YA | 1bda BATAK LETTER MANDAILING SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bdd BATAK LETTER NGA | 1bdc BATAK LETTER SIMALUNGUN YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bdf BATAK LETTER SIMALUNGUN LA | 1bde BATAK LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1be1 BATAK LETTER CA | 1be0 BATAK LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1be3 BATAK LETTER MBA | 1be2 BATAK LETTER NDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1be5 BATAK LETTER U | 1be4 BATAK LETTER I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1be7 BATAK VOWEL SIGN E | 1be6 BATAK SIGN TOMPI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1be9 BATAK VOWEL SIGN EE | 1be8 BATAK VOWEL SIGN PAKPAK E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1beb BATAK VOWEL SIGN KARO I | 1bea BATAK VOWEL SIGN I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1bed BATAK VOWEL SIGN KARO O | 1bec BATAK VOWEL SIGN O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1bef BATAK VOWEL SIGN U FOR SIMALUNGUN SA | 1bee BATAK VOWEL SIGN U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1bf1 BATAK CONSONANT SIGN H | 1bf0 BATAK CONSONANT SIGN NG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1bf3 BATAK PANONGONAN | 1bf2 BATAK PANGOLAT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1bf5 (null) | 1bf4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1bf7 (null) | 1bf6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1bf9 (null) | 1bf8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1bfb (null) | 1bfa (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1bfd BATAK SYMBOL BINDU PINARBORAS | 1bfc BATAK SYMBOL BINDU NA METEK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1bff BATAK SYMBOL BINDU PANGOLAT | 1bfe BATAK SYMBOL BINDU JUDUL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c01 LEPCHA LETTER KLA | 1c00 LEPCHA LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c03 LEPCHA LETTER GA | 1c02 LEPCHA LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c05 LEPCHA LETTER NGA | 1c04 LEPCHA LETTER GLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c07 LEPCHA LETTER CHA | 1c06 LEPCHA LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c09 LEPCHA LETTER NYA | 1c08 LEPCHA LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c0b LEPCHA LETTER THA | 1c0a LEPCHA LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c0d LEPCHA LETTER NA | 1c0c LEPCHA LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c0f LEPCHA LETTER PLA | 1c0e LEPCHA LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c11 LEPCHA LETTER FA | 1c10 LEPCHA LETTER PHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c13 LEPCHA LETTER BA | 1c12 LEPCHA LETTER FLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c15 LEPCHA LETTER MA | 1c14 LEPCHA LETTER BLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c17 LEPCHA LETTER TSA | 1c16 LEPCHA LETTER MLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c19 LEPCHA LETTER DZA | 1c18 LEPCHA LETTER TSHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c1b LEPCHA LETTER RA | 1c1a LEPCHA LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c1d LEPCHA LETTER HA | 1c1c LEPCHA LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c1f LEPCHA LETTER VA | 1c1e LEPCHA LETTER HLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c21 LEPCHA LETTER SHA | 1c20 LEPCHA LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c23 LEPCHA LETTER A | 1c22 LEPCHA LETTER WA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c25 LEPCHA SUBJOINED LETTER RA | 1c24 LEPCHA SUBJOINED LETTER YA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c27 LEPCHA VOWEL SIGN I | 1c26 LEPCHA VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c29 LEPCHA VOWEL SIGN OO | 1c28 LEPCHA VOWEL SIGN O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c2b LEPCHA VOWEL SIGN UU | 1c2a LEPCHA VOWEL SIGN U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c2d LEPCHA CONSONANT SIGN K | 1c2c LEPCHA VOWEL SIGN E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c2f LEPCHA CONSONANT SIGN L | 1c2e LEPCHA CONSONANT SIGN M */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c31 LEPCHA CONSONANT SIGN P | 1c30 LEPCHA CONSONANT SIGN N */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c33 LEPCHA CONSONANT SIGN T | 1c32 LEPCHA CONSONANT SIGN R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c35 LEPCHA CONSONANT SIGN KANG | 1c34 LEPCHA CONSONANT SIGN NYIN-DO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c37 LEPCHA SIGN NUKTA | 1c36 LEPCHA SIGN RAN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c39 (null) | 1c38 (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_UNDEF, /* 1c3b LEPCHA PUNCTUATION TA-ROL | 1c3a (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1c3d LEPCHA PUNCTUATION CER-WA | 1c3c LEPCHA PUNCTUATION NYET THYOOM TA-ROL */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1c3f LEPCHA PUNCTUATION TSHOOK | 1c3e LEPCHA PUNCTUATION TSHOOK CER-WA */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c41 LEPCHA DIGIT ONE | 1c40 LEPCHA DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c43 LEPCHA DIGIT THREE | 1c42 LEPCHA DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c45 LEPCHA DIGIT FIVE | 1c44 LEPCHA DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c47 LEPCHA DIGIT SEVEN | 1c46 LEPCHA DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c49 LEPCHA DIGIT NINE | 1c48 LEPCHA DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c4b (null) | 1c4a (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 1c4d LEPCHA LETTER TTA | 1c4c (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c4f LEPCHA LETTER DDA | 1c4e LEPCHA LETTER TTHA */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c51 OL CHIKI DIGIT ONE | 1c50 OL CHIKI DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c53 OL CHIKI DIGIT THREE | 1c52 OL CHIKI DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c55 OL CHIKI DIGIT FIVE | 1c54 OL CHIKI DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c57 OL CHIKI DIGIT SEVEN | 1c56 OL CHIKI DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c59 OL CHIKI DIGIT NINE | 1c58 OL CHIKI DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c5b OL CHIKI LETTER AT | 1c5a OL CHIKI LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c5d OL CHIKI LETTER ANG | 1c5c OL CHIKI LETTER AG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c5f OL CHIKI LETTER LAA | 1c5e OL CHIKI LETTER AL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c61 OL CHIKI LETTER AAJ | 1c60 OL CHIKI LETTER AAK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c63 OL CHIKI LETTER AAW | 1c62 OL CHIKI LETTER AAM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c65 OL CHIKI LETTER IS | 1c64 OL CHIKI LETTER LI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c67 OL CHIKI LETTER INY | 1c66 OL CHIKI LETTER IH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c69 OL CHIKI LETTER LU | 1c68 OL CHIKI LETTER IR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c6b OL CHIKI LETTER UD | 1c6a OL CHIKI LETTER UC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c6d OL CHIKI LETTER UY | 1c6c OL CHIKI LETTER UNN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c6f OL CHIKI LETTER EP | 1c6e OL CHIKI LETTER LE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c71 OL CHIKI LETTER EN | 1c70 OL CHIKI LETTER EDD */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c73 OL CHIKI LETTER LO | 1c72 OL CHIKI LETTER ERR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c75 OL CHIKI LETTER OB | 1c74 OL CHIKI LETTER OTT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c77 OL CHIKI LETTER OH | 1c76 OL CHIKI LETTER OV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c79 OL CHIKI GAAHLAA TTUDDAAG | 1c78 OL CHIKI MU TTUDDAG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c7b OL CHIKI RELAA | 1c7a OL CHIKI MU-GAAHLAA TTUDDAAG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c7d OL CHIKI AHAD | 1c7c OL CHIKI PHAARKAA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1c7f OL CHIKI PUNCTUATION DOUBLE MUCAAD | 1c7e OL CHIKI PUNCTUATION MUCAAD */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c81 (null) | 1c80 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c83 (null) | 1c82 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c85 (null) | 1c84 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c87 (null) | 1c86 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c89 (null) | 1c88 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c8b (null) | 1c8a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c8d (null) | 1c8c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c8f (null) | 1c8e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c91 (null) | 1c90 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c93 (null) | 1c92 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c95 (null) | 1c94 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c97 (null) | 1c96 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c99 (null) | 1c98 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c9b (null) | 1c9a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c9d (null) | 1c9c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c9f (null) | 1c9e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ca1 (null) | 1ca0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ca3 (null) | 1ca2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ca5 (null) | 1ca4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ca7 (null) | 1ca6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ca9 (null) | 1ca8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cab (null) | 1caa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cad (null) | 1cac (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1caf (null) | 1cae (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cb1 (null) | 1cb0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cb3 (null) | 1cb2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cb5 (null) | 1cb4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cb7 (null) | 1cb6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cb9 (null) | 1cb8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cbb (null) | 1cba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cbd (null) | 1cbc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cbf (null) | 1cbe (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1cc1 SUNDANESE PUNCTUATION BINDU PANGLONG | 1cc0 SUNDANESE PUNCTUATION BINDU SURYA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1cc3 SUNDANESE PUNCTUATION BINDU CAKRA | 1cc2 SUNDANESE PUNCTUATION BINDU PURNAMA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1cc5 SUNDANESE PUNCTUATION BINDU KA SATANGA | 1cc4 SUNDANESE PUNCTUATION BINDU LEU SATANGA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1cc7 SUNDANESE PUNCTUATION BINDU BA SATANGA | 1cc6 SUNDANESE PUNCTUATION BINDU DA SATANGA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cc9 (null) | 1cc8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ccb (null) | 1cca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ccd (null) | 1ccc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ccf (null) | 1cce (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1cd1 VEDIC TONE SHARA | 1cd0 VEDIC TONE KARSHANA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 1cd3 VEDIC SIGN NIHSHVASA | 1cd2 VEDIC TONE PRENKHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1cd5 VEDIC TONE YAJURVEDIC AGGRAVATED INDEPE | 1cd4 VEDIC SIGN YAJURVEDIC MIDLINE SVARITA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1cd7 VEDIC TONE YAJURVEDIC KATHAKA INDEPENDE | 1cd6 VEDIC TONE YAJURVEDIC INDEPENDENT SVARI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1cd9 VEDIC TONE YAJURVEDIC KATHAKA INDEPENDE | 1cd8 VEDIC TONE CANDRA BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1cdb VEDIC TONE TRIPLE SVARITA | 1cda VEDIC TONE DOUBLE SVARITA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1cdd VEDIC TONE DOT BELOW | 1cdc VEDIC TONE KATHAKA ANUDATTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1cdf VEDIC TONE THREE DOTS BELOW | 1cde VEDIC TONE TWO DOTS BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ce1 VEDIC TONE ATHARVAVEDIC INDEPENDENT SVA | 1ce0 VEDIC TONE RIGVEDIC KASHMIRI INDEPENDEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ce3 VEDIC SIGN VISARGA UDATTA | 1ce2 VEDIC SIGN VISARGA SVARITA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ce5 VEDIC SIGN VISARGA ANUDATTA | 1ce4 VEDIC SIGN REVERSED VISARGA UDATTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ce7 VEDIC SIGN VISARGA UDATTA WITH TAIL | 1ce6 VEDIC SIGN REVERSED VISARGA ANUDATTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 1ce9 VEDIC SIGN ANUSVARA ANTARGOMUKHA | 1ce8 VEDIC SIGN VISARGA ANUDATTA WITH TAIL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1ceb VEDIC SIGN ANUSVARA VAMAGOMUKHA | 1cea VEDIC SIGN ANUSVARA BAHIRGOMUKHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 1ced VEDIC SIGN TIRYAK | 1cec VEDIC SIGN ANUSVARA VAMAGOMUKHA WITH TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1cef VEDIC SIGN LONG ANUSVARA | 1cee VEDIC SIGN HEXIFORM LONG ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1cf1 VEDIC SIGN ANUSVARA UBHAYATO MUKHA | 1cf0 VEDIC SIGN RTHANG LONG ANUSVARA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1cf3 VEDIC SIGN ROTATED ARDHAVISARGA | 1cf2 VEDIC SIGN ARDHAVISARGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 1cf5 VEDIC SIGN JIHVAMULIYA | 1cf4 VEDIC TONE CANDRA ABOVE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 1cf7 (null) | 1cf6 VEDIC SIGN UPADHMANIYA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cf9 (null) | 1cf8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cfb (null) | 1cfa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cfd (null) | 1cfc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cff (null) | 1cfe (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d01 LATIN LETTER SMALL CAPITAL AE | 1d00 LATIN LETTER SMALL CAPITAL A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d03 LATIN LETTER SMALL CAPITAL BARRED B | 1d02 LATIN SMALL LETTER TURNED AE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d05 LATIN LETTER SMALL CAPITAL D | 1d04 LATIN LETTER SMALL CAPITAL C */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d07 LATIN LETTER SMALL CAPITAL E | 1d06 LATIN LETTER SMALL CAPITAL ETH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d09 LATIN SMALL LETTER TURNED I | 1d08 LATIN SMALL LETTER TURNED OPEN E */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d0b LATIN LETTER SMALL CAPITAL K | 1d0a LATIN LETTER SMALL CAPITAL J */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d0d LATIN LETTER SMALL CAPITAL M | 1d0c LATIN LETTER SMALL CAPITAL L WITH STROK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d0f LATIN LETTER SMALL CAPITAL O | 1d0e LATIN LETTER SMALL CAPITAL REVERSED N */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d11 LATIN SMALL LETTER SIDEWAYS O | 1d10 LATIN LETTER SMALL CAPITAL OPEN O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d13 LATIN SMALL LETTER SIDEWAYS O WITH STRO | 1d12 LATIN SMALL LETTER SIDEWAYS OPEN O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d15 LATIN LETTER SMALL CAPITAL OU | 1d14 LATIN SMALL LETTER TURNED OE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d17 LATIN SMALL LETTER BOTTOM HALF O | 1d16 LATIN SMALL LETTER TOP HALF O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d19 LATIN LETTER SMALL CAPITAL REVERSED R | 1d18 LATIN LETTER SMALL CAPITAL P */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d1b LATIN LETTER SMALL CAPITAL T | 1d1a LATIN LETTER SMALL CAPITAL TURNED R */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d1d LATIN SMALL LETTER SIDEWAYS U | 1d1c LATIN LETTER SMALL CAPITAL U */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d1f LATIN SMALL LETTER SIDEWAYS TURNED M | 1d1e LATIN SMALL LETTER SIDEWAYS DIAERESIZED */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d21 LATIN LETTER SMALL CAPITAL W | 1d20 LATIN LETTER SMALL CAPITAL V */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d23 LATIN LETTER SMALL CAPITAL EZH | 1d22 LATIN LETTER SMALL CAPITAL Z */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d25 LATIN LETTER AIN | 1d24 LATIN LETTER VOICED LARYNGEAL SPIRANT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d27 GREEK LETTER SMALL CAPITAL LAMDA | 1d26 GREEK LETTER SMALL CAPITAL GAMMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d29 GREEK LETTER SMALL CAPITAL RHO | 1d28 GREEK LETTER SMALL CAPITAL PI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d2b CYRILLIC LETTER SMALL CAPITAL EL | 1d2a GREEK LETTER SMALL CAPITAL PSI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d2d MODIFIER LETTER CAPITAL AE | 1d2c MODIFIER LETTER CAPITAL A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d2f MODIFIER LETTER CAPITAL BARRED B | 1d2e MODIFIER LETTER CAPITAL B */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d31 MODIFIER LETTER CAPITAL E | 1d30 MODIFIER LETTER CAPITAL D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d33 MODIFIER LETTER CAPITAL G | 1d32 MODIFIER LETTER CAPITAL REVERSED E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d35 MODIFIER LETTER CAPITAL I | 1d34 MODIFIER LETTER CAPITAL H */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d37 MODIFIER LETTER CAPITAL K | 1d36 MODIFIER LETTER CAPITAL J */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d39 MODIFIER LETTER CAPITAL M | 1d38 MODIFIER LETTER CAPITAL L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d3b MODIFIER LETTER CAPITAL REVERSED N | 1d3a MODIFIER LETTER CAPITAL N */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d3d MODIFIER LETTER CAPITAL OU | 1d3c MODIFIER LETTER CAPITAL O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d3f MODIFIER LETTER CAPITAL R | 1d3e MODIFIER LETTER CAPITAL P */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d41 MODIFIER LETTER CAPITAL U | 1d40 MODIFIER LETTER CAPITAL T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d43 MODIFIER LETTER SMALL A | 1d42 MODIFIER LETTER CAPITAL W */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d45 MODIFIER LETTER SMALL ALPHA | 1d44 MODIFIER LETTER SMALL TURNED A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d47 MODIFIER LETTER SMALL B | 1d46 MODIFIER LETTER SMALL TURNED AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d49 MODIFIER LETTER SMALL E | 1d48 MODIFIER LETTER SMALL D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d4b MODIFIER LETTER SMALL OPEN E | 1d4a MODIFIER LETTER SMALL SCHWA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d4d MODIFIER LETTER SMALL G | 1d4c MODIFIER LETTER SMALL TURNED OPEN E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d4f MODIFIER LETTER SMALL K | 1d4e MODIFIER LETTER SMALL TURNED I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d51 MODIFIER LETTER SMALL ENG | 1d50 MODIFIER LETTER SMALL M */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d53 MODIFIER LETTER SMALL OPEN O | 1d52 MODIFIER LETTER SMALL O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d55 MODIFIER LETTER SMALL BOTTOM HALF O | 1d54 MODIFIER LETTER SMALL TOP HALF O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d57 MODIFIER LETTER SMALL T | 1d56 MODIFIER LETTER SMALL P */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d59 MODIFIER LETTER SMALL SIDEWAYS U | 1d58 MODIFIER LETTER SMALL U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d5b MODIFIER LETTER SMALL V | 1d5a MODIFIER LETTER SMALL TURNED M */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d5d MODIFIER LETTER SMALL BETA | 1d5c MODIFIER LETTER SMALL AIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d5f MODIFIER LETTER SMALL DELTA | 1d5e MODIFIER LETTER SMALL GREEK GAMMA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d61 MODIFIER LETTER SMALL CHI | 1d60 MODIFIER LETTER SMALL GREEK PHI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d63 LATIN SUBSCRIPT SMALL LETTER R | 1d62 LATIN SUBSCRIPT SMALL LETTER I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d65 LATIN SUBSCRIPT SMALL LETTER V | 1d64 LATIN SUBSCRIPT SMALL LETTER U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d67 GREEK SUBSCRIPT SMALL LETTER GAMMA | 1d66 GREEK SUBSCRIPT SMALL LETTER BETA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d69 GREEK SUBSCRIPT SMALL LETTER PHI | 1d68 GREEK SUBSCRIPT SMALL LETTER RHO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_OTHER, /* 1d6b LATIN SMALL LETTER UE | 1d6a GREEK SUBSCRIPT SMALL LETTER CHI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d6d LATIN SMALL LETTER D WITH MIDDLE TILDE | 1d6c LATIN SMALL LETTER B WITH MIDDLE TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d6f LATIN SMALL LETTER M WITH MIDDLE TILDE | 1d6e LATIN SMALL LETTER F WITH MIDDLE TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d71 LATIN SMALL LETTER P WITH MIDDLE TILDE | 1d70 LATIN SMALL LETTER N WITH MIDDLE TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d73 LATIN SMALL LETTER R WITH FISHHOOK AND | 1d72 LATIN SMALL LETTER R WITH MIDDLE TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d75 LATIN SMALL LETTER T WITH MIDDLE TILDE | 1d74 LATIN SMALL LETTER S WITH MIDDLE TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d77 LATIN SMALL LETTER TURNED G | 1d76 LATIN SMALL LETTER Z WITH MIDDLE TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_OTHER, /* 1d79 LATIN SMALL LETTER INSULAR G | 1d78 MODIFIER LETTER CYRILLIC EN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d7b LATIN SMALL CAPITAL LETTER I WITH STROK | 1d7a LATIN SMALL LETTER TH WITH STRIKETHROUG */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d7d LATIN SMALL LETTER P WITH STROKE | 1d7c LATIN SMALL LETTER IOTA WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d7f LATIN SMALL LETTER UPSILON WITH STROKE | 1d7e LATIN SMALL CAPITAL LETTER U WITH STROK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d81 LATIN SMALL LETTER D WITH PALATAL HOOK | 1d80 LATIN SMALL LETTER B WITH PALATAL HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d83 LATIN SMALL LETTER G WITH PALATAL HOOK | 1d82 LATIN SMALL LETTER F WITH PALATAL HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d85 LATIN SMALL LETTER L WITH PALATAL HOOK | 1d84 LATIN SMALL LETTER K WITH PALATAL HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d87 LATIN SMALL LETTER N WITH PALATAL HOOK | 1d86 LATIN SMALL LETTER M WITH PALATAL HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d89 LATIN SMALL LETTER R WITH PALATAL HOOK | 1d88 LATIN SMALL LETTER P WITH PALATAL HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d8b LATIN SMALL LETTER ESH WITH PALATAL HOO | 1d8a LATIN SMALL LETTER S WITH PALATAL HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d8d LATIN SMALL LETTER X WITH PALATAL HOOK | 1d8c LATIN SMALL LETTER V WITH PALATAL HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d8f LATIN SMALL LETTER A WITH RETROFLEX HOO | 1d8e LATIN SMALL LETTER Z WITH PALATAL HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d91 LATIN SMALL LETTER D WITH HOOK AND TAIL | 1d90 LATIN SMALL LETTER ALPHA WITH RETROFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d93 LATIN SMALL LETTER OPEN E WITH RETROFLE | 1d92 LATIN SMALL LETTER E WITH RETROFLEX HOO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d95 LATIN SMALL LETTER SCHWA WITH RETROFLEX | 1d94 LATIN SMALL LETTER REVERSED OPEN E WITH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d97 LATIN SMALL LETTER OPEN O WITH RETROFLE | 1d96 LATIN SMALL LETTER I WITH RETROFLEX HOO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d99 LATIN SMALL LETTER U WITH RETROFLEX HOO | 1d98 LATIN SMALL LETTER ESH WITH RETROFLEX H */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_LOWER, /* 1d9b MODIFIER LETTER SMALL TURNED ALPHA | 1d9a LATIN SMALL LETTER EZH WITH RETROFLEX H */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d9d MODIFIER LETTER SMALL C WITH CURL | 1d9c MODIFIER LETTER SMALL C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d9f MODIFIER LETTER SMALL REVERSED OPEN E | 1d9e MODIFIER LETTER SMALL ETH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1da1 MODIFIER LETTER SMALL DOTLESS J WITH ST | 1da0 MODIFIER LETTER SMALL F */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1da3 MODIFIER LETTER SMALL TURNED H | 1da2 MODIFIER LETTER SMALL SCRIPT G */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1da5 MODIFIER LETTER SMALL IOTA | 1da4 MODIFIER LETTER SMALL I WITH STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1da7 MODIFIER LETTER SMALL CAPITAL I WITH ST | 1da6 MODIFIER LETTER SMALL CAPITAL I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1da9 MODIFIER LETTER SMALL L WITH RETROFLEX | 1da8 MODIFIER LETTER SMALL J WITH CROSSED-TA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dab MODIFIER LETTER SMALL CAPITAL L | 1daa MODIFIER LETTER SMALL L WITH PALATAL HO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dad MODIFIER LETTER SMALL TURNED M WITH LON | 1dac MODIFIER LETTER SMALL M WITH HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1daf MODIFIER LETTER SMALL N WITH RETROFLEX | 1dae MODIFIER LETTER SMALL N WITH LEFT HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1db1 MODIFIER LETTER SMALL BARRED O | 1db0 MODIFIER LETTER SMALL CAPITAL N */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1db3 MODIFIER LETTER SMALL S WITH HOOK | 1db2 MODIFIER LETTER SMALL PHI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1db5 MODIFIER LETTER SMALL T WITH PALATAL HO | 1db4 MODIFIER LETTER SMALL ESH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1db7 MODIFIER LETTER SMALL UPSILON | 1db6 MODIFIER LETTER SMALL U BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1db9 MODIFIER LETTER SMALL V WITH HOOK | 1db8 MODIFIER LETTER SMALL CAPITAL U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dbb MODIFIER LETTER SMALL Z | 1dba MODIFIER LETTER SMALL TURNED V */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dbd MODIFIER LETTER SMALL Z WITH CURL | 1dbc MODIFIER LETTER SMALL Z WITH RETROFLEX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dbf MODIFIER LETTER SMALL THETA | 1dbe MODIFIER LETTER SMALL EZH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dc1 COMBINING DOTTED ACUTE ACCENT | 1dc0 COMBINING DOTTED GRAVE ACCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dc3 COMBINING SUSPENSION MARK | 1dc2 COMBINING SNAKE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dc5 COMBINING GRAVE-MACRON | 1dc4 COMBINING MACRON-ACUTE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dc7 COMBINING ACUTE-MACRON | 1dc6 COMBINING MACRON-GRAVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dc9 COMBINING ACUTE-GRAVE-ACUTE | 1dc8 COMBINING GRAVE-ACUTE-GRAVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dcb COMBINING BREVE-MACRON | 1dca COMBINING LATIN SMALL LETTER R BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dcd COMBINING DOUBLE CIRCUMFLEX ABOVE | 1dcc COMBINING MACRON-BREVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dcf COMBINING ZIGZAG BELOW | 1dce COMBINING OGONEK ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dd1 COMBINING UR ABOVE | 1dd0 COMBINING IS BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dd3 COMBINING LATIN SMALL LETTER FLATTENED | 1dd2 COMBINING US ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dd5 COMBINING LATIN SMALL LETTER AO | 1dd4 COMBINING LATIN SMALL LETTER AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dd7 COMBINING LATIN SMALL LETTER C CEDILLA | 1dd6 COMBINING LATIN SMALL LETTER AV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dd9 COMBINING LATIN SMALL LETTER ETH | 1dd8 COMBINING LATIN SMALL LETTER INSULAR D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ddb COMBINING LATIN LETTER SMALL CAPITAL G | 1dda COMBINING LATIN SMALL LETTER G */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ddd COMBINING LATIN SMALL LETTER L | 1ddc COMBINING LATIN SMALL LETTER K */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ddf COMBINING LATIN LETTER SMALL CAPITAL M | 1dde COMBINING LATIN LETTER SMALL CAPITAL L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1de1 COMBINING LATIN LETTER SMALL CAPITAL N | 1de0 COMBINING LATIN SMALL LETTER N */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1de3 COMBINING LATIN SMALL LETTER R ROTUNDA | 1de2 COMBINING LATIN LETTER SMALL CAPITAL R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1de5 COMBINING LATIN SMALL LETTER LONG S | 1de4 COMBINING LATIN SMALL LETTER S */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 1de7 (null) | 1de6 COMBINING LATIN SMALL LETTER Z */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1de9 (null) | 1de8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1deb (null) | 1dea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ded (null) | 1dec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1def (null) | 1dee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1df1 (null) | 1df0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1df3 (null) | 1df2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1df5 (null) | 1df4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1df7 (null) | 1df6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1df9 (null) | 1df8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1dfb (null) | 1dfa (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dfd COMBINING ALMOST EQUAL TO BELOW | 1dfc COMBINING DOUBLE INVERTED BREVE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dff COMBINING RIGHT ARROWHEAD AND DOWN ARRO | 1dfe COMBINING LEFT ARROWHEAD ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e01 LATIN SMALL LETTER A WITH RING BELOW | 1e00 LATIN CAPITAL LETTER A WITH RING BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e03 LATIN SMALL LETTER B WITH DOT ABOVE | 1e02 LATIN CAPITAL LETTER B WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e05 LATIN SMALL LETTER B WITH DOT BELOW | 1e04 LATIN CAPITAL LETTER B WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e07 LATIN SMALL LETTER B WITH LINE BELOW | 1e06 LATIN CAPITAL LETTER B WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e09 LATIN SMALL LETTER C WITH CEDILLA AND A | 1e08 LATIN CAPITAL LETTER C WITH CEDILLA AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e0b LATIN SMALL LETTER D WITH DOT ABOVE | 1e0a LATIN CAPITAL LETTER D WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e0d LATIN SMALL LETTER D WITH DOT BELOW | 1e0c LATIN CAPITAL LETTER D WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e0f LATIN SMALL LETTER D WITH LINE BELOW | 1e0e LATIN CAPITAL LETTER D WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e11 LATIN SMALL LETTER D WITH CEDILLA | 1e10 LATIN CAPITAL LETTER D WITH CEDILLA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e13 LATIN SMALL LETTER D WITH CIRCUMFLEX BE | 1e12 LATIN CAPITAL LETTER D WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e15 LATIN SMALL LETTER E WITH MACRON AND GR | 1e14 LATIN CAPITAL LETTER E WITH MACRON AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e17 LATIN SMALL LETTER E WITH MACRON AND AC | 1e16 LATIN CAPITAL LETTER E WITH MACRON AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e19 LATIN SMALL LETTER E WITH CIRCUMFLEX BE | 1e18 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e1b LATIN SMALL LETTER E WITH TILDE BELOW | 1e1a LATIN CAPITAL LETTER E WITH TILDE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e1d LATIN SMALL LETTER E WITH CEDILLA AND B | 1e1c LATIN CAPITAL LETTER E WITH CEDILLA AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e1f LATIN SMALL LETTER F WITH DOT ABOVE | 1e1e LATIN CAPITAL LETTER F WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e21 LATIN SMALL LETTER G WITH MACRON | 1e20 LATIN CAPITAL LETTER G WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e23 LATIN SMALL LETTER H WITH DOT ABOVE | 1e22 LATIN CAPITAL LETTER H WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e25 LATIN SMALL LETTER H WITH DOT BELOW | 1e24 LATIN CAPITAL LETTER H WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e27 LATIN SMALL LETTER H WITH DIAERESIS | 1e26 LATIN CAPITAL LETTER H WITH DIAERESIS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e29 LATIN SMALL LETTER H WITH CEDILLA | 1e28 LATIN CAPITAL LETTER H WITH CEDILLA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e2b LATIN SMALL LETTER H WITH BREVE BELOW | 1e2a LATIN CAPITAL LETTER H WITH BREVE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e2d LATIN SMALL LETTER I WITH TILDE BELOW | 1e2c LATIN CAPITAL LETTER I WITH TILDE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e2f LATIN SMALL LETTER I WITH DIAERESIS AND | 1e2e LATIN CAPITAL LETTER I WITH DIAERESIS A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e31 LATIN SMALL LETTER K WITH ACUTE | 1e30 LATIN CAPITAL LETTER K WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e33 LATIN SMALL LETTER K WITH DOT BELOW | 1e32 LATIN CAPITAL LETTER K WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e35 LATIN SMALL LETTER K WITH LINE BELOW | 1e34 LATIN CAPITAL LETTER K WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e37 LATIN SMALL LETTER L WITH DOT BELOW | 1e36 LATIN CAPITAL LETTER L WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e39 LATIN SMALL LETTER L WITH DOT BELOW AND | 1e38 LATIN CAPITAL LETTER L WITH DOT BELOW A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e3b LATIN SMALL LETTER L WITH LINE BELOW | 1e3a LATIN CAPITAL LETTER L WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e3d LATIN SMALL LETTER L WITH CIRCUMFLEX BE | 1e3c LATIN CAPITAL LETTER L WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e3f LATIN SMALL LETTER M WITH ACUTE | 1e3e LATIN CAPITAL LETTER M WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e41 LATIN SMALL LETTER M WITH DOT ABOVE | 1e40 LATIN CAPITAL LETTER M WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e43 LATIN SMALL LETTER M WITH DOT BELOW | 1e42 LATIN CAPITAL LETTER M WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e45 LATIN SMALL LETTER N WITH DOT ABOVE | 1e44 LATIN CAPITAL LETTER N WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e47 LATIN SMALL LETTER N WITH DOT BELOW | 1e46 LATIN CAPITAL LETTER N WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e49 LATIN SMALL LETTER N WITH LINE BELOW | 1e48 LATIN CAPITAL LETTER N WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e4b LATIN SMALL LETTER N WITH CIRCUMFLEX BE | 1e4a LATIN CAPITAL LETTER N WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e4d LATIN SMALL LETTER O WITH TILDE AND ACU | 1e4c LATIN CAPITAL LETTER O WITH TILDE AND A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e4f LATIN SMALL LETTER O WITH TILDE AND DIA | 1e4e LATIN CAPITAL LETTER O WITH TILDE AND D */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e51 LATIN SMALL LETTER O WITH MACRON AND GR | 1e50 LATIN CAPITAL LETTER O WITH MACRON AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e53 LATIN SMALL LETTER O WITH MACRON AND AC | 1e52 LATIN CAPITAL LETTER O WITH MACRON AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e55 LATIN SMALL LETTER P WITH ACUTE | 1e54 LATIN CAPITAL LETTER P WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e57 LATIN SMALL LETTER P WITH DOT ABOVE | 1e56 LATIN CAPITAL LETTER P WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e59 LATIN SMALL LETTER R WITH DOT ABOVE | 1e58 LATIN CAPITAL LETTER R WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e5b LATIN SMALL LETTER R WITH DOT BELOW | 1e5a LATIN CAPITAL LETTER R WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e5d LATIN SMALL LETTER R WITH DOT BELOW AND | 1e5c LATIN CAPITAL LETTER R WITH DOT BELOW A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e5f LATIN SMALL LETTER R WITH LINE BELOW | 1e5e LATIN CAPITAL LETTER R WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e61 LATIN SMALL LETTER S WITH DOT ABOVE | 1e60 LATIN CAPITAL LETTER S WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e63 LATIN SMALL LETTER S WITH DOT BELOW | 1e62 LATIN CAPITAL LETTER S WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e65 LATIN SMALL LETTER S WITH ACUTE AND DOT | 1e64 LATIN CAPITAL LETTER S WITH ACUTE AND D */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e67 LATIN SMALL LETTER S WITH CARON AND DOT | 1e66 LATIN CAPITAL LETTER S WITH CARON AND D */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e69 LATIN SMALL LETTER S WITH DOT BELOW AND | 1e68 LATIN CAPITAL LETTER S WITH DOT BELOW A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e6b LATIN SMALL LETTER T WITH DOT ABOVE | 1e6a LATIN CAPITAL LETTER T WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e6d LATIN SMALL LETTER T WITH DOT BELOW | 1e6c LATIN CAPITAL LETTER T WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e6f LATIN SMALL LETTER T WITH LINE BELOW | 1e6e LATIN CAPITAL LETTER T WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e71 LATIN SMALL LETTER T WITH CIRCUMFLEX BE | 1e70 LATIN CAPITAL LETTER T WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e73 LATIN SMALL LETTER U WITH DIAERESIS BEL | 1e72 LATIN CAPITAL LETTER U WITH DIAERESIS B */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e75 LATIN SMALL LETTER U WITH TILDE BELOW | 1e74 LATIN CAPITAL LETTER U WITH TILDE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e77 LATIN SMALL LETTER U WITH CIRCUMFLEX BE | 1e76 LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e79 LATIN SMALL LETTER U WITH TILDE AND ACU | 1e78 LATIN CAPITAL LETTER U WITH TILDE AND A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e7b LATIN SMALL LETTER U WITH MACRON AND DI | 1e7a LATIN CAPITAL LETTER U WITH MACRON AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e7d LATIN SMALL LETTER V WITH TILDE | 1e7c LATIN CAPITAL LETTER V WITH TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e7f LATIN SMALL LETTER V WITH DOT BELOW | 1e7e LATIN CAPITAL LETTER V WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e81 LATIN SMALL LETTER W WITH GRAVE | 1e80 LATIN CAPITAL LETTER W WITH GRAVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e83 LATIN SMALL LETTER W WITH ACUTE | 1e82 LATIN CAPITAL LETTER W WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e85 LATIN SMALL LETTER W WITH DIAERESIS | 1e84 LATIN CAPITAL LETTER W WITH DIAERESIS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e87 LATIN SMALL LETTER W WITH DOT ABOVE | 1e86 LATIN CAPITAL LETTER W WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e89 LATIN SMALL LETTER W WITH DOT BELOW | 1e88 LATIN CAPITAL LETTER W WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e8b LATIN SMALL LETTER X WITH DOT ABOVE | 1e8a LATIN CAPITAL LETTER X WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e8d LATIN SMALL LETTER X WITH DIAERESIS | 1e8c LATIN CAPITAL LETTER X WITH DIAERESIS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e8f LATIN SMALL LETTER Y WITH DOT ABOVE | 1e8e LATIN CAPITAL LETTER Y WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e91 LATIN SMALL LETTER Z WITH CIRCUMFLEX | 1e90 LATIN CAPITAL LETTER Z WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e93 LATIN SMALL LETTER Z WITH DOT BELOW | 1e92 LATIN CAPITAL LETTER Z WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e95 LATIN SMALL LETTER Z WITH LINE BELOW | 1e94 LATIN CAPITAL LETTER Z WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1e97 LATIN SMALL LETTER T WITH DIAERESIS | 1e96 LATIN SMALL LETTER H WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1e99 LATIN SMALL LETTER Y WITH RING ABOVE | 1e98 LATIN SMALL LETTER W WITH RING ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1e9b LATIN SMALL LETTER LONG S WITH DOT ABOV | 1e9a LATIN SMALL LETTER A WITH RIGHT HALF RI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1e9d LATIN SMALL LETTER LONG S WITH HIGH STR | 1e9c LATIN SMALL LETTER LONG S WITH DIAGONAL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e9f LATIN SMALL LETTER DELTA | 1e9e LATIN CAPITAL LETTER SHARP S */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ea1 LATIN SMALL LETTER A WITH DOT BELOW | 1ea0 LATIN CAPITAL LETTER A WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ea3 LATIN SMALL LETTER A WITH HOOK ABOVE | 1ea2 LATIN CAPITAL LETTER A WITH HOOK ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ea5 LATIN SMALL LETTER A WITH CIRCUMFLEX AN | 1ea4 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ea7 LATIN SMALL LETTER A WITH CIRCUMFLEX AN | 1ea6 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ea9 LATIN SMALL LETTER A WITH CIRCUMFLEX AN | 1ea8 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eab LATIN SMALL LETTER A WITH CIRCUMFLEX AN | 1eaa LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ead LATIN SMALL LETTER A WITH CIRCUMFLEX AN | 1eac LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eaf LATIN SMALL LETTER A WITH BREVE AND ACU | 1eae LATIN CAPITAL LETTER A WITH BREVE AND A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eb1 LATIN SMALL LETTER A WITH BREVE AND GRA | 1eb0 LATIN CAPITAL LETTER A WITH BREVE AND G */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eb3 LATIN SMALL LETTER A WITH BREVE AND HOO | 1eb2 LATIN CAPITAL LETTER A WITH BREVE AND H */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eb5 LATIN SMALL LETTER A WITH BREVE AND TIL | 1eb4 LATIN CAPITAL LETTER A WITH BREVE AND T */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eb7 LATIN SMALL LETTER A WITH BREVE AND DOT | 1eb6 LATIN CAPITAL LETTER A WITH BREVE AND D */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eb9 LATIN SMALL LETTER E WITH DOT BELOW | 1eb8 LATIN CAPITAL LETTER E WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ebb LATIN SMALL LETTER E WITH HOOK ABOVE | 1eba LATIN CAPITAL LETTER E WITH HOOK ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ebd LATIN SMALL LETTER E WITH TILDE | 1ebc LATIN CAPITAL LETTER E WITH TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ebf LATIN SMALL LETTER E WITH CIRCUMFLEX AN | 1ebe LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ec1 LATIN SMALL LETTER E WITH CIRCUMFLEX AN | 1ec0 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ec3 LATIN SMALL LETTER E WITH CIRCUMFLEX AN | 1ec2 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ec5 LATIN SMALL LETTER E WITH CIRCUMFLEX AN | 1ec4 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ec7 LATIN SMALL LETTER E WITH CIRCUMFLEX AN | 1ec6 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ec9 LATIN SMALL LETTER I WITH HOOK ABOVE | 1ec8 LATIN CAPITAL LETTER I WITH HOOK ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ecb LATIN SMALL LETTER I WITH DOT BELOW | 1eca LATIN CAPITAL LETTER I WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ecd LATIN SMALL LETTER O WITH DOT BELOW | 1ecc LATIN CAPITAL LETTER O WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ecf LATIN SMALL LETTER O WITH HOOK ABOVE | 1ece LATIN CAPITAL LETTER O WITH HOOK ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ed1 LATIN SMALL LETTER O WITH CIRCUMFLEX AN | 1ed0 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ed3 LATIN SMALL LETTER O WITH CIRCUMFLEX AN | 1ed2 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ed5 LATIN SMALL LETTER O WITH CIRCUMFLEX AN | 1ed4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ed7 LATIN SMALL LETTER O WITH CIRCUMFLEX AN | 1ed6 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ed9 LATIN SMALL LETTER O WITH CIRCUMFLEX AN | 1ed8 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1edb LATIN SMALL LETTER O WITH HORN AND ACUT | 1eda LATIN CAPITAL LETTER O WITH HORN AND AC */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1edd LATIN SMALL LETTER O WITH HORN AND GRAV | 1edc LATIN CAPITAL LETTER O WITH HORN AND GR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1edf LATIN SMALL LETTER O WITH HORN AND HOOK | 1ede LATIN CAPITAL LETTER O WITH HORN AND HO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ee1 LATIN SMALL LETTER O WITH HORN AND TILD | 1ee0 LATIN CAPITAL LETTER O WITH HORN AND TI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ee3 LATIN SMALL LETTER O WITH HORN AND DOT | 1ee2 LATIN CAPITAL LETTER O WITH HORN AND DO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ee5 LATIN SMALL LETTER U WITH DOT BELOW | 1ee4 LATIN CAPITAL LETTER U WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ee7 LATIN SMALL LETTER U WITH HOOK ABOVE | 1ee6 LATIN CAPITAL LETTER U WITH HOOK ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ee9 LATIN SMALL LETTER U WITH HORN AND ACUT | 1ee8 LATIN CAPITAL LETTER U WITH HORN AND AC */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eeb LATIN SMALL LETTER U WITH HORN AND GRAV | 1eea LATIN CAPITAL LETTER U WITH HORN AND GR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eed LATIN SMALL LETTER U WITH HORN AND HOOK | 1eec LATIN CAPITAL LETTER U WITH HORN AND HO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eef LATIN SMALL LETTER U WITH HORN AND TILD | 1eee LATIN CAPITAL LETTER U WITH HORN AND TI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ef1 LATIN SMALL LETTER U WITH HORN AND DOT | 1ef0 LATIN CAPITAL LETTER U WITH HORN AND DO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ef3 LATIN SMALL LETTER Y WITH GRAVE | 1ef2 LATIN CAPITAL LETTER Y WITH GRAVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ef5 LATIN SMALL LETTER Y WITH DOT BELOW | 1ef4 LATIN CAPITAL LETTER Y WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ef7 LATIN SMALL LETTER Y WITH HOOK ABOVE | 1ef6 LATIN CAPITAL LETTER Y WITH HOOK ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ef9 LATIN SMALL LETTER Y WITH TILDE | 1ef8 LATIN CAPITAL LETTER Y WITH TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1efb LATIN SMALL LETTER MIDDLE-WELSH LL | 1efa LATIN CAPITAL LETTER MIDDLE-WELSH LL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1efd LATIN SMALL LETTER MIDDLE-WELSH V | 1efc LATIN CAPITAL LETTER MIDDLE-WELSH V */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eff LATIN SMALL LETTER Y WITH LOOP | 1efe LATIN CAPITAL LETTER Y WITH LOOP */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f01 GREEK SMALL LETTER ALPHA WITH DASIA | 1f00 GREEK SMALL LETTER ALPHA WITH PSILI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f03 GREEK SMALL LETTER ALPHA WITH DASIA AND | 1f02 GREEK SMALL LETTER ALPHA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f05 GREEK SMALL LETTER ALPHA WITH DASIA AND | 1f04 GREEK SMALL LETTER ALPHA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f07 GREEK SMALL LETTER ALPHA WITH DASIA AND | 1f06 GREEK SMALL LETTER ALPHA WITH PSILI AND */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f09 GREEK CAPITAL LETTER ALPHA WITH DASIA | 1f08 GREEK CAPITAL LETTER ALPHA WITH PSILI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f0b GREEK CAPITAL LETTER ALPHA WITH DASIA A | 1f0a GREEK CAPITAL LETTER ALPHA WITH PSILI A */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f0d GREEK CAPITAL LETTER ALPHA WITH DASIA A | 1f0c GREEK CAPITAL LETTER ALPHA WITH PSILI A */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f0f GREEK CAPITAL LETTER ALPHA WITH DASIA A | 1f0e GREEK CAPITAL LETTER ALPHA WITH PSILI A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f11 GREEK SMALL LETTER EPSILON WITH DASIA | 1f10 GREEK SMALL LETTER EPSILON WITH PSILI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f13 GREEK SMALL LETTER EPSILON WITH DASIA A | 1f12 GREEK SMALL LETTER EPSILON WITH PSILI A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f15 GREEK SMALL LETTER EPSILON WITH DASIA A | 1f14 GREEK SMALL LETTER EPSILON WITH PSILI A */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1f17 (null) | 1f16 (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f19 GREEK CAPITAL LETTER EPSILON WITH DASIA | 1f18 GREEK CAPITAL LETTER EPSILON WITH PSILI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f1b GREEK CAPITAL LETTER EPSILON WITH DASIA | 1f1a GREEK CAPITAL LETTER EPSILON WITH PSILI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f1d GREEK CAPITAL LETTER EPSILON WITH DASIA | 1f1c GREEK CAPITAL LETTER EPSILON WITH PSILI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1f1f (null) | 1f1e (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f21 GREEK SMALL LETTER ETA WITH DASIA | 1f20 GREEK SMALL LETTER ETA WITH PSILI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f23 GREEK SMALL LETTER ETA WITH DASIA AND V | 1f22 GREEK SMALL LETTER ETA WITH PSILI AND V */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f25 GREEK SMALL LETTER ETA WITH DASIA AND O | 1f24 GREEK SMALL LETTER ETA WITH PSILI AND O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f27 GREEK SMALL LETTER ETA WITH DASIA AND P | 1f26 GREEK SMALL LETTER ETA WITH PSILI AND P */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f29 GREEK CAPITAL LETTER ETA WITH DASIA | 1f28 GREEK CAPITAL LETTER ETA WITH PSILI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f2b GREEK CAPITAL LETTER ETA WITH DASIA AND | 1f2a GREEK CAPITAL LETTER ETA WITH PSILI AND */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f2d GREEK CAPITAL LETTER ETA WITH DASIA AND | 1f2c GREEK CAPITAL LETTER ETA WITH PSILI AND */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f2f GREEK CAPITAL LETTER ETA WITH DASIA AND | 1f2e GREEK CAPITAL LETTER ETA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f31 GREEK SMALL LETTER IOTA WITH DASIA | 1f30 GREEK SMALL LETTER IOTA WITH PSILI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f33 GREEK SMALL LETTER IOTA WITH DASIA AND | 1f32 GREEK SMALL LETTER IOTA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f35 GREEK SMALL LETTER IOTA WITH DASIA AND | 1f34 GREEK SMALL LETTER IOTA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f37 GREEK SMALL LETTER IOTA WITH DASIA AND | 1f36 GREEK SMALL LETTER IOTA WITH PSILI AND */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f39 GREEK CAPITAL LETTER IOTA WITH DASIA | 1f38 GREEK CAPITAL LETTER IOTA WITH PSILI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f3b GREEK CAPITAL LETTER IOTA WITH DASIA AN | 1f3a GREEK CAPITAL LETTER IOTA WITH PSILI AN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f3d GREEK CAPITAL LETTER IOTA WITH DASIA AN | 1f3c GREEK CAPITAL LETTER IOTA WITH PSILI AN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f3f GREEK CAPITAL LETTER IOTA WITH DASIA AN | 1f3e GREEK CAPITAL LETTER IOTA WITH PSILI AN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f41 GREEK SMALL LETTER OMICRON WITH DASIA | 1f40 GREEK SMALL LETTER OMICRON WITH PSILI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f43 GREEK SMALL LETTER OMICRON WITH DASIA A | 1f42 GREEK SMALL LETTER OMICRON WITH PSILI A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f45 GREEK SMALL LETTER OMICRON WITH DASIA A | 1f44 GREEK SMALL LETTER OMICRON WITH PSILI A */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1f47 (null) | 1f46 (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f49 GREEK CAPITAL LETTER OMICRON WITH DASIA | 1f48 GREEK CAPITAL LETTER OMICRON WITH PSILI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f4b GREEK CAPITAL LETTER OMICRON WITH DASIA | 1f4a GREEK CAPITAL LETTER OMICRON WITH PSILI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f4d GREEK CAPITAL LETTER OMICRON WITH DASIA | 1f4c GREEK CAPITAL LETTER OMICRON WITH PSILI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1f4f (null) | 1f4e (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f51 GREEK SMALL LETTER UPSILON WITH DASIA | 1f50 GREEK SMALL LETTER UPSILON WITH PSILI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f53 GREEK SMALL LETTER UPSILON WITH DASIA A | 1f52 GREEK SMALL LETTER UPSILON WITH PSILI A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f55 GREEK SMALL LETTER UPSILON WITH DASIA A | 1f54 GREEK SMALL LETTER UPSILON WITH PSILI A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f57 GREEK SMALL LETTER UPSILON WITH DASIA A | 1f56 GREEK SMALL LETTER UPSILON WITH PSILI A */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UNDEF, /* 1f59 GREEK CAPITAL LETTER UPSILON WITH DASIA | 1f58 (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UNDEF, /* 1f5b GREEK CAPITAL LETTER UPSILON WITH DASIA | 1f5a (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UNDEF, /* 1f5d GREEK CAPITAL LETTER UPSILON WITH DASIA | 1f5c (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UNDEF, /* 1f5f GREEK CAPITAL LETTER UPSILON WITH DASIA | 1f5e (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f61 GREEK SMALL LETTER OMEGA WITH DASIA | 1f60 GREEK SMALL LETTER OMEGA WITH PSILI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f63 GREEK SMALL LETTER OMEGA WITH DASIA AND | 1f62 GREEK SMALL LETTER OMEGA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f65 GREEK SMALL LETTER OMEGA WITH DASIA AND | 1f64 GREEK SMALL LETTER OMEGA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f67 GREEK SMALL LETTER OMEGA WITH DASIA AND | 1f66 GREEK SMALL LETTER OMEGA WITH PSILI AND */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f69 GREEK CAPITAL LETTER OMEGA WITH DASIA | 1f68 GREEK CAPITAL LETTER OMEGA WITH PSILI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f6b GREEK CAPITAL LETTER OMEGA WITH DASIA A | 1f6a GREEK CAPITAL LETTER OMEGA WITH PSILI A */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f6d GREEK CAPITAL LETTER OMEGA WITH DASIA A | 1f6c GREEK CAPITAL LETTER OMEGA WITH PSILI A */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f6f GREEK CAPITAL LETTER OMEGA WITH DASIA A | 1f6e GREEK CAPITAL LETTER OMEGA WITH PSILI A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f71 GREEK SMALL LETTER ALPHA WITH OXIA | 1f70 GREEK SMALL LETTER ALPHA WITH VARIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f73 GREEK SMALL LETTER EPSILON WITH OXIA | 1f72 GREEK SMALL LETTER EPSILON WITH VARIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f75 GREEK SMALL LETTER ETA WITH OXIA | 1f74 GREEK SMALL LETTER ETA WITH VARIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f77 GREEK SMALL LETTER IOTA WITH OXIA | 1f76 GREEK SMALL LETTER IOTA WITH VARIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f79 GREEK SMALL LETTER OMICRON WITH OXIA | 1f78 GREEK SMALL LETTER OMICRON WITH VARIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f7b GREEK SMALL LETTER UPSILON WITH OXIA | 1f7a GREEK SMALL LETTER UPSILON WITH VARIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f7d GREEK SMALL LETTER OMEGA WITH OXIA | 1f7c GREEK SMALL LETTER OMEGA WITH VARIA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1f7f (null) | 1f7e (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f81 GREEK SMALL LETTER ALPHA WITH DASIA AND | 1f80 GREEK SMALL LETTER ALPHA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f83 GREEK SMALL LETTER ALPHA WITH DASIA AND | 1f82 GREEK SMALL LETTER ALPHA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f85 GREEK SMALL LETTER ALPHA WITH DASIA AND | 1f84 GREEK SMALL LETTER ALPHA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f87 GREEK SMALL LETTER ALPHA WITH DASIA AND | 1f86 GREEK SMALL LETTER ALPHA WITH PSILI AND */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1f89 GREEK CAPITAL LETTER ALPHA WITH DASIA A | 1f88 GREEK CAPITAL LETTER ALPHA WITH PSILI A */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1f8b GREEK CAPITAL LETTER ALPHA WITH DASIA A | 1f8a GREEK CAPITAL LETTER ALPHA WITH PSILI A */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1f8d GREEK CAPITAL LETTER ALPHA WITH DASIA A | 1f8c GREEK CAPITAL LETTER ALPHA WITH PSILI A */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1f8f GREEK CAPITAL LETTER ALPHA WITH DASIA A | 1f8e GREEK CAPITAL LETTER ALPHA WITH PSILI A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f91 GREEK SMALL LETTER ETA WITH DASIA AND Y | 1f90 GREEK SMALL LETTER ETA WITH PSILI AND Y */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f93 GREEK SMALL LETTER ETA WITH DASIA AND V | 1f92 GREEK SMALL LETTER ETA WITH PSILI AND V */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f95 GREEK SMALL LETTER ETA WITH DASIA AND O | 1f94 GREEK SMALL LETTER ETA WITH PSILI AND O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f97 GREEK SMALL LETTER ETA WITH DASIA AND P | 1f96 GREEK SMALL LETTER ETA WITH PSILI AND P */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1f99 GREEK CAPITAL LETTER ETA WITH DASIA AND | 1f98 GREEK CAPITAL LETTER ETA WITH PSILI AND */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1f9b GREEK CAPITAL LETTER ETA WITH DASIA AND | 1f9a GREEK CAPITAL LETTER ETA WITH PSILI AND */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1f9d GREEK CAPITAL LETTER ETA WITH DASIA AND | 1f9c GREEK CAPITAL LETTER ETA WITH PSILI AND */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1f9f GREEK CAPITAL LETTER ETA WITH DASIA AND | 1f9e GREEK CAPITAL LETTER ETA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fa1 GREEK SMALL LETTER OMEGA WITH DASIA AND | 1fa0 GREEK SMALL LETTER OMEGA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fa3 GREEK SMALL LETTER OMEGA WITH DASIA AND | 1fa2 GREEK SMALL LETTER OMEGA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fa5 GREEK SMALL LETTER OMEGA WITH DASIA AND | 1fa4 GREEK SMALL LETTER OMEGA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fa7 GREEK SMALL LETTER OMEGA WITH DASIA AND | 1fa6 GREEK SMALL LETTER OMEGA WITH PSILI AND */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1fa9 GREEK CAPITAL LETTER OMEGA WITH DASIA A | 1fa8 GREEK CAPITAL LETTER OMEGA WITH PSILI A */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1fab GREEK CAPITAL LETTER OMEGA WITH DASIA A | 1faa GREEK CAPITAL LETTER OMEGA WITH PSILI A */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1fad GREEK CAPITAL LETTER OMEGA WITH DASIA A | 1fac GREEK CAPITAL LETTER OMEGA WITH PSILI A */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1faf GREEK CAPITAL LETTER OMEGA WITH DASIA A | 1fae GREEK CAPITAL LETTER OMEGA WITH PSILI A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fb1 GREEK SMALL LETTER ALPHA WITH MACRON | 1fb0 GREEK SMALL LETTER ALPHA WITH VRACHY */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fb3 GREEK SMALL LETTER ALPHA WITH YPOGEGRAM | 1fb2 GREEK SMALL LETTER ALPHA WITH VARIA AND */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_LOWER, /* 1fb5 (null) | 1fb4 GREEK SMALL LETTER ALPHA WITH OXIA AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fb7 GREEK SMALL LETTER ALPHA WITH PERISPOME | 1fb6 GREEK SMALL LETTER ALPHA WITH PERISPOME */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1fb9 GREEK CAPITAL LETTER ALPHA WITH MACRON | 1fb8 GREEK CAPITAL LETTER ALPHA WITH VRACHY */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1fbb GREEK CAPITAL LETTER ALPHA WITH OXIA | 1fba GREEK CAPITAL LETTER ALPHA WITH VARIA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_TITLE, /* 1fbd GREEK KORONIS | 1fbc GREEK CAPITAL LETTER ALPHA WITH PROSGEG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_LOWER, /* 1fbf GREEK PSILI | 1fbe GREEK PROSGEGRAMMENI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1fc1 GREEK DIALYTIKA AND PERISPOMENI | 1fc0 GREEK PERISPOMENI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fc3 GREEK SMALL LETTER ETA WITH YPOGEGRAMME | 1fc2 GREEK SMALL LETTER ETA WITH VARIA AND Y */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_LOWER, /* 1fc5 (null) | 1fc4 GREEK SMALL LETTER ETA WITH OXIA AND YP */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fc7 GREEK SMALL LETTER ETA WITH PERISPOMENI | 1fc6 GREEK SMALL LETTER ETA WITH PERISPOMENI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1fc9 GREEK CAPITAL LETTER EPSILON WITH OXIA | 1fc8 GREEK CAPITAL LETTER EPSILON WITH VARIA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1fcb GREEK CAPITAL LETTER ETA WITH OXIA | 1fca GREEK CAPITAL LETTER ETA WITH VARIA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_TITLE, /* 1fcd GREEK PSILI AND VARIA | 1fcc GREEK CAPITAL LETTER ETA WITH PROSGEGRA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1fcf GREEK PSILI AND PERISPOMENI | 1fce GREEK PSILI AND OXIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fd1 GREEK SMALL LETTER IOTA WITH MACRON | 1fd0 GREEK SMALL LETTER IOTA WITH VRACHY */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fd3 GREEK SMALL LETTER IOTA WITH DIALYTIKA | 1fd2 GREEK SMALL LETTER IOTA WITH DIALYTIKA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1fd5 (null) | 1fd4 (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fd7 GREEK SMALL LETTER IOTA WITH DIALYTIKA | 1fd6 GREEK SMALL LETTER IOTA WITH PERISPOMEN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1fd9 GREEK CAPITAL LETTER IOTA WITH MACRON | 1fd8 GREEK CAPITAL LETTER IOTA WITH VRACHY */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1fdb GREEK CAPITAL LETTER IOTA WITH OXIA | 1fda GREEK CAPITAL LETTER IOTA WITH VARIA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 1fdd GREEK DASIA AND VARIA | 1fdc (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1fdf GREEK DASIA AND PERISPOMENI | 1fde GREEK DASIA AND OXIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fe1 GREEK SMALL LETTER UPSILON WITH MACRON | 1fe0 GREEK SMALL LETTER UPSILON WITH VRACHY */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fe3 GREEK SMALL LETTER UPSILON WITH DIALYTI | 1fe2 GREEK SMALL LETTER UPSILON WITH DIALYTI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fe5 GREEK SMALL LETTER RHO WITH DASIA | 1fe4 GREEK SMALL LETTER RHO WITH PSILI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fe7 GREEK SMALL LETTER UPSILON WITH DIALYTI | 1fe6 GREEK SMALL LETTER UPSILON WITH PERISPO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1fe9 GREEK CAPITAL LETTER UPSILON WITH MACRO | 1fe8 GREEK CAPITAL LETTER UPSILON WITH VRACH */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1feb GREEK CAPITAL LETTER UPSILON WITH OXIA | 1fea GREEK CAPITAL LETTER UPSILON WITH VARIA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UPPER, /* 1fed GREEK DIALYTIKA AND VARIA | 1fec GREEK CAPITAL LETTER RHO WITH DASIA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1fef GREEK VARIA | 1fee GREEK DIALYTIKA AND OXIA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ff1 (null) | 1ff0 (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1ff3 GREEK SMALL LETTER OMEGA WITH YPOGEGRAM | 1ff2 GREEK SMALL LETTER OMEGA WITH VARIA AND */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_LOWER, /* 1ff5 (null) | 1ff4 GREEK SMALL LETTER OMEGA WITH OXIA AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1ff7 GREEK SMALL LETTER OMEGA WITH PERISPOME | 1ff6 GREEK SMALL LETTER OMEGA WITH PERISPOME */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1ff9 GREEK CAPITAL LETTER OMICRON WITH OXIA | 1ff8 GREEK CAPITAL LETTER OMICRON WITH VARIA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1ffb GREEK CAPITAL LETTER OMEGA WITH OXIA | 1ffa GREEK CAPITAL LETTER OMEGA WITH VARIA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_TITLE, /* 1ffd GREEK OXIA | 1ffc GREEK CAPITAL LETTER OMEGA WITH PROSGEG */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 1fff (null) | 1ffe GREEK DASIA */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_SPACE, /* 2001 EM QUAD | 2000 EN QUAD */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_SPACE, /* 2003 EM SPACE | 2002 EN SPACE */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_SPACE, /* 2005 FOUR-PER-EM SPACE | 2004 THREE-PER-EM SPACE */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_SPACE, /* 2007 FIGURE SPACE | 2006 SIX-PER-EM SPACE */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_SPACE, /* 2009 THIN SPACE | 2008 PUNCTUATION SPACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_SPACE, /* 200b ZERO WIDTH SPACE | 200a HAIR SPACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 200d ZERO WIDTH JOINER | 200c ZERO WIDTH NON-JOINER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 200f RIGHT-TO-LEFT MARK | 200e LEFT-TO-RIGHT MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2011 NON-BREAKING HYPHEN | 2010 HYPHEN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2013 EN DASH | 2012 FIGURE DASH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2015 HORIZONTAL BAR | 2014 EM DASH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2017 DOUBLE LOW LINE | 2016 DOUBLE VERTICAL LINE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2019 RIGHT SINGLE QUOTATION MARK | 2018 LEFT SINGLE QUOTATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 201b SINGLE HIGH-REVERSED-9 QUOTATION MARK | 201a SINGLE LOW-9 QUOTATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 201d RIGHT DOUBLE QUOTATION MARK | 201c LEFT DOUBLE QUOTATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 201f DOUBLE HIGH-REVERSED-9 QUOTATION MARK | 201e DOUBLE LOW-9 QUOTATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2021 DOUBLE DAGGER | 2020 DAGGER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2023 TRIANGULAR BULLET | 2022 BULLET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2025 TWO DOT LEADER | 2024 ONE DOT LEADER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2027 HYPHENATION POINT | 2026 HORIZONTAL ELLIPSIS */ (T3_CTYPE_VSPAC << 4) | T3_CTYPE_VSPAC, /* 2029 PARAGRAPH SEPARATOR | 2028 LINE SEPARATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 202b RIGHT-TO-LEFT EMBEDDING | 202a LEFT-TO-RIGHT EMBEDDING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 202d LEFT-TO-RIGHT OVERRIDE | 202c POP DIRECTIONAL FORMATTING */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_OTHER, /* 202f NARROW NO-BREAK SPACE | 202e RIGHT-TO-LEFT OVERRIDE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2031 PER TEN THOUSAND SIGN | 2030 PER MILLE SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2033 DOUBLE PRIME | 2032 PRIME */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2035 REVERSED PRIME | 2034 TRIPLE PRIME */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2037 REVERSED TRIPLE PRIME | 2036 REVERSED DOUBLE PRIME */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2039 SINGLE LEFT-POINTING ANGLE QUOTATION MA | 2038 CARET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 203b REFERENCE MARK | 203a SINGLE RIGHT-POINTING ANGLE QUOTATION M */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 203d INTERROBANG | 203c DOUBLE EXCLAMATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 203f UNDERTIE | 203e OVERLINE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2041 CARET INSERTION POINT | 2040 CHARACTER TIE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2043 HYPHEN BULLET | 2042 ASTERISM */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 2045 LEFT SQUARE BRACKET WITH QUILL | 2044 FRACTION SLASH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2047 DOUBLE QUESTION MARK | 2046 RIGHT SQUARE BRACKET WITH QUILL */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2049 EXCLAMATION QUESTION MARK | 2048 QUESTION EXCLAMATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 204b REVERSED PILCROW SIGN | 204a TIRONIAN SIGN ET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 204d BLACK RIGHTWARDS BULLET | 204c BLACK LEFTWARDS BULLET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 204f REVERSED SEMICOLON | 204e LOW ASTERISK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2051 TWO ASTERISKS ALIGNED VERTICALLY | 2050 CLOSE UP */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 2053 SWUNG DASH | 2052 COMMERCIAL MINUS SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2055 FLOWER PUNCTUATION MARK | 2054 INVERTED UNDERTIE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2057 QUADRUPLE PRIME | 2056 THREE DOT PUNCTUATION */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2059 FIVE DOT PUNCTUATION | 2058 FOUR DOT PUNCTUATION */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 205b FOUR DOT MARK | 205a TWO DOT PUNCTUATION */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 205d TRICOLON | 205c DOTTED CROSS */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_PUNCT, /* 205f MEDIUM MATHEMATICAL SPACE | 205e VERTICAL FOUR DOTS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2061 FUNCTION APPLICATION | 2060 WORD JOINER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2063 INVISIBLE SEPARATOR | 2062 INVISIBLE TIMES */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 2065 (null) | 2064 INVISIBLE PLUS */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2067 (null) | 2066 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2069 (null) | 2068 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 206b ACTIVATE SYMMETRIC SWAPPING | 206a INHIBIT SYMMETRIC SWAPPING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 206d ACTIVATE ARABIC FORM SHAPING | 206c INHIBIT ARABIC FORM SHAPING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 206f NOMINAL DIGIT SHAPES | 206e NATIONAL DIGIT SHAPES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2071 SUPERSCRIPT LATIN SMALL LETTER I | 2070 SUPERSCRIPT ZERO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2073 (null) | 2072 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2075 SUPERSCRIPT FIVE | 2074 SUPERSCRIPT FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2077 SUPERSCRIPT SEVEN | 2076 SUPERSCRIPT SIX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2079 SUPERSCRIPT NINE | 2078 SUPERSCRIPT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 207b SUPERSCRIPT MINUS | 207a SUPERSCRIPT PLUS SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 207d SUPERSCRIPT LEFT PARENTHESIS | 207c SUPERSCRIPT EQUALS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 207f SUPERSCRIPT LATIN SMALL LETTER N | 207e SUPERSCRIPT RIGHT PARENTHESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2081 SUBSCRIPT ONE | 2080 SUBSCRIPT ZERO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2083 SUBSCRIPT THREE | 2082 SUBSCRIPT TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2085 SUBSCRIPT FIVE | 2084 SUBSCRIPT FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2087 SUBSCRIPT SEVEN | 2086 SUBSCRIPT SIX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2089 SUBSCRIPT NINE | 2088 SUBSCRIPT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 208b SUBSCRIPT MINUS | 208a SUBSCRIPT PLUS SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 208d SUBSCRIPT LEFT PARENTHESIS | 208c SUBSCRIPT EQUALS SIGN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 208f (null) | 208e SUBSCRIPT RIGHT PARENTHESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2091 LATIN SUBSCRIPT SMALL LETTER E | 2090 LATIN SUBSCRIPT SMALL LETTER A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2093 LATIN SUBSCRIPT SMALL LETTER X | 2092 LATIN SUBSCRIPT SMALL LETTER O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2095 LATIN SUBSCRIPT SMALL LETTER H | 2094 LATIN SUBSCRIPT SMALL LETTER SCHWA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2097 LATIN SUBSCRIPT SMALL LETTER L | 2096 LATIN SUBSCRIPT SMALL LETTER K */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2099 LATIN SUBSCRIPT SMALL LETTER N | 2098 LATIN SUBSCRIPT SMALL LETTER M */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 209b LATIN SUBSCRIPT SMALL LETTER S | 209a LATIN SUBSCRIPT SMALL LETTER P */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 209d (null) | 209c LATIN SUBSCRIPT SMALL LETTER T */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 209f (null) | 209e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20a1 COLON SIGN | 20a0 EURO-CURRENCY SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20a3 FRENCH FRANC SIGN | 20a2 CRUZEIRO SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20a5 MILL SIGN | 20a4 LIRA SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20a7 PESETA SIGN | 20a6 NAIRA SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20a9 WON SIGN | 20a8 RUPEE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20ab DONG SIGN | 20aa NEW SHEQEL SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20ad KIP SIGN | 20ac EURO SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20af DRACHMA SIGN | 20ae TUGRIK SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20b1 PESO SIGN | 20b0 GERMAN PENNY SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20b3 AUSTRAL SIGN | 20b2 GUARANI SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20b5 CEDI SIGN | 20b4 HRYVNIA SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20b7 SPESMILO SIGN | 20b6 LIVRE TOURNOIS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20b9 INDIAN RUPEE SIGN | 20b8 TENGE SIGN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20bb (null) | 20ba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20bd (null) | 20bc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20bf (null) | 20be (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20c1 (null) | 20c0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20c3 (null) | 20c2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20c5 (null) | 20c4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20c7 (null) | 20c6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20c9 (null) | 20c8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20cb (null) | 20ca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20cd (null) | 20cc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20cf (null) | 20ce (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20d1 COMBINING RIGHT HARPOON ABOVE | 20d0 COMBINING LEFT HARPOON ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20d3 COMBINING SHORT VERTICAL LINE OVERLAY | 20d2 COMBINING LONG VERTICAL LINE OVERLAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20d5 COMBINING CLOCKWISE ARROW ABOVE | 20d4 COMBINING ANTICLOCKWISE ARROW ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20d7 COMBINING RIGHT ARROW ABOVE | 20d6 COMBINING LEFT ARROW ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20d9 COMBINING CLOCKWISE RING OVERLAY | 20d8 COMBINING RING OVERLAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20db COMBINING THREE DOTS ABOVE | 20da COMBINING ANTICLOCKWISE RING OVERLAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20dd COMBINING ENCLOSING CIRCLE | 20dc COMBINING FOUR DOTS ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20df COMBINING ENCLOSING DIAMOND | 20de COMBINING ENCLOSING SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20e1 COMBINING LEFT RIGHT ARROW ABOVE | 20e0 COMBINING ENCLOSING CIRCLE BACKSLASH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20e3 COMBINING ENCLOSING KEYCAP | 20e2 COMBINING ENCLOSING SCREEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20e5 COMBINING REVERSE SOLIDUS OVERLAY | 20e4 COMBINING ENCLOSING UPWARD POINTING TRI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20e7 COMBINING ANNUITY SYMBOL | 20e6 COMBINING DOUBLE VERTICAL STROKE OVERLA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20e9 COMBINING WIDE BRIDGE ABOVE | 20e8 COMBINING TRIPLE UNDERDOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20eb COMBINING LONG DOUBLE SOLIDUS OVERLAY | 20ea COMBINING LEFTWARDS ARROW OVERLAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20ed COMBINING LEFTWARDS HARPOON WITH BARB D | 20ec COMBINING RIGHTWARDS HARPOON WITH BARB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20ef COMBINING RIGHT ARROW BELOW | 20ee COMBINING LEFT ARROW BELOW */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 20f1 (null) | 20f0 COMBINING ASTERISK ABOVE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20f3 (null) | 20f2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20f5 (null) | 20f4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20f7 (null) | 20f6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20f9 (null) | 20f8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20fb (null) | 20fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20fd (null) | 20fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20ff (null) | 20fe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2101 ADDRESSED TO THE SUBJECT | 2100 ACCOUNT OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UPPER, /* 2103 DEGREE CELSIUS | 2102 DOUBLE-STRUCK CAPITAL C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2105 CARE OF | 2104 CENTRE LINE SYMBOL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_OTHER, /* 2107 EULER CONSTANT | 2106 CADA UNA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2109 DEGREE FAHRENHEIT | 2108 SCRUPLE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 210b SCRIPT CAPITAL H | 210a SCRIPT SMALL G */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 210d DOUBLE-STRUCK CAPITAL H | 210c BLACK-LETTER CAPITAL H */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 210f PLANCK CONSTANT OVER TWO PI | 210e PLANCK CONSTANT */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2111 BLACK-LETTER CAPITAL I | 2110 SCRIPT CAPITAL I */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2113 SCRIPT SMALL L | 2112 SCRIPT CAPITAL L */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_OTHER, /* 2115 DOUBLE-STRUCK CAPITAL N | 2114 L B BAR SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2117 SOUND RECORDING COPYRIGHT | 2116 NUMERO SIGN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_OTHER, /* 2119 DOUBLE-STRUCK CAPITAL P | 2118 SCRIPT CAPITAL P */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 211b SCRIPT CAPITAL R | 211a DOUBLE-STRUCK CAPITAL Q */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 211d DOUBLE-STRUCK CAPITAL R | 211c BLACK-LETTER CAPITAL R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 211f RESPONSE | 211e PRESCRIPTION TAKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2121 TELEPHONE SIGN | 2120 SERVICE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2123 VERSICLE | 2122 TRADE MARK SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UPPER, /* 2125 OUNCE SIGN | 2124 DOUBLE-STRUCK CAPITAL Z */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UPPER, /* 2127 INVERTED OHM SIGN | 2126 OHM SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UPPER, /* 2129 TURNED GREEK SMALL LETTER IOTA | 2128 BLACK-LETTER CAPITAL Z */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 212b ANGSTROM SIGN | 212a KELVIN SIGN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 212d BLACK-LETTER CAPITAL C | 212c SCRIPT CAPITAL B */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_OTHER, /* 212f SCRIPT SMALL E | 212e ESTIMATED SYMBOL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2131 SCRIPT CAPITAL F | 2130 SCRIPT CAPITAL E */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2133 SCRIPT CAPITAL M | 2132 TURNED CAPITAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_LOWER, /* 2135 ALEF SYMBOL | 2134 SCRIPT SMALL O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2137 GIMEL SYMBOL | 2136 BET SYMBOL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_ALPHA, /* 2139 INFORMATION SOURCE | 2138 DALET SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 213b FACSIMILE SIGN | 213a ROTATED CAPITAL Q */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 213d DOUBLE-STRUCK SMALL GAMMA | 213c DOUBLE-STRUCK SMALL PI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 213f DOUBLE-STRUCK CAPITAL PI | 213e DOUBLE-STRUCK CAPITAL GAMMA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2141 TURNED SANS-SERIF CAPITAL G | 2140 DOUBLE-STRUCK N-ARY SUMMATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2143 REVERSED SANS-SERIF CAPITAL L | 2142 TURNED SANS-SERIF CAPITAL L */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_OTHER, /* 2145 DOUBLE-STRUCK ITALIC CAPITAL D | 2144 TURNED SANS-SERIF CAPITAL Y */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2147 DOUBLE-STRUCK ITALIC SMALL E | 2146 DOUBLE-STRUCK ITALIC SMALL D */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2149 DOUBLE-STRUCK ITALIC SMALL J | 2148 DOUBLE-STRUCK ITALIC SMALL I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 214b TURNED AMPERSAND | 214a PROPERTY LINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 214d AKTIESELSKAB | 214c PER SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_LOWER, /* 214f SYMBOL FOR SAMARITAN SOURCE | 214e TURNED SMALL F */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2151 VULGAR FRACTION ONE NINTH | 2150 VULGAR FRACTION ONE SEVENTH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2153 VULGAR FRACTION ONE THIRD | 2152 VULGAR FRACTION ONE TENTH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2155 VULGAR FRACTION ONE FIFTH | 2154 VULGAR FRACTION TWO THIRDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2157 VULGAR FRACTION THREE FIFTHS | 2156 VULGAR FRACTION TWO FIFTHS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2159 VULGAR FRACTION ONE SIXTH | 2158 VULGAR FRACTION FOUR FIFTHS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 215b VULGAR FRACTION ONE EIGHTH | 215a VULGAR FRACTION FIVE SIXTHS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 215d VULGAR FRACTION FIVE EIGHTHS | 215c VULGAR FRACTION THREE EIGHTHS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 215f FRACTION NUMERATOR ONE | 215e VULGAR FRACTION SEVEN EIGHTHS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2161 ROMAN NUMERAL TWO | 2160 ROMAN NUMERAL ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2163 ROMAN NUMERAL FOUR | 2162 ROMAN NUMERAL THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2165 ROMAN NUMERAL SIX | 2164 ROMAN NUMERAL FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2167 ROMAN NUMERAL EIGHT | 2166 ROMAN NUMERAL SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2169 ROMAN NUMERAL TEN | 2168 ROMAN NUMERAL NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 216b ROMAN NUMERAL TWELVE | 216a ROMAN NUMERAL ELEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 216d ROMAN NUMERAL ONE HUNDRED | 216c ROMAN NUMERAL FIFTY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 216f ROMAN NUMERAL ONE THOUSAND | 216e ROMAN NUMERAL FIVE HUNDRED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2171 SMALL ROMAN NUMERAL TWO | 2170 SMALL ROMAN NUMERAL ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2173 SMALL ROMAN NUMERAL FOUR | 2172 SMALL ROMAN NUMERAL THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2175 SMALL ROMAN NUMERAL SIX | 2174 SMALL ROMAN NUMERAL FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2177 SMALL ROMAN NUMERAL EIGHT | 2176 SMALL ROMAN NUMERAL SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2179 SMALL ROMAN NUMERAL TEN | 2178 SMALL ROMAN NUMERAL NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 217b SMALL ROMAN NUMERAL TWELVE | 217a SMALL ROMAN NUMERAL ELEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 217d SMALL ROMAN NUMERAL ONE HUNDRED | 217c SMALL ROMAN NUMERAL FIFTY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 217f SMALL ROMAN NUMERAL ONE THOUSAND | 217e SMALL ROMAN NUMERAL FIVE HUNDRED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2181 ROMAN NUMERAL FIVE THOUSAND | 2180 ROMAN NUMERAL ONE THOUSAND C D */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_OTHER, /* 2183 ROMAN NUMERAL REVERSED ONE HUNDRED | 2182 ROMAN NUMERAL TEN THOUSAND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_LOWER, /* 2185 ROMAN NUMERAL SIX LATE FORM | 2184 LATIN SMALL LETTER REVERSED C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2187 ROMAN NUMERAL FIFTY THOUSAND | 2186 ROMAN NUMERAL FIFTY EARLY FORM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2189 VULGAR FRACTION ZERO THIRDS | 2188 ROMAN NUMERAL ONE HUNDRED THOUSAND */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 218b (null) | 218a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 218d (null) | 218c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 218f (null) | 218e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2191 UPWARDS ARROW | 2190 LEFTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2193 DOWNWARDS ARROW | 2192 RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2195 UP DOWN ARROW | 2194 LEFT RIGHT ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2197 NORTH EAST ARROW | 2196 NORTH WEST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2199 SOUTH WEST ARROW | 2198 SOUTH EAST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 219b RIGHTWARDS ARROW WITH STROKE | 219a LEFTWARDS ARROW WITH STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 219d RIGHTWARDS WAVE ARROW | 219c LEFTWARDS WAVE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 219f UPWARDS TWO HEADED ARROW | 219e LEFTWARDS TWO HEADED ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21a1 DOWNWARDS TWO HEADED ARROW | 21a0 RIGHTWARDS TWO HEADED ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21a3 RIGHTWARDS ARROW WITH TAIL | 21a2 LEFTWARDS ARROW WITH TAIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21a5 UPWARDS ARROW FROM BAR | 21a4 LEFTWARDS ARROW FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21a7 DOWNWARDS ARROW FROM BAR | 21a6 RIGHTWARDS ARROW FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21a9 LEFTWARDS ARROW WITH HOOK | 21a8 UP DOWN ARROW WITH BASE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21ab LEFTWARDS ARROW WITH LOOP | 21aa RIGHTWARDS ARROW WITH HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21ad LEFT RIGHT WAVE ARROW | 21ac RIGHTWARDS ARROW WITH LOOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21af DOWNWARDS ZIGZAG ARROW | 21ae LEFT RIGHT ARROW WITH STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21b1 UPWARDS ARROW WITH TIP RIGHTWARDS | 21b0 UPWARDS ARROW WITH TIP LEFTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21b3 DOWNWARDS ARROW WITH TIP RIGHTWARDS | 21b2 DOWNWARDS ARROW WITH TIP LEFTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21b5 DOWNWARDS ARROW WITH CORNER LEFTWARDS | 21b4 RIGHTWARDS ARROW WITH CORNER DOWNWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21b7 CLOCKWISE TOP SEMICIRCLE ARROW | 21b6 ANTICLOCKWISE TOP SEMICIRCLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21b9 LEFTWARDS ARROW TO BAR OVER RIGHTWARDS | 21b8 NORTH WEST ARROW TO LONG BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21bb CLOCKWISE OPEN CIRCLE ARROW | 21ba ANTICLOCKWISE OPEN CIRCLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21bd LEFTWARDS HARPOON WITH BARB DOWNWARDS | 21bc LEFTWARDS HARPOON WITH BARB UPWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21bf UPWARDS HARPOON WITH BARB LEFTWARDS | 21be UPWARDS HARPOON WITH BARB RIGHTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21c1 RIGHTWARDS HARPOON WITH BARB DOWNWARDS | 21c0 RIGHTWARDS HARPOON WITH BARB UPWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21c3 DOWNWARDS HARPOON WITH BARB LEFTWARDS | 21c2 DOWNWARDS HARPOON WITH BARB RIGHTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21c5 UPWARDS ARROW LEFTWARDS OF DOWNWARDS AR | 21c4 RIGHTWARDS ARROW OVER LEFTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21c7 LEFTWARDS PAIRED ARROWS | 21c6 LEFTWARDS ARROW OVER RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21c9 RIGHTWARDS PAIRED ARROWS | 21c8 UPWARDS PAIRED ARROWS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21cb LEFTWARDS HARPOON OVER RIGHTWARDS HARPO | 21ca DOWNWARDS PAIRED ARROWS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21cd LEFTWARDS DOUBLE ARROW WITH STROKE | 21cc RIGHTWARDS HARPOON OVER LEFTWARDS HARPO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21cf RIGHTWARDS DOUBLE ARROW WITH STROKE | 21ce LEFT RIGHT DOUBLE ARROW WITH STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21d1 UPWARDS DOUBLE ARROW | 21d0 LEFTWARDS DOUBLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21d3 DOWNWARDS DOUBLE ARROW | 21d2 RIGHTWARDS DOUBLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21d5 UP DOWN DOUBLE ARROW | 21d4 LEFT RIGHT DOUBLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21d7 NORTH EAST DOUBLE ARROW | 21d6 NORTH WEST DOUBLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21d9 SOUTH WEST DOUBLE ARROW | 21d8 SOUTH EAST DOUBLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21db RIGHTWARDS TRIPLE ARROW | 21da LEFTWARDS TRIPLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21dd RIGHTWARDS SQUIGGLE ARROW | 21dc LEFTWARDS SQUIGGLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21df DOWNWARDS ARROW WITH DOUBLE STROKE | 21de UPWARDS ARROW WITH DOUBLE STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21e1 UPWARDS DASHED ARROW | 21e0 LEFTWARDS DASHED ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21e3 DOWNWARDS DASHED ARROW | 21e2 RIGHTWARDS DASHED ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21e5 RIGHTWARDS ARROW TO BAR | 21e4 LEFTWARDS ARROW TO BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21e7 UPWARDS WHITE ARROW | 21e6 LEFTWARDS WHITE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21e9 DOWNWARDS WHITE ARROW | 21e8 RIGHTWARDS WHITE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21eb UPWARDS WHITE ARROW ON PEDESTAL | 21ea UPWARDS WHITE ARROW FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21ed UPWARDS WHITE ARROW ON PEDESTAL WITH VE | 21ec UPWARDS WHITE ARROW ON PEDESTAL WITH HO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21ef UPWARDS WHITE DOUBLE ARROW ON PEDESTAL | 21ee UPWARDS WHITE DOUBLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21f1 NORTH WEST ARROW TO CORNER | 21f0 RIGHTWARDS WHITE ARROW FROM WALL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21f3 UP DOWN WHITE ARROW | 21f2 SOUTH EAST ARROW TO CORNER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21f5 DOWNWARDS ARROW LEFTWARDS OF UPWARDS AR | 21f4 RIGHT ARROW WITH SMALL CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21f7 LEFTWARDS ARROW WITH VERTICAL STROKE | 21f6 THREE RIGHTWARDS ARROWS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21f9 LEFT RIGHT ARROW WITH VERTICAL STROKE | 21f8 RIGHTWARDS ARROW WITH VERTICAL STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21fb RIGHTWARDS ARROW WITH DOUBLE VERTICAL S | 21fa LEFTWARDS ARROW WITH DOUBLE VERTICAL ST */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21fd LEFTWARDS OPEN-HEADED ARROW | 21fc LEFT RIGHT ARROW WITH DOUBLE VERTICAL S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21ff LEFT RIGHT OPEN-HEADED ARROW | 21fe RIGHTWARDS OPEN-HEADED ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2201 COMPLEMENT | 2200 FOR ALL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2203 THERE EXISTS | 2202 PARTIAL DIFFERENTIAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2205 EMPTY SET | 2204 THERE DOES NOT EXIST */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2207 NABLA | 2206 INCREMENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2209 NOT AN ELEMENT OF | 2208 ELEMENT OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 220b CONTAINS AS MEMBER | 220a SMALL ELEMENT OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 220d SMALL CONTAINS AS MEMBER | 220c DOES NOT CONTAIN AS MEMBER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 220f N-ARY PRODUCT | 220e END OF PROOF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2211 N-ARY SUMMATION | 2210 N-ARY COPRODUCT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2213 MINUS-OR-PLUS SIGN | 2212 MINUS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2215 DIVISION SLASH | 2214 DOT PLUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2217 ASTERISK OPERATOR | 2216 SET MINUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2219 BULLET OPERATOR | 2218 RING OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 221b CUBE ROOT | 221a SQUARE ROOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 221d PROPORTIONAL TO | 221c FOURTH ROOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 221f RIGHT ANGLE | 221e INFINITY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2221 MEASURED ANGLE | 2220 ANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2223 DIVIDES | 2222 SPHERICAL ANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2225 PARALLEL TO | 2224 DOES NOT DIVIDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2227 LOGICAL AND | 2226 NOT PARALLEL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2229 INTERSECTION | 2228 LOGICAL OR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 222b INTEGRAL | 222a UNION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 222d TRIPLE INTEGRAL | 222c DOUBLE INTEGRAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 222f SURFACE INTEGRAL | 222e CONTOUR INTEGRAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2231 CLOCKWISE INTEGRAL | 2230 VOLUME INTEGRAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2233 ANTICLOCKWISE CONTOUR INTEGRAL | 2232 CLOCKWISE CONTOUR INTEGRAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2235 BECAUSE | 2234 THEREFORE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2237 PROPORTION | 2236 RATIO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2239 EXCESS | 2238 DOT MINUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 223b HOMOTHETIC | 223a GEOMETRIC PROPORTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 223d REVERSED TILDE | 223c TILDE OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 223f SINE WAVE | 223e INVERTED LAZY S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2241 NOT TILDE | 2240 WREATH PRODUCT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2243 ASYMPTOTICALLY EQUAL TO | 2242 MINUS TILDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2245 APPROXIMATELY EQUAL TO | 2244 NOT ASYMPTOTICALLY EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2247 NEITHER APPROXIMATELY NOR ACTUALLY EQUA | 2246 APPROXIMATELY BUT NOT ACTUALLY EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2249 NOT ALMOST EQUAL TO | 2248 ALMOST EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 224b TRIPLE TILDE | 224a ALMOST EQUAL OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 224d EQUIVALENT TO | 224c ALL EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 224f DIFFERENCE BETWEEN | 224e GEOMETRICALLY EQUIVALENT TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2251 GEOMETRICALLY EQUAL TO | 2250 APPROACHES THE LIMIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2253 IMAGE OF OR APPROXIMATELY EQUAL TO | 2252 APPROXIMATELY EQUAL TO OR THE IMAGE OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2255 EQUALS COLON | 2254 COLON EQUALS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2257 RING EQUAL TO | 2256 RING IN EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2259 ESTIMATES | 2258 CORRESPONDS TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 225b STAR EQUALS | 225a EQUIANGULAR TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 225d EQUAL TO BY DEFINITION | 225c DELTA EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 225f QUESTIONED EQUAL TO | 225e MEASURED BY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2261 IDENTICAL TO | 2260 NOT EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2263 STRICTLY EQUIVALENT TO | 2262 NOT IDENTICAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2265 GREATER-THAN OR EQUAL TO | 2264 LESS-THAN OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2267 GREATER-THAN OVER EQUAL TO | 2266 LESS-THAN OVER EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2269 GREATER-THAN BUT NOT EQUAL TO | 2268 LESS-THAN BUT NOT EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 226b MUCH GREATER-THAN | 226a MUCH LESS-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 226d NOT EQUIVALENT TO | 226c BETWEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 226f NOT GREATER-THAN | 226e NOT LESS-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2271 NEITHER GREATER-THAN NOR EQUAL TO | 2270 NEITHER LESS-THAN NOR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2273 GREATER-THAN OR EQUIVALENT TO | 2272 LESS-THAN OR EQUIVALENT TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2275 NEITHER GREATER-THAN NOR EQUIVALENT TO | 2274 NEITHER LESS-THAN NOR EQUIVALENT TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2277 GREATER-THAN OR LESS-THAN | 2276 LESS-THAN OR GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2279 NEITHER GREATER-THAN NOR LESS-THAN | 2278 NEITHER LESS-THAN NOR GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 227b SUCCEEDS | 227a PRECEDES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 227d SUCCEEDS OR EQUAL TO | 227c PRECEDES OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 227f SUCCEEDS OR EQUIVALENT TO | 227e PRECEDES OR EQUIVALENT TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2281 DOES NOT SUCCEED | 2280 DOES NOT PRECEDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2283 SUPERSET OF | 2282 SUBSET OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2285 NOT A SUPERSET OF | 2284 NOT A SUBSET OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2287 SUPERSET OF OR EQUAL TO | 2286 SUBSET OF OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2289 NEITHER A SUPERSET OF NOR EQUAL TO | 2288 NEITHER A SUBSET OF NOR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 228b SUPERSET OF WITH NOT EQUAL TO | 228a SUBSET OF WITH NOT EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 228d MULTISET MULTIPLICATION | 228c MULTISET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 228f SQUARE IMAGE OF | 228e MULTISET UNION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2291 SQUARE IMAGE OF OR EQUAL TO | 2290 SQUARE ORIGINAL OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2293 SQUARE CAP | 2292 SQUARE ORIGINAL OF OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2295 CIRCLED PLUS | 2294 SQUARE CUP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2297 CIRCLED TIMES | 2296 CIRCLED MINUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2299 CIRCLED DOT OPERATOR | 2298 CIRCLED DIVISION SLASH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 229b CIRCLED ASTERISK OPERATOR | 229a CIRCLED RING OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 229d CIRCLED DASH | 229c CIRCLED EQUALS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 229f SQUARED MINUS | 229e SQUARED PLUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22a1 SQUARED DOT OPERATOR | 22a0 SQUARED TIMES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22a3 LEFT TACK | 22a2 RIGHT TACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22a5 UP TACK | 22a4 DOWN TACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22a7 MODELS | 22a6 ASSERTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22a9 FORCES | 22a8 TRUE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22ab DOUBLE VERTICAL BAR DOUBLE RIGHT TURNST | 22aa TRIPLE VERTICAL BAR RIGHT TURNSTILE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22ad NOT TRUE | 22ac DOES NOT PROVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22af NEGATED DOUBLE VERTICAL BAR DOUBLE RIGH | 22ae DOES NOT FORCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22b1 SUCCEEDS UNDER RELATION | 22b0 PRECEDES UNDER RELATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22b3 CONTAINS AS NORMAL SUBGROUP | 22b2 NORMAL SUBGROUP OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22b5 CONTAINS AS NORMAL SUBGROUP OR EQUAL TO | 22b4 NORMAL SUBGROUP OF OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22b7 IMAGE OF | 22b6 ORIGINAL OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22b9 HERMITIAN CONJUGATE MATRIX | 22b8 MULTIMAP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22bb XOR | 22ba INTERCALATE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22bd NOR | 22bc NAND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22bf RIGHT TRIANGLE | 22be RIGHT ANGLE WITH ARC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22c1 N-ARY LOGICAL OR | 22c0 N-ARY LOGICAL AND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22c3 N-ARY UNION | 22c2 N-ARY INTERSECTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22c5 DOT OPERATOR | 22c4 DIAMOND OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22c7 DIVISION TIMES | 22c6 STAR OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22c9 LEFT NORMAL FACTOR SEMIDIRECT PRODUCT | 22c8 BOWTIE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22cb LEFT SEMIDIRECT PRODUCT | 22ca RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22cd REVERSED TILDE EQUALS | 22cc RIGHT SEMIDIRECT PRODUCT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22cf CURLY LOGICAL AND | 22ce CURLY LOGICAL OR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22d1 DOUBLE SUPERSET | 22d0 DOUBLE SUBSET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22d3 DOUBLE UNION | 22d2 DOUBLE INTERSECTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22d5 EQUAL AND PARALLEL TO | 22d4 PITCHFORK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22d7 GREATER-THAN WITH DOT | 22d6 LESS-THAN WITH DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22d9 VERY MUCH GREATER-THAN | 22d8 VERY MUCH LESS-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22db GREATER-THAN EQUAL TO OR LESS-THAN | 22da LESS-THAN EQUAL TO OR GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22dd EQUAL TO OR GREATER-THAN | 22dc EQUAL TO OR LESS-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22df EQUAL TO OR SUCCEEDS | 22de EQUAL TO OR PRECEDES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22e1 DOES NOT SUCCEED OR EQUAL | 22e0 DOES NOT PRECEDE OR EQUAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22e3 NOT SQUARE ORIGINAL OF OR EQUAL TO | 22e2 NOT SQUARE IMAGE OF OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22e5 SQUARE ORIGINAL OF OR NOT EQUAL TO | 22e4 SQUARE IMAGE OF OR NOT EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22e7 GREATER-THAN BUT NOT EQUIVALENT TO | 22e6 LESS-THAN BUT NOT EQUIVALENT TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22e9 SUCCEEDS BUT NOT EQUIVALENT TO | 22e8 PRECEDES BUT NOT EQUIVALENT TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22eb DOES NOT CONTAIN AS NORMAL SUBGROUP | 22ea NOT NORMAL SUBGROUP OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22ed DOES NOT CONTAIN AS NORMAL SUBGROUP OR | 22ec NOT NORMAL SUBGROUP OF OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22ef MIDLINE HORIZONTAL ELLIPSIS | 22ee VERTICAL ELLIPSIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22f1 DOWN RIGHT DIAGONAL ELLIPSIS | 22f0 UP RIGHT DIAGONAL ELLIPSIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22f3 ELEMENT OF WITH VERTICAL BAR AT END OF | 22f2 ELEMENT OF WITH LONG HORIZONTAL STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22f5 ELEMENT OF WITH DOT ABOVE | 22f4 SMALL ELEMENT OF WITH VERTICAL BAR AT E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22f7 SMALL ELEMENT OF WITH OVERBAR | 22f6 ELEMENT OF WITH OVERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22f9 ELEMENT OF WITH TWO HORIZONTAL STROKES | 22f8 ELEMENT OF WITH UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22fb CONTAINS WITH VERTICAL BAR AT END OF HO | 22fa CONTAINS WITH LONG HORIZONTAL STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22fd CONTAINS WITH OVERBAR | 22fc SMALL CONTAINS WITH VERTICAL BAR AT END */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22ff Z NOTATION BAG MEMBERSHIP | 22fe SMALL CONTAINS WITH OVERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2301 ELECTRIC ARROW | 2300 DIAMETER SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2303 UP ARROWHEAD | 2302 HOUSE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2305 PROJECTIVE | 2304 DOWN ARROWHEAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2307 WAVY LINE | 2306 PERSPECTIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2309 RIGHT CEILING | 2308 LEFT CEILING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 230b RIGHT FLOOR | 230a LEFT FLOOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 230d BOTTOM LEFT CROP | 230c BOTTOM RIGHT CROP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 230f TOP LEFT CROP | 230e TOP RIGHT CROP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2311 SQUARE LOZENGE | 2310 REVERSED NOT SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2313 SEGMENT | 2312 ARC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2315 TELEPHONE RECORDER | 2314 SECTOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2317 VIEWDATA SQUARE | 2316 POSITION INDICATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2319 TURNED NOT SIGN | 2318 PLACE OF INTEREST SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 231b HOURGLASS | 231a WATCH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 231d TOP RIGHT CORNER | 231c TOP LEFT CORNER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 231f BOTTOM RIGHT CORNER | 231e BOTTOM LEFT CORNER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2321 BOTTOM HALF INTEGRAL | 2320 TOP HALF INTEGRAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2323 SMILE | 2322 FROWN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2325 OPTION KEY | 2324 UP ARROWHEAD BETWEEN TWO HORIZONTAL BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2327 X IN A RECTANGLE BOX | 2326 ERASE TO THE RIGHT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 2329 LEFT-POINTING ANGLE BRACKET | 2328 KEYBOARD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 232b ERASE TO THE LEFT | 232a RIGHT-POINTING ANGLE BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 232d CYLINDRICITY | 232c BENZENE RING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 232f SYMMETRY | 232e ALL AROUND-PROFILE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2331 DIMENSION ORIGIN | 2330 TOTAL RUNOUT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2333 SLOPE | 2332 CONICAL TAPER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2335 COUNTERSINK | 2334 COUNTERBORE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2337 APL FUNCTIONAL SYMBOL SQUISH QUAD | 2336 APL FUNCTIONAL SYMBOL I-BEAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2339 APL FUNCTIONAL SYMBOL QUAD DIVIDE | 2338 APL FUNCTIONAL SYMBOL QUAD EQUAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 233b APL FUNCTIONAL SYMBOL QUAD JOT | 233a APL FUNCTIONAL SYMBOL QUAD DIAMOND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 233d APL FUNCTIONAL SYMBOL CIRCLE STILE | 233c APL FUNCTIONAL SYMBOL QUAD CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 233f APL FUNCTIONAL SYMBOL SLASH BAR | 233e APL FUNCTIONAL SYMBOL CIRCLE JOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2341 APL FUNCTIONAL SYMBOL QUAD SLASH | 2340 APL FUNCTIONAL SYMBOL BACKSLASH BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2343 APL FUNCTIONAL SYMBOL QUAD LESS-THAN | 2342 APL FUNCTIONAL SYMBOL QUAD BACKSLASH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2345 APL FUNCTIONAL SYMBOL LEFTWARDS VANE | 2344 APL FUNCTIONAL SYMBOL QUAD GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2347 APL FUNCTIONAL SYMBOL QUAD LEFTWARDS AR | 2346 APL FUNCTIONAL SYMBOL RIGHTWARDS VANE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2349 APL FUNCTIONAL SYMBOL CIRCLE BACKSLASH | 2348 APL FUNCTIONAL SYMBOL QUAD RIGHTWARDS A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 234b APL FUNCTIONAL SYMBOL DELTA STILE | 234a APL FUNCTIONAL SYMBOL DOWN TACK UNDERBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 234d APL FUNCTIONAL SYMBOL QUAD DELTA | 234c APL FUNCTIONAL SYMBOL QUAD DOWN CARET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 234f APL FUNCTIONAL SYMBOL UPWARDS VANE | 234e APL FUNCTIONAL SYMBOL DOWN TACK JOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2351 APL FUNCTIONAL SYMBOL UP TACK OVERBAR | 2350 APL FUNCTIONAL SYMBOL QUAD UPWARDS ARRO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2353 APL FUNCTIONAL SYMBOL QUAD UP CARET | 2352 APL FUNCTIONAL SYMBOL DEL STILE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2355 APL FUNCTIONAL SYMBOL UP TACK JOT | 2354 APL FUNCTIONAL SYMBOL QUAD DEL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2357 APL FUNCTIONAL SYMBOL QUAD DOWNWARDS AR | 2356 APL FUNCTIONAL SYMBOL DOWNWARDS VANE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2359 APL FUNCTIONAL SYMBOL DELTA UNDERBAR | 2358 APL FUNCTIONAL SYMBOL QUOTE UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 235b APL FUNCTIONAL SYMBOL JOT UNDERBAR | 235a APL FUNCTIONAL SYMBOL DIAMOND UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 235d APL FUNCTIONAL SYMBOL UP SHOE JOT | 235c APL FUNCTIONAL SYMBOL CIRCLE UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 235f APL FUNCTIONAL SYMBOL CIRCLE STAR | 235e APL FUNCTIONAL SYMBOL QUOTE QUAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2361 APL FUNCTIONAL SYMBOL UP TACK DIAERESIS | 2360 APL FUNCTIONAL SYMBOL QUAD COLON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2363 APL FUNCTIONAL SYMBOL STAR DIAERESIS | 2362 APL FUNCTIONAL SYMBOL DEL DIAERESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2365 APL FUNCTIONAL SYMBOL CIRCLE DIAERESIS | 2364 APL FUNCTIONAL SYMBOL JOT DIAERESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2367 APL FUNCTIONAL SYMBOL LEFT SHOE STILE | 2366 APL FUNCTIONAL SYMBOL DOWN SHOE STILE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2369 APL FUNCTIONAL SYMBOL GREATER-THAN DIAE | 2368 APL FUNCTIONAL SYMBOL TILDE DIAERESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 236b APL FUNCTIONAL SYMBOL DEL TILDE | 236a APL FUNCTIONAL SYMBOL COMMA BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 236d APL FUNCTIONAL SYMBOL STILE TILDE | 236c APL FUNCTIONAL SYMBOL ZILDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 236f APL FUNCTIONAL SYMBOL QUAD NOT EQUAL | 236e APL FUNCTIONAL SYMBOL SEMICOLON UNDERBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2371 APL FUNCTIONAL SYMBOL DOWN CARET TILDE | 2370 APL FUNCTIONAL SYMBOL QUAD QUESTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2373 APL FUNCTIONAL SYMBOL IOTA | 2372 APL FUNCTIONAL SYMBOL UP CARET TILDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2375 APL FUNCTIONAL SYMBOL OMEGA | 2374 APL FUNCTIONAL SYMBOL RHO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2377 APL FUNCTIONAL SYMBOL EPSILON UNDERBAR | 2376 APL FUNCTIONAL SYMBOL ALPHA UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2379 APL FUNCTIONAL SYMBOL OMEGA UNDERBAR | 2378 APL FUNCTIONAL SYMBOL IOTA UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 237b NOT CHECK MARK | 237a APL FUNCTIONAL SYMBOL ALPHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 237d SHOULDERED OPEN BOX | 237c RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 237f VERTICAL LINE WITH MIDDLE DOT | 237e BELL SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2381 CONTINUOUS UNDERLINE SYMBOL | 2380 INSERTION SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2383 EMPHASIS SYMBOL | 2382 DISCONTINUOUS UNDERLINE SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2385 WHITE SQUARE WITH CENTRE VERTICAL LINE | 2384 COMPOSITION SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2387 ALTERNATIVE KEY SYMBOL | 2386 ENTER SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2389 CIRCLED HORIZONTAL BAR WITH NOTCH | 2388 HELM SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 238b BROKEN CIRCLE WITH NORTHWEST ARROW | 238a CIRCLED TRIANGLE DOWN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 238d MONOSTABLE SYMBOL | 238c UNDO SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 238f OPEN-CIRCUIT-OUTPUT H-TYPE SYMBOL | 238e HYSTERESIS SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2391 PASSIVE-PULL-DOWN-OUTPUT SYMBOL | 2390 OPEN-CIRCUIT-OUTPUT L-TYPE SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2393 DIRECT CURRENT SYMBOL FORM TWO | 2392 PASSIVE-PULL-UP-OUTPUT SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2395 APL FUNCTIONAL SYMBOL QUAD | 2394 SOFTWARE-FUNCTION SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2397 PREVIOUS PAGE | 2396 DECIMAL SEPARATOR KEY SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2399 PRINT SCREEN SYMBOL | 2398 NEXT PAGE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 239b LEFT PARENTHESIS UPPER HOOK | 239a CLEAR SCREEN SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 239d LEFT PARENTHESIS LOWER HOOK | 239c LEFT PARENTHESIS EXTENSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 239f RIGHT PARENTHESIS EXTENSION | 239e RIGHT PARENTHESIS UPPER HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23a1 LEFT SQUARE BRACKET UPPER CORNER | 23a0 RIGHT PARENTHESIS LOWER HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23a3 LEFT SQUARE BRACKET LOWER CORNER | 23a2 LEFT SQUARE BRACKET EXTENSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23a5 RIGHT SQUARE BRACKET EXTENSION | 23a4 RIGHT SQUARE BRACKET UPPER CORNER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23a7 LEFT CURLY BRACKET UPPER HOOK | 23a6 RIGHT SQUARE BRACKET LOWER CORNER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23a9 LEFT CURLY BRACKET LOWER HOOK | 23a8 LEFT CURLY BRACKET MIDDLE PIECE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23ab RIGHT CURLY BRACKET UPPER HOOK | 23aa CURLY BRACKET EXTENSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23ad RIGHT CURLY BRACKET LOWER HOOK | 23ac RIGHT CURLY BRACKET MIDDLE PIECE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23af HORIZONTAL LINE EXTENSION | 23ae INTEGRAL EXTENSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23b1 UPPER RIGHT OR LOWER LEFT CURLY BRACKET | 23b0 UPPER LEFT OR LOWER RIGHT CURLY BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23b3 SUMMATION BOTTOM | 23b2 SUMMATION TOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23b5 BOTTOM SQUARE BRACKET | 23b4 TOP SQUARE BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23b7 RADICAL SYMBOL BOTTOM | 23b6 BOTTOM SQUARE BRACKET OVER TOP SQUARE B */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23b9 RIGHT VERTICAL BOX LINE | 23b8 LEFT VERTICAL BOX LINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23bb HORIZONTAL SCAN LINE-3 | 23ba HORIZONTAL SCAN LINE-1 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23bd HORIZONTAL SCAN LINE-9 | 23bc HORIZONTAL SCAN LINE-7 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23bf DENTISTRY SYMBOL LIGHT VERTICAL AND BOT | 23be DENTISTRY SYMBOL LIGHT VERTICAL AND TOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23c1 DENTISTRY SYMBOL LIGHT DOWN AND HORIZON | 23c0 DENTISTRY SYMBOL LIGHT VERTICAL WITH CI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23c3 DENTISTRY SYMBOL LIGHT VERTICAL WITH TR | 23c2 DENTISTRY SYMBOL LIGHT UP AND HORIZONTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23c5 DENTISTRY SYMBOL LIGHT UP AND HORIZONTA | 23c4 DENTISTRY SYMBOL LIGHT DOWN AND HORIZON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23c7 DENTISTRY SYMBOL LIGHT DOWN AND HORIZON | 23c6 DENTISTRY SYMBOL LIGHT VERTICAL AND WAV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23c9 DENTISTRY SYMBOL LIGHT DOWN AND HORIZON | 23c8 DENTISTRY SYMBOL LIGHT UP AND HORIZONTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23cb DENTISTRY SYMBOL LIGHT VERTICAL AND TOP | 23ca DENTISTRY SYMBOL LIGHT UP AND HORIZONTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23cd SQUARE FOOT | 23cc DENTISTRY SYMBOL LIGHT VERTICAL AND BOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23cf EJECT SYMBOL | 23ce RETURN SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23d1 METRICAL BREVE | 23d0 VERTICAL LINE EXTENSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23d3 METRICAL SHORT OVER LONG | 23d2 METRICAL LONG OVER SHORT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23d5 METRICAL TWO SHORTS OVER LONG | 23d4 METRICAL LONG OVER TWO SHORTS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23d7 METRICAL TRISEME | 23d6 METRICAL TWO SHORTS JOINED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23d9 METRICAL PENTASEME | 23d8 METRICAL TETRASEME */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23db FUSE | 23da EARTH GROUND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23dd BOTTOM PARENTHESIS | 23dc TOP PARENTHESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23df BOTTOM CURLY BRACKET | 23de TOP CURLY BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23e1 BOTTOM TORTOISE SHELL BRACKET | 23e0 TOP TORTOISE SHELL BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23e3 BENZENE RING WITH CIRCLE | 23e2 WHITE TRAPEZIUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23e5 FLATNESS | 23e4 STRAIGHTNESS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23e7 ELECTRICAL INTERSECTION | 23e6 AC CURRENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23e9 BLACK RIGHT-POINTING DOUBLE TRIANGLE | 23e8 DECIMAL EXPONENT SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23eb BLACK UP-POINTING DOUBLE TRIANGLE | 23ea BLACK LEFT-POINTING DOUBLE TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23ed BLACK RIGHT-POINTING DOUBLE TRIANGLE WI | 23ec BLACK DOWN-POINTING DOUBLE TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23ef BLACK RIGHT-POINTING TRIANGLE WITH DOUB | 23ee BLACK LEFT-POINTING DOUBLE TRIANGLE WIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23f1 STOPWATCH | 23f0 ALARM CLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23f3 HOURGLASS WITH FLOWING SAND | 23f2 TIMER CLOCK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 23f5 (null) | 23f4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 23f7 (null) | 23f6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 23f9 (null) | 23f8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 23fb (null) | 23fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 23fd (null) | 23fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 23ff (null) | 23fe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2401 SYMBOL FOR START OF HEADING | 2400 SYMBOL FOR NULL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2403 SYMBOL FOR END OF TEXT | 2402 SYMBOL FOR START OF TEXT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2405 SYMBOL FOR ENQUIRY | 2404 SYMBOL FOR END OF TRANSMISSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2407 SYMBOL FOR BELL | 2406 SYMBOL FOR ACKNOWLEDGE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2409 SYMBOL FOR HORIZONTAL TABULATION | 2408 SYMBOL FOR BACKSPACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 240b SYMBOL FOR VERTICAL TABULATION | 240a SYMBOL FOR LINE FEED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 240d SYMBOL FOR CARRIAGE RETURN | 240c SYMBOL FOR FORM FEED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 240f SYMBOL FOR SHIFT IN | 240e SYMBOL FOR SHIFT OUT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2411 SYMBOL FOR DEVICE CONTROL ONE | 2410 SYMBOL FOR DATA LINK ESCAPE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2413 SYMBOL FOR DEVICE CONTROL THREE | 2412 SYMBOL FOR DEVICE CONTROL TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2415 SYMBOL FOR NEGATIVE ACKNOWLEDGE | 2414 SYMBOL FOR DEVICE CONTROL FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2417 SYMBOL FOR END OF TRANSMISSION BLOCK | 2416 SYMBOL FOR SYNCHRONOUS IDLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2419 SYMBOL FOR END OF MEDIUM | 2418 SYMBOL FOR CANCEL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 241b SYMBOL FOR ESCAPE | 241a SYMBOL FOR SUBSTITUTE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 241d SYMBOL FOR GROUP SEPARATOR | 241c SYMBOL FOR FILE SEPARATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 241f SYMBOL FOR UNIT SEPARATOR | 241e SYMBOL FOR RECORD SEPARATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2421 SYMBOL FOR DELETE | 2420 SYMBOL FOR SPACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2423 OPEN BOX | 2422 BLANK SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2425 SYMBOL FOR DELETE FORM TWO | 2424 SYMBOL FOR NEWLINE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 2427 (null) | 2426 SYMBOL FOR SUBSTITUTE FORM TWO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2429 (null) | 2428 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 242b (null) | 242a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 242d (null) | 242c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 242f (null) | 242e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2431 (null) | 2430 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2433 (null) | 2432 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2435 (null) | 2434 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2437 (null) | 2436 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2439 (null) | 2438 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 243b (null) | 243a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 243d (null) | 243c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 243f (null) | 243e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2441 OCR CHAIR | 2440 OCR HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2443 OCR INVERTED FORK | 2442 OCR FORK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2445 OCR BOW TIE | 2444 OCR BELT BUCKLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2447 OCR AMOUNT OF CHECK | 2446 OCR BRANCH BANK IDENTIFICATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2449 OCR CUSTOMER ACCOUNT NUMBER | 2448 OCR DASH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 244b (null) | 244a OCR DOUBLE BACKSLASH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 244d (null) | 244c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 244f (null) | 244e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2451 (null) | 2450 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2453 (null) | 2452 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2455 (null) | 2454 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2457 (null) | 2456 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2459 (null) | 2458 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 245b (null) | 245a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 245d (null) | 245c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 245f (null) | 245e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2461 CIRCLED DIGIT TWO | 2460 CIRCLED DIGIT ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2463 CIRCLED DIGIT FOUR | 2462 CIRCLED DIGIT THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2465 CIRCLED DIGIT SIX | 2464 CIRCLED DIGIT FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2467 CIRCLED DIGIT EIGHT | 2466 CIRCLED DIGIT SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2469 CIRCLED NUMBER TEN | 2468 CIRCLED DIGIT NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 246b CIRCLED NUMBER TWELVE | 246a CIRCLED NUMBER ELEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 246d CIRCLED NUMBER FOURTEEN | 246c CIRCLED NUMBER THIRTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 246f CIRCLED NUMBER SIXTEEN | 246e CIRCLED NUMBER FIFTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2471 CIRCLED NUMBER EIGHTEEN | 2470 CIRCLED NUMBER SEVENTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2473 CIRCLED NUMBER TWENTY | 2472 CIRCLED NUMBER NINETEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2475 PARENTHESIZED DIGIT TWO | 2474 PARENTHESIZED DIGIT ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2477 PARENTHESIZED DIGIT FOUR | 2476 PARENTHESIZED DIGIT THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2479 PARENTHESIZED DIGIT SIX | 2478 PARENTHESIZED DIGIT FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 247b PARENTHESIZED DIGIT EIGHT | 247a PARENTHESIZED DIGIT SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 247d PARENTHESIZED NUMBER TEN | 247c PARENTHESIZED DIGIT NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 247f PARENTHESIZED NUMBER TWELVE | 247e PARENTHESIZED NUMBER ELEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2481 PARENTHESIZED NUMBER FOURTEEN | 2480 PARENTHESIZED NUMBER THIRTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2483 PARENTHESIZED NUMBER SIXTEEN | 2482 PARENTHESIZED NUMBER FIFTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2485 PARENTHESIZED NUMBER EIGHTEEN | 2484 PARENTHESIZED NUMBER SEVENTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2487 PARENTHESIZED NUMBER TWENTY | 2486 PARENTHESIZED NUMBER NINETEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2489 DIGIT TWO FULL STOP | 2488 DIGIT ONE FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 248b DIGIT FOUR FULL STOP | 248a DIGIT THREE FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 248d DIGIT SIX FULL STOP | 248c DIGIT FIVE FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 248f DIGIT EIGHT FULL STOP | 248e DIGIT SEVEN FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2491 NUMBER TEN FULL STOP | 2490 DIGIT NINE FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2493 NUMBER TWELVE FULL STOP | 2492 NUMBER ELEVEN FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2495 NUMBER FOURTEEN FULL STOP | 2494 NUMBER THIRTEEN FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2497 NUMBER SIXTEEN FULL STOP | 2496 NUMBER FIFTEEN FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2499 NUMBER EIGHTEEN FULL STOP | 2498 NUMBER SEVENTEEN FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 249b NUMBER TWENTY FULL STOP | 249a NUMBER NINETEEN FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 249d PARENTHESIZED LATIN SMALL LETTER B | 249c PARENTHESIZED LATIN SMALL LETTER A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 249f PARENTHESIZED LATIN SMALL LETTER D | 249e PARENTHESIZED LATIN SMALL LETTER C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24a1 PARENTHESIZED LATIN SMALL LETTER F | 24a0 PARENTHESIZED LATIN SMALL LETTER E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24a3 PARENTHESIZED LATIN SMALL LETTER H | 24a2 PARENTHESIZED LATIN SMALL LETTER G */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24a5 PARENTHESIZED LATIN SMALL LETTER J | 24a4 PARENTHESIZED LATIN SMALL LETTER I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24a7 PARENTHESIZED LATIN SMALL LETTER L | 24a6 PARENTHESIZED LATIN SMALL LETTER K */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24a9 PARENTHESIZED LATIN SMALL LETTER N | 24a8 PARENTHESIZED LATIN SMALL LETTER M */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24ab PARENTHESIZED LATIN SMALL LETTER P | 24aa PARENTHESIZED LATIN SMALL LETTER O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24ad PARENTHESIZED LATIN SMALL LETTER R | 24ac PARENTHESIZED LATIN SMALL LETTER Q */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24af PARENTHESIZED LATIN SMALL LETTER T | 24ae PARENTHESIZED LATIN SMALL LETTER S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24b1 PARENTHESIZED LATIN SMALL LETTER V | 24b0 PARENTHESIZED LATIN SMALL LETTER U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24b3 PARENTHESIZED LATIN SMALL LETTER X | 24b2 PARENTHESIZED LATIN SMALL LETTER W */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24b5 PARENTHESIZED LATIN SMALL LETTER Z | 24b4 PARENTHESIZED LATIN SMALL LETTER Y */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24b7 CIRCLED LATIN CAPITAL LETTER B | 24b6 CIRCLED LATIN CAPITAL LETTER A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24b9 CIRCLED LATIN CAPITAL LETTER D | 24b8 CIRCLED LATIN CAPITAL LETTER C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24bb CIRCLED LATIN CAPITAL LETTER F | 24ba CIRCLED LATIN CAPITAL LETTER E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24bd CIRCLED LATIN CAPITAL LETTER H | 24bc CIRCLED LATIN CAPITAL LETTER G */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24bf CIRCLED LATIN CAPITAL LETTER J | 24be CIRCLED LATIN CAPITAL LETTER I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24c1 CIRCLED LATIN CAPITAL LETTER L | 24c0 CIRCLED LATIN CAPITAL LETTER K */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24c3 CIRCLED LATIN CAPITAL LETTER N | 24c2 CIRCLED LATIN CAPITAL LETTER M */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24c5 CIRCLED LATIN CAPITAL LETTER P | 24c4 CIRCLED LATIN CAPITAL LETTER O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24c7 CIRCLED LATIN CAPITAL LETTER R | 24c6 CIRCLED LATIN CAPITAL LETTER Q */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24c9 CIRCLED LATIN CAPITAL LETTER T | 24c8 CIRCLED LATIN CAPITAL LETTER S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24cb CIRCLED LATIN CAPITAL LETTER V | 24ca CIRCLED LATIN CAPITAL LETTER U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24cd CIRCLED LATIN CAPITAL LETTER X | 24cc CIRCLED LATIN CAPITAL LETTER W */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24cf CIRCLED LATIN CAPITAL LETTER Z | 24ce CIRCLED LATIN CAPITAL LETTER Y */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24d1 CIRCLED LATIN SMALL LETTER B | 24d0 CIRCLED LATIN SMALL LETTER A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24d3 CIRCLED LATIN SMALL LETTER D | 24d2 CIRCLED LATIN SMALL LETTER C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24d5 CIRCLED LATIN SMALL LETTER F | 24d4 CIRCLED LATIN SMALL LETTER E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24d7 CIRCLED LATIN SMALL LETTER H | 24d6 CIRCLED LATIN SMALL LETTER G */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24d9 CIRCLED LATIN SMALL LETTER J | 24d8 CIRCLED LATIN SMALL LETTER I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24db CIRCLED LATIN SMALL LETTER L | 24da CIRCLED LATIN SMALL LETTER K */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24dd CIRCLED LATIN SMALL LETTER N | 24dc CIRCLED LATIN SMALL LETTER M */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24df CIRCLED LATIN SMALL LETTER P | 24de CIRCLED LATIN SMALL LETTER O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24e1 CIRCLED LATIN SMALL LETTER R | 24e0 CIRCLED LATIN SMALL LETTER Q */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24e3 CIRCLED LATIN SMALL LETTER T | 24e2 CIRCLED LATIN SMALL LETTER S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24e5 CIRCLED LATIN SMALL LETTER V | 24e4 CIRCLED LATIN SMALL LETTER U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24e7 CIRCLED LATIN SMALL LETTER X | 24e6 CIRCLED LATIN SMALL LETTER W */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24e9 CIRCLED LATIN SMALL LETTER Z | 24e8 CIRCLED LATIN SMALL LETTER Y */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24eb NEGATIVE CIRCLED NUMBER ELEVEN | 24ea CIRCLED DIGIT ZERO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24ed NEGATIVE CIRCLED NUMBER THIRTEEN | 24ec NEGATIVE CIRCLED NUMBER TWELVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24ef NEGATIVE CIRCLED NUMBER FIFTEEN | 24ee NEGATIVE CIRCLED NUMBER FOURTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24f1 NEGATIVE CIRCLED NUMBER SEVENTEEN | 24f0 NEGATIVE CIRCLED NUMBER SIXTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24f3 NEGATIVE CIRCLED NUMBER NINETEEN | 24f2 NEGATIVE CIRCLED NUMBER EIGHTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24f5 DOUBLE CIRCLED DIGIT ONE | 24f4 NEGATIVE CIRCLED NUMBER TWENTY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24f7 DOUBLE CIRCLED DIGIT THREE | 24f6 DOUBLE CIRCLED DIGIT TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24f9 DOUBLE CIRCLED DIGIT FIVE | 24f8 DOUBLE CIRCLED DIGIT FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24fb DOUBLE CIRCLED DIGIT SEVEN | 24fa DOUBLE CIRCLED DIGIT SIX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24fd DOUBLE CIRCLED DIGIT NINE | 24fc DOUBLE CIRCLED DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24ff NEGATIVE CIRCLED DIGIT ZERO | 24fe DOUBLE CIRCLED NUMBER TEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2501 BOX DRAWINGS HEAVY HORIZONTAL | 2500 BOX DRAWINGS LIGHT HORIZONTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2503 BOX DRAWINGS HEAVY VERTICAL | 2502 BOX DRAWINGS LIGHT VERTICAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2505 BOX DRAWINGS HEAVY TRIPLE DASH HORIZONT | 2504 BOX DRAWINGS LIGHT TRIPLE DASH HORIZONT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2507 BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL | 2506 BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2509 BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZ | 2508 BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 250b BOX DRAWINGS HEAVY QUADRUPLE DASH VERTI | 250a BOX DRAWINGS LIGHT QUADRUPLE DASH VERTI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 250d BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY | 250c BOX DRAWINGS LIGHT DOWN AND RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 250f BOX DRAWINGS HEAVY DOWN AND RIGHT | 250e BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2511 BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY | 2510 BOX DRAWINGS LIGHT DOWN AND LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2513 BOX DRAWINGS HEAVY DOWN AND LEFT | 2512 BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2515 BOX DRAWINGS UP LIGHT AND RIGHT HEAVY | 2514 BOX DRAWINGS LIGHT UP AND RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2517 BOX DRAWINGS HEAVY UP AND RIGHT | 2516 BOX DRAWINGS UP HEAVY AND RIGHT LIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2519 BOX DRAWINGS UP LIGHT AND LEFT HEAVY | 2518 BOX DRAWINGS LIGHT UP AND LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 251b BOX DRAWINGS HEAVY UP AND LEFT | 251a BOX DRAWINGS UP HEAVY AND LEFT LIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 251d BOX DRAWINGS VERTICAL LIGHT AND RIGHT H | 251c BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 251f BOX DRAWINGS DOWN HEAVY AND RIGHT UP LI | 251e BOX DRAWINGS UP HEAVY AND RIGHT DOWN LI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2521 BOX DRAWINGS DOWN LIGHT AND RIGHT UP HE | 2520 BOX DRAWINGS VERTICAL HEAVY AND RIGHT L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2523 BOX DRAWINGS HEAVY VERTICAL AND RIGHT | 2522 BOX DRAWINGS UP LIGHT AND RIGHT DOWN HE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2525 BOX DRAWINGS VERTICAL LIGHT AND LEFT HE | 2524 BOX DRAWINGS LIGHT VERTICAL AND LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2527 BOX DRAWINGS DOWN HEAVY AND LEFT UP LIG | 2526 BOX DRAWINGS UP HEAVY AND LEFT DOWN LIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2529 BOX DRAWINGS DOWN LIGHT AND LEFT UP HEA | 2528 BOX DRAWINGS VERTICAL HEAVY AND LEFT LI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 252b BOX DRAWINGS HEAVY VERTICAL AND LEFT | 252a BOX DRAWINGS UP LIGHT AND LEFT DOWN HEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 252d BOX DRAWINGS LEFT HEAVY AND RIGHT DOWN | 252c BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 252f BOX DRAWINGS DOWN LIGHT AND HORIZONTAL | 252e BOX DRAWINGS RIGHT HEAVY AND LEFT DOWN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2531 BOX DRAWINGS RIGHT LIGHT AND LEFT DOWN | 2530 BOX DRAWINGS DOWN HEAVY AND HORIZONTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2533 BOX DRAWINGS HEAVY DOWN AND HORIZONTAL | 2532 BOX DRAWINGS LEFT LIGHT AND RIGHT DOWN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2535 BOX DRAWINGS LEFT HEAVY AND RIGHT UP LI | 2534 BOX DRAWINGS LIGHT UP AND HORIZONTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2537 BOX DRAWINGS UP LIGHT AND HORIZONTAL HE | 2536 BOX DRAWINGS RIGHT HEAVY AND LEFT UP LI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2539 BOX DRAWINGS RIGHT LIGHT AND LEFT UP HE | 2538 BOX DRAWINGS UP HEAVY AND HORIZONTAL LI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 253b BOX DRAWINGS HEAVY UP AND HORIZONTAL | 253a BOX DRAWINGS LEFT LIGHT AND RIGHT UP HE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 253d BOX DRAWINGS LEFT HEAVY AND RIGHT VERTI | 253c BOX DRAWINGS LIGHT VERTICAL AND HORIZON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 253f BOX DRAWINGS VERTICAL LIGHT AND HORIZON | 253e BOX DRAWINGS RIGHT HEAVY AND LEFT VERTI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2541 BOX DRAWINGS DOWN HEAVY AND UP HORIZONT | 2540 BOX DRAWINGS UP HEAVY AND DOWN HORIZONT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2543 BOX DRAWINGS LEFT UP HEAVY AND RIGHT DO | 2542 BOX DRAWINGS VERTICAL HEAVY AND HORIZON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2545 BOX DRAWINGS LEFT DOWN HEAVY AND RIGHT | 2544 BOX DRAWINGS RIGHT UP HEAVY AND LEFT DO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2547 BOX DRAWINGS DOWN LIGHT AND UP HORIZONT | 2546 BOX DRAWINGS RIGHT DOWN HEAVY AND LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2549 BOX DRAWINGS RIGHT LIGHT AND LEFT VERTI | 2548 BOX DRAWINGS UP LIGHT AND DOWN HORIZONT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 254b BOX DRAWINGS HEAVY VERTICAL AND HORIZON | 254a BOX DRAWINGS LEFT LIGHT AND RIGHT VERTI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 254d BOX DRAWINGS HEAVY DOUBLE DASH HORIZONT | 254c BOX DRAWINGS LIGHT DOUBLE DASH HORIZONT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 254f BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL | 254e BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2551 BOX DRAWINGS DOUBLE VERTICAL | 2550 BOX DRAWINGS DOUBLE HORIZONTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2553 BOX DRAWINGS DOWN DOUBLE AND RIGHT SING | 2552 BOX DRAWINGS DOWN SINGLE AND RIGHT DOUB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2555 BOX DRAWINGS DOWN SINGLE AND LEFT DOUBL | 2554 BOX DRAWINGS DOUBLE DOWN AND RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2557 BOX DRAWINGS DOUBLE DOWN AND LEFT | 2556 BOX DRAWINGS DOWN DOUBLE AND LEFT SINGL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2559 BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE | 2558 BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 255b BOX DRAWINGS UP SINGLE AND LEFT DOUBLE | 255a BOX DRAWINGS DOUBLE UP AND RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 255d BOX DRAWINGS DOUBLE UP AND LEFT | 255c BOX DRAWINGS UP DOUBLE AND LEFT SINGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 255f BOX DRAWINGS VERTICAL DOUBLE AND RIGHT | 255e BOX DRAWINGS VERTICAL SINGLE AND RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2561 BOX DRAWINGS VERTICAL SINGLE AND LEFT D | 2560 BOX DRAWINGS DOUBLE VERTICAL AND RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2563 BOX DRAWINGS DOUBLE VERTICAL AND LEFT | 2562 BOX DRAWINGS VERTICAL DOUBLE AND LEFT S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2565 BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL | 2564 BOX DRAWINGS DOWN SINGLE AND HORIZONTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2567 BOX DRAWINGS UP SINGLE AND HORIZONTAL D | 2566 BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2569 BOX DRAWINGS DOUBLE UP AND HORIZONTAL | 2568 BOX DRAWINGS UP DOUBLE AND HORIZONTAL S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 256b BOX DRAWINGS VERTICAL DOUBLE AND HORIZO | 256a BOX DRAWINGS VERTICAL SINGLE AND HORIZO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 256d BOX DRAWINGS LIGHT ARC DOWN AND RIGHT | 256c BOX DRAWINGS DOUBLE VERTICAL AND HORIZO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 256f BOX DRAWINGS LIGHT ARC UP AND LEFT | 256e BOX DRAWINGS LIGHT ARC DOWN AND LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2571 BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT | 2570 BOX DRAWINGS LIGHT ARC UP AND RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2573 BOX DRAWINGS LIGHT DIAGONAL CROSS | 2572 BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2575 BOX DRAWINGS LIGHT UP | 2574 BOX DRAWINGS LIGHT LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2577 BOX DRAWINGS LIGHT DOWN | 2576 BOX DRAWINGS LIGHT RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2579 BOX DRAWINGS HEAVY UP | 2578 BOX DRAWINGS HEAVY LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 257b BOX DRAWINGS HEAVY DOWN | 257a BOX DRAWINGS HEAVY RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 257d BOX DRAWINGS LIGHT UP AND HEAVY DOWN | 257c BOX DRAWINGS LIGHT LEFT AND HEAVY RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 257f BOX DRAWINGS HEAVY UP AND LIGHT DOWN | 257e BOX DRAWINGS HEAVY LEFT AND LIGHT RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2581 LOWER ONE EIGHTH BLOCK | 2580 UPPER HALF BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2583 LOWER THREE EIGHTHS BLOCK | 2582 LOWER ONE QUARTER BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2585 LOWER FIVE EIGHTHS BLOCK | 2584 LOWER HALF BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2587 LOWER SEVEN EIGHTHS BLOCK | 2586 LOWER THREE QUARTERS BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2589 LEFT SEVEN EIGHTHS BLOCK | 2588 FULL BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 258b LEFT FIVE EIGHTHS BLOCK | 258a LEFT THREE QUARTERS BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 258d LEFT THREE EIGHTHS BLOCK | 258c LEFT HALF BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 258f LEFT ONE EIGHTH BLOCK | 258e LEFT ONE QUARTER BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2591 LIGHT SHADE | 2590 RIGHT HALF BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2593 DARK SHADE | 2592 MEDIUM SHADE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2595 RIGHT ONE EIGHTH BLOCK | 2594 UPPER ONE EIGHTH BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2597 QUADRANT LOWER RIGHT | 2596 QUADRANT LOWER LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2599 QUADRANT UPPER LEFT AND LOWER LEFT AND | 2598 QUADRANT UPPER LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 259b QUADRANT UPPER LEFT AND UPPER RIGHT AND | 259a QUADRANT UPPER LEFT AND LOWER RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 259d QUADRANT UPPER RIGHT | 259c QUADRANT UPPER LEFT AND UPPER RIGHT AND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 259f QUADRANT UPPER RIGHT AND LOWER LEFT AND | 259e QUADRANT UPPER RIGHT AND LOWER LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25a1 WHITE SQUARE | 25a0 BLACK SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25a3 WHITE SQUARE CONTAINING BLACK SMALL SQU | 25a2 WHITE SQUARE WITH ROUNDED CORNERS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25a5 SQUARE WITH VERTICAL FILL | 25a4 SQUARE WITH HORIZONTAL FILL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25a7 SQUARE WITH UPPER LEFT TO LOWER RIGHT F | 25a6 SQUARE WITH ORTHOGONAL CROSSHATCH FILL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25a9 SQUARE WITH DIAGONAL CROSSHATCH FILL | 25a8 SQUARE WITH UPPER RIGHT TO LOWER LEFT F */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25ab WHITE SMALL SQUARE | 25aa BLACK SMALL SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25ad WHITE RECTANGLE | 25ac BLACK RECTANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25af WHITE VERTICAL RECTANGLE | 25ae BLACK VERTICAL RECTANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25b1 WHITE PARALLELOGRAM | 25b0 BLACK PARALLELOGRAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25b3 WHITE UP-POINTING TRIANGLE | 25b2 BLACK UP-POINTING TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25b5 WHITE UP-POINTING SMALL TRIANGLE | 25b4 BLACK UP-POINTING SMALL TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25b7 WHITE RIGHT-POINTING TRIANGLE | 25b6 BLACK RIGHT-POINTING TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25b9 WHITE RIGHT-POINTING SMALL TRIANGLE | 25b8 BLACK RIGHT-POINTING SMALL TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25bb WHITE RIGHT-POINTING POINTER | 25ba BLACK RIGHT-POINTING POINTER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25bd WHITE DOWN-POINTING TRIANGLE | 25bc BLACK DOWN-POINTING TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25bf WHITE DOWN-POINTING SMALL TRIANGLE | 25be BLACK DOWN-POINTING SMALL TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25c1 WHITE LEFT-POINTING TRIANGLE | 25c0 BLACK LEFT-POINTING TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25c3 WHITE LEFT-POINTING SMALL TRIANGLE | 25c2 BLACK LEFT-POINTING SMALL TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25c5 WHITE LEFT-POINTING POINTER | 25c4 BLACK LEFT-POINTING POINTER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25c7 WHITE DIAMOND | 25c6 BLACK DIAMOND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25c9 FISHEYE | 25c8 WHITE DIAMOND CONTAINING BLACK SMALL DI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25cb WHITE CIRCLE | 25ca LOZENGE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25cd CIRCLE WITH VERTICAL FILL | 25cc DOTTED CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25cf BLACK CIRCLE | 25ce BULLSEYE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25d1 CIRCLE WITH RIGHT HALF BLACK | 25d0 CIRCLE WITH LEFT HALF BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25d3 CIRCLE WITH UPPER HALF BLACK | 25d2 CIRCLE WITH LOWER HALF BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25d5 CIRCLE WITH ALL BUT UPPER LEFT QUADRANT | 25d4 CIRCLE WITH UPPER RIGHT QUADRANT BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25d7 RIGHT HALF BLACK CIRCLE | 25d6 LEFT HALF BLACK CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25d9 INVERSE WHITE CIRCLE | 25d8 INVERSE BULLET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25db LOWER HALF INVERSE WHITE CIRCLE | 25da UPPER HALF INVERSE WHITE CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25dd UPPER RIGHT QUADRANT CIRCULAR ARC | 25dc UPPER LEFT QUADRANT CIRCULAR ARC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25df LOWER LEFT QUADRANT CIRCULAR ARC | 25de LOWER RIGHT QUADRANT CIRCULAR ARC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25e1 LOWER HALF CIRCLE | 25e0 UPPER HALF CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25e3 BLACK LOWER LEFT TRIANGLE | 25e2 BLACK LOWER RIGHT TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25e5 BLACK UPPER RIGHT TRIANGLE | 25e4 BLACK UPPER LEFT TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25e7 SQUARE WITH LEFT HALF BLACK | 25e6 WHITE BULLET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25e9 SQUARE WITH UPPER LEFT DIAGONAL HALF BL | 25e8 SQUARE WITH RIGHT HALF BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25eb WHITE SQUARE WITH VERTICAL BISECTING LI | 25ea SQUARE WITH LOWER RIGHT DIAGONAL HALF B */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25ed UP-POINTING TRIANGLE WITH LEFT HALF BLA | 25ec WHITE UP-POINTING TRIANGLE WITH DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25ef LARGE CIRCLE | 25ee UP-POINTING TRIANGLE WITH RIGHT HALF BL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25f1 WHITE SQUARE WITH LOWER LEFT QUADRANT | 25f0 WHITE SQUARE WITH UPPER LEFT QUADRANT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25f3 WHITE SQUARE WITH UPPER RIGHT QUADRANT | 25f2 WHITE SQUARE WITH LOWER RIGHT QUADRANT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25f5 WHITE CIRCLE WITH LOWER LEFT QUADRANT | 25f4 WHITE CIRCLE WITH UPPER LEFT QUADRANT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25f7 WHITE CIRCLE WITH UPPER RIGHT QUADRANT | 25f6 WHITE CIRCLE WITH LOWER RIGHT QUADRANT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25f9 UPPER RIGHT TRIANGLE | 25f8 UPPER LEFT TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25fb WHITE MEDIUM SQUARE | 25fa LOWER LEFT TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25fd WHITE MEDIUM SMALL SQUARE | 25fc BLACK MEDIUM SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25ff LOWER RIGHT TRIANGLE | 25fe BLACK MEDIUM SMALL SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2601 CLOUD | 2600 BLACK SUN WITH RAYS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2603 SNOWMAN | 2602 UMBRELLA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2605 BLACK STAR | 2604 COMET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2607 LIGHTNING | 2606 WHITE STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2609 SUN | 2608 THUNDERSTORM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 260b DESCENDING NODE | 260a ASCENDING NODE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 260d OPPOSITION | 260c CONJUNCTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 260f WHITE TELEPHONE | 260e BLACK TELEPHONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2611 BALLOT BOX WITH CHECK | 2610 BALLOT BOX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2613 SALTIRE | 2612 BALLOT BOX WITH X */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2615 HOT BEVERAGE | 2614 UMBRELLA WITH RAIN DROPS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2617 BLACK SHOGI PIECE | 2616 WHITE SHOGI PIECE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2619 REVERSED ROTATED FLORAL HEART BULLET | 2618 SHAMROCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 261b BLACK RIGHT POINTING INDEX | 261a BLACK LEFT POINTING INDEX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 261d WHITE UP POINTING INDEX | 261c WHITE LEFT POINTING INDEX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 261f WHITE DOWN POINTING INDEX | 261e WHITE RIGHT POINTING INDEX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2621 CAUTION SIGN | 2620 SKULL AND CROSSBONES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2623 BIOHAZARD SIGN | 2622 RADIOACTIVE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2625 ANKH | 2624 CADUCEUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2627 CHI RHO | 2626 ORTHODOX CROSS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2629 CROSS OF JERUSALEM | 2628 CROSS OF LORRAINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 262b FARSI SYMBOL | 262a STAR AND CRESCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 262d HAMMER AND SICKLE | 262c ADI SHAKTI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 262f YIN YANG | 262e PEACE SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2631 TRIGRAM FOR LAKE | 2630 TRIGRAM FOR HEAVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2633 TRIGRAM FOR THUNDER | 2632 TRIGRAM FOR FIRE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2635 TRIGRAM FOR WATER | 2634 TRIGRAM FOR WIND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2637 TRIGRAM FOR EARTH | 2636 TRIGRAM FOR MOUNTAIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2639 WHITE FROWNING FACE | 2638 WHEEL OF DHARMA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 263b BLACK SMILING FACE | 263a WHITE SMILING FACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 263d FIRST QUARTER MOON | 263c WHITE SUN WITH RAYS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 263f MERCURY | 263e LAST QUARTER MOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2641 EARTH | 2640 FEMALE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2643 JUPITER | 2642 MALE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2645 URANUS | 2644 SATURN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2647 PLUTO | 2646 NEPTUNE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2649 TAURUS | 2648 ARIES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 264b CANCER | 264a GEMINI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 264d VIRGO | 264c LEO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 264f SCORPIUS | 264e LIBRA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2651 CAPRICORN | 2650 SAGITTARIUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2653 PISCES | 2652 AQUARIUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2655 WHITE CHESS QUEEN | 2654 WHITE CHESS KING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2657 WHITE CHESS BISHOP | 2656 WHITE CHESS ROOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2659 WHITE CHESS PAWN | 2658 WHITE CHESS KNIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 265b BLACK CHESS QUEEN | 265a BLACK CHESS KING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 265d BLACK CHESS BISHOP | 265c BLACK CHESS ROOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 265f BLACK CHESS PAWN | 265e BLACK CHESS KNIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2661 WHITE HEART SUIT | 2660 BLACK SPADE SUIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2663 BLACK CLUB SUIT | 2662 WHITE DIAMOND SUIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2665 BLACK HEART SUIT | 2664 WHITE SPADE SUIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2667 WHITE CLUB SUIT | 2666 BLACK DIAMOND SUIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2669 QUARTER NOTE | 2668 HOT SPRINGS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 266b BEAMED EIGHTH NOTES | 266a EIGHTH NOTE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 266d MUSIC FLAT SIGN | 266c BEAMED SIXTEENTH NOTES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 266f MUSIC SHARP SIGN | 266e MUSIC NATURAL SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2671 EAST SYRIAC CROSS | 2670 WEST SYRIAC CROSS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2673 RECYCLING SYMBOL FOR TYPE-1 PLASTICS | 2672 UNIVERSAL RECYCLING SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2675 RECYCLING SYMBOL FOR TYPE-3 PLASTICS | 2674 RECYCLING SYMBOL FOR TYPE-2 PLASTICS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2677 RECYCLING SYMBOL FOR TYPE-5 PLASTICS | 2676 RECYCLING SYMBOL FOR TYPE-4 PLASTICS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2679 RECYCLING SYMBOL FOR TYPE-7 PLASTICS | 2678 RECYCLING SYMBOL FOR TYPE-6 PLASTICS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 267b BLACK UNIVERSAL RECYCLING SYMBOL | 267a RECYCLING SYMBOL FOR GENERIC MATERIALS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 267d PARTIALLY-RECYCLED PAPER SYMBOL | 267c RECYCLED PAPER SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 267f WHEELCHAIR SYMBOL | 267e PERMANENT PAPER SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2681 DIE FACE-2 | 2680 DIE FACE-1 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2683 DIE FACE-4 | 2682 DIE FACE-3 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2685 DIE FACE-6 | 2684 DIE FACE-5 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2687 WHITE CIRCLE WITH TWO DOTS | 2686 WHITE CIRCLE WITH DOT RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2689 BLACK CIRCLE WITH TWO WHITE DOTS | 2688 BLACK CIRCLE WITH WHITE DOT RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 268b MONOGRAM FOR YIN | 268a MONOGRAM FOR YANG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 268d DIGRAM FOR LESSER YIN | 268c DIGRAM FOR GREATER YANG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 268f DIGRAM FOR GREATER YIN | 268e DIGRAM FOR LESSER YANG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2691 BLACK FLAG | 2690 WHITE FLAG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2693 ANCHOR | 2692 HAMMER AND PICK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2695 STAFF OF AESCULAPIUS | 2694 CROSSED SWORDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2697 ALEMBIC | 2696 SCALES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2699 GEAR | 2698 FLOWER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 269b ATOM SYMBOL | 269a STAFF OF HERMES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 269d OUTLINED WHITE STAR | 269c FLEUR-DE-LIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 269f THREE LINES CONVERGING LEFT | 269e THREE LINES CONVERGING RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26a1 HIGH VOLTAGE SIGN | 26a0 WARNING SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26a3 DOUBLED MALE SIGN | 26a2 DOUBLED FEMALE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26a5 MALE AND FEMALE SIGN | 26a4 INTERLOCKED FEMALE AND MALE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26a7 MALE WITH STROKE AND MALE AND FEMALE SI | 26a6 MALE WITH STROKE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26a9 HORIZONTAL MALE WITH STROKE SIGN | 26a8 VERTICAL MALE WITH STROKE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26ab MEDIUM BLACK CIRCLE | 26aa MEDIUM WHITE CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26ad MARRIAGE SYMBOL | 26ac MEDIUM SMALL WHITE CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26af UNMARRIED PARTNERSHIP SYMBOL | 26ae DIVORCE SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26b1 FUNERAL URN | 26b0 COFFIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26b3 CERES | 26b2 NEUTER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26b5 JUNO | 26b4 PALLAS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26b7 CHIRON | 26b6 VESTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26b9 SEXTILE | 26b8 BLACK MOON LILITH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26bb QUINCUNX | 26ba SEMISEXTILE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26bd SOCCER BALL | 26bc SESQUIQUADRATE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26bf SQUARED KEY | 26be BASEBALL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26c1 WHITE DRAUGHTS KING | 26c0 WHITE DRAUGHTS MAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26c3 BLACK DRAUGHTS KING | 26c2 BLACK DRAUGHTS MAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26c5 SUN BEHIND CLOUD | 26c4 SNOWMAN WITHOUT SNOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26c7 BLACK SNOWMAN | 26c6 RAIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26c9 TURNED WHITE SHOGI PIECE | 26c8 THUNDER CLOUD AND RAIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26cb WHITE DIAMOND IN SQUARE | 26ca TURNED BLACK SHOGI PIECE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26cd DISABLED CAR | 26cc CROSSING LANES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26cf PICK | 26ce OPHIUCHUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26d1 HELMET WITH WHITE CROSS | 26d0 CAR SLIDING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26d3 CHAINS | 26d2 CIRCLED CROSSING LANES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26d5 ALTERNATE ONE-WAY LEFT WAY TRAFFIC | 26d4 NO ENTRY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26d7 WHITE TWO-WAY LEFT WAY TRAFFIC | 26d6 BLACK TWO-WAY LEFT WAY TRAFFIC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26d9 WHITE LEFT LANE MERGE | 26d8 BLACK LEFT LANE MERGE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26db HEAVY WHITE DOWN-POINTING TRIANGLE | 26da DRIVE SLOW SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26dd SQUARED SALTIRE | 26dc LEFT CLOSED ENTRY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26df BLACK TRUCK | 26de FALLING DIAGONAL IN WHITE CIRCLE IN BLA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26e1 RESTRICTED LEFT ENTRY-2 | 26e0 RESTRICTED LEFT ENTRY-1 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26e3 HEAVY CIRCLE WITH STROKE AND TWO DOTS A | 26e2 ASTRONOMICAL SYMBOL FOR URANUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26e5 RIGHT-HANDED INTERLACED PENTAGRAM | 26e4 PENTAGRAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26e7 INVERTED PENTAGRAM | 26e6 LEFT-HANDED INTERLACED PENTAGRAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26e9 SHINTO SHRINE | 26e8 BLACK CROSS ON SHIELD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26eb CASTLE | 26ea CHURCH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26ed GEAR WITHOUT HUB | 26ec HISTORIC SITE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26ef MAP SYMBOL FOR LIGHTHOUSE | 26ee GEAR WITH HANDLES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26f1 UMBRELLA ON GROUND | 26f0 MOUNTAIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26f3 FLAG IN HOLE | 26f2 FOUNTAIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26f5 SAILBOAT | 26f4 FERRY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26f7 SKIER | 26f6 SQUARE FOUR CORNERS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26f9 PERSON WITH BALL | 26f8 ICE SKATE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26fb JAPANESE BANK SYMBOL | 26fa TENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26fd FUEL PUMP | 26fc HEADSTONE GRAVEYARD SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26ff WHITE FLAG WITH HORIZONTAL MIDDLE BLACK | 26fe CUP ON BLACK SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 2701 UPPER BLADE SCISSORS | 2700 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2703 LOWER BLADE SCISSORS | 2702 BLACK SCISSORS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2705 WHITE HEAVY CHECK MARK | 2704 WHITE SCISSORS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2707 TAPE DRIVE | 2706 TELEPHONE LOCATION SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2709 ENVELOPE | 2708 AIRPLANE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 270b RAISED HAND | 270a RAISED FIST */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 270d WRITING HAND | 270c VICTORY HAND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 270f PENCIL | 270e LOWER RIGHT PENCIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2711 WHITE NIB | 2710 UPPER RIGHT PENCIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2713 CHECK MARK | 2712 BLACK NIB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2715 MULTIPLICATION X | 2714 HEAVY CHECK MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2717 BALLOT X | 2716 HEAVY MULTIPLICATION X */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2719 OUTLINED GREEK CROSS | 2718 HEAVY BALLOT X */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 271b OPEN CENTRE CROSS | 271a HEAVY GREEK CROSS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 271d LATIN CROSS | 271c HEAVY OPEN CENTRE CROSS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 271f OUTLINED LATIN CROSS | 271e SHADOWED WHITE LATIN CROSS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2721 STAR OF DAVID | 2720 MALTESE CROSS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2723 FOUR BALLOON-SPOKED ASTERISK | 2722 FOUR TEARDROP-SPOKED ASTERISK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2725 FOUR CLUB-SPOKED ASTERISK | 2724 HEAVY FOUR BALLOON-SPOKED ASTERISK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2727 WHITE FOUR POINTED STAR | 2726 BLACK FOUR POINTED STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2729 STRESS OUTLINED WHITE STAR | 2728 SPARKLES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 272b OPEN CENTRE BLACK STAR | 272a CIRCLED WHITE STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 272d OUTLINED BLACK STAR | 272c BLACK CENTRE WHITE STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 272f PINWHEEL STAR | 272e HEAVY OUTLINED BLACK STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2731 HEAVY ASTERISK | 2730 SHADOWED WHITE STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2733 EIGHT SPOKED ASTERISK | 2732 OPEN CENTRE ASTERISK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2735 EIGHT POINTED PINWHEEL STAR | 2734 EIGHT POINTED BLACK STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2737 EIGHT POINTED RECTILINEAR BLACK STAR | 2736 SIX POINTED BLACK STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2739 TWELVE POINTED BLACK STAR | 2738 HEAVY EIGHT POINTED RECTILINEAR BLACK S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 273b TEARDROP-SPOKED ASTERISK | 273a SIXTEEN POINTED ASTERISK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 273d HEAVY TEARDROP-SPOKED ASTERISK | 273c OPEN CENTRE TEARDROP-SPOKED ASTERISK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 273f BLACK FLORETTE | 273e SIX PETALLED BLACK AND WHITE FLORETTE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2741 EIGHT PETALLED OUTLINED BLACK FLORETTE | 2740 WHITE FLORETTE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2743 HEAVY TEARDROP-SPOKED PINWHEEL ASTERISK | 2742 CIRCLED OPEN CENTRE EIGHT POINTED STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2745 TIGHT TRIFOLIATE SNOWFLAKE | 2744 SNOWFLAKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2747 SPARKLE | 2746 HEAVY CHEVRON SNOWFLAKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2749 BALLOON-SPOKED ASTERISK | 2748 HEAVY SPARKLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 274b HEAVY EIGHT TEARDROP-SPOKED PROPELLER A | 274a EIGHT TEARDROP-SPOKED PROPELLER ASTERIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 274d SHADOWED WHITE CIRCLE | 274c CROSS MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 274f LOWER RIGHT DROP-SHADOWED WHITE SQUARE | 274e NEGATIVE SQUARED CROSS MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2751 LOWER RIGHT SHADOWED WHITE SQUARE | 2750 UPPER RIGHT DROP-SHADOWED WHITE SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2753 BLACK QUESTION MARK ORNAMENT | 2752 UPPER RIGHT SHADOWED WHITE SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2755 WHITE EXCLAMATION MARK ORNAMENT | 2754 WHITE QUESTION MARK ORNAMENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2757 HEAVY EXCLAMATION MARK SYMBOL | 2756 BLACK DIAMOND MINUS WHITE X */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2759 MEDIUM VERTICAL BAR | 2758 LIGHT VERTICAL BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 275b HEAVY SINGLE TURNED COMMA QUOTATION MAR | 275a HEAVY VERTICAL BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 275d HEAVY DOUBLE TURNED COMMA QUOTATION MAR | 275c HEAVY SINGLE COMMA QUOTATION MARK ORNAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 275f HEAVY LOW SINGLE COMMA QUOTATION MARK O | 275e HEAVY DOUBLE COMMA QUOTATION MARK ORNAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2761 CURVED STEM PARAGRAPH SIGN ORNAMENT | 2760 HEAVY LOW DOUBLE COMMA QUOTATION MARK O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2763 HEAVY HEART EXCLAMATION MARK ORNAMENT | 2762 HEAVY EXCLAMATION MARK ORNAMENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2765 ROTATED HEAVY BLACK HEART BULLET | 2764 HEAVY BLACK HEART */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2767 ROTATED FLORAL HEART BULLET | 2766 FLORAL HEART */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2769 MEDIUM RIGHT PARENTHESIS ORNAMENT | 2768 MEDIUM LEFT PARENTHESIS ORNAMENT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 276b MEDIUM FLATTENED RIGHT PARENTHESIS ORNA | 276a MEDIUM FLATTENED LEFT PARENTHESIS ORNAM */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 276d MEDIUM RIGHT-POINTING ANGLE BRACKET ORN | 276c MEDIUM LEFT-POINTING ANGLE BRACKET ORNA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 276f HEAVY RIGHT-POINTING ANGLE QUOTATION MA | 276e HEAVY LEFT-POINTING ANGLE QUOTATION MAR */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2771 HEAVY RIGHT-POINTING ANGLE BRACKET ORNA | 2770 HEAVY LEFT-POINTING ANGLE BRACKET ORNAM */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2773 LIGHT RIGHT TORTOISE SHELL BRACKET ORNA | 2772 LIGHT LEFT TORTOISE SHELL BRACKET ORNAM */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2775 MEDIUM RIGHT CURLY BRACKET ORNAMENT | 2774 MEDIUM LEFT CURLY BRACKET ORNAMENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2777 DINGBAT NEGATIVE CIRCLED DIGIT TWO | 2776 DINGBAT NEGATIVE CIRCLED DIGIT ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2779 DINGBAT NEGATIVE CIRCLED DIGIT FOUR | 2778 DINGBAT NEGATIVE CIRCLED DIGIT THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 277b DINGBAT NEGATIVE CIRCLED DIGIT SIX | 277a DINGBAT NEGATIVE CIRCLED DIGIT FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 277d DINGBAT NEGATIVE CIRCLED DIGIT EIGHT | 277c DINGBAT NEGATIVE CIRCLED DIGIT SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 277f DINGBAT NEGATIVE CIRCLED NUMBER TEN | 277e DINGBAT NEGATIVE CIRCLED DIGIT NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2781 DINGBAT CIRCLED SANS-SERIF DIGIT TWO | 2780 DINGBAT CIRCLED SANS-SERIF DIGIT ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2783 DINGBAT CIRCLED SANS-SERIF DIGIT FOUR | 2782 DINGBAT CIRCLED SANS-SERIF DIGIT THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2785 DINGBAT CIRCLED SANS-SERIF DIGIT SIX | 2784 DINGBAT CIRCLED SANS-SERIF DIGIT FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2787 DINGBAT CIRCLED SANS-SERIF DIGIT EIGHT | 2786 DINGBAT CIRCLED SANS-SERIF DIGIT SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2789 DINGBAT CIRCLED SANS-SERIF NUMBER TEN | 2788 DINGBAT CIRCLED SANS-SERIF DIGIT NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 278b DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG | 278a DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 278d DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG | 278c DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 278f DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG | 278e DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2791 DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG | 2790 DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2793 DINGBAT NEGATIVE CIRCLED SANS-SERIF NUM | 2792 DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2795 HEAVY PLUS SIGN | 2794 HEAVY WIDE-HEADED RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2797 HEAVY DIVISION SIGN | 2796 HEAVY MINUS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2799 HEAVY RIGHTWARDS ARROW | 2798 HEAVY SOUTH EAST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 279b DRAFTING POINT RIGHTWARDS ARROW | 279a HEAVY NORTH EAST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 279d TRIANGLE-HEADED RIGHTWARDS ARROW | 279c HEAVY ROUND-TIPPED RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 279f DASHED TRIANGLE-HEADED RIGHTWARDS ARROW | 279e HEAVY TRIANGLE-HEADED RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27a1 BLACK RIGHTWARDS ARROW | 27a0 HEAVY DASHED TRIANGLE-HEADED RIGHTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27a3 THREE-D BOTTOM-LIGHTED RIGHTWARDS ARROW | 27a2 THREE-D TOP-LIGHTED RIGHTWARDS ARROWHEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27a5 HEAVY BLACK CURVED DOWNWARDS AND RIGHTW | 27a4 BLACK RIGHTWARDS ARROWHEAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27a7 SQUAT BLACK RIGHTWARDS ARROW | 27a6 HEAVY BLACK CURVED UPWARDS AND RIGHTWAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27a9 RIGHT-SHADED WHITE RIGHTWARDS ARROW | 27a8 HEAVY CONCAVE-POINTED BLACK RIGHTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27ab BACK-TILTED SHADOWED WHITE RIGHTWARDS A | 27aa LEFT-SHADED WHITE RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27ad HEAVY LOWER RIGHT-SHADOWED WHITE RIGHTW | 27ac FRONT-TILTED SHADOWED WHITE RIGHTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27af NOTCHED LOWER RIGHT-SHADOWED WHITE RIGH | 27ae HEAVY UPPER RIGHT-SHADOWED WHITE RIGHTW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27b1 NOTCHED UPPER RIGHT-SHADOWED WHITE RIGH | 27b0 CURLY LOOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27b3 WHITE-FEATHERED RIGHTWARDS ARROW | 27b2 CIRCLED HEAVY WHITE RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27b5 BLACK-FEATHERED RIGHTWARDS ARROW | 27b4 BLACK-FEATHERED SOUTH EAST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27b7 HEAVY BLACK-FEATHERED SOUTH EAST ARROW | 27b6 BLACK-FEATHERED NORTH EAST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27b9 HEAVY BLACK-FEATHERED NORTH EAST ARROW | 27b8 HEAVY BLACK-FEATHERED RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27bb HEAVY TEARDROP-SHANKED RIGHTWARDS ARROW | 27ba TEARDROP-BARBED RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27bd HEAVY WEDGE-TAILED RIGHTWARDS ARROW | 27bc WEDGE-TAILED RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27bf DOUBLE CURLY LOOP | 27be OPEN-OUTLINED RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27c1 WHITE TRIANGLE CONTAINING SMALL WHITE T | 27c0 THREE DIMENSIONAL ANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27c3 OPEN SUBSET | 27c2 PERPENDICULAR */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 27c5 LEFT S-SHAPED BAG DELIMITER | 27c4 OPEN SUPERSET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 27c7 OR WITH DOT INSIDE | 27c6 RIGHT S-SHAPED BAG DELIMITER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27c9 SUPERSET PRECEDING SOLIDUS | 27c8 REVERSE SOLIDUS PRECEDING SUBSET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27cb MATHEMATICAL RISING DIAGONAL | 27ca VERTICAL BAR WITH HORIZONTAL STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27cd MATHEMATICAL FALLING DIAGONAL | 27cc LONG DIVISION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27cf SQUARED LOGICAL OR | 27ce SQUARED LOGICAL AND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27d1 AND WITH DOT | 27d0 WHITE DIAMOND WITH CENTRED DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27d3 LOWER RIGHT CORNER WITH DOT | 27d2 ELEMENT OF OPENING UPWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27d5 LEFT OUTER JOIN | 27d4 UPPER LEFT CORNER WITH DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27d7 FULL OUTER JOIN | 27d6 RIGHT OUTER JOIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27d9 LARGE DOWN TACK | 27d8 LARGE UP TACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27db LEFT AND RIGHT TACK | 27da LEFT AND RIGHT DOUBLE TURNSTILE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27dd LONG RIGHT TACK | 27dc LEFT MULTIMAP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27df UP TACK WITH CIRCLE ABOVE | 27de LONG LEFT TACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27e1 WHITE CONCAVE-SIDED DIAMOND | 27e0 LOZENGE DIVIDED BY HORIZONTAL RULE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27e3 WHITE CONCAVE-SIDED DIAMOND WITH RIGHTW | 27e2 WHITE CONCAVE-SIDED DIAMOND WITH LEFTWA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27e5 WHITE SQUARE WITH RIGHTWARDS TICK | 27e4 WHITE SQUARE WITH LEFTWARDS TICK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 27e7 MATHEMATICAL RIGHT WHITE SQUARE BRACKET | 27e6 MATHEMATICAL LEFT WHITE SQUARE BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 27e9 MATHEMATICAL RIGHT ANGLE BRACKET | 27e8 MATHEMATICAL LEFT ANGLE BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 27eb MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET | 27ea MATHEMATICAL LEFT DOUBLE ANGLE BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 27ed MATHEMATICAL RIGHT WHITE TORTOISE SHELL | 27ec MATHEMATICAL LEFT WHITE TORTOISE SHELL */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 27ef MATHEMATICAL RIGHT FLATTENED PARENTHESI | 27ee MATHEMATICAL LEFT FLATTENED PARENTHESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27f1 DOWNWARDS QUADRUPLE ARROW | 27f0 UPWARDS QUADRUPLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27f3 CLOCKWISE GAPPED CIRCLE ARROW | 27f2 ANTICLOCKWISE GAPPED CIRCLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27f5 LONG LEFTWARDS ARROW | 27f4 RIGHT ARROW WITH CIRCLED PLUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27f7 LONG LEFT RIGHT ARROW | 27f6 LONG RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27f9 LONG RIGHTWARDS DOUBLE ARROW | 27f8 LONG LEFTWARDS DOUBLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27fb LONG LEFTWARDS ARROW FROM BAR | 27fa LONG LEFT RIGHT DOUBLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27fd LONG LEFTWARDS DOUBLE ARROW FROM BAR | 27fc LONG RIGHTWARDS ARROW FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27ff LONG RIGHTWARDS SQUIGGLE ARROW | 27fe LONG RIGHTWARDS DOUBLE ARROW FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2801 BRAILLE PATTERN DOTS-1 | 2800 BRAILLE PATTERN BLANK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2803 BRAILLE PATTERN DOTS-12 | 2802 BRAILLE PATTERN DOTS-2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2805 BRAILLE PATTERN DOTS-13 | 2804 BRAILLE PATTERN DOTS-3 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2807 BRAILLE PATTERN DOTS-123 | 2806 BRAILLE PATTERN DOTS-23 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2809 BRAILLE PATTERN DOTS-14 | 2808 BRAILLE PATTERN DOTS-4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 280b BRAILLE PATTERN DOTS-124 | 280a BRAILLE PATTERN DOTS-24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 280d BRAILLE PATTERN DOTS-134 | 280c BRAILLE PATTERN DOTS-34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 280f BRAILLE PATTERN DOTS-1234 | 280e BRAILLE PATTERN DOTS-234 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2811 BRAILLE PATTERN DOTS-15 | 2810 BRAILLE PATTERN DOTS-5 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2813 BRAILLE PATTERN DOTS-125 | 2812 BRAILLE PATTERN DOTS-25 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2815 BRAILLE PATTERN DOTS-135 | 2814 BRAILLE PATTERN DOTS-35 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2817 BRAILLE PATTERN DOTS-1235 | 2816 BRAILLE PATTERN DOTS-235 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2819 BRAILLE PATTERN DOTS-145 | 2818 BRAILLE PATTERN DOTS-45 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 281b BRAILLE PATTERN DOTS-1245 | 281a BRAILLE PATTERN DOTS-245 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 281d BRAILLE PATTERN DOTS-1345 | 281c BRAILLE PATTERN DOTS-345 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 281f BRAILLE PATTERN DOTS-12345 | 281e BRAILLE PATTERN DOTS-2345 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2821 BRAILLE PATTERN DOTS-16 | 2820 BRAILLE PATTERN DOTS-6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2823 BRAILLE PATTERN DOTS-126 | 2822 BRAILLE PATTERN DOTS-26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2825 BRAILLE PATTERN DOTS-136 | 2824 BRAILLE PATTERN DOTS-36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2827 BRAILLE PATTERN DOTS-1236 | 2826 BRAILLE PATTERN DOTS-236 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2829 BRAILLE PATTERN DOTS-146 | 2828 BRAILLE PATTERN DOTS-46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 282b BRAILLE PATTERN DOTS-1246 | 282a BRAILLE PATTERN DOTS-246 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 282d BRAILLE PATTERN DOTS-1346 | 282c BRAILLE PATTERN DOTS-346 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 282f BRAILLE PATTERN DOTS-12346 | 282e BRAILLE PATTERN DOTS-2346 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2831 BRAILLE PATTERN DOTS-156 | 2830 BRAILLE PATTERN DOTS-56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2833 BRAILLE PATTERN DOTS-1256 | 2832 BRAILLE PATTERN DOTS-256 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2835 BRAILLE PATTERN DOTS-1356 | 2834 BRAILLE PATTERN DOTS-356 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2837 BRAILLE PATTERN DOTS-12356 | 2836 BRAILLE PATTERN DOTS-2356 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2839 BRAILLE PATTERN DOTS-1456 | 2838 BRAILLE PATTERN DOTS-456 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 283b BRAILLE PATTERN DOTS-12456 | 283a BRAILLE PATTERN DOTS-2456 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 283d BRAILLE PATTERN DOTS-13456 | 283c BRAILLE PATTERN DOTS-3456 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 283f BRAILLE PATTERN DOTS-123456 | 283e BRAILLE PATTERN DOTS-23456 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2841 BRAILLE PATTERN DOTS-17 | 2840 BRAILLE PATTERN DOTS-7 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2843 BRAILLE PATTERN DOTS-127 | 2842 BRAILLE PATTERN DOTS-27 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2845 BRAILLE PATTERN DOTS-137 | 2844 BRAILLE PATTERN DOTS-37 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2847 BRAILLE PATTERN DOTS-1237 | 2846 BRAILLE PATTERN DOTS-237 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2849 BRAILLE PATTERN DOTS-147 | 2848 BRAILLE PATTERN DOTS-47 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 284b BRAILLE PATTERN DOTS-1247 | 284a BRAILLE PATTERN DOTS-247 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 284d BRAILLE PATTERN DOTS-1347 | 284c BRAILLE PATTERN DOTS-347 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 284f BRAILLE PATTERN DOTS-12347 | 284e BRAILLE PATTERN DOTS-2347 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2851 BRAILLE PATTERN DOTS-157 | 2850 BRAILLE PATTERN DOTS-57 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2853 BRAILLE PATTERN DOTS-1257 | 2852 BRAILLE PATTERN DOTS-257 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2855 BRAILLE PATTERN DOTS-1357 | 2854 BRAILLE PATTERN DOTS-357 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2857 BRAILLE PATTERN DOTS-12357 | 2856 BRAILLE PATTERN DOTS-2357 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2859 BRAILLE PATTERN DOTS-1457 | 2858 BRAILLE PATTERN DOTS-457 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 285b BRAILLE PATTERN DOTS-12457 | 285a BRAILLE PATTERN DOTS-2457 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 285d BRAILLE PATTERN DOTS-13457 | 285c BRAILLE PATTERN DOTS-3457 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 285f BRAILLE PATTERN DOTS-123457 | 285e BRAILLE PATTERN DOTS-23457 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2861 BRAILLE PATTERN DOTS-167 | 2860 BRAILLE PATTERN DOTS-67 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2863 BRAILLE PATTERN DOTS-1267 | 2862 BRAILLE PATTERN DOTS-267 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2865 BRAILLE PATTERN DOTS-1367 | 2864 BRAILLE PATTERN DOTS-367 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2867 BRAILLE PATTERN DOTS-12367 | 2866 BRAILLE PATTERN DOTS-2367 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2869 BRAILLE PATTERN DOTS-1467 | 2868 BRAILLE PATTERN DOTS-467 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 286b BRAILLE PATTERN DOTS-12467 | 286a BRAILLE PATTERN DOTS-2467 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 286d BRAILLE PATTERN DOTS-13467 | 286c BRAILLE PATTERN DOTS-3467 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 286f BRAILLE PATTERN DOTS-123467 | 286e BRAILLE PATTERN DOTS-23467 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2871 BRAILLE PATTERN DOTS-1567 | 2870 BRAILLE PATTERN DOTS-567 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2873 BRAILLE PATTERN DOTS-12567 | 2872 BRAILLE PATTERN DOTS-2567 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2875 BRAILLE PATTERN DOTS-13567 | 2874 BRAILLE PATTERN DOTS-3567 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2877 BRAILLE PATTERN DOTS-123567 | 2876 BRAILLE PATTERN DOTS-23567 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2879 BRAILLE PATTERN DOTS-14567 | 2878 BRAILLE PATTERN DOTS-4567 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 287b BRAILLE PATTERN DOTS-124567 | 287a BRAILLE PATTERN DOTS-24567 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 287d BRAILLE PATTERN DOTS-134567 | 287c BRAILLE PATTERN DOTS-34567 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 287f BRAILLE PATTERN DOTS-1234567 | 287e BRAILLE PATTERN DOTS-234567 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2881 BRAILLE PATTERN DOTS-18 | 2880 BRAILLE PATTERN DOTS-8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2883 BRAILLE PATTERN DOTS-128 | 2882 BRAILLE PATTERN DOTS-28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2885 BRAILLE PATTERN DOTS-138 | 2884 BRAILLE PATTERN DOTS-38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2887 BRAILLE PATTERN DOTS-1238 | 2886 BRAILLE PATTERN DOTS-238 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2889 BRAILLE PATTERN DOTS-148 | 2888 BRAILLE PATTERN DOTS-48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 288b BRAILLE PATTERN DOTS-1248 | 288a BRAILLE PATTERN DOTS-248 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 288d BRAILLE PATTERN DOTS-1348 | 288c BRAILLE PATTERN DOTS-348 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 288f BRAILLE PATTERN DOTS-12348 | 288e BRAILLE PATTERN DOTS-2348 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2891 BRAILLE PATTERN DOTS-158 | 2890 BRAILLE PATTERN DOTS-58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2893 BRAILLE PATTERN DOTS-1258 | 2892 BRAILLE PATTERN DOTS-258 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2895 BRAILLE PATTERN DOTS-1358 | 2894 BRAILLE PATTERN DOTS-358 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2897 BRAILLE PATTERN DOTS-12358 | 2896 BRAILLE PATTERN DOTS-2358 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2899 BRAILLE PATTERN DOTS-1458 | 2898 BRAILLE PATTERN DOTS-458 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 289b BRAILLE PATTERN DOTS-12458 | 289a BRAILLE PATTERN DOTS-2458 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 289d BRAILLE PATTERN DOTS-13458 | 289c BRAILLE PATTERN DOTS-3458 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 289f BRAILLE PATTERN DOTS-123458 | 289e BRAILLE PATTERN DOTS-23458 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28a1 BRAILLE PATTERN DOTS-168 | 28a0 BRAILLE PATTERN DOTS-68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28a3 BRAILLE PATTERN DOTS-1268 | 28a2 BRAILLE PATTERN DOTS-268 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28a5 BRAILLE PATTERN DOTS-1368 | 28a4 BRAILLE PATTERN DOTS-368 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28a7 BRAILLE PATTERN DOTS-12368 | 28a6 BRAILLE PATTERN DOTS-2368 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28a9 BRAILLE PATTERN DOTS-1468 | 28a8 BRAILLE PATTERN DOTS-468 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28ab BRAILLE PATTERN DOTS-12468 | 28aa BRAILLE PATTERN DOTS-2468 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28ad BRAILLE PATTERN DOTS-13468 | 28ac BRAILLE PATTERN DOTS-3468 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28af BRAILLE PATTERN DOTS-123468 | 28ae BRAILLE PATTERN DOTS-23468 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28b1 BRAILLE PATTERN DOTS-1568 | 28b0 BRAILLE PATTERN DOTS-568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28b3 BRAILLE PATTERN DOTS-12568 | 28b2 BRAILLE PATTERN DOTS-2568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28b5 BRAILLE PATTERN DOTS-13568 | 28b4 BRAILLE PATTERN DOTS-3568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28b7 BRAILLE PATTERN DOTS-123568 | 28b6 BRAILLE PATTERN DOTS-23568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28b9 BRAILLE PATTERN DOTS-14568 | 28b8 BRAILLE PATTERN DOTS-4568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28bb BRAILLE PATTERN DOTS-124568 | 28ba BRAILLE PATTERN DOTS-24568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28bd BRAILLE PATTERN DOTS-134568 | 28bc BRAILLE PATTERN DOTS-34568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28bf BRAILLE PATTERN DOTS-1234568 | 28be BRAILLE PATTERN DOTS-234568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28c1 BRAILLE PATTERN DOTS-178 | 28c0 BRAILLE PATTERN DOTS-78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28c3 BRAILLE PATTERN DOTS-1278 | 28c2 BRAILLE PATTERN DOTS-278 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28c5 BRAILLE PATTERN DOTS-1378 | 28c4 BRAILLE PATTERN DOTS-378 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28c7 BRAILLE PATTERN DOTS-12378 | 28c6 BRAILLE PATTERN DOTS-2378 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28c9 BRAILLE PATTERN DOTS-1478 | 28c8 BRAILLE PATTERN DOTS-478 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28cb BRAILLE PATTERN DOTS-12478 | 28ca BRAILLE PATTERN DOTS-2478 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28cd BRAILLE PATTERN DOTS-13478 | 28cc BRAILLE PATTERN DOTS-3478 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28cf BRAILLE PATTERN DOTS-123478 | 28ce BRAILLE PATTERN DOTS-23478 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28d1 BRAILLE PATTERN DOTS-1578 | 28d0 BRAILLE PATTERN DOTS-578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28d3 BRAILLE PATTERN DOTS-12578 | 28d2 BRAILLE PATTERN DOTS-2578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28d5 BRAILLE PATTERN DOTS-13578 | 28d4 BRAILLE PATTERN DOTS-3578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28d7 BRAILLE PATTERN DOTS-123578 | 28d6 BRAILLE PATTERN DOTS-23578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28d9 BRAILLE PATTERN DOTS-14578 | 28d8 BRAILLE PATTERN DOTS-4578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28db BRAILLE PATTERN DOTS-124578 | 28da BRAILLE PATTERN DOTS-24578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28dd BRAILLE PATTERN DOTS-134578 | 28dc BRAILLE PATTERN DOTS-34578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28df BRAILLE PATTERN DOTS-1234578 | 28de BRAILLE PATTERN DOTS-234578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28e1 BRAILLE PATTERN DOTS-1678 | 28e0 BRAILLE PATTERN DOTS-678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28e3 BRAILLE PATTERN DOTS-12678 | 28e2 BRAILLE PATTERN DOTS-2678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28e5 BRAILLE PATTERN DOTS-13678 | 28e4 BRAILLE PATTERN DOTS-3678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28e7 BRAILLE PATTERN DOTS-123678 | 28e6 BRAILLE PATTERN DOTS-23678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28e9 BRAILLE PATTERN DOTS-14678 | 28e8 BRAILLE PATTERN DOTS-4678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28eb BRAILLE PATTERN DOTS-124678 | 28ea BRAILLE PATTERN DOTS-24678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28ed BRAILLE PATTERN DOTS-134678 | 28ec BRAILLE PATTERN DOTS-34678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28ef BRAILLE PATTERN DOTS-1234678 | 28ee BRAILLE PATTERN DOTS-234678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28f1 BRAILLE PATTERN DOTS-15678 | 28f0 BRAILLE PATTERN DOTS-5678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28f3 BRAILLE PATTERN DOTS-125678 | 28f2 BRAILLE PATTERN DOTS-25678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28f5 BRAILLE PATTERN DOTS-135678 | 28f4 BRAILLE PATTERN DOTS-35678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28f7 BRAILLE PATTERN DOTS-1235678 | 28f6 BRAILLE PATTERN DOTS-235678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28f9 BRAILLE PATTERN DOTS-145678 | 28f8 BRAILLE PATTERN DOTS-45678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28fb BRAILLE PATTERN DOTS-1245678 | 28fa BRAILLE PATTERN DOTS-245678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28fd BRAILLE PATTERN DOTS-1345678 | 28fc BRAILLE PATTERN DOTS-345678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28ff BRAILLE PATTERN DOTS-12345678 | 28fe BRAILLE PATTERN DOTS-2345678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2901 RIGHTWARDS TWO-HEADED ARROW WITH DOUBLE | 2900 RIGHTWARDS TWO-HEADED ARROW WITH VERTIC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2903 RIGHTWARDS DOUBLE ARROW WITH VERTICAL S | 2902 LEFTWARDS DOUBLE ARROW WITH VERTICAL ST */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2905 RIGHTWARDS TWO-HEADED ARROW FROM BAR | 2904 LEFT RIGHT DOUBLE ARROW WITH VERTICAL S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2907 RIGHTWARDS DOUBLE ARROW FROM BAR | 2906 LEFTWARDS DOUBLE ARROW FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2909 UPWARDS ARROW WITH HORIZONTAL STROKE | 2908 DOWNWARDS ARROW WITH HORIZONTAL STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 290b DOWNWARDS TRIPLE ARROW | 290a UPWARDS TRIPLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 290d RIGHTWARDS DOUBLE DASH ARROW | 290c LEFTWARDS DOUBLE DASH ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 290f RIGHTWARDS TRIPLE DASH ARROW | 290e LEFTWARDS TRIPLE DASH ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2911 RIGHTWARDS ARROW WITH DOTTED STEM | 2910 RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2913 DOWNWARDS ARROW TO BAR | 2912 UPWARDS ARROW TO BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2915 RIGHTWARDS ARROW WITH TAIL WITH DOUBLE | 2914 RIGHTWARDS ARROW WITH TAIL WITH VERTICA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2917 RIGHTWARDS TWO-HEADED ARROW WITH TAIL W | 2916 RIGHTWARDS TWO-HEADED ARROW WITH TAIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2919 LEFTWARDS ARROW-TAIL | 2918 RIGHTWARDS TWO-HEADED ARROW WITH TAIL W */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 291b LEFTWARDS DOUBLE ARROW-TAIL | 291a RIGHTWARDS ARROW-TAIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 291d LEFTWARDS ARROW TO BLACK DIAMOND | 291c RIGHTWARDS DOUBLE ARROW-TAIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 291f LEFTWARDS ARROW FROM BAR TO BLACK DIAMO | 291e RIGHTWARDS ARROW TO BLACK DIAMOND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2921 NORTH WEST AND SOUTH EAST ARROW | 2920 RIGHTWARDS ARROW FROM BAR TO BLACK DIAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2923 NORTH WEST ARROW WITH HOOK | 2922 NORTH EAST AND SOUTH WEST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2925 SOUTH EAST ARROW WITH HOOK | 2924 NORTH EAST ARROW WITH HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2927 NORTH WEST ARROW AND NORTH EAST ARROW | 2926 SOUTH WEST ARROW WITH HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2929 SOUTH EAST ARROW AND SOUTH WEST ARROW | 2928 NORTH EAST ARROW AND SOUTH EAST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 292b RISING DIAGONAL CROSSING FALLING DIAGON | 292a SOUTH WEST ARROW AND NORTH WEST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 292d SOUTH EAST ARROW CROSSING NORTH EAST AR | 292c FALLING DIAGONAL CROSSING RISING DIAGON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 292f FALLING DIAGONAL CROSSING NORTH EAST AR | 292e NORTH EAST ARROW CROSSING SOUTH EAST AR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2931 NORTH EAST ARROW CROSSING NORTH WEST AR | 2930 RISING DIAGONAL CROSSING SOUTH EAST ARR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2933 WAVE ARROW POINTING DIRECTLY RIGHT | 2932 NORTH WEST ARROW CROSSING NORTH EAST AR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2935 ARROW POINTING RIGHTWARDS THEN CURVING | 2934 ARROW POINTING RIGHTWARDS THEN CURVING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2937 ARROW POINTING DOWNWARDS THEN CURVING R | 2936 ARROW POINTING DOWNWARDS THEN CURVING L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2939 LEFT-SIDE ARC ANTICLOCKWISE ARROW | 2938 RIGHT-SIDE ARC CLOCKWISE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 293b BOTTOM ARC ANTICLOCKWISE ARROW | 293a TOP ARC ANTICLOCKWISE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 293d TOP ARC ANTICLOCKWISE ARROW WITH PLUS | 293c TOP ARC CLOCKWISE ARROW WITH MINUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 293f LOWER LEFT SEMICIRCULAR ANTICLOCKWISE A | 293e LOWER RIGHT SEMICIRCULAR CLOCKWISE ARRO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2941 CLOCKWISE CLOSED CIRCLE ARROW | 2940 ANTICLOCKWISE CLOSED CIRCLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2943 LEFTWARDS ARROW ABOVE SHORT RIGHTWARDS | 2942 RIGHTWARDS ARROW ABOVE SHORT LEFTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2945 RIGHTWARDS ARROW WITH PLUS BELOW | 2944 SHORT RIGHTWARDS ARROW ABOVE LEFTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2947 RIGHTWARDS ARROW THROUGH X | 2946 LEFTWARDS ARROW WITH PLUS BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2949 UPWARDS TWO-HEADED ARROW FROM SMALL CIR | 2948 LEFT RIGHT ARROW THROUGH SMALL CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 294b LEFT BARB DOWN RIGHT BARB UP HARPOON | 294a LEFT BARB UP RIGHT BARB DOWN HARPOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 294d UP BARB LEFT DOWN BARB RIGHT HARPOON | 294c UP BARB RIGHT DOWN BARB LEFT HARPOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 294f UP BARB RIGHT DOWN BARB RIGHT HARPOON | 294e LEFT BARB UP RIGHT BARB UP HARPOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2951 UP BARB LEFT DOWN BARB LEFT HARPOON | 2950 LEFT BARB DOWN RIGHT BARB DOWN HARPOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2953 RIGHTWARDS HARPOON WITH BARB UP TO BAR | 2952 LEFTWARDS HARPOON WITH BARB UP TO BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2955 DOWNWARDS HARPOON WITH BARB RIGHT TO BA | 2954 UPWARDS HARPOON WITH BARB RIGHT TO BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2957 RIGHTWARDS HARPOON WITH BARB DOWN TO BA | 2956 LEFTWARDS HARPOON WITH BARB DOWN TO BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2959 DOWNWARDS HARPOON WITH BARB LEFT TO BAR | 2958 UPWARDS HARPOON WITH BARB LEFT TO BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 295b RIGHTWARDS HARPOON WITH BARB UP FROM BA | 295a LEFTWARDS HARPOON WITH BARB UP FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 295d DOWNWARDS HARPOON WITH BARB RIGHT FROM | 295c UPWARDS HARPOON WITH BARB RIGHT FROM BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 295f RIGHTWARDS HARPOON WITH BARB DOWN FROM | 295e LEFTWARDS HARPOON WITH BARB DOWN FROM B */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2961 DOWNWARDS HARPOON WITH BARB LEFT FROM B | 2960 UPWARDS HARPOON WITH BARB LEFT FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2963 UPWARDS HARPOON WITH BARB LEFT BESIDE U | 2962 LEFTWARDS HARPOON WITH BARB UP ABOVE LE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2965 DOWNWARDS HARPOON WITH BARB LEFT BESIDE | 2964 RIGHTWARDS HARPOON WITH BARB UP ABOVE R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2967 LEFTWARDS HARPOON WITH BARB DOWN ABOVE | 2966 LEFTWARDS HARPOON WITH BARB UP ABOVE RI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2969 RIGHTWARDS HARPOON WITH BARB DOWN ABOVE | 2968 RIGHTWARDS HARPOON WITH BARB UP ABOVE L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 296b LEFTWARDS HARPOON WITH BARB DOWN BELOW | 296a LEFTWARDS HARPOON WITH BARB UP ABOVE LO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 296d RIGHTWARDS HARPOON WITH BARB DOWN BELOW | 296c RIGHTWARDS HARPOON WITH BARB UP ABOVE L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 296f DOWNWARDS HARPOON WITH BARB LEFT BESIDE | 296e UPWARDS HARPOON WITH BARB LEFT BESIDE D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2971 EQUALS SIGN ABOVE RIGHTWARDS ARROW | 2970 RIGHT DOUBLE ARROW WITH ROUNDED HEAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2973 LEFTWARDS ARROW ABOVE TILDE OPERATOR | 2972 TILDE OPERATOR ABOVE RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2975 RIGHTWARDS ARROW ABOVE ALMOST EQUAL TO | 2974 RIGHTWARDS ARROW ABOVE TILDE OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2977 LEFTWARDS ARROW THROUGH LESS-THAN | 2976 LESS-THAN ABOVE LEFTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2979 SUBSET ABOVE RIGHTWARDS ARROW | 2978 GREATER-THAN ABOVE RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 297b SUPERSET ABOVE LEFTWARDS ARROW | 297a LEFTWARDS ARROW THROUGH SUBSET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 297d RIGHT FISH TAIL | 297c LEFT FISH TAIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 297f DOWN FISH TAIL | 297e UP FISH TAIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2981 Z NOTATION SPOT | 2980 TRIPLE VERTICAL BAR DELIMITER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 2983 LEFT WHITE CURLY BRACKET | 2982 Z NOTATION TYPE COLON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2985 LEFT WHITE PARENTHESIS | 2984 RIGHT WHITE CURLY BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2987 Z NOTATION LEFT IMAGE BRACKET | 2986 RIGHT WHITE PARENTHESIS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2989 Z NOTATION LEFT BINDING BRACKET | 2988 Z NOTATION RIGHT IMAGE BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 298b LEFT SQUARE BRACKET WITH UNDERBAR | 298a Z NOTATION RIGHT BINDING BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 298d LEFT SQUARE BRACKET WITH TICK IN TOP CO | 298c RIGHT SQUARE BRACKET WITH UNDERBAR */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 298f LEFT SQUARE BRACKET WITH TICK IN BOTTOM | 298e RIGHT SQUARE BRACKET WITH TICK IN BOTTO */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2991 LEFT ANGLE BRACKET WITH DOT | 2990 RIGHT SQUARE BRACKET WITH TICK IN TOP C */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2993 LEFT ARC LESS-THAN BRACKET | 2992 RIGHT ANGLE BRACKET WITH DOT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2995 DOUBLE LEFT ARC GREATER-THAN BRACKET | 2994 RIGHT ARC GREATER-THAN BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2997 LEFT BLACK TORTOISE SHELL BRACKET | 2996 DOUBLE RIGHT ARC LESS-THAN BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 2999 DOTTED FENCE | 2998 RIGHT BLACK TORTOISE SHELL BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 299b MEASURED ANGLE OPENING LEFT | 299a VERTICAL ZIGZAG LINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 299d MEASURED RIGHT ANGLE WITH DOT | 299c RIGHT ANGLE VARIANT WITH SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 299f ACUTE ANGLE | 299e ANGLE WITH S INSIDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29a1 SPHERICAL ANGLE OPENING UP | 29a0 SPHERICAL ANGLE OPENING LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29a3 REVERSED ANGLE | 29a2 TURNED ANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29a5 REVERSED ANGLE WITH UNDERBAR | 29a4 ANGLE WITH UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29a7 OBLIQUE ANGLE OPENING DOWN | 29a6 OBLIQUE ANGLE OPENING UP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29a9 MEASURED ANGLE WITH OPEN ARM ENDING IN | 29a8 MEASURED ANGLE WITH OPEN ARM ENDING IN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29ab MEASURED ANGLE WITH OPEN ARM ENDING IN | 29aa MEASURED ANGLE WITH OPEN ARM ENDING IN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29ad MEASURED ANGLE WITH OPEN ARM ENDING IN | 29ac MEASURED ANGLE WITH OPEN ARM ENDING IN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29af MEASURED ANGLE WITH OPEN ARM ENDING IN | 29ae MEASURED ANGLE WITH OPEN ARM ENDING IN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29b1 EMPTY SET WITH OVERBAR | 29b0 REVERSED EMPTY SET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29b3 EMPTY SET WITH RIGHT ARROW ABOVE | 29b2 EMPTY SET WITH SMALL CIRCLE ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29b5 CIRCLE WITH HORIZONTAL BAR | 29b4 EMPTY SET WITH LEFT ARROW ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29b7 CIRCLED PARALLEL | 29b6 CIRCLED VERTICAL BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29b9 CIRCLED PERPENDICULAR | 29b8 CIRCLED REVERSE SOLIDUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29bb CIRCLE WITH SUPERIMPOSED X | 29ba CIRCLE DIVIDED BY HORIZONTAL BAR AND TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29bd UP ARROW THROUGH CIRCLE | 29bc CIRCLED ANTICLOCKWISE-ROTATED DIVISION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29bf CIRCLED BULLET | 29be CIRCLED WHITE BULLET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29c1 CIRCLED GREATER-THAN | 29c0 CIRCLED LESS-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29c3 CIRCLE WITH TWO HORIZONTAL STROKES TO T | 29c2 CIRCLE WITH SMALL CIRCLE TO THE RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29c5 SQUARED FALLING DIAGONAL SLASH | 29c4 SQUARED RISING DIAGONAL SLASH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29c7 SQUARED SMALL CIRCLE | 29c6 SQUARED ASTERISK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29c9 TWO JOINED SQUARES | 29c8 SQUARED SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29cb TRIANGLE WITH UNDERBAR | 29ca TRIANGLE WITH DOT ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29cd TRIANGLE WITH SERIFS AT BOTTOM | 29cc S IN TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29cf LEFT TRIANGLE BESIDE VERTICAL BAR | 29ce RIGHT TRIANGLE ABOVE LEFT TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29d1 BOWTIE WITH LEFT HALF BLACK | 29d0 VERTICAL BAR BESIDE RIGHT TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29d3 BLACK BOWTIE | 29d2 BOWTIE WITH RIGHT HALF BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29d5 TIMES WITH RIGHT HALF BLACK | 29d4 TIMES WITH LEFT HALF BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29d7 BLACK HOURGLASS | 29d6 WHITE HOURGLASS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 29d9 RIGHT WIGGLY FENCE | 29d8 LEFT WIGGLY FENCE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 29db RIGHT DOUBLE WIGGLY FENCE | 29da LEFT DOUBLE WIGGLY FENCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29dd TIE OVER INFINITY | 29dc INCOMPLETE INFINITY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29df DOUBLE-ENDED MULTIMAP | 29de INFINITY NEGATED WITH VERTICAL BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29e1 INCREASES AS | 29e0 SQUARE WITH CONTOURED OUTLINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29e3 EQUALS SIGN AND SLANTED PARALLEL | 29e2 SHUFFLE PRODUCT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29e5 IDENTICAL TO AND SLANTED PARALLEL | 29e4 EQUALS SIGN AND SLANTED PARALLEL WITH T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29e7 THERMODYNAMIC | 29e6 GLEICH STARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29e9 DOWN-POINTING TRIANGLE WITH RIGHT HALF | 29e8 DOWN-POINTING TRIANGLE WITH LEFT HALF B */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29eb BLACK LOZENGE | 29ea BLACK DIAMOND WITH DOWN ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29ed BLACK CIRCLE WITH DOWN ARROW | 29ec WHITE CIRCLE WITH DOWN ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29ef ERROR-BARRED BLACK SQUARE | 29ee ERROR-BARRED WHITE SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29f1 ERROR-BARRED BLACK DIAMOND | 29f0 ERROR-BARRED WHITE DIAMOND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29f3 ERROR-BARRED BLACK CIRCLE | 29f2 ERROR-BARRED WHITE CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29f5 REVERSE SOLIDUS OPERATOR | 29f4 RULE-DELAYED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29f7 REVERSE SOLIDUS WITH HORIZONTAL STROKE | 29f6 SOLIDUS WITH OVERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29f9 BIG REVERSE SOLIDUS | 29f8 BIG SOLIDUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29fb TRIPLE PLUS | 29fa DOUBLE PLUS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 29fd RIGHT-POINTING CURVED ANGLE BRACKET | 29fc LEFT-POINTING CURVED ANGLE BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29ff MINY | 29fe TINY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a01 N-ARY CIRCLED PLUS OPERATOR | 2a00 N-ARY CIRCLED DOT OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a03 N-ARY UNION OPERATOR WITH DOT | 2a02 N-ARY CIRCLED TIMES OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a05 N-ARY SQUARE INTERSECTION OPERATOR | 2a04 N-ARY UNION OPERATOR WITH PLUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a07 TWO LOGICAL AND OPERATOR | 2a06 N-ARY SQUARE UNION OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a09 N-ARY TIMES OPERATOR | 2a08 TWO LOGICAL OR OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a0b SUMMATION WITH INTEGRAL | 2a0a MODULO TWO SUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a0d FINITE PART INTEGRAL | 2a0c QUADRUPLE INTEGRAL OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a0f INTEGRAL AVERAGE WITH SLASH | 2a0e INTEGRAL WITH DOUBLE STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a11 ANTICLOCKWISE INTEGRATION | 2a10 CIRCULATION FUNCTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a13 LINE INTEGRATION WITH SEMICIRCULAR PATH | 2a12 LINE INTEGRATION WITH RECTANGULAR PATH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a15 INTEGRAL AROUND A POINT OPERATOR | 2a14 LINE INTEGRATION NOT INCLUDING THE POLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a17 INTEGRAL WITH LEFTWARDS ARROW WITH HOOK | 2a16 QUATERNION INTEGRAL OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a19 INTEGRAL WITH INTERSECTION | 2a18 INTEGRAL WITH TIMES SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a1b INTEGRAL WITH OVERBAR | 2a1a INTEGRAL WITH UNION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a1d JOIN | 2a1c INTEGRAL WITH UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a1f Z NOTATION SCHEMA COMPOSITION | 2a1e LARGE LEFT TRIANGLE OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a21 Z NOTATION SCHEMA PROJECTION | 2a20 Z NOTATION SCHEMA PIPING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a23 PLUS SIGN WITH CIRCUMFLEX ACCENT ABOVE | 2a22 PLUS SIGN WITH SMALL CIRCLE ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a25 PLUS SIGN WITH DOT BELOW | 2a24 PLUS SIGN WITH TILDE ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a27 PLUS SIGN WITH SUBSCRIPT TWO | 2a26 PLUS SIGN WITH TILDE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a29 MINUS SIGN WITH COMMA ABOVE | 2a28 PLUS SIGN WITH BLACK TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a2b MINUS SIGN WITH FALLING DOTS | 2a2a MINUS SIGN WITH DOT BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a2d PLUS SIGN IN LEFT HALF CIRCLE | 2a2c MINUS SIGN WITH RISING DOTS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a2f VECTOR OR CROSS PRODUCT | 2a2e PLUS SIGN IN RIGHT HALF CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a31 MULTIPLICATION SIGN WITH UNDERBAR | 2a30 MULTIPLICATION SIGN WITH DOT ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a33 SMASH PRODUCT | 2a32 SEMIDIRECT PRODUCT WITH BOTTOM CLOSED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a35 MULTIPLICATION SIGN IN RIGHT HALF CIRCL | 2a34 MULTIPLICATION SIGN IN LEFT HALF CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a37 MULTIPLICATION SIGN IN DOUBLE CIRCLE | 2a36 CIRCLED MULTIPLICATION SIGN WITH CIRCUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a39 PLUS SIGN IN TRIANGLE | 2a38 CIRCLED DIVISION SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a3b MULTIPLICATION SIGN IN TRIANGLE | 2a3a MINUS SIGN IN TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a3d RIGHTHAND INTERIOR PRODUCT | 2a3c INTERIOR PRODUCT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a3f AMALGAMATION OR COPRODUCT | 2a3e Z NOTATION RELATIONAL COMPOSITION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a41 UNION WITH MINUS SIGN | 2a40 INTERSECTION WITH DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a43 INTERSECTION WITH OVERBAR | 2a42 UNION WITH OVERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a45 UNION WITH LOGICAL OR | 2a44 INTERSECTION WITH LOGICAL AND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a47 INTERSECTION ABOVE UNION | 2a46 UNION ABOVE INTERSECTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a49 INTERSECTION ABOVE BAR ABOVE UNION | 2a48 UNION ABOVE BAR ABOVE INTERSECTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a4b INTERSECTION BESIDE AND JOINED WITH INT | 2a4a UNION BESIDE AND JOINED WITH UNION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a4d CLOSED INTERSECTION WITH SERIFS | 2a4c CLOSED UNION WITH SERIFS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a4f DOUBLE SQUARE UNION | 2a4e DOUBLE SQUARE INTERSECTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a51 LOGICAL AND WITH DOT ABOVE | 2a50 CLOSED UNION WITH SERIFS AND SMASH PROD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a53 DOUBLE LOGICAL AND | 2a52 LOGICAL OR WITH DOT ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a55 TWO INTERSECTING LOGICAL AND | 2a54 DOUBLE LOGICAL OR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a57 SLOPING LARGE OR | 2a56 TWO INTERSECTING LOGICAL OR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a59 LOGICAL OR OVERLAPPING LOGICAL AND | 2a58 SLOPING LARGE AND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a5b LOGICAL OR WITH MIDDLE STEM | 2a5a LOGICAL AND WITH MIDDLE STEM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a5d LOGICAL OR WITH HORIZONTAL DASH | 2a5c LOGICAL AND WITH HORIZONTAL DASH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a5f LOGICAL AND WITH UNDERBAR | 2a5e LOGICAL AND WITH DOUBLE OVERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a61 SMALL VEE WITH UNDERBAR | 2a60 LOGICAL AND WITH DOUBLE UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a63 LOGICAL OR WITH DOUBLE UNDERBAR | 2a62 LOGICAL OR WITH DOUBLE OVERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a65 Z NOTATION RANGE ANTIRESTRICTION | 2a64 Z NOTATION DOMAIN ANTIRESTRICTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a67 IDENTICAL WITH DOT ABOVE | 2a66 EQUALS SIGN WITH DOT BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a69 TRIPLE HORIZONTAL BAR WITH TRIPLE VERTI | 2a68 TRIPLE HORIZONTAL BAR WITH DOUBLE VERTI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a6b TILDE OPERATOR WITH RISING DOTS | 2a6a TILDE OPERATOR WITH DOT ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a6d CONGRUENT WITH DOT ABOVE | 2a6c SIMILAR MINUS SIMILAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a6f ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT | 2a6e EQUALS WITH ASTERISK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a71 EQUALS SIGN ABOVE PLUS SIGN | 2a70 APPROXIMATELY EQUAL OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a73 EQUALS SIGN ABOVE TILDE OPERATOR | 2a72 PLUS SIGN ABOVE EQUALS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a75 TWO CONSECUTIVE EQUALS SIGNS | 2a74 DOUBLE COLON EQUAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a77 EQUALS SIGN WITH TWO DOTS ABOVE AND TWO | 2a76 THREE CONSECUTIVE EQUALS SIGNS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a79 LESS-THAN WITH CIRCLE INSIDE | 2a78 EQUIVALENT WITH FOUR DOTS ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a7b LESS-THAN WITH QUESTION MARK ABOVE | 2a7a GREATER-THAN WITH CIRCLE INSIDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a7d LESS-THAN OR SLANTED EQUAL TO | 2a7c GREATER-THAN WITH QUESTION MARK ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a7f LESS-THAN OR SLANTED EQUAL TO WITH DOT | 2a7e GREATER-THAN OR SLANTED EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a81 LESS-THAN OR SLANTED EQUAL TO WITH DOT | 2a80 GREATER-THAN OR SLANTED EQUAL TO WITH D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a83 LESS-THAN OR SLANTED EQUAL TO WITH DOT | 2a82 GREATER-THAN OR SLANTED EQUAL TO WITH D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a85 LESS-THAN OR APPROXIMATE | 2a84 GREATER-THAN OR SLANTED EQUAL TO WITH D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a87 LESS-THAN AND SINGLE-LINE NOT EQUAL TO | 2a86 GREATER-THAN OR APPROXIMATE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a89 LESS-THAN AND NOT APPROXIMATE | 2a88 GREATER-THAN AND SINGLE-LINE NOT EQUAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a8b LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE | 2a8a GREATER-THAN AND NOT APPROXIMATE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a8d LESS-THAN ABOVE SIMILAR OR EQUAL | 2a8c GREATER-THAN ABOVE DOUBLE-LINE EQUAL AB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a8f LESS-THAN ABOVE SIMILAR ABOVE GREATER-T | 2a8e GREATER-THAN ABOVE SIMILAR OR EQUAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a91 LESS-THAN ABOVE GREATER-THAN ABOVE DOUB | 2a90 GREATER-THAN ABOVE SIMILAR ABOVE LESS-T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a93 LESS-THAN ABOVE SLANTED EQUAL ABOVE GRE | 2a92 GREATER-THAN ABOVE LESS-THAN ABOVE DOUB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a95 SLANTED EQUAL TO OR LESS-THAN | 2a94 GREATER-THAN ABOVE SLANTED EQUAL ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a97 SLANTED EQUAL TO OR LESS-THAN WITH DOT | 2a96 SLANTED EQUAL TO OR GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a99 DOUBLE-LINE EQUAL TO OR LESS-THAN | 2a98 SLANTED EQUAL TO OR GREATER-THAN WITH D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a9b DOUBLE-LINE SLANTED EQUAL TO OR LESS-TH | 2a9a DOUBLE-LINE EQUAL TO OR GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a9d SIMILAR OR LESS-THAN | 2a9c DOUBLE-LINE SLANTED EQUAL TO OR GREATER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a9f SIMILAR ABOVE LESS-THAN ABOVE EQUALS SI | 2a9e SIMILAR OR GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aa1 DOUBLE NESTED LESS-THAN | 2aa0 SIMILAR ABOVE GREATER-THAN ABOVE EQUALS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aa3 DOUBLE NESTED LESS-THAN WITH UNDERBAR | 2aa2 DOUBLE NESTED GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aa5 GREATER-THAN BESIDE LESS-THAN | 2aa4 GREATER-THAN OVERLAPPING LESS-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aa7 GREATER-THAN CLOSED BY CURVE | 2aa6 LESS-THAN CLOSED BY CURVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aa9 GREATER-THAN CLOSED BY CURVE ABOVE SLAN | 2aa8 LESS-THAN CLOSED BY CURVE ABOVE SLANTED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aab LARGER THAN | 2aaa SMALLER THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aad LARGER THAN OR EQUAL TO | 2aac SMALLER THAN OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aaf PRECEDES ABOVE SINGLE-LINE EQUALS SIGN | 2aae EQUALS SIGN WITH BUMPY ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ab1 PRECEDES ABOVE SINGLE-LINE NOT EQUAL TO | 2ab0 SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ab3 PRECEDES ABOVE EQUALS SIGN | 2ab2 SUCCEEDS ABOVE SINGLE-LINE NOT EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ab5 PRECEDES ABOVE NOT EQUAL TO | 2ab4 SUCCEEDS ABOVE EQUALS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ab7 PRECEDES ABOVE ALMOST EQUAL TO | 2ab6 SUCCEEDS ABOVE NOT EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ab9 PRECEDES ABOVE NOT ALMOST EQUAL TO | 2ab8 SUCCEEDS ABOVE ALMOST EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2abb DOUBLE PRECEDES | 2aba SUCCEEDS ABOVE NOT ALMOST EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2abd SUBSET WITH DOT | 2abc DOUBLE SUCCEEDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2abf SUBSET WITH PLUS SIGN BELOW | 2abe SUPERSET WITH DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ac1 SUBSET WITH MULTIPLICATION SIGN BELOW | 2ac0 SUPERSET WITH PLUS SIGN BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ac3 SUBSET OF OR EQUAL TO WITH DOT ABOVE | 2ac2 SUPERSET WITH MULTIPLICATION SIGN BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ac5 SUBSET OF ABOVE EQUALS SIGN | 2ac4 SUPERSET OF OR EQUAL TO WITH DOT ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ac7 SUBSET OF ABOVE TILDE OPERATOR | 2ac6 SUPERSET OF ABOVE EQUALS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ac9 SUBSET OF ABOVE ALMOST EQUAL TO | 2ac8 SUPERSET OF ABOVE TILDE OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2acb SUBSET OF ABOVE NOT EQUAL TO | 2aca SUPERSET OF ABOVE ALMOST EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2acd SQUARE LEFT OPEN BOX OPERATOR | 2acc SUPERSET OF ABOVE NOT EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2acf CLOSED SUBSET | 2ace SQUARE RIGHT OPEN BOX OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ad1 CLOSED SUBSET OR EQUAL TO | 2ad0 CLOSED SUPERSET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ad3 SUBSET ABOVE SUPERSET | 2ad2 CLOSED SUPERSET OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ad5 SUBSET ABOVE SUBSET | 2ad4 SUPERSET ABOVE SUBSET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ad7 SUPERSET BESIDE SUBSET | 2ad6 SUPERSET ABOVE SUPERSET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ad9 ELEMENT OF OPENING DOWNWARDS | 2ad8 SUPERSET BESIDE AND JOINED BY DASH WITH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2adb TRANSVERSAL INTERSECTION | 2ada PITCHFORK WITH TEE TOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2add NONFORKING | 2adc FORKING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2adf SHORT DOWN TACK | 2ade SHORT LEFT TACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ae1 PERPENDICULAR WITH S | 2ae0 SHORT UP TACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ae3 DOUBLE VERTICAL BAR LEFT TURNSTILE | 2ae2 VERTICAL BAR TRIPLE RIGHT TURNSTILE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ae5 DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTI | 2ae4 VERTICAL BAR DOUBLE LEFT TURNSTILE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ae7 SHORT DOWN TACK WITH OVERBAR | 2ae6 LONG DASH FROM LEFT MEMBER OF DOUBLE VE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ae9 SHORT UP TACK ABOVE SHORT DOWN TACK | 2ae8 SHORT UP TACK WITH UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aeb DOUBLE UP TACK | 2aea DOUBLE DOWN TACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aed REVERSED DOUBLE STROKE NOT SIGN | 2aec DOUBLE STROKE NOT SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aef VERTICAL LINE WITH CIRCLE ABOVE | 2aee DOES NOT DIVIDE WITH REVERSED NEGATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2af1 DOWN TACK WITH CIRCLE BELOW | 2af0 VERTICAL LINE WITH CIRCLE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2af3 PARALLEL WITH TILDE OPERATOR | 2af2 PARALLEL WITH HORIZONTAL STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2af5 TRIPLE VERTICAL BAR WITH HORIZONTAL STR | 2af4 TRIPLE VERTICAL BAR BINARY RELATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2af7 TRIPLE NESTED LESS-THAN | 2af6 TRIPLE COLON OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2af9 DOUBLE-LINE SLANTED LESS-THAN OR EQUAL | 2af8 TRIPLE NESTED GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2afb TRIPLE SOLIDUS BINARY RELATION | 2afa DOUBLE-LINE SLANTED GREATER-THAN OR EQU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2afd DOUBLE SOLIDUS OPERATOR | 2afc LARGE TRIPLE VERTICAL BAR OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aff N-ARY WHITE VERTICAL BAR | 2afe WHITE VERTICAL BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b01 NORTH WEST WHITE ARROW | 2b00 NORTH EAST WHITE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b03 SOUTH WEST WHITE ARROW | 2b02 SOUTH EAST WHITE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b05 LEFTWARDS BLACK ARROW | 2b04 LEFT RIGHT WHITE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b07 DOWNWARDS BLACK ARROW | 2b06 UPWARDS BLACK ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b09 NORTH WEST BLACK ARROW | 2b08 NORTH EAST BLACK ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b0b SOUTH WEST BLACK ARROW | 2b0a SOUTH EAST BLACK ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b0d UP DOWN BLACK ARROW | 2b0c LEFT RIGHT BLACK ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b0f RIGHTWARDS ARROW WITH TIP UPWARDS | 2b0e RIGHTWARDS ARROW WITH TIP DOWNWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b11 LEFTWARDS ARROW WITH TIP UPWARDS | 2b10 LEFTWARDS ARROW WITH TIP DOWNWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b13 SQUARE WITH BOTTOM HALF BLACK | 2b12 SQUARE WITH TOP HALF BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b15 SQUARE WITH LOWER LEFT DIAGONAL HALF BL | 2b14 SQUARE WITH UPPER RIGHT DIAGONAL HALF B */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b17 DIAMOND WITH RIGHT HALF BLACK | 2b16 DIAMOND WITH LEFT HALF BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b19 DIAMOND WITH BOTTOM HALF BLACK | 2b18 DIAMOND WITH TOP HALF BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b1b BLACK LARGE SQUARE | 2b1a DOTTED SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b1d BLACK VERY SMALL SQUARE | 2b1c WHITE LARGE SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b1f BLACK PENTAGON | 2b1e WHITE VERY SMALL SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b21 WHITE HEXAGON | 2b20 WHITE PENTAGON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b23 HORIZONTAL BLACK HEXAGON | 2b22 BLACK HEXAGON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b25 BLACK MEDIUM DIAMOND | 2b24 BLACK LARGE CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b27 BLACK MEDIUM LOZENGE | 2b26 WHITE MEDIUM DIAMOND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b29 BLACK SMALL DIAMOND | 2b28 WHITE MEDIUM LOZENGE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b2b WHITE SMALL LOZENGE | 2b2a BLACK SMALL LOZENGE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b2d WHITE HORIZONTAL ELLIPSE | 2b2c BLACK HORIZONTAL ELLIPSE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b2f WHITE VERTICAL ELLIPSE | 2b2e BLACK VERTICAL ELLIPSE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b31 THREE LEFTWARDS ARROWS | 2b30 LEFT ARROW WITH SMALL CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b33 LONG LEFTWARDS SQUIGGLE ARROW | 2b32 LEFT ARROW WITH CIRCLED PLUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b35 LEFTWARDS TWO-HEADED ARROW WITH DOUBLE | 2b34 LEFTWARDS TWO-HEADED ARROW WITH VERTICA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b37 LEFTWARDS TWO-HEADED TRIPLE DASH ARROW | 2b36 LEFTWARDS TWO-HEADED ARROW FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b39 LEFTWARDS ARROW WITH TAIL WITH VERTICAL | 2b38 LEFTWARDS ARROW WITH DOTTED STEM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b3b LEFTWARDS TWO-HEADED ARROW WITH TAIL | 2b3a LEFTWARDS ARROW WITH TAIL WITH DOUBLE V */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b3d LEFTWARDS TWO-HEADED ARROW WITH TAIL WI | 2b3c LEFTWARDS TWO-HEADED ARROW WITH TAIL WI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b3f WAVE ARROW POINTING DIRECTLY LEFT | 2b3e LEFTWARDS ARROW THROUGH X */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b41 REVERSE TILDE OPERATOR ABOVE LEFTWARDS | 2b40 EQUALS SIGN ABOVE LEFTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b43 RIGHTWARDS ARROW THROUGH GREATER-THAN | 2b42 LEFTWARDS ARROW ABOVE REVERSE ALMOST EQ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b45 LEFTWARDS QUADRUPLE ARROW | 2b44 RIGHTWARDS ARROW THROUGH SUPERSET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b47 REVERSE TILDE OPERATOR ABOVE RIGHTWARDS | 2b46 RIGHTWARDS QUADRUPLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b49 TILDE OPERATOR ABOVE LEFTWARDS ARROW | 2b48 RIGHTWARDS ARROW ABOVE REVERSE ALMOST E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b4b LEFTWARDS ARROW ABOVE REVERSE TILDE OPE | 2b4a LEFTWARDS ARROW ABOVE ALMOST EQUAL TO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 2b4d (null) | 2b4c RIGHTWARDS ARROW ABOVE REVERSE TILDE OP */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b4f (null) | 2b4e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b51 BLACK SMALL STAR | 2b50 WHITE MEDIUM STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b53 BLACK RIGHT-POINTING PENTAGON | 2b52 WHITE SMALL STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b55 HEAVY LARGE CIRCLE | 2b54 WHITE RIGHT-POINTING PENTAGON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b57 HEAVY CIRCLE WITH CIRCLE INSIDE | 2b56 HEAVY OVAL WITH OVAL INSIDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b59 HEAVY CIRCLED SALTIRE | 2b58 HEAVY CIRCLE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b5b (null) | 2b5a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b5d (null) | 2b5c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b5f (null) | 2b5e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b61 (null) | 2b60 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b63 (null) | 2b62 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b65 (null) | 2b64 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b67 (null) | 2b66 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b69 (null) | 2b68 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b6b (null) | 2b6a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b6d (null) | 2b6c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b6f (null) | 2b6e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b71 (null) | 2b70 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b73 (null) | 2b72 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b75 (null) | 2b74 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b77 (null) | 2b76 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b79 (null) | 2b78 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b7b (null) | 2b7a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b7d (null) | 2b7c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b7f (null) | 2b7e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b81 (null) | 2b80 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b83 (null) | 2b82 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b85 (null) | 2b84 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b87 (null) | 2b86 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b89 (null) | 2b88 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b8b (null) | 2b8a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b8d (null) | 2b8c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b8f (null) | 2b8e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b91 (null) | 2b90 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b93 (null) | 2b92 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b95 (null) | 2b94 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b97 (null) | 2b96 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b99 (null) | 2b98 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b9b (null) | 2b9a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b9d (null) | 2b9c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b9f (null) | 2b9e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ba1 (null) | 2ba0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ba3 (null) | 2ba2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ba5 (null) | 2ba4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ba7 (null) | 2ba6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ba9 (null) | 2ba8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bab (null) | 2baa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bad (null) | 2bac (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2baf (null) | 2bae (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bb1 (null) | 2bb0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bb3 (null) | 2bb2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bb5 (null) | 2bb4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bb7 (null) | 2bb6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bb9 (null) | 2bb8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bbb (null) | 2bba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bbd (null) | 2bbc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bbf (null) | 2bbe (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bc1 (null) | 2bc0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bc3 (null) | 2bc2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bc5 (null) | 2bc4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bc7 (null) | 2bc6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bc9 (null) | 2bc8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bcb (null) | 2bca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bcd (null) | 2bcc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bcf (null) | 2bce (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bd1 (null) | 2bd0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bd3 (null) | 2bd2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bd5 (null) | 2bd4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bd7 (null) | 2bd6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bd9 (null) | 2bd8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bdb (null) | 2bda (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bdd (null) | 2bdc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bdf (null) | 2bde (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2be1 (null) | 2be0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2be3 (null) | 2be2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2be5 (null) | 2be4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2be7 (null) | 2be6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2be9 (null) | 2be8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2beb (null) | 2bea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bed (null) | 2bec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bef (null) | 2bee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bf1 (null) | 2bf0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bf3 (null) | 2bf2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bf5 (null) | 2bf4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bf7 (null) | 2bf6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bf9 (null) | 2bf8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bfb (null) | 2bfa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bfd (null) | 2bfc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bff (null) | 2bfe (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c01 GLAGOLITIC CAPITAL LETTER BUKY | 2c00 GLAGOLITIC CAPITAL LETTER AZU */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c03 GLAGOLITIC CAPITAL LETTER GLAGOLI | 2c02 GLAGOLITIC CAPITAL LETTER VEDE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c05 GLAGOLITIC CAPITAL LETTER YESTU | 2c04 GLAGOLITIC CAPITAL LETTER DOBRO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c07 GLAGOLITIC CAPITAL LETTER DZELO | 2c06 GLAGOLITIC CAPITAL LETTER ZHIVETE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c09 GLAGOLITIC CAPITAL LETTER IZHE | 2c08 GLAGOLITIC CAPITAL LETTER ZEMLJA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c0b GLAGOLITIC CAPITAL LETTER I | 2c0a GLAGOLITIC CAPITAL LETTER INITIAL IZHE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c0d GLAGOLITIC CAPITAL LETTER KAKO | 2c0c GLAGOLITIC CAPITAL LETTER DJERVI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c0f GLAGOLITIC CAPITAL LETTER MYSLITE | 2c0e GLAGOLITIC CAPITAL LETTER LJUDIJE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c11 GLAGOLITIC CAPITAL LETTER ONU | 2c10 GLAGOLITIC CAPITAL LETTER NASHI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c13 GLAGOLITIC CAPITAL LETTER RITSI | 2c12 GLAGOLITIC CAPITAL LETTER POKOJI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c15 GLAGOLITIC CAPITAL LETTER TVRIDO | 2c14 GLAGOLITIC CAPITAL LETTER SLOVO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c17 GLAGOLITIC CAPITAL LETTER FRITU | 2c16 GLAGOLITIC CAPITAL LETTER UKU */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c19 GLAGOLITIC CAPITAL LETTER OTU | 2c18 GLAGOLITIC CAPITAL LETTER HERU */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c1b GLAGOLITIC CAPITAL LETTER SHTA | 2c1a GLAGOLITIC CAPITAL LETTER PE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c1d GLAGOLITIC CAPITAL LETTER CHRIVI | 2c1c GLAGOLITIC CAPITAL LETTER TSI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c1f GLAGOLITIC CAPITAL LETTER YERU | 2c1e GLAGOLITIC CAPITAL LETTER SHA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c21 GLAGOLITIC CAPITAL LETTER YATI | 2c20 GLAGOLITIC CAPITAL LETTER YERI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c23 GLAGOLITIC CAPITAL LETTER YU | 2c22 GLAGOLITIC CAPITAL LETTER SPIDERY HA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c25 GLAGOLITIC CAPITAL LETTER SMALL YUS WIT | 2c24 GLAGOLITIC CAPITAL LETTER SMALL YUS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c27 GLAGOLITIC CAPITAL LETTER IOTATED SMALL | 2c26 GLAGOLITIC CAPITAL LETTER YO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c29 GLAGOLITIC CAPITAL LETTER IOTATED BIG Y | 2c28 GLAGOLITIC CAPITAL LETTER BIG YUS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c2b GLAGOLITIC CAPITAL LETTER IZHITSA | 2c2a GLAGOLITIC CAPITAL LETTER FITA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c2d GLAGOLITIC CAPITAL LETTER TROKUTASTI A | 2c2c GLAGOLITIC CAPITAL LETTER SHTAPIC */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UPPER, /* 2c2f (null) | 2c2e GLAGOLITIC CAPITAL LETTER LATINATE MYSL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c31 GLAGOLITIC SMALL LETTER BUKY | 2c30 GLAGOLITIC SMALL LETTER AZU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c33 GLAGOLITIC SMALL LETTER GLAGOLI | 2c32 GLAGOLITIC SMALL LETTER VEDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c35 GLAGOLITIC SMALL LETTER YESTU | 2c34 GLAGOLITIC SMALL LETTER DOBRO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c37 GLAGOLITIC SMALL LETTER DZELO | 2c36 GLAGOLITIC SMALL LETTER ZHIVETE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c39 GLAGOLITIC SMALL LETTER IZHE | 2c38 GLAGOLITIC SMALL LETTER ZEMLJA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c3b GLAGOLITIC SMALL LETTER I | 2c3a GLAGOLITIC SMALL LETTER INITIAL IZHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c3d GLAGOLITIC SMALL LETTER KAKO | 2c3c GLAGOLITIC SMALL LETTER DJERVI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c3f GLAGOLITIC SMALL LETTER MYSLITE | 2c3e GLAGOLITIC SMALL LETTER LJUDIJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c41 GLAGOLITIC SMALL LETTER ONU | 2c40 GLAGOLITIC SMALL LETTER NASHI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c43 GLAGOLITIC SMALL LETTER RITSI | 2c42 GLAGOLITIC SMALL LETTER POKOJI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c45 GLAGOLITIC SMALL LETTER TVRIDO | 2c44 GLAGOLITIC SMALL LETTER SLOVO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c47 GLAGOLITIC SMALL LETTER FRITU | 2c46 GLAGOLITIC SMALL LETTER UKU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c49 GLAGOLITIC SMALL LETTER OTU | 2c48 GLAGOLITIC SMALL LETTER HERU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c4b GLAGOLITIC SMALL LETTER SHTA | 2c4a GLAGOLITIC SMALL LETTER PE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c4d GLAGOLITIC SMALL LETTER CHRIVI | 2c4c GLAGOLITIC SMALL LETTER TSI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c4f GLAGOLITIC SMALL LETTER YERU | 2c4e GLAGOLITIC SMALL LETTER SHA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c51 GLAGOLITIC SMALL LETTER YATI | 2c50 GLAGOLITIC SMALL LETTER YERI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c53 GLAGOLITIC SMALL LETTER YU | 2c52 GLAGOLITIC SMALL LETTER SPIDERY HA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c55 GLAGOLITIC SMALL LETTER SMALL YUS WITH | 2c54 GLAGOLITIC SMALL LETTER SMALL YUS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c57 GLAGOLITIC SMALL LETTER IOTATED SMALL Y | 2c56 GLAGOLITIC SMALL LETTER YO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c59 GLAGOLITIC SMALL LETTER IOTATED BIG YUS | 2c58 GLAGOLITIC SMALL LETTER BIG YUS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c5b GLAGOLITIC SMALL LETTER IZHITSA | 2c5a GLAGOLITIC SMALL LETTER FITA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c5d GLAGOLITIC SMALL LETTER TROKUTASTI A | 2c5c GLAGOLITIC SMALL LETTER SHTAPIC */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_LOWER, /* 2c5f (null) | 2c5e GLAGOLITIC SMALL LETTER LATINATE MYSLIT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c61 LATIN SMALL LETTER L WITH DOUBLE BAR | 2c60 LATIN CAPITAL LETTER L WITH DOUBLE BAR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c63 LATIN CAPITAL LETTER P WITH STROKE | 2c62 LATIN CAPITAL LETTER L WITH MIDDLE TILD */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c65 LATIN SMALL LETTER A WITH STROKE | 2c64 LATIN CAPITAL LETTER R WITH TAIL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 2c67 LATIN CAPITAL LETTER H WITH DESCENDER | 2c66 LATIN SMALL LETTER T WITH DIAGONAL STRO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 2c69 LATIN CAPITAL LETTER K WITH DESCENDER | 2c68 LATIN SMALL LETTER H WITH DESCENDER */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 2c6b LATIN CAPITAL LETTER Z WITH DESCENDER | 2c6a LATIN SMALL LETTER K WITH DESCENDER */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 2c6d LATIN CAPITAL LETTER ALPHA | 2c6c LATIN SMALL LETTER Z WITH DESCENDER */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c6f LATIN CAPITAL LETTER TURNED A | 2c6e LATIN CAPITAL LETTER M WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c71 LATIN SMALL LETTER V WITH RIGHT HOOK | 2c70 LATIN CAPITAL LETTER TURNED ALPHA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c73 LATIN SMALL LETTER W WITH HOOK | 2c72 LATIN CAPITAL LETTER W WITH HOOK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 2c75 LATIN CAPITAL LETTER HALF H | 2c74 LATIN SMALL LETTER V WITH CURL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c77 LATIN SMALL LETTER TAILLESS PHI | 2c76 LATIN SMALL LETTER HALF H */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c79 LATIN SMALL LETTER TURNED R WITH TAIL | 2c78 LATIN SMALL LETTER E WITH NOTCH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c7b LATIN LETTER SMALL CAPITAL TURNED E | 2c7a LATIN SMALL LETTER O WITH LOW RING INSI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2c7d MODIFIER LETTER CAPITAL V | 2c7c LATIN SUBSCRIPT SMALL LETTER J */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c7f LATIN CAPITAL LETTER Z WITH SWASH TAIL | 2c7e LATIN CAPITAL LETTER S WITH SWASH TAIL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c81 COPTIC SMALL LETTER ALFA | 2c80 COPTIC CAPITAL LETTER ALFA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c83 COPTIC SMALL LETTER VIDA | 2c82 COPTIC CAPITAL LETTER VIDA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c85 COPTIC SMALL LETTER GAMMA | 2c84 COPTIC CAPITAL LETTER GAMMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c87 COPTIC SMALL LETTER DALDA | 2c86 COPTIC CAPITAL LETTER DALDA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c89 COPTIC SMALL LETTER EIE | 2c88 COPTIC CAPITAL LETTER EIE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c8b COPTIC SMALL LETTER SOU | 2c8a COPTIC CAPITAL LETTER SOU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c8d COPTIC SMALL LETTER ZATA | 2c8c COPTIC CAPITAL LETTER ZATA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c8f COPTIC SMALL LETTER HATE | 2c8e COPTIC CAPITAL LETTER HATE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c91 COPTIC SMALL LETTER THETHE | 2c90 COPTIC CAPITAL LETTER THETHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c93 COPTIC SMALL LETTER IAUDA | 2c92 COPTIC CAPITAL LETTER IAUDA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c95 COPTIC SMALL LETTER KAPA | 2c94 COPTIC CAPITAL LETTER KAPA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c97 COPTIC SMALL LETTER LAULA | 2c96 COPTIC CAPITAL LETTER LAULA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c99 COPTIC SMALL LETTER MI | 2c98 COPTIC CAPITAL LETTER MI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c9b COPTIC SMALL LETTER NI | 2c9a COPTIC CAPITAL LETTER NI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c9d COPTIC SMALL LETTER KSI | 2c9c COPTIC CAPITAL LETTER KSI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c9f COPTIC SMALL LETTER O | 2c9e COPTIC CAPITAL LETTER O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ca1 COPTIC SMALL LETTER PI | 2ca0 COPTIC CAPITAL LETTER PI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ca3 COPTIC SMALL LETTER RO | 2ca2 COPTIC CAPITAL LETTER RO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ca5 COPTIC SMALL LETTER SIMA | 2ca4 COPTIC CAPITAL LETTER SIMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ca7 COPTIC SMALL LETTER TAU | 2ca6 COPTIC CAPITAL LETTER TAU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ca9 COPTIC SMALL LETTER UA | 2ca8 COPTIC CAPITAL LETTER UA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cab COPTIC SMALL LETTER FI | 2caa COPTIC CAPITAL LETTER FI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cad COPTIC SMALL LETTER KHI | 2cac COPTIC CAPITAL LETTER KHI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2caf COPTIC SMALL LETTER PSI | 2cae COPTIC CAPITAL LETTER PSI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cb1 COPTIC SMALL LETTER OOU | 2cb0 COPTIC CAPITAL LETTER OOU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cb3 COPTIC SMALL LETTER DIALECT-P ALEF | 2cb2 COPTIC CAPITAL LETTER DIALECT-P ALEF */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cb5 COPTIC SMALL LETTER OLD COPTIC AIN | 2cb4 COPTIC CAPITAL LETTER OLD COPTIC AIN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cb7 COPTIC SMALL LETTER CRYPTOGRAMMIC EIE | 2cb6 COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cb9 COPTIC SMALL LETTER DIALECT-P KAPA | 2cb8 COPTIC CAPITAL LETTER DIALECT-P KAPA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cbb COPTIC SMALL LETTER DIALECT-P NI | 2cba COPTIC CAPITAL LETTER DIALECT-P NI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cbd COPTIC SMALL LETTER CRYPTOGRAMMIC NI | 2cbc COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cbf COPTIC SMALL LETTER OLD COPTIC OOU | 2cbe COPTIC CAPITAL LETTER OLD COPTIC OOU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cc1 COPTIC SMALL LETTER SAMPI | 2cc0 COPTIC CAPITAL LETTER SAMPI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cc3 COPTIC SMALL LETTER CROSSED SHEI | 2cc2 COPTIC CAPITAL LETTER CROSSED SHEI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cc5 COPTIC SMALL LETTER OLD COPTIC SHEI | 2cc4 COPTIC CAPITAL LETTER OLD COPTIC SHEI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cc7 COPTIC SMALL LETTER OLD COPTIC ESH | 2cc6 COPTIC CAPITAL LETTER OLD COPTIC ESH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cc9 COPTIC SMALL LETTER AKHMIMIC KHEI | 2cc8 COPTIC CAPITAL LETTER AKHMIMIC KHEI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ccb COPTIC SMALL LETTER DIALECT-P HORI | 2cca COPTIC CAPITAL LETTER DIALECT-P HORI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ccd COPTIC SMALL LETTER OLD COPTIC HORI | 2ccc COPTIC CAPITAL LETTER OLD COPTIC HORI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ccf COPTIC SMALL LETTER OLD COPTIC HA | 2cce COPTIC CAPITAL LETTER OLD COPTIC HA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cd1 COPTIC SMALL LETTER L-SHAPED HA | 2cd0 COPTIC CAPITAL LETTER L-SHAPED HA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cd3 COPTIC SMALL LETTER OLD COPTIC HEI | 2cd2 COPTIC CAPITAL LETTER OLD COPTIC HEI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cd5 COPTIC SMALL LETTER OLD COPTIC HAT | 2cd4 COPTIC CAPITAL LETTER OLD COPTIC HAT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cd7 COPTIC SMALL LETTER OLD COPTIC GANGIA | 2cd6 COPTIC CAPITAL LETTER OLD COPTIC GANGIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cd9 COPTIC SMALL LETTER OLD COPTIC DJA | 2cd8 COPTIC CAPITAL LETTER OLD COPTIC DJA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cdb COPTIC SMALL LETTER OLD COPTIC SHIMA | 2cda COPTIC CAPITAL LETTER OLD COPTIC SHIMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cdd COPTIC SMALL LETTER OLD NUBIAN SHIMA | 2cdc COPTIC CAPITAL LETTER OLD NUBIAN SHIMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cdf COPTIC SMALL LETTER OLD NUBIAN NGI | 2cde COPTIC CAPITAL LETTER OLD NUBIAN NGI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ce1 COPTIC SMALL LETTER OLD NUBIAN NYI | 2ce0 COPTIC CAPITAL LETTER OLD NUBIAN NYI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ce3 COPTIC SMALL LETTER OLD NUBIAN WAU | 2ce2 COPTIC CAPITAL LETTER OLD NUBIAN WAU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_LOWER, /* 2ce5 COPTIC SYMBOL MI RO | 2ce4 COPTIC SYMBOL KAI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ce7 COPTIC SYMBOL STAUROS | 2ce6 COPTIC SYMBOL PI RO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ce9 COPTIC SYMBOL KHI RO | 2ce8 COPTIC SYMBOL TAU RO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_OTHER, /* 2ceb COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHE | 2cea COPTIC SYMBOL SHIMA SIMA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 2ced COPTIC CAPITAL LETTER CRYPTOGRAMMIC GAN | 2cec COPTIC SMALL LETTER CRYPTOGRAMMIC SHEI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_LOWER, /* 2cef COPTIC COMBINING NI ABOVE | 2cee COPTIC SMALL LETTER CRYPTOGRAMMIC GANGI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2cf1 COPTIC COMBINING SPIRITUS LENIS | 2cf0 COPTIC COMBINING SPIRITUS ASPER */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cf3 COPTIC SMALL LETTER BOHAIRIC KHEI | 2cf2 COPTIC CAPITAL LETTER BOHAIRIC KHEI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2cf5 (null) | 2cf4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2cf7 (null) | 2cf6 (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_UNDEF, /* 2cf9 COPTIC OLD NUBIAN FULL STOP | 2cf8 (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2cfb COPTIC OLD NUBIAN INDIRECT QUESTION MAR | 2cfa COPTIC OLD NUBIAN DIRECT QUESTION MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 2cfd COPTIC FRACTION ONE HALF | 2cfc COPTIC OLD NUBIAN VERSE DIVIDER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2cff COPTIC MORPHOLOGICAL DIVIDER | 2cfe COPTIC FULL STOP */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d01 GEORGIAN SMALL LETTER BAN | 2d00 GEORGIAN SMALL LETTER AN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d03 GEORGIAN SMALL LETTER DON | 2d02 GEORGIAN SMALL LETTER GAN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d05 GEORGIAN SMALL LETTER VIN | 2d04 GEORGIAN SMALL LETTER EN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d07 GEORGIAN SMALL LETTER TAN | 2d06 GEORGIAN SMALL LETTER ZEN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d09 GEORGIAN SMALL LETTER KAN | 2d08 GEORGIAN SMALL LETTER IN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d0b GEORGIAN SMALL LETTER MAN | 2d0a GEORGIAN SMALL LETTER LAS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d0d GEORGIAN SMALL LETTER ON | 2d0c GEORGIAN SMALL LETTER NAR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d0f GEORGIAN SMALL LETTER ZHAR | 2d0e GEORGIAN SMALL LETTER PAR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d11 GEORGIAN SMALL LETTER SAN | 2d10 GEORGIAN SMALL LETTER RAE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d13 GEORGIAN SMALL LETTER UN | 2d12 GEORGIAN SMALL LETTER TAR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d15 GEORGIAN SMALL LETTER KHAR | 2d14 GEORGIAN SMALL LETTER PHAR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d17 GEORGIAN SMALL LETTER QAR | 2d16 GEORGIAN SMALL LETTER GHAN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d19 GEORGIAN SMALL LETTER CHIN | 2d18 GEORGIAN SMALL LETTER SHIN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d1b GEORGIAN SMALL LETTER JIL | 2d1a GEORGIAN SMALL LETTER CAN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d1d GEORGIAN SMALL LETTER CHAR | 2d1c GEORGIAN SMALL LETTER CIL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d1f GEORGIAN SMALL LETTER JHAN | 2d1e GEORGIAN SMALL LETTER XAN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d21 GEORGIAN SMALL LETTER HE | 2d20 GEORGIAN SMALL LETTER HAE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d23 GEORGIAN SMALL LETTER WE | 2d22 GEORGIAN SMALL LETTER HIE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d25 GEORGIAN SMALL LETTER HOE | 2d24 GEORGIAN SMALL LETTER HAR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UNDEF, /* 2d27 GEORGIAN SMALL LETTER YN | 2d26 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d29 (null) | 2d28 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d2b (null) | 2d2a (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UNDEF, /* 2d2d GEORGIAN SMALL LETTER AEN | 2d2c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d2f (null) | 2d2e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d31 TIFINAGH LETTER YAB | 2d30 TIFINAGH LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d33 TIFINAGH LETTER YAG | 2d32 TIFINAGH LETTER YABH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d35 TIFINAGH LETTER BERBER ACADEMY YAJ | 2d34 TIFINAGH LETTER YAGHH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d37 TIFINAGH LETTER YAD | 2d36 TIFINAGH LETTER YAJ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d39 TIFINAGH LETTER YADD | 2d38 TIFINAGH LETTER YADH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d3b TIFINAGH LETTER YEY | 2d3a TIFINAGH LETTER YADDH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d3d TIFINAGH LETTER YAK | 2d3c TIFINAGH LETTER YAF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d3f TIFINAGH LETTER YAKHH | 2d3e TIFINAGH LETTER TUAREG YAK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d41 TIFINAGH LETTER BERBER ACADEMY YAH | 2d40 TIFINAGH LETTER YAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d43 TIFINAGH LETTER YAHH | 2d42 TIFINAGH LETTER TUAREG YAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d45 TIFINAGH LETTER YAKH | 2d44 TIFINAGH LETTER YAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d47 TIFINAGH LETTER YAQ | 2d46 TIFINAGH LETTER TUAREG YAKH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d49 TIFINAGH LETTER YI | 2d48 TIFINAGH LETTER TUAREG YAQ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d4b TIFINAGH LETTER AHAGGAR YAZH | 2d4a TIFINAGH LETTER YAZH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d4d TIFINAGH LETTER YAL | 2d4c TIFINAGH LETTER TUAREG YAZH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d4f TIFINAGH LETTER YAN | 2d4e TIFINAGH LETTER YAM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d51 TIFINAGH LETTER TUAREG YANG | 2d50 TIFINAGH LETTER TUAREG YAGN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d53 TIFINAGH LETTER YU | 2d52 TIFINAGH LETTER YAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d55 TIFINAGH LETTER YARR | 2d54 TIFINAGH LETTER YAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d57 TIFINAGH LETTER TUAREG YAGH | 2d56 TIFINAGH LETTER YAGH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d59 TIFINAGH LETTER YAS | 2d58 TIFINAGH LETTER AYER YAGH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d5b TIFINAGH LETTER YASH | 2d5a TIFINAGH LETTER YASS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d5d TIFINAGH LETTER YATH | 2d5c TIFINAGH LETTER YAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d5f TIFINAGH LETTER YATT | 2d5e TIFINAGH LETTER YACH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d61 TIFINAGH LETTER YAW | 2d60 TIFINAGH LETTER YAV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d63 TIFINAGH LETTER YAZ | 2d62 TIFINAGH LETTER YAY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d65 TIFINAGH LETTER YAZZ | 2d64 TIFINAGH LETTER TAWELLEMET YAZ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d67 TIFINAGH LETTER YO | 2d66 TIFINAGH LETTER YE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d69 (null) | 2d68 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d6b (null) | 2d6a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d6d (null) | 2d6c (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 2d6f TIFINAGH MODIFIER LETTER LABIALIZATION | 2d6e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 2d71 (null) | 2d70 TIFINAGH SEPARATOR MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d73 (null) | 2d72 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d75 (null) | 2d74 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d77 (null) | 2d76 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d79 (null) | 2d78 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d7b (null) | 2d7a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d7d (null) | 2d7c (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 2d7f TIFINAGH CONSONANT JOINER | 2d7e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d81 ETHIOPIC SYLLABLE MOA | 2d80 ETHIOPIC SYLLABLE LOA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d83 ETHIOPIC SYLLABLE SOA | 2d82 ETHIOPIC SYLLABLE ROA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d85 ETHIOPIC SYLLABLE BOA | 2d84 ETHIOPIC SYLLABLE SHOA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d87 ETHIOPIC SYLLABLE COA | 2d86 ETHIOPIC SYLLABLE TOA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d89 ETHIOPIC SYLLABLE NYOA | 2d88 ETHIOPIC SYLLABLE NOA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d8b ETHIOPIC SYLLABLE ZOA | 2d8a ETHIOPIC SYLLABLE GLOTTAL OA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d8d ETHIOPIC SYLLABLE DDOA | 2d8c ETHIOPIC SYLLABLE DOA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d8f ETHIOPIC SYLLABLE THOA | 2d8e ETHIOPIC SYLLABLE JOA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d91 ETHIOPIC SYLLABLE PHOA | 2d90 ETHIOPIC SYLLABLE CHOA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d93 ETHIOPIC SYLLABLE GGWA | 2d92 ETHIOPIC SYLLABLE POA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d95 ETHIOPIC SYLLABLE GGWEE | 2d94 ETHIOPIC SYLLABLE GGWI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2d97 (null) | 2d96 ETHIOPIC SYLLABLE GGWE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d99 (null) | 2d98 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d9b (null) | 2d9a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d9d (null) | 2d9c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d9f (null) | 2d9e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2da1 ETHIOPIC SYLLABLE SSU | 2da0 ETHIOPIC SYLLABLE SSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2da3 ETHIOPIC SYLLABLE SSAA | 2da2 ETHIOPIC SYLLABLE SSI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2da5 ETHIOPIC SYLLABLE SSE | 2da4 ETHIOPIC SYLLABLE SSEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2da7 (null) | 2da6 ETHIOPIC SYLLABLE SSO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2da9 ETHIOPIC SYLLABLE CCU | 2da8 ETHIOPIC SYLLABLE CCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dab ETHIOPIC SYLLABLE CCAA | 2daa ETHIOPIC SYLLABLE CCI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dad ETHIOPIC SYLLABLE CCE | 2dac ETHIOPIC SYLLABLE CCEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2daf (null) | 2dae ETHIOPIC SYLLABLE CCO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2db1 ETHIOPIC SYLLABLE ZZU | 2db0 ETHIOPIC SYLLABLE ZZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2db3 ETHIOPIC SYLLABLE ZZAA | 2db2 ETHIOPIC SYLLABLE ZZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2db5 ETHIOPIC SYLLABLE ZZE | 2db4 ETHIOPIC SYLLABLE ZZEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2db7 (null) | 2db6 ETHIOPIC SYLLABLE ZZO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2db9 ETHIOPIC SYLLABLE CCHU | 2db8 ETHIOPIC SYLLABLE CCHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dbb ETHIOPIC SYLLABLE CCHAA | 2dba ETHIOPIC SYLLABLE CCHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dbd ETHIOPIC SYLLABLE CCHE | 2dbc ETHIOPIC SYLLABLE CCHEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2dbf (null) | 2dbe ETHIOPIC SYLLABLE CCHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dc1 ETHIOPIC SYLLABLE QYU | 2dc0 ETHIOPIC SYLLABLE QYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dc3 ETHIOPIC SYLLABLE QYAA | 2dc2 ETHIOPIC SYLLABLE QYI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dc5 ETHIOPIC SYLLABLE QYE | 2dc4 ETHIOPIC SYLLABLE QYEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2dc7 (null) | 2dc6 ETHIOPIC SYLLABLE QYO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dc9 ETHIOPIC SYLLABLE KYU | 2dc8 ETHIOPIC SYLLABLE KYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dcb ETHIOPIC SYLLABLE KYAA | 2dca ETHIOPIC SYLLABLE KYI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dcd ETHIOPIC SYLLABLE KYE | 2dcc ETHIOPIC SYLLABLE KYEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2dcf (null) | 2dce ETHIOPIC SYLLABLE KYO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dd1 ETHIOPIC SYLLABLE XYU | 2dd0 ETHIOPIC SYLLABLE XYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dd3 ETHIOPIC SYLLABLE XYAA | 2dd2 ETHIOPIC SYLLABLE XYI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dd5 ETHIOPIC SYLLABLE XYE | 2dd4 ETHIOPIC SYLLABLE XYEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2dd7 (null) | 2dd6 ETHIOPIC SYLLABLE XYO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dd9 ETHIOPIC SYLLABLE GYU | 2dd8 ETHIOPIC SYLLABLE GYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2ddb ETHIOPIC SYLLABLE GYAA | 2dda ETHIOPIC SYLLABLE GYI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2ddd ETHIOPIC SYLLABLE GYE | 2ddc ETHIOPIC SYLLABLE GYEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2ddf (null) | 2dde ETHIOPIC SYLLABLE GYO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2de1 COMBINING CYRILLIC LETTER VE | 2de0 COMBINING CYRILLIC LETTER BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2de3 COMBINING CYRILLIC LETTER DE | 2de2 COMBINING CYRILLIC LETTER GHE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2de5 COMBINING CYRILLIC LETTER ZE | 2de4 COMBINING CYRILLIC LETTER ZHE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2de7 COMBINING CYRILLIC LETTER EL | 2de6 COMBINING CYRILLIC LETTER KA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2de9 COMBINING CYRILLIC LETTER EN | 2de8 COMBINING CYRILLIC LETTER EM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2deb COMBINING CYRILLIC LETTER PE | 2dea COMBINING CYRILLIC LETTER O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ded COMBINING CYRILLIC LETTER ES | 2dec COMBINING CYRILLIC LETTER ER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2def COMBINING CYRILLIC LETTER HA | 2dee COMBINING CYRILLIC LETTER TE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2df1 COMBINING CYRILLIC LETTER CHE | 2df0 COMBINING CYRILLIC LETTER TSE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2df3 COMBINING CYRILLIC LETTER SHCHA | 2df2 COMBINING CYRILLIC LETTER SHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2df5 COMBINING CYRILLIC LETTER ES-TE | 2df4 COMBINING CYRILLIC LETTER FITA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2df7 COMBINING CYRILLIC LETTER IE | 2df6 COMBINING CYRILLIC LETTER A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2df9 COMBINING CYRILLIC LETTER MONOGRAPH UK | 2df8 COMBINING CYRILLIC LETTER DJERV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2dfb COMBINING CYRILLIC LETTER YU | 2dfa COMBINING CYRILLIC LETTER YAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2dfd COMBINING CYRILLIC LETTER LITTLE YUS | 2dfc COMBINING CYRILLIC LETTER IOTIFIED A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2dff COMBINING CYRILLIC LETTER IOTIFIED BIG | 2dfe COMBINING CYRILLIC LETTER BIG YUS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e01 RIGHT ANGLE DOTTED SUBSTITUTION MARKER | 2e00 RIGHT ANGLE SUBSTITUTION MARKER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e03 RIGHT SUBSTITUTION BRACKET | 2e02 LEFT SUBSTITUTION BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e05 RIGHT DOTTED SUBSTITUTION BRACKET | 2e04 LEFT DOTTED SUBSTITUTION BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e07 RAISED DOTTED INTERPOLATION MARKER | 2e06 RAISED INTERPOLATION MARKER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e09 LEFT TRANSPOSITION BRACKET | 2e08 DOTTED TRANSPOSITION MARKER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e0b RAISED SQUARE | 2e0a RIGHT TRANSPOSITION BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e0d RIGHT RAISED OMISSION BRACKET | 2e0c LEFT RAISED OMISSION BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e0f PARAGRAPHOS | 2e0e EDITORIAL CORONIS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e11 REVERSED FORKED PARAGRAPHOS | 2e10 FORKED PARAGRAPHOS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e13 DOTTED OBELOS | 2e12 HYPODIASTOLE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e15 UPWARDS ANCORA | 2e14 DOWNWARDS ANCORA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e17 DOUBLE OBLIQUE HYPHEN | 2e16 DOTTED RIGHT-POINTING ANGLE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e19 PALM BRANCH | 2e18 INVERTED INTERROBANG */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e1b TILDE WITH RING ABOVE | 2e1a HYPHEN WITH DIAERESIS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e1d RIGHT LOW PARAPHRASE BRACKET | 2e1c LEFT LOW PARAPHRASE BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e1f TILDE WITH DOT BELOW | 2e1e TILDE WITH DOT ABOVE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e21 RIGHT VERTICAL BAR WITH QUILL | 2e20 LEFT VERTICAL BAR WITH QUILL */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e23 TOP RIGHT HALF BRACKET | 2e22 TOP LEFT HALF BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e25 BOTTOM RIGHT HALF BRACKET | 2e24 BOTTOM LEFT HALF BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e27 RIGHT SIDEWAYS U BRACKET | 2e26 LEFT SIDEWAYS U BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e29 RIGHT DOUBLE PARENTHESIS | 2e28 LEFT DOUBLE PARENTHESIS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e2b ONE DOT OVER TWO DOTS PUNCTUATION | 2e2a TWO DOTS OVER ONE DOT PUNCTUATION */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e2d FIVE DOT MARK | 2e2c SQUARED FOUR DOT PUNCTUATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 2e2f VERTICAL TILDE | 2e2e REVERSED QUESTION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e31 WORD SEPARATOR MIDDLE DOT | 2e30 RING POINT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e33 RAISED DOT | 2e32 TURNED COMMA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e35 TURNED SEMICOLON | 2e34 RAISED COMMA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e37 DAGGER WITH RIGHT GUARD | 2e36 DAGGER WITH LEFT GUARD */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e39 TOP HALF SECTION SIGN | 2e38 TURNED DAGGER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e3b THREE-EM DASH | 2e3a TWO-EM DASH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e3d (null) | 2e3c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e3f (null) | 2e3e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e41 (null) | 2e40 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e43 (null) | 2e42 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e45 (null) | 2e44 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e47 (null) | 2e46 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e49 (null) | 2e48 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e4b (null) | 2e4a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e4d (null) | 2e4c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e4f (null) | 2e4e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e51 (null) | 2e50 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e53 (null) | 2e52 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e55 (null) | 2e54 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e57 (null) | 2e56 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e59 (null) | 2e58 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e5b (null) | 2e5a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e5d (null) | 2e5c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e5f (null) | 2e5e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e61 (null) | 2e60 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e63 (null) | 2e62 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e65 (null) | 2e64 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e67 (null) | 2e66 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e69 (null) | 2e68 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e6b (null) | 2e6a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e6d (null) | 2e6c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e6f (null) | 2e6e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e71 (null) | 2e70 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e73 (null) | 2e72 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e75 (null) | 2e74 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e77 (null) | 2e76 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e79 (null) | 2e78 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e7b (null) | 2e7a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e7d (null) | 2e7c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e7f (null) | 2e7e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e81 CJK RADICAL CLIFF | 2e80 CJK RADICAL REPEAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e83 CJK RADICAL SECOND TWO | 2e82 CJK RADICAL SECOND ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e85 CJK RADICAL PERSON | 2e84 CJK RADICAL SECOND THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e87 CJK RADICAL TABLE | 2e86 CJK RADICAL BOX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e89 CJK RADICAL KNIFE TWO | 2e88 CJK RADICAL KNIFE ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e8b CJK RADICAL SEAL | 2e8a CJK RADICAL DIVINATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e8d CJK RADICAL SMALL TWO | 2e8c CJK RADICAL SMALL ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e8f CJK RADICAL LAME TWO | 2e8e CJK RADICAL LAME ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e91 CJK RADICAL LAME FOUR | 2e90 CJK RADICAL LAME THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e93 CJK RADICAL THREAD | 2e92 CJK RADICAL SNAKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e95 CJK RADICAL SNOUT TWO | 2e94 CJK RADICAL SNOUT ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e97 CJK RADICAL HEART TWO | 2e96 CJK RADICAL HEART ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e99 CJK RADICAL RAP | 2e98 CJK RADICAL HAND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 2e9b CJK RADICAL CHOKE | 2e9a (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e9d CJK RADICAL MOON | 2e9c CJK RADICAL SUN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e9f CJK RADICAL MOTHER | 2e9e CJK RADICAL DEATH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ea1 CJK RADICAL WATER ONE | 2ea0 CJK RADICAL CIVILIAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ea3 CJK RADICAL FIRE | 2ea2 CJK RADICAL WATER TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ea5 CJK RADICAL PAW TWO | 2ea4 CJK RADICAL PAW ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ea7 CJK RADICAL COW | 2ea6 CJK RADICAL SIMPLIFIED HALF TREE TRUNK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ea9 CJK RADICAL JADE | 2ea8 CJK RADICAL DOG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eab CJK RADICAL EYE | 2eaa CJK RADICAL BOLT OF CLOTH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ead CJK RADICAL SPIRIT TWO | 2eac CJK RADICAL SPIRIT ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eaf CJK RADICAL SILK | 2eae CJK RADICAL BAMBOO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eb1 CJK RADICAL NET ONE | 2eb0 CJK RADICAL C-SIMPLIFIED SILK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eb3 CJK RADICAL NET THREE | 2eb2 CJK RADICAL NET TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eb5 CJK RADICAL MESH | 2eb4 CJK RADICAL NET FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eb7 CJK RADICAL RAM | 2eb6 CJK RADICAL SHEEP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eb9 CJK RADICAL OLD | 2eb8 CJK RADICAL EWE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ebb CJK RADICAL BRUSH TWO | 2eba CJK RADICAL BRUSH ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ebd CJK RADICAL MORTAR | 2ebc CJK RADICAL MEAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ebf CJK RADICAL GRASS TWO | 2ebe CJK RADICAL GRASS ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ec1 CJK RADICAL TIGER | 2ec0 CJK RADICAL GRASS THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ec3 CJK RADICAL WEST ONE | 2ec2 CJK RADICAL CLOTHES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ec5 CJK RADICAL C-SIMPLIFIED SEE | 2ec4 CJK RADICAL WEST TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ec7 CJK RADICAL HORN | 2ec6 CJK RADICAL SIMPLIFIED HORN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ec9 CJK RADICAL C-SIMPLIFIED SHELL | 2ec8 CJK RADICAL C-SIMPLIFIED SPEECH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ecb CJK RADICAL C-SIMPLIFIED CART | 2eca CJK RADICAL FOOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ecd CJK RADICAL WALK ONE | 2ecc CJK RADICAL SIMPLIFIED WALK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ecf CJK RADICAL CITY | 2ece CJK RADICAL WALK TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ed1 CJK RADICAL LONG ONE | 2ed0 CJK RADICAL C-SIMPLIFIED GOLD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ed3 CJK RADICAL C-SIMPLIFIED LONG | 2ed2 CJK RADICAL LONG TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ed5 CJK RADICAL MOUND ONE | 2ed4 CJK RADICAL C-SIMPLIFIED GATE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ed7 CJK RADICAL RAIN | 2ed6 CJK RADICAL MOUND TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ed9 CJK RADICAL C-SIMPLIFIED TANNED LEATHER | 2ed8 CJK RADICAL BLUE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2edb CJK RADICAL C-SIMPLIFIED WIND | 2eda CJK RADICAL C-SIMPLIFIED LEAF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2edd CJK RADICAL EAT ONE | 2edc CJK RADICAL C-SIMPLIFIED FLY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2edf CJK RADICAL EAT THREE | 2ede CJK RADICAL EAT TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ee1 CJK RADICAL HEAD | 2ee0 CJK RADICAL C-SIMPLIFIED EAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ee3 CJK RADICAL BONE | 2ee2 CJK RADICAL C-SIMPLIFIED HORSE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ee5 CJK RADICAL C-SIMPLIFIED FISH | 2ee4 CJK RADICAL GHOST */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ee7 CJK RADICAL C-SIMPLIFIED SALT | 2ee6 CJK RADICAL C-SIMPLIFIED BIRD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ee9 CJK RADICAL SIMPLIFIED YELLOW | 2ee8 CJK RADICAL SIMPLIFIED WHEAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eeb CJK RADICAL J-SIMPLIFIED EVEN | 2eea CJK RADICAL C-SIMPLIFIED FROG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eed CJK RADICAL J-SIMPLIFIED TOOTH | 2eec CJK RADICAL C-SIMPLIFIED EVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eef CJK RADICAL J-SIMPLIFIED DRAGON | 2eee CJK RADICAL C-SIMPLIFIED TOOTH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ef1 CJK RADICAL TURTLE | 2ef0 CJK RADICAL C-SIMPLIFIED DRAGON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ef3 CJK RADICAL C-SIMPLIFIED TURTLE | 2ef2 CJK RADICAL J-SIMPLIFIED TURTLE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ef5 (null) | 2ef4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ef7 (null) | 2ef6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ef9 (null) | 2ef8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2efb (null) | 2efa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2efd (null) | 2efc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2eff (null) | 2efe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f01 KANGXI RADICAL LINE | 2f00 KANGXI RADICAL ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f03 KANGXI RADICAL SLASH | 2f02 KANGXI RADICAL DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f05 KANGXI RADICAL HOOK | 2f04 KANGXI RADICAL SECOND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f07 KANGXI RADICAL LID | 2f06 KANGXI RADICAL TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f09 KANGXI RADICAL LEGS | 2f08 KANGXI RADICAL MAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f0b KANGXI RADICAL EIGHT | 2f0a KANGXI RADICAL ENTER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f0d KANGXI RADICAL COVER | 2f0c KANGXI RADICAL DOWN BOX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f0f KANGXI RADICAL TABLE | 2f0e KANGXI RADICAL ICE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f11 KANGXI RADICAL KNIFE | 2f10 KANGXI RADICAL OPEN BOX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f13 KANGXI RADICAL WRAP | 2f12 KANGXI RADICAL POWER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f15 KANGXI RADICAL RIGHT OPEN BOX | 2f14 KANGXI RADICAL SPOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f17 KANGXI RADICAL TEN | 2f16 KANGXI RADICAL HIDING ENCLOSURE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f19 KANGXI RADICAL SEAL | 2f18 KANGXI RADICAL DIVINATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f1b KANGXI RADICAL PRIVATE | 2f1a KANGXI RADICAL CLIFF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f1d KANGXI RADICAL MOUTH | 2f1c KANGXI RADICAL AGAIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f1f KANGXI RADICAL EARTH | 2f1e KANGXI RADICAL ENCLOSURE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f21 KANGXI RADICAL GO | 2f20 KANGXI RADICAL SCHOLAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f23 KANGXI RADICAL EVENING | 2f22 KANGXI RADICAL GO SLOWLY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f25 KANGXI RADICAL WOMAN | 2f24 KANGXI RADICAL BIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f27 KANGXI RADICAL ROOF | 2f26 KANGXI RADICAL CHILD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f29 KANGXI RADICAL SMALL | 2f28 KANGXI RADICAL INCH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f2b KANGXI RADICAL CORPSE | 2f2a KANGXI RADICAL LAME */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f2d KANGXI RADICAL MOUNTAIN | 2f2c KANGXI RADICAL SPROUT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f2f KANGXI RADICAL WORK | 2f2e KANGXI RADICAL RIVER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f31 KANGXI RADICAL TURBAN | 2f30 KANGXI RADICAL ONESELF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f33 KANGXI RADICAL SHORT THREAD | 2f32 KANGXI RADICAL DRY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f35 KANGXI RADICAL LONG STRIDE | 2f34 KANGXI RADICAL DOTTED CLIFF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f37 KANGXI RADICAL SHOOT | 2f36 KANGXI RADICAL TWO HANDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f39 KANGXI RADICAL SNOUT | 2f38 KANGXI RADICAL BOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f3b KANGXI RADICAL STEP | 2f3a KANGXI RADICAL BRISTLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f3d KANGXI RADICAL HALBERD | 2f3c KANGXI RADICAL HEART */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f3f KANGXI RADICAL HAND | 2f3e KANGXI RADICAL DOOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f41 KANGXI RADICAL RAP | 2f40 KANGXI RADICAL BRANCH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f43 KANGXI RADICAL DIPPER | 2f42 KANGXI RADICAL SCRIPT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f45 KANGXI RADICAL SQUARE | 2f44 KANGXI RADICAL AXE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f47 KANGXI RADICAL SUN | 2f46 KANGXI RADICAL NOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f49 KANGXI RADICAL MOON | 2f48 KANGXI RADICAL SAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f4b KANGXI RADICAL LACK | 2f4a KANGXI RADICAL TREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f4d KANGXI RADICAL DEATH | 2f4c KANGXI RADICAL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f4f KANGXI RADICAL DO NOT | 2f4e KANGXI RADICAL WEAPON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f51 KANGXI RADICAL FUR | 2f50 KANGXI RADICAL COMPARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f53 KANGXI RADICAL STEAM | 2f52 KANGXI RADICAL CLAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f55 KANGXI RADICAL FIRE | 2f54 KANGXI RADICAL WATER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f57 KANGXI RADICAL FATHER | 2f56 KANGXI RADICAL CLAW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f59 KANGXI RADICAL HALF TREE TRUNK | 2f58 KANGXI RADICAL DOUBLE X */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f5b KANGXI RADICAL FANG | 2f5a KANGXI RADICAL SLICE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f5d KANGXI RADICAL DOG | 2f5c KANGXI RADICAL COW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f5f KANGXI RADICAL JADE | 2f5e KANGXI RADICAL PROFOUND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f61 KANGXI RADICAL TILE | 2f60 KANGXI RADICAL MELON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f63 KANGXI RADICAL LIFE | 2f62 KANGXI RADICAL SWEET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f65 KANGXI RADICAL FIELD | 2f64 KANGXI RADICAL USE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f67 KANGXI RADICAL SICKNESS | 2f66 KANGXI RADICAL BOLT OF CLOTH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f69 KANGXI RADICAL WHITE | 2f68 KANGXI RADICAL DOTTED TENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f6b KANGXI RADICAL DISH | 2f6a KANGXI RADICAL SKIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f6d KANGXI RADICAL SPEAR | 2f6c KANGXI RADICAL EYE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f6f KANGXI RADICAL STONE | 2f6e KANGXI RADICAL ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f71 KANGXI RADICAL TRACK | 2f70 KANGXI RADICAL SPIRIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f73 KANGXI RADICAL CAVE | 2f72 KANGXI RADICAL GRAIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f75 KANGXI RADICAL BAMBOO | 2f74 KANGXI RADICAL STAND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f77 KANGXI RADICAL SILK | 2f76 KANGXI RADICAL RICE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f79 KANGXI RADICAL NET | 2f78 KANGXI RADICAL JAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f7b KANGXI RADICAL FEATHER | 2f7a KANGXI RADICAL SHEEP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f7d KANGXI RADICAL AND | 2f7c KANGXI RADICAL OLD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f7f KANGXI RADICAL EAR | 2f7e KANGXI RADICAL PLOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f81 KANGXI RADICAL MEAT | 2f80 KANGXI RADICAL BRUSH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f83 KANGXI RADICAL SELF | 2f82 KANGXI RADICAL MINISTER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f85 KANGXI RADICAL MORTAR | 2f84 KANGXI RADICAL ARRIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f87 KANGXI RADICAL OPPOSE | 2f86 KANGXI RADICAL TONGUE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f89 KANGXI RADICAL STOPPING | 2f88 KANGXI RADICAL BOAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f8b KANGXI RADICAL GRASS | 2f8a KANGXI RADICAL COLOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f8d KANGXI RADICAL INSECT | 2f8c KANGXI RADICAL TIGER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f8f KANGXI RADICAL WALK ENCLOSURE | 2f8e KANGXI RADICAL BLOOD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f91 KANGXI RADICAL WEST | 2f90 KANGXI RADICAL CLOTHES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f93 KANGXI RADICAL HORN | 2f92 KANGXI RADICAL SEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f95 KANGXI RADICAL VALLEY | 2f94 KANGXI RADICAL SPEECH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f97 KANGXI RADICAL PIG | 2f96 KANGXI RADICAL BEAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f99 KANGXI RADICAL SHELL | 2f98 KANGXI RADICAL BADGER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f9b KANGXI RADICAL RUN | 2f9a KANGXI RADICAL RED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f9d KANGXI RADICAL BODY | 2f9c KANGXI RADICAL FOOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f9f KANGXI RADICAL BITTER | 2f9e KANGXI RADICAL CART */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fa1 KANGXI RADICAL WALK | 2fa0 KANGXI RADICAL MORNING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fa3 KANGXI RADICAL WINE | 2fa2 KANGXI RADICAL CITY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fa5 KANGXI RADICAL VILLAGE | 2fa4 KANGXI RADICAL DISTINGUISH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fa7 KANGXI RADICAL LONG | 2fa6 KANGXI RADICAL GOLD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fa9 KANGXI RADICAL MOUND | 2fa8 KANGXI RADICAL GATE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fab KANGXI RADICAL SHORT TAILED BIRD | 2faa KANGXI RADICAL SLAVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fad KANGXI RADICAL BLUE | 2fac KANGXI RADICAL RAIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2faf KANGXI RADICAL FACE | 2fae KANGXI RADICAL WRONG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fb1 KANGXI RADICAL TANNED LEATHER | 2fb0 KANGXI RADICAL LEATHER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fb3 KANGXI RADICAL SOUND | 2fb2 KANGXI RADICAL LEEK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fb5 KANGXI RADICAL WIND | 2fb4 KANGXI RADICAL LEAF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fb7 KANGXI RADICAL EAT | 2fb6 KANGXI RADICAL FLY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fb9 KANGXI RADICAL FRAGRANT | 2fb8 KANGXI RADICAL HEAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fbb KANGXI RADICAL BONE | 2fba KANGXI RADICAL HORSE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fbd KANGXI RADICAL HAIR | 2fbc KANGXI RADICAL TALL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fbf KANGXI RADICAL SACRIFICIAL WINE | 2fbe KANGXI RADICAL FIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fc1 KANGXI RADICAL GHOST | 2fc0 KANGXI RADICAL CAULDRON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fc3 KANGXI RADICAL BIRD | 2fc2 KANGXI RADICAL FISH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fc5 KANGXI RADICAL DEER | 2fc4 KANGXI RADICAL SALT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fc7 KANGXI RADICAL HEMP | 2fc6 KANGXI RADICAL WHEAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fc9 KANGXI RADICAL MILLET | 2fc8 KANGXI RADICAL YELLOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fcb KANGXI RADICAL EMBROIDERY | 2fca KANGXI RADICAL BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fcd KANGXI RADICAL TRIPOD | 2fcc KANGXI RADICAL FROG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fcf KANGXI RADICAL RAT | 2fce KANGXI RADICAL DRUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fd1 KANGXI RADICAL EVEN | 2fd0 KANGXI RADICAL NOSE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fd3 KANGXI RADICAL DRAGON | 2fd2 KANGXI RADICAL TOOTH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fd5 KANGXI RADICAL FLUTE | 2fd4 KANGXI RADICAL TURTLE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fd7 (null) | 2fd6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fd9 (null) | 2fd8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fdb (null) | 2fda (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fdd (null) | 2fdc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fdf (null) | 2fde (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fe1 (null) | 2fe0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fe3 (null) | 2fe2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fe5 (null) | 2fe4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fe7 (null) | 2fe6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fe9 (null) | 2fe8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2feb (null) | 2fea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fed (null) | 2fec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fef (null) | 2fee (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ff1 IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE | 2ff0 IDEOGRAPHIC DESCRIPTION CHARACTER LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ff3 IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE | 2ff2 IDEOGRAPHIC DESCRIPTION CHARACTER LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ff5 IDEOGRAPHIC DESCRIPTION CHARACTER SURRO | 2ff4 IDEOGRAPHIC DESCRIPTION CHARACTER FULL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ff7 IDEOGRAPHIC DESCRIPTION CHARACTER SURRO | 2ff6 IDEOGRAPHIC DESCRIPTION CHARACTER SURRO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ff9 IDEOGRAPHIC DESCRIPTION CHARACTER SURRO | 2ff8 IDEOGRAPHIC DESCRIPTION CHARACTER SURRO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ffb IDEOGRAPHIC DESCRIPTION CHARACTER OVERL | 2ffa IDEOGRAPHIC DESCRIPTION CHARACTER SURRO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ffd (null) | 2ffc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fff (null) | 2ffe (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_SPACE, /* 3001 IDEOGRAPHIC COMMA | 3000 IDEOGRAPHIC SPACE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 3003 DITTO MARK | 3002 IDEOGRAPHIC FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3005 IDEOGRAPHIC ITERATION MARK | 3004 JAPANESE INDUSTRIAL STANDARD SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 3007 IDEOGRAPHIC NUMBER ZERO | 3006 IDEOGRAPHIC CLOSING MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 3009 RIGHT ANGLE BRACKET | 3008 LEFT ANGLE BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 300b RIGHT DOUBLE ANGLE BRACKET | 300a LEFT DOUBLE ANGLE BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 300d RIGHT CORNER BRACKET | 300c LEFT CORNER BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 300f RIGHT WHITE CORNER BRACKET | 300e LEFT WHITE CORNER BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 3011 RIGHT BLACK LENTICULAR BRACKET | 3010 LEFT BLACK LENTICULAR BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3013 GETA MARK | 3012 POSTAL MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 3015 RIGHT TORTOISE SHELL BRACKET | 3014 LEFT TORTOISE SHELL BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 3017 RIGHT WHITE LENTICULAR BRACKET | 3016 LEFT WHITE LENTICULAR BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 3019 RIGHT WHITE TORTOISE SHELL BRACKET | 3018 LEFT WHITE TORTOISE SHELL BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 301b RIGHT WHITE SQUARE BRACKET | 301a LEFT WHITE SQUARE BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 301d REVERSED DOUBLE PRIME QUOTATION MARK | 301c WAVE DASH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 301f LOW DOUBLE PRIME QUOTATION MARK | 301e DOUBLE PRIME QUOTATION MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3021 HANGZHOU NUMERAL ONE | 3020 POSTAL MARK FACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3023 HANGZHOU NUMERAL THREE | 3022 HANGZHOU NUMERAL TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3025 HANGZHOU NUMERAL FIVE | 3024 HANGZHOU NUMERAL FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3027 HANGZHOU NUMERAL SEVEN | 3026 HANGZHOU NUMERAL SIX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3029 HANGZHOU NUMERAL NINE | 3028 HANGZHOU NUMERAL EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 302b IDEOGRAPHIC RISING TONE MARK | 302a IDEOGRAPHIC LEVEL TONE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 302d IDEOGRAPHIC ENTERING TONE MARK | 302c IDEOGRAPHIC DEPARTING TONE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 302f HANGUL DOUBLE DOT TONE MARK | 302e HANGUL SINGLE DOT TONE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 3031 VERTICAL KANA REPEAT MARK | 3030 WAVY DASH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3033 VERTICAL KANA REPEAT MARK UPPER HALF | 3032 VERTICAL KANA REPEAT WITH VOICED SOUND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3035 VERTICAL KANA REPEAT MARK LOWER HALF | 3034 VERTICAL KANA REPEAT WITH VOICED SOUND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3037 IDEOGRAPHIC TELEGRAPH LINE FEED SEPARAT | 3036 CIRCLED POSTAL MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3039 HANGZHOU NUMERAL TWENTY | 3038 HANGZHOU NUMERAL TEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 303b VERTICAL IDEOGRAPHIC ITERATION MARK | 303a HANGZHOU NUMERAL THIRTY */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 303d PART ALTERNATION MARK | 303c MASU MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 303f IDEOGRAPHIC HALF FILL SPACE | 303e IDEOGRAPHIC VARIATION INDICATOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 3041 HIRAGANA LETTER SMALL A | 3040 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3043 HIRAGANA LETTER SMALL I | 3042 HIRAGANA LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3045 HIRAGANA LETTER SMALL U | 3044 HIRAGANA LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3047 HIRAGANA LETTER SMALL E | 3046 HIRAGANA LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3049 HIRAGANA LETTER SMALL O | 3048 HIRAGANA LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 304b HIRAGANA LETTER KA | 304a HIRAGANA LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 304d HIRAGANA LETTER KI | 304c HIRAGANA LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 304f HIRAGANA LETTER KU | 304e HIRAGANA LETTER GI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3051 HIRAGANA LETTER KE | 3050 HIRAGANA LETTER GU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3053 HIRAGANA LETTER KO | 3052 HIRAGANA LETTER GE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3055 HIRAGANA LETTER SA | 3054 HIRAGANA LETTER GO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3057 HIRAGANA LETTER SI | 3056 HIRAGANA LETTER ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3059 HIRAGANA LETTER SU | 3058 HIRAGANA LETTER ZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 305b HIRAGANA LETTER SE | 305a HIRAGANA LETTER ZU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 305d HIRAGANA LETTER SO | 305c HIRAGANA LETTER ZE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 305f HIRAGANA LETTER TA | 305e HIRAGANA LETTER ZO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3061 HIRAGANA LETTER TI | 3060 HIRAGANA LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3063 HIRAGANA LETTER SMALL TU | 3062 HIRAGANA LETTER DI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3065 HIRAGANA LETTER DU | 3064 HIRAGANA LETTER TU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3067 HIRAGANA LETTER DE | 3066 HIRAGANA LETTER TE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3069 HIRAGANA LETTER DO | 3068 HIRAGANA LETTER TO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 306b HIRAGANA LETTER NI | 306a HIRAGANA LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 306d HIRAGANA LETTER NE | 306c HIRAGANA LETTER NU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 306f HIRAGANA LETTER HA | 306e HIRAGANA LETTER NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3071 HIRAGANA LETTER PA | 3070 HIRAGANA LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3073 HIRAGANA LETTER BI | 3072 HIRAGANA LETTER HI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3075 HIRAGANA LETTER HU | 3074 HIRAGANA LETTER PI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3077 HIRAGANA LETTER PU | 3076 HIRAGANA LETTER BU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3079 HIRAGANA LETTER BE | 3078 HIRAGANA LETTER HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 307b HIRAGANA LETTER HO | 307a HIRAGANA LETTER PE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 307d HIRAGANA LETTER PO | 307c HIRAGANA LETTER BO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 307f HIRAGANA LETTER MI | 307e HIRAGANA LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3081 HIRAGANA LETTER ME | 3080 HIRAGANA LETTER MU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3083 HIRAGANA LETTER SMALL YA | 3082 HIRAGANA LETTER MO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3085 HIRAGANA LETTER SMALL YU | 3084 HIRAGANA LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3087 HIRAGANA LETTER SMALL YO | 3086 HIRAGANA LETTER YU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3089 HIRAGANA LETTER RA | 3088 HIRAGANA LETTER YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 308b HIRAGANA LETTER RU | 308a HIRAGANA LETTER RI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 308d HIRAGANA LETTER RO | 308c HIRAGANA LETTER RE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 308f HIRAGANA LETTER WA | 308e HIRAGANA LETTER SMALL WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3091 HIRAGANA LETTER WE | 3090 HIRAGANA LETTER WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3093 HIRAGANA LETTER N | 3092 HIRAGANA LETTER WO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3095 HIRAGANA LETTER SMALL KA | 3094 HIRAGANA LETTER VU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 3097 (null) | 3096 HIRAGANA LETTER SMALL KE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 3099 COMBINING KATAKANA-HIRAGANA VOICED SOUN | 3098 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 309b KATAKANA-HIRAGANA VOICED SOUND MARK | 309a COMBINING KATAKANA-HIRAGANA SEMI-VOICED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 309d HIRAGANA ITERATION MARK | 309c KATAKANA-HIRAGANA SEMI-VOICED SOUND MAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 309f HIRAGANA DIGRAPH YORI | 309e HIRAGANA VOICED ITERATION MARK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_PUNCT, /* 30a1 KATAKANA LETTER SMALL A | 30a0 KATAKANA-HIRAGANA DOUBLE HYPHEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30a3 KATAKANA LETTER SMALL I | 30a2 KATAKANA LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30a5 KATAKANA LETTER SMALL U | 30a4 KATAKANA LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30a7 KATAKANA LETTER SMALL E | 30a6 KATAKANA LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30a9 KATAKANA LETTER SMALL O | 30a8 KATAKANA LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30ab KATAKANA LETTER KA | 30aa KATAKANA LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30ad KATAKANA LETTER KI | 30ac KATAKANA LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30af KATAKANA LETTER KU | 30ae KATAKANA LETTER GI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30b1 KATAKANA LETTER KE | 30b0 KATAKANA LETTER GU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30b3 KATAKANA LETTER KO | 30b2 KATAKANA LETTER GE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30b5 KATAKANA LETTER SA | 30b4 KATAKANA LETTER GO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30b7 KATAKANA LETTER SI | 30b6 KATAKANA LETTER ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30b9 KATAKANA LETTER SU | 30b8 KATAKANA LETTER ZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30bb KATAKANA LETTER SE | 30ba KATAKANA LETTER ZU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30bd KATAKANA LETTER SO | 30bc KATAKANA LETTER ZE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30bf KATAKANA LETTER TA | 30be KATAKANA LETTER ZO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30c1 KATAKANA LETTER TI | 30c0 KATAKANA LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30c3 KATAKANA LETTER SMALL TU | 30c2 KATAKANA LETTER DI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30c5 KATAKANA LETTER DU | 30c4 KATAKANA LETTER TU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30c7 KATAKANA LETTER DE | 30c6 KATAKANA LETTER TE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30c9 KATAKANA LETTER DO | 30c8 KATAKANA LETTER TO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30cb KATAKANA LETTER NI | 30ca KATAKANA LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30cd KATAKANA LETTER NE | 30cc KATAKANA LETTER NU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30cf KATAKANA LETTER HA | 30ce KATAKANA LETTER NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30d1 KATAKANA LETTER PA | 30d0 KATAKANA LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30d3 KATAKANA LETTER BI | 30d2 KATAKANA LETTER HI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30d5 KATAKANA LETTER HU | 30d4 KATAKANA LETTER PI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30d7 KATAKANA LETTER PU | 30d6 KATAKANA LETTER BU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30d9 KATAKANA LETTER BE | 30d8 KATAKANA LETTER HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30db KATAKANA LETTER HO | 30da KATAKANA LETTER PE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30dd KATAKANA LETTER PO | 30dc KATAKANA LETTER BO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30df KATAKANA LETTER MI | 30de KATAKANA LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30e1 KATAKANA LETTER ME | 30e0 KATAKANA LETTER MU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30e3 KATAKANA LETTER SMALL YA | 30e2 KATAKANA LETTER MO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30e5 KATAKANA LETTER SMALL YU | 30e4 KATAKANA LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30e7 KATAKANA LETTER SMALL YO | 30e6 KATAKANA LETTER YU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30e9 KATAKANA LETTER RA | 30e8 KATAKANA LETTER YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30eb KATAKANA LETTER RU | 30ea KATAKANA LETTER RI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30ed KATAKANA LETTER RO | 30ec KATAKANA LETTER RE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30ef KATAKANA LETTER WA | 30ee KATAKANA LETTER SMALL WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30f1 KATAKANA LETTER WE | 30f0 KATAKANA LETTER WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30f3 KATAKANA LETTER N | 30f2 KATAKANA LETTER WO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30f5 KATAKANA LETTER SMALL KA | 30f4 KATAKANA LETTER VU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30f7 KATAKANA LETTER VA | 30f6 KATAKANA LETTER SMALL KE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30f9 KATAKANA LETTER VE | 30f8 KATAKANA LETTER VI */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 30fb KATAKANA MIDDLE DOT | 30fa KATAKANA LETTER VO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 30fd KATAKANA ITERATION MARK | 30fc KATAKANA-HIRAGANA PROLONGED SOUND MARK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 30ff KATAKANA DIGRAPH KOTO | 30fe KATAKANA VOICED ITERATION MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 3101 (null) | 3100 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 3103 (null) | 3102 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 3105 BOPOMOFO LETTER B | 3104 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3107 BOPOMOFO LETTER M | 3106 BOPOMOFO LETTER P */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3109 BOPOMOFO LETTER D | 3108 BOPOMOFO LETTER F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 310b BOPOMOFO LETTER N | 310a BOPOMOFO LETTER T */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 310d BOPOMOFO LETTER G | 310c BOPOMOFO LETTER L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 310f BOPOMOFO LETTER H | 310e BOPOMOFO LETTER K */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3111 BOPOMOFO LETTER Q | 3110 BOPOMOFO LETTER J */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3113 BOPOMOFO LETTER ZH | 3112 BOPOMOFO LETTER X */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3115 BOPOMOFO LETTER SH | 3114 BOPOMOFO LETTER CH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3117 BOPOMOFO LETTER Z | 3116 BOPOMOFO LETTER R */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3119 BOPOMOFO LETTER S | 3118 BOPOMOFO LETTER C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 311b BOPOMOFO LETTER O | 311a BOPOMOFO LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 311d BOPOMOFO LETTER EH | 311c BOPOMOFO LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 311f BOPOMOFO LETTER EI | 311e BOPOMOFO LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3121 BOPOMOFO LETTER OU | 3120 BOPOMOFO LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3123 BOPOMOFO LETTER EN | 3122 BOPOMOFO LETTER AN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3125 BOPOMOFO LETTER ENG | 3124 BOPOMOFO LETTER ANG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3127 BOPOMOFO LETTER I | 3126 BOPOMOFO LETTER ER */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3129 BOPOMOFO LETTER IU | 3128 BOPOMOFO LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 312b BOPOMOFO LETTER NG | 312a BOPOMOFO LETTER V */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 312d BOPOMOFO LETTER IH | 312c BOPOMOFO LETTER GN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 312f (null) | 312e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 3131 HANGUL LETTER KIYEOK | 3130 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3133 HANGUL LETTER KIYEOK-SIOS | 3132 HANGUL LETTER SSANGKIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3135 HANGUL LETTER NIEUN-CIEUC | 3134 HANGUL LETTER NIEUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3137 HANGUL LETTER TIKEUT | 3136 HANGUL LETTER NIEUN-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3139 HANGUL LETTER RIEUL | 3138 HANGUL LETTER SSANGTIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 313b HANGUL LETTER RIEUL-MIEUM | 313a HANGUL LETTER RIEUL-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 313d HANGUL LETTER RIEUL-SIOS | 313c HANGUL LETTER RIEUL-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 313f HANGUL LETTER RIEUL-PHIEUPH | 313e HANGUL LETTER RIEUL-THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3141 HANGUL LETTER MIEUM | 3140 HANGUL LETTER RIEUL-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3143 HANGUL LETTER SSANGPIEUP | 3142 HANGUL LETTER PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3145 HANGUL LETTER SIOS | 3144 HANGUL LETTER PIEUP-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3147 HANGUL LETTER IEUNG | 3146 HANGUL LETTER SSANGSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3149 HANGUL LETTER SSANGCIEUC | 3148 HANGUL LETTER CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 314b HANGUL LETTER KHIEUKH | 314a HANGUL LETTER CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 314d HANGUL LETTER PHIEUPH | 314c HANGUL LETTER THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 314f HANGUL LETTER A | 314e HANGUL LETTER HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3151 HANGUL LETTER YA | 3150 HANGUL LETTER AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3153 HANGUL LETTER EO | 3152 HANGUL LETTER YAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3155 HANGUL LETTER YEO | 3154 HANGUL LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3157 HANGUL LETTER O | 3156 HANGUL LETTER YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3159 HANGUL LETTER WAE | 3158 HANGUL LETTER WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 315b HANGUL LETTER YO | 315a HANGUL LETTER OE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 315d HANGUL LETTER WEO | 315c HANGUL LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 315f HANGUL LETTER WI | 315e HANGUL LETTER WE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3161 HANGUL LETTER EU | 3160 HANGUL LETTER YU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3163 HANGUL LETTER I | 3162 HANGUL LETTER YI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3165 HANGUL LETTER SSANGNIEUN | 3164 HANGUL FILLER */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3167 HANGUL LETTER NIEUN-SIOS | 3166 HANGUL LETTER NIEUN-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3169 HANGUL LETTER RIEUL-KIYEOK-SIOS | 3168 HANGUL LETTER NIEUN-PANSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 316b HANGUL LETTER RIEUL-PIEUP-SIOS | 316a HANGUL LETTER RIEUL-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 316d HANGUL LETTER RIEUL-YEORINHIEUH | 316c HANGUL LETTER RIEUL-PANSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 316f HANGUL LETTER MIEUM-SIOS | 316e HANGUL LETTER MIEUM-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3171 HANGUL LETTER KAPYEOUNMIEUM | 3170 HANGUL LETTER MIEUM-PANSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3173 HANGUL LETTER PIEUP-TIKEUT | 3172 HANGUL LETTER PIEUP-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3175 HANGUL LETTER PIEUP-SIOS-TIKEUT | 3174 HANGUL LETTER PIEUP-SIOS-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3177 HANGUL LETTER PIEUP-THIEUTH | 3176 HANGUL LETTER PIEUP-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3179 HANGUL LETTER KAPYEOUNSSANGPIEUP | 3178 HANGUL LETTER KAPYEOUNPIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 317b HANGUL LETTER SIOS-NIEUN | 317a HANGUL LETTER SIOS-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 317d HANGUL LETTER SIOS-PIEUP | 317c HANGUL LETTER SIOS-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 317f HANGUL LETTER PANSIOS | 317e HANGUL LETTER SIOS-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3181 HANGUL LETTER YESIEUNG | 3180 HANGUL LETTER SSANGIEUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3183 HANGUL LETTER YESIEUNG-PANSIOS | 3182 HANGUL LETTER YESIEUNG-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3185 HANGUL LETTER SSANGHIEUH | 3184 HANGUL LETTER KAPYEOUNPHIEUPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3187 HANGUL LETTER YO-YA | 3186 HANGUL LETTER YEORINHIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3189 HANGUL LETTER YO-I | 3188 HANGUL LETTER YO-YAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 318b HANGUL LETTER YU-YE | 318a HANGUL LETTER YU-YEO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 318d HANGUL LETTER ARAEA | 318c HANGUL LETTER YU-I */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 318f (null) | 318e HANGUL LETTER ARAEAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3191 IDEOGRAPHIC ANNOTATION REVERSE MARK | 3190 IDEOGRAPHIC ANNOTATION LINKING MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3193 IDEOGRAPHIC ANNOTATION TWO MARK | 3192 IDEOGRAPHIC ANNOTATION ONE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3195 IDEOGRAPHIC ANNOTATION FOUR MARK | 3194 IDEOGRAPHIC ANNOTATION THREE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3197 IDEOGRAPHIC ANNOTATION MIDDLE MARK | 3196 IDEOGRAPHIC ANNOTATION TOP MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3199 IDEOGRAPHIC ANNOTATION FIRST MARK | 3198 IDEOGRAPHIC ANNOTATION BOTTOM MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 319b IDEOGRAPHIC ANNOTATION THIRD MARK | 319a IDEOGRAPHIC ANNOTATION SECOND MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 319d IDEOGRAPHIC ANNOTATION HEAVEN MARK | 319c IDEOGRAPHIC ANNOTATION FOURTH MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 319f IDEOGRAPHIC ANNOTATION MAN MARK | 319e IDEOGRAPHIC ANNOTATION EARTH MARK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31a1 BOPOMOFO LETTER ZI | 31a0 BOPOMOFO LETTER BU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31a3 BOPOMOFO LETTER GU | 31a2 BOPOMOFO LETTER JI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31a5 BOPOMOFO LETTER ENN | 31a4 BOPOMOFO LETTER EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31a7 BOPOMOFO LETTER ONN | 31a6 BOPOMOFO LETTER OO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31a9 BOPOMOFO LETTER ANN | 31a8 BOPOMOFO LETTER IR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31ab BOPOMOFO LETTER UNN | 31aa BOPOMOFO LETTER INN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31ad BOPOMOFO LETTER NGG | 31ac BOPOMOFO LETTER IM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31af BOPOMOFO LETTER AUNN | 31ae BOPOMOFO LETTER AINN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31b1 BOPOMOFO LETTER OM | 31b0 BOPOMOFO LETTER AM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31b3 BOPOMOFO LETTER INNN | 31b2 BOPOMOFO LETTER ONG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31b5 BOPOMOFO FINAL LETTER T | 31b4 BOPOMOFO FINAL LETTER P */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31b7 BOPOMOFO FINAL LETTER H | 31b6 BOPOMOFO FINAL LETTER K */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31b9 BOPOMOFO LETTER LH | 31b8 BOPOMOFO LETTER GH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 31bb (null) | 31ba BOPOMOFO LETTER ZY */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 31bd (null) | 31bc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 31bf (null) | 31be (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31c1 CJK STROKE WG | 31c0 CJK STROKE T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31c3 CJK STROKE BXG | 31c2 CJK STROKE XG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31c5 CJK STROKE HZZ | 31c4 CJK STROKE SW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31c7 CJK STROKE HP | 31c6 CJK STROKE HZG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31c9 CJK STROKE SZWG | 31c8 CJK STROKE HZWG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31cb CJK STROKE HZZP | 31ca CJK STROKE HZT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31cd CJK STROKE HZW | 31cc CJK STROKE HPWG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31cf CJK STROKE N | 31ce CJK STROKE HZZZ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31d1 CJK STROKE S | 31d0 CJK STROKE H */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31d3 CJK STROKE SP | 31d2 CJK STROKE P */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31d5 CJK STROKE HZ | 31d4 CJK STROKE D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31d7 CJK STROKE SZ | 31d6 CJK STROKE HG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31d9 CJK STROKE ST | 31d8 CJK STROKE SWZ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31db CJK STROKE PD | 31da CJK STROKE SG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31dd CJK STROKE TN | 31dc CJK STROKE PZ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31df CJK STROKE SWG | 31de CJK STROKE SZZ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31e1 CJK STROKE HZZZG | 31e0 CJK STROKE HXWG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31e3 CJK STROKE Q | 31e2 CJK STROKE PG */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 31e5 (null) | 31e4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 31e7 (null) | 31e6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 31e9 (null) | 31e8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 31eb (null) | 31ea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 31ed (null) | 31ec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 31ef (null) | 31ee (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31f1 KATAKANA LETTER SMALL SI | 31f0 KATAKANA LETTER SMALL KU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31f3 KATAKANA LETTER SMALL TO | 31f2 KATAKANA LETTER SMALL SU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31f5 KATAKANA LETTER SMALL HA | 31f4 KATAKANA LETTER SMALL NU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31f7 KATAKANA LETTER SMALL HU | 31f6 KATAKANA LETTER SMALL HI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31f9 KATAKANA LETTER SMALL HO | 31f8 KATAKANA LETTER SMALL HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31fb KATAKANA LETTER SMALL RA | 31fa KATAKANA LETTER SMALL MU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31fd KATAKANA LETTER SMALL RU | 31fc KATAKANA LETTER SMALL RI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31ff KATAKANA LETTER SMALL RO | 31fe KATAKANA LETTER SMALL RE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3201 PARENTHESIZED HANGUL NIEUN | 3200 PARENTHESIZED HANGUL KIYEOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3203 PARENTHESIZED HANGUL RIEUL | 3202 PARENTHESIZED HANGUL TIKEUT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3205 PARENTHESIZED HANGUL PIEUP | 3204 PARENTHESIZED HANGUL MIEUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3207 PARENTHESIZED HANGUL IEUNG | 3206 PARENTHESIZED HANGUL SIOS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3209 PARENTHESIZED HANGUL CHIEUCH | 3208 PARENTHESIZED HANGUL CIEUC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 320b PARENTHESIZED HANGUL THIEUTH | 320a PARENTHESIZED HANGUL KHIEUKH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 320d PARENTHESIZED HANGUL HIEUH | 320c PARENTHESIZED HANGUL PHIEUPH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 320f PARENTHESIZED HANGUL NIEUN A | 320e PARENTHESIZED HANGUL KIYEOK A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3211 PARENTHESIZED HANGUL RIEUL A | 3210 PARENTHESIZED HANGUL TIKEUT A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3213 PARENTHESIZED HANGUL PIEUP A | 3212 PARENTHESIZED HANGUL MIEUM A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3215 PARENTHESIZED HANGUL IEUNG A | 3214 PARENTHESIZED HANGUL SIOS A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3217 PARENTHESIZED HANGUL CHIEUCH A | 3216 PARENTHESIZED HANGUL CIEUC A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3219 PARENTHESIZED HANGUL THIEUTH A | 3218 PARENTHESIZED HANGUL KHIEUKH A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 321b PARENTHESIZED HANGUL HIEUH A | 321a PARENTHESIZED HANGUL PHIEUPH A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 321d PARENTHESIZED KOREAN CHARACTER OJEON | 321c PARENTHESIZED HANGUL CIEUC U */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 321f (null) | 321e PARENTHESIZED KOREAN CHARACTER O HU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3221 PARENTHESIZED IDEOGRAPH TWO | 3220 PARENTHESIZED IDEOGRAPH ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3223 PARENTHESIZED IDEOGRAPH FOUR | 3222 PARENTHESIZED IDEOGRAPH THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3225 PARENTHESIZED IDEOGRAPH SIX | 3224 PARENTHESIZED IDEOGRAPH FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3227 PARENTHESIZED IDEOGRAPH EIGHT | 3226 PARENTHESIZED IDEOGRAPH SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3229 PARENTHESIZED IDEOGRAPH TEN | 3228 PARENTHESIZED IDEOGRAPH NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 322b PARENTHESIZED IDEOGRAPH FIRE | 322a PARENTHESIZED IDEOGRAPH MOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 322d PARENTHESIZED IDEOGRAPH WOOD | 322c PARENTHESIZED IDEOGRAPH WATER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 322f PARENTHESIZED IDEOGRAPH EARTH | 322e PARENTHESIZED IDEOGRAPH METAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3231 PARENTHESIZED IDEOGRAPH STOCK | 3230 PARENTHESIZED IDEOGRAPH SUN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3233 PARENTHESIZED IDEOGRAPH SOCIETY | 3232 PARENTHESIZED IDEOGRAPH HAVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3235 PARENTHESIZED IDEOGRAPH SPECIAL | 3234 PARENTHESIZED IDEOGRAPH NAME */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3237 PARENTHESIZED IDEOGRAPH CONGRATULATION | 3236 PARENTHESIZED IDEOGRAPH FINANCIAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3239 PARENTHESIZED IDEOGRAPH REPRESENT | 3238 PARENTHESIZED IDEOGRAPH LABOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 323b PARENTHESIZED IDEOGRAPH STUDY | 323a PARENTHESIZED IDEOGRAPH CALL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 323d PARENTHESIZED IDEOGRAPH ENTERPRISE | 323c PARENTHESIZED IDEOGRAPH SUPERVISE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 323f PARENTHESIZED IDEOGRAPH ALLIANCE | 323e PARENTHESIZED IDEOGRAPH RESOURCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3241 PARENTHESIZED IDEOGRAPH REST | 3240 PARENTHESIZED IDEOGRAPH FESTIVAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3243 PARENTHESIZED IDEOGRAPH REACH | 3242 PARENTHESIZED IDEOGRAPH SELF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3245 CIRCLED IDEOGRAPH KINDERGARTEN | 3244 CIRCLED IDEOGRAPH QUESTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3247 CIRCLED IDEOGRAPH KOTO | 3246 CIRCLED IDEOGRAPH SCHOOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3249 CIRCLED NUMBER TWENTY ON BLACK SQUARE | 3248 CIRCLED NUMBER TEN ON BLACK SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 324b CIRCLED NUMBER FORTY ON BLACK SQUARE | 324a CIRCLED NUMBER THIRTY ON BLACK SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 324d CIRCLED NUMBER SIXTY ON BLACK SQUARE | 324c CIRCLED NUMBER FIFTY ON BLACK SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 324f CIRCLED NUMBER EIGHTY ON BLACK SQUARE | 324e CIRCLED NUMBER SEVENTY ON BLACK SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3251 CIRCLED NUMBER TWENTY ONE | 3250 PARTNERSHIP SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3253 CIRCLED NUMBER TWENTY THREE | 3252 CIRCLED NUMBER TWENTY TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3255 CIRCLED NUMBER TWENTY FIVE | 3254 CIRCLED NUMBER TWENTY FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3257 CIRCLED NUMBER TWENTY SEVEN | 3256 CIRCLED NUMBER TWENTY SIX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3259 CIRCLED NUMBER TWENTY NINE | 3258 CIRCLED NUMBER TWENTY EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 325b CIRCLED NUMBER THIRTY ONE | 325a CIRCLED NUMBER THIRTY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 325d CIRCLED NUMBER THIRTY THREE | 325c CIRCLED NUMBER THIRTY TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 325f CIRCLED NUMBER THIRTY FIVE | 325e CIRCLED NUMBER THIRTY FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3261 CIRCLED HANGUL NIEUN | 3260 CIRCLED HANGUL KIYEOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3263 CIRCLED HANGUL RIEUL | 3262 CIRCLED HANGUL TIKEUT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3265 CIRCLED HANGUL PIEUP | 3264 CIRCLED HANGUL MIEUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3267 CIRCLED HANGUL IEUNG | 3266 CIRCLED HANGUL SIOS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3269 CIRCLED HANGUL CHIEUCH | 3268 CIRCLED HANGUL CIEUC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 326b CIRCLED HANGUL THIEUTH | 326a CIRCLED HANGUL KHIEUKH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 326d CIRCLED HANGUL HIEUH | 326c CIRCLED HANGUL PHIEUPH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 326f CIRCLED HANGUL NIEUN A | 326e CIRCLED HANGUL KIYEOK A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3271 CIRCLED HANGUL RIEUL A | 3270 CIRCLED HANGUL TIKEUT A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3273 CIRCLED HANGUL PIEUP A | 3272 CIRCLED HANGUL MIEUM A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3275 CIRCLED HANGUL IEUNG A | 3274 CIRCLED HANGUL SIOS A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3277 CIRCLED HANGUL CHIEUCH A | 3276 CIRCLED HANGUL CIEUC A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3279 CIRCLED HANGUL THIEUTH A | 3278 CIRCLED HANGUL KHIEUKH A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 327b CIRCLED HANGUL HIEUH A | 327a CIRCLED HANGUL PHIEUPH A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 327d CIRCLED KOREAN CHARACTER JUEUI | 327c CIRCLED KOREAN CHARACTER CHAMKO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 327f KOREAN STANDARD SYMBOL | 327e CIRCLED HANGUL IEUNG U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3281 CIRCLED IDEOGRAPH TWO | 3280 CIRCLED IDEOGRAPH ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3283 CIRCLED IDEOGRAPH FOUR | 3282 CIRCLED IDEOGRAPH THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3285 CIRCLED IDEOGRAPH SIX | 3284 CIRCLED IDEOGRAPH FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3287 CIRCLED IDEOGRAPH EIGHT | 3286 CIRCLED IDEOGRAPH SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3289 CIRCLED IDEOGRAPH TEN | 3288 CIRCLED IDEOGRAPH NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 328b CIRCLED IDEOGRAPH FIRE | 328a CIRCLED IDEOGRAPH MOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 328d CIRCLED IDEOGRAPH WOOD | 328c CIRCLED IDEOGRAPH WATER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 328f CIRCLED IDEOGRAPH EARTH | 328e CIRCLED IDEOGRAPH METAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3291 CIRCLED IDEOGRAPH STOCK | 3290 CIRCLED IDEOGRAPH SUN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3293 CIRCLED IDEOGRAPH SOCIETY | 3292 CIRCLED IDEOGRAPH HAVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3295 CIRCLED IDEOGRAPH SPECIAL | 3294 CIRCLED IDEOGRAPH NAME */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3297 CIRCLED IDEOGRAPH CONGRATULATION | 3296 CIRCLED IDEOGRAPH FINANCIAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3299 CIRCLED IDEOGRAPH SECRET | 3298 CIRCLED IDEOGRAPH LABOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 329b CIRCLED IDEOGRAPH FEMALE | 329a CIRCLED IDEOGRAPH MALE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 329d CIRCLED IDEOGRAPH EXCELLENT | 329c CIRCLED IDEOGRAPH SUITABLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 329f CIRCLED IDEOGRAPH ATTENTION | 329e CIRCLED IDEOGRAPH PRINT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32a1 CIRCLED IDEOGRAPH REST | 32a0 CIRCLED IDEOGRAPH ITEM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32a3 CIRCLED IDEOGRAPH CORRECT | 32a2 CIRCLED IDEOGRAPH COPY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32a5 CIRCLED IDEOGRAPH CENTRE | 32a4 CIRCLED IDEOGRAPH HIGH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32a7 CIRCLED IDEOGRAPH LEFT | 32a6 CIRCLED IDEOGRAPH LOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32a9 CIRCLED IDEOGRAPH MEDICINE | 32a8 CIRCLED IDEOGRAPH RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32ab CIRCLED IDEOGRAPH STUDY | 32aa CIRCLED IDEOGRAPH RELIGION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32ad CIRCLED IDEOGRAPH ENTERPRISE | 32ac CIRCLED IDEOGRAPH SUPERVISE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32af CIRCLED IDEOGRAPH ALLIANCE | 32ae CIRCLED IDEOGRAPH RESOURCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32b1 CIRCLED NUMBER THIRTY SIX | 32b0 CIRCLED IDEOGRAPH NIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32b3 CIRCLED NUMBER THIRTY EIGHT | 32b2 CIRCLED NUMBER THIRTY SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32b5 CIRCLED NUMBER FORTY | 32b4 CIRCLED NUMBER THIRTY NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32b7 CIRCLED NUMBER FORTY TWO | 32b6 CIRCLED NUMBER FORTY ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32b9 CIRCLED NUMBER FORTY FOUR | 32b8 CIRCLED NUMBER FORTY THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32bb CIRCLED NUMBER FORTY SIX | 32ba CIRCLED NUMBER FORTY FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32bd CIRCLED NUMBER FORTY EIGHT | 32bc CIRCLED NUMBER FORTY SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32bf CIRCLED NUMBER FIFTY | 32be CIRCLED NUMBER FORTY NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32c1 IDEOGRAPHIC TELEGRAPH SYMBOL FOR FEBRUA | 32c0 IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32c3 IDEOGRAPHIC TELEGRAPH SYMBOL FOR APRIL | 32c2 IDEOGRAPHIC TELEGRAPH SYMBOL FOR MARCH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32c5 IDEOGRAPHIC TELEGRAPH SYMBOL FOR JUNE | 32c4 IDEOGRAPHIC TELEGRAPH SYMBOL FOR MAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32c7 IDEOGRAPHIC TELEGRAPH SYMBOL FOR AUGUST | 32c6 IDEOGRAPHIC TELEGRAPH SYMBOL FOR JULY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32c9 IDEOGRAPHIC TELEGRAPH SYMBOL FOR OCTOBE | 32c8 IDEOGRAPHIC TELEGRAPH SYMBOL FOR SEPTEM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32cb IDEOGRAPHIC TELEGRAPH SYMBOL FOR DECEMB | 32ca IDEOGRAPHIC TELEGRAPH SYMBOL FOR NOVEMB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32cd SQUARE ERG | 32cc SQUARE HG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32cf LIMITED LIABILITY SIGN | 32ce SQUARE EV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32d1 CIRCLED KATAKANA I | 32d0 CIRCLED KATAKANA A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32d3 CIRCLED KATAKANA E | 32d2 CIRCLED KATAKANA U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32d5 CIRCLED KATAKANA KA | 32d4 CIRCLED KATAKANA O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32d7 CIRCLED KATAKANA KU | 32d6 CIRCLED KATAKANA KI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32d9 CIRCLED KATAKANA KO | 32d8 CIRCLED KATAKANA KE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32db CIRCLED KATAKANA SI | 32da CIRCLED KATAKANA SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32dd CIRCLED KATAKANA SE | 32dc CIRCLED KATAKANA SU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32df CIRCLED KATAKANA TA | 32de CIRCLED KATAKANA SO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32e1 CIRCLED KATAKANA TU | 32e0 CIRCLED KATAKANA TI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32e3 CIRCLED KATAKANA TO | 32e2 CIRCLED KATAKANA TE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32e5 CIRCLED KATAKANA NI | 32e4 CIRCLED KATAKANA NA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32e7 CIRCLED KATAKANA NE | 32e6 CIRCLED KATAKANA NU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32e9 CIRCLED KATAKANA HA | 32e8 CIRCLED KATAKANA NO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32eb CIRCLED KATAKANA HU | 32ea CIRCLED KATAKANA HI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32ed CIRCLED KATAKANA HO | 32ec CIRCLED KATAKANA HE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32ef CIRCLED KATAKANA MI | 32ee CIRCLED KATAKANA MA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32f1 CIRCLED KATAKANA ME | 32f0 CIRCLED KATAKANA MU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32f3 CIRCLED KATAKANA YA | 32f2 CIRCLED KATAKANA MO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32f5 CIRCLED KATAKANA YO | 32f4 CIRCLED KATAKANA YU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32f7 CIRCLED KATAKANA RI | 32f6 CIRCLED KATAKANA RA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32f9 CIRCLED KATAKANA RE | 32f8 CIRCLED KATAKANA RU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32fb CIRCLED KATAKANA WA | 32fa CIRCLED KATAKANA RO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32fd CIRCLED KATAKANA WE | 32fc CIRCLED KATAKANA WI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 32ff (null) | 32fe CIRCLED KATAKANA WO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3301 SQUARE ARUHUA | 3300 SQUARE APAATO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3303 SQUARE AARU | 3302 SQUARE ANPEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3305 SQUARE INTI | 3304 SQUARE ININGU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3307 SQUARE ESUKUUDO | 3306 SQUARE UON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3309 SQUARE ONSU | 3308 SQUARE EEKAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 330b SQUARE KAIRI | 330a SQUARE OOMU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 330d SQUARE KARORII | 330c SQUARE KARATTO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 330f SQUARE GANMA | 330e SQUARE GARON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3311 SQUARE GINII | 3310 SQUARE GIGA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3313 SQUARE GIRUDAA | 3312 SQUARE KYURII */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3315 SQUARE KIROGURAMU | 3314 SQUARE KIRO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3317 SQUARE KIROWATTO | 3316 SQUARE KIROMEETORU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3319 SQUARE GURAMUTON | 3318 SQUARE GURAMU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 331b SQUARE KUROONE | 331a SQUARE KURUZEIRO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 331d SQUARE KORUNA | 331c SQUARE KEESU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 331f SQUARE SAIKURU | 331e SQUARE KOOPO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3321 SQUARE SIRINGU | 3320 SQUARE SANTIIMU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3323 SQUARE SENTO | 3322 SQUARE SENTI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3325 SQUARE DESI | 3324 SQUARE DAASU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3327 SQUARE TON | 3326 SQUARE DORU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3329 SQUARE NOTTO | 3328 SQUARE NANO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 332b SQUARE PAASENTO | 332a SQUARE HAITU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 332d SQUARE BAARERU | 332c SQUARE PAATU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 332f SQUARE PIKURU | 332e SQUARE PIASUTORU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3331 SQUARE BIRU | 3330 SQUARE PIKO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3333 SQUARE HUIITO | 3332 SQUARE HUARADDO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3335 SQUARE HURAN | 3334 SQUARE BUSSYERU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3337 SQUARE PESO | 3336 SQUARE HEKUTAARU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3339 SQUARE HERUTU | 3338 SQUARE PENIHI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 333b SQUARE PEEZI | 333a SQUARE PENSU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 333d SQUARE POINTO | 333c SQUARE BEETA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 333f SQUARE HON | 333e SQUARE BORUTO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3341 SQUARE HOORU | 3340 SQUARE PONDO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3343 SQUARE MAIKURO | 3342 SQUARE HOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3345 SQUARE MAHHA | 3344 SQUARE MAIRU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3347 SQUARE MANSYON | 3346 SQUARE MARUKU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3349 SQUARE MIRI | 3348 SQUARE MIKURON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 334b SQUARE MEGA | 334a SQUARE MIRIBAARU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 334d SQUARE MEETORU | 334c SQUARE MEGATON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 334f SQUARE YAARU | 334e SQUARE YAADO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3351 SQUARE RITTORU | 3350 SQUARE YUAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3353 SQUARE RUPII | 3352 SQUARE RIRA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3355 SQUARE REMU | 3354 SQUARE RUUBURU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3357 SQUARE WATTO | 3356 SQUARE RENTOGEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3359 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR O | 3358 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR Z */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 335b IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T | 335a IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 335d IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR F | 335c IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR F */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 335f IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR S | 335e IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3361 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR N | 3360 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3363 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR E | 3362 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3365 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T | 3364 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3367 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR F | 3366 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR F */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3369 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR S | 3368 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 336b IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR N | 336a IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 336d IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T | 336c IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 336f IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T | 336e IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3371 SQUARE HPA | 3370 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3373 SQUARE AU | 3372 SQUARE DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3375 SQUARE OV | 3374 SQUARE BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3377 SQUARE DM | 3376 SQUARE PC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3379 SQUARE DM CUBED | 3378 SQUARE DM SQUARED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 337b SQUARE ERA NAME HEISEI | 337a SQUARE IU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 337d SQUARE ERA NAME TAISYOU | 337c SQUARE ERA NAME SYOUWA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 337f SQUARE CORPORATION | 337e SQUARE ERA NAME MEIZI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3381 SQUARE NA | 3380 SQUARE PA AMPS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3383 SQUARE MA | 3382 SQUARE MU A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3385 SQUARE KB | 3384 SQUARE KA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3387 SQUARE GB | 3386 SQUARE MB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3389 SQUARE KCAL | 3388 SQUARE CAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 338b SQUARE NF | 338a SQUARE PF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 338d SQUARE MU G | 338c SQUARE MU F */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 338f SQUARE KG | 338e SQUARE MG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3391 SQUARE KHZ | 3390 SQUARE HZ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3393 SQUARE GHZ | 3392 SQUARE MHZ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3395 SQUARE MU L | 3394 SQUARE THZ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3397 SQUARE DL | 3396 SQUARE ML */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3399 SQUARE FM | 3398 SQUARE KL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 339b SQUARE MU M | 339a SQUARE NM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 339d SQUARE CM | 339c SQUARE MM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 339f SQUARE MM SQUARED | 339e SQUARE KM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33a1 SQUARE M SQUARED | 33a0 SQUARE CM SQUARED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33a3 SQUARE MM CUBED | 33a2 SQUARE KM SQUARED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33a5 SQUARE M CUBED | 33a4 SQUARE CM CUBED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33a7 SQUARE M OVER S | 33a6 SQUARE KM CUBED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33a9 SQUARE PA | 33a8 SQUARE M OVER S SQUARED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33ab SQUARE MPA | 33aa SQUARE KPA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33ad SQUARE RAD | 33ac SQUARE GPA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33af SQUARE RAD OVER S SQUARED | 33ae SQUARE RAD OVER S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33b1 SQUARE NS | 33b0 SQUARE PS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33b3 SQUARE MS | 33b2 SQUARE MU S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33b5 SQUARE NV | 33b4 SQUARE PV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33b7 SQUARE MV | 33b6 SQUARE MU V */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33b9 SQUARE MV MEGA | 33b8 SQUARE KV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33bb SQUARE NW | 33ba SQUARE PW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33bd SQUARE MW | 33bc SQUARE MU W */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33bf SQUARE MW MEGA | 33be SQUARE KW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33c1 SQUARE M OHM | 33c0 SQUARE K OHM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33c3 SQUARE BQ | 33c2 SQUARE AM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33c5 SQUARE CD | 33c4 SQUARE CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33c7 SQUARE CO | 33c6 SQUARE C OVER KG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33c9 SQUARE GY | 33c8 SQUARE DB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33cb SQUARE HP | 33ca SQUARE HA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33cd SQUARE KK | 33cc SQUARE IN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33cf SQUARE KT | 33ce SQUARE KM CAPITAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33d1 SQUARE LN | 33d0 SQUARE LM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33d3 SQUARE LX | 33d2 SQUARE LOG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33d5 SQUARE MIL | 33d4 SQUARE MB SMALL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33d7 SQUARE PH | 33d6 SQUARE MOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33d9 SQUARE PPM | 33d8 SQUARE PM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33db SQUARE SR | 33da SQUARE PR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33dd SQUARE WB | 33dc SQUARE SV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33df SQUARE A OVER M | 33de SQUARE V OVER M */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33e1 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW | 33e0 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33e3 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FO | 33e2 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33e5 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SI | 33e4 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33e7 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EI | 33e6 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33e9 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TE | 33e8 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33eb IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW | 33ea IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33ed IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FO | 33ec IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33ef IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SI | 33ee IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33f1 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EI | 33f0 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33f3 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW | 33f2 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33f5 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW | 33f4 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33f7 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW | 33f6 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33f9 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW | 33f8 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33fb IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW | 33fa IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33fd IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TH | 33fc IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33ff SQUARE GAL | 33fe IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3401 CJK Ideograph Extension A-3401 | 3400 CJK Ideograph Extension A-3400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3403 CJK Ideograph Extension A-3403 | 3402 CJK Ideograph Extension A-3402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3405 CJK Ideograph Extension A-3405 | 3404 CJK Ideograph Extension A-3404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3407 CJK Ideograph Extension A-3407 | 3406 CJK Ideograph Extension A-3406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3409 CJK Ideograph Extension A-3409 | 3408 CJK Ideograph Extension A-3408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 340b CJK Ideograph Extension A-340B | 340a CJK Ideograph Extension A-340A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 340d CJK Ideograph Extension A-340D | 340c CJK Ideograph Extension A-340C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 340f CJK Ideograph Extension A-340F | 340e CJK Ideograph Extension A-340E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3411 CJK Ideograph Extension A-3411 | 3410 CJK Ideograph Extension A-3410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3413 CJK Ideograph Extension A-3413 | 3412 CJK Ideograph Extension A-3412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3415 CJK Ideograph Extension A-3415 | 3414 CJK Ideograph Extension A-3414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3417 CJK Ideograph Extension A-3417 | 3416 CJK Ideograph Extension A-3416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3419 CJK Ideograph Extension A-3419 | 3418 CJK Ideograph Extension A-3418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 341b CJK Ideograph Extension A-341B | 341a CJK Ideograph Extension A-341A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 341d CJK Ideograph Extension A-341D | 341c CJK Ideograph Extension A-341C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 341f CJK Ideograph Extension A-341F | 341e CJK Ideograph Extension A-341E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3421 CJK Ideograph Extension A-3421 | 3420 CJK Ideograph Extension A-3420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3423 CJK Ideograph Extension A-3423 | 3422 CJK Ideograph Extension A-3422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3425 CJK Ideograph Extension A-3425 | 3424 CJK Ideograph Extension A-3424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3427 CJK Ideograph Extension A-3427 | 3426 CJK Ideograph Extension A-3426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3429 CJK Ideograph Extension A-3429 | 3428 CJK Ideograph Extension A-3428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 342b CJK Ideograph Extension A-342B | 342a CJK Ideograph Extension A-342A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 342d CJK Ideograph Extension A-342D | 342c CJK Ideograph Extension A-342C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 342f CJK Ideograph Extension A-342F | 342e CJK Ideograph Extension A-342E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3431 CJK Ideograph Extension A-3431 | 3430 CJK Ideograph Extension A-3430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3433 CJK Ideograph Extension A-3433 | 3432 CJK Ideograph Extension A-3432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3435 CJK Ideograph Extension A-3435 | 3434 CJK Ideograph Extension A-3434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3437 CJK Ideograph Extension A-3437 | 3436 CJK Ideograph Extension A-3436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3439 CJK Ideograph Extension A-3439 | 3438 CJK Ideograph Extension A-3438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 343b CJK Ideograph Extension A-343B | 343a CJK Ideograph Extension A-343A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 343d CJK Ideograph Extension A-343D | 343c CJK Ideograph Extension A-343C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 343f CJK Ideograph Extension A-343F | 343e CJK Ideograph Extension A-343E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3441 CJK Ideograph Extension A-3441 | 3440 CJK Ideograph Extension A-3440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3443 CJK Ideograph Extension A-3443 | 3442 CJK Ideograph Extension A-3442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3445 CJK Ideograph Extension A-3445 | 3444 CJK Ideograph Extension A-3444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3447 CJK Ideograph Extension A-3447 | 3446 CJK Ideograph Extension A-3446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3449 CJK Ideograph Extension A-3449 | 3448 CJK Ideograph Extension A-3448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 344b CJK Ideograph Extension A-344B | 344a CJK Ideograph Extension A-344A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 344d CJK Ideograph Extension A-344D | 344c CJK Ideograph Extension A-344C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 344f CJK Ideograph Extension A-344F | 344e CJK Ideograph Extension A-344E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3451 CJK Ideograph Extension A-3451 | 3450 CJK Ideograph Extension A-3450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3453 CJK Ideograph Extension A-3453 | 3452 CJK Ideograph Extension A-3452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3455 CJK Ideograph Extension A-3455 | 3454 CJK Ideograph Extension A-3454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3457 CJK Ideograph Extension A-3457 | 3456 CJK Ideograph Extension A-3456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3459 CJK Ideograph Extension A-3459 | 3458 CJK Ideograph Extension A-3458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 345b CJK Ideograph Extension A-345B | 345a CJK Ideograph Extension A-345A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 345d CJK Ideograph Extension A-345D | 345c CJK Ideograph Extension A-345C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 345f CJK Ideograph Extension A-345F | 345e CJK Ideograph Extension A-345E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3461 CJK Ideograph Extension A-3461 | 3460 CJK Ideograph Extension A-3460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3463 CJK Ideograph Extension A-3463 | 3462 CJK Ideograph Extension A-3462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3465 CJK Ideograph Extension A-3465 | 3464 CJK Ideograph Extension A-3464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3467 CJK Ideograph Extension A-3467 | 3466 CJK Ideograph Extension A-3466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3469 CJK Ideograph Extension A-3469 | 3468 CJK Ideograph Extension A-3468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 346b CJK Ideograph Extension A-346B | 346a CJK Ideograph Extension A-346A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 346d CJK Ideograph Extension A-346D | 346c CJK Ideograph Extension A-346C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 346f CJK Ideograph Extension A-346F | 346e CJK Ideograph Extension A-346E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3471 CJK Ideograph Extension A-3471 | 3470 CJK Ideograph Extension A-3470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3473 CJK Ideograph Extension A-3473 | 3472 CJK Ideograph Extension A-3472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3475 CJK Ideograph Extension A-3475 | 3474 CJK Ideograph Extension A-3474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3477 CJK Ideograph Extension A-3477 | 3476 CJK Ideograph Extension A-3476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3479 CJK Ideograph Extension A-3479 | 3478 CJK Ideograph Extension A-3478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 347b CJK Ideograph Extension A-347B | 347a CJK Ideograph Extension A-347A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 347d CJK Ideograph Extension A-347D | 347c CJK Ideograph Extension A-347C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 347f CJK Ideograph Extension A-347F | 347e CJK Ideograph Extension A-347E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3481 CJK Ideograph Extension A-3481 | 3480 CJK Ideograph Extension A-3480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3483 CJK Ideograph Extension A-3483 | 3482 CJK Ideograph Extension A-3482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3485 CJK Ideograph Extension A-3485 | 3484 CJK Ideograph Extension A-3484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3487 CJK Ideograph Extension A-3487 | 3486 CJK Ideograph Extension A-3486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3489 CJK Ideograph Extension A-3489 | 3488 CJK Ideograph Extension A-3488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 348b CJK Ideograph Extension A-348B | 348a CJK Ideograph Extension A-348A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 348d CJK Ideograph Extension A-348D | 348c CJK Ideograph Extension A-348C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 348f CJK Ideograph Extension A-348F | 348e CJK Ideograph Extension A-348E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3491 CJK Ideograph Extension A-3491 | 3490 CJK Ideograph Extension A-3490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3493 CJK Ideograph Extension A-3493 | 3492 CJK Ideograph Extension A-3492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3495 CJK Ideograph Extension A-3495 | 3494 CJK Ideograph Extension A-3494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3497 CJK Ideograph Extension A-3497 | 3496 CJK Ideograph Extension A-3496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3499 CJK Ideograph Extension A-3499 | 3498 CJK Ideograph Extension A-3498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 349b CJK Ideograph Extension A-349B | 349a CJK Ideograph Extension A-349A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 349d CJK Ideograph Extension A-349D | 349c CJK Ideograph Extension A-349C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 349f CJK Ideograph Extension A-349F | 349e CJK Ideograph Extension A-349E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34a1 CJK Ideograph Extension A-34A1 | 34a0 CJK Ideograph Extension A-34A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34a3 CJK Ideograph Extension A-34A3 | 34a2 CJK Ideograph Extension A-34A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34a5 CJK Ideograph Extension A-34A5 | 34a4 CJK Ideograph Extension A-34A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34a7 CJK Ideograph Extension A-34A7 | 34a6 CJK Ideograph Extension A-34A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34a9 CJK Ideograph Extension A-34A9 | 34a8 CJK Ideograph Extension A-34A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34ab CJK Ideograph Extension A-34AB | 34aa CJK Ideograph Extension A-34AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34ad CJK Ideograph Extension A-34AD | 34ac CJK Ideograph Extension A-34AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34af CJK Ideograph Extension A-34AF | 34ae CJK Ideograph Extension A-34AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34b1 CJK Ideograph Extension A-34B1 | 34b0 CJK Ideograph Extension A-34B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34b3 CJK Ideograph Extension A-34B3 | 34b2 CJK Ideograph Extension A-34B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34b5 CJK Ideograph Extension A-34B5 | 34b4 CJK Ideograph Extension A-34B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34b7 CJK Ideograph Extension A-34B7 | 34b6 CJK Ideograph Extension A-34B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34b9 CJK Ideograph Extension A-34B9 | 34b8 CJK Ideograph Extension A-34B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34bb CJK Ideograph Extension A-34BB | 34ba CJK Ideograph Extension A-34BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34bd CJK Ideograph Extension A-34BD | 34bc CJK Ideograph Extension A-34BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34bf CJK Ideograph Extension A-34BF | 34be CJK Ideograph Extension A-34BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34c1 CJK Ideograph Extension A-34C1 | 34c0 CJK Ideograph Extension A-34C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34c3 CJK Ideograph Extension A-34C3 | 34c2 CJK Ideograph Extension A-34C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34c5 CJK Ideograph Extension A-34C5 | 34c4 CJK Ideograph Extension A-34C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34c7 CJK Ideograph Extension A-34C7 | 34c6 CJK Ideograph Extension A-34C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34c9 CJK Ideograph Extension A-34C9 | 34c8 CJK Ideograph Extension A-34C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34cb CJK Ideograph Extension A-34CB | 34ca CJK Ideograph Extension A-34CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34cd CJK Ideograph Extension A-34CD | 34cc CJK Ideograph Extension A-34CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34cf CJK Ideograph Extension A-34CF | 34ce CJK Ideograph Extension A-34CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34d1 CJK Ideograph Extension A-34D1 | 34d0 CJK Ideograph Extension A-34D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34d3 CJK Ideograph Extension A-34D3 | 34d2 CJK Ideograph Extension A-34D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34d5 CJK Ideograph Extension A-34D5 | 34d4 CJK Ideograph Extension A-34D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34d7 CJK Ideograph Extension A-34D7 | 34d6 CJK Ideograph Extension A-34D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34d9 CJK Ideograph Extension A-34D9 | 34d8 CJK Ideograph Extension A-34D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34db CJK Ideograph Extension A-34DB | 34da CJK Ideograph Extension A-34DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34dd CJK Ideograph Extension A-34DD | 34dc CJK Ideograph Extension A-34DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34df CJK Ideograph Extension A-34DF | 34de CJK Ideograph Extension A-34DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34e1 CJK Ideograph Extension A-34E1 | 34e0 CJK Ideograph Extension A-34E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34e3 CJK Ideograph Extension A-34E3 | 34e2 CJK Ideograph Extension A-34E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34e5 CJK Ideograph Extension A-34E5 | 34e4 CJK Ideograph Extension A-34E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34e7 CJK Ideograph Extension A-34E7 | 34e6 CJK Ideograph Extension A-34E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34e9 CJK Ideograph Extension A-34E9 | 34e8 CJK Ideograph Extension A-34E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34eb CJK Ideograph Extension A-34EB | 34ea CJK Ideograph Extension A-34EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34ed CJK Ideograph Extension A-34ED | 34ec CJK Ideograph Extension A-34EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34ef CJK Ideograph Extension A-34EF | 34ee CJK Ideograph Extension A-34EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34f1 CJK Ideograph Extension A-34F1 | 34f0 CJK Ideograph Extension A-34F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34f3 CJK Ideograph Extension A-34F3 | 34f2 CJK Ideograph Extension A-34F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34f5 CJK Ideograph Extension A-34F5 | 34f4 CJK Ideograph Extension A-34F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34f7 CJK Ideograph Extension A-34F7 | 34f6 CJK Ideograph Extension A-34F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34f9 CJK Ideograph Extension A-34F9 | 34f8 CJK Ideograph Extension A-34F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34fb CJK Ideograph Extension A-34FB | 34fa CJK Ideograph Extension A-34FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34fd CJK Ideograph Extension A-34FD | 34fc CJK Ideograph Extension A-34FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34ff CJK Ideograph Extension A-34FF | 34fe CJK Ideograph Extension A-34FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3501 CJK Ideograph Extension A-3501 | 3500 CJK Ideograph Extension A-3500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3503 CJK Ideograph Extension A-3503 | 3502 CJK Ideograph Extension A-3502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3505 CJK Ideograph Extension A-3505 | 3504 CJK Ideograph Extension A-3504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3507 CJK Ideograph Extension A-3507 | 3506 CJK Ideograph Extension A-3506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3509 CJK Ideograph Extension A-3509 | 3508 CJK Ideograph Extension A-3508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 350b CJK Ideograph Extension A-350B | 350a CJK Ideograph Extension A-350A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 350d CJK Ideograph Extension A-350D | 350c CJK Ideograph Extension A-350C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 350f CJK Ideograph Extension A-350F | 350e CJK Ideograph Extension A-350E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3511 CJK Ideograph Extension A-3511 | 3510 CJK Ideograph Extension A-3510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3513 CJK Ideograph Extension A-3513 | 3512 CJK Ideograph Extension A-3512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3515 CJK Ideograph Extension A-3515 | 3514 CJK Ideograph Extension A-3514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3517 CJK Ideograph Extension A-3517 | 3516 CJK Ideograph Extension A-3516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3519 CJK Ideograph Extension A-3519 | 3518 CJK Ideograph Extension A-3518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 351b CJK Ideograph Extension A-351B | 351a CJK Ideograph Extension A-351A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 351d CJK Ideograph Extension A-351D | 351c CJK Ideograph Extension A-351C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 351f CJK Ideograph Extension A-351F | 351e CJK Ideograph Extension A-351E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3521 CJK Ideograph Extension A-3521 | 3520 CJK Ideograph Extension A-3520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3523 CJK Ideograph Extension A-3523 | 3522 CJK Ideograph Extension A-3522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3525 CJK Ideograph Extension A-3525 | 3524 CJK Ideograph Extension A-3524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3527 CJK Ideograph Extension A-3527 | 3526 CJK Ideograph Extension A-3526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3529 CJK Ideograph Extension A-3529 | 3528 CJK Ideograph Extension A-3528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 352b CJK Ideograph Extension A-352B | 352a CJK Ideograph Extension A-352A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 352d CJK Ideograph Extension A-352D | 352c CJK Ideograph Extension A-352C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 352f CJK Ideograph Extension A-352F | 352e CJK Ideograph Extension A-352E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3531 CJK Ideograph Extension A-3531 | 3530 CJK Ideograph Extension A-3530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3533 CJK Ideograph Extension A-3533 | 3532 CJK Ideograph Extension A-3532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3535 CJK Ideograph Extension A-3535 | 3534 CJK Ideograph Extension A-3534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3537 CJK Ideograph Extension A-3537 | 3536 CJK Ideograph Extension A-3536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3539 CJK Ideograph Extension A-3539 | 3538 CJK Ideograph Extension A-3538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 353b CJK Ideograph Extension A-353B | 353a CJK Ideograph Extension A-353A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 353d CJK Ideograph Extension A-353D | 353c CJK Ideograph Extension A-353C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 353f CJK Ideograph Extension A-353F | 353e CJK Ideograph Extension A-353E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3541 CJK Ideograph Extension A-3541 | 3540 CJK Ideograph Extension A-3540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3543 CJK Ideograph Extension A-3543 | 3542 CJK Ideograph Extension A-3542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3545 CJK Ideograph Extension A-3545 | 3544 CJK Ideograph Extension A-3544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3547 CJK Ideograph Extension A-3547 | 3546 CJK Ideograph Extension A-3546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3549 CJK Ideograph Extension A-3549 | 3548 CJK Ideograph Extension A-3548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 354b CJK Ideograph Extension A-354B | 354a CJK Ideograph Extension A-354A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 354d CJK Ideograph Extension A-354D | 354c CJK Ideograph Extension A-354C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 354f CJK Ideograph Extension A-354F | 354e CJK Ideograph Extension A-354E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3551 CJK Ideograph Extension A-3551 | 3550 CJK Ideograph Extension A-3550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3553 CJK Ideograph Extension A-3553 | 3552 CJK Ideograph Extension A-3552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3555 CJK Ideograph Extension A-3555 | 3554 CJK Ideograph Extension A-3554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3557 CJK Ideograph Extension A-3557 | 3556 CJK Ideograph Extension A-3556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3559 CJK Ideograph Extension A-3559 | 3558 CJK Ideograph Extension A-3558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 355b CJK Ideograph Extension A-355B | 355a CJK Ideograph Extension A-355A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 355d CJK Ideograph Extension A-355D | 355c CJK Ideograph Extension A-355C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 355f CJK Ideograph Extension A-355F | 355e CJK Ideograph Extension A-355E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3561 CJK Ideograph Extension A-3561 | 3560 CJK Ideograph Extension A-3560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3563 CJK Ideograph Extension A-3563 | 3562 CJK Ideograph Extension A-3562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3565 CJK Ideograph Extension A-3565 | 3564 CJK Ideograph Extension A-3564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3567 CJK Ideograph Extension A-3567 | 3566 CJK Ideograph Extension A-3566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3569 CJK Ideograph Extension A-3569 | 3568 CJK Ideograph Extension A-3568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 356b CJK Ideograph Extension A-356B | 356a CJK Ideograph Extension A-356A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 356d CJK Ideograph Extension A-356D | 356c CJK Ideograph Extension A-356C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 356f CJK Ideograph Extension A-356F | 356e CJK Ideograph Extension A-356E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3571 CJK Ideograph Extension A-3571 | 3570 CJK Ideograph Extension A-3570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3573 CJK Ideograph Extension A-3573 | 3572 CJK Ideograph Extension A-3572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3575 CJK Ideograph Extension A-3575 | 3574 CJK Ideograph Extension A-3574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3577 CJK Ideograph Extension A-3577 | 3576 CJK Ideograph Extension A-3576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3579 CJK Ideograph Extension A-3579 | 3578 CJK Ideograph Extension A-3578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 357b CJK Ideograph Extension A-357B | 357a CJK Ideograph Extension A-357A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 357d CJK Ideograph Extension A-357D | 357c CJK Ideograph Extension A-357C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 357f CJK Ideograph Extension A-357F | 357e CJK Ideograph Extension A-357E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3581 CJK Ideograph Extension A-3581 | 3580 CJK Ideograph Extension A-3580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3583 CJK Ideograph Extension A-3583 | 3582 CJK Ideograph Extension A-3582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3585 CJK Ideograph Extension A-3585 | 3584 CJK Ideograph Extension A-3584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3587 CJK Ideograph Extension A-3587 | 3586 CJK Ideograph Extension A-3586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3589 CJK Ideograph Extension A-3589 | 3588 CJK Ideograph Extension A-3588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 358b CJK Ideograph Extension A-358B | 358a CJK Ideograph Extension A-358A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 358d CJK Ideograph Extension A-358D | 358c CJK Ideograph Extension A-358C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 358f CJK Ideograph Extension A-358F | 358e CJK Ideograph Extension A-358E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3591 CJK Ideograph Extension A-3591 | 3590 CJK Ideograph Extension A-3590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3593 CJK Ideograph Extension A-3593 | 3592 CJK Ideograph Extension A-3592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3595 CJK Ideograph Extension A-3595 | 3594 CJK Ideograph Extension A-3594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3597 CJK Ideograph Extension A-3597 | 3596 CJK Ideograph Extension A-3596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3599 CJK Ideograph Extension A-3599 | 3598 CJK Ideograph Extension A-3598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 359b CJK Ideograph Extension A-359B | 359a CJK Ideograph Extension A-359A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 359d CJK Ideograph Extension A-359D | 359c CJK Ideograph Extension A-359C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 359f CJK Ideograph Extension A-359F | 359e CJK Ideograph Extension A-359E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35a1 CJK Ideograph Extension A-35A1 | 35a0 CJK Ideograph Extension A-35A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35a3 CJK Ideograph Extension A-35A3 | 35a2 CJK Ideograph Extension A-35A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35a5 CJK Ideograph Extension A-35A5 | 35a4 CJK Ideograph Extension A-35A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35a7 CJK Ideograph Extension A-35A7 | 35a6 CJK Ideograph Extension A-35A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35a9 CJK Ideograph Extension A-35A9 | 35a8 CJK Ideograph Extension A-35A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35ab CJK Ideograph Extension A-35AB | 35aa CJK Ideograph Extension A-35AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35ad CJK Ideograph Extension A-35AD | 35ac CJK Ideograph Extension A-35AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35af CJK Ideograph Extension A-35AF | 35ae CJK Ideograph Extension A-35AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35b1 CJK Ideograph Extension A-35B1 | 35b0 CJK Ideograph Extension A-35B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35b3 CJK Ideograph Extension A-35B3 | 35b2 CJK Ideograph Extension A-35B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35b5 CJK Ideograph Extension A-35B5 | 35b4 CJK Ideograph Extension A-35B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35b7 CJK Ideograph Extension A-35B7 | 35b6 CJK Ideograph Extension A-35B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35b9 CJK Ideograph Extension A-35B9 | 35b8 CJK Ideograph Extension A-35B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35bb CJK Ideograph Extension A-35BB | 35ba CJK Ideograph Extension A-35BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35bd CJK Ideograph Extension A-35BD | 35bc CJK Ideograph Extension A-35BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35bf CJK Ideograph Extension A-35BF | 35be CJK Ideograph Extension A-35BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35c1 CJK Ideograph Extension A-35C1 | 35c0 CJK Ideograph Extension A-35C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35c3 CJK Ideograph Extension A-35C3 | 35c2 CJK Ideograph Extension A-35C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35c5 CJK Ideograph Extension A-35C5 | 35c4 CJK Ideograph Extension A-35C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35c7 CJK Ideograph Extension A-35C7 | 35c6 CJK Ideograph Extension A-35C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35c9 CJK Ideograph Extension A-35C9 | 35c8 CJK Ideograph Extension A-35C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35cb CJK Ideograph Extension A-35CB | 35ca CJK Ideograph Extension A-35CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35cd CJK Ideograph Extension A-35CD | 35cc CJK Ideograph Extension A-35CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35cf CJK Ideograph Extension A-35CF | 35ce CJK Ideograph Extension A-35CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35d1 CJK Ideograph Extension A-35D1 | 35d0 CJK Ideograph Extension A-35D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35d3 CJK Ideograph Extension A-35D3 | 35d2 CJK Ideograph Extension A-35D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35d5 CJK Ideograph Extension A-35D5 | 35d4 CJK Ideograph Extension A-35D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35d7 CJK Ideograph Extension A-35D7 | 35d6 CJK Ideograph Extension A-35D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35d9 CJK Ideograph Extension A-35D9 | 35d8 CJK Ideograph Extension A-35D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35db CJK Ideograph Extension A-35DB | 35da CJK Ideograph Extension A-35DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35dd CJK Ideograph Extension A-35DD | 35dc CJK Ideograph Extension A-35DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35df CJK Ideograph Extension A-35DF | 35de CJK Ideograph Extension A-35DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35e1 CJK Ideograph Extension A-35E1 | 35e0 CJK Ideograph Extension A-35E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35e3 CJK Ideograph Extension A-35E3 | 35e2 CJK Ideograph Extension A-35E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35e5 CJK Ideograph Extension A-35E5 | 35e4 CJK Ideograph Extension A-35E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35e7 CJK Ideograph Extension A-35E7 | 35e6 CJK Ideograph Extension A-35E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35e9 CJK Ideograph Extension A-35E9 | 35e8 CJK Ideograph Extension A-35E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35eb CJK Ideograph Extension A-35EB | 35ea CJK Ideograph Extension A-35EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35ed CJK Ideograph Extension A-35ED | 35ec CJK Ideograph Extension A-35EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35ef CJK Ideograph Extension A-35EF | 35ee CJK Ideograph Extension A-35EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35f1 CJK Ideograph Extension A-35F1 | 35f0 CJK Ideograph Extension A-35F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35f3 CJK Ideograph Extension A-35F3 | 35f2 CJK Ideograph Extension A-35F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35f5 CJK Ideograph Extension A-35F5 | 35f4 CJK Ideograph Extension A-35F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35f7 CJK Ideograph Extension A-35F7 | 35f6 CJK Ideograph Extension A-35F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35f9 CJK Ideograph Extension A-35F9 | 35f8 CJK Ideograph Extension A-35F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35fb CJK Ideograph Extension A-35FB | 35fa CJK Ideograph Extension A-35FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35fd CJK Ideograph Extension A-35FD | 35fc CJK Ideograph Extension A-35FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35ff CJK Ideograph Extension A-35FF | 35fe CJK Ideograph Extension A-35FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3601 CJK Ideograph Extension A-3601 | 3600 CJK Ideograph Extension A-3600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3603 CJK Ideograph Extension A-3603 | 3602 CJK Ideograph Extension A-3602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3605 CJK Ideograph Extension A-3605 | 3604 CJK Ideograph Extension A-3604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3607 CJK Ideograph Extension A-3607 | 3606 CJK Ideograph Extension A-3606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3609 CJK Ideograph Extension A-3609 | 3608 CJK Ideograph Extension A-3608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 360b CJK Ideograph Extension A-360B | 360a CJK Ideograph Extension A-360A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 360d CJK Ideograph Extension A-360D | 360c CJK Ideograph Extension A-360C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 360f CJK Ideograph Extension A-360F | 360e CJK Ideograph Extension A-360E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3611 CJK Ideograph Extension A-3611 | 3610 CJK Ideograph Extension A-3610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3613 CJK Ideograph Extension A-3613 | 3612 CJK Ideograph Extension A-3612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3615 CJK Ideograph Extension A-3615 | 3614 CJK Ideograph Extension A-3614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3617 CJK Ideograph Extension A-3617 | 3616 CJK Ideograph Extension A-3616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3619 CJK Ideograph Extension A-3619 | 3618 CJK Ideograph Extension A-3618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 361b CJK Ideograph Extension A-361B | 361a CJK Ideograph Extension A-361A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 361d CJK Ideograph Extension A-361D | 361c CJK Ideograph Extension A-361C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 361f CJK Ideograph Extension A-361F | 361e CJK Ideograph Extension A-361E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3621 CJK Ideograph Extension A-3621 | 3620 CJK Ideograph Extension A-3620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3623 CJK Ideograph Extension A-3623 | 3622 CJK Ideograph Extension A-3622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3625 CJK Ideograph Extension A-3625 | 3624 CJK Ideograph Extension A-3624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3627 CJK Ideograph Extension A-3627 | 3626 CJK Ideograph Extension A-3626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3629 CJK Ideograph Extension A-3629 | 3628 CJK Ideograph Extension A-3628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 362b CJK Ideograph Extension A-362B | 362a CJK Ideograph Extension A-362A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 362d CJK Ideograph Extension A-362D | 362c CJK Ideograph Extension A-362C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 362f CJK Ideograph Extension A-362F | 362e CJK Ideograph Extension A-362E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3631 CJK Ideograph Extension A-3631 | 3630 CJK Ideograph Extension A-3630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3633 CJK Ideograph Extension A-3633 | 3632 CJK Ideograph Extension A-3632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3635 CJK Ideograph Extension A-3635 | 3634 CJK Ideograph Extension A-3634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3637 CJK Ideograph Extension A-3637 | 3636 CJK Ideograph Extension A-3636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3639 CJK Ideograph Extension A-3639 | 3638 CJK Ideograph Extension A-3638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 363b CJK Ideograph Extension A-363B | 363a CJK Ideograph Extension A-363A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 363d CJK Ideograph Extension A-363D | 363c CJK Ideograph Extension A-363C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 363f CJK Ideograph Extension A-363F | 363e CJK Ideograph Extension A-363E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3641 CJK Ideograph Extension A-3641 | 3640 CJK Ideograph Extension A-3640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3643 CJK Ideograph Extension A-3643 | 3642 CJK Ideograph Extension A-3642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3645 CJK Ideograph Extension A-3645 | 3644 CJK Ideograph Extension A-3644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3647 CJK Ideograph Extension A-3647 | 3646 CJK Ideograph Extension A-3646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3649 CJK Ideograph Extension A-3649 | 3648 CJK Ideograph Extension A-3648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 364b CJK Ideograph Extension A-364B | 364a CJK Ideograph Extension A-364A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 364d CJK Ideograph Extension A-364D | 364c CJK Ideograph Extension A-364C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 364f CJK Ideograph Extension A-364F | 364e CJK Ideograph Extension A-364E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3651 CJK Ideograph Extension A-3651 | 3650 CJK Ideograph Extension A-3650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3653 CJK Ideograph Extension A-3653 | 3652 CJK Ideograph Extension A-3652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3655 CJK Ideograph Extension A-3655 | 3654 CJK Ideograph Extension A-3654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3657 CJK Ideograph Extension A-3657 | 3656 CJK Ideograph Extension A-3656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3659 CJK Ideograph Extension A-3659 | 3658 CJK Ideograph Extension A-3658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 365b CJK Ideograph Extension A-365B | 365a CJK Ideograph Extension A-365A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 365d CJK Ideograph Extension A-365D | 365c CJK Ideograph Extension A-365C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 365f CJK Ideograph Extension A-365F | 365e CJK Ideograph Extension A-365E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3661 CJK Ideograph Extension A-3661 | 3660 CJK Ideograph Extension A-3660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3663 CJK Ideograph Extension A-3663 | 3662 CJK Ideograph Extension A-3662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3665 CJK Ideograph Extension A-3665 | 3664 CJK Ideograph Extension A-3664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3667 CJK Ideograph Extension A-3667 | 3666 CJK Ideograph Extension A-3666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3669 CJK Ideograph Extension A-3669 | 3668 CJK Ideograph Extension A-3668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 366b CJK Ideograph Extension A-366B | 366a CJK Ideograph Extension A-366A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 366d CJK Ideograph Extension A-366D | 366c CJK Ideograph Extension A-366C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 366f CJK Ideograph Extension A-366F | 366e CJK Ideograph Extension A-366E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3671 CJK Ideograph Extension A-3671 | 3670 CJK Ideograph Extension A-3670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3673 CJK Ideograph Extension A-3673 | 3672 CJK Ideograph Extension A-3672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3675 CJK Ideograph Extension A-3675 | 3674 CJK Ideograph Extension A-3674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3677 CJK Ideograph Extension A-3677 | 3676 CJK Ideograph Extension A-3676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3679 CJK Ideograph Extension A-3679 | 3678 CJK Ideograph Extension A-3678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 367b CJK Ideograph Extension A-367B | 367a CJK Ideograph Extension A-367A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 367d CJK Ideograph Extension A-367D | 367c CJK Ideograph Extension A-367C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 367f CJK Ideograph Extension A-367F | 367e CJK Ideograph Extension A-367E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3681 CJK Ideograph Extension A-3681 | 3680 CJK Ideograph Extension A-3680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3683 CJK Ideograph Extension A-3683 | 3682 CJK Ideograph Extension A-3682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3685 CJK Ideograph Extension A-3685 | 3684 CJK Ideograph Extension A-3684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3687 CJK Ideograph Extension A-3687 | 3686 CJK Ideograph Extension A-3686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3689 CJK Ideograph Extension A-3689 | 3688 CJK Ideograph Extension A-3688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 368b CJK Ideograph Extension A-368B | 368a CJK Ideograph Extension A-368A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 368d CJK Ideograph Extension A-368D | 368c CJK Ideograph Extension A-368C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 368f CJK Ideograph Extension A-368F | 368e CJK Ideograph Extension A-368E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3691 CJK Ideograph Extension A-3691 | 3690 CJK Ideograph Extension A-3690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3693 CJK Ideograph Extension A-3693 | 3692 CJK Ideograph Extension A-3692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3695 CJK Ideograph Extension A-3695 | 3694 CJK Ideograph Extension A-3694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3697 CJK Ideograph Extension A-3697 | 3696 CJK Ideograph Extension A-3696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3699 CJK Ideograph Extension A-3699 | 3698 CJK Ideograph Extension A-3698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 369b CJK Ideograph Extension A-369B | 369a CJK Ideograph Extension A-369A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 369d CJK Ideograph Extension A-369D | 369c CJK Ideograph Extension A-369C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 369f CJK Ideograph Extension A-369F | 369e CJK Ideograph Extension A-369E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36a1 CJK Ideograph Extension A-36A1 | 36a0 CJK Ideograph Extension A-36A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36a3 CJK Ideograph Extension A-36A3 | 36a2 CJK Ideograph Extension A-36A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36a5 CJK Ideograph Extension A-36A5 | 36a4 CJK Ideograph Extension A-36A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36a7 CJK Ideograph Extension A-36A7 | 36a6 CJK Ideograph Extension A-36A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36a9 CJK Ideograph Extension A-36A9 | 36a8 CJK Ideograph Extension A-36A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36ab CJK Ideograph Extension A-36AB | 36aa CJK Ideograph Extension A-36AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36ad CJK Ideograph Extension A-36AD | 36ac CJK Ideograph Extension A-36AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36af CJK Ideograph Extension A-36AF | 36ae CJK Ideograph Extension A-36AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36b1 CJK Ideograph Extension A-36B1 | 36b0 CJK Ideograph Extension A-36B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36b3 CJK Ideograph Extension A-36B3 | 36b2 CJK Ideograph Extension A-36B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36b5 CJK Ideograph Extension A-36B5 | 36b4 CJK Ideograph Extension A-36B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36b7 CJK Ideograph Extension A-36B7 | 36b6 CJK Ideograph Extension A-36B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36b9 CJK Ideograph Extension A-36B9 | 36b8 CJK Ideograph Extension A-36B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36bb CJK Ideograph Extension A-36BB | 36ba CJK Ideograph Extension A-36BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36bd CJK Ideograph Extension A-36BD | 36bc CJK Ideograph Extension A-36BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36bf CJK Ideograph Extension A-36BF | 36be CJK Ideograph Extension A-36BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36c1 CJK Ideograph Extension A-36C1 | 36c0 CJK Ideograph Extension A-36C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36c3 CJK Ideograph Extension A-36C3 | 36c2 CJK Ideograph Extension A-36C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36c5 CJK Ideograph Extension A-36C5 | 36c4 CJK Ideograph Extension A-36C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36c7 CJK Ideograph Extension A-36C7 | 36c6 CJK Ideograph Extension A-36C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36c9 CJK Ideograph Extension A-36C9 | 36c8 CJK Ideograph Extension A-36C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36cb CJK Ideograph Extension A-36CB | 36ca CJK Ideograph Extension A-36CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36cd CJK Ideograph Extension A-36CD | 36cc CJK Ideograph Extension A-36CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36cf CJK Ideograph Extension A-36CF | 36ce CJK Ideograph Extension A-36CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36d1 CJK Ideograph Extension A-36D1 | 36d0 CJK Ideograph Extension A-36D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36d3 CJK Ideograph Extension A-36D3 | 36d2 CJK Ideograph Extension A-36D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36d5 CJK Ideograph Extension A-36D5 | 36d4 CJK Ideograph Extension A-36D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36d7 CJK Ideograph Extension A-36D7 | 36d6 CJK Ideograph Extension A-36D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36d9 CJK Ideograph Extension A-36D9 | 36d8 CJK Ideograph Extension A-36D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36db CJK Ideograph Extension A-36DB | 36da CJK Ideograph Extension A-36DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36dd CJK Ideograph Extension A-36DD | 36dc CJK Ideograph Extension A-36DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36df CJK Ideograph Extension A-36DF | 36de CJK Ideograph Extension A-36DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36e1 CJK Ideograph Extension A-36E1 | 36e0 CJK Ideograph Extension A-36E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36e3 CJK Ideograph Extension A-36E3 | 36e2 CJK Ideograph Extension A-36E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36e5 CJK Ideograph Extension A-36E5 | 36e4 CJK Ideograph Extension A-36E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36e7 CJK Ideograph Extension A-36E7 | 36e6 CJK Ideograph Extension A-36E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36e9 CJK Ideograph Extension A-36E9 | 36e8 CJK Ideograph Extension A-36E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36eb CJK Ideograph Extension A-36EB | 36ea CJK Ideograph Extension A-36EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36ed CJK Ideograph Extension A-36ED | 36ec CJK Ideograph Extension A-36EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36ef CJK Ideograph Extension A-36EF | 36ee CJK Ideograph Extension A-36EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36f1 CJK Ideograph Extension A-36F1 | 36f0 CJK Ideograph Extension A-36F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36f3 CJK Ideograph Extension A-36F3 | 36f2 CJK Ideograph Extension A-36F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36f5 CJK Ideograph Extension A-36F5 | 36f4 CJK Ideograph Extension A-36F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36f7 CJK Ideograph Extension A-36F7 | 36f6 CJK Ideograph Extension A-36F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36f9 CJK Ideograph Extension A-36F9 | 36f8 CJK Ideograph Extension A-36F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36fb CJK Ideograph Extension A-36FB | 36fa CJK Ideograph Extension A-36FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36fd CJK Ideograph Extension A-36FD | 36fc CJK Ideograph Extension A-36FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36ff CJK Ideograph Extension A-36FF | 36fe CJK Ideograph Extension A-36FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3701 CJK Ideograph Extension A-3701 | 3700 CJK Ideograph Extension A-3700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3703 CJK Ideograph Extension A-3703 | 3702 CJK Ideograph Extension A-3702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3705 CJK Ideograph Extension A-3705 | 3704 CJK Ideograph Extension A-3704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3707 CJK Ideograph Extension A-3707 | 3706 CJK Ideograph Extension A-3706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3709 CJK Ideograph Extension A-3709 | 3708 CJK Ideograph Extension A-3708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 370b CJK Ideograph Extension A-370B | 370a CJK Ideograph Extension A-370A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 370d CJK Ideograph Extension A-370D | 370c CJK Ideograph Extension A-370C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 370f CJK Ideograph Extension A-370F | 370e CJK Ideograph Extension A-370E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3711 CJK Ideograph Extension A-3711 | 3710 CJK Ideograph Extension A-3710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3713 CJK Ideograph Extension A-3713 | 3712 CJK Ideograph Extension A-3712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3715 CJK Ideograph Extension A-3715 | 3714 CJK Ideograph Extension A-3714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3717 CJK Ideograph Extension A-3717 | 3716 CJK Ideograph Extension A-3716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3719 CJK Ideograph Extension A-3719 | 3718 CJK Ideograph Extension A-3718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 371b CJK Ideograph Extension A-371B | 371a CJK Ideograph Extension A-371A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 371d CJK Ideograph Extension A-371D | 371c CJK Ideograph Extension A-371C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 371f CJK Ideograph Extension A-371F | 371e CJK Ideograph Extension A-371E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3721 CJK Ideograph Extension A-3721 | 3720 CJK Ideograph Extension A-3720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3723 CJK Ideograph Extension A-3723 | 3722 CJK Ideograph Extension A-3722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3725 CJK Ideograph Extension A-3725 | 3724 CJK Ideograph Extension A-3724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3727 CJK Ideograph Extension A-3727 | 3726 CJK Ideograph Extension A-3726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3729 CJK Ideograph Extension A-3729 | 3728 CJK Ideograph Extension A-3728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 372b CJK Ideograph Extension A-372B | 372a CJK Ideograph Extension A-372A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 372d CJK Ideograph Extension A-372D | 372c CJK Ideograph Extension A-372C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 372f CJK Ideograph Extension A-372F | 372e CJK Ideograph Extension A-372E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3731 CJK Ideograph Extension A-3731 | 3730 CJK Ideograph Extension A-3730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3733 CJK Ideograph Extension A-3733 | 3732 CJK Ideograph Extension A-3732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3735 CJK Ideograph Extension A-3735 | 3734 CJK Ideograph Extension A-3734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3737 CJK Ideograph Extension A-3737 | 3736 CJK Ideograph Extension A-3736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3739 CJK Ideograph Extension A-3739 | 3738 CJK Ideograph Extension A-3738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 373b CJK Ideograph Extension A-373B | 373a CJK Ideograph Extension A-373A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 373d CJK Ideograph Extension A-373D | 373c CJK Ideograph Extension A-373C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 373f CJK Ideograph Extension A-373F | 373e CJK Ideograph Extension A-373E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3741 CJK Ideograph Extension A-3741 | 3740 CJK Ideograph Extension A-3740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3743 CJK Ideograph Extension A-3743 | 3742 CJK Ideograph Extension A-3742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3745 CJK Ideograph Extension A-3745 | 3744 CJK Ideograph Extension A-3744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3747 CJK Ideograph Extension A-3747 | 3746 CJK Ideograph Extension A-3746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3749 CJK Ideograph Extension A-3749 | 3748 CJK Ideograph Extension A-3748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 374b CJK Ideograph Extension A-374B | 374a CJK Ideograph Extension A-374A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 374d CJK Ideograph Extension A-374D | 374c CJK Ideograph Extension A-374C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 374f CJK Ideograph Extension A-374F | 374e CJK Ideograph Extension A-374E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3751 CJK Ideograph Extension A-3751 | 3750 CJK Ideograph Extension A-3750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3753 CJK Ideograph Extension A-3753 | 3752 CJK Ideograph Extension A-3752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3755 CJK Ideograph Extension A-3755 | 3754 CJK Ideograph Extension A-3754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3757 CJK Ideograph Extension A-3757 | 3756 CJK Ideograph Extension A-3756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3759 CJK Ideograph Extension A-3759 | 3758 CJK Ideograph Extension A-3758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 375b CJK Ideograph Extension A-375B | 375a CJK Ideograph Extension A-375A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 375d CJK Ideograph Extension A-375D | 375c CJK Ideograph Extension A-375C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 375f CJK Ideograph Extension A-375F | 375e CJK Ideograph Extension A-375E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3761 CJK Ideograph Extension A-3761 | 3760 CJK Ideograph Extension A-3760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3763 CJK Ideograph Extension A-3763 | 3762 CJK Ideograph Extension A-3762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3765 CJK Ideograph Extension A-3765 | 3764 CJK Ideograph Extension A-3764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3767 CJK Ideograph Extension A-3767 | 3766 CJK Ideograph Extension A-3766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3769 CJK Ideograph Extension A-3769 | 3768 CJK Ideograph Extension A-3768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 376b CJK Ideograph Extension A-376B | 376a CJK Ideograph Extension A-376A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 376d CJK Ideograph Extension A-376D | 376c CJK Ideograph Extension A-376C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 376f CJK Ideograph Extension A-376F | 376e CJK Ideograph Extension A-376E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3771 CJK Ideograph Extension A-3771 | 3770 CJK Ideograph Extension A-3770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3773 CJK Ideograph Extension A-3773 | 3772 CJK Ideograph Extension A-3772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3775 CJK Ideograph Extension A-3775 | 3774 CJK Ideograph Extension A-3774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3777 CJK Ideograph Extension A-3777 | 3776 CJK Ideograph Extension A-3776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3779 CJK Ideograph Extension A-3779 | 3778 CJK Ideograph Extension A-3778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 377b CJK Ideograph Extension A-377B | 377a CJK Ideograph Extension A-377A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 377d CJK Ideograph Extension A-377D | 377c CJK Ideograph Extension A-377C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 377f CJK Ideograph Extension A-377F | 377e CJK Ideograph Extension A-377E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3781 CJK Ideograph Extension A-3781 | 3780 CJK Ideograph Extension A-3780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3783 CJK Ideograph Extension A-3783 | 3782 CJK Ideograph Extension A-3782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3785 CJK Ideograph Extension A-3785 | 3784 CJK Ideograph Extension A-3784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3787 CJK Ideograph Extension A-3787 | 3786 CJK Ideograph Extension A-3786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3789 CJK Ideograph Extension A-3789 | 3788 CJK Ideograph Extension A-3788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 378b CJK Ideograph Extension A-378B | 378a CJK Ideograph Extension A-378A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 378d CJK Ideograph Extension A-378D | 378c CJK Ideograph Extension A-378C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 378f CJK Ideograph Extension A-378F | 378e CJK Ideograph Extension A-378E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3791 CJK Ideograph Extension A-3791 | 3790 CJK Ideograph Extension A-3790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3793 CJK Ideograph Extension A-3793 | 3792 CJK Ideograph Extension A-3792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3795 CJK Ideograph Extension A-3795 | 3794 CJK Ideograph Extension A-3794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3797 CJK Ideograph Extension A-3797 | 3796 CJK Ideograph Extension A-3796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3799 CJK Ideograph Extension A-3799 | 3798 CJK Ideograph Extension A-3798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 379b CJK Ideograph Extension A-379B | 379a CJK Ideograph Extension A-379A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 379d CJK Ideograph Extension A-379D | 379c CJK Ideograph Extension A-379C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 379f CJK Ideograph Extension A-379F | 379e CJK Ideograph Extension A-379E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37a1 CJK Ideograph Extension A-37A1 | 37a0 CJK Ideograph Extension A-37A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37a3 CJK Ideograph Extension A-37A3 | 37a2 CJK Ideograph Extension A-37A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37a5 CJK Ideograph Extension A-37A5 | 37a4 CJK Ideograph Extension A-37A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37a7 CJK Ideograph Extension A-37A7 | 37a6 CJK Ideograph Extension A-37A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37a9 CJK Ideograph Extension A-37A9 | 37a8 CJK Ideograph Extension A-37A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37ab CJK Ideograph Extension A-37AB | 37aa CJK Ideograph Extension A-37AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37ad CJK Ideograph Extension A-37AD | 37ac CJK Ideograph Extension A-37AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37af CJK Ideograph Extension A-37AF | 37ae CJK Ideograph Extension A-37AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37b1 CJK Ideograph Extension A-37B1 | 37b0 CJK Ideograph Extension A-37B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37b3 CJK Ideograph Extension A-37B3 | 37b2 CJK Ideograph Extension A-37B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37b5 CJK Ideograph Extension A-37B5 | 37b4 CJK Ideograph Extension A-37B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37b7 CJK Ideograph Extension A-37B7 | 37b6 CJK Ideograph Extension A-37B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37b9 CJK Ideograph Extension A-37B9 | 37b8 CJK Ideograph Extension A-37B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37bb CJK Ideograph Extension A-37BB | 37ba CJK Ideograph Extension A-37BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37bd CJK Ideograph Extension A-37BD | 37bc CJK Ideograph Extension A-37BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37bf CJK Ideograph Extension A-37BF | 37be CJK Ideograph Extension A-37BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37c1 CJK Ideograph Extension A-37C1 | 37c0 CJK Ideograph Extension A-37C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37c3 CJK Ideograph Extension A-37C3 | 37c2 CJK Ideograph Extension A-37C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37c5 CJK Ideograph Extension A-37C5 | 37c4 CJK Ideograph Extension A-37C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37c7 CJK Ideograph Extension A-37C7 | 37c6 CJK Ideograph Extension A-37C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37c9 CJK Ideograph Extension A-37C9 | 37c8 CJK Ideograph Extension A-37C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37cb CJK Ideograph Extension A-37CB | 37ca CJK Ideograph Extension A-37CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37cd CJK Ideograph Extension A-37CD | 37cc CJK Ideograph Extension A-37CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37cf CJK Ideograph Extension A-37CF | 37ce CJK Ideograph Extension A-37CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37d1 CJK Ideograph Extension A-37D1 | 37d0 CJK Ideograph Extension A-37D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37d3 CJK Ideograph Extension A-37D3 | 37d2 CJK Ideograph Extension A-37D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37d5 CJK Ideograph Extension A-37D5 | 37d4 CJK Ideograph Extension A-37D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37d7 CJK Ideograph Extension A-37D7 | 37d6 CJK Ideograph Extension A-37D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37d9 CJK Ideograph Extension A-37D9 | 37d8 CJK Ideograph Extension A-37D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37db CJK Ideograph Extension A-37DB | 37da CJK Ideograph Extension A-37DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37dd CJK Ideograph Extension A-37DD | 37dc CJK Ideograph Extension A-37DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37df CJK Ideograph Extension A-37DF | 37de CJK Ideograph Extension A-37DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37e1 CJK Ideograph Extension A-37E1 | 37e0 CJK Ideograph Extension A-37E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37e3 CJK Ideograph Extension A-37E3 | 37e2 CJK Ideograph Extension A-37E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37e5 CJK Ideograph Extension A-37E5 | 37e4 CJK Ideograph Extension A-37E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37e7 CJK Ideograph Extension A-37E7 | 37e6 CJK Ideograph Extension A-37E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37e9 CJK Ideograph Extension A-37E9 | 37e8 CJK Ideograph Extension A-37E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37eb CJK Ideograph Extension A-37EB | 37ea CJK Ideograph Extension A-37EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37ed CJK Ideograph Extension A-37ED | 37ec CJK Ideograph Extension A-37EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37ef CJK Ideograph Extension A-37EF | 37ee CJK Ideograph Extension A-37EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37f1 CJK Ideograph Extension A-37F1 | 37f0 CJK Ideograph Extension A-37F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37f3 CJK Ideograph Extension A-37F3 | 37f2 CJK Ideograph Extension A-37F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37f5 CJK Ideograph Extension A-37F5 | 37f4 CJK Ideograph Extension A-37F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37f7 CJK Ideograph Extension A-37F7 | 37f6 CJK Ideograph Extension A-37F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37f9 CJK Ideograph Extension A-37F9 | 37f8 CJK Ideograph Extension A-37F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37fb CJK Ideograph Extension A-37FB | 37fa CJK Ideograph Extension A-37FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37fd CJK Ideograph Extension A-37FD | 37fc CJK Ideograph Extension A-37FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37ff CJK Ideograph Extension A-37FF | 37fe CJK Ideograph Extension A-37FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3801 CJK Ideograph Extension A-3801 | 3800 CJK Ideograph Extension A-3800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3803 CJK Ideograph Extension A-3803 | 3802 CJK Ideograph Extension A-3802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3805 CJK Ideograph Extension A-3805 | 3804 CJK Ideograph Extension A-3804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3807 CJK Ideograph Extension A-3807 | 3806 CJK Ideograph Extension A-3806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3809 CJK Ideograph Extension A-3809 | 3808 CJK Ideograph Extension A-3808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 380b CJK Ideograph Extension A-380B | 380a CJK Ideograph Extension A-380A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 380d CJK Ideograph Extension A-380D | 380c CJK Ideograph Extension A-380C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 380f CJK Ideograph Extension A-380F | 380e CJK Ideograph Extension A-380E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3811 CJK Ideograph Extension A-3811 | 3810 CJK Ideograph Extension A-3810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3813 CJK Ideograph Extension A-3813 | 3812 CJK Ideograph Extension A-3812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3815 CJK Ideograph Extension A-3815 | 3814 CJK Ideograph Extension A-3814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3817 CJK Ideograph Extension A-3817 | 3816 CJK Ideograph Extension A-3816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3819 CJK Ideograph Extension A-3819 | 3818 CJK Ideograph Extension A-3818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 381b CJK Ideograph Extension A-381B | 381a CJK Ideograph Extension A-381A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 381d CJK Ideograph Extension A-381D | 381c CJK Ideograph Extension A-381C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 381f CJK Ideograph Extension A-381F | 381e CJK Ideograph Extension A-381E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3821 CJK Ideograph Extension A-3821 | 3820 CJK Ideograph Extension A-3820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3823 CJK Ideograph Extension A-3823 | 3822 CJK Ideograph Extension A-3822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3825 CJK Ideograph Extension A-3825 | 3824 CJK Ideograph Extension A-3824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3827 CJK Ideograph Extension A-3827 | 3826 CJK Ideograph Extension A-3826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3829 CJK Ideograph Extension A-3829 | 3828 CJK Ideograph Extension A-3828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 382b CJK Ideograph Extension A-382B | 382a CJK Ideograph Extension A-382A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 382d CJK Ideograph Extension A-382D | 382c CJK Ideograph Extension A-382C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 382f CJK Ideograph Extension A-382F | 382e CJK Ideograph Extension A-382E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3831 CJK Ideograph Extension A-3831 | 3830 CJK Ideograph Extension A-3830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3833 CJK Ideograph Extension A-3833 | 3832 CJK Ideograph Extension A-3832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3835 CJK Ideograph Extension A-3835 | 3834 CJK Ideograph Extension A-3834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3837 CJK Ideograph Extension A-3837 | 3836 CJK Ideograph Extension A-3836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3839 CJK Ideograph Extension A-3839 | 3838 CJK Ideograph Extension A-3838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 383b CJK Ideograph Extension A-383B | 383a CJK Ideograph Extension A-383A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 383d CJK Ideograph Extension A-383D | 383c CJK Ideograph Extension A-383C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 383f CJK Ideograph Extension A-383F | 383e CJK Ideograph Extension A-383E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3841 CJK Ideograph Extension A-3841 | 3840 CJK Ideograph Extension A-3840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3843 CJK Ideograph Extension A-3843 | 3842 CJK Ideograph Extension A-3842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3845 CJK Ideograph Extension A-3845 | 3844 CJK Ideograph Extension A-3844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3847 CJK Ideograph Extension A-3847 | 3846 CJK Ideograph Extension A-3846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3849 CJK Ideograph Extension A-3849 | 3848 CJK Ideograph Extension A-3848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 384b CJK Ideograph Extension A-384B | 384a CJK Ideograph Extension A-384A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 384d CJK Ideograph Extension A-384D | 384c CJK Ideograph Extension A-384C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 384f CJK Ideograph Extension A-384F | 384e CJK Ideograph Extension A-384E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3851 CJK Ideograph Extension A-3851 | 3850 CJK Ideograph Extension A-3850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3853 CJK Ideograph Extension A-3853 | 3852 CJK Ideograph Extension A-3852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3855 CJK Ideograph Extension A-3855 | 3854 CJK Ideograph Extension A-3854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3857 CJK Ideograph Extension A-3857 | 3856 CJK Ideograph Extension A-3856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3859 CJK Ideograph Extension A-3859 | 3858 CJK Ideograph Extension A-3858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 385b CJK Ideograph Extension A-385B | 385a CJK Ideograph Extension A-385A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 385d CJK Ideograph Extension A-385D | 385c CJK Ideograph Extension A-385C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 385f CJK Ideograph Extension A-385F | 385e CJK Ideograph Extension A-385E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3861 CJK Ideograph Extension A-3861 | 3860 CJK Ideograph Extension A-3860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3863 CJK Ideograph Extension A-3863 | 3862 CJK Ideograph Extension A-3862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3865 CJK Ideograph Extension A-3865 | 3864 CJK Ideograph Extension A-3864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3867 CJK Ideograph Extension A-3867 | 3866 CJK Ideograph Extension A-3866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3869 CJK Ideograph Extension A-3869 | 3868 CJK Ideograph Extension A-3868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 386b CJK Ideograph Extension A-386B | 386a CJK Ideograph Extension A-386A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 386d CJK Ideograph Extension A-386D | 386c CJK Ideograph Extension A-386C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 386f CJK Ideograph Extension A-386F | 386e CJK Ideograph Extension A-386E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3871 CJK Ideograph Extension A-3871 | 3870 CJK Ideograph Extension A-3870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3873 CJK Ideograph Extension A-3873 | 3872 CJK Ideograph Extension A-3872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3875 CJK Ideograph Extension A-3875 | 3874 CJK Ideograph Extension A-3874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3877 CJK Ideograph Extension A-3877 | 3876 CJK Ideograph Extension A-3876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3879 CJK Ideograph Extension A-3879 | 3878 CJK Ideograph Extension A-3878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 387b CJK Ideograph Extension A-387B | 387a CJK Ideograph Extension A-387A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 387d CJK Ideograph Extension A-387D | 387c CJK Ideograph Extension A-387C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 387f CJK Ideograph Extension A-387F | 387e CJK Ideograph Extension A-387E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3881 CJK Ideograph Extension A-3881 | 3880 CJK Ideograph Extension A-3880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3883 CJK Ideograph Extension A-3883 | 3882 CJK Ideograph Extension A-3882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3885 CJK Ideograph Extension A-3885 | 3884 CJK Ideograph Extension A-3884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3887 CJK Ideograph Extension A-3887 | 3886 CJK Ideograph Extension A-3886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3889 CJK Ideograph Extension A-3889 | 3888 CJK Ideograph Extension A-3888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 388b CJK Ideograph Extension A-388B | 388a CJK Ideograph Extension A-388A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 388d CJK Ideograph Extension A-388D | 388c CJK Ideograph Extension A-388C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 388f CJK Ideograph Extension A-388F | 388e CJK Ideograph Extension A-388E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3891 CJK Ideograph Extension A-3891 | 3890 CJK Ideograph Extension A-3890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3893 CJK Ideograph Extension A-3893 | 3892 CJK Ideograph Extension A-3892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3895 CJK Ideograph Extension A-3895 | 3894 CJK Ideograph Extension A-3894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3897 CJK Ideograph Extension A-3897 | 3896 CJK Ideograph Extension A-3896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3899 CJK Ideograph Extension A-3899 | 3898 CJK Ideograph Extension A-3898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 389b CJK Ideograph Extension A-389B | 389a CJK Ideograph Extension A-389A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 389d CJK Ideograph Extension A-389D | 389c CJK Ideograph Extension A-389C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 389f CJK Ideograph Extension A-389F | 389e CJK Ideograph Extension A-389E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38a1 CJK Ideograph Extension A-38A1 | 38a0 CJK Ideograph Extension A-38A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38a3 CJK Ideograph Extension A-38A3 | 38a2 CJK Ideograph Extension A-38A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38a5 CJK Ideograph Extension A-38A5 | 38a4 CJK Ideograph Extension A-38A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38a7 CJK Ideograph Extension A-38A7 | 38a6 CJK Ideograph Extension A-38A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38a9 CJK Ideograph Extension A-38A9 | 38a8 CJK Ideograph Extension A-38A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38ab CJK Ideograph Extension A-38AB | 38aa CJK Ideograph Extension A-38AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38ad CJK Ideograph Extension A-38AD | 38ac CJK Ideograph Extension A-38AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38af CJK Ideograph Extension A-38AF | 38ae CJK Ideograph Extension A-38AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38b1 CJK Ideograph Extension A-38B1 | 38b0 CJK Ideograph Extension A-38B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38b3 CJK Ideograph Extension A-38B3 | 38b2 CJK Ideograph Extension A-38B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38b5 CJK Ideograph Extension A-38B5 | 38b4 CJK Ideograph Extension A-38B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38b7 CJK Ideograph Extension A-38B7 | 38b6 CJK Ideograph Extension A-38B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38b9 CJK Ideograph Extension A-38B9 | 38b8 CJK Ideograph Extension A-38B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38bb CJK Ideograph Extension A-38BB | 38ba CJK Ideograph Extension A-38BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38bd CJK Ideograph Extension A-38BD | 38bc CJK Ideograph Extension A-38BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38bf CJK Ideograph Extension A-38BF | 38be CJK Ideograph Extension A-38BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38c1 CJK Ideograph Extension A-38C1 | 38c0 CJK Ideograph Extension A-38C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38c3 CJK Ideograph Extension A-38C3 | 38c2 CJK Ideograph Extension A-38C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38c5 CJK Ideograph Extension A-38C5 | 38c4 CJK Ideograph Extension A-38C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38c7 CJK Ideograph Extension A-38C7 | 38c6 CJK Ideograph Extension A-38C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38c9 CJK Ideograph Extension A-38C9 | 38c8 CJK Ideograph Extension A-38C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38cb CJK Ideograph Extension A-38CB | 38ca CJK Ideograph Extension A-38CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38cd CJK Ideograph Extension A-38CD | 38cc CJK Ideograph Extension A-38CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38cf CJK Ideograph Extension A-38CF | 38ce CJK Ideograph Extension A-38CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38d1 CJK Ideograph Extension A-38D1 | 38d0 CJK Ideograph Extension A-38D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38d3 CJK Ideograph Extension A-38D3 | 38d2 CJK Ideograph Extension A-38D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38d5 CJK Ideograph Extension A-38D5 | 38d4 CJK Ideograph Extension A-38D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38d7 CJK Ideograph Extension A-38D7 | 38d6 CJK Ideograph Extension A-38D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38d9 CJK Ideograph Extension A-38D9 | 38d8 CJK Ideograph Extension A-38D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38db CJK Ideograph Extension A-38DB | 38da CJK Ideograph Extension A-38DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38dd CJK Ideograph Extension A-38DD | 38dc CJK Ideograph Extension A-38DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38df CJK Ideograph Extension A-38DF | 38de CJK Ideograph Extension A-38DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38e1 CJK Ideograph Extension A-38E1 | 38e0 CJK Ideograph Extension A-38E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38e3 CJK Ideograph Extension A-38E3 | 38e2 CJK Ideograph Extension A-38E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38e5 CJK Ideograph Extension A-38E5 | 38e4 CJK Ideograph Extension A-38E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38e7 CJK Ideograph Extension A-38E7 | 38e6 CJK Ideograph Extension A-38E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38e9 CJK Ideograph Extension A-38E9 | 38e8 CJK Ideograph Extension A-38E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38eb CJK Ideograph Extension A-38EB | 38ea CJK Ideograph Extension A-38EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38ed CJK Ideograph Extension A-38ED | 38ec CJK Ideograph Extension A-38EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38ef CJK Ideograph Extension A-38EF | 38ee CJK Ideograph Extension A-38EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38f1 CJK Ideograph Extension A-38F1 | 38f0 CJK Ideograph Extension A-38F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38f3 CJK Ideograph Extension A-38F3 | 38f2 CJK Ideograph Extension A-38F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38f5 CJK Ideograph Extension A-38F5 | 38f4 CJK Ideograph Extension A-38F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38f7 CJK Ideograph Extension A-38F7 | 38f6 CJK Ideograph Extension A-38F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38f9 CJK Ideograph Extension A-38F9 | 38f8 CJK Ideograph Extension A-38F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38fb CJK Ideograph Extension A-38FB | 38fa CJK Ideograph Extension A-38FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38fd CJK Ideograph Extension A-38FD | 38fc CJK Ideograph Extension A-38FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38ff CJK Ideograph Extension A-38FF | 38fe CJK Ideograph Extension A-38FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3901 CJK Ideograph Extension A-3901 | 3900 CJK Ideograph Extension A-3900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3903 CJK Ideograph Extension A-3903 | 3902 CJK Ideograph Extension A-3902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3905 CJK Ideograph Extension A-3905 | 3904 CJK Ideograph Extension A-3904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3907 CJK Ideograph Extension A-3907 | 3906 CJK Ideograph Extension A-3906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3909 CJK Ideograph Extension A-3909 | 3908 CJK Ideograph Extension A-3908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 390b CJK Ideograph Extension A-390B | 390a CJK Ideograph Extension A-390A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 390d CJK Ideograph Extension A-390D | 390c CJK Ideograph Extension A-390C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 390f CJK Ideograph Extension A-390F | 390e CJK Ideograph Extension A-390E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3911 CJK Ideograph Extension A-3911 | 3910 CJK Ideograph Extension A-3910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3913 CJK Ideograph Extension A-3913 | 3912 CJK Ideograph Extension A-3912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3915 CJK Ideograph Extension A-3915 | 3914 CJK Ideograph Extension A-3914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3917 CJK Ideograph Extension A-3917 | 3916 CJK Ideograph Extension A-3916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3919 CJK Ideograph Extension A-3919 | 3918 CJK Ideograph Extension A-3918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 391b CJK Ideograph Extension A-391B | 391a CJK Ideograph Extension A-391A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 391d CJK Ideograph Extension A-391D | 391c CJK Ideograph Extension A-391C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 391f CJK Ideograph Extension A-391F | 391e CJK Ideograph Extension A-391E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3921 CJK Ideograph Extension A-3921 | 3920 CJK Ideograph Extension A-3920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3923 CJK Ideograph Extension A-3923 | 3922 CJK Ideograph Extension A-3922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3925 CJK Ideograph Extension A-3925 | 3924 CJK Ideograph Extension A-3924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3927 CJK Ideograph Extension A-3927 | 3926 CJK Ideograph Extension A-3926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3929 CJK Ideograph Extension A-3929 | 3928 CJK Ideograph Extension A-3928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 392b CJK Ideograph Extension A-392B | 392a CJK Ideograph Extension A-392A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 392d CJK Ideograph Extension A-392D | 392c CJK Ideograph Extension A-392C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 392f CJK Ideograph Extension A-392F | 392e CJK Ideograph Extension A-392E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3931 CJK Ideograph Extension A-3931 | 3930 CJK Ideograph Extension A-3930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3933 CJK Ideograph Extension A-3933 | 3932 CJK Ideograph Extension A-3932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3935 CJK Ideograph Extension A-3935 | 3934 CJK Ideograph Extension A-3934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3937 CJK Ideograph Extension A-3937 | 3936 CJK Ideograph Extension A-3936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3939 CJK Ideograph Extension A-3939 | 3938 CJK Ideograph Extension A-3938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 393b CJK Ideograph Extension A-393B | 393a CJK Ideograph Extension A-393A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 393d CJK Ideograph Extension A-393D | 393c CJK Ideograph Extension A-393C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 393f CJK Ideograph Extension A-393F | 393e CJK Ideograph Extension A-393E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3941 CJK Ideograph Extension A-3941 | 3940 CJK Ideograph Extension A-3940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3943 CJK Ideograph Extension A-3943 | 3942 CJK Ideograph Extension A-3942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3945 CJK Ideograph Extension A-3945 | 3944 CJK Ideograph Extension A-3944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3947 CJK Ideograph Extension A-3947 | 3946 CJK Ideograph Extension A-3946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3949 CJK Ideograph Extension A-3949 | 3948 CJK Ideograph Extension A-3948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 394b CJK Ideograph Extension A-394B | 394a CJK Ideograph Extension A-394A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 394d CJK Ideograph Extension A-394D | 394c CJK Ideograph Extension A-394C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 394f CJK Ideograph Extension A-394F | 394e CJK Ideograph Extension A-394E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3951 CJK Ideograph Extension A-3951 | 3950 CJK Ideograph Extension A-3950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3953 CJK Ideograph Extension A-3953 | 3952 CJK Ideograph Extension A-3952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3955 CJK Ideograph Extension A-3955 | 3954 CJK Ideograph Extension A-3954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3957 CJK Ideograph Extension A-3957 | 3956 CJK Ideograph Extension A-3956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3959 CJK Ideograph Extension A-3959 | 3958 CJK Ideograph Extension A-3958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 395b CJK Ideograph Extension A-395B | 395a CJK Ideograph Extension A-395A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 395d CJK Ideograph Extension A-395D | 395c CJK Ideograph Extension A-395C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 395f CJK Ideograph Extension A-395F | 395e CJK Ideograph Extension A-395E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3961 CJK Ideograph Extension A-3961 | 3960 CJK Ideograph Extension A-3960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3963 CJK Ideograph Extension A-3963 | 3962 CJK Ideograph Extension A-3962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3965 CJK Ideograph Extension A-3965 | 3964 CJK Ideograph Extension A-3964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3967 CJK Ideograph Extension A-3967 | 3966 CJK Ideograph Extension A-3966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3969 CJK Ideograph Extension A-3969 | 3968 CJK Ideograph Extension A-3968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 396b CJK Ideograph Extension A-396B | 396a CJK Ideograph Extension A-396A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 396d CJK Ideograph Extension A-396D | 396c CJK Ideograph Extension A-396C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 396f CJK Ideograph Extension A-396F | 396e CJK Ideograph Extension A-396E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3971 CJK Ideograph Extension A-3971 | 3970 CJK Ideograph Extension A-3970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3973 CJK Ideograph Extension A-3973 | 3972 CJK Ideograph Extension A-3972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3975 CJK Ideograph Extension A-3975 | 3974 CJK Ideograph Extension A-3974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3977 CJK Ideograph Extension A-3977 | 3976 CJK Ideograph Extension A-3976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3979 CJK Ideograph Extension A-3979 | 3978 CJK Ideograph Extension A-3978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 397b CJK Ideograph Extension A-397B | 397a CJK Ideograph Extension A-397A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 397d CJK Ideograph Extension A-397D | 397c CJK Ideograph Extension A-397C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 397f CJK Ideograph Extension A-397F | 397e CJK Ideograph Extension A-397E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3981 CJK Ideograph Extension A-3981 | 3980 CJK Ideograph Extension A-3980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3983 CJK Ideograph Extension A-3983 | 3982 CJK Ideograph Extension A-3982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3985 CJK Ideograph Extension A-3985 | 3984 CJK Ideograph Extension A-3984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3987 CJK Ideograph Extension A-3987 | 3986 CJK Ideograph Extension A-3986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3989 CJK Ideograph Extension A-3989 | 3988 CJK Ideograph Extension A-3988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 398b CJK Ideograph Extension A-398B | 398a CJK Ideograph Extension A-398A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 398d CJK Ideograph Extension A-398D | 398c CJK Ideograph Extension A-398C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 398f CJK Ideograph Extension A-398F | 398e CJK Ideograph Extension A-398E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3991 CJK Ideograph Extension A-3991 | 3990 CJK Ideograph Extension A-3990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3993 CJK Ideograph Extension A-3993 | 3992 CJK Ideograph Extension A-3992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3995 CJK Ideograph Extension A-3995 | 3994 CJK Ideograph Extension A-3994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3997 CJK Ideograph Extension A-3997 | 3996 CJK Ideograph Extension A-3996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3999 CJK Ideograph Extension A-3999 | 3998 CJK Ideograph Extension A-3998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 399b CJK Ideograph Extension A-399B | 399a CJK Ideograph Extension A-399A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 399d CJK Ideograph Extension A-399D | 399c CJK Ideograph Extension A-399C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 399f CJK Ideograph Extension A-399F | 399e CJK Ideograph Extension A-399E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39a1 CJK Ideograph Extension A-39A1 | 39a0 CJK Ideograph Extension A-39A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39a3 CJK Ideograph Extension A-39A3 | 39a2 CJK Ideograph Extension A-39A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39a5 CJK Ideograph Extension A-39A5 | 39a4 CJK Ideograph Extension A-39A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39a7 CJK Ideograph Extension A-39A7 | 39a6 CJK Ideograph Extension A-39A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39a9 CJK Ideograph Extension A-39A9 | 39a8 CJK Ideograph Extension A-39A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39ab CJK Ideograph Extension A-39AB | 39aa CJK Ideograph Extension A-39AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39ad CJK Ideograph Extension A-39AD | 39ac CJK Ideograph Extension A-39AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39af CJK Ideograph Extension A-39AF | 39ae CJK Ideograph Extension A-39AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39b1 CJK Ideograph Extension A-39B1 | 39b0 CJK Ideograph Extension A-39B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39b3 CJK Ideograph Extension A-39B3 | 39b2 CJK Ideograph Extension A-39B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39b5 CJK Ideograph Extension A-39B5 | 39b4 CJK Ideograph Extension A-39B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39b7 CJK Ideograph Extension A-39B7 | 39b6 CJK Ideograph Extension A-39B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39b9 CJK Ideograph Extension A-39B9 | 39b8 CJK Ideograph Extension A-39B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39bb CJK Ideograph Extension A-39BB | 39ba CJK Ideograph Extension A-39BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39bd CJK Ideograph Extension A-39BD | 39bc CJK Ideograph Extension A-39BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39bf CJK Ideograph Extension A-39BF | 39be CJK Ideograph Extension A-39BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39c1 CJK Ideograph Extension A-39C1 | 39c0 CJK Ideograph Extension A-39C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39c3 CJK Ideograph Extension A-39C3 | 39c2 CJK Ideograph Extension A-39C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39c5 CJK Ideograph Extension A-39C5 | 39c4 CJK Ideograph Extension A-39C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39c7 CJK Ideograph Extension A-39C7 | 39c6 CJK Ideograph Extension A-39C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39c9 CJK Ideograph Extension A-39C9 | 39c8 CJK Ideograph Extension A-39C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39cb CJK Ideograph Extension A-39CB | 39ca CJK Ideograph Extension A-39CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39cd CJK Ideograph Extension A-39CD | 39cc CJK Ideograph Extension A-39CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39cf CJK Ideograph Extension A-39CF | 39ce CJK Ideograph Extension A-39CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39d1 CJK Ideograph Extension A-39D1 | 39d0 CJK Ideograph Extension A-39D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39d3 CJK Ideograph Extension A-39D3 | 39d2 CJK Ideograph Extension A-39D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39d5 CJK Ideograph Extension A-39D5 | 39d4 CJK Ideograph Extension A-39D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39d7 CJK Ideograph Extension A-39D7 | 39d6 CJK Ideograph Extension A-39D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39d9 CJK Ideograph Extension A-39D9 | 39d8 CJK Ideograph Extension A-39D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39db CJK Ideograph Extension A-39DB | 39da CJK Ideograph Extension A-39DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39dd CJK Ideograph Extension A-39DD | 39dc CJK Ideograph Extension A-39DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39df CJK Ideograph Extension A-39DF | 39de CJK Ideograph Extension A-39DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39e1 CJK Ideograph Extension A-39E1 | 39e0 CJK Ideograph Extension A-39E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39e3 CJK Ideograph Extension A-39E3 | 39e2 CJK Ideograph Extension A-39E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39e5 CJK Ideograph Extension A-39E5 | 39e4 CJK Ideograph Extension A-39E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39e7 CJK Ideograph Extension A-39E7 | 39e6 CJK Ideograph Extension A-39E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39e9 CJK Ideograph Extension A-39E9 | 39e8 CJK Ideograph Extension A-39E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39eb CJK Ideograph Extension A-39EB | 39ea CJK Ideograph Extension A-39EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39ed CJK Ideograph Extension A-39ED | 39ec CJK Ideograph Extension A-39EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39ef CJK Ideograph Extension A-39EF | 39ee CJK Ideograph Extension A-39EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39f1 CJK Ideograph Extension A-39F1 | 39f0 CJK Ideograph Extension A-39F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39f3 CJK Ideograph Extension A-39F3 | 39f2 CJK Ideograph Extension A-39F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39f5 CJK Ideograph Extension A-39F5 | 39f4 CJK Ideograph Extension A-39F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39f7 CJK Ideograph Extension A-39F7 | 39f6 CJK Ideograph Extension A-39F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39f9 CJK Ideograph Extension A-39F9 | 39f8 CJK Ideograph Extension A-39F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39fb CJK Ideograph Extension A-39FB | 39fa CJK Ideograph Extension A-39FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39fd CJK Ideograph Extension A-39FD | 39fc CJK Ideograph Extension A-39FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39ff CJK Ideograph Extension A-39FF | 39fe CJK Ideograph Extension A-39FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a01 CJK Ideograph Extension A-3A01 | 3a00 CJK Ideograph Extension A-3A00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a03 CJK Ideograph Extension A-3A03 | 3a02 CJK Ideograph Extension A-3A02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a05 CJK Ideograph Extension A-3A05 | 3a04 CJK Ideograph Extension A-3A04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a07 CJK Ideograph Extension A-3A07 | 3a06 CJK Ideograph Extension A-3A06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a09 CJK Ideograph Extension A-3A09 | 3a08 CJK Ideograph Extension A-3A08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a0b CJK Ideograph Extension A-3A0B | 3a0a CJK Ideograph Extension A-3A0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a0d CJK Ideograph Extension A-3A0D | 3a0c CJK Ideograph Extension A-3A0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a0f CJK Ideograph Extension A-3A0F | 3a0e CJK Ideograph Extension A-3A0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a11 CJK Ideograph Extension A-3A11 | 3a10 CJK Ideograph Extension A-3A10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a13 CJK Ideograph Extension A-3A13 | 3a12 CJK Ideograph Extension A-3A12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a15 CJK Ideograph Extension A-3A15 | 3a14 CJK Ideograph Extension A-3A14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a17 CJK Ideograph Extension A-3A17 | 3a16 CJK Ideograph Extension A-3A16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a19 CJK Ideograph Extension A-3A19 | 3a18 CJK Ideograph Extension A-3A18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a1b CJK Ideograph Extension A-3A1B | 3a1a CJK Ideograph Extension A-3A1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a1d CJK Ideograph Extension A-3A1D | 3a1c CJK Ideograph Extension A-3A1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a1f CJK Ideograph Extension A-3A1F | 3a1e CJK Ideograph Extension A-3A1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a21 CJK Ideograph Extension A-3A21 | 3a20 CJK Ideograph Extension A-3A20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a23 CJK Ideograph Extension A-3A23 | 3a22 CJK Ideograph Extension A-3A22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a25 CJK Ideograph Extension A-3A25 | 3a24 CJK Ideograph Extension A-3A24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a27 CJK Ideograph Extension A-3A27 | 3a26 CJK Ideograph Extension A-3A26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a29 CJK Ideograph Extension A-3A29 | 3a28 CJK Ideograph Extension A-3A28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a2b CJK Ideograph Extension A-3A2B | 3a2a CJK Ideograph Extension A-3A2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a2d CJK Ideograph Extension A-3A2D | 3a2c CJK Ideograph Extension A-3A2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a2f CJK Ideograph Extension A-3A2F | 3a2e CJK Ideograph Extension A-3A2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a31 CJK Ideograph Extension A-3A31 | 3a30 CJK Ideograph Extension A-3A30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a33 CJK Ideograph Extension A-3A33 | 3a32 CJK Ideograph Extension A-3A32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a35 CJK Ideograph Extension A-3A35 | 3a34 CJK Ideograph Extension A-3A34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a37 CJK Ideograph Extension A-3A37 | 3a36 CJK Ideograph Extension A-3A36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a39 CJK Ideograph Extension A-3A39 | 3a38 CJK Ideograph Extension A-3A38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a3b CJK Ideograph Extension A-3A3B | 3a3a CJK Ideograph Extension A-3A3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a3d CJK Ideograph Extension A-3A3D | 3a3c CJK Ideograph Extension A-3A3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a3f CJK Ideograph Extension A-3A3F | 3a3e CJK Ideograph Extension A-3A3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a41 CJK Ideograph Extension A-3A41 | 3a40 CJK Ideograph Extension A-3A40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a43 CJK Ideograph Extension A-3A43 | 3a42 CJK Ideograph Extension A-3A42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a45 CJK Ideograph Extension A-3A45 | 3a44 CJK Ideograph Extension A-3A44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a47 CJK Ideograph Extension A-3A47 | 3a46 CJK Ideograph Extension A-3A46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a49 CJK Ideograph Extension A-3A49 | 3a48 CJK Ideograph Extension A-3A48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a4b CJK Ideograph Extension A-3A4B | 3a4a CJK Ideograph Extension A-3A4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a4d CJK Ideograph Extension A-3A4D | 3a4c CJK Ideograph Extension A-3A4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a4f CJK Ideograph Extension A-3A4F | 3a4e CJK Ideograph Extension A-3A4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a51 CJK Ideograph Extension A-3A51 | 3a50 CJK Ideograph Extension A-3A50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a53 CJK Ideograph Extension A-3A53 | 3a52 CJK Ideograph Extension A-3A52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a55 CJK Ideograph Extension A-3A55 | 3a54 CJK Ideograph Extension A-3A54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a57 CJK Ideograph Extension A-3A57 | 3a56 CJK Ideograph Extension A-3A56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a59 CJK Ideograph Extension A-3A59 | 3a58 CJK Ideograph Extension A-3A58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a5b CJK Ideograph Extension A-3A5B | 3a5a CJK Ideograph Extension A-3A5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a5d CJK Ideograph Extension A-3A5D | 3a5c CJK Ideograph Extension A-3A5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a5f CJK Ideograph Extension A-3A5F | 3a5e CJK Ideograph Extension A-3A5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a61 CJK Ideograph Extension A-3A61 | 3a60 CJK Ideograph Extension A-3A60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a63 CJK Ideograph Extension A-3A63 | 3a62 CJK Ideograph Extension A-3A62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a65 CJK Ideograph Extension A-3A65 | 3a64 CJK Ideograph Extension A-3A64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a67 CJK Ideograph Extension A-3A67 | 3a66 CJK Ideograph Extension A-3A66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a69 CJK Ideograph Extension A-3A69 | 3a68 CJK Ideograph Extension A-3A68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a6b CJK Ideograph Extension A-3A6B | 3a6a CJK Ideograph Extension A-3A6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a6d CJK Ideograph Extension A-3A6D | 3a6c CJK Ideograph Extension A-3A6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a6f CJK Ideograph Extension A-3A6F | 3a6e CJK Ideograph Extension A-3A6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a71 CJK Ideograph Extension A-3A71 | 3a70 CJK Ideograph Extension A-3A70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a73 CJK Ideograph Extension A-3A73 | 3a72 CJK Ideograph Extension A-3A72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a75 CJK Ideograph Extension A-3A75 | 3a74 CJK Ideograph Extension A-3A74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a77 CJK Ideograph Extension A-3A77 | 3a76 CJK Ideograph Extension A-3A76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a79 CJK Ideograph Extension A-3A79 | 3a78 CJK Ideograph Extension A-3A78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a7b CJK Ideograph Extension A-3A7B | 3a7a CJK Ideograph Extension A-3A7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a7d CJK Ideograph Extension A-3A7D | 3a7c CJK Ideograph Extension A-3A7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a7f CJK Ideograph Extension A-3A7F | 3a7e CJK Ideograph Extension A-3A7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a81 CJK Ideograph Extension A-3A81 | 3a80 CJK Ideograph Extension A-3A80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a83 CJK Ideograph Extension A-3A83 | 3a82 CJK Ideograph Extension A-3A82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a85 CJK Ideograph Extension A-3A85 | 3a84 CJK Ideograph Extension A-3A84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a87 CJK Ideograph Extension A-3A87 | 3a86 CJK Ideograph Extension A-3A86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a89 CJK Ideograph Extension A-3A89 | 3a88 CJK Ideograph Extension A-3A88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a8b CJK Ideograph Extension A-3A8B | 3a8a CJK Ideograph Extension A-3A8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a8d CJK Ideograph Extension A-3A8D | 3a8c CJK Ideograph Extension A-3A8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a8f CJK Ideograph Extension A-3A8F | 3a8e CJK Ideograph Extension A-3A8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a91 CJK Ideograph Extension A-3A91 | 3a90 CJK Ideograph Extension A-3A90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a93 CJK Ideograph Extension A-3A93 | 3a92 CJK Ideograph Extension A-3A92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a95 CJK Ideograph Extension A-3A95 | 3a94 CJK Ideograph Extension A-3A94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a97 CJK Ideograph Extension A-3A97 | 3a96 CJK Ideograph Extension A-3A96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a99 CJK Ideograph Extension A-3A99 | 3a98 CJK Ideograph Extension A-3A98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a9b CJK Ideograph Extension A-3A9B | 3a9a CJK Ideograph Extension A-3A9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a9d CJK Ideograph Extension A-3A9D | 3a9c CJK Ideograph Extension A-3A9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a9f CJK Ideograph Extension A-3A9F | 3a9e CJK Ideograph Extension A-3A9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aa1 CJK Ideograph Extension A-3AA1 | 3aa0 CJK Ideograph Extension A-3AA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aa3 CJK Ideograph Extension A-3AA3 | 3aa2 CJK Ideograph Extension A-3AA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aa5 CJK Ideograph Extension A-3AA5 | 3aa4 CJK Ideograph Extension A-3AA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aa7 CJK Ideograph Extension A-3AA7 | 3aa6 CJK Ideograph Extension A-3AA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aa9 CJK Ideograph Extension A-3AA9 | 3aa8 CJK Ideograph Extension A-3AA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aab CJK Ideograph Extension A-3AAB | 3aaa CJK Ideograph Extension A-3AAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aad CJK Ideograph Extension A-3AAD | 3aac CJK Ideograph Extension A-3AAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aaf CJK Ideograph Extension A-3AAF | 3aae CJK Ideograph Extension A-3AAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ab1 CJK Ideograph Extension A-3AB1 | 3ab0 CJK Ideograph Extension A-3AB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ab3 CJK Ideograph Extension A-3AB3 | 3ab2 CJK Ideograph Extension A-3AB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ab5 CJK Ideograph Extension A-3AB5 | 3ab4 CJK Ideograph Extension A-3AB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ab7 CJK Ideograph Extension A-3AB7 | 3ab6 CJK Ideograph Extension A-3AB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ab9 CJK Ideograph Extension A-3AB9 | 3ab8 CJK Ideograph Extension A-3AB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3abb CJK Ideograph Extension A-3ABB | 3aba CJK Ideograph Extension A-3ABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3abd CJK Ideograph Extension A-3ABD | 3abc CJK Ideograph Extension A-3ABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3abf CJK Ideograph Extension A-3ABF | 3abe CJK Ideograph Extension A-3ABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ac1 CJK Ideograph Extension A-3AC1 | 3ac0 CJK Ideograph Extension A-3AC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ac3 CJK Ideograph Extension A-3AC3 | 3ac2 CJK Ideograph Extension A-3AC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ac5 CJK Ideograph Extension A-3AC5 | 3ac4 CJK Ideograph Extension A-3AC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ac7 CJK Ideograph Extension A-3AC7 | 3ac6 CJK Ideograph Extension A-3AC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ac9 CJK Ideograph Extension A-3AC9 | 3ac8 CJK Ideograph Extension A-3AC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3acb CJK Ideograph Extension A-3ACB | 3aca CJK Ideograph Extension A-3ACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3acd CJK Ideograph Extension A-3ACD | 3acc CJK Ideograph Extension A-3ACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3acf CJK Ideograph Extension A-3ACF | 3ace CJK Ideograph Extension A-3ACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ad1 CJK Ideograph Extension A-3AD1 | 3ad0 CJK Ideograph Extension A-3AD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ad3 CJK Ideograph Extension A-3AD3 | 3ad2 CJK Ideograph Extension A-3AD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ad5 CJK Ideograph Extension A-3AD5 | 3ad4 CJK Ideograph Extension A-3AD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ad7 CJK Ideograph Extension A-3AD7 | 3ad6 CJK Ideograph Extension A-3AD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ad9 CJK Ideograph Extension A-3AD9 | 3ad8 CJK Ideograph Extension A-3AD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3adb CJK Ideograph Extension A-3ADB | 3ada CJK Ideograph Extension A-3ADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3add CJK Ideograph Extension A-3ADD | 3adc CJK Ideograph Extension A-3ADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3adf CJK Ideograph Extension A-3ADF | 3ade CJK Ideograph Extension A-3ADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ae1 CJK Ideograph Extension A-3AE1 | 3ae0 CJK Ideograph Extension A-3AE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ae3 CJK Ideograph Extension A-3AE3 | 3ae2 CJK Ideograph Extension A-3AE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ae5 CJK Ideograph Extension A-3AE5 | 3ae4 CJK Ideograph Extension A-3AE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ae7 CJK Ideograph Extension A-3AE7 | 3ae6 CJK Ideograph Extension A-3AE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ae9 CJK Ideograph Extension A-3AE9 | 3ae8 CJK Ideograph Extension A-3AE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aeb CJK Ideograph Extension A-3AEB | 3aea CJK Ideograph Extension A-3AEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aed CJK Ideograph Extension A-3AED | 3aec CJK Ideograph Extension A-3AEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aef CJK Ideograph Extension A-3AEF | 3aee CJK Ideograph Extension A-3AEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3af1 CJK Ideograph Extension A-3AF1 | 3af0 CJK Ideograph Extension A-3AF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3af3 CJK Ideograph Extension A-3AF3 | 3af2 CJK Ideograph Extension A-3AF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3af5 CJK Ideograph Extension A-3AF5 | 3af4 CJK Ideograph Extension A-3AF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3af7 CJK Ideograph Extension A-3AF7 | 3af6 CJK Ideograph Extension A-3AF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3af9 CJK Ideograph Extension A-3AF9 | 3af8 CJK Ideograph Extension A-3AF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3afb CJK Ideograph Extension A-3AFB | 3afa CJK Ideograph Extension A-3AFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3afd CJK Ideograph Extension A-3AFD | 3afc CJK Ideograph Extension A-3AFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aff CJK Ideograph Extension A-3AFF | 3afe CJK Ideograph Extension A-3AFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b01 CJK Ideograph Extension A-3B01 | 3b00 CJK Ideograph Extension A-3B00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b03 CJK Ideograph Extension A-3B03 | 3b02 CJK Ideograph Extension A-3B02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b05 CJK Ideograph Extension A-3B05 | 3b04 CJK Ideograph Extension A-3B04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b07 CJK Ideograph Extension A-3B07 | 3b06 CJK Ideograph Extension A-3B06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b09 CJK Ideograph Extension A-3B09 | 3b08 CJK Ideograph Extension A-3B08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b0b CJK Ideograph Extension A-3B0B | 3b0a CJK Ideograph Extension A-3B0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b0d CJK Ideograph Extension A-3B0D | 3b0c CJK Ideograph Extension A-3B0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b0f CJK Ideograph Extension A-3B0F | 3b0e CJK Ideograph Extension A-3B0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b11 CJK Ideograph Extension A-3B11 | 3b10 CJK Ideograph Extension A-3B10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b13 CJK Ideograph Extension A-3B13 | 3b12 CJK Ideograph Extension A-3B12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b15 CJK Ideograph Extension A-3B15 | 3b14 CJK Ideograph Extension A-3B14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b17 CJK Ideograph Extension A-3B17 | 3b16 CJK Ideograph Extension A-3B16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b19 CJK Ideograph Extension A-3B19 | 3b18 CJK Ideograph Extension A-3B18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b1b CJK Ideograph Extension A-3B1B | 3b1a CJK Ideograph Extension A-3B1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b1d CJK Ideograph Extension A-3B1D | 3b1c CJK Ideograph Extension A-3B1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b1f CJK Ideograph Extension A-3B1F | 3b1e CJK Ideograph Extension A-3B1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b21 CJK Ideograph Extension A-3B21 | 3b20 CJK Ideograph Extension A-3B20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b23 CJK Ideograph Extension A-3B23 | 3b22 CJK Ideograph Extension A-3B22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b25 CJK Ideograph Extension A-3B25 | 3b24 CJK Ideograph Extension A-3B24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b27 CJK Ideograph Extension A-3B27 | 3b26 CJK Ideograph Extension A-3B26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b29 CJK Ideograph Extension A-3B29 | 3b28 CJK Ideograph Extension A-3B28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b2b CJK Ideograph Extension A-3B2B | 3b2a CJK Ideograph Extension A-3B2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b2d CJK Ideograph Extension A-3B2D | 3b2c CJK Ideograph Extension A-3B2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b2f CJK Ideograph Extension A-3B2F | 3b2e CJK Ideograph Extension A-3B2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b31 CJK Ideograph Extension A-3B31 | 3b30 CJK Ideograph Extension A-3B30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b33 CJK Ideograph Extension A-3B33 | 3b32 CJK Ideograph Extension A-3B32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b35 CJK Ideograph Extension A-3B35 | 3b34 CJK Ideograph Extension A-3B34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b37 CJK Ideograph Extension A-3B37 | 3b36 CJK Ideograph Extension A-3B36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b39 CJK Ideograph Extension A-3B39 | 3b38 CJK Ideograph Extension A-3B38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b3b CJK Ideograph Extension A-3B3B | 3b3a CJK Ideograph Extension A-3B3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b3d CJK Ideograph Extension A-3B3D | 3b3c CJK Ideograph Extension A-3B3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b3f CJK Ideograph Extension A-3B3F | 3b3e CJK Ideograph Extension A-3B3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b41 CJK Ideograph Extension A-3B41 | 3b40 CJK Ideograph Extension A-3B40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b43 CJK Ideograph Extension A-3B43 | 3b42 CJK Ideograph Extension A-3B42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b45 CJK Ideograph Extension A-3B45 | 3b44 CJK Ideograph Extension A-3B44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b47 CJK Ideograph Extension A-3B47 | 3b46 CJK Ideograph Extension A-3B46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b49 CJK Ideograph Extension A-3B49 | 3b48 CJK Ideograph Extension A-3B48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b4b CJK Ideograph Extension A-3B4B | 3b4a CJK Ideograph Extension A-3B4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b4d CJK Ideograph Extension A-3B4D | 3b4c CJK Ideograph Extension A-3B4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b4f CJK Ideograph Extension A-3B4F | 3b4e CJK Ideograph Extension A-3B4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b51 CJK Ideograph Extension A-3B51 | 3b50 CJK Ideograph Extension A-3B50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b53 CJK Ideograph Extension A-3B53 | 3b52 CJK Ideograph Extension A-3B52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b55 CJK Ideograph Extension A-3B55 | 3b54 CJK Ideograph Extension A-3B54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b57 CJK Ideograph Extension A-3B57 | 3b56 CJK Ideograph Extension A-3B56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b59 CJK Ideograph Extension A-3B59 | 3b58 CJK Ideograph Extension A-3B58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b5b CJK Ideograph Extension A-3B5B | 3b5a CJK Ideograph Extension A-3B5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b5d CJK Ideograph Extension A-3B5D | 3b5c CJK Ideograph Extension A-3B5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b5f CJK Ideograph Extension A-3B5F | 3b5e CJK Ideograph Extension A-3B5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b61 CJK Ideograph Extension A-3B61 | 3b60 CJK Ideograph Extension A-3B60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b63 CJK Ideograph Extension A-3B63 | 3b62 CJK Ideograph Extension A-3B62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b65 CJK Ideograph Extension A-3B65 | 3b64 CJK Ideograph Extension A-3B64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b67 CJK Ideograph Extension A-3B67 | 3b66 CJK Ideograph Extension A-3B66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b69 CJK Ideograph Extension A-3B69 | 3b68 CJK Ideograph Extension A-3B68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b6b CJK Ideograph Extension A-3B6B | 3b6a CJK Ideograph Extension A-3B6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b6d CJK Ideograph Extension A-3B6D | 3b6c CJK Ideograph Extension A-3B6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b6f CJK Ideograph Extension A-3B6F | 3b6e CJK Ideograph Extension A-3B6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b71 CJK Ideograph Extension A-3B71 | 3b70 CJK Ideograph Extension A-3B70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b73 CJK Ideograph Extension A-3B73 | 3b72 CJK Ideograph Extension A-3B72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b75 CJK Ideograph Extension A-3B75 | 3b74 CJK Ideograph Extension A-3B74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b77 CJK Ideograph Extension A-3B77 | 3b76 CJK Ideograph Extension A-3B76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b79 CJK Ideograph Extension A-3B79 | 3b78 CJK Ideograph Extension A-3B78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b7b CJK Ideograph Extension A-3B7B | 3b7a CJK Ideograph Extension A-3B7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b7d CJK Ideograph Extension A-3B7D | 3b7c CJK Ideograph Extension A-3B7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b7f CJK Ideograph Extension A-3B7F | 3b7e CJK Ideograph Extension A-3B7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b81 CJK Ideograph Extension A-3B81 | 3b80 CJK Ideograph Extension A-3B80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b83 CJK Ideograph Extension A-3B83 | 3b82 CJK Ideograph Extension A-3B82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b85 CJK Ideograph Extension A-3B85 | 3b84 CJK Ideograph Extension A-3B84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b87 CJK Ideograph Extension A-3B87 | 3b86 CJK Ideograph Extension A-3B86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b89 CJK Ideograph Extension A-3B89 | 3b88 CJK Ideograph Extension A-3B88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b8b CJK Ideograph Extension A-3B8B | 3b8a CJK Ideograph Extension A-3B8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b8d CJK Ideograph Extension A-3B8D | 3b8c CJK Ideograph Extension A-3B8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b8f CJK Ideograph Extension A-3B8F | 3b8e CJK Ideograph Extension A-3B8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b91 CJK Ideograph Extension A-3B91 | 3b90 CJK Ideograph Extension A-3B90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b93 CJK Ideograph Extension A-3B93 | 3b92 CJK Ideograph Extension A-3B92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b95 CJK Ideograph Extension A-3B95 | 3b94 CJK Ideograph Extension A-3B94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b97 CJK Ideograph Extension A-3B97 | 3b96 CJK Ideograph Extension A-3B96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b99 CJK Ideograph Extension A-3B99 | 3b98 CJK Ideograph Extension A-3B98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b9b CJK Ideograph Extension A-3B9B | 3b9a CJK Ideograph Extension A-3B9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b9d CJK Ideograph Extension A-3B9D | 3b9c CJK Ideograph Extension A-3B9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b9f CJK Ideograph Extension A-3B9F | 3b9e CJK Ideograph Extension A-3B9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ba1 CJK Ideograph Extension A-3BA1 | 3ba0 CJK Ideograph Extension A-3BA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ba3 CJK Ideograph Extension A-3BA3 | 3ba2 CJK Ideograph Extension A-3BA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ba5 CJK Ideograph Extension A-3BA5 | 3ba4 CJK Ideograph Extension A-3BA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ba7 CJK Ideograph Extension A-3BA7 | 3ba6 CJK Ideograph Extension A-3BA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ba9 CJK Ideograph Extension A-3BA9 | 3ba8 CJK Ideograph Extension A-3BA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bab CJK Ideograph Extension A-3BAB | 3baa CJK Ideograph Extension A-3BAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bad CJK Ideograph Extension A-3BAD | 3bac CJK Ideograph Extension A-3BAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3baf CJK Ideograph Extension A-3BAF | 3bae CJK Ideograph Extension A-3BAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bb1 CJK Ideograph Extension A-3BB1 | 3bb0 CJK Ideograph Extension A-3BB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bb3 CJK Ideograph Extension A-3BB3 | 3bb2 CJK Ideograph Extension A-3BB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bb5 CJK Ideograph Extension A-3BB5 | 3bb4 CJK Ideograph Extension A-3BB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bb7 CJK Ideograph Extension A-3BB7 | 3bb6 CJK Ideograph Extension A-3BB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bb9 CJK Ideograph Extension A-3BB9 | 3bb8 CJK Ideograph Extension A-3BB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bbb CJK Ideograph Extension A-3BBB | 3bba CJK Ideograph Extension A-3BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bbd CJK Ideograph Extension A-3BBD | 3bbc CJK Ideograph Extension A-3BBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bbf CJK Ideograph Extension A-3BBF | 3bbe CJK Ideograph Extension A-3BBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bc1 CJK Ideograph Extension A-3BC1 | 3bc0 CJK Ideograph Extension A-3BC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bc3 CJK Ideograph Extension A-3BC3 | 3bc2 CJK Ideograph Extension A-3BC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bc5 CJK Ideograph Extension A-3BC5 | 3bc4 CJK Ideograph Extension A-3BC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bc7 CJK Ideograph Extension A-3BC7 | 3bc6 CJK Ideograph Extension A-3BC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bc9 CJK Ideograph Extension A-3BC9 | 3bc8 CJK Ideograph Extension A-3BC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bcb CJK Ideograph Extension A-3BCB | 3bca CJK Ideograph Extension A-3BCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bcd CJK Ideograph Extension A-3BCD | 3bcc CJK Ideograph Extension A-3BCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bcf CJK Ideograph Extension A-3BCF | 3bce CJK Ideograph Extension A-3BCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bd1 CJK Ideograph Extension A-3BD1 | 3bd0 CJK Ideograph Extension A-3BD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bd3 CJK Ideograph Extension A-3BD3 | 3bd2 CJK Ideograph Extension A-3BD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bd5 CJK Ideograph Extension A-3BD5 | 3bd4 CJK Ideograph Extension A-3BD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bd7 CJK Ideograph Extension A-3BD7 | 3bd6 CJK Ideograph Extension A-3BD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bd9 CJK Ideograph Extension A-3BD9 | 3bd8 CJK Ideograph Extension A-3BD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bdb CJK Ideograph Extension A-3BDB | 3bda CJK Ideograph Extension A-3BDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bdd CJK Ideograph Extension A-3BDD | 3bdc CJK Ideograph Extension A-3BDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bdf CJK Ideograph Extension A-3BDF | 3bde CJK Ideograph Extension A-3BDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3be1 CJK Ideograph Extension A-3BE1 | 3be0 CJK Ideograph Extension A-3BE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3be3 CJK Ideograph Extension A-3BE3 | 3be2 CJK Ideograph Extension A-3BE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3be5 CJK Ideograph Extension A-3BE5 | 3be4 CJK Ideograph Extension A-3BE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3be7 CJK Ideograph Extension A-3BE7 | 3be6 CJK Ideograph Extension A-3BE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3be9 CJK Ideograph Extension A-3BE9 | 3be8 CJK Ideograph Extension A-3BE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3beb CJK Ideograph Extension A-3BEB | 3bea CJK Ideograph Extension A-3BEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bed CJK Ideograph Extension A-3BED | 3bec CJK Ideograph Extension A-3BEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bef CJK Ideograph Extension A-3BEF | 3bee CJK Ideograph Extension A-3BEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bf1 CJK Ideograph Extension A-3BF1 | 3bf0 CJK Ideograph Extension A-3BF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bf3 CJK Ideograph Extension A-3BF3 | 3bf2 CJK Ideograph Extension A-3BF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bf5 CJK Ideograph Extension A-3BF5 | 3bf4 CJK Ideograph Extension A-3BF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bf7 CJK Ideograph Extension A-3BF7 | 3bf6 CJK Ideograph Extension A-3BF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bf9 CJK Ideograph Extension A-3BF9 | 3bf8 CJK Ideograph Extension A-3BF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bfb CJK Ideograph Extension A-3BFB | 3bfa CJK Ideograph Extension A-3BFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bfd CJK Ideograph Extension A-3BFD | 3bfc CJK Ideograph Extension A-3BFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bff CJK Ideograph Extension A-3BFF | 3bfe CJK Ideograph Extension A-3BFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c01 CJK Ideograph Extension A-3C01 | 3c00 CJK Ideograph Extension A-3C00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c03 CJK Ideograph Extension A-3C03 | 3c02 CJK Ideograph Extension A-3C02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c05 CJK Ideograph Extension A-3C05 | 3c04 CJK Ideograph Extension A-3C04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c07 CJK Ideograph Extension A-3C07 | 3c06 CJK Ideograph Extension A-3C06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c09 CJK Ideograph Extension A-3C09 | 3c08 CJK Ideograph Extension A-3C08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c0b CJK Ideograph Extension A-3C0B | 3c0a CJK Ideograph Extension A-3C0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c0d CJK Ideograph Extension A-3C0D | 3c0c CJK Ideograph Extension A-3C0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c0f CJK Ideograph Extension A-3C0F | 3c0e CJK Ideograph Extension A-3C0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c11 CJK Ideograph Extension A-3C11 | 3c10 CJK Ideograph Extension A-3C10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c13 CJK Ideograph Extension A-3C13 | 3c12 CJK Ideograph Extension A-3C12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c15 CJK Ideograph Extension A-3C15 | 3c14 CJK Ideograph Extension A-3C14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c17 CJK Ideograph Extension A-3C17 | 3c16 CJK Ideograph Extension A-3C16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c19 CJK Ideograph Extension A-3C19 | 3c18 CJK Ideograph Extension A-3C18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c1b CJK Ideograph Extension A-3C1B | 3c1a CJK Ideograph Extension A-3C1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c1d CJK Ideograph Extension A-3C1D | 3c1c CJK Ideograph Extension A-3C1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c1f CJK Ideograph Extension A-3C1F | 3c1e CJK Ideograph Extension A-3C1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c21 CJK Ideograph Extension A-3C21 | 3c20 CJK Ideograph Extension A-3C20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c23 CJK Ideograph Extension A-3C23 | 3c22 CJK Ideograph Extension A-3C22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c25 CJK Ideograph Extension A-3C25 | 3c24 CJK Ideograph Extension A-3C24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c27 CJK Ideograph Extension A-3C27 | 3c26 CJK Ideograph Extension A-3C26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c29 CJK Ideograph Extension A-3C29 | 3c28 CJK Ideograph Extension A-3C28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c2b CJK Ideograph Extension A-3C2B | 3c2a CJK Ideograph Extension A-3C2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c2d CJK Ideograph Extension A-3C2D | 3c2c CJK Ideograph Extension A-3C2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c2f CJK Ideograph Extension A-3C2F | 3c2e CJK Ideograph Extension A-3C2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c31 CJK Ideograph Extension A-3C31 | 3c30 CJK Ideograph Extension A-3C30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c33 CJK Ideograph Extension A-3C33 | 3c32 CJK Ideograph Extension A-3C32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c35 CJK Ideograph Extension A-3C35 | 3c34 CJK Ideograph Extension A-3C34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c37 CJK Ideograph Extension A-3C37 | 3c36 CJK Ideograph Extension A-3C36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c39 CJK Ideograph Extension A-3C39 | 3c38 CJK Ideograph Extension A-3C38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c3b CJK Ideograph Extension A-3C3B | 3c3a CJK Ideograph Extension A-3C3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c3d CJK Ideograph Extension A-3C3D | 3c3c CJK Ideograph Extension A-3C3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c3f CJK Ideograph Extension A-3C3F | 3c3e CJK Ideograph Extension A-3C3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c41 CJK Ideograph Extension A-3C41 | 3c40 CJK Ideograph Extension A-3C40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c43 CJK Ideograph Extension A-3C43 | 3c42 CJK Ideograph Extension A-3C42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c45 CJK Ideograph Extension A-3C45 | 3c44 CJK Ideograph Extension A-3C44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c47 CJK Ideograph Extension A-3C47 | 3c46 CJK Ideograph Extension A-3C46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c49 CJK Ideograph Extension A-3C49 | 3c48 CJK Ideograph Extension A-3C48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c4b CJK Ideograph Extension A-3C4B | 3c4a CJK Ideograph Extension A-3C4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c4d CJK Ideograph Extension A-3C4D | 3c4c CJK Ideograph Extension A-3C4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c4f CJK Ideograph Extension A-3C4F | 3c4e CJK Ideograph Extension A-3C4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c51 CJK Ideograph Extension A-3C51 | 3c50 CJK Ideograph Extension A-3C50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c53 CJK Ideograph Extension A-3C53 | 3c52 CJK Ideograph Extension A-3C52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c55 CJK Ideograph Extension A-3C55 | 3c54 CJK Ideograph Extension A-3C54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c57 CJK Ideograph Extension A-3C57 | 3c56 CJK Ideograph Extension A-3C56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c59 CJK Ideograph Extension A-3C59 | 3c58 CJK Ideograph Extension A-3C58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c5b CJK Ideograph Extension A-3C5B | 3c5a CJK Ideograph Extension A-3C5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c5d CJK Ideograph Extension A-3C5D | 3c5c CJK Ideograph Extension A-3C5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c5f CJK Ideograph Extension A-3C5F | 3c5e CJK Ideograph Extension A-3C5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c61 CJK Ideograph Extension A-3C61 | 3c60 CJK Ideograph Extension A-3C60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c63 CJK Ideograph Extension A-3C63 | 3c62 CJK Ideograph Extension A-3C62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c65 CJK Ideograph Extension A-3C65 | 3c64 CJK Ideograph Extension A-3C64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c67 CJK Ideograph Extension A-3C67 | 3c66 CJK Ideograph Extension A-3C66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c69 CJK Ideograph Extension A-3C69 | 3c68 CJK Ideograph Extension A-3C68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c6b CJK Ideograph Extension A-3C6B | 3c6a CJK Ideograph Extension A-3C6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c6d CJK Ideograph Extension A-3C6D | 3c6c CJK Ideograph Extension A-3C6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c6f CJK Ideograph Extension A-3C6F | 3c6e CJK Ideograph Extension A-3C6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c71 CJK Ideograph Extension A-3C71 | 3c70 CJK Ideograph Extension A-3C70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c73 CJK Ideograph Extension A-3C73 | 3c72 CJK Ideograph Extension A-3C72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c75 CJK Ideograph Extension A-3C75 | 3c74 CJK Ideograph Extension A-3C74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c77 CJK Ideograph Extension A-3C77 | 3c76 CJK Ideograph Extension A-3C76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c79 CJK Ideograph Extension A-3C79 | 3c78 CJK Ideograph Extension A-3C78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c7b CJK Ideograph Extension A-3C7B | 3c7a CJK Ideograph Extension A-3C7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c7d CJK Ideograph Extension A-3C7D | 3c7c CJK Ideograph Extension A-3C7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c7f CJK Ideograph Extension A-3C7F | 3c7e CJK Ideograph Extension A-3C7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c81 CJK Ideograph Extension A-3C81 | 3c80 CJK Ideograph Extension A-3C80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c83 CJK Ideograph Extension A-3C83 | 3c82 CJK Ideograph Extension A-3C82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c85 CJK Ideograph Extension A-3C85 | 3c84 CJK Ideograph Extension A-3C84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c87 CJK Ideograph Extension A-3C87 | 3c86 CJK Ideograph Extension A-3C86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c89 CJK Ideograph Extension A-3C89 | 3c88 CJK Ideograph Extension A-3C88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c8b CJK Ideograph Extension A-3C8B | 3c8a CJK Ideograph Extension A-3C8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c8d CJK Ideograph Extension A-3C8D | 3c8c CJK Ideograph Extension A-3C8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c8f CJK Ideograph Extension A-3C8F | 3c8e CJK Ideograph Extension A-3C8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c91 CJK Ideograph Extension A-3C91 | 3c90 CJK Ideograph Extension A-3C90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c93 CJK Ideograph Extension A-3C93 | 3c92 CJK Ideograph Extension A-3C92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c95 CJK Ideograph Extension A-3C95 | 3c94 CJK Ideograph Extension A-3C94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c97 CJK Ideograph Extension A-3C97 | 3c96 CJK Ideograph Extension A-3C96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c99 CJK Ideograph Extension A-3C99 | 3c98 CJK Ideograph Extension A-3C98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c9b CJK Ideograph Extension A-3C9B | 3c9a CJK Ideograph Extension A-3C9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c9d CJK Ideograph Extension A-3C9D | 3c9c CJK Ideograph Extension A-3C9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c9f CJK Ideograph Extension A-3C9F | 3c9e CJK Ideograph Extension A-3C9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ca1 CJK Ideograph Extension A-3CA1 | 3ca0 CJK Ideograph Extension A-3CA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ca3 CJK Ideograph Extension A-3CA3 | 3ca2 CJK Ideograph Extension A-3CA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ca5 CJK Ideograph Extension A-3CA5 | 3ca4 CJK Ideograph Extension A-3CA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ca7 CJK Ideograph Extension A-3CA7 | 3ca6 CJK Ideograph Extension A-3CA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ca9 CJK Ideograph Extension A-3CA9 | 3ca8 CJK Ideograph Extension A-3CA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cab CJK Ideograph Extension A-3CAB | 3caa CJK Ideograph Extension A-3CAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cad CJK Ideograph Extension A-3CAD | 3cac CJK Ideograph Extension A-3CAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3caf CJK Ideograph Extension A-3CAF | 3cae CJK Ideograph Extension A-3CAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cb1 CJK Ideograph Extension A-3CB1 | 3cb0 CJK Ideograph Extension A-3CB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cb3 CJK Ideograph Extension A-3CB3 | 3cb2 CJK Ideograph Extension A-3CB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cb5 CJK Ideograph Extension A-3CB5 | 3cb4 CJK Ideograph Extension A-3CB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cb7 CJK Ideograph Extension A-3CB7 | 3cb6 CJK Ideograph Extension A-3CB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cb9 CJK Ideograph Extension A-3CB9 | 3cb8 CJK Ideograph Extension A-3CB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cbb CJK Ideograph Extension A-3CBB | 3cba CJK Ideograph Extension A-3CBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cbd CJK Ideograph Extension A-3CBD | 3cbc CJK Ideograph Extension A-3CBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cbf CJK Ideograph Extension A-3CBF | 3cbe CJK Ideograph Extension A-3CBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cc1 CJK Ideograph Extension A-3CC1 | 3cc0 CJK Ideograph Extension A-3CC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cc3 CJK Ideograph Extension A-3CC3 | 3cc2 CJK Ideograph Extension A-3CC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cc5 CJK Ideograph Extension A-3CC5 | 3cc4 CJK Ideograph Extension A-3CC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cc7 CJK Ideograph Extension A-3CC7 | 3cc6 CJK Ideograph Extension A-3CC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cc9 CJK Ideograph Extension A-3CC9 | 3cc8 CJK Ideograph Extension A-3CC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ccb CJK Ideograph Extension A-3CCB | 3cca CJK Ideograph Extension A-3CCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ccd CJK Ideograph Extension A-3CCD | 3ccc CJK Ideograph Extension A-3CCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ccf CJK Ideograph Extension A-3CCF | 3cce CJK Ideograph Extension A-3CCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cd1 CJK Ideograph Extension A-3CD1 | 3cd0 CJK Ideograph Extension A-3CD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cd3 CJK Ideograph Extension A-3CD3 | 3cd2 CJK Ideograph Extension A-3CD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cd5 CJK Ideograph Extension A-3CD5 | 3cd4 CJK Ideograph Extension A-3CD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cd7 CJK Ideograph Extension A-3CD7 | 3cd6 CJK Ideograph Extension A-3CD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cd9 CJK Ideograph Extension A-3CD9 | 3cd8 CJK Ideograph Extension A-3CD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cdb CJK Ideograph Extension A-3CDB | 3cda CJK Ideograph Extension A-3CDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cdd CJK Ideograph Extension A-3CDD | 3cdc CJK Ideograph Extension A-3CDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cdf CJK Ideograph Extension A-3CDF | 3cde CJK Ideograph Extension A-3CDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ce1 CJK Ideograph Extension A-3CE1 | 3ce0 CJK Ideograph Extension A-3CE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ce3 CJK Ideograph Extension A-3CE3 | 3ce2 CJK Ideograph Extension A-3CE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ce5 CJK Ideograph Extension A-3CE5 | 3ce4 CJK Ideograph Extension A-3CE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ce7 CJK Ideograph Extension A-3CE7 | 3ce6 CJK Ideograph Extension A-3CE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ce9 CJK Ideograph Extension A-3CE9 | 3ce8 CJK Ideograph Extension A-3CE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ceb CJK Ideograph Extension A-3CEB | 3cea CJK Ideograph Extension A-3CEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ced CJK Ideograph Extension A-3CED | 3cec CJK Ideograph Extension A-3CEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cef CJK Ideograph Extension A-3CEF | 3cee CJK Ideograph Extension A-3CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cf1 CJK Ideograph Extension A-3CF1 | 3cf0 CJK Ideograph Extension A-3CF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cf3 CJK Ideograph Extension A-3CF3 | 3cf2 CJK Ideograph Extension A-3CF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cf5 CJK Ideograph Extension A-3CF5 | 3cf4 CJK Ideograph Extension A-3CF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cf7 CJK Ideograph Extension A-3CF7 | 3cf6 CJK Ideograph Extension A-3CF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cf9 CJK Ideograph Extension A-3CF9 | 3cf8 CJK Ideograph Extension A-3CF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cfb CJK Ideograph Extension A-3CFB | 3cfa CJK Ideograph Extension A-3CFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cfd CJK Ideograph Extension A-3CFD | 3cfc CJK Ideograph Extension A-3CFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cff CJK Ideograph Extension A-3CFF | 3cfe CJK Ideograph Extension A-3CFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d01 CJK Ideograph Extension A-3D01 | 3d00 CJK Ideograph Extension A-3D00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d03 CJK Ideograph Extension A-3D03 | 3d02 CJK Ideograph Extension A-3D02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d05 CJK Ideograph Extension A-3D05 | 3d04 CJK Ideograph Extension A-3D04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d07 CJK Ideograph Extension A-3D07 | 3d06 CJK Ideograph Extension A-3D06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d09 CJK Ideograph Extension A-3D09 | 3d08 CJK Ideograph Extension A-3D08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d0b CJK Ideograph Extension A-3D0B | 3d0a CJK Ideograph Extension A-3D0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d0d CJK Ideograph Extension A-3D0D | 3d0c CJK Ideograph Extension A-3D0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d0f CJK Ideograph Extension A-3D0F | 3d0e CJK Ideograph Extension A-3D0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d11 CJK Ideograph Extension A-3D11 | 3d10 CJK Ideograph Extension A-3D10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d13 CJK Ideograph Extension A-3D13 | 3d12 CJK Ideograph Extension A-3D12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d15 CJK Ideograph Extension A-3D15 | 3d14 CJK Ideograph Extension A-3D14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d17 CJK Ideograph Extension A-3D17 | 3d16 CJK Ideograph Extension A-3D16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d19 CJK Ideograph Extension A-3D19 | 3d18 CJK Ideograph Extension A-3D18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d1b CJK Ideograph Extension A-3D1B | 3d1a CJK Ideograph Extension A-3D1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d1d CJK Ideograph Extension A-3D1D | 3d1c CJK Ideograph Extension A-3D1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d1f CJK Ideograph Extension A-3D1F | 3d1e CJK Ideograph Extension A-3D1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d21 CJK Ideograph Extension A-3D21 | 3d20 CJK Ideograph Extension A-3D20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d23 CJK Ideograph Extension A-3D23 | 3d22 CJK Ideograph Extension A-3D22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d25 CJK Ideograph Extension A-3D25 | 3d24 CJK Ideograph Extension A-3D24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d27 CJK Ideograph Extension A-3D27 | 3d26 CJK Ideograph Extension A-3D26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d29 CJK Ideograph Extension A-3D29 | 3d28 CJK Ideograph Extension A-3D28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d2b CJK Ideograph Extension A-3D2B | 3d2a CJK Ideograph Extension A-3D2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d2d CJK Ideograph Extension A-3D2D | 3d2c CJK Ideograph Extension A-3D2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d2f CJK Ideograph Extension A-3D2F | 3d2e CJK Ideograph Extension A-3D2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d31 CJK Ideograph Extension A-3D31 | 3d30 CJK Ideograph Extension A-3D30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d33 CJK Ideograph Extension A-3D33 | 3d32 CJK Ideograph Extension A-3D32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d35 CJK Ideograph Extension A-3D35 | 3d34 CJK Ideograph Extension A-3D34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d37 CJK Ideograph Extension A-3D37 | 3d36 CJK Ideograph Extension A-3D36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d39 CJK Ideograph Extension A-3D39 | 3d38 CJK Ideograph Extension A-3D38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d3b CJK Ideograph Extension A-3D3B | 3d3a CJK Ideograph Extension A-3D3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d3d CJK Ideograph Extension A-3D3D | 3d3c CJK Ideograph Extension A-3D3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d3f CJK Ideograph Extension A-3D3F | 3d3e CJK Ideograph Extension A-3D3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d41 CJK Ideograph Extension A-3D41 | 3d40 CJK Ideograph Extension A-3D40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d43 CJK Ideograph Extension A-3D43 | 3d42 CJK Ideograph Extension A-3D42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d45 CJK Ideograph Extension A-3D45 | 3d44 CJK Ideograph Extension A-3D44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d47 CJK Ideograph Extension A-3D47 | 3d46 CJK Ideograph Extension A-3D46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d49 CJK Ideograph Extension A-3D49 | 3d48 CJK Ideograph Extension A-3D48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d4b CJK Ideograph Extension A-3D4B | 3d4a CJK Ideograph Extension A-3D4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d4d CJK Ideograph Extension A-3D4D | 3d4c CJK Ideograph Extension A-3D4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d4f CJK Ideograph Extension A-3D4F | 3d4e CJK Ideograph Extension A-3D4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d51 CJK Ideograph Extension A-3D51 | 3d50 CJK Ideograph Extension A-3D50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d53 CJK Ideograph Extension A-3D53 | 3d52 CJK Ideograph Extension A-3D52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d55 CJK Ideograph Extension A-3D55 | 3d54 CJK Ideograph Extension A-3D54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d57 CJK Ideograph Extension A-3D57 | 3d56 CJK Ideograph Extension A-3D56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d59 CJK Ideograph Extension A-3D59 | 3d58 CJK Ideograph Extension A-3D58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d5b CJK Ideograph Extension A-3D5B | 3d5a CJK Ideograph Extension A-3D5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d5d CJK Ideograph Extension A-3D5D | 3d5c CJK Ideograph Extension A-3D5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d5f CJK Ideograph Extension A-3D5F | 3d5e CJK Ideograph Extension A-3D5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d61 CJK Ideograph Extension A-3D61 | 3d60 CJK Ideograph Extension A-3D60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d63 CJK Ideograph Extension A-3D63 | 3d62 CJK Ideograph Extension A-3D62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d65 CJK Ideograph Extension A-3D65 | 3d64 CJK Ideograph Extension A-3D64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d67 CJK Ideograph Extension A-3D67 | 3d66 CJK Ideograph Extension A-3D66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d69 CJK Ideograph Extension A-3D69 | 3d68 CJK Ideograph Extension A-3D68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d6b CJK Ideograph Extension A-3D6B | 3d6a CJK Ideograph Extension A-3D6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d6d CJK Ideograph Extension A-3D6D | 3d6c CJK Ideograph Extension A-3D6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d6f CJK Ideograph Extension A-3D6F | 3d6e CJK Ideograph Extension A-3D6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d71 CJK Ideograph Extension A-3D71 | 3d70 CJK Ideograph Extension A-3D70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d73 CJK Ideograph Extension A-3D73 | 3d72 CJK Ideograph Extension A-3D72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d75 CJK Ideograph Extension A-3D75 | 3d74 CJK Ideograph Extension A-3D74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d77 CJK Ideograph Extension A-3D77 | 3d76 CJK Ideograph Extension A-3D76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d79 CJK Ideograph Extension A-3D79 | 3d78 CJK Ideograph Extension A-3D78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d7b CJK Ideograph Extension A-3D7B | 3d7a CJK Ideograph Extension A-3D7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d7d CJK Ideograph Extension A-3D7D | 3d7c CJK Ideograph Extension A-3D7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d7f CJK Ideograph Extension A-3D7F | 3d7e CJK Ideograph Extension A-3D7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d81 CJK Ideograph Extension A-3D81 | 3d80 CJK Ideograph Extension A-3D80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d83 CJK Ideograph Extension A-3D83 | 3d82 CJK Ideograph Extension A-3D82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d85 CJK Ideograph Extension A-3D85 | 3d84 CJK Ideograph Extension A-3D84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d87 CJK Ideograph Extension A-3D87 | 3d86 CJK Ideograph Extension A-3D86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d89 CJK Ideograph Extension A-3D89 | 3d88 CJK Ideograph Extension A-3D88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d8b CJK Ideograph Extension A-3D8B | 3d8a CJK Ideograph Extension A-3D8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d8d CJK Ideograph Extension A-3D8D | 3d8c CJK Ideograph Extension A-3D8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d8f CJK Ideograph Extension A-3D8F | 3d8e CJK Ideograph Extension A-3D8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d91 CJK Ideograph Extension A-3D91 | 3d90 CJK Ideograph Extension A-3D90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d93 CJK Ideograph Extension A-3D93 | 3d92 CJK Ideograph Extension A-3D92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d95 CJK Ideograph Extension A-3D95 | 3d94 CJK Ideograph Extension A-3D94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d97 CJK Ideograph Extension A-3D97 | 3d96 CJK Ideograph Extension A-3D96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d99 CJK Ideograph Extension A-3D99 | 3d98 CJK Ideograph Extension A-3D98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d9b CJK Ideograph Extension A-3D9B | 3d9a CJK Ideograph Extension A-3D9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d9d CJK Ideograph Extension A-3D9D | 3d9c CJK Ideograph Extension A-3D9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d9f CJK Ideograph Extension A-3D9F | 3d9e CJK Ideograph Extension A-3D9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3da1 CJK Ideograph Extension A-3DA1 | 3da0 CJK Ideograph Extension A-3DA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3da3 CJK Ideograph Extension A-3DA3 | 3da2 CJK Ideograph Extension A-3DA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3da5 CJK Ideograph Extension A-3DA5 | 3da4 CJK Ideograph Extension A-3DA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3da7 CJK Ideograph Extension A-3DA7 | 3da6 CJK Ideograph Extension A-3DA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3da9 CJK Ideograph Extension A-3DA9 | 3da8 CJK Ideograph Extension A-3DA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dab CJK Ideograph Extension A-3DAB | 3daa CJK Ideograph Extension A-3DAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dad CJK Ideograph Extension A-3DAD | 3dac CJK Ideograph Extension A-3DAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3daf CJK Ideograph Extension A-3DAF | 3dae CJK Ideograph Extension A-3DAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3db1 CJK Ideograph Extension A-3DB1 | 3db0 CJK Ideograph Extension A-3DB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3db3 CJK Ideograph Extension A-3DB3 | 3db2 CJK Ideograph Extension A-3DB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3db5 CJK Ideograph Extension A-3DB5 | 3db4 CJK Ideograph Extension A-3DB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3db7 CJK Ideograph Extension A-3DB7 | 3db6 CJK Ideograph Extension A-3DB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3db9 CJK Ideograph Extension A-3DB9 | 3db8 CJK Ideograph Extension A-3DB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dbb CJK Ideograph Extension A-3DBB | 3dba CJK Ideograph Extension A-3DBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dbd CJK Ideograph Extension A-3DBD | 3dbc CJK Ideograph Extension A-3DBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dbf CJK Ideograph Extension A-3DBF | 3dbe CJK Ideograph Extension A-3DBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dc1 CJK Ideograph Extension A-3DC1 | 3dc0 CJK Ideograph Extension A-3DC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dc3 CJK Ideograph Extension A-3DC3 | 3dc2 CJK Ideograph Extension A-3DC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dc5 CJK Ideograph Extension A-3DC5 | 3dc4 CJK Ideograph Extension A-3DC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dc7 CJK Ideograph Extension A-3DC7 | 3dc6 CJK Ideograph Extension A-3DC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dc9 CJK Ideograph Extension A-3DC9 | 3dc8 CJK Ideograph Extension A-3DC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dcb CJK Ideograph Extension A-3DCB | 3dca CJK Ideograph Extension A-3DCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dcd CJK Ideograph Extension A-3DCD | 3dcc CJK Ideograph Extension A-3DCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dcf CJK Ideograph Extension A-3DCF | 3dce CJK Ideograph Extension A-3DCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dd1 CJK Ideograph Extension A-3DD1 | 3dd0 CJK Ideograph Extension A-3DD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dd3 CJK Ideograph Extension A-3DD3 | 3dd2 CJK Ideograph Extension A-3DD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dd5 CJK Ideograph Extension A-3DD5 | 3dd4 CJK Ideograph Extension A-3DD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dd7 CJK Ideograph Extension A-3DD7 | 3dd6 CJK Ideograph Extension A-3DD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dd9 CJK Ideograph Extension A-3DD9 | 3dd8 CJK Ideograph Extension A-3DD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ddb CJK Ideograph Extension A-3DDB | 3dda CJK Ideograph Extension A-3DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ddd CJK Ideograph Extension A-3DDD | 3ddc CJK Ideograph Extension A-3DDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ddf CJK Ideograph Extension A-3DDF | 3dde CJK Ideograph Extension A-3DDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3de1 CJK Ideograph Extension A-3DE1 | 3de0 CJK Ideograph Extension A-3DE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3de3 CJK Ideograph Extension A-3DE3 | 3de2 CJK Ideograph Extension A-3DE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3de5 CJK Ideograph Extension A-3DE5 | 3de4 CJK Ideograph Extension A-3DE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3de7 CJK Ideograph Extension A-3DE7 | 3de6 CJK Ideograph Extension A-3DE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3de9 CJK Ideograph Extension A-3DE9 | 3de8 CJK Ideograph Extension A-3DE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3deb CJK Ideograph Extension A-3DEB | 3dea CJK Ideograph Extension A-3DEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ded CJK Ideograph Extension A-3DED | 3dec CJK Ideograph Extension A-3DEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3def CJK Ideograph Extension A-3DEF | 3dee CJK Ideograph Extension A-3DEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3df1 CJK Ideograph Extension A-3DF1 | 3df0 CJK Ideograph Extension A-3DF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3df3 CJK Ideograph Extension A-3DF3 | 3df2 CJK Ideograph Extension A-3DF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3df5 CJK Ideograph Extension A-3DF5 | 3df4 CJK Ideograph Extension A-3DF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3df7 CJK Ideograph Extension A-3DF7 | 3df6 CJK Ideograph Extension A-3DF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3df9 CJK Ideograph Extension A-3DF9 | 3df8 CJK Ideograph Extension A-3DF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dfb CJK Ideograph Extension A-3DFB | 3dfa CJK Ideograph Extension A-3DFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dfd CJK Ideograph Extension A-3DFD | 3dfc CJK Ideograph Extension A-3DFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dff CJK Ideograph Extension A-3DFF | 3dfe CJK Ideograph Extension A-3DFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e01 CJK Ideograph Extension A-3E01 | 3e00 CJK Ideograph Extension A-3E00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e03 CJK Ideograph Extension A-3E03 | 3e02 CJK Ideograph Extension A-3E02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e05 CJK Ideograph Extension A-3E05 | 3e04 CJK Ideograph Extension A-3E04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e07 CJK Ideograph Extension A-3E07 | 3e06 CJK Ideograph Extension A-3E06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e09 CJK Ideograph Extension A-3E09 | 3e08 CJK Ideograph Extension A-3E08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e0b CJK Ideograph Extension A-3E0B | 3e0a CJK Ideograph Extension A-3E0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e0d CJK Ideograph Extension A-3E0D | 3e0c CJK Ideograph Extension A-3E0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e0f CJK Ideograph Extension A-3E0F | 3e0e CJK Ideograph Extension A-3E0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e11 CJK Ideograph Extension A-3E11 | 3e10 CJK Ideograph Extension A-3E10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e13 CJK Ideograph Extension A-3E13 | 3e12 CJK Ideograph Extension A-3E12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e15 CJK Ideograph Extension A-3E15 | 3e14 CJK Ideograph Extension A-3E14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e17 CJK Ideograph Extension A-3E17 | 3e16 CJK Ideograph Extension A-3E16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e19 CJK Ideograph Extension A-3E19 | 3e18 CJK Ideograph Extension A-3E18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e1b CJK Ideograph Extension A-3E1B | 3e1a CJK Ideograph Extension A-3E1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e1d CJK Ideograph Extension A-3E1D | 3e1c CJK Ideograph Extension A-3E1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e1f CJK Ideograph Extension A-3E1F | 3e1e CJK Ideograph Extension A-3E1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e21 CJK Ideograph Extension A-3E21 | 3e20 CJK Ideograph Extension A-3E20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e23 CJK Ideograph Extension A-3E23 | 3e22 CJK Ideograph Extension A-3E22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e25 CJK Ideograph Extension A-3E25 | 3e24 CJK Ideograph Extension A-3E24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e27 CJK Ideograph Extension A-3E27 | 3e26 CJK Ideograph Extension A-3E26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e29 CJK Ideograph Extension A-3E29 | 3e28 CJK Ideograph Extension A-3E28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e2b CJK Ideograph Extension A-3E2B | 3e2a CJK Ideograph Extension A-3E2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e2d CJK Ideograph Extension A-3E2D | 3e2c CJK Ideograph Extension A-3E2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e2f CJK Ideograph Extension A-3E2F | 3e2e CJK Ideograph Extension A-3E2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e31 CJK Ideograph Extension A-3E31 | 3e30 CJK Ideograph Extension A-3E30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e33 CJK Ideograph Extension A-3E33 | 3e32 CJK Ideograph Extension A-3E32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e35 CJK Ideograph Extension A-3E35 | 3e34 CJK Ideograph Extension A-3E34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e37 CJK Ideograph Extension A-3E37 | 3e36 CJK Ideograph Extension A-3E36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e39 CJK Ideograph Extension A-3E39 | 3e38 CJK Ideograph Extension A-3E38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e3b CJK Ideograph Extension A-3E3B | 3e3a CJK Ideograph Extension A-3E3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e3d CJK Ideograph Extension A-3E3D | 3e3c CJK Ideograph Extension A-3E3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e3f CJK Ideograph Extension A-3E3F | 3e3e CJK Ideograph Extension A-3E3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e41 CJK Ideograph Extension A-3E41 | 3e40 CJK Ideograph Extension A-3E40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e43 CJK Ideograph Extension A-3E43 | 3e42 CJK Ideograph Extension A-3E42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e45 CJK Ideograph Extension A-3E45 | 3e44 CJK Ideograph Extension A-3E44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e47 CJK Ideograph Extension A-3E47 | 3e46 CJK Ideograph Extension A-3E46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e49 CJK Ideograph Extension A-3E49 | 3e48 CJK Ideograph Extension A-3E48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e4b CJK Ideograph Extension A-3E4B | 3e4a CJK Ideograph Extension A-3E4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e4d CJK Ideograph Extension A-3E4D | 3e4c CJK Ideograph Extension A-3E4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e4f CJK Ideograph Extension A-3E4F | 3e4e CJK Ideograph Extension A-3E4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e51 CJK Ideograph Extension A-3E51 | 3e50 CJK Ideograph Extension A-3E50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e53 CJK Ideograph Extension A-3E53 | 3e52 CJK Ideograph Extension A-3E52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e55 CJK Ideograph Extension A-3E55 | 3e54 CJK Ideograph Extension A-3E54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e57 CJK Ideograph Extension A-3E57 | 3e56 CJK Ideograph Extension A-3E56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e59 CJK Ideograph Extension A-3E59 | 3e58 CJK Ideograph Extension A-3E58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e5b CJK Ideograph Extension A-3E5B | 3e5a CJK Ideograph Extension A-3E5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e5d CJK Ideograph Extension A-3E5D | 3e5c CJK Ideograph Extension A-3E5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e5f CJK Ideograph Extension A-3E5F | 3e5e CJK Ideograph Extension A-3E5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e61 CJK Ideograph Extension A-3E61 | 3e60 CJK Ideograph Extension A-3E60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e63 CJK Ideograph Extension A-3E63 | 3e62 CJK Ideograph Extension A-3E62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e65 CJK Ideograph Extension A-3E65 | 3e64 CJK Ideograph Extension A-3E64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e67 CJK Ideograph Extension A-3E67 | 3e66 CJK Ideograph Extension A-3E66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e69 CJK Ideograph Extension A-3E69 | 3e68 CJK Ideograph Extension A-3E68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e6b CJK Ideograph Extension A-3E6B | 3e6a CJK Ideograph Extension A-3E6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e6d CJK Ideograph Extension A-3E6D | 3e6c CJK Ideograph Extension A-3E6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e6f CJK Ideograph Extension A-3E6F | 3e6e CJK Ideograph Extension A-3E6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e71 CJK Ideograph Extension A-3E71 | 3e70 CJK Ideograph Extension A-3E70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e73 CJK Ideograph Extension A-3E73 | 3e72 CJK Ideograph Extension A-3E72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e75 CJK Ideograph Extension A-3E75 | 3e74 CJK Ideograph Extension A-3E74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e77 CJK Ideograph Extension A-3E77 | 3e76 CJK Ideograph Extension A-3E76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e79 CJK Ideograph Extension A-3E79 | 3e78 CJK Ideograph Extension A-3E78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e7b CJK Ideograph Extension A-3E7B | 3e7a CJK Ideograph Extension A-3E7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e7d CJK Ideograph Extension A-3E7D | 3e7c CJK Ideograph Extension A-3E7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e7f CJK Ideograph Extension A-3E7F | 3e7e CJK Ideograph Extension A-3E7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e81 CJK Ideograph Extension A-3E81 | 3e80 CJK Ideograph Extension A-3E80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e83 CJK Ideograph Extension A-3E83 | 3e82 CJK Ideograph Extension A-3E82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e85 CJK Ideograph Extension A-3E85 | 3e84 CJK Ideograph Extension A-3E84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e87 CJK Ideograph Extension A-3E87 | 3e86 CJK Ideograph Extension A-3E86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e89 CJK Ideograph Extension A-3E89 | 3e88 CJK Ideograph Extension A-3E88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e8b CJK Ideograph Extension A-3E8B | 3e8a CJK Ideograph Extension A-3E8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e8d CJK Ideograph Extension A-3E8D | 3e8c CJK Ideograph Extension A-3E8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e8f CJK Ideograph Extension A-3E8F | 3e8e CJK Ideograph Extension A-3E8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e91 CJK Ideograph Extension A-3E91 | 3e90 CJK Ideograph Extension A-3E90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e93 CJK Ideograph Extension A-3E93 | 3e92 CJK Ideograph Extension A-3E92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e95 CJK Ideograph Extension A-3E95 | 3e94 CJK Ideograph Extension A-3E94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e97 CJK Ideograph Extension A-3E97 | 3e96 CJK Ideograph Extension A-3E96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e99 CJK Ideograph Extension A-3E99 | 3e98 CJK Ideograph Extension A-3E98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e9b CJK Ideograph Extension A-3E9B | 3e9a CJK Ideograph Extension A-3E9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e9d CJK Ideograph Extension A-3E9D | 3e9c CJK Ideograph Extension A-3E9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e9f CJK Ideograph Extension A-3E9F | 3e9e CJK Ideograph Extension A-3E9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ea1 CJK Ideograph Extension A-3EA1 | 3ea0 CJK Ideograph Extension A-3EA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ea3 CJK Ideograph Extension A-3EA3 | 3ea2 CJK Ideograph Extension A-3EA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ea5 CJK Ideograph Extension A-3EA5 | 3ea4 CJK Ideograph Extension A-3EA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ea7 CJK Ideograph Extension A-3EA7 | 3ea6 CJK Ideograph Extension A-3EA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ea9 CJK Ideograph Extension A-3EA9 | 3ea8 CJK Ideograph Extension A-3EA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eab CJK Ideograph Extension A-3EAB | 3eaa CJK Ideograph Extension A-3EAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ead CJK Ideograph Extension A-3EAD | 3eac CJK Ideograph Extension A-3EAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eaf CJK Ideograph Extension A-3EAF | 3eae CJK Ideograph Extension A-3EAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eb1 CJK Ideograph Extension A-3EB1 | 3eb0 CJK Ideograph Extension A-3EB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eb3 CJK Ideograph Extension A-3EB3 | 3eb2 CJK Ideograph Extension A-3EB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eb5 CJK Ideograph Extension A-3EB5 | 3eb4 CJK Ideograph Extension A-3EB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eb7 CJK Ideograph Extension A-3EB7 | 3eb6 CJK Ideograph Extension A-3EB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eb9 CJK Ideograph Extension A-3EB9 | 3eb8 CJK Ideograph Extension A-3EB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ebb CJK Ideograph Extension A-3EBB | 3eba CJK Ideograph Extension A-3EBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ebd CJK Ideograph Extension A-3EBD | 3ebc CJK Ideograph Extension A-3EBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ebf CJK Ideograph Extension A-3EBF | 3ebe CJK Ideograph Extension A-3EBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ec1 CJK Ideograph Extension A-3EC1 | 3ec0 CJK Ideograph Extension A-3EC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ec3 CJK Ideograph Extension A-3EC3 | 3ec2 CJK Ideograph Extension A-3EC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ec5 CJK Ideograph Extension A-3EC5 | 3ec4 CJK Ideograph Extension A-3EC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ec7 CJK Ideograph Extension A-3EC7 | 3ec6 CJK Ideograph Extension A-3EC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ec9 CJK Ideograph Extension A-3EC9 | 3ec8 CJK Ideograph Extension A-3EC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ecb CJK Ideograph Extension A-3ECB | 3eca CJK Ideograph Extension A-3ECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ecd CJK Ideograph Extension A-3ECD | 3ecc CJK Ideograph Extension A-3ECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ecf CJK Ideograph Extension A-3ECF | 3ece CJK Ideograph Extension A-3ECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ed1 CJK Ideograph Extension A-3ED1 | 3ed0 CJK Ideograph Extension A-3ED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ed3 CJK Ideograph Extension A-3ED3 | 3ed2 CJK Ideograph Extension A-3ED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ed5 CJK Ideograph Extension A-3ED5 | 3ed4 CJK Ideograph Extension A-3ED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ed7 CJK Ideograph Extension A-3ED7 | 3ed6 CJK Ideograph Extension A-3ED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ed9 CJK Ideograph Extension A-3ED9 | 3ed8 CJK Ideograph Extension A-3ED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3edb CJK Ideograph Extension A-3EDB | 3eda CJK Ideograph Extension A-3EDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3edd CJK Ideograph Extension A-3EDD | 3edc CJK Ideograph Extension A-3EDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3edf CJK Ideograph Extension A-3EDF | 3ede CJK Ideograph Extension A-3EDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ee1 CJK Ideograph Extension A-3EE1 | 3ee0 CJK Ideograph Extension A-3EE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ee3 CJK Ideograph Extension A-3EE3 | 3ee2 CJK Ideograph Extension A-3EE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ee5 CJK Ideograph Extension A-3EE5 | 3ee4 CJK Ideograph Extension A-3EE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ee7 CJK Ideograph Extension A-3EE7 | 3ee6 CJK Ideograph Extension A-3EE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ee9 CJK Ideograph Extension A-3EE9 | 3ee8 CJK Ideograph Extension A-3EE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eeb CJK Ideograph Extension A-3EEB | 3eea CJK Ideograph Extension A-3EEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eed CJK Ideograph Extension A-3EED | 3eec CJK Ideograph Extension A-3EEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eef CJK Ideograph Extension A-3EEF | 3eee CJK Ideograph Extension A-3EEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ef1 CJK Ideograph Extension A-3EF1 | 3ef0 CJK Ideograph Extension A-3EF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ef3 CJK Ideograph Extension A-3EF3 | 3ef2 CJK Ideograph Extension A-3EF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ef5 CJK Ideograph Extension A-3EF5 | 3ef4 CJK Ideograph Extension A-3EF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ef7 CJK Ideograph Extension A-3EF7 | 3ef6 CJK Ideograph Extension A-3EF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ef9 CJK Ideograph Extension A-3EF9 | 3ef8 CJK Ideograph Extension A-3EF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3efb CJK Ideograph Extension A-3EFB | 3efa CJK Ideograph Extension A-3EFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3efd CJK Ideograph Extension A-3EFD | 3efc CJK Ideograph Extension A-3EFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eff CJK Ideograph Extension A-3EFF | 3efe CJK Ideograph Extension A-3EFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f01 CJK Ideograph Extension A-3F01 | 3f00 CJK Ideograph Extension A-3F00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f03 CJK Ideograph Extension A-3F03 | 3f02 CJK Ideograph Extension A-3F02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f05 CJK Ideograph Extension A-3F05 | 3f04 CJK Ideograph Extension A-3F04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f07 CJK Ideograph Extension A-3F07 | 3f06 CJK Ideograph Extension A-3F06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f09 CJK Ideograph Extension A-3F09 | 3f08 CJK Ideograph Extension A-3F08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f0b CJK Ideograph Extension A-3F0B | 3f0a CJK Ideograph Extension A-3F0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f0d CJK Ideograph Extension A-3F0D | 3f0c CJK Ideograph Extension A-3F0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f0f CJK Ideograph Extension A-3F0F | 3f0e CJK Ideograph Extension A-3F0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f11 CJK Ideograph Extension A-3F11 | 3f10 CJK Ideograph Extension A-3F10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f13 CJK Ideograph Extension A-3F13 | 3f12 CJK Ideograph Extension A-3F12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f15 CJK Ideograph Extension A-3F15 | 3f14 CJK Ideograph Extension A-3F14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f17 CJK Ideograph Extension A-3F17 | 3f16 CJK Ideograph Extension A-3F16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f19 CJK Ideograph Extension A-3F19 | 3f18 CJK Ideograph Extension A-3F18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f1b CJK Ideograph Extension A-3F1B | 3f1a CJK Ideograph Extension A-3F1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f1d CJK Ideograph Extension A-3F1D | 3f1c CJK Ideograph Extension A-3F1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f1f CJK Ideograph Extension A-3F1F | 3f1e CJK Ideograph Extension A-3F1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f21 CJK Ideograph Extension A-3F21 | 3f20 CJK Ideograph Extension A-3F20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f23 CJK Ideograph Extension A-3F23 | 3f22 CJK Ideograph Extension A-3F22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f25 CJK Ideograph Extension A-3F25 | 3f24 CJK Ideograph Extension A-3F24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f27 CJK Ideograph Extension A-3F27 | 3f26 CJK Ideograph Extension A-3F26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f29 CJK Ideograph Extension A-3F29 | 3f28 CJK Ideograph Extension A-3F28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f2b CJK Ideograph Extension A-3F2B | 3f2a CJK Ideograph Extension A-3F2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f2d CJK Ideograph Extension A-3F2D | 3f2c CJK Ideograph Extension A-3F2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f2f CJK Ideograph Extension A-3F2F | 3f2e CJK Ideograph Extension A-3F2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f31 CJK Ideograph Extension A-3F31 | 3f30 CJK Ideograph Extension A-3F30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f33 CJK Ideograph Extension A-3F33 | 3f32 CJK Ideograph Extension A-3F32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f35 CJK Ideograph Extension A-3F35 | 3f34 CJK Ideograph Extension A-3F34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f37 CJK Ideograph Extension A-3F37 | 3f36 CJK Ideograph Extension A-3F36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f39 CJK Ideograph Extension A-3F39 | 3f38 CJK Ideograph Extension A-3F38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f3b CJK Ideograph Extension A-3F3B | 3f3a CJK Ideograph Extension A-3F3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f3d CJK Ideograph Extension A-3F3D | 3f3c CJK Ideograph Extension A-3F3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f3f CJK Ideograph Extension A-3F3F | 3f3e CJK Ideograph Extension A-3F3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f41 CJK Ideograph Extension A-3F41 | 3f40 CJK Ideograph Extension A-3F40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f43 CJK Ideograph Extension A-3F43 | 3f42 CJK Ideograph Extension A-3F42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f45 CJK Ideograph Extension A-3F45 | 3f44 CJK Ideograph Extension A-3F44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f47 CJK Ideograph Extension A-3F47 | 3f46 CJK Ideograph Extension A-3F46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f49 CJK Ideograph Extension A-3F49 | 3f48 CJK Ideograph Extension A-3F48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f4b CJK Ideograph Extension A-3F4B | 3f4a CJK Ideograph Extension A-3F4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f4d CJK Ideograph Extension A-3F4D | 3f4c CJK Ideograph Extension A-3F4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f4f CJK Ideograph Extension A-3F4F | 3f4e CJK Ideograph Extension A-3F4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f51 CJK Ideograph Extension A-3F51 | 3f50 CJK Ideograph Extension A-3F50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f53 CJK Ideograph Extension A-3F53 | 3f52 CJK Ideograph Extension A-3F52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f55 CJK Ideograph Extension A-3F55 | 3f54 CJK Ideograph Extension A-3F54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f57 CJK Ideograph Extension A-3F57 | 3f56 CJK Ideograph Extension A-3F56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f59 CJK Ideograph Extension A-3F59 | 3f58 CJK Ideograph Extension A-3F58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f5b CJK Ideograph Extension A-3F5B | 3f5a CJK Ideograph Extension A-3F5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f5d CJK Ideograph Extension A-3F5D | 3f5c CJK Ideograph Extension A-3F5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f5f CJK Ideograph Extension A-3F5F | 3f5e CJK Ideograph Extension A-3F5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f61 CJK Ideograph Extension A-3F61 | 3f60 CJK Ideograph Extension A-3F60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f63 CJK Ideograph Extension A-3F63 | 3f62 CJK Ideograph Extension A-3F62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f65 CJK Ideograph Extension A-3F65 | 3f64 CJK Ideograph Extension A-3F64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f67 CJK Ideograph Extension A-3F67 | 3f66 CJK Ideograph Extension A-3F66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f69 CJK Ideograph Extension A-3F69 | 3f68 CJK Ideograph Extension A-3F68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f6b CJK Ideograph Extension A-3F6B | 3f6a CJK Ideograph Extension A-3F6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f6d CJK Ideograph Extension A-3F6D | 3f6c CJK Ideograph Extension A-3F6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f6f CJK Ideograph Extension A-3F6F | 3f6e CJK Ideograph Extension A-3F6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f71 CJK Ideograph Extension A-3F71 | 3f70 CJK Ideograph Extension A-3F70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f73 CJK Ideograph Extension A-3F73 | 3f72 CJK Ideograph Extension A-3F72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f75 CJK Ideograph Extension A-3F75 | 3f74 CJK Ideograph Extension A-3F74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f77 CJK Ideograph Extension A-3F77 | 3f76 CJK Ideograph Extension A-3F76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f79 CJK Ideograph Extension A-3F79 | 3f78 CJK Ideograph Extension A-3F78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f7b CJK Ideograph Extension A-3F7B | 3f7a CJK Ideograph Extension A-3F7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f7d CJK Ideograph Extension A-3F7D | 3f7c CJK Ideograph Extension A-3F7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f7f CJK Ideograph Extension A-3F7F | 3f7e CJK Ideograph Extension A-3F7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f81 CJK Ideograph Extension A-3F81 | 3f80 CJK Ideograph Extension A-3F80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f83 CJK Ideograph Extension A-3F83 | 3f82 CJK Ideograph Extension A-3F82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f85 CJK Ideograph Extension A-3F85 | 3f84 CJK Ideograph Extension A-3F84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f87 CJK Ideograph Extension A-3F87 | 3f86 CJK Ideograph Extension A-3F86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f89 CJK Ideograph Extension A-3F89 | 3f88 CJK Ideograph Extension A-3F88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f8b CJK Ideograph Extension A-3F8B | 3f8a CJK Ideograph Extension A-3F8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f8d CJK Ideograph Extension A-3F8D | 3f8c CJK Ideograph Extension A-3F8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f8f CJK Ideograph Extension A-3F8F | 3f8e CJK Ideograph Extension A-3F8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f91 CJK Ideograph Extension A-3F91 | 3f90 CJK Ideograph Extension A-3F90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f93 CJK Ideograph Extension A-3F93 | 3f92 CJK Ideograph Extension A-3F92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f95 CJK Ideograph Extension A-3F95 | 3f94 CJK Ideograph Extension A-3F94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f97 CJK Ideograph Extension A-3F97 | 3f96 CJK Ideograph Extension A-3F96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f99 CJK Ideograph Extension A-3F99 | 3f98 CJK Ideograph Extension A-3F98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f9b CJK Ideograph Extension A-3F9B | 3f9a CJK Ideograph Extension A-3F9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f9d CJK Ideograph Extension A-3F9D | 3f9c CJK Ideograph Extension A-3F9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f9f CJK Ideograph Extension A-3F9F | 3f9e CJK Ideograph Extension A-3F9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fa1 CJK Ideograph Extension A-3FA1 | 3fa0 CJK Ideograph Extension A-3FA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fa3 CJK Ideograph Extension A-3FA3 | 3fa2 CJK Ideograph Extension A-3FA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fa5 CJK Ideograph Extension A-3FA5 | 3fa4 CJK Ideograph Extension A-3FA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fa7 CJK Ideograph Extension A-3FA7 | 3fa6 CJK Ideograph Extension A-3FA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fa9 CJK Ideograph Extension A-3FA9 | 3fa8 CJK Ideograph Extension A-3FA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fab CJK Ideograph Extension A-3FAB | 3faa CJK Ideograph Extension A-3FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fad CJK Ideograph Extension A-3FAD | 3fac CJK Ideograph Extension A-3FAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3faf CJK Ideograph Extension A-3FAF | 3fae CJK Ideograph Extension A-3FAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fb1 CJK Ideograph Extension A-3FB1 | 3fb0 CJK Ideograph Extension A-3FB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fb3 CJK Ideograph Extension A-3FB3 | 3fb2 CJK Ideograph Extension A-3FB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fb5 CJK Ideograph Extension A-3FB5 | 3fb4 CJK Ideograph Extension A-3FB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fb7 CJK Ideograph Extension A-3FB7 | 3fb6 CJK Ideograph Extension A-3FB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fb9 CJK Ideograph Extension A-3FB9 | 3fb8 CJK Ideograph Extension A-3FB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fbb CJK Ideograph Extension A-3FBB | 3fba CJK Ideograph Extension A-3FBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fbd CJK Ideograph Extension A-3FBD | 3fbc CJK Ideograph Extension A-3FBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fbf CJK Ideograph Extension A-3FBF | 3fbe CJK Ideograph Extension A-3FBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fc1 CJK Ideograph Extension A-3FC1 | 3fc0 CJK Ideograph Extension A-3FC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fc3 CJK Ideograph Extension A-3FC3 | 3fc2 CJK Ideograph Extension A-3FC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fc5 CJK Ideograph Extension A-3FC5 | 3fc4 CJK Ideograph Extension A-3FC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fc7 CJK Ideograph Extension A-3FC7 | 3fc6 CJK Ideograph Extension A-3FC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fc9 CJK Ideograph Extension A-3FC9 | 3fc8 CJK Ideograph Extension A-3FC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fcb CJK Ideograph Extension A-3FCB | 3fca CJK Ideograph Extension A-3FCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fcd CJK Ideograph Extension A-3FCD | 3fcc CJK Ideograph Extension A-3FCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fcf CJK Ideograph Extension A-3FCF | 3fce CJK Ideograph Extension A-3FCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fd1 CJK Ideograph Extension A-3FD1 | 3fd0 CJK Ideograph Extension A-3FD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fd3 CJK Ideograph Extension A-3FD3 | 3fd2 CJK Ideograph Extension A-3FD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fd5 CJK Ideograph Extension A-3FD5 | 3fd4 CJK Ideograph Extension A-3FD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fd7 CJK Ideograph Extension A-3FD7 | 3fd6 CJK Ideograph Extension A-3FD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fd9 CJK Ideograph Extension A-3FD9 | 3fd8 CJK Ideograph Extension A-3FD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fdb CJK Ideograph Extension A-3FDB | 3fda CJK Ideograph Extension A-3FDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fdd CJK Ideograph Extension A-3FDD | 3fdc CJK Ideograph Extension A-3FDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fdf CJK Ideograph Extension A-3FDF | 3fde CJK Ideograph Extension A-3FDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fe1 CJK Ideograph Extension A-3FE1 | 3fe0 CJK Ideograph Extension A-3FE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fe3 CJK Ideograph Extension A-3FE3 | 3fe2 CJK Ideograph Extension A-3FE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fe5 CJK Ideograph Extension A-3FE5 | 3fe4 CJK Ideograph Extension A-3FE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fe7 CJK Ideograph Extension A-3FE7 | 3fe6 CJK Ideograph Extension A-3FE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fe9 CJK Ideograph Extension A-3FE9 | 3fe8 CJK Ideograph Extension A-3FE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3feb CJK Ideograph Extension A-3FEB | 3fea CJK Ideograph Extension A-3FEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fed CJK Ideograph Extension A-3FED | 3fec CJK Ideograph Extension A-3FEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fef CJK Ideograph Extension A-3FEF | 3fee CJK Ideograph Extension A-3FEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ff1 CJK Ideograph Extension A-3FF1 | 3ff0 CJK Ideograph Extension A-3FF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ff3 CJK Ideograph Extension A-3FF3 | 3ff2 CJK Ideograph Extension A-3FF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ff5 CJK Ideograph Extension A-3FF5 | 3ff4 CJK Ideograph Extension A-3FF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ff7 CJK Ideograph Extension A-3FF7 | 3ff6 CJK Ideograph Extension A-3FF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ff9 CJK Ideograph Extension A-3FF9 | 3ff8 CJK Ideograph Extension A-3FF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ffb CJK Ideograph Extension A-3FFB | 3ffa CJK Ideograph Extension A-3FFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ffd CJK Ideograph Extension A-3FFD | 3ffc CJK Ideograph Extension A-3FFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fff CJK Ideograph Extension A-3FFF | 3ffe CJK Ideograph Extension A-3FFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4001 CJK Ideograph Extension A-4001 | 4000 CJK Ideograph Extension A-4000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4003 CJK Ideograph Extension A-4003 | 4002 CJK Ideograph Extension A-4002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4005 CJK Ideograph Extension A-4005 | 4004 CJK Ideograph Extension A-4004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4007 CJK Ideograph Extension A-4007 | 4006 CJK Ideograph Extension A-4006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4009 CJK Ideograph Extension A-4009 | 4008 CJK Ideograph Extension A-4008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 400b CJK Ideograph Extension A-400B | 400a CJK Ideograph Extension A-400A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 400d CJK Ideograph Extension A-400D | 400c CJK Ideograph Extension A-400C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 400f CJK Ideograph Extension A-400F | 400e CJK Ideograph Extension A-400E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4011 CJK Ideograph Extension A-4011 | 4010 CJK Ideograph Extension A-4010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4013 CJK Ideograph Extension A-4013 | 4012 CJK Ideograph Extension A-4012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4015 CJK Ideograph Extension A-4015 | 4014 CJK Ideograph Extension A-4014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4017 CJK Ideograph Extension A-4017 | 4016 CJK Ideograph Extension A-4016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4019 CJK Ideograph Extension A-4019 | 4018 CJK Ideograph Extension A-4018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 401b CJK Ideograph Extension A-401B | 401a CJK Ideograph Extension A-401A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 401d CJK Ideograph Extension A-401D | 401c CJK Ideograph Extension A-401C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 401f CJK Ideograph Extension A-401F | 401e CJK Ideograph Extension A-401E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4021 CJK Ideograph Extension A-4021 | 4020 CJK Ideograph Extension A-4020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4023 CJK Ideograph Extension A-4023 | 4022 CJK Ideograph Extension A-4022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4025 CJK Ideograph Extension A-4025 | 4024 CJK Ideograph Extension A-4024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4027 CJK Ideograph Extension A-4027 | 4026 CJK Ideograph Extension A-4026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4029 CJK Ideograph Extension A-4029 | 4028 CJK Ideograph Extension A-4028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 402b CJK Ideograph Extension A-402B | 402a CJK Ideograph Extension A-402A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 402d CJK Ideograph Extension A-402D | 402c CJK Ideograph Extension A-402C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 402f CJK Ideograph Extension A-402F | 402e CJK Ideograph Extension A-402E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4031 CJK Ideograph Extension A-4031 | 4030 CJK Ideograph Extension A-4030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4033 CJK Ideograph Extension A-4033 | 4032 CJK Ideograph Extension A-4032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4035 CJK Ideograph Extension A-4035 | 4034 CJK Ideograph Extension A-4034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4037 CJK Ideograph Extension A-4037 | 4036 CJK Ideograph Extension A-4036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4039 CJK Ideograph Extension A-4039 | 4038 CJK Ideograph Extension A-4038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 403b CJK Ideograph Extension A-403B | 403a CJK Ideograph Extension A-403A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 403d CJK Ideograph Extension A-403D | 403c CJK Ideograph Extension A-403C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 403f CJK Ideograph Extension A-403F | 403e CJK Ideograph Extension A-403E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4041 CJK Ideograph Extension A-4041 | 4040 CJK Ideograph Extension A-4040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4043 CJK Ideograph Extension A-4043 | 4042 CJK Ideograph Extension A-4042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4045 CJK Ideograph Extension A-4045 | 4044 CJK Ideograph Extension A-4044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4047 CJK Ideograph Extension A-4047 | 4046 CJK Ideograph Extension A-4046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4049 CJK Ideograph Extension A-4049 | 4048 CJK Ideograph Extension A-4048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 404b CJK Ideograph Extension A-404B | 404a CJK Ideograph Extension A-404A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 404d CJK Ideograph Extension A-404D | 404c CJK Ideograph Extension A-404C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 404f CJK Ideograph Extension A-404F | 404e CJK Ideograph Extension A-404E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4051 CJK Ideograph Extension A-4051 | 4050 CJK Ideograph Extension A-4050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4053 CJK Ideograph Extension A-4053 | 4052 CJK Ideograph Extension A-4052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4055 CJK Ideograph Extension A-4055 | 4054 CJK Ideograph Extension A-4054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4057 CJK Ideograph Extension A-4057 | 4056 CJK Ideograph Extension A-4056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4059 CJK Ideograph Extension A-4059 | 4058 CJK Ideograph Extension A-4058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 405b CJK Ideograph Extension A-405B | 405a CJK Ideograph Extension A-405A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 405d CJK Ideograph Extension A-405D | 405c CJK Ideograph Extension A-405C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 405f CJK Ideograph Extension A-405F | 405e CJK Ideograph Extension A-405E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4061 CJK Ideograph Extension A-4061 | 4060 CJK Ideograph Extension A-4060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4063 CJK Ideograph Extension A-4063 | 4062 CJK Ideograph Extension A-4062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4065 CJK Ideograph Extension A-4065 | 4064 CJK Ideograph Extension A-4064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4067 CJK Ideograph Extension A-4067 | 4066 CJK Ideograph Extension A-4066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4069 CJK Ideograph Extension A-4069 | 4068 CJK Ideograph Extension A-4068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 406b CJK Ideograph Extension A-406B | 406a CJK Ideograph Extension A-406A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 406d CJK Ideograph Extension A-406D | 406c CJK Ideograph Extension A-406C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 406f CJK Ideograph Extension A-406F | 406e CJK Ideograph Extension A-406E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4071 CJK Ideograph Extension A-4071 | 4070 CJK Ideograph Extension A-4070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4073 CJK Ideograph Extension A-4073 | 4072 CJK Ideograph Extension A-4072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4075 CJK Ideograph Extension A-4075 | 4074 CJK Ideograph Extension A-4074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4077 CJK Ideograph Extension A-4077 | 4076 CJK Ideograph Extension A-4076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4079 CJK Ideograph Extension A-4079 | 4078 CJK Ideograph Extension A-4078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 407b CJK Ideograph Extension A-407B | 407a CJK Ideograph Extension A-407A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 407d CJK Ideograph Extension A-407D | 407c CJK Ideograph Extension A-407C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 407f CJK Ideograph Extension A-407F | 407e CJK Ideograph Extension A-407E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4081 CJK Ideograph Extension A-4081 | 4080 CJK Ideograph Extension A-4080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4083 CJK Ideograph Extension A-4083 | 4082 CJK Ideograph Extension A-4082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4085 CJK Ideograph Extension A-4085 | 4084 CJK Ideograph Extension A-4084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4087 CJK Ideograph Extension A-4087 | 4086 CJK Ideograph Extension A-4086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4089 CJK Ideograph Extension A-4089 | 4088 CJK Ideograph Extension A-4088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 408b CJK Ideograph Extension A-408B | 408a CJK Ideograph Extension A-408A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 408d CJK Ideograph Extension A-408D | 408c CJK Ideograph Extension A-408C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 408f CJK Ideograph Extension A-408F | 408e CJK Ideograph Extension A-408E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4091 CJK Ideograph Extension A-4091 | 4090 CJK Ideograph Extension A-4090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4093 CJK Ideograph Extension A-4093 | 4092 CJK Ideograph Extension A-4092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4095 CJK Ideograph Extension A-4095 | 4094 CJK Ideograph Extension A-4094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4097 CJK Ideograph Extension A-4097 | 4096 CJK Ideograph Extension A-4096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4099 CJK Ideograph Extension A-4099 | 4098 CJK Ideograph Extension A-4098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 409b CJK Ideograph Extension A-409B | 409a CJK Ideograph Extension A-409A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 409d CJK Ideograph Extension A-409D | 409c CJK Ideograph Extension A-409C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 409f CJK Ideograph Extension A-409F | 409e CJK Ideograph Extension A-409E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40a1 CJK Ideograph Extension A-40A1 | 40a0 CJK Ideograph Extension A-40A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40a3 CJK Ideograph Extension A-40A3 | 40a2 CJK Ideograph Extension A-40A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40a5 CJK Ideograph Extension A-40A5 | 40a4 CJK Ideograph Extension A-40A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40a7 CJK Ideograph Extension A-40A7 | 40a6 CJK Ideograph Extension A-40A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40a9 CJK Ideograph Extension A-40A9 | 40a8 CJK Ideograph Extension A-40A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40ab CJK Ideograph Extension A-40AB | 40aa CJK Ideograph Extension A-40AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40ad CJK Ideograph Extension A-40AD | 40ac CJK Ideograph Extension A-40AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40af CJK Ideograph Extension A-40AF | 40ae CJK Ideograph Extension A-40AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40b1 CJK Ideograph Extension A-40B1 | 40b0 CJK Ideograph Extension A-40B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40b3 CJK Ideograph Extension A-40B3 | 40b2 CJK Ideograph Extension A-40B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40b5 CJK Ideograph Extension A-40B5 | 40b4 CJK Ideograph Extension A-40B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40b7 CJK Ideograph Extension A-40B7 | 40b6 CJK Ideograph Extension A-40B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40b9 CJK Ideograph Extension A-40B9 | 40b8 CJK Ideograph Extension A-40B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40bb CJK Ideograph Extension A-40BB | 40ba CJK Ideograph Extension A-40BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40bd CJK Ideograph Extension A-40BD | 40bc CJK Ideograph Extension A-40BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40bf CJK Ideograph Extension A-40BF | 40be CJK Ideograph Extension A-40BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40c1 CJK Ideograph Extension A-40C1 | 40c0 CJK Ideograph Extension A-40C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40c3 CJK Ideograph Extension A-40C3 | 40c2 CJK Ideograph Extension A-40C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40c5 CJK Ideograph Extension A-40C5 | 40c4 CJK Ideograph Extension A-40C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40c7 CJK Ideograph Extension A-40C7 | 40c6 CJK Ideograph Extension A-40C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40c9 CJK Ideograph Extension A-40C9 | 40c8 CJK Ideograph Extension A-40C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40cb CJK Ideograph Extension A-40CB | 40ca CJK Ideograph Extension A-40CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40cd CJK Ideograph Extension A-40CD | 40cc CJK Ideograph Extension A-40CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40cf CJK Ideograph Extension A-40CF | 40ce CJK Ideograph Extension A-40CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40d1 CJK Ideograph Extension A-40D1 | 40d0 CJK Ideograph Extension A-40D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40d3 CJK Ideograph Extension A-40D3 | 40d2 CJK Ideograph Extension A-40D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40d5 CJK Ideograph Extension A-40D5 | 40d4 CJK Ideograph Extension A-40D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40d7 CJK Ideograph Extension A-40D7 | 40d6 CJK Ideograph Extension A-40D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40d9 CJK Ideograph Extension A-40D9 | 40d8 CJK Ideograph Extension A-40D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40db CJK Ideograph Extension A-40DB | 40da CJK Ideograph Extension A-40DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40dd CJK Ideograph Extension A-40DD | 40dc CJK Ideograph Extension A-40DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40df CJK Ideograph Extension A-40DF | 40de CJK Ideograph Extension A-40DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40e1 CJK Ideograph Extension A-40E1 | 40e0 CJK Ideograph Extension A-40E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40e3 CJK Ideograph Extension A-40E3 | 40e2 CJK Ideograph Extension A-40E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40e5 CJK Ideograph Extension A-40E5 | 40e4 CJK Ideograph Extension A-40E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40e7 CJK Ideograph Extension A-40E7 | 40e6 CJK Ideograph Extension A-40E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40e9 CJK Ideograph Extension A-40E9 | 40e8 CJK Ideograph Extension A-40E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40eb CJK Ideograph Extension A-40EB | 40ea CJK Ideograph Extension A-40EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40ed CJK Ideograph Extension A-40ED | 40ec CJK Ideograph Extension A-40EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40ef CJK Ideograph Extension A-40EF | 40ee CJK Ideograph Extension A-40EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40f1 CJK Ideograph Extension A-40F1 | 40f0 CJK Ideograph Extension A-40F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40f3 CJK Ideograph Extension A-40F3 | 40f2 CJK Ideograph Extension A-40F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40f5 CJK Ideograph Extension A-40F5 | 40f4 CJK Ideograph Extension A-40F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40f7 CJK Ideograph Extension A-40F7 | 40f6 CJK Ideograph Extension A-40F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40f9 CJK Ideograph Extension A-40F9 | 40f8 CJK Ideograph Extension A-40F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40fb CJK Ideograph Extension A-40FB | 40fa CJK Ideograph Extension A-40FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40fd CJK Ideograph Extension A-40FD | 40fc CJK Ideograph Extension A-40FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40ff CJK Ideograph Extension A-40FF | 40fe CJK Ideograph Extension A-40FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4101 CJK Ideograph Extension A-4101 | 4100 CJK Ideograph Extension A-4100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4103 CJK Ideograph Extension A-4103 | 4102 CJK Ideograph Extension A-4102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4105 CJK Ideograph Extension A-4105 | 4104 CJK Ideograph Extension A-4104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4107 CJK Ideograph Extension A-4107 | 4106 CJK Ideograph Extension A-4106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4109 CJK Ideograph Extension A-4109 | 4108 CJK Ideograph Extension A-4108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 410b CJK Ideograph Extension A-410B | 410a CJK Ideograph Extension A-410A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 410d CJK Ideograph Extension A-410D | 410c CJK Ideograph Extension A-410C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 410f CJK Ideograph Extension A-410F | 410e CJK Ideograph Extension A-410E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4111 CJK Ideograph Extension A-4111 | 4110 CJK Ideograph Extension A-4110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4113 CJK Ideograph Extension A-4113 | 4112 CJK Ideograph Extension A-4112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4115 CJK Ideograph Extension A-4115 | 4114 CJK Ideograph Extension A-4114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4117 CJK Ideograph Extension A-4117 | 4116 CJK Ideograph Extension A-4116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4119 CJK Ideograph Extension A-4119 | 4118 CJK Ideograph Extension A-4118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 411b CJK Ideograph Extension A-411B | 411a CJK Ideograph Extension A-411A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 411d CJK Ideograph Extension A-411D | 411c CJK Ideograph Extension A-411C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 411f CJK Ideograph Extension A-411F | 411e CJK Ideograph Extension A-411E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4121 CJK Ideograph Extension A-4121 | 4120 CJK Ideograph Extension A-4120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4123 CJK Ideograph Extension A-4123 | 4122 CJK Ideograph Extension A-4122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4125 CJK Ideograph Extension A-4125 | 4124 CJK Ideograph Extension A-4124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4127 CJK Ideograph Extension A-4127 | 4126 CJK Ideograph Extension A-4126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4129 CJK Ideograph Extension A-4129 | 4128 CJK Ideograph Extension A-4128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 412b CJK Ideograph Extension A-412B | 412a CJK Ideograph Extension A-412A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 412d CJK Ideograph Extension A-412D | 412c CJK Ideograph Extension A-412C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 412f CJK Ideograph Extension A-412F | 412e CJK Ideograph Extension A-412E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4131 CJK Ideograph Extension A-4131 | 4130 CJK Ideograph Extension A-4130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4133 CJK Ideograph Extension A-4133 | 4132 CJK Ideograph Extension A-4132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4135 CJK Ideograph Extension A-4135 | 4134 CJK Ideograph Extension A-4134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4137 CJK Ideograph Extension A-4137 | 4136 CJK Ideograph Extension A-4136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4139 CJK Ideograph Extension A-4139 | 4138 CJK Ideograph Extension A-4138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 413b CJK Ideograph Extension A-413B | 413a CJK Ideograph Extension A-413A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 413d CJK Ideograph Extension A-413D | 413c CJK Ideograph Extension A-413C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 413f CJK Ideograph Extension A-413F | 413e CJK Ideograph Extension A-413E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4141 CJK Ideograph Extension A-4141 | 4140 CJK Ideograph Extension A-4140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4143 CJK Ideograph Extension A-4143 | 4142 CJK Ideograph Extension A-4142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4145 CJK Ideograph Extension A-4145 | 4144 CJK Ideograph Extension A-4144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4147 CJK Ideograph Extension A-4147 | 4146 CJK Ideograph Extension A-4146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4149 CJK Ideograph Extension A-4149 | 4148 CJK Ideograph Extension A-4148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 414b CJK Ideograph Extension A-414B | 414a CJK Ideograph Extension A-414A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 414d CJK Ideograph Extension A-414D | 414c CJK Ideograph Extension A-414C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 414f CJK Ideograph Extension A-414F | 414e CJK Ideograph Extension A-414E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4151 CJK Ideograph Extension A-4151 | 4150 CJK Ideograph Extension A-4150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4153 CJK Ideograph Extension A-4153 | 4152 CJK Ideograph Extension A-4152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4155 CJK Ideograph Extension A-4155 | 4154 CJK Ideograph Extension A-4154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4157 CJK Ideograph Extension A-4157 | 4156 CJK Ideograph Extension A-4156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4159 CJK Ideograph Extension A-4159 | 4158 CJK Ideograph Extension A-4158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 415b CJK Ideograph Extension A-415B | 415a CJK Ideograph Extension A-415A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 415d CJK Ideograph Extension A-415D | 415c CJK Ideograph Extension A-415C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 415f CJK Ideograph Extension A-415F | 415e CJK Ideograph Extension A-415E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4161 CJK Ideograph Extension A-4161 | 4160 CJK Ideograph Extension A-4160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4163 CJK Ideograph Extension A-4163 | 4162 CJK Ideograph Extension A-4162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4165 CJK Ideograph Extension A-4165 | 4164 CJK Ideograph Extension A-4164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4167 CJK Ideograph Extension A-4167 | 4166 CJK Ideograph Extension A-4166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4169 CJK Ideograph Extension A-4169 | 4168 CJK Ideograph Extension A-4168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 416b CJK Ideograph Extension A-416B | 416a CJK Ideograph Extension A-416A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 416d CJK Ideograph Extension A-416D | 416c CJK Ideograph Extension A-416C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 416f CJK Ideograph Extension A-416F | 416e CJK Ideograph Extension A-416E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4171 CJK Ideograph Extension A-4171 | 4170 CJK Ideograph Extension A-4170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4173 CJK Ideograph Extension A-4173 | 4172 CJK Ideograph Extension A-4172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4175 CJK Ideograph Extension A-4175 | 4174 CJK Ideograph Extension A-4174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4177 CJK Ideograph Extension A-4177 | 4176 CJK Ideograph Extension A-4176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4179 CJK Ideograph Extension A-4179 | 4178 CJK Ideograph Extension A-4178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 417b CJK Ideograph Extension A-417B | 417a CJK Ideograph Extension A-417A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 417d CJK Ideograph Extension A-417D | 417c CJK Ideograph Extension A-417C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 417f CJK Ideograph Extension A-417F | 417e CJK Ideograph Extension A-417E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4181 CJK Ideograph Extension A-4181 | 4180 CJK Ideograph Extension A-4180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4183 CJK Ideograph Extension A-4183 | 4182 CJK Ideograph Extension A-4182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4185 CJK Ideograph Extension A-4185 | 4184 CJK Ideograph Extension A-4184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4187 CJK Ideograph Extension A-4187 | 4186 CJK Ideograph Extension A-4186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4189 CJK Ideograph Extension A-4189 | 4188 CJK Ideograph Extension A-4188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 418b CJK Ideograph Extension A-418B | 418a CJK Ideograph Extension A-418A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 418d CJK Ideograph Extension A-418D | 418c CJK Ideograph Extension A-418C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 418f CJK Ideograph Extension A-418F | 418e CJK Ideograph Extension A-418E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4191 CJK Ideograph Extension A-4191 | 4190 CJK Ideograph Extension A-4190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4193 CJK Ideograph Extension A-4193 | 4192 CJK Ideograph Extension A-4192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4195 CJK Ideograph Extension A-4195 | 4194 CJK Ideograph Extension A-4194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4197 CJK Ideograph Extension A-4197 | 4196 CJK Ideograph Extension A-4196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4199 CJK Ideograph Extension A-4199 | 4198 CJK Ideograph Extension A-4198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 419b CJK Ideograph Extension A-419B | 419a CJK Ideograph Extension A-419A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 419d CJK Ideograph Extension A-419D | 419c CJK Ideograph Extension A-419C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 419f CJK Ideograph Extension A-419F | 419e CJK Ideograph Extension A-419E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41a1 CJK Ideograph Extension A-41A1 | 41a0 CJK Ideograph Extension A-41A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41a3 CJK Ideograph Extension A-41A3 | 41a2 CJK Ideograph Extension A-41A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41a5 CJK Ideograph Extension A-41A5 | 41a4 CJK Ideograph Extension A-41A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41a7 CJK Ideograph Extension A-41A7 | 41a6 CJK Ideograph Extension A-41A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41a9 CJK Ideograph Extension A-41A9 | 41a8 CJK Ideograph Extension A-41A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41ab CJK Ideograph Extension A-41AB | 41aa CJK Ideograph Extension A-41AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41ad CJK Ideograph Extension A-41AD | 41ac CJK Ideograph Extension A-41AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41af CJK Ideograph Extension A-41AF | 41ae CJK Ideograph Extension A-41AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41b1 CJK Ideograph Extension A-41B1 | 41b0 CJK Ideograph Extension A-41B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41b3 CJK Ideograph Extension A-41B3 | 41b2 CJK Ideograph Extension A-41B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41b5 CJK Ideograph Extension A-41B5 | 41b4 CJK Ideograph Extension A-41B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41b7 CJK Ideograph Extension A-41B7 | 41b6 CJK Ideograph Extension A-41B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41b9 CJK Ideograph Extension A-41B9 | 41b8 CJK Ideograph Extension A-41B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41bb CJK Ideograph Extension A-41BB | 41ba CJK Ideograph Extension A-41BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41bd CJK Ideograph Extension A-41BD | 41bc CJK Ideograph Extension A-41BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41bf CJK Ideograph Extension A-41BF | 41be CJK Ideograph Extension A-41BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41c1 CJK Ideograph Extension A-41C1 | 41c0 CJK Ideograph Extension A-41C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41c3 CJK Ideograph Extension A-41C3 | 41c2 CJK Ideograph Extension A-41C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41c5 CJK Ideograph Extension A-41C5 | 41c4 CJK Ideograph Extension A-41C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41c7 CJK Ideograph Extension A-41C7 | 41c6 CJK Ideograph Extension A-41C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41c9 CJK Ideograph Extension A-41C9 | 41c8 CJK Ideograph Extension A-41C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41cb CJK Ideograph Extension A-41CB | 41ca CJK Ideograph Extension A-41CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41cd CJK Ideograph Extension A-41CD | 41cc CJK Ideograph Extension A-41CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41cf CJK Ideograph Extension A-41CF | 41ce CJK Ideograph Extension A-41CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41d1 CJK Ideograph Extension A-41D1 | 41d0 CJK Ideograph Extension A-41D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41d3 CJK Ideograph Extension A-41D3 | 41d2 CJK Ideograph Extension A-41D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41d5 CJK Ideograph Extension A-41D5 | 41d4 CJK Ideograph Extension A-41D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41d7 CJK Ideograph Extension A-41D7 | 41d6 CJK Ideograph Extension A-41D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41d9 CJK Ideograph Extension A-41D9 | 41d8 CJK Ideograph Extension A-41D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41db CJK Ideograph Extension A-41DB | 41da CJK Ideograph Extension A-41DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41dd CJK Ideograph Extension A-41DD | 41dc CJK Ideograph Extension A-41DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41df CJK Ideograph Extension A-41DF | 41de CJK Ideograph Extension A-41DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41e1 CJK Ideograph Extension A-41E1 | 41e0 CJK Ideograph Extension A-41E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41e3 CJK Ideograph Extension A-41E3 | 41e2 CJK Ideograph Extension A-41E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41e5 CJK Ideograph Extension A-41E5 | 41e4 CJK Ideograph Extension A-41E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41e7 CJK Ideograph Extension A-41E7 | 41e6 CJK Ideograph Extension A-41E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41e9 CJK Ideograph Extension A-41E9 | 41e8 CJK Ideograph Extension A-41E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41eb CJK Ideograph Extension A-41EB | 41ea CJK Ideograph Extension A-41EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41ed CJK Ideograph Extension A-41ED | 41ec CJK Ideograph Extension A-41EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41ef CJK Ideograph Extension A-41EF | 41ee CJK Ideograph Extension A-41EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41f1 CJK Ideograph Extension A-41F1 | 41f0 CJK Ideograph Extension A-41F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41f3 CJK Ideograph Extension A-41F3 | 41f2 CJK Ideograph Extension A-41F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41f5 CJK Ideograph Extension A-41F5 | 41f4 CJK Ideograph Extension A-41F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41f7 CJK Ideograph Extension A-41F7 | 41f6 CJK Ideograph Extension A-41F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41f9 CJK Ideograph Extension A-41F9 | 41f8 CJK Ideograph Extension A-41F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41fb CJK Ideograph Extension A-41FB | 41fa CJK Ideograph Extension A-41FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41fd CJK Ideograph Extension A-41FD | 41fc CJK Ideograph Extension A-41FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41ff CJK Ideograph Extension A-41FF | 41fe CJK Ideograph Extension A-41FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4201 CJK Ideograph Extension A-4201 | 4200 CJK Ideograph Extension A-4200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4203 CJK Ideograph Extension A-4203 | 4202 CJK Ideograph Extension A-4202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4205 CJK Ideograph Extension A-4205 | 4204 CJK Ideograph Extension A-4204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4207 CJK Ideograph Extension A-4207 | 4206 CJK Ideograph Extension A-4206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4209 CJK Ideograph Extension A-4209 | 4208 CJK Ideograph Extension A-4208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 420b CJK Ideograph Extension A-420B | 420a CJK Ideograph Extension A-420A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 420d CJK Ideograph Extension A-420D | 420c CJK Ideograph Extension A-420C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 420f CJK Ideograph Extension A-420F | 420e CJK Ideograph Extension A-420E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4211 CJK Ideograph Extension A-4211 | 4210 CJK Ideograph Extension A-4210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4213 CJK Ideograph Extension A-4213 | 4212 CJK Ideograph Extension A-4212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4215 CJK Ideograph Extension A-4215 | 4214 CJK Ideograph Extension A-4214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4217 CJK Ideograph Extension A-4217 | 4216 CJK Ideograph Extension A-4216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4219 CJK Ideograph Extension A-4219 | 4218 CJK Ideograph Extension A-4218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 421b CJK Ideograph Extension A-421B | 421a CJK Ideograph Extension A-421A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 421d CJK Ideograph Extension A-421D | 421c CJK Ideograph Extension A-421C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 421f CJK Ideograph Extension A-421F | 421e CJK Ideograph Extension A-421E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4221 CJK Ideograph Extension A-4221 | 4220 CJK Ideograph Extension A-4220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4223 CJK Ideograph Extension A-4223 | 4222 CJK Ideograph Extension A-4222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4225 CJK Ideograph Extension A-4225 | 4224 CJK Ideograph Extension A-4224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4227 CJK Ideograph Extension A-4227 | 4226 CJK Ideograph Extension A-4226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4229 CJK Ideograph Extension A-4229 | 4228 CJK Ideograph Extension A-4228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 422b CJK Ideograph Extension A-422B | 422a CJK Ideograph Extension A-422A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 422d CJK Ideograph Extension A-422D | 422c CJK Ideograph Extension A-422C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 422f CJK Ideograph Extension A-422F | 422e CJK Ideograph Extension A-422E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4231 CJK Ideograph Extension A-4231 | 4230 CJK Ideograph Extension A-4230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4233 CJK Ideograph Extension A-4233 | 4232 CJK Ideograph Extension A-4232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4235 CJK Ideograph Extension A-4235 | 4234 CJK Ideograph Extension A-4234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4237 CJK Ideograph Extension A-4237 | 4236 CJK Ideograph Extension A-4236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4239 CJK Ideograph Extension A-4239 | 4238 CJK Ideograph Extension A-4238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 423b CJK Ideograph Extension A-423B | 423a CJK Ideograph Extension A-423A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 423d CJK Ideograph Extension A-423D | 423c CJK Ideograph Extension A-423C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 423f CJK Ideograph Extension A-423F | 423e CJK Ideograph Extension A-423E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4241 CJK Ideograph Extension A-4241 | 4240 CJK Ideograph Extension A-4240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4243 CJK Ideograph Extension A-4243 | 4242 CJK Ideograph Extension A-4242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4245 CJK Ideograph Extension A-4245 | 4244 CJK Ideograph Extension A-4244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4247 CJK Ideograph Extension A-4247 | 4246 CJK Ideograph Extension A-4246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4249 CJK Ideograph Extension A-4249 | 4248 CJK Ideograph Extension A-4248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 424b CJK Ideograph Extension A-424B | 424a CJK Ideograph Extension A-424A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 424d CJK Ideograph Extension A-424D | 424c CJK Ideograph Extension A-424C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 424f CJK Ideograph Extension A-424F | 424e CJK Ideograph Extension A-424E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4251 CJK Ideograph Extension A-4251 | 4250 CJK Ideograph Extension A-4250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4253 CJK Ideograph Extension A-4253 | 4252 CJK Ideograph Extension A-4252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4255 CJK Ideograph Extension A-4255 | 4254 CJK Ideograph Extension A-4254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4257 CJK Ideograph Extension A-4257 | 4256 CJK Ideograph Extension A-4256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4259 CJK Ideograph Extension A-4259 | 4258 CJK Ideograph Extension A-4258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 425b CJK Ideograph Extension A-425B | 425a CJK Ideograph Extension A-425A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 425d CJK Ideograph Extension A-425D | 425c CJK Ideograph Extension A-425C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 425f CJK Ideograph Extension A-425F | 425e CJK Ideograph Extension A-425E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4261 CJK Ideograph Extension A-4261 | 4260 CJK Ideograph Extension A-4260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4263 CJK Ideograph Extension A-4263 | 4262 CJK Ideograph Extension A-4262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4265 CJK Ideograph Extension A-4265 | 4264 CJK Ideograph Extension A-4264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4267 CJK Ideograph Extension A-4267 | 4266 CJK Ideograph Extension A-4266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4269 CJK Ideograph Extension A-4269 | 4268 CJK Ideograph Extension A-4268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 426b CJK Ideograph Extension A-426B | 426a CJK Ideograph Extension A-426A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 426d CJK Ideograph Extension A-426D | 426c CJK Ideograph Extension A-426C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 426f CJK Ideograph Extension A-426F | 426e CJK Ideograph Extension A-426E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4271 CJK Ideograph Extension A-4271 | 4270 CJK Ideograph Extension A-4270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4273 CJK Ideograph Extension A-4273 | 4272 CJK Ideograph Extension A-4272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4275 CJK Ideograph Extension A-4275 | 4274 CJK Ideograph Extension A-4274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4277 CJK Ideograph Extension A-4277 | 4276 CJK Ideograph Extension A-4276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4279 CJK Ideograph Extension A-4279 | 4278 CJK Ideograph Extension A-4278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 427b CJK Ideograph Extension A-427B | 427a CJK Ideograph Extension A-427A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 427d CJK Ideograph Extension A-427D | 427c CJK Ideograph Extension A-427C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 427f CJK Ideograph Extension A-427F | 427e CJK Ideograph Extension A-427E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4281 CJK Ideograph Extension A-4281 | 4280 CJK Ideograph Extension A-4280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4283 CJK Ideograph Extension A-4283 | 4282 CJK Ideograph Extension A-4282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4285 CJK Ideograph Extension A-4285 | 4284 CJK Ideograph Extension A-4284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4287 CJK Ideograph Extension A-4287 | 4286 CJK Ideograph Extension A-4286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4289 CJK Ideograph Extension A-4289 | 4288 CJK Ideograph Extension A-4288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 428b CJK Ideograph Extension A-428B | 428a CJK Ideograph Extension A-428A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 428d CJK Ideograph Extension A-428D | 428c CJK Ideograph Extension A-428C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 428f CJK Ideograph Extension A-428F | 428e CJK Ideograph Extension A-428E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4291 CJK Ideograph Extension A-4291 | 4290 CJK Ideograph Extension A-4290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4293 CJK Ideograph Extension A-4293 | 4292 CJK Ideograph Extension A-4292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4295 CJK Ideograph Extension A-4295 | 4294 CJK Ideograph Extension A-4294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4297 CJK Ideograph Extension A-4297 | 4296 CJK Ideograph Extension A-4296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4299 CJK Ideograph Extension A-4299 | 4298 CJK Ideograph Extension A-4298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 429b CJK Ideograph Extension A-429B | 429a CJK Ideograph Extension A-429A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 429d CJK Ideograph Extension A-429D | 429c CJK Ideograph Extension A-429C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 429f CJK Ideograph Extension A-429F | 429e CJK Ideograph Extension A-429E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42a1 CJK Ideograph Extension A-42A1 | 42a0 CJK Ideograph Extension A-42A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42a3 CJK Ideograph Extension A-42A3 | 42a2 CJK Ideograph Extension A-42A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42a5 CJK Ideograph Extension A-42A5 | 42a4 CJK Ideograph Extension A-42A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42a7 CJK Ideograph Extension A-42A7 | 42a6 CJK Ideograph Extension A-42A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42a9 CJK Ideograph Extension A-42A9 | 42a8 CJK Ideograph Extension A-42A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42ab CJK Ideograph Extension A-42AB | 42aa CJK Ideograph Extension A-42AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42ad CJK Ideograph Extension A-42AD | 42ac CJK Ideograph Extension A-42AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42af CJK Ideograph Extension A-42AF | 42ae CJK Ideograph Extension A-42AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42b1 CJK Ideograph Extension A-42B1 | 42b0 CJK Ideograph Extension A-42B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42b3 CJK Ideograph Extension A-42B3 | 42b2 CJK Ideograph Extension A-42B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42b5 CJK Ideograph Extension A-42B5 | 42b4 CJK Ideograph Extension A-42B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42b7 CJK Ideograph Extension A-42B7 | 42b6 CJK Ideograph Extension A-42B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42b9 CJK Ideograph Extension A-42B9 | 42b8 CJK Ideograph Extension A-42B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42bb CJK Ideograph Extension A-42BB | 42ba CJK Ideograph Extension A-42BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42bd CJK Ideograph Extension A-42BD | 42bc CJK Ideograph Extension A-42BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42bf CJK Ideograph Extension A-42BF | 42be CJK Ideograph Extension A-42BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42c1 CJK Ideograph Extension A-42C1 | 42c0 CJK Ideograph Extension A-42C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42c3 CJK Ideograph Extension A-42C3 | 42c2 CJK Ideograph Extension A-42C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42c5 CJK Ideograph Extension A-42C5 | 42c4 CJK Ideograph Extension A-42C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42c7 CJK Ideograph Extension A-42C7 | 42c6 CJK Ideograph Extension A-42C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42c9 CJK Ideograph Extension A-42C9 | 42c8 CJK Ideograph Extension A-42C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42cb CJK Ideograph Extension A-42CB | 42ca CJK Ideograph Extension A-42CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42cd CJK Ideograph Extension A-42CD | 42cc CJK Ideograph Extension A-42CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42cf CJK Ideograph Extension A-42CF | 42ce CJK Ideograph Extension A-42CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42d1 CJK Ideograph Extension A-42D1 | 42d0 CJK Ideograph Extension A-42D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42d3 CJK Ideograph Extension A-42D3 | 42d2 CJK Ideograph Extension A-42D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42d5 CJK Ideograph Extension A-42D5 | 42d4 CJK Ideograph Extension A-42D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42d7 CJK Ideograph Extension A-42D7 | 42d6 CJK Ideograph Extension A-42D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42d9 CJK Ideograph Extension A-42D9 | 42d8 CJK Ideograph Extension A-42D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42db CJK Ideograph Extension A-42DB | 42da CJK Ideograph Extension A-42DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42dd CJK Ideograph Extension A-42DD | 42dc CJK Ideograph Extension A-42DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42df CJK Ideograph Extension A-42DF | 42de CJK Ideograph Extension A-42DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42e1 CJK Ideograph Extension A-42E1 | 42e0 CJK Ideograph Extension A-42E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42e3 CJK Ideograph Extension A-42E3 | 42e2 CJK Ideograph Extension A-42E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42e5 CJK Ideograph Extension A-42E5 | 42e4 CJK Ideograph Extension A-42E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42e7 CJK Ideograph Extension A-42E7 | 42e6 CJK Ideograph Extension A-42E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42e9 CJK Ideograph Extension A-42E9 | 42e8 CJK Ideograph Extension A-42E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42eb CJK Ideograph Extension A-42EB | 42ea CJK Ideograph Extension A-42EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42ed CJK Ideograph Extension A-42ED | 42ec CJK Ideograph Extension A-42EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42ef CJK Ideograph Extension A-42EF | 42ee CJK Ideograph Extension A-42EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42f1 CJK Ideograph Extension A-42F1 | 42f0 CJK Ideograph Extension A-42F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42f3 CJK Ideograph Extension A-42F3 | 42f2 CJK Ideograph Extension A-42F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42f5 CJK Ideograph Extension A-42F5 | 42f4 CJK Ideograph Extension A-42F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42f7 CJK Ideograph Extension A-42F7 | 42f6 CJK Ideograph Extension A-42F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42f9 CJK Ideograph Extension A-42F9 | 42f8 CJK Ideograph Extension A-42F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42fb CJK Ideograph Extension A-42FB | 42fa CJK Ideograph Extension A-42FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42fd CJK Ideograph Extension A-42FD | 42fc CJK Ideograph Extension A-42FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42ff CJK Ideograph Extension A-42FF | 42fe CJK Ideograph Extension A-42FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4301 CJK Ideograph Extension A-4301 | 4300 CJK Ideograph Extension A-4300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4303 CJK Ideograph Extension A-4303 | 4302 CJK Ideograph Extension A-4302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4305 CJK Ideograph Extension A-4305 | 4304 CJK Ideograph Extension A-4304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4307 CJK Ideograph Extension A-4307 | 4306 CJK Ideograph Extension A-4306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4309 CJK Ideograph Extension A-4309 | 4308 CJK Ideograph Extension A-4308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 430b CJK Ideograph Extension A-430B | 430a CJK Ideograph Extension A-430A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 430d CJK Ideograph Extension A-430D | 430c CJK Ideograph Extension A-430C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 430f CJK Ideograph Extension A-430F | 430e CJK Ideograph Extension A-430E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4311 CJK Ideograph Extension A-4311 | 4310 CJK Ideograph Extension A-4310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4313 CJK Ideograph Extension A-4313 | 4312 CJK Ideograph Extension A-4312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4315 CJK Ideograph Extension A-4315 | 4314 CJK Ideograph Extension A-4314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4317 CJK Ideograph Extension A-4317 | 4316 CJK Ideograph Extension A-4316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4319 CJK Ideograph Extension A-4319 | 4318 CJK Ideograph Extension A-4318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 431b CJK Ideograph Extension A-431B | 431a CJK Ideograph Extension A-431A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 431d CJK Ideograph Extension A-431D | 431c CJK Ideograph Extension A-431C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 431f CJK Ideograph Extension A-431F | 431e CJK Ideograph Extension A-431E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4321 CJK Ideograph Extension A-4321 | 4320 CJK Ideograph Extension A-4320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4323 CJK Ideograph Extension A-4323 | 4322 CJK Ideograph Extension A-4322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4325 CJK Ideograph Extension A-4325 | 4324 CJK Ideograph Extension A-4324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4327 CJK Ideograph Extension A-4327 | 4326 CJK Ideograph Extension A-4326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4329 CJK Ideograph Extension A-4329 | 4328 CJK Ideograph Extension A-4328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 432b CJK Ideograph Extension A-432B | 432a CJK Ideograph Extension A-432A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 432d CJK Ideograph Extension A-432D | 432c CJK Ideograph Extension A-432C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 432f CJK Ideograph Extension A-432F | 432e CJK Ideograph Extension A-432E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4331 CJK Ideograph Extension A-4331 | 4330 CJK Ideograph Extension A-4330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4333 CJK Ideograph Extension A-4333 | 4332 CJK Ideograph Extension A-4332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4335 CJK Ideograph Extension A-4335 | 4334 CJK Ideograph Extension A-4334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4337 CJK Ideograph Extension A-4337 | 4336 CJK Ideograph Extension A-4336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4339 CJK Ideograph Extension A-4339 | 4338 CJK Ideograph Extension A-4338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 433b CJK Ideograph Extension A-433B | 433a CJK Ideograph Extension A-433A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 433d CJK Ideograph Extension A-433D | 433c CJK Ideograph Extension A-433C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 433f CJK Ideograph Extension A-433F | 433e CJK Ideograph Extension A-433E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4341 CJK Ideograph Extension A-4341 | 4340 CJK Ideograph Extension A-4340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4343 CJK Ideograph Extension A-4343 | 4342 CJK Ideograph Extension A-4342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4345 CJK Ideograph Extension A-4345 | 4344 CJK Ideograph Extension A-4344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4347 CJK Ideograph Extension A-4347 | 4346 CJK Ideograph Extension A-4346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4349 CJK Ideograph Extension A-4349 | 4348 CJK Ideograph Extension A-4348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 434b CJK Ideograph Extension A-434B | 434a CJK Ideograph Extension A-434A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 434d CJK Ideograph Extension A-434D | 434c CJK Ideograph Extension A-434C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 434f CJK Ideograph Extension A-434F | 434e CJK Ideograph Extension A-434E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4351 CJK Ideograph Extension A-4351 | 4350 CJK Ideograph Extension A-4350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4353 CJK Ideograph Extension A-4353 | 4352 CJK Ideograph Extension A-4352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4355 CJK Ideograph Extension A-4355 | 4354 CJK Ideograph Extension A-4354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4357 CJK Ideograph Extension A-4357 | 4356 CJK Ideograph Extension A-4356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4359 CJK Ideograph Extension A-4359 | 4358 CJK Ideograph Extension A-4358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 435b CJK Ideograph Extension A-435B | 435a CJK Ideograph Extension A-435A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 435d CJK Ideograph Extension A-435D | 435c CJK Ideograph Extension A-435C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 435f CJK Ideograph Extension A-435F | 435e CJK Ideograph Extension A-435E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4361 CJK Ideograph Extension A-4361 | 4360 CJK Ideograph Extension A-4360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4363 CJK Ideograph Extension A-4363 | 4362 CJK Ideograph Extension A-4362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4365 CJK Ideograph Extension A-4365 | 4364 CJK Ideograph Extension A-4364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4367 CJK Ideograph Extension A-4367 | 4366 CJK Ideograph Extension A-4366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4369 CJK Ideograph Extension A-4369 | 4368 CJK Ideograph Extension A-4368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 436b CJK Ideograph Extension A-436B | 436a CJK Ideograph Extension A-436A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 436d CJK Ideograph Extension A-436D | 436c CJK Ideograph Extension A-436C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 436f CJK Ideograph Extension A-436F | 436e CJK Ideograph Extension A-436E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4371 CJK Ideograph Extension A-4371 | 4370 CJK Ideograph Extension A-4370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4373 CJK Ideograph Extension A-4373 | 4372 CJK Ideograph Extension A-4372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4375 CJK Ideograph Extension A-4375 | 4374 CJK Ideograph Extension A-4374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4377 CJK Ideograph Extension A-4377 | 4376 CJK Ideograph Extension A-4376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4379 CJK Ideograph Extension A-4379 | 4378 CJK Ideograph Extension A-4378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 437b CJK Ideograph Extension A-437B | 437a CJK Ideograph Extension A-437A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 437d CJK Ideograph Extension A-437D | 437c CJK Ideograph Extension A-437C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 437f CJK Ideograph Extension A-437F | 437e CJK Ideograph Extension A-437E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4381 CJK Ideograph Extension A-4381 | 4380 CJK Ideograph Extension A-4380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4383 CJK Ideograph Extension A-4383 | 4382 CJK Ideograph Extension A-4382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4385 CJK Ideograph Extension A-4385 | 4384 CJK Ideograph Extension A-4384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4387 CJK Ideograph Extension A-4387 | 4386 CJK Ideograph Extension A-4386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4389 CJK Ideograph Extension A-4389 | 4388 CJK Ideograph Extension A-4388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 438b CJK Ideograph Extension A-438B | 438a CJK Ideograph Extension A-438A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 438d CJK Ideograph Extension A-438D | 438c CJK Ideograph Extension A-438C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 438f CJK Ideograph Extension A-438F | 438e CJK Ideograph Extension A-438E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4391 CJK Ideograph Extension A-4391 | 4390 CJK Ideograph Extension A-4390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4393 CJK Ideograph Extension A-4393 | 4392 CJK Ideograph Extension A-4392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4395 CJK Ideograph Extension A-4395 | 4394 CJK Ideograph Extension A-4394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4397 CJK Ideograph Extension A-4397 | 4396 CJK Ideograph Extension A-4396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4399 CJK Ideograph Extension A-4399 | 4398 CJK Ideograph Extension A-4398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 439b CJK Ideograph Extension A-439B | 439a CJK Ideograph Extension A-439A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 439d CJK Ideograph Extension A-439D | 439c CJK Ideograph Extension A-439C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 439f CJK Ideograph Extension A-439F | 439e CJK Ideograph Extension A-439E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43a1 CJK Ideograph Extension A-43A1 | 43a0 CJK Ideograph Extension A-43A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43a3 CJK Ideograph Extension A-43A3 | 43a2 CJK Ideograph Extension A-43A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43a5 CJK Ideograph Extension A-43A5 | 43a4 CJK Ideograph Extension A-43A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43a7 CJK Ideograph Extension A-43A7 | 43a6 CJK Ideograph Extension A-43A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43a9 CJK Ideograph Extension A-43A9 | 43a8 CJK Ideograph Extension A-43A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43ab CJK Ideograph Extension A-43AB | 43aa CJK Ideograph Extension A-43AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43ad CJK Ideograph Extension A-43AD | 43ac CJK Ideograph Extension A-43AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43af CJK Ideograph Extension A-43AF | 43ae CJK Ideograph Extension A-43AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43b1 CJK Ideograph Extension A-43B1 | 43b0 CJK Ideograph Extension A-43B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43b3 CJK Ideograph Extension A-43B3 | 43b2 CJK Ideograph Extension A-43B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43b5 CJK Ideograph Extension A-43B5 | 43b4 CJK Ideograph Extension A-43B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43b7 CJK Ideograph Extension A-43B7 | 43b6 CJK Ideograph Extension A-43B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43b9 CJK Ideograph Extension A-43B9 | 43b8 CJK Ideograph Extension A-43B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43bb CJK Ideograph Extension A-43BB | 43ba CJK Ideograph Extension A-43BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43bd CJK Ideograph Extension A-43BD | 43bc CJK Ideograph Extension A-43BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43bf CJK Ideograph Extension A-43BF | 43be CJK Ideograph Extension A-43BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43c1 CJK Ideograph Extension A-43C1 | 43c0 CJK Ideograph Extension A-43C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43c3 CJK Ideograph Extension A-43C3 | 43c2 CJK Ideograph Extension A-43C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43c5 CJK Ideograph Extension A-43C5 | 43c4 CJK Ideograph Extension A-43C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43c7 CJK Ideograph Extension A-43C7 | 43c6 CJK Ideograph Extension A-43C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43c9 CJK Ideograph Extension A-43C9 | 43c8 CJK Ideograph Extension A-43C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43cb CJK Ideograph Extension A-43CB | 43ca CJK Ideograph Extension A-43CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43cd CJK Ideograph Extension A-43CD | 43cc CJK Ideograph Extension A-43CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43cf CJK Ideograph Extension A-43CF | 43ce CJK Ideograph Extension A-43CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43d1 CJK Ideograph Extension A-43D1 | 43d0 CJK Ideograph Extension A-43D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43d3 CJK Ideograph Extension A-43D3 | 43d2 CJK Ideograph Extension A-43D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43d5 CJK Ideograph Extension A-43D5 | 43d4 CJK Ideograph Extension A-43D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43d7 CJK Ideograph Extension A-43D7 | 43d6 CJK Ideograph Extension A-43D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43d9 CJK Ideograph Extension A-43D9 | 43d8 CJK Ideograph Extension A-43D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43db CJK Ideograph Extension A-43DB | 43da CJK Ideograph Extension A-43DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43dd CJK Ideograph Extension A-43DD | 43dc CJK Ideograph Extension A-43DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43df CJK Ideograph Extension A-43DF | 43de CJK Ideograph Extension A-43DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43e1 CJK Ideograph Extension A-43E1 | 43e0 CJK Ideograph Extension A-43E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43e3 CJK Ideograph Extension A-43E3 | 43e2 CJK Ideograph Extension A-43E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43e5 CJK Ideograph Extension A-43E5 | 43e4 CJK Ideograph Extension A-43E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43e7 CJK Ideograph Extension A-43E7 | 43e6 CJK Ideograph Extension A-43E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43e9 CJK Ideograph Extension A-43E9 | 43e8 CJK Ideograph Extension A-43E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43eb CJK Ideograph Extension A-43EB | 43ea CJK Ideograph Extension A-43EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43ed CJK Ideograph Extension A-43ED | 43ec CJK Ideograph Extension A-43EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43ef CJK Ideograph Extension A-43EF | 43ee CJK Ideograph Extension A-43EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43f1 CJK Ideograph Extension A-43F1 | 43f0 CJK Ideograph Extension A-43F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43f3 CJK Ideograph Extension A-43F3 | 43f2 CJK Ideograph Extension A-43F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43f5 CJK Ideograph Extension A-43F5 | 43f4 CJK Ideograph Extension A-43F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43f7 CJK Ideograph Extension A-43F7 | 43f6 CJK Ideograph Extension A-43F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43f9 CJK Ideograph Extension A-43F9 | 43f8 CJK Ideograph Extension A-43F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43fb CJK Ideograph Extension A-43FB | 43fa CJK Ideograph Extension A-43FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43fd CJK Ideograph Extension A-43FD | 43fc CJK Ideograph Extension A-43FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43ff CJK Ideograph Extension A-43FF | 43fe CJK Ideograph Extension A-43FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4401 CJK Ideograph Extension A-4401 | 4400 CJK Ideograph Extension A-4400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4403 CJK Ideograph Extension A-4403 | 4402 CJK Ideograph Extension A-4402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4405 CJK Ideograph Extension A-4405 | 4404 CJK Ideograph Extension A-4404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4407 CJK Ideograph Extension A-4407 | 4406 CJK Ideograph Extension A-4406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4409 CJK Ideograph Extension A-4409 | 4408 CJK Ideograph Extension A-4408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 440b CJK Ideograph Extension A-440B | 440a CJK Ideograph Extension A-440A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 440d CJK Ideograph Extension A-440D | 440c CJK Ideograph Extension A-440C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 440f CJK Ideograph Extension A-440F | 440e CJK Ideograph Extension A-440E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4411 CJK Ideograph Extension A-4411 | 4410 CJK Ideograph Extension A-4410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4413 CJK Ideograph Extension A-4413 | 4412 CJK Ideograph Extension A-4412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4415 CJK Ideograph Extension A-4415 | 4414 CJK Ideograph Extension A-4414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4417 CJK Ideograph Extension A-4417 | 4416 CJK Ideograph Extension A-4416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4419 CJK Ideograph Extension A-4419 | 4418 CJK Ideograph Extension A-4418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 441b CJK Ideograph Extension A-441B | 441a CJK Ideograph Extension A-441A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 441d CJK Ideograph Extension A-441D | 441c CJK Ideograph Extension A-441C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 441f CJK Ideograph Extension A-441F | 441e CJK Ideograph Extension A-441E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4421 CJK Ideograph Extension A-4421 | 4420 CJK Ideograph Extension A-4420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4423 CJK Ideograph Extension A-4423 | 4422 CJK Ideograph Extension A-4422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4425 CJK Ideograph Extension A-4425 | 4424 CJK Ideograph Extension A-4424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4427 CJK Ideograph Extension A-4427 | 4426 CJK Ideograph Extension A-4426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4429 CJK Ideograph Extension A-4429 | 4428 CJK Ideograph Extension A-4428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 442b CJK Ideograph Extension A-442B | 442a CJK Ideograph Extension A-442A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 442d CJK Ideograph Extension A-442D | 442c CJK Ideograph Extension A-442C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 442f CJK Ideograph Extension A-442F | 442e CJK Ideograph Extension A-442E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4431 CJK Ideograph Extension A-4431 | 4430 CJK Ideograph Extension A-4430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4433 CJK Ideograph Extension A-4433 | 4432 CJK Ideograph Extension A-4432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4435 CJK Ideograph Extension A-4435 | 4434 CJK Ideograph Extension A-4434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4437 CJK Ideograph Extension A-4437 | 4436 CJK Ideograph Extension A-4436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4439 CJK Ideograph Extension A-4439 | 4438 CJK Ideograph Extension A-4438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 443b CJK Ideograph Extension A-443B | 443a CJK Ideograph Extension A-443A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 443d CJK Ideograph Extension A-443D | 443c CJK Ideograph Extension A-443C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 443f CJK Ideograph Extension A-443F | 443e CJK Ideograph Extension A-443E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4441 CJK Ideograph Extension A-4441 | 4440 CJK Ideograph Extension A-4440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4443 CJK Ideograph Extension A-4443 | 4442 CJK Ideograph Extension A-4442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4445 CJK Ideograph Extension A-4445 | 4444 CJK Ideograph Extension A-4444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4447 CJK Ideograph Extension A-4447 | 4446 CJK Ideograph Extension A-4446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4449 CJK Ideograph Extension A-4449 | 4448 CJK Ideograph Extension A-4448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 444b CJK Ideograph Extension A-444B | 444a CJK Ideograph Extension A-444A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 444d CJK Ideograph Extension A-444D | 444c CJK Ideograph Extension A-444C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 444f CJK Ideograph Extension A-444F | 444e CJK Ideograph Extension A-444E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4451 CJK Ideograph Extension A-4451 | 4450 CJK Ideograph Extension A-4450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4453 CJK Ideograph Extension A-4453 | 4452 CJK Ideograph Extension A-4452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4455 CJK Ideograph Extension A-4455 | 4454 CJK Ideograph Extension A-4454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4457 CJK Ideograph Extension A-4457 | 4456 CJK Ideograph Extension A-4456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4459 CJK Ideograph Extension A-4459 | 4458 CJK Ideograph Extension A-4458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 445b CJK Ideograph Extension A-445B | 445a CJK Ideograph Extension A-445A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 445d CJK Ideograph Extension A-445D | 445c CJK Ideograph Extension A-445C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 445f CJK Ideograph Extension A-445F | 445e CJK Ideograph Extension A-445E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4461 CJK Ideograph Extension A-4461 | 4460 CJK Ideograph Extension A-4460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4463 CJK Ideograph Extension A-4463 | 4462 CJK Ideograph Extension A-4462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4465 CJK Ideograph Extension A-4465 | 4464 CJK Ideograph Extension A-4464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4467 CJK Ideograph Extension A-4467 | 4466 CJK Ideograph Extension A-4466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4469 CJK Ideograph Extension A-4469 | 4468 CJK Ideograph Extension A-4468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 446b CJK Ideograph Extension A-446B | 446a CJK Ideograph Extension A-446A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 446d CJK Ideograph Extension A-446D | 446c CJK Ideograph Extension A-446C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 446f CJK Ideograph Extension A-446F | 446e CJK Ideograph Extension A-446E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4471 CJK Ideograph Extension A-4471 | 4470 CJK Ideograph Extension A-4470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4473 CJK Ideograph Extension A-4473 | 4472 CJK Ideograph Extension A-4472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4475 CJK Ideograph Extension A-4475 | 4474 CJK Ideograph Extension A-4474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4477 CJK Ideograph Extension A-4477 | 4476 CJK Ideograph Extension A-4476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4479 CJK Ideograph Extension A-4479 | 4478 CJK Ideograph Extension A-4478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 447b CJK Ideograph Extension A-447B | 447a CJK Ideograph Extension A-447A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 447d CJK Ideograph Extension A-447D | 447c CJK Ideograph Extension A-447C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 447f CJK Ideograph Extension A-447F | 447e CJK Ideograph Extension A-447E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4481 CJK Ideograph Extension A-4481 | 4480 CJK Ideograph Extension A-4480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4483 CJK Ideograph Extension A-4483 | 4482 CJK Ideograph Extension A-4482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4485 CJK Ideograph Extension A-4485 | 4484 CJK Ideograph Extension A-4484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4487 CJK Ideograph Extension A-4487 | 4486 CJK Ideograph Extension A-4486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4489 CJK Ideograph Extension A-4489 | 4488 CJK Ideograph Extension A-4488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 448b CJK Ideograph Extension A-448B | 448a CJK Ideograph Extension A-448A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 448d CJK Ideograph Extension A-448D | 448c CJK Ideograph Extension A-448C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 448f CJK Ideograph Extension A-448F | 448e CJK Ideograph Extension A-448E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4491 CJK Ideograph Extension A-4491 | 4490 CJK Ideograph Extension A-4490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4493 CJK Ideograph Extension A-4493 | 4492 CJK Ideograph Extension A-4492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4495 CJK Ideograph Extension A-4495 | 4494 CJK Ideograph Extension A-4494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4497 CJK Ideograph Extension A-4497 | 4496 CJK Ideograph Extension A-4496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4499 CJK Ideograph Extension A-4499 | 4498 CJK Ideograph Extension A-4498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 449b CJK Ideograph Extension A-449B | 449a CJK Ideograph Extension A-449A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 449d CJK Ideograph Extension A-449D | 449c CJK Ideograph Extension A-449C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 449f CJK Ideograph Extension A-449F | 449e CJK Ideograph Extension A-449E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44a1 CJK Ideograph Extension A-44A1 | 44a0 CJK Ideograph Extension A-44A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44a3 CJK Ideograph Extension A-44A3 | 44a2 CJK Ideograph Extension A-44A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44a5 CJK Ideograph Extension A-44A5 | 44a4 CJK Ideograph Extension A-44A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44a7 CJK Ideograph Extension A-44A7 | 44a6 CJK Ideograph Extension A-44A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44a9 CJK Ideograph Extension A-44A9 | 44a8 CJK Ideograph Extension A-44A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44ab CJK Ideograph Extension A-44AB | 44aa CJK Ideograph Extension A-44AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44ad CJK Ideograph Extension A-44AD | 44ac CJK Ideograph Extension A-44AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44af CJK Ideograph Extension A-44AF | 44ae CJK Ideograph Extension A-44AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44b1 CJK Ideograph Extension A-44B1 | 44b0 CJK Ideograph Extension A-44B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44b3 CJK Ideograph Extension A-44B3 | 44b2 CJK Ideograph Extension A-44B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44b5 CJK Ideograph Extension A-44B5 | 44b4 CJK Ideograph Extension A-44B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44b7 CJK Ideograph Extension A-44B7 | 44b6 CJK Ideograph Extension A-44B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44b9 CJK Ideograph Extension A-44B9 | 44b8 CJK Ideograph Extension A-44B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44bb CJK Ideograph Extension A-44BB | 44ba CJK Ideograph Extension A-44BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44bd CJK Ideograph Extension A-44BD | 44bc CJK Ideograph Extension A-44BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44bf CJK Ideograph Extension A-44BF | 44be CJK Ideograph Extension A-44BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44c1 CJK Ideograph Extension A-44C1 | 44c0 CJK Ideograph Extension A-44C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44c3 CJK Ideograph Extension A-44C3 | 44c2 CJK Ideograph Extension A-44C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44c5 CJK Ideograph Extension A-44C5 | 44c4 CJK Ideograph Extension A-44C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44c7 CJK Ideograph Extension A-44C7 | 44c6 CJK Ideograph Extension A-44C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44c9 CJK Ideograph Extension A-44C9 | 44c8 CJK Ideograph Extension A-44C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44cb CJK Ideograph Extension A-44CB | 44ca CJK Ideograph Extension A-44CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44cd CJK Ideograph Extension A-44CD | 44cc CJK Ideograph Extension A-44CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44cf CJK Ideograph Extension A-44CF | 44ce CJK Ideograph Extension A-44CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44d1 CJK Ideograph Extension A-44D1 | 44d0 CJK Ideograph Extension A-44D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44d3 CJK Ideograph Extension A-44D3 | 44d2 CJK Ideograph Extension A-44D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44d5 CJK Ideograph Extension A-44D5 | 44d4 CJK Ideograph Extension A-44D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44d7 CJK Ideograph Extension A-44D7 | 44d6 CJK Ideograph Extension A-44D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44d9 CJK Ideograph Extension A-44D9 | 44d8 CJK Ideograph Extension A-44D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44db CJK Ideograph Extension A-44DB | 44da CJK Ideograph Extension A-44DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44dd CJK Ideograph Extension A-44DD | 44dc CJK Ideograph Extension A-44DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44df CJK Ideograph Extension A-44DF | 44de CJK Ideograph Extension A-44DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44e1 CJK Ideograph Extension A-44E1 | 44e0 CJK Ideograph Extension A-44E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44e3 CJK Ideograph Extension A-44E3 | 44e2 CJK Ideograph Extension A-44E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44e5 CJK Ideograph Extension A-44E5 | 44e4 CJK Ideograph Extension A-44E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44e7 CJK Ideograph Extension A-44E7 | 44e6 CJK Ideograph Extension A-44E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44e9 CJK Ideograph Extension A-44E9 | 44e8 CJK Ideograph Extension A-44E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44eb CJK Ideograph Extension A-44EB | 44ea CJK Ideograph Extension A-44EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44ed CJK Ideograph Extension A-44ED | 44ec CJK Ideograph Extension A-44EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44ef CJK Ideograph Extension A-44EF | 44ee CJK Ideograph Extension A-44EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44f1 CJK Ideograph Extension A-44F1 | 44f0 CJK Ideograph Extension A-44F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44f3 CJK Ideograph Extension A-44F3 | 44f2 CJK Ideograph Extension A-44F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44f5 CJK Ideograph Extension A-44F5 | 44f4 CJK Ideograph Extension A-44F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44f7 CJK Ideograph Extension A-44F7 | 44f6 CJK Ideograph Extension A-44F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44f9 CJK Ideograph Extension A-44F9 | 44f8 CJK Ideograph Extension A-44F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44fb CJK Ideograph Extension A-44FB | 44fa CJK Ideograph Extension A-44FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44fd CJK Ideograph Extension A-44FD | 44fc CJK Ideograph Extension A-44FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44ff CJK Ideograph Extension A-44FF | 44fe CJK Ideograph Extension A-44FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4501 CJK Ideograph Extension A-4501 | 4500 CJK Ideograph Extension A-4500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4503 CJK Ideograph Extension A-4503 | 4502 CJK Ideograph Extension A-4502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4505 CJK Ideograph Extension A-4505 | 4504 CJK Ideograph Extension A-4504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4507 CJK Ideograph Extension A-4507 | 4506 CJK Ideograph Extension A-4506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4509 CJK Ideograph Extension A-4509 | 4508 CJK Ideograph Extension A-4508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 450b CJK Ideograph Extension A-450B | 450a CJK Ideograph Extension A-450A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 450d CJK Ideograph Extension A-450D | 450c CJK Ideograph Extension A-450C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 450f CJK Ideograph Extension A-450F | 450e CJK Ideograph Extension A-450E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4511 CJK Ideograph Extension A-4511 | 4510 CJK Ideograph Extension A-4510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4513 CJK Ideograph Extension A-4513 | 4512 CJK Ideograph Extension A-4512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4515 CJK Ideograph Extension A-4515 | 4514 CJK Ideograph Extension A-4514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4517 CJK Ideograph Extension A-4517 | 4516 CJK Ideograph Extension A-4516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4519 CJK Ideograph Extension A-4519 | 4518 CJK Ideograph Extension A-4518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 451b CJK Ideograph Extension A-451B | 451a CJK Ideograph Extension A-451A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 451d CJK Ideograph Extension A-451D | 451c CJK Ideograph Extension A-451C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 451f CJK Ideograph Extension A-451F | 451e CJK Ideograph Extension A-451E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4521 CJK Ideograph Extension A-4521 | 4520 CJK Ideograph Extension A-4520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4523 CJK Ideograph Extension A-4523 | 4522 CJK Ideograph Extension A-4522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4525 CJK Ideograph Extension A-4525 | 4524 CJK Ideograph Extension A-4524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4527 CJK Ideograph Extension A-4527 | 4526 CJK Ideograph Extension A-4526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4529 CJK Ideograph Extension A-4529 | 4528 CJK Ideograph Extension A-4528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 452b CJK Ideograph Extension A-452B | 452a CJK Ideograph Extension A-452A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 452d CJK Ideograph Extension A-452D | 452c CJK Ideograph Extension A-452C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 452f CJK Ideograph Extension A-452F | 452e CJK Ideograph Extension A-452E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4531 CJK Ideograph Extension A-4531 | 4530 CJK Ideograph Extension A-4530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4533 CJK Ideograph Extension A-4533 | 4532 CJK Ideograph Extension A-4532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4535 CJK Ideograph Extension A-4535 | 4534 CJK Ideograph Extension A-4534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4537 CJK Ideograph Extension A-4537 | 4536 CJK Ideograph Extension A-4536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4539 CJK Ideograph Extension A-4539 | 4538 CJK Ideograph Extension A-4538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 453b CJK Ideograph Extension A-453B | 453a CJK Ideograph Extension A-453A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 453d CJK Ideograph Extension A-453D | 453c CJK Ideograph Extension A-453C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 453f CJK Ideograph Extension A-453F | 453e CJK Ideograph Extension A-453E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4541 CJK Ideograph Extension A-4541 | 4540 CJK Ideograph Extension A-4540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4543 CJK Ideograph Extension A-4543 | 4542 CJK Ideograph Extension A-4542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4545 CJK Ideograph Extension A-4545 | 4544 CJK Ideograph Extension A-4544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4547 CJK Ideograph Extension A-4547 | 4546 CJK Ideograph Extension A-4546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4549 CJK Ideograph Extension A-4549 | 4548 CJK Ideograph Extension A-4548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 454b CJK Ideograph Extension A-454B | 454a CJK Ideograph Extension A-454A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 454d CJK Ideograph Extension A-454D | 454c CJK Ideograph Extension A-454C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 454f CJK Ideograph Extension A-454F | 454e CJK Ideograph Extension A-454E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4551 CJK Ideograph Extension A-4551 | 4550 CJK Ideograph Extension A-4550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4553 CJK Ideograph Extension A-4553 | 4552 CJK Ideograph Extension A-4552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4555 CJK Ideograph Extension A-4555 | 4554 CJK Ideograph Extension A-4554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4557 CJK Ideograph Extension A-4557 | 4556 CJK Ideograph Extension A-4556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4559 CJK Ideograph Extension A-4559 | 4558 CJK Ideograph Extension A-4558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 455b CJK Ideograph Extension A-455B | 455a CJK Ideograph Extension A-455A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 455d CJK Ideograph Extension A-455D | 455c CJK Ideograph Extension A-455C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 455f CJK Ideograph Extension A-455F | 455e CJK Ideograph Extension A-455E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4561 CJK Ideograph Extension A-4561 | 4560 CJK Ideograph Extension A-4560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4563 CJK Ideograph Extension A-4563 | 4562 CJK Ideograph Extension A-4562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4565 CJK Ideograph Extension A-4565 | 4564 CJK Ideograph Extension A-4564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4567 CJK Ideograph Extension A-4567 | 4566 CJK Ideograph Extension A-4566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4569 CJK Ideograph Extension A-4569 | 4568 CJK Ideograph Extension A-4568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 456b CJK Ideograph Extension A-456B | 456a CJK Ideograph Extension A-456A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 456d CJK Ideograph Extension A-456D | 456c CJK Ideograph Extension A-456C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 456f CJK Ideograph Extension A-456F | 456e CJK Ideograph Extension A-456E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4571 CJK Ideograph Extension A-4571 | 4570 CJK Ideograph Extension A-4570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4573 CJK Ideograph Extension A-4573 | 4572 CJK Ideograph Extension A-4572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4575 CJK Ideograph Extension A-4575 | 4574 CJK Ideograph Extension A-4574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4577 CJK Ideograph Extension A-4577 | 4576 CJK Ideograph Extension A-4576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4579 CJK Ideograph Extension A-4579 | 4578 CJK Ideograph Extension A-4578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 457b CJK Ideograph Extension A-457B | 457a CJK Ideograph Extension A-457A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 457d CJK Ideograph Extension A-457D | 457c CJK Ideograph Extension A-457C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 457f CJK Ideograph Extension A-457F | 457e CJK Ideograph Extension A-457E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4581 CJK Ideograph Extension A-4581 | 4580 CJK Ideograph Extension A-4580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4583 CJK Ideograph Extension A-4583 | 4582 CJK Ideograph Extension A-4582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4585 CJK Ideograph Extension A-4585 | 4584 CJK Ideograph Extension A-4584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4587 CJK Ideograph Extension A-4587 | 4586 CJK Ideograph Extension A-4586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4589 CJK Ideograph Extension A-4589 | 4588 CJK Ideograph Extension A-4588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 458b CJK Ideograph Extension A-458B | 458a CJK Ideograph Extension A-458A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 458d CJK Ideograph Extension A-458D | 458c CJK Ideograph Extension A-458C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 458f CJK Ideograph Extension A-458F | 458e CJK Ideograph Extension A-458E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4591 CJK Ideograph Extension A-4591 | 4590 CJK Ideograph Extension A-4590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4593 CJK Ideograph Extension A-4593 | 4592 CJK Ideograph Extension A-4592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4595 CJK Ideograph Extension A-4595 | 4594 CJK Ideograph Extension A-4594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4597 CJK Ideograph Extension A-4597 | 4596 CJK Ideograph Extension A-4596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4599 CJK Ideograph Extension A-4599 | 4598 CJK Ideograph Extension A-4598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 459b CJK Ideograph Extension A-459B | 459a CJK Ideograph Extension A-459A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 459d CJK Ideograph Extension A-459D | 459c CJK Ideograph Extension A-459C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 459f CJK Ideograph Extension A-459F | 459e CJK Ideograph Extension A-459E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45a1 CJK Ideograph Extension A-45A1 | 45a0 CJK Ideograph Extension A-45A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45a3 CJK Ideograph Extension A-45A3 | 45a2 CJK Ideograph Extension A-45A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45a5 CJK Ideograph Extension A-45A5 | 45a4 CJK Ideograph Extension A-45A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45a7 CJK Ideograph Extension A-45A7 | 45a6 CJK Ideograph Extension A-45A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45a9 CJK Ideograph Extension A-45A9 | 45a8 CJK Ideograph Extension A-45A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45ab CJK Ideograph Extension A-45AB | 45aa CJK Ideograph Extension A-45AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45ad CJK Ideograph Extension A-45AD | 45ac CJK Ideograph Extension A-45AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45af CJK Ideograph Extension A-45AF | 45ae CJK Ideograph Extension A-45AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45b1 CJK Ideograph Extension A-45B1 | 45b0 CJK Ideograph Extension A-45B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45b3 CJK Ideograph Extension A-45B3 | 45b2 CJK Ideograph Extension A-45B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45b5 CJK Ideograph Extension A-45B5 | 45b4 CJK Ideograph Extension A-45B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45b7 CJK Ideograph Extension A-45B7 | 45b6 CJK Ideograph Extension A-45B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45b9 CJK Ideograph Extension A-45B9 | 45b8 CJK Ideograph Extension A-45B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45bb CJK Ideograph Extension A-45BB | 45ba CJK Ideograph Extension A-45BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45bd CJK Ideograph Extension A-45BD | 45bc CJK Ideograph Extension A-45BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45bf CJK Ideograph Extension A-45BF | 45be CJK Ideograph Extension A-45BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45c1 CJK Ideograph Extension A-45C1 | 45c0 CJK Ideograph Extension A-45C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45c3 CJK Ideograph Extension A-45C3 | 45c2 CJK Ideograph Extension A-45C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45c5 CJK Ideograph Extension A-45C5 | 45c4 CJK Ideograph Extension A-45C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45c7 CJK Ideograph Extension A-45C7 | 45c6 CJK Ideograph Extension A-45C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45c9 CJK Ideograph Extension A-45C9 | 45c8 CJK Ideograph Extension A-45C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45cb CJK Ideograph Extension A-45CB | 45ca CJK Ideograph Extension A-45CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45cd CJK Ideograph Extension A-45CD | 45cc CJK Ideograph Extension A-45CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45cf CJK Ideograph Extension A-45CF | 45ce CJK Ideograph Extension A-45CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45d1 CJK Ideograph Extension A-45D1 | 45d0 CJK Ideograph Extension A-45D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45d3 CJK Ideograph Extension A-45D3 | 45d2 CJK Ideograph Extension A-45D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45d5 CJK Ideograph Extension A-45D5 | 45d4 CJK Ideograph Extension A-45D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45d7 CJK Ideograph Extension A-45D7 | 45d6 CJK Ideograph Extension A-45D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45d9 CJK Ideograph Extension A-45D9 | 45d8 CJK Ideograph Extension A-45D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45db CJK Ideograph Extension A-45DB | 45da CJK Ideograph Extension A-45DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45dd CJK Ideograph Extension A-45DD | 45dc CJK Ideograph Extension A-45DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45df CJK Ideograph Extension A-45DF | 45de CJK Ideograph Extension A-45DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45e1 CJK Ideograph Extension A-45E1 | 45e0 CJK Ideograph Extension A-45E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45e3 CJK Ideograph Extension A-45E3 | 45e2 CJK Ideograph Extension A-45E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45e5 CJK Ideograph Extension A-45E5 | 45e4 CJK Ideograph Extension A-45E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45e7 CJK Ideograph Extension A-45E7 | 45e6 CJK Ideograph Extension A-45E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45e9 CJK Ideograph Extension A-45E9 | 45e8 CJK Ideograph Extension A-45E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45eb CJK Ideograph Extension A-45EB | 45ea CJK Ideograph Extension A-45EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45ed CJK Ideograph Extension A-45ED | 45ec CJK Ideograph Extension A-45EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45ef CJK Ideograph Extension A-45EF | 45ee CJK Ideograph Extension A-45EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45f1 CJK Ideograph Extension A-45F1 | 45f0 CJK Ideograph Extension A-45F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45f3 CJK Ideograph Extension A-45F3 | 45f2 CJK Ideograph Extension A-45F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45f5 CJK Ideograph Extension A-45F5 | 45f4 CJK Ideograph Extension A-45F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45f7 CJK Ideograph Extension A-45F7 | 45f6 CJK Ideograph Extension A-45F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45f9 CJK Ideograph Extension A-45F9 | 45f8 CJK Ideograph Extension A-45F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45fb CJK Ideograph Extension A-45FB | 45fa CJK Ideograph Extension A-45FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45fd CJK Ideograph Extension A-45FD | 45fc CJK Ideograph Extension A-45FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45ff CJK Ideograph Extension A-45FF | 45fe CJK Ideograph Extension A-45FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4601 CJK Ideograph Extension A-4601 | 4600 CJK Ideograph Extension A-4600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4603 CJK Ideograph Extension A-4603 | 4602 CJK Ideograph Extension A-4602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4605 CJK Ideograph Extension A-4605 | 4604 CJK Ideograph Extension A-4604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4607 CJK Ideograph Extension A-4607 | 4606 CJK Ideograph Extension A-4606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4609 CJK Ideograph Extension A-4609 | 4608 CJK Ideograph Extension A-4608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 460b CJK Ideograph Extension A-460B | 460a CJK Ideograph Extension A-460A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 460d CJK Ideograph Extension A-460D | 460c CJK Ideograph Extension A-460C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 460f CJK Ideograph Extension A-460F | 460e CJK Ideograph Extension A-460E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4611 CJK Ideograph Extension A-4611 | 4610 CJK Ideograph Extension A-4610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4613 CJK Ideograph Extension A-4613 | 4612 CJK Ideograph Extension A-4612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4615 CJK Ideograph Extension A-4615 | 4614 CJK Ideograph Extension A-4614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4617 CJK Ideograph Extension A-4617 | 4616 CJK Ideograph Extension A-4616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4619 CJK Ideograph Extension A-4619 | 4618 CJK Ideograph Extension A-4618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 461b CJK Ideograph Extension A-461B | 461a CJK Ideograph Extension A-461A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 461d CJK Ideograph Extension A-461D | 461c CJK Ideograph Extension A-461C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 461f CJK Ideograph Extension A-461F | 461e CJK Ideograph Extension A-461E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4621 CJK Ideograph Extension A-4621 | 4620 CJK Ideograph Extension A-4620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4623 CJK Ideograph Extension A-4623 | 4622 CJK Ideograph Extension A-4622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4625 CJK Ideograph Extension A-4625 | 4624 CJK Ideograph Extension A-4624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4627 CJK Ideograph Extension A-4627 | 4626 CJK Ideograph Extension A-4626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4629 CJK Ideograph Extension A-4629 | 4628 CJK Ideograph Extension A-4628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 462b CJK Ideograph Extension A-462B | 462a CJK Ideograph Extension A-462A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 462d CJK Ideograph Extension A-462D | 462c CJK Ideograph Extension A-462C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 462f CJK Ideograph Extension A-462F | 462e CJK Ideograph Extension A-462E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4631 CJK Ideograph Extension A-4631 | 4630 CJK Ideograph Extension A-4630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4633 CJK Ideograph Extension A-4633 | 4632 CJK Ideograph Extension A-4632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4635 CJK Ideograph Extension A-4635 | 4634 CJK Ideograph Extension A-4634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4637 CJK Ideograph Extension A-4637 | 4636 CJK Ideograph Extension A-4636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4639 CJK Ideograph Extension A-4639 | 4638 CJK Ideograph Extension A-4638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 463b CJK Ideograph Extension A-463B | 463a CJK Ideograph Extension A-463A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 463d CJK Ideograph Extension A-463D | 463c CJK Ideograph Extension A-463C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 463f CJK Ideograph Extension A-463F | 463e CJK Ideograph Extension A-463E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4641 CJK Ideograph Extension A-4641 | 4640 CJK Ideograph Extension A-4640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4643 CJK Ideograph Extension A-4643 | 4642 CJK Ideograph Extension A-4642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4645 CJK Ideograph Extension A-4645 | 4644 CJK Ideograph Extension A-4644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4647 CJK Ideograph Extension A-4647 | 4646 CJK Ideograph Extension A-4646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4649 CJK Ideograph Extension A-4649 | 4648 CJK Ideograph Extension A-4648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 464b CJK Ideograph Extension A-464B | 464a CJK Ideograph Extension A-464A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 464d CJK Ideograph Extension A-464D | 464c CJK Ideograph Extension A-464C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 464f CJK Ideograph Extension A-464F | 464e CJK Ideograph Extension A-464E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4651 CJK Ideograph Extension A-4651 | 4650 CJK Ideograph Extension A-4650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4653 CJK Ideograph Extension A-4653 | 4652 CJK Ideograph Extension A-4652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4655 CJK Ideograph Extension A-4655 | 4654 CJK Ideograph Extension A-4654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4657 CJK Ideograph Extension A-4657 | 4656 CJK Ideograph Extension A-4656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4659 CJK Ideograph Extension A-4659 | 4658 CJK Ideograph Extension A-4658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 465b CJK Ideograph Extension A-465B | 465a CJK Ideograph Extension A-465A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 465d CJK Ideograph Extension A-465D | 465c CJK Ideograph Extension A-465C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 465f CJK Ideograph Extension A-465F | 465e CJK Ideograph Extension A-465E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4661 CJK Ideograph Extension A-4661 | 4660 CJK Ideograph Extension A-4660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4663 CJK Ideograph Extension A-4663 | 4662 CJK Ideograph Extension A-4662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4665 CJK Ideograph Extension A-4665 | 4664 CJK Ideograph Extension A-4664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4667 CJK Ideograph Extension A-4667 | 4666 CJK Ideograph Extension A-4666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4669 CJK Ideograph Extension A-4669 | 4668 CJK Ideograph Extension A-4668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 466b CJK Ideograph Extension A-466B | 466a CJK Ideograph Extension A-466A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 466d CJK Ideograph Extension A-466D | 466c CJK Ideograph Extension A-466C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 466f CJK Ideograph Extension A-466F | 466e CJK Ideograph Extension A-466E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4671 CJK Ideograph Extension A-4671 | 4670 CJK Ideograph Extension A-4670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4673 CJK Ideograph Extension A-4673 | 4672 CJK Ideograph Extension A-4672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4675 CJK Ideograph Extension A-4675 | 4674 CJK Ideograph Extension A-4674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4677 CJK Ideograph Extension A-4677 | 4676 CJK Ideograph Extension A-4676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4679 CJK Ideograph Extension A-4679 | 4678 CJK Ideograph Extension A-4678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 467b CJK Ideograph Extension A-467B | 467a CJK Ideograph Extension A-467A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 467d CJK Ideograph Extension A-467D | 467c CJK Ideograph Extension A-467C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 467f CJK Ideograph Extension A-467F | 467e CJK Ideograph Extension A-467E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4681 CJK Ideograph Extension A-4681 | 4680 CJK Ideograph Extension A-4680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4683 CJK Ideograph Extension A-4683 | 4682 CJK Ideograph Extension A-4682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4685 CJK Ideograph Extension A-4685 | 4684 CJK Ideograph Extension A-4684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4687 CJK Ideograph Extension A-4687 | 4686 CJK Ideograph Extension A-4686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4689 CJK Ideograph Extension A-4689 | 4688 CJK Ideograph Extension A-4688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 468b CJK Ideograph Extension A-468B | 468a CJK Ideograph Extension A-468A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 468d CJK Ideograph Extension A-468D | 468c CJK Ideograph Extension A-468C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 468f CJK Ideograph Extension A-468F | 468e CJK Ideograph Extension A-468E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4691 CJK Ideograph Extension A-4691 | 4690 CJK Ideograph Extension A-4690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4693 CJK Ideograph Extension A-4693 | 4692 CJK Ideograph Extension A-4692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4695 CJK Ideograph Extension A-4695 | 4694 CJK Ideograph Extension A-4694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4697 CJK Ideograph Extension A-4697 | 4696 CJK Ideograph Extension A-4696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4699 CJK Ideograph Extension A-4699 | 4698 CJK Ideograph Extension A-4698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 469b CJK Ideograph Extension A-469B | 469a CJK Ideograph Extension A-469A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 469d CJK Ideograph Extension A-469D | 469c CJK Ideograph Extension A-469C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 469f CJK Ideograph Extension A-469F | 469e CJK Ideograph Extension A-469E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46a1 CJK Ideograph Extension A-46A1 | 46a0 CJK Ideograph Extension A-46A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46a3 CJK Ideograph Extension A-46A3 | 46a2 CJK Ideograph Extension A-46A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46a5 CJK Ideograph Extension A-46A5 | 46a4 CJK Ideograph Extension A-46A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46a7 CJK Ideograph Extension A-46A7 | 46a6 CJK Ideograph Extension A-46A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46a9 CJK Ideograph Extension A-46A9 | 46a8 CJK Ideograph Extension A-46A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46ab CJK Ideograph Extension A-46AB | 46aa CJK Ideograph Extension A-46AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46ad CJK Ideograph Extension A-46AD | 46ac CJK Ideograph Extension A-46AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46af CJK Ideograph Extension A-46AF | 46ae CJK Ideograph Extension A-46AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46b1 CJK Ideograph Extension A-46B1 | 46b0 CJK Ideograph Extension A-46B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46b3 CJK Ideograph Extension A-46B3 | 46b2 CJK Ideograph Extension A-46B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46b5 CJK Ideograph Extension A-46B5 | 46b4 CJK Ideograph Extension A-46B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46b7 CJK Ideograph Extension A-46B7 | 46b6 CJK Ideograph Extension A-46B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46b9 CJK Ideograph Extension A-46B9 | 46b8 CJK Ideograph Extension A-46B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46bb CJK Ideograph Extension A-46BB | 46ba CJK Ideograph Extension A-46BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46bd CJK Ideograph Extension A-46BD | 46bc CJK Ideograph Extension A-46BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46bf CJK Ideograph Extension A-46BF | 46be CJK Ideograph Extension A-46BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46c1 CJK Ideograph Extension A-46C1 | 46c0 CJK Ideograph Extension A-46C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46c3 CJK Ideograph Extension A-46C3 | 46c2 CJK Ideograph Extension A-46C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46c5 CJK Ideograph Extension A-46C5 | 46c4 CJK Ideograph Extension A-46C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46c7 CJK Ideograph Extension A-46C7 | 46c6 CJK Ideograph Extension A-46C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46c9 CJK Ideograph Extension A-46C9 | 46c8 CJK Ideograph Extension A-46C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46cb CJK Ideograph Extension A-46CB | 46ca CJK Ideograph Extension A-46CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46cd CJK Ideograph Extension A-46CD | 46cc CJK Ideograph Extension A-46CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46cf CJK Ideograph Extension A-46CF | 46ce CJK Ideograph Extension A-46CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46d1 CJK Ideograph Extension A-46D1 | 46d0 CJK Ideograph Extension A-46D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46d3 CJK Ideograph Extension A-46D3 | 46d2 CJK Ideograph Extension A-46D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46d5 CJK Ideograph Extension A-46D5 | 46d4 CJK Ideograph Extension A-46D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46d7 CJK Ideograph Extension A-46D7 | 46d6 CJK Ideograph Extension A-46D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46d9 CJK Ideograph Extension A-46D9 | 46d8 CJK Ideograph Extension A-46D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46db CJK Ideograph Extension A-46DB | 46da CJK Ideograph Extension A-46DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46dd CJK Ideograph Extension A-46DD | 46dc CJK Ideograph Extension A-46DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46df CJK Ideograph Extension A-46DF | 46de CJK Ideograph Extension A-46DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46e1 CJK Ideograph Extension A-46E1 | 46e0 CJK Ideograph Extension A-46E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46e3 CJK Ideograph Extension A-46E3 | 46e2 CJK Ideograph Extension A-46E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46e5 CJK Ideograph Extension A-46E5 | 46e4 CJK Ideograph Extension A-46E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46e7 CJK Ideograph Extension A-46E7 | 46e6 CJK Ideograph Extension A-46E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46e9 CJK Ideograph Extension A-46E9 | 46e8 CJK Ideograph Extension A-46E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46eb CJK Ideograph Extension A-46EB | 46ea CJK Ideograph Extension A-46EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46ed CJK Ideograph Extension A-46ED | 46ec CJK Ideograph Extension A-46EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46ef CJK Ideograph Extension A-46EF | 46ee CJK Ideograph Extension A-46EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46f1 CJK Ideograph Extension A-46F1 | 46f0 CJK Ideograph Extension A-46F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46f3 CJK Ideograph Extension A-46F3 | 46f2 CJK Ideograph Extension A-46F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46f5 CJK Ideograph Extension A-46F5 | 46f4 CJK Ideograph Extension A-46F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46f7 CJK Ideograph Extension A-46F7 | 46f6 CJK Ideograph Extension A-46F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46f9 CJK Ideograph Extension A-46F9 | 46f8 CJK Ideograph Extension A-46F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46fb CJK Ideograph Extension A-46FB | 46fa CJK Ideograph Extension A-46FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46fd CJK Ideograph Extension A-46FD | 46fc CJK Ideograph Extension A-46FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46ff CJK Ideograph Extension A-46FF | 46fe CJK Ideograph Extension A-46FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4701 CJK Ideograph Extension A-4701 | 4700 CJK Ideograph Extension A-4700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4703 CJK Ideograph Extension A-4703 | 4702 CJK Ideograph Extension A-4702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4705 CJK Ideograph Extension A-4705 | 4704 CJK Ideograph Extension A-4704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4707 CJK Ideograph Extension A-4707 | 4706 CJK Ideograph Extension A-4706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4709 CJK Ideograph Extension A-4709 | 4708 CJK Ideograph Extension A-4708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 470b CJK Ideograph Extension A-470B | 470a CJK Ideograph Extension A-470A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 470d CJK Ideograph Extension A-470D | 470c CJK Ideograph Extension A-470C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 470f CJK Ideograph Extension A-470F | 470e CJK Ideograph Extension A-470E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4711 CJK Ideograph Extension A-4711 | 4710 CJK Ideograph Extension A-4710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4713 CJK Ideograph Extension A-4713 | 4712 CJK Ideograph Extension A-4712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4715 CJK Ideograph Extension A-4715 | 4714 CJK Ideograph Extension A-4714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4717 CJK Ideograph Extension A-4717 | 4716 CJK Ideograph Extension A-4716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4719 CJK Ideograph Extension A-4719 | 4718 CJK Ideograph Extension A-4718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 471b CJK Ideograph Extension A-471B | 471a CJK Ideograph Extension A-471A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 471d CJK Ideograph Extension A-471D | 471c CJK Ideograph Extension A-471C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 471f CJK Ideograph Extension A-471F | 471e CJK Ideograph Extension A-471E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4721 CJK Ideograph Extension A-4721 | 4720 CJK Ideograph Extension A-4720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4723 CJK Ideograph Extension A-4723 | 4722 CJK Ideograph Extension A-4722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4725 CJK Ideograph Extension A-4725 | 4724 CJK Ideograph Extension A-4724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4727 CJK Ideograph Extension A-4727 | 4726 CJK Ideograph Extension A-4726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4729 CJK Ideograph Extension A-4729 | 4728 CJK Ideograph Extension A-4728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 472b CJK Ideograph Extension A-472B | 472a CJK Ideograph Extension A-472A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 472d CJK Ideograph Extension A-472D | 472c CJK Ideograph Extension A-472C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 472f CJK Ideograph Extension A-472F | 472e CJK Ideograph Extension A-472E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4731 CJK Ideograph Extension A-4731 | 4730 CJK Ideograph Extension A-4730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4733 CJK Ideograph Extension A-4733 | 4732 CJK Ideograph Extension A-4732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4735 CJK Ideograph Extension A-4735 | 4734 CJK Ideograph Extension A-4734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4737 CJK Ideograph Extension A-4737 | 4736 CJK Ideograph Extension A-4736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4739 CJK Ideograph Extension A-4739 | 4738 CJK Ideograph Extension A-4738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 473b CJK Ideograph Extension A-473B | 473a CJK Ideograph Extension A-473A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 473d CJK Ideograph Extension A-473D | 473c CJK Ideograph Extension A-473C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 473f CJK Ideograph Extension A-473F | 473e CJK Ideograph Extension A-473E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4741 CJK Ideograph Extension A-4741 | 4740 CJK Ideograph Extension A-4740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4743 CJK Ideograph Extension A-4743 | 4742 CJK Ideograph Extension A-4742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4745 CJK Ideograph Extension A-4745 | 4744 CJK Ideograph Extension A-4744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4747 CJK Ideograph Extension A-4747 | 4746 CJK Ideograph Extension A-4746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4749 CJK Ideograph Extension A-4749 | 4748 CJK Ideograph Extension A-4748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 474b CJK Ideograph Extension A-474B | 474a CJK Ideograph Extension A-474A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 474d CJK Ideograph Extension A-474D | 474c CJK Ideograph Extension A-474C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 474f CJK Ideograph Extension A-474F | 474e CJK Ideograph Extension A-474E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4751 CJK Ideograph Extension A-4751 | 4750 CJK Ideograph Extension A-4750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4753 CJK Ideograph Extension A-4753 | 4752 CJK Ideograph Extension A-4752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4755 CJK Ideograph Extension A-4755 | 4754 CJK Ideograph Extension A-4754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4757 CJK Ideograph Extension A-4757 | 4756 CJK Ideograph Extension A-4756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4759 CJK Ideograph Extension A-4759 | 4758 CJK Ideograph Extension A-4758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 475b CJK Ideograph Extension A-475B | 475a CJK Ideograph Extension A-475A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 475d CJK Ideograph Extension A-475D | 475c CJK Ideograph Extension A-475C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 475f CJK Ideograph Extension A-475F | 475e CJK Ideograph Extension A-475E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4761 CJK Ideograph Extension A-4761 | 4760 CJK Ideograph Extension A-4760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4763 CJK Ideograph Extension A-4763 | 4762 CJK Ideograph Extension A-4762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4765 CJK Ideograph Extension A-4765 | 4764 CJK Ideograph Extension A-4764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4767 CJK Ideograph Extension A-4767 | 4766 CJK Ideograph Extension A-4766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4769 CJK Ideograph Extension A-4769 | 4768 CJK Ideograph Extension A-4768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 476b CJK Ideograph Extension A-476B | 476a CJK Ideograph Extension A-476A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 476d CJK Ideograph Extension A-476D | 476c CJK Ideograph Extension A-476C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 476f CJK Ideograph Extension A-476F | 476e CJK Ideograph Extension A-476E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4771 CJK Ideograph Extension A-4771 | 4770 CJK Ideograph Extension A-4770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4773 CJK Ideograph Extension A-4773 | 4772 CJK Ideograph Extension A-4772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4775 CJK Ideograph Extension A-4775 | 4774 CJK Ideograph Extension A-4774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4777 CJK Ideograph Extension A-4777 | 4776 CJK Ideograph Extension A-4776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4779 CJK Ideograph Extension A-4779 | 4778 CJK Ideograph Extension A-4778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 477b CJK Ideograph Extension A-477B | 477a CJK Ideograph Extension A-477A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 477d CJK Ideograph Extension A-477D | 477c CJK Ideograph Extension A-477C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 477f CJK Ideograph Extension A-477F | 477e CJK Ideograph Extension A-477E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4781 CJK Ideograph Extension A-4781 | 4780 CJK Ideograph Extension A-4780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4783 CJK Ideograph Extension A-4783 | 4782 CJK Ideograph Extension A-4782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4785 CJK Ideograph Extension A-4785 | 4784 CJK Ideograph Extension A-4784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4787 CJK Ideograph Extension A-4787 | 4786 CJK Ideograph Extension A-4786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4789 CJK Ideograph Extension A-4789 | 4788 CJK Ideograph Extension A-4788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 478b CJK Ideograph Extension A-478B | 478a CJK Ideograph Extension A-478A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 478d CJK Ideograph Extension A-478D | 478c CJK Ideograph Extension A-478C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 478f CJK Ideograph Extension A-478F | 478e CJK Ideograph Extension A-478E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4791 CJK Ideograph Extension A-4791 | 4790 CJK Ideograph Extension A-4790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4793 CJK Ideograph Extension A-4793 | 4792 CJK Ideograph Extension A-4792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4795 CJK Ideograph Extension A-4795 | 4794 CJK Ideograph Extension A-4794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4797 CJK Ideograph Extension A-4797 | 4796 CJK Ideograph Extension A-4796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4799 CJK Ideograph Extension A-4799 | 4798 CJK Ideograph Extension A-4798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 479b CJK Ideograph Extension A-479B | 479a CJK Ideograph Extension A-479A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 479d CJK Ideograph Extension A-479D | 479c CJK Ideograph Extension A-479C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 479f CJK Ideograph Extension A-479F | 479e CJK Ideograph Extension A-479E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47a1 CJK Ideograph Extension A-47A1 | 47a0 CJK Ideograph Extension A-47A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47a3 CJK Ideograph Extension A-47A3 | 47a2 CJK Ideograph Extension A-47A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47a5 CJK Ideograph Extension A-47A5 | 47a4 CJK Ideograph Extension A-47A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47a7 CJK Ideograph Extension A-47A7 | 47a6 CJK Ideograph Extension A-47A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47a9 CJK Ideograph Extension A-47A9 | 47a8 CJK Ideograph Extension A-47A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47ab CJK Ideograph Extension A-47AB | 47aa CJK Ideograph Extension A-47AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47ad CJK Ideograph Extension A-47AD | 47ac CJK Ideograph Extension A-47AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47af CJK Ideograph Extension A-47AF | 47ae CJK Ideograph Extension A-47AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47b1 CJK Ideograph Extension A-47B1 | 47b0 CJK Ideograph Extension A-47B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47b3 CJK Ideograph Extension A-47B3 | 47b2 CJK Ideograph Extension A-47B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47b5 CJK Ideograph Extension A-47B5 | 47b4 CJK Ideograph Extension A-47B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47b7 CJK Ideograph Extension A-47B7 | 47b6 CJK Ideograph Extension A-47B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47b9 CJK Ideograph Extension A-47B9 | 47b8 CJK Ideograph Extension A-47B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47bb CJK Ideograph Extension A-47BB | 47ba CJK Ideograph Extension A-47BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47bd CJK Ideograph Extension A-47BD | 47bc CJK Ideograph Extension A-47BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47bf CJK Ideograph Extension A-47BF | 47be CJK Ideograph Extension A-47BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47c1 CJK Ideograph Extension A-47C1 | 47c0 CJK Ideograph Extension A-47C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47c3 CJK Ideograph Extension A-47C3 | 47c2 CJK Ideograph Extension A-47C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47c5 CJK Ideograph Extension A-47C5 | 47c4 CJK Ideograph Extension A-47C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47c7 CJK Ideograph Extension A-47C7 | 47c6 CJK Ideograph Extension A-47C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47c9 CJK Ideograph Extension A-47C9 | 47c8 CJK Ideograph Extension A-47C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47cb CJK Ideograph Extension A-47CB | 47ca CJK Ideograph Extension A-47CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47cd CJK Ideograph Extension A-47CD | 47cc CJK Ideograph Extension A-47CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47cf CJK Ideograph Extension A-47CF | 47ce CJK Ideograph Extension A-47CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47d1 CJK Ideograph Extension A-47D1 | 47d0 CJK Ideograph Extension A-47D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47d3 CJK Ideograph Extension A-47D3 | 47d2 CJK Ideograph Extension A-47D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47d5 CJK Ideograph Extension A-47D5 | 47d4 CJK Ideograph Extension A-47D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47d7 CJK Ideograph Extension A-47D7 | 47d6 CJK Ideograph Extension A-47D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47d9 CJK Ideograph Extension A-47D9 | 47d8 CJK Ideograph Extension A-47D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47db CJK Ideograph Extension A-47DB | 47da CJK Ideograph Extension A-47DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47dd CJK Ideograph Extension A-47DD | 47dc CJK Ideograph Extension A-47DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47df CJK Ideograph Extension A-47DF | 47de CJK Ideograph Extension A-47DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47e1 CJK Ideograph Extension A-47E1 | 47e0 CJK Ideograph Extension A-47E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47e3 CJK Ideograph Extension A-47E3 | 47e2 CJK Ideograph Extension A-47E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47e5 CJK Ideograph Extension A-47E5 | 47e4 CJK Ideograph Extension A-47E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47e7 CJK Ideograph Extension A-47E7 | 47e6 CJK Ideograph Extension A-47E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47e9 CJK Ideograph Extension A-47E9 | 47e8 CJK Ideograph Extension A-47E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47eb CJK Ideograph Extension A-47EB | 47ea CJK Ideograph Extension A-47EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47ed CJK Ideograph Extension A-47ED | 47ec CJK Ideograph Extension A-47EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47ef CJK Ideograph Extension A-47EF | 47ee CJK Ideograph Extension A-47EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47f1 CJK Ideograph Extension A-47F1 | 47f0 CJK Ideograph Extension A-47F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47f3 CJK Ideograph Extension A-47F3 | 47f2 CJK Ideograph Extension A-47F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47f5 CJK Ideograph Extension A-47F5 | 47f4 CJK Ideograph Extension A-47F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47f7 CJK Ideograph Extension A-47F7 | 47f6 CJK Ideograph Extension A-47F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47f9 CJK Ideograph Extension A-47F9 | 47f8 CJK Ideograph Extension A-47F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47fb CJK Ideograph Extension A-47FB | 47fa CJK Ideograph Extension A-47FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47fd CJK Ideograph Extension A-47FD | 47fc CJK Ideograph Extension A-47FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47ff CJK Ideograph Extension A-47FF | 47fe CJK Ideograph Extension A-47FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4801 CJK Ideograph Extension A-4801 | 4800 CJK Ideograph Extension A-4800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4803 CJK Ideograph Extension A-4803 | 4802 CJK Ideograph Extension A-4802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4805 CJK Ideograph Extension A-4805 | 4804 CJK Ideograph Extension A-4804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4807 CJK Ideograph Extension A-4807 | 4806 CJK Ideograph Extension A-4806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4809 CJK Ideograph Extension A-4809 | 4808 CJK Ideograph Extension A-4808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 480b CJK Ideograph Extension A-480B | 480a CJK Ideograph Extension A-480A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 480d CJK Ideograph Extension A-480D | 480c CJK Ideograph Extension A-480C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 480f CJK Ideograph Extension A-480F | 480e CJK Ideograph Extension A-480E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4811 CJK Ideograph Extension A-4811 | 4810 CJK Ideograph Extension A-4810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4813 CJK Ideograph Extension A-4813 | 4812 CJK Ideograph Extension A-4812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4815 CJK Ideograph Extension A-4815 | 4814 CJK Ideograph Extension A-4814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4817 CJK Ideograph Extension A-4817 | 4816 CJK Ideograph Extension A-4816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4819 CJK Ideograph Extension A-4819 | 4818 CJK Ideograph Extension A-4818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 481b CJK Ideograph Extension A-481B | 481a CJK Ideograph Extension A-481A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 481d CJK Ideograph Extension A-481D | 481c CJK Ideograph Extension A-481C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 481f CJK Ideograph Extension A-481F | 481e CJK Ideograph Extension A-481E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4821 CJK Ideograph Extension A-4821 | 4820 CJK Ideograph Extension A-4820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4823 CJK Ideograph Extension A-4823 | 4822 CJK Ideograph Extension A-4822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4825 CJK Ideograph Extension A-4825 | 4824 CJK Ideograph Extension A-4824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4827 CJK Ideograph Extension A-4827 | 4826 CJK Ideograph Extension A-4826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4829 CJK Ideograph Extension A-4829 | 4828 CJK Ideograph Extension A-4828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 482b CJK Ideograph Extension A-482B | 482a CJK Ideograph Extension A-482A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 482d CJK Ideograph Extension A-482D | 482c CJK Ideograph Extension A-482C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 482f CJK Ideograph Extension A-482F | 482e CJK Ideograph Extension A-482E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4831 CJK Ideograph Extension A-4831 | 4830 CJK Ideograph Extension A-4830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4833 CJK Ideograph Extension A-4833 | 4832 CJK Ideograph Extension A-4832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4835 CJK Ideograph Extension A-4835 | 4834 CJK Ideograph Extension A-4834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4837 CJK Ideograph Extension A-4837 | 4836 CJK Ideograph Extension A-4836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4839 CJK Ideograph Extension A-4839 | 4838 CJK Ideograph Extension A-4838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 483b CJK Ideograph Extension A-483B | 483a CJK Ideograph Extension A-483A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 483d CJK Ideograph Extension A-483D | 483c CJK Ideograph Extension A-483C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 483f CJK Ideograph Extension A-483F | 483e CJK Ideograph Extension A-483E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4841 CJK Ideograph Extension A-4841 | 4840 CJK Ideograph Extension A-4840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4843 CJK Ideograph Extension A-4843 | 4842 CJK Ideograph Extension A-4842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4845 CJK Ideograph Extension A-4845 | 4844 CJK Ideograph Extension A-4844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4847 CJK Ideograph Extension A-4847 | 4846 CJK Ideograph Extension A-4846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4849 CJK Ideograph Extension A-4849 | 4848 CJK Ideograph Extension A-4848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 484b CJK Ideograph Extension A-484B | 484a CJK Ideograph Extension A-484A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 484d CJK Ideograph Extension A-484D | 484c CJK Ideograph Extension A-484C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 484f CJK Ideograph Extension A-484F | 484e CJK Ideograph Extension A-484E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4851 CJK Ideograph Extension A-4851 | 4850 CJK Ideograph Extension A-4850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4853 CJK Ideograph Extension A-4853 | 4852 CJK Ideograph Extension A-4852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4855 CJK Ideograph Extension A-4855 | 4854 CJK Ideograph Extension A-4854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4857 CJK Ideograph Extension A-4857 | 4856 CJK Ideograph Extension A-4856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4859 CJK Ideograph Extension A-4859 | 4858 CJK Ideograph Extension A-4858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 485b CJK Ideograph Extension A-485B | 485a CJK Ideograph Extension A-485A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 485d CJK Ideograph Extension A-485D | 485c CJK Ideograph Extension A-485C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 485f CJK Ideograph Extension A-485F | 485e CJK Ideograph Extension A-485E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4861 CJK Ideograph Extension A-4861 | 4860 CJK Ideograph Extension A-4860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4863 CJK Ideograph Extension A-4863 | 4862 CJK Ideograph Extension A-4862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4865 CJK Ideograph Extension A-4865 | 4864 CJK Ideograph Extension A-4864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4867 CJK Ideograph Extension A-4867 | 4866 CJK Ideograph Extension A-4866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4869 CJK Ideograph Extension A-4869 | 4868 CJK Ideograph Extension A-4868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 486b CJK Ideograph Extension A-486B | 486a CJK Ideograph Extension A-486A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 486d CJK Ideograph Extension A-486D | 486c CJK Ideograph Extension A-486C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 486f CJK Ideograph Extension A-486F | 486e CJK Ideograph Extension A-486E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4871 CJK Ideograph Extension A-4871 | 4870 CJK Ideograph Extension A-4870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4873 CJK Ideograph Extension A-4873 | 4872 CJK Ideograph Extension A-4872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4875 CJK Ideograph Extension A-4875 | 4874 CJK Ideograph Extension A-4874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4877 CJK Ideograph Extension A-4877 | 4876 CJK Ideograph Extension A-4876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4879 CJK Ideograph Extension A-4879 | 4878 CJK Ideograph Extension A-4878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 487b CJK Ideograph Extension A-487B | 487a CJK Ideograph Extension A-487A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 487d CJK Ideograph Extension A-487D | 487c CJK Ideograph Extension A-487C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 487f CJK Ideograph Extension A-487F | 487e CJK Ideograph Extension A-487E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4881 CJK Ideograph Extension A-4881 | 4880 CJK Ideograph Extension A-4880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4883 CJK Ideograph Extension A-4883 | 4882 CJK Ideograph Extension A-4882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4885 CJK Ideograph Extension A-4885 | 4884 CJK Ideograph Extension A-4884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4887 CJK Ideograph Extension A-4887 | 4886 CJK Ideograph Extension A-4886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4889 CJK Ideograph Extension A-4889 | 4888 CJK Ideograph Extension A-4888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 488b CJK Ideograph Extension A-488B | 488a CJK Ideograph Extension A-488A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 488d CJK Ideograph Extension A-488D | 488c CJK Ideograph Extension A-488C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 488f CJK Ideograph Extension A-488F | 488e CJK Ideograph Extension A-488E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4891 CJK Ideograph Extension A-4891 | 4890 CJK Ideograph Extension A-4890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4893 CJK Ideograph Extension A-4893 | 4892 CJK Ideograph Extension A-4892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4895 CJK Ideograph Extension A-4895 | 4894 CJK Ideograph Extension A-4894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4897 CJK Ideograph Extension A-4897 | 4896 CJK Ideograph Extension A-4896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4899 CJK Ideograph Extension A-4899 | 4898 CJK Ideograph Extension A-4898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 489b CJK Ideograph Extension A-489B | 489a CJK Ideograph Extension A-489A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 489d CJK Ideograph Extension A-489D | 489c CJK Ideograph Extension A-489C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 489f CJK Ideograph Extension A-489F | 489e CJK Ideograph Extension A-489E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48a1 CJK Ideograph Extension A-48A1 | 48a0 CJK Ideograph Extension A-48A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48a3 CJK Ideograph Extension A-48A3 | 48a2 CJK Ideograph Extension A-48A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48a5 CJK Ideograph Extension A-48A5 | 48a4 CJK Ideograph Extension A-48A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48a7 CJK Ideograph Extension A-48A7 | 48a6 CJK Ideograph Extension A-48A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48a9 CJK Ideograph Extension A-48A9 | 48a8 CJK Ideograph Extension A-48A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48ab CJK Ideograph Extension A-48AB | 48aa CJK Ideograph Extension A-48AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48ad CJK Ideograph Extension A-48AD | 48ac CJK Ideograph Extension A-48AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48af CJK Ideograph Extension A-48AF | 48ae CJK Ideograph Extension A-48AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48b1 CJK Ideograph Extension A-48B1 | 48b0 CJK Ideograph Extension A-48B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48b3 CJK Ideograph Extension A-48B3 | 48b2 CJK Ideograph Extension A-48B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48b5 CJK Ideograph Extension A-48B5 | 48b4 CJK Ideograph Extension A-48B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48b7 CJK Ideograph Extension A-48B7 | 48b6 CJK Ideograph Extension A-48B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48b9 CJK Ideograph Extension A-48B9 | 48b8 CJK Ideograph Extension A-48B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48bb CJK Ideograph Extension A-48BB | 48ba CJK Ideograph Extension A-48BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48bd CJK Ideograph Extension A-48BD | 48bc CJK Ideograph Extension A-48BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48bf CJK Ideograph Extension A-48BF | 48be CJK Ideograph Extension A-48BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48c1 CJK Ideograph Extension A-48C1 | 48c0 CJK Ideograph Extension A-48C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48c3 CJK Ideograph Extension A-48C3 | 48c2 CJK Ideograph Extension A-48C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48c5 CJK Ideograph Extension A-48C5 | 48c4 CJK Ideograph Extension A-48C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48c7 CJK Ideograph Extension A-48C7 | 48c6 CJK Ideograph Extension A-48C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48c9 CJK Ideograph Extension A-48C9 | 48c8 CJK Ideograph Extension A-48C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48cb CJK Ideograph Extension A-48CB | 48ca CJK Ideograph Extension A-48CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48cd CJK Ideograph Extension A-48CD | 48cc CJK Ideograph Extension A-48CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48cf CJK Ideograph Extension A-48CF | 48ce CJK Ideograph Extension A-48CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48d1 CJK Ideograph Extension A-48D1 | 48d0 CJK Ideograph Extension A-48D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48d3 CJK Ideograph Extension A-48D3 | 48d2 CJK Ideograph Extension A-48D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48d5 CJK Ideograph Extension A-48D5 | 48d4 CJK Ideograph Extension A-48D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48d7 CJK Ideograph Extension A-48D7 | 48d6 CJK Ideograph Extension A-48D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48d9 CJK Ideograph Extension A-48D9 | 48d8 CJK Ideograph Extension A-48D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48db CJK Ideograph Extension A-48DB | 48da CJK Ideograph Extension A-48DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48dd CJK Ideograph Extension A-48DD | 48dc CJK Ideograph Extension A-48DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48df CJK Ideograph Extension A-48DF | 48de CJK Ideograph Extension A-48DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48e1 CJK Ideograph Extension A-48E1 | 48e0 CJK Ideograph Extension A-48E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48e3 CJK Ideograph Extension A-48E3 | 48e2 CJK Ideograph Extension A-48E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48e5 CJK Ideograph Extension A-48E5 | 48e4 CJK Ideograph Extension A-48E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48e7 CJK Ideograph Extension A-48E7 | 48e6 CJK Ideograph Extension A-48E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48e9 CJK Ideograph Extension A-48E9 | 48e8 CJK Ideograph Extension A-48E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48eb CJK Ideograph Extension A-48EB | 48ea CJK Ideograph Extension A-48EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48ed CJK Ideograph Extension A-48ED | 48ec CJK Ideograph Extension A-48EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48ef CJK Ideograph Extension A-48EF | 48ee CJK Ideograph Extension A-48EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48f1 CJK Ideograph Extension A-48F1 | 48f0 CJK Ideograph Extension A-48F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48f3 CJK Ideograph Extension A-48F3 | 48f2 CJK Ideograph Extension A-48F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48f5 CJK Ideograph Extension A-48F5 | 48f4 CJK Ideograph Extension A-48F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48f7 CJK Ideograph Extension A-48F7 | 48f6 CJK Ideograph Extension A-48F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48f9 CJK Ideograph Extension A-48F9 | 48f8 CJK Ideograph Extension A-48F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48fb CJK Ideograph Extension A-48FB | 48fa CJK Ideograph Extension A-48FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48fd CJK Ideograph Extension A-48FD | 48fc CJK Ideograph Extension A-48FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48ff CJK Ideograph Extension A-48FF | 48fe CJK Ideograph Extension A-48FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4901 CJK Ideograph Extension A-4901 | 4900 CJK Ideograph Extension A-4900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4903 CJK Ideograph Extension A-4903 | 4902 CJK Ideograph Extension A-4902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4905 CJK Ideograph Extension A-4905 | 4904 CJK Ideograph Extension A-4904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4907 CJK Ideograph Extension A-4907 | 4906 CJK Ideograph Extension A-4906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4909 CJK Ideograph Extension A-4909 | 4908 CJK Ideograph Extension A-4908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 490b CJK Ideograph Extension A-490B | 490a CJK Ideograph Extension A-490A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 490d CJK Ideograph Extension A-490D | 490c CJK Ideograph Extension A-490C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 490f CJK Ideograph Extension A-490F | 490e CJK Ideograph Extension A-490E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4911 CJK Ideograph Extension A-4911 | 4910 CJK Ideograph Extension A-4910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4913 CJK Ideograph Extension A-4913 | 4912 CJK Ideograph Extension A-4912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4915 CJK Ideograph Extension A-4915 | 4914 CJK Ideograph Extension A-4914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4917 CJK Ideograph Extension A-4917 | 4916 CJK Ideograph Extension A-4916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4919 CJK Ideograph Extension A-4919 | 4918 CJK Ideograph Extension A-4918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 491b CJK Ideograph Extension A-491B | 491a CJK Ideograph Extension A-491A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 491d CJK Ideograph Extension A-491D | 491c CJK Ideograph Extension A-491C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 491f CJK Ideograph Extension A-491F | 491e CJK Ideograph Extension A-491E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4921 CJK Ideograph Extension A-4921 | 4920 CJK Ideograph Extension A-4920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4923 CJK Ideograph Extension A-4923 | 4922 CJK Ideograph Extension A-4922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4925 CJK Ideograph Extension A-4925 | 4924 CJK Ideograph Extension A-4924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4927 CJK Ideograph Extension A-4927 | 4926 CJK Ideograph Extension A-4926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4929 CJK Ideograph Extension A-4929 | 4928 CJK Ideograph Extension A-4928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 492b CJK Ideograph Extension A-492B | 492a CJK Ideograph Extension A-492A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 492d CJK Ideograph Extension A-492D | 492c CJK Ideograph Extension A-492C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 492f CJK Ideograph Extension A-492F | 492e CJK Ideograph Extension A-492E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4931 CJK Ideograph Extension A-4931 | 4930 CJK Ideograph Extension A-4930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4933 CJK Ideograph Extension A-4933 | 4932 CJK Ideograph Extension A-4932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4935 CJK Ideograph Extension A-4935 | 4934 CJK Ideograph Extension A-4934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4937 CJK Ideograph Extension A-4937 | 4936 CJK Ideograph Extension A-4936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4939 CJK Ideograph Extension A-4939 | 4938 CJK Ideograph Extension A-4938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 493b CJK Ideograph Extension A-493B | 493a CJK Ideograph Extension A-493A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 493d CJK Ideograph Extension A-493D | 493c CJK Ideograph Extension A-493C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 493f CJK Ideograph Extension A-493F | 493e CJK Ideograph Extension A-493E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4941 CJK Ideograph Extension A-4941 | 4940 CJK Ideograph Extension A-4940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4943 CJK Ideograph Extension A-4943 | 4942 CJK Ideograph Extension A-4942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4945 CJK Ideograph Extension A-4945 | 4944 CJK Ideograph Extension A-4944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4947 CJK Ideograph Extension A-4947 | 4946 CJK Ideograph Extension A-4946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4949 CJK Ideograph Extension A-4949 | 4948 CJK Ideograph Extension A-4948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 494b CJK Ideograph Extension A-494B | 494a CJK Ideograph Extension A-494A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 494d CJK Ideograph Extension A-494D | 494c CJK Ideograph Extension A-494C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 494f CJK Ideograph Extension A-494F | 494e CJK Ideograph Extension A-494E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4951 CJK Ideograph Extension A-4951 | 4950 CJK Ideograph Extension A-4950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4953 CJK Ideograph Extension A-4953 | 4952 CJK Ideograph Extension A-4952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4955 CJK Ideograph Extension A-4955 | 4954 CJK Ideograph Extension A-4954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4957 CJK Ideograph Extension A-4957 | 4956 CJK Ideograph Extension A-4956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4959 CJK Ideograph Extension A-4959 | 4958 CJK Ideograph Extension A-4958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 495b CJK Ideograph Extension A-495B | 495a CJK Ideograph Extension A-495A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 495d CJK Ideograph Extension A-495D | 495c CJK Ideograph Extension A-495C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 495f CJK Ideograph Extension A-495F | 495e CJK Ideograph Extension A-495E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4961 CJK Ideograph Extension A-4961 | 4960 CJK Ideograph Extension A-4960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4963 CJK Ideograph Extension A-4963 | 4962 CJK Ideograph Extension A-4962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4965 CJK Ideograph Extension A-4965 | 4964 CJK Ideograph Extension A-4964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4967 CJK Ideograph Extension A-4967 | 4966 CJK Ideograph Extension A-4966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4969 CJK Ideograph Extension A-4969 | 4968 CJK Ideograph Extension A-4968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 496b CJK Ideograph Extension A-496B | 496a CJK Ideograph Extension A-496A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 496d CJK Ideograph Extension A-496D | 496c CJK Ideograph Extension A-496C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 496f CJK Ideograph Extension A-496F | 496e CJK Ideograph Extension A-496E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4971 CJK Ideograph Extension A-4971 | 4970 CJK Ideograph Extension A-4970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4973 CJK Ideograph Extension A-4973 | 4972 CJK Ideograph Extension A-4972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4975 CJK Ideograph Extension A-4975 | 4974 CJK Ideograph Extension A-4974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4977 CJK Ideograph Extension A-4977 | 4976 CJK Ideograph Extension A-4976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4979 CJK Ideograph Extension A-4979 | 4978 CJK Ideograph Extension A-4978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 497b CJK Ideograph Extension A-497B | 497a CJK Ideograph Extension A-497A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 497d CJK Ideograph Extension A-497D | 497c CJK Ideograph Extension A-497C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 497f CJK Ideograph Extension A-497F | 497e CJK Ideograph Extension A-497E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4981 CJK Ideograph Extension A-4981 | 4980 CJK Ideograph Extension A-4980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4983 CJK Ideograph Extension A-4983 | 4982 CJK Ideograph Extension A-4982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4985 CJK Ideograph Extension A-4985 | 4984 CJK Ideograph Extension A-4984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4987 CJK Ideograph Extension A-4987 | 4986 CJK Ideograph Extension A-4986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4989 CJK Ideograph Extension A-4989 | 4988 CJK Ideograph Extension A-4988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 498b CJK Ideograph Extension A-498B | 498a CJK Ideograph Extension A-498A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 498d CJK Ideograph Extension A-498D | 498c CJK Ideograph Extension A-498C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 498f CJK Ideograph Extension A-498F | 498e CJK Ideograph Extension A-498E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4991 CJK Ideograph Extension A-4991 | 4990 CJK Ideograph Extension A-4990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4993 CJK Ideograph Extension A-4993 | 4992 CJK Ideograph Extension A-4992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4995 CJK Ideograph Extension A-4995 | 4994 CJK Ideograph Extension A-4994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4997 CJK Ideograph Extension A-4997 | 4996 CJK Ideograph Extension A-4996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4999 CJK Ideograph Extension A-4999 | 4998 CJK Ideograph Extension A-4998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 499b CJK Ideograph Extension A-499B | 499a CJK Ideograph Extension A-499A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 499d CJK Ideograph Extension A-499D | 499c CJK Ideograph Extension A-499C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 499f CJK Ideograph Extension A-499F | 499e CJK Ideograph Extension A-499E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49a1 CJK Ideograph Extension A-49A1 | 49a0 CJK Ideograph Extension A-49A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49a3 CJK Ideograph Extension A-49A3 | 49a2 CJK Ideograph Extension A-49A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49a5 CJK Ideograph Extension A-49A5 | 49a4 CJK Ideograph Extension A-49A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49a7 CJK Ideograph Extension A-49A7 | 49a6 CJK Ideograph Extension A-49A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49a9 CJK Ideograph Extension A-49A9 | 49a8 CJK Ideograph Extension A-49A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49ab CJK Ideograph Extension A-49AB | 49aa CJK Ideograph Extension A-49AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49ad CJK Ideograph Extension A-49AD | 49ac CJK Ideograph Extension A-49AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49af CJK Ideograph Extension A-49AF | 49ae CJK Ideograph Extension A-49AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49b1 CJK Ideograph Extension A-49B1 | 49b0 CJK Ideograph Extension A-49B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49b3 CJK Ideograph Extension A-49B3 | 49b2 CJK Ideograph Extension A-49B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49b5 CJK Ideograph Extension A-49B5 | 49b4 CJK Ideograph Extension A-49B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49b7 CJK Ideograph Extension A-49B7 | 49b6 CJK Ideograph Extension A-49B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49b9 CJK Ideograph Extension A-49B9 | 49b8 CJK Ideograph Extension A-49B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49bb CJK Ideograph Extension A-49BB | 49ba CJK Ideograph Extension A-49BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49bd CJK Ideograph Extension A-49BD | 49bc CJK Ideograph Extension A-49BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49bf CJK Ideograph Extension A-49BF | 49be CJK Ideograph Extension A-49BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49c1 CJK Ideograph Extension A-49C1 | 49c0 CJK Ideograph Extension A-49C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49c3 CJK Ideograph Extension A-49C3 | 49c2 CJK Ideograph Extension A-49C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49c5 CJK Ideograph Extension A-49C5 | 49c4 CJK Ideograph Extension A-49C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49c7 CJK Ideograph Extension A-49C7 | 49c6 CJK Ideograph Extension A-49C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49c9 CJK Ideograph Extension A-49C9 | 49c8 CJK Ideograph Extension A-49C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49cb CJK Ideograph Extension A-49CB | 49ca CJK Ideograph Extension A-49CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49cd CJK Ideograph Extension A-49CD | 49cc CJK Ideograph Extension A-49CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49cf CJK Ideograph Extension A-49CF | 49ce CJK Ideograph Extension A-49CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49d1 CJK Ideograph Extension A-49D1 | 49d0 CJK Ideograph Extension A-49D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49d3 CJK Ideograph Extension A-49D3 | 49d2 CJK Ideograph Extension A-49D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49d5 CJK Ideograph Extension A-49D5 | 49d4 CJK Ideograph Extension A-49D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49d7 CJK Ideograph Extension A-49D7 | 49d6 CJK Ideograph Extension A-49D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49d9 CJK Ideograph Extension A-49D9 | 49d8 CJK Ideograph Extension A-49D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49db CJK Ideograph Extension A-49DB | 49da CJK Ideograph Extension A-49DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49dd CJK Ideograph Extension A-49DD | 49dc CJK Ideograph Extension A-49DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49df CJK Ideograph Extension A-49DF | 49de CJK Ideograph Extension A-49DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49e1 CJK Ideograph Extension A-49E1 | 49e0 CJK Ideograph Extension A-49E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49e3 CJK Ideograph Extension A-49E3 | 49e2 CJK Ideograph Extension A-49E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49e5 CJK Ideograph Extension A-49E5 | 49e4 CJK Ideograph Extension A-49E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49e7 CJK Ideograph Extension A-49E7 | 49e6 CJK Ideograph Extension A-49E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49e9 CJK Ideograph Extension A-49E9 | 49e8 CJK Ideograph Extension A-49E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49eb CJK Ideograph Extension A-49EB | 49ea CJK Ideograph Extension A-49EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49ed CJK Ideograph Extension A-49ED | 49ec CJK Ideograph Extension A-49EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49ef CJK Ideograph Extension A-49EF | 49ee CJK Ideograph Extension A-49EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49f1 CJK Ideograph Extension A-49F1 | 49f0 CJK Ideograph Extension A-49F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49f3 CJK Ideograph Extension A-49F3 | 49f2 CJK Ideograph Extension A-49F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49f5 CJK Ideograph Extension A-49F5 | 49f4 CJK Ideograph Extension A-49F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49f7 CJK Ideograph Extension A-49F7 | 49f6 CJK Ideograph Extension A-49F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49f9 CJK Ideograph Extension A-49F9 | 49f8 CJK Ideograph Extension A-49F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49fb CJK Ideograph Extension A-49FB | 49fa CJK Ideograph Extension A-49FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49fd CJK Ideograph Extension A-49FD | 49fc CJK Ideograph Extension A-49FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49ff CJK Ideograph Extension A-49FF | 49fe CJK Ideograph Extension A-49FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a01 CJK Ideograph Extension A-4A01 | 4a00 CJK Ideograph Extension A-4A00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a03 CJK Ideograph Extension A-4A03 | 4a02 CJK Ideograph Extension A-4A02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a05 CJK Ideograph Extension A-4A05 | 4a04 CJK Ideograph Extension A-4A04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a07 CJK Ideograph Extension A-4A07 | 4a06 CJK Ideograph Extension A-4A06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a09 CJK Ideograph Extension A-4A09 | 4a08 CJK Ideograph Extension A-4A08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a0b CJK Ideograph Extension A-4A0B | 4a0a CJK Ideograph Extension A-4A0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a0d CJK Ideograph Extension A-4A0D | 4a0c CJK Ideograph Extension A-4A0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a0f CJK Ideograph Extension A-4A0F | 4a0e CJK Ideograph Extension A-4A0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a11 CJK Ideograph Extension A-4A11 | 4a10 CJK Ideograph Extension A-4A10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a13 CJK Ideograph Extension A-4A13 | 4a12 CJK Ideograph Extension A-4A12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a15 CJK Ideograph Extension A-4A15 | 4a14 CJK Ideograph Extension A-4A14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a17 CJK Ideograph Extension A-4A17 | 4a16 CJK Ideograph Extension A-4A16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a19 CJK Ideograph Extension A-4A19 | 4a18 CJK Ideograph Extension A-4A18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a1b CJK Ideograph Extension A-4A1B | 4a1a CJK Ideograph Extension A-4A1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a1d CJK Ideograph Extension A-4A1D | 4a1c CJK Ideograph Extension A-4A1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a1f CJK Ideograph Extension A-4A1F | 4a1e CJK Ideograph Extension A-4A1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a21 CJK Ideograph Extension A-4A21 | 4a20 CJK Ideograph Extension A-4A20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a23 CJK Ideograph Extension A-4A23 | 4a22 CJK Ideograph Extension A-4A22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a25 CJK Ideograph Extension A-4A25 | 4a24 CJK Ideograph Extension A-4A24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a27 CJK Ideograph Extension A-4A27 | 4a26 CJK Ideograph Extension A-4A26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a29 CJK Ideograph Extension A-4A29 | 4a28 CJK Ideograph Extension A-4A28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a2b CJK Ideograph Extension A-4A2B | 4a2a CJK Ideograph Extension A-4A2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a2d CJK Ideograph Extension A-4A2D | 4a2c CJK Ideograph Extension A-4A2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a2f CJK Ideograph Extension A-4A2F | 4a2e CJK Ideograph Extension A-4A2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a31 CJK Ideograph Extension A-4A31 | 4a30 CJK Ideograph Extension A-4A30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a33 CJK Ideograph Extension A-4A33 | 4a32 CJK Ideograph Extension A-4A32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a35 CJK Ideograph Extension A-4A35 | 4a34 CJK Ideograph Extension A-4A34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a37 CJK Ideograph Extension A-4A37 | 4a36 CJK Ideograph Extension A-4A36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a39 CJK Ideograph Extension A-4A39 | 4a38 CJK Ideograph Extension A-4A38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a3b CJK Ideograph Extension A-4A3B | 4a3a CJK Ideograph Extension A-4A3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a3d CJK Ideograph Extension A-4A3D | 4a3c CJK Ideograph Extension A-4A3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a3f CJK Ideograph Extension A-4A3F | 4a3e CJK Ideograph Extension A-4A3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a41 CJK Ideograph Extension A-4A41 | 4a40 CJK Ideograph Extension A-4A40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a43 CJK Ideograph Extension A-4A43 | 4a42 CJK Ideograph Extension A-4A42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a45 CJK Ideograph Extension A-4A45 | 4a44 CJK Ideograph Extension A-4A44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a47 CJK Ideograph Extension A-4A47 | 4a46 CJK Ideograph Extension A-4A46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a49 CJK Ideograph Extension A-4A49 | 4a48 CJK Ideograph Extension A-4A48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a4b CJK Ideograph Extension A-4A4B | 4a4a CJK Ideograph Extension A-4A4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a4d CJK Ideograph Extension A-4A4D | 4a4c CJK Ideograph Extension A-4A4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a4f CJK Ideograph Extension A-4A4F | 4a4e CJK Ideograph Extension A-4A4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a51 CJK Ideograph Extension A-4A51 | 4a50 CJK Ideograph Extension A-4A50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a53 CJK Ideograph Extension A-4A53 | 4a52 CJK Ideograph Extension A-4A52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a55 CJK Ideograph Extension A-4A55 | 4a54 CJK Ideograph Extension A-4A54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a57 CJK Ideograph Extension A-4A57 | 4a56 CJK Ideograph Extension A-4A56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a59 CJK Ideograph Extension A-4A59 | 4a58 CJK Ideograph Extension A-4A58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a5b CJK Ideograph Extension A-4A5B | 4a5a CJK Ideograph Extension A-4A5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a5d CJK Ideograph Extension A-4A5D | 4a5c CJK Ideograph Extension A-4A5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a5f CJK Ideograph Extension A-4A5F | 4a5e CJK Ideograph Extension A-4A5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a61 CJK Ideograph Extension A-4A61 | 4a60 CJK Ideograph Extension A-4A60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a63 CJK Ideograph Extension A-4A63 | 4a62 CJK Ideograph Extension A-4A62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a65 CJK Ideograph Extension A-4A65 | 4a64 CJK Ideograph Extension A-4A64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a67 CJK Ideograph Extension A-4A67 | 4a66 CJK Ideograph Extension A-4A66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a69 CJK Ideograph Extension A-4A69 | 4a68 CJK Ideograph Extension A-4A68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a6b CJK Ideograph Extension A-4A6B | 4a6a CJK Ideograph Extension A-4A6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a6d CJK Ideograph Extension A-4A6D | 4a6c CJK Ideograph Extension A-4A6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a6f CJK Ideograph Extension A-4A6F | 4a6e CJK Ideograph Extension A-4A6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a71 CJK Ideograph Extension A-4A71 | 4a70 CJK Ideograph Extension A-4A70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a73 CJK Ideograph Extension A-4A73 | 4a72 CJK Ideograph Extension A-4A72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a75 CJK Ideograph Extension A-4A75 | 4a74 CJK Ideograph Extension A-4A74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a77 CJK Ideograph Extension A-4A77 | 4a76 CJK Ideograph Extension A-4A76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a79 CJK Ideograph Extension A-4A79 | 4a78 CJK Ideograph Extension A-4A78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a7b CJK Ideograph Extension A-4A7B | 4a7a CJK Ideograph Extension A-4A7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a7d CJK Ideograph Extension A-4A7D | 4a7c CJK Ideograph Extension A-4A7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a7f CJK Ideograph Extension A-4A7F | 4a7e CJK Ideograph Extension A-4A7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a81 CJK Ideograph Extension A-4A81 | 4a80 CJK Ideograph Extension A-4A80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a83 CJK Ideograph Extension A-4A83 | 4a82 CJK Ideograph Extension A-4A82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a85 CJK Ideograph Extension A-4A85 | 4a84 CJK Ideograph Extension A-4A84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a87 CJK Ideograph Extension A-4A87 | 4a86 CJK Ideograph Extension A-4A86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a89 CJK Ideograph Extension A-4A89 | 4a88 CJK Ideograph Extension A-4A88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a8b CJK Ideograph Extension A-4A8B | 4a8a CJK Ideograph Extension A-4A8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a8d CJK Ideograph Extension A-4A8D | 4a8c CJK Ideograph Extension A-4A8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a8f CJK Ideograph Extension A-4A8F | 4a8e CJK Ideograph Extension A-4A8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a91 CJK Ideograph Extension A-4A91 | 4a90 CJK Ideograph Extension A-4A90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a93 CJK Ideograph Extension A-4A93 | 4a92 CJK Ideograph Extension A-4A92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a95 CJK Ideograph Extension A-4A95 | 4a94 CJK Ideograph Extension A-4A94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a97 CJK Ideograph Extension A-4A97 | 4a96 CJK Ideograph Extension A-4A96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a99 CJK Ideograph Extension A-4A99 | 4a98 CJK Ideograph Extension A-4A98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a9b CJK Ideograph Extension A-4A9B | 4a9a CJK Ideograph Extension A-4A9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a9d CJK Ideograph Extension A-4A9D | 4a9c CJK Ideograph Extension A-4A9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a9f CJK Ideograph Extension A-4A9F | 4a9e CJK Ideograph Extension A-4A9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aa1 CJK Ideograph Extension A-4AA1 | 4aa0 CJK Ideograph Extension A-4AA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aa3 CJK Ideograph Extension A-4AA3 | 4aa2 CJK Ideograph Extension A-4AA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aa5 CJK Ideograph Extension A-4AA5 | 4aa4 CJK Ideograph Extension A-4AA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aa7 CJK Ideograph Extension A-4AA7 | 4aa6 CJK Ideograph Extension A-4AA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aa9 CJK Ideograph Extension A-4AA9 | 4aa8 CJK Ideograph Extension A-4AA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aab CJK Ideograph Extension A-4AAB | 4aaa CJK Ideograph Extension A-4AAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aad CJK Ideograph Extension A-4AAD | 4aac CJK Ideograph Extension A-4AAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aaf CJK Ideograph Extension A-4AAF | 4aae CJK Ideograph Extension A-4AAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ab1 CJK Ideograph Extension A-4AB1 | 4ab0 CJK Ideograph Extension A-4AB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ab3 CJK Ideograph Extension A-4AB3 | 4ab2 CJK Ideograph Extension A-4AB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ab5 CJK Ideograph Extension A-4AB5 | 4ab4 CJK Ideograph Extension A-4AB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ab7 CJK Ideograph Extension A-4AB7 | 4ab6 CJK Ideograph Extension A-4AB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ab9 CJK Ideograph Extension A-4AB9 | 4ab8 CJK Ideograph Extension A-4AB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4abb CJK Ideograph Extension A-4ABB | 4aba CJK Ideograph Extension A-4ABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4abd CJK Ideograph Extension A-4ABD | 4abc CJK Ideograph Extension A-4ABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4abf CJK Ideograph Extension A-4ABF | 4abe CJK Ideograph Extension A-4ABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ac1 CJK Ideograph Extension A-4AC1 | 4ac0 CJK Ideograph Extension A-4AC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ac3 CJK Ideograph Extension A-4AC3 | 4ac2 CJK Ideograph Extension A-4AC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ac5 CJK Ideograph Extension A-4AC5 | 4ac4 CJK Ideograph Extension A-4AC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ac7 CJK Ideograph Extension A-4AC7 | 4ac6 CJK Ideograph Extension A-4AC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ac9 CJK Ideograph Extension A-4AC9 | 4ac8 CJK Ideograph Extension A-4AC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4acb CJK Ideograph Extension A-4ACB | 4aca CJK Ideograph Extension A-4ACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4acd CJK Ideograph Extension A-4ACD | 4acc CJK Ideograph Extension A-4ACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4acf CJK Ideograph Extension A-4ACF | 4ace CJK Ideograph Extension A-4ACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ad1 CJK Ideograph Extension A-4AD1 | 4ad0 CJK Ideograph Extension A-4AD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ad3 CJK Ideograph Extension A-4AD3 | 4ad2 CJK Ideograph Extension A-4AD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ad5 CJK Ideograph Extension A-4AD5 | 4ad4 CJK Ideograph Extension A-4AD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ad7 CJK Ideograph Extension A-4AD7 | 4ad6 CJK Ideograph Extension A-4AD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ad9 CJK Ideograph Extension A-4AD9 | 4ad8 CJK Ideograph Extension A-4AD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4adb CJK Ideograph Extension A-4ADB | 4ada CJK Ideograph Extension A-4ADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4add CJK Ideograph Extension A-4ADD | 4adc CJK Ideograph Extension A-4ADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4adf CJK Ideograph Extension A-4ADF | 4ade CJK Ideograph Extension A-4ADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ae1 CJK Ideograph Extension A-4AE1 | 4ae0 CJK Ideograph Extension A-4AE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ae3 CJK Ideograph Extension A-4AE3 | 4ae2 CJK Ideograph Extension A-4AE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ae5 CJK Ideograph Extension A-4AE5 | 4ae4 CJK Ideograph Extension A-4AE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ae7 CJK Ideograph Extension A-4AE7 | 4ae6 CJK Ideograph Extension A-4AE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ae9 CJK Ideograph Extension A-4AE9 | 4ae8 CJK Ideograph Extension A-4AE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aeb CJK Ideograph Extension A-4AEB | 4aea CJK Ideograph Extension A-4AEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aed CJK Ideograph Extension A-4AED | 4aec CJK Ideograph Extension A-4AEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aef CJK Ideograph Extension A-4AEF | 4aee CJK Ideograph Extension A-4AEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4af1 CJK Ideograph Extension A-4AF1 | 4af0 CJK Ideograph Extension A-4AF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4af3 CJK Ideograph Extension A-4AF3 | 4af2 CJK Ideograph Extension A-4AF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4af5 CJK Ideograph Extension A-4AF5 | 4af4 CJK Ideograph Extension A-4AF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4af7 CJK Ideograph Extension A-4AF7 | 4af6 CJK Ideograph Extension A-4AF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4af9 CJK Ideograph Extension A-4AF9 | 4af8 CJK Ideograph Extension A-4AF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4afb CJK Ideograph Extension A-4AFB | 4afa CJK Ideograph Extension A-4AFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4afd CJK Ideograph Extension A-4AFD | 4afc CJK Ideograph Extension A-4AFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aff CJK Ideograph Extension A-4AFF | 4afe CJK Ideograph Extension A-4AFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b01 CJK Ideograph Extension A-4B01 | 4b00 CJK Ideograph Extension A-4B00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b03 CJK Ideograph Extension A-4B03 | 4b02 CJK Ideograph Extension A-4B02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b05 CJK Ideograph Extension A-4B05 | 4b04 CJK Ideograph Extension A-4B04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b07 CJK Ideograph Extension A-4B07 | 4b06 CJK Ideograph Extension A-4B06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b09 CJK Ideograph Extension A-4B09 | 4b08 CJK Ideograph Extension A-4B08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b0b CJK Ideograph Extension A-4B0B | 4b0a CJK Ideograph Extension A-4B0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b0d CJK Ideograph Extension A-4B0D | 4b0c CJK Ideograph Extension A-4B0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b0f CJK Ideograph Extension A-4B0F | 4b0e CJK Ideograph Extension A-4B0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b11 CJK Ideograph Extension A-4B11 | 4b10 CJK Ideograph Extension A-4B10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b13 CJK Ideograph Extension A-4B13 | 4b12 CJK Ideograph Extension A-4B12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b15 CJK Ideograph Extension A-4B15 | 4b14 CJK Ideograph Extension A-4B14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b17 CJK Ideograph Extension A-4B17 | 4b16 CJK Ideograph Extension A-4B16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b19 CJK Ideograph Extension A-4B19 | 4b18 CJK Ideograph Extension A-4B18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b1b CJK Ideograph Extension A-4B1B | 4b1a CJK Ideograph Extension A-4B1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b1d CJK Ideograph Extension A-4B1D | 4b1c CJK Ideograph Extension A-4B1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b1f CJK Ideograph Extension A-4B1F | 4b1e CJK Ideograph Extension A-4B1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b21 CJK Ideograph Extension A-4B21 | 4b20 CJK Ideograph Extension A-4B20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b23 CJK Ideograph Extension A-4B23 | 4b22 CJK Ideograph Extension A-4B22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b25 CJK Ideograph Extension A-4B25 | 4b24 CJK Ideograph Extension A-4B24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b27 CJK Ideograph Extension A-4B27 | 4b26 CJK Ideograph Extension A-4B26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b29 CJK Ideograph Extension A-4B29 | 4b28 CJK Ideograph Extension A-4B28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b2b CJK Ideograph Extension A-4B2B | 4b2a CJK Ideograph Extension A-4B2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b2d CJK Ideograph Extension A-4B2D | 4b2c CJK Ideograph Extension A-4B2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b2f CJK Ideograph Extension A-4B2F | 4b2e CJK Ideograph Extension A-4B2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b31 CJK Ideograph Extension A-4B31 | 4b30 CJK Ideograph Extension A-4B30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b33 CJK Ideograph Extension A-4B33 | 4b32 CJK Ideograph Extension A-4B32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b35 CJK Ideograph Extension A-4B35 | 4b34 CJK Ideograph Extension A-4B34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b37 CJK Ideograph Extension A-4B37 | 4b36 CJK Ideograph Extension A-4B36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b39 CJK Ideograph Extension A-4B39 | 4b38 CJK Ideograph Extension A-4B38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b3b CJK Ideograph Extension A-4B3B | 4b3a CJK Ideograph Extension A-4B3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b3d CJK Ideograph Extension A-4B3D | 4b3c CJK Ideograph Extension A-4B3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b3f CJK Ideograph Extension A-4B3F | 4b3e CJK Ideograph Extension A-4B3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b41 CJK Ideograph Extension A-4B41 | 4b40 CJK Ideograph Extension A-4B40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b43 CJK Ideograph Extension A-4B43 | 4b42 CJK Ideograph Extension A-4B42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b45 CJK Ideograph Extension A-4B45 | 4b44 CJK Ideograph Extension A-4B44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b47 CJK Ideograph Extension A-4B47 | 4b46 CJK Ideograph Extension A-4B46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b49 CJK Ideograph Extension A-4B49 | 4b48 CJK Ideograph Extension A-4B48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b4b CJK Ideograph Extension A-4B4B | 4b4a CJK Ideograph Extension A-4B4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b4d CJK Ideograph Extension A-4B4D | 4b4c CJK Ideograph Extension A-4B4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b4f CJK Ideograph Extension A-4B4F | 4b4e CJK Ideograph Extension A-4B4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b51 CJK Ideograph Extension A-4B51 | 4b50 CJK Ideograph Extension A-4B50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b53 CJK Ideograph Extension A-4B53 | 4b52 CJK Ideograph Extension A-4B52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b55 CJK Ideograph Extension A-4B55 | 4b54 CJK Ideograph Extension A-4B54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b57 CJK Ideograph Extension A-4B57 | 4b56 CJK Ideograph Extension A-4B56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b59 CJK Ideograph Extension A-4B59 | 4b58 CJK Ideograph Extension A-4B58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b5b CJK Ideograph Extension A-4B5B | 4b5a CJK Ideograph Extension A-4B5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b5d CJK Ideograph Extension A-4B5D | 4b5c CJK Ideograph Extension A-4B5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b5f CJK Ideograph Extension A-4B5F | 4b5e CJK Ideograph Extension A-4B5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b61 CJK Ideograph Extension A-4B61 | 4b60 CJK Ideograph Extension A-4B60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b63 CJK Ideograph Extension A-4B63 | 4b62 CJK Ideograph Extension A-4B62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b65 CJK Ideograph Extension A-4B65 | 4b64 CJK Ideograph Extension A-4B64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b67 CJK Ideograph Extension A-4B67 | 4b66 CJK Ideograph Extension A-4B66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b69 CJK Ideograph Extension A-4B69 | 4b68 CJK Ideograph Extension A-4B68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b6b CJK Ideograph Extension A-4B6B | 4b6a CJK Ideograph Extension A-4B6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b6d CJK Ideograph Extension A-4B6D | 4b6c CJK Ideograph Extension A-4B6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b6f CJK Ideograph Extension A-4B6F | 4b6e CJK Ideograph Extension A-4B6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b71 CJK Ideograph Extension A-4B71 | 4b70 CJK Ideograph Extension A-4B70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b73 CJK Ideograph Extension A-4B73 | 4b72 CJK Ideograph Extension A-4B72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b75 CJK Ideograph Extension A-4B75 | 4b74 CJK Ideograph Extension A-4B74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b77 CJK Ideograph Extension A-4B77 | 4b76 CJK Ideograph Extension A-4B76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b79 CJK Ideograph Extension A-4B79 | 4b78 CJK Ideograph Extension A-4B78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b7b CJK Ideograph Extension A-4B7B | 4b7a CJK Ideograph Extension A-4B7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b7d CJK Ideograph Extension A-4B7D | 4b7c CJK Ideograph Extension A-4B7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b7f CJK Ideograph Extension A-4B7F | 4b7e CJK Ideograph Extension A-4B7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b81 CJK Ideograph Extension A-4B81 | 4b80 CJK Ideograph Extension A-4B80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b83 CJK Ideograph Extension A-4B83 | 4b82 CJK Ideograph Extension A-4B82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b85 CJK Ideograph Extension A-4B85 | 4b84 CJK Ideograph Extension A-4B84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b87 CJK Ideograph Extension A-4B87 | 4b86 CJK Ideograph Extension A-4B86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b89 CJK Ideograph Extension A-4B89 | 4b88 CJK Ideograph Extension A-4B88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b8b CJK Ideograph Extension A-4B8B | 4b8a CJK Ideograph Extension A-4B8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b8d CJK Ideograph Extension A-4B8D | 4b8c CJK Ideograph Extension A-4B8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b8f CJK Ideograph Extension A-4B8F | 4b8e CJK Ideograph Extension A-4B8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b91 CJK Ideograph Extension A-4B91 | 4b90 CJK Ideograph Extension A-4B90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b93 CJK Ideograph Extension A-4B93 | 4b92 CJK Ideograph Extension A-4B92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b95 CJK Ideograph Extension A-4B95 | 4b94 CJK Ideograph Extension A-4B94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b97 CJK Ideograph Extension A-4B97 | 4b96 CJK Ideograph Extension A-4B96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b99 CJK Ideograph Extension A-4B99 | 4b98 CJK Ideograph Extension A-4B98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b9b CJK Ideograph Extension A-4B9B | 4b9a CJK Ideograph Extension A-4B9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b9d CJK Ideograph Extension A-4B9D | 4b9c CJK Ideograph Extension A-4B9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b9f CJK Ideograph Extension A-4B9F | 4b9e CJK Ideograph Extension A-4B9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ba1 CJK Ideograph Extension A-4BA1 | 4ba0 CJK Ideograph Extension A-4BA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ba3 CJK Ideograph Extension A-4BA3 | 4ba2 CJK Ideograph Extension A-4BA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ba5 CJK Ideograph Extension A-4BA5 | 4ba4 CJK Ideograph Extension A-4BA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ba7 CJK Ideograph Extension A-4BA7 | 4ba6 CJK Ideograph Extension A-4BA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ba9 CJK Ideograph Extension A-4BA9 | 4ba8 CJK Ideograph Extension A-4BA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bab CJK Ideograph Extension A-4BAB | 4baa CJK Ideograph Extension A-4BAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bad CJK Ideograph Extension A-4BAD | 4bac CJK Ideograph Extension A-4BAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4baf CJK Ideograph Extension A-4BAF | 4bae CJK Ideograph Extension A-4BAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bb1 CJK Ideograph Extension A-4BB1 | 4bb0 CJK Ideograph Extension A-4BB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bb3 CJK Ideograph Extension A-4BB3 | 4bb2 CJK Ideograph Extension A-4BB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bb5 CJK Ideograph Extension A-4BB5 | 4bb4 CJK Ideograph Extension A-4BB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bb7 CJK Ideograph Extension A-4BB7 | 4bb6 CJK Ideograph Extension A-4BB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bb9 CJK Ideograph Extension A-4BB9 | 4bb8 CJK Ideograph Extension A-4BB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bbb CJK Ideograph Extension A-4BBB | 4bba CJK Ideograph Extension A-4BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bbd CJK Ideograph Extension A-4BBD | 4bbc CJK Ideograph Extension A-4BBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bbf CJK Ideograph Extension A-4BBF | 4bbe CJK Ideograph Extension A-4BBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bc1 CJK Ideograph Extension A-4BC1 | 4bc0 CJK Ideograph Extension A-4BC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bc3 CJK Ideograph Extension A-4BC3 | 4bc2 CJK Ideograph Extension A-4BC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bc5 CJK Ideograph Extension A-4BC5 | 4bc4 CJK Ideograph Extension A-4BC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bc7 CJK Ideograph Extension A-4BC7 | 4bc6 CJK Ideograph Extension A-4BC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bc9 CJK Ideograph Extension A-4BC9 | 4bc8 CJK Ideograph Extension A-4BC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bcb CJK Ideograph Extension A-4BCB | 4bca CJK Ideograph Extension A-4BCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bcd CJK Ideograph Extension A-4BCD | 4bcc CJK Ideograph Extension A-4BCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bcf CJK Ideograph Extension A-4BCF | 4bce CJK Ideograph Extension A-4BCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bd1 CJK Ideograph Extension A-4BD1 | 4bd0 CJK Ideograph Extension A-4BD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bd3 CJK Ideograph Extension A-4BD3 | 4bd2 CJK Ideograph Extension A-4BD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bd5 CJK Ideograph Extension A-4BD5 | 4bd4 CJK Ideograph Extension A-4BD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bd7 CJK Ideograph Extension A-4BD7 | 4bd6 CJK Ideograph Extension A-4BD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bd9 CJK Ideograph Extension A-4BD9 | 4bd8 CJK Ideograph Extension A-4BD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bdb CJK Ideograph Extension A-4BDB | 4bda CJK Ideograph Extension A-4BDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bdd CJK Ideograph Extension A-4BDD | 4bdc CJK Ideograph Extension A-4BDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bdf CJK Ideograph Extension A-4BDF | 4bde CJK Ideograph Extension A-4BDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4be1 CJK Ideograph Extension A-4BE1 | 4be0 CJK Ideograph Extension A-4BE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4be3 CJK Ideograph Extension A-4BE3 | 4be2 CJK Ideograph Extension A-4BE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4be5 CJK Ideograph Extension A-4BE5 | 4be4 CJK Ideograph Extension A-4BE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4be7 CJK Ideograph Extension A-4BE7 | 4be6 CJK Ideograph Extension A-4BE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4be9 CJK Ideograph Extension A-4BE9 | 4be8 CJK Ideograph Extension A-4BE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4beb CJK Ideograph Extension A-4BEB | 4bea CJK Ideograph Extension A-4BEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bed CJK Ideograph Extension A-4BED | 4bec CJK Ideograph Extension A-4BEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bef CJK Ideograph Extension A-4BEF | 4bee CJK Ideograph Extension A-4BEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bf1 CJK Ideograph Extension A-4BF1 | 4bf0 CJK Ideograph Extension A-4BF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bf3 CJK Ideograph Extension A-4BF3 | 4bf2 CJK Ideograph Extension A-4BF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bf5 CJK Ideograph Extension A-4BF5 | 4bf4 CJK Ideograph Extension A-4BF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bf7 CJK Ideograph Extension A-4BF7 | 4bf6 CJK Ideograph Extension A-4BF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bf9 CJK Ideograph Extension A-4BF9 | 4bf8 CJK Ideograph Extension A-4BF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bfb CJK Ideograph Extension A-4BFB | 4bfa CJK Ideograph Extension A-4BFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bfd CJK Ideograph Extension A-4BFD | 4bfc CJK Ideograph Extension A-4BFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bff CJK Ideograph Extension A-4BFF | 4bfe CJK Ideograph Extension A-4BFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c01 CJK Ideograph Extension A-4C01 | 4c00 CJK Ideograph Extension A-4C00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c03 CJK Ideograph Extension A-4C03 | 4c02 CJK Ideograph Extension A-4C02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c05 CJK Ideograph Extension A-4C05 | 4c04 CJK Ideograph Extension A-4C04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c07 CJK Ideograph Extension A-4C07 | 4c06 CJK Ideograph Extension A-4C06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c09 CJK Ideograph Extension A-4C09 | 4c08 CJK Ideograph Extension A-4C08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c0b CJK Ideograph Extension A-4C0B | 4c0a CJK Ideograph Extension A-4C0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c0d CJK Ideograph Extension A-4C0D | 4c0c CJK Ideograph Extension A-4C0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c0f CJK Ideograph Extension A-4C0F | 4c0e CJK Ideograph Extension A-4C0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c11 CJK Ideograph Extension A-4C11 | 4c10 CJK Ideograph Extension A-4C10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c13 CJK Ideograph Extension A-4C13 | 4c12 CJK Ideograph Extension A-4C12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c15 CJK Ideograph Extension A-4C15 | 4c14 CJK Ideograph Extension A-4C14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c17 CJK Ideograph Extension A-4C17 | 4c16 CJK Ideograph Extension A-4C16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c19 CJK Ideograph Extension A-4C19 | 4c18 CJK Ideograph Extension A-4C18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c1b CJK Ideograph Extension A-4C1B | 4c1a CJK Ideograph Extension A-4C1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c1d CJK Ideograph Extension A-4C1D | 4c1c CJK Ideograph Extension A-4C1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c1f CJK Ideograph Extension A-4C1F | 4c1e CJK Ideograph Extension A-4C1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c21 CJK Ideograph Extension A-4C21 | 4c20 CJK Ideograph Extension A-4C20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c23 CJK Ideograph Extension A-4C23 | 4c22 CJK Ideograph Extension A-4C22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c25 CJK Ideograph Extension A-4C25 | 4c24 CJK Ideograph Extension A-4C24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c27 CJK Ideograph Extension A-4C27 | 4c26 CJK Ideograph Extension A-4C26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c29 CJK Ideograph Extension A-4C29 | 4c28 CJK Ideograph Extension A-4C28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c2b CJK Ideograph Extension A-4C2B | 4c2a CJK Ideograph Extension A-4C2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c2d CJK Ideograph Extension A-4C2D | 4c2c CJK Ideograph Extension A-4C2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c2f CJK Ideograph Extension A-4C2F | 4c2e CJK Ideograph Extension A-4C2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c31 CJK Ideograph Extension A-4C31 | 4c30 CJK Ideograph Extension A-4C30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c33 CJK Ideograph Extension A-4C33 | 4c32 CJK Ideograph Extension A-4C32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c35 CJK Ideograph Extension A-4C35 | 4c34 CJK Ideograph Extension A-4C34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c37 CJK Ideograph Extension A-4C37 | 4c36 CJK Ideograph Extension A-4C36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c39 CJK Ideograph Extension A-4C39 | 4c38 CJK Ideograph Extension A-4C38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c3b CJK Ideograph Extension A-4C3B | 4c3a CJK Ideograph Extension A-4C3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c3d CJK Ideograph Extension A-4C3D | 4c3c CJK Ideograph Extension A-4C3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c3f CJK Ideograph Extension A-4C3F | 4c3e CJK Ideograph Extension A-4C3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c41 CJK Ideograph Extension A-4C41 | 4c40 CJK Ideograph Extension A-4C40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c43 CJK Ideograph Extension A-4C43 | 4c42 CJK Ideograph Extension A-4C42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c45 CJK Ideograph Extension A-4C45 | 4c44 CJK Ideograph Extension A-4C44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c47 CJK Ideograph Extension A-4C47 | 4c46 CJK Ideograph Extension A-4C46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c49 CJK Ideograph Extension A-4C49 | 4c48 CJK Ideograph Extension A-4C48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c4b CJK Ideograph Extension A-4C4B | 4c4a CJK Ideograph Extension A-4C4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c4d CJK Ideograph Extension A-4C4D | 4c4c CJK Ideograph Extension A-4C4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c4f CJK Ideograph Extension A-4C4F | 4c4e CJK Ideograph Extension A-4C4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c51 CJK Ideograph Extension A-4C51 | 4c50 CJK Ideograph Extension A-4C50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c53 CJK Ideograph Extension A-4C53 | 4c52 CJK Ideograph Extension A-4C52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c55 CJK Ideograph Extension A-4C55 | 4c54 CJK Ideograph Extension A-4C54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c57 CJK Ideograph Extension A-4C57 | 4c56 CJK Ideograph Extension A-4C56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c59 CJK Ideograph Extension A-4C59 | 4c58 CJK Ideograph Extension A-4C58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c5b CJK Ideograph Extension A-4C5B | 4c5a CJK Ideograph Extension A-4C5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c5d CJK Ideograph Extension A-4C5D | 4c5c CJK Ideograph Extension A-4C5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c5f CJK Ideograph Extension A-4C5F | 4c5e CJK Ideograph Extension A-4C5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c61 CJK Ideograph Extension A-4C61 | 4c60 CJK Ideograph Extension A-4C60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c63 CJK Ideograph Extension A-4C63 | 4c62 CJK Ideograph Extension A-4C62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c65 CJK Ideograph Extension A-4C65 | 4c64 CJK Ideograph Extension A-4C64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c67 CJK Ideograph Extension A-4C67 | 4c66 CJK Ideograph Extension A-4C66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c69 CJK Ideograph Extension A-4C69 | 4c68 CJK Ideograph Extension A-4C68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c6b CJK Ideograph Extension A-4C6B | 4c6a CJK Ideograph Extension A-4C6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c6d CJK Ideograph Extension A-4C6D | 4c6c CJK Ideograph Extension A-4C6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c6f CJK Ideograph Extension A-4C6F | 4c6e CJK Ideograph Extension A-4C6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c71 CJK Ideograph Extension A-4C71 | 4c70 CJK Ideograph Extension A-4C70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c73 CJK Ideograph Extension A-4C73 | 4c72 CJK Ideograph Extension A-4C72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c75 CJK Ideograph Extension A-4C75 | 4c74 CJK Ideograph Extension A-4C74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c77 CJK Ideograph Extension A-4C77 | 4c76 CJK Ideograph Extension A-4C76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c79 CJK Ideograph Extension A-4C79 | 4c78 CJK Ideograph Extension A-4C78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c7b CJK Ideograph Extension A-4C7B | 4c7a CJK Ideograph Extension A-4C7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c7d CJK Ideograph Extension A-4C7D | 4c7c CJK Ideograph Extension A-4C7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c7f CJK Ideograph Extension A-4C7F | 4c7e CJK Ideograph Extension A-4C7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c81 CJK Ideograph Extension A-4C81 | 4c80 CJK Ideograph Extension A-4C80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c83 CJK Ideograph Extension A-4C83 | 4c82 CJK Ideograph Extension A-4C82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c85 CJK Ideograph Extension A-4C85 | 4c84 CJK Ideograph Extension A-4C84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c87 CJK Ideograph Extension A-4C87 | 4c86 CJK Ideograph Extension A-4C86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c89 CJK Ideograph Extension A-4C89 | 4c88 CJK Ideograph Extension A-4C88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c8b CJK Ideograph Extension A-4C8B | 4c8a CJK Ideograph Extension A-4C8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c8d CJK Ideograph Extension A-4C8D | 4c8c CJK Ideograph Extension A-4C8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c8f CJK Ideograph Extension A-4C8F | 4c8e CJK Ideograph Extension A-4C8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c91 CJK Ideograph Extension A-4C91 | 4c90 CJK Ideograph Extension A-4C90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c93 CJK Ideograph Extension A-4C93 | 4c92 CJK Ideograph Extension A-4C92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c95 CJK Ideograph Extension A-4C95 | 4c94 CJK Ideograph Extension A-4C94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c97 CJK Ideograph Extension A-4C97 | 4c96 CJK Ideograph Extension A-4C96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c99 CJK Ideograph Extension A-4C99 | 4c98 CJK Ideograph Extension A-4C98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c9b CJK Ideograph Extension A-4C9B | 4c9a CJK Ideograph Extension A-4C9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c9d CJK Ideograph Extension A-4C9D | 4c9c CJK Ideograph Extension A-4C9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c9f CJK Ideograph Extension A-4C9F | 4c9e CJK Ideograph Extension A-4C9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ca1 CJK Ideograph Extension A-4CA1 | 4ca0 CJK Ideograph Extension A-4CA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ca3 CJK Ideograph Extension A-4CA3 | 4ca2 CJK Ideograph Extension A-4CA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ca5 CJK Ideograph Extension A-4CA5 | 4ca4 CJK Ideograph Extension A-4CA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ca7 CJK Ideograph Extension A-4CA7 | 4ca6 CJK Ideograph Extension A-4CA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ca9 CJK Ideograph Extension A-4CA9 | 4ca8 CJK Ideograph Extension A-4CA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cab CJK Ideograph Extension A-4CAB | 4caa CJK Ideograph Extension A-4CAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cad CJK Ideograph Extension A-4CAD | 4cac CJK Ideograph Extension A-4CAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4caf CJK Ideograph Extension A-4CAF | 4cae CJK Ideograph Extension A-4CAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cb1 CJK Ideograph Extension A-4CB1 | 4cb0 CJK Ideograph Extension A-4CB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cb3 CJK Ideograph Extension A-4CB3 | 4cb2 CJK Ideograph Extension A-4CB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cb5 CJK Ideograph Extension A-4CB5 | 4cb4 CJK Ideograph Extension A-4CB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cb7 CJK Ideograph Extension A-4CB7 | 4cb6 CJK Ideograph Extension A-4CB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cb9 CJK Ideograph Extension A-4CB9 | 4cb8 CJK Ideograph Extension A-4CB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cbb CJK Ideograph Extension A-4CBB | 4cba CJK Ideograph Extension A-4CBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cbd CJK Ideograph Extension A-4CBD | 4cbc CJK Ideograph Extension A-4CBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cbf CJK Ideograph Extension A-4CBF | 4cbe CJK Ideograph Extension A-4CBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cc1 CJK Ideograph Extension A-4CC1 | 4cc0 CJK Ideograph Extension A-4CC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cc3 CJK Ideograph Extension A-4CC3 | 4cc2 CJK Ideograph Extension A-4CC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cc5 CJK Ideograph Extension A-4CC5 | 4cc4 CJK Ideograph Extension A-4CC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cc7 CJK Ideograph Extension A-4CC7 | 4cc6 CJK Ideograph Extension A-4CC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cc9 CJK Ideograph Extension A-4CC9 | 4cc8 CJK Ideograph Extension A-4CC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ccb CJK Ideograph Extension A-4CCB | 4cca CJK Ideograph Extension A-4CCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ccd CJK Ideograph Extension A-4CCD | 4ccc CJK Ideograph Extension A-4CCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ccf CJK Ideograph Extension A-4CCF | 4cce CJK Ideograph Extension A-4CCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cd1 CJK Ideograph Extension A-4CD1 | 4cd0 CJK Ideograph Extension A-4CD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cd3 CJK Ideograph Extension A-4CD3 | 4cd2 CJK Ideograph Extension A-4CD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cd5 CJK Ideograph Extension A-4CD5 | 4cd4 CJK Ideograph Extension A-4CD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cd7 CJK Ideograph Extension A-4CD7 | 4cd6 CJK Ideograph Extension A-4CD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cd9 CJK Ideograph Extension A-4CD9 | 4cd8 CJK Ideograph Extension A-4CD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cdb CJK Ideograph Extension A-4CDB | 4cda CJK Ideograph Extension A-4CDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cdd CJK Ideograph Extension A-4CDD | 4cdc CJK Ideograph Extension A-4CDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cdf CJK Ideograph Extension A-4CDF | 4cde CJK Ideograph Extension A-4CDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ce1 CJK Ideograph Extension A-4CE1 | 4ce0 CJK Ideograph Extension A-4CE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ce3 CJK Ideograph Extension A-4CE3 | 4ce2 CJK Ideograph Extension A-4CE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ce5 CJK Ideograph Extension A-4CE5 | 4ce4 CJK Ideograph Extension A-4CE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ce7 CJK Ideograph Extension A-4CE7 | 4ce6 CJK Ideograph Extension A-4CE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ce9 CJK Ideograph Extension A-4CE9 | 4ce8 CJK Ideograph Extension A-4CE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ceb CJK Ideograph Extension A-4CEB | 4cea CJK Ideograph Extension A-4CEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ced CJK Ideograph Extension A-4CED | 4cec CJK Ideograph Extension A-4CEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cef CJK Ideograph Extension A-4CEF | 4cee CJK Ideograph Extension A-4CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cf1 CJK Ideograph Extension A-4CF1 | 4cf0 CJK Ideograph Extension A-4CF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cf3 CJK Ideograph Extension A-4CF3 | 4cf2 CJK Ideograph Extension A-4CF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cf5 CJK Ideograph Extension A-4CF5 | 4cf4 CJK Ideograph Extension A-4CF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cf7 CJK Ideograph Extension A-4CF7 | 4cf6 CJK Ideograph Extension A-4CF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cf9 CJK Ideograph Extension A-4CF9 | 4cf8 CJK Ideograph Extension A-4CF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cfb CJK Ideograph Extension A-4CFB | 4cfa CJK Ideograph Extension A-4CFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cfd CJK Ideograph Extension A-4CFD | 4cfc CJK Ideograph Extension A-4CFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cff CJK Ideograph Extension A-4CFF | 4cfe CJK Ideograph Extension A-4CFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d01 CJK Ideograph Extension A-4D01 | 4d00 CJK Ideograph Extension A-4D00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d03 CJK Ideograph Extension A-4D03 | 4d02 CJK Ideograph Extension A-4D02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d05 CJK Ideograph Extension A-4D05 | 4d04 CJK Ideograph Extension A-4D04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d07 CJK Ideograph Extension A-4D07 | 4d06 CJK Ideograph Extension A-4D06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d09 CJK Ideograph Extension A-4D09 | 4d08 CJK Ideograph Extension A-4D08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d0b CJK Ideograph Extension A-4D0B | 4d0a CJK Ideograph Extension A-4D0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d0d CJK Ideograph Extension A-4D0D | 4d0c CJK Ideograph Extension A-4D0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d0f CJK Ideograph Extension A-4D0F | 4d0e CJK Ideograph Extension A-4D0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d11 CJK Ideograph Extension A-4D11 | 4d10 CJK Ideograph Extension A-4D10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d13 CJK Ideograph Extension A-4D13 | 4d12 CJK Ideograph Extension A-4D12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d15 CJK Ideograph Extension A-4D15 | 4d14 CJK Ideograph Extension A-4D14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d17 CJK Ideograph Extension A-4D17 | 4d16 CJK Ideograph Extension A-4D16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d19 CJK Ideograph Extension A-4D19 | 4d18 CJK Ideograph Extension A-4D18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d1b CJK Ideograph Extension A-4D1B | 4d1a CJK Ideograph Extension A-4D1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d1d CJK Ideograph Extension A-4D1D | 4d1c CJK Ideograph Extension A-4D1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d1f CJK Ideograph Extension A-4D1F | 4d1e CJK Ideograph Extension A-4D1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d21 CJK Ideograph Extension A-4D21 | 4d20 CJK Ideograph Extension A-4D20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d23 CJK Ideograph Extension A-4D23 | 4d22 CJK Ideograph Extension A-4D22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d25 CJK Ideograph Extension A-4D25 | 4d24 CJK Ideograph Extension A-4D24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d27 CJK Ideograph Extension A-4D27 | 4d26 CJK Ideograph Extension A-4D26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d29 CJK Ideograph Extension A-4D29 | 4d28 CJK Ideograph Extension A-4D28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d2b CJK Ideograph Extension A-4D2B | 4d2a CJK Ideograph Extension A-4D2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d2d CJK Ideograph Extension A-4D2D | 4d2c CJK Ideograph Extension A-4D2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d2f CJK Ideograph Extension A-4D2F | 4d2e CJK Ideograph Extension A-4D2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d31 CJK Ideograph Extension A-4D31 | 4d30 CJK Ideograph Extension A-4D30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d33 CJK Ideograph Extension A-4D33 | 4d32 CJK Ideograph Extension A-4D32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d35 CJK Ideograph Extension A-4D35 | 4d34 CJK Ideograph Extension A-4D34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d37 CJK Ideograph Extension A-4D37 | 4d36 CJK Ideograph Extension A-4D36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d39 CJK Ideograph Extension A-4D39 | 4d38 CJK Ideograph Extension A-4D38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d3b CJK Ideograph Extension A-4D3B | 4d3a CJK Ideograph Extension A-4D3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d3d CJK Ideograph Extension A-4D3D | 4d3c CJK Ideograph Extension A-4D3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d3f CJK Ideograph Extension A-4D3F | 4d3e CJK Ideograph Extension A-4D3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d41 CJK Ideograph Extension A-4D41 | 4d40 CJK Ideograph Extension A-4D40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d43 CJK Ideograph Extension A-4D43 | 4d42 CJK Ideograph Extension A-4D42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d45 CJK Ideograph Extension A-4D45 | 4d44 CJK Ideograph Extension A-4D44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d47 CJK Ideograph Extension A-4D47 | 4d46 CJK Ideograph Extension A-4D46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d49 CJK Ideograph Extension A-4D49 | 4d48 CJK Ideograph Extension A-4D48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d4b CJK Ideograph Extension A-4D4B | 4d4a CJK Ideograph Extension A-4D4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d4d CJK Ideograph Extension A-4D4D | 4d4c CJK Ideograph Extension A-4D4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d4f CJK Ideograph Extension A-4D4F | 4d4e CJK Ideograph Extension A-4D4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d51 CJK Ideograph Extension A-4D51 | 4d50 CJK Ideograph Extension A-4D50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d53 CJK Ideograph Extension A-4D53 | 4d52 CJK Ideograph Extension A-4D52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d55 CJK Ideograph Extension A-4D55 | 4d54 CJK Ideograph Extension A-4D54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d57 CJK Ideograph Extension A-4D57 | 4d56 CJK Ideograph Extension A-4D56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d59 CJK Ideograph Extension A-4D59 | 4d58 CJK Ideograph Extension A-4D58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d5b CJK Ideograph Extension A-4D5B | 4d5a CJK Ideograph Extension A-4D5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d5d CJK Ideograph Extension A-4D5D | 4d5c CJK Ideograph Extension A-4D5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d5f CJK Ideograph Extension A-4D5F | 4d5e CJK Ideograph Extension A-4D5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d61 CJK Ideograph Extension A-4D61 | 4d60 CJK Ideograph Extension A-4D60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d63 CJK Ideograph Extension A-4D63 | 4d62 CJK Ideograph Extension A-4D62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d65 CJK Ideograph Extension A-4D65 | 4d64 CJK Ideograph Extension A-4D64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d67 CJK Ideograph Extension A-4D67 | 4d66 CJK Ideograph Extension A-4D66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d69 CJK Ideograph Extension A-4D69 | 4d68 CJK Ideograph Extension A-4D68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d6b CJK Ideograph Extension A-4D6B | 4d6a CJK Ideograph Extension A-4D6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d6d CJK Ideograph Extension A-4D6D | 4d6c CJK Ideograph Extension A-4D6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d6f CJK Ideograph Extension A-4D6F | 4d6e CJK Ideograph Extension A-4D6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d71 CJK Ideograph Extension A-4D71 | 4d70 CJK Ideograph Extension A-4D70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d73 CJK Ideograph Extension A-4D73 | 4d72 CJK Ideograph Extension A-4D72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d75 CJK Ideograph Extension A-4D75 | 4d74 CJK Ideograph Extension A-4D74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d77 CJK Ideograph Extension A-4D77 | 4d76 CJK Ideograph Extension A-4D76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d79 CJK Ideograph Extension A-4D79 | 4d78 CJK Ideograph Extension A-4D78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d7b CJK Ideograph Extension A-4D7B | 4d7a CJK Ideograph Extension A-4D7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d7d CJK Ideograph Extension A-4D7D | 4d7c CJK Ideograph Extension A-4D7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d7f CJK Ideograph Extension A-4D7F | 4d7e CJK Ideograph Extension A-4D7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d81 CJK Ideograph Extension A-4D81 | 4d80 CJK Ideograph Extension A-4D80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d83 CJK Ideograph Extension A-4D83 | 4d82 CJK Ideograph Extension A-4D82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d85 CJK Ideograph Extension A-4D85 | 4d84 CJK Ideograph Extension A-4D84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d87 CJK Ideograph Extension A-4D87 | 4d86 CJK Ideograph Extension A-4D86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d89 CJK Ideograph Extension A-4D89 | 4d88 CJK Ideograph Extension A-4D88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d8b CJK Ideograph Extension A-4D8B | 4d8a CJK Ideograph Extension A-4D8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d8d CJK Ideograph Extension A-4D8D | 4d8c CJK Ideograph Extension A-4D8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d8f CJK Ideograph Extension A-4D8F | 4d8e CJK Ideograph Extension A-4D8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d91 CJK Ideograph Extension A-4D91 | 4d90 CJK Ideograph Extension A-4D90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d93 CJK Ideograph Extension A-4D93 | 4d92 CJK Ideograph Extension A-4D92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d95 CJK Ideograph Extension A-4D95 | 4d94 CJK Ideograph Extension A-4D94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d97 CJK Ideograph Extension A-4D97 | 4d96 CJK Ideograph Extension A-4D96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d99 CJK Ideograph Extension A-4D99 | 4d98 CJK Ideograph Extension A-4D98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d9b CJK Ideograph Extension A-4D9B | 4d9a CJK Ideograph Extension A-4D9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d9d CJK Ideograph Extension A-4D9D | 4d9c CJK Ideograph Extension A-4D9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d9f CJK Ideograph Extension A-4D9F | 4d9e CJK Ideograph Extension A-4D9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4da1 CJK Ideograph Extension A-4DA1 | 4da0 CJK Ideograph Extension A-4DA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4da3 CJK Ideograph Extension A-4DA3 | 4da2 CJK Ideograph Extension A-4DA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4da5 CJK Ideograph Extension A-4DA5 | 4da4 CJK Ideograph Extension A-4DA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4da7 CJK Ideograph Extension A-4DA7 | 4da6 CJK Ideograph Extension A-4DA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4da9 CJK Ideograph Extension A-4DA9 | 4da8 CJK Ideograph Extension A-4DA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4dab CJK Ideograph Extension A-4DAB | 4daa CJK Ideograph Extension A-4DAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4dad CJK Ideograph Extension A-4DAD | 4dac CJK Ideograph Extension A-4DAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4daf CJK Ideograph Extension A-4DAF | 4dae CJK Ideograph Extension A-4DAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4db1 CJK Ideograph Extension A-4DB1 | 4db0 CJK Ideograph Extension A-4DB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4db3 CJK Ideograph Extension A-4DB3 | 4db2 CJK Ideograph Extension A-4DB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4db5 CJK Ideograph Extension A-4DB5 | 4db4 CJK Ideograph Extension A-4DB4 */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 4db7 (null) | 4db6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 4db9 (null) | 4db8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 4dbb (null) | 4dba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 4dbd (null) | 4dbc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 4dbf (null) | 4dbe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dc1 HEXAGRAM FOR THE RECEPTIVE EARTH | 4dc0 HEXAGRAM FOR THE CREATIVE HEAVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dc3 HEXAGRAM FOR YOUTHFUL FOLLY | 4dc2 HEXAGRAM FOR DIFFICULTY AT THE BEGINNIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dc5 HEXAGRAM FOR CONFLICT | 4dc4 HEXAGRAM FOR WAITING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dc7 HEXAGRAM FOR HOLDING TOGETHER | 4dc6 HEXAGRAM FOR THE ARMY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dc9 HEXAGRAM FOR TREADING | 4dc8 HEXAGRAM FOR SMALL TAMING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dcb HEXAGRAM FOR STANDSTILL | 4dca HEXAGRAM FOR PEACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dcd HEXAGRAM FOR GREAT POSSESSION | 4dcc HEXAGRAM FOR FELLOWSHIP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dcf HEXAGRAM FOR ENTHUSIASM | 4dce HEXAGRAM FOR MODESTY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dd1 HEXAGRAM FOR WORK ON THE DECAYED | 4dd0 HEXAGRAM FOR FOLLOWING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dd3 HEXAGRAM FOR CONTEMPLATION | 4dd2 HEXAGRAM FOR APPROACH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dd5 HEXAGRAM FOR GRACE | 4dd4 HEXAGRAM FOR BITING THROUGH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dd7 HEXAGRAM FOR RETURN | 4dd6 HEXAGRAM FOR SPLITTING APART */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dd9 HEXAGRAM FOR GREAT TAMING | 4dd8 HEXAGRAM FOR INNOCENCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4ddb HEXAGRAM FOR GREAT PREPONDERANCE | 4dda HEXAGRAM FOR MOUTH CORNERS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4ddd HEXAGRAM FOR THE CLINGING FIRE | 4ddc HEXAGRAM FOR THE ABYSMAL WATER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4ddf HEXAGRAM FOR DURATION | 4dde HEXAGRAM FOR INFLUENCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4de1 HEXAGRAM FOR GREAT POWER | 4de0 HEXAGRAM FOR RETREAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4de3 HEXAGRAM FOR DARKENING OF THE LIGHT | 4de2 HEXAGRAM FOR PROGRESS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4de5 HEXAGRAM FOR OPPOSITION | 4de4 HEXAGRAM FOR THE FAMILY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4de7 HEXAGRAM FOR DELIVERANCE | 4de6 HEXAGRAM FOR OBSTRUCTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4de9 HEXAGRAM FOR INCREASE | 4de8 HEXAGRAM FOR DECREASE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4deb HEXAGRAM FOR COMING TO MEET | 4dea HEXAGRAM FOR BREAKTHROUGH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4ded HEXAGRAM FOR PUSHING UPWARD | 4dec HEXAGRAM FOR GATHERING TOGETHER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4def HEXAGRAM FOR THE WELL | 4dee HEXAGRAM FOR OPPRESSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4df1 HEXAGRAM FOR THE CAULDRON | 4df0 HEXAGRAM FOR REVOLUTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4df3 HEXAGRAM FOR THE KEEPING STILL MOUNTAIN | 4df2 HEXAGRAM FOR THE AROUSING THUNDER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4df5 HEXAGRAM FOR THE MARRYING MAIDEN | 4df4 HEXAGRAM FOR DEVELOPMENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4df7 HEXAGRAM FOR THE WANDERER | 4df6 HEXAGRAM FOR ABUNDANCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4df9 HEXAGRAM FOR THE JOYOUS LAKE | 4df8 HEXAGRAM FOR THE GENTLE WIND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dfb HEXAGRAM FOR LIMITATION | 4dfa HEXAGRAM FOR DISPERSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dfd HEXAGRAM FOR SMALL PREPONDERANCE | 4dfc HEXAGRAM FOR INNER TRUTH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dff HEXAGRAM FOR BEFORE COMPLETION | 4dfe HEXAGRAM FOR AFTER COMPLETION */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e01 CJK Ideograph-4E01 | 4e00 CJK Ideograph-4E00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e03 CJK Ideograph-4E03 | 4e02 CJK Ideograph-4E02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e05 CJK Ideograph-4E05 | 4e04 CJK Ideograph-4E04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e07 CJK Ideograph-4E07 | 4e06 CJK Ideograph-4E06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e09 CJK Ideograph-4E09 | 4e08 CJK Ideograph-4E08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e0b CJK Ideograph-4E0B | 4e0a CJK Ideograph-4E0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e0d CJK Ideograph-4E0D | 4e0c CJK Ideograph-4E0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e0f CJK Ideograph-4E0F | 4e0e CJK Ideograph-4E0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e11 CJK Ideograph-4E11 | 4e10 CJK Ideograph-4E10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e13 CJK Ideograph-4E13 | 4e12 CJK Ideograph-4E12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e15 CJK Ideograph-4E15 | 4e14 CJK Ideograph-4E14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e17 CJK Ideograph-4E17 | 4e16 CJK Ideograph-4E16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e19 CJK Ideograph-4E19 | 4e18 CJK Ideograph-4E18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e1b CJK Ideograph-4E1B | 4e1a CJK Ideograph-4E1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e1d CJK Ideograph-4E1D | 4e1c CJK Ideograph-4E1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e1f CJK Ideograph-4E1F | 4e1e CJK Ideograph-4E1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e21 CJK Ideograph-4E21 | 4e20 CJK Ideograph-4E20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e23 CJK Ideograph-4E23 | 4e22 CJK Ideograph-4E22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e25 CJK Ideograph-4E25 | 4e24 CJK Ideograph-4E24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e27 CJK Ideograph-4E27 | 4e26 CJK Ideograph-4E26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e29 CJK Ideograph-4E29 | 4e28 CJK Ideograph-4E28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e2b CJK Ideograph-4E2B | 4e2a CJK Ideograph-4E2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e2d CJK Ideograph-4E2D | 4e2c CJK Ideograph-4E2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e2f CJK Ideograph-4E2F | 4e2e CJK Ideograph-4E2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e31 CJK Ideograph-4E31 | 4e30 CJK Ideograph-4E30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e33 CJK Ideograph-4E33 | 4e32 CJK Ideograph-4E32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e35 CJK Ideograph-4E35 | 4e34 CJK Ideograph-4E34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e37 CJK Ideograph-4E37 | 4e36 CJK Ideograph-4E36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e39 CJK Ideograph-4E39 | 4e38 CJK Ideograph-4E38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e3b CJK Ideograph-4E3B | 4e3a CJK Ideograph-4E3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e3d CJK Ideograph-4E3D | 4e3c CJK Ideograph-4E3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e3f CJK Ideograph-4E3F | 4e3e CJK Ideograph-4E3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e41 CJK Ideograph-4E41 | 4e40 CJK Ideograph-4E40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e43 CJK Ideograph-4E43 | 4e42 CJK Ideograph-4E42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e45 CJK Ideograph-4E45 | 4e44 CJK Ideograph-4E44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e47 CJK Ideograph-4E47 | 4e46 CJK Ideograph-4E46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e49 CJK Ideograph-4E49 | 4e48 CJK Ideograph-4E48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e4b CJK Ideograph-4E4B | 4e4a CJK Ideograph-4E4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e4d CJK Ideograph-4E4D | 4e4c CJK Ideograph-4E4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e4f CJK Ideograph-4E4F | 4e4e CJK Ideograph-4E4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e51 CJK Ideograph-4E51 | 4e50 CJK Ideograph-4E50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e53 CJK Ideograph-4E53 | 4e52 CJK Ideograph-4E52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e55 CJK Ideograph-4E55 | 4e54 CJK Ideograph-4E54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e57 CJK Ideograph-4E57 | 4e56 CJK Ideograph-4E56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e59 CJK Ideograph-4E59 | 4e58 CJK Ideograph-4E58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e5b CJK Ideograph-4E5B | 4e5a CJK Ideograph-4E5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e5d CJK Ideograph-4E5D | 4e5c CJK Ideograph-4E5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e5f CJK Ideograph-4E5F | 4e5e CJK Ideograph-4E5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e61 CJK Ideograph-4E61 | 4e60 CJK Ideograph-4E60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e63 CJK Ideograph-4E63 | 4e62 CJK Ideograph-4E62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e65 CJK Ideograph-4E65 | 4e64 CJK Ideograph-4E64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e67 CJK Ideograph-4E67 | 4e66 CJK Ideograph-4E66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e69 CJK Ideograph-4E69 | 4e68 CJK Ideograph-4E68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e6b CJK Ideograph-4E6B | 4e6a CJK Ideograph-4E6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e6d CJK Ideograph-4E6D | 4e6c CJK Ideograph-4E6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e6f CJK Ideograph-4E6F | 4e6e CJK Ideograph-4E6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e71 CJK Ideograph-4E71 | 4e70 CJK Ideograph-4E70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e73 CJK Ideograph-4E73 | 4e72 CJK Ideograph-4E72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e75 CJK Ideograph-4E75 | 4e74 CJK Ideograph-4E74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e77 CJK Ideograph-4E77 | 4e76 CJK Ideograph-4E76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e79 CJK Ideograph-4E79 | 4e78 CJK Ideograph-4E78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e7b CJK Ideograph-4E7B | 4e7a CJK Ideograph-4E7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e7d CJK Ideograph-4E7D | 4e7c CJK Ideograph-4E7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e7f CJK Ideograph-4E7F | 4e7e CJK Ideograph-4E7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e81 CJK Ideograph-4E81 | 4e80 CJK Ideograph-4E80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e83 CJK Ideograph-4E83 | 4e82 CJK Ideograph-4E82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e85 CJK Ideograph-4E85 | 4e84 CJK Ideograph-4E84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e87 CJK Ideograph-4E87 | 4e86 CJK Ideograph-4E86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e89 CJK Ideograph-4E89 | 4e88 CJK Ideograph-4E88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e8b CJK Ideograph-4E8B | 4e8a CJK Ideograph-4E8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e8d CJK Ideograph-4E8D | 4e8c CJK Ideograph-4E8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e8f CJK Ideograph-4E8F | 4e8e CJK Ideograph-4E8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e91 CJK Ideograph-4E91 | 4e90 CJK Ideograph-4E90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e93 CJK Ideograph-4E93 | 4e92 CJK Ideograph-4E92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e95 CJK Ideograph-4E95 | 4e94 CJK Ideograph-4E94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e97 CJK Ideograph-4E97 | 4e96 CJK Ideograph-4E96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e99 CJK Ideograph-4E99 | 4e98 CJK Ideograph-4E98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e9b CJK Ideograph-4E9B | 4e9a CJK Ideograph-4E9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e9d CJK Ideograph-4E9D | 4e9c CJK Ideograph-4E9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e9f CJK Ideograph-4E9F | 4e9e CJK Ideograph-4E9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ea1 CJK Ideograph-4EA1 | 4ea0 CJK Ideograph-4EA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ea3 CJK Ideograph-4EA3 | 4ea2 CJK Ideograph-4EA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ea5 CJK Ideograph-4EA5 | 4ea4 CJK Ideograph-4EA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ea7 CJK Ideograph-4EA7 | 4ea6 CJK Ideograph-4EA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ea9 CJK Ideograph-4EA9 | 4ea8 CJK Ideograph-4EA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eab CJK Ideograph-4EAB | 4eaa CJK Ideograph-4EAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ead CJK Ideograph-4EAD | 4eac CJK Ideograph-4EAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eaf CJK Ideograph-4EAF | 4eae CJK Ideograph-4EAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eb1 CJK Ideograph-4EB1 | 4eb0 CJK Ideograph-4EB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eb3 CJK Ideograph-4EB3 | 4eb2 CJK Ideograph-4EB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eb5 CJK Ideograph-4EB5 | 4eb4 CJK Ideograph-4EB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eb7 CJK Ideograph-4EB7 | 4eb6 CJK Ideograph-4EB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eb9 CJK Ideograph-4EB9 | 4eb8 CJK Ideograph-4EB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ebb CJK Ideograph-4EBB | 4eba CJK Ideograph-4EBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ebd CJK Ideograph-4EBD | 4ebc CJK Ideograph-4EBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ebf CJK Ideograph-4EBF | 4ebe CJK Ideograph-4EBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ec1 CJK Ideograph-4EC1 | 4ec0 CJK Ideograph-4EC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ec3 CJK Ideograph-4EC3 | 4ec2 CJK Ideograph-4EC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ec5 CJK Ideograph-4EC5 | 4ec4 CJK Ideograph-4EC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ec7 CJK Ideograph-4EC7 | 4ec6 CJK Ideograph-4EC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ec9 CJK Ideograph-4EC9 | 4ec8 CJK Ideograph-4EC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ecb CJK Ideograph-4ECB | 4eca CJK Ideograph-4ECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ecd CJK Ideograph-4ECD | 4ecc CJK Ideograph-4ECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ecf CJK Ideograph-4ECF | 4ece CJK Ideograph-4ECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ed1 CJK Ideograph-4ED1 | 4ed0 CJK Ideograph-4ED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ed3 CJK Ideograph-4ED3 | 4ed2 CJK Ideograph-4ED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ed5 CJK Ideograph-4ED5 | 4ed4 CJK Ideograph-4ED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ed7 CJK Ideograph-4ED7 | 4ed6 CJK Ideograph-4ED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ed9 CJK Ideograph-4ED9 | 4ed8 CJK Ideograph-4ED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4edb CJK Ideograph-4EDB | 4eda CJK Ideograph-4EDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4edd CJK Ideograph-4EDD | 4edc CJK Ideograph-4EDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4edf CJK Ideograph-4EDF | 4ede CJK Ideograph-4EDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ee1 CJK Ideograph-4EE1 | 4ee0 CJK Ideograph-4EE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ee3 CJK Ideograph-4EE3 | 4ee2 CJK Ideograph-4EE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ee5 CJK Ideograph-4EE5 | 4ee4 CJK Ideograph-4EE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ee7 CJK Ideograph-4EE7 | 4ee6 CJK Ideograph-4EE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ee9 CJK Ideograph-4EE9 | 4ee8 CJK Ideograph-4EE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eeb CJK Ideograph-4EEB | 4eea CJK Ideograph-4EEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eed CJK Ideograph-4EED | 4eec CJK Ideograph-4EEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eef CJK Ideograph-4EEF | 4eee CJK Ideograph-4EEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ef1 CJK Ideograph-4EF1 | 4ef0 CJK Ideograph-4EF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ef3 CJK Ideograph-4EF3 | 4ef2 CJK Ideograph-4EF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ef5 CJK Ideograph-4EF5 | 4ef4 CJK Ideograph-4EF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ef7 CJK Ideograph-4EF7 | 4ef6 CJK Ideograph-4EF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ef9 CJK Ideograph-4EF9 | 4ef8 CJK Ideograph-4EF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4efb CJK Ideograph-4EFB | 4efa CJK Ideograph-4EFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4efd CJK Ideograph-4EFD | 4efc CJK Ideograph-4EFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eff CJK Ideograph-4EFF | 4efe CJK Ideograph-4EFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f01 CJK Ideograph-4F01 | 4f00 CJK Ideograph-4F00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f03 CJK Ideograph-4F03 | 4f02 CJK Ideograph-4F02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f05 CJK Ideograph-4F05 | 4f04 CJK Ideograph-4F04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f07 CJK Ideograph-4F07 | 4f06 CJK Ideograph-4F06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f09 CJK Ideograph-4F09 | 4f08 CJK Ideograph-4F08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f0b CJK Ideograph-4F0B | 4f0a CJK Ideograph-4F0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f0d CJK Ideograph-4F0D | 4f0c CJK Ideograph-4F0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f0f CJK Ideograph-4F0F | 4f0e CJK Ideograph-4F0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f11 CJK Ideograph-4F11 | 4f10 CJK Ideograph-4F10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f13 CJK Ideograph-4F13 | 4f12 CJK Ideograph-4F12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f15 CJK Ideograph-4F15 | 4f14 CJK Ideograph-4F14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f17 CJK Ideograph-4F17 | 4f16 CJK Ideograph-4F16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f19 CJK Ideograph-4F19 | 4f18 CJK Ideograph-4F18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f1b CJK Ideograph-4F1B | 4f1a CJK Ideograph-4F1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f1d CJK Ideograph-4F1D | 4f1c CJK Ideograph-4F1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f1f CJK Ideograph-4F1F | 4f1e CJK Ideograph-4F1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f21 CJK Ideograph-4F21 | 4f20 CJK Ideograph-4F20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f23 CJK Ideograph-4F23 | 4f22 CJK Ideograph-4F22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f25 CJK Ideograph-4F25 | 4f24 CJK Ideograph-4F24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f27 CJK Ideograph-4F27 | 4f26 CJK Ideograph-4F26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f29 CJK Ideograph-4F29 | 4f28 CJK Ideograph-4F28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f2b CJK Ideograph-4F2B | 4f2a CJK Ideograph-4F2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f2d CJK Ideograph-4F2D | 4f2c CJK Ideograph-4F2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f2f CJK Ideograph-4F2F | 4f2e CJK Ideograph-4F2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f31 CJK Ideograph-4F31 | 4f30 CJK Ideograph-4F30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f33 CJK Ideograph-4F33 | 4f32 CJK Ideograph-4F32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f35 CJK Ideograph-4F35 | 4f34 CJK Ideograph-4F34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f37 CJK Ideograph-4F37 | 4f36 CJK Ideograph-4F36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f39 CJK Ideograph-4F39 | 4f38 CJK Ideograph-4F38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f3b CJK Ideograph-4F3B | 4f3a CJK Ideograph-4F3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f3d CJK Ideograph-4F3D | 4f3c CJK Ideograph-4F3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f3f CJK Ideograph-4F3F | 4f3e CJK Ideograph-4F3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f41 CJK Ideograph-4F41 | 4f40 CJK Ideograph-4F40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f43 CJK Ideograph-4F43 | 4f42 CJK Ideograph-4F42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f45 CJK Ideograph-4F45 | 4f44 CJK Ideograph-4F44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f47 CJK Ideograph-4F47 | 4f46 CJK Ideograph-4F46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f49 CJK Ideograph-4F49 | 4f48 CJK Ideograph-4F48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f4b CJK Ideograph-4F4B | 4f4a CJK Ideograph-4F4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f4d CJK Ideograph-4F4D | 4f4c CJK Ideograph-4F4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f4f CJK Ideograph-4F4F | 4f4e CJK Ideograph-4F4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f51 CJK Ideograph-4F51 | 4f50 CJK Ideograph-4F50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f53 CJK Ideograph-4F53 | 4f52 CJK Ideograph-4F52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f55 CJK Ideograph-4F55 | 4f54 CJK Ideograph-4F54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f57 CJK Ideograph-4F57 | 4f56 CJK Ideograph-4F56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f59 CJK Ideograph-4F59 | 4f58 CJK Ideograph-4F58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f5b CJK Ideograph-4F5B | 4f5a CJK Ideograph-4F5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f5d CJK Ideograph-4F5D | 4f5c CJK Ideograph-4F5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f5f CJK Ideograph-4F5F | 4f5e CJK Ideograph-4F5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f61 CJK Ideograph-4F61 | 4f60 CJK Ideograph-4F60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f63 CJK Ideograph-4F63 | 4f62 CJK Ideograph-4F62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f65 CJK Ideograph-4F65 | 4f64 CJK Ideograph-4F64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f67 CJK Ideograph-4F67 | 4f66 CJK Ideograph-4F66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f69 CJK Ideograph-4F69 | 4f68 CJK Ideograph-4F68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f6b CJK Ideograph-4F6B | 4f6a CJK Ideograph-4F6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f6d CJK Ideograph-4F6D | 4f6c CJK Ideograph-4F6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f6f CJK Ideograph-4F6F | 4f6e CJK Ideograph-4F6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f71 CJK Ideograph-4F71 | 4f70 CJK Ideograph-4F70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f73 CJK Ideograph-4F73 | 4f72 CJK Ideograph-4F72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f75 CJK Ideograph-4F75 | 4f74 CJK Ideograph-4F74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f77 CJK Ideograph-4F77 | 4f76 CJK Ideograph-4F76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f79 CJK Ideograph-4F79 | 4f78 CJK Ideograph-4F78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f7b CJK Ideograph-4F7B | 4f7a CJK Ideograph-4F7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f7d CJK Ideograph-4F7D | 4f7c CJK Ideograph-4F7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f7f CJK Ideograph-4F7F | 4f7e CJK Ideograph-4F7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f81 CJK Ideograph-4F81 | 4f80 CJK Ideograph-4F80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f83 CJK Ideograph-4F83 | 4f82 CJK Ideograph-4F82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f85 CJK Ideograph-4F85 | 4f84 CJK Ideograph-4F84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f87 CJK Ideograph-4F87 | 4f86 CJK Ideograph-4F86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f89 CJK Ideograph-4F89 | 4f88 CJK Ideograph-4F88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f8b CJK Ideograph-4F8B | 4f8a CJK Ideograph-4F8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f8d CJK Ideograph-4F8D | 4f8c CJK Ideograph-4F8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f8f CJK Ideograph-4F8F | 4f8e CJK Ideograph-4F8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f91 CJK Ideograph-4F91 | 4f90 CJK Ideograph-4F90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f93 CJK Ideograph-4F93 | 4f92 CJK Ideograph-4F92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f95 CJK Ideograph-4F95 | 4f94 CJK Ideograph-4F94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f97 CJK Ideograph-4F97 | 4f96 CJK Ideograph-4F96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f99 CJK Ideograph-4F99 | 4f98 CJK Ideograph-4F98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f9b CJK Ideograph-4F9B | 4f9a CJK Ideograph-4F9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f9d CJK Ideograph-4F9D | 4f9c CJK Ideograph-4F9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f9f CJK Ideograph-4F9F | 4f9e CJK Ideograph-4F9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fa1 CJK Ideograph-4FA1 | 4fa0 CJK Ideograph-4FA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fa3 CJK Ideograph-4FA3 | 4fa2 CJK Ideograph-4FA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fa5 CJK Ideograph-4FA5 | 4fa4 CJK Ideograph-4FA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fa7 CJK Ideograph-4FA7 | 4fa6 CJK Ideograph-4FA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fa9 CJK Ideograph-4FA9 | 4fa8 CJK Ideograph-4FA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fab CJK Ideograph-4FAB | 4faa CJK Ideograph-4FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fad CJK Ideograph-4FAD | 4fac CJK Ideograph-4FAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4faf CJK Ideograph-4FAF | 4fae CJK Ideograph-4FAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fb1 CJK Ideograph-4FB1 | 4fb0 CJK Ideograph-4FB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fb3 CJK Ideograph-4FB3 | 4fb2 CJK Ideograph-4FB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fb5 CJK Ideograph-4FB5 | 4fb4 CJK Ideograph-4FB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fb7 CJK Ideograph-4FB7 | 4fb6 CJK Ideograph-4FB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fb9 CJK Ideograph-4FB9 | 4fb8 CJK Ideograph-4FB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fbb CJK Ideograph-4FBB | 4fba CJK Ideograph-4FBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fbd CJK Ideograph-4FBD | 4fbc CJK Ideograph-4FBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fbf CJK Ideograph-4FBF | 4fbe CJK Ideograph-4FBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fc1 CJK Ideograph-4FC1 | 4fc0 CJK Ideograph-4FC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fc3 CJK Ideograph-4FC3 | 4fc2 CJK Ideograph-4FC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fc5 CJK Ideograph-4FC5 | 4fc4 CJK Ideograph-4FC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fc7 CJK Ideograph-4FC7 | 4fc6 CJK Ideograph-4FC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fc9 CJK Ideograph-4FC9 | 4fc8 CJK Ideograph-4FC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fcb CJK Ideograph-4FCB | 4fca CJK Ideograph-4FCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fcd CJK Ideograph-4FCD | 4fcc CJK Ideograph-4FCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fcf CJK Ideograph-4FCF | 4fce CJK Ideograph-4FCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fd1 CJK Ideograph-4FD1 | 4fd0 CJK Ideograph-4FD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fd3 CJK Ideograph-4FD3 | 4fd2 CJK Ideograph-4FD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fd5 CJK Ideograph-4FD5 | 4fd4 CJK Ideograph-4FD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fd7 CJK Ideograph-4FD7 | 4fd6 CJK Ideograph-4FD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fd9 CJK Ideograph-4FD9 | 4fd8 CJK Ideograph-4FD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fdb CJK Ideograph-4FDB | 4fda CJK Ideograph-4FDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fdd CJK Ideograph-4FDD | 4fdc CJK Ideograph-4FDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fdf CJK Ideograph-4FDF | 4fde CJK Ideograph-4FDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fe1 CJK Ideograph-4FE1 | 4fe0 CJK Ideograph-4FE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fe3 CJK Ideograph-4FE3 | 4fe2 CJK Ideograph-4FE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fe5 CJK Ideograph-4FE5 | 4fe4 CJK Ideograph-4FE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fe7 CJK Ideograph-4FE7 | 4fe6 CJK Ideograph-4FE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fe9 CJK Ideograph-4FE9 | 4fe8 CJK Ideograph-4FE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4feb CJK Ideograph-4FEB | 4fea CJK Ideograph-4FEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fed CJK Ideograph-4FED | 4fec CJK Ideograph-4FEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fef CJK Ideograph-4FEF | 4fee CJK Ideograph-4FEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ff1 CJK Ideograph-4FF1 | 4ff0 CJK Ideograph-4FF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ff3 CJK Ideograph-4FF3 | 4ff2 CJK Ideograph-4FF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ff5 CJK Ideograph-4FF5 | 4ff4 CJK Ideograph-4FF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ff7 CJK Ideograph-4FF7 | 4ff6 CJK Ideograph-4FF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ff9 CJK Ideograph-4FF9 | 4ff8 CJK Ideograph-4FF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ffb CJK Ideograph-4FFB | 4ffa CJK Ideograph-4FFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ffd CJK Ideograph-4FFD | 4ffc CJK Ideograph-4FFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fff CJK Ideograph-4FFF | 4ffe CJK Ideograph-4FFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5001 CJK Ideograph-5001 | 5000 CJK Ideograph-5000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5003 CJK Ideograph-5003 | 5002 CJK Ideograph-5002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5005 CJK Ideograph-5005 | 5004 CJK Ideograph-5004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5007 CJK Ideograph-5007 | 5006 CJK Ideograph-5006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5009 CJK Ideograph-5009 | 5008 CJK Ideograph-5008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 500b CJK Ideograph-500B | 500a CJK Ideograph-500A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 500d CJK Ideograph-500D | 500c CJK Ideograph-500C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 500f CJK Ideograph-500F | 500e CJK Ideograph-500E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5011 CJK Ideograph-5011 | 5010 CJK Ideograph-5010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5013 CJK Ideograph-5013 | 5012 CJK Ideograph-5012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5015 CJK Ideograph-5015 | 5014 CJK Ideograph-5014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5017 CJK Ideograph-5017 | 5016 CJK Ideograph-5016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5019 CJK Ideograph-5019 | 5018 CJK Ideograph-5018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 501b CJK Ideograph-501B | 501a CJK Ideograph-501A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 501d CJK Ideograph-501D | 501c CJK Ideograph-501C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 501f CJK Ideograph-501F | 501e CJK Ideograph-501E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5021 CJK Ideograph-5021 | 5020 CJK Ideograph-5020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5023 CJK Ideograph-5023 | 5022 CJK Ideograph-5022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5025 CJK Ideograph-5025 | 5024 CJK Ideograph-5024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5027 CJK Ideograph-5027 | 5026 CJK Ideograph-5026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5029 CJK Ideograph-5029 | 5028 CJK Ideograph-5028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 502b CJK Ideograph-502B | 502a CJK Ideograph-502A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 502d CJK Ideograph-502D | 502c CJK Ideograph-502C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 502f CJK Ideograph-502F | 502e CJK Ideograph-502E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5031 CJK Ideograph-5031 | 5030 CJK Ideograph-5030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5033 CJK Ideograph-5033 | 5032 CJK Ideograph-5032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5035 CJK Ideograph-5035 | 5034 CJK Ideograph-5034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5037 CJK Ideograph-5037 | 5036 CJK Ideograph-5036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5039 CJK Ideograph-5039 | 5038 CJK Ideograph-5038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 503b CJK Ideograph-503B | 503a CJK Ideograph-503A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 503d CJK Ideograph-503D | 503c CJK Ideograph-503C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 503f CJK Ideograph-503F | 503e CJK Ideograph-503E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5041 CJK Ideograph-5041 | 5040 CJK Ideograph-5040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5043 CJK Ideograph-5043 | 5042 CJK Ideograph-5042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5045 CJK Ideograph-5045 | 5044 CJK Ideograph-5044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5047 CJK Ideograph-5047 | 5046 CJK Ideograph-5046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5049 CJK Ideograph-5049 | 5048 CJK Ideograph-5048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 504b CJK Ideograph-504B | 504a CJK Ideograph-504A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 504d CJK Ideograph-504D | 504c CJK Ideograph-504C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 504f CJK Ideograph-504F | 504e CJK Ideograph-504E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5051 CJK Ideograph-5051 | 5050 CJK Ideograph-5050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5053 CJK Ideograph-5053 | 5052 CJK Ideograph-5052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5055 CJK Ideograph-5055 | 5054 CJK Ideograph-5054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5057 CJK Ideograph-5057 | 5056 CJK Ideograph-5056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5059 CJK Ideograph-5059 | 5058 CJK Ideograph-5058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 505b CJK Ideograph-505B | 505a CJK Ideograph-505A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 505d CJK Ideograph-505D | 505c CJK Ideograph-505C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 505f CJK Ideograph-505F | 505e CJK Ideograph-505E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5061 CJK Ideograph-5061 | 5060 CJK Ideograph-5060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5063 CJK Ideograph-5063 | 5062 CJK Ideograph-5062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5065 CJK Ideograph-5065 | 5064 CJK Ideograph-5064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5067 CJK Ideograph-5067 | 5066 CJK Ideograph-5066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5069 CJK Ideograph-5069 | 5068 CJK Ideograph-5068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 506b CJK Ideograph-506B | 506a CJK Ideograph-506A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 506d CJK Ideograph-506D | 506c CJK Ideograph-506C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 506f CJK Ideograph-506F | 506e CJK Ideograph-506E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5071 CJK Ideograph-5071 | 5070 CJK Ideograph-5070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5073 CJK Ideograph-5073 | 5072 CJK Ideograph-5072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5075 CJK Ideograph-5075 | 5074 CJK Ideograph-5074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5077 CJK Ideograph-5077 | 5076 CJK Ideograph-5076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5079 CJK Ideograph-5079 | 5078 CJK Ideograph-5078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 507b CJK Ideograph-507B | 507a CJK Ideograph-507A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 507d CJK Ideograph-507D | 507c CJK Ideograph-507C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 507f CJK Ideograph-507F | 507e CJK Ideograph-507E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5081 CJK Ideograph-5081 | 5080 CJK Ideograph-5080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5083 CJK Ideograph-5083 | 5082 CJK Ideograph-5082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5085 CJK Ideograph-5085 | 5084 CJK Ideograph-5084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5087 CJK Ideograph-5087 | 5086 CJK Ideograph-5086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5089 CJK Ideograph-5089 | 5088 CJK Ideograph-5088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 508b CJK Ideograph-508B | 508a CJK Ideograph-508A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 508d CJK Ideograph-508D | 508c CJK Ideograph-508C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 508f CJK Ideograph-508F | 508e CJK Ideograph-508E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5091 CJK Ideograph-5091 | 5090 CJK Ideograph-5090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5093 CJK Ideograph-5093 | 5092 CJK Ideograph-5092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5095 CJK Ideograph-5095 | 5094 CJK Ideograph-5094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5097 CJK Ideograph-5097 | 5096 CJK Ideograph-5096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5099 CJK Ideograph-5099 | 5098 CJK Ideograph-5098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 509b CJK Ideograph-509B | 509a CJK Ideograph-509A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 509d CJK Ideograph-509D | 509c CJK Ideograph-509C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 509f CJK Ideograph-509F | 509e CJK Ideograph-509E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50a1 CJK Ideograph-50A1 | 50a0 CJK Ideograph-50A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50a3 CJK Ideograph-50A3 | 50a2 CJK Ideograph-50A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50a5 CJK Ideograph-50A5 | 50a4 CJK Ideograph-50A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50a7 CJK Ideograph-50A7 | 50a6 CJK Ideograph-50A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50a9 CJK Ideograph-50A9 | 50a8 CJK Ideograph-50A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50ab CJK Ideograph-50AB | 50aa CJK Ideograph-50AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50ad CJK Ideograph-50AD | 50ac CJK Ideograph-50AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50af CJK Ideograph-50AF | 50ae CJK Ideograph-50AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50b1 CJK Ideograph-50B1 | 50b0 CJK Ideograph-50B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50b3 CJK Ideograph-50B3 | 50b2 CJK Ideograph-50B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50b5 CJK Ideograph-50B5 | 50b4 CJK Ideograph-50B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50b7 CJK Ideograph-50B7 | 50b6 CJK Ideograph-50B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50b9 CJK Ideograph-50B9 | 50b8 CJK Ideograph-50B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50bb CJK Ideograph-50BB | 50ba CJK Ideograph-50BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50bd CJK Ideograph-50BD | 50bc CJK Ideograph-50BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50bf CJK Ideograph-50BF | 50be CJK Ideograph-50BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50c1 CJK Ideograph-50C1 | 50c0 CJK Ideograph-50C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50c3 CJK Ideograph-50C3 | 50c2 CJK Ideograph-50C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50c5 CJK Ideograph-50C5 | 50c4 CJK Ideograph-50C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50c7 CJK Ideograph-50C7 | 50c6 CJK Ideograph-50C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50c9 CJK Ideograph-50C9 | 50c8 CJK Ideograph-50C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50cb CJK Ideograph-50CB | 50ca CJK Ideograph-50CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50cd CJK Ideograph-50CD | 50cc CJK Ideograph-50CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50cf CJK Ideograph-50CF | 50ce CJK Ideograph-50CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50d1 CJK Ideograph-50D1 | 50d0 CJK Ideograph-50D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50d3 CJK Ideograph-50D3 | 50d2 CJK Ideograph-50D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50d5 CJK Ideograph-50D5 | 50d4 CJK Ideograph-50D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50d7 CJK Ideograph-50D7 | 50d6 CJK Ideograph-50D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50d9 CJK Ideograph-50D9 | 50d8 CJK Ideograph-50D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50db CJK Ideograph-50DB | 50da CJK Ideograph-50DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50dd CJK Ideograph-50DD | 50dc CJK Ideograph-50DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50df CJK Ideograph-50DF | 50de CJK Ideograph-50DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50e1 CJK Ideograph-50E1 | 50e0 CJK Ideograph-50E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50e3 CJK Ideograph-50E3 | 50e2 CJK Ideograph-50E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50e5 CJK Ideograph-50E5 | 50e4 CJK Ideograph-50E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50e7 CJK Ideograph-50E7 | 50e6 CJK Ideograph-50E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50e9 CJK Ideograph-50E9 | 50e8 CJK Ideograph-50E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50eb CJK Ideograph-50EB | 50ea CJK Ideograph-50EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50ed CJK Ideograph-50ED | 50ec CJK Ideograph-50EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50ef CJK Ideograph-50EF | 50ee CJK Ideograph-50EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50f1 CJK Ideograph-50F1 | 50f0 CJK Ideograph-50F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50f3 CJK Ideograph-50F3 | 50f2 CJK Ideograph-50F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50f5 CJK Ideograph-50F5 | 50f4 CJK Ideograph-50F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50f7 CJK Ideograph-50F7 | 50f6 CJK Ideograph-50F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50f9 CJK Ideograph-50F9 | 50f8 CJK Ideograph-50F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50fb CJK Ideograph-50FB | 50fa CJK Ideograph-50FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50fd CJK Ideograph-50FD | 50fc CJK Ideograph-50FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50ff CJK Ideograph-50FF | 50fe CJK Ideograph-50FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5101 CJK Ideograph-5101 | 5100 CJK Ideograph-5100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5103 CJK Ideograph-5103 | 5102 CJK Ideograph-5102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5105 CJK Ideograph-5105 | 5104 CJK Ideograph-5104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5107 CJK Ideograph-5107 | 5106 CJK Ideograph-5106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5109 CJK Ideograph-5109 | 5108 CJK Ideograph-5108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 510b CJK Ideograph-510B | 510a CJK Ideograph-510A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 510d CJK Ideograph-510D | 510c CJK Ideograph-510C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 510f CJK Ideograph-510F | 510e CJK Ideograph-510E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5111 CJK Ideograph-5111 | 5110 CJK Ideograph-5110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5113 CJK Ideograph-5113 | 5112 CJK Ideograph-5112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5115 CJK Ideograph-5115 | 5114 CJK Ideograph-5114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5117 CJK Ideograph-5117 | 5116 CJK Ideograph-5116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5119 CJK Ideograph-5119 | 5118 CJK Ideograph-5118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 511b CJK Ideograph-511B | 511a CJK Ideograph-511A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 511d CJK Ideograph-511D | 511c CJK Ideograph-511C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 511f CJK Ideograph-511F | 511e CJK Ideograph-511E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5121 CJK Ideograph-5121 | 5120 CJK Ideograph-5120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5123 CJK Ideograph-5123 | 5122 CJK Ideograph-5122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5125 CJK Ideograph-5125 | 5124 CJK Ideograph-5124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5127 CJK Ideograph-5127 | 5126 CJK Ideograph-5126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5129 CJK Ideograph-5129 | 5128 CJK Ideograph-5128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 512b CJK Ideograph-512B | 512a CJK Ideograph-512A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 512d CJK Ideograph-512D | 512c CJK Ideograph-512C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 512f CJK Ideograph-512F | 512e CJK Ideograph-512E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5131 CJK Ideograph-5131 | 5130 CJK Ideograph-5130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5133 CJK Ideograph-5133 | 5132 CJK Ideograph-5132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5135 CJK Ideograph-5135 | 5134 CJK Ideograph-5134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5137 CJK Ideograph-5137 | 5136 CJK Ideograph-5136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5139 CJK Ideograph-5139 | 5138 CJK Ideograph-5138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 513b CJK Ideograph-513B | 513a CJK Ideograph-513A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 513d CJK Ideograph-513D | 513c CJK Ideograph-513C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 513f CJK Ideograph-513F | 513e CJK Ideograph-513E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5141 CJK Ideograph-5141 | 5140 CJK Ideograph-5140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5143 CJK Ideograph-5143 | 5142 CJK Ideograph-5142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5145 CJK Ideograph-5145 | 5144 CJK Ideograph-5144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5147 CJK Ideograph-5147 | 5146 CJK Ideograph-5146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5149 CJK Ideograph-5149 | 5148 CJK Ideograph-5148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 514b CJK Ideograph-514B | 514a CJK Ideograph-514A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 514d CJK Ideograph-514D | 514c CJK Ideograph-514C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 514f CJK Ideograph-514F | 514e CJK Ideograph-514E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5151 CJK Ideograph-5151 | 5150 CJK Ideograph-5150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5153 CJK Ideograph-5153 | 5152 CJK Ideograph-5152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5155 CJK Ideograph-5155 | 5154 CJK Ideograph-5154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5157 CJK Ideograph-5157 | 5156 CJK Ideograph-5156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5159 CJK Ideograph-5159 | 5158 CJK Ideograph-5158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 515b CJK Ideograph-515B | 515a CJK Ideograph-515A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 515d CJK Ideograph-515D | 515c CJK Ideograph-515C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 515f CJK Ideograph-515F | 515e CJK Ideograph-515E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5161 CJK Ideograph-5161 | 5160 CJK Ideograph-5160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5163 CJK Ideograph-5163 | 5162 CJK Ideograph-5162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5165 CJK Ideograph-5165 | 5164 CJK Ideograph-5164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5167 CJK Ideograph-5167 | 5166 CJK Ideograph-5166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5169 CJK Ideograph-5169 | 5168 CJK Ideograph-5168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 516b CJK Ideograph-516B | 516a CJK Ideograph-516A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 516d CJK Ideograph-516D | 516c CJK Ideograph-516C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 516f CJK Ideograph-516F | 516e CJK Ideograph-516E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5171 CJK Ideograph-5171 | 5170 CJK Ideograph-5170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5173 CJK Ideograph-5173 | 5172 CJK Ideograph-5172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5175 CJK Ideograph-5175 | 5174 CJK Ideograph-5174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5177 CJK Ideograph-5177 | 5176 CJK Ideograph-5176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5179 CJK Ideograph-5179 | 5178 CJK Ideograph-5178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 517b CJK Ideograph-517B | 517a CJK Ideograph-517A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 517d CJK Ideograph-517D | 517c CJK Ideograph-517C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 517f CJK Ideograph-517F | 517e CJK Ideograph-517E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5181 CJK Ideograph-5181 | 5180 CJK Ideograph-5180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5183 CJK Ideograph-5183 | 5182 CJK Ideograph-5182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5185 CJK Ideograph-5185 | 5184 CJK Ideograph-5184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5187 CJK Ideograph-5187 | 5186 CJK Ideograph-5186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5189 CJK Ideograph-5189 | 5188 CJK Ideograph-5188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 518b CJK Ideograph-518B | 518a CJK Ideograph-518A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 518d CJK Ideograph-518D | 518c CJK Ideograph-518C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 518f CJK Ideograph-518F | 518e CJK Ideograph-518E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5191 CJK Ideograph-5191 | 5190 CJK Ideograph-5190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5193 CJK Ideograph-5193 | 5192 CJK Ideograph-5192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5195 CJK Ideograph-5195 | 5194 CJK Ideograph-5194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5197 CJK Ideograph-5197 | 5196 CJK Ideograph-5196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5199 CJK Ideograph-5199 | 5198 CJK Ideograph-5198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 519b CJK Ideograph-519B | 519a CJK Ideograph-519A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 519d CJK Ideograph-519D | 519c CJK Ideograph-519C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 519f CJK Ideograph-519F | 519e CJK Ideograph-519E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51a1 CJK Ideograph-51A1 | 51a0 CJK Ideograph-51A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51a3 CJK Ideograph-51A3 | 51a2 CJK Ideograph-51A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51a5 CJK Ideograph-51A5 | 51a4 CJK Ideograph-51A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51a7 CJK Ideograph-51A7 | 51a6 CJK Ideograph-51A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51a9 CJK Ideograph-51A9 | 51a8 CJK Ideograph-51A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51ab CJK Ideograph-51AB | 51aa CJK Ideograph-51AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51ad CJK Ideograph-51AD | 51ac CJK Ideograph-51AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51af CJK Ideograph-51AF | 51ae CJK Ideograph-51AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51b1 CJK Ideograph-51B1 | 51b0 CJK Ideograph-51B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51b3 CJK Ideograph-51B3 | 51b2 CJK Ideograph-51B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51b5 CJK Ideograph-51B5 | 51b4 CJK Ideograph-51B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51b7 CJK Ideograph-51B7 | 51b6 CJK Ideograph-51B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51b9 CJK Ideograph-51B9 | 51b8 CJK Ideograph-51B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51bb CJK Ideograph-51BB | 51ba CJK Ideograph-51BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51bd CJK Ideograph-51BD | 51bc CJK Ideograph-51BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51bf CJK Ideograph-51BF | 51be CJK Ideograph-51BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51c1 CJK Ideograph-51C1 | 51c0 CJK Ideograph-51C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51c3 CJK Ideograph-51C3 | 51c2 CJK Ideograph-51C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51c5 CJK Ideograph-51C5 | 51c4 CJK Ideograph-51C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51c7 CJK Ideograph-51C7 | 51c6 CJK Ideograph-51C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51c9 CJK Ideograph-51C9 | 51c8 CJK Ideograph-51C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51cb CJK Ideograph-51CB | 51ca CJK Ideograph-51CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51cd CJK Ideograph-51CD | 51cc CJK Ideograph-51CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51cf CJK Ideograph-51CF | 51ce CJK Ideograph-51CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51d1 CJK Ideograph-51D1 | 51d0 CJK Ideograph-51D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51d3 CJK Ideograph-51D3 | 51d2 CJK Ideograph-51D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51d5 CJK Ideograph-51D5 | 51d4 CJK Ideograph-51D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51d7 CJK Ideograph-51D7 | 51d6 CJK Ideograph-51D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51d9 CJK Ideograph-51D9 | 51d8 CJK Ideograph-51D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51db CJK Ideograph-51DB | 51da CJK Ideograph-51DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51dd CJK Ideograph-51DD | 51dc CJK Ideograph-51DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51df CJK Ideograph-51DF | 51de CJK Ideograph-51DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51e1 CJK Ideograph-51E1 | 51e0 CJK Ideograph-51E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51e3 CJK Ideograph-51E3 | 51e2 CJK Ideograph-51E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51e5 CJK Ideograph-51E5 | 51e4 CJK Ideograph-51E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51e7 CJK Ideograph-51E7 | 51e6 CJK Ideograph-51E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51e9 CJK Ideograph-51E9 | 51e8 CJK Ideograph-51E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51eb CJK Ideograph-51EB | 51ea CJK Ideograph-51EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51ed CJK Ideograph-51ED | 51ec CJK Ideograph-51EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51ef CJK Ideograph-51EF | 51ee CJK Ideograph-51EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51f1 CJK Ideograph-51F1 | 51f0 CJK Ideograph-51F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51f3 CJK Ideograph-51F3 | 51f2 CJK Ideograph-51F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51f5 CJK Ideograph-51F5 | 51f4 CJK Ideograph-51F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51f7 CJK Ideograph-51F7 | 51f6 CJK Ideograph-51F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51f9 CJK Ideograph-51F9 | 51f8 CJK Ideograph-51F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51fb CJK Ideograph-51FB | 51fa CJK Ideograph-51FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51fd CJK Ideograph-51FD | 51fc CJK Ideograph-51FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51ff CJK Ideograph-51FF | 51fe CJK Ideograph-51FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5201 CJK Ideograph-5201 | 5200 CJK Ideograph-5200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5203 CJK Ideograph-5203 | 5202 CJK Ideograph-5202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5205 CJK Ideograph-5205 | 5204 CJK Ideograph-5204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5207 CJK Ideograph-5207 | 5206 CJK Ideograph-5206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5209 CJK Ideograph-5209 | 5208 CJK Ideograph-5208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 520b CJK Ideograph-520B | 520a CJK Ideograph-520A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 520d CJK Ideograph-520D | 520c CJK Ideograph-520C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 520f CJK Ideograph-520F | 520e CJK Ideograph-520E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5211 CJK Ideograph-5211 | 5210 CJK Ideograph-5210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5213 CJK Ideograph-5213 | 5212 CJK Ideograph-5212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5215 CJK Ideograph-5215 | 5214 CJK Ideograph-5214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5217 CJK Ideograph-5217 | 5216 CJK Ideograph-5216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5219 CJK Ideograph-5219 | 5218 CJK Ideograph-5218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 521b CJK Ideograph-521B | 521a CJK Ideograph-521A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 521d CJK Ideograph-521D | 521c CJK Ideograph-521C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 521f CJK Ideograph-521F | 521e CJK Ideograph-521E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5221 CJK Ideograph-5221 | 5220 CJK Ideograph-5220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5223 CJK Ideograph-5223 | 5222 CJK Ideograph-5222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5225 CJK Ideograph-5225 | 5224 CJK Ideograph-5224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5227 CJK Ideograph-5227 | 5226 CJK Ideograph-5226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5229 CJK Ideograph-5229 | 5228 CJK Ideograph-5228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 522b CJK Ideograph-522B | 522a CJK Ideograph-522A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 522d CJK Ideograph-522D | 522c CJK Ideograph-522C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 522f CJK Ideograph-522F | 522e CJK Ideograph-522E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5231 CJK Ideograph-5231 | 5230 CJK Ideograph-5230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5233 CJK Ideograph-5233 | 5232 CJK Ideograph-5232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5235 CJK Ideograph-5235 | 5234 CJK Ideograph-5234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5237 CJK Ideograph-5237 | 5236 CJK Ideograph-5236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5239 CJK Ideograph-5239 | 5238 CJK Ideograph-5238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 523b CJK Ideograph-523B | 523a CJK Ideograph-523A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 523d CJK Ideograph-523D | 523c CJK Ideograph-523C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 523f CJK Ideograph-523F | 523e CJK Ideograph-523E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5241 CJK Ideograph-5241 | 5240 CJK Ideograph-5240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5243 CJK Ideograph-5243 | 5242 CJK Ideograph-5242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5245 CJK Ideograph-5245 | 5244 CJK Ideograph-5244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5247 CJK Ideograph-5247 | 5246 CJK Ideograph-5246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5249 CJK Ideograph-5249 | 5248 CJK Ideograph-5248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 524b CJK Ideograph-524B | 524a CJK Ideograph-524A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 524d CJK Ideograph-524D | 524c CJK Ideograph-524C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 524f CJK Ideograph-524F | 524e CJK Ideograph-524E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5251 CJK Ideograph-5251 | 5250 CJK Ideograph-5250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5253 CJK Ideograph-5253 | 5252 CJK Ideograph-5252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5255 CJK Ideograph-5255 | 5254 CJK Ideograph-5254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5257 CJK Ideograph-5257 | 5256 CJK Ideograph-5256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5259 CJK Ideograph-5259 | 5258 CJK Ideograph-5258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 525b CJK Ideograph-525B | 525a CJK Ideograph-525A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 525d CJK Ideograph-525D | 525c CJK Ideograph-525C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 525f CJK Ideograph-525F | 525e CJK Ideograph-525E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5261 CJK Ideograph-5261 | 5260 CJK Ideograph-5260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5263 CJK Ideograph-5263 | 5262 CJK Ideograph-5262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5265 CJK Ideograph-5265 | 5264 CJK Ideograph-5264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5267 CJK Ideograph-5267 | 5266 CJK Ideograph-5266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5269 CJK Ideograph-5269 | 5268 CJK Ideograph-5268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 526b CJK Ideograph-526B | 526a CJK Ideograph-526A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 526d CJK Ideograph-526D | 526c CJK Ideograph-526C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 526f CJK Ideograph-526F | 526e CJK Ideograph-526E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5271 CJK Ideograph-5271 | 5270 CJK Ideograph-5270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5273 CJK Ideograph-5273 | 5272 CJK Ideograph-5272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5275 CJK Ideograph-5275 | 5274 CJK Ideograph-5274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5277 CJK Ideograph-5277 | 5276 CJK Ideograph-5276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5279 CJK Ideograph-5279 | 5278 CJK Ideograph-5278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 527b CJK Ideograph-527B | 527a CJK Ideograph-527A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 527d CJK Ideograph-527D | 527c CJK Ideograph-527C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 527f CJK Ideograph-527F | 527e CJK Ideograph-527E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5281 CJK Ideograph-5281 | 5280 CJK Ideograph-5280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5283 CJK Ideograph-5283 | 5282 CJK Ideograph-5282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5285 CJK Ideograph-5285 | 5284 CJK Ideograph-5284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5287 CJK Ideograph-5287 | 5286 CJK Ideograph-5286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5289 CJK Ideograph-5289 | 5288 CJK Ideograph-5288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 528b CJK Ideograph-528B | 528a CJK Ideograph-528A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 528d CJK Ideograph-528D | 528c CJK Ideograph-528C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 528f CJK Ideograph-528F | 528e CJK Ideograph-528E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5291 CJK Ideograph-5291 | 5290 CJK Ideograph-5290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5293 CJK Ideograph-5293 | 5292 CJK Ideograph-5292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5295 CJK Ideograph-5295 | 5294 CJK Ideograph-5294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5297 CJK Ideograph-5297 | 5296 CJK Ideograph-5296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5299 CJK Ideograph-5299 | 5298 CJK Ideograph-5298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 529b CJK Ideograph-529B | 529a CJK Ideograph-529A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 529d CJK Ideograph-529D | 529c CJK Ideograph-529C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 529f CJK Ideograph-529F | 529e CJK Ideograph-529E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52a1 CJK Ideograph-52A1 | 52a0 CJK Ideograph-52A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52a3 CJK Ideograph-52A3 | 52a2 CJK Ideograph-52A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52a5 CJK Ideograph-52A5 | 52a4 CJK Ideograph-52A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52a7 CJK Ideograph-52A7 | 52a6 CJK Ideograph-52A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52a9 CJK Ideograph-52A9 | 52a8 CJK Ideograph-52A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52ab CJK Ideograph-52AB | 52aa CJK Ideograph-52AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52ad CJK Ideograph-52AD | 52ac CJK Ideograph-52AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52af CJK Ideograph-52AF | 52ae CJK Ideograph-52AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52b1 CJK Ideograph-52B1 | 52b0 CJK Ideograph-52B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52b3 CJK Ideograph-52B3 | 52b2 CJK Ideograph-52B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52b5 CJK Ideograph-52B5 | 52b4 CJK Ideograph-52B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52b7 CJK Ideograph-52B7 | 52b6 CJK Ideograph-52B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52b9 CJK Ideograph-52B9 | 52b8 CJK Ideograph-52B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52bb CJK Ideograph-52BB | 52ba CJK Ideograph-52BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52bd CJK Ideograph-52BD | 52bc CJK Ideograph-52BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52bf CJK Ideograph-52BF | 52be CJK Ideograph-52BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52c1 CJK Ideograph-52C1 | 52c0 CJK Ideograph-52C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52c3 CJK Ideograph-52C3 | 52c2 CJK Ideograph-52C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52c5 CJK Ideograph-52C5 | 52c4 CJK Ideograph-52C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52c7 CJK Ideograph-52C7 | 52c6 CJK Ideograph-52C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52c9 CJK Ideograph-52C9 | 52c8 CJK Ideograph-52C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52cb CJK Ideograph-52CB | 52ca CJK Ideograph-52CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52cd CJK Ideograph-52CD | 52cc CJK Ideograph-52CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52cf CJK Ideograph-52CF | 52ce CJK Ideograph-52CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52d1 CJK Ideograph-52D1 | 52d0 CJK Ideograph-52D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52d3 CJK Ideograph-52D3 | 52d2 CJK Ideograph-52D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52d5 CJK Ideograph-52D5 | 52d4 CJK Ideograph-52D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52d7 CJK Ideograph-52D7 | 52d6 CJK Ideograph-52D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52d9 CJK Ideograph-52D9 | 52d8 CJK Ideograph-52D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52db CJK Ideograph-52DB | 52da CJK Ideograph-52DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52dd CJK Ideograph-52DD | 52dc CJK Ideograph-52DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52df CJK Ideograph-52DF | 52de CJK Ideograph-52DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52e1 CJK Ideograph-52E1 | 52e0 CJK Ideograph-52E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52e3 CJK Ideograph-52E3 | 52e2 CJK Ideograph-52E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52e5 CJK Ideograph-52E5 | 52e4 CJK Ideograph-52E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52e7 CJK Ideograph-52E7 | 52e6 CJK Ideograph-52E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52e9 CJK Ideograph-52E9 | 52e8 CJK Ideograph-52E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52eb CJK Ideograph-52EB | 52ea CJK Ideograph-52EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52ed CJK Ideograph-52ED | 52ec CJK Ideograph-52EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52ef CJK Ideograph-52EF | 52ee CJK Ideograph-52EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52f1 CJK Ideograph-52F1 | 52f0 CJK Ideograph-52F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52f3 CJK Ideograph-52F3 | 52f2 CJK Ideograph-52F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52f5 CJK Ideograph-52F5 | 52f4 CJK Ideograph-52F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52f7 CJK Ideograph-52F7 | 52f6 CJK Ideograph-52F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52f9 CJK Ideograph-52F9 | 52f8 CJK Ideograph-52F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52fb CJK Ideograph-52FB | 52fa CJK Ideograph-52FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52fd CJK Ideograph-52FD | 52fc CJK Ideograph-52FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52ff CJK Ideograph-52FF | 52fe CJK Ideograph-52FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5301 CJK Ideograph-5301 | 5300 CJK Ideograph-5300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5303 CJK Ideograph-5303 | 5302 CJK Ideograph-5302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5305 CJK Ideograph-5305 | 5304 CJK Ideograph-5304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5307 CJK Ideograph-5307 | 5306 CJK Ideograph-5306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5309 CJK Ideograph-5309 | 5308 CJK Ideograph-5308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 530b CJK Ideograph-530B | 530a CJK Ideograph-530A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 530d CJK Ideograph-530D | 530c CJK Ideograph-530C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 530f CJK Ideograph-530F | 530e CJK Ideograph-530E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5311 CJK Ideograph-5311 | 5310 CJK Ideograph-5310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5313 CJK Ideograph-5313 | 5312 CJK Ideograph-5312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5315 CJK Ideograph-5315 | 5314 CJK Ideograph-5314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5317 CJK Ideograph-5317 | 5316 CJK Ideograph-5316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5319 CJK Ideograph-5319 | 5318 CJK Ideograph-5318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 531b CJK Ideograph-531B | 531a CJK Ideograph-531A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 531d CJK Ideograph-531D | 531c CJK Ideograph-531C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 531f CJK Ideograph-531F | 531e CJK Ideograph-531E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5321 CJK Ideograph-5321 | 5320 CJK Ideograph-5320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5323 CJK Ideograph-5323 | 5322 CJK Ideograph-5322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5325 CJK Ideograph-5325 | 5324 CJK Ideograph-5324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5327 CJK Ideograph-5327 | 5326 CJK Ideograph-5326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5329 CJK Ideograph-5329 | 5328 CJK Ideograph-5328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 532b CJK Ideograph-532B | 532a CJK Ideograph-532A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 532d CJK Ideograph-532D | 532c CJK Ideograph-532C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 532f CJK Ideograph-532F | 532e CJK Ideograph-532E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5331 CJK Ideograph-5331 | 5330 CJK Ideograph-5330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5333 CJK Ideograph-5333 | 5332 CJK Ideograph-5332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5335 CJK Ideograph-5335 | 5334 CJK Ideograph-5334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5337 CJK Ideograph-5337 | 5336 CJK Ideograph-5336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5339 CJK Ideograph-5339 | 5338 CJK Ideograph-5338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 533b CJK Ideograph-533B | 533a CJK Ideograph-533A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 533d CJK Ideograph-533D | 533c CJK Ideograph-533C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 533f CJK Ideograph-533F | 533e CJK Ideograph-533E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5341 CJK Ideograph-5341 | 5340 CJK Ideograph-5340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5343 CJK Ideograph-5343 | 5342 CJK Ideograph-5342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5345 CJK Ideograph-5345 | 5344 CJK Ideograph-5344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5347 CJK Ideograph-5347 | 5346 CJK Ideograph-5346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5349 CJK Ideograph-5349 | 5348 CJK Ideograph-5348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 534b CJK Ideograph-534B | 534a CJK Ideograph-534A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 534d CJK Ideograph-534D | 534c CJK Ideograph-534C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 534f CJK Ideograph-534F | 534e CJK Ideograph-534E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5351 CJK Ideograph-5351 | 5350 CJK Ideograph-5350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5353 CJK Ideograph-5353 | 5352 CJK Ideograph-5352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5355 CJK Ideograph-5355 | 5354 CJK Ideograph-5354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5357 CJK Ideograph-5357 | 5356 CJK Ideograph-5356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5359 CJK Ideograph-5359 | 5358 CJK Ideograph-5358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 535b CJK Ideograph-535B | 535a CJK Ideograph-535A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 535d CJK Ideograph-535D | 535c CJK Ideograph-535C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 535f CJK Ideograph-535F | 535e CJK Ideograph-535E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5361 CJK Ideograph-5361 | 5360 CJK Ideograph-5360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5363 CJK Ideograph-5363 | 5362 CJK Ideograph-5362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5365 CJK Ideograph-5365 | 5364 CJK Ideograph-5364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5367 CJK Ideograph-5367 | 5366 CJK Ideograph-5366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5369 CJK Ideograph-5369 | 5368 CJK Ideograph-5368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 536b CJK Ideograph-536B | 536a CJK Ideograph-536A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 536d CJK Ideograph-536D | 536c CJK Ideograph-536C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 536f CJK Ideograph-536F | 536e CJK Ideograph-536E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5371 CJK Ideograph-5371 | 5370 CJK Ideograph-5370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5373 CJK Ideograph-5373 | 5372 CJK Ideograph-5372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5375 CJK Ideograph-5375 | 5374 CJK Ideograph-5374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5377 CJK Ideograph-5377 | 5376 CJK Ideograph-5376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5379 CJK Ideograph-5379 | 5378 CJK Ideograph-5378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 537b CJK Ideograph-537B | 537a CJK Ideograph-537A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 537d CJK Ideograph-537D | 537c CJK Ideograph-537C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 537f CJK Ideograph-537F | 537e CJK Ideograph-537E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5381 CJK Ideograph-5381 | 5380 CJK Ideograph-5380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5383 CJK Ideograph-5383 | 5382 CJK Ideograph-5382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5385 CJK Ideograph-5385 | 5384 CJK Ideograph-5384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5387 CJK Ideograph-5387 | 5386 CJK Ideograph-5386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5389 CJK Ideograph-5389 | 5388 CJK Ideograph-5388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 538b CJK Ideograph-538B | 538a CJK Ideograph-538A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 538d CJK Ideograph-538D | 538c CJK Ideograph-538C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 538f CJK Ideograph-538F | 538e CJK Ideograph-538E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5391 CJK Ideograph-5391 | 5390 CJK Ideograph-5390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5393 CJK Ideograph-5393 | 5392 CJK Ideograph-5392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5395 CJK Ideograph-5395 | 5394 CJK Ideograph-5394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5397 CJK Ideograph-5397 | 5396 CJK Ideograph-5396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5399 CJK Ideograph-5399 | 5398 CJK Ideograph-5398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 539b CJK Ideograph-539B | 539a CJK Ideograph-539A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 539d CJK Ideograph-539D | 539c CJK Ideograph-539C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 539f CJK Ideograph-539F | 539e CJK Ideograph-539E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53a1 CJK Ideograph-53A1 | 53a0 CJK Ideograph-53A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53a3 CJK Ideograph-53A3 | 53a2 CJK Ideograph-53A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53a5 CJK Ideograph-53A5 | 53a4 CJK Ideograph-53A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53a7 CJK Ideograph-53A7 | 53a6 CJK Ideograph-53A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53a9 CJK Ideograph-53A9 | 53a8 CJK Ideograph-53A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53ab CJK Ideograph-53AB | 53aa CJK Ideograph-53AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53ad CJK Ideograph-53AD | 53ac CJK Ideograph-53AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53af CJK Ideograph-53AF | 53ae CJK Ideograph-53AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53b1 CJK Ideograph-53B1 | 53b0 CJK Ideograph-53B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53b3 CJK Ideograph-53B3 | 53b2 CJK Ideograph-53B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53b5 CJK Ideograph-53B5 | 53b4 CJK Ideograph-53B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53b7 CJK Ideograph-53B7 | 53b6 CJK Ideograph-53B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53b9 CJK Ideograph-53B9 | 53b8 CJK Ideograph-53B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53bb CJK Ideograph-53BB | 53ba CJK Ideograph-53BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53bd CJK Ideograph-53BD | 53bc CJK Ideograph-53BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53bf CJK Ideograph-53BF | 53be CJK Ideograph-53BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53c1 CJK Ideograph-53C1 | 53c0 CJK Ideograph-53C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53c3 CJK Ideograph-53C3 | 53c2 CJK Ideograph-53C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53c5 CJK Ideograph-53C5 | 53c4 CJK Ideograph-53C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53c7 CJK Ideograph-53C7 | 53c6 CJK Ideograph-53C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53c9 CJK Ideograph-53C9 | 53c8 CJK Ideograph-53C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53cb CJK Ideograph-53CB | 53ca CJK Ideograph-53CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53cd CJK Ideograph-53CD | 53cc CJK Ideograph-53CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53cf CJK Ideograph-53CF | 53ce CJK Ideograph-53CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53d1 CJK Ideograph-53D1 | 53d0 CJK Ideograph-53D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53d3 CJK Ideograph-53D3 | 53d2 CJK Ideograph-53D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53d5 CJK Ideograph-53D5 | 53d4 CJK Ideograph-53D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53d7 CJK Ideograph-53D7 | 53d6 CJK Ideograph-53D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53d9 CJK Ideograph-53D9 | 53d8 CJK Ideograph-53D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53db CJK Ideograph-53DB | 53da CJK Ideograph-53DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53dd CJK Ideograph-53DD | 53dc CJK Ideograph-53DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53df CJK Ideograph-53DF | 53de CJK Ideograph-53DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53e1 CJK Ideograph-53E1 | 53e0 CJK Ideograph-53E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53e3 CJK Ideograph-53E3 | 53e2 CJK Ideograph-53E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53e5 CJK Ideograph-53E5 | 53e4 CJK Ideograph-53E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53e7 CJK Ideograph-53E7 | 53e6 CJK Ideograph-53E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53e9 CJK Ideograph-53E9 | 53e8 CJK Ideograph-53E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53eb CJK Ideograph-53EB | 53ea CJK Ideograph-53EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53ed CJK Ideograph-53ED | 53ec CJK Ideograph-53EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53ef CJK Ideograph-53EF | 53ee CJK Ideograph-53EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53f1 CJK Ideograph-53F1 | 53f0 CJK Ideograph-53F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53f3 CJK Ideograph-53F3 | 53f2 CJK Ideograph-53F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53f5 CJK Ideograph-53F5 | 53f4 CJK Ideograph-53F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53f7 CJK Ideograph-53F7 | 53f6 CJK Ideograph-53F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53f9 CJK Ideograph-53F9 | 53f8 CJK Ideograph-53F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53fb CJK Ideograph-53FB | 53fa CJK Ideograph-53FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53fd CJK Ideograph-53FD | 53fc CJK Ideograph-53FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53ff CJK Ideograph-53FF | 53fe CJK Ideograph-53FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5401 CJK Ideograph-5401 | 5400 CJK Ideograph-5400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5403 CJK Ideograph-5403 | 5402 CJK Ideograph-5402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5405 CJK Ideograph-5405 | 5404 CJK Ideograph-5404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5407 CJK Ideograph-5407 | 5406 CJK Ideograph-5406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5409 CJK Ideograph-5409 | 5408 CJK Ideograph-5408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 540b CJK Ideograph-540B | 540a CJK Ideograph-540A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 540d CJK Ideograph-540D | 540c CJK Ideograph-540C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 540f CJK Ideograph-540F | 540e CJK Ideograph-540E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5411 CJK Ideograph-5411 | 5410 CJK Ideograph-5410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5413 CJK Ideograph-5413 | 5412 CJK Ideograph-5412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5415 CJK Ideograph-5415 | 5414 CJK Ideograph-5414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5417 CJK Ideograph-5417 | 5416 CJK Ideograph-5416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5419 CJK Ideograph-5419 | 5418 CJK Ideograph-5418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 541b CJK Ideograph-541B | 541a CJK Ideograph-541A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 541d CJK Ideograph-541D | 541c CJK Ideograph-541C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 541f CJK Ideograph-541F | 541e CJK Ideograph-541E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5421 CJK Ideograph-5421 | 5420 CJK Ideograph-5420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5423 CJK Ideograph-5423 | 5422 CJK Ideograph-5422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5425 CJK Ideograph-5425 | 5424 CJK Ideograph-5424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5427 CJK Ideograph-5427 | 5426 CJK Ideograph-5426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5429 CJK Ideograph-5429 | 5428 CJK Ideograph-5428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 542b CJK Ideograph-542B | 542a CJK Ideograph-542A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 542d CJK Ideograph-542D | 542c CJK Ideograph-542C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 542f CJK Ideograph-542F | 542e CJK Ideograph-542E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5431 CJK Ideograph-5431 | 5430 CJK Ideograph-5430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5433 CJK Ideograph-5433 | 5432 CJK Ideograph-5432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5435 CJK Ideograph-5435 | 5434 CJK Ideograph-5434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5437 CJK Ideograph-5437 | 5436 CJK Ideograph-5436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5439 CJK Ideograph-5439 | 5438 CJK Ideograph-5438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 543b CJK Ideograph-543B | 543a CJK Ideograph-543A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 543d CJK Ideograph-543D | 543c CJK Ideograph-543C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 543f CJK Ideograph-543F | 543e CJK Ideograph-543E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5441 CJK Ideograph-5441 | 5440 CJK Ideograph-5440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5443 CJK Ideograph-5443 | 5442 CJK Ideograph-5442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5445 CJK Ideograph-5445 | 5444 CJK Ideograph-5444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5447 CJK Ideograph-5447 | 5446 CJK Ideograph-5446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5449 CJK Ideograph-5449 | 5448 CJK Ideograph-5448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 544b CJK Ideograph-544B | 544a CJK Ideograph-544A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 544d CJK Ideograph-544D | 544c CJK Ideograph-544C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 544f CJK Ideograph-544F | 544e CJK Ideograph-544E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5451 CJK Ideograph-5451 | 5450 CJK Ideograph-5450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5453 CJK Ideograph-5453 | 5452 CJK Ideograph-5452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5455 CJK Ideograph-5455 | 5454 CJK Ideograph-5454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5457 CJK Ideograph-5457 | 5456 CJK Ideograph-5456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5459 CJK Ideograph-5459 | 5458 CJK Ideograph-5458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 545b CJK Ideograph-545B | 545a CJK Ideograph-545A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 545d CJK Ideograph-545D | 545c CJK Ideograph-545C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 545f CJK Ideograph-545F | 545e CJK Ideograph-545E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5461 CJK Ideograph-5461 | 5460 CJK Ideograph-5460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5463 CJK Ideograph-5463 | 5462 CJK Ideograph-5462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5465 CJK Ideograph-5465 | 5464 CJK Ideograph-5464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5467 CJK Ideograph-5467 | 5466 CJK Ideograph-5466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5469 CJK Ideograph-5469 | 5468 CJK Ideograph-5468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 546b CJK Ideograph-546B | 546a CJK Ideograph-546A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 546d CJK Ideograph-546D | 546c CJK Ideograph-546C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 546f CJK Ideograph-546F | 546e CJK Ideograph-546E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5471 CJK Ideograph-5471 | 5470 CJK Ideograph-5470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5473 CJK Ideograph-5473 | 5472 CJK Ideograph-5472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5475 CJK Ideograph-5475 | 5474 CJK Ideograph-5474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5477 CJK Ideograph-5477 | 5476 CJK Ideograph-5476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5479 CJK Ideograph-5479 | 5478 CJK Ideograph-5478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 547b CJK Ideograph-547B | 547a CJK Ideograph-547A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 547d CJK Ideograph-547D | 547c CJK Ideograph-547C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 547f CJK Ideograph-547F | 547e CJK Ideograph-547E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5481 CJK Ideograph-5481 | 5480 CJK Ideograph-5480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5483 CJK Ideograph-5483 | 5482 CJK Ideograph-5482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5485 CJK Ideograph-5485 | 5484 CJK Ideograph-5484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5487 CJK Ideograph-5487 | 5486 CJK Ideograph-5486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5489 CJK Ideograph-5489 | 5488 CJK Ideograph-5488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 548b CJK Ideograph-548B | 548a CJK Ideograph-548A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 548d CJK Ideograph-548D | 548c CJK Ideograph-548C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 548f CJK Ideograph-548F | 548e CJK Ideograph-548E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5491 CJK Ideograph-5491 | 5490 CJK Ideograph-5490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5493 CJK Ideograph-5493 | 5492 CJK Ideograph-5492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5495 CJK Ideograph-5495 | 5494 CJK Ideograph-5494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5497 CJK Ideograph-5497 | 5496 CJK Ideograph-5496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5499 CJK Ideograph-5499 | 5498 CJK Ideograph-5498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 549b CJK Ideograph-549B | 549a CJK Ideograph-549A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 549d CJK Ideograph-549D | 549c CJK Ideograph-549C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 549f CJK Ideograph-549F | 549e CJK Ideograph-549E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54a1 CJK Ideograph-54A1 | 54a0 CJK Ideograph-54A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54a3 CJK Ideograph-54A3 | 54a2 CJK Ideograph-54A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54a5 CJK Ideograph-54A5 | 54a4 CJK Ideograph-54A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54a7 CJK Ideograph-54A7 | 54a6 CJK Ideograph-54A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54a9 CJK Ideograph-54A9 | 54a8 CJK Ideograph-54A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54ab CJK Ideograph-54AB | 54aa CJK Ideograph-54AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54ad CJK Ideograph-54AD | 54ac CJK Ideograph-54AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54af CJK Ideograph-54AF | 54ae CJK Ideograph-54AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54b1 CJK Ideograph-54B1 | 54b0 CJK Ideograph-54B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54b3 CJK Ideograph-54B3 | 54b2 CJK Ideograph-54B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54b5 CJK Ideograph-54B5 | 54b4 CJK Ideograph-54B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54b7 CJK Ideograph-54B7 | 54b6 CJK Ideograph-54B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54b9 CJK Ideograph-54B9 | 54b8 CJK Ideograph-54B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54bb CJK Ideograph-54BB | 54ba CJK Ideograph-54BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54bd CJK Ideograph-54BD | 54bc CJK Ideograph-54BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54bf CJK Ideograph-54BF | 54be CJK Ideograph-54BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54c1 CJK Ideograph-54C1 | 54c0 CJK Ideograph-54C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54c3 CJK Ideograph-54C3 | 54c2 CJK Ideograph-54C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54c5 CJK Ideograph-54C5 | 54c4 CJK Ideograph-54C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54c7 CJK Ideograph-54C7 | 54c6 CJK Ideograph-54C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54c9 CJK Ideograph-54C9 | 54c8 CJK Ideograph-54C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54cb CJK Ideograph-54CB | 54ca CJK Ideograph-54CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54cd CJK Ideograph-54CD | 54cc CJK Ideograph-54CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54cf CJK Ideograph-54CF | 54ce CJK Ideograph-54CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54d1 CJK Ideograph-54D1 | 54d0 CJK Ideograph-54D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54d3 CJK Ideograph-54D3 | 54d2 CJK Ideograph-54D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54d5 CJK Ideograph-54D5 | 54d4 CJK Ideograph-54D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54d7 CJK Ideograph-54D7 | 54d6 CJK Ideograph-54D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54d9 CJK Ideograph-54D9 | 54d8 CJK Ideograph-54D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54db CJK Ideograph-54DB | 54da CJK Ideograph-54DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54dd CJK Ideograph-54DD | 54dc CJK Ideograph-54DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54df CJK Ideograph-54DF | 54de CJK Ideograph-54DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54e1 CJK Ideograph-54E1 | 54e0 CJK Ideograph-54E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54e3 CJK Ideograph-54E3 | 54e2 CJK Ideograph-54E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54e5 CJK Ideograph-54E5 | 54e4 CJK Ideograph-54E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54e7 CJK Ideograph-54E7 | 54e6 CJK Ideograph-54E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54e9 CJK Ideograph-54E9 | 54e8 CJK Ideograph-54E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54eb CJK Ideograph-54EB | 54ea CJK Ideograph-54EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54ed CJK Ideograph-54ED | 54ec CJK Ideograph-54EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54ef CJK Ideograph-54EF | 54ee CJK Ideograph-54EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54f1 CJK Ideograph-54F1 | 54f0 CJK Ideograph-54F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54f3 CJK Ideograph-54F3 | 54f2 CJK Ideograph-54F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54f5 CJK Ideograph-54F5 | 54f4 CJK Ideograph-54F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54f7 CJK Ideograph-54F7 | 54f6 CJK Ideograph-54F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54f9 CJK Ideograph-54F9 | 54f8 CJK Ideograph-54F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54fb CJK Ideograph-54FB | 54fa CJK Ideograph-54FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54fd CJK Ideograph-54FD | 54fc CJK Ideograph-54FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54ff CJK Ideograph-54FF | 54fe CJK Ideograph-54FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5501 CJK Ideograph-5501 | 5500 CJK Ideograph-5500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5503 CJK Ideograph-5503 | 5502 CJK Ideograph-5502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5505 CJK Ideograph-5505 | 5504 CJK Ideograph-5504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5507 CJK Ideograph-5507 | 5506 CJK Ideograph-5506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5509 CJK Ideograph-5509 | 5508 CJK Ideograph-5508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 550b CJK Ideograph-550B | 550a CJK Ideograph-550A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 550d CJK Ideograph-550D | 550c CJK Ideograph-550C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 550f CJK Ideograph-550F | 550e CJK Ideograph-550E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5511 CJK Ideograph-5511 | 5510 CJK Ideograph-5510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5513 CJK Ideograph-5513 | 5512 CJK Ideograph-5512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5515 CJK Ideograph-5515 | 5514 CJK Ideograph-5514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5517 CJK Ideograph-5517 | 5516 CJK Ideograph-5516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5519 CJK Ideograph-5519 | 5518 CJK Ideograph-5518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 551b CJK Ideograph-551B | 551a CJK Ideograph-551A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 551d CJK Ideograph-551D | 551c CJK Ideograph-551C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 551f CJK Ideograph-551F | 551e CJK Ideograph-551E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5521 CJK Ideograph-5521 | 5520 CJK Ideograph-5520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5523 CJK Ideograph-5523 | 5522 CJK Ideograph-5522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5525 CJK Ideograph-5525 | 5524 CJK Ideograph-5524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5527 CJK Ideograph-5527 | 5526 CJK Ideograph-5526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5529 CJK Ideograph-5529 | 5528 CJK Ideograph-5528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 552b CJK Ideograph-552B | 552a CJK Ideograph-552A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 552d CJK Ideograph-552D | 552c CJK Ideograph-552C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 552f CJK Ideograph-552F | 552e CJK Ideograph-552E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5531 CJK Ideograph-5531 | 5530 CJK Ideograph-5530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5533 CJK Ideograph-5533 | 5532 CJK Ideograph-5532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5535 CJK Ideograph-5535 | 5534 CJK Ideograph-5534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5537 CJK Ideograph-5537 | 5536 CJK Ideograph-5536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5539 CJK Ideograph-5539 | 5538 CJK Ideograph-5538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 553b CJK Ideograph-553B | 553a CJK Ideograph-553A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 553d CJK Ideograph-553D | 553c CJK Ideograph-553C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 553f CJK Ideograph-553F | 553e CJK Ideograph-553E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5541 CJK Ideograph-5541 | 5540 CJK Ideograph-5540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5543 CJK Ideograph-5543 | 5542 CJK Ideograph-5542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5545 CJK Ideograph-5545 | 5544 CJK Ideograph-5544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5547 CJK Ideograph-5547 | 5546 CJK Ideograph-5546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5549 CJK Ideograph-5549 | 5548 CJK Ideograph-5548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 554b CJK Ideograph-554B | 554a CJK Ideograph-554A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 554d CJK Ideograph-554D | 554c CJK Ideograph-554C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 554f CJK Ideograph-554F | 554e CJK Ideograph-554E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5551 CJK Ideograph-5551 | 5550 CJK Ideograph-5550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5553 CJK Ideograph-5553 | 5552 CJK Ideograph-5552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5555 CJK Ideograph-5555 | 5554 CJK Ideograph-5554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5557 CJK Ideograph-5557 | 5556 CJK Ideograph-5556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5559 CJK Ideograph-5559 | 5558 CJK Ideograph-5558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 555b CJK Ideograph-555B | 555a CJK Ideograph-555A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 555d CJK Ideograph-555D | 555c CJK Ideograph-555C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 555f CJK Ideograph-555F | 555e CJK Ideograph-555E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5561 CJK Ideograph-5561 | 5560 CJK Ideograph-5560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5563 CJK Ideograph-5563 | 5562 CJK Ideograph-5562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5565 CJK Ideograph-5565 | 5564 CJK Ideograph-5564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5567 CJK Ideograph-5567 | 5566 CJK Ideograph-5566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5569 CJK Ideograph-5569 | 5568 CJK Ideograph-5568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 556b CJK Ideograph-556B | 556a CJK Ideograph-556A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 556d CJK Ideograph-556D | 556c CJK Ideograph-556C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 556f CJK Ideograph-556F | 556e CJK Ideograph-556E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5571 CJK Ideograph-5571 | 5570 CJK Ideograph-5570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5573 CJK Ideograph-5573 | 5572 CJK Ideograph-5572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5575 CJK Ideograph-5575 | 5574 CJK Ideograph-5574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5577 CJK Ideograph-5577 | 5576 CJK Ideograph-5576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5579 CJK Ideograph-5579 | 5578 CJK Ideograph-5578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 557b CJK Ideograph-557B | 557a CJK Ideograph-557A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 557d CJK Ideograph-557D | 557c CJK Ideograph-557C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 557f CJK Ideograph-557F | 557e CJK Ideograph-557E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5581 CJK Ideograph-5581 | 5580 CJK Ideograph-5580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5583 CJK Ideograph-5583 | 5582 CJK Ideograph-5582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5585 CJK Ideograph-5585 | 5584 CJK Ideograph-5584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5587 CJK Ideograph-5587 | 5586 CJK Ideograph-5586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5589 CJK Ideograph-5589 | 5588 CJK Ideograph-5588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 558b CJK Ideograph-558B | 558a CJK Ideograph-558A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 558d CJK Ideograph-558D | 558c CJK Ideograph-558C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 558f CJK Ideograph-558F | 558e CJK Ideograph-558E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5591 CJK Ideograph-5591 | 5590 CJK Ideograph-5590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5593 CJK Ideograph-5593 | 5592 CJK Ideograph-5592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5595 CJK Ideograph-5595 | 5594 CJK Ideograph-5594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5597 CJK Ideograph-5597 | 5596 CJK Ideograph-5596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5599 CJK Ideograph-5599 | 5598 CJK Ideograph-5598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 559b CJK Ideograph-559B | 559a CJK Ideograph-559A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 559d CJK Ideograph-559D | 559c CJK Ideograph-559C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 559f CJK Ideograph-559F | 559e CJK Ideograph-559E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55a1 CJK Ideograph-55A1 | 55a0 CJK Ideograph-55A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55a3 CJK Ideograph-55A3 | 55a2 CJK Ideograph-55A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55a5 CJK Ideograph-55A5 | 55a4 CJK Ideograph-55A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55a7 CJK Ideograph-55A7 | 55a6 CJK Ideograph-55A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55a9 CJK Ideograph-55A9 | 55a8 CJK Ideograph-55A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55ab CJK Ideograph-55AB | 55aa CJK Ideograph-55AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55ad CJK Ideograph-55AD | 55ac CJK Ideograph-55AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55af CJK Ideograph-55AF | 55ae CJK Ideograph-55AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55b1 CJK Ideograph-55B1 | 55b0 CJK Ideograph-55B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55b3 CJK Ideograph-55B3 | 55b2 CJK Ideograph-55B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55b5 CJK Ideograph-55B5 | 55b4 CJK Ideograph-55B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55b7 CJK Ideograph-55B7 | 55b6 CJK Ideograph-55B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55b9 CJK Ideograph-55B9 | 55b8 CJK Ideograph-55B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55bb CJK Ideograph-55BB | 55ba CJK Ideograph-55BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55bd CJK Ideograph-55BD | 55bc CJK Ideograph-55BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55bf CJK Ideograph-55BF | 55be CJK Ideograph-55BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55c1 CJK Ideograph-55C1 | 55c0 CJK Ideograph-55C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55c3 CJK Ideograph-55C3 | 55c2 CJK Ideograph-55C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55c5 CJK Ideograph-55C5 | 55c4 CJK Ideograph-55C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55c7 CJK Ideograph-55C7 | 55c6 CJK Ideograph-55C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55c9 CJK Ideograph-55C9 | 55c8 CJK Ideograph-55C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55cb CJK Ideograph-55CB | 55ca CJK Ideograph-55CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55cd CJK Ideograph-55CD | 55cc CJK Ideograph-55CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55cf CJK Ideograph-55CF | 55ce CJK Ideograph-55CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55d1 CJK Ideograph-55D1 | 55d0 CJK Ideograph-55D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55d3 CJK Ideograph-55D3 | 55d2 CJK Ideograph-55D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55d5 CJK Ideograph-55D5 | 55d4 CJK Ideograph-55D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55d7 CJK Ideograph-55D7 | 55d6 CJK Ideograph-55D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55d9 CJK Ideograph-55D9 | 55d8 CJK Ideograph-55D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55db CJK Ideograph-55DB | 55da CJK Ideograph-55DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55dd CJK Ideograph-55DD | 55dc CJK Ideograph-55DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55df CJK Ideograph-55DF | 55de CJK Ideograph-55DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55e1 CJK Ideograph-55E1 | 55e0 CJK Ideograph-55E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55e3 CJK Ideograph-55E3 | 55e2 CJK Ideograph-55E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55e5 CJK Ideograph-55E5 | 55e4 CJK Ideograph-55E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55e7 CJK Ideograph-55E7 | 55e6 CJK Ideograph-55E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55e9 CJK Ideograph-55E9 | 55e8 CJK Ideograph-55E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55eb CJK Ideograph-55EB | 55ea CJK Ideograph-55EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55ed CJK Ideograph-55ED | 55ec CJK Ideograph-55EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55ef CJK Ideograph-55EF | 55ee CJK Ideograph-55EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55f1 CJK Ideograph-55F1 | 55f0 CJK Ideograph-55F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55f3 CJK Ideograph-55F3 | 55f2 CJK Ideograph-55F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55f5 CJK Ideograph-55F5 | 55f4 CJK Ideograph-55F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55f7 CJK Ideograph-55F7 | 55f6 CJK Ideograph-55F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55f9 CJK Ideograph-55F9 | 55f8 CJK Ideograph-55F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55fb CJK Ideograph-55FB | 55fa CJK Ideograph-55FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55fd CJK Ideograph-55FD | 55fc CJK Ideograph-55FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55ff CJK Ideograph-55FF | 55fe CJK Ideograph-55FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5601 CJK Ideograph-5601 | 5600 CJK Ideograph-5600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5603 CJK Ideograph-5603 | 5602 CJK Ideograph-5602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5605 CJK Ideograph-5605 | 5604 CJK Ideograph-5604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5607 CJK Ideograph-5607 | 5606 CJK Ideograph-5606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5609 CJK Ideograph-5609 | 5608 CJK Ideograph-5608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 560b CJK Ideograph-560B | 560a CJK Ideograph-560A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 560d CJK Ideograph-560D | 560c CJK Ideograph-560C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 560f CJK Ideograph-560F | 560e CJK Ideograph-560E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5611 CJK Ideograph-5611 | 5610 CJK Ideograph-5610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5613 CJK Ideograph-5613 | 5612 CJK Ideograph-5612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5615 CJK Ideograph-5615 | 5614 CJK Ideograph-5614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5617 CJK Ideograph-5617 | 5616 CJK Ideograph-5616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5619 CJK Ideograph-5619 | 5618 CJK Ideograph-5618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 561b CJK Ideograph-561B | 561a CJK Ideograph-561A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 561d CJK Ideograph-561D | 561c CJK Ideograph-561C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 561f CJK Ideograph-561F | 561e CJK Ideograph-561E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5621 CJK Ideograph-5621 | 5620 CJK Ideograph-5620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5623 CJK Ideograph-5623 | 5622 CJK Ideograph-5622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5625 CJK Ideograph-5625 | 5624 CJK Ideograph-5624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5627 CJK Ideograph-5627 | 5626 CJK Ideograph-5626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5629 CJK Ideograph-5629 | 5628 CJK Ideograph-5628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 562b CJK Ideograph-562B | 562a CJK Ideograph-562A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 562d CJK Ideograph-562D | 562c CJK Ideograph-562C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 562f CJK Ideograph-562F | 562e CJK Ideograph-562E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5631 CJK Ideograph-5631 | 5630 CJK Ideograph-5630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5633 CJK Ideograph-5633 | 5632 CJK Ideograph-5632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5635 CJK Ideograph-5635 | 5634 CJK Ideograph-5634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5637 CJK Ideograph-5637 | 5636 CJK Ideograph-5636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5639 CJK Ideograph-5639 | 5638 CJK Ideograph-5638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 563b CJK Ideograph-563B | 563a CJK Ideograph-563A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 563d CJK Ideograph-563D | 563c CJK Ideograph-563C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 563f CJK Ideograph-563F | 563e CJK Ideograph-563E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5641 CJK Ideograph-5641 | 5640 CJK Ideograph-5640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5643 CJK Ideograph-5643 | 5642 CJK Ideograph-5642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5645 CJK Ideograph-5645 | 5644 CJK Ideograph-5644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5647 CJK Ideograph-5647 | 5646 CJK Ideograph-5646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5649 CJK Ideograph-5649 | 5648 CJK Ideograph-5648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 564b CJK Ideograph-564B | 564a CJK Ideograph-564A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 564d CJK Ideograph-564D | 564c CJK Ideograph-564C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 564f CJK Ideograph-564F | 564e CJK Ideograph-564E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5651 CJK Ideograph-5651 | 5650 CJK Ideograph-5650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5653 CJK Ideograph-5653 | 5652 CJK Ideograph-5652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5655 CJK Ideograph-5655 | 5654 CJK Ideograph-5654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5657 CJK Ideograph-5657 | 5656 CJK Ideograph-5656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5659 CJK Ideograph-5659 | 5658 CJK Ideograph-5658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 565b CJK Ideograph-565B | 565a CJK Ideograph-565A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 565d CJK Ideograph-565D | 565c CJK Ideograph-565C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 565f CJK Ideograph-565F | 565e CJK Ideograph-565E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5661 CJK Ideograph-5661 | 5660 CJK Ideograph-5660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5663 CJK Ideograph-5663 | 5662 CJK Ideograph-5662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5665 CJK Ideograph-5665 | 5664 CJK Ideograph-5664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5667 CJK Ideograph-5667 | 5666 CJK Ideograph-5666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5669 CJK Ideograph-5669 | 5668 CJK Ideograph-5668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 566b CJK Ideograph-566B | 566a CJK Ideograph-566A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 566d CJK Ideograph-566D | 566c CJK Ideograph-566C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 566f CJK Ideograph-566F | 566e CJK Ideograph-566E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5671 CJK Ideograph-5671 | 5670 CJK Ideograph-5670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5673 CJK Ideograph-5673 | 5672 CJK Ideograph-5672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5675 CJK Ideograph-5675 | 5674 CJK Ideograph-5674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5677 CJK Ideograph-5677 | 5676 CJK Ideograph-5676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5679 CJK Ideograph-5679 | 5678 CJK Ideograph-5678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 567b CJK Ideograph-567B | 567a CJK Ideograph-567A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 567d CJK Ideograph-567D | 567c CJK Ideograph-567C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 567f CJK Ideograph-567F | 567e CJK Ideograph-567E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5681 CJK Ideograph-5681 | 5680 CJK Ideograph-5680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5683 CJK Ideograph-5683 | 5682 CJK Ideograph-5682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5685 CJK Ideograph-5685 | 5684 CJK Ideograph-5684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5687 CJK Ideograph-5687 | 5686 CJK Ideograph-5686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5689 CJK Ideograph-5689 | 5688 CJK Ideograph-5688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 568b CJK Ideograph-568B | 568a CJK Ideograph-568A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 568d CJK Ideograph-568D | 568c CJK Ideograph-568C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 568f CJK Ideograph-568F | 568e CJK Ideograph-568E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5691 CJK Ideograph-5691 | 5690 CJK Ideograph-5690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5693 CJK Ideograph-5693 | 5692 CJK Ideograph-5692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5695 CJK Ideograph-5695 | 5694 CJK Ideograph-5694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5697 CJK Ideograph-5697 | 5696 CJK Ideograph-5696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5699 CJK Ideograph-5699 | 5698 CJK Ideograph-5698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 569b CJK Ideograph-569B | 569a CJK Ideograph-569A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 569d CJK Ideograph-569D | 569c CJK Ideograph-569C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 569f CJK Ideograph-569F | 569e CJK Ideograph-569E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56a1 CJK Ideograph-56A1 | 56a0 CJK Ideograph-56A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56a3 CJK Ideograph-56A3 | 56a2 CJK Ideograph-56A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56a5 CJK Ideograph-56A5 | 56a4 CJK Ideograph-56A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56a7 CJK Ideograph-56A7 | 56a6 CJK Ideograph-56A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56a9 CJK Ideograph-56A9 | 56a8 CJK Ideograph-56A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56ab CJK Ideograph-56AB | 56aa CJK Ideograph-56AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56ad CJK Ideograph-56AD | 56ac CJK Ideograph-56AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56af CJK Ideograph-56AF | 56ae CJK Ideograph-56AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56b1 CJK Ideograph-56B1 | 56b0 CJK Ideograph-56B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56b3 CJK Ideograph-56B3 | 56b2 CJK Ideograph-56B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56b5 CJK Ideograph-56B5 | 56b4 CJK Ideograph-56B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56b7 CJK Ideograph-56B7 | 56b6 CJK Ideograph-56B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56b9 CJK Ideograph-56B9 | 56b8 CJK Ideograph-56B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56bb CJK Ideograph-56BB | 56ba CJK Ideograph-56BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56bd CJK Ideograph-56BD | 56bc CJK Ideograph-56BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56bf CJK Ideograph-56BF | 56be CJK Ideograph-56BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56c1 CJK Ideograph-56C1 | 56c0 CJK Ideograph-56C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56c3 CJK Ideograph-56C3 | 56c2 CJK Ideograph-56C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56c5 CJK Ideograph-56C5 | 56c4 CJK Ideograph-56C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56c7 CJK Ideograph-56C7 | 56c6 CJK Ideograph-56C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56c9 CJK Ideograph-56C9 | 56c8 CJK Ideograph-56C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56cb CJK Ideograph-56CB | 56ca CJK Ideograph-56CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56cd CJK Ideograph-56CD | 56cc CJK Ideograph-56CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56cf CJK Ideograph-56CF | 56ce CJK Ideograph-56CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56d1 CJK Ideograph-56D1 | 56d0 CJK Ideograph-56D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56d3 CJK Ideograph-56D3 | 56d2 CJK Ideograph-56D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56d5 CJK Ideograph-56D5 | 56d4 CJK Ideograph-56D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56d7 CJK Ideograph-56D7 | 56d6 CJK Ideograph-56D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56d9 CJK Ideograph-56D9 | 56d8 CJK Ideograph-56D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56db CJK Ideograph-56DB | 56da CJK Ideograph-56DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56dd CJK Ideograph-56DD | 56dc CJK Ideograph-56DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56df CJK Ideograph-56DF | 56de CJK Ideograph-56DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56e1 CJK Ideograph-56E1 | 56e0 CJK Ideograph-56E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56e3 CJK Ideograph-56E3 | 56e2 CJK Ideograph-56E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56e5 CJK Ideograph-56E5 | 56e4 CJK Ideograph-56E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56e7 CJK Ideograph-56E7 | 56e6 CJK Ideograph-56E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56e9 CJK Ideograph-56E9 | 56e8 CJK Ideograph-56E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56eb CJK Ideograph-56EB | 56ea CJK Ideograph-56EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56ed CJK Ideograph-56ED | 56ec CJK Ideograph-56EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56ef CJK Ideograph-56EF | 56ee CJK Ideograph-56EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56f1 CJK Ideograph-56F1 | 56f0 CJK Ideograph-56F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56f3 CJK Ideograph-56F3 | 56f2 CJK Ideograph-56F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56f5 CJK Ideograph-56F5 | 56f4 CJK Ideograph-56F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56f7 CJK Ideograph-56F7 | 56f6 CJK Ideograph-56F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56f9 CJK Ideograph-56F9 | 56f8 CJK Ideograph-56F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56fb CJK Ideograph-56FB | 56fa CJK Ideograph-56FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56fd CJK Ideograph-56FD | 56fc CJK Ideograph-56FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56ff CJK Ideograph-56FF | 56fe CJK Ideograph-56FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5701 CJK Ideograph-5701 | 5700 CJK Ideograph-5700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5703 CJK Ideograph-5703 | 5702 CJK Ideograph-5702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5705 CJK Ideograph-5705 | 5704 CJK Ideograph-5704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5707 CJK Ideograph-5707 | 5706 CJK Ideograph-5706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5709 CJK Ideograph-5709 | 5708 CJK Ideograph-5708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 570b CJK Ideograph-570B | 570a CJK Ideograph-570A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 570d CJK Ideograph-570D | 570c CJK Ideograph-570C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 570f CJK Ideograph-570F | 570e CJK Ideograph-570E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5711 CJK Ideograph-5711 | 5710 CJK Ideograph-5710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5713 CJK Ideograph-5713 | 5712 CJK Ideograph-5712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5715 CJK Ideograph-5715 | 5714 CJK Ideograph-5714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5717 CJK Ideograph-5717 | 5716 CJK Ideograph-5716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5719 CJK Ideograph-5719 | 5718 CJK Ideograph-5718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 571b CJK Ideograph-571B | 571a CJK Ideograph-571A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 571d CJK Ideograph-571D | 571c CJK Ideograph-571C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 571f CJK Ideograph-571F | 571e CJK Ideograph-571E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5721 CJK Ideograph-5721 | 5720 CJK Ideograph-5720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5723 CJK Ideograph-5723 | 5722 CJK Ideograph-5722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5725 CJK Ideograph-5725 | 5724 CJK Ideograph-5724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5727 CJK Ideograph-5727 | 5726 CJK Ideograph-5726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5729 CJK Ideograph-5729 | 5728 CJK Ideograph-5728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 572b CJK Ideograph-572B | 572a CJK Ideograph-572A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 572d CJK Ideograph-572D | 572c CJK Ideograph-572C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 572f CJK Ideograph-572F | 572e CJK Ideograph-572E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5731 CJK Ideograph-5731 | 5730 CJK Ideograph-5730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5733 CJK Ideograph-5733 | 5732 CJK Ideograph-5732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5735 CJK Ideograph-5735 | 5734 CJK Ideograph-5734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5737 CJK Ideograph-5737 | 5736 CJK Ideograph-5736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5739 CJK Ideograph-5739 | 5738 CJK Ideograph-5738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 573b CJK Ideograph-573B | 573a CJK Ideograph-573A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 573d CJK Ideograph-573D | 573c CJK Ideograph-573C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 573f CJK Ideograph-573F | 573e CJK Ideograph-573E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5741 CJK Ideograph-5741 | 5740 CJK Ideograph-5740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5743 CJK Ideograph-5743 | 5742 CJK Ideograph-5742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5745 CJK Ideograph-5745 | 5744 CJK Ideograph-5744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5747 CJK Ideograph-5747 | 5746 CJK Ideograph-5746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5749 CJK Ideograph-5749 | 5748 CJK Ideograph-5748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 574b CJK Ideograph-574B | 574a CJK Ideograph-574A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 574d CJK Ideograph-574D | 574c CJK Ideograph-574C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 574f CJK Ideograph-574F | 574e CJK Ideograph-574E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5751 CJK Ideograph-5751 | 5750 CJK Ideograph-5750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5753 CJK Ideograph-5753 | 5752 CJK Ideograph-5752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5755 CJK Ideograph-5755 | 5754 CJK Ideograph-5754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5757 CJK Ideograph-5757 | 5756 CJK Ideograph-5756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5759 CJK Ideograph-5759 | 5758 CJK Ideograph-5758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 575b CJK Ideograph-575B | 575a CJK Ideograph-575A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 575d CJK Ideograph-575D | 575c CJK Ideograph-575C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 575f CJK Ideograph-575F | 575e CJK Ideograph-575E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5761 CJK Ideograph-5761 | 5760 CJK Ideograph-5760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5763 CJK Ideograph-5763 | 5762 CJK Ideograph-5762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5765 CJK Ideograph-5765 | 5764 CJK Ideograph-5764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5767 CJK Ideograph-5767 | 5766 CJK Ideograph-5766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5769 CJK Ideograph-5769 | 5768 CJK Ideograph-5768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 576b CJK Ideograph-576B | 576a CJK Ideograph-576A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 576d CJK Ideograph-576D | 576c CJK Ideograph-576C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 576f CJK Ideograph-576F | 576e CJK Ideograph-576E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5771 CJK Ideograph-5771 | 5770 CJK Ideograph-5770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5773 CJK Ideograph-5773 | 5772 CJK Ideograph-5772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5775 CJK Ideograph-5775 | 5774 CJK Ideograph-5774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5777 CJK Ideograph-5777 | 5776 CJK Ideograph-5776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5779 CJK Ideograph-5779 | 5778 CJK Ideograph-5778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 577b CJK Ideograph-577B | 577a CJK Ideograph-577A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 577d CJK Ideograph-577D | 577c CJK Ideograph-577C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 577f CJK Ideograph-577F | 577e CJK Ideograph-577E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5781 CJK Ideograph-5781 | 5780 CJK Ideograph-5780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5783 CJK Ideograph-5783 | 5782 CJK Ideograph-5782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5785 CJK Ideograph-5785 | 5784 CJK Ideograph-5784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5787 CJK Ideograph-5787 | 5786 CJK Ideograph-5786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5789 CJK Ideograph-5789 | 5788 CJK Ideograph-5788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 578b CJK Ideograph-578B | 578a CJK Ideograph-578A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 578d CJK Ideograph-578D | 578c CJK Ideograph-578C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 578f CJK Ideograph-578F | 578e CJK Ideograph-578E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5791 CJK Ideograph-5791 | 5790 CJK Ideograph-5790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5793 CJK Ideograph-5793 | 5792 CJK Ideograph-5792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5795 CJK Ideograph-5795 | 5794 CJK Ideograph-5794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5797 CJK Ideograph-5797 | 5796 CJK Ideograph-5796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5799 CJK Ideograph-5799 | 5798 CJK Ideograph-5798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 579b CJK Ideograph-579B | 579a CJK Ideograph-579A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 579d CJK Ideograph-579D | 579c CJK Ideograph-579C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 579f CJK Ideograph-579F | 579e CJK Ideograph-579E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57a1 CJK Ideograph-57A1 | 57a0 CJK Ideograph-57A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57a3 CJK Ideograph-57A3 | 57a2 CJK Ideograph-57A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57a5 CJK Ideograph-57A5 | 57a4 CJK Ideograph-57A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57a7 CJK Ideograph-57A7 | 57a6 CJK Ideograph-57A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57a9 CJK Ideograph-57A9 | 57a8 CJK Ideograph-57A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57ab CJK Ideograph-57AB | 57aa CJK Ideograph-57AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57ad CJK Ideograph-57AD | 57ac CJK Ideograph-57AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57af CJK Ideograph-57AF | 57ae CJK Ideograph-57AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57b1 CJK Ideograph-57B1 | 57b0 CJK Ideograph-57B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57b3 CJK Ideograph-57B3 | 57b2 CJK Ideograph-57B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57b5 CJK Ideograph-57B5 | 57b4 CJK Ideograph-57B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57b7 CJK Ideograph-57B7 | 57b6 CJK Ideograph-57B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57b9 CJK Ideograph-57B9 | 57b8 CJK Ideograph-57B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57bb CJK Ideograph-57BB | 57ba CJK Ideograph-57BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57bd CJK Ideograph-57BD | 57bc CJK Ideograph-57BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57bf CJK Ideograph-57BF | 57be CJK Ideograph-57BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57c1 CJK Ideograph-57C1 | 57c0 CJK Ideograph-57C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57c3 CJK Ideograph-57C3 | 57c2 CJK Ideograph-57C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57c5 CJK Ideograph-57C5 | 57c4 CJK Ideograph-57C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57c7 CJK Ideograph-57C7 | 57c6 CJK Ideograph-57C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57c9 CJK Ideograph-57C9 | 57c8 CJK Ideograph-57C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57cb CJK Ideograph-57CB | 57ca CJK Ideograph-57CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57cd CJK Ideograph-57CD | 57cc CJK Ideograph-57CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57cf CJK Ideograph-57CF | 57ce CJK Ideograph-57CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57d1 CJK Ideograph-57D1 | 57d0 CJK Ideograph-57D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57d3 CJK Ideograph-57D3 | 57d2 CJK Ideograph-57D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57d5 CJK Ideograph-57D5 | 57d4 CJK Ideograph-57D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57d7 CJK Ideograph-57D7 | 57d6 CJK Ideograph-57D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57d9 CJK Ideograph-57D9 | 57d8 CJK Ideograph-57D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57db CJK Ideograph-57DB | 57da CJK Ideograph-57DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57dd CJK Ideograph-57DD | 57dc CJK Ideograph-57DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57df CJK Ideograph-57DF | 57de CJK Ideograph-57DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57e1 CJK Ideograph-57E1 | 57e0 CJK Ideograph-57E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57e3 CJK Ideograph-57E3 | 57e2 CJK Ideograph-57E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57e5 CJK Ideograph-57E5 | 57e4 CJK Ideograph-57E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57e7 CJK Ideograph-57E7 | 57e6 CJK Ideograph-57E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57e9 CJK Ideograph-57E9 | 57e8 CJK Ideograph-57E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57eb CJK Ideograph-57EB | 57ea CJK Ideograph-57EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57ed CJK Ideograph-57ED | 57ec CJK Ideograph-57EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57ef CJK Ideograph-57EF | 57ee CJK Ideograph-57EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57f1 CJK Ideograph-57F1 | 57f0 CJK Ideograph-57F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57f3 CJK Ideograph-57F3 | 57f2 CJK Ideograph-57F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57f5 CJK Ideograph-57F5 | 57f4 CJK Ideograph-57F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57f7 CJK Ideograph-57F7 | 57f6 CJK Ideograph-57F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57f9 CJK Ideograph-57F9 | 57f8 CJK Ideograph-57F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57fb CJK Ideograph-57FB | 57fa CJK Ideograph-57FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57fd CJK Ideograph-57FD | 57fc CJK Ideograph-57FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57ff CJK Ideograph-57FF | 57fe CJK Ideograph-57FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5801 CJK Ideograph-5801 | 5800 CJK Ideograph-5800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5803 CJK Ideograph-5803 | 5802 CJK Ideograph-5802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5805 CJK Ideograph-5805 | 5804 CJK Ideograph-5804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5807 CJK Ideograph-5807 | 5806 CJK Ideograph-5806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5809 CJK Ideograph-5809 | 5808 CJK Ideograph-5808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 580b CJK Ideograph-580B | 580a CJK Ideograph-580A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 580d CJK Ideograph-580D | 580c CJK Ideograph-580C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 580f CJK Ideograph-580F | 580e CJK Ideograph-580E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5811 CJK Ideograph-5811 | 5810 CJK Ideograph-5810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5813 CJK Ideograph-5813 | 5812 CJK Ideograph-5812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5815 CJK Ideograph-5815 | 5814 CJK Ideograph-5814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5817 CJK Ideograph-5817 | 5816 CJK Ideograph-5816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5819 CJK Ideograph-5819 | 5818 CJK Ideograph-5818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 581b CJK Ideograph-581B | 581a CJK Ideograph-581A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 581d CJK Ideograph-581D | 581c CJK Ideograph-581C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 581f CJK Ideograph-581F | 581e CJK Ideograph-581E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5821 CJK Ideograph-5821 | 5820 CJK Ideograph-5820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5823 CJK Ideograph-5823 | 5822 CJK Ideograph-5822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5825 CJK Ideograph-5825 | 5824 CJK Ideograph-5824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5827 CJK Ideograph-5827 | 5826 CJK Ideograph-5826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5829 CJK Ideograph-5829 | 5828 CJK Ideograph-5828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 582b CJK Ideograph-582B | 582a CJK Ideograph-582A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 582d CJK Ideograph-582D | 582c CJK Ideograph-582C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 582f CJK Ideograph-582F | 582e CJK Ideograph-582E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5831 CJK Ideograph-5831 | 5830 CJK Ideograph-5830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5833 CJK Ideograph-5833 | 5832 CJK Ideograph-5832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5835 CJK Ideograph-5835 | 5834 CJK Ideograph-5834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5837 CJK Ideograph-5837 | 5836 CJK Ideograph-5836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5839 CJK Ideograph-5839 | 5838 CJK Ideograph-5838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 583b CJK Ideograph-583B | 583a CJK Ideograph-583A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 583d CJK Ideograph-583D | 583c CJK Ideograph-583C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 583f CJK Ideograph-583F | 583e CJK Ideograph-583E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5841 CJK Ideograph-5841 | 5840 CJK Ideograph-5840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5843 CJK Ideograph-5843 | 5842 CJK Ideograph-5842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5845 CJK Ideograph-5845 | 5844 CJK Ideograph-5844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5847 CJK Ideograph-5847 | 5846 CJK Ideograph-5846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5849 CJK Ideograph-5849 | 5848 CJK Ideograph-5848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 584b CJK Ideograph-584B | 584a CJK Ideograph-584A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 584d CJK Ideograph-584D | 584c CJK Ideograph-584C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 584f CJK Ideograph-584F | 584e CJK Ideograph-584E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5851 CJK Ideograph-5851 | 5850 CJK Ideograph-5850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5853 CJK Ideograph-5853 | 5852 CJK Ideograph-5852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5855 CJK Ideograph-5855 | 5854 CJK Ideograph-5854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5857 CJK Ideograph-5857 | 5856 CJK Ideograph-5856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5859 CJK Ideograph-5859 | 5858 CJK Ideograph-5858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 585b CJK Ideograph-585B | 585a CJK Ideograph-585A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 585d CJK Ideograph-585D | 585c CJK Ideograph-585C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 585f CJK Ideograph-585F | 585e CJK Ideograph-585E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5861 CJK Ideograph-5861 | 5860 CJK Ideograph-5860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5863 CJK Ideograph-5863 | 5862 CJK Ideograph-5862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5865 CJK Ideograph-5865 | 5864 CJK Ideograph-5864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5867 CJK Ideograph-5867 | 5866 CJK Ideograph-5866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5869 CJK Ideograph-5869 | 5868 CJK Ideograph-5868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 586b CJK Ideograph-586B | 586a CJK Ideograph-586A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 586d CJK Ideograph-586D | 586c CJK Ideograph-586C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 586f CJK Ideograph-586F | 586e CJK Ideograph-586E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5871 CJK Ideograph-5871 | 5870 CJK Ideograph-5870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5873 CJK Ideograph-5873 | 5872 CJK Ideograph-5872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5875 CJK Ideograph-5875 | 5874 CJK Ideograph-5874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5877 CJK Ideograph-5877 | 5876 CJK Ideograph-5876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5879 CJK Ideograph-5879 | 5878 CJK Ideograph-5878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 587b CJK Ideograph-587B | 587a CJK Ideograph-587A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 587d CJK Ideograph-587D | 587c CJK Ideograph-587C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 587f CJK Ideograph-587F | 587e CJK Ideograph-587E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5881 CJK Ideograph-5881 | 5880 CJK Ideograph-5880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5883 CJK Ideograph-5883 | 5882 CJK Ideograph-5882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5885 CJK Ideograph-5885 | 5884 CJK Ideograph-5884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5887 CJK Ideograph-5887 | 5886 CJK Ideograph-5886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5889 CJK Ideograph-5889 | 5888 CJK Ideograph-5888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 588b CJK Ideograph-588B | 588a CJK Ideograph-588A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 588d CJK Ideograph-588D | 588c CJK Ideograph-588C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 588f CJK Ideograph-588F | 588e CJK Ideograph-588E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5891 CJK Ideograph-5891 | 5890 CJK Ideograph-5890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5893 CJK Ideograph-5893 | 5892 CJK Ideograph-5892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5895 CJK Ideograph-5895 | 5894 CJK Ideograph-5894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5897 CJK Ideograph-5897 | 5896 CJK Ideograph-5896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5899 CJK Ideograph-5899 | 5898 CJK Ideograph-5898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 589b CJK Ideograph-589B | 589a CJK Ideograph-589A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 589d CJK Ideograph-589D | 589c CJK Ideograph-589C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 589f CJK Ideograph-589F | 589e CJK Ideograph-589E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58a1 CJK Ideograph-58A1 | 58a0 CJK Ideograph-58A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58a3 CJK Ideograph-58A3 | 58a2 CJK Ideograph-58A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58a5 CJK Ideograph-58A5 | 58a4 CJK Ideograph-58A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58a7 CJK Ideograph-58A7 | 58a6 CJK Ideograph-58A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58a9 CJK Ideograph-58A9 | 58a8 CJK Ideograph-58A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58ab CJK Ideograph-58AB | 58aa CJK Ideograph-58AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58ad CJK Ideograph-58AD | 58ac CJK Ideograph-58AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58af CJK Ideograph-58AF | 58ae CJK Ideograph-58AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58b1 CJK Ideograph-58B1 | 58b0 CJK Ideograph-58B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58b3 CJK Ideograph-58B3 | 58b2 CJK Ideograph-58B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58b5 CJK Ideograph-58B5 | 58b4 CJK Ideograph-58B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58b7 CJK Ideograph-58B7 | 58b6 CJK Ideograph-58B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58b9 CJK Ideograph-58B9 | 58b8 CJK Ideograph-58B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58bb CJK Ideograph-58BB | 58ba CJK Ideograph-58BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58bd CJK Ideograph-58BD | 58bc CJK Ideograph-58BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58bf CJK Ideograph-58BF | 58be CJK Ideograph-58BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58c1 CJK Ideograph-58C1 | 58c0 CJK Ideograph-58C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58c3 CJK Ideograph-58C3 | 58c2 CJK Ideograph-58C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58c5 CJK Ideograph-58C5 | 58c4 CJK Ideograph-58C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58c7 CJK Ideograph-58C7 | 58c6 CJK Ideograph-58C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58c9 CJK Ideograph-58C9 | 58c8 CJK Ideograph-58C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58cb CJK Ideograph-58CB | 58ca CJK Ideograph-58CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58cd CJK Ideograph-58CD | 58cc CJK Ideograph-58CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58cf CJK Ideograph-58CF | 58ce CJK Ideograph-58CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58d1 CJK Ideograph-58D1 | 58d0 CJK Ideograph-58D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58d3 CJK Ideograph-58D3 | 58d2 CJK Ideograph-58D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58d5 CJK Ideograph-58D5 | 58d4 CJK Ideograph-58D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58d7 CJK Ideograph-58D7 | 58d6 CJK Ideograph-58D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58d9 CJK Ideograph-58D9 | 58d8 CJK Ideograph-58D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58db CJK Ideograph-58DB | 58da CJK Ideograph-58DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58dd CJK Ideograph-58DD | 58dc CJK Ideograph-58DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58df CJK Ideograph-58DF | 58de CJK Ideograph-58DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58e1 CJK Ideograph-58E1 | 58e0 CJK Ideograph-58E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58e3 CJK Ideograph-58E3 | 58e2 CJK Ideograph-58E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58e5 CJK Ideograph-58E5 | 58e4 CJK Ideograph-58E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58e7 CJK Ideograph-58E7 | 58e6 CJK Ideograph-58E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58e9 CJK Ideograph-58E9 | 58e8 CJK Ideograph-58E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58eb CJK Ideograph-58EB | 58ea CJK Ideograph-58EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58ed CJK Ideograph-58ED | 58ec CJK Ideograph-58EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58ef CJK Ideograph-58EF | 58ee CJK Ideograph-58EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58f1 CJK Ideograph-58F1 | 58f0 CJK Ideograph-58F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58f3 CJK Ideograph-58F3 | 58f2 CJK Ideograph-58F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58f5 CJK Ideograph-58F5 | 58f4 CJK Ideograph-58F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58f7 CJK Ideograph-58F7 | 58f6 CJK Ideograph-58F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58f9 CJK Ideograph-58F9 | 58f8 CJK Ideograph-58F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58fb CJK Ideograph-58FB | 58fa CJK Ideograph-58FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58fd CJK Ideograph-58FD | 58fc CJK Ideograph-58FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58ff CJK Ideograph-58FF | 58fe CJK Ideograph-58FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5901 CJK Ideograph-5901 | 5900 CJK Ideograph-5900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5903 CJK Ideograph-5903 | 5902 CJK Ideograph-5902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5905 CJK Ideograph-5905 | 5904 CJK Ideograph-5904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5907 CJK Ideograph-5907 | 5906 CJK Ideograph-5906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5909 CJK Ideograph-5909 | 5908 CJK Ideograph-5908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 590b CJK Ideograph-590B | 590a CJK Ideograph-590A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 590d CJK Ideograph-590D | 590c CJK Ideograph-590C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 590f CJK Ideograph-590F | 590e CJK Ideograph-590E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5911 CJK Ideograph-5911 | 5910 CJK Ideograph-5910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5913 CJK Ideograph-5913 | 5912 CJK Ideograph-5912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5915 CJK Ideograph-5915 | 5914 CJK Ideograph-5914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5917 CJK Ideograph-5917 | 5916 CJK Ideograph-5916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5919 CJK Ideograph-5919 | 5918 CJK Ideograph-5918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 591b CJK Ideograph-591B | 591a CJK Ideograph-591A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 591d CJK Ideograph-591D | 591c CJK Ideograph-591C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 591f CJK Ideograph-591F | 591e CJK Ideograph-591E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5921 CJK Ideograph-5921 | 5920 CJK Ideograph-5920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5923 CJK Ideograph-5923 | 5922 CJK Ideograph-5922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5925 CJK Ideograph-5925 | 5924 CJK Ideograph-5924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5927 CJK Ideograph-5927 | 5926 CJK Ideograph-5926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5929 CJK Ideograph-5929 | 5928 CJK Ideograph-5928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 592b CJK Ideograph-592B | 592a CJK Ideograph-592A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 592d CJK Ideograph-592D | 592c CJK Ideograph-592C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 592f CJK Ideograph-592F | 592e CJK Ideograph-592E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5931 CJK Ideograph-5931 | 5930 CJK Ideograph-5930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5933 CJK Ideograph-5933 | 5932 CJK Ideograph-5932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5935 CJK Ideograph-5935 | 5934 CJK Ideograph-5934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5937 CJK Ideograph-5937 | 5936 CJK Ideograph-5936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5939 CJK Ideograph-5939 | 5938 CJK Ideograph-5938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 593b CJK Ideograph-593B | 593a CJK Ideograph-593A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 593d CJK Ideograph-593D | 593c CJK Ideograph-593C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 593f CJK Ideograph-593F | 593e CJK Ideograph-593E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5941 CJK Ideograph-5941 | 5940 CJK Ideograph-5940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5943 CJK Ideograph-5943 | 5942 CJK Ideograph-5942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5945 CJK Ideograph-5945 | 5944 CJK Ideograph-5944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5947 CJK Ideograph-5947 | 5946 CJK Ideograph-5946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5949 CJK Ideograph-5949 | 5948 CJK Ideograph-5948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 594b CJK Ideograph-594B | 594a CJK Ideograph-594A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 594d CJK Ideograph-594D | 594c CJK Ideograph-594C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 594f CJK Ideograph-594F | 594e CJK Ideograph-594E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5951 CJK Ideograph-5951 | 5950 CJK Ideograph-5950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5953 CJK Ideograph-5953 | 5952 CJK Ideograph-5952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5955 CJK Ideograph-5955 | 5954 CJK Ideograph-5954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5957 CJK Ideograph-5957 | 5956 CJK Ideograph-5956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5959 CJK Ideograph-5959 | 5958 CJK Ideograph-5958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 595b CJK Ideograph-595B | 595a CJK Ideograph-595A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 595d CJK Ideograph-595D | 595c CJK Ideograph-595C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 595f CJK Ideograph-595F | 595e CJK Ideograph-595E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5961 CJK Ideograph-5961 | 5960 CJK Ideograph-5960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5963 CJK Ideograph-5963 | 5962 CJK Ideograph-5962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5965 CJK Ideograph-5965 | 5964 CJK Ideograph-5964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5967 CJK Ideograph-5967 | 5966 CJK Ideograph-5966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5969 CJK Ideograph-5969 | 5968 CJK Ideograph-5968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 596b CJK Ideograph-596B | 596a CJK Ideograph-596A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 596d CJK Ideograph-596D | 596c CJK Ideograph-596C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 596f CJK Ideograph-596F | 596e CJK Ideograph-596E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5971 CJK Ideograph-5971 | 5970 CJK Ideograph-5970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5973 CJK Ideograph-5973 | 5972 CJK Ideograph-5972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5975 CJK Ideograph-5975 | 5974 CJK Ideograph-5974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5977 CJK Ideograph-5977 | 5976 CJK Ideograph-5976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5979 CJK Ideograph-5979 | 5978 CJK Ideograph-5978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 597b CJK Ideograph-597B | 597a CJK Ideograph-597A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 597d CJK Ideograph-597D | 597c CJK Ideograph-597C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 597f CJK Ideograph-597F | 597e CJK Ideograph-597E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5981 CJK Ideograph-5981 | 5980 CJK Ideograph-5980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5983 CJK Ideograph-5983 | 5982 CJK Ideograph-5982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5985 CJK Ideograph-5985 | 5984 CJK Ideograph-5984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5987 CJK Ideograph-5987 | 5986 CJK Ideograph-5986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5989 CJK Ideograph-5989 | 5988 CJK Ideograph-5988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 598b CJK Ideograph-598B | 598a CJK Ideograph-598A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 598d CJK Ideograph-598D | 598c CJK Ideograph-598C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 598f CJK Ideograph-598F | 598e CJK Ideograph-598E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5991 CJK Ideograph-5991 | 5990 CJK Ideograph-5990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5993 CJK Ideograph-5993 | 5992 CJK Ideograph-5992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5995 CJK Ideograph-5995 | 5994 CJK Ideograph-5994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5997 CJK Ideograph-5997 | 5996 CJK Ideograph-5996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5999 CJK Ideograph-5999 | 5998 CJK Ideograph-5998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 599b CJK Ideograph-599B | 599a CJK Ideograph-599A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 599d CJK Ideograph-599D | 599c CJK Ideograph-599C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 599f CJK Ideograph-599F | 599e CJK Ideograph-599E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59a1 CJK Ideograph-59A1 | 59a0 CJK Ideograph-59A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59a3 CJK Ideograph-59A3 | 59a2 CJK Ideograph-59A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59a5 CJK Ideograph-59A5 | 59a4 CJK Ideograph-59A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59a7 CJK Ideograph-59A7 | 59a6 CJK Ideograph-59A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59a9 CJK Ideograph-59A9 | 59a8 CJK Ideograph-59A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59ab CJK Ideograph-59AB | 59aa CJK Ideograph-59AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59ad CJK Ideograph-59AD | 59ac CJK Ideograph-59AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59af CJK Ideograph-59AF | 59ae CJK Ideograph-59AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59b1 CJK Ideograph-59B1 | 59b0 CJK Ideograph-59B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59b3 CJK Ideograph-59B3 | 59b2 CJK Ideograph-59B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59b5 CJK Ideograph-59B5 | 59b4 CJK Ideograph-59B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59b7 CJK Ideograph-59B7 | 59b6 CJK Ideograph-59B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59b9 CJK Ideograph-59B9 | 59b8 CJK Ideograph-59B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59bb CJK Ideograph-59BB | 59ba CJK Ideograph-59BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59bd CJK Ideograph-59BD | 59bc CJK Ideograph-59BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59bf CJK Ideograph-59BF | 59be CJK Ideograph-59BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59c1 CJK Ideograph-59C1 | 59c0 CJK Ideograph-59C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59c3 CJK Ideograph-59C3 | 59c2 CJK Ideograph-59C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59c5 CJK Ideograph-59C5 | 59c4 CJK Ideograph-59C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59c7 CJK Ideograph-59C7 | 59c6 CJK Ideograph-59C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59c9 CJK Ideograph-59C9 | 59c8 CJK Ideograph-59C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59cb CJK Ideograph-59CB | 59ca CJK Ideograph-59CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59cd CJK Ideograph-59CD | 59cc CJK Ideograph-59CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59cf CJK Ideograph-59CF | 59ce CJK Ideograph-59CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59d1 CJK Ideograph-59D1 | 59d0 CJK Ideograph-59D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59d3 CJK Ideograph-59D3 | 59d2 CJK Ideograph-59D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59d5 CJK Ideograph-59D5 | 59d4 CJK Ideograph-59D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59d7 CJK Ideograph-59D7 | 59d6 CJK Ideograph-59D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59d9 CJK Ideograph-59D9 | 59d8 CJK Ideograph-59D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59db CJK Ideograph-59DB | 59da CJK Ideograph-59DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59dd CJK Ideograph-59DD | 59dc CJK Ideograph-59DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59df CJK Ideograph-59DF | 59de CJK Ideograph-59DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59e1 CJK Ideograph-59E1 | 59e0 CJK Ideograph-59E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59e3 CJK Ideograph-59E3 | 59e2 CJK Ideograph-59E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59e5 CJK Ideograph-59E5 | 59e4 CJK Ideograph-59E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59e7 CJK Ideograph-59E7 | 59e6 CJK Ideograph-59E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59e9 CJK Ideograph-59E9 | 59e8 CJK Ideograph-59E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59eb CJK Ideograph-59EB | 59ea CJK Ideograph-59EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59ed CJK Ideograph-59ED | 59ec CJK Ideograph-59EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59ef CJK Ideograph-59EF | 59ee CJK Ideograph-59EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59f1 CJK Ideograph-59F1 | 59f0 CJK Ideograph-59F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59f3 CJK Ideograph-59F3 | 59f2 CJK Ideograph-59F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59f5 CJK Ideograph-59F5 | 59f4 CJK Ideograph-59F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59f7 CJK Ideograph-59F7 | 59f6 CJK Ideograph-59F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59f9 CJK Ideograph-59F9 | 59f8 CJK Ideograph-59F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59fb CJK Ideograph-59FB | 59fa CJK Ideograph-59FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59fd CJK Ideograph-59FD | 59fc CJK Ideograph-59FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59ff CJK Ideograph-59FF | 59fe CJK Ideograph-59FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a01 CJK Ideograph-5A01 | 5a00 CJK Ideograph-5A00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a03 CJK Ideograph-5A03 | 5a02 CJK Ideograph-5A02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a05 CJK Ideograph-5A05 | 5a04 CJK Ideograph-5A04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a07 CJK Ideograph-5A07 | 5a06 CJK Ideograph-5A06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a09 CJK Ideograph-5A09 | 5a08 CJK Ideograph-5A08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a0b CJK Ideograph-5A0B | 5a0a CJK Ideograph-5A0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a0d CJK Ideograph-5A0D | 5a0c CJK Ideograph-5A0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a0f CJK Ideograph-5A0F | 5a0e CJK Ideograph-5A0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a11 CJK Ideograph-5A11 | 5a10 CJK Ideograph-5A10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a13 CJK Ideograph-5A13 | 5a12 CJK Ideograph-5A12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a15 CJK Ideograph-5A15 | 5a14 CJK Ideograph-5A14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a17 CJK Ideograph-5A17 | 5a16 CJK Ideograph-5A16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a19 CJK Ideograph-5A19 | 5a18 CJK Ideograph-5A18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a1b CJK Ideograph-5A1B | 5a1a CJK Ideograph-5A1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a1d CJK Ideograph-5A1D | 5a1c CJK Ideograph-5A1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a1f CJK Ideograph-5A1F | 5a1e CJK Ideograph-5A1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a21 CJK Ideograph-5A21 | 5a20 CJK Ideograph-5A20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a23 CJK Ideograph-5A23 | 5a22 CJK Ideograph-5A22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a25 CJK Ideograph-5A25 | 5a24 CJK Ideograph-5A24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a27 CJK Ideograph-5A27 | 5a26 CJK Ideograph-5A26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a29 CJK Ideograph-5A29 | 5a28 CJK Ideograph-5A28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a2b CJK Ideograph-5A2B | 5a2a CJK Ideograph-5A2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a2d CJK Ideograph-5A2D | 5a2c CJK Ideograph-5A2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a2f CJK Ideograph-5A2F | 5a2e CJK Ideograph-5A2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a31 CJK Ideograph-5A31 | 5a30 CJK Ideograph-5A30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a33 CJK Ideograph-5A33 | 5a32 CJK Ideograph-5A32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a35 CJK Ideograph-5A35 | 5a34 CJK Ideograph-5A34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a37 CJK Ideograph-5A37 | 5a36 CJK Ideograph-5A36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a39 CJK Ideograph-5A39 | 5a38 CJK Ideograph-5A38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a3b CJK Ideograph-5A3B | 5a3a CJK Ideograph-5A3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a3d CJK Ideograph-5A3D | 5a3c CJK Ideograph-5A3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a3f CJK Ideograph-5A3F | 5a3e CJK Ideograph-5A3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a41 CJK Ideograph-5A41 | 5a40 CJK Ideograph-5A40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a43 CJK Ideograph-5A43 | 5a42 CJK Ideograph-5A42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a45 CJK Ideograph-5A45 | 5a44 CJK Ideograph-5A44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a47 CJK Ideograph-5A47 | 5a46 CJK Ideograph-5A46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a49 CJK Ideograph-5A49 | 5a48 CJK Ideograph-5A48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a4b CJK Ideograph-5A4B | 5a4a CJK Ideograph-5A4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a4d CJK Ideograph-5A4D | 5a4c CJK Ideograph-5A4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a4f CJK Ideograph-5A4F | 5a4e CJK Ideograph-5A4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a51 CJK Ideograph-5A51 | 5a50 CJK Ideograph-5A50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a53 CJK Ideograph-5A53 | 5a52 CJK Ideograph-5A52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a55 CJK Ideograph-5A55 | 5a54 CJK Ideograph-5A54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a57 CJK Ideograph-5A57 | 5a56 CJK Ideograph-5A56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a59 CJK Ideograph-5A59 | 5a58 CJK Ideograph-5A58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a5b CJK Ideograph-5A5B | 5a5a CJK Ideograph-5A5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a5d CJK Ideograph-5A5D | 5a5c CJK Ideograph-5A5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a5f CJK Ideograph-5A5F | 5a5e CJK Ideograph-5A5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a61 CJK Ideograph-5A61 | 5a60 CJK Ideograph-5A60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a63 CJK Ideograph-5A63 | 5a62 CJK Ideograph-5A62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a65 CJK Ideograph-5A65 | 5a64 CJK Ideograph-5A64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a67 CJK Ideograph-5A67 | 5a66 CJK Ideograph-5A66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a69 CJK Ideograph-5A69 | 5a68 CJK Ideograph-5A68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a6b CJK Ideograph-5A6B | 5a6a CJK Ideograph-5A6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a6d CJK Ideograph-5A6D | 5a6c CJK Ideograph-5A6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a6f CJK Ideograph-5A6F | 5a6e CJK Ideograph-5A6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a71 CJK Ideograph-5A71 | 5a70 CJK Ideograph-5A70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a73 CJK Ideograph-5A73 | 5a72 CJK Ideograph-5A72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a75 CJK Ideograph-5A75 | 5a74 CJK Ideograph-5A74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a77 CJK Ideograph-5A77 | 5a76 CJK Ideograph-5A76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a79 CJK Ideograph-5A79 | 5a78 CJK Ideograph-5A78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a7b CJK Ideograph-5A7B | 5a7a CJK Ideograph-5A7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a7d CJK Ideograph-5A7D | 5a7c CJK Ideograph-5A7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a7f CJK Ideograph-5A7F | 5a7e CJK Ideograph-5A7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a81 CJK Ideograph-5A81 | 5a80 CJK Ideograph-5A80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a83 CJK Ideograph-5A83 | 5a82 CJK Ideograph-5A82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a85 CJK Ideograph-5A85 | 5a84 CJK Ideograph-5A84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a87 CJK Ideograph-5A87 | 5a86 CJK Ideograph-5A86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a89 CJK Ideograph-5A89 | 5a88 CJK Ideograph-5A88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a8b CJK Ideograph-5A8B | 5a8a CJK Ideograph-5A8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a8d CJK Ideograph-5A8D | 5a8c CJK Ideograph-5A8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a8f CJK Ideograph-5A8F | 5a8e CJK Ideograph-5A8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a91 CJK Ideograph-5A91 | 5a90 CJK Ideograph-5A90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a93 CJK Ideograph-5A93 | 5a92 CJK Ideograph-5A92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a95 CJK Ideograph-5A95 | 5a94 CJK Ideograph-5A94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a97 CJK Ideograph-5A97 | 5a96 CJK Ideograph-5A96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a99 CJK Ideograph-5A99 | 5a98 CJK Ideograph-5A98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a9b CJK Ideograph-5A9B | 5a9a CJK Ideograph-5A9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a9d CJK Ideograph-5A9D | 5a9c CJK Ideograph-5A9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a9f CJK Ideograph-5A9F | 5a9e CJK Ideograph-5A9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aa1 CJK Ideograph-5AA1 | 5aa0 CJK Ideograph-5AA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aa3 CJK Ideograph-5AA3 | 5aa2 CJK Ideograph-5AA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aa5 CJK Ideograph-5AA5 | 5aa4 CJK Ideograph-5AA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aa7 CJK Ideograph-5AA7 | 5aa6 CJK Ideograph-5AA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aa9 CJK Ideograph-5AA9 | 5aa8 CJK Ideograph-5AA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aab CJK Ideograph-5AAB | 5aaa CJK Ideograph-5AAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aad CJK Ideograph-5AAD | 5aac CJK Ideograph-5AAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aaf CJK Ideograph-5AAF | 5aae CJK Ideograph-5AAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ab1 CJK Ideograph-5AB1 | 5ab0 CJK Ideograph-5AB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ab3 CJK Ideograph-5AB3 | 5ab2 CJK Ideograph-5AB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ab5 CJK Ideograph-5AB5 | 5ab4 CJK Ideograph-5AB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ab7 CJK Ideograph-5AB7 | 5ab6 CJK Ideograph-5AB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ab9 CJK Ideograph-5AB9 | 5ab8 CJK Ideograph-5AB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5abb CJK Ideograph-5ABB | 5aba CJK Ideograph-5ABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5abd CJK Ideograph-5ABD | 5abc CJK Ideograph-5ABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5abf CJK Ideograph-5ABF | 5abe CJK Ideograph-5ABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ac1 CJK Ideograph-5AC1 | 5ac0 CJK Ideograph-5AC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ac3 CJK Ideograph-5AC3 | 5ac2 CJK Ideograph-5AC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ac5 CJK Ideograph-5AC5 | 5ac4 CJK Ideograph-5AC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ac7 CJK Ideograph-5AC7 | 5ac6 CJK Ideograph-5AC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ac9 CJK Ideograph-5AC9 | 5ac8 CJK Ideograph-5AC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5acb CJK Ideograph-5ACB | 5aca CJK Ideograph-5ACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5acd CJK Ideograph-5ACD | 5acc CJK Ideograph-5ACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5acf CJK Ideograph-5ACF | 5ace CJK Ideograph-5ACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ad1 CJK Ideograph-5AD1 | 5ad0 CJK Ideograph-5AD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ad3 CJK Ideograph-5AD3 | 5ad2 CJK Ideograph-5AD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ad5 CJK Ideograph-5AD5 | 5ad4 CJK Ideograph-5AD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ad7 CJK Ideograph-5AD7 | 5ad6 CJK Ideograph-5AD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ad9 CJK Ideograph-5AD9 | 5ad8 CJK Ideograph-5AD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5adb CJK Ideograph-5ADB | 5ada CJK Ideograph-5ADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5add CJK Ideograph-5ADD | 5adc CJK Ideograph-5ADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5adf CJK Ideograph-5ADF | 5ade CJK Ideograph-5ADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ae1 CJK Ideograph-5AE1 | 5ae0 CJK Ideograph-5AE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ae3 CJK Ideograph-5AE3 | 5ae2 CJK Ideograph-5AE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ae5 CJK Ideograph-5AE5 | 5ae4 CJK Ideograph-5AE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ae7 CJK Ideograph-5AE7 | 5ae6 CJK Ideograph-5AE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ae9 CJK Ideograph-5AE9 | 5ae8 CJK Ideograph-5AE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aeb CJK Ideograph-5AEB | 5aea CJK Ideograph-5AEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aed CJK Ideograph-5AED | 5aec CJK Ideograph-5AEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aef CJK Ideograph-5AEF | 5aee CJK Ideograph-5AEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5af1 CJK Ideograph-5AF1 | 5af0 CJK Ideograph-5AF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5af3 CJK Ideograph-5AF3 | 5af2 CJK Ideograph-5AF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5af5 CJK Ideograph-5AF5 | 5af4 CJK Ideograph-5AF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5af7 CJK Ideograph-5AF7 | 5af6 CJK Ideograph-5AF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5af9 CJK Ideograph-5AF9 | 5af8 CJK Ideograph-5AF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5afb CJK Ideograph-5AFB | 5afa CJK Ideograph-5AFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5afd CJK Ideograph-5AFD | 5afc CJK Ideograph-5AFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aff CJK Ideograph-5AFF | 5afe CJK Ideograph-5AFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b01 CJK Ideograph-5B01 | 5b00 CJK Ideograph-5B00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b03 CJK Ideograph-5B03 | 5b02 CJK Ideograph-5B02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b05 CJK Ideograph-5B05 | 5b04 CJK Ideograph-5B04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b07 CJK Ideograph-5B07 | 5b06 CJK Ideograph-5B06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b09 CJK Ideograph-5B09 | 5b08 CJK Ideograph-5B08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b0b CJK Ideograph-5B0B | 5b0a CJK Ideograph-5B0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b0d CJK Ideograph-5B0D | 5b0c CJK Ideograph-5B0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b0f CJK Ideograph-5B0F | 5b0e CJK Ideograph-5B0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b11 CJK Ideograph-5B11 | 5b10 CJK Ideograph-5B10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b13 CJK Ideograph-5B13 | 5b12 CJK Ideograph-5B12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b15 CJK Ideograph-5B15 | 5b14 CJK Ideograph-5B14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b17 CJK Ideograph-5B17 | 5b16 CJK Ideograph-5B16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b19 CJK Ideograph-5B19 | 5b18 CJK Ideograph-5B18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b1b CJK Ideograph-5B1B | 5b1a CJK Ideograph-5B1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b1d CJK Ideograph-5B1D | 5b1c CJK Ideograph-5B1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b1f CJK Ideograph-5B1F | 5b1e CJK Ideograph-5B1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b21 CJK Ideograph-5B21 | 5b20 CJK Ideograph-5B20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b23 CJK Ideograph-5B23 | 5b22 CJK Ideograph-5B22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b25 CJK Ideograph-5B25 | 5b24 CJK Ideograph-5B24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b27 CJK Ideograph-5B27 | 5b26 CJK Ideograph-5B26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b29 CJK Ideograph-5B29 | 5b28 CJK Ideograph-5B28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b2b CJK Ideograph-5B2B | 5b2a CJK Ideograph-5B2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b2d CJK Ideograph-5B2D | 5b2c CJK Ideograph-5B2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b2f CJK Ideograph-5B2F | 5b2e CJK Ideograph-5B2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b31 CJK Ideograph-5B31 | 5b30 CJK Ideograph-5B30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b33 CJK Ideograph-5B33 | 5b32 CJK Ideograph-5B32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b35 CJK Ideograph-5B35 | 5b34 CJK Ideograph-5B34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b37 CJK Ideograph-5B37 | 5b36 CJK Ideograph-5B36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b39 CJK Ideograph-5B39 | 5b38 CJK Ideograph-5B38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b3b CJK Ideograph-5B3B | 5b3a CJK Ideograph-5B3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b3d CJK Ideograph-5B3D | 5b3c CJK Ideograph-5B3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b3f CJK Ideograph-5B3F | 5b3e CJK Ideograph-5B3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b41 CJK Ideograph-5B41 | 5b40 CJK Ideograph-5B40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b43 CJK Ideograph-5B43 | 5b42 CJK Ideograph-5B42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b45 CJK Ideograph-5B45 | 5b44 CJK Ideograph-5B44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b47 CJK Ideograph-5B47 | 5b46 CJK Ideograph-5B46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b49 CJK Ideograph-5B49 | 5b48 CJK Ideograph-5B48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b4b CJK Ideograph-5B4B | 5b4a CJK Ideograph-5B4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b4d CJK Ideograph-5B4D | 5b4c CJK Ideograph-5B4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b4f CJK Ideograph-5B4F | 5b4e CJK Ideograph-5B4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b51 CJK Ideograph-5B51 | 5b50 CJK Ideograph-5B50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b53 CJK Ideograph-5B53 | 5b52 CJK Ideograph-5B52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b55 CJK Ideograph-5B55 | 5b54 CJK Ideograph-5B54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b57 CJK Ideograph-5B57 | 5b56 CJK Ideograph-5B56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b59 CJK Ideograph-5B59 | 5b58 CJK Ideograph-5B58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b5b CJK Ideograph-5B5B | 5b5a CJK Ideograph-5B5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b5d CJK Ideograph-5B5D | 5b5c CJK Ideograph-5B5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b5f CJK Ideograph-5B5F | 5b5e CJK Ideograph-5B5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b61 CJK Ideograph-5B61 | 5b60 CJK Ideograph-5B60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b63 CJK Ideograph-5B63 | 5b62 CJK Ideograph-5B62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b65 CJK Ideograph-5B65 | 5b64 CJK Ideograph-5B64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b67 CJK Ideograph-5B67 | 5b66 CJK Ideograph-5B66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b69 CJK Ideograph-5B69 | 5b68 CJK Ideograph-5B68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b6b CJK Ideograph-5B6B | 5b6a CJK Ideograph-5B6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b6d CJK Ideograph-5B6D | 5b6c CJK Ideograph-5B6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b6f CJK Ideograph-5B6F | 5b6e CJK Ideograph-5B6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b71 CJK Ideograph-5B71 | 5b70 CJK Ideograph-5B70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b73 CJK Ideograph-5B73 | 5b72 CJK Ideograph-5B72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b75 CJK Ideograph-5B75 | 5b74 CJK Ideograph-5B74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b77 CJK Ideograph-5B77 | 5b76 CJK Ideograph-5B76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b79 CJK Ideograph-5B79 | 5b78 CJK Ideograph-5B78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b7b CJK Ideograph-5B7B | 5b7a CJK Ideograph-5B7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b7d CJK Ideograph-5B7D | 5b7c CJK Ideograph-5B7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b7f CJK Ideograph-5B7F | 5b7e CJK Ideograph-5B7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b81 CJK Ideograph-5B81 | 5b80 CJK Ideograph-5B80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b83 CJK Ideograph-5B83 | 5b82 CJK Ideograph-5B82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b85 CJK Ideograph-5B85 | 5b84 CJK Ideograph-5B84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b87 CJK Ideograph-5B87 | 5b86 CJK Ideograph-5B86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b89 CJK Ideograph-5B89 | 5b88 CJK Ideograph-5B88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b8b CJK Ideograph-5B8B | 5b8a CJK Ideograph-5B8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b8d CJK Ideograph-5B8D | 5b8c CJK Ideograph-5B8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b8f CJK Ideograph-5B8F | 5b8e CJK Ideograph-5B8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b91 CJK Ideograph-5B91 | 5b90 CJK Ideograph-5B90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b93 CJK Ideograph-5B93 | 5b92 CJK Ideograph-5B92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b95 CJK Ideograph-5B95 | 5b94 CJK Ideograph-5B94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b97 CJK Ideograph-5B97 | 5b96 CJK Ideograph-5B96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b99 CJK Ideograph-5B99 | 5b98 CJK Ideograph-5B98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b9b CJK Ideograph-5B9B | 5b9a CJK Ideograph-5B9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b9d CJK Ideograph-5B9D | 5b9c CJK Ideograph-5B9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b9f CJK Ideograph-5B9F | 5b9e CJK Ideograph-5B9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ba1 CJK Ideograph-5BA1 | 5ba0 CJK Ideograph-5BA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ba3 CJK Ideograph-5BA3 | 5ba2 CJK Ideograph-5BA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ba5 CJK Ideograph-5BA5 | 5ba4 CJK Ideograph-5BA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ba7 CJK Ideograph-5BA7 | 5ba6 CJK Ideograph-5BA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ba9 CJK Ideograph-5BA9 | 5ba8 CJK Ideograph-5BA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bab CJK Ideograph-5BAB | 5baa CJK Ideograph-5BAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bad CJK Ideograph-5BAD | 5bac CJK Ideograph-5BAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5baf CJK Ideograph-5BAF | 5bae CJK Ideograph-5BAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bb1 CJK Ideograph-5BB1 | 5bb0 CJK Ideograph-5BB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bb3 CJK Ideograph-5BB3 | 5bb2 CJK Ideograph-5BB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bb5 CJK Ideograph-5BB5 | 5bb4 CJK Ideograph-5BB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bb7 CJK Ideograph-5BB7 | 5bb6 CJK Ideograph-5BB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bb9 CJK Ideograph-5BB9 | 5bb8 CJK Ideograph-5BB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bbb CJK Ideograph-5BBB | 5bba CJK Ideograph-5BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bbd CJK Ideograph-5BBD | 5bbc CJK Ideograph-5BBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bbf CJK Ideograph-5BBF | 5bbe CJK Ideograph-5BBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bc1 CJK Ideograph-5BC1 | 5bc0 CJK Ideograph-5BC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bc3 CJK Ideograph-5BC3 | 5bc2 CJK Ideograph-5BC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bc5 CJK Ideograph-5BC5 | 5bc4 CJK Ideograph-5BC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bc7 CJK Ideograph-5BC7 | 5bc6 CJK Ideograph-5BC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bc9 CJK Ideograph-5BC9 | 5bc8 CJK Ideograph-5BC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bcb CJK Ideograph-5BCB | 5bca CJK Ideograph-5BCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bcd CJK Ideograph-5BCD | 5bcc CJK Ideograph-5BCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bcf CJK Ideograph-5BCF | 5bce CJK Ideograph-5BCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bd1 CJK Ideograph-5BD1 | 5bd0 CJK Ideograph-5BD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bd3 CJK Ideograph-5BD3 | 5bd2 CJK Ideograph-5BD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bd5 CJK Ideograph-5BD5 | 5bd4 CJK Ideograph-5BD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bd7 CJK Ideograph-5BD7 | 5bd6 CJK Ideograph-5BD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bd9 CJK Ideograph-5BD9 | 5bd8 CJK Ideograph-5BD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bdb CJK Ideograph-5BDB | 5bda CJK Ideograph-5BDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bdd CJK Ideograph-5BDD | 5bdc CJK Ideograph-5BDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bdf CJK Ideograph-5BDF | 5bde CJK Ideograph-5BDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5be1 CJK Ideograph-5BE1 | 5be0 CJK Ideograph-5BE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5be3 CJK Ideograph-5BE3 | 5be2 CJK Ideograph-5BE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5be5 CJK Ideograph-5BE5 | 5be4 CJK Ideograph-5BE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5be7 CJK Ideograph-5BE7 | 5be6 CJK Ideograph-5BE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5be9 CJK Ideograph-5BE9 | 5be8 CJK Ideograph-5BE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5beb CJK Ideograph-5BEB | 5bea CJK Ideograph-5BEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bed CJK Ideograph-5BED | 5bec CJK Ideograph-5BEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bef CJK Ideograph-5BEF | 5bee CJK Ideograph-5BEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bf1 CJK Ideograph-5BF1 | 5bf0 CJK Ideograph-5BF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bf3 CJK Ideograph-5BF3 | 5bf2 CJK Ideograph-5BF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bf5 CJK Ideograph-5BF5 | 5bf4 CJK Ideograph-5BF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bf7 CJK Ideograph-5BF7 | 5bf6 CJK Ideograph-5BF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bf9 CJK Ideograph-5BF9 | 5bf8 CJK Ideograph-5BF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bfb CJK Ideograph-5BFB | 5bfa CJK Ideograph-5BFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bfd CJK Ideograph-5BFD | 5bfc CJK Ideograph-5BFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bff CJK Ideograph-5BFF | 5bfe CJK Ideograph-5BFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c01 CJK Ideograph-5C01 | 5c00 CJK Ideograph-5C00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c03 CJK Ideograph-5C03 | 5c02 CJK Ideograph-5C02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c05 CJK Ideograph-5C05 | 5c04 CJK Ideograph-5C04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c07 CJK Ideograph-5C07 | 5c06 CJK Ideograph-5C06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c09 CJK Ideograph-5C09 | 5c08 CJK Ideograph-5C08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c0b CJK Ideograph-5C0B | 5c0a CJK Ideograph-5C0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c0d CJK Ideograph-5C0D | 5c0c CJK Ideograph-5C0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c0f CJK Ideograph-5C0F | 5c0e CJK Ideograph-5C0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c11 CJK Ideograph-5C11 | 5c10 CJK Ideograph-5C10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c13 CJK Ideograph-5C13 | 5c12 CJK Ideograph-5C12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c15 CJK Ideograph-5C15 | 5c14 CJK Ideograph-5C14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c17 CJK Ideograph-5C17 | 5c16 CJK Ideograph-5C16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c19 CJK Ideograph-5C19 | 5c18 CJK Ideograph-5C18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c1b CJK Ideograph-5C1B | 5c1a CJK Ideograph-5C1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c1d CJK Ideograph-5C1D | 5c1c CJK Ideograph-5C1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c1f CJK Ideograph-5C1F | 5c1e CJK Ideograph-5C1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c21 CJK Ideograph-5C21 | 5c20 CJK Ideograph-5C20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c23 CJK Ideograph-5C23 | 5c22 CJK Ideograph-5C22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c25 CJK Ideograph-5C25 | 5c24 CJK Ideograph-5C24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c27 CJK Ideograph-5C27 | 5c26 CJK Ideograph-5C26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c29 CJK Ideograph-5C29 | 5c28 CJK Ideograph-5C28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c2b CJK Ideograph-5C2B | 5c2a CJK Ideograph-5C2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c2d CJK Ideograph-5C2D | 5c2c CJK Ideograph-5C2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c2f CJK Ideograph-5C2F | 5c2e CJK Ideograph-5C2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c31 CJK Ideograph-5C31 | 5c30 CJK Ideograph-5C30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c33 CJK Ideograph-5C33 | 5c32 CJK Ideograph-5C32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c35 CJK Ideograph-5C35 | 5c34 CJK Ideograph-5C34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c37 CJK Ideograph-5C37 | 5c36 CJK Ideograph-5C36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c39 CJK Ideograph-5C39 | 5c38 CJK Ideograph-5C38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c3b CJK Ideograph-5C3B | 5c3a CJK Ideograph-5C3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c3d CJK Ideograph-5C3D | 5c3c CJK Ideograph-5C3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c3f CJK Ideograph-5C3F | 5c3e CJK Ideograph-5C3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c41 CJK Ideograph-5C41 | 5c40 CJK Ideograph-5C40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c43 CJK Ideograph-5C43 | 5c42 CJK Ideograph-5C42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c45 CJK Ideograph-5C45 | 5c44 CJK Ideograph-5C44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c47 CJK Ideograph-5C47 | 5c46 CJK Ideograph-5C46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c49 CJK Ideograph-5C49 | 5c48 CJK Ideograph-5C48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c4b CJK Ideograph-5C4B | 5c4a CJK Ideograph-5C4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c4d CJK Ideograph-5C4D | 5c4c CJK Ideograph-5C4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c4f CJK Ideograph-5C4F | 5c4e CJK Ideograph-5C4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c51 CJK Ideograph-5C51 | 5c50 CJK Ideograph-5C50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c53 CJK Ideograph-5C53 | 5c52 CJK Ideograph-5C52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c55 CJK Ideograph-5C55 | 5c54 CJK Ideograph-5C54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c57 CJK Ideograph-5C57 | 5c56 CJK Ideograph-5C56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c59 CJK Ideograph-5C59 | 5c58 CJK Ideograph-5C58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c5b CJK Ideograph-5C5B | 5c5a CJK Ideograph-5C5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c5d CJK Ideograph-5C5D | 5c5c CJK Ideograph-5C5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c5f CJK Ideograph-5C5F | 5c5e CJK Ideograph-5C5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c61 CJK Ideograph-5C61 | 5c60 CJK Ideograph-5C60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c63 CJK Ideograph-5C63 | 5c62 CJK Ideograph-5C62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c65 CJK Ideograph-5C65 | 5c64 CJK Ideograph-5C64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c67 CJK Ideograph-5C67 | 5c66 CJK Ideograph-5C66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c69 CJK Ideograph-5C69 | 5c68 CJK Ideograph-5C68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c6b CJK Ideograph-5C6B | 5c6a CJK Ideograph-5C6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c6d CJK Ideograph-5C6D | 5c6c CJK Ideograph-5C6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c6f CJK Ideograph-5C6F | 5c6e CJK Ideograph-5C6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c71 CJK Ideograph-5C71 | 5c70 CJK Ideograph-5C70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c73 CJK Ideograph-5C73 | 5c72 CJK Ideograph-5C72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c75 CJK Ideograph-5C75 | 5c74 CJK Ideograph-5C74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c77 CJK Ideograph-5C77 | 5c76 CJK Ideograph-5C76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c79 CJK Ideograph-5C79 | 5c78 CJK Ideograph-5C78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c7b CJK Ideograph-5C7B | 5c7a CJK Ideograph-5C7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c7d CJK Ideograph-5C7D | 5c7c CJK Ideograph-5C7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c7f CJK Ideograph-5C7F | 5c7e CJK Ideograph-5C7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c81 CJK Ideograph-5C81 | 5c80 CJK Ideograph-5C80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c83 CJK Ideograph-5C83 | 5c82 CJK Ideograph-5C82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c85 CJK Ideograph-5C85 | 5c84 CJK Ideograph-5C84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c87 CJK Ideograph-5C87 | 5c86 CJK Ideograph-5C86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c89 CJK Ideograph-5C89 | 5c88 CJK Ideograph-5C88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c8b CJK Ideograph-5C8B | 5c8a CJK Ideograph-5C8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c8d CJK Ideograph-5C8D | 5c8c CJK Ideograph-5C8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c8f CJK Ideograph-5C8F | 5c8e CJK Ideograph-5C8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c91 CJK Ideograph-5C91 | 5c90 CJK Ideograph-5C90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c93 CJK Ideograph-5C93 | 5c92 CJK Ideograph-5C92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c95 CJK Ideograph-5C95 | 5c94 CJK Ideograph-5C94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c97 CJK Ideograph-5C97 | 5c96 CJK Ideograph-5C96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c99 CJK Ideograph-5C99 | 5c98 CJK Ideograph-5C98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c9b CJK Ideograph-5C9B | 5c9a CJK Ideograph-5C9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c9d CJK Ideograph-5C9D | 5c9c CJK Ideograph-5C9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c9f CJK Ideograph-5C9F | 5c9e CJK Ideograph-5C9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ca1 CJK Ideograph-5CA1 | 5ca0 CJK Ideograph-5CA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ca3 CJK Ideograph-5CA3 | 5ca2 CJK Ideograph-5CA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ca5 CJK Ideograph-5CA5 | 5ca4 CJK Ideograph-5CA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ca7 CJK Ideograph-5CA7 | 5ca6 CJK Ideograph-5CA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ca9 CJK Ideograph-5CA9 | 5ca8 CJK Ideograph-5CA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cab CJK Ideograph-5CAB | 5caa CJK Ideograph-5CAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cad CJK Ideograph-5CAD | 5cac CJK Ideograph-5CAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5caf CJK Ideograph-5CAF | 5cae CJK Ideograph-5CAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cb1 CJK Ideograph-5CB1 | 5cb0 CJK Ideograph-5CB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cb3 CJK Ideograph-5CB3 | 5cb2 CJK Ideograph-5CB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cb5 CJK Ideograph-5CB5 | 5cb4 CJK Ideograph-5CB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cb7 CJK Ideograph-5CB7 | 5cb6 CJK Ideograph-5CB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cb9 CJK Ideograph-5CB9 | 5cb8 CJK Ideograph-5CB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cbb CJK Ideograph-5CBB | 5cba CJK Ideograph-5CBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cbd CJK Ideograph-5CBD | 5cbc CJK Ideograph-5CBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cbf CJK Ideograph-5CBF | 5cbe CJK Ideograph-5CBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cc1 CJK Ideograph-5CC1 | 5cc0 CJK Ideograph-5CC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cc3 CJK Ideograph-5CC3 | 5cc2 CJK Ideograph-5CC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cc5 CJK Ideograph-5CC5 | 5cc4 CJK Ideograph-5CC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cc7 CJK Ideograph-5CC7 | 5cc6 CJK Ideograph-5CC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cc9 CJK Ideograph-5CC9 | 5cc8 CJK Ideograph-5CC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ccb CJK Ideograph-5CCB | 5cca CJK Ideograph-5CCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ccd CJK Ideograph-5CCD | 5ccc CJK Ideograph-5CCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ccf CJK Ideograph-5CCF | 5cce CJK Ideograph-5CCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cd1 CJK Ideograph-5CD1 | 5cd0 CJK Ideograph-5CD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cd3 CJK Ideograph-5CD3 | 5cd2 CJK Ideograph-5CD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cd5 CJK Ideograph-5CD5 | 5cd4 CJK Ideograph-5CD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cd7 CJK Ideograph-5CD7 | 5cd6 CJK Ideograph-5CD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cd9 CJK Ideograph-5CD9 | 5cd8 CJK Ideograph-5CD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cdb CJK Ideograph-5CDB | 5cda CJK Ideograph-5CDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cdd CJK Ideograph-5CDD | 5cdc CJK Ideograph-5CDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cdf CJK Ideograph-5CDF | 5cde CJK Ideograph-5CDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ce1 CJK Ideograph-5CE1 | 5ce0 CJK Ideograph-5CE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ce3 CJK Ideograph-5CE3 | 5ce2 CJK Ideograph-5CE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ce5 CJK Ideograph-5CE5 | 5ce4 CJK Ideograph-5CE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ce7 CJK Ideograph-5CE7 | 5ce6 CJK Ideograph-5CE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ce9 CJK Ideograph-5CE9 | 5ce8 CJK Ideograph-5CE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ceb CJK Ideograph-5CEB | 5cea CJK Ideograph-5CEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ced CJK Ideograph-5CED | 5cec CJK Ideograph-5CEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cef CJK Ideograph-5CEF | 5cee CJK Ideograph-5CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cf1 CJK Ideograph-5CF1 | 5cf0 CJK Ideograph-5CF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cf3 CJK Ideograph-5CF3 | 5cf2 CJK Ideograph-5CF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cf5 CJK Ideograph-5CF5 | 5cf4 CJK Ideograph-5CF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cf7 CJK Ideograph-5CF7 | 5cf6 CJK Ideograph-5CF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cf9 CJK Ideograph-5CF9 | 5cf8 CJK Ideograph-5CF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cfb CJK Ideograph-5CFB | 5cfa CJK Ideograph-5CFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cfd CJK Ideograph-5CFD | 5cfc CJK Ideograph-5CFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cff CJK Ideograph-5CFF | 5cfe CJK Ideograph-5CFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d01 CJK Ideograph-5D01 | 5d00 CJK Ideograph-5D00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d03 CJK Ideograph-5D03 | 5d02 CJK Ideograph-5D02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d05 CJK Ideograph-5D05 | 5d04 CJK Ideograph-5D04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d07 CJK Ideograph-5D07 | 5d06 CJK Ideograph-5D06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d09 CJK Ideograph-5D09 | 5d08 CJK Ideograph-5D08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d0b CJK Ideograph-5D0B | 5d0a CJK Ideograph-5D0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d0d CJK Ideograph-5D0D | 5d0c CJK Ideograph-5D0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d0f CJK Ideograph-5D0F | 5d0e CJK Ideograph-5D0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d11 CJK Ideograph-5D11 | 5d10 CJK Ideograph-5D10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d13 CJK Ideograph-5D13 | 5d12 CJK Ideograph-5D12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d15 CJK Ideograph-5D15 | 5d14 CJK Ideograph-5D14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d17 CJK Ideograph-5D17 | 5d16 CJK Ideograph-5D16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d19 CJK Ideograph-5D19 | 5d18 CJK Ideograph-5D18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d1b CJK Ideograph-5D1B | 5d1a CJK Ideograph-5D1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d1d CJK Ideograph-5D1D | 5d1c CJK Ideograph-5D1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d1f CJK Ideograph-5D1F | 5d1e CJK Ideograph-5D1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d21 CJK Ideograph-5D21 | 5d20 CJK Ideograph-5D20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d23 CJK Ideograph-5D23 | 5d22 CJK Ideograph-5D22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d25 CJK Ideograph-5D25 | 5d24 CJK Ideograph-5D24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d27 CJK Ideograph-5D27 | 5d26 CJK Ideograph-5D26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d29 CJK Ideograph-5D29 | 5d28 CJK Ideograph-5D28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d2b CJK Ideograph-5D2B | 5d2a CJK Ideograph-5D2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d2d CJK Ideograph-5D2D | 5d2c CJK Ideograph-5D2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d2f CJK Ideograph-5D2F | 5d2e CJK Ideograph-5D2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d31 CJK Ideograph-5D31 | 5d30 CJK Ideograph-5D30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d33 CJK Ideograph-5D33 | 5d32 CJK Ideograph-5D32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d35 CJK Ideograph-5D35 | 5d34 CJK Ideograph-5D34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d37 CJK Ideograph-5D37 | 5d36 CJK Ideograph-5D36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d39 CJK Ideograph-5D39 | 5d38 CJK Ideograph-5D38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d3b CJK Ideograph-5D3B | 5d3a CJK Ideograph-5D3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d3d CJK Ideograph-5D3D | 5d3c CJK Ideograph-5D3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d3f CJK Ideograph-5D3F | 5d3e CJK Ideograph-5D3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d41 CJK Ideograph-5D41 | 5d40 CJK Ideograph-5D40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d43 CJK Ideograph-5D43 | 5d42 CJK Ideograph-5D42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d45 CJK Ideograph-5D45 | 5d44 CJK Ideograph-5D44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d47 CJK Ideograph-5D47 | 5d46 CJK Ideograph-5D46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d49 CJK Ideograph-5D49 | 5d48 CJK Ideograph-5D48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d4b CJK Ideograph-5D4B | 5d4a CJK Ideograph-5D4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d4d CJK Ideograph-5D4D | 5d4c CJK Ideograph-5D4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d4f CJK Ideograph-5D4F | 5d4e CJK Ideograph-5D4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d51 CJK Ideograph-5D51 | 5d50 CJK Ideograph-5D50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d53 CJK Ideograph-5D53 | 5d52 CJK Ideograph-5D52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d55 CJK Ideograph-5D55 | 5d54 CJK Ideograph-5D54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d57 CJK Ideograph-5D57 | 5d56 CJK Ideograph-5D56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d59 CJK Ideograph-5D59 | 5d58 CJK Ideograph-5D58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d5b CJK Ideograph-5D5B | 5d5a CJK Ideograph-5D5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d5d CJK Ideograph-5D5D | 5d5c CJK Ideograph-5D5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d5f CJK Ideograph-5D5F | 5d5e CJK Ideograph-5D5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d61 CJK Ideograph-5D61 | 5d60 CJK Ideograph-5D60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d63 CJK Ideograph-5D63 | 5d62 CJK Ideograph-5D62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d65 CJK Ideograph-5D65 | 5d64 CJK Ideograph-5D64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d67 CJK Ideograph-5D67 | 5d66 CJK Ideograph-5D66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d69 CJK Ideograph-5D69 | 5d68 CJK Ideograph-5D68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d6b CJK Ideograph-5D6B | 5d6a CJK Ideograph-5D6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d6d CJK Ideograph-5D6D | 5d6c CJK Ideograph-5D6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d6f CJK Ideograph-5D6F | 5d6e CJK Ideograph-5D6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d71 CJK Ideograph-5D71 | 5d70 CJK Ideograph-5D70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d73 CJK Ideograph-5D73 | 5d72 CJK Ideograph-5D72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d75 CJK Ideograph-5D75 | 5d74 CJK Ideograph-5D74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d77 CJK Ideograph-5D77 | 5d76 CJK Ideograph-5D76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d79 CJK Ideograph-5D79 | 5d78 CJK Ideograph-5D78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d7b CJK Ideograph-5D7B | 5d7a CJK Ideograph-5D7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d7d CJK Ideograph-5D7D | 5d7c CJK Ideograph-5D7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d7f CJK Ideograph-5D7F | 5d7e CJK Ideograph-5D7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d81 CJK Ideograph-5D81 | 5d80 CJK Ideograph-5D80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d83 CJK Ideograph-5D83 | 5d82 CJK Ideograph-5D82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d85 CJK Ideograph-5D85 | 5d84 CJK Ideograph-5D84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d87 CJK Ideograph-5D87 | 5d86 CJK Ideograph-5D86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d89 CJK Ideograph-5D89 | 5d88 CJK Ideograph-5D88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d8b CJK Ideograph-5D8B | 5d8a CJK Ideograph-5D8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d8d CJK Ideograph-5D8D | 5d8c CJK Ideograph-5D8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d8f CJK Ideograph-5D8F | 5d8e CJK Ideograph-5D8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d91 CJK Ideograph-5D91 | 5d90 CJK Ideograph-5D90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d93 CJK Ideograph-5D93 | 5d92 CJK Ideograph-5D92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d95 CJK Ideograph-5D95 | 5d94 CJK Ideograph-5D94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d97 CJK Ideograph-5D97 | 5d96 CJK Ideograph-5D96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d99 CJK Ideograph-5D99 | 5d98 CJK Ideograph-5D98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d9b CJK Ideograph-5D9B | 5d9a CJK Ideograph-5D9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d9d CJK Ideograph-5D9D | 5d9c CJK Ideograph-5D9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d9f CJK Ideograph-5D9F | 5d9e CJK Ideograph-5D9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5da1 CJK Ideograph-5DA1 | 5da0 CJK Ideograph-5DA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5da3 CJK Ideograph-5DA3 | 5da2 CJK Ideograph-5DA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5da5 CJK Ideograph-5DA5 | 5da4 CJK Ideograph-5DA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5da7 CJK Ideograph-5DA7 | 5da6 CJK Ideograph-5DA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5da9 CJK Ideograph-5DA9 | 5da8 CJK Ideograph-5DA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dab CJK Ideograph-5DAB | 5daa CJK Ideograph-5DAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dad CJK Ideograph-5DAD | 5dac CJK Ideograph-5DAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5daf CJK Ideograph-5DAF | 5dae CJK Ideograph-5DAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5db1 CJK Ideograph-5DB1 | 5db0 CJK Ideograph-5DB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5db3 CJK Ideograph-5DB3 | 5db2 CJK Ideograph-5DB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5db5 CJK Ideograph-5DB5 | 5db4 CJK Ideograph-5DB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5db7 CJK Ideograph-5DB7 | 5db6 CJK Ideograph-5DB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5db9 CJK Ideograph-5DB9 | 5db8 CJK Ideograph-5DB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dbb CJK Ideograph-5DBB | 5dba CJK Ideograph-5DBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dbd CJK Ideograph-5DBD | 5dbc CJK Ideograph-5DBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dbf CJK Ideograph-5DBF | 5dbe CJK Ideograph-5DBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dc1 CJK Ideograph-5DC1 | 5dc0 CJK Ideograph-5DC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dc3 CJK Ideograph-5DC3 | 5dc2 CJK Ideograph-5DC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dc5 CJK Ideograph-5DC5 | 5dc4 CJK Ideograph-5DC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dc7 CJK Ideograph-5DC7 | 5dc6 CJK Ideograph-5DC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dc9 CJK Ideograph-5DC9 | 5dc8 CJK Ideograph-5DC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dcb CJK Ideograph-5DCB | 5dca CJK Ideograph-5DCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dcd CJK Ideograph-5DCD | 5dcc CJK Ideograph-5DCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dcf CJK Ideograph-5DCF | 5dce CJK Ideograph-5DCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dd1 CJK Ideograph-5DD1 | 5dd0 CJK Ideograph-5DD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dd3 CJK Ideograph-5DD3 | 5dd2 CJK Ideograph-5DD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dd5 CJK Ideograph-5DD5 | 5dd4 CJK Ideograph-5DD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dd7 CJK Ideograph-5DD7 | 5dd6 CJK Ideograph-5DD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dd9 CJK Ideograph-5DD9 | 5dd8 CJK Ideograph-5DD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ddb CJK Ideograph-5DDB | 5dda CJK Ideograph-5DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ddd CJK Ideograph-5DDD | 5ddc CJK Ideograph-5DDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ddf CJK Ideograph-5DDF | 5dde CJK Ideograph-5DDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5de1 CJK Ideograph-5DE1 | 5de0 CJK Ideograph-5DE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5de3 CJK Ideograph-5DE3 | 5de2 CJK Ideograph-5DE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5de5 CJK Ideograph-5DE5 | 5de4 CJK Ideograph-5DE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5de7 CJK Ideograph-5DE7 | 5de6 CJK Ideograph-5DE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5de9 CJK Ideograph-5DE9 | 5de8 CJK Ideograph-5DE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5deb CJK Ideograph-5DEB | 5dea CJK Ideograph-5DEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ded CJK Ideograph-5DED | 5dec CJK Ideograph-5DEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5def CJK Ideograph-5DEF | 5dee CJK Ideograph-5DEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5df1 CJK Ideograph-5DF1 | 5df0 CJK Ideograph-5DF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5df3 CJK Ideograph-5DF3 | 5df2 CJK Ideograph-5DF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5df5 CJK Ideograph-5DF5 | 5df4 CJK Ideograph-5DF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5df7 CJK Ideograph-5DF7 | 5df6 CJK Ideograph-5DF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5df9 CJK Ideograph-5DF9 | 5df8 CJK Ideograph-5DF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dfb CJK Ideograph-5DFB | 5dfa CJK Ideograph-5DFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dfd CJK Ideograph-5DFD | 5dfc CJK Ideograph-5DFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dff CJK Ideograph-5DFF | 5dfe CJK Ideograph-5DFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e01 CJK Ideograph-5E01 | 5e00 CJK Ideograph-5E00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e03 CJK Ideograph-5E03 | 5e02 CJK Ideograph-5E02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e05 CJK Ideograph-5E05 | 5e04 CJK Ideograph-5E04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e07 CJK Ideograph-5E07 | 5e06 CJK Ideograph-5E06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e09 CJK Ideograph-5E09 | 5e08 CJK Ideograph-5E08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e0b CJK Ideograph-5E0B | 5e0a CJK Ideograph-5E0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e0d CJK Ideograph-5E0D | 5e0c CJK Ideograph-5E0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e0f CJK Ideograph-5E0F | 5e0e CJK Ideograph-5E0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e11 CJK Ideograph-5E11 | 5e10 CJK Ideograph-5E10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e13 CJK Ideograph-5E13 | 5e12 CJK Ideograph-5E12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e15 CJK Ideograph-5E15 | 5e14 CJK Ideograph-5E14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e17 CJK Ideograph-5E17 | 5e16 CJK Ideograph-5E16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e19 CJK Ideograph-5E19 | 5e18 CJK Ideograph-5E18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e1b CJK Ideograph-5E1B | 5e1a CJK Ideograph-5E1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e1d CJK Ideograph-5E1D | 5e1c CJK Ideograph-5E1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e1f CJK Ideograph-5E1F | 5e1e CJK Ideograph-5E1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e21 CJK Ideograph-5E21 | 5e20 CJK Ideograph-5E20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e23 CJK Ideograph-5E23 | 5e22 CJK Ideograph-5E22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e25 CJK Ideograph-5E25 | 5e24 CJK Ideograph-5E24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e27 CJK Ideograph-5E27 | 5e26 CJK Ideograph-5E26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e29 CJK Ideograph-5E29 | 5e28 CJK Ideograph-5E28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e2b CJK Ideograph-5E2B | 5e2a CJK Ideograph-5E2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e2d CJK Ideograph-5E2D | 5e2c CJK Ideograph-5E2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e2f CJK Ideograph-5E2F | 5e2e CJK Ideograph-5E2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e31 CJK Ideograph-5E31 | 5e30 CJK Ideograph-5E30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e33 CJK Ideograph-5E33 | 5e32 CJK Ideograph-5E32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e35 CJK Ideograph-5E35 | 5e34 CJK Ideograph-5E34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e37 CJK Ideograph-5E37 | 5e36 CJK Ideograph-5E36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e39 CJK Ideograph-5E39 | 5e38 CJK Ideograph-5E38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e3b CJK Ideograph-5E3B | 5e3a CJK Ideograph-5E3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e3d CJK Ideograph-5E3D | 5e3c CJK Ideograph-5E3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e3f CJK Ideograph-5E3F | 5e3e CJK Ideograph-5E3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e41 CJK Ideograph-5E41 | 5e40 CJK Ideograph-5E40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e43 CJK Ideograph-5E43 | 5e42 CJK Ideograph-5E42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e45 CJK Ideograph-5E45 | 5e44 CJK Ideograph-5E44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e47 CJK Ideograph-5E47 | 5e46 CJK Ideograph-5E46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e49 CJK Ideograph-5E49 | 5e48 CJK Ideograph-5E48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e4b CJK Ideograph-5E4B | 5e4a CJK Ideograph-5E4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e4d CJK Ideograph-5E4D | 5e4c CJK Ideograph-5E4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e4f CJK Ideograph-5E4F | 5e4e CJK Ideograph-5E4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e51 CJK Ideograph-5E51 | 5e50 CJK Ideograph-5E50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e53 CJK Ideograph-5E53 | 5e52 CJK Ideograph-5E52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e55 CJK Ideograph-5E55 | 5e54 CJK Ideograph-5E54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e57 CJK Ideograph-5E57 | 5e56 CJK Ideograph-5E56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e59 CJK Ideograph-5E59 | 5e58 CJK Ideograph-5E58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e5b CJK Ideograph-5E5B | 5e5a CJK Ideograph-5E5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e5d CJK Ideograph-5E5D | 5e5c CJK Ideograph-5E5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e5f CJK Ideograph-5E5F | 5e5e CJK Ideograph-5E5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e61 CJK Ideograph-5E61 | 5e60 CJK Ideograph-5E60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e63 CJK Ideograph-5E63 | 5e62 CJK Ideograph-5E62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e65 CJK Ideograph-5E65 | 5e64 CJK Ideograph-5E64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e67 CJK Ideograph-5E67 | 5e66 CJK Ideograph-5E66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e69 CJK Ideograph-5E69 | 5e68 CJK Ideograph-5E68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e6b CJK Ideograph-5E6B | 5e6a CJK Ideograph-5E6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e6d CJK Ideograph-5E6D | 5e6c CJK Ideograph-5E6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e6f CJK Ideograph-5E6F | 5e6e CJK Ideograph-5E6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e71 CJK Ideograph-5E71 | 5e70 CJK Ideograph-5E70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e73 CJK Ideograph-5E73 | 5e72 CJK Ideograph-5E72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e75 CJK Ideograph-5E75 | 5e74 CJK Ideograph-5E74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e77 CJK Ideograph-5E77 | 5e76 CJK Ideograph-5E76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e79 CJK Ideograph-5E79 | 5e78 CJK Ideograph-5E78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e7b CJK Ideograph-5E7B | 5e7a CJK Ideograph-5E7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e7d CJK Ideograph-5E7D | 5e7c CJK Ideograph-5E7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e7f CJK Ideograph-5E7F | 5e7e CJK Ideograph-5E7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e81 CJK Ideograph-5E81 | 5e80 CJK Ideograph-5E80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e83 CJK Ideograph-5E83 | 5e82 CJK Ideograph-5E82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e85 CJK Ideograph-5E85 | 5e84 CJK Ideograph-5E84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e87 CJK Ideograph-5E87 | 5e86 CJK Ideograph-5E86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e89 CJK Ideograph-5E89 | 5e88 CJK Ideograph-5E88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e8b CJK Ideograph-5E8B | 5e8a CJK Ideograph-5E8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e8d CJK Ideograph-5E8D | 5e8c CJK Ideograph-5E8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e8f CJK Ideograph-5E8F | 5e8e CJK Ideograph-5E8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e91 CJK Ideograph-5E91 | 5e90 CJK Ideograph-5E90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e93 CJK Ideograph-5E93 | 5e92 CJK Ideograph-5E92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e95 CJK Ideograph-5E95 | 5e94 CJK Ideograph-5E94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e97 CJK Ideograph-5E97 | 5e96 CJK Ideograph-5E96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e99 CJK Ideograph-5E99 | 5e98 CJK Ideograph-5E98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e9b CJK Ideograph-5E9B | 5e9a CJK Ideograph-5E9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e9d CJK Ideograph-5E9D | 5e9c CJK Ideograph-5E9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e9f CJK Ideograph-5E9F | 5e9e CJK Ideograph-5E9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ea1 CJK Ideograph-5EA1 | 5ea0 CJK Ideograph-5EA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ea3 CJK Ideograph-5EA3 | 5ea2 CJK Ideograph-5EA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ea5 CJK Ideograph-5EA5 | 5ea4 CJK Ideograph-5EA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ea7 CJK Ideograph-5EA7 | 5ea6 CJK Ideograph-5EA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ea9 CJK Ideograph-5EA9 | 5ea8 CJK Ideograph-5EA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eab CJK Ideograph-5EAB | 5eaa CJK Ideograph-5EAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ead CJK Ideograph-5EAD | 5eac CJK Ideograph-5EAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eaf CJK Ideograph-5EAF | 5eae CJK Ideograph-5EAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eb1 CJK Ideograph-5EB1 | 5eb0 CJK Ideograph-5EB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eb3 CJK Ideograph-5EB3 | 5eb2 CJK Ideograph-5EB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eb5 CJK Ideograph-5EB5 | 5eb4 CJK Ideograph-5EB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eb7 CJK Ideograph-5EB7 | 5eb6 CJK Ideograph-5EB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eb9 CJK Ideograph-5EB9 | 5eb8 CJK Ideograph-5EB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ebb CJK Ideograph-5EBB | 5eba CJK Ideograph-5EBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ebd CJK Ideograph-5EBD | 5ebc CJK Ideograph-5EBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ebf CJK Ideograph-5EBF | 5ebe CJK Ideograph-5EBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ec1 CJK Ideograph-5EC1 | 5ec0 CJK Ideograph-5EC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ec3 CJK Ideograph-5EC3 | 5ec2 CJK Ideograph-5EC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ec5 CJK Ideograph-5EC5 | 5ec4 CJK Ideograph-5EC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ec7 CJK Ideograph-5EC7 | 5ec6 CJK Ideograph-5EC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ec9 CJK Ideograph-5EC9 | 5ec8 CJK Ideograph-5EC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ecb CJK Ideograph-5ECB | 5eca CJK Ideograph-5ECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ecd CJK Ideograph-5ECD | 5ecc CJK Ideograph-5ECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ecf CJK Ideograph-5ECF | 5ece CJK Ideograph-5ECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ed1 CJK Ideograph-5ED1 | 5ed0 CJK Ideograph-5ED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ed3 CJK Ideograph-5ED3 | 5ed2 CJK Ideograph-5ED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ed5 CJK Ideograph-5ED5 | 5ed4 CJK Ideograph-5ED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ed7 CJK Ideograph-5ED7 | 5ed6 CJK Ideograph-5ED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ed9 CJK Ideograph-5ED9 | 5ed8 CJK Ideograph-5ED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5edb CJK Ideograph-5EDB | 5eda CJK Ideograph-5EDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5edd CJK Ideograph-5EDD | 5edc CJK Ideograph-5EDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5edf CJK Ideograph-5EDF | 5ede CJK Ideograph-5EDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ee1 CJK Ideograph-5EE1 | 5ee0 CJK Ideograph-5EE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ee3 CJK Ideograph-5EE3 | 5ee2 CJK Ideograph-5EE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ee5 CJK Ideograph-5EE5 | 5ee4 CJK Ideograph-5EE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ee7 CJK Ideograph-5EE7 | 5ee6 CJK Ideograph-5EE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ee9 CJK Ideograph-5EE9 | 5ee8 CJK Ideograph-5EE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eeb CJK Ideograph-5EEB | 5eea CJK Ideograph-5EEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eed CJK Ideograph-5EED | 5eec CJK Ideograph-5EEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eef CJK Ideograph-5EEF | 5eee CJK Ideograph-5EEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ef1 CJK Ideograph-5EF1 | 5ef0 CJK Ideograph-5EF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ef3 CJK Ideograph-5EF3 | 5ef2 CJK Ideograph-5EF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ef5 CJK Ideograph-5EF5 | 5ef4 CJK Ideograph-5EF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ef7 CJK Ideograph-5EF7 | 5ef6 CJK Ideograph-5EF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ef9 CJK Ideograph-5EF9 | 5ef8 CJK Ideograph-5EF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5efb CJK Ideograph-5EFB | 5efa CJK Ideograph-5EFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5efd CJK Ideograph-5EFD | 5efc CJK Ideograph-5EFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eff CJK Ideograph-5EFF | 5efe CJK Ideograph-5EFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f01 CJK Ideograph-5F01 | 5f00 CJK Ideograph-5F00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f03 CJK Ideograph-5F03 | 5f02 CJK Ideograph-5F02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f05 CJK Ideograph-5F05 | 5f04 CJK Ideograph-5F04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f07 CJK Ideograph-5F07 | 5f06 CJK Ideograph-5F06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f09 CJK Ideograph-5F09 | 5f08 CJK Ideograph-5F08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f0b CJK Ideograph-5F0B | 5f0a CJK Ideograph-5F0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f0d CJK Ideograph-5F0D | 5f0c CJK Ideograph-5F0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f0f CJK Ideograph-5F0F | 5f0e CJK Ideograph-5F0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f11 CJK Ideograph-5F11 | 5f10 CJK Ideograph-5F10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f13 CJK Ideograph-5F13 | 5f12 CJK Ideograph-5F12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f15 CJK Ideograph-5F15 | 5f14 CJK Ideograph-5F14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f17 CJK Ideograph-5F17 | 5f16 CJK Ideograph-5F16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f19 CJK Ideograph-5F19 | 5f18 CJK Ideograph-5F18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f1b CJK Ideograph-5F1B | 5f1a CJK Ideograph-5F1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f1d CJK Ideograph-5F1D | 5f1c CJK Ideograph-5F1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f1f CJK Ideograph-5F1F | 5f1e CJK Ideograph-5F1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f21 CJK Ideograph-5F21 | 5f20 CJK Ideograph-5F20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f23 CJK Ideograph-5F23 | 5f22 CJK Ideograph-5F22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f25 CJK Ideograph-5F25 | 5f24 CJK Ideograph-5F24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f27 CJK Ideograph-5F27 | 5f26 CJK Ideograph-5F26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f29 CJK Ideograph-5F29 | 5f28 CJK Ideograph-5F28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f2b CJK Ideograph-5F2B | 5f2a CJK Ideograph-5F2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f2d CJK Ideograph-5F2D | 5f2c CJK Ideograph-5F2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f2f CJK Ideograph-5F2F | 5f2e CJK Ideograph-5F2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f31 CJK Ideograph-5F31 | 5f30 CJK Ideograph-5F30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f33 CJK Ideograph-5F33 | 5f32 CJK Ideograph-5F32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f35 CJK Ideograph-5F35 | 5f34 CJK Ideograph-5F34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f37 CJK Ideograph-5F37 | 5f36 CJK Ideograph-5F36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f39 CJK Ideograph-5F39 | 5f38 CJK Ideograph-5F38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f3b CJK Ideograph-5F3B | 5f3a CJK Ideograph-5F3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f3d CJK Ideograph-5F3D | 5f3c CJK Ideograph-5F3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f3f CJK Ideograph-5F3F | 5f3e CJK Ideograph-5F3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f41 CJK Ideograph-5F41 | 5f40 CJK Ideograph-5F40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f43 CJK Ideograph-5F43 | 5f42 CJK Ideograph-5F42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f45 CJK Ideograph-5F45 | 5f44 CJK Ideograph-5F44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f47 CJK Ideograph-5F47 | 5f46 CJK Ideograph-5F46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f49 CJK Ideograph-5F49 | 5f48 CJK Ideograph-5F48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f4b CJK Ideograph-5F4B | 5f4a CJK Ideograph-5F4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f4d CJK Ideograph-5F4D | 5f4c CJK Ideograph-5F4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f4f CJK Ideograph-5F4F | 5f4e CJK Ideograph-5F4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f51 CJK Ideograph-5F51 | 5f50 CJK Ideograph-5F50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f53 CJK Ideograph-5F53 | 5f52 CJK Ideograph-5F52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f55 CJK Ideograph-5F55 | 5f54 CJK Ideograph-5F54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f57 CJK Ideograph-5F57 | 5f56 CJK Ideograph-5F56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f59 CJK Ideograph-5F59 | 5f58 CJK Ideograph-5F58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f5b CJK Ideograph-5F5B | 5f5a CJK Ideograph-5F5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f5d CJK Ideograph-5F5D | 5f5c CJK Ideograph-5F5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f5f CJK Ideograph-5F5F | 5f5e CJK Ideograph-5F5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f61 CJK Ideograph-5F61 | 5f60 CJK Ideograph-5F60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f63 CJK Ideograph-5F63 | 5f62 CJK Ideograph-5F62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f65 CJK Ideograph-5F65 | 5f64 CJK Ideograph-5F64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f67 CJK Ideograph-5F67 | 5f66 CJK Ideograph-5F66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f69 CJK Ideograph-5F69 | 5f68 CJK Ideograph-5F68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f6b CJK Ideograph-5F6B | 5f6a CJK Ideograph-5F6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f6d CJK Ideograph-5F6D | 5f6c CJK Ideograph-5F6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f6f CJK Ideograph-5F6F | 5f6e CJK Ideograph-5F6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f71 CJK Ideograph-5F71 | 5f70 CJK Ideograph-5F70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f73 CJK Ideograph-5F73 | 5f72 CJK Ideograph-5F72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f75 CJK Ideograph-5F75 | 5f74 CJK Ideograph-5F74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f77 CJK Ideograph-5F77 | 5f76 CJK Ideograph-5F76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f79 CJK Ideograph-5F79 | 5f78 CJK Ideograph-5F78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f7b CJK Ideograph-5F7B | 5f7a CJK Ideograph-5F7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f7d CJK Ideograph-5F7D | 5f7c CJK Ideograph-5F7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f7f CJK Ideograph-5F7F | 5f7e CJK Ideograph-5F7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f81 CJK Ideograph-5F81 | 5f80 CJK Ideograph-5F80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f83 CJK Ideograph-5F83 | 5f82 CJK Ideograph-5F82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f85 CJK Ideograph-5F85 | 5f84 CJK Ideograph-5F84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f87 CJK Ideograph-5F87 | 5f86 CJK Ideograph-5F86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f89 CJK Ideograph-5F89 | 5f88 CJK Ideograph-5F88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f8b CJK Ideograph-5F8B | 5f8a CJK Ideograph-5F8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f8d CJK Ideograph-5F8D | 5f8c CJK Ideograph-5F8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f8f CJK Ideograph-5F8F | 5f8e CJK Ideograph-5F8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f91 CJK Ideograph-5F91 | 5f90 CJK Ideograph-5F90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f93 CJK Ideograph-5F93 | 5f92 CJK Ideograph-5F92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f95 CJK Ideograph-5F95 | 5f94 CJK Ideograph-5F94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f97 CJK Ideograph-5F97 | 5f96 CJK Ideograph-5F96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f99 CJK Ideograph-5F99 | 5f98 CJK Ideograph-5F98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f9b CJK Ideograph-5F9B | 5f9a CJK Ideograph-5F9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f9d CJK Ideograph-5F9D | 5f9c CJK Ideograph-5F9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f9f CJK Ideograph-5F9F | 5f9e CJK Ideograph-5F9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fa1 CJK Ideograph-5FA1 | 5fa0 CJK Ideograph-5FA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fa3 CJK Ideograph-5FA3 | 5fa2 CJK Ideograph-5FA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fa5 CJK Ideograph-5FA5 | 5fa4 CJK Ideograph-5FA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fa7 CJK Ideograph-5FA7 | 5fa6 CJK Ideograph-5FA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fa9 CJK Ideograph-5FA9 | 5fa8 CJK Ideograph-5FA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fab CJK Ideograph-5FAB | 5faa CJK Ideograph-5FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fad CJK Ideograph-5FAD | 5fac CJK Ideograph-5FAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5faf CJK Ideograph-5FAF | 5fae CJK Ideograph-5FAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fb1 CJK Ideograph-5FB1 | 5fb0 CJK Ideograph-5FB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fb3 CJK Ideograph-5FB3 | 5fb2 CJK Ideograph-5FB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fb5 CJK Ideograph-5FB5 | 5fb4 CJK Ideograph-5FB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fb7 CJK Ideograph-5FB7 | 5fb6 CJK Ideograph-5FB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fb9 CJK Ideograph-5FB9 | 5fb8 CJK Ideograph-5FB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fbb CJK Ideograph-5FBB | 5fba CJK Ideograph-5FBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fbd CJK Ideograph-5FBD | 5fbc CJK Ideograph-5FBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fbf CJK Ideograph-5FBF | 5fbe CJK Ideograph-5FBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fc1 CJK Ideograph-5FC1 | 5fc0 CJK Ideograph-5FC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fc3 CJK Ideograph-5FC3 | 5fc2 CJK Ideograph-5FC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fc5 CJK Ideograph-5FC5 | 5fc4 CJK Ideograph-5FC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fc7 CJK Ideograph-5FC7 | 5fc6 CJK Ideograph-5FC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fc9 CJK Ideograph-5FC9 | 5fc8 CJK Ideograph-5FC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fcb CJK Ideograph-5FCB | 5fca CJK Ideograph-5FCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fcd CJK Ideograph-5FCD | 5fcc CJK Ideograph-5FCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fcf CJK Ideograph-5FCF | 5fce CJK Ideograph-5FCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fd1 CJK Ideograph-5FD1 | 5fd0 CJK Ideograph-5FD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fd3 CJK Ideograph-5FD3 | 5fd2 CJK Ideograph-5FD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fd5 CJK Ideograph-5FD5 | 5fd4 CJK Ideograph-5FD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fd7 CJK Ideograph-5FD7 | 5fd6 CJK Ideograph-5FD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fd9 CJK Ideograph-5FD9 | 5fd8 CJK Ideograph-5FD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fdb CJK Ideograph-5FDB | 5fda CJK Ideograph-5FDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fdd CJK Ideograph-5FDD | 5fdc CJK Ideograph-5FDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fdf CJK Ideograph-5FDF | 5fde CJK Ideograph-5FDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fe1 CJK Ideograph-5FE1 | 5fe0 CJK Ideograph-5FE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fe3 CJK Ideograph-5FE3 | 5fe2 CJK Ideograph-5FE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fe5 CJK Ideograph-5FE5 | 5fe4 CJK Ideograph-5FE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fe7 CJK Ideograph-5FE7 | 5fe6 CJK Ideograph-5FE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fe9 CJK Ideograph-5FE9 | 5fe8 CJK Ideograph-5FE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5feb CJK Ideograph-5FEB | 5fea CJK Ideograph-5FEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fed CJK Ideograph-5FED | 5fec CJK Ideograph-5FEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fef CJK Ideograph-5FEF | 5fee CJK Ideograph-5FEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ff1 CJK Ideograph-5FF1 | 5ff0 CJK Ideograph-5FF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ff3 CJK Ideograph-5FF3 | 5ff2 CJK Ideograph-5FF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ff5 CJK Ideograph-5FF5 | 5ff4 CJK Ideograph-5FF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ff7 CJK Ideograph-5FF7 | 5ff6 CJK Ideograph-5FF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ff9 CJK Ideograph-5FF9 | 5ff8 CJK Ideograph-5FF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ffb CJK Ideograph-5FFB | 5ffa CJK Ideograph-5FFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ffd CJK Ideograph-5FFD | 5ffc CJK Ideograph-5FFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fff CJK Ideograph-5FFF | 5ffe CJK Ideograph-5FFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6001 CJK Ideograph-6001 | 6000 CJK Ideograph-6000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6003 CJK Ideograph-6003 | 6002 CJK Ideograph-6002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6005 CJK Ideograph-6005 | 6004 CJK Ideograph-6004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6007 CJK Ideograph-6007 | 6006 CJK Ideograph-6006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6009 CJK Ideograph-6009 | 6008 CJK Ideograph-6008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 600b CJK Ideograph-600B | 600a CJK Ideograph-600A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 600d CJK Ideograph-600D | 600c CJK Ideograph-600C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 600f CJK Ideograph-600F | 600e CJK Ideograph-600E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6011 CJK Ideograph-6011 | 6010 CJK Ideograph-6010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6013 CJK Ideograph-6013 | 6012 CJK Ideograph-6012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6015 CJK Ideograph-6015 | 6014 CJK Ideograph-6014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6017 CJK Ideograph-6017 | 6016 CJK Ideograph-6016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6019 CJK Ideograph-6019 | 6018 CJK Ideograph-6018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 601b CJK Ideograph-601B | 601a CJK Ideograph-601A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 601d CJK Ideograph-601D | 601c CJK Ideograph-601C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 601f CJK Ideograph-601F | 601e CJK Ideograph-601E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6021 CJK Ideograph-6021 | 6020 CJK Ideograph-6020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6023 CJK Ideograph-6023 | 6022 CJK Ideograph-6022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6025 CJK Ideograph-6025 | 6024 CJK Ideograph-6024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6027 CJK Ideograph-6027 | 6026 CJK Ideograph-6026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6029 CJK Ideograph-6029 | 6028 CJK Ideograph-6028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 602b CJK Ideograph-602B | 602a CJK Ideograph-602A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 602d CJK Ideograph-602D | 602c CJK Ideograph-602C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 602f CJK Ideograph-602F | 602e CJK Ideograph-602E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6031 CJK Ideograph-6031 | 6030 CJK Ideograph-6030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6033 CJK Ideograph-6033 | 6032 CJK Ideograph-6032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6035 CJK Ideograph-6035 | 6034 CJK Ideograph-6034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6037 CJK Ideograph-6037 | 6036 CJK Ideograph-6036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6039 CJK Ideograph-6039 | 6038 CJK Ideograph-6038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 603b CJK Ideograph-603B | 603a CJK Ideograph-603A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 603d CJK Ideograph-603D | 603c CJK Ideograph-603C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 603f CJK Ideograph-603F | 603e CJK Ideograph-603E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6041 CJK Ideograph-6041 | 6040 CJK Ideograph-6040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6043 CJK Ideograph-6043 | 6042 CJK Ideograph-6042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6045 CJK Ideograph-6045 | 6044 CJK Ideograph-6044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6047 CJK Ideograph-6047 | 6046 CJK Ideograph-6046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6049 CJK Ideograph-6049 | 6048 CJK Ideograph-6048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 604b CJK Ideograph-604B | 604a CJK Ideograph-604A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 604d CJK Ideograph-604D | 604c CJK Ideograph-604C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 604f CJK Ideograph-604F | 604e CJK Ideograph-604E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6051 CJK Ideograph-6051 | 6050 CJK Ideograph-6050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6053 CJK Ideograph-6053 | 6052 CJK Ideograph-6052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6055 CJK Ideograph-6055 | 6054 CJK Ideograph-6054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6057 CJK Ideograph-6057 | 6056 CJK Ideograph-6056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6059 CJK Ideograph-6059 | 6058 CJK Ideograph-6058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 605b CJK Ideograph-605B | 605a CJK Ideograph-605A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 605d CJK Ideograph-605D | 605c CJK Ideograph-605C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 605f CJK Ideograph-605F | 605e CJK Ideograph-605E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6061 CJK Ideograph-6061 | 6060 CJK Ideograph-6060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6063 CJK Ideograph-6063 | 6062 CJK Ideograph-6062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6065 CJK Ideograph-6065 | 6064 CJK Ideograph-6064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6067 CJK Ideograph-6067 | 6066 CJK Ideograph-6066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6069 CJK Ideograph-6069 | 6068 CJK Ideograph-6068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 606b CJK Ideograph-606B | 606a CJK Ideograph-606A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 606d CJK Ideograph-606D | 606c CJK Ideograph-606C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 606f CJK Ideograph-606F | 606e CJK Ideograph-606E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6071 CJK Ideograph-6071 | 6070 CJK Ideograph-6070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6073 CJK Ideograph-6073 | 6072 CJK Ideograph-6072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6075 CJK Ideograph-6075 | 6074 CJK Ideograph-6074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6077 CJK Ideograph-6077 | 6076 CJK Ideograph-6076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6079 CJK Ideograph-6079 | 6078 CJK Ideograph-6078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 607b CJK Ideograph-607B | 607a CJK Ideograph-607A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 607d CJK Ideograph-607D | 607c CJK Ideograph-607C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 607f CJK Ideograph-607F | 607e CJK Ideograph-607E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6081 CJK Ideograph-6081 | 6080 CJK Ideograph-6080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6083 CJK Ideograph-6083 | 6082 CJK Ideograph-6082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6085 CJK Ideograph-6085 | 6084 CJK Ideograph-6084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6087 CJK Ideograph-6087 | 6086 CJK Ideograph-6086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6089 CJK Ideograph-6089 | 6088 CJK Ideograph-6088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 608b CJK Ideograph-608B | 608a CJK Ideograph-608A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 608d CJK Ideograph-608D | 608c CJK Ideograph-608C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 608f CJK Ideograph-608F | 608e CJK Ideograph-608E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6091 CJK Ideograph-6091 | 6090 CJK Ideograph-6090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6093 CJK Ideograph-6093 | 6092 CJK Ideograph-6092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6095 CJK Ideograph-6095 | 6094 CJK Ideograph-6094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6097 CJK Ideograph-6097 | 6096 CJK Ideograph-6096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6099 CJK Ideograph-6099 | 6098 CJK Ideograph-6098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 609b CJK Ideograph-609B | 609a CJK Ideograph-609A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 609d CJK Ideograph-609D | 609c CJK Ideograph-609C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 609f CJK Ideograph-609F | 609e CJK Ideograph-609E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60a1 CJK Ideograph-60A1 | 60a0 CJK Ideograph-60A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60a3 CJK Ideograph-60A3 | 60a2 CJK Ideograph-60A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60a5 CJK Ideograph-60A5 | 60a4 CJK Ideograph-60A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60a7 CJK Ideograph-60A7 | 60a6 CJK Ideograph-60A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60a9 CJK Ideograph-60A9 | 60a8 CJK Ideograph-60A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60ab CJK Ideograph-60AB | 60aa CJK Ideograph-60AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60ad CJK Ideograph-60AD | 60ac CJK Ideograph-60AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60af CJK Ideograph-60AF | 60ae CJK Ideograph-60AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60b1 CJK Ideograph-60B1 | 60b0 CJK Ideograph-60B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60b3 CJK Ideograph-60B3 | 60b2 CJK Ideograph-60B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60b5 CJK Ideograph-60B5 | 60b4 CJK Ideograph-60B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60b7 CJK Ideograph-60B7 | 60b6 CJK Ideograph-60B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60b9 CJK Ideograph-60B9 | 60b8 CJK Ideograph-60B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60bb CJK Ideograph-60BB | 60ba CJK Ideograph-60BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60bd CJK Ideograph-60BD | 60bc CJK Ideograph-60BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60bf CJK Ideograph-60BF | 60be CJK Ideograph-60BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60c1 CJK Ideograph-60C1 | 60c0 CJK Ideograph-60C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60c3 CJK Ideograph-60C3 | 60c2 CJK Ideograph-60C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60c5 CJK Ideograph-60C5 | 60c4 CJK Ideograph-60C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60c7 CJK Ideograph-60C7 | 60c6 CJK Ideograph-60C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60c9 CJK Ideograph-60C9 | 60c8 CJK Ideograph-60C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60cb CJK Ideograph-60CB | 60ca CJK Ideograph-60CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60cd CJK Ideograph-60CD | 60cc CJK Ideograph-60CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60cf CJK Ideograph-60CF | 60ce CJK Ideograph-60CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60d1 CJK Ideograph-60D1 | 60d0 CJK Ideograph-60D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60d3 CJK Ideograph-60D3 | 60d2 CJK Ideograph-60D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60d5 CJK Ideograph-60D5 | 60d4 CJK Ideograph-60D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60d7 CJK Ideograph-60D7 | 60d6 CJK Ideograph-60D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60d9 CJK Ideograph-60D9 | 60d8 CJK Ideograph-60D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60db CJK Ideograph-60DB | 60da CJK Ideograph-60DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60dd CJK Ideograph-60DD | 60dc CJK Ideograph-60DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60df CJK Ideograph-60DF | 60de CJK Ideograph-60DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60e1 CJK Ideograph-60E1 | 60e0 CJK Ideograph-60E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60e3 CJK Ideograph-60E3 | 60e2 CJK Ideograph-60E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60e5 CJK Ideograph-60E5 | 60e4 CJK Ideograph-60E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60e7 CJK Ideograph-60E7 | 60e6 CJK Ideograph-60E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60e9 CJK Ideograph-60E9 | 60e8 CJK Ideograph-60E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60eb CJK Ideograph-60EB | 60ea CJK Ideograph-60EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60ed CJK Ideograph-60ED | 60ec CJK Ideograph-60EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60ef CJK Ideograph-60EF | 60ee CJK Ideograph-60EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60f1 CJK Ideograph-60F1 | 60f0 CJK Ideograph-60F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60f3 CJK Ideograph-60F3 | 60f2 CJK Ideograph-60F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60f5 CJK Ideograph-60F5 | 60f4 CJK Ideograph-60F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60f7 CJK Ideograph-60F7 | 60f6 CJK Ideograph-60F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60f9 CJK Ideograph-60F9 | 60f8 CJK Ideograph-60F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60fb CJK Ideograph-60FB | 60fa CJK Ideograph-60FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60fd CJK Ideograph-60FD | 60fc CJK Ideograph-60FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60ff CJK Ideograph-60FF | 60fe CJK Ideograph-60FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6101 CJK Ideograph-6101 | 6100 CJK Ideograph-6100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6103 CJK Ideograph-6103 | 6102 CJK Ideograph-6102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6105 CJK Ideograph-6105 | 6104 CJK Ideograph-6104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6107 CJK Ideograph-6107 | 6106 CJK Ideograph-6106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6109 CJK Ideograph-6109 | 6108 CJK Ideograph-6108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 610b CJK Ideograph-610B | 610a CJK Ideograph-610A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 610d CJK Ideograph-610D | 610c CJK Ideograph-610C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 610f CJK Ideograph-610F | 610e CJK Ideograph-610E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6111 CJK Ideograph-6111 | 6110 CJK Ideograph-6110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6113 CJK Ideograph-6113 | 6112 CJK Ideograph-6112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6115 CJK Ideograph-6115 | 6114 CJK Ideograph-6114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6117 CJK Ideograph-6117 | 6116 CJK Ideograph-6116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6119 CJK Ideograph-6119 | 6118 CJK Ideograph-6118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 611b CJK Ideograph-611B | 611a CJK Ideograph-611A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 611d CJK Ideograph-611D | 611c CJK Ideograph-611C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 611f CJK Ideograph-611F | 611e CJK Ideograph-611E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6121 CJK Ideograph-6121 | 6120 CJK Ideograph-6120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6123 CJK Ideograph-6123 | 6122 CJK Ideograph-6122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6125 CJK Ideograph-6125 | 6124 CJK Ideograph-6124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6127 CJK Ideograph-6127 | 6126 CJK Ideograph-6126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6129 CJK Ideograph-6129 | 6128 CJK Ideograph-6128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 612b CJK Ideograph-612B | 612a CJK Ideograph-612A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 612d CJK Ideograph-612D | 612c CJK Ideograph-612C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 612f CJK Ideograph-612F | 612e CJK Ideograph-612E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6131 CJK Ideograph-6131 | 6130 CJK Ideograph-6130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6133 CJK Ideograph-6133 | 6132 CJK Ideograph-6132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6135 CJK Ideograph-6135 | 6134 CJK Ideograph-6134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6137 CJK Ideograph-6137 | 6136 CJK Ideograph-6136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6139 CJK Ideograph-6139 | 6138 CJK Ideograph-6138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 613b CJK Ideograph-613B | 613a CJK Ideograph-613A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 613d CJK Ideograph-613D | 613c CJK Ideograph-613C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 613f CJK Ideograph-613F | 613e CJK Ideograph-613E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6141 CJK Ideograph-6141 | 6140 CJK Ideograph-6140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6143 CJK Ideograph-6143 | 6142 CJK Ideograph-6142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6145 CJK Ideograph-6145 | 6144 CJK Ideograph-6144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6147 CJK Ideograph-6147 | 6146 CJK Ideograph-6146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6149 CJK Ideograph-6149 | 6148 CJK Ideograph-6148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 614b CJK Ideograph-614B | 614a CJK Ideograph-614A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 614d CJK Ideograph-614D | 614c CJK Ideograph-614C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 614f CJK Ideograph-614F | 614e CJK Ideograph-614E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6151 CJK Ideograph-6151 | 6150 CJK Ideograph-6150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6153 CJK Ideograph-6153 | 6152 CJK Ideograph-6152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6155 CJK Ideograph-6155 | 6154 CJK Ideograph-6154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6157 CJK Ideograph-6157 | 6156 CJK Ideograph-6156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6159 CJK Ideograph-6159 | 6158 CJK Ideograph-6158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 615b CJK Ideograph-615B | 615a CJK Ideograph-615A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 615d CJK Ideograph-615D | 615c CJK Ideograph-615C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 615f CJK Ideograph-615F | 615e CJK Ideograph-615E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6161 CJK Ideograph-6161 | 6160 CJK Ideograph-6160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6163 CJK Ideograph-6163 | 6162 CJK Ideograph-6162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6165 CJK Ideograph-6165 | 6164 CJK Ideograph-6164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6167 CJK Ideograph-6167 | 6166 CJK Ideograph-6166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6169 CJK Ideograph-6169 | 6168 CJK Ideograph-6168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 616b CJK Ideograph-616B | 616a CJK Ideograph-616A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 616d CJK Ideograph-616D | 616c CJK Ideograph-616C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 616f CJK Ideograph-616F | 616e CJK Ideograph-616E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6171 CJK Ideograph-6171 | 6170 CJK Ideograph-6170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6173 CJK Ideograph-6173 | 6172 CJK Ideograph-6172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6175 CJK Ideograph-6175 | 6174 CJK Ideograph-6174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6177 CJK Ideograph-6177 | 6176 CJK Ideograph-6176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6179 CJK Ideograph-6179 | 6178 CJK Ideograph-6178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 617b CJK Ideograph-617B | 617a CJK Ideograph-617A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 617d CJK Ideograph-617D | 617c CJK Ideograph-617C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 617f CJK Ideograph-617F | 617e CJK Ideograph-617E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6181 CJK Ideograph-6181 | 6180 CJK Ideograph-6180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6183 CJK Ideograph-6183 | 6182 CJK Ideograph-6182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6185 CJK Ideograph-6185 | 6184 CJK Ideograph-6184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6187 CJK Ideograph-6187 | 6186 CJK Ideograph-6186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6189 CJK Ideograph-6189 | 6188 CJK Ideograph-6188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 618b CJK Ideograph-618B | 618a CJK Ideograph-618A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 618d CJK Ideograph-618D | 618c CJK Ideograph-618C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 618f CJK Ideograph-618F | 618e CJK Ideograph-618E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6191 CJK Ideograph-6191 | 6190 CJK Ideograph-6190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6193 CJK Ideograph-6193 | 6192 CJK Ideograph-6192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6195 CJK Ideograph-6195 | 6194 CJK Ideograph-6194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6197 CJK Ideograph-6197 | 6196 CJK Ideograph-6196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6199 CJK Ideograph-6199 | 6198 CJK Ideograph-6198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 619b CJK Ideograph-619B | 619a CJK Ideograph-619A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 619d CJK Ideograph-619D | 619c CJK Ideograph-619C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 619f CJK Ideograph-619F | 619e CJK Ideograph-619E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61a1 CJK Ideograph-61A1 | 61a0 CJK Ideograph-61A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61a3 CJK Ideograph-61A3 | 61a2 CJK Ideograph-61A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61a5 CJK Ideograph-61A5 | 61a4 CJK Ideograph-61A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61a7 CJK Ideograph-61A7 | 61a6 CJK Ideograph-61A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61a9 CJK Ideograph-61A9 | 61a8 CJK Ideograph-61A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61ab CJK Ideograph-61AB | 61aa CJK Ideograph-61AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61ad CJK Ideograph-61AD | 61ac CJK Ideograph-61AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61af CJK Ideograph-61AF | 61ae CJK Ideograph-61AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61b1 CJK Ideograph-61B1 | 61b0 CJK Ideograph-61B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61b3 CJK Ideograph-61B3 | 61b2 CJK Ideograph-61B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61b5 CJK Ideograph-61B5 | 61b4 CJK Ideograph-61B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61b7 CJK Ideograph-61B7 | 61b6 CJK Ideograph-61B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61b9 CJK Ideograph-61B9 | 61b8 CJK Ideograph-61B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61bb CJK Ideograph-61BB | 61ba CJK Ideograph-61BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61bd CJK Ideograph-61BD | 61bc CJK Ideograph-61BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61bf CJK Ideograph-61BF | 61be CJK Ideograph-61BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61c1 CJK Ideograph-61C1 | 61c0 CJK Ideograph-61C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61c3 CJK Ideograph-61C3 | 61c2 CJK Ideograph-61C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61c5 CJK Ideograph-61C5 | 61c4 CJK Ideograph-61C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61c7 CJK Ideograph-61C7 | 61c6 CJK Ideograph-61C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61c9 CJK Ideograph-61C9 | 61c8 CJK Ideograph-61C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61cb CJK Ideograph-61CB | 61ca CJK Ideograph-61CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61cd CJK Ideograph-61CD | 61cc CJK Ideograph-61CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61cf CJK Ideograph-61CF | 61ce CJK Ideograph-61CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61d1 CJK Ideograph-61D1 | 61d0 CJK Ideograph-61D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61d3 CJK Ideograph-61D3 | 61d2 CJK Ideograph-61D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61d5 CJK Ideograph-61D5 | 61d4 CJK Ideograph-61D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61d7 CJK Ideograph-61D7 | 61d6 CJK Ideograph-61D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61d9 CJK Ideograph-61D9 | 61d8 CJK Ideograph-61D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61db CJK Ideograph-61DB | 61da CJK Ideograph-61DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61dd CJK Ideograph-61DD | 61dc CJK Ideograph-61DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61df CJK Ideograph-61DF | 61de CJK Ideograph-61DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61e1 CJK Ideograph-61E1 | 61e0 CJK Ideograph-61E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61e3 CJK Ideograph-61E3 | 61e2 CJK Ideograph-61E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61e5 CJK Ideograph-61E5 | 61e4 CJK Ideograph-61E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61e7 CJK Ideograph-61E7 | 61e6 CJK Ideograph-61E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61e9 CJK Ideograph-61E9 | 61e8 CJK Ideograph-61E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61eb CJK Ideograph-61EB | 61ea CJK Ideograph-61EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61ed CJK Ideograph-61ED | 61ec CJK Ideograph-61EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61ef CJK Ideograph-61EF | 61ee CJK Ideograph-61EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61f1 CJK Ideograph-61F1 | 61f0 CJK Ideograph-61F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61f3 CJK Ideograph-61F3 | 61f2 CJK Ideograph-61F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61f5 CJK Ideograph-61F5 | 61f4 CJK Ideograph-61F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61f7 CJK Ideograph-61F7 | 61f6 CJK Ideograph-61F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61f9 CJK Ideograph-61F9 | 61f8 CJK Ideograph-61F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61fb CJK Ideograph-61FB | 61fa CJK Ideograph-61FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61fd CJK Ideograph-61FD | 61fc CJK Ideograph-61FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61ff CJK Ideograph-61FF | 61fe CJK Ideograph-61FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6201 CJK Ideograph-6201 | 6200 CJK Ideograph-6200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6203 CJK Ideograph-6203 | 6202 CJK Ideograph-6202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6205 CJK Ideograph-6205 | 6204 CJK Ideograph-6204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6207 CJK Ideograph-6207 | 6206 CJK Ideograph-6206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6209 CJK Ideograph-6209 | 6208 CJK Ideograph-6208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 620b CJK Ideograph-620B | 620a CJK Ideograph-620A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 620d CJK Ideograph-620D | 620c CJK Ideograph-620C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 620f CJK Ideograph-620F | 620e CJK Ideograph-620E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6211 CJK Ideograph-6211 | 6210 CJK Ideograph-6210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6213 CJK Ideograph-6213 | 6212 CJK Ideograph-6212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6215 CJK Ideograph-6215 | 6214 CJK Ideograph-6214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6217 CJK Ideograph-6217 | 6216 CJK Ideograph-6216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6219 CJK Ideograph-6219 | 6218 CJK Ideograph-6218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 621b CJK Ideograph-621B | 621a CJK Ideograph-621A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 621d CJK Ideograph-621D | 621c CJK Ideograph-621C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 621f CJK Ideograph-621F | 621e CJK Ideograph-621E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6221 CJK Ideograph-6221 | 6220 CJK Ideograph-6220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6223 CJK Ideograph-6223 | 6222 CJK Ideograph-6222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6225 CJK Ideograph-6225 | 6224 CJK Ideograph-6224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6227 CJK Ideograph-6227 | 6226 CJK Ideograph-6226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6229 CJK Ideograph-6229 | 6228 CJK Ideograph-6228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 622b CJK Ideograph-622B | 622a CJK Ideograph-622A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 622d CJK Ideograph-622D | 622c CJK Ideograph-622C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 622f CJK Ideograph-622F | 622e CJK Ideograph-622E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6231 CJK Ideograph-6231 | 6230 CJK Ideograph-6230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6233 CJK Ideograph-6233 | 6232 CJK Ideograph-6232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6235 CJK Ideograph-6235 | 6234 CJK Ideograph-6234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6237 CJK Ideograph-6237 | 6236 CJK Ideograph-6236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6239 CJK Ideograph-6239 | 6238 CJK Ideograph-6238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 623b CJK Ideograph-623B | 623a CJK Ideograph-623A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 623d CJK Ideograph-623D | 623c CJK Ideograph-623C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 623f CJK Ideograph-623F | 623e CJK Ideograph-623E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6241 CJK Ideograph-6241 | 6240 CJK Ideograph-6240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6243 CJK Ideograph-6243 | 6242 CJK Ideograph-6242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6245 CJK Ideograph-6245 | 6244 CJK Ideograph-6244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6247 CJK Ideograph-6247 | 6246 CJK Ideograph-6246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6249 CJK Ideograph-6249 | 6248 CJK Ideograph-6248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 624b CJK Ideograph-624B | 624a CJK Ideograph-624A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 624d CJK Ideograph-624D | 624c CJK Ideograph-624C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 624f CJK Ideograph-624F | 624e CJK Ideograph-624E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6251 CJK Ideograph-6251 | 6250 CJK Ideograph-6250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6253 CJK Ideograph-6253 | 6252 CJK Ideograph-6252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6255 CJK Ideograph-6255 | 6254 CJK Ideograph-6254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6257 CJK Ideograph-6257 | 6256 CJK Ideograph-6256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6259 CJK Ideograph-6259 | 6258 CJK Ideograph-6258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 625b CJK Ideograph-625B | 625a CJK Ideograph-625A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 625d CJK Ideograph-625D | 625c CJK Ideograph-625C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 625f CJK Ideograph-625F | 625e CJK Ideograph-625E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6261 CJK Ideograph-6261 | 6260 CJK Ideograph-6260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6263 CJK Ideograph-6263 | 6262 CJK Ideograph-6262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6265 CJK Ideograph-6265 | 6264 CJK Ideograph-6264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6267 CJK Ideograph-6267 | 6266 CJK Ideograph-6266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6269 CJK Ideograph-6269 | 6268 CJK Ideograph-6268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 626b CJK Ideograph-626B | 626a CJK Ideograph-626A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 626d CJK Ideograph-626D | 626c CJK Ideograph-626C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 626f CJK Ideograph-626F | 626e CJK Ideograph-626E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6271 CJK Ideograph-6271 | 6270 CJK Ideograph-6270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6273 CJK Ideograph-6273 | 6272 CJK Ideograph-6272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6275 CJK Ideograph-6275 | 6274 CJK Ideograph-6274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6277 CJK Ideograph-6277 | 6276 CJK Ideograph-6276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6279 CJK Ideograph-6279 | 6278 CJK Ideograph-6278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 627b CJK Ideograph-627B | 627a CJK Ideograph-627A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 627d CJK Ideograph-627D | 627c CJK Ideograph-627C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 627f CJK Ideograph-627F | 627e CJK Ideograph-627E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6281 CJK Ideograph-6281 | 6280 CJK Ideograph-6280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6283 CJK Ideograph-6283 | 6282 CJK Ideograph-6282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6285 CJK Ideograph-6285 | 6284 CJK Ideograph-6284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6287 CJK Ideograph-6287 | 6286 CJK Ideograph-6286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6289 CJK Ideograph-6289 | 6288 CJK Ideograph-6288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 628b CJK Ideograph-628B | 628a CJK Ideograph-628A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 628d CJK Ideograph-628D | 628c CJK Ideograph-628C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 628f CJK Ideograph-628F | 628e CJK Ideograph-628E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6291 CJK Ideograph-6291 | 6290 CJK Ideograph-6290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6293 CJK Ideograph-6293 | 6292 CJK Ideograph-6292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6295 CJK Ideograph-6295 | 6294 CJK Ideograph-6294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6297 CJK Ideograph-6297 | 6296 CJK Ideograph-6296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6299 CJK Ideograph-6299 | 6298 CJK Ideograph-6298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 629b CJK Ideograph-629B | 629a CJK Ideograph-629A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 629d CJK Ideograph-629D | 629c CJK Ideograph-629C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 629f CJK Ideograph-629F | 629e CJK Ideograph-629E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62a1 CJK Ideograph-62A1 | 62a0 CJK Ideograph-62A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62a3 CJK Ideograph-62A3 | 62a2 CJK Ideograph-62A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62a5 CJK Ideograph-62A5 | 62a4 CJK Ideograph-62A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62a7 CJK Ideograph-62A7 | 62a6 CJK Ideograph-62A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62a9 CJK Ideograph-62A9 | 62a8 CJK Ideograph-62A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62ab CJK Ideograph-62AB | 62aa CJK Ideograph-62AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62ad CJK Ideograph-62AD | 62ac CJK Ideograph-62AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62af CJK Ideograph-62AF | 62ae CJK Ideograph-62AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62b1 CJK Ideograph-62B1 | 62b0 CJK Ideograph-62B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62b3 CJK Ideograph-62B3 | 62b2 CJK Ideograph-62B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62b5 CJK Ideograph-62B5 | 62b4 CJK Ideograph-62B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62b7 CJK Ideograph-62B7 | 62b6 CJK Ideograph-62B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62b9 CJK Ideograph-62B9 | 62b8 CJK Ideograph-62B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62bb CJK Ideograph-62BB | 62ba CJK Ideograph-62BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62bd CJK Ideograph-62BD | 62bc CJK Ideograph-62BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62bf CJK Ideograph-62BF | 62be CJK Ideograph-62BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62c1 CJK Ideograph-62C1 | 62c0 CJK Ideograph-62C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62c3 CJK Ideograph-62C3 | 62c2 CJK Ideograph-62C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62c5 CJK Ideograph-62C5 | 62c4 CJK Ideograph-62C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62c7 CJK Ideograph-62C7 | 62c6 CJK Ideograph-62C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62c9 CJK Ideograph-62C9 | 62c8 CJK Ideograph-62C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62cb CJK Ideograph-62CB | 62ca CJK Ideograph-62CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62cd CJK Ideograph-62CD | 62cc CJK Ideograph-62CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62cf CJK Ideograph-62CF | 62ce CJK Ideograph-62CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62d1 CJK Ideograph-62D1 | 62d0 CJK Ideograph-62D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62d3 CJK Ideograph-62D3 | 62d2 CJK Ideograph-62D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62d5 CJK Ideograph-62D5 | 62d4 CJK Ideograph-62D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62d7 CJK Ideograph-62D7 | 62d6 CJK Ideograph-62D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62d9 CJK Ideograph-62D9 | 62d8 CJK Ideograph-62D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62db CJK Ideograph-62DB | 62da CJK Ideograph-62DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62dd CJK Ideograph-62DD | 62dc CJK Ideograph-62DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62df CJK Ideograph-62DF | 62de CJK Ideograph-62DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62e1 CJK Ideograph-62E1 | 62e0 CJK Ideograph-62E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62e3 CJK Ideograph-62E3 | 62e2 CJK Ideograph-62E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62e5 CJK Ideograph-62E5 | 62e4 CJK Ideograph-62E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62e7 CJK Ideograph-62E7 | 62e6 CJK Ideograph-62E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62e9 CJK Ideograph-62E9 | 62e8 CJK Ideograph-62E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62eb CJK Ideograph-62EB | 62ea CJK Ideograph-62EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62ed CJK Ideograph-62ED | 62ec CJK Ideograph-62EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62ef CJK Ideograph-62EF | 62ee CJK Ideograph-62EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62f1 CJK Ideograph-62F1 | 62f0 CJK Ideograph-62F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62f3 CJK Ideograph-62F3 | 62f2 CJK Ideograph-62F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62f5 CJK Ideograph-62F5 | 62f4 CJK Ideograph-62F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62f7 CJK Ideograph-62F7 | 62f6 CJK Ideograph-62F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62f9 CJK Ideograph-62F9 | 62f8 CJK Ideograph-62F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62fb CJK Ideograph-62FB | 62fa CJK Ideograph-62FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62fd CJK Ideograph-62FD | 62fc CJK Ideograph-62FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62ff CJK Ideograph-62FF | 62fe CJK Ideograph-62FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6301 CJK Ideograph-6301 | 6300 CJK Ideograph-6300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6303 CJK Ideograph-6303 | 6302 CJK Ideograph-6302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6305 CJK Ideograph-6305 | 6304 CJK Ideograph-6304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6307 CJK Ideograph-6307 | 6306 CJK Ideograph-6306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6309 CJK Ideograph-6309 | 6308 CJK Ideograph-6308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 630b CJK Ideograph-630B | 630a CJK Ideograph-630A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 630d CJK Ideograph-630D | 630c CJK Ideograph-630C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 630f CJK Ideograph-630F | 630e CJK Ideograph-630E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6311 CJK Ideograph-6311 | 6310 CJK Ideograph-6310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6313 CJK Ideograph-6313 | 6312 CJK Ideograph-6312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6315 CJK Ideograph-6315 | 6314 CJK Ideograph-6314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6317 CJK Ideograph-6317 | 6316 CJK Ideograph-6316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6319 CJK Ideograph-6319 | 6318 CJK Ideograph-6318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 631b CJK Ideograph-631B | 631a CJK Ideograph-631A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 631d CJK Ideograph-631D | 631c CJK Ideograph-631C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 631f CJK Ideograph-631F | 631e CJK Ideograph-631E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6321 CJK Ideograph-6321 | 6320 CJK Ideograph-6320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6323 CJK Ideograph-6323 | 6322 CJK Ideograph-6322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6325 CJK Ideograph-6325 | 6324 CJK Ideograph-6324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6327 CJK Ideograph-6327 | 6326 CJK Ideograph-6326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6329 CJK Ideograph-6329 | 6328 CJK Ideograph-6328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 632b CJK Ideograph-632B | 632a CJK Ideograph-632A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 632d CJK Ideograph-632D | 632c CJK Ideograph-632C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 632f CJK Ideograph-632F | 632e CJK Ideograph-632E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6331 CJK Ideograph-6331 | 6330 CJK Ideograph-6330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6333 CJK Ideograph-6333 | 6332 CJK Ideograph-6332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6335 CJK Ideograph-6335 | 6334 CJK Ideograph-6334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6337 CJK Ideograph-6337 | 6336 CJK Ideograph-6336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6339 CJK Ideograph-6339 | 6338 CJK Ideograph-6338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 633b CJK Ideograph-633B | 633a CJK Ideograph-633A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 633d CJK Ideograph-633D | 633c CJK Ideograph-633C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 633f CJK Ideograph-633F | 633e CJK Ideograph-633E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6341 CJK Ideograph-6341 | 6340 CJK Ideograph-6340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6343 CJK Ideograph-6343 | 6342 CJK Ideograph-6342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6345 CJK Ideograph-6345 | 6344 CJK Ideograph-6344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6347 CJK Ideograph-6347 | 6346 CJK Ideograph-6346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6349 CJK Ideograph-6349 | 6348 CJK Ideograph-6348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 634b CJK Ideograph-634B | 634a CJK Ideograph-634A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 634d CJK Ideograph-634D | 634c CJK Ideograph-634C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 634f CJK Ideograph-634F | 634e CJK Ideograph-634E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6351 CJK Ideograph-6351 | 6350 CJK Ideograph-6350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6353 CJK Ideograph-6353 | 6352 CJK Ideograph-6352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6355 CJK Ideograph-6355 | 6354 CJK Ideograph-6354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6357 CJK Ideograph-6357 | 6356 CJK Ideograph-6356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6359 CJK Ideograph-6359 | 6358 CJK Ideograph-6358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 635b CJK Ideograph-635B | 635a CJK Ideograph-635A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 635d CJK Ideograph-635D | 635c CJK Ideograph-635C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 635f CJK Ideograph-635F | 635e CJK Ideograph-635E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6361 CJK Ideograph-6361 | 6360 CJK Ideograph-6360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6363 CJK Ideograph-6363 | 6362 CJK Ideograph-6362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6365 CJK Ideograph-6365 | 6364 CJK Ideograph-6364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6367 CJK Ideograph-6367 | 6366 CJK Ideograph-6366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6369 CJK Ideograph-6369 | 6368 CJK Ideograph-6368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 636b CJK Ideograph-636B | 636a CJK Ideograph-636A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 636d CJK Ideograph-636D | 636c CJK Ideograph-636C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 636f CJK Ideograph-636F | 636e CJK Ideograph-636E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6371 CJK Ideograph-6371 | 6370 CJK Ideograph-6370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6373 CJK Ideograph-6373 | 6372 CJK Ideograph-6372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6375 CJK Ideograph-6375 | 6374 CJK Ideograph-6374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6377 CJK Ideograph-6377 | 6376 CJK Ideograph-6376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6379 CJK Ideograph-6379 | 6378 CJK Ideograph-6378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 637b CJK Ideograph-637B | 637a CJK Ideograph-637A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 637d CJK Ideograph-637D | 637c CJK Ideograph-637C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 637f CJK Ideograph-637F | 637e CJK Ideograph-637E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6381 CJK Ideograph-6381 | 6380 CJK Ideograph-6380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6383 CJK Ideograph-6383 | 6382 CJK Ideograph-6382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6385 CJK Ideograph-6385 | 6384 CJK Ideograph-6384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6387 CJK Ideograph-6387 | 6386 CJK Ideograph-6386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6389 CJK Ideograph-6389 | 6388 CJK Ideograph-6388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 638b CJK Ideograph-638B | 638a CJK Ideograph-638A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 638d CJK Ideograph-638D | 638c CJK Ideograph-638C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 638f CJK Ideograph-638F | 638e CJK Ideograph-638E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6391 CJK Ideograph-6391 | 6390 CJK Ideograph-6390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6393 CJK Ideograph-6393 | 6392 CJK Ideograph-6392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6395 CJK Ideograph-6395 | 6394 CJK Ideograph-6394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6397 CJK Ideograph-6397 | 6396 CJK Ideograph-6396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6399 CJK Ideograph-6399 | 6398 CJK Ideograph-6398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 639b CJK Ideograph-639B | 639a CJK Ideograph-639A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 639d CJK Ideograph-639D | 639c CJK Ideograph-639C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 639f CJK Ideograph-639F | 639e CJK Ideograph-639E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63a1 CJK Ideograph-63A1 | 63a0 CJK Ideograph-63A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63a3 CJK Ideograph-63A3 | 63a2 CJK Ideograph-63A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63a5 CJK Ideograph-63A5 | 63a4 CJK Ideograph-63A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63a7 CJK Ideograph-63A7 | 63a6 CJK Ideograph-63A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63a9 CJK Ideograph-63A9 | 63a8 CJK Ideograph-63A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63ab CJK Ideograph-63AB | 63aa CJK Ideograph-63AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63ad CJK Ideograph-63AD | 63ac CJK Ideograph-63AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63af CJK Ideograph-63AF | 63ae CJK Ideograph-63AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63b1 CJK Ideograph-63B1 | 63b0 CJK Ideograph-63B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63b3 CJK Ideograph-63B3 | 63b2 CJK Ideograph-63B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63b5 CJK Ideograph-63B5 | 63b4 CJK Ideograph-63B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63b7 CJK Ideograph-63B7 | 63b6 CJK Ideograph-63B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63b9 CJK Ideograph-63B9 | 63b8 CJK Ideograph-63B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63bb CJK Ideograph-63BB | 63ba CJK Ideograph-63BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63bd CJK Ideograph-63BD | 63bc CJK Ideograph-63BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63bf CJK Ideograph-63BF | 63be CJK Ideograph-63BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63c1 CJK Ideograph-63C1 | 63c0 CJK Ideograph-63C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63c3 CJK Ideograph-63C3 | 63c2 CJK Ideograph-63C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63c5 CJK Ideograph-63C5 | 63c4 CJK Ideograph-63C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63c7 CJK Ideograph-63C7 | 63c6 CJK Ideograph-63C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63c9 CJK Ideograph-63C9 | 63c8 CJK Ideograph-63C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63cb CJK Ideograph-63CB | 63ca CJK Ideograph-63CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63cd CJK Ideograph-63CD | 63cc CJK Ideograph-63CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63cf CJK Ideograph-63CF | 63ce CJK Ideograph-63CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63d1 CJK Ideograph-63D1 | 63d0 CJK Ideograph-63D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63d3 CJK Ideograph-63D3 | 63d2 CJK Ideograph-63D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63d5 CJK Ideograph-63D5 | 63d4 CJK Ideograph-63D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63d7 CJK Ideograph-63D7 | 63d6 CJK Ideograph-63D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63d9 CJK Ideograph-63D9 | 63d8 CJK Ideograph-63D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63db CJK Ideograph-63DB | 63da CJK Ideograph-63DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63dd CJK Ideograph-63DD | 63dc CJK Ideograph-63DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63df CJK Ideograph-63DF | 63de CJK Ideograph-63DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63e1 CJK Ideograph-63E1 | 63e0 CJK Ideograph-63E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63e3 CJK Ideograph-63E3 | 63e2 CJK Ideograph-63E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63e5 CJK Ideograph-63E5 | 63e4 CJK Ideograph-63E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63e7 CJK Ideograph-63E7 | 63e6 CJK Ideograph-63E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63e9 CJK Ideograph-63E9 | 63e8 CJK Ideograph-63E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63eb CJK Ideograph-63EB | 63ea CJK Ideograph-63EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63ed CJK Ideograph-63ED | 63ec CJK Ideograph-63EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63ef CJK Ideograph-63EF | 63ee CJK Ideograph-63EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63f1 CJK Ideograph-63F1 | 63f0 CJK Ideograph-63F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63f3 CJK Ideograph-63F3 | 63f2 CJK Ideograph-63F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63f5 CJK Ideograph-63F5 | 63f4 CJK Ideograph-63F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63f7 CJK Ideograph-63F7 | 63f6 CJK Ideograph-63F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63f9 CJK Ideograph-63F9 | 63f8 CJK Ideograph-63F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63fb CJK Ideograph-63FB | 63fa CJK Ideograph-63FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63fd CJK Ideograph-63FD | 63fc CJK Ideograph-63FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63ff CJK Ideograph-63FF | 63fe CJK Ideograph-63FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6401 CJK Ideograph-6401 | 6400 CJK Ideograph-6400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6403 CJK Ideograph-6403 | 6402 CJK Ideograph-6402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6405 CJK Ideograph-6405 | 6404 CJK Ideograph-6404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6407 CJK Ideograph-6407 | 6406 CJK Ideograph-6406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6409 CJK Ideograph-6409 | 6408 CJK Ideograph-6408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 640b CJK Ideograph-640B | 640a CJK Ideograph-640A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 640d CJK Ideograph-640D | 640c CJK Ideograph-640C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 640f CJK Ideograph-640F | 640e CJK Ideograph-640E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6411 CJK Ideograph-6411 | 6410 CJK Ideograph-6410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6413 CJK Ideograph-6413 | 6412 CJK Ideograph-6412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6415 CJK Ideograph-6415 | 6414 CJK Ideograph-6414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6417 CJK Ideograph-6417 | 6416 CJK Ideograph-6416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6419 CJK Ideograph-6419 | 6418 CJK Ideograph-6418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 641b CJK Ideograph-641B | 641a CJK Ideograph-641A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 641d CJK Ideograph-641D | 641c CJK Ideograph-641C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 641f CJK Ideograph-641F | 641e CJK Ideograph-641E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6421 CJK Ideograph-6421 | 6420 CJK Ideograph-6420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6423 CJK Ideograph-6423 | 6422 CJK Ideograph-6422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6425 CJK Ideograph-6425 | 6424 CJK Ideograph-6424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6427 CJK Ideograph-6427 | 6426 CJK Ideograph-6426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6429 CJK Ideograph-6429 | 6428 CJK Ideograph-6428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 642b CJK Ideograph-642B | 642a CJK Ideograph-642A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 642d CJK Ideograph-642D | 642c CJK Ideograph-642C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 642f CJK Ideograph-642F | 642e CJK Ideograph-642E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6431 CJK Ideograph-6431 | 6430 CJK Ideograph-6430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6433 CJK Ideograph-6433 | 6432 CJK Ideograph-6432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6435 CJK Ideograph-6435 | 6434 CJK Ideograph-6434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6437 CJK Ideograph-6437 | 6436 CJK Ideograph-6436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6439 CJK Ideograph-6439 | 6438 CJK Ideograph-6438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 643b CJK Ideograph-643B | 643a CJK Ideograph-643A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 643d CJK Ideograph-643D | 643c CJK Ideograph-643C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 643f CJK Ideograph-643F | 643e CJK Ideograph-643E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6441 CJK Ideograph-6441 | 6440 CJK Ideograph-6440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6443 CJK Ideograph-6443 | 6442 CJK Ideograph-6442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6445 CJK Ideograph-6445 | 6444 CJK Ideograph-6444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6447 CJK Ideograph-6447 | 6446 CJK Ideograph-6446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6449 CJK Ideograph-6449 | 6448 CJK Ideograph-6448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 644b CJK Ideograph-644B | 644a CJK Ideograph-644A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 644d CJK Ideograph-644D | 644c CJK Ideograph-644C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 644f CJK Ideograph-644F | 644e CJK Ideograph-644E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6451 CJK Ideograph-6451 | 6450 CJK Ideograph-6450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6453 CJK Ideograph-6453 | 6452 CJK Ideograph-6452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6455 CJK Ideograph-6455 | 6454 CJK Ideograph-6454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6457 CJK Ideograph-6457 | 6456 CJK Ideograph-6456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6459 CJK Ideograph-6459 | 6458 CJK Ideograph-6458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 645b CJK Ideograph-645B | 645a CJK Ideograph-645A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 645d CJK Ideograph-645D | 645c CJK Ideograph-645C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 645f CJK Ideograph-645F | 645e CJK Ideograph-645E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6461 CJK Ideograph-6461 | 6460 CJK Ideograph-6460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6463 CJK Ideograph-6463 | 6462 CJK Ideograph-6462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6465 CJK Ideograph-6465 | 6464 CJK Ideograph-6464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6467 CJK Ideograph-6467 | 6466 CJK Ideograph-6466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6469 CJK Ideograph-6469 | 6468 CJK Ideograph-6468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 646b CJK Ideograph-646B | 646a CJK Ideograph-646A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 646d CJK Ideograph-646D | 646c CJK Ideograph-646C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 646f CJK Ideograph-646F | 646e CJK Ideograph-646E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6471 CJK Ideograph-6471 | 6470 CJK Ideograph-6470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6473 CJK Ideograph-6473 | 6472 CJK Ideograph-6472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6475 CJK Ideograph-6475 | 6474 CJK Ideograph-6474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6477 CJK Ideograph-6477 | 6476 CJK Ideograph-6476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6479 CJK Ideograph-6479 | 6478 CJK Ideograph-6478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 647b CJK Ideograph-647B | 647a CJK Ideograph-647A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 647d CJK Ideograph-647D | 647c CJK Ideograph-647C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 647f CJK Ideograph-647F | 647e CJK Ideograph-647E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6481 CJK Ideograph-6481 | 6480 CJK Ideograph-6480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6483 CJK Ideograph-6483 | 6482 CJK Ideograph-6482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6485 CJK Ideograph-6485 | 6484 CJK Ideograph-6484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6487 CJK Ideograph-6487 | 6486 CJK Ideograph-6486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6489 CJK Ideograph-6489 | 6488 CJK Ideograph-6488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 648b CJK Ideograph-648B | 648a CJK Ideograph-648A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 648d CJK Ideograph-648D | 648c CJK Ideograph-648C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 648f CJK Ideograph-648F | 648e CJK Ideograph-648E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6491 CJK Ideograph-6491 | 6490 CJK Ideograph-6490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6493 CJK Ideograph-6493 | 6492 CJK Ideograph-6492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6495 CJK Ideograph-6495 | 6494 CJK Ideograph-6494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6497 CJK Ideograph-6497 | 6496 CJK Ideograph-6496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6499 CJK Ideograph-6499 | 6498 CJK Ideograph-6498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 649b CJK Ideograph-649B | 649a CJK Ideograph-649A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 649d CJK Ideograph-649D | 649c CJK Ideograph-649C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 649f CJK Ideograph-649F | 649e CJK Ideograph-649E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64a1 CJK Ideograph-64A1 | 64a0 CJK Ideograph-64A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64a3 CJK Ideograph-64A3 | 64a2 CJK Ideograph-64A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64a5 CJK Ideograph-64A5 | 64a4 CJK Ideograph-64A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64a7 CJK Ideograph-64A7 | 64a6 CJK Ideograph-64A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64a9 CJK Ideograph-64A9 | 64a8 CJK Ideograph-64A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64ab CJK Ideograph-64AB | 64aa CJK Ideograph-64AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64ad CJK Ideograph-64AD | 64ac CJK Ideograph-64AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64af CJK Ideograph-64AF | 64ae CJK Ideograph-64AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64b1 CJK Ideograph-64B1 | 64b0 CJK Ideograph-64B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64b3 CJK Ideograph-64B3 | 64b2 CJK Ideograph-64B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64b5 CJK Ideograph-64B5 | 64b4 CJK Ideograph-64B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64b7 CJK Ideograph-64B7 | 64b6 CJK Ideograph-64B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64b9 CJK Ideograph-64B9 | 64b8 CJK Ideograph-64B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64bb CJK Ideograph-64BB | 64ba CJK Ideograph-64BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64bd CJK Ideograph-64BD | 64bc CJK Ideograph-64BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64bf CJK Ideograph-64BF | 64be CJK Ideograph-64BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64c1 CJK Ideograph-64C1 | 64c0 CJK Ideograph-64C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64c3 CJK Ideograph-64C3 | 64c2 CJK Ideograph-64C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64c5 CJK Ideograph-64C5 | 64c4 CJK Ideograph-64C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64c7 CJK Ideograph-64C7 | 64c6 CJK Ideograph-64C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64c9 CJK Ideograph-64C9 | 64c8 CJK Ideograph-64C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64cb CJK Ideograph-64CB | 64ca CJK Ideograph-64CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64cd CJK Ideograph-64CD | 64cc CJK Ideograph-64CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64cf CJK Ideograph-64CF | 64ce CJK Ideograph-64CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64d1 CJK Ideograph-64D1 | 64d0 CJK Ideograph-64D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64d3 CJK Ideograph-64D3 | 64d2 CJK Ideograph-64D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64d5 CJK Ideograph-64D5 | 64d4 CJK Ideograph-64D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64d7 CJK Ideograph-64D7 | 64d6 CJK Ideograph-64D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64d9 CJK Ideograph-64D9 | 64d8 CJK Ideograph-64D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64db CJK Ideograph-64DB | 64da CJK Ideograph-64DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64dd CJK Ideograph-64DD | 64dc CJK Ideograph-64DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64df CJK Ideograph-64DF | 64de CJK Ideograph-64DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64e1 CJK Ideograph-64E1 | 64e0 CJK Ideograph-64E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64e3 CJK Ideograph-64E3 | 64e2 CJK Ideograph-64E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64e5 CJK Ideograph-64E5 | 64e4 CJK Ideograph-64E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64e7 CJK Ideograph-64E7 | 64e6 CJK Ideograph-64E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64e9 CJK Ideograph-64E9 | 64e8 CJK Ideograph-64E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64eb CJK Ideograph-64EB | 64ea CJK Ideograph-64EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64ed CJK Ideograph-64ED | 64ec CJK Ideograph-64EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64ef CJK Ideograph-64EF | 64ee CJK Ideograph-64EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64f1 CJK Ideograph-64F1 | 64f0 CJK Ideograph-64F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64f3 CJK Ideograph-64F3 | 64f2 CJK Ideograph-64F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64f5 CJK Ideograph-64F5 | 64f4 CJK Ideograph-64F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64f7 CJK Ideograph-64F7 | 64f6 CJK Ideograph-64F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64f9 CJK Ideograph-64F9 | 64f8 CJK Ideograph-64F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64fb CJK Ideograph-64FB | 64fa CJK Ideograph-64FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64fd CJK Ideograph-64FD | 64fc CJK Ideograph-64FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64ff CJK Ideograph-64FF | 64fe CJK Ideograph-64FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6501 CJK Ideograph-6501 | 6500 CJK Ideograph-6500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6503 CJK Ideograph-6503 | 6502 CJK Ideograph-6502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6505 CJK Ideograph-6505 | 6504 CJK Ideograph-6504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6507 CJK Ideograph-6507 | 6506 CJK Ideograph-6506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6509 CJK Ideograph-6509 | 6508 CJK Ideograph-6508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 650b CJK Ideograph-650B | 650a CJK Ideograph-650A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 650d CJK Ideograph-650D | 650c CJK Ideograph-650C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 650f CJK Ideograph-650F | 650e CJK Ideograph-650E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6511 CJK Ideograph-6511 | 6510 CJK Ideograph-6510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6513 CJK Ideograph-6513 | 6512 CJK Ideograph-6512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6515 CJK Ideograph-6515 | 6514 CJK Ideograph-6514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6517 CJK Ideograph-6517 | 6516 CJK Ideograph-6516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6519 CJK Ideograph-6519 | 6518 CJK Ideograph-6518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 651b CJK Ideograph-651B | 651a CJK Ideograph-651A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 651d CJK Ideograph-651D | 651c CJK Ideograph-651C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 651f CJK Ideograph-651F | 651e CJK Ideograph-651E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6521 CJK Ideograph-6521 | 6520 CJK Ideograph-6520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6523 CJK Ideograph-6523 | 6522 CJK Ideograph-6522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6525 CJK Ideograph-6525 | 6524 CJK Ideograph-6524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6527 CJK Ideograph-6527 | 6526 CJK Ideograph-6526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6529 CJK Ideograph-6529 | 6528 CJK Ideograph-6528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 652b CJK Ideograph-652B | 652a CJK Ideograph-652A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 652d CJK Ideograph-652D | 652c CJK Ideograph-652C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 652f CJK Ideograph-652F | 652e CJK Ideograph-652E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6531 CJK Ideograph-6531 | 6530 CJK Ideograph-6530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6533 CJK Ideograph-6533 | 6532 CJK Ideograph-6532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6535 CJK Ideograph-6535 | 6534 CJK Ideograph-6534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6537 CJK Ideograph-6537 | 6536 CJK Ideograph-6536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6539 CJK Ideograph-6539 | 6538 CJK Ideograph-6538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 653b CJK Ideograph-653B | 653a CJK Ideograph-653A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 653d CJK Ideograph-653D | 653c CJK Ideograph-653C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 653f CJK Ideograph-653F | 653e CJK Ideograph-653E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6541 CJK Ideograph-6541 | 6540 CJK Ideograph-6540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6543 CJK Ideograph-6543 | 6542 CJK Ideograph-6542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6545 CJK Ideograph-6545 | 6544 CJK Ideograph-6544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6547 CJK Ideograph-6547 | 6546 CJK Ideograph-6546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6549 CJK Ideograph-6549 | 6548 CJK Ideograph-6548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 654b CJK Ideograph-654B | 654a CJK Ideograph-654A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 654d CJK Ideograph-654D | 654c CJK Ideograph-654C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 654f CJK Ideograph-654F | 654e CJK Ideograph-654E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6551 CJK Ideograph-6551 | 6550 CJK Ideograph-6550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6553 CJK Ideograph-6553 | 6552 CJK Ideograph-6552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6555 CJK Ideograph-6555 | 6554 CJK Ideograph-6554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6557 CJK Ideograph-6557 | 6556 CJK Ideograph-6556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6559 CJK Ideograph-6559 | 6558 CJK Ideograph-6558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 655b CJK Ideograph-655B | 655a CJK Ideograph-655A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 655d CJK Ideograph-655D | 655c CJK Ideograph-655C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 655f CJK Ideograph-655F | 655e CJK Ideograph-655E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6561 CJK Ideograph-6561 | 6560 CJK Ideograph-6560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6563 CJK Ideograph-6563 | 6562 CJK Ideograph-6562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6565 CJK Ideograph-6565 | 6564 CJK Ideograph-6564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6567 CJK Ideograph-6567 | 6566 CJK Ideograph-6566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6569 CJK Ideograph-6569 | 6568 CJK Ideograph-6568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 656b CJK Ideograph-656B | 656a CJK Ideograph-656A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 656d CJK Ideograph-656D | 656c CJK Ideograph-656C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 656f CJK Ideograph-656F | 656e CJK Ideograph-656E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6571 CJK Ideograph-6571 | 6570 CJK Ideograph-6570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6573 CJK Ideograph-6573 | 6572 CJK Ideograph-6572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6575 CJK Ideograph-6575 | 6574 CJK Ideograph-6574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6577 CJK Ideograph-6577 | 6576 CJK Ideograph-6576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6579 CJK Ideograph-6579 | 6578 CJK Ideograph-6578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 657b CJK Ideograph-657B | 657a CJK Ideograph-657A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 657d CJK Ideograph-657D | 657c CJK Ideograph-657C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 657f CJK Ideograph-657F | 657e CJK Ideograph-657E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6581 CJK Ideograph-6581 | 6580 CJK Ideograph-6580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6583 CJK Ideograph-6583 | 6582 CJK Ideograph-6582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6585 CJK Ideograph-6585 | 6584 CJK Ideograph-6584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6587 CJK Ideograph-6587 | 6586 CJK Ideograph-6586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6589 CJK Ideograph-6589 | 6588 CJK Ideograph-6588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 658b CJK Ideograph-658B | 658a CJK Ideograph-658A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 658d CJK Ideograph-658D | 658c CJK Ideograph-658C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 658f CJK Ideograph-658F | 658e CJK Ideograph-658E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6591 CJK Ideograph-6591 | 6590 CJK Ideograph-6590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6593 CJK Ideograph-6593 | 6592 CJK Ideograph-6592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6595 CJK Ideograph-6595 | 6594 CJK Ideograph-6594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6597 CJK Ideograph-6597 | 6596 CJK Ideograph-6596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6599 CJK Ideograph-6599 | 6598 CJK Ideograph-6598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 659b CJK Ideograph-659B | 659a CJK Ideograph-659A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 659d CJK Ideograph-659D | 659c CJK Ideograph-659C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 659f CJK Ideograph-659F | 659e CJK Ideograph-659E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65a1 CJK Ideograph-65A1 | 65a0 CJK Ideograph-65A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65a3 CJK Ideograph-65A3 | 65a2 CJK Ideograph-65A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65a5 CJK Ideograph-65A5 | 65a4 CJK Ideograph-65A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65a7 CJK Ideograph-65A7 | 65a6 CJK Ideograph-65A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65a9 CJK Ideograph-65A9 | 65a8 CJK Ideograph-65A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65ab CJK Ideograph-65AB | 65aa CJK Ideograph-65AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65ad CJK Ideograph-65AD | 65ac CJK Ideograph-65AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65af CJK Ideograph-65AF | 65ae CJK Ideograph-65AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65b1 CJK Ideograph-65B1 | 65b0 CJK Ideograph-65B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65b3 CJK Ideograph-65B3 | 65b2 CJK Ideograph-65B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65b5 CJK Ideograph-65B5 | 65b4 CJK Ideograph-65B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65b7 CJK Ideograph-65B7 | 65b6 CJK Ideograph-65B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65b9 CJK Ideograph-65B9 | 65b8 CJK Ideograph-65B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65bb CJK Ideograph-65BB | 65ba CJK Ideograph-65BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65bd CJK Ideograph-65BD | 65bc CJK Ideograph-65BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65bf CJK Ideograph-65BF | 65be CJK Ideograph-65BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65c1 CJK Ideograph-65C1 | 65c0 CJK Ideograph-65C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65c3 CJK Ideograph-65C3 | 65c2 CJK Ideograph-65C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65c5 CJK Ideograph-65C5 | 65c4 CJK Ideograph-65C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65c7 CJK Ideograph-65C7 | 65c6 CJK Ideograph-65C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65c9 CJK Ideograph-65C9 | 65c8 CJK Ideograph-65C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65cb CJK Ideograph-65CB | 65ca CJK Ideograph-65CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65cd CJK Ideograph-65CD | 65cc CJK Ideograph-65CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65cf CJK Ideograph-65CF | 65ce CJK Ideograph-65CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65d1 CJK Ideograph-65D1 | 65d0 CJK Ideograph-65D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65d3 CJK Ideograph-65D3 | 65d2 CJK Ideograph-65D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65d5 CJK Ideograph-65D5 | 65d4 CJK Ideograph-65D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65d7 CJK Ideograph-65D7 | 65d6 CJK Ideograph-65D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65d9 CJK Ideograph-65D9 | 65d8 CJK Ideograph-65D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65db CJK Ideograph-65DB | 65da CJK Ideograph-65DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65dd CJK Ideograph-65DD | 65dc CJK Ideograph-65DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65df CJK Ideograph-65DF | 65de CJK Ideograph-65DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65e1 CJK Ideograph-65E1 | 65e0 CJK Ideograph-65E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65e3 CJK Ideograph-65E3 | 65e2 CJK Ideograph-65E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65e5 CJK Ideograph-65E5 | 65e4 CJK Ideograph-65E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65e7 CJK Ideograph-65E7 | 65e6 CJK Ideograph-65E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65e9 CJK Ideograph-65E9 | 65e8 CJK Ideograph-65E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65eb CJK Ideograph-65EB | 65ea CJK Ideograph-65EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65ed CJK Ideograph-65ED | 65ec CJK Ideograph-65EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65ef CJK Ideograph-65EF | 65ee CJK Ideograph-65EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65f1 CJK Ideograph-65F1 | 65f0 CJK Ideograph-65F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65f3 CJK Ideograph-65F3 | 65f2 CJK Ideograph-65F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65f5 CJK Ideograph-65F5 | 65f4 CJK Ideograph-65F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65f7 CJK Ideograph-65F7 | 65f6 CJK Ideograph-65F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65f9 CJK Ideograph-65F9 | 65f8 CJK Ideograph-65F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65fb CJK Ideograph-65FB | 65fa CJK Ideograph-65FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65fd CJK Ideograph-65FD | 65fc CJK Ideograph-65FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65ff CJK Ideograph-65FF | 65fe CJK Ideograph-65FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6601 CJK Ideograph-6601 | 6600 CJK Ideograph-6600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6603 CJK Ideograph-6603 | 6602 CJK Ideograph-6602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6605 CJK Ideograph-6605 | 6604 CJK Ideograph-6604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6607 CJK Ideograph-6607 | 6606 CJK Ideograph-6606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6609 CJK Ideograph-6609 | 6608 CJK Ideograph-6608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 660b CJK Ideograph-660B | 660a CJK Ideograph-660A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 660d CJK Ideograph-660D | 660c CJK Ideograph-660C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 660f CJK Ideograph-660F | 660e CJK Ideograph-660E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6611 CJK Ideograph-6611 | 6610 CJK Ideograph-6610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6613 CJK Ideograph-6613 | 6612 CJK Ideograph-6612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6615 CJK Ideograph-6615 | 6614 CJK Ideograph-6614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6617 CJK Ideograph-6617 | 6616 CJK Ideograph-6616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6619 CJK Ideograph-6619 | 6618 CJK Ideograph-6618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 661b CJK Ideograph-661B | 661a CJK Ideograph-661A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 661d CJK Ideograph-661D | 661c CJK Ideograph-661C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 661f CJK Ideograph-661F | 661e CJK Ideograph-661E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6621 CJK Ideograph-6621 | 6620 CJK Ideograph-6620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6623 CJK Ideograph-6623 | 6622 CJK Ideograph-6622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6625 CJK Ideograph-6625 | 6624 CJK Ideograph-6624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6627 CJK Ideograph-6627 | 6626 CJK Ideograph-6626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6629 CJK Ideograph-6629 | 6628 CJK Ideograph-6628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 662b CJK Ideograph-662B | 662a CJK Ideograph-662A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 662d CJK Ideograph-662D | 662c CJK Ideograph-662C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 662f CJK Ideograph-662F | 662e CJK Ideograph-662E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6631 CJK Ideograph-6631 | 6630 CJK Ideograph-6630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6633 CJK Ideograph-6633 | 6632 CJK Ideograph-6632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6635 CJK Ideograph-6635 | 6634 CJK Ideograph-6634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6637 CJK Ideograph-6637 | 6636 CJK Ideograph-6636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6639 CJK Ideograph-6639 | 6638 CJK Ideograph-6638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 663b CJK Ideograph-663B | 663a CJK Ideograph-663A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 663d CJK Ideograph-663D | 663c CJK Ideograph-663C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 663f CJK Ideograph-663F | 663e CJK Ideograph-663E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6641 CJK Ideograph-6641 | 6640 CJK Ideograph-6640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6643 CJK Ideograph-6643 | 6642 CJK Ideograph-6642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6645 CJK Ideograph-6645 | 6644 CJK Ideograph-6644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6647 CJK Ideograph-6647 | 6646 CJK Ideograph-6646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6649 CJK Ideograph-6649 | 6648 CJK Ideograph-6648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 664b CJK Ideograph-664B | 664a CJK Ideograph-664A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 664d CJK Ideograph-664D | 664c CJK Ideograph-664C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 664f CJK Ideograph-664F | 664e CJK Ideograph-664E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6651 CJK Ideograph-6651 | 6650 CJK Ideograph-6650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6653 CJK Ideograph-6653 | 6652 CJK Ideograph-6652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6655 CJK Ideograph-6655 | 6654 CJK Ideograph-6654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6657 CJK Ideograph-6657 | 6656 CJK Ideograph-6656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6659 CJK Ideograph-6659 | 6658 CJK Ideograph-6658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 665b CJK Ideograph-665B | 665a CJK Ideograph-665A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 665d CJK Ideograph-665D | 665c CJK Ideograph-665C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 665f CJK Ideograph-665F | 665e CJK Ideograph-665E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6661 CJK Ideograph-6661 | 6660 CJK Ideograph-6660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6663 CJK Ideograph-6663 | 6662 CJK Ideograph-6662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6665 CJK Ideograph-6665 | 6664 CJK Ideograph-6664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6667 CJK Ideograph-6667 | 6666 CJK Ideograph-6666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6669 CJK Ideograph-6669 | 6668 CJK Ideograph-6668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 666b CJK Ideograph-666B | 666a CJK Ideograph-666A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 666d CJK Ideograph-666D | 666c CJK Ideograph-666C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 666f CJK Ideograph-666F | 666e CJK Ideograph-666E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6671 CJK Ideograph-6671 | 6670 CJK Ideograph-6670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6673 CJK Ideograph-6673 | 6672 CJK Ideograph-6672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6675 CJK Ideograph-6675 | 6674 CJK Ideograph-6674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6677 CJK Ideograph-6677 | 6676 CJK Ideograph-6676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6679 CJK Ideograph-6679 | 6678 CJK Ideograph-6678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 667b CJK Ideograph-667B | 667a CJK Ideograph-667A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 667d CJK Ideograph-667D | 667c CJK Ideograph-667C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 667f CJK Ideograph-667F | 667e CJK Ideograph-667E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6681 CJK Ideograph-6681 | 6680 CJK Ideograph-6680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6683 CJK Ideograph-6683 | 6682 CJK Ideograph-6682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6685 CJK Ideograph-6685 | 6684 CJK Ideograph-6684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6687 CJK Ideograph-6687 | 6686 CJK Ideograph-6686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6689 CJK Ideograph-6689 | 6688 CJK Ideograph-6688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 668b CJK Ideograph-668B | 668a CJK Ideograph-668A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 668d CJK Ideograph-668D | 668c CJK Ideograph-668C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 668f CJK Ideograph-668F | 668e CJK Ideograph-668E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6691 CJK Ideograph-6691 | 6690 CJK Ideograph-6690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6693 CJK Ideograph-6693 | 6692 CJK Ideograph-6692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6695 CJK Ideograph-6695 | 6694 CJK Ideograph-6694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6697 CJK Ideograph-6697 | 6696 CJK Ideograph-6696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6699 CJK Ideograph-6699 | 6698 CJK Ideograph-6698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 669b CJK Ideograph-669B | 669a CJK Ideograph-669A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 669d CJK Ideograph-669D | 669c CJK Ideograph-669C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 669f CJK Ideograph-669F | 669e CJK Ideograph-669E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66a1 CJK Ideograph-66A1 | 66a0 CJK Ideograph-66A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66a3 CJK Ideograph-66A3 | 66a2 CJK Ideograph-66A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66a5 CJK Ideograph-66A5 | 66a4 CJK Ideograph-66A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66a7 CJK Ideograph-66A7 | 66a6 CJK Ideograph-66A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66a9 CJK Ideograph-66A9 | 66a8 CJK Ideograph-66A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66ab CJK Ideograph-66AB | 66aa CJK Ideograph-66AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66ad CJK Ideograph-66AD | 66ac CJK Ideograph-66AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66af CJK Ideograph-66AF | 66ae CJK Ideograph-66AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66b1 CJK Ideograph-66B1 | 66b0 CJK Ideograph-66B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66b3 CJK Ideograph-66B3 | 66b2 CJK Ideograph-66B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66b5 CJK Ideograph-66B5 | 66b4 CJK Ideograph-66B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66b7 CJK Ideograph-66B7 | 66b6 CJK Ideograph-66B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66b9 CJK Ideograph-66B9 | 66b8 CJK Ideograph-66B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66bb CJK Ideograph-66BB | 66ba CJK Ideograph-66BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66bd CJK Ideograph-66BD | 66bc CJK Ideograph-66BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66bf CJK Ideograph-66BF | 66be CJK Ideograph-66BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66c1 CJK Ideograph-66C1 | 66c0 CJK Ideograph-66C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66c3 CJK Ideograph-66C3 | 66c2 CJK Ideograph-66C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66c5 CJK Ideograph-66C5 | 66c4 CJK Ideograph-66C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66c7 CJK Ideograph-66C7 | 66c6 CJK Ideograph-66C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66c9 CJK Ideograph-66C9 | 66c8 CJK Ideograph-66C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66cb CJK Ideograph-66CB | 66ca CJK Ideograph-66CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66cd CJK Ideograph-66CD | 66cc CJK Ideograph-66CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66cf CJK Ideograph-66CF | 66ce CJK Ideograph-66CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66d1 CJK Ideograph-66D1 | 66d0 CJK Ideograph-66D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66d3 CJK Ideograph-66D3 | 66d2 CJK Ideograph-66D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66d5 CJK Ideograph-66D5 | 66d4 CJK Ideograph-66D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66d7 CJK Ideograph-66D7 | 66d6 CJK Ideograph-66D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66d9 CJK Ideograph-66D9 | 66d8 CJK Ideograph-66D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66db CJK Ideograph-66DB | 66da CJK Ideograph-66DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66dd CJK Ideograph-66DD | 66dc CJK Ideograph-66DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66df CJK Ideograph-66DF | 66de CJK Ideograph-66DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66e1 CJK Ideograph-66E1 | 66e0 CJK Ideograph-66E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66e3 CJK Ideograph-66E3 | 66e2 CJK Ideograph-66E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66e5 CJK Ideograph-66E5 | 66e4 CJK Ideograph-66E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66e7 CJK Ideograph-66E7 | 66e6 CJK Ideograph-66E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66e9 CJK Ideograph-66E9 | 66e8 CJK Ideograph-66E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66eb CJK Ideograph-66EB | 66ea CJK Ideograph-66EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66ed CJK Ideograph-66ED | 66ec CJK Ideograph-66EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66ef CJK Ideograph-66EF | 66ee CJK Ideograph-66EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66f1 CJK Ideograph-66F1 | 66f0 CJK Ideograph-66F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66f3 CJK Ideograph-66F3 | 66f2 CJK Ideograph-66F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66f5 CJK Ideograph-66F5 | 66f4 CJK Ideograph-66F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66f7 CJK Ideograph-66F7 | 66f6 CJK Ideograph-66F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66f9 CJK Ideograph-66F9 | 66f8 CJK Ideograph-66F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66fb CJK Ideograph-66FB | 66fa CJK Ideograph-66FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66fd CJK Ideograph-66FD | 66fc CJK Ideograph-66FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66ff CJK Ideograph-66FF | 66fe CJK Ideograph-66FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6701 CJK Ideograph-6701 | 6700 CJK Ideograph-6700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6703 CJK Ideograph-6703 | 6702 CJK Ideograph-6702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6705 CJK Ideograph-6705 | 6704 CJK Ideograph-6704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6707 CJK Ideograph-6707 | 6706 CJK Ideograph-6706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6709 CJK Ideograph-6709 | 6708 CJK Ideograph-6708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 670b CJK Ideograph-670B | 670a CJK Ideograph-670A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 670d CJK Ideograph-670D | 670c CJK Ideograph-670C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 670f CJK Ideograph-670F | 670e CJK Ideograph-670E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6711 CJK Ideograph-6711 | 6710 CJK Ideograph-6710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6713 CJK Ideograph-6713 | 6712 CJK Ideograph-6712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6715 CJK Ideograph-6715 | 6714 CJK Ideograph-6714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6717 CJK Ideograph-6717 | 6716 CJK Ideograph-6716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6719 CJK Ideograph-6719 | 6718 CJK Ideograph-6718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 671b CJK Ideograph-671B | 671a CJK Ideograph-671A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 671d CJK Ideograph-671D | 671c CJK Ideograph-671C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 671f CJK Ideograph-671F | 671e CJK Ideograph-671E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6721 CJK Ideograph-6721 | 6720 CJK Ideograph-6720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6723 CJK Ideograph-6723 | 6722 CJK Ideograph-6722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6725 CJK Ideograph-6725 | 6724 CJK Ideograph-6724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6727 CJK Ideograph-6727 | 6726 CJK Ideograph-6726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6729 CJK Ideograph-6729 | 6728 CJK Ideograph-6728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 672b CJK Ideograph-672B | 672a CJK Ideograph-672A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 672d CJK Ideograph-672D | 672c CJK Ideograph-672C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 672f CJK Ideograph-672F | 672e CJK Ideograph-672E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6731 CJK Ideograph-6731 | 6730 CJK Ideograph-6730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6733 CJK Ideograph-6733 | 6732 CJK Ideograph-6732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6735 CJK Ideograph-6735 | 6734 CJK Ideograph-6734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6737 CJK Ideograph-6737 | 6736 CJK Ideograph-6736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6739 CJK Ideograph-6739 | 6738 CJK Ideograph-6738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 673b CJK Ideograph-673B | 673a CJK Ideograph-673A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 673d CJK Ideograph-673D | 673c CJK Ideograph-673C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 673f CJK Ideograph-673F | 673e CJK Ideograph-673E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6741 CJK Ideograph-6741 | 6740 CJK Ideograph-6740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6743 CJK Ideograph-6743 | 6742 CJK Ideograph-6742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6745 CJK Ideograph-6745 | 6744 CJK Ideograph-6744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6747 CJK Ideograph-6747 | 6746 CJK Ideograph-6746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6749 CJK Ideograph-6749 | 6748 CJK Ideograph-6748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 674b CJK Ideograph-674B | 674a CJK Ideograph-674A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 674d CJK Ideograph-674D | 674c CJK Ideograph-674C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 674f CJK Ideograph-674F | 674e CJK Ideograph-674E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6751 CJK Ideograph-6751 | 6750 CJK Ideograph-6750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6753 CJK Ideograph-6753 | 6752 CJK Ideograph-6752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6755 CJK Ideograph-6755 | 6754 CJK Ideograph-6754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6757 CJK Ideograph-6757 | 6756 CJK Ideograph-6756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6759 CJK Ideograph-6759 | 6758 CJK Ideograph-6758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 675b CJK Ideograph-675B | 675a CJK Ideograph-675A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 675d CJK Ideograph-675D | 675c CJK Ideograph-675C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 675f CJK Ideograph-675F | 675e CJK Ideograph-675E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6761 CJK Ideograph-6761 | 6760 CJK Ideograph-6760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6763 CJK Ideograph-6763 | 6762 CJK Ideograph-6762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6765 CJK Ideograph-6765 | 6764 CJK Ideograph-6764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6767 CJK Ideograph-6767 | 6766 CJK Ideograph-6766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6769 CJK Ideograph-6769 | 6768 CJK Ideograph-6768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 676b CJK Ideograph-676B | 676a CJK Ideograph-676A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 676d CJK Ideograph-676D | 676c CJK Ideograph-676C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 676f CJK Ideograph-676F | 676e CJK Ideograph-676E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6771 CJK Ideograph-6771 | 6770 CJK Ideograph-6770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6773 CJK Ideograph-6773 | 6772 CJK Ideograph-6772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6775 CJK Ideograph-6775 | 6774 CJK Ideograph-6774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6777 CJK Ideograph-6777 | 6776 CJK Ideograph-6776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6779 CJK Ideograph-6779 | 6778 CJK Ideograph-6778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 677b CJK Ideograph-677B | 677a CJK Ideograph-677A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 677d CJK Ideograph-677D | 677c CJK Ideograph-677C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 677f CJK Ideograph-677F | 677e CJK Ideograph-677E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6781 CJK Ideograph-6781 | 6780 CJK Ideograph-6780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6783 CJK Ideograph-6783 | 6782 CJK Ideograph-6782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6785 CJK Ideograph-6785 | 6784 CJK Ideograph-6784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6787 CJK Ideograph-6787 | 6786 CJK Ideograph-6786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6789 CJK Ideograph-6789 | 6788 CJK Ideograph-6788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 678b CJK Ideograph-678B | 678a CJK Ideograph-678A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 678d CJK Ideograph-678D | 678c CJK Ideograph-678C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 678f CJK Ideograph-678F | 678e CJK Ideograph-678E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6791 CJK Ideograph-6791 | 6790 CJK Ideograph-6790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6793 CJK Ideograph-6793 | 6792 CJK Ideograph-6792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6795 CJK Ideograph-6795 | 6794 CJK Ideograph-6794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6797 CJK Ideograph-6797 | 6796 CJK Ideograph-6796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6799 CJK Ideograph-6799 | 6798 CJK Ideograph-6798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 679b CJK Ideograph-679B | 679a CJK Ideograph-679A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 679d CJK Ideograph-679D | 679c CJK Ideograph-679C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 679f CJK Ideograph-679F | 679e CJK Ideograph-679E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67a1 CJK Ideograph-67A1 | 67a0 CJK Ideograph-67A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67a3 CJK Ideograph-67A3 | 67a2 CJK Ideograph-67A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67a5 CJK Ideograph-67A5 | 67a4 CJK Ideograph-67A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67a7 CJK Ideograph-67A7 | 67a6 CJK Ideograph-67A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67a9 CJK Ideograph-67A9 | 67a8 CJK Ideograph-67A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67ab CJK Ideograph-67AB | 67aa CJK Ideograph-67AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67ad CJK Ideograph-67AD | 67ac CJK Ideograph-67AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67af CJK Ideograph-67AF | 67ae CJK Ideograph-67AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67b1 CJK Ideograph-67B1 | 67b0 CJK Ideograph-67B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67b3 CJK Ideograph-67B3 | 67b2 CJK Ideograph-67B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67b5 CJK Ideograph-67B5 | 67b4 CJK Ideograph-67B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67b7 CJK Ideograph-67B7 | 67b6 CJK Ideograph-67B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67b9 CJK Ideograph-67B9 | 67b8 CJK Ideograph-67B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67bb CJK Ideograph-67BB | 67ba CJK Ideograph-67BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67bd CJK Ideograph-67BD | 67bc CJK Ideograph-67BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67bf CJK Ideograph-67BF | 67be CJK Ideograph-67BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67c1 CJK Ideograph-67C1 | 67c0 CJK Ideograph-67C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67c3 CJK Ideograph-67C3 | 67c2 CJK Ideograph-67C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67c5 CJK Ideograph-67C5 | 67c4 CJK Ideograph-67C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67c7 CJK Ideograph-67C7 | 67c6 CJK Ideograph-67C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67c9 CJK Ideograph-67C9 | 67c8 CJK Ideograph-67C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67cb CJK Ideograph-67CB | 67ca CJK Ideograph-67CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67cd CJK Ideograph-67CD | 67cc CJK Ideograph-67CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67cf CJK Ideograph-67CF | 67ce CJK Ideograph-67CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67d1 CJK Ideograph-67D1 | 67d0 CJK Ideograph-67D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67d3 CJK Ideograph-67D3 | 67d2 CJK Ideograph-67D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67d5 CJK Ideograph-67D5 | 67d4 CJK Ideograph-67D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67d7 CJK Ideograph-67D7 | 67d6 CJK Ideograph-67D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67d9 CJK Ideograph-67D9 | 67d8 CJK Ideograph-67D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67db CJK Ideograph-67DB | 67da CJK Ideograph-67DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67dd CJK Ideograph-67DD | 67dc CJK Ideograph-67DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67df CJK Ideograph-67DF | 67de CJK Ideograph-67DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67e1 CJK Ideograph-67E1 | 67e0 CJK Ideograph-67E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67e3 CJK Ideograph-67E3 | 67e2 CJK Ideograph-67E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67e5 CJK Ideograph-67E5 | 67e4 CJK Ideograph-67E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67e7 CJK Ideograph-67E7 | 67e6 CJK Ideograph-67E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67e9 CJK Ideograph-67E9 | 67e8 CJK Ideograph-67E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67eb CJK Ideograph-67EB | 67ea CJK Ideograph-67EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67ed CJK Ideograph-67ED | 67ec CJK Ideograph-67EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67ef CJK Ideograph-67EF | 67ee CJK Ideograph-67EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67f1 CJK Ideograph-67F1 | 67f0 CJK Ideograph-67F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67f3 CJK Ideograph-67F3 | 67f2 CJK Ideograph-67F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67f5 CJK Ideograph-67F5 | 67f4 CJK Ideograph-67F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67f7 CJK Ideograph-67F7 | 67f6 CJK Ideograph-67F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67f9 CJK Ideograph-67F9 | 67f8 CJK Ideograph-67F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67fb CJK Ideograph-67FB | 67fa CJK Ideograph-67FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67fd CJK Ideograph-67FD | 67fc CJK Ideograph-67FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67ff CJK Ideograph-67FF | 67fe CJK Ideograph-67FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6801 CJK Ideograph-6801 | 6800 CJK Ideograph-6800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6803 CJK Ideograph-6803 | 6802 CJK Ideograph-6802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6805 CJK Ideograph-6805 | 6804 CJK Ideograph-6804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6807 CJK Ideograph-6807 | 6806 CJK Ideograph-6806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6809 CJK Ideograph-6809 | 6808 CJK Ideograph-6808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 680b CJK Ideograph-680B | 680a CJK Ideograph-680A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 680d CJK Ideograph-680D | 680c CJK Ideograph-680C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 680f CJK Ideograph-680F | 680e CJK Ideograph-680E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6811 CJK Ideograph-6811 | 6810 CJK Ideograph-6810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6813 CJK Ideograph-6813 | 6812 CJK Ideograph-6812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6815 CJK Ideograph-6815 | 6814 CJK Ideograph-6814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6817 CJK Ideograph-6817 | 6816 CJK Ideograph-6816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6819 CJK Ideograph-6819 | 6818 CJK Ideograph-6818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 681b CJK Ideograph-681B | 681a CJK Ideograph-681A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 681d CJK Ideograph-681D | 681c CJK Ideograph-681C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 681f CJK Ideograph-681F | 681e CJK Ideograph-681E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6821 CJK Ideograph-6821 | 6820 CJK Ideograph-6820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6823 CJK Ideograph-6823 | 6822 CJK Ideograph-6822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6825 CJK Ideograph-6825 | 6824 CJK Ideograph-6824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6827 CJK Ideograph-6827 | 6826 CJK Ideograph-6826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6829 CJK Ideograph-6829 | 6828 CJK Ideograph-6828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 682b CJK Ideograph-682B | 682a CJK Ideograph-682A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 682d CJK Ideograph-682D | 682c CJK Ideograph-682C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 682f CJK Ideograph-682F | 682e CJK Ideograph-682E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6831 CJK Ideograph-6831 | 6830 CJK Ideograph-6830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6833 CJK Ideograph-6833 | 6832 CJK Ideograph-6832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6835 CJK Ideograph-6835 | 6834 CJK Ideograph-6834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6837 CJK Ideograph-6837 | 6836 CJK Ideograph-6836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6839 CJK Ideograph-6839 | 6838 CJK Ideograph-6838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 683b CJK Ideograph-683B | 683a CJK Ideograph-683A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 683d CJK Ideograph-683D | 683c CJK Ideograph-683C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 683f CJK Ideograph-683F | 683e CJK Ideograph-683E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6841 CJK Ideograph-6841 | 6840 CJK Ideograph-6840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6843 CJK Ideograph-6843 | 6842 CJK Ideograph-6842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6845 CJK Ideograph-6845 | 6844 CJK Ideograph-6844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6847 CJK Ideograph-6847 | 6846 CJK Ideograph-6846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6849 CJK Ideograph-6849 | 6848 CJK Ideograph-6848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 684b CJK Ideograph-684B | 684a CJK Ideograph-684A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 684d CJK Ideograph-684D | 684c CJK Ideograph-684C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 684f CJK Ideograph-684F | 684e CJK Ideograph-684E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6851 CJK Ideograph-6851 | 6850 CJK Ideograph-6850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6853 CJK Ideograph-6853 | 6852 CJK Ideograph-6852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6855 CJK Ideograph-6855 | 6854 CJK Ideograph-6854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6857 CJK Ideograph-6857 | 6856 CJK Ideograph-6856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6859 CJK Ideograph-6859 | 6858 CJK Ideograph-6858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 685b CJK Ideograph-685B | 685a CJK Ideograph-685A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 685d CJK Ideograph-685D | 685c CJK Ideograph-685C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 685f CJK Ideograph-685F | 685e CJK Ideograph-685E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6861 CJK Ideograph-6861 | 6860 CJK Ideograph-6860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6863 CJK Ideograph-6863 | 6862 CJK Ideograph-6862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6865 CJK Ideograph-6865 | 6864 CJK Ideograph-6864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6867 CJK Ideograph-6867 | 6866 CJK Ideograph-6866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6869 CJK Ideograph-6869 | 6868 CJK Ideograph-6868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 686b CJK Ideograph-686B | 686a CJK Ideograph-686A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 686d CJK Ideograph-686D | 686c CJK Ideograph-686C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 686f CJK Ideograph-686F | 686e CJK Ideograph-686E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6871 CJK Ideograph-6871 | 6870 CJK Ideograph-6870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6873 CJK Ideograph-6873 | 6872 CJK Ideograph-6872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6875 CJK Ideograph-6875 | 6874 CJK Ideograph-6874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6877 CJK Ideograph-6877 | 6876 CJK Ideograph-6876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6879 CJK Ideograph-6879 | 6878 CJK Ideograph-6878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 687b CJK Ideograph-687B | 687a CJK Ideograph-687A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 687d CJK Ideograph-687D | 687c CJK Ideograph-687C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 687f CJK Ideograph-687F | 687e CJK Ideograph-687E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6881 CJK Ideograph-6881 | 6880 CJK Ideograph-6880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6883 CJK Ideograph-6883 | 6882 CJK Ideograph-6882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6885 CJK Ideograph-6885 | 6884 CJK Ideograph-6884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6887 CJK Ideograph-6887 | 6886 CJK Ideograph-6886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6889 CJK Ideograph-6889 | 6888 CJK Ideograph-6888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 688b CJK Ideograph-688B | 688a CJK Ideograph-688A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 688d CJK Ideograph-688D | 688c CJK Ideograph-688C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 688f CJK Ideograph-688F | 688e CJK Ideograph-688E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6891 CJK Ideograph-6891 | 6890 CJK Ideograph-6890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6893 CJK Ideograph-6893 | 6892 CJK Ideograph-6892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6895 CJK Ideograph-6895 | 6894 CJK Ideograph-6894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6897 CJK Ideograph-6897 | 6896 CJK Ideograph-6896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6899 CJK Ideograph-6899 | 6898 CJK Ideograph-6898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 689b CJK Ideograph-689B | 689a CJK Ideograph-689A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 689d CJK Ideograph-689D | 689c CJK Ideograph-689C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 689f CJK Ideograph-689F | 689e CJK Ideograph-689E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68a1 CJK Ideograph-68A1 | 68a0 CJK Ideograph-68A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68a3 CJK Ideograph-68A3 | 68a2 CJK Ideograph-68A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68a5 CJK Ideograph-68A5 | 68a4 CJK Ideograph-68A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68a7 CJK Ideograph-68A7 | 68a6 CJK Ideograph-68A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68a9 CJK Ideograph-68A9 | 68a8 CJK Ideograph-68A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68ab CJK Ideograph-68AB | 68aa CJK Ideograph-68AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68ad CJK Ideograph-68AD | 68ac CJK Ideograph-68AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68af CJK Ideograph-68AF | 68ae CJK Ideograph-68AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68b1 CJK Ideograph-68B1 | 68b0 CJK Ideograph-68B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68b3 CJK Ideograph-68B3 | 68b2 CJK Ideograph-68B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68b5 CJK Ideograph-68B5 | 68b4 CJK Ideograph-68B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68b7 CJK Ideograph-68B7 | 68b6 CJK Ideograph-68B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68b9 CJK Ideograph-68B9 | 68b8 CJK Ideograph-68B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68bb CJK Ideograph-68BB | 68ba CJK Ideograph-68BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68bd CJK Ideograph-68BD | 68bc CJK Ideograph-68BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68bf CJK Ideograph-68BF | 68be CJK Ideograph-68BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68c1 CJK Ideograph-68C1 | 68c0 CJK Ideograph-68C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68c3 CJK Ideograph-68C3 | 68c2 CJK Ideograph-68C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68c5 CJK Ideograph-68C5 | 68c4 CJK Ideograph-68C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68c7 CJK Ideograph-68C7 | 68c6 CJK Ideograph-68C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68c9 CJK Ideograph-68C9 | 68c8 CJK Ideograph-68C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68cb CJK Ideograph-68CB | 68ca CJK Ideograph-68CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68cd CJK Ideograph-68CD | 68cc CJK Ideograph-68CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68cf CJK Ideograph-68CF | 68ce CJK Ideograph-68CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68d1 CJK Ideograph-68D1 | 68d0 CJK Ideograph-68D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68d3 CJK Ideograph-68D3 | 68d2 CJK Ideograph-68D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68d5 CJK Ideograph-68D5 | 68d4 CJK Ideograph-68D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68d7 CJK Ideograph-68D7 | 68d6 CJK Ideograph-68D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68d9 CJK Ideograph-68D9 | 68d8 CJK Ideograph-68D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68db CJK Ideograph-68DB | 68da CJK Ideograph-68DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68dd CJK Ideograph-68DD | 68dc CJK Ideograph-68DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68df CJK Ideograph-68DF | 68de CJK Ideograph-68DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68e1 CJK Ideograph-68E1 | 68e0 CJK Ideograph-68E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68e3 CJK Ideograph-68E3 | 68e2 CJK Ideograph-68E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68e5 CJK Ideograph-68E5 | 68e4 CJK Ideograph-68E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68e7 CJK Ideograph-68E7 | 68e6 CJK Ideograph-68E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68e9 CJK Ideograph-68E9 | 68e8 CJK Ideograph-68E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68eb CJK Ideograph-68EB | 68ea CJK Ideograph-68EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68ed CJK Ideograph-68ED | 68ec CJK Ideograph-68EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68ef CJK Ideograph-68EF | 68ee CJK Ideograph-68EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68f1 CJK Ideograph-68F1 | 68f0 CJK Ideograph-68F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68f3 CJK Ideograph-68F3 | 68f2 CJK Ideograph-68F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68f5 CJK Ideograph-68F5 | 68f4 CJK Ideograph-68F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68f7 CJK Ideograph-68F7 | 68f6 CJK Ideograph-68F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68f9 CJK Ideograph-68F9 | 68f8 CJK Ideograph-68F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68fb CJK Ideograph-68FB | 68fa CJK Ideograph-68FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68fd CJK Ideograph-68FD | 68fc CJK Ideograph-68FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68ff CJK Ideograph-68FF | 68fe CJK Ideograph-68FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6901 CJK Ideograph-6901 | 6900 CJK Ideograph-6900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6903 CJK Ideograph-6903 | 6902 CJK Ideograph-6902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6905 CJK Ideograph-6905 | 6904 CJK Ideograph-6904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6907 CJK Ideograph-6907 | 6906 CJK Ideograph-6906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6909 CJK Ideograph-6909 | 6908 CJK Ideograph-6908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 690b CJK Ideograph-690B | 690a CJK Ideograph-690A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 690d CJK Ideograph-690D | 690c CJK Ideograph-690C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 690f CJK Ideograph-690F | 690e CJK Ideograph-690E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6911 CJK Ideograph-6911 | 6910 CJK Ideograph-6910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6913 CJK Ideograph-6913 | 6912 CJK Ideograph-6912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6915 CJK Ideograph-6915 | 6914 CJK Ideograph-6914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6917 CJK Ideograph-6917 | 6916 CJK Ideograph-6916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6919 CJK Ideograph-6919 | 6918 CJK Ideograph-6918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 691b CJK Ideograph-691B | 691a CJK Ideograph-691A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 691d CJK Ideograph-691D | 691c CJK Ideograph-691C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 691f CJK Ideograph-691F | 691e CJK Ideograph-691E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6921 CJK Ideograph-6921 | 6920 CJK Ideograph-6920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6923 CJK Ideograph-6923 | 6922 CJK Ideograph-6922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6925 CJK Ideograph-6925 | 6924 CJK Ideograph-6924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6927 CJK Ideograph-6927 | 6926 CJK Ideograph-6926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6929 CJK Ideograph-6929 | 6928 CJK Ideograph-6928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 692b CJK Ideograph-692B | 692a CJK Ideograph-692A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 692d CJK Ideograph-692D | 692c CJK Ideograph-692C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 692f CJK Ideograph-692F | 692e CJK Ideograph-692E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6931 CJK Ideograph-6931 | 6930 CJK Ideograph-6930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6933 CJK Ideograph-6933 | 6932 CJK Ideograph-6932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6935 CJK Ideograph-6935 | 6934 CJK Ideograph-6934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6937 CJK Ideograph-6937 | 6936 CJK Ideograph-6936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6939 CJK Ideograph-6939 | 6938 CJK Ideograph-6938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 693b CJK Ideograph-693B | 693a CJK Ideograph-693A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 693d CJK Ideograph-693D | 693c CJK Ideograph-693C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 693f CJK Ideograph-693F | 693e CJK Ideograph-693E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6941 CJK Ideograph-6941 | 6940 CJK Ideograph-6940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6943 CJK Ideograph-6943 | 6942 CJK Ideograph-6942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6945 CJK Ideograph-6945 | 6944 CJK Ideograph-6944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6947 CJK Ideograph-6947 | 6946 CJK Ideograph-6946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6949 CJK Ideograph-6949 | 6948 CJK Ideograph-6948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 694b CJK Ideograph-694B | 694a CJK Ideograph-694A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 694d CJK Ideograph-694D | 694c CJK Ideograph-694C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 694f CJK Ideograph-694F | 694e CJK Ideograph-694E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6951 CJK Ideograph-6951 | 6950 CJK Ideograph-6950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6953 CJK Ideograph-6953 | 6952 CJK Ideograph-6952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6955 CJK Ideograph-6955 | 6954 CJK Ideograph-6954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6957 CJK Ideograph-6957 | 6956 CJK Ideograph-6956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6959 CJK Ideograph-6959 | 6958 CJK Ideograph-6958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 695b CJK Ideograph-695B | 695a CJK Ideograph-695A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 695d CJK Ideograph-695D | 695c CJK Ideograph-695C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 695f CJK Ideograph-695F | 695e CJK Ideograph-695E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6961 CJK Ideograph-6961 | 6960 CJK Ideograph-6960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6963 CJK Ideograph-6963 | 6962 CJK Ideograph-6962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6965 CJK Ideograph-6965 | 6964 CJK Ideograph-6964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6967 CJK Ideograph-6967 | 6966 CJK Ideograph-6966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6969 CJK Ideograph-6969 | 6968 CJK Ideograph-6968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 696b CJK Ideograph-696B | 696a CJK Ideograph-696A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 696d CJK Ideograph-696D | 696c CJK Ideograph-696C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 696f CJK Ideograph-696F | 696e CJK Ideograph-696E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6971 CJK Ideograph-6971 | 6970 CJK Ideograph-6970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6973 CJK Ideograph-6973 | 6972 CJK Ideograph-6972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6975 CJK Ideograph-6975 | 6974 CJK Ideograph-6974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6977 CJK Ideograph-6977 | 6976 CJK Ideograph-6976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6979 CJK Ideograph-6979 | 6978 CJK Ideograph-6978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 697b CJK Ideograph-697B | 697a CJK Ideograph-697A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 697d CJK Ideograph-697D | 697c CJK Ideograph-697C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 697f CJK Ideograph-697F | 697e CJK Ideograph-697E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6981 CJK Ideograph-6981 | 6980 CJK Ideograph-6980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6983 CJK Ideograph-6983 | 6982 CJK Ideograph-6982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6985 CJK Ideograph-6985 | 6984 CJK Ideograph-6984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6987 CJK Ideograph-6987 | 6986 CJK Ideograph-6986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6989 CJK Ideograph-6989 | 6988 CJK Ideograph-6988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 698b CJK Ideograph-698B | 698a CJK Ideograph-698A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 698d CJK Ideograph-698D | 698c CJK Ideograph-698C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 698f CJK Ideograph-698F | 698e CJK Ideograph-698E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6991 CJK Ideograph-6991 | 6990 CJK Ideograph-6990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6993 CJK Ideograph-6993 | 6992 CJK Ideograph-6992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6995 CJK Ideograph-6995 | 6994 CJK Ideograph-6994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6997 CJK Ideograph-6997 | 6996 CJK Ideograph-6996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6999 CJK Ideograph-6999 | 6998 CJK Ideograph-6998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 699b CJK Ideograph-699B | 699a CJK Ideograph-699A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 699d CJK Ideograph-699D | 699c CJK Ideograph-699C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 699f CJK Ideograph-699F | 699e CJK Ideograph-699E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69a1 CJK Ideograph-69A1 | 69a0 CJK Ideograph-69A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69a3 CJK Ideograph-69A3 | 69a2 CJK Ideograph-69A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69a5 CJK Ideograph-69A5 | 69a4 CJK Ideograph-69A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69a7 CJK Ideograph-69A7 | 69a6 CJK Ideograph-69A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69a9 CJK Ideograph-69A9 | 69a8 CJK Ideograph-69A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69ab CJK Ideograph-69AB | 69aa CJK Ideograph-69AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69ad CJK Ideograph-69AD | 69ac CJK Ideograph-69AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69af CJK Ideograph-69AF | 69ae CJK Ideograph-69AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69b1 CJK Ideograph-69B1 | 69b0 CJK Ideograph-69B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69b3 CJK Ideograph-69B3 | 69b2 CJK Ideograph-69B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69b5 CJK Ideograph-69B5 | 69b4 CJK Ideograph-69B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69b7 CJK Ideograph-69B7 | 69b6 CJK Ideograph-69B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69b9 CJK Ideograph-69B9 | 69b8 CJK Ideograph-69B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69bb CJK Ideograph-69BB | 69ba CJK Ideograph-69BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69bd CJK Ideograph-69BD | 69bc CJK Ideograph-69BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69bf CJK Ideograph-69BF | 69be CJK Ideograph-69BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69c1 CJK Ideograph-69C1 | 69c0 CJK Ideograph-69C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69c3 CJK Ideograph-69C3 | 69c2 CJK Ideograph-69C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69c5 CJK Ideograph-69C5 | 69c4 CJK Ideograph-69C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69c7 CJK Ideograph-69C7 | 69c6 CJK Ideograph-69C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69c9 CJK Ideograph-69C9 | 69c8 CJK Ideograph-69C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69cb CJK Ideograph-69CB | 69ca CJK Ideograph-69CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69cd CJK Ideograph-69CD | 69cc CJK Ideograph-69CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69cf CJK Ideograph-69CF | 69ce CJK Ideograph-69CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69d1 CJK Ideograph-69D1 | 69d0 CJK Ideograph-69D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69d3 CJK Ideograph-69D3 | 69d2 CJK Ideograph-69D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69d5 CJK Ideograph-69D5 | 69d4 CJK Ideograph-69D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69d7 CJK Ideograph-69D7 | 69d6 CJK Ideograph-69D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69d9 CJK Ideograph-69D9 | 69d8 CJK Ideograph-69D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69db CJK Ideograph-69DB | 69da CJK Ideograph-69DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69dd CJK Ideograph-69DD | 69dc CJK Ideograph-69DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69df CJK Ideograph-69DF | 69de CJK Ideograph-69DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69e1 CJK Ideograph-69E1 | 69e0 CJK Ideograph-69E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69e3 CJK Ideograph-69E3 | 69e2 CJK Ideograph-69E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69e5 CJK Ideograph-69E5 | 69e4 CJK Ideograph-69E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69e7 CJK Ideograph-69E7 | 69e6 CJK Ideograph-69E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69e9 CJK Ideograph-69E9 | 69e8 CJK Ideograph-69E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69eb CJK Ideograph-69EB | 69ea CJK Ideograph-69EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69ed CJK Ideograph-69ED | 69ec CJK Ideograph-69EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69ef CJK Ideograph-69EF | 69ee CJK Ideograph-69EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69f1 CJK Ideograph-69F1 | 69f0 CJK Ideograph-69F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69f3 CJK Ideograph-69F3 | 69f2 CJK Ideograph-69F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69f5 CJK Ideograph-69F5 | 69f4 CJK Ideograph-69F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69f7 CJK Ideograph-69F7 | 69f6 CJK Ideograph-69F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69f9 CJK Ideograph-69F9 | 69f8 CJK Ideograph-69F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69fb CJK Ideograph-69FB | 69fa CJK Ideograph-69FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69fd CJK Ideograph-69FD | 69fc CJK Ideograph-69FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69ff CJK Ideograph-69FF | 69fe CJK Ideograph-69FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a01 CJK Ideograph-6A01 | 6a00 CJK Ideograph-6A00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a03 CJK Ideograph-6A03 | 6a02 CJK Ideograph-6A02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a05 CJK Ideograph-6A05 | 6a04 CJK Ideograph-6A04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a07 CJK Ideograph-6A07 | 6a06 CJK Ideograph-6A06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a09 CJK Ideograph-6A09 | 6a08 CJK Ideograph-6A08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a0b CJK Ideograph-6A0B | 6a0a CJK Ideograph-6A0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a0d CJK Ideograph-6A0D | 6a0c CJK Ideograph-6A0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a0f CJK Ideograph-6A0F | 6a0e CJK Ideograph-6A0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a11 CJK Ideograph-6A11 | 6a10 CJK Ideograph-6A10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a13 CJK Ideograph-6A13 | 6a12 CJK Ideograph-6A12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a15 CJK Ideograph-6A15 | 6a14 CJK Ideograph-6A14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a17 CJK Ideograph-6A17 | 6a16 CJK Ideograph-6A16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a19 CJK Ideograph-6A19 | 6a18 CJK Ideograph-6A18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a1b CJK Ideograph-6A1B | 6a1a CJK Ideograph-6A1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a1d CJK Ideograph-6A1D | 6a1c CJK Ideograph-6A1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a1f CJK Ideograph-6A1F | 6a1e CJK Ideograph-6A1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a21 CJK Ideograph-6A21 | 6a20 CJK Ideograph-6A20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a23 CJK Ideograph-6A23 | 6a22 CJK Ideograph-6A22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a25 CJK Ideograph-6A25 | 6a24 CJK Ideograph-6A24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a27 CJK Ideograph-6A27 | 6a26 CJK Ideograph-6A26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a29 CJK Ideograph-6A29 | 6a28 CJK Ideograph-6A28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a2b CJK Ideograph-6A2B | 6a2a CJK Ideograph-6A2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a2d CJK Ideograph-6A2D | 6a2c CJK Ideograph-6A2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a2f CJK Ideograph-6A2F | 6a2e CJK Ideograph-6A2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a31 CJK Ideograph-6A31 | 6a30 CJK Ideograph-6A30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a33 CJK Ideograph-6A33 | 6a32 CJK Ideograph-6A32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a35 CJK Ideograph-6A35 | 6a34 CJK Ideograph-6A34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a37 CJK Ideograph-6A37 | 6a36 CJK Ideograph-6A36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a39 CJK Ideograph-6A39 | 6a38 CJK Ideograph-6A38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a3b CJK Ideograph-6A3B | 6a3a CJK Ideograph-6A3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a3d CJK Ideograph-6A3D | 6a3c CJK Ideograph-6A3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a3f CJK Ideograph-6A3F | 6a3e CJK Ideograph-6A3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a41 CJK Ideograph-6A41 | 6a40 CJK Ideograph-6A40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a43 CJK Ideograph-6A43 | 6a42 CJK Ideograph-6A42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a45 CJK Ideograph-6A45 | 6a44 CJK Ideograph-6A44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a47 CJK Ideograph-6A47 | 6a46 CJK Ideograph-6A46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a49 CJK Ideograph-6A49 | 6a48 CJK Ideograph-6A48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a4b CJK Ideograph-6A4B | 6a4a CJK Ideograph-6A4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a4d CJK Ideograph-6A4D | 6a4c CJK Ideograph-6A4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a4f CJK Ideograph-6A4F | 6a4e CJK Ideograph-6A4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a51 CJK Ideograph-6A51 | 6a50 CJK Ideograph-6A50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a53 CJK Ideograph-6A53 | 6a52 CJK Ideograph-6A52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a55 CJK Ideograph-6A55 | 6a54 CJK Ideograph-6A54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a57 CJK Ideograph-6A57 | 6a56 CJK Ideograph-6A56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a59 CJK Ideograph-6A59 | 6a58 CJK Ideograph-6A58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a5b CJK Ideograph-6A5B | 6a5a CJK Ideograph-6A5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a5d CJK Ideograph-6A5D | 6a5c CJK Ideograph-6A5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a5f CJK Ideograph-6A5F | 6a5e CJK Ideograph-6A5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a61 CJK Ideograph-6A61 | 6a60 CJK Ideograph-6A60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a63 CJK Ideograph-6A63 | 6a62 CJK Ideograph-6A62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a65 CJK Ideograph-6A65 | 6a64 CJK Ideograph-6A64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a67 CJK Ideograph-6A67 | 6a66 CJK Ideograph-6A66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a69 CJK Ideograph-6A69 | 6a68 CJK Ideograph-6A68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a6b CJK Ideograph-6A6B | 6a6a CJK Ideograph-6A6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a6d CJK Ideograph-6A6D | 6a6c CJK Ideograph-6A6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a6f CJK Ideograph-6A6F | 6a6e CJK Ideograph-6A6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a71 CJK Ideograph-6A71 | 6a70 CJK Ideograph-6A70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a73 CJK Ideograph-6A73 | 6a72 CJK Ideograph-6A72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a75 CJK Ideograph-6A75 | 6a74 CJK Ideograph-6A74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a77 CJK Ideograph-6A77 | 6a76 CJK Ideograph-6A76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a79 CJK Ideograph-6A79 | 6a78 CJK Ideograph-6A78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a7b CJK Ideograph-6A7B | 6a7a CJK Ideograph-6A7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a7d CJK Ideograph-6A7D | 6a7c CJK Ideograph-6A7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a7f CJK Ideograph-6A7F | 6a7e CJK Ideograph-6A7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a81 CJK Ideograph-6A81 | 6a80 CJK Ideograph-6A80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a83 CJK Ideograph-6A83 | 6a82 CJK Ideograph-6A82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a85 CJK Ideograph-6A85 | 6a84 CJK Ideograph-6A84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a87 CJK Ideograph-6A87 | 6a86 CJK Ideograph-6A86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a89 CJK Ideograph-6A89 | 6a88 CJK Ideograph-6A88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a8b CJK Ideograph-6A8B | 6a8a CJK Ideograph-6A8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a8d CJK Ideograph-6A8D | 6a8c CJK Ideograph-6A8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a8f CJK Ideograph-6A8F | 6a8e CJK Ideograph-6A8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a91 CJK Ideograph-6A91 | 6a90 CJK Ideograph-6A90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a93 CJK Ideograph-6A93 | 6a92 CJK Ideograph-6A92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a95 CJK Ideograph-6A95 | 6a94 CJK Ideograph-6A94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a97 CJK Ideograph-6A97 | 6a96 CJK Ideograph-6A96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a99 CJK Ideograph-6A99 | 6a98 CJK Ideograph-6A98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a9b CJK Ideograph-6A9B | 6a9a CJK Ideograph-6A9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a9d CJK Ideograph-6A9D | 6a9c CJK Ideograph-6A9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a9f CJK Ideograph-6A9F | 6a9e CJK Ideograph-6A9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aa1 CJK Ideograph-6AA1 | 6aa0 CJK Ideograph-6AA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aa3 CJK Ideograph-6AA3 | 6aa2 CJK Ideograph-6AA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aa5 CJK Ideograph-6AA5 | 6aa4 CJK Ideograph-6AA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aa7 CJK Ideograph-6AA7 | 6aa6 CJK Ideograph-6AA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aa9 CJK Ideograph-6AA9 | 6aa8 CJK Ideograph-6AA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aab CJK Ideograph-6AAB | 6aaa CJK Ideograph-6AAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aad CJK Ideograph-6AAD | 6aac CJK Ideograph-6AAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aaf CJK Ideograph-6AAF | 6aae CJK Ideograph-6AAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ab1 CJK Ideograph-6AB1 | 6ab0 CJK Ideograph-6AB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ab3 CJK Ideograph-6AB3 | 6ab2 CJK Ideograph-6AB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ab5 CJK Ideograph-6AB5 | 6ab4 CJK Ideograph-6AB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ab7 CJK Ideograph-6AB7 | 6ab6 CJK Ideograph-6AB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ab9 CJK Ideograph-6AB9 | 6ab8 CJK Ideograph-6AB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6abb CJK Ideograph-6ABB | 6aba CJK Ideograph-6ABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6abd CJK Ideograph-6ABD | 6abc CJK Ideograph-6ABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6abf CJK Ideograph-6ABF | 6abe CJK Ideograph-6ABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ac1 CJK Ideograph-6AC1 | 6ac0 CJK Ideograph-6AC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ac3 CJK Ideograph-6AC3 | 6ac2 CJK Ideograph-6AC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ac5 CJK Ideograph-6AC5 | 6ac4 CJK Ideograph-6AC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ac7 CJK Ideograph-6AC7 | 6ac6 CJK Ideograph-6AC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ac9 CJK Ideograph-6AC9 | 6ac8 CJK Ideograph-6AC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6acb CJK Ideograph-6ACB | 6aca CJK Ideograph-6ACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6acd CJK Ideograph-6ACD | 6acc CJK Ideograph-6ACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6acf CJK Ideograph-6ACF | 6ace CJK Ideograph-6ACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ad1 CJK Ideograph-6AD1 | 6ad0 CJK Ideograph-6AD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ad3 CJK Ideograph-6AD3 | 6ad2 CJK Ideograph-6AD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ad5 CJK Ideograph-6AD5 | 6ad4 CJK Ideograph-6AD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ad7 CJK Ideograph-6AD7 | 6ad6 CJK Ideograph-6AD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ad9 CJK Ideograph-6AD9 | 6ad8 CJK Ideograph-6AD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6adb CJK Ideograph-6ADB | 6ada CJK Ideograph-6ADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6add CJK Ideograph-6ADD | 6adc CJK Ideograph-6ADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6adf CJK Ideograph-6ADF | 6ade CJK Ideograph-6ADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ae1 CJK Ideograph-6AE1 | 6ae0 CJK Ideograph-6AE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ae3 CJK Ideograph-6AE3 | 6ae2 CJK Ideograph-6AE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ae5 CJK Ideograph-6AE5 | 6ae4 CJK Ideograph-6AE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ae7 CJK Ideograph-6AE7 | 6ae6 CJK Ideograph-6AE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ae9 CJK Ideograph-6AE9 | 6ae8 CJK Ideograph-6AE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aeb CJK Ideograph-6AEB | 6aea CJK Ideograph-6AEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aed CJK Ideograph-6AED | 6aec CJK Ideograph-6AEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aef CJK Ideograph-6AEF | 6aee CJK Ideograph-6AEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6af1 CJK Ideograph-6AF1 | 6af0 CJK Ideograph-6AF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6af3 CJK Ideograph-6AF3 | 6af2 CJK Ideograph-6AF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6af5 CJK Ideograph-6AF5 | 6af4 CJK Ideograph-6AF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6af7 CJK Ideograph-6AF7 | 6af6 CJK Ideograph-6AF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6af9 CJK Ideograph-6AF9 | 6af8 CJK Ideograph-6AF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6afb CJK Ideograph-6AFB | 6afa CJK Ideograph-6AFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6afd CJK Ideograph-6AFD | 6afc CJK Ideograph-6AFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aff CJK Ideograph-6AFF | 6afe CJK Ideograph-6AFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b01 CJK Ideograph-6B01 | 6b00 CJK Ideograph-6B00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b03 CJK Ideograph-6B03 | 6b02 CJK Ideograph-6B02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b05 CJK Ideograph-6B05 | 6b04 CJK Ideograph-6B04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b07 CJK Ideograph-6B07 | 6b06 CJK Ideograph-6B06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b09 CJK Ideograph-6B09 | 6b08 CJK Ideograph-6B08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b0b CJK Ideograph-6B0B | 6b0a CJK Ideograph-6B0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b0d CJK Ideograph-6B0D | 6b0c CJK Ideograph-6B0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b0f CJK Ideograph-6B0F | 6b0e CJK Ideograph-6B0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b11 CJK Ideograph-6B11 | 6b10 CJK Ideograph-6B10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b13 CJK Ideograph-6B13 | 6b12 CJK Ideograph-6B12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b15 CJK Ideograph-6B15 | 6b14 CJK Ideograph-6B14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b17 CJK Ideograph-6B17 | 6b16 CJK Ideograph-6B16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b19 CJK Ideograph-6B19 | 6b18 CJK Ideograph-6B18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b1b CJK Ideograph-6B1B | 6b1a CJK Ideograph-6B1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b1d CJK Ideograph-6B1D | 6b1c CJK Ideograph-6B1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b1f CJK Ideograph-6B1F | 6b1e CJK Ideograph-6B1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b21 CJK Ideograph-6B21 | 6b20 CJK Ideograph-6B20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b23 CJK Ideograph-6B23 | 6b22 CJK Ideograph-6B22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b25 CJK Ideograph-6B25 | 6b24 CJK Ideograph-6B24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b27 CJK Ideograph-6B27 | 6b26 CJK Ideograph-6B26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b29 CJK Ideograph-6B29 | 6b28 CJK Ideograph-6B28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b2b CJK Ideograph-6B2B | 6b2a CJK Ideograph-6B2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b2d CJK Ideograph-6B2D | 6b2c CJK Ideograph-6B2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b2f CJK Ideograph-6B2F | 6b2e CJK Ideograph-6B2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b31 CJK Ideograph-6B31 | 6b30 CJK Ideograph-6B30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b33 CJK Ideograph-6B33 | 6b32 CJK Ideograph-6B32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b35 CJK Ideograph-6B35 | 6b34 CJK Ideograph-6B34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b37 CJK Ideograph-6B37 | 6b36 CJK Ideograph-6B36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b39 CJK Ideograph-6B39 | 6b38 CJK Ideograph-6B38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b3b CJK Ideograph-6B3B | 6b3a CJK Ideograph-6B3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b3d CJK Ideograph-6B3D | 6b3c CJK Ideograph-6B3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b3f CJK Ideograph-6B3F | 6b3e CJK Ideograph-6B3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b41 CJK Ideograph-6B41 | 6b40 CJK Ideograph-6B40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b43 CJK Ideograph-6B43 | 6b42 CJK Ideograph-6B42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b45 CJK Ideograph-6B45 | 6b44 CJK Ideograph-6B44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b47 CJK Ideograph-6B47 | 6b46 CJK Ideograph-6B46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b49 CJK Ideograph-6B49 | 6b48 CJK Ideograph-6B48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b4b CJK Ideograph-6B4B | 6b4a CJK Ideograph-6B4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b4d CJK Ideograph-6B4D | 6b4c CJK Ideograph-6B4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b4f CJK Ideograph-6B4F | 6b4e CJK Ideograph-6B4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b51 CJK Ideograph-6B51 | 6b50 CJK Ideograph-6B50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b53 CJK Ideograph-6B53 | 6b52 CJK Ideograph-6B52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b55 CJK Ideograph-6B55 | 6b54 CJK Ideograph-6B54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b57 CJK Ideograph-6B57 | 6b56 CJK Ideograph-6B56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b59 CJK Ideograph-6B59 | 6b58 CJK Ideograph-6B58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b5b CJK Ideograph-6B5B | 6b5a CJK Ideograph-6B5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b5d CJK Ideograph-6B5D | 6b5c CJK Ideograph-6B5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b5f CJK Ideograph-6B5F | 6b5e CJK Ideograph-6B5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b61 CJK Ideograph-6B61 | 6b60 CJK Ideograph-6B60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b63 CJK Ideograph-6B63 | 6b62 CJK Ideograph-6B62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b65 CJK Ideograph-6B65 | 6b64 CJK Ideograph-6B64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b67 CJK Ideograph-6B67 | 6b66 CJK Ideograph-6B66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b69 CJK Ideograph-6B69 | 6b68 CJK Ideograph-6B68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b6b CJK Ideograph-6B6B | 6b6a CJK Ideograph-6B6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b6d CJK Ideograph-6B6D | 6b6c CJK Ideograph-6B6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b6f CJK Ideograph-6B6F | 6b6e CJK Ideograph-6B6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b71 CJK Ideograph-6B71 | 6b70 CJK Ideograph-6B70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b73 CJK Ideograph-6B73 | 6b72 CJK Ideograph-6B72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b75 CJK Ideograph-6B75 | 6b74 CJK Ideograph-6B74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b77 CJK Ideograph-6B77 | 6b76 CJK Ideograph-6B76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b79 CJK Ideograph-6B79 | 6b78 CJK Ideograph-6B78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b7b CJK Ideograph-6B7B | 6b7a CJK Ideograph-6B7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b7d CJK Ideograph-6B7D | 6b7c CJK Ideograph-6B7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b7f CJK Ideograph-6B7F | 6b7e CJK Ideograph-6B7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b81 CJK Ideograph-6B81 | 6b80 CJK Ideograph-6B80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b83 CJK Ideograph-6B83 | 6b82 CJK Ideograph-6B82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b85 CJK Ideograph-6B85 | 6b84 CJK Ideograph-6B84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b87 CJK Ideograph-6B87 | 6b86 CJK Ideograph-6B86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b89 CJK Ideograph-6B89 | 6b88 CJK Ideograph-6B88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b8b CJK Ideograph-6B8B | 6b8a CJK Ideograph-6B8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b8d CJK Ideograph-6B8D | 6b8c CJK Ideograph-6B8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b8f CJK Ideograph-6B8F | 6b8e CJK Ideograph-6B8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b91 CJK Ideograph-6B91 | 6b90 CJK Ideograph-6B90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b93 CJK Ideograph-6B93 | 6b92 CJK Ideograph-6B92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b95 CJK Ideograph-6B95 | 6b94 CJK Ideograph-6B94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b97 CJK Ideograph-6B97 | 6b96 CJK Ideograph-6B96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b99 CJK Ideograph-6B99 | 6b98 CJK Ideograph-6B98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b9b CJK Ideograph-6B9B | 6b9a CJK Ideograph-6B9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b9d CJK Ideograph-6B9D | 6b9c CJK Ideograph-6B9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b9f CJK Ideograph-6B9F | 6b9e CJK Ideograph-6B9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ba1 CJK Ideograph-6BA1 | 6ba0 CJK Ideograph-6BA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ba3 CJK Ideograph-6BA3 | 6ba2 CJK Ideograph-6BA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ba5 CJK Ideograph-6BA5 | 6ba4 CJK Ideograph-6BA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ba7 CJK Ideograph-6BA7 | 6ba6 CJK Ideograph-6BA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ba9 CJK Ideograph-6BA9 | 6ba8 CJK Ideograph-6BA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bab CJK Ideograph-6BAB | 6baa CJK Ideograph-6BAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bad CJK Ideograph-6BAD | 6bac CJK Ideograph-6BAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6baf CJK Ideograph-6BAF | 6bae CJK Ideograph-6BAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bb1 CJK Ideograph-6BB1 | 6bb0 CJK Ideograph-6BB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bb3 CJK Ideograph-6BB3 | 6bb2 CJK Ideograph-6BB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bb5 CJK Ideograph-6BB5 | 6bb4 CJK Ideograph-6BB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bb7 CJK Ideograph-6BB7 | 6bb6 CJK Ideograph-6BB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bb9 CJK Ideograph-6BB9 | 6bb8 CJK Ideograph-6BB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bbb CJK Ideograph-6BBB | 6bba CJK Ideograph-6BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bbd CJK Ideograph-6BBD | 6bbc CJK Ideograph-6BBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bbf CJK Ideograph-6BBF | 6bbe CJK Ideograph-6BBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bc1 CJK Ideograph-6BC1 | 6bc0 CJK Ideograph-6BC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bc3 CJK Ideograph-6BC3 | 6bc2 CJK Ideograph-6BC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bc5 CJK Ideograph-6BC5 | 6bc4 CJK Ideograph-6BC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bc7 CJK Ideograph-6BC7 | 6bc6 CJK Ideograph-6BC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bc9 CJK Ideograph-6BC9 | 6bc8 CJK Ideograph-6BC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bcb CJK Ideograph-6BCB | 6bca CJK Ideograph-6BCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bcd CJK Ideograph-6BCD | 6bcc CJK Ideograph-6BCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bcf CJK Ideograph-6BCF | 6bce CJK Ideograph-6BCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bd1 CJK Ideograph-6BD1 | 6bd0 CJK Ideograph-6BD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bd3 CJK Ideograph-6BD3 | 6bd2 CJK Ideograph-6BD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bd5 CJK Ideograph-6BD5 | 6bd4 CJK Ideograph-6BD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bd7 CJK Ideograph-6BD7 | 6bd6 CJK Ideograph-6BD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bd9 CJK Ideograph-6BD9 | 6bd8 CJK Ideograph-6BD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bdb CJK Ideograph-6BDB | 6bda CJK Ideograph-6BDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bdd CJK Ideograph-6BDD | 6bdc CJK Ideograph-6BDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bdf CJK Ideograph-6BDF | 6bde CJK Ideograph-6BDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6be1 CJK Ideograph-6BE1 | 6be0 CJK Ideograph-6BE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6be3 CJK Ideograph-6BE3 | 6be2 CJK Ideograph-6BE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6be5 CJK Ideograph-6BE5 | 6be4 CJK Ideograph-6BE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6be7 CJK Ideograph-6BE7 | 6be6 CJK Ideograph-6BE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6be9 CJK Ideograph-6BE9 | 6be8 CJK Ideograph-6BE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6beb CJK Ideograph-6BEB | 6bea CJK Ideograph-6BEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bed CJK Ideograph-6BED | 6bec CJK Ideograph-6BEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bef CJK Ideograph-6BEF | 6bee CJK Ideograph-6BEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bf1 CJK Ideograph-6BF1 | 6bf0 CJK Ideograph-6BF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bf3 CJK Ideograph-6BF3 | 6bf2 CJK Ideograph-6BF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bf5 CJK Ideograph-6BF5 | 6bf4 CJK Ideograph-6BF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bf7 CJK Ideograph-6BF7 | 6bf6 CJK Ideograph-6BF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bf9 CJK Ideograph-6BF9 | 6bf8 CJK Ideograph-6BF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bfb CJK Ideograph-6BFB | 6bfa CJK Ideograph-6BFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bfd CJK Ideograph-6BFD | 6bfc CJK Ideograph-6BFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bff CJK Ideograph-6BFF | 6bfe CJK Ideograph-6BFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c01 CJK Ideograph-6C01 | 6c00 CJK Ideograph-6C00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c03 CJK Ideograph-6C03 | 6c02 CJK Ideograph-6C02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c05 CJK Ideograph-6C05 | 6c04 CJK Ideograph-6C04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c07 CJK Ideograph-6C07 | 6c06 CJK Ideograph-6C06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c09 CJK Ideograph-6C09 | 6c08 CJK Ideograph-6C08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c0b CJK Ideograph-6C0B | 6c0a CJK Ideograph-6C0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c0d CJK Ideograph-6C0D | 6c0c CJK Ideograph-6C0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c0f CJK Ideograph-6C0F | 6c0e CJK Ideograph-6C0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c11 CJK Ideograph-6C11 | 6c10 CJK Ideograph-6C10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c13 CJK Ideograph-6C13 | 6c12 CJK Ideograph-6C12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c15 CJK Ideograph-6C15 | 6c14 CJK Ideograph-6C14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c17 CJK Ideograph-6C17 | 6c16 CJK Ideograph-6C16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c19 CJK Ideograph-6C19 | 6c18 CJK Ideograph-6C18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c1b CJK Ideograph-6C1B | 6c1a CJK Ideograph-6C1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c1d CJK Ideograph-6C1D | 6c1c CJK Ideograph-6C1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c1f CJK Ideograph-6C1F | 6c1e CJK Ideograph-6C1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c21 CJK Ideograph-6C21 | 6c20 CJK Ideograph-6C20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c23 CJK Ideograph-6C23 | 6c22 CJK Ideograph-6C22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c25 CJK Ideograph-6C25 | 6c24 CJK Ideograph-6C24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c27 CJK Ideograph-6C27 | 6c26 CJK Ideograph-6C26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c29 CJK Ideograph-6C29 | 6c28 CJK Ideograph-6C28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c2b CJK Ideograph-6C2B | 6c2a CJK Ideograph-6C2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c2d CJK Ideograph-6C2D | 6c2c CJK Ideograph-6C2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c2f CJK Ideograph-6C2F | 6c2e CJK Ideograph-6C2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c31 CJK Ideograph-6C31 | 6c30 CJK Ideograph-6C30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c33 CJK Ideograph-6C33 | 6c32 CJK Ideograph-6C32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c35 CJK Ideograph-6C35 | 6c34 CJK Ideograph-6C34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c37 CJK Ideograph-6C37 | 6c36 CJK Ideograph-6C36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c39 CJK Ideograph-6C39 | 6c38 CJK Ideograph-6C38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c3b CJK Ideograph-6C3B | 6c3a CJK Ideograph-6C3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c3d CJK Ideograph-6C3D | 6c3c CJK Ideograph-6C3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c3f CJK Ideograph-6C3F | 6c3e CJK Ideograph-6C3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c41 CJK Ideograph-6C41 | 6c40 CJK Ideograph-6C40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c43 CJK Ideograph-6C43 | 6c42 CJK Ideograph-6C42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c45 CJK Ideograph-6C45 | 6c44 CJK Ideograph-6C44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c47 CJK Ideograph-6C47 | 6c46 CJK Ideograph-6C46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c49 CJK Ideograph-6C49 | 6c48 CJK Ideograph-6C48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c4b CJK Ideograph-6C4B | 6c4a CJK Ideograph-6C4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c4d CJK Ideograph-6C4D | 6c4c CJK Ideograph-6C4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c4f CJK Ideograph-6C4F | 6c4e CJK Ideograph-6C4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c51 CJK Ideograph-6C51 | 6c50 CJK Ideograph-6C50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c53 CJK Ideograph-6C53 | 6c52 CJK Ideograph-6C52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c55 CJK Ideograph-6C55 | 6c54 CJK Ideograph-6C54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c57 CJK Ideograph-6C57 | 6c56 CJK Ideograph-6C56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c59 CJK Ideograph-6C59 | 6c58 CJK Ideograph-6C58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c5b CJK Ideograph-6C5B | 6c5a CJK Ideograph-6C5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c5d CJK Ideograph-6C5D | 6c5c CJK Ideograph-6C5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c5f CJK Ideograph-6C5F | 6c5e CJK Ideograph-6C5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c61 CJK Ideograph-6C61 | 6c60 CJK Ideograph-6C60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c63 CJK Ideograph-6C63 | 6c62 CJK Ideograph-6C62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c65 CJK Ideograph-6C65 | 6c64 CJK Ideograph-6C64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c67 CJK Ideograph-6C67 | 6c66 CJK Ideograph-6C66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c69 CJK Ideograph-6C69 | 6c68 CJK Ideograph-6C68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c6b CJK Ideograph-6C6B | 6c6a CJK Ideograph-6C6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c6d CJK Ideograph-6C6D | 6c6c CJK Ideograph-6C6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c6f CJK Ideograph-6C6F | 6c6e CJK Ideograph-6C6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c71 CJK Ideograph-6C71 | 6c70 CJK Ideograph-6C70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c73 CJK Ideograph-6C73 | 6c72 CJK Ideograph-6C72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c75 CJK Ideograph-6C75 | 6c74 CJK Ideograph-6C74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c77 CJK Ideograph-6C77 | 6c76 CJK Ideograph-6C76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c79 CJK Ideograph-6C79 | 6c78 CJK Ideograph-6C78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c7b CJK Ideograph-6C7B | 6c7a CJK Ideograph-6C7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c7d CJK Ideograph-6C7D | 6c7c CJK Ideograph-6C7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c7f CJK Ideograph-6C7F | 6c7e CJK Ideograph-6C7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c81 CJK Ideograph-6C81 | 6c80 CJK Ideograph-6C80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c83 CJK Ideograph-6C83 | 6c82 CJK Ideograph-6C82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c85 CJK Ideograph-6C85 | 6c84 CJK Ideograph-6C84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c87 CJK Ideograph-6C87 | 6c86 CJK Ideograph-6C86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c89 CJK Ideograph-6C89 | 6c88 CJK Ideograph-6C88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c8b CJK Ideograph-6C8B | 6c8a CJK Ideograph-6C8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c8d CJK Ideograph-6C8D | 6c8c CJK Ideograph-6C8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c8f CJK Ideograph-6C8F | 6c8e CJK Ideograph-6C8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c91 CJK Ideograph-6C91 | 6c90 CJK Ideograph-6C90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c93 CJK Ideograph-6C93 | 6c92 CJK Ideograph-6C92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c95 CJK Ideograph-6C95 | 6c94 CJK Ideograph-6C94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c97 CJK Ideograph-6C97 | 6c96 CJK Ideograph-6C96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c99 CJK Ideograph-6C99 | 6c98 CJK Ideograph-6C98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c9b CJK Ideograph-6C9B | 6c9a CJK Ideograph-6C9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c9d CJK Ideograph-6C9D | 6c9c CJK Ideograph-6C9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c9f CJK Ideograph-6C9F | 6c9e CJK Ideograph-6C9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ca1 CJK Ideograph-6CA1 | 6ca0 CJK Ideograph-6CA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ca3 CJK Ideograph-6CA3 | 6ca2 CJK Ideograph-6CA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ca5 CJK Ideograph-6CA5 | 6ca4 CJK Ideograph-6CA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ca7 CJK Ideograph-6CA7 | 6ca6 CJK Ideograph-6CA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ca9 CJK Ideograph-6CA9 | 6ca8 CJK Ideograph-6CA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cab CJK Ideograph-6CAB | 6caa CJK Ideograph-6CAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cad CJK Ideograph-6CAD | 6cac CJK Ideograph-6CAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6caf CJK Ideograph-6CAF | 6cae CJK Ideograph-6CAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cb1 CJK Ideograph-6CB1 | 6cb0 CJK Ideograph-6CB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cb3 CJK Ideograph-6CB3 | 6cb2 CJK Ideograph-6CB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cb5 CJK Ideograph-6CB5 | 6cb4 CJK Ideograph-6CB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cb7 CJK Ideograph-6CB7 | 6cb6 CJK Ideograph-6CB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cb9 CJK Ideograph-6CB9 | 6cb8 CJK Ideograph-6CB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cbb CJK Ideograph-6CBB | 6cba CJK Ideograph-6CBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cbd CJK Ideograph-6CBD | 6cbc CJK Ideograph-6CBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cbf CJK Ideograph-6CBF | 6cbe CJK Ideograph-6CBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cc1 CJK Ideograph-6CC1 | 6cc0 CJK Ideograph-6CC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cc3 CJK Ideograph-6CC3 | 6cc2 CJK Ideograph-6CC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cc5 CJK Ideograph-6CC5 | 6cc4 CJK Ideograph-6CC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cc7 CJK Ideograph-6CC7 | 6cc6 CJK Ideograph-6CC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cc9 CJK Ideograph-6CC9 | 6cc8 CJK Ideograph-6CC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ccb CJK Ideograph-6CCB | 6cca CJK Ideograph-6CCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ccd CJK Ideograph-6CCD | 6ccc CJK Ideograph-6CCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ccf CJK Ideograph-6CCF | 6cce CJK Ideograph-6CCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cd1 CJK Ideograph-6CD1 | 6cd0 CJK Ideograph-6CD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cd3 CJK Ideograph-6CD3 | 6cd2 CJK Ideograph-6CD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cd5 CJK Ideograph-6CD5 | 6cd4 CJK Ideograph-6CD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cd7 CJK Ideograph-6CD7 | 6cd6 CJK Ideograph-6CD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cd9 CJK Ideograph-6CD9 | 6cd8 CJK Ideograph-6CD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cdb CJK Ideograph-6CDB | 6cda CJK Ideograph-6CDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cdd CJK Ideograph-6CDD | 6cdc CJK Ideograph-6CDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cdf CJK Ideograph-6CDF | 6cde CJK Ideograph-6CDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ce1 CJK Ideograph-6CE1 | 6ce0 CJK Ideograph-6CE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ce3 CJK Ideograph-6CE3 | 6ce2 CJK Ideograph-6CE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ce5 CJK Ideograph-6CE5 | 6ce4 CJK Ideograph-6CE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ce7 CJK Ideograph-6CE7 | 6ce6 CJK Ideograph-6CE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ce9 CJK Ideograph-6CE9 | 6ce8 CJK Ideograph-6CE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ceb CJK Ideograph-6CEB | 6cea CJK Ideograph-6CEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ced CJK Ideograph-6CED | 6cec CJK Ideograph-6CEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cef CJK Ideograph-6CEF | 6cee CJK Ideograph-6CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cf1 CJK Ideograph-6CF1 | 6cf0 CJK Ideograph-6CF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cf3 CJK Ideograph-6CF3 | 6cf2 CJK Ideograph-6CF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cf5 CJK Ideograph-6CF5 | 6cf4 CJK Ideograph-6CF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cf7 CJK Ideograph-6CF7 | 6cf6 CJK Ideograph-6CF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cf9 CJK Ideograph-6CF9 | 6cf8 CJK Ideograph-6CF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cfb CJK Ideograph-6CFB | 6cfa CJK Ideograph-6CFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cfd CJK Ideograph-6CFD | 6cfc CJK Ideograph-6CFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cff CJK Ideograph-6CFF | 6cfe CJK Ideograph-6CFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d01 CJK Ideograph-6D01 | 6d00 CJK Ideograph-6D00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d03 CJK Ideograph-6D03 | 6d02 CJK Ideograph-6D02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d05 CJK Ideograph-6D05 | 6d04 CJK Ideograph-6D04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d07 CJK Ideograph-6D07 | 6d06 CJK Ideograph-6D06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d09 CJK Ideograph-6D09 | 6d08 CJK Ideograph-6D08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d0b CJK Ideograph-6D0B | 6d0a CJK Ideograph-6D0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d0d CJK Ideograph-6D0D | 6d0c CJK Ideograph-6D0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d0f CJK Ideograph-6D0F | 6d0e CJK Ideograph-6D0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d11 CJK Ideograph-6D11 | 6d10 CJK Ideograph-6D10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d13 CJK Ideograph-6D13 | 6d12 CJK Ideograph-6D12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d15 CJK Ideograph-6D15 | 6d14 CJK Ideograph-6D14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d17 CJK Ideograph-6D17 | 6d16 CJK Ideograph-6D16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d19 CJK Ideograph-6D19 | 6d18 CJK Ideograph-6D18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d1b CJK Ideograph-6D1B | 6d1a CJK Ideograph-6D1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d1d CJK Ideograph-6D1D | 6d1c CJK Ideograph-6D1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d1f CJK Ideograph-6D1F | 6d1e CJK Ideograph-6D1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d21 CJK Ideograph-6D21 | 6d20 CJK Ideograph-6D20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d23 CJK Ideograph-6D23 | 6d22 CJK Ideograph-6D22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d25 CJK Ideograph-6D25 | 6d24 CJK Ideograph-6D24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d27 CJK Ideograph-6D27 | 6d26 CJK Ideograph-6D26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d29 CJK Ideograph-6D29 | 6d28 CJK Ideograph-6D28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d2b CJK Ideograph-6D2B | 6d2a CJK Ideograph-6D2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d2d CJK Ideograph-6D2D | 6d2c CJK Ideograph-6D2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d2f CJK Ideograph-6D2F | 6d2e CJK Ideograph-6D2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d31 CJK Ideograph-6D31 | 6d30 CJK Ideograph-6D30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d33 CJK Ideograph-6D33 | 6d32 CJK Ideograph-6D32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d35 CJK Ideograph-6D35 | 6d34 CJK Ideograph-6D34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d37 CJK Ideograph-6D37 | 6d36 CJK Ideograph-6D36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d39 CJK Ideograph-6D39 | 6d38 CJK Ideograph-6D38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d3b CJK Ideograph-6D3B | 6d3a CJK Ideograph-6D3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d3d CJK Ideograph-6D3D | 6d3c CJK Ideograph-6D3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d3f CJK Ideograph-6D3F | 6d3e CJK Ideograph-6D3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d41 CJK Ideograph-6D41 | 6d40 CJK Ideograph-6D40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d43 CJK Ideograph-6D43 | 6d42 CJK Ideograph-6D42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d45 CJK Ideograph-6D45 | 6d44 CJK Ideograph-6D44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d47 CJK Ideograph-6D47 | 6d46 CJK Ideograph-6D46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d49 CJK Ideograph-6D49 | 6d48 CJK Ideograph-6D48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d4b CJK Ideograph-6D4B | 6d4a CJK Ideograph-6D4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d4d CJK Ideograph-6D4D | 6d4c CJK Ideograph-6D4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d4f CJK Ideograph-6D4F | 6d4e CJK Ideograph-6D4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d51 CJK Ideograph-6D51 | 6d50 CJK Ideograph-6D50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d53 CJK Ideograph-6D53 | 6d52 CJK Ideograph-6D52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d55 CJK Ideograph-6D55 | 6d54 CJK Ideograph-6D54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d57 CJK Ideograph-6D57 | 6d56 CJK Ideograph-6D56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d59 CJK Ideograph-6D59 | 6d58 CJK Ideograph-6D58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d5b CJK Ideograph-6D5B | 6d5a CJK Ideograph-6D5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d5d CJK Ideograph-6D5D | 6d5c CJK Ideograph-6D5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d5f CJK Ideograph-6D5F | 6d5e CJK Ideograph-6D5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d61 CJK Ideograph-6D61 | 6d60 CJK Ideograph-6D60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d63 CJK Ideograph-6D63 | 6d62 CJK Ideograph-6D62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d65 CJK Ideograph-6D65 | 6d64 CJK Ideograph-6D64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d67 CJK Ideograph-6D67 | 6d66 CJK Ideograph-6D66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d69 CJK Ideograph-6D69 | 6d68 CJK Ideograph-6D68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d6b CJK Ideograph-6D6B | 6d6a CJK Ideograph-6D6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d6d CJK Ideograph-6D6D | 6d6c CJK Ideograph-6D6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d6f CJK Ideograph-6D6F | 6d6e CJK Ideograph-6D6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d71 CJK Ideograph-6D71 | 6d70 CJK Ideograph-6D70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d73 CJK Ideograph-6D73 | 6d72 CJK Ideograph-6D72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d75 CJK Ideograph-6D75 | 6d74 CJK Ideograph-6D74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d77 CJK Ideograph-6D77 | 6d76 CJK Ideograph-6D76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d79 CJK Ideograph-6D79 | 6d78 CJK Ideograph-6D78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d7b CJK Ideograph-6D7B | 6d7a CJK Ideograph-6D7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d7d CJK Ideograph-6D7D | 6d7c CJK Ideograph-6D7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d7f CJK Ideograph-6D7F | 6d7e CJK Ideograph-6D7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d81 CJK Ideograph-6D81 | 6d80 CJK Ideograph-6D80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d83 CJK Ideograph-6D83 | 6d82 CJK Ideograph-6D82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d85 CJK Ideograph-6D85 | 6d84 CJK Ideograph-6D84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d87 CJK Ideograph-6D87 | 6d86 CJK Ideograph-6D86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d89 CJK Ideograph-6D89 | 6d88 CJK Ideograph-6D88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d8b CJK Ideograph-6D8B | 6d8a CJK Ideograph-6D8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d8d CJK Ideograph-6D8D | 6d8c CJK Ideograph-6D8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d8f CJK Ideograph-6D8F | 6d8e CJK Ideograph-6D8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d91 CJK Ideograph-6D91 | 6d90 CJK Ideograph-6D90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d93 CJK Ideograph-6D93 | 6d92 CJK Ideograph-6D92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d95 CJK Ideograph-6D95 | 6d94 CJK Ideograph-6D94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d97 CJK Ideograph-6D97 | 6d96 CJK Ideograph-6D96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d99 CJK Ideograph-6D99 | 6d98 CJK Ideograph-6D98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d9b CJK Ideograph-6D9B | 6d9a CJK Ideograph-6D9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d9d CJK Ideograph-6D9D | 6d9c CJK Ideograph-6D9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d9f CJK Ideograph-6D9F | 6d9e CJK Ideograph-6D9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6da1 CJK Ideograph-6DA1 | 6da0 CJK Ideograph-6DA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6da3 CJK Ideograph-6DA3 | 6da2 CJK Ideograph-6DA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6da5 CJK Ideograph-6DA5 | 6da4 CJK Ideograph-6DA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6da7 CJK Ideograph-6DA7 | 6da6 CJK Ideograph-6DA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6da9 CJK Ideograph-6DA9 | 6da8 CJK Ideograph-6DA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dab CJK Ideograph-6DAB | 6daa CJK Ideograph-6DAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dad CJK Ideograph-6DAD | 6dac CJK Ideograph-6DAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6daf CJK Ideograph-6DAF | 6dae CJK Ideograph-6DAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6db1 CJK Ideograph-6DB1 | 6db0 CJK Ideograph-6DB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6db3 CJK Ideograph-6DB3 | 6db2 CJK Ideograph-6DB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6db5 CJK Ideograph-6DB5 | 6db4 CJK Ideograph-6DB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6db7 CJK Ideograph-6DB7 | 6db6 CJK Ideograph-6DB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6db9 CJK Ideograph-6DB9 | 6db8 CJK Ideograph-6DB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dbb CJK Ideograph-6DBB | 6dba CJK Ideograph-6DBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dbd CJK Ideograph-6DBD | 6dbc CJK Ideograph-6DBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dbf CJK Ideograph-6DBF | 6dbe CJK Ideograph-6DBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dc1 CJK Ideograph-6DC1 | 6dc0 CJK Ideograph-6DC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dc3 CJK Ideograph-6DC3 | 6dc2 CJK Ideograph-6DC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dc5 CJK Ideograph-6DC5 | 6dc4 CJK Ideograph-6DC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dc7 CJK Ideograph-6DC7 | 6dc6 CJK Ideograph-6DC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dc9 CJK Ideograph-6DC9 | 6dc8 CJK Ideograph-6DC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dcb CJK Ideograph-6DCB | 6dca CJK Ideograph-6DCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dcd CJK Ideograph-6DCD | 6dcc CJK Ideograph-6DCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dcf CJK Ideograph-6DCF | 6dce CJK Ideograph-6DCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dd1 CJK Ideograph-6DD1 | 6dd0 CJK Ideograph-6DD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dd3 CJK Ideograph-6DD3 | 6dd2 CJK Ideograph-6DD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dd5 CJK Ideograph-6DD5 | 6dd4 CJK Ideograph-6DD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dd7 CJK Ideograph-6DD7 | 6dd6 CJK Ideograph-6DD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dd9 CJK Ideograph-6DD9 | 6dd8 CJK Ideograph-6DD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ddb CJK Ideograph-6DDB | 6dda CJK Ideograph-6DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ddd CJK Ideograph-6DDD | 6ddc CJK Ideograph-6DDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ddf CJK Ideograph-6DDF | 6dde CJK Ideograph-6DDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6de1 CJK Ideograph-6DE1 | 6de0 CJK Ideograph-6DE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6de3 CJK Ideograph-6DE3 | 6de2 CJK Ideograph-6DE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6de5 CJK Ideograph-6DE5 | 6de4 CJK Ideograph-6DE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6de7 CJK Ideograph-6DE7 | 6de6 CJK Ideograph-6DE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6de9 CJK Ideograph-6DE9 | 6de8 CJK Ideograph-6DE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6deb CJK Ideograph-6DEB | 6dea CJK Ideograph-6DEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ded CJK Ideograph-6DED | 6dec CJK Ideograph-6DEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6def CJK Ideograph-6DEF | 6dee CJK Ideograph-6DEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6df1 CJK Ideograph-6DF1 | 6df0 CJK Ideograph-6DF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6df3 CJK Ideograph-6DF3 | 6df2 CJK Ideograph-6DF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6df5 CJK Ideograph-6DF5 | 6df4 CJK Ideograph-6DF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6df7 CJK Ideograph-6DF7 | 6df6 CJK Ideograph-6DF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6df9 CJK Ideograph-6DF9 | 6df8 CJK Ideograph-6DF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dfb CJK Ideograph-6DFB | 6dfa CJK Ideograph-6DFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dfd CJK Ideograph-6DFD | 6dfc CJK Ideograph-6DFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dff CJK Ideograph-6DFF | 6dfe CJK Ideograph-6DFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e01 CJK Ideograph-6E01 | 6e00 CJK Ideograph-6E00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e03 CJK Ideograph-6E03 | 6e02 CJK Ideograph-6E02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e05 CJK Ideograph-6E05 | 6e04 CJK Ideograph-6E04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e07 CJK Ideograph-6E07 | 6e06 CJK Ideograph-6E06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e09 CJK Ideograph-6E09 | 6e08 CJK Ideograph-6E08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e0b CJK Ideograph-6E0B | 6e0a CJK Ideograph-6E0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e0d CJK Ideograph-6E0D | 6e0c CJK Ideograph-6E0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e0f CJK Ideograph-6E0F | 6e0e CJK Ideograph-6E0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e11 CJK Ideograph-6E11 | 6e10 CJK Ideograph-6E10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e13 CJK Ideograph-6E13 | 6e12 CJK Ideograph-6E12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e15 CJK Ideograph-6E15 | 6e14 CJK Ideograph-6E14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e17 CJK Ideograph-6E17 | 6e16 CJK Ideograph-6E16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e19 CJK Ideograph-6E19 | 6e18 CJK Ideograph-6E18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e1b CJK Ideograph-6E1B | 6e1a CJK Ideograph-6E1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e1d CJK Ideograph-6E1D | 6e1c CJK Ideograph-6E1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e1f CJK Ideograph-6E1F | 6e1e CJK Ideograph-6E1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e21 CJK Ideograph-6E21 | 6e20 CJK Ideograph-6E20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e23 CJK Ideograph-6E23 | 6e22 CJK Ideograph-6E22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e25 CJK Ideograph-6E25 | 6e24 CJK Ideograph-6E24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e27 CJK Ideograph-6E27 | 6e26 CJK Ideograph-6E26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e29 CJK Ideograph-6E29 | 6e28 CJK Ideograph-6E28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e2b CJK Ideograph-6E2B | 6e2a CJK Ideograph-6E2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e2d CJK Ideograph-6E2D | 6e2c CJK Ideograph-6E2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e2f CJK Ideograph-6E2F | 6e2e CJK Ideograph-6E2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e31 CJK Ideograph-6E31 | 6e30 CJK Ideograph-6E30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e33 CJK Ideograph-6E33 | 6e32 CJK Ideograph-6E32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e35 CJK Ideograph-6E35 | 6e34 CJK Ideograph-6E34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e37 CJK Ideograph-6E37 | 6e36 CJK Ideograph-6E36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e39 CJK Ideograph-6E39 | 6e38 CJK Ideograph-6E38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e3b CJK Ideograph-6E3B | 6e3a CJK Ideograph-6E3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e3d CJK Ideograph-6E3D | 6e3c CJK Ideograph-6E3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e3f CJK Ideograph-6E3F | 6e3e CJK Ideograph-6E3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e41 CJK Ideograph-6E41 | 6e40 CJK Ideograph-6E40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e43 CJK Ideograph-6E43 | 6e42 CJK Ideograph-6E42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e45 CJK Ideograph-6E45 | 6e44 CJK Ideograph-6E44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e47 CJK Ideograph-6E47 | 6e46 CJK Ideograph-6E46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e49 CJK Ideograph-6E49 | 6e48 CJK Ideograph-6E48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e4b CJK Ideograph-6E4B | 6e4a CJK Ideograph-6E4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e4d CJK Ideograph-6E4D | 6e4c CJK Ideograph-6E4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e4f CJK Ideograph-6E4F | 6e4e CJK Ideograph-6E4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e51 CJK Ideograph-6E51 | 6e50 CJK Ideograph-6E50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e53 CJK Ideograph-6E53 | 6e52 CJK Ideograph-6E52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e55 CJK Ideograph-6E55 | 6e54 CJK Ideograph-6E54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e57 CJK Ideograph-6E57 | 6e56 CJK Ideograph-6E56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e59 CJK Ideograph-6E59 | 6e58 CJK Ideograph-6E58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e5b CJK Ideograph-6E5B | 6e5a CJK Ideograph-6E5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e5d CJK Ideograph-6E5D | 6e5c CJK Ideograph-6E5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e5f CJK Ideograph-6E5F | 6e5e CJK Ideograph-6E5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e61 CJK Ideograph-6E61 | 6e60 CJK Ideograph-6E60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e63 CJK Ideograph-6E63 | 6e62 CJK Ideograph-6E62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e65 CJK Ideograph-6E65 | 6e64 CJK Ideograph-6E64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e67 CJK Ideograph-6E67 | 6e66 CJK Ideograph-6E66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e69 CJK Ideograph-6E69 | 6e68 CJK Ideograph-6E68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e6b CJK Ideograph-6E6B | 6e6a CJK Ideograph-6E6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e6d CJK Ideograph-6E6D | 6e6c CJK Ideograph-6E6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e6f CJK Ideograph-6E6F | 6e6e CJK Ideograph-6E6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e71 CJK Ideograph-6E71 | 6e70 CJK Ideograph-6E70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e73 CJK Ideograph-6E73 | 6e72 CJK Ideograph-6E72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e75 CJK Ideograph-6E75 | 6e74 CJK Ideograph-6E74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e77 CJK Ideograph-6E77 | 6e76 CJK Ideograph-6E76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e79 CJK Ideograph-6E79 | 6e78 CJK Ideograph-6E78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e7b CJK Ideograph-6E7B | 6e7a CJK Ideograph-6E7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e7d CJK Ideograph-6E7D | 6e7c CJK Ideograph-6E7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e7f CJK Ideograph-6E7F | 6e7e CJK Ideograph-6E7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e81 CJK Ideograph-6E81 | 6e80 CJK Ideograph-6E80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e83 CJK Ideograph-6E83 | 6e82 CJK Ideograph-6E82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e85 CJK Ideograph-6E85 | 6e84 CJK Ideograph-6E84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e87 CJK Ideograph-6E87 | 6e86 CJK Ideograph-6E86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e89 CJK Ideograph-6E89 | 6e88 CJK Ideograph-6E88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e8b CJK Ideograph-6E8B | 6e8a CJK Ideograph-6E8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e8d CJK Ideograph-6E8D | 6e8c CJK Ideograph-6E8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e8f CJK Ideograph-6E8F | 6e8e CJK Ideograph-6E8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e91 CJK Ideograph-6E91 | 6e90 CJK Ideograph-6E90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e93 CJK Ideograph-6E93 | 6e92 CJK Ideograph-6E92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e95 CJK Ideograph-6E95 | 6e94 CJK Ideograph-6E94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e97 CJK Ideograph-6E97 | 6e96 CJK Ideograph-6E96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e99 CJK Ideograph-6E99 | 6e98 CJK Ideograph-6E98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e9b CJK Ideograph-6E9B | 6e9a CJK Ideograph-6E9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e9d CJK Ideograph-6E9D | 6e9c CJK Ideograph-6E9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e9f CJK Ideograph-6E9F | 6e9e CJK Ideograph-6E9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ea1 CJK Ideograph-6EA1 | 6ea0 CJK Ideograph-6EA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ea3 CJK Ideograph-6EA3 | 6ea2 CJK Ideograph-6EA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ea5 CJK Ideograph-6EA5 | 6ea4 CJK Ideograph-6EA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ea7 CJK Ideograph-6EA7 | 6ea6 CJK Ideograph-6EA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ea9 CJK Ideograph-6EA9 | 6ea8 CJK Ideograph-6EA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eab CJK Ideograph-6EAB | 6eaa CJK Ideograph-6EAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ead CJK Ideograph-6EAD | 6eac CJK Ideograph-6EAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eaf CJK Ideograph-6EAF | 6eae CJK Ideograph-6EAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eb1 CJK Ideograph-6EB1 | 6eb0 CJK Ideograph-6EB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eb3 CJK Ideograph-6EB3 | 6eb2 CJK Ideograph-6EB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eb5 CJK Ideograph-6EB5 | 6eb4 CJK Ideograph-6EB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eb7 CJK Ideograph-6EB7 | 6eb6 CJK Ideograph-6EB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eb9 CJK Ideograph-6EB9 | 6eb8 CJK Ideograph-6EB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ebb CJK Ideograph-6EBB | 6eba CJK Ideograph-6EBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ebd CJK Ideograph-6EBD | 6ebc CJK Ideograph-6EBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ebf CJK Ideograph-6EBF | 6ebe CJK Ideograph-6EBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ec1 CJK Ideograph-6EC1 | 6ec0 CJK Ideograph-6EC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ec3 CJK Ideograph-6EC3 | 6ec2 CJK Ideograph-6EC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ec5 CJK Ideograph-6EC5 | 6ec4 CJK Ideograph-6EC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ec7 CJK Ideograph-6EC7 | 6ec6 CJK Ideograph-6EC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ec9 CJK Ideograph-6EC9 | 6ec8 CJK Ideograph-6EC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ecb CJK Ideograph-6ECB | 6eca CJK Ideograph-6ECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ecd CJK Ideograph-6ECD | 6ecc CJK Ideograph-6ECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ecf CJK Ideograph-6ECF | 6ece CJK Ideograph-6ECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ed1 CJK Ideograph-6ED1 | 6ed0 CJK Ideograph-6ED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ed3 CJK Ideograph-6ED3 | 6ed2 CJK Ideograph-6ED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ed5 CJK Ideograph-6ED5 | 6ed4 CJK Ideograph-6ED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ed7 CJK Ideograph-6ED7 | 6ed6 CJK Ideograph-6ED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ed9 CJK Ideograph-6ED9 | 6ed8 CJK Ideograph-6ED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6edb CJK Ideograph-6EDB | 6eda CJK Ideograph-6EDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6edd CJK Ideograph-6EDD | 6edc CJK Ideograph-6EDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6edf CJK Ideograph-6EDF | 6ede CJK Ideograph-6EDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ee1 CJK Ideograph-6EE1 | 6ee0 CJK Ideograph-6EE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ee3 CJK Ideograph-6EE3 | 6ee2 CJK Ideograph-6EE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ee5 CJK Ideograph-6EE5 | 6ee4 CJK Ideograph-6EE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ee7 CJK Ideograph-6EE7 | 6ee6 CJK Ideograph-6EE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ee9 CJK Ideograph-6EE9 | 6ee8 CJK Ideograph-6EE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eeb CJK Ideograph-6EEB | 6eea CJK Ideograph-6EEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eed CJK Ideograph-6EED | 6eec CJK Ideograph-6EEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eef CJK Ideograph-6EEF | 6eee CJK Ideograph-6EEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ef1 CJK Ideograph-6EF1 | 6ef0 CJK Ideograph-6EF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ef3 CJK Ideograph-6EF3 | 6ef2 CJK Ideograph-6EF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ef5 CJK Ideograph-6EF5 | 6ef4 CJK Ideograph-6EF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ef7 CJK Ideograph-6EF7 | 6ef6 CJK Ideograph-6EF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ef9 CJK Ideograph-6EF9 | 6ef8 CJK Ideograph-6EF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6efb CJK Ideograph-6EFB | 6efa CJK Ideograph-6EFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6efd CJK Ideograph-6EFD | 6efc CJK Ideograph-6EFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eff CJK Ideograph-6EFF | 6efe CJK Ideograph-6EFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f01 CJK Ideograph-6F01 | 6f00 CJK Ideograph-6F00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f03 CJK Ideograph-6F03 | 6f02 CJK Ideograph-6F02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f05 CJK Ideograph-6F05 | 6f04 CJK Ideograph-6F04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f07 CJK Ideograph-6F07 | 6f06 CJK Ideograph-6F06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f09 CJK Ideograph-6F09 | 6f08 CJK Ideograph-6F08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f0b CJK Ideograph-6F0B | 6f0a CJK Ideograph-6F0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f0d CJK Ideograph-6F0D | 6f0c CJK Ideograph-6F0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f0f CJK Ideograph-6F0F | 6f0e CJK Ideograph-6F0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f11 CJK Ideograph-6F11 | 6f10 CJK Ideograph-6F10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f13 CJK Ideograph-6F13 | 6f12 CJK Ideograph-6F12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f15 CJK Ideograph-6F15 | 6f14 CJK Ideograph-6F14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f17 CJK Ideograph-6F17 | 6f16 CJK Ideograph-6F16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f19 CJK Ideograph-6F19 | 6f18 CJK Ideograph-6F18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f1b CJK Ideograph-6F1B | 6f1a CJK Ideograph-6F1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f1d CJK Ideograph-6F1D | 6f1c CJK Ideograph-6F1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f1f CJK Ideograph-6F1F | 6f1e CJK Ideograph-6F1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f21 CJK Ideograph-6F21 | 6f20 CJK Ideograph-6F20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f23 CJK Ideograph-6F23 | 6f22 CJK Ideograph-6F22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f25 CJK Ideograph-6F25 | 6f24 CJK Ideograph-6F24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f27 CJK Ideograph-6F27 | 6f26 CJK Ideograph-6F26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f29 CJK Ideograph-6F29 | 6f28 CJK Ideograph-6F28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f2b CJK Ideograph-6F2B | 6f2a CJK Ideograph-6F2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f2d CJK Ideograph-6F2D | 6f2c CJK Ideograph-6F2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f2f CJK Ideograph-6F2F | 6f2e CJK Ideograph-6F2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f31 CJK Ideograph-6F31 | 6f30 CJK Ideograph-6F30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f33 CJK Ideograph-6F33 | 6f32 CJK Ideograph-6F32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f35 CJK Ideograph-6F35 | 6f34 CJK Ideograph-6F34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f37 CJK Ideograph-6F37 | 6f36 CJK Ideograph-6F36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f39 CJK Ideograph-6F39 | 6f38 CJK Ideograph-6F38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f3b CJK Ideograph-6F3B | 6f3a CJK Ideograph-6F3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f3d CJK Ideograph-6F3D | 6f3c CJK Ideograph-6F3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f3f CJK Ideograph-6F3F | 6f3e CJK Ideograph-6F3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f41 CJK Ideograph-6F41 | 6f40 CJK Ideograph-6F40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f43 CJK Ideograph-6F43 | 6f42 CJK Ideograph-6F42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f45 CJK Ideograph-6F45 | 6f44 CJK Ideograph-6F44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f47 CJK Ideograph-6F47 | 6f46 CJK Ideograph-6F46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f49 CJK Ideograph-6F49 | 6f48 CJK Ideograph-6F48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f4b CJK Ideograph-6F4B | 6f4a CJK Ideograph-6F4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f4d CJK Ideograph-6F4D | 6f4c CJK Ideograph-6F4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f4f CJK Ideograph-6F4F | 6f4e CJK Ideograph-6F4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f51 CJK Ideograph-6F51 | 6f50 CJK Ideograph-6F50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f53 CJK Ideograph-6F53 | 6f52 CJK Ideograph-6F52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f55 CJK Ideograph-6F55 | 6f54 CJK Ideograph-6F54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f57 CJK Ideograph-6F57 | 6f56 CJK Ideograph-6F56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f59 CJK Ideograph-6F59 | 6f58 CJK Ideograph-6F58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f5b CJK Ideograph-6F5B | 6f5a CJK Ideograph-6F5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f5d CJK Ideograph-6F5D | 6f5c CJK Ideograph-6F5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f5f CJK Ideograph-6F5F | 6f5e CJK Ideograph-6F5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f61 CJK Ideograph-6F61 | 6f60 CJK Ideograph-6F60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f63 CJK Ideograph-6F63 | 6f62 CJK Ideograph-6F62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f65 CJK Ideograph-6F65 | 6f64 CJK Ideograph-6F64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f67 CJK Ideograph-6F67 | 6f66 CJK Ideograph-6F66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f69 CJK Ideograph-6F69 | 6f68 CJK Ideograph-6F68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f6b CJK Ideograph-6F6B | 6f6a CJK Ideograph-6F6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f6d CJK Ideograph-6F6D | 6f6c CJK Ideograph-6F6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f6f CJK Ideograph-6F6F | 6f6e CJK Ideograph-6F6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f71 CJK Ideograph-6F71 | 6f70 CJK Ideograph-6F70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f73 CJK Ideograph-6F73 | 6f72 CJK Ideograph-6F72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f75 CJK Ideograph-6F75 | 6f74 CJK Ideograph-6F74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f77 CJK Ideograph-6F77 | 6f76 CJK Ideograph-6F76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f79 CJK Ideograph-6F79 | 6f78 CJK Ideograph-6F78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f7b CJK Ideograph-6F7B | 6f7a CJK Ideograph-6F7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f7d CJK Ideograph-6F7D | 6f7c CJK Ideograph-6F7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f7f CJK Ideograph-6F7F | 6f7e CJK Ideograph-6F7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f81 CJK Ideograph-6F81 | 6f80 CJK Ideograph-6F80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f83 CJK Ideograph-6F83 | 6f82 CJK Ideograph-6F82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f85 CJK Ideograph-6F85 | 6f84 CJK Ideograph-6F84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f87 CJK Ideograph-6F87 | 6f86 CJK Ideograph-6F86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f89 CJK Ideograph-6F89 | 6f88 CJK Ideograph-6F88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f8b CJK Ideograph-6F8B | 6f8a CJK Ideograph-6F8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f8d CJK Ideograph-6F8D | 6f8c CJK Ideograph-6F8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f8f CJK Ideograph-6F8F | 6f8e CJK Ideograph-6F8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f91 CJK Ideograph-6F91 | 6f90 CJK Ideograph-6F90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f93 CJK Ideograph-6F93 | 6f92 CJK Ideograph-6F92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f95 CJK Ideograph-6F95 | 6f94 CJK Ideograph-6F94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f97 CJK Ideograph-6F97 | 6f96 CJK Ideograph-6F96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f99 CJK Ideograph-6F99 | 6f98 CJK Ideograph-6F98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f9b CJK Ideograph-6F9B | 6f9a CJK Ideograph-6F9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f9d CJK Ideograph-6F9D | 6f9c CJK Ideograph-6F9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f9f CJK Ideograph-6F9F | 6f9e CJK Ideograph-6F9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fa1 CJK Ideograph-6FA1 | 6fa0 CJK Ideograph-6FA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fa3 CJK Ideograph-6FA3 | 6fa2 CJK Ideograph-6FA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fa5 CJK Ideograph-6FA5 | 6fa4 CJK Ideograph-6FA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fa7 CJK Ideograph-6FA7 | 6fa6 CJK Ideograph-6FA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fa9 CJK Ideograph-6FA9 | 6fa8 CJK Ideograph-6FA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fab CJK Ideograph-6FAB | 6faa CJK Ideograph-6FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fad CJK Ideograph-6FAD | 6fac CJK Ideograph-6FAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6faf CJK Ideograph-6FAF | 6fae CJK Ideograph-6FAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fb1 CJK Ideograph-6FB1 | 6fb0 CJK Ideograph-6FB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fb3 CJK Ideograph-6FB3 | 6fb2 CJK Ideograph-6FB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fb5 CJK Ideograph-6FB5 | 6fb4 CJK Ideograph-6FB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fb7 CJK Ideograph-6FB7 | 6fb6 CJK Ideograph-6FB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fb9 CJK Ideograph-6FB9 | 6fb8 CJK Ideograph-6FB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fbb CJK Ideograph-6FBB | 6fba CJK Ideograph-6FBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fbd CJK Ideograph-6FBD | 6fbc CJK Ideograph-6FBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fbf CJK Ideograph-6FBF | 6fbe CJK Ideograph-6FBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fc1 CJK Ideograph-6FC1 | 6fc0 CJK Ideograph-6FC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fc3 CJK Ideograph-6FC3 | 6fc2 CJK Ideograph-6FC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fc5 CJK Ideograph-6FC5 | 6fc4 CJK Ideograph-6FC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fc7 CJK Ideograph-6FC7 | 6fc6 CJK Ideograph-6FC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fc9 CJK Ideograph-6FC9 | 6fc8 CJK Ideograph-6FC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fcb CJK Ideograph-6FCB | 6fca CJK Ideograph-6FCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fcd CJK Ideograph-6FCD | 6fcc CJK Ideograph-6FCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fcf CJK Ideograph-6FCF | 6fce CJK Ideograph-6FCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fd1 CJK Ideograph-6FD1 | 6fd0 CJK Ideograph-6FD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fd3 CJK Ideograph-6FD3 | 6fd2 CJK Ideograph-6FD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fd5 CJK Ideograph-6FD5 | 6fd4 CJK Ideograph-6FD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fd7 CJK Ideograph-6FD7 | 6fd6 CJK Ideograph-6FD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fd9 CJK Ideograph-6FD9 | 6fd8 CJK Ideograph-6FD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fdb CJK Ideograph-6FDB | 6fda CJK Ideograph-6FDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fdd CJK Ideograph-6FDD | 6fdc CJK Ideograph-6FDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fdf CJK Ideograph-6FDF | 6fde CJK Ideograph-6FDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fe1 CJK Ideograph-6FE1 | 6fe0 CJK Ideograph-6FE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fe3 CJK Ideograph-6FE3 | 6fe2 CJK Ideograph-6FE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fe5 CJK Ideograph-6FE5 | 6fe4 CJK Ideograph-6FE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fe7 CJK Ideograph-6FE7 | 6fe6 CJK Ideograph-6FE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fe9 CJK Ideograph-6FE9 | 6fe8 CJK Ideograph-6FE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6feb CJK Ideograph-6FEB | 6fea CJK Ideograph-6FEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fed CJK Ideograph-6FED | 6fec CJK Ideograph-6FEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fef CJK Ideograph-6FEF | 6fee CJK Ideograph-6FEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ff1 CJK Ideograph-6FF1 | 6ff0 CJK Ideograph-6FF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ff3 CJK Ideograph-6FF3 | 6ff2 CJK Ideograph-6FF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ff5 CJK Ideograph-6FF5 | 6ff4 CJK Ideograph-6FF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ff7 CJK Ideograph-6FF7 | 6ff6 CJK Ideograph-6FF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ff9 CJK Ideograph-6FF9 | 6ff8 CJK Ideograph-6FF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ffb CJK Ideograph-6FFB | 6ffa CJK Ideograph-6FFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ffd CJK Ideograph-6FFD | 6ffc CJK Ideograph-6FFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fff CJK Ideograph-6FFF | 6ffe CJK Ideograph-6FFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7001 CJK Ideograph-7001 | 7000 CJK Ideograph-7000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7003 CJK Ideograph-7003 | 7002 CJK Ideograph-7002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7005 CJK Ideograph-7005 | 7004 CJK Ideograph-7004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7007 CJK Ideograph-7007 | 7006 CJK Ideograph-7006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7009 CJK Ideograph-7009 | 7008 CJK Ideograph-7008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 700b CJK Ideograph-700B | 700a CJK Ideograph-700A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 700d CJK Ideograph-700D | 700c CJK Ideograph-700C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 700f CJK Ideograph-700F | 700e CJK Ideograph-700E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7011 CJK Ideograph-7011 | 7010 CJK Ideograph-7010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7013 CJK Ideograph-7013 | 7012 CJK Ideograph-7012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7015 CJK Ideograph-7015 | 7014 CJK Ideograph-7014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7017 CJK Ideograph-7017 | 7016 CJK Ideograph-7016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7019 CJK Ideograph-7019 | 7018 CJK Ideograph-7018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 701b CJK Ideograph-701B | 701a CJK Ideograph-701A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 701d CJK Ideograph-701D | 701c CJK Ideograph-701C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 701f CJK Ideograph-701F | 701e CJK Ideograph-701E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7021 CJK Ideograph-7021 | 7020 CJK Ideograph-7020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7023 CJK Ideograph-7023 | 7022 CJK Ideograph-7022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7025 CJK Ideograph-7025 | 7024 CJK Ideograph-7024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7027 CJK Ideograph-7027 | 7026 CJK Ideograph-7026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7029 CJK Ideograph-7029 | 7028 CJK Ideograph-7028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 702b CJK Ideograph-702B | 702a CJK Ideograph-702A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 702d CJK Ideograph-702D | 702c CJK Ideograph-702C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 702f CJK Ideograph-702F | 702e CJK Ideograph-702E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7031 CJK Ideograph-7031 | 7030 CJK Ideograph-7030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7033 CJK Ideograph-7033 | 7032 CJK Ideograph-7032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7035 CJK Ideograph-7035 | 7034 CJK Ideograph-7034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7037 CJK Ideograph-7037 | 7036 CJK Ideograph-7036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7039 CJK Ideograph-7039 | 7038 CJK Ideograph-7038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 703b CJK Ideograph-703B | 703a CJK Ideograph-703A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 703d CJK Ideograph-703D | 703c CJK Ideograph-703C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 703f CJK Ideograph-703F | 703e CJK Ideograph-703E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7041 CJK Ideograph-7041 | 7040 CJK Ideograph-7040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7043 CJK Ideograph-7043 | 7042 CJK Ideograph-7042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7045 CJK Ideograph-7045 | 7044 CJK Ideograph-7044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7047 CJK Ideograph-7047 | 7046 CJK Ideograph-7046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7049 CJK Ideograph-7049 | 7048 CJK Ideograph-7048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 704b CJK Ideograph-704B | 704a CJK Ideograph-704A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 704d CJK Ideograph-704D | 704c CJK Ideograph-704C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 704f CJK Ideograph-704F | 704e CJK Ideograph-704E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7051 CJK Ideograph-7051 | 7050 CJK Ideograph-7050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7053 CJK Ideograph-7053 | 7052 CJK Ideograph-7052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7055 CJK Ideograph-7055 | 7054 CJK Ideograph-7054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7057 CJK Ideograph-7057 | 7056 CJK Ideograph-7056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7059 CJK Ideograph-7059 | 7058 CJK Ideograph-7058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 705b CJK Ideograph-705B | 705a CJK Ideograph-705A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 705d CJK Ideograph-705D | 705c CJK Ideograph-705C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 705f CJK Ideograph-705F | 705e CJK Ideograph-705E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7061 CJK Ideograph-7061 | 7060 CJK Ideograph-7060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7063 CJK Ideograph-7063 | 7062 CJK Ideograph-7062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7065 CJK Ideograph-7065 | 7064 CJK Ideograph-7064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7067 CJK Ideograph-7067 | 7066 CJK Ideograph-7066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7069 CJK Ideograph-7069 | 7068 CJK Ideograph-7068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 706b CJK Ideograph-706B | 706a CJK Ideograph-706A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 706d CJK Ideograph-706D | 706c CJK Ideograph-706C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 706f CJK Ideograph-706F | 706e CJK Ideograph-706E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7071 CJK Ideograph-7071 | 7070 CJK Ideograph-7070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7073 CJK Ideograph-7073 | 7072 CJK Ideograph-7072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7075 CJK Ideograph-7075 | 7074 CJK Ideograph-7074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7077 CJK Ideograph-7077 | 7076 CJK Ideograph-7076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7079 CJK Ideograph-7079 | 7078 CJK Ideograph-7078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 707b CJK Ideograph-707B | 707a CJK Ideograph-707A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 707d CJK Ideograph-707D | 707c CJK Ideograph-707C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 707f CJK Ideograph-707F | 707e CJK Ideograph-707E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7081 CJK Ideograph-7081 | 7080 CJK Ideograph-7080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7083 CJK Ideograph-7083 | 7082 CJK Ideograph-7082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7085 CJK Ideograph-7085 | 7084 CJK Ideograph-7084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7087 CJK Ideograph-7087 | 7086 CJK Ideograph-7086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7089 CJK Ideograph-7089 | 7088 CJK Ideograph-7088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 708b CJK Ideograph-708B | 708a CJK Ideograph-708A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 708d CJK Ideograph-708D | 708c CJK Ideograph-708C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 708f CJK Ideograph-708F | 708e CJK Ideograph-708E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7091 CJK Ideograph-7091 | 7090 CJK Ideograph-7090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7093 CJK Ideograph-7093 | 7092 CJK Ideograph-7092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7095 CJK Ideograph-7095 | 7094 CJK Ideograph-7094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7097 CJK Ideograph-7097 | 7096 CJK Ideograph-7096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7099 CJK Ideograph-7099 | 7098 CJK Ideograph-7098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 709b CJK Ideograph-709B | 709a CJK Ideograph-709A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 709d CJK Ideograph-709D | 709c CJK Ideograph-709C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 709f CJK Ideograph-709F | 709e CJK Ideograph-709E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70a1 CJK Ideograph-70A1 | 70a0 CJK Ideograph-70A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70a3 CJK Ideograph-70A3 | 70a2 CJK Ideograph-70A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70a5 CJK Ideograph-70A5 | 70a4 CJK Ideograph-70A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70a7 CJK Ideograph-70A7 | 70a6 CJK Ideograph-70A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70a9 CJK Ideograph-70A9 | 70a8 CJK Ideograph-70A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70ab CJK Ideograph-70AB | 70aa CJK Ideograph-70AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70ad CJK Ideograph-70AD | 70ac CJK Ideograph-70AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70af CJK Ideograph-70AF | 70ae CJK Ideograph-70AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70b1 CJK Ideograph-70B1 | 70b0 CJK Ideograph-70B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70b3 CJK Ideograph-70B3 | 70b2 CJK Ideograph-70B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70b5 CJK Ideograph-70B5 | 70b4 CJK Ideograph-70B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70b7 CJK Ideograph-70B7 | 70b6 CJK Ideograph-70B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70b9 CJK Ideograph-70B9 | 70b8 CJK Ideograph-70B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70bb CJK Ideograph-70BB | 70ba CJK Ideograph-70BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70bd CJK Ideograph-70BD | 70bc CJK Ideograph-70BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70bf CJK Ideograph-70BF | 70be CJK Ideograph-70BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70c1 CJK Ideograph-70C1 | 70c0 CJK Ideograph-70C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70c3 CJK Ideograph-70C3 | 70c2 CJK Ideograph-70C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70c5 CJK Ideograph-70C5 | 70c4 CJK Ideograph-70C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70c7 CJK Ideograph-70C7 | 70c6 CJK Ideograph-70C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70c9 CJK Ideograph-70C9 | 70c8 CJK Ideograph-70C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70cb CJK Ideograph-70CB | 70ca CJK Ideograph-70CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70cd CJK Ideograph-70CD | 70cc CJK Ideograph-70CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70cf CJK Ideograph-70CF | 70ce CJK Ideograph-70CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70d1 CJK Ideograph-70D1 | 70d0 CJK Ideograph-70D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70d3 CJK Ideograph-70D3 | 70d2 CJK Ideograph-70D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70d5 CJK Ideograph-70D5 | 70d4 CJK Ideograph-70D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70d7 CJK Ideograph-70D7 | 70d6 CJK Ideograph-70D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70d9 CJK Ideograph-70D9 | 70d8 CJK Ideograph-70D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70db CJK Ideograph-70DB | 70da CJK Ideograph-70DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70dd CJK Ideograph-70DD | 70dc CJK Ideograph-70DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70df CJK Ideograph-70DF | 70de CJK Ideograph-70DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70e1 CJK Ideograph-70E1 | 70e0 CJK Ideograph-70E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70e3 CJK Ideograph-70E3 | 70e2 CJK Ideograph-70E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70e5 CJK Ideograph-70E5 | 70e4 CJK Ideograph-70E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70e7 CJK Ideograph-70E7 | 70e6 CJK Ideograph-70E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70e9 CJK Ideograph-70E9 | 70e8 CJK Ideograph-70E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70eb CJK Ideograph-70EB | 70ea CJK Ideograph-70EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70ed CJK Ideograph-70ED | 70ec CJK Ideograph-70EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70ef CJK Ideograph-70EF | 70ee CJK Ideograph-70EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70f1 CJK Ideograph-70F1 | 70f0 CJK Ideograph-70F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70f3 CJK Ideograph-70F3 | 70f2 CJK Ideograph-70F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70f5 CJK Ideograph-70F5 | 70f4 CJK Ideograph-70F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70f7 CJK Ideograph-70F7 | 70f6 CJK Ideograph-70F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70f9 CJK Ideograph-70F9 | 70f8 CJK Ideograph-70F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70fb CJK Ideograph-70FB | 70fa CJK Ideograph-70FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70fd CJK Ideograph-70FD | 70fc CJK Ideograph-70FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70ff CJK Ideograph-70FF | 70fe CJK Ideograph-70FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7101 CJK Ideograph-7101 | 7100 CJK Ideograph-7100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7103 CJK Ideograph-7103 | 7102 CJK Ideograph-7102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7105 CJK Ideograph-7105 | 7104 CJK Ideograph-7104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7107 CJK Ideograph-7107 | 7106 CJK Ideograph-7106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7109 CJK Ideograph-7109 | 7108 CJK Ideograph-7108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 710b CJK Ideograph-710B | 710a CJK Ideograph-710A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 710d CJK Ideograph-710D | 710c CJK Ideograph-710C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 710f CJK Ideograph-710F | 710e CJK Ideograph-710E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7111 CJK Ideograph-7111 | 7110 CJK Ideograph-7110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7113 CJK Ideograph-7113 | 7112 CJK Ideograph-7112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7115 CJK Ideograph-7115 | 7114 CJK Ideograph-7114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7117 CJK Ideograph-7117 | 7116 CJK Ideograph-7116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7119 CJK Ideograph-7119 | 7118 CJK Ideograph-7118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 711b CJK Ideograph-711B | 711a CJK Ideograph-711A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 711d CJK Ideograph-711D | 711c CJK Ideograph-711C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 711f CJK Ideograph-711F | 711e CJK Ideograph-711E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7121 CJK Ideograph-7121 | 7120 CJK Ideograph-7120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7123 CJK Ideograph-7123 | 7122 CJK Ideograph-7122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7125 CJK Ideograph-7125 | 7124 CJK Ideograph-7124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7127 CJK Ideograph-7127 | 7126 CJK Ideograph-7126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7129 CJK Ideograph-7129 | 7128 CJK Ideograph-7128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 712b CJK Ideograph-712B | 712a CJK Ideograph-712A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 712d CJK Ideograph-712D | 712c CJK Ideograph-712C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 712f CJK Ideograph-712F | 712e CJK Ideograph-712E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7131 CJK Ideograph-7131 | 7130 CJK Ideograph-7130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7133 CJK Ideograph-7133 | 7132 CJK Ideograph-7132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7135 CJK Ideograph-7135 | 7134 CJK Ideograph-7134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7137 CJK Ideograph-7137 | 7136 CJK Ideograph-7136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7139 CJK Ideograph-7139 | 7138 CJK Ideograph-7138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 713b CJK Ideograph-713B | 713a CJK Ideograph-713A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 713d CJK Ideograph-713D | 713c CJK Ideograph-713C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 713f CJK Ideograph-713F | 713e CJK Ideograph-713E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7141 CJK Ideograph-7141 | 7140 CJK Ideograph-7140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7143 CJK Ideograph-7143 | 7142 CJK Ideograph-7142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7145 CJK Ideograph-7145 | 7144 CJK Ideograph-7144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7147 CJK Ideograph-7147 | 7146 CJK Ideograph-7146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7149 CJK Ideograph-7149 | 7148 CJK Ideograph-7148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 714b CJK Ideograph-714B | 714a CJK Ideograph-714A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 714d CJK Ideograph-714D | 714c CJK Ideograph-714C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 714f CJK Ideograph-714F | 714e CJK Ideograph-714E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7151 CJK Ideograph-7151 | 7150 CJK Ideograph-7150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7153 CJK Ideograph-7153 | 7152 CJK Ideograph-7152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7155 CJK Ideograph-7155 | 7154 CJK Ideograph-7154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7157 CJK Ideograph-7157 | 7156 CJK Ideograph-7156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7159 CJK Ideograph-7159 | 7158 CJK Ideograph-7158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 715b CJK Ideograph-715B | 715a CJK Ideograph-715A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 715d CJK Ideograph-715D | 715c CJK Ideograph-715C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 715f CJK Ideograph-715F | 715e CJK Ideograph-715E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7161 CJK Ideograph-7161 | 7160 CJK Ideograph-7160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7163 CJK Ideograph-7163 | 7162 CJK Ideograph-7162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7165 CJK Ideograph-7165 | 7164 CJK Ideograph-7164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7167 CJK Ideograph-7167 | 7166 CJK Ideograph-7166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7169 CJK Ideograph-7169 | 7168 CJK Ideograph-7168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 716b CJK Ideograph-716B | 716a CJK Ideograph-716A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 716d CJK Ideograph-716D | 716c CJK Ideograph-716C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 716f CJK Ideograph-716F | 716e CJK Ideograph-716E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7171 CJK Ideograph-7171 | 7170 CJK Ideograph-7170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7173 CJK Ideograph-7173 | 7172 CJK Ideograph-7172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7175 CJK Ideograph-7175 | 7174 CJK Ideograph-7174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7177 CJK Ideograph-7177 | 7176 CJK Ideograph-7176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7179 CJK Ideograph-7179 | 7178 CJK Ideograph-7178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 717b CJK Ideograph-717B | 717a CJK Ideograph-717A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 717d CJK Ideograph-717D | 717c CJK Ideograph-717C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 717f CJK Ideograph-717F | 717e CJK Ideograph-717E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7181 CJK Ideograph-7181 | 7180 CJK Ideograph-7180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7183 CJK Ideograph-7183 | 7182 CJK Ideograph-7182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7185 CJK Ideograph-7185 | 7184 CJK Ideograph-7184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7187 CJK Ideograph-7187 | 7186 CJK Ideograph-7186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7189 CJK Ideograph-7189 | 7188 CJK Ideograph-7188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 718b CJK Ideograph-718B | 718a CJK Ideograph-718A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 718d CJK Ideograph-718D | 718c CJK Ideograph-718C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 718f CJK Ideograph-718F | 718e CJK Ideograph-718E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7191 CJK Ideograph-7191 | 7190 CJK Ideograph-7190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7193 CJK Ideograph-7193 | 7192 CJK Ideograph-7192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7195 CJK Ideograph-7195 | 7194 CJK Ideograph-7194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7197 CJK Ideograph-7197 | 7196 CJK Ideograph-7196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7199 CJK Ideograph-7199 | 7198 CJK Ideograph-7198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 719b CJK Ideograph-719B | 719a CJK Ideograph-719A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 719d CJK Ideograph-719D | 719c CJK Ideograph-719C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 719f CJK Ideograph-719F | 719e CJK Ideograph-719E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71a1 CJK Ideograph-71A1 | 71a0 CJK Ideograph-71A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71a3 CJK Ideograph-71A3 | 71a2 CJK Ideograph-71A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71a5 CJK Ideograph-71A5 | 71a4 CJK Ideograph-71A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71a7 CJK Ideograph-71A7 | 71a6 CJK Ideograph-71A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71a9 CJK Ideograph-71A9 | 71a8 CJK Ideograph-71A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71ab CJK Ideograph-71AB | 71aa CJK Ideograph-71AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71ad CJK Ideograph-71AD | 71ac CJK Ideograph-71AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71af CJK Ideograph-71AF | 71ae CJK Ideograph-71AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71b1 CJK Ideograph-71B1 | 71b0 CJK Ideograph-71B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71b3 CJK Ideograph-71B3 | 71b2 CJK Ideograph-71B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71b5 CJK Ideograph-71B5 | 71b4 CJK Ideograph-71B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71b7 CJK Ideograph-71B7 | 71b6 CJK Ideograph-71B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71b9 CJK Ideograph-71B9 | 71b8 CJK Ideograph-71B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71bb CJK Ideograph-71BB | 71ba CJK Ideograph-71BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71bd CJK Ideograph-71BD | 71bc CJK Ideograph-71BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71bf CJK Ideograph-71BF | 71be CJK Ideograph-71BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71c1 CJK Ideograph-71C1 | 71c0 CJK Ideograph-71C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71c3 CJK Ideograph-71C3 | 71c2 CJK Ideograph-71C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71c5 CJK Ideograph-71C5 | 71c4 CJK Ideograph-71C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71c7 CJK Ideograph-71C7 | 71c6 CJK Ideograph-71C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71c9 CJK Ideograph-71C9 | 71c8 CJK Ideograph-71C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71cb CJK Ideograph-71CB | 71ca CJK Ideograph-71CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71cd CJK Ideograph-71CD | 71cc CJK Ideograph-71CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71cf CJK Ideograph-71CF | 71ce CJK Ideograph-71CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71d1 CJK Ideograph-71D1 | 71d0 CJK Ideograph-71D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71d3 CJK Ideograph-71D3 | 71d2 CJK Ideograph-71D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71d5 CJK Ideograph-71D5 | 71d4 CJK Ideograph-71D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71d7 CJK Ideograph-71D7 | 71d6 CJK Ideograph-71D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71d9 CJK Ideograph-71D9 | 71d8 CJK Ideograph-71D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71db CJK Ideograph-71DB | 71da CJK Ideograph-71DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71dd CJK Ideograph-71DD | 71dc CJK Ideograph-71DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71df CJK Ideograph-71DF | 71de CJK Ideograph-71DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71e1 CJK Ideograph-71E1 | 71e0 CJK Ideograph-71E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71e3 CJK Ideograph-71E3 | 71e2 CJK Ideograph-71E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71e5 CJK Ideograph-71E5 | 71e4 CJK Ideograph-71E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71e7 CJK Ideograph-71E7 | 71e6 CJK Ideograph-71E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71e9 CJK Ideograph-71E9 | 71e8 CJK Ideograph-71E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71eb CJK Ideograph-71EB | 71ea CJK Ideograph-71EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71ed CJK Ideograph-71ED | 71ec CJK Ideograph-71EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71ef CJK Ideograph-71EF | 71ee CJK Ideograph-71EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71f1 CJK Ideograph-71F1 | 71f0 CJK Ideograph-71F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71f3 CJK Ideograph-71F3 | 71f2 CJK Ideograph-71F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71f5 CJK Ideograph-71F5 | 71f4 CJK Ideograph-71F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71f7 CJK Ideograph-71F7 | 71f6 CJK Ideograph-71F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71f9 CJK Ideograph-71F9 | 71f8 CJK Ideograph-71F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71fb CJK Ideograph-71FB | 71fa CJK Ideograph-71FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71fd CJK Ideograph-71FD | 71fc CJK Ideograph-71FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71ff CJK Ideograph-71FF | 71fe CJK Ideograph-71FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7201 CJK Ideograph-7201 | 7200 CJK Ideograph-7200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7203 CJK Ideograph-7203 | 7202 CJK Ideograph-7202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7205 CJK Ideograph-7205 | 7204 CJK Ideograph-7204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7207 CJK Ideograph-7207 | 7206 CJK Ideograph-7206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7209 CJK Ideograph-7209 | 7208 CJK Ideograph-7208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 720b CJK Ideograph-720B | 720a CJK Ideograph-720A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 720d CJK Ideograph-720D | 720c CJK Ideograph-720C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 720f CJK Ideograph-720F | 720e CJK Ideograph-720E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7211 CJK Ideograph-7211 | 7210 CJK Ideograph-7210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7213 CJK Ideograph-7213 | 7212 CJK Ideograph-7212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7215 CJK Ideograph-7215 | 7214 CJK Ideograph-7214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7217 CJK Ideograph-7217 | 7216 CJK Ideograph-7216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7219 CJK Ideograph-7219 | 7218 CJK Ideograph-7218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 721b CJK Ideograph-721B | 721a CJK Ideograph-721A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 721d CJK Ideograph-721D | 721c CJK Ideograph-721C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 721f CJK Ideograph-721F | 721e CJK Ideograph-721E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7221 CJK Ideograph-7221 | 7220 CJK Ideograph-7220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7223 CJK Ideograph-7223 | 7222 CJK Ideograph-7222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7225 CJK Ideograph-7225 | 7224 CJK Ideograph-7224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7227 CJK Ideograph-7227 | 7226 CJK Ideograph-7226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7229 CJK Ideograph-7229 | 7228 CJK Ideograph-7228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 722b CJK Ideograph-722B | 722a CJK Ideograph-722A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 722d CJK Ideograph-722D | 722c CJK Ideograph-722C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 722f CJK Ideograph-722F | 722e CJK Ideograph-722E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7231 CJK Ideograph-7231 | 7230 CJK Ideograph-7230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7233 CJK Ideograph-7233 | 7232 CJK Ideograph-7232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7235 CJK Ideograph-7235 | 7234 CJK Ideograph-7234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7237 CJK Ideograph-7237 | 7236 CJK Ideograph-7236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7239 CJK Ideograph-7239 | 7238 CJK Ideograph-7238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 723b CJK Ideograph-723B | 723a CJK Ideograph-723A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 723d CJK Ideograph-723D | 723c CJK Ideograph-723C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 723f CJK Ideograph-723F | 723e CJK Ideograph-723E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7241 CJK Ideograph-7241 | 7240 CJK Ideograph-7240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7243 CJK Ideograph-7243 | 7242 CJK Ideograph-7242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7245 CJK Ideograph-7245 | 7244 CJK Ideograph-7244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7247 CJK Ideograph-7247 | 7246 CJK Ideograph-7246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7249 CJK Ideograph-7249 | 7248 CJK Ideograph-7248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 724b CJK Ideograph-724B | 724a CJK Ideograph-724A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 724d CJK Ideograph-724D | 724c CJK Ideograph-724C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 724f CJK Ideograph-724F | 724e CJK Ideograph-724E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7251 CJK Ideograph-7251 | 7250 CJK Ideograph-7250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7253 CJK Ideograph-7253 | 7252 CJK Ideograph-7252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7255 CJK Ideograph-7255 | 7254 CJK Ideograph-7254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7257 CJK Ideograph-7257 | 7256 CJK Ideograph-7256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7259 CJK Ideograph-7259 | 7258 CJK Ideograph-7258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 725b CJK Ideograph-725B | 725a CJK Ideograph-725A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 725d CJK Ideograph-725D | 725c CJK Ideograph-725C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 725f CJK Ideograph-725F | 725e CJK Ideograph-725E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7261 CJK Ideograph-7261 | 7260 CJK Ideograph-7260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7263 CJK Ideograph-7263 | 7262 CJK Ideograph-7262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7265 CJK Ideograph-7265 | 7264 CJK Ideograph-7264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7267 CJK Ideograph-7267 | 7266 CJK Ideograph-7266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7269 CJK Ideograph-7269 | 7268 CJK Ideograph-7268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 726b CJK Ideograph-726B | 726a CJK Ideograph-726A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 726d CJK Ideograph-726D | 726c CJK Ideograph-726C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 726f CJK Ideograph-726F | 726e CJK Ideograph-726E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7271 CJK Ideograph-7271 | 7270 CJK Ideograph-7270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7273 CJK Ideograph-7273 | 7272 CJK Ideograph-7272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7275 CJK Ideograph-7275 | 7274 CJK Ideograph-7274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7277 CJK Ideograph-7277 | 7276 CJK Ideograph-7276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7279 CJK Ideograph-7279 | 7278 CJK Ideograph-7278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 727b CJK Ideograph-727B | 727a CJK Ideograph-727A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 727d CJK Ideograph-727D | 727c CJK Ideograph-727C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 727f CJK Ideograph-727F | 727e CJK Ideograph-727E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7281 CJK Ideograph-7281 | 7280 CJK Ideograph-7280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7283 CJK Ideograph-7283 | 7282 CJK Ideograph-7282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7285 CJK Ideograph-7285 | 7284 CJK Ideograph-7284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7287 CJK Ideograph-7287 | 7286 CJK Ideograph-7286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7289 CJK Ideograph-7289 | 7288 CJK Ideograph-7288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 728b CJK Ideograph-728B | 728a CJK Ideograph-728A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 728d CJK Ideograph-728D | 728c CJK Ideograph-728C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 728f CJK Ideograph-728F | 728e CJK Ideograph-728E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7291 CJK Ideograph-7291 | 7290 CJK Ideograph-7290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7293 CJK Ideograph-7293 | 7292 CJK Ideograph-7292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7295 CJK Ideograph-7295 | 7294 CJK Ideograph-7294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7297 CJK Ideograph-7297 | 7296 CJK Ideograph-7296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7299 CJK Ideograph-7299 | 7298 CJK Ideograph-7298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 729b CJK Ideograph-729B | 729a CJK Ideograph-729A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 729d CJK Ideograph-729D | 729c CJK Ideograph-729C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 729f CJK Ideograph-729F | 729e CJK Ideograph-729E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72a1 CJK Ideograph-72A1 | 72a0 CJK Ideograph-72A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72a3 CJK Ideograph-72A3 | 72a2 CJK Ideograph-72A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72a5 CJK Ideograph-72A5 | 72a4 CJK Ideograph-72A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72a7 CJK Ideograph-72A7 | 72a6 CJK Ideograph-72A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72a9 CJK Ideograph-72A9 | 72a8 CJK Ideograph-72A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72ab CJK Ideograph-72AB | 72aa CJK Ideograph-72AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72ad CJK Ideograph-72AD | 72ac CJK Ideograph-72AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72af CJK Ideograph-72AF | 72ae CJK Ideograph-72AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72b1 CJK Ideograph-72B1 | 72b0 CJK Ideograph-72B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72b3 CJK Ideograph-72B3 | 72b2 CJK Ideograph-72B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72b5 CJK Ideograph-72B5 | 72b4 CJK Ideograph-72B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72b7 CJK Ideograph-72B7 | 72b6 CJK Ideograph-72B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72b9 CJK Ideograph-72B9 | 72b8 CJK Ideograph-72B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72bb CJK Ideograph-72BB | 72ba CJK Ideograph-72BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72bd CJK Ideograph-72BD | 72bc CJK Ideograph-72BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72bf CJK Ideograph-72BF | 72be CJK Ideograph-72BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72c1 CJK Ideograph-72C1 | 72c0 CJK Ideograph-72C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72c3 CJK Ideograph-72C3 | 72c2 CJK Ideograph-72C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72c5 CJK Ideograph-72C5 | 72c4 CJK Ideograph-72C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72c7 CJK Ideograph-72C7 | 72c6 CJK Ideograph-72C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72c9 CJK Ideograph-72C9 | 72c8 CJK Ideograph-72C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72cb CJK Ideograph-72CB | 72ca CJK Ideograph-72CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72cd CJK Ideograph-72CD | 72cc CJK Ideograph-72CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72cf CJK Ideograph-72CF | 72ce CJK Ideograph-72CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72d1 CJK Ideograph-72D1 | 72d0 CJK Ideograph-72D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72d3 CJK Ideograph-72D3 | 72d2 CJK Ideograph-72D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72d5 CJK Ideograph-72D5 | 72d4 CJK Ideograph-72D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72d7 CJK Ideograph-72D7 | 72d6 CJK Ideograph-72D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72d9 CJK Ideograph-72D9 | 72d8 CJK Ideograph-72D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72db CJK Ideograph-72DB | 72da CJK Ideograph-72DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72dd CJK Ideograph-72DD | 72dc CJK Ideograph-72DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72df CJK Ideograph-72DF | 72de CJK Ideograph-72DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72e1 CJK Ideograph-72E1 | 72e0 CJK Ideograph-72E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72e3 CJK Ideograph-72E3 | 72e2 CJK Ideograph-72E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72e5 CJK Ideograph-72E5 | 72e4 CJK Ideograph-72E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72e7 CJK Ideograph-72E7 | 72e6 CJK Ideograph-72E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72e9 CJK Ideograph-72E9 | 72e8 CJK Ideograph-72E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72eb CJK Ideograph-72EB | 72ea CJK Ideograph-72EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72ed CJK Ideograph-72ED | 72ec CJK Ideograph-72EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72ef CJK Ideograph-72EF | 72ee CJK Ideograph-72EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72f1 CJK Ideograph-72F1 | 72f0 CJK Ideograph-72F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72f3 CJK Ideograph-72F3 | 72f2 CJK Ideograph-72F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72f5 CJK Ideograph-72F5 | 72f4 CJK Ideograph-72F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72f7 CJK Ideograph-72F7 | 72f6 CJK Ideograph-72F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72f9 CJK Ideograph-72F9 | 72f8 CJK Ideograph-72F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72fb CJK Ideograph-72FB | 72fa CJK Ideograph-72FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72fd CJK Ideograph-72FD | 72fc CJK Ideograph-72FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72ff CJK Ideograph-72FF | 72fe CJK Ideograph-72FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7301 CJK Ideograph-7301 | 7300 CJK Ideograph-7300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7303 CJK Ideograph-7303 | 7302 CJK Ideograph-7302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7305 CJK Ideograph-7305 | 7304 CJK Ideograph-7304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7307 CJK Ideograph-7307 | 7306 CJK Ideograph-7306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7309 CJK Ideograph-7309 | 7308 CJK Ideograph-7308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 730b CJK Ideograph-730B | 730a CJK Ideograph-730A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 730d CJK Ideograph-730D | 730c CJK Ideograph-730C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 730f CJK Ideograph-730F | 730e CJK Ideograph-730E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7311 CJK Ideograph-7311 | 7310 CJK Ideograph-7310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7313 CJK Ideograph-7313 | 7312 CJK Ideograph-7312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7315 CJK Ideograph-7315 | 7314 CJK Ideograph-7314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7317 CJK Ideograph-7317 | 7316 CJK Ideograph-7316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7319 CJK Ideograph-7319 | 7318 CJK Ideograph-7318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 731b CJK Ideograph-731B | 731a CJK Ideograph-731A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 731d CJK Ideograph-731D | 731c CJK Ideograph-731C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 731f CJK Ideograph-731F | 731e CJK Ideograph-731E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7321 CJK Ideograph-7321 | 7320 CJK Ideograph-7320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7323 CJK Ideograph-7323 | 7322 CJK Ideograph-7322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7325 CJK Ideograph-7325 | 7324 CJK Ideograph-7324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7327 CJK Ideograph-7327 | 7326 CJK Ideograph-7326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7329 CJK Ideograph-7329 | 7328 CJK Ideograph-7328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 732b CJK Ideograph-732B | 732a CJK Ideograph-732A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 732d CJK Ideograph-732D | 732c CJK Ideograph-732C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 732f CJK Ideograph-732F | 732e CJK Ideograph-732E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7331 CJK Ideograph-7331 | 7330 CJK Ideograph-7330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7333 CJK Ideograph-7333 | 7332 CJK Ideograph-7332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7335 CJK Ideograph-7335 | 7334 CJK Ideograph-7334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7337 CJK Ideograph-7337 | 7336 CJK Ideograph-7336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7339 CJK Ideograph-7339 | 7338 CJK Ideograph-7338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 733b CJK Ideograph-733B | 733a CJK Ideograph-733A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 733d CJK Ideograph-733D | 733c CJK Ideograph-733C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 733f CJK Ideograph-733F | 733e CJK Ideograph-733E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7341 CJK Ideograph-7341 | 7340 CJK Ideograph-7340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7343 CJK Ideograph-7343 | 7342 CJK Ideograph-7342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7345 CJK Ideograph-7345 | 7344 CJK Ideograph-7344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7347 CJK Ideograph-7347 | 7346 CJK Ideograph-7346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7349 CJK Ideograph-7349 | 7348 CJK Ideograph-7348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 734b CJK Ideograph-734B | 734a CJK Ideograph-734A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 734d CJK Ideograph-734D | 734c CJK Ideograph-734C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 734f CJK Ideograph-734F | 734e CJK Ideograph-734E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7351 CJK Ideograph-7351 | 7350 CJK Ideograph-7350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7353 CJK Ideograph-7353 | 7352 CJK Ideograph-7352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7355 CJK Ideograph-7355 | 7354 CJK Ideograph-7354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7357 CJK Ideograph-7357 | 7356 CJK Ideograph-7356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7359 CJK Ideograph-7359 | 7358 CJK Ideograph-7358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 735b CJK Ideograph-735B | 735a CJK Ideograph-735A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 735d CJK Ideograph-735D | 735c CJK Ideograph-735C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 735f CJK Ideograph-735F | 735e CJK Ideograph-735E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7361 CJK Ideograph-7361 | 7360 CJK Ideograph-7360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7363 CJK Ideograph-7363 | 7362 CJK Ideograph-7362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7365 CJK Ideograph-7365 | 7364 CJK Ideograph-7364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7367 CJK Ideograph-7367 | 7366 CJK Ideograph-7366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7369 CJK Ideograph-7369 | 7368 CJK Ideograph-7368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 736b CJK Ideograph-736B | 736a CJK Ideograph-736A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 736d CJK Ideograph-736D | 736c CJK Ideograph-736C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 736f CJK Ideograph-736F | 736e CJK Ideograph-736E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7371 CJK Ideograph-7371 | 7370 CJK Ideograph-7370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7373 CJK Ideograph-7373 | 7372 CJK Ideograph-7372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7375 CJK Ideograph-7375 | 7374 CJK Ideograph-7374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7377 CJK Ideograph-7377 | 7376 CJK Ideograph-7376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7379 CJK Ideograph-7379 | 7378 CJK Ideograph-7378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 737b CJK Ideograph-737B | 737a CJK Ideograph-737A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 737d CJK Ideograph-737D | 737c CJK Ideograph-737C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 737f CJK Ideograph-737F | 737e CJK Ideograph-737E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7381 CJK Ideograph-7381 | 7380 CJK Ideograph-7380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7383 CJK Ideograph-7383 | 7382 CJK Ideograph-7382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7385 CJK Ideograph-7385 | 7384 CJK Ideograph-7384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7387 CJK Ideograph-7387 | 7386 CJK Ideograph-7386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7389 CJK Ideograph-7389 | 7388 CJK Ideograph-7388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 738b CJK Ideograph-738B | 738a CJK Ideograph-738A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 738d CJK Ideograph-738D | 738c CJK Ideograph-738C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 738f CJK Ideograph-738F | 738e CJK Ideograph-738E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7391 CJK Ideograph-7391 | 7390 CJK Ideograph-7390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7393 CJK Ideograph-7393 | 7392 CJK Ideograph-7392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7395 CJK Ideograph-7395 | 7394 CJK Ideograph-7394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7397 CJK Ideograph-7397 | 7396 CJK Ideograph-7396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7399 CJK Ideograph-7399 | 7398 CJK Ideograph-7398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 739b CJK Ideograph-739B | 739a CJK Ideograph-739A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 739d CJK Ideograph-739D | 739c CJK Ideograph-739C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 739f CJK Ideograph-739F | 739e CJK Ideograph-739E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73a1 CJK Ideograph-73A1 | 73a0 CJK Ideograph-73A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73a3 CJK Ideograph-73A3 | 73a2 CJK Ideograph-73A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73a5 CJK Ideograph-73A5 | 73a4 CJK Ideograph-73A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73a7 CJK Ideograph-73A7 | 73a6 CJK Ideograph-73A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73a9 CJK Ideograph-73A9 | 73a8 CJK Ideograph-73A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73ab CJK Ideograph-73AB | 73aa CJK Ideograph-73AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73ad CJK Ideograph-73AD | 73ac CJK Ideograph-73AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73af CJK Ideograph-73AF | 73ae CJK Ideograph-73AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73b1 CJK Ideograph-73B1 | 73b0 CJK Ideograph-73B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73b3 CJK Ideograph-73B3 | 73b2 CJK Ideograph-73B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73b5 CJK Ideograph-73B5 | 73b4 CJK Ideograph-73B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73b7 CJK Ideograph-73B7 | 73b6 CJK Ideograph-73B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73b9 CJK Ideograph-73B9 | 73b8 CJK Ideograph-73B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73bb CJK Ideograph-73BB | 73ba CJK Ideograph-73BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73bd CJK Ideograph-73BD | 73bc CJK Ideograph-73BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73bf CJK Ideograph-73BF | 73be CJK Ideograph-73BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73c1 CJK Ideograph-73C1 | 73c0 CJK Ideograph-73C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73c3 CJK Ideograph-73C3 | 73c2 CJK Ideograph-73C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73c5 CJK Ideograph-73C5 | 73c4 CJK Ideograph-73C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73c7 CJK Ideograph-73C7 | 73c6 CJK Ideograph-73C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73c9 CJK Ideograph-73C9 | 73c8 CJK Ideograph-73C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73cb CJK Ideograph-73CB | 73ca CJK Ideograph-73CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73cd CJK Ideograph-73CD | 73cc CJK Ideograph-73CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73cf CJK Ideograph-73CF | 73ce CJK Ideograph-73CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73d1 CJK Ideograph-73D1 | 73d0 CJK Ideograph-73D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73d3 CJK Ideograph-73D3 | 73d2 CJK Ideograph-73D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73d5 CJK Ideograph-73D5 | 73d4 CJK Ideograph-73D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73d7 CJK Ideograph-73D7 | 73d6 CJK Ideograph-73D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73d9 CJK Ideograph-73D9 | 73d8 CJK Ideograph-73D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73db CJK Ideograph-73DB | 73da CJK Ideograph-73DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73dd CJK Ideograph-73DD | 73dc CJK Ideograph-73DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73df CJK Ideograph-73DF | 73de CJK Ideograph-73DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73e1 CJK Ideograph-73E1 | 73e0 CJK Ideograph-73E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73e3 CJK Ideograph-73E3 | 73e2 CJK Ideograph-73E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73e5 CJK Ideograph-73E5 | 73e4 CJK Ideograph-73E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73e7 CJK Ideograph-73E7 | 73e6 CJK Ideograph-73E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73e9 CJK Ideograph-73E9 | 73e8 CJK Ideograph-73E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73eb CJK Ideograph-73EB | 73ea CJK Ideograph-73EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73ed CJK Ideograph-73ED | 73ec CJK Ideograph-73EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73ef CJK Ideograph-73EF | 73ee CJK Ideograph-73EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73f1 CJK Ideograph-73F1 | 73f0 CJK Ideograph-73F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73f3 CJK Ideograph-73F3 | 73f2 CJK Ideograph-73F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73f5 CJK Ideograph-73F5 | 73f4 CJK Ideograph-73F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73f7 CJK Ideograph-73F7 | 73f6 CJK Ideograph-73F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73f9 CJK Ideograph-73F9 | 73f8 CJK Ideograph-73F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73fb CJK Ideograph-73FB | 73fa CJK Ideograph-73FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73fd CJK Ideograph-73FD | 73fc CJK Ideograph-73FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73ff CJK Ideograph-73FF | 73fe CJK Ideograph-73FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7401 CJK Ideograph-7401 | 7400 CJK Ideograph-7400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7403 CJK Ideograph-7403 | 7402 CJK Ideograph-7402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7405 CJK Ideograph-7405 | 7404 CJK Ideograph-7404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7407 CJK Ideograph-7407 | 7406 CJK Ideograph-7406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7409 CJK Ideograph-7409 | 7408 CJK Ideograph-7408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 740b CJK Ideograph-740B | 740a CJK Ideograph-740A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 740d CJK Ideograph-740D | 740c CJK Ideograph-740C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 740f CJK Ideograph-740F | 740e CJK Ideograph-740E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7411 CJK Ideograph-7411 | 7410 CJK Ideograph-7410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7413 CJK Ideograph-7413 | 7412 CJK Ideograph-7412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7415 CJK Ideograph-7415 | 7414 CJK Ideograph-7414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7417 CJK Ideograph-7417 | 7416 CJK Ideograph-7416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7419 CJK Ideograph-7419 | 7418 CJK Ideograph-7418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 741b CJK Ideograph-741B | 741a CJK Ideograph-741A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 741d CJK Ideograph-741D | 741c CJK Ideograph-741C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 741f CJK Ideograph-741F | 741e CJK Ideograph-741E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7421 CJK Ideograph-7421 | 7420 CJK Ideograph-7420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7423 CJK Ideograph-7423 | 7422 CJK Ideograph-7422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7425 CJK Ideograph-7425 | 7424 CJK Ideograph-7424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7427 CJK Ideograph-7427 | 7426 CJK Ideograph-7426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7429 CJK Ideograph-7429 | 7428 CJK Ideograph-7428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 742b CJK Ideograph-742B | 742a CJK Ideograph-742A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 742d CJK Ideograph-742D | 742c CJK Ideograph-742C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 742f CJK Ideograph-742F | 742e CJK Ideograph-742E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7431 CJK Ideograph-7431 | 7430 CJK Ideograph-7430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7433 CJK Ideograph-7433 | 7432 CJK Ideograph-7432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7435 CJK Ideograph-7435 | 7434 CJK Ideograph-7434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7437 CJK Ideograph-7437 | 7436 CJK Ideograph-7436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7439 CJK Ideograph-7439 | 7438 CJK Ideograph-7438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 743b CJK Ideograph-743B | 743a CJK Ideograph-743A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 743d CJK Ideograph-743D | 743c CJK Ideograph-743C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 743f CJK Ideograph-743F | 743e CJK Ideograph-743E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7441 CJK Ideograph-7441 | 7440 CJK Ideograph-7440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7443 CJK Ideograph-7443 | 7442 CJK Ideograph-7442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7445 CJK Ideograph-7445 | 7444 CJK Ideograph-7444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7447 CJK Ideograph-7447 | 7446 CJK Ideograph-7446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7449 CJK Ideograph-7449 | 7448 CJK Ideograph-7448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 744b CJK Ideograph-744B | 744a CJK Ideograph-744A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 744d CJK Ideograph-744D | 744c CJK Ideograph-744C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 744f CJK Ideograph-744F | 744e CJK Ideograph-744E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7451 CJK Ideograph-7451 | 7450 CJK Ideograph-7450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7453 CJK Ideograph-7453 | 7452 CJK Ideograph-7452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7455 CJK Ideograph-7455 | 7454 CJK Ideograph-7454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7457 CJK Ideograph-7457 | 7456 CJK Ideograph-7456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7459 CJK Ideograph-7459 | 7458 CJK Ideograph-7458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 745b CJK Ideograph-745B | 745a CJK Ideograph-745A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 745d CJK Ideograph-745D | 745c CJK Ideograph-745C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 745f CJK Ideograph-745F | 745e CJK Ideograph-745E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7461 CJK Ideograph-7461 | 7460 CJK Ideograph-7460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7463 CJK Ideograph-7463 | 7462 CJK Ideograph-7462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7465 CJK Ideograph-7465 | 7464 CJK Ideograph-7464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7467 CJK Ideograph-7467 | 7466 CJK Ideograph-7466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7469 CJK Ideograph-7469 | 7468 CJK Ideograph-7468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 746b CJK Ideograph-746B | 746a CJK Ideograph-746A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 746d CJK Ideograph-746D | 746c CJK Ideograph-746C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 746f CJK Ideograph-746F | 746e CJK Ideograph-746E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7471 CJK Ideograph-7471 | 7470 CJK Ideograph-7470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7473 CJK Ideograph-7473 | 7472 CJK Ideograph-7472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7475 CJK Ideograph-7475 | 7474 CJK Ideograph-7474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7477 CJK Ideograph-7477 | 7476 CJK Ideograph-7476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7479 CJK Ideograph-7479 | 7478 CJK Ideograph-7478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 747b CJK Ideograph-747B | 747a CJK Ideograph-747A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 747d CJK Ideograph-747D | 747c CJK Ideograph-747C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 747f CJK Ideograph-747F | 747e CJK Ideograph-747E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7481 CJK Ideograph-7481 | 7480 CJK Ideograph-7480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7483 CJK Ideograph-7483 | 7482 CJK Ideograph-7482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7485 CJK Ideograph-7485 | 7484 CJK Ideograph-7484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7487 CJK Ideograph-7487 | 7486 CJK Ideograph-7486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7489 CJK Ideograph-7489 | 7488 CJK Ideograph-7488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 748b CJK Ideograph-748B | 748a CJK Ideograph-748A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 748d CJK Ideograph-748D | 748c CJK Ideograph-748C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 748f CJK Ideograph-748F | 748e CJK Ideograph-748E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7491 CJK Ideograph-7491 | 7490 CJK Ideograph-7490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7493 CJK Ideograph-7493 | 7492 CJK Ideograph-7492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7495 CJK Ideograph-7495 | 7494 CJK Ideograph-7494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7497 CJK Ideograph-7497 | 7496 CJK Ideograph-7496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7499 CJK Ideograph-7499 | 7498 CJK Ideograph-7498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 749b CJK Ideograph-749B | 749a CJK Ideograph-749A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 749d CJK Ideograph-749D | 749c CJK Ideograph-749C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 749f CJK Ideograph-749F | 749e CJK Ideograph-749E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74a1 CJK Ideograph-74A1 | 74a0 CJK Ideograph-74A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74a3 CJK Ideograph-74A3 | 74a2 CJK Ideograph-74A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74a5 CJK Ideograph-74A5 | 74a4 CJK Ideograph-74A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74a7 CJK Ideograph-74A7 | 74a6 CJK Ideograph-74A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74a9 CJK Ideograph-74A9 | 74a8 CJK Ideograph-74A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74ab CJK Ideograph-74AB | 74aa CJK Ideograph-74AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74ad CJK Ideograph-74AD | 74ac CJK Ideograph-74AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74af CJK Ideograph-74AF | 74ae CJK Ideograph-74AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74b1 CJK Ideograph-74B1 | 74b0 CJK Ideograph-74B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74b3 CJK Ideograph-74B3 | 74b2 CJK Ideograph-74B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74b5 CJK Ideograph-74B5 | 74b4 CJK Ideograph-74B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74b7 CJK Ideograph-74B7 | 74b6 CJK Ideograph-74B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74b9 CJK Ideograph-74B9 | 74b8 CJK Ideograph-74B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74bb CJK Ideograph-74BB | 74ba CJK Ideograph-74BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74bd CJK Ideograph-74BD | 74bc CJK Ideograph-74BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74bf CJK Ideograph-74BF | 74be CJK Ideograph-74BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74c1 CJK Ideograph-74C1 | 74c0 CJK Ideograph-74C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74c3 CJK Ideograph-74C3 | 74c2 CJK Ideograph-74C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74c5 CJK Ideograph-74C5 | 74c4 CJK Ideograph-74C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74c7 CJK Ideograph-74C7 | 74c6 CJK Ideograph-74C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74c9 CJK Ideograph-74C9 | 74c8 CJK Ideograph-74C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74cb CJK Ideograph-74CB | 74ca CJK Ideograph-74CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74cd CJK Ideograph-74CD | 74cc CJK Ideograph-74CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74cf CJK Ideograph-74CF | 74ce CJK Ideograph-74CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74d1 CJK Ideograph-74D1 | 74d0 CJK Ideograph-74D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74d3 CJK Ideograph-74D3 | 74d2 CJK Ideograph-74D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74d5 CJK Ideograph-74D5 | 74d4 CJK Ideograph-74D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74d7 CJK Ideograph-74D7 | 74d6 CJK Ideograph-74D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74d9 CJK Ideograph-74D9 | 74d8 CJK Ideograph-74D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74db CJK Ideograph-74DB | 74da CJK Ideograph-74DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74dd CJK Ideograph-74DD | 74dc CJK Ideograph-74DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74df CJK Ideograph-74DF | 74de CJK Ideograph-74DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74e1 CJK Ideograph-74E1 | 74e0 CJK Ideograph-74E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74e3 CJK Ideograph-74E3 | 74e2 CJK Ideograph-74E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74e5 CJK Ideograph-74E5 | 74e4 CJK Ideograph-74E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74e7 CJK Ideograph-74E7 | 74e6 CJK Ideograph-74E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74e9 CJK Ideograph-74E9 | 74e8 CJK Ideograph-74E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74eb CJK Ideograph-74EB | 74ea CJK Ideograph-74EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74ed CJK Ideograph-74ED | 74ec CJK Ideograph-74EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74ef CJK Ideograph-74EF | 74ee CJK Ideograph-74EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74f1 CJK Ideograph-74F1 | 74f0 CJK Ideograph-74F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74f3 CJK Ideograph-74F3 | 74f2 CJK Ideograph-74F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74f5 CJK Ideograph-74F5 | 74f4 CJK Ideograph-74F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74f7 CJK Ideograph-74F7 | 74f6 CJK Ideograph-74F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74f9 CJK Ideograph-74F9 | 74f8 CJK Ideograph-74F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74fb CJK Ideograph-74FB | 74fa CJK Ideograph-74FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74fd CJK Ideograph-74FD | 74fc CJK Ideograph-74FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74ff CJK Ideograph-74FF | 74fe CJK Ideograph-74FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7501 CJK Ideograph-7501 | 7500 CJK Ideograph-7500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7503 CJK Ideograph-7503 | 7502 CJK Ideograph-7502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7505 CJK Ideograph-7505 | 7504 CJK Ideograph-7504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7507 CJK Ideograph-7507 | 7506 CJK Ideograph-7506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7509 CJK Ideograph-7509 | 7508 CJK Ideograph-7508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 750b CJK Ideograph-750B | 750a CJK Ideograph-750A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 750d CJK Ideograph-750D | 750c CJK Ideograph-750C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 750f CJK Ideograph-750F | 750e CJK Ideograph-750E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7511 CJK Ideograph-7511 | 7510 CJK Ideograph-7510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7513 CJK Ideograph-7513 | 7512 CJK Ideograph-7512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7515 CJK Ideograph-7515 | 7514 CJK Ideograph-7514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7517 CJK Ideograph-7517 | 7516 CJK Ideograph-7516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7519 CJK Ideograph-7519 | 7518 CJK Ideograph-7518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 751b CJK Ideograph-751B | 751a CJK Ideograph-751A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 751d CJK Ideograph-751D | 751c CJK Ideograph-751C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 751f CJK Ideograph-751F | 751e CJK Ideograph-751E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7521 CJK Ideograph-7521 | 7520 CJK Ideograph-7520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7523 CJK Ideograph-7523 | 7522 CJK Ideograph-7522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7525 CJK Ideograph-7525 | 7524 CJK Ideograph-7524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7527 CJK Ideograph-7527 | 7526 CJK Ideograph-7526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7529 CJK Ideograph-7529 | 7528 CJK Ideograph-7528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 752b CJK Ideograph-752B | 752a CJK Ideograph-752A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 752d CJK Ideograph-752D | 752c CJK Ideograph-752C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 752f CJK Ideograph-752F | 752e CJK Ideograph-752E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7531 CJK Ideograph-7531 | 7530 CJK Ideograph-7530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7533 CJK Ideograph-7533 | 7532 CJK Ideograph-7532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7535 CJK Ideograph-7535 | 7534 CJK Ideograph-7534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7537 CJK Ideograph-7537 | 7536 CJK Ideograph-7536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7539 CJK Ideograph-7539 | 7538 CJK Ideograph-7538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 753b CJK Ideograph-753B | 753a CJK Ideograph-753A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 753d CJK Ideograph-753D | 753c CJK Ideograph-753C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 753f CJK Ideograph-753F | 753e CJK Ideograph-753E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7541 CJK Ideograph-7541 | 7540 CJK Ideograph-7540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7543 CJK Ideograph-7543 | 7542 CJK Ideograph-7542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7545 CJK Ideograph-7545 | 7544 CJK Ideograph-7544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7547 CJK Ideograph-7547 | 7546 CJK Ideograph-7546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7549 CJK Ideograph-7549 | 7548 CJK Ideograph-7548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 754b CJK Ideograph-754B | 754a CJK Ideograph-754A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 754d CJK Ideograph-754D | 754c CJK Ideograph-754C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 754f CJK Ideograph-754F | 754e CJK Ideograph-754E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7551 CJK Ideograph-7551 | 7550 CJK Ideograph-7550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7553 CJK Ideograph-7553 | 7552 CJK Ideograph-7552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7555 CJK Ideograph-7555 | 7554 CJK Ideograph-7554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7557 CJK Ideograph-7557 | 7556 CJK Ideograph-7556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7559 CJK Ideograph-7559 | 7558 CJK Ideograph-7558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 755b CJK Ideograph-755B | 755a CJK Ideograph-755A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 755d CJK Ideograph-755D | 755c CJK Ideograph-755C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 755f CJK Ideograph-755F | 755e CJK Ideograph-755E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7561 CJK Ideograph-7561 | 7560 CJK Ideograph-7560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7563 CJK Ideograph-7563 | 7562 CJK Ideograph-7562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7565 CJK Ideograph-7565 | 7564 CJK Ideograph-7564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7567 CJK Ideograph-7567 | 7566 CJK Ideograph-7566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7569 CJK Ideograph-7569 | 7568 CJK Ideograph-7568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 756b CJK Ideograph-756B | 756a CJK Ideograph-756A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 756d CJK Ideograph-756D | 756c CJK Ideograph-756C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 756f CJK Ideograph-756F | 756e CJK Ideograph-756E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7571 CJK Ideograph-7571 | 7570 CJK Ideograph-7570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7573 CJK Ideograph-7573 | 7572 CJK Ideograph-7572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7575 CJK Ideograph-7575 | 7574 CJK Ideograph-7574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7577 CJK Ideograph-7577 | 7576 CJK Ideograph-7576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7579 CJK Ideograph-7579 | 7578 CJK Ideograph-7578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 757b CJK Ideograph-757B | 757a CJK Ideograph-757A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 757d CJK Ideograph-757D | 757c CJK Ideograph-757C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 757f CJK Ideograph-757F | 757e CJK Ideograph-757E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7581 CJK Ideograph-7581 | 7580 CJK Ideograph-7580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7583 CJK Ideograph-7583 | 7582 CJK Ideograph-7582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7585 CJK Ideograph-7585 | 7584 CJK Ideograph-7584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7587 CJK Ideograph-7587 | 7586 CJK Ideograph-7586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7589 CJK Ideograph-7589 | 7588 CJK Ideograph-7588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 758b CJK Ideograph-758B | 758a CJK Ideograph-758A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 758d CJK Ideograph-758D | 758c CJK Ideograph-758C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 758f CJK Ideograph-758F | 758e CJK Ideograph-758E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7591 CJK Ideograph-7591 | 7590 CJK Ideograph-7590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7593 CJK Ideograph-7593 | 7592 CJK Ideograph-7592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7595 CJK Ideograph-7595 | 7594 CJK Ideograph-7594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7597 CJK Ideograph-7597 | 7596 CJK Ideograph-7596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7599 CJK Ideograph-7599 | 7598 CJK Ideograph-7598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 759b CJK Ideograph-759B | 759a CJK Ideograph-759A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 759d CJK Ideograph-759D | 759c CJK Ideograph-759C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 759f CJK Ideograph-759F | 759e CJK Ideograph-759E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75a1 CJK Ideograph-75A1 | 75a0 CJK Ideograph-75A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75a3 CJK Ideograph-75A3 | 75a2 CJK Ideograph-75A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75a5 CJK Ideograph-75A5 | 75a4 CJK Ideograph-75A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75a7 CJK Ideograph-75A7 | 75a6 CJK Ideograph-75A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75a9 CJK Ideograph-75A9 | 75a8 CJK Ideograph-75A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75ab CJK Ideograph-75AB | 75aa CJK Ideograph-75AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75ad CJK Ideograph-75AD | 75ac CJK Ideograph-75AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75af CJK Ideograph-75AF | 75ae CJK Ideograph-75AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75b1 CJK Ideograph-75B1 | 75b0 CJK Ideograph-75B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75b3 CJK Ideograph-75B3 | 75b2 CJK Ideograph-75B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75b5 CJK Ideograph-75B5 | 75b4 CJK Ideograph-75B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75b7 CJK Ideograph-75B7 | 75b6 CJK Ideograph-75B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75b9 CJK Ideograph-75B9 | 75b8 CJK Ideograph-75B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75bb CJK Ideograph-75BB | 75ba CJK Ideograph-75BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75bd CJK Ideograph-75BD | 75bc CJK Ideograph-75BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75bf CJK Ideograph-75BF | 75be CJK Ideograph-75BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75c1 CJK Ideograph-75C1 | 75c0 CJK Ideograph-75C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75c3 CJK Ideograph-75C3 | 75c2 CJK Ideograph-75C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75c5 CJK Ideograph-75C5 | 75c4 CJK Ideograph-75C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75c7 CJK Ideograph-75C7 | 75c6 CJK Ideograph-75C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75c9 CJK Ideograph-75C9 | 75c8 CJK Ideograph-75C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75cb CJK Ideograph-75CB | 75ca CJK Ideograph-75CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75cd CJK Ideograph-75CD | 75cc CJK Ideograph-75CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75cf CJK Ideograph-75CF | 75ce CJK Ideograph-75CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75d1 CJK Ideograph-75D1 | 75d0 CJK Ideograph-75D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75d3 CJK Ideograph-75D3 | 75d2 CJK Ideograph-75D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75d5 CJK Ideograph-75D5 | 75d4 CJK Ideograph-75D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75d7 CJK Ideograph-75D7 | 75d6 CJK Ideograph-75D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75d9 CJK Ideograph-75D9 | 75d8 CJK Ideograph-75D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75db CJK Ideograph-75DB | 75da CJK Ideograph-75DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75dd CJK Ideograph-75DD | 75dc CJK Ideograph-75DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75df CJK Ideograph-75DF | 75de CJK Ideograph-75DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75e1 CJK Ideograph-75E1 | 75e0 CJK Ideograph-75E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75e3 CJK Ideograph-75E3 | 75e2 CJK Ideograph-75E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75e5 CJK Ideograph-75E5 | 75e4 CJK Ideograph-75E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75e7 CJK Ideograph-75E7 | 75e6 CJK Ideograph-75E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75e9 CJK Ideograph-75E9 | 75e8 CJK Ideograph-75E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75eb CJK Ideograph-75EB | 75ea CJK Ideograph-75EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75ed CJK Ideograph-75ED | 75ec CJK Ideograph-75EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75ef CJK Ideograph-75EF | 75ee CJK Ideograph-75EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75f1 CJK Ideograph-75F1 | 75f0 CJK Ideograph-75F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75f3 CJK Ideograph-75F3 | 75f2 CJK Ideograph-75F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75f5 CJK Ideograph-75F5 | 75f4 CJK Ideograph-75F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75f7 CJK Ideograph-75F7 | 75f6 CJK Ideograph-75F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75f9 CJK Ideograph-75F9 | 75f8 CJK Ideograph-75F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75fb CJK Ideograph-75FB | 75fa CJK Ideograph-75FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75fd CJK Ideograph-75FD | 75fc CJK Ideograph-75FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75ff CJK Ideograph-75FF | 75fe CJK Ideograph-75FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7601 CJK Ideograph-7601 | 7600 CJK Ideograph-7600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7603 CJK Ideograph-7603 | 7602 CJK Ideograph-7602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7605 CJK Ideograph-7605 | 7604 CJK Ideograph-7604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7607 CJK Ideograph-7607 | 7606 CJK Ideograph-7606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7609 CJK Ideograph-7609 | 7608 CJK Ideograph-7608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 760b CJK Ideograph-760B | 760a CJK Ideograph-760A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 760d CJK Ideograph-760D | 760c CJK Ideograph-760C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 760f CJK Ideograph-760F | 760e CJK Ideograph-760E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7611 CJK Ideograph-7611 | 7610 CJK Ideograph-7610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7613 CJK Ideograph-7613 | 7612 CJK Ideograph-7612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7615 CJK Ideograph-7615 | 7614 CJK Ideograph-7614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7617 CJK Ideograph-7617 | 7616 CJK Ideograph-7616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7619 CJK Ideograph-7619 | 7618 CJK Ideograph-7618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 761b CJK Ideograph-761B | 761a CJK Ideograph-761A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 761d CJK Ideograph-761D | 761c CJK Ideograph-761C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 761f CJK Ideograph-761F | 761e CJK Ideograph-761E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7621 CJK Ideograph-7621 | 7620 CJK Ideograph-7620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7623 CJK Ideograph-7623 | 7622 CJK Ideograph-7622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7625 CJK Ideograph-7625 | 7624 CJK Ideograph-7624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7627 CJK Ideograph-7627 | 7626 CJK Ideograph-7626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7629 CJK Ideograph-7629 | 7628 CJK Ideograph-7628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 762b CJK Ideograph-762B | 762a CJK Ideograph-762A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 762d CJK Ideograph-762D | 762c CJK Ideograph-762C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 762f CJK Ideograph-762F | 762e CJK Ideograph-762E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7631 CJK Ideograph-7631 | 7630 CJK Ideograph-7630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7633 CJK Ideograph-7633 | 7632 CJK Ideograph-7632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7635 CJK Ideograph-7635 | 7634 CJK Ideograph-7634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7637 CJK Ideograph-7637 | 7636 CJK Ideograph-7636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7639 CJK Ideograph-7639 | 7638 CJK Ideograph-7638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 763b CJK Ideograph-763B | 763a CJK Ideograph-763A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 763d CJK Ideograph-763D | 763c CJK Ideograph-763C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 763f CJK Ideograph-763F | 763e CJK Ideograph-763E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7641 CJK Ideograph-7641 | 7640 CJK Ideograph-7640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7643 CJK Ideograph-7643 | 7642 CJK Ideograph-7642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7645 CJK Ideograph-7645 | 7644 CJK Ideograph-7644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7647 CJK Ideograph-7647 | 7646 CJK Ideograph-7646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7649 CJK Ideograph-7649 | 7648 CJK Ideograph-7648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 764b CJK Ideograph-764B | 764a CJK Ideograph-764A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 764d CJK Ideograph-764D | 764c CJK Ideograph-764C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 764f CJK Ideograph-764F | 764e CJK Ideograph-764E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7651 CJK Ideograph-7651 | 7650 CJK Ideograph-7650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7653 CJK Ideograph-7653 | 7652 CJK Ideograph-7652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7655 CJK Ideograph-7655 | 7654 CJK Ideograph-7654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7657 CJK Ideograph-7657 | 7656 CJK Ideograph-7656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7659 CJK Ideograph-7659 | 7658 CJK Ideograph-7658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 765b CJK Ideograph-765B | 765a CJK Ideograph-765A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 765d CJK Ideograph-765D | 765c CJK Ideograph-765C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 765f CJK Ideograph-765F | 765e CJK Ideograph-765E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7661 CJK Ideograph-7661 | 7660 CJK Ideograph-7660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7663 CJK Ideograph-7663 | 7662 CJK Ideograph-7662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7665 CJK Ideograph-7665 | 7664 CJK Ideograph-7664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7667 CJK Ideograph-7667 | 7666 CJK Ideograph-7666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7669 CJK Ideograph-7669 | 7668 CJK Ideograph-7668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 766b CJK Ideograph-766B | 766a CJK Ideograph-766A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 766d CJK Ideograph-766D | 766c CJK Ideograph-766C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 766f CJK Ideograph-766F | 766e CJK Ideograph-766E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7671 CJK Ideograph-7671 | 7670 CJK Ideograph-7670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7673 CJK Ideograph-7673 | 7672 CJK Ideograph-7672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7675 CJK Ideograph-7675 | 7674 CJK Ideograph-7674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7677 CJK Ideograph-7677 | 7676 CJK Ideograph-7676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7679 CJK Ideograph-7679 | 7678 CJK Ideograph-7678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 767b CJK Ideograph-767B | 767a CJK Ideograph-767A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 767d CJK Ideograph-767D | 767c CJK Ideograph-767C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 767f CJK Ideograph-767F | 767e CJK Ideograph-767E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7681 CJK Ideograph-7681 | 7680 CJK Ideograph-7680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7683 CJK Ideograph-7683 | 7682 CJK Ideograph-7682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7685 CJK Ideograph-7685 | 7684 CJK Ideograph-7684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7687 CJK Ideograph-7687 | 7686 CJK Ideograph-7686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7689 CJK Ideograph-7689 | 7688 CJK Ideograph-7688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 768b CJK Ideograph-768B | 768a CJK Ideograph-768A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 768d CJK Ideograph-768D | 768c CJK Ideograph-768C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 768f CJK Ideograph-768F | 768e CJK Ideograph-768E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7691 CJK Ideograph-7691 | 7690 CJK Ideograph-7690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7693 CJK Ideograph-7693 | 7692 CJK Ideograph-7692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7695 CJK Ideograph-7695 | 7694 CJK Ideograph-7694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7697 CJK Ideograph-7697 | 7696 CJK Ideograph-7696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7699 CJK Ideograph-7699 | 7698 CJK Ideograph-7698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 769b CJK Ideograph-769B | 769a CJK Ideograph-769A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 769d CJK Ideograph-769D | 769c CJK Ideograph-769C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 769f CJK Ideograph-769F | 769e CJK Ideograph-769E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76a1 CJK Ideograph-76A1 | 76a0 CJK Ideograph-76A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76a3 CJK Ideograph-76A3 | 76a2 CJK Ideograph-76A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76a5 CJK Ideograph-76A5 | 76a4 CJK Ideograph-76A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76a7 CJK Ideograph-76A7 | 76a6 CJK Ideograph-76A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76a9 CJK Ideograph-76A9 | 76a8 CJK Ideograph-76A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76ab CJK Ideograph-76AB | 76aa CJK Ideograph-76AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76ad CJK Ideograph-76AD | 76ac CJK Ideograph-76AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76af CJK Ideograph-76AF | 76ae CJK Ideograph-76AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76b1 CJK Ideograph-76B1 | 76b0 CJK Ideograph-76B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76b3 CJK Ideograph-76B3 | 76b2 CJK Ideograph-76B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76b5 CJK Ideograph-76B5 | 76b4 CJK Ideograph-76B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76b7 CJK Ideograph-76B7 | 76b6 CJK Ideograph-76B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76b9 CJK Ideograph-76B9 | 76b8 CJK Ideograph-76B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76bb CJK Ideograph-76BB | 76ba CJK Ideograph-76BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76bd CJK Ideograph-76BD | 76bc CJK Ideograph-76BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76bf CJK Ideograph-76BF | 76be CJK Ideograph-76BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76c1 CJK Ideograph-76C1 | 76c0 CJK Ideograph-76C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76c3 CJK Ideograph-76C3 | 76c2 CJK Ideograph-76C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76c5 CJK Ideograph-76C5 | 76c4 CJK Ideograph-76C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76c7 CJK Ideograph-76C7 | 76c6 CJK Ideograph-76C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76c9 CJK Ideograph-76C9 | 76c8 CJK Ideograph-76C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76cb CJK Ideograph-76CB | 76ca CJK Ideograph-76CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76cd CJK Ideograph-76CD | 76cc CJK Ideograph-76CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76cf CJK Ideograph-76CF | 76ce CJK Ideograph-76CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76d1 CJK Ideograph-76D1 | 76d0 CJK Ideograph-76D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76d3 CJK Ideograph-76D3 | 76d2 CJK Ideograph-76D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76d5 CJK Ideograph-76D5 | 76d4 CJK Ideograph-76D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76d7 CJK Ideograph-76D7 | 76d6 CJK Ideograph-76D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76d9 CJK Ideograph-76D9 | 76d8 CJK Ideograph-76D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76db CJK Ideograph-76DB | 76da CJK Ideograph-76DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76dd CJK Ideograph-76DD | 76dc CJK Ideograph-76DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76df CJK Ideograph-76DF | 76de CJK Ideograph-76DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76e1 CJK Ideograph-76E1 | 76e0 CJK Ideograph-76E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76e3 CJK Ideograph-76E3 | 76e2 CJK Ideograph-76E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76e5 CJK Ideograph-76E5 | 76e4 CJK Ideograph-76E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76e7 CJK Ideograph-76E7 | 76e6 CJK Ideograph-76E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76e9 CJK Ideograph-76E9 | 76e8 CJK Ideograph-76E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76eb CJK Ideograph-76EB | 76ea CJK Ideograph-76EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76ed CJK Ideograph-76ED | 76ec CJK Ideograph-76EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76ef CJK Ideograph-76EF | 76ee CJK Ideograph-76EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76f1 CJK Ideograph-76F1 | 76f0 CJK Ideograph-76F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76f3 CJK Ideograph-76F3 | 76f2 CJK Ideograph-76F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76f5 CJK Ideograph-76F5 | 76f4 CJK Ideograph-76F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76f7 CJK Ideograph-76F7 | 76f6 CJK Ideograph-76F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76f9 CJK Ideograph-76F9 | 76f8 CJK Ideograph-76F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76fb CJK Ideograph-76FB | 76fa CJK Ideograph-76FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76fd CJK Ideograph-76FD | 76fc CJK Ideograph-76FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76ff CJK Ideograph-76FF | 76fe CJK Ideograph-76FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7701 CJK Ideograph-7701 | 7700 CJK Ideograph-7700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7703 CJK Ideograph-7703 | 7702 CJK Ideograph-7702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7705 CJK Ideograph-7705 | 7704 CJK Ideograph-7704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7707 CJK Ideograph-7707 | 7706 CJK Ideograph-7706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7709 CJK Ideograph-7709 | 7708 CJK Ideograph-7708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 770b CJK Ideograph-770B | 770a CJK Ideograph-770A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 770d CJK Ideograph-770D | 770c CJK Ideograph-770C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 770f CJK Ideograph-770F | 770e CJK Ideograph-770E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7711 CJK Ideograph-7711 | 7710 CJK Ideograph-7710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7713 CJK Ideograph-7713 | 7712 CJK Ideograph-7712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7715 CJK Ideograph-7715 | 7714 CJK Ideograph-7714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7717 CJK Ideograph-7717 | 7716 CJK Ideograph-7716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7719 CJK Ideograph-7719 | 7718 CJK Ideograph-7718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 771b CJK Ideograph-771B | 771a CJK Ideograph-771A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 771d CJK Ideograph-771D | 771c CJK Ideograph-771C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 771f CJK Ideograph-771F | 771e CJK Ideograph-771E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7721 CJK Ideograph-7721 | 7720 CJK Ideograph-7720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7723 CJK Ideograph-7723 | 7722 CJK Ideograph-7722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7725 CJK Ideograph-7725 | 7724 CJK Ideograph-7724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7727 CJK Ideograph-7727 | 7726 CJK Ideograph-7726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7729 CJK Ideograph-7729 | 7728 CJK Ideograph-7728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 772b CJK Ideograph-772B | 772a CJK Ideograph-772A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 772d CJK Ideograph-772D | 772c CJK Ideograph-772C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 772f CJK Ideograph-772F | 772e CJK Ideograph-772E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7731 CJK Ideograph-7731 | 7730 CJK Ideograph-7730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7733 CJK Ideograph-7733 | 7732 CJK Ideograph-7732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7735 CJK Ideograph-7735 | 7734 CJK Ideograph-7734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7737 CJK Ideograph-7737 | 7736 CJK Ideograph-7736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7739 CJK Ideograph-7739 | 7738 CJK Ideograph-7738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 773b CJK Ideograph-773B | 773a CJK Ideograph-773A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 773d CJK Ideograph-773D | 773c CJK Ideograph-773C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 773f CJK Ideograph-773F | 773e CJK Ideograph-773E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7741 CJK Ideograph-7741 | 7740 CJK Ideograph-7740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7743 CJK Ideograph-7743 | 7742 CJK Ideograph-7742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7745 CJK Ideograph-7745 | 7744 CJK Ideograph-7744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7747 CJK Ideograph-7747 | 7746 CJK Ideograph-7746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7749 CJK Ideograph-7749 | 7748 CJK Ideograph-7748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 774b CJK Ideograph-774B | 774a CJK Ideograph-774A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 774d CJK Ideograph-774D | 774c CJK Ideograph-774C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 774f CJK Ideograph-774F | 774e CJK Ideograph-774E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7751 CJK Ideograph-7751 | 7750 CJK Ideograph-7750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7753 CJK Ideograph-7753 | 7752 CJK Ideograph-7752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7755 CJK Ideograph-7755 | 7754 CJK Ideograph-7754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7757 CJK Ideograph-7757 | 7756 CJK Ideograph-7756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7759 CJK Ideograph-7759 | 7758 CJK Ideograph-7758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 775b CJK Ideograph-775B | 775a CJK Ideograph-775A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 775d CJK Ideograph-775D | 775c CJK Ideograph-775C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 775f CJK Ideograph-775F | 775e CJK Ideograph-775E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7761 CJK Ideograph-7761 | 7760 CJK Ideograph-7760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7763 CJK Ideograph-7763 | 7762 CJK Ideograph-7762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7765 CJK Ideograph-7765 | 7764 CJK Ideograph-7764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7767 CJK Ideograph-7767 | 7766 CJK Ideograph-7766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7769 CJK Ideograph-7769 | 7768 CJK Ideograph-7768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 776b CJK Ideograph-776B | 776a CJK Ideograph-776A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 776d CJK Ideograph-776D | 776c CJK Ideograph-776C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 776f CJK Ideograph-776F | 776e CJK Ideograph-776E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7771 CJK Ideograph-7771 | 7770 CJK Ideograph-7770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7773 CJK Ideograph-7773 | 7772 CJK Ideograph-7772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7775 CJK Ideograph-7775 | 7774 CJK Ideograph-7774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7777 CJK Ideograph-7777 | 7776 CJK Ideograph-7776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7779 CJK Ideograph-7779 | 7778 CJK Ideograph-7778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 777b CJK Ideograph-777B | 777a CJK Ideograph-777A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 777d CJK Ideograph-777D | 777c CJK Ideograph-777C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 777f CJK Ideograph-777F | 777e CJK Ideograph-777E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7781 CJK Ideograph-7781 | 7780 CJK Ideograph-7780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7783 CJK Ideograph-7783 | 7782 CJK Ideograph-7782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7785 CJK Ideograph-7785 | 7784 CJK Ideograph-7784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7787 CJK Ideograph-7787 | 7786 CJK Ideograph-7786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7789 CJK Ideograph-7789 | 7788 CJK Ideograph-7788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 778b CJK Ideograph-778B | 778a CJK Ideograph-778A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 778d CJK Ideograph-778D | 778c CJK Ideograph-778C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 778f CJK Ideograph-778F | 778e CJK Ideograph-778E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7791 CJK Ideograph-7791 | 7790 CJK Ideograph-7790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7793 CJK Ideograph-7793 | 7792 CJK Ideograph-7792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7795 CJK Ideograph-7795 | 7794 CJK Ideograph-7794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7797 CJK Ideograph-7797 | 7796 CJK Ideograph-7796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7799 CJK Ideograph-7799 | 7798 CJK Ideograph-7798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 779b CJK Ideograph-779B | 779a CJK Ideograph-779A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 779d CJK Ideograph-779D | 779c CJK Ideograph-779C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 779f CJK Ideograph-779F | 779e CJK Ideograph-779E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77a1 CJK Ideograph-77A1 | 77a0 CJK Ideograph-77A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77a3 CJK Ideograph-77A3 | 77a2 CJK Ideograph-77A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77a5 CJK Ideograph-77A5 | 77a4 CJK Ideograph-77A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77a7 CJK Ideograph-77A7 | 77a6 CJK Ideograph-77A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77a9 CJK Ideograph-77A9 | 77a8 CJK Ideograph-77A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77ab CJK Ideograph-77AB | 77aa CJK Ideograph-77AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77ad CJK Ideograph-77AD | 77ac CJK Ideograph-77AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77af CJK Ideograph-77AF | 77ae CJK Ideograph-77AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77b1 CJK Ideograph-77B1 | 77b0 CJK Ideograph-77B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77b3 CJK Ideograph-77B3 | 77b2 CJK Ideograph-77B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77b5 CJK Ideograph-77B5 | 77b4 CJK Ideograph-77B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77b7 CJK Ideograph-77B7 | 77b6 CJK Ideograph-77B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77b9 CJK Ideograph-77B9 | 77b8 CJK Ideograph-77B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77bb CJK Ideograph-77BB | 77ba CJK Ideograph-77BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77bd CJK Ideograph-77BD | 77bc CJK Ideograph-77BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77bf CJK Ideograph-77BF | 77be CJK Ideograph-77BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77c1 CJK Ideograph-77C1 | 77c0 CJK Ideograph-77C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77c3 CJK Ideograph-77C3 | 77c2 CJK Ideograph-77C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77c5 CJK Ideograph-77C5 | 77c4 CJK Ideograph-77C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77c7 CJK Ideograph-77C7 | 77c6 CJK Ideograph-77C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77c9 CJK Ideograph-77C9 | 77c8 CJK Ideograph-77C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77cb CJK Ideograph-77CB | 77ca CJK Ideograph-77CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77cd CJK Ideograph-77CD | 77cc CJK Ideograph-77CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77cf CJK Ideograph-77CF | 77ce CJK Ideograph-77CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77d1 CJK Ideograph-77D1 | 77d0 CJK Ideograph-77D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77d3 CJK Ideograph-77D3 | 77d2 CJK Ideograph-77D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77d5 CJK Ideograph-77D5 | 77d4 CJK Ideograph-77D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77d7 CJK Ideograph-77D7 | 77d6 CJK Ideograph-77D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77d9 CJK Ideograph-77D9 | 77d8 CJK Ideograph-77D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77db CJK Ideograph-77DB | 77da CJK Ideograph-77DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77dd CJK Ideograph-77DD | 77dc CJK Ideograph-77DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77df CJK Ideograph-77DF | 77de CJK Ideograph-77DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77e1 CJK Ideograph-77E1 | 77e0 CJK Ideograph-77E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77e3 CJK Ideograph-77E3 | 77e2 CJK Ideograph-77E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77e5 CJK Ideograph-77E5 | 77e4 CJK Ideograph-77E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77e7 CJK Ideograph-77E7 | 77e6 CJK Ideograph-77E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77e9 CJK Ideograph-77E9 | 77e8 CJK Ideograph-77E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77eb CJK Ideograph-77EB | 77ea CJK Ideograph-77EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77ed CJK Ideograph-77ED | 77ec CJK Ideograph-77EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77ef CJK Ideograph-77EF | 77ee CJK Ideograph-77EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77f1 CJK Ideograph-77F1 | 77f0 CJK Ideograph-77F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77f3 CJK Ideograph-77F3 | 77f2 CJK Ideograph-77F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77f5 CJK Ideograph-77F5 | 77f4 CJK Ideograph-77F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77f7 CJK Ideograph-77F7 | 77f6 CJK Ideograph-77F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77f9 CJK Ideograph-77F9 | 77f8 CJK Ideograph-77F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77fb CJK Ideograph-77FB | 77fa CJK Ideograph-77FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77fd CJK Ideograph-77FD | 77fc CJK Ideograph-77FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77ff CJK Ideograph-77FF | 77fe CJK Ideograph-77FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7801 CJK Ideograph-7801 | 7800 CJK Ideograph-7800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7803 CJK Ideograph-7803 | 7802 CJK Ideograph-7802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7805 CJK Ideograph-7805 | 7804 CJK Ideograph-7804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7807 CJK Ideograph-7807 | 7806 CJK Ideograph-7806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7809 CJK Ideograph-7809 | 7808 CJK Ideograph-7808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 780b CJK Ideograph-780B | 780a CJK Ideograph-780A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 780d CJK Ideograph-780D | 780c CJK Ideograph-780C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 780f CJK Ideograph-780F | 780e CJK Ideograph-780E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7811 CJK Ideograph-7811 | 7810 CJK Ideograph-7810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7813 CJK Ideograph-7813 | 7812 CJK Ideograph-7812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7815 CJK Ideograph-7815 | 7814 CJK Ideograph-7814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7817 CJK Ideograph-7817 | 7816 CJK Ideograph-7816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7819 CJK Ideograph-7819 | 7818 CJK Ideograph-7818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 781b CJK Ideograph-781B | 781a CJK Ideograph-781A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 781d CJK Ideograph-781D | 781c CJK Ideograph-781C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 781f CJK Ideograph-781F | 781e CJK Ideograph-781E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7821 CJK Ideograph-7821 | 7820 CJK Ideograph-7820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7823 CJK Ideograph-7823 | 7822 CJK Ideograph-7822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7825 CJK Ideograph-7825 | 7824 CJK Ideograph-7824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7827 CJK Ideograph-7827 | 7826 CJK Ideograph-7826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7829 CJK Ideograph-7829 | 7828 CJK Ideograph-7828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 782b CJK Ideograph-782B | 782a CJK Ideograph-782A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 782d CJK Ideograph-782D | 782c CJK Ideograph-782C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 782f CJK Ideograph-782F | 782e CJK Ideograph-782E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7831 CJK Ideograph-7831 | 7830 CJK Ideograph-7830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7833 CJK Ideograph-7833 | 7832 CJK Ideograph-7832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7835 CJK Ideograph-7835 | 7834 CJK Ideograph-7834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7837 CJK Ideograph-7837 | 7836 CJK Ideograph-7836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7839 CJK Ideograph-7839 | 7838 CJK Ideograph-7838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 783b CJK Ideograph-783B | 783a CJK Ideograph-783A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 783d CJK Ideograph-783D | 783c CJK Ideograph-783C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 783f CJK Ideograph-783F | 783e CJK Ideograph-783E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7841 CJK Ideograph-7841 | 7840 CJK Ideograph-7840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7843 CJK Ideograph-7843 | 7842 CJK Ideograph-7842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7845 CJK Ideograph-7845 | 7844 CJK Ideograph-7844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7847 CJK Ideograph-7847 | 7846 CJK Ideograph-7846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7849 CJK Ideograph-7849 | 7848 CJK Ideograph-7848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 784b CJK Ideograph-784B | 784a CJK Ideograph-784A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 784d CJK Ideograph-784D | 784c CJK Ideograph-784C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 784f CJK Ideograph-784F | 784e CJK Ideograph-784E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7851 CJK Ideograph-7851 | 7850 CJK Ideograph-7850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7853 CJK Ideograph-7853 | 7852 CJK Ideograph-7852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7855 CJK Ideograph-7855 | 7854 CJK Ideograph-7854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7857 CJK Ideograph-7857 | 7856 CJK Ideograph-7856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7859 CJK Ideograph-7859 | 7858 CJK Ideograph-7858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 785b CJK Ideograph-785B | 785a CJK Ideograph-785A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 785d CJK Ideograph-785D | 785c CJK Ideograph-785C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 785f CJK Ideograph-785F | 785e CJK Ideograph-785E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7861 CJK Ideograph-7861 | 7860 CJK Ideograph-7860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7863 CJK Ideograph-7863 | 7862 CJK Ideograph-7862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7865 CJK Ideograph-7865 | 7864 CJK Ideograph-7864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7867 CJK Ideograph-7867 | 7866 CJK Ideograph-7866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7869 CJK Ideograph-7869 | 7868 CJK Ideograph-7868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 786b CJK Ideograph-786B | 786a CJK Ideograph-786A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 786d CJK Ideograph-786D | 786c CJK Ideograph-786C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 786f CJK Ideograph-786F | 786e CJK Ideograph-786E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7871 CJK Ideograph-7871 | 7870 CJK Ideograph-7870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7873 CJK Ideograph-7873 | 7872 CJK Ideograph-7872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7875 CJK Ideograph-7875 | 7874 CJK Ideograph-7874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7877 CJK Ideograph-7877 | 7876 CJK Ideograph-7876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7879 CJK Ideograph-7879 | 7878 CJK Ideograph-7878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 787b CJK Ideograph-787B | 787a CJK Ideograph-787A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 787d CJK Ideograph-787D | 787c CJK Ideograph-787C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 787f CJK Ideograph-787F | 787e CJK Ideograph-787E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7881 CJK Ideograph-7881 | 7880 CJK Ideograph-7880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7883 CJK Ideograph-7883 | 7882 CJK Ideograph-7882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7885 CJK Ideograph-7885 | 7884 CJK Ideograph-7884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7887 CJK Ideograph-7887 | 7886 CJK Ideograph-7886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7889 CJK Ideograph-7889 | 7888 CJK Ideograph-7888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 788b CJK Ideograph-788B | 788a CJK Ideograph-788A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 788d CJK Ideograph-788D | 788c CJK Ideograph-788C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 788f CJK Ideograph-788F | 788e CJK Ideograph-788E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7891 CJK Ideograph-7891 | 7890 CJK Ideograph-7890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7893 CJK Ideograph-7893 | 7892 CJK Ideograph-7892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7895 CJK Ideograph-7895 | 7894 CJK Ideograph-7894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7897 CJK Ideograph-7897 | 7896 CJK Ideograph-7896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7899 CJK Ideograph-7899 | 7898 CJK Ideograph-7898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 789b CJK Ideograph-789B | 789a CJK Ideograph-789A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 789d CJK Ideograph-789D | 789c CJK Ideograph-789C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 789f CJK Ideograph-789F | 789e CJK Ideograph-789E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78a1 CJK Ideograph-78A1 | 78a0 CJK Ideograph-78A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78a3 CJK Ideograph-78A3 | 78a2 CJK Ideograph-78A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78a5 CJK Ideograph-78A5 | 78a4 CJK Ideograph-78A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78a7 CJK Ideograph-78A7 | 78a6 CJK Ideograph-78A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78a9 CJK Ideograph-78A9 | 78a8 CJK Ideograph-78A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78ab CJK Ideograph-78AB | 78aa CJK Ideograph-78AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78ad CJK Ideograph-78AD | 78ac CJK Ideograph-78AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78af CJK Ideograph-78AF | 78ae CJK Ideograph-78AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78b1 CJK Ideograph-78B1 | 78b0 CJK Ideograph-78B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78b3 CJK Ideograph-78B3 | 78b2 CJK Ideograph-78B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78b5 CJK Ideograph-78B5 | 78b4 CJK Ideograph-78B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78b7 CJK Ideograph-78B7 | 78b6 CJK Ideograph-78B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78b9 CJK Ideograph-78B9 | 78b8 CJK Ideograph-78B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78bb CJK Ideograph-78BB | 78ba CJK Ideograph-78BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78bd CJK Ideograph-78BD | 78bc CJK Ideograph-78BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78bf CJK Ideograph-78BF | 78be CJK Ideograph-78BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78c1 CJK Ideograph-78C1 | 78c0 CJK Ideograph-78C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78c3 CJK Ideograph-78C3 | 78c2 CJK Ideograph-78C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78c5 CJK Ideograph-78C5 | 78c4 CJK Ideograph-78C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78c7 CJK Ideograph-78C7 | 78c6 CJK Ideograph-78C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78c9 CJK Ideograph-78C9 | 78c8 CJK Ideograph-78C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78cb CJK Ideograph-78CB | 78ca CJK Ideograph-78CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78cd CJK Ideograph-78CD | 78cc CJK Ideograph-78CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78cf CJK Ideograph-78CF | 78ce CJK Ideograph-78CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78d1 CJK Ideograph-78D1 | 78d0 CJK Ideograph-78D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78d3 CJK Ideograph-78D3 | 78d2 CJK Ideograph-78D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78d5 CJK Ideograph-78D5 | 78d4 CJK Ideograph-78D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78d7 CJK Ideograph-78D7 | 78d6 CJK Ideograph-78D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78d9 CJK Ideograph-78D9 | 78d8 CJK Ideograph-78D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78db CJK Ideograph-78DB | 78da CJK Ideograph-78DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78dd CJK Ideograph-78DD | 78dc CJK Ideograph-78DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78df CJK Ideograph-78DF | 78de CJK Ideograph-78DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78e1 CJK Ideograph-78E1 | 78e0 CJK Ideograph-78E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78e3 CJK Ideograph-78E3 | 78e2 CJK Ideograph-78E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78e5 CJK Ideograph-78E5 | 78e4 CJK Ideograph-78E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78e7 CJK Ideograph-78E7 | 78e6 CJK Ideograph-78E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78e9 CJK Ideograph-78E9 | 78e8 CJK Ideograph-78E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78eb CJK Ideograph-78EB | 78ea CJK Ideograph-78EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78ed CJK Ideograph-78ED | 78ec CJK Ideograph-78EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78ef CJK Ideograph-78EF | 78ee CJK Ideograph-78EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78f1 CJK Ideograph-78F1 | 78f0 CJK Ideograph-78F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78f3 CJK Ideograph-78F3 | 78f2 CJK Ideograph-78F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78f5 CJK Ideograph-78F5 | 78f4 CJK Ideograph-78F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78f7 CJK Ideograph-78F7 | 78f6 CJK Ideograph-78F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78f9 CJK Ideograph-78F9 | 78f8 CJK Ideograph-78F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78fb CJK Ideograph-78FB | 78fa CJK Ideograph-78FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78fd CJK Ideograph-78FD | 78fc CJK Ideograph-78FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78ff CJK Ideograph-78FF | 78fe CJK Ideograph-78FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7901 CJK Ideograph-7901 | 7900 CJK Ideograph-7900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7903 CJK Ideograph-7903 | 7902 CJK Ideograph-7902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7905 CJK Ideograph-7905 | 7904 CJK Ideograph-7904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7907 CJK Ideograph-7907 | 7906 CJK Ideograph-7906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7909 CJK Ideograph-7909 | 7908 CJK Ideograph-7908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 790b CJK Ideograph-790B | 790a CJK Ideograph-790A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 790d CJK Ideograph-790D | 790c CJK Ideograph-790C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 790f CJK Ideograph-790F | 790e CJK Ideograph-790E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7911 CJK Ideograph-7911 | 7910 CJK Ideograph-7910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7913 CJK Ideograph-7913 | 7912 CJK Ideograph-7912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7915 CJK Ideograph-7915 | 7914 CJK Ideograph-7914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7917 CJK Ideograph-7917 | 7916 CJK Ideograph-7916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7919 CJK Ideograph-7919 | 7918 CJK Ideograph-7918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 791b CJK Ideograph-791B | 791a CJK Ideograph-791A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 791d CJK Ideograph-791D | 791c CJK Ideograph-791C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 791f CJK Ideograph-791F | 791e CJK Ideograph-791E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7921 CJK Ideograph-7921 | 7920 CJK Ideograph-7920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7923 CJK Ideograph-7923 | 7922 CJK Ideograph-7922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7925 CJK Ideograph-7925 | 7924 CJK Ideograph-7924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7927 CJK Ideograph-7927 | 7926 CJK Ideograph-7926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7929 CJK Ideograph-7929 | 7928 CJK Ideograph-7928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 792b CJK Ideograph-792B | 792a CJK Ideograph-792A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 792d CJK Ideograph-792D | 792c CJK Ideograph-792C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 792f CJK Ideograph-792F | 792e CJK Ideograph-792E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7931 CJK Ideograph-7931 | 7930 CJK Ideograph-7930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7933 CJK Ideograph-7933 | 7932 CJK Ideograph-7932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7935 CJK Ideograph-7935 | 7934 CJK Ideograph-7934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7937 CJK Ideograph-7937 | 7936 CJK Ideograph-7936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7939 CJK Ideograph-7939 | 7938 CJK Ideograph-7938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 793b CJK Ideograph-793B | 793a CJK Ideograph-793A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 793d CJK Ideograph-793D | 793c CJK Ideograph-793C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 793f CJK Ideograph-793F | 793e CJK Ideograph-793E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7941 CJK Ideograph-7941 | 7940 CJK Ideograph-7940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7943 CJK Ideograph-7943 | 7942 CJK Ideograph-7942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7945 CJK Ideograph-7945 | 7944 CJK Ideograph-7944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7947 CJK Ideograph-7947 | 7946 CJK Ideograph-7946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7949 CJK Ideograph-7949 | 7948 CJK Ideograph-7948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 794b CJK Ideograph-794B | 794a CJK Ideograph-794A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 794d CJK Ideograph-794D | 794c CJK Ideograph-794C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 794f CJK Ideograph-794F | 794e CJK Ideograph-794E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7951 CJK Ideograph-7951 | 7950 CJK Ideograph-7950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7953 CJK Ideograph-7953 | 7952 CJK Ideograph-7952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7955 CJK Ideograph-7955 | 7954 CJK Ideograph-7954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7957 CJK Ideograph-7957 | 7956 CJK Ideograph-7956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7959 CJK Ideograph-7959 | 7958 CJK Ideograph-7958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 795b CJK Ideograph-795B | 795a CJK Ideograph-795A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 795d CJK Ideograph-795D | 795c CJK Ideograph-795C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 795f CJK Ideograph-795F | 795e CJK Ideograph-795E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7961 CJK Ideograph-7961 | 7960 CJK Ideograph-7960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7963 CJK Ideograph-7963 | 7962 CJK Ideograph-7962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7965 CJK Ideograph-7965 | 7964 CJK Ideograph-7964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7967 CJK Ideograph-7967 | 7966 CJK Ideograph-7966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7969 CJK Ideograph-7969 | 7968 CJK Ideograph-7968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 796b CJK Ideograph-796B | 796a CJK Ideograph-796A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 796d CJK Ideograph-796D | 796c CJK Ideograph-796C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 796f CJK Ideograph-796F | 796e CJK Ideograph-796E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7971 CJK Ideograph-7971 | 7970 CJK Ideograph-7970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7973 CJK Ideograph-7973 | 7972 CJK Ideograph-7972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7975 CJK Ideograph-7975 | 7974 CJK Ideograph-7974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7977 CJK Ideograph-7977 | 7976 CJK Ideograph-7976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7979 CJK Ideograph-7979 | 7978 CJK Ideograph-7978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 797b CJK Ideograph-797B | 797a CJK Ideograph-797A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 797d CJK Ideograph-797D | 797c CJK Ideograph-797C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 797f CJK Ideograph-797F | 797e CJK Ideograph-797E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7981 CJK Ideograph-7981 | 7980 CJK Ideograph-7980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7983 CJK Ideograph-7983 | 7982 CJK Ideograph-7982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7985 CJK Ideograph-7985 | 7984 CJK Ideograph-7984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7987 CJK Ideograph-7987 | 7986 CJK Ideograph-7986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7989 CJK Ideograph-7989 | 7988 CJK Ideograph-7988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 798b CJK Ideograph-798B | 798a CJK Ideograph-798A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 798d CJK Ideograph-798D | 798c CJK Ideograph-798C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 798f CJK Ideograph-798F | 798e CJK Ideograph-798E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7991 CJK Ideograph-7991 | 7990 CJK Ideograph-7990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7993 CJK Ideograph-7993 | 7992 CJK Ideograph-7992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7995 CJK Ideograph-7995 | 7994 CJK Ideograph-7994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7997 CJK Ideograph-7997 | 7996 CJK Ideograph-7996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7999 CJK Ideograph-7999 | 7998 CJK Ideograph-7998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 799b CJK Ideograph-799B | 799a CJK Ideograph-799A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 799d CJK Ideograph-799D | 799c CJK Ideograph-799C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 799f CJK Ideograph-799F | 799e CJK Ideograph-799E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79a1 CJK Ideograph-79A1 | 79a0 CJK Ideograph-79A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79a3 CJK Ideograph-79A3 | 79a2 CJK Ideograph-79A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79a5 CJK Ideograph-79A5 | 79a4 CJK Ideograph-79A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79a7 CJK Ideograph-79A7 | 79a6 CJK Ideograph-79A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79a9 CJK Ideograph-79A9 | 79a8 CJK Ideograph-79A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79ab CJK Ideograph-79AB | 79aa CJK Ideograph-79AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79ad CJK Ideograph-79AD | 79ac CJK Ideograph-79AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79af CJK Ideograph-79AF | 79ae CJK Ideograph-79AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79b1 CJK Ideograph-79B1 | 79b0 CJK Ideograph-79B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79b3 CJK Ideograph-79B3 | 79b2 CJK Ideograph-79B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79b5 CJK Ideograph-79B5 | 79b4 CJK Ideograph-79B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79b7 CJK Ideograph-79B7 | 79b6 CJK Ideograph-79B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79b9 CJK Ideograph-79B9 | 79b8 CJK Ideograph-79B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79bb CJK Ideograph-79BB | 79ba CJK Ideograph-79BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79bd CJK Ideograph-79BD | 79bc CJK Ideograph-79BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79bf CJK Ideograph-79BF | 79be CJK Ideograph-79BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79c1 CJK Ideograph-79C1 | 79c0 CJK Ideograph-79C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79c3 CJK Ideograph-79C3 | 79c2 CJK Ideograph-79C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79c5 CJK Ideograph-79C5 | 79c4 CJK Ideograph-79C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79c7 CJK Ideograph-79C7 | 79c6 CJK Ideograph-79C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79c9 CJK Ideograph-79C9 | 79c8 CJK Ideograph-79C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79cb CJK Ideograph-79CB | 79ca CJK Ideograph-79CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79cd CJK Ideograph-79CD | 79cc CJK Ideograph-79CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79cf CJK Ideograph-79CF | 79ce CJK Ideograph-79CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79d1 CJK Ideograph-79D1 | 79d0 CJK Ideograph-79D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79d3 CJK Ideograph-79D3 | 79d2 CJK Ideograph-79D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79d5 CJK Ideograph-79D5 | 79d4 CJK Ideograph-79D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79d7 CJK Ideograph-79D7 | 79d6 CJK Ideograph-79D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79d9 CJK Ideograph-79D9 | 79d8 CJK Ideograph-79D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79db CJK Ideograph-79DB | 79da CJK Ideograph-79DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79dd CJK Ideograph-79DD | 79dc CJK Ideograph-79DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79df CJK Ideograph-79DF | 79de CJK Ideograph-79DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79e1 CJK Ideograph-79E1 | 79e0 CJK Ideograph-79E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79e3 CJK Ideograph-79E3 | 79e2 CJK Ideograph-79E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79e5 CJK Ideograph-79E5 | 79e4 CJK Ideograph-79E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79e7 CJK Ideograph-79E7 | 79e6 CJK Ideograph-79E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79e9 CJK Ideograph-79E9 | 79e8 CJK Ideograph-79E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79eb CJK Ideograph-79EB | 79ea CJK Ideograph-79EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79ed CJK Ideograph-79ED | 79ec CJK Ideograph-79EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79ef CJK Ideograph-79EF | 79ee CJK Ideograph-79EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79f1 CJK Ideograph-79F1 | 79f0 CJK Ideograph-79F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79f3 CJK Ideograph-79F3 | 79f2 CJK Ideograph-79F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79f5 CJK Ideograph-79F5 | 79f4 CJK Ideograph-79F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79f7 CJK Ideograph-79F7 | 79f6 CJK Ideograph-79F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79f9 CJK Ideograph-79F9 | 79f8 CJK Ideograph-79F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79fb CJK Ideograph-79FB | 79fa CJK Ideograph-79FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79fd CJK Ideograph-79FD | 79fc CJK Ideograph-79FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79ff CJK Ideograph-79FF | 79fe CJK Ideograph-79FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a01 CJK Ideograph-7A01 | 7a00 CJK Ideograph-7A00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a03 CJK Ideograph-7A03 | 7a02 CJK Ideograph-7A02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a05 CJK Ideograph-7A05 | 7a04 CJK Ideograph-7A04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a07 CJK Ideograph-7A07 | 7a06 CJK Ideograph-7A06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a09 CJK Ideograph-7A09 | 7a08 CJK Ideograph-7A08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a0b CJK Ideograph-7A0B | 7a0a CJK Ideograph-7A0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a0d CJK Ideograph-7A0D | 7a0c CJK Ideograph-7A0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a0f CJK Ideograph-7A0F | 7a0e CJK Ideograph-7A0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a11 CJK Ideograph-7A11 | 7a10 CJK Ideograph-7A10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a13 CJK Ideograph-7A13 | 7a12 CJK Ideograph-7A12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a15 CJK Ideograph-7A15 | 7a14 CJK Ideograph-7A14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a17 CJK Ideograph-7A17 | 7a16 CJK Ideograph-7A16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a19 CJK Ideograph-7A19 | 7a18 CJK Ideograph-7A18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a1b CJK Ideograph-7A1B | 7a1a CJK Ideograph-7A1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a1d CJK Ideograph-7A1D | 7a1c CJK Ideograph-7A1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a1f CJK Ideograph-7A1F | 7a1e CJK Ideograph-7A1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a21 CJK Ideograph-7A21 | 7a20 CJK Ideograph-7A20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a23 CJK Ideograph-7A23 | 7a22 CJK Ideograph-7A22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a25 CJK Ideograph-7A25 | 7a24 CJK Ideograph-7A24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a27 CJK Ideograph-7A27 | 7a26 CJK Ideograph-7A26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a29 CJK Ideograph-7A29 | 7a28 CJK Ideograph-7A28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a2b CJK Ideograph-7A2B | 7a2a CJK Ideograph-7A2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a2d CJK Ideograph-7A2D | 7a2c CJK Ideograph-7A2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a2f CJK Ideograph-7A2F | 7a2e CJK Ideograph-7A2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a31 CJK Ideograph-7A31 | 7a30 CJK Ideograph-7A30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a33 CJK Ideograph-7A33 | 7a32 CJK Ideograph-7A32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a35 CJK Ideograph-7A35 | 7a34 CJK Ideograph-7A34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a37 CJK Ideograph-7A37 | 7a36 CJK Ideograph-7A36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a39 CJK Ideograph-7A39 | 7a38 CJK Ideograph-7A38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a3b CJK Ideograph-7A3B | 7a3a CJK Ideograph-7A3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a3d CJK Ideograph-7A3D | 7a3c CJK Ideograph-7A3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a3f CJK Ideograph-7A3F | 7a3e CJK Ideograph-7A3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a41 CJK Ideograph-7A41 | 7a40 CJK Ideograph-7A40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a43 CJK Ideograph-7A43 | 7a42 CJK Ideograph-7A42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a45 CJK Ideograph-7A45 | 7a44 CJK Ideograph-7A44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a47 CJK Ideograph-7A47 | 7a46 CJK Ideograph-7A46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a49 CJK Ideograph-7A49 | 7a48 CJK Ideograph-7A48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a4b CJK Ideograph-7A4B | 7a4a CJK Ideograph-7A4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a4d CJK Ideograph-7A4D | 7a4c CJK Ideograph-7A4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a4f CJK Ideograph-7A4F | 7a4e CJK Ideograph-7A4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a51 CJK Ideograph-7A51 | 7a50 CJK Ideograph-7A50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a53 CJK Ideograph-7A53 | 7a52 CJK Ideograph-7A52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a55 CJK Ideograph-7A55 | 7a54 CJK Ideograph-7A54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a57 CJK Ideograph-7A57 | 7a56 CJK Ideograph-7A56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a59 CJK Ideograph-7A59 | 7a58 CJK Ideograph-7A58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a5b CJK Ideograph-7A5B | 7a5a CJK Ideograph-7A5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a5d CJK Ideograph-7A5D | 7a5c CJK Ideograph-7A5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a5f CJK Ideograph-7A5F | 7a5e CJK Ideograph-7A5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a61 CJK Ideograph-7A61 | 7a60 CJK Ideograph-7A60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a63 CJK Ideograph-7A63 | 7a62 CJK Ideograph-7A62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a65 CJK Ideograph-7A65 | 7a64 CJK Ideograph-7A64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a67 CJK Ideograph-7A67 | 7a66 CJK Ideograph-7A66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a69 CJK Ideograph-7A69 | 7a68 CJK Ideograph-7A68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a6b CJK Ideograph-7A6B | 7a6a CJK Ideograph-7A6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a6d CJK Ideograph-7A6D | 7a6c CJK Ideograph-7A6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a6f CJK Ideograph-7A6F | 7a6e CJK Ideograph-7A6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a71 CJK Ideograph-7A71 | 7a70 CJK Ideograph-7A70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a73 CJK Ideograph-7A73 | 7a72 CJK Ideograph-7A72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a75 CJK Ideograph-7A75 | 7a74 CJK Ideograph-7A74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a77 CJK Ideograph-7A77 | 7a76 CJK Ideograph-7A76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a79 CJK Ideograph-7A79 | 7a78 CJK Ideograph-7A78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a7b CJK Ideograph-7A7B | 7a7a CJK Ideograph-7A7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a7d CJK Ideograph-7A7D | 7a7c CJK Ideograph-7A7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a7f CJK Ideograph-7A7F | 7a7e CJK Ideograph-7A7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a81 CJK Ideograph-7A81 | 7a80 CJK Ideograph-7A80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a83 CJK Ideograph-7A83 | 7a82 CJK Ideograph-7A82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a85 CJK Ideograph-7A85 | 7a84 CJK Ideograph-7A84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a87 CJK Ideograph-7A87 | 7a86 CJK Ideograph-7A86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a89 CJK Ideograph-7A89 | 7a88 CJK Ideograph-7A88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a8b CJK Ideograph-7A8B | 7a8a CJK Ideograph-7A8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a8d CJK Ideograph-7A8D | 7a8c CJK Ideograph-7A8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a8f CJK Ideograph-7A8F | 7a8e CJK Ideograph-7A8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a91 CJK Ideograph-7A91 | 7a90 CJK Ideograph-7A90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a93 CJK Ideograph-7A93 | 7a92 CJK Ideograph-7A92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a95 CJK Ideograph-7A95 | 7a94 CJK Ideograph-7A94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a97 CJK Ideograph-7A97 | 7a96 CJK Ideograph-7A96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a99 CJK Ideograph-7A99 | 7a98 CJK Ideograph-7A98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a9b CJK Ideograph-7A9B | 7a9a CJK Ideograph-7A9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a9d CJK Ideograph-7A9D | 7a9c CJK Ideograph-7A9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a9f CJK Ideograph-7A9F | 7a9e CJK Ideograph-7A9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aa1 CJK Ideograph-7AA1 | 7aa0 CJK Ideograph-7AA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aa3 CJK Ideograph-7AA3 | 7aa2 CJK Ideograph-7AA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aa5 CJK Ideograph-7AA5 | 7aa4 CJK Ideograph-7AA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aa7 CJK Ideograph-7AA7 | 7aa6 CJK Ideograph-7AA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aa9 CJK Ideograph-7AA9 | 7aa8 CJK Ideograph-7AA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aab CJK Ideograph-7AAB | 7aaa CJK Ideograph-7AAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aad CJK Ideograph-7AAD | 7aac CJK Ideograph-7AAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aaf CJK Ideograph-7AAF | 7aae CJK Ideograph-7AAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ab1 CJK Ideograph-7AB1 | 7ab0 CJK Ideograph-7AB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ab3 CJK Ideograph-7AB3 | 7ab2 CJK Ideograph-7AB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ab5 CJK Ideograph-7AB5 | 7ab4 CJK Ideograph-7AB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ab7 CJK Ideograph-7AB7 | 7ab6 CJK Ideograph-7AB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ab9 CJK Ideograph-7AB9 | 7ab8 CJK Ideograph-7AB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7abb CJK Ideograph-7ABB | 7aba CJK Ideograph-7ABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7abd CJK Ideograph-7ABD | 7abc CJK Ideograph-7ABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7abf CJK Ideograph-7ABF | 7abe CJK Ideograph-7ABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ac1 CJK Ideograph-7AC1 | 7ac0 CJK Ideograph-7AC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ac3 CJK Ideograph-7AC3 | 7ac2 CJK Ideograph-7AC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ac5 CJK Ideograph-7AC5 | 7ac4 CJK Ideograph-7AC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ac7 CJK Ideograph-7AC7 | 7ac6 CJK Ideograph-7AC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ac9 CJK Ideograph-7AC9 | 7ac8 CJK Ideograph-7AC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7acb CJK Ideograph-7ACB | 7aca CJK Ideograph-7ACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7acd CJK Ideograph-7ACD | 7acc CJK Ideograph-7ACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7acf CJK Ideograph-7ACF | 7ace CJK Ideograph-7ACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ad1 CJK Ideograph-7AD1 | 7ad0 CJK Ideograph-7AD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ad3 CJK Ideograph-7AD3 | 7ad2 CJK Ideograph-7AD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ad5 CJK Ideograph-7AD5 | 7ad4 CJK Ideograph-7AD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ad7 CJK Ideograph-7AD7 | 7ad6 CJK Ideograph-7AD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ad9 CJK Ideograph-7AD9 | 7ad8 CJK Ideograph-7AD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7adb CJK Ideograph-7ADB | 7ada CJK Ideograph-7ADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7add CJK Ideograph-7ADD | 7adc CJK Ideograph-7ADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7adf CJK Ideograph-7ADF | 7ade CJK Ideograph-7ADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ae1 CJK Ideograph-7AE1 | 7ae0 CJK Ideograph-7AE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ae3 CJK Ideograph-7AE3 | 7ae2 CJK Ideograph-7AE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ae5 CJK Ideograph-7AE5 | 7ae4 CJK Ideograph-7AE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ae7 CJK Ideograph-7AE7 | 7ae6 CJK Ideograph-7AE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ae9 CJK Ideograph-7AE9 | 7ae8 CJK Ideograph-7AE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aeb CJK Ideograph-7AEB | 7aea CJK Ideograph-7AEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aed CJK Ideograph-7AED | 7aec CJK Ideograph-7AEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aef CJK Ideograph-7AEF | 7aee CJK Ideograph-7AEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7af1 CJK Ideograph-7AF1 | 7af0 CJK Ideograph-7AF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7af3 CJK Ideograph-7AF3 | 7af2 CJK Ideograph-7AF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7af5 CJK Ideograph-7AF5 | 7af4 CJK Ideograph-7AF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7af7 CJK Ideograph-7AF7 | 7af6 CJK Ideograph-7AF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7af9 CJK Ideograph-7AF9 | 7af8 CJK Ideograph-7AF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7afb CJK Ideograph-7AFB | 7afa CJK Ideograph-7AFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7afd CJK Ideograph-7AFD | 7afc CJK Ideograph-7AFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aff CJK Ideograph-7AFF | 7afe CJK Ideograph-7AFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b01 CJK Ideograph-7B01 | 7b00 CJK Ideograph-7B00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b03 CJK Ideograph-7B03 | 7b02 CJK Ideograph-7B02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b05 CJK Ideograph-7B05 | 7b04 CJK Ideograph-7B04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b07 CJK Ideograph-7B07 | 7b06 CJK Ideograph-7B06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b09 CJK Ideograph-7B09 | 7b08 CJK Ideograph-7B08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b0b CJK Ideograph-7B0B | 7b0a CJK Ideograph-7B0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b0d CJK Ideograph-7B0D | 7b0c CJK Ideograph-7B0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b0f CJK Ideograph-7B0F | 7b0e CJK Ideograph-7B0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b11 CJK Ideograph-7B11 | 7b10 CJK Ideograph-7B10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b13 CJK Ideograph-7B13 | 7b12 CJK Ideograph-7B12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b15 CJK Ideograph-7B15 | 7b14 CJK Ideograph-7B14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b17 CJK Ideograph-7B17 | 7b16 CJK Ideograph-7B16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b19 CJK Ideograph-7B19 | 7b18 CJK Ideograph-7B18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b1b CJK Ideograph-7B1B | 7b1a CJK Ideograph-7B1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b1d CJK Ideograph-7B1D | 7b1c CJK Ideograph-7B1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b1f CJK Ideograph-7B1F | 7b1e CJK Ideograph-7B1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b21 CJK Ideograph-7B21 | 7b20 CJK Ideograph-7B20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b23 CJK Ideograph-7B23 | 7b22 CJK Ideograph-7B22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b25 CJK Ideograph-7B25 | 7b24 CJK Ideograph-7B24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b27 CJK Ideograph-7B27 | 7b26 CJK Ideograph-7B26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b29 CJK Ideograph-7B29 | 7b28 CJK Ideograph-7B28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b2b CJK Ideograph-7B2B | 7b2a CJK Ideograph-7B2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b2d CJK Ideograph-7B2D | 7b2c CJK Ideograph-7B2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b2f CJK Ideograph-7B2F | 7b2e CJK Ideograph-7B2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b31 CJK Ideograph-7B31 | 7b30 CJK Ideograph-7B30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b33 CJK Ideograph-7B33 | 7b32 CJK Ideograph-7B32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b35 CJK Ideograph-7B35 | 7b34 CJK Ideograph-7B34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b37 CJK Ideograph-7B37 | 7b36 CJK Ideograph-7B36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b39 CJK Ideograph-7B39 | 7b38 CJK Ideograph-7B38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b3b CJK Ideograph-7B3B | 7b3a CJK Ideograph-7B3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b3d CJK Ideograph-7B3D | 7b3c CJK Ideograph-7B3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b3f CJK Ideograph-7B3F | 7b3e CJK Ideograph-7B3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b41 CJK Ideograph-7B41 | 7b40 CJK Ideograph-7B40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b43 CJK Ideograph-7B43 | 7b42 CJK Ideograph-7B42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b45 CJK Ideograph-7B45 | 7b44 CJK Ideograph-7B44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b47 CJK Ideograph-7B47 | 7b46 CJK Ideograph-7B46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b49 CJK Ideograph-7B49 | 7b48 CJK Ideograph-7B48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b4b CJK Ideograph-7B4B | 7b4a CJK Ideograph-7B4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b4d CJK Ideograph-7B4D | 7b4c CJK Ideograph-7B4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b4f CJK Ideograph-7B4F | 7b4e CJK Ideograph-7B4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b51 CJK Ideograph-7B51 | 7b50 CJK Ideograph-7B50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b53 CJK Ideograph-7B53 | 7b52 CJK Ideograph-7B52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b55 CJK Ideograph-7B55 | 7b54 CJK Ideograph-7B54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b57 CJK Ideograph-7B57 | 7b56 CJK Ideograph-7B56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b59 CJK Ideograph-7B59 | 7b58 CJK Ideograph-7B58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b5b CJK Ideograph-7B5B | 7b5a CJK Ideograph-7B5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b5d CJK Ideograph-7B5D | 7b5c CJK Ideograph-7B5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b5f CJK Ideograph-7B5F | 7b5e CJK Ideograph-7B5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b61 CJK Ideograph-7B61 | 7b60 CJK Ideograph-7B60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b63 CJK Ideograph-7B63 | 7b62 CJK Ideograph-7B62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b65 CJK Ideograph-7B65 | 7b64 CJK Ideograph-7B64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b67 CJK Ideograph-7B67 | 7b66 CJK Ideograph-7B66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b69 CJK Ideograph-7B69 | 7b68 CJK Ideograph-7B68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b6b CJK Ideograph-7B6B | 7b6a CJK Ideograph-7B6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b6d CJK Ideograph-7B6D | 7b6c CJK Ideograph-7B6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b6f CJK Ideograph-7B6F | 7b6e CJK Ideograph-7B6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b71 CJK Ideograph-7B71 | 7b70 CJK Ideograph-7B70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b73 CJK Ideograph-7B73 | 7b72 CJK Ideograph-7B72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b75 CJK Ideograph-7B75 | 7b74 CJK Ideograph-7B74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b77 CJK Ideograph-7B77 | 7b76 CJK Ideograph-7B76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b79 CJK Ideograph-7B79 | 7b78 CJK Ideograph-7B78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b7b CJK Ideograph-7B7B | 7b7a CJK Ideograph-7B7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b7d CJK Ideograph-7B7D | 7b7c CJK Ideograph-7B7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b7f CJK Ideograph-7B7F | 7b7e CJK Ideograph-7B7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b81 CJK Ideograph-7B81 | 7b80 CJK Ideograph-7B80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b83 CJK Ideograph-7B83 | 7b82 CJK Ideograph-7B82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b85 CJK Ideograph-7B85 | 7b84 CJK Ideograph-7B84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b87 CJK Ideograph-7B87 | 7b86 CJK Ideograph-7B86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b89 CJK Ideograph-7B89 | 7b88 CJK Ideograph-7B88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b8b CJK Ideograph-7B8B | 7b8a CJK Ideograph-7B8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b8d CJK Ideograph-7B8D | 7b8c CJK Ideograph-7B8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b8f CJK Ideograph-7B8F | 7b8e CJK Ideograph-7B8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b91 CJK Ideograph-7B91 | 7b90 CJK Ideograph-7B90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b93 CJK Ideograph-7B93 | 7b92 CJK Ideograph-7B92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b95 CJK Ideograph-7B95 | 7b94 CJK Ideograph-7B94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b97 CJK Ideograph-7B97 | 7b96 CJK Ideograph-7B96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b99 CJK Ideograph-7B99 | 7b98 CJK Ideograph-7B98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b9b CJK Ideograph-7B9B | 7b9a CJK Ideograph-7B9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b9d CJK Ideograph-7B9D | 7b9c CJK Ideograph-7B9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b9f CJK Ideograph-7B9F | 7b9e CJK Ideograph-7B9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ba1 CJK Ideograph-7BA1 | 7ba0 CJK Ideograph-7BA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ba3 CJK Ideograph-7BA3 | 7ba2 CJK Ideograph-7BA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ba5 CJK Ideograph-7BA5 | 7ba4 CJK Ideograph-7BA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ba7 CJK Ideograph-7BA7 | 7ba6 CJK Ideograph-7BA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ba9 CJK Ideograph-7BA9 | 7ba8 CJK Ideograph-7BA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bab CJK Ideograph-7BAB | 7baa CJK Ideograph-7BAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bad CJK Ideograph-7BAD | 7bac CJK Ideograph-7BAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7baf CJK Ideograph-7BAF | 7bae CJK Ideograph-7BAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bb1 CJK Ideograph-7BB1 | 7bb0 CJK Ideograph-7BB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bb3 CJK Ideograph-7BB3 | 7bb2 CJK Ideograph-7BB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bb5 CJK Ideograph-7BB5 | 7bb4 CJK Ideograph-7BB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bb7 CJK Ideograph-7BB7 | 7bb6 CJK Ideograph-7BB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bb9 CJK Ideograph-7BB9 | 7bb8 CJK Ideograph-7BB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bbb CJK Ideograph-7BBB | 7bba CJK Ideograph-7BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bbd CJK Ideograph-7BBD | 7bbc CJK Ideograph-7BBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bbf CJK Ideograph-7BBF | 7bbe CJK Ideograph-7BBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bc1 CJK Ideograph-7BC1 | 7bc0 CJK Ideograph-7BC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bc3 CJK Ideograph-7BC3 | 7bc2 CJK Ideograph-7BC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bc5 CJK Ideograph-7BC5 | 7bc4 CJK Ideograph-7BC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bc7 CJK Ideograph-7BC7 | 7bc6 CJK Ideograph-7BC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bc9 CJK Ideograph-7BC9 | 7bc8 CJK Ideograph-7BC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bcb CJK Ideograph-7BCB | 7bca CJK Ideograph-7BCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bcd CJK Ideograph-7BCD | 7bcc CJK Ideograph-7BCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bcf CJK Ideograph-7BCF | 7bce CJK Ideograph-7BCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bd1 CJK Ideograph-7BD1 | 7bd0 CJK Ideograph-7BD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bd3 CJK Ideograph-7BD3 | 7bd2 CJK Ideograph-7BD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bd5 CJK Ideograph-7BD5 | 7bd4 CJK Ideograph-7BD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bd7 CJK Ideograph-7BD7 | 7bd6 CJK Ideograph-7BD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bd9 CJK Ideograph-7BD9 | 7bd8 CJK Ideograph-7BD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bdb CJK Ideograph-7BDB | 7bda CJK Ideograph-7BDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bdd CJK Ideograph-7BDD | 7bdc CJK Ideograph-7BDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bdf CJK Ideograph-7BDF | 7bde CJK Ideograph-7BDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7be1 CJK Ideograph-7BE1 | 7be0 CJK Ideograph-7BE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7be3 CJK Ideograph-7BE3 | 7be2 CJK Ideograph-7BE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7be5 CJK Ideograph-7BE5 | 7be4 CJK Ideograph-7BE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7be7 CJK Ideograph-7BE7 | 7be6 CJK Ideograph-7BE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7be9 CJK Ideograph-7BE9 | 7be8 CJK Ideograph-7BE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7beb CJK Ideograph-7BEB | 7bea CJK Ideograph-7BEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bed CJK Ideograph-7BED | 7bec CJK Ideograph-7BEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bef CJK Ideograph-7BEF | 7bee CJK Ideograph-7BEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bf1 CJK Ideograph-7BF1 | 7bf0 CJK Ideograph-7BF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bf3 CJK Ideograph-7BF3 | 7bf2 CJK Ideograph-7BF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bf5 CJK Ideograph-7BF5 | 7bf4 CJK Ideograph-7BF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bf7 CJK Ideograph-7BF7 | 7bf6 CJK Ideograph-7BF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bf9 CJK Ideograph-7BF9 | 7bf8 CJK Ideograph-7BF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bfb CJK Ideograph-7BFB | 7bfa CJK Ideograph-7BFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bfd CJK Ideograph-7BFD | 7bfc CJK Ideograph-7BFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bff CJK Ideograph-7BFF | 7bfe CJK Ideograph-7BFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c01 CJK Ideograph-7C01 | 7c00 CJK Ideograph-7C00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c03 CJK Ideograph-7C03 | 7c02 CJK Ideograph-7C02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c05 CJK Ideograph-7C05 | 7c04 CJK Ideograph-7C04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c07 CJK Ideograph-7C07 | 7c06 CJK Ideograph-7C06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c09 CJK Ideograph-7C09 | 7c08 CJK Ideograph-7C08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c0b CJK Ideograph-7C0B | 7c0a CJK Ideograph-7C0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c0d CJK Ideograph-7C0D | 7c0c CJK Ideograph-7C0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c0f CJK Ideograph-7C0F | 7c0e CJK Ideograph-7C0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c11 CJK Ideograph-7C11 | 7c10 CJK Ideograph-7C10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c13 CJK Ideograph-7C13 | 7c12 CJK Ideograph-7C12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c15 CJK Ideograph-7C15 | 7c14 CJK Ideograph-7C14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c17 CJK Ideograph-7C17 | 7c16 CJK Ideograph-7C16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c19 CJK Ideograph-7C19 | 7c18 CJK Ideograph-7C18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c1b CJK Ideograph-7C1B | 7c1a CJK Ideograph-7C1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c1d CJK Ideograph-7C1D | 7c1c CJK Ideograph-7C1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c1f CJK Ideograph-7C1F | 7c1e CJK Ideograph-7C1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c21 CJK Ideograph-7C21 | 7c20 CJK Ideograph-7C20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c23 CJK Ideograph-7C23 | 7c22 CJK Ideograph-7C22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c25 CJK Ideograph-7C25 | 7c24 CJK Ideograph-7C24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c27 CJK Ideograph-7C27 | 7c26 CJK Ideograph-7C26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c29 CJK Ideograph-7C29 | 7c28 CJK Ideograph-7C28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c2b CJK Ideograph-7C2B | 7c2a CJK Ideograph-7C2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c2d CJK Ideograph-7C2D | 7c2c CJK Ideograph-7C2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c2f CJK Ideograph-7C2F | 7c2e CJK Ideograph-7C2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c31 CJK Ideograph-7C31 | 7c30 CJK Ideograph-7C30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c33 CJK Ideograph-7C33 | 7c32 CJK Ideograph-7C32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c35 CJK Ideograph-7C35 | 7c34 CJK Ideograph-7C34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c37 CJK Ideograph-7C37 | 7c36 CJK Ideograph-7C36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c39 CJK Ideograph-7C39 | 7c38 CJK Ideograph-7C38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c3b CJK Ideograph-7C3B | 7c3a CJK Ideograph-7C3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c3d CJK Ideograph-7C3D | 7c3c CJK Ideograph-7C3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c3f CJK Ideograph-7C3F | 7c3e CJK Ideograph-7C3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c41 CJK Ideograph-7C41 | 7c40 CJK Ideograph-7C40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c43 CJK Ideograph-7C43 | 7c42 CJK Ideograph-7C42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c45 CJK Ideograph-7C45 | 7c44 CJK Ideograph-7C44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c47 CJK Ideograph-7C47 | 7c46 CJK Ideograph-7C46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c49 CJK Ideograph-7C49 | 7c48 CJK Ideograph-7C48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c4b CJK Ideograph-7C4B | 7c4a CJK Ideograph-7C4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c4d CJK Ideograph-7C4D | 7c4c CJK Ideograph-7C4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c4f CJK Ideograph-7C4F | 7c4e CJK Ideograph-7C4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c51 CJK Ideograph-7C51 | 7c50 CJK Ideograph-7C50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c53 CJK Ideograph-7C53 | 7c52 CJK Ideograph-7C52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c55 CJK Ideograph-7C55 | 7c54 CJK Ideograph-7C54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c57 CJK Ideograph-7C57 | 7c56 CJK Ideograph-7C56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c59 CJK Ideograph-7C59 | 7c58 CJK Ideograph-7C58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c5b CJK Ideograph-7C5B | 7c5a CJK Ideograph-7C5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c5d CJK Ideograph-7C5D | 7c5c CJK Ideograph-7C5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c5f CJK Ideograph-7C5F | 7c5e CJK Ideograph-7C5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c61 CJK Ideograph-7C61 | 7c60 CJK Ideograph-7C60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c63 CJK Ideograph-7C63 | 7c62 CJK Ideograph-7C62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c65 CJK Ideograph-7C65 | 7c64 CJK Ideograph-7C64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c67 CJK Ideograph-7C67 | 7c66 CJK Ideograph-7C66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c69 CJK Ideograph-7C69 | 7c68 CJK Ideograph-7C68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c6b CJK Ideograph-7C6B | 7c6a CJK Ideograph-7C6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c6d CJK Ideograph-7C6D | 7c6c CJK Ideograph-7C6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c6f CJK Ideograph-7C6F | 7c6e CJK Ideograph-7C6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c71 CJK Ideograph-7C71 | 7c70 CJK Ideograph-7C70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c73 CJK Ideograph-7C73 | 7c72 CJK Ideograph-7C72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c75 CJK Ideograph-7C75 | 7c74 CJK Ideograph-7C74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c77 CJK Ideograph-7C77 | 7c76 CJK Ideograph-7C76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c79 CJK Ideograph-7C79 | 7c78 CJK Ideograph-7C78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c7b CJK Ideograph-7C7B | 7c7a CJK Ideograph-7C7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c7d CJK Ideograph-7C7D | 7c7c CJK Ideograph-7C7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c7f CJK Ideograph-7C7F | 7c7e CJK Ideograph-7C7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c81 CJK Ideograph-7C81 | 7c80 CJK Ideograph-7C80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c83 CJK Ideograph-7C83 | 7c82 CJK Ideograph-7C82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c85 CJK Ideograph-7C85 | 7c84 CJK Ideograph-7C84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c87 CJK Ideograph-7C87 | 7c86 CJK Ideograph-7C86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c89 CJK Ideograph-7C89 | 7c88 CJK Ideograph-7C88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c8b CJK Ideograph-7C8B | 7c8a CJK Ideograph-7C8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c8d CJK Ideograph-7C8D | 7c8c CJK Ideograph-7C8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c8f CJK Ideograph-7C8F | 7c8e CJK Ideograph-7C8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c91 CJK Ideograph-7C91 | 7c90 CJK Ideograph-7C90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c93 CJK Ideograph-7C93 | 7c92 CJK Ideograph-7C92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c95 CJK Ideograph-7C95 | 7c94 CJK Ideograph-7C94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c97 CJK Ideograph-7C97 | 7c96 CJK Ideograph-7C96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c99 CJK Ideograph-7C99 | 7c98 CJK Ideograph-7C98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c9b CJK Ideograph-7C9B | 7c9a CJK Ideograph-7C9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c9d CJK Ideograph-7C9D | 7c9c CJK Ideograph-7C9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c9f CJK Ideograph-7C9F | 7c9e CJK Ideograph-7C9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ca1 CJK Ideograph-7CA1 | 7ca0 CJK Ideograph-7CA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ca3 CJK Ideograph-7CA3 | 7ca2 CJK Ideograph-7CA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ca5 CJK Ideograph-7CA5 | 7ca4 CJK Ideograph-7CA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ca7 CJK Ideograph-7CA7 | 7ca6 CJK Ideograph-7CA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ca9 CJK Ideograph-7CA9 | 7ca8 CJK Ideograph-7CA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cab CJK Ideograph-7CAB | 7caa CJK Ideograph-7CAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cad CJK Ideograph-7CAD | 7cac CJK Ideograph-7CAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7caf CJK Ideograph-7CAF | 7cae CJK Ideograph-7CAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cb1 CJK Ideograph-7CB1 | 7cb0 CJK Ideograph-7CB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cb3 CJK Ideograph-7CB3 | 7cb2 CJK Ideograph-7CB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cb5 CJK Ideograph-7CB5 | 7cb4 CJK Ideograph-7CB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cb7 CJK Ideograph-7CB7 | 7cb6 CJK Ideograph-7CB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cb9 CJK Ideograph-7CB9 | 7cb8 CJK Ideograph-7CB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cbb CJK Ideograph-7CBB | 7cba CJK Ideograph-7CBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cbd CJK Ideograph-7CBD | 7cbc CJK Ideograph-7CBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cbf CJK Ideograph-7CBF | 7cbe CJK Ideograph-7CBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cc1 CJK Ideograph-7CC1 | 7cc0 CJK Ideograph-7CC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cc3 CJK Ideograph-7CC3 | 7cc2 CJK Ideograph-7CC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cc5 CJK Ideograph-7CC5 | 7cc4 CJK Ideograph-7CC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cc7 CJK Ideograph-7CC7 | 7cc6 CJK Ideograph-7CC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cc9 CJK Ideograph-7CC9 | 7cc8 CJK Ideograph-7CC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ccb CJK Ideograph-7CCB | 7cca CJK Ideograph-7CCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ccd CJK Ideograph-7CCD | 7ccc CJK Ideograph-7CCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ccf CJK Ideograph-7CCF | 7cce CJK Ideograph-7CCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cd1 CJK Ideograph-7CD1 | 7cd0 CJK Ideograph-7CD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cd3 CJK Ideograph-7CD3 | 7cd2 CJK Ideograph-7CD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cd5 CJK Ideograph-7CD5 | 7cd4 CJK Ideograph-7CD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cd7 CJK Ideograph-7CD7 | 7cd6 CJK Ideograph-7CD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cd9 CJK Ideograph-7CD9 | 7cd8 CJK Ideograph-7CD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cdb CJK Ideograph-7CDB | 7cda CJK Ideograph-7CDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cdd CJK Ideograph-7CDD | 7cdc CJK Ideograph-7CDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cdf CJK Ideograph-7CDF | 7cde CJK Ideograph-7CDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ce1 CJK Ideograph-7CE1 | 7ce0 CJK Ideograph-7CE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ce3 CJK Ideograph-7CE3 | 7ce2 CJK Ideograph-7CE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ce5 CJK Ideograph-7CE5 | 7ce4 CJK Ideograph-7CE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ce7 CJK Ideograph-7CE7 | 7ce6 CJK Ideograph-7CE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ce9 CJK Ideograph-7CE9 | 7ce8 CJK Ideograph-7CE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ceb CJK Ideograph-7CEB | 7cea CJK Ideograph-7CEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ced CJK Ideograph-7CED | 7cec CJK Ideograph-7CEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cef CJK Ideograph-7CEF | 7cee CJK Ideograph-7CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cf1 CJK Ideograph-7CF1 | 7cf0 CJK Ideograph-7CF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cf3 CJK Ideograph-7CF3 | 7cf2 CJK Ideograph-7CF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cf5 CJK Ideograph-7CF5 | 7cf4 CJK Ideograph-7CF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cf7 CJK Ideograph-7CF7 | 7cf6 CJK Ideograph-7CF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cf9 CJK Ideograph-7CF9 | 7cf8 CJK Ideograph-7CF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cfb CJK Ideograph-7CFB | 7cfa CJK Ideograph-7CFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cfd CJK Ideograph-7CFD | 7cfc CJK Ideograph-7CFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cff CJK Ideograph-7CFF | 7cfe CJK Ideograph-7CFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d01 CJK Ideograph-7D01 | 7d00 CJK Ideograph-7D00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d03 CJK Ideograph-7D03 | 7d02 CJK Ideograph-7D02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d05 CJK Ideograph-7D05 | 7d04 CJK Ideograph-7D04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d07 CJK Ideograph-7D07 | 7d06 CJK Ideograph-7D06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d09 CJK Ideograph-7D09 | 7d08 CJK Ideograph-7D08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d0b CJK Ideograph-7D0B | 7d0a CJK Ideograph-7D0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d0d CJK Ideograph-7D0D | 7d0c CJK Ideograph-7D0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d0f CJK Ideograph-7D0F | 7d0e CJK Ideograph-7D0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d11 CJK Ideograph-7D11 | 7d10 CJK Ideograph-7D10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d13 CJK Ideograph-7D13 | 7d12 CJK Ideograph-7D12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d15 CJK Ideograph-7D15 | 7d14 CJK Ideograph-7D14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d17 CJK Ideograph-7D17 | 7d16 CJK Ideograph-7D16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d19 CJK Ideograph-7D19 | 7d18 CJK Ideograph-7D18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d1b CJK Ideograph-7D1B | 7d1a CJK Ideograph-7D1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d1d CJK Ideograph-7D1D | 7d1c CJK Ideograph-7D1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d1f CJK Ideograph-7D1F | 7d1e CJK Ideograph-7D1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d21 CJK Ideograph-7D21 | 7d20 CJK Ideograph-7D20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d23 CJK Ideograph-7D23 | 7d22 CJK Ideograph-7D22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d25 CJK Ideograph-7D25 | 7d24 CJK Ideograph-7D24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d27 CJK Ideograph-7D27 | 7d26 CJK Ideograph-7D26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d29 CJK Ideograph-7D29 | 7d28 CJK Ideograph-7D28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d2b CJK Ideograph-7D2B | 7d2a CJK Ideograph-7D2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d2d CJK Ideograph-7D2D | 7d2c CJK Ideograph-7D2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d2f CJK Ideograph-7D2F | 7d2e CJK Ideograph-7D2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d31 CJK Ideograph-7D31 | 7d30 CJK Ideograph-7D30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d33 CJK Ideograph-7D33 | 7d32 CJK Ideograph-7D32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d35 CJK Ideograph-7D35 | 7d34 CJK Ideograph-7D34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d37 CJK Ideograph-7D37 | 7d36 CJK Ideograph-7D36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d39 CJK Ideograph-7D39 | 7d38 CJK Ideograph-7D38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d3b CJK Ideograph-7D3B | 7d3a CJK Ideograph-7D3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d3d CJK Ideograph-7D3D | 7d3c CJK Ideograph-7D3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d3f CJK Ideograph-7D3F | 7d3e CJK Ideograph-7D3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d41 CJK Ideograph-7D41 | 7d40 CJK Ideograph-7D40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d43 CJK Ideograph-7D43 | 7d42 CJK Ideograph-7D42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d45 CJK Ideograph-7D45 | 7d44 CJK Ideograph-7D44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d47 CJK Ideograph-7D47 | 7d46 CJK Ideograph-7D46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d49 CJK Ideograph-7D49 | 7d48 CJK Ideograph-7D48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d4b CJK Ideograph-7D4B | 7d4a CJK Ideograph-7D4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d4d CJK Ideograph-7D4D | 7d4c CJK Ideograph-7D4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d4f CJK Ideograph-7D4F | 7d4e CJK Ideograph-7D4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d51 CJK Ideograph-7D51 | 7d50 CJK Ideograph-7D50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d53 CJK Ideograph-7D53 | 7d52 CJK Ideograph-7D52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d55 CJK Ideograph-7D55 | 7d54 CJK Ideograph-7D54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d57 CJK Ideograph-7D57 | 7d56 CJK Ideograph-7D56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d59 CJK Ideograph-7D59 | 7d58 CJK Ideograph-7D58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d5b CJK Ideograph-7D5B | 7d5a CJK Ideograph-7D5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d5d CJK Ideograph-7D5D | 7d5c CJK Ideograph-7D5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d5f CJK Ideograph-7D5F | 7d5e CJK Ideograph-7D5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d61 CJK Ideograph-7D61 | 7d60 CJK Ideograph-7D60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d63 CJK Ideograph-7D63 | 7d62 CJK Ideograph-7D62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d65 CJK Ideograph-7D65 | 7d64 CJK Ideograph-7D64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d67 CJK Ideograph-7D67 | 7d66 CJK Ideograph-7D66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d69 CJK Ideograph-7D69 | 7d68 CJK Ideograph-7D68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d6b CJK Ideograph-7D6B | 7d6a CJK Ideograph-7D6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d6d CJK Ideograph-7D6D | 7d6c CJK Ideograph-7D6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d6f CJK Ideograph-7D6F | 7d6e CJK Ideograph-7D6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d71 CJK Ideograph-7D71 | 7d70 CJK Ideograph-7D70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d73 CJK Ideograph-7D73 | 7d72 CJK Ideograph-7D72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d75 CJK Ideograph-7D75 | 7d74 CJK Ideograph-7D74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d77 CJK Ideograph-7D77 | 7d76 CJK Ideograph-7D76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d79 CJK Ideograph-7D79 | 7d78 CJK Ideograph-7D78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d7b CJK Ideograph-7D7B | 7d7a CJK Ideograph-7D7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d7d CJK Ideograph-7D7D | 7d7c CJK Ideograph-7D7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d7f CJK Ideograph-7D7F | 7d7e CJK Ideograph-7D7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d81 CJK Ideograph-7D81 | 7d80 CJK Ideograph-7D80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d83 CJK Ideograph-7D83 | 7d82 CJK Ideograph-7D82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d85 CJK Ideograph-7D85 | 7d84 CJK Ideograph-7D84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d87 CJK Ideograph-7D87 | 7d86 CJK Ideograph-7D86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d89 CJK Ideograph-7D89 | 7d88 CJK Ideograph-7D88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d8b CJK Ideograph-7D8B | 7d8a CJK Ideograph-7D8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d8d CJK Ideograph-7D8D | 7d8c CJK Ideograph-7D8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d8f CJK Ideograph-7D8F | 7d8e CJK Ideograph-7D8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d91 CJK Ideograph-7D91 | 7d90 CJK Ideograph-7D90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d93 CJK Ideograph-7D93 | 7d92 CJK Ideograph-7D92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d95 CJK Ideograph-7D95 | 7d94 CJK Ideograph-7D94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d97 CJK Ideograph-7D97 | 7d96 CJK Ideograph-7D96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d99 CJK Ideograph-7D99 | 7d98 CJK Ideograph-7D98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d9b CJK Ideograph-7D9B | 7d9a CJK Ideograph-7D9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d9d CJK Ideograph-7D9D | 7d9c CJK Ideograph-7D9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d9f CJK Ideograph-7D9F | 7d9e CJK Ideograph-7D9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7da1 CJK Ideograph-7DA1 | 7da0 CJK Ideograph-7DA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7da3 CJK Ideograph-7DA3 | 7da2 CJK Ideograph-7DA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7da5 CJK Ideograph-7DA5 | 7da4 CJK Ideograph-7DA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7da7 CJK Ideograph-7DA7 | 7da6 CJK Ideograph-7DA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7da9 CJK Ideograph-7DA9 | 7da8 CJK Ideograph-7DA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dab CJK Ideograph-7DAB | 7daa CJK Ideograph-7DAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dad CJK Ideograph-7DAD | 7dac CJK Ideograph-7DAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7daf CJK Ideograph-7DAF | 7dae CJK Ideograph-7DAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7db1 CJK Ideograph-7DB1 | 7db0 CJK Ideograph-7DB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7db3 CJK Ideograph-7DB3 | 7db2 CJK Ideograph-7DB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7db5 CJK Ideograph-7DB5 | 7db4 CJK Ideograph-7DB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7db7 CJK Ideograph-7DB7 | 7db6 CJK Ideograph-7DB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7db9 CJK Ideograph-7DB9 | 7db8 CJK Ideograph-7DB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dbb CJK Ideograph-7DBB | 7dba CJK Ideograph-7DBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dbd CJK Ideograph-7DBD | 7dbc CJK Ideograph-7DBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dbf CJK Ideograph-7DBF | 7dbe CJK Ideograph-7DBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dc1 CJK Ideograph-7DC1 | 7dc0 CJK Ideograph-7DC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dc3 CJK Ideograph-7DC3 | 7dc2 CJK Ideograph-7DC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dc5 CJK Ideograph-7DC5 | 7dc4 CJK Ideograph-7DC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dc7 CJK Ideograph-7DC7 | 7dc6 CJK Ideograph-7DC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dc9 CJK Ideograph-7DC9 | 7dc8 CJK Ideograph-7DC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dcb CJK Ideograph-7DCB | 7dca CJK Ideograph-7DCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dcd CJK Ideograph-7DCD | 7dcc CJK Ideograph-7DCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dcf CJK Ideograph-7DCF | 7dce CJK Ideograph-7DCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dd1 CJK Ideograph-7DD1 | 7dd0 CJK Ideograph-7DD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dd3 CJK Ideograph-7DD3 | 7dd2 CJK Ideograph-7DD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dd5 CJK Ideograph-7DD5 | 7dd4 CJK Ideograph-7DD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dd7 CJK Ideograph-7DD7 | 7dd6 CJK Ideograph-7DD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dd9 CJK Ideograph-7DD9 | 7dd8 CJK Ideograph-7DD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ddb CJK Ideograph-7DDB | 7dda CJK Ideograph-7DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ddd CJK Ideograph-7DDD | 7ddc CJK Ideograph-7DDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ddf CJK Ideograph-7DDF | 7dde CJK Ideograph-7DDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7de1 CJK Ideograph-7DE1 | 7de0 CJK Ideograph-7DE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7de3 CJK Ideograph-7DE3 | 7de2 CJK Ideograph-7DE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7de5 CJK Ideograph-7DE5 | 7de4 CJK Ideograph-7DE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7de7 CJK Ideograph-7DE7 | 7de6 CJK Ideograph-7DE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7de9 CJK Ideograph-7DE9 | 7de8 CJK Ideograph-7DE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7deb CJK Ideograph-7DEB | 7dea CJK Ideograph-7DEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ded CJK Ideograph-7DED | 7dec CJK Ideograph-7DEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7def CJK Ideograph-7DEF | 7dee CJK Ideograph-7DEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7df1 CJK Ideograph-7DF1 | 7df0 CJK Ideograph-7DF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7df3 CJK Ideograph-7DF3 | 7df2 CJK Ideograph-7DF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7df5 CJK Ideograph-7DF5 | 7df4 CJK Ideograph-7DF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7df7 CJK Ideograph-7DF7 | 7df6 CJK Ideograph-7DF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7df9 CJK Ideograph-7DF9 | 7df8 CJK Ideograph-7DF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dfb CJK Ideograph-7DFB | 7dfa CJK Ideograph-7DFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dfd CJK Ideograph-7DFD | 7dfc CJK Ideograph-7DFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dff CJK Ideograph-7DFF | 7dfe CJK Ideograph-7DFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e01 CJK Ideograph-7E01 | 7e00 CJK Ideograph-7E00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e03 CJK Ideograph-7E03 | 7e02 CJK Ideograph-7E02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e05 CJK Ideograph-7E05 | 7e04 CJK Ideograph-7E04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e07 CJK Ideograph-7E07 | 7e06 CJK Ideograph-7E06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e09 CJK Ideograph-7E09 | 7e08 CJK Ideograph-7E08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e0b CJK Ideograph-7E0B | 7e0a CJK Ideograph-7E0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e0d CJK Ideograph-7E0D | 7e0c CJK Ideograph-7E0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e0f CJK Ideograph-7E0F | 7e0e CJK Ideograph-7E0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e11 CJK Ideograph-7E11 | 7e10 CJK Ideograph-7E10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e13 CJK Ideograph-7E13 | 7e12 CJK Ideograph-7E12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e15 CJK Ideograph-7E15 | 7e14 CJK Ideograph-7E14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e17 CJK Ideograph-7E17 | 7e16 CJK Ideograph-7E16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e19 CJK Ideograph-7E19 | 7e18 CJK Ideograph-7E18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e1b CJK Ideograph-7E1B | 7e1a CJK Ideograph-7E1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e1d CJK Ideograph-7E1D | 7e1c CJK Ideograph-7E1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e1f CJK Ideograph-7E1F | 7e1e CJK Ideograph-7E1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e21 CJK Ideograph-7E21 | 7e20 CJK Ideograph-7E20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e23 CJK Ideograph-7E23 | 7e22 CJK Ideograph-7E22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e25 CJK Ideograph-7E25 | 7e24 CJK Ideograph-7E24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e27 CJK Ideograph-7E27 | 7e26 CJK Ideograph-7E26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e29 CJK Ideograph-7E29 | 7e28 CJK Ideograph-7E28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e2b CJK Ideograph-7E2B | 7e2a CJK Ideograph-7E2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e2d CJK Ideograph-7E2D | 7e2c CJK Ideograph-7E2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e2f CJK Ideograph-7E2F | 7e2e CJK Ideograph-7E2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e31 CJK Ideograph-7E31 | 7e30 CJK Ideograph-7E30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e33 CJK Ideograph-7E33 | 7e32 CJK Ideograph-7E32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e35 CJK Ideograph-7E35 | 7e34 CJK Ideograph-7E34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e37 CJK Ideograph-7E37 | 7e36 CJK Ideograph-7E36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e39 CJK Ideograph-7E39 | 7e38 CJK Ideograph-7E38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e3b CJK Ideograph-7E3B | 7e3a CJK Ideograph-7E3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e3d CJK Ideograph-7E3D | 7e3c CJK Ideograph-7E3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e3f CJK Ideograph-7E3F | 7e3e CJK Ideograph-7E3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e41 CJK Ideograph-7E41 | 7e40 CJK Ideograph-7E40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e43 CJK Ideograph-7E43 | 7e42 CJK Ideograph-7E42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e45 CJK Ideograph-7E45 | 7e44 CJK Ideograph-7E44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e47 CJK Ideograph-7E47 | 7e46 CJK Ideograph-7E46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e49 CJK Ideograph-7E49 | 7e48 CJK Ideograph-7E48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e4b CJK Ideograph-7E4B | 7e4a CJK Ideograph-7E4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e4d CJK Ideograph-7E4D | 7e4c CJK Ideograph-7E4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e4f CJK Ideograph-7E4F | 7e4e CJK Ideograph-7E4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e51 CJK Ideograph-7E51 | 7e50 CJK Ideograph-7E50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e53 CJK Ideograph-7E53 | 7e52 CJK Ideograph-7E52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e55 CJK Ideograph-7E55 | 7e54 CJK Ideograph-7E54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e57 CJK Ideograph-7E57 | 7e56 CJK Ideograph-7E56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e59 CJK Ideograph-7E59 | 7e58 CJK Ideograph-7E58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e5b CJK Ideograph-7E5B | 7e5a CJK Ideograph-7E5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e5d CJK Ideograph-7E5D | 7e5c CJK Ideograph-7E5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e5f CJK Ideograph-7E5F | 7e5e CJK Ideograph-7E5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e61 CJK Ideograph-7E61 | 7e60 CJK Ideograph-7E60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e63 CJK Ideograph-7E63 | 7e62 CJK Ideograph-7E62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e65 CJK Ideograph-7E65 | 7e64 CJK Ideograph-7E64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e67 CJK Ideograph-7E67 | 7e66 CJK Ideograph-7E66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e69 CJK Ideograph-7E69 | 7e68 CJK Ideograph-7E68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e6b CJK Ideograph-7E6B | 7e6a CJK Ideograph-7E6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e6d CJK Ideograph-7E6D | 7e6c CJK Ideograph-7E6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e6f CJK Ideograph-7E6F | 7e6e CJK Ideograph-7E6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e71 CJK Ideograph-7E71 | 7e70 CJK Ideograph-7E70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e73 CJK Ideograph-7E73 | 7e72 CJK Ideograph-7E72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e75 CJK Ideograph-7E75 | 7e74 CJK Ideograph-7E74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e77 CJK Ideograph-7E77 | 7e76 CJK Ideograph-7E76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e79 CJK Ideograph-7E79 | 7e78 CJK Ideograph-7E78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e7b CJK Ideograph-7E7B | 7e7a CJK Ideograph-7E7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e7d CJK Ideograph-7E7D | 7e7c CJK Ideograph-7E7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e7f CJK Ideograph-7E7F | 7e7e CJK Ideograph-7E7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e81 CJK Ideograph-7E81 | 7e80 CJK Ideograph-7E80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e83 CJK Ideograph-7E83 | 7e82 CJK Ideograph-7E82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e85 CJK Ideograph-7E85 | 7e84 CJK Ideograph-7E84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e87 CJK Ideograph-7E87 | 7e86 CJK Ideograph-7E86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e89 CJK Ideograph-7E89 | 7e88 CJK Ideograph-7E88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e8b CJK Ideograph-7E8B | 7e8a CJK Ideograph-7E8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e8d CJK Ideograph-7E8D | 7e8c CJK Ideograph-7E8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e8f CJK Ideograph-7E8F | 7e8e CJK Ideograph-7E8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e91 CJK Ideograph-7E91 | 7e90 CJK Ideograph-7E90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e93 CJK Ideograph-7E93 | 7e92 CJK Ideograph-7E92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e95 CJK Ideograph-7E95 | 7e94 CJK Ideograph-7E94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e97 CJK Ideograph-7E97 | 7e96 CJK Ideograph-7E96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e99 CJK Ideograph-7E99 | 7e98 CJK Ideograph-7E98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e9b CJK Ideograph-7E9B | 7e9a CJK Ideograph-7E9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e9d CJK Ideograph-7E9D | 7e9c CJK Ideograph-7E9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e9f CJK Ideograph-7E9F | 7e9e CJK Ideograph-7E9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ea1 CJK Ideograph-7EA1 | 7ea0 CJK Ideograph-7EA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ea3 CJK Ideograph-7EA3 | 7ea2 CJK Ideograph-7EA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ea5 CJK Ideograph-7EA5 | 7ea4 CJK Ideograph-7EA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ea7 CJK Ideograph-7EA7 | 7ea6 CJK Ideograph-7EA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ea9 CJK Ideograph-7EA9 | 7ea8 CJK Ideograph-7EA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eab CJK Ideograph-7EAB | 7eaa CJK Ideograph-7EAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ead CJK Ideograph-7EAD | 7eac CJK Ideograph-7EAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eaf CJK Ideograph-7EAF | 7eae CJK Ideograph-7EAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eb1 CJK Ideograph-7EB1 | 7eb0 CJK Ideograph-7EB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eb3 CJK Ideograph-7EB3 | 7eb2 CJK Ideograph-7EB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eb5 CJK Ideograph-7EB5 | 7eb4 CJK Ideograph-7EB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eb7 CJK Ideograph-7EB7 | 7eb6 CJK Ideograph-7EB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eb9 CJK Ideograph-7EB9 | 7eb8 CJK Ideograph-7EB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ebb CJK Ideograph-7EBB | 7eba CJK Ideograph-7EBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ebd CJK Ideograph-7EBD | 7ebc CJK Ideograph-7EBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ebf CJK Ideograph-7EBF | 7ebe CJK Ideograph-7EBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ec1 CJK Ideograph-7EC1 | 7ec0 CJK Ideograph-7EC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ec3 CJK Ideograph-7EC3 | 7ec2 CJK Ideograph-7EC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ec5 CJK Ideograph-7EC5 | 7ec4 CJK Ideograph-7EC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ec7 CJK Ideograph-7EC7 | 7ec6 CJK Ideograph-7EC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ec9 CJK Ideograph-7EC9 | 7ec8 CJK Ideograph-7EC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ecb CJK Ideograph-7ECB | 7eca CJK Ideograph-7ECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ecd CJK Ideograph-7ECD | 7ecc CJK Ideograph-7ECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ecf CJK Ideograph-7ECF | 7ece CJK Ideograph-7ECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ed1 CJK Ideograph-7ED1 | 7ed0 CJK Ideograph-7ED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ed3 CJK Ideograph-7ED3 | 7ed2 CJK Ideograph-7ED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ed5 CJK Ideograph-7ED5 | 7ed4 CJK Ideograph-7ED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ed7 CJK Ideograph-7ED7 | 7ed6 CJK Ideograph-7ED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ed9 CJK Ideograph-7ED9 | 7ed8 CJK Ideograph-7ED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7edb CJK Ideograph-7EDB | 7eda CJK Ideograph-7EDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7edd CJK Ideograph-7EDD | 7edc CJK Ideograph-7EDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7edf CJK Ideograph-7EDF | 7ede CJK Ideograph-7EDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ee1 CJK Ideograph-7EE1 | 7ee0 CJK Ideograph-7EE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ee3 CJK Ideograph-7EE3 | 7ee2 CJK Ideograph-7EE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ee5 CJK Ideograph-7EE5 | 7ee4 CJK Ideograph-7EE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ee7 CJK Ideograph-7EE7 | 7ee6 CJK Ideograph-7EE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ee9 CJK Ideograph-7EE9 | 7ee8 CJK Ideograph-7EE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eeb CJK Ideograph-7EEB | 7eea CJK Ideograph-7EEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eed CJK Ideograph-7EED | 7eec CJK Ideograph-7EEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eef CJK Ideograph-7EEF | 7eee CJK Ideograph-7EEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ef1 CJK Ideograph-7EF1 | 7ef0 CJK Ideograph-7EF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ef3 CJK Ideograph-7EF3 | 7ef2 CJK Ideograph-7EF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ef5 CJK Ideograph-7EF5 | 7ef4 CJK Ideograph-7EF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ef7 CJK Ideograph-7EF7 | 7ef6 CJK Ideograph-7EF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ef9 CJK Ideograph-7EF9 | 7ef8 CJK Ideograph-7EF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7efb CJK Ideograph-7EFB | 7efa CJK Ideograph-7EFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7efd CJK Ideograph-7EFD | 7efc CJK Ideograph-7EFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eff CJK Ideograph-7EFF | 7efe CJK Ideograph-7EFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f01 CJK Ideograph-7F01 | 7f00 CJK Ideograph-7F00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f03 CJK Ideograph-7F03 | 7f02 CJK Ideograph-7F02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f05 CJK Ideograph-7F05 | 7f04 CJK Ideograph-7F04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f07 CJK Ideograph-7F07 | 7f06 CJK Ideograph-7F06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f09 CJK Ideograph-7F09 | 7f08 CJK Ideograph-7F08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f0b CJK Ideograph-7F0B | 7f0a CJK Ideograph-7F0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f0d CJK Ideograph-7F0D | 7f0c CJK Ideograph-7F0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f0f CJK Ideograph-7F0F | 7f0e CJK Ideograph-7F0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f11 CJK Ideograph-7F11 | 7f10 CJK Ideograph-7F10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f13 CJK Ideograph-7F13 | 7f12 CJK Ideograph-7F12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f15 CJK Ideograph-7F15 | 7f14 CJK Ideograph-7F14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f17 CJK Ideograph-7F17 | 7f16 CJK Ideograph-7F16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f19 CJK Ideograph-7F19 | 7f18 CJK Ideograph-7F18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f1b CJK Ideograph-7F1B | 7f1a CJK Ideograph-7F1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f1d CJK Ideograph-7F1D | 7f1c CJK Ideograph-7F1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f1f CJK Ideograph-7F1F | 7f1e CJK Ideograph-7F1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f21 CJK Ideograph-7F21 | 7f20 CJK Ideograph-7F20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f23 CJK Ideograph-7F23 | 7f22 CJK Ideograph-7F22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f25 CJK Ideograph-7F25 | 7f24 CJK Ideograph-7F24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f27 CJK Ideograph-7F27 | 7f26 CJK Ideograph-7F26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f29 CJK Ideograph-7F29 | 7f28 CJK Ideograph-7F28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f2b CJK Ideograph-7F2B | 7f2a CJK Ideograph-7F2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f2d CJK Ideograph-7F2D | 7f2c CJK Ideograph-7F2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f2f CJK Ideograph-7F2F | 7f2e CJK Ideograph-7F2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f31 CJK Ideograph-7F31 | 7f30 CJK Ideograph-7F30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f33 CJK Ideograph-7F33 | 7f32 CJK Ideograph-7F32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f35 CJK Ideograph-7F35 | 7f34 CJK Ideograph-7F34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f37 CJK Ideograph-7F37 | 7f36 CJK Ideograph-7F36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f39 CJK Ideograph-7F39 | 7f38 CJK Ideograph-7F38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f3b CJK Ideograph-7F3B | 7f3a CJK Ideograph-7F3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f3d CJK Ideograph-7F3D | 7f3c CJK Ideograph-7F3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f3f CJK Ideograph-7F3F | 7f3e CJK Ideograph-7F3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f41 CJK Ideograph-7F41 | 7f40 CJK Ideograph-7F40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f43 CJK Ideograph-7F43 | 7f42 CJK Ideograph-7F42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f45 CJK Ideograph-7F45 | 7f44 CJK Ideograph-7F44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f47 CJK Ideograph-7F47 | 7f46 CJK Ideograph-7F46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f49 CJK Ideograph-7F49 | 7f48 CJK Ideograph-7F48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f4b CJK Ideograph-7F4B | 7f4a CJK Ideograph-7F4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f4d CJK Ideograph-7F4D | 7f4c CJK Ideograph-7F4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f4f CJK Ideograph-7F4F | 7f4e CJK Ideograph-7F4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f51 CJK Ideograph-7F51 | 7f50 CJK Ideograph-7F50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f53 CJK Ideograph-7F53 | 7f52 CJK Ideograph-7F52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f55 CJK Ideograph-7F55 | 7f54 CJK Ideograph-7F54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f57 CJK Ideograph-7F57 | 7f56 CJK Ideograph-7F56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f59 CJK Ideograph-7F59 | 7f58 CJK Ideograph-7F58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f5b CJK Ideograph-7F5B | 7f5a CJK Ideograph-7F5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f5d CJK Ideograph-7F5D | 7f5c CJK Ideograph-7F5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f5f CJK Ideograph-7F5F | 7f5e CJK Ideograph-7F5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f61 CJK Ideograph-7F61 | 7f60 CJK Ideograph-7F60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f63 CJK Ideograph-7F63 | 7f62 CJK Ideograph-7F62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f65 CJK Ideograph-7F65 | 7f64 CJK Ideograph-7F64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f67 CJK Ideograph-7F67 | 7f66 CJK Ideograph-7F66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f69 CJK Ideograph-7F69 | 7f68 CJK Ideograph-7F68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f6b CJK Ideograph-7F6B | 7f6a CJK Ideograph-7F6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f6d CJK Ideograph-7F6D | 7f6c CJK Ideograph-7F6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f6f CJK Ideograph-7F6F | 7f6e CJK Ideograph-7F6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f71 CJK Ideograph-7F71 | 7f70 CJK Ideograph-7F70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f73 CJK Ideograph-7F73 | 7f72 CJK Ideograph-7F72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f75 CJK Ideograph-7F75 | 7f74 CJK Ideograph-7F74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f77 CJK Ideograph-7F77 | 7f76 CJK Ideograph-7F76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f79 CJK Ideograph-7F79 | 7f78 CJK Ideograph-7F78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f7b CJK Ideograph-7F7B | 7f7a CJK Ideograph-7F7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f7d CJK Ideograph-7F7D | 7f7c CJK Ideograph-7F7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f7f CJK Ideograph-7F7F | 7f7e CJK Ideograph-7F7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f81 CJK Ideograph-7F81 | 7f80 CJK Ideograph-7F80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f83 CJK Ideograph-7F83 | 7f82 CJK Ideograph-7F82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f85 CJK Ideograph-7F85 | 7f84 CJK Ideograph-7F84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f87 CJK Ideograph-7F87 | 7f86 CJK Ideograph-7F86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f89 CJK Ideograph-7F89 | 7f88 CJK Ideograph-7F88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f8b CJK Ideograph-7F8B | 7f8a CJK Ideograph-7F8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f8d CJK Ideograph-7F8D | 7f8c CJK Ideograph-7F8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f8f CJK Ideograph-7F8F | 7f8e CJK Ideograph-7F8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f91 CJK Ideograph-7F91 | 7f90 CJK Ideograph-7F90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f93 CJK Ideograph-7F93 | 7f92 CJK Ideograph-7F92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f95 CJK Ideograph-7F95 | 7f94 CJK Ideograph-7F94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f97 CJK Ideograph-7F97 | 7f96 CJK Ideograph-7F96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f99 CJK Ideograph-7F99 | 7f98 CJK Ideograph-7F98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f9b CJK Ideograph-7F9B | 7f9a CJK Ideograph-7F9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f9d CJK Ideograph-7F9D | 7f9c CJK Ideograph-7F9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f9f CJK Ideograph-7F9F | 7f9e CJK Ideograph-7F9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fa1 CJK Ideograph-7FA1 | 7fa0 CJK Ideograph-7FA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fa3 CJK Ideograph-7FA3 | 7fa2 CJK Ideograph-7FA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fa5 CJK Ideograph-7FA5 | 7fa4 CJK Ideograph-7FA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fa7 CJK Ideograph-7FA7 | 7fa6 CJK Ideograph-7FA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fa9 CJK Ideograph-7FA9 | 7fa8 CJK Ideograph-7FA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fab CJK Ideograph-7FAB | 7faa CJK Ideograph-7FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fad CJK Ideograph-7FAD | 7fac CJK Ideograph-7FAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7faf CJK Ideograph-7FAF | 7fae CJK Ideograph-7FAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fb1 CJK Ideograph-7FB1 | 7fb0 CJK Ideograph-7FB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fb3 CJK Ideograph-7FB3 | 7fb2 CJK Ideograph-7FB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fb5 CJK Ideograph-7FB5 | 7fb4 CJK Ideograph-7FB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fb7 CJK Ideograph-7FB7 | 7fb6 CJK Ideograph-7FB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fb9 CJK Ideograph-7FB9 | 7fb8 CJK Ideograph-7FB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fbb CJK Ideograph-7FBB | 7fba CJK Ideograph-7FBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fbd CJK Ideograph-7FBD | 7fbc CJK Ideograph-7FBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fbf CJK Ideograph-7FBF | 7fbe CJK Ideograph-7FBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fc1 CJK Ideograph-7FC1 | 7fc0 CJK Ideograph-7FC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fc3 CJK Ideograph-7FC3 | 7fc2 CJK Ideograph-7FC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fc5 CJK Ideograph-7FC5 | 7fc4 CJK Ideograph-7FC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fc7 CJK Ideograph-7FC7 | 7fc6 CJK Ideograph-7FC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fc9 CJK Ideograph-7FC9 | 7fc8 CJK Ideograph-7FC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fcb CJK Ideograph-7FCB | 7fca CJK Ideograph-7FCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fcd CJK Ideograph-7FCD | 7fcc CJK Ideograph-7FCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fcf CJK Ideograph-7FCF | 7fce CJK Ideograph-7FCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fd1 CJK Ideograph-7FD1 | 7fd0 CJK Ideograph-7FD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fd3 CJK Ideograph-7FD3 | 7fd2 CJK Ideograph-7FD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fd5 CJK Ideograph-7FD5 | 7fd4 CJK Ideograph-7FD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fd7 CJK Ideograph-7FD7 | 7fd6 CJK Ideograph-7FD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fd9 CJK Ideograph-7FD9 | 7fd8 CJK Ideograph-7FD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fdb CJK Ideograph-7FDB | 7fda CJK Ideograph-7FDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fdd CJK Ideograph-7FDD | 7fdc CJK Ideograph-7FDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fdf CJK Ideograph-7FDF | 7fde CJK Ideograph-7FDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fe1 CJK Ideograph-7FE1 | 7fe0 CJK Ideograph-7FE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fe3 CJK Ideograph-7FE3 | 7fe2 CJK Ideograph-7FE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fe5 CJK Ideograph-7FE5 | 7fe4 CJK Ideograph-7FE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fe7 CJK Ideograph-7FE7 | 7fe6 CJK Ideograph-7FE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fe9 CJK Ideograph-7FE9 | 7fe8 CJK Ideograph-7FE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7feb CJK Ideograph-7FEB | 7fea CJK Ideograph-7FEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fed CJK Ideograph-7FED | 7fec CJK Ideograph-7FEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fef CJK Ideograph-7FEF | 7fee CJK Ideograph-7FEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ff1 CJK Ideograph-7FF1 | 7ff0 CJK Ideograph-7FF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ff3 CJK Ideograph-7FF3 | 7ff2 CJK Ideograph-7FF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ff5 CJK Ideograph-7FF5 | 7ff4 CJK Ideograph-7FF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ff7 CJK Ideograph-7FF7 | 7ff6 CJK Ideograph-7FF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ff9 CJK Ideograph-7FF9 | 7ff8 CJK Ideograph-7FF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ffb CJK Ideograph-7FFB | 7ffa CJK Ideograph-7FFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ffd CJK Ideograph-7FFD | 7ffc CJK Ideograph-7FFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fff CJK Ideograph-7FFF | 7ffe CJK Ideograph-7FFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8001 CJK Ideograph-8001 | 8000 CJK Ideograph-8000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8003 CJK Ideograph-8003 | 8002 CJK Ideograph-8002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8005 CJK Ideograph-8005 | 8004 CJK Ideograph-8004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8007 CJK Ideograph-8007 | 8006 CJK Ideograph-8006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8009 CJK Ideograph-8009 | 8008 CJK Ideograph-8008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 800b CJK Ideograph-800B | 800a CJK Ideograph-800A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 800d CJK Ideograph-800D | 800c CJK Ideograph-800C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 800f CJK Ideograph-800F | 800e CJK Ideograph-800E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8011 CJK Ideograph-8011 | 8010 CJK Ideograph-8010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8013 CJK Ideograph-8013 | 8012 CJK Ideograph-8012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8015 CJK Ideograph-8015 | 8014 CJK Ideograph-8014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8017 CJK Ideograph-8017 | 8016 CJK Ideograph-8016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8019 CJK Ideograph-8019 | 8018 CJK Ideograph-8018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 801b CJK Ideograph-801B | 801a CJK Ideograph-801A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 801d CJK Ideograph-801D | 801c CJK Ideograph-801C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 801f CJK Ideograph-801F | 801e CJK Ideograph-801E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8021 CJK Ideograph-8021 | 8020 CJK Ideograph-8020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8023 CJK Ideograph-8023 | 8022 CJK Ideograph-8022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8025 CJK Ideograph-8025 | 8024 CJK Ideograph-8024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8027 CJK Ideograph-8027 | 8026 CJK Ideograph-8026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8029 CJK Ideograph-8029 | 8028 CJK Ideograph-8028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 802b CJK Ideograph-802B | 802a CJK Ideograph-802A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 802d CJK Ideograph-802D | 802c CJK Ideograph-802C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 802f CJK Ideograph-802F | 802e CJK Ideograph-802E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8031 CJK Ideograph-8031 | 8030 CJK Ideograph-8030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8033 CJK Ideograph-8033 | 8032 CJK Ideograph-8032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8035 CJK Ideograph-8035 | 8034 CJK Ideograph-8034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8037 CJK Ideograph-8037 | 8036 CJK Ideograph-8036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8039 CJK Ideograph-8039 | 8038 CJK Ideograph-8038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 803b CJK Ideograph-803B | 803a CJK Ideograph-803A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 803d CJK Ideograph-803D | 803c CJK Ideograph-803C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 803f CJK Ideograph-803F | 803e CJK Ideograph-803E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8041 CJK Ideograph-8041 | 8040 CJK Ideograph-8040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8043 CJK Ideograph-8043 | 8042 CJK Ideograph-8042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8045 CJK Ideograph-8045 | 8044 CJK Ideograph-8044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8047 CJK Ideograph-8047 | 8046 CJK Ideograph-8046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8049 CJK Ideograph-8049 | 8048 CJK Ideograph-8048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 804b CJK Ideograph-804B | 804a CJK Ideograph-804A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 804d CJK Ideograph-804D | 804c CJK Ideograph-804C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 804f CJK Ideograph-804F | 804e CJK Ideograph-804E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8051 CJK Ideograph-8051 | 8050 CJK Ideograph-8050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8053 CJK Ideograph-8053 | 8052 CJK Ideograph-8052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8055 CJK Ideograph-8055 | 8054 CJK Ideograph-8054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8057 CJK Ideograph-8057 | 8056 CJK Ideograph-8056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8059 CJK Ideograph-8059 | 8058 CJK Ideograph-8058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 805b CJK Ideograph-805B | 805a CJK Ideograph-805A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 805d CJK Ideograph-805D | 805c CJK Ideograph-805C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 805f CJK Ideograph-805F | 805e CJK Ideograph-805E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8061 CJK Ideograph-8061 | 8060 CJK Ideograph-8060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8063 CJK Ideograph-8063 | 8062 CJK Ideograph-8062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8065 CJK Ideograph-8065 | 8064 CJK Ideograph-8064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8067 CJK Ideograph-8067 | 8066 CJK Ideograph-8066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8069 CJK Ideograph-8069 | 8068 CJK Ideograph-8068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 806b CJK Ideograph-806B | 806a CJK Ideograph-806A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 806d CJK Ideograph-806D | 806c CJK Ideograph-806C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 806f CJK Ideograph-806F | 806e CJK Ideograph-806E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8071 CJK Ideograph-8071 | 8070 CJK Ideograph-8070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8073 CJK Ideograph-8073 | 8072 CJK Ideograph-8072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8075 CJK Ideograph-8075 | 8074 CJK Ideograph-8074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8077 CJK Ideograph-8077 | 8076 CJK Ideograph-8076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8079 CJK Ideograph-8079 | 8078 CJK Ideograph-8078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 807b CJK Ideograph-807B | 807a CJK Ideograph-807A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 807d CJK Ideograph-807D | 807c CJK Ideograph-807C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 807f CJK Ideograph-807F | 807e CJK Ideograph-807E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8081 CJK Ideograph-8081 | 8080 CJK Ideograph-8080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8083 CJK Ideograph-8083 | 8082 CJK Ideograph-8082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8085 CJK Ideograph-8085 | 8084 CJK Ideograph-8084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8087 CJK Ideograph-8087 | 8086 CJK Ideograph-8086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8089 CJK Ideograph-8089 | 8088 CJK Ideograph-8088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 808b CJK Ideograph-808B | 808a CJK Ideograph-808A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 808d CJK Ideograph-808D | 808c CJK Ideograph-808C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 808f CJK Ideograph-808F | 808e CJK Ideograph-808E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8091 CJK Ideograph-8091 | 8090 CJK Ideograph-8090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8093 CJK Ideograph-8093 | 8092 CJK Ideograph-8092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8095 CJK Ideograph-8095 | 8094 CJK Ideograph-8094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8097 CJK Ideograph-8097 | 8096 CJK Ideograph-8096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8099 CJK Ideograph-8099 | 8098 CJK Ideograph-8098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 809b CJK Ideograph-809B | 809a CJK Ideograph-809A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 809d CJK Ideograph-809D | 809c CJK Ideograph-809C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 809f CJK Ideograph-809F | 809e CJK Ideograph-809E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80a1 CJK Ideograph-80A1 | 80a0 CJK Ideograph-80A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80a3 CJK Ideograph-80A3 | 80a2 CJK Ideograph-80A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80a5 CJK Ideograph-80A5 | 80a4 CJK Ideograph-80A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80a7 CJK Ideograph-80A7 | 80a6 CJK Ideograph-80A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80a9 CJK Ideograph-80A9 | 80a8 CJK Ideograph-80A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80ab CJK Ideograph-80AB | 80aa CJK Ideograph-80AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80ad CJK Ideograph-80AD | 80ac CJK Ideograph-80AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80af CJK Ideograph-80AF | 80ae CJK Ideograph-80AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80b1 CJK Ideograph-80B1 | 80b0 CJK Ideograph-80B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80b3 CJK Ideograph-80B3 | 80b2 CJK Ideograph-80B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80b5 CJK Ideograph-80B5 | 80b4 CJK Ideograph-80B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80b7 CJK Ideograph-80B7 | 80b6 CJK Ideograph-80B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80b9 CJK Ideograph-80B9 | 80b8 CJK Ideograph-80B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80bb CJK Ideograph-80BB | 80ba CJK Ideograph-80BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80bd CJK Ideograph-80BD | 80bc CJK Ideograph-80BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80bf CJK Ideograph-80BF | 80be CJK Ideograph-80BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80c1 CJK Ideograph-80C1 | 80c0 CJK Ideograph-80C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80c3 CJK Ideograph-80C3 | 80c2 CJK Ideograph-80C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80c5 CJK Ideograph-80C5 | 80c4 CJK Ideograph-80C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80c7 CJK Ideograph-80C7 | 80c6 CJK Ideograph-80C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80c9 CJK Ideograph-80C9 | 80c8 CJK Ideograph-80C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80cb CJK Ideograph-80CB | 80ca CJK Ideograph-80CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80cd CJK Ideograph-80CD | 80cc CJK Ideograph-80CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80cf CJK Ideograph-80CF | 80ce CJK Ideograph-80CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80d1 CJK Ideograph-80D1 | 80d0 CJK Ideograph-80D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80d3 CJK Ideograph-80D3 | 80d2 CJK Ideograph-80D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80d5 CJK Ideograph-80D5 | 80d4 CJK Ideograph-80D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80d7 CJK Ideograph-80D7 | 80d6 CJK Ideograph-80D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80d9 CJK Ideograph-80D9 | 80d8 CJK Ideograph-80D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80db CJK Ideograph-80DB | 80da CJK Ideograph-80DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80dd CJK Ideograph-80DD | 80dc CJK Ideograph-80DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80df CJK Ideograph-80DF | 80de CJK Ideograph-80DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80e1 CJK Ideograph-80E1 | 80e0 CJK Ideograph-80E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80e3 CJK Ideograph-80E3 | 80e2 CJK Ideograph-80E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80e5 CJK Ideograph-80E5 | 80e4 CJK Ideograph-80E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80e7 CJK Ideograph-80E7 | 80e6 CJK Ideograph-80E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80e9 CJK Ideograph-80E9 | 80e8 CJK Ideograph-80E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80eb CJK Ideograph-80EB | 80ea CJK Ideograph-80EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80ed CJK Ideograph-80ED | 80ec CJK Ideograph-80EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80ef CJK Ideograph-80EF | 80ee CJK Ideograph-80EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80f1 CJK Ideograph-80F1 | 80f0 CJK Ideograph-80F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80f3 CJK Ideograph-80F3 | 80f2 CJK Ideograph-80F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80f5 CJK Ideograph-80F5 | 80f4 CJK Ideograph-80F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80f7 CJK Ideograph-80F7 | 80f6 CJK Ideograph-80F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80f9 CJK Ideograph-80F9 | 80f8 CJK Ideograph-80F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80fb CJK Ideograph-80FB | 80fa CJK Ideograph-80FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80fd CJK Ideograph-80FD | 80fc CJK Ideograph-80FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80ff CJK Ideograph-80FF | 80fe CJK Ideograph-80FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8101 CJK Ideograph-8101 | 8100 CJK Ideograph-8100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8103 CJK Ideograph-8103 | 8102 CJK Ideograph-8102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8105 CJK Ideograph-8105 | 8104 CJK Ideograph-8104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8107 CJK Ideograph-8107 | 8106 CJK Ideograph-8106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8109 CJK Ideograph-8109 | 8108 CJK Ideograph-8108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 810b CJK Ideograph-810B | 810a CJK Ideograph-810A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 810d CJK Ideograph-810D | 810c CJK Ideograph-810C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 810f CJK Ideograph-810F | 810e CJK Ideograph-810E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8111 CJK Ideograph-8111 | 8110 CJK Ideograph-8110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8113 CJK Ideograph-8113 | 8112 CJK Ideograph-8112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8115 CJK Ideograph-8115 | 8114 CJK Ideograph-8114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8117 CJK Ideograph-8117 | 8116 CJK Ideograph-8116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8119 CJK Ideograph-8119 | 8118 CJK Ideograph-8118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 811b CJK Ideograph-811B | 811a CJK Ideograph-811A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 811d CJK Ideograph-811D | 811c CJK Ideograph-811C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 811f CJK Ideograph-811F | 811e CJK Ideograph-811E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8121 CJK Ideograph-8121 | 8120 CJK Ideograph-8120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8123 CJK Ideograph-8123 | 8122 CJK Ideograph-8122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8125 CJK Ideograph-8125 | 8124 CJK Ideograph-8124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8127 CJK Ideograph-8127 | 8126 CJK Ideograph-8126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8129 CJK Ideograph-8129 | 8128 CJK Ideograph-8128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 812b CJK Ideograph-812B | 812a CJK Ideograph-812A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 812d CJK Ideograph-812D | 812c CJK Ideograph-812C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 812f CJK Ideograph-812F | 812e CJK Ideograph-812E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8131 CJK Ideograph-8131 | 8130 CJK Ideograph-8130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8133 CJK Ideograph-8133 | 8132 CJK Ideograph-8132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8135 CJK Ideograph-8135 | 8134 CJK Ideograph-8134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8137 CJK Ideograph-8137 | 8136 CJK Ideograph-8136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8139 CJK Ideograph-8139 | 8138 CJK Ideograph-8138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 813b CJK Ideograph-813B | 813a CJK Ideograph-813A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 813d CJK Ideograph-813D | 813c CJK Ideograph-813C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 813f CJK Ideograph-813F | 813e CJK Ideograph-813E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8141 CJK Ideograph-8141 | 8140 CJK Ideograph-8140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8143 CJK Ideograph-8143 | 8142 CJK Ideograph-8142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8145 CJK Ideograph-8145 | 8144 CJK Ideograph-8144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8147 CJK Ideograph-8147 | 8146 CJK Ideograph-8146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8149 CJK Ideograph-8149 | 8148 CJK Ideograph-8148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 814b CJK Ideograph-814B | 814a CJK Ideograph-814A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 814d CJK Ideograph-814D | 814c CJK Ideograph-814C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 814f CJK Ideograph-814F | 814e CJK Ideograph-814E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8151 CJK Ideograph-8151 | 8150 CJK Ideograph-8150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8153 CJK Ideograph-8153 | 8152 CJK Ideograph-8152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8155 CJK Ideograph-8155 | 8154 CJK Ideograph-8154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8157 CJK Ideograph-8157 | 8156 CJK Ideograph-8156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8159 CJK Ideograph-8159 | 8158 CJK Ideograph-8158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 815b CJK Ideograph-815B | 815a CJK Ideograph-815A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 815d CJK Ideograph-815D | 815c CJK Ideograph-815C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 815f CJK Ideograph-815F | 815e CJK Ideograph-815E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8161 CJK Ideograph-8161 | 8160 CJK Ideograph-8160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8163 CJK Ideograph-8163 | 8162 CJK Ideograph-8162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8165 CJK Ideograph-8165 | 8164 CJK Ideograph-8164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8167 CJK Ideograph-8167 | 8166 CJK Ideograph-8166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8169 CJK Ideograph-8169 | 8168 CJK Ideograph-8168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 816b CJK Ideograph-816B | 816a CJK Ideograph-816A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 816d CJK Ideograph-816D | 816c CJK Ideograph-816C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 816f CJK Ideograph-816F | 816e CJK Ideograph-816E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8171 CJK Ideograph-8171 | 8170 CJK Ideograph-8170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8173 CJK Ideograph-8173 | 8172 CJK Ideograph-8172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8175 CJK Ideograph-8175 | 8174 CJK Ideograph-8174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8177 CJK Ideograph-8177 | 8176 CJK Ideograph-8176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8179 CJK Ideograph-8179 | 8178 CJK Ideograph-8178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 817b CJK Ideograph-817B | 817a CJK Ideograph-817A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 817d CJK Ideograph-817D | 817c CJK Ideograph-817C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 817f CJK Ideograph-817F | 817e CJK Ideograph-817E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8181 CJK Ideograph-8181 | 8180 CJK Ideograph-8180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8183 CJK Ideograph-8183 | 8182 CJK Ideograph-8182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8185 CJK Ideograph-8185 | 8184 CJK Ideograph-8184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8187 CJK Ideograph-8187 | 8186 CJK Ideograph-8186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8189 CJK Ideograph-8189 | 8188 CJK Ideograph-8188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 818b CJK Ideograph-818B | 818a CJK Ideograph-818A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 818d CJK Ideograph-818D | 818c CJK Ideograph-818C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 818f CJK Ideograph-818F | 818e CJK Ideograph-818E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8191 CJK Ideograph-8191 | 8190 CJK Ideograph-8190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8193 CJK Ideograph-8193 | 8192 CJK Ideograph-8192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8195 CJK Ideograph-8195 | 8194 CJK Ideograph-8194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8197 CJK Ideograph-8197 | 8196 CJK Ideograph-8196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8199 CJK Ideograph-8199 | 8198 CJK Ideograph-8198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 819b CJK Ideograph-819B | 819a CJK Ideograph-819A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 819d CJK Ideograph-819D | 819c CJK Ideograph-819C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 819f CJK Ideograph-819F | 819e CJK Ideograph-819E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81a1 CJK Ideograph-81A1 | 81a0 CJK Ideograph-81A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81a3 CJK Ideograph-81A3 | 81a2 CJK Ideograph-81A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81a5 CJK Ideograph-81A5 | 81a4 CJK Ideograph-81A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81a7 CJK Ideograph-81A7 | 81a6 CJK Ideograph-81A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81a9 CJK Ideograph-81A9 | 81a8 CJK Ideograph-81A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81ab CJK Ideograph-81AB | 81aa CJK Ideograph-81AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81ad CJK Ideograph-81AD | 81ac CJK Ideograph-81AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81af CJK Ideograph-81AF | 81ae CJK Ideograph-81AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81b1 CJK Ideograph-81B1 | 81b0 CJK Ideograph-81B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81b3 CJK Ideograph-81B3 | 81b2 CJK Ideograph-81B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81b5 CJK Ideograph-81B5 | 81b4 CJK Ideograph-81B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81b7 CJK Ideograph-81B7 | 81b6 CJK Ideograph-81B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81b9 CJK Ideograph-81B9 | 81b8 CJK Ideograph-81B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81bb CJK Ideograph-81BB | 81ba CJK Ideograph-81BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81bd CJK Ideograph-81BD | 81bc CJK Ideograph-81BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81bf CJK Ideograph-81BF | 81be CJK Ideograph-81BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81c1 CJK Ideograph-81C1 | 81c0 CJK Ideograph-81C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81c3 CJK Ideograph-81C3 | 81c2 CJK Ideograph-81C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81c5 CJK Ideograph-81C5 | 81c4 CJK Ideograph-81C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81c7 CJK Ideograph-81C7 | 81c6 CJK Ideograph-81C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81c9 CJK Ideograph-81C9 | 81c8 CJK Ideograph-81C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81cb CJK Ideograph-81CB | 81ca CJK Ideograph-81CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81cd CJK Ideograph-81CD | 81cc CJK Ideograph-81CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81cf CJK Ideograph-81CF | 81ce CJK Ideograph-81CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81d1 CJK Ideograph-81D1 | 81d0 CJK Ideograph-81D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81d3 CJK Ideograph-81D3 | 81d2 CJK Ideograph-81D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81d5 CJK Ideograph-81D5 | 81d4 CJK Ideograph-81D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81d7 CJK Ideograph-81D7 | 81d6 CJK Ideograph-81D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81d9 CJK Ideograph-81D9 | 81d8 CJK Ideograph-81D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81db CJK Ideograph-81DB | 81da CJK Ideograph-81DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81dd CJK Ideograph-81DD | 81dc CJK Ideograph-81DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81df CJK Ideograph-81DF | 81de CJK Ideograph-81DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81e1 CJK Ideograph-81E1 | 81e0 CJK Ideograph-81E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81e3 CJK Ideograph-81E3 | 81e2 CJK Ideograph-81E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81e5 CJK Ideograph-81E5 | 81e4 CJK Ideograph-81E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81e7 CJK Ideograph-81E7 | 81e6 CJK Ideograph-81E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81e9 CJK Ideograph-81E9 | 81e8 CJK Ideograph-81E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81eb CJK Ideograph-81EB | 81ea CJK Ideograph-81EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81ed CJK Ideograph-81ED | 81ec CJK Ideograph-81EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81ef CJK Ideograph-81EF | 81ee CJK Ideograph-81EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81f1 CJK Ideograph-81F1 | 81f0 CJK Ideograph-81F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81f3 CJK Ideograph-81F3 | 81f2 CJK Ideograph-81F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81f5 CJK Ideograph-81F5 | 81f4 CJK Ideograph-81F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81f7 CJK Ideograph-81F7 | 81f6 CJK Ideograph-81F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81f9 CJK Ideograph-81F9 | 81f8 CJK Ideograph-81F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81fb CJK Ideograph-81FB | 81fa CJK Ideograph-81FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81fd CJK Ideograph-81FD | 81fc CJK Ideograph-81FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81ff CJK Ideograph-81FF | 81fe CJK Ideograph-81FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8201 CJK Ideograph-8201 | 8200 CJK Ideograph-8200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8203 CJK Ideograph-8203 | 8202 CJK Ideograph-8202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8205 CJK Ideograph-8205 | 8204 CJK Ideograph-8204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8207 CJK Ideograph-8207 | 8206 CJK Ideograph-8206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8209 CJK Ideograph-8209 | 8208 CJK Ideograph-8208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 820b CJK Ideograph-820B | 820a CJK Ideograph-820A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 820d CJK Ideograph-820D | 820c CJK Ideograph-820C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 820f CJK Ideograph-820F | 820e CJK Ideograph-820E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8211 CJK Ideograph-8211 | 8210 CJK Ideograph-8210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8213 CJK Ideograph-8213 | 8212 CJK Ideograph-8212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8215 CJK Ideograph-8215 | 8214 CJK Ideograph-8214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8217 CJK Ideograph-8217 | 8216 CJK Ideograph-8216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8219 CJK Ideograph-8219 | 8218 CJK Ideograph-8218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 821b CJK Ideograph-821B | 821a CJK Ideograph-821A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 821d CJK Ideograph-821D | 821c CJK Ideograph-821C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 821f CJK Ideograph-821F | 821e CJK Ideograph-821E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8221 CJK Ideograph-8221 | 8220 CJK Ideograph-8220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8223 CJK Ideograph-8223 | 8222 CJK Ideograph-8222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8225 CJK Ideograph-8225 | 8224 CJK Ideograph-8224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8227 CJK Ideograph-8227 | 8226 CJK Ideograph-8226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8229 CJK Ideograph-8229 | 8228 CJK Ideograph-8228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 822b CJK Ideograph-822B | 822a CJK Ideograph-822A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 822d CJK Ideograph-822D | 822c CJK Ideograph-822C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 822f CJK Ideograph-822F | 822e CJK Ideograph-822E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8231 CJK Ideograph-8231 | 8230 CJK Ideograph-8230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8233 CJK Ideograph-8233 | 8232 CJK Ideograph-8232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8235 CJK Ideograph-8235 | 8234 CJK Ideograph-8234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8237 CJK Ideograph-8237 | 8236 CJK Ideograph-8236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8239 CJK Ideograph-8239 | 8238 CJK Ideograph-8238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 823b CJK Ideograph-823B | 823a CJK Ideograph-823A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 823d CJK Ideograph-823D | 823c CJK Ideograph-823C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 823f CJK Ideograph-823F | 823e CJK Ideograph-823E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8241 CJK Ideograph-8241 | 8240 CJK Ideograph-8240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8243 CJK Ideograph-8243 | 8242 CJK Ideograph-8242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8245 CJK Ideograph-8245 | 8244 CJK Ideograph-8244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8247 CJK Ideograph-8247 | 8246 CJK Ideograph-8246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8249 CJK Ideograph-8249 | 8248 CJK Ideograph-8248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 824b CJK Ideograph-824B | 824a CJK Ideograph-824A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 824d CJK Ideograph-824D | 824c CJK Ideograph-824C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 824f CJK Ideograph-824F | 824e CJK Ideograph-824E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8251 CJK Ideograph-8251 | 8250 CJK Ideograph-8250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8253 CJK Ideograph-8253 | 8252 CJK Ideograph-8252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8255 CJK Ideograph-8255 | 8254 CJK Ideograph-8254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8257 CJK Ideograph-8257 | 8256 CJK Ideograph-8256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8259 CJK Ideograph-8259 | 8258 CJK Ideograph-8258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 825b CJK Ideograph-825B | 825a CJK Ideograph-825A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 825d CJK Ideograph-825D | 825c CJK Ideograph-825C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 825f CJK Ideograph-825F | 825e CJK Ideograph-825E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8261 CJK Ideograph-8261 | 8260 CJK Ideograph-8260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8263 CJK Ideograph-8263 | 8262 CJK Ideograph-8262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8265 CJK Ideograph-8265 | 8264 CJK Ideograph-8264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8267 CJK Ideograph-8267 | 8266 CJK Ideograph-8266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8269 CJK Ideograph-8269 | 8268 CJK Ideograph-8268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 826b CJK Ideograph-826B | 826a CJK Ideograph-826A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 826d CJK Ideograph-826D | 826c CJK Ideograph-826C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 826f CJK Ideograph-826F | 826e CJK Ideograph-826E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8271 CJK Ideograph-8271 | 8270 CJK Ideograph-8270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8273 CJK Ideograph-8273 | 8272 CJK Ideograph-8272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8275 CJK Ideograph-8275 | 8274 CJK Ideograph-8274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8277 CJK Ideograph-8277 | 8276 CJK Ideograph-8276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8279 CJK Ideograph-8279 | 8278 CJK Ideograph-8278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 827b CJK Ideograph-827B | 827a CJK Ideograph-827A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 827d CJK Ideograph-827D | 827c CJK Ideograph-827C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 827f CJK Ideograph-827F | 827e CJK Ideograph-827E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8281 CJK Ideograph-8281 | 8280 CJK Ideograph-8280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8283 CJK Ideograph-8283 | 8282 CJK Ideograph-8282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8285 CJK Ideograph-8285 | 8284 CJK Ideograph-8284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8287 CJK Ideograph-8287 | 8286 CJK Ideograph-8286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8289 CJK Ideograph-8289 | 8288 CJK Ideograph-8288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 828b CJK Ideograph-828B | 828a CJK Ideograph-828A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 828d CJK Ideograph-828D | 828c CJK Ideograph-828C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 828f CJK Ideograph-828F | 828e CJK Ideograph-828E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8291 CJK Ideograph-8291 | 8290 CJK Ideograph-8290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8293 CJK Ideograph-8293 | 8292 CJK Ideograph-8292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8295 CJK Ideograph-8295 | 8294 CJK Ideograph-8294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8297 CJK Ideograph-8297 | 8296 CJK Ideograph-8296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8299 CJK Ideograph-8299 | 8298 CJK Ideograph-8298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 829b CJK Ideograph-829B | 829a CJK Ideograph-829A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 829d CJK Ideograph-829D | 829c CJK Ideograph-829C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 829f CJK Ideograph-829F | 829e CJK Ideograph-829E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82a1 CJK Ideograph-82A1 | 82a0 CJK Ideograph-82A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82a3 CJK Ideograph-82A3 | 82a2 CJK Ideograph-82A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82a5 CJK Ideograph-82A5 | 82a4 CJK Ideograph-82A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82a7 CJK Ideograph-82A7 | 82a6 CJK Ideograph-82A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82a9 CJK Ideograph-82A9 | 82a8 CJK Ideograph-82A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82ab CJK Ideograph-82AB | 82aa CJK Ideograph-82AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82ad CJK Ideograph-82AD | 82ac CJK Ideograph-82AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82af CJK Ideograph-82AF | 82ae CJK Ideograph-82AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82b1 CJK Ideograph-82B1 | 82b0 CJK Ideograph-82B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82b3 CJK Ideograph-82B3 | 82b2 CJK Ideograph-82B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82b5 CJK Ideograph-82B5 | 82b4 CJK Ideograph-82B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82b7 CJK Ideograph-82B7 | 82b6 CJK Ideograph-82B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82b9 CJK Ideograph-82B9 | 82b8 CJK Ideograph-82B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82bb CJK Ideograph-82BB | 82ba CJK Ideograph-82BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82bd CJK Ideograph-82BD | 82bc CJK Ideograph-82BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82bf CJK Ideograph-82BF | 82be CJK Ideograph-82BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82c1 CJK Ideograph-82C1 | 82c0 CJK Ideograph-82C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82c3 CJK Ideograph-82C3 | 82c2 CJK Ideograph-82C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82c5 CJK Ideograph-82C5 | 82c4 CJK Ideograph-82C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82c7 CJK Ideograph-82C7 | 82c6 CJK Ideograph-82C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82c9 CJK Ideograph-82C9 | 82c8 CJK Ideograph-82C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82cb CJK Ideograph-82CB | 82ca CJK Ideograph-82CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82cd CJK Ideograph-82CD | 82cc CJK Ideograph-82CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82cf CJK Ideograph-82CF | 82ce CJK Ideograph-82CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82d1 CJK Ideograph-82D1 | 82d0 CJK Ideograph-82D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82d3 CJK Ideograph-82D3 | 82d2 CJK Ideograph-82D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82d5 CJK Ideograph-82D5 | 82d4 CJK Ideograph-82D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82d7 CJK Ideograph-82D7 | 82d6 CJK Ideograph-82D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82d9 CJK Ideograph-82D9 | 82d8 CJK Ideograph-82D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82db CJK Ideograph-82DB | 82da CJK Ideograph-82DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82dd CJK Ideograph-82DD | 82dc CJK Ideograph-82DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82df CJK Ideograph-82DF | 82de CJK Ideograph-82DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82e1 CJK Ideograph-82E1 | 82e0 CJK Ideograph-82E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82e3 CJK Ideograph-82E3 | 82e2 CJK Ideograph-82E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82e5 CJK Ideograph-82E5 | 82e4 CJK Ideograph-82E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82e7 CJK Ideograph-82E7 | 82e6 CJK Ideograph-82E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82e9 CJK Ideograph-82E9 | 82e8 CJK Ideograph-82E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82eb CJK Ideograph-82EB | 82ea CJK Ideograph-82EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82ed CJK Ideograph-82ED | 82ec CJK Ideograph-82EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82ef CJK Ideograph-82EF | 82ee CJK Ideograph-82EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82f1 CJK Ideograph-82F1 | 82f0 CJK Ideograph-82F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82f3 CJK Ideograph-82F3 | 82f2 CJK Ideograph-82F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82f5 CJK Ideograph-82F5 | 82f4 CJK Ideograph-82F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82f7 CJK Ideograph-82F7 | 82f6 CJK Ideograph-82F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82f9 CJK Ideograph-82F9 | 82f8 CJK Ideograph-82F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82fb CJK Ideograph-82FB | 82fa CJK Ideograph-82FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82fd CJK Ideograph-82FD | 82fc CJK Ideograph-82FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82ff CJK Ideograph-82FF | 82fe CJK Ideograph-82FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8301 CJK Ideograph-8301 | 8300 CJK Ideograph-8300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8303 CJK Ideograph-8303 | 8302 CJK Ideograph-8302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8305 CJK Ideograph-8305 | 8304 CJK Ideograph-8304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8307 CJK Ideograph-8307 | 8306 CJK Ideograph-8306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8309 CJK Ideograph-8309 | 8308 CJK Ideograph-8308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 830b CJK Ideograph-830B | 830a CJK Ideograph-830A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 830d CJK Ideograph-830D | 830c CJK Ideograph-830C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 830f CJK Ideograph-830F | 830e CJK Ideograph-830E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8311 CJK Ideograph-8311 | 8310 CJK Ideograph-8310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8313 CJK Ideograph-8313 | 8312 CJK Ideograph-8312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8315 CJK Ideograph-8315 | 8314 CJK Ideograph-8314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8317 CJK Ideograph-8317 | 8316 CJK Ideograph-8316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8319 CJK Ideograph-8319 | 8318 CJK Ideograph-8318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 831b CJK Ideograph-831B | 831a CJK Ideograph-831A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 831d CJK Ideograph-831D | 831c CJK Ideograph-831C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 831f CJK Ideograph-831F | 831e CJK Ideograph-831E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8321 CJK Ideograph-8321 | 8320 CJK Ideograph-8320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8323 CJK Ideograph-8323 | 8322 CJK Ideograph-8322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8325 CJK Ideograph-8325 | 8324 CJK Ideograph-8324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8327 CJK Ideograph-8327 | 8326 CJK Ideograph-8326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8329 CJK Ideograph-8329 | 8328 CJK Ideograph-8328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 832b CJK Ideograph-832B | 832a CJK Ideograph-832A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 832d CJK Ideograph-832D | 832c CJK Ideograph-832C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 832f CJK Ideograph-832F | 832e CJK Ideograph-832E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8331 CJK Ideograph-8331 | 8330 CJK Ideograph-8330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8333 CJK Ideograph-8333 | 8332 CJK Ideograph-8332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8335 CJK Ideograph-8335 | 8334 CJK Ideograph-8334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8337 CJK Ideograph-8337 | 8336 CJK Ideograph-8336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8339 CJK Ideograph-8339 | 8338 CJK Ideograph-8338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 833b CJK Ideograph-833B | 833a CJK Ideograph-833A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 833d CJK Ideograph-833D | 833c CJK Ideograph-833C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 833f CJK Ideograph-833F | 833e CJK Ideograph-833E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8341 CJK Ideograph-8341 | 8340 CJK Ideograph-8340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8343 CJK Ideograph-8343 | 8342 CJK Ideograph-8342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8345 CJK Ideograph-8345 | 8344 CJK Ideograph-8344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8347 CJK Ideograph-8347 | 8346 CJK Ideograph-8346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8349 CJK Ideograph-8349 | 8348 CJK Ideograph-8348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 834b CJK Ideograph-834B | 834a CJK Ideograph-834A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 834d CJK Ideograph-834D | 834c CJK Ideograph-834C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 834f CJK Ideograph-834F | 834e CJK Ideograph-834E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8351 CJK Ideograph-8351 | 8350 CJK Ideograph-8350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8353 CJK Ideograph-8353 | 8352 CJK Ideograph-8352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8355 CJK Ideograph-8355 | 8354 CJK Ideograph-8354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8357 CJK Ideograph-8357 | 8356 CJK Ideograph-8356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8359 CJK Ideograph-8359 | 8358 CJK Ideograph-8358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 835b CJK Ideograph-835B | 835a CJK Ideograph-835A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 835d CJK Ideograph-835D | 835c CJK Ideograph-835C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 835f CJK Ideograph-835F | 835e CJK Ideograph-835E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8361 CJK Ideograph-8361 | 8360 CJK Ideograph-8360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8363 CJK Ideograph-8363 | 8362 CJK Ideograph-8362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8365 CJK Ideograph-8365 | 8364 CJK Ideograph-8364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8367 CJK Ideograph-8367 | 8366 CJK Ideograph-8366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8369 CJK Ideograph-8369 | 8368 CJK Ideograph-8368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 836b CJK Ideograph-836B | 836a CJK Ideograph-836A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 836d CJK Ideograph-836D | 836c CJK Ideograph-836C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 836f CJK Ideograph-836F | 836e CJK Ideograph-836E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8371 CJK Ideograph-8371 | 8370 CJK Ideograph-8370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8373 CJK Ideograph-8373 | 8372 CJK Ideograph-8372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8375 CJK Ideograph-8375 | 8374 CJK Ideograph-8374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8377 CJK Ideograph-8377 | 8376 CJK Ideograph-8376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8379 CJK Ideograph-8379 | 8378 CJK Ideograph-8378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 837b CJK Ideograph-837B | 837a CJK Ideograph-837A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 837d CJK Ideograph-837D | 837c CJK Ideograph-837C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 837f CJK Ideograph-837F | 837e CJK Ideograph-837E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8381 CJK Ideograph-8381 | 8380 CJK Ideograph-8380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8383 CJK Ideograph-8383 | 8382 CJK Ideograph-8382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8385 CJK Ideograph-8385 | 8384 CJK Ideograph-8384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8387 CJK Ideograph-8387 | 8386 CJK Ideograph-8386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8389 CJK Ideograph-8389 | 8388 CJK Ideograph-8388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 838b CJK Ideograph-838B | 838a CJK Ideograph-838A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 838d CJK Ideograph-838D | 838c CJK Ideograph-838C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 838f CJK Ideograph-838F | 838e CJK Ideograph-838E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8391 CJK Ideograph-8391 | 8390 CJK Ideograph-8390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8393 CJK Ideograph-8393 | 8392 CJK Ideograph-8392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8395 CJK Ideograph-8395 | 8394 CJK Ideograph-8394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8397 CJK Ideograph-8397 | 8396 CJK Ideograph-8396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8399 CJK Ideograph-8399 | 8398 CJK Ideograph-8398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 839b CJK Ideograph-839B | 839a CJK Ideograph-839A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 839d CJK Ideograph-839D | 839c CJK Ideograph-839C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 839f CJK Ideograph-839F | 839e CJK Ideograph-839E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83a1 CJK Ideograph-83A1 | 83a0 CJK Ideograph-83A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83a3 CJK Ideograph-83A3 | 83a2 CJK Ideograph-83A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83a5 CJK Ideograph-83A5 | 83a4 CJK Ideograph-83A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83a7 CJK Ideograph-83A7 | 83a6 CJK Ideograph-83A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83a9 CJK Ideograph-83A9 | 83a8 CJK Ideograph-83A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83ab CJK Ideograph-83AB | 83aa CJK Ideograph-83AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83ad CJK Ideograph-83AD | 83ac CJK Ideograph-83AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83af CJK Ideograph-83AF | 83ae CJK Ideograph-83AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83b1 CJK Ideograph-83B1 | 83b0 CJK Ideograph-83B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83b3 CJK Ideograph-83B3 | 83b2 CJK Ideograph-83B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83b5 CJK Ideograph-83B5 | 83b4 CJK Ideograph-83B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83b7 CJK Ideograph-83B7 | 83b6 CJK Ideograph-83B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83b9 CJK Ideograph-83B9 | 83b8 CJK Ideograph-83B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83bb CJK Ideograph-83BB | 83ba CJK Ideograph-83BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83bd CJK Ideograph-83BD | 83bc CJK Ideograph-83BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83bf CJK Ideograph-83BF | 83be CJK Ideograph-83BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83c1 CJK Ideograph-83C1 | 83c0 CJK Ideograph-83C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83c3 CJK Ideograph-83C3 | 83c2 CJK Ideograph-83C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83c5 CJK Ideograph-83C5 | 83c4 CJK Ideograph-83C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83c7 CJK Ideograph-83C7 | 83c6 CJK Ideograph-83C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83c9 CJK Ideograph-83C9 | 83c8 CJK Ideograph-83C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83cb CJK Ideograph-83CB | 83ca CJK Ideograph-83CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83cd CJK Ideograph-83CD | 83cc CJK Ideograph-83CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83cf CJK Ideograph-83CF | 83ce CJK Ideograph-83CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83d1 CJK Ideograph-83D1 | 83d0 CJK Ideograph-83D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83d3 CJK Ideograph-83D3 | 83d2 CJK Ideograph-83D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83d5 CJK Ideograph-83D5 | 83d4 CJK Ideograph-83D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83d7 CJK Ideograph-83D7 | 83d6 CJK Ideograph-83D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83d9 CJK Ideograph-83D9 | 83d8 CJK Ideograph-83D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83db CJK Ideograph-83DB | 83da CJK Ideograph-83DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83dd CJK Ideograph-83DD | 83dc CJK Ideograph-83DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83df CJK Ideograph-83DF | 83de CJK Ideograph-83DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83e1 CJK Ideograph-83E1 | 83e0 CJK Ideograph-83E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83e3 CJK Ideograph-83E3 | 83e2 CJK Ideograph-83E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83e5 CJK Ideograph-83E5 | 83e4 CJK Ideograph-83E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83e7 CJK Ideograph-83E7 | 83e6 CJK Ideograph-83E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83e9 CJK Ideograph-83E9 | 83e8 CJK Ideograph-83E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83eb CJK Ideograph-83EB | 83ea CJK Ideograph-83EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83ed CJK Ideograph-83ED | 83ec CJK Ideograph-83EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83ef CJK Ideograph-83EF | 83ee CJK Ideograph-83EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83f1 CJK Ideograph-83F1 | 83f0 CJK Ideograph-83F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83f3 CJK Ideograph-83F3 | 83f2 CJK Ideograph-83F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83f5 CJK Ideograph-83F5 | 83f4 CJK Ideograph-83F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83f7 CJK Ideograph-83F7 | 83f6 CJK Ideograph-83F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83f9 CJK Ideograph-83F9 | 83f8 CJK Ideograph-83F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83fb CJK Ideograph-83FB | 83fa CJK Ideograph-83FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83fd CJK Ideograph-83FD | 83fc CJK Ideograph-83FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83ff CJK Ideograph-83FF | 83fe CJK Ideograph-83FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8401 CJK Ideograph-8401 | 8400 CJK Ideograph-8400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8403 CJK Ideograph-8403 | 8402 CJK Ideograph-8402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8405 CJK Ideograph-8405 | 8404 CJK Ideograph-8404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8407 CJK Ideograph-8407 | 8406 CJK Ideograph-8406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8409 CJK Ideograph-8409 | 8408 CJK Ideograph-8408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 840b CJK Ideograph-840B | 840a CJK Ideograph-840A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 840d CJK Ideograph-840D | 840c CJK Ideograph-840C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 840f CJK Ideograph-840F | 840e CJK Ideograph-840E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8411 CJK Ideograph-8411 | 8410 CJK Ideograph-8410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8413 CJK Ideograph-8413 | 8412 CJK Ideograph-8412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8415 CJK Ideograph-8415 | 8414 CJK Ideograph-8414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8417 CJK Ideograph-8417 | 8416 CJK Ideograph-8416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8419 CJK Ideograph-8419 | 8418 CJK Ideograph-8418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 841b CJK Ideograph-841B | 841a CJK Ideograph-841A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 841d CJK Ideograph-841D | 841c CJK Ideograph-841C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 841f CJK Ideograph-841F | 841e CJK Ideograph-841E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8421 CJK Ideograph-8421 | 8420 CJK Ideograph-8420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8423 CJK Ideograph-8423 | 8422 CJK Ideograph-8422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8425 CJK Ideograph-8425 | 8424 CJK Ideograph-8424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8427 CJK Ideograph-8427 | 8426 CJK Ideograph-8426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8429 CJK Ideograph-8429 | 8428 CJK Ideograph-8428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 842b CJK Ideograph-842B | 842a CJK Ideograph-842A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 842d CJK Ideograph-842D | 842c CJK Ideograph-842C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 842f CJK Ideograph-842F | 842e CJK Ideograph-842E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8431 CJK Ideograph-8431 | 8430 CJK Ideograph-8430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8433 CJK Ideograph-8433 | 8432 CJK Ideograph-8432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8435 CJK Ideograph-8435 | 8434 CJK Ideograph-8434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8437 CJK Ideograph-8437 | 8436 CJK Ideograph-8436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8439 CJK Ideograph-8439 | 8438 CJK Ideograph-8438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 843b CJK Ideograph-843B | 843a CJK Ideograph-843A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 843d CJK Ideograph-843D | 843c CJK Ideograph-843C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 843f CJK Ideograph-843F | 843e CJK Ideograph-843E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8441 CJK Ideograph-8441 | 8440 CJK Ideograph-8440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8443 CJK Ideograph-8443 | 8442 CJK Ideograph-8442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8445 CJK Ideograph-8445 | 8444 CJK Ideograph-8444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8447 CJK Ideograph-8447 | 8446 CJK Ideograph-8446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8449 CJK Ideograph-8449 | 8448 CJK Ideograph-8448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 844b CJK Ideograph-844B | 844a CJK Ideograph-844A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 844d CJK Ideograph-844D | 844c CJK Ideograph-844C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 844f CJK Ideograph-844F | 844e CJK Ideograph-844E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8451 CJK Ideograph-8451 | 8450 CJK Ideograph-8450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8453 CJK Ideograph-8453 | 8452 CJK Ideograph-8452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8455 CJK Ideograph-8455 | 8454 CJK Ideograph-8454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8457 CJK Ideograph-8457 | 8456 CJK Ideograph-8456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8459 CJK Ideograph-8459 | 8458 CJK Ideograph-8458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 845b CJK Ideograph-845B | 845a CJK Ideograph-845A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 845d CJK Ideograph-845D | 845c CJK Ideograph-845C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 845f CJK Ideograph-845F | 845e CJK Ideograph-845E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8461 CJK Ideograph-8461 | 8460 CJK Ideograph-8460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8463 CJK Ideograph-8463 | 8462 CJK Ideograph-8462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8465 CJK Ideograph-8465 | 8464 CJK Ideograph-8464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8467 CJK Ideograph-8467 | 8466 CJK Ideograph-8466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8469 CJK Ideograph-8469 | 8468 CJK Ideograph-8468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 846b CJK Ideograph-846B | 846a CJK Ideograph-846A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 846d CJK Ideograph-846D | 846c CJK Ideograph-846C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 846f CJK Ideograph-846F | 846e CJK Ideograph-846E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8471 CJK Ideograph-8471 | 8470 CJK Ideograph-8470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8473 CJK Ideograph-8473 | 8472 CJK Ideograph-8472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8475 CJK Ideograph-8475 | 8474 CJK Ideograph-8474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8477 CJK Ideograph-8477 | 8476 CJK Ideograph-8476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8479 CJK Ideograph-8479 | 8478 CJK Ideograph-8478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 847b CJK Ideograph-847B | 847a CJK Ideograph-847A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 847d CJK Ideograph-847D | 847c CJK Ideograph-847C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 847f CJK Ideograph-847F | 847e CJK Ideograph-847E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8481 CJK Ideograph-8481 | 8480 CJK Ideograph-8480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8483 CJK Ideograph-8483 | 8482 CJK Ideograph-8482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8485 CJK Ideograph-8485 | 8484 CJK Ideograph-8484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8487 CJK Ideograph-8487 | 8486 CJK Ideograph-8486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8489 CJK Ideograph-8489 | 8488 CJK Ideograph-8488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 848b CJK Ideograph-848B | 848a CJK Ideograph-848A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 848d CJK Ideograph-848D | 848c CJK Ideograph-848C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 848f CJK Ideograph-848F | 848e CJK Ideograph-848E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8491 CJK Ideograph-8491 | 8490 CJK Ideograph-8490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8493 CJK Ideograph-8493 | 8492 CJK Ideograph-8492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8495 CJK Ideograph-8495 | 8494 CJK Ideograph-8494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8497 CJK Ideograph-8497 | 8496 CJK Ideograph-8496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8499 CJK Ideograph-8499 | 8498 CJK Ideograph-8498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 849b CJK Ideograph-849B | 849a CJK Ideograph-849A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 849d CJK Ideograph-849D | 849c CJK Ideograph-849C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 849f CJK Ideograph-849F | 849e CJK Ideograph-849E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84a1 CJK Ideograph-84A1 | 84a0 CJK Ideograph-84A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84a3 CJK Ideograph-84A3 | 84a2 CJK Ideograph-84A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84a5 CJK Ideograph-84A5 | 84a4 CJK Ideograph-84A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84a7 CJK Ideograph-84A7 | 84a6 CJK Ideograph-84A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84a9 CJK Ideograph-84A9 | 84a8 CJK Ideograph-84A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84ab CJK Ideograph-84AB | 84aa CJK Ideograph-84AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84ad CJK Ideograph-84AD | 84ac CJK Ideograph-84AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84af CJK Ideograph-84AF | 84ae CJK Ideograph-84AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84b1 CJK Ideograph-84B1 | 84b0 CJK Ideograph-84B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84b3 CJK Ideograph-84B3 | 84b2 CJK Ideograph-84B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84b5 CJK Ideograph-84B5 | 84b4 CJK Ideograph-84B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84b7 CJK Ideograph-84B7 | 84b6 CJK Ideograph-84B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84b9 CJK Ideograph-84B9 | 84b8 CJK Ideograph-84B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84bb CJK Ideograph-84BB | 84ba CJK Ideograph-84BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84bd CJK Ideograph-84BD | 84bc CJK Ideograph-84BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84bf CJK Ideograph-84BF | 84be CJK Ideograph-84BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84c1 CJK Ideograph-84C1 | 84c0 CJK Ideograph-84C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84c3 CJK Ideograph-84C3 | 84c2 CJK Ideograph-84C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84c5 CJK Ideograph-84C5 | 84c4 CJK Ideograph-84C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84c7 CJK Ideograph-84C7 | 84c6 CJK Ideograph-84C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84c9 CJK Ideograph-84C9 | 84c8 CJK Ideograph-84C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84cb CJK Ideograph-84CB | 84ca CJK Ideograph-84CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84cd CJK Ideograph-84CD | 84cc CJK Ideograph-84CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84cf CJK Ideograph-84CF | 84ce CJK Ideograph-84CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84d1 CJK Ideograph-84D1 | 84d0 CJK Ideograph-84D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84d3 CJK Ideograph-84D3 | 84d2 CJK Ideograph-84D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84d5 CJK Ideograph-84D5 | 84d4 CJK Ideograph-84D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84d7 CJK Ideograph-84D7 | 84d6 CJK Ideograph-84D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84d9 CJK Ideograph-84D9 | 84d8 CJK Ideograph-84D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84db CJK Ideograph-84DB | 84da CJK Ideograph-84DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84dd CJK Ideograph-84DD | 84dc CJK Ideograph-84DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84df CJK Ideograph-84DF | 84de CJK Ideograph-84DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84e1 CJK Ideograph-84E1 | 84e0 CJK Ideograph-84E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84e3 CJK Ideograph-84E3 | 84e2 CJK Ideograph-84E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84e5 CJK Ideograph-84E5 | 84e4 CJK Ideograph-84E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84e7 CJK Ideograph-84E7 | 84e6 CJK Ideograph-84E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84e9 CJK Ideograph-84E9 | 84e8 CJK Ideograph-84E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84eb CJK Ideograph-84EB | 84ea CJK Ideograph-84EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84ed CJK Ideograph-84ED | 84ec CJK Ideograph-84EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84ef CJK Ideograph-84EF | 84ee CJK Ideograph-84EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84f1 CJK Ideograph-84F1 | 84f0 CJK Ideograph-84F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84f3 CJK Ideograph-84F3 | 84f2 CJK Ideograph-84F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84f5 CJK Ideograph-84F5 | 84f4 CJK Ideograph-84F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84f7 CJK Ideograph-84F7 | 84f6 CJK Ideograph-84F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84f9 CJK Ideograph-84F9 | 84f8 CJK Ideograph-84F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84fb CJK Ideograph-84FB | 84fa CJK Ideograph-84FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84fd CJK Ideograph-84FD | 84fc CJK Ideograph-84FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84ff CJK Ideograph-84FF | 84fe CJK Ideograph-84FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8501 CJK Ideograph-8501 | 8500 CJK Ideograph-8500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8503 CJK Ideograph-8503 | 8502 CJK Ideograph-8502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8505 CJK Ideograph-8505 | 8504 CJK Ideograph-8504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8507 CJK Ideograph-8507 | 8506 CJK Ideograph-8506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8509 CJK Ideograph-8509 | 8508 CJK Ideograph-8508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 850b CJK Ideograph-850B | 850a CJK Ideograph-850A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 850d CJK Ideograph-850D | 850c CJK Ideograph-850C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 850f CJK Ideograph-850F | 850e CJK Ideograph-850E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8511 CJK Ideograph-8511 | 8510 CJK Ideograph-8510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8513 CJK Ideograph-8513 | 8512 CJK Ideograph-8512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8515 CJK Ideograph-8515 | 8514 CJK Ideograph-8514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8517 CJK Ideograph-8517 | 8516 CJK Ideograph-8516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8519 CJK Ideograph-8519 | 8518 CJK Ideograph-8518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 851b CJK Ideograph-851B | 851a CJK Ideograph-851A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 851d CJK Ideograph-851D | 851c CJK Ideograph-851C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 851f CJK Ideograph-851F | 851e CJK Ideograph-851E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8521 CJK Ideograph-8521 | 8520 CJK Ideograph-8520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8523 CJK Ideograph-8523 | 8522 CJK Ideograph-8522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8525 CJK Ideograph-8525 | 8524 CJK Ideograph-8524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8527 CJK Ideograph-8527 | 8526 CJK Ideograph-8526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8529 CJK Ideograph-8529 | 8528 CJK Ideograph-8528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 852b CJK Ideograph-852B | 852a CJK Ideograph-852A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 852d CJK Ideograph-852D | 852c CJK Ideograph-852C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 852f CJK Ideograph-852F | 852e CJK Ideograph-852E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8531 CJK Ideograph-8531 | 8530 CJK Ideograph-8530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8533 CJK Ideograph-8533 | 8532 CJK Ideograph-8532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8535 CJK Ideograph-8535 | 8534 CJK Ideograph-8534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8537 CJK Ideograph-8537 | 8536 CJK Ideograph-8536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8539 CJK Ideograph-8539 | 8538 CJK Ideograph-8538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 853b CJK Ideograph-853B | 853a CJK Ideograph-853A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 853d CJK Ideograph-853D | 853c CJK Ideograph-853C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 853f CJK Ideograph-853F | 853e CJK Ideograph-853E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8541 CJK Ideograph-8541 | 8540 CJK Ideograph-8540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8543 CJK Ideograph-8543 | 8542 CJK Ideograph-8542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8545 CJK Ideograph-8545 | 8544 CJK Ideograph-8544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8547 CJK Ideograph-8547 | 8546 CJK Ideograph-8546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8549 CJK Ideograph-8549 | 8548 CJK Ideograph-8548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 854b CJK Ideograph-854B | 854a CJK Ideograph-854A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 854d CJK Ideograph-854D | 854c CJK Ideograph-854C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 854f CJK Ideograph-854F | 854e CJK Ideograph-854E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8551 CJK Ideograph-8551 | 8550 CJK Ideograph-8550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8553 CJK Ideograph-8553 | 8552 CJK Ideograph-8552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8555 CJK Ideograph-8555 | 8554 CJK Ideograph-8554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8557 CJK Ideograph-8557 | 8556 CJK Ideograph-8556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8559 CJK Ideograph-8559 | 8558 CJK Ideograph-8558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 855b CJK Ideograph-855B | 855a CJK Ideograph-855A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 855d CJK Ideograph-855D | 855c CJK Ideograph-855C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 855f CJK Ideograph-855F | 855e CJK Ideograph-855E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8561 CJK Ideograph-8561 | 8560 CJK Ideograph-8560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8563 CJK Ideograph-8563 | 8562 CJK Ideograph-8562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8565 CJK Ideograph-8565 | 8564 CJK Ideograph-8564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8567 CJK Ideograph-8567 | 8566 CJK Ideograph-8566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8569 CJK Ideograph-8569 | 8568 CJK Ideograph-8568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 856b CJK Ideograph-856B | 856a CJK Ideograph-856A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 856d CJK Ideograph-856D | 856c CJK Ideograph-856C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 856f CJK Ideograph-856F | 856e CJK Ideograph-856E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8571 CJK Ideograph-8571 | 8570 CJK Ideograph-8570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8573 CJK Ideograph-8573 | 8572 CJK Ideograph-8572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8575 CJK Ideograph-8575 | 8574 CJK Ideograph-8574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8577 CJK Ideograph-8577 | 8576 CJK Ideograph-8576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8579 CJK Ideograph-8579 | 8578 CJK Ideograph-8578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 857b CJK Ideograph-857B | 857a CJK Ideograph-857A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 857d CJK Ideograph-857D | 857c CJK Ideograph-857C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 857f CJK Ideograph-857F | 857e CJK Ideograph-857E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8581 CJK Ideograph-8581 | 8580 CJK Ideograph-8580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8583 CJK Ideograph-8583 | 8582 CJK Ideograph-8582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8585 CJK Ideograph-8585 | 8584 CJK Ideograph-8584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8587 CJK Ideograph-8587 | 8586 CJK Ideograph-8586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8589 CJK Ideograph-8589 | 8588 CJK Ideograph-8588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 858b CJK Ideograph-858B | 858a CJK Ideograph-858A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 858d CJK Ideograph-858D | 858c CJK Ideograph-858C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 858f CJK Ideograph-858F | 858e CJK Ideograph-858E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8591 CJK Ideograph-8591 | 8590 CJK Ideograph-8590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8593 CJK Ideograph-8593 | 8592 CJK Ideograph-8592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8595 CJK Ideograph-8595 | 8594 CJK Ideograph-8594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8597 CJK Ideograph-8597 | 8596 CJK Ideograph-8596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8599 CJK Ideograph-8599 | 8598 CJK Ideograph-8598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 859b CJK Ideograph-859B | 859a CJK Ideograph-859A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 859d CJK Ideograph-859D | 859c CJK Ideograph-859C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 859f CJK Ideograph-859F | 859e CJK Ideograph-859E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85a1 CJK Ideograph-85A1 | 85a0 CJK Ideograph-85A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85a3 CJK Ideograph-85A3 | 85a2 CJK Ideograph-85A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85a5 CJK Ideograph-85A5 | 85a4 CJK Ideograph-85A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85a7 CJK Ideograph-85A7 | 85a6 CJK Ideograph-85A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85a9 CJK Ideograph-85A9 | 85a8 CJK Ideograph-85A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85ab CJK Ideograph-85AB | 85aa CJK Ideograph-85AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85ad CJK Ideograph-85AD | 85ac CJK Ideograph-85AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85af CJK Ideograph-85AF | 85ae CJK Ideograph-85AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85b1 CJK Ideograph-85B1 | 85b0 CJK Ideograph-85B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85b3 CJK Ideograph-85B3 | 85b2 CJK Ideograph-85B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85b5 CJK Ideograph-85B5 | 85b4 CJK Ideograph-85B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85b7 CJK Ideograph-85B7 | 85b6 CJK Ideograph-85B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85b9 CJK Ideograph-85B9 | 85b8 CJK Ideograph-85B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85bb CJK Ideograph-85BB | 85ba CJK Ideograph-85BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85bd CJK Ideograph-85BD | 85bc CJK Ideograph-85BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85bf CJK Ideograph-85BF | 85be CJK Ideograph-85BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85c1 CJK Ideograph-85C1 | 85c0 CJK Ideograph-85C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85c3 CJK Ideograph-85C3 | 85c2 CJK Ideograph-85C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85c5 CJK Ideograph-85C5 | 85c4 CJK Ideograph-85C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85c7 CJK Ideograph-85C7 | 85c6 CJK Ideograph-85C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85c9 CJK Ideograph-85C9 | 85c8 CJK Ideograph-85C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85cb CJK Ideograph-85CB | 85ca CJK Ideograph-85CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85cd CJK Ideograph-85CD | 85cc CJK Ideograph-85CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85cf CJK Ideograph-85CF | 85ce CJK Ideograph-85CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85d1 CJK Ideograph-85D1 | 85d0 CJK Ideograph-85D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85d3 CJK Ideograph-85D3 | 85d2 CJK Ideograph-85D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85d5 CJK Ideograph-85D5 | 85d4 CJK Ideograph-85D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85d7 CJK Ideograph-85D7 | 85d6 CJK Ideograph-85D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85d9 CJK Ideograph-85D9 | 85d8 CJK Ideograph-85D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85db CJK Ideograph-85DB | 85da CJK Ideograph-85DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85dd CJK Ideograph-85DD | 85dc CJK Ideograph-85DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85df CJK Ideograph-85DF | 85de CJK Ideograph-85DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85e1 CJK Ideograph-85E1 | 85e0 CJK Ideograph-85E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85e3 CJK Ideograph-85E3 | 85e2 CJK Ideograph-85E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85e5 CJK Ideograph-85E5 | 85e4 CJK Ideograph-85E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85e7 CJK Ideograph-85E7 | 85e6 CJK Ideograph-85E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85e9 CJK Ideograph-85E9 | 85e8 CJK Ideograph-85E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85eb CJK Ideograph-85EB | 85ea CJK Ideograph-85EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85ed CJK Ideograph-85ED | 85ec CJK Ideograph-85EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85ef CJK Ideograph-85EF | 85ee CJK Ideograph-85EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85f1 CJK Ideograph-85F1 | 85f0 CJK Ideograph-85F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85f3 CJK Ideograph-85F3 | 85f2 CJK Ideograph-85F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85f5 CJK Ideograph-85F5 | 85f4 CJK Ideograph-85F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85f7 CJK Ideograph-85F7 | 85f6 CJK Ideograph-85F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85f9 CJK Ideograph-85F9 | 85f8 CJK Ideograph-85F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85fb CJK Ideograph-85FB | 85fa CJK Ideograph-85FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85fd CJK Ideograph-85FD | 85fc CJK Ideograph-85FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85ff CJK Ideograph-85FF | 85fe CJK Ideograph-85FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8601 CJK Ideograph-8601 | 8600 CJK Ideograph-8600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8603 CJK Ideograph-8603 | 8602 CJK Ideograph-8602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8605 CJK Ideograph-8605 | 8604 CJK Ideograph-8604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8607 CJK Ideograph-8607 | 8606 CJK Ideograph-8606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8609 CJK Ideograph-8609 | 8608 CJK Ideograph-8608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 860b CJK Ideograph-860B | 860a CJK Ideograph-860A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 860d CJK Ideograph-860D | 860c CJK Ideograph-860C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 860f CJK Ideograph-860F | 860e CJK Ideograph-860E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8611 CJK Ideograph-8611 | 8610 CJK Ideograph-8610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8613 CJK Ideograph-8613 | 8612 CJK Ideograph-8612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8615 CJK Ideograph-8615 | 8614 CJK Ideograph-8614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8617 CJK Ideograph-8617 | 8616 CJK Ideograph-8616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8619 CJK Ideograph-8619 | 8618 CJK Ideograph-8618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 861b CJK Ideograph-861B | 861a CJK Ideograph-861A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 861d CJK Ideograph-861D | 861c CJK Ideograph-861C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 861f CJK Ideograph-861F | 861e CJK Ideograph-861E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8621 CJK Ideograph-8621 | 8620 CJK Ideograph-8620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8623 CJK Ideograph-8623 | 8622 CJK Ideograph-8622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8625 CJK Ideograph-8625 | 8624 CJK Ideograph-8624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8627 CJK Ideograph-8627 | 8626 CJK Ideograph-8626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8629 CJK Ideograph-8629 | 8628 CJK Ideograph-8628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 862b CJK Ideograph-862B | 862a CJK Ideograph-862A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 862d CJK Ideograph-862D | 862c CJK Ideograph-862C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 862f CJK Ideograph-862F | 862e CJK Ideograph-862E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8631 CJK Ideograph-8631 | 8630 CJK Ideograph-8630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8633 CJK Ideograph-8633 | 8632 CJK Ideograph-8632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8635 CJK Ideograph-8635 | 8634 CJK Ideograph-8634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8637 CJK Ideograph-8637 | 8636 CJK Ideograph-8636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8639 CJK Ideograph-8639 | 8638 CJK Ideograph-8638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 863b CJK Ideograph-863B | 863a CJK Ideograph-863A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 863d CJK Ideograph-863D | 863c CJK Ideograph-863C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 863f CJK Ideograph-863F | 863e CJK Ideograph-863E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8641 CJK Ideograph-8641 | 8640 CJK Ideograph-8640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8643 CJK Ideograph-8643 | 8642 CJK Ideograph-8642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8645 CJK Ideograph-8645 | 8644 CJK Ideograph-8644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8647 CJK Ideograph-8647 | 8646 CJK Ideograph-8646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8649 CJK Ideograph-8649 | 8648 CJK Ideograph-8648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 864b CJK Ideograph-864B | 864a CJK Ideograph-864A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 864d CJK Ideograph-864D | 864c CJK Ideograph-864C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 864f CJK Ideograph-864F | 864e CJK Ideograph-864E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8651 CJK Ideograph-8651 | 8650 CJK Ideograph-8650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8653 CJK Ideograph-8653 | 8652 CJK Ideograph-8652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8655 CJK Ideograph-8655 | 8654 CJK Ideograph-8654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8657 CJK Ideograph-8657 | 8656 CJK Ideograph-8656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8659 CJK Ideograph-8659 | 8658 CJK Ideograph-8658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 865b CJK Ideograph-865B | 865a CJK Ideograph-865A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 865d CJK Ideograph-865D | 865c CJK Ideograph-865C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 865f CJK Ideograph-865F | 865e CJK Ideograph-865E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8661 CJK Ideograph-8661 | 8660 CJK Ideograph-8660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8663 CJK Ideograph-8663 | 8662 CJK Ideograph-8662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8665 CJK Ideograph-8665 | 8664 CJK Ideograph-8664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8667 CJK Ideograph-8667 | 8666 CJK Ideograph-8666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8669 CJK Ideograph-8669 | 8668 CJK Ideograph-8668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 866b CJK Ideograph-866B | 866a CJK Ideograph-866A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 866d CJK Ideograph-866D | 866c CJK Ideograph-866C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 866f CJK Ideograph-866F | 866e CJK Ideograph-866E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8671 CJK Ideograph-8671 | 8670 CJK Ideograph-8670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8673 CJK Ideograph-8673 | 8672 CJK Ideograph-8672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8675 CJK Ideograph-8675 | 8674 CJK Ideograph-8674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8677 CJK Ideograph-8677 | 8676 CJK Ideograph-8676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8679 CJK Ideograph-8679 | 8678 CJK Ideograph-8678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 867b CJK Ideograph-867B | 867a CJK Ideograph-867A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 867d CJK Ideograph-867D | 867c CJK Ideograph-867C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 867f CJK Ideograph-867F | 867e CJK Ideograph-867E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8681 CJK Ideograph-8681 | 8680 CJK Ideograph-8680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8683 CJK Ideograph-8683 | 8682 CJK Ideograph-8682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8685 CJK Ideograph-8685 | 8684 CJK Ideograph-8684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8687 CJK Ideograph-8687 | 8686 CJK Ideograph-8686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8689 CJK Ideograph-8689 | 8688 CJK Ideograph-8688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 868b CJK Ideograph-868B | 868a CJK Ideograph-868A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 868d CJK Ideograph-868D | 868c CJK Ideograph-868C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 868f CJK Ideograph-868F | 868e CJK Ideograph-868E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8691 CJK Ideograph-8691 | 8690 CJK Ideograph-8690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8693 CJK Ideograph-8693 | 8692 CJK Ideograph-8692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8695 CJK Ideograph-8695 | 8694 CJK Ideograph-8694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8697 CJK Ideograph-8697 | 8696 CJK Ideograph-8696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8699 CJK Ideograph-8699 | 8698 CJK Ideograph-8698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 869b CJK Ideograph-869B | 869a CJK Ideograph-869A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 869d CJK Ideograph-869D | 869c CJK Ideograph-869C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 869f CJK Ideograph-869F | 869e CJK Ideograph-869E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86a1 CJK Ideograph-86A1 | 86a0 CJK Ideograph-86A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86a3 CJK Ideograph-86A3 | 86a2 CJK Ideograph-86A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86a5 CJK Ideograph-86A5 | 86a4 CJK Ideograph-86A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86a7 CJK Ideograph-86A7 | 86a6 CJK Ideograph-86A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86a9 CJK Ideograph-86A9 | 86a8 CJK Ideograph-86A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86ab CJK Ideograph-86AB | 86aa CJK Ideograph-86AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86ad CJK Ideograph-86AD | 86ac CJK Ideograph-86AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86af CJK Ideograph-86AF | 86ae CJK Ideograph-86AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86b1 CJK Ideograph-86B1 | 86b0 CJK Ideograph-86B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86b3 CJK Ideograph-86B3 | 86b2 CJK Ideograph-86B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86b5 CJK Ideograph-86B5 | 86b4 CJK Ideograph-86B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86b7 CJK Ideograph-86B7 | 86b6 CJK Ideograph-86B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86b9 CJK Ideograph-86B9 | 86b8 CJK Ideograph-86B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86bb CJK Ideograph-86BB | 86ba CJK Ideograph-86BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86bd CJK Ideograph-86BD | 86bc CJK Ideograph-86BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86bf CJK Ideograph-86BF | 86be CJK Ideograph-86BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86c1 CJK Ideograph-86C1 | 86c0 CJK Ideograph-86C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86c3 CJK Ideograph-86C3 | 86c2 CJK Ideograph-86C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86c5 CJK Ideograph-86C5 | 86c4 CJK Ideograph-86C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86c7 CJK Ideograph-86C7 | 86c6 CJK Ideograph-86C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86c9 CJK Ideograph-86C9 | 86c8 CJK Ideograph-86C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86cb CJK Ideograph-86CB | 86ca CJK Ideograph-86CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86cd CJK Ideograph-86CD | 86cc CJK Ideograph-86CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86cf CJK Ideograph-86CF | 86ce CJK Ideograph-86CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86d1 CJK Ideograph-86D1 | 86d0 CJK Ideograph-86D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86d3 CJK Ideograph-86D3 | 86d2 CJK Ideograph-86D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86d5 CJK Ideograph-86D5 | 86d4 CJK Ideograph-86D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86d7 CJK Ideograph-86D7 | 86d6 CJK Ideograph-86D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86d9 CJK Ideograph-86D9 | 86d8 CJK Ideograph-86D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86db CJK Ideograph-86DB | 86da CJK Ideograph-86DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86dd CJK Ideograph-86DD | 86dc CJK Ideograph-86DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86df CJK Ideograph-86DF | 86de CJK Ideograph-86DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86e1 CJK Ideograph-86E1 | 86e0 CJK Ideograph-86E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86e3 CJK Ideograph-86E3 | 86e2 CJK Ideograph-86E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86e5 CJK Ideograph-86E5 | 86e4 CJK Ideograph-86E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86e7 CJK Ideograph-86E7 | 86e6 CJK Ideograph-86E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86e9 CJK Ideograph-86E9 | 86e8 CJK Ideograph-86E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86eb CJK Ideograph-86EB | 86ea CJK Ideograph-86EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86ed CJK Ideograph-86ED | 86ec CJK Ideograph-86EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86ef CJK Ideograph-86EF | 86ee CJK Ideograph-86EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86f1 CJK Ideograph-86F1 | 86f0 CJK Ideograph-86F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86f3 CJK Ideograph-86F3 | 86f2 CJK Ideograph-86F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86f5 CJK Ideograph-86F5 | 86f4 CJK Ideograph-86F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86f7 CJK Ideograph-86F7 | 86f6 CJK Ideograph-86F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86f9 CJK Ideograph-86F9 | 86f8 CJK Ideograph-86F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86fb CJK Ideograph-86FB | 86fa CJK Ideograph-86FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86fd CJK Ideograph-86FD | 86fc CJK Ideograph-86FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86ff CJK Ideograph-86FF | 86fe CJK Ideograph-86FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8701 CJK Ideograph-8701 | 8700 CJK Ideograph-8700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8703 CJK Ideograph-8703 | 8702 CJK Ideograph-8702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8705 CJK Ideograph-8705 | 8704 CJK Ideograph-8704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8707 CJK Ideograph-8707 | 8706 CJK Ideograph-8706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8709 CJK Ideograph-8709 | 8708 CJK Ideograph-8708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 870b CJK Ideograph-870B | 870a CJK Ideograph-870A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 870d CJK Ideograph-870D | 870c CJK Ideograph-870C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 870f CJK Ideograph-870F | 870e CJK Ideograph-870E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8711 CJK Ideograph-8711 | 8710 CJK Ideograph-8710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8713 CJK Ideograph-8713 | 8712 CJK Ideograph-8712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8715 CJK Ideograph-8715 | 8714 CJK Ideograph-8714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8717 CJK Ideograph-8717 | 8716 CJK Ideograph-8716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8719 CJK Ideograph-8719 | 8718 CJK Ideograph-8718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 871b CJK Ideograph-871B | 871a CJK Ideograph-871A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 871d CJK Ideograph-871D | 871c CJK Ideograph-871C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 871f CJK Ideograph-871F | 871e CJK Ideograph-871E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8721 CJK Ideograph-8721 | 8720 CJK Ideograph-8720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8723 CJK Ideograph-8723 | 8722 CJK Ideograph-8722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8725 CJK Ideograph-8725 | 8724 CJK Ideograph-8724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8727 CJK Ideograph-8727 | 8726 CJK Ideograph-8726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8729 CJK Ideograph-8729 | 8728 CJK Ideograph-8728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 872b CJK Ideograph-872B | 872a CJK Ideograph-872A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 872d CJK Ideograph-872D | 872c CJK Ideograph-872C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 872f CJK Ideograph-872F | 872e CJK Ideograph-872E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8731 CJK Ideograph-8731 | 8730 CJK Ideograph-8730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8733 CJK Ideograph-8733 | 8732 CJK Ideograph-8732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8735 CJK Ideograph-8735 | 8734 CJK Ideograph-8734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8737 CJK Ideograph-8737 | 8736 CJK Ideograph-8736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8739 CJK Ideograph-8739 | 8738 CJK Ideograph-8738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 873b CJK Ideograph-873B | 873a CJK Ideograph-873A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 873d CJK Ideograph-873D | 873c CJK Ideograph-873C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 873f CJK Ideograph-873F | 873e CJK Ideograph-873E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8741 CJK Ideograph-8741 | 8740 CJK Ideograph-8740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8743 CJK Ideograph-8743 | 8742 CJK Ideograph-8742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8745 CJK Ideograph-8745 | 8744 CJK Ideograph-8744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8747 CJK Ideograph-8747 | 8746 CJK Ideograph-8746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8749 CJK Ideograph-8749 | 8748 CJK Ideograph-8748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 874b CJK Ideograph-874B | 874a CJK Ideograph-874A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 874d CJK Ideograph-874D | 874c CJK Ideograph-874C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 874f CJK Ideograph-874F | 874e CJK Ideograph-874E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8751 CJK Ideograph-8751 | 8750 CJK Ideograph-8750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8753 CJK Ideograph-8753 | 8752 CJK Ideograph-8752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8755 CJK Ideograph-8755 | 8754 CJK Ideograph-8754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8757 CJK Ideograph-8757 | 8756 CJK Ideograph-8756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8759 CJK Ideograph-8759 | 8758 CJK Ideograph-8758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 875b CJK Ideograph-875B | 875a CJK Ideograph-875A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 875d CJK Ideograph-875D | 875c CJK Ideograph-875C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 875f CJK Ideograph-875F | 875e CJK Ideograph-875E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8761 CJK Ideograph-8761 | 8760 CJK Ideograph-8760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8763 CJK Ideograph-8763 | 8762 CJK Ideograph-8762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8765 CJK Ideograph-8765 | 8764 CJK Ideograph-8764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8767 CJK Ideograph-8767 | 8766 CJK Ideograph-8766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8769 CJK Ideograph-8769 | 8768 CJK Ideograph-8768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 876b CJK Ideograph-876B | 876a CJK Ideograph-876A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 876d CJK Ideograph-876D | 876c CJK Ideograph-876C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 876f CJK Ideograph-876F | 876e CJK Ideograph-876E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8771 CJK Ideograph-8771 | 8770 CJK Ideograph-8770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8773 CJK Ideograph-8773 | 8772 CJK Ideograph-8772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8775 CJK Ideograph-8775 | 8774 CJK Ideograph-8774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8777 CJK Ideograph-8777 | 8776 CJK Ideograph-8776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8779 CJK Ideograph-8779 | 8778 CJK Ideograph-8778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 877b CJK Ideograph-877B | 877a CJK Ideograph-877A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 877d CJK Ideograph-877D | 877c CJK Ideograph-877C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 877f CJK Ideograph-877F | 877e CJK Ideograph-877E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8781 CJK Ideograph-8781 | 8780 CJK Ideograph-8780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8783 CJK Ideograph-8783 | 8782 CJK Ideograph-8782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8785 CJK Ideograph-8785 | 8784 CJK Ideograph-8784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8787 CJK Ideograph-8787 | 8786 CJK Ideograph-8786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8789 CJK Ideograph-8789 | 8788 CJK Ideograph-8788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 878b CJK Ideograph-878B | 878a CJK Ideograph-878A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 878d CJK Ideograph-878D | 878c CJK Ideograph-878C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 878f CJK Ideograph-878F | 878e CJK Ideograph-878E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8791 CJK Ideograph-8791 | 8790 CJK Ideograph-8790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8793 CJK Ideograph-8793 | 8792 CJK Ideograph-8792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8795 CJK Ideograph-8795 | 8794 CJK Ideograph-8794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8797 CJK Ideograph-8797 | 8796 CJK Ideograph-8796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8799 CJK Ideograph-8799 | 8798 CJK Ideograph-8798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 879b CJK Ideograph-879B | 879a CJK Ideograph-879A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 879d CJK Ideograph-879D | 879c CJK Ideograph-879C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 879f CJK Ideograph-879F | 879e CJK Ideograph-879E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87a1 CJK Ideograph-87A1 | 87a0 CJK Ideograph-87A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87a3 CJK Ideograph-87A3 | 87a2 CJK Ideograph-87A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87a5 CJK Ideograph-87A5 | 87a4 CJK Ideograph-87A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87a7 CJK Ideograph-87A7 | 87a6 CJK Ideograph-87A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87a9 CJK Ideograph-87A9 | 87a8 CJK Ideograph-87A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87ab CJK Ideograph-87AB | 87aa CJK Ideograph-87AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87ad CJK Ideograph-87AD | 87ac CJK Ideograph-87AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87af CJK Ideograph-87AF | 87ae CJK Ideograph-87AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87b1 CJK Ideograph-87B1 | 87b0 CJK Ideograph-87B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87b3 CJK Ideograph-87B3 | 87b2 CJK Ideograph-87B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87b5 CJK Ideograph-87B5 | 87b4 CJK Ideograph-87B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87b7 CJK Ideograph-87B7 | 87b6 CJK Ideograph-87B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87b9 CJK Ideograph-87B9 | 87b8 CJK Ideograph-87B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87bb CJK Ideograph-87BB | 87ba CJK Ideograph-87BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87bd CJK Ideograph-87BD | 87bc CJK Ideograph-87BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87bf CJK Ideograph-87BF | 87be CJK Ideograph-87BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87c1 CJK Ideograph-87C1 | 87c0 CJK Ideograph-87C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87c3 CJK Ideograph-87C3 | 87c2 CJK Ideograph-87C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87c5 CJK Ideograph-87C5 | 87c4 CJK Ideograph-87C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87c7 CJK Ideograph-87C7 | 87c6 CJK Ideograph-87C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87c9 CJK Ideograph-87C9 | 87c8 CJK Ideograph-87C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87cb CJK Ideograph-87CB | 87ca CJK Ideograph-87CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87cd CJK Ideograph-87CD | 87cc CJK Ideograph-87CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87cf CJK Ideograph-87CF | 87ce CJK Ideograph-87CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87d1 CJK Ideograph-87D1 | 87d0 CJK Ideograph-87D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87d3 CJK Ideograph-87D3 | 87d2 CJK Ideograph-87D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87d5 CJK Ideograph-87D5 | 87d4 CJK Ideograph-87D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87d7 CJK Ideograph-87D7 | 87d6 CJK Ideograph-87D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87d9 CJK Ideograph-87D9 | 87d8 CJK Ideograph-87D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87db CJK Ideograph-87DB | 87da CJK Ideograph-87DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87dd CJK Ideograph-87DD | 87dc CJK Ideograph-87DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87df CJK Ideograph-87DF | 87de CJK Ideograph-87DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87e1 CJK Ideograph-87E1 | 87e0 CJK Ideograph-87E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87e3 CJK Ideograph-87E3 | 87e2 CJK Ideograph-87E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87e5 CJK Ideograph-87E5 | 87e4 CJK Ideograph-87E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87e7 CJK Ideograph-87E7 | 87e6 CJK Ideograph-87E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87e9 CJK Ideograph-87E9 | 87e8 CJK Ideograph-87E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87eb CJK Ideograph-87EB | 87ea CJK Ideograph-87EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87ed CJK Ideograph-87ED | 87ec CJK Ideograph-87EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87ef CJK Ideograph-87EF | 87ee CJK Ideograph-87EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87f1 CJK Ideograph-87F1 | 87f0 CJK Ideograph-87F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87f3 CJK Ideograph-87F3 | 87f2 CJK Ideograph-87F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87f5 CJK Ideograph-87F5 | 87f4 CJK Ideograph-87F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87f7 CJK Ideograph-87F7 | 87f6 CJK Ideograph-87F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87f9 CJK Ideograph-87F9 | 87f8 CJK Ideograph-87F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87fb CJK Ideograph-87FB | 87fa CJK Ideograph-87FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87fd CJK Ideograph-87FD | 87fc CJK Ideograph-87FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87ff CJK Ideograph-87FF | 87fe CJK Ideograph-87FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8801 CJK Ideograph-8801 | 8800 CJK Ideograph-8800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8803 CJK Ideograph-8803 | 8802 CJK Ideograph-8802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8805 CJK Ideograph-8805 | 8804 CJK Ideograph-8804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8807 CJK Ideograph-8807 | 8806 CJK Ideograph-8806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8809 CJK Ideograph-8809 | 8808 CJK Ideograph-8808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 880b CJK Ideograph-880B | 880a CJK Ideograph-880A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 880d CJK Ideograph-880D | 880c CJK Ideograph-880C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 880f CJK Ideograph-880F | 880e CJK Ideograph-880E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8811 CJK Ideograph-8811 | 8810 CJK Ideograph-8810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8813 CJK Ideograph-8813 | 8812 CJK Ideograph-8812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8815 CJK Ideograph-8815 | 8814 CJK Ideograph-8814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8817 CJK Ideograph-8817 | 8816 CJK Ideograph-8816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8819 CJK Ideograph-8819 | 8818 CJK Ideograph-8818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 881b CJK Ideograph-881B | 881a CJK Ideograph-881A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 881d CJK Ideograph-881D | 881c CJK Ideograph-881C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 881f CJK Ideograph-881F | 881e CJK Ideograph-881E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8821 CJK Ideograph-8821 | 8820 CJK Ideograph-8820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8823 CJK Ideograph-8823 | 8822 CJK Ideograph-8822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8825 CJK Ideograph-8825 | 8824 CJK Ideograph-8824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8827 CJK Ideograph-8827 | 8826 CJK Ideograph-8826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8829 CJK Ideograph-8829 | 8828 CJK Ideograph-8828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 882b CJK Ideograph-882B | 882a CJK Ideograph-882A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 882d CJK Ideograph-882D | 882c CJK Ideograph-882C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 882f CJK Ideograph-882F | 882e CJK Ideograph-882E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8831 CJK Ideograph-8831 | 8830 CJK Ideograph-8830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8833 CJK Ideograph-8833 | 8832 CJK Ideograph-8832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8835 CJK Ideograph-8835 | 8834 CJK Ideograph-8834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8837 CJK Ideograph-8837 | 8836 CJK Ideograph-8836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8839 CJK Ideograph-8839 | 8838 CJK Ideograph-8838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 883b CJK Ideograph-883B | 883a CJK Ideograph-883A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 883d CJK Ideograph-883D | 883c CJK Ideograph-883C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 883f CJK Ideograph-883F | 883e CJK Ideograph-883E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8841 CJK Ideograph-8841 | 8840 CJK Ideograph-8840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8843 CJK Ideograph-8843 | 8842 CJK Ideograph-8842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8845 CJK Ideograph-8845 | 8844 CJK Ideograph-8844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8847 CJK Ideograph-8847 | 8846 CJK Ideograph-8846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8849 CJK Ideograph-8849 | 8848 CJK Ideograph-8848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 884b CJK Ideograph-884B | 884a CJK Ideograph-884A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 884d CJK Ideograph-884D | 884c CJK Ideograph-884C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 884f CJK Ideograph-884F | 884e CJK Ideograph-884E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8851 CJK Ideograph-8851 | 8850 CJK Ideograph-8850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8853 CJK Ideograph-8853 | 8852 CJK Ideograph-8852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8855 CJK Ideograph-8855 | 8854 CJK Ideograph-8854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8857 CJK Ideograph-8857 | 8856 CJK Ideograph-8856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8859 CJK Ideograph-8859 | 8858 CJK Ideograph-8858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 885b CJK Ideograph-885B | 885a CJK Ideograph-885A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 885d CJK Ideograph-885D | 885c CJK Ideograph-885C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 885f CJK Ideograph-885F | 885e CJK Ideograph-885E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8861 CJK Ideograph-8861 | 8860 CJK Ideograph-8860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8863 CJK Ideograph-8863 | 8862 CJK Ideograph-8862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8865 CJK Ideograph-8865 | 8864 CJK Ideograph-8864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8867 CJK Ideograph-8867 | 8866 CJK Ideograph-8866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8869 CJK Ideograph-8869 | 8868 CJK Ideograph-8868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 886b CJK Ideograph-886B | 886a CJK Ideograph-886A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 886d CJK Ideograph-886D | 886c CJK Ideograph-886C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 886f CJK Ideograph-886F | 886e CJK Ideograph-886E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8871 CJK Ideograph-8871 | 8870 CJK Ideograph-8870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8873 CJK Ideograph-8873 | 8872 CJK Ideograph-8872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8875 CJK Ideograph-8875 | 8874 CJK Ideograph-8874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8877 CJK Ideograph-8877 | 8876 CJK Ideograph-8876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8879 CJK Ideograph-8879 | 8878 CJK Ideograph-8878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 887b CJK Ideograph-887B | 887a CJK Ideograph-887A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 887d CJK Ideograph-887D | 887c CJK Ideograph-887C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 887f CJK Ideograph-887F | 887e CJK Ideograph-887E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8881 CJK Ideograph-8881 | 8880 CJK Ideograph-8880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8883 CJK Ideograph-8883 | 8882 CJK Ideograph-8882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8885 CJK Ideograph-8885 | 8884 CJK Ideograph-8884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8887 CJK Ideograph-8887 | 8886 CJK Ideograph-8886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8889 CJK Ideograph-8889 | 8888 CJK Ideograph-8888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 888b CJK Ideograph-888B | 888a CJK Ideograph-888A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 888d CJK Ideograph-888D | 888c CJK Ideograph-888C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 888f CJK Ideograph-888F | 888e CJK Ideograph-888E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8891 CJK Ideograph-8891 | 8890 CJK Ideograph-8890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8893 CJK Ideograph-8893 | 8892 CJK Ideograph-8892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8895 CJK Ideograph-8895 | 8894 CJK Ideograph-8894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8897 CJK Ideograph-8897 | 8896 CJK Ideograph-8896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8899 CJK Ideograph-8899 | 8898 CJK Ideograph-8898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 889b CJK Ideograph-889B | 889a CJK Ideograph-889A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 889d CJK Ideograph-889D | 889c CJK Ideograph-889C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 889f CJK Ideograph-889F | 889e CJK Ideograph-889E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88a1 CJK Ideograph-88A1 | 88a0 CJK Ideograph-88A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88a3 CJK Ideograph-88A3 | 88a2 CJK Ideograph-88A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88a5 CJK Ideograph-88A5 | 88a4 CJK Ideograph-88A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88a7 CJK Ideograph-88A7 | 88a6 CJK Ideograph-88A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88a9 CJK Ideograph-88A9 | 88a8 CJK Ideograph-88A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88ab CJK Ideograph-88AB | 88aa CJK Ideograph-88AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88ad CJK Ideograph-88AD | 88ac CJK Ideograph-88AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88af CJK Ideograph-88AF | 88ae CJK Ideograph-88AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88b1 CJK Ideograph-88B1 | 88b0 CJK Ideograph-88B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88b3 CJK Ideograph-88B3 | 88b2 CJK Ideograph-88B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88b5 CJK Ideograph-88B5 | 88b4 CJK Ideograph-88B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88b7 CJK Ideograph-88B7 | 88b6 CJK Ideograph-88B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88b9 CJK Ideograph-88B9 | 88b8 CJK Ideograph-88B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88bb CJK Ideograph-88BB | 88ba CJK Ideograph-88BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88bd CJK Ideograph-88BD | 88bc CJK Ideograph-88BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88bf CJK Ideograph-88BF | 88be CJK Ideograph-88BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88c1 CJK Ideograph-88C1 | 88c0 CJK Ideograph-88C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88c3 CJK Ideograph-88C3 | 88c2 CJK Ideograph-88C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88c5 CJK Ideograph-88C5 | 88c4 CJK Ideograph-88C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88c7 CJK Ideograph-88C7 | 88c6 CJK Ideograph-88C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88c9 CJK Ideograph-88C9 | 88c8 CJK Ideograph-88C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88cb CJK Ideograph-88CB | 88ca CJK Ideograph-88CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88cd CJK Ideograph-88CD | 88cc CJK Ideograph-88CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88cf CJK Ideograph-88CF | 88ce CJK Ideograph-88CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88d1 CJK Ideograph-88D1 | 88d0 CJK Ideograph-88D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88d3 CJK Ideograph-88D3 | 88d2 CJK Ideograph-88D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88d5 CJK Ideograph-88D5 | 88d4 CJK Ideograph-88D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88d7 CJK Ideograph-88D7 | 88d6 CJK Ideograph-88D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88d9 CJK Ideograph-88D9 | 88d8 CJK Ideograph-88D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88db CJK Ideograph-88DB | 88da CJK Ideograph-88DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88dd CJK Ideograph-88DD | 88dc CJK Ideograph-88DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88df CJK Ideograph-88DF | 88de CJK Ideograph-88DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88e1 CJK Ideograph-88E1 | 88e0 CJK Ideograph-88E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88e3 CJK Ideograph-88E3 | 88e2 CJK Ideograph-88E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88e5 CJK Ideograph-88E5 | 88e4 CJK Ideograph-88E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88e7 CJK Ideograph-88E7 | 88e6 CJK Ideograph-88E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88e9 CJK Ideograph-88E9 | 88e8 CJK Ideograph-88E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88eb CJK Ideograph-88EB | 88ea CJK Ideograph-88EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88ed CJK Ideograph-88ED | 88ec CJK Ideograph-88EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88ef CJK Ideograph-88EF | 88ee CJK Ideograph-88EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88f1 CJK Ideograph-88F1 | 88f0 CJK Ideograph-88F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88f3 CJK Ideograph-88F3 | 88f2 CJK Ideograph-88F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88f5 CJK Ideograph-88F5 | 88f4 CJK Ideograph-88F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88f7 CJK Ideograph-88F7 | 88f6 CJK Ideograph-88F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88f9 CJK Ideograph-88F9 | 88f8 CJK Ideograph-88F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88fb CJK Ideograph-88FB | 88fa CJK Ideograph-88FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88fd CJK Ideograph-88FD | 88fc CJK Ideograph-88FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88ff CJK Ideograph-88FF | 88fe CJK Ideograph-88FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8901 CJK Ideograph-8901 | 8900 CJK Ideograph-8900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8903 CJK Ideograph-8903 | 8902 CJK Ideograph-8902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8905 CJK Ideograph-8905 | 8904 CJK Ideograph-8904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8907 CJK Ideograph-8907 | 8906 CJK Ideograph-8906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8909 CJK Ideograph-8909 | 8908 CJK Ideograph-8908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 890b CJK Ideograph-890B | 890a CJK Ideograph-890A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 890d CJK Ideograph-890D | 890c CJK Ideograph-890C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 890f CJK Ideograph-890F | 890e CJK Ideograph-890E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8911 CJK Ideograph-8911 | 8910 CJK Ideograph-8910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8913 CJK Ideograph-8913 | 8912 CJK Ideograph-8912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8915 CJK Ideograph-8915 | 8914 CJK Ideograph-8914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8917 CJK Ideograph-8917 | 8916 CJK Ideograph-8916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8919 CJK Ideograph-8919 | 8918 CJK Ideograph-8918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 891b CJK Ideograph-891B | 891a CJK Ideograph-891A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 891d CJK Ideograph-891D | 891c CJK Ideograph-891C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 891f CJK Ideograph-891F | 891e CJK Ideograph-891E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8921 CJK Ideograph-8921 | 8920 CJK Ideograph-8920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8923 CJK Ideograph-8923 | 8922 CJK Ideograph-8922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8925 CJK Ideograph-8925 | 8924 CJK Ideograph-8924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8927 CJK Ideograph-8927 | 8926 CJK Ideograph-8926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8929 CJK Ideograph-8929 | 8928 CJK Ideograph-8928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 892b CJK Ideograph-892B | 892a CJK Ideograph-892A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 892d CJK Ideograph-892D | 892c CJK Ideograph-892C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 892f CJK Ideograph-892F | 892e CJK Ideograph-892E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8931 CJK Ideograph-8931 | 8930 CJK Ideograph-8930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8933 CJK Ideograph-8933 | 8932 CJK Ideograph-8932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8935 CJK Ideograph-8935 | 8934 CJK Ideograph-8934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8937 CJK Ideograph-8937 | 8936 CJK Ideograph-8936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8939 CJK Ideograph-8939 | 8938 CJK Ideograph-8938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 893b CJK Ideograph-893B | 893a CJK Ideograph-893A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 893d CJK Ideograph-893D | 893c CJK Ideograph-893C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 893f CJK Ideograph-893F | 893e CJK Ideograph-893E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8941 CJK Ideograph-8941 | 8940 CJK Ideograph-8940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8943 CJK Ideograph-8943 | 8942 CJK Ideograph-8942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8945 CJK Ideograph-8945 | 8944 CJK Ideograph-8944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8947 CJK Ideograph-8947 | 8946 CJK Ideograph-8946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8949 CJK Ideograph-8949 | 8948 CJK Ideograph-8948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 894b CJK Ideograph-894B | 894a CJK Ideograph-894A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 894d CJK Ideograph-894D | 894c CJK Ideograph-894C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 894f CJK Ideograph-894F | 894e CJK Ideograph-894E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8951 CJK Ideograph-8951 | 8950 CJK Ideograph-8950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8953 CJK Ideograph-8953 | 8952 CJK Ideograph-8952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8955 CJK Ideograph-8955 | 8954 CJK Ideograph-8954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8957 CJK Ideograph-8957 | 8956 CJK Ideograph-8956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8959 CJK Ideograph-8959 | 8958 CJK Ideograph-8958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 895b CJK Ideograph-895B | 895a CJK Ideograph-895A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 895d CJK Ideograph-895D | 895c CJK Ideograph-895C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 895f CJK Ideograph-895F | 895e CJK Ideograph-895E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8961 CJK Ideograph-8961 | 8960 CJK Ideograph-8960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8963 CJK Ideograph-8963 | 8962 CJK Ideograph-8962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8965 CJK Ideograph-8965 | 8964 CJK Ideograph-8964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8967 CJK Ideograph-8967 | 8966 CJK Ideograph-8966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8969 CJK Ideograph-8969 | 8968 CJK Ideograph-8968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 896b CJK Ideograph-896B | 896a CJK Ideograph-896A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 896d CJK Ideograph-896D | 896c CJK Ideograph-896C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 896f CJK Ideograph-896F | 896e CJK Ideograph-896E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8971 CJK Ideograph-8971 | 8970 CJK Ideograph-8970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8973 CJK Ideograph-8973 | 8972 CJK Ideograph-8972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8975 CJK Ideograph-8975 | 8974 CJK Ideograph-8974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8977 CJK Ideograph-8977 | 8976 CJK Ideograph-8976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8979 CJK Ideograph-8979 | 8978 CJK Ideograph-8978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 897b CJK Ideograph-897B | 897a CJK Ideograph-897A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 897d CJK Ideograph-897D | 897c CJK Ideograph-897C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 897f CJK Ideograph-897F | 897e CJK Ideograph-897E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8981 CJK Ideograph-8981 | 8980 CJK Ideograph-8980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8983 CJK Ideograph-8983 | 8982 CJK Ideograph-8982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8985 CJK Ideograph-8985 | 8984 CJK Ideograph-8984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8987 CJK Ideograph-8987 | 8986 CJK Ideograph-8986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8989 CJK Ideograph-8989 | 8988 CJK Ideograph-8988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 898b CJK Ideograph-898B | 898a CJK Ideograph-898A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 898d CJK Ideograph-898D | 898c CJK Ideograph-898C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 898f CJK Ideograph-898F | 898e CJK Ideograph-898E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8991 CJK Ideograph-8991 | 8990 CJK Ideograph-8990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8993 CJK Ideograph-8993 | 8992 CJK Ideograph-8992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8995 CJK Ideograph-8995 | 8994 CJK Ideograph-8994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8997 CJK Ideograph-8997 | 8996 CJK Ideograph-8996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8999 CJK Ideograph-8999 | 8998 CJK Ideograph-8998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 899b CJK Ideograph-899B | 899a CJK Ideograph-899A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 899d CJK Ideograph-899D | 899c CJK Ideograph-899C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 899f CJK Ideograph-899F | 899e CJK Ideograph-899E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89a1 CJK Ideograph-89A1 | 89a0 CJK Ideograph-89A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89a3 CJK Ideograph-89A3 | 89a2 CJK Ideograph-89A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89a5 CJK Ideograph-89A5 | 89a4 CJK Ideograph-89A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89a7 CJK Ideograph-89A7 | 89a6 CJK Ideograph-89A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89a9 CJK Ideograph-89A9 | 89a8 CJK Ideograph-89A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89ab CJK Ideograph-89AB | 89aa CJK Ideograph-89AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89ad CJK Ideograph-89AD | 89ac CJK Ideograph-89AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89af CJK Ideograph-89AF | 89ae CJK Ideograph-89AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89b1 CJK Ideograph-89B1 | 89b0 CJK Ideograph-89B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89b3 CJK Ideograph-89B3 | 89b2 CJK Ideograph-89B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89b5 CJK Ideograph-89B5 | 89b4 CJK Ideograph-89B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89b7 CJK Ideograph-89B7 | 89b6 CJK Ideograph-89B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89b9 CJK Ideograph-89B9 | 89b8 CJK Ideograph-89B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89bb CJK Ideograph-89BB | 89ba CJK Ideograph-89BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89bd CJK Ideograph-89BD | 89bc CJK Ideograph-89BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89bf CJK Ideograph-89BF | 89be CJK Ideograph-89BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89c1 CJK Ideograph-89C1 | 89c0 CJK Ideograph-89C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89c3 CJK Ideograph-89C3 | 89c2 CJK Ideograph-89C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89c5 CJK Ideograph-89C5 | 89c4 CJK Ideograph-89C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89c7 CJK Ideograph-89C7 | 89c6 CJK Ideograph-89C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89c9 CJK Ideograph-89C9 | 89c8 CJK Ideograph-89C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89cb CJK Ideograph-89CB | 89ca CJK Ideograph-89CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89cd CJK Ideograph-89CD | 89cc CJK Ideograph-89CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89cf CJK Ideograph-89CF | 89ce CJK Ideograph-89CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89d1 CJK Ideograph-89D1 | 89d0 CJK Ideograph-89D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89d3 CJK Ideograph-89D3 | 89d2 CJK Ideograph-89D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89d5 CJK Ideograph-89D5 | 89d4 CJK Ideograph-89D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89d7 CJK Ideograph-89D7 | 89d6 CJK Ideograph-89D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89d9 CJK Ideograph-89D9 | 89d8 CJK Ideograph-89D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89db CJK Ideograph-89DB | 89da CJK Ideograph-89DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89dd CJK Ideograph-89DD | 89dc CJK Ideograph-89DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89df CJK Ideograph-89DF | 89de CJK Ideograph-89DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89e1 CJK Ideograph-89E1 | 89e0 CJK Ideograph-89E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89e3 CJK Ideograph-89E3 | 89e2 CJK Ideograph-89E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89e5 CJK Ideograph-89E5 | 89e4 CJK Ideograph-89E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89e7 CJK Ideograph-89E7 | 89e6 CJK Ideograph-89E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89e9 CJK Ideograph-89E9 | 89e8 CJK Ideograph-89E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89eb CJK Ideograph-89EB | 89ea CJK Ideograph-89EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89ed CJK Ideograph-89ED | 89ec CJK Ideograph-89EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89ef CJK Ideograph-89EF | 89ee CJK Ideograph-89EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89f1 CJK Ideograph-89F1 | 89f0 CJK Ideograph-89F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89f3 CJK Ideograph-89F3 | 89f2 CJK Ideograph-89F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89f5 CJK Ideograph-89F5 | 89f4 CJK Ideograph-89F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89f7 CJK Ideograph-89F7 | 89f6 CJK Ideograph-89F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89f9 CJK Ideograph-89F9 | 89f8 CJK Ideograph-89F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89fb CJK Ideograph-89FB | 89fa CJK Ideograph-89FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89fd CJK Ideograph-89FD | 89fc CJK Ideograph-89FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89ff CJK Ideograph-89FF | 89fe CJK Ideograph-89FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a01 CJK Ideograph-8A01 | 8a00 CJK Ideograph-8A00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a03 CJK Ideograph-8A03 | 8a02 CJK Ideograph-8A02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a05 CJK Ideograph-8A05 | 8a04 CJK Ideograph-8A04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a07 CJK Ideograph-8A07 | 8a06 CJK Ideograph-8A06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a09 CJK Ideograph-8A09 | 8a08 CJK Ideograph-8A08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a0b CJK Ideograph-8A0B | 8a0a CJK Ideograph-8A0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a0d CJK Ideograph-8A0D | 8a0c CJK Ideograph-8A0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a0f CJK Ideograph-8A0F | 8a0e CJK Ideograph-8A0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a11 CJK Ideograph-8A11 | 8a10 CJK Ideograph-8A10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a13 CJK Ideograph-8A13 | 8a12 CJK Ideograph-8A12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a15 CJK Ideograph-8A15 | 8a14 CJK Ideograph-8A14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a17 CJK Ideograph-8A17 | 8a16 CJK Ideograph-8A16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a19 CJK Ideograph-8A19 | 8a18 CJK Ideograph-8A18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a1b CJK Ideograph-8A1B | 8a1a CJK Ideograph-8A1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a1d CJK Ideograph-8A1D | 8a1c CJK Ideograph-8A1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a1f CJK Ideograph-8A1F | 8a1e CJK Ideograph-8A1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a21 CJK Ideograph-8A21 | 8a20 CJK Ideograph-8A20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a23 CJK Ideograph-8A23 | 8a22 CJK Ideograph-8A22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a25 CJK Ideograph-8A25 | 8a24 CJK Ideograph-8A24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a27 CJK Ideograph-8A27 | 8a26 CJK Ideograph-8A26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a29 CJK Ideograph-8A29 | 8a28 CJK Ideograph-8A28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a2b CJK Ideograph-8A2B | 8a2a CJK Ideograph-8A2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a2d CJK Ideograph-8A2D | 8a2c CJK Ideograph-8A2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a2f CJK Ideograph-8A2F | 8a2e CJK Ideograph-8A2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a31 CJK Ideograph-8A31 | 8a30 CJK Ideograph-8A30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a33 CJK Ideograph-8A33 | 8a32 CJK Ideograph-8A32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a35 CJK Ideograph-8A35 | 8a34 CJK Ideograph-8A34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a37 CJK Ideograph-8A37 | 8a36 CJK Ideograph-8A36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a39 CJK Ideograph-8A39 | 8a38 CJK Ideograph-8A38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a3b CJK Ideograph-8A3B | 8a3a CJK Ideograph-8A3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a3d CJK Ideograph-8A3D | 8a3c CJK Ideograph-8A3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a3f CJK Ideograph-8A3F | 8a3e CJK Ideograph-8A3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a41 CJK Ideograph-8A41 | 8a40 CJK Ideograph-8A40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a43 CJK Ideograph-8A43 | 8a42 CJK Ideograph-8A42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a45 CJK Ideograph-8A45 | 8a44 CJK Ideograph-8A44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a47 CJK Ideograph-8A47 | 8a46 CJK Ideograph-8A46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a49 CJK Ideograph-8A49 | 8a48 CJK Ideograph-8A48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a4b CJK Ideograph-8A4B | 8a4a CJK Ideograph-8A4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a4d CJK Ideograph-8A4D | 8a4c CJK Ideograph-8A4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a4f CJK Ideograph-8A4F | 8a4e CJK Ideograph-8A4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a51 CJK Ideograph-8A51 | 8a50 CJK Ideograph-8A50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a53 CJK Ideograph-8A53 | 8a52 CJK Ideograph-8A52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a55 CJK Ideograph-8A55 | 8a54 CJK Ideograph-8A54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a57 CJK Ideograph-8A57 | 8a56 CJK Ideograph-8A56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a59 CJK Ideograph-8A59 | 8a58 CJK Ideograph-8A58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a5b CJK Ideograph-8A5B | 8a5a CJK Ideograph-8A5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a5d CJK Ideograph-8A5D | 8a5c CJK Ideograph-8A5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a5f CJK Ideograph-8A5F | 8a5e CJK Ideograph-8A5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a61 CJK Ideograph-8A61 | 8a60 CJK Ideograph-8A60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a63 CJK Ideograph-8A63 | 8a62 CJK Ideograph-8A62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a65 CJK Ideograph-8A65 | 8a64 CJK Ideograph-8A64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a67 CJK Ideograph-8A67 | 8a66 CJK Ideograph-8A66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a69 CJK Ideograph-8A69 | 8a68 CJK Ideograph-8A68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a6b CJK Ideograph-8A6B | 8a6a CJK Ideograph-8A6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a6d CJK Ideograph-8A6D | 8a6c CJK Ideograph-8A6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a6f CJK Ideograph-8A6F | 8a6e CJK Ideograph-8A6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a71 CJK Ideograph-8A71 | 8a70 CJK Ideograph-8A70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a73 CJK Ideograph-8A73 | 8a72 CJK Ideograph-8A72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a75 CJK Ideograph-8A75 | 8a74 CJK Ideograph-8A74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a77 CJK Ideograph-8A77 | 8a76 CJK Ideograph-8A76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a79 CJK Ideograph-8A79 | 8a78 CJK Ideograph-8A78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a7b CJK Ideograph-8A7B | 8a7a CJK Ideograph-8A7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a7d CJK Ideograph-8A7D | 8a7c CJK Ideograph-8A7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a7f CJK Ideograph-8A7F | 8a7e CJK Ideograph-8A7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a81 CJK Ideograph-8A81 | 8a80 CJK Ideograph-8A80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a83 CJK Ideograph-8A83 | 8a82 CJK Ideograph-8A82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a85 CJK Ideograph-8A85 | 8a84 CJK Ideograph-8A84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a87 CJK Ideograph-8A87 | 8a86 CJK Ideograph-8A86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a89 CJK Ideograph-8A89 | 8a88 CJK Ideograph-8A88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a8b CJK Ideograph-8A8B | 8a8a CJK Ideograph-8A8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a8d CJK Ideograph-8A8D | 8a8c CJK Ideograph-8A8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a8f CJK Ideograph-8A8F | 8a8e CJK Ideograph-8A8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a91 CJK Ideograph-8A91 | 8a90 CJK Ideograph-8A90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a93 CJK Ideograph-8A93 | 8a92 CJK Ideograph-8A92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a95 CJK Ideograph-8A95 | 8a94 CJK Ideograph-8A94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a97 CJK Ideograph-8A97 | 8a96 CJK Ideograph-8A96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a99 CJK Ideograph-8A99 | 8a98 CJK Ideograph-8A98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a9b CJK Ideograph-8A9B | 8a9a CJK Ideograph-8A9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a9d CJK Ideograph-8A9D | 8a9c CJK Ideograph-8A9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a9f CJK Ideograph-8A9F | 8a9e CJK Ideograph-8A9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aa1 CJK Ideograph-8AA1 | 8aa0 CJK Ideograph-8AA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aa3 CJK Ideograph-8AA3 | 8aa2 CJK Ideograph-8AA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aa5 CJK Ideograph-8AA5 | 8aa4 CJK Ideograph-8AA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aa7 CJK Ideograph-8AA7 | 8aa6 CJK Ideograph-8AA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aa9 CJK Ideograph-8AA9 | 8aa8 CJK Ideograph-8AA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aab CJK Ideograph-8AAB | 8aaa CJK Ideograph-8AAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aad CJK Ideograph-8AAD | 8aac CJK Ideograph-8AAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aaf CJK Ideograph-8AAF | 8aae CJK Ideograph-8AAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ab1 CJK Ideograph-8AB1 | 8ab0 CJK Ideograph-8AB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ab3 CJK Ideograph-8AB3 | 8ab2 CJK Ideograph-8AB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ab5 CJK Ideograph-8AB5 | 8ab4 CJK Ideograph-8AB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ab7 CJK Ideograph-8AB7 | 8ab6 CJK Ideograph-8AB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ab9 CJK Ideograph-8AB9 | 8ab8 CJK Ideograph-8AB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8abb CJK Ideograph-8ABB | 8aba CJK Ideograph-8ABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8abd CJK Ideograph-8ABD | 8abc CJK Ideograph-8ABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8abf CJK Ideograph-8ABF | 8abe CJK Ideograph-8ABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ac1 CJK Ideograph-8AC1 | 8ac0 CJK Ideograph-8AC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ac3 CJK Ideograph-8AC3 | 8ac2 CJK Ideograph-8AC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ac5 CJK Ideograph-8AC5 | 8ac4 CJK Ideograph-8AC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ac7 CJK Ideograph-8AC7 | 8ac6 CJK Ideograph-8AC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ac9 CJK Ideograph-8AC9 | 8ac8 CJK Ideograph-8AC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8acb CJK Ideograph-8ACB | 8aca CJK Ideograph-8ACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8acd CJK Ideograph-8ACD | 8acc CJK Ideograph-8ACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8acf CJK Ideograph-8ACF | 8ace CJK Ideograph-8ACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ad1 CJK Ideograph-8AD1 | 8ad0 CJK Ideograph-8AD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ad3 CJK Ideograph-8AD3 | 8ad2 CJK Ideograph-8AD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ad5 CJK Ideograph-8AD5 | 8ad4 CJK Ideograph-8AD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ad7 CJK Ideograph-8AD7 | 8ad6 CJK Ideograph-8AD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ad9 CJK Ideograph-8AD9 | 8ad8 CJK Ideograph-8AD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8adb CJK Ideograph-8ADB | 8ada CJK Ideograph-8ADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8add CJK Ideograph-8ADD | 8adc CJK Ideograph-8ADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8adf CJK Ideograph-8ADF | 8ade CJK Ideograph-8ADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ae1 CJK Ideograph-8AE1 | 8ae0 CJK Ideograph-8AE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ae3 CJK Ideograph-8AE3 | 8ae2 CJK Ideograph-8AE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ae5 CJK Ideograph-8AE5 | 8ae4 CJK Ideograph-8AE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ae7 CJK Ideograph-8AE7 | 8ae6 CJK Ideograph-8AE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ae9 CJK Ideograph-8AE9 | 8ae8 CJK Ideograph-8AE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aeb CJK Ideograph-8AEB | 8aea CJK Ideograph-8AEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aed CJK Ideograph-8AED | 8aec CJK Ideograph-8AEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aef CJK Ideograph-8AEF | 8aee CJK Ideograph-8AEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8af1 CJK Ideograph-8AF1 | 8af0 CJK Ideograph-8AF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8af3 CJK Ideograph-8AF3 | 8af2 CJK Ideograph-8AF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8af5 CJK Ideograph-8AF5 | 8af4 CJK Ideograph-8AF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8af7 CJK Ideograph-8AF7 | 8af6 CJK Ideograph-8AF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8af9 CJK Ideograph-8AF9 | 8af8 CJK Ideograph-8AF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8afb CJK Ideograph-8AFB | 8afa CJK Ideograph-8AFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8afd CJK Ideograph-8AFD | 8afc CJK Ideograph-8AFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aff CJK Ideograph-8AFF | 8afe CJK Ideograph-8AFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b01 CJK Ideograph-8B01 | 8b00 CJK Ideograph-8B00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b03 CJK Ideograph-8B03 | 8b02 CJK Ideograph-8B02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b05 CJK Ideograph-8B05 | 8b04 CJK Ideograph-8B04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b07 CJK Ideograph-8B07 | 8b06 CJK Ideograph-8B06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b09 CJK Ideograph-8B09 | 8b08 CJK Ideograph-8B08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b0b CJK Ideograph-8B0B | 8b0a CJK Ideograph-8B0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b0d CJK Ideograph-8B0D | 8b0c CJK Ideograph-8B0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b0f CJK Ideograph-8B0F | 8b0e CJK Ideograph-8B0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b11 CJK Ideograph-8B11 | 8b10 CJK Ideograph-8B10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b13 CJK Ideograph-8B13 | 8b12 CJK Ideograph-8B12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b15 CJK Ideograph-8B15 | 8b14 CJK Ideograph-8B14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b17 CJK Ideograph-8B17 | 8b16 CJK Ideograph-8B16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b19 CJK Ideograph-8B19 | 8b18 CJK Ideograph-8B18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b1b CJK Ideograph-8B1B | 8b1a CJK Ideograph-8B1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b1d CJK Ideograph-8B1D | 8b1c CJK Ideograph-8B1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b1f CJK Ideograph-8B1F | 8b1e CJK Ideograph-8B1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b21 CJK Ideograph-8B21 | 8b20 CJK Ideograph-8B20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b23 CJK Ideograph-8B23 | 8b22 CJK Ideograph-8B22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b25 CJK Ideograph-8B25 | 8b24 CJK Ideograph-8B24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b27 CJK Ideograph-8B27 | 8b26 CJK Ideograph-8B26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b29 CJK Ideograph-8B29 | 8b28 CJK Ideograph-8B28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b2b CJK Ideograph-8B2B | 8b2a CJK Ideograph-8B2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b2d CJK Ideograph-8B2D | 8b2c CJK Ideograph-8B2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b2f CJK Ideograph-8B2F | 8b2e CJK Ideograph-8B2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b31 CJK Ideograph-8B31 | 8b30 CJK Ideograph-8B30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b33 CJK Ideograph-8B33 | 8b32 CJK Ideograph-8B32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b35 CJK Ideograph-8B35 | 8b34 CJK Ideograph-8B34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b37 CJK Ideograph-8B37 | 8b36 CJK Ideograph-8B36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b39 CJK Ideograph-8B39 | 8b38 CJK Ideograph-8B38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b3b CJK Ideograph-8B3B | 8b3a CJK Ideograph-8B3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b3d CJK Ideograph-8B3D | 8b3c CJK Ideograph-8B3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b3f CJK Ideograph-8B3F | 8b3e CJK Ideograph-8B3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b41 CJK Ideograph-8B41 | 8b40 CJK Ideograph-8B40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b43 CJK Ideograph-8B43 | 8b42 CJK Ideograph-8B42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b45 CJK Ideograph-8B45 | 8b44 CJK Ideograph-8B44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b47 CJK Ideograph-8B47 | 8b46 CJK Ideograph-8B46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b49 CJK Ideograph-8B49 | 8b48 CJK Ideograph-8B48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b4b CJK Ideograph-8B4B | 8b4a CJK Ideograph-8B4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b4d CJK Ideograph-8B4D | 8b4c CJK Ideograph-8B4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b4f CJK Ideograph-8B4F | 8b4e CJK Ideograph-8B4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b51 CJK Ideograph-8B51 | 8b50 CJK Ideograph-8B50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b53 CJK Ideograph-8B53 | 8b52 CJK Ideograph-8B52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b55 CJK Ideograph-8B55 | 8b54 CJK Ideograph-8B54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b57 CJK Ideograph-8B57 | 8b56 CJK Ideograph-8B56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b59 CJK Ideograph-8B59 | 8b58 CJK Ideograph-8B58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b5b CJK Ideograph-8B5B | 8b5a CJK Ideograph-8B5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b5d CJK Ideograph-8B5D | 8b5c CJK Ideograph-8B5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b5f CJK Ideograph-8B5F | 8b5e CJK Ideograph-8B5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b61 CJK Ideograph-8B61 | 8b60 CJK Ideograph-8B60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b63 CJK Ideograph-8B63 | 8b62 CJK Ideograph-8B62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b65 CJK Ideograph-8B65 | 8b64 CJK Ideograph-8B64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b67 CJK Ideograph-8B67 | 8b66 CJK Ideograph-8B66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b69 CJK Ideograph-8B69 | 8b68 CJK Ideograph-8B68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b6b CJK Ideograph-8B6B | 8b6a CJK Ideograph-8B6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b6d CJK Ideograph-8B6D | 8b6c CJK Ideograph-8B6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b6f CJK Ideograph-8B6F | 8b6e CJK Ideograph-8B6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b71 CJK Ideograph-8B71 | 8b70 CJK Ideograph-8B70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b73 CJK Ideograph-8B73 | 8b72 CJK Ideograph-8B72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b75 CJK Ideograph-8B75 | 8b74 CJK Ideograph-8B74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b77 CJK Ideograph-8B77 | 8b76 CJK Ideograph-8B76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b79 CJK Ideograph-8B79 | 8b78 CJK Ideograph-8B78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b7b CJK Ideograph-8B7B | 8b7a CJK Ideograph-8B7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b7d CJK Ideograph-8B7D | 8b7c CJK Ideograph-8B7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b7f CJK Ideograph-8B7F | 8b7e CJK Ideograph-8B7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b81 CJK Ideograph-8B81 | 8b80 CJK Ideograph-8B80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b83 CJK Ideograph-8B83 | 8b82 CJK Ideograph-8B82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b85 CJK Ideograph-8B85 | 8b84 CJK Ideograph-8B84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b87 CJK Ideograph-8B87 | 8b86 CJK Ideograph-8B86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b89 CJK Ideograph-8B89 | 8b88 CJK Ideograph-8B88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b8b CJK Ideograph-8B8B | 8b8a CJK Ideograph-8B8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b8d CJK Ideograph-8B8D | 8b8c CJK Ideograph-8B8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b8f CJK Ideograph-8B8F | 8b8e CJK Ideograph-8B8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b91 CJK Ideograph-8B91 | 8b90 CJK Ideograph-8B90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b93 CJK Ideograph-8B93 | 8b92 CJK Ideograph-8B92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b95 CJK Ideograph-8B95 | 8b94 CJK Ideograph-8B94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b97 CJK Ideograph-8B97 | 8b96 CJK Ideograph-8B96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b99 CJK Ideograph-8B99 | 8b98 CJK Ideograph-8B98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b9b CJK Ideograph-8B9B | 8b9a CJK Ideograph-8B9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b9d CJK Ideograph-8B9D | 8b9c CJK Ideograph-8B9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b9f CJK Ideograph-8B9F | 8b9e CJK Ideograph-8B9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ba1 CJK Ideograph-8BA1 | 8ba0 CJK Ideograph-8BA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ba3 CJK Ideograph-8BA3 | 8ba2 CJK Ideograph-8BA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ba5 CJK Ideograph-8BA5 | 8ba4 CJK Ideograph-8BA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ba7 CJK Ideograph-8BA7 | 8ba6 CJK Ideograph-8BA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ba9 CJK Ideograph-8BA9 | 8ba8 CJK Ideograph-8BA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bab CJK Ideograph-8BAB | 8baa CJK Ideograph-8BAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bad CJK Ideograph-8BAD | 8bac CJK Ideograph-8BAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8baf CJK Ideograph-8BAF | 8bae CJK Ideograph-8BAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bb1 CJK Ideograph-8BB1 | 8bb0 CJK Ideograph-8BB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bb3 CJK Ideograph-8BB3 | 8bb2 CJK Ideograph-8BB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bb5 CJK Ideograph-8BB5 | 8bb4 CJK Ideograph-8BB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bb7 CJK Ideograph-8BB7 | 8bb6 CJK Ideograph-8BB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bb9 CJK Ideograph-8BB9 | 8bb8 CJK Ideograph-8BB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bbb CJK Ideograph-8BBB | 8bba CJK Ideograph-8BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bbd CJK Ideograph-8BBD | 8bbc CJK Ideograph-8BBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bbf CJK Ideograph-8BBF | 8bbe CJK Ideograph-8BBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bc1 CJK Ideograph-8BC1 | 8bc0 CJK Ideograph-8BC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bc3 CJK Ideograph-8BC3 | 8bc2 CJK Ideograph-8BC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bc5 CJK Ideograph-8BC5 | 8bc4 CJK Ideograph-8BC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bc7 CJK Ideograph-8BC7 | 8bc6 CJK Ideograph-8BC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bc9 CJK Ideograph-8BC9 | 8bc8 CJK Ideograph-8BC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bcb CJK Ideograph-8BCB | 8bca CJK Ideograph-8BCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bcd CJK Ideograph-8BCD | 8bcc CJK Ideograph-8BCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bcf CJK Ideograph-8BCF | 8bce CJK Ideograph-8BCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bd1 CJK Ideograph-8BD1 | 8bd0 CJK Ideograph-8BD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bd3 CJK Ideograph-8BD3 | 8bd2 CJK Ideograph-8BD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bd5 CJK Ideograph-8BD5 | 8bd4 CJK Ideograph-8BD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bd7 CJK Ideograph-8BD7 | 8bd6 CJK Ideograph-8BD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bd9 CJK Ideograph-8BD9 | 8bd8 CJK Ideograph-8BD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bdb CJK Ideograph-8BDB | 8bda CJK Ideograph-8BDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bdd CJK Ideograph-8BDD | 8bdc CJK Ideograph-8BDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bdf CJK Ideograph-8BDF | 8bde CJK Ideograph-8BDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8be1 CJK Ideograph-8BE1 | 8be0 CJK Ideograph-8BE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8be3 CJK Ideograph-8BE3 | 8be2 CJK Ideograph-8BE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8be5 CJK Ideograph-8BE5 | 8be4 CJK Ideograph-8BE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8be7 CJK Ideograph-8BE7 | 8be6 CJK Ideograph-8BE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8be9 CJK Ideograph-8BE9 | 8be8 CJK Ideograph-8BE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8beb CJK Ideograph-8BEB | 8bea CJK Ideograph-8BEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bed CJK Ideograph-8BED | 8bec CJK Ideograph-8BEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bef CJK Ideograph-8BEF | 8bee CJK Ideograph-8BEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bf1 CJK Ideograph-8BF1 | 8bf0 CJK Ideograph-8BF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bf3 CJK Ideograph-8BF3 | 8bf2 CJK Ideograph-8BF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bf5 CJK Ideograph-8BF5 | 8bf4 CJK Ideograph-8BF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bf7 CJK Ideograph-8BF7 | 8bf6 CJK Ideograph-8BF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bf9 CJK Ideograph-8BF9 | 8bf8 CJK Ideograph-8BF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bfb CJK Ideograph-8BFB | 8bfa CJK Ideograph-8BFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bfd CJK Ideograph-8BFD | 8bfc CJK Ideograph-8BFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bff CJK Ideograph-8BFF | 8bfe CJK Ideograph-8BFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c01 CJK Ideograph-8C01 | 8c00 CJK Ideograph-8C00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c03 CJK Ideograph-8C03 | 8c02 CJK Ideograph-8C02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c05 CJK Ideograph-8C05 | 8c04 CJK Ideograph-8C04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c07 CJK Ideograph-8C07 | 8c06 CJK Ideograph-8C06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c09 CJK Ideograph-8C09 | 8c08 CJK Ideograph-8C08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c0b CJK Ideograph-8C0B | 8c0a CJK Ideograph-8C0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c0d CJK Ideograph-8C0D | 8c0c CJK Ideograph-8C0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c0f CJK Ideograph-8C0F | 8c0e CJK Ideograph-8C0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c11 CJK Ideograph-8C11 | 8c10 CJK Ideograph-8C10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c13 CJK Ideograph-8C13 | 8c12 CJK Ideograph-8C12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c15 CJK Ideograph-8C15 | 8c14 CJK Ideograph-8C14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c17 CJK Ideograph-8C17 | 8c16 CJK Ideograph-8C16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c19 CJK Ideograph-8C19 | 8c18 CJK Ideograph-8C18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c1b CJK Ideograph-8C1B | 8c1a CJK Ideograph-8C1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c1d CJK Ideograph-8C1D | 8c1c CJK Ideograph-8C1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c1f CJK Ideograph-8C1F | 8c1e CJK Ideograph-8C1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c21 CJK Ideograph-8C21 | 8c20 CJK Ideograph-8C20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c23 CJK Ideograph-8C23 | 8c22 CJK Ideograph-8C22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c25 CJK Ideograph-8C25 | 8c24 CJK Ideograph-8C24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c27 CJK Ideograph-8C27 | 8c26 CJK Ideograph-8C26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c29 CJK Ideograph-8C29 | 8c28 CJK Ideograph-8C28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c2b CJK Ideograph-8C2B | 8c2a CJK Ideograph-8C2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c2d CJK Ideograph-8C2D | 8c2c CJK Ideograph-8C2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c2f CJK Ideograph-8C2F | 8c2e CJK Ideograph-8C2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c31 CJK Ideograph-8C31 | 8c30 CJK Ideograph-8C30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c33 CJK Ideograph-8C33 | 8c32 CJK Ideograph-8C32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c35 CJK Ideograph-8C35 | 8c34 CJK Ideograph-8C34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c37 CJK Ideograph-8C37 | 8c36 CJK Ideograph-8C36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c39 CJK Ideograph-8C39 | 8c38 CJK Ideograph-8C38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c3b CJK Ideograph-8C3B | 8c3a CJK Ideograph-8C3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c3d CJK Ideograph-8C3D | 8c3c CJK Ideograph-8C3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c3f CJK Ideograph-8C3F | 8c3e CJK Ideograph-8C3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c41 CJK Ideograph-8C41 | 8c40 CJK Ideograph-8C40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c43 CJK Ideograph-8C43 | 8c42 CJK Ideograph-8C42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c45 CJK Ideograph-8C45 | 8c44 CJK Ideograph-8C44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c47 CJK Ideograph-8C47 | 8c46 CJK Ideograph-8C46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c49 CJK Ideograph-8C49 | 8c48 CJK Ideograph-8C48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c4b CJK Ideograph-8C4B | 8c4a CJK Ideograph-8C4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c4d CJK Ideograph-8C4D | 8c4c CJK Ideograph-8C4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c4f CJK Ideograph-8C4F | 8c4e CJK Ideograph-8C4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c51 CJK Ideograph-8C51 | 8c50 CJK Ideograph-8C50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c53 CJK Ideograph-8C53 | 8c52 CJK Ideograph-8C52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c55 CJK Ideograph-8C55 | 8c54 CJK Ideograph-8C54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c57 CJK Ideograph-8C57 | 8c56 CJK Ideograph-8C56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c59 CJK Ideograph-8C59 | 8c58 CJK Ideograph-8C58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c5b CJK Ideograph-8C5B | 8c5a CJK Ideograph-8C5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c5d CJK Ideograph-8C5D | 8c5c CJK Ideograph-8C5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c5f CJK Ideograph-8C5F | 8c5e CJK Ideograph-8C5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c61 CJK Ideograph-8C61 | 8c60 CJK Ideograph-8C60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c63 CJK Ideograph-8C63 | 8c62 CJK Ideograph-8C62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c65 CJK Ideograph-8C65 | 8c64 CJK Ideograph-8C64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c67 CJK Ideograph-8C67 | 8c66 CJK Ideograph-8C66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c69 CJK Ideograph-8C69 | 8c68 CJK Ideograph-8C68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c6b CJK Ideograph-8C6B | 8c6a CJK Ideograph-8C6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c6d CJK Ideograph-8C6D | 8c6c CJK Ideograph-8C6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c6f CJK Ideograph-8C6F | 8c6e CJK Ideograph-8C6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c71 CJK Ideograph-8C71 | 8c70 CJK Ideograph-8C70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c73 CJK Ideograph-8C73 | 8c72 CJK Ideograph-8C72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c75 CJK Ideograph-8C75 | 8c74 CJK Ideograph-8C74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c77 CJK Ideograph-8C77 | 8c76 CJK Ideograph-8C76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c79 CJK Ideograph-8C79 | 8c78 CJK Ideograph-8C78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c7b CJK Ideograph-8C7B | 8c7a CJK Ideograph-8C7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c7d CJK Ideograph-8C7D | 8c7c CJK Ideograph-8C7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c7f CJK Ideograph-8C7F | 8c7e CJK Ideograph-8C7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c81 CJK Ideograph-8C81 | 8c80 CJK Ideograph-8C80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c83 CJK Ideograph-8C83 | 8c82 CJK Ideograph-8C82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c85 CJK Ideograph-8C85 | 8c84 CJK Ideograph-8C84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c87 CJK Ideograph-8C87 | 8c86 CJK Ideograph-8C86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c89 CJK Ideograph-8C89 | 8c88 CJK Ideograph-8C88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c8b CJK Ideograph-8C8B | 8c8a CJK Ideograph-8C8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c8d CJK Ideograph-8C8D | 8c8c CJK Ideograph-8C8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c8f CJK Ideograph-8C8F | 8c8e CJK Ideograph-8C8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c91 CJK Ideograph-8C91 | 8c90 CJK Ideograph-8C90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c93 CJK Ideograph-8C93 | 8c92 CJK Ideograph-8C92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c95 CJK Ideograph-8C95 | 8c94 CJK Ideograph-8C94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c97 CJK Ideograph-8C97 | 8c96 CJK Ideograph-8C96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c99 CJK Ideograph-8C99 | 8c98 CJK Ideograph-8C98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c9b CJK Ideograph-8C9B | 8c9a CJK Ideograph-8C9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c9d CJK Ideograph-8C9D | 8c9c CJK Ideograph-8C9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c9f CJK Ideograph-8C9F | 8c9e CJK Ideograph-8C9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ca1 CJK Ideograph-8CA1 | 8ca0 CJK Ideograph-8CA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ca3 CJK Ideograph-8CA3 | 8ca2 CJK Ideograph-8CA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ca5 CJK Ideograph-8CA5 | 8ca4 CJK Ideograph-8CA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ca7 CJK Ideograph-8CA7 | 8ca6 CJK Ideograph-8CA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ca9 CJK Ideograph-8CA9 | 8ca8 CJK Ideograph-8CA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cab CJK Ideograph-8CAB | 8caa CJK Ideograph-8CAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cad CJK Ideograph-8CAD | 8cac CJK Ideograph-8CAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8caf CJK Ideograph-8CAF | 8cae CJK Ideograph-8CAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cb1 CJK Ideograph-8CB1 | 8cb0 CJK Ideograph-8CB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cb3 CJK Ideograph-8CB3 | 8cb2 CJK Ideograph-8CB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cb5 CJK Ideograph-8CB5 | 8cb4 CJK Ideograph-8CB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cb7 CJK Ideograph-8CB7 | 8cb6 CJK Ideograph-8CB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cb9 CJK Ideograph-8CB9 | 8cb8 CJK Ideograph-8CB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cbb CJK Ideograph-8CBB | 8cba CJK Ideograph-8CBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cbd CJK Ideograph-8CBD | 8cbc CJK Ideograph-8CBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cbf CJK Ideograph-8CBF | 8cbe CJK Ideograph-8CBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cc1 CJK Ideograph-8CC1 | 8cc0 CJK Ideograph-8CC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cc3 CJK Ideograph-8CC3 | 8cc2 CJK Ideograph-8CC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cc5 CJK Ideograph-8CC5 | 8cc4 CJK Ideograph-8CC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cc7 CJK Ideograph-8CC7 | 8cc6 CJK Ideograph-8CC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cc9 CJK Ideograph-8CC9 | 8cc8 CJK Ideograph-8CC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ccb CJK Ideograph-8CCB | 8cca CJK Ideograph-8CCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ccd CJK Ideograph-8CCD | 8ccc CJK Ideograph-8CCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ccf CJK Ideograph-8CCF | 8cce CJK Ideograph-8CCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cd1 CJK Ideograph-8CD1 | 8cd0 CJK Ideograph-8CD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cd3 CJK Ideograph-8CD3 | 8cd2 CJK Ideograph-8CD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cd5 CJK Ideograph-8CD5 | 8cd4 CJK Ideograph-8CD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cd7 CJK Ideograph-8CD7 | 8cd6 CJK Ideograph-8CD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cd9 CJK Ideograph-8CD9 | 8cd8 CJK Ideograph-8CD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cdb CJK Ideograph-8CDB | 8cda CJK Ideograph-8CDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cdd CJK Ideograph-8CDD | 8cdc CJK Ideograph-8CDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cdf CJK Ideograph-8CDF | 8cde CJK Ideograph-8CDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ce1 CJK Ideograph-8CE1 | 8ce0 CJK Ideograph-8CE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ce3 CJK Ideograph-8CE3 | 8ce2 CJK Ideograph-8CE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ce5 CJK Ideograph-8CE5 | 8ce4 CJK Ideograph-8CE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ce7 CJK Ideograph-8CE7 | 8ce6 CJK Ideograph-8CE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ce9 CJK Ideograph-8CE9 | 8ce8 CJK Ideograph-8CE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ceb CJK Ideograph-8CEB | 8cea CJK Ideograph-8CEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ced CJK Ideograph-8CED | 8cec CJK Ideograph-8CEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cef CJK Ideograph-8CEF | 8cee CJK Ideograph-8CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cf1 CJK Ideograph-8CF1 | 8cf0 CJK Ideograph-8CF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cf3 CJK Ideograph-8CF3 | 8cf2 CJK Ideograph-8CF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cf5 CJK Ideograph-8CF5 | 8cf4 CJK Ideograph-8CF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cf7 CJK Ideograph-8CF7 | 8cf6 CJK Ideograph-8CF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cf9 CJK Ideograph-8CF9 | 8cf8 CJK Ideograph-8CF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cfb CJK Ideograph-8CFB | 8cfa CJK Ideograph-8CFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cfd CJK Ideograph-8CFD | 8cfc CJK Ideograph-8CFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cff CJK Ideograph-8CFF | 8cfe CJK Ideograph-8CFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d01 CJK Ideograph-8D01 | 8d00 CJK Ideograph-8D00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d03 CJK Ideograph-8D03 | 8d02 CJK Ideograph-8D02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d05 CJK Ideograph-8D05 | 8d04 CJK Ideograph-8D04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d07 CJK Ideograph-8D07 | 8d06 CJK Ideograph-8D06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d09 CJK Ideograph-8D09 | 8d08 CJK Ideograph-8D08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d0b CJK Ideograph-8D0B | 8d0a CJK Ideograph-8D0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d0d CJK Ideograph-8D0D | 8d0c CJK Ideograph-8D0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d0f CJK Ideograph-8D0F | 8d0e CJK Ideograph-8D0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d11 CJK Ideograph-8D11 | 8d10 CJK Ideograph-8D10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d13 CJK Ideograph-8D13 | 8d12 CJK Ideograph-8D12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d15 CJK Ideograph-8D15 | 8d14 CJK Ideograph-8D14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d17 CJK Ideograph-8D17 | 8d16 CJK Ideograph-8D16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d19 CJK Ideograph-8D19 | 8d18 CJK Ideograph-8D18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d1b CJK Ideograph-8D1B | 8d1a CJK Ideograph-8D1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d1d CJK Ideograph-8D1D | 8d1c CJK Ideograph-8D1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d1f CJK Ideograph-8D1F | 8d1e CJK Ideograph-8D1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d21 CJK Ideograph-8D21 | 8d20 CJK Ideograph-8D20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d23 CJK Ideograph-8D23 | 8d22 CJK Ideograph-8D22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d25 CJK Ideograph-8D25 | 8d24 CJK Ideograph-8D24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d27 CJK Ideograph-8D27 | 8d26 CJK Ideograph-8D26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d29 CJK Ideograph-8D29 | 8d28 CJK Ideograph-8D28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d2b CJK Ideograph-8D2B | 8d2a CJK Ideograph-8D2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d2d CJK Ideograph-8D2D | 8d2c CJK Ideograph-8D2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d2f CJK Ideograph-8D2F | 8d2e CJK Ideograph-8D2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d31 CJK Ideograph-8D31 | 8d30 CJK Ideograph-8D30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d33 CJK Ideograph-8D33 | 8d32 CJK Ideograph-8D32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d35 CJK Ideograph-8D35 | 8d34 CJK Ideograph-8D34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d37 CJK Ideograph-8D37 | 8d36 CJK Ideograph-8D36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d39 CJK Ideograph-8D39 | 8d38 CJK Ideograph-8D38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d3b CJK Ideograph-8D3B | 8d3a CJK Ideograph-8D3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d3d CJK Ideograph-8D3D | 8d3c CJK Ideograph-8D3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d3f CJK Ideograph-8D3F | 8d3e CJK Ideograph-8D3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d41 CJK Ideograph-8D41 | 8d40 CJK Ideograph-8D40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d43 CJK Ideograph-8D43 | 8d42 CJK Ideograph-8D42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d45 CJK Ideograph-8D45 | 8d44 CJK Ideograph-8D44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d47 CJK Ideograph-8D47 | 8d46 CJK Ideograph-8D46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d49 CJK Ideograph-8D49 | 8d48 CJK Ideograph-8D48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d4b CJK Ideograph-8D4B | 8d4a CJK Ideograph-8D4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d4d CJK Ideograph-8D4D | 8d4c CJK Ideograph-8D4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d4f CJK Ideograph-8D4F | 8d4e CJK Ideograph-8D4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d51 CJK Ideograph-8D51 | 8d50 CJK Ideograph-8D50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d53 CJK Ideograph-8D53 | 8d52 CJK Ideograph-8D52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d55 CJK Ideograph-8D55 | 8d54 CJK Ideograph-8D54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d57 CJK Ideograph-8D57 | 8d56 CJK Ideograph-8D56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d59 CJK Ideograph-8D59 | 8d58 CJK Ideograph-8D58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d5b CJK Ideograph-8D5B | 8d5a CJK Ideograph-8D5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d5d CJK Ideograph-8D5D | 8d5c CJK Ideograph-8D5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d5f CJK Ideograph-8D5F | 8d5e CJK Ideograph-8D5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d61 CJK Ideograph-8D61 | 8d60 CJK Ideograph-8D60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d63 CJK Ideograph-8D63 | 8d62 CJK Ideograph-8D62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d65 CJK Ideograph-8D65 | 8d64 CJK Ideograph-8D64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d67 CJK Ideograph-8D67 | 8d66 CJK Ideograph-8D66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d69 CJK Ideograph-8D69 | 8d68 CJK Ideograph-8D68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d6b CJK Ideograph-8D6B | 8d6a CJK Ideograph-8D6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d6d CJK Ideograph-8D6D | 8d6c CJK Ideograph-8D6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d6f CJK Ideograph-8D6F | 8d6e CJK Ideograph-8D6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d71 CJK Ideograph-8D71 | 8d70 CJK Ideograph-8D70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d73 CJK Ideograph-8D73 | 8d72 CJK Ideograph-8D72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d75 CJK Ideograph-8D75 | 8d74 CJK Ideograph-8D74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d77 CJK Ideograph-8D77 | 8d76 CJK Ideograph-8D76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d79 CJK Ideograph-8D79 | 8d78 CJK Ideograph-8D78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d7b CJK Ideograph-8D7B | 8d7a CJK Ideograph-8D7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d7d CJK Ideograph-8D7D | 8d7c CJK Ideograph-8D7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d7f CJK Ideograph-8D7F | 8d7e CJK Ideograph-8D7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d81 CJK Ideograph-8D81 | 8d80 CJK Ideograph-8D80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d83 CJK Ideograph-8D83 | 8d82 CJK Ideograph-8D82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d85 CJK Ideograph-8D85 | 8d84 CJK Ideograph-8D84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d87 CJK Ideograph-8D87 | 8d86 CJK Ideograph-8D86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d89 CJK Ideograph-8D89 | 8d88 CJK Ideograph-8D88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d8b CJK Ideograph-8D8B | 8d8a CJK Ideograph-8D8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d8d CJK Ideograph-8D8D | 8d8c CJK Ideograph-8D8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d8f CJK Ideograph-8D8F | 8d8e CJK Ideograph-8D8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d91 CJK Ideograph-8D91 | 8d90 CJK Ideograph-8D90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d93 CJK Ideograph-8D93 | 8d92 CJK Ideograph-8D92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d95 CJK Ideograph-8D95 | 8d94 CJK Ideograph-8D94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d97 CJK Ideograph-8D97 | 8d96 CJK Ideograph-8D96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d99 CJK Ideograph-8D99 | 8d98 CJK Ideograph-8D98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d9b CJK Ideograph-8D9B | 8d9a CJK Ideograph-8D9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d9d CJK Ideograph-8D9D | 8d9c CJK Ideograph-8D9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d9f CJK Ideograph-8D9F | 8d9e CJK Ideograph-8D9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8da1 CJK Ideograph-8DA1 | 8da0 CJK Ideograph-8DA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8da3 CJK Ideograph-8DA3 | 8da2 CJK Ideograph-8DA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8da5 CJK Ideograph-8DA5 | 8da4 CJK Ideograph-8DA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8da7 CJK Ideograph-8DA7 | 8da6 CJK Ideograph-8DA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8da9 CJK Ideograph-8DA9 | 8da8 CJK Ideograph-8DA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dab CJK Ideograph-8DAB | 8daa CJK Ideograph-8DAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dad CJK Ideograph-8DAD | 8dac CJK Ideograph-8DAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8daf CJK Ideograph-8DAF | 8dae CJK Ideograph-8DAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8db1 CJK Ideograph-8DB1 | 8db0 CJK Ideograph-8DB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8db3 CJK Ideograph-8DB3 | 8db2 CJK Ideograph-8DB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8db5 CJK Ideograph-8DB5 | 8db4 CJK Ideograph-8DB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8db7 CJK Ideograph-8DB7 | 8db6 CJK Ideograph-8DB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8db9 CJK Ideograph-8DB9 | 8db8 CJK Ideograph-8DB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dbb CJK Ideograph-8DBB | 8dba CJK Ideograph-8DBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dbd CJK Ideograph-8DBD | 8dbc CJK Ideograph-8DBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dbf CJK Ideograph-8DBF | 8dbe CJK Ideograph-8DBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dc1 CJK Ideograph-8DC1 | 8dc0 CJK Ideograph-8DC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dc3 CJK Ideograph-8DC3 | 8dc2 CJK Ideograph-8DC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dc5 CJK Ideograph-8DC5 | 8dc4 CJK Ideograph-8DC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dc7 CJK Ideograph-8DC7 | 8dc6 CJK Ideograph-8DC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dc9 CJK Ideograph-8DC9 | 8dc8 CJK Ideograph-8DC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dcb CJK Ideograph-8DCB | 8dca CJK Ideograph-8DCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dcd CJK Ideograph-8DCD | 8dcc CJK Ideograph-8DCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dcf CJK Ideograph-8DCF | 8dce CJK Ideograph-8DCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dd1 CJK Ideograph-8DD1 | 8dd0 CJK Ideograph-8DD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dd3 CJK Ideograph-8DD3 | 8dd2 CJK Ideograph-8DD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dd5 CJK Ideograph-8DD5 | 8dd4 CJK Ideograph-8DD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dd7 CJK Ideograph-8DD7 | 8dd6 CJK Ideograph-8DD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dd9 CJK Ideograph-8DD9 | 8dd8 CJK Ideograph-8DD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ddb CJK Ideograph-8DDB | 8dda CJK Ideograph-8DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ddd CJK Ideograph-8DDD | 8ddc CJK Ideograph-8DDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ddf CJK Ideograph-8DDF | 8dde CJK Ideograph-8DDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8de1 CJK Ideograph-8DE1 | 8de0 CJK Ideograph-8DE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8de3 CJK Ideograph-8DE3 | 8de2 CJK Ideograph-8DE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8de5 CJK Ideograph-8DE5 | 8de4 CJK Ideograph-8DE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8de7 CJK Ideograph-8DE7 | 8de6 CJK Ideograph-8DE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8de9 CJK Ideograph-8DE9 | 8de8 CJK Ideograph-8DE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8deb CJK Ideograph-8DEB | 8dea CJK Ideograph-8DEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ded CJK Ideograph-8DED | 8dec CJK Ideograph-8DEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8def CJK Ideograph-8DEF | 8dee CJK Ideograph-8DEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8df1 CJK Ideograph-8DF1 | 8df0 CJK Ideograph-8DF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8df3 CJK Ideograph-8DF3 | 8df2 CJK Ideograph-8DF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8df5 CJK Ideograph-8DF5 | 8df4 CJK Ideograph-8DF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8df7 CJK Ideograph-8DF7 | 8df6 CJK Ideograph-8DF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8df9 CJK Ideograph-8DF9 | 8df8 CJK Ideograph-8DF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dfb CJK Ideograph-8DFB | 8dfa CJK Ideograph-8DFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dfd CJK Ideograph-8DFD | 8dfc CJK Ideograph-8DFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dff CJK Ideograph-8DFF | 8dfe CJK Ideograph-8DFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e01 CJK Ideograph-8E01 | 8e00 CJK Ideograph-8E00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e03 CJK Ideograph-8E03 | 8e02 CJK Ideograph-8E02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e05 CJK Ideograph-8E05 | 8e04 CJK Ideograph-8E04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e07 CJK Ideograph-8E07 | 8e06 CJK Ideograph-8E06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e09 CJK Ideograph-8E09 | 8e08 CJK Ideograph-8E08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e0b CJK Ideograph-8E0B | 8e0a CJK Ideograph-8E0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e0d CJK Ideograph-8E0D | 8e0c CJK Ideograph-8E0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e0f CJK Ideograph-8E0F | 8e0e CJK Ideograph-8E0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e11 CJK Ideograph-8E11 | 8e10 CJK Ideograph-8E10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e13 CJK Ideograph-8E13 | 8e12 CJK Ideograph-8E12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e15 CJK Ideograph-8E15 | 8e14 CJK Ideograph-8E14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e17 CJK Ideograph-8E17 | 8e16 CJK Ideograph-8E16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e19 CJK Ideograph-8E19 | 8e18 CJK Ideograph-8E18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e1b CJK Ideograph-8E1B | 8e1a CJK Ideograph-8E1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e1d CJK Ideograph-8E1D | 8e1c CJK Ideograph-8E1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e1f CJK Ideograph-8E1F | 8e1e CJK Ideograph-8E1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e21 CJK Ideograph-8E21 | 8e20 CJK Ideograph-8E20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e23 CJK Ideograph-8E23 | 8e22 CJK Ideograph-8E22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e25 CJK Ideograph-8E25 | 8e24 CJK Ideograph-8E24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e27 CJK Ideograph-8E27 | 8e26 CJK Ideograph-8E26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e29 CJK Ideograph-8E29 | 8e28 CJK Ideograph-8E28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e2b CJK Ideograph-8E2B | 8e2a CJK Ideograph-8E2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e2d CJK Ideograph-8E2D | 8e2c CJK Ideograph-8E2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e2f CJK Ideograph-8E2F | 8e2e CJK Ideograph-8E2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e31 CJK Ideograph-8E31 | 8e30 CJK Ideograph-8E30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e33 CJK Ideograph-8E33 | 8e32 CJK Ideograph-8E32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e35 CJK Ideograph-8E35 | 8e34 CJK Ideograph-8E34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e37 CJK Ideograph-8E37 | 8e36 CJK Ideograph-8E36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e39 CJK Ideograph-8E39 | 8e38 CJK Ideograph-8E38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e3b CJK Ideograph-8E3B | 8e3a CJK Ideograph-8E3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e3d CJK Ideograph-8E3D | 8e3c CJK Ideograph-8E3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e3f CJK Ideograph-8E3F | 8e3e CJK Ideograph-8E3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e41 CJK Ideograph-8E41 | 8e40 CJK Ideograph-8E40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e43 CJK Ideograph-8E43 | 8e42 CJK Ideograph-8E42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e45 CJK Ideograph-8E45 | 8e44 CJK Ideograph-8E44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e47 CJK Ideograph-8E47 | 8e46 CJK Ideograph-8E46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e49 CJK Ideograph-8E49 | 8e48 CJK Ideograph-8E48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e4b CJK Ideograph-8E4B | 8e4a CJK Ideograph-8E4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e4d CJK Ideograph-8E4D | 8e4c CJK Ideograph-8E4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e4f CJK Ideograph-8E4F | 8e4e CJK Ideograph-8E4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e51 CJK Ideograph-8E51 | 8e50 CJK Ideograph-8E50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e53 CJK Ideograph-8E53 | 8e52 CJK Ideograph-8E52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e55 CJK Ideograph-8E55 | 8e54 CJK Ideograph-8E54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e57 CJK Ideograph-8E57 | 8e56 CJK Ideograph-8E56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e59 CJK Ideograph-8E59 | 8e58 CJK Ideograph-8E58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e5b CJK Ideograph-8E5B | 8e5a CJK Ideograph-8E5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e5d CJK Ideograph-8E5D | 8e5c CJK Ideograph-8E5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e5f CJK Ideograph-8E5F | 8e5e CJK Ideograph-8E5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e61 CJK Ideograph-8E61 | 8e60 CJK Ideograph-8E60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e63 CJK Ideograph-8E63 | 8e62 CJK Ideograph-8E62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e65 CJK Ideograph-8E65 | 8e64 CJK Ideograph-8E64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e67 CJK Ideograph-8E67 | 8e66 CJK Ideograph-8E66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e69 CJK Ideograph-8E69 | 8e68 CJK Ideograph-8E68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e6b CJK Ideograph-8E6B | 8e6a CJK Ideograph-8E6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e6d CJK Ideograph-8E6D | 8e6c CJK Ideograph-8E6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e6f CJK Ideograph-8E6F | 8e6e CJK Ideograph-8E6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e71 CJK Ideograph-8E71 | 8e70 CJK Ideograph-8E70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e73 CJK Ideograph-8E73 | 8e72 CJK Ideograph-8E72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e75 CJK Ideograph-8E75 | 8e74 CJK Ideograph-8E74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e77 CJK Ideograph-8E77 | 8e76 CJK Ideograph-8E76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e79 CJK Ideograph-8E79 | 8e78 CJK Ideograph-8E78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e7b CJK Ideograph-8E7B | 8e7a CJK Ideograph-8E7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e7d CJK Ideograph-8E7D | 8e7c CJK Ideograph-8E7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e7f CJK Ideograph-8E7F | 8e7e CJK Ideograph-8E7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e81 CJK Ideograph-8E81 | 8e80 CJK Ideograph-8E80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e83 CJK Ideograph-8E83 | 8e82 CJK Ideograph-8E82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e85 CJK Ideograph-8E85 | 8e84 CJK Ideograph-8E84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e87 CJK Ideograph-8E87 | 8e86 CJK Ideograph-8E86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e89 CJK Ideograph-8E89 | 8e88 CJK Ideograph-8E88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e8b CJK Ideograph-8E8B | 8e8a CJK Ideograph-8E8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e8d CJK Ideograph-8E8D | 8e8c CJK Ideograph-8E8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e8f CJK Ideograph-8E8F | 8e8e CJK Ideograph-8E8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e91 CJK Ideograph-8E91 | 8e90 CJK Ideograph-8E90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e93 CJK Ideograph-8E93 | 8e92 CJK Ideograph-8E92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e95 CJK Ideograph-8E95 | 8e94 CJK Ideograph-8E94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e97 CJK Ideograph-8E97 | 8e96 CJK Ideograph-8E96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e99 CJK Ideograph-8E99 | 8e98 CJK Ideograph-8E98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e9b CJK Ideograph-8E9B | 8e9a CJK Ideograph-8E9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e9d CJK Ideograph-8E9D | 8e9c CJK Ideograph-8E9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e9f CJK Ideograph-8E9F | 8e9e CJK Ideograph-8E9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ea1 CJK Ideograph-8EA1 | 8ea0 CJK Ideograph-8EA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ea3 CJK Ideograph-8EA3 | 8ea2 CJK Ideograph-8EA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ea5 CJK Ideograph-8EA5 | 8ea4 CJK Ideograph-8EA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ea7 CJK Ideograph-8EA7 | 8ea6 CJK Ideograph-8EA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ea9 CJK Ideograph-8EA9 | 8ea8 CJK Ideograph-8EA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eab CJK Ideograph-8EAB | 8eaa CJK Ideograph-8EAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ead CJK Ideograph-8EAD | 8eac CJK Ideograph-8EAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eaf CJK Ideograph-8EAF | 8eae CJK Ideograph-8EAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eb1 CJK Ideograph-8EB1 | 8eb0 CJK Ideograph-8EB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eb3 CJK Ideograph-8EB3 | 8eb2 CJK Ideograph-8EB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eb5 CJK Ideograph-8EB5 | 8eb4 CJK Ideograph-8EB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eb7 CJK Ideograph-8EB7 | 8eb6 CJK Ideograph-8EB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eb9 CJK Ideograph-8EB9 | 8eb8 CJK Ideograph-8EB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ebb CJK Ideograph-8EBB | 8eba CJK Ideograph-8EBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ebd CJK Ideograph-8EBD | 8ebc CJK Ideograph-8EBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ebf CJK Ideograph-8EBF | 8ebe CJK Ideograph-8EBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ec1 CJK Ideograph-8EC1 | 8ec0 CJK Ideograph-8EC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ec3 CJK Ideograph-8EC3 | 8ec2 CJK Ideograph-8EC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ec5 CJK Ideograph-8EC5 | 8ec4 CJK Ideograph-8EC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ec7 CJK Ideograph-8EC7 | 8ec6 CJK Ideograph-8EC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ec9 CJK Ideograph-8EC9 | 8ec8 CJK Ideograph-8EC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ecb CJK Ideograph-8ECB | 8eca CJK Ideograph-8ECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ecd CJK Ideograph-8ECD | 8ecc CJK Ideograph-8ECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ecf CJK Ideograph-8ECF | 8ece CJK Ideograph-8ECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ed1 CJK Ideograph-8ED1 | 8ed0 CJK Ideograph-8ED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ed3 CJK Ideograph-8ED3 | 8ed2 CJK Ideograph-8ED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ed5 CJK Ideograph-8ED5 | 8ed4 CJK Ideograph-8ED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ed7 CJK Ideograph-8ED7 | 8ed6 CJK Ideograph-8ED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ed9 CJK Ideograph-8ED9 | 8ed8 CJK Ideograph-8ED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8edb CJK Ideograph-8EDB | 8eda CJK Ideograph-8EDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8edd CJK Ideograph-8EDD | 8edc CJK Ideograph-8EDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8edf CJK Ideograph-8EDF | 8ede CJK Ideograph-8EDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ee1 CJK Ideograph-8EE1 | 8ee0 CJK Ideograph-8EE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ee3 CJK Ideograph-8EE3 | 8ee2 CJK Ideograph-8EE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ee5 CJK Ideograph-8EE5 | 8ee4 CJK Ideograph-8EE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ee7 CJK Ideograph-8EE7 | 8ee6 CJK Ideograph-8EE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ee9 CJK Ideograph-8EE9 | 8ee8 CJK Ideograph-8EE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eeb CJK Ideograph-8EEB | 8eea CJK Ideograph-8EEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eed CJK Ideograph-8EED | 8eec CJK Ideograph-8EEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eef CJK Ideograph-8EEF | 8eee CJK Ideograph-8EEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ef1 CJK Ideograph-8EF1 | 8ef0 CJK Ideograph-8EF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ef3 CJK Ideograph-8EF3 | 8ef2 CJK Ideograph-8EF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ef5 CJK Ideograph-8EF5 | 8ef4 CJK Ideograph-8EF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ef7 CJK Ideograph-8EF7 | 8ef6 CJK Ideograph-8EF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ef9 CJK Ideograph-8EF9 | 8ef8 CJK Ideograph-8EF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8efb CJK Ideograph-8EFB | 8efa CJK Ideograph-8EFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8efd CJK Ideograph-8EFD | 8efc CJK Ideograph-8EFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eff CJK Ideograph-8EFF | 8efe CJK Ideograph-8EFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f01 CJK Ideograph-8F01 | 8f00 CJK Ideograph-8F00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f03 CJK Ideograph-8F03 | 8f02 CJK Ideograph-8F02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f05 CJK Ideograph-8F05 | 8f04 CJK Ideograph-8F04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f07 CJK Ideograph-8F07 | 8f06 CJK Ideograph-8F06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f09 CJK Ideograph-8F09 | 8f08 CJK Ideograph-8F08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f0b CJK Ideograph-8F0B | 8f0a CJK Ideograph-8F0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f0d CJK Ideograph-8F0D | 8f0c CJK Ideograph-8F0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f0f CJK Ideograph-8F0F | 8f0e CJK Ideograph-8F0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f11 CJK Ideograph-8F11 | 8f10 CJK Ideograph-8F10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f13 CJK Ideograph-8F13 | 8f12 CJK Ideograph-8F12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f15 CJK Ideograph-8F15 | 8f14 CJK Ideograph-8F14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f17 CJK Ideograph-8F17 | 8f16 CJK Ideograph-8F16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f19 CJK Ideograph-8F19 | 8f18 CJK Ideograph-8F18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f1b CJK Ideograph-8F1B | 8f1a CJK Ideograph-8F1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f1d CJK Ideograph-8F1D | 8f1c CJK Ideograph-8F1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f1f CJK Ideograph-8F1F | 8f1e CJK Ideograph-8F1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f21 CJK Ideograph-8F21 | 8f20 CJK Ideograph-8F20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f23 CJK Ideograph-8F23 | 8f22 CJK Ideograph-8F22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f25 CJK Ideograph-8F25 | 8f24 CJK Ideograph-8F24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f27 CJK Ideograph-8F27 | 8f26 CJK Ideograph-8F26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f29 CJK Ideograph-8F29 | 8f28 CJK Ideograph-8F28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f2b CJK Ideograph-8F2B | 8f2a CJK Ideograph-8F2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f2d CJK Ideograph-8F2D | 8f2c CJK Ideograph-8F2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f2f CJK Ideograph-8F2F | 8f2e CJK Ideograph-8F2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f31 CJK Ideograph-8F31 | 8f30 CJK Ideograph-8F30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f33 CJK Ideograph-8F33 | 8f32 CJK Ideograph-8F32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f35 CJK Ideograph-8F35 | 8f34 CJK Ideograph-8F34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f37 CJK Ideograph-8F37 | 8f36 CJK Ideograph-8F36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f39 CJK Ideograph-8F39 | 8f38 CJK Ideograph-8F38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f3b CJK Ideograph-8F3B | 8f3a CJK Ideograph-8F3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f3d CJK Ideograph-8F3D | 8f3c CJK Ideograph-8F3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f3f CJK Ideograph-8F3F | 8f3e CJK Ideograph-8F3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f41 CJK Ideograph-8F41 | 8f40 CJK Ideograph-8F40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f43 CJK Ideograph-8F43 | 8f42 CJK Ideograph-8F42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f45 CJK Ideograph-8F45 | 8f44 CJK Ideograph-8F44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f47 CJK Ideograph-8F47 | 8f46 CJK Ideograph-8F46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f49 CJK Ideograph-8F49 | 8f48 CJK Ideograph-8F48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f4b CJK Ideograph-8F4B | 8f4a CJK Ideograph-8F4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f4d CJK Ideograph-8F4D | 8f4c CJK Ideograph-8F4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f4f CJK Ideograph-8F4F | 8f4e CJK Ideograph-8F4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f51 CJK Ideograph-8F51 | 8f50 CJK Ideograph-8F50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f53 CJK Ideograph-8F53 | 8f52 CJK Ideograph-8F52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f55 CJK Ideograph-8F55 | 8f54 CJK Ideograph-8F54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f57 CJK Ideograph-8F57 | 8f56 CJK Ideograph-8F56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f59 CJK Ideograph-8F59 | 8f58 CJK Ideograph-8F58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f5b CJK Ideograph-8F5B | 8f5a CJK Ideograph-8F5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f5d CJK Ideograph-8F5D | 8f5c CJK Ideograph-8F5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f5f CJK Ideograph-8F5F | 8f5e CJK Ideograph-8F5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f61 CJK Ideograph-8F61 | 8f60 CJK Ideograph-8F60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f63 CJK Ideograph-8F63 | 8f62 CJK Ideograph-8F62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f65 CJK Ideograph-8F65 | 8f64 CJK Ideograph-8F64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f67 CJK Ideograph-8F67 | 8f66 CJK Ideograph-8F66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f69 CJK Ideograph-8F69 | 8f68 CJK Ideograph-8F68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f6b CJK Ideograph-8F6B | 8f6a CJK Ideograph-8F6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f6d CJK Ideograph-8F6D | 8f6c CJK Ideograph-8F6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f6f CJK Ideograph-8F6F | 8f6e CJK Ideograph-8F6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f71 CJK Ideograph-8F71 | 8f70 CJK Ideograph-8F70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f73 CJK Ideograph-8F73 | 8f72 CJK Ideograph-8F72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f75 CJK Ideograph-8F75 | 8f74 CJK Ideograph-8F74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f77 CJK Ideograph-8F77 | 8f76 CJK Ideograph-8F76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f79 CJK Ideograph-8F79 | 8f78 CJK Ideograph-8F78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f7b CJK Ideograph-8F7B | 8f7a CJK Ideograph-8F7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f7d CJK Ideograph-8F7D | 8f7c CJK Ideograph-8F7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f7f CJK Ideograph-8F7F | 8f7e CJK Ideograph-8F7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f81 CJK Ideograph-8F81 | 8f80 CJK Ideograph-8F80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f83 CJK Ideograph-8F83 | 8f82 CJK Ideograph-8F82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f85 CJK Ideograph-8F85 | 8f84 CJK Ideograph-8F84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f87 CJK Ideograph-8F87 | 8f86 CJK Ideograph-8F86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f89 CJK Ideograph-8F89 | 8f88 CJK Ideograph-8F88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f8b CJK Ideograph-8F8B | 8f8a CJK Ideograph-8F8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f8d CJK Ideograph-8F8D | 8f8c CJK Ideograph-8F8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f8f CJK Ideograph-8F8F | 8f8e CJK Ideograph-8F8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f91 CJK Ideograph-8F91 | 8f90 CJK Ideograph-8F90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f93 CJK Ideograph-8F93 | 8f92 CJK Ideograph-8F92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f95 CJK Ideograph-8F95 | 8f94 CJK Ideograph-8F94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f97 CJK Ideograph-8F97 | 8f96 CJK Ideograph-8F96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f99 CJK Ideograph-8F99 | 8f98 CJK Ideograph-8F98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f9b CJK Ideograph-8F9B | 8f9a CJK Ideograph-8F9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f9d CJK Ideograph-8F9D | 8f9c CJK Ideograph-8F9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f9f CJK Ideograph-8F9F | 8f9e CJK Ideograph-8F9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fa1 CJK Ideograph-8FA1 | 8fa0 CJK Ideograph-8FA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fa3 CJK Ideograph-8FA3 | 8fa2 CJK Ideograph-8FA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fa5 CJK Ideograph-8FA5 | 8fa4 CJK Ideograph-8FA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fa7 CJK Ideograph-8FA7 | 8fa6 CJK Ideograph-8FA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fa9 CJK Ideograph-8FA9 | 8fa8 CJK Ideograph-8FA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fab CJK Ideograph-8FAB | 8faa CJK Ideograph-8FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fad CJK Ideograph-8FAD | 8fac CJK Ideograph-8FAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8faf CJK Ideograph-8FAF | 8fae CJK Ideograph-8FAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fb1 CJK Ideograph-8FB1 | 8fb0 CJK Ideograph-8FB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fb3 CJK Ideograph-8FB3 | 8fb2 CJK Ideograph-8FB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fb5 CJK Ideograph-8FB5 | 8fb4 CJK Ideograph-8FB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fb7 CJK Ideograph-8FB7 | 8fb6 CJK Ideograph-8FB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fb9 CJK Ideograph-8FB9 | 8fb8 CJK Ideograph-8FB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fbb CJK Ideograph-8FBB | 8fba CJK Ideograph-8FBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fbd CJK Ideograph-8FBD | 8fbc CJK Ideograph-8FBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fbf CJK Ideograph-8FBF | 8fbe CJK Ideograph-8FBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fc1 CJK Ideograph-8FC1 | 8fc0 CJK Ideograph-8FC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fc3 CJK Ideograph-8FC3 | 8fc2 CJK Ideograph-8FC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fc5 CJK Ideograph-8FC5 | 8fc4 CJK Ideograph-8FC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fc7 CJK Ideograph-8FC7 | 8fc6 CJK Ideograph-8FC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fc9 CJK Ideograph-8FC9 | 8fc8 CJK Ideograph-8FC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fcb CJK Ideograph-8FCB | 8fca CJK Ideograph-8FCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fcd CJK Ideograph-8FCD | 8fcc CJK Ideograph-8FCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fcf CJK Ideograph-8FCF | 8fce CJK Ideograph-8FCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fd1 CJK Ideograph-8FD1 | 8fd0 CJK Ideograph-8FD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fd3 CJK Ideograph-8FD3 | 8fd2 CJK Ideograph-8FD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fd5 CJK Ideograph-8FD5 | 8fd4 CJK Ideograph-8FD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fd7 CJK Ideograph-8FD7 | 8fd6 CJK Ideograph-8FD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fd9 CJK Ideograph-8FD9 | 8fd8 CJK Ideograph-8FD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fdb CJK Ideograph-8FDB | 8fda CJK Ideograph-8FDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fdd CJK Ideograph-8FDD | 8fdc CJK Ideograph-8FDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fdf CJK Ideograph-8FDF | 8fde CJK Ideograph-8FDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fe1 CJK Ideograph-8FE1 | 8fe0 CJK Ideograph-8FE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fe3 CJK Ideograph-8FE3 | 8fe2 CJK Ideograph-8FE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fe5 CJK Ideograph-8FE5 | 8fe4 CJK Ideograph-8FE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fe7 CJK Ideograph-8FE7 | 8fe6 CJK Ideograph-8FE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fe9 CJK Ideograph-8FE9 | 8fe8 CJK Ideograph-8FE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8feb CJK Ideograph-8FEB | 8fea CJK Ideograph-8FEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fed CJK Ideograph-8FED | 8fec CJK Ideograph-8FEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fef CJK Ideograph-8FEF | 8fee CJK Ideograph-8FEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ff1 CJK Ideograph-8FF1 | 8ff0 CJK Ideograph-8FF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ff3 CJK Ideograph-8FF3 | 8ff2 CJK Ideograph-8FF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ff5 CJK Ideograph-8FF5 | 8ff4 CJK Ideograph-8FF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ff7 CJK Ideograph-8FF7 | 8ff6 CJK Ideograph-8FF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ff9 CJK Ideograph-8FF9 | 8ff8 CJK Ideograph-8FF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ffb CJK Ideograph-8FFB | 8ffa CJK Ideograph-8FFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ffd CJK Ideograph-8FFD | 8ffc CJK Ideograph-8FFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fff CJK Ideograph-8FFF | 8ffe CJK Ideograph-8FFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9001 CJK Ideograph-9001 | 9000 CJK Ideograph-9000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9003 CJK Ideograph-9003 | 9002 CJK Ideograph-9002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9005 CJK Ideograph-9005 | 9004 CJK Ideograph-9004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9007 CJK Ideograph-9007 | 9006 CJK Ideograph-9006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9009 CJK Ideograph-9009 | 9008 CJK Ideograph-9008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 900b CJK Ideograph-900B | 900a CJK Ideograph-900A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 900d CJK Ideograph-900D | 900c CJK Ideograph-900C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 900f CJK Ideograph-900F | 900e CJK Ideograph-900E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9011 CJK Ideograph-9011 | 9010 CJK Ideograph-9010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9013 CJK Ideograph-9013 | 9012 CJK Ideograph-9012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9015 CJK Ideograph-9015 | 9014 CJK Ideograph-9014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9017 CJK Ideograph-9017 | 9016 CJK Ideograph-9016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9019 CJK Ideograph-9019 | 9018 CJK Ideograph-9018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 901b CJK Ideograph-901B | 901a CJK Ideograph-901A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 901d CJK Ideograph-901D | 901c CJK Ideograph-901C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 901f CJK Ideograph-901F | 901e CJK Ideograph-901E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9021 CJK Ideograph-9021 | 9020 CJK Ideograph-9020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9023 CJK Ideograph-9023 | 9022 CJK Ideograph-9022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9025 CJK Ideograph-9025 | 9024 CJK Ideograph-9024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9027 CJK Ideograph-9027 | 9026 CJK Ideograph-9026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9029 CJK Ideograph-9029 | 9028 CJK Ideograph-9028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 902b CJK Ideograph-902B | 902a CJK Ideograph-902A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 902d CJK Ideograph-902D | 902c CJK Ideograph-902C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 902f CJK Ideograph-902F | 902e CJK Ideograph-902E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9031 CJK Ideograph-9031 | 9030 CJK Ideograph-9030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9033 CJK Ideograph-9033 | 9032 CJK Ideograph-9032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9035 CJK Ideograph-9035 | 9034 CJK Ideograph-9034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9037 CJK Ideograph-9037 | 9036 CJK Ideograph-9036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9039 CJK Ideograph-9039 | 9038 CJK Ideograph-9038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 903b CJK Ideograph-903B | 903a CJK Ideograph-903A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 903d CJK Ideograph-903D | 903c CJK Ideograph-903C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 903f CJK Ideograph-903F | 903e CJK Ideograph-903E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9041 CJK Ideograph-9041 | 9040 CJK Ideograph-9040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9043 CJK Ideograph-9043 | 9042 CJK Ideograph-9042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9045 CJK Ideograph-9045 | 9044 CJK Ideograph-9044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9047 CJK Ideograph-9047 | 9046 CJK Ideograph-9046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9049 CJK Ideograph-9049 | 9048 CJK Ideograph-9048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 904b CJK Ideograph-904B | 904a CJK Ideograph-904A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 904d CJK Ideograph-904D | 904c CJK Ideograph-904C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 904f CJK Ideograph-904F | 904e CJK Ideograph-904E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9051 CJK Ideograph-9051 | 9050 CJK Ideograph-9050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9053 CJK Ideograph-9053 | 9052 CJK Ideograph-9052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9055 CJK Ideograph-9055 | 9054 CJK Ideograph-9054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9057 CJK Ideograph-9057 | 9056 CJK Ideograph-9056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9059 CJK Ideograph-9059 | 9058 CJK Ideograph-9058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 905b CJK Ideograph-905B | 905a CJK Ideograph-905A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 905d CJK Ideograph-905D | 905c CJK Ideograph-905C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 905f CJK Ideograph-905F | 905e CJK Ideograph-905E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9061 CJK Ideograph-9061 | 9060 CJK Ideograph-9060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9063 CJK Ideograph-9063 | 9062 CJK Ideograph-9062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9065 CJK Ideograph-9065 | 9064 CJK Ideograph-9064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9067 CJK Ideograph-9067 | 9066 CJK Ideograph-9066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9069 CJK Ideograph-9069 | 9068 CJK Ideograph-9068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 906b CJK Ideograph-906B | 906a CJK Ideograph-906A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 906d CJK Ideograph-906D | 906c CJK Ideograph-906C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 906f CJK Ideograph-906F | 906e CJK Ideograph-906E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9071 CJK Ideograph-9071 | 9070 CJK Ideograph-9070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9073 CJK Ideograph-9073 | 9072 CJK Ideograph-9072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9075 CJK Ideograph-9075 | 9074 CJK Ideograph-9074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9077 CJK Ideograph-9077 | 9076 CJK Ideograph-9076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9079 CJK Ideograph-9079 | 9078 CJK Ideograph-9078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 907b CJK Ideograph-907B | 907a CJK Ideograph-907A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 907d CJK Ideograph-907D | 907c CJK Ideograph-907C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 907f CJK Ideograph-907F | 907e CJK Ideograph-907E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9081 CJK Ideograph-9081 | 9080 CJK Ideograph-9080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9083 CJK Ideograph-9083 | 9082 CJK Ideograph-9082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9085 CJK Ideograph-9085 | 9084 CJK Ideograph-9084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9087 CJK Ideograph-9087 | 9086 CJK Ideograph-9086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9089 CJK Ideograph-9089 | 9088 CJK Ideograph-9088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 908b CJK Ideograph-908B | 908a CJK Ideograph-908A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 908d CJK Ideograph-908D | 908c CJK Ideograph-908C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 908f CJK Ideograph-908F | 908e CJK Ideograph-908E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9091 CJK Ideograph-9091 | 9090 CJK Ideograph-9090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9093 CJK Ideograph-9093 | 9092 CJK Ideograph-9092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9095 CJK Ideograph-9095 | 9094 CJK Ideograph-9094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9097 CJK Ideograph-9097 | 9096 CJK Ideograph-9096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9099 CJK Ideograph-9099 | 9098 CJK Ideograph-9098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 909b CJK Ideograph-909B | 909a CJK Ideograph-909A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 909d CJK Ideograph-909D | 909c CJK Ideograph-909C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 909f CJK Ideograph-909F | 909e CJK Ideograph-909E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90a1 CJK Ideograph-90A1 | 90a0 CJK Ideograph-90A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90a3 CJK Ideograph-90A3 | 90a2 CJK Ideograph-90A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90a5 CJK Ideograph-90A5 | 90a4 CJK Ideograph-90A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90a7 CJK Ideograph-90A7 | 90a6 CJK Ideograph-90A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90a9 CJK Ideograph-90A9 | 90a8 CJK Ideograph-90A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90ab CJK Ideograph-90AB | 90aa CJK Ideograph-90AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90ad CJK Ideograph-90AD | 90ac CJK Ideograph-90AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90af CJK Ideograph-90AF | 90ae CJK Ideograph-90AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90b1 CJK Ideograph-90B1 | 90b0 CJK Ideograph-90B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90b3 CJK Ideograph-90B3 | 90b2 CJK Ideograph-90B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90b5 CJK Ideograph-90B5 | 90b4 CJK Ideograph-90B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90b7 CJK Ideograph-90B7 | 90b6 CJK Ideograph-90B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90b9 CJK Ideograph-90B9 | 90b8 CJK Ideograph-90B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90bb CJK Ideograph-90BB | 90ba CJK Ideograph-90BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90bd CJK Ideograph-90BD | 90bc CJK Ideograph-90BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90bf CJK Ideograph-90BF | 90be CJK Ideograph-90BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90c1 CJK Ideograph-90C1 | 90c0 CJK Ideograph-90C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90c3 CJK Ideograph-90C3 | 90c2 CJK Ideograph-90C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90c5 CJK Ideograph-90C5 | 90c4 CJK Ideograph-90C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90c7 CJK Ideograph-90C7 | 90c6 CJK Ideograph-90C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90c9 CJK Ideograph-90C9 | 90c8 CJK Ideograph-90C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90cb CJK Ideograph-90CB | 90ca CJK Ideograph-90CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90cd CJK Ideograph-90CD | 90cc CJK Ideograph-90CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90cf CJK Ideograph-90CF | 90ce CJK Ideograph-90CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90d1 CJK Ideograph-90D1 | 90d0 CJK Ideograph-90D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90d3 CJK Ideograph-90D3 | 90d2 CJK Ideograph-90D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90d5 CJK Ideograph-90D5 | 90d4 CJK Ideograph-90D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90d7 CJK Ideograph-90D7 | 90d6 CJK Ideograph-90D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90d9 CJK Ideograph-90D9 | 90d8 CJK Ideograph-90D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90db CJK Ideograph-90DB | 90da CJK Ideograph-90DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90dd CJK Ideograph-90DD | 90dc CJK Ideograph-90DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90df CJK Ideograph-90DF | 90de CJK Ideograph-90DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90e1 CJK Ideograph-90E1 | 90e0 CJK Ideograph-90E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90e3 CJK Ideograph-90E3 | 90e2 CJK Ideograph-90E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90e5 CJK Ideograph-90E5 | 90e4 CJK Ideograph-90E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90e7 CJK Ideograph-90E7 | 90e6 CJK Ideograph-90E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90e9 CJK Ideograph-90E9 | 90e8 CJK Ideograph-90E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90eb CJK Ideograph-90EB | 90ea CJK Ideograph-90EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90ed CJK Ideograph-90ED | 90ec CJK Ideograph-90EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90ef CJK Ideograph-90EF | 90ee CJK Ideograph-90EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90f1 CJK Ideograph-90F1 | 90f0 CJK Ideograph-90F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90f3 CJK Ideograph-90F3 | 90f2 CJK Ideograph-90F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90f5 CJK Ideograph-90F5 | 90f4 CJK Ideograph-90F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90f7 CJK Ideograph-90F7 | 90f6 CJK Ideograph-90F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90f9 CJK Ideograph-90F9 | 90f8 CJK Ideograph-90F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90fb CJK Ideograph-90FB | 90fa CJK Ideograph-90FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90fd CJK Ideograph-90FD | 90fc CJK Ideograph-90FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90ff CJK Ideograph-90FF | 90fe CJK Ideograph-90FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9101 CJK Ideograph-9101 | 9100 CJK Ideograph-9100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9103 CJK Ideograph-9103 | 9102 CJK Ideograph-9102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9105 CJK Ideograph-9105 | 9104 CJK Ideograph-9104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9107 CJK Ideograph-9107 | 9106 CJK Ideograph-9106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9109 CJK Ideograph-9109 | 9108 CJK Ideograph-9108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 910b CJK Ideograph-910B | 910a CJK Ideograph-910A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 910d CJK Ideograph-910D | 910c CJK Ideograph-910C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 910f CJK Ideograph-910F | 910e CJK Ideograph-910E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9111 CJK Ideograph-9111 | 9110 CJK Ideograph-9110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9113 CJK Ideograph-9113 | 9112 CJK Ideograph-9112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9115 CJK Ideograph-9115 | 9114 CJK Ideograph-9114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9117 CJK Ideograph-9117 | 9116 CJK Ideograph-9116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9119 CJK Ideograph-9119 | 9118 CJK Ideograph-9118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 911b CJK Ideograph-911B | 911a CJK Ideograph-911A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 911d CJK Ideograph-911D | 911c CJK Ideograph-911C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 911f CJK Ideograph-911F | 911e CJK Ideograph-911E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9121 CJK Ideograph-9121 | 9120 CJK Ideograph-9120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9123 CJK Ideograph-9123 | 9122 CJK Ideograph-9122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9125 CJK Ideograph-9125 | 9124 CJK Ideograph-9124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9127 CJK Ideograph-9127 | 9126 CJK Ideograph-9126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9129 CJK Ideograph-9129 | 9128 CJK Ideograph-9128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 912b CJK Ideograph-912B | 912a CJK Ideograph-912A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 912d CJK Ideograph-912D | 912c CJK Ideograph-912C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 912f CJK Ideograph-912F | 912e CJK Ideograph-912E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9131 CJK Ideograph-9131 | 9130 CJK Ideograph-9130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9133 CJK Ideograph-9133 | 9132 CJK Ideograph-9132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9135 CJK Ideograph-9135 | 9134 CJK Ideograph-9134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9137 CJK Ideograph-9137 | 9136 CJK Ideograph-9136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9139 CJK Ideograph-9139 | 9138 CJK Ideograph-9138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 913b CJK Ideograph-913B | 913a CJK Ideograph-913A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 913d CJK Ideograph-913D | 913c CJK Ideograph-913C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 913f CJK Ideograph-913F | 913e CJK Ideograph-913E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9141 CJK Ideograph-9141 | 9140 CJK Ideograph-9140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9143 CJK Ideograph-9143 | 9142 CJK Ideograph-9142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9145 CJK Ideograph-9145 | 9144 CJK Ideograph-9144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9147 CJK Ideograph-9147 | 9146 CJK Ideograph-9146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9149 CJK Ideograph-9149 | 9148 CJK Ideograph-9148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 914b CJK Ideograph-914B | 914a CJK Ideograph-914A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 914d CJK Ideograph-914D | 914c CJK Ideograph-914C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 914f CJK Ideograph-914F | 914e CJK Ideograph-914E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9151 CJK Ideograph-9151 | 9150 CJK Ideograph-9150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9153 CJK Ideograph-9153 | 9152 CJK Ideograph-9152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9155 CJK Ideograph-9155 | 9154 CJK Ideograph-9154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9157 CJK Ideograph-9157 | 9156 CJK Ideograph-9156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9159 CJK Ideograph-9159 | 9158 CJK Ideograph-9158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 915b CJK Ideograph-915B | 915a CJK Ideograph-915A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 915d CJK Ideograph-915D | 915c CJK Ideograph-915C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 915f CJK Ideograph-915F | 915e CJK Ideograph-915E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9161 CJK Ideograph-9161 | 9160 CJK Ideograph-9160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9163 CJK Ideograph-9163 | 9162 CJK Ideograph-9162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9165 CJK Ideograph-9165 | 9164 CJK Ideograph-9164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9167 CJK Ideograph-9167 | 9166 CJK Ideograph-9166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9169 CJK Ideograph-9169 | 9168 CJK Ideograph-9168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 916b CJK Ideograph-916B | 916a CJK Ideograph-916A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 916d CJK Ideograph-916D | 916c CJK Ideograph-916C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 916f CJK Ideograph-916F | 916e CJK Ideograph-916E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9171 CJK Ideograph-9171 | 9170 CJK Ideograph-9170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9173 CJK Ideograph-9173 | 9172 CJK Ideograph-9172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9175 CJK Ideograph-9175 | 9174 CJK Ideograph-9174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9177 CJK Ideograph-9177 | 9176 CJK Ideograph-9176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9179 CJK Ideograph-9179 | 9178 CJK Ideograph-9178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 917b CJK Ideograph-917B | 917a CJK Ideograph-917A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 917d CJK Ideograph-917D | 917c CJK Ideograph-917C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 917f CJK Ideograph-917F | 917e CJK Ideograph-917E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9181 CJK Ideograph-9181 | 9180 CJK Ideograph-9180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9183 CJK Ideograph-9183 | 9182 CJK Ideograph-9182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9185 CJK Ideograph-9185 | 9184 CJK Ideograph-9184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9187 CJK Ideograph-9187 | 9186 CJK Ideograph-9186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9189 CJK Ideograph-9189 | 9188 CJK Ideograph-9188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 918b CJK Ideograph-918B | 918a CJK Ideograph-918A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 918d CJK Ideograph-918D | 918c CJK Ideograph-918C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 918f CJK Ideograph-918F | 918e CJK Ideograph-918E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9191 CJK Ideograph-9191 | 9190 CJK Ideograph-9190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9193 CJK Ideograph-9193 | 9192 CJK Ideograph-9192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9195 CJK Ideograph-9195 | 9194 CJK Ideograph-9194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9197 CJK Ideograph-9197 | 9196 CJK Ideograph-9196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9199 CJK Ideograph-9199 | 9198 CJK Ideograph-9198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 919b CJK Ideograph-919B | 919a CJK Ideograph-919A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 919d CJK Ideograph-919D | 919c CJK Ideograph-919C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 919f CJK Ideograph-919F | 919e CJK Ideograph-919E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91a1 CJK Ideograph-91A1 | 91a0 CJK Ideograph-91A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91a3 CJK Ideograph-91A3 | 91a2 CJK Ideograph-91A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91a5 CJK Ideograph-91A5 | 91a4 CJK Ideograph-91A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91a7 CJK Ideograph-91A7 | 91a6 CJK Ideograph-91A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91a9 CJK Ideograph-91A9 | 91a8 CJK Ideograph-91A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91ab CJK Ideograph-91AB | 91aa CJK Ideograph-91AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91ad CJK Ideograph-91AD | 91ac CJK Ideograph-91AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91af CJK Ideograph-91AF | 91ae CJK Ideograph-91AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91b1 CJK Ideograph-91B1 | 91b0 CJK Ideograph-91B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91b3 CJK Ideograph-91B3 | 91b2 CJK Ideograph-91B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91b5 CJK Ideograph-91B5 | 91b4 CJK Ideograph-91B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91b7 CJK Ideograph-91B7 | 91b6 CJK Ideograph-91B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91b9 CJK Ideograph-91B9 | 91b8 CJK Ideograph-91B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91bb CJK Ideograph-91BB | 91ba CJK Ideograph-91BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91bd CJK Ideograph-91BD | 91bc CJK Ideograph-91BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91bf CJK Ideograph-91BF | 91be CJK Ideograph-91BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91c1 CJK Ideograph-91C1 | 91c0 CJK Ideograph-91C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91c3 CJK Ideograph-91C3 | 91c2 CJK Ideograph-91C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91c5 CJK Ideograph-91C5 | 91c4 CJK Ideograph-91C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91c7 CJK Ideograph-91C7 | 91c6 CJK Ideograph-91C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91c9 CJK Ideograph-91C9 | 91c8 CJK Ideograph-91C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91cb CJK Ideograph-91CB | 91ca CJK Ideograph-91CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91cd CJK Ideograph-91CD | 91cc CJK Ideograph-91CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91cf CJK Ideograph-91CF | 91ce CJK Ideograph-91CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91d1 CJK Ideograph-91D1 | 91d0 CJK Ideograph-91D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91d3 CJK Ideograph-91D3 | 91d2 CJK Ideograph-91D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91d5 CJK Ideograph-91D5 | 91d4 CJK Ideograph-91D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91d7 CJK Ideograph-91D7 | 91d6 CJK Ideograph-91D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91d9 CJK Ideograph-91D9 | 91d8 CJK Ideograph-91D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91db CJK Ideograph-91DB | 91da CJK Ideograph-91DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91dd CJK Ideograph-91DD | 91dc CJK Ideograph-91DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91df CJK Ideograph-91DF | 91de CJK Ideograph-91DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91e1 CJK Ideograph-91E1 | 91e0 CJK Ideograph-91E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91e3 CJK Ideograph-91E3 | 91e2 CJK Ideograph-91E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91e5 CJK Ideograph-91E5 | 91e4 CJK Ideograph-91E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91e7 CJK Ideograph-91E7 | 91e6 CJK Ideograph-91E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91e9 CJK Ideograph-91E9 | 91e8 CJK Ideograph-91E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91eb CJK Ideograph-91EB | 91ea CJK Ideograph-91EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91ed CJK Ideograph-91ED | 91ec CJK Ideograph-91EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91ef CJK Ideograph-91EF | 91ee CJK Ideograph-91EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91f1 CJK Ideograph-91F1 | 91f0 CJK Ideograph-91F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91f3 CJK Ideograph-91F3 | 91f2 CJK Ideograph-91F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91f5 CJK Ideograph-91F5 | 91f4 CJK Ideograph-91F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91f7 CJK Ideograph-91F7 | 91f6 CJK Ideograph-91F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91f9 CJK Ideograph-91F9 | 91f8 CJK Ideograph-91F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91fb CJK Ideograph-91FB | 91fa CJK Ideograph-91FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91fd CJK Ideograph-91FD | 91fc CJK Ideograph-91FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91ff CJK Ideograph-91FF | 91fe CJK Ideograph-91FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9201 CJK Ideograph-9201 | 9200 CJK Ideograph-9200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9203 CJK Ideograph-9203 | 9202 CJK Ideograph-9202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9205 CJK Ideograph-9205 | 9204 CJK Ideograph-9204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9207 CJK Ideograph-9207 | 9206 CJK Ideograph-9206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9209 CJK Ideograph-9209 | 9208 CJK Ideograph-9208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 920b CJK Ideograph-920B | 920a CJK Ideograph-920A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 920d CJK Ideograph-920D | 920c CJK Ideograph-920C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 920f CJK Ideograph-920F | 920e CJK Ideograph-920E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9211 CJK Ideograph-9211 | 9210 CJK Ideograph-9210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9213 CJK Ideograph-9213 | 9212 CJK Ideograph-9212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9215 CJK Ideograph-9215 | 9214 CJK Ideograph-9214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9217 CJK Ideograph-9217 | 9216 CJK Ideograph-9216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9219 CJK Ideograph-9219 | 9218 CJK Ideograph-9218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 921b CJK Ideograph-921B | 921a CJK Ideograph-921A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 921d CJK Ideograph-921D | 921c CJK Ideograph-921C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 921f CJK Ideograph-921F | 921e CJK Ideograph-921E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9221 CJK Ideograph-9221 | 9220 CJK Ideograph-9220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9223 CJK Ideograph-9223 | 9222 CJK Ideograph-9222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9225 CJK Ideograph-9225 | 9224 CJK Ideograph-9224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9227 CJK Ideograph-9227 | 9226 CJK Ideograph-9226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9229 CJK Ideograph-9229 | 9228 CJK Ideograph-9228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 922b CJK Ideograph-922B | 922a CJK Ideograph-922A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 922d CJK Ideograph-922D | 922c CJK Ideograph-922C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 922f CJK Ideograph-922F | 922e CJK Ideograph-922E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9231 CJK Ideograph-9231 | 9230 CJK Ideograph-9230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9233 CJK Ideograph-9233 | 9232 CJK Ideograph-9232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9235 CJK Ideograph-9235 | 9234 CJK Ideograph-9234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9237 CJK Ideograph-9237 | 9236 CJK Ideograph-9236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9239 CJK Ideograph-9239 | 9238 CJK Ideograph-9238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 923b CJK Ideograph-923B | 923a CJK Ideograph-923A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 923d CJK Ideograph-923D | 923c CJK Ideograph-923C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 923f CJK Ideograph-923F | 923e CJK Ideograph-923E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9241 CJK Ideograph-9241 | 9240 CJK Ideograph-9240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9243 CJK Ideograph-9243 | 9242 CJK Ideograph-9242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9245 CJK Ideograph-9245 | 9244 CJK Ideograph-9244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9247 CJK Ideograph-9247 | 9246 CJK Ideograph-9246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9249 CJK Ideograph-9249 | 9248 CJK Ideograph-9248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 924b CJK Ideograph-924B | 924a CJK Ideograph-924A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 924d CJK Ideograph-924D | 924c CJK Ideograph-924C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 924f CJK Ideograph-924F | 924e CJK Ideograph-924E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9251 CJK Ideograph-9251 | 9250 CJK Ideograph-9250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9253 CJK Ideograph-9253 | 9252 CJK Ideograph-9252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9255 CJK Ideograph-9255 | 9254 CJK Ideograph-9254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9257 CJK Ideograph-9257 | 9256 CJK Ideograph-9256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9259 CJK Ideograph-9259 | 9258 CJK Ideograph-9258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 925b CJK Ideograph-925B | 925a CJK Ideograph-925A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 925d CJK Ideograph-925D | 925c CJK Ideograph-925C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 925f CJK Ideograph-925F | 925e CJK Ideograph-925E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9261 CJK Ideograph-9261 | 9260 CJK Ideograph-9260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9263 CJK Ideograph-9263 | 9262 CJK Ideograph-9262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9265 CJK Ideograph-9265 | 9264 CJK Ideograph-9264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9267 CJK Ideograph-9267 | 9266 CJK Ideograph-9266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9269 CJK Ideograph-9269 | 9268 CJK Ideograph-9268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 926b CJK Ideograph-926B | 926a CJK Ideograph-926A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 926d CJK Ideograph-926D | 926c CJK Ideograph-926C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 926f CJK Ideograph-926F | 926e CJK Ideograph-926E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9271 CJK Ideograph-9271 | 9270 CJK Ideograph-9270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9273 CJK Ideograph-9273 | 9272 CJK Ideograph-9272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9275 CJK Ideograph-9275 | 9274 CJK Ideograph-9274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9277 CJK Ideograph-9277 | 9276 CJK Ideograph-9276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9279 CJK Ideograph-9279 | 9278 CJK Ideograph-9278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 927b CJK Ideograph-927B | 927a CJK Ideograph-927A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 927d CJK Ideograph-927D | 927c CJK Ideograph-927C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 927f CJK Ideograph-927F | 927e CJK Ideograph-927E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9281 CJK Ideograph-9281 | 9280 CJK Ideograph-9280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9283 CJK Ideograph-9283 | 9282 CJK Ideograph-9282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9285 CJK Ideograph-9285 | 9284 CJK Ideograph-9284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9287 CJK Ideograph-9287 | 9286 CJK Ideograph-9286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9289 CJK Ideograph-9289 | 9288 CJK Ideograph-9288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 928b CJK Ideograph-928B | 928a CJK Ideograph-928A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 928d CJK Ideograph-928D | 928c CJK Ideograph-928C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 928f CJK Ideograph-928F | 928e CJK Ideograph-928E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9291 CJK Ideograph-9291 | 9290 CJK Ideograph-9290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9293 CJK Ideograph-9293 | 9292 CJK Ideograph-9292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9295 CJK Ideograph-9295 | 9294 CJK Ideograph-9294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9297 CJK Ideograph-9297 | 9296 CJK Ideograph-9296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9299 CJK Ideograph-9299 | 9298 CJK Ideograph-9298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 929b CJK Ideograph-929B | 929a CJK Ideograph-929A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 929d CJK Ideograph-929D | 929c CJK Ideograph-929C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 929f CJK Ideograph-929F | 929e CJK Ideograph-929E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92a1 CJK Ideograph-92A1 | 92a0 CJK Ideograph-92A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92a3 CJK Ideograph-92A3 | 92a2 CJK Ideograph-92A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92a5 CJK Ideograph-92A5 | 92a4 CJK Ideograph-92A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92a7 CJK Ideograph-92A7 | 92a6 CJK Ideograph-92A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92a9 CJK Ideograph-92A9 | 92a8 CJK Ideograph-92A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92ab CJK Ideograph-92AB | 92aa CJK Ideograph-92AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92ad CJK Ideograph-92AD | 92ac CJK Ideograph-92AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92af CJK Ideograph-92AF | 92ae CJK Ideograph-92AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92b1 CJK Ideograph-92B1 | 92b0 CJK Ideograph-92B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92b3 CJK Ideograph-92B3 | 92b2 CJK Ideograph-92B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92b5 CJK Ideograph-92B5 | 92b4 CJK Ideograph-92B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92b7 CJK Ideograph-92B7 | 92b6 CJK Ideograph-92B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92b9 CJK Ideograph-92B9 | 92b8 CJK Ideograph-92B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92bb CJK Ideograph-92BB | 92ba CJK Ideograph-92BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92bd CJK Ideograph-92BD | 92bc CJK Ideograph-92BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92bf CJK Ideograph-92BF | 92be CJK Ideograph-92BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92c1 CJK Ideograph-92C1 | 92c0 CJK Ideograph-92C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92c3 CJK Ideograph-92C3 | 92c2 CJK Ideograph-92C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92c5 CJK Ideograph-92C5 | 92c4 CJK Ideograph-92C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92c7 CJK Ideograph-92C7 | 92c6 CJK Ideograph-92C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92c9 CJK Ideograph-92C9 | 92c8 CJK Ideograph-92C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92cb CJK Ideograph-92CB | 92ca CJK Ideograph-92CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92cd CJK Ideograph-92CD | 92cc CJK Ideograph-92CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92cf CJK Ideograph-92CF | 92ce CJK Ideograph-92CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92d1 CJK Ideograph-92D1 | 92d0 CJK Ideograph-92D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92d3 CJK Ideograph-92D3 | 92d2 CJK Ideograph-92D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92d5 CJK Ideograph-92D5 | 92d4 CJK Ideograph-92D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92d7 CJK Ideograph-92D7 | 92d6 CJK Ideograph-92D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92d9 CJK Ideograph-92D9 | 92d8 CJK Ideograph-92D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92db CJK Ideograph-92DB | 92da CJK Ideograph-92DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92dd CJK Ideograph-92DD | 92dc CJK Ideograph-92DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92df CJK Ideograph-92DF | 92de CJK Ideograph-92DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92e1 CJK Ideograph-92E1 | 92e0 CJK Ideograph-92E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92e3 CJK Ideograph-92E3 | 92e2 CJK Ideograph-92E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92e5 CJK Ideograph-92E5 | 92e4 CJK Ideograph-92E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92e7 CJK Ideograph-92E7 | 92e6 CJK Ideograph-92E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92e9 CJK Ideograph-92E9 | 92e8 CJK Ideograph-92E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92eb CJK Ideograph-92EB | 92ea CJK Ideograph-92EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92ed CJK Ideograph-92ED | 92ec CJK Ideograph-92EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92ef CJK Ideograph-92EF | 92ee CJK Ideograph-92EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92f1 CJK Ideograph-92F1 | 92f0 CJK Ideograph-92F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92f3 CJK Ideograph-92F3 | 92f2 CJK Ideograph-92F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92f5 CJK Ideograph-92F5 | 92f4 CJK Ideograph-92F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92f7 CJK Ideograph-92F7 | 92f6 CJK Ideograph-92F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92f9 CJK Ideograph-92F9 | 92f8 CJK Ideograph-92F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92fb CJK Ideograph-92FB | 92fa CJK Ideograph-92FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92fd CJK Ideograph-92FD | 92fc CJK Ideograph-92FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92ff CJK Ideograph-92FF | 92fe CJK Ideograph-92FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9301 CJK Ideograph-9301 | 9300 CJK Ideograph-9300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9303 CJK Ideograph-9303 | 9302 CJK Ideograph-9302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9305 CJK Ideograph-9305 | 9304 CJK Ideograph-9304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9307 CJK Ideograph-9307 | 9306 CJK Ideograph-9306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9309 CJK Ideograph-9309 | 9308 CJK Ideograph-9308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 930b CJK Ideograph-930B | 930a CJK Ideograph-930A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 930d CJK Ideograph-930D | 930c CJK Ideograph-930C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 930f CJK Ideograph-930F | 930e CJK Ideograph-930E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9311 CJK Ideograph-9311 | 9310 CJK Ideograph-9310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9313 CJK Ideograph-9313 | 9312 CJK Ideograph-9312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9315 CJK Ideograph-9315 | 9314 CJK Ideograph-9314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9317 CJK Ideograph-9317 | 9316 CJK Ideograph-9316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9319 CJK Ideograph-9319 | 9318 CJK Ideograph-9318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 931b CJK Ideograph-931B | 931a CJK Ideograph-931A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 931d CJK Ideograph-931D | 931c CJK Ideograph-931C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 931f CJK Ideograph-931F | 931e CJK Ideograph-931E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9321 CJK Ideograph-9321 | 9320 CJK Ideograph-9320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9323 CJK Ideograph-9323 | 9322 CJK Ideograph-9322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9325 CJK Ideograph-9325 | 9324 CJK Ideograph-9324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9327 CJK Ideograph-9327 | 9326 CJK Ideograph-9326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9329 CJK Ideograph-9329 | 9328 CJK Ideograph-9328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 932b CJK Ideograph-932B | 932a CJK Ideograph-932A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 932d CJK Ideograph-932D | 932c CJK Ideograph-932C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 932f CJK Ideograph-932F | 932e CJK Ideograph-932E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9331 CJK Ideograph-9331 | 9330 CJK Ideograph-9330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9333 CJK Ideograph-9333 | 9332 CJK Ideograph-9332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9335 CJK Ideograph-9335 | 9334 CJK Ideograph-9334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9337 CJK Ideograph-9337 | 9336 CJK Ideograph-9336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9339 CJK Ideograph-9339 | 9338 CJK Ideograph-9338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 933b CJK Ideograph-933B | 933a CJK Ideograph-933A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 933d CJK Ideograph-933D | 933c CJK Ideograph-933C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 933f CJK Ideograph-933F | 933e CJK Ideograph-933E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9341 CJK Ideograph-9341 | 9340 CJK Ideograph-9340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9343 CJK Ideograph-9343 | 9342 CJK Ideograph-9342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9345 CJK Ideograph-9345 | 9344 CJK Ideograph-9344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9347 CJK Ideograph-9347 | 9346 CJK Ideograph-9346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9349 CJK Ideograph-9349 | 9348 CJK Ideograph-9348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 934b CJK Ideograph-934B | 934a CJK Ideograph-934A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 934d CJK Ideograph-934D | 934c CJK Ideograph-934C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 934f CJK Ideograph-934F | 934e CJK Ideograph-934E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9351 CJK Ideograph-9351 | 9350 CJK Ideograph-9350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9353 CJK Ideograph-9353 | 9352 CJK Ideograph-9352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9355 CJK Ideograph-9355 | 9354 CJK Ideograph-9354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9357 CJK Ideograph-9357 | 9356 CJK Ideograph-9356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9359 CJK Ideograph-9359 | 9358 CJK Ideograph-9358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 935b CJK Ideograph-935B | 935a CJK Ideograph-935A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 935d CJK Ideograph-935D | 935c CJK Ideograph-935C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 935f CJK Ideograph-935F | 935e CJK Ideograph-935E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9361 CJK Ideograph-9361 | 9360 CJK Ideograph-9360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9363 CJK Ideograph-9363 | 9362 CJK Ideograph-9362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9365 CJK Ideograph-9365 | 9364 CJK Ideograph-9364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9367 CJK Ideograph-9367 | 9366 CJK Ideograph-9366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9369 CJK Ideograph-9369 | 9368 CJK Ideograph-9368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 936b CJK Ideograph-936B | 936a CJK Ideograph-936A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 936d CJK Ideograph-936D | 936c CJK Ideograph-936C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 936f CJK Ideograph-936F | 936e CJK Ideograph-936E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9371 CJK Ideograph-9371 | 9370 CJK Ideograph-9370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9373 CJK Ideograph-9373 | 9372 CJK Ideograph-9372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9375 CJK Ideograph-9375 | 9374 CJK Ideograph-9374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9377 CJK Ideograph-9377 | 9376 CJK Ideograph-9376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9379 CJK Ideograph-9379 | 9378 CJK Ideograph-9378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 937b CJK Ideograph-937B | 937a CJK Ideograph-937A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 937d CJK Ideograph-937D | 937c CJK Ideograph-937C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 937f CJK Ideograph-937F | 937e CJK Ideograph-937E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9381 CJK Ideograph-9381 | 9380 CJK Ideograph-9380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9383 CJK Ideograph-9383 | 9382 CJK Ideograph-9382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9385 CJK Ideograph-9385 | 9384 CJK Ideograph-9384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9387 CJK Ideograph-9387 | 9386 CJK Ideograph-9386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9389 CJK Ideograph-9389 | 9388 CJK Ideograph-9388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 938b CJK Ideograph-938B | 938a CJK Ideograph-938A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 938d CJK Ideograph-938D | 938c CJK Ideograph-938C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 938f CJK Ideograph-938F | 938e CJK Ideograph-938E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9391 CJK Ideograph-9391 | 9390 CJK Ideograph-9390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9393 CJK Ideograph-9393 | 9392 CJK Ideograph-9392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9395 CJK Ideograph-9395 | 9394 CJK Ideograph-9394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9397 CJK Ideograph-9397 | 9396 CJK Ideograph-9396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9399 CJK Ideograph-9399 | 9398 CJK Ideograph-9398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 939b CJK Ideograph-939B | 939a CJK Ideograph-939A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 939d CJK Ideograph-939D | 939c CJK Ideograph-939C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 939f CJK Ideograph-939F | 939e CJK Ideograph-939E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93a1 CJK Ideograph-93A1 | 93a0 CJK Ideograph-93A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93a3 CJK Ideograph-93A3 | 93a2 CJK Ideograph-93A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93a5 CJK Ideograph-93A5 | 93a4 CJK Ideograph-93A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93a7 CJK Ideograph-93A7 | 93a6 CJK Ideograph-93A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93a9 CJK Ideograph-93A9 | 93a8 CJK Ideograph-93A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93ab CJK Ideograph-93AB | 93aa CJK Ideograph-93AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93ad CJK Ideograph-93AD | 93ac CJK Ideograph-93AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93af CJK Ideograph-93AF | 93ae CJK Ideograph-93AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93b1 CJK Ideograph-93B1 | 93b0 CJK Ideograph-93B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93b3 CJK Ideograph-93B3 | 93b2 CJK Ideograph-93B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93b5 CJK Ideograph-93B5 | 93b4 CJK Ideograph-93B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93b7 CJK Ideograph-93B7 | 93b6 CJK Ideograph-93B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93b9 CJK Ideograph-93B9 | 93b8 CJK Ideograph-93B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93bb CJK Ideograph-93BB | 93ba CJK Ideograph-93BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93bd CJK Ideograph-93BD | 93bc CJK Ideograph-93BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93bf CJK Ideograph-93BF | 93be CJK Ideograph-93BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93c1 CJK Ideograph-93C1 | 93c0 CJK Ideograph-93C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93c3 CJK Ideograph-93C3 | 93c2 CJK Ideograph-93C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93c5 CJK Ideograph-93C5 | 93c4 CJK Ideograph-93C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93c7 CJK Ideograph-93C7 | 93c6 CJK Ideograph-93C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93c9 CJK Ideograph-93C9 | 93c8 CJK Ideograph-93C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93cb CJK Ideograph-93CB | 93ca CJK Ideograph-93CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93cd CJK Ideograph-93CD | 93cc CJK Ideograph-93CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93cf CJK Ideograph-93CF | 93ce CJK Ideograph-93CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93d1 CJK Ideograph-93D1 | 93d0 CJK Ideograph-93D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93d3 CJK Ideograph-93D3 | 93d2 CJK Ideograph-93D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93d5 CJK Ideograph-93D5 | 93d4 CJK Ideograph-93D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93d7 CJK Ideograph-93D7 | 93d6 CJK Ideograph-93D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93d9 CJK Ideograph-93D9 | 93d8 CJK Ideograph-93D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93db CJK Ideograph-93DB | 93da CJK Ideograph-93DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93dd CJK Ideograph-93DD | 93dc CJK Ideograph-93DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93df CJK Ideograph-93DF | 93de CJK Ideograph-93DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93e1 CJK Ideograph-93E1 | 93e0 CJK Ideograph-93E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93e3 CJK Ideograph-93E3 | 93e2 CJK Ideograph-93E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93e5 CJK Ideograph-93E5 | 93e4 CJK Ideograph-93E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93e7 CJK Ideograph-93E7 | 93e6 CJK Ideograph-93E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93e9 CJK Ideograph-93E9 | 93e8 CJK Ideograph-93E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93eb CJK Ideograph-93EB | 93ea CJK Ideograph-93EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93ed CJK Ideograph-93ED | 93ec CJK Ideograph-93EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93ef CJK Ideograph-93EF | 93ee CJK Ideograph-93EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93f1 CJK Ideograph-93F1 | 93f0 CJK Ideograph-93F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93f3 CJK Ideograph-93F3 | 93f2 CJK Ideograph-93F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93f5 CJK Ideograph-93F5 | 93f4 CJK Ideograph-93F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93f7 CJK Ideograph-93F7 | 93f6 CJK Ideograph-93F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93f9 CJK Ideograph-93F9 | 93f8 CJK Ideograph-93F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93fb CJK Ideograph-93FB | 93fa CJK Ideograph-93FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93fd CJK Ideograph-93FD | 93fc CJK Ideograph-93FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93ff CJK Ideograph-93FF | 93fe CJK Ideograph-93FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9401 CJK Ideograph-9401 | 9400 CJK Ideograph-9400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9403 CJK Ideograph-9403 | 9402 CJK Ideograph-9402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9405 CJK Ideograph-9405 | 9404 CJK Ideograph-9404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9407 CJK Ideograph-9407 | 9406 CJK Ideograph-9406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9409 CJK Ideograph-9409 | 9408 CJK Ideograph-9408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 940b CJK Ideograph-940B | 940a CJK Ideograph-940A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 940d CJK Ideograph-940D | 940c CJK Ideograph-940C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 940f CJK Ideograph-940F | 940e CJK Ideograph-940E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9411 CJK Ideograph-9411 | 9410 CJK Ideograph-9410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9413 CJK Ideograph-9413 | 9412 CJK Ideograph-9412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9415 CJK Ideograph-9415 | 9414 CJK Ideograph-9414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9417 CJK Ideograph-9417 | 9416 CJK Ideograph-9416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9419 CJK Ideograph-9419 | 9418 CJK Ideograph-9418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 941b CJK Ideograph-941B | 941a CJK Ideograph-941A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 941d CJK Ideograph-941D | 941c CJK Ideograph-941C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 941f CJK Ideograph-941F | 941e CJK Ideograph-941E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9421 CJK Ideograph-9421 | 9420 CJK Ideograph-9420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9423 CJK Ideograph-9423 | 9422 CJK Ideograph-9422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9425 CJK Ideograph-9425 | 9424 CJK Ideograph-9424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9427 CJK Ideograph-9427 | 9426 CJK Ideograph-9426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9429 CJK Ideograph-9429 | 9428 CJK Ideograph-9428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 942b CJK Ideograph-942B | 942a CJK Ideograph-942A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 942d CJK Ideograph-942D | 942c CJK Ideograph-942C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 942f CJK Ideograph-942F | 942e CJK Ideograph-942E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9431 CJK Ideograph-9431 | 9430 CJK Ideograph-9430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9433 CJK Ideograph-9433 | 9432 CJK Ideograph-9432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9435 CJK Ideograph-9435 | 9434 CJK Ideograph-9434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9437 CJK Ideograph-9437 | 9436 CJK Ideograph-9436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9439 CJK Ideograph-9439 | 9438 CJK Ideograph-9438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 943b CJK Ideograph-943B | 943a CJK Ideograph-943A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 943d CJK Ideograph-943D | 943c CJK Ideograph-943C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 943f CJK Ideograph-943F | 943e CJK Ideograph-943E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9441 CJK Ideograph-9441 | 9440 CJK Ideograph-9440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9443 CJK Ideograph-9443 | 9442 CJK Ideograph-9442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9445 CJK Ideograph-9445 | 9444 CJK Ideograph-9444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9447 CJK Ideograph-9447 | 9446 CJK Ideograph-9446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9449 CJK Ideograph-9449 | 9448 CJK Ideograph-9448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 944b CJK Ideograph-944B | 944a CJK Ideograph-944A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 944d CJK Ideograph-944D | 944c CJK Ideograph-944C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 944f CJK Ideograph-944F | 944e CJK Ideograph-944E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9451 CJK Ideograph-9451 | 9450 CJK Ideograph-9450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9453 CJK Ideograph-9453 | 9452 CJK Ideograph-9452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9455 CJK Ideograph-9455 | 9454 CJK Ideograph-9454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9457 CJK Ideograph-9457 | 9456 CJK Ideograph-9456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9459 CJK Ideograph-9459 | 9458 CJK Ideograph-9458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 945b CJK Ideograph-945B | 945a CJK Ideograph-945A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 945d CJK Ideograph-945D | 945c CJK Ideograph-945C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 945f CJK Ideograph-945F | 945e CJK Ideograph-945E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9461 CJK Ideograph-9461 | 9460 CJK Ideograph-9460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9463 CJK Ideograph-9463 | 9462 CJK Ideograph-9462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9465 CJK Ideograph-9465 | 9464 CJK Ideograph-9464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9467 CJK Ideograph-9467 | 9466 CJK Ideograph-9466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9469 CJK Ideograph-9469 | 9468 CJK Ideograph-9468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 946b CJK Ideograph-946B | 946a CJK Ideograph-946A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 946d CJK Ideograph-946D | 946c CJK Ideograph-946C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 946f CJK Ideograph-946F | 946e CJK Ideograph-946E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9471 CJK Ideograph-9471 | 9470 CJK Ideograph-9470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9473 CJK Ideograph-9473 | 9472 CJK Ideograph-9472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9475 CJK Ideograph-9475 | 9474 CJK Ideograph-9474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9477 CJK Ideograph-9477 | 9476 CJK Ideograph-9476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9479 CJK Ideograph-9479 | 9478 CJK Ideograph-9478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 947b CJK Ideograph-947B | 947a CJK Ideograph-947A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 947d CJK Ideograph-947D | 947c CJK Ideograph-947C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 947f CJK Ideograph-947F | 947e CJK Ideograph-947E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9481 CJK Ideograph-9481 | 9480 CJK Ideograph-9480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9483 CJK Ideograph-9483 | 9482 CJK Ideograph-9482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9485 CJK Ideograph-9485 | 9484 CJK Ideograph-9484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9487 CJK Ideograph-9487 | 9486 CJK Ideograph-9486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9489 CJK Ideograph-9489 | 9488 CJK Ideograph-9488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 948b CJK Ideograph-948B | 948a CJK Ideograph-948A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 948d CJK Ideograph-948D | 948c CJK Ideograph-948C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 948f CJK Ideograph-948F | 948e CJK Ideograph-948E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9491 CJK Ideograph-9491 | 9490 CJK Ideograph-9490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9493 CJK Ideograph-9493 | 9492 CJK Ideograph-9492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9495 CJK Ideograph-9495 | 9494 CJK Ideograph-9494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9497 CJK Ideograph-9497 | 9496 CJK Ideograph-9496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9499 CJK Ideograph-9499 | 9498 CJK Ideograph-9498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 949b CJK Ideograph-949B | 949a CJK Ideograph-949A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 949d CJK Ideograph-949D | 949c CJK Ideograph-949C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 949f CJK Ideograph-949F | 949e CJK Ideograph-949E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94a1 CJK Ideograph-94A1 | 94a0 CJK Ideograph-94A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94a3 CJK Ideograph-94A3 | 94a2 CJK Ideograph-94A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94a5 CJK Ideograph-94A5 | 94a4 CJK Ideograph-94A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94a7 CJK Ideograph-94A7 | 94a6 CJK Ideograph-94A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94a9 CJK Ideograph-94A9 | 94a8 CJK Ideograph-94A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94ab CJK Ideograph-94AB | 94aa CJK Ideograph-94AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94ad CJK Ideograph-94AD | 94ac CJK Ideograph-94AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94af CJK Ideograph-94AF | 94ae CJK Ideograph-94AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94b1 CJK Ideograph-94B1 | 94b0 CJK Ideograph-94B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94b3 CJK Ideograph-94B3 | 94b2 CJK Ideograph-94B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94b5 CJK Ideograph-94B5 | 94b4 CJK Ideograph-94B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94b7 CJK Ideograph-94B7 | 94b6 CJK Ideograph-94B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94b9 CJK Ideograph-94B9 | 94b8 CJK Ideograph-94B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94bb CJK Ideograph-94BB | 94ba CJK Ideograph-94BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94bd CJK Ideograph-94BD | 94bc CJK Ideograph-94BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94bf CJK Ideograph-94BF | 94be CJK Ideograph-94BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94c1 CJK Ideograph-94C1 | 94c0 CJK Ideograph-94C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94c3 CJK Ideograph-94C3 | 94c2 CJK Ideograph-94C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94c5 CJK Ideograph-94C5 | 94c4 CJK Ideograph-94C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94c7 CJK Ideograph-94C7 | 94c6 CJK Ideograph-94C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94c9 CJK Ideograph-94C9 | 94c8 CJK Ideograph-94C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94cb CJK Ideograph-94CB | 94ca CJK Ideograph-94CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94cd CJK Ideograph-94CD | 94cc CJK Ideograph-94CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94cf CJK Ideograph-94CF | 94ce CJK Ideograph-94CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94d1 CJK Ideograph-94D1 | 94d0 CJK Ideograph-94D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94d3 CJK Ideograph-94D3 | 94d2 CJK Ideograph-94D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94d5 CJK Ideograph-94D5 | 94d4 CJK Ideograph-94D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94d7 CJK Ideograph-94D7 | 94d6 CJK Ideograph-94D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94d9 CJK Ideograph-94D9 | 94d8 CJK Ideograph-94D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94db CJK Ideograph-94DB | 94da CJK Ideograph-94DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94dd CJK Ideograph-94DD | 94dc CJK Ideograph-94DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94df CJK Ideograph-94DF | 94de CJK Ideograph-94DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94e1 CJK Ideograph-94E1 | 94e0 CJK Ideograph-94E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94e3 CJK Ideograph-94E3 | 94e2 CJK Ideograph-94E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94e5 CJK Ideograph-94E5 | 94e4 CJK Ideograph-94E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94e7 CJK Ideograph-94E7 | 94e6 CJK Ideograph-94E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94e9 CJK Ideograph-94E9 | 94e8 CJK Ideograph-94E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94eb CJK Ideograph-94EB | 94ea CJK Ideograph-94EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94ed CJK Ideograph-94ED | 94ec CJK Ideograph-94EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94ef CJK Ideograph-94EF | 94ee CJK Ideograph-94EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94f1 CJK Ideograph-94F1 | 94f0 CJK Ideograph-94F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94f3 CJK Ideograph-94F3 | 94f2 CJK Ideograph-94F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94f5 CJK Ideograph-94F5 | 94f4 CJK Ideograph-94F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94f7 CJK Ideograph-94F7 | 94f6 CJK Ideograph-94F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94f9 CJK Ideograph-94F9 | 94f8 CJK Ideograph-94F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94fb CJK Ideograph-94FB | 94fa CJK Ideograph-94FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94fd CJK Ideograph-94FD | 94fc CJK Ideograph-94FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94ff CJK Ideograph-94FF | 94fe CJK Ideograph-94FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9501 CJK Ideograph-9501 | 9500 CJK Ideograph-9500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9503 CJK Ideograph-9503 | 9502 CJK Ideograph-9502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9505 CJK Ideograph-9505 | 9504 CJK Ideograph-9504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9507 CJK Ideograph-9507 | 9506 CJK Ideograph-9506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9509 CJK Ideograph-9509 | 9508 CJK Ideograph-9508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 950b CJK Ideograph-950B | 950a CJK Ideograph-950A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 950d CJK Ideograph-950D | 950c CJK Ideograph-950C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 950f CJK Ideograph-950F | 950e CJK Ideograph-950E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9511 CJK Ideograph-9511 | 9510 CJK Ideograph-9510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9513 CJK Ideograph-9513 | 9512 CJK Ideograph-9512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9515 CJK Ideograph-9515 | 9514 CJK Ideograph-9514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9517 CJK Ideograph-9517 | 9516 CJK Ideograph-9516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9519 CJK Ideograph-9519 | 9518 CJK Ideograph-9518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 951b CJK Ideograph-951B | 951a CJK Ideograph-951A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 951d CJK Ideograph-951D | 951c CJK Ideograph-951C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 951f CJK Ideograph-951F | 951e CJK Ideograph-951E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9521 CJK Ideograph-9521 | 9520 CJK Ideograph-9520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9523 CJK Ideograph-9523 | 9522 CJK Ideograph-9522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9525 CJK Ideograph-9525 | 9524 CJK Ideograph-9524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9527 CJK Ideograph-9527 | 9526 CJK Ideograph-9526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9529 CJK Ideograph-9529 | 9528 CJK Ideograph-9528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 952b CJK Ideograph-952B | 952a CJK Ideograph-952A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 952d CJK Ideograph-952D | 952c CJK Ideograph-952C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 952f CJK Ideograph-952F | 952e CJK Ideograph-952E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9531 CJK Ideograph-9531 | 9530 CJK Ideograph-9530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9533 CJK Ideograph-9533 | 9532 CJK Ideograph-9532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9535 CJK Ideograph-9535 | 9534 CJK Ideograph-9534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9537 CJK Ideograph-9537 | 9536 CJK Ideograph-9536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9539 CJK Ideograph-9539 | 9538 CJK Ideograph-9538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 953b CJK Ideograph-953B | 953a CJK Ideograph-953A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 953d CJK Ideograph-953D | 953c CJK Ideograph-953C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 953f CJK Ideograph-953F | 953e CJK Ideograph-953E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9541 CJK Ideograph-9541 | 9540 CJK Ideograph-9540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9543 CJK Ideograph-9543 | 9542 CJK Ideograph-9542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9545 CJK Ideograph-9545 | 9544 CJK Ideograph-9544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9547 CJK Ideograph-9547 | 9546 CJK Ideograph-9546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9549 CJK Ideograph-9549 | 9548 CJK Ideograph-9548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 954b CJK Ideograph-954B | 954a CJK Ideograph-954A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 954d CJK Ideograph-954D | 954c CJK Ideograph-954C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 954f CJK Ideograph-954F | 954e CJK Ideograph-954E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9551 CJK Ideograph-9551 | 9550 CJK Ideograph-9550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9553 CJK Ideograph-9553 | 9552 CJK Ideograph-9552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9555 CJK Ideograph-9555 | 9554 CJK Ideograph-9554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9557 CJK Ideograph-9557 | 9556 CJK Ideograph-9556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9559 CJK Ideograph-9559 | 9558 CJK Ideograph-9558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 955b CJK Ideograph-955B | 955a CJK Ideograph-955A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 955d CJK Ideograph-955D | 955c CJK Ideograph-955C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 955f CJK Ideograph-955F | 955e CJK Ideograph-955E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9561 CJK Ideograph-9561 | 9560 CJK Ideograph-9560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9563 CJK Ideograph-9563 | 9562 CJK Ideograph-9562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9565 CJK Ideograph-9565 | 9564 CJK Ideograph-9564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9567 CJK Ideograph-9567 | 9566 CJK Ideograph-9566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9569 CJK Ideograph-9569 | 9568 CJK Ideograph-9568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 956b CJK Ideograph-956B | 956a CJK Ideograph-956A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 956d CJK Ideograph-956D | 956c CJK Ideograph-956C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 956f CJK Ideograph-956F | 956e CJK Ideograph-956E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9571 CJK Ideograph-9571 | 9570 CJK Ideograph-9570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9573 CJK Ideograph-9573 | 9572 CJK Ideograph-9572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9575 CJK Ideograph-9575 | 9574 CJK Ideograph-9574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9577 CJK Ideograph-9577 | 9576 CJK Ideograph-9576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9579 CJK Ideograph-9579 | 9578 CJK Ideograph-9578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 957b CJK Ideograph-957B | 957a CJK Ideograph-957A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 957d CJK Ideograph-957D | 957c CJK Ideograph-957C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 957f CJK Ideograph-957F | 957e CJK Ideograph-957E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9581 CJK Ideograph-9581 | 9580 CJK Ideograph-9580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9583 CJK Ideograph-9583 | 9582 CJK Ideograph-9582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9585 CJK Ideograph-9585 | 9584 CJK Ideograph-9584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9587 CJK Ideograph-9587 | 9586 CJK Ideograph-9586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9589 CJK Ideograph-9589 | 9588 CJK Ideograph-9588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 958b CJK Ideograph-958B | 958a CJK Ideograph-958A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 958d CJK Ideograph-958D | 958c CJK Ideograph-958C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 958f CJK Ideograph-958F | 958e CJK Ideograph-958E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9591 CJK Ideograph-9591 | 9590 CJK Ideograph-9590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9593 CJK Ideograph-9593 | 9592 CJK Ideograph-9592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9595 CJK Ideograph-9595 | 9594 CJK Ideograph-9594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9597 CJK Ideograph-9597 | 9596 CJK Ideograph-9596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9599 CJK Ideograph-9599 | 9598 CJK Ideograph-9598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 959b CJK Ideograph-959B | 959a CJK Ideograph-959A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 959d CJK Ideograph-959D | 959c CJK Ideograph-959C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 959f CJK Ideograph-959F | 959e CJK Ideograph-959E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95a1 CJK Ideograph-95A1 | 95a0 CJK Ideograph-95A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95a3 CJK Ideograph-95A3 | 95a2 CJK Ideograph-95A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95a5 CJK Ideograph-95A5 | 95a4 CJK Ideograph-95A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95a7 CJK Ideograph-95A7 | 95a6 CJK Ideograph-95A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95a9 CJK Ideograph-95A9 | 95a8 CJK Ideograph-95A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95ab CJK Ideograph-95AB | 95aa CJK Ideograph-95AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95ad CJK Ideograph-95AD | 95ac CJK Ideograph-95AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95af CJK Ideograph-95AF | 95ae CJK Ideograph-95AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95b1 CJK Ideograph-95B1 | 95b0 CJK Ideograph-95B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95b3 CJK Ideograph-95B3 | 95b2 CJK Ideograph-95B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95b5 CJK Ideograph-95B5 | 95b4 CJK Ideograph-95B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95b7 CJK Ideograph-95B7 | 95b6 CJK Ideograph-95B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95b9 CJK Ideograph-95B9 | 95b8 CJK Ideograph-95B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95bb CJK Ideograph-95BB | 95ba CJK Ideograph-95BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95bd CJK Ideograph-95BD | 95bc CJK Ideograph-95BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95bf CJK Ideograph-95BF | 95be CJK Ideograph-95BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95c1 CJK Ideograph-95C1 | 95c0 CJK Ideograph-95C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95c3 CJK Ideograph-95C3 | 95c2 CJK Ideograph-95C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95c5 CJK Ideograph-95C5 | 95c4 CJK Ideograph-95C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95c7 CJK Ideograph-95C7 | 95c6 CJK Ideograph-95C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95c9 CJK Ideograph-95C9 | 95c8 CJK Ideograph-95C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95cb CJK Ideograph-95CB | 95ca CJK Ideograph-95CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95cd CJK Ideograph-95CD | 95cc CJK Ideograph-95CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95cf CJK Ideograph-95CF | 95ce CJK Ideograph-95CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95d1 CJK Ideograph-95D1 | 95d0 CJK Ideograph-95D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95d3 CJK Ideograph-95D3 | 95d2 CJK Ideograph-95D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95d5 CJK Ideograph-95D5 | 95d4 CJK Ideograph-95D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95d7 CJK Ideograph-95D7 | 95d6 CJK Ideograph-95D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95d9 CJK Ideograph-95D9 | 95d8 CJK Ideograph-95D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95db CJK Ideograph-95DB | 95da CJK Ideograph-95DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95dd CJK Ideograph-95DD | 95dc CJK Ideograph-95DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95df CJK Ideograph-95DF | 95de CJK Ideograph-95DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95e1 CJK Ideograph-95E1 | 95e0 CJK Ideograph-95E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95e3 CJK Ideograph-95E3 | 95e2 CJK Ideograph-95E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95e5 CJK Ideograph-95E5 | 95e4 CJK Ideograph-95E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95e7 CJK Ideograph-95E7 | 95e6 CJK Ideograph-95E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95e9 CJK Ideograph-95E9 | 95e8 CJK Ideograph-95E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95eb CJK Ideograph-95EB | 95ea CJK Ideograph-95EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95ed CJK Ideograph-95ED | 95ec CJK Ideograph-95EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95ef CJK Ideograph-95EF | 95ee CJK Ideograph-95EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95f1 CJK Ideograph-95F1 | 95f0 CJK Ideograph-95F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95f3 CJK Ideograph-95F3 | 95f2 CJK Ideograph-95F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95f5 CJK Ideograph-95F5 | 95f4 CJK Ideograph-95F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95f7 CJK Ideograph-95F7 | 95f6 CJK Ideograph-95F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95f9 CJK Ideograph-95F9 | 95f8 CJK Ideograph-95F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95fb CJK Ideograph-95FB | 95fa CJK Ideograph-95FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95fd CJK Ideograph-95FD | 95fc CJK Ideograph-95FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95ff CJK Ideograph-95FF | 95fe CJK Ideograph-95FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9601 CJK Ideograph-9601 | 9600 CJK Ideograph-9600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9603 CJK Ideograph-9603 | 9602 CJK Ideograph-9602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9605 CJK Ideograph-9605 | 9604 CJK Ideograph-9604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9607 CJK Ideograph-9607 | 9606 CJK Ideograph-9606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9609 CJK Ideograph-9609 | 9608 CJK Ideograph-9608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 960b CJK Ideograph-960B | 960a CJK Ideograph-960A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 960d CJK Ideograph-960D | 960c CJK Ideograph-960C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 960f CJK Ideograph-960F | 960e CJK Ideograph-960E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9611 CJK Ideograph-9611 | 9610 CJK Ideograph-9610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9613 CJK Ideograph-9613 | 9612 CJK Ideograph-9612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9615 CJK Ideograph-9615 | 9614 CJK Ideograph-9614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9617 CJK Ideograph-9617 | 9616 CJK Ideograph-9616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9619 CJK Ideograph-9619 | 9618 CJK Ideograph-9618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 961b CJK Ideograph-961B | 961a CJK Ideograph-961A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 961d CJK Ideograph-961D | 961c CJK Ideograph-961C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 961f CJK Ideograph-961F | 961e CJK Ideograph-961E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9621 CJK Ideograph-9621 | 9620 CJK Ideograph-9620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9623 CJK Ideograph-9623 | 9622 CJK Ideograph-9622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9625 CJK Ideograph-9625 | 9624 CJK Ideograph-9624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9627 CJK Ideograph-9627 | 9626 CJK Ideograph-9626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9629 CJK Ideograph-9629 | 9628 CJK Ideograph-9628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 962b CJK Ideograph-962B | 962a CJK Ideograph-962A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 962d CJK Ideograph-962D | 962c CJK Ideograph-962C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 962f CJK Ideograph-962F | 962e CJK Ideograph-962E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9631 CJK Ideograph-9631 | 9630 CJK Ideograph-9630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9633 CJK Ideograph-9633 | 9632 CJK Ideograph-9632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9635 CJK Ideograph-9635 | 9634 CJK Ideograph-9634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9637 CJK Ideograph-9637 | 9636 CJK Ideograph-9636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9639 CJK Ideograph-9639 | 9638 CJK Ideograph-9638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 963b CJK Ideograph-963B | 963a CJK Ideograph-963A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 963d CJK Ideograph-963D | 963c CJK Ideograph-963C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 963f CJK Ideograph-963F | 963e CJK Ideograph-963E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9641 CJK Ideograph-9641 | 9640 CJK Ideograph-9640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9643 CJK Ideograph-9643 | 9642 CJK Ideograph-9642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9645 CJK Ideograph-9645 | 9644 CJK Ideograph-9644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9647 CJK Ideograph-9647 | 9646 CJK Ideograph-9646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9649 CJK Ideograph-9649 | 9648 CJK Ideograph-9648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 964b CJK Ideograph-964B | 964a CJK Ideograph-964A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 964d CJK Ideograph-964D | 964c CJK Ideograph-964C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 964f CJK Ideograph-964F | 964e CJK Ideograph-964E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9651 CJK Ideograph-9651 | 9650 CJK Ideograph-9650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9653 CJK Ideograph-9653 | 9652 CJK Ideograph-9652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9655 CJK Ideograph-9655 | 9654 CJK Ideograph-9654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9657 CJK Ideograph-9657 | 9656 CJK Ideograph-9656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9659 CJK Ideograph-9659 | 9658 CJK Ideograph-9658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 965b CJK Ideograph-965B | 965a CJK Ideograph-965A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 965d CJK Ideograph-965D | 965c CJK Ideograph-965C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 965f CJK Ideograph-965F | 965e CJK Ideograph-965E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9661 CJK Ideograph-9661 | 9660 CJK Ideograph-9660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9663 CJK Ideograph-9663 | 9662 CJK Ideograph-9662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9665 CJK Ideograph-9665 | 9664 CJK Ideograph-9664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9667 CJK Ideograph-9667 | 9666 CJK Ideograph-9666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9669 CJK Ideograph-9669 | 9668 CJK Ideograph-9668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 966b CJK Ideograph-966B | 966a CJK Ideograph-966A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 966d CJK Ideograph-966D | 966c CJK Ideograph-966C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 966f CJK Ideograph-966F | 966e CJK Ideograph-966E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9671 CJK Ideograph-9671 | 9670 CJK Ideograph-9670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9673 CJK Ideograph-9673 | 9672 CJK Ideograph-9672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9675 CJK Ideograph-9675 | 9674 CJK Ideograph-9674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9677 CJK Ideograph-9677 | 9676 CJK Ideograph-9676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9679 CJK Ideograph-9679 | 9678 CJK Ideograph-9678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 967b CJK Ideograph-967B | 967a CJK Ideograph-967A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 967d CJK Ideograph-967D | 967c CJK Ideograph-967C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 967f CJK Ideograph-967F | 967e CJK Ideograph-967E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9681 CJK Ideograph-9681 | 9680 CJK Ideograph-9680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9683 CJK Ideograph-9683 | 9682 CJK Ideograph-9682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9685 CJK Ideograph-9685 | 9684 CJK Ideograph-9684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9687 CJK Ideograph-9687 | 9686 CJK Ideograph-9686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9689 CJK Ideograph-9689 | 9688 CJK Ideograph-9688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 968b CJK Ideograph-968B | 968a CJK Ideograph-968A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 968d CJK Ideograph-968D | 968c CJK Ideograph-968C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 968f CJK Ideograph-968F | 968e CJK Ideograph-968E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9691 CJK Ideograph-9691 | 9690 CJK Ideograph-9690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9693 CJK Ideograph-9693 | 9692 CJK Ideograph-9692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9695 CJK Ideograph-9695 | 9694 CJK Ideograph-9694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9697 CJK Ideograph-9697 | 9696 CJK Ideograph-9696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9699 CJK Ideograph-9699 | 9698 CJK Ideograph-9698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 969b CJK Ideograph-969B | 969a CJK Ideograph-969A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 969d CJK Ideograph-969D | 969c CJK Ideograph-969C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 969f CJK Ideograph-969F | 969e CJK Ideograph-969E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96a1 CJK Ideograph-96A1 | 96a0 CJK Ideograph-96A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96a3 CJK Ideograph-96A3 | 96a2 CJK Ideograph-96A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96a5 CJK Ideograph-96A5 | 96a4 CJK Ideograph-96A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96a7 CJK Ideograph-96A7 | 96a6 CJK Ideograph-96A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96a9 CJK Ideograph-96A9 | 96a8 CJK Ideograph-96A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96ab CJK Ideograph-96AB | 96aa CJK Ideograph-96AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96ad CJK Ideograph-96AD | 96ac CJK Ideograph-96AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96af CJK Ideograph-96AF | 96ae CJK Ideograph-96AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96b1 CJK Ideograph-96B1 | 96b0 CJK Ideograph-96B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96b3 CJK Ideograph-96B3 | 96b2 CJK Ideograph-96B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96b5 CJK Ideograph-96B5 | 96b4 CJK Ideograph-96B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96b7 CJK Ideograph-96B7 | 96b6 CJK Ideograph-96B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96b9 CJK Ideograph-96B9 | 96b8 CJK Ideograph-96B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96bb CJK Ideograph-96BB | 96ba CJK Ideograph-96BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96bd CJK Ideograph-96BD | 96bc CJK Ideograph-96BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96bf CJK Ideograph-96BF | 96be CJK Ideograph-96BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96c1 CJK Ideograph-96C1 | 96c0 CJK Ideograph-96C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96c3 CJK Ideograph-96C3 | 96c2 CJK Ideograph-96C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96c5 CJK Ideograph-96C5 | 96c4 CJK Ideograph-96C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96c7 CJK Ideograph-96C7 | 96c6 CJK Ideograph-96C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96c9 CJK Ideograph-96C9 | 96c8 CJK Ideograph-96C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96cb CJK Ideograph-96CB | 96ca CJK Ideograph-96CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96cd CJK Ideograph-96CD | 96cc CJK Ideograph-96CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96cf CJK Ideograph-96CF | 96ce CJK Ideograph-96CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96d1 CJK Ideograph-96D1 | 96d0 CJK Ideograph-96D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96d3 CJK Ideograph-96D3 | 96d2 CJK Ideograph-96D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96d5 CJK Ideograph-96D5 | 96d4 CJK Ideograph-96D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96d7 CJK Ideograph-96D7 | 96d6 CJK Ideograph-96D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96d9 CJK Ideograph-96D9 | 96d8 CJK Ideograph-96D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96db CJK Ideograph-96DB | 96da CJK Ideograph-96DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96dd CJK Ideograph-96DD | 96dc CJK Ideograph-96DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96df CJK Ideograph-96DF | 96de CJK Ideograph-96DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96e1 CJK Ideograph-96E1 | 96e0 CJK Ideograph-96E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96e3 CJK Ideograph-96E3 | 96e2 CJK Ideograph-96E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96e5 CJK Ideograph-96E5 | 96e4 CJK Ideograph-96E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96e7 CJK Ideograph-96E7 | 96e6 CJK Ideograph-96E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96e9 CJK Ideograph-96E9 | 96e8 CJK Ideograph-96E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96eb CJK Ideograph-96EB | 96ea CJK Ideograph-96EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96ed CJK Ideograph-96ED | 96ec CJK Ideograph-96EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96ef CJK Ideograph-96EF | 96ee CJK Ideograph-96EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96f1 CJK Ideograph-96F1 | 96f0 CJK Ideograph-96F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96f3 CJK Ideograph-96F3 | 96f2 CJK Ideograph-96F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96f5 CJK Ideograph-96F5 | 96f4 CJK Ideograph-96F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96f7 CJK Ideograph-96F7 | 96f6 CJK Ideograph-96F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96f9 CJK Ideograph-96F9 | 96f8 CJK Ideograph-96F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96fb CJK Ideograph-96FB | 96fa CJK Ideograph-96FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96fd CJK Ideograph-96FD | 96fc CJK Ideograph-96FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96ff CJK Ideograph-96FF | 96fe CJK Ideograph-96FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9701 CJK Ideograph-9701 | 9700 CJK Ideograph-9700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9703 CJK Ideograph-9703 | 9702 CJK Ideograph-9702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9705 CJK Ideograph-9705 | 9704 CJK Ideograph-9704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9707 CJK Ideograph-9707 | 9706 CJK Ideograph-9706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9709 CJK Ideograph-9709 | 9708 CJK Ideograph-9708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 970b CJK Ideograph-970B | 970a CJK Ideograph-970A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 970d CJK Ideograph-970D | 970c CJK Ideograph-970C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 970f CJK Ideograph-970F | 970e CJK Ideograph-970E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9711 CJK Ideograph-9711 | 9710 CJK Ideograph-9710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9713 CJK Ideograph-9713 | 9712 CJK Ideograph-9712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9715 CJK Ideograph-9715 | 9714 CJK Ideograph-9714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9717 CJK Ideograph-9717 | 9716 CJK Ideograph-9716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9719 CJK Ideograph-9719 | 9718 CJK Ideograph-9718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 971b CJK Ideograph-971B | 971a CJK Ideograph-971A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 971d CJK Ideograph-971D | 971c CJK Ideograph-971C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 971f CJK Ideograph-971F | 971e CJK Ideograph-971E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9721 CJK Ideograph-9721 | 9720 CJK Ideograph-9720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9723 CJK Ideograph-9723 | 9722 CJK Ideograph-9722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9725 CJK Ideograph-9725 | 9724 CJK Ideograph-9724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9727 CJK Ideograph-9727 | 9726 CJK Ideograph-9726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9729 CJK Ideograph-9729 | 9728 CJK Ideograph-9728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 972b CJK Ideograph-972B | 972a CJK Ideograph-972A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 972d CJK Ideograph-972D | 972c CJK Ideograph-972C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 972f CJK Ideograph-972F | 972e CJK Ideograph-972E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9731 CJK Ideograph-9731 | 9730 CJK Ideograph-9730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9733 CJK Ideograph-9733 | 9732 CJK Ideograph-9732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9735 CJK Ideograph-9735 | 9734 CJK Ideograph-9734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9737 CJK Ideograph-9737 | 9736 CJK Ideograph-9736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9739 CJK Ideograph-9739 | 9738 CJK Ideograph-9738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 973b CJK Ideograph-973B | 973a CJK Ideograph-973A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 973d CJK Ideograph-973D | 973c CJK Ideograph-973C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 973f CJK Ideograph-973F | 973e CJK Ideograph-973E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9741 CJK Ideograph-9741 | 9740 CJK Ideograph-9740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9743 CJK Ideograph-9743 | 9742 CJK Ideograph-9742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9745 CJK Ideograph-9745 | 9744 CJK Ideograph-9744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9747 CJK Ideograph-9747 | 9746 CJK Ideograph-9746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9749 CJK Ideograph-9749 | 9748 CJK Ideograph-9748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 974b CJK Ideograph-974B | 974a CJK Ideograph-974A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 974d CJK Ideograph-974D | 974c CJK Ideograph-974C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 974f CJK Ideograph-974F | 974e CJK Ideograph-974E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9751 CJK Ideograph-9751 | 9750 CJK Ideograph-9750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9753 CJK Ideograph-9753 | 9752 CJK Ideograph-9752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9755 CJK Ideograph-9755 | 9754 CJK Ideograph-9754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9757 CJK Ideograph-9757 | 9756 CJK Ideograph-9756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9759 CJK Ideograph-9759 | 9758 CJK Ideograph-9758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 975b CJK Ideograph-975B | 975a CJK Ideograph-975A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 975d CJK Ideograph-975D | 975c CJK Ideograph-975C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 975f CJK Ideograph-975F | 975e CJK Ideograph-975E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9761 CJK Ideograph-9761 | 9760 CJK Ideograph-9760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9763 CJK Ideograph-9763 | 9762 CJK Ideograph-9762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9765 CJK Ideograph-9765 | 9764 CJK Ideograph-9764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9767 CJK Ideograph-9767 | 9766 CJK Ideograph-9766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9769 CJK Ideograph-9769 | 9768 CJK Ideograph-9768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 976b CJK Ideograph-976B | 976a CJK Ideograph-976A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 976d CJK Ideograph-976D | 976c CJK Ideograph-976C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 976f CJK Ideograph-976F | 976e CJK Ideograph-976E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9771 CJK Ideograph-9771 | 9770 CJK Ideograph-9770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9773 CJK Ideograph-9773 | 9772 CJK Ideograph-9772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9775 CJK Ideograph-9775 | 9774 CJK Ideograph-9774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9777 CJK Ideograph-9777 | 9776 CJK Ideograph-9776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9779 CJK Ideograph-9779 | 9778 CJK Ideograph-9778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 977b CJK Ideograph-977B | 977a CJK Ideograph-977A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 977d CJK Ideograph-977D | 977c CJK Ideograph-977C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 977f CJK Ideograph-977F | 977e CJK Ideograph-977E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9781 CJK Ideograph-9781 | 9780 CJK Ideograph-9780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9783 CJK Ideograph-9783 | 9782 CJK Ideograph-9782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9785 CJK Ideograph-9785 | 9784 CJK Ideograph-9784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9787 CJK Ideograph-9787 | 9786 CJK Ideograph-9786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9789 CJK Ideograph-9789 | 9788 CJK Ideograph-9788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 978b CJK Ideograph-978B | 978a CJK Ideograph-978A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 978d CJK Ideograph-978D | 978c CJK Ideograph-978C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 978f CJK Ideograph-978F | 978e CJK Ideograph-978E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9791 CJK Ideograph-9791 | 9790 CJK Ideograph-9790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9793 CJK Ideograph-9793 | 9792 CJK Ideograph-9792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9795 CJK Ideograph-9795 | 9794 CJK Ideograph-9794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9797 CJK Ideograph-9797 | 9796 CJK Ideograph-9796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9799 CJK Ideograph-9799 | 9798 CJK Ideograph-9798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 979b CJK Ideograph-979B | 979a CJK Ideograph-979A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 979d CJK Ideograph-979D | 979c CJK Ideograph-979C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 979f CJK Ideograph-979F | 979e CJK Ideograph-979E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97a1 CJK Ideograph-97A1 | 97a0 CJK Ideograph-97A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97a3 CJK Ideograph-97A3 | 97a2 CJK Ideograph-97A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97a5 CJK Ideograph-97A5 | 97a4 CJK Ideograph-97A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97a7 CJK Ideograph-97A7 | 97a6 CJK Ideograph-97A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97a9 CJK Ideograph-97A9 | 97a8 CJK Ideograph-97A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97ab CJK Ideograph-97AB | 97aa CJK Ideograph-97AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97ad CJK Ideograph-97AD | 97ac CJK Ideograph-97AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97af CJK Ideograph-97AF | 97ae CJK Ideograph-97AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97b1 CJK Ideograph-97B1 | 97b0 CJK Ideograph-97B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97b3 CJK Ideograph-97B3 | 97b2 CJK Ideograph-97B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97b5 CJK Ideograph-97B5 | 97b4 CJK Ideograph-97B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97b7 CJK Ideograph-97B7 | 97b6 CJK Ideograph-97B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97b9 CJK Ideograph-97B9 | 97b8 CJK Ideograph-97B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97bb CJK Ideograph-97BB | 97ba CJK Ideograph-97BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97bd CJK Ideograph-97BD | 97bc CJK Ideograph-97BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97bf CJK Ideograph-97BF | 97be CJK Ideograph-97BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97c1 CJK Ideograph-97C1 | 97c0 CJK Ideograph-97C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97c3 CJK Ideograph-97C3 | 97c2 CJK Ideograph-97C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97c5 CJK Ideograph-97C5 | 97c4 CJK Ideograph-97C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97c7 CJK Ideograph-97C7 | 97c6 CJK Ideograph-97C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97c9 CJK Ideograph-97C9 | 97c8 CJK Ideograph-97C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97cb CJK Ideograph-97CB | 97ca CJK Ideograph-97CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97cd CJK Ideograph-97CD | 97cc CJK Ideograph-97CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97cf CJK Ideograph-97CF | 97ce CJK Ideograph-97CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97d1 CJK Ideograph-97D1 | 97d0 CJK Ideograph-97D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97d3 CJK Ideograph-97D3 | 97d2 CJK Ideograph-97D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97d5 CJK Ideograph-97D5 | 97d4 CJK Ideograph-97D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97d7 CJK Ideograph-97D7 | 97d6 CJK Ideograph-97D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97d9 CJK Ideograph-97D9 | 97d8 CJK Ideograph-97D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97db CJK Ideograph-97DB | 97da CJK Ideograph-97DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97dd CJK Ideograph-97DD | 97dc CJK Ideograph-97DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97df CJK Ideograph-97DF | 97de CJK Ideograph-97DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97e1 CJK Ideograph-97E1 | 97e0 CJK Ideograph-97E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97e3 CJK Ideograph-97E3 | 97e2 CJK Ideograph-97E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97e5 CJK Ideograph-97E5 | 97e4 CJK Ideograph-97E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97e7 CJK Ideograph-97E7 | 97e6 CJK Ideograph-97E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97e9 CJK Ideograph-97E9 | 97e8 CJK Ideograph-97E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97eb CJK Ideograph-97EB | 97ea CJK Ideograph-97EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97ed CJK Ideograph-97ED | 97ec CJK Ideograph-97EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97ef CJK Ideograph-97EF | 97ee CJK Ideograph-97EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97f1 CJK Ideograph-97F1 | 97f0 CJK Ideograph-97F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97f3 CJK Ideograph-97F3 | 97f2 CJK Ideograph-97F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97f5 CJK Ideograph-97F5 | 97f4 CJK Ideograph-97F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97f7 CJK Ideograph-97F7 | 97f6 CJK Ideograph-97F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97f9 CJK Ideograph-97F9 | 97f8 CJK Ideograph-97F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97fb CJK Ideograph-97FB | 97fa CJK Ideograph-97FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97fd CJK Ideograph-97FD | 97fc CJK Ideograph-97FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97ff CJK Ideograph-97FF | 97fe CJK Ideograph-97FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9801 CJK Ideograph-9801 | 9800 CJK Ideograph-9800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9803 CJK Ideograph-9803 | 9802 CJK Ideograph-9802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9805 CJK Ideograph-9805 | 9804 CJK Ideograph-9804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9807 CJK Ideograph-9807 | 9806 CJK Ideograph-9806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9809 CJK Ideograph-9809 | 9808 CJK Ideograph-9808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 980b CJK Ideograph-980B | 980a CJK Ideograph-980A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 980d CJK Ideograph-980D | 980c CJK Ideograph-980C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 980f CJK Ideograph-980F | 980e CJK Ideograph-980E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9811 CJK Ideograph-9811 | 9810 CJK Ideograph-9810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9813 CJK Ideograph-9813 | 9812 CJK Ideograph-9812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9815 CJK Ideograph-9815 | 9814 CJK Ideograph-9814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9817 CJK Ideograph-9817 | 9816 CJK Ideograph-9816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9819 CJK Ideograph-9819 | 9818 CJK Ideograph-9818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 981b CJK Ideograph-981B | 981a CJK Ideograph-981A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 981d CJK Ideograph-981D | 981c CJK Ideograph-981C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 981f CJK Ideograph-981F | 981e CJK Ideograph-981E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9821 CJK Ideograph-9821 | 9820 CJK Ideograph-9820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9823 CJK Ideograph-9823 | 9822 CJK Ideograph-9822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9825 CJK Ideograph-9825 | 9824 CJK Ideograph-9824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9827 CJK Ideograph-9827 | 9826 CJK Ideograph-9826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9829 CJK Ideograph-9829 | 9828 CJK Ideograph-9828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 982b CJK Ideograph-982B | 982a CJK Ideograph-982A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 982d CJK Ideograph-982D | 982c CJK Ideograph-982C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 982f CJK Ideograph-982F | 982e CJK Ideograph-982E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9831 CJK Ideograph-9831 | 9830 CJK Ideograph-9830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9833 CJK Ideograph-9833 | 9832 CJK Ideograph-9832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9835 CJK Ideograph-9835 | 9834 CJK Ideograph-9834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9837 CJK Ideograph-9837 | 9836 CJK Ideograph-9836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9839 CJK Ideograph-9839 | 9838 CJK Ideograph-9838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 983b CJK Ideograph-983B | 983a CJK Ideograph-983A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 983d CJK Ideograph-983D | 983c CJK Ideograph-983C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 983f CJK Ideograph-983F | 983e CJK Ideograph-983E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9841 CJK Ideograph-9841 | 9840 CJK Ideograph-9840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9843 CJK Ideograph-9843 | 9842 CJK Ideograph-9842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9845 CJK Ideograph-9845 | 9844 CJK Ideograph-9844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9847 CJK Ideograph-9847 | 9846 CJK Ideograph-9846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9849 CJK Ideograph-9849 | 9848 CJK Ideograph-9848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 984b CJK Ideograph-984B | 984a CJK Ideograph-984A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 984d CJK Ideograph-984D | 984c CJK Ideograph-984C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 984f CJK Ideograph-984F | 984e CJK Ideograph-984E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9851 CJK Ideograph-9851 | 9850 CJK Ideograph-9850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9853 CJK Ideograph-9853 | 9852 CJK Ideograph-9852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9855 CJK Ideograph-9855 | 9854 CJK Ideograph-9854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9857 CJK Ideograph-9857 | 9856 CJK Ideograph-9856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9859 CJK Ideograph-9859 | 9858 CJK Ideograph-9858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 985b CJK Ideograph-985B | 985a CJK Ideograph-985A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 985d CJK Ideograph-985D | 985c CJK Ideograph-985C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 985f CJK Ideograph-985F | 985e CJK Ideograph-985E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9861 CJK Ideograph-9861 | 9860 CJK Ideograph-9860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9863 CJK Ideograph-9863 | 9862 CJK Ideograph-9862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9865 CJK Ideograph-9865 | 9864 CJK Ideograph-9864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9867 CJK Ideograph-9867 | 9866 CJK Ideograph-9866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9869 CJK Ideograph-9869 | 9868 CJK Ideograph-9868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 986b CJK Ideograph-986B | 986a CJK Ideograph-986A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 986d CJK Ideograph-986D | 986c CJK Ideograph-986C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 986f CJK Ideograph-986F | 986e CJK Ideograph-986E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9871 CJK Ideograph-9871 | 9870 CJK Ideograph-9870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9873 CJK Ideograph-9873 | 9872 CJK Ideograph-9872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9875 CJK Ideograph-9875 | 9874 CJK Ideograph-9874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9877 CJK Ideograph-9877 | 9876 CJK Ideograph-9876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9879 CJK Ideograph-9879 | 9878 CJK Ideograph-9878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 987b CJK Ideograph-987B | 987a CJK Ideograph-987A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 987d CJK Ideograph-987D | 987c CJK Ideograph-987C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 987f CJK Ideograph-987F | 987e CJK Ideograph-987E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9881 CJK Ideograph-9881 | 9880 CJK Ideograph-9880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9883 CJK Ideograph-9883 | 9882 CJK Ideograph-9882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9885 CJK Ideograph-9885 | 9884 CJK Ideograph-9884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9887 CJK Ideograph-9887 | 9886 CJK Ideograph-9886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9889 CJK Ideograph-9889 | 9888 CJK Ideograph-9888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 988b CJK Ideograph-988B | 988a CJK Ideograph-988A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 988d CJK Ideograph-988D | 988c CJK Ideograph-988C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 988f CJK Ideograph-988F | 988e CJK Ideograph-988E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9891 CJK Ideograph-9891 | 9890 CJK Ideograph-9890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9893 CJK Ideograph-9893 | 9892 CJK Ideograph-9892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9895 CJK Ideograph-9895 | 9894 CJK Ideograph-9894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9897 CJK Ideograph-9897 | 9896 CJK Ideograph-9896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9899 CJK Ideograph-9899 | 9898 CJK Ideograph-9898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 989b CJK Ideograph-989B | 989a CJK Ideograph-989A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 989d CJK Ideograph-989D | 989c CJK Ideograph-989C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 989f CJK Ideograph-989F | 989e CJK Ideograph-989E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98a1 CJK Ideograph-98A1 | 98a0 CJK Ideograph-98A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98a3 CJK Ideograph-98A3 | 98a2 CJK Ideograph-98A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98a5 CJK Ideograph-98A5 | 98a4 CJK Ideograph-98A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98a7 CJK Ideograph-98A7 | 98a6 CJK Ideograph-98A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98a9 CJK Ideograph-98A9 | 98a8 CJK Ideograph-98A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98ab CJK Ideograph-98AB | 98aa CJK Ideograph-98AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98ad CJK Ideograph-98AD | 98ac CJK Ideograph-98AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98af CJK Ideograph-98AF | 98ae CJK Ideograph-98AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98b1 CJK Ideograph-98B1 | 98b0 CJK Ideograph-98B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98b3 CJK Ideograph-98B3 | 98b2 CJK Ideograph-98B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98b5 CJK Ideograph-98B5 | 98b4 CJK Ideograph-98B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98b7 CJK Ideograph-98B7 | 98b6 CJK Ideograph-98B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98b9 CJK Ideograph-98B9 | 98b8 CJK Ideograph-98B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98bb CJK Ideograph-98BB | 98ba CJK Ideograph-98BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98bd CJK Ideograph-98BD | 98bc CJK Ideograph-98BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98bf CJK Ideograph-98BF | 98be CJK Ideograph-98BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98c1 CJK Ideograph-98C1 | 98c0 CJK Ideograph-98C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98c3 CJK Ideograph-98C3 | 98c2 CJK Ideograph-98C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98c5 CJK Ideograph-98C5 | 98c4 CJK Ideograph-98C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98c7 CJK Ideograph-98C7 | 98c6 CJK Ideograph-98C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98c9 CJK Ideograph-98C9 | 98c8 CJK Ideograph-98C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98cb CJK Ideograph-98CB | 98ca CJK Ideograph-98CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98cd CJK Ideograph-98CD | 98cc CJK Ideograph-98CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98cf CJK Ideograph-98CF | 98ce CJK Ideograph-98CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98d1 CJK Ideograph-98D1 | 98d0 CJK Ideograph-98D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98d3 CJK Ideograph-98D3 | 98d2 CJK Ideograph-98D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98d5 CJK Ideograph-98D5 | 98d4 CJK Ideograph-98D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98d7 CJK Ideograph-98D7 | 98d6 CJK Ideograph-98D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98d9 CJK Ideograph-98D9 | 98d8 CJK Ideograph-98D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98db CJK Ideograph-98DB | 98da CJK Ideograph-98DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98dd CJK Ideograph-98DD | 98dc CJK Ideograph-98DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98df CJK Ideograph-98DF | 98de CJK Ideograph-98DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98e1 CJK Ideograph-98E1 | 98e0 CJK Ideograph-98E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98e3 CJK Ideograph-98E3 | 98e2 CJK Ideograph-98E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98e5 CJK Ideograph-98E5 | 98e4 CJK Ideograph-98E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98e7 CJK Ideograph-98E7 | 98e6 CJK Ideograph-98E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98e9 CJK Ideograph-98E9 | 98e8 CJK Ideograph-98E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98eb CJK Ideograph-98EB | 98ea CJK Ideograph-98EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98ed CJK Ideograph-98ED | 98ec CJK Ideograph-98EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98ef CJK Ideograph-98EF | 98ee CJK Ideograph-98EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98f1 CJK Ideograph-98F1 | 98f0 CJK Ideograph-98F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98f3 CJK Ideograph-98F3 | 98f2 CJK Ideograph-98F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98f5 CJK Ideograph-98F5 | 98f4 CJK Ideograph-98F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98f7 CJK Ideograph-98F7 | 98f6 CJK Ideograph-98F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98f9 CJK Ideograph-98F9 | 98f8 CJK Ideograph-98F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98fb CJK Ideograph-98FB | 98fa CJK Ideograph-98FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98fd CJK Ideograph-98FD | 98fc CJK Ideograph-98FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98ff CJK Ideograph-98FF | 98fe CJK Ideograph-98FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9901 CJK Ideograph-9901 | 9900 CJK Ideograph-9900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9903 CJK Ideograph-9903 | 9902 CJK Ideograph-9902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9905 CJK Ideograph-9905 | 9904 CJK Ideograph-9904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9907 CJK Ideograph-9907 | 9906 CJK Ideograph-9906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9909 CJK Ideograph-9909 | 9908 CJK Ideograph-9908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 990b CJK Ideograph-990B | 990a CJK Ideograph-990A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 990d CJK Ideograph-990D | 990c CJK Ideograph-990C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 990f CJK Ideograph-990F | 990e CJK Ideograph-990E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9911 CJK Ideograph-9911 | 9910 CJK Ideograph-9910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9913 CJK Ideograph-9913 | 9912 CJK Ideograph-9912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9915 CJK Ideograph-9915 | 9914 CJK Ideograph-9914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9917 CJK Ideograph-9917 | 9916 CJK Ideograph-9916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9919 CJK Ideograph-9919 | 9918 CJK Ideograph-9918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 991b CJK Ideograph-991B | 991a CJK Ideograph-991A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 991d CJK Ideograph-991D | 991c CJK Ideograph-991C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 991f CJK Ideograph-991F | 991e CJK Ideograph-991E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9921 CJK Ideograph-9921 | 9920 CJK Ideograph-9920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9923 CJK Ideograph-9923 | 9922 CJK Ideograph-9922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9925 CJK Ideograph-9925 | 9924 CJK Ideograph-9924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9927 CJK Ideograph-9927 | 9926 CJK Ideograph-9926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9929 CJK Ideograph-9929 | 9928 CJK Ideograph-9928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 992b CJK Ideograph-992B | 992a CJK Ideograph-992A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 992d CJK Ideograph-992D | 992c CJK Ideograph-992C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 992f CJK Ideograph-992F | 992e CJK Ideograph-992E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9931 CJK Ideograph-9931 | 9930 CJK Ideograph-9930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9933 CJK Ideograph-9933 | 9932 CJK Ideograph-9932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9935 CJK Ideograph-9935 | 9934 CJK Ideograph-9934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9937 CJK Ideograph-9937 | 9936 CJK Ideograph-9936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9939 CJK Ideograph-9939 | 9938 CJK Ideograph-9938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 993b CJK Ideograph-993B | 993a CJK Ideograph-993A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 993d CJK Ideograph-993D | 993c CJK Ideograph-993C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 993f CJK Ideograph-993F | 993e CJK Ideograph-993E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9941 CJK Ideograph-9941 | 9940 CJK Ideograph-9940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9943 CJK Ideograph-9943 | 9942 CJK Ideograph-9942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9945 CJK Ideograph-9945 | 9944 CJK Ideograph-9944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9947 CJK Ideograph-9947 | 9946 CJK Ideograph-9946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9949 CJK Ideograph-9949 | 9948 CJK Ideograph-9948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 994b CJK Ideograph-994B | 994a CJK Ideograph-994A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 994d CJK Ideograph-994D | 994c CJK Ideograph-994C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 994f CJK Ideograph-994F | 994e CJK Ideograph-994E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9951 CJK Ideograph-9951 | 9950 CJK Ideograph-9950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9953 CJK Ideograph-9953 | 9952 CJK Ideograph-9952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9955 CJK Ideograph-9955 | 9954 CJK Ideograph-9954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9957 CJK Ideograph-9957 | 9956 CJK Ideograph-9956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9959 CJK Ideograph-9959 | 9958 CJK Ideograph-9958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 995b CJK Ideograph-995B | 995a CJK Ideograph-995A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 995d CJK Ideograph-995D | 995c CJK Ideograph-995C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 995f CJK Ideograph-995F | 995e CJK Ideograph-995E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9961 CJK Ideograph-9961 | 9960 CJK Ideograph-9960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9963 CJK Ideograph-9963 | 9962 CJK Ideograph-9962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9965 CJK Ideograph-9965 | 9964 CJK Ideograph-9964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9967 CJK Ideograph-9967 | 9966 CJK Ideograph-9966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9969 CJK Ideograph-9969 | 9968 CJK Ideograph-9968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 996b CJK Ideograph-996B | 996a CJK Ideograph-996A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 996d CJK Ideograph-996D | 996c CJK Ideograph-996C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 996f CJK Ideograph-996F | 996e CJK Ideograph-996E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9971 CJK Ideograph-9971 | 9970 CJK Ideograph-9970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9973 CJK Ideograph-9973 | 9972 CJK Ideograph-9972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9975 CJK Ideograph-9975 | 9974 CJK Ideograph-9974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9977 CJK Ideograph-9977 | 9976 CJK Ideograph-9976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9979 CJK Ideograph-9979 | 9978 CJK Ideograph-9978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 997b CJK Ideograph-997B | 997a CJK Ideograph-997A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 997d CJK Ideograph-997D | 997c CJK Ideograph-997C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 997f CJK Ideograph-997F | 997e CJK Ideograph-997E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9981 CJK Ideograph-9981 | 9980 CJK Ideograph-9980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9983 CJK Ideograph-9983 | 9982 CJK Ideograph-9982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9985 CJK Ideograph-9985 | 9984 CJK Ideograph-9984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9987 CJK Ideograph-9987 | 9986 CJK Ideograph-9986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9989 CJK Ideograph-9989 | 9988 CJK Ideograph-9988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 998b CJK Ideograph-998B | 998a CJK Ideograph-998A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 998d CJK Ideograph-998D | 998c CJK Ideograph-998C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 998f CJK Ideograph-998F | 998e CJK Ideograph-998E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9991 CJK Ideograph-9991 | 9990 CJK Ideograph-9990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9993 CJK Ideograph-9993 | 9992 CJK Ideograph-9992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9995 CJK Ideograph-9995 | 9994 CJK Ideograph-9994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9997 CJK Ideograph-9997 | 9996 CJK Ideograph-9996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9999 CJK Ideograph-9999 | 9998 CJK Ideograph-9998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 999b CJK Ideograph-999B | 999a CJK Ideograph-999A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 999d CJK Ideograph-999D | 999c CJK Ideograph-999C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 999f CJK Ideograph-999F | 999e CJK Ideograph-999E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99a1 CJK Ideograph-99A1 | 99a0 CJK Ideograph-99A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99a3 CJK Ideograph-99A3 | 99a2 CJK Ideograph-99A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99a5 CJK Ideograph-99A5 | 99a4 CJK Ideograph-99A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99a7 CJK Ideograph-99A7 | 99a6 CJK Ideograph-99A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99a9 CJK Ideograph-99A9 | 99a8 CJK Ideograph-99A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99ab CJK Ideograph-99AB | 99aa CJK Ideograph-99AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99ad CJK Ideograph-99AD | 99ac CJK Ideograph-99AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99af CJK Ideograph-99AF | 99ae CJK Ideograph-99AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99b1 CJK Ideograph-99B1 | 99b0 CJK Ideograph-99B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99b3 CJK Ideograph-99B3 | 99b2 CJK Ideograph-99B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99b5 CJK Ideograph-99B5 | 99b4 CJK Ideograph-99B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99b7 CJK Ideograph-99B7 | 99b6 CJK Ideograph-99B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99b9 CJK Ideograph-99B9 | 99b8 CJK Ideograph-99B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99bb CJK Ideograph-99BB | 99ba CJK Ideograph-99BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99bd CJK Ideograph-99BD | 99bc CJK Ideograph-99BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99bf CJK Ideograph-99BF | 99be CJK Ideograph-99BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99c1 CJK Ideograph-99C1 | 99c0 CJK Ideograph-99C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99c3 CJK Ideograph-99C3 | 99c2 CJK Ideograph-99C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99c5 CJK Ideograph-99C5 | 99c4 CJK Ideograph-99C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99c7 CJK Ideograph-99C7 | 99c6 CJK Ideograph-99C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99c9 CJK Ideograph-99C9 | 99c8 CJK Ideograph-99C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99cb CJK Ideograph-99CB | 99ca CJK Ideograph-99CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99cd CJK Ideograph-99CD | 99cc CJK Ideograph-99CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99cf CJK Ideograph-99CF | 99ce CJK Ideograph-99CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99d1 CJK Ideograph-99D1 | 99d0 CJK Ideograph-99D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99d3 CJK Ideograph-99D3 | 99d2 CJK Ideograph-99D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99d5 CJK Ideograph-99D5 | 99d4 CJK Ideograph-99D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99d7 CJK Ideograph-99D7 | 99d6 CJK Ideograph-99D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99d9 CJK Ideograph-99D9 | 99d8 CJK Ideograph-99D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99db CJK Ideograph-99DB | 99da CJK Ideograph-99DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99dd CJK Ideograph-99DD | 99dc CJK Ideograph-99DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99df CJK Ideograph-99DF | 99de CJK Ideograph-99DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99e1 CJK Ideograph-99E1 | 99e0 CJK Ideograph-99E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99e3 CJK Ideograph-99E3 | 99e2 CJK Ideograph-99E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99e5 CJK Ideograph-99E5 | 99e4 CJK Ideograph-99E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99e7 CJK Ideograph-99E7 | 99e6 CJK Ideograph-99E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99e9 CJK Ideograph-99E9 | 99e8 CJK Ideograph-99E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99eb CJK Ideograph-99EB | 99ea CJK Ideograph-99EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99ed CJK Ideograph-99ED | 99ec CJK Ideograph-99EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99ef CJK Ideograph-99EF | 99ee CJK Ideograph-99EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99f1 CJK Ideograph-99F1 | 99f0 CJK Ideograph-99F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99f3 CJK Ideograph-99F3 | 99f2 CJK Ideograph-99F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99f5 CJK Ideograph-99F5 | 99f4 CJK Ideograph-99F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99f7 CJK Ideograph-99F7 | 99f6 CJK Ideograph-99F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99f9 CJK Ideograph-99F9 | 99f8 CJK Ideograph-99F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99fb CJK Ideograph-99FB | 99fa CJK Ideograph-99FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99fd CJK Ideograph-99FD | 99fc CJK Ideograph-99FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99ff CJK Ideograph-99FF | 99fe CJK Ideograph-99FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a01 CJK Ideograph-9A01 | 9a00 CJK Ideograph-9A00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a03 CJK Ideograph-9A03 | 9a02 CJK Ideograph-9A02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a05 CJK Ideograph-9A05 | 9a04 CJK Ideograph-9A04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a07 CJK Ideograph-9A07 | 9a06 CJK Ideograph-9A06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a09 CJK Ideograph-9A09 | 9a08 CJK Ideograph-9A08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a0b CJK Ideograph-9A0B | 9a0a CJK Ideograph-9A0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a0d CJK Ideograph-9A0D | 9a0c CJK Ideograph-9A0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a0f CJK Ideograph-9A0F | 9a0e CJK Ideograph-9A0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a11 CJK Ideograph-9A11 | 9a10 CJK Ideograph-9A10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a13 CJK Ideograph-9A13 | 9a12 CJK Ideograph-9A12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a15 CJK Ideograph-9A15 | 9a14 CJK Ideograph-9A14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a17 CJK Ideograph-9A17 | 9a16 CJK Ideograph-9A16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a19 CJK Ideograph-9A19 | 9a18 CJK Ideograph-9A18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a1b CJK Ideograph-9A1B | 9a1a CJK Ideograph-9A1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a1d CJK Ideograph-9A1D | 9a1c CJK Ideograph-9A1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a1f CJK Ideograph-9A1F | 9a1e CJK Ideograph-9A1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a21 CJK Ideograph-9A21 | 9a20 CJK Ideograph-9A20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a23 CJK Ideograph-9A23 | 9a22 CJK Ideograph-9A22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a25 CJK Ideograph-9A25 | 9a24 CJK Ideograph-9A24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a27 CJK Ideograph-9A27 | 9a26 CJK Ideograph-9A26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a29 CJK Ideograph-9A29 | 9a28 CJK Ideograph-9A28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a2b CJK Ideograph-9A2B | 9a2a CJK Ideograph-9A2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a2d CJK Ideograph-9A2D | 9a2c CJK Ideograph-9A2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a2f CJK Ideograph-9A2F | 9a2e CJK Ideograph-9A2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a31 CJK Ideograph-9A31 | 9a30 CJK Ideograph-9A30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a33 CJK Ideograph-9A33 | 9a32 CJK Ideograph-9A32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a35 CJK Ideograph-9A35 | 9a34 CJK Ideograph-9A34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a37 CJK Ideograph-9A37 | 9a36 CJK Ideograph-9A36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a39 CJK Ideograph-9A39 | 9a38 CJK Ideograph-9A38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a3b CJK Ideograph-9A3B | 9a3a CJK Ideograph-9A3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a3d CJK Ideograph-9A3D | 9a3c CJK Ideograph-9A3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a3f CJK Ideograph-9A3F | 9a3e CJK Ideograph-9A3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a41 CJK Ideograph-9A41 | 9a40 CJK Ideograph-9A40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a43 CJK Ideograph-9A43 | 9a42 CJK Ideograph-9A42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a45 CJK Ideograph-9A45 | 9a44 CJK Ideograph-9A44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a47 CJK Ideograph-9A47 | 9a46 CJK Ideograph-9A46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a49 CJK Ideograph-9A49 | 9a48 CJK Ideograph-9A48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a4b CJK Ideograph-9A4B | 9a4a CJK Ideograph-9A4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a4d CJK Ideograph-9A4D | 9a4c CJK Ideograph-9A4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a4f CJK Ideograph-9A4F | 9a4e CJK Ideograph-9A4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a51 CJK Ideograph-9A51 | 9a50 CJK Ideograph-9A50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a53 CJK Ideograph-9A53 | 9a52 CJK Ideograph-9A52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a55 CJK Ideograph-9A55 | 9a54 CJK Ideograph-9A54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a57 CJK Ideograph-9A57 | 9a56 CJK Ideograph-9A56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a59 CJK Ideograph-9A59 | 9a58 CJK Ideograph-9A58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a5b CJK Ideograph-9A5B | 9a5a CJK Ideograph-9A5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a5d CJK Ideograph-9A5D | 9a5c CJK Ideograph-9A5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a5f CJK Ideograph-9A5F | 9a5e CJK Ideograph-9A5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a61 CJK Ideograph-9A61 | 9a60 CJK Ideograph-9A60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a63 CJK Ideograph-9A63 | 9a62 CJK Ideograph-9A62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a65 CJK Ideograph-9A65 | 9a64 CJK Ideograph-9A64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a67 CJK Ideograph-9A67 | 9a66 CJK Ideograph-9A66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a69 CJK Ideograph-9A69 | 9a68 CJK Ideograph-9A68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a6b CJK Ideograph-9A6B | 9a6a CJK Ideograph-9A6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a6d CJK Ideograph-9A6D | 9a6c CJK Ideograph-9A6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a6f CJK Ideograph-9A6F | 9a6e CJK Ideograph-9A6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a71 CJK Ideograph-9A71 | 9a70 CJK Ideograph-9A70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a73 CJK Ideograph-9A73 | 9a72 CJK Ideograph-9A72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a75 CJK Ideograph-9A75 | 9a74 CJK Ideograph-9A74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a77 CJK Ideograph-9A77 | 9a76 CJK Ideograph-9A76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a79 CJK Ideograph-9A79 | 9a78 CJK Ideograph-9A78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a7b CJK Ideograph-9A7B | 9a7a CJK Ideograph-9A7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a7d CJK Ideograph-9A7D | 9a7c CJK Ideograph-9A7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a7f CJK Ideograph-9A7F | 9a7e CJK Ideograph-9A7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a81 CJK Ideograph-9A81 | 9a80 CJK Ideograph-9A80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a83 CJK Ideograph-9A83 | 9a82 CJK Ideograph-9A82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a85 CJK Ideograph-9A85 | 9a84 CJK Ideograph-9A84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a87 CJK Ideograph-9A87 | 9a86 CJK Ideograph-9A86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a89 CJK Ideograph-9A89 | 9a88 CJK Ideograph-9A88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a8b CJK Ideograph-9A8B | 9a8a CJK Ideograph-9A8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a8d CJK Ideograph-9A8D | 9a8c CJK Ideograph-9A8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a8f CJK Ideograph-9A8F | 9a8e CJK Ideograph-9A8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a91 CJK Ideograph-9A91 | 9a90 CJK Ideograph-9A90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a93 CJK Ideograph-9A93 | 9a92 CJK Ideograph-9A92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a95 CJK Ideograph-9A95 | 9a94 CJK Ideograph-9A94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a97 CJK Ideograph-9A97 | 9a96 CJK Ideograph-9A96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a99 CJK Ideograph-9A99 | 9a98 CJK Ideograph-9A98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a9b CJK Ideograph-9A9B | 9a9a CJK Ideograph-9A9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a9d CJK Ideograph-9A9D | 9a9c CJK Ideograph-9A9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a9f CJK Ideograph-9A9F | 9a9e CJK Ideograph-9A9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aa1 CJK Ideograph-9AA1 | 9aa0 CJK Ideograph-9AA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aa3 CJK Ideograph-9AA3 | 9aa2 CJK Ideograph-9AA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aa5 CJK Ideograph-9AA5 | 9aa4 CJK Ideograph-9AA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aa7 CJK Ideograph-9AA7 | 9aa6 CJK Ideograph-9AA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aa9 CJK Ideograph-9AA9 | 9aa8 CJK Ideograph-9AA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aab CJK Ideograph-9AAB | 9aaa CJK Ideograph-9AAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aad CJK Ideograph-9AAD | 9aac CJK Ideograph-9AAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aaf CJK Ideograph-9AAF | 9aae CJK Ideograph-9AAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ab1 CJK Ideograph-9AB1 | 9ab0 CJK Ideograph-9AB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ab3 CJK Ideograph-9AB3 | 9ab2 CJK Ideograph-9AB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ab5 CJK Ideograph-9AB5 | 9ab4 CJK Ideograph-9AB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ab7 CJK Ideograph-9AB7 | 9ab6 CJK Ideograph-9AB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ab9 CJK Ideograph-9AB9 | 9ab8 CJK Ideograph-9AB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9abb CJK Ideograph-9ABB | 9aba CJK Ideograph-9ABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9abd CJK Ideograph-9ABD | 9abc CJK Ideograph-9ABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9abf CJK Ideograph-9ABF | 9abe CJK Ideograph-9ABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ac1 CJK Ideograph-9AC1 | 9ac0 CJK Ideograph-9AC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ac3 CJK Ideograph-9AC3 | 9ac2 CJK Ideograph-9AC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ac5 CJK Ideograph-9AC5 | 9ac4 CJK Ideograph-9AC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ac7 CJK Ideograph-9AC7 | 9ac6 CJK Ideograph-9AC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ac9 CJK Ideograph-9AC9 | 9ac8 CJK Ideograph-9AC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9acb CJK Ideograph-9ACB | 9aca CJK Ideograph-9ACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9acd CJK Ideograph-9ACD | 9acc CJK Ideograph-9ACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9acf CJK Ideograph-9ACF | 9ace CJK Ideograph-9ACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ad1 CJK Ideograph-9AD1 | 9ad0 CJK Ideograph-9AD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ad3 CJK Ideograph-9AD3 | 9ad2 CJK Ideograph-9AD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ad5 CJK Ideograph-9AD5 | 9ad4 CJK Ideograph-9AD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ad7 CJK Ideograph-9AD7 | 9ad6 CJK Ideograph-9AD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ad9 CJK Ideograph-9AD9 | 9ad8 CJK Ideograph-9AD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9adb CJK Ideograph-9ADB | 9ada CJK Ideograph-9ADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9add CJK Ideograph-9ADD | 9adc CJK Ideograph-9ADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9adf CJK Ideograph-9ADF | 9ade CJK Ideograph-9ADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ae1 CJK Ideograph-9AE1 | 9ae0 CJK Ideograph-9AE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ae3 CJK Ideograph-9AE3 | 9ae2 CJK Ideograph-9AE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ae5 CJK Ideograph-9AE5 | 9ae4 CJK Ideograph-9AE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ae7 CJK Ideograph-9AE7 | 9ae6 CJK Ideograph-9AE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ae9 CJK Ideograph-9AE9 | 9ae8 CJK Ideograph-9AE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aeb CJK Ideograph-9AEB | 9aea CJK Ideograph-9AEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aed CJK Ideograph-9AED | 9aec CJK Ideograph-9AEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aef CJK Ideograph-9AEF | 9aee CJK Ideograph-9AEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9af1 CJK Ideograph-9AF1 | 9af0 CJK Ideograph-9AF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9af3 CJK Ideograph-9AF3 | 9af2 CJK Ideograph-9AF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9af5 CJK Ideograph-9AF5 | 9af4 CJK Ideograph-9AF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9af7 CJK Ideograph-9AF7 | 9af6 CJK Ideograph-9AF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9af9 CJK Ideograph-9AF9 | 9af8 CJK Ideograph-9AF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9afb CJK Ideograph-9AFB | 9afa CJK Ideograph-9AFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9afd CJK Ideograph-9AFD | 9afc CJK Ideograph-9AFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aff CJK Ideograph-9AFF | 9afe CJK Ideograph-9AFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b01 CJK Ideograph-9B01 | 9b00 CJK Ideograph-9B00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b03 CJK Ideograph-9B03 | 9b02 CJK Ideograph-9B02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b05 CJK Ideograph-9B05 | 9b04 CJK Ideograph-9B04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b07 CJK Ideograph-9B07 | 9b06 CJK Ideograph-9B06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b09 CJK Ideograph-9B09 | 9b08 CJK Ideograph-9B08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b0b CJK Ideograph-9B0B | 9b0a CJK Ideograph-9B0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b0d CJK Ideograph-9B0D | 9b0c CJK Ideograph-9B0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b0f CJK Ideograph-9B0F | 9b0e CJK Ideograph-9B0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b11 CJK Ideograph-9B11 | 9b10 CJK Ideograph-9B10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b13 CJK Ideograph-9B13 | 9b12 CJK Ideograph-9B12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b15 CJK Ideograph-9B15 | 9b14 CJK Ideograph-9B14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b17 CJK Ideograph-9B17 | 9b16 CJK Ideograph-9B16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b19 CJK Ideograph-9B19 | 9b18 CJK Ideograph-9B18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b1b CJK Ideograph-9B1B | 9b1a CJK Ideograph-9B1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b1d CJK Ideograph-9B1D | 9b1c CJK Ideograph-9B1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b1f CJK Ideograph-9B1F | 9b1e CJK Ideograph-9B1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b21 CJK Ideograph-9B21 | 9b20 CJK Ideograph-9B20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b23 CJK Ideograph-9B23 | 9b22 CJK Ideograph-9B22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b25 CJK Ideograph-9B25 | 9b24 CJK Ideograph-9B24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b27 CJK Ideograph-9B27 | 9b26 CJK Ideograph-9B26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b29 CJK Ideograph-9B29 | 9b28 CJK Ideograph-9B28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b2b CJK Ideograph-9B2B | 9b2a CJK Ideograph-9B2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b2d CJK Ideograph-9B2D | 9b2c CJK Ideograph-9B2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b2f CJK Ideograph-9B2F | 9b2e CJK Ideograph-9B2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b31 CJK Ideograph-9B31 | 9b30 CJK Ideograph-9B30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b33 CJK Ideograph-9B33 | 9b32 CJK Ideograph-9B32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b35 CJK Ideograph-9B35 | 9b34 CJK Ideograph-9B34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b37 CJK Ideograph-9B37 | 9b36 CJK Ideograph-9B36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b39 CJK Ideograph-9B39 | 9b38 CJK Ideograph-9B38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b3b CJK Ideograph-9B3B | 9b3a CJK Ideograph-9B3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b3d CJK Ideograph-9B3D | 9b3c CJK Ideograph-9B3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b3f CJK Ideograph-9B3F | 9b3e CJK Ideograph-9B3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b41 CJK Ideograph-9B41 | 9b40 CJK Ideograph-9B40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b43 CJK Ideograph-9B43 | 9b42 CJK Ideograph-9B42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b45 CJK Ideograph-9B45 | 9b44 CJK Ideograph-9B44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b47 CJK Ideograph-9B47 | 9b46 CJK Ideograph-9B46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b49 CJK Ideograph-9B49 | 9b48 CJK Ideograph-9B48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b4b CJK Ideograph-9B4B | 9b4a CJK Ideograph-9B4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b4d CJK Ideograph-9B4D | 9b4c CJK Ideograph-9B4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b4f CJK Ideograph-9B4F | 9b4e CJK Ideograph-9B4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b51 CJK Ideograph-9B51 | 9b50 CJK Ideograph-9B50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b53 CJK Ideograph-9B53 | 9b52 CJK Ideograph-9B52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b55 CJK Ideograph-9B55 | 9b54 CJK Ideograph-9B54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b57 CJK Ideograph-9B57 | 9b56 CJK Ideograph-9B56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b59 CJK Ideograph-9B59 | 9b58 CJK Ideograph-9B58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b5b CJK Ideograph-9B5B | 9b5a CJK Ideograph-9B5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b5d CJK Ideograph-9B5D | 9b5c CJK Ideograph-9B5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b5f CJK Ideograph-9B5F | 9b5e CJK Ideograph-9B5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b61 CJK Ideograph-9B61 | 9b60 CJK Ideograph-9B60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b63 CJK Ideograph-9B63 | 9b62 CJK Ideograph-9B62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b65 CJK Ideograph-9B65 | 9b64 CJK Ideograph-9B64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b67 CJK Ideograph-9B67 | 9b66 CJK Ideograph-9B66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b69 CJK Ideograph-9B69 | 9b68 CJK Ideograph-9B68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b6b CJK Ideograph-9B6B | 9b6a CJK Ideograph-9B6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b6d CJK Ideograph-9B6D | 9b6c CJK Ideograph-9B6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b6f CJK Ideograph-9B6F | 9b6e CJK Ideograph-9B6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b71 CJK Ideograph-9B71 | 9b70 CJK Ideograph-9B70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b73 CJK Ideograph-9B73 | 9b72 CJK Ideograph-9B72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b75 CJK Ideograph-9B75 | 9b74 CJK Ideograph-9B74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b77 CJK Ideograph-9B77 | 9b76 CJK Ideograph-9B76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b79 CJK Ideograph-9B79 | 9b78 CJK Ideograph-9B78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b7b CJK Ideograph-9B7B | 9b7a CJK Ideograph-9B7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b7d CJK Ideograph-9B7D | 9b7c CJK Ideograph-9B7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b7f CJK Ideograph-9B7F | 9b7e CJK Ideograph-9B7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b81 CJK Ideograph-9B81 | 9b80 CJK Ideograph-9B80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b83 CJK Ideograph-9B83 | 9b82 CJK Ideograph-9B82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b85 CJK Ideograph-9B85 | 9b84 CJK Ideograph-9B84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b87 CJK Ideograph-9B87 | 9b86 CJK Ideograph-9B86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b89 CJK Ideograph-9B89 | 9b88 CJK Ideograph-9B88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b8b CJK Ideograph-9B8B | 9b8a CJK Ideograph-9B8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b8d CJK Ideograph-9B8D | 9b8c CJK Ideograph-9B8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b8f CJK Ideograph-9B8F | 9b8e CJK Ideograph-9B8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b91 CJK Ideograph-9B91 | 9b90 CJK Ideograph-9B90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b93 CJK Ideograph-9B93 | 9b92 CJK Ideograph-9B92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b95 CJK Ideograph-9B95 | 9b94 CJK Ideograph-9B94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b97 CJK Ideograph-9B97 | 9b96 CJK Ideograph-9B96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b99 CJK Ideograph-9B99 | 9b98 CJK Ideograph-9B98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b9b CJK Ideograph-9B9B | 9b9a CJK Ideograph-9B9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b9d CJK Ideograph-9B9D | 9b9c CJK Ideograph-9B9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b9f CJK Ideograph-9B9F | 9b9e CJK Ideograph-9B9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ba1 CJK Ideograph-9BA1 | 9ba0 CJK Ideograph-9BA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ba3 CJK Ideograph-9BA3 | 9ba2 CJK Ideograph-9BA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ba5 CJK Ideograph-9BA5 | 9ba4 CJK Ideograph-9BA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ba7 CJK Ideograph-9BA7 | 9ba6 CJK Ideograph-9BA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ba9 CJK Ideograph-9BA9 | 9ba8 CJK Ideograph-9BA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bab CJK Ideograph-9BAB | 9baa CJK Ideograph-9BAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bad CJK Ideograph-9BAD | 9bac CJK Ideograph-9BAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9baf CJK Ideograph-9BAF | 9bae CJK Ideograph-9BAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bb1 CJK Ideograph-9BB1 | 9bb0 CJK Ideograph-9BB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bb3 CJK Ideograph-9BB3 | 9bb2 CJK Ideograph-9BB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bb5 CJK Ideograph-9BB5 | 9bb4 CJK Ideograph-9BB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bb7 CJK Ideograph-9BB7 | 9bb6 CJK Ideograph-9BB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bb9 CJK Ideograph-9BB9 | 9bb8 CJK Ideograph-9BB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bbb CJK Ideograph-9BBB | 9bba CJK Ideograph-9BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bbd CJK Ideograph-9BBD | 9bbc CJK Ideograph-9BBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bbf CJK Ideograph-9BBF | 9bbe CJK Ideograph-9BBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bc1 CJK Ideograph-9BC1 | 9bc0 CJK Ideograph-9BC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bc3 CJK Ideograph-9BC3 | 9bc2 CJK Ideograph-9BC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bc5 CJK Ideograph-9BC5 | 9bc4 CJK Ideograph-9BC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bc7 CJK Ideograph-9BC7 | 9bc6 CJK Ideograph-9BC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bc9 CJK Ideograph-9BC9 | 9bc8 CJK Ideograph-9BC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bcb CJK Ideograph-9BCB | 9bca CJK Ideograph-9BCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bcd CJK Ideograph-9BCD | 9bcc CJK Ideograph-9BCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bcf CJK Ideograph-9BCF | 9bce CJK Ideograph-9BCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bd1 CJK Ideograph-9BD1 | 9bd0 CJK Ideograph-9BD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bd3 CJK Ideograph-9BD3 | 9bd2 CJK Ideograph-9BD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bd5 CJK Ideograph-9BD5 | 9bd4 CJK Ideograph-9BD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bd7 CJK Ideograph-9BD7 | 9bd6 CJK Ideograph-9BD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bd9 CJK Ideograph-9BD9 | 9bd8 CJK Ideograph-9BD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bdb CJK Ideograph-9BDB | 9bda CJK Ideograph-9BDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bdd CJK Ideograph-9BDD | 9bdc CJK Ideograph-9BDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bdf CJK Ideograph-9BDF | 9bde CJK Ideograph-9BDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9be1 CJK Ideograph-9BE1 | 9be0 CJK Ideograph-9BE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9be3 CJK Ideograph-9BE3 | 9be2 CJK Ideograph-9BE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9be5 CJK Ideograph-9BE5 | 9be4 CJK Ideograph-9BE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9be7 CJK Ideograph-9BE7 | 9be6 CJK Ideograph-9BE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9be9 CJK Ideograph-9BE9 | 9be8 CJK Ideograph-9BE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9beb CJK Ideograph-9BEB | 9bea CJK Ideograph-9BEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bed CJK Ideograph-9BED | 9bec CJK Ideograph-9BEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bef CJK Ideograph-9BEF | 9bee CJK Ideograph-9BEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bf1 CJK Ideograph-9BF1 | 9bf0 CJK Ideograph-9BF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bf3 CJK Ideograph-9BF3 | 9bf2 CJK Ideograph-9BF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bf5 CJK Ideograph-9BF5 | 9bf4 CJK Ideograph-9BF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bf7 CJK Ideograph-9BF7 | 9bf6 CJK Ideograph-9BF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bf9 CJK Ideograph-9BF9 | 9bf8 CJK Ideograph-9BF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bfb CJK Ideograph-9BFB | 9bfa CJK Ideograph-9BFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bfd CJK Ideograph-9BFD | 9bfc CJK Ideograph-9BFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bff CJK Ideograph-9BFF | 9bfe CJK Ideograph-9BFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c01 CJK Ideograph-9C01 | 9c00 CJK Ideograph-9C00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c03 CJK Ideograph-9C03 | 9c02 CJK Ideograph-9C02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c05 CJK Ideograph-9C05 | 9c04 CJK Ideograph-9C04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c07 CJK Ideograph-9C07 | 9c06 CJK Ideograph-9C06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c09 CJK Ideograph-9C09 | 9c08 CJK Ideograph-9C08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c0b CJK Ideograph-9C0B | 9c0a CJK Ideograph-9C0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c0d CJK Ideograph-9C0D | 9c0c CJK Ideograph-9C0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c0f CJK Ideograph-9C0F | 9c0e CJK Ideograph-9C0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c11 CJK Ideograph-9C11 | 9c10 CJK Ideograph-9C10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c13 CJK Ideograph-9C13 | 9c12 CJK Ideograph-9C12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c15 CJK Ideograph-9C15 | 9c14 CJK Ideograph-9C14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c17 CJK Ideograph-9C17 | 9c16 CJK Ideograph-9C16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c19 CJK Ideograph-9C19 | 9c18 CJK Ideograph-9C18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c1b CJK Ideograph-9C1B | 9c1a CJK Ideograph-9C1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c1d CJK Ideograph-9C1D | 9c1c CJK Ideograph-9C1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c1f CJK Ideograph-9C1F | 9c1e CJK Ideograph-9C1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c21 CJK Ideograph-9C21 | 9c20 CJK Ideograph-9C20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c23 CJK Ideograph-9C23 | 9c22 CJK Ideograph-9C22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c25 CJK Ideograph-9C25 | 9c24 CJK Ideograph-9C24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c27 CJK Ideograph-9C27 | 9c26 CJK Ideograph-9C26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c29 CJK Ideograph-9C29 | 9c28 CJK Ideograph-9C28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c2b CJK Ideograph-9C2B | 9c2a CJK Ideograph-9C2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c2d CJK Ideograph-9C2D | 9c2c CJK Ideograph-9C2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c2f CJK Ideograph-9C2F | 9c2e CJK Ideograph-9C2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c31 CJK Ideograph-9C31 | 9c30 CJK Ideograph-9C30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c33 CJK Ideograph-9C33 | 9c32 CJK Ideograph-9C32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c35 CJK Ideograph-9C35 | 9c34 CJK Ideograph-9C34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c37 CJK Ideograph-9C37 | 9c36 CJK Ideograph-9C36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c39 CJK Ideograph-9C39 | 9c38 CJK Ideograph-9C38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c3b CJK Ideograph-9C3B | 9c3a CJK Ideograph-9C3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c3d CJK Ideograph-9C3D | 9c3c CJK Ideograph-9C3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c3f CJK Ideograph-9C3F | 9c3e CJK Ideograph-9C3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c41 CJK Ideograph-9C41 | 9c40 CJK Ideograph-9C40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c43 CJK Ideograph-9C43 | 9c42 CJK Ideograph-9C42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c45 CJK Ideograph-9C45 | 9c44 CJK Ideograph-9C44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c47 CJK Ideograph-9C47 | 9c46 CJK Ideograph-9C46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c49 CJK Ideograph-9C49 | 9c48 CJK Ideograph-9C48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c4b CJK Ideograph-9C4B | 9c4a CJK Ideograph-9C4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c4d CJK Ideograph-9C4D | 9c4c CJK Ideograph-9C4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c4f CJK Ideograph-9C4F | 9c4e CJK Ideograph-9C4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c51 CJK Ideograph-9C51 | 9c50 CJK Ideograph-9C50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c53 CJK Ideograph-9C53 | 9c52 CJK Ideograph-9C52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c55 CJK Ideograph-9C55 | 9c54 CJK Ideograph-9C54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c57 CJK Ideograph-9C57 | 9c56 CJK Ideograph-9C56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c59 CJK Ideograph-9C59 | 9c58 CJK Ideograph-9C58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c5b CJK Ideograph-9C5B | 9c5a CJK Ideograph-9C5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c5d CJK Ideograph-9C5D | 9c5c CJK Ideograph-9C5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c5f CJK Ideograph-9C5F | 9c5e CJK Ideograph-9C5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c61 CJK Ideograph-9C61 | 9c60 CJK Ideograph-9C60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c63 CJK Ideograph-9C63 | 9c62 CJK Ideograph-9C62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c65 CJK Ideograph-9C65 | 9c64 CJK Ideograph-9C64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c67 CJK Ideograph-9C67 | 9c66 CJK Ideograph-9C66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c69 CJK Ideograph-9C69 | 9c68 CJK Ideograph-9C68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c6b CJK Ideograph-9C6B | 9c6a CJK Ideograph-9C6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c6d CJK Ideograph-9C6D | 9c6c CJK Ideograph-9C6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c6f CJK Ideograph-9C6F | 9c6e CJK Ideograph-9C6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c71 CJK Ideograph-9C71 | 9c70 CJK Ideograph-9C70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c73 CJK Ideograph-9C73 | 9c72 CJK Ideograph-9C72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c75 CJK Ideograph-9C75 | 9c74 CJK Ideograph-9C74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c77 CJK Ideograph-9C77 | 9c76 CJK Ideograph-9C76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c79 CJK Ideograph-9C79 | 9c78 CJK Ideograph-9C78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c7b CJK Ideograph-9C7B | 9c7a CJK Ideograph-9C7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c7d CJK Ideograph-9C7D | 9c7c CJK Ideograph-9C7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c7f CJK Ideograph-9C7F | 9c7e CJK Ideograph-9C7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c81 CJK Ideograph-9C81 | 9c80 CJK Ideograph-9C80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c83 CJK Ideograph-9C83 | 9c82 CJK Ideograph-9C82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c85 CJK Ideograph-9C85 | 9c84 CJK Ideograph-9C84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c87 CJK Ideograph-9C87 | 9c86 CJK Ideograph-9C86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c89 CJK Ideograph-9C89 | 9c88 CJK Ideograph-9C88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c8b CJK Ideograph-9C8B | 9c8a CJK Ideograph-9C8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c8d CJK Ideograph-9C8D | 9c8c CJK Ideograph-9C8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c8f CJK Ideograph-9C8F | 9c8e CJK Ideograph-9C8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c91 CJK Ideograph-9C91 | 9c90 CJK Ideograph-9C90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c93 CJK Ideograph-9C93 | 9c92 CJK Ideograph-9C92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c95 CJK Ideograph-9C95 | 9c94 CJK Ideograph-9C94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c97 CJK Ideograph-9C97 | 9c96 CJK Ideograph-9C96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c99 CJK Ideograph-9C99 | 9c98 CJK Ideograph-9C98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c9b CJK Ideograph-9C9B | 9c9a CJK Ideograph-9C9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c9d CJK Ideograph-9C9D | 9c9c CJK Ideograph-9C9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c9f CJK Ideograph-9C9F | 9c9e CJK Ideograph-9C9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ca1 CJK Ideograph-9CA1 | 9ca0 CJK Ideograph-9CA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ca3 CJK Ideograph-9CA3 | 9ca2 CJK Ideograph-9CA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ca5 CJK Ideograph-9CA5 | 9ca4 CJK Ideograph-9CA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ca7 CJK Ideograph-9CA7 | 9ca6 CJK Ideograph-9CA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ca9 CJK Ideograph-9CA9 | 9ca8 CJK Ideograph-9CA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cab CJK Ideograph-9CAB | 9caa CJK Ideograph-9CAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cad CJK Ideograph-9CAD | 9cac CJK Ideograph-9CAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9caf CJK Ideograph-9CAF | 9cae CJK Ideograph-9CAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cb1 CJK Ideograph-9CB1 | 9cb0 CJK Ideograph-9CB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cb3 CJK Ideograph-9CB3 | 9cb2 CJK Ideograph-9CB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cb5 CJK Ideograph-9CB5 | 9cb4 CJK Ideograph-9CB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cb7 CJK Ideograph-9CB7 | 9cb6 CJK Ideograph-9CB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cb9 CJK Ideograph-9CB9 | 9cb8 CJK Ideograph-9CB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cbb CJK Ideograph-9CBB | 9cba CJK Ideograph-9CBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cbd CJK Ideograph-9CBD | 9cbc CJK Ideograph-9CBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cbf CJK Ideograph-9CBF | 9cbe CJK Ideograph-9CBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cc1 CJK Ideograph-9CC1 | 9cc0 CJK Ideograph-9CC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cc3 CJK Ideograph-9CC3 | 9cc2 CJK Ideograph-9CC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cc5 CJK Ideograph-9CC5 | 9cc4 CJK Ideograph-9CC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cc7 CJK Ideograph-9CC7 | 9cc6 CJK Ideograph-9CC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cc9 CJK Ideograph-9CC9 | 9cc8 CJK Ideograph-9CC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ccb CJK Ideograph-9CCB | 9cca CJK Ideograph-9CCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ccd CJK Ideograph-9CCD | 9ccc CJK Ideograph-9CCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ccf CJK Ideograph-9CCF | 9cce CJK Ideograph-9CCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cd1 CJK Ideograph-9CD1 | 9cd0 CJK Ideograph-9CD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cd3 CJK Ideograph-9CD3 | 9cd2 CJK Ideograph-9CD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cd5 CJK Ideograph-9CD5 | 9cd4 CJK Ideograph-9CD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cd7 CJK Ideograph-9CD7 | 9cd6 CJK Ideograph-9CD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cd9 CJK Ideograph-9CD9 | 9cd8 CJK Ideograph-9CD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cdb CJK Ideograph-9CDB | 9cda CJK Ideograph-9CDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cdd CJK Ideograph-9CDD | 9cdc CJK Ideograph-9CDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cdf CJK Ideograph-9CDF | 9cde CJK Ideograph-9CDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ce1 CJK Ideograph-9CE1 | 9ce0 CJK Ideograph-9CE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ce3 CJK Ideograph-9CE3 | 9ce2 CJK Ideograph-9CE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ce5 CJK Ideograph-9CE5 | 9ce4 CJK Ideograph-9CE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ce7 CJK Ideograph-9CE7 | 9ce6 CJK Ideograph-9CE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ce9 CJK Ideograph-9CE9 | 9ce8 CJK Ideograph-9CE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ceb CJK Ideograph-9CEB | 9cea CJK Ideograph-9CEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ced CJK Ideograph-9CED | 9cec CJK Ideograph-9CEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cef CJK Ideograph-9CEF | 9cee CJK Ideograph-9CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cf1 CJK Ideograph-9CF1 | 9cf0 CJK Ideograph-9CF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cf3 CJK Ideograph-9CF3 | 9cf2 CJK Ideograph-9CF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cf5 CJK Ideograph-9CF5 | 9cf4 CJK Ideograph-9CF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cf7 CJK Ideograph-9CF7 | 9cf6 CJK Ideograph-9CF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cf9 CJK Ideograph-9CF9 | 9cf8 CJK Ideograph-9CF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cfb CJK Ideograph-9CFB | 9cfa CJK Ideograph-9CFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cfd CJK Ideograph-9CFD | 9cfc CJK Ideograph-9CFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cff CJK Ideograph-9CFF | 9cfe CJK Ideograph-9CFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d01 CJK Ideograph-9D01 | 9d00 CJK Ideograph-9D00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d03 CJK Ideograph-9D03 | 9d02 CJK Ideograph-9D02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d05 CJK Ideograph-9D05 | 9d04 CJK Ideograph-9D04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d07 CJK Ideograph-9D07 | 9d06 CJK Ideograph-9D06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d09 CJK Ideograph-9D09 | 9d08 CJK Ideograph-9D08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d0b CJK Ideograph-9D0B | 9d0a CJK Ideograph-9D0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d0d CJK Ideograph-9D0D | 9d0c CJK Ideograph-9D0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d0f CJK Ideograph-9D0F | 9d0e CJK Ideograph-9D0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d11 CJK Ideograph-9D11 | 9d10 CJK Ideograph-9D10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d13 CJK Ideograph-9D13 | 9d12 CJK Ideograph-9D12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d15 CJK Ideograph-9D15 | 9d14 CJK Ideograph-9D14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d17 CJK Ideograph-9D17 | 9d16 CJK Ideograph-9D16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d19 CJK Ideograph-9D19 | 9d18 CJK Ideograph-9D18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d1b CJK Ideograph-9D1B | 9d1a CJK Ideograph-9D1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d1d CJK Ideograph-9D1D | 9d1c CJK Ideograph-9D1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d1f CJK Ideograph-9D1F | 9d1e CJK Ideograph-9D1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d21 CJK Ideograph-9D21 | 9d20 CJK Ideograph-9D20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d23 CJK Ideograph-9D23 | 9d22 CJK Ideograph-9D22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d25 CJK Ideograph-9D25 | 9d24 CJK Ideograph-9D24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d27 CJK Ideograph-9D27 | 9d26 CJK Ideograph-9D26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d29 CJK Ideograph-9D29 | 9d28 CJK Ideograph-9D28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d2b CJK Ideograph-9D2B | 9d2a CJK Ideograph-9D2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d2d CJK Ideograph-9D2D | 9d2c CJK Ideograph-9D2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d2f CJK Ideograph-9D2F | 9d2e CJK Ideograph-9D2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d31 CJK Ideograph-9D31 | 9d30 CJK Ideograph-9D30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d33 CJK Ideograph-9D33 | 9d32 CJK Ideograph-9D32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d35 CJK Ideograph-9D35 | 9d34 CJK Ideograph-9D34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d37 CJK Ideograph-9D37 | 9d36 CJK Ideograph-9D36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d39 CJK Ideograph-9D39 | 9d38 CJK Ideograph-9D38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d3b CJK Ideograph-9D3B | 9d3a CJK Ideograph-9D3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d3d CJK Ideograph-9D3D | 9d3c CJK Ideograph-9D3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d3f CJK Ideograph-9D3F | 9d3e CJK Ideograph-9D3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d41 CJK Ideograph-9D41 | 9d40 CJK Ideograph-9D40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d43 CJK Ideograph-9D43 | 9d42 CJK Ideograph-9D42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d45 CJK Ideograph-9D45 | 9d44 CJK Ideograph-9D44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d47 CJK Ideograph-9D47 | 9d46 CJK Ideograph-9D46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d49 CJK Ideograph-9D49 | 9d48 CJK Ideograph-9D48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d4b CJK Ideograph-9D4B | 9d4a CJK Ideograph-9D4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d4d CJK Ideograph-9D4D | 9d4c CJK Ideograph-9D4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d4f CJK Ideograph-9D4F | 9d4e CJK Ideograph-9D4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d51 CJK Ideograph-9D51 | 9d50 CJK Ideograph-9D50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d53 CJK Ideograph-9D53 | 9d52 CJK Ideograph-9D52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d55 CJK Ideograph-9D55 | 9d54 CJK Ideograph-9D54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d57 CJK Ideograph-9D57 | 9d56 CJK Ideograph-9D56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d59 CJK Ideograph-9D59 | 9d58 CJK Ideograph-9D58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d5b CJK Ideograph-9D5B | 9d5a CJK Ideograph-9D5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d5d CJK Ideograph-9D5D | 9d5c CJK Ideograph-9D5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d5f CJK Ideograph-9D5F | 9d5e CJK Ideograph-9D5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d61 CJK Ideograph-9D61 | 9d60 CJK Ideograph-9D60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d63 CJK Ideograph-9D63 | 9d62 CJK Ideograph-9D62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d65 CJK Ideograph-9D65 | 9d64 CJK Ideograph-9D64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d67 CJK Ideograph-9D67 | 9d66 CJK Ideograph-9D66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d69 CJK Ideograph-9D69 | 9d68 CJK Ideograph-9D68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d6b CJK Ideograph-9D6B | 9d6a CJK Ideograph-9D6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d6d CJK Ideograph-9D6D | 9d6c CJK Ideograph-9D6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d6f CJK Ideograph-9D6F | 9d6e CJK Ideograph-9D6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d71 CJK Ideograph-9D71 | 9d70 CJK Ideograph-9D70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d73 CJK Ideograph-9D73 | 9d72 CJK Ideograph-9D72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d75 CJK Ideograph-9D75 | 9d74 CJK Ideograph-9D74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d77 CJK Ideograph-9D77 | 9d76 CJK Ideograph-9D76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d79 CJK Ideograph-9D79 | 9d78 CJK Ideograph-9D78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d7b CJK Ideograph-9D7B | 9d7a CJK Ideograph-9D7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d7d CJK Ideograph-9D7D | 9d7c CJK Ideograph-9D7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d7f CJK Ideograph-9D7F | 9d7e CJK Ideograph-9D7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d81 CJK Ideograph-9D81 | 9d80 CJK Ideograph-9D80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d83 CJK Ideograph-9D83 | 9d82 CJK Ideograph-9D82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d85 CJK Ideograph-9D85 | 9d84 CJK Ideograph-9D84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d87 CJK Ideograph-9D87 | 9d86 CJK Ideograph-9D86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d89 CJK Ideograph-9D89 | 9d88 CJK Ideograph-9D88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d8b CJK Ideograph-9D8B | 9d8a CJK Ideograph-9D8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d8d CJK Ideograph-9D8D | 9d8c CJK Ideograph-9D8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d8f CJK Ideograph-9D8F | 9d8e CJK Ideograph-9D8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d91 CJK Ideograph-9D91 | 9d90 CJK Ideograph-9D90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d93 CJK Ideograph-9D93 | 9d92 CJK Ideograph-9D92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d95 CJK Ideograph-9D95 | 9d94 CJK Ideograph-9D94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d97 CJK Ideograph-9D97 | 9d96 CJK Ideograph-9D96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d99 CJK Ideograph-9D99 | 9d98 CJK Ideograph-9D98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d9b CJK Ideograph-9D9B | 9d9a CJK Ideograph-9D9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d9d CJK Ideograph-9D9D | 9d9c CJK Ideograph-9D9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d9f CJK Ideograph-9D9F | 9d9e CJK Ideograph-9D9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9da1 CJK Ideograph-9DA1 | 9da0 CJK Ideograph-9DA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9da3 CJK Ideograph-9DA3 | 9da2 CJK Ideograph-9DA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9da5 CJK Ideograph-9DA5 | 9da4 CJK Ideograph-9DA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9da7 CJK Ideograph-9DA7 | 9da6 CJK Ideograph-9DA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9da9 CJK Ideograph-9DA9 | 9da8 CJK Ideograph-9DA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dab CJK Ideograph-9DAB | 9daa CJK Ideograph-9DAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dad CJK Ideograph-9DAD | 9dac CJK Ideograph-9DAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9daf CJK Ideograph-9DAF | 9dae CJK Ideograph-9DAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9db1 CJK Ideograph-9DB1 | 9db0 CJK Ideograph-9DB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9db3 CJK Ideograph-9DB3 | 9db2 CJK Ideograph-9DB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9db5 CJK Ideograph-9DB5 | 9db4 CJK Ideograph-9DB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9db7 CJK Ideograph-9DB7 | 9db6 CJK Ideograph-9DB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9db9 CJK Ideograph-9DB9 | 9db8 CJK Ideograph-9DB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dbb CJK Ideograph-9DBB | 9dba CJK Ideograph-9DBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dbd CJK Ideograph-9DBD | 9dbc CJK Ideograph-9DBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dbf CJK Ideograph-9DBF | 9dbe CJK Ideograph-9DBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dc1 CJK Ideograph-9DC1 | 9dc0 CJK Ideograph-9DC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dc3 CJK Ideograph-9DC3 | 9dc2 CJK Ideograph-9DC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dc5 CJK Ideograph-9DC5 | 9dc4 CJK Ideograph-9DC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dc7 CJK Ideograph-9DC7 | 9dc6 CJK Ideograph-9DC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dc9 CJK Ideograph-9DC9 | 9dc8 CJK Ideograph-9DC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dcb CJK Ideograph-9DCB | 9dca CJK Ideograph-9DCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dcd CJK Ideograph-9DCD | 9dcc CJK Ideograph-9DCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dcf CJK Ideograph-9DCF | 9dce CJK Ideograph-9DCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dd1 CJK Ideograph-9DD1 | 9dd0 CJK Ideograph-9DD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dd3 CJK Ideograph-9DD3 | 9dd2 CJK Ideograph-9DD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dd5 CJK Ideograph-9DD5 | 9dd4 CJK Ideograph-9DD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dd7 CJK Ideograph-9DD7 | 9dd6 CJK Ideograph-9DD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dd9 CJK Ideograph-9DD9 | 9dd8 CJK Ideograph-9DD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ddb CJK Ideograph-9DDB | 9dda CJK Ideograph-9DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ddd CJK Ideograph-9DDD | 9ddc CJK Ideograph-9DDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ddf CJK Ideograph-9DDF | 9dde CJK Ideograph-9DDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9de1 CJK Ideograph-9DE1 | 9de0 CJK Ideograph-9DE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9de3 CJK Ideograph-9DE3 | 9de2 CJK Ideograph-9DE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9de5 CJK Ideograph-9DE5 | 9de4 CJK Ideograph-9DE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9de7 CJK Ideograph-9DE7 | 9de6 CJK Ideograph-9DE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9de9 CJK Ideograph-9DE9 | 9de8 CJK Ideograph-9DE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9deb CJK Ideograph-9DEB | 9dea CJK Ideograph-9DEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ded CJK Ideograph-9DED | 9dec CJK Ideograph-9DEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9def CJK Ideograph-9DEF | 9dee CJK Ideograph-9DEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9df1 CJK Ideograph-9DF1 | 9df0 CJK Ideograph-9DF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9df3 CJK Ideograph-9DF3 | 9df2 CJK Ideograph-9DF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9df5 CJK Ideograph-9DF5 | 9df4 CJK Ideograph-9DF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9df7 CJK Ideograph-9DF7 | 9df6 CJK Ideograph-9DF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9df9 CJK Ideograph-9DF9 | 9df8 CJK Ideograph-9DF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dfb CJK Ideograph-9DFB | 9dfa CJK Ideograph-9DFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dfd CJK Ideograph-9DFD | 9dfc CJK Ideograph-9DFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dff CJK Ideograph-9DFF | 9dfe CJK Ideograph-9DFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e01 CJK Ideograph-9E01 | 9e00 CJK Ideograph-9E00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e03 CJK Ideograph-9E03 | 9e02 CJK Ideograph-9E02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e05 CJK Ideograph-9E05 | 9e04 CJK Ideograph-9E04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e07 CJK Ideograph-9E07 | 9e06 CJK Ideograph-9E06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e09 CJK Ideograph-9E09 | 9e08 CJK Ideograph-9E08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e0b CJK Ideograph-9E0B | 9e0a CJK Ideograph-9E0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e0d CJK Ideograph-9E0D | 9e0c CJK Ideograph-9E0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e0f CJK Ideograph-9E0F | 9e0e CJK Ideograph-9E0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e11 CJK Ideograph-9E11 | 9e10 CJK Ideograph-9E10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e13 CJK Ideograph-9E13 | 9e12 CJK Ideograph-9E12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e15 CJK Ideograph-9E15 | 9e14 CJK Ideograph-9E14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e17 CJK Ideograph-9E17 | 9e16 CJK Ideograph-9E16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e19 CJK Ideograph-9E19 | 9e18 CJK Ideograph-9E18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e1b CJK Ideograph-9E1B | 9e1a CJK Ideograph-9E1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e1d CJK Ideograph-9E1D | 9e1c CJK Ideograph-9E1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e1f CJK Ideograph-9E1F | 9e1e CJK Ideograph-9E1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e21 CJK Ideograph-9E21 | 9e20 CJK Ideograph-9E20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e23 CJK Ideograph-9E23 | 9e22 CJK Ideograph-9E22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e25 CJK Ideograph-9E25 | 9e24 CJK Ideograph-9E24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e27 CJK Ideograph-9E27 | 9e26 CJK Ideograph-9E26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e29 CJK Ideograph-9E29 | 9e28 CJK Ideograph-9E28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e2b CJK Ideograph-9E2B | 9e2a CJK Ideograph-9E2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e2d CJK Ideograph-9E2D | 9e2c CJK Ideograph-9E2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e2f CJK Ideograph-9E2F | 9e2e CJK Ideograph-9E2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e31 CJK Ideograph-9E31 | 9e30 CJK Ideograph-9E30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e33 CJK Ideograph-9E33 | 9e32 CJK Ideograph-9E32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e35 CJK Ideograph-9E35 | 9e34 CJK Ideograph-9E34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e37 CJK Ideograph-9E37 | 9e36 CJK Ideograph-9E36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e39 CJK Ideograph-9E39 | 9e38 CJK Ideograph-9E38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e3b CJK Ideograph-9E3B | 9e3a CJK Ideograph-9E3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e3d CJK Ideograph-9E3D | 9e3c CJK Ideograph-9E3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e3f CJK Ideograph-9E3F | 9e3e CJK Ideograph-9E3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e41 CJK Ideograph-9E41 | 9e40 CJK Ideograph-9E40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e43 CJK Ideograph-9E43 | 9e42 CJK Ideograph-9E42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e45 CJK Ideograph-9E45 | 9e44 CJK Ideograph-9E44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e47 CJK Ideograph-9E47 | 9e46 CJK Ideograph-9E46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e49 CJK Ideograph-9E49 | 9e48 CJK Ideograph-9E48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e4b CJK Ideograph-9E4B | 9e4a CJK Ideograph-9E4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e4d CJK Ideograph-9E4D | 9e4c CJK Ideograph-9E4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e4f CJK Ideograph-9E4F | 9e4e CJK Ideograph-9E4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e51 CJK Ideograph-9E51 | 9e50 CJK Ideograph-9E50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e53 CJK Ideograph-9E53 | 9e52 CJK Ideograph-9E52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e55 CJK Ideograph-9E55 | 9e54 CJK Ideograph-9E54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e57 CJK Ideograph-9E57 | 9e56 CJK Ideograph-9E56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e59 CJK Ideograph-9E59 | 9e58 CJK Ideograph-9E58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e5b CJK Ideograph-9E5B | 9e5a CJK Ideograph-9E5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e5d CJK Ideograph-9E5D | 9e5c CJK Ideograph-9E5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e5f CJK Ideograph-9E5F | 9e5e CJK Ideograph-9E5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e61 CJK Ideograph-9E61 | 9e60 CJK Ideograph-9E60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e63 CJK Ideograph-9E63 | 9e62 CJK Ideograph-9E62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e65 CJK Ideograph-9E65 | 9e64 CJK Ideograph-9E64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e67 CJK Ideograph-9E67 | 9e66 CJK Ideograph-9E66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e69 CJK Ideograph-9E69 | 9e68 CJK Ideograph-9E68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e6b CJK Ideograph-9E6B | 9e6a CJK Ideograph-9E6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e6d CJK Ideograph-9E6D | 9e6c CJK Ideograph-9E6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e6f CJK Ideograph-9E6F | 9e6e CJK Ideograph-9E6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e71 CJK Ideograph-9E71 | 9e70 CJK Ideograph-9E70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e73 CJK Ideograph-9E73 | 9e72 CJK Ideograph-9E72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e75 CJK Ideograph-9E75 | 9e74 CJK Ideograph-9E74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e77 CJK Ideograph-9E77 | 9e76 CJK Ideograph-9E76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e79 CJK Ideograph-9E79 | 9e78 CJK Ideograph-9E78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e7b CJK Ideograph-9E7B | 9e7a CJK Ideograph-9E7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e7d CJK Ideograph-9E7D | 9e7c CJK Ideograph-9E7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e7f CJK Ideograph-9E7F | 9e7e CJK Ideograph-9E7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e81 CJK Ideograph-9E81 | 9e80 CJK Ideograph-9E80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e83 CJK Ideograph-9E83 | 9e82 CJK Ideograph-9E82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e85 CJK Ideograph-9E85 | 9e84 CJK Ideograph-9E84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e87 CJK Ideograph-9E87 | 9e86 CJK Ideograph-9E86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e89 CJK Ideograph-9E89 | 9e88 CJK Ideograph-9E88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e8b CJK Ideograph-9E8B | 9e8a CJK Ideograph-9E8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e8d CJK Ideograph-9E8D | 9e8c CJK Ideograph-9E8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e8f CJK Ideograph-9E8F | 9e8e CJK Ideograph-9E8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e91 CJK Ideograph-9E91 | 9e90 CJK Ideograph-9E90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e93 CJK Ideograph-9E93 | 9e92 CJK Ideograph-9E92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e95 CJK Ideograph-9E95 | 9e94 CJK Ideograph-9E94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e97 CJK Ideograph-9E97 | 9e96 CJK Ideograph-9E96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e99 CJK Ideograph-9E99 | 9e98 CJK Ideograph-9E98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e9b CJK Ideograph-9E9B | 9e9a CJK Ideograph-9E9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e9d CJK Ideograph-9E9D | 9e9c CJK Ideograph-9E9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e9f CJK Ideograph-9E9F | 9e9e CJK Ideograph-9E9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ea1 CJK Ideograph-9EA1 | 9ea0 CJK Ideograph-9EA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ea3 CJK Ideograph-9EA3 | 9ea2 CJK Ideograph-9EA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ea5 CJK Ideograph-9EA5 | 9ea4 CJK Ideograph-9EA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ea7 CJK Ideograph-9EA7 | 9ea6 CJK Ideograph-9EA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ea9 CJK Ideograph-9EA9 | 9ea8 CJK Ideograph-9EA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eab CJK Ideograph-9EAB | 9eaa CJK Ideograph-9EAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ead CJK Ideograph-9EAD | 9eac CJK Ideograph-9EAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eaf CJK Ideograph-9EAF | 9eae CJK Ideograph-9EAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eb1 CJK Ideograph-9EB1 | 9eb0 CJK Ideograph-9EB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eb3 CJK Ideograph-9EB3 | 9eb2 CJK Ideograph-9EB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eb5 CJK Ideograph-9EB5 | 9eb4 CJK Ideograph-9EB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eb7 CJK Ideograph-9EB7 | 9eb6 CJK Ideograph-9EB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eb9 CJK Ideograph-9EB9 | 9eb8 CJK Ideograph-9EB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ebb CJK Ideograph-9EBB | 9eba CJK Ideograph-9EBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ebd CJK Ideograph-9EBD | 9ebc CJK Ideograph-9EBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ebf CJK Ideograph-9EBF | 9ebe CJK Ideograph-9EBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ec1 CJK Ideograph-9EC1 | 9ec0 CJK Ideograph-9EC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ec3 CJK Ideograph-9EC3 | 9ec2 CJK Ideograph-9EC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ec5 CJK Ideograph-9EC5 | 9ec4 CJK Ideograph-9EC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ec7 CJK Ideograph-9EC7 | 9ec6 CJK Ideograph-9EC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ec9 CJK Ideograph-9EC9 | 9ec8 CJK Ideograph-9EC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ecb CJK Ideograph-9ECB | 9eca CJK Ideograph-9ECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ecd CJK Ideograph-9ECD | 9ecc CJK Ideograph-9ECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ecf CJK Ideograph-9ECF | 9ece CJK Ideograph-9ECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ed1 CJK Ideograph-9ED1 | 9ed0 CJK Ideograph-9ED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ed3 CJK Ideograph-9ED3 | 9ed2 CJK Ideograph-9ED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ed5 CJK Ideograph-9ED5 | 9ed4 CJK Ideograph-9ED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ed7 CJK Ideograph-9ED7 | 9ed6 CJK Ideograph-9ED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ed9 CJK Ideograph-9ED9 | 9ed8 CJK Ideograph-9ED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9edb CJK Ideograph-9EDB | 9eda CJK Ideograph-9EDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9edd CJK Ideograph-9EDD | 9edc CJK Ideograph-9EDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9edf CJK Ideograph-9EDF | 9ede CJK Ideograph-9EDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ee1 CJK Ideograph-9EE1 | 9ee0 CJK Ideograph-9EE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ee3 CJK Ideograph-9EE3 | 9ee2 CJK Ideograph-9EE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ee5 CJK Ideograph-9EE5 | 9ee4 CJK Ideograph-9EE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ee7 CJK Ideograph-9EE7 | 9ee6 CJK Ideograph-9EE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ee9 CJK Ideograph-9EE9 | 9ee8 CJK Ideograph-9EE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eeb CJK Ideograph-9EEB | 9eea CJK Ideograph-9EEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eed CJK Ideograph-9EED | 9eec CJK Ideograph-9EEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eef CJK Ideograph-9EEF | 9eee CJK Ideograph-9EEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ef1 CJK Ideograph-9EF1 | 9ef0 CJK Ideograph-9EF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ef3 CJK Ideograph-9EF3 | 9ef2 CJK Ideograph-9EF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ef5 CJK Ideograph-9EF5 | 9ef4 CJK Ideograph-9EF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ef7 CJK Ideograph-9EF7 | 9ef6 CJK Ideograph-9EF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ef9 CJK Ideograph-9EF9 | 9ef8 CJK Ideograph-9EF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9efb CJK Ideograph-9EFB | 9efa CJK Ideograph-9EFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9efd CJK Ideograph-9EFD | 9efc CJK Ideograph-9EFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eff CJK Ideograph-9EFF | 9efe CJK Ideograph-9EFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f01 CJK Ideograph-9F01 | 9f00 CJK Ideograph-9F00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f03 CJK Ideograph-9F03 | 9f02 CJK Ideograph-9F02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f05 CJK Ideograph-9F05 | 9f04 CJK Ideograph-9F04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f07 CJK Ideograph-9F07 | 9f06 CJK Ideograph-9F06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f09 CJK Ideograph-9F09 | 9f08 CJK Ideograph-9F08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f0b CJK Ideograph-9F0B | 9f0a CJK Ideograph-9F0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f0d CJK Ideograph-9F0D | 9f0c CJK Ideograph-9F0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f0f CJK Ideograph-9F0F | 9f0e CJK Ideograph-9F0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f11 CJK Ideograph-9F11 | 9f10 CJK Ideograph-9F10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f13 CJK Ideograph-9F13 | 9f12 CJK Ideograph-9F12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f15 CJK Ideograph-9F15 | 9f14 CJK Ideograph-9F14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f17 CJK Ideograph-9F17 | 9f16 CJK Ideograph-9F16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f19 CJK Ideograph-9F19 | 9f18 CJK Ideograph-9F18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f1b CJK Ideograph-9F1B | 9f1a CJK Ideograph-9F1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f1d CJK Ideograph-9F1D | 9f1c CJK Ideograph-9F1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f1f CJK Ideograph-9F1F | 9f1e CJK Ideograph-9F1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f21 CJK Ideograph-9F21 | 9f20 CJK Ideograph-9F20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f23 CJK Ideograph-9F23 | 9f22 CJK Ideograph-9F22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f25 CJK Ideograph-9F25 | 9f24 CJK Ideograph-9F24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f27 CJK Ideograph-9F27 | 9f26 CJK Ideograph-9F26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f29 CJK Ideograph-9F29 | 9f28 CJK Ideograph-9F28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f2b CJK Ideograph-9F2B | 9f2a CJK Ideograph-9F2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f2d CJK Ideograph-9F2D | 9f2c CJK Ideograph-9F2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f2f CJK Ideograph-9F2F | 9f2e CJK Ideograph-9F2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f31 CJK Ideograph-9F31 | 9f30 CJK Ideograph-9F30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f33 CJK Ideograph-9F33 | 9f32 CJK Ideograph-9F32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f35 CJK Ideograph-9F35 | 9f34 CJK Ideograph-9F34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f37 CJK Ideograph-9F37 | 9f36 CJK Ideograph-9F36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f39 CJK Ideograph-9F39 | 9f38 CJK Ideograph-9F38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f3b CJK Ideograph-9F3B | 9f3a CJK Ideograph-9F3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f3d CJK Ideograph-9F3D | 9f3c CJK Ideograph-9F3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f3f CJK Ideograph-9F3F | 9f3e CJK Ideograph-9F3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f41 CJK Ideograph-9F41 | 9f40 CJK Ideograph-9F40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f43 CJK Ideograph-9F43 | 9f42 CJK Ideograph-9F42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f45 CJK Ideograph-9F45 | 9f44 CJK Ideograph-9F44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f47 CJK Ideograph-9F47 | 9f46 CJK Ideograph-9F46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f49 CJK Ideograph-9F49 | 9f48 CJK Ideograph-9F48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f4b CJK Ideograph-9F4B | 9f4a CJK Ideograph-9F4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f4d CJK Ideograph-9F4D | 9f4c CJK Ideograph-9F4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f4f CJK Ideograph-9F4F | 9f4e CJK Ideograph-9F4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f51 CJK Ideograph-9F51 | 9f50 CJK Ideograph-9F50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f53 CJK Ideograph-9F53 | 9f52 CJK Ideograph-9F52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f55 CJK Ideograph-9F55 | 9f54 CJK Ideograph-9F54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f57 CJK Ideograph-9F57 | 9f56 CJK Ideograph-9F56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f59 CJK Ideograph-9F59 | 9f58 CJK Ideograph-9F58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f5b CJK Ideograph-9F5B | 9f5a CJK Ideograph-9F5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f5d CJK Ideograph-9F5D | 9f5c CJK Ideograph-9F5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f5f CJK Ideograph-9F5F | 9f5e CJK Ideograph-9F5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f61 CJK Ideograph-9F61 | 9f60 CJK Ideograph-9F60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f63 CJK Ideograph-9F63 | 9f62 CJK Ideograph-9F62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f65 CJK Ideograph-9F65 | 9f64 CJK Ideograph-9F64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f67 CJK Ideograph-9F67 | 9f66 CJK Ideograph-9F66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f69 CJK Ideograph-9F69 | 9f68 CJK Ideograph-9F68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f6b CJK Ideograph-9F6B | 9f6a CJK Ideograph-9F6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f6d CJK Ideograph-9F6D | 9f6c CJK Ideograph-9F6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f6f CJK Ideograph-9F6F | 9f6e CJK Ideograph-9F6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f71 CJK Ideograph-9F71 | 9f70 CJK Ideograph-9F70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f73 CJK Ideograph-9F73 | 9f72 CJK Ideograph-9F72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f75 CJK Ideograph-9F75 | 9f74 CJK Ideograph-9F74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f77 CJK Ideograph-9F77 | 9f76 CJK Ideograph-9F76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f79 CJK Ideograph-9F79 | 9f78 CJK Ideograph-9F78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f7b CJK Ideograph-9F7B | 9f7a CJK Ideograph-9F7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f7d CJK Ideograph-9F7D | 9f7c CJK Ideograph-9F7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f7f CJK Ideograph-9F7F | 9f7e CJK Ideograph-9F7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f81 CJK Ideograph-9F81 | 9f80 CJK Ideograph-9F80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f83 CJK Ideograph-9F83 | 9f82 CJK Ideograph-9F82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f85 CJK Ideograph-9F85 | 9f84 CJK Ideograph-9F84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f87 CJK Ideograph-9F87 | 9f86 CJK Ideograph-9F86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f89 CJK Ideograph-9F89 | 9f88 CJK Ideograph-9F88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f8b CJK Ideograph-9F8B | 9f8a CJK Ideograph-9F8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f8d CJK Ideograph-9F8D | 9f8c CJK Ideograph-9F8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f8f CJK Ideograph-9F8F | 9f8e CJK Ideograph-9F8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f91 CJK Ideograph-9F91 | 9f90 CJK Ideograph-9F90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f93 CJK Ideograph-9F93 | 9f92 CJK Ideograph-9F92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f95 CJK Ideograph-9F95 | 9f94 CJK Ideograph-9F94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f97 CJK Ideograph-9F97 | 9f96 CJK Ideograph-9F96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f99 CJK Ideograph-9F99 | 9f98 CJK Ideograph-9F98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f9b CJK Ideograph-9F9B | 9f9a CJK Ideograph-9F9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f9d CJK Ideograph-9F9D | 9f9c CJK Ideograph-9F9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f9f CJK Ideograph-9F9F | 9f9e CJK Ideograph-9F9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fa1 CJK Ideograph-9FA1 | 9fa0 CJK Ideograph-9FA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fa3 CJK Ideograph-9FA3 | 9fa2 CJK Ideograph-9FA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fa5 CJK Ideograph-9FA5 | 9fa4 CJK Ideograph-9FA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fa7 CJK Ideograph-9FA7 | 9fa6 CJK Ideograph-9FA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fa9 CJK Ideograph-9FA9 | 9fa8 CJK Ideograph-9FA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fab CJK Ideograph-9FAB | 9faa CJK Ideograph-9FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fad CJK Ideograph-9FAD | 9fac CJK Ideograph-9FAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9faf CJK Ideograph-9FAF | 9fae CJK Ideograph-9FAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fb1 CJK Ideograph-9FB1 | 9fb0 CJK Ideograph-9FB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fb3 CJK Ideograph-9FB3 | 9fb2 CJK Ideograph-9FB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fb5 CJK Ideograph-9FB5 | 9fb4 CJK Ideograph-9FB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fb7 CJK Ideograph-9FB7 | 9fb6 CJK Ideograph-9FB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fb9 CJK Ideograph-9FB9 | 9fb8 CJK Ideograph-9FB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fbb CJK Ideograph-9FBB | 9fba CJK Ideograph-9FBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fbd CJK Ideograph-9FBD | 9fbc CJK Ideograph-9FBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fbf CJK Ideograph-9FBF | 9fbe CJK Ideograph-9FBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fc1 CJK Ideograph-9FC1 | 9fc0 CJK Ideograph-9FC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fc3 CJK Ideograph-9FC3 | 9fc2 CJK Ideograph-9FC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fc5 CJK Ideograph-9FC5 | 9fc4 CJK Ideograph-9FC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fc7 CJK Ideograph-9FC7 | 9fc6 CJK Ideograph-9FC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fc9 CJK Ideograph-9FC9 | 9fc8 CJK Ideograph-9FC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fcb CJK Ideograph-9FCB | 9fca CJK Ideograph-9FCA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 9fcd (null) | 9fcc CJK Ideograph-9FCC */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fcf (null) | 9fce (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fd1 (null) | 9fd0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fd3 (null) | 9fd2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fd5 (null) | 9fd4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fd7 (null) | 9fd6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fd9 (null) | 9fd8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fdb (null) | 9fda (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fdd (null) | 9fdc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fdf (null) | 9fde (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fe1 (null) | 9fe0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fe3 (null) | 9fe2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fe5 (null) | 9fe4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fe7 (null) | 9fe6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fe9 (null) | 9fe8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9feb (null) | 9fea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fed (null) | 9fec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fef (null) | 9fee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9ff1 (null) | 9ff0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9ff3 (null) | 9ff2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9ff5 (null) | 9ff4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9ff7 (null) | 9ff6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9ff9 (null) | 9ff8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9ffb (null) | 9ffa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9ffd (null) | 9ffc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fff (null) | 9ffe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a001 YI SYLLABLE IX | a000 YI SYLLABLE IT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a003 YI SYLLABLE IP | a002 YI SYLLABLE I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a005 YI SYLLABLE IEX | a004 YI SYLLABLE IET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a007 YI SYLLABLE IEP | a006 YI SYLLABLE IE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a009 YI SYLLABLE AX | a008 YI SYLLABLE AT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a00b YI SYLLABLE AP | a00a YI SYLLABLE A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a00d YI SYLLABLE UO | a00c YI SYLLABLE UOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a00f YI SYLLABLE OT | a00e YI SYLLABLE UOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a011 YI SYLLABLE O | a010 YI SYLLABLE OX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a013 YI SYLLABLE EX | a012 YI SYLLABLE OP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* a015 YI SYLLABLE WU | a014 YI SYLLABLE E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a017 YI SYLLABLE BIX | a016 YI SYLLABLE BIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a019 YI SYLLABLE BIP | a018 YI SYLLABLE BI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a01b YI SYLLABLE BIEX | a01a YI SYLLABLE BIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a01d YI SYLLABLE BIEP | a01c YI SYLLABLE BIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a01f YI SYLLABLE BAX | a01e YI SYLLABLE BAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a021 YI SYLLABLE BAP | a020 YI SYLLABLE BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a023 YI SYLLABLE BUO | a022 YI SYLLABLE BUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a025 YI SYLLABLE BOT | a024 YI SYLLABLE BUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a027 YI SYLLABLE BO | a026 YI SYLLABLE BOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a029 YI SYLLABLE BEX | a028 YI SYLLABLE BOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a02b YI SYLLABLE BEP | a02a YI SYLLABLE BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a02d YI SYLLABLE BUX | a02c YI SYLLABLE BUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a02f YI SYLLABLE BUP | a02e YI SYLLABLE BU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a031 YI SYLLABLE BUR | a030 YI SYLLABLE BURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a033 YI SYLLABLE BYX | a032 YI SYLLABLE BYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a035 YI SYLLABLE BYP | a034 YI SYLLABLE BY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a037 YI SYLLABLE BYR | a036 YI SYLLABLE BYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a039 YI SYLLABLE PIX | a038 YI SYLLABLE PIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a03b YI SYLLABLE PIP | a03a YI SYLLABLE PI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a03d YI SYLLABLE PIE | a03c YI SYLLABLE PIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a03f YI SYLLABLE PAT | a03e YI SYLLABLE PIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a041 YI SYLLABLE PA | a040 YI SYLLABLE PAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a043 YI SYLLABLE PUOX | a042 YI SYLLABLE PAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a045 YI SYLLABLE PUOP | a044 YI SYLLABLE PUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a047 YI SYLLABLE POX | a046 YI SYLLABLE POT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a049 YI SYLLABLE POP | a048 YI SYLLABLE PO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a04b YI SYLLABLE PUX | a04a YI SYLLABLE PUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a04d YI SYLLABLE PUP | a04c YI SYLLABLE PU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a04f YI SYLLABLE PUR | a04e YI SYLLABLE PURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a051 YI SYLLABLE PYX | a050 YI SYLLABLE PYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a053 YI SYLLABLE PYP | a052 YI SYLLABLE PY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a055 YI SYLLABLE PYR | a054 YI SYLLABLE PYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a057 YI SYLLABLE BBIX | a056 YI SYLLABLE BBIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a059 YI SYLLABLE BBIP | a058 YI SYLLABLE BBI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a05b YI SYLLABLE BBIEX | a05a YI SYLLABLE BBIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a05d YI SYLLABLE BBIEP | a05c YI SYLLABLE BBIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a05f YI SYLLABLE BBAX | a05e YI SYLLABLE BBAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a061 YI SYLLABLE BBAP | a060 YI SYLLABLE BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a063 YI SYLLABLE BBUO | a062 YI SYLLABLE BBUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a065 YI SYLLABLE BBOT | a064 YI SYLLABLE BBUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a067 YI SYLLABLE BBO | a066 YI SYLLABLE BBOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a069 YI SYLLABLE BBEX | a068 YI SYLLABLE BBOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a06b YI SYLLABLE BBEP | a06a YI SYLLABLE BBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a06d YI SYLLABLE BBUX | a06c YI SYLLABLE BBUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a06f YI SYLLABLE BBUP | a06e YI SYLLABLE BBU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a071 YI SYLLABLE BBUR | a070 YI SYLLABLE BBURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a073 YI SYLLABLE BBYX | a072 YI SYLLABLE BBYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a075 YI SYLLABLE BBYP | a074 YI SYLLABLE BBY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a077 YI SYLLABLE NBIX | a076 YI SYLLABLE NBIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a079 YI SYLLABLE NBIP | a078 YI SYLLABLE NBI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a07b YI SYLLABLE NBIE | a07a YI SYLLABLE NBIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a07d YI SYLLABLE NBAT | a07c YI SYLLABLE NBIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a07f YI SYLLABLE NBA | a07e YI SYLLABLE NBAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a081 YI SYLLABLE NBOT | a080 YI SYLLABLE NBAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a083 YI SYLLABLE NBO | a082 YI SYLLABLE NBOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a085 YI SYLLABLE NBUT | a084 YI SYLLABLE NBOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a087 YI SYLLABLE NBU | a086 YI SYLLABLE NBUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a089 YI SYLLABLE NBURX | a088 YI SYLLABLE NBUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a08b YI SYLLABLE NBYT | a08a YI SYLLABLE NBUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a08d YI SYLLABLE NBY | a08c YI SYLLABLE NBYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a08f YI SYLLABLE NBYRX | a08e YI SYLLABLE NBYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a091 YI SYLLABLE HMIT | a090 YI SYLLABLE NBYR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a093 YI SYLLABLE HMI | a092 YI SYLLABLE HMIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a095 YI SYLLABLE HMIEX | a094 YI SYLLABLE HMIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a097 YI SYLLABLE HMIEP | a096 YI SYLLABLE HMIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a099 YI SYLLABLE HMAX | a098 YI SYLLABLE HMAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a09b YI SYLLABLE HMAP | a09a YI SYLLABLE HMA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a09d YI SYLLABLE HMUO | a09c YI SYLLABLE HMUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a09f YI SYLLABLE HMOT | a09e YI SYLLABLE HMUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0a1 YI SYLLABLE HMO | a0a0 YI SYLLABLE HMOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0a3 YI SYLLABLE HMUT | a0a2 YI SYLLABLE HMOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0a5 YI SYLLABLE HMU | a0a4 YI SYLLABLE HMUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0a7 YI SYLLABLE HMURX | a0a6 YI SYLLABLE HMUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0a9 YI SYLLABLE HMYX | a0a8 YI SYLLABLE HMUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0ab YI SYLLABLE HMYP | a0aa YI SYLLABLE HMY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0ad YI SYLLABLE HMYR | a0ac YI SYLLABLE HMYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0af YI SYLLABLE MIX | a0ae YI SYLLABLE MIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0b1 YI SYLLABLE MIP | a0b0 YI SYLLABLE MI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0b3 YI SYLLABLE MIE | a0b2 YI SYLLABLE MIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0b5 YI SYLLABLE MAT | a0b4 YI SYLLABLE MIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0b7 YI SYLLABLE MA | a0b6 YI SYLLABLE MAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0b9 YI SYLLABLE MUOT | a0b8 YI SYLLABLE MAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0bb YI SYLLABLE MUO | a0ba YI SYLLABLE MUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0bd YI SYLLABLE MOT | a0bc YI SYLLABLE MUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0bf YI SYLLABLE MO | a0be YI SYLLABLE MOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0c1 YI SYLLABLE MEX | a0c0 YI SYLLABLE MOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0c3 YI SYLLABLE MUT | a0c2 YI SYLLABLE ME */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0c5 YI SYLLABLE MU | a0c4 YI SYLLABLE MUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0c7 YI SYLLABLE MURX | a0c6 YI SYLLABLE MUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0c9 YI SYLLABLE MYT | a0c8 YI SYLLABLE MUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0cb YI SYLLABLE MY | a0ca YI SYLLABLE MYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0cd YI SYLLABLE FIT | a0cc YI SYLLABLE MYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0cf YI SYLLABLE FI | a0ce YI SYLLABLE FIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0d1 YI SYLLABLE FAT | a0d0 YI SYLLABLE FIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0d3 YI SYLLABLE FA | a0d2 YI SYLLABLE FAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0d5 YI SYLLABLE FOX | a0d4 YI SYLLABLE FAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0d7 YI SYLLABLE FOP | a0d6 YI SYLLABLE FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0d9 YI SYLLABLE FUX | a0d8 YI SYLLABLE FUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0db YI SYLLABLE FUP | a0da YI SYLLABLE FU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0dd YI SYLLABLE FUR | a0dc YI SYLLABLE FURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0df YI SYLLABLE FYX | a0de YI SYLLABLE FYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0e1 YI SYLLABLE FYP | a0e0 YI SYLLABLE FY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0e3 YI SYLLABLE VIX | a0e2 YI SYLLABLE VIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0e5 YI SYLLABLE VIP | a0e4 YI SYLLABLE VI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0e7 YI SYLLABLE VIEX | a0e6 YI SYLLABLE VIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0e9 YI SYLLABLE VIEP | a0e8 YI SYLLABLE VIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0eb YI SYLLABLE VAX | a0ea YI SYLLABLE VAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0ed YI SYLLABLE VAP | a0ec YI SYLLABLE VA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0ef YI SYLLABLE VOX | a0ee YI SYLLABLE VOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0f1 YI SYLLABLE VOP | a0f0 YI SYLLABLE VO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0f3 YI SYLLABLE VEP | a0f2 YI SYLLABLE VEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0f5 YI SYLLABLE VUX | a0f4 YI SYLLABLE VUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0f7 YI SYLLABLE VUP | a0f6 YI SYLLABLE VU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0f9 YI SYLLABLE VUR | a0f8 YI SYLLABLE VURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0fb YI SYLLABLE VYX | a0fa YI SYLLABLE VYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0fd YI SYLLABLE VYP | a0fc YI SYLLABLE VY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0ff YI SYLLABLE VYR | a0fe YI SYLLABLE VYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a101 YI SYLLABLE DIX | a100 YI SYLLABLE DIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a103 YI SYLLABLE DIP | a102 YI SYLLABLE DI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a105 YI SYLLABLE DIE | a104 YI SYLLABLE DIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a107 YI SYLLABLE DAT | a106 YI SYLLABLE DIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a109 YI SYLLABLE DA | a108 YI SYLLABLE DAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a10b YI SYLLABLE DUOX | a10a YI SYLLABLE DAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a10d YI SYLLABLE DOT | a10c YI SYLLABLE DUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a10f YI SYLLABLE DO | a10e YI SYLLABLE DOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a111 YI SYLLABLE DEX | a110 YI SYLLABLE DOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a113 YI SYLLABLE DEP | a112 YI SYLLABLE DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a115 YI SYLLABLE DUX | a114 YI SYLLABLE DUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a117 YI SYLLABLE DUP | a116 YI SYLLABLE DU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a119 YI SYLLABLE DUR | a118 YI SYLLABLE DURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a11b YI SYLLABLE TIX | a11a YI SYLLABLE TIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a11d YI SYLLABLE TIP | a11c YI SYLLABLE TI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a11f YI SYLLABLE TIE | a11e YI SYLLABLE TIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a121 YI SYLLABLE TAT | a120 YI SYLLABLE TIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a123 YI SYLLABLE TA | a122 YI SYLLABLE TAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a125 YI SYLLABLE TUOT | a124 YI SYLLABLE TAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a127 YI SYLLABLE TUO | a126 YI SYLLABLE TUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a129 YI SYLLABLE TOT | a128 YI SYLLABLE TUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a12b YI SYLLABLE TO | a12a YI SYLLABLE TOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a12d YI SYLLABLE TEX | a12c YI SYLLABLE TOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a12f YI SYLLABLE TEP | a12e YI SYLLABLE TE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a131 YI SYLLABLE TUX | a130 YI SYLLABLE TUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a133 YI SYLLABLE TUP | a132 YI SYLLABLE TU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a135 YI SYLLABLE TUR | a134 YI SYLLABLE TURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a137 YI SYLLABLE DDIX | a136 YI SYLLABLE DDIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a139 YI SYLLABLE DDIP | a138 YI SYLLABLE DDI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a13b YI SYLLABLE DDIE | a13a YI SYLLABLE DDIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a13d YI SYLLABLE DDAT | a13c YI SYLLABLE DDIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a13f YI SYLLABLE DDA | a13e YI SYLLABLE DDAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a141 YI SYLLABLE DDUOX | a140 YI SYLLABLE DDAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a143 YI SYLLABLE DDUOP | a142 YI SYLLABLE DDUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a145 YI SYLLABLE DDOX | a144 YI SYLLABLE DDOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a147 YI SYLLABLE DDOP | a146 YI SYLLABLE DDO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a149 YI SYLLABLE DDE | a148 YI SYLLABLE DDEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a14b YI SYLLABLE DDUT | a14a YI SYLLABLE DDEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a14d YI SYLLABLE DDU | a14c YI SYLLABLE DDUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a14f YI SYLLABLE DDURX | a14e YI SYLLABLE DDUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a151 YI SYLLABLE NDIT | a150 YI SYLLABLE DDUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a153 YI SYLLABLE NDI | a152 YI SYLLABLE NDIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a155 YI SYLLABLE NDIEX | a154 YI SYLLABLE NDIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a157 YI SYLLABLE NDAT | a156 YI SYLLABLE NDIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a159 YI SYLLABLE NDA | a158 YI SYLLABLE NDAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a15b YI SYLLABLE NDOT | a15a YI SYLLABLE NDAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a15d YI SYLLABLE NDO | a15c YI SYLLABLE NDOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a15f YI SYLLABLE NDEX | a15e YI SYLLABLE NDOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a161 YI SYLLABLE NDEP | a160 YI SYLLABLE NDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a163 YI SYLLABLE NDUX | a162 YI SYLLABLE NDUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a165 YI SYLLABLE NDUP | a164 YI SYLLABLE NDU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a167 YI SYLLABLE NDUR | a166 YI SYLLABLE NDURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a169 YI SYLLABLE HNIX | a168 YI SYLLABLE HNIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a16b YI SYLLABLE HNIP | a16a YI SYLLABLE HNI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a16d YI SYLLABLE HNIEX | a16c YI SYLLABLE HNIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a16f YI SYLLABLE HNIEP | a16e YI SYLLABLE HNIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a171 YI SYLLABLE HNAX | a170 YI SYLLABLE HNAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a173 YI SYLLABLE HNAP | a172 YI SYLLABLE HNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a175 YI SYLLABLE HNUO | a174 YI SYLLABLE HNUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a177 YI SYLLABLE HNOX | a176 YI SYLLABLE HNOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a179 YI SYLLABLE HNEX | a178 YI SYLLABLE HNOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a17b YI SYLLABLE HNEP | a17a YI SYLLABLE HNE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a17d YI SYLLABLE NIT | a17c YI SYLLABLE HNUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a17f YI SYLLABLE NI | a17e YI SYLLABLE NIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a181 YI SYLLABLE NIEX | a180 YI SYLLABLE NIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a183 YI SYLLABLE NIEP | a182 YI SYLLABLE NIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a185 YI SYLLABLE NA | a184 YI SYLLABLE NAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a187 YI SYLLABLE NUOX | a186 YI SYLLABLE NAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a189 YI SYLLABLE NUOP | a188 YI SYLLABLE NUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a18b YI SYLLABLE NOX | a18a YI SYLLABLE NOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a18d YI SYLLABLE NOP | a18c YI SYLLABLE NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a18f YI SYLLABLE NE | a18e YI SYLLABLE NEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a191 YI SYLLABLE NUT | a190 YI SYLLABLE NEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a193 YI SYLLABLE NU | a192 YI SYLLABLE NUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a195 YI SYLLABLE NURX | a194 YI SYLLABLE NUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a197 YI SYLLABLE HLIT | a196 YI SYLLABLE NUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a199 YI SYLLABLE HLI | a198 YI SYLLABLE HLIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a19b YI SYLLABLE HLIEX | a19a YI SYLLABLE HLIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a19d YI SYLLABLE HLIEP | a19c YI SYLLABLE HLIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a19f YI SYLLABLE HLAX | a19e YI SYLLABLE HLAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1a1 YI SYLLABLE HLAP | a1a0 YI SYLLABLE HLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1a3 YI SYLLABLE HLUO | a1a2 YI SYLLABLE HLUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1a5 YI SYLLABLE HLOX | a1a4 YI SYLLABLE HLUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1a7 YI SYLLABLE HLOP | a1a6 YI SYLLABLE HLO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1a9 YI SYLLABLE HLE | a1a8 YI SYLLABLE HLEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1ab YI SYLLABLE HLUT | a1aa YI SYLLABLE HLEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1ad YI SYLLABLE HLU | a1ac YI SYLLABLE HLUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1af YI SYLLABLE HLURX | a1ae YI SYLLABLE HLUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1b1 YI SYLLABLE HLYT | a1b0 YI SYLLABLE HLUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1b3 YI SYLLABLE HLY | a1b2 YI SYLLABLE HLYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1b5 YI SYLLABLE HLYRX | a1b4 YI SYLLABLE HLYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1b7 YI SYLLABLE LIT | a1b6 YI SYLLABLE HLYR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1b9 YI SYLLABLE LI | a1b8 YI SYLLABLE LIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1bb YI SYLLABLE LIET | a1ba YI SYLLABLE LIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1bd YI SYLLABLE LIE | a1bc YI SYLLABLE LIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1bf YI SYLLABLE LAT | a1be YI SYLLABLE LIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1c1 YI SYLLABLE LA | a1c0 YI SYLLABLE LAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1c3 YI SYLLABLE LUOT | a1c2 YI SYLLABLE LAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1c5 YI SYLLABLE LUO | a1c4 YI SYLLABLE LUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1c7 YI SYLLABLE LOT | a1c6 YI SYLLABLE LUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1c9 YI SYLLABLE LO | a1c8 YI SYLLABLE LOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1cb YI SYLLABLE LEX | a1ca YI SYLLABLE LOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1cd YI SYLLABLE LEP | a1cc YI SYLLABLE LE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1cf YI SYLLABLE LUX | a1ce YI SYLLABLE LUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1d1 YI SYLLABLE LUP | a1d0 YI SYLLABLE LU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1d3 YI SYLLABLE LUR | a1d2 YI SYLLABLE LURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1d5 YI SYLLABLE LYX | a1d4 YI SYLLABLE LYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1d7 YI SYLLABLE LYP | a1d6 YI SYLLABLE LY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1d9 YI SYLLABLE LYR | a1d8 YI SYLLABLE LYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1db YI SYLLABLE GIX | a1da YI SYLLABLE GIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1dd YI SYLLABLE GIP | a1dc YI SYLLABLE GI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1df YI SYLLABLE GIEX | a1de YI SYLLABLE GIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1e1 YI SYLLABLE GIEP | a1e0 YI SYLLABLE GIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1e3 YI SYLLABLE GAX | a1e2 YI SYLLABLE GAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1e5 YI SYLLABLE GAP | a1e4 YI SYLLABLE GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1e7 YI SYLLABLE GUOX | a1e6 YI SYLLABLE GUOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1e9 YI SYLLABLE GUOP | a1e8 YI SYLLABLE GUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1eb YI SYLLABLE GOX | a1ea YI SYLLABLE GOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1ed YI SYLLABLE GOP | a1ec YI SYLLABLE GO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1ef YI SYLLABLE GEX | a1ee YI SYLLABLE GET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1f1 YI SYLLABLE GEP | a1f0 YI SYLLABLE GE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1f3 YI SYLLABLE GUX | a1f2 YI SYLLABLE GUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1f5 YI SYLLABLE GUP | a1f4 YI SYLLABLE GU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1f7 YI SYLLABLE GUR | a1f6 YI SYLLABLE GURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1f9 YI SYLLABLE KIX | a1f8 YI SYLLABLE KIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1fb YI SYLLABLE KIP | a1fa YI SYLLABLE KI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1fd YI SYLLABLE KIE | a1fc YI SYLLABLE KIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1ff YI SYLLABLE KAT | a1fe YI SYLLABLE KIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a201 YI SYLLABLE KA | a200 YI SYLLABLE KAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a203 YI SYLLABLE KUOX | a202 YI SYLLABLE KAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a205 YI SYLLABLE KUOP | a204 YI SYLLABLE KUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a207 YI SYLLABLE KOX | a206 YI SYLLABLE KOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a209 YI SYLLABLE KOP | a208 YI SYLLABLE KO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a20b YI SYLLABLE KEX | a20a YI SYLLABLE KET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a20d YI SYLLABLE KEP | a20c YI SYLLABLE KE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a20f YI SYLLABLE KUX | a20e YI SYLLABLE KUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a211 YI SYLLABLE KUP | a210 YI SYLLABLE KU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a213 YI SYLLABLE KUR | a212 YI SYLLABLE KURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a215 YI SYLLABLE GGIX | a214 YI SYLLABLE GGIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a217 YI SYLLABLE GGIEX | a216 YI SYLLABLE GGI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a219 YI SYLLABLE GGIEP | a218 YI SYLLABLE GGIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a21b YI SYLLABLE GGAX | a21a YI SYLLABLE GGAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a21d YI SYLLABLE GGAP | a21c YI SYLLABLE GGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a21f YI SYLLABLE GGUOX | a21e YI SYLLABLE GGUOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a221 YI SYLLABLE GGUOP | a220 YI SYLLABLE GGUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a223 YI SYLLABLE GGOX | a222 YI SYLLABLE GGOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a225 YI SYLLABLE GGOP | a224 YI SYLLABLE GGO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a227 YI SYLLABLE GGEX | a226 YI SYLLABLE GGET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a229 YI SYLLABLE GGEP | a228 YI SYLLABLE GGE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a22b YI SYLLABLE GGUX | a22a YI SYLLABLE GGUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a22d YI SYLLABLE GGUP | a22c YI SYLLABLE GGU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a22f YI SYLLABLE GGUR | a22e YI SYLLABLE GGURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a231 YI SYLLABLE MGIE | a230 YI SYLLABLE MGIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a233 YI SYLLABLE MGAX | a232 YI SYLLABLE MGAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a235 YI SYLLABLE MGAP | a234 YI SYLLABLE MGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a237 YI SYLLABLE MGUO | a236 YI SYLLABLE MGUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a239 YI SYLLABLE MGOT | a238 YI SYLLABLE MGUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a23b YI SYLLABLE MGO | a23a YI SYLLABLE MGOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a23d YI SYLLABLE MGEX | a23c YI SYLLABLE MGOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a23f YI SYLLABLE MGEP | a23e YI SYLLABLE MGE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a241 YI SYLLABLE MGUX | a240 YI SYLLABLE MGUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a243 YI SYLLABLE MGUP | a242 YI SYLLABLE MGU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a245 YI SYLLABLE MGUR | a244 YI SYLLABLE MGURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a247 YI SYLLABLE HXIX | a246 YI SYLLABLE HXIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a249 YI SYLLABLE HXIP | a248 YI SYLLABLE HXI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a24b YI SYLLABLE HXIEX | a24a YI SYLLABLE HXIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a24d YI SYLLABLE HXIEP | a24c YI SYLLABLE HXIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a24f YI SYLLABLE HXAX | a24e YI SYLLABLE HXAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a251 YI SYLLABLE HXAP | a250 YI SYLLABLE HXA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a253 YI SYLLABLE HXUOX | a252 YI SYLLABLE HXUOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a255 YI SYLLABLE HXUOP | a254 YI SYLLABLE HXUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a257 YI SYLLABLE HXOX | a256 YI SYLLABLE HXOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a259 YI SYLLABLE HXOP | a258 YI SYLLABLE HXO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a25b YI SYLLABLE HXE | a25a YI SYLLABLE HXEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a25d YI SYLLABLE NGIEX | a25c YI SYLLABLE HXEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a25f YI SYLLABLE NGIEP | a25e YI SYLLABLE NGIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a261 YI SYLLABLE NGAX | a260 YI SYLLABLE NGAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a263 YI SYLLABLE NGAP | a262 YI SYLLABLE NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a265 YI SYLLABLE NGUOX | a264 YI SYLLABLE NGUOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a267 YI SYLLABLE NGOT | a266 YI SYLLABLE NGUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a269 YI SYLLABLE NGO | a268 YI SYLLABLE NGOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a26b YI SYLLABLE NGEX | a26a YI SYLLABLE NGOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a26d YI SYLLABLE NGEP | a26c YI SYLLABLE NGE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a26f YI SYLLABLE HIEX | a26e YI SYLLABLE HIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a271 YI SYLLABLE HAT | a270 YI SYLLABLE HIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a273 YI SYLLABLE HA | a272 YI SYLLABLE HAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a275 YI SYLLABLE HUOT | a274 YI SYLLABLE HAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a277 YI SYLLABLE HUO | a276 YI SYLLABLE HUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a279 YI SYLLABLE HOT | a278 YI SYLLABLE HUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a27b YI SYLLABLE HO | a27a YI SYLLABLE HOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a27d YI SYLLABLE HEX | a27c YI SYLLABLE HOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a27f YI SYLLABLE HEP | a27e YI SYLLABLE HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a281 YI SYLLABLE WAX | a280 YI SYLLABLE WAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a283 YI SYLLABLE WAP | a282 YI SYLLABLE WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a285 YI SYLLABLE WUO | a284 YI SYLLABLE WUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a287 YI SYLLABLE WOX | a286 YI SYLLABLE WUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a289 YI SYLLABLE WOP | a288 YI SYLLABLE WO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a28b YI SYLLABLE WE | a28a YI SYLLABLE WEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a28d YI SYLLABLE ZIT | a28c YI SYLLABLE WEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a28f YI SYLLABLE ZI | a28e YI SYLLABLE ZIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a291 YI SYLLABLE ZIEX | a290 YI SYLLABLE ZIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a293 YI SYLLABLE ZIEP | a292 YI SYLLABLE ZIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a295 YI SYLLABLE ZAX | a294 YI SYLLABLE ZAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a297 YI SYLLABLE ZAP | a296 YI SYLLABLE ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a299 YI SYLLABLE ZUO | a298 YI SYLLABLE ZUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a29b YI SYLLABLE ZOT | a29a YI SYLLABLE ZUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a29d YI SYLLABLE ZO | a29c YI SYLLABLE ZOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a29f YI SYLLABLE ZEX | a29e YI SYLLABLE ZOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2a1 YI SYLLABLE ZEP | a2a0 YI SYLLABLE ZE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2a3 YI SYLLABLE ZUX | a2a2 YI SYLLABLE ZUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2a5 YI SYLLABLE ZUP | a2a4 YI SYLLABLE ZU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2a7 YI SYLLABLE ZUR | a2a6 YI SYLLABLE ZURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2a9 YI SYLLABLE ZYX | a2a8 YI SYLLABLE ZYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2ab YI SYLLABLE ZYP | a2aa YI SYLLABLE ZY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2ad YI SYLLABLE ZYR | a2ac YI SYLLABLE ZYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2af YI SYLLABLE CIX | a2ae YI SYLLABLE CIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2b1 YI SYLLABLE CIP | a2b0 YI SYLLABLE CI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2b3 YI SYLLABLE CIEX | a2b2 YI SYLLABLE CIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2b5 YI SYLLABLE CIEP | a2b4 YI SYLLABLE CIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2b7 YI SYLLABLE CAX | a2b6 YI SYLLABLE CAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2b9 YI SYLLABLE CAP | a2b8 YI SYLLABLE CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2bb YI SYLLABLE CUO | a2ba YI SYLLABLE CUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2bd YI SYLLABLE COT | a2bc YI SYLLABLE CUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2bf YI SYLLABLE CO | a2be YI SYLLABLE COX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2c1 YI SYLLABLE CEX | a2c0 YI SYLLABLE COP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2c3 YI SYLLABLE CEP | a2c2 YI SYLLABLE CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2c5 YI SYLLABLE CUX | a2c4 YI SYLLABLE CUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2c7 YI SYLLABLE CUP | a2c6 YI SYLLABLE CU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2c9 YI SYLLABLE CUR | a2c8 YI SYLLABLE CURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2cb YI SYLLABLE CYX | a2ca YI SYLLABLE CYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2cd YI SYLLABLE CYP | a2cc YI SYLLABLE CY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2cf YI SYLLABLE CYR | a2ce YI SYLLABLE CYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2d1 YI SYLLABLE ZZIX | a2d0 YI SYLLABLE ZZIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2d3 YI SYLLABLE ZZIP | a2d2 YI SYLLABLE ZZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2d5 YI SYLLABLE ZZIEX | a2d4 YI SYLLABLE ZZIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2d7 YI SYLLABLE ZZIEP | a2d6 YI SYLLABLE ZZIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2d9 YI SYLLABLE ZZAX | a2d8 YI SYLLABLE ZZAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2db YI SYLLABLE ZZAP | a2da YI SYLLABLE ZZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2dd YI SYLLABLE ZZO | a2dc YI SYLLABLE ZZOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2df YI SYLLABLE ZZEX | a2de YI SYLLABLE ZZOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2e1 YI SYLLABLE ZZEP | a2e0 YI SYLLABLE ZZE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2e3 YI SYLLABLE ZZU | a2e2 YI SYLLABLE ZZUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2e5 YI SYLLABLE ZZURX | a2e4 YI SYLLABLE ZZUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2e7 YI SYLLABLE ZZYT | a2e6 YI SYLLABLE ZZUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2e9 YI SYLLABLE ZZY | a2e8 YI SYLLABLE ZZYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2eb YI SYLLABLE ZZYRX | a2ea YI SYLLABLE ZZYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2ed YI SYLLABLE NZIT | a2ec YI SYLLABLE ZZYR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2ef YI SYLLABLE NZI | a2ee YI SYLLABLE NZIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2f1 YI SYLLABLE NZIEX | a2f0 YI SYLLABLE NZIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2f3 YI SYLLABLE NZIEP | a2f2 YI SYLLABLE NZIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2f5 YI SYLLABLE NZAX | a2f4 YI SYLLABLE NZAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2f7 YI SYLLABLE NZAP | a2f6 YI SYLLABLE NZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2f9 YI SYLLABLE NZUO | a2f8 YI SYLLABLE NZUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2fb YI SYLLABLE NZOP | a2fa YI SYLLABLE NZOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2fd YI SYLLABLE NZE | a2fc YI SYLLABLE NZEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2ff YI SYLLABLE NZU | a2fe YI SYLLABLE NZUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a301 YI SYLLABLE NZURX | a300 YI SYLLABLE NZUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a303 YI SYLLABLE NZYT | a302 YI SYLLABLE NZUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a305 YI SYLLABLE NZY | a304 YI SYLLABLE NZYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a307 YI SYLLABLE NZYRX | a306 YI SYLLABLE NZYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a309 YI SYLLABLE SIT | a308 YI SYLLABLE NZYR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a30b YI SYLLABLE SI | a30a YI SYLLABLE SIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a30d YI SYLLABLE SIEX | a30c YI SYLLABLE SIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a30f YI SYLLABLE SIEP | a30e YI SYLLABLE SIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a311 YI SYLLABLE SAX | a310 YI SYLLABLE SAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a313 YI SYLLABLE SAP | a312 YI SYLLABLE SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a315 YI SYLLABLE SUO | a314 YI SYLLABLE SUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a317 YI SYLLABLE SOT | a316 YI SYLLABLE SUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a319 YI SYLLABLE SO | a318 YI SYLLABLE SOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a31b YI SYLLABLE SEX | a31a YI SYLLABLE SOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a31d YI SYLLABLE SEP | a31c YI SYLLABLE SE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a31f YI SYLLABLE SUX | a31e YI SYLLABLE SUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a321 YI SYLLABLE SUP | a320 YI SYLLABLE SU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a323 YI SYLLABLE SUR | a322 YI SYLLABLE SURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a325 YI SYLLABLE SYX | a324 YI SYLLABLE SYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a327 YI SYLLABLE SYP | a326 YI SYLLABLE SY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a329 YI SYLLABLE SYR | a328 YI SYLLABLE SYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a32b YI SYLLABLE SSIX | a32a YI SYLLABLE SSIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a32d YI SYLLABLE SSIP | a32c YI SYLLABLE SSI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a32f YI SYLLABLE SSIE | a32e YI SYLLABLE SSIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a331 YI SYLLABLE SSAT | a330 YI SYLLABLE SSIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a333 YI SYLLABLE SSA | a332 YI SYLLABLE SSAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a335 YI SYLLABLE SSOT | a334 YI SYLLABLE SSAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a337 YI SYLLABLE SSO | a336 YI SYLLABLE SSOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a339 YI SYLLABLE SSEX | a338 YI SYLLABLE SSOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a33b YI SYLLABLE SSEP | a33a YI SYLLABLE SSE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a33d YI SYLLABLE SSUX | a33c YI SYLLABLE SSUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a33f YI SYLLABLE SSUP | a33e YI SYLLABLE SSU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a341 YI SYLLABLE SSYX | a340 YI SYLLABLE SSYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a343 YI SYLLABLE SSYP | a342 YI SYLLABLE SSY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a345 YI SYLLABLE SSYR | a344 YI SYLLABLE SSYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a347 YI SYLLABLE ZHAX | a346 YI SYLLABLE ZHAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a349 YI SYLLABLE ZHAP | a348 YI SYLLABLE ZHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a34b YI SYLLABLE ZHUO | a34a YI SYLLABLE ZHUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a34d YI SYLLABLE ZHOT | a34c YI SYLLABLE ZHUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a34f YI SYLLABLE ZHO | a34e YI SYLLABLE ZHOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a351 YI SYLLABLE ZHET | a350 YI SYLLABLE ZHOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a353 YI SYLLABLE ZHE | a352 YI SYLLABLE ZHEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a355 YI SYLLABLE ZHUT | a354 YI SYLLABLE ZHEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a357 YI SYLLABLE ZHU | a356 YI SYLLABLE ZHUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a359 YI SYLLABLE ZHURX | a358 YI SYLLABLE ZHUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a35b YI SYLLABLE ZHYT | a35a YI SYLLABLE ZHUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a35d YI SYLLABLE ZHY | a35c YI SYLLABLE ZHYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a35f YI SYLLABLE ZHYRX | a35e YI SYLLABLE ZHYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a361 YI SYLLABLE CHAT | a360 YI SYLLABLE ZHYR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a363 YI SYLLABLE CHA | a362 YI SYLLABLE CHAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a365 YI SYLLABLE CHUOT | a364 YI SYLLABLE CHAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a367 YI SYLLABLE CHUO | a366 YI SYLLABLE CHUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a369 YI SYLLABLE CHOT | a368 YI SYLLABLE CHUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a36b YI SYLLABLE CHO | a36a YI SYLLABLE CHOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a36d YI SYLLABLE CHET | a36c YI SYLLABLE CHOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a36f YI SYLLABLE CHE | a36e YI SYLLABLE CHEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a371 YI SYLLABLE CHUX | a370 YI SYLLABLE CHEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a373 YI SYLLABLE CHUP | a372 YI SYLLABLE CHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a375 YI SYLLABLE CHUR | a374 YI SYLLABLE CHURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a377 YI SYLLABLE CHYX | a376 YI SYLLABLE CHYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a379 YI SYLLABLE CHYP | a378 YI SYLLABLE CHY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a37b YI SYLLABLE CHYR | a37a YI SYLLABLE CHYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a37d YI SYLLABLE RRA | a37c YI SYLLABLE RRAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a37f YI SYLLABLE RRUO | a37e YI SYLLABLE RRUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a381 YI SYLLABLE RROX | a380 YI SYLLABLE RROT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a383 YI SYLLABLE RROP | a382 YI SYLLABLE RRO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a385 YI SYLLABLE RREX | a384 YI SYLLABLE RRET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a387 YI SYLLABLE RREP | a386 YI SYLLABLE RRE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a389 YI SYLLABLE RRUX | a388 YI SYLLABLE RRUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a38b YI SYLLABLE RRUP | a38a YI SYLLABLE RRU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a38d YI SYLLABLE RRUR | a38c YI SYLLABLE RRURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a38f YI SYLLABLE RRYX | a38e YI SYLLABLE RRYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a391 YI SYLLABLE RRYP | a390 YI SYLLABLE RRY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a393 YI SYLLABLE RRYR | a392 YI SYLLABLE RRYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a395 YI SYLLABLE NRAX | a394 YI SYLLABLE NRAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a397 YI SYLLABLE NRAP | a396 YI SYLLABLE NRA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a399 YI SYLLABLE NRO | a398 YI SYLLABLE NROX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a39b YI SYLLABLE NRET | a39a YI SYLLABLE NROP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a39d YI SYLLABLE NRE | a39c YI SYLLABLE NREX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a39f YI SYLLABLE NRUT | a39e YI SYLLABLE NREP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3a1 YI SYLLABLE NRU | a3a0 YI SYLLABLE NRUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3a3 YI SYLLABLE NRURX | a3a2 YI SYLLABLE NRUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3a5 YI SYLLABLE NRYT | a3a4 YI SYLLABLE NRUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3a7 YI SYLLABLE NRY | a3a6 YI SYLLABLE NRYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3a9 YI SYLLABLE NRYRX | a3a8 YI SYLLABLE NRYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3ab YI SYLLABLE SHAT | a3aa YI SYLLABLE NRYR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3ad YI SYLLABLE SHA | a3ac YI SYLLABLE SHAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3af YI SYLLABLE SHUOX | a3ae YI SYLLABLE SHAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3b1 YI SYLLABLE SHUOP | a3b0 YI SYLLABLE SHUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3b3 YI SYLLABLE SHOX | a3b2 YI SYLLABLE SHOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3b5 YI SYLLABLE SHOP | a3b4 YI SYLLABLE SHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3b7 YI SYLLABLE SHEX | a3b6 YI SYLLABLE SHET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3b9 YI SYLLABLE SHEP | a3b8 YI SYLLABLE SHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3bb YI SYLLABLE SHUX | a3ba YI SYLLABLE SHUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3bd YI SYLLABLE SHUP | a3bc YI SYLLABLE SHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3bf YI SYLLABLE SHUR | a3be YI SYLLABLE SHURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3c1 YI SYLLABLE SHYX | a3c0 YI SYLLABLE SHYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3c3 YI SYLLABLE SHYP | a3c2 YI SYLLABLE SHY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3c5 YI SYLLABLE SHYR | a3c4 YI SYLLABLE SHYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3c7 YI SYLLABLE RAX | a3c6 YI SYLLABLE RAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3c9 YI SYLLABLE RAP | a3c8 YI SYLLABLE RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3cb YI SYLLABLE RUO | a3ca YI SYLLABLE RUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3cd YI SYLLABLE ROT | a3cc YI SYLLABLE RUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3cf YI SYLLABLE RO | a3ce YI SYLLABLE ROX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3d1 YI SYLLABLE REX | a3d0 YI SYLLABLE ROP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3d3 YI SYLLABLE REP | a3d2 YI SYLLABLE RE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3d5 YI SYLLABLE RUX | a3d4 YI SYLLABLE RUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3d7 YI SYLLABLE RUP | a3d6 YI SYLLABLE RU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3d9 YI SYLLABLE RUR | a3d8 YI SYLLABLE RURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3db YI SYLLABLE RYX | a3da YI SYLLABLE RYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3dd YI SYLLABLE RYP | a3dc YI SYLLABLE RY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3df YI SYLLABLE RYR | a3de YI SYLLABLE RYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3e1 YI SYLLABLE JIX | a3e0 YI SYLLABLE JIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3e3 YI SYLLABLE JIP | a3e2 YI SYLLABLE JI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3e5 YI SYLLABLE JIEX | a3e4 YI SYLLABLE JIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3e7 YI SYLLABLE JIEP | a3e6 YI SYLLABLE JIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3e9 YI SYLLABLE JUOX | a3e8 YI SYLLABLE JUOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3eb YI SYLLABLE JUOP | a3ea YI SYLLABLE JUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3ed YI SYLLABLE JOX | a3ec YI SYLLABLE JOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3ef YI SYLLABLE JOP | a3ee YI SYLLABLE JO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3f1 YI SYLLABLE JUX | a3f0 YI SYLLABLE JUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3f3 YI SYLLABLE JUP | a3f2 YI SYLLABLE JU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3f5 YI SYLLABLE JUR | a3f4 YI SYLLABLE JURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3f7 YI SYLLABLE JYX | a3f6 YI SYLLABLE JYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3f9 YI SYLLABLE JYP | a3f8 YI SYLLABLE JY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3fb YI SYLLABLE JYR | a3fa YI SYLLABLE JYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3fd YI SYLLABLE QIX | a3fc YI SYLLABLE QIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3ff YI SYLLABLE QIP | a3fe YI SYLLABLE QI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a401 YI SYLLABLE QIEX | a400 YI SYLLABLE QIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a403 YI SYLLABLE QIEP | a402 YI SYLLABLE QIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a405 YI SYLLABLE QUOX | a404 YI SYLLABLE QUOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a407 YI SYLLABLE QUOP | a406 YI SYLLABLE QUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a409 YI SYLLABLE QOX | a408 YI SYLLABLE QOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a40b YI SYLLABLE QOP | a40a YI SYLLABLE QO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a40d YI SYLLABLE QUX | a40c YI SYLLABLE QUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a40f YI SYLLABLE QUP | a40e YI SYLLABLE QU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a411 YI SYLLABLE QUR | a410 YI SYLLABLE QURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a413 YI SYLLABLE QYX | a412 YI SYLLABLE QYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a415 YI SYLLABLE QYP | a414 YI SYLLABLE QY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a417 YI SYLLABLE QYR | a416 YI SYLLABLE QYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a419 YI SYLLABLE JJIX | a418 YI SYLLABLE JJIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a41b YI SYLLABLE JJIP | a41a YI SYLLABLE JJI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a41d YI SYLLABLE JJIEX | a41c YI SYLLABLE JJIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a41f YI SYLLABLE JJIEP | a41e YI SYLLABLE JJIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a421 YI SYLLABLE JJUO | a420 YI SYLLABLE JJUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a423 YI SYLLABLE JJOT | a422 YI SYLLABLE JJUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a425 YI SYLLABLE JJO | a424 YI SYLLABLE JJOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a427 YI SYLLABLE JJUT | a426 YI SYLLABLE JJOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a429 YI SYLLABLE JJU | a428 YI SYLLABLE JJUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a42b YI SYLLABLE JJURX | a42a YI SYLLABLE JJUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a42d YI SYLLABLE JJYT | a42c YI SYLLABLE JJUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a42f YI SYLLABLE JJY | a42e YI SYLLABLE JJYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a431 YI SYLLABLE NJIT | a430 YI SYLLABLE JJYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a433 YI SYLLABLE NJI | a432 YI SYLLABLE NJIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a435 YI SYLLABLE NJIET | a434 YI SYLLABLE NJIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a437 YI SYLLABLE NJIE | a436 YI SYLLABLE NJIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a439 YI SYLLABLE NJUOX | a438 YI SYLLABLE NJIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a43b YI SYLLABLE NJOT | a43a YI SYLLABLE NJUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a43d YI SYLLABLE NJO | a43c YI SYLLABLE NJOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a43f YI SYLLABLE NJUX | a43e YI SYLLABLE NJOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a441 YI SYLLABLE NJUP | a440 YI SYLLABLE NJU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a443 YI SYLLABLE NJUR | a442 YI SYLLABLE NJURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a445 YI SYLLABLE NJYX | a444 YI SYLLABLE NJYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a447 YI SYLLABLE NJYP | a446 YI SYLLABLE NJY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a449 YI SYLLABLE NJYR | a448 YI SYLLABLE NJYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a44b YI SYLLABLE NYIX | a44a YI SYLLABLE NYIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a44d YI SYLLABLE NYIP | a44c YI SYLLABLE NYI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a44f YI SYLLABLE NYIEX | a44e YI SYLLABLE NYIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a451 YI SYLLABLE NYIEP | a450 YI SYLLABLE NYIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a453 YI SYLLABLE NYUO | a452 YI SYLLABLE NYUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a455 YI SYLLABLE NYOT | a454 YI SYLLABLE NYUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a457 YI SYLLABLE NYO | a456 YI SYLLABLE NYOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a459 YI SYLLABLE NYUT | a458 YI SYLLABLE NYOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a45b YI SYLLABLE NYU | a45a YI SYLLABLE NYUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a45d YI SYLLABLE XIT | a45c YI SYLLABLE NYUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a45f YI SYLLABLE XI | a45e YI SYLLABLE XIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a461 YI SYLLABLE XIET | a460 YI SYLLABLE XIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a463 YI SYLLABLE XIE | a462 YI SYLLABLE XIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a465 YI SYLLABLE XUOX | a464 YI SYLLABLE XIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a467 YI SYLLABLE XOT | a466 YI SYLLABLE XUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a469 YI SYLLABLE XO | a468 YI SYLLABLE XOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a46b YI SYLLABLE XYT | a46a YI SYLLABLE XOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a46d YI SYLLABLE XY | a46c YI SYLLABLE XYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a46f YI SYLLABLE XYRX | a46e YI SYLLABLE XYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a471 YI SYLLABLE YIT | a470 YI SYLLABLE XYR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a473 YI SYLLABLE YI | a472 YI SYLLABLE YIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a475 YI SYLLABLE YIET | a474 YI SYLLABLE YIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a477 YI SYLLABLE YIE | a476 YI SYLLABLE YIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a479 YI SYLLABLE YUOT | a478 YI SYLLABLE YIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a47b YI SYLLABLE YUO | a47a YI SYLLABLE YUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a47d YI SYLLABLE YOT | a47c YI SYLLABLE YUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a47f YI SYLLABLE YO | a47e YI SYLLABLE YOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a481 YI SYLLABLE YUT | a480 YI SYLLABLE YOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a483 YI SYLLABLE YU | a482 YI SYLLABLE YUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a485 YI SYLLABLE YURX | a484 YI SYLLABLE YUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a487 YI SYLLABLE YYT | a486 YI SYLLABLE YUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a489 YI SYLLABLE YY | a488 YI SYLLABLE YYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a48b YI SYLLABLE YYRX | a48a YI SYLLABLE YYP */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* a48d (null) | a48c YI SYLLABLE YYR */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a48f (null) | a48e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a491 YI RADICAL LI | a490 YI RADICAL QOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a493 YI RADICAL NYIP | a492 YI RADICAL KIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a495 YI RADICAL SSI | a494 YI RADICAL CYP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a497 YI RADICAL GEP | a496 YI RADICAL GGOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a499 YI RADICAL HXIT | a498 YI RADICAL MI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a49b YI RADICAL BBUT | a49a YI RADICAL LYR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a49d YI RADICAL YO | a49c YI RADICAL MOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a49f YI RADICAL HXUO | a49e YI RADICAL PUT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4a1 YI RADICAL GA | a4a0 YI RADICAL TAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4a3 YI RADICAL CYT | a4a2 YI RADICAL ZUP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4a5 YI RADICAL BUR | a4a4 YI RADICAL DDUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4a7 YI RADICAL NYOP | a4a6 YI RADICAL GGUO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4a9 YI RADICAL OP | a4a8 YI RADICAL TU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4ab YI RADICAL ZOT | a4aa YI RADICAL JJUT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4ad YI RADICAL HMO | a4ac YI RADICAL PYT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4af YI RADICAL VUR | a4ae YI RADICAL YIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4b1 YI RADICAL VEP | a4b0 YI RADICAL SHY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4b3 YI RADICAL JO | a4b2 YI RADICAL ZA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4b5 YI RADICAL JJY | a4b4 YI RADICAL NZUP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4b7 YI RADICAL JJIE | a4b6 YI RADICAL GOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4b9 YI RADICAL DU | a4b8 YI RADICAL WO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4bb YI RADICAL LIE | a4ba YI RADICAL SHUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4bd YI RADICAL CUOP | a4bc YI RADICAL CY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4bf YI RADICAL HXOP | a4be YI RADICAL CIP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4c1 YI RADICAL ZUR | a4c0 YI RADICAL SHAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4c3 YI RADICAL CHE | a4c2 YI RADICAL SHOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4c5 YI RADICAL NBIE | a4c4 YI RADICAL ZZIET */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* a4c7 (null) | a4c6 YI RADICAL KE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a4c9 (null) | a4c8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a4cb (null) | a4ca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a4cd (null) | a4cc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a4cf (null) | a4ce (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4d1 LISU LETTER PA | a4d0 LISU LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4d3 LISU LETTER DA | a4d2 LISU LETTER PHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4d5 LISU LETTER THA | a4d4 LISU LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4d7 LISU LETTER KA | a4d6 LISU LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4d9 LISU LETTER JA | a4d8 LISU LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4db LISU LETTER CHA | a4da LISU LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4dd LISU LETTER TSA | a4dc LISU LETTER DZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4df LISU LETTER MA | a4de LISU LETTER TSHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4e1 LISU LETTER LA | a4e0 LISU LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4e3 LISU LETTER ZHA | a4e2 LISU LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4e5 LISU LETTER NGA | a4e4 LISU LETTER ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4e7 LISU LETTER XA | a4e6 LISU LETTER HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4e9 LISU LETTER FA | a4e8 LISU LETTER HHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4eb LISU LETTER SHA | a4ea LISU LETTER WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4ed LISU LETTER GHA | a4ec LISU LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4ef LISU LETTER AE | a4ee LISU LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4f1 LISU LETTER EU | a4f0 LISU LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4f3 LISU LETTER O | a4f2 LISU LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4f5 LISU LETTER UE | a4f4 LISU LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4f7 LISU LETTER OE | a4f6 LISU LETTER UH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4f9 LISU LETTER TONE NA PO | a4f8 LISU LETTER TONE MYA TI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4fb LISU LETTER TONE MYA BO | a4fa LISU LETTER TONE MYA CYA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4fd LISU LETTER TONE MYA JEU | a4fc LISU LETTER TONE MYA NA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a4ff LISU PUNCTUATION FULL STOP | a4fe LISU PUNCTUATION COMMA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a501 VAI SYLLABLE EEN | a500 VAI SYLLABLE EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a503 VAI SYLLABLE WEE | a502 VAI SYLLABLE HEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a505 VAI SYLLABLE PEE | a504 VAI SYLLABLE WEEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a507 VAI SYLLABLE BEE | a506 VAI SYLLABLE BHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a509 VAI SYLLABLE KPEE | a508 VAI SYLLABLE MBEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a50b VAI SYLLABLE GBEE | a50a VAI SYLLABLE MGBEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a50d VAI SYLLABLE VEE | a50c VAI SYLLABLE FEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a50f VAI SYLLABLE THEE | a50e VAI SYLLABLE TEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a511 VAI SYLLABLE DHHEE | a510 VAI SYLLABLE DHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a513 VAI SYLLABLE REE | a512 VAI SYLLABLE LEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a515 VAI SYLLABLE NDEE | a514 VAI SYLLABLE DEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a517 VAI SYLLABLE SHEE | a516 VAI SYLLABLE SEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a519 VAI SYLLABLE ZHEE | a518 VAI SYLLABLE ZEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a51b VAI SYLLABLE JEE | a51a VAI SYLLABLE CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a51d VAI SYLLABLE YEE | a51c VAI SYLLABLE NJEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a51f VAI SYLLABLE NGGEE | a51e VAI SYLLABLE KEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a521 VAI SYLLABLE MEE | a520 VAI SYLLABLE GEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a523 VAI SYLLABLE NYEE | a522 VAI SYLLABLE NEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a525 VAI SYLLABLE IN | a524 VAI SYLLABLE I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a527 VAI SYLLABLE HIN | a526 VAI SYLLABLE HI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a529 VAI SYLLABLE WIN | a528 VAI SYLLABLE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a52b VAI SYLLABLE BHI | a52a VAI SYLLABLE PI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a52d VAI SYLLABLE MBI | a52c VAI SYLLABLE BI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a52f VAI SYLLABLE MGBI | a52e VAI SYLLABLE KPI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a531 VAI SYLLABLE FI | a530 VAI SYLLABLE GBI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a533 VAI SYLLABLE TI | a532 VAI SYLLABLE VI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a535 VAI SYLLABLE DHI | a534 VAI SYLLABLE THI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a537 VAI SYLLABLE LI | a536 VAI SYLLABLE DHHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a539 VAI SYLLABLE DI | a538 VAI SYLLABLE RI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a53b VAI SYLLABLE SI | a53a VAI SYLLABLE NDI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a53d VAI SYLLABLE ZI | a53c VAI SYLLABLE SHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a53f VAI SYLLABLE CI | a53e VAI SYLLABLE ZHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a541 VAI SYLLABLE NJI | a540 VAI SYLLABLE JI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a543 VAI SYLLABLE KI | a542 VAI SYLLABLE YI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a545 VAI SYLLABLE GI | a544 VAI SYLLABLE NGGI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a547 VAI SYLLABLE NI | a546 VAI SYLLABLE MI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a549 VAI SYLLABLE A | a548 VAI SYLLABLE NYI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a54b VAI SYLLABLE NGAN | a54a VAI SYLLABLE AN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a54d VAI SYLLABLE HAN | a54c VAI SYLLABLE HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a54f VAI SYLLABLE WAN | a54e VAI SYLLABLE WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a551 VAI SYLLABLE BHA | a550 VAI SYLLABLE PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a553 VAI SYLLABLE MBA | a552 VAI SYLLABLE BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a555 VAI SYLLABLE KPAN | a554 VAI SYLLABLE KPA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a557 VAI SYLLABLE GBA | a556 VAI SYLLABLE MGBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a559 VAI SYLLABLE VA | a558 VAI SYLLABLE FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a55b VAI SYLLABLE THA | a55a VAI SYLLABLE TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a55d VAI SYLLABLE DHHA | a55c VAI SYLLABLE DHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a55f VAI SYLLABLE RA | a55e VAI SYLLABLE LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a561 VAI SYLLABLE NDA | a560 VAI SYLLABLE DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a563 VAI SYLLABLE SHA | a562 VAI SYLLABLE SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a565 VAI SYLLABLE ZHA | a564 VAI SYLLABLE ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a567 VAI SYLLABLE JA | a566 VAI SYLLABLE CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a569 VAI SYLLABLE YA | a568 VAI SYLLABLE NJA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a56b VAI SYLLABLE KAN | a56a VAI SYLLABLE KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a56d VAI SYLLABLE GA | a56c VAI SYLLABLE NGGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a56f VAI SYLLABLE NA | a56e VAI SYLLABLE MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a571 VAI SYLLABLE OO | a570 VAI SYLLABLE NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a573 VAI SYLLABLE HOO | a572 VAI SYLLABLE OON */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a575 VAI SYLLABLE WOON | a574 VAI SYLLABLE WOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a577 VAI SYLLABLE BHOO | a576 VAI SYLLABLE POO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a579 VAI SYLLABLE MBOO | a578 VAI SYLLABLE BOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a57b VAI SYLLABLE MGBOO | a57a VAI SYLLABLE KPOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a57d VAI SYLLABLE FOO | a57c VAI SYLLABLE GBOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a57f VAI SYLLABLE TOO | a57e VAI SYLLABLE VOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a581 VAI SYLLABLE DHOO | a580 VAI SYLLABLE THOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a583 VAI SYLLABLE LOO | a582 VAI SYLLABLE DHHOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a585 VAI SYLLABLE DOO | a584 VAI SYLLABLE ROO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a587 VAI SYLLABLE SOO | a586 VAI SYLLABLE NDOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a589 VAI SYLLABLE ZOO | a588 VAI SYLLABLE SHOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a58b VAI SYLLABLE COO | a58a VAI SYLLABLE ZHOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a58d VAI SYLLABLE NJOO | a58c VAI SYLLABLE JOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a58f VAI SYLLABLE KOO | a58e VAI SYLLABLE YOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a591 VAI SYLLABLE GOO | a590 VAI SYLLABLE NGGOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a593 VAI SYLLABLE NOO | a592 VAI SYLLABLE MOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a595 VAI SYLLABLE U | a594 VAI SYLLABLE NYOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a597 VAI SYLLABLE HU | a596 VAI SYLLABLE UN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a599 VAI SYLLABLE WU | a598 VAI SYLLABLE HUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a59b VAI SYLLABLE PU | a59a VAI SYLLABLE WUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a59d VAI SYLLABLE BU | a59c VAI SYLLABLE BHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a59f VAI SYLLABLE KPU | a59e VAI SYLLABLE MBU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5a1 VAI SYLLABLE GBU | a5a0 VAI SYLLABLE MGBU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5a3 VAI SYLLABLE VU | a5a2 VAI SYLLABLE FU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5a5 VAI SYLLABLE THU | a5a4 VAI SYLLABLE TU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5a7 VAI SYLLABLE DHHU | a5a6 VAI SYLLABLE DHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5a9 VAI SYLLABLE RU | a5a8 VAI SYLLABLE LU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5ab VAI SYLLABLE NDU | a5aa VAI SYLLABLE DU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5ad VAI SYLLABLE SHU | a5ac VAI SYLLABLE SU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5af VAI SYLLABLE ZHU | a5ae VAI SYLLABLE ZU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5b1 VAI SYLLABLE JU | a5b0 VAI SYLLABLE CU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5b3 VAI SYLLABLE YU | a5b2 VAI SYLLABLE NJU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5b5 VAI SYLLABLE NGGU | a5b4 VAI SYLLABLE KU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5b7 VAI SYLLABLE MU | a5b6 VAI SYLLABLE GU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5b9 VAI SYLLABLE NYU | a5b8 VAI SYLLABLE NU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5bb VAI SYLLABLE ON | a5ba VAI SYLLABLE O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5bd VAI SYLLABLE HO | a5bc VAI SYLLABLE NGON */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5bf VAI SYLLABLE WO | a5be VAI SYLLABLE HON */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5c1 VAI SYLLABLE PO | a5c0 VAI SYLLABLE WON */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5c3 VAI SYLLABLE BO | a5c2 VAI SYLLABLE BHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5c5 VAI SYLLABLE KPO | a5c4 VAI SYLLABLE MBO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5c7 VAI SYLLABLE GBO | a5c6 VAI SYLLABLE MGBO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5c9 VAI SYLLABLE FO | a5c8 VAI SYLLABLE GBON */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5cb VAI SYLLABLE TO | a5ca VAI SYLLABLE VO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5cd VAI SYLLABLE DHO | a5cc VAI SYLLABLE THO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5cf VAI SYLLABLE LO | a5ce VAI SYLLABLE DHHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5d1 VAI SYLLABLE DO | a5d0 VAI SYLLABLE RO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5d3 VAI SYLLABLE SO | a5d2 VAI SYLLABLE NDO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5d5 VAI SYLLABLE ZO | a5d4 VAI SYLLABLE SHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5d7 VAI SYLLABLE CO | a5d6 VAI SYLLABLE ZHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5d9 VAI SYLLABLE NJO | a5d8 VAI SYLLABLE JO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5db VAI SYLLABLE KO | a5da VAI SYLLABLE YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5dd VAI SYLLABLE GO | a5dc VAI SYLLABLE NGGO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5df VAI SYLLABLE NO | a5de VAI SYLLABLE MO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5e1 VAI SYLLABLE E | a5e0 VAI SYLLABLE NYO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5e3 VAI SYLLABLE NGEN | a5e2 VAI SYLLABLE EN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5e5 VAI SYLLABLE HEN | a5e4 VAI SYLLABLE HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5e7 VAI SYLLABLE WEN | a5e6 VAI SYLLABLE WE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5e9 VAI SYLLABLE BHE | a5e8 VAI SYLLABLE PE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5eb VAI SYLLABLE MBE | a5ea VAI SYLLABLE BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5ed VAI SYLLABLE KPEN | a5ec VAI SYLLABLE KPE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5ef VAI SYLLABLE GBE | a5ee VAI SYLLABLE MGBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5f1 VAI SYLLABLE FE | a5f0 VAI SYLLABLE GBEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5f3 VAI SYLLABLE TE | a5f2 VAI SYLLABLE VE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5f5 VAI SYLLABLE DHE | a5f4 VAI SYLLABLE THE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5f7 VAI SYLLABLE LE | a5f6 VAI SYLLABLE DHHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5f9 VAI SYLLABLE DE | a5f8 VAI SYLLABLE RE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5fb VAI SYLLABLE SE | a5fa VAI SYLLABLE NDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5fd VAI SYLLABLE ZE | a5fc VAI SYLLABLE SHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5ff VAI SYLLABLE CE | a5fe VAI SYLLABLE ZHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a601 VAI SYLLABLE NJE | a600 VAI SYLLABLE JE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a603 VAI SYLLABLE KE | a602 VAI SYLLABLE YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a605 VAI SYLLABLE NGGEN | a604 VAI SYLLABLE NGGE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a607 VAI SYLLABLE GEN | a606 VAI SYLLABLE GE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a609 VAI SYLLABLE NE | a608 VAI SYLLABLE ME */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a60b VAI SYLLABLE NG | a60a VAI SYLLABLE NYE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* a60d VAI COMMA | a60c VAI SYLLABLE LENGTHENER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a60f VAI QUESTION MARK | a60e VAI FULL STOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a611 VAI SYLLABLE NDOLE KA | a610 VAI SYLLABLE NDOLE FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a613 VAI SYMBOL FEENG | a612 VAI SYLLABLE NDOLE SOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a615 VAI SYMBOL TING | a614 VAI SYMBOL KEENG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a617 VAI SYMBOL BANG | a616 VAI SYMBOL NII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a619 VAI SYMBOL TAA | a618 VAI SYMBOL FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a61b VAI SYMBOL DOONG | a61a VAI SYMBOL DANG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a61d VAI SYMBOL TONG | a61c VAI SYMBOL KUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a61f VAI SYMBOL JONG | a61e VAI SYMBOL DO-O */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a621 VAI DIGIT ONE | a620 VAI DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a623 VAI DIGIT THREE | a622 VAI DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a625 VAI DIGIT FIVE | a624 VAI DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a627 VAI DIGIT SEVEN | a626 VAI DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a629 VAI DIGIT NINE | a628 VAI DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a62b VAI SYLLABLE NDOLE DO | a62a VAI SYLLABLE NDOLE MA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a62d (null) | a62c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a62f (null) | a62e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a631 (null) | a630 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a633 (null) | a632 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a635 (null) | a634 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a637 (null) | a636 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a639 (null) | a638 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a63b (null) | a63a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a63d (null) | a63c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a63f (null) | a63e (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a641 CYRILLIC SMALL LETTER ZEMLYA | a640 CYRILLIC CAPITAL LETTER ZEMLYA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a643 CYRILLIC SMALL LETTER DZELO | a642 CYRILLIC CAPITAL LETTER DZELO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a645 CYRILLIC SMALL LETTER REVERSED DZE | a644 CYRILLIC CAPITAL LETTER REVERSED DZE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a647 CYRILLIC SMALL LETTER IOTA | a646 CYRILLIC CAPITAL LETTER IOTA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a649 CYRILLIC SMALL LETTER DJERV | a648 CYRILLIC CAPITAL LETTER DJERV */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a64b CYRILLIC SMALL LETTER MONOGRAPH UK | a64a CYRILLIC CAPITAL LETTER MONOGRAPH UK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a64d CYRILLIC SMALL LETTER BROAD OMEGA | a64c CYRILLIC CAPITAL LETTER BROAD OMEGA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a64f CYRILLIC SMALL LETTER NEUTRAL YER | a64e CYRILLIC CAPITAL LETTER NEUTRAL YER */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a651 CYRILLIC SMALL LETTER YERU WITH BACK YE | a650 CYRILLIC CAPITAL LETTER YERU WITH BACK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a653 CYRILLIC SMALL LETTER IOTIFIED YAT | a652 CYRILLIC CAPITAL LETTER IOTIFIED YAT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a655 CYRILLIC SMALL LETTER REVERSED YU | a654 CYRILLIC CAPITAL LETTER REVERSED YU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a657 CYRILLIC SMALL LETTER IOTIFIED A | a656 CYRILLIC CAPITAL LETTER IOTIFIED A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a659 CYRILLIC SMALL LETTER CLOSED LITTLE YUS | a658 CYRILLIC CAPITAL LETTER CLOSED LITTLE Y */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a65b CYRILLIC SMALL LETTER BLENDED YUS | a65a CYRILLIC CAPITAL LETTER BLENDED YUS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a65d CYRILLIC SMALL LETTER IOTIFIED CLOSED L | a65c CYRILLIC CAPITAL LETTER IOTIFIED CLOSED */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a65f CYRILLIC SMALL LETTER YN | a65e CYRILLIC CAPITAL LETTER YN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a661 CYRILLIC SMALL LETTER REVERSED TSE | a660 CYRILLIC CAPITAL LETTER REVERSED TSE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a663 CYRILLIC SMALL LETTER SOFT DE | a662 CYRILLIC CAPITAL LETTER SOFT DE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a665 CYRILLIC SMALL LETTER SOFT EL | a664 CYRILLIC CAPITAL LETTER SOFT EL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a667 CYRILLIC SMALL LETTER SOFT EM | a666 CYRILLIC CAPITAL LETTER SOFT EM */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a669 CYRILLIC SMALL LETTER MONOCULAR O | a668 CYRILLIC CAPITAL LETTER MONOCULAR O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a66b CYRILLIC SMALL LETTER BINOCULAR O | a66a CYRILLIC CAPITAL LETTER BINOCULAR O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a66d CYRILLIC SMALL LETTER DOUBLE MONOCULAR | a66c CYRILLIC CAPITAL LETTER DOUBLE MONOCULA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* a66f COMBINING CYRILLIC VZMET | a66e CYRILLIC LETTER MULTIOCULAR O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a671 COMBINING CYRILLIC HUNDRED MILLIONS SIG | a670 COMBINING CYRILLIC TEN MILLIONS SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* a673 SLAVONIC ASTERISK | a672 COMBINING CYRILLIC THOUSAND MILLIONS SI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a675 COMBINING CYRILLIC LETTER I | a674 COMBINING CYRILLIC LETTER UKRAINIAN IE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a677 COMBINING CYRILLIC LETTER U | a676 COMBINING CYRILLIC LETTER YI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a679 COMBINING CYRILLIC LETTER YERU | a678 COMBINING CYRILLIC LETTER HARD SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a67b COMBINING CYRILLIC LETTER OMEGA | a67a COMBINING CYRILLIC LETTER SOFT SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a67d COMBINING CYRILLIC PAYEROK | a67c COMBINING CYRILLIC KAVYKA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* a67f CYRILLIC PAYEROK | a67e CYRILLIC KAVYKA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a681 CYRILLIC SMALL LETTER DWE | a680 CYRILLIC CAPITAL LETTER DWE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a683 CYRILLIC SMALL LETTER DZWE | a682 CYRILLIC CAPITAL LETTER DZWE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a685 CYRILLIC SMALL LETTER ZHWE | a684 CYRILLIC CAPITAL LETTER ZHWE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a687 CYRILLIC SMALL LETTER CCHE | a686 CYRILLIC CAPITAL LETTER CCHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a689 CYRILLIC SMALL LETTER DZZE | a688 CYRILLIC CAPITAL LETTER DZZE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a68b CYRILLIC SMALL LETTER TE WITH MIDDLE HO | a68a CYRILLIC CAPITAL LETTER TE WITH MIDDLE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a68d CYRILLIC SMALL LETTER TWE | a68c CYRILLIC CAPITAL LETTER TWE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a68f CYRILLIC SMALL LETTER TSWE | a68e CYRILLIC CAPITAL LETTER TSWE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a691 CYRILLIC SMALL LETTER TSSE | a690 CYRILLIC CAPITAL LETTER TSSE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a693 CYRILLIC SMALL LETTER TCHE | a692 CYRILLIC CAPITAL LETTER TCHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a695 CYRILLIC SMALL LETTER HWE | a694 CYRILLIC CAPITAL LETTER HWE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a697 CYRILLIC SMALL LETTER SHWE | a696 CYRILLIC CAPITAL LETTER SHWE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a699 (null) | a698 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a69b (null) | a69a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a69d (null) | a69c (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* a69f COMBINING CYRILLIC LETTER IOTIFIED E | a69e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6a1 BAMUM LETTER KA | a6a0 BAMUM LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6a3 BAMUM LETTER KU | a6a2 BAMUM LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6a5 BAMUM LETTER REE | a6a4 BAMUM LETTER EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6a7 BAMUM LETTER O | a6a6 BAMUM LETTER TAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6a9 BAMUM LETTER I | a6a8 BAMUM LETTER NYI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6ab BAMUM LETTER PA | a6aa BAMUM LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6ad BAMUM LETTER RIEE | a6ac BAMUM LETTER RII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6af BAMUM LETTER MEEEE | a6ae BAMUM LETTER LEEEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6b1 BAMUM LETTER NDAA | a6b0 BAMUM LETTER TAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6b3 BAMUM LETTER M | a6b2 BAMUM LETTER NJAEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6b5 BAMUM LETTER MU | a6b4 BAMUM LETTER SUU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6b7 BAMUM LETTER SI | a6b6 BAMUM LETTER SHII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6b9 BAMUM LETTER SEUX | a6b8 BAMUM LETTER SHEUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6bb BAMUM LETTER KET | a6ba BAMUM LETTER KYEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6bd BAMUM LETTER NU | a6bc BAMUM LETTER NUAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6bf BAMUM LETTER YOQ | a6be BAMUM LETTER NJUAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6c1 BAMUM LETTER YUQ | a6c0 BAMUM LETTER SHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6c3 BAMUM LETTER NSHA | a6c2 BAMUM LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6c5 BAMUM LETTER PEUX | a6c4 BAMUM LETTER KEUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6c7 BAMUM LETTER NTEE | a6c6 BAMUM LETTER NJEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6c9 BAMUM LETTER WUE | a6c8 BAMUM LETTER PUE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6cb BAMUM LETTER FEE | a6ca BAMUM LETTER PEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6cd BAMUM LETTER LU | a6cc BAMUM LETTER RU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6cf BAMUM LETTER NI | a6ce BAMUM LETTER MI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6d1 BAMUM LETTER RAE | a6d0 BAMUM LETTER REUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6d3 BAMUM LETTER NGKWAEN | a6d2 BAMUM LETTER KEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6d5 BAMUM LETTER NGA | a6d4 BAMUM LETTER NGGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6d7 BAMUM LETTER PUAE | a6d6 BAMUM LETTER SHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6d9 BAMUM LETTER FOM | a6d8 BAMUM LETTER FU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6db BAMUM LETTER NA | a6da BAMUM LETTER WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6dd BAMUM LETTER PI | a6dc BAMUM LETTER LI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6df BAMUM LETTER KO | a6de BAMUM LETTER LOQ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6e1 BAMUM LETTER REN | a6e0 BAMUM LETTER MBEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6e3 BAMUM LETTER MA | a6e2 BAMUM LETTER MEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6e5 BAMUM LETTER KI | a6e4 BAMUM LETTER TI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a6e7 BAMUM LETTER MBAA | a6e6 BAMUM LETTER MO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a6e9 BAMUM LETTER KPA | a6e8 BAMUM LETTER TET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a6eb BAMUM LETTER NTUU | a6ea BAMUM LETTER TEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a6ed BAMUM LETTER FAAMAE | a6ec BAMUM LETTER SAMBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a6ef BAMUM LETTER KOGHOM | a6ee BAMUM LETTER KOVUU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a6f1 BAMUM COMBINING MARK TUKWENTIS | a6f0 BAMUM COMBINING MARK KOQNDON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a6f3 BAMUM FULL STOP | a6f2 BAMUM NJAEMLI */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a6f5 BAMUM COMMA | a6f4 BAMUM COLON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a6f7 BAMUM QUESTION MARK | a6f6 BAMUM SEMICOLON */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a6f9 (null) | a6f8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a6fb (null) | a6fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a6fd (null) | a6fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a6ff (null) | a6fe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a701 MODIFIER LETTER CHINESE TONE YANG PING | a700 MODIFIER LETTER CHINESE TONE YIN PING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a703 MODIFIER LETTER CHINESE TONE YANG SHANG | a702 MODIFIER LETTER CHINESE TONE YIN SHANG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a705 MODIFIER LETTER CHINESE TONE YANG QU | a704 MODIFIER LETTER CHINESE TONE YIN QU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a707 MODIFIER LETTER CHINESE TONE YANG RU | a706 MODIFIER LETTER CHINESE TONE YIN RU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a709 MODIFIER LETTER HIGH DOTTED TONE BAR | a708 MODIFIER LETTER EXTRA-HIGH DOTTED TONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a70b MODIFIER LETTER LOW DOTTED TONE BAR | a70a MODIFIER LETTER MID DOTTED TONE BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a70d MODIFIER LETTER EXTRA-HIGH DOTTED LEFT- | a70c MODIFIER LETTER EXTRA-LOW DOTTED TONE B */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a70f MODIFIER LETTER MID DOTTED LEFT-STEM TO | a70e MODIFIER LETTER HIGH DOTTED LEFT-STEM T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a711 MODIFIER LETTER EXTRA-LOW DOTTED LEFT-S | a710 MODIFIER LETTER LOW DOTTED LEFT-STEM TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a713 MODIFIER LETTER HIGH LEFT-STEM TONE BAR | a712 MODIFIER LETTER EXTRA-HIGH LEFT-STEM TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a715 MODIFIER LETTER LOW LEFT-STEM TONE BAR | a714 MODIFIER LETTER MID LEFT-STEM TONE BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a717 MODIFIER LETTER DOT VERTICAL BAR | a716 MODIFIER LETTER EXTRA-LOW LEFT-STEM TON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a719 MODIFIER LETTER DOT HORIZONTAL BAR | a718 MODIFIER LETTER DOT SLASH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a71b MODIFIER LETTER RAISED UP ARROW | a71a MODIFIER LETTER LOWER RIGHT CORNER ANGL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a71d MODIFIER LETTER RAISED EXCLAMATION MARK | a71c MODIFIER LETTER RAISED DOWN ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a71f MODIFIER LETTER LOW INVERTED EXCLAMATIO | a71e MODIFIER LETTER RAISED INVERTED EXCLAMA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a721 MODIFIER LETTER STRESS AND LOW TONE | a720 MODIFIER LETTER STRESS AND HIGH TONE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a723 LATIN SMALL LETTER EGYPTOLOGICAL ALEF | a722 LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a725 LATIN SMALL LETTER EGYPTOLOGICAL AIN | a724 LATIN CAPITAL LETTER EGYPTOLOGICAL AIN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a727 LATIN SMALL LETTER HENG | a726 LATIN CAPITAL LETTER HENG */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a729 LATIN SMALL LETTER TZ | a728 LATIN CAPITAL LETTER TZ */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a72b LATIN SMALL LETTER TRESILLO | a72a LATIN CAPITAL LETTER TRESILLO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a72d LATIN SMALL LETTER CUATRILLO | a72c LATIN CAPITAL LETTER CUATRILLO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a72f LATIN SMALL LETTER CUATRILLO WITH COMMA | a72e LATIN CAPITAL LETTER CUATRILLO WITH COM */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* a731 LATIN LETTER SMALL CAPITAL S | a730 LATIN LETTER SMALL CAPITAL F */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a733 LATIN SMALL LETTER AA | a732 LATIN CAPITAL LETTER AA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a735 LATIN SMALL LETTER AO | a734 LATIN CAPITAL LETTER AO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a737 LATIN SMALL LETTER AU | a736 LATIN CAPITAL LETTER AU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a739 LATIN SMALL LETTER AV | a738 LATIN CAPITAL LETTER AV */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a73b LATIN SMALL LETTER AV WITH HORIZONTAL B | a73a LATIN CAPITAL LETTER AV WITH HORIZONTAL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a73d LATIN SMALL LETTER AY | a73c LATIN CAPITAL LETTER AY */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a73f LATIN SMALL LETTER REVERSED C WITH DOT | a73e LATIN CAPITAL LETTER REVERSED C WITH DO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a741 LATIN SMALL LETTER K WITH STROKE | a740 LATIN CAPITAL LETTER K WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a743 LATIN SMALL LETTER K WITH DIAGONAL STRO | a742 LATIN CAPITAL LETTER K WITH DIAGONAL ST */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a745 LATIN SMALL LETTER K WITH STROKE AND DI | a744 LATIN CAPITAL LETTER K WITH STROKE AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a747 LATIN SMALL LETTER BROKEN L | a746 LATIN CAPITAL LETTER BROKEN L */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a749 LATIN SMALL LETTER L WITH HIGH STROKE | a748 LATIN CAPITAL LETTER L WITH HIGH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a74b LATIN SMALL LETTER O WITH LONG STROKE O | a74a LATIN CAPITAL LETTER O WITH LONG STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a74d LATIN SMALL LETTER O WITH LOOP | a74c LATIN CAPITAL LETTER O WITH LOOP */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a74f LATIN SMALL LETTER OO | a74e LATIN CAPITAL LETTER OO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a751 LATIN SMALL LETTER P WITH STROKE THROUG | a750 LATIN CAPITAL LETTER P WITH STROKE THRO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a753 LATIN SMALL LETTER P WITH FLOURISH | a752 LATIN CAPITAL LETTER P WITH FLOURISH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a755 LATIN SMALL LETTER P WITH SQUIRREL TAIL | a754 LATIN CAPITAL LETTER P WITH SQUIRREL TA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a757 LATIN SMALL LETTER Q WITH STROKE THROUG | a756 LATIN CAPITAL LETTER Q WITH STROKE THRO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a759 LATIN SMALL LETTER Q WITH DIAGONAL STRO | a758 LATIN CAPITAL LETTER Q WITH DIAGONAL ST */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a75b LATIN SMALL LETTER R ROTUNDA | a75a LATIN CAPITAL LETTER R ROTUNDA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a75d LATIN SMALL LETTER RUM ROTUNDA | a75c LATIN CAPITAL LETTER RUM ROTUNDA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a75f LATIN SMALL LETTER V WITH DIAGONAL STRO | a75e LATIN CAPITAL LETTER V WITH DIAGONAL ST */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a761 LATIN SMALL LETTER VY | a760 LATIN CAPITAL LETTER VY */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a763 LATIN SMALL LETTER VISIGOTHIC Z | a762 LATIN CAPITAL LETTER VISIGOTHIC Z */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a765 LATIN SMALL LETTER THORN WITH STROKE | a764 LATIN CAPITAL LETTER THORN WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a767 LATIN SMALL LETTER THORN WITH STROKE TH | a766 LATIN CAPITAL LETTER THORN WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a769 LATIN SMALL LETTER VEND | a768 LATIN CAPITAL LETTER VEND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a76b LATIN SMALL LETTER ET | a76a LATIN CAPITAL LETTER ET */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a76d LATIN SMALL LETTER IS | a76c LATIN CAPITAL LETTER IS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a76f LATIN SMALL LETTER CON | a76e LATIN CAPITAL LETTER CON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_OTHER, /* a771 LATIN SMALL LETTER DUM | a770 MODIFIER LETTER US */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* a773 LATIN SMALL LETTER MUM | a772 LATIN SMALL LETTER LUM */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* a775 LATIN SMALL LETTER RUM | a774 LATIN SMALL LETTER NUM */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* a777 LATIN SMALL LETTER TUM | a776 LATIN LETTER SMALL CAPITAL RUM */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* a779 LATIN CAPITAL LETTER INSULAR D | a778 LATIN SMALL LETTER UM */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* a77b LATIN CAPITAL LETTER INSULAR F | a77a LATIN SMALL LETTER INSULAR D */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* a77d LATIN CAPITAL LETTER INSULAR G | a77c LATIN SMALL LETTER INSULAR F */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a77f LATIN SMALL LETTER TURNED INSULAR G | a77e LATIN CAPITAL LETTER TURNED INSULAR G */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a781 LATIN SMALL LETTER TURNED L | a780 LATIN CAPITAL LETTER TURNED L */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a783 LATIN SMALL LETTER INSULAR R | a782 LATIN CAPITAL LETTER INSULAR R */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a785 LATIN SMALL LETTER INSULAR S | a784 LATIN CAPITAL LETTER INSULAR S */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a787 LATIN SMALL LETTER INSULAR T | a786 LATIN CAPITAL LETTER INSULAR T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a789 MODIFIER LETTER COLON | a788 MODIFIER LETTER LOW CIRCUMFLEX ACCENT */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_OTHER, /* a78b LATIN CAPITAL LETTER SALTILLO | a78a MODIFIER LETTER SHORT EQUALS SIGN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* a78d LATIN CAPITAL LETTER TURNED H | a78c LATIN SMALL LETTER SALTILLO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_LOWER, /* a78f (null) | a78e LATIN SMALL LETTER L WITH RETROFLEX HOO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a791 LATIN SMALL LETTER N WITH DESCENDER | a790 LATIN CAPITAL LETTER N WITH DESCENDER */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a793 LATIN SMALL LETTER C WITH BAR | a792 LATIN CAPITAL LETTER C WITH BAR */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a795 (null) | a794 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a797 (null) | a796 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a799 (null) | a798 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a79b (null) | a79a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a79d (null) | a79c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a79f (null) | a79e (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a7a1 LATIN SMALL LETTER G WITH OBLIQUE STROK | a7a0 LATIN CAPITAL LETTER G WITH OBLIQUE STR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a7a3 LATIN SMALL LETTER K WITH OBLIQUE STROK | a7a2 LATIN CAPITAL LETTER K WITH OBLIQUE STR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a7a5 LATIN SMALL LETTER N WITH OBLIQUE STROK | a7a4 LATIN CAPITAL LETTER N WITH OBLIQUE STR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a7a7 LATIN SMALL LETTER R WITH OBLIQUE STROK | a7a6 LATIN CAPITAL LETTER R WITH OBLIQUE STR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a7a9 LATIN SMALL LETTER S WITH OBLIQUE STROK | a7a8 LATIN CAPITAL LETTER S WITH OBLIQUE STR */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UPPER, /* a7ab (null) | a7aa LATIN CAPITAL LETTER H WITH HOOK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7ad (null) | a7ac (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7af (null) | a7ae (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7b1 (null) | a7b0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7b3 (null) | a7b2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7b5 (null) | a7b4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7b7 (null) | a7b6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7b9 (null) | a7b8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7bb (null) | a7ba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7bd (null) | a7bc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7bf (null) | a7be (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7c1 (null) | a7c0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7c3 (null) | a7c2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7c5 (null) | a7c4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7c7 (null) | a7c6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7c9 (null) | a7c8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7cb (null) | a7ca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7cd (null) | a7cc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7cf (null) | a7ce (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7d1 (null) | a7d0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7d3 (null) | a7d2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7d5 (null) | a7d4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7d7 (null) | a7d6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7d9 (null) | a7d8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7db (null) | a7da (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7dd (null) | a7dc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7df (null) | a7de (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7e1 (null) | a7e0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7e3 (null) | a7e2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7e5 (null) | a7e4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7e7 (null) | a7e6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7e9 (null) | a7e8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7eb (null) | a7ea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7ed (null) | a7ec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7ef (null) | a7ee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7f1 (null) | a7f0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7f3 (null) | a7f2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7f5 (null) | a7f4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7f7 (null) | a7f6 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a7f9 MODIFIER LETTER SMALL LIGATURE OE | a7f8 MODIFIER LETTER CAPITAL H WITH STROKE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_LOWER, /* a7fb LATIN EPIGRAPHIC LETTER REVERSED F | a7fa LATIN LETTER SMALL CAPITAL TURNED M */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a7fd LATIN EPIGRAPHIC LETTER INVERTED M | a7fc LATIN EPIGRAPHIC LETTER REVERSED P */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a7ff LATIN EPIGRAPHIC LETTER ARCHAIC M | a7fe LATIN EPIGRAPHIC LETTER I LONGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a801 SYLOTI NAGRI LETTER I | a800 SYLOTI NAGRI LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* a803 SYLOTI NAGRI LETTER U | a802 SYLOTI NAGRI SIGN DVISVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a805 SYLOTI NAGRI LETTER O | a804 SYLOTI NAGRI LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* a807 SYLOTI NAGRI LETTER KO | a806 SYLOTI NAGRI SIGN HASANTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a809 SYLOTI NAGRI LETTER GO | a808 SYLOTI NAGRI LETTER KHO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* a80b SYLOTI NAGRI SIGN ANUSVARA | a80a SYLOTI NAGRI LETTER GHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a80d SYLOTI NAGRI LETTER CHO | a80c SYLOTI NAGRI LETTER CO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a80f SYLOTI NAGRI LETTER JHO | a80e SYLOTI NAGRI LETTER JO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a811 SYLOTI NAGRI LETTER TTHO | a810 SYLOTI NAGRI LETTER TTO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a813 SYLOTI NAGRI LETTER DDHO | a812 SYLOTI NAGRI LETTER DDO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a815 SYLOTI NAGRI LETTER THO | a814 SYLOTI NAGRI LETTER TO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a817 SYLOTI NAGRI LETTER DHO | a816 SYLOTI NAGRI LETTER DO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a819 SYLOTI NAGRI LETTER PO | a818 SYLOTI NAGRI LETTER NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a81b SYLOTI NAGRI LETTER BO | a81a SYLOTI NAGRI LETTER PHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a81d SYLOTI NAGRI LETTER MO | a81c SYLOTI NAGRI LETTER BHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a81f SYLOTI NAGRI LETTER LO | a81e SYLOTI NAGRI LETTER RO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a821 SYLOTI NAGRI LETTER SO | a820 SYLOTI NAGRI LETTER RRO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* a823 SYLOTI NAGRI VOWEL SIGN A | a822 SYLOTI NAGRI LETTER HO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a825 SYLOTI NAGRI VOWEL SIGN U | a824 SYLOTI NAGRI VOWEL SIGN I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a827 SYLOTI NAGRI VOWEL SIGN OO | a826 SYLOTI NAGRI VOWEL SIGN E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a829 SYLOTI NAGRI POETRY MARK-2 | a828 SYLOTI NAGRI POETRY MARK-1 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a82b SYLOTI NAGRI POETRY MARK-4 | a82a SYLOTI NAGRI POETRY MARK-3 */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a82d (null) | a82c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a82f (null) | a82e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a831 NORTH INDIC FRACTION ONE HALF | a830 NORTH INDIC FRACTION ONE QUARTER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a833 NORTH INDIC FRACTION ONE SIXTEENTH | a832 NORTH INDIC FRACTION THREE QUARTERS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a835 NORTH INDIC FRACTION THREE SIXTEENTHS | a834 NORTH INDIC FRACTION ONE EIGHTH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a837 NORTH INDIC PLACEHOLDER MARK | a836 NORTH INDIC QUARTER MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a839 NORTH INDIC QUANTITY MARK | a838 NORTH INDIC RUPEE MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a83b (null) | a83a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a83d (null) | a83c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a83f (null) | a83e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a841 PHAGS-PA LETTER KHA | a840 PHAGS-PA LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a843 PHAGS-PA LETTER NGA | a842 PHAGS-PA LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a845 PHAGS-PA LETTER CHA | a844 PHAGS-PA LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a847 PHAGS-PA LETTER NYA | a846 PHAGS-PA LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a849 PHAGS-PA LETTER THA | a848 PHAGS-PA LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a84b PHAGS-PA LETTER NA | a84a PHAGS-PA LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a84d PHAGS-PA LETTER PHA | a84c PHAGS-PA LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a84f PHAGS-PA LETTER MA | a84e PHAGS-PA LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a851 PHAGS-PA LETTER TSHA | a850 PHAGS-PA LETTER TSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a853 PHAGS-PA LETTER WA | a852 PHAGS-PA LETTER DZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a855 PHAGS-PA LETTER ZA | a854 PHAGS-PA LETTER ZHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a857 PHAGS-PA LETTER YA | a856 PHAGS-PA LETTER SMALL A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a859 PHAGS-PA LETTER LA | a858 PHAGS-PA LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a85b PHAGS-PA LETTER SA | a85a PHAGS-PA LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a85d PHAGS-PA LETTER A | a85c PHAGS-PA LETTER HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a85f PHAGS-PA LETTER U | a85e PHAGS-PA LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a861 PHAGS-PA LETTER O | a860 PHAGS-PA LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a863 PHAGS-PA LETTER XA | a862 PHAGS-PA LETTER QA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a865 PHAGS-PA LETTER GGA | a864 PHAGS-PA LETTER FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a867 PHAGS-PA SUBJOINED LETTER WA | a866 PHAGS-PA LETTER EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a869 PHAGS-PA LETTER TTA | a868 PHAGS-PA SUBJOINED LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a86b PHAGS-PA LETTER DDA | a86a PHAGS-PA LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a86d PHAGS-PA LETTER ALTERNATE YA | a86c PHAGS-PA LETTER NNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a86f PHAGS-PA LETTER VOICED HA | a86e PHAGS-PA LETTER VOICELESS SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a871 PHAGS-PA SUBJOINED LETTER RA | a870 PHAGS-PA LETTER ASPIRATED FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a873 PHAGS-PA LETTER CANDRABINDU | a872 PHAGS-PA SUPERFIXED LETTER RA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a875 PHAGS-PA DOUBLE HEAD MARK | a874 PHAGS-PA SINGLE HEAD MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a877 PHAGS-PA MARK DOUBLE SHAD | a876 PHAGS-PA MARK SHAD */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a879 (null) | a878 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a87b (null) | a87a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a87d (null) | a87c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a87f (null) | a87e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a881 SAURASHTRA SIGN VISARGA | a880 SAURASHTRA SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a883 SAURASHTRA LETTER AA | a882 SAURASHTRA LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a885 SAURASHTRA LETTER II | a884 SAURASHTRA LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a887 SAURASHTRA LETTER UU | a886 SAURASHTRA LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a889 SAURASHTRA LETTER VOCALIC RR | a888 SAURASHTRA LETTER VOCALIC R */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a88b SAURASHTRA LETTER VOCALIC LL | a88a SAURASHTRA LETTER VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a88d SAURASHTRA LETTER EE | a88c SAURASHTRA LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a88f SAURASHTRA LETTER O | a88e SAURASHTRA LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a891 SAURASHTRA LETTER AU | a890 SAURASHTRA LETTER OO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a893 SAURASHTRA LETTER KHA | a892 SAURASHTRA LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a895 SAURASHTRA LETTER GHA | a894 SAURASHTRA LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a897 SAURASHTRA LETTER CA | a896 SAURASHTRA LETTER NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a899 SAURASHTRA LETTER JA | a898 SAURASHTRA LETTER CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a89b SAURASHTRA LETTER NYA | a89a SAURASHTRA LETTER JHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a89d SAURASHTRA LETTER TTHA | a89c SAURASHTRA LETTER TTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a89f SAURASHTRA LETTER DDHA | a89e SAURASHTRA LETTER DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8a1 SAURASHTRA LETTER TA | a8a0 SAURASHTRA LETTER NNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8a3 SAURASHTRA LETTER DA | a8a2 SAURASHTRA LETTER THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8a5 SAURASHTRA LETTER NA | a8a4 SAURASHTRA LETTER DHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8a7 SAURASHTRA LETTER PHA | a8a6 SAURASHTRA LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8a9 SAURASHTRA LETTER BHA | a8a8 SAURASHTRA LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8ab SAURASHTRA LETTER YA | a8aa SAURASHTRA LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8ad SAURASHTRA LETTER LA | a8ac SAURASHTRA LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8af SAURASHTRA LETTER SHA | a8ae SAURASHTRA LETTER VA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8b1 SAURASHTRA LETTER SA | a8b0 SAURASHTRA LETTER SSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8b3 SAURASHTRA LETTER LLA | a8b2 SAURASHTRA LETTER HA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8b5 SAURASHTRA VOWEL SIGN AA | a8b4 SAURASHTRA CONSONANT SIGN HAARU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8b7 SAURASHTRA VOWEL SIGN II | a8b6 SAURASHTRA VOWEL SIGN I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8b9 SAURASHTRA VOWEL SIGN UU | a8b8 SAURASHTRA VOWEL SIGN U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8bb SAURASHTRA VOWEL SIGN VOCALIC RR | a8ba SAURASHTRA VOWEL SIGN VOCALIC R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8bd SAURASHTRA VOWEL SIGN VOCALIC LL | a8bc SAURASHTRA VOWEL SIGN VOCALIC L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8bf SAURASHTRA VOWEL SIGN EE | a8be SAURASHTRA VOWEL SIGN E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8c1 SAURASHTRA VOWEL SIGN O | a8c0 SAURASHTRA VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8c3 SAURASHTRA VOWEL SIGN AU | a8c2 SAURASHTRA VOWEL SIGN OO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* a8c5 (null) | a8c4 SAURASHTRA SIGN VIRAMA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8c7 (null) | a8c6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8c9 (null) | a8c8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8cb (null) | a8ca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8cd (null) | a8cc (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a8cf SAURASHTRA DOUBLE DANDA | a8ce SAURASHTRA DANDA */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a8d1 SAURASHTRA DIGIT ONE | a8d0 SAURASHTRA DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a8d3 SAURASHTRA DIGIT THREE | a8d2 SAURASHTRA DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a8d5 SAURASHTRA DIGIT FIVE | a8d4 SAURASHTRA DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a8d7 SAURASHTRA DIGIT SEVEN | a8d6 SAURASHTRA DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a8d9 SAURASHTRA DIGIT NINE | a8d8 SAURASHTRA DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8db (null) | a8da (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8dd (null) | a8dc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8df (null) | a8de (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8e1 COMBINING DEVANAGARI DIGIT ONE | a8e0 COMBINING DEVANAGARI DIGIT ZERO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8e3 COMBINING DEVANAGARI DIGIT THREE | a8e2 COMBINING DEVANAGARI DIGIT TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8e5 COMBINING DEVANAGARI DIGIT FIVE | a8e4 COMBINING DEVANAGARI DIGIT FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8e7 COMBINING DEVANAGARI DIGIT SEVEN | a8e6 COMBINING DEVANAGARI DIGIT SIX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8e9 COMBINING DEVANAGARI DIGIT NINE | a8e8 COMBINING DEVANAGARI DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8eb COMBINING DEVANAGARI LETTER U | a8ea COMBINING DEVANAGARI LETTER A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8ed COMBINING DEVANAGARI LETTER NA | a8ec COMBINING DEVANAGARI LETTER KA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8ef COMBINING DEVANAGARI LETTER RA | a8ee COMBINING DEVANAGARI LETTER PA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8f1 COMBINING DEVANAGARI SIGN AVAGRAHA | a8f0 COMBINING DEVANAGARI LETTER VI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8f3 DEVANAGARI SIGN CANDRABINDU VIRAMA | a8f2 DEVANAGARI SIGN SPACING CANDRABINDU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8f5 DEVANAGARI SIGN CANDRABINDU TWO | a8f4 DEVANAGARI SIGN DOUBLE CANDRABINDU VIRA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8f7 DEVANAGARI SIGN CANDRABINDU AVAGRAHA | a8f6 DEVANAGARI SIGN CANDRABINDU THREE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a8f9 DEVANAGARI GAP FILLER | a8f8 DEVANAGARI SIGN PUSHPIKA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_PUNCT, /* a8fb DEVANAGARI HEADSTROKE | a8fa DEVANAGARI CARET */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8fd (null) | a8fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8ff (null) | a8fe (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a901 KAYAH LI DIGIT ONE | a900 KAYAH LI DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a903 KAYAH LI DIGIT THREE | a902 KAYAH LI DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a905 KAYAH LI DIGIT FIVE | a904 KAYAH LI DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a907 KAYAH LI DIGIT SEVEN | a906 KAYAH LI DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a909 KAYAH LI DIGIT NINE | a908 KAYAH LI DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a90b KAYAH LI LETTER KHA | a90a KAYAH LI LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a90d KAYAH LI LETTER NGA | a90c KAYAH LI LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a90f KAYAH LI LETTER SHA | a90e KAYAH LI LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a911 KAYAH LI LETTER NYA | a910 KAYAH LI LETTER ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a913 KAYAH LI LETTER HTA | a912 KAYAH LI LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a915 KAYAH LI LETTER PA | a914 KAYAH LI LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a917 KAYAH LI LETTER MA | a916 KAYAH LI LETTER PHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a919 KAYAH LI LETTER BA | a918 KAYAH LI LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a91b KAYAH LI LETTER YA | a91a KAYAH LI LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a91d KAYAH LI LETTER WA | a91c KAYAH LI LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a91f KAYAH LI LETTER HA | a91e KAYAH LI LETTER THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a921 KAYAH LI LETTER CA | a920 KAYAH LI LETTER VA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a923 KAYAH LI LETTER OE | a922 KAYAH LI LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a925 KAYAH LI LETTER OO | a924 KAYAH LI LETTER I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a927 KAYAH LI VOWEL E | a926 KAYAH LI VOWEL UE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a929 KAYAH LI VOWEL EE | a928 KAYAH LI VOWEL U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a92b KAYAH LI TONE PLOPHU | a92a KAYAH LI VOWEL O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a92d KAYAH LI TONE CALYA PLOPHU | a92c KAYAH LI TONE CALYA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a92f KAYAH LI SIGN SHYA | a92e KAYAH LI SIGN CWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a931 REJANG LETTER GA | a930 REJANG LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a933 REJANG LETTER TA | a932 REJANG LETTER NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a935 REJANG LETTER NA | a934 REJANG LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a937 REJANG LETTER BA | a936 REJANG LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a939 REJANG LETTER CA | a938 REJANG LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a93b REJANG LETTER NYA | a93a REJANG LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a93d REJANG LETTER RA | a93c REJANG LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a93f REJANG LETTER YA | a93e REJANG LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a941 REJANG LETTER HA | a940 REJANG LETTER WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a943 REJANG LETTER NGGA | a942 REJANG LETTER MBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a945 REJANG LETTER NYJA | a944 REJANG LETTER NDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* a947 REJANG VOWEL SIGN I | a946 REJANG LETTER A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a949 REJANG VOWEL SIGN E | a948 REJANG VOWEL SIGN U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a94b REJANG VOWEL SIGN O | a94a REJANG VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a94d REJANG VOWEL SIGN EU | a94c REJANG VOWEL SIGN AU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a94f REJANG CONSONANT SIGN NG | a94e REJANG VOWEL SIGN EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a951 REJANG CONSONANT SIGN R | a950 REJANG CONSONANT SIGN N */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a953 REJANG VIRAMA | a952 REJANG CONSONANT SIGN H */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a955 (null) | a954 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a957 (null) | a956 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a959 (null) | a958 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a95b (null) | a95a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a95d (null) | a95c (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_UNDEF, /* a95f REJANG SECTION MARK | a95e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a961 HANGUL CHOSEONG TIKEUT-PIEUP | a960 HANGUL CHOSEONG TIKEUT-MIEUM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a963 HANGUL CHOSEONG TIKEUT-CIEUC | a962 HANGUL CHOSEONG TIKEUT-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a965 HANGUL CHOSEONG RIEUL-SSANGKIYEOK | a964 HANGUL CHOSEONG RIEUL-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a967 HANGUL CHOSEONG RIEUL-SSANGTIKEUT | a966 HANGUL CHOSEONG RIEUL-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a969 HANGUL CHOSEONG RIEUL-PIEUP | a968 HANGUL CHOSEONG RIEUL-MIEUM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a96b HANGUL CHOSEONG RIEUL-KAPYEOUNPIEUP | a96a HANGUL CHOSEONG RIEUL-SSANGPIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a96d HANGUL CHOSEONG RIEUL-CIEUC | a96c HANGUL CHOSEONG RIEUL-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a96f HANGUL CHOSEONG MIEUM-KIYEOK | a96e HANGUL CHOSEONG RIEUL-KHIEUKH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a971 HANGUL CHOSEONG MIEUM-SIOS | a970 HANGUL CHOSEONG MIEUM-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a973 HANGUL CHOSEONG PIEUP-KHIEUKH | a972 HANGUL CHOSEONG PIEUP-SIOS-THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a975 HANGUL CHOSEONG SSANGSIOS-PIEUP | a974 HANGUL CHOSEONG PIEUP-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a977 HANGUL CHOSEONG IEUNG-HIEUH | a976 HANGUL CHOSEONG IEUNG-RIEUL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a979 HANGUL CHOSEONG SSANGTHIEUTH | a978 HANGUL CHOSEONG SSANGCIEUC-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a97b HANGUL CHOSEONG HIEUH-SIOS | a97a HANGUL CHOSEONG PHIEUPH-HIEUH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* a97d (null) | a97c HANGUL CHOSEONG SSANGYEORINHIEUH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a97f (null) | a97e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a981 JAVANESE SIGN CECAK | a980 JAVANESE SIGN PANYANGGA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a983 JAVANESE SIGN WIGNYAN | a982 JAVANESE SIGN LAYAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a985 JAVANESE LETTER I KAWI | a984 JAVANESE LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a987 JAVANESE LETTER II | a986 JAVANESE LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a989 JAVANESE LETTER PA CEREK | a988 JAVANESE LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a98b JAVANESE LETTER NGA LELET RASWADI | a98a JAVANESE LETTER NGA LELET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a98d JAVANESE LETTER AI | a98c JAVANESE LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a98f JAVANESE LETTER KA | a98e JAVANESE LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a991 JAVANESE LETTER KA MURDA | a990 JAVANESE LETTER KA SASAK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a993 JAVANESE LETTER GA MURDA | a992 JAVANESE LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a995 JAVANESE LETTER CA | a994 JAVANESE LETTER NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a997 JAVANESE LETTER JA | a996 JAVANESE LETTER CA MURDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a999 JAVANESE LETTER JA MAHAPRANA | a998 JAVANESE LETTER NYA MURDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a99b JAVANESE LETTER TTA | a99a JAVANESE LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a99d JAVANESE LETTER DDA | a99c JAVANESE LETTER TTA MAHAPRANA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a99f JAVANESE LETTER NA MURDA | a99e JAVANESE LETTER DDA MAHAPRANA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9a1 JAVANESE LETTER TA MURDA | a9a0 JAVANESE LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9a3 JAVANESE LETTER DA MAHAPRANA | a9a2 JAVANESE LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9a5 JAVANESE LETTER PA | a9a4 JAVANESE LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9a7 JAVANESE LETTER BA | a9a6 JAVANESE LETTER PA MURDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9a9 JAVANESE LETTER MA | a9a8 JAVANESE LETTER BA MURDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9ab JAVANESE LETTER RA | a9aa JAVANESE LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9ad JAVANESE LETTER LA | a9ac JAVANESE LETTER RA AGUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9af JAVANESE LETTER SA MURDA | a9ae JAVANESE LETTER WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9b1 JAVANESE LETTER SA | a9b0 JAVANESE LETTER SA MAHAPRANA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* a9b3 JAVANESE SIGN CECAK TELU | a9b2 JAVANESE LETTER HA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a9b5 JAVANESE VOWEL SIGN TOLONG | a9b4 JAVANESE VOWEL SIGN TARUNG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a9b7 JAVANESE VOWEL SIGN WULU MELIK | a9b6 JAVANESE VOWEL SIGN WULU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a9b9 JAVANESE VOWEL SIGN SUKU MENDUT | a9b8 JAVANESE VOWEL SIGN SUKU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a9bb JAVANESE VOWEL SIGN DIRGA MURE | a9ba JAVANESE VOWEL SIGN TALING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a9bd JAVANESE CONSONANT SIGN KERET | a9bc JAVANESE VOWEL SIGN PEPET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a9bf JAVANESE CONSONANT SIGN CAKRA | a9be JAVANESE CONSONANT SIGN PENGKAL */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* a9c1 JAVANESE LEFT RERENGGAN | a9c0 JAVANESE PANGKON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a9c3 JAVANESE PADA ANDAP | a9c2 JAVANESE RIGHT RERENGGAN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a9c5 JAVANESE PADA LUHUR | a9c4 JAVANESE PADA MADYA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a9c7 JAVANESE PADA PANGKAT | a9c6 JAVANESE PADA WINDU */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a9c9 JAVANESE PADA LUNGSI | a9c8 JAVANESE PADA LINGSA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a9cb JAVANESE PADA ADEG ADEG | a9ca JAVANESE PADA ADEG */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a9cd JAVANESE TURNED PADA PISELEH | a9cc JAVANESE PADA PISELEH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* a9cf JAVANESE PANGRANGKEP | a9ce (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a9d1 JAVANESE DIGIT ONE | a9d0 JAVANESE DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a9d3 JAVANESE DIGIT THREE | a9d2 JAVANESE DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a9d5 JAVANESE DIGIT FIVE | a9d4 JAVANESE DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a9d7 JAVANESE DIGIT SEVEN | a9d6 JAVANESE DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a9d9 JAVANESE DIGIT NINE | a9d8 JAVANESE DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9db (null) | a9da (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9dd (null) | a9dc (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a9df JAVANESE PADA ISEN-ISEN | a9de JAVANESE PADA TIRTA TUMETES */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9e1 (null) | a9e0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9e3 (null) | a9e2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9e5 (null) | a9e4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9e7 (null) | a9e6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9e9 (null) | a9e8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9eb (null) | a9ea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9ed (null) | a9ec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9ef (null) | a9ee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9f1 (null) | a9f0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9f3 (null) | a9f2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9f5 (null) | a9f4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9f7 (null) | a9f6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9f9 (null) | a9f8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9fb (null) | a9fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9fd (null) | a9fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9ff (null) | a9fe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa01 CHAM LETTER I | aa00 CHAM LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa03 CHAM LETTER E | aa02 CHAM LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa05 CHAM LETTER O | aa04 CHAM LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa07 CHAM LETTER KHA | aa06 CHAM LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa09 CHAM LETTER GHA | aa08 CHAM LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa0b CHAM LETTER NGA | aa0a CHAM LETTER NGUE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa0d CHAM LETTER CHHA | aa0c CHAM LETTER CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa0f CHAM LETTER JHA | aa0e CHAM LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa11 CHAM LETTER NHA | aa10 CHAM LETTER NHUE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa13 CHAM LETTER TA | aa12 CHAM LETTER NHJA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa15 CHAM LETTER DA | aa14 CHAM LETTER THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa17 CHAM LETTER NUE | aa16 CHAM LETTER DHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa19 CHAM LETTER DDA | aa18 CHAM LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa1b CHAM LETTER PPA | aa1a CHAM LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa1d CHAM LETTER BA | aa1c CHAM LETTER PHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa1f CHAM LETTER MUE | aa1e CHAM LETTER BHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa21 CHAM LETTER BBA | aa20 CHAM LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa23 CHAM LETTER RA | aa22 CHAM LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa25 CHAM LETTER VA | aa24 CHAM LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa27 CHAM LETTER SA | aa26 CHAM LETTER SSA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aa29 CHAM VOWEL SIGN AA | aa28 CHAM LETTER HA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aa2b CHAM VOWEL SIGN II | aa2a CHAM VOWEL SIGN I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aa2d CHAM VOWEL SIGN U | aa2c CHAM VOWEL SIGN EI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aa2f CHAM VOWEL SIGN O | aa2e CHAM VOWEL SIGN OE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aa31 CHAM VOWEL SIGN AU | aa30 CHAM VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aa33 CHAM CONSONANT SIGN YA | aa32 CHAM VOWEL SIGN UE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aa35 CHAM CONSONANT SIGN LA | aa34 CHAM CONSONANT SIGN RA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* aa37 (null) | aa36 CHAM CONSONANT SIGN WA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aa39 (null) | aa38 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aa3b (null) | aa3a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aa3d (null) | aa3c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aa3f (null) | aa3e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa41 CHAM LETTER FINAL G | aa40 CHAM LETTER FINAL K */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aa43 CHAM CONSONANT SIGN FINAL NG | aa42 CHAM LETTER FINAL NG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa45 CHAM LETTER FINAL T | aa44 CHAM LETTER FINAL CH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa47 CHAM LETTER FINAL P | aa46 CHAM LETTER FINAL N */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa49 CHAM LETTER FINAL R | aa48 CHAM LETTER FINAL Y */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa4b CHAM LETTER FINAL SS | aa4a CHAM LETTER FINAL L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aa4d CHAM CONSONANT SIGN FINAL H | aa4c CHAM CONSONANT SIGN FINAL M */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aa4f (null) | aa4e (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* aa51 CHAM DIGIT ONE | aa50 CHAM DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* aa53 CHAM DIGIT THREE | aa52 CHAM DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* aa55 CHAM DIGIT FIVE | aa54 CHAM DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* aa57 CHAM DIGIT SEVEN | aa56 CHAM DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* aa59 CHAM DIGIT NINE | aa58 CHAM DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aa5b (null) | aa5a (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* aa5d CHAM PUNCTUATION DANDA | aa5c CHAM PUNCTUATION SPIRAL */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* aa5f CHAM PUNCTUATION TRIPLE DANDA | aa5e CHAM PUNCTUATION DOUBLE DANDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa61 MYANMAR LETTER KHAMTI CA | aa60 MYANMAR LETTER KHAMTI GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa63 MYANMAR LETTER KHAMTI JA | aa62 MYANMAR LETTER KHAMTI CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa65 MYANMAR LETTER KHAMTI NYA | aa64 MYANMAR LETTER KHAMTI JHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa67 MYANMAR LETTER KHAMTI TTHA | aa66 MYANMAR LETTER KHAMTI TTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa69 MYANMAR LETTER KHAMTI DDHA | aa68 MYANMAR LETTER KHAMTI DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa6b MYANMAR LETTER KHAMTI NA | aa6a MYANMAR LETTER KHAMTI DHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa6d MYANMAR LETTER KHAMTI HA | aa6c MYANMAR LETTER KHAMTI SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa6f MYANMAR LETTER KHAMTI FA | aa6e MYANMAR LETTER KHAMTI HHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* aa71 MYANMAR LETTER KHAMTI XA | aa70 MYANMAR MODIFIER LETTER KHAMTI REDUPLIC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa73 MYANMAR LETTER KHAMTI RA | aa72 MYANMAR LETTER KHAMTI ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa75 MYANMAR LOGOGRAM KHAMTI QN | aa74 MYANMAR LOGOGRAM KHAMTI OAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aa77 MYANMAR SYMBOL AITON EXCLAMATION | aa76 MYANMAR LOGOGRAM KHAMTI HM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aa79 MYANMAR SYMBOL AITON TWO | aa78 MYANMAR SYMBOL AITON ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aa7b MYANMAR SIGN PAO KAREN TONE | aa7a MYANMAR LETTER AITON RA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aa7d (null) | aa7c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aa7f (null) | aa7e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa81 TAI VIET LETTER HIGH KO | aa80 TAI VIET LETTER LOW KO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa83 TAI VIET LETTER HIGH KHO | aa82 TAI VIET LETTER LOW KHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa85 TAI VIET LETTER HIGH KHHO | aa84 TAI VIET LETTER LOW KHHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa87 TAI VIET LETTER HIGH GO | aa86 TAI VIET LETTER LOW GO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa89 TAI VIET LETTER HIGH NGO | aa88 TAI VIET LETTER LOW NGO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa8b TAI VIET LETTER HIGH CO | aa8a TAI VIET LETTER LOW CO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa8d TAI VIET LETTER HIGH CHO | aa8c TAI VIET LETTER LOW CHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa8f TAI VIET LETTER HIGH SO | aa8e TAI VIET LETTER LOW SO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa91 TAI VIET LETTER HIGH NYO | aa90 TAI VIET LETTER LOW NYO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa93 TAI VIET LETTER HIGH DO | aa92 TAI VIET LETTER LOW DO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa95 TAI VIET LETTER HIGH TO | aa94 TAI VIET LETTER LOW TO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa97 TAI VIET LETTER HIGH THO | aa96 TAI VIET LETTER LOW THO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa99 TAI VIET LETTER HIGH NO | aa98 TAI VIET LETTER LOW NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa9b TAI VIET LETTER HIGH BO | aa9a TAI VIET LETTER LOW BO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa9d TAI VIET LETTER HIGH PO | aa9c TAI VIET LETTER LOW PO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa9f TAI VIET LETTER HIGH PHO | aa9e TAI VIET LETTER LOW PHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aaa1 TAI VIET LETTER HIGH FO | aaa0 TAI VIET LETTER LOW FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aaa3 TAI VIET LETTER HIGH MO | aaa2 TAI VIET LETTER LOW MO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aaa5 TAI VIET LETTER HIGH YO | aaa4 TAI VIET LETTER LOW YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aaa7 TAI VIET LETTER HIGH RO | aaa6 TAI VIET LETTER LOW RO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aaa9 TAI VIET LETTER HIGH LO | aaa8 TAI VIET LETTER LOW LO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aaab TAI VIET LETTER HIGH VO | aaaa TAI VIET LETTER LOW VO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aaad TAI VIET LETTER HIGH HO | aaac TAI VIET LETTER LOW HO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aaaf TAI VIET LETTER HIGH O | aaae TAI VIET LETTER LOW O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* aab1 TAI VIET VOWEL AA | aab0 TAI VIET MAI KANG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aab3 TAI VIET VOWEL UE | aab2 TAI VIET VOWEL I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* aab5 TAI VIET VOWEL E | aab4 TAI VIET VOWEL U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aab7 TAI VIET MAI KHIT | aab6 TAI VIET VOWEL O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* aab9 TAI VIET VOWEL UEA | aab8 TAI VIET VOWEL IA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aabb TAI VIET VOWEL AUE | aaba TAI VIET VOWEL UA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aabd TAI VIET VOWEL AN | aabc TAI VIET VOWEL AY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aabf TAI VIET TONE MAI EK | aabe TAI VIET VOWEL AM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aac1 TAI VIET TONE MAI THO | aac0 TAI VIET TONE MAI NUENG */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* aac3 (null) | aac2 TAI VIET TONE MAI SONG */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aac5 (null) | aac4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aac7 (null) | aac6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aac9 (null) | aac8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aacb (null) | aaca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aacd (null) | aacc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aacf (null) | aace (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aad1 (null) | aad0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aad3 (null) | aad2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aad5 (null) | aad4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aad7 (null) | aad6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aad9 (null) | aad8 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* aadb TAI VIET SYMBOL KON | aada (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aadd TAI VIET SYMBOL SAM | aadc TAI VIET SYMBOL NUENG */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* aadf TAI VIET SYMBOL KOI KOI | aade TAI VIET SYMBOL HO HOI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aae1 MEETEI MAYEK LETTER O | aae0 MEETEI MAYEK LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aae3 MEETEI MAYEK LETTER NYA | aae2 MEETEI MAYEK LETTER CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aae5 MEETEI MAYEK LETTER TTHA | aae4 MEETEI MAYEK LETTER TTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aae7 MEETEI MAYEK LETTER DDHA | aae6 MEETEI MAYEK LETTER DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aae9 MEETEI MAYEK LETTER SHA | aae8 MEETEI MAYEK LETTER NNA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aaeb MEETEI MAYEK VOWEL SIGN II | aaea MEETEI MAYEK LETTER SSA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aaed MEETEI MAYEK VOWEL SIGN AAI | aaec MEETEI MAYEK VOWEL SIGN UU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aaef MEETEI MAYEK VOWEL SIGN AAU | aaee MEETEI MAYEK VOWEL SIGN AU */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* aaf1 MEETEI MAYEK AHANG KHUDAM | aaf0 MEETEI MAYEK CHEIKHAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aaf3 MEETEI MAYEK SYLLABLE REPETITION MARK | aaf2 MEETEI MAYEK ANJI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aaf5 MEETEI MAYEK VOWEL SIGN VISARGA | aaf4 MEETEI MAYEK WORD REPETITION MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* aaf7 (null) | aaf6 MEETEI MAYEK VIRAMA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aaf9 (null) | aaf8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aafb (null) | aafa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aafd (null) | aafc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aaff (null) | aafe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* ab01 ETHIOPIC SYLLABLE TTHU | ab00 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab03 ETHIOPIC SYLLABLE TTHAA | ab02 ETHIOPIC SYLLABLE TTHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab05 ETHIOPIC SYLLABLE TTHE | ab04 ETHIOPIC SYLLABLE TTHEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* ab07 (null) | ab06 ETHIOPIC SYLLABLE TTHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* ab09 ETHIOPIC SYLLABLE DDHU | ab08 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab0b ETHIOPIC SYLLABLE DDHAA | ab0a ETHIOPIC SYLLABLE DDHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab0d ETHIOPIC SYLLABLE DDHE | ab0c ETHIOPIC SYLLABLE DDHEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* ab0f (null) | ab0e ETHIOPIC SYLLABLE DDHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* ab11 ETHIOPIC SYLLABLE DZU | ab10 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab13 ETHIOPIC SYLLABLE DZAA | ab12 ETHIOPIC SYLLABLE DZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab15 ETHIOPIC SYLLABLE DZE | ab14 ETHIOPIC SYLLABLE DZEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* ab17 (null) | ab16 ETHIOPIC SYLLABLE DZO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab19 (null) | ab18 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab1b (null) | ab1a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab1d (null) | ab1c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab1f (null) | ab1e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab21 ETHIOPIC SYLLABLE CCHHU | ab20 ETHIOPIC SYLLABLE CCHHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab23 ETHIOPIC SYLLABLE CCHHAA | ab22 ETHIOPIC SYLLABLE CCHHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab25 ETHIOPIC SYLLABLE CCHHE | ab24 ETHIOPIC SYLLABLE CCHHEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* ab27 (null) | ab26 ETHIOPIC SYLLABLE CCHHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab29 ETHIOPIC SYLLABLE BBU | ab28 ETHIOPIC SYLLABLE BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab2b ETHIOPIC SYLLABLE BBAA | ab2a ETHIOPIC SYLLABLE BBI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab2d ETHIOPIC SYLLABLE BBE | ab2c ETHIOPIC SYLLABLE BBEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* ab2f (null) | ab2e ETHIOPIC SYLLABLE BBO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab31 (null) | ab30 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab33 (null) | ab32 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab35 (null) | ab34 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab37 (null) | ab36 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab39 (null) | ab38 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab3b (null) | ab3a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab3d (null) | ab3c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab3f (null) | ab3e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab41 (null) | ab40 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab43 (null) | ab42 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab45 (null) | ab44 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab47 (null) | ab46 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab49 (null) | ab48 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab4b (null) | ab4a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab4d (null) | ab4c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab4f (null) | ab4e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab51 (null) | ab50 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab53 (null) | ab52 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab55 (null) | ab54 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab57 (null) | ab56 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab59 (null) | ab58 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab5b (null) | ab5a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab5d (null) | ab5c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab5f (null) | ab5e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab61 (null) | ab60 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab63 (null) | ab62 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab65 (null) | ab64 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab67 (null) | ab66 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab69 (null) | ab68 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab6b (null) | ab6a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab6d (null) | ab6c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab6f (null) | ab6e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab71 (null) | ab70 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab73 (null) | ab72 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab75 (null) | ab74 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab77 (null) | ab76 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab79 (null) | ab78 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab7b (null) | ab7a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab7d (null) | ab7c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab7f (null) | ab7e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab81 (null) | ab80 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab83 (null) | ab82 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab85 (null) | ab84 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab87 (null) | ab86 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab89 (null) | ab88 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab8b (null) | ab8a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab8d (null) | ab8c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab8f (null) | ab8e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab91 (null) | ab90 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab93 (null) | ab92 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab95 (null) | ab94 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab97 (null) | ab96 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab99 (null) | ab98 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab9b (null) | ab9a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab9d (null) | ab9c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab9f (null) | ab9e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aba1 (null) | aba0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aba3 (null) | aba2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aba5 (null) | aba4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aba7 (null) | aba6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aba9 (null) | aba8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abab (null) | abaa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abad (null) | abac (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abaf (null) | abae (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abb1 (null) | abb0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abb3 (null) | abb2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abb5 (null) | abb4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abb7 (null) | abb6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abb9 (null) | abb8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abbb (null) | abba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abbd (null) | abbc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abbf (null) | abbe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abc1 MEETEI MAYEK LETTER SAM | abc0 MEETEI MAYEK LETTER KOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abc3 MEETEI MAYEK LETTER MIT | abc2 MEETEI MAYEK LETTER LAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abc5 MEETEI MAYEK LETTER NA | abc4 MEETEI MAYEK LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abc7 MEETEI MAYEK LETTER TIL | abc6 MEETEI MAYEK LETTER CHIL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abc9 MEETEI MAYEK LETTER NGOU | abc8 MEETEI MAYEK LETTER KHOU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abcb MEETEI MAYEK LETTER WAI | abca MEETEI MAYEK LETTER THOU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abcd MEETEI MAYEK LETTER HUK | abcc MEETEI MAYEK LETTER YANG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abcf MEETEI MAYEK LETTER I | abce MEETEI MAYEK LETTER UN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abd1 MEETEI MAYEK LETTER ATIYA | abd0 MEETEI MAYEK LETTER PHAM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abd3 MEETEI MAYEK LETTER JHAM | abd2 MEETEI MAYEK LETTER GOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abd5 MEETEI MAYEK LETTER BA | abd4 MEETEI MAYEK LETTER RAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abd7 MEETEI MAYEK LETTER DIL | abd6 MEETEI MAYEK LETTER JIL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abd9 MEETEI MAYEK LETTER DHOU | abd8 MEETEI MAYEK LETTER GHOU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abdb MEETEI MAYEK LETTER KOK LONSUM | abda MEETEI MAYEK LETTER BHAM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abdd MEETEI MAYEK LETTER MIT LONSUM | abdc MEETEI MAYEK LETTER LAI LONSUM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abdf MEETEI MAYEK LETTER NA LONSUM | abde MEETEI MAYEK LETTER PA LONSUM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abe1 MEETEI MAYEK LETTER NGOU LONSUM | abe0 MEETEI MAYEK LETTER TIL LONSUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* abe3 MEETEI MAYEK VOWEL SIGN ONAP | abe2 MEETEI MAYEK LETTER I LONSUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* abe5 MEETEI MAYEK VOWEL SIGN ANAP | abe4 MEETEI MAYEK VOWEL SIGN INAP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* abe7 MEETEI MAYEK VOWEL SIGN SOUNAP | abe6 MEETEI MAYEK VOWEL SIGN YENAP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* abe9 MEETEI MAYEK VOWEL SIGN CHEINAP | abe8 MEETEI MAYEK VOWEL SIGN UNAP */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* abeb MEETEI MAYEK CHEIKHEI | abea MEETEI MAYEK VOWEL SIGN NUNG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* abed MEETEI MAYEK APUN IYEK | abec MEETEI MAYEK LUM IYEK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abef (null) | abee (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* abf1 MEETEI MAYEK DIGIT ONE | abf0 MEETEI MAYEK DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* abf3 MEETEI MAYEK DIGIT THREE | abf2 MEETEI MAYEK DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* abf5 MEETEI MAYEK DIGIT FIVE | abf4 MEETEI MAYEK DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* abf7 MEETEI MAYEK DIGIT SEVEN | abf6 MEETEI MAYEK DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* abf9 MEETEI MAYEK DIGIT NINE | abf8 MEETEI MAYEK DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abfb (null) | abfa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abfd (null) | abfc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abff (null) | abfe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac01 Hangul Syllable-AC01 | ac00 Hangul Syllable-AC00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac03 Hangul Syllable-AC03 | ac02 Hangul Syllable-AC02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac05 Hangul Syllable-AC05 | ac04 Hangul Syllable-AC04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac07 Hangul Syllable-AC07 | ac06 Hangul Syllable-AC06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac09 Hangul Syllable-AC09 | ac08 Hangul Syllable-AC08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac0b Hangul Syllable-AC0B | ac0a Hangul Syllable-AC0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac0d Hangul Syllable-AC0D | ac0c Hangul Syllable-AC0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac0f Hangul Syllable-AC0F | ac0e Hangul Syllable-AC0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac11 Hangul Syllable-AC11 | ac10 Hangul Syllable-AC10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac13 Hangul Syllable-AC13 | ac12 Hangul Syllable-AC12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac15 Hangul Syllable-AC15 | ac14 Hangul Syllable-AC14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac17 Hangul Syllable-AC17 | ac16 Hangul Syllable-AC16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac19 Hangul Syllable-AC19 | ac18 Hangul Syllable-AC18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac1b Hangul Syllable-AC1B | ac1a Hangul Syllable-AC1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac1d Hangul Syllable-AC1D | ac1c Hangul Syllable-AC1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac1f Hangul Syllable-AC1F | ac1e Hangul Syllable-AC1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac21 Hangul Syllable-AC21 | ac20 Hangul Syllable-AC20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac23 Hangul Syllable-AC23 | ac22 Hangul Syllable-AC22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac25 Hangul Syllable-AC25 | ac24 Hangul Syllable-AC24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac27 Hangul Syllable-AC27 | ac26 Hangul Syllable-AC26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac29 Hangul Syllable-AC29 | ac28 Hangul Syllable-AC28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac2b Hangul Syllable-AC2B | ac2a Hangul Syllable-AC2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac2d Hangul Syllable-AC2D | ac2c Hangul Syllable-AC2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac2f Hangul Syllable-AC2F | ac2e Hangul Syllable-AC2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac31 Hangul Syllable-AC31 | ac30 Hangul Syllable-AC30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac33 Hangul Syllable-AC33 | ac32 Hangul Syllable-AC32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac35 Hangul Syllable-AC35 | ac34 Hangul Syllable-AC34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac37 Hangul Syllable-AC37 | ac36 Hangul Syllable-AC36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac39 Hangul Syllable-AC39 | ac38 Hangul Syllable-AC38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac3b Hangul Syllable-AC3B | ac3a Hangul Syllable-AC3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac3d Hangul Syllable-AC3D | ac3c Hangul Syllable-AC3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac3f Hangul Syllable-AC3F | ac3e Hangul Syllable-AC3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac41 Hangul Syllable-AC41 | ac40 Hangul Syllable-AC40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac43 Hangul Syllable-AC43 | ac42 Hangul Syllable-AC42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac45 Hangul Syllable-AC45 | ac44 Hangul Syllable-AC44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac47 Hangul Syllable-AC47 | ac46 Hangul Syllable-AC46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac49 Hangul Syllable-AC49 | ac48 Hangul Syllable-AC48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac4b Hangul Syllable-AC4B | ac4a Hangul Syllable-AC4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac4d Hangul Syllable-AC4D | ac4c Hangul Syllable-AC4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac4f Hangul Syllable-AC4F | ac4e Hangul Syllable-AC4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac51 Hangul Syllable-AC51 | ac50 Hangul Syllable-AC50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac53 Hangul Syllable-AC53 | ac52 Hangul Syllable-AC52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac55 Hangul Syllable-AC55 | ac54 Hangul Syllable-AC54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac57 Hangul Syllable-AC57 | ac56 Hangul Syllable-AC56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac59 Hangul Syllable-AC59 | ac58 Hangul Syllable-AC58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac5b Hangul Syllable-AC5B | ac5a Hangul Syllable-AC5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac5d Hangul Syllable-AC5D | ac5c Hangul Syllable-AC5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac5f Hangul Syllable-AC5F | ac5e Hangul Syllable-AC5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac61 Hangul Syllable-AC61 | ac60 Hangul Syllable-AC60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac63 Hangul Syllable-AC63 | ac62 Hangul Syllable-AC62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac65 Hangul Syllable-AC65 | ac64 Hangul Syllable-AC64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac67 Hangul Syllable-AC67 | ac66 Hangul Syllable-AC66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac69 Hangul Syllable-AC69 | ac68 Hangul Syllable-AC68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac6b Hangul Syllable-AC6B | ac6a Hangul Syllable-AC6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac6d Hangul Syllable-AC6D | ac6c Hangul Syllable-AC6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac6f Hangul Syllable-AC6F | ac6e Hangul Syllable-AC6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac71 Hangul Syllable-AC71 | ac70 Hangul Syllable-AC70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac73 Hangul Syllable-AC73 | ac72 Hangul Syllable-AC72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac75 Hangul Syllable-AC75 | ac74 Hangul Syllable-AC74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac77 Hangul Syllable-AC77 | ac76 Hangul Syllable-AC76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac79 Hangul Syllable-AC79 | ac78 Hangul Syllable-AC78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac7b Hangul Syllable-AC7B | ac7a Hangul Syllable-AC7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac7d Hangul Syllable-AC7D | ac7c Hangul Syllable-AC7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac7f Hangul Syllable-AC7F | ac7e Hangul Syllable-AC7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac81 Hangul Syllable-AC81 | ac80 Hangul Syllable-AC80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac83 Hangul Syllable-AC83 | ac82 Hangul Syllable-AC82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac85 Hangul Syllable-AC85 | ac84 Hangul Syllable-AC84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac87 Hangul Syllable-AC87 | ac86 Hangul Syllable-AC86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac89 Hangul Syllable-AC89 | ac88 Hangul Syllable-AC88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac8b Hangul Syllable-AC8B | ac8a Hangul Syllable-AC8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac8d Hangul Syllable-AC8D | ac8c Hangul Syllable-AC8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac8f Hangul Syllable-AC8F | ac8e Hangul Syllable-AC8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac91 Hangul Syllable-AC91 | ac90 Hangul Syllable-AC90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac93 Hangul Syllable-AC93 | ac92 Hangul Syllable-AC92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac95 Hangul Syllable-AC95 | ac94 Hangul Syllable-AC94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac97 Hangul Syllable-AC97 | ac96 Hangul Syllable-AC96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac99 Hangul Syllable-AC99 | ac98 Hangul Syllable-AC98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac9b Hangul Syllable-AC9B | ac9a Hangul Syllable-AC9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac9d Hangul Syllable-AC9D | ac9c Hangul Syllable-AC9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac9f Hangul Syllable-AC9F | ac9e Hangul Syllable-AC9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aca1 Hangul Syllable-ACA1 | aca0 Hangul Syllable-ACA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aca3 Hangul Syllable-ACA3 | aca2 Hangul Syllable-ACA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aca5 Hangul Syllable-ACA5 | aca4 Hangul Syllable-ACA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aca7 Hangul Syllable-ACA7 | aca6 Hangul Syllable-ACA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aca9 Hangul Syllable-ACA9 | aca8 Hangul Syllable-ACA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acab Hangul Syllable-ACAB | acaa Hangul Syllable-ACAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acad Hangul Syllable-ACAD | acac Hangul Syllable-ACAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acaf Hangul Syllable-ACAF | acae Hangul Syllable-ACAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acb1 Hangul Syllable-ACB1 | acb0 Hangul Syllable-ACB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acb3 Hangul Syllable-ACB3 | acb2 Hangul Syllable-ACB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acb5 Hangul Syllable-ACB5 | acb4 Hangul Syllable-ACB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acb7 Hangul Syllable-ACB7 | acb6 Hangul Syllable-ACB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acb9 Hangul Syllable-ACB9 | acb8 Hangul Syllable-ACB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acbb Hangul Syllable-ACBB | acba Hangul Syllable-ACBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acbd Hangul Syllable-ACBD | acbc Hangul Syllable-ACBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acbf Hangul Syllable-ACBF | acbe Hangul Syllable-ACBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acc1 Hangul Syllable-ACC1 | acc0 Hangul Syllable-ACC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acc3 Hangul Syllable-ACC3 | acc2 Hangul Syllable-ACC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acc5 Hangul Syllable-ACC5 | acc4 Hangul Syllable-ACC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acc7 Hangul Syllable-ACC7 | acc6 Hangul Syllable-ACC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acc9 Hangul Syllable-ACC9 | acc8 Hangul Syllable-ACC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* accb Hangul Syllable-ACCB | acca Hangul Syllable-ACCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* accd Hangul Syllable-ACCD | accc Hangul Syllable-ACCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* accf Hangul Syllable-ACCF | acce Hangul Syllable-ACCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acd1 Hangul Syllable-ACD1 | acd0 Hangul Syllable-ACD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acd3 Hangul Syllable-ACD3 | acd2 Hangul Syllable-ACD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acd5 Hangul Syllable-ACD5 | acd4 Hangul Syllable-ACD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acd7 Hangul Syllable-ACD7 | acd6 Hangul Syllable-ACD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acd9 Hangul Syllable-ACD9 | acd8 Hangul Syllable-ACD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acdb Hangul Syllable-ACDB | acda Hangul Syllable-ACDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acdd Hangul Syllable-ACDD | acdc Hangul Syllable-ACDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acdf Hangul Syllable-ACDF | acde Hangul Syllable-ACDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ace1 Hangul Syllable-ACE1 | ace0 Hangul Syllable-ACE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ace3 Hangul Syllable-ACE3 | ace2 Hangul Syllable-ACE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ace5 Hangul Syllable-ACE5 | ace4 Hangul Syllable-ACE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ace7 Hangul Syllable-ACE7 | ace6 Hangul Syllable-ACE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ace9 Hangul Syllable-ACE9 | ace8 Hangul Syllable-ACE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aceb Hangul Syllable-ACEB | acea Hangul Syllable-ACEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aced Hangul Syllable-ACED | acec Hangul Syllable-ACEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acef Hangul Syllable-ACEF | acee Hangul Syllable-ACEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acf1 Hangul Syllable-ACF1 | acf0 Hangul Syllable-ACF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acf3 Hangul Syllable-ACF3 | acf2 Hangul Syllable-ACF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acf5 Hangul Syllable-ACF5 | acf4 Hangul Syllable-ACF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acf7 Hangul Syllable-ACF7 | acf6 Hangul Syllable-ACF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acf9 Hangul Syllable-ACF9 | acf8 Hangul Syllable-ACF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acfb Hangul Syllable-ACFB | acfa Hangul Syllable-ACFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acfd Hangul Syllable-ACFD | acfc Hangul Syllable-ACFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acff Hangul Syllable-ACFF | acfe Hangul Syllable-ACFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad01 Hangul Syllable-AD01 | ad00 Hangul Syllable-AD00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad03 Hangul Syllable-AD03 | ad02 Hangul Syllable-AD02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad05 Hangul Syllable-AD05 | ad04 Hangul Syllable-AD04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad07 Hangul Syllable-AD07 | ad06 Hangul Syllable-AD06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad09 Hangul Syllable-AD09 | ad08 Hangul Syllable-AD08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad0b Hangul Syllable-AD0B | ad0a Hangul Syllable-AD0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad0d Hangul Syllable-AD0D | ad0c Hangul Syllable-AD0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad0f Hangul Syllable-AD0F | ad0e Hangul Syllable-AD0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad11 Hangul Syllable-AD11 | ad10 Hangul Syllable-AD10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad13 Hangul Syllable-AD13 | ad12 Hangul Syllable-AD12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad15 Hangul Syllable-AD15 | ad14 Hangul Syllable-AD14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad17 Hangul Syllable-AD17 | ad16 Hangul Syllable-AD16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad19 Hangul Syllable-AD19 | ad18 Hangul Syllable-AD18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad1b Hangul Syllable-AD1B | ad1a Hangul Syllable-AD1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad1d Hangul Syllable-AD1D | ad1c Hangul Syllable-AD1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad1f Hangul Syllable-AD1F | ad1e Hangul Syllable-AD1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad21 Hangul Syllable-AD21 | ad20 Hangul Syllable-AD20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad23 Hangul Syllable-AD23 | ad22 Hangul Syllable-AD22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad25 Hangul Syllable-AD25 | ad24 Hangul Syllable-AD24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad27 Hangul Syllable-AD27 | ad26 Hangul Syllable-AD26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad29 Hangul Syllable-AD29 | ad28 Hangul Syllable-AD28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad2b Hangul Syllable-AD2B | ad2a Hangul Syllable-AD2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad2d Hangul Syllable-AD2D | ad2c Hangul Syllable-AD2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad2f Hangul Syllable-AD2F | ad2e Hangul Syllable-AD2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad31 Hangul Syllable-AD31 | ad30 Hangul Syllable-AD30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad33 Hangul Syllable-AD33 | ad32 Hangul Syllable-AD32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad35 Hangul Syllable-AD35 | ad34 Hangul Syllable-AD34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad37 Hangul Syllable-AD37 | ad36 Hangul Syllable-AD36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad39 Hangul Syllable-AD39 | ad38 Hangul Syllable-AD38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad3b Hangul Syllable-AD3B | ad3a Hangul Syllable-AD3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad3d Hangul Syllable-AD3D | ad3c Hangul Syllable-AD3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad3f Hangul Syllable-AD3F | ad3e Hangul Syllable-AD3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad41 Hangul Syllable-AD41 | ad40 Hangul Syllable-AD40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad43 Hangul Syllable-AD43 | ad42 Hangul Syllable-AD42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad45 Hangul Syllable-AD45 | ad44 Hangul Syllable-AD44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad47 Hangul Syllable-AD47 | ad46 Hangul Syllable-AD46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad49 Hangul Syllable-AD49 | ad48 Hangul Syllable-AD48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad4b Hangul Syllable-AD4B | ad4a Hangul Syllable-AD4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad4d Hangul Syllable-AD4D | ad4c Hangul Syllable-AD4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad4f Hangul Syllable-AD4F | ad4e Hangul Syllable-AD4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad51 Hangul Syllable-AD51 | ad50 Hangul Syllable-AD50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad53 Hangul Syllable-AD53 | ad52 Hangul Syllable-AD52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad55 Hangul Syllable-AD55 | ad54 Hangul Syllable-AD54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad57 Hangul Syllable-AD57 | ad56 Hangul Syllable-AD56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad59 Hangul Syllable-AD59 | ad58 Hangul Syllable-AD58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad5b Hangul Syllable-AD5B | ad5a Hangul Syllable-AD5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad5d Hangul Syllable-AD5D | ad5c Hangul Syllable-AD5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad5f Hangul Syllable-AD5F | ad5e Hangul Syllable-AD5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad61 Hangul Syllable-AD61 | ad60 Hangul Syllable-AD60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad63 Hangul Syllable-AD63 | ad62 Hangul Syllable-AD62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad65 Hangul Syllable-AD65 | ad64 Hangul Syllable-AD64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad67 Hangul Syllable-AD67 | ad66 Hangul Syllable-AD66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad69 Hangul Syllable-AD69 | ad68 Hangul Syllable-AD68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad6b Hangul Syllable-AD6B | ad6a Hangul Syllable-AD6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad6d Hangul Syllable-AD6D | ad6c Hangul Syllable-AD6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad6f Hangul Syllable-AD6F | ad6e Hangul Syllable-AD6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad71 Hangul Syllable-AD71 | ad70 Hangul Syllable-AD70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad73 Hangul Syllable-AD73 | ad72 Hangul Syllable-AD72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad75 Hangul Syllable-AD75 | ad74 Hangul Syllable-AD74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad77 Hangul Syllable-AD77 | ad76 Hangul Syllable-AD76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad79 Hangul Syllable-AD79 | ad78 Hangul Syllable-AD78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad7b Hangul Syllable-AD7B | ad7a Hangul Syllable-AD7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad7d Hangul Syllable-AD7D | ad7c Hangul Syllable-AD7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad7f Hangul Syllable-AD7F | ad7e Hangul Syllable-AD7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad81 Hangul Syllable-AD81 | ad80 Hangul Syllable-AD80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad83 Hangul Syllable-AD83 | ad82 Hangul Syllable-AD82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad85 Hangul Syllable-AD85 | ad84 Hangul Syllable-AD84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad87 Hangul Syllable-AD87 | ad86 Hangul Syllable-AD86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad89 Hangul Syllable-AD89 | ad88 Hangul Syllable-AD88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad8b Hangul Syllable-AD8B | ad8a Hangul Syllable-AD8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad8d Hangul Syllable-AD8D | ad8c Hangul Syllable-AD8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad8f Hangul Syllable-AD8F | ad8e Hangul Syllable-AD8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad91 Hangul Syllable-AD91 | ad90 Hangul Syllable-AD90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad93 Hangul Syllable-AD93 | ad92 Hangul Syllable-AD92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad95 Hangul Syllable-AD95 | ad94 Hangul Syllable-AD94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad97 Hangul Syllable-AD97 | ad96 Hangul Syllable-AD96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad99 Hangul Syllable-AD99 | ad98 Hangul Syllable-AD98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad9b Hangul Syllable-AD9B | ad9a Hangul Syllable-AD9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad9d Hangul Syllable-AD9D | ad9c Hangul Syllable-AD9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad9f Hangul Syllable-AD9F | ad9e Hangul Syllable-AD9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ada1 Hangul Syllable-ADA1 | ada0 Hangul Syllable-ADA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ada3 Hangul Syllable-ADA3 | ada2 Hangul Syllable-ADA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ada5 Hangul Syllable-ADA5 | ada4 Hangul Syllable-ADA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ada7 Hangul Syllable-ADA7 | ada6 Hangul Syllable-ADA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ada9 Hangul Syllable-ADA9 | ada8 Hangul Syllable-ADA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adab Hangul Syllable-ADAB | adaa Hangul Syllable-ADAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adad Hangul Syllable-ADAD | adac Hangul Syllable-ADAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adaf Hangul Syllable-ADAF | adae Hangul Syllable-ADAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adb1 Hangul Syllable-ADB1 | adb0 Hangul Syllable-ADB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adb3 Hangul Syllable-ADB3 | adb2 Hangul Syllable-ADB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adb5 Hangul Syllable-ADB5 | adb4 Hangul Syllable-ADB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adb7 Hangul Syllable-ADB7 | adb6 Hangul Syllable-ADB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adb9 Hangul Syllable-ADB9 | adb8 Hangul Syllable-ADB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adbb Hangul Syllable-ADBB | adba Hangul Syllable-ADBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adbd Hangul Syllable-ADBD | adbc Hangul Syllable-ADBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adbf Hangul Syllable-ADBF | adbe Hangul Syllable-ADBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adc1 Hangul Syllable-ADC1 | adc0 Hangul Syllable-ADC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adc3 Hangul Syllable-ADC3 | adc2 Hangul Syllable-ADC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adc5 Hangul Syllable-ADC5 | adc4 Hangul Syllable-ADC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adc7 Hangul Syllable-ADC7 | adc6 Hangul Syllable-ADC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adc9 Hangul Syllable-ADC9 | adc8 Hangul Syllable-ADC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adcb Hangul Syllable-ADCB | adca Hangul Syllable-ADCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adcd Hangul Syllable-ADCD | adcc Hangul Syllable-ADCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adcf Hangul Syllable-ADCF | adce Hangul Syllable-ADCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* add1 Hangul Syllable-ADD1 | add0 Hangul Syllable-ADD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* add3 Hangul Syllable-ADD3 | add2 Hangul Syllable-ADD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* add5 Hangul Syllable-ADD5 | add4 Hangul Syllable-ADD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* add7 Hangul Syllable-ADD7 | add6 Hangul Syllable-ADD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* add9 Hangul Syllable-ADD9 | add8 Hangul Syllable-ADD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* addb Hangul Syllable-ADDB | adda Hangul Syllable-ADDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* addd Hangul Syllable-ADDD | addc Hangul Syllable-ADDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* addf Hangul Syllable-ADDF | adde Hangul Syllable-ADDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ade1 Hangul Syllable-ADE1 | ade0 Hangul Syllable-ADE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ade3 Hangul Syllable-ADE3 | ade2 Hangul Syllable-ADE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ade5 Hangul Syllable-ADE5 | ade4 Hangul Syllable-ADE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ade7 Hangul Syllable-ADE7 | ade6 Hangul Syllable-ADE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ade9 Hangul Syllable-ADE9 | ade8 Hangul Syllable-ADE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adeb Hangul Syllable-ADEB | adea Hangul Syllable-ADEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aded Hangul Syllable-ADED | adec Hangul Syllable-ADEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adef Hangul Syllable-ADEF | adee Hangul Syllable-ADEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adf1 Hangul Syllable-ADF1 | adf0 Hangul Syllable-ADF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adf3 Hangul Syllable-ADF3 | adf2 Hangul Syllable-ADF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adf5 Hangul Syllable-ADF5 | adf4 Hangul Syllable-ADF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adf7 Hangul Syllable-ADF7 | adf6 Hangul Syllable-ADF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adf9 Hangul Syllable-ADF9 | adf8 Hangul Syllable-ADF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adfb Hangul Syllable-ADFB | adfa Hangul Syllable-ADFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adfd Hangul Syllable-ADFD | adfc Hangul Syllable-ADFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adff Hangul Syllable-ADFF | adfe Hangul Syllable-ADFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae01 Hangul Syllable-AE01 | ae00 Hangul Syllable-AE00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae03 Hangul Syllable-AE03 | ae02 Hangul Syllable-AE02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae05 Hangul Syllable-AE05 | ae04 Hangul Syllable-AE04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae07 Hangul Syllable-AE07 | ae06 Hangul Syllable-AE06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae09 Hangul Syllable-AE09 | ae08 Hangul Syllable-AE08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae0b Hangul Syllable-AE0B | ae0a Hangul Syllable-AE0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae0d Hangul Syllable-AE0D | ae0c Hangul Syllable-AE0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae0f Hangul Syllable-AE0F | ae0e Hangul Syllable-AE0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae11 Hangul Syllable-AE11 | ae10 Hangul Syllable-AE10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae13 Hangul Syllable-AE13 | ae12 Hangul Syllable-AE12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae15 Hangul Syllable-AE15 | ae14 Hangul Syllable-AE14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae17 Hangul Syllable-AE17 | ae16 Hangul Syllable-AE16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae19 Hangul Syllable-AE19 | ae18 Hangul Syllable-AE18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae1b Hangul Syllable-AE1B | ae1a Hangul Syllable-AE1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae1d Hangul Syllable-AE1D | ae1c Hangul Syllable-AE1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae1f Hangul Syllable-AE1F | ae1e Hangul Syllable-AE1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae21 Hangul Syllable-AE21 | ae20 Hangul Syllable-AE20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae23 Hangul Syllable-AE23 | ae22 Hangul Syllable-AE22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae25 Hangul Syllable-AE25 | ae24 Hangul Syllable-AE24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae27 Hangul Syllable-AE27 | ae26 Hangul Syllable-AE26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae29 Hangul Syllable-AE29 | ae28 Hangul Syllable-AE28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae2b Hangul Syllable-AE2B | ae2a Hangul Syllable-AE2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae2d Hangul Syllable-AE2D | ae2c Hangul Syllable-AE2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae2f Hangul Syllable-AE2F | ae2e Hangul Syllable-AE2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae31 Hangul Syllable-AE31 | ae30 Hangul Syllable-AE30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae33 Hangul Syllable-AE33 | ae32 Hangul Syllable-AE32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae35 Hangul Syllable-AE35 | ae34 Hangul Syllable-AE34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae37 Hangul Syllable-AE37 | ae36 Hangul Syllable-AE36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae39 Hangul Syllable-AE39 | ae38 Hangul Syllable-AE38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae3b Hangul Syllable-AE3B | ae3a Hangul Syllable-AE3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae3d Hangul Syllable-AE3D | ae3c Hangul Syllable-AE3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae3f Hangul Syllable-AE3F | ae3e Hangul Syllable-AE3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae41 Hangul Syllable-AE41 | ae40 Hangul Syllable-AE40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae43 Hangul Syllable-AE43 | ae42 Hangul Syllable-AE42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae45 Hangul Syllable-AE45 | ae44 Hangul Syllable-AE44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae47 Hangul Syllable-AE47 | ae46 Hangul Syllable-AE46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae49 Hangul Syllable-AE49 | ae48 Hangul Syllable-AE48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae4b Hangul Syllable-AE4B | ae4a Hangul Syllable-AE4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae4d Hangul Syllable-AE4D | ae4c Hangul Syllable-AE4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae4f Hangul Syllable-AE4F | ae4e Hangul Syllable-AE4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae51 Hangul Syllable-AE51 | ae50 Hangul Syllable-AE50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae53 Hangul Syllable-AE53 | ae52 Hangul Syllable-AE52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae55 Hangul Syllable-AE55 | ae54 Hangul Syllable-AE54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae57 Hangul Syllable-AE57 | ae56 Hangul Syllable-AE56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae59 Hangul Syllable-AE59 | ae58 Hangul Syllable-AE58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae5b Hangul Syllable-AE5B | ae5a Hangul Syllable-AE5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae5d Hangul Syllable-AE5D | ae5c Hangul Syllable-AE5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae5f Hangul Syllable-AE5F | ae5e Hangul Syllable-AE5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae61 Hangul Syllable-AE61 | ae60 Hangul Syllable-AE60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae63 Hangul Syllable-AE63 | ae62 Hangul Syllable-AE62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae65 Hangul Syllable-AE65 | ae64 Hangul Syllable-AE64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae67 Hangul Syllable-AE67 | ae66 Hangul Syllable-AE66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae69 Hangul Syllable-AE69 | ae68 Hangul Syllable-AE68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae6b Hangul Syllable-AE6B | ae6a Hangul Syllable-AE6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae6d Hangul Syllable-AE6D | ae6c Hangul Syllable-AE6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae6f Hangul Syllable-AE6F | ae6e Hangul Syllable-AE6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae71 Hangul Syllable-AE71 | ae70 Hangul Syllable-AE70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae73 Hangul Syllable-AE73 | ae72 Hangul Syllable-AE72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae75 Hangul Syllable-AE75 | ae74 Hangul Syllable-AE74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae77 Hangul Syllable-AE77 | ae76 Hangul Syllable-AE76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae79 Hangul Syllable-AE79 | ae78 Hangul Syllable-AE78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae7b Hangul Syllable-AE7B | ae7a Hangul Syllable-AE7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae7d Hangul Syllable-AE7D | ae7c Hangul Syllable-AE7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae7f Hangul Syllable-AE7F | ae7e Hangul Syllable-AE7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae81 Hangul Syllable-AE81 | ae80 Hangul Syllable-AE80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae83 Hangul Syllable-AE83 | ae82 Hangul Syllable-AE82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae85 Hangul Syllable-AE85 | ae84 Hangul Syllable-AE84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae87 Hangul Syllable-AE87 | ae86 Hangul Syllable-AE86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae89 Hangul Syllable-AE89 | ae88 Hangul Syllable-AE88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae8b Hangul Syllable-AE8B | ae8a Hangul Syllable-AE8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae8d Hangul Syllable-AE8D | ae8c Hangul Syllable-AE8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae8f Hangul Syllable-AE8F | ae8e Hangul Syllable-AE8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae91 Hangul Syllable-AE91 | ae90 Hangul Syllable-AE90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae93 Hangul Syllable-AE93 | ae92 Hangul Syllable-AE92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae95 Hangul Syllable-AE95 | ae94 Hangul Syllable-AE94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae97 Hangul Syllable-AE97 | ae96 Hangul Syllable-AE96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae99 Hangul Syllable-AE99 | ae98 Hangul Syllable-AE98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae9b Hangul Syllable-AE9B | ae9a Hangul Syllable-AE9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae9d Hangul Syllable-AE9D | ae9c Hangul Syllable-AE9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae9f Hangul Syllable-AE9F | ae9e Hangul Syllable-AE9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aea1 Hangul Syllable-AEA1 | aea0 Hangul Syllable-AEA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aea3 Hangul Syllable-AEA3 | aea2 Hangul Syllable-AEA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aea5 Hangul Syllable-AEA5 | aea4 Hangul Syllable-AEA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aea7 Hangul Syllable-AEA7 | aea6 Hangul Syllable-AEA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aea9 Hangul Syllable-AEA9 | aea8 Hangul Syllable-AEA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeab Hangul Syllable-AEAB | aeaa Hangul Syllable-AEAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aead Hangul Syllable-AEAD | aeac Hangul Syllable-AEAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeaf Hangul Syllable-AEAF | aeae Hangul Syllable-AEAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeb1 Hangul Syllable-AEB1 | aeb0 Hangul Syllable-AEB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeb3 Hangul Syllable-AEB3 | aeb2 Hangul Syllable-AEB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeb5 Hangul Syllable-AEB5 | aeb4 Hangul Syllable-AEB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeb7 Hangul Syllable-AEB7 | aeb6 Hangul Syllable-AEB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeb9 Hangul Syllable-AEB9 | aeb8 Hangul Syllable-AEB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aebb Hangul Syllable-AEBB | aeba Hangul Syllable-AEBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aebd Hangul Syllable-AEBD | aebc Hangul Syllable-AEBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aebf Hangul Syllable-AEBF | aebe Hangul Syllable-AEBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aec1 Hangul Syllable-AEC1 | aec0 Hangul Syllable-AEC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aec3 Hangul Syllable-AEC3 | aec2 Hangul Syllable-AEC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aec5 Hangul Syllable-AEC5 | aec4 Hangul Syllable-AEC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aec7 Hangul Syllable-AEC7 | aec6 Hangul Syllable-AEC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aec9 Hangul Syllable-AEC9 | aec8 Hangul Syllable-AEC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aecb Hangul Syllable-AECB | aeca Hangul Syllable-AECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aecd Hangul Syllable-AECD | aecc Hangul Syllable-AECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aecf Hangul Syllable-AECF | aece Hangul Syllable-AECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aed1 Hangul Syllable-AED1 | aed0 Hangul Syllable-AED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aed3 Hangul Syllable-AED3 | aed2 Hangul Syllable-AED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aed5 Hangul Syllable-AED5 | aed4 Hangul Syllable-AED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aed7 Hangul Syllable-AED7 | aed6 Hangul Syllable-AED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aed9 Hangul Syllable-AED9 | aed8 Hangul Syllable-AED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aedb Hangul Syllable-AEDB | aeda Hangul Syllable-AEDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aedd Hangul Syllable-AEDD | aedc Hangul Syllable-AEDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aedf Hangul Syllable-AEDF | aede Hangul Syllable-AEDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aee1 Hangul Syllable-AEE1 | aee0 Hangul Syllable-AEE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aee3 Hangul Syllable-AEE3 | aee2 Hangul Syllable-AEE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aee5 Hangul Syllable-AEE5 | aee4 Hangul Syllable-AEE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aee7 Hangul Syllable-AEE7 | aee6 Hangul Syllable-AEE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aee9 Hangul Syllable-AEE9 | aee8 Hangul Syllable-AEE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeeb Hangul Syllable-AEEB | aeea Hangul Syllable-AEEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeed Hangul Syllable-AEED | aeec Hangul Syllable-AEEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeef Hangul Syllable-AEEF | aeee Hangul Syllable-AEEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aef1 Hangul Syllable-AEF1 | aef0 Hangul Syllable-AEF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aef3 Hangul Syllable-AEF3 | aef2 Hangul Syllable-AEF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aef5 Hangul Syllable-AEF5 | aef4 Hangul Syllable-AEF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aef7 Hangul Syllable-AEF7 | aef6 Hangul Syllable-AEF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aef9 Hangul Syllable-AEF9 | aef8 Hangul Syllable-AEF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aefb Hangul Syllable-AEFB | aefa Hangul Syllable-AEFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aefd Hangul Syllable-AEFD | aefc Hangul Syllable-AEFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeff Hangul Syllable-AEFF | aefe Hangul Syllable-AEFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af01 Hangul Syllable-AF01 | af00 Hangul Syllable-AF00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af03 Hangul Syllable-AF03 | af02 Hangul Syllable-AF02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af05 Hangul Syllable-AF05 | af04 Hangul Syllable-AF04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af07 Hangul Syllable-AF07 | af06 Hangul Syllable-AF06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af09 Hangul Syllable-AF09 | af08 Hangul Syllable-AF08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af0b Hangul Syllable-AF0B | af0a Hangul Syllable-AF0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af0d Hangul Syllable-AF0D | af0c Hangul Syllable-AF0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af0f Hangul Syllable-AF0F | af0e Hangul Syllable-AF0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af11 Hangul Syllable-AF11 | af10 Hangul Syllable-AF10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af13 Hangul Syllable-AF13 | af12 Hangul Syllable-AF12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af15 Hangul Syllable-AF15 | af14 Hangul Syllable-AF14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af17 Hangul Syllable-AF17 | af16 Hangul Syllable-AF16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af19 Hangul Syllable-AF19 | af18 Hangul Syllable-AF18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af1b Hangul Syllable-AF1B | af1a Hangul Syllable-AF1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af1d Hangul Syllable-AF1D | af1c Hangul Syllable-AF1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af1f Hangul Syllable-AF1F | af1e Hangul Syllable-AF1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af21 Hangul Syllable-AF21 | af20 Hangul Syllable-AF20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af23 Hangul Syllable-AF23 | af22 Hangul Syllable-AF22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af25 Hangul Syllable-AF25 | af24 Hangul Syllable-AF24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af27 Hangul Syllable-AF27 | af26 Hangul Syllable-AF26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af29 Hangul Syllable-AF29 | af28 Hangul Syllable-AF28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af2b Hangul Syllable-AF2B | af2a Hangul Syllable-AF2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af2d Hangul Syllable-AF2D | af2c Hangul Syllable-AF2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af2f Hangul Syllable-AF2F | af2e Hangul Syllable-AF2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af31 Hangul Syllable-AF31 | af30 Hangul Syllable-AF30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af33 Hangul Syllable-AF33 | af32 Hangul Syllable-AF32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af35 Hangul Syllable-AF35 | af34 Hangul Syllable-AF34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af37 Hangul Syllable-AF37 | af36 Hangul Syllable-AF36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af39 Hangul Syllable-AF39 | af38 Hangul Syllable-AF38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af3b Hangul Syllable-AF3B | af3a Hangul Syllable-AF3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af3d Hangul Syllable-AF3D | af3c Hangul Syllable-AF3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af3f Hangul Syllable-AF3F | af3e Hangul Syllable-AF3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af41 Hangul Syllable-AF41 | af40 Hangul Syllable-AF40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af43 Hangul Syllable-AF43 | af42 Hangul Syllable-AF42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af45 Hangul Syllable-AF45 | af44 Hangul Syllable-AF44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af47 Hangul Syllable-AF47 | af46 Hangul Syllable-AF46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af49 Hangul Syllable-AF49 | af48 Hangul Syllable-AF48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af4b Hangul Syllable-AF4B | af4a Hangul Syllable-AF4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af4d Hangul Syllable-AF4D | af4c Hangul Syllable-AF4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af4f Hangul Syllable-AF4F | af4e Hangul Syllable-AF4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af51 Hangul Syllable-AF51 | af50 Hangul Syllable-AF50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af53 Hangul Syllable-AF53 | af52 Hangul Syllable-AF52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af55 Hangul Syllable-AF55 | af54 Hangul Syllable-AF54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af57 Hangul Syllable-AF57 | af56 Hangul Syllable-AF56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af59 Hangul Syllable-AF59 | af58 Hangul Syllable-AF58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af5b Hangul Syllable-AF5B | af5a Hangul Syllable-AF5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af5d Hangul Syllable-AF5D | af5c Hangul Syllable-AF5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af5f Hangul Syllable-AF5F | af5e Hangul Syllable-AF5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af61 Hangul Syllable-AF61 | af60 Hangul Syllable-AF60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af63 Hangul Syllable-AF63 | af62 Hangul Syllable-AF62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af65 Hangul Syllable-AF65 | af64 Hangul Syllable-AF64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af67 Hangul Syllable-AF67 | af66 Hangul Syllable-AF66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af69 Hangul Syllable-AF69 | af68 Hangul Syllable-AF68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af6b Hangul Syllable-AF6B | af6a Hangul Syllable-AF6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af6d Hangul Syllable-AF6D | af6c Hangul Syllable-AF6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af6f Hangul Syllable-AF6F | af6e Hangul Syllable-AF6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af71 Hangul Syllable-AF71 | af70 Hangul Syllable-AF70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af73 Hangul Syllable-AF73 | af72 Hangul Syllable-AF72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af75 Hangul Syllable-AF75 | af74 Hangul Syllable-AF74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af77 Hangul Syllable-AF77 | af76 Hangul Syllable-AF76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af79 Hangul Syllable-AF79 | af78 Hangul Syllable-AF78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af7b Hangul Syllable-AF7B | af7a Hangul Syllable-AF7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af7d Hangul Syllable-AF7D | af7c Hangul Syllable-AF7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af7f Hangul Syllable-AF7F | af7e Hangul Syllable-AF7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af81 Hangul Syllable-AF81 | af80 Hangul Syllable-AF80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af83 Hangul Syllable-AF83 | af82 Hangul Syllable-AF82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af85 Hangul Syllable-AF85 | af84 Hangul Syllable-AF84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af87 Hangul Syllable-AF87 | af86 Hangul Syllable-AF86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af89 Hangul Syllable-AF89 | af88 Hangul Syllable-AF88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af8b Hangul Syllable-AF8B | af8a Hangul Syllable-AF8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af8d Hangul Syllable-AF8D | af8c Hangul Syllable-AF8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af8f Hangul Syllable-AF8F | af8e Hangul Syllable-AF8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af91 Hangul Syllable-AF91 | af90 Hangul Syllable-AF90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af93 Hangul Syllable-AF93 | af92 Hangul Syllable-AF92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af95 Hangul Syllable-AF95 | af94 Hangul Syllable-AF94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af97 Hangul Syllable-AF97 | af96 Hangul Syllable-AF96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af99 Hangul Syllable-AF99 | af98 Hangul Syllable-AF98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af9b Hangul Syllable-AF9B | af9a Hangul Syllable-AF9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af9d Hangul Syllable-AF9D | af9c Hangul Syllable-AF9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af9f Hangul Syllable-AF9F | af9e Hangul Syllable-AF9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afa1 Hangul Syllable-AFA1 | afa0 Hangul Syllable-AFA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afa3 Hangul Syllable-AFA3 | afa2 Hangul Syllable-AFA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afa5 Hangul Syllable-AFA5 | afa4 Hangul Syllable-AFA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afa7 Hangul Syllable-AFA7 | afa6 Hangul Syllable-AFA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afa9 Hangul Syllable-AFA9 | afa8 Hangul Syllable-AFA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afab Hangul Syllable-AFAB | afaa Hangul Syllable-AFAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afad Hangul Syllable-AFAD | afac Hangul Syllable-AFAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afaf Hangul Syllable-AFAF | afae Hangul Syllable-AFAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afb1 Hangul Syllable-AFB1 | afb0 Hangul Syllable-AFB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afb3 Hangul Syllable-AFB3 | afb2 Hangul Syllable-AFB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afb5 Hangul Syllable-AFB5 | afb4 Hangul Syllable-AFB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afb7 Hangul Syllable-AFB7 | afb6 Hangul Syllable-AFB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afb9 Hangul Syllable-AFB9 | afb8 Hangul Syllable-AFB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afbb Hangul Syllable-AFBB | afba Hangul Syllable-AFBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afbd Hangul Syllable-AFBD | afbc Hangul Syllable-AFBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afbf Hangul Syllable-AFBF | afbe Hangul Syllable-AFBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afc1 Hangul Syllable-AFC1 | afc0 Hangul Syllable-AFC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afc3 Hangul Syllable-AFC3 | afc2 Hangul Syllable-AFC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afc5 Hangul Syllable-AFC5 | afc4 Hangul Syllable-AFC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afc7 Hangul Syllable-AFC7 | afc6 Hangul Syllable-AFC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afc9 Hangul Syllable-AFC9 | afc8 Hangul Syllable-AFC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afcb Hangul Syllable-AFCB | afca Hangul Syllable-AFCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afcd Hangul Syllable-AFCD | afcc Hangul Syllable-AFCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afcf Hangul Syllable-AFCF | afce Hangul Syllable-AFCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afd1 Hangul Syllable-AFD1 | afd0 Hangul Syllable-AFD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afd3 Hangul Syllable-AFD3 | afd2 Hangul Syllable-AFD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afd5 Hangul Syllable-AFD5 | afd4 Hangul Syllable-AFD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afd7 Hangul Syllable-AFD7 | afd6 Hangul Syllable-AFD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afd9 Hangul Syllable-AFD9 | afd8 Hangul Syllable-AFD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afdb Hangul Syllable-AFDB | afda Hangul Syllable-AFDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afdd Hangul Syllable-AFDD | afdc Hangul Syllable-AFDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afdf Hangul Syllable-AFDF | afde Hangul Syllable-AFDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afe1 Hangul Syllable-AFE1 | afe0 Hangul Syllable-AFE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afe3 Hangul Syllable-AFE3 | afe2 Hangul Syllable-AFE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afe5 Hangul Syllable-AFE5 | afe4 Hangul Syllable-AFE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afe7 Hangul Syllable-AFE7 | afe6 Hangul Syllable-AFE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afe9 Hangul Syllable-AFE9 | afe8 Hangul Syllable-AFE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afeb Hangul Syllable-AFEB | afea Hangul Syllable-AFEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afed Hangul Syllable-AFED | afec Hangul Syllable-AFEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afef Hangul Syllable-AFEF | afee Hangul Syllable-AFEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aff1 Hangul Syllable-AFF1 | aff0 Hangul Syllable-AFF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aff3 Hangul Syllable-AFF3 | aff2 Hangul Syllable-AFF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aff5 Hangul Syllable-AFF5 | aff4 Hangul Syllable-AFF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aff7 Hangul Syllable-AFF7 | aff6 Hangul Syllable-AFF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aff9 Hangul Syllable-AFF9 | aff8 Hangul Syllable-AFF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* affb Hangul Syllable-AFFB | affa Hangul Syllable-AFFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* affd Hangul Syllable-AFFD | affc Hangul Syllable-AFFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afff Hangul Syllable-AFFF | affe Hangul Syllable-AFFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b001 Hangul Syllable-B001 | b000 Hangul Syllable-B000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b003 Hangul Syllable-B003 | b002 Hangul Syllable-B002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b005 Hangul Syllable-B005 | b004 Hangul Syllable-B004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b007 Hangul Syllable-B007 | b006 Hangul Syllable-B006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b009 Hangul Syllable-B009 | b008 Hangul Syllable-B008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b00b Hangul Syllable-B00B | b00a Hangul Syllable-B00A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b00d Hangul Syllable-B00D | b00c Hangul Syllable-B00C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b00f Hangul Syllable-B00F | b00e Hangul Syllable-B00E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b011 Hangul Syllable-B011 | b010 Hangul Syllable-B010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b013 Hangul Syllable-B013 | b012 Hangul Syllable-B012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b015 Hangul Syllable-B015 | b014 Hangul Syllable-B014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b017 Hangul Syllable-B017 | b016 Hangul Syllable-B016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b019 Hangul Syllable-B019 | b018 Hangul Syllable-B018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b01b Hangul Syllable-B01B | b01a Hangul Syllable-B01A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b01d Hangul Syllable-B01D | b01c Hangul Syllable-B01C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b01f Hangul Syllable-B01F | b01e Hangul Syllable-B01E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b021 Hangul Syllable-B021 | b020 Hangul Syllable-B020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b023 Hangul Syllable-B023 | b022 Hangul Syllable-B022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b025 Hangul Syllable-B025 | b024 Hangul Syllable-B024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b027 Hangul Syllable-B027 | b026 Hangul Syllable-B026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b029 Hangul Syllable-B029 | b028 Hangul Syllable-B028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b02b Hangul Syllable-B02B | b02a Hangul Syllable-B02A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b02d Hangul Syllable-B02D | b02c Hangul Syllable-B02C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b02f Hangul Syllable-B02F | b02e Hangul Syllable-B02E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b031 Hangul Syllable-B031 | b030 Hangul Syllable-B030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b033 Hangul Syllable-B033 | b032 Hangul Syllable-B032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b035 Hangul Syllable-B035 | b034 Hangul Syllable-B034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b037 Hangul Syllable-B037 | b036 Hangul Syllable-B036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b039 Hangul Syllable-B039 | b038 Hangul Syllable-B038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b03b Hangul Syllable-B03B | b03a Hangul Syllable-B03A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b03d Hangul Syllable-B03D | b03c Hangul Syllable-B03C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b03f Hangul Syllable-B03F | b03e Hangul Syllable-B03E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b041 Hangul Syllable-B041 | b040 Hangul Syllable-B040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b043 Hangul Syllable-B043 | b042 Hangul Syllable-B042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b045 Hangul Syllable-B045 | b044 Hangul Syllable-B044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b047 Hangul Syllable-B047 | b046 Hangul Syllable-B046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b049 Hangul Syllable-B049 | b048 Hangul Syllable-B048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b04b Hangul Syllable-B04B | b04a Hangul Syllable-B04A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b04d Hangul Syllable-B04D | b04c Hangul Syllable-B04C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b04f Hangul Syllable-B04F | b04e Hangul Syllable-B04E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b051 Hangul Syllable-B051 | b050 Hangul Syllable-B050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b053 Hangul Syllable-B053 | b052 Hangul Syllable-B052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b055 Hangul Syllable-B055 | b054 Hangul Syllable-B054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b057 Hangul Syllable-B057 | b056 Hangul Syllable-B056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b059 Hangul Syllable-B059 | b058 Hangul Syllable-B058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b05b Hangul Syllable-B05B | b05a Hangul Syllable-B05A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b05d Hangul Syllable-B05D | b05c Hangul Syllable-B05C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b05f Hangul Syllable-B05F | b05e Hangul Syllable-B05E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b061 Hangul Syllable-B061 | b060 Hangul Syllable-B060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b063 Hangul Syllable-B063 | b062 Hangul Syllable-B062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b065 Hangul Syllable-B065 | b064 Hangul Syllable-B064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b067 Hangul Syllable-B067 | b066 Hangul Syllable-B066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b069 Hangul Syllable-B069 | b068 Hangul Syllable-B068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b06b Hangul Syllable-B06B | b06a Hangul Syllable-B06A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b06d Hangul Syllable-B06D | b06c Hangul Syllable-B06C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b06f Hangul Syllable-B06F | b06e Hangul Syllable-B06E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b071 Hangul Syllable-B071 | b070 Hangul Syllable-B070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b073 Hangul Syllable-B073 | b072 Hangul Syllable-B072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b075 Hangul Syllable-B075 | b074 Hangul Syllable-B074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b077 Hangul Syllable-B077 | b076 Hangul Syllable-B076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b079 Hangul Syllable-B079 | b078 Hangul Syllable-B078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b07b Hangul Syllable-B07B | b07a Hangul Syllable-B07A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b07d Hangul Syllable-B07D | b07c Hangul Syllable-B07C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b07f Hangul Syllable-B07F | b07e Hangul Syllable-B07E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b081 Hangul Syllable-B081 | b080 Hangul Syllable-B080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b083 Hangul Syllable-B083 | b082 Hangul Syllable-B082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b085 Hangul Syllable-B085 | b084 Hangul Syllable-B084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b087 Hangul Syllable-B087 | b086 Hangul Syllable-B086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b089 Hangul Syllable-B089 | b088 Hangul Syllable-B088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b08b Hangul Syllable-B08B | b08a Hangul Syllable-B08A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b08d Hangul Syllable-B08D | b08c Hangul Syllable-B08C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b08f Hangul Syllable-B08F | b08e Hangul Syllable-B08E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b091 Hangul Syllable-B091 | b090 Hangul Syllable-B090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b093 Hangul Syllable-B093 | b092 Hangul Syllable-B092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b095 Hangul Syllable-B095 | b094 Hangul Syllable-B094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b097 Hangul Syllable-B097 | b096 Hangul Syllable-B096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b099 Hangul Syllable-B099 | b098 Hangul Syllable-B098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b09b Hangul Syllable-B09B | b09a Hangul Syllable-B09A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b09d Hangul Syllable-B09D | b09c Hangul Syllable-B09C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b09f Hangul Syllable-B09F | b09e Hangul Syllable-B09E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0a1 Hangul Syllable-B0A1 | b0a0 Hangul Syllable-B0A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0a3 Hangul Syllable-B0A3 | b0a2 Hangul Syllable-B0A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0a5 Hangul Syllable-B0A5 | b0a4 Hangul Syllable-B0A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0a7 Hangul Syllable-B0A7 | b0a6 Hangul Syllable-B0A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0a9 Hangul Syllable-B0A9 | b0a8 Hangul Syllable-B0A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0ab Hangul Syllable-B0AB | b0aa Hangul Syllable-B0AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0ad Hangul Syllable-B0AD | b0ac Hangul Syllable-B0AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0af Hangul Syllable-B0AF | b0ae Hangul Syllable-B0AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0b1 Hangul Syllable-B0B1 | b0b0 Hangul Syllable-B0B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0b3 Hangul Syllable-B0B3 | b0b2 Hangul Syllable-B0B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0b5 Hangul Syllable-B0B5 | b0b4 Hangul Syllable-B0B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0b7 Hangul Syllable-B0B7 | b0b6 Hangul Syllable-B0B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0b9 Hangul Syllable-B0B9 | b0b8 Hangul Syllable-B0B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0bb Hangul Syllable-B0BB | b0ba Hangul Syllable-B0BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0bd Hangul Syllable-B0BD | b0bc Hangul Syllable-B0BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0bf Hangul Syllable-B0BF | b0be Hangul Syllable-B0BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0c1 Hangul Syllable-B0C1 | b0c0 Hangul Syllable-B0C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0c3 Hangul Syllable-B0C3 | b0c2 Hangul Syllable-B0C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0c5 Hangul Syllable-B0C5 | b0c4 Hangul Syllable-B0C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0c7 Hangul Syllable-B0C7 | b0c6 Hangul Syllable-B0C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0c9 Hangul Syllable-B0C9 | b0c8 Hangul Syllable-B0C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0cb Hangul Syllable-B0CB | b0ca Hangul Syllable-B0CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0cd Hangul Syllable-B0CD | b0cc Hangul Syllable-B0CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0cf Hangul Syllable-B0CF | b0ce Hangul Syllable-B0CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0d1 Hangul Syllable-B0D1 | b0d0 Hangul Syllable-B0D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0d3 Hangul Syllable-B0D3 | b0d2 Hangul Syllable-B0D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0d5 Hangul Syllable-B0D5 | b0d4 Hangul Syllable-B0D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0d7 Hangul Syllable-B0D7 | b0d6 Hangul Syllable-B0D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0d9 Hangul Syllable-B0D9 | b0d8 Hangul Syllable-B0D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0db Hangul Syllable-B0DB | b0da Hangul Syllable-B0DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0dd Hangul Syllable-B0DD | b0dc Hangul Syllable-B0DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0df Hangul Syllable-B0DF | b0de Hangul Syllable-B0DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0e1 Hangul Syllable-B0E1 | b0e0 Hangul Syllable-B0E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0e3 Hangul Syllable-B0E3 | b0e2 Hangul Syllable-B0E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0e5 Hangul Syllable-B0E5 | b0e4 Hangul Syllable-B0E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0e7 Hangul Syllable-B0E7 | b0e6 Hangul Syllable-B0E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0e9 Hangul Syllable-B0E9 | b0e8 Hangul Syllable-B0E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0eb Hangul Syllable-B0EB | b0ea Hangul Syllable-B0EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0ed Hangul Syllable-B0ED | b0ec Hangul Syllable-B0EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0ef Hangul Syllable-B0EF | b0ee Hangul Syllable-B0EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0f1 Hangul Syllable-B0F1 | b0f0 Hangul Syllable-B0F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0f3 Hangul Syllable-B0F3 | b0f2 Hangul Syllable-B0F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0f5 Hangul Syllable-B0F5 | b0f4 Hangul Syllable-B0F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0f7 Hangul Syllable-B0F7 | b0f6 Hangul Syllable-B0F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0f9 Hangul Syllable-B0F9 | b0f8 Hangul Syllable-B0F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0fb Hangul Syllable-B0FB | b0fa Hangul Syllable-B0FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0fd Hangul Syllable-B0FD | b0fc Hangul Syllable-B0FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0ff Hangul Syllable-B0FF | b0fe Hangul Syllable-B0FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b101 Hangul Syllable-B101 | b100 Hangul Syllable-B100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b103 Hangul Syllable-B103 | b102 Hangul Syllable-B102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b105 Hangul Syllable-B105 | b104 Hangul Syllable-B104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b107 Hangul Syllable-B107 | b106 Hangul Syllable-B106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b109 Hangul Syllable-B109 | b108 Hangul Syllable-B108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b10b Hangul Syllable-B10B | b10a Hangul Syllable-B10A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b10d Hangul Syllable-B10D | b10c Hangul Syllable-B10C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b10f Hangul Syllable-B10F | b10e Hangul Syllable-B10E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b111 Hangul Syllable-B111 | b110 Hangul Syllable-B110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b113 Hangul Syllable-B113 | b112 Hangul Syllable-B112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b115 Hangul Syllable-B115 | b114 Hangul Syllable-B114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b117 Hangul Syllable-B117 | b116 Hangul Syllable-B116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b119 Hangul Syllable-B119 | b118 Hangul Syllable-B118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b11b Hangul Syllable-B11B | b11a Hangul Syllable-B11A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b11d Hangul Syllable-B11D | b11c Hangul Syllable-B11C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b11f Hangul Syllable-B11F | b11e Hangul Syllable-B11E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b121 Hangul Syllable-B121 | b120 Hangul Syllable-B120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b123 Hangul Syllable-B123 | b122 Hangul Syllable-B122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b125 Hangul Syllable-B125 | b124 Hangul Syllable-B124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b127 Hangul Syllable-B127 | b126 Hangul Syllable-B126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b129 Hangul Syllable-B129 | b128 Hangul Syllable-B128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b12b Hangul Syllable-B12B | b12a Hangul Syllable-B12A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b12d Hangul Syllable-B12D | b12c Hangul Syllable-B12C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b12f Hangul Syllable-B12F | b12e Hangul Syllable-B12E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b131 Hangul Syllable-B131 | b130 Hangul Syllable-B130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b133 Hangul Syllable-B133 | b132 Hangul Syllable-B132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b135 Hangul Syllable-B135 | b134 Hangul Syllable-B134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b137 Hangul Syllable-B137 | b136 Hangul Syllable-B136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b139 Hangul Syllable-B139 | b138 Hangul Syllable-B138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b13b Hangul Syllable-B13B | b13a Hangul Syllable-B13A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b13d Hangul Syllable-B13D | b13c Hangul Syllable-B13C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b13f Hangul Syllable-B13F | b13e Hangul Syllable-B13E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b141 Hangul Syllable-B141 | b140 Hangul Syllable-B140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b143 Hangul Syllable-B143 | b142 Hangul Syllable-B142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b145 Hangul Syllable-B145 | b144 Hangul Syllable-B144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b147 Hangul Syllable-B147 | b146 Hangul Syllable-B146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b149 Hangul Syllable-B149 | b148 Hangul Syllable-B148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b14b Hangul Syllable-B14B | b14a Hangul Syllable-B14A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b14d Hangul Syllable-B14D | b14c Hangul Syllable-B14C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b14f Hangul Syllable-B14F | b14e Hangul Syllable-B14E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b151 Hangul Syllable-B151 | b150 Hangul Syllable-B150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b153 Hangul Syllable-B153 | b152 Hangul Syllable-B152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b155 Hangul Syllable-B155 | b154 Hangul Syllable-B154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b157 Hangul Syllable-B157 | b156 Hangul Syllable-B156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b159 Hangul Syllable-B159 | b158 Hangul Syllable-B158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b15b Hangul Syllable-B15B | b15a Hangul Syllable-B15A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b15d Hangul Syllable-B15D | b15c Hangul Syllable-B15C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b15f Hangul Syllable-B15F | b15e Hangul Syllable-B15E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b161 Hangul Syllable-B161 | b160 Hangul Syllable-B160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b163 Hangul Syllable-B163 | b162 Hangul Syllable-B162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b165 Hangul Syllable-B165 | b164 Hangul Syllable-B164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b167 Hangul Syllable-B167 | b166 Hangul Syllable-B166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b169 Hangul Syllable-B169 | b168 Hangul Syllable-B168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b16b Hangul Syllable-B16B | b16a Hangul Syllable-B16A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b16d Hangul Syllable-B16D | b16c Hangul Syllable-B16C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b16f Hangul Syllable-B16F | b16e Hangul Syllable-B16E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b171 Hangul Syllable-B171 | b170 Hangul Syllable-B170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b173 Hangul Syllable-B173 | b172 Hangul Syllable-B172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b175 Hangul Syllable-B175 | b174 Hangul Syllable-B174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b177 Hangul Syllable-B177 | b176 Hangul Syllable-B176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b179 Hangul Syllable-B179 | b178 Hangul Syllable-B178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b17b Hangul Syllable-B17B | b17a Hangul Syllable-B17A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b17d Hangul Syllable-B17D | b17c Hangul Syllable-B17C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b17f Hangul Syllable-B17F | b17e Hangul Syllable-B17E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b181 Hangul Syllable-B181 | b180 Hangul Syllable-B180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b183 Hangul Syllable-B183 | b182 Hangul Syllable-B182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b185 Hangul Syllable-B185 | b184 Hangul Syllable-B184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b187 Hangul Syllable-B187 | b186 Hangul Syllable-B186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b189 Hangul Syllable-B189 | b188 Hangul Syllable-B188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b18b Hangul Syllable-B18B | b18a Hangul Syllable-B18A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b18d Hangul Syllable-B18D | b18c Hangul Syllable-B18C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b18f Hangul Syllable-B18F | b18e Hangul Syllable-B18E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b191 Hangul Syllable-B191 | b190 Hangul Syllable-B190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b193 Hangul Syllable-B193 | b192 Hangul Syllable-B192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b195 Hangul Syllable-B195 | b194 Hangul Syllable-B194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b197 Hangul Syllable-B197 | b196 Hangul Syllable-B196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b199 Hangul Syllable-B199 | b198 Hangul Syllable-B198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b19b Hangul Syllable-B19B | b19a Hangul Syllable-B19A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b19d Hangul Syllable-B19D | b19c Hangul Syllable-B19C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b19f Hangul Syllable-B19F | b19e Hangul Syllable-B19E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1a1 Hangul Syllable-B1A1 | b1a0 Hangul Syllable-B1A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1a3 Hangul Syllable-B1A3 | b1a2 Hangul Syllable-B1A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1a5 Hangul Syllable-B1A5 | b1a4 Hangul Syllable-B1A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1a7 Hangul Syllable-B1A7 | b1a6 Hangul Syllable-B1A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1a9 Hangul Syllable-B1A9 | b1a8 Hangul Syllable-B1A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1ab Hangul Syllable-B1AB | b1aa Hangul Syllable-B1AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1ad Hangul Syllable-B1AD | b1ac Hangul Syllable-B1AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1af Hangul Syllable-B1AF | b1ae Hangul Syllable-B1AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1b1 Hangul Syllable-B1B1 | b1b0 Hangul Syllable-B1B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1b3 Hangul Syllable-B1B3 | b1b2 Hangul Syllable-B1B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1b5 Hangul Syllable-B1B5 | b1b4 Hangul Syllable-B1B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1b7 Hangul Syllable-B1B7 | b1b6 Hangul Syllable-B1B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1b9 Hangul Syllable-B1B9 | b1b8 Hangul Syllable-B1B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1bb Hangul Syllable-B1BB | b1ba Hangul Syllable-B1BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1bd Hangul Syllable-B1BD | b1bc Hangul Syllable-B1BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1bf Hangul Syllable-B1BF | b1be Hangul Syllable-B1BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1c1 Hangul Syllable-B1C1 | b1c0 Hangul Syllable-B1C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1c3 Hangul Syllable-B1C3 | b1c2 Hangul Syllable-B1C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1c5 Hangul Syllable-B1C5 | b1c4 Hangul Syllable-B1C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1c7 Hangul Syllable-B1C7 | b1c6 Hangul Syllable-B1C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1c9 Hangul Syllable-B1C9 | b1c8 Hangul Syllable-B1C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1cb Hangul Syllable-B1CB | b1ca Hangul Syllable-B1CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1cd Hangul Syllable-B1CD | b1cc Hangul Syllable-B1CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1cf Hangul Syllable-B1CF | b1ce Hangul Syllable-B1CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1d1 Hangul Syllable-B1D1 | b1d0 Hangul Syllable-B1D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1d3 Hangul Syllable-B1D3 | b1d2 Hangul Syllable-B1D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1d5 Hangul Syllable-B1D5 | b1d4 Hangul Syllable-B1D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1d7 Hangul Syllable-B1D7 | b1d6 Hangul Syllable-B1D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1d9 Hangul Syllable-B1D9 | b1d8 Hangul Syllable-B1D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1db Hangul Syllable-B1DB | b1da Hangul Syllable-B1DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1dd Hangul Syllable-B1DD | b1dc Hangul Syllable-B1DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1df Hangul Syllable-B1DF | b1de Hangul Syllable-B1DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1e1 Hangul Syllable-B1E1 | b1e0 Hangul Syllable-B1E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1e3 Hangul Syllable-B1E3 | b1e2 Hangul Syllable-B1E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1e5 Hangul Syllable-B1E5 | b1e4 Hangul Syllable-B1E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1e7 Hangul Syllable-B1E7 | b1e6 Hangul Syllable-B1E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1e9 Hangul Syllable-B1E9 | b1e8 Hangul Syllable-B1E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1eb Hangul Syllable-B1EB | b1ea Hangul Syllable-B1EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1ed Hangul Syllable-B1ED | b1ec Hangul Syllable-B1EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1ef Hangul Syllable-B1EF | b1ee Hangul Syllable-B1EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1f1 Hangul Syllable-B1F1 | b1f0 Hangul Syllable-B1F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1f3 Hangul Syllable-B1F3 | b1f2 Hangul Syllable-B1F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1f5 Hangul Syllable-B1F5 | b1f4 Hangul Syllable-B1F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1f7 Hangul Syllable-B1F7 | b1f6 Hangul Syllable-B1F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1f9 Hangul Syllable-B1F9 | b1f8 Hangul Syllable-B1F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1fb Hangul Syllable-B1FB | b1fa Hangul Syllable-B1FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1fd Hangul Syllable-B1FD | b1fc Hangul Syllable-B1FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1ff Hangul Syllable-B1FF | b1fe Hangul Syllable-B1FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b201 Hangul Syllable-B201 | b200 Hangul Syllable-B200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b203 Hangul Syllable-B203 | b202 Hangul Syllable-B202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b205 Hangul Syllable-B205 | b204 Hangul Syllable-B204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b207 Hangul Syllable-B207 | b206 Hangul Syllable-B206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b209 Hangul Syllable-B209 | b208 Hangul Syllable-B208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b20b Hangul Syllable-B20B | b20a Hangul Syllable-B20A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b20d Hangul Syllable-B20D | b20c Hangul Syllable-B20C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b20f Hangul Syllable-B20F | b20e Hangul Syllable-B20E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b211 Hangul Syllable-B211 | b210 Hangul Syllable-B210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b213 Hangul Syllable-B213 | b212 Hangul Syllable-B212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b215 Hangul Syllable-B215 | b214 Hangul Syllable-B214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b217 Hangul Syllable-B217 | b216 Hangul Syllable-B216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b219 Hangul Syllable-B219 | b218 Hangul Syllable-B218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b21b Hangul Syllable-B21B | b21a Hangul Syllable-B21A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b21d Hangul Syllable-B21D | b21c Hangul Syllable-B21C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b21f Hangul Syllable-B21F | b21e Hangul Syllable-B21E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b221 Hangul Syllable-B221 | b220 Hangul Syllable-B220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b223 Hangul Syllable-B223 | b222 Hangul Syllable-B222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b225 Hangul Syllable-B225 | b224 Hangul Syllable-B224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b227 Hangul Syllable-B227 | b226 Hangul Syllable-B226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b229 Hangul Syllable-B229 | b228 Hangul Syllable-B228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b22b Hangul Syllable-B22B | b22a Hangul Syllable-B22A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b22d Hangul Syllable-B22D | b22c Hangul Syllable-B22C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b22f Hangul Syllable-B22F | b22e Hangul Syllable-B22E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b231 Hangul Syllable-B231 | b230 Hangul Syllable-B230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b233 Hangul Syllable-B233 | b232 Hangul Syllable-B232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b235 Hangul Syllable-B235 | b234 Hangul Syllable-B234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b237 Hangul Syllable-B237 | b236 Hangul Syllable-B236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b239 Hangul Syllable-B239 | b238 Hangul Syllable-B238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b23b Hangul Syllable-B23B | b23a Hangul Syllable-B23A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b23d Hangul Syllable-B23D | b23c Hangul Syllable-B23C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b23f Hangul Syllable-B23F | b23e Hangul Syllable-B23E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b241 Hangul Syllable-B241 | b240 Hangul Syllable-B240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b243 Hangul Syllable-B243 | b242 Hangul Syllable-B242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b245 Hangul Syllable-B245 | b244 Hangul Syllable-B244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b247 Hangul Syllable-B247 | b246 Hangul Syllable-B246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b249 Hangul Syllable-B249 | b248 Hangul Syllable-B248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b24b Hangul Syllable-B24B | b24a Hangul Syllable-B24A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b24d Hangul Syllable-B24D | b24c Hangul Syllable-B24C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b24f Hangul Syllable-B24F | b24e Hangul Syllable-B24E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b251 Hangul Syllable-B251 | b250 Hangul Syllable-B250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b253 Hangul Syllable-B253 | b252 Hangul Syllable-B252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b255 Hangul Syllable-B255 | b254 Hangul Syllable-B254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b257 Hangul Syllable-B257 | b256 Hangul Syllable-B256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b259 Hangul Syllable-B259 | b258 Hangul Syllable-B258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b25b Hangul Syllable-B25B | b25a Hangul Syllable-B25A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b25d Hangul Syllable-B25D | b25c Hangul Syllable-B25C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b25f Hangul Syllable-B25F | b25e Hangul Syllable-B25E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b261 Hangul Syllable-B261 | b260 Hangul Syllable-B260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b263 Hangul Syllable-B263 | b262 Hangul Syllable-B262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b265 Hangul Syllable-B265 | b264 Hangul Syllable-B264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b267 Hangul Syllable-B267 | b266 Hangul Syllable-B266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b269 Hangul Syllable-B269 | b268 Hangul Syllable-B268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b26b Hangul Syllable-B26B | b26a Hangul Syllable-B26A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b26d Hangul Syllable-B26D | b26c Hangul Syllable-B26C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b26f Hangul Syllable-B26F | b26e Hangul Syllable-B26E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b271 Hangul Syllable-B271 | b270 Hangul Syllable-B270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b273 Hangul Syllable-B273 | b272 Hangul Syllable-B272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b275 Hangul Syllable-B275 | b274 Hangul Syllable-B274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b277 Hangul Syllable-B277 | b276 Hangul Syllable-B276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b279 Hangul Syllable-B279 | b278 Hangul Syllable-B278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b27b Hangul Syllable-B27B | b27a Hangul Syllable-B27A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b27d Hangul Syllable-B27D | b27c Hangul Syllable-B27C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b27f Hangul Syllable-B27F | b27e Hangul Syllable-B27E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b281 Hangul Syllable-B281 | b280 Hangul Syllable-B280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b283 Hangul Syllable-B283 | b282 Hangul Syllable-B282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b285 Hangul Syllable-B285 | b284 Hangul Syllable-B284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b287 Hangul Syllable-B287 | b286 Hangul Syllable-B286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b289 Hangul Syllable-B289 | b288 Hangul Syllable-B288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b28b Hangul Syllable-B28B | b28a Hangul Syllable-B28A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b28d Hangul Syllable-B28D | b28c Hangul Syllable-B28C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b28f Hangul Syllable-B28F | b28e Hangul Syllable-B28E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b291 Hangul Syllable-B291 | b290 Hangul Syllable-B290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b293 Hangul Syllable-B293 | b292 Hangul Syllable-B292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b295 Hangul Syllable-B295 | b294 Hangul Syllable-B294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b297 Hangul Syllable-B297 | b296 Hangul Syllable-B296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b299 Hangul Syllable-B299 | b298 Hangul Syllable-B298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b29b Hangul Syllable-B29B | b29a Hangul Syllable-B29A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b29d Hangul Syllable-B29D | b29c Hangul Syllable-B29C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b29f Hangul Syllable-B29F | b29e Hangul Syllable-B29E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2a1 Hangul Syllable-B2A1 | b2a0 Hangul Syllable-B2A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2a3 Hangul Syllable-B2A3 | b2a2 Hangul Syllable-B2A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2a5 Hangul Syllable-B2A5 | b2a4 Hangul Syllable-B2A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2a7 Hangul Syllable-B2A7 | b2a6 Hangul Syllable-B2A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2a9 Hangul Syllable-B2A9 | b2a8 Hangul Syllable-B2A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2ab Hangul Syllable-B2AB | b2aa Hangul Syllable-B2AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2ad Hangul Syllable-B2AD | b2ac Hangul Syllable-B2AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2af Hangul Syllable-B2AF | b2ae Hangul Syllable-B2AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2b1 Hangul Syllable-B2B1 | b2b0 Hangul Syllable-B2B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2b3 Hangul Syllable-B2B3 | b2b2 Hangul Syllable-B2B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2b5 Hangul Syllable-B2B5 | b2b4 Hangul Syllable-B2B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2b7 Hangul Syllable-B2B7 | b2b6 Hangul Syllable-B2B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2b9 Hangul Syllable-B2B9 | b2b8 Hangul Syllable-B2B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2bb Hangul Syllable-B2BB | b2ba Hangul Syllable-B2BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2bd Hangul Syllable-B2BD | b2bc Hangul Syllable-B2BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2bf Hangul Syllable-B2BF | b2be Hangul Syllable-B2BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2c1 Hangul Syllable-B2C1 | b2c0 Hangul Syllable-B2C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2c3 Hangul Syllable-B2C3 | b2c2 Hangul Syllable-B2C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2c5 Hangul Syllable-B2C5 | b2c4 Hangul Syllable-B2C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2c7 Hangul Syllable-B2C7 | b2c6 Hangul Syllable-B2C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2c9 Hangul Syllable-B2C9 | b2c8 Hangul Syllable-B2C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2cb Hangul Syllable-B2CB | b2ca Hangul Syllable-B2CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2cd Hangul Syllable-B2CD | b2cc Hangul Syllable-B2CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2cf Hangul Syllable-B2CF | b2ce Hangul Syllable-B2CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2d1 Hangul Syllable-B2D1 | b2d0 Hangul Syllable-B2D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2d3 Hangul Syllable-B2D3 | b2d2 Hangul Syllable-B2D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2d5 Hangul Syllable-B2D5 | b2d4 Hangul Syllable-B2D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2d7 Hangul Syllable-B2D7 | b2d6 Hangul Syllable-B2D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2d9 Hangul Syllable-B2D9 | b2d8 Hangul Syllable-B2D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2db Hangul Syllable-B2DB | b2da Hangul Syllable-B2DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2dd Hangul Syllable-B2DD | b2dc Hangul Syllable-B2DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2df Hangul Syllable-B2DF | b2de Hangul Syllable-B2DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2e1 Hangul Syllable-B2E1 | b2e0 Hangul Syllable-B2E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2e3 Hangul Syllable-B2E3 | b2e2 Hangul Syllable-B2E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2e5 Hangul Syllable-B2E5 | b2e4 Hangul Syllable-B2E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2e7 Hangul Syllable-B2E7 | b2e6 Hangul Syllable-B2E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2e9 Hangul Syllable-B2E9 | b2e8 Hangul Syllable-B2E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2eb Hangul Syllable-B2EB | b2ea Hangul Syllable-B2EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2ed Hangul Syllable-B2ED | b2ec Hangul Syllable-B2EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2ef Hangul Syllable-B2EF | b2ee Hangul Syllable-B2EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2f1 Hangul Syllable-B2F1 | b2f0 Hangul Syllable-B2F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2f3 Hangul Syllable-B2F3 | b2f2 Hangul Syllable-B2F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2f5 Hangul Syllable-B2F5 | b2f4 Hangul Syllable-B2F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2f7 Hangul Syllable-B2F7 | b2f6 Hangul Syllable-B2F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2f9 Hangul Syllable-B2F9 | b2f8 Hangul Syllable-B2F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2fb Hangul Syllable-B2FB | b2fa Hangul Syllable-B2FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2fd Hangul Syllable-B2FD | b2fc Hangul Syllable-B2FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2ff Hangul Syllable-B2FF | b2fe Hangul Syllable-B2FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b301 Hangul Syllable-B301 | b300 Hangul Syllable-B300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b303 Hangul Syllable-B303 | b302 Hangul Syllable-B302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b305 Hangul Syllable-B305 | b304 Hangul Syllable-B304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b307 Hangul Syllable-B307 | b306 Hangul Syllable-B306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b309 Hangul Syllable-B309 | b308 Hangul Syllable-B308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b30b Hangul Syllable-B30B | b30a Hangul Syllable-B30A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b30d Hangul Syllable-B30D | b30c Hangul Syllable-B30C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b30f Hangul Syllable-B30F | b30e Hangul Syllable-B30E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b311 Hangul Syllable-B311 | b310 Hangul Syllable-B310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b313 Hangul Syllable-B313 | b312 Hangul Syllable-B312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b315 Hangul Syllable-B315 | b314 Hangul Syllable-B314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b317 Hangul Syllable-B317 | b316 Hangul Syllable-B316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b319 Hangul Syllable-B319 | b318 Hangul Syllable-B318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b31b Hangul Syllable-B31B | b31a Hangul Syllable-B31A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b31d Hangul Syllable-B31D | b31c Hangul Syllable-B31C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b31f Hangul Syllable-B31F | b31e Hangul Syllable-B31E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b321 Hangul Syllable-B321 | b320 Hangul Syllable-B320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b323 Hangul Syllable-B323 | b322 Hangul Syllable-B322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b325 Hangul Syllable-B325 | b324 Hangul Syllable-B324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b327 Hangul Syllable-B327 | b326 Hangul Syllable-B326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b329 Hangul Syllable-B329 | b328 Hangul Syllable-B328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b32b Hangul Syllable-B32B | b32a Hangul Syllable-B32A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b32d Hangul Syllable-B32D | b32c Hangul Syllable-B32C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b32f Hangul Syllable-B32F | b32e Hangul Syllable-B32E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b331 Hangul Syllable-B331 | b330 Hangul Syllable-B330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b333 Hangul Syllable-B333 | b332 Hangul Syllable-B332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b335 Hangul Syllable-B335 | b334 Hangul Syllable-B334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b337 Hangul Syllable-B337 | b336 Hangul Syllable-B336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b339 Hangul Syllable-B339 | b338 Hangul Syllable-B338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b33b Hangul Syllable-B33B | b33a Hangul Syllable-B33A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b33d Hangul Syllable-B33D | b33c Hangul Syllable-B33C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b33f Hangul Syllable-B33F | b33e Hangul Syllable-B33E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b341 Hangul Syllable-B341 | b340 Hangul Syllable-B340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b343 Hangul Syllable-B343 | b342 Hangul Syllable-B342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b345 Hangul Syllable-B345 | b344 Hangul Syllable-B344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b347 Hangul Syllable-B347 | b346 Hangul Syllable-B346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b349 Hangul Syllable-B349 | b348 Hangul Syllable-B348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b34b Hangul Syllable-B34B | b34a Hangul Syllable-B34A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b34d Hangul Syllable-B34D | b34c Hangul Syllable-B34C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b34f Hangul Syllable-B34F | b34e Hangul Syllable-B34E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b351 Hangul Syllable-B351 | b350 Hangul Syllable-B350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b353 Hangul Syllable-B353 | b352 Hangul Syllable-B352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b355 Hangul Syllable-B355 | b354 Hangul Syllable-B354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b357 Hangul Syllable-B357 | b356 Hangul Syllable-B356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b359 Hangul Syllable-B359 | b358 Hangul Syllable-B358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b35b Hangul Syllable-B35B | b35a Hangul Syllable-B35A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b35d Hangul Syllable-B35D | b35c Hangul Syllable-B35C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b35f Hangul Syllable-B35F | b35e Hangul Syllable-B35E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b361 Hangul Syllable-B361 | b360 Hangul Syllable-B360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b363 Hangul Syllable-B363 | b362 Hangul Syllable-B362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b365 Hangul Syllable-B365 | b364 Hangul Syllable-B364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b367 Hangul Syllable-B367 | b366 Hangul Syllable-B366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b369 Hangul Syllable-B369 | b368 Hangul Syllable-B368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b36b Hangul Syllable-B36B | b36a Hangul Syllable-B36A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b36d Hangul Syllable-B36D | b36c Hangul Syllable-B36C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b36f Hangul Syllable-B36F | b36e Hangul Syllable-B36E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b371 Hangul Syllable-B371 | b370 Hangul Syllable-B370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b373 Hangul Syllable-B373 | b372 Hangul Syllable-B372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b375 Hangul Syllable-B375 | b374 Hangul Syllable-B374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b377 Hangul Syllable-B377 | b376 Hangul Syllable-B376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b379 Hangul Syllable-B379 | b378 Hangul Syllable-B378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b37b Hangul Syllable-B37B | b37a Hangul Syllable-B37A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b37d Hangul Syllable-B37D | b37c Hangul Syllable-B37C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b37f Hangul Syllable-B37F | b37e Hangul Syllable-B37E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b381 Hangul Syllable-B381 | b380 Hangul Syllable-B380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b383 Hangul Syllable-B383 | b382 Hangul Syllable-B382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b385 Hangul Syllable-B385 | b384 Hangul Syllable-B384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b387 Hangul Syllable-B387 | b386 Hangul Syllable-B386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b389 Hangul Syllable-B389 | b388 Hangul Syllable-B388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b38b Hangul Syllable-B38B | b38a Hangul Syllable-B38A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b38d Hangul Syllable-B38D | b38c Hangul Syllable-B38C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b38f Hangul Syllable-B38F | b38e Hangul Syllable-B38E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b391 Hangul Syllable-B391 | b390 Hangul Syllable-B390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b393 Hangul Syllable-B393 | b392 Hangul Syllable-B392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b395 Hangul Syllable-B395 | b394 Hangul Syllable-B394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b397 Hangul Syllable-B397 | b396 Hangul Syllable-B396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b399 Hangul Syllable-B399 | b398 Hangul Syllable-B398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b39b Hangul Syllable-B39B | b39a Hangul Syllable-B39A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b39d Hangul Syllable-B39D | b39c Hangul Syllable-B39C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b39f Hangul Syllable-B39F | b39e Hangul Syllable-B39E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3a1 Hangul Syllable-B3A1 | b3a0 Hangul Syllable-B3A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3a3 Hangul Syllable-B3A3 | b3a2 Hangul Syllable-B3A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3a5 Hangul Syllable-B3A5 | b3a4 Hangul Syllable-B3A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3a7 Hangul Syllable-B3A7 | b3a6 Hangul Syllable-B3A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3a9 Hangul Syllable-B3A9 | b3a8 Hangul Syllable-B3A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3ab Hangul Syllable-B3AB | b3aa Hangul Syllable-B3AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3ad Hangul Syllable-B3AD | b3ac Hangul Syllable-B3AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3af Hangul Syllable-B3AF | b3ae Hangul Syllable-B3AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3b1 Hangul Syllable-B3B1 | b3b0 Hangul Syllable-B3B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3b3 Hangul Syllable-B3B3 | b3b2 Hangul Syllable-B3B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3b5 Hangul Syllable-B3B5 | b3b4 Hangul Syllable-B3B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3b7 Hangul Syllable-B3B7 | b3b6 Hangul Syllable-B3B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3b9 Hangul Syllable-B3B9 | b3b8 Hangul Syllable-B3B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3bb Hangul Syllable-B3BB | b3ba Hangul Syllable-B3BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3bd Hangul Syllable-B3BD | b3bc Hangul Syllable-B3BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3bf Hangul Syllable-B3BF | b3be Hangul Syllable-B3BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3c1 Hangul Syllable-B3C1 | b3c0 Hangul Syllable-B3C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3c3 Hangul Syllable-B3C3 | b3c2 Hangul Syllable-B3C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3c5 Hangul Syllable-B3C5 | b3c4 Hangul Syllable-B3C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3c7 Hangul Syllable-B3C7 | b3c6 Hangul Syllable-B3C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3c9 Hangul Syllable-B3C9 | b3c8 Hangul Syllable-B3C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3cb Hangul Syllable-B3CB | b3ca Hangul Syllable-B3CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3cd Hangul Syllable-B3CD | b3cc Hangul Syllable-B3CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3cf Hangul Syllable-B3CF | b3ce Hangul Syllable-B3CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3d1 Hangul Syllable-B3D1 | b3d0 Hangul Syllable-B3D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3d3 Hangul Syllable-B3D3 | b3d2 Hangul Syllable-B3D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3d5 Hangul Syllable-B3D5 | b3d4 Hangul Syllable-B3D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3d7 Hangul Syllable-B3D7 | b3d6 Hangul Syllable-B3D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3d9 Hangul Syllable-B3D9 | b3d8 Hangul Syllable-B3D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3db Hangul Syllable-B3DB | b3da Hangul Syllable-B3DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3dd Hangul Syllable-B3DD | b3dc Hangul Syllable-B3DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3df Hangul Syllable-B3DF | b3de Hangul Syllable-B3DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3e1 Hangul Syllable-B3E1 | b3e0 Hangul Syllable-B3E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3e3 Hangul Syllable-B3E3 | b3e2 Hangul Syllable-B3E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3e5 Hangul Syllable-B3E5 | b3e4 Hangul Syllable-B3E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3e7 Hangul Syllable-B3E7 | b3e6 Hangul Syllable-B3E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3e9 Hangul Syllable-B3E9 | b3e8 Hangul Syllable-B3E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3eb Hangul Syllable-B3EB | b3ea Hangul Syllable-B3EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3ed Hangul Syllable-B3ED | b3ec Hangul Syllable-B3EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3ef Hangul Syllable-B3EF | b3ee Hangul Syllable-B3EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3f1 Hangul Syllable-B3F1 | b3f0 Hangul Syllable-B3F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3f3 Hangul Syllable-B3F3 | b3f2 Hangul Syllable-B3F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3f5 Hangul Syllable-B3F5 | b3f4 Hangul Syllable-B3F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3f7 Hangul Syllable-B3F7 | b3f6 Hangul Syllable-B3F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3f9 Hangul Syllable-B3F9 | b3f8 Hangul Syllable-B3F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3fb Hangul Syllable-B3FB | b3fa Hangul Syllable-B3FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3fd Hangul Syllable-B3FD | b3fc Hangul Syllable-B3FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3ff Hangul Syllable-B3FF | b3fe Hangul Syllable-B3FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b401 Hangul Syllable-B401 | b400 Hangul Syllable-B400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b403 Hangul Syllable-B403 | b402 Hangul Syllable-B402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b405 Hangul Syllable-B405 | b404 Hangul Syllable-B404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b407 Hangul Syllable-B407 | b406 Hangul Syllable-B406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b409 Hangul Syllable-B409 | b408 Hangul Syllable-B408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b40b Hangul Syllable-B40B | b40a Hangul Syllable-B40A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b40d Hangul Syllable-B40D | b40c Hangul Syllable-B40C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b40f Hangul Syllable-B40F | b40e Hangul Syllable-B40E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b411 Hangul Syllable-B411 | b410 Hangul Syllable-B410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b413 Hangul Syllable-B413 | b412 Hangul Syllable-B412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b415 Hangul Syllable-B415 | b414 Hangul Syllable-B414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b417 Hangul Syllable-B417 | b416 Hangul Syllable-B416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b419 Hangul Syllable-B419 | b418 Hangul Syllable-B418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b41b Hangul Syllable-B41B | b41a Hangul Syllable-B41A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b41d Hangul Syllable-B41D | b41c Hangul Syllable-B41C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b41f Hangul Syllable-B41F | b41e Hangul Syllable-B41E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b421 Hangul Syllable-B421 | b420 Hangul Syllable-B420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b423 Hangul Syllable-B423 | b422 Hangul Syllable-B422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b425 Hangul Syllable-B425 | b424 Hangul Syllable-B424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b427 Hangul Syllable-B427 | b426 Hangul Syllable-B426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b429 Hangul Syllable-B429 | b428 Hangul Syllable-B428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b42b Hangul Syllable-B42B | b42a Hangul Syllable-B42A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b42d Hangul Syllable-B42D | b42c Hangul Syllable-B42C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b42f Hangul Syllable-B42F | b42e Hangul Syllable-B42E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b431 Hangul Syllable-B431 | b430 Hangul Syllable-B430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b433 Hangul Syllable-B433 | b432 Hangul Syllable-B432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b435 Hangul Syllable-B435 | b434 Hangul Syllable-B434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b437 Hangul Syllable-B437 | b436 Hangul Syllable-B436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b439 Hangul Syllable-B439 | b438 Hangul Syllable-B438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b43b Hangul Syllable-B43B | b43a Hangul Syllable-B43A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b43d Hangul Syllable-B43D | b43c Hangul Syllable-B43C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b43f Hangul Syllable-B43F | b43e Hangul Syllable-B43E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b441 Hangul Syllable-B441 | b440 Hangul Syllable-B440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b443 Hangul Syllable-B443 | b442 Hangul Syllable-B442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b445 Hangul Syllable-B445 | b444 Hangul Syllable-B444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b447 Hangul Syllable-B447 | b446 Hangul Syllable-B446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b449 Hangul Syllable-B449 | b448 Hangul Syllable-B448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b44b Hangul Syllable-B44B | b44a Hangul Syllable-B44A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b44d Hangul Syllable-B44D | b44c Hangul Syllable-B44C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b44f Hangul Syllable-B44F | b44e Hangul Syllable-B44E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b451 Hangul Syllable-B451 | b450 Hangul Syllable-B450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b453 Hangul Syllable-B453 | b452 Hangul Syllable-B452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b455 Hangul Syllable-B455 | b454 Hangul Syllable-B454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b457 Hangul Syllable-B457 | b456 Hangul Syllable-B456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b459 Hangul Syllable-B459 | b458 Hangul Syllable-B458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b45b Hangul Syllable-B45B | b45a Hangul Syllable-B45A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b45d Hangul Syllable-B45D | b45c Hangul Syllable-B45C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b45f Hangul Syllable-B45F | b45e Hangul Syllable-B45E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b461 Hangul Syllable-B461 | b460 Hangul Syllable-B460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b463 Hangul Syllable-B463 | b462 Hangul Syllable-B462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b465 Hangul Syllable-B465 | b464 Hangul Syllable-B464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b467 Hangul Syllable-B467 | b466 Hangul Syllable-B466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b469 Hangul Syllable-B469 | b468 Hangul Syllable-B468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b46b Hangul Syllable-B46B | b46a Hangul Syllable-B46A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b46d Hangul Syllable-B46D | b46c Hangul Syllable-B46C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b46f Hangul Syllable-B46F | b46e Hangul Syllable-B46E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b471 Hangul Syllable-B471 | b470 Hangul Syllable-B470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b473 Hangul Syllable-B473 | b472 Hangul Syllable-B472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b475 Hangul Syllable-B475 | b474 Hangul Syllable-B474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b477 Hangul Syllable-B477 | b476 Hangul Syllable-B476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b479 Hangul Syllable-B479 | b478 Hangul Syllable-B478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b47b Hangul Syllable-B47B | b47a Hangul Syllable-B47A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b47d Hangul Syllable-B47D | b47c Hangul Syllable-B47C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b47f Hangul Syllable-B47F | b47e Hangul Syllable-B47E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b481 Hangul Syllable-B481 | b480 Hangul Syllable-B480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b483 Hangul Syllable-B483 | b482 Hangul Syllable-B482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b485 Hangul Syllable-B485 | b484 Hangul Syllable-B484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b487 Hangul Syllable-B487 | b486 Hangul Syllable-B486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b489 Hangul Syllable-B489 | b488 Hangul Syllable-B488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b48b Hangul Syllable-B48B | b48a Hangul Syllable-B48A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b48d Hangul Syllable-B48D | b48c Hangul Syllable-B48C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b48f Hangul Syllable-B48F | b48e Hangul Syllable-B48E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b491 Hangul Syllable-B491 | b490 Hangul Syllable-B490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b493 Hangul Syllable-B493 | b492 Hangul Syllable-B492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b495 Hangul Syllable-B495 | b494 Hangul Syllable-B494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b497 Hangul Syllable-B497 | b496 Hangul Syllable-B496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b499 Hangul Syllable-B499 | b498 Hangul Syllable-B498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b49b Hangul Syllable-B49B | b49a Hangul Syllable-B49A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b49d Hangul Syllable-B49D | b49c Hangul Syllable-B49C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b49f Hangul Syllable-B49F | b49e Hangul Syllable-B49E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4a1 Hangul Syllable-B4A1 | b4a0 Hangul Syllable-B4A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4a3 Hangul Syllable-B4A3 | b4a2 Hangul Syllable-B4A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4a5 Hangul Syllable-B4A5 | b4a4 Hangul Syllable-B4A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4a7 Hangul Syllable-B4A7 | b4a6 Hangul Syllable-B4A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4a9 Hangul Syllable-B4A9 | b4a8 Hangul Syllable-B4A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4ab Hangul Syllable-B4AB | b4aa Hangul Syllable-B4AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4ad Hangul Syllable-B4AD | b4ac Hangul Syllable-B4AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4af Hangul Syllable-B4AF | b4ae Hangul Syllable-B4AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4b1 Hangul Syllable-B4B1 | b4b0 Hangul Syllable-B4B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4b3 Hangul Syllable-B4B3 | b4b2 Hangul Syllable-B4B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4b5 Hangul Syllable-B4B5 | b4b4 Hangul Syllable-B4B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4b7 Hangul Syllable-B4B7 | b4b6 Hangul Syllable-B4B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4b9 Hangul Syllable-B4B9 | b4b8 Hangul Syllable-B4B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4bb Hangul Syllable-B4BB | b4ba Hangul Syllable-B4BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4bd Hangul Syllable-B4BD | b4bc Hangul Syllable-B4BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4bf Hangul Syllable-B4BF | b4be Hangul Syllable-B4BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4c1 Hangul Syllable-B4C1 | b4c0 Hangul Syllable-B4C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4c3 Hangul Syllable-B4C3 | b4c2 Hangul Syllable-B4C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4c5 Hangul Syllable-B4C5 | b4c4 Hangul Syllable-B4C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4c7 Hangul Syllable-B4C7 | b4c6 Hangul Syllable-B4C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4c9 Hangul Syllable-B4C9 | b4c8 Hangul Syllable-B4C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4cb Hangul Syllable-B4CB | b4ca Hangul Syllable-B4CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4cd Hangul Syllable-B4CD | b4cc Hangul Syllable-B4CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4cf Hangul Syllable-B4CF | b4ce Hangul Syllable-B4CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4d1 Hangul Syllable-B4D1 | b4d0 Hangul Syllable-B4D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4d3 Hangul Syllable-B4D3 | b4d2 Hangul Syllable-B4D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4d5 Hangul Syllable-B4D5 | b4d4 Hangul Syllable-B4D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4d7 Hangul Syllable-B4D7 | b4d6 Hangul Syllable-B4D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4d9 Hangul Syllable-B4D9 | b4d8 Hangul Syllable-B4D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4db Hangul Syllable-B4DB | b4da Hangul Syllable-B4DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4dd Hangul Syllable-B4DD | b4dc Hangul Syllable-B4DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4df Hangul Syllable-B4DF | b4de Hangul Syllable-B4DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4e1 Hangul Syllable-B4E1 | b4e0 Hangul Syllable-B4E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4e3 Hangul Syllable-B4E3 | b4e2 Hangul Syllable-B4E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4e5 Hangul Syllable-B4E5 | b4e4 Hangul Syllable-B4E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4e7 Hangul Syllable-B4E7 | b4e6 Hangul Syllable-B4E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4e9 Hangul Syllable-B4E9 | b4e8 Hangul Syllable-B4E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4eb Hangul Syllable-B4EB | b4ea Hangul Syllable-B4EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4ed Hangul Syllable-B4ED | b4ec Hangul Syllable-B4EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4ef Hangul Syllable-B4EF | b4ee Hangul Syllable-B4EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4f1 Hangul Syllable-B4F1 | b4f0 Hangul Syllable-B4F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4f3 Hangul Syllable-B4F3 | b4f2 Hangul Syllable-B4F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4f5 Hangul Syllable-B4F5 | b4f4 Hangul Syllable-B4F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4f7 Hangul Syllable-B4F7 | b4f6 Hangul Syllable-B4F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4f9 Hangul Syllable-B4F9 | b4f8 Hangul Syllable-B4F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4fb Hangul Syllable-B4FB | b4fa Hangul Syllable-B4FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4fd Hangul Syllable-B4FD | b4fc Hangul Syllable-B4FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4ff Hangul Syllable-B4FF | b4fe Hangul Syllable-B4FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b501 Hangul Syllable-B501 | b500 Hangul Syllable-B500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b503 Hangul Syllable-B503 | b502 Hangul Syllable-B502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b505 Hangul Syllable-B505 | b504 Hangul Syllable-B504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b507 Hangul Syllable-B507 | b506 Hangul Syllable-B506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b509 Hangul Syllable-B509 | b508 Hangul Syllable-B508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b50b Hangul Syllable-B50B | b50a Hangul Syllable-B50A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b50d Hangul Syllable-B50D | b50c Hangul Syllable-B50C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b50f Hangul Syllable-B50F | b50e Hangul Syllable-B50E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b511 Hangul Syllable-B511 | b510 Hangul Syllable-B510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b513 Hangul Syllable-B513 | b512 Hangul Syllable-B512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b515 Hangul Syllable-B515 | b514 Hangul Syllable-B514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b517 Hangul Syllable-B517 | b516 Hangul Syllable-B516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b519 Hangul Syllable-B519 | b518 Hangul Syllable-B518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b51b Hangul Syllable-B51B | b51a Hangul Syllable-B51A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b51d Hangul Syllable-B51D | b51c Hangul Syllable-B51C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b51f Hangul Syllable-B51F | b51e Hangul Syllable-B51E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b521 Hangul Syllable-B521 | b520 Hangul Syllable-B520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b523 Hangul Syllable-B523 | b522 Hangul Syllable-B522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b525 Hangul Syllable-B525 | b524 Hangul Syllable-B524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b527 Hangul Syllable-B527 | b526 Hangul Syllable-B526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b529 Hangul Syllable-B529 | b528 Hangul Syllable-B528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b52b Hangul Syllable-B52B | b52a Hangul Syllable-B52A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b52d Hangul Syllable-B52D | b52c Hangul Syllable-B52C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b52f Hangul Syllable-B52F | b52e Hangul Syllable-B52E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b531 Hangul Syllable-B531 | b530 Hangul Syllable-B530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b533 Hangul Syllable-B533 | b532 Hangul Syllable-B532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b535 Hangul Syllable-B535 | b534 Hangul Syllable-B534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b537 Hangul Syllable-B537 | b536 Hangul Syllable-B536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b539 Hangul Syllable-B539 | b538 Hangul Syllable-B538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b53b Hangul Syllable-B53B | b53a Hangul Syllable-B53A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b53d Hangul Syllable-B53D | b53c Hangul Syllable-B53C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b53f Hangul Syllable-B53F | b53e Hangul Syllable-B53E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b541 Hangul Syllable-B541 | b540 Hangul Syllable-B540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b543 Hangul Syllable-B543 | b542 Hangul Syllable-B542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b545 Hangul Syllable-B545 | b544 Hangul Syllable-B544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b547 Hangul Syllable-B547 | b546 Hangul Syllable-B546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b549 Hangul Syllable-B549 | b548 Hangul Syllable-B548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b54b Hangul Syllable-B54B | b54a Hangul Syllable-B54A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b54d Hangul Syllable-B54D | b54c Hangul Syllable-B54C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b54f Hangul Syllable-B54F | b54e Hangul Syllable-B54E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b551 Hangul Syllable-B551 | b550 Hangul Syllable-B550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b553 Hangul Syllable-B553 | b552 Hangul Syllable-B552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b555 Hangul Syllable-B555 | b554 Hangul Syllable-B554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b557 Hangul Syllable-B557 | b556 Hangul Syllable-B556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b559 Hangul Syllable-B559 | b558 Hangul Syllable-B558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b55b Hangul Syllable-B55B | b55a Hangul Syllable-B55A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b55d Hangul Syllable-B55D | b55c Hangul Syllable-B55C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b55f Hangul Syllable-B55F | b55e Hangul Syllable-B55E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b561 Hangul Syllable-B561 | b560 Hangul Syllable-B560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b563 Hangul Syllable-B563 | b562 Hangul Syllable-B562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b565 Hangul Syllable-B565 | b564 Hangul Syllable-B564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b567 Hangul Syllable-B567 | b566 Hangul Syllable-B566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b569 Hangul Syllable-B569 | b568 Hangul Syllable-B568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b56b Hangul Syllable-B56B | b56a Hangul Syllable-B56A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b56d Hangul Syllable-B56D | b56c Hangul Syllable-B56C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b56f Hangul Syllable-B56F | b56e Hangul Syllable-B56E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b571 Hangul Syllable-B571 | b570 Hangul Syllable-B570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b573 Hangul Syllable-B573 | b572 Hangul Syllable-B572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b575 Hangul Syllable-B575 | b574 Hangul Syllable-B574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b577 Hangul Syllable-B577 | b576 Hangul Syllable-B576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b579 Hangul Syllable-B579 | b578 Hangul Syllable-B578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b57b Hangul Syllable-B57B | b57a Hangul Syllable-B57A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b57d Hangul Syllable-B57D | b57c Hangul Syllable-B57C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b57f Hangul Syllable-B57F | b57e Hangul Syllable-B57E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b581 Hangul Syllable-B581 | b580 Hangul Syllable-B580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b583 Hangul Syllable-B583 | b582 Hangul Syllable-B582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b585 Hangul Syllable-B585 | b584 Hangul Syllable-B584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b587 Hangul Syllable-B587 | b586 Hangul Syllable-B586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b589 Hangul Syllable-B589 | b588 Hangul Syllable-B588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b58b Hangul Syllable-B58B | b58a Hangul Syllable-B58A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b58d Hangul Syllable-B58D | b58c Hangul Syllable-B58C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b58f Hangul Syllable-B58F | b58e Hangul Syllable-B58E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b591 Hangul Syllable-B591 | b590 Hangul Syllable-B590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b593 Hangul Syllable-B593 | b592 Hangul Syllable-B592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b595 Hangul Syllable-B595 | b594 Hangul Syllable-B594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b597 Hangul Syllable-B597 | b596 Hangul Syllable-B596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b599 Hangul Syllable-B599 | b598 Hangul Syllable-B598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b59b Hangul Syllable-B59B | b59a Hangul Syllable-B59A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b59d Hangul Syllable-B59D | b59c Hangul Syllable-B59C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b59f Hangul Syllable-B59F | b59e Hangul Syllable-B59E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5a1 Hangul Syllable-B5A1 | b5a0 Hangul Syllable-B5A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5a3 Hangul Syllable-B5A3 | b5a2 Hangul Syllable-B5A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5a5 Hangul Syllable-B5A5 | b5a4 Hangul Syllable-B5A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5a7 Hangul Syllable-B5A7 | b5a6 Hangul Syllable-B5A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5a9 Hangul Syllable-B5A9 | b5a8 Hangul Syllable-B5A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5ab Hangul Syllable-B5AB | b5aa Hangul Syllable-B5AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5ad Hangul Syllable-B5AD | b5ac Hangul Syllable-B5AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5af Hangul Syllable-B5AF | b5ae Hangul Syllable-B5AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5b1 Hangul Syllable-B5B1 | b5b0 Hangul Syllable-B5B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5b3 Hangul Syllable-B5B3 | b5b2 Hangul Syllable-B5B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5b5 Hangul Syllable-B5B5 | b5b4 Hangul Syllable-B5B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5b7 Hangul Syllable-B5B7 | b5b6 Hangul Syllable-B5B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5b9 Hangul Syllable-B5B9 | b5b8 Hangul Syllable-B5B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5bb Hangul Syllable-B5BB | b5ba Hangul Syllable-B5BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5bd Hangul Syllable-B5BD | b5bc Hangul Syllable-B5BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5bf Hangul Syllable-B5BF | b5be Hangul Syllable-B5BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5c1 Hangul Syllable-B5C1 | b5c0 Hangul Syllable-B5C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5c3 Hangul Syllable-B5C3 | b5c2 Hangul Syllable-B5C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5c5 Hangul Syllable-B5C5 | b5c4 Hangul Syllable-B5C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5c7 Hangul Syllable-B5C7 | b5c6 Hangul Syllable-B5C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5c9 Hangul Syllable-B5C9 | b5c8 Hangul Syllable-B5C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5cb Hangul Syllable-B5CB | b5ca Hangul Syllable-B5CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5cd Hangul Syllable-B5CD | b5cc Hangul Syllable-B5CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5cf Hangul Syllable-B5CF | b5ce Hangul Syllable-B5CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5d1 Hangul Syllable-B5D1 | b5d0 Hangul Syllable-B5D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5d3 Hangul Syllable-B5D3 | b5d2 Hangul Syllable-B5D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5d5 Hangul Syllable-B5D5 | b5d4 Hangul Syllable-B5D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5d7 Hangul Syllable-B5D7 | b5d6 Hangul Syllable-B5D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5d9 Hangul Syllable-B5D9 | b5d8 Hangul Syllable-B5D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5db Hangul Syllable-B5DB | b5da Hangul Syllable-B5DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5dd Hangul Syllable-B5DD | b5dc Hangul Syllable-B5DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5df Hangul Syllable-B5DF | b5de Hangul Syllable-B5DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5e1 Hangul Syllable-B5E1 | b5e0 Hangul Syllable-B5E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5e3 Hangul Syllable-B5E3 | b5e2 Hangul Syllable-B5E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5e5 Hangul Syllable-B5E5 | b5e4 Hangul Syllable-B5E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5e7 Hangul Syllable-B5E7 | b5e6 Hangul Syllable-B5E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5e9 Hangul Syllable-B5E9 | b5e8 Hangul Syllable-B5E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5eb Hangul Syllable-B5EB | b5ea Hangul Syllable-B5EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5ed Hangul Syllable-B5ED | b5ec Hangul Syllable-B5EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5ef Hangul Syllable-B5EF | b5ee Hangul Syllable-B5EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5f1 Hangul Syllable-B5F1 | b5f0 Hangul Syllable-B5F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5f3 Hangul Syllable-B5F3 | b5f2 Hangul Syllable-B5F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5f5 Hangul Syllable-B5F5 | b5f4 Hangul Syllable-B5F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5f7 Hangul Syllable-B5F7 | b5f6 Hangul Syllable-B5F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5f9 Hangul Syllable-B5F9 | b5f8 Hangul Syllable-B5F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5fb Hangul Syllable-B5FB | b5fa Hangul Syllable-B5FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5fd Hangul Syllable-B5FD | b5fc Hangul Syllable-B5FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5ff Hangul Syllable-B5FF | b5fe Hangul Syllable-B5FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b601 Hangul Syllable-B601 | b600 Hangul Syllable-B600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b603 Hangul Syllable-B603 | b602 Hangul Syllable-B602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b605 Hangul Syllable-B605 | b604 Hangul Syllable-B604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b607 Hangul Syllable-B607 | b606 Hangul Syllable-B606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b609 Hangul Syllable-B609 | b608 Hangul Syllable-B608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b60b Hangul Syllable-B60B | b60a Hangul Syllable-B60A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b60d Hangul Syllable-B60D | b60c Hangul Syllable-B60C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b60f Hangul Syllable-B60F | b60e Hangul Syllable-B60E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b611 Hangul Syllable-B611 | b610 Hangul Syllable-B610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b613 Hangul Syllable-B613 | b612 Hangul Syllable-B612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b615 Hangul Syllable-B615 | b614 Hangul Syllable-B614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b617 Hangul Syllable-B617 | b616 Hangul Syllable-B616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b619 Hangul Syllable-B619 | b618 Hangul Syllable-B618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b61b Hangul Syllable-B61B | b61a Hangul Syllable-B61A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b61d Hangul Syllable-B61D | b61c Hangul Syllable-B61C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b61f Hangul Syllable-B61F | b61e Hangul Syllable-B61E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b621 Hangul Syllable-B621 | b620 Hangul Syllable-B620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b623 Hangul Syllable-B623 | b622 Hangul Syllable-B622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b625 Hangul Syllable-B625 | b624 Hangul Syllable-B624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b627 Hangul Syllable-B627 | b626 Hangul Syllable-B626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b629 Hangul Syllable-B629 | b628 Hangul Syllable-B628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b62b Hangul Syllable-B62B | b62a Hangul Syllable-B62A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b62d Hangul Syllable-B62D | b62c Hangul Syllable-B62C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b62f Hangul Syllable-B62F | b62e Hangul Syllable-B62E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b631 Hangul Syllable-B631 | b630 Hangul Syllable-B630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b633 Hangul Syllable-B633 | b632 Hangul Syllable-B632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b635 Hangul Syllable-B635 | b634 Hangul Syllable-B634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b637 Hangul Syllable-B637 | b636 Hangul Syllable-B636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b639 Hangul Syllable-B639 | b638 Hangul Syllable-B638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b63b Hangul Syllable-B63B | b63a Hangul Syllable-B63A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b63d Hangul Syllable-B63D | b63c Hangul Syllable-B63C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b63f Hangul Syllable-B63F | b63e Hangul Syllable-B63E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b641 Hangul Syllable-B641 | b640 Hangul Syllable-B640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b643 Hangul Syllable-B643 | b642 Hangul Syllable-B642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b645 Hangul Syllable-B645 | b644 Hangul Syllable-B644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b647 Hangul Syllable-B647 | b646 Hangul Syllable-B646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b649 Hangul Syllable-B649 | b648 Hangul Syllable-B648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b64b Hangul Syllable-B64B | b64a Hangul Syllable-B64A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b64d Hangul Syllable-B64D | b64c Hangul Syllable-B64C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b64f Hangul Syllable-B64F | b64e Hangul Syllable-B64E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b651 Hangul Syllable-B651 | b650 Hangul Syllable-B650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b653 Hangul Syllable-B653 | b652 Hangul Syllable-B652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b655 Hangul Syllable-B655 | b654 Hangul Syllable-B654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b657 Hangul Syllable-B657 | b656 Hangul Syllable-B656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b659 Hangul Syllable-B659 | b658 Hangul Syllable-B658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b65b Hangul Syllable-B65B | b65a Hangul Syllable-B65A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b65d Hangul Syllable-B65D | b65c Hangul Syllable-B65C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b65f Hangul Syllable-B65F | b65e Hangul Syllable-B65E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b661 Hangul Syllable-B661 | b660 Hangul Syllable-B660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b663 Hangul Syllable-B663 | b662 Hangul Syllable-B662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b665 Hangul Syllable-B665 | b664 Hangul Syllable-B664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b667 Hangul Syllable-B667 | b666 Hangul Syllable-B666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b669 Hangul Syllable-B669 | b668 Hangul Syllable-B668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b66b Hangul Syllable-B66B | b66a Hangul Syllable-B66A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b66d Hangul Syllable-B66D | b66c Hangul Syllable-B66C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b66f Hangul Syllable-B66F | b66e Hangul Syllable-B66E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b671 Hangul Syllable-B671 | b670 Hangul Syllable-B670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b673 Hangul Syllable-B673 | b672 Hangul Syllable-B672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b675 Hangul Syllable-B675 | b674 Hangul Syllable-B674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b677 Hangul Syllable-B677 | b676 Hangul Syllable-B676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b679 Hangul Syllable-B679 | b678 Hangul Syllable-B678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b67b Hangul Syllable-B67B | b67a Hangul Syllable-B67A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b67d Hangul Syllable-B67D | b67c Hangul Syllable-B67C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b67f Hangul Syllable-B67F | b67e Hangul Syllable-B67E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b681 Hangul Syllable-B681 | b680 Hangul Syllable-B680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b683 Hangul Syllable-B683 | b682 Hangul Syllable-B682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b685 Hangul Syllable-B685 | b684 Hangul Syllable-B684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b687 Hangul Syllable-B687 | b686 Hangul Syllable-B686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b689 Hangul Syllable-B689 | b688 Hangul Syllable-B688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b68b Hangul Syllable-B68B | b68a Hangul Syllable-B68A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b68d Hangul Syllable-B68D | b68c Hangul Syllable-B68C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b68f Hangul Syllable-B68F | b68e Hangul Syllable-B68E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b691 Hangul Syllable-B691 | b690 Hangul Syllable-B690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b693 Hangul Syllable-B693 | b692 Hangul Syllable-B692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b695 Hangul Syllable-B695 | b694 Hangul Syllable-B694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b697 Hangul Syllable-B697 | b696 Hangul Syllable-B696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b699 Hangul Syllable-B699 | b698 Hangul Syllable-B698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b69b Hangul Syllable-B69B | b69a Hangul Syllable-B69A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b69d Hangul Syllable-B69D | b69c Hangul Syllable-B69C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b69f Hangul Syllable-B69F | b69e Hangul Syllable-B69E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6a1 Hangul Syllable-B6A1 | b6a0 Hangul Syllable-B6A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6a3 Hangul Syllable-B6A3 | b6a2 Hangul Syllable-B6A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6a5 Hangul Syllable-B6A5 | b6a4 Hangul Syllable-B6A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6a7 Hangul Syllable-B6A7 | b6a6 Hangul Syllable-B6A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6a9 Hangul Syllable-B6A9 | b6a8 Hangul Syllable-B6A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6ab Hangul Syllable-B6AB | b6aa Hangul Syllable-B6AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6ad Hangul Syllable-B6AD | b6ac Hangul Syllable-B6AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6af Hangul Syllable-B6AF | b6ae Hangul Syllable-B6AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6b1 Hangul Syllable-B6B1 | b6b0 Hangul Syllable-B6B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6b3 Hangul Syllable-B6B3 | b6b2 Hangul Syllable-B6B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6b5 Hangul Syllable-B6B5 | b6b4 Hangul Syllable-B6B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6b7 Hangul Syllable-B6B7 | b6b6 Hangul Syllable-B6B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6b9 Hangul Syllable-B6B9 | b6b8 Hangul Syllable-B6B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6bb Hangul Syllable-B6BB | b6ba Hangul Syllable-B6BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6bd Hangul Syllable-B6BD | b6bc Hangul Syllable-B6BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6bf Hangul Syllable-B6BF | b6be Hangul Syllable-B6BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6c1 Hangul Syllable-B6C1 | b6c0 Hangul Syllable-B6C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6c3 Hangul Syllable-B6C3 | b6c2 Hangul Syllable-B6C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6c5 Hangul Syllable-B6C5 | b6c4 Hangul Syllable-B6C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6c7 Hangul Syllable-B6C7 | b6c6 Hangul Syllable-B6C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6c9 Hangul Syllable-B6C9 | b6c8 Hangul Syllable-B6C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6cb Hangul Syllable-B6CB | b6ca Hangul Syllable-B6CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6cd Hangul Syllable-B6CD | b6cc Hangul Syllable-B6CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6cf Hangul Syllable-B6CF | b6ce Hangul Syllable-B6CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6d1 Hangul Syllable-B6D1 | b6d0 Hangul Syllable-B6D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6d3 Hangul Syllable-B6D3 | b6d2 Hangul Syllable-B6D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6d5 Hangul Syllable-B6D5 | b6d4 Hangul Syllable-B6D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6d7 Hangul Syllable-B6D7 | b6d6 Hangul Syllable-B6D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6d9 Hangul Syllable-B6D9 | b6d8 Hangul Syllable-B6D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6db Hangul Syllable-B6DB | b6da Hangul Syllable-B6DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6dd Hangul Syllable-B6DD | b6dc Hangul Syllable-B6DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6df Hangul Syllable-B6DF | b6de Hangul Syllable-B6DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6e1 Hangul Syllable-B6E1 | b6e0 Hangul Syllable-B6E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6e3 Hangul Syllable-B6E3 | b6e2 Hangul Syllable-B6E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6e5 Hangul Syllable-B6E5 | b6e4 Hangul Syllable-B6E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6e7 Hangul Syllable-B6E7 | b6e6 Hangul Syllable-B6E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6e9 Hangul Syllable-B6E9 | b6e8 Hangul Syllable-B6E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6eb Hangul Syllable-B6EB | b6ea Hangul Syllable-B6EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6ed Hangul Syllable-B6ED | b6ec Hangul Syllable-B6EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6ef Hangul Syllable-B6EF | b6ee Hangul Syllable-B6EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6f1 Hangul Syllable-B6F1 | b6f0 Hangul Syllable-B6F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6f3 Hangul Syllable-B6F3 | b6f2 Hangul Syllable-B6F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6f5 Hangul Syllable-B6F5 | b6f4 Hangul Syllable-B6F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6f7 Hangul Syllable-B6F7 | b6f6 Hangul Syllable-B6F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6f9 Hangul Syllable-B6F9 | b6f8 Hangul Syllable-B6F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6fb Hangul Syllable-B6FB | b6fa Hangul Syllable-B6FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6fd Hangul Syllable-B6FD | b6fc Hangul Syllable-B6FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6ff Hangul Syllable-B6FF | b6fe Hangul Syllable-B6FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b701 Hangul Syllable-B701 | b700 Hangul Syllable-B700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b703 Hangul Syllable-B703 | b702 Hangul Syllable-B702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b705 Hangul Syllable-B705 | b704 Hangul Syllable-B704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b707 Hangul Syllable-B707 | b706 Hangul Syllable-B706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b709 Hangul Syllable-B709 | b708 Hangul Syllable-B708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b70b Hangul Syllable-B70B | b70a Hangul Syllable-B70A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b70d Hangul Syllable-B70D | b70c Hangul Syllable-B70C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b70f Hangul Syllable-B70F | b70e Hangul Syllable-B70E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b711 Hangul Syllable-B711 | b710 Hangul Syllable-B710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b713 Hangul Syllable-B713 | b712 Hangul Syllable-B712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b715 Hangul Syllable-B715 | b714 Hangul Syllable-B714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b717 Hangul Syllable-B717 | b716 Hangul Syllable-B716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b719 Hangul Syllable-B719 | b718 Hangul Syllable-B718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b71b Hangul Syllable-B71B | b71a Hangul Syllable-B71A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b71d Hangul Syllable-B71D | b71c Hangul Syllable-B71C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b71f Hangul Syllable-B71F | b71e Hangul Syllable-B71E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b721 Hangul Syllable-B721 | b720 Hangul Syllable-B720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b723 Hangul Syllable-B723 | b722 Hangul Syllable-B722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b725 Hangul Syllable-B725 | b724 Hangul Syllable-B724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b727 Hangul Syllable-B727 | b726 Hangul Syllable-B726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b729 Hangul Syllable-B729 | b728 Hangul Syllable-B728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b72b Hangul Syllable-B72B | b72a Hangul Syllable-B72A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b72d Hangul Syllable-B72D | b72c Hangul Syllable-B72C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b72f Hangul Syllable-B72F | b72e Hangul Syllable-B72E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b731 Hangul Syllable-B731 | b730 Hangul Syllable-B730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b733 Hangul Syllable-B733 | b732 Hangul Syllable-B732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b735 Hangul Syllable-B735 | b734 Hangul Syllable-B734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b737 Hangul Syllable-B737 | b736 Hangul Syllable-B736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b739 Hangul Syllable-B739 | b738 Hangul Syllable-B738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b73b Hangul Syllable-B73B | b73a Hangul Syllable-B73A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b73d Hangul Syllable-B73D | b73c Hangul Syllable-B73C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b73f Hangul Syllable-B73F | b73e Hangul Syllable-B73E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b741 Hangul Syllable-B741 | b740 Hangul Syllable-B740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b743 Hangul Syllable-B743 | b742 Hangul Syllable-B742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b745 Hangul Syllable-B745 | b744 Hangul Syllable-B744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b747 Hangul Syllable-B747 | b746 Hangul Syllable-B746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b749 Hangul Syllable-B749 | b748 Hangul Syllable-B748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b74b Hangul Syllable-B74B | b74a Hangul Syllable-B74A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b74d Hangul Syllable-B74D | b74c Hangul Syllable-B74C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b74f Hangul Syllable-B74F | b74e Hangul Syllable-B74E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b751 Hangul Syllable-B751 | b750 Hangul Syllable-B750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b753 Hangul Syllable-B753 | b752 Hangul Syllable-B752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b755 Hangul Syllable-B755 | b754 Hangul Syllable-B754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b757 Hangul Syllable-B757 | b756 Hangul Syllable-B756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b759 Hangul Syllable-B759 | b758 Hangul Syllable-B758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b75b Hangul Syllable-B75B | b75a Hangul Syllable-B75A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b75d Hangul Syllable-B75D | b75c Hangul Syllable-B75C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b75f Hangul Syllable-B75F | b75e Hangul Syllable-B75E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b761 Hangul Syllable-B761 | b760 Hangul Syllable-B760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b763 Hangul Syllable-B763 | b762 Hangul Syllable-B762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b765 Hangul Syllable-B765 | b764 Hangul Syllable-B764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b767 Hangul Syllable-B767 | b766 Hangul Syllable-B766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b769 Hangul Syllable-B769 | b768 Hangul Syllable-B768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b76b Hangul Syllable-B76B | b76a Hangul Syllable-B76A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b76d Hangul Syllable-B76D | b76c Hangul Syllable-B76C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b76f Hangul Syllable-B76F | b76e Hangul Syllable-B76E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b771 Hangul Syllable-B771 | b770 Hangul Syllable-B770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b773 Hangul Syllable-B773 | b772 Hangul Syllable-B772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b775 Hangul Syllable-B775 | b774 Hangul Syllable-B774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b777 Hangul Syllable-B777 | b776 Hangul Syllable-B776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b779 Hangul Syllable-B779 | b778 Hangul Syllable-B778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b77b Hangul Syllable-B77B | b77a Hangul Syllable-B77A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b77d Hangul Syllable-B77D | b77c Hangul Syllable-B77C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b77f Hangul Syllable-B77F | b77e Hangul Syllable-B77E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b781 Hangul Syllable-B781 | b780 Hangul Syllable-B780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b783 Hangul Syllable-B783 | b782 Hangul Syllable-B782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b785 Hangul Syllable-B785 | b784 Hangul Syllable-B784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b787 Hangul Syllable-B787 | b786 Hangul Syllable-B786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b789 Hangul Syllable-B789 | b788 Hangul Syllable-B788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b78b Hangul Syllable-B78B | b78a Hangul Syllable-B78A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b78d Hangul Syllable-B78D | b78c Hangul Syllable-B78C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b78f Hangul Syllable-B78F | b78e Hangul Syllable-B78E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b791 Hangul Syllable-B791 | b790 Hangul Syllable-B790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b793 Hangul Syllable-B793 | b792 Hangul Syllable-B792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b795 Hangul Syllable-B795 | b794 Hangul Syllable-B794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b797 Hangul Syllable-B797 | b796 Hangul Syllable-B796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b799 Hangul Syllable-B799 | b798 Hangul Syllable-B798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b79b Hangul Syllable-B79B | b79a Hangul Syllable-B79A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b79d Hangul Syllable-B79D | b79c Hangul Syllable-B79C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b79f Hangul Syllable-B79F | b79e Hangul Syllable-B79E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7a1 Hangul Syllable-B7A1 | b7a0 Hangul Syllable-B7A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7a3 Hangul Syllable-B7A3 | b7a2 Hangul Syllable-B7A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7a5 Hangul Syllable-B7A5 | b7a4 Hangul Syllable-B7A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7a7 Hangul Syllable-B7A7 | b7a6 Hangul Syllable-B7A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7a9 Hangul Syllable-B7A9 | b7a8 Hangul Syllable-B7A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7ab Hangul Syllable-B7AB | b7aa Hangul Syllable-B7AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7ad Hangul Syllable-B7AD | b7ac Hangul Syllable-B7AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7af Hangul Syllable-B7AF | b7ae Hangul Syllable-B7AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7b1 Hangul Syllable-B7B1 | b7b0 Hangul Syllable-B7B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7b3 Hangul Syllable-B7B3 | b7b2 Hangul Syllable-B7B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7b5 Hangul Syllable-B7B5 | b7b4 Hangul Syllable-B7B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7b7 Hangul Syllable-B7B7 | b7b6 Hangul Syllable-B7B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7b9 Hangul Syllable-B7B9 | b7b8 Hangul Syllable-B7B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7bb Hangul Syllable-B7BB | b7ba Hangul Syllable-B7BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7bd Hangul Syllable-B7BD | b7bc Hangul Syllable-B7BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7bf Hangul Syllable-B7BF | b7be Hangul Syllable-B7BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7c1 Hangul Syllable-B7C1 | b7c0 Hangul Syllable-B7C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7c3 Hangul Syllable-B7C3 | b7c2 Hangul Syllable-B7C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7c5 Hangul Syllable-B7C5 | b7c4 Hangul Syllable-B7C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7c7 Hangul Syllable-B7C7 | b7c6 Hangul Syllable-B7C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7c9 Hangul Syllable-B7C9 | b7c8 Hangul Syllable-B7C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7cb Hangul Syllable-B7CB | b7ca Hangul Syllable-B7CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7cd Hangul Syllable-B7CD | b7cc Hangul Syllable-B7CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7cf Hangul Syllable-B7CF | b7ce Hangul Syllable-B7CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7d1 Hangul Syllable-B7D1 | b7d0 Hangul Syllable-B7D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7d3 Hangul Syllable-B7D3 | b7d2 Hangul Syllable-B7D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7d5 Hangul Syllable-B7D5 | b7d4 Hangul Syllable-B7D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7d7 Hangul Syllable-B7D7 | b7d6 Hangul Syllable-B7D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7d9 Hangul Syllable-B7D9 | b7d8 Hangul Syllable-B7D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7db Hangul Syllable-B7DB | b7da Hangul Syllable-B7DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7dd Hangul Syllable-B7DD | b7dc Hangul Syllable-B7DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7df Hangul Syllable-B7DF | b7de Hangul Syllable-B7DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7e1 Hangul Syllable-B7E1 | b7e0 Hangul Syllable-B7E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7e3 Hangul Syllable-B7E3 | b7e2 Hangul Syllable-B7E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7e5 Hangul Syllable-B7E5 | b7e4 Hangul Syllable-B7E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7e7 Hangul Syllable-B7E7 | b7e6 Hangul Syllable-B7E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7e9 Hangul Syllable-B7E9 | b7e8 Hangul Syllable-B7E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7eb Hangul Syllable-B7EB | b7ea Hangul Syllable-B7EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7ed Hangul Syllable-B7ED | b7ec Hangul Syllable-B7EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7ef Hangul Syllable-B7EF | b7ee Hangul Syllable-B7EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7f1 Hangul Syllable-B7F1 | b7f0 Hangul Syllable-B7F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7f3 Hangul Syllable-B7F3 | b7f2 Hangul Syllable-B7F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7f5 Hangul Syllable-B7F5 | b7f4 Hangul Syllable-B7F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7f7 Hangul Syllable-B7F7 | b7f6 Hangul Syllable-B7F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7f9 Hangul Syllable-B7F9 | b7f8 Hangul Syllable-B7F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7fb Hangul Syllable-B7FB | b7fa Hangul Syllable-B7FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7fd Hangul Syllable-B7FD | b7fc Hangul Syllable-B7FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7ff Hangul Syllable-B7FF | b7fe Hangul Syllable-B7FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b801 Hangul Syllable-B801 | b800 Hangul Syllable-B800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b803 Hangul Syllable-B803 | b802 Hangul Syllable-B802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b805 Hangul Syllable-B805 | b804 Hangul Syllable-B804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b807 Hangul Syllable-B807 | b806 Hangul Syllable-B806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b809 Hangul Syllable-B809 | b808 Hangul Syllable-B808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b80b Hangul Syllable-B80B | b80a Hangul Syllable-B80A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b80d Hangul Syllable-B80D | b80c Hangul Syllable-B80C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b80f Hangul Syllable-B80F | b80e Hangul Syllable-B80E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b811 Hangul Syllable-B811 | b810 Hangul Syllable-B810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b813 Hangul Syllable-B813 | b812 Hangul Syllable-B812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b815 Hangul Syllable-B815 | b814 Hangul Syllable-B814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b817 Hangul Syllable-B817 | b816 Hangul Syllable-B816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b819 Hangul Syllable-B819 | b818 Hangul Syllable-B818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b81b Hangul Syllable-B81B | b81a Hangul Syllable-B81A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b81d Hangul Syllable-B81D | b81c Hangul Syllable-B81C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b81f Hangul Syllable-B81F | b81e Hangul Syllable-B81E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b821 Hangul Syllable-B821 | b820 Hangul Syllable-B820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b823 Hangul Syllable-B823 | b822 Hangul Syllable-B822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b825 Hangul Syllable-B825 | b824 Hangul Syllable-B824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b827 Hangul Syllable-B827 | b826 Hangul Syllable-B826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b829 Hangul Syllable-B829 | b828 Hangul Syllable-B828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b82b Hangul Syllable-B82B | b82a Hangul Syllable-B82A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b82d Hangul Syllable-B82D | b82c Hangul Syllable-B82C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b82f Hangul Syllable-B82F | b82e Hangul Syllable-B82E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b831 Hangul Syllable-B831 | b830 Hangul Syllable-B830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b833 Hangul Syllable-B833 | b832 Hangul Syllable-B832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b835 Hangul Syllable-B835 | b834 Hangul Syllable-B834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b837 Hangul Syllable-B837 | b836 Hangul Syllable-B836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b839 Hangul Syllable-B839 | b838 Hangul Syllable-B838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b83b Hangul Syllable-B83B | b83a Hangul Syllable-B83A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b83d Hangul Syllable-B83D | b83c Hangul Syllable-B83C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b83f Hangul Syllable-B83F | b83e Hangul Syllable-B83E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b841 Hangul Syllable-B841 | b840 Hangul Syllable-B840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b843 Hangul Syllable-B843 | b842 Hangul Syllable-B842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b845 Hangul Syllable-B845 | b844 Hangul Syllable-B844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b847 Hangul Syllable-B847 | b846 Hangul Syllable-B846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b849 Hangul Syllable-B849 | b848 Hangul Syllable-B848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b84b Hangul Syllable-B84B | b84a Hangul Syllable-B84A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b84d Hangul Syllable-B84D | b84c Hangul Syllable-B84C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b84f Hangul Syllable-B84F | b84e Hangul Syllable-B84E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b851 Hangul Syllable-B851 | b850 Hangul Syllable-B850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b853 Hangul Syllable-B853 | b852 Hangul Syllable-B852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b855 Hangul Syllable-B855 | b854 Hangul Syllable-B854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b857 Hangul Syllable-B857 | b856 Hangul Syllable-B856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b859 Hangul Syllable-B859 | b858 Hangul Syllable-B858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b85b Hangul Syllable-B85B | b85a Hangul Syllable-B85A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b85d Hangul Syllable-B85D | b85c Hangul Syllable-B85C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b85f Hangul Syllable-B85F | b85e Hangul Syllable-B85E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b861 Hangul Syllable-B861 | b860 Hangul Syllable-B860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b863 Hangul Syllable-B863 | b862 Hangul Syllable-B862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b865 Hangul Syllable-B865 | b864 Hangul Syllable-B864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b867 Hangul Syllable-B867 | b866 Hangul Syllable-B866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b869 Hangul Syllable-B869 | b868 Hangul Syllable-B868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b86b Hangul Syllable-B86B | b86a Hangul Syllable-B86A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b86d Hangul Syllable-B86D | b86c Hangul Syllable-B86C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b86f Hangul Syllable-B86F | b86e Hangul Syllable-B86E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b871 Hangul Syllable-B871 | b870 Hangul Syllable-B870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b873 Hangul Syllable-B873 | b872 Hangul Syllable-B872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b875 Hangul Syllable-B875 | b874 Hangul Syllable-B874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b877 Hangul Syllable-B877 | b876 Hangul Syllable-B876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b879 Hangul Syllable-B879 | b878 Hangul Syllable-B878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b87b Hangul Syllable-B87B | b87a Hangul Syllable-B87A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b87d Hangul Syllable-B87D | b87c Hangul Syllable-B87C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b87f Hangul Syllable-B87F | b87e Hangul Syllable-B87E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b881 Hangul Syllable-B881 | b880 Hangul Syllable-B880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b883 Hangul Syllable-B883 | b882 Hangul Syllable-B882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b885 Hangul Syllable-B885 | b884 Hangul Syllable-B884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b887 Hangul Syllable-B887 | b886 Hangul Syllable-B886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b889 Hangul Syllable-B889 | b888 Hangul Syllable-B888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b88b Hangul Syllable-B88B | b88a Hangul Syllable-B88A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b88d Hangul Syllable-B88D | b88c Hangul Syllable-B88C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b88f Hangul Syllable-B88F | b88e Hangul Syllable-B88E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b891 Hangul Syllable-B891 | b890 Hangul Syllable-B890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b893 Hangul Syllable-B893 | b892 Hangul Syllable-B892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b895 Hangul Syllable-B895 | b894 Hangul Syllable-B894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b897 Hangul Syllable-B897 | b896 Hangul Syllable-B896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b899 Hangul Syllable-B899 | b898 Hangul Syllable-B898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b89b Hangul Syllable-B89B | b89a Hangul Syllable-B89A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b89d Hangul Syllable-B89D | b89c Hangul Syllable-B89C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b89f Hangul Syllable-B89F | b89e Hangul Syllable-B89E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8a1 Hangul Syllable-B8A1 | b8a0 Hangul Syllable-B8A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8a3 Hangul Syllable-B8A3 | b8a2 Hangul Syllable-B8A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8a5 Hangul Syllable-B8A5 | b8a4 Hangul Syllable-B8A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8a7 Hangul Syllable-B8A7 | b8a6 Hangul Syllable-B8A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8a9 Hangul Syllable-B8A9 | b8a8 Hangul Syllable-B8A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8ab Hangul Syllable-B8AB | b8aa Hangul Syllable-B8AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8ad Hangul Syllable-B8AD | b8ac Hangul Syllable-B8AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8af Hangul Syllable-B8AF | b8ae Hangul Syllable-B8AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8b1 Hangul Syllable-B8B1 | b8b0 Hangul Syllable-B8B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8b3 Hangul Syllable-B8B3 | b8b2 Hangul Syllable-B8B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8b5 Hangul Syllable-B8B5 | b8b4 Hangul Syllable-B8B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8b7 Hangul Syllable-B8B7 | b8b6 Hangul Syllable-B8B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8b9 Hangul Syllable-B8B9 | b8b8 Hangul Syllable-B8B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8bb Hangul Syllable-B8BB | b8ba Hangul Syllable-B8BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8bd Hangul Syllable-B8BD | b8bc Hangul Syllable-B8BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8bf Hangul Syllable-B8BF | b8be Hangul Syllable-B8BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8c1 Hangul Syllable-B8C1 | b8c0 Hangul Syllable-B8C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8c3 Hangul Syllable-B8C3 | b8c2 Hangul Syllable-B8C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8c5 Hangul Syllable-B8C5 | b8c4 Hangul Syllable-B8C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8c7 Hangul Syllable-B8C7 | b8c6 Hangul Syllable-B8C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8c9 Hangul Syllable-B8C9 | b8c8 Hangul Syllable-B8C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8cb Hangul Syllable-B8CB | b8ca Hangul Syllable-B8CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8cd Hangul Syllable-B8CD | b8cc Hangul Syllable-B8CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8cf Hangul Syllable-B8CF | b8ce Hangul Syllable-B8CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8d1 Hangul Syllable-B8D1 | b8d0 Hangul Syllable-B8D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8d3 Hangul Syllable-B8D3 | b8d2 Hangul Syllable-B8D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8d5 Hangul Syllable-B8D5 | b8d4 Hangul Syllable-B8D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8d7 Hangul Syllable-B8D7 | b8d6 Hangul Syllable-B8D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8d9 Hangul Syllable-B8D9 | b8d8 Hangul Syllable-B8D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8db Hangul Syllable-B8DB | b8da Hangul Syllable-B8DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8dd Hangul Syllable-B8DD | b8dc Hangul Syllable-B8DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8df Hangul Syllable-B8DF | b8de Hangul Syllable-B8DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8e1 Hangul Syllable-B8E1 | b8e0 Hangul Syllable-B8E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8e3 Hangul Syllable-B8E3 | b8e2 Hangul Syllable-B8E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8e5 Hangul Syllable-B8E5 | b8e4 Hangul Syllable-B8E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8e7 Hangul Syllable-B8E7 | b8e6 Hangul Syllable-B8E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8e9 Hangul Syllable-B8E9 | b8e8 Hangul Syllable-B8E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8eb Hangul Syllable-B8EB | b8ea Hangul Syllable-B8EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8ed Hangul Syllable-B8ED | b8ec Hangul Syllable-B8EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8ef Hangul Syllable-B8EF | b8ee Hangul Syllable-B8EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8f1 Hangul Syllable-B8F1 | b8f0 Hangul Syllable-B8F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8f3 Hangul Syllable-B8F3 | b8f2 Hangul Syllable-B8F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8f5 Hangul Syllable-B8F5 | b8f4 Hangul Syllable-B8F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8f7 Hangul Syllable-B8F7 | b8f6 Hangul Syllable-B8F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8f9 Hangul Syllable-B8F9 | b8f8 Hangul Syllable-B8F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8fb Hangul Syllable-B8FB | b8fa Hangul Syllable-B8FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8fd Hangul Syllable-B8FD | b8fc Hangul Syllable-B8FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8ff Hangul Syllable-B8FF | b8fe Hangul Syllable-B8FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b901 Hangul Syllable-B901 | b900 Hangul Syllable-B900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b903 Hangul Syllable-B903 | b902 Hangul Syllable-B902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b905 Hangul Syllable-B905 | b904 Hangul Syllable-B904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b907 Hangul Syllable-B907 | b906 Hangul Syllable-B906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b909 Hangul Syllable-B909 | b908 Hangul Syllable-B908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b90b Hangul Syllable-B90B | b90a Hangul Syllable-B90A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b90d Hangul Syllable-B90D | b90c Hangul Syllable-B90C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b90f Hangul Syllable-B90F | b90e Hangul Syllable-B90E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b911 Hangul Syllable-B911 | b910 Hangul Syllable-B910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b913 Hangul Syllable-B913 | b912 Hangul Syllable-B912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b915 Hangul Syllable-B915 | b914 Hangul Syllable-B914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b917 Hangul Syllable-B917 | b916 Hangul Syllable-B916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b919 Hangul Syllable-B919 | b918 Hangul Syllable-B918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b91b Hangul Syllable-B91B | b91a Hangul Syllable-B91A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b91d Hangul Syllable-B91D | b91c Hangul Syllable-B91C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b91f Hangul Syllable-B91F | b91e Hangul Syllable-B91E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b921 Hangul Syllable-B921 | b920 Hangul Syllable-B920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b923 Hangul Syllable-B923 | b922 Hangul Syllable-B922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b925 Hangul Syllable-B925 | b924 Hangul Syllable-B924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b927 Hangul Syllable-B927 | b926 Hangul Syllable-B926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b929 Hangul Syllable-B929 | b928 Hangul Syllable-B928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b92b Hangul Syllable-B92B | b92a Hangul Syllable-B92A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b92d Hangul Syllable-B92D | b92c Hangul Syllable-B92C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b92f Hangul Syllable-B92F | b92e Hangul Syllable-B92E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b931 Hangul Syllable-B931 | b930 Hangul Syllable-B930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b933 Hangul Syllable-B933 | b932 Hangul Syllable-B932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b935 Hangul Syllable-B935 | b934 Hangul Syllable-B934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b937 Hangul Syllable-B937 | b936 Hangul Syllable-B936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b939 Hangul Syllable-B939 | b938 Hangul Syllable-B938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b93b Hangul Syllable-B93B | b93a Hangul Syllable-B93A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b93d Hangul Syllable-B93D | b93c Hangul Syllable-B93C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b93f Hangul Syllable-B93F | b93e Hangul Syllable-B93E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b941 Hangul Syllable-B941 | b940 Hangul Syllable-B940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b943 Hangul Syllable-B943 | b942 Hangul Syllable-B942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b945 Hangul Syllable-B945 | b944 Hangul Syllable-B944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b947 Hangul Syllable-B947 | b946 Hangul Syllable-B946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b949 Hangul Syllable-B949 | b948 Hangul Syllable-B948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b94b Hangul Syllable-B94B | b94a Hangul Syllable-B94A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b94d Hangul Syllable-B94D | b94c Hangul Syllable-B94C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b94f Hangul Syllable-B94F | b94e Hangul Syllable-B94E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b951 Hangul Syllable-B951 | b950 Hangul Syllable-B950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b953 Hangul Syllable-B953 | b952 Hangul Syllable-B952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b955 Hangul Syllable-B955 | b954 Hangul Syllable-B954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b957 Hangul Syllable-B957 | b956 Hangul Syllable-B956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b959 Hangul Syllable-B959 | b958 Hangul Syllable-B958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b95b Hangul Syllable-B95B | b95a Hangul Syllable-B95A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b95d Hangul Syllable-B95D | b95c Hangul Syllable-B95C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b95f Hangul Syllable-B95F | b95e Hangul Syllable-B95E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b961 Hangul Syllable-B961 | b960 Hangul Syllable-B960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b963 Hangul Syllable-B963 | b962 Hangul Syllable-B962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b965 Hangul Syllable-B965 | b964 Hangul Syllable-B964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b967 Hangul Syllable-B967 | b966 Hangul Syllable-B966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b969 Hangul Syllable-B969 | b968 Hangul Syllable-B968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b96b Hangul Syllable-B96B | b96a Hangul Syllable-B96A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b96d Hangul Syllable-B96D | b96c Hangul Syllable-B96C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b96f Hangul Syllable-B96F | b96e Hangul Syllable-B96E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b971 Hangul Syllable-B971 | b970 Hangul Syllable-B970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b973 Hangul Syllable-B973 | b972 Hangul Syllable-B972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b975 Hangul Syllable-B975 | b974 Hangul Syllable-B974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b977 Hangul Syllable-B977 | b976 Hangul Syllable-B976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b979 Hangul Syllable-B979 | b978 Hangul Syllable-B978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b97b Hangul Syllable-B97B | b97a Hangul Syllable-B97A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b97d Hangul Syllable-B97D | b97c Hangul Syllable-B97C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b97f Hangul Syllable-B97F | b97e Hangul Syllable-B97E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b981 Hangul Syllable-B981 | b980 Hangul Syllable-B980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b983 Hangul Syllable-B983 | b982 Hangul Syllable-B982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b985 Hangul Syllable-B985 | b984 Hangul Syllable-B984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b987 Hangul Syllable-B987 | b986 Hangul Syllable-B986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b989 Hangul Syllable-B989 | b988 Hangul Syllable-B988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b98b Hangul Syllable-B98B | b98a Hangul Syllable-B98A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b98d Hangul Syllable-B98D | b98c Hangul Syllable-B98C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b98f Hangul Syllable-B98F | b98e Hangul Syllable-B98E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b991 Hangul Syllable-B991 | b990 Hangul Syllable-B990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b993 Hangul Syllable-B993 | b992 Hangul Syllable-B992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b995 Hangul Syllable-B995 | b994 Hangul Syllable-B994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b997 Hangul Syllable-B997 | b996 Hangul Syllable-B996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b999 Hangul Syllable-B999 | b998 Hangul Syllable-B998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b99b Hangul Syllable-B99B | b99a Hangul Syllable-B99A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b99d Hangul Syllable-B99D | b99c Hangul Syllable-B99C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b99f Hangul Syllable-B99F | b99e Hangul Syllable-B99E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9a1 Hangul Syllable-B9A1 | b9a0 Hangul Syllable-B9A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9a3 Hangul Syllable-B9A3 | b9a2 Hangul Syllable-B9A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9a5 Hangul Syllable-B9A5 | b9a4 Hangul Syllable-B9A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9a7 Hangul Syllable-B9A7 | b9a6 Hangul Syllable-B9A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9a9 Hangul Syllable-B9A9 | b9a8 Hangul Syllable-B9A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9ab Hangul Syllable-B9AB | b9aa Hangul Syllable-B9AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9ad Hangul Syllable-B9AD | b9ac Hangul Syllable-B9AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9af Hangul Syllable-B9AF | b9ae Hangul Syllable-B9AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9b1 Hangul Syllable-B9B1 | b9b0 Hangul Syllable-B9B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9b3 Hangul Syllable-B9B3 | b9b2 Hangul Syllable-B9B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9b5 Hangul Syllable-B9B5 | b9b4 Hangul Syllable-B9B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9b7 Hangul Syllable-B9B7 | b9b6 Hangul Syllable-B9B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9b9 Hangul Syllable-B9B9 | b9b8 Hangul Syllable-B9B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9bb Hangul Syllable-B9BB | b9ba Hangul Syllable-B9BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9bd Hangul Syllable-B9BD | b9bc Hangul Syllable-B9BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9bf Hangul Syllable-B9BF | b9be Hangul Syllable-B9BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9c1 Hangul Syllable-B9C1 | b9c0 Hangul Syllable-B9C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9c3 Hangul Syllable-B9C3 | b9c2 Hangul Syllable-B9C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9c5 Hangul Syllable-B9C5 | b9c4 Hangul Syllable-B9C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9c7 Hangul Syllable-B9C7 | b9c6 Hangul Syllable-B9C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9c9 Hangul Syllable-B9C9 | b9c8 Hangul Syllable-B9C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9cb Hangul Syllable-B9CB | b9ca Hangul Syllable-B9CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9cd Hangul Syllable-B9CD | b9cc Hangul Syllable-B9CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9cf Hangul Syllable-B9CF | b9ce Hangul Syllable-B9CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9d1 Hangul Syllable-B9D1 | b9d0 Hangul Syllable-B9D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9d3 Hangul Syllable-B9D3 | b9d2 Hangul Syllable-B9D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9d5 Hangul Syllable-B9D5 | b9d4 Hangul Syllable-B9D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9d7 Hangul Syllable-B9D7 | b9d6 Hangul Syllable-B9D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9d9 Hangul Syllable-B9D9 | b9d8 Hangul Syllable-B9D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9db Hangul Syllable-B9DB | b9da Hangul Syllable-B9DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9dd Hangul Syllable-B9DD | b9dc Hangul Syllable-B9DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9df Hangul Syllable-B9DF | b9de Hangul Syllable-B9DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9e1 Hangul Syllable-B9E1 | b9e0 Hangul Syllable-B9E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9e3 Hangul Syllable-B9E3 | b9e2 Hangul Syllable-B9E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9e5 Hangul Syllable-B9E5 | b9e4 Hangul Syllable-B9E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9e7 Hangul Syllable-B9E7 | b9e6 Hangul Syllable-B9E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9e9 Hangul Syllable-B9E9 | b9e8 Hangul Syllable-B9E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9eb Hangul Syllable-B9EB | b9ea Hangul Syllable-B9EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9ed Hangul Syllable-B9ED | b9ec Hangul Syllable-B9EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9ef Hangul Syllable-B9EF | b9ee Hangul Syllable-B9EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9f1 Hangul Syllable-B9F1 | b9f0 Hangul Syllable-B9F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9f3 Hangul Syllable-B9F3 | b9f2 Hangul Syllable-B9F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9f5 Hangul Syllable-B9F5 | b9f4 Hangul Syllable-B9F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9f7 Hangul Syllable-B9F7 | b9f6 Hangul Syllable-B9F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9f9 Hangul Syllable-B9F9 | b9f8 Hangul Syllable-B9F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9fb Hangul Syllable-B9FB | b9fa Hangul Syllable-B9FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9fd Hangul Syllable-B9FD | b9fc Hangul Syllable-B9FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9ff Hangul Syllable-B9FF | b9fe Hangul Syllable-B9FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba01 Hangul Syllable-BA01 | ba00 Hangul Syllable-BA00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba03 Hangul Syllable-BA03 | ba02 Hangul Syllable-BA02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba05 Hangul Syllable-BA05 | ba04 Hangul Syllable-BA04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba07 Hangul Syllable-BA07 | ba06 Hangul Syllable-BA06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba09 Hangul Syllable-BA09 | ba08 Hangul Syllable-BA08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba0b Hangul Syllable-BA0B | ba0a Hangul Syllable-BA0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba0d Hangul Syllable-BA0D | ba0c Hangul Syllable-BA0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba0f Hangul Syllable-BA0F | ba0e Hangul Syllable-BA0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba11 Hangul Syllable-BA11 | ba10 Hangul Syllable-BA10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba13 Hangul Syllable-BA13 | ba12 Hangul Syllable-BA12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba15 Hangul Syllable-BA15 | ba14 Hangul Syllable-BA14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba17 Hangul Syllable-BA17 | ba16 Hangul Syllable-BA16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba19 Hangul Syllable-BA19 | ba18 Hangul Syllable-BA18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba1b Hangul Syllable-BA1B | ba1a Hangul Syllable-BA1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba1d Hangul Syllable-BA1D | ba1c Hangul Syllable-BA1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba1f Hangul Syllable-BA1F | ba1e Hangul Syllable-BA1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba21 Hangul Syllable-BA21 | ba20 Hangul Syllable-BA20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba23 Hangul Syllable-BA23 | ba22 Hangul Syllable-BA22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba25 Hangul Syllable-BA25 | ba24 Hangul Syllable-BA24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba27 Hangul Syllable-BA27 | ba26 Hangul Syllable-BA26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba29 Hangul Syllable-BA29 | ba28 Hangul Syllable-BA28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba2b Hangul Syllable-BA2B | ba2a Hangul Syllable-BA2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba2d Hangul Syllable-BA2D | ba2c Hangul Syllable-BA2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba2f Hangul Syllable-BA2F | ba2e Hangul Syllable-BA2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba31 Hangul Syllable-BA31 | ba30 Hangul Syllable-BA30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba33 Hangul Syllable-BA33 | ba32 Hangul Syllable-BA32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba35 Hangul Syllable-BA35 | ba34 Hangul Syllable-BA34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba37 Hangul Syllable-BA37 | ba36 Hangul Syllable-BA36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba39 Hangul Syllable-BA39 | ba38 Hangul Syllable-BA38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba3b Hangul Syllable-BA3B | ba3a Hangul Syllable-BA3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba3d Hangul Syllable-BA3D | ba3c Hangul Syllable-BA3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba3f Hangul Syllable-BA3F | ba3e Hangul Syllable-BA3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba41 Hangul Syllable-BA41 | ba40 Hangul Syllable-BA40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba43 Hangul Syllable-BA43 | ba42 Hangul Syllable-BA42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba45 Hangul Syllable-BA45 | ba44 Hangul Syllable-BA44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba47 Hangul Syllable-BA47 | ba46 Hangul Syllable-BA46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba49 Hangul Syllable-BA49 | ba48 Hangul Syllable-BA48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba4b Hangul Syllable-BA4B | ba4a Hangul Syllable-BA4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba4d Hangul Syllable-BA4D | ba4c Hangul Syllable-BA4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba4f Hangul Syllable-BA4F | ba4e Hangul Syllable-BA4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba51 Hangul Syllable-BA51 | ba50 Hangul Syllable-BA50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba53 Hangul Syllable-BA53 | ba52 Hangul Syllable-BA52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba55 Hangul Syllable-BA55 | ba54 Hangul Syllable-BA54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba57 Hangul Syllable-BA57 | ba56 Hangul Syllable-BA56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba59 Hangul Syllable-BA59 | ba58 Hangul Syllable-BA58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba5b Hangul Syllable-BA5B | ba5a Hangul Syllable-BA5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba5d Hangul Syllable-BA5D | ba5c Hangul Syllable-BA5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba5f Hangul Syllable-BA5F | ba5e Hangul Syllable-BA5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba61 Hangul Syllable-BA61 | ba60 Hangul Syllable-BA60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba63 Hangul Syllable-BA63 | ba62 Hangul Syllable-BA62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba65 Hangul Syllable-BA65 | ba64 Hangul Syllable-BA64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba67 Hangul Syllable-BA67 | ba66 Hangul Syllable-BA66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba69 Hangul Syllable-BA69 | ba68 Hangul Syllable-BA68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba6b Hangul Syllable-BA6B | ba6a Hangul Syllable-BA6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba6d Hangul Syllable-BA6D | ba6c Hangul Syllable-BA6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba6f Hangul Syllable-BA6F | ba6e Hangul Syllable-BA6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba71 Hangul Syllable-BA71 | ba70 Hangul Syllable-BA70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba73 Hangul Syllable-BA73 | ba72 Hangul Syllable-BA72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba75 Hangul Syllable-BA75 | ba74 Hangul Syllable-BA74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba77 Hangul Syllable-BA77 | ba76 Hangul Syllable-BA76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba79 Hangul Syllable-BA79 | ba78 Hangul Syllable-BA78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba7b Hangul Syllable-BA7B | ba7a Hangul Syllable-BA7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba7d Hangul Syllable-BA7D | ba7c Hangul Syllable-BA7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba7f Hangul Syllable-BA7F | ba7e Hangul Syllable-BA7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba81 Hangul Syllable-BA81 | ba80 Hangul Syllable-BA80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba83 Hangul Syllable-BA83 | ba82 Hangul Syllable-BA82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba85 Hangul Syllable-BA85 | ba84 Hangul Syllable-BA84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba87 Hangul Syllable-BA87 | ba86 Hangul Syllable-BA86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba89 Hangul Syllable-BA89 | ba88 Hangul Syllable-BA88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba8b Hangul Syllable-BA8B | ba8a Hangul Syllable-BA8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba8d Hangul Syllable-BA8D | ba8c Hangul Syllable-BA8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba8f Hangul Syllable-BA8F | ba8e Hangul Syllable-BA8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba91 Hangul Syllable-BA91 | ba90 Hangul Syllable-BA90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba93 Hangul Syllable-BA93 | ba92 Hangul Syllable-BA92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba95 Hangul Syllable-BA95 | ba94 Hangul Syllable-BA94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba97 Hangul Syllable-BA97 | ba96 Hangul Syllable-BA96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba99 Hangul Syllable-BA99 | ba98 Hangul Syllable-BA98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba9b Hangul Syllable-BA9B | ba9a Hangul Syllable-BA9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba9d Hangul Syllable-BA9D | ba9c Hangul Syllable-BA9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba9f Hangul Syllable-BA9F | ba9e Hangul Syllable-BA9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baa1 Hangul Syllable-BAA1 | baa0 Hangul Syllable-BAA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baa3 Hangul Syllable-BAA3 | baa2 Hangul Syllable-BAA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baa5 Hangul Syllable-BAA5 | baa4 Hangul Syllable-BAA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baa7 Hangul Syllable-BAA7 | baa6 Hangul Syllable-BAA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baa9 Hangul Syllable-BAA9 | baa8 Hangul Syllable-BAA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baab Hangul Syllable-BAAB | baaa Hangul Syllable-BAAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baad Hangul Syllable-BAAD | baac Hangul Syllable-BAAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baaf Hangul Syllable-BAAF | baae Hangul Syllable-BAAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bab1 Hangul Syllable-BAB1 | bab0 Hangul Syllable-BAB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bab3 Hangul Syllable-BAB3 | bab2 Hangul Syllable-BAB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bab5 Hangul Syllable-BAB5 | bab4 Hangul Syllable-BAB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bab7 Hangul Syllable-BAB7 | bab6 Hangul Syllable-BAB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bab9 Hangul Syllable-BAB9 | bab8 Hangul Syllable-BAB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* babb Hangul Syllable-BABB | baba Hangul Syllable-BABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* babd Hangul Syllable-BABD | babc Hangul Syllable-BABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* babf Hangul Syllable-BABF | babe Hangul Syllable-BABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bac1 Hangul Syllable-BAC1 | bac0 Hangul Syllable-BAC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bac3 Hangul Syllable-BAC3 | bac2 Hangul Syllable-BAC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bac5 Hangul Syllable-BAC5 | bac4 Hangul Syllable-BAC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bac7 Hangul Syllable-BAC7 | bac6 Hangul Syllable-BAC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bac9 Hangul Syllable-BAC9 | bac8 Hangul Syllable-BAC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bacb Hangul Syllable-BACB | baca Hangul Syllable-BACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bacd Hangul Syllable-BACD | bacc Hangul Syllable-BACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bacf Hangul Syllable-BACF | bace Hangul Syllable-BACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bad1 Hangul Syllable-BAD1 | bad0 Hangul Syllable-BAD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bad3 Hangul Syllable-BAD3 | bad2 Hangul Syllable-BAD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bad5 Hangul Syllable-BAD5 | bad4 Hangul Syllable-BAD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bad7 Hangul Syllable-BAD7 | bad6 Hangul Syllable-BAD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bad9 Hangul Syllable-BAD9 | bad8 Hangul Syllable-BAD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* badb Hangul Syllable-BADB | bada Hangul Syllable-BADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* badd Hangul Syllable-BADD | badc Hangul Syllable-BADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* badf Hangul Syllable-BADF | bade Hangul Syllable-BADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bae1 Hangul Syllable-BAE1 | bae0 Hangul Syllable-BAE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bae3 Hangul Syllable-BAE3 | bae2 Hangul Syllable-BAE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bae5 Hangul Syllable-BAE5 | bae4 Hangul Syllable-BAE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bae7 Hangul Syllable-BAE7 | bae6 Hangul Syllable-BAE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bae9 Hangul Syllable-BAE9 | bae8 Hangul Syllable-BAE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baeb Hangul Syllable-BAEB | baea Hangul Syllable-BAEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baed Hangul Syllable-BAED | baec Hangul Syllable-BAEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baef Hangul Syllable-BAEF | baee Hangul Syllable-BAEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baf1 Hangul Syllable-BAF1 | baf0 Hangul Syllable-BAF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baf3 Hangul Syllable-BAF3 | baf2 Hangul Syllable-BAF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baf5 Hangul Syllable-BAF5 | baf4 Hangul Syllable-BAF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baf7 Hangul Syllable-BAF7 | baf6 Hangul Syllable-BAF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baf9 Hangul Syllable-BAF9 | baf8 Hangul Syllable-BAF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bafb Hangul Syllable-BAFB | bafa Hangul Syllable-BAFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bafd Hangul Syllable-BAFD | bafc Hangul Syllable-BAFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baff Hangul Syllable-BAFF | bafe Hangul Syllable-BAFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb01 Hangul Syllable-BB01 | bb00 Hangul Syllable-BB00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb03 Hangul Syllable-BB03 | bb02 Hangul Syllable-BB02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb05 Hangul Syllable-BB05 | bb04 Hangul Syllable-BB04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb07 Hangul Syllable-BB07 | bb06 Hangul Syllable-BB06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb09 Hangul Syllable-BB09 | bb08 Hangul Syllable-BB08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb0b Hangul Syllable-BB0B | bb0a Hangul Syllable-BB0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb0d Hangul Syllable-BB0D | bb0c Hangul Syllable-BB0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb0f Hangul Syllable-BB0F | bb0e Hangul Syllable-BB0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb11 Hangul Syllable-BB11 | bb10 Hangul Syllable-BB10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb13 Hangul Syllable-BB13 | bb12 Hangul Syllable-BB12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb15 Hangul Syllable-BB15 | bb14 Hangul Syllable-BB14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb17 Hangul Syllable-BB17 | bb16 Hangul Syllable-BB16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb19 Hangul Syllable-BB19 | bb18 Hangul Syllable-BB18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb1b Hangul Syllable-BB1B | bb1a Hangul Syllable-BB1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb1d Hangul Syllable-BB1D | bb1c Hangul Syllable-BB1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb1f Hangul Syllable-BB1F | bb1e Hangul Syllable-BB1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb21 Hangul Syllable-BB21 | bb20 Hangul Syllable-BB20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb23 Hangul Syllable-BB23 | bb22 Hangul Syllable-BB22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb25 Hangul Syllable-BB25 | bb24 Hangul Syllable-BB24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb27 Hangul Syllable-BB27 | bb26 Hangul Syllable-BB26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb29 Hangul Syllable-BB29 | bb28 Hangul Syllable-BB28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb2b Hangul Syllable-BB2B | bb2a Hangul Syllable-BB2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb2d Hangul Syllable-BB2D | bb2c Hangul Syllable-BB2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb2f Hangul Syllable-BB2F | bb2e Hangul Syllable-BB2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb31 Hangul Syllable-BB31 | bb30 Hangul Syllable-BB30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb33 Hangul Syllable-BB33 | bb32 Hangul Syllable-BB32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb35 Hangul Syllable-BB35 | bb34 Hangul Syllable-BB34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb37 Hangul Syllable-BB37 | bb36 Hangul Syllable-BB36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb39 Hangul Syllable-BB39 | bb38 Hangul Syllable-BB38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb3b Hangul Syllable-BB3B | bb3a Hangul Syllable-BB3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb3d Hangul Syllable-BB3D | bb3c Hangul Syllable-BB3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb3f Hangul Syllable-BB3F | bb3e Hangul Syllable-BB3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb41 Hangul Syllable-BB41 | bb40 Hangul Syllable-BB40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb43 Hangul Syllable-BB43 | bb42 Hangul Syllable-BB42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb45 Hangul Syllable-BB45 | bb44 Hangul Syllable-BB44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb47 Hangul Syllable-BB47 | bb46 Hangul Syllable-BB46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb49 Hangul Syllable-BB49 | bb48 Hangul Syllable-BB48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb4b Hangul Syllable-BB4B | bb4a Hangul Syllable-BB4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb4d Hangul Syllable-BB4D | bb4c Hangul Syllable-BB4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb4f Hangul Syllable-BB4F | bb4e Hangul Syllable-BB4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb51 Hangul Syllable-BB51 | bb50 Hangul Syllable-BB50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb53 Hangul Syllable-BB53 | bb52 Hangul Syllable-BB52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb55 Hangul Syllable-BB55 | bb54 Hangul Syllable-BB54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb57 Hangul Syllable-BB57 | bb56 Hangul Syllable-BB56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb59 Hangul Syllable-BB59 | bb58 Hangul Syllable-BB58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb5b Hangul Syllable-BB5B | bb5a Hangul Syllable-BB5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb5d Hangul Syllable-BB5D | bb5c Hangul Syllable-BB5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb5f Hangul Syllable-BB5F | bb5e Hangul Syllable-BB5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb61 Hangul Syllable-BB61 | bb60 Hangul Syllable-BB60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb63 Hangul Syllable-BB63 | bb62 Hangul Syllable-BB62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb65 Hangul Syllable-BB65 | bb64 Hangul Syllable-BB64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb67 Hangul Syllable-BB67 | bb66 Hangul Syllable-BB66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb69 Hangul Syllable-BB69 | bb68 Hangul Syllable-BB68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb6b Hangul Syllable-BB6B | bb6a Hangul Syllable-BB6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb6d Hangul Syllable-BB6D | bb6c Hangul Syllable-BB6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb6f Hangul Syllable-BB6F | bb6e Hangul Syllable-BB6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb71 Hangul Syllable-BB71 | bb70 Hangul Syllable-BB70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb73 Hangul Syllable-BB73 | bb72 Hangul Syllable-BB72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb75 Hangul Syllable-BB75 | bb74 Hangul Syllable-BB74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb77 Hangul Syllable-BB77 | bb76 Hangul Syllable-BB76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb79 Hangul Syllable-BB79 | bb78 Hangul Syllable-BB78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb7b Hangul Syllable-BB7B | bb7a Hangul Syllable-BB7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb7d Hangul Syllable-BB7D | bb7c Hangul Syllable-BB7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb7f Hangul Syllable-BB7F | bb7e Hangul Syllable-BB7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb81 Hangul Syllable-BB81 | bb80 Hangul Syllable-BB80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb83 Hangul Syllable-BB83 | bb82 Hangul Syllable-BB82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb85 Hangul Syllable-BB85 | bb84 Hangul Syllable-BB84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb87 Hangul Syllable-BB87 | bb86 Hangul Syllable-BB86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb89 Hangul Syllable-BB89 | bb88 Hangul Syllable-BB88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb8b Hangul Syllable-BB8B | bb8a Hangul Syllable-BB8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb8d Hangul Syllable-BB8D | bb8c Hangul Syllable-BB8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb8f Hangul Syllable-BB8F | bb8e Hangul Syllable-BB8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb91 Hangul Syllable-BB91 | bb90 Hangul Syllable-BB90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb93 Hangul Syllable-BB93 | bb92 Hangul Syllable-BB92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb95 Hangul Syllable-BB95 | bb94 Hangul Syllable-BB94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb97 Hangul Syllable-BB97 | bb96 Hangul Syllable-BB96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb99 Hangul Syllable-BB99 | bb98 Hangul Syllable-BB98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb9b Hangul Syllable-BB9B | bb9a Hangul Syllable-BB9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb9d Hangul Syllable-BB9D | bb9c Hangul Syllable-BB9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb9f Hangul Syllable-BB9F | bb9e Hangul Syllable-BB9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bba1 Hangul Syllable-BBA1 | bba0 Hangul Syllable-BBA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bba3 Hangul Syllable-BBA3 | bba2 Hangul Syllable-BBA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bba5 Hangul Syllable-BBA5 | bba4 Hangul Syllable-BBA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bba7 Hangul Syllable-BBA7 | bba6 Hangul Syllable-BBA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bba9 Hangul Syllable-BBA9 | bba8 Hangul Syllable-BBA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbab Hangul Syllable-BBAB | bbaa Hangul Syllable-BBAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbad Hangul Syllable-BBAD | bbac Hangul Syllable-BBAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbaf Hangul Syllable-BBAF | bbae Hangul Syllable-BBAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbb1 Hangul Syllable-BBB1 | bbb0 Hangul Syllable-BBB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbb3 Hangul Syllable-BBB3 | bbb2 Hangul Syllable-BBB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbb5 Hangul Syllable-BBB5 | bbb4 Hangul Syllable-BBB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbb7 Hangul Syllable-BBB7 | bbb6 Hangul Syllable-BBB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbb9 Hangul Syllable-BBB9 | bbb8 Hangul Syllable-BBB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbbb Hangul Syllable-BBBB | bbba Hangul Syllable-BBBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbbd Hangul Syllable-BBBD | bbbc Hangul Syllable-BBBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbbf Hangul Syllable-BBBF | bbbe Hangul Syllable-BBBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbc1 Hangul Syllable-BBC1 | bbc0 Hangul Syllable-BBC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbc3 Hangul Syllable-BBC3 | bbc2 Hangul Syllable-BBC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbc5 Hangul Syllable-BBC5 | bbc4 Hangul Syllable-BBC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbc7 Hangul Syllable-BBC7 | bbc6 Hangul Syllable-BBC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbc9 Hangul Syllable-BBC9 | bbc8 Hangul Syllable-BBC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbcb Hangul Syllable-BBCB | bbca Hangul Syllable-BBCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbcd Hangul Syllable-BBCD | bbcc Hangul Syllable-BBCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbcf Hangul Syllable-BBCF | bbce Hangul Syllable-BBCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbd1 Hangul Syllable-BBD1 | bbd0 Hangul Syllable-BBD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbd3 Hangul Syllable-BBD3 | bbd2 Hangul Syllable-BBD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbd5 Hangul Syllable-BBD5 | bbd4 Hangul Syllable-BBD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbd7 Hangul Syllable-BBD7 | bbd6 Hangul Syllable-BBD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbd9 Hangul Syllable-BBD9 | bbd8 Hangul Syllable-BBD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbdb Hangul Syllable-BBDB | bbda Hangul Syllable-BBDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbdd Hangul Syllable-BBDD | bbdc Hangul Syllable-BBDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbdf Hangul Syllable-BBDF | bbde Hangul Syllable-BBDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbe1 Hangul Syllable-BBE1 | bbe0 Hangul Syllable-BBE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbe3 Hangul Syllable-BBE3 | bbe2 Hangul Syllable-BBE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbe5 Hangul Syllable-BBE5 | bbe4 Hangul Syllable-BBE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbe7 Hangul Syllable-BBE7 | bbe6 Hangul Syllable-BBE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbe9 Hangul Syllable-BBE9 | bbe8 Hangul Syllable-BBE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbeb Hangul Syllable-BBEB | bbea Hangul Syllable-BBEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbed Hangul Syllable-BBED | bbec Hangul Syllable-BBEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbef Hangul Syllable-BBEF | bbee Hangul Syllable-BBEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbf1 Hangul Syllable-BBF1 | bbf0 Hangul Syllable-BBF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbf3 Hangul Syllable-BBF3 | bbf2 Hangul Syllable-BBF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbf5 Hangul Syllable-BBF5 | bbf4 Hangul Syllable-BBF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbf7 Hangul Syllable-BBF7 | bbf6 Hangul Syllable-BBF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbf9 Hangul Syllable-BBF9 | bbf8 Hangul Syllable-BBF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbfb Hangul Syllable-BBFB | bbfa Hangul Syllable-BBFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbfd Hangul Syllable-BBFD | bbfc Hangul Syllable-BBFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbff Hangul Syllable-BBFF | bbfe Hangul Syllable-BBFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc01 Hangul Syllable-BC01 | bc00 Hangul Syllable-BC00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc03 Hangul Syllable-BC03 | bc02 Hangul Syllable-BC02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc05 Hangul Syllable-BC05 | bc04 Hangul Syllable-BC04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc07 Hangul Syllable-BC07 | bc06 Hangul Syllable-BC06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc09 Hangul Syllable-BC09 | bc08 Hangul Syllable-BC08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc0b Hangul Syllable-BC0B | bc0a Hangul Syllable-BC0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc0d Hangul Syllable-BC0D | bc0c Hangul Syllable-BC0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc0f Hangul Syllable-BC0F | bc0e Hangul Syllable-BC0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc11 Hangul Syllable-BC11 | bc10 Hangul Syllable-BC10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc13 Hangul Syllable-BC13 | bc12 Hangul Syllable-BC12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc15 Hangul Syllable-BC15 | bc14 Hangul Syllable-BC14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc17 Hangul Syllable-BC17 | bc16 Hangul Syllable-BC16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc19 Hangul Syllable-BC19 | bc18 Hangul Syllable-BC18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc1b Hangul Syllable-BC1B | bc1a Hangul Syllable-BC1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc1d Hangul Syllable-BC1D | bc1c Hangul Syllable-BC1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc1f Hangul Syllable-BC1F | bc1e Hangul Syllable-BC1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc21 Hangul Syllable-BC21 | bc20 Hangul Syllable-BC20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc23 Hangul Syllable-BC23 | bc22 Hangul Syllable-BC22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc25 Hangul Syllable-BC25 | bc24 Hangul Syllable-BC24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc27 Hangul Syllable-BC27 | bc26 Hangul Syllable-BC26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc29 Hangul Syllable-BC29 | bc28 Hangul Syllable-BC28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc2b Hangul Syllable-BC2B | bc2a Hangul Syllable-BC2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc2d Hangul Syllable-BC2D | bc2c Hangul Syllable-BC2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc2f Hangul Syllable-BC2F | bc2e Hangul Syllable-BC2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc31 Hangul Syllable-BC31 | bc30 Hangul Syllable-BC30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc33 Hangul Syllable-BC33 | bc32 Hangul Syllable-BC32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc35 Hangul Syllable-BC35 | bc34 Hangul Syllable-BC34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc37 Hangul Syllable-BC37 | bc36 Hangul Syllable-BC36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc39 Hangul Syllable-BC39 | bc38 Hangul Syllable-BC38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc3b Hangul Syllable-BC3B | bc3a Hangul Syllable-BC3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc3d Hangul Syllable-BC3D | bc3c Hangul Syllable-BC3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc3f Hangul Syllable-BC3F | bc3e Hangul Syllable-BC3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc41 Hangul Syllable-BC41 | bc40 Hangul Syllable-BC40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc43 Hangul Syllable-BC43 | bc42 Hangul Syllable-BC42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc45 Hangul Syllable-BC45 | bc44 Hangul Syllable-BC44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc47 Hangul Syllable-BC47 | bc46 Hangul Syllable-BC46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc49 Hangul Syllable-BC49 | bc48 Hangul Syllable-BC48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc4b Hangul Syllable-BC4B | bc4a Hangul Syllable-BC4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc4d Hangul Syllable-BC4D | bc4c Hangul Syllable-BC4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc4f Hangul Syllable-BC4F | bc4e Hangul Syllable-BC4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc51 Hangul Syllable-BC51 | bc50 Hangul Syllable-BC50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc53 Hangul Syllable-BC53 | bc52 Hangul Syllable-BC52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc55 Hangul Syllable-BC55 | bc54 Hangul Syllable-BC54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc57 Hangul Syllable-BC57 | bc56 Hangul Syllable-BC56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc59 Hangul Syllable-BC59 | bc58 Hangul Syllable-BC58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc5b Hangul Syllable-BC5B | bc5a Hangul Syllable-BC5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc5d Hangul Syllable-BC5D | bc5c Hangul Syllable-BC5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc5f Hangul Syllable-BC5F | bc5e Hangul Syllable-BC5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc61 Hangul Syllable-BC61 | bc60 Hangul Syllable-BC60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc63 Hangul Syllable-BC63 | bc62 Hangul Syllable-BC62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc65 Hangul Syllable-BC65 | bc64 Hangul Syllable-BC64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc67 Hangul Syllable-BC67 | bc66 Hangul Syllable-BC66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc69 Hangul Syllable-BC69 | bc68 Hangul Syllable-BC68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc6b Hangul Syllable-BC6B | bc6a Hangul Syllable-BC6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc6d Hangul Syllable-BC6D | bc6c Hangul Syllable-BC6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc6f Hangul Syllable-BC6F | bc6e Hangul Syllable-BC6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc71 Hangul Syllable-BC71 | bc70 Hangul Syllable-BC70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc73 Hangul Syllable-BC73 | bc72 Hangul Syllable-BC72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc75 Hangul Syllable-BC75 | bc74 Hangul Syllable-BC74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc77 Hangul Syllable-BC77 | bc76 Hangul Syllable-BC76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc79 Hangul Syllable-BC79 | bc78 Hangul Syllable-BC78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc7b Hangul Syllable-BC7B | bc7a Hangul Syllable-BC7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc7d Hangul Syllable-BC7D | bc7c Hangul Syllable-BC7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc7f Hangul Syllable-BC7F | bc7e Hangul Syllable-BC7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc81 Hangul Syllable-BC81 | bc80 Hangul Syllable-BC80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc83 Hangul Syllable-BC83 | bc82 Hangul Syllable-BC82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc85 Hangul Syllable-BC85 | bc84 Hangul Syllable-BC84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc87 Hangul Syllable-BC87 | bc86 Hangul Syllable-BC86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc89 Hangul Syllable-BC89 | bc88 Hangul Syllable-BC88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc8b Hangul Syllable-BC8B | bc8a Hangul Syllable-BC8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc8d Hangul Syllable-BC8D | bc8c Hangul Syllable-BC8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc8f Hangul Syllable-BC8F | bc8e Hangul Syllable-BC8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc91 Hangul Syllable-BC91 | bc90 Hangul Syllable-BC90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc93 Hangul Syllable-BC93 | bc92 Hangul Syllable-BC92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc95 Hangul Syllable-BC95 | bc94 Hangul Syllable-BC94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc97 Hangul Syllable-BC97 | bc96 Hangul Syllable-BC96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc99 Hangul Syllable-BC99 | bc98 Hangul Syllable-BC98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc9b Hangul Syllable-BC9B | bc9a Hangul Syllable-BC9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc9d Hangul Syllable-BC9D | bc9c Hangul Syllable-BC9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc9f Hangul Syllable-BC9F | bc9e Hangul Syllable-BC9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bca1 Hangul Syllable-BCA1 | bca0 Hangul Syllable-BCA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bca3 Hangul Syllable-BCA3 | bca2 Hangul Syllable-BCA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bca5 Hangul Syllable-BCA5 | bca4 Hangul Syllable-BCA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bca7 Hangul Syllable-BCA7 | bca6 Hangul Syllable-BCA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bca9 Hangul Syllable-BCA9 | bca8 Hangul Syllable-BCA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcab Hangul Syllable-BCAB | bcaa Hangul Syllable-BCAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcad Hangul Syllable-BCAD | bcac Hangul Syllable-BCAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcaf Hangul Syllable-BCAF | bcae Hangul Syllable-BCAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcb1 Hangul Syllable-BCB1 | bcb0 Hangul Syllable-BCB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcb3 Hangul Syllable-BCB3 | bcb2 Hangul Syllable-BCB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcb5 Hangul Syllable-BCB5 | bcb4 Hangul Syllable-BCB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcb7 Hangul Syllable-BCB7 | bcb6 Hangul Syllable-BCB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcb9 Hangul Syllable-BCB9 | bcb8 Hangul Syllable-BCB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcbb Hangul Syllable-BCBB | bcba Hangul Syllable-BCBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcbd Hangul Syllable-BCBD | bcbc Hangul Syllable-BCBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcbf Hangul Syllable-BCBF | bcbe Hangul Syllable-BCBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcc1 Hangul Syllable-BCC1 | bcc0 Hangul Syllable-BCC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcc3 Hangul Syllable-BCC3 | bcc2 Hangul Syllable-BCC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcc5 Hangul Syllable-BCC5 | bcc4 Hangul Syllable-BCC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcc7 Hangul Syllable-BCC7 | bcc6 Hangul Syllable-BCC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcc9 Hangul Syllable-BCC9 | bcc8 Hangul Syllable-BCC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bccb Hangul Syllable-BCCB | bcca Hangul Syllable-BCCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bccd Hangul Syllable-BCCD | bccc Hangul Syllable-BCCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bccf Hangul Syllable-BCCF | bcce Hangul Syllable-BCCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcd1 Hangul Syllable-BCD1 | bcd0 Hangul Syllable-BCD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcd3 Hangul Syllable-BCD3 | bcd2 Hangul Syllable-BCD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcd5 Hangul Syllable-BCD5 | bcd4 Hangul Syllable-BCD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcd7 Hangul Syllable-BCD7 | bcd6 Hangul Syllable-BCD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcd9 Hangul Syllable-BCD9 | bcd8 Hangul Syllable-BCD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcdb Hangul Syllable-BCDB | bcda Hangul Syllable-BCDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcdd Hangul Syllable-BCDD | bcdc Hangul Syllable-BCDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcdf Hangul Syllable-BCDF | bcde Hangul Syllable-BCDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bce1 Hangul Syllable-BCE1 | bce0 Hangul Syllable-BCE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bce3 Hangul Syllable-BCE3 | bce2 Hangul Syllable-BCE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bce5 Hangul Syllable-BCE5 | bce4 Hangul Syllable-BCE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bce7 Hangul Syllable-BCE7 | bce6 Hangul Syllable-BCE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bce9 Hangul Syllable-BCE9 | bce8 Hangul Syllable-BCE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bceb Hangul Syllable-BCEB | bcea Hangul Syllable-BCEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bced Hangul Syllable-BCED | bcec Hangul Syllable-BCEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcef Hangul Syllable-BCEF | bcee Hangul Syllable-BCEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcf1 Hangul Syllable-BCF1 | bcf0 Hangul Syllable-BCF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcf3 Hangul Syllable-BCF3 | bcf2 Hangul Syllable-BCF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcf5 Hangul Syllable-BCF5 | bcf4 Hangul Syllable-BCF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcf7 Hangul Syllable-BCF7 | bcf6 Hangul Syllable-BCF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcf9 Hangul Syllable-BCF9 | bcf8 Hangul Syllable-BCF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcfb Hangul Syllable-BCFB | bcfa Hangul Syllable-BCFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcfd Hangul Syllable-BCFD | bcfc Hangul Syllable-BCFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcff Hangul Syllable-BCFF | bcfe Hangul Syllable-BCFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd01 Hangul Syllable-BD01 | bd00 Hangul Syllable-BD00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd03 Hangul Syllable-BD03 | bd02 Hangul Syllable-BD02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd05 Hangul Syllable-BD05 | bd04 Hangul Syllable-BD04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd07 Hangul Syllable-BD07 | bd06 Hangul Syllable-BD06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd09 Hangul Syllable-BD09 | bd08 Hangul Syllable-BD08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd0b Hangul Syllable-BD0B | bd0a Hangul Syllable-BD0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd0d Hangul Syllable-BD0D | bd0c Hangul Syllable-BD0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd0f Hangul Syllable-BD0F | bd0e Hangul Syllable-BD0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd11 Hangul Syllable-BD11 | bd10 Hangul Syllable-BD10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd13 Hangul Syllable-BD13 | bd12 Hangul Syllable-BD12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd15 Hangul Syllable-BD15 | bd14 Hangul Syllable-BD14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd17 Hangul Syllable-BD17 | bd16 Hangul Syllable-BD16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd19 Hangul Syllable-BD19 | bd18 Hangul Syllable-BD18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd1b Hangul Syllable-BD1B | bd1a Hangul Syllable-BD1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd1d Hangul Syllable-BD1D | bd1c Hangul Syllable-BD1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd1f Hangul Syllable-BD1F | bd1e Hangul Syllable-BD1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd21 Hangul Syllable-BD21 | bd20 Hangul Syllable-BD20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd23 Hangul Syllable-BD23 | bd22 Hangul Syllable-BD22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd25 Hangul Syllable-BD25 | bd24 Hangul Syllable-BD24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd27 Hangul Syllable-BD27 | bd26 Hangul Syllable-BD26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd29 Hangul Syllable-BD29 | bd28 Hangul Syllable-BD28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd2b Hangul Syllable-BD2B | bd2a Hangul Syllable-BD2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd2d Hangul Syllable-BD2D | bd2c Hangul Syllable-BD2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd2f Hangul Syllable-BD2F | bd2e Hangul Syllable-BD2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd31 Hangul Syllable-BD31 | bd30 Hangul Syllable-BD30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd33 Hangul Syllable-BD33 | bd32 Hangul Syllable-BD32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd35 Hangul Syllable-BD35 | bd34 Hangul Syllable-BD34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd37 Hangul Syllable-BD37 | bd36 Hangul Syllable-BD36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd39 Hangul Syllable-BD39 | bd38 Hangul Syllable-BD38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd3b Hangul Syllable-BD3B | bd3a Hangul Syllable-BD3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd3d Hangul Syllable-BD3D | bd3c Hangul Syllable-BD3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd3f Hangul Syllable-BD3F | bd3e Hangul Syllable-BD3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd41 Hangul Syllable-BD41 | bd40 Hangul Syllable-BD40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd43 Hangul Syllable-BD43 | bd42 Hangul Syllable-BD42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd45 Hangul Syllable-BD45 | bd44 Hangul Syllable-BD44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd47 Hangul Syllable-BD47 | bd46 Hangul Syllable-BD46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd49 Hangul Syllable-BD49 | bd48 Hangul Syllable-BD48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd4b Hangul Syllable-BD4B | bd4a Hangul Syllable-BD4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd4d Hangul Syllable-BD4D | bd4c Hangul Syllable-BD4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd4f Hangul Syllable-BD4F | bd4e Hangul Syllable-BD4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd51 Hangul Syllable-BD51 | bd50 Hangul Syllable-BD50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd53 Hangul Syllable-BD53 | bd52 Hangul Syllable-BD52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd55 Hangul Syllable-BD55 | bd54 Hangul Syllable-BD54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd57 Hangul Syllable-BD57 | bd56 Hangul Syllable-BD56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd59 Hangul Syllable-BD59 | bd58 Hangul Syllable-BD58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd5b Hangul Syllable-BD5B | bd5a Hangul Syllable-BD5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd5d Hangul Syllable-BD5D | bd5c Hangul Syllable-BD5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd5f Hangul Syllable-BD5F | bd5e Hangul Syllable-BD5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd61 Hangul Syllable-BD61 | bd60 Hangul Syllable-BD60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd63 Hangul Syllable-BD63 | bd62 Hangul Syllable-BD62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd65 Hangul Syllable-BD65 | bd64 Hangul Syllable-BD64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd67 Hangul Syllable-BD67 | bd66 Hangul Syllable-BD66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd69 Hangul Syllable-BD69 | bd68 Hangul Syllable-BD68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd6b Hangul Syllable-BD6B | bd6a Hangul Syllable-BD6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd6d Hangul Syllable-BD6D | bd6c Hangul Syllable-BD6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd6f Hangul Syllable-BD6F | bd6e Hangul Syllable-BD6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd71 Hangul Syllable-BD71 | bd70 Hangul Syllable-BD70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd73 Hangul Syllable-BD73 | bd72 Hangul Syllable-BD72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd75 Hangul Syllable-BD75 | bd74 Hangul Syllable-BD74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd77 Hangul Syllable-BD77 | bd76 Hangul Syllable-BD76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd79 Hangul Syllable-BD79 | bd78 Hangul Syllable-BD78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd7b Hangul Syllable-BD7B | bd7a Hangul Syllable-BD7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd7d Hangul Syllable-BD7D | bd7c Hangul Syllable-BD7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd7f Hangul Syllable-BD7F | bd7e Hangul Syllable-BD7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd81 Hangul Syllable-BD81 | bd80 Hangul Syllable-BD80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd83 Hangul Syllable-BD83 | bd82 Hangul Syllable-BD82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd85 Hangul Syllable-BD85 | bd84 Hangul Syllable-BD84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd87 Hangul Syllable-BD87 | bd86 Hangul Syllable-BD86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd89 Hangul Syllable-BD89 | bd88 Hangul Syllable-BD88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd8b Hangul Syllable-BD8B | bd8a Hangul Syllable-BD8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd8d Hangul Syllable-BD8D | bd8c Hangul Syllable-BD8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd8f Hangul Syllable-BD8F | bd8e Hangul Syllable-BD8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd91 Hangul Syllable-BD91 | bd90 Hangul Syllable-BD90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd93 Hangul Syllable-BD93 | bd92 Hangul Syllable-BD92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd95 Hangul Syllable-BD95 | bd94 Hangul Syllable-BD94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd97 Hangul Syllable-BD97 | bd96 Hangul Syllable-BD96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd99 Hangul Syllable-BD99 | bd98 Hangul Syllable-BD98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd9b Hangul Syllable-BD9B | bd9a Hangul Syllable-BD9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd9d Hangul Syllable-BD9D | bd9c Hangul Syllable-BD9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd9f Hangul Syllable-BD9F | bd9e Hangul Syllable-BD9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bda1 Hangul Syllable-BDA1 | bda0 Hangul Syllable-BDA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bda3 Hangul Syllable-BDA3 | bda2 Hangul Syllable-BDA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bda5 Hangul Syllable-BDA5 | bda4 Hangul Syllable-BDA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bda7 Hangul Syllable-BDA7 | bda6 Hangul Syllable-BDA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bda9 Hangul Syllable-BDA9 | bda8 Hangul Syllable-BDA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdab Hangul Syllable-BDAB | bdaa Hangul Syllable-BDAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdad Hangul Syllable-BDAD | bdac Hangul Syllable-BDAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdaf Hangul Syllable-BDAF | bdae Hangul Syllable-BDAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdb1 Hangul Syllable-BDB1 | bdb0 Hangul Syllable-BDB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdb3 Hangul Syllable-BDB3 | bdb2 Hangul Syllable-BDB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdb5 Hangul Syllable-BDB5 | bdb4 Hangul Syllable-BDB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdb7 Hangul Syllable-BDB7 | bdb6 Hangul Syllable-BDB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdb9 Hangul Syllable-BDB9 | bdb8 Hangul Syllable-BDB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdbb Hangul Syllable-BDBB | bdba Hangul Syllable-BDBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdbd Hangul Syllable-BDBD | bdbc Hangul Syllable-BDBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdbf Hangul Syllable-BDBF | bdbe Hangul Syllable-BDBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdc1 Hangul Syllable-BDC1 | bdc0 Hangul Syllable-BDC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdc3 Hangul Syllable-BDC3 | bdc2 Hangul Syllable-BDC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdc5 Hangul Syllable-BDC5 | bdc4 Hangul Syllable-BDC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdc7 Hangul Syllable-BDC7 | bdc6 Hangul Syllable-BDC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdc9 Hangul Syllable-BDC9 | bdc8 Hangul Syllable-BDC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdcb Hangul Syllable-BDCB | bdca Hangul Syllable-BDCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdcd Hangul Syllable-BDCD | bdcc Hangul Syllable-BDCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdcf Hangul Syllable-BDCF | bdce Hangul Syllable-BDCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdd1 Hangul Syllable-BDD1 | bdd0 Hangul Syllable-BDD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdd3 Hangul Syllable-BDD3 | bdd2 Hangul Syllable-BDD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdd5 Hangul Syllable-BDD5 | bdd4 Hangul Syllable-BDD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdd7 Hangul Syllable-BDD7 | bdd6 Hangul Syllable-BDD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdd9 Hangul Syllable-BDD9 | bdd8 Hangul Syllable-BDD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bddb Hangul Syllable-BDDB | bdda Hangul Syllable-BDDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bddd Hangul Syllable-BDDD | bddc Hangul Syllable-BDDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bddf Hangul Syllable-BDDF | bdde Hangul Syllable-BDDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bde1 Hangul Syllable-BDE1 | bde0 Hangul Syllable-BDE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bde3 Hangul Syllable-BDE3 | bde2 Hangul Syllable-BDE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bde5 Hangul Syllable-BDE5 | bde4 Hangul Syllable-BDE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bde7 Hangul Syllable-BDE7 | bde6 Hangul Syllable-BDE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bde9 Hangul Syllable-BDE9 | bde8 Hangul Syllable-BDE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdeb Hangul Syllable-BDEB | bdea Hangul Syllable-BDEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bded Hangul Syllable-BDED | bdec Hangul Syllable-BDEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdef Hangul Syllable-BDEF | bdee Hangul Syllable-BDEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdf1 Hangul Syllable-BDF1 | bdf0 Hangul Syllable-BDF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdf3 Hangul Syllable-BDF3 | bdf2 Hangul Syllable-BDF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdf5 Hangul Syllable-BDF5 | bdf4 Hangul Syllable-BDF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdf7 Hangul Syllable-BDF7 | bdf6 Hangul Syllable-BDF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdf9 Hangul Syllable-BDF9 | bdf8 Hangul Syllable-BDF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdfb Hangul Syllable-BDFB | bdfa Hangul Syllable-BDFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdfd Hangul Syllable-BDFD | bdfc Hangul Syllable-BDFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdff Hangul Syllable-BDFF | bdfe Hangul Syllable-BDFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be01 Hangul Syllable-BE01 | be00 Hangul Syllable-BE00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be03 Hangul Syllable-BE03 | be02 Hangul Syllable-BE02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be05 Hangul Syllable-BE05 | be04 Hangul Syllable-BE04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be07 Hangul Syllable-BE07 | be06 Hangul Syllable-BE06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be09 Hangul Syllable-BE09 | be08 Hangul Syllable-BE08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be0b Hangul Syllable-BE0B | be0a Hangul Syllable-BE0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be0d Hangul Syllable-BE0D | be0c Hangul Syllable-BE0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be0f Hangul Syllable-BE0F | be0e Hangul Syllable-BE0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be11 Hangul Syllable-BE11 | be10 Hangul Syllable-BE10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be13 Hangul Syllable-BE13 | be12 Hangul Syllable-BE12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be15 Hangul Syllable-BE15 | be14 Hangul Syllable-BE14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be17 Hangul Syllable-BE17 | be16 Hangul Syllable-BE16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be19 Hangul Syllable-BE19 | be18 Hangul Syllable-BE18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be1b Hangul Syllable-BE1B | be1a Hangul Syllable-BE1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be1d Hangul Syllable-BE1D | be1c Hangul Syllable-BE1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be1f Hangul Syllable-BE1F | be1e Hangul Syllable-BE1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be21 Hangul Syllable-BE21 | be20 Hangul Syllable-BE20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be23 Hangul Syllable-BE23 | be22 Hangul Syllable-BE22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be25 Hangul Syllable-BE25 | be24 Hangul Syllable-BE24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be27 Hangul Syllable-BE27 | be26 Hangul Syllable-BE26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be29 Hangul Syllable-BE29 | be28 Hangul Syllable-BE28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be2b Hangul Syllable-BE2B | be2a Hangul Syllable-BE2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be2d Hangul Syllable-BE2D | be2c Hangul Syllable-BE2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be2f Hangul Syllable-BE2F | be2e Hangul Syllable-BE2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be31 Hangul Syllable-BE31 | be30 Hangul Syllable-BE30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be33 Hangul Syllable-BE33 | be32 Hangul Syllable-BE32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be35 Hangul Syllable-BE35 | be34 Hangul Syllable-BE34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be37 Hangul Syllable-BE37 | be36 Hangul Syllable-BE36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be39 Hangul Syllable-BE39 | be38 Hangul Syllable-BE38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be3b Hangul Syllable-BE3B | be3a Hangul Syllable-BE3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be3d Hangul Syllable-BE3D | be3c Hangul Syllable-BE3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be3f Hangul Syllable-BE3F | be3e Hangul Syllable-BE3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be41 Hangul Syllable-BE41 | be40 Hangul Syllable-BE40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be43 Hangul Syllable-BE43 | be42 Hangul Syllable-BE42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be45 Hangul Syllable-BE45 | be44 Hangul Syllable-BE44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be47 Hangul Syllable-BE47 | be46 Hangul Syllable-BE46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be49 Hangul Syllable-BE49 | be48 Hangul Syllable-BE48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be4b Hangul Syllable-BE4B | be4a Hangul Syllable-BE4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be4d Hangul Syllable-BE4D | be4c Hangul Syllable-BE4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be4f Hangul Syllable-BE4F | be4e Hangul Syllable-BE4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be51 Hangul Syllable-BE51 | be50 Hangul Syllable-BE50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be53 Hangul Syllable-BE53 | be52 Hangul Syllable-BE52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be55 Hangul Syllable-BE55 | be54 Hangul Syllable-BE54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be57 Hangul Syllable-BE57 | be56 Hangul Syllable-BE56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be59 Hangul Syllable-BE59 | be58 Hangul Syllable-BE58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be5b Hangul Syllable-BE5B | be5a Hangul Syllable-BE5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be5d Hangul Syllable-BE5D | be5c Hangul Syllable-BE5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be5f Hangul Syllable-BE5F | be5e Hangul Syllable-BE5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be61 Hangul Syllable-BE61 | be60 Hangul Syllable-BE60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be63 Hangul Syllable-BE63 | be62 Hangul Syllable-BE62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be65 Hangul Syllable-BE65 | be64 Hangul Syllable-BE64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be67 Hangul Syllable-BE67 | be66 Hangul Syllable-BE66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be69 Hangul Syllable-BE69 | be68 Hangul Syllable-BE68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be6b Hangul Syllable-BE6B | be6a Hangul Syllable-BE6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be6d Hangul Syllable-BE6D | be6c Hangul Syllable-BE6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be6f Hangul Syllable-BE6F | be6e Hangul Syllable-BE6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be71 Hangul Syllable-BE71 | be70 Hangul Syllable-BE70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be73 Hangul Syllable-BE73 | be72 Hangul Syllable-BE72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be75 Hangul Syllable-BE75 | be74 Hangul Syllable-BE74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be77 Hangul Syllable-BE77 | be76 Hangul Syllable-BE76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be79 Hangul Syllable-BE79 | be78 Hangul Syllable-BE78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be7b Hangul Syllable-BE7B | be7a Hangul Syllable-BE7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be7d Hangul Syllable-BE7D | be7c Hangul Syllable-BE7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be7f Hangul Syllable-BE7F | be7e Hangul Syllable-BE7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be81 Hangul Syllable-BE81 | be80 Hangul Syllable-BE80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be83 Hangul Syllable-BE83 | be82 Hangul Syllable-BE82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be85 Hangul Syllable-BE85 | be84 Hangul Syllable-BE84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be87 Hangul Syllable-BE87 | be86 Hangul Syllable-BE86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be89 Hangul Syllable-BE89 | be88 Hangul Syllable-BE88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be8b Hangul Syllable-BE8B | be8a Hangul Syllable-BE8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be8d Hangul Syllable-BE8D | be8c Hangul Syllable-BE8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be8f Hangul Syllable-BE8F | be8e Hangul Syllable-BE8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be91 Hangul Syllable-BE91 | be90 Hangul Syllable-BE90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be93 Hangul Syllable-BE93 | be92 Hangul Syllable-BE92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be95 Hangul Syllable-BE95 | be94 Hangul Syllable-BE94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be97 Hangul Syllable-BE97 | be96 Hangul Syllable-BE96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be99 Hangul Syllable-BE99 | be98 Hangul Syllable-BE98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be9b Hangul Syllable-BE9B | be9a Hangul Syllable-BE9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be9d Hangul Syllable-BE9D | be9c Hangul Syllable-BE9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be9f Hangul Syllable-BE9F | be9e Hangul Syllable-BE9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bea1 Hangul Syllable-BEA1 | bea0 Hangul Syllable-BEA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bea3 Hangul Syllable-BEA3 | bea2 Hangul Syllable-BEA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bea5 Hangul Syllable-BEA5 | bea4 Hangul Syllable-BEA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bea7 Hangul Syllable-BEA7 | bea6 Hangul Syllable-BEA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bea9 Hangul Syllable-BEA9 | bea8 Hangul Syllable-BEA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beab Hangul Syllable-BEAB | beaa Hangul Syllable-BEAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bead Hangul Syllable-BEAD | beac Hangul Syllable-BEAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beaf Hangul Syllable-BEAF | beae Hangul Syllable-BEAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beb1 Hangul Syllable-BEB1 | beb0 Hangul Syllable-BEB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beb3 Hangul Syllable-BEB3 | beb2 Hangul Syllable-BEB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beb5 Hangul Syllable-BEB5 | beb4 Hangul Syllable-BEB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beb7 Hangul Syllable-BEB7 | beb6 Hangul Syllable-BEB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beb9 Hangul Syllable-BEB9 | beb8 Hangul Syllable-BEB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bebb Hangul Syllable-BEBB | beba Hangul Syllable-BEBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bebd Hangul Syllable-BEBD | bebc Hangul Syllable-BEBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bebf Hangul Syllable-BEBF | bebe Hangul Syllable-BEBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bec1 Hangul Syllable-BEC1 | bec0 Hangul Syllable-BEC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bec3 Hangul Syllable-BEC3 | bec2 Hangul Syllable-BEC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bec5 Hangul Syllable-BEC5 | bec4 Hangul Syllable-BEC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bec7 Hangul Syllable-BEC7 | bec6 Hangul Syllable-BEC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bec9 Hangul Syllable-BEC9 | bec8 Hangul Syllable-BEC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* becb Hangul Syllable-BECB | beca Hangul Syllable-BECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* becd Hangul Syllable-BECD | becc Hangul Syllable-BECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* becf Hangul Syllable-BECF | bece Hangul Syllable-BECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bed1 Hangul Syllable-BED1 | bed0 Hangul Syllable-BED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bed3 Hangul Syllable-BED3 | bed2 Hangul Syllable-BED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bed5 Hangul Syllable-BED5 | bed4 Hangul Syllable-BED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bed7 Hangul Syllable-BED7 | bed6 Hangul Syllable-BED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bed9 Hangul Syllable-BED9 | bed8 Hangul Syllable-BED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bedb Hangul Syllable-BEDB | beda Hangul Syllable-BEDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bedd Hangul Syllable-BEDD | bedc Hangul Syllable-BEDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bedf Hangul Syllable-BEDF | bede Hangul Syllable-BEDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bee1 Hangul Syllable-BEE1 | bee0 Hangul Syllable-BEE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bee3 Hangul Syllable-BEE3 | bee2 Hangul Syllable-BEE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bee5 Hangul Syllable-BEE5 | bee4 Hangul Syllable-BEE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bee7 Hangul Syllable-BEE7 | bee6 Hangul Syllable-BEE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bee9 Hangul Syllable-BEE9 | bee8 Hangul Syllable-BEE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beeb Hangul Syllable-BEEB | beea Hangul Syllable-BEEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beed Hangul Syllable-BEED | beec Hangul Syllable-BEEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beef Hangul Syllable-BEEF | beee Hangul Syllable-BEEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bef1 Hangul Syllable-BEF1 | bef0 Hangul Syllable-BEF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bef3 Hangul Syllable-BEF3 | bef2 Hangul Syllable-BEF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bef5 Hangul Syllable-BEF5 | bef4 Hangul Syllable-BEF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bef7 Hangul Syllable-BEF7 | bef6 Hangul Syllable-BEF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bef9 Hangul Syllable-BEF9 | bef8 Hangul Syllable-BEF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* befb Hangul Syllable-BEFB | befa Hangul Syllable-BEFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* befd Hangul Syllable-BEFD | befc Hangul Syllable-BEFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beff Hangul Syllable-BEFF | befe Hangul Syllable-BEFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf01 Hangul Syllable-BF01 | bf00 Hangul Syllable-BF00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf03 Hangul Syllable-BF03 | bf02 Hangul Syllable-BF02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf05 Hangul Syllable-BF05 | bf04 Hangul Syllable-BF04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf07 Hangul Syllable-BF07 | bf06 Hangul Syllable-BF06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf09 Hangul Syllable-BF09 | bf08 Hangul Syllable-BF08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf0b Hangul Syllable-BF0B | bf0a Hangul Syllable-BF0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf0d Hangul Syllable-BF0D | bf0c Hangul Syllable-BF0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf0f Hangul Syllable-BF0F | bf0e Hangul Syllable-BF0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf11 Hangul Syllable-BF11 | bf10 Hangul Syllable-BF10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf13 Hangul Syllable-BF13 | bf12 Hangul Syllable-BF12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf15 Hangul Syllable-BF15 | bf14 Hangul Syllable-BF14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf17 Hangul Syllable-BF17 | bf16 Hangul Syllable-BF16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf19 Hangul Syllable-BF19 | bf18 Hangul Syllable-BF18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf1b Hangul Syllable-BF1B | bf1a Hangul Syllable-BF1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf1d Hangul Syllable-BF1D | bf1c Hangul Syllable-BF1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf1f Hangul Syllable-BF1F | bf1e Hangul Syllable-BF1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf21 Hangul Syllable-BF21 | bf20 Hangul Syllable-BF20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf23 Hangul Syllable-BF23 | bf22 Hangul Syllable-BF22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf25 Hangul Syllable-BF25 | bf24 Hangul Syllable-BF24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf27 Hangul Syllable-BF27 | bf26 Hangul Syllable-BF26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf29 Hangul Syllable-BF29 | bf28 Hangul Syllable-BF28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf2b Hangul Syllable-BF2B | bf2a Hangul Syllable-BF2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf2d Hangul Syllable-BF2D | bf2c Hangul Syllable-BF2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf2f Hangul Syllable-BF2F | bf2e Hangul Syllable-BF2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf31 Hangul Syllable-BF31 | bf30 Hangul Syllable-BF30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf33 Hangul Syllable-BF33 | bf32 Hangul Syllable-BF32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf35 Hangul Syllable-BF35 | bf34 Hangul Syllable-BF34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf37 Hangul Syllable-BF37 | bf36 Hangul Syllable-BF36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf39 Hangul Syllable-BF39 | bf38 Hangul Syllable-BF38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf3b Hangul Syllable-BF3B | bf3a Hangul Syllable-BF3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf3d Hangul Syllable-BF3D | bf3c Hangul Syllable-BF3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf3f Hangul Syllable-BF3F | bf3e Hangul Syllable-BF3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf41 Hangul Syllable-BF41 | bf40 Hangul Syllable-BF40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf43 Hangul Syllable-BF43 | bf42 Hangul Syllable-BF42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf45 Hangul Syllable-BF45 | bf44 Hangul Syllable-BF44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf47 Hangul Syllable-BF47 | bf46 Hangul Syllable-BF46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf49 Hangul Syllable-BF49 | bf48 Hangul Syllable-BF48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf4b Hangul Syllable-BF4B | bf4a Hangul Syllable-BF4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf4d Hangul Syllable-BF4D | bf4c Hangul Syllable-BF4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf4f Hangul Syllable-BF4F | bf4e Hangul Syllable-BF4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf51 Hangul Syllable-BF51 | bf50 Hangul Syllable-BF50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf53 Hangul Syllable-BF53 | bf52 Hangul Syllable-BF52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf55 Hangul Syllable-BF55 | bf54 Hangul Syllable-BF54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf57 Hangul Syllable-BF57 | bf56 Hangul Syllable-BF56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf59 Hangul Syllable-BF59 | bf58 Hangul Syllable-BF58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf5b Hangul Syllable-BF5B | bf5a Hangul Syllable-BF5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf5d Hangul Syllable-BF5D | bf5c Hangul Syllable-BF5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf5f Hangul Syllable-BF5F | bf5e Hangul Syllable-BF5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf61 Hangul Syllable-BF61 | bf60 Hangul Syllable-BF60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf63 Hangul Syllable-BF63 | bf62 Hangul Syllable-BF62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf65 Hangul Syllable-BF65 | bf64 Hangul Syllable-BF64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf67 Hangul Syllable-BF67 | bf66 Hangul Syllable-BF66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf69 Hangul Syllable-BF69 | bf68 Hangul Syllable-BF68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf6b Hangul Syllable-BF6B | bf6a Hangul Syllable-BF6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf6d Hangul Syllable-BF6D | bf6c Hangul Syllable-BF6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf6f Hangul Syllable-BF6F | bf6e Hangul Syllable-BF6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf71 Hangul Syllable-BF71 | bf70 Hangul Syllable-BF70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf73 Hangul Syllable-BF73 | bf72 Hangul Syllable-BF72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf75 Hangul Syllable-BF75 | bf74 Hangul Syllable-BF74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf77 Hangul Syllable-BF77 | bf76 Hangul Syllable-BF76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf79 Hangul Syllable-BF79 | bf78 Hangul Syllable-BF78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf7b Hangul Syllable-BF7B | bf7a Hangul Syllable-BF7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf7d Hangul Syllable-BF7D | bf7c Hangul Syllable-BF7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf7f Hangul Syllable-BF7F | bf7e Hangul Syllable-BF7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf81 Hangul Syllable-BF81 | bf80 Hangul Syllable-BF80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf83 Hangul Syllable-BF83 | bf82 Hangul Syllable-BF82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf85 Hangul Syllable-BF85 | bf84 Hangul Syllable-BF84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf87 Hangul Syllable-BF87 | bf86 Hangul Syllable-BF86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf89 Hangul Syllable-BF89 | bf88 Hangul Syllable-BF88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf8b Hangul Syllable-BF8B | bf8a Hangul Syllable-BF8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf8d Hangul Syllable-BF8D | bf8c Hangul Syllable-BF8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf8f Hangul Syllable-BF8F | bf8e Hangul Syllable-BF8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf91 Hangul Syllable-BF91 | bf90 Hangul Syllable-BF90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf93 Hangul Syllable-BF93 | bf92 Hangul Syllable-BF92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf95 Hangul Syllable-BF95 | bf94 Hangul Syllable-BF94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf97 Hangul Syllable-BF97 | bf96 Hangul Syllable-BF96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf99 Hangul Syllable-BF99 | bf98 Hangul Syllable-BF98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf9b Hangul Syllable-BF9B | bf9a Hangul Syllable-BF9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf9d Hangul Syllable-BF9D | bf9c Hangul Syllable-BF9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf9f Hangul Syllable-BF9F | bf9e Hangul Syllable-BF9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfa1 Hangul Syllable-BFA1 | bfa0 Hangul Syllable-BFA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfa3 Hangul Syllable-BFA3 | bfa2 Hangul Syllable-BFA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfa5 Hangul Syllable-BFA5 | bfa4 Hangul Syllable-BFA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfa7 Hangul Syllable-BFA7 | bfa6 Hangul Syllable-BFA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfa9 Hangul Syllable-BFA9 | bfa8 Hangul Syllable-BFA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfab Hangul Syllable-BFAB | bfaa Hangul Syllable-BFAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfad Hangul Syllable-BFAD | bfac Hangul Syllable-BFAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfaf Hangul Syllable-BFAF | bfae Hangul Syllable-BFAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfb1 Hangul Syllable-BFB1 | bfb0 Hangul Syllable-BFB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfb3 Hangul Syllable-BFB3 | bfb2 Hangul Syllable-BFB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfb5 Hangul Syllable-BFB5 | bfb4 Hangul Syllable-BFB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfb7 Hangul Syllable-BFB7 | bfb6 Hangul Syllable-BFB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfb9 Hangul Syllable-BFB9 | bfb8 Hangul Syllable-BFB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfbb Hangul Syllable-BFBB | bfba Hangul Syllable-BFBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfbd Hangul Syllable-BFBD | bfbc Hangul Syllable-BFBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfbf Hangul Syllable-BFBF | bfbe Hangul Syllable-BFBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfc1 Hangul Syllable-BFC1 | bfc0 Hangul Syllable-BFC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfc3 Hangul Syllable-BFC3 | bfc2 Hangul Syllable-BFC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfc5 Hangul Syllable-BFC5 | bfc4 Hangul Syllable-BFC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfc7 Hangul Syllable-BFC7 | bfc6 Hangul Syllable-BFC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfc9 Hangul Syllable-BFC9 | bfc8 Hangul Syllable-BFC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfcb Hangul Syllable-BFCB | bfca Hangul Syllable-BFCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfcd Hangul Syllable-BFCD | bfcc Hangul Syllable-BFCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfcf Hangul Syllable-BFCF | bfce Hangul Syllable-BFCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfd1 Hangul Syllable-BFD1 | bfd0 Hangul Syllable-BFD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfd3 Hangul Syllable-BFD3 | bfd2 Hangul Syllable-BFD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfd5 Hangul Syllable-BFD5 | bfd4 Hangul Syllable-BFD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfd7 Hangul Syllable-BFD7 | bfd6 Hangul Syllable-BFD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfd9 Hangul Syllable-BFD9 | bfd8 Hangul Syllable-BFD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfdb Hangul Syllable-BFDB | bfda Hangul Syllable-BFDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfdd Hangul Syllable-BFDD | bfdc Hangul Syllable-BFDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfdf Hangul Syllable-BFDF | bfde Hangul Syllable-BFDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfe1 Hangul Syllable-BFE1 | bfe0 Hangul Syllable-BFE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfe3 Hangul Syllable-BFE3 | bfe2 Hangul Syllable-BFE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfe5 Hangul Syllable-BFE5 | bfe4 Hangul Syllable-BFE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfe7 Hangul Syllable-BFE7 | bfe6 Hangul Syllable-BFE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfe9 Hangul Syllable-BFE9 | bfe8 Hangul Syllable-BFE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfeb Hangul Syllable-BFEB | bfea Hangul Syllable-BFEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfed Hangul Syllable-BFED | bfec Hangul Syllable-BFEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfef Hangul Syllable-BFEF | bfee Hangul Syllable-BFEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bff1 Hangul Syllable-BFF1 | bff0 Hangul Syllable-BFF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bff3 Hangul Syllable-BFF3 | bff2 Hangul Syllable-BFF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bff5 Hangul Syllable-BFF5 | bff4 Hangul Syllable-BFF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bff7 Hangul Syllable-BFF7 | bff6 Hangul Syllable-BFF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bff9 Hangul Syllable-BFF9 | bff8 Hangul Syllable-BFF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bffb Hangul Syllable-BFFB | bffa Hangul Syllable-BFFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bffd Hangul Syllable-BFFD | bffc Hangul Syllable-BFFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfff Hangul Syllable-BFFF | bffe Hangul Syllable-BFFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c001 Hangul Syllable-C001 | c000 Hangul Syllable-C000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c003 Hangul Syllable-C003 | c002 Hangul Syllable-C002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c005 Hangul Syllable-C005 | c004 Hangul Syllable-C004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c007 Hangul Syllable-C007 | c006 Hangul Syllable-C006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c009 Hangul Syllable-C009 | c008 Hangul Syllable-C008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c00b Hangul Syllable-C00B | c00a Hangul Syllable-C00A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c00d Hangul Syllable-C00D | c00c Hangul Syllable-C00C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c00f Hangul Syllable-C00F | c00e Hangul Syllable-C00E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c011 Hangul Syllable-C011 | c010 Hangul Syllable-C010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c013 Hangul Syllable-C013 | c012 Hangul Syllable-C012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c015 Hangul Syllable-C015 | c014 Hangul Syllable-C014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c017 Hangul Syllable-C017 | c016 Hangul Syllable-C016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c019 Hangul Syllable-C019 | c018 Hangul Syllable-C018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c01b Hangul Syllable-C01B | c01a Hangul Syllable-C01A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c01d Hangul Syllable-C01D | c01c Hangul Syllable-C01C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c01f Hangul Syllable-C01F | c01e Hangul Syllable-C01E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c021 Hangul Syllable-C021 | c020 Hangul Syllable-C020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c023 Hangul Syllable-C023 | c022 Hangul Syllable-C022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c025 Hangul Syllable-C025 | c024 Hangul Syllable-C024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c027 Hangul Syllable-C027 | c026 Hangul Syllable-C026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c029 Hangul Syllable-C029 | c028 Hangul Syllable-C028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c02b Hangul Syllable-C02B | c02a Hangul Syllable-C02A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c02d Hangul Syllable-C02D | c02c Hangul Syllable-C02C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c02f Hangul Syllable-C02F | c02e Hangul Syllable-C02E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c031 Hangul Syllable-C031 | c030 Hangul Syllable-C030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c033 Hangul Syllable-C033 | c032 Hangul Syllable-C032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c035 Hangul Syllable-C035 | c034 Hangul Syllable-C034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c037 Hangul Syllable-C037 | c036 Hangul Syllable-C036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c039 Hangul Syllable-C039 | c038 Hangul Syllable-C038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c03b Hangul Syllable-C03B | c03a Hangul Syllable-C03A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c03d Hangul Syllable-C03D | c03c Hangul Syllable-C03C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c03f Hangul Syllable-C03F | c03e Hangul Syllable-C03E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c041 Hangul Syllable-C041 | c040 Hangul Syllable-C040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c043 Hangul Syllable-C043 | c042 Hangul Syllable-C042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c045 Hangul Syllable-C045 | c044 Hangul Syllable-C044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c047 Hangul Syllable-C047 | c046 Hangul Syllable-C046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c049 Hangul Syllable-C049 | c048 Hangul Syllable-C048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c04b Hangul Syllable-C04B | c04a Hangul Syllable-C04A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c04d Hangul Syllable-C04D | c04c Hangul Syllable-C04C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c04f Hangul Syllable-C04F | c04e Hangul Syllable-C04E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c051 Hangul Syllable-C051 | c050 Hangul Syllable-C050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c053 Hangul Syllable-C053 | c052 Hangul Syllable-C052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c055 Hangul Syllable-C055 | c054 Hangul Syllable-C054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c057 Hangul Syllable-C057 | c056 Hangul Syllable-C056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c059 Hangul Syllable-C059 | c058 Hangul Syllable-C058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c05b Hangul Syllable-C05B | c05a Hangul Syllable-C05A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c05d Hangul Syllable-C05D | c05c Hangul Syllable-C05C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c05f Hangul Syllable-C05F | c05e Hangul Syllable-C05E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c061 Hangul Syllable-C061 | c060 Hangul Syllable-C060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c063 Hangul Syllable-C063 | c062 Hangul Syllable-C062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c065 Hangul Syllable-C065 | c064 Hangul Syllable-C064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c067 Hangul Syllable-C067 | c066 Hangul Syllable-C066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c069 Hangul Syllable-C069 | c068 Hangul Syllable-C068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c06b Hangul Syllable-C06B | c06a Hangul Syllable-C06A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c06d Hangul Syllable-C06D | c06c Hangul Syllable-C06C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c06f Hangul Syllable-C06F | c06e Hangul Syllable-C06E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c071 Hangul Syllable-C071 | c070 Hangul Syllable-C070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c073 Hangul Syllable-C073 | c072 Hangul Syllable-C072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c075 Hangul Syllable-C075 | c074 Hangul Syllable-C074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c077 Hangul Syllable-C077 | c076 Hangul Syllable-C076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c079 Hangul Syllable-C079 | c078 Hangul Syllable-C078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c07b Hangul Syllable-C07B | c07a Hangul Syllable-C07A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c07d Hangul Syllable-C07D | c07c Hangul Syllable-C07C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c07f Hangul Syllable-C07F | c07e Hangul Syllable-C07E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c081 Hangul Syllable-C081 | c080 Hangul Syllable-C080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c083 Hangul Syllable-C083 | c082 Hangul Syllable-C082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c085 Hangul Syllable-C085 | c084 Hangul Syllable-C084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c087 Hangul Syllable-C087 | c086 Hangul Syllable-C086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c089 Hangul Syllable-C089 | c088 Hangul Syllable-C088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c08b Hangul Syllable-C08B | c08a Hangul Syllable-C08A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c08d Hangul Syllable-C08D | c08c Hangul Syllable-C08C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c08f Hangul Syllable-C08F | c08e Hangul Syllable-C08E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c091 Hangul Syllable-C091 | c090 Hangul Syllable-C090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c093 Hangul Syllable-C093 | c092 Hangul Syllable-C092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c095 Hangul Syllable-C095 | c094 Hangul Syllable-C094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c097 Hangul Syllable-C097 | c096 Hangul Syllable-C096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c099 Hangul Syllable-C099 | c098 Hangul Syllable-C098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c09b Hangul Syllable-C09B | c09a Hangul Syllable-C09A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c09d Hangul Syllable-C09D | c09c Hangul Syllable-C09C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c09f Hangul Syllable-C09F | c09e Hangul Syllable-C09E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0a1 Hangul Syllable-C0A1 | c0a0 Hangul Syllable-C0A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0a3 Hangul Syllable-C0A3 | c0a2 Hangul Syllable-C0A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0a5 Hangul Syllable-C0A5 | c0a4 Hangul Syllable-C0A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0a7 Hangul Syllable-C0A7 | c0a6 Hangul Syllable-C0A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0a9 Hangul Syllable-C0A9 | c0a8 Hangul Syllable-C0A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0ab Hangul Syllable-C0AB | c0aa Hangul Syllable-C0AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0ad Hangul Syllable-C0AD | c0ac Hangul Syllable-C0AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0af Hangul Syllable-C0AF | c0ae Hangul Syllable-C0AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0b1 Hangul Syllable-C0B1 | c0b0 Hangul Syllable-C0B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0b3 Hangul Syllable-C0B3 | c0b2 Hangul Syllable-C0B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0b5 Hangul Syllable-C0B5 | c0b4 Hangul Syllable-C0B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0b7 Hangul Syllable-C0B7 | c0b6 Hangul Syllable-C0B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0b9 Hangul Syllable-C0B9 | c0b8 Hangul Syllable-C0B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0bb Hangul Syllable-C0BB | c0ba Hangul Syllable-C0BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0bd Hangul Syllable-C0BD | c0bc Hangul Syllable-C0BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0bf Hangul Syllable-C0BF | c0be Hangul Syllable-C0BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0c1 Hangul Syllable-C0C1 | c0c0 Hangul Syllable-C0C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0c3 Hangul Syllable-C0C3 | c0c2 Hangul Syllable-C0C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0c5 Hangul Syllable-C0C5 | c0c4 Hangul Syllable-C0C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0c7 Hangul Syllable-C0C7 | c0c6 Hangul Syllable-C0C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0c9 Hangul Syllable-C0C9 | c0c8 Hangul Syllable-C0C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0cb Hangul Syllable-C0CB | c0ca Hangul Syllable-C0CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0cd Hangul Syllable-C0CD | c0cc Hangul Syllable-C0CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0cf Hangul Syllable-C0CF | c0ce Hangul Syllable-C0CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0d1 Hangul Syllable-C0D1 | c0d0 Hangul Syllable-C0D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0d3 Hangul Syllable-C0D3 | c0d2 Hangul Syllable-C0D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0d5 Hangul Syllable-C0D5 | c0d4 Hangul Syllable-C0D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0d7 Hangul Syllable-C0D7 | c0d6 Hangul Syllable-C0D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0d9 Hangul Syllable-C0D9 | c0d8 Hangul Syllable-C0D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0db Hangul Syllable-C0DB | c0da Hangul Syllable-C0DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0dd Hangul Syllable-C0DD | c0dc Hangul Syllable-C0DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0df Hangul Syllable-C0DF | c0de Hangul Syllable-C0DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0e1 Hangul Syllable-C0E1 | c0e0 Hangul Syllable-C0E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0e3 Hangul Syllable-C0E3 | c0e2 Hangul Syllable-C0E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0e5 Hangul Syllable-C0E5 | c0e4 Hangul Syllable-C0E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0e7 Hangul Syllable-C0E7 | c0e6 Hangul Syllable-C0E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0e9 Hangul Syllable-C0E9 | c0e8 Hangul Syllable-C0E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0eb Hangul Syllable-C0EB | c0ea Hangul Syllable-C0EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0ed Hangul Syllable-C0ED | c0ec Hangul Syllable-C0EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0ef Hangul Syllable-C0EF | c0ee Hangul Syllable-C0EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0f1 Hangul Syllable-C0F1 | c0f0 Hangul Syllable-C0F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0f3 Hangul Syllable-C0F3 | c0f2 Hangul Syllable-C0F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0f5 Hangul Syllable-C0F5 | c0f4 Hangul Syllable-C0F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0f7 Hangul Syllable-C0F7 | c0f6 Hangul Syllable-C0F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0f9 Hangul Syllable-C0F9 | c0f8 Hangul Syllable-C0F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0fb Hangul Syllable-C0FB | c0fa Hangul Syllable-C0FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0fd Hangul Syllable-C0FD | c0fc Hangul Syllable-C0FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0ff Hangul Syllable-C0FF | c0fe Hangul Syllable-C0FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c101 Hangul Syllable-C101 | c100 Hangul Syllable-C100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c103 Hangul Syllable-C103 | c102 Hangul Syllable-C102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c105 Hangul Syllable-C105 | c104 Hangul Syllable-C104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c107 Hangul Syllable-C107 | c106 Hangul Syllable-C106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c109 Hangul Syllable-C109 | c108 Hangul Syllable-C108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c10b Hangul Syllable-C10B | c10a Hangul Syllable-C10A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c10d Hangul Syllable-C10D | c10c Hangul Syllable-C10C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c10f Hangul Syllable-C10F | c10e Hangul Syllable-C10E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c111 Hangul Syllable-C111 | c110 Hangul Syllable-C110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c113 Hangul Syllable-C113 | c112 Hangul Syllable-C112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c115 Hangul Syllable-C115 | c114 Hangul Syllable-C114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c117 Hangul Syllable-C117 | c116 Hangul Syllable-C116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c119 Hangul Syllable-C119 | c118 Hangul Syllable-C118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c11b Hangul Syllable-C11B | c11a Hangul Syllable-C11A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c11d Hangul Syllable-C11D | c11c Hangul Syllable-C11C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c11f Hangul Syllable-C11F | c11e Hangul Syllable-C11E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c121 Hangul Syllable-C121 | c120 Hangul Syllable-C120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c123 Hangul Syllable-C123 | c122 Hangul Syllable-C122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c125 Hangul Syllable-C125 | c124 Hangul Syllable-C124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c127 Hangul Syllable-C127 | c126 Hangul Syllable-C126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c129 Hangul Syllable-C129 | c128 Hangul Syllable-C128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c12b Hangul Syllable-C12B | c12a Hangul Syllable-C12A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c12d Hangul Syllable-C12D | c12c Hangul Syllable-C12C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c12f Hangul Syllable-C12F | c12e Hangul Syllable-C12E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c131 Hangul Syllable-C131 | c130 Hangul Syllable-C130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c133 Hangul Syllable-C133 | c132 Hangul Syllable-C132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c135 Hangul Syllable-C135 | c134 Hangul Syllable-C134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c137 Hangul Syllable-C137 | c136 Hangul Syllable-C136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c139 Hangul Syllable-C139 | c138 Hangul Syllable-C138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c13b Hangul Syllable-C13B | c13a Hangul Syllable-C13A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c13d Hangul Syllable-C13D | c13c Hangul Syllable-C13C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c13f Hangul Syllable-C13F | c13e Hangul Syllable-C13E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c141 Hangul Syllable-C141 | c140 Hangul Syllable-C140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c143 Hangul Syllable-C143 | c142 Hangul Syllable-C142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c145 Hangul Syllable-C145 | c144 Hangul Syllable-C144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c147 Hangul Syllable-C147 | c146 Hangul Syllable-C146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c149 Hangul Syllable-C149 | c148 Hangul Syllable-C148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c14b Hangul Syllable-C14B | c14a Hangul Syllable-C14A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c14d Hangul Syllable-C14D | c14c Hangul Syllable-C14C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c14f Hangul Syllable-C14F | c14e Hangul Syllable-C14E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c151 Hangul Syllable-C151 | c150 Hangul Syllable-C150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c153 Hangul Syllable-C153 | c152 Hangul Syllable-C152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c155 Hangul Syllable-C155 | c154 Hangul Syllable-C154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c157 Hangul Syllable-C157 | c156 Hangul Syllable-C156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c159 Hangul Syllable-C159 | c158 Hangul Syllable-C158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c15b Hangul Syllable-C15B | c15a Hangul Syllable-C15A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c15d Hangul Syllable-C15D | c15c Hangul Syllable-C15C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c15f Hangul Syllable-C15F | c15e Hangul Syllable-C15E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c161 Hangul Syllable-C161 | c160 Hangul Syllable-C160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c163 Hangul Syllable-C163 | c162 Hangul Syllable-C162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c165 Hangul Syllable-C165 | c164 Hangul Syllable-C164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c167 Hangul Syllable-C167 | c166 Hangul Syllable-C166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c169 Hangul Syllable-C169 | c168 Hangul Syllable-C168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c16b Hangul Syllable-C16B | c16a Hangul Syllable-C16A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c16d Hangul Syllable-C16D | c16c Hangul Syllable-C16C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c16f Hangul Syllable-C16F | c16e Hangul Syllable-C16E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c171 Hangul Syllable-C171 | c170 Hangul Syllable-C170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c173 Hangul Syllable-C173 | c172 Hangul Syllable-C172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c175 Hangul Syllable-C175 | c174 Hangul Syllable-C174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c177 Hangul Syllable-C177 | c176 Hangul Syllable-C176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c179 Hangul Syllable-C179 | c178 Hangul Syllable-C178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c17b Hangul Syllable-C17B | c17a Hangul Syllable-C17A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c17d Hangul Syllable-C17D | c17c Hangul Syllable-C17C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c17f Hangul Syllable-C17F | c17e Hangul Syllable-C17E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c181 Hangul Syllable-C181 | c180 Hangul Syllable-C180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c183 Hangul Syllable-C183 | c182 Hangul Syllable-C182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c185 Hangul Syllable-C185 | c184 Hangul Syllable-C184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c187 Hangul Syllable-C187 | c186 Hangul Syllable-C186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c189 Hangul Syllable-C189 | c188 Hangul Syllable-C188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c18b Hangul Syllable-C18B | c18a Hangul Syllable-C18A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c18d Hangul Syllable-C18D | c18c Hangul Syllable-C18C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c18f Hangul Syllable-C18F | c18e Hangul Syllable-C18E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c191 Hangul Syllable-C191 | c190 Hangul Syllable-C190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c193 Hangul Syllable-C193 | c192 Hangul Syllable-C192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c195 Hangul Syllable-C195 | c194 Hangul Syllable-C194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c197 Hangul Syllable-C197 | c196 Hangul Syllable-C196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c199 Hangul Syllable-C199 | c198 Hangul Syllable-C198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c19b Hangul Syllable-C19B | c19a Hangul Syllable-C19A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c19d Hangul Syllable-C19D | c19c Hangul Syllable-C19C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c19f Hangul Syllable-C19F | c19e Hangul Syllable-C19E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1a1 Hangul Syllable-C1A1 | c1a0 Hangul Syllable-C1A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1a3 Hangul Syllable-C1A3 | c1a2 Hangul Syllable-C1A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1a5 Hangul Syllable-C1A5 | c1a4 Hangul Syllable-C1A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1a7 Hangul Syllable-C1A7 | c1a6 Hangul Syllable-C1A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1a9 Hangul Syllable-C1A9 | c1a8 Hangul Syllable-C1A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1ab Hangul Syllable-C1AB | c1aa Hangul Syllable-C1AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1ad Hangul Syllable-C1AD | c1ac Hangul Syllable-C1AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1af Hangul Syllable-C1AF | c1ae Hangul Syllable-C1AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1b1 Hangul Syllable-C1B1 | c1b0 Hangul Syllable-C1B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1b3 Hangul Syllable-C1B3 | c1b2 Hangul Syllable-C1B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1b5 Hangul Syllable-C1B5 | c1b4 Hangul Syllable-C1B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1b7 Hangul Syllable-C1B7 | c1b6 Hangul Syllable-C1B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1b9 Hangul Syllable-C1B9 | c1b8 Hangul Syllable-C1B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1bb Hangul Syllable-C1BB | c1ba Hangul Syllable-C1BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1bd Hangul Syllable-C1BD | c1bc Hangul Syllable-C1BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1bf Hangul Syllable-C1BF | c1be Hangul Syllable-C1BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1c1 Hangul Syllable-C1C1 | c1c0 Hangul Syllable-C1C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1c3 Hangul Syllable-C1C3 | c1c2 Hangul Syllable-C1C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1c5 Hangul Syllable-C1C5 | c1c4 Hangul Syllable-C1C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1c7 Hangul Syllable-C1C7 | c1c6 Hangul Syllable-C1C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1c9 Hangul Syllable-C1C9 | c1c8 Hangul Syllable-C1C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1cb Hangul Syllable-C1CB | c1ca Hangul Syllable-C1CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1cd Hangul Syllable-C1CD | c1cc Hangul Syllable-C1CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1cf Hangul Syllable-C1CF | c1ce Hangul Syllable-C1CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1d1 Hangul Syllable-C1D1 | c1d0 Hangul Syllable-C1D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1d3 Hangul Syllable-C1D3 | c1d2 Hangul Syllable-C1D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1d5 Hangul Syllable-C1D5 | c1d4 Hangul Syllable-C1D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1d7 Hangul Syllable-C1D7 | c1d6 Hangul Syllable-C1D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1d9 Hangul Syllable-C1D9 | c1d8 Hangul Syllable-C1D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1db Hangul Syllable-C1DB | c1da Hangul Syllable-C1DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1dd Hangul Syllable-C1DD | c1dc Hangul Syllable-C1DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1df Hangul Syllable-C1DF | c1de Hangul Syllable-C1DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1e1 Hangul Syllable-C1E1 | c1e0 Hangul Syllable-C1E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1e3 Hangul Syllable-C1E3 | c1e2 Hangul Syllable-C1E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1e5 Hangul Syllable-C1E5 | c1e4 Hangul Syllable-C1E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1e7 Hangul Syllable-C1E7 | c1e6 Hangul Syllable-C1E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1e9 Hangul Syllable-C1E9 | c1e8 Hangul Syllable-C1E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1eb Hangul Syllable-C1EB | c1ea Hangul Syllable-C1EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1ed Hangul Syllable-C1ED | c1ec Hangul Syllable-C1EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1ef Hangul Syllable-C1EF | c1ee Hangul Syllable-C1EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1f1 Hangul Syllable-C1F1 | c1f0 Hangul Syllable-C1F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1f3 Hangul Syllable-C1F3 | c1f2 Hangul Syllable-C1F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1f5 Hangul Syllable-C1F5 | c1f4 Hangul Syllable-C1F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1f7 Hangul Syllable-C1F7 | c1f6 Hangul Syllable-C1F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1f9 Hangul Syllable-C1F9 | c1f8 Hangul Syllable-C1F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1fb Hangul Syllable-C1FB | c1fa Hangul Syllable-C1FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1fd Hangul Syllable-C1FD | c1fc Hangul Syllable-C1FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1ff Hangul Syllable-C1FF | c1fe Hangul Syllable-C1FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c201 Hangul Syllable-C201 | c200 Hangul Syllable-C200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c203 Hangul Syllable-C203 | c202 Hangul Syllable-C202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c205 Hangul Syllable-C205 | c204 Hangul Syllable-C204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c207 Hangul Syllable-C207 | c206 Hangul Syllable-C206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c209 Hangul Syllable-C209 | c208 Hangul Syllable-C208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c20b Hangul Syllable-C20B | c20a Hangul Syllable-C20A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c20d Hangul Syllable-C20D | c20c Hangul Syllable-C20C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c20f Hangul Syllable-C20F | c20e Hangul Syllable-C20E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c211 Hangul Syllable-C211 | c210 Hangul Syllable-C210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c213 Hangul Syllable-C213 | c212 Hangul Syllable-C212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c215 Hangul Syllable-C215 | c214 Hangul Syllable-C214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c217 Hangul Syllable-C217 | c216 Hangul Syllable-C216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c219 Hangul Syllable-C219 | c218 Hangul Syllable-C218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c21b Hangul Syllable-C21B | c21a Hangul Syllable-C21A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c21d Hangul Syllable-C21D | c21c Hangul Syllable-C21C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c21f Hangul Syllable-C21F | c21e Hangul Syllable-C21E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c221 Hangul Syllable-C221 | c220 Hangul Syllable-C220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c223 Hangul Syllable-C223 | c222 Hangul Syllable-C222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c225 Hangul Syllable-C225 | c224 Hangul Syllable-C224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c227 Hangul Syllable-C227 | c226 Hangul Syllable-C226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c229 Hangul Syllable-C229 | c228 Hangul Syllable-C228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c22b Hangul Syllable-C22B | c22a Hangul Syllable-C22A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c22d Hangul Syllable-C22D | c22c Hangul Syllable-C22C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c22f Hangul Syllable-C22F | c22e Hangul Syllable-C22E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c231 Hangul Syllable-C231 | c230 Hangul Syllable-C230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c233 Hangul Syllable-C233 | c232 Hangul Syllable-C232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c235 Hangul Syllable-C235 | c234 Hangul Syllable-C234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c237 Hangul Syllable-C237 | c236 Hangul Syllable-C236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c239 Hangul Syllable-C239 | c238 Hangul Syllable-C238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c23b Hangul Syllable-C23B | c23a Hangul Syllable-C23A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c23d Hangul Syllable-C23D | c23c Hangul Syllable-C23C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c23f Hangul Syllable-C23F | c23e Hangul Syllable-C23E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c241 Hangul Syllable-C241 | c240 Hangul Syllable-C240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c243 Hangul Syllable-C243 | c242 Hangul Syllable-C242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c245 Hangul Syllable-C245 | c244 Hangul Syllable-C244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c247 Hangul Syllable-C247 | c246 Hangul Syllable-C246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c249 Hangul Syllable-C249 | c248 Hangul Syllable-C248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c24b Hangul Syllable-C24B | c24a Hangul Syllable-C24A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c24d Hangul Syllable-C24D | c24c Hangul Syllable-C24C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c24f Hangul Syllable-C24F | c24e Hangul Syllable-C24E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c251 Hangul Syllable-C251 | c250 Hangul Syllable-C250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c253 Hangul Syllable-C253 | c252 Hangul Syllable-C252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c255 Hangul Syllable-C255 | c254 Hangul Syllable-C254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c257 Hangul Syllable-C257 | c256 Hangul Syllable-C256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c259 Hangul Syllable-C259 | c258 Hangul Syllable-C258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c25b Hangul Syllable-C25B | c25a Hangul Syllable-C25A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c25d Hangul Syllable-C25D | c25c Hangul Syllable-C25C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c25f Hangul Syllable-C25F | c25e Hangul Syllable-C25E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c261 Hangul Syllable-C261 | c260 Hangul Syllable-C260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c263 Hangul Syllable-C263 | c262 Hangul Syllable-C262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c265 Hangul Syllable-C265 | c264 Hangul Syllable-C264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c267 Hangul Syllable-C267 | c266 Hangul Syllable-C266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c269 Hangul Syllable-C269 | c268 Hangul Syllable-C268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c26b Hangul Syllable-C26B | c26a Hangul Syllable-C26A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c26d Hangul Syllable-C26D | c26c Hangul Syllable-C26C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c26f Hangul Syllable-C26F | c26e Hangul Syllable-C26E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c271 Hangul Syllable-C271 | c270 Hangul Syllable-C270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c273 Hangul Syllable-C273 | c272 Hangul Syllable-C272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c275 Hangul Syllable-C275 | c274 Hangul Syllable-C274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c277 Hangul Syllable-C277 | c276 Hangul Syllable-C276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c279 Hangul Syllable-C279 | c278 Hangul Syllable-C278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c27b Hangul Syllable-C27B | c27a Hangul Syllable-C27A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c27d Hangul Syllable-C27D | c27c Hangul Syllable-C27C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c27f Hangul Syllable-C27F | c27e Hangul Syllable-C27E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c281 Hangul Syllable-C281 | c280 Hangul Syllable-C280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c283 Hangul Syllable-C283 | c282 Hangul Syllable-C282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c285 Hangul Syllable-C285 | c284 Hangul Syllable-C284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c287 Hangul Syllable-C287 | c286 Hangul Syllable-C286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c289 Hangul Syllable-C289 | c288 Hangul Syllable-C288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c28b Hangul Syllable-C28B | c28a Hangul Syllable-C28A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c28d Hangul Syllable-C28D | c28c Hangul Syllable-C28C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c28f Hangul Syllable-C28F | c28e Hangul Syllable-C28E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c291 Hangul Syllable-C291 | c290 Hangul Syllable-C290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c293 Hangul Syllable-C293 | c292 Hangul Syllable-C292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c295 Hangul Syllable-C295 | c294 Hangul Syllable-C294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c297 Hangul Syllable-C297 | c296 Hangul Syllable-C296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c299 Hangul Syllable-C299 | c298 Hangul Syllable-C298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c29b Hangul Syllable-C29B | c29a Hangul Syllable-C29A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c29d Hangul Syllable-C29D | c29c Hangul Syllable-C29C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c29f Hangul Syllable-C29F | c29e Hangul Syllable-C29E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2a1 Hangul Syllable-C2A1 | c2a0 Hangul Syllable-C2A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2a3 Hangul Syllable-C2A3 | c2a2 Hangul Syllable-C2A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2a5 Hangul Syllable-C2A5 | c2a4 Hangul Syllable-C2A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2a7 Hangul Syllable-C2A7 | c2a6 Hangul Syllable-C2A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2a9 Hangul Syllable-C2A9 | c2a8 Hangul Syllable-C2A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2ab Hangul Syllable-C2AB | c2aa Hangul Syllable-C2AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2ad Hangul Syllable-C2AD | c2ac Hangul Syllable-C2AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2af Hangul Syllable-C2AF | c2ae Hangul Syllable-C2AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2b1 Hangul Syllable-C2B1 | c2b0 Hangul Syllable-C2B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2b3 Hangul Syllable-C2B3 | c2b2 Hangul Syllable-C2B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2b5 Hangul Syllable-C2B5 | c2b4 Hangul Syllable-C2B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2b7 Hangul Syllable-C2B7 | c2b6 Hangul Syllable-C2B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2b9 Hangul Syllable-C2B9 | c2b8 Hangul Syllable-C2B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2bb Hangul Syllable-C2BB | c2ba Hangul Syllable-C2BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2bd Hangul Syllable-C2BD | c2bc Hangul Syllable-C2BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2bf Hangul Syllable-C2BF | c2be Hangul Syllable-C2BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2c1 Hangul Syllable-C2C1 | c2c0 Hangul Syllable-C2C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2c3 Hangul Syllable-C2C3 | c2c2 Hangul Syllable-C2C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2c5 Hangul Syllable-C2C5 | c2c4 Hangul Syllable-C2C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2c7 Hangul Syllable-C2C7 | c2c6 Hangul Syllable-C2C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2c9 Hangul Syllable-C2C9 | c2c8 Hangul Syllable-C2C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2cb Hangul Syllable-C2CB | c2ca Hangul Syllable-C2CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2cd Hangul Syllable-C2CD | c2cc Hangul Syllable-C2CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2cf Hangul Syllable-C2CF | c2ce Hangul Syllable-C2CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2d1 Hangul Syllable-C2D1 | c2d0 Hangul Syllable-C2D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2d3 Hangul Syllable-C2D3 | c2d2 Hangul Syllable-C2D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2d5 Hangul Syllable-C2D5 | c2d4 Hangul Syllable-C2D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2d7 Hangul Syllable-C2D7 | c2d6 Hangul Syllable-C2D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2d9 Hangul Syllable-C2D9 | c2d8 Hangul Syllable-C2D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2db Hangul Syllable-C2DB | c2da Hangul Syllable-C2DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2dd Hangul Syllable-C2DD | c2dc Hangul Syllable-C2DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2df Hangul Syllable-C2DF | c2de Hangul Syllable-C2DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2e1 Hangul Syllable-C2E1 | c2e0 Hangul Syllable-C2E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2e3 Hangul Syllable-C2E3 | c2e2 Hangul Syllable-C2E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2e5 Hangul Syllable-C2E5 | c2e4 Hangul Syllable-C2E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2e7 Hangul Syllable-C2E7 | c2e6 Hangul Syllable-C2E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2e9 Hangul Syllable-C2E9 | c2e8 Hangul Syllable-C2E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2eb Hangul Syllable-C2EB | c2ea Hangul Syllable-C2EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2ed Hangul Syllable-C2ED | c2ec Hangul Syllable-C2EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2ef Hangul Syllable-C2EF | c2ee Hangul Syllable-C2EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2f1 Hangul Syllable-C2F1 | c2f0 Hangul Syllable-C2F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2f3 Hangul Syllable-C2F3 | c2f2 Hangul Syllable-C2F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2f5 Hangul Syllable-C2F5 | c2f4 Hangul Syllable-C2F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2f7 Hangul Syllable-C2F7 | c2f6 Hangul Syllable-C2F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2f9 Hangul Syllable-C2F9 | c2f8 Hangul Syllable-C2F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2fb Hangul Syllable-C2FB | c2fa Hangul Syllable-C2FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2fd Hangul Syllable-C2FD | c2fc Hangul Syllable-C2FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2ff Hangul Syllable-C2FF | c2fe Hangul Syllable-C2FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c301 Hangul Syllable-C301 | c300 Hangul Syllable-C300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c303 Hangul Syllable-C303 | c302 Hangul Syllable-C302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c305 Hangul Syllable-C305 | c304 Hangul Syllable-C304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c307 Hangul Syllable-C307 | c306 Hangul Syllable-C306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c309 Hangul Syllable-C309 | c308 Hangul Syllable-C308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c30b Hangul Syllable-C30B | c30a Hangul Syllable-C30A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c30d Hangul Syllable-C30D | c30c Hangul Syllable-C30C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c30f Hangul Syllable-C30F | c30e Hangul Syllable-C30E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c311 Hangul Syllable-C311 | c310 Hangul Syllable-C310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c313 Hangul Syllable-C313 | c312 Hangul Syllable-C312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c315 Hangul Syllable-C315 | c314 Hangul Syllable-C314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c317 Hangul Syllable-C317 | c316 Hangul Syllable-C316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c319 Hangul Syllable-C319 | c318 Hangul Syllable-C318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c31b Hangul Syllable-C31B | c31a Hangul Syllable-C31A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c31d Hangul Syllable-C31D | c31c Hangul Syllable-C31C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c31f Hangul Syllable-C31F | c31e Hangul Syllable-C31E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c321 Hangul Syllable-C321 | c320 Hangul Syllable-C320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c323 Hangul Syllable-C323 | c322 Hangul Syllable-C322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c325 Hangul Syllable-C325 | c324 Hangul Syllable-C324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c327 Hangul Syllable-C327 | c326 Hangul Syllable-C326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c329 Hangul Syllable-C329 | c328 Hangul Syllable-C328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c32b Hangul Syllable-C32B | c32a Hangul Syllable-C32A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c32d Hangul Syllable-C32D | c32c Hangul Syllable-C32C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c32f Hangul Syllable-C32F | c32e Hangul Syllable-C32E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c331 Hangul Syllable-C331 | c330 Hangul Syllable-C330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c333 Hangul Syllable-C333 | c332 Hangul Syllable-C332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c335 Hangul Syllable-C335 | c334 Hangul Syllable-C334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c337 Hangul Syllable-C337 | c336 Hangul Syllable-C336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c339 Hangul Syllable-C339 | c338 Hangul Syllable-C338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c33b Hangul Syllable-C33B | c33a Hangul Syllable-C33A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c33d Hangul Syllable-C33D | c33c Hangul Syllable-C33C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c33f Hangul Syllable-C33F | c33e Hangul Syllable-C33E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c341 Hangul Syllable-C341 | c340 Hangul Syllable-C340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c343 Hangul Syllable-C343 | c342 Hangul Syllable-C342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c345 Hangul Syllable-C345 | c344 Hangul Syllable-C344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c347 Hangul Syllable-C347 | c346 Hangul Syllable-C346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c349 Hangul Syllable-C349 | c348 Hangul Syllable-C348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c34b Hangul Syllable-C34B | c34a Hangul Syllable-C34A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c34d Hangul Syllable-C34D | c34c Hangul Syllable-C34C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c34f Hangul Syllable-C34F | c34e Hangul Syllable-C34E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c351 Hangul Syllable-C351 | c350 Hangul Syllable-C350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c353 Hangul Syllable-C353 | c352 Hangul Syllable-C352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c355 Hangul Syllable-C355 | c354 Hangul Syllable-C354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c357 Hangul Syllable-C357 | c356 Hangul Syllable-C356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c359 Hangul Syllable-C359 | c358 Hangul Syllable-C358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c35b Hangul Syllable-C35B | c35a Hangul Syllable-C35A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c35d Hangul Syllable-C35D | c35c Hangul Syllable-C35C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c35f Hangul Syllable-C35F | c35e Hangul Syllable-C35E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c361 Hangul Syllable-C361 | c360 Hangul Syllable-C360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c363 Hangul Syllable-C363 | c362 Hangul Syllable-C362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c365 Hangul Syllable-C365 | c364 Hangul Syllable-C364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c367 Hangul Syllable-C367 | c366 Hangul Syllable-C366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c369 Hangul Syllable-C369 | c368 Hangul Syllable-C368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c36b Hangul Syllable-C36B | c36a Hangul Syllable-C36A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c36d Hangul Syllable-C36D | c36c Hangul Syllable-C36C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c36f Hangul Syllable-C36F | c36e Hangul Syllable-C36E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c371 Hangul Syllable-C371 | c370 Hangul Syllable-C370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c373 Hangul Syllable-C373 | c372 Hangul Syllable-C372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c375 Hangul Syllable-C375 | c374 Hangul Syllable-C374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c377 Hangul Syllable-C377 | c376 Hangul Syllable-C376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c379 Hangul Syllable-C379 | c378 Hangul Syllable-C378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c37b Hangul Syllable-C37B | c37a Hangul Syllable-C37A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c37d Hangul Syllable-C37D | c37c Hangul Syllable-C37C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c37f Hangul Syllable-C37F | c37e Hangul Syllable-C37E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c381 Hangul Syllable-C381 | c380 Hangul Syllable-C380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c383 Hangul Syllable-C383 | c382 Hangul Syllable-C382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c385 Hangul Syllable-C385 | c384 Hangul Syllable-C384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c387 Hangul Syllable-C387 | c386 Hangul Syllable-C386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c389 Hangul Syllable-C389 | c388 Hangul Syllable-C388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c38b Hangul Syllable-C38B | c38a Hangul Syllable-C38A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c38d Hangul Syllable-C38D | c38c Hangul Syllable-C38C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c38f Hangul Syllable-C38F | c38e Hangul Syllable-C38E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c391 Hangul Syllable-C391 | c390 Hangul Syllable-C390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c393 Hangul Syllable-C393 | c392 Hangul Syllable-C392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c395 Hangul Syllable-C395 | c394 Hangul Syllable-C394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c397 Hangul Syllable-C397 | c396 Hangul Syllable-C396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c399 Hangul Syllable-C399 | c398 Hangul Syllable-C398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c39b Hangul Syllable-C39B | c39a Hangul Syllable-C39A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c39d Hangul Syllable-C39D | c39c Hangul Syllable-C39C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c39f Hangul Syllable-C39F | c39e Hangul Syllable-C39E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3a1 Hangul Syllable-C3A1 | c3a0 Hangul Syllable-C3A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3a3 Hangul Syllable-C3A3 | c3a2 Hangul Syllable-C3A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3a5 Hangul Syllable-C3A5 | c3a4 Hangul Syllable-C3A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3a7 Hangul Syllable-C3A7 | c3a6 Hangul Syllable-C3A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3a9 Hangul Syllable-C3A9 | c3a8 Hangul Syllable-C3A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3ab Hangul Syllable-C3AB | c3aa Hangul Syllable-C3AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3ad Hangul Syllable-C3AD | c3ac Hangul Syllable-C3AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3af Hangul Syllable-C3AF | c3ae Hangul Syllable-C3AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3b1 Hangul Syllable-C3B1 | c3b0 Hangul Syllable-C3B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3b3 Hangul Syllable-C3B3 | c3b2 Hangul Syllable-C3B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3b5 Hangul Syllable-C3B5 | c3b4 Hangul Syllable-C3B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3b7 Hangul Syllable-C3B7 | c3b6 Hangul Syllable-C3B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3b9 Hangul Syllable-C3B9 | c3b8 Hangul Syllable-C3B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3bb Hangul Syllable-C3BB | c3ba Hangul Syllable-C3BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3bd Hangul Syllable-C3BD | c3bc Hangul Syllable-C3BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3bf Hangul Syllable-C3BF | c3be Hangul Syllable-C3BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3c1 Hangul Syllable-C3C1 | c3c0 Hangul Syllable-C3C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3c3 Hangul Syllable-C3C3 | c3c2 Hangul Syllable-C3C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3c5 Hangul Syllable-C3C5 | c3c4 Hangul Syllable-C3C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3c7 Hangul Syllable-C3C7 | c3c6 Hangul Syllable-C3C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3c9 Hangul Syllable-C3C9 | c3c8 Hangul Syllable-C3C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3cb Hangul Syllable-C3CB | c3ca Hangul Syllable-C3CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3cd Hangul Syllable-C3CD | c3cc Hangul Syllable-C3CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3cf Hangul Syllable-C3CF | c3ce Hangul Syllable-C3CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3d1 Hangul Syllable-C3D1 | c3d0 Hangul Syllable-C3D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3d3 Hangul Syllable-C3D3 | c3d2 Hangul Syllable-C3D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3d5 Hangul Syllable-C3D5 | c3d4 Hangul Syllable-C3D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3d7 Hangul Syllable-C3D7 | c3d6 Hangul Syllable-C3D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3d9 Hangul Syllable-C3D9 | c3d8 Hangul Syllable-C3D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3db Hangul Syllable-C3DB | c3da Hangul Syllable-C3DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3dd Hangul Syllable-C3DD | c3dc Hangul Syllable-C3DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3df Hangul Syllable-C3DF | c3de Hangul Syllable-C3DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3e1 Hangul Syllable-C3E1 | c3e0 Hangul Syllable-C3E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3e3 Hangul Syllable-C3E3 | c3e2 Hangul Syllable-C3E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3e5 Hangul Syllable-C3E5 | c3e4 Hangul Syllable-C3E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3e7 Hangul Syllable-C3E7 | c3e6 Hangul Syllable-C3E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3e9 Hangul Syllable-C3E9 | c3e8 Hangul Syllable-C3E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3eb Hangul Syllable-C3EB | c3ea Hangul Syllable-C3EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3ed Hangul Syllable-C3ED | c3ec Hangul Syllable-C3EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3ef Hangul Syllable-C3EF | c3ee Hangul Syllable-C3EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3f1 Hangul Syllable-C3F1 | c3f0 Hangul Syllable-C3F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3f3 Hangul Syllable-C3F3 | c3f2 Hangul Syllable-C3F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3f5 Hangul Syllable-C3F5 | c3f4 Hangul Syllable-C3F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3f7 Hangul Syllable-C3F7 | c3f6 Hangul Syllable-C3F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3f9 Hangul Syllable-C3F9 | c3f8 Hangul Syllable-C3F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3fb Hangul Syllable-C3FB | c3fa Hangul Syllable-C3FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3fd Hangul Syllable-C3FD | c3fc Hangul Syllable-C3FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3ff Hangul Syllable-C3FF | c3fe Hangul Syllable-C3FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c401 Hangul Syllable-C401 | c400 Hangul Syllable-C400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c403 Hangul Syllable-C403 | c402 Hangul Syllable-C402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c405 Hangul Syllable-C405 | c404 Hangul Syllable-C404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c407 Hangul Syllable-C407 | c406 Hangul Syllable-C406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c409 Hangul Syllable-C409 | c408 Hangul Syllable-C408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c40b Hangul Syllable-C40B | c40a Hangul Syllable-C40A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c40d Hangul Syllable-C40D | c40c Hangul Syllable-C40C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c40f Hangul Syllable-C40F | c40e Hangul Syllable-C40E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c411 Hangul Syllable-C411 | c410 Hangul Syllable-C410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c413 Hangul Syllable-C413 | c412 Hangul Syllable-C412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c415 Hangul Syllable-C415 | c414 Hangul Syllable-C414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c417 Hangul Syllable-C417 | c416 Hangul Syllable-C416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c419 Hangul Syllable-C419 | c418 Hangul Syllable-C418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c41b Hangul Syllable-C41B | c41a Hangul Syllable-C41A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c41d Hangul Syllable-C41D | c41c Hangul Syllable-C41C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c41f Hangul Syllable-C41F | c41e Hangul Syllable-C41E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c421 Hangul Syllable-C421 | c420 Hangul Syllable-C420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c423 Hangul Syllable-C423 | c422 Hangul Syllable-C422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c425 Hangul Syllable-C425 | c424 Hangul Syllable-C424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c427 Hangul Syllable-C427 | c426 Hangul Syllable-C426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c429 Hangul Syllable-C429 | c428 Hangul Syllable-C428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c42b Hangul Syllable-C42B | c42a Hangul Syllable-C42A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c42d Hangul Syllable-C42D | c42c Hangul Syllable-C42C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c42f Hangul Syllable-C42F | c42e Hangul Syllable-C42E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c431 Hangul Syllable-C431 | c430 Hangul Syllable-C430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c433 Hangul Syllable-C433 | c432 Hangul Syllable-C432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c435 Hangul Syllable-C435 | c434 Hangul Syllable-C434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c437 Hangul Syllable-C437 | c436 Hangul Syllable-C436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c439 Hangul Syllable-C439 | c438 Hangul Syllable-C438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c43b Hangul Syllable-C43B | c43a Hangul Syllable-C43A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c43d Hangul Syllable-C43D | c43c Hangul Syllable-C43C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c43f Hangul Syllable-C43F | c43e Hangul Syllable-C43E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c441 Hangul Syllable-C441 | c440 Hangul Syllable-C440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c443 Hangul Syllable-C443 | c442 Hangul Syllable-C442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c445 Hangul Syllable-C445 | c444 Hangul Syllable-C444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c447 Hangul Syllable-C447 | c446 Hangul Syllable-C446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c449 Hangul Syllable-C449 | c448 Hangul Syllable-C448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c44b Hangul Syllable-C44B | c44a Hangul Syllable-C44A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c44d Hangul Syllable-C44D | c44c Hangul Syllable-C44C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c44f Hangul Syllable-C44F | c44e Hangul Syllable-C44E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c451 Hangul Syllable-C451 | c450 Hangul Syllable-C450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c453 Hangul Syllable-C453 | c452 Hangul Syllable-C452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c455 Hangul Syllable-C455 | c454 Hangul Syllable-C454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c457 Hangul Syllable-C457 | c456 Hangul Syllable-C456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c459 Hangul Syllable-C459 | c458 Hangul Syllable-C458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c45b Hangul Syllable-C45B | c45a Hangul Syllable-C45A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c45d Hangul Syllable-C45D | c45c Hangul Syllable-C45C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c45f Hangul Syllable-C45F | c45e Hangul Syllable-C45E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c461 Hangul Syllable-C461 | c460 Hangul Syllable-C460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c463 Hangul Syllable-C463 | c462 Hangul Syllable-C462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c465 Hangul Syllable-C465 | c464 Hangul Syllable-C464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c467 Hangul Syllable-C467 | c466 Hangul Syllable-C466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c469 Hangul Syllable-C469 | c468 Hangul Syllable-C468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c46b Hangul Syllable-C46B | c46a Hangul Syllable-C46A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c46d Hangul Syllable-C46D | c46c Hangul Syllable-C46C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c46f Hangul Syllable-C46F | c46e Hangul Syllable-C46E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c471 Hangul Syllable-C471 | c470 Hangul Syllable-C470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c473 Hangul Syllable-C473 | c472 Hangul Syllable-C472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c475 Hangul Syllable-C475 | c474 Hangul Syllable-C474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c477 Hangul Syllable-C477 | c476 Hangul Syllable-C476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c479 Hangul Syllable-C479 | c478 Hangul Syllable-C478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c47b Hangul Syllable-C47B | c47a Hangul Syllable-C47A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c47d Hangul Syllable-C47D | c47c Hangul Syllable-C47C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c47f Hangul Syllable-C47F | c47e Hangul Syllable-C47E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c481 Hangul Syllable-C481 | c480 Hangul Syllable-C480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c483 Hangul Syllable-C483 | c482 Hangul Syllable-C482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c485 Hangul Syllable-C485 | c484 Hangul Syllable-C484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c487 Hangul Syllable-C487 | c486 Hangul Syllable-C486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c489 Hangul Syllable-C489 | c488 Hangul Syllable-C488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c48b Hangul Syllable-C48B | c48a Hangul Syllable-C48A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c48d Hangul Syllable-C48D | c48c Hangul Syllable-C48C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c48f Hangul Syllable-C48F | c48e Hangul Syllable-C48E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c491 Hangul Syllable-C491 | c490 Hangul Syllable-C490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c493 Hangul Syllable-C493 | c492 Hangul Syllable-C492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c495 Hangul Syllable-C495 | c494 Hangul Syllable-C494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c497 Hangul Syllable-C497 | c496 Hangul Syllable-C496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c499 Hangul Syllable-C499 | c498 Hangul Syllable-C498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c49b Hangul Syllable-C49B | c49a Hangul Syllable-C49A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c49d Hangul Syllable-C49D | c49c Hangul Syllable-C49C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c49f Hangul Syllable-C49F | c49e Hangul Syllable-C49E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4a1 Hangul Syllable-C4A1 | c4a0 Hangul Syllable-C4A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4a3 Hangul Syllable-C4A3 | c4a2 Hangul Syllable-C4A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4a5 Hangul Syllable-C4A5 | c4a4 Hangul Syllable-C4A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4a7 Hangul Syllable-C4A7 | c4a6 Hangul Syllable-C4A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4a9 Hangul Syllable-C4A9 | c4a8 Hangul Syllable-C4A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4ab Hangul Syllable-C4AB | c4aa Hangul Syllable-C4AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4ad Hangul Syllable-C4AD | c4ac Hangul Syllable-C4AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4af Hangul Syllable-C4AF | c4ae Hangul Syllable-C4AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4b1 Hangul Syllable-C4B1 | c4b0 Hangul Syllable-C4B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4b3 Hangul Syllable-C4B3 | c4b2 Hangul Syllable-C4B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4b5 Hangul Syllable-C4B5 | c4b4 Hangul Syllable-C4B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4b7 Hangul Syllable-C4B7 | c4b6 Hangul Syllable-C4B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4b9 Hangul Syllable-C4B9 | c4b8 Hangul Syllable-C4B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4bb Hangul Syllable-C4BB | c4ba Hangul Syllable-C4BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4bd Hangul Syllable-C4BD | c4bc Hangul Syllable-C4BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4bf Hangul Syllable-C4BF | c4be Hangul Syllable-C4BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4c1 Hangul Syllable-C4C1 | c4c0 Hangul Syllable-C4C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4c3 Hangul Syllable-C4C3 | c4c2 Hangul Syllable-C4C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4c5 Hangul Syllable-C4C5 | c4c4 Hangul Syllable-C4C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4c7 Hangul Syllable-C4C7 | c4c6 Hangul Syllable-C4C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4c9 Hangul Syllable-C4C9 | c4c8 Hangul Syllable-C4C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4cb Hangul Syllable-C4CB | c4ca Hangul Syllable-C4CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4cd Hangul Syllable-C4CD | c4cc Hangul Syllable-C4CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4cf Hangul Syllable-C4CF | c4ce Hangul Syllable-C4CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4d1 Hangul Syllable-C4D1 | c4d0 Hangul Syllable-C4D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4d3 Hangul Syllable-C4D3 | c4d2 Hangul Syllable-C4D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4d5 Hangul Syllable-C4D5 | c4d4 Hangul Syllable-C4D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4d7 Hangul Syllable-C4D7 | c4d6 Hangul Syllable-C4D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4d9 Hangul Syllable-C4D9 | c4d8 Hangul Syllable-C4D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4db Hangul Syllable-C4DB | c4da Hangul Syllable-C4DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4dd Hangul Syllable-C4DD | c4dc Hangul Syllable-C4DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4df Hangul Syllable-C4DF | c4de Hangul Syllable-C4DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4e1 Hangul Syllable-C4E1 | c4e0 Hangul Syllable-C4E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4e3 Hangul Syllable-C4E3 | c4e2 Hangul Syllable-C4E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4e5 Hangul Syllable-C4E5 | c4e4 Hangul Syllable-C4E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4e7 Hangul Syllable-C4E7 | c4e6 Hangul Syllable-C4E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4e9 Hangul Syllable-C4E9 | c4e8 Hangul Syllable-C4E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4eb Hangul Syllable-C4EB | c4ea Hangul Syllable-C4EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4ed Hangul Syllable-C4ED | c4ec Hangul Syllable-C4EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4ef Hangul Syllable-C4EF | c4ee Hangul Syllable-C4EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4f1 Hangul Syllable-C4F1 | c4f0 Hangul Syllable-C4F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4f3 Hangul Syllable-C4F3 | c4f2 Hangul Syllable-C4F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4f5 Hangul Syllable-C4F5 | c4f4 Hangul Syllable-C4F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4f7 Hangul Syllable-C4F7 | c4f6 Hangul Syllable-C4F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4f9 Hangul Syllable-C4F9 | c4f8 Hangul Syllable-C4F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4fb Hangul Syllable-C4FB | c4fa Hangul Syllable-C4FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4fd Hangul Syllable-C4FD | c4fc Hangul Syllable-C4FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4ff Hangul Syllable-C4FF | c4fe Hangul Syllable-C4FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c501 Hangul Syllable-C501 | c500 Hangul Syllable-C500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c503 Hangul Syllable-C503 | c502 Hangul Syllable-C502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c505 Hangul Syllable-C505 | c504 Hangul Syllable-C504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c507 Hangul Syllable-C507 | c506 Hangul Syllable-C506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c509 Hangul Syllable-C509 | c508 Hangul Syllable-C508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c50b Hangul Syllable-C50B | c50a Hangul Syllable-C50A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c50d Hangul Syllable-C50D | c50c Hangul Syllable-C50C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c50f Hangul Syllable-C50F | c50e Hangul Syllable-C50E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c511 Hangul Syllable-C511 | c510 Hangul Syllable-C510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c513 Hangul Syllable-C513 | c512 Hangul Syllable-C512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c515 Hangul Syllable-C515 | c514 Hangul Syllable-C514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c517 Hangul Syllable-C517 | c516 Hangul Syllable-C516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c519 Hangul Syllable-C519 | c518 Hangul Syllable-C518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c51b Hangul Syllable-C51B | c51a Hangul Syllable-C51A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c51d Hangul Syllable-C51D | c51c Hangul Syllable-C51C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c51f Hangul Syllable-C51F | c51e Hangul Syllable-C51E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c521 Hangul Syllable-C521 | c520 Hangul Syllable-C520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c523 Hangul Syllable-C523 | c522 Hangul Syllable-C522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c525 Hangul Syllable-C525 | c524 Hangul Syllable-C524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c527 Hangul Syllable-C527 | c526 Hangul Syllable-C526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c529 Hangul Syllable-C529 | c528 Hangul Syllable-C528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c52b Hangul Syllable-C52B | c52a Hangul Syllable-C52A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c52d Hangul Syllable-C52D | c52c Hangul Syllable-C52C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c52f Hangul Syllable-C52F | c52e Hangul Syllable-C52E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c531 Hangul Syllable-C531 | c530 Hangul Syllable-C530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c533 Hangul Syllable-C533 | c532 Hangul Syllable-C532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c535 Hangul Syllable-C535 | c534 Hangul Syllable-C534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c537 Hangul Syllable-C537 | c536 Hangul Syllable-C536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c539 Hangul Syllable-C539 | c538 Hangul Syllable-C538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c53b Hangul Syllable-C53B | c53a Hangul Syllable-C53A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c53d Hangul Syllable-C53D | c53c Hangul Syllable-C53C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c53f Hangul Syllable-C53F | c53e Hangul Syllable-C53E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c541 Hangul Syllable-C541 | c540 Hangul Syllable-C540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c543 Hangul Syllable-C543 | c542 Hangul Syllable-C542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c545 Hangul Syllable-C545 | c544 Hangul Syllable-C544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c547 Hangul Syllable-C547 | c546 Hangul Syllable-C546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c549 Hangul Syllable-C549 | c548 Hangul Syllable-C548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c54b Hangul Syllable-C54B | c54a Hangul Syllable-C54A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c54d Hangul Syllable-C54D | c54c Hangul Syllable-C54C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c54f Hangul Syllable-C54F | c54e Hangul Syllable-C54E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c551 Hangul Syllable-C551 | c550 Hangul Syllable-C550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c553 Hangul Syllable-C553 | c552 Hangul Syllable-C552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c555 Hangul Syllable-C555 | c554 Hangul Syllable-C554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c557 Hangul Syllable-C557 | c556 Hangul Syllable-C556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c559 Hangul Syllable-C559 | c558 Hangul Syllable-C558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c55b Hangul Syllable-C55B | c55a Hangul Syllable-C55A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c55d Hangul Syllable-C55D | c55c Hangul Syllable-C55C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c55f Hangul Syllable-C55F | c55e Hangul Syllable-C55E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c561 Hangul Syllable-C561 | c560 Hangul Syllable-C560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c563 Hangul Syllable-C563 | c562 Hangul Syllable-C562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c565 Hangul Syllable-C565 | c564 Hangul Syllable-C564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c567 Hangul Syllable-C567 | c566 Hangul Syllable-C566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c569 Hangul Syllable-C569 | c568 Hangul Syllable-C568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c56b Hangul Syllable-C56B | c56a Hangul Syllable-C56A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c56d Hangul Syllable-C56D | c56c Hangul Syllable-C56C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c56f Hangul Syllable-C56F | c56e Hangul Syllable-C56E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c571 Hangul Syllable-C571 | c570 Hangul Syllable-C570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c573 Hangul Syllable-C573 | c572 Hangul Syllable-C572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c575 Hangul Syllable-C575 | c574 Hangul Syllable-C574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c577 Hangul Syllable-C577 | c576 Hangul Syllable-C576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c579 Hangul Syllable-C579 | c578 Hangul Syllable-C578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c57b Hangul Syllable-C57B | c57a Hangul Syllable-C57A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c57d Hangul Syllable-C57D | c57c Hangul Syllable-C57C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c57f Hangul Syllable-C57F | c57e Hangul Syllable-C57E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c581 Hangul Syllable-C581 | c580 Hangul Syllable-C580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c583 Hangul Syllable-C583 | c582 Hangul Syllable-C582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c585 Hangul Syllable-C585 | c584 Hangul Syllable-C584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c587 Hangul Syllable-C587 | c586 Hangul Syllable-C586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c589 Hangul Syllable-C589 | c588 Hangul Syllable-C588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c58b Hangul Syllable-C58B | c58a Hangul Syllable-C58A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c58d Hangul Syllable-C58D | c58c Hangul Syllable-C58C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c58f Hangul Syllable-C58F | c58e Hangul Syllable-C58E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c591 Hangul Syllable-C591 | c590 Hangul Syllable-C590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c593 Hangul Syllable-C593 | c592 Hangul Syllable-C592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c595 Hangul Syllable-C595 | c594 Hangul Syllable-C594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c597 Hangul Syllable-C597 | c596 Hangul Syllable-C596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c599 Hangul Syllable-C599 | c598 Hangul Syllable-C598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c59b Hangul Syllable-C59B | c59a Hangul Syllable-C59A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c59d Hangul Syllable-C59D | c59c Hangul Syllable-C59C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c59f Hangul Syllable-C59F | c59e Hangul Syllable-C59E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5a1 Hangul Syllable-C5A1 | c5a0 Hangul Syllable-C5A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5a3 Hangul Syllable-C5A3 | c5a2 Hangul Syllable-C5A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5a5 Hangul Syllable-C5A5 | c5a4 Hangul Syllable-C5A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5a7 Hangul Syllable-C5A7 | c5a6 Hangul Syllable-C5A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5a9 Hangul Syllable-C5A9 | c5a8 Hangul Syllable-C5A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5ab Hangul Syllable-C5AB | c5aa Hangul Syllable-C5AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5ad Hangul Syllable-C5AD | c5ac Hangul Syllable-C5AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5af Hangul Syllable-C5AF | c5ae Hangul Syllable-C5AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5b1 Hangul Syllable-C5B1 | c5b0 Hangul Syllable-C5B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5b3 Hangul Syllable-C5B3 | c5b2 Hangul Syllable-C5B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5b5 Hangul Syllable-C5B5 | c5b4 Hangul Syllable-C5B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5b7 Hangul Syllable-C5B7 | c5b6 Hangul Syllable-C5B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5b9 Hangul Syllable-C5B9 | c5b8 Hangul Syllable-C5B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5bb Hangul Syllable-C5BB | c5ba Hangul Syllable-C5BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5bd Hangul Syllable-C5BD | c5bc Hangul Syllable-C5BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5bf Hangul Syllable-C5BF | c5be Hangul Syllable-C5BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5c1 Hangul Syllable-C5C1 | c5c0 Hangul Syllable-C5C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5c3 Hangul Syllable-C5C3 | c5c2 Hangul Syllable-C5C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5c5 Hangul Syllable-C5C5 | c5c4 Hangul Syllable-C5C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5c7 Hangul Syllable-C5C7 | c5c6 Hangul Syllable-C5C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5c9 Hangul Syllable-C5C9 | c5c8 Hangul Syllable-C5C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5cb Hangul Syllable-C5CB | c5ca Hangul Syllable-C5CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5cd Hangul Syllable-C5CD | c5cc Hangul Syllable-C5CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5cf Hangul Syllable-C5CF | c5ce Hangul Syllable-C5CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5d1 Hangul Syllable-C5D1 | c5d0 Hangul Syllable-C5D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5d3 Hangul Syllable-C5D3 | c5d2 Hangul Syllable-C5D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5d5 Hangul Syllable-C5D5 | c5d4 Hangul Syllable-C5D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5d7 Hangul Syllable-C5D7 | c5d6 Hangul Syllable-C5D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5d9 Hangul Syllable-C5D9 | c5d8 Hangul Syllable-C5D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5db Hangul Syllable-C5DB | c5da Hangul Syllable-C5DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5dd Hangul Syllable-C5DD | c5dc Hangul Syllable-C5DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5df Hangul Syllable-C5DF | c5de Hangul Syllable-C5DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5e1 Hangul Syllable-C5E1 | c5e0 Hangul Syllable-C5E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5e3 Hangul Syllable-C5E3 | c5e2 Hangul Syllable-C5E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5e5 Hangul Syllable-C5E5 | c5e4 Hangul Syllable-C5E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5e7 Hangul Syllable-C5E7 | c5e6 Hangul Syllable-C5E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5e9 Hangul Syllable-C5E9 | c5e8 Hangul Syllable-C5E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5eb Hangul Syllable-C5EB | c5ea Hangul Syllable-C5EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5ed Hangul Syllable-C5ED | c5ec Hangul Syllable-C5EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5ef Hangul Syllable-C5EF | c5ee Hangul Syllable-C5EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5f1 Hangul Syllable-C5F1 | c5f0 Hangul Syllable-C5F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5f3 Hangul Syllable-C5F3 | c5f2 Hangul Syllable-C5F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5f5 Hangul Syllable-C5F5 | c5f4 Hangul Syllable-C5F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5f7 Hangul Syllable-C5F7 | c5f6 Hangul Syllable-C5F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5f9 Hangul Syllable-C5F9 | c5f8 Hangul Syllable-C5F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5fb Hangul Syllable-C5FB | c5fa Hangul Syllable-C5FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5fd Hangul Syllable-C5FD | c5fc Hangul Syllable-C5FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5ff Hangul Syllable-C5FF | c5fe Hangul Syllable-C5FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c601 Hangul Syllable-C601 | c600 Hangul Syllable-C600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c603 Hangul Syllable-C603 | c602 Hangul Syllable-C602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c605 Hangul Syllable-C605 | c604 Hangul Syllable-C604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c607 Hangul Syllable-C607 | c606 Hangul Syllable-C606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c609 Hangul Syllable-C609 | c608 Hangul Syllable-C608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c60b Hangul Syllable-C60B | c60a Hangul Syllable-C60A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c60d Hangul Syllable-C60D | c60c Hangul Syllable-C60C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c60f Hangul Syllable-C60F | c60e Hangul Syllable-C60E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c611 Hangul Syllable-C611 | c610 Hangul Syllable-C610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c613 Hangul Syllable-C613 | c612 Hangul Syllable-C612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c615 Hangul Syllable-C615 | c614 Hangul Syllable-C614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c617 Hangul Syllable-C617 | c616 Hangul Syllable-C616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c619 Hangul Syllable-C619 | c618 Hangul Syllable-C618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c61b Hangul Syllable-C61B | c61a Hangul Syllable-C61A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c61d Hangul Syllable-C61D | c61c Hangul Syllable-C61C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c61f Hangul Syllable-C61F | c61e Hangul Syllable-C61E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c621 Hangul Syllable-C621 | c620 Hangul Syllable-C620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c623 Hangul Syllable-C623 | c622 Hangul Syllable-C622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c625 Hangul Syllable-C625 | c624 Hangul Syllable-C624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c627 Hangul Syllable-C627 | c626 Hangul Syllable-C626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c629 Hangul Syllable-C629 | c628 Hangul Syllable-C628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c62b Hangul Syllable-C62B | c62a Hangul Syllable-C62A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c62d Hangul Syllable-C62D | c62c Hangul Syllable-C62C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c62f Hangul Syllable-C62F | c62e Hangul Syllable-C62E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c631 Hangul Syllable-C631 | c630 Hangul Syllable-C630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c633 Hangul Syllable-C633 | c632 Hangul Syllable-C632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c635 Hangul Syllable-C635 | c634 Hangul Syllable-C634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c637 Hangul Syllable-C637 | c636 Hangul Syllable-C636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c639 Hangul Syllable-C639 | c638 Hangul Syllable-C638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c63b Hangul Syllable-C63B | c63a Hangul Syllable-C63A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c63d Hangul Syllable-C63D | c63c Hangul Syllable-C63C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c63f Hangul Syllable-C63F | c63e Hangul Syllable-C63E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c641 Hangul Syllable-C641 | c640 Hangul Syllable-C640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c643 Hangul Syllable-C643 | c642 Hangul Syllable-C642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c645 Hangul Syllable-C645 | c644 Hangul Syllable-C644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c647 Hangul Syllable-C647 | c646 Hangul Syllable-C646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c649 Hangul Syllable-C649 | c648 Hangul Syllable-C648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c64b Hangul Syllable-C64B | c64a Hangul Syllable-C64A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c64d Hangul Syllable-C64D | c64c Hangul Syllable-C64C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c64f Hangul Syllable-C64F | c64e Hangul Syllable-C64E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c651 Hangul Syllable-C651 | c650 Hangul Syllable-C650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c653 Hangul Syllable-C653 | c652 Hangul Syllable-C652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c655 Hangul Syllable-C655 | c654 Hangul Syllable-C654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c657 Hangul Syllable-C657 | c656 Hangul Syllable-C656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c659 Hangul Syllable-C659 | c658 Hangul Syllable-C658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c65b Hangul Syllable-C65B | c65a Hangul Syllable-C65A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c65d Hangul Syllable-C65D | c65c Hangul Syllable-C65C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c65f Hangul Syllable-C65F | c65e Hangul Syllable-C65E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c661 Hangul Syllable-C661 | c660 Hangul Syllable-C660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c663 Hangul Syllable-C663 | c662 Hangul Syllable-C662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c665 Hangul Syllable-C665 | c664 Hangul Syllable-C664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c667 Hangul Syllable-C667 | c666 Hangul Syllable-C666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c669 Hangul Syllable-C669 | c668 Hangul Syllable-C668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c66b Hangul Syllable-C66B | c66a Hangul Syllable-C66A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c66d Hangul Syllable-C66D | c66c Hangul Syllable-C66C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c66f Hangul Syllable-C66F | c66e Hangul Syllable-C66E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c671 Hangul Syllable-C671 | c670 Hangul Syllable-C670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c673 Hangul Syllable-C673 | c672 Hangul Syllable-C672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c675 Hangul Syllable-C675 | c674 Hangul Syllable-C674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c677 Hangul Syllable-C677 | c676 Hangul Syllable-C676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c679 Hangul Syllable-C679 | c678 Hangul Syllable-C678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c67b Hangul Syllable-C67B | c67a Hangul Syllable-C67A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c67d Hangul Syllable-C67D | c67c Hangul Syllable-C67C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c67f Hangul Syllable-C67F | c67e Hangul Syllable-C67E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c681 Hangul Syllable-C681 | c680 Hangul Syllable-C680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c683 Hangul Syllable-C683 | c682 Hangul Syllable-C682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c685 Hangul Syllable-C685 | c684 Hangul Syllable-C684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c687 Hangul Syllable-C687 | c686 Hangul Syllable-C686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c689 Hangul Syllable-C689 | c688 Hangul Syllable-C688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c68b Hangul Syllable-C68B | c68a Hangul Syllable-C68A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c68d Hangul Syllable-C68D | c68c Hangul Syllable-C68C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c68f Hangul Syllable-C68F | c68e Hangul Syllable-C68E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c691 Hangul Syllable-C691 | c690 Hangul Syllable-C690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c693 Hangul Syllable-C693 | c692 Hangul Syllable-C692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c695 Hangul Syllable-C695 | c694 Hangul Syllable-C694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c697 Hangul Syllable-C697 | c696 Hangul Syllable-C696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c699 Hangul Syllable-C699 | c698 Hangul Syllable-C698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c69b Hangul Syllable-C69B | c69a Hangul Syllable-C69A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c69d Hangul Syllable-C69D | c69c Hangul Syllable-C69C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c69f Hangul Syllable-C69F | c69e Hangul Syllable-C69E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6a1 Hangul Syllable-C6A1 | c6a0 Hangul Syllable-C6A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6a3 Hangul Syllable-C6A3 | c6a2 Hangul Syllable-C6A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6a5 Hangul Syllable-C6A5 | c6a4 Hangul Syllable-C6A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6a7 Hangul Syllable-C6A7 | c6a6 Hangul Syllable-C6A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6a9 Hangul Syllable-C6A9 | c6a8 Hangul Syllable-C6A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6ab Hangul Syllable-C6AB | c6aa Hangul Syllable-C6AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6ad Hangul Syllable-C6AD | c6ac Hangul Syllable-C6AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6af Hangul Syllable-C6AF | c6ae Hangul Syllable-C6AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6b1 Hangul Syllable-C6B1 | c6b0 Hangul Syllable-C6B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6b3 Hangul Syllable-C6B3 | c6b2 Hangul Syllable-C6B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6b5 Hangul Syllable-C6B5 | c6b4 Hangul Syllable-C6B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6b7 Hangul Syllable-C6B7 | c6b6 Hangul Syllable-C6B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6b9 Hangul Syllable-C6B9 | c6b8 Hangul Syllable-C6B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6bb Hangul Syllable-C6BB | c6ba Hangul Syllable-C6BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6bd Hangul Syllable-C6BD | c6bc Hangul Syllable-C6BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6bf Hangul Syllable-C6BF | c6be Hangul Syllable-C6BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6c1 Hangul Syllable-C6C1 | c6c0 Hangul Syllable-C6C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6c3 Hangul Syllable-C6C3 | c6c2 Hangul Syllable-C6C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6c5 Hangul Syllable-C6C5 | c6c4 Hangul Syllable-C6C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6c7 Hangul Syllable-C6C7 | c6c6 Hangul Syllable-C6C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6c9 Hangul Syllable-C6C9 | c6c8 Hangul Syllable-C6C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6cb Hangul Syllable-C6CB | c6ca Hangul Syllable-C6CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6cd Hangul Syllable-C6CD | c6cc Hangul Syllable-C6CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6cf Hangul Syllable-C6CF | c6ce Hangul Syllable-C6CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6d1 Hangul Syllable-C6D1 | c6d0 Hangul Syllable-C6D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6d3 Hangul Syllable-C6D3 | c6d2 Hangul Syllable-C6D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6d5 Hangul Syllable-C6D5 | c6d4 Hangul Syllable-C6D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6d7 Hangul Syllable-C6D7 | c6d6 Hangul Syllable-C6D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6d9 Hangul Syllable-C6D9 | c6d8 Hangul Syllable-C6D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6db Hangul Syllable-C6DB | c6da Hangul Syllable-C6DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6dd Hangul Syllable-C6DD | c6dc Hangul Syllable-C6DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6df Hangul Syllable-C6DF | c6de Hangul Syllable-C6DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6e1 Hangul Syllable-C6E1 | c6e0 Hangul Syllable-C6E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6e3 Hangul Syllable-C6E3 | c6e2 Hangul Syllable-C6E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6e5 Hangul Syllable-C6E5 | c6e4 Hangul Syllable-C6E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6e7 Hangul Syllable-C6E7 | c6e6 Hangul Syllable-C6E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6e9 Hangul Syllable-C6E9 | c6e8 Hangul Syllable-C6E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6eb Hangul Syllable-C6EB | c6ea Hangul Syllable-C6EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6ed Hangul Syllable-C6ED | c6ec Hangul Syllable-C6EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6ef Hangul Syllable-C6EF | c6ee Hangul Syllable-C6EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6f1 Hangul Syllable-C6F1 | c6f0 Hangul Syllable-C6F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6f3 Hangul Syllable-C6F3 | c6f2 Hangul Syllable-C6F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6f5 Hangul Syllable-C6F5 | c6f4 Hangul Syllable-C6F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6f7 Hangul Syllable-C6F7 | c6f6 Hangul Syllable-C6F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6f9 Hangul Syllable-C6F9 | c6f8 Hangul Syllable-C6F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6fb Hangul Syllable-C6FB | c6fa Hangul Syllable-C6FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6fd Hangul Syllable-C6FD | c6fc Hangul Syllable-C6FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6ff Hangul Syllable-C6FF | c6fe Hangul Syllable-C6FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c701 Hangul Syllable-C701 | c700 Hangul Syllable-C700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c703 Hangul Syllable-C703 | c702 Hangul Syllable-C702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c705 Hangul Syllable-C705 | c704 Hangul Syllable-C704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c707 Hangul Syllable-C707 | c706 Hangul Syllable-C706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c709 Hangul Syllable-C709 | c708 Hangul Syllable-C708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c70b Hangul Syllable-C70B | c70a Hangul Syllable-C70A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c70d Hangul Syllable-C70D | c70c Hangul Syllable-C70C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c70f Hangul Syllable-C70F | c70e Hangul Syllable-C70E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c711 Hangul Syllable-C711 | c710 Hangul Syllable-C710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c713 Hangul Syllable-C713 | c712 Hangul Syllable-C712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c715 Hangul Syllable-C715 | c714 Hangul Syllable-C714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c717 Hangul Syllable-C717 | c716 Hangul Syllable-C716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c719 Hangul Syllable-C719 | c718 Hangul Syllable-C718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c71b Hangul Syllable-C71B | c71a Hangul Syllable-C71A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c71d Hangul Syllable-C71D | c71c Hangul Syllable-C71C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c71f Hangul Syllable-C71F | c71e Hangul Syllable-C71E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c721 Hangul Syllable-C721 | c720 Hangul Syllable-C720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c723 Hangul Syllable-C723 | c722 Hangul Syllable-C722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c725 Hangul Syllable-C725 | c724 Hangul Syllable-C724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c727 Hangul Syllable-C727 | c726 Hangul Syllable-C726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c729 Hangul Syllable-C729 | c728 Hangul Syllable-C728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c72b Hangul Syllable-C72B | c72a Hangul Syllable-C72A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c72d Hangul Syllable-C72D | c72c Hangul Syllable-C72C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c72f Hangul Syllable-C72F | c72e Hangul Syllable-C72E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c731 Hangul Syllable-C731 | c730 Hangul Syllable-C730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c733 Hangul Syllable-C733 | c732 Hangul Syllable-C732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c735 Hangul Syllable-C735 | c734 Hangul Syllable-C734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c737 Hangul Syllable-C737 | c736 Hangul Syllable-C736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c739 Hangul Syllable-C739 | c738 Hangul Syllable-C738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c73b Hangul Syllable-C73B | c73a Hangul Syllable-C73A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c73d Hangul Syllable-C73D | c73c Hangul Syllable-C73C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c73f Hangul Syllable-C73F | c73e Hangul Syllable-C73E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c741 Hangul Syllable-C741 | c740 Hangul Syllable-C740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c743 Hangul Syllable-C743 | c742 Hangul Syllable-C742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c745 Hangul Syllable-C745 | c744 Hangul Syllable-C744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c747 Hangul Syllable-C747 | c746 Hangul Syllable-C746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c749 Hangul Syllable-C749 | c748 Hangul Syllable-C748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c74b Hangul Syllable-C74B | c74a Hangul Syllable-C74A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c74d Hangul Syllable-C74D | c74c Hangul Syllable-C74C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c74f Hangul Syllable-C74F | c74e Hangul Syllable-C74E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c751 Hangul Syllable-C751 | c750 Hangul Syllable-C750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c753 Hangul Syllable-C753 | c752 Hangul Syllable-C752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c755 Hangul Syllable-C755 | c754 Hangul Syllable-C754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c757 Hangul Syllable-C757 | c756 Hangul Syllable-C756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c759 Hangul Syllable-C759 | c758 Hangul Syllable-C758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c75b Hangul Syllable-C75B | c75a Hangul Syllable-C75A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c75d Hangul Syllable-C75D | c75c Hangul Syllable-C75C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c75f Hangul Syllable-C75F | c75e Hangul Syllable-C75E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c761 Hangul Syllable-C761 | c760 Hangul Syllable-C760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c763 Hangul Syllable-C763 | c762 Hangul Syllable-C762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c765 Hangul Syllable-C765 | c764 Hangul Syllable-C764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c767 Hangul Syllable-C767 | c766 Hangul Syllable-C766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c769 Hangul Syllable-C769 | c768 Hangul Syllable-C768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c76b Hangul Syllable-C76B | c76a Hangul Syllable-C76A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c76d Hangul Syllable-C76D | c76c Hangul Syllable-C76C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c76f Hangul Syllable-C76F | c76e Hangul Syllable-C76E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c771 Hangul Syllable-C771 | c770 Hangul Syllable-C770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c773 Hangul Syllable-C773 | c772 Hangul Syllable-C772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c775 Hangul Syllable-C775 | c774 Hangul Syllable-C774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c777 Hangul Syllable-C777 | c776 Hangul Syllable-C776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c779 Hangul Syllable-C779 | c778 Hangul Syllable-C778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c77b Hangul Syllable-C77B | c77a Hangul Syllable-C77A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c77d Hangul Syllable-C77D | c77c Hangul Syllable-C77C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c77f Hangul Syllable-C77F | c77e Hangul Syllable-C77E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c781 Hangul Syllable-C781 | c780 Hangul Syllable-C780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c783 Hangul Syllable-C783 | c782 Hangul Syllable-C782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c785 Hangul Syllable-C785 | c784 Hangul Syllable-C784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c787 Hangul Syllable-C787 | c786 Hangul Syllable-C786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c789 Hangul Syllable-C789 | c788 Hangul Syllable-C788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c78b Hangul Syllable-C78B | c78a Hangul Syllable-C78A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c78d Hangul Syllable-C78D | c78c Hangul Syllable-C78C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c78f Hangul Syllable-C78F | c78e Hangul Syllable-C78E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c791 Hangul Syllable-C791 | c790 Hangul Syllable-C790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c793 Hangul Syllable-C793 | c792 Hangul Syllable-C792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c795 Hangul Syllable-C795 | c794 Hangul Syllable-C794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c797 Hangul Syllable-C797 | c796 Hangul Syllable-C796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c799 Hangul Syllable-C799 | c798 Hangul Syllable-C798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c79b Hangul Syllable-C79B | c79a Hangul Syllable-C79A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c79d Hangul Syllable-C79D | c79c Hangul Syllable-C79C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c79f Hangul Syllable-C79F | c79e Hangul Syllable-C79E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7a1 Hangul Syllable-C7A1 | c7a0 Hangul Syllable-C7A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7a3 Hangul Syllable-C7A3 | c7a2 Hangul Syllable-C7A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7a5 Hangul Syllable-C7A5 | c7a4 Hangul Syllable-C7A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7a7 Hangul Syllable-C7A7 | c7a6 Hangul Syllable-C7A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7a9 Hangul Syllable-C7A9 | c7a8 Hangul Syllable-C7A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7ab Hangul Syllable-C7AB | c7aa Hangul Syllable-C7AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7ad Hangul Syllable-C7AD | c7ac Hangul Syllable-C7AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7af Hangul Syllable-C7AF | c7ae Hangul Syllable-C7AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7b1 Hangul Syllable-C7B1 | c7b0 Hangul Syllable-C7B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7b3 Hangul Syllable-C7B3 | c7b2 Hangul Syllable-C7B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7b5 Hangul Syllable-C7B5 | c7b4 Hangul Syllable-C7B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7b7 Hangul Syllable-C7B7 | c7b6 Hangul Syllable-C7B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7b9 Hangul Syllable-C7B9 | c7b8 Hangul Syllable-C7B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7bb Hangul Syllable-C7BB | c7ba Hangul Syllable-C7BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7bd Hangul Syllable-C7BD | c7bc Hangul Syllable-C7BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7bf Hangul Syllable-C7BF | c7be Hangul Syllable-C7BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7c1 Hangul Syllable-C7C1 | c7c0 Hangul Syllable-C7C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7c3 Hangul Syllable-C7C3 | c7c2 Hangul Syllable-C7C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7c5 Hangul Syllable-C7C5 | c7c4 Hangul Syllable-C7C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7c7 Hangul Syllable-C7C7 | c7c6 Hangul Syllable-C7C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7c9 Hangul Syllable-C7C9 | c7c8 Hangul Syllable-C7C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7cb Hangul Syllable-C7CB | c7ca Hangul Syllable-C7CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7cd Hangul Syllable-C7CD | c7cc Hangul Syllable-C7CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7cf Hangul Syllable-C7CF | c7ce Hangul Syllable-C7CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7d1 Hangul Syllable-C7D1 | c7d0 Hangul Syllable-C7D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7d3 Hangul Syllable-C7D3 | c7d2 Hangul Syllable-C7D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7d5 Hangul Syllable-C7D5 | c7d4 Hangul Syllable-C7D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7d7 Hangul Syllable-C7D7 | c7d6 Hangul Syllable-C7D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7d9 Hangul Syllable-C7D9 | c7d8 Hangul Syllable-C7D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7db Hangul Syllable-C7DB | c7da Hangul Syllable-C7DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7dd Hangul Syllable-C7DD | c7dc Hangul Syllable-C7DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7df Hangul Syllable-C7DF | c7de Hangul Syllable-C7DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7e1 Hangul Syllable-C7E1 | c7e0 Hangul Syllable-C7E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7e3 Hangul Syllable-C7E3 | c7e2 Hangul Syllable-C7E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7e5 Hangul Syllable-C7E5 | c7e4 Hangul Syllable-C7E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7e7 Hangul Syllable-C7E7 | c7e6 Hangul Syllable-C7E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7e9 Hangul Syllable-C7E9 | c7e8 Hangul Syllable-C7E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7eb Hangul Syllable-C7EB | c7ea Hangul Syllable-C7EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7ed Hangul Syllable-C7ED | c7ec Hangul Syllable-C7EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7ef Hangul Syllable-C7EF | c7ee Hangul Syllable-C7EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7f1 Hangul Syllable-C7F1 | c7f0 Hangul Syllable-C7F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7f3 Hangul Syllable-C7F3 | c7f2 Hangul Syllable-C7F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7f5 Hangul Syllable-C7F5 | c7f4 Hangul Syllable-C7F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7f7 Hangul Syllable-C7F7 | c7f6 Hangul Syllable-C7F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7f9 Hangul Syllable-C7F9 | c7f8 Hangul Syllable-C7F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7fb Hangul Syllable-C7FB | c7fa Hangul Syllable-C7FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7fd Hangul Syllable-C7FD | c7fc Hangul Syllable-C7FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7ff Hangul Syllable-C7FF | c7fe Hangul Syllable-C7FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c801 Hangul Syllable-C801 | c800 Hangul Syllable-C800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c803 Hangul Syllable-C803 | c802 Hangul Syllable-C802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c805 Hangul Syllable-C805 | c804 Hangul Syllable-C804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c807 Hangul Syllable-C807 | c806 Hangul Syllable-C806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c809 Hangul Syllable-C809 | c808 Hangul Syllable-C808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c80b Hangul Syllable-C80B | c80a Hangul Syllable-C80A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c80d Hangul Syllable-C80D | c80c Hangul Syllable-C80C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c80f Hangul Syllable-C80F | c80e Hangul Syllable-C80E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c811 Hangul Syllable-C811 | c810 Hangul Syllable-C810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c813 Hangul Syllable-C813 | c812 Hangul Syllable-C812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c815 Hangul Syllable-C815 | c814 Hangul Syllable-C814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c817 Hangul Syllable-C817 | c816 Hangul Syllable-C816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c819 Hangul Syllable-C819 | c818 Hangul Syllable-C818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c81b Hangul Syllable-C81B | c81a Hangul Syllable-C81A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c81d Hangul Syllable-C81D | c81c Hangul Syllable-C81C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c81f Hangul Syllable-C81F | c81e Hangul Syllable-C81E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c821 Hangul Syllable-C821 | c820 Hangul Syllable-C820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c823 Hangul Syllable-C823 | c822 Hangul Syllable-C822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c825 Hangul Syllable-C825 | c824 Hangul Syllable-C824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c827 Hangul Syllable-C827 | c826 Hangul Syllable-C826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c829 Hangul Syllable-C829 | c828 Hangul Syllable-C828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c82b Hangul Syllable-C82B | c82a Hangul Syllable-C82A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c82d Hangul Syllable-C82D | c82c Hangul Syllable-C82C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c82f Hangul Syllable-C82F | c82e Hangul Syllable-C82E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c831 Hangul Syllable-C831 | c830 Hangul Syllable-C830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c833 Hangul Syllable-C833 | c832 Hangul Syllable-C832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c835 Hangul Syllable-C835 | c834 Hangul Syllable-C834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c837 Hangul Syllable-C837 | c836 Hangul Syllable-C836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c839 Hangul Syllable-C839 | c838 Hangul Syllable-C838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c83b Hangul Syllable-C83B | c83a Hangul Syllable-C83A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c83d Hangul Syllable-C83D | c83c Hangul Syllable-C83C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c83f Hangul Syllable-C83F | c83e Hangul Syllable-C83E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c841 Hangul Syllable-C841 | c840 Hangul Syllable-C840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c843 Hangul Syllable-C843 | c842 Hangul Syllable-C842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c845 Hangul Syllable-C845 | c844 Hangul Syllable-C844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c847 Hangul Syllable-C847 | c846 Hangul Syllable-C846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c849 Hangul Syllable-C849 | c848 Hangul Syllable-C848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c84b Hangul Syllable-C84B | c84a Hangul Syllable-C84A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c84d Hangul Syllable-C84D | c84c Hangul Syllable-C84C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c84f Hangul Syllable-C84F | c84e Hangul Syllable-C84E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c851 Hangul Syllable-C851 | c850 Hangul Syllable-C850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c853 Hangul Syllable-C853 | c852 Hangul Syllable-C852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c855 Hangul Syllable-C855 | c854 Hangul Syllable-C854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c857 Hangul Syllable-C857 | c856 Hangul Syllable-C856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c859 Hangul Syllable-C859 | c858 Hangul Syllable-C858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c85b Hangul Syllable-C85B | c85a Hangul Syllable-C85A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c85d Hangul Syllable-C85D | c85c Hangul Syllable-C85C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c85f Hangul Syllable-C85F | c85e Hangul Syllable-C85E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c861 Hangul Syllable-C861 | c860 Hangul Syllable-C860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c863 Hangul Syllable-C863 | c862 Hangul Syllable-C862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c865 Hangul Syllable-C865 | c864 Hangul Syllable-C864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c867 Hangul Syllable-C867 | c866 Hangul Syllable-C866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c869 Hangul Syllable-C869 | c868 Hangul Syllable-C868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c86b Hangul Syllable-C86B | c86a Hangul Syllable-C86A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c86d Hangul Syllable-C86D | c86c Hangul Syllable-C86C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c86f Hangul Syllable-C86F | c86e Hangul Syllable-C86E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c871 Hangul Syllable-C871 | c870 Hangul Syllable-C870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c873 Hangul Syllable-C873 | c872 Hangul Syllable-C872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c875 Hangul Syllable-C875 | c874 Hangul Syllable-C874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c877 Hangul Syllable-C877 | c876 Hangul Syllable-C876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c879 Hangul Syllable-C879 | c878 Hangul Syllable-C878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c87b Hangul Syllable-C87B | c87a Hangul Syllable-C87A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c87d Hangul Syllable-C87D | c87c Hangul Syllable-C87C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c87f Hangul Syllable-C87F | c87e Hangul Syllable-C87E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c881 Hangul Syllable-C881 | c880 Hangul Syllable-C880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c883 Hangul Syllable-C883 | c882 Hangul Syllable-C882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c885 Hangul Syllable-C885 | c884 Hangul Syllable-C884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c887 Hangul Syllable-C887 | c886 Hangul Syllable-C886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c889 Hangul Syllable-C889 | c888 Hangul Syllable-C888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c88b Hangul Syllable-C88B | c88a Hangul Syllable-C88A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c88d Hangul Syllable-C88D | c88c Hangul Syllable-C88C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c88f Hangul Syllable-C88F | c88e Hangul Syllable-C88E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c891 Hangul Syllable-C891 | c890 Hangul Syllable-C890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c893 Hangul Syllable-C893 | c892 Hangul Syllable-C892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c895 Hangul Syllable-C895 | c894 Hangul Syllable-C894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c897 Hangul Syllable-C897 | c896 Hangul Syllable-C896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c899 Hangul Syllable-C899 | c898 Hangul Syllable-C898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c89b Hangul Syllable-C89B | c89a Hangul Syllable-C89A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c89d Hangul Syllable-C89D | c89c Hangul Syllable-C89C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c89f Hangul Syllable-C89F | c89e Hangul Syllable-C89E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8a1 Hangul Syllable-C8A1 | c8a0 Hangul Syllable-C8A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8a3 Hangul Syllable-C8A3 | c8a2 Hangul Syllable-C8A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8a5 Hangul Syllable-C8A5 | c8a4 Hangul Syllable-C8A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8a7 Hangul Syllable-C8A7 | c8a6 Hangul Syllable-C8A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8a9 Hangul Syllable-C8A9 | c8a8 Hangul Syllable-C8A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8ab Hangul Syllable-C8AB | c8aa Hangul Syllable-C8AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8ad Hangul Syllable-C8AD | c8ac Hangul Syllable-C8AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8af Hangul Syllable-C8AF | c8ae Hangul Syllable-C8AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8b1 Hangul Syllable-C8B1 | c8b0 Hangul Syllable-C8B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8b3 Hangul Syllable-C8B3 | c8b2 Hangul Syllable-C8B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8b5 Hangul Syllable-C8B5 | c8b4 Hangul Syllable-C8B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8b7 Hangul Syllable-C8B7 | c8b6 Hangul Syllable-C8B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8b9 Hangul Syllable-C8B9 | c8b8 Hangul Syllable-C8B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8bb Hangul Syllable-C8BB | c8ba Hangul Syllable-C8BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8bd Hangul Syllable-C8BD | c8bc Hangul Syllable-C8BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8bf Hangul Syllable-C8BF | c8be Hangul Syllable-C8BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8c1 Hangul Syllable-C8C1 | c8c0 Hangul Syllable-C8C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8c3 Hangul Syllable-C8C3 | c8c2 Hangul Syllable-C8C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8c5 Hangul Syllable-C8C5 | c8c4 Hangul Syllable-C8C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8c7 Hangul Syllable-C8C7 | c8c6 Hangul Syllable-C8C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8c9 Hangul Syllable-C8C9 | c8c8 Hangul Syllable-C8C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8cb Hangul Syllable-C8CB | c8ca Hangul Syllable-C8CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8cd Hangul Syllable-C8CD | c8cc Hangul Syllable-C8CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8cf Hangul Syllable-C8CF | c8ce Hangul Syllable-C8CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8d1 Hangul Syllable-C8D1 | c8d0 Hangul Syllable-C8D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8d3 Hangul Syllable-C8D3 | c8d2 Hangul Syllable-C8D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8d5 Hangul Syllable-C8D5 | c8d4 Hangul Syllable-C8D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8d7 Hangul Syllable-C8D7 | c8d6 Hangul Syllable-C8D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8d9 Hangul Syllable-C8D9 | c8d8 Hangul Syllable-C8D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8db Hangul Syllable-C8DB | c8da Hangul Syllable-C8DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8dd Hangul Syllable-C8DD | c8dc Hangul Syllable-C8DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8df Hangul Syllable-C8DF | c8de Hangul Syllable-C8DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8e1 Hangul Syllable-C8E1 | c8e0 Hangul Syllable-C8E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8e3 Hangul Syllable-C8E3 | c8e2 Hangul Syllable-C8E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8e5 Hangul Syllable-C8E5 | c8e4 Hangul Syllable-C8E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8e7 Hangul Syllable-C8E7 | c8e6 Hangul Syllable-C8E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8e9 Hangul Syllable-C8E9 | c8e8 Hangul Syllable-C8E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8eb Hangul Syllable-C8EB | c8ea Hangul Syllable-C8EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8ed Hangul Syllable-C8ED | c8ec Hangul Syllable-C8EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8ef Hangul Syllable-C8EF | c8ee Hangul Syllable-C8EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8f1 Hangul Syllable-C8F1 | c8f0 Hangul Syllable-C8F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8f3 Hangul Syllable-C8F3 | c8f2 Hangul Syllable-C8F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8f5 Hangul Syllable-C8F5 | c8f4 Hangul Syllable-C8F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8f7 Hangul Syllable-C8F7 | c8f6 Hangul Syllable-C8F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8f9 Hangul Syllable-C8F9 | c8f8 Hangul Syllable-C8F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8fb Hangul Syllable-C8FB | c8fa Hangul Syllable-C8FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8fd Hangul Syllable-C8FD | c8fc Hangul Syllable-C8FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8ff Hangul Syllable-C8FF | c8fe Hangul Syllable-C8FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c901 Hangul Syllable-C901 | c900 Hangul Syllable-C900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c903 Hangul Syllable-C903 | c902 Hangul Syllable-C902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c905 Hangul Syllable-C905 | c904 Hangul Syllable-C904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c907 Hangul Syllable-C907 | c906 Hangul Syllable-C906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c909 Hangul Syllable-C909 | c908 Hangul Syllable-C908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c90b Hangul Syllable-C90B | c90a Hangul Syllable-C90A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c90d Hangul Syllable-C90D | c90c Hangul Syllable-C90C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c90f Hangul Syllable-C90F | c90e Hangul Syllable-C90E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c911 Hangul Syllable-C911 | c910 Hangul Syllable-C910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c913 Hangul Syllable-C913 | c912 Hangul Syllable-C912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c915 Hangul Syllable-C915 | c914 Hangul Syllable-C914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c917 Hangul Syllable-C917 | c916 Hangul Syllable-C916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c919 Hangul Syllable-C919 | c918 Hangul Syllable-C918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c91b Hangul Syllable-C91B | c91a Hangul Syllable-C91A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c91d Hangul Syllable-C91D | c91c Hangul Syllable-C91C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c91f Hangul Syllable-C91F | c91e Hangul Syllable-C91E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c921 Hangul Syllable-C921 | c920 Hangul Syllable-C920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c923 Hangul Syllable-C923 | c922 Hangul Syllable-C922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c925 Hangul Syllable-C925 | c924 Hangul Syllable-C924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c927 Hangul Syllable-C927 | c926 Hangul Syllable-C926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c929 Hangul Syllable-C929 | c928 Hangul Syllable-C928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c92b Hangul Syllable-C92B | c92a Hangul Syllable-C92A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c92d Hangul Syllable-C92D | c92c Hangul Syllable-C92C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c92f Hangul Syllable-C92F | c92e Hangul Syllable-C92E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c931 Hangul Syllable-C931 | c930 Hangul Syllable-C930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c933 Hangul Syllable-C933 | c932 Hangul Syllable-C932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c935 Hangul Syllable-C935 | c934 Hangul Syllable-C934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c937 Hangul Syllable-C937 | c936 Hangul Syllable-C936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c939 Hangul Syllable-C939 | c938 Hangul Syllable-C938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c93b Hangul Syllable-C93B | c93a Hangul Syllable-C93A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c93d Hangul Syllable-C93D | c93c Hangul Syllable-C93C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c93f Hangul Syllable-C93F | c93e Hangul Syllable-C93E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c941 Hangul Syllable-C941 | c940 Hangul Syllable-C940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c943 Hangul Syllable-C943 | c942 Hangul Syllable-C942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c945 Hangul Syllable-C945 | c944 Hangul Syllable-C944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c947 Hangul Syllable-C947 | c946 Hangul Syllable-C946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c949 Hangul Syllable-C949 | c948 Hangul Syllable-C948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c94b Hangul Syllable-C94B | c94a Hangul Syllable-C94A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c94d Hangul Syllable-C94D | c94c Hangul Syllable-C94C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c94f Hangul Syllable-C94F | c94e Hangul Syllable-C94E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c951 Hangul Syllable-C951 | c950 Hangul Syllable-C950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c953 Hangul Syllable-C953 | c952 Hangul Syllable-C952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c955 Hangul Syllable-C955 | c954 Hangul Syllable-C954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c957 Hangul Syllable-C957 | c956 Hangul Syllable-C956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c959 Hangul Syllable-C959 | c958 Hangul Syllable-C958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c95b Hangul Syllable-C95B | c95a Hangul Syllable-C95A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c95d Hangul Syllable-C95D | c95c Hangul Syllable-C95C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c95f Hangul Syllable-C95F | c95e Hangul Syllable-C95E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c961 Hangul Syllable-C961 | c960 Hangul Syllable-C960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c963 Hangul Syllable-C963 | c962 Hangul Syllable-C962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c965 Hangul Syllable-C965 | c964 Hangul Syllable-C964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c967 Hangul Syllable-C967 | c966 Hangul Syllable-C966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c969 Hangul Syllable-C969 | c968 Hangul Syllable-C968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c96b Hangul Syllable-C96B | c96a Hangul Syllable-C96A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c96d Hangul Syllable-C96D | c96c Hangul Syllable-C96C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c96f Hangul Syllable-C96F | c96e Hangul Syllable-C96E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c971 Hangul Syllable-C971 | c970 Hangul Syllable-C970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c973 Hangul Syllable-C973 | c972 Hangul Syllable-C972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c975 Hangul Syllable-C975 | c974 Hangul Syllable-C974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c977 Hangul Syllable-C977 | c976 Hangul Syllable-C976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c979 Hangul Syllable-C979 | c978 Hangul Syllable-C978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c97b Hangul Syllable-C97B | c97a Hangul Syllable-C97A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c97d Hangul Syllable-C97D | c97c Hangul Syllable-C97C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c97f Hangul Syllable-C97F | c97e Hangul Syllable-C97E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c981 Hangul Syllable-C981 | c980 Hangul Syllable-C980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c983 Hangul Syllable-C983 | c982 Hangul Syllable-C982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c985 Hangul Syllable-C985 | c984 Hangul Syllable-C984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c987 Hangul Syllable-C987 | c986 Hangul Syllable-C986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c989 Hangul Syllable-C989 | c988 Hangul Syllable-C988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c98b Hangul Syllable-C98B | c98a Hangul Syllable-C98A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c98d Hangul Syllable-C98D | c98c Hangul Syllable-C98C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c98f Hangul Syllable-C98F | c98e Hangul Syllable-C98E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c991 Hangul Syllable-C991 | c990 Hangul Syllable-C990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c993 Hangul Syllable-C993 | c992 Hangul Syllable-C992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c995 Hangul Syllable-C995 | c994 Hangul Syllable-C994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c997 Hangul Syllable-C997 | c996 Hangul Syllable-C996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c999 Hangul Syllable-C999 | c998 Hangul Syllable-C998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c99b Hangul Syllable-C99B | c99a Hangul Syllable-C99A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c99d Hangul Syllable-C99D | c99c Hangul Syllable-C99C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c99f Hangul Syllable-C99F | c99e Hangul Syllable-C99E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9a1 Hangul Syllable-C9A1 | c9a0 Hangul Syllable-C9A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9a3 Hangul Syllable-C9A3 | c9a2 Hangul Syllable-C9A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9a5 Hangul Syllable-C9A5 | c9a4 Hangul Syllable-C9A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9a7 Hangul Syllable-C9A7 | c9a6 Hangul Syllable-C9A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9a9 Hangul Syllable-C9A9 | c9a8 Hangul Syllable-C9A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9ab Hangul Syllable-C9AB | c9aa Hangul Syllable-C9AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9ad Hangul Syllable-C9AD | c9ac Hangul Syllable-C9AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9af Hangul Syllable-C9AF | c9ae Hangul Syllable-C9AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9b1 Hangul Syllable-C9B1 | c9b0 Hangul Syllable-C9B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9b3 Hangul Syllable-C9B3 | c9b2 Hangul Syllable-C9B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9b5 Hangul Syllable-C9B5 | c9b4 Hangul Syllable-C9B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9b7 Hangul Syllable-C9B7 | c9b6 Hangul Syllable-C9B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9b9 Hangul Syllable-C9B9 | c9b8 Hangul Syllable-C9B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9bb Hangul Syllable-C9BB | c9ba Hangul Syllable-C9BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9bd Hangul Syllable-C9BD | c9bc Hangul Syllable-C9BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9bf Hangul Syllable-C9BF | c9be Hangul Syllable-C9BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9c1 Hangul Syllable-C9C1 | c9c0 Hangul Syllable-C9C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9c3 Hangul Syllable-C9C3 | c9c2 Hangul Syllable-C9C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9c5 Hangul Syllable-C9C5 | c9c4 Hangul Syllable-C9C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9c7 Hangul Syllable-C9C7 | c9c6 Hangul Syllable-C9C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9c9 Hangul Syllable-C9C9 | c9c8 Hangul Syllable-C9C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9cb Hangul Syllable-C9CB | c9ca Hangul Syllable-C9CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9cd Hangul Syllable-C9CD | c9cc Hangul Syllable-C9CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9cf Hangul Syllable-C9CF | c9ce Hangul Syllable-C9CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9d1 Hangul Syllable-C9D1 | c9d0 Hangul Syllable-C9D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9d3 Hangul Syllable-C9D3 | c9d2 Hangul Syllable-C9D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9d5 Hangul Syllable-C9D5 | c9d4 Hangul Syllable-C9D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9d7 Hangul Syllable-C9D7 | c9d6 Hangul Syllable-C9D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9d9 Hangul Syllable-C9D9 | c9d8 Hangul Syllable-C9D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9db Hangul Syllable-C9DB | c9da Hangul Syllable-C9DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9dd Hangul Syllable-C9DD | c9dc Hangul Syllable-C9DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9df Hangul Syllable-C9DF | c9de Hangul Syllable-C9DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9e1 Hangul Syllable-C9E1 | c9e0 Hangul Syllable-C9E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9e3 Hangul Syllable-C9E3 | c9e2 Hangul Syllable-C9E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9e5 Hangul Syllable-C9E5 | c9e4 Hangul Syllable-C9E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9e7 Hangul Syllable-C9E7 | c9e6 Hangul Syllable-C9E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9e9 Hangul Syllable-C9E9 | c9e8 Hangul Syllable-C9E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9eb Hangul Syllable-C9EB | c9ea Hangul Syllable-C9EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9ed Hangul Syllable-C9ED | c9ec Hangul Syllable-C9EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9ef Hangul Syllable-C9EF | c9ee Hangul Syllable-C9EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9f1 Hangul Syllable-C9F1 | c9f0 Hangul Syllable-C9F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9f3 Hangul Syllable-C9F3 | c9f2 Hangul Syllable-C9F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9f5 Hangul Syllable-C9F5 | c9f4 Hangul Syllable-C9F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9f7 Hangul Syllable-C9F7 | c9f6 Hangul Syllable-C9F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9f9 Hangul Syllable-C9F9 | c9f8 Hangul Syllable-C9F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9fb Hangul Syllable-C9FB | c9fa Hangul Syllable-C9FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9fd Hangul Syllable-C9FD | c9fc Hangul Syllable-C9FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9ff Hangul Syllable-C9FF | c9fe Hangul Syllable-C9FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca01 Hangul Syllable-CA01 | ca00 Hangul Syllable-CA00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca03 Hangul Syllable-CA03 | ca02 Hangul Syllable-CA02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca05 Hangul Syllable-CA05 | ca04 Hangul Syllable-CA04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca07 Hangul Syllable-CA07 | ca06 Hangul Syllable-CA06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca09 Hangul Syllable-CA09 | ca08 Hangul Syllable-CA08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca0b Hangul Syllable-CA0B | ca0a Hangul Syllable-CA0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca0d Hangul Syllable-CA0D | ca0c Hangul Syllable-CA0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca0f Hangul Syllable-CA0F | ca0e Hangul Syllable-CA0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca11 Hangul Syllable-CA11 | ca10 Hangul Syllable-CA10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca13 Hangul Syllable-CA13 | ca12 Hangul Syllable-CA12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca15 Hangul Syllable-CA15 | ca14 Hangul Syllable-CA14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca17 Hangul Syllable-CA17 | ca16 Hangul Syllable-CA16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca19 Hangul Syllable-CA19 | ca18 Hangul Syllable-CA18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca1b Hangul Syllable-CA1B | ca1a Hangul Syllable-CA1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca1d Hangul Syllable-CA1D | ca1c Hangul Syllable-CA1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca1f Hangul Syllable-CA1F | ca1e Hangul Syllable-CA1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca21 Hangul Syllable-CA21 | ca20 Hangul Syllable-CA20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca23 Hangul Syllable-CA23 | ca22 Hangul Syllable-CA22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca25 Hangul Syllable-CA25 | ca24 Hangul Syllable-CA24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca27 Hangul Syllable-CA27 | ca26 Hangul Syllable-CA26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca29 Hangul Syllable-CA29 | ca28 Hangul Syllable-CA28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca2b Hangul Syllable-CA2B | ca2a Hangul Syllable-CA2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca2d Hangul Syllable-CA2D | ca2c Hangul Syllable-CA2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca2f Hangul Syllable-CA2F | ca2e Hangul Syllable-CA2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca31 Hangul Syllable-CA31 | ca30 Hangul Syllable-CA30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca33 Hangul Syllable-CA33 | ca32 Hangul Syllable-CA32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca35 Hangul Syllable-CA35 | ca34 Hangul Syllable-CA34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca37 Hangul Syllable-CA37 | ca36 Hangul Syllable-CA36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca39 Hangul Syllable-CA39 | ca38 Hangul Syllable-CA38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca3b Hangul Syllable-CA3B | ca3a Hangul Syllable-CA3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca3d Hangul Syllable-CA3D | ca3c Hangul Syllable-CA3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca3f Hangul Syllable-CA3F | ca3e Hangul Syllable-CA3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca41 Hangul Syllable-CA41 | ca40 Hangul Syllable-CA40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca43 Hangul Syllable-CA43 | ca42 Hangul Syllable-CA42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca45 Hangul Syllable-CA45 | ca44 Hangul Syllable-CA44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca47 Hangul Syllable-CA47 | ca46 Hangul Syllable-CA46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca49 Hangul Syllable-CA49 | ca48 Hangul Syllable-CA48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca4b Hangul Syllable-CA4B | ca4a Hangul Syllable-CA4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca4d Hangul Syllable-CA4D | ca4c Hangul Syllable-CA4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca4f Hangul Syllable-CA4F | ca4e Hangul Syllable-CA4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca51 Hangul Syllable-CA51 | ca50 Hangul Syllable-CA50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca53 Hangul Syllable-CA53 | ca52 Hangul Syllable-CA52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca55 Hangul Syllable-CA55 | ca54 Hangul Syllable-CA54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca57 Hangul Syllable-CA57 | ca56 Hangul Syllable-CA56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca59 Hangul Syllable-CA59 | ca58 Hangul Syllable-CA58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca5b Hangul Syllable-CA5B | ca5a Hangul Syllable-CA5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca5d Hangul Syllable-CA5D | ca5c Hangul Syllable-CA5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca5f Hangul Syllable-CA5F | ca5e Hangul Syllable-CA5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca61 Hangul Syllable-CA61 | ca60 Hangul Syllable-CA60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca63 Hangul Syllable-CA63 | ca62 Hangul Syllable-CA62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca65 Hangul Syllable-CA65 | ca64 Hangul Syllable-CA64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca67 Hangul Syllable-CA67 | ca66 Hangul Syllable-CA66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca69 Hangul Syllable-CA69 | ca68 Hangul Syllable-CA68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca6b Hangul Syllable-CA6B | ca6a Hangul Syllable-CA6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca6d Hangul Syllable-CA6D | ca6c Hangul Syllable-CA6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca6f Hangul Syllable-CA6F | ca6e Hangul Syllable-CA6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca71 Hangul Syllable-CA71 | ca70 Hangul Syllable-CA70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca73 Hangul Syllable-CA73 | ca72 Hangul Syllable-CA72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca75 Hangul Syllable-CA75 | ca74 Hangul Syllable-CA74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca77 Hangul Syllable-CA77 | ca76 Hangul Syllable-CA76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca79 Hangul Syllable-CA79 | ca78 Hangul Syllable-CA78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca7b Hangul Syllable-CA7B | ca7a Hangul Syllable-CA7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca7d Hangul Syllable-CA7D | ca7c Hangul Syllable-CA7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca7f Hangul Syllable-CA7F | ca7e Hangul Syllable-CA7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca81 Hangul Syllable-CA81 | ca80 Hangul Syllable-CA80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca83 Hangul Syllable-CA83 | ca82 Hangul Syllable-CA82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca85 Hangul Syllable-CA85 | ca84 Hangul Syllable-CA84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca87 Hangul Syllable-CA87 | ca86 Hangul Syllable-CA86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca89 Hangul Syllable-CA89 | ca88 Hangul Syllable-CA88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca8b Hangul Syllable-CA8B | ca8a Hangul Syllable-CA8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca8d Hangul Syllable-CA8D | ca8c Hangul Syllable-CA8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca8f Hangul Syllable-CA8F | ca8e Hangul Syllable-CA8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca91 Hangul Syllable-CA91 | ca90 Hangul Syllable-CA90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca93 Hangul Syllable-CA93 | ca92 Hangul Syllable-CA92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca95 Hangul Syllable-CA95 | ca94 Hangul Syllable-CA94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca97 Hangul Syllable-CA97 | ca96 Hangul Syllable-CA96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca99 Hangul Syllable-CA99 | ca98 Hangul Syllable-CA98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca9b Hangul Syllable-CA9B | ca9a Hangul Syllable-CA9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca9d Hangul Syllable-CA9D | ca9c Hangul Syllable-CA9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca9f Hangul Syllable-CA9F | ca9e Hangul Syllable-CA9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caa1 Hangul Syllable-CAA1 | caa0 Hangul Syllable-CAA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caa3 Hangul Syllable-CAA3 | caa2 Hangul Syllable-CAA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caa5 Hangul Syllable-CAA5 | caa4 Hangul Syllable-CAA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caa7 Hangul Syllable-CAA7 | caa6 Hangul Syllable-CAA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caa9 Hangul Syllable-CAA9 | caa8 Hangul Syllable-CAA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caab Hangul Syllable-CAAB | caaa Hangul Syllable-CAAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caad Hangul Syllable-CAAD | caac Hangul Syllable-CAAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caaf Hangul Syllable-CAAF | caae Hangul Syllable-CAAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cab1 Hangul Syllable-CAB1 | cab0 Hangul Syllable-CAB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cab3 Hangul Syllable-CAB3 | cab2 Hangul Syllable-CAB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cab5 Hangul Syllable-CAB5 | cab4 Hangul Syllable-CAB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cab7 Hangul Syllable-CAB7 | cab6 Hangul Syllable-CAB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cab9 Hangul Syllable-CAB9 | cab8 Hangul Syllable-CAB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cabb Hangul Syllable-CABB | caba Hangul Syllable-CABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cabd Hangul Syllable-CABD | cabc Hangul Syllable-CABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cabf Hangul Syllable-CABF | cabe Hangul Syllable-CABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cac1 Hangul Syllable-CAC1 | cac0 Hangul Syllable-CAC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cac3 Hangul Syllable-CAC3 | cac2 Hangul Syllable-CAC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cac5 Hangul Syllable-CAC5 | cac4 Hangul Syllable-CAC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cac7 Hangul Syllable-CAC7 | cac6 Hangul Syllable-CAC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cac9 Hangul Syllable-CAC9 | cac8 Hangul Syllable-CAC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cacb Hangul Syllable-CACB | caca Hangul Syllable-CACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cacd Hangul Syllable-CACD | cacc Hangul Syllable-CACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cacf Hangul Syllable-CACF | cace Hangul Syllable-CACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cad1 Hangul Syllable-CAD1 | cad0 Hangul Syllable-CAD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cad3 Hangul Syllable-CAD3 | cad2 Hangul Syllable-CAD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cad5 Hangul Syllable-CAD5 | cad4 Hangul Syllable-CAD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cad7 Hangul Syllable-CAD7 | cad6 Hangul Syllable-CAD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cad9 Hangul Syllable-CAD9 | cad8 Hangul Syllable-CAD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cadb Hangul Syllable-CADB | cada Hangul Syllable-CADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cadd Hangul Syllable-CADD | cadc Hangul Syllable-CADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cadf Hangul Syllable-CADF | cade Hangul Syllable-CADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cae1 Hangul Syllable-CAE1 | cae0 Hangul Syllable-CAE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cae3 Hangul Syllable-CAE3 | cae2 Hangul Syllable-CAE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cae5 Hangul Syllable-CAE5 | cae4 Hangul Syllable-CAE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cae7 Hangul Syllable-CAE7 | cae6 Hangul Syllable-CAE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cae9 Hangul Syllable-CAE9 | cae8 Hangul Syllable-CAE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caeb Hangul Syllable-CAEB | caea Hangul Syllable-CAEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caed Hangul Syllable-CAED | caec Hangul Syllable-CAEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caef Hangul Syllable-CAEF | caee Hangul Syllable-CAEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caf1 Hangul Syllable-CAF1 | caf0 Hangul Syllable-CAF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caf3 Hangul Syllable-CAF3 | caf2 Hangul Syllable-CAF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caf5 Hangul Syllable-CAF5 | caf4 Hangul Syllable-CAF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caf7 Hangul Syllable-CAF7 | caf6 Hangul Syllable-CAF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caf9 Hangul Syllable-CAF9 | caf8 Hangul Syllable-CAF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cafb Hangul Syllable-CAFB | cafa Hangul Syllable-CAFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cafd Hangul Syllable-CAFD | cafc Hangul Syllable-CAFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caff Hangul Syllable-CAFF | cafe Hangul Syllable-CAFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb01 Hangul Syllable-CB01 | cb00 Hangul Syllable-CB00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb03 Hangul Syllable-CB03 | cb02 Hangul Syllable-CB02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb05 Hangul Syllable-CB05 | cb04 Hangul Syllable-CB04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb07 Hangul Syllable-CB07 | cb06 Hangul Syllable-CB06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb09 Hangul Syllable-CB09 | cb08 Hangul Syllable-CB08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb0b Hangul Syllable-CB0B | cb0a Hangul Syllable-CB0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb0d Hangul Syllable-CB0D | cb0c Hangul Syllable-CB0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb0f Hangul Syllable-CB0F | cb0e Hangul Syllable-CB0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb11 Hangul Syllable-CB11 | cb10 Hangul Syllable-CB10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb13 Hangul Syllable-CB13 | cb12 Hangul Syllable-CB12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb15 Hangul Syllable-CB15 | cb14 Hangul Syllable-CB14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb17 Hangul Syllable-CB17 | cb16 Hangul Syllable-CB16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb19 Hangul Syllable-CB19 | cb18 Hangul Syllable-CB18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb1b Hangul Syllable-CB1B | cb1a Hangul Syllable-CB1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb1d Hangul Syllable-CB1D | cb1c Hangul Syllable-CB1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb1f Hangul Syllable-CB1F | cb1e Hangul Syllable-CB1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb21 Hangul Syllable-CB21 | cb20 Hangul Syllable-CB20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb23 Hangul Syllable-CB23 | cb22 Hangul Syllable-CB22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb25 Hangul Syllable-CB25 | cb24 Hangul Syllable-CB24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb27 Hangul Syllable-CB27 | cb26 Hangul Syllable-CB26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb29 Hangul Syllable-CB29 | cb28 Hangul Syllable-CB28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb2b Hangul Syllable-CB2B | cb2a Hangul Syllable-CB2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb2d Hangul Syllable-CB2D | cb2c Hangul Syllable-CB2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb2f Hangul Syllable-CB2F | cb2e Hangul Syllable-CB2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb31 Hangul Syllable-CB31 | cb30 Hangul Syllable-CB30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb33 Hangul Syllable-CB33 | cb32 Hangul Syllable-CB32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb35 Hangul Syllable-CB35 | cb34 Hangul Syllable-CB34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb37 Hangul Syllable-CB37 | cb36 Hangul Syllable-CB36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb39 Hangul Syllable-CB39 | cb38 Hangul Syllable-CB38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb3b Hangul Syllable-CB3B | cb3a Hangul Syllable-CB3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb3d Hangul Syllable-CB3D | cb3c Hangul Syllable-CB3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb3f Hangul Syllable-CB3F | cb3e Hangul Syllable-CB3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb41 Hangul Syllable-CB41 | cb40 Hangul Syllable-CB40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb43 Hangul Syllable-CB43 | cb42 Hangul Syllable-CB42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb45 Hangul Syllable-CB45 | cb44 Hangul Syllable-CB44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb47 Hangul Syllable-CB47 | cb46 Hangul Syllable-CB46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb49 Hangul Syllable-CB49 | cb48 Hangul Syllable-CB48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb4b Hangul Syllable-CB4B | cb4a Hangul Syllable-CB4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb4d Hangul Syllable-CB4D | cb4c Hangul Syllable-CB4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb4f Hangul Syllable-CB4F | cb4e Hangul Syllable-CB4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb51 Hangul Syllable-CB51 | cb50 Hangul Syllable-CB50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb53 Hangul Syllable-CB53 | cb52 Hangul Syllable-CB52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb55 Hangul Syllable-CB55 | cb54 Hangul Syllable-CB54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb57 Hangul Syllable-CB57 | cb56 Hangul Syllable-CB56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb59 Hangul Syllable-CB59 | cb58 Hangul Syllable-CB58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb5b Hangul Syllable-CB5B | cb5a Hangul Syllable-CB5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb5d Hangul Syllable-CB5D | cb5c Hangul Syllable-CB5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb5f Hangul Syllable-CB5F | cb5e Hangul Syllable-CB5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb61 Hangul Syllable-CB61 | cb60 Hangul Syllable-CB60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb63 Hangul Syllable-CB63 | cb62 Hangul Syllable-CB62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb65 Hangul Syllable-CB65 | cb64 Hangul Syllable-CB64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb67 Hangul Syllable-CB67 | cb66 Hangul Syllable-CB66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb69 Hangul Syllable-CB69 | cb68 Hangul Syllable-CB68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb6b Hangul Syllable-CB6B | cb6a Hangul Syllable-CB6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb6d Hangul Syllable-CB6D | cb6c Hangul Syllable-CB6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb6f Hangul Syllable-CB6F | cb6e Hangul Syllable-CB6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb71 Hangul Syllable-CB71 | cb70 Hangul Syllable-CB70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb73 Hangul Syllable-CB73 | cb72 Hangul Syllable-CB72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb75 Hangul Syllable-CB75 | cb74 Hangul Syllable-CB74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb77 Hangul Syllable-CB77 | cb76 Hangul Syllable-CB76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb79 Hangul Syllable-CB79 | cb78 Hangul Syllable-CB78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb7b Hangul Syllable-CB7B | cb7a Hangul Syllable-CB7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb7d Hangul Syllable-CB7D | cb7c Hangul Syllable-CB7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb7f Hangul Syllable-CB7F | cb7e Hangul Syllable-CB7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb81 Hangul Syllable-CB81 | cb80 Hangul Syllable-CB80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb83 Hangul Syllable-CB83 | cb82 Hangul Syllable-CB82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb85 Hangul Syllable-CB85 | cb84 Hangul Syllable-CB84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb87 Hangul Syllable-CB87 | cb86 Hangul Syllable-CB86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb89 Hangul Syllable-CB89 | cb88 Hangul Syllable-CB88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb8b Hangul Syllable-CB8B | cb8a Hangul Syllable-CB8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb8d Hangul Syllable-CB8D | cb8c Hangul Syllable-CB8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb8f Hangul Syllable-CB8F | cb8e Hangul Syllable-CB8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb91 Hangul Syllable-CB91 | cb90 Hangul Syllable-CB90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb93 Hangul Syllable-CB93 | cb92 Hangul Syllable-CB92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb95 Hangul Syllable-CB95 | cb94 Hangul Syllable-CB94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb97 Hangul Syllable-CB97 | cb96 Hangul Syllable-CB96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb99 Hangul Syllable-CB99 | cb98 Hangul Syllable-CB98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb9b Hangul Syllable-CB9B | cb9a Hangul Syllable-CB9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb9d Hangul Syllable-CB9D | cb9c Hangul Syllable-CB9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb9f Hangul Syllable-CB9F | cb9e Hangul Syllable-CB9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cba1 Hangul Syllable-CBA1 | cba0 Hangul Syllable-CBA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cba3 Hangul Syllable-CBA3 | cba2 Hangul Syllable-CBA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cba5 Hangul Syllable-CBA5 | cba4 Hangul Syllable-CBA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cba7 Hangul Syllable-CBA7 | cba6 Hangul Syllable-CBA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cba9 Hangul Syllable-CBA9 | cba8 Hangul Syllable-CBA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbab Hangul Syllable-CBAB | cbaa Hangul Syllable-CBAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbad Hangul Syllable-CBAD | cbac Hangul Syllable-CBAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbaf Hangul Syllable-CBAF | cbae Hangul Syllable-CBAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbb1 Hangul Syllable-CBB1 | cbb0 Hangul Syllable-CBB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbb3 Hangul Syllable-CBB3 | cbb2 Hangul Syllable-CBB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbb5 Hangul Syllable-CBB5 | cbb4 Hangul Syllable-CBB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbb7 Hangul Syllable-CBB7 | cbb6 Hangul Syllable-CBB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbb9 Hangul Syllable-CBB9 | cbb8 Hangul Syllable-CBB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbbb Hangul Syllable-CBBB | cbba Hangul Syllable-CBBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbbd Hangul Syllable-CBBD | cbbc Hangul Syllable-CBBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbbf Hangul Syllable-CBBF | cbbe Hangul Syllable-CBBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbc1 Hangul Syllable-CBC1 | cbc0 Hangul Syllable-CBC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbc3 Hangul Syllable-CBC3 | cbc2 Hangul Syllable-CBC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbc5 Hangul Syllable-CBC5 | cbc4 Hangul Syllable-CBC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbc7 Hangul Syllable-CBC7 | cbc6 Hangul Syllable-CBC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbc9 Hangul Syllable-CBC9 | cbc8 Hangul Syllable-CBC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbcb Hangul Syllable-CBCB | cbca Hangul Syllable-CBCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbcd Hangul Syllable-CBCD | cbcc Hangul Syllable-CBCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbcf Hangul Syllable-CBCF | cbce Hangul Syllable-CBCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbd1 Hangul Syllable-CBD1 | cbd0 Hangul Syllable-CBD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbd3 Hangul Syllable-CBD3 | cbd2 Hangul Syllable-CBD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbd5 Hangul Syllable-CBD5 | cbd4 Hangul Syllable-CBD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbd7 Hangul Syllable-CBD7 | cbd6 Hangul Syllable-CBD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbd9 Hangul Syllable-CBD9 | cbd8 Hangul Syllable-CBD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbdb Hangul Syllable-CBDB | cbda Hangul Syllable-CBDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbdd Hangul Syllable-CBDD | cbdc Hangul Syllable-CBDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbdf Hangul Syllable-CBDF | cbde Hangul Syllable-CBDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbe1 Hangul Syllable-CBE1 | cbe0 Hangul Syllable-CBE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbe3 Hangul Syllable-CBE3 | cbe2 Hangul Syllable-CBE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbe5 Hangul Syllable-CBE5 | cbe4 Hangul Syllable-CBE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbe7 Hangul Syllable-CBE7 | cbe6 Hangul Syllable-CBE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbe9 Hangul Syllable-CBE9 | cbe8 Hangul Syllable-CBE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbeb Hangul Syllable-CBEB | cbea Hangul Syllable-CBEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbed Hangul Syllable-CBED | cbec Hangul Syllable-CBEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbef Hangul Syllable-CBEF | cbee Hangul Syllable-CBEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbf1 Hangul Syllable-CBF1 | cbf0 Hangul Syllable-CBF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbf3 Hangul Syllable-CBF3 | cbf2 Hangul Syllable-CBF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbf5 Hangul Syllable-CBF5 | cbf4 Hangul Syllable-CBF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbf7 Hangul Syllable-CBF7 | cbf6 Hangul Syllable-CBF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbf9 Hangul Syllable-CBF9 | cbf8 Hangul Syllable-CBF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbfb Hangul Syllable-CBFB | cbfa Hangul Syllable-CBFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbfd Hangul Syllable-CBFD | cbfc Hangul Syllable-CBFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbff Hangul Syllable-CBFF | cbfe Hangul Syllable-CBFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc01 Hangul Syllable-CC01 | cc00 Hangul Syllable-CC00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc03 Hangul Syllable-CC03 | cc02 Hangul Syllable-CC02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc05 Hangul Syllable-CC05 | cc04 Hangul Syllable-CC04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc07 Hangul Syllable-CC07 | cc06 Hangul Syllable-CC06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc09 Hangul Syllable-CC09 | cc08 Hangul Syllable-CC08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc0b Hangul Syllable-CC0B | cc0a Hangul Syllable-CC0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc0d Hangul Syllable-CC0D | cc0c Hangul Syllable-CC0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc0f Hangul Syllable-CC0F | cc0e Hangul Syllable-CC0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc11 Hangul Syllable-CC11 | cc10 Hangul Syllable-CC10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc13 Hangul Syllable-CC13 | cc12 Hangul Syllable-CC12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc15 Hangul Syllable-CC15 | cc14 Hangul Syllable-CC14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc17 Hangul Syllable-CC17 | cc16 Hangul Syllable-CC16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc19 Hangul Syllable-CC19 | cc18 Hangul Syllable-CC18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc1b Hangul Syllable-CC1B | cc1a Hangul Syllable-CC1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc1d Hangul Syllable-CC1D | cc1c Hangul Syllable-CC1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc1f Hangul Syllable-CC1F | cc1e Hangul Syllable-CC1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc21 Hangul Syllable-CC21 | cc20 Hangul Syllable-CC20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc23 Hangul Syllable-CC23 | cc22 Hangul Syllable-CC22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc25 Hangul Syllable-CC25 | cc24 Hangul Syllable-CC24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc27 Hangul Syllable-CC27 | cc26 Hangul Syllable-CC26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc29 Hangul Syllable-CC29 | cc28 Hangul Syllable-CC28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc2b Hangul Syllable-CC2B | cc2a Hangul Syllable-CC2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc2d Hangul Syllable-CC2D | cc2c Hangul Syllable-CC2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc2f Hangul Syllable-CC2F | cc2e Hangul Syllable-CC2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc31 Hangul Syllable-CC31 | cc30 Hangul Syllable-CC30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc33 Hangul Syllable-CC33 | cc32 Hangul Syllable-CC32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc35 Hangul Syllable-CC35 | cc34 Hangul Syllable-CC34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc37 Hangul Syllable-CC37 | cc36 Hangul Syllable-CC36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc39 Hangul Syllable-CC39 | cc38 Hangul Syllable-CC38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc3b Hangul Syllable-CC3B | cc3a Hangul Syllable-CC3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc3d Hangul Syllable-CC3D | cc3c Hangul Syllable-CC3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc3f Hangul Syllable-CC3F | cc3e Hangul Syllable-CC3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc41 Hangul Syllable-CC41 | cc40 Hangul Syllable-CC40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc43 Hangul Syllable-CC43 | cc42 Hangul Syllable-CC42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc45 Hangul Syllable-CC45 | cc44 Hangul Syllable-CC44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc47 Hangul Syllable-CC47 | cc46 Hangul Syllable-CC46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc49 Hangul Syllable-CC49 | cc48 Hangul Syllable-CC48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc4b Hangul Syllable-CC4B | cc4a Hangul Syllable-CC4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc4d Hangul Syllable-CC4D | cc4c Hangul Syllable-CC4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc4f Hangul Syllable-CC4F | cc4e Hangul Syllable-CC4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc51 Hangul Syllable-CC51 | cc50 Hangul Syllable-CC50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc53 Hangul Syllable-CC53 | cc52 Hangul Syllable-CC52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc55 Hangul Syllable-CC55 | cc54 Hangul Syllable-CC54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc57 Hangul Syllable-CC57 | cc56 Hangul Syllable-CC56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc59 Hangul Syllable-CC59 | cc58 Hangul Syllable-CC58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc5b Hangul Syllable-CC5B | cc5a Hangul Syllable-CC5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc5d Hangul Syllable-CC5D | cc5c Hangul Syllable-CC5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc5f Hangul Syllable-CC5F | cc5e Hangul Syllable-CC5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc61 Hangul Syllable-CC61 | cc60 Hangul Syllable-CC60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc63 Hangul Syllable-CC63 | cc62 Hangul Syllable-CC62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc65 Hangul Syllable-CC65 | cc64 Hangul Syllable-CC64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc67 Hangul Syllable-CC67 | cc66 Hangul Syllable-CC66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc69 Hangul Syllable-CC69 | cc68 Hangul Syllable-CC68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc6b Hangul Syllable-CC6B | cc6a Hangul Syllable-CC6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc6d Hangul Syllable-CC6D | cc6c Hangul Syllable-CC6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc6f Hangul Syllable-CC6F | cc6e Hangul Syllable-CC6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc71 Hangul Syllable-CC71 | cc70 Hangul Syllable-CC70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc73 Hangul Syllable-CC73 | cc72 Hangul Syllable-CC72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc75 Hangul Syllable-CC75 | cc74 Hangul Syllable-CC74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc77 Hangul Syllable-CC77 | cc76 Hangul Syllable-CC76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc79 Hangul Syllable-CC79 | cc78 Hangul Syllable-CC78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc7b Hangul Syllable-CC7B | cc7a Hangul Syllable-CC7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc7d Hangul Syllable-CC7D | cc7c Hangul Syllable-CC7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc7f Hangul Syllable-CC7F | cc7e Hangul Syllable-CC7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc81 Hangul Syllable-CC81 | cc80 Hangul Syllable-CC80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc83 Hangul Syllable-CC83 | cc82 Hangul Syllable-CC82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc85 Hangul Syllable-CC85 | cc84 Hangul Syllable-CC84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc87 Hangul Syllable-CC87 | cc86 Hangul Syllable-CC86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc89 Hangul Syllable-CC89 | cc88 Hangul Syllable-CC88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc8b Hangul Syllable-CC8B | cc8a Hangul Syllable-CC8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc8d Hangul Syllable-CC8D | cc8c Hangul Syllable-CC8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc8f Hangul Syllable-CC8F | cc8e Hangul Syllable-CC8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc91 Hangul Syllable-CC91 | cc90 Hangul Syllable-CC90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc93 Hangul Syllable-CC93 | cc92 Hangul Syllable-CC92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc95 Hangul Syllable-CC95 | cc94 Hangul Syllable-CC94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc97 Hangul Syllable-CC97 | cc96 Hangul Syllable-CC96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc99 Hangul Syllable-CC99 | cc98 Hangul Syllable-CC98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc9b Hangul Syllable-CC9B | cc9a Hangul Syllable-CC9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc9d Hangul Syllable-CC9D | cc9c Hangul Syllable-CC9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc9f Hangul Syllable-CC9F | cc9e Hangul Syllable-CC9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cca1 Hangul Syllable-CCA1 | cca0 Hangul Syllable-CCA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cca3 Hangul Syllable-CCA3 | cca2 Hangul Syllable-CCA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cca5 Hangul Syllable-CCA5 | cca4 Hangul Syllable-CCA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cca7 Hangul Syllable-CCA7 | cca6 Hangul Syllable-CCA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cca9 Hangul Syllable-CCA9 | cca8 Hangul Syllable-CCA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccab Hangul Syllable-CCAB | ccaa Hangul Syllable-CCAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccad Hangul Syllable-CCAD | ccac Hangul Syllable-CCAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccaf Hangul Syllable-CCAF | ccae Hangul Syllable-CCAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccb1 Hangul Syllable-CCB1 | ccb0 Hangul Syllable-CCB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccb3 Hangul Syllable-CCB3 | ccb2 Hangul Syllable-CCB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccb5 Hangul Syllable-CCB5 | ccb4 Hangul Syllable-CCB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccb7 Hangul Syllable-CCB7 | ccb6 Hangul Syllable-CCB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccb9 Hangul Syllable-CCB9 | ccb8 Hangul Syllable-CCB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccbb Hangul Syllable-CCBB | ccba Hangul Syllable-CCBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccbd Hangul Syllable-CCBD | ccbc Hangul Syllable-CCBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccbf Hangul Syllable-CCBF | ccbe Hangul Syllable-CCBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccc1 Hangul Syllable-CCC1 | ccc0 Hangul Syllable-CCC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccc3 Hangul Syllable-CCC3 | ccc2 Hangul Syllable-CCC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccc5 Hangul Syllable-CCC5 | ccc4 Hangul Syllable-CCC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccc7 Hangul Syllable-CCC7 | ccc6 Hangul Syllable-CCC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccc9 Hangul Syllable-CCC9 | ccc8 Hangul Syllable-CCC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cccb Hangul Syllable-CCCB | ccca Hangul Syllable-CCCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cccd Hangul Syllable-CCCD | cccc Hangul Syllable-CCCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cccf Hangul Syllable-CCCF | ccce Hangul Syllable-CCCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccd1 Hangul Syllable-CCD1 | ccd0 Hangul Syllable-CCD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccd3 Hangul Syllable-CCD3 | ccd2 Hangul Syllable-CCD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccd5 Hangul Syllable-CCD5 | ccd4 Hangul Syllable-CCD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccd7 Hangul Syllable-CCD7 | ccd6 Hangul Syllable-CCD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccd9 Hangul Syllable-CCD9 | ccd8 Hangul Syllable-CCD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccdb Hangul Syllable-CCDB | ccda Hangul Syllable-CCDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccdd Hangul Syllable-CCDD | ccdc Hangul Syllable-CCDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccdf Hangul Syllable-CCDF | ccde Hangul Syllable-CCDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cce1 Hangul Syllable-CCE1 | cce0 Hangul Syllable-CCE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cce3 Hangul Syllable-CCE3 | cce2 Hangul Syllable-CCE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cce5 Hangul Syllable-CCE5 | cce4 Hangul Syllable-CCE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cce7 Hangul Syllable-CCE7 | cce6 Hangul Syllable-CCE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cce9 Hangul Syllable-CCE9 | cce8 Hangul Syllable-CCE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cceb Hangul Syllable-CCEB | ccea Hangul Syllable-CCEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cced Hangul Syllable-CCED | ccec Hangul Syllable-CCEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccef Hangul Syllable-CCEF | ccee Hangul Syllable-CCEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccf1 Hangul Syllable-CCF1 | ccf0 Hangul Syllable-CCF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccf3 Hangul Syllable-CCF3 | ccf2 Hangul Syllable-CCF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccf5 Hangul Syllable-CCF5 | ccf4 Hangul Syllable-CCF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccf7 Hangul Syllable-CCF7 | ccf6 Hangul Syllable-CCF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccf9 Hangul Syllable-CCF9 | ccf8 Hangul Syllable-CCF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccfb Hangul Syllable-CCFB | ccfa Hangul Syllable-CCFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccfd Hangul Syllable-CCFD | ccfc Hangul Syllable-CCFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccff Hangul Syllable-CCFF | ccfe Hangul Syllable-CCFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd01 Hangul Syllable-CD01 | cd00 Hangul Syllable-CD00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd03 Hangul Syllable-CD03 | cd02 Hangul Syllable-CD02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd05 Hangul Syllable-CD05 | cd04 Hangul Syllable-CD04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd07 Hangul Syllable-CD07 | cd06 Hangul Syllable-CD06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd09 Hangul Syllable-CD09 | cd08 Hangul Syllable-CD08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd0b Hangul Syllable-CD0B | cd0a Hangul Syllable-CD0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd0d Hangul Syllable-CD0D | cd0c Hangul Syllable-CD0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd0f Hangul Syllable-CD0F | cd0e Hangul Syllable-CD0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd11 Hangul Syllable-CD11 | cd10 Hangul Syllable-CD10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd13 Hangul Syllable-CD13 | cd12 Hangul Syllable-CD12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd15 Hangul Syllable-CD15 | cd14 Hangul Syllable-CD14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd17 Hangul Syllable-CD17 | cd16 Hangul Syllable-CD16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd19 Hangul Syllable-CD19 | cd18 Hangul Syllable-CD18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd1b Hangul Syllable-CD1B | cd1a Hangul Syllable-CD1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd1d Hangul Syllable-CD1D | cd1c Hangul Syllable-CD1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd1f Hangul Syllable-CD1F | cd1e Hangul Syllable-CD1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd21 Hangul Syllable-CD21 | cd20 Hangul Syllable-CD20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd23 Hangul Syllable-CD23 | cd22 Hangul Syllable-CD22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd25 Hangul Syllable-CD25 | cd24 Hangul Syllable-CD24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd27 Hangul Syllable-CD27 | cd26 Hangul Syllable-CD26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd29 Hangul Syllable-CD29 | cd28 Hangul Syllable-CD28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd2b Hangul Syllable-CD2B | cd2a Hangul Syllable-CD2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd2d Hangul Syllable-CD2D | cd2c Hangul Syllable-CD2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd2f Hangul Syllable-CD2F | cd2e Hangul Syllable-CD2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd31 Hangul Syllable-CD31 | cd30 Hangul Syllable-CD30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd33 Hangul Syllable-CD33 | cd32 Hangul Syllable-CD32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd35 Hangul Syllable-CD35 | cd34 Hangul Syllable-CD34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd37 Hangul Syllable-CD37 | cd36 Hangul Syllable-CD36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd39 Hangul Syllable-CD39 | cd38 Hangul Syllable-CD38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd3b Hangul Syllable-CD3B | cd3a Hangul Syllable-CD3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd3d Hangul Syllable-CD3D | cd3c Hangul Syllable-CD3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd3f Hangul Syllable-CD3F | cd3e Hangul Syllable-CD3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd41 Hangul Syllable-CD41 | cd40 Hangul Syllable-CD40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd43 Hangul Syllable-CD43 | cd42 Hangul Syllable-CD42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd45 Hangul Syllable-CD45 | cd44 Hangul Syllable-CD44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd47 Hangul Syllable-CD47 | cd46 Hangul Syllable-CD46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd49 Hangul Syllable-CD49 | cd48 Hangul Syllable-CD48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd4b Hangul Syllable-CD4B | cd4a Hangul Syllable-CD4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd4d Hangul Syllable-CD4D | cd4c Hangul Syllable-CD4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd4f Hangul Syllable-CD4F | cd4e Hangul Syllable-CD4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd51 Hangul Syllable-CD51 | cd50 Hangul Syllable-CD50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd53 Hangul Syllable-CD53 | cd52 Hangul Syllable-CD52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd55 Hangul Syllable-CD55 | cd54 Hangul Syllable-CD54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd57 Hangul Syllable-CD57 | cd56 Hangul Syllable-CD56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd59 Hangul Syllable-CD59 | cd58 Hangul Syllable-CD58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd5b Hangul Syllable-CD5B | cd5a Hangul Syllable-CD5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd5d Hangul Syllable-CD5D | cd5c Hangul Syllable-CD5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd5f Hangul Syllable-CD5F | cd5e Hangul Syllable-CD5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd61 Hangul Syllable-CD61 | cd60 Hangul Syllable-CD60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd63 Hangul Syllable-CD63 | cd62 Hangul Syllable-CD62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd65 Hangul Syllable-CD65 | cd64 Hangul Syllable-CD64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd67 Hangul Syllable-CD67 | cd66 Hangul Syllable-CD66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd69 Hangul Syllable-CD69 | cd68 Hangul Syllable-CD68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd6b Hangul Syllable-CD6B | cd6a Hangul Syllable-CD6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd6d Hangul Syllable-CD6D | cd6c Hangul Syllable-CD6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd6f Hangul Syllable-CD6F | cd6e Hangul Syllable-CD6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd71 Hangul Syllable-CD71 | cd70 Hangul Syllable-CD70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd73 Hangul Syllable-CD73 | cd72 Hangul Syllable-CD72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd75 Hangul Syllable-CD75 | cd74 Hangul Syllable-CD74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd77 Hangul Syllable-CD77 | cd76 Hangul Syllable-CD76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd79 Hangul Syllable-CD79 | cd78 Hangul Syllable-CD78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd7b Hangul Syllable-CD7B | cd7a Hangul Syllable-CD7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd7d Hangul Syllable-CD7D | cd7c Hangul Syllable-CD7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd7f Hangul Syllable-CD7F | cd7e Hangul Syllable-CD7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd81 Hangul Syllable-CD81 | cd80 Hangul Syllable-CD80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd83 Hangul Syllable-CD83 | cd82 Hangul Syllable-CD82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd85 Hangul Syllable-CD85 | cd84 Hangul Syllable-CD84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd87 Hangul Syllable-CD87 | cd86 Hangul Syllable-CD86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd89 Hangul Syllable-CD89 | cd88 Hangul Syllable-CD88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd8b Hangul Syllable-CD8B | cd8a Hangul Syllable-CD8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd8d Hangul Syllable-CD8D | cd8c Hangul Syllable-CD8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd8f Hangul Syllable-CD8F | cd8e Hangul Syllable-CD8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd91 Hangul Syllable-CD91 | cd90 Hangul Syllable-CD90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd93 Hangul Syllable-CD93 | cd92 Hangul Syllable-CD92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd95 Hangul Syllable-CD95 | cd94 Hangul Syllable-CD94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd97 Hangul Syllable-CD97 | cd96 Hangul Syllable-CD96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd99 Hangul Syllable-CD99 | cd98 Hangul Syllable-CD98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd9b Hangul Syllable-CD9B | cd9a Hangul Syllable-CD9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd9d Hangul Syllable-CD9D | cd9c Hangul Syllable-CD9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd9f Hangul Syllable-CD9F | cd9e Hangul Syllable-CD9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cda1 Hangul Syllable-CDA1 | cda0 Hangul Syllable-CDA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cda3 Hangul Syllable-CDA3 | cda2 Hangul Syllable-CDA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cda5 Hangul Syllable-CDA5 | cda4 Hangul Syllable-CDA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cda7 Hangul Syllable-CDA7 | cda6 Hangul Syllable-CDA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cda9 Hangul Syllable-CDA9 | cda8 Hangul Syllable-CDA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdab Hangul Syllable-CDAB | cdaa Hangul Syllable-CDAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdad Hangul Syllable-CDAD | cdac Hangul Syllable-CDAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdaf Hangul Syllable-CDAF | cdae Hangul Syllable-CDAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdb1 Hangul Syllable-CDB1 | cdb0 Hangul Syllable-CDB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdb3 Hangul Syllable-CDB3 | cdb2 Hangul Syllable-CDB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdb5 Hangul Syllable-CDB5 | cdb4 Hangul Syllable-CDB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdb7 Hangul Syllable-CDB7 | cdb6 Hangul Syllable-CDB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdb9 Hangul Syllable-CDB9 | cdb8 Hangul Syllable-CDB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdbb Hangul Syllable-CDBB | cdba Hangul Syllable-CDBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdbd Hangul Syllable-CDBD | cdbc Hangul Syllable-CDBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdbf Hangul Syllable-CDBF | cdbe Hangul Syllable-CDBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdc1 Hangul Syllable-CDC1 | cdc0 Hangul Syllable-CDC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdc3 Hangul Syllable-CDC3 | cdc2 Hangul Syllable-CDC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdc5 Hangul Syllable-CDC5 | cdc4 Hangul Syllable-CDC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdc7 Hangul Syllable-CDC7 | cdc6 Hangul Syllable-CDC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdc9 Hangul Syllable-CDC9 | cdc8 Hangul Syllable-CDC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdcb Hangul Syllable-CDCB | cdca Hangul Syllable-CDCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdcd Hangul Syllable-CDCD | cdcc Hangul Syllable-CDCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdcf Hangul Syllable-CDCF | cdce Hangul Syllable-CDCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdd1 Hangul Syllable-CDD1 | cdd0 Hangul Syllable-CDD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdd3 Hangul Syllable-CDD3 | cdd2 Hangul Syllable-CDD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdd5 Hangul Syllable-CDD5 | cdd4 Hangul Syllable-CDD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdd7 Hangul Syllable-CDD7 | cdd6 Hangul Syllable-CDD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdd9 Hangul Syllable-CDD9 | cdd8 Hangul Syllable-CDD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cddb Hangul Syllable-CDDB | cdda Hangul Syllable-CDDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cddd Hangul Syllable-CDDD | cddc Hangul Syllable-CDDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cddf Hangul Syllable-CDDF | cdde Hangul Syllable-CDDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cde1 Hangul Syllable-CDE1 | cde0 Hangul Syllable-CDE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cde3 Hangul Syllable-CDE3 | cde2 Hangul Syllable-CDE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cde5 Hangul Syllable-CDE5 | cde4 Hangul Syllable-CDE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cde7 Hangul Syllable-CDE7 | cde6 Hangul Syllable-CDE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cde9 Hangul Syllable-CDE9 | cde8 Hangul Syllable-CDE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdeb Hangul Syllable-CDEB | cdea Hangul Syllable-CDEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cded Hangul Syllable-CDED | cdec Hangul Syllable-CDEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdef Hangul Syllable-CDEF | cdee Hangul Syllable-CDEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdf1 Hangul Syllable-CDF1 | cdf0 Hangul Syllable-CDF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdf3 Hangul Syllable-CDF3 | cdf2 Hangul Syllable-CDF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdf5 Hangul Syllable-CDF5 | cdf4 Hangul Syllable-CDF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdf7 Hangul Syllable-CDF7 | cdf6 Hangul Syllable-CDF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdf9 Hangul Syllable-CDF9 | cdf8 Hangul Syllable-CDF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdfb Hangul Syllable-CDFB | cdfa Hangul Syllable-CDFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdfd Hangul Syllable-CDFD | cdfc Hangul Syllable-CDFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdff Hangul Syllable-CDFF | cdfe Hangul Syllable-CDFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce01 Hangul Syllable-CE01 | ce00 Hangul Syllable-CE00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce03 Hangul Syllable-CE03 | ce02 Hangul Syllable-CE02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce05 Hangul Syllable-CE05 | ce04 Hangul Syllable-CE04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce07 Hangul Syllable-CE07 | ce06 Hangul Syllable-CE06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce09 Hangul Syllable-CE09 | ce08 Hangul Syllable-CE08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce0b Hangul Syllable-CE0B | ce0a Hangul Syllable-CE0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce0d Hangul Syllable-CE0D | ce0c Hangul Syllable-CE0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce0f Hangul Syllable-CE0F | ce0e Hangul Syllable-CE0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce11 Hangul Syllable-CE11 | ce10 Hangul Syllable-CE10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce13 Hangul Syllable-CE13 | ce12 Hangul Syllable-CE12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce15 Hangul Syllable-CE15 | ce14 Hangul Syllable-CE14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce17 Hangul Syllable-CE17 | ce16 Hangul Syllable-CE16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce19 Hangul Syllable-CE19 | ce18 Hangul Syllable-CE18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce1b Hangul Syllable-CE1B | ce1a Hangul Syllable-CE1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce1d Hangul Syllable-CE1D | ce1c Hangul Syllable-CE1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce1f Hangul Syllable-CE1F | ce1e Hangul Syllable-CE1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce21 Hangul Syllable-CE21 | ce20 Hangul Syllable-CE20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce23 Hangul Syllable-CE23 | ce22 Hangul Syllable-CE22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce25 Hangul Syllable-CE25 | ce24 Hangul Syllable-CE24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce27 Hangul Syllable-CE27 | ce26 Hangul Syllable-CE26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce29 Hangul Syllable-CE29 | ce28 Hangul Syllable-CE28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce2b Hangul Syllable-CE2B | ce2a Hangul Syllable-CE2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce2d Hangul Syllable-CE2D | ce2c Hangul Syllable-CE2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce2f Hangul Syllable-CE2F | ce2e Hangul Syllable-CE2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce31 Hangul Syllable-CE31 | ce30 Hangul Syllable-CE30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce33 Hangul Syllable-CE33 | ce32 Hangul Syllable-CE32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce35 Hangul Syllable-CE35 | ce34 Hangul Syllable-CE34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce37 Hangul Syllable-CE37 | ce36 Hangul Syllable-CE36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce39 Hangul Syllable-CE39 | ce38 Hangul Syllable-CE38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce3b Hangul Syllable-CE3B | ce3a Hangul Syllable-CE3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce3d Hangul Syllable-CE3D | ce3c Hangul Syllable-CE3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce3f Hangul Syllable-CE3F | ce3e Hangul Syllable-CE3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce41 Hangul Syllable-CE41 | ce40 Hangul Syllable-CE40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce43 Hangul Syllable-CE43 | ce42 Hangul Syllable-CE42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce45 Hangul Syllable-CE45 | ce44 Hangul Syllable-CE44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce47 Hangul Syllable-CE47 | ce46 Hangul Syllable-CE46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce49 Hangul Syllable-CE49 | ce48 Hangul Syllable-CE48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce4b Hangul Syllable-CE4B | ce4a Hangul Syllable-CE4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce4d Hangul Syllable-CE4D | ce4c Hangul Syllable-CE4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce4f Hangul Syllable-CE4F | ce4e Hangul Syllable-CE4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce51 Hangul Syllable-CE51 | ce50 Hangul Syllable-CE50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce53 Hangul Syllable-CE53 | ce52 Hangul Syllable-CE52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce55 Hangul Syllable-CE55 | ce54 Hangul Syllable-CE54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce57 Hangul Syllable-CE57 | ce56 Hangul Syllable-CE56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce59 Hangul Syllable-CE59 | ce58 Hangul Syllable-CE58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce5b Hangul Syllable-CE5B | ce5a Hangul Syllable-CE5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce5d Hangul Syllable-CE5D | ce5c Hangul Syllable-CE5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce5f Hangul Syllable-CE5F | ce5e Hangul Syllable-CE5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce61 Hangul Syllable-CE61 | ce60 Hangul Syllable-CE60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce63 Hangul Syllable-CE63 | ce62 Hangul Syllable-CE62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce65 Hangul Syllable-CE65 | ce64 Hangul Syllable-CE64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce67 Hangul Syllable-CE67 | ce66 Hangul Syllable-CE66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce69 Hangul Syllable-CE69 | ce68 Hangul Syllable-CE68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce6b Hangul Syllable-CE6B | ce6a Hangul Syllable-CE6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce6d Hangul Syllable-CE6D | ce6c Hangul Syllable-CE6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce6f Hangul Syllable-CE6F | ce6e Hangul Syllable-CE6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce71 Hangul Syllable-CE71 | ce70 Hangul Syllable-CE70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce73 Hangul Syllable-CE73 | ce72 Hangul Syllable-CE72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce75 Hangul Syllable-CE75 | ce74 Hangul Syllable-CE74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce77 Hangul Syllable-CE77 | ce76 Hangul Syllable-CE76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce79 Hangul Syllable-CE79 | ce78 Hangul Syllable-CE78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce7b Hangul Syllable-CE7B | ce7a Hangul Syllable-CE7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce7d Hangul Syllable-CE7D | ce7c Hangul Syllable-CE7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce7f Hangul Syllable-CE7F | ce7e Hangul Syllable-CE7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce81 Hangul Syllable-CE81 | ce80 Hangul Syllable-CE80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce83 Hangul Syllable-CE83 | ce82 Hangul Syllable-CE82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce85 Hangul Syllable-CE85 | ce84 Hangul Syllable-CE84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce87 Hangul Syllable-CE87 | ce86 Hangul Syllable-CE86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce89 Hangul Syllable-CE89 | ce88 Hangul Syllable-CE88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce8b Hangul Syllable-CE8B | ce8a Hangul Syllable-CE8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce8d Hangul Syllable-CE8D | ce8c Hangul Syllable-CE8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce8f Hangul Syllable-CE8F | ce8e Hangul Syllable-CE8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce91 Hangul Syllable-CE91 | ce90 Hangul Syllable-CE90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce93 Hangul Syllable-CE93 | ce92 Hangul Syllable-CE92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce95 Hangul Syllable-CE95 | ce94 Hangul Syllable-CE94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce97 Hangul Syllable-CE97 | ce96 Hangul Syllable-CE96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce99 Hangul Syllable-CE99 | ce98 Hangul Syllable-CE98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce9b Hangul Syllable-CE9B | ce9a Hangul Syllable-CE9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce9d Hangul Syllable-CE9D | ce9c Hangul Syllable-CE9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce9f Hangul Syllable-CE9F | ce9e Hangul Syllable-CE9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cea1 Hangul Syllable-CEA1 | cea0 Hangul Syllable-CEA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cea3 Hangul Syllable-CEA3 | cea2 Hangul Syllable-CEA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cea5 Hangul Syllable-CEA5 | cea4 Hangul Syllable-CEA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cea7 Hangul Syllable-CEA7 | cea6 Hangul Syllable-CEA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cea9 Hangul Syllable-CEA9 | cea8 Hangul Syllable-CEA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceab Hangul Syllable-CEAB | ceaa Hangul Syllable-CEAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cead Hangul Syllable-CEAD | ceac Hangul Syllable-CEAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceaf Hangul Syllable-CEAF | ceae Hangul Syllable-CEAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceb1 Hangul Syllable-CEB1 | ceb0 Hangul Syllable-CEB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceb3 Hangul Syllable-CEB3 | ceb2 Hangul Syllable-CEB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceb5 Hangul Syllable-CEB5 | ceb4 Hangul Syllable-CEB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceb7 Hangul Syllable-CEB7 | ceb6 Hangul Syllable-CEB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceb9 Hangul Syllable-CEB9 | ceb8 Hangul Syllable-CEB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cebb Hangul Syllable-CEBB | ceba Hangul Syllable-CEBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cebd Hangul Syllable-CEBD | cebc Hangul Syllable-CEBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cebf Hangul Syllable-CEBF | cebe Hangul Syllable-CEBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cec1 Hangul Syllable-CEC1 | cec0 Hangul Syllable-CEC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cec3 Hangul Syllable-CEC3 | cec2 Hangul Syllable-CEC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cec5 Hangul Syllable-CEC5 | cec4 Hangul Syllable-CEC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cec7 Hangul Syllable-CEC7 | cec6 Hangul Syllable-CEC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cec9 Hangul Syllable-CEC9 | cec8 Hangul Syllable-CEC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cecb Hangul Syllable-CECB | ceca Hangul Syllable-CECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cecd Hangul Syllable-CECD | cecc Hangul Syllable-CECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cecf Hangul Syllable-CECF | cece Hangul Syllable-CECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ced1 Hangul Syllable-CED1 | ced0 Hangul Syllable-CED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ced3 Hangul Syllable-CED3 | ced2 Hangul Syllable-CED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ced5 Hangul Syllable-CED5 | ced4 Hangul Syllable-CED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ced7 Hangul Syllable-CED7 | ced6 Hangul Syllable-CED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ced9 Hangul Syllable-CED9 | ced8 Hangul Syllable-CED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cedb Hangul Syllable-CEDB | ceda Hangul Syllable-CEDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cedd Hangul Syllable-CEDD | cedc Hangul Syllable-CEDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cedf Hangul Syllable-CEDF | cede Hangul Syllable-CEDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cee1 Hangul Syllable-CEE1 | cee0 Hangul Syllable-CEE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cee3 Hangul Syllable-CEE3 | cee2 Hangul Syllable-CEE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cee5 Hangul Syllable-CEE5 | cee4 Hangul Syllable-CEE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cee7 Hangul Syllable-CEE7 | cee6 Hangul Syllable-CEE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cee9 Hangul Syllable-CEE9 | cee8 Hangul Syllable-CEE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceeb Hangul Syllable-CEEB | ceea Hangul Syllable-CEEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceed Hangul Syllable-CEED | ceec Hangul Syllable-CEEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceef Hangul Syllable-CEEF | ceee Hangul Syllable-CEEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cef1 Hangul Syllable-CEF1 | cef0 Hangul Syllable-CEF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cef3 Hangul Syllable-CEF3 | cef2 Hangul Syllable-CEF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cef5 Hangul Syllable-CEF5 | cef4 Hangul Syllable-CEF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cef7 Hangul Syllable-CEF7 | cef6 Hangul Syllable-CEF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cef9 Hangul Syllable-CEF9 | cef8 Hangul Syllable-CEF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cefb Hangul Syllable-CEFB | cefa Hangul Syllable-CEFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cefd Hangul Syllable-CEFD | cefc Hangul Syllable-CEFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceff Hangul Syllable-CEFF | cefe Hangul Syllable-CEFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf01 Hangul Syllable-CF01 | cf00 Hangul Syllable-CF00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf03 Hangul Syllable-CF03 | cf02 Hangul Syllable-CF02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf05 Hangul Syllable-CF05 | cf04 Hangul Syllable-CF04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf07 Hangul Syllable-CF07 | cf06 Hangul Syllable-CF06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf09 Hangul Syllable-CF09 | cf08 Hangul Syllable-CF08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf0b Hangul Syllable-CF0B | cf0a Hangul Syllable-CF0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf0d Hangul Syllable-CF0D | cf0c Hangul Syllable-CF0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf0f Hangul Syllable-CF0F | cf0e Hangul Syllable-CF0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf11 Hangul Syllable-CF11 | cf10 Hangul Syllable-CF10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf13 Hangul Syllable-CF13 | cf12 Hangul Syllable-CF12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf15 Hangul Syllable-CF15 | cf14 Hangul Syllable-CF14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf17 Hangul Syllable-CF17 | cf16 Hangul Syllable-CF16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf19 Hangul Syllable-CF19 | cf18 Hangul Syllable-CF18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf1b Hangul Syllable-CF1B | cf1a Hangul Syllable-CF1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf1d Hangul Syllable-CF1D | cf1c Hangul Syllable-CF1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf1f Hangul Syllable-CF1F | cf1e Hangul Syllable-CF1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf21 Hangul Syllable-CF21 | cf20 Hangul Syllable-CF20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf23 Hangul Syllable-CF23 | cf22 Hangul Syllable-CF22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf25 Hangul Syllable-CF25 | cf24 Hangul Syllable-CF24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf27 Hangul Syllable-CF27 | cf26 Hangul Syllable-CF26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf29 Hangul Syllable-CF29 | cf28 Hangul Syllable-CF28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf2b Hangul Syllable-CF2B | cf2a Hangul Syllable-CF2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf2d Hangul Syllable-CF2D | cf2c Hangul Syllable-CF2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf2f Hangul Syllable-CF2F | cf2e Hangul Syllable-CF2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf31 Hangul Syllable-CF31 | cf30 Hangul Syllable-CF30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf33 Hangul Syllable-CF33 | cf32 Hangul Syllable-CF32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf35 Hangul Syllable-CF35 | cf34 Hangul Syllable-CF34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf37 Hangul Syllable-CF37 | cf36 Hangul Syllable-CF36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf39 Hangul Syllable-CF39 | cf38 Hangul Syllable-CF38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf3b Hangul Syllable-CF3B | cf3a Hangul Syllable-CF3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf3d Hangul Syllable-CF3D | cf3c Hangul Syllable-CF3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf3f Hangul Syllable-CF3F | cf3e Hangul Syllable-CF3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf41 Hangul Syllable-CF41 | cf40 Hangul Syllable-CF40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf43 Hangul Syllable-CF43 | cf42 Hangul Syllable-CF42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf45 Hangul Syllable-CF45 | cf44 Hangul Syllable-CF44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf47 Hangul Syllable-CF47 | cf46 Hangul Syllable-CF46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf49 Hangul Syllable-CF49 | cf48 Hangul Syllable-CF48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf4b Hangul Syllable-CF4B | cf4a Hangul Syllable-CF4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf4d Hangul Syllable-CF4D | cf4c Hangul Syllable-CF4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf4f Hangul Syllable-CF4F | cf4e Hangul Syllable-CF4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf51 Hangul Syllable-CF51 | cf50 Hangul Syllable-CF50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf53 Hangul Syllable-CF53 | cf52 Hangul Syllable-CF52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf55 Hangul Syllable-CF55 | cf54 Hangul Syllable-CF54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf57 Hangul Syllable-CF57 | cf56 Hangul Syllable-CF56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf59 Hangul Syllable-CF59 | cf58 Hangul Syllable-CF58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf5b Hangul Syllable-CF5B | cf5a Hangul Syllable-CF5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf5d Hangul Syllable-CF5D | cf5c Hangul Syllable-CF5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf5f Hangul Syllable-CF5F | cf5e Hangul Syllable-CF5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf61 Hangul Syllable-CF61 | cf60 Hangul Syllable-CF60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf63 Hangul Syllable-CF63 | cf62 Hangul Syllable-CF62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf65 Hangul Syllable-CF65 | cf64 Hangul Syllable-CF64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf67 Hangul Syllable-CF67 | cf66 Hangul Syllable-CF66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf69 Hangul Syllable-CF69 | cf68 Hangul Syllable-CF68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf6b Hangul Syllable-CF6B | cf6a Hangul Syllable-CF6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf6d Hangul Syllable-CF6D | cf6c Hangul Syllable-CF6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf6f Hangul Syllable-CF6F | cf6e Hangul Syllable-CF6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf71 Hangul Syllable-CF71 | cf70 Hangul Syllable-CF70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf73 Hangul Syllable-CF73 | cf72 Hangul Syllable-CF72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf75 Hangul Syllable-CF75 | cf74 Hangul Syllable-CF74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf77 Hangul Syllable-CF77 | cf76 Hangul Syllable-CF76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf79 Hangul Syllable-CF79 | cf78 Hangul Syllable-CF78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf7b Hangul Syllable-CF7B | cf7a Hangul Syllable-CF7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf7d Hangul Syllable-CF7D | cf7c Hangul Syllable-CF7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf7f Hangul Syllable-CF7F | cf7e Hangul Syllable-CF7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf81 Hangul Syllable-CF81 | cf80 Hangul Syllable-CF80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf83 Hangul Syllable-CF83 | cf82 Hangul Syllable-CF82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf85 Hangul Syllable-CF85 | cf84 Hangul Syllable-CF84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf87 Hangul Syllable-CF87 | cf86 Hangul Syllable-CF86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf89 Hangul Syllable-CF89 | cf88 Hangul Syllable-CF88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf8b Hangul Syllable-CF8B | cf8a Hangul Syllable-CF8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf8d Hangul Syllable-CF8D | cf8c Hangul Syllable-CF8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf8f Hangul Syllable-CF8F | cf8e Hangul Syllable-CF8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf91 Hangul Syllable-CF91 | cf90 Hangul Syllable-CF90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf93 Hangul Syllable-CF93 | cf92 Hangul Syllable-CF92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf95 Hangul Syllable-CF95 | cf94 Hangul Syllable-CF94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf97 Hangul Syllable-CF97 | cf96 Hangul Syllable-CF96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf99 Hangul Syllable-CF99 | cf98 Hangul Syllable-CF98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf9b Hangul Syllable-CF9B | cf9a Hangul Syllable-CF9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf9d Hangul Syllable-CF9D | cf9c Hangul Syllable-CF9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf9f Hangul Syllable-CF9F | cf9e Hangul Syllable-CF9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfa1 Hangul Syllable-CFA1 | cfa0 Hangul Syllable-CFA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfa3 Hangul Syllable-CFA3 | cfa2 Hangul Syllable-CFA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfa5 Hangul Syllable-CFA5 | cfa4 Hangul Syllable-CFA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfa7 Hangul Syllable-CFA7 | cfa6 Hangul Syllable-CFA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfa9 Hangul Syllable-CFA9 | cfa8 Hangul Syllable-CFA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfab Hangul Syllable-CFAB | cfaa Hangul Syllable-CFAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfad Hangul Syllable-CFAD | cfac Hangul Syllable-CFAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfaf Hangul Syllable-CFAF | cfae Hangul Syllable-CFAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfb1 Hangul Syllable-CFB1 | cfb0 Hangul Syllable-CFB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfb3 Hangul Syllable-CFB3 | cfb2 Hangul Syllable-CFB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfb5 Hangul Syllable-CFB5 | cfb4 Hangul Syllable-CFB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfb7 Hangul Syllable-CFB7 | cfb6 Hangul Syllable-CFB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfb9 Hangul Syllable-CFB9 | cfb8 Hangul Syllable-CFB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfbb Hangul Syllable-CFBB | cfba Hangul Syllable-CFBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfbd Hangul Syllable-CFBD | cfbc Hangul Syllable-CFBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfbf Hangul Syllable-CFBF | cfbe Hangul Syllable-CFBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfc1 Hangul Syllable-CFC1 | cfc0 Hangul Syllable-CFC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfc3 Hangul Syllable-CFC3 | cfc2 Hangul Syllable-CFC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfc5 Hangul Syllable-CFC5 | cfc4 Hangul Syllable-CFC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfc7 Hangul Syllable-CFC7 | cfc6 Hangul Syllable-CFC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfc9 Hangul Syllable-CFC9 | cfc8 Hangul Syllable-CFC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfcb Hangul Syllable-CFCB | cfca Hangul Syllable-CFCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfcd Hangul Syllable-CFCD | cfcc Hangul Syllable-CFCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfcf Hangul Syllable-CFCF | cfce Hangul Syllable-CFCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfd1 Hangul Syllable-CFD1 | cfd0 Hangul Syllable-CFD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfd3 Hangul Syllable-CFD3 | cfd2 Hangul Syllable-CFD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfd5 Hangul Syllable-CFD5 | cfd4 Hangul Syllable-CFD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfd7 Hangul Syllable-CFD7 | cfd6 Hangul Syllable-CFD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfd9 Hangul Syllable-CFD9 | cfd8 Hangul Syllable-CFD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfdb Hangul Syllable-CFDB | cfda Hangul Syllable-CFDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfdd Hangul Syllable-CFDD | cfdc Hangul Syllable-CFDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfdf Hangul Syllable-CFDF | cfde Hangul Syllable-CFDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfe1 Hangul Syllable-CFE1 | cfe0 Hangul Syllable-CFE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfe3 Hangul Syllable-CFE3 | cfe2 Hangul Syllable-CFE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfe5 Hangul Syllable-CFE5 | cfe4 Hangul Syllable-CFE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfe7 Hangul Syllable-CFE7 | cfe6 Hangul Syllable-CFE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfe9 Hangul Syllable-CFE9 | cfe8 Hangul Syllable-CFE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfeb Hangul Syllable-CFEB | cfea Hangul Syllable-CFEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfed Hangul Syllable-CFED | cfec Hangul Syllable-CFEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfef Hangul Syllable-CFEF | cfee Hangul Syllable-CFEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cff1 Hangul Syllable-CFF1 | cff0 Hangul Syllable-CFF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cff3 Hangul Syllable-CFF3 | cff2 Hangul Syllable-CFF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cff5 Hangul Syllable-CFF5 | cff4 Hangul Syllable-CFF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cff7 Hangul Syllable-CFF7 | cff6 Hangul Syllable-CFF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cff9 Hangul Syllable-CFF9 | cff8 Hangul Syllable-CFF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cffb Hangul Syllable-CFFB | cffa Hangul Syllable-CFFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cffd Hangul Syllable-CFFD | cffc Hangul Syllable-CFFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfff Hangul Syllable-CFFF | cffe Hangul Syllable-CFFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d001 Hangul Syllable-D001 | d000 Hangul Syllable-D000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d003 Hangul Syllable-D003 | d002 Hangul Syllable-D002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d005 Hangul Syllable-D005 | d004 Hangul Syllable-D004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d007 Hangul Syllable-D007 | d006 Hangul Syllable-D006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d009 Hangul Syllable-D009 | d008 Hangul Syllable-D008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d00b Hangul Syllable-D00B | d00a Hangul Syllable-D00A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d00d Hangul Syllable-D00D | d00c Hangul Syllable-D00C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d00f Hangul Syllable-D00F | d00e Hangul Syllable-D00E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d011 Hangul Syllable-D011 | d010 Hangul Syllable-D010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d013 Hangul Syllable-D013 | d012 Hangul Syllable-D012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d015 Hangul Syllable-D015 | d014 Hangul Syllable-D014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d017 Hangul Syllable-D017 | d016 Hangul Syllable-D016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d019 Hangul Syllable-D019 | d018 Hangul Syllable-D018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d01b Hangul Syllable-D01B | d01a Hangul Syllable-D01A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d01d Hangul Syllable-D01D | d01c Hangul Syllable-D01C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d01f Hangul Syllable-D01F | d01e Hangul Syllable-D01E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d021 Hangul Syllable-D021 | d020 Hangul Syllable-D020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d023 Hangul Syllable-D023 | d022 Hangul Syllable-D022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d025 Hangul Syllable-D025 | d024 Hangul Syllable-D024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d027 Hangul Syllable-D027 | d026 Hangul Syllable-D026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d029 Hangul Syllable-D029 | d028 Hangul Syllable-D028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d02b Hangul Syllable-D02B | d02a Hangul Syllable-D02A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d02d Hangul Syllable-D02D | d02c Hangul Syllable-D02C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d02f Hangul Syllable-D02F | d02e Hangul Syllable-D02E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d031 Hangul Syllable-D031 | d030 Hangul Syllable-D030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d033 Hangul Syllable-D033 | d032 Hangul Syllable-D032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d035 Hangul Syllable-D035 | d034 Hangul Syllable-D034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d037 Hangul Syllable-D037 | d036 Hangul Syllable-D036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d039 Hangul Syllable-D039 | d038 Hangul Syllable-D038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d03b Hangul Syllable-D03B | d03a Hangul Syllable-D03A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d03d Hangul Syllable-D03D | d03c Hangul Syllable-D03C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d03f Hangul Syllable-D03F | d03e Hangul Syllable-D03E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d041 Hangul Syllable-D041 | d040 Hangul Syllable-D040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d043 Hangul Syllable-D043 | d042 Hangul Syllable-D042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d045 Hangul Syllable-D045 | d044 Hangul Syllable-D044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d047 Hangul Syllable-D047 | d046 Hangul Syllable-D046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d049 Hangul Syllable-D049 | d048 Hangul Syllable-D048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d04b Hangul Syllable-D04B | d04a Hangul Syllable-D04A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d04d Hangul Syllable-D04D | d04c Hangul Syllable-D04C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d04f Hangul Syllable-D04F | d04e Hangul Syllable-D04E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d051 Hangul Syllable-D051 | d050 Hangul Syllable-D050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d053 Hangul Syllable-D053 | d052 Hangul Syllable-D052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d055 Hangul Syllable-D055 | d054 Hangul Syllable-D054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d057 Hangul Syllable-D057 | d056 Hangul Syllable-D056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d059 Hangul Syllable-D059 | d058 Hangul Syllable-D058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d05b Hangul Syllable-D05B | d05a Hangul Syllable-D05A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d05d Hangul Syllable-D05D | d05c Hangul Syllable-D05C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d05f Hangul Syllable-D05F | d05e Hangul Syllable-D05E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d061 Hangul Syllable-D061 | d060 Hangul Syllable-D060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d063 Hangul Syllable-D063 | d062 Hangul Syllable-D062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d065 Hangul Syllable-D065 | d064 Hangul Syllable-D064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d067 Hangul Syllable-D067 | d066 Hangul Syllable-D066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d069 Hangul Syllable-D069 | d068 Hangul Syllable-D068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d06b Hangul Syllable-D06B | d06a Hangul Syllable-D06A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d06d Hangul Syllable-D06D | d06c Hangul Syllable-D06C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d06f Hangul Syllable-D06F | d06e Hangul Syllable-D06E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d071 Hangul Syllable-D071 | d070 Hangul Syllable-D070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d073 Hangul Syllable-D073 | d072 Hangul Syllable-D072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d075 Hangul Syllable-D075 | d074 Hangul Syllable-D074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d077 Hangul Syllable-D077 | d076 Hangul Syllable-D076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d079 Hangul Syllable-D079 | d078 Hangul Syllable-D078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d07b Hangul Syllable-D07B | d07a Hangul Syllable-D07A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d07d Hangul Syllable-D07D | d07c Hangul Syllable-D07C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d07f Hangul Syllable-D07F | d07e Hangul Syllable-D07E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d081 Hangul Syllable-D081 | d080 Hangul Syllable-D080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d083 Hangul Syllable-D083 | d082 Hangul Syllable-D082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d085 Hangul Syllable-D085 | d084 Hangul Syllable-D084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d087 Hangul Syllable-D087 | d086 Hangul Syllable-D086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d089 Hangul Syllable-D089 | d088 Hangul Syllable-D088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d08b Hangul Syllable-D08B | d08a Hangul Syllable-D08A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d08d Hangul Syllable-D08D | d08c Hangul Syllable-D08C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d08f Hangul Syllable-D08F | d08e Hangul Syllable-D08E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d091 Hangul Syllable-D091 | d090 Hangul Syllable-D090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d093 Hangul Syllable-D093 | d092 Hangul Syllable-D092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d095 Hangul Syllable-D095 | d094 Hangul Syllable-D094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d097 Hangul Syllable-D097 | d096 Hangul Syllable-D096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d099 Hangul Syllable-D099 | d098 Hangul Syllable-D098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d09b Hangul Syllable-D09B | d09a Hangul Syllable-D09A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d09d Hangul Syllable-D09D | d09c Hangul Syllable-D09C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d09f Hangul Syllable-D09F | d09e Hangul Syllable-D09E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0a1 Hangul Syllable-D0A1 | d0a0 Hangul Syllable-D0A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0a3 Hangul Syllable-D0A3 | d0a2 Hangul Syllable-D0A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0a5 Hangul Syllable-D0A5 | d0a4 Hangul Syllable-D0A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0a7 Hangul Syllable-D0A7 | d0a6 Hangul Syllable-D0A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0a9 Hangul Syllable-D0A9 | d0a8 Hangul Syllable-D0A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0ab Hangul Syllable-D0AB | d0aa Hangul Syllable-D0AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0ad Hangul Syllable-D0AD | d0ac Hangul Syllable-D0AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0af Hangul Syllable-D0AF | d0ae Hangul Syllable-D0AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0b1 Hangul Syllable-D0B1 | d0b0 Hangul Syllable-D0B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0b3 Hangul Syllable-D0B3 | d0b2 Hangul Syllable-D0B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0b5 Hangul Syllable-D0B5 | d0b4 Hangul Syllable-D0B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0b7 Hangul Syllable-D0B7 | d0b6 Hangul Syllable-D0B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0b9 Hangul Syllable-D0B9 | d0b8 Hangul Syllable-D0B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0bb Hangul Syllable-D0BB | d0ba Hangul Syllable-D0BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0bd Hangul Syllable-D0BD | d0bc Hangul Syllable-D0BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0bf Hangul Syllable-D0BF | d0be Hangul Syllable-D0BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0c1 Hangul Syllable-D0C1 | d0c0 Hangul Syllable-D0C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0c3 Hangul Syllable-D0C3 | d0c2 Hangul Syllable-D0C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0c5 Hangul Syllable-D0C5 | d0c4 Hangul Syllable-D0C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0c7 Hangul Syllable-D0C7 | d0c6 Hangul Syllable-D0C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0c9 Hangul Syllable-D0C9 | d0c8 Hangul Syllable-D0C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0cb Hangul Syllable-D0CB | d0ca Hangul Syllable-D0CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0cd Hangul Syllable-D0CD | d0cc Hangul Syllable-D0CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0cf Hangul Syllable-D0CF | d0ce Hangul Syllable-D0CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0d1 Hangul Syllable-D0D1 | d0d0 Hangul Syllable-D0D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0d3 Hangul Syllable-D0D3 | d0d2 Hangul Syllable-D0D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0d5 Hangul Syllable-D0D5 | d0d4 Hangul Syllable-D0D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0d7 Hangul Syllable-D0D7 | d0d6 Hangul Syllable-D0D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0d9 Hangul Syllable-D0D9 | d0d8 Hangul Syllable-D0D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0db Hangul Syllable-D0DB | d0da Hangul Syllable-D0DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0dd Hangul Syllable-D0DD | d0dc Hangul Syllable-D0DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0df Hangul Syllable-D0DF | d0de Hangul Syllable-D0DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0e1 Hangul Syllable-D0E1 | d0e0 Hangul Syllable-D0E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0e3 Hangul Syllable-D0E3 | d0e2 Hangul Syllable-D0E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0e5 Hangul Syllable-D0E5 | d0e4 Hangul Syllable-D0E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0e7 Hangul Syllable-D0E7 | d0e6 Hangul Syllable-D0E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0e9 Hangul Syllable-D0E9 | d0e8 Hangul Syllable-D0E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0eb Hangul Syllable-D0EB | d0ea Hangul Syllable-D0EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0ed Hangul Syllable-D0ED | d0ec Hangul Syllable-D0EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0ef Hangul Syllable-D0EF | d0ee Hangul Syllable-D0EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0f1 Hangul Syllable-D0F1 | d0f0 Hangul Syllable-D0F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0f3 Hangul Syllable-D0F3 | d0f2 Hangul Syllable-D0F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0f5 Hangul Syllable-D0F5 | d0f4 Hangul Syllable-D0F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0f7 Hangul Syllable-D0F7 | d0f6 Hangul Syllable-D0F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0f9 Hangul Syllable-D0F9 | d0f8 Hangul Syllable-D0F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0fb Hangul Syllable-D0FB | d0fa Hangul Syllable-D0FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0fd Hangul Syllable-D0FD | d0fc Hangul Syllable-D0FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0ff Hangul Syllable-D0FF | d0fe Hangul Syllable-D0FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d101 Hangul Syllable-D101 | d100 Hangul Syllable-D100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d103 Hangul Syllable-D103 | d102 Hangul Syllable-D102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d105 Hangul Syllable-D105 | d104 Hangul Syllable-D104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d107 Hangul Syllable-D107 | d106 Hangul Syllable-D106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d109 Hangul Syllable-D109 | d108 Hangul Syllable-D108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d10b Hangul Syllable-D10B | d10a Hangul Syllable-D10A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d10d Hangul Syllable-D10D | d10c Hangul Syllable-D10C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d10f Hangul Syllable-D10F | d10e Hangul Syllable-D10E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d111 Hangul Syllable-D111 | d110 Hangul Syllable-D110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d113 Hangul Syllable-D113 | d112 Hangul Syllable-D112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d115 Hangul Syllable-D115 | d114 Hangul Syllable-D114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d117 Hangul Syllable-D117 | d116 Hangul Syllable-D116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d119 Hangul Syllable-D119 | d118 Hangul Syllable-D118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d11b Hangul Syllable-D11B | d11a Hangul Syllable-D11A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d11d Hangul Syllable-D11D | d11c Hangul Syllable-D11C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d11f Hangul Syllable-D11F | d11e Hangul Syllable-D11E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d121 Hangul Syllable-D121 | d120 Hangul Syllable-D120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d123 Hangul Syllable-D123 | d122 Hangul Syllable-D122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d125 Hangul Syllable-D125 | d124 Hangul Syllable-D124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d127 Hangul Syllable-D127 | d126 Hangul Syllable-D126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d129 Hangul Syllable-D129 | d128 Hangul Syllable-D128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d12b Hangul Syllable-D12B | d12a Hangul Syllable-D12A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d12d Hangul Syllable-D12D | d12c Hangul Syllable-D12C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d12f Hangul Syllable-D12F | d12e Hangul Syllable-D12E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d131 Hangul Syllable-D131 | d130 Hangul Syllable-D130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d133 Hangul Syllable-D133 | d132 Hangul Syllable-D132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d135 Hangul Syllable-D135 | d134 Hangul Syllable-D134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d137 Hangul Syllable-D137 | d136 Hangul Syllable-D136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d139 Hangul Syllable-D139 | d138 Hangul Syllable-D138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d13b Hangul Syllable-D13B | d13a Hangul Syllable-D13A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d13d Hangul Syllable-D13D | d13c Hangul Syllable-D13C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d13f Hangul Syllable-D13F | d13e Hangul Syllable-D13E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d141 Hangul Syllable-D141 | d140 Hangul Syllable-D140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d143 Hangul Syllable-D143 | d142 Hangul Syllable-D142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d145 Hangul Syllable-D145 | d144 Hangul Syllable-D144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d147 Hangul Syllable-D147 | d146 Hangul Syllable-D146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d149 Hangul Syllable-D149 | d148 Hangul Syllable-D148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d14b Hangul Syllable-D14B | d14a Hangul Syllable-D14A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d14d Hangul Syllable-D14D | d14c Hangul Syllable-D14C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d14f Hangul Syllable-D14F | d14e Hangul Syllable-D14E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d151 Hangul Syllable-D151 | d150 Hangul Syllable-D150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d153 Hangul Syllable-D153 | d152 Hangul Syllable-D152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d155 Hangul Syllable-D155 | d154 Hangul Syllable-D154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d157 Hangul Syllable-D157 | d156 Hangul Syllable-D156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d159 Hangul Syllable-D159 | d158 Hangul Syllable-D158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d15b Hangul Syllable-D15B | d15a Hangul Syllable-D15A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d15d Hangul Syllable-D15D | d15c Hangul Syllable-D15C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d15f Hangul Syllable-D15F | d15e Hangul Syllable-D15E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d161 Hangul Syllable-D161 | d160 Hangul Syllable-D160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d163 Hangul Syllable-D163 | d162 Hangul Syllable-D162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d165 Hangul Syllable-D165 | d164 Hangul Syllable-D164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d167 Hangul Syllable-D167 | d166 Hangul Syllable-D166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d169 Hangul Syllable-D169 | d168 Hangul Syllable-D168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d16b Hangul Syllable-D16B | d16a Hangul Syllable-D16A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d16d Hangul Syllable-D16D | d16c Hangul Syllable-D16C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d16f Hangul Syllable-D16F | d16e Hangul Syllable-D16E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d171 Hangul Syllable-D171 | d170 Hangul Syllable-D170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d173 Hangul Syllable-D173 | d172 Hangul Syllable-D172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d175 Hangul Syllable-D175 | d174 Hangul Syllable-D174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d177 Hangul Syllable-D177 | d176 Hangul Syllable-D176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d179 Hangul Syllable-D179 | d178 Hangul Syllable-D178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d17b Hangul Syllable-D17B | d17a Hangul Syllable-D17A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d17d Hangul Syllable-D17D | d17c Hangul Syllable-D17C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d17f Hangul Syllable-D17F | d17e Hangul Syllable-D17E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d181 Hangul Syllable-D181 | d180 Hangul Syllable-D180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d183 Hangul Syllable-D183 | d182 Hangul Syllable-D182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d185 Hangul Syllable-D185 | d184 Hangul Syllable-D184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d187 Hangul Syllable-D187 | d186 Hangul Syllable-D186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d189 Hangul Syllable-D189 | d188 Hangul Syllable-D188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d18b Hangul Syllable-D18B | d18a Hangul Syllable-D18A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d18d Hangul Syllable-D18D | d18c Hangul Syllable-D18C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d18f Hangul Syllable-D18F | d18e Hangul Syllable-D18E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d191 Hangul Syllable-D191 | d190 Hangul Syllable-D190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d193 Hangul Syllable-D193 | d192 Hangul Syllable-D192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d195 Hangul Syllable-D195 | d194 Hangul Syllable-D194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d197 Hangul Syllable-D197 | d196 Hangul Syllable-D196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d199 Hangul Syllable-D199 | d198 Hangul Syllable-D198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d19b Hangul Syllable-D19B | d19a Hangul Syllable-D19A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d19d Hangul Syllable-D19D | d19c Hangul Syllable-D19C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d19f Hangul Syllable-D19F | d19e Hangul Syllable-D19E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1a1 Hangul Syllable-D1A1 | d1a0 Hangul Syllable-D1A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1a3 Hangul Syllable-D1A3 | d1a2 Hangul Syllable-D1A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1a5 Hangul Syllable-D1A5 | d1a4 Hangul Syllable-D1A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1a7 Hangul Syllable-D1A7 | d1a6 Hangul Syllable-D1A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1a9 Hangul Syllable-D1A9 | d1a8 Hangul Syllable-D1A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1ab Hangul Syllable-D1AB | d1aa Hangul Syllable-D1AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1ad Hangul Syllable-D1AD | d1ac Hangul Syllable-D1AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1af Hangul Syllable-D1AF | d1ae Hangul Syllable-D1AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1b1 Hangul Syllable-D1B1 | d1b0 Hangul Syllable-D1B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1b3 Hangul Syllable-D1B3 | d1b2 Hangul Syllable-D1B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1b5 Hangul Syllable-D1B5 | d1b4 Hangul Syllable-D1B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1b7 Hangul Syllable-D1B7 | d1b6 Hangul Syllable-D1B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1b9 Hangul Syllable-D1B9 | d1b8 Hangul Syllable-D1B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1bb Hangul Syllable-D1BB | d1ba Hangul Syllable-D1BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1bd Hangul Syllable-D1BD | d1bc Hangul Syllable-D1BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1bf Hangul Syllable-D1BF | d1be Hangul Syllable-D1BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1c1 Hangul Syllable-D1C1 | d1c0 Hangul Syllable-D1C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1c3 Hangul Syllable-D1C3 | d1c2 Hangul Syllable-D1C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1c5 Hangul Syllable-D1C5 | d1c4 Hangul Syllable-D1C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1c7 Hangul Syllable-D1C7 | d1c6 Hangul Syllable-D1C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1c9 Hangul Syllable-D1C9 | d1c8 Hangul Syllable-D1C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1cb Hangul Syllable-D1CB | d1ca Hangul Syllable-D1CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1cd Hangul Syllable-D1CD | d1cc Hangul Syllable-D1CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1cf Hangul Syllable-D1CF | d1ce Hangul Syllable-D1CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1d1 Hangul Syllable-D1D1 | d1d0 Hangul Syllable-D1D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1d3 Hangul Syllable-D1D3 | d1d2 Hangul Syllable-D1D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1d5 Hangul Syllable-D1D5 | d1d4 Hangul Syllable-D1D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1d7 Hangul Syllable-D1D7 | d1d6 Hangul Syllable-D1D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1d9 Hangul Syllable-D1D9 | d1d8 Hangul Syllable-D1D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1db Hangul Syllable-D1DB | d1da Hangul Syllable-D1DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1dd Hangul Syllable-D1DD | d1dc Hangul Syllable-D1DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1df Hangul Syllable-D1DF | d1de Hangul Syllable-D1DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1e1 Hangul Syllable-D1E1 | d1e0 Hangul Syllable-D1E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1e3 Hangul Syllable-D1E3 | d1e2 Hangul Syllable-D1E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1e5 Hangul Syllable-D1E5 | d1e4 Hangul Syllable-D1E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1e7 Hangul Syllable-D1E7 | d1e6 Hangul Syllable-D1E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1e9 Hangul Syllable-D1E9 | d1e8 Hangul Syllable-D1E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1eb Hangul Syllable-D1EB | d1ea Hangul Syllable-D1EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1ed Hangul Syllable-D1ED | d1ec Hangul Syllable-D1EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1ef Hangul Syllable-D1EF | d1ee Hangul Syllable-D1EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1f1 Hangul Syllable-D1F1 | d1f0 Hangul Syllable-D1F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1f3 Hangul Syllable-D1F3 | d1f2 Hangul Syllable-D1F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1f5 Hangul Syllable-D1F5 | d1f4 Hangul Syllable-D1F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1f7 Hangul Syllable-D1F7 | d1f6 Hangul Syllable-D1F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1f9 Hangul Syllable-D1F9 | d1f8 Hangul Syllable-D1F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1fb Hangul Syllable-D1FB | d1fa Hangul Syllable-D1FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1fd Hangul Syllable-D1FD | d1fc Hangul Syllable-D1FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1ff Hangul Syllable-D1FF | d1fe Hangul Syllable-D1FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d201 Hangul Syllable-D201 | d200 Hangul Syllable-D200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d203 Hangul Syllable-D203 | d202 Hangul Syllable-D202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d205 Hangul Syllable-D205 | d204 Hangul Syllable-D204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d207 Hangul Syllable-D207 | d206 Hangul Syllable-D206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d209 Hangul Syllable-D209 | d208 Hangul Syllable-D208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d20b Hangul Syllable-D20B | d20a Hangul Syllable-D20A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d20d Hangul Syllable-D20D | d20c Hangul Syllable-D20C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d20f Hangul Syllable-D20F | d20e Hangul Syllable-D20E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d211 Hangul Syllable-D211 | d210 Hangul Syllable-D210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d213 Hangul Syllable-D213 | d212 Hangul Syllable-D212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d215 Hangul Syllable-D215 | d214 Hangul Syllable-D214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d217 Hangul Syllable-D217 | d216 Hangul Syllable-D216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d219 Hangul Syllable-D219 | d218 Hangul Syllable-D218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d21b Hangul Syllable-D21B | d21a Hangul Syllable-D21A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d21d Hangul Syllable-D21D | d21c Hangul Syllable-D21C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d21f Hangul Syllable-D21F | d21e Hangul Syllable-D21E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d221 Hangul Syllable-D221 | d220 Hangul Syllable-D220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d223 Hangul Syllable-D223 | d222 Hangul Syllable-D222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d225 Hangul Syllable-D225 | d224 Hangul Syllable-D224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d227 Hangul Syllable-D227 | d226 Hangul Syllable-D226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d229 Hangul Syllable-D229 | d228 Hangul Syllable-D228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d22b Hangul Syllable-D22B | d22a Hangul Syllable-D22A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d22d Hangul Syllable-D22D | d22c Hangul Syllable-D22C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d22f Hangul Syllable-D22F | d22e Hangul Syllable-D22E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d231 Hangul Syllable-D231 | d230 Hangul Syllable-D230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d233 Hangul Syllable-D233 | d232 Hangul Syllable-D232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d235 Hangul Syllable-D235 | d234 Hangul Syllable-D234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d237 Hangul Syllable-D237 | d236 Hangul Syllable-D236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d239 Hangul Syllable-D239 | d238 Hangul Syllable-D238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d23b Hangul Syllable-D23B | d23a Hangul Syllable-D23A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d23d Hangul Syllable-D23D | d23c Hangul Syllable-D23C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d23f Hangul Syllable-D23F | d23e Hangul Syllable-D23E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d241 Hangul Syllable-D241 | d240 Hangul Syllable-D240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d243 Hangul Syllable-D243 | d242 Hangul Syllable-D242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d245 Hangul Syllable-D245 | d244 Hangul Syllable-D244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d247 Hangul Syllable-D247 | d246 Hangul Syllable-D246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d249 Hangul Syllable-D249 | d248 Hangul Syllable-D248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d24b Hangul Syllable-D24B | d24a Hangul Syllable-D24A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d24d Hangul Syllable-D24D | d24c Hangul Syllable-D24C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d24f Hangul Syllable-D24F | d24e Hangul Syllable-D24E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d251 Hangul Syllable-D251 | d250 Hangul Syllable-D250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d253 Hangul Syllable-D253 | d252 Hangul Syllable-D252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d255 Hangul Syllable-D255 | d254 Hangul Syllable-D254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d257 Hangul Syllable-D257 | d256 Hangul Syllable-D256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d259 Hangul Syllable-D259 | d258 Hangul Syllable-D258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d25b Hangul Syllable-D25B | d25a Hangul Syllable-D25A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d25d Hangul Syllable-D25D | d25c Hangul Syllable-D25C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d25f Hangul Syllable-D25F | d25e Hangul Syllable-D25E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d261 Hangul Syllable-D261 | d260 Hangul Syllable-D260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d263 Hangul Syllable-D263 | d262 Hangul Syllable-D262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d265 Hangul Syllable-D265 | d264 Hangul Syllable-D264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d267 Hangul Syllable-D267 | d266 Hangul Syllable-D266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d269 Hangul Syllable-D269 | d268 Hangul Syllable-D268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d26b Hangul Syllable-D26B | d26a Hangul Syllable-D26A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d26d Hangul Syllable-D26D | d26c Hangul Syllable-D26C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d26f Hangul Syllable-D26F | d26e Hangul Syllable-D26E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d271 Hangul Syllable-D271 | d270 Hangul Syllable-D270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d273 Hangul Syllable-D273 | d272 Hangul Syllable-D272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d275 Hangul Syllable-D275 | d274 Hangul Syllable-D274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d277 Hangul Syllable-D277 | d276 Hangul Syllable-D276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d279 Hangul Syllable-D279 | d278 Hangul Syllable-D278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d27b Hangul Syllable-D27B | d27a Hangul Syllable-D27A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d27d Hangul Syllable-D27D | d27c Hangul Syllable-D27C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d27f Hangul Syllable-D27F | d27e Hangul Syllable-D27E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d281 Hangul Syllable-D281 | d280 Hangul Syllable-D280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d283 Hangul Syllable-D283 | d282 Hangul Syllable-D282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d285 Hangul Syllable-D285 | d284 Hangul Syllable-D284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d287 Hangul Syllable-D287 | d286 Hangul Syllable-D286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d289 Hangul Syllable-D289 | d288 Hangul Syllable-D288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d28b Hangul Syllable-D28B | d28a Hangul Syllable-D28A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d28d Hangul Syllable-D28D | d28c Hangul Syllable-D28C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d28f Hangul Syllable-D28F | d28e Hangul Syllable-D28E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d291 Hangul Syllable-D291 | d290 Hangul Syllable-D290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d293 Hangul Syllable-D293 | d292 Hangul Syllable-D292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d295 Hangul Syllable-D295 | d294 Hangul Syllable-D294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d297 Hangul Syllable-D297 | d296 Hangul Syllable-D296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d299 Hangul Syllable-D299 | d298 Hangul Syllable-D298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d29b Hangul Syllable-D29B | d29a Hangul Syllable-D29A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d29d Hangul Syllable-D29D | d29c Hangul Syllable-D29C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d29f Hangul Syllable-D29F | d29e Hangul Syllable-D29E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2a1 Hangul Syllable-D2A1 | d2a0 Hangul Syllable-D2A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2a3 Hangul Syllable-D2A3 | d2a2 Hangul Syllable-D2A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2a5 Hangul Syllable-D2A5 | d2a4 Hangul Syllable-D2A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2a7 Hangul Syllable-D2A7 | d2a6 Hangul Syllable-D2A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2a9 Hangul Syllable-D2A9 | d2a8 Hangul Syllable-D2A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2ab Hangul Syllable-D2AB | d2aa Hangul Syllable-D2AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2ad Hangul Syllable-D2AD | d2ac Hangul Syllable-D2AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2af Hangul Syllable-D2AF | d2ae Hangul Syllable-D2AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2b1 Hangul Syllable-D2B1 | d2b0 Hangul Syllable-D2B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2b3 Hangul Syllable-D2B3 | d2b2 Hangul Syllable-D2B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2b5 Hangul Syllable-D2B5 | d2b4 Hangul Syllable-D2B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2b7 Hangul Syllable-D2B7 | d2b6 Hangul Syllable-D2B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2b9 Hangul Syllable-D2B9 | d2b8 Hangul Syllable-D2B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2bb Hangul Syllable-D2BB | d2ba Hangul Syllable-D2BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2bd Hangul Syllable-D2BD | d2bc Hangul Syllable-D2BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2bf Hangul Syllable-D2BF | d2be Hangul Syllable-D2BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2c1 Hangul Syllable-D2C1 | d2c0 Hangul Syllable-D2C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2c3 Hangul Syllable-D2C3 | d2c2 Hangul Syllable-D2C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2c5 Hangul Syllable-D2C5 | d2c4 Hangul Syllable-D2C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2c7 Hangul Syllable-D2C7 | d2c6 Hangul Syllable-D2C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2c9 Hangul Syllable-D2C9 | d2c8 Hangul Syllable-D2C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2cb Hangul Syllable-D2CB | d2ca Hangul Syllable-D2CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2cd Hangul Syllable-D2CD | d2cc Hangul Syllable-D2CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2cf Hangul Syllable-D2CF | d2ce Hangul Syllable-D2CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2d1 Hangul Syllable-D2D1 | d2d0 Hangul Syllable-D2D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2d3 Hangul Syllable-D2D3 | d2d2 Hangul Syllable-D2D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2d5 Hangul Syllable-D2D5 | d2d4 Hangul Syllable-D2D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2d7 Hangul Syllable-D2D7 | d2d6 Hangul Syllable-D2D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2d9 Hangul Syllable-D2D9 | d2d8 Hangul Syllable-D2D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2db Hangul Syllable-D2DB | d2da Hangul Syllable-D2DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2dd Hangul Syllable-D2DD | d2dc Hangul Syllable-D2DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2df Hangul Syllable-D2DF | d2de Hangul Syllable-D2DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2e1 Hangul Syllable-D2E1 | d2e0 Hangul Syllable-D2E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2e3 Hangul Syllable-D2E3 | d2e2 Hangul Syllable-D2E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2e5 Hangul Syllable-D2E5 | d2e4 Hangul Syllable-D2E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2e7 Hangul Syllable-D2E7 | d2e6 Hangul Syllable-D2E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2e9 Hangul Syllable-D2E9 | d2e8 Hangul Syllable-D2E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2eb Hangul Syllable-D2EB | d2ea Hangul Syllable-D2EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2ed Hangul Syllable-D2ED | d2ec Hangul Syllable-D2EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2ef Hangul Syllable-D2EF | d2ee Hangul Syllable-D2EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2f1 Hangul Syllable-D2F1 | d2f0 Hangul Syllable-D2F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2f3 Hangul Syllable-D2F3 | d2f2 Hangul Syllable-D2F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2f5 Hangul Syllable-D2F5 | d2f4 Hangul Syllable-D2F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2f7 Hangul Syllable-D2F7 | d2f6 Hangul Syllable-D2F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2f9 Hangul Syllable-D2F9 | d2f8 Hangul Syllable-D2F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2fb Hangul Syllable-D2FB | d2fa Hangul Syllable-D2FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2fd Hangul Syllable-D2FD | d2fc Hangul Syllable-D2FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2ff Hangul Syllable-D2FF | d2fe Hangul Syllable-D2FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d301 Hangul Syllable-D301 | d300 Hangul Syllable-D300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d303 Hangul Syllable-D303 | d302 Hangul Syllable-D302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d305 Hangul Syllable-D305 | d304 Hangul Syllable-D304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d307 Hangul Syllable-D307 | d306 Hangul Syllable-D306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d309 Hangul Syllable-D309 | d308 Hangul Syllable-D308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d30b Hangul Syllable-D30B | d30a Hangul Syllable-D30A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d30d Hangul Syllable-D30D | d30c Hangul Syllable-D30C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d30f Hangul Syllable-D30F | d30e Hangul Syllable-D30E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d311 Hangul Syllable-D311 | d310 Hangul Syllable-D310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d313 Hangul Syllable-D313 | d312 Hangul Syllable-D312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d315 Hangul Syllable-D315 | d314 Hangul Syllable-D314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d317 Hangul Syllable-D317 | d316 Hangul Syllable-D316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d319 Hangul Syllable-D319 | d318 Hangul Syllable-D318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d31b Hangul Syllable-D31B | d31a Hangul Syllable-D31A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d31d Hangul Syllable-D31D | d31c Hangul Syllable-D31C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d31f Hangul Syllable-D31F | d31e Hangul Syllable-D31E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d321 Hangul Syllable-D321 | d320 Hangul Syllable-D320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d323 Hangul Syllable-D323 | d322 Hangul Syllable-D322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d325 Hangul Syllable-D325 | d324 Hangul Syllable-D324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d327 Hangul Syllable-D327 | d326 Hangul Syllable-D326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d329 Hangul Syllable-D329 | d328 Hangul Syllable-D328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d32b Hangul Syllable-D32B | d32a Hangul Syllable-D32A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d32d Hangul Syllable-D32D | d32c Hangul Syllable-D32C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d32f Hangul Syllable-D32F | d32e Hangul Syllable-D32E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d331 Hangul Syllable-D331 | d330 Hangul Syllable-D330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d333 Hangul Syllable-D333 | d332 Hangul Syllable-D332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d335 Hangul Syllable-D335 | d334 Hangul Syllable-D334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d337 Hangul Syllable-D337 | d336 Hangul Syllable-D336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d339 Hangul Syllable-D339 | d338 Hangul Syllable-D338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d33b Hangul Syllable-D33B | d33a Hangul Syllable-D33A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d33d Hangul Syllable-D33D | d33c Hangul Syllable-D33C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d33f Hangul Syllable-D33F | d33e Hangul Syllable-D33E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d341 Hangul Syllable-D341 | d340 Hangul Syllable-D340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d343 Hangul Syllable-D343 | d342 Hangul Syllable-D342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d345 Hangul Syllable-D345 | d344 Hangul Syllable-D344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d347 Hangul Syllable-D347 | d346 Hangul Syllable-D346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d349 Hangul Syllable-D349 | d348 Hangul Syllable-D348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d34b Hangul Syllable-D34B | d34a Hangul Syllable-D34A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d34d Hangul Syllable-D34D | d34c Hangul Syllable-D34C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d34f Hangul Syllable-D34F | d34e Hangul Syllable-D34E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d351 Hangul Syllable-D351 | d350 Hangul Syllable-D350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d353 Hangul Syllable-D353 | d352 Hangul Syllable-D352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d355 Hangul Syllable-D355 | d354 Hangul Syllable-D354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d357 Hangul Syllable-D357 | d356 Hangul Syllable-D356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d359 Hangul Syllable-D359 | d358 Hangul Syllable-D358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d35b Hangul Syllable-D35B | d35a Hangul Syllable-D35A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d35d Hangul Syllable-D35D | d35c Hangul Syllable-D35C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d35f Hangul Syllable-D35F | d35e Hangul Syllable-D35E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d361 Hangul Syllable-D361 | d360 Hangul Syllable-D360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d363 Hangul Syllable-D363 | d362 Hangul Syllable-D362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d365 Hangul Syllable-D365 | d364 Hangul Syllable-D364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d367 Hangul Syllable-D367 | d366 Hangul Syllable-D366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d369 Hangul Syllable-D369 | d368 Hangul Syllable-D368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d36b Hangul Syllable-D36B | d36a Hangul Syllable-D36A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d36d Hangul Syllable-D36D | d36c Hangul Syllable-D36C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d36f Hangul Syllable-D36F | d36e Hangul Syllable-D36E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d371 Hangul Syllable-D371 | d370 Hangul Syllable-D370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d373 Hangul Syllable-D373 | d372 Hangul Syllable-D372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d375 Hangul Syllable-D375 | d374 Hangul Syllable-D374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d377 Hangul Syllable-D377 | d376 Hangul Syllable-D376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d379 Hangul Syllable-D379 | d378 Hangul Syllable-D378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d37b Hangul Syllable-D37B | d37a Hangul Syllable-D37A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d37d Hangul Syllable-D37D | d37c Hangul Syllable-D37C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d37f Hangul Syllable-D37F | d37e Hangul Syllable-D37E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d381 Hangul Syllable-D381 | d380 Hangul Syllable-D380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d383 Hangul Syllable-D383 | d382 Hangul Syllable-D382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d385 Hangul Syllable-D385 | d384 Hangul Syllable-D384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d387 Hangul Syllable-D387 | d386 Hangul Syllable-D386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d389 Hangul Syllable-D389 | d388 Hangul Syllable-D388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d38b Hangul Syllable-D38B | d38a Hangul Syllable-D38A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d38d Hangul Syllable-D38D | d38c Hangul Syllable-D38C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d38f Hangul Syllable-D38F | d38e Hangul Syllable-D38E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d391 Hangul Syllable-D391 | d390 Hangul Syllable-D390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d393 Hangul Syllable-D393 | d392 Hangul Syllable-D392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d395 Hangul Syllable-D395 | d394 Hangul Syllable-D394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d397 Hangul Syllable-D397 | d396 Hangul Syllable-D396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d399 Hangul Syllable-D399 | d398 Hangul Syllable-D398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d39b Hangul Syllable-D39B | d39a Hangul Syllable-D39A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d39d Hangul Syllable-D39D | d39c Hangul Syllable-D39C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d39f Hangul Syllable-D39F | d39e Hangul Syllable-D39E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3a1 Hangul Syllable-D3A1 | d3a0 Hangul Syllable-D3A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3a3 Hangul Syllable-D3A3 | d3a2 Hangul Syllable-D3A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3a5 Hangul Syllable-D3A5 | d3a4 Hangul Syllable-D3A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3a7 Hangul Syllable-D3A7 | d3a6 Hangul Syllable-D3A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3a9 Hangul Syllable-D3A9 | d3a8 Hangul Syllable-D3A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3ab Hangul Syllable-D3AB | d3aa Hangul Syllable-D3AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3ad Hangul Syllable-D3AD | d3ac Hangul Syllable-D3AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3af Hangul Syllable-D3AF | d3ae Hangul Syllable-D3AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3b1 Hangul Syllable-D3B1 | d3b0 Hangul Syllable-D3B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3b3 Hangul Syllable-D3B3 | d3b2 Hangul Syllable-D3B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3b5 Hangul Syllable-D3B5 | d3b4 Hangul Syllable-D3B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3b7 Hangul Syllable-D3B7 | d3b6 Hangul Syllable-D3B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3b9 Hangul Syllable-D3B9 | d3b8 Hangul Syllable-D3B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3bb Hangul Syllable-D3BB | d3ba Hangul Syllable-D3BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3bd Hangul Syllable-D3BD | d3bc Hangul Syllable-D3BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3bf Hangul Syllable-D3BF | d3be Hangul Syllable-D3BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3c1 Hangul Syllable-D3C1 | d3c0 Hangul Syllable-D3C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3c3 Hangul Syllable-D3C3 | d3c2 Hangul Syllable-D3C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3c5 Hangul Syllable-D3C5 | d3c4 Hangul Syllable-D3C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3c7 Hangul Syllable-D3C7 | d3c6 Hangul Syllable-D3C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3c9 Hangul Syllable-D3C9 | d3c8 Hangul Syllable-D3C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3cb Hangul Syllable-D3CB | d3ca Hangul Syllable-D3CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3cd Hangul Syllable-D3CD | d3cc Hangul Syllable-D3CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3cf Hangul Syllable-D3CF | d3ce Hangul Syllable-D3CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3d1 Hangul Syllable-D3D1 | d3d0 Hangul Syllable-D3D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3d3 Hangul Syllable-D3D3 | d3d2 Hangul Syllable-D3D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3d5 Hangul Syllable-D3D5 | d3d4 Hangul Syllable-D3D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3d7 Hangul Syllable-D3D7 | d3d6 Hangul Syllable-D3D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3d9 Hangul Syllable-D3D9 | d3d8 Hangul Syllable-D3D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3db Hangul Syllable-D3DB | d3da Hangul Syllable-D3DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3dd Hangul Syllable-D3DD | d3dc Hangul Syllable-D3DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3df Hangul Syllable-D3DF | d3de Hangul Syllable-D3DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3e1 Hangul Syllable-D3E1 | d3e0 Hangul Syllable-D3E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3e3 Hangul Syllable-D3E3 | d3e2 Hangul Syllable-D3E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3e5 Hangul Syllable-D3E5 | d3e4 Hangul Syllable-D3E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3e7 Hangul Syllable-D3E7 | d3e6 Hangul Syllable-D3E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3e9 Hangul Syllable-D3E9 | d3e8 Hangul Syllable-D3E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3eb Hangul Syllable-D3EB | d3ea Hangul Syllable-D3EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3ed Hangul Syllable-D3ED | d3ec Hangul Syllable-D3EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3ef Hangul Syllable-D3EF | d3ee Hangul Syllable-D3EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3f1 Hangul Syllable-D3F1 | d3f0 Hangul Syllable-D3F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3f3 Hangul Syllable-D3F3 | d3f2 Hangul Syllable-D3F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3f5 Hangul Syllable-D3F5 | d3f4 Hangul Syllable-D3F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3f7 Hangul Syllable-D3F7 | d3f6 Hangul Syllable-D3F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3f9 Hangul Syllable-D3F9 | d3f8 Hangul Syllable-D3F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3fb Hangul Syllable-D3FB | d3fa Hangul Syllable-D3FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3fd Hangul Syllable-D3FD | d3fc Hangul Syllable-D3FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3ff Hangul Syllable-D3FF | d3fe Hangul Syllable-D3FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d401 Hangul Syllable-D401 | d400 Hangul Syllable-D400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d403 Hangul Syllable-D403 | d402 Hangul Syllable-D402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d405 Hangul Syllable-D405 | d404 Hangul Syllable-D404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d407 Hangul Syllable-D407 | d406 Hangul Syllable-D406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d409 Hangul Syllable-D409 | d408 Hangul Syllable-D408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d40b Hangul Syllable-D40B | d40a Hangul Syllable-D40A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d40d Hangul Syllable-D40D | d40c Hangul Syllable-D40C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d40f Hangul Syllable-D40F | d40e Hangul Syllable-D40E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d411 Hangul Syllable-D411 | d410 Hangul Syllable-D410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d413 Hangul Syllable-D413 | d412 Hangul Syllable-D412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d415 Hangul Syllable-D415 | d414 Hangul Syllable-D414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d417 Hangul Syllable-D417 | d416 Hangul Syllable-D416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d419 Hangul Syllable-D419 | d418 Hangul Syllable-D418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d41b Hangul Syllable-D41B | d41a Hangul Syllable-D41A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d41d Hangul Syllable-D41D | d41c Hangul Syllable-D41C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d41f Hangul Syllable-D41F | d41e Hangul Syllable-D41E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d421 Hangul Syllable-D421 | d420 Hangul Syllable-D420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d423 Hangul Syllable-D423 | d422 Hangul Syllable-D422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d425 Hangul Syllable-D425 | d424 Hangul Syllable-D424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d427 Hangul Syllable-D427 | d426 Hangul Syllable-D426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d429 Hangul Syllable-D429 | d428 Hangul Syllable-D428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d42b Hangul Syllable-D42B | d42a Hangul Syllable-D42A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d42d Hangul Syllable-D42D | d42c Hangul Syllable-D42C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d42f Hangul Syllable-D42F | d42e Hangul Syllable-D42E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d431 Hangul Syllable-D431 | d430 Hangul Syllable-D430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d433 Hangul Syllable-D433 | d432 Hangul Syllable-D432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d435 Hangul Syllable-D435 | d434 Hangul Syllable-D434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d437 Hangul Syllable-D437 | d436 Hangul Syllable-D436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d439 Hangul Syllable-D439 | d438 Hangul Syllable-D438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d43b Hangul Syllable-D43B | d43a Hangul Syllable-D43A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d43d Hangul Syllable-D43D | d43c Hangul Syllable-D43C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d43f Hangul Syllable-D43F | d43e Hangul Syllable-D43E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d441 Hangul Syllable-D441 | d440 Hangul Syllable-D440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d443 Hangul Syllable-D443 | d442 Hangul Syllable-D442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d445 Hangul Syllable-D445 | d444 Hangul Syllable-D444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d447 Hangul Syllable-D447 | d446 Hangul Syllable-D446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d449 Hangul Syllable-D449 | d448 Hangul Syllable-D448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d44b Hangul Syllable-D44B | d44a Hangul Syllable-D44A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d44d Hangul Syllable-D44D | d44c Hangul Syllable-D44C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d44f Hangul Syllable-D44F | d44e Hangul Syllable-D44E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d451 Hangul Syllable-D451 | d450 Hangul Syllable-D450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d453 Hangul Syllable-D453 | d452 Hangul Syllable-D452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d455 Hangul Syllable-D455 | d454 Hangul Syllable-D454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d457 Hangul Syllable-D457 | d456 Hangul Syllable-D456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d459 Hangul Syllable-D459 | d458 Hangul Syllable-D458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d45b Hangul Syllable-D45B | d45a Hangul Syllable-D45A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d45d Hangul Syllable-D45D | d45c Hangul Syllable-D45C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d45f Hangul Syllable-D45F | d45e Hangul Syllable-D45E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d461 Hangul Syllable-D461 | d460 Hangul Syllable-D460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d463 Hangul Syllable-D463 | d462 Hangul Syllable-D462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d465 Hangul Syllable-D465 | d464 Hangul Syllable-D464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d467 Hangul Syllable-D467 | d466 Hangul Syllable-D466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d469 Hangul Syllable-D469 | d468 Hangul Syllable-D468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d46b Hangul Syllable-D46B | d46a Hangul Syllable-D46A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d46d Hangul Syllable-D46D | d46c Hangul Syllable-D46C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d46f Hangul Syllable-D46F | d46e Hangul Syllable-D46E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d471 Hangul Syllable-D471 | d470 Hangul Syllable-D470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d473 Hangul Syllable-D473 | d472 Hangul Syllable-D472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d475 Hangul Syllable-D475 | d474 Hangul Syllable-D474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d477 Hangul Syllable-D477 | d476 Hangul Syllable-D476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d479 Hangul Syllable-D479 | d478 Hangul Syllable-D478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d47b Hangul Syllable-D47B | d47a Hangul Syllable-D47A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d47d Hangul Syllable-D47D | d47c Hangul Syllable-D47C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d47f Hangul Syllable-D47F | d47e Hangul Syllable-D47E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d481 Hangul Syllable-D481 | d480 Hangul Syllable-D480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d483 Hangul Syllable-D483 | d482 Hangul Syllable-D482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d485 Hangul Syllable-D485 | d484 Hangul Syllable-D484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d487 Hangul Syllable-D487 | d486 Hangul Syllable-D486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d489 Hangul Syllable-D489 | d488 Hangul Syllable-D488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d48b Hangul Syllable-D48B | d48a Hangul Syllable-D48A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d48d Hangul Syllable-D48D | d48c Hangul Syllable-D48C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d48f Hangul Syllable-D48F | d48e Hangul Syllable-D48E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d491 Hangul Syllable-D491 | d490 Hangul Syllable-D490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d493 Hangul Syllable-D493 | d492 Hangul Syllable-D492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d495 Hangul Syllable-D495 | d494 Hangul Syllable-D494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d497 Hangul Syllable-D497 | d496 Hangul Syllable-D496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d499 Hangul Syllable-D499 | d498 Hangul Syllable-D498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d49b Hangul Syllable-D49B | d49a Hangul Syllable-D49A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d49d Hangul Syllable-D49D | d49c Hangul Syllable-D49C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d49f Hangul Syllable-D49F | d49e Hangul Syllable-D49E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4a1 Hangul Syllable-D4A1 | d4a0 Hangul Syllable-D4A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4a3 Hangul Syllable-D4A3 | d4a2 Hangul Syllable-D4A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4a5 Hangul Syllable-D4A5 | d4a4 Hangul Syllable-D4A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4a7 Hangul Syllable-D4A7 | d4a6 Hangul Syllable-D4A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4a9 Hangul Syllable-D4A9 | d4a8 Hangul Syllable-D4A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4ab Hangul Syllable-D4AB | d4aa Hangul Syllable-D4AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4ad Hangul Syllable-D4AD | d4ac Hangul Syllable-D4AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4af Hangul Syllable-D4AF | d4ae Hangul Syllable-D4AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4b1 Hangul Syllable-D4B1 | d4b0 Hangul Syllable-D4B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4b3 Hangul Syllable-D4B3 | d4b2 Hangul Syllable-D4B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4b5 Hangul Syllable-D4B5 | d4b4 Hangul Syllable-D4B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4b7 Hangul Syllable-D4B7 | d4b6 Hangul Syllable-D4B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4b9 Hangul Syllable-D4B9 | d4b8 Hangul Syllable-D4B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4bb Hangul Syllable-D4BB | d4ba Hangul Syllable-D4BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4bd Hangul Syllable-D4BD | d4bc Hangul Syllable-D4BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4bf Hangul Syllable-D4BF | d4be Hangul Syllable-D4BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4c1 Hangul Syllable-D4C1 | d4c0 Hangul Syllable-D4C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4c3 Hangul Syllable-D4C3 | d4c2 Hangul Syllable-D4C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4c5 Hangul Syllable-D4C5 | d4c4 Hangul Syllable-D4C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4c7 Hangul Syllable-D4C7 | d4c6 Hangul Syllable-D4C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4c9 Hangul Syllable-D4C9 | d4c8 Hangul Syllable-D4C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4cb Hangul Syllable-D4CB | d4ca Hangul Syllable-D4CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4cd Hangul Syllable-D4CD | d4cc Hangul Syllable-D4CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4cf Hangul Syllable-D4CF | d4ce Hangul Syllable-D4CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4d1 Hangul Syllable-D4D1 | d4d0 Hangul Syllable-D4D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4d3 Hangul Syllable-D4D3 | d4d2 Hangul Syllable-D4D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4d5 Hangul Syllable-D4D5 | d4d4 Hangul Syllable-D4D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4d7 Hangul Syllable-D4D7 | d4d6 Hangul Syllable-D4D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4d9 Hangul Syllable-D4D9 | d4d8 Hangul Syllable-D4D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4db Hangul Syllable-D4DB | d4da Hangul Syllable-D4DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4dd Hangul Syllable-D4DD | d4dc Hangul Syllable-D4DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4df Hangul Syllable-D4DF | d4de Hangul Syllable-D4DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4e1 Hangul Syllable-D4E1 | d4e0 Hangul Syllable-D4E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4e3 Hangul Syllable-D4E3 | d4e2 Hangul Syllable-D4E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4e5 Hangul Syllable-D4E5 | d4e4 Hangul Syllable-D4E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4e7 Hangul Syllable-D4E7 | d4e6 Hangul Syllable-D4E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4e9 Hangul Syllable-D4E9 | d4e8 Hangul Syllable-D4E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4eb Hangul Syllable-D4EB | d4ea Hangul Syllable-D4EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4ed Hangul Syllable-D4ED | d4ec Hangul Syllable-D4EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4ef Hangul Syllable-D4EF | d4ee Hangul Syllable-D4EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4f1 Hangul Syllable-D4F1 | d4f0 Hangul Syllable-D4F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4f3 Hangul Syllable-D4F3 | d4f2 Hangul Syllable-D4F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4f5 Hangul Syllable-D4F5 | d4f4 Hangul Syllable-D4F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4f7 Hangul Syllable-D4F7 | d4f6 Hangul Syllable-D4F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4f9 Hangul Syllable-D4F9 | d4f8 Hangul Syllable-D4F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4fb Hangul Syllable-D4FB | d4fa Hangul Syllable-D4FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4fd Hangul Syllable-D4FD | d4fc Hangul Syllable-D4FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4ff Hangul Syllable-D4FF | d4fe Hangul Syllable-D4FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d501 Hangul Syllable-D501 | d500 Hangul Syllable-D500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d503 Hangul Syllable-D503 | d502 Hangul Syllable-D502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d505 Hangul Syllable-D505 | d504 Hangul Syllable-D504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d507 Hangul Syllable-D507 | d506 Hangul Syllable-D506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d509 Hangul Syllable-D509 | d508 Hangul Syllable-D508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d50b Hangul Syllable-D50B | d50a Hangul Syllable-D50A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d50d Hangul Syllable-D50D | d50c Hangul Syllable-D50C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d50f Hangul Syllable-D50F | d50e Hangul Syllable-D50E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d511 Hangul Syllable-D511 | d510 Hangul Syllable-D510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d513 Hangul Syllable-D513 | d512 Hangul Syllable-D512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d515 Hangul Syllable-D515 | d514 Hangul Syllable-D514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d517 Hangul Syllable-D517 | d516 Hangul Syllable-D516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d519 Hangul Syllable-D519 | d518 Hangul Syllable-D518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d51b Hangul Syllable-D51B | d51a Hangul Syllable-D51A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d51d Hangul Syllable-D51D | d51c Hangul Syllable-D51C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d51f Hangul Syllable-D51F | d51e Hangul Syllable-D51E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d521 Hangul Syllable-D521 | d520 Hangul Syllable-D520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d523 Hangul Syllable-D523 | d522 Hangul Syllable-D522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d525 Hangul Syllable-D525 | d524 Hangul Syllable-D524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d527 Hangul Syllable-D527 | d526 Hangul Syllable-D526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d529 Hangul Syllable-D529 | d528 Hangul Syllable-D528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d52b Hangul Syllable-D52B | d52a Hangul Syllable-D52A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d52d Hangul Syllable-D52D | d52c Hangul Syllable-D52C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d52f Hangul Syllable-D52F | d52e Hangul Syllable-D52E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d531 Hangul Syllable-D531 | d530 Hangul Syllable-D530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d533 Hangul Syllable-D533 | d532 Hangul Syllable-D532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d535 Hangul Syllable-D535 | d534 Hangul Syllable-D534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d537 Hangul Syllable-D537 | d536 Hangul Syllable-D536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d539 Hangul Syllable-D539 | d538 Hangul Syllable-D538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d53b Hangul Syllable-D53B | d53a Hangul Syllable-D53A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d53d Hangul Syllable-D53D | d53c Hangul Syllable-D53C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d53f Hangul Syllable-D53F | d53e Hangul Syllable-D53E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d541 Hangul Syllable-D541 | d540 Hangul Syllable-D540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d543 Hangul Syllable-D543 | d542 Hangul Syllable-D542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d545 Hangul Syllable-D545 | d544 Hangul Syllable-D544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d547 Hangul Syllable-D547 | d546 Hangul Syllable-D546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d549 Hangul Syllable-D549 | d548 Hangul Syllable-D548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d54b Hangul Syllable-D54B | d54a Hangul Syllable-D54A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d54d Hangul Syllable-D54D | d54c Hangul Syllable-D54C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d54f Hangul Syllable-D54F | d54e Hangul Syllable-D54E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d551 Hangul Syllable-D551 | d550 Hangul Syllable-D550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d553 Hangul Syllable-D553 | d552 Hangul Syllable-D552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d555 Hangul Syllable-D555 | d554 Hangul Syllable-D554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d557 Hangul Syllable-D557 | d556 Hangul Syllable-D556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d559 Hangul Syllable-D559 | d558 Hangul Syllable-D558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d55b Hangul Syllable-D55B | d55a Hangul Syllable-D55A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d55d Hangul Syllable-D55D | d55c Hangul Syllable-D55C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d55f Hangul Syllable-D55F | d55e Hangul Syllable-D55E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d561 Hangul Syllable-D561 | d560 Hangul Syllable-D560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d563 Hangul Syllable-D563 | d562 Hangul Syllable-D562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d565 Hangul Syllable-D565 | d564 Hangul Syllable-D564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d567 Hangul Syllable-D567 | d566 Hangul Syllable-D566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d569 Hangul Syllable-D569 | d568 Hangul Syllable-D568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d56b Hangul Syllable-D56B | d56a Hangul Syllable-D56A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d56d Hangul Syllable-D56D | d56c Hangul Syllable-D56C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d56f Hangul Syllable-D56F | d56e Hangul Syllable-D56E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d571 Hangul Syllable-D571 | d570 Hangul Syllable-D570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d573 Hangul Syllable-D573 | d572 Hangul Syllable-D572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d575 Hangul Syllable-D575 | d574 Hangul Syllable-D574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d577 Hangul Syllable-D577 | d576 Hangul Syllable-D576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d579 Hangul Syllable-D579 | d578 Hangul Syllable-D578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d57b Hangul Syllable-D57B | d57a Hangul Syllable-D57A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d57d Hangul Syllable-D57D | d57c Hangul Syllable-D57C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d57f Hangul Syllable-D57F | d57e Hangul Syllable-D57E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d581 Hangul Syllable-D581 | d580 Hangul Syllable-D580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d583 Hangul Syllable-D583 | d582 Hangul Syllable-D582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d585 Hangul Syllable-D585 | d584 Hangul Syllable-D584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d587 Hangul Syllable-D587 | d586 Hangul Syllable-D586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d589 Hangul Syllable-D589 | d588 Hangul Syllable-D588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d58b Hangul Syllable-D58B | d58a Hangul Syllable-D58A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d58d Hangul Syllable-D58D | d58c Hangul Syllable-D58C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d58f Hangul Syllable-D58F | d58e Hangul Syllable-D58E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d591 Hangul Syllable-D591 | d590 Hangul Syllable-D590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d593 Hangul Syllable-D593 | d592 Hangul Syllable-D592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d595 Hangul Syllable-D595 | d594 Hangul Syllable-D594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d597 Hangul Syllable-D597 | d596 Hangul Syllable-D596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d599 Hangul Syllable-D599 | d598 Hangul Syllable-D598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d59b Hangul Syllable-D59B | d59a Hangul Syllable-D59A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d59d Hangul Syllable-D59D | d59c Hangul Syllable-D59C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d59f Hangul Syllable-D59F | d59e Hangul Syllable-D59E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5a1 Hangul Syllable-D5A1 | d5a0 Hangul Syllable-D5A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5a3 Hangul Syllable-D5A3 | d5a2 Hangul Syllable-D5A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5a5 Hangul Syllable-D5A5 | d5a4 Hangul Syllable-D5A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5a7 Hangul Syllable-D5A7 | d5a6 Hangul Syllable-D5A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5a9 Hangul Syllable-D5A9 | d5a8 Hangul Syllable-D5A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5ab Hangul Syllable-D5AB | d5aa Hangul Syllable-D5AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5ad Hangul Syllable-D5AD | d5ac Hangul Syllable-D5AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5af Hangul Syllable-D5AF | d5ae Hangul Syllable-D5AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5b1 Hangul Syllable-D5B1 | d5b0 Hangul Syllable-D5B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5b3 Hangul Syllable-D5B3 | d5b2 Hangul Syllable-D5B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5b5 Hangul Syllable-D5B5 | d5b4 Hangul Syllable-D5B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5b7 Hangul Syllable-D5B7 | d5b6 Hangul Syllable-D5B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5b9 Hangul Syllable-D5B9 | d5b8 Hangul Syllable-D5B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5bb Hangul Syllable-D5BB | d5ba Hangul Syllable-D5BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5bd Hangul Syllable-D5BD | d5bc Hangul Syllable-D5BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5bf Hangul Syllable-D5BF | d5be Hangul Syllable-D5BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5c1 Hangul Syllable-D5C1 | d5c0 Hangul Syllable-D5C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5c3 Hangul Syllable-D5C3 | d5c2 Hangul Syllable-D5C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5c5 Hangul Syllable-D5C5 | d5c4 Hangul Syllable-D5C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5c7 Hangul Syllable-D5C7 | d5c6 Hangul Syllable-D5C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5c9 Hangul Syllable-D5C9 | d5c8 Hangul Syllable-D5C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5cb Hangul Syllable-D5CB | d5ca Hangul Syllable-D5CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5cd Hangul Syllable-D5CD | d5cc Hangul Syllable-D5CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5cf Hangul Syllable-D5CF | d5ce Hangul Syllable-D5CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5d1 Hangul Syllable-D5D1 | d5d0 Hangul Syllable-D5D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5d3 Hangul Syllable-D5D3 | d5d2 Hangul Syllable-D5D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5d5 Hangul Syllable-D5D5 | d5d4 Hangul Syllable-D5D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5d7 Hangul Syllable-D5D7 | d5d6 Hangul Syllable-D5D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5d9 Hangul Syllable-D5D9 | d5d8 Hangul Syllable-D5D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5db Hangul Syllable-D5DB | d5da Hangul Syllable-D5DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5dd Hangul Syllable-D5DD | d5dc Hangul Syllable-D5DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5df Hangul Syllable-D5DF | d5de Hangul Syllable-D5DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5e1 Hangul Syllable-D5E1 | d5e0 Hangul Syllable-D5E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5e3 Hangul Syllable-D5E3 | d5e2 Hangul Syllable-D5E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5e5 Hangul Syllable-D5E5 | d5e4 Hangul Syllable-D5E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5e7 Hangul Syllable-D5E7 | d5e6 Hangul Syllable-D5E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5e9 Hangul Syllable-D5E9 | d5e8 Hangul Syllable-D5E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5eb Hangul Syllable-D5EB | d5ea Hangul Syllable-D5EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5ed Hangul Syllable-D5ED | d5ec Hangul Syllable-D5EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5ef Hangul Syllable-D5EF | d5ee Hangul Syllable-D5EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5f1 Hangul Syllable-D5F1 | d5f0 Hangul Syllable-D5F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5f3 Hangul Syllable-D5F3 | d5f2 Hangul Syllable-D5F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5f5 Hangul Syllable-D5F5 | d5f4 Hangul Syllable-D5F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5f7 Hangul Syllable-D5F7 | d5f6 Hangul Syllable-D5F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5f9 Hangul Syllable-D5F9 | d5f8 Hangul Syllable-D5F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5fb Hangul Syllable-D5FB | d5fa Hangul Syllable-D5FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5fd Hangul Syllable-D5FD | d5fc Hangul Syllable-D5FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5ff Hangul Syllable-D5FF | d5fe Hangul Syllable-D5FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d601 Hangul Syllable-D601 | d600 Hangul Syllable-D600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d603 Hangul Syllable-D603 | d602 Hangul Syllable-D602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d605 Hangul Syllable-D605 | d604 Hangul Syllable-D604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d607 Hangul Syllable-D607 | d606 Hangul Syllable-D606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d609 Hangul Syllable-D609 | d608 Hangul Syllable-D608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d60b Hangul Syllable-D60B | d60a Hangul Syllable-D60A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d60d Hangul Syllable-D60D | d60c Hangul Syllable-D60C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d60f Hangul Syllable-D60F | d60e Hangul Syllable-D60E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d611 Hangul Syllable-D611 | d610 Hangul Syllable-D610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d613 Hangul Syllable-D613 | d612 Hangul Syllable-D612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d615 Hangul Syllable-D615 | d614 Hangul Syllable-D614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d617 Hangul Syllable-D617 | d616 Hangul Syllable-D616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d619 Hangul Syllable-D619 | d618 Hangul Syllable-D618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d61b Hangul Syllable-D61B | d61a Hangul Syllable-D61A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d61d Hangul Syllable-D61D | d61c Hangul Syllable-D61C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d61f Hangul Syllable-D61F | d61e Hangul Syllable-D61E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d621 Hangul Syllable-D621 | d620 Hangul Syllable-D620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d623 Hangul Syllable-D623 | d622 Hangul Syllable-D622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d625 Hangul Syllable-D625 | d624 Hangul Syllable-D624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d627 Hangul Syllable-D627 | d626 Hangul Syllable-D626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d629 Hangul Syllable-D629 | d628 Hangul Syllable-D628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d62b Hangul Syllable-D62B | d62a Hangul Syllable-D62A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d62d Hangul Syllable-D62D | d62c Hangul Syllable-D62C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d62f Hangul Syllable-D62F | d62e Hangul Syllable-D62E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d631 Hangul Syllable-D631 | d630 Hangul Syllable-D630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d633 Hangul Syllable-D633 | d632 Hangul Syllable-D632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d635 Hangul Syllable-D635 | d634 Hangul Syllable-D634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d637 Hangul Syllable-D637 | d636 Hangul Syllable-D636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d639 Hangul Syllable-D639 | d638 Hangul Syllable-D638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d63b Hangul Syllable-D63B | d63a Hangul Syllable-D63A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d63d Hangul Syllable-D63D | d63c Hangul Syllable-D63C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d63f Hangul Syllable-D63F | d63e Hangul Syllable-D63E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d641 Hangul Syllable-D641 | d640 Hangul Syllable-D640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d643 Hangul Syllable-D643 | d642 Hangul Syllable-D642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d645 Hangul Syllable-D645 | d644 Hangul Syllable-D644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d647 Hangul Syllable-D647 | d646 Hangul Syllable-D646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d649 Hangul Syllable-D649 | d648 Hangul Syllable-D648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d64b Hangul Syllable-D64B | d64a Hangul Syllable-D64A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d64d Hangul Syllable-D64D | d64c Hangul Syllable-D64C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d64f Hangul Syllable-D64F | d64e Hangul Syllable-D64E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d651 Hangul Syllable-D651 | d650 Hangul Syllable-D650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d653 Hangul Syllable-D653 | d652 Hangul Syllable-D652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d655 Hangul Syllable-D655 | d654 Hangul Syllable-D654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d657 Hangul Syllable-D657 | d656 Hangul Syllable-D656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d659 Hangul Syllable-D659 | d658 Hangul Syllable-D658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d65b Hangul Syllable-D65B | d65a Hangul Syllable-D65A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d65d Hangul Syllable-D65D | d65c Hangul Syllable-D65C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d65f Hangul Syllable-D65F | d65e Hangul Syllable-D65E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d661 Hangul Syllable-D661 | d660 Hangul Syllable-D660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d663 Hangul Syllable-D663 | d662 Hangul Syllable-D662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d665 Hangul Syllable-D665 | d664 Hangul Syllable-D664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d667 Hangul Syllable-D667 | d666 Hangul Syllable-D666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d669 Hangul Syllable-D669 | d668 Hangul Syllable-D668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d66b Hangul Syllable-D66B | d66a Hangul Syllable-D66A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d66d Hangul Syllable-D66D | d66c Hangul Syllable-D66C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d66f Hangul Syllable-D66F | d66e Hangul Syllable-D66E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d671 Hangul Syllable-D671 | d670 Hangul Syllable-D670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d673 Hangul Syllable-D673 | d672 Hangul Syllable-D672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d675 Hangul Syllable-D675 | d674 Hangul Syllable-D674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d677 Hangul Syllable-D677 | d676 Hangul Syllable-D676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d679 Hangul Syllable-D679 | d678 Hangul Syllable-D678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d67b Hangul Syllable-D67B | d67a Hangul Syllable-D67A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d67d Hangul Syllable-D67D | d67c Hangul Syllable-D67C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d67f Hangul Syllable-D67F | d67e Hangul Syllable-D67E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d681 Hangul Syllable-D681 | d680 Hangul Syllable-D680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d683 Hangul Syllable-D683 | d682 Hangul Syllable-D682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d685 Hangul Syllable-D685 | d684 Hangul Syllable-D684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d687 Hangul Syllable-D687 | d686 Hangul Syllable-D686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d689 Hangul Syllable-D689 | d688 Hangul Syllable-D688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d68b Hangul Syllable-D68B | d68a Hangul Syllable-D68A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d68d Hangul Syllable-D68D | d68c Hangul Syllable-D68C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d68f Hangul Syllable-D68F | d68e Hangul Syllable-D68E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d691 Hangul Syllable-D691 | d690 Hangul Syllable-D690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d693 Hangul Syllable-D693 | d692 Hangul Syllable-D692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d695 Hangul Syllable-D695 | d694 Hangul Syllable-D694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d697 Hangul Syllable-D697 | d696 Hangul Syllable-D696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d699 Hangul Syllable-D699 | d698 Hangul Syllable-D698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d69b Hangul Syllable-D69B | d69a Hangul Syllable-D69A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d69d Hangul Syllable-D69D | d69c Hangul Syllable-D69C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d69f Hangul Syllable-D69F | d69e Hangul Syllable-D69E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6a1 Hangul Syllable-D6A1 | d6a0 Hangul Syllable-D6A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6a3 Hangul Syllable-D6A3 | d6a2 Hangul Syllable-D6A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6a5 Hangul Syllable-D6A5 | d6a4 Hangul Syllable-D6A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6a7 Hangul Syllable-D6A7 | d6a6 Hangul Syllable-D6A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6a9 Hangul Syllable-D6A9 | d6a8 Hangul Syllable-D6A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6ab Hangul Syllable-D6AB | d6aa Hangul Syllable-D6AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6ad Hangul Syllable-D6AD | d6ac Hangul Syllable-D6AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6af Hangul Syllable-D6AF | d6ae Hangul Syllable-D6AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6b1 Hangul Syllable-D6B1 | d6b0 Hangul Syllable-D6B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6b3 Hangul Syllable-D6B3 | d6b2 Hangul Syllable-D6B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6b5 Hangul Syllable-D6B5 | d6b4 Hangul Syllable-D6B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6b7 Hangul Syllable-D6B7 | d6b6 Hangul Syllable-D6B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6b9 Hangul Syllable-D6B9 | d6b8 Hangul Syllable-D6B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6bb Hangul Syllable-D6BB | d6ba Hangul Syllable-D6BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6bd Hangul Syllable-D6BD | d6bc Hangul Syllable-D6BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6bf Hangul Syllable-D6BF | d6be Hangul Syllable-D6BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6c1 Hangul Syllable-D6C1 | d6c0 Hangul Syllable-D6C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6c3 Hangul Syllable-D6C3 | d6c2 Hangul Syllable-D6C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6c5 Hangul Syllable-D6C5 | d6c4 Hangul Syllable-D6C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6c7 Hangul Syllable-D6C7 | d6c6 Hangul Syllable-D6C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6c9 Hangul Syllable-D6C9 | d6c8 Hangul Syllable-D6C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6cb Hangul Syllable-D6CB | d6ca Hangul Syllable-D6CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6cd Hangul Syllable-D6CD | d6cc Hangul Syllable-D6CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6cf Hangul Syllable-D6CF | d6ce Hangul Syllable-D6CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6d1 Hangul Syllable-D6D1 | d6d0 Hangul Syllable-D6D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6d3 Hangul Syllable-D6D3 | d6d2 Hangul Syllable-D6D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6d5 Hangul Syllable-D6D5 | d6d4 Hangul Syllable-D6D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6d7 Hangul Syllable-D6D7 | d6d6 Hangul Syllable-D6D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6d9 Hangul Syllable-D6D9 | d6d8 Hangul Syllable-D6D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6db Hangul Syllable-D6DB | d6da Hangul Syllable-D6DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6dd Hangul Syllable-D6DD | d6dc Hangul Syllable-D6DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6df Hangul Syllable-D6DF | d6de Hangul Syllable-D6DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6e1 Hangul Syllable-D6E1 | d6e0 Hangul Syllable-D6E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6e3 Hangul Syllable-D6E3 | d6e2 Hangul Syllable-D6E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6e5 Hangul Syllable-D6E5 | d6e4 Hangul Syllable-D6E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6e7 Hangul Syllable-D6E7 | d6e6 Hangul Syllable-D6E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6e9 Hangul Syllable-D6E9 | d6e8 Hangul Syllable-D6E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6eb Hangul Syllable-D6EB | d6ea Hangul Syllable-D6EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6ed Hangul Syllable-D6ED | d6ec Hangul Syllable-D6EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6ef Hangul Syllable-D6EF | d6ee Hangul Syllable-D6EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6f1 Hangul Syllable-D6F1 | d6f0 Hangul Syllable-D6F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6f3 Hangul Syllable-D6F3 | d6f2 Hangul Syllable-D6F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6f5 Hangul Syllable-D6F5 | d6f4 Hangul Syllable-D6F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6f7 Hangul Syllable-D6F7 | d6f6 Hangul Syllable-D6F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6f9 Hangul Syllable-D6F9 | d6f8 Hangul Syllable-D6F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6fb Hangul Syllable-D6FB | d6fa Hangul Syllable-D6FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6fd Hangul Syllable-D6FD | d6fc Hangul Syllable-D6FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6ff Hangul Syllable-D6FF | d6fe Hangul Syllable-D6FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d701 Hangul Syllable-D701 | d700 Hangul Syllable-D700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d703 Hangul Syllable-D703 | d702 Hangul Syllable-D702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d705 Hangul Syllable-D705 | d704 Hangul Syllable-D704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d707 Hangul Syllable-D707 | d706 Hangul Syllable-D706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d709 Hangul Syllable-D709 | d708 Hangul Syllable-D708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d70b Hangul Syllable-D70B | d70a Hangul Syllable-D70A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d70d Hangul Syllable-D70D | d70c Hangul Syllable-D70C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d70f Hangul Syllable-D70F | d70e Hangul Syllable-D70E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d711 Hangul Syllable-D711 | d710 Hangul Syllable-D710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d713 Hangul Syllable-D713 | d712 Hangul Syllable-D712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d715 Hangul Syllable-D715 | d714 Hangul Syllable-D714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d717 Hangul Syllable-D717 | d716 Hangul Syllable-D716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d719 Hangul Syllable-D719 | d718 Hangul Syllable-D718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d71b Hangul Syllable-D71B | d71a Hangul Syllable-D71A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d71d Hangul Syllable-D71D | d71c Hangul Syllable-D71C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d71f Hangul Syllable-D71F | d71e Hangul Syllable-D71E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d721 Hangul Syllable-D721 | d720 Hangul Syllable-D720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d723 Hangul Syllable-D723 | d722 Hangul Syllable-D722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d725 Hangul Syllable-D725 | d724 Hangul Syllable-D724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d727 Hangul Syllable-D727 | d726 Hangul Syllable-D726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d729 Hangul Syllable-D729 | d728 Hangul Syllable-D728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d72b Hangul Syllable-D72B | d72a Hangul Syllable-D72A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d72d Hangul Syllable-D72D | d72c Hangul Syllable-D72C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d72f Hangul Syllable-D72F | d72e Hangul Syllable-D72E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d731 Hangul Syllable-D731 | d730 Hangul Syllable-D730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d733 Hangul Syllable-D733 | d732 Hangul Syllable-D732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d735 Hangul Syllable-D735 | d734 Hangul Syllable-D734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d737 Hangul Syllable-D737 | d736 Hangul Syllable-D736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d739 Hangul Syllable-D739 | d738 Hangul Syllable-D738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d73b Hangul Syllable-D73B | d73a Hangul Syllable-D73A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d73d Hangul Syllable-D73D | d73c Hangul Syllable-D73C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d73f Hangul Syllable-D73F | d73e Hangul Syllable-D73E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d741 Hangul Syllable-D741 | d740 Hangul Syllable-D740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d743 Hangul Syllable-D743 | d742 Hangul Syllable-D742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d745 Hangul Syllable-D745 | d744 Hangul Syllable-D744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d747 Hangul Syllable-D747 | d746 Hangul Syllable-D746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d749 Hangul Syllable-D749 | d748 Hangul Syllable-D748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d74b Hangul Syllable-D74B | d74a Hangul Syllable-D74A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d74d Hangul Syllable-D74D | d74c Hangul Syllable-D74C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d74f Hangul Syllable-D74F | d74e Hangul Syllable-D74E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d751 Hangul Syllable-D751 | d750 Hangul Syllable-D750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d753 Hangul Syllable-D753 | d752 Hangul Syllable-D752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d755 Hangul Syllable-D755 | d754 Hangul Syllable-D754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d757 Hangul Syllable-D757 | d756 Hangul Syllable-D756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d759 Hangul Syllable-D759 | d758 Hangul Syllable-D758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d75b Hangul Syllable-D75B | d75a Hangul Syllable-D75A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d75d Hangul Syllable-D75D | d75c Hangul Syllable-D75C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d75f Hangul Syllable-D75F | d75e Hangul Syllable-D75E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d761 Hangul Syllable-D761 | d760 Hangul Syllable-D760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d763 Hangul Syllable-D763 | d762 Hangul Syllable-D762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d765 Hangul Syllable-D765 | d764 Hangul Syllable-D764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d767 Hangul Syllable-D767 | d766 Hangul Syllable-D766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d769 Hangul Syllable-D769 | d768 Hangul Syllable-D768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d76b Hangul Syllable-D76B | d76a Hangul Syllable-D76A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d76d Hangul Syllable-D76D | d76c Hangul Syllable-D76C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d76f Hangul Syllable-D76F | d76e Hangul Syllable-D76E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d771 Hangul Syllable-D771 | d770 Hangul Syllable-D770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d773 Hangul Syllable-D773 | d772 Hangul Syllable-D772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d775 Hangul Syllable-D775 | d774 Hangul Syllable-D774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d777 Hangul Syllable-D777 | d776 Hangul Syllable-D776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d779 Hangul Syllable-D779 | d778 Hangul Syllable-D778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d77b Hangul Syllable-D77B | d77a Hangul Syllable-D77A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d77d Hangul Syllable-D77D | d77c Hangul Syllable-D77C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d77f Hangul Syllable-D77F | d77e Hangul Syllable-D77E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d781 Hangul Syllable-D781 | d780 Hangul Syllable-D780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d783 Hangul Syllable-D783 | d782 Hangul Syllable-D782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d785 Hangul Syllable-D785 | d784 Hangul Syllable-D784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d787 Hangul Syllable-D787 | d786 Hangul Syllable-D786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d789 Hangul Syllable-D789 | d788 Hangul Syllable-D788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d78b Hangul Syllable-D78B | d78a Hangul Syllable-D78A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d78d Hangul Syllable-D78D | d78c Hangul Syllable-D78C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d78f Hangul Syllable-D78F | d78e Hangul Syllable-D78E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d791 Hangul Syllable-D791 | d790 Hangul Syllable-D790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d793 Hangul Syllable-D793 | d792 Hangul Syllable-D792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d795 Hangul Syllable-D795 | d794 Hangul Syllable-D794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d797 Hangul Syllable-D797 | d796 Hangul Syllable-D796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d799 Hangul Syllable-D799 | d798 Hangul Syllable-D798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d79b Hangul Syllable-D79B | d79a Hangul Syllable-D79A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d79d Hangul Syllable-D79D | d79c Hangul Syllable-D79C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d79f Hangul Syllable-D79F | d79e Hangul Syllable-D79E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7a1 Hangul Syllable-D7A1 | d7a0 Hangul Syllable-D7A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7a3 Hangul Syllable-D7A3 | d7a2 Hangul Syllable-D7A2 */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7a5 (null) | d7a4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7a7 (null) | d7a6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7a9 (null) | d7a8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7ab (null) | d7aa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7ad (null) | d7ac (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7af (null) | d7ae (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7b1 HANGUL JUNGSEONG O-O-I | d7b0 HANGUL JUNGSEONG O-YEO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7b3 HANGUL JUNGSEONG YO-AE | d7b2 HANGUL JUNGSEONG YO-A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7b5 HANGUL JUNGSEONG U-YEO | d7b4 HANGUL JUNGSEONG YO-EO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7b7 HANGUL JUNGSEONG YU-AE | d7b6 HANGUL JUNGSEONG U-I-I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7b9 HANGUL JUNGSEONG EU-A | d7b8 HANGUL JUNGSEONG YU-O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7bb HANGUL JUNGSEONG EU-E | d7ba HANGUL JUNGSEONG EU-EO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7bd HANGUL JUNGSEONG I-YA-O | d7bc HANGUL JUNGSEONG EU-O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7bf HANGUL JUNGSEONG I-YEO | d7be HANGUL JUNGSEONG I-YAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7c1 HANGUL JUNGSEONG I-O-I | d7c0 HANGUL JUNGSEONG I-YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7c3 HANGUL JUNGSEONG I-YU | d7c2 HANGUL JUNGSEONG I-YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7c5 HANGUL JUNGSEONG ARAEA-A | d7c4 HANGUL JUNGSEONG I-I */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* d7c7 (null) | d7c6 HANGUL JUNGSEONG ARAEA-E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7c9 (null) | d7c8 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* d7cb HANGUL JONGSEONG NIEUN-RIEUL | d7ca (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7cd HANGUL JONGSEONG SSANGTIKEUT | d7cc HANGUL JONGSEONG NIEUN-CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7cf HANGUL JONGSEONG TIKEUT-PIEUP | d7ce HANGUL JONGSEONG SSANGTIKEUT-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7d1 HANGUL JONGSEONG TIKEUT-SIOS-KIYEOK | d7d0 HANGUL JONGSEONG TIKEUT-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7d3 HANGUL JONGSEONG TIKEUT-CHIEUCH | d7d2 HANGUL JONGSEONG TIKEUT-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7d5 HANGUL JONGSEONG RIEUL-SSANGKIYEOK | d7d4 HANGUL JONGSEONG TIKEUT-THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7d7 HANGUL JONGSEONG SSANGRIEUL-KHIEUKH | d7d6 HANGUL JONGSEONG RIEUL-KIYEOK-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7d9 HANGUL JONGSEONG RIEUL-PIEUP-TIKEUT | d7d8 HANGUL JONGSEONG RIEUL-MIEUM-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7db HANGUL JONGSEONG RIEUL-YESIEUNG | d7da HANGUL JONGSEONG RIEUL-PIEUP-PHIEUPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7dd HANGUL JONGSEONG KAPYEOUNRIEUL | d7dc HANGUL JONGSEONG RIEUL-YEORINHIEUH-HIEU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7df HANGUL JONGSEONG MIEUM-SSANGNIEUN | d7de HANGUL JONGSEONG MIEUM-NIEUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7e1 HANGUL JONGSEONG MIEUM-PIEUP-SIOS | d7e0 HANGUL JONGSEONG SSANGMIEUM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7e3 HANGUL JONGSEONG PIEUP-TIKEUT | d7e2 HANGUL JONGSEONG MIEUM-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7e5 HANGUL JONGSEONG PIEUP-MIEUM | d7e4 HANGUL JONGSEONG PIEUP-RIEUL-PHIEUPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7e7 HANGUL JONGSEONG PIEUP-SIOS-TIKEUT | d7e6 HANGUL JONGSEONG SSANGPIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7e9 HANGUL JONGSEONG PIEUP-CHIEUCH | d7e8 HANGUL JONGSEONG PIEUP-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7eb HANGUL JONGSEONG SIOS-KAPYEOUNPIEUP | d7ea HANGUL JONGSEONG SIOS-MIEUM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7ed HANGUL JONGSEONG SSANGSIOS-TIKEUT | d7ec HANGUL JONGSEONG SSANGSIOS-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7ef HANGUL JONGSEONG SIOS-CIEUC | d7ee HANGUL JONGSEONG SIOS-PANSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7f1 HANGUL JONGSEONG SIOS-THIEUTH | d7f0 HANGUL JONGSEONG SIOS-CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7f3 HANGUL JONGSEONG PANSIOS-PIEUP | d7f2 HANGUL JONGSEONG SIOS-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7f5 HANGUL JONGSEONG YESIEUNG-MIEUM | d7f4 HANGUL JONGSEONG PANSIOS-KAPYEOUNPIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7f7 HANGUL JONGSEONG CIEUC-PIEUP | d7f6 HANGUL JONGSEONG YESIEUNG-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7f9 HANGUL JONGSEONG SSANGCIEUC | d7f8 HANGUL JONGSEONG CIEUC-SSANGPIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7fb HANGUL JONGSEONG PHIEUPH-THIEUTH | d7fa HANGUL JONGSEONG PHIEUPH-SIOS */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7fd (null) | d7fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7ff (null) | d7fe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d801 Non Private Use High Surrogate-D801 | d800 Non Private Use High Surrogate-D800 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d803 Non Private Use High Surrogate-D803 | d802 Non Private Use High Surrogate-D802 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d805 Non Private Use High Surrogate-D805 | d804 Non Private Use High Surrogate-D804 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d807 Non Private Use High Surrogate-D807 | d806 Non Private Use High Surrogate-D806 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d809 Non Private Use High Surrogate-D809 | d808 Non Private Use High Surrogate-D808 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d80b Non Private Use High Surrogate-D80B | d80a Non Private Use High Surrogate-D80A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d80d Non Private Use High Surrogate-D80D | d80c Non Private Use High Surrogate-D80C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d80f Non Private Use High Surrogate-D80F | d80e Non Private Use High Surrogate-D80E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d811 Non Private Use High Surrogate-D811 | d810 Non Private Use High Surrogate-D810 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d813 Non Private Use High Surrogate-D813 | d812 Non Private Use High Surrogate-D812 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d815 Non Private Use High Surrogate-D815 | d814 Non Private Use High Surrogate-D814 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d817 Non Private Use High Surrogate-D817 | d816 Non Private Use High Surrogate-D816 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d819 Non Private Use High Surrogate-D819 | d818 Non Private Use High Surrogate-D818 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d81b Non Private Use High Surrogate-D81B | d81a Non Private Use High Surrogate-D81A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d81d Non Private Use High Surrogate-D81D | d81c Non Private Use High Surrogate-D81C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d81f Non Private Use High Surrogate-D81F | d81e Non Private Use High Surrogate-D81E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d821 Non Private Use High Surrogate-D821 | d820 Non Private Use High Surrogate-D820 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d823 Non Private Use High Surrogate-D823 | d822 Non Private Use High Surrogate-D822 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d825 Non Private Use High Surrogate-D825 | d824 Non Private Use High Surrogate-D824 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d827 Non Private Use High Surrogate-D827 | d826 Non Private Use High Surrogate-D826 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d829 Non Private Use High Surrogate-D829 | d828 Non Private Use High Surrogate-D828 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d82b Non Private Use High Surrogate-D82B | d82a Non Private Use High Surrogate-D82A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d82d Non Private Use High Surrogate-D82D | d82c Non Private Use High Surrogate-D82C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d82f Non Private Use High Surrogate-D82F | d82e Non Private Use High Surrogate-D82E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d831 Non Private Use High Surrogate-D831 | d830 Non Private Use High Surrogate-D830 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d833 Non Private Use High Surrogate-D833 | d832 Non Private Use High Surrogate-D832 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d835 Non Private Use High Surrogate-D835 | d834 Non Private Use High Surrogate-D834 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d837 Non Private Use High Surrogate-D837 | d836 Non Private Use High Surrogate-D836 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d839 Non Private Use High Surrogate-D839 | d838 Non Private Use High Surrogate-D838 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d83b Non Private Use High Surrogate-D83B | d83a Non Private Use High Surrogate-D83A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d83d Non Private Use High Surrogate-D83D | d83c Non Private Use High Surrogate-D83C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d83f Non Private Use High Surrogate-D83F | d83e Non Private Use High Surrogate-D83E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d841 Non Private Use High Surrogate-D841 | d840 Non Private Use High Surrogate-D840 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d843 Non Private Use High Surrogate-D843 | d842 Non Private Use High Surrogate-D842 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d845 Non Private Use High Surrogate-D845 | d844 Non Private Use High Surrogate-D844 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d847 Non Private Use High Surrogate-D847 | d846 Non Private Use High Surrogate-D846 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d849 Non Private Use High Surrogate-D849 | d848 Non Private Use High Surrogate-D848 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d84b Non Private Use High Surrogate-D84B | d84a Non Private Use High Surrogate-D84A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d84d Non Private Use High Surrogate-D84D | d84c Non Private Use High Surrogate-D84C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d84f Non Private Use High Surrogate-D84F | d84e Non Private Use High Surrogate-D84E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d851 Non Private Use High Surrogate-D851 | d850 Non Private Use High Surrogate-D850 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d853 Non Private Use High Surrogate-D853 | d852 Non Private Use High Surrogate-D852 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d855 Non Private Use High Surrogate-D855 | d854 Non Private Use High Surrogate-D854 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d857 Non Private Use High Surrogate-D857 | d856 Non Private Use High Surrogate-D856 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d859 Non Private Use High Surrogate-D859 | d858 Non Private Use High Surrogate-D858 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d85b Non Private Use High Surrogate-D85B | d85a Non Private Use High Surrogate-D85A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d85d Non Private Use High Surrogate-D85D | d85c Non Private Use High Surrogate-D85C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d85f Non Private Use High Surrogate-D85F | d85e Non Private Use High Surrogate-D85E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d861 Non Private Use High Surrogate-D861 | d860 Non Private Use High Surrogate-D860 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d863 Non Private Use High Surrogate-D863 | d862 Non Private Use High Surrogate-D862 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d865 Non Private Use High Surrogate-D865 | d864 Non Private Use High Surrogate-D864 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d867 Non Private Use High Surrogate-D867 | d866 Non Private Use High Surrogate-D866 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d869 Non Private Use High Surrogate-D869 | d868 Non Private Use High Surrogate-D868 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d86b Non Private Use High Surrogate-D86B | d86a Non Private Use High Surrogate-D86A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d86d Non Private Use High Surrogate-D86D | d86c Non Private Use High Surrogate-D86C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d86f Non Private Use High Surrogate-D86F | d86e Non Private Use High Surrogate-D86E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d871 Non Private Use High Surrogate-D871 | d870 Non Private Use High Surrogate-D870 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d873 Non Private Use High Surrogate-D873 | d872 Non Private Use High Surrogate-D872 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d875 Non Private Use High Surrogate-D875 | d874 Non Private Use High Surrogate-D874 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d877 Non Private Use High Surrogate-D877 | d876 Non Private Use High Surrogate-D876 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d879 Non Private Use High Surrogate-D879 | d878 Non Private Use High Surrogate-D878 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d87b Non Private Use High Surrogate-D87B | d87a Non Private Use High Surrogate-D87A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d87d Non Private Use High Surrogate-D87D | d87c Non Private Use High Surrogate-D87C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d87f Non Private Use High Surrogate-D87F | d87e Non Private Use High Surrogate-D87E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d881 Non Private Use High Surrogate-D881 | d880 Non Private Use High Surrogate-D880 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d883 Non Private Use High Surrogate-D883 | d882 Non Private Use High Surrogate-D882 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d885 Non Private Use High Surrogate-D885 | d884 Non Private Use High Surrogate-D884 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d887 Non Private Use High Surrogate-D887 | d886 Non Private Use High Surrogate-D886 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d889 Non Private Use High Surrogate-D889 | d888 Non Private Use High Surrogate-D888 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d88b Non Private Use High Surrogate-D88B | d88a Non Private Use High Surrogate-D88A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d88d Non Private Use High Surrogate-D88D | d88c Non Private Use High Surrogate-D88C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d88f Non Private Use High Surrogate-D88F | d88e Non Private Use High Surrogate-D88E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d891 Non Private Use High Surrogate-D891 | d890 Non Private Use High Surrogate-D890 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d893 Non Private Use High Surrogate-D893 | d892 Non Private Use High Surrogate-D892 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d895 Non Private Use High Surrogate-D895 | d894 Non Private Use High Surrogate-D894 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d897 Non Private Use High Surrogate-D897 | d896 Non Private Use High Surrogate-D896 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d899 Non Private Use High Surrogate-D899 | d898 Non Private Use High Surrogate-D898 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d89b Non Private Use High Surrogate-D89B | d89a Non Private Use High Surrogate-D89A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d89d Non Private Use High Surrogate-D89D | d89c Non Private Use High Surrogate-D89C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d89f Non Private Use High Surrogate-D89F | d89e Non Private Use High Surrogate-D89E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8a1 Non Private Use High Surrogate-D8A1 | d8a0 Non Private Use High Surrogate-D8A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8a3 Non Private Use High Surrogate-D8A3 | d8a2 Non Private Use High Surrogate-D8A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8a5 Non Private Use High Surrogate-D8A5 | d8a4 Non Private Use High Surrogate-D8A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8a7 Non Private Use High Surrogate-D8A7 | d8a6 Non Private Use High Surrogate-D8A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8a9 Non Private Use High Surrogate-D8A9 | d8a8 Non Private Use High Surrogate-D8A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8ab Non Private Use High Surrogate-D8AB | d8aa Non Private Use High Surrogate-D8AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8ad Non Private Use High Surrogate-D8AD | d8ac Non Private Use High Surrogate-D8AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8af Non Private Use High Surrogate-D8AF | d8ae Non Private Use High Surrogate-D8AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8b1 Non Private Use High Surrogate-D8B1 | d8b0 Non Private Use High Surrogate-D8B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8b3 Non Private Use High Surrogate-D8B3 | d8b2 Non Private Use High Surrogate-D8B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8b5 Non Private Use High Surrogate-D8B5 | d8b4 Non Private Use High Surrogate-D8B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8b7 Non Private Use High Surrogate-D8B7 | d8b6 Non Private Use High Surrogate-D8B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8b9 Non Private Use High Surrogate-D8B9 | d8b8 Non Private Use High Surrogate-D8B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8bb Non Private Use High Surrogate-D8BB | d8ba Non Private Use High Surrogate-D8BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8bd Non Private Use High Surrogate-D8BD | d8bc Non Private Use High Surrogate-D8BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8bf Non Private Use High Surrogate-D8BF | d8be Non Private Use High Surrogate-D8BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8c1 Non Private Use High Surrogate-D8C1 | d8c0 Non Private Use High Surrogate-D8C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8c3 Non Private Use High Surrogate-D8C3 | d8c2 Non Private Use High Surrogate-D8C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8c5 Non Private Use High Surrogate-D8C5 | d8c4 Non Private Use High Surrogate-D8C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8c7 Non Private Use High Surrogate-D8C7 | d8c6 Non Private Use High Surrogate-D8C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8c9 Non Private Use High Surrogate-D8C9 | d8c8 Non Private Use High Surrogate-D8C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8cb Non Private Use High Surrogate-D8CB | d8ca Non Private Use High Surrogate-D8CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8cd Non Private Use High Surrogate-D8CD | d8cc Non Private Use High Surrogate-D8CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8cf Non Private Use High Surrogate-D8CF | d8ce Non Private Use High Surrogate-D8CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8d1 Non Private Use High Surrogate-D8D1 | d8d0 Non Private Use High Surrogate-D8D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8d3 Non Private Use High Surrogate-D8D3 | d8d2 Non Private Use High Surrogate-D8D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8d5 Non Private Use High Surrogate-D8D5 | d8d4 Non Private Use High Surrogate-D8D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8d7 Non Private Use High Surrogate-D8D7 | d8d6 Non Private Use High Surrogate-D8D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8d9 Non Private Use High Surrogate-D8D9 | d8d8 Non Private Use High Surrogate-D8D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8db Non Private Use High Surrogate-D8DB | d8da Non Private Use High Surrogate-D8DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8dd Non Private Use High Surrogate-D8DD | d8dc Non Private Use High Surrogate-D8DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8df Non Private Use High Surrogate-D8DF | d8de Non Private Use High Surrogate-D8DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8e1 Non Private Use High Surrogate-D8E1 | d8e0 Non Private Use High Surrogate-D8E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8e3 Non Private Use High Surrogate-D8E3 | d8e2 Non Private Use High Surrogate-D8E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8e5 Non Private Use High Surrogate-D8E5 | d8e4 Non Private Use High Surrogate-D8E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8e7 Non Private Use High Surrogate-D8E7 | d8e6 Non Private Use High Surrogate-D8E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8e9 Non Private Use High Surrogate-D8E9 | d8e8 Non Private Use High Surrogate-D8E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8eb Non Private Use High Surrogate-D8EB | d8ea Non Private Use High Surrogate-D8EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8ed Non Private Use High Surrogate-D8ED | d8ec Non Private Use High Surrogate-D8EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8ef Non Private Use High Surrogate-D8EF | d8ee Non Private Use High Surrogate-D8EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8f1 Non Private Use High Surrogate-D8F1 | d8f0 Non Private Use High Surrogate-D8F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8f3 Non Private Use High Surrogate-D8F3 | d8f2 Non Private Use High Surrogate-D8F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8f5 Non Private Use High Surrogate-D8F5 | d8f4 Non Private Use High Surrogate-D8F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8f7 Non Private Use High Surrogate-D8F7 | d8f6 Non Private Use High Surrogate-D8F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8f9 Non Private Use High Surrogate-D8F9 | d8f8 Non Private Use High Surrogate-D8F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8fb Non Private Use High Surrogate-D8FB | d8fa Non Private Use High Surrogate-D8FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8fd Non Private Use High Surrogate-D8FD | d8fc Non Private Use High Surrogate-D8FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8ff Non Private Use High Surrogate-D8FF | d8fe Non Private Use High Surrogate-D8FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d901 Non Private Use High Surrogate-D901 | d900 Non Private Use High Surrogate-D900 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d903 Non Private Use High Surrogate-D903 | d902 Non Private Use High Surrogate-D902 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d905 Non Private Use High Surrogate-D905 | d904 Non Private Use High Surrogate-D904 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d907 Non Private Use High Surrogate-D907 | d906 Non Private Use High Surrogate-D906 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d909 Non Private Use High Surrogate-D909 | d908 Non Private Use High Surrogate-D908 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d90b Non Private Use High Surrogate-D90B | d90a Non Private Use High Surrogate-D90A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d90d Non Private Use High Surrogate-D90D | d90c Non Private Use High Surrogate-D90C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d90f Non Private Use High Surrogate-D90F | d90e Non Private Use High Surrogate-D90E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d911 Non Private Use High Surrogate-D911 | d910 Non Private Use High Surrogate-D910 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d913 Non Private Use High Surrogate-D913 | d912 Non Private Use High Surrogate-D912 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d915 Non Private Use High Surrogate-D915 | d914 Non Private Use High Surrogate-D914 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d917 Non Private Use High Surrogate-D917 | d916 Non Private Use High Surrogate-D916 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d919 Non Private Use High Surrogate-D919 | d918 Non Private Use High Surrogate-D918 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d91b Non Private Use High Surrogate-D91B | d91a Non Private Use High Surrogate-D91A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d91d Non Private Use High Surrogate-D91D | d91c Non Private Use High Surrogate-D91C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d91f Non Private Use High Surrogate-D91F | d91e Non Private Use High Surrogate-D91E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d921 Non Private Use High Surrogate-D921 | d920 Non Private Use High Surrogate-D920 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d923 Non Private Use High Surrogate-D923 | d922 Non Private Use High Surrogate-D922 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d925 Non Private Use High Surrogate-D925 | d924 Non Private Use High Surrogate-D924 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d927 Non Private Use High Surrogate-D927 | d926 Non Private Use High Surrogate-D926 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d929 Non Private Use High Surrogate-D929 | d928 Non Private Use High Surrogate-D928 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d92b Non Private Use High Surrogate-D92B | d92a Non Private Use High Surrogate-D92A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d92d Non Private Use High Surrogate-D92D | d92c Non Private Use High Surrogate-D92C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d92f Non Private Use High Surrogate-D92F | d92e Non Private Use High Surrogate-D92E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d931 Non Private Use High Surrogate-D931 | d930 Non Private Use High Surrogate-D930 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d933 Non Private Use High Surrogate-D933 | d932 Non Private Use High Surrogate-D932 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d935 Non Private Use High Surrogate-D935 | d934 Non Private Use High Surrogate-D934 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d937 Non Private Use High Surrogate-D937 | d936 Non Private Use High Surrogate-D936 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d939 Non Private Use High Surrogate-D939 | d938 Non Private Use High Surrogate-D938 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d93b Non Private Use High Surrogate-D93B | d93a Non Private Use High Surrogate-D93A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d93d Non Private Use High Surrogate-D93D | d93c Non Private Use High Surrogate-D93C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d93f Non Private Use High Surrogate-D93F | d93e Non Private Use High Surrogate-D93E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d941 Non Private Use High Surrogate-D941 | d940 Non Private Use High Surrogate-D940 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d943 Non Private Use High Surrogate-D943 | d942 Non Private Use High Surrogate-D942 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d945 Non Private Use High Surrogate-D945 | d944 Non Private Use High Surrogate-D944 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d947 Non Private Use High Surrogate-D947 | d946 Non Private Use High Surrogate-D946 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d949 Non Private Use High Surrogate-D949 | d948 Non Private Use High Surrogate-D948 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d94b Non Private Use High Surrogate-D94B | d94a Non Private Use High Surrogate-D94A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d94d Non Private Use High Surrogate-D94D | d94c Non Private Use High Surrogate-D94C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d94f Non Private Use High Surrogate-D94F | d94e Non Private Use High Surrogate-D94E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d951 Non Private Use High Surrogate-D951 | d950 Non Private Use High Surrogate-D950 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d953 Non Private Use High Surrogate-D953 | d952 Non Private Use High Surrogate-D952 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d955 Non Private Use High Surrogate-D955 | d954 Non Private Use High Surrogate-D954 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d957 Non Private Use High Surrogate-D957 | d956 Non Private Use High Surrogate-D956 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d959 Non Private Use High Surrogate-D959 | d958 Non Private Use High Surrogate-D958 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d95b Non Private Use High Surrogate-D95B | d95a Non Private Use High Surrogate-D95A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d95d Non Private Use High Surrogate-D95D | d95c Non Private Use High Surrogate-D95C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d95f Non Private Use High Surrogate-D95F | d95e Non Private Use High Surrogate-D95E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d961 Non Private Use High Surrogate-D961 | d960 Non Private Use High Surrogate-D960 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d963 Non Private Use High Surrogate-D963 | d962 Non Private Use High Surrogate-D962 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d965 Non Private Use High Surrogate-D965 | d964 Non Private Use High Surrogate-D964 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d967 Non Private Use High Surrogate-D967 | d966 Non Private Use High Surrogate-D966 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d969 Non Private Use High Surrogate-D969 | d968 Non Private Use High Surrogate-D968 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d96b Non Private Use High Surrogate-D96B | d96a Non Private Use High Surrogate-D96A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d96d Non Private Use High Surrogate-D96D | d96c Non Private Use High Surrogate-D96C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d96f Non Private Use High Surrogate-D96F | d96e Non Private Use High Surrogate-D96E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d971 Non Private Use High Surrogate-D971 | d970 Non Private Use High Surrogate-D970 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d973 Non Private Use High Surrogate-D973 | d972 Non Private Use High Surrogate-D972 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d975 Non Private Use High Surrogate-D975 | d974 Non Private Use High Surrogate-D974 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d977 Non Private Use High Surrogate-D977 | d976 Non Private Use High Surrogate-D976 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d979 Non Private Use High Surrogate-D979 | d978 Non Private Use High Surrogate-D978 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d97b Non Private Use High Surrogate-D97B | d97a Non Private Use High Surrogate-D97A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d97d Non Private Use High Surrogate-D97D | d97c Non Private Use High Surrogate-D97C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d97f Non Private Use High Surrogate-D97F | d97e Non Private Use High Surrogate-D97E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d981 Non Private Use High Surrogate-D981 | d980 Non Private Use High Surrogate-D980 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d983 Non Private Use High Surrogate-D983 | d982 Non Private Use High Surrogate-D982 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d985 Non Private Use High Surrogate-D985 | d984 Non Private Use High Surrogate-D984 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d987 Non Private Use High Surrogate-D987 | d986 Non Private Use High Surrogate-D986 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d989 Non Private Use High Surrogate-D989 | d988 Non Private Use High Surrogate-D988 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d98b Non Private Use High Surrogate-D98B | d98a Non Private Use High Surrogate-D98A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d98d Non Private Use High Surrogate-D98D | d98c Non Private Use High Surrogate-D98C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d98f Non Private Use High Surrogate-D98F | d98e Non Private Use High Surrogate-D98E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d991 Non Private Use High Surrogate-D991 | d990 Non Private Use High Surrogate-D990 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d993 Non Private Use High Surrogate-D993 | d992 Non Private Use High Surrogate-D992 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d995 Non Private Use High Surrogate-D995 | d994 Non Private Use High Surrogate-D994 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d997 Non Private Use High Surrogate-D997 | d996 Non Private Use High Surrogate-D996 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d999 Non Private Use High Surrogate-D999 | d998 Non Private Use High Surrogate-D998 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d99b Non Private Use High Surrogate-D99B | d99a Non Private Use High Surrogate-D99A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d99d Non Private Use High Surrogate-D99D | d99c Non Private Use High Surrogate-D99C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d99f Non Private Use High Surrogate-D99F | d99e Non Private Use High Surrogate-D99E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9a1 Non Private Use High Surrogate-D9A1 | d9a0 Non Private Use High Surrogate-D9A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9a3 Non Private Use High Surrogate-D9A3 | d9a2 Non Private Use High Surrogate-D9A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9a5 Non Private Use High Surrogate-D9A5 | d9a4 Non Private Use High Surrogate-D9A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9a7 Non Private Use High Surrogate-D9A7 | d9a6 Non Private Use High Surrogate-D9A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9a9 Non Private Use High Surrogate-D9A9 | d9a8 Non Private Use High Surrogate-D9A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9ab Non Private Use High Surrogate-D9AB | d9aa Non Private Use High Surrogate-D9AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9ad Non Private Use High Surrogate-D9AD | d9ac Non Private Use High Surrogate-D9AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9af Non Private Use High Surrogate-D9AF | d9ae Non Private Use High Surrogate-D9AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9b1 Non Private Use High Surrogate-D9B1 | d9b0 Non Private Use High Surrogate-D9B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9b3 Non Private Use High Surrogate-D9B3 | d9b2 Non Private Use High Surrogate-D9B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9b5 Non Private Use High Surrogate-D9B5 | d9b4 Non Private Use High Surrogate-D9B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9b7 Non Private Use High Surrogate-D9B7 | d9b6 Non Private Use High Surrogate-D9B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9b9 Non Private Use High Surrogate-D9B9 | d9b8 Non Private Use High Surrogate-D9B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9bb Non Private Use High Surrogate-D9BB | d9ba Non Private Use High Surrogate-D9BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9bd Non Private Use High Surrogate-D9BD | d9bc Non Private Use High Surrogate-D9BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9bf Non Private Use High Surrogate-D9BF | d9be Non Private Use High Surrogate-D9BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9c1 Non Private Use High Surrogate-D9C1 | d9c0 Non Private Use High Surrogate-D9C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9c3 Non Private Use High Surrogate-D9C3 | d9c2 Non Private Use High Surrogate-D9C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9c5 Non Private Use High Surrogate-D9C5 | d9c4 Non Private Use High Surrogate-D9C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9c7 Non Private Use High Surrogate-D9C7 | d9c6 Non Private Use High Surrogate-D9C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9c9 Non Private Use High Surrogate-D9C9 | d9c8 Non Private Use High Surrogate-D9C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9cb Non Private Use High Surrogate-D9CB | d9ca Non Private Use High Surrogate-D9CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9cd Non Private Use High Surrogate-D9CD | d9cc Non Private Use High Surrogate-D9CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9cf Non Private Use High Surrogate-D9CF | d9ce Non Private Use High Surrogate-D9CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9d1 Non Private Use High Surrogate-D9D1 | d9d0 Non Private Use High Surrogate-D9D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9d3 Non Private Use High Surrogate-D9D3 | d9d2 Non Private Use High Surrogate-D9D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9d5 Non Private Use High Surrogate-D9D5 | d9d4 Non Private Use High Surrogate-D9D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9d7 Non Private Use High Surrogate-D9D7 | d9d6 Non Private Use High Surrogate-D9D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9d9 Non Private Use High Surrogate-D9D9 | d9d8 Non Private Use High Surrogate-D9D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9db Non Private Use High Surrogate-D9DB | d9da Non Private Use High Surrogate-D9DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9dd Non Private Use High Surrogate-D9DD | d9dc Non Private Use High Surrogate-D9DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9df Non Private Use High Surrogate-D9DF | d9de Non Private Use High Surrogate-D9DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9e1 Non Private Use High Surrogate-D9E1 | d9e0 Non Private Use High Surrogate-D9E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9e3 Non Private Use High Surrogate-D9E3 | d9e2 Non Private Use High Surrogate-D9E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9e5 Non Private Use High Surrogate-D9E5 | d9e4 Non Private Use High Surrogate-D9E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9e7 Non Private Use High Surrogate-D9E7 | d9e6 Non Private Use High Surrogate-D9E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9e9 Non Private Use High Surrogate-D9E9 | d9e8 Non Private Use High Surrogate-D9E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9eb Non Private Use High Surrogate-D9EB | d9ea Non Private Use High Surrogate-D9EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9ed Non Private Use High Surrogate-D9ED | d9ec Non Private Use High Surrogate-D9EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9ef Non Private Use High Surrogate-D9EF | d9ee Non Private Use High Surrogate-D9EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9f1 Non Private Use High Surrogate-D9F1 | d9f0 Non Private Use High Surrogate-D9F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9f3 Non Private Use High Surrogate-D9F3 | d9f2 Non Private Use High Surrogate-D9F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9f5 Non Private Use High Surrogate-D9F5 | d9f4 Non Private Use High Surrogate-D9F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9f7 Non Private Use High Surrogate-D9F7 | d9f6 Non Private Use High Surrogate-D9F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9f9 Non Private Use High Surrogate-D9F9 | d9f8 Non Private Use High Surrogate-D9F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9fb Non Private Use High Surrogate-D9FB | d9fa Non Private Use High Surrogate-D9FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9fd Non Private Use High Surrogate-D9FD | d9fc Non Private Use High Surrogate-D9FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9ff Non Private Use High Surrogate-D9FF | d9fe Non Private Use High Surrogate-D9FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da01 Non Private Use High Surrogate-DA01 | da00 Non Private Use High Surrogate-DA00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da03 Non Private Use High Surrogate-DA03 | da02 Non Private Use High Surrogate-DA02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da05 Non Private Use High Surrogate-DA05 | da04 Non Private Use High Surrogate-DA04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da07 Non Private Use High Surrogate-DA07 | da06 Non Private Use High Surrogate-DA06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da09 Non Private Use High Surrogate-DA09 | da08 Non Private Use High Surrogate-DA08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da0b Non Private Use High Surrogate-DA0B | da0a Non Private Use High Surrogate-DA0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da0d Non Private Use High Surrogate-DA0D | da0c Non Private Use High Surrogate-DA0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da0f Non Private Use High Surrogate-DA0F | da0e Non Private Use High Surrogate-DA0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da11 Non Private Use High Surrogate-DA11 | da10 Non Private Use High Surrogate-DA10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da13 Non Private Use High Surrogate-DA13 | da12 Non Private Use High Surrogate-DA12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da15 Non Private Use High Surrogate-DA15 | da14 Non Private Use High Surrogate-DA14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da17 Non Private Use High Surrogate-DA17 | da16 Non Private Use High Surrogate-DA16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da19 Non Private Use High Surrogate-DA19 | da18 Non Private Use High Surrogate-DA18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da1b Non Private Use High Surrogate-DA1B | da1a Non Private Use High Surrogate-DA1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da1d Non Private Use High Surrogate-DA1D | da1c Non Private Use High Surrogate-DA1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da1f Non Private Use High Surrogate-DA1F | da1e Non Private Use High Surrogate-DA1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da21 Non Private Use High Surrogate-DA21 | da20 Non Private Use High Surrogate-DA20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da23 Non Private Use High Surrogate-DA23 | da22 Non Private Use High Surrogate-DA22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da25 Non Private Use High Surrogate-DA25 | da24 Non Private Use High Surrogate-DA24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da27 Non Private Use High Surrogate-DA27 | da26 Non Private Use High Surrogate-DA26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da29 Non Private Use High Surrogate-DA29 | da28 Non Private Use High Surrogate-DA28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da2b Non Private Use High Surrogate-DA2B | da2a Non Private Use High Surrogate-DA2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da2d Non Private Use High Surrogate-DA2D | da2c Non Private Use High Surrogate-DA2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da2f Non Private Use High Surrogate-DA2F | da2e Non Private Use High Surrogate-DA2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da31 Non Private Use High Surrogate-DA31 | da30 Non Private Use High Surrogate-DA30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da33 Non Private Use High Surrogate-DA33 | da32 Non Private Use High Surrogate-DA32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da35 Non Private Use High Surrogate-DA35 | da34 Non Private Use High Surrogate-DA34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da37 Non Private Use High Surrogate-DA37 | da36 Non Private Use High Surrogate-DA36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da39 Non Private Use High Surrogate-DA39 | da38 Non Private Use High Surrogate-DA38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da3b Non Private Use High Surrogate-DA3B | da3a Non Private Use High Surrogate-DA3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da3d Non Private Use High Surrogate-DA3D | da3c Non Private Use High Surrogate-DA3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da3f Non Private Use High Surrogate-DA3F | da3e Non Private Use High Surrogate-DA3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da41 Non Private Use High Surrogate-DA41 | da40 Non Private Use High Surrogate-DA40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da43 Non Private Use High Surrogate-DA43 | da42 Non Private Use High Surrogate-DA42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da45 Non Private Use High Surrogate-DA45 | da44 Non Private Use High Surrogate-DA44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da47 Non Private Use High Surrogate-DA47 | da46 Non Private Use High Surrogate-DA46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da49 Non Private Use High Surrogate-DA49 | da48 Non Private Use High Surrogate-DA48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da4b Non Private Use High Surrogate-DA4B | da4a Non Private Use High Surrogate-DA4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da4d Non Private Use High Surrogate-DA4D | da4c Non Private Use High Surrogate-DA4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da4f Non Private Use High Surrogate-DA4F | da4e Non Private Use High Surrogate-DA4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da51 Non Private Use High Surrogate-DA51 | da50 Non Private Use High Surrogate-DA50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da53 Non Private Use High Surrogate-DA53 | da52 Non Private Use High Surrogate-DA52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da55 Non Private Use High Surrogate-DA55 | da54 Non Private Use High Surrogate-DA54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da57 Non Private Use High Surrogate-DA57 | da56 Non Private Use High Surrogate-DA56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da59 Non Private Use High Surrogate-DA59 | da58 Non Private Use High Surrogate-DA58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da5b Non Private Use High Surrogate-DA5B | da5a Non Private Use High Surrogate-DA5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da5d Non Private Use High Surrogate-DA5D | da5c Non Private Use High Surrogate-DA5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da5f Non Private Use High Surrogate-DA5F | da5e Non Private Use High Surrogate-DA5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da61 Non Private Use High Surrogate-DA61 | da60 Non Private Use High Surrogate-DA60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da63 Non Private Use High Surrogate-DA63 | da62 Non Private Use High Surrogate-DA62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da65 Non Private Use High Surrogate-DA65 | da64 Non Private Use High Surrogate-DA64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da67 Non Private Use High Surrogate-DA67 | da66 Non Private Use High Surrogate-DA66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da69 Non Private Use High Surrogate-DA69 | da68 Non Private Use High Surrogate-DA68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da6b Non Private Use High Surrogate-DA6B | da6a Non Private Use High Surrogate-DA6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da6d Non Private Use High Surrogate-DA6D | da6c Non Private Use High Surrogate-DA6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da6f Non Private Use High Surrogate-DA6F | da6e Non Private Use High Surrogate-DA6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da71 Non Private Use High Surrogate-DA71 | da70 Non Private Use High Surrogate-DA70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da73 Non Private Use High Surrogate-DA73 | da72 Non Private Use High Surrogate-DA72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da75 Non Private Use High Surrogate-DA75 | da74 Non Private Use High Surrogate-DA74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da77 Non Private Use High Surrogate-DA77 | da76 Non Private Use High Surrogate-DA76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da79 Non Private Use High Surrogate-DA79 | da78 Non Private Use High Surrogate-DA78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da7b Non Private Use High Surrogate-DA7B | da7a Non Private Use High Surrogate-DA7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da7d Non Private Use High Surrogate-DA7D | da7c Non Private Use High Surrogate-DA7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da7f Non Private Use High Surrogate-DA7F | da7e Non Private Use High Surrogate-DA7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da81 Non Private Use High Surrogate-DA81 | da80 Non Private Use High Surrogate-DA80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da83 Non Private Use High Surrogate-DA83 | da82 Non Private Use High Surrogate-DA82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da85 Non Private Use High Surrogate-DA85 | da84 Non Private Use High Surrogate-DA84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da87 Non Private Use High Surrogate-DA87 | da86 Non Private Use High Surrogate-DA86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da89 Non Private Use High Surrogate-DA89 | da88 Non Private Use High Surrogate-DA88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da8b Non Private Use High Surrogate-DA8B | da8a Non Private Use High Surrogate-DA8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da8d Non Private Use High Surrogate-DA8D | da8c Non Private Use High Surrogate-DA8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da8f Non Private Use High Surrogate-DA8F | da8e Non Private Use High Surrogate-DA8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da91 Non Private Use High Surrogate-DA91 | da90 Non Private Use High Surrogate-DA90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da93 Non Private Use High Surrogate-DA93 | da92 Non Private Use High Surrogate-DA92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da95 Non Private Use High Surrogate-DA95 | da94 Non Private Use High Surrogate-DA94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da97 Non Private Use High Surrogate-DA97 | da96 Non Private Use High Surrogate-DA96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da99 Non Private Use High Surrogate-DA99 | da98 Non Private Use High Surrogate-DA98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da9b Non Private Use High Surrogate-DA9B | da9a Non Private Use High Surrogate-DA9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da9d Non Private Use High Surrogate-DA9D | da9c Non Private Use High Surrogate-DA9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da9f Non Private Use High Surrogate-DA9F | da9e Non Private Use High Surrogate-DA9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daa1 Non Private Use High Surrogate-DAA1 | daa0 Non Private Use High Surrogate-DAA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daa3 Non Private Use High Surrogate-DAA3 | daa2 Non Private Use High Surrogate-DAA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daa5 Non Private Use High Surrogate-DAA5 | daa4 Non Private Use High Surrogate-DAA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daa7 Non Private Use High Surrogate-DAA7 | daa6 Non Private Use High Surrogate-DAA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daa9 Non Private Use High Surrogate-DAA9 | daa8 Non Private Use High Surrogate-DAA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daab Non Private Use High Surrogate-DAAB | daaa Non Private Use High Surrogate-DAAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daad Non Private Use High Surrogate-DAAD | daac Non Private Use High Surrogate-DAAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daaf Non Private Use High Surrogate-DAAF | daae Non Private Use High Surrogate-DAAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dab1 Non Private Use High Surrogate-DAB1 | dab0 Non Private Use High Surrogate-DAB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dab3 Non Private Use High Surrogate-DAB3 | dab2 Non Private Use High Surrogate-DAB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dab5 Non Private Use High Surrogate-DAB5 | dab4 Non Private Use High Surrogate-DAB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dab7 Non Private Use High Surrogate-DAB7 | dab6 Non Private Use High Surrogate-DAB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dab9 Non Private Use High Surrogate-DAB9 | dab8 Non Private Use High Surrogate-DAB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dabb Non Private Use High Surrogate-DABB | daba Non Private Use High Surrogate-DABA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dabd Non Private Use High Surrogate-DABD | dabc Non Private Use High Surrogate-DABC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dabf Non Private Use High Surrogate-DABF | dabe Non Private Use High Surrogate-DABE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dac1 Non Private Use High Surrogate-DAC1 | dac0 Non Private Use High Surrogate-DAC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dac3 Non Private Use High Surrogate-DAC3 | dac2 Non Private Use High Surrogate-DAC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dac5 Non Private Use High Surrogate-DAC5 | dac4 Non Private Use High Surrogate-DAC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dac7 Non Private Use High Surrogate-DAC7 | dac6 Non Private Use High Surrogate-DAC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dac9 Non Private Use High Surrogate-DAC9 | dac8 Non Private Use High Surrogate-DAC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dacb Non Private Use High Surrogate-DACB | daca Non Private Use High Surrogate-DACA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dacd Non Private Use High Surrogate-DACD | dacc Non Private Use High Surrogate-DACC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dacf Non Private Use High Surrogate-DACF | dace Non Private Use High Surrogate-DACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dad1 Non Private Use High Surrogate-DAD1 | dad0 Non Private Use High Surrogate-DAD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dad3 Non Private Use High Surrogate-DAD3 | dad2 Non Private Use High Surrogate-DAD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dad5 Non Private Use High Surrogate-DAD5 | dad4 Non Private Use High Surrogate-DAD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dad7 Non Private Use High Surrogate-DAD7 | dad6 Non Private Use High Surrogate-DAD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dad9 Non Private Use High Surrogate-DAD9 | dad8 Non Private Use High Surrogate-DAD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dadb Non Private Use High Surrogate-DADB | dada Non Private Use High Surrogate-DADA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dadd Non Private Use High Surrogate-DADD | dadc Non Private Use High Surrogate-DADC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dadf Non Private Use High Surrogate-DADF | dade Non Private Use High Surrogate-DADE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dae1 Non Private Use High Surrogate-DAE1 | dae0 Non Private Use High Surrogate-DAE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dae3 Non Private Use High Surrogate-DAE3 | dae2 Non Private Use High Surrogate-DAE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dae5 Non Private Use High Surrogate-DAE5 | dae4 Non Private Use High Surrogate-DAE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dae7 Non Private Use High Surrogate-DAE7 | dae6 Non Private Use High Surrogate-DAE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dae9 Non Private Use High Surrogate-DAE9 | dae8 Non Private Use High Surrogate-DAE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daeb Non Private Use High Surrogate-DAEB | daea Non Private Use High Surrogate-DAEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daed Non Private Use High Surrogate-DAED | daec Non Private Use High Surrogate-DAEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daef Non Private Use High Surrogate-DAEF | daee Non Private Use High Surrogate-DAEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daf1 Non Private Use High Surrogate-DAF1 | daf0 Non Private Use High Surrogate-DAF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daf3 Non Private Use High Surrogate-DAF3 | daf2 Non Private Use High Surrogate-DAF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daf5 Non Private Use High Surrogate-DAF5 | daf4 Non Private Use High Surrogate-DAF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daf7 Non Private Use High Surrogate-DAF7 | daf6 Non Private Use High Surrogate-DAF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daf9 Non Private Use High Surrogate-DAF9 | daf8 Non Private Use High Surrogate-DAF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dafb Non Private Use High Surrogate-DAFB | dafa Non Private Use High Surrogate-DAFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dafd Non Private Use High Surrogate-DAFD | dafc Non Private Use High Surrogate-DAFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daff Non Private Use High Surrogate-DAFF | dafe Non Private Use High Surrogate-DAFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db01 Non Private Use High Surrogate-DB01 | db00 Non Private Use High Surrogate-DB00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db03 Non Private Use High Surrogate-DB03 | db02 Non Private Use High Surrogate-DB02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db05 Non Private Use High Surrogate-DB05 | db04 Non Private Use High Surrogate-DB04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db07 Non Private Use High Surrogate-DB07 | db06 Non Private Use High Surrogate-DB06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db09 Non Private Use High Surrogate-DB09 | db08 Non Private Use High Surrogate-DB08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db0b Non Private Use High Surrogate-DB0B | db0a Non Private Use High Surrogate-DB0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db0d Non Private Use High Surrogate-DB0D | db0c Non Private Use High Surrogate-DB0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db0f Non Private Use High Surrogate-DB0F | db0e Non Private Use High Surrogate-DB0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db11 Non Private Use High Surrogate-DB11 | db10 Non Private Use High Surrogate-DB10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db13 Non Private Use High Surrogate-DB13 | db12 Non Private Use High Surrogate-DB12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db15 Non Private Use High Surrogate-DB15 | db14 Non Private Use High Surrogate-DB14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db17 Non Private Use High Surrogate-DB17 | db16 Non Private Use High Surrogate-DB16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db19 Non Private Use High Surrogate-DB19 | db18 Non Private Use High Surrogate-DB18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db1b Non Private Use High Surrogate-DB1B | db1a Non Private Use High Surrogate-DB1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db1d Non Private Use High Surrogate-DB1D | db1c Non Private Use High Surrogate-DB1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db1f Non Private Use High Surrogate-DB1F | db1e Non Private Use High Surrogate-DB1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db21 Non Private Use High Surrogate-DB21 | db20 Non Private Use High Surrogate-DB20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db23 Non Private Use High Surrogate-DB23 | db22 Non Private Use High Surrogate-DB22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db25 Non Private Use High Surrogate-DB25 | db24 Non Private Use High Surrogate-DB24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db27 Non Private Use High Surrogate-DB27 | db26 Non Private Use High Surrogate-DB26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db29 Non Private Use High Surrogate-DB29 | db28 Non Private Use High Surrogate-DB28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db2b Non Private Use High Surrogate-DB2B | db2a Non Private Use High Surrogate-DB2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db2d Non Private Use High Surrogate-DB2D | db2c Non Private Use High Surrogate-DB2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db2f Non Private Use High Surrogate-DB2F | db2e Non Private Use High Surrogate-DB2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db31 Non Private Use High Surrogate-DB31 | db30 Non Private Use High Surrogate-DB30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db33 Non Private Use High Surrogate-DB33 | db32 Non Private Use High Surrogate-DB32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db35 Non Private Use High Surrogate-DB35 | db34 Non Private Use High Surrogate-DB34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db37 Non Private Use High Surrogate-DB37 | db36 Non Private Use High Surrogate-DB36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db39 Non Private Use High Surrogate-DB39 | db38 Non Private Use High Surrogate-DB38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db3b Non Private Use High Surrogate-DB3B | db3a Non Private Use High Surrogate-DB3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db3d Non Private Use High Surrogate-DB3D | db3c Non Private Use High Surrogate-DB3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db3f Non Private Use High Surrogate-DB3F | db3e Non Private Use High Surrogate-DB3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db41 Non Private Use High Surrogate-DB41 | db40 Non Private Use High Surrogate-DB40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db43 Non Private Use High Surrogate-DB43 | db42 Non Private Use High Surrogate-DB42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db45 Non Private Use High Surrogate-DB45 | db44 Non Private Use High Surrogate-DB44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db47 Non Private Use High Surrogate-DB47 | db46 Non Private Use High Surrogate-DB46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db49 Non Private Use High Surrogate-DB49 | db48 Non Private Use High Surrogate-DB48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db4b Non Private Use High Surrogate-DB4B | db4a Non Private Use High Surrogate-DB4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db4d Non Private Use High Surrogate-DB4D | db4c Non Private Use High Surrogate-DB4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db4f Non Private Use High Surrogate-DB4F | db4e Non Private Use High Surrogate-DB4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db51 Non Private Use High Surrogate-DB51 | db50 Non Private Use High Surrogate-DB50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db53 Non Private Use High Surrogate-DB53 | db52 Non Private Use High Surrogate-DB52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db55 Non Private Use High Surrogate-DB55 | db54 Non Private Use High Surrogate-DB54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db57 Non Private Use High Surrogate-DB57 | db56 Non Private Use High Surrogate-DB56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db59 Non Private Use High Surrogate-DB59 | db58 Non Private Use High Surrogate-DB58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db5b Non Private Use High Surrogate-DB5B | db5a Non Private Use High Surrogate-DB5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db5d Non Private Use High Surrogate-DB5D | db5c Non Private Use High Surrogate-DB5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db5f Non Private Use High Surrogate-DB5F | db5e Non Private Use High Surrogate-DB5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db61 Non Private Use High Surrogate-DB61 | db60 Non Private Use High Surrogate-DB60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db63 Non Private Use High Surrogate-DB63 | db62 Non Private Use High Surrogate-DB62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db65 Non Private Use High Surrogate-DB65 | db64 Non Private Use High Surrogate-DB64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db67 Non Private Use High Surrogate-DB67 | db66 Non Private Use High Surrogate-DB66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db69 Non Private Use High Surrogate-DB69 | db68 Non Private Use High Surrogate-DB68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db6b Non Private Use High Surrogate-DB6B | db6a Non Private Use High Surrogate-DB6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db6d Non Private Use High Surrogate-DB6D | db6c Non Private Use High Surrogate-DB6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db6f Non Private Use High Surrogate-DB6F | db6e Non Private Use High Surrogate-DB6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db71 Non Private Use High Surrogate-DB71 | db70 Non Private Use High Surrogate-DB70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db73 Non Private Use High Surrogate-DB73 | db72 Non Private Use High Surrogate-DB72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db75 Non Private Use High Surrogate-DB75 | db74 Non Private Use High Surrogate-DB74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db77 Non Private Use High Surrogate-DB77 | db76 Non Private Use High Surrogate-DB76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db79 Non Private Use High Surrogate-DB79 | db78 Non Private Use High Surrogate-DB78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db7b Non Private Use High Surrogate-DB7B | db7a Non Private Use High Surrogate-DB7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db7d Non Private Use High Surrogate-DB7D | db7c Non Private Use High Surrogate-DB7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db7f Non Private Use High Surrogate-DB7F | db7e Non Private Use High Surrogate-DB7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db81 Private Use High Surrogate-DB81 | db80 Private Use High Surrogate-DB80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db83 Private Use High Surrogate-DB83 | db82 Private Use High Surrogate-DB82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db85 Private Use High Surrogate-DB85 | db84 Private Use High Surrogate-DB84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db87 Private Use High Surrogate-DB87 | db86 Private Use High Surrogate-DB86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db89 Private Use High Surrogate-DB89 | db88 Private Use High Surrogate-DB88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db8b Private Use High Surrogate-DB8B | db8a Private Use High Surrogate-DB8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db8d Private Use High Surrogate-DB8D | db8c Private Use High Surrogate-DB8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db8f Private Use High Surrogate-DB8F | db8e Private Use High Surrogate-DB8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db91 Private Use High Surrogate-DB91 | db90 Private Use High Surrogate-DB90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db93 Private Use High Surrogate-DB93 | db92 Private Use High Surrogate-DB92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db95 Private Use High Surrogate-DB95 | db94 Private Use High Surrogate-DB94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db97 Private Use High Surrogate-DB97 | db96 Private Use High Surrogate-DB96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db99 Private Use High Surrogate-DB99 | db98 Private Use High Surrogate-DB98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db9b Private Use High Surrogate-DB9B | db9a Private Use High Surrogate-DB9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db9d Private Use High Surrogate-DB9D | db9c Private Use High Surrogate-DB9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db9f Private Use High Surrogate-DB9F | db9e Private Use High Surrogate-DB9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dba1 Private Use High Surrogate-DBA1 | dba0 Private Use High Surrogate-DBA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dba3 Private Use High Surrogate-DBA3 | dba2 Private Use High Surrogate-DBA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dba5 Private Use High Surrogate-DBA5 | dba4 Private Use High Surrogate-DBA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dba7 Private Use High Surrogate-DBA7 | dba6 Private Use High Surrogate-DBA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dba9 Private Use High Surrogate-DBA9 | dba8 Private Use High Surrogate-DBA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbab Private Use High Surrogate-DBAB | dbaa Private Use High Surrogate-DBAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbad Private Use High Surrogate-DBAD | dbac Private Use High Surrogate-DBAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbaf Private Use High Surrogate-DBAF | dbae Private Use High Surrogate-DBAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbb1 Private Use High Surrogate-DBB1 | dbb0 Private Use High Surrogate-DBB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbb3 Private Use High Surrogate-DBB3 | dbb2 Private Use High Surrogate-DBB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbb5 Private Use High Surrogate-DBB5 | dbb4 Private Use High Surrogate-DBB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbb7 Private Use High Surrogate-DBB7 | dbb6 Private Use High Surrogate-DBB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbb9 Private Use High Surrogate-DBB9 | dbb8 Private Use High Surrogate-DBB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbbb Private Use High Surrogate-DBBB | dbba Private Use High Surrogate-DBBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbbd Private Use High Surrogate-DBBD | dbbc Private Use High Surrogate-DBBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbbf Private Use High Surrogate-DBBF | dbbe Private Use High Surrogate-DBBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbc1 Private Use High Surrogate-DBC1 | dbc0 Private Use High Surrogate-DBC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbc3 Private Use High Surrogate-DBC3 | dbc2 Private Use High Surrogate-DBC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbc5 Private Use High Surrogate-DBC5 | dbc4 Private Use High Surrogate-DBC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbc7 Private Use High Surrogate-DBC7 | dbc6 Private Use High Surrogate-DBC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbc9 Private Use High Surrogate-DBC9 | dbc8 Private Use High Surrogate-DBC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbcb Private Use High Surrogate-DBCB | dbca Private Use High Surrogate-DBCA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbcd Private Use High Surrogate-DBCD | dbcc Private Use High Surrogate-DBCC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbcf Private Use High Surrogate-DBCF | dbce Private Use High Surrogate-DBCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbd1 Private Use High Surrogate-DBD1 | dbd0 Private Use High Surrogate-DBD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbd3 Private Use High Surrogate-DBD3 | dbd2 Private Use High Surrogate-DBD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbd5 Private Use High Surrogate-DBD5 | dbd4 Private Use High Surrogate-DBD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbd7 Private Use High Surrogate-DBD7 | dbd6 Private Use High Surrogate-DBD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbd9 Private Use High Surrogate-DBD9 | dbd8 Private Use High Surrogate-DBD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbdb Private Use High Surrogate-DBDB | dbda Private Use High Surrogate-DBDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbdd Private Use High Surrogate-DBDD | dbdc Private Use High Surrogate-DBDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbdf Private Use High Surrogate-DBDF | dbde Private Use High Surrogate-DBDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbe1 Private Use High Surrogate-DBE1 | dbe0 Private Use High Surrogate-DBE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbe3 Private Use High Surrogate-DBE3 | dbe2 Private Use High Surrogate-DBE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbe5 Private Use High Surrogate-DBE5 | dbe4 Private Use High Surrogate-DBE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbe7 Private Use High Surrogate-DBE7 | dbe6 Private Use High Surrogate-DBE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbe9 Private Use High Surrogate-DBE9 | dbe8 Private Use High Surrogate-DBE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbeb Private Use High Surrogate-DBEB | dbea Private Use High Surrogate-DBEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbed Private Use High Surrogate-DBED | dbec Private Use High Surrogate-DBEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbef Private Use High Surrogate-DBEF | dbee Private Use High Surrogate-DBEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbf1 Private Use High Surrogate-DBF1 | dbf0 Private Use High Surrogate-DBF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbf3 Private Use High Surrogate-DBF3 | dbf2 Private Use High Surrogate-DBF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbf5 Private Use High Surrogate-DBF5 | dbf4 Private Use High Surrogate-DBF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbf7 Private Use High Surrogate-DBF7 | dbf6 Private Use High Surrogate-DBF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbf9 Private Use High Surrogate-DBF9 | dbf8 Private Use High Surrogate-DBF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbfb Private Use High Surrogate-DBFB | dbfa Private Use High Surrogate-DBFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbfd Private Use High Surrogate-DBFD | dbfc Private Use High Surrogate-DBFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbff Private Use High Surrogate-DBFF | dbfe Private Use High Surrogate-DBFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc01 Low Surrogate-DC01 | dc00 Low Surrogate-DC00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc03 Low Surrogate-DC03 | dc02 Low Surrogate-DC02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc05 Low Surrogate-DC05 | dc04 Low Surrogate-DC04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc07 Low Surrogate-DC07 | dc06 Low Surrogate-DC06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc09 Low Surrogate-DC09 | dc08 Low Surrogate-DC08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc0b Low Surrogate-DC0B | dc0a Low Surrogate-DC0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc0d Low Surrogate-DC0D | dc0c Low Surrogate-DC0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc0f Low Surrogate-DC0F | dc0e Low Surrogate-DC0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc11 Low Surrogate-DC11 | dc10 Low Surrogate-DC10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc13 Low Surrogate-DC13 | dc12 Low Surrogate-DC12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc15 Low Surrogate-DC15 | dc14 Low Surrogate-DC14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc17 Low Surrogate-DC17 | dc16 Low Surrogate-DC16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc19 Low Surrogate-DC19 | dc18 Low Surrogate-DC18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc1b Low Surrogate-DC1B | dc1a Low Surrogate-DC1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc1d Low Surrogate-DC1D | dc1c Low Surrogate-DC1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc1f Low Surrogate-DC1F | dc1e Low Surrogate-DC1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc21 Low Surrogate-DC21 | dc20 Low Surrogate-DC20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc23 Low Surrogate-DC23 | dc22 Low Surrogate-DC22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc25 Low Surrogate-DC25 | dc24 Low Surrogate-DC24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc27 Low Surrogate-DC27 | dc26 Low Surrogate-DC26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc29 Low Surrogate-DC29 | dc28 Low Surrogate-DC28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc2b Low Surrogate-DC2B | dc2a Low Surrogate-DC2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc2d Low Surrogate-DC2D | dc2c Low Surrogate-DC2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc2f Low Surrogate-DC2F | dc2e Low Surrogate-DC2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc31 Low Surrogate-DC31 | dc30 Low Surrogate-DC30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc33 Low Surrogate-DC33 | dc32 Low Surrogate-DC32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc35 Low Surrogate-DC35 | dc34 Low Surrogate-DC34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc37 Low Surrogate-DC37 | dc36 Low Surrogate-DC36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc39 Low Surrogate-DC39 | dc38 Low Surrogate-DC38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc3b Low Surrogate-DC3B | dc3a Low Surrogate-DC3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc3d Low Surrogate-DC3D | dc3c Low Surrogate-DC3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc3f Low Surrogate-DC3F | dc3e Low Surrogate-DC3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc41 Low Surrogate-DC41 | dc40 Low Surrogate-DC40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc43 Low Surrogate-DC43 | dc42 Low Surrogate-DC42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc45 Low Surrogate-DC45 | dc44 Low Surrogate-DC44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc47 Low Surrogate-DC47 | dc46 Low Surrogate-DC46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc49 Low Surrogate-DC49 | dc48 Low Surrogate-DC48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc4b Low Surrogate-DC4B | dc4a Low Surrogate-DC4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc4d Low Surrogate-DC4D | dc4c Low Surrogate-DC4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc4f Low Surrogate-DC4F | dc4e Low Surrogate-DC4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc51 Low Surrogate-DC51 | dc50 Low Surrogate-DC50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc53 Low Surrogate-DC53 | dc52 Low Surrogate-DC52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc55 Low Surrogate-DC55 | dc54 Low Surrogate-DC54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc57 Low Surrogate-DC57 | dc56 Low Surrogate-DC56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc59 Low Surrogate-DC59 | dc58 Low Surrogate-DC58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc5b Low Surrogate-DC5B | dc5a Low Surrogate-DC5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc5d Low Surrogate-DC5D | dc5c Low Surrogate-DC5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc5f Low Surrogate-DC5F | dc5e Low Surrogate-DC5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc61 Low Surrogate-DC61 | dc60 Low Surrogate-DC60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc63 Low Surrogate-DC63 | dc62 Low Surrogate-DC62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc65 Low Surrogate-DC65 | dc64 Low Surrogate-DC64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc67 Low Surrogate-DC67 | dc66 Low Surrogate-DC66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc69 Low Surrogate-DC69 | dc68 Low Surrogate-DC68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc6b Low Surrogate-DC6B | dc6a Low Surrogate-DC6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc6d Low Surrogate-DC6D | dc6c Low Surrogate-DC6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc6f Low Surrogate-DC6F | dc6e Low Surrogate-DC6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc71 Low Surrogate-DC71 | dc70 Low Surrogate-DC70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc73 Low Surrogate-DC73 | dc72 Low Surrogate-DC72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc75 Low Surrogate-DC75 | dc74 Low Surrogate-DC74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc77 Low Surrogate-DC77 | dc76 Low Surrogate-DC76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc79 Low Surrogate-DC79 | dc78 Low Surrogate-DC78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc7b Low Surrogate-DC7B | dc7a Low Surrogate-DC7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc7d Low Surrogate-DC7D | dc7c Low Surrogate-DC7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc7f Low Surrogate-DC7F | dc7e Low Surrogate-DC7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc81 Low Surrogate-DC81 | dc80 Low Surrogate-DC80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc83 Low Surrogate-DC83 | dc82 Low Surrogate-DC82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc85 Low Surrogate-DC85 | dc84 Low Surrogate-DC84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc87 Low Surrogate-DC87 | dc86 Low Surrogate-DC86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc89 Low Surrogate-DC89 | dc88 Low Surrogate-DC88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc8b Low Surrogate-DC8B | dc8a Low Surrogate-DC8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc8d Low Surrogate-DC8D | dc8c Low Surrogate-DC8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc8f Low Surrogate-DC8F | dc8e Low Surrogate-DC8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc91 Low Surrogate-DC91 | dc90 Low Surrogate-DC90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc93 Low Surrogate-DC93 | dc92 Low Surrogate-DC92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc95 Low Surrogate-DC95 | dc94 Low Surrogate-DC94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc97 Low Surrogate-DC97 | dc96 Low Surrogate-DC96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc99 Low Surrogate-DC99 | dc98 Low Surrogate-DC98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc9b Low Surrogate-DC9B | dc9a Low Surrogate-DC9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc9d Low Surrogate-DC9D | dc9c Low Surrogate-DC9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc9f Low Surrogate-DC9F | dc9e Low Surrogate-DC9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dca1 Low Surrogate-DCA1 | dca0 Low Surrogate-DCA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dca3 Low Surrogate-DCA3 | dca2 Low Surrogate-DCA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dca5 Low Surrogate-DCA5 | dca4 Low Surrogate-DCA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dca7 Low Surrogate-DCA7 | dca6 Low Surrogate-DCA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dca9 Low Surrogate-DCA9 | dca8 Low Surrogate-DCA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcab Low Surrogate-DCAB | dcaa Low Surrogate-DCAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcad Low Surrogate-DCAD | dcac Low Surrogate-DCAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcaf Low Surrogate-DCAF | dcae Low Surrogate-DCAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcb1 Low Surrogate-DCB1 | dcb0 Low Surrogate-DCB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcb3 Low Surrogate-DCB3 | dcb2 Low Surrogate-DCB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcb5 Low Surrogate-DCB5 | dcb4 Low Surrogate-DCB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcb7 Low Surrogate-DCB7 | dcb6 Low Surrogate-DCB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcb9 Low Surrogate-DCB9 | dcb8 Low Surrogate-DCB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcbb Low Surrogate-DCBB | dcba Low Surrogate-DCBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcbd Low Surrogate-DCBD | dcbc Low Surrogate-DCBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcbf Low Surrogate-DCBF | dcbe Low Surrogate-DCBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcc1 Low Surrogate-DCC1 | dcc0 Low Surrogate-DCC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcc3 Low Surrogate-DCC3 | dcc2 Low Surrogate-DCC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcc5 Low Surrogate-DCC5 | dcc4 Low Surrogate-DCC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcc7 Low Surrogate-DCC7 | dcc6 Low Surrogate-DCC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcc9 Low Surrogate-DCC9 | dcc8 Low Surrogate-DCC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dccb Low Surrogate-DCCB | dcca Low Surrogate-DCCA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dccd Low Surrogate-DCCD | dccc Low Surrogate-DCCC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dccf Low Surrogate-DCCF | dcce Low Surrogate-DCCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcd1 Low Surrogate-DCD1 | dcd0 Low Surrogate-DCD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcd3 Low Surrogate-DCD3 | dcd2 Low Surrogate-DCD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcd5 Low Surrogate-DCD5 | dcd4 Low Surrogate-DCD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcd7 Low Surrogate-DCD7 | dcd6 Low Surrogate-DCD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcd9 Low Surrogate-DCD9 | dcd8 Low Surrogate-DCD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcdb Low Surrogate-DCDB | dcda Low Surrogate-DCDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcdd Low Surrogate-DCDD | dcdc Low Surrogate-DCDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcdf Low Surrogate-DCDF | dcde Low Surrogate-DCDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dce1 Low Surrogate-DCE1 | dce0 Low Surrogate-DCE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dce3 Low Surrogate-DCE3 | dce2 Low Surrogate-DCE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dce5 Low Surrogate-DCE5 | dce4 Low Surrogate-DCE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dce7 Low Surrogate-DCE7 | dce6 Low Surrogate-DCE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dce9 Low Surrogate-DCE9 | dce8 Low Surrogate-DCE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dceb Low Surrogate-DCEB | dcea Low Surrogate-DCEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dced Low Surrogate-DCED | dcec Low Surrogate-DCEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcef Low Surrogate-DCEF | dcee Low Surrogate-DCEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcf1 Low Surrogate-DCF1 | dcf0 Low Surrogate-DCF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcf3 Low Surrogate-DCF3 | dcf2 Low Surrogate-DCF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcf5 Low Surrogate-DCF5 | dcf4 Low Surrogate-DCF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcf7 Low Surrogate-DCF7 | dcf6 Low Surrogate-DCF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcf9 Low Surrogate-DCF9 | dcf8 Low Surrogate-DCF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcfb Low Surrogate-DCFB | dcfa Low Surrogate-DCFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcfd Low Surrogate-DCFD | dcfc Low Surrogate-DCFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcff Low Surrogate-DCFF | dcfe Low Surrogate-DCFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd01 Low Surrogate-DD01 | dd00 Low Surrogate-DD00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd03 Low Surrogate-DD03 | dd02 Low Surrogate-DD02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd05 Low Surrogate-DD05 | dd04 Low Surrogate-DD04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd07 Low Surrogate-DD07 | dd06 Low Surrogate-DD06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd09 Low Surrogate-DD09 | dd08 Low Surrogate-DD08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd0b Low Surrogate-DD0B | dd0a Low Surrogate-DD0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd0d Low Surrogate-DD0D | dd0c Low Surrogate-DD0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd0f Low Surrogate-DD0F | dd0e Low Surrogate-DD0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd11 Low Surrogate-DD11 | dd10 Low Surrogate-DD10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd13 Low Surrogate-DD13 | dd12 Low Surrogate-DD12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd15 Low Surrogate-DD15 | dd14 Low Surrogate-DD14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd17 Low Surrogate-DD17 | dd16 Low Surrogate-DD16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd19 Low Surrogate-DD19 | dd18 Low Surrogate-DD18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd1b Low Surrogate-DD1B | dd1a Low Surrogate-DD1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd1d Low Surrogate-DD1D | dd1c Low Surrogate-DD1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd1f Low Surrogate-DD1F | dd1e Low Surrogate-DD1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd21 Low Surrogate-DD21 | dd20 Low Surrogate-DD20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd23 Low Surrogate-DD23 | dd22 Low Surrogate-DD22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd25 Low Surrogate-DD25 | dd24 Low Surrogate-DD24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd27 Low Surrogate-DD27 | dd26 Low Surrogate-DD26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd29 Low Surrogate-DD29 | dd28 Low Surrogate-DD28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd2b Low Surrogate-DD2B | dd2a Low Surrogate-DD2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd2d Low Surrogate-DD2D | dd2c Low Surrogate-DD2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd2f Low Surrogate-DD2F | dd2e Low Surrogate-DD2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd31 Low Surrogate-DD31 | dd30 Low Surrogate-DD30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd33 Low Surrogate-DD33 | dd32 Low Surrogate-DD32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd35 Low Surrogate-DD35 | dd34 Low Surrogate-DD34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd37 Low Surrogate-DD37 | dd36 Low Surrogate-DD36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd39 Low Surrogate-DD39 | dd38 Low Surrogate-DD38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd3b Low Surrogate-DD3B | dd3a Low Surrogate-DD3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd3d Low Surrogate-DD3D | dd3c Low Surrogate-DD3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd3f Low Surrogate-DD3F | dd3e Low Surrogate-DD3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd41 Low Surrogate-DD41 | dd40 Low Surrogate-DD40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd43 Low Surrogate-DD43 | dd42 Low Surrogate-DD42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd45 Low Surrogate-DD45 | dd44 Low Surrogate-DD44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd47 Low Surrogate-DD47 | dd46 Low Surrogate-DD46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd49 Low Surrogate-DD49 | dd48 Low Surrogate-DD48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd4b Low Surrogate-DD4B | dd4a Low Surrogate-DD4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd4d Low Surrogate-DD4D | dd4c Low Surrogate-DD4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd4f Low Surrogate-DD4F | dd4e Low Surrogate-DD4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd51 Low Surrogate-DD51 | dd50 Low Surrogate-DD50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd53 Low Surrogate-DD53 | dd52 Low Surrogate-DD52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd55 Low Surrogate-DD55 | dd54 Low Surrogate-DD54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd57 Low Surrogate-DD57 | dd56 Low Surrogate-DD56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd59 Low Surrogate-DD59 | dd58 Low Surrogate-DD58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd5b Low Surrogate-DD5B | dd5a Low Surrogate-DD5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd5d Low Surrogate-DD5D | dd5c Low Surrogate-DD5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd5f Low Surrogate-DD5F | dd5e Low Surrogate-DD5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd61 Low Surrogate-DD61 | dd60 Low Surrogate-DD60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd63 Low Surrogate-DD63 | dd62 Low Surrogate-DD62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd65 Low Surrogate-DD65 | dd64 Low Surrogate-DD64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd67 Low Surrogate-DD67 | dd66 Low Surrogate-DD66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd69 Low Surrogate-DD69 | dd68 Low Surrogate-DD68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd6b Low Surrogate-DD6B | dd6a Low Surrogate-DD6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd6d Low Surrogate-DD6D | dd6c Low Surrogate-DD6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd6f Low Surrogate-DD6F | dd6e Low Surrogate-DD6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd71 Low Surrogate-DD71 | dd70 Low Surrogate-DD70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd73 Low Surrogate-DD73 | dd72 Low Surrogate-DD72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd75 Low Surrogate-DD75 | dd74 Low Surrogate-DD74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd77 Low Surrogate-DD77 | dd76 Low Surrogate-DD76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd79 Low Surrogate-DD79 | dd78 Low Surrogate-DD78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd7b Low Surrogate-DD7B | dd7a Low Surrogate-DD7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd7d Low Surrogate-DD7D | dd7c Low Surrogate-DD7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd7f Low Surrogate-DD7F | dd7e Low Surrogate-DD7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd81 Low Surrogate-DD81 | dd80 Low Surrogate-DD80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd83 Low Surrogate-DD83 | dd82 Low Surrogate-DD82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd85 Low Surrogate-DD85 | dd84 Low Surrogate-DD84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd87 Low Surrogate-DD87 | dd86 Low Surrogate-DD86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd89 Low Surrogate-DD89 | dd88 Low Surrogate-DD88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd8b Low Surrogate-DD8B | dd8a Low Surrogate-DD8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd8d Low Surrogate-DD8D | dd8c Low Surrogate-DD8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd8f Low Surrogate-DD8F | dd8e Low Surrogate-DD8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd91 Low Surrogate-DD91 | dd90 Low Surrogate-DD90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd93 Low Surrogate-DD93 | dd92 Low Surrogate-DD92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd95 Low Surrogate-DD95 | dd94 Low Surrogate-DD94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd97 Low Surrogate-DD97 | dd96 Low Surrogate-DD96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd99 Low Surrogate-DD99 | dd98 Low Surrogate-DD98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd9b Low Surrogate-DD9B | dd9a Low Surrogate-DD9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd9d Low Surrogate-DD9D | dd9c Low Surrogate-DD9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd9f Low Surrogate-DD9F | dd9e Low Surrogate-DD9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dda1 Low Surrogate-DDA1 | dda0 Low Surrogate-DDA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dda3 Low Surrogate-DDA3 | dda2 Low Surrogate-DDA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dda5 Low Surrogate-DDA5 | dda4 Low Surrogate-DDA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dda7 Low Surrogate-DDA7 | dda6 Low Surrogate-DDA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dda9 Low Surrogate-DDA9 | dda8 Low Surrogate-DDA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddab Low Surrogate-DDAB | ddaa Low Surrogate-DDAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddad Low Surrogate-DDAD | ddac Low Surrogate-DDAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddaf Low Surrogate-DDAF | ddae Low Surrogate-DDAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddb1 Low Surrogate-DDB1 | ddb0 Low Surrogate-DDB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddb3 Low Surrogate-DDB3 | ddb2 Low Surrogate-DDB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddb5 Low Surrogate-DDB5 | ddb4 Low Surrogate-DDB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddb7 Low Surrogate-DDB7 | ddb6 Low Surrogate-DDB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddb9 Low Surrogate-DDB9 | ddb8 Low Surrogate-DDB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddbb Low Surrogate-DDBB | ddba Low Surrogate-DDBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddbd Low Surrogate-DDBD | ddbc Low Surrogate-DDBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddbf Low Surrogate-DDBF | ddbe Low Surrogate-DDBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddc1 Low Surrogate-DDC1 | ddc0 Low Surrogate-DDC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddc3 Low Surrogate-DDC3 | ddc2 Low Surrogate-DDC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddc5 Low Surrogate-DDC5 | ddc4 Low Surrogate-DDC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddc7 Low Surrogate-DDC7 | ddc6 Low Surrogate-DDC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddc9 Low Surrogate-DDC9 | ddc8 Low Surrogate-DDC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddcb Low Surrogate-DDCB | ddca Low Surrogate-DDCA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddcd Low Surrogate-DDCD | ddcc Low Surrogate-DDCC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddcf Low Surrogate-DDCF | ddce Low Surrogate-DDCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddd1 Low Surrogate-DDD1 | ddd0 Low Surrogate-DDD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddd3 Low Surrogate-DDD3 | ddd2 Low Surrogate-DDD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddd5 Low Surrogate-DDD5 | ddd4 Low Surrogate-DDD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddd7 Low Surrogate-DDD7 | ddd6 Low Surrogate-DDD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddd9 Low Surrogate-DDD9 | ddd8 Low Surrogate-DDD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dddb Low Surrogate-DDDB | ddda Low Surrogate-DDDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dddd Low Surrogate-DDDD | dddc Low Surrogate-DDDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dddf Low Surrogate-DDDF | ddde Low Surrogate-DDDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dde1 Low Surrogate-DDE1 | dde0 Low Surrogate-DDE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dde3 Low Surrogate-DDE3 | dde2 Low Surrogate-DDE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dde5 Low Surrogate-DDE5 | dde4 Low Surrogate-DDE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dde7 Low Surrogate-DDE7 | dde6 Low Surrogate-DDE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dde9 Low Surrogate-DDE9 | dde8 Low Surrogate-DDE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddeb Low Surrogate-DDEB | ddea Low Surrogate-DDEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dded Low Surrogate-DDED | ddec Low Surrogate-DDEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddef Low Surrogate-DDEF | ddee Low Surrogate-DDEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddf1 Low Surrogate-DDF1 | ddf0 Low Surrogate-DDF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddf3 Low Surrogate-DDF3 | ddf2 Low Surrogate-DDF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddf5 Low Surrogate-DDF5 | ddf4 Low Surrogate-DDF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddf7 Low Surrogate-DDF7 | ddf6 Low Surrogate-DDF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddf9 Low Surrogate-DDF9 | ddf8 Low Surrogate-DDF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddfb Low Surrogate-DDFB | ddfa Low Surrogate-DDFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddfd Low Surrogate-DDFD | ddfc Low Surrogate-DDFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddff Low Surrogate-DDFF | ddfe Low Surrogate-DDFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de01 Low Surrogate-DE01 | de00 Low Surrogate-DE00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de03 Low Surrogate-DE03 | de02 Low Surrogate-DE02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de05 Low Surrogate-DE05 | de04 Low Surrogate-DE04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de07 Low Surrogate-DE07 | de06 Low Surrogate-DE06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de09 Low Surrogate-DE09 | de08 Low Surrogate-DE08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de0b Low Surrogate-DE0B | de0a Low Surrogate-DE0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de0d Low Surrogate-DE0D | de0c Low Surrogate-DE0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de0f Low Surrogate-DE0F | de0e Low Surrogate-DE0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de11 Low Surrogate-DE11 | de10 Low Surrogate-DE10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de13 Low Surrogate-DE13 | de12 Low Surrogate-DE12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de15 Low Surrogate-DE15 | de14 Low Surrogate-DE14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de17 Low Surrogate-DE17 | de16 Low Surrogate-DE16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de19 Low Surrogate-DE19 | de18 Low Surrogate-DE18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de1b Low Surrogate-DE1B | de1a Low Surrogate-DE1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de1d Low Surrogate-DE1D | de1c Low Surrogate-DE1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de1f Low Surrogate-DE1F | de1e Low Surrogate-DE1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de21 Low Surrogate-DE21 | de20 Low Surrogate-DE20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de23 Low Surrogate-DE23 | de22 Low Surrogate-DE22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de25 Low Surrogate-DE25 | de24 Low Surrogate-DE24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de27 Low Surrogate-DE27 | de26 Low Surrogate-DE26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de29 Low Surrogate-DE29 | de28 Low Surrogate-DE28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de2b Low Surrogate-DE2B | de2a Low Surrogate-DE2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de2d Low Surrogate-DE2D | de2c Low Surrogate-DE2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de2f Low Surrogate-DE2F | de2e Low Surrogate-DE2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de31 Low Surrogate-DE31 | de30 Low Surrogate-DE30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de33 Low Surrogate-DE33 | de32 Low Surrogate-DE32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de35 Low Surrogate-DE35 | de34 Low Surrogate-DE34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de37 Low Surrogate-DE37 | de36 Low Surrogate-DE36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de39 Low Surrogate-DE39 | de38 Low Surrogate-DE38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de3b Low Surrogate-DE3B | de3a Low Surrogate-DE3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de3d Low Surrogate-DE3D | de3c Low Surrogate-DE3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de3f Low Surrogate-DE3F | de3e Low Surrogate-DE3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de41 Low Surrogate-DE41 | de40 Low Surrogate-DE40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de43 Low Surrogate-DE43 | de42 Low Surrogate-DE42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de45 Low Surrogate-DE45 | de44 Low Surrogate-DE44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de47 Low Surrogate-DE47 | de46 Low Surrogate-DE46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de49 Low Surrogate-DE49 | de48 Low Surrogate-DE48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de4b Low Surrogate-DE4B | de4a Low Surrogate-DE4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de4d Low Surrogate-DE4D | de4c Low Surrogate-DE4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de4f Low Surrogate-DE4F | de4e Low Surrogate-DE4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de51 Low Surrogate-DE51 | de50 Low Surrogate-DE50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de53 Low Surrogate-DE53 | de52 Low Surrogate-DE52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de55 Low Surrogate-DE55 | de54 Low Surrogate-DE54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de57 Low Surrogate-DE57 | de56 Low Surrogate-DE56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de59 Low Surrogate-DE59 | de58 Low Surrogate-DE58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de5b Low Surrogate-DE5B | de5a Low Surrogate-DE5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de5d Low Surrogate-DE5D | de5c Low Surrogate-DE5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de5f Low Surrogate-DE5F | de5e Low Surrogate-DE5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de61 Low Surrogate-DE61 | de60 Low Surrogate-DE60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de63 Low Surrogate-DE63 | de62 Low Surrogate-DE62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de65 Low Surrogate-DE65 | de64 Low Surrogate-DE64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de67 Low Surrogate-DE67 | de66 Low Surrogate-DE66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de69 Low Surrogate-DE69 | de68 Low Surrogate-DE68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de6b Low Surrogate-DE6B | de6a Low Surrogate-DE6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de6d Low Surrogate-DE6D | de6c Low Surrogate-DE6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de6f Low Surrogate-DE6F | de6e Low Surrogate-DE6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de71 Low Surrogate-DE71 | de70 Low Surrogate-DE70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de73 Low Surrogate-DE73 | de72 Low Surrogate-DE72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de75 Low Surrogate-DE75 | de74 Low Surrogate-DE74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de77 Low Surrogate-DE77 | de76 Low Surrogate-DE76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de79 Low Surrogate-DE79 | de78 Low Surrogate-DE78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de7b Low Surrogate-DE7B | de7a Low Surrogate-DE7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de7d Low Surrogate-DE7D | de7c Low Surrogate-DE7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de7f Low Surrogate-DE7F | de7e Low Surrogate-DE7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de81 Low Surrogate-DE81 | de80 Low Surrogate-DE80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de83 Low Surrogate-DE83 | de82 Low Surrogate-DE82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de85 Low Surrogate-DE85 | de84 Low Surrogate-DE84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de87 Low Surrogate-DE87 | de86 Low Surrogate-DE86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de89 Low Surrogate-DE89 | de88 Low Surrogate-DE88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de8b Low Surrogate-DE8B | de8a Low Surrogate-DE8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de8d Low Surrogate-DE8D | de8c Low Surrogate-DE8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de8f Low Surrogate-DE8F | de8e Low Surrogate-DE8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de91 Low Surrogate-DE91 | de90 Low Surrogate-DE90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de93 Low Surrogate-DE93 | de92 Low Surrogate-DE92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de95 Low Surrogate-DE95 | de94 Low Surrogate-DE94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de97 Low Surrogate-DE97 | de96 Low Surrogate-DE96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de99 Low Surrogate-DE99 | de98 Low Surrogate-DE98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de9b Low Surrogate-DE9B | de9a Low Surrogate-DE9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de9d Low Surrogate-DE9D | de9c Low Surrogate-DE9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de9f Low Surrogate-DE9F | de9e Low Surrogate-DE9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dea1 Low Surrogate-DEA1 | dea0 Low Surrogate-DEA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dea3 Low Surrogate-DEA3 | dea2 Low Surrogate-DEA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dea5 Low Surrogate-DEA5 | dea4 Low Surrogate-DEA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dea7 Low Surrogate-DEA7 | dea6 Low Surrogate-DEA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dea9 Low Surrogate-DEA9 | dea8 Low Surrogate-DEA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deab Low Surrogate-DEAB | deaa Low Surrogate-DEAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dead Low Surrogate-DEAD | deac Low Surrogate-DEAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deaf Low Surrogate-DEAF | deae Low Surrogate-DEAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deb1 Low Surrogate-DEB1 | deb0 Low Surrogate-DEB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deb3 Low Surrogate-DEB3 | deb2 Low Surrogate-DEB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deb5 Low Surrogate-DEB5 | deb4 Low Surrogate-DEB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deb7 Low Surrogate-DEB7 | deb6 Low Surrogate-DEB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deb9 Low Surrogate-DEB9 | deb8 Low Surrogate-DEB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* debb Low Surrogate-DEBB | deba Low Surrogate-DEBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* debd Low Surrogate-DEBD | debc Low Surrogate-DEBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* debf Low Surrogate-DEBF | debe Low Surrogate-DEBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dec1 Low Surrogate-DEC1 | dec0 Low Surrogate-DEC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dec3 Low Surrogate-DEC3 | dec2 Low Surrogate-DEC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dec5 Low Surrogate-DEC5 | dec4 Low Surrogate-DEC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dec7 Low Surrogate-DEC7 | dec6 Low Surrogate-DEC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dec9 Low Surrogate-DEC9 | dec8 Low Surrogate-DEC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* decb Low Surrogate-DECB | deca Low Surrogate-DECA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* decd Low Surrogate-DECD | decc Low Surrogate-DECC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* decf Low Surrogate-DECF | dece Low Surrogate-DECE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ded1 Low Surrogate-DED1 | ded0 Low Surrogate-DED0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ded3 Low Surrogate-DED3 | ded2 Low Surrogate-DED2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ded5 Low Surrogate-DED5 | ded4 Low Surrogate-DED4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ded7 Low Surrogate-DED7 | ded6 Low Surrogate-DED6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ded9 Low Surrogate-DED9 | ded8 Low Surrogate-DED8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dedb Low Surrogate-DEDB | deda Low Surrogate-DEDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dedd Low Surrogate-DEDD | dedc Low Surrogate-DEDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dedf Low Surrogate-DEDF | dede Low Surrogate-DEDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dee1 Low Surrogate-DEE1 | dee0 Low Surrogate-DEE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dee3 Low Surrogate-DEE3 | dee2 Low Surrogate-DEE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dee5 Low Surrogate-DEE5 | dee4 Low Surrogate-DEE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dee7 Low Surrogate-DEE7 | dee6 Low Surrogate-DEE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dee9 Low Surrogate-DEE9 | dee8 Low Surrogate-DEE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deeb Low Surrogate-DEEB | deea Low Surrogate-DEEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deed Low Surrogate-DEED | deec Low Surrogate-DEEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deef Low Surrogate-DEEF | deee Low Surrogate-DEEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* def1 Low Surrogate-DEF1 | def0 Low Surrogate-DEF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* def3 Low Surrogate-DEF3 | def2 Low Surrogate-DEF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* def5 Low Surrogate-DEF5 | def4 Low Surrogate-DEF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* def7 Low Surrogate-DEF7 | def6 Low Surrogate-DEF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* def9 Low Surrogate-DEF9 | def8 Low Surrogate-DEF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* defb Low Surrogate-DEFB | defa Low Surrogate-DEFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* defd Low Surrogate-DEFD | defc Low Surrogate-DEFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deff Low Surrogate-DEFF | defe Low Surrogate-DEFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df01 Low Surrogate-DF01 | df00 Low Surrogate-DF00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df03 Low Surrogate-DF03 | df02 Low Surrogate-DF02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df05 Low Surrogate-DF05 | df04 Low Surrogate-DF04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df07 Low Surrogate-DF07 | df06 Low Surrogate-DF06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df09 Low Surrogate-DF09 | df08 Low Surrogate-DF08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df0b Low Surrogate-DF0B | df0a Low Surrogate-DF0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df0d Low Surrogate-DF0D | df0c Low Surrogate-DF0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df0f Low Surrogate-DF0F | df0e Low Surrogate-DF0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df11 Low Surrogate-DF11 | df10 Low Surrogate-DF10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df13 Low Surrogate-DF13 | df12 Low Surrogate-DF12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df15 Low Surrogate-DF15 | df14 Low Surrogate-DF14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df17 Low Surrogate-DF17 | df16 Low Surrogate-DF16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df19 Low Surrogate-DF19 | df18 Low Surrogate-DF18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df1b Low Surrogate-DF1B | df1a Low Surrogate-DF1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df1d Low Surrogate-DF1D | df1c Low Surrogate-DF1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df1f Low Surrogate-DF1F | df1e Low Surrogate-DF1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df21 Low Surrogate-DF21 | df20 Low Surrogate-DF20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df23 Low Surrogate-DF23 | df22 Low Surrogate-DF22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df25 Low Surrogate-DF25 | df24 Low Surrogate-DF24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df27 Low Surrogate-DF27 | df26 Low Surrogate-DF26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df29 Low Surrogate-DF29 | df28 Low Surrogate-DF28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df2b Low Surrogate-DF2B | df2a Low Surrogate-DF2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df2d Low Surrogate-DF2D | df2c Low Surrogate-DF2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df2f Low Surrogate-DF2F | df2e Low Surrogate-DF2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df31 Low Surrogate-DF31 | df30 Low Surrogate-DF30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df33 Low Surrogate-DF33 | df32 Low Surrogate-DF32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df35 Low Surrogate-DF35 | df34 Low Surrogate-DF34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df37 Low Surrogate-DF37 | df36 Low Surrogate-DF36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df39 Low Surrogate-DF39 | df38 Low Surrogate-DF38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df3b Low Surrogate-DF3B | df3a Low Surrogate-DF3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df3d Low Surrogate-DF3D | df3c Low Surrogate-DF3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df3f Low Surrogate-DF3F | df3e Low Surrogate-DF3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df41 Low Surrogate-DF41 | df40 Low Surrogate-DF40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df43 Low Surrogate-DF43 | df42 Low Surrogate-DF42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df45 Low Surrogate-DF45 | df44 Low Surrogate-DF44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df47 Low Surrogate-DF47 | df46 Low Surrogate-DF46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df49 Low Surrogate-DF49 | df48 Low Surrogate-DF48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df4b Low Surrogate-DF4B | df4a Low Surrogate-DF4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df4d Low Surrogate-DF4D | df4c Low Surrogate-DF4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df4f Low Surrogate-DF4F | df4e Low Surrogate-DF4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df51 Low Surrogate-DF51 | df50 Low Surrogate-DF50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df53 Low Surrogate-DF53 | df52 Low Surrogate-DF52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df55 Low Surrogate-DF55 | df54 Low Surrogate-DF54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df57 Low Surrogate-DF57 | df56 Low Surrogate-DF56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df59 Low Surrogate-DF59 | df58 Low Surrogate-DF58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df5b Low Surrogate-DF5B | df5a Low Surrogate-DF5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df5d Low Surrogate-DF5D | df5c Low Surrogate-DF5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df5f Low Surrogate-DF5F | df5e Low Surrogate-DF5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df61 Low Surrogate-DF61 | df60 Low Surrogate-DF60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df63 Low Surrogate-DF63 | df62 Low Surrogate-DF62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df65 Low Surrogate-DF65 | df64 Low Surrogate-DF64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df67 Low Surrogate-DF67 | df66 Low Surrogate-DF66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df69 Low Surrogate-DF69 | df68 Low Surrogate-DF68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df6b Low Surrogate-DF6B | df6a Low Surrogate-DF6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df6d Low Surrogate-DF6D | df6c Low Surrogate-DF6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df6f Low Surrogate-DF6F | df6e Low Surrogate-DF6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df71 Low Surrogate-DF71 | df70 Low Surrogate-DF70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df73 Low Surrogate-DF73 | df72 Low Surrogate-DF72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df75 Low Surrogate-DF75 | df74 Low Surrogate-DF74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df77 Low Surrogate-DF77 | df76 Low Surrogate-DF76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df79 Low Surrogate-DF79 | df78 Low Surrogate-DF78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df7b Low Surrogate-DF7B | df7a Low Surrogate-DF7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df7d Low Surrogate-DF7D | df7c Low Surrogate-DF7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df7f Low Surrogate-DF7F | df7e Low Surrogate-DF7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df81 Low Surrogate-DF81 | df80 Low Surrogate-DF80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df83 Low Surrogate-DF83 | df82 Low Surrogate-DF82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df85 Low Surrogate-DF85 | df84 Low Surrogate-DF84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df87 Low Surrogate-DF87 | df86 Low Surrogate-DF86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df89 Low Surrogate-DF89 | df88 Low Surrogate-DF88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df8b Low Surrogate-DF8B | df8a Low Surrogate-DF8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df8d Low Surrogate-DF8D | df8c Low Surrogate-DF8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df8f Low Surrogate-DF8F | df8e Low Surrogate-DF8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df91 Low Surrogate-DF91 | df90 Low Surrogate-DF90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df93 Low Surrogate-DF93 | df92 Low Surrogate-DF92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df95 Low Surrogate-DF95 | df94 Low Surrogate-DF94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df97 Low Surrogate-DF97 | df96 Low Surrogate-DF96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df99 Low Surrogate-DF99 | df98 Low Surrogate-DF98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df9b Low Surrogate-DF9B | df9a Low Surrogate-DF9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df9d Low Surrogate-DF9D | df9c Low Surrogate-DF9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df9f Low Surrogate-DF9F | df9e Low Surrogate-DF9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfa1 Low Surrogate-DFA1 | dfa0 Low Surrogate-DFA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfa3 Low Surrogate-DFA3 | dfa2 Low Surrogate-DFA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfa5 Low Surrogate-DFA5 | dfa4 Low Surrogate-DFA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfa7 Low Surrogate-DFA7 | dfa6 Low Surrogate-DFA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfa9 Low Surrogate-DFA9 | dfa8 Low Surrogate-DFA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfab Low Surrogate-DFAB | dfaa Low Surrogate-DFAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfad Low Surrogate-DFAD | dfac Low Surrogate-DFAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfaf Low Surrogate-DFAF | dfae Low Surrogate-DFAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfb1 Low Surrogate-DFB1 | dfb0 Low Surrogate-DFB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfb3 Low Surrogate-DFB3 | dfb2 Low Surrogate-DFB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfb5 Low Surrogate-DFB5 | dfb4 Low Surrogate-DFB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfb7 Low Surrogate-DFB7 | dfb6 Low Surrogate-DFB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfb9 Low Surrogate-DFB9 | dfb8 Low Surrogate-DFB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfbb Low Surrogate-DFBB | dfba Low Surrogate-DFBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfbd Low Surrogate-DFBD | dfbc Low Surrogate-DFBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfbf Low Surrogate-DFBF | dfbe Low Surrogate-DFBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfc1 Low Surrogate-DFC1 | dfc0 Low Surrogate-DFC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfc3 Low Surrogate-DFC3 | dfc2 Low Surrogate-DFC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfc5 Low Surrogate-DFC5 | dfc4 Low Surrogate-DFC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfc7 Low Surrogate-DFC7 | dfc6 Low Surrogate-DFC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfc9 Low Surrogate-DFC9 | dfc8 Low Surrogate-DFC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfcb Low Surrogate-DFCB | dfca Low Surrogate-DFCA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfcd Low Surrogate-DFCD | dfcc Low Surrogate-DFCC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfcf Low Surrogate-DFCF | dfce Low Surrogate-DFCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfd1 Low Surrogate-DFD1 | dfd0 Low Surrogate-DFD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfd3 Low Surrogate-DFD3 | dfd2 Low Surrogate-DFD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfd5 Low Surrogate-DFD5 | dfd4 Low Surrogate-DFD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfd7 Low Surrogate-DFD7 | dfd6 Low Surrogate-DFD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfd9 Low Surrogate-DFD9 | dfd8 Low Surrogate-DFD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfdb Low Surrogate-DFDB | dfda Low Surrogate-DFDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfdd Low Surrogate-DFDD | dfdc Low Surrogate-DFDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfdf Low Surrogate-DFDF | dfde Low Surrogate-DFDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfe1 Low Surrogate-DFE1 | dfe0 Low Surrogate-DFE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfe3 Low Surrogate-DFE3 | dfe2 Low Surrogate-DFE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfe5 Low Surrogate-DFE5 | dfe4 Low Surrogate-DFE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfe7 Low Surrogate-DFE7 | dfe6 Low Surrogate-DFE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfe9 Low Surrogate-DFE9 | dfe8 Low Surrogate-DFE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfeb Low Surrogate-DFEB | dfea Low Surrogate-DFEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfed Low Surrogate-DFED | dfec Low Surrogate-DFEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfef Low Surrogate-DFEF | dfee Low Surrogate-DFEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dff1 Low Surrogate-DFF1 | dff0 Low Surrogate-DFF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dff3 Low Surrogate-DFF3 | dff2 Low Surrogate-DFF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dff5 Low Surrogate-DFF5 | dff4 Low Surrogate-DFF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dff7 Low Surrogate-DFF7 | dff6 Low Surrogate-DFF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dff9 Low Surrogate-DFF9 | dff8 Low Surrogate-DFF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dffb Low Surrogate-DFFB | dffa Low Surrogate-DFFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dffd Low Surrogate-DFFD | dffc Low Surrogate-DFFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfff Low Surrogate-DFFF | dffe Low Surrogate-DFFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e001 Private Use-E001 | e000 Private Use-E000 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e003 Private Use-E003 | e002 Private Use-E002 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e005 Private Use-E005 | e004 Private Use-E004 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e007 Private Use-E007 | e006 Private Use-E006 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e009 Private Use-E009 | e008 Private Use-E008 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e00b Private Use-E00B | e00a Private Use-E00A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e00d Private Use-E00D | e00c Private Use-E00C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e00f Private Use-E00F | e00e Private Use-E00E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e011 Private Use-E011 | e010 Private Use-E010 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e013 Private Use-E013 | e012 Private Use-E012 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e015 Private Use-E015 | e014 Private Use-E014 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e017 Private Use-E017 | e016 Private Use-E016 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e019 Private Use-E019 | e018 Private Use-E018 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e01b Private Use-E01B | e01a Private Use-E01A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e01d Private Use-E01D | e01c Private Use-E01C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e01f Private Use-E01F | e01e Private Use-E01E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e021 Private Use-E021 | e020 Private Use-E020 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e023 Private Use-E023 | e022 Private Use-E022 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e025 Private Use-E025 | e024 Private Use-E024 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e027 Private Use-E027 | e026 Private Use-E026 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e029 Private Use-E029 | e028 Private Use-E028 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e02b Private Use-E02B | e02a Private Use-E02A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e02d Private Use-E02D | e02c Private Use-E02C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e02f Private Use-E02F | e02e Private Use-E02E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e031 Private Use-E031 | e030 Private Use-E030 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e033 Private Use-E033 | e032 Private Use-E032 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e035 Private Use-E035 | e034 Private Use-E034 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e037 Private Use-E037 | e036 Private Use-E036 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e039 Private Use-E039 | e038 Private Use-E038 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e03b Private Use-E03B | e03a Private Use-E03A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e03d Private Use-E03D | e03c Private Use-E03C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e03f Private Use-E03F | e03e Private Use-E03E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e041 Private Use-E041 | e040 Private Use-E040 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e043 Private Use-E043 | e042 Private Use-E042 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e045 Private Use-E045 | e044 Private Use-E044 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e047 Private Use-E047 | e046 Private Use-E046 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e049 Private Use-E049 | e048 Private Use-E048 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e04b Private Use-E04B | e04a Private Use-E04A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e04d Private Use-E04D | e04c Private Use-E04C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e04f Private Use-E04F | e04e Private Use-E04E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e051 Private Use-E051 | e050 Private Use-E050 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e053 Private Use-E053 | e052 Private Use-E052 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e055 Private Use-E055 | e054 Private Use-E054 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e057 Private Use-E057 | e056 Private Use-E056 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e059 Private Use-E059 | e058 Private Use-E058 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e05b Private Use-E05B | e05a Private Use-E05A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e05d Private Use-E05D | e05c Private Use-E05C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e05f Private Use-E05F | e05e Private Use-E05E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e061 Private Use-E061 | e060 Private Use-E060 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e063 Private Use-E063 | e062 Private Use-E062 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e065 Private Use-E065 | e064 Private Use-E064 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e067 Private Use-E067 | e066 Private Use-E066 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e069 Private Use-E069 | e068 Private Use-E068 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e06b Private Use-E06B | e06a Private Use-E06A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e06d Private Use-E06D | e06c Private Use-E06C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e06f Private Use-E06F | e06e Private Use-E06E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e071 Private Use-E071 | e070 Private Use-E070 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e073 Private Use-E073 | e072 Private Use-E072 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e075 Private Use-E075 | e074 Private Use-E074 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e077 Private Use-E077 | e076 Private Use-E076 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e079 Private Use-E079 | e078 Private Use-E078 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e07b Private Use-E07B | e07a Private Use-E07A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e07d Private Use-E07D | e07c Private Use-E07C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e07f Private Use-E07F | e07e Private Use-E07E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e081 Private Use-E081 | e080 Private Use-E080 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e083 Private Use-E083 | e082 Private Use-E082 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e085 Private Use-E085 | e084 Private Use-E084 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e087 Private Use-E087 | e086 Private Use-E086 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e089 Private Use-E089 | e088 Private Use-E088 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e08b Private Use-E08B | e08a Private Use-E08A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e08d Private Use-E08D | e08c Private Use-E08C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e08f Private Use-E08F | e08e Private Use-E08E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e091 Private Use-E091 | e090 Private Use-E090 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e093 Private Use-E093 | e092 Private Use-E092 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e095 Private Use-E095 | e094 Private Use-E094 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e097 Private Use-E097 | e096 Private Use-E096 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e099 Private Use-E099 | e098 Private Use-E098 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e09b Private Use-E09B | e09a Private Use-E09A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e09d Private Use-E09D | e09c Private Use-E09C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e09f Private Use-E09F | e09e Private Use-E09E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0a1 Private Use-E0A1 | e0a0 Private Use-E0A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0a3 Private Use-E0A3 | e0a2 Private Use-E0A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0a5 Private Use-E0A5 | e0a4 Private Use-E0A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0a7 Private Use-E0A7 | e0a6 Private Use-E0A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0a9 Private Use-E0A9 | e0a8 Private Use-E0A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0ab Private Use-E0AB | e0aa Private Use-E0AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0ad Private Use-E0AD | e0ac Private Use-E0AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0af Private Use-E0AF | e0ae Private Use-E0AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0b1 Private Use-E0B1 | e0b0 Private Use-E0B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0b3 Private Use-E0B3 | e0b2 Private Use-E0B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0b5 Private Use-E0B5 | e0b4 Private Use-E0B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0b7 Private Use-E0B7 | e0b6 Private Use-E0B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0b9 Private Use-E0B9 | e0b8 Private Use-E0B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0bb Private Use-E0BB | e0ba Private Use-E0BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0bd Private Use-E0BD | e0bc Private Use-E0BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0bf Private Use-E0BF | e0be Private Use-E0BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0c1 Private Use-E0C1 | e0c0 Private Use-E0C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0c3 Private Use-E0C3 | e0c2 Private Use-E0C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0c5 Private Use-E0C5 | e0c4 Private Use-E0C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0c7 Private Use-E0C7 | e0c6 Private Use-E0C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0c9 Private Use-E0C9 | e0c8 Private Use-E0C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0cb Private Use-E0CB | e0ca Private Use-E0CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0cd Private Use-E0CD | e0cc Private Use-E0CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0cf Private Use-E0CF | e0ce Private Use-E0CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0d1 Private Use-E0D1 | e0d0 Private Use-E0D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0d3 Private Use-E0D3 | e0d2 Private Use-E0D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0d5 Private Use-E0D5 | e0d4 Private Use-E0D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0d7 Private Use-E0D7 | e0d6 Private Use-E0D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0d9 Private Use-E0D9 | e0d8 Private Use-E0D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0db Private Use-E0DB | e0da Private Use-E0DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0dd Private Use-E0DD | e0dc Private Use-E0DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0df Private Use-E0DF | e0de Private Use-E0DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0e1 Private Use-E0E1 | e0e0 Private Use-E0E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0e3 Private Use-E0E3 | e0e2 Private Use-E0E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0e5 Private Use-E0E5 | e0e4 Private Use-E0E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0e7 Private Use-E0E7 | e0e6 Private Use-E0E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0e9 Private Use-E0E9 | e0e8 Private Use-E0E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0eb Private Use-E0EB | e0ea Private Use-E0EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0ed Private Use-E0ED | e0ec Private Use-E0EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0ef Private Use-E0EF | e0ee Private Use-E0EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0f1 Private Use-E0F1 | e0f0 Private Use-E0F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0f3 Private Use-E0F3 | e0f2 Private Use-E0F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0f5 Private Use-E0F5 | e0f4 Private Use-E0F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0f7 Private Use-E0F7 | e0f6 Private Use-E0F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0f9 Private Use-E0F9 | e0f8 Private Use-E0F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0fb Private Use-E0FB | e0fa Private Use-E0FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0fd Private Use-E0FD | e0fc Private Use-E0FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0ff Private Use-E0FF | e0fe Private Use-E0FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e101 Private Use-E101 | e100 Private Use-E100 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e103 Private Use-E103 | e102 Private Use-E102 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e105 Private Use-E105 | e104 Private Use-E104 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e107 Private Use-E107 | e106 Private Use-E106 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e109 Private Use-E109 | e108 Private Use-E108 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e10b Private Use-E10B | e10a Private Use-E10A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e10d Private Use-E10D | e10c Private Use-E10C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e10f Private Use-E10F | e10e Private Use-E10E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e111 Private Use-E111 | e110 Private Use-E110 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e113 Private Use-E113 | e112 Private Use-E112 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e115 Private Use-E115 | e114 Private Use-E114 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e117 Private Use-E117 | e116 Private Use-E116 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e119 Private Use-E119 | e118 Private Use-E118 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e11b Private Use-E11B | e11a Private Use-E11A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e11d Private Use-E11D | e11c Private Use-E11C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e11f Private Use-E11F | e11e Private Use-E11E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e121 Private Use-E121 | e120 Private Use-E120 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e123 Private Use-E123 | e122 Private Use-E122 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e125 Private Use-E125 | e124 Private Use-E124 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e127 Private Use-E127 | e126 Private Use-E126 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e129 Private Use-E129 | e128 Private Use-E128 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e12b Private Use-E12B | e12a Private Use-E12A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e12d Private Use-E12D | e12c Private Use-E12C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e12f Private Use-E12F | e12e Private Use-E12E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e131 Private Use-E131 | e130 Private Use-E130 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e133 Private Use-E133 | e132 Private Use-E132 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e135 Private Use-E135 | e134 Private Use-E134 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e137 Private Use-E137 | e136 Private Use-E136 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e139 Private Use-E139 | e138 Private Use-E138 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e13b Private Use-E13B | e13a Private Use-E13A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e13d Private Use-E13D | e13c Private Use-E13C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e13f Private Use-E13F | e13e Private Use-E13E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e141 Private Use-E141 | e140 Private Use-E140 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e143 Private Use-E143 | e142 Private Use-E142 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e145 Private Use-E145 | e144 Private Use-E144 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e147 Private Use-E147 | e146 Private Use-E146 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e149 Private Use-E149 | e148 Private Use-E148 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e14b Private Use-E14B | e14a Private Use-E14A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e14d Private Use-E14D | e14c Private Use-E14C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e14f Private Use-E14F | e14e Private Use-E14E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e151 Private Use-E151 | e150 Private Use-E150 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e153 Private Use-E153 | e152 Private Use-E152 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e155 Private Use-E155 | e154 Private Use-E154 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e157 Private Use-E157 | e156 Private Use-E156 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e159 Private Use-E159 | e158 Private Use-E158 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e15b Private Use-E15B | e15a Private Use-E15A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e15d Private Use-E15D | e15c Private Use-E15C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e15f Private Use-E15F | e15e Private Use-E15E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e161 Private Use-E161 | e160 Private Use-E160 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e163 Private Use-E163 | e162 Private Use-E162 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e165 Private Use-E165 | e164 Private Use-E164 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e167 Private Use-E167 | e166 Private Use-E166 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e169 Private Use-E169 | e168 Private Use-E168 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e16b Private Use-E16B | e16a Private Use-E16A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e16d Private Use-E16D | e16c Private Use-E16C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e16f Private Use-E16F | e16e Private Use-E16E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e171 Private Use-E171 | e170 Private Use-E170 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e173 Private Use-E173 | e172 Private Use-E172 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e175 Private Use-E175 | e174 Private Use-E174 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e177 Private Use-E177 | e176 Private Use-E176 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e179 Private Use-E179 | e178 Private Use-E178 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e17b Private Use-E17B | e17a Private Use-E17A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e17d Private Use-E17D | e17c Private Use-E17C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e17f Private Use-E17F | e17e Private Use-E17E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e181 Private Use-E181 | e180 Private Use-E180 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e183 Private Use-E183 | e182 Private Use-E182 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e185 Private Use-E185 | e184 Private Use-E184 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e187 Private Use-E187 | e186 Private Use-E186 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e189 Private Use-E189 | e188 Private Use-E188 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e18b Private Use-E18B | e18a Private Use-E18A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e18d Private Use-E18D | e18c Private Use-E18C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e18f Private Use-E18F | e18e Private Use-E18E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e191 Private Use-E191 | e190 Private Use-E190 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e193 Private Use-E193 | e192 Private Use-E192 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e195 Private Use-E195 | e194 Private Use-E194 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e197 Private Use-E197 | e196 Private Use-E196 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e199 Private Use-E199 | e198 Private Use-E198 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e19b Private Use-E19B | e19a Private Use-E19A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e19d Private Use-E19D | e19c Private Use-E19C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e19f Private Use-E19F | e19e Private Use-E19E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1a1 Private Use-E1A1 | e1a0 Private Use-E1A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1a3 Private Use-E1A3 | e1a2 Private Use-E1A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1a5 Private Use-E1A5 | e1a4 Private Use-E1A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1a7 Private Use-E1A7 | e1a6 Private Use-E1A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1a9 Private Use-E1A9 | e1a8 Private Use-E1A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1ab Private Use-E1AB | e1aa Private Use-E1AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1ad Private Use-E1AD | e1ac Private Use-E1AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1af Private Use-E1AF | e1ae Private Use-E1AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1b1 Private Use-E1B1 | e1b0 Private Use-E1B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1b3 Private Use-E1B3 | e1b2 Private Use-E1B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1b5 Private Use-E1B5 | e1b4 Private Use-E1B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1b7 Private Use-E1B7 | e1b6 Private Use-E1B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1b9 Private Use-E1B9 | e1b8 Private Use-E1B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1bb Private Use-E1BB | e1ba Private Use-E1BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1bd Private Use-E1BD | e1bc Private Use-E1BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1bf Private Use-E1BF | e1be Private Use-E1BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1c1 Private Use-E1C1 | e1c0 Private Use-E1C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1c3 Private Use-E1C3 | e1c2 Private Use-E1C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1c5 Private Use-E1C5 | e1c4 Private Use-E1C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1c7 Private Use-E1C7 | e1c6 Private Use-E1C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1c9 Private Use-E1C9 | e1c8 Private Use-E1C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1cb Private Use-E1CB | e1ca Private Use-E1CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1cd Private Use-E1CD | e1cc Private Use-E1CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1cf Private Use-E1CF | e1ce Private Use-E1CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1d1 Private Use-E1D1 | e1d0 Private Use-E1D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1d3 Private Use-E1D3 | e1d2 Private Use-E1D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1d5 Private Use-E1D5 | e1d4 Private Use-E1D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1d7 Private Use-E1D7 | e1d6 Private Use-E1D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1d9 Private Use-E1D9 | e1d8 Private Use-E1D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1db Private Use-E1DB | e1da Private Use-E1DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1dd Private Use-E1DD | e1dc Private Use-E1DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1df Private Use-E1DF | e1de Private Use-E1DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1e1 Private Use-E1E1 | e1e0 Private Use-E1E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1e3 Private Use-E1E3 | e1e2 Private Use-E1E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1e5 Private Use-E1E5 | e1e4 Private Use-E1E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1e7 Private Use-E1E7 | e1e6 Private Use-E1E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1e9 Private Use-E1E9 | e1e8 Private Use-E1E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1eb Private Use-E1EB | e1ea Private Use-E1EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1ed Private Use-E1ED | e1ec Private Use-E1EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1ef Private Use-E1EF | e1ee Private Use-E1EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1f1 Private Use-E1F1 | e1f0 Private Use-E1F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1f3 Private Use-E1F3 | e1f2 Private Use-E1F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1f5 Private Use-E1F5 | e1f4 Private Use-E1F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1f7 Private Use-E1F7 | e1f6 Private Use-E1F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1f9 Private Use-E1F9 | e1f8 Private Use-E1F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1fb Private Use-E1FB | e1fa Private Use-E1FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1fd Private Use-E1FD | e1fc Private Use-E1FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1ff Private Use-E1FF | e1fe Private Use-E1FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e201 Private Use-E201 | e200 Private Use-E200 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e203 Private Use-E203 | e202 Private Use-E202 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e205 Private Use-E205 | e204 Private Use-E204 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e207 Private Use-E207 | e206 Private Use-E206 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e209 Private Use-E209 | e208 Private Use-E208 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e20b Private Use-E20B | e20a Private Use-E20A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e20d Private Use-E20D | e20c Private Use-E20C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e20f Private Use-E20F | e20e Private Use-E20E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e211 Private Use-E211 | e210 Private Use-E210 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e213 Private Use-E213 | e212 Private Use-E212 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e215 Private Use-E215 | e214 Private Use-E214 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e217 Private Use-E217 | e216 Private Use-E216 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e219 Private Use-E219 | e218 Private Use-E218 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e21b Private Use-E21B | e21a Private Use-E21A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e21d Private Use-E21D | e21c Private Use-E21C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e21f Private Use-E21F | e21e Private Use-E21E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e221 Private Use-E221 | e220 Private Use-E220 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e223 Private Use-E223 | e222 Private Use-E222 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e225 Private Use-E225 | e224 Private Use-E224 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e227 Private Use-E227 | e226 Private Use-E226 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e229 Private Use-E229 | e228 Private Use-E228 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e22b Private Use-E22B | e22a Private Use-E22A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e22d Private Use-E22D | e22c Private Use-E22C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e22f Private Use-E22F | e22e Private Use-E22E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e231 Private Use-E231 | e230 Private Use-E230 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e233 Private Use-E233 | e232 Private Use-E232 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e235 Private Use-E235 | e234 Private Use-E234 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e237 Private Use-E237 | e236 Private Use-E236 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e239 Private Use-E239 | e238 Private Use-E238 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e23b Private Use-E23B | e23a Private Use-E23A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e23d Private Use-E23D | e23c Private Use-E23C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e23f Private Use-E23F | e23e Private Use-E23E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e241 Private Use-E241 | e240 Private Use-E240 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e243 Private Use-E243 | e242 Private Use-E242 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e245 Private Use-E245 | e244 Private Use-E244 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e247 Private Use-E247 | e246 Private Use-E246 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e249 Private Use-E249 | e248 Private Use-E248 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e24b Private Use-E24B | e24a Private Use-E24A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e24d Private Use-E24D | e24c Private Use-E24C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e24f Private Use-E24F | e24e Private Use-E24E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e251 Private Use-E251 | e250 Private Use-E250 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e253 Private Use-E253 | e252 Private Use-E252 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e255 Private Use-E255 | e254 Private Use-E254 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e257 Private Use-E257 | e256 Private Use-E256 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e259 Private Use-E259 | e258 Private Use-E258 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e25b Private Use-E25B | e25a Private Use-E25A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e25d Private Use-E25D | e25c Private Use-E25C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e25f Private Use-E25F | e25e Private Use-E25E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e261 Private Use-E261 | e260 Private Use-E260 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e263 Private Use-E263 | e262 Private Use-E262 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e265 Private Use-E265 | e264 Private Use-E264 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e267 Private Use-E267 | e266 Private Use-E266 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e269 Private Use-E269 | e268 Private Use-E268 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e26b Private Use-E26B | e26a Private Use-E26A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e26d Private Use-E26D | e26c Private Use-E26C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e26f Private Use-E26F | e26e Private Use-E26E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e271 Private Use-E271 | e270 Private Use-E270 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e273 Private Use-E273 | e272 Private Use-E272 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e275 Private Use-E275 | e274 Private Use-E274 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e277 Private Use-E277 | e276 Private Use-E276 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e279 Private Use-E279 | e278 Private Use-E278 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e27b Private Use-E27B | e27a Private Use-E27A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e27d Private Use-E27D | e27c Private Use-E27C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e27f Private Use-E27F | e27e Private Use-E27E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e281 Private Use-E281 | e280 Private Use-E280 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e283 Private Use-E283 | e282 Private Use-E282 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e285 Private Use-E285 | e284 Private Use-E284 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e287 Private Use-E287 | e286 Private Use-E286 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e289 Private Use-E289 | e288 Private Use-E288 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e28b Private Use-E28B | e28a Private Use-E28A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e28d Private Use-E28D | e28c Private Use-E28C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e28f Private Use-E28F | e28e Private Use-E28E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e291 Private Use-E291 | e290 Private Use-E290 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e293 Private Use-E293 | e292 Private Use-E292 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e295 Private Use-E295 | e294 Private Use-E294 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e297 Private Use-E297 | e296 Private Use-E296 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e299 Private Use-E299 | e298 Private Use-E298 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e29b Private Use-E29B | e29a Private Use-E29A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e29d Private Use-E29D | e29c Private Use-E29C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e29f Private Use-E29F | e29e Private Use-E29E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2a1 Private Use-E2A1 | e2a0 Private Use-E2A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2a3 Private Use-E2A3 | e2a2 Private Use-E2A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2a5 Private Use-E2A5 | e2a4 Private Use-E2A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2a7 Private Use-E2A7 | e2a6 Private Use-E2A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2a9 Private Use-E2A9 | e2a8 Private Use-E2A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2ab Private Use-E2AB | e2aa Private Use-E2AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2ad Private Use-E2AD | e2ac Private Use-E2AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2af Private Use-E2AF | e2ae Private Use-E2AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2b1 Private Use-E2B1 | e2b0 Private Use-E2B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2b3 Private Use-E2B3 | e2b2 Private Use-E2B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2b5 Private Use-E2B5 | e2b4 Private Use-E2B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2b7 Private Use-E2B7 | e2b6 Private Use-E2B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2b9 Private Use-E2B9 | e2b8 Private Use-E2B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2bb Private Use-E2BB | e2ba Private Use-E2BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2bd Private Use-E2BD | e2bc Private Use-E2BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2bf Private Use-E2BF | e2be Private Use-E2BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2c1 Private Use-E2C1 | e2c0 Private Use-E2C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2c3 Private Use-E2C3 | e2c2 Private Use-E2C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2c5 Private Use-E2C5 | e2c4 Private Use-E2C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2c7 Private Use-E2C7 | e2c6 Private Use-E2C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2c9 Private Use-E2C9 | e2c8 Private Use-E2C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2cb Private Use-E2CB | e2ca Private Use-E2CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2cd Private Use-E2CD | e2cc Private Use-E2CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2cf Private Use-E2CF | e2ce Private Use-E2CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2d1 Private Use-E2D1 | e2d0 Private Use-E2D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2d3 Private Use-E2D3 | e2d2 Private Use-E2D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2d5 Private Use-E2D5 | e2d4 Private Use-E2D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2d7 Private Use-E2D7 | e2d6 Private Use-E2D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2d9 Private Use-E2D9 | e2d8 Private Use-E2D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2db Private Use-E2DB | e2da Private Use-E2DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2dd Private Use-E2DD | e2dc Private Use-E2DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2df Private Use-E2DF | e2de Private Use-E2DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2e1 Private Use-E2E1 | e2e0 Private Use-E2E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2e3 Private Use-E2E3 | e2e2 Private Use-E2E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2e5 Private Use-E2E5 | e2e4 Private Use-E2E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2e7 Private Use-E2E7 | e2e6 Private Use-E2E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2e9 Private Use-E2E9 | e2e8 Private Use-E2E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2eb Private Use-E2EB | e2ea Private Use-E2EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2ed Private Use-E2ED | e2ec Private Use-E2EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2ef Private Use-E2EF | e2ee Private Use-E2EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2f1 Private Use-E2F1 | e2f0 Private Use-E2F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2f3 Private Use-E2F3 | e2f2 Private Use-E2F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2f5 Private Use-E2F5 | e2f4 Private Use-E2F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2f7 Private Use-E2F7 | e2f6 Private Use-E2F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2f9 Private Use-E2F9 | e2f8 Private Use-E2F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2fb Private Use-E2FB | e2fa Private Use-E2FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2fd Private Use-E2FD | e2fc Private Use-E2FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2ff Private Use-E2FF | e2fe Private Use-E2FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e301 Private Use-E301 | e300 Private Use-E300 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e303 Private Use-E303 | e302 Private Use-E302 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e305 Private Use-E305 | e304 Private Use-E304 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e307 Private Use-E307 | e306 Private Use-E306 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e309 Private Use-E309 | e308 Private Use-E308 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e30b Private Use-E30B | e30a Private Use-E30A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e30d Private Use-E30D | e30c Private Use-E30C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e30f Private Use-E30F | e30e Private Use-E30E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e311 Private Use-E311 | e310 Private Use-E310 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e313 Private Use-E313 | e312 Private Use-E312 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e315 Private Use-E315 | e314 Private Use-E314 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e317 Private Use-E317 | e316 Private Use-E316 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e319 Private Use-E319 | e318 Private Use-E318 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e31b Private Use-E31B | e31a Private Use-E31A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e31d Private Use-E31D | e31c Private Use-E31C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e31f Private Use-E31F | e31e Private Use-E31E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e321 Private Use-E321 | e320 Private Use-E320 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e323 Private Use-E323 | e322 Private Use-E322 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e325 Private Use-E325 | e324 Private Use-E324 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e327 Private Use-E327 | e326 Private Use-E326 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e329 Private Use-E329 | e328 Private Use-E328 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e32b Private Use-E32B | e32a Private Use-E32A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e32d Private Use-E32D | e32c Private Use-E32C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e32f Private Use-E32F | e32e Private Use-E32E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e331 Private Use-E331 | e330 Private Use-E330 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e333 Private Use-E333 | e332 Private Use-E332 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e335 Private Use-E335 | e334 Private Use-E334 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e337 Private Use-E337 | e336 Private Use-E336 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e339 Private Use-E339 | e338 Private Use-E338 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e33b Private Use-E33B | e33a Private Use-E33A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e33d Private Use-E33D | e33c Private Use-E33C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e33f Private Use-E33F | e33e Private Use-E33E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e341 Private Use-E341 | e340 Private Use-E340 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e343 Private Use-E343 | e342 Private Use-E342 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e345 Private Use-E345 | e344 Private Use-E344 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e347 Private Use-E347 | e346 Private Use-E346 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e349 Private Use-E349 | e348 Private Use-E348 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e34b Private Use-E34B | e34a Private Use-E34A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e34d Private Use-E34D | e34c Private Use-E34C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e34f Private Use-E34F | e34e Private Use-E34E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e351 Private Use-E351 | e350 Private Use-E350 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e353 Private Use-E353 | e352 Private Use-E352 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e355 Private Use-E355 | e354 Private Use-E354 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e357 Private Use-E357 | e356 Private Use-E356 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e359 Private Use-E359 | e358 Private Use-E358 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e35b Private Use-E35B | e35a Private Use-E35A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e35d Private Use-E35D | e35c Private Use-E35C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e35f Private Use-E35F | e35e Private Use-E35E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e361 Private Use-E361 | e360 Private Use-E360 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e363 Private Use-E363 | e362 Private Use-E362 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e365 Private Use-E365 | e364 Private Use-E364 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e367 Private Use-E367 | e366 Private Use-E366 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e369 Private Use-E369 | e368 Private Use-E368 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e36b Private Use-E36B | e36a Private Use-E36A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e36d Private Use-E36D | e36c Private Use-E36C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e36f Private Use-E36F | e36e Private Use-E36E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e371 Private Use-E371 | e370 Private Use-E370 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e373 Private Use-E373 | e372 Private Use-E372 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e375 Private Use-E375 | e374 Private Use-E374 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e377 Private Use-E377 | e376 Private Use-E376 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e379 Private Use-E379 | e378 Private Use-E378 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e37b Private Use-E37B | e37a Private Use-E37A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e37d Private Use-E37D | e37c Private Use-E37C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e37f Private Use-E37F | e37e Private Use-E37E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e381 Private Use-E381 | e380 Private Use-E380 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e383 Private Use-E383 | e382 Private Use-E382 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e385 Private Use-E385 | e384 Private Use-E384 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e387 Private Use-E387 | e386 Private Use-E386 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e389 Private Use-E389 | e388 Private Use-E388 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e38b Private Use-E38B | e38a Private Use-E38A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e38d Private Use-E38D | e38c Private Use-E38C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e38f Private Use-E38F | e38e Private Use-E38E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e391 Private Use-E391 | e390 Private Use-E390 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e393 Private Use-E393 | e392 Private Use-E392 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e395 Private Use-E395 | e394 Private Use-E394 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e397 Private Use-E397 | e396 Private Use-E396 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e399 Private Use-E399 | e398 Private Use-E398 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e39b Private Use-E39B | e39a Private Use-E39A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e39d Private Use-E39D | e39c Private Use-E39C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e39f Private Use-E39F | e39e Private Use-E39E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3a1 Private Use-E3A1 | e3a0 Private Use-E3A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3a3 Private Use-E3A3 | e3a2 Private Use-E3A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3a5 Private Use-E3A5 | e3a4 Private Use-E3A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3a7 Private Use-E3A7 | e3a6 Private Use-E3A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3a9 Private Use-E3A9 | e3a8 Private Use-E3A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3ab Private Use-E3AB | e3aa Private Use-E3AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3ad Private Use-E3AD | e3ac Private Use-E3AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3af Private Use-E3AF | e3ae Private Use-E3AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3b1 Private Use-E3B1 | e3b0 Private Use-E3B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3b3 Private Use-E3B3 | e3b2 Private Use-E3B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3b5 Private Use-E3B5 | e3b4 Private Use-E3B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3b7 Private Use-E3B7 | e3b6 Private Use-E3B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3b9 Private Use-E3B9 | e3b8 Private Use-E3B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3bb Private Use-E3BB | e3ba Private Use-E3BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3bd Private Use-E3BD | e3bc Private Use-E3BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3bf Private Use-E3BF | e3be Private Use-E3BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3c1 Private Use-E3C1 | e3c0 Private Use-E3C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3c3 Private Use-E3C3 | e3c2 Private Use-E3C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3c5 Private Use-E3C5 | e3c4 Private Use-E3C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3c7 Private Use-E3C7 | e3c6 Private Use-E3C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3c9 Private Use-E3C9 | e3c8 Private Use-E3C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3cb Private Use-E3CB | e3ca Private Use-E3CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3cd Private Use-E3CD | e3cc Private Use-E3CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3cf Private Use-E3CF | e3ce Private Use-E3CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3d1 Private Use-E3D1 | e3d0 Private Use-E3D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3d3 Private Use-E3D3 | e3d2 Private Use-E3D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3d5 Private Use-E3D5 | e3d4 Private Use-E3D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3d7 Private Use-E3D7 | e3d6 Private Use-E3D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3d9 Private Use-E3D9 | e3d8 Private Use-E3D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3db Private Use-E3DB | e3da Private Use-E3DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3dd Private Use-E3DD | e3dc Private Use-E3DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3df Private Use-E3DF | e3de Private Use-E3DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3e1 Private Use-E3E1 | e3e0 Private Use-E3E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3e3 Private Use-E3E3 | e3e2 Private Use-E3E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3e5 Private Use-E3E5 | e3e4 Private Use-E3E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3e7 Private Use-E3E7 | e3e6 Private Use-E3E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3e9 Private Use-E3E9 | e3e8 Private Use-E3E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3eb Private Use-E3EB | e3ea Private Use-E3EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3ed Private Use-E3ED | e3ec Private Use-E3EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3ef Private Use-E3EF | e3ee Private Use-E3EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3f1 Private Use-E3F1 | e3f0 Private Use-E3F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3f3 Private Use-E3F3 | e3f2 Private Use-E3F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3f5 Private Use-E3F5 | e3f4 Private Use-E3F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3f7 Private Use-E3F7 | e3f6 Private Use-E3F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3f9 Private Use-E3F9 | e3f8 Private Use-E3F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3fb Private Use-E3FB | e3fa Private Use-E3FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3fd Private Use-E3FD | e3fc Private Use-E3FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3ff Private Use-E3FF | e3fe Private Use-E3FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e401 Private Use-E401 | e400 Private Use-E400 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e403 Private Use-E403 | e402 Private Use-E402 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e405 Private Use-E405 | e404 Private Use-E404 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e407 Private Use-E407 | e406 Private Use-E406 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e409 Private Use-E409 | e408 Private Use-E408 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e40b Private Use-E40B | e40a Private Use-E40A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e40d Private Use-E40D | e40c Private Use-E40C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e40f Private Use-E40F | e40e Private Use-E40E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e411 Private Use-E411 | e410 Private Use-E410 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e413 Private Use-E413 | e412 Private Use-E412 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e415 Private Use-E415 | e414 Private Use-E414 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e417 Private Use-E417 | e416 Private Use-E416 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e419 Private Use-E419 | e418 Private Use-E418 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e41b Private Use-E41B | e41a Private Use-E41A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e41d Private Use-E41D | e41c Private Use-E41C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e41f Private Use-E41F | e41e Private Use-E41E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e421 Private Use-E421 | e420 Private Use-E420 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e423 Private Use-E423 | e422 Private Use-E422 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e425 Private Use-E425 | e424 Private Use-E424 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e427 Private Use-E427 | e426 Private Use-E426 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e429 Private Use-E429 | e428 Private Use-E428 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e42b Private Use-E42B | e42a Private Use-E42A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e42d Private Use-E42D | e42c Private Use-E42C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e42f Private Use-E42F | e42e Private Use-E42E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e431 Private Use-E431 | e430 Private Use-E430 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e433 Private Use-E433 | e432 Private Use-E432 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e435 Private Use-E435 | e434 Private Use-E434 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e437 Private Use-E437 | e436 Private Use-E436 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e439 Private Use-E439 | e438 Private Use-E438 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e43b Private Use-E43B | e43a Private Use-E43A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e43d Private Use-E43D | e43c Private Use-E43C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e43f Private Use-E43F | e43e Private Use-E43E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e441 Private Use-E441 | e440 Private Use-E440 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e443 Private Use-E443 | e442 Private Use-E442 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e445 Private Use-E445 | e444 Private Use-E444 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e447 Private Use-E447 | e446 Private Use-E446 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e449 Private Use-E449 | e448 Private Use-E448 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e44b Private Use-E44B | e44a Private Use-E44A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e44d Private Use-E44D | e44c Private Use-E44C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e44f Private Use-E44F | e44e Private Use-E44E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e451 Private Use-E451 | e450 Private Use-E450 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e453 Private Use-E453 | e452 Private Use-E452 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e455 Private Use-E455 | e454 Private Use-E454 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e457 Private Use-E457 | e456 Private Use-E456 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e459 Private Use-E459 | e458 Private Use-E458 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e45b Private Use-E45B | e45a Private Use-E45A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e45d Private Use-E45D | e45c Private Use-E45C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e45f Private Use-E45F | e45e Private Use-E45E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e461 Private Use-E461 | e460 Private Use-E460 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e463 Private Use-E463 | e462 Private Use-E462 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e465 Private Use-E465 | e464 Private Use-E464 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e467 Private Use-E467 | e466 Private Use-E466 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e469 Private Use-E469 | e468 Private Use-E468 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e46b Private Use-E46B | e46a Private Use-E46A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e46d Private Use-E46D | e46c Private Use-E46C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e46f Private Use-E46F | e46e Private Use-E46E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e471 Private Use-E471 | e470 Private Use-E470 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e473 Private Use-E473 | e472 Private Use-E472 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e475 Private Use-E475 | e474 Private Use-E474 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e477 Private Use-E477 | e476 Private Use-E476 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e479 Private Use-E479 | e478 Private Use-E478 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e47b Private Use-E47B | e47a Private Use-E47A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e47d Private Use-E47D | e47c Private Use-E47C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e47f Private Use-E47F | e47e Private Use-E47E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e481 Private Use-E481 | e480 Private Use-E480 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e483 Private Use-E483 | e482 Private Use-E482 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e485 Private Use-E485 | e484 Private Use-E484 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e487 Private Use-E487 | e486 Private Use-E486 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e489 Private Use-E489 | e488 Private Use-E488 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e48b Private Use-E48B | e48a Private Use-E48A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e48d Private Use-E48D | e48c Private Use-E48C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e48f Private Use-E48F | e48e Private Use-E48E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e491 Private Use-E491 | e490 Private Use-E490 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e493 Private Use-E493 | e492 Private Use-E492 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e495 Private Use-E495 | e494 Private Use-E494 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e497 Private Use-E497 | e496 Private Use-E496 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e499 Private Use-E499 | e498 Private Use-E498 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e49b Private Use-E49B | e49a Private Use-E49A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e49d Private Use-E49D | e49c Private Use-E49C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e49f Private Use-E49F | e49e Private Use-E49E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4a1 Private Use-E4A1 | e4a0 Private Use-E4A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4a3 Private Use-E4A3 | e4a2 Private Use-E4A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4a5 Private Use-E4A5 | e4a4 Private Use-E4A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4a7 Private Use-E4A7 | e4a6 Private Use-E4A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4a9 Private Use-E4A9 | e4a8 Private Use-E4A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4ab Private Use-E4AB | e4aa Private Use-E4AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4ad Private Use-E4AD | e4ac Private Use-E4AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4af Private Use-E4AF | e4ae Private Use-E4AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4b1 Private Use-E4B1 | e4b0 Private Use-E4B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4b3 Private Use-E4B3 | e4b2 Private Use-E4B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4b5 Private Use-E4B5 | e4b4 Private Use-E4B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4b7 Private Use-E4B7 | e4b6 Private Use-E4B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4b9 Private Use-E4B9 | e4b8 Private Use-E4B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4bb Private Use-E4BB | e4ba Private Use-E4BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4bd Private Use-E4BD | e4bc Private Use-E4BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4bf Private Use-E4BF | e4be Private Use-E4BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4c1 Private Use-E4C1 | e4c0 Private Use-E4C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4c3 Private Use-E4C3 | e4c2 Private Use-E4C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4c5 Private Use-E4C5 | e4c4 Private Use-E4C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4c7 Private Use-E4C7 | e4c6 Private Use-E4C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4c9 Private Use-E4C9 | e4c8 Private Use-E4C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4cb Private Use-E4CB | e4ca Private Use-E4CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4cd Private Use-E4CD | e4cc Private Use-E4CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4cf Private Use-E4CF | e4ce Private Use-E4CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4d1 Private Use-E4D1 | e4d0 Private Use-E4D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4d3 Private Use-E4D3 | e4d2 Private Use-E4D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4d5 Private Use-E4D5 | e4d4 Private Use-E4D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4d7 Private Use-E4D7 | e4d6 Private Use-E4D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4d9 Private Use-E4D9 | e4d8 Private Use-E4D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4db Private Use-E4DB | e4da Private Use-E4DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4dd Private Use-E4DD | e4dc Private Use-E4DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4df Private Use-E4DF | e4de Private Use-E4DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4e1 Private Use-E4E1 | e4e0 Private Use-E4E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4e3 Private Use-E4E3 | e4e2 Private Use-E4E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4e5 Private Use-E4E5 | e4e4 Private Use-E4E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4e7 Private Use-E4E7 | e4e6 Private Use-E4E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4e9 Private Use-E4E9 | e4e8 Private Use-E4E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4eb Private Use-E4EB | e4ea Private Use-E4EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4ed Private Use-E4ED | e4ec Private Use-E4EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4ef Private Use-E4EF | e4ee Private Use-E4EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4f1 Private Use-E4F1 | e4f0 Private Use-E4F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4f3 Private Use-E4F3 | e4f2 Private Use-E4F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4f5 Private Use-E4F5 | e4f4 Private Use-E4F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4f7 Private Use-E4F7 | e4f6 Private Use-E4F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4f9 Private Use-E4F9 | e4f8 Private Use-E4F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4fb Private Use-E4FB | e4fa Private Use-E4FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4fd Private Use-E4FD | e4fc Private Use-E4FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4ff Private Use-E4FF | e4fe Private Use-E4FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e501 Private Use-E501 | e500 Private Use-E500 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e503 Private Use-E503 | e502 Private Use-E502 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e505 Private Use-E505 | e504 Private Use-E504 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e507 Private Use-E507 | e506 Private Use-E506 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e509 Private Use-E509 | e508 Private Use-E508 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e50b Private Use-E50B | e50a Private Use-E50A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e50d Private Use-E50D | e50c Private Use-E50C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e50f Private Use-E50F | e50e Private Use-E50E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e511 Private Use-E511 | e510 Private Use-E510 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e513 Private Use-E513 | e512 Private Use-E512 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e515 Private Use-E515 | e514 Private Use-E514 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e517 Private Use-E517 | e516 Private Use-E516 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e519 Private Use-E519 | e518 Private Use-E518 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e51b Private Use-E51B | e51a Private Use-E51A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e51d Private Use-E51D | e51c Private Use-E51C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e51f Private Use-E51F | e51e Private Use-E51E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e521 Private Use-E521 | e520 Private Use-E520 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e523 Private Use-E523 | e522 Private Use-E522 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e525 Private Use-E525 | e524 Private Use-E524 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e527 Private Use-E527 | e526 Private Use-E526 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e529 Private Use-E529 | e528 Private Use-E528 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e52b Private Use-E52B | e52a Private Use-E52A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e52d Private Use-E52D | e52c Private Use-E52C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e52f Private Use-E52F | e52e Private Use-E52E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e531 Private Use-E531 | e530 Private Use-E530 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e533 Private Use-E533 | e532 Private Use-E532 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e535 Private Use-E535 | e534 Private Use-E534 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e537 Private Use-E537 | e536 Private Use-E536 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e539 Private Use-E539 | e538 Private Use-E538 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e53b Private Use-E53B | e53a Private Use-E53A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e53d Private Use-E53D | e53c Private Use-E53C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e53f Private Use-E53F | e53e Private Use-E53E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e541 Private Use-E541 | e540 Private Use-E540 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e543 Private Use-E543 | e542 Private Use-E542 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e545 Private Use-E545 | e544 Private Use-E544 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e547 Private Use-E547 | e546 Private Use-E546 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e549 Private Use-E549 | e548 Private Use-E548 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e54b Private Use-E54B | e54a Private Use-E54A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e54d Private Use-E54D | e54c Private Use-E54C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e54f Private Use-E54F | e54e Private Use-E54E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e551 Private Use-E551 | e550 Private Use-E550 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e553 Private Use-E553 | e552 Private Use-E552 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e555 Private Use-E555 | e554 Private Use-E554 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e557 Private Use-E557 | e556 Private Use-E556 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e559 Private Use-E559 | e558 Private Use-E558 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e55b Private Use-E55B | e55a Private Use-E55A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e55d Private Use-E55D | e55c Private Use-E55C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e55f Private Use-E55F | e55e Private Use-E55E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e561 Private Use-E561 | e560 Private Use-E560 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e563 Private Use-E563 | e562 Private Use-E562 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e565 Private Use-E565 | e564 Private Use-E564 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e567 Private Use-E567 | e566 Private Use-E566 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e569 Private Use-E569 | e568 Private Use-E568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e56b Private Use-E56B | e56a Private Use-E56A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e56d Private Use-E56D | e56c Private Use-E56C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e56f Private Use-E56F | e56e Private Use-E56E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e571 Private Use-E571 | e570 Private Use-E570 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e573 Private Use-E573 | e572 Private Use-E572 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e575 Private Use-E575 | e574 Private Use-E574 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e577 Private Use-E577 | e576 Private Use-E576 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e579 Private Use-E579 | e578 Private Use-E578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e57b Private Use-E57B | e57a Private Use-E57A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e57d Private Use-E57D | e57c Private Use-E57C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e57f Private Use-E57F | e57e Private Use-E57E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e581 Private Use-E581 | e580 Private Use-E580 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e583 Private Use-E583 | e582 Private Use-E582 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e585 Private Use-E585 | e584 Private Use-E584 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e587 Private Use-E587 | e586 Private Use-E586 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e589 Private Use-E589 | e588 Private Use-E588 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e58b Private Use-E58B | e58a Private Use-E58A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e58d Private Use-E58D | e58c Private Use-E58C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e58f Private Use-E58F | e58e Private Use-E58E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e591 Private Use-E591 | e590 Private Use-E590 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e593 Private Use-E593 | e592 Private Use-E592 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e595 Private Use-E595 | e594 Private Use-E594 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e597 Private Use-E597 | e596 Private Use-E596 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e599 Private Use-E599 | e598 Private Use-E598 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e59b Private Use-E59B | e59a Private Use-E59A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e59d Private Use-E59D | e59c Private Use-E59C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e59f Private Use-E59F | e59e Private Use-E59E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5a1 Private Use-E5A1 | e5a0 Private Use-E5A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5a3 Private Use-E5A3 | e5a2 Private Use-E5A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5a5 Private Use-E5A5 | e5a4 Private Use-E5A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5a7 Private Use-E5A7 | e5a6 Private Use-E5A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5a9 Private Use-E5A9 | e5a8 Private Use-E5A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5ab Private Use-E5AB | e5aa Private Use-E5AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5ad Private Use-E5AD | e5ac Private Use-E5AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5af Private Use-E5AF | e5ae Private Use-E5AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5b1 Private Use-E5B1 | e5b0 Private Use-E5B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5b3 Private Use-E5B3 | e5b2 Private Use-E5B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5b5 Private Use-E5B5 | e5b4 Private Use-E5B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5b7 Private Use-E5B7 | e5b6 Private Use-E5B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5b9 Private Use-E5B9 | e5b8 Private Use-E5B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5bb Private Use-E5BB | e5ba Private Use-E5BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5bd Private Use-E5BD | e5bc Private Use-E5BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5bf Private Use-E5BF | e5be Private Use-E5BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5c1 Private Use-E5C1 | e5c0 Private Use-E5C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5c3 Private Use-E5C3 | e5c2 Private Use-E5C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5c5 Private Use-E5C5 | e5c4 Private Use-E5C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5c7 Private Use-E5C7 | e5c6 Private Use-E5C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5c9 Private Use-E5C9 | e5c8 Private Use-E5C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5cb Private Use-E5CB | e5ca Private Use-E5CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5cd Private Use-E5CD | e5cc Private Use-E5CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5cf Private Use-E5CF | e5ce Private Use-E5CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5d1 Private Use-E5D1 | e5d0 Private Use-E5D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5d3 Private Use-E5D3 | e5d2 Private Use-E5D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5d5 Private Use-E5D5 | e5d4 Private Use-E5D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5d7 Private Use-E5D7 | e5d6 Private Use-E5D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5d9 Private Use-E5D9 | e5d8 Private Use-E5D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5db Private Use-E5DB | e5da Private Use-E5DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5dd Private Use-E5DD | e5dc Private Use-E5DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5df Private Use-E5DF | e5de Private Use-E5DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5e1 Private Use-E5E1 | e5e0 Private Use-E5E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5e3 Private Use-E5E3 | e5e2 Private Use-E5E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5e5 Private Use-E5E5 | e5e4 Private Use-E5E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5e7 Private Use-E5E7 | e5e6 Private Use-E5E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5e9 Private Use-E5E9 | e5e8 Private Use-E5E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5eb Private Use-E5EB | e5ea Private Use-E5EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5ed Private Use-E5ED | e5ec Private Use-E5EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5ef Private Use-E5EF | e5ee Private Use-E5EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5f1 Private Use-E5F1 | e5f0 Private Use-E5F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5f3 Private Use-E5F3 | e5f2 Private Use-E5F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5f5 Private Use-E5F5 | e5f4 Private Use-E5F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5f7 Private Use-E5F7 | e5f6 Private Use-E5F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5f9 Private Use-E5F9 | e5f8 Private Use-E5F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5fb Private Use-E5FB | e5fa Private Use-E5FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5fd Private Use-E5FD | e5fc Private Use-E5FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5ff Private Use-E5FF | e5fe Private Use-E5FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e601 Private Use-E601 | e600 Private Use-E600 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e603 Private Use-E603 | e602 Private Use-E602 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e605 Private Use-E605 | e604 Private Use-E604 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e607 Private Use-E607 | e606 Private Use-E606 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e609 Private Use-E609 | e608 Private Use-E608 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e60b Private Use-E60B | e60a Private Use-E60A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e60d Private Use-E60D | e60c Private Use-E60C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e60f Private Use-E60F | e60e Private Use-E60E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e611 Private Use-E611 | e610 Private Use-E610 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e613 Private Use-E613 | e612 Private Use-E612 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e615 Private Use-E615 | e614 Private Use-E614 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e617 Private Use-E617 | e616 Private Use-E616 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e619 Private Use-E619 | e618 Private Use-E618 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e61b Private Use-E61B | e61a Private Use-E61A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e61d Private Use-E61D | e61c Private Use-E61C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e61f Private Use-E61F | e61e Private Use-E61E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e621 Private Use-E621 | e620 Private Use-E620 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e623 Private Use-E623 | e622 Private Use-E622 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e625 Private Use-E625 | e624 Private Use-E624 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e627 Private Use-E627 | e626 Private Use-E626 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e629 Private Use-E629 | e628 Private Use-E628 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e62b Private Use-E62B | e62a Private Use-E62A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e62d Private Use-E62D | e62c Private Use-E62C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e62f Private Use-E62F | e62e Private Use-E62E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e631 Private Use-E631 | e630 Private Use-E630 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e633 Private Use-E633 | e632 Private Use-E632 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e635 Private Use-E635 | e634 Private Use-E634 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e637 Private Use-E637 | e636 Private Use-E636 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e639 Private Use-E639 | e638 Private Use-E638 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e63b Private Use-E63B | e63a Private Use-E63A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e63d Private Use-E63D | e63c Private Use-E63C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e63f Private Use-E63F | e63e Private Use-E63E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e641 Private Use-E641 | e640 Private Use-E640 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e643 Private Use-E643 | e642 Private Use-E642 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e645 Private Use-E645 | e644 Private Use-E644 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e647 Private Use-E647 | e646 Private Use-E646 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e649 Private Use-E649 | e648 Private Use-E648 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e64b Private Use-E64B | e64a Private Use-E64A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e64d Private Use-E64D | e64c Private Use-E64C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e64f Private Use-E64F | e64e Private Use-E64E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e651 Private Use-E651 | e650 Private Use-E650 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e653 Private Use-E653 | e652 Private Use-E652 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e655 Private Use-E655 | e654 Private Use-E654 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e657 Private Use-E657 | e656 Private Use-E656 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e659 Private Use-E659 | e658 Private Use-E658 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e65b Private Use-E65B | e65a Private Use-E65A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e65d Private Use-E65D | e65c Private Use-E65C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e65f Private Use-E65F | e65e Private Use-E65E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e661 Private Use-E661 | e660 Private Use-E660 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e663 Private Use-E663 | e662 Private Use-E662 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e665 Private Use-E665 | e664 Private Use-E664 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e667 Private Use-E667 | e666 Private Use-E666 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e669 Private Use-E669 | e668 Private Use-E668 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e66b Private Use-E66B | e66a Private Use-E66A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e66d Private Use-E66D | e66c Private Use-E66C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e66f Private Use-E66F | e66e Private Use-E66E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e671 Private Use-E671 | e670 Private Use-E670 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e673 Private Use-E673 | e672 Private Use-E672 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e675 Private Use-E675 | e674 Private Use-E674 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e677 Private Use-E677 | e676 Private Use-E676 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e679 Private Use-E679 | e678 Private Use-E678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e67b Private Use-E67B | e67a Private Use-E67A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e67d Private Use-E67D | e67c Private Use-E67C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e67f Private Use-E67F | e67e Private Use-E67E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e681 Private Use-E681 | e680 Private Use-E680 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e683 Private Use-E683 | e682 Private Use-E682 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e685 Private Use-E685 | e684 Private Use-E684 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e687 Private Use-E687 | e686 Private Use-E686 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e689 Private Use-E689 | e688 Private Use-E688 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e68b Private Use-E68B | e68a Private Use-E68A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e68d Private Use-E68D | e68c Private Use-E68C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e68f Private Use-E68F | e68e Private Use-E68E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e691 Private Use-E691 | e690 Private Use-E690 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e693 Private Use-E693 | e692 Private Use-E692 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e695 Private Use-E695 | e694 Private Use-E694 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e697 Private Use-E697 | e696 Private Use-E696 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e699 Private Use-E699 | e698 Private Use-E698 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e69b Private Use-E69B | e69a Private Use-E69A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e69d Private Use-E69D | e69c Private Use-E69C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e69f Private Use-E69F | e69e Private Use-E69E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6a1 Private Use-E6A1 | e6a0 Private Use-E6A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6a3 Private Use-E6A3 | e6a2 Private Use-E6A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6a5 Private Use-E6A5 | e6a4 Private Use-E6A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6a7 Private Use-E6A7 | e6a6 Private Use-E6A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6a9 Private Use-E6A9 | e6a8 Private Use-E6A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6ab Private Use-E6AB | e6aa Private Use-E6AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6ad Private Use-E6AD | e6ac Private Use-E6AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6af Private Use-E6AF | e6ae Private Use-E6AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6b1 Private Use-E6B1 | e6b0 Private Use-E6B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6b3 Private Use-E6B3 | e6b2 Private Use-E6B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6b5 Private Use-E6B5 | e6b4 Private Use-E6B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6b7 Private Use-E6B7 | e6b6 Private Use-E6B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6b9 Private Use-E6B9 | e6b8 Private Use-E6B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6bb Private Use-E6BB | e6ba Private Use-E6BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6bd Private Use-E6BD | e6bc Private Use-E6BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6bf Private Use-E6BF | e6be Private Use-E6BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6c1 Private Use-E6C1 | e6c0 Private Use-E6C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6c3 Private Use-E6C3 | e6c2 Private Use-E6C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6c5 Private Use-E6C5 | e6c4 Private Use-E6C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6c7 Private Use-E6C7 | e6c6 Private Use-E6C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6c9 Private Use-E6C9 | e6c8 Private Use-E6C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6cb Private Use-E6CB | e6ca Private Use-E6CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6cd Private Use-E6CD | e6cc Private Use-E6CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6cf Private Use-E6CF | e6ce Private Use-E6CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6d1 Private Use-E6D1 | e6d0 Private Use-E6D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6d3 Private Use-E6D3 | e6d2 Private Use-E6D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6d5 Private Use-E6D5 | e6d4 Private Use-E6D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6d7 Private Use-E6D7 | e6d6 Private Use-E6D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6d9 Private Use-E6D9 | e6d8 Private Use-E6D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6db Private Use-E6DB | e6da Private Use-E6DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6dd Private Use-E6DD | e6dc Private Use-E6DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6df Private Use-E6DF | e6de Private Use-E6DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6e1 Private Use-E6E1 | e6e0 Private Use-E6E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6e3 Private Use-E6E3 | e6e2 Private Use-E6E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6e5 Private Use-E6E5 | e6e4 Private Use-E6E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6e7 Private Use-E6E7 | e6e6 Private Use-E6E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6e9 Private Use-E6E9 | e6e8 Private Use-E6E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6eb Private Use-E6EB | e6ea Private Use-E6EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6ed Private Use-E6ED | e6ec Private Use-E6EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6ef Private Use-E6EF | e6ee Private Use-E6EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6f1 Private Use-E6F1 | e6f0 Private Use-E6F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6f3 Private Use-E6F3 | e6f2 Private Use-E6F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6f5 Private Use-E6F5 | e6f4 Private Use-E6F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6f7 Private Use-E6F7 | e6f6 Private Use-E6F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6f9 Private Use-E6F9 | e6f8 Private Use-E6F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6fb Private Use-E6FB | e6fa Private Use-E6FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6fd Private Use-E6FD | e6fc Private Use-E6FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6ff Private Use-E6FF | e6fe Private Use-E6FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e701 Private Use-E701 | e700 Private Use-E700 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e703 Private Use-E703 | e702 Private Use-E702 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e705 Private Use-E705 | e704 Private Use-E704 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e707 Private Use-E707 | e706 Private Use-E706 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e709 Private Use-E709 | e708 Private Use-E708 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e70b Private Use-E70B | e70a Private Use-E70A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e70d Private Use-E70D | e70c Private Use-E70C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e70f Private Use-E70F | e70e Private Use-E70E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e711 Private Use-E711 | e710 Private Use-E710 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e713 Private Use-E713 | e712 Private Use-E712 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e715 Private Use-E715 | e714 Private Use-E714 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e717 Private Use-E717 | e716 Private Use-E716 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e719 Private Use-E719 | e718 Private Use-E718 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e71b Private Use-E71B | e71a Private Use-E71A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e71d Private Use-E71D | e71c Private Use-E71C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e71f Private Use-E71F | e71e Private Use-E71E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e721 Private Use-E721 | e720 Private Use-E720 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e723 Private Use-E723 | e722 Private Use-E722 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e725 Private Use-E725 | e724 Private Use-E724 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e727 Private Use-E727 | e726 Private Use-E726 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e729 Private Use-E729 | e728 Private Use-E728 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e72b Private Use-E72B | e72a Private Use-E72A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e72d Private Use-E72D | e72c Private Use-E72C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e72f Private Use-E72F | e72e Private Use-E72E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e731 Private Use-E731 | e730 Private Use-E730 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e733 Private Use-E733 | e732 Private Use-E732 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e735 Private Use-E735 | e734 Private Use-E734 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e737 Private Use-E737 | e736 Private Use-E736 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e739 Private Use-E739 | e738 Private Use-E738 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e73b Private Use-E73B | e73a Private Use-E73A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e73d Private Use-E73D | e73c Private Use-E73C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e73f Private Use-E73F | e73e Private Use-E73E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e741 Private Use-E741 | e740 Private Use-E740 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e743 Private Use-E743 | e742 Private Use-E742 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e745 Private Use-E745 | e744 Private Use-E744 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e747 Private Use-E747 | e746 Private Use-E746 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e749 Private Use-E749 | e748 Private Use-E748 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e74b Private Use-E74B | e74a Private Use-E74A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e74d Private Use-E74D | e74c Private Use-E74C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e74f Private Use-E74F | e74e Private Use-E74E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e751 Private Use-E751 | e750 Private Use-E750 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e753 Private Use-E753 | e752 Private Use-E752 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e755 Private Use-E755 | e754 Private Use-E754 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e757 Private Use-E757 | e756 Private Use-E756 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e759 Private Use-E759 | e758 Private Use-E758 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e75b Private Use-E75B | e75a Private Use-E75A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e75d Private Use-E75D | e75c Private Use-E75C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e75f Private Use-E75F | e75e Private Use-E75E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e761 Private Use-E761 | e760 Private Use-E760 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e763 Private Use-E763 | e762 Private Use-E762 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e765 Private Use-E765 | e764 Private Use-E764 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e767 Private Use-E767 | e766 Private Use-E766 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e769 Private Use-E769 | e768 Private Use-E768 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e76b Private Use-E76B | e76a Private Use-E76A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e76d Private Use-E76D | e76c Private Use-E76C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e76f Private Use-E76F | e76e Private Use-E76E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e771 Private Use-E771 | e770 Private Use-E770 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e773 Private Use-E773 | e772 Private Use-E772 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e775 Private Use-E775 | e774 Private Use-E774 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e777 Private Use-E777 | e776 Private Use-E776 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e779 Private Use-E779 | e778 Private Use-E778 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e77b Private Use-E77B | e77a Private Use-E77A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e77d Private Use-E77D | e77c Private Use-E77C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e77f Private Use-E77F | e77e Private Use-E77E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e781 Private Use-E781 | e780 Private Use-E780 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e783 Private Use-E783 | e782 Private Use-E782 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e785 Private Use-E785 | e784 Private Use-E784 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e787 Private Use-E787 | e786 Private Use-E786 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e789 Private Use-E789 | e788 Private Use-E788 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e78b Private Use-E78B | e78a Private Use-E78A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e78d Private Use-E78D | e78c Private Use-E78C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e78f Private Use-E78F | e78e Private Use-E78E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e791 Private Use-E791 | e790 Private Use-E790 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e793 Private Use-E793 | e792 Private Use-E792 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e795 Private Use-E795 | e794 Private Use-E794 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e797 Private Use-E797 | e796 Private Use-E796 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e799 Private Use-E799 | e798 Private Use-E798 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e79b Private Use-E79B | e79a Private Use-E79A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e79d Private Use-E79D | e79c Private Use-E79C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e79f Private Use-E79F | e79e Private Use-E79E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7a1 Private Use-E7A1 | e7a0 Private Use-E7A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7a3 Private Use-E7A3 | e7a2 Private Use-E7A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7a5 Private Use-E7A5 | e7a4 Private Use-E7A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7a7 Private Use-E7A7 | e7a6 Private Use-E7A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7a9 Private Use-E7A9 | e7a8 Private Use-E7A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7ab Private Use-E7AB | e7aa Private Use-E7AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7ad Private Use-E7AD | e7ac Private Use-E7AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7af Private Use-E7AF | e7ae Private Use-E7AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7b1 Private Use-E7B1 | e7b0 Private Use-E7B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7b3 Private Use-E7B3 | e7b2 Private Use-E7B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7b5 Private Use-E7B5 | e7b4 Private Use-E7B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7b7 Private Use-E7B7 | e7b6 Private Use-E7B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7b9 Private Use-E7B9 | e7b8 Private Use-E7B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7bb Private Use-E7BB | e7ba Private Use-E7BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7bd Private Use-E7BD | e7bc Private Use-E7BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7bf Private Use-E7BF | e7be Private Use-E7BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7c1 Private Use-E7C1 | e7c0 Private Use-E7C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7c3 Private Use-E7C3 | e7c2 Private Use-E7C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7c5 Private Use-E7C5 | e7c4 Private Use-E7C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7c7 Private Use-E7C7 | e7c6 Private Use-E7C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7c9 Private Use-E7C9 | e7c8 Private Use-E7C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7cb Private Use-E7CB | e7ca Private Use-E7CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7cd Private Use-E7CD | e7cc Private Use-E7CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7cf Private Use-E7CF | e7ce Private Use-E7CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7d1 Private Use-E7D1 | e7d0 Private Use-E7D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7d3 Private Use-E7D3 | e7d2 Private Use-E7D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7d5 Private Use-E7D5 | e7d4 Private Use-E7D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7d7 Private Use-E7D7 | e7d6 Private Use-E7D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7d9 Private Use-E7D9 | e7d8 Private Use-E7D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7db Private Use-E7DB | e7da Private Use-E7DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7dd Private Use-E7DD | e7dc Private Use-E7DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7df Private Use-E7DF | e7de Private Use-E7DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7e1 Private Use-E7E1 | e7e0 Private Use-E7E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7e3 Private Use-E7E3 | e7e2 Private Use-E7E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7e5 Private Use-E7E5 | e7e4 Private Use-E7E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7e7 Private Use-E7E7 | e7e6 Private Use-E7E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7e9 Private Use-E7E9 | e7e8 Private Use-E7E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7eb Private Use-E7EB | e7ea Private Use-E7EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7ed Private Use-E7ED | e7ec Private Use-E7EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7ef Private Use-E7EF | e7ee Private Use-E7EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7f1 Private Use-E7F1 | e7f0 Private Use-E7F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7f3 Private Use-E7F3 | e7f2 Private Use-E7F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7f5 Private Use-E7F5 | e7f4 Private Use-E7F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7f7 Private Use-E7F7 | e7f6 Private Use-E7F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7f9 Private Use-E7F9 | e7f8 Private Use-E7F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7fb Private Use-E7FB | e7fa Private Use-E7FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7fd Private Use-E7FD | e7fc Private Use-E7FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7ff Private Use-E7FF | e7fe Private Use-E7FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e801 Private Use-E801 | e800 Private Use-E800 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e803 Private Use-E803 | e802 Private Use-E802 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e805 Private Use-E805 | e804 Private Use-E804 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e807 Private Use-E807 | e806 Private Use-E806 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e809 Private Use-E809 | e808 Private Use-E808 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e80b Private Use-E80B | e80a Private Use-E80A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e80d Private Use-E80D | e80c Private Use-E80C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e80f Private Use-E80F | e80e Private Use-E80E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e811 Private Use-E811 | e810 Private Use-E810 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e813 Private Use-E813 | e812 Private Use-E812 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e815 Private Use-E815 | e814 Private Use-E814 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e817 Private Use-E817 | e816 Private Use-E816 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e819 Private Use-E819 | e818 Private Use-E818 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e81b Private Use-E81B | e81a Private Use-E81A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e81d Private Use-E81D | e81c Private Use-E81C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e81f Private Use-E81F | e81e Private Use-E81E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e821 Private Use-E821 | e820 Private Use-E820 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e823 Private Use-E823 | e822 Private Use-E822 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e825 Private Use-E825 | e824 Private Use-E824 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e827 Private Use-E827 | e826 Private Use-E826 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e829 Private Use-E829 | e828 Private Use-E828 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e82b Private Use-E82B | e82a Private Use-E82A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e82d Private Use-E82D | e82c Private Use-E82C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e82f Private Use-E82F | e82e Private Use-E82E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e831 Private Use-E831 | e830 Private Use-E830 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e833 Private Use-E833 | e832 Private Use-E832 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e835 Private Use-E835 | e834 Private Use-E834 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e837 Private Use-E837 | e836 Private Use-E836 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e839 Private Use-E839 | e838 Private Use-E838 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e83b Private Use-E83B | e83a Private Use-E83A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e83d Private Use-E83D | e83c Private Use-E83C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e83f Private Use-E83F | e83e Private Use-E83E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e841 Private Use-E841 | e840 Private Use-E840 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e843 Private Use-E843 | e842 Private Use-E842 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e845 Private Use-E845 | e844 Private Use-E844 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e847 Private Use-E847 | e846 Private Use-E846 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e849 Private Use-E849 | e848 Private Use-E848 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e84b Private Use-E84B | e84a Private Use-E84A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e84d Private Use-E84D | e84c Private Use-E84C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e84f Private Use-E84F | e84e Private Use-E84E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e851 Private Use-E851 | e850 Private Use-E850 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e853 Private Use-E853 | e852 Private Use-E852 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e855 Private Use-E855 | e854 Private Use-E854 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e857 Private Use-E857 | e856 Private Use-E856 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e859 Private Use-E859 | e858 Private Use-E858 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e85b Private Use-E85B | e85a Private Use-E85A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e85d Private Use-E85D | e85c Private Use-E85C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e85f Private Use-E85F | e85e Private Use-E85E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e861 Private Use-E861 | e860 Private Use-E860 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e863 Private Use-E863 | e862 Private Use-E862 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e865 Private Use-E865 | e864 Private Use-E864 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e867 Private Use-E867 | e866 Private Use-E866 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e869 Private Use-E869 | e868 Private Use-E868 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e86b Private Use-E86B | e86a Private Use-E86A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e86d Private Use-E86D | e86c Private Use-E86C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e86f Private Use-E86F | e86e Private Use-E86E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e871 Private Use-E871 | e870 Private Use-E870 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e873 Private Use-E873 | e872 Private Use-E872 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e875 Private Use-E875 | e874 Private Use-E874 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e877 Private Use-E877 | e876 Private Use-E876 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e879 Private Use-E879 | e878 Private Use-E878 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e87b Private Use-E87B | e87a Private Use-E87A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e87d Private Use-E87D | e87c Private Use-E87C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e87f Private Use-E87F | e87e Private Use-E87E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e881 Private Use-E881 | e880 Private Use-E880 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e883 Private Use-E883 | e882 Private Use-E882 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e885 Private Use-E885 | e884 Private Use-E884 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e887 Private Use-E887 | e886 Private Use-E886 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e889 Private Use-E889 | e888 Private Use-E888 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e88b Private Use-E88B | e88a Private Use-E88A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e88d Private Use-E88D | e88c Private Use-E88C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e88f Private Use-E88F | e88e Private Use-E88E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e891 Private Use-E891 | e890 Private Use-E890 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e893 Private Use-E893 | e892 Private Use-E892 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e895 Private Use-E895 | e894 Private Use-E894 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e897 Private Use-E897 | e896 Private Use-E896 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e899 Private Use-E899 | e898 Private Use-E898 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e89b Private Use-E89B | e89a Private Use-E89A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e89d Private Use-E89D | e89c Private Use-E89C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e89f Private Use-E89F | e89e Private Use-E89E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8a1 Private Use-E8A1 | e8a0 Private Use-E8A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8a3 Private Use-E8A3 | e8a2 Private Use-E8A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8a5 Private Use-E8A5 | e8a4 Private Use-E8A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8a7 Private Use-E8A7 | e8a6 Private Use-E8A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8a9 Private Use-E8A9 | e8a8 Private Use-E8A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8ab Private Use-E8AB | e8aa Private Use-E8AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8ad Private Use-E8AD | e8ac Private Use-E8AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8af Private Use-E8AF | e8ae Private Use-E8AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8b1 Private Use-E8B1 | e8b0 Private Use-E8B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8b3 Private Use-E8B3 | e8b2 Private Use-E8B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8b5 Private Use-E8B5 | e8b4 Private Use-E8B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8b7 Private Use-E8B7 | e8b6 Private Use-E8B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8b9 Private Use-E8B9 | e8b8 Private Use-E8B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8bb Private Use-E8BB | e8ba Private Use-E8BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8bd Private Use-E8BD | e8bc Private Use-E8BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8bf Private Use-E8BF | e8be Private Use-E8BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8c1 Private Use-E8C1 | e8c0 Private Use-E8C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8c3 Private Use-E8C3 | e8c2 Private Use-E8C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8c5 Private Use-E8C5 | e8c4 Private Use-E8C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8c7 Private Use-E8C7 | e8c6 Private Use-E8C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8c9 Private Use-E8C9 | e8c8 Private Use-E8C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8cb Private Use-E8CB | e8ca Private Use-E8CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8cd Private Use-E8CD | e8cc Private Use-E8CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8cf Private Use-E8CF | e8ce Private Use-E8CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8d1 Private Use-E8D1 | e8d0 Private Use-E8D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8d3 Private Use-E8D3 | e8d2 Private Use-E8D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8d5 Private Use-E8D5 | e8d4 Private Use-E8D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8d7 Private Use-E8D7 | e8d6 Private Use-E8D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8d9 Private Use-E8D9 | e8d8 Private Use-E8D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8db Private Use-E8DB | e8da Private Use-E8DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8dd Private Use-E8DD | e8dc Private Use-E8DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8df Private Use-E8DF | e8de Private Use-E8DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8e1 Private Use-E8E1 | e8e0 Private Use-E8E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8e3 Private Use-E8E3 | e8e2 Private Use-E8E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8e5 Private Use-E8E5 | e8e4 Private Use-E8E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8e7 Private Use-E8E7 | e8e6 Private Use-E8E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8e9 Private Use-E8E9 | e8e8 Private Use-E8E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8eb Private Use-E8EB | e8ea Private Use-E8EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8ed Private Use-E8ED | e8ec Private Use-E8EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8ef Private Use-E8EF | e8ee Private Use-E8EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8f1 Private Use-E8F1 | e8f0 Private Use-E8F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8f3 Private Use-E8F3 | e8f2 Private Use-E8F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8f5 Private Use-E8F5 | e8f4 Private Use-E8F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8f7 Private Use-E8F7 | e8f6 Private Use-E8F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8f9 Private Use-E8F9 | e8f8 Private Use-E8F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8fb Private Use-E8FB | e8fa Private Use-E8FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8fd Private Use-E8FD | e8fc Private Use-E8FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8ff Private Use-E8FF | e8fe Private Use-E8FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e901 Private Use-E901 | e900 Private Use-E900 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e903 Private Use-E903 | e902 Private Use-E902 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e905 Private Use-E905 | e904 Private Use-E904 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e907 Private Use-E907 | e906 Private Use-E906 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e909 Private Use-E909 | e908 Private Use-E908 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e90b Private Use-E90B | e90a Private Use-E90A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e90d Private Use-E90D | e90c Private Use-E90C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e90f Private Use-E90F | e90e Private Use-E90E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e911 Private Use-E911 | e910 Private Use-E910 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e913 Private Use-E913 | e912 Private Use-E912 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e915 Private Use-E915 | e914 Private Use-E914 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e917 Private Use-E917 | e916 Private Use-E916 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e919 Private Use-E919 | e918 Private Use-E918 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e91b Private Use-E91B | e91a Private Use-E91A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e91d Private Use-E91D | e91c Private Use-E91C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e91f Private Use-E91F | e91e Private Use-E91E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e921 Private Use-E921 | e920 Private Use-E920 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e923 Private Use-E923 | e922 Private Use-E922 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e925 Private Use-E925 | e924 Private Use-E924 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e927 Private Use-E927 | e926 Private Use-E926 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e929 Private Use-E929 | e928 Private Use-E928 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e92b Private Use-E92B | e92a Private Use-E92A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e92d Private Use-E92D | e92c Private Use-E92C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e92f Private Use-E92F | e92e Private Use-E92E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e931 Private Use-E931 | e930 Private Use-E930 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e933 Private Use-E933 | e932 Private Use-E932 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e935 Private Use-E935 | e934 Private Use-E934 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e937 Private Use-E937 | e936 Private Use-E936 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e939 Private Use-E939 | e938 Private Use-E938 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e93b Private Use-E93B | e93a Private Use-E93A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e93d Private Use-E93D | e93c Private Use-E93C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e93f Private Use-E93F | e93e Private Use-E93E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e941 Private Use-E941 | e940 Private Use-E940 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e943 Private Use-E943 | e942 Private Use-E942 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e945 Private Use-E945 | e944 Private Use-E944 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e947 Private Use-E947 | e946 Private Use-E946 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e949 Private Use-E949 | e948 Private Use-E948 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e94b Private Use-E94B | e94a Private Use-E94A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e94d Private Use-E94D | e94c Private Use-E94C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e94f Private Use-E94F | e94e Private Use-E94E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e951 Private Use-E951 | e950 Private Use-E950 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e953 Private Use-E953 | e952 Private Use-E952 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e955 Private Use-E955 | e954 Private Use-E954 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e957 Private Use-E957 | e956 Private Use-E956 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e959 Private Use-E959 | e958 Private Use-E958 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e95b Private Use-E95B | e95a Private Use-E95A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e95d Private Use-E95D | e95c Private Use-E95C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e95f Private Use-E95F | e95e Private Use-E95E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e961 Private Use-E961 | e960 Private Use-E960 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e963 Private Use-E963 | e962 Private Use-E962 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e965 Private Use-E965 | e964 Private Use-E964 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e967 Private Use-E967 | e966 Private Use-E966 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e969 Private Use-E969 | e968 Private Use-E968 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e96b Private Use-E96B | e96a Private Use-E96A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e96d Private Use-E96D | e96c Private Use-E96C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e96f Private Use-E96F | e96e Private Use-E96E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e971 Private Use-E971 | e970 Private Use-E970 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e973 Private Use-E973 | e972 Private Use-E972 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e975 Private Use-E975 | e974 Private Use-E974 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e977 Private Use-E977 | e976 Private Use-E976 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e979 Private Use-E979 | e978 Private Use-E978 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e97b Private Use-E97B | e97a Private Use-E97A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e97d Private Use-E97D | e97c Private Use-E97C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e97f Private Use-E97F | e97e Private Use-E97E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e981 Private Use-E981 | e980 Private Use-E980 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e983 Private Use-E983 | e982 Private Use-E982 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e985 Private Use-E985 | e984 Private Use-E984 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e987 Private Use-E987 | e986 Private Use-E986 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e989 Private Use-E989 | e988 Private Use-E988 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e98b Private Use-E98B | e98a Private Use-E98A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e98d Private Use-E98D | e98c Private Use-E98C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e98f Private Use-E98F | e98e Private Use-E98E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e991 Private Use-E991 | e990 Private Use-E990 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e993 Private Use-E993 | e992 Private Use-E992 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e995 Private Use-E995 | e994 Private Use-E994 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e997 Private Use-E997 | e996 Private Use-E996 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e999 Private Use-E999 | e998 Private Use-E998 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e99b Private Use-E99B | e99a Private Use-E99A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e99d Private Use-E99D | e99c Private Use-E99C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e99f Private Use-E99F | e99e Private Use-E99E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9a1 Private Use-E9A1 | e9a0 Private Use-E9A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9a3 Private Use-E9A3 | e9a2 Private Use-E9A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9a5 Private Use-E9A5 | e9a4 Private Use-E9A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9a7 Private Use-E9A7 | e9a6 Private Use-E9A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9a9 Private Use-E9A9 | e9a8 Private Use-E9A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9ab Private Use-E9AB | e9aa Private Use-E9AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9ad Private Use-E9AD | e9ac Private Use-E9AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9af Private Use-E9AF | e9ae Private Use-E9AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9b1 Private Use-E9B1 | e9b0 Private Use-E9B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9b3 Private Use-E9B3 | e9b2 Private Use-E9B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9b5 Private Use-E9B5 | e9b4 Private Use-E9B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9b7 Private Use-E9B7 | e9b6 Private Use-E9B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9b9 Private Use-E9B9 | e9b8 Private Use-E9B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9bb Private Use-E9BB | e9ba Private Use-E9BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9bd Private Use-E9BD | e9bc Private Use-E9BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9bf Private Use-E9BF | e9be Private Use-E9BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9c1 Private Use-E9C1 | e9c0 Private Use-E9C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9c3 Private Use-E9C3 | e9c2 Private Use-E9C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9c5 Private Use-E9C5 | e9c4 Private Use-E9C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9c7 Private Use-E9C7 | e9c6 Private Use-E9C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9c9 Private Use-E9C9 | e9c8 Private Use-E9C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9cb Private Use-E9CB | e9ca Private Use-E9CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9cd Private Use-E9CD | e9cc Private Use-E9CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9cf Private Use-E9CF | e9ce Private Use-E9CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9d1 Private Use-E9D1 | e9d0 Private Use-E9D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9d3 Private Use-E9D3 | e9d2 Private Use-E9D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9d5 Private Use-E9D5 | e9d4 Private Use-E9D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9d7 Private Use-E9D7 | e9d6 Private Use-E9D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9d9 Private Use-E9D9 | e9d8 Private Use-E9D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9db Private Use-E9DB | e9da Private Use-E9DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9dd Private Use-E9DD | e9dc Private Use-E9DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9df Private Use-E9DF | e9de Private Use-E9DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9e1 Private Use-E9E1 | e9e0 Private Use-E9E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9e3 Private Use-E9E3 | e9e2 Private Use-E9E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9e5 Private Use-E9E5 | e9e4 Private Use-E9E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9e7 Private Use-E9E7 | e9e6 Private Use-E9E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9e9 Private Use-E9E9 | e9e8 Private Use-E9E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9eb Private Use-E9EB | e9ea Private Use-E9EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9ed Private Use-E9ED | e9ec Private Use-E9EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9ef Private Use-E9EF | e9ee Private Use-E9EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9f1 Private Use-E9F1 | e9f0 Private Use-E9F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9f3 Private Use-E9F3 | e9f2 Private Use-E9F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9f5 Private Use-E9F5 | e9f4 Private Use-E9F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9f7 Private Use-E9F7 | e9f6 Private Use-E9F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9f9 Private Use-E9F9 | e9f8 Private Use-E9F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9fb Private Use-E9FB | e9fa Private Use-E9FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9fd Private Use-E9FD | e9fc Private Use-E9FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9ff Private Use-E9FF | e9fe Private Use-E9FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea01 Private Use-EA01 | ea00 Private Use-EA00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea03 Private Use-EA03 | ea02 Private Use-EA02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea05 Private Use-EA05 | ea04 Private Use-EA04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea07 Private Use-EA07 | ea06 Private Use-EA06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea09 Private Use-EA09 | ea08 Private Use-EA08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea0b Private Use-EA0B | ea0a Private Use-EA0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea0d Private Use-EA0D | ea0c Private Use-EA0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea0f Private Use-EA0F | ea0e Private Use-EA0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea11 Private Use-EA11 | ea10 Private Use-EA10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea13 Private Use-EA13 | ea12 Private Use-EA12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea15 Private Use-EA15 | ea14 Private Use-EA14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea17 Private Use-EA17 | ea16 Private Use-EA16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea19 Private Use-EA19 | ea18 Private Use-EA18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea1b Private Use-EA1B | ea1a Private Use-EA1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea1d Private Use-EA1D | ea1c Private Use-EA1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea1f Private Use-EA1F | ea1e Private Use-EA1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea21 Private Use-EA21 | ea20 Private Use-EA20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea23 Private Use-EA23 | ea22 Private Use-EA22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea25 Private Use-EA25 | ea24 Private Use-EA24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea27 Private Use-EA27 | ea26 Private Use-EA26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea29 Private Use-EA29 | ea28 Private Use-EA28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea2b Private Use-EA2B | ea2a Private Use-EA2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea2d Private Use-EA2D | ea2c Private Use-EA2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea2f Private Use-EA2F | ea2e Private Use-EA2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea31 Private Use-EA31 | ea30 Private Use-EA30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea33 Private Use-EA33 | ea32 Private Use-EA32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea35 Private Use-EA35 | ea34 Private Use-EA34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea37 Private Use-EA37 | ea36 Private Use-EA36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea39 Private Use-EA39 | ea38 Private Use-EA38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea3b Private Use-EA3B | ea3a Private Use-EA3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea3d Private Use-EA3D | ea3c Private Use-EA3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea3f Private Use-EA3F | ea3e Private Use-EA3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea41 Private Use-EA41 | ea40 Private Use-EA40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea43 Private Use-EA43 | ea42 Private Use-EA42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea45 Private Use-EA45 | ea44 Private Use-EA44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea47 Private Use-EA47 | ea46 Private Use-EA46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea49 Private Use-EA49 | ea48 Private Use-EA48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea4b Private Use-EA4B | ea4a Private Use-EA4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea4d Private Use-EA4D | ea4c Private Use-EA4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea4f Private Use-EA4F | ea4e Private Use-EA4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea51 Private Use-EA51 | ea50 Private Use-EA50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea53 Private Use-EA53 | ea52 Private Use-EA52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea55 Private Use-EA55 | ea54 Private Use-EA54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea57 Private Use-EA57 | ea56 Private Use-EA56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea59 Private Use-EA59 | ea58 Private Use-EA58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea5b Private Use-EA5B | ea5a Private Use-EA5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea5d Private Use-EA5D | ea5c Private Use-EA5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea5f Private Use-EA5F | ea5e Private Use-EA5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea61 Private Use-EA61 | ea60 Private Use-EA60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea63 Private Use-EA63 | ea62 Private Use-EA62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea65 Private Use-EA65 | ea64 Private Use-EA64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea67 Private Use-EA67 | ea66 Private Use-EA66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea69 Private Use-EA69 | ea68 Private Use-EA68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea6b Private Use-EA6B | ea6a Private Use-EA6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea6d Private Use-EA6D | ea6c Private Use-EA6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea6f Private Use-EA6F | ea6e Private Use-EA6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea71 Private Use-EA71 | ea70 Private Use-EA70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea73 Private Use-EA73 | ea72 Private Use-EA72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea75 Private Use-EA75 | ea74 Private Use-EA74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea77 Private Use-EA77 | ea76 Private Use-EA76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea79 Private Use-EA79 | ea78 Private Use-EA78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea7b Private Use-EA7B | ea7a Private Use-EA7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea7d Private Use-EA7D | ea7c Private Use-EA7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea7f Private Use-EA7F | ea7e Private Use-EA7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea81 Private Use-EA81 | ea80 Private Use-EA80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea83 Private Use-EA83 | ea82 Private Use-EA82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea85 Private Use-EA85 | ea84 Private Use-EA84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea87 Private Use-EA87 | ea86 Private Use-EA86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea89 Private Use-EA89 | ea88 Private Use-EA88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea8b Private Use-EA8B | ea8a Private Use-EA8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea8d Private Use-EA8D | ea8c Private Use-EA8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea8f Private Use-EA8F | ea8e Private Use-EA8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea91 Private Use-EA91 | ea90 Private Use-EA90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea93 Private Use-EA93 | ea92 Private Use-EA92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea95 Private Use-EA95 | ea94 Private Use-EA94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea97 Private Use-EA97 | ea96 Private Use-EA96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea99 Private Use-EA99 | ea98 Private Use-EA98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea9b Private Use-EA9B | ea9a Private Use-EA9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea9d Private Use-EA9D | ea9c Private Use-EA9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea9f Private Use-EA9F | ea9e Private Use-EA9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaa1 Private Use-EAA1 | eaa0 Private Use-EAA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaa3 Private Use-EAA3 | eaa2 Private Use-EAA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaa5 Private Use-EAA5 | eaa4 Private Use-EAA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaa7 Private Use-EAA7 | eaa6 Private Use-EAA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaa9 Private Use-EAA9 | eaa8 Private Use-EAA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaab Private Use-EAAB | eaaa Private Use-EAAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaad Private Use-EAAD | eaac Private Use-EAAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaaf Private Use-EAAF | eaae Private Use-EAAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eab1 Private Use-EAB1 | eab0 Private Use-EAB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eab3 Private Use-EAB3 | eab2 Private Use-EAB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eab5 Private Use-EAB5 | eab4 Private Use-EAB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eab7 Private Use-EAB7 | eab6 Private Use-EAB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eab9 Private Use-EAB9 | eab8 Private Use-EAB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eabb Private Use-EABB | eaba Private Use-EABA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eabd Private Use-EABD | eabc Private Use-EABC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eabf Private Use-EABF | eabe Private Use-EABE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eac1 Private Use-EAC1 | eac0 Private Use-EAC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eac3 Private Use-EAC3 | eac2 Private Use-EAC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eac5 Private Use-EAC5 | eac4 Private Use-EAC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eac7 Private Use-EAC7 | eac6 Private Use-EAC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eac9 Private Use-EAC9 | eac8 Private Use-EAC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eacb Private Use-EACB | eaca Private Use-EACA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eacd Private Use-EACD | eacc Private Use-EACC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eacf Private Use-EACF | eace Private Use-EACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ead1 Private Use-EAD1 | ead0 Private Use-EAD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ead3 Private Use-EAD3 | ead2 Private Use-EAD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ead5 Private Use-EAD5 | ead4 Private Use-EAD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ead7 Private Use-EAD7 | ead6 Private Use-EAD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ead9 Private Use-EAD9 | ead8 Private Use-EAD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eadb Private Use-EADB | eada Private Use-EADA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eadd Private Use-EADD | eadc Private Use-EADC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eadf Private Use-EADF | eade Private Use-EADE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eae1 Private Use-EAE1 | eae0 Private Use-EAE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eae3 Private Use-EAE3 | eae2 Private Use-EAE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eae5 Private Use-EAE5 | eae4 Private Use-EAE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eae7 Private Use-EAE7 | eae6 Private Use-EAE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eae9 Private Use-EAE9 | eae8 Private Use-EAE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaeb Private Use-EAEB | eaea Private Use-EAEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaed Private Use-EAED | eaec Private Use-EAEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaef Private Use-EAEF | eaee Private Use-EAEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaf1 Private Use-EAF1 | eaf0 Private Use-EAF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaf3 Private Use-EAF3 | eaf2 Private Use-EAF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaf5 Private Use-EAF5 | eaf4 Private Use-EAF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaf7 Private Use-EAF7 | eaf6 Private Use-EAF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaf9 Private Use-EAF9 | eaf8 Private Use-EAF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eafb Private Use-EAFB | eafa Private Use-EAFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eafd Private Use-EAFD | eafc Private Use-EAFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaff Private Use-EAFF | eafe Private Use-EAFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb01 Private Use-EB01 | eb00 Private Use-EB00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb03 Private Use-EB03 | eb02 Private Use-EB02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb05 Private Use-EB05 | eb04 Private Use-EB04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb07 Private Use-EB07 | eb06 Private Use-EB06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb09 Private Use-EB09 | eb08 Private Use-EB08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb0b Private Use-EB0B | eb0a Private Use-EB0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb0d Private Use-EB0D | eb0c Private Use-EB0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb0f Private Use-EB0F | eb0e Private Use-EB0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb11 Private Use-EB11 | eb10 Private Use-EB10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb13 Private Use-EB13 | eb12 Private Use-EB12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb15 Private Use-EB15 | eb14 Private Use-EB14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb17 Private Use-EB17 | eb16 Private Use-EB16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb19 Private Use-EB19 | eb18 Private Use-EB18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb1b Private Use-EB1B | eb1a Private Use-EB1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb1d Private Use-EB1D | eb1c Private Use-EB1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb1f Private Use-EB1F | eb1e Private Use-EB1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb21 Private Use-EB21 | eb20 Private Use-EB20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb23 Private Use-EB23 | eb22 Private Use-EB22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb25 Private Use-EB25 | eb24 Private Use-EB24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb27 Private Use-EB27 | eb26 Private Use-EB26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb29 Private Use-EB29 | eb28 Private Use-EB28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb2b Private Use-EB2B | eb2a Private Use-EB2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb2d Private Use-EB2D | eb2c Private Use-EB2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb2f Private Use-EB2F | eb2e Private Use-EB2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb31 Private Use-EB31 | eb30 Private Use-EB30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb33 Private Use-EB33 | eb32 Private Use-EB32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb35 Private Use-EB35 | eb34 Private Use-EB34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb37 Private Use-EB37 | eb36 Private Use-EB36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb39 Private Use-EB39 | eb38 Private Use-EB38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb3b Private Use-EB3B | eb3a Private Use-EB3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb3d Private Use-EB3D | eb3c Private Use-EB3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb3f Private Use-EB3F | eb3e Private Use-EB3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb41 Private Use-EB41 | eb40 Private Use-EB40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb43 Private Use-EB43 | eb42 Private Use-EB42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb45 Private Use-EB45 | eb44 Private Use-EB44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb47 Private Use-EB47 | eb46 Private Use-EB46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb49 Private Use-EB49 | eb48 Private Use-EB48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb4b Private Use-EB4B | eb4a Private Use-EB4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb4d Private Use-EB4D | eb4c Private Use-EB4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb4f Private Use-EB4F | eb4e Private Use-EB4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb51 Private Use-EB51 | eb50 Private Use-EB50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb53 Private Use-EB53 | eb52 Private Use-EB52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb55 Private Use-EB55 | eb54 Private Use-EB54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb57 Private Use-EB57 | eb56 Private Use-EB56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb59 Private Use-EB59 | eb58 Private Use-EB58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb5b Private Use-EB5B | eb5a Private Use-EB5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb5d Private Use-EB5D | eb5c Private Use-EB5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb5f Private Use-EB5F | eb5e Private Use-EB5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb61 Private Use-EB61 | eb60 Private Use-EB60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb63 Private Use-EB63 | eb62 Private Use-EB62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb65 Private Use-EB65 | eb64 Private Use-EB64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb67 Private Use-EB67 | eb66 Private Use-EB66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb69 Private Use-EB69 | eb68 Private Use-EB68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb6b Private Use-EB6B | eb6a Private Use-EB6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb6d Private Use-EB6D | eb6c Private Use-EB6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb6f Private Use-EB6F | eb6e Private Use-EB6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb71 Private Use-EB71 | eb70 Private Use-EB70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb73 Private Use-EB73 | eb72 Private Use-EB72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb75 Private Use-EB75 | eb74 Private Use-EB74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb77 Private Use-EB77 | eb76 Private Use-EB76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb79 Private Use-EB79 | eb78 Private Use-EB78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb7b Private Use-EB7B | eb7a Private Use-EB7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb7d Private Use-EB7D | eb7c Private Use-EB7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb7f Private Use-EB7F | eb7e Private Use-EB7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb81 Private Use-EB81 | eb80 Private Use-EB80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb83 Private Use-EB83 | eb82 Private Use-EB82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb85 Private Use-EB85 | eb84 Private Use-EB84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb87 Private Use-EB87 | eb86 Private Use-EB86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb89 Private Use-EB89 | eb88 Private Use-EB88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb8b Private Use-EB8B | eb8a Private Use-EB8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb8d Private Use-EB8D | eb8c Private Use-EB8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb8f Private Use-EB8F | eb8e Private Use-EB8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb91 Private Use-EB91 | eb90 Private Use-EB90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb93 Private Use-EB93 | eb92 Private Use-EB92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb95 Private Use-EB95 | eb94 Private Use-EB94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb97 Private Use-EB97 | eb96 Private Use-EB96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb99 Private Use-EB99 | eb98 Private Use-EB98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb9b Private Use-EB9B | eb9a Private Use-EB9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb9d Private Use-EB9D | eb9c Private Use-EB9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb9f Private Use-EB9F | eb9e Private Use-EB9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eba1 Private Use-EBA1 | eba0 Private Use-EBA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eba3 Private Use-EBA3 | eba2 Private Use-EBA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eba5 Private Use-EBA5 | eba4 Private Use-EBA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eba7 Private Use-EBA7 | eba6 Private Use-EBA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eba9 Private Use-EBA9 | eba8 Private Use-EBA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebab Private Use-EBAB | ebaa Private Use-EBAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebad Private Use-EBAD | ebac Private Use-EBAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebaf Private Use-EBAF | ebae Private Use-EBAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebb1 Private Use-EBB1 | ebb0 Private Use-EBB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebb3 Private Use-EBB3 | ebb2 Private Use-EBB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebb5 Private Use-EBB5 | ebb4 Private Use-EBB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebb7 Private Use-EBB7 | ebb6 Private Use-EBB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebb9 Private Use-EBB9 | ebb8 Private Use-EBB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebbb Private Use-EBBB | ebba Private Use-EBBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebbd Private Use-EBBD | ebbc Private Use-EBBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebbf Private Use-EBBF | ebbe Private Use-EBBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebc1 Private Use-EBC1 | ebc0 Private Use-EBC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebc3 Private Use-EBC3 | ebc2 Private Use-EBC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebc5 Private Use-EBC5 | ebc4 Private Use-EBC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebc7 Private Use-EBC7 | ebc6 Private Use-EBC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebc9 Private Use-EBC9 | ebc8 Private Use-EBC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebcb Private Use-EBCB | ebca Private Use-EBCA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebcd Private Use-EBCD | ebcc Private Use-EBCC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebcf Private Use-EBCF | ebce Private Use-EBCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebd1 Private Use-EBD1 | ebd0 Private Use-EBD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebd3 Private Use-EBD3 | ebd2 Private Use-EBD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebd5 Private Use-EBD5 | ebd4 Private Use-EBD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebd7 Private Use-EBD7 | ebd6 Private Use-EBD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebd9 Private Use-EBD9 | ebd8 Private Use-EBD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebdb Private Use-EBDB | ebda Private Use-EBDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebdd Private Use-EBDD | ebdc Private Use-EBDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebdf Private Use-EBDF | ebde Private Use-EBDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebe1 Private Use-EBE1 | ebe0 Private Use-EBE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebe3 Private Use-EBE3 | ebe2 Private Use-EBE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebe5 Private Use-EBE5 | ebe4 Private Use-EBE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebe7 Private Use-EBE7 | ebe6 Private Use-EBE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebe9 Private Use-EBE9 | ebe8 Private Use-EBE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebeb Private Use-EBEB | ebea Private Use-EBEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebed Private Use-EBED | ebec Private Use-EBEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebef Private Use-EBEF | ebee Private Use-EBEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebf1 Private Use-EBF1 | ebf0 Private Use-EBF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebf3 Private Use-EBF3 | ebf2 Private Use-EBF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebf5 Private Use-EBF5 | ebf4 Private Use-EBF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebf7 Private Use-EBF7 | ebf6 Private Use-EBF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebf9 Private Use-EBF9 | ebf8 Private Use-EBF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebfb Private Use-EBFB | ebfa Private Use-EBFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebfd Private Use-EBFD | ebfc Private Use-EBFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebff Private Use-EBFF | ebfe Private Use-EBFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec01 Private Use-EC01 | ec00 Private Use-EC00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec03 Private Use-EC03 | ec02 Private Use-EC02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec05 Private Use-EC05 | ec04 Private Use-EC04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec07 Private Use-EC07 | ec06 Private Use-EC06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec09 Private Use-EC09 | ec08 Private Use-EC08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec0b Private Use-EC0B | ec0a Private Use-EC0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec0d Private Use-EC0D | ec0c Private Use-EC0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec0f Private Use-EC0F | ec0e Private Use-EC0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec11 Private Use-EC11 | ec10 Private Use-EC10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec13 Private Use-EC13 | ec12 Private Use-EC12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec15 Private Use-EC15 | ec14 Private Use-EC14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec17 Private Use-EC17 | ec16 Private Use-EC16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec19 Private Use-EC19 | ec18 Private Use-EC18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec1b Private Use-EC1B | ec1a Private Use-EC1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec1d Private Use-EC1D | ec1c Private Use-EC1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec1f Private Use-EC1F | ec1e Private Use-EC1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec21 Private Use-EC21 | ec20 Private Use-EC20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec23 Private Use-EC23 | ec22 Private Use-EC22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec25 Private Use-EC25 | ec24 Private Use-EC24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec27 Private Use-EC27 | ec26 Private Use-EC26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec29 Private Use-EC29 | ec28 Private Use-EC28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec2b Private Use-EC2B | ec2a Private Use-EC2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec2d Private Use-EC2D | ec2c Private Use-EC2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec2f Private Use-EC2F | ec2e Private Use-EC2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec31 Private Use-EC31 | ec30 Private Use-EC30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec33 Private Use-EC33 | ec32 Private Use-EC32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec35 Private Use-EC35 | ec34 Private Use-EC34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec37 Private Use-EC37 | ec36 Private Use-EC36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec39 Private Use-EC39 | ec38 Private Use-EC38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec3b Private Use-EC3B | ec3a Private Use-EC3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec3d Private Use-EC3D | ec3c Private Use-EC3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec3f Private Use-EC3F | ec3e Private Use-EC3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec41 Private Use-EC41 | ec40 Private Use-EC40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec43 Private Use-EC43 | ec42 Private Use-EC42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec45 Private Use-EC45 | ec44 Private Use-EC44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec47 Private Use-EC47 | ec46 Private Use-EC46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec49 Private Use-EC49 | ec48 Private Use-EC48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec4b Private Use-EC4B | ec4a Private Use-EC4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec4d Private Use-EC4D | ec4c Private Use-EC4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec4f Private Use-EC4F | ec4e Private Use-EC4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec51 Private Use-EC51 | ec50 Private Use-EC50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec53 Private Use-EC53 | ec52 Private Use-EC52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec55 Private Use-EC55 | ec54 Private Use-EC54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec57 Private Use-EC57 | ec56 Private Use-EC56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec59 Private Use-EC59 | ec58 Private Use-EC58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec5b Private Use-EC5B | ec5a Private Use-EC5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec5d Private Use-EC5D | ec5c Private Use-EC5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec5f Private Use-EC5F | ec5e Private Use-EC5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec61 Private Use-EC61 | ec60 Private Use-EC60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec63 Private Use-EC63 | ec62 Private Use-EC62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec65 Private Use-EC65 | ec64 Private Use-EC64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec67 Private Use-EC67 | ec66 Private Use-EC66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec69 Private Use-EC69 | ec68 Private Use-EC68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec6b Private Use-EC6B | ec6a Private Use-EC6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec6d Private Use-EC6D | ec6c Private Use-EC6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec6f Private Use-EC6F | ec6e Private Use-EC6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec71 Private Use-EC71 | ec70 Private Use-EC70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec73 Private Use-EC73 | ec72 Private Use-EC72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec75 Private Use-EC75 | ec74 Private Use-EC74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec77 Private Use-EC77 | ec76 Private Use-EC76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec79 Private Use-EC79 | ec78 Private Use-EC78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec7b Private Use-EC7B | ec7a Private Use-EC7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec7d Private Use-EC7D | ec7c Private Use-EC7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec7f Private Use-EC7F | ec7e Private Use-EC7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec81 Private Use-EC81 | ec80 Private Use-EC80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec83 Private Use-EC83 | ec82 Private Use-EC82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec85 Private Use-EC85 | ec84 Private Use-EC84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec87 Private Use-EC87 | ec86 Private Use-EC86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec89 Private Use-EC89 | ec88 Private Use-EC88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec8b Private Use-EC8B | ec8a Private Use-EC8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec8d Private Use-EC8D | ec8c Private Use-EC8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec8f Private Use-EC8F | ec8e Private Use-EC8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec91 Private Use-EC91 | ec90 Private Use-EC90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec93 Private Use-EC93 | ec92 Private Use-EC92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec95 Private Use-EC95 | ec94 Private Use-EC94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec97 Private Use-EC97 | ec96 Private Use-EC96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec99 Private Use-EC99 | ec98 Private Use-EC98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec9b Private Use-EC9B | ec9a Private Use-EC9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec9d Private Use-EC9D | ec9c Private Use-EC9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec9f Private Use-EC9F | ec9e Private Use-EC9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eca1 Private Use-ECA1 | eca0 Private Use-ECA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eca3 Private Use-ECA3 | eca2 Private Use-ECA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eca5 Private Use-ECA5 | eca4 Private Use-ECA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eca7 Private Use-ECA7 | eca6 Private Use-ECA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eca9 Private Use-ECA9 | eca8 Private Use-ECA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecab Private Use-ECAB | ecaa Private Use-ECAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecad Private Use-ECAD | ecac Private Use-ECAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecaf Private Use-ECAF | ecae Private Use-ECAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecb1 Private Use-ECB1 | ecb0 Private Use-ECB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecb3 Private Use-ECB3 | ecb2 Private Use-ECB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecb5 Private Use-ECB5 | ecb4 Private Use-ECB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecb7 Private Use-ECB7 | ecb6 Private Use-ECB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecb9 Private Use-ECB9 | ecb8 Private Use-ECB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecbb Private Use-ECBB | ecba Private Use-ECBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecbd Private Use-ECBD | ecbc Private Use-ECBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecbf Private Use-ECBF | ecbe Private Use-ECBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecc1 Private Use-ECC1 | ecc0 Private Use-ECC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecc3 Private Use-ECC3 | ecc2 Private Use-ECC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecc5 Private Use-ECC5 | ecc4 Private Use-ECC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecc7 Private Use-ECC7 | ecc6 Private Use-ECC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecc9 Private Use-ECC9 | ecc8 Private Use-ECC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eccb Private Use-ECCB | ecca Private Use-ECCA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eccd Private Use-ECCD | eccc Private Use-ECCC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eccf Private Use-ECCF | ecce Private Use-ECCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecd1 Private Use-ECD1 | ecd0 Private Use-ECD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecd3 Private Use-ECD3 | ecd2 Private Use-ECD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecd5 Private Use-ECD5 | ecd4 Private Use-ECD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecd7 Private Use-ECD7 | ecd6 Private Use-ECD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecd9 Private Use-ECD9 | ecd8 Private Use-ECD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecdb Private Use-ECDB | ecda Private Use-ECDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecdd Private Use-ECDD | ecdc Private Use-ECDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecdf Private Use-ECDF | ecde Private Use-ECDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ece1 Private Use-ECE1 | ece0 Private Use-ECE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ece3 Private Use-ECE3 | ece2 Private Use-ECE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ece5 Private Use-ECE5 | ece4 Private Use-ECE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ece7 Private Use-ECE7 | ece6 Private Use-ECE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ece9 Private Use-ECE9 | ece8 Private Use-ECE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eceb Private Use-ECEB | ecea Private Use-ECEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eced Private Use-ECED | ecec Private Use-ECEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecef Private Use-ECEF | ecee Private Use-ECEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecf1 Private Use-ECF1 | ecf0 Private Use-ECF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecf3 Private Use-ECF3 | ecf2 Private Use-ECF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecf5 Private Use-ECF5 | ecf4 Private Use-ECF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecf7 Private Use-ECF7 | ecf6 Private Use-ECF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecf9 Private Use-ECF9 | ecf8 Private Use-ECF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecfb Private Use-ECFB | ecfa Private Use-ECFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecfd Private Use-ECFD | ecfc Private Use-ECFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecff Private Use-ECFF | ecfe Private Use-ECFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed01 Private Use-ED01 | ed00 Private Use-ED00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed03 Private Use-ED03 | ed02 Private Use-ED02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed05 Private Use-ED05 | ed04 Private Use-ED04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed07 Private Use-ED07 | ed06 Private Use-ED06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed09 Private Use-ED09 | ed08 Private Use-ED08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed0b Private Use-ED0B | ed0a Private Use-ED0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed0d Private Use-ED0D | ed0c Private Use-ED0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed0f Private Use-ED0F | ed0e Private Use-ED0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed11 Private Use-ED11 | ed10 Private Use-ED10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed13 Private Use-ED13 | ed12 Private Use-ED12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed15 Private Use-ED15 | ed14 Private Use-ED14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed17 Private Use-ED17 | ed16 Private Use-ED16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed19 Private Use-ED19 | ed18 Private Use-ED18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed1b Private Use-ED1B | ed1a Private Use-ED1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed1d Private Use-ED1D | ed1c Private Use-ED1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed1f Private Use-ED1F | ed1e Private Use-ED1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed21 Private Use-ED21 | ed20 Private Use-ED20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed23 Private Use-ED23 | ed22 Private Use-ED22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed25 Private Use-ED25 | ed24 Private Use-ED24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed27 Private Use-ED27 | ed26 Private Use-ED26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed29 Private Use-ED29 | ed28 Private Use-ED28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed2b Private Use-ED2B | ed2a Private Use-ED2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed2d Private Use-ED2D | ed2c Private Use-ED2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed2f Private Use-ED2F | ed2e Private Use-ED2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed31 Private Use-ED31 | ed30 Private Use-ED30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed33 Private Use-ED33 | ed32 Private Use-ED32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed35 Private Use-ED35 | ed34 Private Use-ED34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed37 Private Use-ED37 | ed36 Private Use-ED36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed39 Private Use-ED39 | ed38 Private Use-ED38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed3b Private Use-ED3B | ed3a Private Use-ED3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed3d Private Use-ED3D | ed3c Private Use-ED3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed3f Private Use-ED3F | ed3e Private Use-ED3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed41 Private Use-ED41 | ed40 Private Use-ED40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed43 Private Use-ED43 | ed42 Private Use-ED42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed45 Private Use-ED45 | ed44 Private Use-ED44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed47 Private Use-ED47 | ed46 Private Use-ED46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed49 Private Use-ED49 | ed48 Private Use-ED48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed4b Private Use-ED4B | ed4a Private Use-ED4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed4d Private Use-ED4D | ed4c Private Use-ED4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed4f Private Use-ED4F | ed4e Private Use-ED4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed51 Private Use-ED51 | ed50 Private Use-ED50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed53 Private Use-ED53 | ed52 Private Use-ED52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed55 Private Use-ED55 | ed54 Private Use-ED54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed57 Private Use-ED57 | ed56 Private Use-ED56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed59 Private Use-ED59 | ed58 Private Use-ED58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed5b Private Use-ED5B | ed5a Private Use-ED5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed5d Private Use-ED5D | ed5c Private Use-ED5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed5f Private Use-ED5F | ed5e Private Use-ED5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed61 Private Use-ED61 | ed60 Private Use-ED60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed63 Private Use-ED63 | ed62 Private Use-ED62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed65 Private Use-ED65 | ed64 Private Use-ED64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed67 Private Use-ED67 | ed66 Private Use-ED66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed69 Private Use-ED69 | ed68 Private Use-ED68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed6b Private Use-ED6B | ed6a Private Use-ED6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed6d Private Use-ED6D | ed6c Private Use-ED6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed6f Private Use-ED6F | ed6e Private Use-ED6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed71 Private Use-ED71 | ed70 Private Use-ED70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed73 Private Use-ED73 | ed72 Private Use-ED72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed75 Private Use-ED75 | ed74 Private Use-ED74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed77 Private Use-ED77 | ed76 Private Use-ED76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed79 Private Use-ED79 | ed78 Private Use-ED78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed7b Private Use-ED7B | ed7a Private Use-ED7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed7d Private Use-ED7D | ed7c Private Use-ED7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed7f Private Use-ED7F | ed7e Private Use-ED7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed81 Private Use-ED81 | ed80 Private Use-ED80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed83 Private Use-ED83 | ed82 Private Use-ED82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed85 Private Use-ED85 | ed84 Private Use-ED84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed87 Private Use-ED87 | ed86 Private Use-ED86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed89 Private Use-ED89 | ed88 Private Use-ED88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed8b Private Use-ED8B | ed8a Private Use-ED8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed8d Private Use-ED8D | ed8c Private Use-ED8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed8f Private Use-ED8F | ed8e Private Use-ED8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed91 Private Use-ED91 | ed90 Private Use-ED90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed93 Private Use-ED93 | ed92 Private Use-ED92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed95 Private Use-ED95 | ed94 Private Use-ED94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed97 Private Use-ED97 | ed96 Private Use-ED96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed99 Private Use-ED99 | ed98 Private Use-ED98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed9b Private Use-ED9B | ed9a Private Use-ED9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed9d Private Use-ED9D | ed9c Private Use-ED9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed9f Private Use-ED9F | ed9e Private Use-ED9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eda1 Private Use-EDA1 | eda0 Private Use-EDA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eda3 Private Use-EDA3 | eda2 Private Use-EDA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eda5 Private Use-EDA5 | eda4 Private Use-EDA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eda7 Private Use-EDA7 | eda6 Private Use-EDA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eda9 Private Use-EDA9 | eda8 Private Use-EDA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edab Private Use-EDAB | edaa Private Use-EDAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edad Private Use-EDAD | edac Private Use-EDAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edaf Private Use-EDAF | edae Private Use-EDAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edb1 Private Use-EDB1 | edb0 Private Use-EDB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edb3 Private Use-EDB3 | edb2 Private Use-EDB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edb5 Private Use-EDB5 | edb4 Private Use-EDB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edb7 Private Use-EDB7 | edb6 Private Use-EDB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edb9 Private Use-EDB9 | edb8 Private Use-EDB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edbb Private Use-EDBB | edba Private Use-EDBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edbd Private Use-EDBD | edbc Private Use-EDBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edbf Private Use-EDBF | edbe Private Use-EDBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edc1 Private Use-EDC1 | edc0 Private Use-EDC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edc3 Private Use-EDC3 | edc2 Private Use-EDC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edc5 Private Use-EDC5 | edc4 Private Use-EDC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edc7 Private Use-EDC7 | edc6 Private Use-EDC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edc9 Private Use-EDC9 | edc8 Private Use-EDC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edcb Private Use-EDCB | edca Private Use-EDCA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edcd Private Use-EDCD | edcc Private Use-EDCC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edcf Private Use-EDCF | edce Private Use-EDCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edd1 Private Use-EDD1 | edd0 Private Use-EDD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edd3 Private Use-EDD3 | edd2 Private Use-EDD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edd5 Private Use-EDD5 | edd4 Private Use-EDD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edd7 Private Use-EDD7 | edd6 Private Use-EDD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edd9 Private Use-EDD9 | edd8 Private Use-EDD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eddb Private Use-EDDB | edda Private Use-EDDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eddd Private Use-EDDD | eddc Private Use-EDDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eddf Private Use-EDDF | edde Private Use-EDDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ede1 Private Use-EDE1 | ede0 Private Use-EDE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ede3 Private Use-EDE3 | ede2 Private Use-EDE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ede5 Private Use-EDE5 | ede4 Private Use-EDE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ede7 Private Use-EDE7 | ede6 Private Use-EDE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ede9 Private Use-EDE9 | ede8 Private Use-EDE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edeb Private Use-EDEB | edea Private Use-EDEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eded Private Use-EDED | edec Private Use-EDEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edef Private Use-EDEF | edee Private Use-EDEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edf1 Private Use-EDF1 | edf0 Private Use-EDF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edf3 Private Use-EDF3 | edf2 Private Use-EDF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edf5 Private Use-EDF5 | edf4 Private Use-EDF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edf7 Private Use-EDF7 | edf6 Private Use-EDF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edf9 Private Use-EDF9 | edf8 Private Use-EDF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edfb Private Use-EDFB | edfa Private Use-EDFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edfd Private Use-EDFD | edfc Private Use-EDFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edff Private Use-EDFF | edfe Private Use-EDFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee01 Private Use-EE01 | ee00 Private Use-EE00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee03 Private Use-EE03 | ee02 Private Use-EE02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee05 Private Use-EE05 | ee04 Private Use-EE04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee07 Private Use-EE07 | ee06 Private Use-EE06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee09 Private Use-EE09 | ee08 Private Use-EE08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee0b Private Use-EE0B | ee0a Private Use-EE0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee0d Private Use-EE0D | ee0c Private Use-EE0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee0f Private Use-EE0F | ee0e Private Use-EE0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee11 Private Use-EE11 | ee10 Private Use-EE10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee13 Private Use-EE13 | ee12 Private Use-EE12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee15 Private Use-EE15 | ee14 Private Use-EE14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee17 Private Use-EE17 | ee16 Private Use-EE16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee19 Private Use-EE19 | ee18 Private Use-EE18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee1b Private Use-EE1B | ee1a Private Use-EE1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee1d Private Use-EE1D | ee1c Private Use-EE1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee1f Private Use-EE1F | ee1e Private Use-EE1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee21 Private Use-EE21 | ee20 Private Use-EE20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee23 Private Use-EE23 | ee22 Private Use-EE22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee25 Private Use-EE25 | ee24 Private Use-EE24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee27 Private Use-EE27 | ee26 Private Use-EE26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee29 Private Use-EE29 | ee28 Private Use-EE28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee2b Private Use-EE2B | ee2a Private Use-EE2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee2d Private Use-EE2D | ee2c Private Use-EE2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee2f Private Use-EE2F | ee2e Private Use-EE2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee31 Private Use-EE31 | ee30 Private Use-EE30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee33 Private Use-EE33 | ee32 Private Use-EE32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee35 Private Use-EE35 | ee34 Private Use-EE34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee37 Private Use-EE37 | ee36 Private Use-EE36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee39 Private Use-EE39 | ee38 Private Use-EE38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee3b Private Use-EE3B | ee3a Private Use-EE3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee3d Private Use-EE3D | ee3c Private Use-EE3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee3f Private Use-EE3F | ee3e Private Use-EE3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee41 Private Use-EE41 | ee40 Private Use-EE40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee43 Private Use-EE43 | ee42 Private Use-EE42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee45 Private Use-EE45 | ee44 Private Use-EE44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee47 Private Use-EE47 | ee46 Private Use-EE46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee49 Private Use-EE49 | ee48 Private Use-EE48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee4b Private Use-EE4B | ee4a Private Use-EE4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee4d Private Use-EE4D | ee4c Private Use-EE4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee4f Private Use-EE4F | ee4e Private Use-EE4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee51 Private Use-EE51 | ee50 Private Use-EE50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee53 Private Use-EE53 | ee52 Private Use-EE52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee55 Private Use-EE55 | ee54 Private Use-EE54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee57 Private Use-EE57 | ee56 Private Use-EE56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee59 Private Use-EE59 | ee58 Private Use-EE58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee5b Private Use-EE5B | ee5a Private Use-EE5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee5d Private Use-EE5D | ee5c Private Use-EE5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee5f Private Use-EE5F | ee5e Private Use-EE5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee61 Private Use-EE61 | ee60 Private Use-EE60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee63 Private Use-EE63 | ee62 Private Use-EE62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee65 Private Use-EE65 | ee64 Private Use-EE64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee67 Private Use-EE67 | ee66 Private Use-EE66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee69 Private Use-EE69 | ee68 Private Use-EE68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee6b Private Use-EE6B | ee6a Private Use-EE6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee6d Private Use-EE6D | ee6c Private Use-EE6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee6f Private Use-EE6F | ee6e Private Use-EE6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee71 Private Use-EE71 | ee70 Private Use-EE70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee73 Private Use-EE73 | ee72 Private Use-EE72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee75 Private Use-EE75 | ee74 Private Use-EE74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee77 Private Use-EE77 | ee76 Private Use-EE76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee79 Private Use-EE79 | ee78 Private Use-EE78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee7b Private Use-EE7B | ee7a Private Use-EE7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee7d Private Use-EE7D | ee7c Private Use-EE7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee7f Private Use-EE7F | ee7e Private Use-EE7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee81 Private Use-EE81 | ee80 Private Use-EE80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee83 Private Use-EE83 | ee82 Private Use-EE82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee85 Private Use-EE85 | ee84 Private Use-EE84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee87 Private Use-EE87 | ee86 Private Use-EE86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee89 Private Use-EE89 | ee88 Private Use-EE88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee8b Private Use-EE8B | ee8a Private Use-EE8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee8d Private Use-EE8D | ee8c Private Use-EE8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee8f Private Use-EE8F | ee8e Private Use-EE8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee91 Private Use-EE91 | ee90 Private Use-EE90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee93 Private Use-EE93 | ee92 Private Use-EE92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee95 Private Use-EE95 | ee94 Private Use-EE94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee97 Private Use-EE97 | ee96 Private Use-EE96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee99 Private Use-EE99 | ee98 Private Use-EE98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee9b Private Use-EE9B | ee9a Private Use-EE9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee9d Private Use-EE9D | ee9c Private Use-EE9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee9f Private Use-EE9F | ee9e Private Use-EE9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eea1 Private Use-EEA1 | eea0 Private Use-EEA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eea3 Private Use-EEA3 | eea2 Private Use-EEA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eea5 Private Use-EEA5 | eea4 Private Use-EEA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eea7 Private Use-EEA7 | eea6 Private Use-EEA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eea9 Private Use-EEA9 | eea8 Private Use-EEA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeab Private Use-EEAB | eeaa Private Use-EEAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eead Private Use-EEAD | eeac Private Use-EEAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeaf Private Use-EEAF | eeae Private Use-EEAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeb1 Private Use-EEB1 | eeb0 Private Use-EEB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeb3 Private Use-EEB3 | eeb2 Private Use-EEB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeb5 Private Use-EEB5 | eeb4 Private Use-EEB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeb7 Private Use-EEB7 | eeb6 Private Use-EEB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeb9 Private Use-EEB9 | eeb8 Private Use-EEB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eebb Private Use-EEBB | eeba Private Use-EEBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eebd Private Use-EEBD | eebc Private Use-EEBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eebf Private Use-EEBF | eebe Private Use-EEBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eec1 Private Use-EEC1 | eec0 Private Use-EEC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eec3 Private Use-EEC3 | eec2 Private Use-EEC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eec5 Private Use-EEC5 | eec4 Private Use-EEC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eec7 Private Use-EEC7 | eec6 Private Use-EEC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eec9 Private Use-EEC9 | eec8 Private Use-EEC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eecb Private Use-EECB | eeca Private Use-EECA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eecd Private Use-EECD | eecc Private Use-EECC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eecf Private Use-EECF | eece Private Use-EECE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eed1 Private Use-EED1 | eed0 Private Use-EED0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eed3 Private Use-EED3 | eed2 Private Use-EED2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eed5 Private Use-EED5 | eed4 Private Use-EED4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eed7 Private Use-EED7 | eed6 Private Use-EED6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eed9 Private Use-EED9 | eed8 Private Use-EED8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eedb Private Use-EEDB | eeda Private Use-EEDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eedd Private Use-EEDD | eedc Private Use-EEDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eedf Private Use-EEDF | eede Private Use-EEDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eee1 Private Use-EEE1 | eee0 Private Use-EEE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eee3 Private Use-EEE3 | eee2 Private Use-EEE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eee5 Private Use-EEE5 | eee4 Private Use-EEE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eee7 Private Use-EEE7 | eee6 Private Use-EEE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eee9 Private Use-EEE9 | eee8 Private Use-EEE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeeb Private Use-EEEB | eeea Private Use-EEEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeed Private Use-EEED | eeec Private Use-EEEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeef Private Use-EEEF | eeee Private Use-EEEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eef1 Private Use-EEF1 | eef0 Private Use-EEF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eef3 Private Use-EEF3 | eef2 Private Use-EEF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eef5 Private Use-EEF5 | eef4 Private Use-EEF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eef7 Private Use-EEF7 | eef6 Private Use-EEF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eef9 Private Use-EEF9 | eef8 Private Use-EEF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eefb Private Use-EEFB | eefa Private Use-EEFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eefd Private Use-EEFD | eefc Private Use-EEFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeff Private Use-EEFF | eefe Private Use-EEFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef01 Private Use-EF01 | ef00 Private Use-EF00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef03 Private Use-EF03 | ef02 Private Use-EF02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef05 Private Use-EF05 | ef04 Private Use-EF04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef07 Private Use-EF07 | ef06 Private Use-EF06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef09 Private Use-EF09 | ef08 Private Use-EF08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef0b Private Use-EF0B | ef0a Private Use-EF0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef0d Private Use-EF0D | ef0c Private Use-EF0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef0f Private Use-EF0F | ef0e Private Use-EF0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef11 Private Use-EF11 | ef10 Private Use-EF10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef13 Private Use-EF13 | ef12 Private Use-EF12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef15 Private Use-EF15 | ef14 Private Use-EF14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef17 Private Use-EF17 | ef16 Private Use-EF16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef19 Private Use-EF19 | ef18 Private Use-EF18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef1b Private Use-EF1B | ef1a Private Use-EF1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef1d Private Use-EF1D | ef1c Private Use-EF1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef1f Private Use-EF1F | ef1e Private Use-EF1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef21 Private Use-EF21 | ef20 Private Use-EF20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef23 Private Use-EF23 | ef22 Private Use-EF22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef25 Private Use-EF25 | ef24 Private Use-EF24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef27 Private Use-EF27 | ef26 Private Use-EF26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef29 Private Use-EF29 | ef28 Private Use-EF28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef2b Private Use-EF2B | ef2a Private Use-EF2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef2d Private Use-EF2D | ef2c Private Use-EF2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef2f Private Use-EF2F | ef2e Private Use-EF2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef31 Private Use-EF31 | ef30 Private Use-EF30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef33 Private Use-EF33 | ef32 Private Use-EF32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef35 Private Use-EF35 | ef34 Private Use-EF34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef37 Private Use-EF37 | ef36 Private Use-EF36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef39 Private Use-EF39 | ef38 Private Use-EF38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef3b Private Use-EF3B | ef3a Private Use-EF3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef3d Private Use-EF3D | ef3c Private Use-EF3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef3f Private Use-EF3F | ef3e Private Use-EF3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef41 Private Use-EF41 | ef40 Private Use-EF40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef43 Private Use-EF43 | ef42 Private Use-EF42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef45 Private Use-EF45 | ef44 Private Use-EF44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef47 Private Use-EF47 | ef46 Private Use-EF46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef49 Private Use-EF49 | ef48 Private Use-EF48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef4b Private Use-EF4B | ef4a Private Use-EF4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef4d Private Use-EF4D | ef4c Private Use-EF4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef4f Private Use-EF4F | ef4e Private Use-EF4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef51 Private Use-EF51 | ef50 Private Use-EF50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef53 Private Use-EF53 | ef52 Private Use-EF52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef55 Private Use-EF55 | ef54 Private Use-EF54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef57 Private Use-EF57 | ef56 Private Use-EF56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef59 Private Use-EF59 | ef58 Private Use-EF58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef5b Private Use-EF5B | ef5a Private Use-EF5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef5d Private Use-EF5D | ef5c Private Use-EF5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef5f Private Use-EF5F | ef5e Private Use-EF5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef61 Private Use-EF61 | ef60 Private Use-EF60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef63 Private Use-EF63 | ef62 Private Use-EF62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef65 Private Use-EF65 | ef64 Private Use-EF64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef67 Private Use-EF67 | ef66 Private Use-EF66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef69 Private Use-EF69 | ef68 Private Use-EF68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef6b Private Use-EF6B | ef6a Private Use-EF6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef6d Private Use-EF6D | ef6c Private Use-EF6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef6f Private Use-EF6F | ef6e Private Use-EF6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef71 Private Use-EF71 | ef70 Private Use-EF70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef73 Private Use-EF73 | ef72 Private Use-EF72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef75 Private Use-EF75 | ef74 Private Use-EF74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef77 Private Use-EF77 | ef76 Private Use-EF76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef79 Private Use-EF79 | ef78 Private Use-EF78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef7b Private Use-EF7B | ef7a Private Use-EF7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef7d Private Use-EF7D | ef7c Private Use-EF7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef7f Private Use-EF7F | ef7e Private Use-EF7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef81 Private Use-EF81 | ef80 Private Use-EF80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef83 Private Use-EF83 | ef82 Private Use-EF82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef85 Private Use-EF85 | ef84 Private Use-EF84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef87 Private Use-EF87 | ef86 Private Use-EF86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef89 Private Use-EF89 | ef88 Private Use-EF88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef8b Private Use-EF8B | ef8a Private Use-EF8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef8d Private Use-EF8D | ef8c Private Use-EF8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef8f Private Use-EF8F | ef8e Private Use-EF8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef91 Private Use-EF91 | ef90 Private Use-EF90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef93 Private Use-EF93 | ef92 Private Use-EF92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef95 Private Use-EF95 | ef94 Private Use-EF94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef97 Private Use-EF97 | ef96 Private Use-EF96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef99 Private Use-EF99 | ef98 Private Use-EF98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef9b Private Use-EF9B | ef9a Private Use-EF9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef9d Private Use-EF9D | ef9c Private Use-EF9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef9f Private Use-EF9F | ef9e Private Use-EF9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efa1 Private Use-EFA1 | efa0 Private Use-EFA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efa3 Private Use-EFA3 | efa2 Private Use-EFA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efa5 Private Use-EFA5 | efa4 Private Use-EFA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efa7 Private Use-EFA7 | efa6 Private Use-EFA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efa9 Private Use-EFA9 | efa8 Private Use-EFA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efab Private Use-EFAB | efaa Private Use-EFAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efad Private Use-EFAD | efac Private Use-EFAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efaf Private Use-EFAF | efae Private Use-EFAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efb1 Private Use-EFB1 | efb0 Private Use-EFB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efb3 Private Use-EFB3 | efb2 Private Use-EFB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efb5 Private Use-EFB5 | efb4 Private Use-EFB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efb7 Private Use-EFB7 | efb6 Private Use-EFB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efb9 Private Use-EFB9 | efb8 Private Use-EFB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efbb Private Use-EFBB | efba Private Use-EFBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efbd Private Use-EFBD | efbc Private Use-EFBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efbf Private Use-EFBF | efbe Private Use-EFBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efc1 Private Use-EFC1 | efc0 Private Use-EFC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efc3 Private Use-EFC3 | efc2 Private Use-EFC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efc5 Private Use-EFC5 | efc4 Private Use-EFC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efc7 Private Use-EFC7 | efc6 Private Use-EFC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efc9 Private Use-EFC9 | efc8 Private Use-EFC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efcb Private Use-EFCB | efca Private Use-EFCA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efcd Private Use-EFCD | efcc Private Use-EFCC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efcf Private Use-EFCF | efce Private Use-EFCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efd1 Private Use-EFD1 | efd0 Private Use-EFD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efd3 Private Use-EFD3 | efd2 Private Use-EFD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efd5 Private Use-EFD5 | efd4 Private Use-EFD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efd7 Private Use-EFD7 | efd6 Private Use-EFD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efd9 Private Use-EFD9 | efd8 Private Use-EFD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efdb Private Use-EFDB | efda Private Use-EFDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efdd Private Use-EFDD | efdc Private Use-EFDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efdf Private Use-EFDF | efde Private Use-EFDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efe1 Private Use-EFE1 | efe0 Private Use-EFE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efe3 Private Use-EFE3 | efe2 Private Use-EFE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efe5 Private Use-EFE5 | efe4 Private Use-EFE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efe7 Private Use-EFE7 | efe6 Private Use-EFE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efe9 Private Use-EFE9 | efe8 Private Use-EFE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efeb Private Use-EFEB | efea Private Use-EFEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efed Private Use-EFED | efec Private Use-EFEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efef Private Use-EFEF | efee Private Use-EFEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eff1 Private Use-EFF1 | eff0 Private Use-EFF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eff3 Private Use-EFF3 | eff2 Private Use-EFF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eff5 Private Use-EFF5 | eff4 Private Use-EFF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eff7 Private Use-EFF7 | eff6 Private Use-EFF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eff9 Private Use-EFF9 | eff8 Private Use-EFF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* effb Private Use-EFFB | effa Private Use-EFFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* effd Private Use-EFFD | effc Private Use-EFFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efff Private Use-EFFF | effe Private Use-EFFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f001 Private Use-F001 | f000 Private Use-F000 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f003 Private Use-F003 | f002 Private Use-F002 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f005 Private Use-F005 | f004 Private Use-F004 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f007 Private Use-F007 | f006 Private Use-F006 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f009 Private Use-F009 | f008 Private Use-F008 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f00b Private Use-F00B | f00a Private Use-F00A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f00d Private Use-F00D | f00c Private Use-F00C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f00f Private Use-F00F | f00e Private Use-F00E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f011 Private Use-F011 | f010 Private Use-F010 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f013 Private Use-F013 | f012 Private Use-F012 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f015 Private Use-F015 | f014 Private Use-F014 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f017 Private Use-F017 | f016 Private Use-F016 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f019 Private Use-F019 | f018 Private Use-F018 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f01b Private Use-F01B | f01a Private Use-F01A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f01d Private Use-F01D | f01c Private Use-F01C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f01f Private Use-F01F | f01e Private Use-F01E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f021 Private Use-F021 | f020 Private Use-F020 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f023 Private Use-F023 | f022 Private Use-F022 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f025 Private Use-F025 | f024 Private Use-F024 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f027 Private Use-F027 | f026 Private Use-F026 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f029 Private Use-F029 | f028 Private Use-F028 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f02b Private Use-F02B | f02a Private Use-F02A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f02d Private Use-F02D | f02c Private Use-F02C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f02f Private Use-F02F | f02e Private Use-F02E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f031 Private Use-F031 | f030 Private Use-F030 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f033 Private Use-F033 | f032 Private Use-F032 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f035 Private Use-F035 | f034 Private Use-F034 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f037 Private Use-F037 | f036 Private Use-F036 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f039 Private Use-F039 | f038 Private Use-F038 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f03b Private Use-F03B | f03a Private Use-F03A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f03d Private Use-F03D | f03c Private Use-F03C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f03f Private Use-F03F | f03e Private Use-F03E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f041 Private Use-F041 | f040 Private Use-F040 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f043 Private Use-F043 | f042 Private Use-F042 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f045 Private Use-F045 | f044 Private Use-F044 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f047 Private Use-F047 | f046 Private Use-F046 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f049 Private Use-F049 | f048 Private Use-F048 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f04b Private Use-F04B | f04a Private Use-F04A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f04d Private Use-F04D | f04c Private Use-F04C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f04f Private Use-F04F | f04e Private Use-F04E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f051 Private Use-F051 | f050 Private Use-F050 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f053 Private Use-F053 | f052 Private Use-F052 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f055 Private Use-F055 | f054 Private Use-F054 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f057 Private Use-F057 | f056 Private Use-F056 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f059 Private Use-F059 | f058 Private Use-F058 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f05b Private Use-F05B | f05a Private Use-F05A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f05d Private Use-F05D | f05c Private Use-F05C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f05f Private Use-F05F | f05e Private Use-F05E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f061 Private Use-F061 | f060 Private Use-F060 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f063 Private Use-F063 | f062 Private Use-F062 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f065 Private Use-F065 | f064 Private Use-F064 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f067 Private Use-F067 | f066 Private Use-F066 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f069 Private Use-F069 | f068 Private Use-F068 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f06b Private Use-F06B | f06a Private Use-F06A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f06d Private Use-F06D | f06c Private Use-F06C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f06f Private Use-F06F | f06e Private Use-F06E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f071 Private Use-F071 | f070 Private Use-F070 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f073 Private Use-F073 | f072 Private Use-F072 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f075 Private Use-F075 | f074 Private Use-F074 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f077 Private Use-F077 | f076 Private Use-F076 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f079 Private Use-F079 | f078 Private Use-F078 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f07b Private Use-F07B | f07a Private Use-F07A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f07d Private Use-F07D | f07c Private Use-F07C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f07f Private Use-F07F | f07e Private Use-F07E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f081 Private Use-F081 | f080 Private Use-F080 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f083 Private Use-F083 | f082 Private Use-F082 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f085 Private Use-F085 | f084 Private Use-F084 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f087 Private Use-F087 | f086 Private Use-F086 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f089 Private Use-F089 | f088 Private Use-F088 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f08b Private Use-F08B | f08a Private Use-F08A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f08d Private Use-F08D | f08c Private Use-F08C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f08f Private Use-F08F | f08e Private Use-F08E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f091 Private Use-F091 | f090 Private Use-F090 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f093 Private Use-F093 | f092 Private Use-F092 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f095 Private Use-F095 | f094 Private Use-F094 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f097 Private Use-F097 | f096 Private Use-F096 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f099 Private Use-F099 | f098 Private Use-F098 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f09b Private Use-F09B | f09a Private Use-F09A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f09d Private Use-F09D | f09c Private Use-F09C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f09f Private Use-F09F | f09e Private Use-F09E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0a1 Private Use-F0A1 | f0a0 Private Use-F0A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0a3 Private Use-F0A3 | f0a2 Private Use-F0A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0a5 Private Use-F0A5 | f0a4 Private Use-F0A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0a7 Private Use-F0A7 | f0a6 Private Use-F0A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0a9 Private Use-F0A9 | f0a8 Private Use-F0A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0ab Private Use-F0AB | f0aa Private Use-F0AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0ad Private Use-F0AD | f0ac Private Use-F0AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0af Private Use-F0AF | f0ae Private Use-F0AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0b1 Private Use-F0B1 | f0b0 Private Use-F0B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0b3 Private Use-F0B3 | f0b2 Private Use-F0B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0b5 Private Use-F0B5 | f0b4 Private Use-F0B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0b7 Private Use-F0B7 | f0b6 Private Use-F0B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0b9 Private Use-F0B9 | f0b8 Private Use-F0B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0bb Private Use-F0BB | f0ba Private Use-F0BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0bd Private Use-F0BD | f0bc Private Use-F0BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0bf Private Use-F0BF | f0be Private Use-F0BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0c1 Private Use-F0C1 | f0c0 Private Use-F0C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0c3 Private Use-F0C3 | f0c2 Private Use-F0C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0c5 Private Use-F0C5 | f0c4 Private Use-F0C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0c7 Private Use-F0C7 | f0c6 Private Use-F0C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0c9 Private Use-F0C9 | f0c8 Private Use-F0C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0cb Private Use-F0CB | f0ca Private Use-F0CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0cd Private Use-F0CD | f0cc Private Use-F0CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0cf Private Use-F0CF | f0ce Private Use-F0CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0d1 Private Use-F0D1 | f0d0 Private Use-F0D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0d3 Private Use-F0D3 | f0d2 Private Use-F0D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0d5 Private Use-F0D5 | f0d4 Private Use-F0D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0d7 Private Use-F0D7 | f0d6 Private Use-F0D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0d9 Private Use-F0D9 | f0d8 Private Use-F0D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0db Private Use-F0DB | f0da Private Use-F0DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0dd Private Use-F0DD | f0dc Private Use-F0DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0df Private Use-F0DF | f0de Private Use-F0DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0e1 Private Use-F0E1 | f0e0 Private Use-F0E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0e3 Private Use-F0E3 | f0e2 Private Use-F0E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0e5 Private Use-F0E5 | f0e4 Private Use-F0E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0e7 Private Use-F0E7 | f0e6 Private Use-F0E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0e9 Private Use-F0E9 | f0e8 Private Use-F0E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0eb Private Use-F0EB | f0ea Private Use-F0EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0ed Private Use-F0ED | f0ec Private Use-F0EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0ef Private Use-F0EF | f0ee Private Use-F0EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0f1 Private Use-F0F1 | f0f0 Private Use-F0F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0f3 Private Use-F0F3 | f0f2 Private Use-F0F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0f5 Private Use-F0F5 | f0f4 Private Use-F0F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0f7 Private Use-F0F7 | f0f6 Private Use-F0F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0f9 Private Use-F0F9 | f0f8 Private Use-F0F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0fb Private Use-F0FB | f0fa Private Use-F0FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0fd Private Use-F0FD | f0fc Private Use-F0FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0ff Private Use-F0FF | f0fe Private Use-F0FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f101 Private Use-F101 | f100 Private Use-F100 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f103 Private Use-F103 | f102 Private Use-F102 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f105 Private Use-F105 | f104 Private Use-F104 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f107 Private Use-F107 | f106 Private Use-F106 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f109 Private Use-F109 | f108 Private Use-F108 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f10b Private Use-F10B | f10a Private Use-F10A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f10d Private Use-F10D | f10c Private Use-F10C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f10f Private Use-F10F | f10e Private Use-F10E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f111 Private Use-F111 | f110 Private Use-F110 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f113 Private Use-F113 | f112 Private Use-F112 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f115 Private Use-F115 | f114 Private Use-F114 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f117 Private Use-F117 | f116 Private Use-F116 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f119 Private Use-F119 | f118 Private Use-F118 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f11b Private Use-F11B | f11a Private Use-F11A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f11d Private Use-F11D | f11c Private Use-F11C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f11f Private Use-F11F | f11e Private Use-F11E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f121 Private Use-F121 | f120 Private Use-F120 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f123 Private Use-F123 | f122 Private Use-F122 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f125 Private Use-F125 | f124 Private Use-F124 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f127 Private Use-F127 | f126 Private Use-F126 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f129 Private Use-F129 | f128 Private Use-F128 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f12b Private Use-F12B | f12a Private Use-F12A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f12d Private Use-F12D | f12c Private Use-F12C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f12f Private Use-F12F | f12e Private Use-F12E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f131 Private Use-F131 | f130 Private Use-F130 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f133 Private Use-F133 | f132 Private Use-F132 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f135 Private Use-F135 | f134 Private Use-F134 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f137 Private Use-F137 | f136 Private Use-F136 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f139 Private Use-F139 | f138 Private Use-F138 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f13b Private Use-F13B | f13a Private Use-F13A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f13d Private Use-F13D | f13c Private Use-F13C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f13f Private Use-F13F | f13e Private Use-F13E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f141 Private Use-F141 | f140 Private Use-F140 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f143 Private Use-F143 | f142 Private Use-F142 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f145 Private Use-F145 | f144 Private Use-F144 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f147 Private Use-F147 | f146 Private Use-F146 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f149 Private Use-F149 | f148 Private Use-F148 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f14b Private Use-F14B | f14a Private Use-F14A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f14d Private Use-F14D | f14c Private Use-F14C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f14f Private Use-F14F | f14e Private Use-F14E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f151 Private Use-F151 | f150 Private Use-F150 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f153 Private Use-F153 | f152 Private Use-F152 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f155 Private Use-F155 | f154 Private Use-F154 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f157 Private Use-F157 | f156 Private Use-F156 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f159 Private Use-F159 | f158 Private Use-F158 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f15b Private Use-F15B | f15a Private Use-F15A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f15d Private Use-F15D | f15c Private Use-F15C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f15f Private Use-F15F | f15e Private Use-F15E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f161 Private Use-F161 | f160 Private Use-F160 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f163 Private Use-F163 | f162 Private Use-F162 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f165 Private Use-F165 | f164 Private Use-F164 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f167 Private Use-F167 | f166 Private Use-F166 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f169 Private Use-F169 | f168 Private Use-F168 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f16b Private Use-F16B | f16a Private Use-F16A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f16d Private Use-F16D | f16c Private Use-F16C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f16f Private Use-F16F | f16e Private Use-F16E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f171 Private Use-F171 | f170 Private Use-F170 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f173 Private Use-F173 | f172 Private Use-F172 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f175 Private Use-F175 | f174 Private Use-F174 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f177 Private Use-F177 | f176 Private Use-F176 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f179 Private Use-F179 | f178 Private Use-F178 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f17b Private Use-F17B | f17a Private Use-F17A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f17d Private Use-F17D | f17c Private Use-F17C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f17f Private Use-F17F | f17e Private Use-F17E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f181 Private Use-F181 | f180 Private Use-F180 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f183 Private Use-F183 | f182 Private Use-F182 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f185 Private Use-F185 | f184 Private Use-F184 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f187 Private Use-F187 | f186 Private Use-F186 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f189 Private Use-F189 | f188 Private Use-F188 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f18b Private Use-F18B | f18a Private Use-F18A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f18d Private Use-F18D | f18c Private Use-F18C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f18f Private Use-F18F | f18e Private Use-F18E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f191 Private Use-F191 | f190 Private Use-F190 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f193 Private Use-F193 | f192 Private Use-F192 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f195 Private Use-F195 | f194 Private Use-F194 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f197 Private Use-F197 | f196 Private Use-F196 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f199 Private Use-F199 | f198 Private Use-F198 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f19b Private Use-F19B | f19a Private Use-F19A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f19d Private Use-F19D | f19c Private Use-F19C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f19f Private Use-F19F | f19e Private Use-F19E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1a1 Private Use-F1A1 | f1a0 Private Use-F1A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1a3 Private Use-F1A3 | f1a2 Private Use-F1A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1a5 Private Use-F1A5 | f1a4 Private Use-F1A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1a7 Private Use-F1A7 | f1a6 Private Use-F1A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1a9 Private Use-F1A9 | f1a8 Private Use-F1A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1ab Private Use-F1AB | f1aa Private Use-F1AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1ad Private Use-F1AD | f1ac Private Use-F1AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1af Private Use-F1AF | f1ae Private Use-F1AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1b1 Private Use-F1B1 | f1b0 Private Use-F1B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1b3 Private Use-F1B3 | f1b2 Private Use-F1B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1b5 Private Use-F1B5 | f1b4 Private Use-F1B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1b7 Private Use-F1B7 | f1b6 Private Use-F1B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1b9 Private Use-F1B9 | f1b8 Private Use-F1B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1bb Private Use-F1BB | f1ba Private Use-F1BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1bd Private Use-F1BD | f1bc Private Use-F1BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1bf Private Use-F1BF | f1be Private Use-F1BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1c1 Private Use-F1C1 | f1c0 Private Use-F1C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1c3 Private Use-F1C3 | f1c2 Private Use-F1C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1c5 Private Use-F1C5 | f1c4 Private Use-F1C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1c7 Private Use-F1C7 | f1c6 Private Use-F1C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1c9 Private Use-F1C9 | f1c8 Private Use-F1C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1cb Private Use-F1CB | f1ca Private Use-F1CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1cd Private Use-F1CD | f1cc Private Use-F1CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1cf Private Use-F1CF | f1ce Private Use-F1CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1d1 Private Use-F1D1 | f1d0 Private Use-F1D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1d3 Private Use-F1D3 | f1d2 Private Use-F1D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1d5 Private Use-F1D5 | f1d4 Private Use-F1D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1d7 Private Use-F1D7 | f1d6 Private Use-F1D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1d9 Private Use-F1D9 | f1d8 Private Use-F1D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1db Private Use-F1DB | f1da Private Use-F1DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1dd Private Use-F1DD | f1dc Private Use-F1DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1df Private Use-F1DF | f1de Private Use-F1DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1e1 Private Use-F1E1 | f1e0 Private Use-F1E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1e3 Private Use-F1E3 | f1e2 Private Use-F1E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1e5 Private Use-F1E5 | f1e4 Private Use-F1E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1e7 Private Use-F1E7 | f1e6 Private Use-F1E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1e9 Private Use-F1E9 | f1e8 Private Use-F1E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1eb Private Use-F1EB | f1ea Private Use-F1EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1ed Private Use-F1ED | f1ec Private Use-F1EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1ef Private Use-F1EF | f1ee Private Use-F1EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1f1 Private Use-F1F1 | f1f0 Private Use-F1F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1f3 Private Use-F1F3 | f1f2 Private Use-F1F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1f5 Private Use-F1F5 | f1f4 Private Use-F1F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1f7 Private Use-F1F7 | f1f6 Private Use-F1F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1f9 Private Use-F1F9 | f1f8 Private Use-F1F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1fb Private Use-F1FB | f1fa Private Use-F1FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1fd Private Use-F1FD | f1fc Private Use-F1FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1ff Private Use-F1FF | f1fe Private Use-F1FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f201 Private Use-F201 | f200 Private Use-F200 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f203 Private Use-F203 | f202 Private Use-F202 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f205 Private Use-F205 | f204 Private Use-F204 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f207 Private Use-F207 | f206 Private Use-F206 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f209 Private Use-F209 | f208 Private Use-F208 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f20b Private Use-F20B | f20a Private Use-F20A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f20d Private Use-F20D | f20c Private Use-F20C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f20f Private Use-F20F | f20e Private Use-F20E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f211 Private Use-F211 | f210 Private Use-F210 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f213 Private Use-F213 | f212 Private Use-F212 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f215 Private Use-F215 | f214 Private Use-F214 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f217 Private Use-F217 | f216 Private Use-F216 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f219 Private Use-F219 | f218 Private Use-F218 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f21b Private Use-F21B | f21a Private Use-F21A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f21d Private Use-F21D | f21c Private Use-F21C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f21f Private Use-F21F | f21e Private Use-F21E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f221 Private Use-F221 | f220 Private Use-F220 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f223 Private Use-F223 | f222 Private Use-F222 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f225 Private Use-F225 | f224 Private Use-F224 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f227 Private Use-F227 | f226 Private Use-F226 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f229 Private Use-F229 | f228 Private Use-F228 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f22b Private Use-F22B | f22a Private Use-F22A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f22d Private Use-F22D | f22c Private Use-F22C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f22f Private Use-F22F | f22e Private Use-F22E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f231 Private Use-F231 | f230 Private Use-F230 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f233 Private Use-F233 | f232 Private Use-F232 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f235 Private Use-F235 | f234 Private Use-F234 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f237 Private Use-F237 | f236 Private Use-F236 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f239 Private Use-F239 | f238 Private Use-F238 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f23b Private Use-F23B | f23a Private Use-F23A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f23d Private Use-F23D | f23c Private Use-F23C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f23f Private Use-F23F | f23e Private Use-F23E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f241 Private Use-F241 | f240 Private Use-F240 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f243 Private Use-F243 | f242 Private Use-F242 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f245 Private Use-F245 | f244 Private Use-F244 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f247 Private Use-F247 | f246 Private Use-F246 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f249 Private Use-F249 | f248 Private Use-F248 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f24b Private Use-F24B | f24a Private Use-F24A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f24d Private Use-F24D | f24c Private Use-F24C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f24f Private Use-F24F | f24e Private Use-F24E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f251 Private Use-F251 | f250 Private Use-F250 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f253 Private Use-F253 | f252 Private Use-F252 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f255 Private Use-F255 | f254 Private Use-F254 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f257 Private Use-F257 | f256 Private Use-F256 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f259 Private Use-F259 | f258 Private Use-F258 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f25b Private Use-F25B | f25a Private Use-F25A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f25d Private Use-F25D | f25c Private Use-F25C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f25f Private Use-F25F | f25e Private Use-F25E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f261 Private Use-F261 | f260 Private Use-F260 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f263 Private Use-F263 | f262 Private Use-F262 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f265 Private Use-F265 | f264 Private Use-F264 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f267 Private Use-F267 | f266 Private Use-F266 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f269 Private Use-F269 | f268 Private Use-F268 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f26b Private Use-F26B | f26a Private Use-F26A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f26d Private Use-F26D | f26c Private Use-F26C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f26f Private Use-F26F | f26e Private Use-F26E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f271 Private Use-F271 | f270 Private Use-F270 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f273 Private Use-F273 | f272 Private Use-F272 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f275 Private Use-F275 | f274 Private Use-F274 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f277 Private Use-F277 | f276 Private Use-F276 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f279 Private Use-F279 | f278 Private Use-F278 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f27b Private Use-F27B | f27a Private Use-F27A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f27d Private Use-F27D | f27c Private Use-F27C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f27f Private Use-F27F | f27e Private Use-F27E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f281 Private Use-F281 | f280 Private Use-F280 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f283 Private Use-F283 | f282 Private Use-F282 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f285 Private Use-F285 | f284 Private Use-F284 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f287 Private Use-F287 | f286 Private Use-F286 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f289 Private Use-F289 | f288 Private Use-F288 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f28b Private Use-F28B | f28a Private Use-F28A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f28d Private Use-F28D | f28c Private Use-F28C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f28f Private Use-F28F | f28e Private Use-F28E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f291 Private Use-F291 | f290 Private Use-F290 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f293 Private Use-F293 | f292 Private Use-F292 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f295 Private Use-F295 | f294 Private Use-F294 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f297 Private Use-F297 | f296 Private Use-F296 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f299 Private Use-F299 | f298 Private Use-F298 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f29b Private Use-F29B | f29a Private Use-F29A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f29d Private Use-F29D | f29c Private Use-F29C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f29f Private Use-F29F | f29e Private Use-F29E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2a1 Private Use-F2A1 | f2a0 Private Use-F2A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2a3 Private Use-F2A3 | f2a2 Private Use-F2A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2a5 Private Use-F2A5 | f2a4 Private Use-F2A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2a7 Private Use-F2A7 | f2a6 Private Use-F2A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2a9 Private Use-F2A9 | f2a8 Private Use-F2A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2ab Private Use-F2AB | f2aa Private Use-F2AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2ad Private Use-F2AD | f2ac Private Use-F2AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2af Private Use-F2AF | f2ae Private Use-F2AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2b1 Private Use-F2B1 | f2b0 Private Use-F2B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2b3 Private Use-F2B3 | f2b2 Private Use-F2B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2b5 Private Use-F2B5 | f2b4 Private Use-F2B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2b7 Private Use-F2B7 | f2b6 Private Use-F2B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2b9 Private Use-F2B9 | f2b8 Private Use-F2B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2bb Private Use-F2BB | f2ba Private Use-F2BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2bd Private Use-F2BD | f2bc Private Use-F2BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2bf Private Use-F2BF | f2be Private Use-F2BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2c1 Private Use-F2C1 | f2c0 Private Use-F2C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2c3 Private Use-F2C3 | f2c2 Private Use-F2C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2c5 Private Use-F2C5 | f2c4 Private Use-F2C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2c7 Private Use-F2C7 | f2c6 Private Use-F2C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2c9 Private Use-F2C9 | f2c8 Private Use-F2C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2cb Private Use-F2CB | f2ca Private Use-F2CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2cd Private Use-F2CD | f2cc Private Use-F2CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2cf Private Use-F2CF | f2ce Private Use-F2CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2d1 Private Use-F2D1 | f2d0 Private Use-F2D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2d3 Private Use-F2D3 | f2d2 Private Use-F2D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2d5 Private Use-F2D5 | f2d4 Private Use-F2D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2d7 Private Use-F2D7 | f2d6 Private Use-F2D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2d9 Private Use-F2D9 | f2d8 Private Use-F2D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2db Private Use-F2DB | f2da Private Use-F2DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2dd Private Use-F2DD | f2dc Private Use-F2DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2df Private Use-F2DF | f2de Private Use-F2DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2e1 Private Use-F2E1 | f2e0 Private Use-F2E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2e3 Private Use-F2E3 | f2e2 Private Use-F2E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2e5 Private Use-F2E5 | f2e4 Private Use-F2E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2e7 Private Use-F2E7 | f2e6 Private Use-F2E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2e9 Private Use-F2E9 | f2e8 Private Use-F2E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2eb Private Use-F2EB | f2ea Private Use-F2EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2ed Private Use-F2ED | f2ec Private Use-F2EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2ef Private Use-F2EF | f2ee Private Use-F2EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2f1 Private Use-F2F1 | f2f0 Private Use-F2F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2f3 Private Use-F2F3 | f2f2 Private Use-F2F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2f5 Private Use-F2F5 | f2f4 Private Use-F2F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2f7 Private Use-F2F7 | f2f6 Private Use-F2F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2f9 Private Use-F2F9 | f2f8 Private Use-F2F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2fb Private Use-F2FB | f2fa Private Use-F2FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2fd Private Use-F2FD | f2fc Private Use-F2FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2ff Private Use-F2FF | f2fe Private Use-F2FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f301 Private Use-F301 | f300 Private Use-F300 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f303 Private Use-F303 | f302 Private Use-F302 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f305 Private Use-F305 | f304 Private Use-F304 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f307 Private Use-F307 | f306 Private Use-F306 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f309 Private Use-F309 | f308 Private Use-F308 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f30b Private Use-F30B | f30a Private Use-F30A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f30d Private Use-F30D | f30c Private Use-F30C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f30f Private Use-F30F | f30e Private Use-F30E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f311 Private Use-F311 | f310 Private Use-F310 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f313 Private Use-F313 | f312 Private Use-F312 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f315 Private Use-F315 | f314 Private Use-F314 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f317 Private Use-F317 | f316 Private Use-F316 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f319 Private Use-F319 | f318 Private Use-F318 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f31b Private Use-F31B | f31a Private Use-F31A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f31d Private Use-F31D | f31c Private Use-F31C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f31f Private Use-F31F | f31e Private Use-F31E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f321 Private Use-F321 | f320 Private Use-F320 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f323 Private Use-F323 | f322 Private Use-F322 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f325 Private Use-F325 | f324 Private Use-F324 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f327 Private Use-F327 | f326 Private Use-F326 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f329 Private Use-F329 | f328 Private Use-F328 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f32b Private Use-F32B | f32a Private Use-F32A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f32d Private Use-F32D | f32c Private Use-F32C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f32f Private Use-F32F | f32e Private Use-F32E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f331 Private Use-F331 | f330 Private Use-F330 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f333 Private Use-F333 | f332 Private Use-F332 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f335 Private Use-F335 | f334 Private Use-F334 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f337 Private Use-F337 | f336 Private Use-F336 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f339 Private Use-F339 | f338 Private Use-F338 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f33b Private Use-F33B | f33a Private Use-F33A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f33d Private Use-F33D | f33c Private Use-F33C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f33f Private Use-F33F | f33e Private Use-F33E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f341 Private Use-F341 | f340 Private Use-F340 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f343 Private Use-F343 | f342 Private Use-F342 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f345 Private Use-F345 | f344 Private Use-F344 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f347 Private Use-F347 | f346 Private Use-F346 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f349 Private Use-F349 | f348 Private Use-F348 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f34b Private Use-F34B | f34a Private Use-F34A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f34d Private Use-F34D | f34c Private Use-F34C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f34f Private Use-F34F | f34e Private Use-F34E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f351 Private Use-F351 | f350 Private Use-F350 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f353 Private Use-F353 | f352 Private Use-F352 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f355 Private Use-F355 | f354 Private Use-F354 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f357 Private Use-F357 | f356 Private Use-F356 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f359 Private Use-F359 | f358 Private Use-F358 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f35b Private Use-F35B | f35a Private Use-F35A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f35d Private Use-F35D | f35c Private Use-F35C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f35f Private Use-F35F | f35e Private Use-F35E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f361 Private Use-F361 | f360 Private Use-F360 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f363 Private Use-F363 | f362 Private Use-F362 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f365 Private Use-F365 | f364 Private Use-F364 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f367 Private Use-F367 | f366 Private Use-F366 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f369 Private Use-F369 | f368 Private Use-F368 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f36b Private Use-F36B | f36a Private Use-F36A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f36d Private Use-F36D | f36c Private Use-F36C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f36f Private Use-F36F | f36e Private Use-F36E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f371 Private Use-F371 | f370 Private Use-F370 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f373 Private Use-F373 | f372 Private Use-F372 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f375 Private Use-F375 | f374 Private Use-F374 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f377 Private Use-F377 | f376 Private Use-F376 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f379 Private Use-F379 | f378 Private Use-F378 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f37b Private Use-F37B | f37a Private Use-F37A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f37d Private Use-F37D | f37c Private Use-F37C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f37f Private Use-F37F | f37e Private Use-F37E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f381 Private Use-F381 | f380 Private Use-F380 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f383 Private Use-F383 | f382 Private Use-F382 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f385 Private Use-F385 | f384 Private Use-F384 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f387 Private Use-F387 | f386 Private Use-F386 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f389 Private Use-F389 | f388 Private Use-F388 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f38b Private Use-F38B | f38a Private Use-F38A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f38d Private Use-F38D | f38c Private Use-F38C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f38f Private Use-F38F | f38e Private Use-F38E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f391 Private Use-F391 | f390 Private Use-F390 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f393 Private Use-F393 | f392 Private Use-F392 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f395 Private Use-F395 | f394 Private Use-F394 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f397 Private Use-F397 | f396 Private Use-F396 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f399 Private Use-F399 | f398 Private Use-F398 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f39b Private Use-F39B | f39a Private Use-F39A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f39d Private Use-F39D | f39c Private Use-F39C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f39f Private Use-F39F | f39e Private Use-F39E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3a1 Private Use-F3A1 | f3a0 Private Use-F3A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3a3 Private Use-F3A3 | f3a2 Private Use-F3A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3a5 Private Use-F3A5 | f3a4 Private Use-F3A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3a7 Private Use-F3A7 | f3a6 Private Use-F3A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3a9 Private Use-F3A9 | f3a8 Private Use-F3A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3ab Private Use-F3AB | f3aa Private Use-F3AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3ad Private Use-F3AD | f3ac Private Use-F3AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3af Private Use-F3AF | f3ae Private Use-F3AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3b1 Private Use-F3B1 | f3b0 Private Use-F3B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3b3 Private Use-F3B3 | f3b2 Private Use-F3B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3b5 Private Use-F3B5 | f3b4 Private Use-F3B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3b7 Private Use-F3B7 | f3b6 Private Use-F3B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3b9 Private Use-F3B9 | f3b8 Private Use-F3B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3bb Private Use-F3BB | f3ba Private Use-F3BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3bd Private Use-F3BD | f3bc Private Use-F3BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3bf Private Use-F3BF | f3be Private Use-F3BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3c1 Private Use-F3C1 | f3c0 Private Use-F3C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3c3 Private Use-F3C3 | f3c2 Private Use-F3C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3c5 Private Use-F3C5 | f3c4 Private Use-F3C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3c7 Private Use-F3C7 | f3c6 Private Use-F3C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3c9 Private Use-F3C9 | f3c8 Private Use-F3C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3cb Private Use-F3CB | f3ca Private Use-F3CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3cd Private Use-F3CD | f3cc Private Use-F3CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3cf Private Use-F3CF | f3ce Private Use-F3CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3d1 Private Use-F3D1 | f3d0 Private Use-F3D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3d3 Private Use-F3D3 | f3d2 Private Use-F3D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3d5 Private Use-F3D5 | f3d4 Private Use-F3D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3d7 Private Use-F3D7 | f3d6 Private Use-F3D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3d9 Private Use-F3D9 | f3d8 Private Use-F3D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3db Private Use-F3DB | f3da Private Use-F3DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3dd Private Use-F3DD | f3dc Private Use-F3DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3df Private Use-F3DF | f3de Private Use-F3DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3e1 Private Use-F3E1 | f3e0 Private Use-F3E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3e3 Private Use-F3E3 | f3e2 Private Use-F3E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3e5 Private Use-F3E5 | f3e4 Private Use-F3E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3e7 Private Use-F3E7 | f3e6 Private Use-F3E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3e9 Private Use-F3E9 | f3e8 Private Use-F3E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3eb Private Use-F3EB | f3ea Private Use-F3EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3ed Private Use-F3ED | f3ec Private Use-F3EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3ef Private Use-F3EF | f3ee Private Use-F3EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3f1 Private Use-F3F1 | f3f0 Private Use-F3F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3f3 Private Use-F3F3 | f3f2 Private Use-F3F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3f5 Private Use-F3F5 | f3f4 Private Use-F3F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3f7 Private Use-F3F7 | f3f6 Private Use-F3F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3f9 Private Use-F3F9 | f3f8 Private Use-F3F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3fb Private Use-F3FB | f3fa Private Use-F3FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3fd Private Use-F3FD | f3fc Private Use-F3FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3ff Private Use-F3FF | f3fe Private Use-F3FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f401 Private Use-F401 | f400 Private Use-F400 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f403 Private Use-F403 | f402 Private Use-F402 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f405 Private Use-F405 | f404 Private Use-F404 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f407 Private Use-F407 | f406 Private Use-F406 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f409 Private Use-F409 | f408 Private Use-F408 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f40b Private Use-F40B | f40a Private Use-F40A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f40d Private Use-F40D | f40c Private Use-F40C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f40f Private Use-F40F | f40e Private Use-F40E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f411 Private Use-F411 | f410 Private Use-F410 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f413 Private Use-F413 | f412 Private Use-F412 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f415 Private Use-F415 | f414 Private Use-F414 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f417 Private Use-F417 | f416 Private Use-F416 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f419 Private Use-F419 | f418 Private Use-F418 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f41b Private Use-F41B | f41a Private Use-F41A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f41d Private Use-F41D | f41c Private Use-F41C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f41f Private Use-F41F | f41e Private Use-F41E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f421 Private Use-F421 | f420 Private Use-F420 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f423 Private Use-F423 | f422 Private Use-F422 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f425 Private Use-F425 | f424 Private Use-F424 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f427 Private Use-F427 | f426 Private Use-F426 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f429 Private Use-F429 | f428 Private Use-F428 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f42b Private Use-F42B | f42a Private Use-F42A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f42d Private Use-F42D | f42c Private Use-F42C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f42f Private Use-F42F | f42e Private Use-F42E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f431 Private Use-F431 | f430 Private Use-F430 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f433 Private Use-F433 | f432 Private Use-F432 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f435 Private Use-F435 | f434 Private Use-F434 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f437 Private Use-F437 | f436 Private Use-F436 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f439 Private Use-F439 | f438 Private Use-F438 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f43b Private Use-F43B | f43a Private Use-F43A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f43d Private Use-F43D | f43c Private Use-F43C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f43f Private Use-F43F | f43e Private Use-F43E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f441 Private Use-F441 | f440 Private Use-F440 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f443 Private Use-F443 | f442 Private Use-F442 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f445 Private Use-F445 | f444 Private Use-F444 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f447 Private Use-F447 | f446 Private Use-F446 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f449 Private Use-F449 | f448 Private Use-F448 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f44b Private Use-F44B | f44a Private Use-F44A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f44d Private Use-F44D | f44c Private Use-F44C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f44f Private Use-F44F | f44e Private Use-F44E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f451 Private Use-F451 | f450 Private Use-F450 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f453 Private Use-F453 | f452 Private Use-F452 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f455 Private Use-F455 | f454 Private Use-F454 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f457 Private Use-F457 | f456 Private Use-F456 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f459 Private Use-F459 | f458 Private Use-F458 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f45b Private Use-F45B | f45a Private Use-F45A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f45d Private Use-F45D | f45c Private Use-F45C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f45f Private Use-F45F | f45e Private Use-F45E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f461 Private Use-F461 | f460 Private Use-F460 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f463 Private Use-F463 | f462 Private Use-F462 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f465 Private Use-F465 | f464 Private Use-F464 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f467 Private Use-F467 | f466 Private Use-F466 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f469 Private Use-F469 | f468 Private Use-F468 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f46b Private Use-F46B | f46a Private Use-F46A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f46d Private Use-F46D | f46c Private Use-F46C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f46f Private Use-F46F | f46e Private Use-F46E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f471 Private Use-F471 | f470 Private Use-F470 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f473 Private Use-F473 | f472 Private Use-F472 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f475 Private Use-F475 | f474 Private Use-F474 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f477 Private Use-F477 | f476 Private Use-F476 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f479 Private Use-F479 | f478 Private Use-F478 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f47b Private Use-F47B | f47a Private Use-F47A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f47d Private Use-F47D | f47c Private Use-F47C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f47f Private Use-F47F | f47e Private Use-F47E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f481 Private Use-F481 | f480 Private Use-F480 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f483 Private Use-F483 | f482 Private Use-F482 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f485 Private Use-F485 | f484 Private Use-F484 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f487 Private Use-F487 | f486 Private Use-F486 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f489 Private Use-F489 | f488 Private Use-F488 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f48b Private Use-F48B | f48a Private Use-F48A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f48d Private Use-F48D | f48c Private Use-F48C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f48f Private Use-F48F | f48e Private Use-F48E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f491 Private Use-F491 | f490 Private Use-F490 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f493 Private Use-F493 | f492 Private Use-F492 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f495 Private Use-F495 | f494 Private Use-F494 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f497 Private Use-F497 | f496 Private Use-F496 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f499 Private Use-F499 | f498 Private Use-F498 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f49b Private Use-F49B | f49a Private Use-F49A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f49d Private Use-F49D | f49c Private Use-F49C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f49f Private Use-F49F | f49e Private Use-F49E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4a1 Private Use-F4A1 | f4a0 Private Use-F4A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4a3 Private Use-F4A3 | f4a2 Private Use-F4A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4a5 Private Use-F4A5 | f4a4 Private Use-F4A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4a7 Private Use-F4A7 | f4a6 Private Use-F4A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4a9 Private Use-F4A9 | f4a8 Private Use-F4A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4ab Private Use-F4AB | f4aa Private Use-F4AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4ad Private Use-F4AD | f4ac Private Use-F4AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4af Private Use-F4AF | f4ae Private Use-F4AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4b1 Private Use-F4B1 | f4b0 Private Use-F4B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4b3 Private Use-F4B3 | f4b2 Private Use-F4B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4b5 Private Use-F4B5 | f4b4 Private Use-F4B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4b7 Private Use-F4B7 | f4b6 Private Use-F4B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4b9 Private Use-F4B9 | f4b8 Private Use-F4B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4bb Private Use-F4BB | f4ba Private Use-F4BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4bd Private Use-F4BD | f4bc Private Use-F4BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4bf Private Use-F4BF | f4be Private Use-F4BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4c1 Private Use-F4C1 | f4c0 Private Use-F4C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4c3 Private Use-F4C3 | f4c2 Private Use-F4C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4c5 Private Use-F4C5 | f4c4 Private Use-F4C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4c7 Private Use-F4C7 | f4c6 Private Use-F4C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4c9 Private Use-F4C9 | f4c8 Private Use-F4C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4cb Private Use-F4CB | f4ca Private Use-F4CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4cd Private Use-F4CD | f4cc Private Use-F4CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4cf Private Use-F4CF | f4ce Private Use-F4CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4d1 Private Use-F4D1 | f4d0 Private Use-F4D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4d3 Private Use-F4D3 | f4d2 Private Use-F4D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4d5 Private Use-F4D5 | f4d4 Private Use-F4D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4d7 Private Use-F4D7 | f4d6 Private Use-F4D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4d9 Private Use-F4D9 | f4d8 Private Use-F4D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4db Private Use-F4DB | f4da Private Use-F4DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4dd Private Use-F4DD | f4dc Private Use-F4DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4df Private Use-F4DF | f4de Private Use-F4DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4e1 Private Use-F4E1 | f4e0 Private Use-F4E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4e3 Private Use-F4E3 | f4e2 Private Use-F4E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4e5 Private Use-F4E5 | f4e4 Private Use-F4E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4e7 Private Use-F4E7 | f4e6 Private Use-F4E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4e9 Private Use-F4E9 | f4e8 Private Use-F4E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4eb Private Use-F4EB | f4ea Private Use-F4EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4ed Private Use-F4ED | f4ec Private Use-F4EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4ef Private Use-F4EF | f4ee Private Use-F4EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4f1 Private Use-F4F1 | f4f0 Private Use-F4F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4f3 Private Use-F4F3 | f4f2 Private Use-F4F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4f5 Private Use-F4F5 | f4f4 Private Use-F4F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4f7 Private Use-F4F7 | f4f6 Private Use-F4F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4f9 Private Use-F4F9 | f4f8 Private Use-F4F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4fb Private Use-F4FB | f4fa Private Use-F4FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4fd Private Use-F4FD | f4fc Private Use-F4FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4ff Private Use-F4FF | f4fe Private Use-F4FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f501 Private Use-F501 | f500 Private Use-F500 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f503 Private Use-F503 | f502 Private Use-F502 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f505 Private Use-F505 | f504 Private Use-F504 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f507 Private Use-F507 | f506 Private Use-F506 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f509 Private Use-F509 | f508 Private Use-F508 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f50b Private Use-F50B | f50a Private Use-F50A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f50d Private Use-F50D | f50c Private Use-F50C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f50f Private Use-F50F | f50e Private Use-F50E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f511 Private Use-F511 | f510 Private Use-F510 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f513 Private Use-F513 | f512 Private Use-F512 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f515 Private Use-F515 | f514 Private Use-F514 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f517 Private Use-F517 | f516 Private Use-F516 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f519 Private Use-F519 | f518 Private Use-F518 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f51b Private Use-F51B | f51a Private Use-F51A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f51d Private Use-F51D | f51c Private Use-F51C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f51f Private Use-F51F | f51e Private Use-F51E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f521 Private Use-F521 | f520 Private Use-F520 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f523 Private Use-F523 | f522 Private Use-F522 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f525 Private Use-F525 | f524 Private Use-F524 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f527 Private Use-F527 | f526 Private Use-F526 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f529 Private Use-F529 | f528 Private Use-F528 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f52b Private Use-F52B | f52a Private Use-F52A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f52d Private Use-F52D | f52c Private Use-F52C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f52f Private Use-F52F | f52e Private Use-F52E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f531 Private Use-F531 | f530 Private Use-F530 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f533 Private Use-F533 | f532 Private Use-F532 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f535 Private Use-F535 | f534 Private Use-F534 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f537 Private Use-F537 | f536 Private Use-F536 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f539 Private Use-F539 | f538 Private Use-F538 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f53b Private Use-F53B | f53a Private Use-F53A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f53d Private Use-F53D | f53c Private Use-F53C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f53f Private Use-F53F | f53e Private Use-F53E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f541 Private Use-F541 | f540 Private Use-F540 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f543 Private Use-F543 | f542 Private Use-F542 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f545 Private Use-F545 | f544 Private Use-F544 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f547 Private Use-F547 | f546 Private Use-F546 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f549 Private Use-F549 | f548 Private Use-F548 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f54b Private Use-F54B | f54a Private Use-F54A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f54d Private Use-F54D | f54c Private Use-F54C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f54f Private Use-F54F | f54e Private Use-F54E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f551 Private Use-F551 | f550 Private Use-F550 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f553 Private Use-F553 | f552 Private Use-F552 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f555 Private Use-F555 | f554 Private Use-F554 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f557 Private Use-F557 | f556 Private Use-F556 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f559 Private Use-F559 | f558 Private Use-F558 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f55b Private Use-F55B | f55a Private Use-F55A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f55d Private Use-F55D | f55c Private Use-F55C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f55f Private Use-F55F | f55e Private Use-F55E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f561 Private Use-F561 | f560 Private Use-F560 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f563 Private Use-F563 | f562 Private Use-F562 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f565 Private Use-F565 | f564 Private Use-F564 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f567 Private Use-F567 | f566 Private Use-F566 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f569 Private Use-F569 | f568 Private Use-F568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f56b Private Use-F56B | f56a Private Use-F56A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f56d Private Use-F56D | f56c Private Use-F56C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f56f Private Use-F56F | f56e Private Use-F56E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f571 Private Use-F571 | f570 Private Use-F570 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f573 Private Use-F573 | f572 Private Use-F572 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f575 Private Use-F575 | f574 Private Use-F574 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f577 Private Use-F577 | f576 Private Use-F576 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f579 Private Use-F579 | f578 Private Use-F578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f57b Private Use-F57B | f57a Private Use-F57A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f57d Private Use-F57D | f57c Private Use-F57C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f57f Private Use-F57F | f57e Private Use-F57E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f581 Private Use-F581 | f580 Private Use-F580 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f583 Private Use-F583 | f582 Private Use-F582 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f585 Private Use-F585 | f584 Private Use-F584 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f587 Private Use-F587 | f586 Private Use-F586 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f589 Private Use-F589 | f588 Private Use-F588 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f58b Private Use-F58B | f58a Private Use-F58A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f58d Private Use-F58D | f58c Private Use-F58C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f58f Private Use-F58F | f58e Private Use-F58E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f591 Private Use-F591 | f590 Private Use-F590 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f593 Private Use-F593 | f592 Private Use-F592 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f595 Private Use-F595 | f594 Private Use-F594 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f597 Private Use-F597 | f596 Private Use-F596 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f599 Private Use-F599 | f598 Private Use-F598 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f59b Private Use-F59B | f59a Private Use-F59A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f59d Private Use-F59D | f59c Private Use-F59C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f59f Private Use-F59F | f59e Private Use-F59E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5a1 Private Use-F5A1 | f5a0 Private Use-F5A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5a3 Private Use-F5A3 | f5a2 Private Use-F5A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5a5 Private Use-F5A5 | f5a4 Private Use-F5A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5a7 Private Use-F5A7 | f5a6 Private Use-F5A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5a9 Private Use-F5A9 | f5a8 Private Use-F5A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5ab Private Use-F5AB | f5aa Private Use-F5AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5ad Private Use-F5AD | f5ac Private Use-F5AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5af Private Use-F5AF | f5ae Private Use-F5AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5b1 Private Use-F5B1 | f5b0 Private Use-F5B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5b3 Private Use-F5B3 | f5b2 Private Use-F5B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5b5 Private Use-F5B5 | f5b4 Private Use-F5B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5b7 Private Use-F5B7 | f5b6 Private Use-F5B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5b9 Private Use-F5B9 | f5b8 Private Use-F5B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5bb Private Use-F5BB | f5ba Private Use-F5BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5bd Private Use-F5BD | f5bc Private Use-F5BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5bf Private Use-F5BF | f5be Private Use-F5BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5c1 Private Use-F5C1 | f5c0 Private Use-F5C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5c3 Private Use-F5C3 | f5c2 Private Use-F5C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5c5 Private Use-F5C5 | f5c4 Private Use-F5C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5c7 Private Use-F5C7 | f5c6 Private Use-F5C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5c9 Private Use-F5C9 | f5c8 Private Use-F5C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5cb Private Use-F5CB | f5ca Private Use-F5CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5cd Private Use-F5CD | f5cc Private Use-F5CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5cf Private Use-F5CF | f5ce Private Use-F5CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5d1 Private Use-F5D1 | f5d0 Private Use-F5D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5d3 Private Use-F5D3 | f5d2 Private Use-F5D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5d5 Private Use-F5D5 | f5d4 Private Use-F5D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5d7 Private Use-F5D7 | f5d6 Private Use-F5D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5d9 Private Use-F5D9 | f5d8 Private Use-F5D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5db Private Use-F5DB | f5da Private Use-F5DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5dd Private Use-F5DD | f5dc Private Use-F5DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5df Private Use-F5DF | f5de Private Use-F5DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5e1 Private Use-F5E1 | f5e0 Private Use-F5E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5e3 Private Use-F5E3 | f5e2 Private Use-F5E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5e5 Private Use-F5E5 | f5e4 Private Use-F5E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5e7 Private Use-F5E7 | f5e6 Private Use-F5E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5e9 Private Use-F5E9 | f5e8 Private Use-F5E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5eb Private Use-F5EB | f5ea Private Use-F5EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5ed Private Use-F5ED | f5ec Private Use-F5EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5ef Private Use-F5EF | f5ee Private Use-F5EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5f1 Private Use-F5F1 | f5f0 Private Use-F5F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5f3 Private Use-F5F3 | f5f2 Private Use-F5F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5f5 Private Use-F5F5 | f5f4 Private Use-F5F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5f7 Private Use-F5F7 | f5f6 Private Use-F5F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5f9 Private Use-F5F9 | f5f8 Private Use-F5F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5fb Private Use-F5FB | f5fa Private Use-F5FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5fd Private Use-F5FD | f5fc Private Use-F5FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5ff Private Use-F5FF | f5fe Private Use-F5FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f601 Private Use-F601 | f600 Private Use-F600 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f603 Private Use-F603 | f602 Private Use-F602 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f605 Private Use-F605 | f604 Private Use-F604 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f607 Private Use-F607 | f606 Private Use-F606 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f609 Private Use-F609 | f608 Private Use-F608 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f60b Private Use-F60B | f60a Private Use-F60A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f60d Private Use-F60D | f60c Private Use-F60C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f60f Private Use-F60F | f60e Private Use-F60E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f611 Private Use-F611 | f610 Private Use-F610 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f613 Private Use-F613 | f612 Private Use-F612 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f615 Private Use-F615 | f614 Private Use-F614 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f617 Private Use-F617 | f616 Private Use-F616 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f619 Private Use-F619 | f618 Private Use-F618 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f61b Private Use-F61B | f61a Private Use-F61A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f61d Private Use-F61D | f61c Private Use-F61C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f61f Private Use-F61F | f61e Private Use-F61E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f621 Private Use-F621 | f620 Private Use-F620 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f623 Private Use-F623 | f622 Private Use-F622 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f625 Private Use-F625 | f624 Private Use-F624 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f627 Private Use-F627 | f626 Private Use-F626 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f629 Private Use-F629 | f628 Private Use-F628 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f62b Private Use-F62B | f62a Private Use-F62A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f62d Private Use-F62D | f62c Private Use-F62C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f62f Private Use-F62F | f62e Private Use-F62E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f631 Private Use-F631 | f630 Private Use-F630 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f633 Private Use-F633 | f632 Private Use-F632 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f635 Private Use-F635 | f634 Private Use-F634 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f637 Private Use-F637 | f636 Private Use-F636 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f639 Private Use-F639 | f638 Private Use-F638 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f63b Private Use-F63B | f63a Private Use-F63A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f63d Private Use-F63D | f63c Private Use-F63C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f63f Private Use-F63F | f63e Private Use-F63E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f641 Private Use-F641 | f640 Private Use-F640 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f643 Private Use-F643 | f642 Private Use-F642 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f645 Private Use-F645 | f644 Private Use-F644 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f647 Private Use-F647 | f646 Private Use-F646 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f649 Private Use-F649 | f648 Private Use-F648 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f64b Private Use-F64B | f64a Private Use-F64A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f64d Private Use-F64D | f64c Private Use-F64C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f64f Private Use-F64F | f64e Private Use-F64E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f651 Private Use-F651 | f650 Private Use-F650 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f653 Private Use-F653 | f652 Private Use-F652 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f655 Private Use-F655 | f654 Private Use-F654 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f657 Private Use-F657 | f656 Private Use-F656 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f659 Private Use-F659 | f658 Private Use-F658 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f65b Private Use-F65B | f65a Private Use-F65A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f65d Private Use-F65D | f65c Private Use-F65C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f65f Private Use-F65F | f65e Private Use-F65E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f661 Private Use-F661 | f660 Private Use-F660 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f663 Private Use-F663 | f662 Private Use-F662 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f665 Private Use-F665 | f664 Private Use-F664 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f667 Private Use-F667 | f666 Private Use-F666 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f669 Private Use-F669 | f668 Private Use-F668 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f66b Private Use-F66B | f66a Private Use-F66A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f66d Private Use-F66D | f66c Private Use-F66C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f66f Private Use-F66F | f66e Private Use-F66E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f671 Private Use-F671 | f670 Private Use-F670 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f673 Private Use-F673 | f672 Private Use-F672 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f675 Private Use-F675 | f674 Private Use-F674 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f677 Private Use-F677 | f676 Private Use-F676 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f679 Private Use-F679 | f678 Private Use-F678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f67b Private Use-F67B | f67a Private Use-F67A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f67d Private Use-F67D | f67c Private Use-F67C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f67f Private Use-F67F | f67e Private Use-F67E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f681 Private Use-F681 | f680 Private Use-F680 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f683 Private Use-F683 | f682 Private Use-F682 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f685 Private Use-F685 | f684 Private Use-F684 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f687 Private Use-F687 | f686 Private Use-F686 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f689 Private Use-F689 | f688 Private Use-F688 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f68b Private Use-F68B | f68a Private Use-F68A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f68d Private Use-F68D | f68c Private Use-F68C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f68f Private Use-F68F | f68e Private Use-F68E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f691 Private Use-F691 | f690 Private Use-F690 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f693 Private Use-F693 | f692 Private Use-F692 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f695 Private Use-F695 | f694 Private Use-F694 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f697 Private Use-F697 | f696 Private Use-F696 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f699 Private Use-F699 | f698 Private Use-F698 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f69b Private Use-F69B | f69a Private Use-F69A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f69d Private Use-F69D | f69c Private Use-F69C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f69f Private Use-F69F | f69e Private Use-F69E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6a1 Private Use-F6A1 | f6a0 Private Use-F6A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6a3 Private Use-F6A3 | f6a2 Private Use-F6A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6a5 Private Use-F6A5 | f6a4 Private Use-F6A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6a7 Private Use-F6A7 | f6a6 Private Use-F6A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6a9 Private Use-F6A9 | f6a8 Private Use-F6A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6ab Private Use-F6AB | f6aa Private Use-F6AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6ad Private Use-F6AD | f6ac Private Use-F6AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6af Private Use-F6AF | f6ae Private Use-F6AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6b1 Private Use-F6B1 | f6b0 Private Use-F6B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6b3 Private Use-F6B3 | f6b2 Private Use-F6B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6b5 Private Use-F6B5 | f6b4 Private Use-F6B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6b7 Private Use-F6B7 | f6b6 Private Use-F6B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6b9 Private Use-F6B9 | f6b8 Private Use-F6B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6bb Private Use-F6BB | f6ba Private Use-F6BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6bd Private Use-F6BD | f6bc Private Use-F6BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6bf Private Use-F6BF | f6be Private Use-F6BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6c1 Private Use-F6C1 | f6c0 Private Use-F6C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6c3 Private Use-F6C3 | f6c2 Private Use-F6C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6c5 Private Use-F6C5 | f6c4 Private Use-F6C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6c7 Private Use-F6C7 | f6c6 Private Use-F6C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6c9 Private Use-F6C9 | f6c8 Private Use-F6C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6cb Private Use-F6CB | f6ca Private Use-F6CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6cd Private Use-F6CD | f6cc Private Use-F6CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6cf Private Use-F6CF | f6ce Private Use-F6CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6d1 Private Use-F6D1 | f6d0 Private Use-F6D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6d3 Private Use-F6D3 | f6d2 Private Use-F6D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6d5 Private Use-F6D5 | f6d4 Private Use-F6D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6d7 Private Use-F6D7 | f6d6 Private Use-F6D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6d9 Private Use-F6D9 | f6d8 Private Use-F6D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6db Private Use-F6DB | f6da Private Use-F6DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6dd Private Use-F6DD | f6dc Private Use-F6DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6df Private Use-F6DF | f6de Private Use-F6DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6e1 Private Use-F6E1 | f6e0 Private Use-F6E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6e3 Private Use-F6E3 | f6e2 Private Use-F6E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6e5 Private Use-F6E5 | f6e4 Private Use-F6E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6e7 Private Use-F6E7 | f6e6 Private Use-F6E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6e9 Private Use-F6E9 | f6e8 Private Use-F6E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6eb Private Use-F6EB | f6ea Private Use-F6EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6ed Private Use-F6ED | f6ec Private Use-F6EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6ef Private Use-F6EF | f6ee Private Use-F6EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6f1 Private Use-F6F1 | f6f0 Private Use-F6F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6f3 Private Use-F6F3 | f6f2 Private Use-F6F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6f5 Private Use-F6F5 | f6f4 Private Use-F6F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6f7 Private Use-F6F7 | f6f6 Private Use-F6F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6f9 Private Use-F6F9 | f6f8 Private Use-F6F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6fb Private Use-F6FB | f6fa Private Use-F6FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6fd Private Use-F6FD | f6fc Private Use-F6FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6ff Private Use-F6FF | f6fe Private Use-F6FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f701 Private Use-F701 | f700 Private Use-F700 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f703 Private Use-F703 | f702 Private Use-F702 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f705 Private Use-F705 | f704 Private Use-F704 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f707 Private Use-F707 | f706 Private Use-F706 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f709 Private Use-F709 | f708 Private Use-F708 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f70b Private Use-F70B | f70a Private Use-F70A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f70d Private Use-F70D | f70c Private Use-F70C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f70f Private Use-F70F | f70e Private Use-F70E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f711 Private Use-F711 | f710 Private Use-F710 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f713 Private Use-F713 | f712 Private Use-F712 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f715 Private Use-F715 | f714 Private Use-F714 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f717 Private Use-F717 | f716 Private Use-F716 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f719 Private Use-F719 | f718 Private Use-F718 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f71b Private Use-F71B | f71a Private Use-F71A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f71d Private Use-F71D | f71c Private Use-F71C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f71f Private Use-F71F | f71e Private Use-F71E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f721 Private Use-F721 | f720 Private Use-F720 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f723 Private Use-F723 | f722 Private Use-F722 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f725 Private Use-F725 | f724 Private Use-F724 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f727 Private Use-F727 | f726 Private Use-F726 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f729 Private Use-F729 | f728 Private Use-F728 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f72b Private Use-F72B | f72a Private Use-F72A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f72d Private Use-F72D | f72c Private Use-F72C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f72f Private Use-F72F | f72e Private Use-F72E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f731 Private Use-F731 | f730 Private Use-F730 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f733 Private Use-F733 | f732 Private Use-F732 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f735 Private Use-F735 | f734 Private Use-F734 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f737 Private Use-F737 | f736 Private Use-F736 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f739 Private Use-F739 | f738 Private Use-F738 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f73b Private Use-F73B | f73a Private Use-F73A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f73d Private Use-F73D | f73c Private Use-F73C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f73f Private Use-F73F | f73e Private Use-F73E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f741 Private Use-F741 | f740 Private Use-F740 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f743 Private Use-F743 | f742 Private Use-F742 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f745 Private Use-F745 | f744 Private Use-F744 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f747 Private Use-F747 | f746 Private Use-F746 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f749 Private Use-F749 | f748 Private Use-F748 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f74b Private Use-F74B | f74a Private Use-F74A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f74d Private Use-F74D | f74c Private Use-F74C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f74f Private Use-F74F | f74e Private Use-F74E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f751 Private Use-F751 | f750 Private Use-F750 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f753 Private Use-F753 | f752 Private Use-F752 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f755 Private Use-F755 | f754 Private Use-F754 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f757 Private Use-F757 | f756 Private Use-F756 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f759 Private Use-F759 | f758 Private Use-F758 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f75b Private Use-F75B | f75a Private Use-F75A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f75d Private Use-F75D | f75c Private Use-F75C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f75f Private Use-F75F | f75e Private Use-F75E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f761 Private Use-F761 | f760 Private Use-F760 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f763 Private Use-F763 | f762 Private Use-F762 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f765 Private Use-F765 | f764 Private Use-F764 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f767 Private Use-F767 | f766 Private Use-F766 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f769 Private Use-F769 | f768 Private Use-F768 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f76b Private Use-F76B | f76a Private Use-F76A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f76d Private Use-F76D | f76c Private Use-F76C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f76f Private Use-F76F | f76e Private Use-F76E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f771 Private Use-F771 | f770 Private Use-F770 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f773 Private Use-F773 | f772 Private Use-F772 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f775 Private Use-F775 | f774 Private Use-F774 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f777 Private Use-F777 | f776 Private Use-F776 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f779 Private Use-F779 | f778 Private Use-F778 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f77b Private Use-F77B | f77a Private Use-F77A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f77d Private Use-F77D | f77c Private Use-F77C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f77f Private Use-F77F | f77e Private Use-F77E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f781 Private Use-F781 | f780 Private Use-F780 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f783 Private Use-F783 | f782 Private Use-F782 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f785 Private Use-F785 | f784 Private Use-F784 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f787 Private Use-F787 | f786 Private Use-F786 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f789 Private Use-F789 | f788 Private Use-F788 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f78b Private Use-F78B | f78a Private Use-F78A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f78d Private Use-F78D | f78c Private Use-F78C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f78f Private Use-F78F | f78e Private Use-F78E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f791 Private Use-F791 | f790 Private Use-F790 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f793 Private Use-F793 | f792 Private Use-F792 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f795 Private Use-F795 | f794 Private Use-F794 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f797 Private Use-F797 | f796 Private Use-F796 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f799 Private Use-F799 | f798 Private Use-F798 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f79b Private Use-F79B | f79a Private Use-F79A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f79d Private Use-F79D | f79c Private Use-F79C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f79f Private Use-F79F | f79e Private Use-F79E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7a1 Private Use-F7A1 | f7a0 Private Use-F7A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7a3 Private Use-F7A3 | f7a2 Private Use-F7A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7a5 Private Use-F7A5 | f7a4 Private Use-F7A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7a7 Private Use-F7A7 | f7a6 Private Use-F7A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7a9 Private Use-F7A9 | f7a8 Private Use-F7A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7ab Private Use-F7AB | f7aa Private Use-F7AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7ad Private Use-F7AD | f7ac Private Use-F7AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7af Private Use-F7AF | f7ae Private Use-F7AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7b1 Private Use-F7B1 | f7b0 Private Use-F7B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7b3 Private Use-F7B3 | f7b2 Private Use-F7B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7b5 Private Use-F7B5 | f7b4 Private Use-F7B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7b7 Private Use-F7B7 | f7b6 Private Use-F7B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7b9 Private Use-F7B9 | f7b8 Private Use-F7B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7bb Private Use-F7BB | f7ba Private Use-F7BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7bd Private Use-F7BD | f7bc Private Use-F7BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7bf Private Use-F7BF | f7be Private Use-F7BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7c1 Private Use-F7C1 | f7c0 Private Use-F7C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7c3 Private Use-F7C3 | f7c2 Private Use-F7C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7c5 Private Use-F7C5 | f7c4 Private Use-F7C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7c7 Private Use-F7C7 | f7c6 Private Use-F7C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7c9 Private Use-F7C9 | f7c8 Private Use-F7C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7cb Private Use-F7CB | f7ca Private Use-F7CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7cd Private Use-F7CD | f7cc Private Use-F7CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7cf Private Use-F7CF | f7ce Private Use-F7CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7d1 Private Use-F7D1 | f7d0 Private Use-F7D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7d3 Private Use-F7D3 | f7d2 Private Use-F7D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7d5 Private Use-F7D5 | f7d4 Private Use-F7D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7d7 Private Use-F7D7 | f7d6 Private Use-F7D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7d9 Private Use-F7D9 | f7d8 Private Use-F7D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7db Private Use-F7DB | f7da Private Use-F7DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7dd Private Use-F7DD | f7dc Private Use-F7DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7df Private Use-F7DF | f7de Private Use-F7DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7e1 Private Use-F7E1 | f7e0 Private Use-F7E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7e3 Private Use-F7E3 | f7e2 Private Use-F7E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7e5 Private Use-F7E5 | f7e4 Private Use-F7E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7e7 Private Use-F7E7 | f7e6 Private Use-F7E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7e9 Private Use-F7E9 | f7e8 Private Use-F7E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7eb Private Use-F7EB | f7ea Private Use-F7EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7ed Private Use-F7ED | f7ec Private Use-F7EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7ef Private Use-F7EF | f7ee Private Use-F7EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7f1 Private Use-F7F1 | f7f0 Private Use-F7F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7f3 Private Use-F7F3 | f7f2 Private Use-F7F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7f5 Private Use-F7F5 | f7f4 Private Use-F7F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7f7 Private Use-F7F7 | f7f6 Private Use-F7F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7f9 Private Use-F7F9 | f7f8 Private Use-F7F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7fb Private Use-F7FB | f7fa Private Use-F7FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7fd Private Use-F7FD | f7fc Private Use-F7FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7ff Private Use-F7FF | f7fe Private Use-F7FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f801 Private Use-F801 | f800 Private Use-F800 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f803 Private Use-F803 | f802 Private Use-F802 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f805 Private Use-F805 | f804 Private Use-F804 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f807 Private Use-F807 | f806 Private Use-F806 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f809 Private Use-F809 | f808 Private Use-F808 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f80b Private Use-F80B | f80a Private Use-F80A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f80d Private Use-F80D | f80c Private Use-F80C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f80f Private Use-F80F | f80e Private Use-F80E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f811 Private Use-F811 | f810 Private Use-F810 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f813 Private Use-F813 | f812 Private Use-F812 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f815 Private Use-F815 | f814 Private Use-F814 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f817 Private Use-F817 | f816 Private Use-F816 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f819 Private Use-F819 | f818 Private Use-F818 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f81b Private Use-F81B | f81a Private Use-F81A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f81d Private Use-F81D | f81c Private Use-F81C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f81f Private Use-F81F | f81e Private Use-F81E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f821 Private Use-F821 | f820 Private Use-F820 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f823 Private Use-F823 | f822 Private Use-F822 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f825 Private Use-F825 | f824 Private Use-F824 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f827 Private Use-F827 | f826 Private Use-F826 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f829 Private Use-F829 | f828 Private Use-F828 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f82b Private Use-F82B | f82a Private Use-F82A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f82d Private Use-F82D | f82c Private Use-F82C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f82f Private Use-F82F | f82e Private Use-F82E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f831 Private Use-F831 | f830 Private Use-F830 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f833 Private Use-F833 | f832 Private Use-F832 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f835 Private Use-F835 | f834 Private Use-F834 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f837 Private Use-F837 | f836 Private Use-F836 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f839 Private Use-F839 | f838 Private Use-F838 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f83b Private Use-F83B | f83a Private Use-F83A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f83d Private Use-F83D | f83c Private Use-F83C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f83f Private Use-F83F | f83e Private Use-F83E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f841 Private Use-F841 | f840 Private Use-F840 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f843 Private Use-F843 | f842 Private Use-F842 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f845 Private Use-F845 | f844 Private Use-F844 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f847 Private Use-F847 | f846 Private Use-F846 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f849 Private Use-F849 | f848 Private Use-F848 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f84b Private Use-F84B | f84a Private Use-F84A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f84d Private Use-F84D | f84c Private Use-F84C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f84f Private Use-F84F | f84e Private Use-F84E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f851 Private Use-F851 | f850 Private Use-F850 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f853 Private Use-F853 | f852 Private Use-F852 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f855 Private Use-F855 | f854 Private Use-F854 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f857 Private Use-F857 | f856 Private Use-F856 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f859 Private Use-F859 | f858 Private Use-F858 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f85b Private Use-F85B | f85a Private Use-F85A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f85d Private Use-F85D | f85c Private Use-F85C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f85f Private Use-F85F | f85e Private Use-F85E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f861 Private Use-F861 | f860 Private Use-F860 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f863 Private Use-F863 | f862 Private Use-F862 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f865 Private Use-F865 | f864 Private Use-F864 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f867 Private Use-F867 | f866 Private Use-F866 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f869 Private Use-F869 | f868 Private Use-F868 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f86b Private Use-F86B | f86a Private Use-F86A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f86d Private Use-F86D | f86c Private Use-F86C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f86f Private Use-F86F | f86e Private Use-F86E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f871 Private Use-F871 | f870 Private Use-F870 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f873 Private Use-F873 | f872 Private Use-F872 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f875 Private Use-F875 | f874 Private Use-F874 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f877 Private Use-F877 | f876 Private Use-F876 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f879 Private Use-F879 | f878 Private Use-F878 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f87b Private Use-F87B | f87a Private Use-F87A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f87d Private Use-F87D | f87c Private Use-F87C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f87f Private Use-F87F | f87e Private Use-F87E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f881 Private Use-F881 | f880 Private Use-F880 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f883 Private Use-F883 | f882 Private Use-F882 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f885 Private Use-F885 | f884 Private Use-F884 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f887 Private Use-F887 | f886 Private Use-F886 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f889 Private Use-F889 | f888 Private Use-F888 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f88b Private Use-F88B | f88a Private Use-F88A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f88d Private Use-F88D | f88c Private Use-F88C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f88f Private Use-F88F | f88e Private Use-F88E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f891 Private Use-F891 | f890 Private Use-F890 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f893 Private Use-F893 | f892 Private Use-F892 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f895 Private Use-F895 | f894 Private Use-F894 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f897 Private Use-F897 | f896 Private Use-F896 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f899 Private Use-F899 | f898 Private Use-F898 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f89b Private Use-F89B | f89a Private Use-F89A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f89d Private Use-F89D | f89c Private Use-F89C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f89f Private Use-F89F | f89e Private Use-F89E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8a1 Private Use-F8A1 | f8a0 Private Use-F8A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8a3 Private Use-F8A3 | f8a2 Private Use-F8A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8a5 Private Use-F8A5 | f8a4 Private Use-F8A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8a7 Private Use-F8A7 | f8a6 Private Use-F8A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8a9 Private Use-F8A9 | f8a8 Private Use-F8A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8ab Private Use-F8AB | f8aa Private Use-F8AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8ad Private Use-F8AD | f8ac Private Use-F8AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8af Private Use-F8AF | f8ae Private Use-F8AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8b1 Private Use-F8B1 | f8b0 Private Use-F8B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8b3 Private Use-F8B3 | f8b2 Private Use-F8B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8b5 Private Use-F8B5 | f8b4 Private Use-F8B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8b7 Private Use-F8B7 | f8b6 Private Use-F8B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8b9 Private Use-F8B9 | f8b8 Private Use-F8B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8bb Private Use-F8BB | f8ba Private Use-F8BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8bd Private Use-F8BD | f8bc Private Use-F8BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8bf Private Use-F8BF | f8be Private Use-F8BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8c1 Private Use-F8C1 | f8c0 Private Use-F8C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8c3 Private Use-F8C3 | f8c2 Private Use-F8C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8c5 Private Use-F8C5 | f8c4 Private Use-F8C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8c7 Private Use-F8C7 | f8c6 Private Use-F8C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8c9 Private Use-F8C9 | f8c8 Private Use-F8C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8cb Private Use-F8CB | f8ca Private Use-F8CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8cd Private Use-F8CD | f8cc Private Use-F8CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8cf Private Use-F8CF | f8ce Private Use-F8CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8d1 Private Use-F8D1 | f8d0 Private Use-F8D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8d3 Private Use-F8D3 | f8d2 Private Use-F8D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8d5 Private Use-F8D5 | f8d4 Private Use-F8D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8d7 Private Use-F8D7 | f8d6 Private Use-F8D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8d9 Private Use-F8D9 | f8d8 Private Use-F8D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8db Private Use-F8DB | f8da Private Use-F8DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8dd Private Use-F8DD | f8dc Private Use-F8DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8df Private Use-F8DF | f8de Private Use-F8DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8e1 Private Use-F8E1 | f8e0 Private Use-F8E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8e3 Private Use-F8E3 | f8e2 Private Use-F8E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8e5 Private Use-F8E5 | f8e4 Private Use-F8E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8e7 Private Use-F8E7 | f8e6 Private Use-F8E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8e9 Private Use-F8E9 | f8e8 Private Use-F8E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8eb Private Use-F8EB | f8ea Private Use-F8EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8ed Private Use-F8ED | f8ec Private Use-F8EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8ef Private Use-F8EF | f8ee Private Use-F8EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8f1 Private Use-F8F1 | f8f0 Private Use-F8F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8f3 Private Use-F8F3 | f8f2 Private Use-F8F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8f5 Private Use-F8F5 | f8f4 Private Use-F8F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8f7 Private Use-F8F7 | f8f6 Private Use-F8F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8f9 Private Use-F8F9 | f8f8 Private Use-F8F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8fb Private Use-F8FB | f8fa Private Use-F8FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8fd Private Use-F8FD | f8fc Private Use-F8FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8ff Private Use-F8FF | f8fe Private Use-F8FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f901 CJK COMPATIBILITY IDEOGRAPH-F901 | f900 CJK COMPATIBILITY IDEOGRAPH-F900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f903 CJK COMPATIBILITY IDEOGRAPH-F903 | f902 CJK COMPATIBILITY IDEOGRAPH-F902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f905 CJK COMPATIBILITY IDEOGRAPH-F905 | f904 CJK COMPATIBILITY IDEOGRAPH-F904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f907 CJK COMPATIBILITY IDEOGRAPH-F907 | f906 CJK COMPATIBILITY IDEOGRAPH-F906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f909 CJK COMPATIBILITY IDEOGRAPH-F909 | f908 CJK COMPATIBILITY IDEOGRAPH-F908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f90b CJK COMPATIBILITY IDEOGRAPH-F90B | f90a CJK COMPATIBILITY IDEOGRAPH-F90A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f90d CJK COMPATIBILITY IDEOGRAPH-F90D | f90c CJK COMPATIBILITY IDEOGRAPH-F90C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f90f CJK COMPATIBILITY IDEOGRAPH-F90F | f90e CJK COMPATIBILITY IDEOGRAPH-F90E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f911 CJK COMPATIBILITY IDEOGRAPH-F911 | f910 CJK COMPATIBILITY IDEOGRAPH-F910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f913 CJK COMPATIBILITY IDEOGRAPH-F913 | f912 CJK COMPATIBILITY IDEOGRAPH-F912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f915 CJK COMPATIBILITY IDEOGRAPH-F915 | f914 CJK COMPATIBILITY IDEOGRAPH-F914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f917 CJK COMPATIBILITY IDEOGRAPH-F917 | f916 CJK COMPATIBILITY IDEOGRAPH-F916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f919 CJK COMPATIBILITY IDEOGRAPH-F919 | f918 CJK COMPATIBILITY IDEOGRAPH-F918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f91b CJK COMPATIBILITY IDEOGRAPH-F91B | f91a CJK COMPATIBILITY IDEOGRAPH-F91A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f91d CJK COMPATIBILITY IDEOGRAPH-F91D | f91c CJK COMPATIBILITY IDEOGRAPH-F91C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f91f CJK COMPATIBILITY IDEOGRAPH-F91F | f91e CJK COMPATIBILITY IDEOGRAPH-F91E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f921 CJK COMPATIBILITY IDEOGRAPH-F921 | f920 CJK COMPATIBILITY IDEOGRAPH-F920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f923 CJK COMPATIBILITY IDEOGRAPH-F923 | f922 CJK COMPATIBILITY IDEOGRAPH-F922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f925 CJK COMPATIBILITY IDEOGRAPH-F925 | f924 CJK COMPATIBILITY IDEOGRAPH-F924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f927 CJK COMPATIBILITY IDEOGRAPH-F927 | f926 CJK COMPATIBILITY IDEOGRAPH-F926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f929 CJK COMPATIBILITY IDEOGRAPH-F929 | f928 CJK COMPATIBILITY IDEOGRAPH-F928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f92b CJK COMPATIBILITY IDEOGRAPH-F92B | f92a CJK COMPATIBILITY IDEOGRAPH-F92A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f92d CJK COMPATIBILITY IDEOGRAPH-F92D | f92c CJK COMPATIBILITY IDEOGRAPH-F92C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f92f CJK COMPATIBILITY IDEOGRAPH-F92F | f92e CJK COMPATIBILITY IDEOGRAPH-F92E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f931 CJK COMPATIBILITY IDEOGRAPH-F931 | f930 CJK COMPATIBILITY IDEOGRAPH-F930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f933 CJK COMPATIBILITY IDEOGRAPH-F933 | f932 CJK COMPATIBILITY IDEOGRAPH-F932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f935 CJK COMPATIBILITY IDEOGRAPH-F935 | f934 CJK COMPATIBILITY IDEOGRAPH-F934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f937 CJK COMPATIBILITY IDEOGRAPH-F937 | f936 CJK COMPATIBILITY IDEOGRAPH-F936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f939 CJK COMPATIBILITY IDEOGRAPH-F939 | f938 CJK COMPATIBILITY IDEOGRAPH-F938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f93b CJK COMPATIBILITY IDEOGRAPH-F93B | f93a CJK COMPATIBILITY IDEOGRAPH-F93A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f93d CJK COMPATIBILITY IDEOGRAPH-F93D | f93c CJK COMPATIBILITY IDEOGRAPH-F93C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f93f CJK COMPATIBILITY IDEOGRAPH-F93F | f93e CJK COMPATIBILITY IDEOGRAPH-F93E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f941 CJK COMPATIBILITY IDEOGRAPH-F941 | f940 CJK COMPATIBILITY IDEOGRAPH-F940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f943 CJK COMPATIBILITY IDEOGRAPH-F943 | f942 CJK COMPATIBILITY IDEOGRAPH-F942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f945 CJK COMPATIBILITY IDEOGRAPH-F945 | f944 CJK COMPATIBILITY IDEOGRAPH-F944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f947 CJK COMPATIBILITY IDEOGRAPH-F947 | f946 CJK COMPATIBILITY IDEOGRAPH-F946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f949 CJK COMPATIBILITY IDEOGRAPH-F949 | f948 CJK COMPATIBILITY IDEOGRAPH-F948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f94b CJK COMPATIBILITY IDEOGRAPH-F94B | f94a CJK COMPATIBILITY IDEOGRAPH-F94A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f94d CJK COMPATIBILITY IDEOGRAPH-F94D | f94c CJK COMPATIBILITY IDEOGRAPH-F94C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f94f CJK COMPATIBILITY IDEOGRAPH-F94F | f94e CJK COMPATIBILITY IDEOGRAPH-F94E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f951 CJK COMPATIBILITY IDEOGRAPH-F951 | f950 CJK COMPATIBILITY IDEOGRAPH-F950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f953 CJK COMPATIBILITY IDEOGRAPH-F953 | f952 CJK COMPATIBILITY IDEOGRAPH-F952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f955 CJK COMPATIBILITY IDEOGRAPH-F955 | f954 CJK COMPATIBILITY IDEOGRAPH-F954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f957 CJK COMPATIBILITY IDEOGRAPH-F957 | f956 CJK COMPATIBILITY IDEOGRAPH-F956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f959 CJK COMPATIBILITY IDEOGRAPH-F959 | f958 CJK COMPATIBILITY IDEOGRAPH-F958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f95b CJK COMPATIBILITY IDEOGRAPH-F95B | f95a CJK COMPATIBILITY IDEOGRAPH-F95A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f95d CJK COMPATIBILITY IDEOGRAPH-F95D | f95c CJK COMPATIBILITY IDEOGRAPH-F95C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f95f CJK COMPATIBILITY IDEOGRAPH-F95F | f95e CJK COMPATIBILITY IDEOGRAPH-F95E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f961 CJK COMPATIBILITY IDEOGRAPH-F961 | f960 CJK COMPATIBILITY IDEOGRAPH-F960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f963 CJK COMPATIBILITY IDEOGRAPH-F963 | f962 CJK COMPATIBILITY IDEOGRAPH-F962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f965 CJK COMPATIBILITY IDEOGRAPH-F965 | f964 CJK COMPATIBILITY IDEOGRAPH-F964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f967 CJK COMPATIBILITY IDEOGRAPH-F967 | f966 CJK COMPATIBILITY IDEOGRAPH-F966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f969 CJK COMPATIBILITY IDEOGRAPH-F969 | f968 CJK COMPATIBILITY IDEOGRAPH-F968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f96b CJK COMPATIBILITY IDEOGRAPH-F96B | f96a CJK COMPATIBILITY IDEOGRAPH-F96A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f96d CJK COMPATIBILITY IDEOGRAPH-F96D | f96c CJK COMPATIBILITY IDEOGRAPH-F96C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f96f CJK COMPATIBILITY IDEOGRAPH-F96F | f96e CJK COMPATIBILITY IDEOGRAPH-F96E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f971 CJK COMPATIBILITY IDEOGRAPH-F971 | f970 CJK COMPATIBILITY IDEOGRAPH-F970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f973 CJK COMPATIBILITY IDEOGRAPH-F973 | f972 CJK COMPATIBILITY IDEOGRAPH-F972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f975 CJK COMPATIBILITY IDEOGRAPH-F975 | f974 CJK COMPATIBILITY IDEOGRAPH-F974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f977 CJK COMPATIBILITY IDEOGRAPH-F977 | f976 CJK COMPATIBILITY IDEOGRAPH-F976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f979 CJK COMPATIBILITY IDEOGRAPH-F979 | f978 CJK COMPATIBILITY IDEOGRAPH-F978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f97b CJK COMPATIBILITY IDEOGRAPH-F97B | f97a CJK COMPATIBILITY IDEOGRAPH-F97A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f97d CJK COMPATIBILITY IDEOGRAPH-F97D | f97c CJK COMPATIBILITY IDEOGRAPH-F97C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f97f CJK COMPATIBILITY IDEOGRAPH-F97F | f97e CJK COMPATIBILITY IDEOGRAPH-F97E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f981 CJK COMPATIBILITY IDEOGRAPH-F981 | f980 CJK COMPATIBILITY IDEOGRAPH-F980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f983 CJK COMPATIBILITY IDEOGRAPH-F983 | f982 CJK COMPATIBILITY IDEOGRAPH-F982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f985 CJK COMPATIBILITY IDEOGRAPH-F985 | f984 CJK COMPATIBILITY IDEOGRAPH-F984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f987 CJK COMPATIBILITY IDEOGRAPH-F987 | f986 CJK COMPATIBILITY IDEOGRAPH-F986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f989 CJK COMPATIBILITY IDEOGRAPH-F989 | f988 CJK COMPATIBILITY IDEOGRAPH-F988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f98b CJK COMPATIBILITY IDEOGRAPH-F98B | f98a CJK COMPATIBILITY IDEOGRAPH-F98A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f98d CJK COMPATIBILITY IDEOGRAPH-F98D | f98c CJK COMPATIBILITY IDEOGRAPH-F98C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f98f CJK COMPATIBILITY IDEOGRAPH-F98F | f98e CJK COMPATIBILITY IDEOGRAPH-F98E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f991 CJK COMPATIBILITY IDEOGRAPH-F991 | f990 CJK COMPATIBILITY IDEOGRAPH-F990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f993 CJK COMPATIBILITY IDEOGRAPH-F993 | f992 CJK COMPATIBILITY IDEOGRAPH-F992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f995 CJK COMPATIBILITY IDEOGRAPH-F995 | f994 CJK COMPATIBILITY IDEOGRAPH-F994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f997 CJK COMPATIBILITY IDEOGRAPH-F997 | f996 CJK COMPATIBILITY IDEOGRAPH-F996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f999 CJK COMPATIBILITY IDEOGRAPH-F999 | f998 CJK COMPATIBILITY IDEOGRAPH-F998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f99b CJK COMPATIBILITY IDEOGRAPH-F99B | f99a CJK COMPATIBILITY IDEOGRAPH-F99A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f99d CJK COMPATIBILITY IDEOGRAPH-F99D | f99c CJK COMPATIBILITY IDEOGRAPH-F99C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f99f CJK COMPATIBILITY IDEOGRAPH-F99F | f99e CJK COMPATIBILITY IDEOGRAPH-F99E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9a1 CJK COMPATIBILITY IDEOGRAPH-F9A1 | f9a0 CJK COMPATIBILITY IDEOGRAPH-F9A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9a3 CJK COMPATIBILITY IDEOGRAPH-F9A3 | f9a2 CJK COMPATIBILITY IDEOGRAPH-F9A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9a5 CJK COMPATIBILITY IDEOGRAPH-F9A5 | f9a4 CJK COMPATIBILITY IDEOGRAPH-F9A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9a7 CJK COMPATIBILITY IDEOGRAPH-F9A7 | f9a6 CJK COMPATIBILITY IDEOGRAPH-F9A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9a9 CJK COMPATIBILITY IDEOGRAPH-F9A9 | f9a8 CJK COMPATIBILITY IDEOGRAPH-F9A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9ab CJK COMPATIBILITY IDEOGRAPH-F9AB | f9aa CJK COMPATIBILITY IDEOGRAPH-F9AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9ad CJK COMPATIBILITY IDEOGRAPH-F9AD | f9ac CJK COMPATIBILITY IDEOGRAPH-F9AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9af CJK COMPATIBILITY IDEOGRAPH-F9AF | f9ae CJK COMPATIBILITY IDEOGRAPH-F9AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9b1 CJK COMPATIBILITY IDEOGRAPH-F9B1 | f9b0 CJK COMPATIBILITY IDEOGRAPH-F9B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9b3 CJK COMPATIBILITY IDEOGRAPH-F9B3 | f9b2 CJK COMPATIBILITY IDEOGRAPH-F9B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9b5 CJK COMPATIBILITY IDEOGRAPH-F9B5 | f9b4 CJK COMPATIBILITY IDEOGRAPH-F9B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9b7 CJK COMPATIBILITY IDEOGRAPH-F9B7 | f9b6 CJK COMPATIBILITY IDEOGRAPH-F9B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9b9 CJK COMPATIBILITY IDEOGRAPH-F9B9 | f9b8 CJK COMPATIBILITY IDEOGRAPH-F9B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9bb CJK COMPATIBILITY IDEOGRAPH-F9BB | f9ba CJK COMPATIBILITY IDEOGRAPH-F9BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9bd CJK COMPATIBILITY IDEOGRAPH-F9BD | f9bc CJK COMPATIBILITY IDEOGRAPH-F9BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9bf CJK COMPATIBILITY IDEOGRAPH-F9BF | f9be CJK COMPATIBILITY IDEOGRAPH-F9BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9c1 CJK COMPATIBILITY IDEOGRAPH-F9C1 | f9c0 CJK COMPATIBILITY IDEOGRAPH-F9C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9c3 CJK COMPATIBILITY IDEOGRAPH-F9C3 | f9c2 CJK COMPATIBILITY IDEOGRAPH-F9C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9c5 CJK COMPATIBILITY IDEOGRAPH-F9C5 | f9c4 CJK COMPATIBILITY IDEOGRAPH-F9C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9c7 CJK COMPATIBILITY IDEOGRAPH-F9C7 | f9c6 CJK COMPATIBILITY IDEOGRAPH-F9C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9c9 CJK COMPATIBILITY IDEOGRAPH-F9C9 | f9c8 CJK COMPATIBILITY IDEOGRAPH-F9C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9cb CJK COMPATIBILITY IDEOGRAPH-F9CB | f9ca CJK COMPATIBILITY IDEOGRAPH-F9CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9cd CJK COMPATIBILITY IDEOGRAPH-F9CD | f9cc CJK COMPATIBILITY IDEOGRAPH-F9CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9cf CJK COMPATIBILITY IDEOGRAPH-F9CF | f9ce CJK COMPATIBILITY IDEOGRAPH-F9CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9d1 CJK COMPATIBILITY IDEOGRAPH-F9D1 | f9d0 CJK COMPATIBILITY IDEOGRAPH-F9D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9d3 CJK COMPATIBILITY IDEOGRAPH-F9D3 | f9d2 CJK COMPATIBILITY IDEOGRAPH-F9D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9d5 CJK COMPATIBILITY IDEOGRAPH-F9D5 | f9d4 CJK COMPATIBILITY IDEOGRAPH-F9D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9d7 CJK COMPATIBILITY IDEOGRAPH-F9D7 | f9d6 CJK COMPATIBILITY IDEOGRAPH-F9D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9d9 CJK COMPATIBILITY IDEOGRAPH-F9D9 | f9d8 CJK COMPATIBILITY IDEOGRAPH-F9D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9db CJK COMPATIBILITY IDEOGRAPH-F9DB | f9da CJK COMPATIBILITY IDEOGRAPH-F9DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9dd CJK COMPATIBILITY IDEOGRAPH-F9DD | f9dc CJK COMPATIBILITY IDEOGRAPH-F9DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9df CJK COMPATIBILITY IDEOGRAPH-F9DF | f9de CJK COMPATIBILITY IDEOGRAPH-F9DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9e1 CJK COMPATIBILITY IDEOGRAPH-F9E1 | f9e0 CJK COMPATIBILITY IDEOGRAPH-F9E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9e3 CJK COMPATIBILITY IDEOGRAPH-F9E3 | f9e2 CJK COMPATIBILITY IDEOGRAPH-F9E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9e5 CJK COMPATIBILITY IDEOGRAPH-F9E5 | f9e4 CJK COMPATIBILITY IDEOGRAPH-F9E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9e7 CJK COMPATIBILITY IDEOGRAPH-F9E7 | f9e6 CJK COMPATIBILITY IDEOGRAPH-F9E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9e9 CJK COMPATIBILITY IDEOGRAPH-F9E9 | f9e8 CJK COMPATIBILITY IDEOGRAPH-F9E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9eb CJK COMPATIBILITY IDEOGRAPH-F9EB | f9ea CJK COMPATIBILITY IDEOGRAPH-F9EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9ed CJK COMPATIBILITY IDEOGRAPH-F9ED | f9ec CJK COMPATIBILITY IDEOGRAPH-F9EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9ef CJK COMPATIBILITY IDEOGRAPH-F9EF | f9ee CJK COMPATIBILITY IDEOGRAPH-F9EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9f1 CJK COMPATIBILITY IDEOGRAPH-F9F1 | f9f0 CJK COMPATIBILITY IDEOGRAPH-F9F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9f3 CJK COMPATIBILITY IDEOGRAPH-F9F3 | f9f2 CJK COMPATIBILITY IDEOGRAPH-F9F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9f5 CJK COMPATIBILITY IDEOGRAPH-F9F5 | f9f4 CJK COMPATIBILITY IDEOGRAPH-F9F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9f7 CJK COMPATIBILITY IDEOGRAPH-F9F7 | f9f6 CJK COMPATIBILITY IDEOGRAPH-F9F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9f9 CJK COMPATIBILITY IDEOGRAPH-F9F9 | f9f8 CJK COMPATIBILITY IDEOGRAPH-F9F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9fb CJK COMPATIBILITY IDEOGRAPH-F9FB | f9fa CJK COMPATIBILITY IDEOGRAPH-F9FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9fd CJK COMPATIBILITY IDEOGRAPH-F9FD | f9fc CJK COMPATIBILITY IDEOGRAPH-F9FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9ff CJK COMPATIBILITY IDEOGRAPH-F9FF | f9fe CJK COMPATIBILITY IDEOGRAPH-F9FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa01 CJK COMPATIBILITY IDEOGRAPH-FA01 | fa00 CJK COMPATIBILITY IDEOGRAPH-FA00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa03 CJK COMPATIBILITY IDEOGRAPH-FA03 | fa02 CJK COMPATIBILITY IDEOGRAPH-FA02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa05 CJK COMPATIBILITY IDEOGRAPH-FA05 | fa04 CJK COMPATIBILITY IDEOGRAPH-FA04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa07 CJK COMPATIBILITY IDEOGRAPH-FA07 | fa06 CJK COMPATIBILITY IDEOGRAPH-FA06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa09 CJK COMPATIBILITY IDEOGRAPH-FA09 | fa08 CJK COMPATIBILITY IDEOGRAPH-FA08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa0b CJK COMPATIBILITY IDEOGRAPH-FA0B | fa0a CJK COMPATIBILITY IDEOGRAPH-FA0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa0d CJK COMPATIBILITY IDEOGRAPH-FA0D | fa0c CJK COMPATIBILITY IDEOGRAPH-FA0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa0f CJK COMPATIBILITY IDEOGRAPH-FA0F | fa0e CJK COMPATIBILITY IDEOGRAPH-FA0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa11 CJK COMPATIBILITY IDEOGRAPH-FA11 | fa10 CJK COMPATIBILITY IDEOGRAPH-FA10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa13 CJK COMPATIBILITY IDEOGRAPH-FA13 | fa12 CJK COMPATIBILITY IDEOGRAPH-FA12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa15 CJK COMPATIBILITY IDEOGRAPH-FA15 | fa14 CJK COMPATIBILITY IDEOGRAPH-FA14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa17 CJK COMPATIBILITY IDEOGRAPH-FA17 | fa16 CJK COMPATIBILITY IDEOGRAPH-FA16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa19 CJK COMPATIBILITY IDEOGRAPH-FA19 | fa18 CJK COMPATIBILITY IDEOGRAPH-FA18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa1b CJK COMPATIBILITY IDEOGRAPH-FA1B | fa1a CJK COMPATIBILITY IDEOGRAPH-FA1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa1d CJK COMPATIBILITY IDEOGRAPH-FA1D | fa1c CJK COMPATIBILITY IDEOGRAPH-FA1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa1f CJK COMPATIBILITY IDEOGRAPH-FA1F | fa1e CJK COMPATIBILITY IDEOGRAPH-FA1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa21 CJK COMPATIBILITY IDEOGRAPH-FA21 | fa20 CJK COMPATIBILITY IDEOGRAPH-FA20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa23 CJK COMPATIBILITY IDEOGRAPH-FA23 | fa22 CJK COMPATIBILITY IDEOGRAPH-FA22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa25 CJK COMPATIBILITY IDEOGRAPH-FA25 | fa24 CJK COMPATIBILITY IDEOGRAPH-FA24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa27 CJK COMPATIBILITY IDEOGRAPH-FA27 | fa26 CJK COMPATIBILITY IDEOGRAPH-FA26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa29 CJK COMPATIBILITY IDEOGRAPH-FA29 | fa28 CJK COMPATIBILITY IDEOGRAPH-FA28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa2b CJK COMPATIBILITY IDEOGRAPH-FA2B | fa2a CJK COMPATIBILITY IDEOGRAPH-FA2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa2d CJK COMPATIBILITY IDEOGRAPH-FA2D | fa2c CJK COMPATIBILITY IDEOGRAPH-FA2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa2f CJK COMPATIBILITY IDEOGRAPH-FA2F | fa2e CJK COMPATIBILITY IDEOGRAPH-FA2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa31 CJK COMPATIBILITY IDEOGRAPH-FA31 | fa30 CJK COMPATIBILITY IDEOGRAPH-FA30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa33 CJK COMPATIBILITY IDEOGRAPH-FA33 | fa32 CJK COMPATIBILITY IDEOGRAPH-FA32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa35 CJK COMPATIBILITY IDEOGRAPH-FA35 | fa34 CJK COMPATIBILITY IDEOGRAPH-FA34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa37 CJK COMPATIBILITY IDEOGRAPH-FA37 | fa36 CJK COMPATIBILITY IDEOGRAPH-FA36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa39 CJK COMPATIBILITY IDEOGRAPH-FA39 | fa38 CJK COMPATIBILITY IDEOGRAPH-FA38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa3b CJK COMPATIBILITY IDEOGRAPH-FA3B | fa3a CJK COMPATIBILITY IDEOGRAPH-FA3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa3d CJK COMPATIBILITY IDEOGRAPH-FA3D | fa3c CJK COMPATIBILITY IDEOGRAPH-FA3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa3f CJK COMPATIBILITY IDEOGRAPH-FA3F | fa3e CJK COMPATIBILITY IDEOGRAPH-FA3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa41 CJK COMPATIBILITY IDEOGRAPH-FA41 | fa40 CJK COMPATIBILITY IDEOGRAPH-FA40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa43 CJK COMPATIBILITY IDEOGRAPH-FA43 | fa42 CJK COMPATIBILITY IDEOGRAPH-FA42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa45 CJK COMPATIBILITY IDEOGRAPH-FA45 | fa44 CJK COMPATIBILITY IDEOGRAPH-FA44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa47 CJK COMPATIBILITY IDEOGRAPH-FA47 | fa46 CJK COMPATIBILITY IDEOGRAPH-FA46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa49 CJK COMPATIBILITY IDEOGRAPH-FA49 | fa48 CJK COMPATIBILITY IDEOGRAPH-FA48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa4b CJK COMPATIBILITY IDEOGRAPH-FA4B | fa4a CJK COMPATIBILITY IDEOGRAPH-FA4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa4d CJK COMPATIBILITY IDEOGRAPH-FA4D | fa4c CJK COMPATIBILITY IDEOGRAPH-FA4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa4f CJK COMPATIBILITY IDEOGRAPH-FA4F | fa4e CJK COMPATIBILITY IDEOGRAPH-FA4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa51 CJK COMPATIBILITY IDEOGRAPH-FA51 | fa50 CJK COMPATIBILITY IDEOGRAPH-FA50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa53 CJK COMPATIBILITY IDEOGRAPH-FA53 | fa52 CJK COMPATIBILITY IDEOGRAPH-FA52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa55 CJK COMPATIBILITY IDEOGRAPH-FA55 | fa54 CJK COMPATIBILITY IDEOGRAPH-FA54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa57 CJK COMPATIBILITY IDEOGRAPH-FA57 | fa56 CJK COMPATIBILITY IDEOGRAPH-FA56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa59 CJK COMPATIBILITY IDEOGRAPH-FA59 | fa58 CJK COMPATIBILITY IDEOGRAPH-FA58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa5b CJK COMPATIBILITY IDEOGRAPH-FA5B | fa5a CJK COMPATIBILITY IDEOGRAPH-FA5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa5d CJK COMPATIBILITY IDEOGRAPH-FA5D | fa5c CJK COMPATIBILITY IDEOGRAPH-FA5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa5f CJK COMPATIBILITY IDEOGRAPH-FA5F | fa5e CJK COMPATIBILITY IDEOGRAPH-FA5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa61 CJK COMPATIBILITY IDEOGRAPH-FA61 | fa60 CJK COMPATIBILITY IDEOGRAPH-FA60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa63 CJK COMPATIBILITY IDEOGRAPH-FA63 | fa62 CJK COMPATIBILITY IDEOGRAPH-FA62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa65 CJK COMPATIBILITY IDEOGRAPH-FA65 | fa64 CJK COMPATIBILITY IDEOGRAPH-FA64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa67 CJK COMPATIBILITY IDEOGRAPH-FA67 | fa66 CJK COMPATIBILITY IDEOGRAPH-FA66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa69 CJK COMPATIBILITY IDEOGRAPH-FA69 | fa68 CJK COMPATIBILITY IDEOGRAPH-FA68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa6b CJK COMPATIBILITY IDEOGRAPH-FA6B | fa6a CJK COMPATIBILITY IDEOGRAPH-FA6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa6d CJK COMPATIBILITY IDEOGRAPH-FA6D | fa6c CJK COMPATIBILITY IDEOGRAPH-FA6C */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fa6f (null) | fa6e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa71 CJK COMPATIBILITY IDEOGRAPH-FA71 | fa70 CJK COMPATIBILITY IDEOGRAPH-FA70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa73 CJK COMPATIBILITY IDEOGRAPH-FA73 | fa72 CJK COMPATIBILITY IDEOGRAPH-FA72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa75 CJK COMPATIBILITY IDEOGRAPH-FA75 | fa74 CJK COMPATIBILITY IDEOGRAPH-FA74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa77 CJK COMPATIBILITY IDEOGRAPH-FA77 | fa76 CJK COMPATIBILITY IDEOGRAPH-FA76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa79 CJK COMPATIBILITY IDEOGRAPH-FA79 | fa78 CJK COMPATIBILITY IDEOGRAPH-FA78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa7b CJK COMPATIBILITY IDEOGRAPH-FA7B | fa7a CJK COMPATIBILITY IDEOGRAPH-FA7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa7d CJK COMPATIBILITY IDEOGRAPH-FA7D | fa7c CJK COMPATIBILITY IDEOGRAPH-FA7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa7f CJK COMPATIBILITY IDEOGRAPH-FA7F | fa7e CJK COMPATIBILITY IDEOGRAPH-FA7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa81 CJK COMPATIBILITY IDEOGRAPH-FA81 | fa80 CJK COMPATIBILITY IDEOGRAPH-FA80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa83 CJK COMPATIBILITY IDEOGRAPH-FA83 | fa82 CJK COMPATIBILITY IDEOGRAPH-FA82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa85 CJK COMPATIBILITY IDEOGRAPH-FA85 | fa84 CJK COMPATIBILITY IDEOGRAPH-FA84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa87 CJK COMPATIBILITY IDEOGRAPH-FA87 | fa86 CJK COMPATIBILITY IDEOGRAPH-FA86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa89 CJK COMPATIBILITY IDEOGRAPH-FA89 | fa88 CJK COMPATIBILITY IDEOGRAPH-FA88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa8b CJK COMPATIBILITY IDEOGRAPH-FA8B | fa8a CJK COMPATIBILITY IDEOGRAPH-FA8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa8d CJK COMPATIBILITY IDEOGRAPH-FA8D | fa8c CJK COMPATIBILITY IDEOGRAPH-FA8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa8f CJK COMPATIBILITY IDEOGRAPH-FA8F | fa8e CJK COMPATIBILITY IDEOGRAPH-FA8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa91 CJK COMPATIBILITY IDEOGRAPH-FA91 | fa90 CJK COMPATIBILITY IDEOGRAPH-FA90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa93 CJK COMPATIBILITY IDEOGRAPH-FA93 | fa92 CJK COMPATIBILITY IDEOGRAPH-FA92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa95 CJK COMPATIBILITY IDEOGRAPH-FA95 | fa94 CJK COMPATIBILITY IDEOGRAPH-FA94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa97 CJK COMPATIBILITY IDEOGRAPH-FA97 | fa96 CJK COMPATIBILITY IDEOGRAPH-FA96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa99 CJK COMPATIBILITY IDEOGRAPH-FA99 | fa98 CJK COMPATIBILITY IDEOGRAPH-FA98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa9b CJK COMPATIBILITY IDEOGRAPH-FA9B | fa9a CJK COMPATIBILITY IDEOGRAPH-FA9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa9d CJK COMPATIBILITY IDEOGRAPH-FA9D | fa9c CJK COMPATIBILITY IDEOGRAPH-FA9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa9f CJK COMPATIBILITY IDEOGRAPH-FA9F | fa9e CJK COMPATIBILITY IDEOGRAPH-FA9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* faa1 CJK COMPATIBILITY IDEOGRAPH-FAA1 | faa0 CJK COMPATIBILITY IDEOGRAPH-FAA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* faa3 CJK COMPATIBILITY IDEOGRAPH-FAA3 | faa2 CJK COMPATIBILITY IDEOGRAPH-FAA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* faa5 CJK COMPATIBILITY IDEOGRAPH-FAA5 | faa4 CJK COMPATIBILITY IDEOGRAPH-FAA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* faa7 CJK COMPATIBILITY IDEOGRAPH-FAA7 | faa6 CJK COMPATIBILITY IDEOGRAPH-FAA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* faa9 CJK COMPATIBILITY IDEOGRAPH-FAA9 | faa8 CJK COMPATIBILITY IDEOGRAPH-FAA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* faab CJK COMPATIBILITY IDEOGRAPH-FAAB | faaa CJK COMPATIBILITY IDEOGRAPH-FAAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* faad CJK COMPATIBILITY IDEOGRAPH-FAAD | faac CJK COMPATIBILITY IDEOGRAPH-FAAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* faaf CJK COMPATIBILITY IDEOGRAPH-FAAF | faae CJK COMPATIBILITY IDEOGRAPH-FAAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fab1 CJK COMPATIBILITY IDEOGRAPH-FAB1 | fab0 CJK COMPATIBILITY IDEOGRAPH-FAB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fab3 CJK COMPATIBILITY IDEOGRAPH-FAB3 | fab2 CJK COMPATIBILITY IDEOGRAPH-FAB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fab5 CJK COMPATIBILITY IDEOGRAPH-FAB5 | fab4 CJK COMPATIBILITY IDEOGRAPH-FAB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fab7 CJK COMPATIBILITY IDEOGRAPH-FAB7 | fab6 CJK COMPATIBILITY IDEOGRAPH-FAB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fab9 CJK COMPATIBILITY IDEOGRAPH-FAB9 | fab8 CJK COMPATIBILITY IDEOGRAPH-FAB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fabb CJK COMPATIBILITY IDEOGRAPH-FABB | faba CJK COMPATIBILITY IDEOGRAPH-FABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fabd CJK COMPATIBILITY IDEOGRAPH-FABD | fabc CJK COMPATIBILITY IDEOGRAPH-FABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fabf CJK COMPATIBILITY IDEOGRAPH-FABF | fabe CJK COMPATIBILITY IDEOGRAPH-FABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fac1 CJK COMPATIBILITY IDEOGRAPH-FAC1 | fac0 CJK COMPATIBILITY IDEOGRAPH-FAC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fac3 CJK COMPATIBILITY IDEOGRAPH-FAC3 | fac2 CJK COMPATIBILITY IDEOGRAPH-FAC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fac5 CJK COMPATIBILITY IDEOGRAPH-FAC5 | fac4 CJK COMPATIBILITY IDEOGRAPH-FAC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fac7 CJK COMPATIBILITY IDEOGRAPH-FAC7 | fac6 CJK COMPATIBILITY IDEOGRAPH-FAC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fac9 CJK COMPATIBILITY IDEOGRAPH-FAC9 | fac8 CJK COMPATIBILITY IDEOGRAPH-FAC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* facb CJK COMPATIBILITY IDEOGRAPH-FACB | faca CJK COMPATIBILITY IDEOGRAPH-FACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* facd CJK COMPATIBILITY IDEOGRAPH-FACD | facc CJK COMPATIBILITY IDEOGRAPH-FACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* facf CJK COMPATIBILITY IDEOGRAPH-FACF | face CJK COMPATIBILITY IDEOGRAPH-FACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fad1 CJK COMPATIBILITY IDEOGRAPH-FAD1 | fad0 CJK COMPATIBILITY IDEOGRAPH-FAD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fad3 CJK COMPATIBILITY IDEOGRAPH-FAD3 | fad2 CJK COMPATIBILITY IDEOGRAPH-FAD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fad5 CJK COMPATIBILITY IDEOGRAPH-FAD5 | fad4 CJK COMPATIBILITY IDEOGRAPH-FAD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fad7 CJK COMPATIBILITY IDEOGRAPH-FAD7 | fad6 CJK COMPATIBILITY IDEOGRAPH-FAD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fad9 CJK COMPATIBILITY IDEOGRAPH-FAD9 | fad8 CJK COMPATIBILITY IDEOGRAPH-FAD8 */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fadb (null) | fada (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fadd (null) | fadc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fadf (null) | fade (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fae1 (null) | fae0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fae3 (null) | fae2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fae5 (null) | fae4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fae7 (null) | fae6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fae9 (null) | fae8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faeb (null) | faea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faed (null) | faec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faef (null) | faee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faf1 (null) | faf0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faf3 (null) | faf2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faf5 (null) | faf4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faf7 (null) | faf6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faf9 (null) | faf8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fafb (null) | fafa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fafd (null) | fafc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faff (null) | fafe (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* fb01 LATIN SMALL LIGATURE FI | fb00 LATIN SMALL LIGATURE FF */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* fb03 LATIN SMALL LIGATURE FFI | fb02 LATIN SMALL LIGATURE FL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* fb05 LATIN SMALL LIGATURE LONG S T | fb04 LATIN SMALL LIGATURE FFL */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_LOWER, /* fb07 (null) | fb06 LATIN SMALL LIGATURE ST */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fb09 (null) | fb08 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fb0b (null) | fb0a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fb0d (null) | fb0c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fb0f (null) | fb0e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fb11 (null) | fb10 (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UNDEF, /* fb13 ARMENIAN SMALL LIGATURE MEN NOW | fb12 (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* fb15 ARMENIAN SMALL LIGATURE MEN INI | fb14 ARMENIAN SMALL LIGATURE MEN ECH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* fb17 ARMENIAN SMALL LIGATURE MEN XEH | fb16 ARMENIAN SMALL LIGATURE VEW NOW */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fb19 (null) | fb18 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fb1b (null) | fb1a (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* fb1d HEBREW LETTER YOD WITH HIRIQ | fb1c (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* fb1f HEBREW LIGATURE YIDDISH YOD YOD PATAH | fb1e HEBREW POINT JUDEO-SPANISH VARIKA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb21 HEBREW LETTER WIDE ALEF | fb20 HEBREW LETTER ALTERNATIVE AYIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb23 HEBREW LETTER WIDE HE | fb22 HEBREW LETTER WIDE DALET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb25 HEBREW LETTER WIDE LAMED | fb24 HEBREW LETTER WIDE KAF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb27 HEBREW LETTER WIDE RESH | fb26 HEBREW LETTER WIDE FINAL MEM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* fb29 HEBREW LETTER ALTERNATIVE PLUS SIGN | fb28 HEBREW LETTER WIDE TAV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb2b HEBREW LETTER SHIN WITH SIN DOT | fb2a HEBREW LETTER SHIN WITH SHIN DOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb2d HEBREW LETTER SHIN WITH DAGESH AND SIN | fb2c HEBREW LETTER SHIN WITH DAGESH AND SHIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb2f HEBREW LETTER ALEF WITH QAMATS | fb2e HEBREW LETTER ALEF WITH PATAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb31 HEBREW LETTER BET WITH DAGESH | fb30 HEBREW LETTER ALEF WITH MAPIQ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb33 HEBREW LETTER DALET WITH DAGESH | fb32 HEBREW LETTER GIMEL WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb35 HEBREW LETTER VAV WITH DAGESH | fb34 HEBREW LETTER HE WITH MAPIQ */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* fb37 (null) | fb36 HEBREW LETTER ZAYIN WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb39 HEBREW LETTER YOD WITH DAGESH | fb38 HEBREW LETTER TET WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb3b HEBREW LETTER KAF WITH DAGESH | fb3a HEBREW LETTER FINAL KAF WITH DAGESH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* fb3d (null) | fb3c HEBREW LETTER LAMED WITH DAGESH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* fb3f (null) | fb3e HEBREW LETTER MEM WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb41 HEBREW LETTER SAMEKH WITH DAGESH | fb40 HEBREW LETTER NUN WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* fb43 HEBREW LETTER FINAL PE WITH DAGESH | fb42 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* fb45 (null) | fb44 HEBREW LETTER PE WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb47 HEBREW LETTER QOF WITH DAGESH | fb46 HEBREW LETTER TSADI WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb49 HEBREW LETTER SHIN WITH DAGESH | fb48 HEBREW LETTER RESH WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb4b HEBREW LETTER VAV WITH HOLAM | fb4a HEBREW LETTER TAV WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb4d HEBREW LETTER KAF WITH RAFE | fb4c HEBREW LETTER BET WITH RAFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb4f HEBREW LIGATURE ALEF LAMED | fb4e HEBREW LETTER PE WITH RAFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb51 ARABIC LETTER ALEF WASLA FINAL FORM | fb50 ARABIC LETTER ALEF WASLA ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb53 ARABIC LETTER BEEH FINAL FORM | fb52 ARABIC LETTER BEEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb55 ARABIC LETTER BEEH MEDIAL FORM | fb54 ARABIC LETTER BEEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb57 ARABIC LETTER PEH FINAL FORM | fb56 ARABIC LETTER PEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb59 ARABIC LETTER PEH MEDIAL FORM | fb58 ARABIC LETTER PEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb5b ARABIC LETTER BEHEH FINAL FORM | fb5a ARABIC LETTER BEHEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb5d ARABIC LETTER BEHEH MEDIAL FORM | fb5c ARABIC LETTER BEHEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb5f ARABIC LETTER TTEHEH FINAL FORM | fb5e ARABIC LETTER TTEHEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb61 ARABIC LETTER TTEHEH MEDIAL FORM | fb60 ARABIC LETTER TTEHEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb63 ARABIC LETTER TEHEH FINAL FORM | fb62 ARABIC LETTER TEHEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb65 ARABIC LETTER TEHEH MEDIAL FORM | fb64 ARABIC LETTER TEHEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb67 ARABIC LETTER TTEH FINAL FORM | fb66 ARABIC LETTER TTEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb69 ARABIC LETTER TTEH MEDIAL FORM | fb68 ARABIC LETTER TTEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb6b ARABIC LETTER VEH FINAL FORM | fb6a ARABIC LETTER VEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb6d ARABIC LETTER VEH MEDIAL FORM | fb6c ARABIC LETTER VEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb6f ARABIC LETTER PEHEH FINAL FORM | fb6e ARABIC LETTER PEHEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb71 ARABIC LETTER PEHEH MEDIAL FORM | fb70 ARABIC LETTER PEHEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb73 ARABIC LETTER DYEH FINAL FORM | fb72 ARABIC LETTER DYEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb75 ARABIC LETTER DYEH MEDIAL FORM | fb74 ARABIC LETTER DYEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb77 ARABIC LETTER NYEH FINAL FORM | fb76 ARABIC LETTER NYEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb79 ARABIC LETTER NYEH MEDIAL FORM | fb78 ARABIC LETTER NYEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb7b ARABIC LETTER TCHEH FINAL FORM | fb7a ARABIC LETTER TCHEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb7d ARABIC LETTER TCHEH MEDIAL FORM | fb7c ARABIC LETTER TCHEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb7f ARABIC LETTER TCHEHEH FINAL FORM | fb7e ARABIC LETTER TCHEHEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb81 ARABIC LETTER TCHEHEH MEDIAL FORM | fb80 ARABIC LETTER TCHEHEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb83 ARABIC LETTER DDAHAL FINAL FORM | fb82 ARABIC LETTER DDAHAL ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb85 ARABIC LETTER DAHAL FINAL FORM | fb84 ARABIC LETTER DAHAL ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb87 ARABIC LETTER DUL FINAL FORM | fb86 ARABIC LETTER DUL ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb89 ARABIC LETTER DDAL FINAL FORM | fb88 ARABIC LETTER DDAL ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb8b ARABIC LETTER JEH FINAL FORM | fb8a ARABIC LETTER JEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb8d ARABIC LETTER RREH FINAL FORM | fb8c ARABIC LETTER RREH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb8f ARABIC LETTER KEHEH FINAL FORM | fb8e ARABIC LETTER KEHEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb91 ARABIC LETTER KEHEH MEDIAL FORM | fb90 ARABIC LETTER KEHEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb93 ARABIC LETTER GAF FINAL FORM | fb92 ARABIC LETTER GAF ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb95 ARABIC LETTER GAF MEDIAL FORM | fb94 ARABIC LETTER GAF INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb97 ARABIC LETTER GUEH FINAL FORM | fb96 ARABIC LETTER GUEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb99 ARABIC LETTER GUEH MEDIAL FORM | fb98 ARABIC LETTER GUEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb9b ARABIC LETTER NGOEH FINAL FORM | fb9a ARABIC LETTER NGOEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb9d ARABIC LETTER NGOEH MEDIAL FORM | fb9c ARABIC LETTER NGOEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb9f ARABIC LETTER NOON GHUNNA FINAL FORM | fb9e ARABIC LETTER NOON GHUNNA ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fba1 ARABIC LETTER RNOON FINAL FORM | fba0 ARABIC LETTER RNOON ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fba3 ARABIC LETTER RNOON MEDIAL FORM | fba2 ARABIC LETTER RNOON INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fba5 ARABIC LETTER HEH WITH YEH ABOVE FINAL | fba4 ARABIC LETTER HEH WITH YEH ABOVE ISOLAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fba7 ARABIC LETTER HEH GOAL FINAL FORM | fba6 ARABIC LETTER HEH GOAL ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fba9 ARABIC LETTER HEH GOAL MEDIAL FORM | fba8 ARABIC LETTER HEH GOAL INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbab ARABIC LETTER HEH DOACHASHMEE FINAL FOR | fbaa ARABIC LETTER HEH DOACHASHMEE ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbad ARABIC LETTER HEH DOACHASHMEE MEDIAL FO | fbac ARABIC LETTER HEH DOACHASHMEE INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbaf ARABIC LETTER YEH BARREE FINAL FORM | fbae ARABIC LETTER YEH BARREE ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbb1 ARABIC LETTER YEH BARREE WITH HAMZA ABO | fbb0 ARABIC LETTER YEH BARREE WITH HAMZA ABO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fbb3 ARABIC SYMBOL DOT BELOW | fbb2 ARABIC SYMBOL DOT ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fbb5 ARABIC SYMBOL TWO DOTS BELOW | fbb4 ARABIC SYMBOL TWO DOTS ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fbb7 ARABIC SYMBOL THREE DOTS BELOW | fbb6 ARABIC SYMBOL THREE DOTS ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fbb9 ARABIC SYMBOL THREE DOTS POINTING DOWNW | fbb8 ARABIC SYMBOL THREE DOTS POINTING DOWNW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fbbb ARABIC SYMBOL FOUR DOTS BELOW | fbba ARABIC SYMBOL FOUR DOTS ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fbbd ARABIC SYMBOL TWO DOTS VERTICALLY ABOVE | fbbc ARABIC SYMBOL DOUBLE VERTICAL BAR BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fbbf ARABIC SYMBOL RING | fbbe ARABIC SYMBOL TWO DOTS VERTICALLY BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fbc1 ARABIC SYMBOL SMALL TAH BELOW | fbc0 ARABIC SYMBOL SMALL TAH ABOVE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fbc3 (null) | fbc2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fbc5 (null) | fbc4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fbc7 (null) | fbc6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fbc9 (null) | fbc8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fbcb (null) | fbca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fbcd (null) | fbcc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fbcf (null) | fbce (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fbd1 (null) | fbd0 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* fbd3 ARABIC LETTER NG ISOLATED FORM | fbd2 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbd5 ARABIC LETTER NG INITIAL FORM | fbd4 ARABIC LETTER NG FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbd7 ARABIC LETTER U ISOLATED FORM | fbd6 ARABIC LETTER NG MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbd9 ARABIC LETTER OE ISOLATED FORM | fbd8 ARABIC LETTER U FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbdb ARABIC LETTER YU ISOLATED FORM | fbda ARABIC LETTER OE FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbdd ARABIC LETTER U WITH HAMZA ABOVE ISOLAT | fbdc ARABIC LETTER YU FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbdf ARABIC LETTER VE FINAL FORM | fbde ARABIC LETTER VE ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbe1 ARABIC LETTER KIRGHIZ OE FINAL FORM | fbe0 ARABIC LETTER KIRGHIZ OE ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbe3 ARABIC LETTER KIRGHIZ YU FINAL FORM | fbe2 ARABIC LETTER KIRGHIZ YU ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbe5 ARABIC LETTER E FINAL FORM | fbe4 ARABIC LETTER E ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbe7 ARABIC LETTER E MEDIAL FORM | fbe6 ARABIC LETTER E INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbe9 ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALE | fbe8 ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbeb ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fbea ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbed ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fbec ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbef ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fbee ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbf1 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fbf0 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbf3 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fbf2 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbf5 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fbf4 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbf7 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fbf6 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbf9 ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH | fbf8 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbfb ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH | fbfa ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbfd ARABIC LETTER FARSI YEH FINAL FORM | fbfc ARABIC LETTER FARSI YEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbff ARABIC LETTER FARSI YEH MEDIAL FORM | fbfe ARABIC LETTER FARSI YEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc01 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fc00 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc03 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fc02 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc05 ARABIC LIGATURE BEH WITH JEEM ISOLATED | fc04 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc07 ARABIC LIGATURE BEH WITH KHAH ISOLATED | fc06 ARABIC LIGATURE BEH WITH HAH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc09 ARABIC LIGATURE BEH WITH ALEF MAKSURA I | fc08 ARABIC LIGATURE BEH WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc0b ARABIC LIGATURE TEH WITH JEEM ISOLATED | fc0a ARABIC LIGATURE BEH WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc0d ARABIC LIGATURE TEH WITH KHAH ISOLATED | fc0c ARABIC LIGATURE TEH WITH HAH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc0f ARABIC LIGATURE TEH WITH ALEF MAKSURA I | fc0e ARABIC LIGATURE TEH WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc11 ARABIC LIGATURE THEH WITH JEEM ISOLATED | fc10 ARABIC LIGATURE TEH WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc13 ARABIC LIGATURE THEH WITH ALEF MAKSURA | fc12 ARABIC LIGATURE THEH WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc15 ARABIC LIGATURE JEEM WITH HAH ISOLATED | fc14 ARABIC LIGATURE THEH WITH YEH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc17 ARABIC LIGATURE HAH WITH JEEM ISOLATED | fc16 ARABIC LIGATURE JEEM WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc19 ARABIC LIGATURE KHAH WITH JEEM ISOLATED | fc18 ARABIC LIGATURE HAH WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc1b ARABIC LIGATURE KHAH WITH MEEM ISOLATED | fc1a ARABIC LIGATURE KHAH WITH HAH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc1d ARABIC LIGATURE SEEN WITH HAH ISOLATED | fc1c ARABIC LIGATURE SEEN WITH JEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc1f ARABIC LIGATURE SEEN WITH MEEM ISOLATED | fc1e ARABIC LIGATURE SEEN WITH KHAH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc21 ARABIC LIGATURE SAD WITH MEEM ISOLATED | fc20 ARABIC LIGATURE SAD WITH HAH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc23 ARABIC LIGATURE DAD WITH HAH ISOLATED F | fc22 ARABIC LIGATURE DAD WITH JEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc25 ARABIC LIGATURE DAD WITH MEEM ISOLATED | fc24 ARABIC LIGATURE DAD WITH KHAH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc27 ARABIC LIGATURE TAH WITH MEEM ISOLATED | fc26 ARABIC LIGATURE TAH WITH HAH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc29 ARABIC LIGATURE AIN WITH JEEM ISOLATED | fc28 ARABIC LIGATURE ZAH WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc2b ARABIC LIGATURE GHAIN WITH JEEM ISOLATE | fc2a ARABIC LIGATURE AIN WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc2d ARABIC LIGATURE FEH WITH JEEM ISOLATED | fc2c ARABIC LIGATURE GHAIN WITH MEEM ISOLATE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc2f ARABIC LIGATURE FEH WITH KHAH ISOLATED | fc2e ARABIC LIGATURE FEH WITH HAH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc31 ARABIC LIGATURE FEH WITH ALEF MAKSURA I | fc30 ARABIC LIGATURE FEH WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc33 ARABIC LIGATURE QAF WITH HAH ISOLATED F | fc32 ARABIC LIGATURE FEH WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc35 ARABIC LIGATURE QAF WITH ALEF MAKSURA I | fc34 ARABIC LIGATURE QAF WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc37 ARABIC LIGATURE KAF WITH ALEF ISOLATED | fc36 ARABIC LIGATURE QAF WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc39 ARABIC LIGATURE KAF WITH HAH ISOLATED F | fc38 ARABIC LIGATURE KAF WITH JEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc3b ARABIC LIGATURE KAF WITH LAM ISOLATED F | fc3a ARABIC LIGATURE KAF WITH KHAH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc3d ARABIC LIGATURE KAF WITH ALEF MAKSURA I | fc3c ARABIC LIGATURE KAF WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc3f ARABIC LIGATURE LAM WITH JEEM ISOLATED | fc3e ARABIC LIGATURE KAF WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc41 ARABIC LIGATURE LAM WITH KHAH ISOLATED | fc40 ARABIC LIGATURE LAM WITH HAH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc43 ARABIC LIGATURE LAM WITH ALEF MAKSURA I | fc42 ARABIC LIGATURE LAM WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc45 ARABIC LIGATURE MEEM WITH JEEM ISOLATED | fc44 ARABIC LIGATURE LAM WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc47 ARABIC LIGATURE MEEM WITH KHAH ISOLATED | fc46 ARABIC LIGATURE MEEM WITH HAH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc49 ARABIC LIGATURE MEEM WITH ALEF MAKSURA | fc48 ARABIC LIGATURE MEEM WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc4b ARABIC LIGATURE NOON WITH JEEM ISOLATED | fc4a ARABIC LIGATURE MEEM WITH YEH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc4d ARABIC LIGATURE NOON WITH KHAH ISOLATED | fc4c ARABIC LIGATURE NOON WITH HAH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc4f ARABIC LIGATURE NOON WITH ALEF MAKSURA | fc4e ARABIC LIGATURE NOON WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc51 ARABIC LIGATURE HEH WITH JEEM ISOLATED | fc50 ARABIC LIGATURE NOON WITH YEH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc53 ARABIC LIGATURE HEH WITH ALEF MAKSURA I | fc52 ARABIC LIGATURE HEH WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc55 ARABIC LIGATURE YEH WITH JEEM ISOLATED | fc54 ARABIC LIGATURE HEH WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc57 ARABIC LIGATURE YEH WITH KHAH ISOLATED | fc56 ARABIC LIGATURE YEH WITH HAH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc59 ARABIC LIGATURE YEH WITH ALEF MAKSURA I | fc58 ARABIC LIGATURE YEH WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc5b ARABIC LIGATURE THAL WITH SUPERSCRIPT A | fc5a ARABIC LIGATURE YEH WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc5d ARABIC LIGATURE ALEF MAKSURA WITH SUPER | fc5c ARABIC LIGATURE REH WITH SUPERSCRIPT AL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc5f ARABIC LIGATURE SHADDA WITH KASRATAN IS | fc5e ARABIC LIGATURE SHADDA WITH DAMMATAN IS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc61 ARABIC LIGATURE SHADDA WITH DAMMA ISOLA | fc60 ARABIC LIGATURE SHADDA WITH FATHA ISOLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc63 ARABIC LIGATURE SHADDA WITH SUPERSCRIPT | fc62 ARABIC LIGATURE SHADDA WITH KASRA ISOLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc65 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fc64 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc67 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fc66 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc69 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fc68 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc6b ARABIC LIGATURE BEH WITH ZAIN FINAL FOR | fc6a ARABIC LIGATURE BEH WITH REH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc6d ARABIC LIGATURE BEH WITH NOON FINAL FOR | fc6c ARABIC LIGATURE BEH WITH MEEM FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc6f ARABIC LIGATURE BEH WITH YEH FINAL FORM | fc6e ARABIC LIGATURE BEH WITH ALEF MAKSURA F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc71 ARABIC LIGATURE TEH WITH ZAIN FINAL FOR | fc70 ARABIC LIGATURE TEH WITH REH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc73 ARABIC LIGATURE TEH WITH NOON FINAL FOR | fc72 ARABIC LIGATURE TEH WITH MEEM FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc75 ARABIC LIGATURE TEH WITH YEH FINAL FORM | fc74 ARABIC LIGATURE TEH WITH ALEF MAKSURA F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc77 ARABIC LIGATURE THEH WITH ZAIN FINAL FO | fc76 ARABIC LIGATURE THEH WITH REH FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc79 ARABIC LIGATURE THEH WITH NOON FINAL FO | fc78 ARABIC LIGATURE THEH WITH MEEM FINAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc7b ARABIC LIGATURE THEH WITH YEH FINAL FOR | fc7a ARABIC LIGATURE THEH WITH ALEF MAKSURA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc7d ARABIC LIGATURE FEH WITH YEH FINAL FORM | fc7c ARABIC LIGATURE FEH WITH ALEF MAKSURA F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc7f ARABIC LIGATURE QAF WITH YEH FINAL FORM | fc7e ARABIC LIGATURE QAF WITH ALEF MAKSURA F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc81 ARABIC LIGATURE KAF WITH LAM FINAL FORM | fc80 ARABIC LIGATURE KAF WITH ALEF FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc83 ARABIC LIGATURE KAF WITH ALEF MAKSURA F | fc82 ARABIC LIGATURE KAF WITH MEEM FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc85 ARABIC LIGATURE LAM WITH MEEM FINAL FOR | fc84 ARABIC LIGATURE KAF WITH YEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc87 ARABIC LIGATURE LAM WITH YEH FINAL FORM | fc86 ARABIC LIGATURE LAM WITH ALEF MAKSURA F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc89 ARABIC LIGATURE MEEM WITH MEEM FINAL FO | fc88 ARABIC LIGATURE MEEM WITH ALEF FINAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc8b ARABIC LIGATURE NOON WITH ZAIN FINAL FO | fc8a ARABIC LIGATURE NOON WITH REH FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc8d ARABIC LIGATURE NOON WITH NOON FINAL FO | fc8c ARABIC LIGATURE NOON WITH MEEM FINAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc8f ARABIC LIGATURE NOON WITH YEH FINAL FOR | fc8e ARABIC LIGATURE NOON WITH ALEF MAKSURA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc91 ARABIC LIGATURE YEH WITH REH FINAL FORM | fc90 ARABIC LIGATURE ALEF MAKSURA WITH SUPER */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc93 ARABIC LIGATURE YEH WITH MEEM FINAL FOR | fc92 ARABIC LIGATURE YEH WITH ZAIN FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc95 ARABIC LIGATURE YEH WITH ALEF MAKSURA F | fc94 ARABIC LIGATURE YEH WITH NOON FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc97 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fc96 ARABIC LIGATURE YEH WITH YEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc99 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fc98 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc9b ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fc9a ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc9d ARABIC LIGATURE BEH WITH HAH INITIAL FO | fc9c ARABIC LIGATURE BEH WITH JEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc9f ARABIC LIGATURE BEH WITH MEEM INITIAL F | fc9e ARABIC LIGATURE BEH WITH KHAH INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fca1 ARABIC LIGATURE TEH WITH JEEM INITIAL F | fca0 ARABIC LIGATURE BEH WITH HEH INITIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fca3 ARABIC LIGATURE TEH WITH KHAH INITIAL F | fca2 ARABIC LIGATURE TEH WITH HAH INITIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fca5 ARABIC LIGATURE TEH WITH HEH INITIAL FO | fca4 ARABIC LIGATURE TEH WITH MEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fca7 ARABIC LIGATURE JEEM WITH HAH INITIAL F | fca6 ARABIC LIGATURE THEH WITH MEEM INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fca9 ARABIC LIGATURE HAH WITH JEEM INITIAL F | fca8 ARABIC LIGATURE JEEM WITH MEEM INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcab ARABIC LIGATURE KHAH WITH JEEM INITIAL | fcaa ARABIC LIGATURE HAH WITH MEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcad ARABIC LIGATURE SEEN WITH JEEM INITIAL | fcac ARABIC LIGATURE KHAH WITH MEEM INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcaf ARABIC LIGATURE SEEN WITH KHAH INITIAL | fcae ARABIC LIGATURE SEEN WITH HAH INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcb1 ARABIC LIGATURE SAD WITH HAH INITIAL FO | fcb0 ARABIC LIGATURE SEEN WITH MEEM INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcb3 ARABIC LIGATURE SAD WITH MEEM INITIAL F | fcb2 ARABIC LIGATURE SAD WITH KHAH INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcb5 ARABIC LIGATURE DAD WITH HAH INITIAL FO | fcb4 ARABIC LIGATURE DAD WITH JEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcb7 ARABIC LIGATURE DAD WITH MEEM INITIAL F | fcb6 ARABIC LIGATURE DAD WITH KHAH INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcb9 ARABIC LIGATURE ZAH WITH MEEM INITIAL F | fcb8 ARABIC LIGATURE TAH WITH HAH INITIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcbb ARABIC LIGATURE AIN WITH MEEM INITIAL F | fcba ARABIC LIGATURE AIN WITH JEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcbd ARABIC LIGATURE GHAIN WITH MEEM INITIAL | fcbc ARABIC LIGATURE GHAIN WITH JEEM INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcbf ARABIC LIGATURE FEH WITH HAH INITIAL FO | fcbe ARABIC LIGATURE FEH WITH JEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcc1 ARABIC LIGATURE FEH WITH MEEM INITIAL F | fcc0 ARABIC LIGATURE FEH WITH KHAH INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcc3 ARABIC LIGATURE QAF WITH MEEM INITIAL F | fcc2 ARABIC LIGATURE QAF WITH HAH INITIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcc5 ARABIC LIGATURE KAF WITH HAH INITIAL FO | fcc4 ARABIC LIGATURE KAF WITH JEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcc7 ARABIC LIGATURE KAF WITH LAM INITIAL FO | fcc6 ARABIC LIGATURE KAF WITH KHAH INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcc9 ARABIC LIGATURE LAM WITH JEEM INITIAL F | fcc8 ARABIC LIGATURE KAF WITH MEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fccb ARABIC LIGATURE LAM WITH KHAH INITIAL F | fcca ARABIC LIGATURE LAM WITH HAH INITIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fccd ARABIC LIGATURE LAM WITH HEH INITIAL FO | fccc ARABIC LIGATURE LAM WITH MEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fccf ARABIC LIGATURE MEEM WITH HAH INITIAL F | fcce ARABIC LIGATURE MEEM WITH JEEM INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcd1 ARABIC LIGATURE MEEM WITH MEEM INITIAL | fcd0 ARABIC LIGATURE MEEM WITH KHAH INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcd3 ARABIC LIGATURE NOON WITH HAH INITIAL F | fcd2 ARABIC LIGATURE NOON WITH JEEM INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcd5 ARABIC LIGATURE NOON WITH MEEM INITIAL | fcd4 ARABIC LIGATURE NOON WITH KHAH INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcd7 ARABIC LIGATURE HEH WITH JEEM INITIAL F | fcd6 ARABIC LIGATURE NOON WITH HEH INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcd9 ARABIC LIGATURE HEH WITH SUPERSCRIPT AL | fcd8 ARABIC LIGATURE HEH WITH MEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcdb ARABIC LIGATURE YEH WITH HAH INITIAL FO | fcda ARABIC LIGATURE YEH WITH JEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcdd ARABIC LIGATURE YEH WITH MEEM INITIAL F | fcdc ARABIC LIGATURE YEH WITH KHAH INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcdf ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fcde ARABIC LIGATURE YEH WITH HEH INITIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fce1 ARABIC LIGATURE BEH WITH MEEM MEDIAL FO | fce0 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fce3 ARABIC LIGATURE TEH WITH MEEM MEDIAL FO | fce2 ARABIC LIGATURE BEH WITH HEH MEDIAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fce5 ARABIC LIGATURE THEH WITH MEEM MEDIAL F | fce4 ARABIC LIGATURE TEH WITH HEH MEDIAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fce7 ARABIC LIGATURE SEEN WITH MEEM MEDIAL F | fce6 ARABIC LIGATURE THEH WITH HEH MEDIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fce9 ARABIC LIGATURE SHEEN WITH MEEM MEDIAL | fce8 ARABIC LIGATURE SEEN WITH HEH MEDIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fceb ARABIC LIGATURE KAF WITH LAM MEDIAL FOR | fcea ARABIC LIGATURE SHEEN WITH HEH MEDIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fced ARABIC LIGATURE LAM WITH MEEM MEDIAL FO | fcec ARABIC LIGATURE KAF WITH MEEM MEDIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcef ARABIC LIGATURE NOON WITH HEH MEDIAL FO | fcee ARABIC LIGATURE NOON WITH MEEM MEDIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcf1 ARABIC LIGATURE YEH WITH HEH MEDIAL FOR | fcf0 ARABIC LIGATURE YEH WITH MEEM MEDIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcf3 ARABIC LIGATURE SHADDA WITH DAMMA MEDIA | fcf2 ARABIC LIGATURE SHADDA WITH FATHA MEDIA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcf5 ARABIC LIGATURE TAH WITH ALEF MAKSURA I | fcf4 ARABIC LIGATURE SHADDA WITH KASRA MEDIA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcf7 ARABIC LIGATURE AIN WITH ALEF MAKSURA I | fcf6 ARABIC LIGATURE TAH WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcf9 ARABIC LIGATURE GHAIN WITH ALEF MAKSURA | fcf8 ARABIC LIGATURE AIN WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcfb ARABIC LIGATURE SEEN WITH ALEF MAKSURA | fcfa ARABIC LIGATURE GHAIN WITH YEH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcfd ARABIC LIGATURE SHEEN WITH ALEF MAKSURA | fcfc ARABIC LIGATURE SEEN WITH YEH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcff ARABIC LIGATURE HAH WITH ALEF MAKSURA I | fcfe ARABIC LIGATURE SHEEN WITH YEH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd01 ARABIC LIGATURE JEEM WITH ALEF MAKSURA | fd00 ARABIC LIGATURE HAH WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd03 ARABIC LIGATURE KHAH WITH ALEF MAKSURA | fd02 ARABIC LIGATURE JEEM WITH YEH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd05 ARABIC LIGATURE SAD WITH ALEF MAKSURA I | fd04 ARABIC LIGATURE KHAH WITH YEH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd07 ARABIC LIGATURE DAD WITH ALEF MAKSURA I | fd06 ARABIC LIGATURE SAD WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd09 ARABIC LIGATURE SHEEN WITH JEEM ISOLATE | fd08 ARABIC LIGATURE DAD WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd0b ARABIC LIGATURE SHEEN WITH KHAH ISOLATE | fd0a ARABIC LIGATURE SHEEN WITH HAH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd0d ARABIC LIGATURE SHEEN WITH REH ISOLATED | fd0c ARABIC LIGATURE SHEEN WITH MEEM ISOLATE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd0f ARABIC LIGATURE SAD WITH REH ISOLATED F | fd0e ARABIC LIGATURE SEEN WITH REH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd11 ARABIC LIGATURE TAH WITH ALEF MAKSURA F | fd10 ARABIC LIGATURE DAD WITH REH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd13 ARABIC LIGATURE AIN WITH ALEF MAKSURA F | fd12 ARABIC LIGATURE TAH WITH YEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd15 ARABIC LIGATURE GHAIN WITH ALEF MAKSURA | fd14 ARABIC LIGATURE AIN WITH YEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd17 ARABIC LIGATURE SEEN WITH ALEF MAKSURA | fd16 ARABIC LIGATURE GHAIN WITH YEH FINAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd19 ARABIC LIGATURE SHEEN WITH ALEF MAKSURA | fd18 ARABIC LIGATURE SEEN WITH YEH FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd1b ARABIC LIGATURE HAH WITH ALEF MAKSURA F | fd1a ARABIC LIGATURE SHEEN WITH YEH FINAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd1d ARABIC LIGATURE JEEM WITH ALEF MAKSURA | fd1c ARABIC LIGATURE HAH WITH YEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd1f ARABIC LIGATURE KHAH WITH ALEF MAKSURA | fd1e ARABIC LIGATURE JEEM WITH YEH FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd21 ARABIC LIGATURE SAD WITH ALEF MAKSURA F | fd20 ARABIC LIGATURE KHAH WITH YEH FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd23 ARABIC LIGATURE DAD WITH ALEF MAKSURA F | fd22 ARABIC LIGATURE SAD WITH YEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd25 ARABIC LIGATURE SHEEN WITH JEEM FINAL F | fd24 ARABIC LIGATURE DAD WITH YEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd27 ARABIC LIGATURE SHEEN WITH KHAH FINAL F | fd26 ARABIC LIGATURE SHEEN WITH HAH FINAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd29 ARABIC LIGATURE SHEEN WITH REH FINAL FO | fd28 ARABIC LIGATURE SHEEN WITH MEEM FINAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd2b ARABIC LIGATURE SAD WITH REH FINAL FORM | fd2a ARABIC LIGATURE SEEN WITH REH FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd2d ARABIC LIGATURE SHEEN WITH JEEM INITIAL | fd2c ARABIC LIGATURE DAD WITH REH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd2f ARABIC LIGATURE SHEEN WITH KHAH INITIAL | fd2e ARABIC LIGATURE SHEEN WITH HAH INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd31 ARABIC LIGATURE SEEN WITH HEH INITIAL F | fd30 ARABIC LIGATURE SHEEN WITH MEEM INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd33 ARABIC LIGATURE TAH WITH MEEM INITIAL F | fd32 ARABIC LIGATURE SHEEN WITH HEH INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd35 ARABIC LIGATURE SEEN WITH HAH MEDIAL FO | fd34 ARABIC LIGATURE SEEN WITH JEEM MEDIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd37 ARABIC LIGATURE SHEEN WITH JEEM MEDIAL | fd36 ARABIC LIGATURE SEEN WITH KHAH MEDIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd39 ARABIC LIGATURE SHEEN WITH KHAH MEDIAL | fd38 ARABIC LIGATURE SHEEN WITH HAH MEDIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd3b ARABIC LIGATURE ZAH WITH MEEM MEDIAL FO | fd3a ARABIC LIGATURE TAH WITH MEEM MEDIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd3d ARABIC LIGATURE ALEF WITH FATHATAN ISOL | fd3c ARABIC LIGATURE ALEF WITH FATHATAN FINA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fd3f ORNATE RIGHT PARENTHESIS | fd3e ORNATE LEFT PARENTHESIS */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd41 (null) | fd40 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd43 (null) | fd42 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd45 (null) | fd44 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd47 (null) | fd46 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd49 (null) | fd48 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd4b (null) | fd4a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd4d (null) | fd4c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd4f (null) | fd4e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd51 ARABIC LIGATURE TEH WITH HAH WITH JEEM | fd50 ARABIC LIGATURE TEH WITH JEEM WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd53 ARABIC LIGATURE TEH WITH HAH WITH MEEM | fd52 ARABIC LIGATURE TEH WITH HAH WITH JEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd55 ARABIC LIGATURE TEH WITH MEEM WITH JEEM | fd54 ARABIC LIGATURE TEH WITH KHAH WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd57 ARABIC LIGATURE TEH WITH MEEM WITH KHAH | fd56 ARABIC LIGATURE TEH WITH MEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd59 ARABIC LIGATURE JEEM WITH MEEM WITH HAH | fd58 ARABIC LIGATURE JEEM WITH MEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd5b ARABIC LIGATURE HAH WITH MEEM WITH ALEF | fd5a ARABIC LIGATURE HAH WITH MEEM WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd5d ARABIC LIGATURE SEEN WITH JEEM WITH HAH | fd5c ARABIC LIGATURE SEEN WITH HAH WITH JEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd5f ARABIC LIGATURE SEEN WITH MEEM WITH HAH | fd5e ARABIC LIGATURE SEEN WITH JEEM WITH ALE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd61 ARABIC LIGATURE SEEN WITH MEEM WITH JEE | fd60 ARABIC LIGATURE SEEN WITH MEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd63 ARABIC LIGATURE SEEN WITH MEEM WITH MEE | fd62 ARABIC LIGATURE SEEN WITH MEEM WITH MEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd65 ARABIC LIGATURE SAD WITH HAH WITH HAH I | fd64 ARABIC LIGATURE SAD WITH HAH WITH HAH F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd67 ARABIC LIGATURE SHEEN WITH HAH WITH MEE | fd66 ARABIC LIGATURE SAD WITH MEEM WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd69 ARABIC LIGATURE SHEEN WITH JEEM WITH YE | fd68 ARABIC LIGATURE SHEEN WITH HAH WITH MEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd6b ARABIC LIGATURE SHEEN WITH MEEM WITH KH | fd6a ARABIC LIGATURE SHEEN WITH MEEM WITH KH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd6d ARABIC LIGATURE SHEEN WITH MEEM WITH ME | fd6c ARABIC LIGATURE SHEEN WITH MEEM WITH ME */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd6f ARABIC LIGATURE DAD WITH KHAH WITH MEEM | fd6e ARABIC LIGATURE DAD WITH HAH WITH ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd71 ARABIC LIGATURE TAH WITH MEEM WITH HAH | fd70 ARABIC LIGATURE DAD WITH KHAH WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd73 ARABIC LIGATURE TAH WITH MEEM WITH MEEM | fd72 ARABIC LIGATURE TAH WITH MEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd75 ARABIC LIGATURE AIN WITH JEEM WITH MEEM | fd74 ARABIC LIGATURE TAH WITH MEEM WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd77 ARABIC LIGATURE AIN WITH MEEM WITH MEEM | fd76 ARABIC LIGATURE AIN WITH MEEM WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd79 ARABIC LIGATURE GHAIN WITH MEEM WITH ME | fd78 ARABIC LIGATURE AIN WITH MEEM WITH ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd7b ARABIC LIGATURE GHAIN WITH MEEM WITH AL | fd7a ARABIC LIGATURE GHAIN WITH MEEM WITH YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd7d ARABIC LIGATURE FEH WITH KHAH WITH MEEM | fd7c ARABIC LIGATURE FEH WITH KHAH WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd7f ARABIC LIGATURE QAF WITH MEEM WITH MEEM | fd7e ARABIC LIGATURE QAF WITH MEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd81 ARABIC LIGATURE LAM WITH HAH WITH YEH F | fd80 ARABIC LIGATURE LAM WITH HAH WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd83 ARABIC LIGATURE LAM WITH JEEM WITH JEEM | fd82 ARABIC LIGATURE LAM WITH HAH WITH ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd85 ARABIC LIGATURE LAM WITH KHAH WITH MEEM | fd84 ARABIC LIGATURE LAM WITH JEEM WITH JEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd87 ARABIC LIGATURE LAM WITH MEEM WITH HAH | fd86 ARABIC LIGATURE LAM WITH KHAH WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd89 ARABIC LIGATURE MEEM WITH HAH WITH JEEM | fd88 ARABIC LIGATURE LAM WITH MEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd8b ARABIC LIGATURE MEEM WITH HAH WITH YEH | fd8a ARABIC LIGATURE MEEM WITH HAH WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd8d ARABIC LIGATURE MEEM WITH JEEM WITH MEE | fd8c ARABIC LIGATURE MEEM WITH JEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd8f ARABIC LIGATURE MEEM WITH KHAH WITH MEE | fd8e ARABIC LIGATURE MEEM WITH KHAH WITH JEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd91 (null) | fd90 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd93 ARABIC LIGATURE HEH WITH MEEM WITH JEEM | fd92 ARABIC LIGATURE MEEM WITH JEEM WITH KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd95 ARABIC LIGATURE NOON WITH HAH WITH MEEM | fd94 ARABIC LIGATURE HEH WITH MEEM WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd97 ARABIC LIGATURE NOON WITH JEEM WITH MEE | fd96 ARABIC LIGATURE NOON WITH HAH WITH ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd99 ARABIC LIGATURE NOON WITH JEEM WITH ALE | fd98 ARABIC LIGATURE NOON WITH JEEM WITH MEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd9b ARABIC LIGATURE NOON WITH MEEM WITH ALE | fd9a ARABIC LIGATURE NOON WITH MEEM WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd9d ARABIC LIGATURE YEH WITH MEEM WITH MEEM | fd9c ARABIC LIGATURE YEH WITH MEEM WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd9f ARABIC LIGATURE TEH WITH JEEM WITH YEH | fd9e ARABIC LIGATURE BEH WITH KHAH WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fda1 ARABIC LIGATURE TEH WITH KHAH WITH YEH | fda0 ARABIC LIGATURE TEH WITH JEEM WITH ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fda3 ARABIC LIGATURE TEH WITH MEEM WITH YEH | fda2 ARABIC LIGATURE TEH WITH KHAH WITH ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fda5 ARABIC LIGATURE JEEM WITH MEEM WITH YEH | fda4 ARABIC LIGATURE TEH WITH MEEM WITH ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fda7 ARABIC LIGATURE JEEM WITH MEEM WITH ALE | fda6 ARABIC LIGATURE JEEM WITH HAH WITH ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fda9 ARABIC LIGATURE SAD WITH HAH WITH YEH F | fda8 ARABIC LIGATURE SEEN WITH KHAH WITH ALE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdab ARABIC LIGATURE DAD WITH HAH WITH YEH F | fdaa ARABIC LIGATURE SHEEN WITH HAH WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdad ARABIC LIGATURE LAM WITH MEEM WITH YEH | fdac ARABIC LIGATURE LAM WITH JEEM WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdaf ARABIC LIGATURE YEH WITH JEEM WITH YEH | fdae ARABIC LIGATURE YEH WITH HAH WITH YEH F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdb1 ARABIC LIGATURE MEEM WITH MEEM WITH YEH | fdb0 ARABIC LIGATURE YEH WITH MEEM WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdb3 ARABIC LIGATURE NOON WITH HAH WITH YEH | fdb2 ARABIC LIGATURE QAF WITH MEEM WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdb5 ARABIC LIGATURE LAM WITH HAH WITH MEEM | fdb4 ARABIC LIGATURE QAF WITH MEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdb7 ARABIC LIGATURE KAF WITH MEEM WITH YEH | fdb6 ARABIC LIGATURE AIN WITH MEEM WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdb9 ARABIC LIGATURE MEEM WITH KHAH WITH YEH | fdb8 ARABIC LIGATURE NOON WITH JEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdbb ARABIC LIGATURE KAF WITH MEEM WITH MEEM | fdba ARABIC LIGATURE LAM WITH JEEM WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdbd ARABIC LIGATURE NOON WITH JEEM WITH HAH | fdbc ARABIC LIGATURE LAM WITH JEEM WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdbf ARABIC LIGATURE HAH WITH JEEM WITH YEH | fdbe ARABIC LIGATURE JEEM WITH HAH WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdc1 ARABIC LIGATURE FEH WITH MEEM WITH YEH | fdc0 ARABIC LIGATURE MEEM WITH JEEM WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdc3 ARABIC LIGATURE KAF WITH MEEM WITH MEEM | fdc2 ARABIC LIGATURE BEH WITH HAH WITH YEH F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdc5 ARABIC LIGATURE SAD WITH MEEM WITH MEEM | fdc4 ARABIC LIGATURE AIN WITH JEEM WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdc7 ARABIC LIGATURE NOON WITH JEEM WITH YEH | fdc6 ARABIC LIGATURE SEEN WITH KHAH WITH YEH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdc9 (null) | fdc8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdcb (null) | fdca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdcd (null) | fdcc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdcf (null) | fdce (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdd1 (null) | fdd0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdd3 (null) | fdd2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdd5 (null) | fdd4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdd7 (null) | fdd6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdd9 (null) | fdd8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fddb (null) | fdda (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fddd (null) | fddc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fddf (null) | fdde (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fde1 (null) | fde0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fde3 (null) | fde2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fde5 (null) | fde4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fde7 (null) | fde6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fde9 (null) | fde8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdeb (null) | fdea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fded (null) | fdec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdef (null) | fdee (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdf1 ARABIC LIGATURE QALA USED AS KORANIC ST | fdf0 ARABIC LIGATURE SALLA USED AS KORANIC S */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdf3 ARABIC LIGATURE AKBAR ISOLATED FORM | fdf2 ARABIC LIGATURE ALLAH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdf5 ARABIC LIGATURE SALAM ISOLATED FORM | fdf4 ARABIC LIGATURE MOHAMMAD ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdf7 ARABIC LIGATURE ALAYHE ISOLATED FORM | fdf6 ARABIC LIGATURE RASOUL ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdf9 ARABIC LIGATURE SALLA ISOLATED FORM | fdf8 ARABIC LIGATURE WASALLAM ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdfb ARABIC LIGATURE JALLAJALALOUHOU | fdfa ARABIC LIGATURE SALLALLAHOU ALAYHE WASA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fdfd ARABIC LIGATURE BISMILLAH AR-RAHMAN AR- | fdfc RIAL SIGN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdff (null) | fdfe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe01 VARIATION SELECTOR-2 | fe00 VARIATION SELECTOR-1 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe03 VARIATION SELECTOR-4 | fe02 VARIATION SELECTOR-3 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe05 VARIATION SELECTOR-6 | fe04 VARIATION SELECTOR-5 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe07 VARIATION SELECTOR-8 | fe06 VARIATION SELECTOR-7 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe09 VARIATION SELECTOR-10 | fe08 VARIATION SELECTOR-9 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe0b VARIATION SELECTOR-12 | fe0a VARIATION SELECTOR-11 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe0d VARIATION SELECTOR-14 | fe0c VARIATION SELECTOR-13 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe0f VARIATION SELECTOR-16 | fe0e VARIATION SELECTOR-15 */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe11 PRESENTATION FORM FOR VERTICAL IDEOGRAP | fe10 PRESENTATION FORM FOR VERTICAL COMMA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe13 PRESENTATION FORM FOR VERTICAL COLON | fe12 PRESENTATION FORM FOR VERTICAL IDEOGRAP */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe15 PRESENTATION FORM FOR VERTICAL EXCLAMAT | fe14 PRESENTATION FORM FOR VERTICAL SEMICOLO */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe17 PRESENTATION FORM FOR VERTICAL LEFT WHI | fe16 PRESENTATION FORM FOR VERTICAL QUESTION */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe19 PRESENTATION FORM FOR VERTICAL HORIZONT | fe18 PRESENTATION FORM FOR VERTICAL RIGHT WH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe1b (null) | fe1a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe1d (null) | fe1c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe1f (null) | fe1e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe21 COMBINING LIGATURE RIGHT HALF | fe20 COMBINING LIGATURE LEFT HALF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe23 COMBINING DOUBLE TILDE RIGHT HALF | fe22 COMBINING DOUBLE TILDE LEFT HALF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe25 COMBINING MACRON RIGHT HALF | fe24 COMBINING MACRON LEFT HALF */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* fe27 (null) | fe26 COMBINING CONJOINING MACRON */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe29 (null) | fe28 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe2b (null) | fe2a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe2d (null) | fe2c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe2f (null) | fe2e (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe31 PRESENTATION FORM FOR VERTICAL EM DASH | fe30 PRESENTATION FORM FOR VERTICAL TWO DOT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe33 PRESENTATION FORM FOR VERTICAL LOW LINE | fe32 PRESENTATION FORM FOR VERTICAL EN DASH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe35 PRESENTATION FORM FOR VERTICAL LEFT PAR | fe34 PRESENTATION FORM FOR VERTICAL WAVY LOW */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe37 PRESENTATION FORM FOR VERTICAL LEFT CUR | fe36 PRESENTATION FORM FOR VERTICAL RIGHT PA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe39 PRESENTATION FORM FOR VERTICAL LEFT TOR | fe38 PRESENTATION FORM FOR VERTICAL RIGHT CU */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe3b PRESENTATION FORM FOR VERTICAL LEFT BLA | fe3a PRESENTATION FORM FOR VERTICAL RIGHT TO */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe3d PRESENTATION FORM FOR VERTICAL LEFT DOU | fe3c PRESENTATION FORM FOR VERTICAL RIGHT BL */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe3f PRESENTATION FORM FOR VERTICAL LEFT ANG | fe3e PRESENTATION FORM FOR VERTICAL RIGHT DO */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe41 PRESENTATION FORM FOR VERTICAL LEFT COR | fe40 PRESENTATION FORM FOR VERTICAL RIGHT AN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe43 PRESENTATION FORM FOR VERTICAL LEFT WHI | fe42 PRESENTATION FORM FOR VERTICAL RIGHT CO */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe45 SESAME DOT | fe44 PRESENTATION FORM FOR VERTICAL RIGHT WH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe47 PRESENTATION FORM FOR VERTICAL LEFT SQU | fe46 WHITE SESAME DOT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe49 DASHED OVERLINE | fe48 PRESENTATION FORM FOR VERTICAL RIGHT SQ */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe4b WAVY OVERLINE | fe4a CENTRELINE OVERLINE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe4d DASHED LOW LINE | fe4c DOUBLE WAVY OVERLINE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe4f WAVY LOW LINE | fe4e CENTRELINE LOW LINE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe51 SMALL IDEOGRAPHIC COMMA | fe50 SMALL COMMA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* fe53 (null) | fe52 SMALL FULL STOP */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe55 SMALL COLON | fe54 SMALL SEMICOLON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe57 SMALL EXCLAMATION MARK | fe56 SMALL QUESTION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe59 SMALL LEFT PARENTHESIS | fe58 SMALL EM DASH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe5b SMALL LEFT CURLY BRACKET | fe5a SMALL RIGHT PARENTHESIS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe5d SMALL LEFT TORTOISE SHELL BRACKET | fe5c SMALL RIGHT CURLY BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe5f SMALL NUMBER SIGN | fe5e SMALL RIGHT TORTOISE SHELL BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe61 SMALL ASTERISK | fe60 SMALL AMPERSAND */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* fe63 SMALL HYPHEN-MINUS | fe62 SMALL PLUS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe65 SMALL GREATER-THAN SIGN | fe64 SMALL LESS-THAN SIGN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* fe67 (null) | fe66 SMALL EQUALS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* fe69 SMALL DOLLAR SIGN | fe68 SMALL REVERSE SOLIDUS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe6b SMALL COMMERCIAL AT | fe6a SMALL PERCENT SIGN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe6d (null) | fe6c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe6f (null) | fe6e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe71 ARABIC TATWEEL WITH FATHATAN ABOVE | fe70 ARABIC FATHATAN ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe73 ARABIC TAIL FRAGMENT | fe72 ARABIC DAMMATAN ISOLATED FORM */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* fe75 (null) | fe74 ARABIC KASRATAN ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe77 ARABIC FATHA MEDIAL FORM | fe76 ARABIC FATHA ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe79 ARABIC DAMMA MEDIAL FORM | fe78 ARABIC DAMMA ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe7b ARABIC KASRA MEDIAL FORM | fe7a ARABIC KASRA ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe7d ARABIC SHADDA MEDIAL FORM | fe7c ARABIC SHADDA ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe7f ARABIC SUKUN MEDIAL FORM | fe7e ARABIC SUKUN ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe81 ARABIC LETTER ALEF WITH MADDA ABOVE ISO | fe80 ARABIC LETTER HAMZA ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe83 ARABIC LETTER ALEF WITH HAMZA ABOVE ISO | fe82 ARABIC LETTER ALEF WITH MADDA ABOVE FIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe85 ARABIC LETTER WAW WITH HAMZA ABOVE ISOL | fe84 ARABIC LETTER ALEF WITH HAMZA ABOVE FIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe87 ARABIC LETTER ALEF WITH HAMZA BELOW ISO | fe86 ARABIC LETTER WAW WITH HAMZA ABOVE FINA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe89 ARABIC LETTER YEH WITH HAMZA ABOVE ISOL | fe88 ARABIC LETTER ALEF WITH HAMZA BELOW FIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe8b ARABIC LETTER YEH WITH HAMZA ABOVE INIT | fe8a ARABIC LETTER YEH WITH HAMZA ABOVE FINA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe8d ARABIC LETTER ALEF ISOLATED FORM | fe8c ARABIC LETTER YEH WITH HAMZA ABOVE MEDI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe8f ARABIC LETTER BEH ISOLATED FORM | fe8e ARABIC LETTER ALEF FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe91 ARABIC LETTER BEH INITIAL FORM | fe90 ARABIC LETTER BEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe93 ARABIC LETTER TEH MARBUTA ISOLATED FORM | fe92 ARABIC LETTER BEH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe95 ARABIC LETTER TEH ISOLATED FORM | fe94 ARABIC LETTER TEH MARBUTA FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe97 ARABIC LETTER TEH INITIAL FORM | fe96 ARABIC LETTER TEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe99 ARABIC LETTER THEH ISOLATED FORM | fe98 ARABIC LETTER TEH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe9b ARABIC LETTER THEH INITIAL FORM | fe9a ARABIC LETTER THEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe9d ARABIC LETTER JEEM ISOLATED FORM | fe9c ARABIC LETTER THEH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe9f ARABIC LETTER JEEM INITIAL FORM | fe9e ARABIC LETTER JEEM FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fea1 ARABIC LETTER HAH ISOLATED FORM | fea0 ARABIC LETTER JEEM MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fea3 ARABIC LETTER HAH INITIAL FORM | fea2 ARABIC LETTER HAH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fea5 ARABIC LETTER KHAH ISOLATED FORM | fea4 ARABIC LETTER HAH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fea7 ARABIC LETTER KHAH INITIAL FORM | fea6 ARABIC LETTER KHAH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fea9 ARABIC LETTER DAL ISOLATED FORM | fea8 ARABIC LETTER KHAH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feab ARABIC LETTER THAL ISOLATED FORM | feaa ARABIC LETTER DAL FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fead ARABIC LETTER REH ISOLATED FORM | feac ARABIC LETTER THAL FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feaf ARABIC LETTER ZAIN ISOLATED FORM | feae ARABIC LETTER REH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feb1 ARABIC LETTER SEEN ISOLATED FORM | feb0 ARABIC LETTER ZAIN FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feb3 ARABIC LETTER SEEN INITIAL FORM | feb2 ARABIC LETTER SEEN FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feb5 ARABIC LETTER SHEEN ISOLATED FORM | feb4 ARABIC LETTER SEEN MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feb7 ARABIC LETTER SHEEN INITIAL FORM | feb6 ARABIC LETTER SHEEN FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feb9 ARABIC LETTER SAD ISOLATED FORM | feb8 ARABIC LETTER SHEEN MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* febb ARABIC LETTER SAD INITIAL FORM | feba ARABIC LETTER SAD FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* febd ARABIC LETTER DAD ISOLATED FORM | febc ARABIC LETTER SAD MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* febf ARABIC LETTER DAD INITIAL FORM | febe ARABIC LETTER DAD FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fec1 ARABIC LETTER TAH ISOLATED FORM | fec0 ARABIC LETTER DAD MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fec3 ARABIC LETTER TAH INITIAL FORM | fec2 ARABIC LETTER TAH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fec5 ARABIC LETTER ZAH ISOLATED FORM | fec4 ARABIC LETTER TAH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fec7 ARABIC LETTER ZAH INITIAL FORM | fec6 ARABIC LETTER ZAH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fec9 ARABIC LETTER AIN ISOLATED FORM | fec8 ARABIC LETTER ZAH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fecb ARABIC LETTER AIN INITIAL FORM | feca ARABIC LETTER AIN FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fecd ARABIC LETTER GHAIN ISOLATED FORM | fecc ARABIC LETTER AIN MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fecf ARABIC LETTER GHAIN INITIAL FORM | fece ARABIC LETTER GHAIN FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fed1 ARABIC LETTER FEH ISOLATED FORM | fed0 ARABIC LETTER GHAIN MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fed3 ARABIC LETTER FEH INITIAL FORM | fed2 ARABIC LETTER FEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fed5 ARABIC LETTER QAF ISOLATED FORM | fed4 ARABIC LETTER FEH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fed7 ARABIC LETTER QAF INITIAL FORM | fed6 ARABIC LETTER QAF FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fed9 ARABIC LETTER KAF ISOLATED FORM | fed8 ARABIC LETTER QAF MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fedb ARABIC LETTER KAF INITIAL FORM | feda ARABIC LETTER KAF FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fedd ARABIC LETTER LAM ISOLATED FORM | fedc ARABIC LETTER KAF MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fedf ARABIC LETTER LAM INITIAL FORM | fede ARABIC LETTER LAM FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fee1 ARABIC LETTER MEEM ISOLATED FORM | fee0 ARABIC LETTER LAM MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fee3 ARABIC LETTER MEEM INITIAL FORM | fee2 ARABIC LETTER MEEM FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fee5 ARABIC LETTER NOON ISOLATED FORM | fee4 ARABIC LETTER MEEM MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fee7 ARABIC LETTER NOON INITIAL FORM | fee6 ARABIC LETTER NOON FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fee9 ARABIC LETTER HEH ISOLATED FORM | fee8 ARABIC LETTER NOON MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feeb ARABIC LETTER HEH INITIAL FORM | feea ARABIC LETTER HEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feed ARABIC LETTER WAW ISOLATED FORM | feec ARABIC LETTER HEH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feef ARABIC LETTER ALEF MAKSURA ISOLATED FOR | feee ARABIC LETTER WAW FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fef1 ARABIC LETTER YEH ISOLATED FORM | fef0 ARABIC LETTER ALEF MAKSURA FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fef3 ARABIC LETTER YEH INITIAL FORM | fef2 ARABIC LETTER YEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fef5 ARABIC LIGATURE LAM WITH ALEF WITH MADD | fef4 ARABIC LETTER YEH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fef7 ARABIC LIGATURE LAM WITH ALEF WITH HAMZ | fef6 ARABIC LIGATURE LAM WITH ALEF WITH MADD */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fef9 ARABIC LIGATURE LAM WITH ALEF WITH HAMZ | fef8 ARABIC LIGATURE LAM WITH ALEF WITH HAMZ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fefb ARABIC LIGATURE LAM WITH ALEF ISOLATED | fefa ARABIC LIGATURE LAM WITH ALEF WITH HAMZ */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* fefd (null) | fefc ARABIC LIGATURE LAM WITH ALEF FINAL FOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* feff ZERO WIDTH NO-BREAK SPACE | fefe (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_UNDEF, /* ff01 FULLWIDTH EXCLAMATION MARK | ff00 (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff03 FULLWIDTH NUMBER SIGN | ff02 FULLWIDTH QUOTATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* ff05 FULLWIDTH PERCENT SIGN | ff04 FULLWIDTH DOLLAR SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff07 FULLWIDTH APOSTROPHE | ff06 FULLWIDTH AMPERSAND */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff09 FULLWIDTH RIGHT PARENTHESIS | ff08 FULLWIDTH LEFT PARENTHESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* ff0b FULLWIDTH PLUS SIGN | ff0a FULLWIDTH ASTERISK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff0d FULLWIDTH HYPHEN-MINUS | ff0c FULLWIDTH COMMA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff0f FULLWIDTH SOLIDUS | ff0e FULLWIDTH FULL STOP */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* ff11 FULLWIDTH DIGIT ONE | ff10 FULLWIDTH DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* ff13 FULLWIDTH DIGIT THREE | ff12 FULLWIDTH DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* ff15 FULLWIDTH DIGIT FIVE | ff14 FULLWIDTH DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* ff17 FULLWIDTH DIGIT SEVEN | ff16 FULLWIDTH DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* ff19 FULLWIDTH DIGIT NINE | ff18 FULLWIDTH DIGIT EIGHT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff1b FULLWIDTH SEMICOLON | ff1a FULLWIDTH COLON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ff1d FULLWIDTH EQUALS SIGN | ff1c FULLWIDTH LESS-THAN SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* ff1f FULLWIDTH QUESTION MARK | ff1e FULLWIDTH GREATER-THAN SIGN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_PUNCT, /* ff21 FULLWIDTH LATIN CAPITAL LETTER A | ff20 FULLWIDTH COMMERCIAL AT */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff23 FULLWIDTH LATIN CAPITAL LETTER C | ff22 FULLWIDTH LATIN CAPITAL LETTER B */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff25 FULLWIDTH LATIN CAPITAL LETTER E | ff24 FULLWIDTH LATIN CAPITAL LETTER D */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff27 FULLWIDTH LATIN CAPITAL LETTER G | ff26 FULLWIDTH LATIN CAPITAL LETTER F */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff29 FULLWIDTH LATIN CAPITAL LETTER I | ff28 FULLWIDTH LATIN CAPITAL LETTER H */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff2b FULLWIDTH LATIN CAPITAL LETTER K | ff2a FULLWIDTH LATIN CAPITAL LETTER J */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff2d FULLWIDTH LATIN CAPITAL LETTER M | ff2c FULLWIDTH LATIN CAPITAL LETTER L */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff2f FULLWIDTH LATIN CAPITAL LETTER O | ff2e FULLWIDTH LATIN CAPITAL LETTER N */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff31 FULLWIDTH LATIN CAPITAL LETTER Q | ff30 FULLWIDTH LATIN CAPITAL LETTER P */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff33 FULLWIDTH LATIN CAPITAL LETTER S | ff32 FULLWIDTH LATIN CAPITAL LETTER R */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff35 FULLWIDTH LATIN CAPITAL LETTER U | ff34 FULLWIDTH LATIN CAPITAL LETTER T */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff37 FULLWIDTH LATIN CAPITAL LETTER W | ff36 FULLWIDTH LATIN CAPITAL LETTER V */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff39 FULLWIDTH LATIN CAPITAL LETTER Y | ff38 FULLWIDTH LATIN CAPITAL LETTER X */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_UPPER, /* ff3b FULLWIDTH LEFT SQUARE BRACKET | ff3a FULLWIDTH LATIN CAPITAL LETTER Z */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff3d FULLWIDTH RIGHT SQUARE BRACKET | ff3c FULLWIDTH REVERSE SOLIDUS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* ff3f FULLWIDTH LOW LINE | ff3e FULLWIDTH CIRCUMFLEX ACCENT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_OTHER, /* ff41 FULLWIDTH LATIN SMALL LETTER A | ff40 FULLWIDTH GRAVE ACCENT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff43 FULLWIDTH LATIN SMALL LETTER C | ff42 FULLWIDTH LATIN SMALL LETTER B */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff45 FULLWIDTH LATIN SMALL LETTER E | ff44 FULLWIDTH LATIN SMALL LETTER D */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff47 FULLWIDTH LATIN SMALL LETTER G | ff46 FULLWIDTH LATIN SMALL LETTER F */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff49 FULLWIDTH LATIN SMALL LETTER I | ff48 FULLWIDTH LATIN SMALL LETTER H */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff4b FULLWIDTH LATIN SMALL LETTER K | ff4a FULLWIDTH LATIN SMALL LETTER J */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff4d FULLWIDTH LATIN SMALL LETTER M | ff4c FULLWIDTH LATIN SMALL LETTER L */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff4f FULLWIDTH LATIN SMALL LETTER O | ff4e FULLWIDTH LATIN SMALL LETTER N */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff51 FULLWIDTH LATIN SMALL LETTER Q | ff50 FULLWIDTH LATIN SMALL LETTER P */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff53 FULLWIDTH LATIN SMALL LETTER S | ff52 FULLWIDTH LATIN SMALL LETTER R */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff55 FULLWIDTH LATIN SMALL LETTER U | ff54 FULLWIDTH LATIN SMALL LETTER T */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff57 FULLWIDTH LATIN SMALL LETTER W | ff56 FULLWIDTH LATIN SMALL LETTER V */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff59 FULLWIDTH LATIN SMALL LETTER Y | ff58 FULLWIDTH LATIN SMALL LETTER X */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_LOWER, /* ff5b FULLWIDTH LEFT CURLY BRACKET | ff5a FULLWIDTH LATIN SMALL LETTER Z */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* ff5d FULLWIDTH RIGHT CURLY BRACKET | ff5c FULLWIDTH VERTICAL LINE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* ff5f FULLWIDTH LEFT WHITE PARENTHESIS | ff5e FULLWIDTH TILDE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff61 HALFWIDTH IDEOGRAPHIC FULL STOP | ff60 FULLWIDTH RIGHT WHITE PARENTHESIS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff63 HALFWIDTH RIGHT CORNER BRACKET | ff62 HALFWIDTH LEFT CORNER BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff65 HALFWIDTH KATAKANA MIDDLE DOT | ff64 HALFWIDTH IDEOGRAPHIC COMMA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff67 HALFWIDTH KATAKANA LETTER SMALL A | ff66 HALFWIDTH KATAKANA LETTER WO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff69 HALFWIDTH KATAKANA LETTER SMALL U | ff68 HALFWIDTH KATAKANA LETTER SMALL I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff6b HALFWIDTH KATAKANA LETTER SMALL O | ff6a HALFWIDTH KATAKANA LETTER SMALL E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff6d HALFWIDTH KATAKANA LETTER SMALL YU | ff6c HALFWIDTH KATAKANA LETTER SMALL YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff6f HALFWIDTH KATAKANA LETTER SMALL TU | ff6e HALFWIDTH KATAKANA LETTER SMALL YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* ff71 HALFWIDTH KATAKANA LETTER A | ff70 HALFWIDTH KATAKANA-HIRAGANA PROLONGED S */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff73 HALFWIDTH KATAKANA LETTER U | ff72 HALFWIDTH KATAKANA LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff75 HALFWIDTH KATAKANA LETTER O | ff74 HALFWIDTH KATAKANA LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff77 HALFWIDTH KATAKANA LETTER KI | ff76 HALFWIDTH KATAKANA LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff79 HALFWIDTH KATAKANA LETTER KE | ff78 HALFWIDTH KATAKANA LETTER KU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff7b HALFWIDTH KATAKANA LETTER SA | ff7a HALFWIDTH KATAKANA LETTER KO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff7d HALFWIDTH KATAKANA LETTER SU | ff7c HALFWIDTH KATAKANA LETTER SI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff7f HALFWIDTH KATAKANA LETTER SO | ff7e HALFWIDTH KATAKANA LETTER SE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff81 HALFWIDTH KATAKANA LETTER TI | ff80 HALFWIDTH KATAKANA LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff83 HALFWIDTH KATAKANA LETTER TE | ff82 HALFWIDTH KATAKANA LETTER TU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff85 HALFWIDTH KATAKANA LETTER NA | ff84 HALFWIDTH KATAKANA LETTER TO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff87 HALFWIDTH KATAKANA LETTER NU | ff86 HALFWIDTH KATAKANA LETTER NI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff89 HALFWIDTH KATAKANA LETTER NO | ff88 HALFWIDTH KATAKANA LETTER NE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff8b HALFWIDTH KATAKANA LETTER HI | ff8a HALFWIDTH KATAKANA LETTER HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff8d HALFWIDTH KATAKANA LETTER HE | ff8c HALFWIDTH KATAKANA LETTER HU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff8f HALFWIDTH KATAKANA LETTER MA | ff8e HALFWIDTH KATAKANA LETTER HO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff91 HALFWIDTH KATAKANA LETTER MU | ff90 HALFWIDTH KATAKANA LETTER MI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff93 HALFWIDTH KATAKANA LETTER MO | ff92 HALFWIDTH KATAKANA LETTER ME */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff95 HALFWIDTH KATAKANA LETTER YU | ff94 HALFWIDTH KATAKANA LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff97 HALFWIDTH KATAKANA LETTER RA | ff96 HALFWIDTH KATAKANA LETTER YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff99 HALFWIDTH KATAKANA LETTER RU | ff98 HALFWIDTH KATAKANA LETTER RI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff9b HALFWIDTH KATAKANA LETTER RO | ff9a HALFWIDTH KATAKANA LETTER RE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff9d HALFWIDTH KATAKANA LETTER N | ff9c HALFWIDTH KATAKANA LETTER WA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ff9f HALFWIDTH KATAKANA SEMI-VOICED SOUND MA | ff9e HALFWIDTH KATAKANA VOICED SOUND MARK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffa1 HALFWIDTH HANGUL LETTER KIYEOK | ffa0 HALFWIDTH HANGUL FILLER */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffa3 HALFWIDTH HANGUL LETTER KIYEOK-SIOS | ffa2 HALFWIDTH HANGUL LETTER SSANGKIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffa5 HALFWIDTH HANGUL LETTER NIEUN-CIEUC | ffa4 HALFWIDTH HANGUL LETTER NIEUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffa7 HALFWIDTH HANGUL LETTER TIKEUT | ffa6 HALFWIDTH HANGUL LETTER NIEUN-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffa9 HALFWIDTH HANGUL LETTER RIEUL | ffa8 HALFWIDTH HANGUL LETTER SSANGTIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffab HALFWIDTH HANGUL LETTER RIEUL-MIEUM | ffaa HALFWIDTH HANGUL LETTER RIEUL-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffad HALFWIDTH HANGUL LETTER RIEUL-SIOS | ffac HALFWIDTH HANGUL LETTER RIEUL-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffaf HALFWIDTH HANGUL LETTER RIEUL-PHIEUPH | ffae HALFWIDTH HANGUL LETTER RIEUL-THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffb1 HALFWIDTH HANGUL LETTER MIEUM | ffb0 HALFWIDTH HANGUL LETTER RIEUL-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffb3 HALFWIDTH HANGUL LETTER SSANGPIEUP | ffb2 HALFWIDTH HANGUL LETTER PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffb5 HALFWIDTH HANGUL LETTER SIOS | ffb4 HALFWIDTH HANGUL LETTER PIEUP-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffb7 HALFWIDTH HANGUL LETTER IEUNG | ffb6 HALFWIDTH HANGUL LETTER SSANGSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffb9 HALFWIDTH HANGUL LETTER SSANGCIEUC | ffb8 HALFWIDTH HANGUL LETTER CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffbb HALFWIDTH HANGUL LETTER KHIEUKH | ffba HALFWIDTH HANGUL LETTER CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffbd HALFWIDTH HANGUL LETTER PHIEUPH | ffbc HALFWIDTH HANGUL LETTER THIEUTH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* ffbf (null) | ffbe HALFWIDTH HANGUL LETTER HIEUH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ffc1 (null) | ffc0 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffc3 HALFWIDTH HANGUL LETTER AE | ffc2 HALFWIDTH HANGUL LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffc5 HALFWIDTH HANGUL LETTER YAE | ffc4 HALFWIDTH HANGUL LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffc7 HALFWIDTH HANGUL LETTER E | ffc6 HALFWIDTH HANGUL LETTER EO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ffc9 (null) | ffc8 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffcb HALFWIDTH HANGUL LETTER YE | ffca HALFWIDTH HANGUL LETTER YEO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffcd HALFWIDTH HANGUL LETTER WA | ffcc HALFWIDTH HANGUL LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffcf HALFWIDTH HANGUL LETTER OE | ffce HALFWIDTH HANGUL LETTER WAE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ffd1 (null) | ffd0 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffd3 HALFWIDTH HANGUL LETTER U | ffd2 HALFWIDTH HANGUL LETTER YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffd5 HALFWIDTH HANGUL LETTER WE | ffd4 HALFWIDTH HANGUL LETTER WEO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffd7 HALFWIDTH HANGUL LETTER YU | ffd6 HALFWIDTH HANGUL LETTER WI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ffd9 (null) | ffd8 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffdb HALFWIDTH HANGUL LETTER YI | ffda HALFWIDTH HANGUL LETTER EU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* ffdd (null) | ffdc HALFWIDTH HANGUL LETTER I */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ffdf (null) | ffde (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ffe1 FULLWIDTH POUND SIGN | ffe0 FULLWIDTH CENT SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ffe3 FULLWIDTH MACRON | ffe2 FULLWIDTH NOT SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ffe5 FULLWIDTH YEN SIGN | ffe4 FULLWIDTH BROKEN BAR */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* ffe7 (null) | ffe6 FULLWIDTH WON SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ffe9 HALFWIDTH LEFTWARDS ARROW | ffe8 HALFWIDTH FORMS LIGHT VERTICAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ffeb HALFWIDTH RIGHTWARDS ARROW | ffea HALFWIDTH UPWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ffed HALFWIDTH BLACK SQUARE | ffec HALFWIDTH DOWNWARDS ARROW */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* ffef (null) | ffee HALFWIDTH WHITE CIRCLE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fff1 (null) | fff0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fff3 (null) | fff2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fff5 (null) | fff4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fff7 (null) | fff6 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* fff9 INTERLINEAR ANNOTATION ANCHOR | fff8 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fffb INTERLINEAR ANNOTATION TERMINATOR | fffa INTERLINEAR ANNOTATION SEPARATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fffd REPLACEMENT CHARACTER | fffc OBJECT REPLACEMENT CHARACTER */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF /* ffff (null) | fffe (null) */ }; int t3_get_chartype(wchar_t ch) { return (chartype[ch >> 1] >> ((ch & 1) << 2)) & 0x0F; } struct case_table { uint16_t lower; uint16_t title; uint16_t upper; uint16_t fold; }; static case_table case_pg_000[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0000 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0001 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0002 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0003 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0004 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0005 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0006 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0007 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0008 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0009 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 000a */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 000b */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 000c */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 000d */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 000e */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 000f */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0010 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0011 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0012 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0013 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0014 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0015 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0016 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0017 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0018 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0019 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 001a */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 001b */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 001c */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 001d */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 001e */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 001f */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0020 SPACE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0021 EXCLAMATION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0022 QUOTATION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0023 NUMBER SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0024 DOLLAR SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0025 PERCENT SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0026 AMPERSAND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0027 APOSTROPHE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0028 LEFT PARENTHESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0029 RIGHT PARENTHESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 002a ASTERISK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 002b PLUS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 002c COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 002d HYPHEN-MINUS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 002e FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 002f SOLIDUS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0030 DIGIT ZERO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0031 DIGIT ONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0032 DIGIT TWO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0033 DIGIT THREE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0034 DIGIT FOUR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0035 DIGIT FIVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0036 DIGIT SIX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0037 DIGIT SEVEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0038 DIGIT EIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0039 DIGIT NINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 003a COLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 003b SEMICOLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 003c LESS-THAN SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 003d EQUALS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 003e GREATER-THAN SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 003f QUESTION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0040 COMMERCIAL AT */ { 0x0001, 0x0000, 0x0000, 0x0001 }, /* 0041 LATIN CAPITAL LETTER A */ { 0x0003, 0x0000, 0x0000, 0x0003 }, /* 0042 LATIN CAPITAL LETTER B */ { 0x0005, 0x0000, 0x0000, 0x0005 }, /* 0043 LATIN CAPITAL LETTER C */ { 0x0007, 0x0000, 0x0000, 0x0007 }, /* 0044 LATIN CAPITAL LETTER D */ { 0x0009, 0x0000, 0x0000, 0x0009 }, /* 0045 LATIN CAPITAL LETTER E */ { 0x000b, 0x0000, 0x0000, 0x000b }, /* 0046 LATIN CAPITAL LETTER F */ { 0x000d, 0x0000, 0x0000, 0x000d }, /* 0047 LATIN CAPITAL LETTER G */ { 0x000f, 0x0000, 0x0000, 0x000f }, /* 0048 LATIN CAPITAL LETTER H */ { 0x0011, 0x0000, 0x0000, 0x0011 }, /* 0049 LATIN CAPITAL LETTER I */ { 0x0013, 0x0000, 0x0000, 0x0013 }, /* 004a LATIN CAPITAL LETTER J */ { 0x0015, 0x0000, 0x0000, 0x0015 }, /* 004b LATIN CAPITAL LETTER K */ { 0x0017, 0x0000, 0x0000, 0x0017 }, /* 004c LATIN CAPITAL LETTER L */ { 0x0019, 0x0000, 0x0000, 0x0019 }, /* 004d LATIN CAPITAL LETTER M */ { 0x001b, 0x0000, 0x0000, 0x001b }, /* 004e LATIN CAPITAL LETTER N */ { 0x001d, 0x0000, 0x0000, 0x001d }, /* 004f LATIN CAPITAL LETTER O */ { 0x001f, 0x0000, 0x0000, 0x001f }, /* 0050 LATIN CAPITAL LETTER P */ { 0x0021, 0x0000, 0x0000, 0x0021 }, /* 0051 LATIN CAPITAL LETTER Q */ { 0x0023, 0x0000, 0x0000, 0x0023 }, /* 0052 LATIN CAPITAL LETTER R */ { 0x0025, 0x0000, 0x0000, 0x0025 }, /* 0053 LATIN CAPITAL LETTER S */ { 0x0027, 0x0000, 0x0000, 0x0027 }, /* 0054 LATIN CAPITAL LETTER T */ { 0x0029, 0x0000, 0x0000, 0x0029 }, /* 0055 LATIN CAPITAL LETTER U */ { 0x002b, 0x0000, 0x0000, 0x002b }, /* 0056 LATIN CAPITAL LETTER V */ { 0x002d, 0x0000, 0x0000, 0x002d }, /* 0057 LATIN CAPITAL LETTER W */ { 0x002f, 0x0000, 0x0000, 0x002f }, /* 0058 LATIN CAPITAL LETTER X */ { 0x0031, 0x0000, 0x0000, 0x0031 }, /* 0059 LATIN CAPITAL LETTER Y */ { 0x0033, 0x0000, 0x0000, 0x0033 }, /* 005a LATIN CAPITAL LETTER Z */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 005b LEFT SQUARE BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 005c REVERSE SOLIDUS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 005d RIGHT SQUARE BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 005e CIRCUMFLEX ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 005f LOW LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0060 GRAVE ACCENT */ { 0x0000, 0x0035, 0x0035, 0x0000 }, /* 0061 LATIN SMALL LETTER A */ { 0x0000, 0x0037, 0x0037, 0x0000 }, /* 0062 LATIN SMALL LETTER B */ { 0x0000, 0x0039, 0x0039, 0x0000 }, /* 0063 LATIN SMALL LETTER C */ { 0x0000, 0x003b, 0x003b, 0x0000 }, /* 0064 LATIN SMALL LETTER D */ { 0x0000, 0x003d, 0x003d, 0x0000 }, /* 0065 LATIN SMALL LETTER E */ { 0x0000, 0x003f, 0x003f, 0x0000 }, /* 0066 LATIN SMALL LETTER F */ { 0x0000, 0x0041, 0x0041, 0x0000 }, /* 0067 LATIN SMALL LETTER G */ { 0x0000, 0x0043, 0x0043, 0x0000 }, /* 0068 LATIN SMALL LETTER H */ { 0x0000, 0x0045, 0x0045, 0x0000 }, /* 0069 LATIN SMALL LETTER I */ { 0x0000, 0x0047, 0x0047, 0x0000 }, /* 006a LATIN SMALL LETTER J */ { 0x0000, 0x0049, 0x0049, 0x0000 }, /* 006b LATIN SMALL LETTER K */ { 0x0000, 0x004b, 0x004b, 0x0000 }, /* 006c LATIN SMALL LETTER L */ { 0x0000, 0x004d, 0x004d, 0x0000 }, /* 006d LATIN SMALL LETTER M */ { 0x0000, 0x004f, 0x004f, 0x0000 }, /* 006e LATIN SMALL LETTER N */ { 0x0000, 0x0051, 0x0051, 0x0000 }, /* 006f LATIN SMALL LETTER O */ { 0x0000, 0x0053, 0x0053, 0x0000 }, /* 0070 LATIN SMALL LETTER P */ { 0x0000, 0x0055, 0x0055, 0x0000 }, /* 0071 LATIN SMALL LETTER Q */ { 0x0000, 0x0057, 0x0057, 0x0000 }, /* 0072 LATIN SMALL LETTER R */ { 0x0000, 0x0059, 0x0059, 0x0000 }, /* 0073 LATIN SMALL LETTER S */ { 0x0000, 0x005b, 0x005b, 0x0000 }, /* 0074 LATIN SMALL LETTER T */ { 0x0000, 0x005d, 0x005d, 0x0000 }, /* 0075 LATIN SMALL LETTER U */ { 0x0000, 0x005f, 0x005f, 0x0000 }, /* 0076 LATIN SMALL LETTER V */ { 0x0000, 0x0061, 0x0061, 0x0000 }, /* 0077 LATIN SMALL LETTER W */ { 0x0000, 0x0063, 0x0063, 0x0000 }, /* 0078 LATIN SMALL LETTER X */ { 0x0000, 0x0065, 0x0065, 0x0000 }, /* 0079 LATIN SMALL LETTER Y */ { 0x0000, 0x0067, 0x0067, 0x0000 }, /* 007a LATIN SMALL LETTER Z */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 007b LEFT CURLY BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 007c VERTICAL LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 007d RIGHT CURLY BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 007e TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 007f */ }; static case_table case_pg_001[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0080 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0081 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0082 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0083 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0084 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0085 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0086 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0087 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0088 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0089 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 008a */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 008b */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 008c */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 008d */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 008e */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 008f */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0090 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0091 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0092 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0093 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0094 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0095 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0096 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0097 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0098 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0099 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 009a */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 009b */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 009c */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 009d */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 009e */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 009f */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a0 NO-BREAK SPACE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a1 INVERTED EXCLAMATION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a2 CENT SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a3 POUND SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a4 CURRENCY SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a5 YEN SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a6 BROKEN BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a7 SECTION SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a8 DIAERESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a9 COPYRIGHT SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00aa FEMININE ORDINAL INDICATOR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00ab LEFT-POINTING DOUBLE ANGLE QUOTATION MA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00ac NOT SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00ad SOFT HYPHEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00ae REGISTERED SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00af MACRON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b0 DEGREE SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b1 PLUS-MINUS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b2 SUPERSCRIPT TWO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b3 SUPERSCRIPT THREE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b4 ACUTE ACCENT */ { 0x0000, 0x0069, 0x0069, 0x006b }, /* 00b5 MICRO SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b6 PILCROW SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b7 MIDDLE DOT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b8 CEDILLA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b9 SUPERSCRIPT ONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00ba MASCULINE ORDINAL INDICATOR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00bb RIGHT-POINTING DOUBLE ANGLE QUOTATION M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00bc VULGAR FRACTION ONE QUARTER */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00bd VULGAR FRACTION ONE HALF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00be VULGAR FRACTION THREE QUARTERS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00bf INVERTED QUESTION MARK */ { 0x006d, 0x0000, 0x0000, 0x006d }, /* 00c0 LATIN CAPITAL LETTER A WITH GRAVE */ { 0x006f, 0x0000, 0x0000, 0x006f }, /* 00c1 LATIN CAPITAL LETTER A WITH ACUTE */ { 0x0071, 0x0000, 0x0000, 0x0071 }, /* 00c2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ { 0x0073, 0x0000, 0x0000, 0x0073 }, /* 00c3 LATIN CAPITAL LETTER A WITH TILDE */ { 0x0075, 0x0000, 0x0000, 0x0075 }, /* 00c4 LATIN CAPITAL LETTER A WITH DIAERESIS */ { 0x0077, 0x0000, 0x0000, 0x0077 }, /* 00c5 LATIN CAPITAL LETTER A WITH RING ABOVE */ { 0x0079, 0x0000, 0x0000, 0x0079 }, /* 00c6 LATIN CAPITAL LETTER AE */ { 0x007b, 0x0000, 0x0000, 0x007b }, /* 00c7 LATIN CAPITAL LETTER C WITH CEDILLA */ { 0x007d, 0x0000, 0x0000, 0x007d }, /* 00c8 LATIN CAPITAL LETTER E WITH GRAVE */ { 0x007f, 0x0000, 0x0000, 0x007f }, /* 00c9 LATIN CAPITAL LETTER E WITH ACUTE */ { 0x0081, 0x0000, 0x0000, 0x0081 }, /* 00ca LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ { 0x0083, 0x0000, 0x0000, 0x0083 }, /* 00cb LATIN CAPITAL LETTER E WITH DIAERESIS */ { 0x0085, 0x0000, 0x0000, 0x0085 }, /* 00cc LATIN CAPITAL LETTER I WITH GRAVE */ { 0x0087, 0x0000, 0x0000, 0x0087 }, /* 00cd LATIN CAPITAL LETTER I WITH ACUTE */ { 0x0089, 0x0000, 0x0000, 0x0089 }, /* 00ce LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ { 0x008b, 0x0000, 0x0000, 0x008b }, /* 00cf LATIN CAPITAL LETTER I WITH DIAERESIS */ { 0x008d, 0x0000, 0x0000, 0x008d }, /* 00d0 LATIN CAPITAL LETTER ETH */ { 0x008f, 0x0000, 0x0000, 0x008f }, /* 00d1 LATIN CAPITAL LETTER N WITH TILDE */ { 0x0091, 0x0000, 0x0000, 0x0091 }, /* 00d2 LATIN CAPITAL LETTER O WITH GRAVE */ { 0x0093, 0x0000, 0x0000, 0x0093 }, /* 00d3 LATIN CAPITAL LETTER O WITH ACUTE */ { 0x0095, 0x0000, 0x0000, 0x0095 }, /* 00d4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ { 0x0097, 0x0000, 0x0000, 0x0097 }, /* 00d5 LATIN CAPITAL LETTER O WITH TILDE */ { 0x0099, 0x0000, 0x0000, 0x0099 }, /* 00d6 LATIN CAPITAL LETTER O WITH DIAERESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00d7 MULTIPLICATION SIGN */ { 0x009b, 0x0000, 0x0000, 0x009b }, /* 00d8 LATIN CAPITAL LETTER O WITH STROKE */ { 0x009d, 0x0000, 0x0000, 0x009d }, /* 00d9 LATIN CAPITAL LETTER U WITH GRAVE */ { 0x009f, 0x0000, 0x0000, 0x009f }, /* 00da LATIN CAPITAL LETTER U WITH ACUTE */ { 0x00a1, 0x0000, 0x0000, 0x00a1 }, /* 00db LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ { 0x00a3, 0x0000, 0x0000, 0x00a3 }, /* 00dc LATIN CAPITAL LETTER U WITH DIAERESIS */ { 0x00a5, 0x0000, 0x0000, 0x00a5 }, /* 00dd LATIN CAPITAL LETTER Y WITH ACUTE */ { 0x00a7, 0x0000, 0x0000, 0x00a7 }, /* 00de LATIN CAPITAL LETTER THORN */ { 0x0000, 0x00a9, 0x00ac, 0x00af }, /* 00df LATIN SMALL LETTER SHARP S */ { 0x0000, 0x00b2, 0x00b2, 0x0000 }, /* 00e0 LATIN SMALL LETTER A WITH GRAVE */ { 0x0000, 0x00b4, 0x00b4, 0x0000 }, /* 00e1 LATIN SMALL LETTER A WITH ACUTE */ { 0x0000, 0x00b6, 0x00b6, 0x0000 }, /* 00e2 LATIN SMALL LETTER A WITH CIRCUMFLEX */ { 0x0000, 0x00b8, 0x00b8, 0x0000 }, /* 00e3 LATIN SMALL LETTER A WITH TILDE */ { 0x0000, 0x00ba, 0x00ba, 0x0000 }, /* 00e4 LATIN SMALL LETTER A WITH DIAERESIS */ { 0x0000, 0x00bc, 0x00bc, 0x0000 }, /* 00e5 LATIN SMALL LETTER A WITH RING ABOVE */ { 0x0000, 0x00be, 0x00be, 0x0000 }, /* 00e6 LATIN SMALL LETTER AE */ { 0x0000, 0x00c0, 0x00c0, 0x0000 }, /* 00e7 LATIN SMALL LETTER C WITH CEDILLA */ { 0x0000, 0x00c2, 0x00c2, 0x0000 }, /* 00e8 LATIN SMALL LETTER E WITH GRAVE */ { 0x0000, 0x00c4, 0x00c4, 0x0000 }, /* 00e9 LATIN SMALL LETTER E WITH ACUTE */ { 0x0000, 0x00c6, 0x00c6, 0x0000 }, /* 00ea LATIN SMALL LETTER E WITH CIRCUMFLEX */ { 0x0000, 0x00c8, 0x00c8, 0x0000 }, /* 00eb LATIN SMALL LETTER E WITH DIAERESIS */ { 0x0000, 0x00ca, 0x00ca, 0x0000 }, /* 00ec LATIN SMALL LETTER I WITH GRAVE */ { 0x0000, 0x00cc, 0x00cc, 0x0000 }, /* 00ed LATIN SMALL LETTER I WITH ACUTE */ { 0x0000, 0x00ce, 0x00ce, 0x0000 }, /* 00ee LATIN SMALL LETTER I WITH CIRCUMFLEX */ { 0x0000, 0x00d0, 0x00d0, 0x0000 }, /* 00ef LATIN SMALL LETTER I WITH DIAERESIS */ { 0x0000, 0x00d2, 0x00d2, 0x0000 }, /* 00f0 LATIN SMALL LETTER ETH */ { 0x0000, 0x00d4, 0x00d4, 0x0000 }, /* 00f1 LATIN SMALL LETTER N WITH TILDE */ { 0x0000, 0x00d6, 0x00d6, 0x0000 }, /* 00f2 LATIN SMALL LETTER O WITH GRAVE */ { 0x0000, 0x00d8, 0x00d8, 0x0000 }, /* 00f3 LATIN SMALL LETTER O WITH ACUTE */ { 0x0000, 0x00da, 0x00da, 0x0000 }, /* 00f4 LATIN SMALL LETTER O WITH CIRCUMFLEX */ { 0x0000, 0x00dc, 0x00dc, 0x0000 }, /* 00f5 LATIN SMALL LETTER O WITH TILDE */ { 0x0000, 0x00de, 0x00de, 0x0000 }, /* 00f6 LATIN SMALL LETTER O WITH DIAERESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00f7 DIVISION SIGN */ { 0x0000, 0x00e0, 0x00e0, 0x0000 }, /* 00f8 LATIN SMALL LETTER O WITH STROKE */ { 0x0000, 0x00e2, 0x00e2, 0x0000 }, /* 00f9 LATIN SMALL LETTER U WITH GRAVE */ { 0x0000, 0x00e4, 0x00e4, 0x0000 }, /* 00fa LATIN SMALL LETTER U WITH ACUTE */ { 0x0000, 0x00e6, 0x00e6, 0x0000 }, /* 00fb LATIN SMALL LETTER U WITH CIRCUMFLEX */ { 0x0000, 0x00e8, 0x00e8, 0x0000 }, /* 00fc LATIN SMALL LETTER U WITH DIAERESIS */ { 0x0000, 0x00ea, 0x00ea, 0x0000 }, /* 00fd LATIN SMALL LETTER Y WITH ACUTE */ { 0x0000, 0x00ec, 0x00ec, 0x0000 }, /* 00fe LATIN SMALL LETTER THORN */ { 0x0000, 0x00ee, 0x00ee, 0x0000 } /* 00ff LATIN SMALL LETTER Y WITH DIAERESIS */ }; static case_table case_pg_002[128] = { { 0x00f0, 0x0000, 0x0000, 0x00f0 }, /* 0100 LATIN CAPITAL LETTER A WITH MACRON */ { 0x0000, 0x00f2, 0x00f2, 0x0000 }, /* 0101 LATIN SMALL LETTER A WITH MACRON */ { 0x00f4, 0x0000, 0x0000, 0x00f4 }, /* 0102 LATIN CAPITAL LETTER A WITH BREVE */ { 0x0000, 0x00f6, 0x00f6, 0x0000 }, /* 0103 LATIN SMALL LETTER A WITH BREVE */ { 0x00f8, 0x0000, 0x0000, 0x00f8 }, /* 0104 LATIN CAPITAL LETTER A WITH OGONEK */ { 0x0000, 0x00fa, 0x00fa, 0x0000 }, /* 0105 LATIN SMALL LETTER A WITH OGONEK */ { 0x00fc, 0x0000, 0x0000, 0x00fc }, /* 0106 LATIN CAPITAL LETTER C WITH ACUTE */ { 0x0000, 0x00fe, 0x00fe, 0x0000 }, /* 0107 LATIN SMALL LETTER C WITH ACUTE */ { 0x0100, 0x0000, 0x0000, 0x0100 }, /* 0108 LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ { 0x0000, 0x0102, 0x0102, 0x0000 }, /* 0109 LATIN SMALL LETTER C WITH CIRCUMFLEX */ { 0x0104, 0x0000, 0x0000, 0x0104 }, /* 010a LATIN CAPITAL LETTER C WITH DOT ABOVE */ { 0x0000, 0x0106, 0x0106, 0x0000 }, /* 010b LATIN SMALL LETTER C WITH DOT ABOVE */ { 0x0108, 0x0000, 0x0000, 0x0108 }, /* 010c LATIN CAPITAL LETTER C WITH CARON */ { 0x0000, 0x010a, 0x010a, 0x0000 }, /* 010d LATIN SMALL LETTER C WITH CARON */ { 0x010c, 0x0000, 0x0000, 0x010c }, /* 010e LATIN CAPITAL LETTER D WITH CARON */ { 0x0000, 0x010e, 0x010e, 0x0000 }, /* 010f LATIN SMALL LETTER D WITH CARON */ { 0x0110, 0x0000, 0x0000, 0x0110 }, /* 0110 LATIN CAPITAL LETTER D WITH STROKE */ { 0x0000, 0x0112, 0x0112, 0x0000 }, /* 0111 LATIN SMALL LETTER D WITH STROKE */ { 0x0114, 0x0000, 0x0000, 0x0114 }, /* 0112 LATIN CAPITAL LETTER E WITH MACRON */ { 0x0000, 0x0116, 0x0116, 0x0000 }, /* 0113 LATIN SMALL LETTER E WITH MACRON */ { 0x0118, 0x0000, 0x0000, 0x0118 }, /* 0114 LATIN CAPITAL LETTER E WITH BREVE */ { 0x0000, 0x011a, 0x011a, 0x0000 }, /* 0115 LATIN SMALL LETTER E WITH BREVE */ { 0x011c, 0x0000, 0x0000, 0x011c }, /* 0116 LATIN CAPITAL LETTER E WITH DOT ABOVE */ { 0x0000, 0x011e, 0x011e, 0x0000 }, /* 0117 LATIN SMALL LETTER E WITH DOT ABOVE */ { 0x0120, 0x0000, 0x0000, 0x0120 }, /* 0118 LATIN CAPITAL LETTER E WITH OGONEK */ { 0x0000, 0x0122, 0x0122, 0x0000 }, /* 0119 LATIN SMALL LETTER E WITH OGONEK */ { 0x0124, 0x0000, 0x0000, 0x0124 }, /* 011a LATIN CAPITAL LETTER E WITH CARON */ { 0x0000, 0x0126, 0x0126, 0x0000 }, /* 011b LATIN SMALL LETTER E WITH CARON */ { 0x0128, 0x0000, 0x0000, 0x0128 }, /* 011c LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ { 0x0000, 0x012a, 0x012a, 0x0000 }, /* 011d LATIN SMALL LETTER G WITH CIRCUMFLEX */ { 0x012c, 0x0000, 0x0000, 0x012c }, /* 011e LATIN CAPITAL LETTER G WITH BREVE */ { 0x0000, 0x012e, 0x012e, 0x0000 }, /* 011f LATIN SMALL LETTER G WITH BREVE */ { 0x0130, 0x0000, 0x0000, 0x0130 }, /* 0120 LATIN CAPITAL LETTER G WITH DOT ABOVE */ { 0x0000, 0x0132, 0x0132, 0x0000 }, /* 0121 LATIN SMALL LETTER G WITH DOT ABOVE */ { 0x0134, 0x0000, 0x0000, 0x0134 }, /* 0122 LATIN CAPITAL LETTER G WITH CEDILLA */ { 0x0000, 0x0136, 0x0136, 0x0000 }, /* 0123 LATIN SMALL LETTER G WITH CEDILLA */ { 0x0138, 0x0000, 0x0000, 0x0138 }, /* 0124 LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ { 0x0000, 0x013a, 0x013a, 0x0000 }, /* 0125 LATIN SMALL LETTER H WITH CIRCUMFLEX */ { 0x013c, 0x0000, 0x0000, 0x013c }, /* 0126 LATIN CAPITAL LETTER H WITH STROKE */ { 0x0000, 0x013e, 0x013e, 0x0000 }, /* 0127 LATIN SMALL LETTER H WITH STROKE */ { 0x0140, 0x0000, 0x0000, 0x0140 }, /* 0128 LATIN CAPITAL LETTER I WITH TILDE */ { 0x0000, 0x0142, 0x0142, 0x0000 }, /* 0129 LATIN SMALL LETTER I WITH TILDE */ { 0x0144, 0x0000, 0x0000, 0x0144 }, /* 012a LATIN CAPITAL LETTER I WITH MACRON */ { 0x0000, 0x0146, 0x0146, 0x0000 }, /* 012b LATIN SMALL LETTER I WITH MACRON */ { 0x0148, 0x0000, 0x0000, 0x0148 }, /* 012c LATIN CAPITAL LETTER I WITH BREVE */ { 0x0000, 0x014a, 0x014a, 0x0000 }, /* 012d LATIN SMALL LETTER I WITH BREVE */ { 0x014c, 0x0000, 0x0000, 0x014c }, /* 012e LATIN CAPITAL LETTER I WITH OGONEK */ { 0x0000, 0x014e, 0x014e, 0x0000 }, /* 012f LATIN SMALL LETTER I WITH OGONEK */ { 0x0150, 0x0000, 0x0000, 0x0150 }, /* 0130 LATIN CAPITAL LETTER I WITH DOT ABOVE */ { 0x0000, 0x0153, 0x0153, 0x0000 }, /* 0131 LATIN SMALL LETTER DOTLESS I */ { 0x0155, 0x0000, 0x0000, 0x0155 }, /* 0132 LATIN CAPITAL LIGATURE IJ */ { 0x0000, 0x0157, 0x0157, 0x0000 }, /* 0133 LATIN SMALL LIGATURE IJ */ { 0x0159, 0x0000, 0x0000, 0x0159 }, /* 0134 LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ { 0x0000, 0x015b, 0x015b, 0x0000 }, /* 0135 LATIN SMALL LETTER J WITH CIRCUMFLEX */ { 0x015d, 0x0000, 0x0000, 0x015d }, /* 0136 LATIN CAPITAL LETTER K WITH CEDILLA */ { 0x0000, 0x015f, 0x015f, 0x0000 }, /* 0137 LATIN SMALL LETTER K WITH CEDILLA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0138 LATIN SMALL LETTER KRA */ { 0x0161, 0x0000, 0x0000, 0x0161 }, /* 0139 LATIN CAPITAL LETTER L WITH ACUTE */ { 0x0000, 0x0163, 0x0163, 0x0000 }, /* 013a LATIN SMALL LETTER L WITH ACUTE */ { 0x0165, 0x0000, 0x0000, 0x0165 }, /* 013b LATIN CAPITAL LETTER L WITH CEDILLA */ { 0x0000, 0x0167, 0x0167, 0x0000 }, /* 013c LATIN SMALL LETTER L WITH CEDILLA */ { 0x0169, 0x0000, 0x0000, 0x0169 }, /* 013d LATIN CAPITAL LETTER L WITH CARON */ { 0x0000, 0x016b, 0x016b, 0x0000 }, /* 013e LATIN SMALL LETTER L WITH CARON */ { 0x016d, 0x0000, 0x0000, 0x016d }, /* 013f LATIN CAPITAL LETTER L WITH MIDDLE DOT */ { 0x0000, 0x016f, 0x016f, 0x0000 }, /* 0140 LATIN SMALL LETTER L WITH MIDDLE DOT */ { 0x0171, 0x0000, 0x0000, 0x0171 }, /* 0141 LATIN CAPITAL LETTER L WITH STROKE */ { 0x0000, 0x0173, 0x0173, 0x0000 }, /* 0142 LATIN SMALL LETTER L WITH STROKE */ { 0x0175, 0x0000, 0x0000, 0x0175 }, /* 0143 LATIN CAPITAL LETTER N WITH ACUTE */ { 0x0000, 0x0177, 0x0177, 0x0000 }, /* 0144 LATIN SMALL LETTER N WITH ACUTE */ { 0x0179, 0x0000, 0x0000, 0x0179 }, /* 0145 LATIN CAPITAL LETTER N WITH CEDILLA */ { 0x0000, 0x017b, 0x017b, 0x0000 }, /* 0146 LATIN SMALL LETTER N WITH CEDILLA */ { 0x017d, 0x0000, 0x0000, 0x017d }, /* 0147 LATIN CAPITAL LETTER N WITH CARON */ { 0x0000, 0x017f, 0x017f, 0x0000 }, /* 0148 LATIN SMALL LETTER N WITH CARON */ { 0x0000, 0x0181, 0x0181, 0x0184 }, /* 0149 LATIN SMALL LETTER N PRECEDED BY APOSTR */ { 0x0187, 0x0000, 0x0000, 0x0187 }, /* 014a LATIN CAPITAL LETTER ENG */ { 0x0000, 0x0189, 0x0189, 0x0000 }, /* 014b LATIN SMALL LETTER ENG */ { 0x018b, 0x0000, 0x0000, 0x018b }, /* 014c LATIN CAPITAL LETTER O WITH MACRON */ { 0x0000, 0x018d, 0x018d, 0x0000 }, /* 014d LATIN SMALL LETTER O WITH MACRON */ { 0x018f, 0x0000, 0x0000, 0x018f }, /* 014e LATIN CAPITAL LETTER O WITH BREVE */ { 0x0000, 0x0191, 0x0191, 0x0000 }, /* 014f LATIN SMALL LETTER O WITH BREVE */ { 0x0193, 0x0000, 0x0000, 0x0193 }, /* 0150 LATIN CAPITAL LETTER O WITH DOUBLE ACUT */ { 0x0000, 0x0195, 0x0195, 0x0000 }, /* 0151 LATIN SMALL LETTER O WITH DOUBLE ACUTE */ { 0x0197, 0x0000, 0x0000, 0x0197 }, /* 0152 LATIN CAPITAL LIGATURE OE */ { 0x0000, 0x0199, 0x0199, 0x0000 }, /* 0153 LATIN SMALL LIGATURE OE */ { 0x019b, 0x0000, 0x0000, 0x019b }, /* 0154 LATIN CAPITAL LETTER R WITH ACUTE */ { 0x0000, 0x019d, 0x019d, 0x0000 }, /* 0155 LATIN SMALL LETTER R WITH ACUTE */ { 0x019f, 0x0000, 0x0000, 0x019f }, /* 0156 LATIN CAPITAL LETTER R WITH CEDILLA */ { 0x0000, 0x01a1, 0x01a1, 0x0000 }, /* 0157 LATIN SMALL LETTER R WITH CEDILLA */ { 0x01a3, 0x0000, 0x0000, 0x01a3 }, /* 0158 LATIN CAPITAL LETTER R WITH CARON */ { 0x0000, 0x01a5, 0x01a5, 0x0000 }, /* 0159 LATIN SMALL LETTER R WITH CARON */ { 0x01a7, 0x0000, 0x0000, 0x01a7 }, /* 015a LATIN CAPITAL LETTER S WITH ACUTE */ { 0x0000, 0x01a9, 0x01a9, 0x0000 }, /* 015b LATIN SMALL LETTER S WITH ACUTE */ { 0x01ab, 0x0000, 0x0000, 0x01ab }, /* 015c LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ { 0x0000, 0x01ad, 0x01ad, 0x0000 }, /* 015d LATIN SMALL LETTER S WITH CIRCUMFLEX */ { 0x01af, 0x0000, 0x0000, 0x01af }, /* 015e LATIN CAPITAL LETTER S WITH CEDILLA */ { 0x0000, 0x01b1, 0x01b1, 0x0000 }, /* 015f LATIN SMALL LETTER S WITH CEDILLA */ { 0x01b3, 0x0000, 0x0000, 0x01b3 }, /* 0160 LATIN CAPITAL LETTER S WITH CARON */ { 0x0000, 0x01b5, 0x01b5, 0x0000 }, /* 0161 LATIN SMALL LETTER S WITH CARON */ { 0x01b7, 0x0000, 0x0000, 0x01b7 }, /* 0162 LATIN CAPITAL LETTER T WITH CEDILLA */ { 0x0000, 0x01b9, 0x01b9, 0x0000 }, /* 0163 LATIN SMALL LETTER T WITH CEDILLA */ { 0x01bb, 0x0000, 0x0000, 0x01bb }, /* 0164 LATIN CAPITAL LETTER T WITH CARON */ { 0x0000, 0x01bd, 0x01bd, 0x0000 }, /* 0165 LATIN SMALL LETTER T WITH CARON */ { 0x01bf, 0x0000, 0x0000, 0x01bf }, /* 0166 LATIN CAPITAL LETTER T WITH STROKE */ { 0x0000, 0x01c1, 0x01c1, 0x0000 }, /* 0167 LATIN SMALL LETTER T WITH STROKE */ { 0x01c3, 0x0000, 0x0000, 0x01c3 }, /* 0168 LATIN CAPITAL LETTER U WITH TILDE */ { 0x0000, 0x01c5, 0x01c5, 0x0000 }, /* 0169 LATIN SMALL LETTER U WITH TILDE */ { 0x01c7, 0x0000, 0x0000, 0x01c7 }, /* 016a LATIN CAPITAL LETTER U WITH MACRON */ { 0x0000, 0x01c9, 0x01c9, 0x0000 }, /* 016b LATIN SMALL LETTER U WITH MACRON */ { 0x01cb, 0x0000, 0x0000, 0x01cb }, /* 016c LATIN CAPITAL LETTER U WITH BREVE */ { 0x0000, 0x01cd, 0x01cd, 0x0000 }, /* 016d LATIN SMALL LETTER U WITH BREVE */ { 0x01cf, 0x0000, 0x0000, 0x01cf }, /* 016e LATIN CAPITAL LETTER U WITH RING ABOVE */ { 0x0000, 0x01d1, 0x01d1, 0x0000 }, /* 016f LATIN SMALL LETTER U WITH RING ABOVE */ { 0x01d3, 0x0000, 0x0000, 0x01d3 }, /* 0170 LATIN CAPITAL LETTER U WITH DOUBLE ACUT */ { 0x0000, 0x01d5, 0x01d5, 0x0000 }, /* 0171 LATIN SMALL LETTER U WITH DOUBLE ACUTE */ { 0x01d7, 0x0000, 0x0000, 0x01d7 }, /* 0172 LATIN CAPITAL LETTER U WITH OGONEK */ { 0x0000, 0x01d9, 0x01d9, 0x0000 }, /* 0173 LATIN SMALL LETTER U WITH OGONEK */ { 0x01db, 0x0000, 0x0000, 0x01db }, /* 0174 LATIN CAPITAL LETTER W WITH CIRCUMFLEX */ { 0x0000, 0x01dd, 0x01dd, 0x0000 }, /* 0175 LATIN SMALL LETTER W WITH CIRCUMFLEX */ { 0x01df, 0x0000, 0x0000, 0x01df }, /* 0176 LATIN CAPITAL LETTER Y WITH CIRCUMFLEX */ { 0x0000, 0x01e1, 0x01e1, 0x0000 }, /* 0177 LATIN SMALL LETTER Y WITH CIRCUMFLEX */ { 0x01e3, 0x0000, 0x0000, 0x01e3 }, /* 0178 LATIN CAPITAL LETTER Y WITH DIAERESIS */ { 0x01e5, 0x0000, 0x0000, 0x01e5 }, /* 0179 LATIN CAPITAL LETTER Z WITH ACUTE */ { 0x0000, 0x01e7, 0x01e7, 0x0000 }, /* 017a LATIN SMALL LETTER Z WITH ACUTE */ { 0x01e9, 0x0000, 0x0000, 0x01e9 }, /* 017b LATIN CAPITAL LETTER Z WITH DOT ABOVE */ { 0x0000, 0x01eb, 0x01eb, 0x0000 }, /* 017c LATIN SMALL LETTER Z WITH DOT ABOVE */ { 0x01ed, 0x0000, 0x0000, 0x01ed }, /* 017d LATIN CAPITAL LETTER Z WITH CARON */ { 0x0000, 0x01ef, 0x01ef, 0x0000 }, /* 017e LATIN SMALL LETTER Z WITH CARON */ { 0x0000, 0x01f1, 0x01f1, 0x01f3 } /* 017f LATIN SMALL LETTER LONG S */ }; static case_table case_pg_003[128] = { { 0x0000, 0x01f5, 0x01f5, 0x0000 }, /* 0180 LATIN SMALL LETTER B WITH STROKE */ { 0x01f7, 0x0000, 0x0000, 0x01f7 }, /* 0181 LATIN CAPITAL LETTER B WITH HOOK */ { 0x01f9, 0x0000, 0x0000, 0x01f9 }, /* 0182 LATIN CAPITAL LETTER B WITH TOPBAR */ { 0x0000, 0x01fb, 0x01fb, 0x0000 }, /* 0183 LATIN SMALL LETTER B WITH TOPBAR */ { 0x01fd, 0x0000, 0x0000, 0x01fd }, /* 0184 LATIN CAPITAL LETTER TONE SIX */ { 0x0000, 0x01ff, 0x01ff, 0x0000 }, /* 0185 LATIN SMALL LETTER TONE SIX */ { 0x0201, 0x0000, 0x0000, 0x0201 }, /* 0186 LATIN CAPITAL LETTER OPEN O */ { 0x0203, 0x0000, 0x0000, 0x0203 }, /* 0187 LATIN CAPITAL LETTER C WITH HOOK */ { 0x0000, 0x0205, 0x0205, 0x0000 }, /* 0188 LATIN SMALL LETTER C WITH HOOK */ { 0x0207, 0x0000, 0x0000, 0x0207 }, /* 0189 LATIN CAPITAL LETTER AFRICAN D */ { 0x0209, 0x0000, 0x0000, 0x0209 }, /* 018a LATIN CAPITAL LETTER D WITH HOOK */ { 0x020b, 0x0000, 0x0000, 0x020b }, /* 018b LATIN CAPITAL LETTER D WITH TOPBAR */ { 0x0000, 0x020d, 0x020d, 0x0000 }, /* 018c LATIN SMALL LETTER D WITH TOPBAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 018d LATIN SMALL LETTER TURNED DELTA */ { 0x020f, 0x0000, 0x0000, 0x020f }, /* 018e LATIN CAPITAL LETTER REVERSED E */ { 0x0211, 0x0000, 0x0000, 0x0211 }, /* 018f LATIN CAPITAL LETTER SCHWA */ { 0x0213, 0x0000, 0x0000, 0x0213 }, /* 0190 LATIN CAPITAL LETTER OPEN E */ { 0x0215, 0x0000, 0x0000, 0x0215 }, /* 0191 LATIN CAPITAL LETTER F WITH HOOK */ { 0x0000, 0x0217, 0x0217, 0x0000 }, /* 0192 LATIN SMALL LETTER F WITH HOOK */ { 0x0219, 0x0000, 0x0000, 0x0219 }, /* 0193 LATIN CAPITAL LETTER G WITH HOOK */ { 0x021b, 0x0000, 0x0000, 0x021b }, /* 0194 LATIN CAPITAL LETTER GAMMA */ { 0x0000, 0x021d, 0x021d, 0x0000 }, /* 0195 LATIN SMALL LETTER HV */ { 0x021f, 0x0000, 0x0000, 0x021f }, /* 0196 LATIN CAPITAL LETTER IOTA */ { 0x0221, 0x0000, 0x0000, 0x0221 }, /* 0197 LATIN CAPITAL LETTER I WITH STROKE */ { 0x0223, 0x0000, 0x0000, 0x0223 }, /* 0198 LATIN CAPITAL LETTER K WITH HOOK */ { 0x0000, 0x0225, 0x0225, 0x0000 }, /* 0199 LATIN SMALL LETTER K WITH HOOK */ { 0x0000, 0x0227, 0x0227, 0x0000 }, /* 019a LATIN SMALL LETTER L WITH BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 019b LATIN SMALL LETTER LAMBDA WITH STROKE */ { 0x0229, 0x0000, 0x0000, 0x0229 }, /* 019c LATIN CAPITAL LETTER TURNED M */ { 0x022b, 0x0000, 0x0000, 0x022b }, /* 019d LATIN CAPITAL LETTER N WITH LEFT HOOK */ { 0x0000, 0x022d, 0x022d, 0x0000 }, /* 019e LATIN SMALL LETTER N WITH LONG RIGHT LE */ { 0x022f, 0x0000, 0x0000, 0x022f }, /* 019f LATIN CAPITAL LETTER O WITH MIDDLE TILD */ { 0x0231, 0x0000, 0x0000, 0x0231 }, /* 01a0 LATIN CAPITAL LETTER O WITH HORN */ { 0x0000, 0x0233, 0x0233, 0x0000 }, /* 01a1 LATIN SMALL LETTER O WITH HORN */ { 0x0235, 0x0000, 0x0000, 0x0235 }, /* 01a2 LATIN CAPITAL LETTER OI */ { 0x0000, 0x0237, 0x0237, 0x0000 }, /* 01a3 LATIN SMALL LETTER OI */ { 0x0239, 0x0000, 0x0000, 0x0239 }, /* 01a4 LATIN CAPITAL LETTER P WITH HOOK */ { 0x0000, 0x023b, 0x023b, 0x0000 }, /* 01a5 LATIN SMALL LETTER P WITH HOOK */ { 0x023d, 0x0000, 0x0000, 0x023d }, /* 01a6 LATIN LETTER YR */ { 0x023f, 0x0000, 0x0000, 0x023f }, /* 01a7 LATIN CAPITAL LETTER TONE TWO */ { 0x0000, 0x0241, 0x0241, 0x0000 }, /* 01a8 LATIN SMALL LETTER TONE TWO */ { 0x0243, 0x0000, 0x0000, 0x0243 }, /* 01a9 LATIN CAPITAL LETTER ESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01aa LATIN LETTER REVERSED ESH LOOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01ab LATIN SMALL LETTER T WITH PALATAL HOOK */ { 0x0245, 0x0000, 0x0000, 0x0245 }, /* 01ac LATIN CAPITAL LETTER T WITH HOOK */ { 0x0000, 0x0247, 0x0247, 0x0000 }, /* 01ad LATIN SMALL LETTER T WITH HOOK */ { 0x0249, 0x0000, 0x0000, 0x0249 }, /* 01ae LATIN CAPITAL LETTER T WITH RETROFLEX H */ { 0x024b, 0x0000, 0x0000, 0x024b }, /* 01af LATIN CAPITAL LETTER U WITH HORN */ { 0x0000, 0x024d, 0x024d, 0x0000 }, /* 01b0 LATIN SMALL LETTER U WITH HORN */ { 0x024f, 0x0000, 0x0000, 0x024f }, /* 01b1 LATIN CAPITAL LETTER UPSILON */ { 0x0251, 0x0000, 0x0000, 0x0251 }, /* 01b2 LATIN CAPITAL LETTER V WITH HOOK */ { 0x0253, 0x0000, 0x0000, 0x0253 }, /* 01b3 LATIN CAPITAL LETTER Y WITH HOOK */ { 0x0000, 0x0255, 0x0255, 0x0000 }, /* 01b4 LATIN SMALL LETTER Y WITH HOOK */ { 0x0257, 0x0000, 0x0000, 0x0257 }, /* 01b5 LATIN CAPITAL LETTER Z WITH STROKE */ { 0x0000, 0x0259, 0x0259, 0x0000 }, /* 01b6 LATIN SMALL LETTER Z WITH STROKE */ { 0x025b, 0x0000, 0x0000, 0x025b }, /* 01b7 LATIN CAPITAL LETTER EZH */ { 0x025d, 0x0000, 0x0000, 0x025d }, /* 01b8 LATIN CAPITAL LETTER EZH REVERSED */ { 0x0000, 0x025f, 0x025f, 0x0000 }, /* 01b9 LATIN SMALL LETTER EZH REVERSED */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01ba LATIN SMALL LETTER EZH WITH TAIL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01bb LATIN LETTER TWO WITH STROKE */ { 0x0261, 0x0000, 0x0000, 0x0261 }, /* 01bc LATIN CAPITAL LETTER TONE FIVE */ { 0x0000, 0x0263, 0x0263, 0x0000 }, /* 01bd LATIN SMALL LETTER TONE FIVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01be LATIN LETTER INVERTED GLOTTAL STOP WITH */ { 0x0000, 0x0265, 0x0265, 0x0000 }, /* 01bf LATIN LETTER WYNN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01c0 LATIN LETTER DENTAL CLICK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01c1 LATIN LETTER LATERAL CLICK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01c2 LATIN LETTER ALVEOLAR CLICK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01c3 LATIN LETTER RETROFLEX CLICK */ { 0x0267, 0x0269, 0x0000, 0x0267 }, /* 01c4 LATIN CAPITAL LETTER DZ WITH CARON */ { 0x026b, 0x026d, 0x026f, 0x026b }, /* 01c5 LATIN CAPITAL LETTER D WITH SMALL LETTE */ { 0x0000, 0x0271, 0x0273, 0x0000 }, /* 01c6 LATIN SMALL LETTER DZ WITH CARON */ { 0x0275, 0x0277, 0x0000, 0x0275 }, /* 01c7 LATIN CAPITAL LETTER LJ */ { 0x0279, 0x027b, 0x027d, 0x0279 }, /* 01c8 LATIN CAPITAL LETTER L WITH SMALL LETTE */ { 0x0000, 0x027f, 0x0281, 0x0000 }, /* 01c9 LATIN SMALL LETTER LJ */ { 0x0283, 0x0285, 0x0000, 0x0283 }, /* 01ca LATIN CAPITAL LETTER NJ */ { 0x0287, 0x0289, 0x028b, 0x0287 }, /* 01cb LATIN CAPITAL LETTER N WITH SMALL LETTE */ { 0x0000, 0x028d, 0x028f, 0x0000 }, /* 01cc LATIN SMALL LETTER NJ */ { 0x0291, 0x0000, 0x0000, 0x0291 }, /* 01cd LATIN CAPITAL LETTER A WITH CARON */ { 0x0000, 0x0293, 0x0293, 0x0000 }, /* 01ce LATIN SMALL LETTER A WITH CARON */ { 0x0295, 0x0000, 0x0000, 0x0295 }, /* 01cf LATIN CAPITAL LETTER I WITH CARON */ { 0x0000, 0x0297, 0x0297, 0x0000 }, /* 01d0 LATIN SMALL LETTER I WITH CARON */ { 0x0299, 0x0000, 0x0000, 0x0299 }, /* 01d1 LATIN CAPITAL LETTER O WITH CARON */ { 0x0000, 0x029b, 0x029b, 0x0000 }, /* 01d2 LATIN SMALL LETTER O WITH CARON */ { 0x029d, 0x0000, 0x0000, 0x029d }, /* 01d3 LATIN CAPITAL LETTER U WITH CARON */ { 0x0000, 0x029f, 0x029f, 0x0000 }, /* 01d4 LATIN SMALL LETTER U WITH CARON */ { 0x02a1, 0x0000, 0x0000, 0x02a1 }, /* 01d5 LATIN CAPITAL LETTER U WITH DIAERESIS A */ { 0x0000, 0x02a3, 0x02a3, 0x0000 }, /* 01d6 LATIN SMALL LETTER U WITH DIAERESIS AND */ { 0x02a5, 0x0000, 0x0000, 0x02a5 }, /* 01d7 LATIN CAPITAL LETTER U WITH DIAERESIS A */ { 0x0000, 0x02a7, 0x02a7, 0x0000 }, /* 01d8 LATIN SMALL LETTER U WITH DIAERESIS AND */ { 0x02a9, 0x0000, 0x0000, 0x02a9 }, /* 01d9 LATIN CAPITAL LETTER U WITH DIAERESIS A */ { 0x0000, 0x02ab, 0x02ab, 0x0000 }, /* 01da LATIN SMALL LETTER U WITH DIAERESIS AND */ { 0x02ad, 0x0000, 0x0000, 0x02ad }, /* 01db LATIN CAPITAL LETTER U WITH DIAERESIS A */ { 0x0000, 0x02af, 0x02af, 0x0000 }, /* 01dc LATIN SMALL LETTER U WITH DIAERESIS AND */ { 0x0000, 0x02b1, 0x02b1, 0x0000 }, /* 01dd LATIN SMALL LETTER TURNED E */ { 0x02b3, 0x0000, 0x0000, 0x02b3 }, /* 01de LATIN CAPITAL LETTER A WITH DIAERESIS A */ { 0x0000, 0x02b5, 0x02b5, 0x0000 }, /* 01df LATIN SMALL LETTER A WITH DIAERESIS AND */ { 0x02b7, 0x0000, 0x0000, 0x02b7 }, /* 01e0 LATIN CAPITAL LETTER A WITH DOT ABOVE A */ { 0x0000, 0x02b9, 0x02b9, 0x0000 }, /* 01e1 LATIN SMALL LETTER A WITH DOT ABOVE AND */ { 0x02bb, 0x0000, 0x0000, 0x02bb }, /* 01e2 LATIN CAPITAL LETTER AE WITH MACRON */ { 0x0000, 0x02bd, 0x02bd, 0x0000 }, /* 01e3 LATIN SMALL LETTER AE WITH MACRON */ { 0x02bf, 0x0000, 0x0000, 0x02bf }, /* 01e4 LATIN CAPITAL LETTER G WITH STROKE */ { 0x0000, 0x02c1, 0x02c1, 0x0000 }, /* 01e5 LATIN SMALL LETTER G WITH STROKE */ { 0x02c3, 0x0000, 0x0000, 0x02c3 }, /* 01e6 LATIN CAPITAL LETTER G WITH CARON */ { 0x0000, 0x02c5, 0x02c5, 0x0000 }, /* 01e7 LATIN SMALL LETTER G WITH CARON */ { 0x02c7, 0x0000, 0x0000, 0x02c7 }, /* 01e8 LATIN CAPITAL LETTER K WITH CARON */ { 0x0000, 0x02c9, 0x02c9, 0x0000 }, /* 01e9 LATIN SMALL LETTER K WITH CARON */ { 0x02cb, 0x0000, 0x0000, 0x02cb }, /* 01ea LATIN CAPITAL LETTER O WITH OGONEK */ { 0x0000, 0x02cd, 0x02cd, 0x0000 }, /* 01eb LATIN SMALL LETTER O WITH OGONEK */ { 0x02cf, 0x0000, 0x0000, 0x02cf }, /* 01ec LATIN CAPITAL LETTER O WITH OGONEK AND */ { 0x0000, 0x02d1, 0x02d1, 0x0000 }, /* 01ed LATIN SMALL LETTER O WITH OGONEK AND MA */ { 0x02d3, 0x0000, 0x0000, 0x02d3 }, /* 01ee LATIN CAPITAL LETTER EZH WITH CARON */ { 0x0000, 0x02d5, 0x02d5, 0x0000 }, /* 01ef LATIN SMALL LETTER EZH WITH CARON */ { 0x0000, 0x02d7, 0x02d7, 0x02da }, /* 01f0 LATIN SMALL LETTER J WITH CARON */ { 0x02dd, 0x02df, 0x0000, 0x02dd }, /* 01f1 LATIN CAPITAL LETTER DZ */ { 0x02e1, 0x02e3, 0x02e5, 0x02e1 }, /* 01f2 LATIN CAPITAL LETTER D WITH SMALL LETTE */ { 0x0000, 0x02e7, 0x02e9, 0x0000 }, /* 01f3 LATIN SMALL LETTER DZ */ { 0x02eb, 0x0000, 0x0000, 0x02eb }, /* 01f4 LATIN CAPITAL LETTER G WITH ACUTE */ { 0x0000, 0x02ed, 0x02ed, 0x0000 }, /* 01f5 LATIN SMALL LETTER G WITH ACUTE */ { 0x02ef, 0x0000, 0x0000, 0x02ef }, /* 01f6 LATIN CAPITAL LETTER HWAIR */ { 0x02f1, 0x0000, 0x0000, 0x02f1 }, /* 01f7 LATIN CAPITAL LETTER WYNN */ { 0x02f3, 0x0000, 0x0000, 0x02f3 }, /* 01f8 LATIN CAPITAL LETTER N WITH GRAVE */ { 0x0000, 0x02f5, 0x02f5, 0x0000 }, /* 01f9 LATIN SMALL LETTER N WITH GRAVE */ { 0x02f7, 0x0000, 0x0000, 0x02f7 }, /* 01fa LATIN CAPITAL LETTER A WITH RING ABOVE */ { 0x0000, 0x02f9, 0x02f9, 0x0000 }, /* 01fb LATIN SMALL LETTER A WITH RING ABOVE AN */ { 0x02fb, 0x0000, 0x0000, 0x02fb }, /* 01fc LATIN CAPITAL LETTER AE WITH ACUTE */ { 0x0000, 0x02fd, 0x02fd, 0x0000 }, /* 01fd LATIN SMALL LETTER AE WITH ACUTE */ { 0x02ff, 0x0000, 0x0000, 0x02ff }, /* 01fe LATIN CAPITAL LETTER O WITH STROKE AND */ { 0x0000, 0x0301, 0x0301, 0x0000 } /* 01ff LATIN SMALL LETTER O WITH STROKE AND AC */ }; static case_table case_pg_004[128] = { { 0x0303, 0x0000, 0x0000, 0x0303 }, /* 0200 LATIN CAPITAL LETTER A WITH DOUBLE GRAV */ { 0x0000, 0x0305, 0x0305, 0x0000 }, /* 0201 LATIN SMALL LETTER A WITH DOUBLE GRAVE */ { 0x0307, 0x0000, 0x0000, 0x0307 }, /* 0202 LATIN CAPITAL LETTER A WITH INVERTED BR */ { 0x0000, 0x0309, 0x0309, 0x0000 }, /* 0203 LATIN SMALL LETTER A WITH INVERTED BREV */ { 0x030b, 0x0000, 0x0000, 0x030b }, /* 0204 LATIN CAPITAL LETTER E WITH DOUBLE GRAV */ { 0x0000, 0x030d, 0x030d, 0x0000 }, /* 0205 LATIN SMALL LETTER E WITH DOUBLE GRAVE */ { 0x030f, 0x0000, 0x0000, 0x030f }, /* 0206 LATIN CAPITAL LETTER E WITH INVERTED BR */ { 0x0000, 0x0311, 0x0311, 0x0000 }, /* 0207 LATIN SMALL LETTER E WITH INVERTED BREV */ { 0x0313, 0x0000, 0x0000, 0x0313 }, /* 0208 LATIN CAPITAL LETTER I WITH DOUBLE GRAV */ { 0x0000, 0x0315, 0x0315, 0x0000 }, /* 0209 LATIN SMALL LETTER I WITH DOUBLE GRAVE */ { 0x0317, 0x0000, 0x0000, 0x0317 }, /* 020a LATIN CAPITAL LETTER I WITH INVERTED BR */ { 0x0000, 0x0319, 0x0319, 0x0000 }, /* 020b LATIN SMALL LETTER I WITH INVERTED BREV */ { 0x031b, 0x0000, 0x0000, 0x031b }, /* 020c LATIN CAPITAL LETTER O WITH DOUBLE GRAV */ { 0x0000, 0x031d, 0x031d, 0x0000 }, /* 020d LATIN SMALL LETTER O WITH DOUBLE GRAVE */ { 0x031f, 0x0000, 0x0000, 0x031f }, /* 020e LATIN CAPITAL LETTER O WITH INVERTED BR */ { 0x0000, 0x0321, 0x0321, 0x0000 }, /* 020f LATIN SMALL LETTER O WITH INVERTED BREV */ { 0x0323, 0x0000, 0x0000, 0x0323 }, /* 0210 LATIN CAPITAL LETTER R WITH DOUBLE GRAV */ { 0x0000, 0x0325, 0x0325, 0x0000 }, /* 0211 LATIN SMALL LETTER R WITH DOUBLE GRAVE */ { 0x0327, 0x0000, 0x0000, 0x0327 }, /* 0212 LATIN CAPITAL LETTER R WITH INVERTED BR */ { 0x0000, 0x0329, 0x0329, 0x0000 }, /* 0213 LATIN SMALL LETTER R WITH INVERTED BREV */ { 0x032b, 0x0000, 0x0000, 0x032b }, /* 0214 LATIN CAPITAL LETTER U WITH DOUBLE GRAV */ { 0x0000, 0x032d, 0x032d, 0x0000 }, /* 0215 LATIN SMALL LETTER U WITH DOUBLE GRAVE */ { 0x032f, 0x0000, 0x0000, 0x032f }, /* 0216 LATIN CAPITAL LETTER U WITH INVERTED BR */ { 0x0000, 0x0331, 0x0331, 0x0000 }, /* 0217 LATIN SMALL LETTER U WITH INVERTED BREV */ { 0x0333, 0x0000, 0x0000, 0x0333 }, /* 0218 LATIN CAPITAL LETTER S WITH COMMA BELOW */ { 0x0000, 0x0335, 0x0335, 0x0000 }, /* 0219 LATIN SMALL LETTER S WITH COMMA BELOW */ { 0x0337, 0x0000, 0x0000, 0x0337 }, /* 021a LATIN CAPITAL LETTER T WITH COMMA BELOW */ { 0x0000, 0x0339, 0x0339, 0x0000 }, /* 021b LATIN SMALL LETTER T WITH COMMA BELOW */ { 0x033b, 0x0000, 0x0000, 0x033b }, /* 021c LATIN CAPITAL LETTER YOGH */ { 0x0000, 0x033d, 0x033d, 0x0000 }, /* 021d LATIN SMALL LETTER YOGH */ { 0x033f, 0x0000, 0x0000, 0x033f }, /* 021e LATIN CAPITAL LETTER H WITH CARON */ { 0x0000, 0x0341, 0x0341, 0x0000 }, /* 021f LATIN SMALL LETTER H WITH CARON */ { 0x0343, 0x0000, 0x0000, 0x0343 }, /* 0220 LATIN CAPITAL LETTER N WITH LONG RIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0221 LATIN SMALL LETTER D WITH CURL */ { 0x0345, 0x0000, 0x0000, 0x0345 }, /* 0222 LATIN CAPITAL LETTER OU */ { 0x0000, 0x0347, 0x0347, 0x0000 }, /* 0223 LATIN SMALL LETTER OU */ { 0x0349, 0x0000, 0x0000, 0x0349 }, /* 0224 LATIN CAPITAL LETTER Z WITH HOOK */ { 0x0000, 0x034b, 0x034b, 0x0000 }, /* 0225 LATIN SMALL LETTER Z WITH HOOK */ { 0x034d, 0x0000, 0x0000, 0x034d }, /* 0226 LATIN CAPITAL LETTER A WITH DOT ABOVE */ { 0x0000, 0x034f, 0x034f, 0x0000 }, /* 0227 LATIN SMALL LETTER A WITH DOT ABOVE */ { 0x0351, 0x0000, 0x0000, 0x0351 }, /* 0228 LATIN CAPITAL LETTER E WITH CEDILLA */ { 0x0000, 0x0353, 0x0353, 0x0000 }, /* 0229 LATIN SMALL LETTER E WITH CEDILLA */ { 0x0355, 0x0000, 0x0000, 0x0355 }, /* 022a LATIN CAPITAL LETTER O WITH DIAERESIS A */ { 0x0000, 0x0357, 0x0357, 0x0000 }, /* 022b LATIN SMALL LETTER O WITH DIAERESIS AND */ { 0x0359, 0x0000, 0x0000, 0x0359 }, /* 022c LATIN CAPITAL LETTER O WITH TILDE AND M */ { 0x0000, 0x035b, 0x035b, 0x0000 }, /* 022d LATIN SMALL LETTER O WITH TILDE AND MAC */ { 0x035d, 0x0000, 0x0000, 0x035d }, /* 022e LATIN CAPITAL LETTER O WITH DOT ABOVE */ { 0x0000, 0x035f, 0x035f, 0x0000 }, /* 022f LATIN SMALL LETTER O WITH DOT ABOVE */ { 0x0361, 0x0000, 0x0000, 0x0361 }, /* 0230 LATIN CAPITAL LETTER O WITH DOT ABOVE A */ { 0x0000, 0x0363, 0x0363, 0x0000 }, /* 0231 LATIN SMALL LETTER O WITH DOT ABOVE AND */ { 0x0365, 0x0000, 0x0000, 0x0365 }, /* 0232 LATIN CAPITAL LETTER Y WITH MACRON */ { 0x0000, 0x0367, 0x0367, 0x0000 }, /* 0233 LATIN SMALL LETTER Y WITH MACRON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0234 LATIN SMALL LETTER L WITH CURL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0235 LATIN SMALL LETTER N WITH CURL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0236 LATIN SMALL LETTER T WITH CURL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0237 LATIN SMALL LETTER DOTLESS J */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0238 LATIN SMALL LETTER DB DIGRAPH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0239 LATIN SMALL LETTER QP DIGRAPH */ { 0x0369, 0x0000, 0x0000, 0x0369 }, /* 023a LATIN CAPITAL LETTER A WITH STROKE */ { 0x036b, 0x0000, 0x0000, 0x036b }, /* 023b LATIN CAPITAL LETTER C WITH STROKE */ { 0x0000, 0x036d, 0x036d, 0x0000 }, /* 023c LATIN SMALL LETTER C WITH STROKE */ { 0x036f, 0x0000, 0x0000, 0x036f }, /* 023d LATIN CAPITAL LETTER L WITH BAR */ { 0x0371, 0x0000, 0x0000, 0x0371 }, /* 023e LATIN CAPITAL LETTER T WITH DIAGONAL ST */ { 0x0000, 0x0373, 0x0373, 0x0000 }, /* 023f LATIN SMALL LETTER S WITH SWASH TAIL */ { 0x0000, 0x0375, 0x0375, 0x0000 }, /* 0240 LATIN SMALL LETTER Z WITH SWASH TAIL */ { 0x0377, 0x0000, 0x0000, 0x0377 }, /* 0241 LATIN CAPITAL LETTER GLOTTAL STOP */ { 0x0000, 0x0379, 0x0379, 0x0000 }, /* 0242 LATIN SMALL LETTER GLOTTAL STOP */ { 0x037b, 0x0000, 0x0000, 0x037b }, /* 0243 LATIN CAPITAL LETTER B WITH STROKE */ { 0x037d, 0x0000, 0x0000, 0x037d }, /* 0244 LATIN CAPITAL LETTER U BAR */ { 0x037f, 0x0000, 0x0000, 0x037f }, /* 0245 LATIN CAPITAL LETTER TURNED V */ { 0x0381, 0x0000, 0x0000, 0x0381 }, /* 0246 LATIN CAPITAL LETTER E WITH STROKE */ { 0x0000, 0x0383, 0x0383, 0x0000 }, /* 0247 LATIN SMALL LETTER E WITH STROKE */ { 0x0385, 0x0000, 0x0000, 0x0385 }, /* 0248 LATIN CAPITAL LETTER J WITH STROKE */ { 0x0000, 0x0387, 0x0387, 0x0000 }, /* 0249 LATIN SMALL LETTER J WITH STROKE */ { 0x0389, 0x0000, 0x0000, 0x0389 }, /* 024a LATIN CAPITAL LETTER SMALL Q WITH HOOK */ { 0x0000, 0x038b, 0x038b, 0x0000 }, /* 024b LATIN SMALL LETTER Q WITH HOOK TAIL */ { 0x038d, 0x0000, 0x0000, 0x038d }, /* 024c LATIN CAPITAL LETTER R WITH STROKE */ { 0x0000, 0x038f, 0x038f, 0x0000 }, /* 024d LATIN SMALL LETTER R WITH STROKE */ { 0x0391, 0x0000, 0x0000, 0x0391 }, /* 024e LATIN CAPITAL LETTER Y WITH STROKE */ { 0x0000, 0x0393, 0x0393, 0x0000 }, /* 024f LATIN SMALL LETTER Y WITH STROKE */ { 0x0000, 0x0395, 0x0395, 0x0000 }, /* 0250 LATIN SMALL LETTER TURNED A */ { 0x0000, 0x0397, 0x0397, 0x0000 }, /* 0251 LATIN SMALL LETTER ALPHA */ { 0x0000, 0x0399, 0x0399, 0x0000 }, /* 0252 LATIN SMALL LETTER TURNED ALPHA */ { 0x0000, 0x039b, 0x039b, 0x0000 }, /* 0253 LATIN SMALL LETTER B WITH HOOK */ { 0x0000, 0x039d, 0x039d, 0x0000 }, /* 0254 LATIN SMALL LETTER OPEN O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0255 LATIN SMALL LETTER C WITH CURL */ { 0x0000, 0x039f, 0x039f, 0x0000 }, /* 0256 LATIN SMALL LETTER D WITH TAIL */ { 0x0000, 0x03a1, 0x03a1, 0x0000 }, /* 0257 LATIN SMALL LETTER D WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0258 LATIN SMALL LETTER REVERSED E */ { 0x0000, 0x03a3, 0x03a3, 0x0000 }, /* 0259 LATIN SMALL LETTER SCHWA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 025a LATIN SMALL LETTER SCHWA WITH HOOK */ { 0x0000, 0x03a5, 0x03a5, 0x0000 }, /* 025b LATIN SMALL LETTER OPEN E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 025c LATIN SMALL LETTER REVERSED OPEN E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 025d LATIN SMALL LETTER REVERSED OPEN E WITH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 025e LATIN SMALL LETTER CLOSED REVERSED OPEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 025f LATIN SMALL LETTER DOTLESS J WITH STROK */ { 0x0000, 0x03a7, 0x03a7, 0x0000 }, /* 0260 LATIN SMALL LETTER G WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0261 LATIN SMALL LETTER SCRIPT G */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0262 LATIN LETTER SMALL CAPITAL G */ { 0x0000, 0x03a9, 0x03a9, 0x0000 }, /* 0263 LATIN SMALL LETTER GAMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0264 LATIN SMALL LETTER RAMS HORN */ { 0x0000, 0x03ab, 0x03ab, 0x0000 }, /* 0265 LATIN SMALL LETTER TURNED H */ { 0x0000, 0x03ad, 0x03ad, 0x0000 }, /* 0266 LATIN SMALL LETTER H WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0267 LATIN SMALL LETTER HENG WITH HOOK */ { 0x0000, 0x03af, 0x03af, 0x0000 }, /* 0268 LATIN SMALL LETTER I WITH STROKE */ { 0x0000, 0x03b1, 0x03b1, 0x0000 }, /* 0269 LATIN SMALL LETTER IOTA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 026a LATIN LETTER SMALL CAPITAL I */ { 0x0000, 0x03b3, 0x03b3, 0x0000 }, /* 026b LATIN SMALL LETTER L WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 026c LATIN SMALL LETTER L WITH BELT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 026d LATIN SMALL LETTER L WITH RETROFLEX HOO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 026e LATIN SMALL LETTER LEZH */ { 0x0000, 0x03b5, 0x03b5, 0x0000 }, /* 026f LATIN SMALL LETTER TURNED M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0270 LATIN SMALL LETTER TURNED M WITH LONG L */ { 0x0000, 0x03b7, 0x03b7, 0x0000 }, /* 0271 LATIN SMALL LETTER M WITH HOOK */ { 0x0000, 0x03b9, 0x03b9, 0x0000 }, /* 0272 LATIN SMALL LETTER N WITH LEFT HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0273 LATIN SMALL LETTER N WITH RETROFLEX HOO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0274 LATIN LETTER SMALL CAPITAL N */ { 0x0000, 0x03bb, 0x03bb, 0x0000 }, /* 0275 LATIN SMALL LETTER BARRED O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0276 LATIN LETTER SMALL CAPITAL OE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0277 LATIN SMALL LETTER CLOSED OMEGA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0278 LATIN SMALL LETTER PHI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0279 LATIN SMALL LETTER TURNED R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 027a LATIN SMALL LETTER TURNED R WITH LONG L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 027b LATIN SMALL LETTER TURNED R WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 027c LATIN SMALL LETTER R WITH LONG LEG */ { 0x0000, 0x03bd, 0x03bd, 0x0000 }, /* 027d LATIN SMALL LETTER R WITH TAIL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 027e LATIN SMALL LETTER R WITH FISHHOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 027f LATIN SMALL LETTER REVERSED R WITH FISH */ }; static case_table case_pg_005[128] = { { 0x0000, 0x03bf, 0x03bf, 0x0000 }, /* 0280 LATIN LETTER SMALL CAPITAL R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0281 LATIN LETTER SMALL CAPITAL INVERTED R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0282 LATIN SMALL LETTER S WITH HOOK */ { 0x0000, 0x03c1, 0x03c1, 0x0000 }, /* 0283 LATIN SMALL LETTER ESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0284 LATIN SMALL LETTER DOTLESS J WITH STROK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0285 LATIN SMALL LETTER SQUAT REVERSED ESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0286 LATIN SMALL LETTER ESH WITH CURL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0287 LATIN SMALL LETTER TURNED T */ { 0x0000, 0x03c3, 0x03c3, 0x0000 }, /* 0288 LATIN SMALL LETTER T WITH RETROFLEX HOO */ { 0x0000, 0x03c5, 0x03c5, 0x0000 }, /* 0289 LATIN SMALL LETTER U BAR */ { 0x0000, 0x03c7, 0x03c7, 0x0000 }, /* 028a LATIN SMALL LETTER UPSILON */ { 0x0000, 0x03c9, 0x03c9, 0x0000 }, /* 028b LATIN SMALL LETTER V WITH HOOK */ { 0x0000, 0x03cb, 0x03cb, 0x0000 }, /* 028c LATIN SMALL LETTER TURNED V */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 028d LATIN SMALL LETTER TURNED W */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 028e LATIN SMALL LETTER TURNED Y */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 028f LATIN LETTER SMALL CAPITAL Y */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0290 LATIN SMALL LETTER Z WITH RETROFLEX HOO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0291 LATIN SMALL LETTER Z WITH CURL */ { 0x0000, 0x03cd, 0x03cd, 0x0000 }, /* 0292 LATIN SMALL LETTER EZH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0293 LATIN SMALL LETTER EZH WITH CURL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0294 LATIN LETTER GLOTTAL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0295 LATIN LETTER PHARYNGEAL VOICED FRICATIV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0296 LATIN LETTER INVERTED GLOTTAL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0297 LATIN LETTER STRETCHED C */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0298 LATIN LETTER BILABIAL CLICK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0299 LATIN LETTER SMALL CAPITAL B */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 029a LATIN SMALL LETTER CLOSED OPEN E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 029b LATIN LETTER SMALL CAPITAL G WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 029c LATIN LETTER SMALL CAPITAL H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 029d LATIN SMALL LETTER J WITH CROSSED-TAIL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 029e LATIN SMALL LETTER TURNED K */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 029f LATIN LETTER SMALL CAPITAL L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a0 LATIN SMALL LETTER Q WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a1 LATIN LETTER GLOTTAL STOP WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a2 LATIN LETTER REVERSED GLOTTAL STOP WITH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a3 LATIN SMALL LETTER DZ DIGRAPH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a4 LATIN SMALL LETTER DEZH DIGRAPH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a5 LATIN SMALL LETTER DZ DIGRAPH WITH CURL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a6 LATIN SMALL LETTER TS DIGRAPH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a7 LATIN SMALL LETTER TESH DIGRAPH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a8 LATIN SMALL LETTER TC DIGRAPH WITH CURL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a9 LATIN SMALL LETTER FENG DIGRAPH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02aa LATIN SMALL LETTER LS DIGRAPH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ab LATIN SMALL LETTER LZ DIGRAPH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ac LATIN LETTER BILABIAL PERCUSSIVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ad LATIN LETTER BIDENTAL PERCUSSIVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ae LATIN SMALL LETTER TURNED H WITH FISHHO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02af LATIN SMALL LETTER TURNED H WITH FISHHO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b0 MODIFIER LETTER SMALL H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b1 MODIFIER LETTER SMALL H WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b2 MODIFIER LETTER SMALL J */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b3 MODIFIER LETTER SMALL R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b4 MODIFIER LETTER SMALL TURNED R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b5 MODIFIER LETTER SMALL TURNED R WITH HOO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b6 MODIFIER LETTER SMALL CAPITAL INVERTED */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b7 MODIFIER LETTER SMALL W */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b8 MODIFIER LETTER SMALL Y */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b9 MODIFIER LETTER PRIME */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ba MODIFIER LETTER DOUBLE PRIME */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02bb MODIFIER LETTER TURNED COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02bc MODIFIER LETTER APOSTROPHE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02bd MODIFIER LETTER REVERSED COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02be MODIFIER LETTER RIGHT HALF RING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02bf MODIFIER LETTER LEFT HALF RING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c0 MODIFIER LETTER GLOTTAL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c1 MODIFIER LETTER REVERSED GLOTTAL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c2 MODIFIER LETTER LEFT ARROWHEAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c3 MODIFIER LETTER RIGHT ARROWHEAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c4 MODIFIER LETTER UP ARROWHEAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c5 MODIFIER LETTER DOWN ARROWHEAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c6 MODIFIER LETTER CIRCUMFLEX ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c7 CARON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c8 MODIFIER LETTER VERTICAL LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c9 MODIFIER LETTER MACRON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ca MODIFIER LETTER ACUTE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02cb MODIFIER LETTER GRAVE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02cc MODIFIER LETTER LOW VERTICAL LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02cd MODIFIER LETTER LOW MACRON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ce MODIFIER LETTER LOW GRAVE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02cf MODIFIER LETTER LOW ACUTE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d0 MODIFIER LETTER TRIANGULAR COLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d1 MODIFIER LETTER HALF TRIANGULAR COLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d2 MODIFIER LETTER CENTRED RIGHT HALF RING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d3 MODIFIER LETTER CENTRED LEFT HALF RING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d4 MODIFIER LETTER UP TACK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d5 MODIFIER LETTER DOWN TACK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d6 MODIFIER LETTER PLUS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d7 MODIFIER LETTER MINUS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d8 BREVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d9 DOT ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02da RING ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02db OGONEK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02dc SMALL TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02dd DOUBLE ACUTE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02de MODIFIER LETTER RHOTIC HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02df MODIFIER LETTER CROSS ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e0 MODIFIER LETTER SMALL GAMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e1 MODIFIER LETTER SMALL L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e2 MODIFIER LETTER SMALL S */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e3 MODIFIER LETTER SMALL X */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e4 MODIFIER LETTER SMALL REVERSED GLOTTAL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e5 MODIFIER LETTER EXTRA-HIGH TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e6 MODIFIER LETTER HIGH TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e7 MODIFIER LETTER MID TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e8 MODIFIER LETTER LOW TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e9 MODIFIER LETTER EXTRA-LOW TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ea MODIFIER LETTER YIN DEPARTING TONE MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02eb MODIFIER LETTER YANG DEPARTING TONE MAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ec MODIFIER LETTER VOICING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ed MODIFIER LETTER UNASPIRATED */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ee MODIFIER LETTER DOUBLE APOSTROPHE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ef MODIFIER LETTER LOW DOWN ARROWHEAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f0 MODIFIER LETTER LOW UP ARROWHEAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f1 MODIFIER LETTER LOW LEFT ARROWHEAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f2 MODIFIER LETTER LOW RIGHT ARROWHEAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f3 MODIFIER LETTER LOW RING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f4 MODIFIER LETTER MIDDLE GRAVE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f5 MODIFIER LETTER MIDDLE DOUBLE GRAVE ACC */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f6 MODIFIER LETTER MIDDLE DOUBLE ACUTE ACC */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f7 MODIFIER LETTER LOW TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f8 MODIFIER LETTER RAISED COLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f9 MODIFIER LETTER BEGIN HIGH TONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02fa MODIFIER LETTER END HIGH TONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02fb MODIFIER LETTER BEGIN LOW TONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02fc MODIFIER LETTER END LOW TONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02fd MODIFIER LETTER SHELF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02fe MODIFIER LETTER OPEN SHELF */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 02ff MODIFIER LETTER LOW LEFT ARROW */ }; static case_table case_pg_006[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0300 COMBINING GRAVE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0301 COMBINING ACUTE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0302 COMBINING CIRCUMFLEX ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0303 COMBINING TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0304 COMBINING MACRON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0305 COMBINING OVERLINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0306 COMBINING BREVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0307 COMBINING DOT ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0308 COMBINING DIAERESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0309 COMBINING HOOK ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 030a COMBINING RING ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 030b COMBINING DOUBLE ACUTE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 030c COMBINING CARON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 030d COMBINING VERTICAL LINE ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 030e COMBINING DOUBLE VERTICAL LINE ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 030f COMBINING DOUBLE GRAVE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0310 COMBINING CANDRABINDU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0311 COMBINING INVERTED BREVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0312 COMBINING TURNED COMMA ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0313 COMBINING COMMA ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0314 COMBINING REVERSED COMMA ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0315 COMBINING COMMA ABOVE RIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0316 COMBINING GRAVE ACCENT BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0317 COMBINING ACUTE ACCENT BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0318 COMBINING LEFT TACK BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0319 COMBINING RIGHT TACK BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 031a COMBINING LEFT ANGLE ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 031b COMBINING HORN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 031c COMBINING LEFT HALF RING BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 031d COMBINING UP TACK BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 031e COMBINING DOWN TACK BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 031f COMBINING PLUS SIGN BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0320 COMBINING MINUS SIGN BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0321 COMBINING PALATALIZED HOOK BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0322 COMBINING RETROFLEX HOOK BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0323 COMBINING DOT BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0324 COMBINING DIAERESIS BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0325 COMBINING RING BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0326 COMBINING COMMA BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0327 COMBINING CEDILLA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0328 COMBINING OGONEK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0329 COMBINING VERTICAL LINE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 032a COMBINING BRIDGE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 032b COMBINING INVERTED DOUBLE ARCH BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 032c COMBINING CARON BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 032d COMBINING CIRCUMFLEX ACCENT BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 032e COMBINING BREVE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 032f COMBINING INVERTED BREVE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0330 COMBINING TILDE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0331 COMBINING MACRON BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0332 COMBINING LOW LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0333 COMBINING DOUBLE LOW LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0334 COMBINING TILDE OVERLAY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0335 COMBINING SHORT STROKE OVERLAY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0336 COMBINING LONG STROKE OVERLAY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0337 COMBINING SHORT SOLIDUS OVERLAY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0338 COMBINING LONG SOLIDUS OVERLAY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0339 COMBINING RIGHT HALF RING BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 033a COMBINING INVERTED BRIDGE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 033b COMBINING SQUARE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 033c COMBINING SEAGULL BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 033d COMBINING X ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 033e COMBINING VERTICAL TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 033f COMBINING DOUBLE OVERLINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0340 COMBINING GRAVE TONE MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0341 COMBINING ACUTE TONE MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0342 COMBINING GREEK PERISPOMENI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0343 COMBINING GREEK KORONIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0344 COMBINING GREEK DIALYTIKA TONOS */ { 0x0000, 0x03cf, 0x03cf, 0x03d1 }, /* 0345 COMBINING GREEK YPOGEGRAMMENI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0346 COMBINING BRIDGE ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0347 COMBINING EQUALS SIGN BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0348 COMBINING DOUBLE VERTICAL LINE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0349 COMBINING LEFT ANGLE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 034a COMBINING NOT TILDE ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 034b COMBINING HOMOTHETIC ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 034c COMBINING ALMOST EQUAL TO ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 034d COMBINING LEFT RIGHT ARROW BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 034e COMBINING UPWARDS ARROW BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 034f COMBINING GRAPHEME JOINER */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0350 COMBINING RIGHT ARROWHEAD ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0351 COMBINING LEFT HALF RING ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0352 COMBINING FERMATA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0353 COMBINING X BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0354 COMBINING LEFT ARROWHEAD BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0355 COMBINING RIGHT ARROWHEAD BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0356 COMBINING RIGHT ARROWHEAD AND UP ARROWH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0357 COMBINING RIGHT HALF RING ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0358 COMBINING DOT ABOVE RIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0359 COMBINING ASTERISK BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 035a COMBINING DOUBLE RING BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 035b COMBINING ZIGZAG ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 035c COMBINING DOUBLE BREVE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 035d COMBINING DOUBLE BREVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 035e COMBINING DOUBLE MACRON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 035f COMBINING DOUBLE MACRON BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0360 COMBINING DOUBLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0361 COMBINING DOUBLE INVERTED BREVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0362 COMBINING DOUBLE RIGHTWARDS ARROW BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0363 COMBINING LATIN SMALL LETTER A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0364 COMBINING LATIN SMALL LETTER E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0365 COMBINING LATIN SMALL LETTER I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0366 COMBINING LATIN SMALL LETTER O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0367 COMBINING LATIN SMALL LETTER U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0368 COMBINING LATIN SMALL LETTER C */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0369 COMBINING LATIN SMALL LETTER D */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 036a COMBINING LATIN SMALL LETTER H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 036b COMBINING LATIN SMALL LETTER M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 036c COMBINING LATIN SMALL LETTER R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 036d COMBINING LATIN SMALL LETTER T */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 036e COMBINING LATIN SMALL LETTER V */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 036f COMBINING LATIN SMALL LETTER X */ { 0x03d3, 0x0000, 0x0000, 0x03d3 }, /* 0370 GREEK CAPITAL LETTER HETA */ { 0x0000, 0x03d5, 0x03d5, 0x0000 }, /* 0371 GREEK SMALL LETTER HETA */ { 0x03d7, 0x0000, 0x0000, 0x03d7 }, /* 0372 GREEK CAPITAL LETTER ARCHAIC SAMPI */ { 0x0000, 0x03d9, 0x03d9, 0x0000 }, /* 0373 GREEK SMALL LETTER ARCHAIC SAMPI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0374 GREEK NUMERAL SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0375 GREEK LOWER NUMERAL SIGN */ { 0x03db, 0x0000, 0x0000, 0x03db }, /* 0376 GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA */ { 0x0000, 0x03dd, 0x03dd, 0x0000 }, /* 0377 GREEK SMALL LETTER PAMPHYLIAN DIGAMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0378 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0379 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 037a GREEK YPOGEGRAMMENI */ { 0x0000, 0x03df, 0x03df, 0x0000 }, /* 037b GREEK SMALL REVERSED LUNATE SIGMA SYMBO */ { 0x0000, 0x03e1, 0x03e1, 0x0000 }, /* 037c GREEK SMALL DOTTED LUNATE SIGMA SYMBOL */ { 0x0000, 0x03e3, 0x03e3, 0x0000 }, /* 037d GREEK SMALL REVERSED DOTTED LUNATE SIGM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 037e GREEK QUESTION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 037f (null) */ }; static case_table case_pg_007[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0380 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0381 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0382 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0383 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0384 GREEK TONOS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0385 GREEK DIALYTIKA TONOS */ { 0x03e5, 0x0000, 0x0000, 0x03e5 }, /* 0386 GREEK CAPITAL LETTER ALPHA WITH TONOS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0387 GREEK ANO TELEIA */ { 0x03e7, 0x0000, 0x0000, 0x03e7 }, /* 0388 GREEK CAPITAL LETTER EPSILON WITH TONOS */ { 0x03e9, 0x0000, 0x0000, 0x03e9 }, /* 0389 GREEK CAPITAL LETTER ETA WITH TONOS */ { 0x03eb, 0x0000, 0x0000, 0x03eb }, /* 038a GREEK CAPITAL LETTER IOTA WITH TONOS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 038b (null) */ { 0x03ed, 0x0000, 0x0000, 0x03ed }, /* 038c GREEK CAPITAL LETTER OMICRON WITH TONOS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 038d (null) */ { 0x03ef, 0x0000, 0x0000, 0x03ef }, /* 038e GREEK CAPITAL LETTER UPSILON WITH TONOS */ { 0x03f1, 0x0000, 0x0000, 0x03f1 }, /* 038f GREEK CAPITAL LETTER OMEGA WITH TONOS */ { 0x0000, 0x03f3, 0x03f3, 0x03f7 }, /* 0390 GREEK SMALL LETTER IOTA WITH DIALYTIKA */ { 0x03fb, 0x0000, 0x0000, 0x03fb }, /* 0391 GREEK CAPITAL LETTER ALPHA */ { 0x03fd, 0x0000, 0x0000, 0x03fd }, /* 0392 GREEK CAPITAL LETTER BETA */ { 0x03ff, 0x0000, 0x0000, 0x03ff }, /* 0393 GREEK CAPITAL LETTER GAMMA */ { 0x0401, 0x0000, 0x0000, 0x0401 }, /* 0394 GREEK CAPITAL LETTER DELTA */ { 0x0403, 0x0000, 0x0000, 0x0403 }, /* 0395 GREEK CAPITAL LETTER EPSILON */ { 0x0405, 0x0000, 0x0000, 0x0405 }, /* 0396 GREEK CAPITAL LETTER ZETA */ { 0x0407, 0x0000, 0x0000, 0x0407 }, /* 0397 GREEK CAPITAL LETTER ETA */ { 0x0409, 0x0000, 0x0000, 0x0409 }, /* 0398 GREEK CAPITAL LETTER THETA */ { 0x040b, 0x0000, 0x0000, 0x040b }, /* 0399 GREEK CAPITAL LETTER IOTA */ { 0x040d, 0x0000, 0x0000, 0x040d }, /* 039a GREEK CAPITAL LETTER KAPPA */ { 0x040f, 0x0000, 0x0000, 0x040f }, /* 039b GREEK CAPITAL LETTER LAMDA */ { 0x0411, 0x0000, 0x0000, 0x0411 }, /* 039c GREEK CAPITAL LETTER MU */ { 0x0413, 0x0000, 0x0000, 0x0413 }, /* 039d GREEK CAPITAL LETTER NU */ { 0x0415, 0x0000, 0x0000, 0x0415 }, /* 039e GREEK CAPITAL LETTER XI */ { 0x0417, 0x0000, 0x0000, 0x0417 }, /* 039f GREEK CAPITAL LETTER OMICRON */ { 0x0419, 0x0000, 0x0000, 0x0419 }, /* 03a0 GREEK CAPITAL LETTER PI */ { 0x041b, 0x0000, 0x0000, 0x041b }, /* 03a1 GREEK CAPITAL LETTER RHO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 03a2 (null) */ { 0x041d, 0x0000, 0x0000, 0x041d }, /* 03a3 GREEK CAPITAL LETTER SIGMA */ { 0x041f, 0x0000, 0x0000, 0x041f }, /* 03a4 GREEK CAPITAL LETTER TAU */ { 0x0421, 0x0000, 0x0000, 0x0421 }, /* 03a5 GREEK CAPITAL LETTER UPSILON */ { 0x0423, 0x0000, 0x0000, 0x0423 }, /* 03a6 GREEK CAPITAL LETTER PHI */ { 0x0425, 0x0000, 0x0000, 0x0425 }, /* 03a7 GREEK CAPITAL LETTER CHI */ { 0x0427, 0x0000, 0x0000, 0x0427 }, /* 03a8 GREEK CAPITAL LETTER PSI */ { 0x0429, 0x0000, 0x0000, 0x0429 }, /* 03a9 GREEK CAPITAL LETTER OMEGA */ { 0x042b, 0x0000, 0x0000, 0x042b }, /* 03aa GREEK CAPITAL LETTER IOTA WITH DIALYTIK */ { 0x042d, 0x0000, 0x0000, 0x042d }, /* 03ab GREEK CAPITAL LETTER UPSILON WITH DIALY */ { 0x0000, 0x042f, 0x042f, 0x0000 }, /* 03ac GREEK SMALL LETTER ALPHA WITH TONOS */ { 0x0000, 0x0431, 0x0431, 0x0000 }, /* 03ad GREEK SMALL LETTER EPSILON WITH TONOS */ { 0x0000, 0x0433, 0x0433, 0x0000 }, /* 03ae GREEK SMALL LETTER ETA WITH TONOS */ { 0x0000, 0x0435, 0x0435, 0x0000 }, /* 03af GREEK SMALL LETTER IOTA WITH TONOS */ { 0x0000, 0x0437, 0x0437, 0x043b }, /* 03b0 GREEK SMALL LETTER UPSILON WITH DIALYTI */ { 0x0000, 0x043f, 0x043f, 0x0000 }, /* 03b1 GREEK SMALL LETTER ALPHA */ { 0x0000, 0x0441, 0x0441, 0x0000 }, /* 03b2 GREEK SMALL LETTER BETA */ { 0x0000, 0x0443, 0x0443, 0x0000 }, /* 03b3 GREEK SMALL LETTER GAMMA */ { 0x0000, 0x0445, 0x0445, 0x0000 }, /* 03b4 GREEK SMALL LETTER DELTA */ { 0x0000, 0x0447, 0x0447, 0x0000 }, /* 03b5 GREEK SMALL LETTER EPSILON */ { 0x0000, 0x0449, 0x0449, 0x0000 }, /* 03b6 GREEK SMALL LETTER ZETA */ { 0x0000, 0x044b, 0x044b, 0x0000 }, /* 03b7 GREEK SMALL LETTER ETA */ { 0x0000, 0x044d, 0x044d, 0x0000 }, /* 03b8 GREEK SMALL LETTER THETA */ { 0x0000, 0x044f, 0x044f, 0x0000 }, /* 03b9 GREEK SMALL LETTER IOTA */ { 0x0000, 0x0451, 0x0451, 0x0000 }, /* 03ba GREEK SMALL LETTER KAPPA */ { 0x0000, 0x0453, 0x0453, 0x0000 }, /* 03bb GREEK SMALL LETTER LAMDA */ { 0x0000, 0x0455, 0x0455, 0x0000 }, /* 03bc GREEK SMALL LETTER MU */ { 0x0000, 0x0457, 0x0457, 0x0000 }, /* 03bd GREEK SMALL LETTER NU */ { 0x0000, 0x0459, 0x0459, 0x0000 }, /* 03be GREEK SMALL LETTER XI */ { 0x0000, 0x045b, 0x045b, 0x0000 }, /* 03bf GREEK SMALL LETTER OMICRON */ { 0x0000, 0x045d, 0x045d, 0x0000 }, /* 03c0 GREEK SMALL LETTER PI */ { 0x0000, 0x045f, 0x045f, 0x0000 }, /* 03c1 GREEK SMALL LETTER RHO */ { 0x0000, 0x0461, 0x0461, 0x0463 }, /* 03c2 GREEK SMALL LETTER FINAL SIGMA */ { 0x0000, 0x0465, 0x0465, 0x0000 }, /* 03c3 GREEK SMALL LETTER SIGMA */ { 0x0000, 0x0467, 0x0467, 0x0000 }, /* 03c4 GREEK SMALL LETTER TAU */ { 0x0000, 0x0469, 0x0469, 0x0000 }, /* 03c5 GREEK SMALL LETTER UPSILON */ { 0x0000, 0x046b, 0x046b, 0x0000 }, /* 03c6 GREEK SMALL LETTER PHI */ { 0x0000, 0x046d, 0x046d, 0x0000 }, /* 03c7 GREEK SMALL LETTER CHI */ { 0x0000, 0x046f, 0x046f, 0x0000 }, /* 03c8 GREEK SMALL LETTER PSI */ { 0x0000, 0x0471, 0x0471, 0x0000 }, /* 03c9 GREEK SMALL LETTER OMEGA */ { 0x0000, 0x0473, 0x0473, 0x0000 }, /* 03ca GREEK SMALL LETTER IOTA WITH DIALYTIKA */ { 0x0000, 0x0475, 0x0475, 0x0000 }, /* 03cb GREEK SMALL LETTER UPSILON WITH DIALYTI */ { 0x0000, 0x0477, 0x0477, 0x0000 }, /* 03cc GREEK SMALL LETTER OMICRON WITH TONOS */ { 0x0000, 0x0479, 0x0479, 0x0000 }, /* 03cd GREEK SMALL LETTER UPSILON WITH TONOS */ { 0x0000, 0x047b, 0x047b, 0x0000 }, /* 03ce GREEK SMALL LETTER OMEGA WITH TONOS */ { 0x047d, 0x0000, 0x0000, 0x047d }, /* 03cf GREEK CAPITAL KAI SYMBOL */ { 0x0000, 0x047f, 0x047f, 0x0481 }, /* 03d0 GREEK BETA SYMBOL */ { 0x0000, 0x0483, 0x0483, 0x0485 }, /* 03d1 GREEK THETA SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 03d2 GREEK UPSILON WITH HOOK SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 03d3 GREEK UPSILON WITH ACUTE AND HOOK SYMBO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 03d4 GREEK UPSILON WITH DIAERESIS AND HOOK S */ { 0x0000, 0x0487, 0x0487, 0x0489 }, /* 03d5 GREEK PHI SYMBOL */ { 0x0000, 0x048b, 0x048b, 0x048d }, /* 03d6 GREEK PI SYMBOL */ { 0x0000, 0x048f, 0x048f, 0x0000 }, /* 03d7 GREEK KAI SYMBOL */ { 0x0491, 0x0000, 0x0000, 0x0491 }, /* 03d8 GREEK LETTER ARCHAIC KOPPA */ { 0x0000, 0x0493, 0x0493, 0x0000 }, /* 03d9 GREEK SMALL LETTER ARCHAIC KOPPA */ { 0x0495, 0x0000, 0x0000, 0x0495 }, /* 03da GREEK LETTER STIGMA */ { 0x0000, 0x0497, 0x0497, 0x0000 }, /* 03db GREEK SMALL LETTER STIGMA */ { 0x0499, 0x0000, 0x0000, 0x0499 }, /* 03dc GREEK LETTER DIGAMMA */ { 0x0000, 0x049b, 0x049b, 0x0000 }, /* 03dd GREEK SMALL LETTER DIGAMMA */ { 0x049d, 0x0000, 0x0000, 0x049d }, /* 03de GREEK LETTER KOPPA */ { 0x0000, 0x049f, 0x049f, 0x0000 }, /* 03df GREEK SMALL LETTER KOPPA */ { 0x04a1, 0x0000, 0x0000, 0x04a1 }, /* 03e0 GREEK LETTER SAMPI */ { 0x0000, 0x04a3, 0x04a3, 0x0000 }, /* 03e1 GREEK SMALL LETTER SAMPI */ { 0x04a5, 0x0000, 0x0000, 0x04a5 }, /* 03e2 COPTIC CAPITAL LETTER SHEI */ { 0x0000, 0x04a7, 0x04a7, 0x0000 }, /* 03e3 COPTIC SMALL LETTER SHEI */ { 0x04a9, 0x0000, 0x0000, 0x04a9 }, /* 03e4 COPTIC CAPITAL LETTER FEI */ { 0x0000, 0x04ab, 0x04ab, 0x0000 }, /* 03e5 COPTIC SMALL LETTER FEI */ { 0x04ad, 0x0000, 0x0000, 0x04ad }, /* 03e6 COPTIC CAPITAL LETTER KHEI */ { 0x0000, 0x04af, 0x04af, 0x0000 }, /* 03e7 COPTIC SMALL LETTER KHEI */ { 0x04b1, 0x0000, 0x0000, 0x04b1 }, /* 03e8 COPTIC CAPITAL LETTER HORI */ { 0x0000, 0x04b3, 0x04b3, 0x0000 }, /* 03e9 COPTIC SMALL LETTER HORI */ { 0x04b5, 0x0000, 0x0000, 0x04b5 }, /* 03ea COPTIC CAPITAL LETTER GANGIA */ { 0x0000, 0x04b7, 0x04b7, 0x0000 }, /* 03eb COPTIC SMALL LETTER GANGIA */ { 0x04b9, 0x0000, 0x0000, 0x04b9 }, /* 03ec COPTIC CAPITAL LETTER SHIMA */ { 0x0000, 0x04bb, 0x04bb, 0x0000 }, /* 03ed COPTIC SMALL LETTER SHIMA */ { 0x04bd, 0x0000, 0x0000, 0x04bd }, /* 03ee COPTIC CAPITAL LETTER DEI */ { 0x0000, 0x04bf, 0x04bf, 0x0000 }, /* 03ef COPTIC SMALL LETTER DEI */ { 0x0000, 0x04c1, 0x04c1, 0x04c3 }, /* 03f0 GREEK KAPPA SYMBOL */ { 0x0000, 0x04c5, 0x04c5, 0x04c7 }, /* 03f1 GREEK RHO SYMBOL */ { 0x0000, 0x04c9, 0x04c9, 0x0000 }, /* 03f2 GREEK LUNATE SIGMA SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 03f3 GREEK LETTER YOT */ { 0x04cb, 0x0000, 0x0000, 0x04cb }, /* 03f4 GREEK CAPITAL THETA SYMBOL */ { 0x0000, 0x04cd, 0x04cd, 0x04cf }, /* 03f5 GREEK LUNATE EPSILON SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 03f6 GREEK REVERSED LUNATE EPSILON SYMBOL */ { 0x04d1, 0x0000, 0x0000, 0x04d1 }, /* 03f7 GREEK CAPITAL LETTER SHO */ { 0x0000, 0x04d3, 0x04d3, 0x0000 }, /* 03f8 GREEK SMALL LETTER SHO */ { 0x04d5, 0x0000, 0x0000, 0x04d5 }, /* 03f9 GREEK CAPITAL LUNATE SIGMA SYMBOL */ { 0x04d7, 0x0000, 0x0000, 0x04d7 }, /* 03fa GREEK CAPITAL LETTER SAN */ { 0x0000, 0x04d9, 0x04d9, 0x0000 }, /* 03fb GREEK SMALL LETTER SAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 03fc GREEK RHO WITH STROKE SYMBOL */ { 0x04db, 0x0000, 0x0000, 0x04db }, /* 03fd GREEK CAPITAL REVERSED LUNATE SIGMA SYM */ { 0x04dd, 0x0000, 0x0000, 0x04dd }, /* 03fe GREEK CAPITAL DOTTED LUNATE SIGMA SYMBO */ { 0x04df, 0x0000, 0x0000, 0x04df } /* 03ff GREEK CAPITAL REVERSED DOTTED LUNATE SI */ }; static case_table case_pg_008[128] = { { 0x04e1, 0x0000, 0x0000, 0x04e1 }, /* 0400 CYRILLIC CAPITAL LETTER IE WITH GRAVE */ { 0x04e3, 0x0000, 0x0000, 0x04e3 }, /* 0401 CYRILLIC CAPITAL LETTER IO */ { 0x04e5, 0x0000, 0x0000, 0x04e5 }, /* 0402 CYRILLIC CAPITAL LETTER DJE */ { 0x04e7, 0x0000, 0x0000, 0x04e7 }, /* 0403 CYRILLIC CAPITAL LETTER GJE */ { 0x04e9, 0x0000, 0x0000, 0x04e9 }, /* 0404 CYRILLIC CAPITAL LETTER UKRAINIAN IE */ { 0x04eb, 0x0000, 0x0000, 0x04eb }, /* 0405 CYRILLIC CAPITAL LETTER DZE */ { 0x04ed, 0x0000, 0x0000, 0x04ed }, /* 0406 CYRILLIC CAPITAL LETTER BYELORUSSIAN-UK */ { 0x04ef, 0x0000, 0x0000, 0x04ef }, /* 0407 CYRILLIC CAPITAL LETTER YI */ { 0x04f1, 0x0000, 0x0000, 0x04f1 }, /* 0408 CYRILLIC CAPITAL LETTER JE */ { 0x04f3, 0x0000, 0x0000, 0x04f3 }, /* 0409 CYRILLIC CAPITAL LETTER LJE */ { 0x04f5, 0x0000, 0x0000, 0x04f5 }, /* 040a CYRILLIC CAPITAL LETTER NJE */ { 0x04f7, 0x0000, 0x0000, 0x04f7 }, /* 040b CYRILLIC CAPITAL LETTER TSHE */ { 0x04f9, 0x0000, 0x0000, 0x04f9 }, /* 040c CYRILLIC CAPITAL LETTER KJE */ { 0x04fb, 0x0000, 0x0000, 0x04fb }, /* 040d CYRILLIC CAPITAL LETTER I WITH GRAVE */ { 0x04fd, 0x0000, 0x0000, 0x04fd }, /* 040e CYRILLIC CAPITAL LETTER SHORT U */ { 0x04ff, 0x0000, 0x0000, 0x04ff }, /* 040f CYRILLIC CAPITAL LETTER DZHE */ { 0x0501, 0x0000, 0x0000, 0x0501 }, /* 0410 CYRILLIC CAPITAL LETTER A */ { 0x0503, 0x0000, 0x0000, 0x0503 }, /* 0411 CYRILLIC CAPITAL LETTER BE */ { 0x0505, 0x0000, 0x0000, 0x0505 }, /* 0412 CYRILLIC CAPITAL LETTER VE */ { 0x0507, 0x0000, 0x0000, 0x0507 }, /* 0413 CYRILLIC CAPITAL LETTER GHE */ { 0x0509, 0x0000, 0x0000, 0x0509 }, /* 0414 CYRILLIC CAPITAL LETTER DE */ { 0x050b, 0x0000, 0x0000, 0x050b }, /* 0415 CYRILLIC CAPITAL LETTER IE */ { 0x050d, 0x0000, 0x0000, 0x050d }, /* 0416 CYRILLIC CAPITAL LETTER ZHE */ { 0x050f, 0x0000, 0x0000, 0x050f }, /* 0417 CYRILLIC CAPITAL LETTER ZE */ { 0x0511, 0x0000, 0x0000, 0x0511 }, /* 0418 CYRILLIC CAPITAL LETTER I */ { 0x0513, 0x0000, 0x0000, 0x0513 }, /* 0419 CYRILLIC CAPITAL LETTER SHORT I */ { 0x0515, 0x0000, 0x0000, 0x0515 }, /* 041a CYRILLIC CAPITAL LETTER KA */ { 0x0517, 0x0000, 0x0000, 0x0517 }, /* 041b CYRILLIC CAPITAL LETTER EL */ { 0x0519, 0x0000, 0x0000, 0x0519 }, /* 041c CYRILLIC CAPITAL LETTER EM */ { 0x051b, 0x0000, 0x0000, 0x051b }, /* 041d CYRILLIC CAPITAL LETTER EN */ { 0x051d, 0x0000, 0x0000, 0x051d }, /* 041e CYRILLIC CAPITAL LETTER O */ { 0x051f, 0x0000, 0x0000, 0x051f }, /* 041f CYRILLIC CAPITAL LETTER PE */ { 0x0521, 0x0000, 0x0000, 0x0521 }, /* 0420 CYRILLIC CAPITAL LETTER ER */ { 0x0523, 0x0000, 0x0000, 0x0523 }, /* 0421 CYRILLIC CAPITAL LETTER ES */ { 0x0525, 0x0000, 0x0000, 0x0525 }, /* 0422 CYRILLIC CAPITAL LETTER TE */ { 0x0527, 0x0000, 0x0000, 0x0527 }, /* 0423 CYRILLIC CAPITAL LETTER U */ { 0x0529, 0x0000, 0x0000, 0x0529 }, /* 0424 CYRILLIC CAPITAL LETTER EF */ { 0x052b, 0x0000, 0x0000, 0x052b }, /* 0425 CYRILLIC CAPITAL LETTER HA */ { 0x052d, 0x0000, 0x0000, 0x052d }, /* 0426 CYRILLIC CAPITAL LETTER TSE */ { 0x052f, 0x0000, 0x0000, 0x052f }, /* 0427 CYRILLIC CAPITAL LETTER CHE */ { 0x0531, 0x0000, 0x0000, 0x0531 }, /* 0428 CYRILLIC CAPITAL LETTER SHA */ { 0x0533, 0x0000, 0x0000, 0x0533 }, /* 0429 CYRILLIC CAPITAL LETTER SHCHA */ { 0x0535, 0x0000, 0x0000, 0x0535 }, /* 042a CYRILLIC CAPITAL LETTER HARD SIGN */ { 0x0537, 0x0000, 0x0000, 0x0537 }, /* 042b CYRILLIC CAPITAL LETTER YERU */ { 0x0539, 0x0000, 0x0000, 0x0539 }, /* 042c CYRILLIC CAPITAL LETTER SOFT SIGN */ { 0x053b, 0x0000, 0x0000, 0x053b }, /* 042d CYRILLIC CAPITAL LETTER E */ { 0x053d, 0x0000, 0x0000, 0x053d }, /* 042e CYRILLIC CAPITAL LETTER YU */ { 0x053f, 0x0000, 0x0000, 0x053f }, /* 042f CYRILLIC CAPITAL LETTER YA */ { 0x0000, 0x0541, 0x0541, 0x0000 }, /* 0430 CYRILLIC SMALL LETTER A */ { 0x0000, 0x0543, 0x0543, 0x0000 }, /* 0431 CYRILLIC SMALL LETTER BE */ { 0x0000, 0x0545, 0x0545, 0x0000 }, /* 0432 CYRILLIC SMALL LETTER VE */ { 0x0000, 0x0547, 0x0547, 0x0000 }, /* 0433 CYRILLIC SMALL LETTER GHE */ { 0x0000, 0x0549, 0x0549, 0x0000 }, /* 0434 CYRILLIC SMALL LETTER DE */ { 0x0000, 0x054b, 0x054b, 0x0000 }, /* 0435 CYRILLIC SMALL LETTER IE */ { 0x0000, 0x054d, 0x054d, 0x0000 }, /* 0436 CYRILLIC SMALL LETTER ZHE */ { 0x0000, 0x054f, 0x054f, 0x0000 }, /* 0437 CYRILLIC SMALL LETTER ZE */ { 0x0000, 0x0551, 0x0551, 0x0000 }, /* 0438 CYRILLIC SMALL LETTER I */ { 0x0000, 0x0553, 0x0553, 0x0000 }, /* 0439 CYRILLIC SMALL LETTER SHORT I */ { 0x0000, 0x0555, 0x0555, 0x0000 }, /* 043a CYRILLIC SMALL LETTER KA */ { 0x0000, 0x0557, 0x0557, 0x0000 }, /* 043b CYRILLIC SMALL LETTER EL */ { 0x0000, 0x0559, 0x0559, 0x0000 }, /* 043c CYRILLIC SMALL LETTER EM */ { 0x0000, 0x055b, 0x055b, 0x0000 }, /* 043d CYRILLIC SMALL LETTER EN */ { 0x0000, 0x055d, 0x055d, 0x0000 }, /* 043e CYRILLIC SMALL LETTER O */ { 0x0000, 0x055f, 0x055f, 0x0000 }, /* 043f CYRILLIC SMALL LETTER PE */ { 0x0000, 0x0561, 0x0561, 0x0000 }, /* 0440 CYRILLIC SMALL LETTER ER */ { 0x0000, 0x0563, 0x0563, 0x0000 }, /* 0441 CYRILLIC SMALL LETTER ES */ { 0x0000, 0x0565, 0x0565, 0x0000 }, /* 0442 CYRILLIC SMALL LETTER TE */ { 0x0000, 0x0567, 0x0567, 0x0000 }, /* 0443 CYRILLIC SMALL LETTER U */ { 0x0000, 0x0569, 0x0569, 0x0000 }, /* 0444 CYRILLIC SMALL LETTER EF */ { 0x0000, 0x056b, 0x056b, 0x0000 }, /* 0445 CYRILLIC SMALL LETTER HA */ { 0x0000, 0x056d, 0x056d, 0x0000 }, /* 0446 CYRILLIC SMALL LETTER TSE */ { 0x0000, 0x056f, 0x056f, 0x0000 }, /* 0447 CYRILLIC SMALL LETTER CHE */ { 0x0000, 0x0571, 0x0571, 0x0000 }, /* 0448 CYRILLIC SMALL LETTER SHA */ { 0x0000, 0x0573, 0x0573, 0x0000 }, /* 0449 CYRILLIC SMALL LETTER SHCHA */ { 0x0000, 0x0575, 0x0575, 0x0000 }, /* 044a CYRILLIC SMALL LETTER HARD SIGN */ { 0x0000, 0x0577, 0x0577, 0x0000 }, /* 044b CYRILLIC SMALL LETTER YERU */ { 0x0000, 0x0579, 0x0579, 0x0000 }, /* 044c CYRILLIC SMALL LETTER SOFT SIGN */ { 0x0000, 0x057b, 0x057b, 0x0000 }, /* 044d CYRILLIC SMALL LETTER E */ { 0x0000, 0x057d, 0x057d, 0x0000 }, /* 044e CYRILLIC SMALL LETTER YU */ { 0x0000, 0x057f, 0x057f, 0x0000 }, /* 044f CYRILLIC SMALL LETTER YA */ { 0x0000, 0x0581, 0x0581, 0x0000 }, /* 0450 CYRILLIC SMALL LETTER IE WITH GRAVE */ { 0x0000, 0x0583, 0x0583, 0x0000 }, /* 0451 CYRILLIC SMALL LETTER IO */ { 0x0000, 0x0585, 0x0585, 0x0000 }, /* 0452 CYRILLIC SMALL LETTER DJE */ { 0x0000, 0x0587, 0x0587, 0x0000 }, /* 0453 CYRILLIC SMALL LETTER GJE */ { 0x0000, 0x0589, 0x0589, 0x0000 }, /* 0454 CYRILLIC SMALL LETTER UKRAINIAN IE */ { 0x0000, 0x058b, 0x058b, 0x0000 }, /* 0455 CYRILLIC SMALL LETTER DZE */ { 0x0000, 0x058d, 0x058d, 0x0000 }, /* 0456 CYRILLIC SMALL LETTER BYELORUSSIAN-UKRA */ { 0x0000, 0x058f, 0x058f, 0x0000 }, /* 0457 CYRILLIC SMALL LETTER YI */ { 0x0000, 0x0591, 0x0591, 0x0000 }, /* 0458 CYRILLIC SMALL LETTER JE */ { 0x0000, 0x0593, 0x0593, 0x0000 }, /* 0459 CYRILLIC SMALL LETTER LJE */ { 0x0000, 0x0595, 0x0595, 0x0000 }, /* 045a CYRILLIC SMALL LETTER NJE */ { 0x0000, 0x0597, 0x0597, 0x0000 }, /* 045b CYRILLIC SMALL LETTER TSHE */ { 0x0000, 0x0599, 0x0599, 0x0000 }, /* 045c CYRILLIC SMALL LETTER KJE */ { 0x0000, 0x059b, 0x059b, 0x0000 }, /* 045d CYRILLIC SMALL LETTER I WITH GRAVE */ { 0x0000, 0x059d, 0x059d, 0x0000 }, /* 045e CYRILLIC SMALL LETTER SHORT U */ { 0x0000, 0x059f, 0x059f, 0x0000 }, /* 045f CYRILLIC SMALL LETTER DZHE */ { 0x05a1, 0x0000, 0x0000, 0x05a1 }, /* 0460 CYRILLIC CAPITAL LETTER OMEGA */ { 0x0000, 0x05a3, 0x05a3, 0x0000 }, /* 0461 CYRILLIC SMALL LETTER OMEGA */ { 0x05a5, 0x0000, 0x0000, 0x05a5 }, /* 0462 CYRILLIC CAPITAL LETTER YAT */ { 0x0000, 0x05a7, 0x05a7, 0x0000 }, /* 0463 CYRILLIC SMALL LETTER YAT */ { 0x05a9, 0x0000, 0x0000, 0x05a9 }, /* 0464 CYRILLIC CAPITAL LETTER IOTIFIED E */ { 0x0000, 0x05ab, 0x05ab, 0x0000 }, /* 0465 CYRILLIC SMALL LETTER IOTIFIED E */ { 0x05ad, 0x0000, 0x0000, 0x05ad }, /* 0466 CYRILLIC CAPITAL LETTER LITTLE YUS */ { 0x0000, 0x05af, 0x05af, 0x0000 }, /* 0467 CYRILLIC SMALL LETTER LITTLE YUS */ { 0x05b1, 0x0000, 0x0000, 0x05b1 }, /* 0468 CYRILLIC CAPITAL LETTER IOTIFIED LITTLE */ { 0x0000, 0x05b3, 0x05b3, 0x0000 }, /* 0469 CYRILLIC SMALL LETTER IOTIFIED LITTLE Y */ { 0x05b5, 0x0000, 0x0000, 0x05b5 }, /* 046a CYRILLIC CAPITAL LETTER BIG YUS */ { 0x0000, 0x05b7, 0x05b7, 0x0000 }, /* 046b CYRILLIC SMALL LETTER BIG YUS */ { 0x05b9, 0x0000, 0x0000, 0x05b9 }, /* 046c CYRILLIC CAPITAL LETTER IOTIFIED BIG YU */ { 0x0000, 0x05bb, 0x05bb, 0x0000 }, /* 046d CYRILLIC SMALL LETTER IOTIFIED BIG YUS */ { 0x05bd, 0x0000, 0x0000, 0x05bd }, /* 046e CYRILLIC CAPITAL LETTER KSI */ { 0x0000, 0x05bf, 0x05bf, 0x0000 }, /* 046f CYRILLIC SMALL LETTER KSI */ { 0x05c1, 0x0000, 0x0000, 0x05c1 }, /* 0470 CYRILLIC CAPITAL LETTER PSI */ { 0x0000, 0x05c3, 0x05c3, 0x0000 }, /* 0471 CYRILLIC SMALL LETTER PSI */ { 0x05c5, 0x0000, 0x0000, 0x05c5 }, /* 0472 CYRILLIC CAPITAL LETTER FITA */ { 0x0000, 0x05c7, 0x05c7, 0x0000 }, /* 0473 CYRILLIC SMALL LETTER FITA */ { 0x05c9, 0x0000, 0x0000, 0x05c9 }, /* 0474 CYRILLIC CAPITAL LETTER IZHITSA */ { 0x0000, 0x05cb, 0x05cb, 0x0000 }, /* 0475 CYRILLIC SMALL LETTER IZHITSA */ { 0x05cd, 0x0000, 0x0000, 0x05cd }, /* 0476 CYRILLIC CAPITAL LETTER IZHITSA WITH DO */ { 0x0000, 0x05cf, 0x05cf, 0x0000 }, /* 0477 CYRILLIC SMALL LETTER IZHITSA WITH DOUB */ { 0x05d1, 0x0000, 0x0000, 0x05d1 }, /* 0478 CYRILLIC CAPITAL LETTER UK */ { 0x0000, 0x05d3, 0x05d3, 0x0000 }, /* 0479 CYRILLIC SMALL LETTER UK */ { 0x05d5, 0x0000, 0x0000, 0x05d5 }, /* 047a CYRILLIC CAPITAL LETTER ROUND OMEGA */ { 0x0000, 0x05d7, 0x05d7, 0x0000 }, /* 047b CYRILLIC SMALL LETTER ROUND OMEGA */ { 0x05d9, 0x0000, 0x0000, 0x05d9 }, /* 047c CYRILLIC CAPITAL LETTER OMEGA WITH TITL */ { 0x0000, 0x05db, 0x05db, 0x0000 }, /* 047d CYRILLIC SMALL LETTER OMEGA WITH TITLO */ { 0x05dd, 0x0000, 0x0000, 0x05dd }, /* 047e CYRILLIC CAPITAL LETTER OT */ { 0x0000, 0x05df, 0x05df, 0x0000 } /* 047f CYRILLIC SMALL LETTER OT */ }; static case_table case_pg_009[128] = { { 0x05e1, 0x0000, 0x0000, 0x05e1 }, /* 0480 CYRILLIC CAPITAL LETTER KOPPA */ { 0x0000, 0x05e3, 0x05e3, 0x0000 }, /* 0481 CYRILLIC SMALL LETTER KOPPA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0482 CYRILLIC THOUSANDS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0483 COMBINING CYRILLIC TITLO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0484 COMBINING CYRILLIC PALATALIZATION */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0485 COMBINING CYRILLIC DASIA PNEUMATA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0486 COMBINING CYRILLIC PSILI PNEUMATA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0487 COMBINING CYRILLIC POKRYTIE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0488 COMBINING CYRILLIC HUNDRED THOUSANDS SI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0489 COMBINING CYRILLIC MILLIONS SIGN */ { 0x05e5, 0x0000, 0x0000, 0x05e5 }, /* 048a CYRILLIC CAPITAL LETTER SHORT I WITH TA */ { 0x0000, 0x05e7, 0x05e7, 0x0000 }, /* 048b CYRILLIC SMALL LETTER SHORT I WITH TAIL */ { 0x05e9, 0x0000, 0x0000, 0x05e9 }, /* 048c CYRILLIC CAPITAL LETTER SEMISOFT SIGN */ { 0x0000, 0x05eb, 0x05eb, 0x0000 }, /* 048d CYRILLIC SMALL LETTER SEMISOFT SIGN */ { 0x05ed, 0x0000, 0x0000, 0x05ed }, /* 048e CYRILLIC CAPITAL LETTER ER WITH TICK */ { 0x0000, 0x05ef, 0x05ef, 0x0000 }, /* 048f CYRILLIC SMALL LETTER ER WITH TICK */ { 0x05f1, 0x0000, 0x0000, 0x05f1 }, /* 0490 CYRILLIC CAPITAL LETTER GHE WITH UPTURN */ { 0x0000, 0x05f3, 0x05f3, 0x0000 }, /* 0491 CYRILLIC SMALL LETTER GHE WITH UPTURN */ { 0x05f5, 0x0000, 0x0000, 0x05f5 }, /* 0492 CYRILLIC CAPITAL LETTER GHE WITH STROKE */ { 0x0000, 0x05f7, 0x05f7, 0x0000 }, /* 0493 CYRILLIC SMALL LETTER GHE WITH STROKE */ { 0x05f9, 0x0000, 0x0000, 0x05f9 }, /* 0494 CYRILLIC CAPITAL LETTER GHE WITH MIDDLE */ { 0x0000, 0x05fb, 0x05fb, 0x0000 }, /* 0495 CYRILLIC SMALL LETTER GHE WITH MIDDLE H */ { 0x05fd, 0x0000, 0x0000, 0x05fd }, /* 0496 CYRILLIC CAPITAL LETTER ZHE WITH DESCEN */ { 0x0000, 0x05ff, 0x05ff, 0x0000 }, /* 0497 CYRILLIC SMALL LETTER ZHE WITH DESCENDE */ { 0x0601, 0x0000, 0x0000, 0x0601 }, /* 0498 CYRILLIC CAPITAL LETTER ZE WITH DESCEND */ { 0x0000, 0x0603, 0x0603, 0x0000 }, /* 0499 CYRILLIC SMALL LETTER ZE WITH DESCENDER */ { 0x0605, 0x0000, 0x0000, 0x0605 }, /* 049a CYRILLIC CAPITAL LETTER KA WITH DESCEND */ { 0x0000, 0x0607, 0x0607, 0x0000 }, /* 049b CYRILLIC SMALL LETTER KA WITH DESCENDER */ { 0x0609, 0x0000, 0x0000, 0x0609 }, /* 049c CYRILLIC CAPITAL LETTER KA WITH VERTICA */ { 0x0000, 0x060b, 0x060b, 0x0000 }, /* 049d CYRILLIC SMALL LETTER KA WITH VERTICAL */ { 0x060d, 0x0000, 0x0000, 0x060d }, /* 049e CYRILLIC CAPITAL LETTER KA WITH STROKE */ { 0x0000, 0x060f, 0x060f, 0x0000 }, /* 049f CYRILLIC SMALL LETTER KA WITH STROKE */ { 0x0611, 0x0000, 0x0000, 0x0611 }, /* 04a0 CYRILLIC CAPITAL LETTER BASHKIR KA */ { 0x0000, 0x0613, 0x0613, 0x0000 }, /* 04a1 CYRILLIC SMALL LETTER BASHKIR KA */ { 0x0615, 0x0000, 0x0000, 0x0615 }, /* 04a2 CYRILLIC CAPITAL LETTER EN WITH DESCEND */ { 0x0000, 0x0617, 0x0617, 0x0000 }, /* 04a3 CYRILLIC SMALL LETTER EN WITH DESCENDER */ { 0x0619, 0x0000, 0x0000, 0x0619 }, /* 04a4 CYRILLIC CAPITAL LIGATURE EN GHE */ { 0x0000, 0x061b, 0x061b, 0x0000 }, /* 04a5 CYRILLIC SMALL LIGATURE EN GHE */ { 0x061d, 0x0000, 0x0000, 0x061d }, /* 04a6 CYRILLIC CAPITAL LETTER PE WITH MIDDLE */ { 0x0000, 0x061f, 0x061f, 0x0000 }, /* 04a7 CYRILLIC SMALL LETTER PE WITH MIDDLE HO */ { 0x0621, 0x0000, 0x0000, 0x0621 }, /* 04a8 CYRILLIC CAPITAL LETTER ABKHASIAN HA */ { 0x0000, 0x0623, 0x0623, 0x0000 }, /* 04a9 CYRILLIC SMALL LETTER ABKHASIAN HA */ { 0x0625, 0x0000, 0x0000, 0x0625 }, /* 04aa CYRILLIC CAPITAL LETTER ES WITH DESCEND */ { 0x0000, 0x0627, 0x0627, 0x0000 }, /* 04ab CYRILLIC SMALL LETTER ES WITH DESCENDER */ { 0x0629, 0x0000, 0x0000, 0x0629 }, /* 04ac CYRILLIC CAPITAL LETTER TE WITH DESCEND */ { 0x0000, 0x062b, 0x062b, 0x0000 }, /* 04ad CYRILLIC SMALL LETTER TE WITH DESCENDER */ { 0x062d, 0x0000, 0x0000, 0x062d }, /* 04ae CYRILLIC CAPITAL LETTER STRAIGHT U */ { 0x0000, 0x062f, 0x062f, 0x0000 }, /* 04af CYRILLIC SMALL LETTER STRAIGHT U */ { 0x0631, 0x0000, 0x0000, 0x0631 }, /* 04b0 CYRILLIC CAPITAL LETTER STRAIGHT U WITH */ { 0x0000, 0x0633, 0x0633, 0x0000 }, /* 04b1 CYRILLIC SMALL LETTER STRAIGHT U WITH S */ { 0x0635, 0x0000, 0x0000, 0x0635 }, /* 04b2 CYRILLIC CAPITAL LETTER HA WITH DESCEND */ { 0x0000, 0x0637, 0x0637, 0x0000 }, /* 04b3 CYRILLIC SMALL LETTER HA WITH DESCENDER */ { 0x0639, 0x0000, 0x0000, 0x0639 }, /* 04b4 CYRILLIC CAPITAL LIGATURE TE TSE */ { 0x0000, 0x063b, 0x063b, 0x0000 }, /* 04b5 CYRILLIC SMALL LIGATURE TE TSE */ { 0x063d, 0x0000, 0x0000, 0x063d }, /* 04b6 CYRILLIC CAPITAL LETTER CHE WITH DESCEN */ { 0x0000, 0x063f, 0x063f, 0x0000 }, /* 04b7 CYRILLIC SMALL LETTER CHE WITH DESCENDE */ { 0x0641, 0x0000, 0x0000, 0x0641 }, /* 04b8 CYRILLIC CAPITAL LETTER CHE WITH VERTIC */ { 0x0000, 0x0643, 0x0643, 0x0000 }, /* 04b9 CYRILLIC SMALL LETTER CHE WITH VERTICAL */ { 0x0645, 0x0000, 0x0000, 0x0645 }, /* 04ba CYRILLIC CAPITAL LETTER SHHA */ { 0x0000, 0x0647, 0x0647, 0x0000 }, /* 04bb CYRILLIC SMALL LETTER SHHA */ { 0x0649, 0x0000, 0x0000, 0x0649 }, /* 04bc CYRILLIC CAPITAL LETTER ABKHASIAN CHE */ { 0x0000, 0x064b, 0x064b, 0x0000 }, /* 04bd CYRILLIC SMALL LETTER ABKHASIAN CHE */ { 0x064d, 0x0000, 0x0000, 0x064d }, /* 04be CYRILLIC CAPITAL LETTER ABKHASIAN CHE W */ { 0x0000, 0x064f, 0x064f, 0x0000 }, /* 04bf CYRILLIC SMALL LETTER ABKHASIAN CHE WIT */ { 0x0651, 0x0000, 0x0000, 0x0651 }, /* 04c0 CYRILLIC LETTER PALOCHKA */ { 0x0653, 0x0000, 0x0000, 0x0653 }, /* 04c1 CYRILLIC CAPITAL LETTER ZHE WITH BREVE */ { 0x0000, 0x0655, 0x0655, 0x0000 }, /* 04c2 CYRILLIC SMALL LETTER ZHE WITH BREVE */ { 0x0657, 0x0000, 0x0000, 0x0657 }, /* 04c3 CYRILLIC CAPITAL LETTER KA WITH HOOK */ { 0x0000, 0x0659, 0x0659, 0x0000 }, /* 04c4 CYRILLIC SMALL LETTER KA WITH HOOK */ { 0x065b, 0x0000, 0x0000, 0x065b }, /* 04c5 CYRILLIC CAPITAL LETTER EL WITH TAIL */ { 0x0000, 0x065d, 0x065d, 0x0000 }, /* 04c6 CYRILLIC SMALL LETTER EL WITH TAIL */ { 0x065f, 0x0000, 0x0000, 0x065f }, /* 04c7 CYRILLIC CAPITAL LETTER EN WITH HOOK */ { 0x0000, 0x0661, 0x0661, 0x0000 }, /* 04c8 CYRILLIC SMALL LETTER EN WITH HOOK */ { 0x0663, 0x0000, 0x0000, 0x0663 }, /* 04c9 CYRILLIC CAPITAL LETTER EN WITH TAIL */ { 0x0000, 0x0665, 0x0665, 0x0000 }, /* 04ca CYRILLIC SMALL LETTER EN WITH TAIL */ { 0x0667, 0x0000, 0x0000, 0x0667 }, /* 04cb CYRILLIC CAPITAL LETTER KHAKASSIAN CHE */ { 0x0000, 0x0669, 0x0669, 0x0000 }, /* 04cc CYRILLIC SMALL LETTER KHAKASSIAN CHE */ { 0x066b, 0x0000, 0x0000, 0x066b }, /* 04cd CYRILLIC CAPITAL LETTER EM WITH TAIL */ { 0x0000, 0x066d, 0x066d, 0x0000 }, /* 04ce CYRILLIC SMALL LETTER EM WITH TAIL */ { 0x0000, 0x066f, 0x066f, 0x0000 }, /* 04cf CYRILLIC SMALL LETTER PALOCHKA */ { 0x0671, 0x0000, 0x0000, 0x0671 }, /* 04d0 CYRILLIC CAPITAL LETTER A WITH BREVE */ { 0x0000, 0x0673, 0x0673, 0x0000 }, /* 04d1 CYRILLIC SMALL LETTER A WITH BREVE */ { 0x0675, 0x0000, 0x0000, 0x0675 }, /* 04d2 CYRILLIC CAPITAL LETTER A WITH DIAERESI */ { 0x0000, 0x0677, 0x0677, 0x0000 }, /* 04d3 CYRILLIC SMALL LETTER A WITH DIAERESIS */ { 0x0679, 0x0000, 0x0000, 0x0679 }, /* 04d4 CYRILLIC CAPITAL LIGATURE A IE */ { 0x0000, 0x067b, 0x067b, 0x0000 }, /* 04d5 CYRILLIC SMALL LIGATURE A IE */ { 0x067d, 0x0000, 0x0000, 0x067d }, /* 04d6 CYRILLIC CAPITAL LETTER IE WITH BREVE */ { 0x0000, 0x067f, 0x067f, 0x0000 }, /* 04d7 CYRILLIC SMALL LETTER IE WITH BREVE */ { 0x0681, 0x0000, 0x0000, 0x0681 }, /* 04d8 CYRILLIC CAPITAL LETTER SCHWA */ { 0x0000, 0x0683, 0x0683, 0x0000 }, /* 04d9 CYRILLIC SMALL LETTER SCHWA */ { 0x0685, 0x0000, 0x0000, 0x0685 }, /* 04da CYRILLIC CAPITAL LETTER SCHWA WITH DIAE */ { 0x0000, 0x0687, 0x0687, 0x0000 }, /* 04db CYRILLIC SMALL LETTER SCHWA WITH DIAERE */ { 0x0689, 0x0000, 0x0000, 0x0689 }, /* 04dc CYRILLIC CAPITAL LETTER ZHE WITH DIAERE */ { 0x0000, 0x068b, 0x068b, 0x0000 }, /* 04dd CYRILLIC SMALL LETTER ZHE WITH DIAERESI */ { 0x068d, 0x0000, 0x0000, 0x068d }, /* 04de CYRILLIC CAPITAL LETTER ZE WITH DIAERES */ { 0x0000, 0x068f, 0x068f, 0x0000 }, /* 04df CYRILLIC SMALL LETTER ZE WITH DIAERESIS */ { 0x0691, 0x0000, 0x0000, 0x0691 }, /* 04e0 CYRILLIC CAPITAL LETTER ABKHASIAN DZE */ { 0x0000, 0x0693, 0x0693, 0x0000 }, /* 04e1 CYRILLIC SMALL LETTER ABKHASIAN DZE */ { 0x0695, 0x0000, 0x0000, 0x0695 }, /* 04e2 CYRILLIC CAPITAL LETTER I WITH MACRON */ { 0x0000, 0x0697, 0x0697, 0x0000 }, /* 04e3 CYRILLIC SMALL LETTER I WITH MACRON */ { 0x0699, 0x0000, 0x0000, 0x0699 }, /* 04e4 CYRILLIC CAPITAL LETTER I WITH DIAERESI */ { 0x0000, 0x069b, 0x069b, 0x0000 }, /* 04e5 CYRILLIC SMALL LETTER I WITH DIAERESIS */ { 0x069d, 0x0000, 0x0000, 0x069d }, /* 04e6 CYRILLIC CAPITAL LETTER O WITH DIAERESI */ { 0x0000, 0x069f, 0x069f, 0x0000 }, /* 04e7 CYRILLIC SMALL LETTER O WITH DIAERESIS */ { 0x06a1, 0x0000, 0x0000, 0x06a1 }, /* 04e8 CYRILLIC CAPITAL LETTER BARRED O */ { 0x0000, 0x06a3, 0x06a3, 0x0000 }, /* 04e9 CYRILLIC SMALL LETTER BARRED O */ { 0x06a5, 0x0000, 0x0000, 0x06a5 }, /* 04ea CYRILLIC CAPITAL LETTER BARRED O WITH D */ { 0x0000, 0x06a7, 0x06a7, 0x0000 }, /* 04eb CYRILLIC SMALL LETTER BARRED O WITH DIA */ { 0x06a9, 0x0000, 0x0000, 0x06a9 }, /* 04ec CYRILLIC CAPITAL LETTER E WITH DIAERESI */ { 0x0000, 0x06ab, 0x06ab, 0x0000 }, /* 04ed CYRILLIC SMALL LETTER E WITH DIAERESIS */ { 0x06ad, 0x0000, 0x0000, 0x06ad }, /* 04ee CYRILLIC CAPITAL LETTER U WITH MACRON */ { 0x0000, 0x06af, 0x06af, 0x0000 }, /* 04ef CYRILLIC SMALL LETTER U WITH MACRON */ { 0x06b1, 0x0000, 0x0000, 0x06b1 }, /* 04f0 CYRILLIC CAPITAL LETTER U WITH DIAERESI */ { 0x0000, 0x06b3, 0x06b3, 0x0000 }, /* 04f1 CYRILLIC SMALL LETTER U WITH DIAERESIS */ { 0x06b5, 0x0000, 0x0000, 0x06b5 }, /* 04f2 CYRILLIC CAPITAL LETTER U WITH DOUBLE A */ { 0x0000, 0x06b7, 0x06b7, 0x0000 }, /* 04f3 CYRILLIC SMALL LETTER U WITH DOUBLE ACU */ { 0x06b9, 0x0000, 0x0000, 0x06b9 }, /* 04f4 CYRILLIC CAPITAL LETTER CHE WITH DIAERE */ { 0x0000, 0x06bb, 0x06bb, 0x0000 }, /* 04f5 CYRILLIC SMALL LETTER CHE WITH DIAERESI */ { 0x06bd, 0x0000, 0x0000, 0x06bd }, /* 04f6 CYRILLIC CAPITAL LETTER GHE WITH DESCEN */ { 0x0000, 0x06bf, 0x06bf, 0x0000 }, /* 04f7 CYRILLIC SMALL LETTER GHE WITH DESCENDE */ { 0x06c1, 0x0000, 0x0000, 0x06c1 }, /* 04f8 CYRILLIC CAPITAL LETTER YERU WITH DIAER */ { 0x0000, 0x06c3, 0x06c3, 0x0000 }, /* 04f9 CYRILLIC SMALL LETTER YERU WITH DIAERES */ { 0x06c5, 0x0000, 0x0000, 0x06c5 }, /* 04fa CYRILLIC CAPITAL LETTER GHE WITH STROKE */ { 0x0000, 0x06c7, 0x06c7, 0x0000 }, /* 04fb CYRILLIC SMALL LETTER GHE WITH STROKE A */ { 0x06c9, 0x0000, 0x0000, 0x06c9 }, /* 04fc CYRILLIC CAPITAL LETTER HA WITH HOOK */ { 0x0000, 0x06cb, 0x06cb, 0x0000 }, /* 04fd CYRILLIC SMALL LETTER HA WITH HOOK */ { 0x06cd, 0x0000, 0x0000, 0x06cd }, /* 04fe CYRILLIC CAPITAL LETTER HA WITH STROKE */ { 0x0000, 0x06cf, 0x06cf, 0x0000 } /* 04ff CYRILLIC SMALL LETTER HA WITH STROKE */ }; static case_table case_pg_00a[128] = { { 0x06d1, 0x0000, 0x0000, 0x06d1 }, /* 0500 CYRILLIC CAPITAL LETTER KOMI DE */ { 0x0000, 0x06d3, 0x06d3, 0x0000 }, /* 0501 CYRILLIC SMALL LETTER KOMI DE */ { 0x06d5, 0x0000, 0x0000, 0x06d5 }, /* 0502 CYRILLIC CAPITAL LETTER KOMI DJE */ { 0x0000, 0x06d7, 0x06d7, 0x0000 }, /* 0503 CYRILLIC SMALL LETTER KOMI DJE */ { 0x06d9, 0x0000, 0x0000, 0x06d9 }, /* 0504 CYRILLIC CAPITAL LETTER KOMI ZJE */ { 0x0000, 0x06db, 0x06db, 0x0000 }, /* 0505 CYRILLIC SMALL LETTER KOMI ZJE */ { 0x06dd, 0x0000, 0x0000, 0x06dd }, /* 0506 CYRILLIC CAPITAL LETTER KOMI DZJE */ { 0x0000, 0x06df, 0x06df, 0x0000 }, /* 0507 CYRILLIC SMALL LETTER KOMI DZJE */ { 0x06e1, 0x0000, 0x0000, 0x06e1 }, /* 0508 CYRILLIC CAPITAL LETTER KOMI LJE */ { 0x0000, 0x06e3, 0x06e3, 0x0000 }, /* 0509 CYRILLIC SMALL LETTER KOMI LJE */ { 0x06e5, 0x0000, 0x0000, 0x06e5 }, /* 050a CYRILLIC CAPITAL LETTER KOMI NJE */ { 0x0000, 0x06e7, 0x06e7, 0x0000 }, /* 050b CYRILLIC SMALL LETTER KOMI NJE */ { 0x06e9, 0x0000, 0x0000, 0x06e9 }, /* 050c CYRILLIC CAPITAL LETTER KOMI SJE */ { 0x0000, 0x06eb, 0x06eb, 0x0000 }, /* 050d CYRILLIC SMALL LETTER KOMI SJE */ { 0x06ed, 0x0000, 0x0000, 0x06ed }, /* 050e CYRILLIC CAPITAL LETTER KOMI TJE */ { 0x0000, 0x06ef, 0x06ef, 0x0000 }, /* 050f CYRILLIC SMALL LETTER KOMI TJE */ { 0x06f1, 0x0000, 0x0000, 0x06f1 }, /* 0510 CYRILLIC CAPITAL LETTER REVERSED ZE */ { 0x0000, 0x06f3, 0x06f3, 0x0000 }, /* 0511 CYRILLIC SMALL LETTER REVERSED ZE */ { 0x06f5, 0x0000, 0x0000, 0x06f5 }, /* 0512 CYRILLIC CAPITAL LETTER EL WITH HOOK */ { 0x0000, 0x06f7, 0x06f7, 0x0000 }, /* 0513 CYRILLIC SMALL LETTER EL WITH HOOK */ { 0x06f9, 0x0000, 0x0000, 0x06f9 }, /* 0514 CYRILLIC CAPITAL LETTER LHA */ { 0x0000, 0x06fb, 0x06fb, 0x0000 }, /* 0515 CYRILLIC SMALL LETTER LHA */ { 0x06fd, 0x0000, 0x0000, 0x06fd }, /* 0516 CYRILLIC CAPITAL LETTER RHA */ { 0x0000, 0x06ff, 0x06ff, 0x0000 }, /* 0517 CYRILLIC SMALL LETTER RHA */ { 0x0701, 0x0000, 0x0000, 0x0701 }, /* 0518 CYRILLIC CAPITAL LETTER YAE */ { 0x0000, 0x0703, 0x0703, 0x0000 }, /* 0519 CYRILLIC SMALL LETTER YAE */ { 0x0705, 0x0000, 0x0000, 0x0705 }, /* 051a CYRILLIC CAPITAL LETTER QA */ { 0x0000, 0x0707, 0x0707, 0x0000 }, /* 051b CYRILLIC SMALL LETTER QA */ { 0x0709, 0x0000, 0x0000, 0x0709 }, /* 051c CYRILLIC CAPITAL LETTER WE */ { 0x0000, 0x070b, 0x070b, 0x0000 }, /* 051d CYRILLIC SMALL LETTER WE */ { 0x070d, 0x0000, 0x0000, 0x070d }, /* 051e CYRILLIC CAPITAL LETTER ALEUT KA */ { 0x0000, 0x070f, 0x070f, 0x0000 }, /* 051f CYRILLIC SMALL LETTER ALEUT KA */ { 0x0711, 0x0000, 0x0000, 0x0711 }, /* 0520 CYRILLIC CAPITAL LETTER EL WITH MIDDLE */ { 0x0000, 0x0713, 0x0713, 0x0000 }, /* 0521 CYRILLIC SMALL LETTER EL WITH MIDDLE HO */ { 0x0715, 0x0000, 0x0000, 0x0715 }, /* 0522 CYRILLIC CAPITAL LETTER EN WITH MIDDLE */ { 0x0000, 0x0717, 0x0717, 0x0000 }, /* 0523 CYRILLIC SMALL LETTER EN WITH MIDDLE HO */ { 0x0719, 0x0000, 0x0000, 0x0719 }, /* 0524 CYRILLIC CAPITAL LETTER PE WITH DESCEND */ { 0x0000, 0x071b, 0x071b, 0x0000 }, /* 0525 CYRILLIC SMALL LETTER PE WITH DESCENDER */ { 0x071d, 0x0000, 0x0000, 0x071d }, /* 0526 CYRILLIC CAPITAL LETTER SHHA WITH DESCE */ { 0x0000, 0x071f, 0x071f, 0x0000 }, /* 0527 CYRILLIC SMALL LETTER SHHA WITH DESCEND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0528 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0529 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 052a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 052b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 052c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 052d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 052e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 052f (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0530 (null) */ { 0x0721, 0x0000, 0x0000, 0x0721 }, /* 0531 ARMENIAN CAPITAL LETTER AYB */ { 0x0723, 0x0000, 0x0000, 0x0723 }, /* 0532 ARMENIAN CAPITAL LETTER BEN */ { 0x0725, 0x0000, 0x0000, 0x0725 }, /* 0533 ARMENIAN CAPITAL LETTER GIM */ { 0x0727, 0x0000, 0x0000, 0x0727 }, /* 0534 ARMENIAN CAPITAL LETTER DA */ { 0x0729, 0x0000, 0x0000, 0x0729 }, /* 0535 ARMENIAN CAPITAL LETTER ECH */ { 0x072b, 0x0000, 0x0000, 0x072b }, /* 0536 ARMENIAN CAPITAL LETTER ZA */ { 0x072d, 0x0000, 0x0000, 0x072d }, /* 0537 ARMENIAN CAPITAL LETTER EH */ { 0x072f, 0x0000, 0x0000, 0x072f }, /* 0538 ARMENIAN CAPITAL LETTER ET */ { 0x0731, 0x0000, 0x0000, 0x0731 }, /* 0539 ARMENIAN CAPITAL LETTER TO */ { 0x0733, 0x0000, 0x0000, 0x0733 }, /* 053a ARMENIAN CAPITAL LETTER ZHE */ { 0x0735, 0x0000, 0x0000, 0x0735 }, /* 053b ARMENIAN CAPITAL LETTER INI */ { 0x0737, 0x0000, 0x0000, 0x0737 }, /* 053c ARMENIAN CAPITAL LETTER LIWN */ { 0x0739, 0x0000, 0x0000, 0x0739 }, /* 053d ARMENIAN CAPITAL LETTER XEH */ { 0x073b, 0x0000, 0x0000, 0x073b }, /* 053e ARMENIAN CAPITAL LETTER CA */ { 0x073d, 0x0000, 0x0000, 0x073d }, /* 053f ARMENIAN CAPITAL LETTER KEN */ { 0x073f, 0x0000, 0x0000, 0x073f }, /* 0540 ARMENIAN CAPITAL LETTER HO */ { 0x0741, 0x0000, 0x0000, 0x0741 }, /* 0541 ARMENIAN CAPITAL LETTER JA */ { 0x0743, 0x0000, 0x0000, 0x0743 }, /* 0542 ARMENIAN CAPITAL LETTER GHAD */ { 0x0745, 0x0000, 0x0000, 0x0745 }, /* 0543 ARMENIAN CAPITAL LETTER CHEH */ { 0x0747, 0x0000, 0x0000, 0x0747 }, /* 0544 ARMENIAN CAPITAL LETTER MEN */ { 0x0749, 0x0000, 0x0000, 0x0749 }, /* 0545 ARMENIAN CAPITAL LETTER YI */ { 0x074b, 0x0000, 0x0000, 0x074b }, /* 0546 ARMENIAN CAPITAL LETTER NOW */ { 0x074d, 0x0000, 0x0000, 0x074d }, /* 0547 ARMENIAN CAPITAL LETTER SHA */ { 0x074f, 0x0000, 0x0000, 0x074f }, /* 0548 ARMENIAN CAPITAL LETTER VO */ { 0x0751, 0x0000, 0x0000, 0x0751 }, /* 0549 ARMENIAN CAPITAL LETTER CHA */ { 0x0753, 0x0000, 0x0000, 0x0753 }, /* 054a ARMENIAN CAPITAL LETTER PEH */ { 0x0755, 0x0000, 0x0000, 0x0755 }, /* 054b ARMENIAN CAPITAL LETTER JHEH */ { 0x0757, 0x0000, 0x0000, 0x0757 }, /* 054c ARMENIAN CAPITAL LETTER RA */ { 0x0759, 0x0000, 0x0000, 0x0759 }, /* 054d ARMENIAN CAPITAL LETTER SEH */ { 0x075b, 0x0000, 0x0000, 0x075b }, /* 054e ARMENIAN CAPITAL LETTER VEW */ { 0x075d, 0x0000, 0x0000, 0x075d }, /* 054f ARMENIAN CAPITAL LETTER TIWN */ { 0x075f, 0x0000, 0x0000, 0x075f }, /* 0550 ARMENIAN CAPITAL LETTER REH */ { 0x0761, 0x0000, 0x0000, 0x0761 }, /* 0551 ARMENIAN CAPITAL LETTER CO */ { 0x0763, 0x0000, 0x0000, 0x0763 }, /* 0552 ARMENIAN CAPITAL LETTER YIWN */ { 0x0765, 0x0000, 0x0000, 0x0765 }, /* 0553 ARMENIAN CAPITAL LETTER PIWR */ { 0x0767, 0x0000, 0x0000, 0x0767 }, /* 0554 ARMENIAN CAPITAL LETTER KEH */ { 0x0769, 0x0000, 0x0000, 0x0769 }, /* 0555 ARMENIAN CAPITAL LETTER OH */ { 0x076b, 0x0000, 0x0000, 0x076b }, /* 0556 ARMENIAN CAPITAL LETTER FEH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0557 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0558 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0559 ARMENIAN MODIFIER LETTER LEFT HALF RING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 055a ARMENIAN APOSTROPHE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 055b ARMENIAN EMPHASIS MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 055c ARMENIAN EXCLAMATION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 055d ARMENIAN COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 055e ARMENIAN QUESTION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 055f ARMENIAN ABBREVIATION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0560 (null) */ { 0x0000, 0x076d, 0x076d, 0x0000 }, /* 0561 ARMENIAN SMALL LETTER AYB */ { 0x0000, 0x076f, 0x076f, 0x0000 }, /* 0562 ARMENIAN SMALL LETTER BEN */ { 0x0000, 0x0771, 0x0771, 0x0000 }, /* 0563 ARMENIAN SMALL LETTER GIM */ { 0x0000, 0x0773, 0x0773, 0x0000 }, /* 0564 ARMENIAN SMALL LETTER DA */ { 0x0000, 0x0775, 0x0775, 0x0000 }, /* 0565 ARMENIAN SMALL LETTER ECH */ { 0x0000, 0x0777, 0x0777, 0x0000 }, /* 0566 ARMENIAN SMALL LETTER ZA */ { 0x0000, 0x0779, 0x0779, 0x0000 }, /* 0567 ARMENIAN SMALL LETTER EH */ { 0x0000, 0x077b, 0x077b, 0x0000 }, /* 0568 ARMENIAN SMALL LETTER ET */ { 0x0000, 0x077d, 0x077d, 0x0000 }, /* 0569 ARMENIAN SMALL LETTER TO */ { 0x0000, 0x077f, 0x077f, 0x0000 }, /* 056a ARMENIAN SMALL LETTER ZHE */ { 0x0000, 0x0781, 0x0781, 0x0000 }, /* 056b ARMENIAN SMALL LETTER INI */ { 0x0000, 0x0783, 0x0783, 0x0000 }, /* 056c ARMENIAN SMALL LETTER LIWN */ { 0x0000, 0x0785, 0x0785, 0x0000 }, /* 056d ARMENIAN SMALL LETTER XEH */ { 0x0000, 0x0787, 0x0787, 0x0000 }, /* 056e ARMENIAN SMALL LETTER CA */ { 0x0000, 0x0789, 0x0789, 0x0000 }, /* 056f ARMENIAN SMALL LETTER KEN */ { 0x0000, 0x078b, 0x078b, 0x0000 }, /* 0570 ARMENIAN SMALL LETTER HO */ { 0x0000, 0x078d, 0x078d, 0x0000 }, /* 0571 ARMENIAN SMALL LETTER JA */ { 0x0000, 0x078f, 0x078f, 0x0000 }, /* 0572 ARMENIAN SMALL LETTER GHAD */ { 0x0000, 0x0791, 0x0791, 0x0000 }, /* 0573 ARMENIAN SMALL LETTER CHEH */ { 0x0000, 0x0793, 0x0793, 0x0000 }, /* 0574 ARMENIAN SMALL LETTER MEN */ { 0x0000, 0x0795, 0x0795, 0x0000 }, /* 0575 ARMENIAN SMALL LETTER YI */ { 0x0000, 0x0797, 0x0797, 0x0000 }, /* 0576 ARMENIAN SMALL LETTER NOW */ { 0x0000, 0x0799, 0x0799, 0x0000 }, /* 0577 ARMENIAN SMALL LETTER SHA */ { 0x0000, 0x079b, 0x079b, 0x0000 }, /* 0578 ARMENIAN SMALL LETTER VO */ { 0x0000, 0x079d, 0x079d, 0x0000 }, /* 0579 ARMENIAN SMALL LETTER CHA */ { 0x0000, 0x079f, 0x079f, 0x0000 }, /* 057a ARMENIAN SMALL LETTER PEH */ { 0x0000, 0x07a1, 0x07a1, 0x0000 }, /* 057b ARMENIAN SMALL LETTER JHEH */ { 0x0000, 0x07a3, 0x07a3, 0x0000 }, /* 057c ARMENIAN SMALL LETTER RA */ { 0x0000, 0x07a5, 0x07a5, 0x0000 }, /* 057d ARMENIAN SMALL LETTER SEH */ { 0x0000, 0x07a7, 0x07a7, 0x0000 }, /* 057e ARMENIAN SMALL LETTER VEW */ { 0x0000, 0x07a9, 0x07a9, 0x0000 } /* 057f ARMENIAN SMALL LETTER TIWN */ }; static case_table case_pg_00b[128] = { { 0x0000, 0x07ab, 0x07ab, 0x0000 }, /* 0580 ARMENIAN SMALL LETTER REH */ { 0x0000, 0x07ad, 0x07ad, 0x0000 }, /* 0581 ARMENIAN SMALL LETTER CO */ { 0x0000, 0x07af, 0x07af, 0x0000 }, /* 0582 ARMENIAN SMALL LETTER YIWN */ { 0x0000, 0x07b1, 0x07b1, 0x0000 }, /* 0583 ARMENIAN SMALL LETTER PIWR */ { 0x0000, 0x07b3, 0x07b3, 0x0000 }, /* 0584 ARMENIAN SMALL LETTER KEH */ { 0x0000, 0x07b5, 0x07b5, 0x0000 }, /* 0585 ARMENIAN SMALL LETTER OH */ { 0x0000, 0x07b7, 0x07b7, 0x0000 }, /* 0586 ARMENIAN SMALL LETTER FEH */ { 0x0000, 0x07b9, 0x07bc, 0x07bf }, /* 0587 ARMENIAN SMALL LIGATURE ECH YIWN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0588 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0589 ARMENIAN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 058a ARMENIAN HYPHEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 058b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 058c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 058d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 058e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 058f ARMENIAN DRAM SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0590 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0591 HEBREW ACCENT ETNAHTA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0592 HEBREW ACCENT SEGOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0593 HEBREW ACCENT SHALSHELET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0594 HEBREW ACCENT ZAQEF QATAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0595 HEBREW ACCENT ZAQEF GADOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0596 HEBREW ACCENT TIPEHA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0597 HEBREW ACCENT REVIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0598 HEBREW ACCENT ZARQA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0599 HEBREW ACCENT PASHTA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 059a HEBREW ACCENT YETIV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 059b HEBREW ACCENT TEVIR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 059c HEBREW ACCENT GERESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 059d HEBREW ACCENT GERESH MUQDAM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 059e HEBREW ACCENT GERSHAYIM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 059f HEBREW ACCENT QARNEY PARA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a0 HEBREW ACCENT TELISHA GEDOLA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a1 HEBREW ACCENT PAZER */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a2 HEBREW ACCENT ATNAH HAFUKH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a3 HEBREW ACCENT MUNAH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a4 HEBREW ACCENT MAHAPAKH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a5 HEBREW ACCENT MERKHA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a6 HEBREW ACCENT MERKHA KEFULA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a7 HEBREW ACCENT DARGA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a8 HEBREW ACCENT QADMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a9 HEBREW ACCENT TELISHA QETANA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05aa HEBREW ACCENT YERAH BEN YOMO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ab HEBREW ACCENT OLE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ac HEBREW ACCENT ILUY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ad HEBREW ACCENT DEHI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ae HEBREW ACCENT ZINOR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05af HEBREW MARK MASORA CIRCLE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b0 HEBREW POINT SHEVA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b1 HEBREW POINT HATAF SEGOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b2 HEBREW POINT HATAF PATAH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b3 HEBREW POINT HATAF QAMATS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b4 HEBREW POINT HIRIQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b5 HEBREW POINT TSERE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b6 HEBREW POINT SEGOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b7 HEBREW POINT PATAH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b8 HEBREW POINT QAMATS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b9 HEBREW POINT HOLAM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ba HEBREW POINT HOLAM HASER FOR VAV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05bb HEBREW POINT QUBUTS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05bc HEBREW POINT DAGESH OR MAPIQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05bd HEBREW POINT METEG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05be HEBREW PUNCTUATION MAQAF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05bf HEBREW POINT RAFE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c0 HEBREW PUNCTUATION PASEQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c1 HEBREW POINT SHIN DOT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c2 HEBREW POINT SIN DOT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c3 HEBREW PUNCTUATION SOF PASUQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c4 HEBREW MARK UPPER DOT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c5 HEBREW MARK LOWER DOT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c6 HEBREW PUNCTUATION NUN HAFUKHA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c7 HEBREW POINT QAMATS QATAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c9 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ca (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05cb (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05cc (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05cd (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ce (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05cf (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d0 HEBREW LETTER ALEF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d1 HEBREW LETTER BET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d2 HEBREW LETTER GIMEL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d3 HEBREW LETTER DALET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d4 HEBREW LETTER HE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d5 HEBREW LETTER VAV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d6 HEBREW LETTER ZAYIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d7 HEBREW LETTER HET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d8 HEBREW LETTER TET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d9 HEBREW LETTER YOD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05da HEBREW LETTER FINAL KAF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05db HEBREW LETTER KAF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05dc HEBREW LETTER LAMED */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05dd HEBREW LETTER FINAL MEM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05de HEBREW LETTER MEM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05df HEBREW LETTER FINAL NUN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e0 HEBREW LETTER NUN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e1 HEBREW LETTER SAMEKH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e2 HEBREW LETTER AYIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e3 HEBREW LETTER FINAL PE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e4 HEBREW LETTER PE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e5 HEBREW LETTER FINAL TSADI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e6 HEBREW LETTER TSADI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e7 HEBREW LETTER QOF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e8 HEBREW LETTER RESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e9 HEBREW LETTER SHIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ea HEBREW LETTER TAV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05eb (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ec (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ed (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ee (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ef (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f0 HEBREW LIGATURE YIDDISH DOUBLE VAV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f1 HEBREW LIGATURE YIDDISH VAV YOD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f2 HEBREW LIGATURE YIDDISH DOUBLE YOD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f3 HEBREW PUNCTUATION GERESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f4 HEBREW PUNCTUATION GERSHAYIM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f5 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f6 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f7 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f9 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05fa (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05fb (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05fc (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05fd (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05fe (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 05ff (null) */ }; static case_table case_pg_021[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1080 MYANMAR LETTER SHAN THA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1081 MYANMAR LETTER SHAN HA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1082 MYANMAR CONSONANT SIGN SHAN MEDIAL WA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1083 MYANMAR VOWEL SIGN SHAN AA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1084 MYANMAR VOWEL SIGN SHAN E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1085 MYANMAR VOWEL SIGN SHAN E ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1086 MYANMAR VOWEL SIGN SHAN FINAL Y */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1087 MYANMAR SIGN SHAN TONE-2 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1088 MYANMAR SIGN SHAN TONE-3 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1089 MYANMAR SIGN SHAN TONE-5 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 108a MYANMAR SIGN SHAN TONE-6 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 108b MYANMAR SIGN SHAN COUNCIL TONE-2 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 108c MYANMAR SIGN SHAN COUNCIL TONE-3 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 108d MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 108e MYANMAR LETTER RUMAI PALAUNG FA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 108f MYANMAR SIGN RUMAI PALAUNG TONE-5 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1090 MYANMAR SHAN DIGIT ZERO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1091 MYANMAR SHAN DIGIT ONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1092 MYANMAR SHAN DIGIT TWO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1093 MYANMAR SHAN DIGIT THREE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1094 MYANMAR SHAN DIGIT FOUR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1095 MYANMAR SHAN DIGIT FIVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1096 MYANMAR SHAN DIGIT SIX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1097 MYANMAR SHAN DIGIT SEVEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1098 MYANMAR SHAN DIGIT EIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1099 MYANMAR SHAN DIGIT NINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 109a MYANMAR SIGN KHAMTI TONE-1 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 109b MYANMAR SIGN KHAMTI TONE-3 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 109c MYANMAR VOWEL SIGN AITON A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 109d MYANMAR VOWEL SIGN AITON AI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 109e MYANMAR SYMBOL SHAN ONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 109f MYANMAR SYMBOL SHAN EXCLAMATION */ { 0x07c2, 0x0000, 0x0000, 0x07c2 }, /* 10a0 GEORGIAN CAPITAL LETTER AN */ { 0x07c4, 0x0000, 0x0000, 0x07c4 }, /* 10a1 GEORGIAN CAPITAL LETTER BAN */ { 0x07c6, 0x0000, 0x0000, 0x07c6 }, /* 10a2 GEORGIAN CAPITAL LETTER GAN */ { 0x07c8, 0x0000, 0x0000, 0x07c8 }, /* 10a3 GEORGIAN CAPITAL LETTER DON */ { 0x07ca, 0x0000, 0x0000, 0x07ca }, /* 10a4 GEORGIAN CAPITAL LETTER EN */ { 0x07cc, 0x0000, 0x0000, 0x07cc }, /* 10a5 GEORGIAN CAPITAL LETTER VIN */ { 0x07ce, 0x0000, 0x0000, 0x07ce }, /* 10a6 GEORGIAN CAPITAL LETTER ZEN */ { 0x07d0, 0x0000, 0x0000, 0x07d0 }, /* 10a7 GEORGIAN CAPITAL LETTER TAN */ { 0x07d2, 0x0000, 0x0000, 0x07d2 }, /* 10a8 GEORGIAN CAPITAL LETTER IN */ { 0x07d4, 0x0000, 0x0000, 0x07d4 }, /* 10a9 GEORGIAN CAPITAL LETTER KAN */ { 0x07d6, 0x0000, 0x0000, 0x07d6 }, /* 10aa GEORGIAN CAPITAL LETTER LAS */ { 0x07d8, 0x0000, 0x0000, 0x07d8 }, /* 10ab GEORGIAN CAPITAL LETTER MAN */ { 0x07da, 0x0000, 0x0000, 0x07da }, /* 10ac GEORGIAN CAPITAL LETTER NAR */ { 0x07dc, 0x0000, 0x0000, 0x07dc }, /* 10ad GEORGIAN CAPITAL LETTER ON */ { 0x07de, 0x0000, 0x0000, 0x07de }, /* 10ae GEORGIAN CAPITAL LETTER PAR */ { 0x07e0, 0x0000, 0x0000, 0x07e0 }, /* 10af GEORGIAN CAPITAL LETTER ZHAR */ { 0x07e2, 0x0000, 0x0000, 0x07e2 }, /* 10b0 GEORGIAN CAPITAL LETTER RAE */ { 0x07e4, 0x0000, 0x0000, 0x07e4 }, /* 10b1 GEORGIAN CAPITAL LETTER SAN */ { 0x07e6, 0x0000, 0x0000, 0x07e6 }, /* 10b2 GEORGIAN CAPITAL LETTER TAR */ { 0x07e8, 0x0000, 0x0000, 0x07e8 }, /* 10b3 GEORGIAN CAPITAL LETTER UN */ { 0x07ea, 0x0000, 0x0000, 0x07ea }, /* 10b4 GEORGIAN CAPITAL LETTER PHAR */ { 0x07ec, 0x0000, 0x0000, 0x07ec }, /* 10b5 GEORGIAN CAPITAL LETTER KHAR */ { 0x07ee, 0x0000, 0x0000, 0x07ee }, /* 10b6 GEORGIAN CAPITAL LETTER GHAN */ { 0x07f0, 0x0000, 0x0000, 0x07f0 }, /* 10b7 GEORGIAN CAPITAL LETTER QAR */ { 0x07f2, 0x0000, 0x0000, 0x07f2 }, /* 10b8 GEORGIAN CAPITAL LETTER SHIN */ { 0x07f4, 0x0000, 0x0000, 0x07f4 }, /* 10b9 GEORGIAN CAPITAL LETTER CHIN */ { 0x07f6, 0x0000, 0x0000, 0x07f6 }, /* 10ba GEORGIAN CAPITAL LETTER CAN */ { 0x07f8, 0x0000, 0x0000, 0x07f8 }, /* 10bb GEORGIAN CAPITAL LETTER JIL */ { 0x07fa, 0x0000, 0x0000, 0x07fa }, /* 10bc GEORGIAN CAPITAL LETTER CIL */ { 0x07fc, 0x0000, 0x0000, 0x07fc }, /* 10bd GEORGIAN CAPITAL LETTER CHAR */ { 0x07fe, 0x0000, 0x0000, 0x07fe }, /* 10be GEORGIAN CAPITAL LETTER XAN */ { 0x0800, 0x0000, 0x0000, 0x0800 }, /* 10bf GEORGIAN CAPITAL LETTER JHAN */ { 0x0802, 0x0000, 0x0000, 0x0802 }, /* 10c0 GEORGIAN CAPITAL LETTER HAE */ { 0x0804, 0x0000, 0x0000, 0x0804 }, /* 10c1 GEORGIAN CAPITAL LETTER HE */ { 0x0806, 0x0000, 0x0000, 0x0806 }, /* 10c2 GEORGIAN CAPITAL LETTER HIE */ { 0x0808, 0x0000, 0x0000, 0x0808 }, /* 10c3 GEORGIAN CAPITAL LETTER WE */ { 0x080a, 0x0000, 0x0000, 0x080a }, /* 10c4 GEORGIAN CAPITAL LETTER HAR */ { 0x080c, 0x0000, 0x0000, 0x080c }, /* 10c5 GEORGIAN CAPITAL LETTER HOE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10c6 (null) */ { 0x080e, 0x0000, 0x0000, 0x080e }, /* 10c7 GEORGIAN CAPITAL LETTER YN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10c8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10c9 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10ca (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10cb (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10cc (null) */ { 0x0810, 0x0000, 0x0000, 0x0810 }, /* 10cd GEORGIAN CAPITAL LETTER AEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10ce (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10cf (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d0 GEORGIAN LETTER AN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d1 GEORGIAN LETTER BAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d2 GEORGIAN LETTER GAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d3 GEORGIAN LETTER DON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d4 GEORGIAN LETTER EN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d5 GEORGIAN LETTER VIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d6 GEORGIAN LETTER ZEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d7 GEORGIAN LETTER TAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d8 GEORGIAN LETTER IN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d9 GEORGIAN LETTER KAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10da GEORGIAN LETTER LAS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10db GEORGIAN LETTER MAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10dc GEORGIAN LETTER NAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10dd GEORGIAN LETTER ON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10de GEORGIAN LETTER PAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10df GEORGIAN LETTER ZHAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e0 GEORGIAN LETTER RAE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e1 GEORGIAN LETTER SAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e2 GEORGIAN LETTER TAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e3 GEORGIAN LETTER UN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e4 GEORGIAN LETTER PHAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e5 GEORGIAN LETTER KHAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e6 GEORGIAN LETTER GHAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e7 GEORGIAN LETTER QAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e8 GEORGIAN LETTER SHIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e9 GEORGIAN LETTER CHIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10ea GEORGIAN LETTER CAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10eb GEORGIAN LETTER JIL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10ec GEORGIAN LETTER CIL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10ed GEORGIAN LETTER CHAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10ee GEORGIAN LETTER XAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10ef GEORGIAN LETTER JHAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f0 GEORGIAN LETTER HAE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f1 GEORGIAN LETTER HE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f2 GEORGIAN LETTER HIE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f3 GEORGIAN LETTER WE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f4 GEORGIAN LETTER HAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f5 GEORGIAN LETTER HOE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f6 GEORGIAN LETTER FI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f7 GEORGIAN LETTER YN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f8 GEORGIAN LETTER ELIFI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f9 GEORGIAN LETTER TURNED GAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10fa GEORGIAN LETTER AIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10fb GEORGIAN PARAGRAPH SEPARATOR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10fc MODIFIER LETTER GEORGIAN NAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10fd GEORGIAN LETTER AEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10fe GEORGIAN LETTER HARD SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 10ff GEORGIAN LETTER LABIAL SIGN */ }; static case_table case_pg_03a[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d00 LATIN LETTER SMALL CAPITAL A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d01 LATIN LETTER SMALL CAPITAL AE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d02 LATIN SMALL LETTER TURNED AE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d03 LATIN LETTER SMALL CAPITAL BARRED B */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d04 LATIN LETTER SMALL CAPITAL C */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d05 LATIN LETTER SMALL CAPITAL D */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d06 LATIN LETTER SMALL CAPITAL ETH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d07 LATIN LETTER SMALL CAPITAL E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d08 LATIN SMALL LETTER TURNED OPEN E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d09 LATIN SMALL LETTER TURNED I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d0a LATIN LETTER SMALL CAPITAL J */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d0b LATIN LETTER SMALL CAPITAL K */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d0c LATIN LETTER SMALL CAPITAL L WITH STROK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d0d LATIN LETTER SMALL CAPITAL M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d0e LATIN LETTER SMALL CAPITAL REVERSED N */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d0f LATIN LETTER SMALL CAPITAL O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d10 LATIN LETTER SMALL CAPITAL OPEN O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d11 LATIN SMALL LETTER SIDEWAYS O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d12 LATIN SMALL LETTER SIDEWAYS OPEN O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d13 LATIN SMALL LETTER SIDEWAYS O WITH STRO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d14 LATIN SMALL LETTER TURNED OE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d15 LATIN LETTER SMALL CAPITAL OU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d16 LATIN SMALL LETTER TOP HALF O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d17 LATIN SMALL LETTER BOTTOM HALF O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d18 LATIN LETTER SMALL CAPITAL P */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d19 LATIN LETTER SMALL CAPITAL REVERSED R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d1a LATIN LETTER SMALL CAPITAL TURNED R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d1b LATIN LETTER SMALL CAPITAL T */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d1c LATIN LETTER SMALL CAPITAL U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d1d LATIN SMALL LETTER SIDEWAYS U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d1e LATIN SMALL LETTER SIDEWAYS DIAERESIZED */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d1f LATIN SMALL LETTER SIDEWAYS TURNED M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d20 LATIN LETTER SMALL CAPITAL V */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d21 LATIN LETTER SMALL CAPITAL W */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d22 LATIN LETTER SMALL CAPITAL Z */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d23 LATIN LETTER SMALL CAPITAL EZH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d24 LATIN LETTER VOICED LARYNGEAL SPIRANT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d25 LATIN LETTER AIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d26 GREEK LETTER SMALL CAPITAL GAMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d27 GREEK LETTER SMALL CAPITAL LAMDA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d28 GREEK LETTER SMALL CAPITAL PI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d29 GREEK LETTER SMALL CAPITAL RHO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d2a GREEK LETTER SMALL CAPITAL PSI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d2b CYRILLIC LETTER SMALL CAPITAL EL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d2c MODIFIER LETTER CAPITAL A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d2d MODIFIER LETTER CAPITAL AE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d2e MODIFIER LETTER CAPITAL B */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d2f MODIFIER LETTER CAPITAL BARRED B */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d30 MODIFIER LETTER CAPITAL D */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d31 MODIFIER LETTER CAPITAL E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d32 MODIFIER LETTER CAPITAL REVERSED E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d33 MODIFIER LETTER CAPITAL G */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d34 MODIFIER LETTER CAPITAL H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d35 MODIFIER LETTER CAPITAL I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d36 MODIFIER LETTER CAPITAL J */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d37 MODIFIER LETTER CAPITAL K */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d38 MODIFIER LETTER CAPITAL L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d39 MODIFIER LETTER CAPITAL M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d3a MODIFIER LETTER CAPITAL N */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d3b MODIFIER LETTER CAPITAL REVERSED N */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d3c MODIFIER LETTER CAPITAL O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d3d MODIFIER LETTER CAPITAL OU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d3e MODIFIER LETTER CAPITAL P */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d3f MODIFIER LETTER CAPITAL R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d40 MODIFIER LETTER CAPITAL T */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d41 MODIFIER LETTER CAPITAL U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d42 MODIFIER LETTER CAPITAL W */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d43 MODIFIER LETTER SMALL A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d44 MODIFIER LETTER SMALL TURNED A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d45 MODIFIER LETTER SMALL ALPHA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d46 MODIFIER LETTER SMALL TURNED AE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d47 MODIFIER LETTER SMALL B */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d48 MODIFIER LETTER SMALL D */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d49 MODIFIER LETTER SMALL E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d4a MODIFIER LETTER SMALL SCHWA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d4b MODIFIER LETTER SMALL OPEN E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d4c MODIFIER LETTER SMALL TURNED OPEN E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d4d MODIFIER LETTER SMALL G */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d4e MODIFIER LETTER SMALL TURNED I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d4f MODIFIER LETTER SMALL K */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d50 MODIFIER LETTER SMALL M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d51 MODIFIER LETTER SMALL ENG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d52 MODIFIER LETTER SMALL O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d53 MODIFIER LETTER SMALL OPEN O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d54 MODIFIER LETTER SMALL TOP HALF O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d55 MODIFIER LETTER SMALL BOTTOM HALF O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d56 MODIFIER LETTER SMALL P */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d57 MODIFIER LETTER SMALL T */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d58 MODIFIER LETTER SMALL U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d59 MODIFIER LETTER SMALL SIDEWAYS U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d5a MODIFIER LETTER SMALL TURNED M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d5b MODIFIER LETTER SMALL V */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d5c MODIFIER LETTER SMALL AIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d5d MODIFIER LETTER SMALL BETA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d5e MODIFIER LETTER SMALL GREEK GAMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d5f MODIFIER LETTER SMALL DELTA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d60 MODIFIER LETTER SMALL GREEK PHI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d61 MODIFIER LETTER SMALL CHI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d62 LATIN SUBSCRIPT SMALL LETTER I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d63 LATIN SUBSCRIPT SMALL LETTER R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d64 LATIN SUBSCRIPT SMALL LETTER U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d65 LATIN SUBSCRIPT SMALL LETTER V */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d66 GREEK SUBSCRIPT SMALL LETTER BETA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d67 GREEK SUBSCRIPT SMALL LETTER GAMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d68 GREEK SUBSCRIPT SMALL LETTER RHO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d69 GREEK SUBSCRIPT SMALL LETTER PHI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d6a GREEK SUBSCRIPT SMALL LETTER CHI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d6b LATIN SMALL LETTER UE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d6c LATIN SMALL LETTER B WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d6d LATIN SMALL LETTER D WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d6e LATIN SMALL LETTER F WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d6f LATIN SMALL LETTER M WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d70 LATIN SMALL LETTER N WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d71 LATIN SMALL LETTER P WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d72 LATIN SMALL LETTER R WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d73 LATIN SMALL LETTER R WITH FISHHOOK AND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d74 LATIN SMALL LETTER S WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d75 LATIN SMALL LETTER T WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d76 LATIN SMALL LETTER Z WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d77 LATIN SMALL LETTER TURNED G */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d78 MODIFIER LETTER CYRILLIC EN */ { 0x0000, 0x0812, 0x0812, 0x0000 }, /* 1d79 LATIN SMALL LETTER INSULAR G */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d7a LATIN SMALL LETTER TH WITH STRIKETHROUG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d7b LATIN SMALL CAPITAL LETTER I WITH STROK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d7c LATIN SMALL LETTER IOTA WITH STROKE */ { 0x0000, 0x0814, 0x0814, 0x0000 }, /* 1d7d LATIN SMALL LETTER P WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d7e LATIN SMALL CAPITAL LETTER U WITH STROK */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 1d7f LATIN SMALL LETTER UPSILON WITH STROKE */ }; static case_table case_pg_03c[128] = { { 0x0816, 0x0000, 0x0000, 0x0816 }, /* 1e00 LATIN CAPITAL LETTER A WITH RING BELOW */ { 0x0000, 0x0818, 0x0818, 0x0000 }, /* 1e01 LATIN SMALL LETTER A WITH RING BELOW */ { 0x081a, 0x0000, 0x0000, 0x081a }, /* 1e02 LATIN CAPITAL LETTER B WITH DOT ABOVE */ { 0x0000, 0x081c, 0x081c, 0x0000 }, /* 1e03 LATIN SMALL LETTER B WITH DOT ABOVE */ { 0x081e, 0x0000, 0x0000, 0x081e }, /* 1e04 LATIN CAPITAL LETTER B WITH DOT BELOW */ { 0x0000, 0x0820, 0x0820, 0x0000 }, /* 1e05 LATIN SMALL LETTER B WITH DOT BELOW */ { 0x0822, 0x0000, 0x0000, 0x0822 }, /* 1e06 LATIN CAPITAL LETTER B WITH LINE BELOW */ { 0x0000, 0x0824, 0x0824, 0x0000 }, /* 1e07 LATIN SMALL LETTER B WITH LINE BELOW */ { 0x0826, 0x0000, 0x0000, 0x0826 }, /* 1e08 LATIN CAPITAL LETTER C WITH CEDILLA AND */ { 0x0000, 0x0828, 0x0828, 0x0000 }, /* 1e09 LATIN SMALL LETTER C WITH CEDILLA AND A */ { 0x082a, 0x0000, 0x0000, 0x082a }, /* 1e0a LATIN CAPITAL LETTER D WITH DOT ABOVE */ { 0x0000, 0x082c, 0x082c, 0x0000 }, /* 1e0b LATIN SMALL LETTER D WITH DOT ABOVE */ { 0x082e, 0x0000, 0x0000, 0x082e }, /* 1e0c LATIN CAPITAL LETTER D WITH DOT BELOW */ { 0x0000, 0x0830, 0x0830, 0x0000 }, /* 1e0d LATIN SMALL LETTER D WITH DOT BELOW */ { 0x0832, 0x0000, 0x0000, 0x0832 }, /* 1e0e LATIN CAPITAL LETTER D WITH LINE BELOW */ { 0x0000, 0x0834, 0x0834, 0x0000 }, /* 1e0f LATIN SMALL LETTER D WITH LINE BELOW */ { 0x0836, 0x0000, 0x0000, 0x0836 }, /* 1e10 LATIN CAPITAL LETTER D WITH CEDILLA */ { 0x0000, 0x0838, 0x0838, 0x0000 }, /* 1e11 LATIN SMALL LETTER D WITH CEDILLA */ { 0x083a, 0x0000, 0x0000, 0x083a }, /* 1e12 LATIN CAPITAL LETTER D WITH CIRCUMFLEX */ { 0x0000, 0x083c, 0x083c, 0x0000 }, /* 1e13 LATIN SMALL LETTER D WITH CIRCUMFLEX BE */ { 0x083e, 0x0000, 0x0000, 0x083e }, /* 1e14 LATIN CAPITAL LETTER E WITH MACRON AND */ { 0x0000, 0x0840, 0x0840, 0x0000 }, /* 1e15 LATIN SMALL LETTER E WITH MACRON AND GR */ { 0x0842, 0x0000, 0x0000, 0x0842 }, /* 1e16 LATIN CAPITAL LETTER E WITH MACRON AND */ { 0x0000, 0x0844, 0x0844, 0x0000 }, /* 1e17 LATIN SMALL LETTER E WITH MACRON AND AC */ { 0x0846, 0x0000, 0x0000, 0x0846 }, /* 1e18 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ { 0x0000, 0x0848, 0x0848, 0x0000 }, /* 1e19 LATIN SMALL LETTER E WITH CIRCUMFLEX BE */ { 0x084a, 0x0000, 0x0000, 0x084a }, /* 1e1a LATIN CAPITAL LETTER E WITH TILDE BELOW */ { 0x0000, 0x084c, 0x084c, 0x0000 }, /* 1e1b LATIN SMALL LETTER E WITH TILDE BELOW */ { 0x084e, 0x0000, 0x0000, 0x084e }, /* 1e1c LATIN CAPITAL LETTER E WITH CEDILLA AND */ { 0x0000, 0x0850, 0x0850, 0x0000 }, /* 1e1d LATIN SMALL LETTER E WITH CEDILLA AND B */ { 0x0852, 0x0000, 0x0000, 0x0852 }, /* 1e1e LATIN CAPITAL LETTER F WITH DOT ABOVE */ { 0x0000, 0x0854, 0x0854, 0x0000 }, /* 1e1f LATIN SMALL LETTER F WITH DOT ABOVE */ { 0x0856, 0x0000, 0x0000, 0x0856 }, /* 1e20 LATIN CAPITAL LETTER G WITH MACRON */ { 0x0000, 0x0858, 0x0858, 0x0000 }, /* 1e21 LATIN SMALL LETTER G WITH MACRON */ { 0x085a, 0x0000, 0x0000, 0x085a }, /* 1e22 LATIN CAPITAL LETTER H WITH DOT ABOVE */ { 0x0000, 0x085c, 0x085c, 0x0000 }, /* 1e23 LATIN SMALL LETTER H WITH DOT ABOVE */ { 0x085e, 0x0000, 0x0000, 0x085e }, /* 1e24 LATIN CAPITAL LETTER H WITH DOT BELOW */ { 0x0000, 0x0860, 0x0860, 0x0000 }, /* 1e25 LATIN SMALL LETTER H WITH DOT BELOW */ { 0x0862, 0x0000, 0x0000, 0x0862 }, /* 1e26 LATIN CAPITAL LETTER H WITH DIAERESIS */ { 0x0000, 0x0864, 0x0864, 0x0000 }, /* 1e27 LATIN SMALL LETTER H WITH DIAERESIS */ { 0x0866, 0x0000, 0x0000, 0x0866 }, /* 1e28 LATIN CAPITAL LETTER H WITH CEDILLA */ { 0x0000, 0x0868, 0x0868, 0x0000 }, /* 1e29 LATIN SMALL LETTER H WITH CEDILLA */ { 0x086a, 0x0000, 0x0000, 0x086a }, /* 1e2a LATIN CAPITAL LETTER H WITH BREVE BELOW */ { 0x0000, 0x086c, 0x086c, 0x0000 }, /* 1e2b LATIN SMALL LETTER H WITH BREVE BELOW */ { 0x086e, 0x0000, 0x0000, 0x086e }, /* 1e2c LATIN CAPITAL LETTER I WITH TILDE BELOW */ { 0x0000, 0x0870, 0x0870, 0x0000 }, /* 1e2d LATIN SMALL LETTER I WITH TILDE BELOW */ { 0x0872, 0x0000, 0x0000, 0x0872 }, /* 1e2e LATIN CAPITAL LETTER I WITH DIAERESIS A */ { 0x0000, 0x0874, 0x0874, 0x0000 }, /* 1e2f LATIN SMALL LETTER I WITH DIAERESIS AND */ { 0x0876, 0x0000, 0x0000, 0x0876 }, /* 1e30 LATIN CAPITAL LETTER K WITH ACUTE */ { 0x0000, 0x0878, 0x0878, 0x0000 }, /* 1e31 LATIN SMALL LETTER K WITH ACUTE */ { 0x087a, 0x0000, 0x0000, 0x087a }, /* 1e32 LATIN CAPITAL LETTER K WITH DOT BELOW */ { 0x0000, 0x087c, 0x087c, 0x0000 }, /* 1e33 LATIN SMALL LETTER K WITH DOT BELOW */ { 0x087e, 0x0000, 0x0000, 0x087e }, /* 1e34 LATIN CAPITAL LETTER K WITH LINE BELOW */ { 0x0000, 0x0880, 0x0880, 0x0000 }, /* 1e35 LATIN SMALL LETTER K WITH LINE BELOW */ { 0x0882, 0x0000, 0x0000, 0x0882 }, /* 1e36 LATIN CAPITAL LETTER L WITH DOT BELOW */ { 0x0000, 0x0884, 0x0884, 0x0000 }, /* 1e37 LATIN SMALL LETTER L WITH DOT BELOW */ { 0x0886, 0x0000, 0x0000, 0x0886 }, /* 1e38 LATIN CAPITAL LETTER L WITH DOT BELOW A */ { 0x0000, 0x0888, 0x0888, 0x0000 }, /* 1e39 LATIN SMALL LETTER L WITH DOT BELOW AND */ { 0x088a, 0x0000, 0x0000, 0x088a }, /* 1e3a LATIN CAPITAL LETTER L WITH LINE BELOW */ { 0x0000, 0x088c, 0x088c, 0x0000 }, /* 1e3b LATIN SMALL LETTER L WITH LINE BELOW */ { 0x088e, 0x0000, 0x0000, 0x088e }, /* 1e3c LATIN CAPITAL LETTER L WITH CIRCUMFLEX */ { 0x0000, 0x0890, 0x0890, 0x0000 }, /* 1e3d LATIN SMALL LETTER L WITH CIRCUMFLEX BE */ { 0x0892, 0x0000, 0x0000, 0x0892 }, /* 1e3e LATIN CAPITAL LETTER M WITH ACUTE */ { 0x0000, 0x0894, 0x0894, 0x0000 }, /* 1e3f LATIN SMALL LETTER M WITH ACUTE */ { 0x0896, 0x0000, 0x0000, 0x0896 }, /* 1e40 LATIN CAPITAL LETTER M WITH DOT ABOVE */ { 0x0000, 0x0898, 0x0898, 0x0000 }, /* 1e41 LATIN SMALL LETTER M WITH DOT ABOVE */ { 0x089a, 0x0000, 0x0000, 0x089a }, /* 1e42 LATIN CAPITAL LETTER M WITH DOT BELOW */ { 0x0000, 0x089c, 0x089c, 0x0000 }, /* 1e43 LATIN SMALL LETTER M WITH DOT BELOW */ { 0x089e, 0x0000, 0x0000, 0x089e }, /* 1e44 LATIN CAPITAL LETTER N WITH DOT ABOVE */ { 0x0000, 0x08a0, 0x08a0, 0x0000 }, /* 1e45 LATIN SMALL LETTER N WITH DOT ABOVE */ { 0x08a2, 0x0000, 0x0000, 0x08a2 }, /* 1e46 LATIN CAPITAL LETTER N WITH DOT BELOW */ { 0x0000, 0x08a4, 0x08a4, 0x0000 }, /* 1e47 LATIN SMALL LETTER N WITH DOT BELOW */ { 0x08a6, 0x0000, 0x0000, 0x08a6 }, /* 1e48 LATIN CAPITAL LETTER N WITH LINE BELOW */ { 0x0000, 0x08a8, 0x08a8, 0x0000 }, /* 1e49 LATIN SMALL LETTER N WITH LINE BELOW */ { 0x08aa, 0x0000, 0x0000, 0x08aa }, /* 1e4a LATIN CAPITAL LETTER N WITH CIRCUMFLEX */ { 0x0000, 0x08ac, 0x08ac, 0x0000 }, /* 1e4b LATIN SMALL LETTER N WITH CIRCUMFLEX BE */ { 0x08ae, 0x0000, 0x0000, 0x08ae }, /* 1e4c LATIN CAPITAL LETTER O WITH TILDE AND A */ { 0x0000, 0x08b0, 0x08b0, 0x0000 }, /* 1e4d LATIN SMALL LETTER O WITH TILDE AND ACU */ { 0x08b2, 0x0000, 0x0000, 0x08b2 }, /* 1e4e LATIN CAPITAL LETTER O WITH TILDE AND D */ { 0x0000, 0x08b4, 0x08b4, 0x0000 }, /* 1e4f LATIN SMALL LETTER O WITH TILDE AND DIA */ { 0x08b6, 0x0000, 0x0000, 0x08b6 }, /* 1e50 LATIN CAPITAL LETTER O WITH MACRON AND */ { 0x0000, 0x08b8, 0x08b8, 0x0000 }, /* 1e51 LATIN SMALL LETTER O WITH MACRON AND GR */ { 0x08ba, 0x0000, 0x0000, 0x08ba }, /* 1e52 LATIN CAPITAL LETTER O WITH MACRON AND */ { 0x0000, 0x08bc, 0x08bc, 0x0000 }, /* 1e53 LATIN SMALL LETTER O WITH MACRON AND AC */ { 0x08be, 0x0000, 0x0000, 0x08be }, /* 1e54 LATIN CAPITAL LETTER P WITH ACUTE */ { 0x0000, 0x08c0, 0x08c0, 0x0000 }, /* 1e55 LATIN SMALL LETTER P WITH ACUTE */ { 0x08c2, 0x0000, 0x0000, 0x08c2 }, /* 1e56 LATIN CAPITAL LETTER P WITH DOT ABOVE */ { 0x0000, 0x08c4, 0x08c4, 0x0000 }, /* 1e57 LATIN SMALL LETTER P WITH DOT ABOVE */ { 0x08c6, 0x0000, 0x0000, 0x08c6 }, /* 1e58 LATIN CAPITAL LETTER R WITH DOT ABOVE */ { 0x0000, 0x08c8, 0x08c8, 0x0000 }, /* 1e59 LATIN SMALL LETTER R WITH DOT ABOVE */ { 0x08ca, 0x0000, 0x0000, 0x08ca }, /* 1e5a LATIN CAPITAL LETTER R WITH DOT BELOW */ { 0x0000, 0x08cc, 0x08cc, 0x0000 }, /* 1e5b LATIN SMALL LETTER R WITH DOT BELOW */ { 0x08ce, 0x0000, 0x0000, 0x08ce }, /* 1e5c LATIN CAPITAL LETTER R WITH DOT BELOW A */ { 0x0000, 0x08d0, 0x08d0, 0x0000 }, /* 1e5d LATIN SMALL LETTER R WITH DOT BELOW AND */ { 0x08d2, 0x0000, 0x0000, 0x08d2 }, /* 1e5e LATIN CAPITAL LETTER R WITH LINE BELOW */ { 0x0000, 0x08d4, 0x08d4, 0x0000 }, /* 1e5f LATIN SMALL LETTER R WITH LINE BELOW */ { 0x08d6, 0x0000, 0x0000, 0x08d6 }, /* 1e60 LATIN CAPITAL LETTER S WITH DOT ABOVE */ { 0x0000, 0x08d8, 0x08d8, 0x0000 }, /* 1e61 LATIN SMALL LETTER S WITH DOT ABOVE */ { 0x08da, 0x0000, 0x0000, 0x08da }, /* 1e62 LATIN CAPITAL LETTER S WITH DOT BELOW */ { 0x0000, 0x08dc, 0x08dc, 0x0000 }, /* 1e63 LATIN SMALL LETTER S WITH DOT BELOW */ { 0x08de, 0x0000, 0x0000, 0x08de }, /* 1e64 LATIN CAPITAL LETTER S WITH ACUTE AND D */ { 0x0000, 0x08e0, 0x08e0, 0x0000 }, /* 1e65 LATIN SMALL LETTER S WITH ACUTE AND DOT */ { 0x08e2, 0x0000, 0x0000, 0x08e2 }, /* 1e66 LATIN CAPITAL LETTER S WITH CARON AND D */ { 0x0000, 0x08e4, 0x08e4, 0x0000 }, /* 1e67 LATIN SMALL LETTER S WITH CARON AND DOT */ { 0x08e6, 0x0000, 0x0000, 0x08e6 }, /* 1e68 LATIN CAPITAL LETTER S WITH DOT BELOW A */ { 0x0000, 0x08e8, 0x08e8, 0x0000 }, /* 1e69 LATIN SMALL LETTER S WITH DOT BELOW AND */ { 0x08ea, 0x0000, 0x0000, 0x08ea }, /* 1e6a LATIN CAPITAL LETTER T WITH DOT ABOVE */ { 0x0000, 0x08ec, 0x08ec, 0x0000 }, /* 1e6b LATIN SMALL LETTER T WITH DOT ABOVE */ { 0x08ee, 0x0000, 0x0000, 0x08ee }, /* 1e6c LATIN CAPITAL LETTER T WITH DOT BELOW */ { 0x0000, 0x08f0, 0x08f0, 0x0000 }, /* 1e6d LATIN SMALL LETTER T WITH DOT BELOW */ { 0x08f2, 0x0000, 0x0000, 0x08f2 }, /* 1e6e LATIN CAPITAL LETTER T WITH LINE BELOW */ { 0x0000, 0x08f4, 0x08f4, 0x0000 }, /* 1e6f LATIN SMALL LETTER T WITH LINE BELOW */ { 0x08f6, 0x0000, 0x0000, 0x08f6 }, /* 1e70 LATIN CAPITAL LETTER T WITH CIRCUMFLEX */ { 0x0000, 0x08f8, 0x08f8, 0x0000 }, /* 1e71 LATIN SMALL LETTER T WITH CIRCUMFLEX BE */ { 0x08fa, 0x0000, 0x0000, 0x08fa }, /* 1e72 LATIN CAPITAL LETTER U WITH DIAERESIS B */ { 0x0000, 0x08fc, 0x08fc, 0x0000 }, /* 1e73 LATIN SMALL LETTER U WITH DIAERESIS BEL */ { 0x08fe, 0x0000, 0x0000, 0x08fe }, /* 1e74 LATIN CAPITAL LETTER U WITH TILDE BELOW */ { 0x0000, 0x0900, 0x0900, 0x0000 }, /* 1e75 LATIN SMALL LETTER U WITH TILDE BELOW */ { 0x0902, 0x0000, 0x0000, 0x0902 }, /* 1e76 LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ { 0x0000, 0x0904, 0x0904, 0x0000 }, /* 1e77 LATIN SMALL LETTER U WITH CIRCUMFLEX BE */ { 0x0906, 0x0000, 0x0000, 0x0906 }, /* 1e78 LATIN CAPITAL LETTER U WITH TILDE AND A */ { 0x0000, 0x0908, 0x0908, 0x0000 }, /* 1e79 LATIN SMALL LETTER U WITH TILDE AND ACU */ { 0x090a, 0x0000, 0x0000, 0x090a }, /* 1e7a LATIN CAPITAL LETTER U WITH MACRON AND */ { 0x0000, 0x090c, 0x090c, 0x0000 }, /* 1e7b LATIN SMALL LETTER U WITH MACRON AND DI */ { 0x090e, 0x0000, 0x0000, 0x090e }, /* 1e7c LATIN CAPITAL LETTER V WITH TILDE */ { 0x0000, 0x0910, 0x0910, 0x0000 }, /* 1e7d LATIN SMALL LETTER V WITH TILDE */ { 0x0912, 0x0000, 0x0000, 0x0912 }, /* 1e7e LATIN CAPITAL LETTER V WITH DOT BELOW */ { 0x0000, 0x0914, 0x0914, 0x0000 } /* 1e7f LATIN SMALL LETTER V WITH DOT BELOW */ }; static case_table case_pg_03d[128] = { { 0x0916, 0x0000, 0x0000, 0x0916 }, /* 1e80 LATIN CAPITAL LETTER W WITH GRAVE */ { 0x0000, 0x0918, 0x0918, 0x0000 }, /* 1e81 LATIN SMALL LETTER W WITH GRAVE */ { 0x091a, 0x0000, 0x0000, 0x091a }, /* 1e82 LATIN CAPITAL LETTER W WITH ACUTE */ { 0x0000, 0x091c, 0x091c, 0x0000 }, /* 1e83 LATIN SMALL LETTER W WITH ACUTE */ { 0x091e, 0x0000, 0x0000, 0x091e }, /* 1e84 LATIN CAPITAL LETTER W WITH DIAERESIS */ { 0x0000, 0x0920, 0x0920, 0x0000 }, /* 1e85 LATIN SMALL LETTER W WITH DIAERESIS */ { 0x0922, 0x0000, 0x0000, 0x0922 }, /* 1e86 LATIN CAPITAL LETTER W WITH DOT ABOVE */ { 0x0000, 0x0924, 0x0924, 0x0000 }, /* 1e87 LATIN SMALL LETTER W WITH DOT ABOVE */ { 0x0926, 0x0000, 0x0000, 0x0926 }, /* 1e88 LATIN CAPITAL LETTER W WITH DOT BELOW */ { 0x0000, 0x0928, 0x0928, 0x0000 }, /* 1e89 LATIN SMALL LETTER W WITH DOT BELOW */ { 0x092a, 0x0000, 0x0000, 0x092a }, /* 1e8a LATIN CAPITAL LETTER X WITH DOT ABOVE */ { 0x0000, 0x092c, 0x092c, 0x0000 }, /* 1e8b LATIN SMALL LETTER X WITH DOT ABOVE */ { 0x092e, 0x0000, 0x0000, 0x092e }, /* 1e8c LATIN CAPITAL LETTER X WITH DIAERESIS */ { 0x0000, 0x0930, 0x0930, 0x0000 }, /* 1e8d LATIN SMALL LETTER X WITH DIAERESIS */ { 0x0932, 0x0000, 0x0000, 0x0932 }, /* 1e8e LATIN CAPITAL LETTER Y WITH DOT ABOVE */ { 0x0000, 0x0934, 0x0934, 0x0000 }, /* 1e8f LATIN SMALL LETTER Y WITH DOT ABOVE */ { 0x0936, 0x0000, 0x0000, 0x0936 }, /* 1e90 LATIN CAPITAL LETTER Z WITH CIRCUMFLEX */ { 0x0000, 0x0938, 0x0938, 0x0000 }, /* 1e91 LATIN SMALL LETTER Z WITH CIRCUMFLEX */ { 0x093a, 0x0000, 0x0000, 0x093a }, /* 1e92 LATIN CAPITAL LETTER Z WITH DOT BELOW */ { 0x0000, 0x093c, 0x093c, 0x0000 }, /* 1e93 LATIN SMALL LETTER Z WITH DOT BELOW */ { 0x093e, 0x0000, 0x0000, 0x093e }, /* 1e94 LATIN CAPITAL LETTER Z WITH LINE BELOW */ { 0x0000, 0x0940, 0x0940, 0x0000 }, /* 1e95 LATIN SMALL LETTER Z WITH LINE BELOW */ { 0x0000, 0x0942, 0x0942, 0x0945 }, /* 1e96 LATIN SMALL LETTER H WITH LINE BELOW */ { 0x0000, 0x0948, 0x0948, 0x094b }, /* 1e97 LATIN SMALL LETTER T WITH DIAERESIS */ { 0x0000, 0x094e, 0x094e, 0x0951 }, /* 1e98 LATIN SMALL LETTER W WITH RING ABOVE */ { 0x0000, 0x0954, 0x0954, 0x0957 }, /* 1e99 LATIN SMALL LETTER Y WITH RING ABOVE */ { 0x0000, 0x095a, 0x095a, 0x095d }, /* 1e9a LATIN SMALL LETTER A WITH RIGHT HALF RI */ { 0x0000, 0x0960, 0x0960, 0x0962 }, /* 1e9b LATIN SMALL LETTER LONG S WITH DOT ABOV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1e9c LATIN SMALL LETTER LONG S WITH DIAGONAL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1e9d LATIN SMALL LETTER LONG S WITH HIGH STR */ { 0x0964, 0x0000, 0x0000, 0x0966 }, /* 1e9e LATIN CAPITAL LETTER SHARP S */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1e9f LATIN SMALL LETTER DELTA */ { 0x0969, 0x0000, 0x0000, 0x0969 }, /* 1ea0 LATIN CAPITAL LETTER A WITH DOT BELOW */ { 0x0000, 0x096b, 0x096b, 0x0000 }, /* 1ea1 LATIN SMALL LETTER A WITH DOT BELOW */ { 0x096d, 0x0000, 0x0000, 0x096d }, /* 1ea2 LATIN CAPITAL LETTER A WITH HOOK ABOVE */ { 0x0000, 0x096f, 0x096f, 0x0000 }, /* 1ea3 LATIN SMALL LETTER A WITH HOOK ABOVE */ { 0x0971, 0x0000, 0x0000, 0x0971 }, /* 1ea4 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ { 0x0000, 0x0973, 0x0973, 0x0000 }, /* 1ea5 LATIN SMALL LETTER A WITH CIRCUMFLEX AN */ { 0x0975, 0x0000, 0x0000, 0x0975 }, /* 1ea6 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ { 0x0000, 0x0977, 0x0977, 0x0000 }, /* 1ea7 LATIN SMALL LETTER A WITH CIRCUMFLEX AN */ { 0x0979, 0x0000, 0x0000, 0x0979 }, /* 1ea8 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ { 0x0000, 0x097b, 0x097b, 0x0000 }, /* 1ea9 LATIN SMALL LETTER A WITH CIRCUMFLEX AN */ { 0x097d, 0x0000, 0x0000, 0x097d }, /* 1eaa LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ { 0x0000, 0x097f, 0x097f, 0x0000 }, /* 1eab LATIN SMALL LETTER A WITH CIRCUMFLEX AN */ { 0x0981, 0x0000, 0x0000, 0x0981 }, /* 1eac LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ { 0x0000, 0x0983, 0x0983, 0x0000 }, /* 1ead LATIN SMALL LETTER A WITH CIRCUMFLEX AN */ { 0x0985, 0x0000, 0x0000, 0x0985 }, /* 1eae LATIN CAPITAL LETTER A WITH BREVE AND A */ { 0x0000, 0x0987, 0x0987, 0x0000 }, /* 1eaf LATIN SMALL LETTER A WITH BREVE AND ACU */ { 0x0989, 0x0000, 0x0000, 0x0989 }, /* 1eb0 LATIN CAPITAL LETTER A WITH BREVE AND G */ { 0x0000, 0x098b, 0x098b, 0x0000 }, /* 1eb1 LATIN SMALL LETTER A WITH BREVE AND GRA */ { 0x098d, 0x0000, 0x0000, 0x098d }, /* 1eb2 LATIN CAPITAL LETTER A WITH BREVE AND H */ { 0x0000, 0x098f, 0x098f, 0x0000 }, /* 1eb3 LATIN SMALL LETTER A WITH BREVE AND HOO */ { 0x0991, 0x0000, 0x0000, 0x0991 }, /* 1eb4 LATIN CAPITAL LETTER A WITH BREVE AND T */ { 0x0000, 0x0993, 0x0993, 0x0000 }, /* 1eb5 LATIN SMALL LETTER A WITH BREVE AND TIL */ { 0x0995, 0x0000, 0x0000, 0x0995 }, /* 1eb6 LATIN CAPITAL LETTER A WITH BREVE AND D */ { 0x0000, 0x0997, 0x0997, 0x0000 }, /* 1eb7 LATIN SMALL LETTER A WITH BREVE AND DOT */ { 0x0999, 0x0000, 0x0000, 0x0999 }, /* 1eb8 LATIN CAPITAL LETTER E WITH DOT BELOW */ { 0x0000, 0x099b, 0x099b, 0x0000 }, /* 1eb9 LATIN SMALL LETTER E WITH DOT BELOW */ { 0x099d, 0x0000, 0x0000, 0x099d }, /* 1eba LATIN CAPITAL LETTER E WITH HOOK ABOVE */ { 0x0000, 0x099f, 0x099f, 0x0000 }, /* 1ebb LATIN SMALL LETTER E WITH HOOK ABOVE */ { 0x09a1, 0x0000, 0x0000, 0x09a1 }, /* 1ebc LATIN CAPITAL LETTER E WITH TILDE */ { 0x0000, 0x09a3, 0x09a3, 0x0000 }, /* 1ebd LATIN SMALL LETTER E WITH TILDE */ { 0x09a5, 0x0000, 0x0000, 0x09a5 }, /* 1ebe LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ { 0x0000, 0x09a7, 0x09a7, 0x0000 }, /* 1ebf LATIN SMALL LETTER E WITH CIRCUMFLEX AN */ { 0x09a9, 0x0000, 0x0000, 0x09a9 }, /* 1ec0 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ { 0x0000, 0x09ab, 0x09ab, 0x0000 }, /* 1ec1 LATIN SMALL LETTER E WITH CIRCUMFLEX AN */ { 0x09ad, 0x0000, 0x0000, 0x09ad }, /* 1ec2 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ { 0x0000, 0x09af, 0x09af, 0x0000 }, /* 1ec3 LATIN SMALL LETTER E WITH CIRCUMFLEX AN */ { 0x09b1, 0x0000, 0x0000, 0x09b1 }, /* 1ec4 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ { 0x0000, 0x09b3, 0x09b3, 0x0000 }, /* 1ec5 LATIN SMALL LETTER E WITH CIRCUMFLEX AN */ { 0x09b5, 0x0000, 0x0000, 0x09b5 }, /* 1ec6 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ { 0x0000, 0x09b7, 0x09b7, 0x0000 }, /* 1ec7 LATIN SMALL LETTER E WITH CIRCUMFLEX AN */ { 0x09b9, 0x0000, 0x0000, 0x09b9 }, /* 1ec8 LATIN CAPITAL LETTER I WITH HOOK ABOVE */ { 0x0000, 0x09bb, 0x09bb, 0x0000 }, /* 1ec9 LATIN SMALL LETTER I WITH HOOK ABOVE */ { 0x09bd, 0x0000, 0x0000, 0x09bd }, /* 1eca LATIN CAPITAL LETTER I WITH DOT BELOW */ { 0x0000, 0x09bf, 0x09bf, 0x0000 }, /* 1ecb LATIN SMALL LETTER I WITH DOT BELOW */ { 0x09c1, 0x0000, 0x0000, 0x09c1 }, /* 1ecc LATIN CAPITAL LETTER O WITH DOT BELOW */ { 0x0000, 0x09c3, 0x09c3, 0x0000 }, /* 1ecd LATIN SMALL LETTER O WITH DOT BELOW */ { 0x09c5, 0x0000, 0x0000, 0x09c5 }, /* 1ece LATIN CAPITAL LETTER O WITH HOOK ABOVE */ { 0x0000, 0x09c7, 0x09c7, 0x0000 }, /* 1ecf LATIN SMALL LETTER O WITH HOOK ABOVE */ { 0x09c9, 0x0000, 0x0000, 0x09c9 }, /* 1ed0 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ { 0x0000, 0x09cb, 0x09cb, 0x0000 }, /* 1ed1 LATIN SMALL LETTER O WITH CIRCUMFLEX AN */ { 0x09cd, 0x0000, 0x0000, 0x09cd }, /* 1ed2 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ { 0x0000, 0x09cf, 0x09cf, 0x0000 }, /* 1ed3 LATIN SMALL LETTER O WITH CIRCUMFLEX AN */ { 0x09d1, 0x0000, 0x0000, 0x09d1 }, /* 1ed4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ { 0x0000, 0x09d3, 0x09d3, 0x0000 }, /* 1ed5 LATIN SMALL LETTER O WITH CIRCUMFLEX AN */ { 0x09d5, 0x0000, 0x0000, 0x09d5 }, /* 1ed6 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ { 0x0000, 0x09d7, 0x09d7, 0x0000 }, /* 1ed7 LATIN SMALL LETTER O WITH CIRCUMFLEX AN */ { 0x09d9, 0x0000, 0x0000, 0x09d9 }, /* 1ed8 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ { 0x0000, 0x09db, 0x09db, 0x0000 }, /* 1ed9 LATIN SMALL LETTER O WITH CIRCUMFLEX AN */ { 0x09dd, 0x0000, 0x0000, 0x09dd }, /* 1eda LATIN CAPITAL LETTER O WITH HORN AND AC */ { 0x0000, 0x09df, 0x09df, 0x0000 }, /* 1edb LATIN SMALL LETTER O WITH HORN AND ACUT */ { 0x09e1, 0x0000, 0x0000, 0x09e1 }, /* 1edc LATIN CAPITAL LETTER O WITH HORN AND GR */ { 0x0000, 0x09e3, 0x09e3, 0x0000 }, /* 1edd LATIN SMALL LETTER O WITH HORN AND GRAV */ { 0x09e5, 0x0000, 0x0000, 0x09e5 }, /* 1ede LATIN CAPITAL LETTER O WITH HORN AND HO */ { 0x0000, 0x09e7, 0x09e7, 0x0000 }, /* 1edf LATIN SMALL LETTER O WITH HORN AND HOOK */ { 0x09e9, 0x0000, 0x0000, 0x09e9 }, /* 1ee0 LATIN CAPITAL LETTER O WITH HORN AND TI */ { 0x0000, 0x09eb, 0x09eb, 0x0000 }, /* 1ee1 LATIN SMALL LETTER O WITH HORN AND TILD */ { 0x09ed, 0x0000, 0x0000, 0x09ed }, /* 1ee2 LATIN CAPITAL LETTER O WITH HORN AND DO */ { 0x0000, 0x09ef, 0x09ef, 0x0000 }, /* 1ee3 LATIN SMALL LETTER O WITH HORN AND DOT */ { 0x09f1, 0x0000, 0x0000, 0x09f1 }, /* 1ee4 LATIN CAPITAL LETTER U WITH DOT BELOW */ { 0x0000, 0x09f3, 0x09f3, 0x0000 }, /* 1ee5 LATIN SMALL LETTER U WITH DOT BELOW */ { 0x09f5, 0x0000, 0x0000, 0x09f5 }, /* 1ee6 LATIN CAPITAL LETTER U WITH HOOK ABOVE */ { 0x0000, 0x09f7, 0x09f7, 0x0000 }, /* 1ee7 LATIN SMALL LETTER U WITH HOOK ABOVE */ { 0x09f9, 0x0000, 0x0000, 0x09f9 }, /* 1ee8 LATIN CAPITAL LETTER U WITH HORN AND AC */ { 0x0000, 0x09fb, 0x09fb, 0x0000 }, /* 1ee9 LATIN SMALL LETTER U WITH HORN AND ACUT */ { 0x09fd, 0x0000, 0x0000, 0x09fd }, /* 1eea LATIN CAPITAL LETTER U WITH HORN AND GR */ { 0x0000, 0x09ff, 0x09ff, 0x0000 }, /* 1eeb LATIN SMALL LETTER U WITH HORN AND GRAV */ { 0x0a01, 0x0000, 0x0000, 0x0a01 }, /* 1eec LATIN CAPITAL LETTER U WITH HORN AND HO */ { 0x0000, 0x0a03, 0x0a03, 0x0000 }, /* 1eed LATIN SMALL LETTER U WITH HORN AND HOOK */ { 0x0a05, 0x0000, 0x0000, 0x0a05 }, /* 1eee LATIN CAPITAL LETTER U WITH HORN AND TI */ { 0x0000, 0x0a07, 0x0a07, 0x0000 }, /* 1eef LATIN SMALL LETTER U WITH HORN AND TILD */ { 0x0a09, 0x0000, 0x0000, 0x0a09 }, /* 1ef0 LATIN CAPITAL LETTER U WITH HORN AND DO */ { 0x0000, 0x0a0b, 0x0a0b, 0x0000 }, /* 1ef1 LATIN SMALL LETTER U WITH HORN AND DOT */ { 0x0a0d, 0x0000, 0x0000, 0x0a0d }, /* 1ef2 LATIN CAPITAL LETTER Y WITH GRAVE */ { 0x0000, 0x0a0f, 0x0a0f, 0x0000 }, /* 1ef3 LATIN SMALL LETTER Y WITH GRAVE */ { 0x0a11, 0x0000, 0x0000, 0x0a11 }, /* 1ef4 LATIN CAPITAL LETTER Y WITH DOT BELOW */ { 0x0000, 0x0a13, 0x0a13, 0x0000 }, /* 1ef5 LATIN SMALL LETTER Y WITH DOT BELOW */ { 0x0a15, 0x0000, 0x0000, 0x0a15 }, /* 1ef6 LATIN CAPITAL LETTER Y WITH HOOK ABOVE */ { 0x0000, 0x0a17, 0x0a17, 0x0000 }, /* 1ef7 LATIN SMALL LETTER Y WITH HOOK ABOVE */ { 0x0a19, 0x0000, 0x0000, 0x0a19 }, /* 1ef8 LATIN CAPITAL LETTER Y WITH TILDE */ { 0x0000, 0x0a1b, 0x0a1b, 0x0000 }, /* 1ef9 LATIN SMALL LETTER Y WITH TILDE */ { 0x0a1d, 0x0000, 0x0000, 0x0a1d }, /* 1efa LATIN CAPITAL LETTER MIDDLE-WELSH LL */ { 0x0000, 0x0a1f, 0x0a1f, 0x0000 }, /* 1efb LATIN SMALL LETTER MIDDLE-WELSH LL */ { 0x0a21, 0x0000, 0x0000, 0x0a21 }, /* 1efc LATIN CAPITAL LETTER MIDDLE-WELSH V */ { 0x0000, 0x0a23, 0x0a23, 0x0000 }, /* 1efd LATIN SMALL LETTER MIDDLE-WELSH V */ { 0x0a25, 0x0000, 0x0000, 0x0a25 }, /* 1efe LATIN CAPITAL LETTER Y WITH LOOP */ { 0x0000, 0x0a27, 0x0a27, 0x0000 } /* 1eff LATIN SMALL LETTER Y WITH LOOP */ }; static case_table case_pg_03e[128] = { { 0x0000, 0x0a29, 0x0a29, 0x0000 }, /* 1f00 GREEK SMALL LETTER ALPHA WITH PSILI */ { 0x0000, 0x0a2b, 0x0a2b, 0x0000 }, /* 1f01 GREEK SMALL LETTER ALPHA WITH DASIA */ { 0x0000, 0x0a2d, 0x0a2d, 0x0000 }, /* 1f02 GREEK SMALL LETTER ALPHA WITH PSILI AND */ { 0x0000, 0x0a2f, 0x0a2f, 0x0000 }, /* 1f03 GREEK SMALL LETTER ALPHA WITH DASIA AND */ { 0x0000, 0x0a31, 0x0a31, 0x0000 }, /* 1f04 GREEK SMALL LETTER ALPHA WITH PSILI AND */ { 0x0000, 0x0a33, 0x0a33, 0x0000 }, /* 1f05 GREEK SMALL LETTER ALPHA WITH DASIA AND */ { 0x0000, 0x0a35, 0x0a35, 0x0000 }, /* 1f06 GREEK SMALL LETTER ALPHA WITH PSILI AND */ { 0x0000, 0x0a37, 0x0a37, 0x0000 }, /* 1f07 GREEK SMALL LETTER ALPHA WITH DASIA AND */ { 0x0a39, 0x0000, 0x0000, 0x0a39 }, /* 1f08 GREEK CAPITAL LETTER ALPHA WITH PSILI */ { 0x0a3b, 0x0000, 0x0000, 0x0a3b }, /* 1f09 GREEK CAPITAL LETTER ALPHA WITH DASIA */ { 0x0a3d, 0x0000, 0x0000, 0x0a3d }, /* 1f0a GREEK CAPITAL LETTER ALPHA WITH PSILI A */ { 0x0a3f, 0x0000, 0x0000, 0x0a3f }, /* 1f0b GREEK CAPITAL LETTER ALPHA WITH DASIA A */ { 0x0a41, 0x0000, 0x0000, 0x0a41 }, /* 1f0c GREEK CAPITAL LETTER ALPHA WITH PSILI A */ { 0x0a43, 0x0000, 0x0000, 0x0a43 }, /* 1f0d GREEK CAPITAL LETTER ALPHA WITH DASIA A */ { 0x0a45, 0x0000, 0x0000, 0x0a45 }, /* 1f0e GREEK CAPITAL LETTER ALPHA WITH PSILI A */ { 0x0a47, 0x0000, 0x0000, 0x0a47 }, /* 1f0f GREEK CAPITAL LETTER ALPHA WITH DASIA A */ { 0x0000, 0x0a49, 0x0a49, 0x0000 }, /* 1f10 GREEK SMALL LETTER EPSILON WITH PSILI */ { 0x0000, 0x0a4b, 0x0a4b, 0x0000 }, /* 1f11 GREEK SMALL LETTER EPSILON WITH DASIA */ { 0x0000, 0x0a4d, 0x0a4d, 0x0000 }, /* 1f12 GREEK SMALL LETTER EPSILON WITH PSILI A */ { 0x0000, 0x0a4f, 0x0a4f, 0x0000 }, /* 1f13 GREEK SMALL LETTER EPSILON WITH DASIA A */ { 0x0000, 0x0a51, 0x0a51, 0x0000 }, /* 1f14 GREEK SMALL LETTER EPSILON WITH PSILI A */ { 0x0000, 0x0a53, 0x0a53, 0x0000 }, /* 1f15 GREEK SMALL LETTER EPSILON WITH DASIA A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f16 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f17 (null) */ { 0x0a55, 0x0000, 0x0000, 0x0a55 }, /* 1f18 GREEK CAPITAL LETTER EPSILON WITH PSILI */ { 0x0a57, 0x0000, 0x0000, 0x0a57 }, /* 1f19 GREEK CAPITAL LETTER EPSILON WITH DASIA */ { 0x0a59, 0x0000, 0x0000, 0x0a59 }, /* 1f1a GREEK CAPITAL LETTER EPSILON WITH PSILI */ { 0x0a5b, 0x0000, 0x0000, 0x0a5b }, /* 1f1b GREEK CAPITAL LETTER EPSILON WITH DASIA */ { 0x0a5d, 0x0000, 0x0000, 0x0a5d }, /* 1f1c GREEK CAPITAL LETTER EPSILON WITH PSILI */ { 0x0a5f, 0x0000, 0x0000, 0x0a5f }, /* 1f1d GREEK CAPITAL LETTER EPSILON WITH DASIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f1e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f1f (null) */ { 0x0000, 0x0a61, 0x0a61, 0x0000 }, /* 1f20 GREEK SMALL LETTER ETA WITH PSILI */ { 0x0000, 0x0a63, 0x0a63, 0x0000 }, /* 1f21 GREEK SMALL LETTER ETA WITH DASIA */ { 0x0000, 0x0a65, 0x0a65, 0x0000 }, /* 1f22 GREEK SMALL LETTER ETA WITH PSILI AND V */ { 0x0000, 0x0a67, 0x0a67, 0x0000 }, /* 1f23 GREEK SMALL LETTER ETA WITH DASIA AND V */ { 0x0000, 0x0a69, 0x0a69, 0x0000 }, /* 1f24 GREEK SMALL LETTER ETA WITH PSILI AND O */ { 0x0000, 0x0a6b, 0x0a6b, 0x0000 }, /* 1f25 GREEK SMALL LETTER ETA WITH DASIA AND O */ { 0x0000, 0x0a6d, 0x0a6d, 0x0000 }, /* 1f26 GREEK SMALL LETTER ETA WITH PSILI AND P */ { 0x0000, 0x0a6f, 0x0a6f, 0x0000 }, /* 1f27 GREEK SMALL LETTER ETA WITH DASIA AND P */ { 0x0a71, 0x0000, 0x0000, 0x0a71 }, /* 1f28 GREEK CAPITAL LETTER ETA WITH PSILI */ { 0x0a73, 0x0000, 0x0000, 0x0a73 }, /* 1f29 GREEK CAPITAL LETTER ETA WITH DASIA */ { 0x0a75, 0x0000, 0x0000, 0x0a75 }, /* 1f2a GREEK CAPITAL LETTER ETA WITH PSILI AND */ { 0x0a77, 0x0000, 0x0000, 0x0a77 }, /* 1f2b GREEK CAPITAL LETTER ETA WITH DASIA AND */ { 0x0a79, 0x0000, 0x0000, 0x0a79 }, /* 1f2c GREEK CAPITAL LETTER ETA WITH PSILI AND */ { 0x0a7b, 0x0000, 0x0000, 0x0a7b }, /* 1f2d GREEK CAPITAL LETTER ETA WITH DASIA AND */ { 0x0a7d, 0x0000, 0x0000, 0x0a7d }, /* 1f2e GREEK CAPITAL LETTER ETA WITH PSILI AND */ { 0x0a7f, 0x0000, 0x0000, 0x0a7f }, /* 1f2f GREEK CAPITAL LETTER ETA WITH DASIA AND */ { 0x0000, 0x0a81, 0x0a81, 0x0000 }, /* 1f30 GREEK SMALL LETTER IOTA WITH PSILI */ { 0x0000, 0x0a83, 0x0a83, 0x0000 }, /* 1f31 GREEK SMALL LETTER IOTA WITH DASIA */ { 0x0000, 0x0a85, 0x0a85, 0x0000 }, /* 1f32 GREEK SMALL LETTER IOTA WITH PSILI AND */ { 0x0000, 0x0a87, 0x0a87, 0x0000 }, /* 1f33 GREEK SMALL LETTER IOTA WITH DASIA AND */ { 0x0000, 0x0a89, 0x0a89, 0x0000 }, /* 1f34 GREEK SMALL LETTER IOTA WITH PSILI AND */ { 0x0000, 0x0a8b, 0x0a8b, 0x0000 }, /* 1f35 GREEK SMALL LETTER IOTA WITH DASIA AND */ { 0x0000, 0x0a8d, 0x0a8d, 0x0000 }, /* 1f36 GREEK SMALL LETTER IOTA WITH PSILI AND */ { 0x0000, 0x0a8f, 0x0a8f, 0x0000 }, /* 1f37 GREEK SMALL LETTER IOTA WITH DASIA AND */ { 0x0a91, 0x0000, 0x0000, 0x0a91 }, /* 1f38 GREEK CAPITAL LETTER IOTA WITH PSILI */ { 0x0a93, 0x0000, 0x0000, 0x0a93 }, /* 1f39 GREEK CAPITAL LETTER IOTA WITH DASIA */ { 0x0a95, 0x0000, 0x0000, 0x0a95 }, /* 1f3a GREEK CAPITAL LETTER IOTA WITH PSILI AN */ { 0x0a97, 0x0000, 0x0000, 0x0a97 }, /* 1f3b GREEK CAPITAL LETTER IOTA WITH DASIA AN */ { 0x0a99, 0x0000, 0x0000, 0x0a99 }, /* 1f3c GREEK CAPITAL LETTER IOTA WITH PSILI AN */ { 0x0a9b, 0x0000, 0x0000, 0x0a9b }, /* 1f3d GREEK CAPITAL LETTER IOTA WITH DASIA AN */ { 0x0a9d, 0x0000, 0x0000, 0x0a9d }, /* 1f3e GREEK CAPITAL LETTER IOTA WITH PSILI AN */ { 0x0a9f, 0x0000, 0x0000, 0x0a9f }, /* 1f3f GREEK CAPITAL LETTER IOTA WITH DASIA AN */ { 0x0000, 0x0aa1, 0x0aa1, 0x0000 }, /* 1f40 GREEK SMALL LETTER OMICRON WITH PSILI */ { 0x0000, 0x0aa3, 0x0aa3, 0x0000 }, /* 1f41 GREEK SMALL LETTER OMICRON WITH DASIA */ { 0x0000, 0x0aa5, 0x0aa5, 0x0000 }, /* 1f42 GREEK SMALL LETTER OMICRON WITH PSILI A */ { 0x0000, 0x0aa7, 0x0aa7, 0x0000 }, /* 1f43 GREEK SMALL LETTER OMICRON WITH DASIA A */ { 0x0000, 0x0aa9, 0x0aa9, 0x0000 }, /* 1f44 GREEK SMALL LETTER OMICRON WITH PSILI A */ { 0x0000, 0x0aab, 0x0aab, 0x0000 }, /* 1f45 GREEK SMALL LETTER OMICRON WITH DASIA A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f46 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f47 (null) */ { 0x0aad, 0x0000, 0x0000, 0x0aad }, /* 1f48 GREEK CAPITAL LETTER OMICRON WITH PSILI */ { 0x0aaf, 0x0000, 0x0000, 0x0aaf }, /* 1f49 GREEK CAPITAL LETTER OMICRON WITH DASIA */ { 0x0ab1, 0x0000, 0x0000, 0x0ab1 }, /* 1f4a GREEK CAPITAL LETTER OMICRON WITH PSILI */ { 0x0ab3, 0x0000, 0x0000, 0x0ab3 }, /* 1f4b GREEK CAPITAL LETTER OMICRON WITH DASIA */ { 0x0ab5, 0x0000, 0x0000, 0x0ab5 }, /* 1f4c GREEK CAPITAL LETTER OMICRON WITH PSILI */ { 0x0ab7, 0x0000, 0x0000, 0x0ab7 }, /* 1f4d GREEK CAPITAL LETTER OMICRON WITH DASIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f4e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f4f (null) */ { 0x0000, 0x0ab9, 0x0ab9, 0x0abc }, /* 1f50 GREEK SMALL LETTER UPSILON WITH PSILI */ { 0x0000, 0x0abf, 0x0abf, 0x0000 }, /* 1f51 GREEK SMALL LETTER UPSILON WITH DASIA */ { 0x0000, 0x0ac1, 0x0ac1, 0x0ac5 }, /* 1f52 GREEK SMALL LETTER UPSILON WITH PSILI A */ { 0x0000, 0x0ac9, 0x0ac9, 0x0000 }, /* 1f53 GREEK SMALL LETTER UPSILON WITH DASIA A */ { 0x0000, 0x0acb, 0x0acb, 0x0acf }, /* 1f54 GREEK SMALL LETTER UPSILON WITH PSILI A */ { 0x0000, 0x0ad3, 0x0ad3, 0x0000 }, /* 1f55 GREEK SMALL LETTER UPSILON WITH DASIA A */ { 0x0000, 0x0ad5, 0x0ad5, 0x0ad9 }, /* 1f56 GREEK SMALL LETTER UPSILON WITH PSILI A */ { 0x0000, 0x0add, 0x0add, 0x0000 }, /* 1f57 GREEK SMALL LETTER UPSILON WITH DASIA A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f58 (null) */ { 0x0adf, 0x0000, 0x0000, 0x0adf }, /* 1f59 GREEK CAPITAL LETTER UPSILON WITH DASIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f5a (null) */ { 0x0ae1, 0x0000, 0x0000, 0x0ae1 }, /* 1f5b GREEK CAPITAL LETTER UPSILON WITH DASIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f5c (null) */ { 0x0ae3, 0x0000, 0x0000, 0x0ae3 }, /* 1f5d GREEK CAPITAL LETTER UPSILON WITH DASIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f5e (null) */ { 0x0ae5, 0x0000, 0x0000, 0x0ae5 }, /* 1f5f GREEK CAPITAL LETTER UPSILON WITH DASIA */ { 0x0000, 0x0ae7, 0x0ae7, 0x0000 }, /* 1f60 GREEK SMALL LETTER OMEGA WITH PSILI */ { 0x0000, 0x0ae9, 0x0ae9, 0x0000 }, /* 1f61 GREEK SMALL LETTER OMEGA WITH DASIA */ { 0x0000, 0x0aeb, 0x0aeb, 0x0000 }, /* 1f62 GREEK SMALL LETTER OMEGA WITH PSILI AND */ { 0x0000, 0x0aed, 0x0aed, 0x0000 }, /* 1f63 GREEK SMALL LETTER OMEGA WITH DASIA AND */ { 0x0000, 0x0aef, 0x0aef, 0x0000 }, /* 1f64 GREEK SMALL LETTER OMEGA WITH PSILI AND */ { 0x0000, 0x0af1, 0x0af1, 0x0000 }, /* 1f65 GREEK SMALL LETTER OMEGA WITH DASIA AND */ { 0x0000, 0x0af3, 0x0af3, 0x0000 }, /* 1f66 GREEK SMALL LETTER OMEGA WITH PSILI AND */ { 0x0000, 0x0af5, 0x0af5, 0x0000 }, /* 1f67 GREEK SMALL LETTER OMEGA WITH DASIA AND */ { 0x0af7, 0x0000, 0x0000, 0x0af7 }, /* 1f68 GREEK CAPITAL LETTER OMEGA WITH PSILI */ { 0x0af9, 0x0000, 0x0000, 0x0af9 }, /* 1f69 GREEK CAPITAL LETTER OMEGA WITH DASIA */ { 0x0afb, 0x0000, 0x0000, 0x0afb }, /* 1f6a GREEK CAPITAL LETTER OMEGA WITH PSILI A */ { 0x0afd, 0x0000, 0x0000, 0x0afd }, /* 1f6b GREEK CAPITAL LETTER OMEGA WITH DASIA A */ { 0x0aff, 0x0000, 0x0000, 0x0aff }, /* 1f6c GREEK CAPITAL LETTER OMEGA WITH PSILI A */ { 0x0b01, 0x0000, 0x0000, 0x0b01 }, /* 1f6d GREEK CAPITAL LETTER OMEGA WITH DASIA A */ { 0x0b03, 0x0000, 0x0000, 0x0b03 }, /* 1f6e GREEK CAPITAL LETTER OMEGA WITH PSILI A */ { 0x0b05, 0x0000, 0x0000, 0x0b05 }, /* 1f6f GREEK CAPITAL LETTER OMEGA WITH DASIA A */ { 0x0000, 0x0b07, 0x0b07, 0x0000 }, /* 1f70 GREEK SMALL LETTER ALPHA WITH VARIA */ { 0x0000, 0x0b09, 0x0b09, 0x0000 }, /* 1f71 GREEK SMALL LETTER ALPHA WITH OXIA */ { 0x0000, 0x0b0b, 0x0b0b, 0x0000 }, /* 1f72 GREEK SMALL LETTER EPSILON WITH VARIA */ { 0x0000, 0x0b0d, 0x0b0d, 0x0000 }, /* 1f73 GREEK SMALL LETTER EPSILON WITH OXIA */ { 0x0000, 0x0b0f, 0x0b0f, 0x0000 }, /* 1f74 GREEK SMALL LETTER ETA WITH VARIA */ { 0x0000, 0x0b11, 0x0b11, 0x0000 }, /* 1f75 GREEK SMALL LETTER ETA WITH OXIA */ { 0x0000, 0x0b13, 0x0b13, 0x0000 }, /* 1f76 GREEK SMALL LETTER IOTA WITH VARIA */ { 0x0000, 0x0b15, 0x0b15, 0x0000 }, /* 1f77 GREEK SMALL LETTER IOTA WITH OXIA */ { 0x0000, 0x0b17, 0x0b17, 0x0000 }, /* 1f78 GREEK SMALL LETTER OMICRON WITH VARIA */ { 0x0000, 0x0b19, 0x0b19, 0x0000 }, /* 1f79 GREEK SMALL LETTER OMICRON WITH OXIA */ { 0x0000, 0x0b1b, 0x0b1b, 0x0000 }, /* 1f7a GREEK SMALL LETTER UPSILON WITH VARIA */ { 0x0000, 0x0b1d, 0x0b1d, 0x0000 }, /* 1f7b GREEK SMALL LETTER UPSILON WITH OXIA */ { 0x0000, 0x0b1f, 0x0b1f, 0x0000 }, /* 1f7c GREEK SMALL LETTER OMEGA WITH VARIA */ { 0x0000, 0x0b21, 0x0b21, 0x0000 }, /* 1f7d GREEK SMALL LETTER OMEGA WITH OXIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f7e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 1f7f (null) */ }; static case_table case_pg_03f[128] = { { 0x0000, 0x0b23, 0x0b25, 0x0b28 }, /* 1f80 GREEK SMALL LETTER ALPHA WITH PSILI AND */ { 0x0000, 0x0b2b, 0x0b2d, 0x0b30 }, /* 1f81 GREEK SMALL LETTER ALPHA WITH DASIA AND */ { 0x0000, 0x0b33, 0x0b35, 0x0b38 }, /* 1f82 GREEK SMALL LETTER ALPHA WITH PSILI AND */ { 0x0000, 0x0b3b, 0x0b3d, 0x0b40 }, /* 1f83 GREEK SMALL LETTER ALPHA WITH DASIA AND */ { 0x0000, 0x0b43, 0x0b45, 0x0b48 }, /* 1f84 GREEK SMALL LETTER ALPHA WITH PSILI AND */ { 0x0000, 0x0b4b, 0x0b4d, 0x0b50 }, /* 1f85 GREEK SMALL LETTER ALPHA WITH DASIA AND */ { 0x0000, 0x0b53, 0x0b55, 0x0b58 }, /* 1f86 GREEK SMALL LETTER ALPHA WITH PSILI AND */ { 0x0000, 0x0b5b, 0x0b5d, 0x0b60 }, /* 1f87 GREEK SMALL LETTER ALPHA WITH DASIA AND */ { 0x0b63, 0x0000, 0x0b65, 0x0b68 }, /* 1f88 GREEK CAPITAL LETTER ALPHA WITH PSILI A */ { 0x0b6b, 0x0000, 0x0b6d, 0x0b70 }, /* 1f89 GREEK CAPITAL LETTER ALPHA WITH DASIA A */ { 0x0b73, 0x0000, 0x0b75, 0x0b78 }, /* 1f8a GREEK CAPITAL LETTER ALPHA WITH PSILI A */ { 0x0b7b, 0x0000, 0x0b7d, 0x0b80 }, /* 1f8b GREEK CAPITAL LETTER ALPHA WITH DASIA A */ { 0x0b83, 0x0000, 0x0b85, 0x0b88 }, /* 1f8c GREEK CAPITAL LETTER ALPHA WITH PSILI A */ { 0x0b8b, 0x0000, 0x0b8d, 0x0b90 }, /* 1f8d GREEK CAPITAL LETTER ALPHA WITH DASIA A */ { 0x0b93, 0x0000, 0x0b95, 0x0b98 }, /* 1f8e GREEK CAPITAL LETTER ALPHA WITH PSILI A */ { 0x0b9b, 0x0000, 0x0b9d, 0x0ba0 }, /* 1f8f GREEK CAPITAL LETTER ALPHA WITH DASIA A */ { 0x0000, 0x0ba3, 0x0ba5, 0x0ba8 }, /* 1f90 GREEK SMALL LETTER ETA WITH PSILI AND Y */ { 0x0000, 0x0bab, 0x0bad, 0x0bb0 }, /* 1f91 GREEK SMALL LETTER ETA WITH DASIA AND Y */ { 0x0000, 0x0bb3, 0x0bb5, 0x0bb8 }, /* 1f92 GREEK SMALL LETTER ETA WITH PSILI AND V */ { 0x0000, 0x0bbb, 0x0bbd, 0x0bc0 }, /* 1f93 GREEK SMALL LETTER ETA WITH DASIA AND V */ { 0x0000, 0x0bc3, 0x0bc5, 0x0bc8 }, /* 1f94 GREEK SMALL LETTER ETA WITH PSILI AND O */ { 0x0000, 0x0bcb, 0x0bcd, 0x0bd0 }, /* 1f95 GREEK SMALL LETTER ETA WITH DASIA AND O */ { 0x0000, 0x0bd3, 0x0bd5, 0x0bd8 }, /* 1f96 GREEK SMALL LETTER ETA WITH PSILI AND P */ { 0x0000, 0x0bdb, 0x0bdd, 0x0be0 }, /* 1f97 GREEK SMALL LETTER ETA WITH DASIA AND P */ { 0x0be3, 0x0000, 0x0be5, 0x0be8 }, /* 1f98 GREEK CAPITAL LETTER ETA WITH PSILI AND */ { 0x0beb, 0x0000, 0x0bed, 0x0bf0 }, /* 1f99 GREEK CAPITAL LETTER ETA WITH DASIA AND */ { 0x0bf3, 0x0000, 0x0bf5, 0x0bf8 }, /* 1f9a GREEK CAPITAL LETTER ETA WITH PSILI AND */ { 0x0bfb, 0x0000, 0x0bfd, 0x0c00 }, /* 1f9b GREEK CAPITAL LETTER ETA WITH DASIA AND */ { 0x0c03, 0x0000, 0x0c05, 0x0c08 }, /* 1f9c GREEK CAPITAL LETTER ETA WITH PSILI AND */ { 0x0c0b, 0x0000, 0x0c0d, 0x0c10 }, /* 1f9d GREEK CAPITAL LETTER ETA WITH DASIA AND */ { 0x0c13, 0x0000, 0x0c15, 0x0c18 }, /* 1f9e GREEK CAPITAL LETTER ETA WITH PSILI AND */ { 0x0c1b, 0x0000, 0x0c1d, 0x0c20 }, /* 1f9f GREEK CAPITAL LETTER ETA WITH DASIA AND */ { 0x0000, 0x0c23, 0x0c25, 0x0c28 }, /* 1fa0 GREEK SMALL LETTER OMEGA WITH PSILI AND */ { 0x0000, 0x0c2b, 0x0c2d, 0x0c30 }, /* 1fa1 GREEK SMALL LETTER OMEGA WITH DASIA AND */ { 0x0000, 0x0c33, 0x0c35, 0x0c38 }, /* 1fa2 GREEK SMALL LETTER OMEGA WITH PSILI AND */ { 0x0000, 0x0c3b, 0x0c3d, 0x0c40 }, /* 1fa3 GREEK SMALL LETTER OMEGA WITH DASIA AND */ { 0x0000, 0x0c43, 0x0c45, 0x0c48 }, /* 1fa4 GREEK SMALL LETTER OMEGA WITH PSILI AND */ { 0x0000, 0x0c4b, 0x0c4d, 0x0c50 }, /* 1fa5 GREEK SMALL LETTER OMEGA WITH DASIA AND */ { 0x0000, 0x0c53, 0x0c55, 0x0c58 }, /* 1fa6 GREEK SMALL LETTER OMEGA WITH PSILI AND */ { 0x0000, 0x0c5b, 0x0c5d, 0x0c60 }, /* 1fa7 GREEK SMALL LETTER OMEGA WITH DASIA AND */ { 0x0c63, 0x0000, 0x0c65, 0x0c68 }, /* 1fa8 GREEK CAPITAL LETTER OMEGA WITH PSILI A */ { 0x0c6b, 0x0000, 0x0c6d, 0x0c70 }, /* 1fa9 GREEK CAPITAL LETTER OMEGA WITH DASIA A */ { 0x0c73, 0x0000, 0x0c75, 0x0c78 }, /* 1faa GREEK CAPITAL LETTER OMEGA WITH PSILI A */ { 0x0c7b, 0x0000, 0x0c7d, 0x0c80 }, /* 1fab GREEK CAPITAL LETTER OMEGA WITH DASIA A */ { 0x0c83, 0x0000, 0x0c85, 0x0c88 }, /* 1fac GREEK CAPITAL LETTER OMEGA WITH PSILI A */ { 0x0c8b, 0x0000, 0x0c8d, 0x0c90 }, /* 1fad GREEK CAPITAL LETTER OMEGA WITH DASIA A */ { 0x0c93, 0x0000, 0x0c95, 0x0c98 }, /* 1fae GREEK CAPITAL LETTER OMEGA WITH PSILI A */ { 0x0c9b, 0x0000, 0x0c9d, 0x0ca0 }, /* 1faf GREEK CAPITAL LETTER OMEGA WITH DASIA A */ { 0x0000, 0x0ca3, 0x0ca3, 0x0000 }, /* 1fb0 GREEK SMALL LETTER ALPHA WITH VRACHY */ { 0x0000, 0x0ca5, 0x0ca5, 0x0000 }, /* 1fb1 GREEK SMALL LETTER ALPHA WITH MACRON */ { 0x0000, 0x0ca7, 0x0caa, 0x0cad }, /* 1fb2 GREEK SMALL LETTER ALPHA WITH VARIA AND */ { 0x0000, 0x0cb0, 0x0cb2, 0x0cb5 }, /* 1fb3 GREEK SMALL LETTER ALPHA WITH YPOGEGRAM */ { 0x0000, 0x0cb8, 0x0cbb, 0x0cbe }, /* 1fb4 GREEK SMALL LETTER ALPHA WITH OXIA AND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fb5 (null) */ { 0x0000, 0x0cc1, 0x0cc1, 0x0cc4 }, /* 1fb6 GREEK SMALL LETTER ALPHA WITH PERISPOME */ { 0x0000, 0x0cc7, 0x0ccb, 0x0ccf }, /* 1fb7 GREEK SMALL LETTER ALPHA WITH PERISPOME */ { 0x0cd3, 0x0000, 0x0000, 0x0cd3 }, /* 1fb8 GREEK CAPITAL LETTER ALPHA WITH VRACHY */ { 0x0cd5, 0x0000, 0x0000, 0x0cd5 }, /* 1fb9 GREEK CAPITAL LETTER ALPHA WITH MACRON */ { 0x0cd7, 0x0000, 0x0000, 0x0cd7 }, /* 1fba GREEK CAPITAL LETTER ALPHA WITH VARIA */ { 0x0cd9, 0x0000, 0x0000, 0x0cd9 }, /* 1fbb GREEK CAPITAL LETTER ALPHA WITH OXIA */ { 0x0cdb, 0x0000, 0x0cdd, 0x0ce0 }, /* 1fbc GREEK CAPITAL LETTER ALPHA WITH PROSGEG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fbd GREEK KORONIS */ { 0x0000, 0x0ce3, 0x0ce3, 0x0ce5 }, /* 1fbe GREEK PROSGEGRAMMENI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fbf GREEK PSILI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fc0 GREEK PERISPOMENI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fc1 GREEK DIALYTIKA AND PERISPOMENI */ { 0x0000, 0x0ce7, 0x0cea, 0x0ced }, /* 1fc2 GREEK SMALL LETTER ETA WITH VARIA AND Y */ { 0x0000, 0x0cf0, 0x0cf2, 0x0cf5 }, /* 1fc3 GREEK SMALL LETTER ETA WITH YPOGEGRAMME */ { 0x0000, 0x0cf8, 0x0cfb, 0x0cfe }, /* 1fc4 GREEK SMALL LETTER ETA WITH OXIA AND YP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fc5 (null) */ { 0x0000, 0x0d01, 0x0d01, 0x0d04 }, /* 1fc6 GREEK SMALL LETTER ETA WITH PERISPOMENI */ { 0x0000, 0x0d07, 0x0d0b, 0x0d0f }, /* 1fc7 GREEK SMALL LETTER ETA WITH PERISPOMENI */ { 0x0d13, 0x0000, 0x0000, 0x0d13 }, /* 1fc8 GREEK CAPITAL LETTER EPSILON WITH VARIA */ { 0x0d15, 0x0000, 0x0000, 0x0d15 }, /* 1fc9 GREEK CAPITAL LETTER EPSILON WITH OXIA */ { 0x0d17, 0x0000, 0x0000, 0x0d17 }, /* 1fca GREEK CAPITAL LETTER ETA WITH VARIA */ { 0x0d19, 0x0000, 0x0000, 0x0d19 }, /* 1fcb GREEK CAPITAL LETTER ETA WITH OXIA */ { 0x0d1b, 0x0000, 0x0d1d, 0x0d20 }, /* 1fcc GREEK CAPITAL LETTER ETA WITH PROSGEGRA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fcd GREEK PSILI AND VARIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fce GREEK PSILI AND OXIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fcf GREEK PSILI AND PERISPOMENI */ { 0x0000, 0x0d23, 0x0d23, 0x0000 }, /* 1fd0 GREEK SMALL LETTER IOTA WITH VRACHY */ { 0x0000, 0x0d25, 0x0d25, 0x0000 }, /* 1fd1 GREEK SMALL LETTER IOTA WITH MACRON */ { 0x0000, 0x0d27, 0x0d27, 0x0d2b }, /* 1fd2 GREEK SMALL LETTER IOTA WITH DIALYTIKA */ { 0x0000, 0x0d2f, 0x0d2f, 0x0d33 }, /* 1fd3 GREEK SMALL LETTER IOTA WITH DIALYTIKA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fd4 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fd5 (null) */ { 0x0000, 0x0d37, 0x0d37, 0x0d3a }, /* 1fd6 GREEK SMALL LETTER IOTA WITH PERISPOMEN */ { 0x0000, 0x0d3d, 0x0d3d, 0x0d41 }, /* 1fd7 GREEK SMALL LETTER IOTA WITH DIALYTIKA */ { 0x0d45, 0x0000, 0x0000, 0x0d45 }, /* 1fd8 GREEK CAPITAL LETTER IOTA WITH VRACHY */ { 0x0d47, 0x0000, 0x0000, 0x0d47 }, /* 1fd9 GREEK CAPITAL LETTER IOTA WITH MACRON */ { 0x0d49, 0x0000, 0x0000, 0x0d49 }, /* 1fda GREEK CAPITAL LETTER IOTA WITH VARIA */ { 0x0d4b, 0x0000, 0x0000, 0x0d4b }, /* 1fdb GREEK CAPITAL LETTER IOTA WITH OXIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fdc (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fdd GREEK DASIA AND VARIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fde GREEK DASIA AND OXIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fdf GREEK DASIA AND PERISPOMENI */ { 0x0000, 0x0d4d, 0x0d4d, 0x0000 }, /* 1fe0 GREEK SMALL LETTER UPSILON WITH VRACHY */ { 0x0000, 0x0d4f, 0x0d4f, 0x0000 }, /* 1fe1 GREEK SMALL LETTER UPSILON WITH MACRON */ { 0x0000, 0x0d51, 0x0d51, 0x0d55 }, /* 1fe2 GREEK SMALL LETTER UPSILON WITH DIALYTI */ { 0x0000, 0x0d59, 0x0d59, 0x0d5d }, /* 1fe3 GREEK SMALL LETTER UPSILON WITH DIALYTI */ { 0x0000, 0x0d61, 0x0d61, 0x0d64 }, /* 1fe4 GREEK SMALL LETTER RHO WITH PSILI */ { 0x0000, 0x0d67, 0x0d67, 0x0000 }, /* 1fe5 GREEK SMALL LETTER RHO WITH DASIA */ { 0x0000, 0x0d69, 0x0d69, 0x0d6c }, /* 1fe6 GREEK SMALL LETTER UPSILON WITH PERISPO */ { 0x0000, 0x0d6f, 0x0d6f, 0x0d73 }, /* 1fe7 GREEK SMALL LETTER UPSILON WITH DIALYTI */ { 0x0d77, 0x0000, 0x0000, 0x0d77 }, /* 1fe8 GREEK CAPITAL LETTER UPSILON WITH VRACH */ { 0x0d79, 0x0000, 0x0000, 0x0d79 }, /* 1fe9 GREEK CAPITAL LETTER UPSILON WITH MACRO */ { 0x0d7b, 0x0000, 0x0000, 0x0d7b }, /* 1fea GREEK CAPITAL LETTER UPSILON WITH VARIA */ { 0x0d7d, 0x0000, 0x0000, 0x0d7d }, /* 1feb GREEK CAPITAL LETTER UPSILON WITH OXIA */ { 0x0d7f, 0x0000, 0x0000, 0x0d7f }, /* 1fec GREEK CAPITAL LETTER RHO WITH DASIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fed GREEK DIALYTIKA AND VARIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fee GREEK DIALYTIKA AND OXIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fef GREEK VARIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1ff0 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1ff1 (null) */ { 0x0000, 0x0d81, 0x0d84, 0x0d87 }, /* 1ff2 GREEK SMALL LETTER OMEGA WITH VARIA AND */ { 0x0000, 0x0d8a, 0x0d8c, 0x0d8f }, /* 1ff3 GREEK SMALL LETTER OMEGA WITH YPOGEGRAM */ { 0x0000, 0x0d92, 0x0d95, 0x0d98 }, /* 1ff4 GREEK SMALL LETTER OMEGA WITH OXIA AND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1ff5 (null) */ { 0x0000, 0x0d9b, 0x0d9b, 0x0d9e }, /* 1ff6 GREEK SMALL LETTER OMEGA WITH PERISPOME */ { 0x0000, 0x0da1, 0x0da5, 0x0da9 }, /* 1ff7 GREEK SMALL LETTER OMEGA WITH PERISPOME */ { 0x0dad, 0x0000, 0x0000, 0x0dad }, /* 1ff8 GREEK CAPITAL LETTER OMICRON WITH VARIA */ { 0x0daf, 0x0000, 0x0000, 0x0daf }, /* 1ff9 GREEK CAPITAL LETTER OMICRON WITH OXIA */ { 0x0db1, 0x0000, 0x0000, 0x0db1 }, /* 1ffa GREEK CAPITAL LETTER OMEGA WITH VARIA */ { 0x0db3, 0x0000, 0x0000, 0x0db3 }, /* 1ffb GREEK CAPITAL LETTER OMEGA WITH OXIA */ { 0x0db5, 0x0000, 0x0db7, 0x0dba }, /* 1ffc GREEK CAPITAL LETTER OMEGA WITH PROSGEG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1ffd GREEK OXIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1ffe GREEK DASIA */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 1fff (null) */ }; static case_table case_pg_042[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2100 ACCOUNT OF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2101 ADDRESSED TO THE SUBJECT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2102 DOUBLE-STRUCK CAPITAL C */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2103 DEGREE CELSIUS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2104 CENTRE LINE SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2105 CARE OF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2106 CADA UNA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2107 EULER CONSTANT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2108 SCRUPLE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2109 DEGREE FAHRENHEIT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 210a SCRIPT SMALL G */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 210b SCRIPT CAPITAL H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 210c BLACK-LETTER CAPITAL H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 210d DOUBLE-STRUCK CAPITAL H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 210e PLANCK CONSTANT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 210f PLANCK CONSTANT OVER TWO PI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2110 SCRIPT CAPITAL I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2111 BLACK-LETTER CAPITAL I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2112 SCRIPT CAPITAL L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2113 SCRIPT SMALL L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2114 L B BAR SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2115 DOUBLE-STRUCK CAPITAL N */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2116 NUMERO SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2117 SOUND RECORDING COPYRIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2118 SCRIPT CAPITAL P */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2119 DOUBLE-STRUCK CAPITAL P */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 211a DOUBLE-STRUCK CAPITAL Q */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 211b SCRIPT CAPITAL R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 211c BLACK-LETTER CAPITAL R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 211d DOUBLE-STRUCK CAPITAL R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 211e PRESCRIPTION TAKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 211f RESPONSE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2120 SERVICE MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2121 TELEPHONE SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2122 TRADE MARK SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2123 VERSICLE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2124 DOUBLE-STRUCK CAPITAL Z */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2125 OUNCE SIGN */ { 0x0dbd, 0x0000, 0x0000, 0x0dbd }, /* 2126 OHM SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2127 INVERTED OHM SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2128 BLACK-LETTER CAPITAL Z */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2129 TURNED GREEK SMALL LETTER IOTA */ { 0x0dbf, 0x0000, 0x0000, 0x0dbf }, /* 212a KELVIN SIGN */ { 0x0dc1, 0x0000, 0x0000, 0x0dc1 }, /* 212b ANGSTROM SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 212c SCRIPT CAPITAL B */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 212d BLACK-LETTER CAPITAL C */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 212e ESTIMATED SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 212f SCRIPT SMALL E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2130 SCRIPT CAPITAL E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2131 SCRIPT CAPITAL F */ { 0x0dc3, 0x0000, 0x0000, 0x0dc3 }, /* 2132 TURNED CAPITAL F */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2133 SCRIPT CAPITAL M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2134 SCRIPT SMALL O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2135 ALEF SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2136 BET SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2137 GIMEL SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2138 DALET SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2139 INFORMATION SOURCE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 213a ROTATED CAPITAL Q */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 213b FACSIMILE SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 213c DOUBLE-STRUCK SMALL PI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 213d DOUBLE-STRUCK SMALL GAMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 213e DOUBLE-STRUCK CAPITAL GAMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 213f DOUBLE-STRUCK CAPITAL PI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2140 DOUBLE-STRUCK N-ARY SUMMATION */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2141 TURNED SANS-SERIF CAPITAL G */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2142 TURNED SANS-SERIF CAPITAL L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2143 REVERSED SANS-SERIF CAPITAL L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2144 TURNED SANS-SERIF CAPITAL Y */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2145 DOUBLE-STRUCK ITALIC CAPITAL D */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2146 DOUBLE-STRUCK ITALIC SMALL D */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2147 DOUBLE-STRUCK ITALIC SMALL E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2148 DOUBLE-STRUCK ITALIC SMALL I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2149 DOUBLE-STRUCK ITALIC SMALL J */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 214a PROPERTY LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 214b TURNED AMPERSAND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 214c PER SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 214d AKTIESELSKAB */ { 0x0000, 0x0dc5, 0x0dc5, 0x0000 }, /* 214e TURNED SMALL F */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 214f SYMBOL FOR SAMARITAN SOURCE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2150 VULGAR FRACTION ONE SEVENTH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2151 VULGAR FRACTION ONE NINTH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2152 VULGAR FRACTION ONE TENTH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2153 VULGAR FRACTION ONE THIRD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2154 VULGAR FRACTION TWO THIRDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2155 VULGAR FRACTION ONE FIFTH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2156 VULGAR FRACTION TWO FIFTHS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2157 VULGAR FRACTION THREE FIFTHS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2158 VULGAR FRACTION FOUR FIFTHS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2159 VULGAR FRACTION ONE SIXTH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 215a VULGAR FRACTION FIVE SIXTHS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 215b VULGAR FRACTION ONE EIGHTH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 215c VULGAR FRACTION THREE EIGHTHS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 215d VULGAR FRACTION FIVE EIGHTHS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 215e VULGAR FRACTION SEVEN EIGHTHS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 215f FRACTION NUMERATOR ONE */ { 0x0dc7, 0x0000, 0x0000, 0x0dc7 }, /* 2160 ROMAN NUMERAL ONE */ { 0x0dc9, 0x0000, 0x0000, 0x0dc9 }, /* 2161 ROMAN NUMERAL TWO */ { 0x0dcb, 0x0000, 0x0000, 0x0dcb }, /* 2162 ROMAN NUMERAL THREE */ { 0x0dcd, 0x0000, 0x0000, 0x0dcd }, /* 2163 ROMAN NUMERAL FOUR */ { 0x0dcf, 0x0000, 0x0000, 0x0dcf }, /* 2164 ROMAN NUMERAL FIVE */ { 0x0dd1, 0x0000, 0x0000, 0x0dd1 }, /* 2165 ROMAN NUMERAL SIX */ { 0x0dd3, 0x0000, 0x0000, 0x0dd3 }, /* 2166 ROMAN NUMERAL SEVEN */ { 0x0dd5, 0x0000, 0x0000, 0x0dd5 }, /* 2167 ROMAN NUMERAL EIGHT */ { 0x0dd7, 0x0000, 0x0000, 0x0dd7 }, /* 2168 ROMAN NUMERAL NINE */ { 0x0dd9, 0x0000, 0x0000, 0x0dd9 }, /* 2169 ROMAN NUMERAL TEN */ { 0x0ddb, 0x0000, 0x0000, 0x0ddb }, /* 216a ROMAN NUMERAL ELEVEN */ { 0x0ddd, 0x0000, 0x0000, 0x0ddd }, /* 216b ROMAN NUMERAL TWELVE */ { 0x0ddf, 0x0000, 0x0000, 0x0ddf }, /* 216c ROMAN NUMERAL FIFTY */ { 0x0de1, 0x0000, 0x0000, 0x0de1 }, /* 216d ROMAN NUMERAL ONE HUNDRED */ { 0x0de3, 0x0000, 0x0000, 0x0de3 }, /* 216e ROMAN NUMERAL FIVE HUNDRED */ { 0x0de5, 0x0000, 0x0000, 0x0de5 }, /* 216f ROMAN NUMERAL ONE THOUSAND */ { 0x0000, 0x0de7, 0x0de7, 0x0000 }, /* 2170 SMALL ROMAN NUMERAL ONE */ { 0x0000, 0x0de9, 0x0de9, 0x0000 }, /* 2171 SMALL ROMAN NUMERAL TWO */ { 0x0000, 0x0deb, 0x0deb, 0x0000 }, /* 2172 SMALL ROMAN NUMERAL THREE */ { 0x0000, 0x0ded, 0x0ded, 0x0000 }, /* 2173 SMALL ROMAN NUMERAL FOUR */ { 0x0000, 0x0def, 0x0def, 0x0000 }, /* 2174 SMALL ROMAN NUMERAL FIVE */ { 0x0000, 0x0df1, 0x0df1, 0x0000 }, /* 2175 SMALL ROMAN NUMERAL SIX */ { 0x0000, 0x0df3, 0x0df3, 0x0000 }, /* 2176 SMALL ROMAN NUMERAL SEVEN */ { 0x0000, 0x0df5, 0x0df5, 0x0000 }, /* 2177 SMALL ROMAN NUMERAL EIGHT */ { 0x0000, 0x0df7, 0x0df7, 0x0000 }, /* 2178 SMALL ROMAN NUMERAL NINE */ { 0x0000, 0x0df9, 0x0df9, 0x0000 }, /* 2179 SMALL ROMAN NUMERAL TEN */ { 0x0000, 0x0dfb, 0x0dfb, 0x0000 }, /* 217a SMALL ROMAN NUMERAL ELEVEN */ { 0x0000, 0x0dfd, 0x0dfd, 0x0000 }, /* 217b SMALL ROMAN NUMERAL TWELVE */ { 0x0000, 0x0dff, 0x0dff, 0x0000 }, /* 217c SMALL ROMAN NUMERAL FIFTY */ { 0x0000, 0x0e01, 0x0e01, 0x0000 }, /* 217d SMALL ROMAN NUMERAL ONE HUNDRED */ { 0x0000, 0x0e03, 0x0e03, 0x0000 }, /* 217e SMALL ROMAN NUMERAL FIVE HUNDRED */ { 0x0000, 0x0e05, 0x0e05, 0x0000 } /* 217f SMALL ROMAN NUMERAL ONE THOUSAND */ }; static case_table case_pg_043[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2180 ROMAN NUMERAL ONE THOUSAND C D */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2181 ROMAN NUMERAL FIVE THOUSAND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2182 ROMAN NUMERAL TEN THOUSAND */ { 0x0e07, 0x0000, 0x0000, 0x0e07 }, /* 2183 ROMAN NUMERAL REVERSED ONE HUNDRED */ { 0x0000, 0x0e09, 0x0e09, 0x0000 }, /* 2184 LATIN SMALL LETTER REVERSED C */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2185 ROMAN NUMERAL SIX LATE FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2186 ROMAN NUMERAL FIFTY EARLY FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2187 ROMAN NUMERAL FIFTY THOUSAND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2188 ROMAN NUMERAL ONE HUNDRED THOUSAND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2189 VULGAR FRACTION ZERO THIRDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 218a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 218b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 218c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 218d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 218e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 218f (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2190 LEFTWARDS ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2191 UPWARDS ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2192 RIGHTWARDS ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2193 DOWNWARDS ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2194 LEFT RIGHT ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2195 UP DOWN ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2196 NORTH WEST ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2197 NORTH EAST ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2198 SOUTH EAST ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2199 SOUTH WEST ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 219a LEFTWARDS ARROW WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 219b RIGHTWARDS ARROW WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 219c LEFTWARDS WAVE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 219d RIGHTWARDS WAVE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 219e LEFTWARDS TWO HEADED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 219f UPWARDS TWO HEADED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a0 RIGHTWARDS TWO HEADED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a1 DOWNWARDS TWO HEADED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a2 LEFTWARDS ARROW WITH TAIL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a3 RIGHTWARDS ARROW WITH TAIL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a4 LEFTWARDS ARROW FROM BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a5 UPWARDS ARROW FROM BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a6 RIGHTWARDS ARROW FROM BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a7 DOWNWARDS ARROW FROM BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a8 UP DOWN ARROW WITH BASE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a9 LEFTWARDS ARROW WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21aa RIGHTWARDS ARROW WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ab LEFTWARDS ARROW WITH LOOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ac RIGHTWARDS ARROW WITH LOOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ad LEFT RIGHT WAVE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ae LEFT RIGHT ARROW WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21af DOWNWARDS ZIGZAG ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b0 UPWARDS ARROW WITH TIP LEFTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b1 UPWARDS ARROW WITH TIP RIGHTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b2 DOWNWARDS ARROW WITH TIP LEFTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b3 DOWNWARDS ARROW WITH TIP RIGHTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b4 RIGHTWARDS ARROW WITH CORNER DOWNWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b5 DOWNWARDS ARROW WITH CORNER LEFTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b6 ANTICLOCKWISE TOP SEMICIRCLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b7 CLOCKWISE TOP SEMICIRCLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b8 NORTH WEST ARROW TO LONG BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b9 LEFTWARDS ARROW TO BAR OVER RIGHTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ba ANTICLOCKWISE OPEN CIRCLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21bb CLOCKWISE OPEN CIRCLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21bc LEFTWARDS HARPOON WITH BARB UPWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21bd LEFTWARDS HARPOON WITH BARB DOWNWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21be UPWARDS HARPOON WITH BARB RIGHTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21bf UPWARDS HARPOON WITH BARB LEFTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c0 RIGHTWARDS HARPOON WITH BARB UPWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c1 RIGHTWARDS HARPOON WITH BARB DOWNWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c2 DOWNWARDS HARPOON WITH BARB RIGHTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c3 DOWNWARDS HARPOON WITH BARB LEFTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c4 RIGHTWARDS ARROW OVER LEFTWARDS ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c5 UPWARDS ARROW LEFTWARDS OF DOWNWARDS AR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c6 LEFTWARDS ARROW OVER RIGHTWARDS ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c7 LEFTWARDS PAIRED ARROWS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c8 UPWARDS PAIRED ARROWS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c9 RIGHTWARDS PAIRED ARROWS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ca DOWNWARDS PAIRED ARROWS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21cb LEFTWARDS HARPOON OVER RIGHTWARDS HARPO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21cc RIGHTWARDS HARPOON OVER LEFTWARDS HARPO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21cd LEFTWARDS DOUBLE ARROW WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ce LEFT RIGHT DOUBLE ARROW WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21cf RIGHTWARDS DOUBLE ARROW WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d0 LEFTWARDS DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d1 UPWARDS DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d2 RIGHTWARDS DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d3 DOWNWARDS DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d4 LEFT RIGHT DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d5 UP DOWN DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d6 NORTH WEST DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d7 NORTH EAST DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d8 SOUTH EAST DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d9 SOUTH WEST DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21da LEFTWARDS TRIPLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21db RIGHTWARDS TRIPLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21dc LEFTWARDS SQUIGGLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21dd RIGHTWARDS SQUIGGLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21de UPWARDS ARROW WITH DOUBLE STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21df DOWNWARDS ARROW WITH DOUBLE STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e0 LEFTWARDS DASHED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e1 UPWARDS DASHED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e2 RIGHTWARDS DASHED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e3 DOWNWARDS DASHED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e4 LEFTWARDS ARROW TO BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e5 RIGHTWARDS ARROW TO BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e6 LEFTWARDS WHITE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e7 UPWARDS WHITE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e8 RIGHTWARDS WHITE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e9 DOWNWARDS WHITE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ea UPWARDS WHITE ARROW FROM BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21eb UPWARDS WHITE ARROW ON PEDESTAL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ec UPWARDS WHITE ARROW ON PEDESTAL WITH HO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ed UPWARDS WHITE ARROW ON PEDESTAL WITH VE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ee UPWARDS WHITE DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ef UPWARDS WHITE DOUBLE ARROW ON PEDESTAL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f0 RIGHTWARDS WHITE ARROW FROM WALL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f1 NORTH WEST ARROW TO CORNER */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f2 SOUTH EAST ARROW TO CORNER */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f3 UP DOWN WHITE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f4 RIGHT ARROW WITH SMALL CIRCLE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f5 DOWNWARDS ARROW LEFTWARDS OF UPWARDS AR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f6 THREE RIGHTWARDS ARROWS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f7 LEFTWARDS ARROW WITH VERTICAL STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f8 RIGHTWARDS ARROW WITH VERTICAL STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f9 LEFT RIGHT ARROW WITH VERTICAL STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21fa LEFTWARDS ARROW WITH DOUBLE VERTICAL ST */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21fb RIGHTWARDS ARROW WITH DOUBLE VERTICAL S */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21fc LEFT RIGHT ARROW WITH DOUBLE VERTICAL S */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21fd LEFTWARDS OPEN-HEADED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21fe RIGHTWARDS OPEN-HEADED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 21ff LEFT RIGHT OPEN-HEADED ARROW */ }; static case_table case_pg_049[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2480 PARENTHESIZED NUMBER THIRTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2481 PARENTHESIZED NUMBER FOURTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2482 PARENTHESIZED NUMBER FIFTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2483 PARENTHESIZED NUMBER SIXTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2484 PARENTHESIZED NUMBER SEVENTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2485 PARENTHESIZED NUMBER EIGHTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2486 PARENTHESIZED NUMBER NINETEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2487 PARENTHESIZED NUMBER TWENTY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2488 DIGIT ONE FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2489 DIGIT TWO FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 248a DIGIT THREE FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 248b DIGIT FOUR FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 248c DIGIT FIVE FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 248d DIGIT SIX FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 248e DIGIT SEVEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 248f DIGIT EIGHT FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2490 DIGIT NINE FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2491 NUMBER TEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2492 NUMBER ELEVEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2493 NUMBER TWELVE FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2494 NUMBER THIRTEEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2495 NUMBER FOURTEEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2496 NUMBER FIFTEEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2497 NUMBER SIXTEEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2498 NUMBER SEVENTEEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2499 NUMBER EIGHTEEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 249a NUMBER NINETEEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 249b NUMBER TWENTY FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 249c PARENTHESIZED LATIN SMALL LETTER A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 249d PARENTHESIZED LATIN SMALL LETTER B */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 249e PARENTHESIZED LATIN SMALL LETTER C */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 249f PARENTHESIZED LATIN SMALL LETTER D */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a0 PARENTHESIZED LATIN SMALL LETTER E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a1 PARENTHESIZED LATIN SMALL LETTER F */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a2 PARENTHESIZED LATIN SMALL LETTER G */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a3 PARENTHESIZED LATIN SMALL LETTER H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a4 PARENTHESIZED LATIN SMALL LETTER I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a5 PARENTHESIZED LATIN SMALL LETTER J */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a6 PARENTHESIZED LATIN SMALL LETTER K */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a7 PARENTHESIZED LATIN SMALL LETTER L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a8 PARENTHESIZED LATIN SMALL LETTER M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a9 PARENTHESIZED LATIN SMALL LETTER N */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24aa PARENTHESIZED LATIN SMALL LETTER O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ab PARENTHESIZED LATIN SMALL LETTER P */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ac PARENTHESIZED LATIN SMALL LETTER Q */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ad PARENTHESIZED LATIN SMALL LETTER R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ae PARENTHESIZED LATIN SMALL LETTER S */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24af PARENTHESIZED LATIN SMALL LETTER T */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24b0 PARENTHESIZED LATIN SMALL LETTER U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24b1 PARENTHESIZED LATIN SMALL LETTER V */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24b2 PARENTHESIZED LATIN SMALL LETTER W */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24b3 PARENTHESIZED LATIN SMALL LETTER X */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24b4 PARENTHESIZED LATIN SMALL LETTER Y */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24b5 PARENTHESIZED LATIN SMALL LETTER Z */ { 0x0e0b, 0x0000, 0x0000, 0x0e0b }, /* 24b6 CIRCLED LATIN CAPITAL LETTER A */ { 0x0e0d, 0x0000, 0x0000, 0x0e0d }, /* 24b7 CIRCLED LATIN CAPITAL LETTER B */ { 0x0e0f, 0x0000, 0x0000, 0x0e0f }, /* 24b8 CIRCLED LATIN CAPITAL LETTER C */ { 0x0e11, 0x0000, 0x0000, 0x0e11 }, /* 24b9 CIRCLED LATIN CAPITAL LETTER D */ { 0x0e13, 0x0000, 0x0000, 0x0e13 }, /* 24ba CIRCLED LATIN CAPITAL LETTER E */ { 0x0e15, 0x0000, 0x0000, 0x0e15 }, /* 24bb CIRCLED LATIN CAPITAL LETTER F */ { 0x0e17, 0x0000, 0x0000, 0x0e17 }, /* 24bc CIRCLED LATIN CAPITAL LETTER G */ { 0x0e19, 0x0000, 0x0000, 0x0e19 }, /* 24bd CIRCLED LATIN CAPITAL LETTER H */ { 0x0e1b, 0x0000, 0x0000, 0x0e1b }, /* 24be CIRCLED LATIN CAPITAL LETTER I */ { 0x0e1d, 0x0000, 0x0000, 0x0e1d }, /* 24bf CIRCLED LATIN CAPITAL LETTER J */ { 0x0e1f, 0x0000, 0x0000, 0x0e1f }, /* 24c0 CIRCLED LATIN CAPITAL LETTER K */ { 0x0e21, 0x0000, 0x0000, 0x0e21 }, /* 24c1 CIRCLED LATIN CAPITAL LETTER L */ { 0x0e23, 0x0000, 0x0000, 0x0e23 }, /* 24c2 CIRCLED LATIN CAPITAL LETTER M */ { 0x0e25, 0x0000, 0x0000, 0x0e25 }, /* 24c3 CIRCLED LATIN CAPITAL LETTER N */ { 0x0e27, 0x0000, 0x0000, 0x0e27 }, /* 24c4 CIRCLED LATIN CAPITAL LETTER O */ { 0x0e29, 0x0000, 0x0000, 0x0e29 }, /* 24c5 CIRCLED LATIN CAPITAL LETTER P */ { 0x0e2b, 0x0000, 0x0000, 0x0e2b }, /* 24c6 CIRCLED LATIN CAPITAL LETTER Q */ { 0x0e2d, 0x0000, 0x0000, 0x0e2d }, /* 24c7 CIRCLED LATIN CAPITAL LETTER R */ { 0x0e2f, 0x0000, 0x0000, 0x0e2f }, /* 24c8 CIRCLED LATIN CAPITAL LETTER S */ { 0x0e31, 0x0000, 0x0000, 0x0e31 }, /* 24c9 CIRCLED LATIN CAPITAL LETTER T */ { 0x0e33, 0x0000, 0x0000, 0x0e33 }, /* 24ca CIRCLED LATIN CAPITAL LETTER U */ { 0x0e35, 0x0000, 0x0000, 0x0e35 }, /* 24cb CIRCLED LATIN CAPITAL LETTER V */ { 0x0e37, 0x0000, 0x0000, 0x0e37 }, /* 24cc CIRCLED LATIN CAPITAL LETTER W */ { 0x0e39, 0x0000, 0x0000, 0x0e39 }, /* 24cd CIRCLED LATIN CAPITAL LETTER X */ { 0x0e3b, 0x0000, 0x0000, 0x0e3b }, /* 24ce CIRCLED LATIN CAPITAL LETTER Y */ { 0x0e3d, 0x0000, 0x0000, 0x0e3d }, /* 24cf CIRCLED LATIN CAPITAL LETTER Z */ { 0x0000, 0x0e3f, 0x0e3f, 0x0000 }, /* 24d0 CIRCLED LATIN SMALL LETTER A */ { 0x0000, 0x0e41, 0x0e41, 0x0000 }, /* 24d1 CIRCLED LATIN SMALL LETTER B */ { 0x0000, 0x0e43, 0x0e43, 0x0000 }, /* 24d2 CIRCLED LATIN SMALL LETTER C */ { 0x0000, 0x0e45, 0x0e45, 0x0000 }, /* 24d3 CIRCLED LATIN SMALL LETTER D */ { 0x0000, 0x0e47, 0x0e47, 0x0000 }, /* 24d4 CIRCLED LATIN SMALL LETTER E */ { 0x0000, 0x0e49, 0x0e49, 0x0000 }, /* 24d5 CIRCLED LATIN SMALL LETTER F */ { 0x0000, 0x0e4b, 0x0e4b, 0x0000 }, /* 24d6 CIRCLED LATIN SMALL LETTER G */ { 0x0000, 0x0e4d, 0x0e4d, 0x0000 }, /* 24d7 CIRCLED LATIN SMALL LETTER H */ { 0x0000, 0x0e4f, 0x0e4f, 0x0000 }, /* 24d8 CIRCLED LATIN SMALL LETTER I */ { 0x0000, 0x0e51, 0x0e51, 0x0000 }, /* 24d9 CIRCLED LATIN SMALL LETTER J */ { 0x0000, 0x0e53, 0x0e53, 0x0000 }, /* 24da CIRCLED LATIN SMALL LETTER K */ { 0x0000, 0x0e55, 0x0e55, 0x0000 }, /* 24db CIRCLED LATIN SMALL LETTER L */ { 0x0000, 0x0e57, 0x0e57, 0x0000 }, /* 24dc CIRCLED LATIN SMALL LETTER M */ { 0x0000, 0x0e59, 0x0e59, 0x0000 }, /* 24dd CIRCLED LATIN SMALL LETTER N */ { 0x0000, 0x0e5b, 0x0e5b, 0x0000 }, /* 24de CIRCLED LATIN SMALL LETTER O */ { 0x0000, 0x0e5d, 0x0e5d, 0x0000 }, /* 24df CIRCLED LATIN SMALL LETTER P */ { 0x0000, 0x0e5f, 0x0e5f, 0x0000 }, /* 24e0 CIRCLED LATIN SMALL LETTER Q */ { 0x0000, 0x0e61, 0x0e61, 0x0000 }, /* 24e1 CIRCLED LATIN SMALL LETTER R */ { 0x0000, 0x0e63, 0x0e63, 0x0000 }, /* 24e2 CIRCLED LATIN SMALL LETTER S */ { 0x0000, 0x0e65, 0x0e65, 0x0000 }, /* 24e3 CIRCLED LATIN SMALL LETTER T */ { 0x0000, 0x0e67, 0x0e67, 0x0000 }, /* 24e4 CIRCLED LATIN SMALL LETTER U */ { 0x0000, 0x0e69, 0x0e69, 0x0000 }, /* 24e5 CIRCLED LATIN SMALL LETTER V */ { 0x0000, 0x0e6b, 0x0e6b, 0x0000 }, /* 24e6 CIRCLED LATIN SMALL LETTER W */ { 0x0000, 0x0e6d, 0x0e6d, 0x0000 }, /* 24e7 CIRCLED LATIN SMALL LETTER X */ { 0x0000, 0x0e6f, 0x0e6f, 0x0000 }, /* 24e8 CIRCLED LATIN SMALL LETTER Y */ { 0x0000, 0x0e71, 0x0e71, 0x0000 }, /* 24e9 CIRCLED LATIN SMALL LETTER Z */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ea CIRCLED DIGIT ZERO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24eb NEGATIVE CIRCLED NUMBER ELEVEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ec NEGATIVE CIRCLED NUMBER TWELVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ed NEGATIVE CIRCLED NUMBER THIRTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ee NEGATIVE CIRCLED NUMBER FOURTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ef NEGATIVE CIRCLED NUMBER FIFTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f0 NEGATIVE CIRCLED NUMBER SIXTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f1 NEGATIVE CIRCLED NUMBER SEVENTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f2 NEGATIVE CIRCLED NUMBER EIGHTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f3 NEGATIVE CIRCLED NUMBER NINETEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f4 NEGATIVE CIRCLED NUMBER TWENTY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f5 DOUBLE CIRCLED DIGIT ONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f6 DOUBLE CIRCLED DIGIT TWO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f7 DOUBLE CIRCLED DIGIT THREE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f8 DOUBLE CIRCLED DIGIT FOUR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f9 DOUBLE CIRCLED DIGIT FIVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24fa DOUBLE CIRCLED DIGIT SIX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24fb DOUBLE CIRCLED DIGIT SEVEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24fc DOUBLE CIRCLED DIGIT EIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24fd DOUBLE CIRCLED DIGIT NINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24fe DOUBLE CIRCLED NUMBER TEN */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 24ff NEGATIVE CIRCLED DIGIT ZERO */ }; static case_table case_pg_058[128] = { { 0x0e73, 0x0000, 0x0000, 0x0e73 }, /* 2c00 GLAGOLITIC CAPITAL LETTER AZU */ { 0x0e75, 0x0000, 0x0000, 0x0e75 }, /* 2c01 GLAGOLITIC CAPITAL LETTER BUKY */ { 0x0e77, 0x0000, 0x0000, 0x0e77 }, /* 2c02 GLAGOLITIC CAPITAL LETTER VEDE */ { 0x0e79, 0x0000, 0x0000, 0x0e79 }, /* 2c03 GLAGOLITIC CAPITAL LETTER GLAGOLI */ { 0x0e7b, 0x0000, 0x0000, 0x0e7b }, /* 2c04 GLAGOLITIC CAPITAL LETTER DOBRO */ { 0x0e7d, 0x0000, 0x0000, 0x0e7d }, /* 2c05 GLAGOLITIC CAPITAL LETTER YESTU */ { 0x0e7f, 0x0000, 0x0000, 0x0e7f }, /* 2c06 GLAGOLITIC CAPITAL LETTER ZHIVETE */ { 0x0e81, 0x0000, 0x0000, 0x0e81 }, /* 2c07 GLAGOLITIC CAPITAL LETTER DZELO */ { 0x0e83, 0x0000, 0x0000, 0x0e83 }, /* 2c08 GLAGOLITIC CAPITAL LETTER ZEMLJA */ { 0x0e85, 0x0000, 0x0000, 0x0e85 }, /* 2c09 GLAGOLITIC CAPITAL LETTER IZHE */ { 0x0e87, 0x0000, 0x0000, 0x0e87 }, /* 2c0a GLAGOLITIC CAPITAL LETTER INITIAL IZHE */ { 0x0e89, 0x0000, 0x0000, 0x0e89 }, /* 2c0b GLAGOLITIC CAPITAL LETTER I */ { 0x0e8b, 0x0000, 0x0000, 0x0e8b }, /* 2c0c GLAGOLITIC CAPITAL LETTER DJERVI */ { 0x0e8d, 0x0000, 0x0000, 0x0e8d }, /* 2c0d GLAGOLITIC CAPITAL LETTER KAKO */ { 0x0e8f, 0x0000, 0x0000, 0x0e8f }, /* 2c0e GLAGOLITIC CAPITAL LETTER LJUDIJE */ { 0x0e91, 0x0000, 0x0000, 0x0e91 }, /* 2c0f GLAGOLITIC CAPITAL LETTER MYSLITE */ { 0x0e93, 0x0000, 0x0000, 0x0e93 }, /* 2c10 GLAGOLITIC CAPITAL LETTER NASHI */ { 0x0e95, 0x0000, 0x0000, 0x0e95 }, /* 2c11 GLAGOLITIC CAPITAL LETTER ONU */ { 0x0e97, 0x0000, 0x0000, 0x0e97 }, /* 2c12 GLAGOLITIC CAPITAL LETTER POKOJI */ { 0x0e99, 0x0000, 0x0000, 0x0e99 }, /* 2c13 GLAGOLITIC CAPITAL LETTER RITSI */ { 0x0e9b, 0x0000, 0x0000, 0x0e9b }, /* 2c14 GLAGOLITIC CAPITAL LETTER SLOVO */ { 0x0e9d, 0x0000, 0x0000, 0x0e9d }, /* 2c15 GLAGOLITIC CAPITAL LETTER TVRIDO */ { 0x0e9f, 0x0000, 0x0000, 0x0e9f }, /* 2c16 GLAGOLITIC CAPITAL LETTER UKU */ { 0x0ea1, 0x0000, 0x0000, 0x0ea1 }, /* 2c17 GLAGOLITIC CAPITAL LETTER FRITU */ { 0x0ea3, 0x0000, 0x0000, 0x0ea3 }, /* 2c18 GLAGOLITIC CAPITAL LETTER HERU */ { 0x0ea5, 0x0000, 0x0000, 0x0ea5 }, /* 2c19 GLAGOLITIC CAPITAL LETTER OTU */ { 0x0ea7, 0x0000, 0x0000, 0x0ea7 }, /* 2c1a GLAGOLITIC CAPITAL LETTER PE */ { 0x0ea9, 0x0000, 0x0000, 0x0ea9 }, /* 2c1b GLAGOLITIC CAPITAL LETTER SHTA */ { 0x0eab, 0x0000, 0x0000, 0x0eab }, /* 2c1c GLAGOLITIC CAPITAL LETTER TSI */ { 0x0ead, 0x0000, 0x0000, 0x0ead }, /* 2c1d GLAGOLITIC CAPITAL LETTER CHRIVI */ { 0x0eaf, 0x0000, 0x0000, 0x0eaf }, /* 2c1e GLAGOLITIC CAPITAL LETTER SHA */ { 0x0eb1, 0x0000, 0x0000, 0x0eb1 }, /* 2c1f GLAGOLITIC CAPITAL LETTER YERU */ { 0x0eb3, 0x0000, 0x0000, 0x0eb3 }, /* 2c20 GLAGOLITIC CAPITAL LETTER YERI */ { 0x0eb5, 0x0000, 0x0000, 0x0eb5 }, /* 2c21 GLAGOLITIC CAPITAL LETTER YATI */ { 0x0eb7, 0x0000, 0x0000, 0x0eb7 }, /* 2c22 GLAGOLITIC CAPITAL LETTER SPIDERY HA */ { 0x0eb9, 0x0000, 0x0000, 0x0eb9 }, /* 2c23 GLAGOLITIC CAPITAL LETTER YU */ { 0x0ebb, 0x0000, 0x0000, 0x0ebb }, /* 2c24 GLAGOLITIC CAPITAL LETTER SMALL YUS */ { 0x0ebd, 0x0000, 0x0000, 0x0ebd }, /* 2c25 GLAGOLITIC CAPITAL LETTER SMALL YUS WIT */ { 0x0ebf, 0x0000, 0x0000, 0x0ebf }, /* 2c26 GLAGOLITIC CAPITAL LETTER YO */ { 0x0ec1, 0x0000, 0x0000, 0x0ec1 }, /* 2c27 GLAGOLITIC CAPITAL LETTER IOTATED SMALL */ { 0x0ec3, 0x0000, 0x0000, 0x0ec3 }, /* 2c28 GLAGOLITIC CAPITAL LETTER BIG YUS */ { 0x0ec5, 0x0000, 0x0000, 0x0ec5 }, /* 2c29 GLAGOLITIC CAPITAL LETTER IOTATED BIG Y */ { 0x0ec7, 0x0000, 0x0000, 0x0ec7 }, /* 2c2a GLAGOLITIC CAPITAL LETTER FITA */ { 0x0ec9, 0x0000, 0x0000, 0x0ec9 }, /* 2c2b GLAGOLITIC CAPITAL LETTER IZHITSA */ { 0x0ecb, 0x0000, 0x0000, 0x0ecb }, /* 2c2c GLAGOLITIC CAPITAL LETTER SHTAPIC */ { 0x0ecd, 0x0000, 0x0000, 0x0ecd }, /* 2c2d GLAGOLITIC CAPITAL LETTER TROKUTASTI A */ { 0x0ecf, 0x0000, 0x0000, 0x0ecf }, /* 2c2e GLAGOLITIC CAPITAL LETTER LATINATE MYSL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c2f (null) */ { 0x0000, 0x0ed1, 0x0ed1, 0x0000 }, /* 2c30 GLAGOLITIC SMALL LETTER AZU */ { 0x0000, 0x0ed3, 0x0ed3, 0x0000 }, /* 2c31 GLAGOLITIC SMALL LETTER BUKY */ { 0x0000, 0x0ed5, 0x0ed5, 0x0000 }, /* 2c32 GLAGOLITIC SMALL LETTER VEDE */ { 0x0000, 0x0ed7, 0x0ed7, 0x0000 }, /* 2c33 GLAGOLITIC SMALL LETTER GLAGOLI */ { 0x0000, 0x0ed9, 0x0ed9, 0x0000 }, /* 2c34 GLAGOLITIC SMALL LETTER DOBRO */ { 0x0000, 0x0edb, 0x0edb, 0x0000 }, /* 2c35 GLAGOLITIC SMALL LETTER YESTU */ { 0x0000, 0x0edd, 0x0edd, 0x0000 }, /* 2c36 GLAGOLITIC SMALL LETTER ZHIVETE */ { 0x0000, 0x0edf, 0x0edf, 0x0000 }, /* 2c37 GLAGOLITIC SMALL LETTER DZELO */ { 0x0000, 0x0ee1, 0x0ee1, 0x0000 }, /* 2c38 GLAGOLITIC SMALL LETTER ZEMLJA */ { 0x0000, 0x0ee3, 0x0ee3, 0x0000 }, /* 2c39 GLAGOLITIC SMALL LETTER IZHE */ { 0x0000, 0x0ee5, 0x0ee5, 0x0000 }, /* 2c3a GLAGOLITIC SMALL LETTER INITIAL IZHE */ { 0x0000, 0x0ee7, 0x0ee7, 0x0000 }, /* 2c3b GLAGOLITIC SMALL LETTER I */ { 0x0000, 0x0ee9, 0x0ee9, 0x0000 }, /* 2c3c GLAGOLITIC SMALL LETTER DJERVI */ { 0x0000, 0x0eeb, 0x0eeb, 0x0000 }, /* 2c3d GLAGOLITIC SMALL LETTER KAKO */ { 0x0000, 0x0eed, 0x0eed, 0x0000 }, /* 2c3e GLAGOLITIC SMALL LETTER LJUDIJE */ { 0x0000, 0x0eef, 0x0eef, 0x0000 }, /* 2c3f GLAGOLITIC SMALL LETTER MYSLITE */ { 0x0000, 0x0ef1, 0x0ef1, 0x0000 }, /* 2c40 GLAGOLITIC SMALL LETTER NASHI */ { 0x0000, 0x0ef3, 0x0ef3, 0x0000 }, /* 2c41 GLAGOLITIC SMALL LETTER ONU */ { 0x0000, 0x0ef5, 0x0ef5, 0x0000 }, /* 2c42 GLAGOLITIC SMALL LETTER POKOJI */ { 0x0000, 0x0ef7, 0x0ef7, 0x0000 }, /* 2c43 GLAGOLITIC SMALL LETTER RITSI */ { 0x0000, 0x0ef9, 0x0ef9, 0x0000 }, /* 2c44 GLAGOLITIC SMALL LETTER SLOVO */ { 0x0000, 0x0efb, 0x0efb, 0x0000 }, /* 2c45 GLAGOLITIC SMALL LETTER TVRIDO */ { 0x0000, 0x0efd, 0x0efd, 0x0000 }, /* 2c46 GLAGOLITIC SMALL LETTER UKU */ { 0x0000, 0x0eff, 0x0eff, 0x0000 }, /* 2c47 GLAGOLITIC SMALL LETTER FRITU */ { 0x0000, 0x0f01, 0x0f01, 0x0000 }, /* 2c48 GLAGOLITIC SMALL LETTER HERU */ { 0x0000, 0x0f03, 0x0f03, 0x0000 }, /* 2c49 GLAGOLITIC SMALL LETTER OTU */ { 0x0000, 0x0f05, 0x0f05, 0x0000 }, /* 2c4a GLAGOLITIC SMALL LETTER PE */ { 0x0000, 0x0f07, 0x0f07, 0x0000 }, /* 2c4b GLAGOLITIC SMALL LETTER SHTA */ { 0x0000, 0x0f09, 0x0f09, 0x0000 }, /* 2c4c GLAGOLITIC SMALL LETTER TSI */ { 0x0000, 0x0f0b, 0x0f0b, 0x0000 }, /* 2c4d GLAGOLITIC SMALL LETTER CHRIVI */ { 0x0000, 0x0f0d, 0x0f0d, 0x0000 }, /* 2c4e GLAGOLITIC SMALL LETTER SHA */ { 0x0000, 0x0f0f, 0x0f0f, 0x0000 }, /* 2c4f GLAGOLITIC SMALL LETTER YERU */ { 0x0000, 0x0f11, 0x0f11, 0x0000 }, /* 2c50 GLAGOLITIC SMALL LETTER YERI */ { 0x0000, 0x0f13, 0x0f13, 0x0000 }, /* 2c51 GLAGOLITIC SMALL LETTER YATI */ { 0x0000, 0x0f15, 0x0f15, 0x0000 }, /* 2c52 GLAGOLITIC SMALL LETTER SPIDERY HA */ { 0x0000, 0x0f17, 0x0f17, 0x0000 }, /* 2c53 GLAGOLITIC SMALL LETTER YU */ { 0x0000, 0x0f19, 0x0f19, 0x0000 }, /* 2c54 GLAGOLITIC SMALL LETTER SMALL YUS */ { 0x0000, 0x0f1b, 0x0f1b, 0x0000 }, /* 2c55 GLAGOLITIC SMALL LETTER SMALL YUS WITH */ { 0x0000, 0x0f1d, 0x0f1d, 0x0000 }, /* 2c56 GLAGOLITIC SMALL LETTER YO */ { 0x0000, 0x0f1f, 0x0f1f, 0x0000 }, /* 2c57 GLAGOLITIC SMALL LETTER IOTATED SMALL Y */ { 0x0000, 0x0f21, 0x0f21, 0x0000 }, /* 2c58 GLAGOLITIC SMALL LETTER BIG YUS */ { 0x0000, 0x0f23, 0x0f23, 0x0000 }, /* 2c59 GLAGOLITIC SMALL LETTER IOTATED BIG YUS */ { 0x0000, 0x0f25, 0x0f25, 0x0000 }, /* 2c5a GLAGOLITIC SMALL LETTER FITA */ { 0x0000, 0x0f27, 0x0f27, 0x0000 }, /* 2c5b GLAGOLITIC SMALL LETTER IZHITSA */ { 0x0000, 0x0f29, 0x0f29, 0x0000 }, /* 2c5c GLAGOLITIC SMALL LETTER SHTAPIC */ { 0x0000, 0x0f2b, 0x0f2b, 0x0000 }, /* 2c5d GLAGOLITIC SMALL LETTER TROKUTASTI A */ { 0x0000, 0x0f2d, 0x0f2d, 0x0000 }, /* 2c5e GLAGOLITIC SMALL LETTER LATINATE MYSLIT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c5f (null) */ { 0x0f2f, 0x0000, 0x0000, 0x0f2f }, /* 2c60 LATIN CAPITAL LETTER L WITH DOUBLE BAR */ { 0x0000, 0x0f31, 0x0f31, 0x0000 }, /* 2c61 LATIN SMALL LETTER L WITH DOUBLE BAR */ { 0x0f33, 0x0000, 0x0000, 0x0f33 }, /* 2c62 LATIN CAPITAL LETTER L WITH MIDDLE TILD */ { 0x0f35, 0x0000, 0x0000, 0x0f35 }, /* 2c63 LATIN CAPITAL LETTER P WITH STROKE */ { 0x0f37, 0x0000, 0x0000, 0x0f37 }, /* 2c64 LATIN CAPITAL LETTER R WITH TAIL */ { 0x0000, 0x0f39, 0x0f39, 0x0000 }, /* 2c65 LATIN SMALL LETTER A WITH STROKE */ { 0x0000, 0x0f3b, 0x0f3b, 0x0000 }, /* 2c66 LATIN SMALL LETTER T WITH DIAGONAL STRO */ { 0x0f3d, 0x0000, 0x0000, 0x0f3d }, /* 2c67 LATIN CAPITAL LETTER H WITH DESCENDER */ { 0x0000, 0x0f3f, 0x0f3f, 0x0000 }, /* 2c68 LATIN SMALL LETTER H WITH DESCENDER */ { 0x0f41, 0x0000, 0x0000, 0x0f41 }, /* 2c69 LATIN CAPITAL LETTER K WITH DESCENDER */ { 0x0000, 0x0f43, 0x0f43, 0x0000 }, /* 2c6a LATIN SMALL LETTER K WITH DESCENDER */ { 0x0f45, 0x0000, 0x0000, 0x0f45 }, /* 2c6b LATIN CAPITAL LETTER Z WITH DESCENDER */ { 0x0000, 0x0f47, 0x0f47, 0x0000 }, /* 2c6c LATIN SMALL LETTER Z WITH DESCENDER */ { 0x0f49, 0x0000, 0x0000, 0x0f49 }, /* 2c6d LATIN CAPITAL LETTER ALPHA */ { 0x0f4b, 0x0000, 0x0000, 0x0f4b }, /* 2c6e LATIN CAPITAL LETTER M WITH HOOK */ { 0x0f4d, 0x0000, 0x0000, 0x0f4d }, /* 2c6f LATIN CAPITAL LETTER TURNED A */ { 0x0f4f, 0x0000, 0x0000, 0x0f4f }, /* 2c70 LATIN CAPITAL LETTER TURNED ALPHA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c71 LATIN SMALL LETTER V WITH RIGHT HOOK */ { 0x0f51, 0x0000, 0x0000, 0x0f51 }, /* 2c72 LATIN CAPITAL LETTER W WITH HOOK */ { 0x0000, 0x0f53, 0x0f53, 0x0000 }, /* 2c73 LATIN SMALL LETTER W WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c74 LATIN SMALL LETTER V WITH CURL */ { 0x0f55, 0x0000, 0x0000, 0x0f55 }, /* 2c75 LATIN CAPITAL LETTER HALF H */ { 0x0000, 0x0f57, 0x0f57, 0x0000 }, /* 2c76 LATIN SMALL LETTER HALF H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c77 LATIN SMALL LETTER TAILLESS PHI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c78 LATIN SMALL LETTER E WITH NOTCH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c79 LATIN SMALL LETTER TURNED R WITH TAIL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c7a LATIN SMALL LETTER O WITH LOW RING INSI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c7b LATIN LETTER SMALL CAPITAL TURNED E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c7c LATIN SUBSCRIPT SMALL LETTER J */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c7d MODIFIER LETTER CAPITAL V */ { 0x0f59, 0x0000, 0x0000, 0x0f59 }, /* 2c7e LATIN CAPITAL LETTER S WITH SWASH TAIL */ { 0x0f5b, 0x0000, 0x0000, 0x0f5b } /* 2c7f LATIN CAPITAL LETTER Z WITH SWASH TAIL */ }; static case_table case_pg_059[128] = { { 0x0f5d, 0x0000, 0x0000, 0x0f5d }, /* 2c80 COPTIC CAPITAL LETTER ALFA */ { 0x0000, 0x0f5f, 0x0f5f, 0x0000 }, /* 2c81 COPTIC SMALL LETTER ALFA */ { 0x0f61, 0x0000, 0x0000, 0x0f61 }, /* 2c82 COPTIC CAPITAL LETTER VIDA */ { 0x0000, 0x0f63, 0x0f63, 0x0000 }, /* 2c83 COPTIC SMALL LETTER VIDA */ { 0x0f65, 0x0000, 0x0000, 0x0f65 }, /* 2c84 COPTIC CAPITAL LETTER GAMMA */ { 0x0000, 0x0f67, 0x0f67, 0x0000 }, /* 2c85 COPTIC SMALL LETTER GAMMA */ { 0x0f69, 0x0000, 0x0000, 0x0f69 }, /* 2c86 COPTIC CAPITAL LETTER DALDA */ { 0x0000, 0x0f6b, 0x0f6b, 0x0000 }, /* 2c87 COPTIC SMALL LETTER DALDA */ { 0x0f6d, 0x0000, 0x0000, 0x0f6d }, /* 2c88 COPTIC CAPITAL LETTER EIE */ { 0x0000, 0x0f6f, 0x0f6f, 0x0000 }, /* 2c89 COPTIC SMALL LETTER EIE */ { 0x0f71, 0x0000, 0x0000, 0x0f71 }, /* 2c8a COPTIC CAPITAL LETTER SOU */ { 0x0000, 0x0f73, 0x0f73, 0x0000 }, /* 2c8b COPTIC SMALL LETTER SOU */ { 0x0f75, 0x0000, 0x0000, 0x0f75 }, /* 2c8c COPTIC CAPITAL LETTER ZATA */ { 0x0000, 0x0f77, 0x0f77, 0x0000 }, /* 2c8d COPTIC SMALL LETTER ZATA */ { 0x0f79, 0x0000, 0x0000, 0x0f79 }, /* 2c8e COPTIC CAPITAL LETTER HATE */ { 0x0000, 0x0f7b, 0x0f7b, 0x0000 }, /* 2c8f COPTIC SMALL LETTER HATE */ { 0x0f7d, 0x0000, 0x0000, 0x0f7d }, /* 2c90 COPTIC CAPITAL LETTER THETHE */ { 0x0000, 0x0f7f, 0x0f7f, 0x0000 }, /* 2c91 COPTIC SMALL LETTER THETHE */ { 0x0f81, 0x0000, 0x0000, 0x0f81 }, /* 2c92 COPTIC CAPITAL LETTER IAUDA */ { 0x0000, 0x0f83, 0x0f83, 0x0000 }, /* 2c93 COPTIC SMALL LETTER IAUDA */ { 0x0f85, 0x0000, 0x0000, 0x0f85 }, /* 2c94 COPTIC CAPITAL LETTER KAPA */ { 0x0000, 0x0f87, 0x0f87, 0x0000 }, /* 2c95 COPTIC SMALL LETTER KAPA */ { 0x0f89, 0x0000, 0x0000, 0x0f89 }, /* 2c96 COPTIC CAPITAL LETTER LAULA */ { 0x0000, 0x0f8b, 0x0f8b, 0x0000 }, /* 2c97 COPTIC SMALL LETTER LAULA */ { 0x0f8d, 0x0000, 0x0000, 0x0f8d }, /* 2c98 COPTIC CAPITAL LETTER MI */ { 0x0000, 0x0f8f, 0x0f8f, 0x0000 }, /* 2c99 COPTIC SMALL LETTER MI */ { 0x0f91, 0x0000, 0x0000, 0x0f91 }, /* 2c9a COPTIC CAPITAL LETTER NI */ { 0x0000, 0x0f93, 0x0f93, 0x0000 }, /* 2c9b COPTIC SMALL LETTER NI */ { 0x0f95, 0x0000, 0x0000, 0x0f95 }, /* 2c9c COPTIC CAPITAL LETTER KSI */ { 0x0000, 0x0f97, 0x0f97, 0x0000 }, /* 2c9d COPTIC SMALL LETTER KSI */ { 0x0f99, 0x0000, 0x0000, 0x0f99 }, /* 2c9e COPTIC CAPITAL LETTER O */ { 0x0000, 0x0f9b, 0x0f9b, 0x0000 }, /* 2c9f COPTIC SMALL LETTER O */ { 0x0f9d, 0x0000, 0x0000, 0x0f9d }, /* 2ca0 COPTIC CAPITAL LETTER PI */ { 0x0000, 0x0f9f, 0x0f9f, 0x0000 }, /* 2ca1 COPTIC SMALL LETTER PI */ { 0x0fa1, 0x0000, 0x0000, 0x0fa1 }, /* 2ca2 COPTIC CAPITAL LETTER RO */ { 0x0000, 0x0fa3, 0x0fa3, 0x0000 }, /* 2ca3 COPTIC SMALL LETTER RO */ { 0x0fa5, 0x0000, 0x0000, 0x0fa5 }, /* 2ca4 COPTIC CAPITAL LETTER SIMA */ { 0x0000, 0x0fa7, 0x0fa7, 0x0000 }, /* 2ca5 COPTIC SMALL LETTER SIMA */ { 0x0fa9, 0x0000, 0x0000, 0x0fa9 }, /* 2ca6 COPTIC CAPITAL LETTER TAU */ { 0x0000, 0x0fab, 0x0fab, 0x0000 }, /* 2ca7 COPTIC SMALL LETTER TAU */ { 0x0fad, 0x0000, 0x0000, 0x0fad }, /* 2ca8 COPTIC CAPITAL LETTER UA */ { 0x0000, 0x0faf, 0x0faf, 0x0000 }, /* 2ca9 COPTIC SMALL LETTER UA */ { 0x0fb1, 0x0000, 0x0000, 0x0fb1 }, /* 2caa COPTIC CAPITAL LETTER FI */ { 0x0000, 0x0fb3, 0x0fb3, 0x0000 }, /* 2cab COPTIC SMALL LETTER FI */ { 0x0fb5, 0x0000, 0x0000, 0x0fb5 }, /* 2cac COPTIC CAPITAL LETTER KHI */ { 0x0000, 0x0fb7, 0x0fb7, 0x0000 }, /* 2cad COPTIC SMALL LETTER KHI */ { 0x0fb9, 0x0000, 0x0000, 0x0fb9 }, /* 2cae COPTIC CAPITAL LETTER PSI */ { 0x0000, 0x0fbb, 0x0fbb, 0x0000 }, /* 2caf COPTIC SMALL LETTER PSI */ { 0x0fbd, 0x0000, 0x0000, 0x0fbd }, /* 2cb0 COPTIC CAPITAL LETTER OOU */ { 0x0000, 0x0fbf, 0x0fbf, 0x0000 }, /* 2cb1 COPTIC SMALL LETTER OOU */ { 0x0fc1, 0x0000, 0x0000, 0x0fc1 }, /* 2cb2 COPTIC CAPITAL LETTER DIALECT-P ALEF */ { 0x0000, 0x0fc3, 0x0fc3, 0x0000 }, /* 2cb3 COPTIC SMALL LETTER DIALECT-P ALEF */ { 0x0fc5, 0x0000, 0x0000, 0x0fc5 }, /* 2cb4 COPTIC CAPITAL LETTER OLD COPTIC AIN */ { 0x0000, 0x0fc7, 0x0fc7, 0x0000 }, /* 2cb5 COPTIC SMALL LETTER OLD COPTIC AIN */ { 0x0fc9, 0x0000, 0x0000, 0x0fc9 }, /* 2cb6 COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE */ { 0x0000, 0x0fcb, 0x0fcb, 0x0000 }, /* 2cb7 COPTIC SMALL LETTER CRYPTOGRAMMIC EIE */ { 0x0fcd, 0x0000, 0x0000, 0x0fcd }, /* 2cb8 COPTIC CAPITAL LETTER DIALECT-P KAPA */ { 0x0000, 0x0fcf, 0x0fcf, 0x0000 }, /* 2cb9 COPTIC SMALL LETTER DIALECT-P KAPA */ { 0x0fd1, 0x0000, 0x0000, 0x0fd1 }, /* 2cba COPTIC CAPITAL LETTER DIALECT-P NI */ { 0x0000, 0x0fd3, 0x0fd3, 0x0000 }, /* 2cbb COPTIC SMALL LETTER DIALECT-P NI */ { 0x0fd5, 0x0000, 0x0000, 0x0fd5 }, /* 2cbc COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI */ { 0x0000, 0x0fd7, 0x0fd7, 0x0000 }, /* 2cbd COPTIC SMALL LETTER CRYPTOGRAMMIC NI */ { 0x0fd9, 0x0000, 0x0000, 0x0fd9 }, /* 2cbe COPTIC CAPITAL LETTER OLD COPTIC OOU */ { 0x0000, 0x0fdb, 0x0fdb, 0x0000 }, /* 2cbf COPTIC SMALL LETTER OLD COPTIC OOU */ { 0x0fdd, 0x0000, 0x0000, 0x0fdd }, /* 2cc0 COPTIC CAPITAL LETTER SAMPI */ { 0x0000, 0x0fdf, 0x0fdf, 0x0000 }, /* 2cc1 COPTIC SMALL LETTER SAMPI */ { 0x0fe1, 0x0000, 0x0000, 0x0fe1 }, /* 2cc2 COPTIC CAPITAL LETTER CROSSED SHEI */ { 0x0000, 0x0fe3, 0x0fe3, 0x0000 }, /* 2cc3 COPTIC SMALL LETTER CROSSED SHEI */ { 0x0fe5, 0x0000, 0x0000, 0x0fe5 }, /* 2cc4 COPTIC CAPITAL LETTER OLD COPTIC SHEI */ { 0x0000, 0x0fe7, 0x0fe7, 0x0000 }, /* 2cc5 COPTIC SMALL LETTER OLD COPTIC SHEI */ { 0x0fe9, 0x0000, 0x0000, 0x0fe9 }, /* 2cc6 COPTIC CAPITAL LETTER OLD COPTIC ESH */ { 0x0000, 0x0feb, 0x0feb, 0x0000 }, /* 2cc7 COPTIC SMALL LETTER OLD COPTIC ESH */ { 0x0fed, 0x0000, 0x0000, 0x0fed }, /* 2cc8 COPTIC CAPITAL LETTER AKHMIMIC KHEI */ { 0x0000, 0x0fef, 0x0fef, 0x0000 }, /* 2cc9 COPTIC SMALL LETTER AKHMIMIC KHEI */ { 0x0ff1, 0x0000, 0x0000, 0x0ff1 }, /* 2cca COPTIC CAPITAL LETTER DIALECT-P HORI */ { 0x0000, 0x0ff3, 0x0ff3, 0x0000 }, /* 2ccb COPTIC SMALL LETTER DIALECT-P HORI */ { 0x0ff5, 0x0000, 0x0000, 0x0ff5 }, /* 2ccc COPTIC CAPITAL LETTER OLD COPTIC HORI */ { 0x0000, 0x0ff7, 0x0ff7, 0x0000 }, /* 2ccd COPTIC SMALL LETTER OLD COPTIC HORI */ { 0x0ff9, 0x0000, 0x0000, 0x0ff9 }, /* 2cce COPTIC CAPITAL LETTER OLD COPTIC HA */ { 0x0000, 0x0ffb, 0x0ffb, 0x0000 }, /* 2ccf COPTIC SMALL LETTER OLD COPTIC HA */ { 0x0ffd, 0x0000, 0x0000, 0x0ffd }, /* 2cd0 COPTIC CAPITAL LETTER L-SHAPED HA */ { 0x0000, 0x0fff, 0x0fff, 0x0000 }, /* 2cd1 COPTIC SMALL LETTER L-SHAPED HA */ { 0x1001, 0x0000, 0x0000, 0x1001 }, /* 2cd2 COPTIC CAPITAL LETTER OLD COPTIC HEI */ { 0x0000, 0x1003, 0x1003, 0x0000 }, /* 2cd3 COPTIC SMALL LETTER OLD COPTIC HEI */ { 0x1005, 0x0000, 0x0000, 0x1005 }, /* 2cd4 COPTIC CAPITAL LETTER OLD COPTIC HAT */ { 0x0000, 0x1007, 0x1007, 0x0000 }, /* 2cd5 COPTIC SMALL LETTER OLD COPTIC HAT */ { 0x1009, 0x0000, 0x0000, 0x1009 }, /* 2cd6 COPTIC CAPITAL LETTER OLD COPTIC GANGIA */ { 0x0000, 0x100b, 0x100b, 0x0000 }, /* 2cd7 COPTIC SMALL LETTER OLD COPTIC GANGIA */ { 0x100d, 0x0000, 0x0000, 0x100d }, /* 2cd8 COPTIC CAPITAL LETTER OLD COPTIC DJA */ { 0x0000, 0x100f, 0x100f, 0x0000 }, /* 2cd9 COPTIC SMALL LETTER OLD COPTIC DJA */ { 0x1011, 0x0000, 0x0000, 0x1011 }, /* 2cda COPTIC CAPITAL LETTER OLD COPTIC SHIMA */ { 0x0000, 0x1013, 0x1013, 0x0000 }, /* 2cdb COPTIC SMALL LETTER OLD COPTIC SHIMA */ { 0x1015, 0x0000, 0x0000, 0x1015 }, /* 2cdc COPTIC CAPITAL LETTER OLD NUBIAN SHIMA */ { 0x0000, 0x1017, 0x1017, 0x0000 }, /* 2cdd COPTIC SMALL LETTER OLD NUBIAN SHIMA */ { 0x1019, 0x0000, 0x0000, 0x1019 }, /* 2cde COPTIC CAPITAL LETTER OLD NUBIAN NGI */ { 0x0000, 0x101b, 0x101b, 0x0000 }, /* 2cdf COPTIC SMALL LETTER OLD NUBIAN NGI */ { 0x101d, 0x0000, 0x0000, 0x101d }, /* 2ce0 COPTIC CAPITAL LETTER OLD NUBIAN NYI */ { 0x0000, 0x101f, 0x101f, 0x0000 }, /* 2ce1 COPTIC SMALL LETTER OLD NUBIAN NYI */ { 0x1021, 0x0000, 0x0000, 0x1021 }, /* 2ce2 COPTIC CAPITAL LETTER OLD NUBIAN WAU */ { 0x0000, 0x1023, 0x1023, 0x0000 }, /* 2ce3 COPTIC SMALL LETTER OLD NUBIAN WAU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2ce4 COPTIC SYMBOL KAI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2ce5 COPTIC SYMBOL MI RO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2ce6 COPTIC SYMBOL PI RO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2ce7 COPTIC SYMBOL STAUROS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2ce8 COPTIC SYMBOL TAU RO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2ce9 COPTIC SYMBOL KHI RO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cea COPTIC SYMBOL SHIMA SIMA */ { 0x1025, 0x0000, 0x0000, 0x1025 }, /* 2ceb COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHE */ { 0x0000, 0x1027, 0x1027, 0x0000 }, /* 2cec COPTIC SMALL LETTER CRYPTOGRAMMIC SHEI */ { 0x1029, 0x0000, 0x0000, 0x1029 }, /* 2ced COPTIC CAPITAL LETTER CRYPTOGRAMMIC GAN */ { 0x0000, 0x102b, 0x102b, 0x0000 }, /* 2cee COPTIC SMALL LETTER CRYPTOGRAMMIC GANGI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cef COPTIC COMBINING NI ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cf0 COPTIC COMBINING SPIRITUS ASPER */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cf1 COPTIC COMBINING SPIRITUS LENIS */ { 0x102d, 0x0000, 0x0000, 0x102d }, /* 2cf2 COPTIC CAPITAL LETTER BOHAIRIC KHEI */ { 0x0000, 0x102f, 0x102f, 0x0000 }, /* 2cf3 COPTIC SMALL LETTER BOHAIRIC KHEI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cf4 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cf5 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cf6 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cf7 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cf8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cf9 COPTIC OLD NUBIAN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cfa COPTIC OLD NUBIAN DIRECT QUESTION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cfb COPTIC OLD NUBIAN INDIRECT QUESTION MAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cfc COPTIC OLD NUBIAN VERSE DIVIDER */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cfd COPTIC FRACTION ONE HALF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cfe COPTIC FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 2cff COPTIC MORPHOLOGICAL DIVIDER */ }; static case_table case_pg_05a[128] = { { 0x0000, 0x1031, 0x1031, 0x0000 }, /* 2d00 GEORGIAN SMALL LETTER AN */ { 0x0000, 0x1033, 0x1033, 0x0000 }, /* 2d01 GEORGIAN SMALL LETTER BAN */ { 0x0000, 0x1035, 0x1035, 0x0000 }, /* 2d02 GEORGIAN SMALL LETTER GAN */ { 0x0000, 0x1037, 0x1037, 0x0000 }, /* 2d03 GEORGIAN SMALL LETTER DON */ { 0x0000, 0x1039, 0x1039, 0x0000 }, /* 2d04 GEORGIAN SMALL LETTER EN */ { 0x0000, 0x103b, 0x103b, 0x0000 }, /* 2d05 GEORGIAN SMALL LETTER VIN */ { 0x0000, 0x103d, 0x103d, 0x0000 }, /* 2d06 GEORGIAN SMALL LETTER ZEN */ { 0x0000, 0x103f, 0x103f, 0x0000 }, /* 2d07 GEORGIAN SMALL LETTER TAN */ { 0x0000, 0x1041, 0x1041, 0x0000 }, /* 2d08 GEORGIAN SMALL LETTER IN */ { 0x0000, 0x1043, 0x1043, 0x0000 }, /* 2d09 GEORGIAN SMALL LETTER KAN */ { 0x0000, 0x1045, 0x1045, 0x0000 }, /* 2d0a GEORGIAN SMALL LETTER LAS */ { 0x0000, 0x1047, 0x1047, 0x0000 }, /* 2d0b GEORGIAN SMALL LETTER MAN */ { 0x0000, 0x1049, 0x1049, 0x0000 }, /* 2d0c GEORGIAN SMALL LETTER NAR */ { 0x0000, 0x104b, 0x104b, 0x0000 }, /* 2d0d GEORGIAN SMALL LETTER ON */ { 0x0000, 0x104d, 0x104d, 0x0000 }, /* 2d0e GEORGIAN SMALL LETTER PAR */ { 0x0000, 0x104f, 0x104f, 0x0000 }, /* 2d0f GEORGIAN SMALL LETTER ZHAR */ { 0x0000, 0x1051, 0x1051, 0x0000 }, /* 2d10 GEORGIAN SMALL LETTER RAE */ { 0x0000, 0x1053, 0x1053, 0x0000 }, /* 2d11 GEORGIAN SMALL LETTER SAN */ { 0x0000, 0x1055, 0x1055, 0x0000 }, /* 2d12 GEORGIAN SMALL LETTER TAR */ { 0x0000, 0x1057, 0x1057, 0x0000 }, /* 2d13 GEORGIAN SMALL LETTER UN */ { 0x0000, 0x1059, 0x1059, 0x0000 }, /* 2d14 GEORGIAN SMALL LETTER PHAR */ { 0x0000, 0x105b, 0x105b, 0x0000 }, /* 2d15 GEORGIAN SMALL LETTER KHAR */ { 0x0000, 0x105d, 0x105d, 0x0000 }, /* 2d16 GEORGIAN SMALL LETTER GHAN */ { 0x0000, 0x105f, 0x105f, 0x0000 }, /* 2d17 GEORGIAN SMALL LETTER QAR */ { 0x0000, 0x1061, 0x1061, 0x0000 }, /* 2d18 GEORGIAN SMALL LETTER SHIN */ { 0x0000, 0x1063, 0x1063, 0x0000 }, /* 2d19 GEORGIAN SMALL LETTER CHIN */ { 0x0000, 0x1065, 0x1065, 0x0000 }, /* 2d1a GEORGIAN SMALL LETTER CAN */ { 0x0000, 0x1067, 0x1067, 0x0000 }, /* 2d1b GEORGIAN SMALL LETTER JIL */ { 0x0000, 0x1069, 0x1069, 0x0000 }, /* 2d1c GEORGIAN SMALL LETTER CIL */ { 0x0000, 0x106b, 0x106b, 0x0000 }, /* 2d1d GEORGIAN SMALL LETTER CHAR */ { 0x0000, 0x106d, 0x106d, 0x0000 }, /* 2d1e GEORGIAN SMALL LETTER XAN */ { 0x0000, 0x106f, 0x106f, 0x0000 }, /* 2d1f GEORGIAN SMALL LETTER JHAN */ { 0x0000, 0x1071, 0x1071, 0x0000 }, /* 2d20 GEORGIAN SMALL LETTER HAE */ { 0x0000, 0x1073, 0x1073, 0x0000 }, /* 2d21 GEORGIAN SMALL LETTER HE */ { 0x0000, 0x1075, 0x1075, 0x0000 }, /* 2d22 GEORGIAN SMALL LETTER HIE */ { 0x0000, 0x1077, 0x1077, 0x0000 }, /* 2d23 GEORGIAN SMALL LETTER WE */ { 0x0000, 0x1079, 0x1079, 0x0000 }, /* 2d24 GEORGIAN SMALL LETTER HAR */ { 0x0000, 0x107b, 0x107b, 0x0000 }, /* 2d25 GEORGIAN SMALL LETTER HOE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d26 (null) */ { 0x0000, 0x107d, 0x107d, 0x0000 }, /* 2d27 GEORGIAN SMALL LETTER YN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d28 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d29 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d2a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d2b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d2c (null) */ { 0x0000, 0x107f, 0x107f, 0x0000 }, /* 2d2d GEORGIAN SMALL LETTER AEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d2e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d2f (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d30 TIFINAGH LETTER YA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d31 TIFINAGH LETTER YAB */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d32 TIFINAGH LETTER YABH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d33 TIFINAGH LETTER YAG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d34 TIFINAGH LETTER YAGHH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d35 TIFINAGH LETTER BERBER ACADEMY YAJ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d36 TIFINAGH LETTER YAJ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d37 TIFINAGH LETTER YAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d38 TIFINAGH LETTER YADH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d39 TIFINAGH LETTER YADD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d3a TIFINAGH LETTER YADDH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d3b TIFINAGH LETTER YEY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d3c TIFINAGH LETTER YAF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d3d TIFINAGH LETTER YAK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d3e TIFINAGH LETTER TUAREG YAK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d3f TIFINAGH LETTER YAKHH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d40 TIFINAGH LETTER YAH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d41 TIFINAGH LETTER BERBER ACADEMY YAH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d42 TIFINAGH LETTER TUAREG YAH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d43 TIFINAGH LETTER YAHH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d44 TIFINAGH LETTER YAA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d45 TIFINAGH LETTER YAKH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d46 TIFINAGH LETTER TUAREG YAKH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d47 TIFINAGH LETTER YAQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d48 TIFINAGH LETTER TUAREG YAQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d49 TIFINAGH LETTER YI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d4a TIFINAGH LETTER YAZH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d4b TIFINAGH LETTER AHAGGAR YAZH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d4c TIFINAGH LETTER TUAREG YAZH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d4d TIFINAGH LETTER YAL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d4e TIFINAGH LETTER YAM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d4f TIFINAGH LETTER YAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d50 TIFINAGH LETTER TUAREG YAGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d51 TIFINAGH LETTER TUAREG YANG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d52 TIFINAGH LETTER YAP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d53 TIFINAGH LETTER YU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d54 TIFINAGH LETTER YAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d55 TIFINAGH LETTER YARR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d56 TIFINAGH LETTER YAGH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d57 TIFINAGH LETTER TUAREG YAGH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d58 TIFINAGH LETTER AYER YAGH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d59 TIFINAGH LETTER YAS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d5a TIFINAGH LETTER YASS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d5b TIFINAGH LETTER YASH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d5c TIFINAGH LETTER YAT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d5d TIFINAGH LETTER YATH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d5e TIFINAGH LETTER YACH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d5f TIFINAGH LETTER YATT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d60 TIFINAGH LETTER YAV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d61 TIFINAGH LETTER YAW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d62 TIFINAGH LETTER YAY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d63 TIFINAGH LETTER YAZ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d64 TIFINAGH LETTER TAWELLEMET YAZ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d65 TIFINAGH LETTER YAZZ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d66 TIFINAGH LETTER YE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d67 TIFINAGH LETTER YO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d68 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d69 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d6a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d6b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d6c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d6d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d6e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d6f TIFINAGH MODIFIER LETTER LABIALIZATION */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d70 TIFINAGH SEPARATOR MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d71 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d72 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d73 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d74 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d75 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d76 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d77 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d78 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d79 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d7a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d7b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d7c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d7d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d7e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 2d7f TIFINAGH CONSONANT JOINER */ }; static case_table case_pg_14c[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a600 VAI SYLLABLE JE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a601 VAI SYLLABLE NJE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a602 VAI SYLLABLE YE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a603 VAI SYLLABLE KE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a604 VAI SYLLABLE NGGE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a605 VAI SYLLABLE NGGEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a606 VAI SYLLABLE GE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a607 VAI SYLLABLE GEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a608 VAI SYLLABLE ME */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a609 VAI SYLLABLE NE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a60a VAI SYLLABLE NYE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a60b VAI SYLLABLE NG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a60c VAI SYLLABLE LENGTHENER */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a60d VAI COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a60e VAI FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a60f VAI QUESTION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a610 VAI SYLLABLE NDOLE FA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a611 VAI SYLLABLE NDOLE KA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a612 VAI SYLLABLE NDOLE SOO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a613 VAI SYMBOL FEENG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a614 VAI SYMBOL KEENG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a615 VAI SYMBOL TING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a616 VAI SYMBOL NII */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a617 VAI SYMBOL BANG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a618 VAI SYMBOL FAA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a619 VAI SYMBOL TAA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a61a VAI SYMBOL DANG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a61b VAI SYMBOL DOONG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a61c VAI SYMBOL KUNG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a61d VAI SYMBOL TONG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a61e VAI SYMBOL DO-O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a61f VAI SYMBOL JONG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a620 VAI DIGIT ZERO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a621 VAI DIGIT ONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a622 VAI DIGIT TWO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a623 VAI DIGIT THREE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a624 VAI DIGIT FOUR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a625 VAI DIGIT FIVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a626 VAI DIGIT SIX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a627 VAI DIGIT SEVEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a628 VAI DIGIT EIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a629 VAI DIGIT NINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a62a VAI SYLLABLE NDOLE MA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a62b VAI SYLLABLE NDOLE DO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a62c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a62d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a62e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a62f (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a630 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a631 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a632 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a633 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a634 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a635 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a636 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a637 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a638 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a639 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a63a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a63b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a63c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a63d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a63e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a63f (null) */ { 0x1081, 0x0000, 0x0000, 0x1081 }, /* a640 CYRILLIC CAPITAL LETTER ZEMLYA */ { 0x0000, 0x1083, 0x1083, 0x0000 }, /* a641 CYRILLIC SMALL LETTER ZEMLYA */ { 0x1085, 0x0000, 0x0000, 0x1085 }, /* a642 CYRILLIC CAPITAL LETTER DZELO */ { 0x0000, 0x1087, 0x1087, 0x0000 }, /* a643 CYRILLIC SMALL LETTER DZELO */ { 0x1089, 0x0000, 0x0000, 0x1089 }, /* a644 CYRILLIC CAPITAL LETTER REVERSED DZE */ { 0x0000, 0x108b, 0x108b, 0x0000 }, /* a645 CYRILLIC SMALL LETTER REVERSED DZE */ { 0x108d, 0x0000, 0x0000, 0x108d }, /* a646 CYRILLIC CAPITAL LETTER IOTA */ { 0x0000, 0x108f, 0x108f, 0x0000 }, /* a647 CYRILLIC SMALL LETTER IOTA */ { 0x1091, 0x0000, 0x0000, 0x1091 }, /* a648 CYRILLIC CAPITAL LETTER DJERV */ { 0x0000, 0x1093, 0x1093, 0x0000 }, /* a649 CYRILLIC SMALL LETTER DJERV */ { 0x1095, 0x0000, 0x0000, 0x1095 }, /* a64a CYRILLIC CAPITAL LETTER MONOGRAPH UK */ { 0x0000, 0x1097, 0x1097, 0x0000 }, /* a64b CYRILLIC SMALL LETTER MONOGRAPH UK */ { 0x1099, 0x0000, 0x0000, 0x1099 }, /* a64c CYRILLIC CAPITAL LETTER BROAD OMEGA */ { 0x0000, 0x109b, 0x109b, 0x0000 }, /* a64d CYRILLIC SMALL LETTER BROAD OMEGA */ { 0x109d, 0x0000, 0x0000, 0x109d }, /* a64e CYRILLIC CAPITAL LETTER NEUTRAL YER */ { 0x0000, 0x109f, 0x109f, 0x0000 }, /* a64f CYRILLIC SMALL LETTER NEUTRAL YER */ { 0x10a1, 0x0000, 0x0000, 0x10a1 }, /* a650 CYRILLIC CAPITAL LETTER YERU WITH BACK */ { 0x0000, 0x10a3, 0x10a3, 0x0000 }, /* a651 CYRILLIC SMALL LETTER YERU WITH BACK YE */ { 0x10a5, 0x0000, 0x0000, 0x10a5 }, /* a652 CYRILLIC CAPITAL LETTER IOTIFIED YAT */ { 0x0000, 0x10a7, 0x10a7, 0x0000 }, /* a653 CYRILLIC SMALL LETTER IOTIFIED YAT */ { 0x10a9, 0x0000, 0x0000, 0x10a9 }, /* a654 CYRILLIC CAPITAL LETTER REVERSED YU */ { 0x0000, 0x10ab, 0x10ab, 0x0000 }, /* a655 CYRILLIC SMALL LETTER REVERSED YU */ { 0x10ad, 0x0000, 0x0000, 0x10ad }, /* a656 CYRILLIC CAPITAL LETTER IOTIFIED A */ { 0x0000, 0x10af, 0x10af, 0x0000 }, /* a657 CYRILLIC SMALL LETTER IOTIFIED A */ { 0x10b1, 0x0000, 0x0000, 0x10b1 }, /* a658 CYRILLIC CAPITAL LETTER CLOSED LITTLE Y */ { 0x0000, 0x10b3, 0x10b3, 0x0000 }, /* a659 CYRILLIC SMALL LETTER CLOSED LITTLE YUS */ { 0x10b5, 0x0000, 0x0000, 0x10b5 }, /* a65a CYRILLIC CAPITAL LETTER BLENDED YUS */ { 0x0000, 0x10b7, 0x10b7, 0x0000 }, /* a65b CYRILLIC SMALL LETTER BLENDED YUS */ { 0x10b9, 0x0000, 0x0000, 0x10b9 }, /* a65c CYRILLIC CAPITAL LETTER IOTIFIED CLOSED */ { 0x0000, 0x10bb, 0x10bb, 0x0000 }, /* a65d CYRILLIC SMALL LETTER IOTIFIED CLOSED L */ { 0x10bd, 0x0000, 0x0000, 0x10bd }, /* a65e CYRILLIC CAPITAL LETTER YN */ { 0x0000, 0x10bf, 0x10bf, 0x0000 }, /* a65f CYRILLIC SMALL LETTER YN */ { 0x10c1, 0x0000, 0x0000, 0x10c1 }, /* a660 CYRILLIC CAPITAL LETTER REVERSED TSE */ { 0x0000, 0x10c3, 0x10c3, 0x0000 }, /* a661 CYRILLIC SMALL LETTER REVERSED TSE */ { 0x10c5, 0x0000, 0x0000, 0x10c5 }, /* a662 CYRILLIC CAPITAL LETTER SOFT DE */ { 0x0000, 0x10c7, 0x10c7, 0x0000 }, /* a663 CYRILLIC SMALL LETTER SOFT DE */ { 0x10c9, 0x0000, 0x0000, 0x10c9 }, /* a664 CYRILLIC CAPITAL LETTER SOFT EL */ { 0x0000, 0x10cb, 0x10cb, 0x0000 }, /* a665 CYRILLIC SMALL LETTER SOFT EL */ { 0x10cd, 0x0000, 0x0000, 0x10cd }, /* a666 CYRILLIC CAPITAL LETTER SOFT EM */ { 0x0000, 0x10cf, 0x10cf, 0x0000 }, /* a667 CYRILLIC SMALL LETTER SOFT EM */ { 0x10d1, 0x0000, 0x0000, 0x10d1 }, /* a668 CYRILLIC CAPITAL LETTER MONOCULAR O */ { 0x0000, 0x10d3, 0x10d3, 0x0000 }, /* a669 CYRILLIC SMALL LETTER MONOCULAR O */ { 0x10d5, 0x0000, 0x0000, 0x10d5 }, /* a66a CYRILLIC CAPITAL LETTER BINOCULAR O */ { 0x0000, 0x10d7, 0x10d7, 0x0000 }, /* a66b CYRILLIC SMALL LETTER BINOCULAR O */ { 0x10d9, 0x0000, 0x0000, 0x10d9 }, /* a66c CYRILLIC CAPITAL LETTER DOUBLE MONOCULA */ { 0x0000, 0x10db, 0x10db, 0x0000 }, /* a66d CYRILLIC SMALL LETTER DOUBLE MONOCULAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a66e CYRILLIC LETTER MULTIOCULAR O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a66f COMBINING CYRILLIC VZMET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a670 COMBINING CYRILLIC TEN MILLIONS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a671 COMBINING CYRILLIC HUNDRED MILLIONS SIG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a672 COMBINING CYRILLIC THOUSAND MILLIONS SI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a673 SLAVONIC ASTERISK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a674 COMBINING CYRILLIC LETTER UKRAINIAN IE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a675 COMBINING CYRILLIC LETTER I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a676 COMBINING CYRILLIC LETTER YI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a677 COMBINING CYRILLIC LETTER U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a678 COMBINING CYRILLIC LETTER HARD SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a679 COMBINING CYRILLIC LETTER YERU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a67a COMBINING CYRILLIC LETTER SOFT SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a67b COMBINING CYRILLIC LETTER OMEGA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a67c COMBINING CYRILLIC KAVYKA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a67d COMBINING CYRILLIC PAYEROK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a67e CYRILLIC KAVYKA */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* a67f CYRILLIC PAYEROK */ }; static case_table case_pg_14d[128] = { { 0x10dd, 0x0000, 0x0000, 0x10dd }, /* a680 CYRILLIC CAPITAL LETTER DWE */ { 0x0000, 0x10df, 0x10df, 0x0000 }, /* a681 CYRILLIC SMALL LETTER DWE */ { 0x10e1, 0x0000, 0x0000, 0x10e1 }, /* a682 CYRILLIC CAPITAL LETTER DZWE */ { 0x0000, 0x10e3, 0x10e3, 0x0000 }, /* a683 CYRILLIC SMALL LETTER DZWE */ { 0x10e5, 0x0000, 0x0000, 0x10e5 }, /* a684 CYRILLIC CAPITAL LETTER ZHWE */ { 0x0000, 0x10e7, 0x10e7, 0x0000 }, /* a685 CYRILLIC SMALL LETTER ZHWE */ { 0x10e9, 0x0000, 0x0000, 0x10e9 }, /* a686 CYRILLIC CAPITAL LETTER CCHE */ { 0x0000, 0x10eb, 0x10eb, 0x0000 }, /* a687 CYRILLIC SMALL LETTER CCHE */ { 0x10ed, 0x0000, 0x0000, 0x10ed }, /* a688 CYRILLIC CAPITAL LETTER DZZE */ { 0x0000, 0x10ef, 0x10ef, 0x0000 }, /* a689 CYRILLIC SMALL LETTER DZZE */ { 0x10f1, 0x0000, 0x0000, 0x10f1 }, /* a68a CYRILLIC CAPITAL LETTER TE WITH MIDDLE */ { 0x0000, 0x10f3, 0x10f3, 0x0000 }, /* a68b CYRILLIC SMALL LETTER TE WITH MIDDLE HO */ { 0x10f5, 0x0000, 0x0000, 0x10f5 }, /* a68c CYRILLIC CAPITAL LETTER TWE */ { 0x0000, 0x10f7, 0x10f7, 0x0000 }, /* a68d CYRILLIC SMALL LETTER TWE */ { 0x10f9, 0x0000, 0x0000, 0x10f9 }, /* a68e CYRILLIC CAPITAL LETTER TSWE */ { 0x0000, 0x10fb, 0x10fb, 0x0000 }, /* a68f CYRILLIC SMALL LETTER TSWE */ { 0x10fd, 0x0000, 0x0000, 0x10fd }, /* a690 CYRILLIC CAPITAL LETTER TSSE */ { 0x0000, 0x10ff, 0x10ff, 0x0000 }, /* a691 CYRILLIC SMALL LETTER TSSE */ { 0x1101, 0x0000, 0x0000, 0x1101 }, /* a692 CYRILLIC CAPITAL LETTER TCHE */ { 0x0000, 0x1103, 0x1103, 0x0000 }, /* a693 CYRILLIC SMALL LETTER TCHE */ { 0x1105, 0x0000, 0x0000, 0x1105 }, /* a694 CYRILLIC CAPITAL LETTER HWE */ { 0x0000, 0x1107, 0x1107, 0x0000 }, /* a695 CYRILLIC SMALL LETTER HWE */ { 0x1109, 0x0000, 0x0000, 0x1109 }, /* a696 CYRILLIC CAPITAL LETTER SHWE */ { 0x0000, 0x110b, 0x110b, 0x0000 }, /* a697 CYRILLIC SMALL LETTER SHWE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a698 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a699 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a69a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a69b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a69c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a69d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a69e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a69f COMBINING CYRILLIC LETTER IOTIFIED E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a0 BAMUM LETTER A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a1 BAMUM LETTER KA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a2 BAMUM LETTER U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a3 BAMUM LETTER KU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a4 BAMUM LETTER EE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a5 BAMUM LETTER REE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a6 BAMUM LETTER TAE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a7 BAMUM LETTER O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a8 BAMUM LETTER NYI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a9 BAMUM LETTER I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6aa BAMUM LETTER LA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ab BAMUM LETTER PA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ac BAMUM LETTER RII */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ad BAMUM LETTER RIEE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ae BAMUM LETTER LEEEE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6af BAMUM LETTER MEEEE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b0 BAMUM LETTER TAA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b1 BAMUM LETTER NDAA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b2 BAMUM LETTER NJAEM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b3 BAMUM LETTER M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b4 BAMUM LETTER SUU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b5 BAMUM LETTER MU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b6 BAMUM LETTER SHII */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b7 BAMUM LETTER SI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b8 BAMUM LETTER SHEUX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b9 BAMUM LETTER SEUX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ba BAMUM LETTER KYEE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6bb BAMUM LETTER KET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6bc BAMUM LETTER NUAE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6bd BAMUM LETTER NU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6be BAMUM LETTER NJUAE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6bf BAMUM LETTER YOQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c0 BAMUM LETTER SHU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c1 BAMUM LETTER YUQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c2 BAMUM LETTER YA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c3 BAMUM LETTER NSHA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c4 BAMUM LETTER KEUX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c5 BAMUM LETTER PEUX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c6 BAMUM LETTER NJEE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c7 BAMUM LETTER NTEE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c8 BAMUM LETTER PUE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c9 BAMUM LETTER WUE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ca BAMUM LETTER PEE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6cb BAMUM LETTER FEE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6cc BAMUM LETTER RU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6cd BAMUM LETTER LU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ce BAMUM LETTER MI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6cf BAMUM LETTER NI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d0 BAMUM LETTER REUX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d1 BAMUM LETTER RAE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d2 BAMUM LETTER KEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d3 BAMUM LETTER NGKWAEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d4 BAMUM LETTER NGGA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d5 BAMUM LETTER NGA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d6 BAMUM LETTER SHO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d7 BAMUM LETTER PUAE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d8 BAMUM LETTER FU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d9 BAMUM LETTER FOM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6da BAMUM LETTER WA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6db BAMUM LETTER NA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6dc BAMUM LETTER LI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6dd BAMUM LETTER PI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6de BAMUM LETTER LOQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6df BAMUM LETTER KO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e0 BAMUM LETTER MBEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e1 BAMUM LETTER REN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e2 BAMUM LETTER MEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e3 BAMUM LETTER MA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e4 BAMUM LETTER TI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e5 BAMUM LETTER KI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e6 BAMUM LETTER MO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e7 BAMUM LETTER MBAA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e8 BAMUM LETTER TET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e9 BAMUM LETTER KPA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ea BAMUM LETTER TEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6eb BAMUM LETTER NTUU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ec BAMUM LETTER SAMBA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ed BAMUM LETTER FAAMAE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ee BAMUM LETTER KOVUU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ef BAMUM LETTER KOGHOM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f0 BAMUM COMBINING MARK KOQNDON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f1 BAMUM COMBINING MARK TUKWENTIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f2 BAMUM NJAEMLI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f3 BAMUM FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f4 BAMUM COLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f5 BAMUM COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f6 BAMUM SEMICOLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f7 BAMUM QUESTION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f9 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6fa (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6fb (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6fc (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6fd (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6fe (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* a6ff (null) */ }; static case_table case_pg_14e[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a700 MODIFIER LETTER CHINESE TONE YIN PING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a701 MODIFIER LETTER CHINESE TONE YANG PING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a702 MODIFIER LETTER CHINESE TONE YIN SHANG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a703 MODIFIER LETTER CHINESE TONE YANG SHANG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a704 MODIFIER LETTER CHINESE TONE YIN QU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a705 MODIFIER LETTER CHINESE TONE YANG QU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a706 MODIFIER LETTER CHINESE TONE YIN RU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a707 MODIFIER LETTER CHINESE TONE YANG RU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a708 MODIFIER LETTER EXTRA-HIGH DOTTED TONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a709 MODIFIER LETTER HIGH DOTTED TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a70a MODIFIER LETTER MID DOTTED TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a70b MODIFIER LETTER LOW DOTTED TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a70c MODIFIER LETTER EXTRA-LOW DOTTED TONE B */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a70d MODIFIER LETTER EXTRA-HIGH DOTTED LEFT- */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a70e MODIFIER LETTER HIGH DOTTED LEFT-STEM T */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a70f MODIFIER LETTER MID DOTTED LEFT-STEM TO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a710 MODIFIER LETTER LOW DOTTED LEFT-STEM TO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a711 MODIFIER LETTER EXTRA-LOW DOTTED LEFT-S */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a712 MODIFIER LETTER EXTRA-HIGH LEFT-STEM TO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a713 MODIFIER LETTER HIGH LEFT-STEM TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a714 MODIFIER LETTER MID LEFT-STEM TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a715 MODIFIER LETTER LOW LEFT-STEM TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a716 MODIFIER LETTER EXTRA-LOW LEFT-STEM TON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a717 MODIFIER LETTER DOT VERTICAL BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a718 MODIFIER LETTER DOT SLASH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a719 MODIFIER LETTER DOT HORIZONTAL BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a71a MODIFIER LETTER LOWER RIGHT CORNER ANGL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a71b MODIFIER LETTER RAISED UP ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a71c MODIFIER LETTER RAISED DOWN ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a71d MODIFIER LETTER RAISED EXCLAMATION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a71e MODIFIER LETTER RAISED INVERTED EXCLAMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a71f MODIFIER LETTER LOW INVERTED EXCLAMATIO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a720 MODIFIER LETTER STRESS AND HIGH TONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a721 MODIFIER LETTER STRESS AND LOW TONE */ { 0x110d, 0x0000, 0x0000, 0x110d }, /* a722 LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF */ { 0x0000, 0x110f, 0x110f, 0x0000 }, /* a723 LATIN SMALL LETTER EGYPTOLOGICAL ALEF */ { 0x1111, 0x0000, 0x0000, 0x1111 }, /* a724 LATIN CAPITAL LETTER EGYPTOLOGICAL AIN */ { 0x0000, 0x1113, 0x1113, 0x0000 }, /* a725 LATIN SMALL LETTER EGYPTOLOGICAL AIN */ { 0x1115, 0x0000, 0x0000, 0x1115 }, /* a726 LATIN CAPITAL LETTER HENG */ { 0x0000, 0x1117, 0x1117, 0x0000 }, /* a727 LATIN SMALL LETTER HENG */ { 0x1119, 0x0000, 0x0000, 0x1119 }, /* a728 LATIN CAPITAL LETTER TZ */ { 0x0000, 0x111b, 0x111b, 0x0000 }, /* a729 LATIN SMALL LETTER TZ */ { 0x111d, 0x0000, 0x0000, 0x111d }, /* a72a LATIN CAPITAL LETTER TRESILLO */ { 0x0000, 0x111f, 0x111f, 0x0000 }, /* a72b LATIN SMALL LETTER TRESILLO */ { 0x1121, 0x0000, 0x0000, 0x1121 }, /* a72c LATIN CAPITAL LETTER CUATRILLO */ { 0x0000, 0x1123, 0x1123, 0x0000 }, /* a72d LATIN SMALL LETTER CUATRILLO */ { 0x1125, 0x0000, 0x0000, 0x1125 }, /* a72e LATIN CAPITAL LETTER CUATRILLO WITH COM */ { 0x0000, 0x1127, 0x1127, 0x0000 }, /* a72f LATIN SMALL LETTER CUATRILLO WITH COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a730 LATIN LETTER SMALL CAPITAL F */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a731 LATIN LETTER SMALL CAPITAL S */ { 0x1129, 0x0000, 0x0000, 0x1129 }, /* a732 LATIN CAPITAL LETTER AA */ { 0x0000, 0x112b, 0x112b, 0x0000 }, /* a733 LATIN SMALL LETTER AA */ { 0x112d, 0x0000, 0x0000, 0x112d }, /* a734 LATIN CAPITAL LETTER AO */ { 0x0000, 0x112f, 0x112f, 0x0000 }, /* a735 LATIN SMALL LETTER AO */ { 0x1131, 0x0000, 0x0000, 0x1131 }, /* a736 LATIN CAPITAL LETTER AU */ { 0x0000, 0x1133, 0x1133, 0x0000 }, /* a737 LATIN SMALL LETTER AU */ { 0x1135, 0x0000, 0x0000, 0x1135 }, /* a738 LATIN CAPITAL LETTER AV */ { 0x0000, 0x1137, 0x1137, 0x0000 }, /* a739 LATIN SMALL LETTER AV */ { 0x1139, 0x0000, 0x0000, 0x1139 }, /* a73a LATIN CAPITAL LETTER AV WITH HORIZONTAL */ { 0x0000, 0x113b, 0x113b, 0x0000 }, /* a73b LATIN SMALL LETTER AV WITH HORIZONTAL B */ { 0x113d, 0x0000, 0x0000, 0x113d }, /* a73c LATIN CAPITAL LETTER AY */ { 0x0000, 0x113f, 0x113f, 0x0000 }, /* a73d LATIN SMALL LETTER AY */ { 0x1141, 0x0000, 0x0000, 0x1141 }, /* a73e LATIN CAPITAL LETTER REVERSED C WITH DO */ { 0x0000, 0x1143, 0x1143, 0x0000 }, /* a73f LATIN SMALL LETTER REVERSED C WITH DOT */ { 0x1145, 0x0000, 0x0000, 0x1145 }, /* a740 LATIN CAPITAL LETTER K WITH STROKE */ { 0x0000, 0x1147, 0x1147, 0x0000 }, /* a741 LATIN SMALL LETTER K WITH STROKE */ { 0x1149, 0x0000, 0x0000, 0x1149 }, /* a742 LATIN CAPITAL LETTER K WITH DIAGONAL ST */ { 0x0000, 0x114b, 0x114b, 0x0000 }, /* a743 LATIN SMALL LETTER K WITH DIAGONAL STRO */ { 0x114d, 0x0000, 0x0000, 0x114d }, /* a744 LATIN CAPITAL LETTER K WITH STROKE AND */ { 0x0000, 0x114f, 0x114f, 0x0000 }, /* a745 LATIN SMALL LETTER K WITH STROKE AND DI */ { 0x1151, 0x0000, 0x0000, 0x1151 }, /* a746 LATIN CAPITAL LETTER BROKEN L */ { 0x0000, 0x1153, 0x1153, 0x0000 }, /* a747 LATIN SMALL LETTER BROKEN L */ { 0x1155, 0x0000, 0x0000, 0x1155 }, /* a748 LATIN CAPITAL LETTER L WITH HIGH STROKE */ { 0x0000, 0x1157, 0x1157, 0x0000 }, /* a749 LATIN SMALL LETTER L WITH HIGH STROKE */ { 0x1159, 0x0000, 0x0000, 0x1159 }, /* a74a LATIN CAPITAL LETTER O WITH LONG STROKE */ { 0x0000, 0x115b, 0x115b, 0x0000 }, /* a74b LATIN SMALL LETTER O WITH LONG STROKE O */ { 0x115d, 0x0000, 0x0000, 0x115d }, /* a74c LATIN CAPITAL LETTER O WITH LOOP */ { 0x0000, 0x115f, 0x115f, 0x0000 }, /* a74d LATIN SMALL LETTER O WITH LOOP */ { 0x1161, 0x0000, 0x0000, 0x1161 }, /* a74e LATIN CAPITAL LETTER OO */ { 0x0000, 0x1163, 0x1163, 0x0000 }, /* a74f LATIN SMALL LETTER OO */ { 0x1165, 0x0000, 0x0000, 0x1165 }, /* a750 LATIN CAPITAL LETTER P WITH STROKE THRO */ { 0x0000, 0x1167, 0x1167, 0x0000 }, /* a751 LATIN SMALL LETTER P WITH STROKE THROUG */ { 0x1169, 0x0000, 0x0000, 0x1169 }, /* a752 LATIN CAPITAL LETTER P WITH FLOURISH */ { 0x0000, 0x116b, 0x116b, 0x0000 }, /* a753 LATIN SMALL LETTER P WITH FLOURISH */ { 0x116d, 0x0000, 0x0000, 0x116d }, /* a754 LATIN CAPITAL LETTER P WITH SQUIRREL TA */ { 0x0000, 0x116f, 0x116f, 0x0000 }, /* a755 LATIN SMALL LETTER P WITH SQUIRREL TAIL */ { 0x1171, 0x0000, 0x0000, 0x1171 }, /* a756 LATIN CAPITAL LETTER Q WITH STROKE THRO */ { 0x0000, 0x1173, 0x1173, 0x0000 }, /* a757 LATIN SMALL LETTER Q WITH STROKE THROUG */ { 0x1175, 0x0000, 0x0000, 0x1175 }, /* a758 LATIN CAPITAL LETTER Q WITH DIAGONAL ST */ { 0x0000, 0x1177, 0x1177, 0x0000 }, /* a759 LATIN SMALL LETTER Q WITH DIAGONAL STRO */ { 0x1179, 0x0000, 0x0000, 0x1179 }, /* a75a LATIN CAPITAL LETTER R ROTUNDA */ { 0x0000, 0x117b, 0x117b, 0x0000 }, /* a75b LATIN SMALL LETTER R ROTUNDA */ { 0x117d, 0x0000, 0x0000, 0x117d }, /* a75c LATIN CAPITAL LETTER RUM ROTUNDA */ { 0x0000, 0x117f, 0x117f, 0x0000 }, /* a75d LATIN SMALL LETTER RUM ROTUNDA */ { 0x1181, 0x0000, 0x0000, 0x1181 }, /* a75e LATIN CAPITAL LETTER V WITH DIAGONAL ST */ { 0x0000, 0x1183, 0x1183, 0x0000 }, /* a75f LATIN SMALL LETTER V WITH DIAGONAL STRO */ { 0x1185, 0x0000, 0x0000, 0x1185 }, /* a760 LATIN CAPITAL LETTER VY */ { 0x0000, 0x1187, 0x1187, 0x0000 }, /* a761 LATIN SMALL LETTER VY */ { 0x1189, 0x0000, 0x0000, 0x1189 }, /* a762 LATIN CAPITAL LETTER VISIGOTHIC Z */ { 0x0000, 0x118b, 0x118b, 0x0000 }, /* a763 LATIN SMALL LETTER VISIGOTHIC Z */ { 0x118d, 0x0000, 0x0000, 0x118d }, /* a764 LATIN CAPITAL LETTER THORN WITH STROKE */ { 0x0000, 0x118f, 0x118f, 0x0000 }, /* a765 LATIN SMALL LETTER THORN WITH STROKE */ { 0x1191, 0x0000, 0x0000, 0x1191 }, /* a766 LATIN CAPITAL LETTER THORN WITH STROKE */ { 0x0000, 0x1193, 0x1193, 0x0000 }, /* a767 LATIN SMALL LETTER THORN WITH STROKE TH */ { 0x1195, 0x0000, 0x0000, 0x1195 }, /* a768 LATIN CAPITAL LETTER VEND */ { 0x0000, 0x1197, 0x1197, 0x0000 }, /* a769 LATIN SMALL LETTER VEND */ { 0x1199, 0x0000, 0x0000, 0x1199 }, /* a76a LATIN CAPITAL LETTER ET */ { 0x0000, 0x119b, 0x119b, 0x0000 }, /* a76b LATIN SMALL LETTER ET */ { 0x119d, 0x0000, 0x0000, 0x119d }, /* a76c LATIN CAPITAL LETTER IS */ { 0x0000, 0x119f, 0x119f, 0x0000 }, /* a76d LATIN SMALL LETTER IS */ { 0x11a1, 0x0000, 0x0000, 0x11a1 }, /* a76e LATIN CAPITAL LETTER CON */ { 0x0000, 0x11a3, 0x11a3, 0x0000 }, /* a76f LATIN SMALL LETTER CON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a770 MODIFIER LETTER US */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a771 LATIN SMALL LETTER DUM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a772 LATIN SMALL LETTER LUM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a773 LATIN SMALL LETTER MUM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a774 LATIN SMALL LETTER NUM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a775 LATIN SMALL LETTER RUM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a776 LATIN LETTER SMALL CAPITAL RUM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a777 LATIN SMALL LETTER TUM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a778 LATIN SMALL LETTER UM */ { 0x11a5, 0x0000, 0x0000, 0x11a5 }, /* a779 LATIN CAPITAL LETTER INSULAR D */ { 0x0000, 0x11a7, 0x11a7, 0x0000 }, /* a77a LATIN SMALL LETTER INSULAR D */ { 0x11a9, 0x0000, 0x0000, 0x11a9 }, /* a77b LATIN CAPITAL LETTER INSULAR F */ { 0x0000, 0x11ab, 0x11ab, 0x0000 }, /* a77c LATIN SMALL LETTER INSULAR F */ { 0x11ad, 0x0000, 0x0000, 0x11ad }, /* a77d LATIN CAPITAL LETTER INSULAR G */ { 0x11af, 0x0000, 0x0000, 0x11af }, /* a77e LATIN CAPITAL LETTER TURNED INSULAR G */ { 0x0000, 0x11b1, 0x11b1, 0x0000 } /* a77f LATIN SMALL LETTER TURNED INSULAR G */ }; static case_table case_pg_14f[128] = { { 0x11b3, 0x0000, 0x0000, 0x11b3 }, /* a780 LATIN CAPITAL LETTER TURNED L */ { 0x0000, 0x11b5, 0x11b5, 0x0000 }, /* a781 LATIN SMALL LETTER TURNED L */ { 0x11b7, 0x0000, 0x0000, 0x11b7 }, /* a782 LATIN CAPITAL LETTER INSULAR R */ { 0x0000, 0x11b9, 0x11b9, 0x0000 }, /* a783 LATIN SMALL LETTER INSULAR R */ { 0x11bb, 0x0000, 0x0000, 0x11bb }, /* a784 LATIN CAPITAL LETTER INSULAR S */ { 0x0000, 0x11bd, 0x11bd, 0x0000 }, /* a785 LATIN SMALL LETTER INSULAR S */ { 0x11bf, 0x0000, 0x0000, 0x11bf }, /* a786 LATIN CAPITAL LETTER INSULAR T */ { 0x0000, 0x11c1, 0x11c1, 0x0000 }, /* a787 LATIN SMALL LETTER INSULAR T */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a788 MODIFIER LETTER LOW CIRCUMFLEX ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a789 MODIFIER LETTER COLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a78a MODIFIER LETTER SHORT EQUALS SIGN */ { 0x11c3, 0x0000, 0x0000, 0x11c3 }, /* a78b LATIN CAPITAL LETTER SALTILLO */ { 0x0000, 0x11c5, 0x11c5, 0x0000 }, /* a78c LATIN SMALL LETTER SALTILLO */ { 0x11c7, 0x0000, 0x0000, 0x11c7 }, /* a78d LATIN CAPITAL LETTER TURNED H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a78e LATIN SMALL LETTER L WITH RETROFLEX HOO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a78f (null) */ { 0x11c9, 0x0000, 0x0000, 0x11c9 }, /* a790 LATIN CAPITAL LETTER N WITH DESCENDER */ { 0x0000, 0x11cb, 0x11cb, 0x0000 }, /* a791 LATIN SMALL LETTER N WITH DESCENDER */ { 0x11cd, 0x0000, 0x0000, 0x11cd }, /* a792 LATIN CAPITAL LETTER C WITH BAR */ { 0x0000, 0x11cf, 0x11cf, 0x0000 }, /* a793 LATIN SMALL LETTER C WITH BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a794 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a795 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a796 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a797 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a798 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a799 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a79a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a79b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a79c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a79d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a79e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a79f (null) */ { 0x11d1, 0x0000, 0x0000, 0x11d1 }, /* a7a0 LATIN CAPITAL LETTER G WITH OBLIQUE STR */ { 0x0000, 0x11d3, 0x11d3, 0x0000 }, /* a7a1 LATIN SMALL LETTER G WITH OBLIQUE STROK */ { 0x11d5, 0x0000, 0x0000, 0x11d5 }, /* a7a2 LATIN CAPITAL LETTER K WITH OBLIQUE STR */ { 0x0000, 0x11d7, 0x11d7, 0x0000 }, /* a7a3 LATIN SMALL LETTER K WITH OBLIQUE STROK */ { 0x11d9, 0x0000, 0x0000, 0x11d9 }, /* a7a4 LATIN CAPITAL LETTER N WITH OBLIQUE STR */ { 0x0000, 0x11db, 0x11db, 0x0000 }, /* a7a5 LATIN SMALL LETTER N WITH OBLIQUE STROK */ { 0x11dd, 0x0000, 0x0000, 0x11dd }, /* a7a6 LATIN CAPITAL LETTER R WITH OBLIQUE STR */ { 0x0000, 0x11df, 0x11df, 0x0000 }, /* a7a7 LATIN SMALL LETTER R WITH OBLIQUE STROK */ { 0x11e1, 0x0000, 0x0000, 0x11e1 }, /* a7a8 LATIN CAPITAL LETTER S WITH OBLIQUE STR */ { 0x0000, 0x11e3, 0x11e3, 0x0000 }, /* a7a9 LATIN SMALL LETTER S WITH OBLIQUE STROK */ { 0x11e5, 0x0000, 0x0000, 0x11e5 }, /* a7aa LATIN CAPITAL LETTER H WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ab (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ac (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ad (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ae (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7af (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b0 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b1 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b2 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b3 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b4 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b5 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b6 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b7 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b9 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ba (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7bb (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7bc (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7bd (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7be (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7bf (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c0 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c1 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c2 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c3 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c4 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c5 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c6 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c7 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c9 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ca (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7cb (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7cc (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7cd (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ce (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7cf (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d0 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d1 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d2 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d3 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d4 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d5 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d6 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d7 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d9 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7da (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7db (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7dc (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7dd (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7de (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7df (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e0 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e1 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e2 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e3 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e4 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e5 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e6 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e7 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e9 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ea (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7eb (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ec (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ed (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ee (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ef (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f0 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f1 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f2 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f3 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f4 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f5 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f6 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f7 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f8 MODIFIER LETTER CAPITAL H WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f9 MODIFIER LETTER SMALL LIGATURE OE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7fa LATIN LETTER SMALL CAPITAL TURNED M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7fb LATIN EPIGRAPHIC LETTER REVERSED F */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7fc LATIN EPIGRAPHIC LETTER REVERSED P */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7fd LATIN EPIGRAPHIC LETTER INVERTED M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7fe LATIN EPIGRAPHIC LETTER I LONGA */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* a7ff LATIN EPIGRAPHIC LETTER ARCHAIC M */ }; static case_table case_pg_1f6[128] = { { 0x0000, 0x11e7, 0x11ea, 0x11ed }, /* fb00 LATIN SMALL LIGATURE FF */ { 0x0000, 0x11f0, 0x11f3, 0x11f6 }, /* fb01 LATIN SMALL LIGATURE FI */ { 0x0000, 0x11f9, 0x11fc, 0x11ff }, /* fb02 LATIN SMALL LIGATURE FL */ { 0x0000, 0x1202, 0x1206, 0x120a }, /* fb03 LATIN SMALL LIGATURE FFI */ { 0x0000, 0x120e, 0x1212, 0x1216 }, /* fb04 LATIN SMALL LIGATURE FFL */ { 0x0000, 0x121a, 0x121d, 0x1220 }, /* fb05 LATIN SMALL LIGATURE LONG S T */ { 0x0000, 0x1223, 0x1226, 0x1229 }, /* fb06 LATIN SMALL LIGATURE ST */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb07 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb08 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb09 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb0a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb0b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb0c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb0d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb0e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb0f (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb10 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb11 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb12 (null) */ { 0x0000, 0x122c, 0x122f, 0x1232 }, /* fb13 ARMENIAN SMALL LIGATURE MEN NOW */ { 0x0000, 0x1235, 0x1238, 0x123b }, /* fb14 ARMENIAN SMALL LIGATURE MEN ECH */ { 0x0000, 0x123e, 0x1241, 0x1244 }, /* fb15 ARMENIAN SMALL LIGATURE MEN INI */ { 0x0000, 0x1247, 0x124a, 0x124d }, /* fb16 ARMENIAN SMALL LIGATURE VEW NOW */ { 0x0000, 0x1250, 0x1253, 0x1256 }, /* fb17 ARMENIAN SMALL LIGATURE MEN XEH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb18 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb19 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb1a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb1b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb1c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb1d HEBREW LETTER YOD WITH HIRIQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb1e HEBREW POINT JUDEO-SPANISH VARIKA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb1f HEBREW LIGATURE YIDDISH YOD YOD PATAH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb20 HEBREW LETTER ALTERNATIVE AYIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb21 HEBREW LETTER WIDE ALEF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb22 HEBREW LETTER WIDE DALET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb23 HEBREW LETTER WIDE HE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb24 HEBREW LETTER WIDE KAF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb25 HEBREW LETTER WIDE LAMED */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb26 HEBREW LETTER WIDE FINAL MEM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb27 HEBREW LETTER WIDE RESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb28 HEBREW LETTER WIDE TAV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb29 HEBREW LETTER ALTERNATIVE PLUS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb2a HEBREW LETTER SHIN WITH SHIN DOT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb2b HEBREW LETTER SHIN WITH SIN DOT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb2c HEBREW LETTER SHIN WITH DAGESH AND SHIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb2d HEBREW LETTER SHIN WITH DAGESH AND SIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb2e HEBREW LETTER ALEF WITH PATAH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb2f HEBREW LETTER ALEF WITH QAMATS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb30 HEBREW LETTER ALEF WITH MAPIQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb31 HEBREW LETTER BET WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb32 HEBREW LETTER GIMEL WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb33 HEBREW LETTER DALET WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb34 HEBREW LETTER HE WITH MAPIQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb35 HEBREW LETTER VAV WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb36 HEBREW LETTER ZAYIN WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb37 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb38 HEBREW LETTER TET WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb39 HEBREW LETTER YOD WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb3a HEBREW LETTER FINAL KAF WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb3b HEBREW LETTER KAF WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb3c HEBREW LETTER LAMED WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb3d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb3e HEBREW LETTER MEM WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb3f (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb40 HEBREW LETTER NUN WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb41 HEBREW LETTER SAMEKH WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb42 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb43 HEBREW LETTER FINAL PE WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb44 HEBREW LETTER PE WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb45 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb46 HEBREW LETTER TSADI WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb47 HEBREW LETTER QOF WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb48 HEBREW LETTER RESH WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb49 HEBREW LETTER SHIN WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb4a HEBREW LETTER TAV WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb4b HEBREW LETTER VAV WITH HOLAM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb4c HEBREW LETTER BET WITH RAFE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb4d HEBREW LETTER KAF WITH RAFE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb4e HEBREW LETTER PE WITH RAFE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb4f HEBREW LIGATURE ALEF LAMED */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb50 ARABIC LETTER ALEF WASLA ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb51 ARABIC LETTER ALEF WASLA FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb52 ARABIC LETTER BEEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb53 ARABIC LETTER BEEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb54 ARABIC LETTER BEEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb55 ARABIC LETTER BEEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb56 ARABIC LETTER PEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb57 ARABIC LETTER PEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb58 ARABIC LETTER PEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb59 ARABIC LETTER PEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb5a ARABIC LETTER BEHEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb5b ARABIC LETTER BEHEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb5c ARABIC LETTER BEHEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb5d ARABIC LETTER BEHEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb5e ARABIC LETTER TTEHEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb5f ARABIC LETTER TTEHEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb60 ARABIC LETTER TTEHEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb61 ARABIC LETTER TTEHEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb62 ARABIC LETTER TEHEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb63 ARABIC LETTER TEHEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb64 ARABIC LETTER TEHEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb65 ARABIC LETTER TEHEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb66 ARABIC LETTER TTEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb67 ARABIC LETTER TTEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb68 ARABIC LETTER TTEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb69 ARABIC LETTER TTEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb6a ARABIC LETTER VEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb6b ARABIC LETTER VEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb6c ARABIC LETTER VEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb6d ARABIC LETTER VEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb6e ARABIC LETTER PEHEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb6f ARABIC LETTER PEHEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb70 ARABIC LETTER PEHEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb71 ARABIC LETTER PEHEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb72 ARABIC LETTER DYEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb73 ARABIC LETTER DYEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb74 ARABIC LETTER DYEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb75 ARABIC LETTER DYEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb76 ARABIC LETTER NYEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb77 ARABIC LETTER NYEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb78 ARABIC LETTER NYEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb79 ARABIC LETTER NYEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb7a ARABIC LETTER TCHEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb7b ARABIC LETTER TCHEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb7c ARABIC LETTER TCHEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb7d ARABIC LETTER TCHEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb7e ARABIC LETTER TCHEHEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* fb7f ARABIC LETTER TCHEHEH FINAL FORM */ }; static case_table case_pg_1fe[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff00 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff01 FULLWIDTH EXCLAMATION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff02 FULLWIDTH QUOTATION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff03 FULLWIDTH NUMBER SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff04 FULLWIDTH DOLLAR SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff05 FULLWIDTH PERCENT SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff06 FULLWIDTH AMPERSAND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff07 FULLWIDTH APOSTROPHE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff08 FULLWIDTH LEFT PARENTHESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff09 FULLWIDTH RIGHT PARENTHESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff0a FULLWIDTH ASTERISK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff0b FULLWIDTH PLUS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff0c FULLWIDTH COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff0d FULLWIDTH HYPHEN-MINUS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff0e FULLWIDTH FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff0f FULLWIDTH SOLIDUS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff10 FULLWIDTH DIGIT ZERO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff11 FULLWIDTH DIGIT ONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff12 FULLWIDTH DIGIT TWO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff13 FULLWIDTH DIGIT THREE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff14 FULLWIDTH DIGIT FOUR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff15 FULLWIDTH DIGIT FIVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff16 FULLWIDTH DIGIT SIX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff17 FULLWIDTH DIGIT SEVEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff18 FULLWIDTH DIGIT EIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff19 FULLWIDTH DIGIT NINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff1a FULLWIDTH COLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff1b FULLWIDTH SEMICOLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff1c FULLWIDTH LESS-THAN SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff1d FULLWIDTH EQUALS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff1e FULLWIDTH GREATER-THAN SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff1f FULLWIDTH QUESTION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff20 FULLWIDTH COMMERCIAL AT */ { 0x1259, 0x0000, 0x0000, 0x1259 }, /* ff21 FULLWIDTH LATIN CAPITAL LETTER A */ { 0x125b, 0x0000, 0x0000, 0x125b }, /* ff22 FULLWIDTH LATIN CAPITAL LETTER B */ { 0x125d, 0x0000, 0x0000, 0x125d }, /* ff23 FULLWIDTH LATIN CAPITAL LETTER C */ { 0x125f, 0x0000, 0x0000, 0x125f }, /* ff24 FULLWIDTH LATIN CAPITAL LETTER D */ { 0x1261, 0x0000, 0x0000, 0x1261 }, /* ff25 FULLWIDTH LATIN CAPITAL LETTER E */ { 0x1263, 0x0000, 0x0000, 0x1263 }, /* ff26 FULLWIDTH LATIN CAPITAL LETTER F */ { 0x1265, 0x0000, 0x0000, 0x1265 }, /* ff27 FULLWIDTH LATIN CAPITAL LETTER G */ { 0x1267, 0x0000, 0x0000, 0x1267 }, /* ff28 FULLWIDTH LATIN CAPITAL LETTER H */ { 0x1269, 0x0000, 0x0000, 0x1269 }, /* ff29 FULLWIDTH LATIN CAPITAL LETTER I */ { 0x126b, 0x0000, 0x0000, 0x126b }, /* ff2a FULLWIDTH LATIN CAPITAL LETTER J */ { 0x126d, 0x0000, 0x0000, 0x126d }, /* ff2b FULLWIDTH LATIN CAPITAL LETTER K */ { 0x126f, 0x0000, 0x0000, 0x126f }, /* ff2c FULLWIDTH LATIN CAPITAL LETTER L */ { 0x1271, 0x0000, 0x0000, 0x1271 }, /* ff2d FULLWIDTH LATIN CAPITAL LETTER M */ { 0x1273, 0x0000, 0x0000, 0x1273 }, /* ff2e FULLWIDTH LATIN CAPITAL LETTER N */ { 0x1275, 0x0000, 0x0000, 0x1275 }, /* ff2f FULLWIDTH LATIN CAPITAL LETTER O */ { 0x1277, 0x0000, 0x0000, 0x1277 }, /* ff30 FULLWIDTH LATIN CAPITAL LETTER P */ { 0x1279, 0x0000, 0x0000, 0x1279 }, /* ff31 FULLWIDTH LATIN CAPITAL LETTER Q */ { 0x127b, 0x0000, 0x0000, 0x127b }, /* ff32 FULLWIDTH LATIN CAPITAL LETTER R */ { 0x127d, 0x0000, 0x0000, 0x127d }, /* ff33 FULLWIDTH LATIN CAPITAL LETTER S */ { 0x127f, 0x0000, 0x0000, 0x127f }, /* ff34 FULLWIDTH LATIN CAPITAL LETTER T */ { 0x1281, 0x0000, 0x0000, 0x1281 }, /* ff35 FULLWIDTH LATIN CAPITAL LETTER U */ { 0x1283, 0x0000, 0x0000, 0x1283 }, /* ff36 FULLWIDTH LATIN CAPITAL LETTER V */ { 0x1285, 0x0000, 0x0000, 0x1285 }, /* ff37 FULLWIDTH LATIN CAPITAL LETTER W */ { 0x1287, 0x0000, 0x0000, 0x1287 }, /* ff38 FULLWIDTH LATIN CAPITAL LETTER X */ { 0x1289, 0x0000, 0x0000, 0x1289 }, /* ff39 FULLWIDTH LATIN CAPITAL LETTER Y */ { 0x128b, 0x0000, 0x0000, 0x128b }, /* ff3a FULLWIDTH LATIN CAPITAL LETTER Z */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff3b FULLWIDTH LEFT SQUARE BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff3c FULLWIDTH REVERSE SOLIDUS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff3d FULLWIDTH RIGHT SQUARE BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff3e FULLWIDTH CIRCUMFLEX ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff3f FULLWIDTH LOW LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff40 FULLWIDTH GRAVE ACCENT */ { 0x0000, 0x128d, 0x128d, 0x0000 }, /* ff41 FULLWIDTH LATIN SMALL LETTER A */ { 0x0000, 0x128f, 0x128f, 0x0000 }, /* ff42 FULLWIDTH LATIN SMALL LETTER B */ { 0x0000, 0x1291, 0x1291, 0x0000 }, /* ff43 FULLWIDTH LATIN SMALL LETTER C */ { 0x0000, 0x1293, 0x1293, 0x0000 }, /* ff44 FULLWIDTH LATIN SMALL LETTER D */ { 0x0000, 0x1295, 0x1295, 0x0000 }, /* ff45 FULLWIDTH LATIN SMALL LETTER E */ { 0x0000, 0x1297, 0x1297, 0x0000 }, /* ff46 FULLWIDTH LATIN SMALL LETTER F */ { 0x0000, 0x1299, 0x1299, 0x0000 }, /* ff47 FULLWIDTH LATIN SMALL LETTER G */ { 0x0000, 0x129b, 0x129b, 0x0000 }, /* ff48 FULLWIDTH LATIN SMALL LETTER H */ { 0x0000, 0x129d, 0x129d, 0x0000 }, /* ff49 FULLWIDTH LATIN SMALL LETTER I */ { 0x0000, 0x129f, 0x129f, 0x0000 }, /* ff4a FULLWIDTH LATIN SMALL LETTER J */ { 0x0000, 0x12a1, 0x12a1, 0x0000 }, /* ff4b FULLWIDTH LATIN SMALL LETTER K */ { 0x0000, 0x12a3, 0x12a3, 0x0000 }, /* ff4c FULLWIDTH LATIN SMALL LETTER L */ { 0x0000, 0x12a5, 0x12a5, 0x0000 }, /* ff4d FULLWIDTH LATIN SMALL LETTER M */ { 0x0000, 0x12a7, 0x12a7, 0x0000 }, /* ff4e FULLWIDTH LATIN SMALL LETTER N */ { 0x0000, 0x12a9, 0x12a9, 0x0000 }, /* ff4f FULLWIDTH LATIN SMALL LETTER O */ { 0x0000, 0x12ab, 0x12ab, 0x0000 }, /* ff50 FULLWIDTH LATIN SMALL LETTER P */ { 0x0000, 0x12ad, 0x12ad, 0x0000 }, /* ff51 FULLWIDTH LATIN SMALL LETTER Q */ { 0x0000, 0x12af, 0x12af, 0x0000 }, /* ff52 FULLWIDTH LATIN SMALL LETTER R */ { 0x0000, 0x12b1, 0x12b1, 0x0000 }, /* ff53 FULLWIDTH LATIN SMALL LETTER S */ { 0x0000, 0x12b3, 0x12b3, 0x0000 }, /* ff54 FULLWIDTH LATIN SMALL LETTER T */ { 0x0000, 0x12b5, 0x12b5, 0x0000 }, /* ff55 FULLWIDTH LATIN SMALL LETTER U */ { 0x0000, 0x12b7, 0x12b7, 0x0000 }, /* ff56 FULLWIDTH LATIN SMALL LETTER V */ { 0x0000, 0x12b9, 0x12b9, 0x0000 }, /* ff57 FULLWIDTH LATIN SMALL LETTER W */ { 0x0000, 0x12bb, 0x12bb, 0x0000 }, /* ff58 FULLWIDTH LATIN SMALL LETTER X */ { 0x0000, 0x12bd, 0x12bd, 0x0000 }, /* ff59 FULLWIDTH LATIN SMALL LETTER Y */ { 0x0000, 0x12bf, 0x12bf, 0x0000 }, /* ff5a FULLWIDTH LATIN SMALL LETTER Z */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff5b FULLWIDTH LEFT CURLY BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff5c FULLWIDTH VERTICAL LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff5d FULLWIDTH RIGHT CURLY BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff5e FULLWIDTH TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff5f FULLWIDTH LEFT WHITE PARENTHESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff60 FULLWIDTH RIGHT WHITE PARENTHESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff61 HALFWIDTH IDEOGRAPHIC FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff62 HALFWIDTH LEFT CORNER BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff63 HALFWIDTH RIGHT CORNER BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff64 HALFWIDTH IDEOGRAPHIC COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff65 HALFWIDTH KATAKANA MIDDLE DOT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff66 HALFWIDTH KATAKANA LETTER WO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff67 HALFWIDTH KATAKANA LETTER SMALL A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff68 HALFWIDTH KATAKANA LETTER SMALL I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff69 HALFWIDTH KATAKANA LETTER SMALL U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff6a HALFWIDTH KATAKANA LETTER SMALL E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff6b HALFWIDTH KATAKANA LETTER SMALL O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff6c HALFWIDTH KATAKANA LETTER SMALL YA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff6d HALFWIDTH KATAKANA LETTER SMALL YU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff6e HALFWIDTH KATAKANA LETTER SMALL YO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff6f HALFWIDTH KATAKANA LETTER SMALL TU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff70 HALFWIDTH KATAKANA-HIRAGANA PROLONGED S */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff71 HALFWIDTH KATAKANA LETTER A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff72 HALFWIDTH KATAKANA LETTER I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff73 HALFWIDTH KATAKANA LETTER U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff74 HALFWIDTH KATAKANA LETTER E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff75 HALFWIDTH KATAKANA LETTER O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff76 HALFWIDTH KATAKANA LETTER KA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff77 HALFWIDTH KATAKANA LETTER KI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff78 HALFWIDTH KATAKANA LETTER KU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff79 HALFWIDTH KATAKANA LETTER KE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff7a HALFWIDTH KATAKANA LETTER KO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff7b HALFWIDTH KATAKANA LETTER SA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff7c HALFWIDTH KATAKANA LETTER SI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff7d HALFWIDTH KATAKANA LETTER SU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff7e HALFWIDTH KATAKANA LETTER SE */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* ff7f HALFWIDTH KATAKANA LETTER SO */ }; static case_table *case_tab[512] = { case_pg_000, case_pg_001, case_pg_002, case_pg_003, case_pg_004, case_pg_005, case_pg_006, case_pg_007, case_pg_008, case_pg_009, case_pg_00a, case_pg_00b, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, case_pg_021, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, case_pg_03a, 0, case_pg_03c, case_pg_03d, case_pg_03e, case_pg_03f, 0, 0, case_pg_042, case_pg_043, 0, 0, 0, 0, 0, case_pg_049, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, case_pg_058, case_pg_059, case_pg_05a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, case_pg_14c, case_pg_14d, case_pg_14e, case_pg_14f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, case_pg_1f6, 0, 0, 0, 0, 0, 0, 0, case_pg_1fe, 0 }; static wchar_t case_expansion[] = { /* 0000 */ 0x0000, /* 0001 */ 0x0061, 0x0000, /* 0003 */ 0x0062, 0x0000, /* 0005 */ 0x0063, 0x0000, /* 0007 */ 0x0064, 0x0000, /* 0009 */ 0x0065, 0x0000, /* 000b */ 0x0066, 0x0000, /* 000d */ 0x0067, 0x0000, /* 000f */ 0x0068, 0x0000, /* 0011 */ 0x0069, 0x0000, /* 0013 */ 0x006a, 0x0000, /* 0015 */ 0x006b, 0x0000, /* 0017 */ 0x006c, 0x0000, /* 0019 */ 0x006d, 0x0000, /* 001b */ 0x006e, 0x0000, /* 001d */ 0x006f, 0x0000, /* 001f */ 0x0070, 0x0000, /* 0021 */ 0x0071, 0x0000, /* 0023 */ 0x0072, 0x0000, /* 0025 */ 0x0073, 0x0000, /* 0027 */ 0x0074, 0x0000, /* 0029 */ 0x0075, 0x0000, /* 002b */ 0x0076, 0x0000, /* 002d */ 0x0077, 0x0000, /* 002f */ 0x0078, 0x0000, /* 0031 */ 0x0079, 0x0000, /* 0033 */ 0x007a, 0x0000, /* 0035 */ 0x0041, 0x0000, /* 0037 */ 0x0042, 0x0000, /* 0039 */ 0x0043, 0x0000, /* 003b */ 0x0044, 0x0000, /* 003d */ 0x0045, 0x0000, /* 003f */ 0x0046, 0x0000, /* 0041 */ 0x0047, 0x0000, /* 0043 */ 0x0048, 0x0000, /* 0045 */ 0x0049, 0x0000, /* 0047 */ 0x004a, 0x0000, /* 0049 */ 0x004b, 0x0000, /* 004b */ 0x004c, 0x0000, /* 004d */ 0x004d, 0x0000, /* 004f */ 0x004e, 0x0000, /* 0051 */ 0x004f, 0x0000, /* 0053 */ 0x0050, 0x0000, /* 0055 */ 0x0051, 0x0000, /* 0057 */ 0x0052, 0x0000, /* 0059 */ 0x0053, 0x0000, /* 005b */ 0x0054, 0x0000, /* 005d */ 0x0055, 0x0000, /* 005f */ 0x0056, 0x0000, /* 0061 */ 0x0057, 0x0000, /* 0063 */ 0x0058, 0x0000, /* 0065 */ 0x0059, 0x0000, /* 0067 */ 0x005a, 0x0000, /* 0069 */ 0x039c, 0x0000, /* 006b */ 0x03bc, 0x0000, /* 006d */ 0x00e0, 0x0000, /* 006f */ 0x00e1, 0x0000, /* 0071 */ 0x00e2, 0x0000, /* 0073 */ 0x00e3, 0x0000, /* 0075 */ 0x00e4, 0x0000, /* 0077 */ 0x00e5, 0x0000, /* 0079 */ 0x00e6, 0x0000, /* 007b */ 0x00e7, 0x0000, /* 007d */ 0x00e8, 0x0000, /* 007f */ 0x00e9, 0x0000, /* 0081 */ 0x00ea, 0x0000, /* 0083 */ 0x00eb, 0x0000, /* 0085 */ 0x00ec, 0x0000, /* 0087 */ 0x00ed, 0x0000, /* 0089 */ 0x00ee, 0x0000, /* 008b */ 0x00ef, 0x0000, /* 008d */ 0x00f0, 0x0000, /* 008f */ 0x00f1, 0x0000, /* 0091 */ 0x00f2, 0x0000, /* 0093 */ 0x00f3, 0x0000, /* 0095 */ 0x00f4, 0x0000, /* 0097 */ 0x00f5, 0x0000, /* 0099 */ 0x00f6, 0x0000, /* 009b */ 0x00f8, 0x0000, /* 009d */ 0x00f9, 0x0000, /* 009f */ 0x00fa, 0x0000, /* 00a1 */ 0x00fb, 0x0000, /* 00a3 */ 0x00fc, 0x0000, /* 00a5 */ 0x00fd, 0x0000, /* 00a7 */ 0x00fe, 0x0000, /* 00a9 */ 0x0053, 0x0073, 0x0000, /* 00ac */ 0x0053, 0x0053, 0x0000, /* 00af */ 0x0073, 0x0073, 0x0000, /* 00b2 */ 0x00c0, 0x0000, /* 00b4 */ 0x00c1, 0x0000, /* 00b6 */ 0x00c2, 0x0000, /* 00b8 */ 0x00c3, 0x0000, /* 00ba */ 0x00c4, 0x0000, /* 00bc */ 0x00c5, 0x0000, /* 00be */ 0x00c6, 0x0000, /* 00c0 */ 0x00c7, 0x0000, /* 00c2 */ 0x00c8, 0x0000, /* 00c4 */ 0x00c9, 0x0000, /* 00c6 */ 0x00ca, 0x0000, /* 00c8 */ 0x00cb, 0x0000, /* 00ca */ 0x00cc, 0x0000, /* 00cc */ 0x00cd, 0x0000, /* 00ce */ 0x00ce, 0x0000, /* 00d0 */ 0x00cf, 0x0000, /* 00d2 */ 0x00d0, 0x0000, /* 00d4 */ 0x00d1, 0x0000, /* 00d6 */ 0x00d2, 0x0000, /* 00d8 */ 0x00d3, 0x0000, /* 00da */ 0x00d4, 0x0000, /* 00dc */ 0x00d5, 0x0000, /* 00de */ 0x00d6, 0x0000, /* 00e0 */ 0x00d8, 0x0000, /* 00e2 */ 0x00d9, 0x0000, /* 00e4 */ 0x00da, 0x0000, /* 00e6 */ 0x00db, 0x0000, /* 00e8 */ 0x00dc, 0x0000, /* 00ea */ 0x00dd, 0x0000, /* 00ec */ 0x00de, 0x0000, /* 00ee */ 0x0178, 0x0000, /* 00f0 */ 0x0101, 0x0000, /* 00f2 */ 0x0100, 0x0000, /* 00f4 */ 0x0103, 0x0000, /* 00f6 */ 0x0102, 0x0000, /* 00f8 */ 0x0105, 0x0000, /* 00fa */ 0x0104, 0x0000, /* 00fc */ 0x0107, 0x0000, /* 00fe */ 0x0106, 0x0000, /* 0100 */ 0x0109, 0x0000, /* 0102 */ 0x0108, 0x0000, /* 0104 */ 0x010b, 0x0000, /* 0106 */ 0x010a, 0x0000, /* 0108 */ 0x010d, 0x0000, /* 010a */ 0x010c, 0x0000, /* 010c */ 0x010f, 0x0000, /* 010e */ 0x010e, 0x0000, /* 0110 */ 0x0111, 0x0000, /* 0112 */ 0x0110, 0x0000, /* 0114 */ 0x0113, 0x0000, /* 0116 */ 0x0112, 0x0000, /* 0118 */ 0x0115, 0x0000, /* 011a */ 0x0114, 0x0000, /* 011c */ 0x0117, 0x0000, /* 011e */ 0x0116, 0x0000, /* 0120 */ 0x0119, 0x0000, /* 0122 */ 0x0118, 0x0000, /* 0124 */ 0x011b, 0x0000, /* 0126 */ 0x011a, 0x0000, /* 0128 */ 0x011d, 0x0000, /* 012a */ 0x011c, 0x0000, /* 012c */ 0x011f, 0x0000, /* 012e */ 0x011e, 0x0000, /* 0130 */ 0x0121, 0x0000, /* 0132 */ 0x0120, 0x0000, /* 0134 */ 0x0123, 0x0000, /* 0136 */ 0x0122, 0x0000, /* 0138 */ 0x0125, 0x0000, /* 013a */ 0x0124, 0x0000, /* 013c */ 0x0127, 0x0000, /* 013e */ 0x0126, 0x0000, /* 0140 */ 0x0129, 0x0000, /* 0142 */ 0x0128, 0x0000, /* 0144 */ 0x012b, 0x0000, /* 0146 */ 0x012a, 0x0000, /* 0148 */ 0x012d, 0x0000, /* 014a */ 0x012c, 0x0000, /* 014c */ 0x012f, 0x0000, /* 014e */ 0x012e, 0x0000, /* 0150 */ 0x0069, 0x0307, 0x0000, /* 0153 */ 0x0049, 0x0000, /* 0155 */ 0x0133, 0x0000, /* 0157 */ 0x0132, 0x0000, /* 0159 */ 0x0135, 0x0000, /* 015b */ 0x0134, 0x0000, /* 015d */ 0x0137, 0x0000, /* 015f */ 0x0136, 0x0000, /* 0161 */ 0x013a, 0x0000, /* 0163 */ 0x0139, 0x0000, /* 0165 */ 0x013c, 0x0000, /* 0167 */ 0x013b, 0x0000, /* 0169 */ 0x013e, 0x0000, /* 016b */ 0x013d, 0x0000, /* 016d */ 0x0140, 0x0000, /* 016f */ 0x013f, 0x0000, /* 0171 */ 0x0142, 0x0000, /* 0173 */ 0x0141, 0x0000, /* 0175 */ 0x0144, 0x0000, /* 0177 */ 0x0143, 0x0000, /* 0179 */ 0x0146, 0x0000, /* 017b */ 0x0145, 0x0000, /* 017d */ 0x0148, 0x0000, /* 017f */ 0x0147, 0x0000, /* 0181 */ 0x02bc, 0x004e, 0x0000, /* 0184 */ 0x02bc, 0x006e, 0x0000, /* 0187 */ 0x014b, 0x0000, /* 0189 */ 0x014a, 0x0000, /* 018b */ 0x014d, 0x0000, /* 018d */ 0x014c, 0x0000, /* 018f */ 0x014f, 0x0000, /* 0191 */ 0x014e, 0x0000, /* 0193 */ 0x0151, 0x0000, /* 0195 */ 0x0150, 0x0000, /* 0197 */ 0x0153, 0x0000, /* 0199 */ 0x0152, 0x0000, /* 019b */ 0x0155, 0x0000, /* 019d */ 0x0154, 0x0000, /* 019f */ 0x0157, 0x0000, /* 01a1 */ 0x0156, 0x0000, /* 01a3 */ 0x0159, 0x0000, /* 01a5 */ 0x0158, 0x0000, /* 01a7 */ 0x015b, 0x0000, /* 01a9 */ 0x015a, 0x0000, /* 01ab */ 0x015d, 0x0000, /* 01ad */ 0x015c, 0x0000, /* 01af */ 0x015f, 0x0000, /* 01b1 */ 0x015e, 0x0000, /* 01b3 */ 0x0161, 0x0000, /* 01b5 */ 0x0160, 0x0000, /* 01b7 */ 0x0163, 0x0000, /* 01b9 */ 0x0162, 0x0000, /* 01bb */ 0x0165, 0x0000, /* 01bd */ 0x0164, 0x0000, /* 01bf */ 0x0167, 0x0000, /* 01c1 */ 0x0166, 0x0000, /* 01c3 */ 0x0169, 0x0000, /* 01c5 */ 0x0168, 0x0000, /* 01c7 */ 0x016b, 0x0000, /* 01c9 */ 0x016a, 0x0000, /* 01cb */ 0x016d, 0x0000, /* 01cd */ 0x016c, 0x0000, /* 01cf */ 0x016f, 0x0000, /* 01d1 */ 0x016e, 0x0000, /* 01d3 */ 0x0171, 0x0000, /* 01d5 */ 0x0170, 0x0000, /* 01d7 */ 0x0173, 0x0000, /* 01d9 */ 0x0172, 0x0000, /* 01db */ 0x0175, 0x0000, /* 01dd */ 0x0174, 0x0000, /* 01df */ 0x0177, 0x0000, /* 01e1 */ 0x0176, 0x0000, /* 01e3 */ 0x00ff, 0x0000, /* 01e5 */ 0x017a, 0x0000, /* 01e7 */ 0x0179, 0x0000, /* 01e9 */ 0x017c, 0x0000, /* 01eb */ 0x017b, 0x0000, /* 01ed */ 0x017e, 0x0000, /* 01ef */ 0x017d, 0x0000, /* 01f1 */ 0x0053, 0x0000, /* 01f3 */ 0x0073, 0x0000, /* 01f5 */ 0x0243, 0x0000, /* 01f7 */ 0x0253, 0x0000, /* 01f9 */ 0x0183, 0x0000, /* 01fb */ 0x0182, 0x0000, /* 01fd */ 0x0185, 0x0000, /* 01ff */ 0x0184, 0x0000, /* 0201 */ 0x0254, 0x0000, /* 0203 */ 0x0188, 0x0000, /* 0205 */ 0x0187, 0x0000, /* 0207 */ 0x0256, 0x0000, /* 0209 */ 0x0257, 0x0000, /* 020b */ 0x018c, 0x0000, /* 020d */ 0x018b, 0x0000, /* 020f */ 0x01dd, 0x0000, /* 0211 */ 0x0259, 0x0000, /* 0213 */ 0x025b, 0x0000, /* 0215 */ 0x0192, 0x0000, /* 0217 */ 0x0191, 0x0000, /* 0219 */ 0x0260, 0x0000, /* 021b */ 0x0263, 0x0000, /* 021d */ 0x01f6, 0x0000, /* 021f */ 0x0269, 0x0000, /* 0221 */ 0x0268, 0x0000, /* 0223 */ 0x0199, 0x0000, /* 0225 */ 0x0198, 0x0000, /* 0227 */ 0x023d, 0x0000, /* 0229 */ 0x026f, 0x0000, /* 022b */ 0x0272, 0x0000, /* 022d */ 0x0220, 0x0000, /* 022f */ 0x0275, 0x0000, /* 0231 */ 0x01a1, 0x0000, /* 0233 */ 0x01a0, 0x0000, /* 0235 */ 0x01a3, 0x0000, /* 0237 */ 0x01a2, 0x0000, /* 0239 */ 0x01a5, 0x0000, /* 023b */ 0x01a4, 0x0000, /* 023d */ 0x0280, 0x0000, /* 023f */ 0x01a8, 0x0000, /* 0241 */ 0x01a7, 0x0000, /* 0243 */ 0x0283, 0x0000, /* 0245 */ 0x01ad, 0x0000, /* 0247 */ 0x01ac, 0x0000, /* 0249 */ 0x0288, 0x0000, /* 024b */ 0x01b0, 0x0000, /* 024d */ 0x01af, 0x0000, /* 024f */ 0x028a, 0x0000, /* 0251 */ 0x028b, 0x0000, /* 0253 */ 0x01b4, 0x0000, /* 0255 */ 0x01b3, 0x0000, /* 0257 */ 0x01b6, 0x0000, /* 0259 */ 0x01b5, 0x0000, /* 025b */ 0x0292, 0x0000, /* 025d */ 0x01b9, 0x0000, /* 025f */ 0x01b8, 0x0000, /* 0261 */ 0x01bd, 0x0000, /* 0263 */ 0x01bc, 0x0000, /* 0265 */ 0x01f7, 0x0000, /* 0267 */ 0x01c6, 0x0000, /* 0269 */ 0x01c5, 0x0000, /* 026b */ 0x01c6, 0x0000, /* 026d */ 0x01c5, 0x0000, /* 026f */ 0x01c4, 0x0000, /* 0271 */ 0x01c5, 0x0000, /* 0273 */ 0x01c4, 0x0000, /* 0275 */ 0x01c9, 0x0000, /* 0277 */ 0x01c8, 0x0000, /* 0279 */ 0x01c9, 0x0000, /* 027b */ 0x01c8, 0x0000, /* 027d */ 0x01c7, 0x0000, /* 027f */ 0x01c8, 0x0000, /* 0281 */ 0x01c7, 0x0000, /* 0283 */ 0x01cc, 0x0000, /* 0285 */ 0x01cb, 0x0000, /* 0287 */ 0x01cc, 0x0000, /* 0289 */ 0x01cb, 0x0000, /* 028b */ 0x01ca, 0x0000, /* 028d */ 0x01cb, 0x0000, /* 028f */ 0x01ca, 0x0000, /* 0291 */ 0x01ce, 0x0000, /* 0293 */ 0x01cd, 0x0000, /* 0295 */ 0x01d0, 0x0000, /* 0297 */ 0x01cf, 0x0000, /* 0299 */ 0x01d2, 0x0000, /* 029b */ 0x01d1, 0x0000, /* 029d */ 0x01d4, 0x0000, /* 029f */ 0x01d3, 0x0000, /* 02a1 */ 0x01d6, 0x0000, /* 02a3 */ 0x01d5, 0x0000, /* 02a5 */ 0x01d8, 0x0000, /* 02a7 */ 0x01d7, 0x0000, /* 02a9 */ 0x01da, 0x0000, /* 02ab */ 0x01d9, 0x0000, /* 02ad */ 0x01dc, 0x0000, /* 02af */ 0x01db, 0x0000, /* 02b1 */ 0x018e, 0x0000, /* 02b3 */ 0x01df, 0x0000, /* 02b5 */ 0x01de, 0x0000, /* 02b7 */ 0x01e1, 0x0000, /* 02b9 */ 0x01e0, 0x0000, /* 02bb */ 0x01e3, 0x0000, /* 02bd */ 0x01e2, 0x0000, /* 02bf */ 0x01e5, 0x0000, /* 02c1 */ 0x01e4, 0x0000, /* 02c3 */ 0x01e7, 0x0000, /* 02c5 */ 0x01e6, 0x0000, /* 02c7 */ 0x01e9, 0x0000, /* 02c9 */ 0x01e8, 0x0000, /* 02cb */ 0x01eb, 0x0000, /* 02cd */ 0x01ea, 0x0000, /* 02cf */ 0x01ed, 0x0000, /* 02d1 */ 0x01ec, 0x0000, /* 02d3 */ 0x01ef, 0x0000, /* 02d5 */ 0x01ee, 0x0000, /* 02d7 */ 0x004a, 0x030c, 0x0000, /* 02da */ 0x006a, 0x030c, 0x0000, /* 02dd */ 0x01f3, 0x0000, /* 02df */ 0x01f2, 0x0000, /* 02e1 */ 0x01f3, 0x0000, /* 02e3 */ 0x01f2, 0x0000, /* 02e5 */ 0x01f1, 0x0000, /* 02e7 */ 0x01f2, 0x0000, /* 02e9 */ 0x01f1, 0x0000, /* 02eb */ 0x01f5, 0x0000, /* 02ed */ 0x01f4, 0x0000, /* 02ef */ 0x0195, 0x0000, /* 02f1 */ 0x01bf, 0x0000, /* 02f3 */ 0x01f9, 0x0000, /* 02f5 */ 0x01f8, 0x0000, /* 02f7 */ 0x01fb, 0x0000, /* 02f9 */ 0x01fa, 0x0000, /* 02fb */ 0x01fd, 0x0000, /* 02fd */ 0x01fc, 0x0000, /* 02ff */ 0x01ff, 0x0000, /* 0301 */ 0x01fe, 0x0000, /* 0303 */ 0x0201, 0x0000, /* 0305 */ 0x0200, 0x0000, /* 0307 */ 0x0203, 0x0000, /* 0309 */ 0x0202, 0x0000, /* 030b */ 0x0205, 0x0000, /* 030d */ 0x0204, 0x0000, /* 030f */ 0x0207, 0x0000, /* 0311 */ 0x0206, 0x0000, /* 0313 */ 0x0209, 0x0000, /* 0315 */ 0x0208, 0x0000, /* 0317 */ 0x020b, 0x0000, /* 0319 */ 0x020a, 0x0000, /* 031b */ 0x020d, 0x0000, /* 031d */ 0x020c, 0x0000, /* 031f */ 0x020f, 0x0000, /* 0321 */ 0x020e, 0x0000, /* 0323 */ 0x0211, 0x0000, /* 0325 */ 0x0210, 0x0000, /* 0327 */ 0x0213, 0x0000, /* 0329 */ 0x0212, 0x0000, /* 032b */ 0x0215, 0x0000, /* 032d */ 0x0214, 0x0000, /* 032f */ 0x0217, 0x0000, /* 0331 */ 0x0216, 0x0000, /* 0333 */ 0x0219, 0x0000, /* 0335 */ 0x0218, 0x0000, /* 0337 */ 0x021b, 0x0000, /* 0339 */ 0x021a, 0x0000, /* 033b */ 0x021d, 0x0000, /* 033d */ 0x021c, 0x0000, /* 033f */ 0x021f, 0x0000, /* 0341 */ 0x021e, 0x0000, /* 0343 */ 0x019e, 0x0000, /* 0345 */ 0x0223, 0x0000, /* 0347 */ 0x0222, 0x0000, /* 0349 */ 0x0225, 0x0000, /* 034b */ 0x0224, 0x0000, /* 034d */ 0x0227, 0x0000, /* 034f */ 0x0226, 0x0000, /* 0351 */ 0x0229, 0x0000, /* 0353 */ 0x0228, 0x0000, /* 0355 */ 0x022b, 0x0000, /* 0357 */ 0x022a, 0x0000, /* 0359 */ 0x022d, 0x0000, /* 035b */ 0x022c, 0x0000, /* 035d */ 0x022f, 0x0000, /* 035f */ 0x022e, 0x0000, /* 0361 */ 0x0231, 0x0000, /* 0363 */ 0x0230, 0x0000, /* 0365 */ 0x0233, 0x0000, /* 0367 */ 0x0232, 0x0000, /* 0369 */ 0x2c65, 0x0000, /* 036b */ 0x023c, 0x0000, /* 036d */ 0x023b, 0x0000, /* 036f */ 0x019a, 0x0000, /* 0371 */ 0x2c66, 0x0000, /* 0373 */ 0x2c7e, 0x0000, /* 0375 */ 0x2c7f, 0x0000, /* 0377 */ 0x0242, 0x0000, /* 0379 */ 0x0241, 0x0000, /* 037b */ 0x0180, 0x0000, /* 037d */ 0x0289, 0x0000, /* 037f */ 0x028c, 0x0000, /* 0381 */ 0x0247, 0x0000, /* 0383 */ 0x0246, 0x0000, /* 0385 */ 0x0249, 0x0000, /* 0387 */ 0x0248, 0x0000, /* 0389 */ 0x024b, 0x0000, /* 038b */ 0x024a, 0x0000, /* 038d */ 0x024d, 0x0000, /* 038f */ 0x024c, 0x0000, /* 0391 */ 0x024f, 0x0000, /* 0393 */ 0x024e, 0x0000, /* 0395 */ 0x2c6f, 0x0000, /* 0397 */ 0x2c6d, 0x0000, /* 0399 */ 0x2c70, 0x0000, /* 039b */ 0x0181, 0x0000, /* 039d */ 0x0186, 0x0000, /* 039f */ 0x0189, 0x0000, /* 03a1 */ 0x018a, 0x0000, /* 03a3 */ 0x018f, 0x0000, /* 03a5 */ 0x0190, 0x0000, /* 03a7 */ 0x0193, 0x0000, /* 03a9 */ 0x0194, 0x0000, /* 03ab */ 0xa78d, 0x0000, /* 03ad */ 0xa7aa, 0x0000, /* 03af */ 0x0197, 0x0000, /* 03b1 */ 0x0196, 0x0000, /* 03b3 */ 0x2c62, 0x0000, /* 03b5 */ 0x019c, 0x0000, /* 03b7 */ 0x2c6e, 0x0000, /* 03b9 */ 0x019d, 0x0000, /* 03bb */ 0x019f, 0x0000, /* 03bd */ 0x2c64, 0x0000, /* 03bf */ 0x01a6, 0x0000, /* 03c1 */ 0x01a9, 0x0000, /* 03c3 */ 0x01ae, 0x0000, /* 03c5 */ 0x0244, 0x0000, /* 03c7 */ 0x01b1, 0x0000, /* 03c9 */ 0x01b2, 0x0000, /* 03cb */ 0x0245, 0x0000, /* 03cd */ 0x01b7, 0x0000, /* 03cf */ 0x0399, 0x0000, /* 03d1 */ 0x03b9, 0x0000, /* 03d3 */ 0x0371, 0x0000, /* 03d5 */ 0x0370, 0x0000, /* 03d7 */ 0x0373, 0x0000, /* 03d9 */ 0x0372, 0x0000, /* 03db */ 0x0377, 0x0000, /* 03dd */ 0x0376, 0x0000, /* 03df */ 0x03fd, 0x0000, /* 03e1 */ 0x03fe, 0x0000, /* 03e3 */ 0x03ff, 0x0000, /* 03e5 */ 0x03ac, 0x0000, /* 03e7 */ 0x03ad, 0x0000, /* 03e9 */ 0x03ae, 0x0000, /* 03eb */ 0x03af, 0x0000, /* 03ed */ 0x03cc, 0x0000, /* 03ef */ 0x03cd, 0x0000, /* 03f1 */ 0x03ce, 0x0000, /* 03f3 */ 0x0399, 0x0308, 0x0301, 0x0000, /* 03f7 */ 0x03b9, 0x0308, 0x0301, 0x0000, /* 03fb */ 0x03b1, 0x0000, /* 03fd */ 0x03b2, 0x0000, /* 03ff */ 0x03b3, 0x0000, /* 0401 */ 0x03b4, 0x0000, /* 0403 */ 0x03b5, 0x0000, /* 0405 */ 0x03b6, 0x0000, /* 0407 */ 0x03b7, 0x0000, /* 0409 */ 0x03b8, 0x0000, /* 040b */ 0x03b9, 0x0000, /* 040d */ 0x03ba, 0x0000, /* 040f */ 0x03bb, 0x0000, /* 0411 */ 0x03bc, 0x0000, /* 0413 */ 0x03bd, 0x0000, /* 0415 */ 0x03be, 0x0000, /* 0417 */ 0x03bf, 0x0000, /* 0419 */ 0x03c0, 0x0000, /* 041b */ 0x03c1, 0x0000, /* 041d */ 0x03c3, 0x0000, /* 041f */ 0x03c4, 0x0000, /* 0421 */ 0x03c5, 0x0000, /* 0423 */ 0x03c6, 0x0000, /* 0425 */ 0x03c7, 0x0000, /* 0427 */ 0x03c8, 0x0000, /* 0429 */ 0x03c9, 0x0000, /* 042b */ 0x03ca, 0x0000, /* 042d */ 0x03cb, 0x0000, /* 042f */ 0x0386, 0x0000, /* 0431 */ 0x0388, 0x0000, /* 0433 */ 0x0389, 0x0000, /* 0435 */ 0x038a, 0x0000, /* 0437 */ 0x03a5, 0x0308, 0x0301, 0x0000, /* 043b */ 0x03c5, 0x0308, 0x0301, 0x0000, /* 043f */ 0x0391, 0x0000, /* 0441 */ 0x0392, 0x0000, /* 0443 */ 0x0393, 0x0000, /* 0445 */ 0x0394, 0x0000, /* 0447 */ 0x0395, 0x0000, /* 0449 */ 0x0396, 0x0000, /* 044b */ 0x0397, 0x0000, /* 044d */ 0x0398, 0x0000, /* 044f */ 0x0399, 0x0000, /* 0451 */ 0x039a, 0x0000, /* 0453 */ 0x039b, 0x0000, /* 0455 */ 0x039c, 0x0000, /* 0457 */ 0x039d, 0x0000, /* 0459 */ 0x039e, 0x0000, /* 045b */ 0x039f, 0x0000, /* 045d */ 0x03a0, 0x0000, /* 045f */ 0x03a1, 0x0000, /* 0461 */ 0x03a3, 0x0000, /* 0463 */ 0x03c3, 0x0000, /* 0465 */ 0x03a3, 0x0000, /* 0467 */ 0x03a4, 0x0000, /* 0469 */ 0x03a5, 0x0000, /* 046b */ 0x03a6, 0x0000, /* 046d */ 0x03a7, 0x0000, /* 046f */ 0x03a8, 0x0000, /* 0471 */ 0x03a9, 0x0000, /* 0473 */ 0x03aa, 0x0000, /* 0475 */ 0x03ab, 0x0000, /* 0477 */ 0x038c, 0x0000, /* 0479 */ 0x038e, 0x0000, /* 047b */ 0x038f, 0x0000, /* 047d */ 0x03d7, 0x0000, /* 047f */ 0x0392, 0x0000, /* 0481 */ 0x03b2, 0x0000, /* 0483 */ 0x0398, 0x0000, /* 0485 */ 0x03b8, 0x0000, /* 0487 */ 0x03a6, 0x0000, /* 0489 */ 0x03c6, 0x0000, /* 048b */ 0x03a0, 0x0000, /* 048d */ 0x03c0, 0x0000, /* 048f */ 0x03cf, 0x0000, /* 0491 */ 0x03d9, 0x0000, /* 0493 */ 0x03d8, 0x0000, /* 0495 */ 0x03db, 0x0000, /* 0497 */ 0x03da, 0x0000, /* 0499 */ 0x03dd, 0x0000, /* 049b */ 0x03dc, 0x0000, /* 049d */ 0x03df, 0x0000, /* 049f */ 0x03de, 0x0000, /* 04a1 */ 0x03e1, 0x0000, /* 04a3 */ 0x03e0, 0x0000, /* 04a5 */ 0x03e3, 0x0000, /* 04a7 */ 0x03e2, 0x0000, /* 04a9 */ 0x03e5, 0x0000, /* 04ab */ 0x03e4, 0x0000, /* 04ad */ 0x03e7, 0x0000, /* 04af */ 0x03e6, 0x0000, /* 04b1 */ 0x03e9, 0x0000, /* 04b3 */ 0x03e8, 0x0000, /* 04b5 */ 0x03eb, 0x0000, /* 04b7 */ 0x03ea, 0x0000, /* 04b9 */ 0x03ed, 0x0000, /* 04bb */ 0x03ec, 0x0000, /* 04bd */ 0x03ef, 0x0000, /* 04bf */ 0x03ee, 0x0000, /* 04c1 */ 0x039a, 0x0000, /* 04c3 */ 0x03ba, 0x0000, /* 04c5 */ 0x03a1, 0x0000, /* 04c7 */ 0x03c1, 0x0000, /* 04c9 */ 0x03f9, 0x0000, /* 04cb */ 0x03b8, 0x0000, /* 04cd */ 0x0395, 0x0000, /* 04cf */ 0x03b5, 0x0000, /* 04d1 */ 0x03f8, 0x0000, /* 04d3 */ 0x03f7, 0x0000, /* 04d5 */ 0x03f2, 0x0000, /* 04d7 */ 0x03fb, 0x0000, /* 04d9 */ 0x03fa, 0x0000, /* 04db */ 0x037b, 0x0000, /* 04dd */ 0x037c, 0x0000, /* 04df */ 0x037d, 0x0000, /* 04e1 */ 0x0450, 0x0000, /* 04e3 */ 0x0451, 0x0000, /* 04e5 */ 0x0452, 0x0000, /* 04e7 */ 0x0453, 0x0000, /* 04e9 */ 0x0454, 0x0000, /* 04eb */ 0x0455, 0x0000, /* 04ed */ 0x0456, 0x0000, /* 04ef */ 0x0457, 0x0000, /* 04f1 */ 0x0458, 0x0000, /* 04f3 */ 0x0459, 0x0000, /* 04f5 */ 0x045a, 0x0000, /* 04f7 */ 0x045b, 0x0000, /* 04f9 */ 0x045c, 0x0000, /* 04fb */ 0x045d, 0x0000, /* 04fd */ 0x045e, 0x0000, /* 04ff */ 0x045f, 0x0000, /* 0501 */ 0x0430, 0x0000, /* 0503 */ 0x0431, 0x0000, /* 0505 */ 0x0432, 0x0000, /* 0507 */ 0x0433, 0x0000, /* 0509 */ 0x0434, 0x0000, /* 050b */ 0x0435, 0x0000, /* 050d */ 0x0436, 0x0000, /* 050f */ 0x0437, 0x0000, /* 0511 */ 0x0438, 0x0000, /* 0513 */ 0x0439, 0x0000, /* 0515 */ 0x043a, 0x0000, /* 0517 */ 0x043b, 0x0000, /* 0519 */ 0x043c, 0x0000, /* 051b */ 0x043d, 0x0000, /* 051d */ 0x043e, 0x0000, /* 051f */ 0x043f, 0x0000, /* 0521 */ 0x0440, 0x0000, /* 0523 */ 0x0441, 0x0000, /* 0525 */ 0x0442, 0x0000, /* 0527 */ 0x0443, 0x0000, /* 0529 */ 0x0444, 0x0000, /* 052b */ 0x0445, 0x0000, /* 052d */ 0x0446, 0x0000, /* 052f */ 0x0447, 0x0000, /* 0531 */ 0x0448, 0x0000, /* 0533 */ 0x0449, 0x0000, /* 0535 */ 0x044a, 0x0000, /* 0537 */ 0x044b, 0x0000, /* 0539 */ 0x044c, 0x0000, /* 053b */ 0x044d, 0x0000, /* 053d */ 0x044e, 0x0000, /* 053f */ 0x044f, 0x0000, /* 0541 */ 0x0410, 0x0000, /* 0543 */ 0x0411, 0x0000, /* 0545 */ 0x0412, 0x0000, /* 0547 */ 0x0413, 0x0000, /* 0549 */ 0x0414, 0x0000, /* 054b */ 0x0415, 0x0000, /* 054d */ 0x0416, 0x0000, /* 054f */ 0x0417, 0x0000, /* 0551 */ 0x0418, 0x0000, /* 0553 */ 0x0419, 0x0000, /* 0555 */ 0x041a, 0x0000, /* 0557 */ 0x041b, 0x0000, /* 0559 */ 0x041c, 0x0000, /* 055b */ 0x041d, 0x0000, /* 055d */ 0x041e, 0x0000, /* 055f */ 0x041f, 0x0000, /* 0561 */ 0x0420, 0x0000, /* 0563 */ 0x0421, 0x0000, /* 0565 */ 0x0422, 0x0000, /* 0567 */ 0x0423, 0x0000, /* 0569 */ 0x0424, 0x0000, /* 056b */ 0x0425, 0x0000, /* 056d */ 0x0426, 0x0000, /* 056f */ 0x0427, 0x0000, /* 0571 */ 0x0428, 0x0000, /* 0573 */ 0x0429, 0x0000, /* 0575 */ 0x042a, 0x0000, /* 0577 */ 0x042b, 0x0000, /* 0579 */ 0x042c, 0x0000, /* 057b */ 0x042d, 0x0000, /* 057d */ 0x042e, 0x0000, /* 057f */ 0x042f, 0x0000, /* 0581 */ 0x0400, 0x0000, /* 0583 */ 0x0401, 0x0000, /* 0585 */ 0x0402, 0x0000, /* 0587 */ 0x0403, 0x0000, /* 0589 */ 0x0404, 0x0000, /* 058b */ 0x0405, 0x0000, /* 058d */ 0x0406, 0x0000, /* 058f */ 0x0407, 0x0000, /* 0591 */ 0x0408, 0x0000, /* 0593 */ 0x0409, 0x0000, /* 0595 */ 0x040a, 0x0000, /* 0597 */ 0x040b, 0x0000, /* 0599 */ 0x040c, 0x0000, /* 059b */ 0x040d, 0x0000, /* 059d */ 0x040e, 0x0000, /* 059f */ 0x040f, 0x0000, /* 05a1 */ 0x0461, 0x0000, /* 05a3 */ 0x0460, 0x0000, /* 05a5 */ 0x0463, 0x0000, /* 05a7 */ 0x0462, 0x0000, /* 05a9 */ 0x0465, 0x0000, /* 05ab */ 0x0464, 0x0000, /* 05ad */ 0x0467, 0x0000, /* 05af */ 0x0466, 0x0000, /* 05b1 */ 0x0469, 0x0000, /* 05b3 */ 0x0468, 0x0000, /* 05b5 */ 0x046b, 0x0000, /* 05b7 */ 0x046a, 0x0000, /* 05b9 */ 0x046d, 0x0000, /* 05bb */ 0x046c, 0x0000, /* 05bd */ 0x046f, 0x0000, /* 05bf */ 0x046e, 0x0000, /* 05c1 */ 0x0471, 0x0000, /* 05c3 */ 0x0470, 0x0000, /* 05c5 */ 0x0473, 0x0000, /* 05c7 */ 0x0472, 0x0000, /* 05c9 */ 0x0475, 0x0000, /* 05cb */ 0x0474, 0x0000, /* 05cd */ 0x0477, 0x0000, /* 05cf */ 0x0476, 0x0000, /* 05d1 */ 0x0479, 0x0000, /* 05d3 */ 0x0478, 0x0000, /* 05d5 */ 0x047b, 0x0000, /* 05d7 */ 0x047a, 0x0000, /* 05d9 */ 0x047d, 0x0000, /* 05db */ 0x047c, 0x0000, /* 05dd */ 0x047f, 0x0000, /* 05df */ 0x047e, 0x0000, /* 05e1 */ 0x0481, 0x0000, /* 05e3 */ 0x0480, 0x0000, /* 05e5 */ 0x048b, 0x0000, /* 05e7 */ 0x048a, 0x0000, /* 05e9 */ 0x048d, 0x0000, /* 05eb */ 0x048c, 0x0000, /* 05ed */ 0x048f, 0x0000, /* 05ef */ 0x048e, 0x0000, /* 05f1 */ 0x0491, 0x0000, /* 05f3 */ 0x0490, 0x0000, /* 05f5 */ 0x0493, 0x0000, /* 05f7 */ 0x0492, 0x0000, /* 05f9 */ 0x0495, 0x0000, /* 05fb */ 0x0494, 0x0000, /* 05fd */ 0x0497, 0x0000, /* 05ff */ 0x0496, 0x0000, /* 0601 */ 0x0499, 0x0000, /* 0603 */ 0x0498, 0x0000, /* 0605 */ 0x049b, 0x0000, /* 0607 */ 0x049a, 0x0000, /* 0609 */ 0x049d, 0x0000, /* 060b */ 0x049c, 0x0000, /* 060d */ 0x049f, 0x0000, /* 060f */ 0x049e, 0x0000, /* 0611 */ 0x04a1, 0x0000, /* 0613 */ 0x04a0, 0x0000, /* 0615 */ 0x04a3, 0x0000, /* 0617 */ 0x04a2, 0x0000, /* 0619 */ 0x04a5, 0x0000, /* 061b */ 0x04a4, 0x0000, /* 061d */ 0x04a7, 0x0000, /* 061f */ 0x04a6, 0x0000, /* 0621 */ 0x04a9, 0x0000, /* 0623 */ 0x04a8, 0x0000, /* 0625 */ 0x04ab, 0x0000, /* 0627 */ 0x04aa, 0x0000, /* 0629 */ 0x04ad, 0x0000, /* 062b */ 0x04ac, 0x0000, /* 062d */ 0x04af, 0x0000, /* 062f */ 0x04ae, 0x0000, /* 0631 */ 0x04b1, 0x0000, /* 0633 */ 0x04b0, 0x0000, /* 0635 */ 0x04b3, 0x0000, /* 0637 */ 0x04b2, 0x0000, /* 0639 */ 0x04b5, 0x0000, /* 063b */ 0x04b4, 0x0000, /* 063d */ 0x04b7, 0x0000, /* 063f */ 0x04b6, 0x0000, /* 0641 */ 0x04b9, 0x0000, /* 0643 */ 0x04b8, 0x0000, /* 0645 */ 0x04bb, 0x0000, /* 0647 */ 0x04ba, 0x0000, /* 0649 */ 0x04bd, 0x0000, /* 064b */ 0x04bc, 0x0000, /* 064d */ 0x04bf, 0x0000, /* 064f */ 0x04be, 0x0000, /* 0651 */ 0x04cf, 0x0000, /* 0653 */ 0x04c2, 0x0000, /* 0655 */ 0x04c1, 0x0000, /* 0657 */ 0x04c4, 0x0000, /* 0659 */ 0x04c3, 0x0000, /* 065b */ 0x04c6, 0x0000, /* 065d */ 0x04c5, 0x0000, /* 065f */ 0x04c8, 0x0000, /* 0661 */ 0x04c7, 0x0000, /* 0663 */ 0x04ca, 0x0000, /* 0665 */ 0x04c9, 0x0000, /* 0667 */ 0x04cc, 0x0000, /* 0669 */ 0x04cb, 0x0000, /* 066b */ 0x04ce, 0x0000, /* 066d */ 0x04cd, 0x0000, /* 066f */ 0x04c0, 0x0000, /* 0671 */ 0x04d1, 0x0000, /* 0673 */ 0x04d0, 0x0000, /* 0675 */ 0x04d3, 0x0000, /* 0677 */ 0x04d2, 0x0000, /* 0679 */ 0x04d5, 0x0000, /* 067b */ 0x04d4, 0x0000, /* 067d */ 0x04d7, 0x0000, /* 067f */ 0x04d6, 0x0000, /* 0681 */ 0x04d9, 0x0000, /* 0683 */ 0x04d8, 0x0000, /* 0685 */ 0x04db, 0x0000, /* 0687 */ 0x04da, 0x0000, /* 0689 */ 0x04dd, 0x0000, /* 068b */ 0x04dc, 0x0000, /* 068d */ 0x04df, 0x0000, /* 068f */ 0x04de, 0x0000, /* 0691 */ 0x04e1, 0x0000, /* 0693 */ 0x04e0, 0x0000, /* 0695 */ 0x04e3, 0x0000, /* 0697 */ 0x04e2, 0x0000, /* 0699 */ 0x04e5, 0x0000, /* 069b */ 0x04e4, 0x0000, /* 069d */ 0x04e7, 0x0000, /* 069f */ 0x04e6, 0x0000, /* 06a1 */ 0x04e9, 0x0000, /* 06a3 */ 0x04e8, 0x0000, /* 06a5 */ 0x04eb, 0x0000, /* 06a7 */ 0x04ea, 0x0000, /* 06a9 */ 0x04ed, 0x0000, /* 06ab */ 0x04ec, 0x0000, /* 06ad */ 0x04ef, 0x0000, /* 06af */ 0x04ee, 0x0000, /* 06b1 */ 0x04f1, 0x0000, /* 06b3 */ 0x04f0, 0x0000, /* 06b5 */ 0x04f3, 0x0000, /* 06b7 */ 0x04f2, 0x0000, /* 06b9 */ 0x04f5, 0x0000, /* 06bb */ 0x04f4, 0x0000, /* 06bd */ 0x04f7, 0x0000, /* 06bf */ 0x04f6, 0x0000, /* 06c1 */ 0x04f9, 0x0000, /* 06c3 */ 0x04f8, 0x0000, /* 06c5 */ 0x04fb, 0x0000, /* 06c7 */ 0x04fa, 0x0000, /* 06c9 */ 0x04fd, 0x0000, /* 06cb */ 0x04fc, 0x0000, /* 06cd */ 0x04ff, 0x0000, /* 06cf */ 0x04fe, 0x0000, /* 06d1 */ 0x0501, 0x0000, /* 06d3 */ 0x0500, 0x0000, /* 06d5 */ 0x0503, 0x0000, /* 06d7 */ 0x0502, 0x0000, /* 06d9 */ 0x0505, 0x0000, /* 06db */ 0x0504, 0x0000, /* 06dd */ 0x0507, 0x0000, /* 06df */ 0x0506, 0x0000, /* 06e1 */ 0x0509, 0x0000, /* 06e3 */ 0x0508, 0x0000, /* 06e5 */ 0x050b, 0x0000, /* 06e7 */ 0x050a, 0x0000, /* 06e9 */ 0x050d, 0x0000, /* 06eb */ 0x050c, 0x0000, /* 06ed */ 0x050f, 0x0000, /* 06ef */ 0x050e, 0x0000, /* 06f1 */ 0x0511, 0x0000, /* 06f3 */ 0x0510, 0x0000, /* 06f5 */ 0x0513, 0x0000, /* 06f7 */ 0x0512, 0x0000, /* 06f9 */ 0x0515, 0x0000, /* 06fb */ 0x0514, 0x0000, /* 06fd */ 0x0517, 0x0000, /* 06ff */ 0x0516, 0x0000, /* 0701 */ 0x0519, 0x0000, /* 0703 */ 0x0518, 0x0000, /* 0705 */ 0x051b, 0x0000, /* 0707 */ 0x051a, 0x0000, /* 0709 */ 0x051d, 0x0000, /* 070b */ 0x051c, 0x0000, /* 070d */ 0x051f, 0x0000, /* 070f */ 0x051e, 0x0000, /* 0711 */ 0x0521, 0x0000, /* 0713 */ 0x0520, 0x0000, /* 0715 */ 0x0523, 0x0000, /* 0717 */ 0x0522, 0x0000, /* 0719 */ 0x0525, 0x0000, /* 071b */ 0x0524, 0x0000, /* 071d */ 0x0527, 0x0000, /* 071f */ 0x0526, 0x0000, /* 0721 */ 0x0561, 0x0000, /* 0723 */ 0x0562, 0x0000, /* 0725 */ 0x0563, 0x0000, /* 0727 */ 0x0564, 0x0000, /* 0729 */ 0x0565, 0x0000, /* 072b */ 0x0566, 0x0000, /* 072d */ 0x0567, 0x0000, /* 072f */ 0x0568, 0x0000, /* 0731 */ 0x0569, 0x0000, /* 0733 */ 0x056a, 0x0000, /* 0735 */ 0x056b, 0x0000, /* 0737 */ 0x056c, 0x0000, /* 0739 */ 0x056d, 0x0000, /* 073b */ 0x056e, 0x0000, /* 073d */ 0x056f, 0x0000, /* 073f */ 0x0570, 0x0000, /* 0741 */ 0x0571, 0x0000, /* 0743 */ 0x0572, 0x0000, /* 0745 */ 0x0573, 0x0000, /* 0747 */ 0x0574, 0x0000, /* 0749 */ 0x0575, 0x0000, /* 074b */ 0x0576, 0x0000, /* 074d */ 0x0577, 0x0000, /* 074f */ 0x0578, 0x0000, /* 0751 */ 0x0579, 0x0000, /* 0753 */ 0x057a, 0x0000, /* 0755 */ 0x057b, 0x0000, /* 0757 */ 0x057c, 0x0000, /* 0759 */ 0x057d, 0x0000, /* 075b */ 0x057e, 0x0000, /* 075d */ 0x057f, 0x0000, /* 075f */ 0x0580, 0x0000, /* 0761 */ 0x0581, 0x0000, /* 0763 */ 0x0582, 0x0000, /* 0765 */ 0x0583, 0x0000, /* 0767 */ 0x0584, 0x0000, /* 0769 */ 0x0585, 0x0000, /* 076b */ 0x0586, 0x0000, /* 076d */ 0x0531, 0x0000, /* 076f */ 0x0532, 0x0000, /* 0771 */ 0x0533, 0x0000, /* 0773 */ 0x0534, 0x0000, /* 0775 */ 0x0535, 0x0000, /* 0777 */ 0x0536, 0x0000, /* 0779 */ 0x0537, 0x0000, /* 077b */ 0x0538, 0x0000, /* 077d */ 0x0539, 0x0000, /* 077f */ 0x053a, 0x0000, /* 0781 */ 0x053b, 0x0000, /* 0783 */ 0x053c, 0x0000, /* 0785 */ 0x053d, 0x0000, /* 0787 */ 0x053e, 0x0000, /* 0789 */ 0x053f, 0x0000, /* 078b */ 0x0540, 0x0000, /* 078d */ 0x0541, 0x0000, /* 078f */ 0x0542, 0x0000, /* 0791 */ 0x0543, 0x0000, /* 0793 */ 0x0544, 0x0000, /* 0795 */ 0x0545, 0x0000, /* 0797 */ 0x0546, 0x0000, /* 0799 */ 0x0547, 0x0000, /* 079b */ 0x0548, 0x0000, /* 079d */ 0x0549, 0x0000, /* 079f */ 0x054a, 0x0000, /* 07a1 */ 0x054b, 0x0000, /* 07a3 */ 0x054c, 0x0000, /* 07a5 */ 0x054d, 0x0000, /* 07a7 */ 0x054e, 0x0000, /* 07a9 */ 0x054f, 0x0000, /* 07ab */ 0x0550, 0x0000, /* 07ad */ 0x0551, 0x0000, /* 07af */ 0x0552, 0x0000, /* 07b1 */ 0x0553, 0x0000, /* 07b3 */ 0x0554, 0x0000, /* 07b5 */ 0x0555, 0x0000, /* 07b7 */ 0x0556, 0x0000, /* 07b9 */ 0x0535, 0x0582, 0x0000, /* 07bc */ 0x0535, 0x0552, 0x0000, /* 07bf */ 0x0565, 0x0582, 0x0000, /* 07c2 */ 0x2d00, 0x0000, /* 07c4 */ 0x2d01, 0x0000, /* 07c6 */ 0x2d02, 0x0000, /* 07c8 */ 0x2d03, 0x0000, /* 07ca */ 0x2d04, 0x0000, /* 07cc */ 0x2d05, 0x0000, /* 07ce */ 0x2d06, 0x0000, /* 07d0 */ 0x2d07, 0x0000, /* 07d2 */ 0x2d08, 0x0000, /* 07d4 */ 0x2d09, 0x0000, /* 07d6 */ 0x2d0a, 0x0000, /* 07d8 */ 0x2d0b, 0x0000, /* 07da */ 0x2d0c, 0x0000, /* 07dc */ 0x2d0d, 0x0000, /* 07de */ 0x2d0e, 0x0000, /* 07e0 */ 0x2d0f, 0x0000, /* 07e2 */ 0x2d10, 0x0000, /* 07e4 */ 0x2d11, 0x0000, /* 07e6 */ 0x2d12, 0x0000, /* 07e8 */ 0x2d13, 0x0000, /* 07ea */ 0x2d14, 0x0000, /* 07ec */ 0x2d15, 0x0000, /* 07ee */ 0x2d16, 0x0000, /* 07f0 */ 0x2d17, 0x0000, /* 07f2 */ 0x2d18, 0x0000, /* 07f4 */ 0x2d19, 0x0000, /* 07f6 */ 0x2d1a, 0x0000, /* 07f8 */ 0x2d1b, 0x0000, /* 07fa */ 0x2d1c, 0x0000, /* 07fc */ 0x2d1d, 0x0000, /* 07fe */ 0x2d1e, 0x0000, /* 0800 */ 0x2d1f, 0x0000, /* 0802 */ 0x2d20, 0x0000, /* 0804 */ 0x2d21, 0x0000, /* 0806 */ 0x2d22, 0x0000, /* 0808 */ 0x2d23, 0x0000, /* 080a */ 0x2d24, 0x0000, /* 080c */ 0x2d25, 0x0000, /* 080e */ 0x2d27, 0x0000, /* 0810 */ 0x2d2d, 0x0000, /* 0812 */ 0xa77d, 0x0000, /* 0814 */ 0x2c63, 0x0000, /* 0816 */ 0x1e01, 0x0000, /* 0818 */ 0x1e00, 0x0000, /* 081a */ 0x1e03, 0x0000, /* 081c */ 0x1e02, 0x0000, /* 081e */ 0x1e05, 0x0000, /* 0820 */ 0x1e04, 0x0000, /* 0822 */ 0x1e07, 0x0000, /* 0824 */ 0x1e06, 0x0000, /* 0826 */ 0x1e09, 0x0000, /* 0828 */ 0x1e08, 0x0000, /* 082a */ 0x1e0b, 0x0000, /* 082c */ 0x1e0a, 0x0000, /* 082e */ 0x1e0d, 0x0000, /* 0830 */ 0x1e0c, 0x0000, /* 0832 */ 0x1e0f, 0x0000, /* 0834 */ 0x1e0e, 0x0000, /* 0836 */ 0x1e11, 0x0000, /* 0838 */ 0x1e10, 0x0000, /* 083a */ 0x1e13, 0x0000, /* 083c */ 0x1e12, 0x0000, /* 083e */ 0x1e15, 0x0000, /* 0840 */ 0x1e14, 0x0000, /* 0842 */ 0x1e17, 0x0000, /* 0844 */ 0x1e16, 0x0000, /* 0846 */ 0x1e19, 0x0000, /* 0848 */ 0x1e18, 0x0000, /* 084a */ 0x1e1b, 0x0000, /* 084c */ 0x1e1a, 0x0000, /* 084e */ 0x1e1d, 0x0000, /* 0850 */ 0x1e1c, 0x0000, /* 0852 */ 0x1e1f, 0x0000, /* 0854 */ 0x1e1e, 0x0000, /* 0856 */ 0x1e21, 0x0000, /* 0858 */ 0x1e20, 0x0000, /* 085a */ 0x1e23, 0x0000, /* 085c */ 0x1e22, 0x0000, /* 085e */ 0x1e25, 0x0000, /* 0860 */ 0x1e24, 0x0000, /* 0862 */ 0x1e27, 0x0000, /* 0864 */ 0x1e26, 0x0000, /* 0866 */ 0x1e29, 0x0000, /* 0868 */ 0x1e28, 0x0000, /* 086a */ 0x1e2b, 0x0000, /* 086c */ 0x1e2a, 0x0000, /* 086e */ 0x1e2d, 0x0000, /* 0870 */ 0x1e2c, 0x0000, /* 0872 */ 0x1e2f, 0x0000, /* 0874 */ 0x1e2e, 0x0000, /* 0876 */ 0x1e31, 0x0000, /* 0878 */ 0x1e30, 0x0000, /* 087a */ 0x1e33, 0x0000, /* 087c */ 0x1e32, 0x0000, /* 087e */ 0x1e35, 0x0000, /* 0880 */ 0x1e34, 0x0000, /* 0882 */ 0x1e37, 0x0000, /* 0884 */ 0x1e36, 0x0000, /* 0886 */ 0x1e39, 0x0000, /* 0888 */ 0x1e38, 0x0000, /* 088a */ 0x1e3b, 0x0000, /* 088c */ 0x1e3a, 0x0000, /* 088e */ 0x1e3d, 0x0000, /* 0890 */ 0x1e3c, 0x0000, /* 0892 */ 0x1e3f, 0x0000, /* 0894 */ 0x1e3e, 0x0000, /* 0896 */ 0x1e41, 0x0000, /* 0898 */ 0x1e40, 0x0000, /* 089a */ 0x1e43, 0x0000, /* 089c */ 0x1e42, 0x0000, /* 089e */ 0x1e45, 0x0000, /* 08a0 */ 0x1e44, 0x0000, /* 08a2 */ 0x1e47, 0x0000, /* 08a4 */ 0x1e46, 0x0000, /* 08a6 */ 0x1e49, 0x0000, /* 08a8 */ 0x1e48, 0x0000, /* 08aa */ 0x1e4b, 0x0000, /* 08ac */ 0x1e4a, 0x0000, /* 08ae */ 0x1e4d, 0x0000, /* 08b0 */ 0x1e4c, 0x0000, /* 08b2 */ 0x1e4f, 0x0000, /* 08b4 */ 0x1e4e, 0x0000, /* 08b6 */ 0x1e51, 0x0000, /* 08b8 */ 0x1e50, 0x0000, /* 08ba */ 0x1e53, 0x0000, /* 08bc */ 0x1e52, 0x0000, /* 08be */ 0x1e55, 0x0000, /* 08c0 */ 0x1e54, 0x0000, /* 08c2 */ 0x1e57, 0x0000, /* 08c4 */ 0x1e56, 0x0000, /* 08c6 */ 0x1e59, 0x0000, /* 08c8 */ 0x1e58, 0x0000, /* 08ca */ 0x1e5b, 0x0000, /* 08cc */ 0x1e5a, 0x0000, /* 08ce */ 0x1e5d, 0x0000, /* 08d0 */ 0x1e5c, 0x0000, /* 08d2 */ 0x1e5f, 0x0000, /* 08d4 */ 0x1e5e, 0x0000, /* 08d6 */ 0x1e61, 0x0000, /* 08d8 */ 0x1e60, 0x0000, /* 08da */ 0x1e63, 0x0000, /* 08dc */ 0x1e62, 0x0000, /* 08de */ 0x1e65, 0x0000, /* 08e0 */ 0x1e64, 0x0000, /* 08e2 */ 0x1e67, 0x0000, /* 08e4 */ 0x1e66, 0x0000, /* 08e6 */ 0x1e69, 0x0000, /* 08e8 */ 0x1e68, 0x0000, /* 08ea */ 0x1e6b, 0x0000, /* 08ec */ 0x1e6a, 0x0000, /* 08ee */ 0x1e6d, 0x0000, /* 08f0 */ 0x1e6c, 0x0000, /* 08f2 */ 0x1e6f, 0x0000, /* 08f4 */ 0x1e6e, 0x0000, /* 08f6 */ 0x1e71, 0x0000, /* 08f8 */ 0x1e70, 0x0000, /* 08fa */ 0x1e73, 0x0000, /* 08fc */ 0x1e72, 0x0000, /* 08fe */ 0x1e75, 0x0000, /* 0900 */ 0x1e74, 0x0000, /* 0902 */ 0x1e77, 0x0000, /* 0904 */ 0x1e76, 0x0000, /* 0906 */ 0x1e79, 0x0000, /* 0908 */ 0x1e78, 0x0000, /* 090a */ 0x1e7b, 0x0000, /* 090c */ 0x1e7a, 0x0000, /* 090e */ 0x1e7d, 0x0000, /* 0910 */ 0x1e7c, 0x0000, /* 0912 */ 0x1e7f, 0x0000, /* 0914 */ 0x1e7e, 0x0000, /* 0916 */ 0x1e81, 0x0000, /* 0918 */ 0x1e80, 0x0000, /* 091a */ 0x1e83, 0x0000, /* 091c */ 0x1e82, 0x0000, /* 091e */ 0x1e85, 0x0000, /* 0920 */ 0x1e84, 0x0000, /* 0922 */ 0x1e87, 0x0000, /* 0924 */ 0x1e86, 0x0000, /* 0926 */ 0x1e89, 0x0000, /* 0928 */ 0x1e88, 0x0000, /* 092a */ 0x1e8b, 0x0000, /* 092c */ 0x1e8a, 0x0000, /* 092e */ 0x1e8d, 0x0000, /* 0930 */ 0x1e8c, 0x0000, /* 0932 */ 0x1e8f, 0x0000, /* 0934 */ 0x1e8e, 0x0000, /* 0936 */ 0x1e91, 0x0000, /* 0938 */ 0x1e90, 0x0000, /* 093a */ 0x1e93, 0x0000, /* 093c */ 0x1e92, 0x0000, /* 093e */ 0x1e95, 0x0000, /* 0940 */ 0x1e94, 0x0000, /* 0942 */ 0x0048, 0x0331, 0x0000, /* 0945 */ 0x0068, 0x0331, 0x0000, /* 0948 */ 0x0054, 0x0308, 0x0000, /* 094b */ 0x0074, 0x0308, 0x0000, /* 094e */ 0x0057, 0x030a, 0x0000, /* 0951 */ 0x0077, 0x030a, 0x0000, /* 0954 */ 0x0059, 0x030a, 0x0000, /* 0957 */ 0x0079, 0x030a, 0x0000, /* 095a */ 0x0041, 0x02be, 0x0000, /* 095d */ 0x0061, 0x02be, 0x0000, /* 0960 */ 0x1e60, 0x0000, /* 0962 */ 0x1e61, 0x0000, /* 0964 */ 0x00df, 0x0000, /* 0966 */ 0x0073, 0x0073, 0x0000, /* 0969 */ 0x1ea1, 0x0000, /* 096b */ 0x1ea0, 0x0000, /* 096d */ 0x1ea3, 0x0000, /* 096f */ 0x1ea2, 0x0000, /* 0971 */ 0x1ea5, 0x0000, /* 0973 */ 0x1ea4, 0x0000, /* 0975 */ 0x1ea7, 0x0000, /* 0977 */ 0x1ea6, 0x0000, /* 0979 */ 0x1ea9, 0x0000, /* 097b */ 0x1ea8, 0x0000, /* 097d */ 0x1eab, 0x0000, /* 097f */ 0x1eaa, 0x0000, /* 0981 */ 0x1ead, 0x0000, /* 0983 */ 0x1eac, 0x0000, /* 0985 */ 0x1eaf, 0x0000, /* 0987 */ 0x1eae, 0x0000, /* 0989 */ 0x1eb1, 0x0000, /* 098b */ 0x1eb0, 0x0000, /* 098d */ 0x1eb3, 0x0000, /* 098f */ 0x1eb2, 0x0000, /* 0991 */ 0x1eb5, 0x0000, /* 0993 */ 0x1eb4, 0x0000, /* 0995 */ 0x1eb7, 0x0000, /* 0997 */ 0x1eb6, 0x0000, /* 0999 */ 0x1eb9, 0x0000, /* 099b */ 0x1eb8, 0x0000, /* 099d */ 0x1ebb, 0x0000, /* 099f */ 0x1eba, 0x0000, /* 09a1 */ 0x1ebd, 0x0000, /* 09a3 */ 0x1ebc, 0x0000, /* 09a5 */ 0x1ebf, 0x0000, /* 09a7 */ 0x1ebe, 0x0000, /* 09a9 */ 0x1ec1, 0x0000, /* 09ab */ 0x1ec0, 0x0000, /* 09ad */ 0x1ec3, 0x0000, /* 09af */ 0x1ec2, 0x0000, /* 09b1 */ 0x1ec5, 0x0000, /* 09b3 */ 0x1ec4, 0x0000, /* 09b5 */ 0x1ec7, 0x0000, /* 09b7 */ 0x1ec6, 0x0000, /* 09b9 */ 0x1ec9, 0x0000, /* 09bb */ 0x1ec8, 0x0000, /* 09bd */ 0x1ecb, 0x0000, /* 09bf */ 0x1eca, 0x0000, /* 09c1 */ 0x1ecd, 0x0000, /* 09c3 */ 0x1ecc, 0x0000, /* 09c5 */ 0x1ecf, 0x0000, /* 09c7 */ 0x1ece, 0x0000, /* 09c9 */ 0x1ed1, 0x0000, /* 09cb */ 0x1ed0, 0x0000, /* 09cd */ 0x1ed3, 0x0000, /* 09cf */ 0x1ed2, 0x0000, /* 09d1 */ 0x1ed5, 0x0000, /* 09d3 */ 0x1ed4, 0x0000, /* 09d5 */ 0x1ed7, 0x0000, /* 09d7 */ 0x1ed6, 0x0000, /* 09d9 */ 0x1ed9, 0x0000, /* 09db */ 0x1ed8, 0x0000, /* 09dd */ 0x1edb, 0x0000, /* 09df */ 0x1eda, 0x0000, /* 09e1 */ 0x1edd, 0x0000, /* 09e3 */ 0x1edc, 0x0000, /* 09e5 */ 0x1edf, 0x0000, /* 09e7 */ 0x1ede, 0x0000, /* 09e9 */ 0x1ee1, 0x0000, /* 09eb */ 0x1ee0, 0x0000, /* 09ed */ 0x1ee3, 0x0000, /* 09ef */ 0x1ee2, 0x0000, /* 09f1 */ 0x1ee5, 0x0000, /* 09f3 */ 0x1ee4, 0x0000, /* 09f5 */ 0x1ee7, 0x0000, /* 09f7 */ 0x1ee6, 0x0000, /* 09f9 */ 0x1ee9, 0x0000, /* 09fb */ 0x1ee8, 0x0000, /* 09fd */ 0x1eeb, 0x0000, /* 09ff */ 0x1eea, 0x0000, /* 0a01 */ 0x1eed, 0x0000, /* 0a03 */ 0x1eec, 0x0000, /* 0a05 */ 0x1eef, 0x0000, /* 0a07 */ 0x1eee, 0x0000, /* 0a09 */ 0x1ef1, 0x0000, /* 0a0b */ 0x1ef0, 0x0000, /* 0a0d */ 0x1ef3, 0x0000, /* 0a0f */ 0x1ef2, 0x0000, /* 0a11 */ 0x1ef5, 0x0000, /* 0a13 */ 0x1ef4, 0x0000, /* 0a15 */ 0x1ef7, 0x0000, /* 0a17 */ 0x1ef6, 0x0000, /* 0a19 */ 0x1ef9, 0x0000, /* 0a1b */ 0x1ef8, 0x0000, /* 0a1d */ 0x1efb, 0x0000, /* 0a1f */ 0x1efa, 0x0000, /* 0a21 */ 0x1efd, 0x0000, /* 0a23 */ 0x1efc, 0x0000, /* 0a25 */ 0x1eff, 0x0000, /* 0a27 */ 0x1efe, 0x0000, /* 0a29 */ 0x1f08, 0x0000, /* 0a2b */ 0x1f09, 0x0000, /* 0a2d */ 0x1f0a, 0x0000, /* 0a2f */ 0x1f0b, 0x0000, /* 0a31 */ 0x1f0c, 0x0000, /* 0a33 */ 0x1f0d, 0x0000, /* 0a35 */ 0x1f0e, 0x0000, /* 0a37 */ 0x1f0f, 0x0000, /* 0a39 */ 0x1f00, 0x0000, /* 0a3b */ 0x1f01, 0x0000, /* 0a3d */ 0x1f02, 0x0000, /* 0a3f */ 0x1f03, 0x0000, /* 0a41 */ 0x1f04, 0x0000, /* 0a43 */ 0x1f05, 0x0000, /* 0a45 */ 0x1f06, 0x0000, /* 0a47 */ 0x1f07, 0x0000, /* 0a49 */ 0x1f18, 0x0000, /* 0a4b */ 0x1f19, 0x0000, /* 0a4d */ 0x1f1a, 0x0000, /* 0a4f */ 0x1f1b, 0x0000, /* 0a51 */ 0x1f1c, 0x0000, /* 0a53 */ 0x1f1d, 0x0000, /* 0a55 */ 0x1f10, 0x0000, /* 0a57 */ 0x1f11, 0x0000, /* 0a59 */ 0x1f12, 0x0000, /* 0a5b */ 0x1f13, 0x0000, /* 0a5d */ 0x1f14, 0x0000, /* 0a5f */ 0x1f15, 0x0000, /* 0a61 */ 0x1f28, 0x0000, /* 0a63 */ 0x1f29, 0x0000, /* 0a65 */ 0x1f2a, 0x0000, /* 0a67 */ 0x1f2b, 0x0000, /* 0a69 */ 0x1f2c, 0x0000, /* 0a6b */ 0x1f2d, 0x0000, /* 0a6d */ 0x1f2e, 0x0000, /* 0a6f */ 0x1f2f, 0x0000, /* 0a71 */ 0x1f20, 0x0000, /* 0a73 */ 0x1f21, 0x0000, /* 0a75 */ 0x1f22, 0x0000, /* 0a77 */ 0x1f23, 0x0000, /* 0a79 */ 0x1f24, 0x0000, /* 0a7b */ 0x1f25, 0x0000, /* 0a7d */ 0x1f26, 0x0000, /* 0a7f */ 0x1f27, 0x0000, /* 0a81 */ 0x1f38, 0x0000, /* 0a83 */ 0x1f39, 0x0000, /* 0a85 */ 0x1f3a, 0x0000, /* 0a87 */ 0x1f3b, 0x0000, /* 0a89 */ 0x1f3c, 0x0000, /* 0a8b */ 0x1f3d, 0x0000, /* 0a8d */ 0x1f3e, 0x0000, /* 0a8f */ 0x1f3f, 0x0000, /* 0a91 */ 0x1f30, 0x0000, /* 0a93 */ 0x1f31, 0x0000, /* 0a95 */ 0x1f32, 0x0000, /* 0a97 */ 0x1f33, 0x0000, /* 0a99 */ 0x1f34, 0x0000, /* 0a9b */ 0x1f35, 0x0000, /* 0a9d */ 0x1f36, 0x0000, /* 0a9f */ 0x1f37, 0x0000, /* 0aa1 */ 0x1f48, 0x0000, /* 0aa3 */ 0x1f49, 0x0000, /* 0aa5 */ 0x1f4a, 0x0000, /* 0aa7 */ 0x1f4b, 0x0000, /* 0aa9 */ 0x1f4c, 0x0000, /* 0aab */ 0x1f4d, 0x0000, /* 0aad */ 0x1f40, 0x0000, /* 0aaf */ 0x1f41, 0x0000, /* 0ab1 */ 0x1f42, 0x0000, /* 0ab3 */ 0x1f43, 0x0000, /* 0ab5 */ 0x1f44, 0x0000, /* 0ab7 */ 0x1f45, 0x0000, /* 0ab9 */ 0x03a5, 0x0313, 0x0000, /* 0abc */ 0x03c5, 0x0313, 0x0000, /* 0abf */ 0x1f59, 0x0000, /* 0ac1 */ 0x03a5, 0x0313, 0x0300, 0x0000, /* 0ac5 */ 0x03c5, 0x0313, 0x0300, 0x0000, /* 0ac9 */ 0x1f5b, 0x0000, /* 0acb */ 0x03a5, 0x0313, 0x0301, 0x0000, /* 0acf */ 0x03c5, 0x0313, 0x0301, 0x0000, /* 0ad3 */ 0x1f5d, 0x0000, /* 0ad5 */ 0x03a5, 0x0313, 0x0342, 0x0000, /* 0ad9 */ 0x03c5, 0x0313, 0x0342, 0x0000, /* 0add */ 0x1f5f, 0x0000, /* 0adf */ 0x1f51, 0x0000, /* 0ae1 */ 0x1f53, 0x0000, /* 0ae3 */ 0x1f55, 0x0000, /* 0ae5 */ 0x1f57, 0x0000, /* 0ae7 */ 0x1f68, 0x0000, /* 0ae9 */ 0x1f69, 0x0000, /* 0aeb */ 0x1f6a, 0x0000, /* 0aed */ 0x1f6b, 0x0000, /* 0aef */ 0x1f6c, 0x0000, /* 0af1 */ 0x1f6d, 0x0000, /* 0af3 */ 0x1f6e, 0x0000, /* 0af5 */ 0x1f6f, 0x0000, /* 0af7 */ 0x1f60, 0x0000, /* 0af9 */ 0x1f61, 0x0000, /* 0afb */ 0x1f62, 0x0000, /* 0afd */ 0x1f63, 0x0000, /* 0aff */ 0x1f64, 0x0000, /* 0b01 */ 0x1f65, 0x0000, /* 0b03 */ 0x1f66, 0x0000, /* 0b05 */ 0x1f67, 0x0000, /* 0b07 */ 0x1fba, 0x0000, /* 0b09 */ 0x1fbb, 0x0000, /* 0b0b */ 0x1fc8, 0x0000, /* 0b0d */ 0x1fc9, 0x0000, /* 0b0f */ 0x1fca, 0x0000, /* 0b11 */ 0x1fcb, 0x0000, /* 0b13 */ 0x1fda, 0x0000, /* 0b15 */ 0x1fdb, 0x0000, /* 0b17 */ 0x1ff8, 0x0000, /* 0b19 */ 0x1ff9, 0x0000, /* 0b1b */ 0x1fea, 0x0000, /* 0b1d */ 0x1feb, 0x0000, /* 0b1f */ 0x1ffa, 0x0000, /* 0b21 */ 0x1ffb, 0x0000, /* 0b23 */ 0x1f88, 0x0000, /* 0b25 */ 0x1f08, 0x0399, 0x0000, /* 0b28 */ 0x1f00, 0x03b9, 0x0000, /* 0b2b */ 0x1f89, 0x0000, /* 0b2d */ 0x1f09, 0x0399, 0x0000, /* 0b30 */ 0x1f01, 0x03b9, 0x0000, /* 0b33 */ 0x1f8a, 0x0000, /* 0b35 */ 0x1f0a, 0x0399, 0x0000, /* 0b38 */ 0x1f02, 0x03b9, 0x0000, /* 0b3b */ 0x1f8b, 0x0000, /* 0b3d */ 0x1f0b, 0x0399, 0x0000, /* 0b40 */ 0x1f03, 0x03b9, 0x0000, /* 0b43 */ 0x1f8c, 0x0000, /* 0b45 */ 0x1f0c, 0x0399, 0x0000, /* 0b48 */ 0x1f04, 0x03b9, 0x0000, /* 0b4b */ 0x1f8d, 0x0000, /* 0b4d */ 0x1f0d, 0x0399, 0x0000, /* 0b50 */ 0x1f05, 0x03b9, 0x0000, /* 0b53 */ 0x1f8e, 0x0000, /* 0b55 */ 0x1f0e, 0x0399, 0x0000, /* 0b58 */ 0x1f06, 0x03b9, 0x0000, /* 0b5b */ 0x1f8f, 0x0000, /* 0b5d */ 0x1f0f, 0x0399, 0x0000, /* 0b60 */ 0x1f07, 0x03b9, 0x0000, /* 0b63 */ 0x1f80, 0x0000, /* 0b65 */ 0x1f08, 0x0399, 0x0000, /* 0b68 */ 0x1f00, 0x03b9, 0x0000, /* 0b6b */ 0x1f81, 0x0000, /* 0b6d */ 0x1f09, 0x0399, 0x0000, /* 0b70 */ 0x1f01, 0x03b9, 0x0000, /* 0b73 */ 0x1f82, 0x0000, /* 0b75 */ 0x1f0a, 0x0399, 0x0000, /* 0b78 */ 0x1f02, 0x03b9, 0x0000, /* 0b7b */ 0x1f83, 0x0000, /* 0b7d */ 0x1f0b, 0x0399, 0x0000, /* 0b80 */ 0x1f03, 0x03b9, 0x0000, /* 0b83 */ 0x1f84, 0x0000, /* 0b85 */ 0x1f0c, 0x0399, 0x0000, /* 0b88 */ 0x1f04, 0x03b9, 0x0000, /* 0b8b */ 0x1f85, 0x0000, /* 0b8d */ 0x1f0d, 0x0399, 0x0000, /* 0b90 */ 0x1f05, 0x03b9, 0x0000, /* 0b93 */ 0x1f86, 0x0000, /* 0b95 */ 0x1f0e, 0x0399, 0x0000, /* 0b98 */ 0x1f06, 0x03b9, 0x0000, /* 0b9b */ 0x1f87, 0x0000, /* 0b9d */ 0x1f0f, 0x0399, 0x0000, /* 0ba0 */ 0x1f07, 0x03b9, 0x0000, /* 0ba3 */ 0x1f98, 0x0000, /* 0ba5 */ 0x1f28, 0x0399, 0x0000, /* 0ba8 */ 0x1f20, 0x03b9, 0x0000, /* 0bab */ 0x1f99, 0x0000, /* 0bad */ 0x1f29, 0x0399, 0x0000, /* 0bb0 */ 0x1f21, 0x03b9, 0x0000, /* 0bb3 */ 0x1f9a, 0x0000, /* 0bb5 */ 0x1f2a, 0x0399, 0x0000, /* 0bb8 */ 0x1f22, 0x03b9, 0x0000, /* 0bbb */ 0x1f9b, 0x0000, /* 0bbd */ 0x1f2b, 0x0399, 0x0000, /* 0bc0 */ 0x1f23, 0x03b9, 0x0000, /* 0bc3 */ 0x1f9c, 0x0000, /* 0bc5 */ 0x1f2c, 0x0399, 0x0000, /* 0bc8 */ 0x1f24, 0x03b9, 0x0000, /* 0bcb */ 0x1f9d, 0x0000, /* 0bcd */ 0x1f2d, 0x0399, 0x0000, /* 0bd0 */ 0x1f25, 0x03b9, 0x0000, /* 0bd3 */ 0x1f9e, 0x0000, /* 0bd5 */ 0x1f2e, 0x0399, 0x0000, /* 0bd8 */ 0x1f26, 0x03b9, 0x0000, /* 0bdb */ 0x1f9f, 0x0000, /* 0bdd */ 0x1f2f, 0x0399, 0x0000, /* 0be0 */ 0x1f27, 0x03b9, 0x0000, /* 0be3 */ 0x1f90, 0x0000, /* 0be5 */ 0x1f28, 0x0399, 0x0000, /* 0be8 */ 0x1f20, 0x03b9, 0x0000, /* 0beb */ 0x1f91, 0x0000, /* 0bed */ 0x1f29, 0x0399, 0x0000, /* 0bf0 */ 0x1f21, 0x03b9, 0x0000, /* 0bf3 */ 0x1f92, 0x0000, /* 0bf5 */ 0x1f2a, 0x0399, 0x0000, /* 0bf8 */ 0x1f22, 0x03b9, 0x0000, /* 0bfb */ 0x1f93, 0x0000, /* 0bfd */ 0x1f2b, 0x0399, 0x0000, /* 0c00 */ 0x1f23, 0x03b9, 0x0000, /* 0c03 */ 0x1f94, 0x0000, /* 0c05 */ 0x1f2c, 0x0399, 0x0000, /* 0c08 */ 0x1f24, 0x03b9, 0x0000, /* 0c0b */ 0x1f95, 0x0000, /* 0c0d */ 0x1f2d, 0x0399, 0x0000, /* 0c10 */ 0x1f25, 0x03b9, 0x0000, /* 0c13 */ 0x1f96, 0x0000, /* 0c15 */ 0x1f2e, 0x0399, 0x0000, /* 0c18 */ 0x1f26, 0x03b9, 0x0000, /* 0c1b */ 0x1f97, 0x0000, /* 0c1d */ 0x1f2f, 0x0399, 0x0000, /* 0c20 */ 0x1f27, 0x03b9, 0x0000, /* 0c23 */ 0x1fa8, 0x0000, /* 0c25 */ 0x1f68, 0x0399, 0x0000, /* 0c28 */ 0x1f60, 0x03b9, 0x0000, /* 0c2b */ 0x1fa9, 0x0000, /* 0c2d */ 0x1f69, 0x0399, 0x0000, /* 0c30 */ 0x1f61, 0x03b9, 0x0000, /* 0c33 */ 0x1faa, 0x0000, /* 0c35 */ 0x1f6a, 0x0399, 0x0000, /* 0c38 */ 0x1f62, 0x03b9, 0x0000, /* 0c3b */ 0x1fab, 0x0000, /* 0c3d */ 0x1f6b, 0x0399, 0x0000, /* 0c40 */ 0x1f63, 0x03b9, 0x0000, /* 0c43 */ 0x1fac, 0x0000, /* 0c45 */ 0x1f6c, 0x0399, 0x0000, /* 0c48 */ 0x1f64, 0x03b9, 0x0000, /* 0c4b */ 0x1fad, 0x0000, /* 0c4d */ 0x1f6d, 0x0399, 0x0000, /* 0c50 */ 0x1f65, 0x03b9, 0x0000, /* 0c53 */ 0x1fae, 0x0000, /* 0c55 */ 0x1f6e, 0x0399, 0x0000, /* 0c58 */ 0x1f66, 0x03b9, 0x0000, /* 0c5b */ 0x1faf, 0x0000, /* 0c5d */ 0x1f6f, 0x0399, 0x0000, /* 0c60 */ 0x1f67, 0x03b9, 0x0000, /* 0c63 */ 0x1fa0, 0x0000, /* 0c65 */ 0x1f68, 0x0399, 0x0000, /* 0c68 */ 0x1f60, 0x03b9, 0x0000, /* 0c6b */ 0x1fa1, 0x0000, /* 0c6d */ 0x1f69, 0x0399, 0x0000, /* 0c70 */ 0x1f61, 0x03b9, 0x0000, /* 0c73 */ 0x1fa2, 0x0000, /* 0c75 */ 0x1f6a, 0x0399, 0x0000, /* 0c78 */ 0x1f62, 0x03b9, 0x0000, /* 0c7b */ 0x1fa3, 0x0000, /* 0c7d */ 0x1f6b, 0x0399, 0x0000, /* 0c80 */ 0x1f63, 0x03b9, 0x0000, /* 0c83 */ 0x1fa4, 0x0000, /* 0c85 */ 0x1f6c, 0x0399, 0x0000, /* 0c88 */ 0x1f64, 0x03b9, 0x0000, /* 0c8b */ 0x1fa5, 0x0000, /* 0c8d */ 0x1f6d, 0x0399, 0x0000, /* 0c90 */ 0x1f65, 0x03b9, 0x0000, /* 0c93 */ 0x1fa6, 0x0000, /* 0c95 */ 0x1f6e, 0x0399, 0x0000, /* 0c98 */ 0x1f66, 0x03b9, 0x0000, /* 0c9b */ 0x1fa7, 0x0000, /* 0c9d */ 0x1f6f, 0x0399, 0x0000, /* 0ca0 */ 0x1f67, 0x03b9, 0x0000, /* 0ca3 */ 0x1fb8, 0x0000, /* 0ca5 */ 0x1fb9, 0x0000, /* 0ca7 */ 0x1fba, 0x0345, 0x0000, /* 0caa */ 0x1fba, 0x0399, 0x0000, /* 0cad */ 0x1f70, 0x03b9, 0x0000, /* 0cb0 */ 0x1fbc, 0x0000, /* 0cb2 */ 0x0391, 0x0399, 0x0000, /* 0cb5 */ 0x03b1, 0x03b9, 0x0000, /* 0cb8 */ 0x0386, 0x0345, 0x0000, /* 0cbb */ 0x0386, 0x0399, 0x0000, /* 0cbe */ 0x03ac, 0x03b9, 0x0000, /* 0cc1 */ 0x0391, 0x0342, 0x0000, /* 0cc4 */ 0x03b1, 0x0342, 0x0000, /* 0cc7 */ 0x0391, 0x0342, 0x0345, 0x0000, /* 0ccb */ 0x0391, 0x0342, 0x0399, 0x0000, /* 0ccf */ 0x03b1, 0x0342, 0x03b9, 0x0000, /* 0cd3 */ 0x1fb0, 0x0000, /* 0cd5 */ 0x1fb1, 0x0000, /* 0cd7 */ 0x1f70, 0x0000, /* 0cd9 */ 0x1f71, 0x0000, /* 0cdb */ 0x1fb3, 0x0000, /* 0cdd */ 0x0391, 0x0399, 0x0000, /* 0ce0 */ 0x03b1, 0x03b9, 0x0000, /* 0ce3 */ 0x0399, 0x0000, /* 0ce5 */ 0x03b9, 0x0000, /* 0ce7 */ 0x1fca, 0x0345, 0x0000, /* 0cea */ 0x1fca, 0x0399, 0x0000, /* 0ced */ 0x1f74, 0x03b9, 0x0000, /* 0cf0 */ 0x1fcc, 0x0000, /* 0cf2 */ 0x0397, 0x0399, 0x0000, /* 0cf5 */ 0x03b7, 0x03b9, 0x0000, /* 0cf8 */ 0x0389, 0x0345, 0x0000, /* 0cfb */ 0x0389, 0x0399, 0x0000, /* 0cfe */ 0x03ae, 0x03b9, 0x0000, /* 0d01 */ 0x0397, 0x0342, 0x0000, /* 0d04 */ 0x03b7, 0x0342, 0x0000, /* 0d07 */ 0x0397, 0x0342, 0x0345, 0x0000, /* 0d0b */ 0x0397, 0x0342, 0x0399, 0x0000, /* 0d0f */ 0x03b7, 0x0342, 0x03b9, 0x0000, /* 0d13 */ 0x1f72, 0x0000, /* 0d15 */ 0x1f73, 0x0000, /* 0d17 */ 0x1f74, 0x0000, /* 0d19 */ 0x1f75, 0x0000, /* 0d1b */ 0x1fc3, 0x0000, /* 0d1d */ 0x0397, 0x0399, 0x0000, /* 0d20 */ 0x03b7, 0x03b9, 0x0000, /* 0d23 */ 0x1fd8, 0x0000, /* 0d25 */ 0x1fd9, 0x0000, /* 0d27 */ 0x0399, 0x0308, 0x0300, 0x0000, /* 0d2b */ 0x03b9, 0x0308, 0x0300, 0x0000, /* 0d2f */ 0x0399, 0x0308, 0x0301, 0x0000, /* 0d33 */ 0x03b9, 0x0308, 0x0301, 0x0000, /* 0d37 */ 0x0399, 0x0342, 0x0000, /* 0d3a */ 0x03b9, 0x0342, 0x0000, /* 0d3d */ 0x0399, 0x0308, 0x0342, 0x0000, /* 0d41 */ 0x03b9, 0x0308, 0x0342, 0x0000, /* 0d45 */ 0x1fd0, 0x0000, /* 0d47 */ 0x1fd1, 0x0000, /* 0d49 */ 0x1f76, 0x0000, /* 0d4b */ 0x1f77, 0x0000, /* 0d4d */ 0x1fe8, 0x0000, /* 0d4f */ 0x1fe9, 0x0000, /* 0d51 */ 0x03a5, 0x0308, 0x0300, 0x0000, /* 0d55 */ 0x03c5, 0x0308, 0x0300, 0x0000, /* 0d59 */ 0x03a5, 0x0308, 0x0301, 0x0000, /* 0d5d */ 0x03c5, 0x0308, 0x0301, 0x0000, /* 0d61 */ 0x03a1, 0x0313, 0x0000, /* 0d64 */ 0x03c1, 0x0313, 0x0000, /* 0d67 */ 0x1fec, 0x0000, /* 0d69 */ 0x03a5, 0x0342, 0x0000, /* 0d6c */ 0x03c5, 0x0342, 0x0000, /* 0d6f */ 0x03a5, 0x0308, 0x0342, 0x0000, /* 0d73 */ 0x03c5, 0x0308, 0x0342, 0x0000, /* 0d77 */ 0x1fe0, 0x0000, /* 0d79 */ 0x1fe1, 0x0000, /* 0d7b */ 0x1f7a, 0x0000, /* 0d7d */ 0x1f7b, 0x0000, /* 0d7f */ 0x1fe5, 0x0000, /* 0d81 */ 0x1ffa, 0x0345, 0x0000, /* 0d84 */ 0x1ffa, 0x0399, 0x0000, /* 0d87 */ 0x1f7c, 0x03b9, 0x0000, /* 0d8a */ 0x1ffc, 0x0000, /* 0d8c */ 0x03a9, 0x0399, 0x0000, /* 0d8f */ 0x03c9, 0x03b9, 0x0000, /* 0d92 */ 0x038f, 0x0345, 0x0000, /* 0d95 */ 0x038f, 0x0399, 0x0000, /* 0d98 */ 0x03ce, 0x03b9, 0x0000, /* 0d9b */ 0x03a9, 0x0342, 0x0000, /* 0d9e */ 0x03c9, 0x0342, 0x0000, /* 0da1 */ 0x03a9, 0x0342, 0x0345, 0x0000, /* 0da5 */ 0x03a9, 0x0342, 0x0399, 0x0000, /* 0da9 */ 0x03c9, 0x0342, 0x03b9, 0x0000, /* 0dad */ 0x1f78, 0x0000, /* 0daf */ 0x1f79, 0x0000, /* 0db1 */ 0x1f7c, 0x0000, /* 0db3 */ 0x1f7d, 0x0000, /* 0db5 */ 0x1ff3, 0x0000, /* 0db7 */ 0x03a9, 0x0399, 0x0000, /* 0dba */ 0x03c9, 0x03b9, 0x0000, /* 0dbd */ 0x03c9, 0x0000, /* 0dbf */ 0x006b, 0x0000, /* 0dc1 */ 0x00e5, 0x0000, /* 0dc3 */ 0x214e, 0x0000, /* 0dc5 */ 0x2132, 0x0000, /* 0dc7 */ 0x2170, 0x0000, /* 0dc9 */ 0x2171, 0x0000, /* 0dcb */ 0x2172, 0x0000, /* 0dcd */ 0x2173, 0x0000, /* 0dcf */ 0x2174, 0x0000, /* 0dd1 */ 0x2175, 0x0000, /* 0dd3 */ 0x2176, 0x0000, /* 0dd5 */ 0x2177, 0x0000, /* 0dd7 */ 0x2178, 0x0000, /* 0dd9 */ 0x2179, 0x0000, /* 0ddb */ 0x217a, 0x0000, /* 0ddd */ 0x217b, 0x0000, /* 0ddf */ 0x217c, 0x0000, /* 0de1 */ 0x217d, 0x0000, /* 0de3 */ 0x217e, 0x0000, /* 0de5 */ 0x217f, 0x0000, /* 0de7 */ 0x2160, 0x0000, /* 0de9 */ 0x2161, 0x0000, /* 0deb */ 0x2162, 0x0000, /* 0ded */ 0x2163, 0x0000, /* 0def */ 0x2164, 0x0000, /* 0df1 */ 0x2165, 0x0000, /* 0df3 */ 0x2166, 0x0000, /* 0df5 */ 0x2167, 0x0000, /* 0df7 */ 0x2168, 0x0000, /* 0df9 */ 0x2169, 0x0000, /* 0dfb */ 0x216a, 0x0000, /* 0dfd */ 0x216b, 0x0000, /* 0dff */ 0x216c, 0x0000, /* 0e01 */ 0x216d, 0x0000, /* 0e03 */ 0x216e, 0x0000, /* 0e05 */ 0x216f, 0x0000, /* 0e07 */ 0x2184, 0x0000, /* 0e09 */ 0x2183, 0x0000, /* 0e0b */ 0x24d0, 0x0000, /* 0e0d */ 0x24d1, 0x0000, /* 0e0f */ 0x24d2, 0x0000, /* 0e11 */ 0x24d3, 0x0000, /* 0e13 */ 0x24d4, 0x0000, /* 0e15 */ 0x24d5, 0x0000, /* 0e17 */ 0x24d6, 0x0000, /* 0e19 */ 0x24d7, 0x0000, /* 0e1b */ 0x24d8, 0x0000, /* 0e1d */ 0x24d9, 0x0000, /* 0e1f */ 0x24da, 0x0000, /* 0e21 */ 0x24db, 0x0000, /* 0e23 */ 0x24dc, 0x0000, /* 0e25 */ 0x24dd, 0x0000, /* 0e27 */ 0x24de, 0x0000, /* 0e29 */ 0x24df, 0x0000, /* 0e2b */ 0x24e0, 0x0000, /* 0e2d */ 0x24e1, 0x0000, /* 0e2f */ 0x24e2, 0x0000, /* 0e31 */ 0x24e3, 0x0000, /* 0e33 */ 0x24e4, 0x0000, /* 0e35 */ 0x24e5, 0x0000, /* 0e37 */ 0x24e6, 0x0000, /* 0e39 */ 0x24e7, 0x0000, /* 0e3b */ 0x24e8, 0x0000, /* 0e3d */ 0x24e9, 0x0000, /* 0e3f */ 0x24b6, 0x0000, /* 0e41 */ 0x24b7, 0x0000, /* 0e43 */ 0x24b8, 0x0000, /* 0e45 */ 0x24b9, 0x0000, /* 0e47 */ 0x24ba, 0x0000, /* 0e49 */ 0x24bb, 0x0000, /* 0e4b */ 0x24bc, 0x0000, /* 0e4d */ 0x24bd, 0x0000, /* 0e4f */ 0x24be, 0x0000, /* 0e51 */ 0x24bf, 0x0000, /* 0e53 */ 0x24c0, 0x0000, /* 0e55 */ 0x24c1, 0x0000, /* 0e57 */ 0x24c2, 0x0000, /* 0e59 */ 0x24c3, 0x0000, /* 0e5b */ 0x24c4, 0x0000, /* 0e5d */ 0x24c5, 0x0000, /* 0e5f */ 0x24c6, 0x0000, /* 0e61 */ 0x24c7, 0x0000, /* 0e63 */ 0x24c8, 0x0000, /* 0e65 */ 0x24c9, 0x0000, /* 0e67 */ 0x24ca, 0x0000, /* 0e69 */ 0x24cb, 0x0000, /* 0e6b */ 0x24cc, 0x0000, /* 0e6d */ 0x24cd, 0x0000, /* 0e6f */ 0x24ce, 0x0000, /* 0e71 */ 0x24cf, 0x0000, /* 0e73 */ 0x2c30, 0x0000, /* 0e75 */ 0x2c31, 0x0000, /* 0e77 */ 0x2c32, 0x0000, /* 0e79 */ 0x2c33, 0x0000, /* 0e7b */ 0x2c34, 0x0000, /* 0e7d */ 0x2c35, 0x0000, /* 0e7f */ 0x2c36, 0x0000, /* 0e81 */ 0x2c37, 0x0000, /* 0e83 */ 0x2c38, 0x0000, /* 0e85 */ 0x2c39, 0x0000, /* 0e87 */ 0x2c3a, 0x0000, /* 0e89 */ 0x2c3b, 0x0000, /* 0e8b */ 0x2c3c, 0x0000, /* 0e8d */ 0x2c3d, 0x0000, /* 0e8f */ 0x2c3e, 0x0000, /* 0e91 */ 0x2c3f, 0x0000, /* 0e93 */ 0x2c40, 0x0000, /* 0e95 */ 0x2c41, 0x0000, /* 0e97 */ 0x2c42, 0x0000, /* 0e99 */ 0x2c43, 0x0000, /* 0e9b */ 0x2c44, 0x0000, /* 0e9d */ 0x2c45, 0x0000, /* 0e9f */ 0x2c46, 0x0000, /* 0ea1 */ 0x2c47, 0x0000, /* 0ea3 */ 0x2c48, 0x0000, /* 0ea5 */ 0x2c49, 0x0000, /* 0ea7 */ 0x2c4a, 0x0000, /* 0ea9 */ 0x2c4b, 0x0000, /* 0eab */ 0x2c4c, 0x0000, /* 0ead */ 0x2c4d, 0x0000, /* 0eaf */ 0x2c4e, 0x0000, /* 0eb1 */ 0x2c4f, 0x0000, /* 0eb3 */ 0x2c50, 0x0000, /* 0eb5 */ 0x2c51, 0x0000, /* 0eb7 */ 0x2c52, 0x0000, /* 0eb9 */ 0x2c53, 0x0000, /* 0ebb */ 0x2c54, 0x0000, /* 0ebd */ 0x2c55, 0x0000, /* 0ebf */ 0x2c56, 0x0000, /* 0ec1 */ 0x2c57, 0x0000, /* 0ec3 */ 0x2c58, 0x0000, /* 0ec5 */ 0x2c59, 0x0000, /* 0ec7 */ 0x2c5a, 0x0000, /* 0ec9 */ 0x2c5b, 0x0000, /* 0ecb */ 0x2c5c, 0x0000, /* 0ecd */ 0x2c5d, 0x0000, /* 0ecf */ 0x2c5e, 0x0000, /* 0ed1 */ 0x2c00, 0x0000, /* 0ed3 */ 0x2c01, 0x0000, /* 0ed5 */ 0x2c02, 0x0000, /* 0ed7 */ 0x2c03, 0x0000, /* 0ed9 */ 0x2c04, 0x0000, /* 0edb */ 0x2c05, 0x0000, /* 0edd */ 0x2c06, 0x0000, /* 0edf */ 0x2c07, 0x0000, /* 0ee1 */ 0x2c08, 0x0000, /* 0ee3 */ 0x2c09, 0x0000, /* 0ee5 */ 0x2c0a, 0x0000, /* 0ee7 */ 0x2c0b, 0x0000, /* 0ee9 */ 0x2c0c, 0x0000, /* 0eeb */ 0x2c0d, 0x0000, /* 0eed */ 0x2c0e, 0x0000, /* 0eef */ 0x2c0f, 0x0000, /* 0ef1 */ 0x2c10, 0x0000, /* 0ef3 */ 0x2c11, 0x0000, /* 0ef5 */ 0x2c12, 0x0000, /* 0ef7 */ 0x2c13, 0x0000, /* 0ef9 */ 0x2c14, 0x0000, /* 0efb */ 0x2c15, 0x0000, /* 0efd */ 0x2c16, 0x0000, /* 0eff */ 0x2c17, 0x0000, /* 0f01 */ 0x2c18, 0x0000, /* 0f03 */ 0x2c19, 0x0000, /* 0f05 */ 0x2c1a, 0x0000, /* 0f07 */ 0x2c1b, 0x0000, /* 0f09 */ 0x2c1c, 0x0000, /* 0f0b */ 0x2c1d, 0x0000, /* 0f0d */ 0x2c1e, 0x0000, /* 0f0f */ 0x2c1f, 0x0000, /* 0f11 */ 0x2c20, 0x0000, /* 0f13 */ 0x2c21, 0x0000, /* 0f15 */ 0x2c22, 0x0000, /* 0f17 */ 0x2c23, 0x0000, /* 0f19 */ 0x2c24, 0x0000, /* 0f1b */ 0x2c25, 0x0000, /* 0f1d */ 0x2c26, 0x0000, /* 0f1f */ 0x2c27, 0x0000, /* 0f21 */ 0x2c28, 0x0000, /* 0f23 */ 0x2c29, 0x0000, /* 0f25 */ 0x2c2a, 0x0000, /* 0f27 */ 0x2c2b, 0x0000, /* 0f29 */ 0x2c2c, 0x0000, /* 0f2b */ 0x2c2d, 0x0000, /* 0f2d */ 0x2c2e, 0x0000, /* 0f2f */ 0x2c61, 0x0000, /* 0f31 */ 0x2c60, 0x0000, /* 0f33 */ 0x026b, 0x0000, /* 0f35 */ 0x1d7d, 0x0000, /* 0f37 */ 0x027d, 0x0000, /* 0f39 */ 0x023a, 0x0000, /* 0f3b */ 0x023e, 0x0000, /* 0f3d */ 0x2c68, 0x0000, /* 0f3f */ 0x2c67, 0x0000, /* 0f41 */ 0x2c6a, 0x0000, /* 0f43 */ 0x2c69, 0x0000, /* 0f45 */ 0x2c6c, 0x0000, /* 0f47 */ 0x2c6b, 0x0000, /* 0f49 */ 0x0251, 0x0000, /* 0f4b */ 0x0271, 0x0000, /* 0f4d */ 0x0250, 0x0000, /* 0f4f */ 0x0252, 0x0000, /* 0f51 */ 0x2c73, 0x0000, /* 0f53 */ 0x2c72, 0x0000, /* 0f55 */ 0x2c76, 0x0000, /* 0f57 */ 0x2c75, 0x0000, /* 0f59 */ 0x023f, 0x0000, /* 0f5b */ 0x0240, 0x0000, /* 0f5d */ 0x2c81, 0x0000, /* 0f5f */ 0x2c80, 0x0000, /* 0f61 */ 0x2c83, 0x0000, /* 0f63 */ 0x2c82, 0x0000, /* 0f65 */ 0x2c85, 0x0000, /* 0f67 */ 0x2c84, 0x0000, /* 0f69 */ 0x2c87, 0x0000, /* 0f6b */ 0x2c86, 0x0000, /* 0f6d */ 0x2c89, 0x0000, /* 0f6f */ 0x2c88, 0x0000, /* 0f71 */ 0x2c8b, 0x0000, /* 0f73 */ 0x2c8a, 0x0000, /* 0f75 */ 0x2c8d, 0x0000, /* 0f77 */ 0x2c8c, 0x0000, /* 0f79 */ 0x2c8f, 0x0000, /* 0f7b */ 0x2c8e, 0x0000, /* 0f7d */ 0x2c91, 0x0000, /* 0f7f */ 0x2c90, 0x0000, /* 0f81 */ 0x2c93, 0x0000, /* 0f83 */ 0x2c92, 0x0000, /* 0f85 */ 0x2c95, 0x0000, /* 0f87 */ 0x2c94, 0x0000, /* 0f89 */ 0x2c97, 0x0000, /* 0f8b */ 0x2c96, 0x0000, /* 0f8d */ 0x2c99, 0x0000, /* 0f8f */ 0x2c98, 0x0000, /* 0f91 */ 0x2c9b, 0x0000, /* 0f93 */ 0x2c9a, 0x0000, /* 0f95 */ 0x2c9d, 0x0000, /* 0f97 */ 0x2c9c, 0x0000, /* 0f99 */ 0x2c9f, 0x0000, /* 0f9b */ 0x2c9e, 0x0000, /* 0f9d */ 0x2ca1, 0x0000, /* 0f9f */ 0x2ca0, 0x0000, /* 0fa1 */ 0x2ca3, 0x0000, /* 0fa3 */ 0x2ca2, 0x0000, /* 0fa5 */ 0x2ca5, 0x0000, /* 0fa7 */ 0x2ca4, 0x0000, /* 0fa9 */ 0x2ca7, 0x0000, /* 0fab */ 0x2ca6, 0x0000, /* 0fad */ 0x2ca9, 0x0000, /* 0faf */ 0x2ca8, 0x0000, /* 0fb1 */ 0x2cab, 0x0000, /* 0fb3 */ 0x2caa, 0x0000, /* 0fb5 */ 0x2cad, 0x0000, /* 0fb7 */ 0x2cac, 0x0000, /* 0fb9 */ 0x2caf, 0x0000, /* 0fbb */ 0x2cae, 0x0000, /* 0fbd */ 0x2cb1, 0x0000, /* 0fbf */ 0x2cb0, 0x0000, /* 0fc1 */ 0x2cb3, 0x0000, /* 0fc3 */ 0x2cb2, 0x0000, /* 0fc5 */ 0x2cb5, 0x0000, /* 0fc7 */ 0x2cb4, 0x0000, /* 0fc9 */ 0x2cb7, 0x0000, /* 0fcb */ 0x2cb6, 0x0000, /* 0fcd */ 0x2cb9, 0x0000, /* 0fcf */ 0x2cb8, 0x0000, /* 0fd1 */ 0x2cbb, 0x0000, /* 0fd3 */ 0x2cba, 0x0000, /* 0fd5 */ 0x2cbd, 0x0000, /* 0fd7 */ 0x2cbc, 0x0000, /* 0fd9 */ 0x2cbf, 0x0000, /* 0fdb */ 0x2cbe, 0x0000, /* 0fdd */ 0x2cc1, 0x0000, /* 0fdf */ 0x2cc0, 0x0000, /* 0fe1 */ 0x2cc3, 0x0000, /* 0fe3 */ 0x2cc2, 0x0000, /* 0fe5 */ 0x2cc5, 0x0000, /* 0fe7 */ 0x2cc4, 0x0000, /* 0fe9 */ 0x2cc7, 0x0000, /* 0feb */ 0x2cc6, 0x0000, /* 0fed */ 0x2cc9, 0x0000, /* 0fef */ 0x2cc8, 0x0000, /* 0ff1 */ 0x2ccb, 0x0000, /* 0ff3 */ 0x2cca, 0x0000, /* 0ff5 */ 0x2ccd, 0x0000, /* 0ff7 */ 0x2ccc, 0x0000, /* 0ff9 */ 0x2ccf, 0x0000, /* 0ffb */ 0x2cce, 0x0000, /* 0ffd */ 0x2cd1, 0x0000, /* 0fff */ 0x2cd0, 0x0000, /* 1001 */ 0x2cd3, 0x0000, /* 1003 */ 0x2cd2, 0x0000, /* 1005 */ 0x2cd5, 0x0000, /* 1007 */ 0x2cd4, 0x0000, /* 1009 */ 0x2cd7, 0x0000, /* 100b */ 0x2cd6, 0x0000, /* 100d */ 0x2cd9, 0x0000, /* 100f */ 0x2cd8, 0x0000, /* 1011 */ 0x2cdb, 0x0000, /* 1013 */ 0x2cda, 0x0000, /* 1015 */ 0x2cdd, 0x0000, /* 1017 */ 0x2cdc, 0x0000, /* 1019 */ 0x2cdf, 0x0000, /* 101b */ 0x2cde, 0x0000, /* 101d */ 0x2ce1, 0x0000, /* 101f */ 0x2ce0, 0x0000, /* 1021 */ 0x2ce3, 0x0000, /* 1023 */ 0x2ce2, 0x0000, /* 1025 */ 0x2cec, 0x0000, /* 1027 */ 0x2ceb, 0x0000, /* 1029 */ 0x2cee, 0x0000, /* 102b */ 0x2ced, 0x0000, /* 102d */ 0x2cf3, 0x0000, /* 102f */ 0x2cf2, 0x0000, /* 1031 */ 0x10a0, 0x0000, /* 1033 */ 0x10a1, 0x0000, /* 1035 */ 0x10a2, 0x0000, /* 1037 */ 0x10a3, 0x0000, /* 1039 */ 0x10a4, 0x0000, /* 103b */ 0x10a5, 0x0000, /* 103d */ 0x10a6, 0x0000, /* 103f */ 0x10a7, 0x0000, /* 1041 */ 0x10a8, 0x0000, /* 1043 */ 0x10a9, 0x0000, /* 1045 */ 0x10aa, 0x0000, /* 1047 */ 0x10ab, 0x0000, /* 1049 */ 0x10ac, 0x0000, /* 104b */ 0x10ad, 0x0000, /* 104d */ 0x10ae, 0x0000, /* 104f */ 0x10af, 0x0000, /* 1051 */ 0x10b0, 0x0000, /* 1053 */ 0x10b1, 0x0000, /* 1055 */ 0x10b2, 0x0000, /* 1057 */ 0x10b3, 0x0000, /* 1059 */ 0x10b4, 0x0000, /* 105b */ 0x10b5, 0x0000, /* 105d */ 0x10b6, 0x0000, /* 105f */ 0x10b7, 0x0000, /* 1061 */ 0x10b8, 0x0000, /* 1063 */ 0x10b9, 0x0000, /* 1065 */ 0x10ba, 0x0000, /* 1067 */ 0x10bb, 0x0000, /* 1069 */ 0x10bc, 0x0000, /* 106b */ 0x10bd, 0x0000, /* 106d */ 0x10be, 0x0000, /* 106f */ 0x10bf, 0x0000, /* 1071 */ 0x10c0, 0x0000, /* 1073 */ 0x10c1, 0x0000, /* 1075 */ 0x10c2, 0x0000, /* 1077 */ 0x10c3, 0x0000, /* 1079 */ 0x10c4, 0x0000, /* 107b */ 0x10c5, 0x0000, /* 107d */ 0x10c7, 0x0000, /* 107f */ 0x10cd, 0x0000, /* 1081 */ 0xa641, 0x0000, /* 1083 */ 0xa640, 0x0000, /* 1085 */ 0xa643, 0x0000, /* 1087 */ 0xa642, 0x0000, /* 1089 */ 0xa645, 0x0000, /* 108b */ 0xa644, 0x0000, /* 108d */ 0xa647, 0x0000, /* 108f */ 0xa646, 0x0000, /* 1091 */ 0xa649, 0x0000, /* 1093 */ 0xa648, 0x0000, /* 1095 */ 0xa64b, 0x0000, /* 1097 */ 0xa64a, 0x0000, /* 1099 */ 0xa64d, 0x0000, /* 109b */ 0xa64c, 0x0000, /* 109d */ 0xa64f, 0x0000, /* 109f */ 0xa64e, 0x0000, /* 10a1 */ 0xa651, 0x0000, /* 10a3 */ 0xa650, 0x0000, /* 10a5 */ 0xa653, 0x0000, /* 10a7 */ 0xa652, 0x0000, /* 10a9 */ 0xa655, 0x0000, /* 10ab */ 0xa654, 0x0000, /* 10ad */ 0xa657, 0x0000, /* 10af */ 0xa656, 0x0000, /* 10b1 */ 0xa659, 0x0000, /* 10b3 */ 0xa658, 0x0000, /* 10b5 */ 0xa65b, 0x0000, /* 10b7 */ 0xa65a, 0x0000, /* 10b9 */ 0xa65d, 0x0000, /* 10bb */ 0xa65c, 0x0000, /* 10bd */ 0xa65f, 0x0000, /* 10bf */ 0xa65e, 0x0000, /* 10c1 */ 0xa661, 0x0000, /* 10c3 */ 0xa660, 0x0000, /* 10c5 */ 0xa663, 0x0000, /* 10c7 */ 0xa662, 0x0000, /* 10c9 */ 0xa665, 0x0000, /* 10cb */ 0xa664, 0x0000, /* 10cd */ 0xa667, 0x0000, /* 10cf */ 0xa666, 0x0000, /* 10d1 */ 0xa669, 0x0000, /* 10d3 */ 0xa668, 0x0000, /* 10d5 */ 0xa66b, 0x0000, /* 10d7 */ 0xa66a, 0x0000, /* 10d9 */ 0xa66d, 0x0000, /* 10db */ 0xa66c, 0x0000, /* 10dd */ 0xa681, 0x0000, /* 10df */ 0xa680, 0x0000, /* 10e1 */ 0xa683, 0x0000, /* 10e3 */ 0xa682, 0x0000, /* 10e5 */ 0xa685, 0x0000, /* 10e7 */ 0xa684, 0x0000, /* 10e9 */ 0xa687, 0x0000, /* 10eb */ 0xa686, 0x0000, /* 10ed */ 0xa689, 0x0000, /* 10ef */ 0xa688, 0x0000, /* 10f1 */ 0xa68b, 0x0000, /* 10f3 */ 0xa68a, 0x0000, /* 10f5 */ 0xa68d, 0x0000, /* 10f7 */ 0xa68c, 0x0000, /* 10f9 */ 0xa68f, 0x0000, /* 10fb */ 0xa68e, 0x0000, /* 10fd */ 0xa691, 0x0000, /* 10ff */ 0xa690, 0x0000, /* 1101 */ 0xa693, 0x0000, /* 1103 */ 0xa692, 0x0000, /* 1105 */ 0xa695, 0x0000, /* 1107 */ 0xa694, 0x0000, /* 1109 */ 0xa697, 0x0000, /* 110b */ 0xa696, 0x0000, /* 110d */ 0xa723, 0x0000, /* 110f */ 0xa722, 0x0000, /* 1111 */ 0xa725, 0x0000, /* 1113 */ 0xa724, 0x0000, /* 1115 */ 0xa727, 0x0000, /* 1117 */ 0xa726, 0x0000, /* 1119 */ 0xa729, 0x0000, /* 111b */ 0xa728, 0x0000, /* 111d */ 0xa72b, 0x0000, /* 111f */ 0xa72a, 0x0000, /* 1121 */ 0xa72d, 0x0000, /* 1123 */ 0xa72c, 0x0000, /* 1125 */ 0xa72f, 0x0000, /* 1127 */ 0xa72e, 0x0000, /* 1129 */ 0xa733, 0x0000, /* 112b */ 0xa732, 0x0000, /* 112d */ 0xa735, 0x0000, /* 112f */ 0xa734, 0x0000, /* 1131 */ 0xa737, 0x0000, /* 1133 */ 0xa736, 0x0000, /* 1135 */ 0xa739, 0x0000, /* 1137 */ 0xa738, 0x0000, /* 1139 */ 0xa73b, 0x0000, /* 113b */ 0xa73a, 0x0000, /* 113d */ 0xa73d, 0x0000, /* 113f */ 0xa73c, 0x0000, /* 1141 */ 0xa73f, 0x0000, /* 1143 */ 0xa73e, 0x0000, /* 1145 */ 0xa741, 0x0000, /* 1147 */ 0xa740, 0x0000, /* 1149 */ 0xa743, 0x0000, /* 114b */ 0xa742, 0x0000, /* 114d */ 0xa745, 0x0000, /* 114f */ 0xa744, 0x0000, /* 1151 */ 0xa747, 0x0000, /* 1153 */ 0xa746, 0x0000, /* 1155 */ 0xa749, 0x0000, /* 1157 */ 0xa748, 0x0000, /* 1159 */ 0xa74b, 0x0000, /* 115b */ 0xa74a, 0x0000, /* 115d */ 0xa74d, 0x0000, /* 115f */ 0xa74c, 0x0000, /* 1161 */ 0xa74f, 0x0000, /* 1163 */ 0xa74e, 0x0000, /* 1165 */ 0xa751, 0x0000, /* 1167 */ 0xa750, 0x0000, /* 1169 */ 0xa753, 0x0000, /* 116b */ 0xa752, 0x0000, /* 116d */ 0xa755, 0x0000, /* 116f */ 0xa754, 0x0000, /* 1171 */ 0xa757, 0x0000, /* 1173 */ 0xa756, 0x0000, /* 1175 */ 0xa759, 0x0000, /* 1177 */ 0xa758, 0x0000, /* 1179 */ 0xa75b, 0x0000, /* 117b */ 0xa75a, 0x0000, /* 117d */ 0xa75d, 0x0000, /* 117f */ 0xa75c, 0x0000, /* 1181 */ 0xa75f, 0x0000, /* 1183 */ 0xa75e, 0x0000, /* 1185 */ 0xa761, 0x0000, /* 1187 */ 0xa760, 0x0000, /* 1189 */ 0xa763, 0x0000, /* 118b */ 0xa762, 0x0000, /* 118d */ 0xa765, 0x0000, /* 118f */ 0xa764, 0x0000, /* 1191 */ 0xa767, 0x0000, /* 1193 */ 0xa766, 0x0000, /* 1195 */ 0xa769, 0x0000, /* 1197 */ 0xa768, 0x0000, /* 1199 */ 0xa76b, 0x0000, /* 119b */ 0xa76a, 0x0000, /* 119d */ 0xa76d, 0x0000, /* 119f */ 0xa76c, 0x0000, /* 11a1 */ 0xa76f, 0x0000, /* 11a3 */ 0xa76e, 0x0000, /* 11a5 */ 0xa77a, 0x0000, /* 11a7 */ 0xa779, 0x0000, /* 11a9 */ 0xa77c, 0x0000, /* 11ab */ 0xa77b, 0x0000, /* 11ad */ 0x1d79, 0x0000, /* 11af */ 0xa77f, 0x0000, /* 11b1 */ 0xa77e, 0x0000, /* 11b3 */ 0xa781, 0x0000, /* 11b5 */ 0xa780, 0x0000, /* 11b7 */ 0xa783, 0x0000, /* 11b9 */ 0xa782, 0x0000, /* 11bb */ 0xa785, 0x0000, /* 11bd */ 0xa784, 0x0000, /* 11bf */ 0xa787, 0x0000, /* 11c1 */ 0xa786, 0x0000, /* 11c3 */ 0xa78c, 0x0000, /* 11c5 */ 0xa78b, 0x0000, /* 11c7 */ 0x0265, 0x0000, /* 11c9 */ 0xa791, 0x0000, /* 11cb */ 0xa790, 0x0000, /* 11cd */ 0xa793, 0x0000, /* 11cf */ 0xa792, 0x0000, /* 11d1 */ 0xa7a1, 0x0000, /* 11d3 */ 0xa7a0, 0x0000, /* 11d5 */ 0xa7a3, 0x0000, /* 11d7 */ 0xa7a2, 0x0000, /* 11d9 */ 0xa7a5, 0x0000, /* 11db */ 0xa7a4, 0x0000, /* 11dd */ 0xa7a7, 0x0000, /* 11df */ 0xa7a6, 0x0000, /* 11e1 */ 0xa7a9, 0x0000, /* 11e3 */ 0xa7a8, 0x0000, /* 11e5 */ 0x0266, 0x0000, /* 11e7 */ 0x0046, 0x0066, 0x0000, /* 11ea */ 0x0046, 0x0046, 0x0000, /* 11ed */ 0x0066, 0x0066, 0x0000, /* 11f0 */ 0x0046, 0x0069, 0x0000, /* 11f3 */ 0x0046, 0x0049, 0x0000, /* 11f6 */ 0x0066, 0x0069, 0x0000, /* 11f9 */ 0x0046, 0x006c, 0x0000, /* 11fc */ 0x0046, 0x004c, 0x0000, /* 11ff */ 0x0066, 0x006c, 0x0000, /* 1202 */ 0x0046, 0x0066, 0x0069, 0x0000, /* 1206 */ 0x0046, 0x0046, 0x0049, 0x0000, /* 120a */ 0x0066, 0x0066, 0x0069, 0x0000, /* 120e */ 0x0046, 0x0066, 0x006c, 0x0000, /* 1212 */ 0x0046, 0x0046, 0x004c, 0x0000, /* 1216 */ 0x0066, 0x0066, 0x006c, 0x0000, /* 121a */ 0x0053, 0x0074, 0x0000, /* 121d */ 0x0053, 0x0054, 0x0000, /* 1220 */ 0x0073, 0x0074, 0x0000, /* 1223 */ 0x0053, 0x0074, 0x0000, /* 1226 */ 0x0053, 0x0054, 0x0000, /* 1229 */ 0x0073, 0x0074, 0x0000, /* 122c */ 0x0544, 0x0576, 0x0000, /* 122f */ 0x0544, 0x0546, 0x0000, /* 1232 */ 0x0574, 0x0576, 0x0000, /* 1235 */ 0x0544, 0x0565, 0x0000, /* 1238 */ 0x0544, 0x0535, 0x0000, /* 123b */ 0x0574, 0x0565, 0x0000, /* 123e */ 0x0544, 0x056b, 0x0000, /* 1241 */ 0x0544, 0x053b, 0x0000, /* 1244 */ 0x0574, 0x056b, 0x0000, /* 1247 */ 0x054e, 0x0576, 0x0000, /* 124a */ 0x054e, 0x0546, 0x0000, /* 124d */ 0x057e, 0x0576, 0x0000, /* 1250 */ 0x0544, 0x056d, 0x0000, /* 1253 */ 0x0544, 0x053d, 0x0000, /* 1256 */ 0x0574, 0x056d, 0x0000, /* 1259 */ 0xff41, 0x0000, /* 125b */ 0xff42, 0x0000, /* 125d */ 0xff43, 0x0000, /* 125f */ 0xff44, 0x0000, /* 1261 */ 0xff45, 0x0000, /* 1263 */ 0xff46, 0x0000, /* 1265 */ 0xff47, 0x0000, /* 1267 */ 0xff48, 0x0000, /* 1269 */ 0xff49, 0x0000, /* 126b */ 0xff4a, 0x0000, /* 126d */ 0xff4b, 0x0000, /* 126f */ 0xff4c, 0x0000, /* 1271 */ 0xff4d, 0x0000, /* 1273 */ 0xff4e, 0x0000, /* 1275 */ 0xff4f, 0x0000, /* 1277 */ 0xff50, 0x0000, /* 1279 */ 0xff51, 0x0000, /* 127b */ 0xff52, 0x0000, /* 127d */ 0xff53, 0x0000, /* 127f */ 0xff54, 0x0000, /* 1281 */ 0xff55, 0x0000, /* 1283 */ 0xff56, 0x0000, /* 1285 */ 0xff57, 0x0000, /* 1287 */ 0xff58, 0x0000, /* 1289 */ 0xff59, 0x0000, /* 128b */ 0xff5a, 0x0000, /* 128d */ 0xff21, 0x0000, /* 128f */ 0xff22, 0x0000, /* 1291 */ 0xff23, 0x0000, /* 1293 */ 0xff24, 0x0000, /* 1295 */ 0xff25, 0x0000, /* 1297 */ 0xff26, 0x0000, /* 1299 */ 0xff27, 0x0000, /* 129b */ 0xff28, 0x0000, /* 129d */ 0xff29, 0x0000, /* 129f */ 0xff2a, 0x0000, /* 12a1 */ 0xff2b, 0x0000, /* 12a3 */ 0xff2c, 0x0000, /* 12a5 */ 0xff2d, 0x0000, /* 12a7 */ 0xff2e, 0x0000, /* 12a9 */ 0xff2f, 0x0000, /* 12ab */ 0xff30, 0x0000, /* 12ad */ 0xff31, 0x0000, /* 12af */ 0xff32, 0x0000, /* 12b1 */ 0xff33, 0x0000, /* 12b3 */ 0xff34, 0x0000, /* 12b5 */ 0xff35, 0x0000, /* 12b7 */ 0xff36, 0x0000, /* 12b9 */ 0xff37, 0x0000, /* 12bb */ 0xff38, 0x0000, /* 12bd */ 0xff39, 0x0000, /* 12bf */ 0xff3a, 0x0000 }; const wchar_t *t3_to_lower(wchar_t ch) { case_table *e = case_tab[ch >> 7]; if (e == 0) return 0; int ofs = e[ch & 127].lower; return ofs == 0 ? 0 : &case_expansion[ofs]; } const wchar_t *t3_to_title(wchar_t ch) { case_table *e = case_tab[ch >> 7]; if (e == 0) return 0; int ofs = e[ch & 127].title; return ofs == 0 ? 0 : &case_expansion[ofs]; } const wchar_t *t3_to_upper(wchar_t ch) { case_table *e = case_tab[ch >> 7]; if (e == 0) return 0; int ofs = e[ch & 127].upper; return ofs == 0 ? 0 : &case_expansion[ofs]; } const wchar_t *t3_to_fold(wchar_t ch) { case_table *e = case_tab[ch >> 7]; if (e == 0) return 0; int ofs = e[ch & 127].fold; return ofs == 0 ? 0 : &case_expansion[ofs]; } wchar_t t3_simple_case_fold(wchar_t ch) { case_table *e = case_tab[ch >> 7]; if (e == 0) return 0; int ofs = e[ch & 127].lower; return ofs == 0 ? ch : case_expansion[ofs + 1] != 0 ? ch : case_expansion[ofs]; } /* total static data size (16-bit pointers) = 75138 bytes */ qtads-2.1.7/tads3/gameinfo.cpp000066400000000000000000000425101265017072300161510ustar00rootroot00000000000000/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name gameinfo.cpp - extract and parse tads 2/3 game file information Function Searches a compiled tads 2 game file or a compiled tads 3 image file for a game information resource. If the resource is found, extracts and parses the resource. A game information resource is a set of name/value pairs giving information on the game, such as its name, author, and description. Each value is simply a text string. The game information is supplied by the author, and is intended to help archives and users by providing a way to obtain specific information about the game in a structured format directly from the game file itself. This implementation is independent of any tads 2 or tads 3 subsystem except osifc, which it uses for portable file I/O and portable byte format conversions. We do depend upon the 'resfind' utility module, which extracts multimedia resources from tads game files. For an example of how to use this module, please refer to the TEST definitions at the bottom of this file - this gives a small sample program that loads game information from a file and displays each field on the standard output. For sample build commands, refer to mk_gameinfo.bat, which builds the test program for Win32 console mode using MS Visual C++. Notes Modified 09/24/01 MJRoberts - Creation */ #include #include #include #include "t3std.h" #include "utf8.h" #include "resfind.h" #include "gameinfo.h" /* ------------------------------------------------------------------------ */ /* * Name/value pair list entry */ struct tads_valinfo { const char *name; size_t name_len; /* value string */ const char *val; /* next entry in the list */ tads_valinfo *nxt; }; /* ------------------------------------------------------------------------ */ /* * Create */ CTadsGameInfo::CTadsGameInfo() { /* no buffer yet */ buf_ = 0; /* no name/value pairs yet */ first_val_ = 0; } /* ------------------------------------------------------------------------ */ /* * Delete */ CTadsGameInfo::~CTadsGameInfo() { /* if we have a buffer, delete it */ if (buf_ != 0) osfree(buf_); /* free the name/value pair list */ free_value_list(); } /* * Delete the list. If a subclass overrides free_value(), it should call * this routine from its overridden destructor, to ensure that the vtable * still has a reference to the subclass version of free_value() when this * is called. * * Since this routine deletes each entry and leaves us with an empty list, * it's harmless to call this more than once; each subclass can thus * safely call this from its own destructor, even if it thinks its own * superclass or subclasses might call it as well. */ void CTadsGameInfo::free_value_list() { while (first_val_ != 0) { /* remember the next one */ tads_valinfo *nxt = first_val_->nxt; /* * delete this one - delete its value string and then the * structure itself */ free_value(first_val_->val); osfree(first_val_); /* move on to the next */ first_val_ = nxt; } } /* ------------------------------------------------------------------------ */ /* * Parse a game file and retrieve game information */ int CTadsGameInfo::read_from_fp(osfildef *fp) { /* find the GameInfo.txt resource */ tads_resinfo resinfo; if (!tads_find_resource_fp(fp, "GameInfo.txt", &resinfo)) { /* no GameInfo.txt resource - there's no game information */ return FALSE; } /* parse the file and return the result code */ return parse_file(fp, resinfo.seek_pos, resinfo.siz); } /* * Parse a game file and retrieve game information */ int CTadsGameInfo::read_from_file(const char *fname) { /* open the file */ osfildef *fp; if ((fp = osfoprb(fname, OSFTGAME)) == 0 && (fp = osfoprb(fname, OSFTT3IMG)) == 0) { /* * we can't open the file, so we obviously can't parse it to find * game information */ return 0; } /* parse the file and find the game information */ int ret = read_from_fp(fp); /* we're done with the file - close it */ osfcls(fp); /* return the results from the parser */ return ret; } /* ------------------------------------------------------------------------ */ /* * Is this a vertical whitespace character? */ static int is_vspace(wchar_t ch) { return ch == '\n' || ch == '\r' || ch == 0x2028; } /* * Is this a horizontal whitespace character (i.e., any whitespace * character other than a newline character of some kind)? */ static int is_hspace(wchar_t ch) { /* * it's a horizontal whitespace character if it's a whitespace * character and it's not a vertical whitespace character */ return is_space(ch) && !is_vspace(ch); } /* ------------------------------------------------------------------------ */ /* * Skip a newline sequence. We'll skip the longest of the following * sequences we find: CR, LF, CR-LF, LF-CR, 0x2028. */ static void skip_newline(utf8_ptr *p, size_t *rem) { /* if there's nothing left, there's nothing to do */ if (*rem == 0) return; /* check what we have and skip accordingly */ switch(p->getch()) { case '\n': /* * it's an LF, so skip it and check for a CR; if a CR immediately * follows, consider the entire LF-CR to be a single newline, so * skip the CR as well */ p->inc(rem); if (*rem != 0 && p->getch() == '\r') p->inc(rem); break; case '\r': /* * it's a CR, so skip it and check for an LF; if an LF immediately * follows, consider the entire CR-LF to be a single newline, so * skip the LF as well */ p->inc(rem); if (*rem != 0 && p->getch() == '\n') p->inc(rem); break; case 0x2028: /* * it's a Unicode line separator character - this indicates a * newline but cannot be used in combination with any other * characters to form multi-character newline sequences, so simply * skip this one and we're done */ p->inc(rem); break; default: /* we're not on a newline at all, so there's nothing for us to do */ break; } } /* ------------------------------------------------------------------------ */ /* * Skip to the start of the next line */ static void skip_to_next_line(utf8_ptr *p, size_t *rem) { /* find the next line-ending sequence */ for ( ; *rem != 0 ; p->inc(rem)) { /* get the current character */ wchar_t ch = p->getch(); /* check to see if we've reached a newline yet */ if (is_vspace(ch)) { /* skip the entire newline sequence */ skip_newline(p, rem); /* we're done looking for the end of the line */ break; } } } /* ------------------------------------------------------------------------ */ /* * Parse a file */ int CTadsGameInfo::parse_file(osfildef *fp, unsigned long res_seek_pos, unsigned long res_size) { /* find the tail of the existing list */ tads_valinfo *last_val; for (last_val = first_val_ ; last_val != 0 && last_val->nxt != 0 ; last_val = last_val->nxt) ; /* we found the resource - seek to it in the file */ if (osfseek(fp, res_seek_pos, OSFSK_SET)) return FALSE; /* * Allocate a block of memory for loading the game information. The * game information resource is typically fairly small, so for * simplicity we'll just allocate a single block of memory and load * the whole resource into the block. Allocate space for one extra * byte, so that we can ensure we have a newline at the end of the * buffer. */ buf_ = (char *)osmalloc(res_size + 1); if (buf_ == 0) return FALSE; /* read the data */ if (osfrb(fp, buf_, res_size)) return FALSE; /* * store an extra newline at the end of the buffer, so that we can be * certain that the last line ends in a newline - at worst, this will * add an extra blank line to the end, but since we ignore blank lines * this will do no harm */ buf_[res_size++] = '\n'; /* parse the data */ utf8_ptr p(buf_); for (size_t rem = res_size ; rem != 0 ; ) { /* skip any leading whitespace */ while (rem != 0 && is_space(p.getch())) p.inc(&rem); /* if the line starts with '#', it's a comment, so skip it */ if (rem != 0 && p.getch() == '#') { /* skip the entire line, and go back for the next one */ skip_to_next_line(&p, &rem); continue; } /* we must have the start of a name - note it */ utf8_ptr name_start = p; /* skip ahead to a space or colon */ while (rem != 0 && p.getch() != ':' && !is_hspace(p.getch())) p.inc(&rem); /* note the length of the name */ size_t name_len = p.getptr() - name_start.getptr(); /* skip any whitespace before the presumed colon */ while (rem != 0 && is_hspace(p.getch())) p.inc(&rem); /* if we're not at a colon, the line is ill-formed, so skip it */ if (rem == 0 || p.getch() != ':') { /* skip the entire line, and go back for the next one */ skip_to_next_line(&p, &rem); continue; } /* skip the colon and any whitespace immediately after it */ for (p.inc(&rem) ; rem != 0 && is_hspace(p.getch()) ; p.inc(&rem)) ; /* * Whatever terminated the name, replace it with a null character * - this is safe, since we at least have a colon character to * replace, and we've already skipped it so we can overwrite it * now. A null character in utf-8 is simply a single zero byte, * so we can store our byte directly without worrying about any * utf-8 multi-byte strangeness. */ *(name_start.getptr() + name_len) = '\0'; /* note where the value starts */ utf8_ptr val_start = p; /* set up to write to the buffer at the current point */ utf8_ptr dst = p; /* * Now find the end of the value. The value can span multiple * lines; if we find a newline followed by a space, it means that * the next line is a continuation of the current value, and that * the entire sequence of newlines and immediately following * whitespace should be converted to a single space in the value. * * Note that we copy the transformed value directly over the old * version of the value in the buffer. This is safe because the * transformation can only remove characters - we merely collapse * each newline-whitespace sequence into a single space. */ while (rem != 0) { /* get this character */ wchar_t ch = p.getch(); /* check for a newline */ if (is_vspace(ch)) { /* * it's a newline - skip it (and any other characters in * the newline sequence) */ skip_newline(&p, &rem); /* * if there's no leading whitespace on the next line, * we've reached the end of this value */ if (rem == 0 || !is_hspace(p.getch())) { /* * no whitespace -> end of the value - stop scanning * the value */ break; } /* skip leading whitespace on the line */ while (rem != 0 && is_hspace(p.getch())) p.inc(&rem); /* * add a single whitespace character to the output for the * entire sequence of the newline plus the leading * whitespace on the line */ dst.setch(' '); } else if (ch == 0) { /* change null bytes to spaces */ dst.setch(' '); /* skip this character */ p.inc(&rem); } else { /* it's not a newline - simply copy it out and keep going */ dst.setch(ch); /* skip this character */ p.inc(&rem); } } /* * Store a null terminator at the end of the value (it's safe to * write this to the buffer because at worst it'll overwrite the * newline at the end of the last line, which we've already * skipped in the source). Note that we obtain the length of the * value string before storing the null terminator, because we * don't want to count the null byte in the value's length. */ dst.setch('\0'); /* * Create a new value list entry. Point the entry directly into * our buffer, since we're keeping the buffer around as long as * the value list is around. */ tads_valinfo *val_info = (tads_valinfo *)osmalloc(sizeof(tads_valinfo)); val_info->name = name_start.getptr(); val_info->name_len = name_len; val_info->val = store_value(val_start.getptr(), dst.getptr() - val_start.getptr()); val_info->nxt = 0; /* link the new value at the end of our list */ if (last_val != 0) last_val->nxt = val_info; else first_val_ = val_info; last_val = val_info; } /* success */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Look up a a value by name, returning a pointer to the value string. */ const char *CTadsGameInfo::get_val(const char *name) const { /* for efficiency, note the length of the name up front */ size_t name_len = strlen(name); /* scan our list of value entries for the given name */ for (tads_valinfo *cur = first_val_ ; cur != 0 ; cur = cur->nxt) { /* * If the name matches, return the value for this entry. Note * that, since the name is always a plain ASCII string, we can * perform a simple byte-by-byte case-insensitive comparison using * memicmp. */ if (cur->name_len == name_len && memicmp(cur->name, name, name_len) == 0) { /* it's the one - return its value */ return cur->val; } } /* didn't find it - return null */ return 0; } /* ------------------------------------------------------------------------ */ /* * Enumerate all name/value pairs */ void CTadsGameInfo::enum_values(CTadsGameInfo_enum *cb) { /* invoke the callback for each value in our list */ for (tads_valinfo *cur = first_val_ ; cur != 0 ; cur = cur->nxt) cb->tads_enum_game_info(cur->name, cur->val); } /* ------------------------------------------------------------------------ */ /* * Testing routine. We'll find the game information in a given file, then * display all of the strings. We'll convert the values to the display * character set before showing them. */ #ifdef TEST #include "charmap.h" #include "resload.h" #include "vmimage.h" // $$$ for our vmimage substitution /* * name/value pair enumeration interface implementation - this is an * implementation for the testing program that merely displays the * name/value pair on the standard output */ class CTestEnum: public CTadsGameInfo_enum { public: /* * CTadsGameInfo_enum implementation */ /* receive a name/value pair in the enumeration */ virtual void tads_enum_game_info(const char *name, const char *val) { /* display the name/value pair */ printf("%s = %s\n", name, val); } }; /* * main test program entrypoint */ int main(int argc, char **argv) { /* check usage */ if (argc != 2) { printf("usage: gameinfo \n"); exit(2); } /* get the arguments */ const char *fname = argv[1]; /* * set up a game information reader that translates to the default * local display character set */ CTadsGameInfo *info = new CTadsGameInfoLocal(argv[0]); /* load the game information object, if possible */ if (!info->read_from_file(fname)) { printf("unable to load game information from file \"%s\"\n", fname); delete info; exit(2); } /* enumerate the name/value pairs through our callback implementation */ CTestEnum cb; info->enum_values(&cb); /* we're done with our game information reader - delete it */ delete info; /* success */ exit(0); return 0; } #endif /* TEST */ qtads-2.1.7/tads3/gameinfo.h000066400000000000000000000152471265017072300156250ustar00rootroot00000000000000/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name gameinfo.h - extract and parse tads 2/3 game file information Function Searches a compiled tads 2 game file or a compiled tads 3 image file for a game information resource. If the resource is found, extracts and parses the resource. A game information resource is a set of name/value pairs giving information on the game, such as its name, author, and description. Each value is simply a text string. The game information is supplied by the author, and is intended to help archives and users by providing a way to obtain specific information about the game in a structured format directly from the game file itself. This implementation is independent of any tads 2 or tads 3 subsystem except osifc, which it uses for portable file I/O and portable byte format conversions. We also depend upon the 'resfind' module, which extracts multimedia resources from tads game files. Notes Modified 09/24/01 MJRoberts - Creation */ #ifndef GAMEINFO_H #define GAMEINFO_H #include "os.h" /* * Base game information parser. This base version, when used directly, * uses UTF-8 encoding for all value strings. If values are desired in a * different character set, use the CTadsGameInfoLocal subclass. */ class CTadsGameInfo { public: /* create */ CTadsGameInfo(); /* delete the object */ virtual ~CTadsGameInfo(); /* * Parse a game file and extract the game information. Returns FALSE * if there is no game information in the file, the file is not a * valid game file, or any other error occurs. Returns TRUE on * success. */ int read_from_file(const char *fname); /* * Parse a file that's already open and positioned to the start of the * game file data. This can be used to parse a game file data stream * that is embedded in a larger file. */ int read_from_fp(osfildef *fp); /* * Find a value given the value name. Returns a pointer to a * null-terminated value string, which uses the character set * appropriate to the game information reader; this is UTF-8 for this * base class. (The pointer returned refers to memory we manage, so * the pointer is valid as long as 'this' is valid - i.e., as long as * 'this' isn't deleted.) Returns null if there is no value with the * given name. * * The name is given as an ASCII string and is insensitive to case. */ const char *get_val(const char *name) const; /* * Enumerate all of the named values. Invokes the callback interface * (cb->tads_enum_game_info()) for each value. */ void enum_values(class CTadsGameInfo_enum *cb); protected: /* * Store a value string. The value is given as UTF-8; the result * should be in the appropriate character set for the subclass. This * is virtualized to allow subclasses to translate the value's * character set before storing the value. * * The given value string is null-terminated, and is in memory managed * by the class and retained as long as the class is valid. * Implementations can thus return the given value string unchanged, * if desired. */ virtual const char *store_value(const char *val, size_t len) { /* * The base version can simply return a reference to the original * string, since we want to store the values in UTF-8 as given. */ return val; } /* free a value string previously stored with store_value() */ virtual void free_value(const char *) { /* * The base version doesn't allocate the value strings separately * (it just uses the original pool of strings), so we don't have * to free the strings separately. We thus simply do nothing. */ } /* free the value list */ void free_value_list(); /* parse the given file data; returns true on success, false on error */ int parse_file(osfildef *fp, unsigned long res_seek_pos, unsigned long res_size); /* buffer containing the contents of the game information */ char *buf_; /* our list of name/value pairs */ struct tads_valinfo *first_val_; }; /* * Game information extensions for the local character set. This subclass * has some extra functions that allow retrieving the game information in * the local character set, rather than in utf-8. By default, we use the * local display character set as specified by os_get_charmap(), but the * caller can specify a character set explicitly by name, or by giving us * a character mapper object. */ class CTadsGameInfoLocal: public CTadsGameInfo { public: /* initialize with the default character mapper */ CTadsGameInfoLocal(const char *argv0); /* initialize with the named character mapper */ CTadsGameInfoLocal(const char *argv0, const char *charset_name); /* initialize with the given character mapper */ CTadsGameInfoLocal(class CCharmapToLocal *charmap); /* delete */ ~CTadsGameInfoLocal(); protected: /* store a value in the local character set */ virtual const char *store_value(const char *val, size_t len); /* free a value string previously stored with store_value() */ virtual void free_value(const char *); /* initialize from a named character mapping */ void init(const char *argv0, const char *charset_name); /* * our unicode-to-local character mapper, for the local character set * methods; this is null if we haven't had a need for it yet */ class CCharmapToLocal *local_mapper_; }; /* * Enumerator interface. This interface must be implemented in order to * call CTadsGameInfo::enum_values(). */ class CTadsGameInfo_enum { public: /* * Receive one name/value pair. 'name' points to a null-terminated * ASCII string giving the value's name, and 'val' points to a * null-terminated string giving the value text. The value string * will be encoded in the character set which the GameInfo reader is * using. * * The name and value pointers refer to memory managed as part of the * CTadsGameInfo object doing the enumerating. The memory to which * these pointers refer is valid as long as the CTadsGameInfo object * is valid (i.e., until it's deleted). */ virtual void tads_enum_game_info(const char *name, const char *val) = 0; }; #endif /* GAMEINFO_H */ qtads-2.1.7/tads3/indlg_os3.cpp000066400000000000000000000057601265017072300162530ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name indlg_os.cpp - OS-dialog-based implementation of input_dialog Function Implements the input dialog using the OS-provided dialog Notes Only one of indlg_tx.c or indlg_os.c should be included in a given executable. For a text-only version, include indlg_tx. For a version where os_input_dialog() provides a system dialog, use indlg_os instead. We provide a choice of input_dialog() implementations in the portable code (rather than only through the OS code) so that we can call the formatted text output routines in this version. An OS-layer implementation could not call the formatted output routines (it would have to call os_printf directly), which would result in poor prompt formatting any time a prompt exceeded a single line of text. Modified 09/27/99 MJRoberts - Creation */ #include "os.h" #include "t3std.h" #include "vmglob.h" #include "vmconsol.h" #include "charmap.h" /* * formatted text-only file prompt */ int CVmConsole::input_dialog(VMG_ int icon_id, const char *prompt, int standard_button_set, const char **buttons, int button_count, int default_index, int cancel_index, int bypass_script) { char ui_prompt[256]; const char *ui_buttons[10]; char ui_button_buf[256]; size_t ui_prompt_len; char *dst; size_t dstrem; int i; /* flush any pending output */ flush(vmg_ VM_NL_INPUT); /* convert the prompt to the local character set */ ui_prompt_len = G_cmap_to_ui->map_utf8(ui_prompt, sizeof(ui_prompt) - 1, prompt, strlen(prompt), 0); /* null-terminate the prompt text */ ui_prompt[ui_prompt_len] = '\0'; /* convert the button labels to the local character set */ if (button_count > (int)sizeof(ui_buttons)/sizeof(ui_buttons[0])) button_count = (int)sizeof(ui_buttons)/sizeof(ui_buttons[0]); for (i = 0, dst = ui_button_buf, dstrem = sizeof(ui_button_buf) ; i < button_count ; ++i) { size_t len; /* set up this converted button pointer */ ui_buttons[i] = dst; /* map this button's text, leaving room for a null terminator */ len = G_cmap_to_ui->map_utf8(dst, dstrem - 1, buttons[i], strlen(buttons[i]), 0); /* null-terminate it */ dst[len++] = '\0'; /* advance past this label */ dst += len; dstrem -= len; } /* ask the OS to do the work, and return the result */ return os_input_dialog(icon_id, ui_prompt, standard_button_set, ui_buttons, button_count, default_index, cancel_index); } qtads-2.1.7/tads3/md5.cpp000066400000000000000000000345351265017072300150610ustar00rootroot00000000000000/* Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.c is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order either statically or dynamically; added missing #include in library. 2002-03-11 lpd Corrected argument list for main(), and added int return type, in test program and T value program. 2002-02-21 lpd Added missing #include in test program. 2000-07-03 lpd Patched to eliminate warnings about "constant is unsigned in ANSI C, signed in traditional"; made test program self-checking. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). 1999-05-03 lpd Original version. */ #include "md5.h" #include "t3std.h" // MJR addition #include "vmdatasrc.h" // MJR addition #include #undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ #ifdef ARCH_IS_BIG_ENDIAN # define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) #else # define BYTE_ORDER 0 #endif #define T_MASK ((md5_word_t)~0) #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) #define T3 0x242070db #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) #define T6 0x4787c62a #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) #define T9 0x698098d8 #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) #define T13 0x6b901122 #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) #define T16 0x49b40821 #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) #define T19 0x265e5a51 #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) #define T22 0x02441453 #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) #define T25 0x21e1cde6 #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) #define T28 0x455a14ed #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) #define T31 0x676f02d9 #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) #define T35 0x6d9d6122 #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) #define T38 0x4bdecfa9 #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) #define T41 0x289b7ec6 #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) #define T44 0x04881d05 #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) #define T47 0x1fa27cf8 #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) #define T50 0x432aff97 #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) #define T53 0x655b59c3 #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) #define T57 0x6fa87e4f #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) #define T60 0x4e0811a1 #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) #define T63 0x2ad7d2bb #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; #if BYTE_ORDER > 0 /* Define storage only for big-endian CPUs. */ md5_word_t X[16]; #else /* Define storage for little-endian or both types of CPUs. */ md5_word_t xbuf[16]; const md5_word_t *X; #endif { #if BYTE_ORDER == 0 /* * Determine dynamically whether this is a big-endian or * little-endian machine, since we can use a more efficient * algorithm on the latter. */ static const int w = 1; if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ #endif #if BYTE_ORDER <= 0 /* little-endian */ { /* * On little-endian machines, we can process properly aligned * data without copying it. */ if (!((data - (const md5_byte_t *)0) & 3)) { /* data are properly aligned */ X = (const md5_word_t *)data; } else { /* not aligned */ memcpy(xbuf, data, 64); X = xbuf; } } #endif #if BYTE_ORDER == 0 else /* dynamic big-endian */ #endif #if BYTE_ORDER >= 0 /* big-endian */ { /* * On big-endian machines, we must arrange the bytes in the * right order. */ const md5_byte_t *xp = data; int i; # if BYTE_ORDER == 0 X = xbuf; /* (dynamic only) */ # else # define xbuf X /* (static only) */ # endif for (i = 0; i < 16; ++i, xp += 4) xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); } #endif } #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* Round 1. */ /* Let [abcd k s i] denote the operation a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + F(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 7, T1); SET(d, a, b, c, 1, 12, T2); SET(c, d, a, b, 2, 17, T3); SET(b, c, d, a, 3, 22, T4); SET(a, b, c, d, 4, 7, T5); SET(d, a, b, c, 5, 12, T6); SET(c, d, a, b, 6, 17, T7); SET(b, c, d, a, 7, 22, T8); SET(a, b, c, d, 8, 7, T9); SET(d, a, b, c, 9, 12, T10); SET(c, d, a, b, 10, 17, T11); SET(b, c, d, a, 11, 22, T12); SET(a, b, c, d, 12, 7, T13); SET(d, a, b, c, 13, 12, T14); SET(c, d, a, b, 14, 17, T15); SET(b, c, d, a, 15, 22, T16); #undef SET /* Round 2. */ /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + G(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 1, 5, T17); SET(d, a, b, c, 6, 9, T18); SET(c, d, a, b, 11, 14, T19); SET(b, c, d, a, 0, 20, T20); SET(a, b, c, d, 5, 5, T21); SET(d, a, b, c, 10, 9, T22); SET(c, d, a, b, 15, 14, T23); SET(b, c, d, a, 4, 20, T24); SET(a, b, c, d, 9, 5, T25); SET(d, a, b, c, 14, 9, T26); SET(c, d, a, b, 3, 14, T27); SET(b, c, d, a, 8, 20, T28); SET(a, b, c, d, 13, 5, T29); SET(d, a, b, c, 2, 9, T30); SET(c, d, a, b, 7, 14, T31); SET(b, c, d, a, 12, 20, T32); #undef SET /* Round 3. */ /* Let [abcd k s t] denote the operation a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ #define H(x, y, z) ((x) ^ (y) ^ (z)) #define SET(a, b, c, d, k, s, Ti)\ t = a + H(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 5, 4, T33); SET(d, a, b, c, 8, 11, T34); SET(c, d, a, b, 11, 16, T35); SET(b, c, d, a, 14, 23, T36); SET(a, b, c, d, 1, 4, T37); SET(d, a, b, c, 4, 11, T38); SET(c, d, a, b, 7, 16, T39); SET(b, c, d, a, 10, 23, T40); SET(a, b, c, d, 13, 4, T41); SET(d, a, b, c, 0, 11, T42); SET(c, d, a, b, 3, 16, T43); SET(b, c, d, a, 6, 23, T44); SET(a, b, c, d, 9, 4, T45); SET(d, a, b, c, 12, 11, T46); SET(c, d, a, b, 15, 16, T47); SET(b, c, d, a, 2, 23, T48); #undef SET /* Round 4. */ /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + I(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 6, T49); SET(d, a, b, c, 7, 10, T50); SET(c, d, a, b, 14, 15, T51); SET(b, c, d, a, 5, 21, T52); SET(a, b, c, d, 12, 6, T53); SET(d, a, b, c, 3, 10, T54); SET(c, d, a, b, 10, 15, T55); SET(b, c, d, a, 1, 21, T56); SET(a, b, c, d, 8, 6, T57); SET(d, a, b, c, 15, 10, T58); SET(c, d, a, b, 6, 15, T59); SET(b, c, d, a, 13, 21, T60); SET(a, b, c, d, 4, 6, T61); SET(d, a, b, c, 11, 10, T62); SET(c, d, a, b, 2, 15, T63); SET(b, c, d, a, 9, 21, T64); #undef SET /* Then perform the following additions. (That is increment each of the four registers by the value it had before this block was started.) */ pms->abcd[0] += a; pms->abcd[1] += b; pms->abcd[2] += c; pms->abcd[3] += d; } void md5_init(md5_state_t *pms) { pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; pms->abcd[3] = 0x10325476; } void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) { const md5_byte_t *p = data; int left = nbytes; int offset = (pms->count[0] >> 3) & 63; md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) return; /* Update the message length. */ pms->count[1] += nbytes >> 29; pms->count[0] += nbits; if (pms->count[0] < nbits) pms->count[1]++; /* Process an initial partial block. */ if (offset) { int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); memcpy(pms->buf + offset, p, copy); if (offset + copy < 64) return; p += copy; left -= copy; md5_process(pms, pms->buf); } /* Process full blocks. */ for (; left >= 64; p += 64, left -= 64) md5_process(pms, p); /* Process a final partial block. */ if (left) memcpy(pms->buf, p, left); } void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) { static const md5_byte_t pad[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; md5_byte_t data[8]; int i; /* Save the length before padding. */ for (i = 0; i < 8; ++i) data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); /* Append the length. */ md5_append(pms, data, 8); for (i = 0; i < 16; ++i) digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); } /* ------------------------------------------------------------------------ */ /* * E-Z MD5 [MJR addition] */ void md5_ez(char *hash, const char *msg, size_t len) { md5_state_t s; md5_byte_t digest[16]; /* calculate the binary md5 of the message */ md5_init(&s); md5_append(&s, (const md5_byte_t *)msg, len); md5_finish(&s, digest); /* convert the binary hash to printable hex digits */ for (int i = 0 ; i < 16 ; ++i, hash += 2) byte_to_xdigits(hash, digest[i]); /* null-terminate the output string */ *hash = '\0'; } /* * Hash a data source [MJR addition] */ void md5_datasrc(char *hash, class CVmDataSource *src, unsigned long len) { /* initialize the digest state */ md5_state_t s; md5_init(&s); /* read from the data source */ while (len != 0) { /* get a chunk up to one buffer-full, or the total remaining */ md5_byte_t buf[1024]; size_t cur = (len > sizeof(buf) ? sizeof(buf) : (size_t)len); /* read the chunk */ if (cur != 0) cur = src->readc(buf, cur); /* if there's nothing left, we're done */ if (cur == 0) break; /* feed this chunk into the hash */ md5_append(&s, buf, cur); /* deduct this chunk from the remaining length */ len -= cur; } /* calculate the digest value */ md5_byte_t digest[16]; md5_finish(&s, digest); /* convert the binary hash to printable hex digits */ for (int i = 0 ; i < 16 ; ++i, hash += 2) byte_to_xdigits(hash, digest[i]); /* null-terminate the output string */ *hash = '\0'; } qtads-2.1.7/tads3/md5.h000066400000000000000000000075421265017072300145240ustar00rootroot00000000000000/* Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.h is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Removed support for non-ANSI compilers; removed references to Ghostscript; clarified derivation from RFC 1321; now handles byte order either statically or dynamically. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); added conditionalization for C++ compilation from Martin Purschke . 1999-05-03 lpd Original version. */ #ifndef md5_INCLUDED # define md5_INCLUDED #include /* * This package supports both compile-time and run-time determination of CPU * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is * defined as non-zero, the code will be compiled to run only on big-endian * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to * run on either big- or little-endian CPUs, but will run slightly less * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. */ typedef unsigned char md5_byte_t; /* 8-bit byte */ typedef unsigned int md5_word_t; /* 32-bit word */ /* Define the state of the MD5 Algorithm. */ typedef struct md5_state_s { md5_word_t count[2]; /* message length in bits, lsw first */ md5_word_t abcd[4]; /* digest buffer */ md5_byte_t buf[64]; /* accumulate block */ } md5_state_t; #ifdef __cplusplus extern "C" { #endif /* Initialize the algorithm. */ void md5_init(md5_state_t *pms); /* Append a string to the message. */ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); /* * E-Z MD5 - calculate the MD5 of a string, returning printable hex. The * 'hash' buffer must be at least 33 characters long. [MJR addition] */ void md5_ez(char *hash, const char *msg, size_t len); /* * DataSource MD5 - calculate the MD5 of bytes from a data source, * returning printable hex. The hash buffer must be at least 33 characters * long. [MJR addition] */ void md5_datasrc(char *hash, class CVmDataSource *src, unsigned long len); #ifdef __cplusplus } /* end extern "C" */ #endif #endif /* md5_INCLUDED */ qtads-2.1.7/tads3/osifcnet.h000066400000000000000000001474271265017072300156600ustar00rootroot00000000000000/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name osifcnet.h - TADS operating system interfaces for networking and threading Function Virtualizes OS APIs for networking and threading. The style of the interface defined here is different from the basic osifc.h function interfaces. Instead, the interfaces are defined via C++ classes. This is a more convenient way to represent the facilities in this package, because they all involve resources (network sockets, thread handles, event handles, etc) that have associated sets of operations - which maps very well onto objects and methods. We could have used C++'s abstract class mechanism to define a set of public interfaces here that are implemented by system .cpp files. However, that approach isn't very efficient, since it involves run-time virtual method dispatch that truly isn't necessary in this situation (since a given build will be for a single OS, so only that one OS's concrete classes can ever be invoked in that build). Furthermore, as we learned with HTML TADS, that's a somewhat confusing and high-overhead way to set things up for porting. So, instead, we define dummy skeletons for the classes, showing the public interfaces that must be implemented. These aren't the real definitions - we #ifdef them out so that they're not actually compiled. The real definitions must be provided by each OS implementation instead. Simply copy the skeleton from this file into your OS file, define the methods listed, and add any additional methods and/or variables that you need for your OS version. You can also add OS-specific base classes as needed. This approach gives you full flexibility in defining the concrete classes, while giving the portable code a predictable set of interfaces. Notes The network API defined here is for the TADS-in-a-browser setup. This includes both the client/server configuration, where the user interface is presented through a web browser on a client machine, but the TADS VM runs on a remote server, and the two talk across an internet connection via HTTP; and the stand-alone browser configuration, where both the browser and the TADS web server are running on the same machine. The latter is really a special case of the former: it's actually just a software bundle that launches the web server and the browser at once, and transparentl to the user, so that the game looks like a simple stand-alone interpreter app to the user. For traditional interpreter builds, the network API isn't used, so no implementation is required. Modified 04/04/10 MJRoberts - Creation */ #ifndef OSIFCNET_H #define OSIFCNET_H #include "vmglob.h" /* * Intialize the networking and threading package. This must be called * once at program startup. The OS isn't required to do anything here; * it's just a chance for the OS layer to do any required static/global * initialization. If the OS implementation uses the global thread list * (see TadsThreadList in vmnet.h), it should create the global list object * here. */ void os_net_init(class TadsNetConfig *config); /* * Clean up the networking and threading package. This must be called once * just before program termination. The OS isn't required to do anything * here; it's just a chance for the OS layer to clean up resources before * quitting. * * If the OS implementation uses the global thread list (see * CTadsThreadList in vmnet.h), it can use this opportunity to do a * wait_all() on the thread list to allow background threads to terminate * gracefully, then it should release its reference on the thread list * object. */ void os_net_cleanup(); /* * Get the local host name, if possible. This should return the EXTERNAL * network name of the local computer (i.e., it shouldn't return * "localhost", but rather the name that other machines on the network can * use to connect to this machine). Copies the host name to the buffer; * returns true if successful, false if not. */ int os_get_hostname(char *buf, size_t buflen); /* * Get the IP address for the given local host name, if possible. On * success, fills in 'buf' with the IP address in the standard decimal * format ("1.2.3.4") and returns true. On failure, returns false. * * 'host' is a host name for an adapter on the local machine. This * function resolves the external IP address for the host name, which is * the address that other machines on the network use to connect to this * host on the given adapter. Note that this should only be the external * address as far as this machine is concerned. For example, if the * machine is connected to a LAN, and the LAN contains a NAT router that's * connected to the public Internet, this routine does NOT go so far as to * resolve the external address outside the NAT router - it merely returns * the machine's local address on the LAN. * * If 'host' is null, this should return the default adapter's IP address, * if there is such a thing. On a machine with a single adapter, the * default is that single adapter; on a machine with multiple adapters, if * there's a local convention for designating which one is the default, use * the local convention, otherwise the implementation can choose the * default according to its own criteria, or simply pick one arbitrarily. */ int os_get_local_ip(char *buf, size_t buflen, const char *host); /* * Special timeout value for an infinite wait. When this is passed as a * timeout value to the wait() functions, the wait continues indefinitely, * until the desired event occurs. */ /* #define OS_FOREVER (define per OS) */ /* * Event wait results. */ #define OSWAIT_TIMEOUT -1 /* timeout expired before the event dired */ #define OSWAIT_ERROR -2 /* wait failed with a system error */ #define OSWAIT_EVENT 0 /* got the event we were waiting for */ /* for a multi-wait, OSWAIT_EVENT+n means the nth event fired */ #define OSWAIT_USER 1000 /* user-defined code can use this and above */ /* * Socket error code. This is the value returned by OS_Socket::send() and * recv() when an error occurs, in lieu of a valid length value. Define * per operating system. */ /* #define OS_SOCKET_ERROR (-1) */ /* * Error values for OS_Socket::last_error(). Define these per OS. * * OS_EWOULDBLOCK - the error code returned from accept(), send(), and * recv() when the socket is in non-blocking mode AND the call can't do any * work without blocking. This tells the caller that they should continue * with asynchronous processing for a while and poll again later. * * OS_ECONNRESET - returned from send(), recv(), and others when the * network peer (on the other side of the socket) has closed its end of the * connection. This means that further communications through the socket * are not possible, since the other side isn't paying attention any more. * * OS_ECONNABORTED - returned from send(), recv(), and others when the * local host has terminated the connection (i.e., some lower-level * software in the network stack - not the application - has decided on its * own to close the socket). This can happen in various situations, such * as when the peer fails to acknowledge a transmission from our side. * From the application's perspective, this is usually equivalent to * OS_ECONNRESET, since in either case we've lost the ability to * communicate with the client due to some kind of interruption outside of * the local machine's control. */ /* #define OS_EWOULDBLOCK EWOULDBLOCK */ /* #define OS_ECONNRESET ECONNRESET */ /* #define OS_ECONNABORTED ECONNABORTED */ /* * Protected counter. This object maintains an integer count that can be * incremented and decremented. The inc/dec operations are explicitly * atomic, meaning they're thread-safe: it's not necessary to use any * additional access serialization mechanism to perform an increment or * decrement. */ #if 0 /* skeleton definition - make a copy for the actual OS definition */ class OS_Counter { public: /* initialize with a starting count value */ OS_Counter(long c = 1); /* get the current counter value */ long get() const; /* * Increment/decrement. These return the post-update result. * * The entire operation must be atomic. In machine language terms, the * fetch and set must be locked against concurrent access by other * threads or other CPUs. */ long inc(); long dec(); }; #endif /* ------------------------------------------------------------------------ */ /* * IMPORTANT: in your OS-specific header, #include "vmrefcnt.h" here, * immediately after defining class OS_Counter. * * vmrefcnt.h defines generic classes that are used as base classes by OS * interface classes below. */ /* ------------------------------------------------------------------------ */ /* * Waitable object. This is the base class for objects that can be used in * a multi-object wait. A waitable object has two states: ready and not * ready. Waiting for the object blocks the waiting thread as long as the * object is in the not-ready state, and releases the thread (allows it to * continue running) as soon as the object enters the rady state. Waiting * for an object that's already in the ready state simply allows the * calling thread to continue immediately without blocking. */ #if 0 /* skeleton definition - make a copy for the actual OS definition */ class OS_Waitable { public: OS_Waitable(); virtual ~OS_Waitable(); /* * Wait for the object, with a maximum wait of 'timeout' milliseconds. * Blocks the calling thread until the object is in its ready state, OR * the timeout interval elapses, whichever comes first. At that point * the thread is released and this function returns. If the object is * already in the ready state when this is called, the method returns * immediately. * * If 'timeout' is OS_FOREVER, the wait is indefinite - it continues * until the event occurs, no matter how long that takes. * * Returns an OSWAIT_xxx code to indicate what happened. If the object * is already in the ready state, returns OSWAIT_EVENT immediately. A * timeout value of zero returns OSWAIT_TIMEOUT immediately (without * blocking) if the object isn't ready. */ int wait(unsigned long timeout = OS_FOREVER); /* * Test to see if the object is ready, without blocking. Returns true * if the object is ready, false if not. If the object is ready, * waiting for the object would immediately release the thread. * * Note that testing some types of objects "consumes" the ready state * and resets the object to not-ready. This is true of auto-reset * event objects. */ int test(); /* * Wait for multiple objects. This blocks until at least one object in * the list is in the ready state, at which point it returns * OSWAIT_OBJECT+N, where N is the array index in 'objs' of an object * that's ready. If the timeout (given in milliseconds) expires before * any objects are ready, we return OSWAIT_TIMEOUT. If the timeout is * omitted, we don't return until an object is ready. * * If multiple objects become ready at the same time, this picks one * and returns its index. * * If any of the objects are auto-reset event objects, the signaled * state of the *returned* object is affected the same way as it would * be for an ordinary wait(). The signaled states of any other objects * involved in the multi_wait are not affected. For example, if you * multi_wait for three auto-reset events, and all three start off in * the signaled state, multi_wait will arbitrarily choose one as the * returned event, and only that one chosen event will be reset by the * multi_wait. */ static int multi_wait(int cnt, OS_Waitable **objs, unsigned long timeout = OS_FOREVER); }; #endif /* * Event. This is a waitable object with two states: signaled and * unsignaled. In the unsignaled state, a thread waiting for the event * blocks until the event becomes signaled, at which point the thread is * released. In the signaled state, a thread waiting for the event simply * continues running without blocking. * * There are two types of event objects: auto-reset and manual-reset. * * An auto-reset event automatically reverts to the unsignaled state as * soon as ONE thread waiting for the event is released. This means that * if multiple threads are waiting for the event, signaling the event will * only release a single thread; the others will continue blocking until * the event is signaled again, at which point one more thread will be * released. An auto-reset event balances signals and waits: one signal * releases one waiting thread. If the event is signaled twice while no * threads are waiting, the next two waits will immediately be released, * and the third wait will block. * * A manual-reset event simply stays signaled once signaled, no matter how * many threads are waiting for it. The event reverts to unsignaled only * when someone manually calls reset(). */ #if 0 /* skeleton definition - make a copy for the actual OS definition */ class OS_Event: public OS_Waitable, public CVmRefCntObj { public: /* construct */ OS_Event(int manual_reset); /* * Signal the event. In the case of a manual-reset event, this * releases all threads waiting for the event, and leaves the event in * a signaled state until it's explicitly reset. For an auto-reset * event, this releases only one thread waiting for the event and then * automatically resets the event. */ void signal(); /* * Reset the event. For a manual-reset event, this returns the event * to the unsignaled state. This has no effect for an auto-reset * event. */ void reset(); }; #endif /* * Mutex - mutual exclusion object. This is used to protect shared * resources from concurrent access by multiple threads. * * You create a mutex to protect a designated set of shared resources; a * typical example is that you create a mutex for each instance of a class, * to protect the member variables of each instance. When a thread is * about to start a series of operation on the shared variables, and that * series of operations is required to be atomic, the thread first locks * the mutex associated with the variables (e.g., if you're about to make * some changes to an object's member variables, you lock the object's * mutex first). Only one thread can hold a mutex lock at a time, so once * you have the mutex lock, you know that any other thread attempting the * same operation would be blocked on its own lock attempt as long as * you're holding the lock, ensuring that no other thread will attempt to * modify the same variables. When you're done with the atomic operation, * you release the mutex to allow other threads to access the variables * again. * * The OS implementation for mutexes should be as lightweight as possible. * It's more efficient in terms of thread concurrency if callers can create * very granular mutexes, to protect specific resources. This means that * callers will want to create many mutexes in a typical system, so the * cost of creating lots of mutexes shouldn't outweigh the efficiency gain * of using lots of them. * * Care should be taken to avoid deadlocks when locking more than one mutex * at a time. The simplest technique for avoiding deadlocks is to use a * fixed order for acquiring any given set of mutexes. For example, if you * have two mutexes A and B, make sure all threads that acquire both will * always acquire A first, then B. This ensures that a thread that * successfully acquires A and then wants to acquire B will not deadlock * with another thread that already locked B and wants to acquire A. Since * the other thread will also always try to lock A before B, the first * thread can be assured that once it has A, no one else who wants both A * and B can already have B, since anyone else wanting both would * necessarily already have A, which the first thread knows can't be the * case because it acquired A. */ #if 0 /* skeleton definition - make a copy for the actual OS definition */ class OS_Mutex: public CVmRefCntObj { public: OS_Mutex(); ~OS_Mutex(); /* * Lock the mutex. Blocks until the mutex is available. */ void lock(); /* * Test the mutex: if the mutex is available, returns true; if not, * returns false. Doesn't block in either case. If the return value * is true, the mutex is acquired as though lock() had been called, so * the caller must use release() when done. */ int test(); /* * Unlock the mutex. This can only be used after a successful lock() * or test(). This releases our lock and makes the mutex available to * other threads. */ void unlock(); }; #endif /* * "Core" socket. This is the common base class of data sockets and * listener sockets. * * In the standard Unix socket API, there's really just one kind of socket * object, and its usage as a data or listener socket depends on the way * it's set up. In OO terms, though, the data and listener sockets are * really separate subclasses, with a small part of their interfaces in * common. We've extracted the common parts into this base class. * * Our socket classes in aggregate are just a thin layer implementing the * same model as the Unix socket API. Most modern operating systems have * socket libraries modeled closely on the Unix socket API, so this should * be easily implementable on most systems. We can't use the Unix socket * API directly, because other systems do tend to have idiosyncracies, * despite their overall similarity to the Unix API. Windows is a * particularly good (?) example: a lot of the basic structure of the API * is the same as in Unix, but with a bunch of gratuitous name changes; and * some of the more advanced features, such as non-blocking mode, are * handled rather differently because of deeper differences in the OS. */ #if 0 /* skeleton definition - make a copy for the actual OS definition */ class OS_CoreSocket: public CVmRefCntObj, public OS_Waitable { public: OS_CoreSocket(); /* deleting the object closes the underlying system socket */ ~OS_CoreSocket() { close(); } /* * Close the socket. This releases any system resources associated * with the socket, including any network ports it's using. */ void close(); /* * Set the socket to non-blocking mode. * * In non-blocking mode, any network operation on the socket will * return immediately with OS_SOCKET_ERROR if the operation can't be * completed immediately, and thus would block pending the arrival of * data or completion of a transmission. Specifically: * * - reading a data socket (recv(), in the Unix API) would block if * there are zero bytes available to be read in the inbound buffer * * - writing a data socket (send() in Unix) would block if the outbound * buffer is too full to accommodate all of the bytes being sent * * - listening for a new incoming connection request (accept() in Unix) * would block if there are no pending connection requests * * If one of these functions returns OS_SOCKET_ERROR, the caller should * check the specific error code by calling last_error(). This will * return OS_EWOULDBLOCK when the cause of the error is one of the * above conditions that would normally cause the call to block. * * After an OS_EWOULDBLOCK occurs, the caller can do one of two things. * First, it can simply carry on with other operations; since the * thread isn't blocked waiting for the network operation to complete, * it can do whatever else it wants in the meantime. The thread can * "poll" the socket by attempting the operation again from time to * time to see if it's finally ready. Second, if the thread doesn't * have any other business to transact, it can wait for the socket to * become ready for the desired operation using the socket's * OS_Waitable interface. That blocks the thread the same way that the * same network operation functions would in the default blocking mode, * but provides two extra capabilities that the regular network * functions don't: you can set a timeout so that the thread will only * block for a certain maximum interval, and you can use the multi-wait * interface so that other events of interest will also wake up the * thread if they should occur before the socket is ready. * * Note that the default mode when you create a new socket is blocking * mode. In blocking mode, any of the above blocking conditions do not * cause an error: instead, they simply block the thread until the * network operation completes. */ void set_non_blocking(); /* * In non-blocking mode, the caller must manually reset the socket's * event waiting status after each successful wait. */ void reset_event(); /* * Get the last error for a network operation (for a data socket, send * and receive; for a listener socket, accept). Returns an OS_Exxx * value. */ int last_error() const; /* * Get my port number. For a listener socket, this is the port on * which we're listening; for a regular data socket, this is the port * number on our side (the local side) of the connection. Returns 0 if * the port hasn't been opened yet. */ unsigned short get_port_num(); /* * Get this socket's local IP address. This returns the IP address of * the network adapter on our side (the local side) of the connection. * For a listening socket, this is the IP address that clients use to * initiate a connection to this server. * * The returned string is allocated. The caller must free the string * when done with it, using lib_free_str(). */ char *get_ip_addr() const { return lib_copy_str(local_ip); } /* * Get the IP address for the given host. The return value is an * allocated buffer that the caller must delete with 'delete[]'. If * the host IP address is unavailable, returns null. * * A null or empty hostname gets the default external IP address for * the local machine. This does NOT return "localhost" or "127.0.0.1", * but rather the external IP that other machines on the same local * area network can use to connect to this machine. Note that this * address is valid on our LAN, but not necessarily on the broader * Internet, since we could be behind a NAT firewall that assigns local * IP addresses within the LAN. */ static char *get_host_ip(const char *hostname); }; #endif /* * Data socket. This is a subclass of the core socket that's used for * basic data transmission. A data socket is a full-duplex communication * channel between this process and another process, which might be running * on the same host or on a remote host connected via a network. * * On the server side, sockets are created by OS_Listener::accept(). * * Currently, there is no interface for creating a client-side socket. * Instead, we provide a request-level API for initiating HTTP requests * from the client side (see class OS_HttpRequest). */ #if 0 /* skeleton definition - make a copy for the actual OS definition */ class OS_Socket: public OS_CoreSocket { public: /* * Construction. Note that callers do not generally create socket * objects directly; instead, these objects are created by other * osifcnet system classes and returned to user code to access. For * example, server sockets are created by OS_Listener::accept(). */ OS_Socket(); /* * Send bytes. Returns the number of bytes sent, or OS_SOCKET_ERROR if * an error occurs. * * When the socket is in non-blocking mode, and the buffer is too full * to accept the complete write, this writes nothing and returns * immediately with an error; last_error() will return OS_EWOULDBLOCK. */ int send(const char *buf, size_t len); /* * Receive bytes. Returns the number of bytes received. If the return * value is 0, it means that the peer has closed its end of the socket * and all buffered data have already been received. If the return * value is negative, it indicates an error; the specific error code * can be obtained from last_error(). * * This doesn't necessarily fulfill the complete read request. If * there are fewer bytes immediately available than 'len', this reads * all of the bytes immediately available and returns, indicating the * number of bytes actually transfered. This is the case regardless of * the blocking mode of the socket. * * When the socket is in non-blocking mode, and there are no bytes * available, this returns immediately with an error, and last_error() * will return OS_EWOULDBLOCK. */ int recv(char *buf, size_t len); /* * Get the IP address and port for our side of the connection. 'ip' * receives an allocated string with the IP address string in decimal * notation ("192.168.115"). We fill in 'port' with our port number. * Returns true on success, false on failure. If we return success, * the caller is responsible for freeing the 'ip' string with * lib_free_str(). */ int get_local_addr(char *&ip, int &port); /* * Get the IP address and port for the network peer to which we're * connected. 'ip' receives an allocated string with the IP address * string in decimal notation ("192.168.1.15"). We fill in 'port' with * the port number on the remote host. Returns true on success, false * on failure. If we return success, the caller is responsible for * freeing the allocated 'ip' string via lib_free_str(). */ int get_peer_addr(char *&ip, int &port); }; #endif /* * Listener. This class represents a network listener socket, which is * used to wait for and accept new incoming connections from clients. */ #if 0 /* skeleton definition - make a copy for the actual OS definition */ class OS_Listener: public OS_CoreSocket { public: /* * Construction. This sets up the object, but doesn't open a listener * port. Call open() to actually open the port. */ OS_Listener(); /* * Open the listener on the given port number. This can be used to * create a server on a well-known port, but it has the drawback that * ports can't be shared, so this will fail if another process is * already using the same port number. * * Returns true on success, false on failure. */ int open(const char *hostname, unsigned short port_num); /* * Open the listener port, with the port number assigned by the system. * The system will select an available port and assign it to this * listener, so this avoids contention for specific port numbers. * However, it requires some separate means of communicating the port * number to clients, since there's no way for them to know the port * number in advance. * * Returns true on success, false on failure. */ int open(const char *hostname); /* * Accept the next incoming connection. If the listener is in blocking * mode (the default), and there are no pending requests, this blocks * until the next pending request arrives. In non-blocking mode, this * returns immediately with a null socket if no requests are pending, * and last_error() indicates OS_EWOULDBLOCK. */ OS_Socket *accept(); }; #endif /* * Thread. * * To define the code to execute for a thread, you subclass this class and * define a concrete implementation for thread_main(). The thread * automatically terminates when thread_main() returns. * * When used as a Waitable object, a thread is in in the "ready" state when * the thread's thread_main() has returned. In other words, waiting for a * thread means that you're waiting for the thread to complete. */ #if 0 /* skeleton definition - make a copy for the actual OS definition */ class OS_Thread: public CVmWeakRefable, public OS_Waitable { /* the TadsThreadList class must be a friend, if we use it */ friend class TadsThreadList; public: /* * Construction. This doesn't actually launch the thread; that can't * happen in the base class constructor because the thread entrypoint * might be reached before the construction is completed. Instead, * call launch() to start the system-level thread running. * * If the OS uses the TadsThreadList master list of threads, each new * thread should be registered here through the ThreadList global. * This is optional; it's for the OS layer's benefit, so if the OS * layer doesn't need it, you can omit it. */ OS_Thread() { TadsThreadList::add(this); } /* * Destruction. * * If the OS uses the ThreadList master list of threads, you should * clean up the master thread list here. The thread list keeps a weak * reference to the thread, and the weak reference is automatically * cleared when we're deleted, so the thread list simply needs to scan * for dead weak refs and clean them up. */ ~OS_Thread() { TadsThreadList::clean(); } /* * Launch the system thread, and set it up to start running in * thread_main(). Returns true on success, false if the thread * couldn't be started. * * When this returns, the thread may or may not have actually started * executing thread_main(). On most operating systems it's up to the * OS scheduler to decide when to give CPU time to the thread, so the * thread's execution status depends on the OS and on current CPU * conditions in the system. If the caller needs to know that the * thread has started executing, or that the thread has reached some * particular point in its execution, it can easily coordinate this by * creating an event object that it shares with the thread: the thread * signals the event when the desired milestone has been reached, and * the caller waits for the event after creating the thread, ensuring * that the caller won't proceed until the thread has complete the * desired startup steps. */ int launch(); /* * Thread entrypoint routine. Callers must subclass this class and * provide a concrete definition for this method. This method is * called at thread entry. */ virtual void thread_main() = 0; }; #endif /* * HTTP client operations */ class OS_HttpClient { public: /* * Perform an HTTP request on a given resource, returning the resource * contents. * * 'opts' is a combination of OptXxx option flags (see below). * * 'verb' is the HTTP verb to perform; this is usually GET, HEAD, POST, * or PUT. 'resource' is the full URL to the resource to be retrieved. * 'portno' is the port number (usually 80). 'ua' is an optional User * Agent string; if this is null, we'll use a default UA string * identifying ourselves as the generic TADS HTTP client. * * 'send_headers' is an optional string of custom headers to send with * the request. The headers are appended to the headers that we * automatically generate. The string must be in the standard "Name: * Value" format, with headers separated by CR-LF sequences. The * overall string can end wtih one or more CR-LF sequences but isn't * required to. Pass null if no custom headers are to be sent. * 'send_headers_len' is the length in bytes of this string. * * If 'payload' is non-null, it contains the payload data to send with * the request. This is for use with POST and PUT to specify the data * to send with the request. For a POST, the payload can be a mix of * form fields and file items. If there are any file items, we'll send * the POST data in the multipart/form-data format; otherwise we'll * send as application/x-www-form-urlencoded. For any verb other than * POST, the payload (if present at all) is limited to a single file * item. * * Special exception: for POST, if the payload consists of a single * payload item with an empty string ("") as the name, we'll assume * that the caller did all of the necessary POST encoding already, and * we'll send that payload item as the entire content body of the * request WITHOUT any further encoding. This allows the caller to * hand-code an x-www-form-urlencoded or multipart/form-data body, or * to use an entirely different encoding for the body. In other words, * we treat a POST payload with a single unnamed payload item exactly * as we treat a payload for PUT or any other non-POST verb. * * Returns the HTTP status code from the server, or an ErrXxx code * defined below if an error occurred on the client side. The ErrXxx * codes are all negative, which distinguishes them from HTTP status * codes. * * If the server sends back a reply body, we'll store the contents of * the reply in the 'reply' object. If provided, this must be a * writable stream object. 'reply' can alternatively be null, in which * case we'll simply discard any reply body. If the request doesn't * return any reply body, but 'reply' is non-null, we'll simply leave * 'reply' unchanged. Note that we don't rewind or truncate the * 'reply' stream object, so the caller has to prepare the object in * the desired initial conditions before calling this method. * * 'headers' is optional. If this is non-null, we'll allocate a buffer * for the headers and store the pointer to the buffer in '*headers'. * This buffer will contain the raw CR-LF delimited header lines from * the reply, with a null terminator. The caller must delete this * memory with delete[]. The header buffer will be returned only if * the request makes it far enough to retrieve headers; if the call * fails at the socket connection level, for example, no headers are * returned. * * 'location' is optional. If this is non-null, AND the request result * is a 301 code ("resource moved"), we'll allocate a copy of the * redirected URL from the HTTP reply and hand it back via *location. * The caller is responsible for freeing this buffer when done with it * via lib_free_str(). * * If 'location' is null, and the resource has moved, we'll * automatically follow the redirection link and retrieve the resource * at the redirected location. Any redirection is thus transparent to * the caller when 'location' is null. */ static int request(int opts, const char *host, unsigned short portno, const char *verb, const char *resource, const char *send_headers, size_t send_headers_len, class OS_HttpPayload *payload, class CVmDataSource *reply, char **reply_headers, char **location, const char *ua); /* option flags for request() */ static const int OptHTTPS = 0x0001; /* use https scheme */ /* non-HTTP status codes for request() */ static const int ErrNoMem = -1; /* out of memory */ static const int ErrNoConn = -2; /* couldn't connect to host */ static const int ErrNet = -3; /* other network/socket error */ static const int ErrParams = -4; /* invalid parameters */ static const int ErrReadFile = -5; /* can't read payload file contents */ static const int ErrWriteFile = -6; /* error writing reply stream */ static const int ErrGetHdrs = -7; /* error retrieving reply headers */ static const int ErrThread = -8; /* error starting thread */ static const int ErrOther = -100; /* other error */ }; /* HTTP request payload item */ class OS_HttpPayloadItem { public: /* create as a name/value pair for a form field for a POST */ OS_HttpPayloadItem(const char *name, const char *val); OS_HttpPayloadItem(const char *name, size_t name_len, const char *val, size_t val_len); /* * Create as a file upload for PUT or POST. 'filename' is the nominal * name for the upload - it doesn't have to correspond to an actual * file system object. The actual payload contents will be taken from * the stream, NOT from a disk file. * * Since 'filename' is only used on the server side to identify the * upload, it's normally a root name only, stripped of any path prefix. * A local path on the client won't mean anything to the server, so * there's no value in sending it, and some people consider doing so a * security risk in that it unnecessarily exposes information about the * local system's directory structure across the network. */ OS_HttpPayloadItem(const char *name, const char *filename, const char *mime_type, class CVmDataSource *contents); OS_HttpPayloadItem(const char *name, size_t name_len, const char *filename, size_t filename_len, const char *mime_type, size_t mime_type_len, class CVmDataSource *contents); /* delete */ ~OS_HttpPayloadItem(); void init(); /* field name, for a POST form field or POST form file upload */ char *name; /* * Field value, for a simple POST form field; for a file, this is the * nominal filename to send to the server. */ char *val; /* data stream for a PUT file or a POST form file upload */ class CVmDataSource *stream; /* MIME type, for a PUT file or a POST form file upload */ char *mime_type; /* next item in the list */ OS_HttpPayloadItem *nxt; }; /* * HTTP request payload data. Use this to package the uploaded data for an * HTTP PUT or POST via OS_HttpClient::request(). */ class OS_HttpPayload { public: OS_HttpPayload(); ~OS_HttpPayload(); /* add a simple name/value form field */ void add(const char *name, const char *val); /* add a simple name/value field, in tads-string (VMB_LEN prefix) format */ void add_tstr(const char *name, const char *val); /* add a simple file upload */ void add(const char *name, const char *filename, const char *mime_type, class CVmDataSource *contents); void add(const char *name, size_t name_len, const char *filename, size_t filename_len, const char *mime_type, size_t mime_type_len, class CVmDataSource *contents); /* add an item */ void add(OS_HttpPayloadItem *item); /* get the number of payload items */ int count_items() const; /* * Is this a multipart payload? This returns true if any of the fields * contain file uploads, false if they're all simple form fields. A * POST consisting entirely of simple form fields is normally sent with * MIME type application/x-www-form-urlencoded, whereas a POST * containing uploaded file data requires multipart/formdata. */ int is_multipart() const; /* * Create an application/x-www-form-urlencoded buffer from the payload. * This is only applicable when there are no file upload fields. The * returned string is an allocated buffer that the caller is * responsible for freeing when done with it, via t3free(). */ char *urlencode(size_t &len) const; /* get the Nth item */ OS_HttpPayloadItem *get(int n) const; protected: /* list of payload items */ OS_HttpPayloadItem *first_item, *last_item; }; /* ------------------------------------------------------------------------ */ /* * Forward declarations */ class TadsThreadList; /* ------------------------------------------------------------------------ */ /* * Include the appropriate system-specific header */ #if defined(_WIN32) #include "osnetwin.h" #elif defined(UNIX) || defined(FROBTADS) #include "osnetunix.h" #else #include "osnetdum.h" #endif /* ------------------------------------------------------------------------ */ /* * Connect to the Web UI. The game calls this after it starts its HTTP * server through which it will present its Web UI. This function's job is * to get the client connected to the new HTTP server - in other words, to * navigate the client's browser to the game's HTTP server and load the * game's start page. * * 'message_queue' is an optional message queue to receive notifications * about events in the UI after it's connected. If this is null, no * notifications are sent. If provided, the UI will send the following * event messages to the queue as appropriate: * * - For a stand-alone local configuration (where the browser UI is an * integrated part of the interpreter application - see below), a * TadsUICloseEvent is posted when the user manually closes the UI window. * For the local configuration, the game will usually want to consider this * to be an "end of file" on the input, causing the game to terminate. * * 'addr' is the network address of the server (as a decimal IP string, * e.g. "192.168.1.25", or as a DNS host name), and 'port' is the port * number on which the server is listening. The address will be sent to * the client, and the client will use it to connect to the game, so the * address must be in a form that the client can use to reach our server. * For a client within the same LAN, the local IP address will work. This * won't work in cases where we're behind a NAT firewall and the client is * on the other side of the firewall, though: in those cases, you have to * be sure to use the external Internet IP address. In most cases this * isn't something the game will have to worry about, since the game * generally just takes its listener binding address from the Web server * that launched it - so assuming the Web server is configured correctly, * the game will usually have the right address. * * 'path' is the path to the starting UI page; this is an absolute path on * the server, such as "/webui.htm". 'errmsg' is a pointer to a pointer to * be filled in with an allocated error message string if the operation * fails. 'errmsg' must be set to a string allocated with lib_alloc_str() * or lib_copy_str(); the caller is responsible for freeing this string * with lib_free_str() when done with it. The function must set '*errmsg' * to null if not used. Return true on success, false on failure. * * This function could conceivably be implemented any number of ways, but * we foresee two main implementation strategies, for our two main expected * configurations. * * CONFIGURATION 1: STAND-ALONE PLAY * * Stand-alone play is where the whole game, client and server, runs on a * single PC and looks to the user like a single application. The fact * that there's a network server running is transparent to the user; they * see what looks like the traditional command-line or HTML TADS setup, * where there's just a "t3run " command that opens a game window. * * In this configuration, connecting to the Web UI is easy. Since both * client and server are running under one roof, we have control over the * client UI. We simply need to open a Web browser window and navigate it * to the game server's start page. * * Opening the browser window could consist of actually launching a * separate Web browser application and passing in the starting URL. This * is the simplest approach. For a more integrated appearance, the * application could instead launch its own custom frame window with an * embedded HTML browser widget filling the interior of the window (the * "client area" in Windows parlance). In either case, the job is the * same: launch a browser and point it to the game's HTTP server. * * CONFIGURATION 2: CLIENT/SERVER WEB PLAY * * Starting a Web-based TADS game presents us with an interesting problem. * The client initiates the game by connecting to a Web server where the * game is hosted, and navigating to a "launch" page. The launch page is * actually a server-side program that starts the TADS 3 Interpreter in a * child process, passing the .t3 file name to the interpreter. The * interpreter loads the game and starts executing it. The game then sets * up an HTTP server which it will use to present its Web UI. * * That's where our "interesting problem" comes up. We have a client who * knows how to talk to the "launch" page, and separately we have the * game's HTTP server on an ad hoc port assigned by the operating system. * (For details on why this is an ad hoc port rather than a well-known * port, see the discussion of Network Service Registration below.) The * problem is that we now need to connect the client to the server. * * The way we solve this is to use that "launch" page as the conduit. When * the launch page loads, it doesn't immediately send anything back to the * client. Instead, it stays momentarily silent while it launches the TADS * Interpreter and lets the game start up. When the launch page starts the * interpreter, it establishes an interprocess communication channel of * some kind between itself and the interpreter - for example, a Unix pipe * would work nicely. When the game calls vmnet_connect_webui(), we use * this IPC to send our IP address and port number information back to the * launch page. When the launch page receives this information, it finally * replies to the client. But rather than sending back just any old HTML, * it sends back a REDIRECT reply, pointing the client to the game server * start page. When the client receives the redirect, it turns around and * loads the start page from the game server. * * --- * * The implementation of this function is specific to both the * CONFIGURATION and the SYSTEM. For example, there might be two separate * Unix implementations: one for stand-alone play that opens an integrated * frame window containing an embedded browser widget; and the other for * Web server use that writes the information to stdout for the "launch" * page script to read. * * --- * * This routine should reuse the same UI display resources if repeated * calls are made. For example, in the stand-alone configuration, if we've * already opened a local browser window in a previous call, and that * window is still open, we should simply navigate that existing window to * the new location rather than opening a second window. * * The caller should invoke osnet_disconnect_webui() before terminating the * program. However, calls to osnet_connect_webui() are NOT required to be * matched by an equal number of calls to the disconnect function. It's * sufficient to call disconnect once after calling connect several times. */ int osnet_connect_webui(VMG_ const char *addr, int port, const char *path, char **errmsg); /* * Disconnect from the Web UI. This is used at program termination to * notify the Web UI that the server program is no longer running, to allow * it to update or remove its UI, and to release system resources. * * If there's no Web UI currently active, this routine should simply return * without doing anything. It must be harmless to call this routine with * no previous calls to osnet_connect_webui(). * * For a stand-alone configuration, this should disconnect from the Web * UI's window. If the Web UI is running as a separate process, this * should clean up any IPC channel between the processes, and should update * the UI to indicate that the game has ended. For example, the UI window * could show a message to this effect in its title bar or status line, or * could add a message to the text display. * * For a client/server web-play configuration, this usually doesn't do * anything on the UI side, since the UI is a separate browser (on a * separate machine) that the user launched and which we're not responsible * for closing. However, this routine can still take the opportunity to * free up any local resources allocated is osnet_connect_webui(). * * If 'close' is true, the web UI should be visually removed. If it's * running in a separate process, the process can be terminated. If * 'close' is false, a separate process can be left running for the user to * close manually. Explicitly closing the UI is appropriate for debuggers * and other "container" environments where it's reasonable to expect that * closing the container environment would close the UI window as well. * Leaving the UI open is appropriate for a stand-alone intepreter in a GUI * environment; even though the game engine has terminated, we usually want * to leave the final state of the UI displayed until the user manually * closes it. This gives the user a chance to read any final messages the * game displays before terminating. */ void osnet_disconnect_webui(int close); /* * Run the file selector dialog in the *local standalone mode* Web UI. * * This is a substitute for the regular os_askfile() routine that the * interpreter calls when we're running in the local standalone mode. This * mode is a hybrid of the Web UI and the local interpreter console UI: it * uses a web browser as the UI, but it uses the local file system for * storage. The file selection dialog is at the intersection of these two * axes: the file dialog is a UI element, and our primary UI is the * browser, but in this configuration the dialog accesses the local file * system and should present the same UI as it would for the a local * application. That's why we need a custom function to handle this. * * Generally, the implementation should invoke a standard local file * selector dialog, but show it within the UI context of the Web UI window. * In the Windows implementation, for example, we run the Web UI in a child * process that's a customized browser window, so we handle this function * by sending a message to the child UI process asking it to display a file * dialog on our behalf. The child process then sends us back a reply * containing the result of the dialog. * * This can be stubbed out with an empty implementation on platforms that * don't have a standalone Web UI mode. */ int osnet_askfile(const char *prompt, char *fname_buf, int fname_buf_len, int prompt_type, int file_type); #endif /* OSIFCNET_H */ qtads-2.1.7/tads3/osnetdum.h000066400000000000000000000016031265017072300156650ustar00rootroot00000000000000/* $Header$ */ /* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name osnetdum.h - dummy implementation of TADS networking for non-supporting platforms Function This module provides a stub implementation of the portions of the portable networking interface that are required to link the rest of the system. This should be used on platforms that don't provide TADS networking functionality. Notes Modified 08/11/10 MJRoberts - Creation */ #ifndef OSNETDUM_H #define OSNETDUM_H class OS_Counter { public: OS_Counter(long c = 1) { cnt = c; } long get() const { return cnt; } long inc() { return ++cnt; } long dec() { return --cnt; } private: long cnt; }; #include "vmrefcnt.h" class OS_Event: public CVmRefCntObj { public: OS_Event(int /*manual_reset*/) { } void signal() { } void reset() { } }; #endif /* OSNETDUM_H */ qtads-2.1.7/tads3/resfind.cpp000066400000000000000000000333461265017072300160250ustar00rootroot00000000000000/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name resfind.cpp - find a multimedia resource in a tads 2 or tads 3 game file Function Searches a compiled tads 2 game file or a compiled tads 3 image file for a multimedia resource of a given name. The caller doesn't have to know which tads version created the file; we'll sense the file type and parse it accordingly. This implementation is independent of any tads 2 or tads 3 subsystem except osifc, which it uses for portable file I/O and portable byte format conversions. Notes Modified 09/24/01 MJRoberts - Creation */ #include #include #include #include "t3std.h" #include "resfind.h" /* ------------------------------------------------------------------------ */ /* * tads 2 file signature */ #define T2_SIG "TADS2 bin\012\015\032" /* * tads 3 file signature */ #define T3_SIG "T3-image\015\012\032" /* ------------------------------------------------------------------------ */ /* * Find a resource in a tads 2 game file */ static int t2_find_res(osfildef *fp, const char *resname, tads_resinfo *info) { char buf[300]; unsigned long startpos; size_t resname_len; int found; /* we haven't found what we're looking for yet */ found = FALSE; /* note the length of the name we're seeking */ resname_len = strlen(resname); /* * note the seek location of the start of the tads 2 game file stream * within the file - if the game file is embedded in a larger file * stream, the seek locations we find within the file are relative to * this starting location */ startpos = osfpos(fp); /* * skip past the tads 2 file header (13 bytes for the signature, 7 * bytes for the version header, 2 bytes for the flags, 26 bytes for * the timestamp) */ osfseek(fp, 13 + 7 + 2 + 26, OSFSK_CUR); /* * scan the sections in the file; stop on $EOF, and skip everything * else but HTMLRES, which is the section type that */ for (;;) { unsigned long endofs; /* read the section type and next-section pointer */ if (osfrb(fp, buf, 1) || osfrb(fp, buf + 1, (int)((unsigned char)buf[0] + 4))) { /* failed to read it - give up */ return FALSE; } /* note the ending position of this section */ endofs = t3rp4u(buf + 1 + (unsigned char)buf[0]); /* check the type */ if (buf[0] == 7 && memcmp(buf+1, "HTMLRES", 7) == 0) { unsigned long entry_cnt; unsigned long i; /* * It's a multimedia resource block. Read the index table * header (which contains the number of entries and a reserved * uint32). */ if (osfrb(fp, buf, 8)) return FALSE; /* get the number of entries from the header */ entry_cnt = t3rp4u(buf); /* read the entries */ for (i = 0 ; i < entry_cnt ; ++i) { unsigned long res_ofs; unsigned long res_siz; unsigned short name_len; /* read the entry header */ if (osfrb(fp, buf, 10)) return FALSE; /* parse the header */ res_ofs = t3rp4u(buf); res_siz = t3rp4u(buf + 4); name_len = osrp2(buf + 8); /* read the entry's name */ if (name_len > sizeof(buf) || osfrb(fp, buf, name_len)) return FALSE; /* * if it matches the name we're looking for, note that we * found it */ if (name_len == resname_len && memicmp(resname, buf, name_len) == 0) { /* * note that we found it, and note its resource size * and offset in the return structure - but keep * scanning the rest of the directory, since we need * to know where the directory ends to know where the * actual resources begin */ found = TRUE; info->seek_pos = res_ofs; info->siz = res_siz; } } /* * if we found our resource, the current seek position is the * base of the offset we found in the directory; so fix up the * offset to give the actual file location */ if (found) { /* fix up the offset with the actual file location */ info->seek_pos += osfpos(fp); /* tell the caller we found it */ return TRUE; } /* we didn't find it - seek to the end of this section */ osfseek(fp, endofs + startpos, OSFSK_SET); } else if (buf[0] == 4 && memcmp(buf+1, "$EOF", 4) == 0) { /* * that's the end of the file - we've finished without finding * the resource, so return failure */ return FALSE; } else { /* * this isn't a section we're interested in - skip to the end * of the section and keep going */ osfseek(fp, endofs + startpos, OSFSK_SET); } } } /* ------------------------------------------------------------------------ */ /* * Find a resource in a T3 image file */ static int t3_find_res(osfildef *fp, const char *resname, tads_resinfo *info) { char buf[256]; size_t resname_len; /* note the length of the name we're seeking */ resname_len = strlen(resname); /* * skip the file header - 11 bytes for the signature, 2 bytes for the * format version, 32 reserved bytes, and 24 bytes for the timestamp */ osfseek(fp, 11 + 2 + 32 + 24, OSFSK_CUR); /* scan the data blocks */ for (;;) { unsigned long siz; /* read the block header */ if (osfrb(fp, buf, 10)) return FALSE; /* get the block size */ siz = t3rp4u(buf + 4); /* check the type */ if (memcmp(buf, "MRES", 4) == 0) { unsigned long base_ofs; unsigned int entry_cnt; unsigned int i; /* * remember the current seek position - the data seek location * for each index entry is given as an offset from this * location */ base_ofs = osfpos(fp); /* read the number of entries */ if (osfrb(fp, buf, 2)) return FALSE; /* parse the entry count */ entry_cnt = osrp2(buf); /* read the entries */ for (i = 0 ; i < entry_cnt ; ++i) { unsigned long entry_ofs; unsigned long entry_siz; unsigned int entry_name_len; char *p; size_t rem; /* read this index entry's header */ if (osfrb(fp, buf, 9)) return FALSE; /* parse the header */ entry_ofs = t3rp4u(buf); entry_siz = t3rp4u(buf + 4); entry_name_len = (unsigned char)buf[8]; /* read the entry's name */ if (osfrb(fp, buf, entry_name_len)) return FALSE; /* XOR the bytes of the name with 0xFF */ for (p = buf, rem = entry_name_len ; rem != 0 ; ++p, --rem) *p ^= 0xFF; /* if this is the one we're looking for, return it */ if (entry_name_len == resname_len && memicmp(resname, buf, resname_len) == 0) { /* * fill in the return information - note that the * entry offset given in the header is an offset from * data block's starting location, so fix this up to * an absolute seek location for the return value */ info->seek_pos = base_ofs + entry_ofs; info->siz = entry_siz; /* return success */ return TRUE; } } } else if (memcmp(buf, "EOF ", 4) == 0) { /* * end of file - we've finished without finding the resource, * so return failure */ return FALSE; } else { /* * we don't care about anything else - just skip this block * and keep going; to skip the block, simply seek ahead by the * size of the block's contents as given in the block header */ osfseek(fp, siz, OSFSK_CUR); } } } /* ------------------------------------------------------------------------ */ /* * Find a multimedia resource with the given name in the given file. The * file must be positioned at the start of the tads game file when we're * invoked - this allows searching for a resource within a game file that * is embedded in a larger file stream, since we don't care where within * the osfildef stream the tads game file starts. * * Fills in the resource information structure with the seek offset and * size of the resource in the file and returns true if the resource is * found; returns false if a resource with the given name doesn't exist in * the file. */ int tads_find_resource_fp(osfildef *fp, const char *resname, tads_resinfo *info) { char buf[12]; /* read the signature */ if (!osfrb(fp, buf, 12)) { /* seek back to the start of the header */ osfseek(fp, -12, OSFSK_CUR); /* check which signature we have */ if (memcmp(buf, T2_SIG, strlen(T2_SIG)) == 0) { /* it's a tads 2 game file - read it accordingly */ return t2_find_res(fp, resname, info); } else if (memcmp(buf, T3_SIG, strlen(T3_SIG)) == 0) { /* it's a t3 image file - read it accordingly */ return t3_find_res(fp, resname, info); } } /* * if we get here, it means either that we couldn't read the * signature, or that we didn't recognize the signature - in either * case, we can't parse the file at all, so we can't find the resource */ return FALSE; } /* * Find a resource in a file, given the filename. * * Fills in the resource information structure with the seek offset and * size of the resource in the file and returns true if the resource is * found; returns false if a resource with the given name doesn't exist in * the file. */ int tads_find_resource(const char *fname, const char *resname, tads_resinfo *info) { osfildef *fp; int found; /* open the file */ if ((fp = osfoprb(fname, OSFTGAME)) == 0 && (fp = osfoprb(fname, OSFTT3IMG)) == 0) { /* we couldn't open the file, so there's no resource to be found */ return FALSE; } /* find the resource in the file */ found = tads_find_resource_fp(fp, resname, info); /* we're done with the file - close it */ osfcls(fp); /* return our found or not-found indication */ return found; } /* ------------------------------------------------------------------------ */ /* * Testing main - looks for a given resource in a given file, and copies * it to standard output if found. */ #ifdef TEST int main(int argc, char **argv) { osfildef *fp; const char *fname; const char *resname; tads_resinfo res_info; unsigned long rem; /* check usage */ if (argc != 3) { fprintf(stderr, "usage: resfind \n"); exit(2); } /* get the arguments */ fname = argv[1]; resname = argv[2]; /* open the file */ if ((fp = osfoprb(fname, OSFTGAME)) == 0 && (fp = osfoprb(fname, OSFTT3IMG)) == 0) { fprintf(stderr, "unable to open file \"%s\"\n", fname); exit(2); } /* find the resource */ if (!tads_find_resource_fp(fp, resname, &res_info)) { fprintf(stderr, "unable to find resource \"%s\"\n", resname); osfcls(fp); exit(1); } /* seek to the resource */ osfseek(fp, res_info.seek_pos, OSFSK_SET); /* copy the resource to standard output */ for (rem = res_info.siz ; rem != 0 ; ) { char buf[1024]; size_t cur; /* * read up to the buffer size or up to the resource size * remaining, whichever is smaller */ cur = sizeof(buf); if (cur > rem) cur = (size_t)rem; /* read the chunk */ if (osfrb(fp, buf, cur)) { fprintf(stderr, "error reading %u bytes from file\n", (unsigned)cur); osfcls(fp); exit(2); } /* copy the chunk to standard output */ fwrite(buf, cur, 1, stdout); /* deduct the amount we just read from the size remaining */ rem -= cur; } /* done with the file - close it */ osfcls(fp); /* success */ exit(0); return 0; } #endif /* TEST */ qtads-2.1.7/tads3/resfind.h000066400000000000000000000042531265017072300154650ustar00rootroot00000000000000/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name resfind.cpp - find a multimedia resource in a tads 2 or tads 3 game file Function Searches a compiled tads 2 game file or a compiled tads 3 image file for a multimedia resource of a given name. The caller doesn't have to know which tads version created the file; we'll sense the file type and parse it accordingly. This implementation is independent of any tads 2 or tads 3 subsystem except osifc, which it uses for portable file I/O and portable byte format conversions. Notes Modified 09/24/01 MJRoberts - Creation */ #ifndef RESFIND_H #define RESFIND_H /* * Resource locator. This gives the seek offset and size of a resource * within a larger file. */ struct tads_resinfo { /* seek location of start of resource */ unsigned long seek_pos; /* size of resource in bytes */ unsigned long siz; }; /* * Find a multimedia resource with the given name in the given file. The * file must be positioned at the start of the tads game file when we're * invoked - this allows searching for a resource within a game file that * is embedded in a larger file stream, since we don't care where within * the osfildef stream the tads game file starts. * * Fills in the resource information structure with the seek offset and * size of the resource in the file and returns true if the resource is * found; returns false if a resource with the given name doesn't exist in * the file. */ int tads_find_resource_fp(osfildef *fp, const char *resname, tads_resinfo *info); /* * Find a resource in a file, given the filename. * * Fills in the resource information structure with the seek offset and * size of the resource in the file and returns true if the resource is * found; returns false if a resource with the given name doesn't exist in * the file. */ int tads_find_resource(const char *fname, const char *resname, tads_resinfo *info); #endif /* RESFIND_H */ qtads-2.1.7/tads3/resload.cpp000066400000000000000000000057471265017072300160300ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/resload.cpp,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name resload.cpp - resource loader Function Notes Modified 10/17/98 MJRoberts - Creation */ #include #include #include "t3std.h" #include "resload.h" /* ------------------------------------------------------------------------ */ /* * create resource loader */ CResLoader::CResLoader() { /* we have no root directory */ root_dir_ = 0; /* no executable path yet */ exe_filename_ = 0; } /* * create a resource loader given a root directory */ CResLoader::CResLoader(const char *root_dir) { /* remember the root directory for file searches */ root_dir_ = lib_copy_str(root_dir); /* no executable path yet */ exe_filename_ = 0; } /* * delete resource loader */ CResLoader::~CResLoader() { /* free our root directory string and executable path string */ lib_free_str(root_dir_); lib_free_str(exe_filename_); } /* * Open a resource file given the resource path. */ osfildef *CResLoader::open_res_file(const char *respath, const char *deflib, const char *exerestype) { char filepath[OSFNMAX]; osfildef *fp; /* * Look for the resource as an external file. If we have a root * directory, look for the file under that directory; otherwise, * look for it in the current directory. */ if (root_dir_ != 0) { /* get the resource name as a file path */ char fname[OSFNMAX]; os_cvt_url_dir(fname, sizeof(fname), respath); /* build a full path from the root directory and the resource path */ os_build_full_path(filepath, sizeof(filepath), root_dir_, fname); } else { /* get the resource name as a file path */ os_cvt_url_dir(filepath, sizeof(filepath), respath); } /* try opening the file */ fp = osfoprb(filepath, OSFTBIN); /* if we didn't find it, try looking in the default library */ if (fp == 0 && deflib != 0) { char fname[OSFNMAX]; /* convert from URL notation to local path conventions */ os_cvt_url_dir(fname, sizeof(fname), deflib); /* build the full path, starting in the root resource directory */ os_build_full_path(filepath, sizeof(filepath), root_dir_, fname); /* add the default resource library extension */ os_defext(filepath, "t3r"); /* try opening the file */ fp = open_lib_res(filepath, respath); } /* if we still didn't find it, try looking in the executable */ if (fp == 0 && exerestype != 0) fp = open_exe_res(respath, exerestype); /* return the result */ return fp; } qtads-2.1.7/tads3/resload.h000066400000000000000000000072401265017072300154630ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/resload.h,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name resload.h - resource loader Function Notes Modified 10/17/98 MJRoberts - Creation */ #ifndef RESLOAD_H #define RESLOAD_H #include "t3std.h" /* ------------------------------------------------------------------------ */ /* * Resource loader. */ class CResLoader { public: /* create the loader */ CResLoader(); /* * create the loader with a root directory - we'll look for external * files under this directory */ CResLoader(const char *root_dir); /* set the executable file name */ void set_exe_filename(const char *exe_filename) { /* discard the old executable filename, if there is one */ lib_free_str(exe_filename_); /* remember the new path */ exe_filename_ = lib_copy_str(exe_filename); } /* delete */ ~CResLoader(); /* * Load a resource given a resource path. * * We'll start by looking for a file in the local file system matching * the name 'respath'. We interpret 'respath' as a relative URL; we'll * convert it to local system conventions before looking for the local * file. The path is relative to the root resource directory, which is * the directory specified when the resource loader was created. * * If we fail to find a local file matching the given name, we'll look * for a T3 resource library file with name 'deflib', if that argument * is non-null. 'deflib' is given as a URL to a file relative to the * root resource directory, and should be specified WITHOUT the default * ".t3r" suffix - we'll add that automatically using the appropriate * local filename conventions. If we can find this resource library, * we'll look for 'respath' within the library. Note that 'deflib' is * given in URL notation - we'll convert this to local path conventions * before attempting to locate the file. * * If we still can't find the file, and 'exerestype' is non-null, we'll * look in the executable file for a resource of the given type (this * should be a four-byte identifier, as used with MAKETRX). This * allows resources of different types to be bundled into the * executable file. Note that resources bundled into the executable * must use the standard T3 resource-image file format. */ osfildef *open_res_file(const char *respath, const char *deflib, const char *exerestype); protected: /* * Load a resource from the executable file. If we find the resource, * returns a file handle with its seek position set to the start of * the resource data. If we can't find the resource in the * executable, returns null. */ osfildef *open_exe_res(const char *respath, const char *restype); /* * Load a resource from a resource library. If we find the resource, * returns a file handle with its seek position set to the start of the * resource data. If we can't find the library or we can't find the * resource in the library, returns null. */ osfildef *open_lib_res(const char *libfile, const char *respath); /* root directory for external file searches */ char *root_dir_; /* path to executable file */ char *exe_filename_; }; #endif /* RESLOAD_H */ qtads-2.1.7/tads3/resnoexe.cpp000066400000000000000000000022461265017072300162160ustar00rootroot00000000000000/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name resnoexe.cpp - null implementation of executable file resource loader Function Provides a null implementation of CResLoader::open_exe_res(). Programs that do not want to be able to load resources from the executable file can link this module (rather than resldexe.cpp); this avoids dragging in additional osifc routines that are superfluous when executable resource loading is not required. Notes Modified 11/03/01 MJRoberts - Creation */ #include "resload.h" /* * Try loading a resource from the executable file */ osfildef *CResLoader::open_exe_res(const char *respath, const char *restype) { /* this version is a null implementation - return failure */ return 0; } /* * load a resource from a library */ osfildef *CResLoader::open_lib_res(const char *libfile, const char *respath) { /* this version is a null implementation - return failure */ return 0; } qtads-2.1.7/tads3/sha2.cpp000066400000000000000000000656761265017072300152430ustar00rootroot00000000000000/* --------------------------------------------------------------------------- Copyright (c) 2002, Dr Brian Gladman , Worcester, UK. All rights reserved. LICENSE TERMS The free distribution and use of this software in both source and binary form is allowed (with or without changes) provided that: 1. distributions of this source code include the above copyright notice, this list of conditions and the following disclaimer; 2. distributions in binary form include the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other associated materials; 3. the copyright holder's name is not used to endorse products built using this software without specific written permission. ALTERNATIVELY, provided that this notice is retained in full, this product may be distributed under the terms of the GNU General Public License (GPL), in which case the provisions of the GPL apply INSTEAD OF those given above. DISCLAIMER This software is provided 'as is' with no explicit or implied warranties in respect of its properties, including, but not limited to, correctness and/or fitness for purpose. --------------------------------------------------------------------------- Issue Date: 30/11/2002 This is a byte oriented version of SHA2 that operates on arrays of bytes stored in memory. This code implements sha256, sha384 and sha512 but the latter two functions rely on efficient 64-bit integer operations that may not be very efficient on 32-bit machines The sha256 functions use a type 'sha256_ctx' to hold details of the current hash state and uses the following three calls: void sha256_begin(sha256_ctx ctx[1]) void sha256_hash(const unsigned char data[], unsigned long len, sha256_ctx ctx[1]) void sha256_end(unsigned char hval[], sha256_ctx ctx[1]) The first subroutine initialises a hash computation by setting up the context in the sha256_ctx context. The second subroutine hashes 8-bit bytes from array data[] into the hash state withinh sha256_ctx context, the number of bytes to be hashed being given by the the unsigned long integer len. The third subroutine completes the hash calculation and places the resulting digest value in the array of 8-bit bytes hval[]. The sha384 and sha512 functions are similar and use the interfaces: void sha384_begin(sha384_ctx ctx[1]); void sha384_hash(const unsigned char data[], unsigned long len, sha384_ctx ctx[1]); void sha384_end(unsigned char hval[], sha384_ctx ctx[1]); void sha512_begin(sha512_ctx ctx[1]); void sha512_hash(const unsigned char data[], unsigned long len, sha512_ctx ctx[1]); void sha512_end(unsigned char hval[], sha512_ctx ctx[1]); In addition there is a function sha2 that can be used to call all these functions using a call with a hash length parameter as follows: int sha2_begin(unsigned long len, sha2_ctx ctx[1]); void sha2_hash(const unsigned char data[], unsigned long len, sha2_ctx ctx[1]); void sha2_end(unsigned char hval[], sha2_ctx ctx[1]); My thanks to Erik Andersen for testing this code on big-endian systems and for his assistance with corrections */ /* define the hash functions that you need */ //#define SHA_2 /* for dynamic hash length */ // $$$MJR - removed #define SHA_256 //#define SHA_384 // $$$MJR - removed //#define SHA_512 // $$$MJR - removed #include /* for memcpy() etc. */ #include /* for _lrotr with VC++ */ #include #include "sha2.h" #include "t3std.h" #include "vmdatasrc.h" /* 1. PLATFORM SPECIFIC INCLUDES */ #if defined(__GNU_LIBRARY__) # include # include #elif defined(__CRYPTLIB__) # if defined( INC_ALL ) # include "crypt.h" # elif defined( INC_CHILD ) # include "../crypt.h" # else # include "crypt.h" # endif # if defined(DATA_LITTLEENDIAN) # define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN # else # define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN # endif #elif defined(_MSC_VER) # include #elif !defined(WIN32) # include # if !defined (_ENDIAN_H) # include # else # include _ENDIAN_H # endif #endif /* 2. BYTE ORDER IN 32-BIT WORDS To obtain the highest speed on processors with 32-bit words, this code needs to determine the order in which bytes are packed into such words. The following block of code is an attempt to capture the most obvious ways in which various environemnts specify their endian definitions. It may well fail, in which case the definitions will need to be set by editing at the points marked **** EDIT HERE IF NECESSARY **** below. */ #define SHA_LITTLE_ENDIAN 1234 /* byte 0 is least significant (i386) */ #define SHA_BIG_ENDIAN 4321 /* byte 0 is most significant (mc68k) */ #if defined(CPU_IS_BIGENDIAN) # if (CPU_IS_BIGENDIAN == 1) # define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN # elif (CPU_IS_BIGENDIAN == 0) # define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN # endif #endif #if !defined(PLATFORM_BYTE_ORDER) #if defined(LITTLE_ENDIAN) || defined(BIG_ENDIAN) # if defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN) # if defined(BYTE_ORDER) # if (BYTE_ORDER == LITTLE_ENDIAN) # define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN # elif (BYTE_ORDER == BIG_ENDIAN) # define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN # endif # endif # elif defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN) # define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN # elif !defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN) # define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN # endif #elif defined(_LITTLE_ENDIAN) || defined(_BIG_ENDIAN) # if defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN) # if defined(_BYTE_ORDER) # if (_BYTE_ORDER == _LITTLE_ENDIAN) # define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN # elif (_BYTE_ORDER == _BIG_ENDIAN) # define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN # endif # endif # elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) # define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN # elif !defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN) # define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN # endif #elif 0 /* **** EDIT HERE IF NECESSARY **** */ #define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN #elif 0 /* **** EDIT HERE IF NECESSARY **** */ #define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN #elif (('1234' >> 24) == '1') # define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN #elif (('4321' >> 24) == '1') # define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN #endif #endif #if !defined(PLATFORM_BYTE_ORDER) # error Please set undetermined byte order (lines 159 or 161 of sha2.c). #endif #ifdef _MSC_VER #pragma intrinsic(memcpy) #endif #define rotr32(x,n) (((x) >> n) | ((x) << (32 - n))) #if !defined(bswap_32) #define bswap_32(x) (rotr32((x), 24) & 0x00ff00ff | rotr32((x), 8) & 0xff00ff00) #endif #if (PLATFORM_BYTE_ORDER == SHA_LITTLE_ENDIAN) #define SWAP_BYTES #else #undef SWAP_BYTES #endif #if defined(SHA_2) || defined(SHA_256) #define SHA256_MASK (SHA256_BLOCK_SIZE - 1) #if defined(SWAP_BYTES) #define bsw_32(p,n) { int _i = (n); while(_i--) p[_i] = bswap_32(p[_i]); } #else #define bsw_32(p,n) #endif /* SHA256 mixing function definitions */ #define ch(x,y,z) (((x) & (y)) ^ (~(x) & (z))) #define maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) #define s256_0(x) (rotr32((x), 2) ^ rotr32((x), 13) ^ rotr32((x), 22)) #define s256_1(x) (rotr32((x), 6) ^ rotr32((x), 11) ^ rotr32((x), 25)) #define g256_0(x) (rotr32((x), 7) ^ rotr32((x), 18) ^ ((x) >> 3)) #define g256_1(x) (rotr32((x), 17) ^ rotr32((x), 19) ^ ((x) >> 10)) /* rotated SHA256 round definition. Rather than swapping variables as in */ /* FIPS-180, different variables are 'rotated' on each round, returning */ /* to their starting positions every eight rounds */ #define h2(i) ctx->wbuf[i & 15] += \ g256_1(ctx->wbuf[(i + 14) & 15]) + ctx->wbuf[(i + 9) & 15] + g256_0(ctx->wbuf[(i + 1) & 15]) #define h2_cycle(i,j) \ v[(7 - i) & 7] += (j ? h2(i) : ctx->wbuf[i & 15]) + k256[i + j] \ + s256_1(v[(4 - i) & 7]) + ch(v[(4 - i) & 7], v[(5 - i) & 7], v[(6 - i) & 7]); \ v[(3 - i) & 7] += v[(7 - i) & 7]; \ v[(7 - i) & 7] += s256_0(v[(0 - i) & 7]) + maj(v[(0 - i) & 7], v[(1 - i) & 7], v[(2 - i) & 7]) /* SHA256 mixing data */ const sha2_32t k256[64] = { n_u32(428a2f98), n_u32(71374491), n_u32(b5c0fbcf), n_u32(e9b5dba5), n_u32(3956c25b), n_u32(59f111f1), n_u32(923f82a4), n_u32(ab1c5ed5), n_u32(d807aa98), n_u32(12835b01), n_u32(243185be), n_u32(550c7dc3), n_u32(72be5d74), n_u32(80deb1fe), n_u32(9bdc06a7), n_u32(c19bf174), n_u32(e49b69c1), n_u32(efbe4786), n_u32(0fc19dc6), n_u32(240ca1cc), n_u32(2de92c6f), n_u32(4a7484aa), n_u32(5cb0a9dc), n_u32(76f988da), n_u32(983e5152), n_u32(a831c66d), n_u32(b00327c8), n_u32(bf597fc7), n_u32(c6e00bf3), n_u32(d5a79147), n_u32(06ca6351), n_u32(14292967), n_u32(27b70a85), n_u32(2e1b2138), n_u32(4d2c6dfc), n_u32(53380d13), n_u32(650a7354), n_u32(766a0abb), n_u32(81c2c92e), n_u32(92722c85), n_u32(a2bfe8a1), n_u32(a81a664b), n_u32(c24b8b70), n_u32(c76c51a3), n_u32(d192e819), n_u32(d6990624), n_u32(f40e3585), n_u32(106aa070), n_u32(19a4c116), n_u32(1e376c08), n_u32(2748774c), n_u32(34b0bcb5), n_u32(391c0cb3), n_u32(4ed8aa4a), n_u32(5b9cca4f), n_u32(682e6ff3), n_u32(748f82ee), n_u32(78a5636f), n_u32(84c87814), n_u32(8cc70208), n_u32(90befffa), n_u32(a4506ceb), n_u32(bef9a3f7), n_u32(c67178f2), }; /* SHA256 initialisation data */ const sha2_32t i256[8] = { n_u32(6a09e667), n_u32(bb67ae85), n_u32(3c6ef372), n_u32(a54ff53a), n_u32(510e527f), n_u32(9b05688c), n_u32(1f83d9ab), n_u32(5be0cd19) }; void sha256_begin(sha256_ctx ctx[1]) { ctx->count[0] = ctx->count[1] = 0; memcpy(ctx->hash, i256, 8 * sizeof(sha2_32t)); } /* Compile 64 bytes of hash data into SHA256 digest value */ /* NOTE: this routine assumes that the byte order in the */ /* ctx->wbuf[] at this point is in such an order that low */ /* address bytes in the ORIGINAL byte stream placed in this */ /* buffer will now go to the high end of words on BOTH big */ /* and little endian systems */ void sha256_compile(sha256_ctx ctx[1]) { sha2_32t v[8], j; memcpy(v, ctx->hash, 8 * sizeof(sha2_32t)); for(j = 0; j < 64; j += 16) { h2_cycle( 0, j); h2_cycle( 1, j); h2_cycle( 2, j); h2_cycle( 3, j); h2_cycle( 4, j); h2_cycle( 5, j); h2_cycle( 6, j); h2_cycle( 7, j); h2_cycle( 8, j); h2_cycle( 9, j); h2_cycle(10, j); h2_cycle(11, j); h2_cycle(12, j); h2_cycle(13, j); h2_cycle(14, j); h2_cycle(15, j); } ctx->hash[0] += v[0]; ctx->hash[1] += v[1]; ctx->hash[2] += v[2]; ctx->hash[3] += v[3]; ctx->hash[4] += v[4]; ctx->hash[5] += v[5]; ctx->hash[6] += v[6]; ctx->hash[7] += v[7]; } /* SHA256 hash data in an array of bytes into hash buffer */ /* and call the hash_compile function as required. */ void sha256_hash(const unsigned char data[], unsigned long len, sha256_ctx ctx[1]) { sha2_32t pos = (sha2_32t)(ctx->count[0] & SHA256_MASK), space = SHA256_BLOCK_SIZE - pos; const unsigned char *sp = data; if((ctx->count[0] += len) < len) ++(ctx->count[1]); while(len >= space) /* tranfer whole blocks while possible */ { memcpy(((unsigned char*)ctx->wbuf) + pos, sp, space); sp += space; len -= space; space = SHA256_BLOCK_SIZE; pos = 0; bsw_32(ctx->wbuf, SHA256_BLOCK_SIZE >> 2) sha256_compile(ctx); } memcpy(((unsigned char*)ctx->wbuf) + pos, sp, len); } /* SHA256 Final padding and digest calculation */ static sha2_32t m1[4] = { n_u32(00000000), n_u32(ff000000), n_u32(ffff0000), n_u32(ffffff00) }; static sha2_32t b1[4] = { n_u32(80000000), n_u32(00800000), n_u32(00008000), n_u32(00000080) }; void sha256_end(unsigned char hval[], sha256_ctx ctx[1]) { sha2_32t i = (sha2_32t)(ctx->count[0] & SHA256_MASK); bsw_32(ctx->wbuf, (i + 3) >> 2) /* bytes in the buffer are now in an order in which references */ /* to 32-bit words will put bytes with lower addresses into the */ /* top of 32 bit words on BOTH big and little endian machines */ /* we now need to mask valid bytes and add the padding which is */ /* a single 1 bit and as many zero bits as necessary. */ ctx->wbuf[i >> 2] = (ctx->wbuf[i >> 2] & m1[i & 3]) | b1[i & 3]; /* we need 9 or more empty positions, one for the padding byte */ /* (above) and eight for the length count. If there is not */ /* enough space pad and empty the buffer */ if(i > SHA256_BLOCK_SIZE - 9) { if(i < 60) ctx->wbuf[15] = 0; sha256_compile(ctx); i = 0; } else /* compute a word index for the empty buffer positions */ i = (i >> 2) + 1; while(i < 14) /* and zero pad all but last two positions */ ctx->wbuf[i++] = 0; /* the following 32-bit length fields are assembled in the */ /* wrong byte order on little endian machines but this is */ /* corrected later since they are only ever used as 32-bit */ /* word values. */ ctx->wbuf[14] = (ctx->count[1] << 3) | (ctx->count[0] >> 29); ctx->wbuf[15] = ctx->count[0] << 3; sha256_compile(ctx); /* extract the hash value as bytes in case the hash buffer is */ /* mislaigned for 32-bit words */ for(i = 0; i < SHA256_DIGEST_SIZE; ++i) hval[i] = (unsigned char)(ctx->hash[i >> 2] >> 8 * (~i & 3)); } void sha256(unsigned char hval[], const unsigned char data[], unsigned long len) { sha256_ctx cx[1]; sha256_begin(cx); sha256_hash(data, len, cx); sha256_end(hval, cx); } #endif #if defined(SHA_2) || defined(SHA_384) || defined(SHA_512) #define SHA512_MASK (SHA512_BLOCK_SIZE - 1) #define rotr64(x,n) (((x) >> n) | ((x) << (64 - n))) #if !defined(bswap_64) #define bswap_64(x) (((sha2_64t)(bswap_32((sha2_32t)(x)))) << 32 | bswap_32((sha2_32t)((x) >> 32))) #endif #if defined(SWAP_BYTES) #define bsw_64(p,n) { int _i = (n); while(_i--) p[_i] = bswap_64(p[_i]); } #else #define bsw_64(p,n) #endif /* SHA512 mixing function definitions */ #define s512_0(x) (rotr64((x), 28) ^ rotr64((x), 34) ^ rotr64((x), 39)) #define s512_1(x) (rotr64((x), 14) ^ rotr64((x), 18) ^ rotr64((x), 41)) #define g512_0(x) (rotr64((x), 1) ^ rotr64((x), 8) ^ ((x) >> 7)) #define g512_1(x) (rotr64((x), 19) ^ rotr64((x), 61) ^ ((x) >> 6)) /* rotated SHA512 round definition. Rather than swapping variables as in */ /* FIPS-180, different variables are 'rotated' on each round, returning */ /* to their starting positions every eight rounds */ #define h5(i) ctx->wbuf[i & 15] += \ g512_1(ctx->wbuf[(i + 14) & 15]) + ctx->wbuf[(i + 9) & 15] + g512_0(ctx->wbuf[(i + 1) & 15]) #define h5_cycle(i,j) \ v[(7 - i) & 7] += (j ? h5(i) : ctx->wbuf[i & 15]) + k512[i + j] \ + s512_1(v[(4 - i) & 7]) + ch(v[(4 - i) & 7], v[(5 - i) & 7], v[(6 - i) & 7]); \ v[(3 - i) & 7] += v[(7 - i) & 7]; \ v[(7 - i) & 7] += s512_0(v[(0 - i) & 7]) + maj(v[(0 - i) & 7], v[(1 - i) & 7], v[(2 - i) & 7]) /* SHA384/SHA512 mixing data */ const sha2_64t k512[80] = { n_u64(428a2f98d728ae22), n_u64(7137449123ef65cd), n_u64(b5c0fbcfec4d3b2f), n_u64(e9b5dba58189dbbc), n_u64(3956c25bf348b538), n_u64(59f111f1b605d019), n_u64(923f82a4af194f9b), n_u64(ab1c5ed5da6d8118), n_u64(d807aa98a3030242), n_u64(12835b0145706fbe), n_u64(243185be4ee4b28c), n_u64(550c7dc3d5ffb4e2), n_u64(72be5d74f27b896f), n_u64(80deb1fe3b1696b1), n_u64(9bdc06a725c71235), n_u64(c19bf174cf692694), n_u64(e49b69c19ef14ad2), n_u64(efbe4786384f25e3), n_u64(0fc19dc68b8cd5b5), n_u64(240ca1cc77ac9c65), n_u64(2de92c6f592b0275), n_u64(4a7484aa6ea6e483), n_u64(5cb0a9dcbd41fbd4), n_u64(76f988da831153b5), n_u64(983e5152ee66dfab), n_u64(a831c66d2db43210), n_u64(b00327c898fb213f), n_u64(bf597fc7beef0ee4), n_u64(c6e00bf33da88fc2), n_u64(d5a79147930aa725), n_u64(06ca6351e003826f), n_u64(142929670a0e6e70), n_u64(27b70a8546d22ffc), n_u64(2e1b21385c26c926), n_u64(4d2c6dfc5ac42aed), n_u64(53380d139d95b3df), n_u64(650a73548baf63de), n_u64(766a0abb3c77b2a8), n_u64(81c2c92e47edaee6), n_u64(92722c851482353b), n_u64(a2bfe8a14cf10364), n_u64(a81a664bbc423001), n_u64(c24b8b70d0f89791), n_u64(c76c51a30654be30), n_u64(d192e819d6ef5218), n_u64(d69906245565a910), n_u64(f40e35855771202a), n_u64(106aa07032bbd1b8), n_u64(19a4c116b8d2d0c8), n_u64(1e376c085141ab53), n_u64(2748774cdf8eeb99), n_u64(34b0bcb5e19b48a8), n_u64(391c0cb3c5c95a63), n_u64(4ed8aa4ae3418acb), n_u64(5b9cca4f7763e373), n_u64(682e6ff3d6b2b8a3), n_u64(748f82ee5defb2fc), n_u64(78a5636f43172f60), n_u64(84c87814a1f0ab72), n_u64(8cc702081a6439ec), n_u64(90befffa23631e28), n_u64(a4506cebde82bde9), n_u64(bef9a3f7b2c67915), n_u64(c67178f2e372532b), n_u64(ca273eceea26619c), n_u64(d186b8c721c0c207), n_u64(eada7dd6cde0eb1e), n_u64(f57d4f7fee6ed178), n_u64(06f067aa72176fba), n_u64(0a637dc5a2c898a6), n_u64(113f9804bef90dae), n_u64(1b710b35131c471b), n_u64(28db77f523047d84), n_u64(32caab7b40c72493), n_u64(3c9ebe0a15c9bebc), n_u64(431d67c49c100d4c), n_u64(4cc5d4becb3e42b6), n_u64(597f299cfc657e2a), n_u64(5fcb6fab3ad6faec), n_u64(6c44198c4a475817) }; /* Compile 64 bytes of hash data into SHA384/SHA512 digest value */ void sha512_compile(sha512_ctx ctx[1]) { sha2_64t v[8]; sha2_32t j; memcpy(v, ctx->hash, 8 * sizeof(sha2_64t)); for(j = 0; j < 80; j += 16) { h5_cycle( 0, j); h5_cycle( 1, j); h5_cycle( 2, j); h5_cycle( 3, j); h5_cycle( 4, j); h5_cycle( 5, j); h5_cycle( 6, j); h5_cycle( 7, j); h5_cycle( 8, j); h5_cycle( 9, j); h5_cycle(10, j); h5_cycle(11, j); h5_cycle(12, j); h5_cycle(13, j); h5_cycle(14, j); h5_cycle(15, j); } ctx->hash[0] += v[0]; ctx->hash[1] += v[1]; ctx->hash[2] += v[2]; ctx->hash[3] += v[3]; ctx->hash[4] += v[4]; ctx->hash[5] += v[5]; ctx->hash[6] += v[6]; ctx->hash[7] += v[7]; } /* Compile 128 bytes of hash data into SHA256 digest value */ /* NOTE: this routine assumes that the byte order in the */ /* ctx->wbuf[] at this point is in such an order that low */ /* address bytes in the ORIGINAL byte stream placed in this */ /* buffer will now go to the high end of words on BOTH big */ /* and little endian systems */ void sha512_hash(const unsigned char data[], unsigned long len, sha512_ctx ctx[1]) { sha2_32t pos = (sha2_32t)(ctx->count[0] & SHA512_MASK), space = SHA512_BLOCK_SIZE - pos; const unsigned char *sp = data; if((ctx->count[0] += len) < len) ++(ctx->count[1]); while(len >= space) /* tranfer whole blocks while possible */ { memcpy(((unsigned char*)ctx->wbuf) + pos, sp, space); sp += space; len -= space; space = SHA512_BLOCK_SIZE; pos = 0; bsw_64(ctx->wbuf, SHA512_BLOCK_SIZE >> 3); sha512_compile(ctx); } memcpy(((unsigned char*)ctx->wbuf) + pos, sp, len); } /* SHA384/512 Final padding and digest calculation */ static sha2_64t m2[8] = { n_u64(0000000000000000), n_u64(ff00000000000000), n_u64(ffff000000000000), n_u64(ffffff0000000000), n_u64(ffffffff00000000), n_u64(ffffffffff000000), n_u64(ffffffffffff0000), n_u64(ffffffffffffff00) }; static sha2_64t b2[8] = { n_u64(8000000000000000), n_u64(0080000000000000), n_u64(0000800000000000), n_u64(0000008000000000), n_u64(0000000080000000), n_u64(0000000000800000), n_u64(0000000000008000), n_u64(0000000000000080) }; static void sha_end(unsigned char hval[], sha512_ctx ctx[1], const unsigned int hlen) { sha2_32t i = (sha2_32t)(ctx->count[0] & SHA512_MASK); bsw_64(ctx->wbuf, (i + 7) >> 3); /* bytes in the buffer are now in an order in which references */ /* to 64-bit words will put bytes with lower addresses into the */ /* top of 64 bit words on BOTH big and little endian machines */ /* we now need to mask valid bytes and add the padding which is */ /* a single 1 bit and as many zero bits as necessary. */ ctx->wbuf[i >> 3] = (ctx->wbuf[i >> 3] & m2[i & 7]) | b2[i & 7]; /* we need 17 or more empty byte positions, one for the padding */ /* byte (above) and sixteen for the length count. If there is */ /* not enough space pad and empty the buffer */ if(i > SHA512_BLOCK_SIZE - 17) { if(i < 120) ctx->wbuf[15] = 0; sha512_compile(ctx); i = 0; } else i = (i >> 3) + 1; while(i < 14) ctx->wbuf[i++] = 0; /* the following 64-bit length fields are assembled in the */ /* wrong byte order on little endian machines but this is */ /* corrected later since they are only ever used as 64-bit */ /* word values. */ ctx->wbuf[14] = (ctx->count[1] << 3) | (ctx->count[0] >> 61); ctx->wbuf[15] = ctx->count[0] << 3; sha512_compile(ctx); /* extract the hash value as bytes in case the hash buffer is */ /* misaligned for 32-bit words */ for(i = 0; i < hlen; ++i) hval[i] = (unsigned char)(ctx->hash[i >> 3] >> 8 * (~i & 7)); } #endif #if defined(SHA_2) || defined(SHA_384) /* SHA384 initialisation data */ const sha2_64t i384[80] = { n_u64(cbbb9d5dc1059ed8), n_u64(629a292a367cd507), n_u64(9159015a3070dd17), n_u64(152fecd8f70e5939), n_u64(67332667ffc00b31), n_u64(8eb44a8768581511), n_u64(db0c2e0d64f98fa7), n_u64(47b5481dbefa4fa4) }; void sha384_begin(sha384_ctx ctx[1]) { ctx->count[0] = ctx->count[1] = 0; memcpy(ctx->hash, i384, 8 * sizeof(sha2_64t)); } void sha384_end(unsigned char hval[], sha384_ctx ctx[1]) { sha_end(hval, ctx, SHA384_DIGEST_SIZE); } void sha384(unsigned char hval[], const unsigned char data[], unsigned long len) { sha384_ctx cx[1]; sha384_begin(cx); sha384_hash(data, len, cx); sha384_end(hval, cx); } #endif #if defined(SHA_2) || defined(SHA_512) /* SHA512 initialisation data */ const sha2_64t i512[80] = { n_u64(6a09e667f3bcc908), n_u64(bb67ae8584caa73b), n_u64(3c6ef372fe94f82b), n_u64(a54ff53a5f1d36f1), n_u64(510e527fade682d1), n_u64(9b05688c2b3e6c1f), n_u64(1f83d9abfb41bd6b), n_u64(5be0cd19137e2179) }; void sha512_begin(sha512_ctx ctx[1]) { ctx->count[0] = ctx->count[1] = 0; memcpy(ctx->hash, i512, 8 * sizeof(sha2_64t)); } void sha512_end(unsigned char hval[], sha512_ctx ctx[1]) { sha_end(hval, ctx, SHA512_DIGEST_SIZE); } void sha512(unsigned char hval[], const unsigned char data[], unsigned long len) { sha512_ctx cx[1]; sha512_begin(cx); sha512_hash(data, len, cx); sha512_end(hval, cx); } #endif #if defined(SHA_2) #define CTX_256(x) ((x)->uu->ctx256) #define CTX_384(x) ((x)->uu->ctx512) #define CTX_512(x) ((x)->uu->ctx512) /* SHA2 initialisation */ int sha2_begin(unsigned long len, sha2_ctx ctx[1]) { unsigned long l = len; switch(len) { case 256: l = len >> 3; case 32: CTX_256(ctx)->count[0] = CTX_256(ctx)->count[1] = 0; memcpy(CTX_256(ctx)->hash, i256, 32); break; case 384: l = len >> 3; case 48: CTX_384(ctx)->count[0] = CTX_384(ctx)->count[1] = 0; memcpy(CTX_384(ctx)->hash, i384, 64); break; case 512: l = len >> 3; case 64: CTX_512(ctx)->count[0] = CTX_512(ctx)->count[1] = 0; memcpy(CTX_512(ctx)->hash, i512, 64); break; default: return SHA2_BAD; } ctx->sha2_len = l; return SHA2_GOOD; } void sha2_hash(const unsigned char data[], unsigned long len, sha2_ctx ctx[1]) { switch(ctx->sha2_len) { case 32: sha256_hash(data, len, CTX_256(ctx)); return; case 48: sha384_hash(data, len, CTX_384(ctx)); return; case 64: sha512_hash(data, len, CTX_512(ctx)); return; } } void sha2_end(unsigned char hval[], sha2_ctx ctx[1]) { switch(ctx->sha2_len) { case 32: sha256_end(hval, CTX_256(ctx)); return; case 48: sha_end(hval, CTX_384(ctx), SHA384_DIGEST_SIZE); return; case 64: sha_end(hval, CTX_512(ctx), SHA512_DIGEST_SIZE); return; } } int sha2(unsigned char hval[], unsigned long size, const unsigned char data[], unsigned long len) { sha2_ctx cx[1]; if(sha2_begin(size, cx) == SHA2_GOOD) { sha2_hash(data, len, cx); sha2_end(hval, cx); return SHA2_GOOD; } else return SHA2_BAD; } #endif /* ------------------------------------------------------------------------ */ /* * MJR additions */ /* * E-Z sha256 - hash a buffer and generate a printable hash string [mjr] */ void sha256_ez(char *hash, const char *data, size_t len) { sha256_ctx ctx; const int HASH_BYTES = 32; unsigned char hval[HASH_BYTES]; /* calculate the hash in binary format */ sha256_begin(&ctx); sha256_hash((const unsigned char *)data, len, &ctx); sha256_end(hval, &ctx); /* convert the binary hash to printable hex digits */ for (int i = 0 ; i < HASH_BYTES ; ++i, hash += 2) byte_to_xdigits(hash, hval[i]); /* null-terminate the string */ *hash = '\0'; } /* * Printf-style sha256 hashing [mjr] */ void sha256_ezf(char *hash, const char *fmt, ...) { /* generate the hash source */ va_list args; va_start(args, fmt); char *src = t3vsprintf_alloc(fmt, args); va_end(args); /* hash the result */ sha256_ez(hash, src, strlen(src)); /* done with the source buffer */ t3free(src); } /* * E-Z sha256 - hash from a data source [mjr] */ void sha256_datasrc(char *buf, CVmDataSource *src, unsigned long len) { /* set up the hash accumulator */ sha256_ctx ctx; sha256_begin(&ctx); /* feed the selected range of bytes into the hash */ while (len != 0) { /* get a chunk up to one buffer-full, or the total remaining */ unsigned char buf[1024]; size_t cur = (len > sizeof(buf) ? sizeof(buf) : (size_t)len); /* read the chunk */ if (cur != 0) cur = src->readc(buf, cur); /* if there's nothing left, we're done */ if (cur == 0) break; /* feed this chunk into the hash */ sha256_hash(buf, cur, &ctx); /* deduct this chunk from the remaining length */ len -= cur; } /* calculate the hash result */ const int HASH_BYTES = 32; unsigned char hash[HASH_BYTES]; sha256_end(hash, &ctx); /* convert the binary hash to printable hex digits */ char *bufp = buf; for (int i = 0 ; i < HASH_BYTES ; ++i, bufp += 2) byte_to_xdigits(bufp, hash[i]); /* null-terminate the string */ *bufp = '\0'; } qtads-2.1.7/tads3/sha2.h000066400000000000000000000117271265017072300146740ustar00rootroot00000000000000/* --------------------------------------------------------------------------- Copyright (c) 2002, Dr Brian Gladman , Worcester, UK. All rights reserved. LICENSE TERMS The free distribution and use of this software in both source and binary form is allowed (with or without changes) provided that: 1. distributions of this source code include the above copyright notice, this list of conditions and the following disclaimer; 2. distributions in binary form include the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other associated materials; 3. the copyright holder's name is not used to endorse products built using this software without specific written permission. ALTERNATIVELY, provided that this notice is retained in full, this product may be distributed under the terms of the GNU General Public License (GPL), in which case the provisions of the GPL apply INSTEAD OF those given above. DISCLAIMER This software is provided 'as is' with no explicit or implied warranties in respect of its properties, including, but not limited to, correctness and/or fitness for purpose. --------------------------------------------------------------------------- Issue Date: 30/11/2002 */ #ifndef _SHA2_H #define _SHA2_H #include /* Defines for suffixes to 32 and 64 bit unsigned numeric values */ #define sfx_lo(x,y) x##y #define sfx_hi(x,y) sfx_lo(x,y) #define n_u32(p) sfx_hi(0x##p,s_u32) #define n_u64(p) sfx_hi(0x##p,s_u64) /* define an unsigned 32-bit type */ #if UINT_MAX == 0xffffffff typedef unsigned int sha2_32t; #define s_u32 u #elif ULONG_MAX == 0xffffffff typedef unsigned long sha2_32t; #define s_u32 ul #else #error Please define sha2_32t as an unsigned 32 bit type in sha2.h #endif /* define an unsigned 64-bit type */ #if defined( _MSC_VER ) typedef unsigned __int64 sha2_64t; #define s_u64 ui64 #elif ULONG_MAX == 0xffffffffffffffff typedef unsigned long sha2_64t; #define s_u64 ul #elif ULONG_MAX == 0xffffffff typedef unsigned long long sha2_64t; /* a somewhat dangerous guess */ #define s_u64 ull #else #error Please define sha2_64t as an unsigned 64 bit type in sha2.h #endif #if defined(__cplusplus) extern "C" { #endif #define SHA256_DIGEST_SIZE 32 #define SHA384_DIGEST_SIZE 48 #define SHA512_DIGEST_SIZE 64 #define SHA256_BLOCK_SIZE 64 #define SHA384_BLOCK_SIZE 128 #define SHA512_BLOCK_SIZE 128 #define SHA2_DIGEST_SIZE SHA256_DIGEST_SIZE #define SHA2_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE #define SHA2_GOOD 0 #define SHA2_BAD 1 /* type to hold the SHA256 context */ typedef struct { sha2_32t count[2]; sha2_32t hash[8]; sha2_32t wbuf[16]; } sha256_ctx; /* type to hold the SHA384/512 context */ typedef struct { sha2_64t count[2]; sha2_64t hash[8]; sha2_64t wbuf[16]; } sha512_ctx; typedef sha512_ctx sha384_ctx; /* type to hold a SHA2 context (256/384/512) */ typedef struct { union { sha256_ctx ctx256[1]; sha512_ctx ctx512[1]; } uu[1]; sha2_32t sha2_len; } sha2_ctx; void sha256_compile(sha256_ctx ctx[1]); void sha512_compile(sha512_ctx ctx[1]); void sha256_begin(sha256_ctx ctx[1]); void sha256_hash(const unsigned char data[], unsigned long len, sha256_ctx ctx[1]); void sha256_end(unsigned char hval[], sha256_ctx ctx[1]); void sha256(unsigned char hval[], const unsigned char data[], unsigned long len); /* * Generate a printable version of a hash for a given buffer. 'hash' is an * array of at least 65 characters to receive the hash string. It's fine * to pass in the same buffer for both 'hash' and 'data', as long as it's * big enough (>=65 characters). [mjr] */ void sha256_ez(char *hash, const char *data, size_t data_len); /* * Generate a printable version of a hash for a given data source. [mjr] */ void sha256_datasrc(char *hash, class CVmDataSource *src, unsigned long len); /* * printf-style hash construction: format the string given by 'fmt' and the * subsequent arguments, and hash the result */ void sha256_ezf(char *hash, const char *fmt, ...); void sha384_begin(sha384_ctx ctx[1]); #define sha384_hash sha512_hash void sha384_end(unsigned char hval[], sha384_ctx ctx[1]); void sha384(unsigned char hval[], const unsigned char data[], unsigned long len); void sha512_begin(sha512_ctx ctx[1]); void sha512_hash(const unsigned char data[], unsigned long len, sha512_ctx ctx[1]); void sha512_end(unsigned char hval[], sha512_ctx ctx[1]); void sha512(unsigned char hval[], const unsigned char data[], unsigned long len); int sha2_begin(unsigned long size, sha2_ctx ctx[1]); void sha2_hash(const unsigned char data[], unsigned long len, sha2_ctx ctx[1]); void sha2_end(unsigned char hval[], sha2_ctx ctx[1]); int sha2(unsigned char hval[], unsigned long size, const unsigned char data[], unsigned long len); #if defined(__cplusplus) } #endif #endif qtads-2.1.7/tads3/std.cpp000066400000000000000000001146641265017072300151700ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/STD.CPP,v 1.3 1999/07/11 00:46:52 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name std.cpp - T3 library functions Function Notes Modified 04/16/99 MJRoberts - Creation */ #include #include #include #include "os.h" #include "t3std.h" #include "utf8.h" #include "osifcnet.h" /* ------------------------------------------------------------------------ */ /* * Allocate space for a string of a given length. We'll add in space * for the null terminator. */ char *lib_alloc_str(size_t len) { char *buf; /* allocate the space */ buf = (char *)t3malloc(len + 1); /* make sure it's initially null-terminated */ buf[0] = '\0'; /* return the space */ return buf; } /* * Allocate space for a string of known length, and save a copy of the * string. The length does not include a null terminator, and in fact * the string does not need to be null-terminated. The copy returned, * however, is null-terminated. */ char *lib_copy_str(const char *str, size_t len) { /* if the source string is null, just return null as the result */ if (str == 0) return 0; /* allocate space */ char *buf = lib_alloc_str(len); /* if that succeeded, make a copy */ if (buf != 0) { /* copy the string */ memcpy(buf, str, len); /* null-terminate it */ buf[len] = '\0'; } /* return the buffer */ return buf; } /* * allocate and copy a null-terminated string */ char *lib_copy_str(const char *str) { return (str == 0 ? 0 : lib_copy_str(str, strlen(str))); } /* * Free a string previously allocated with lib_copy_str() */ void lib_free_str(char *buf) { if (buf != 0) t3free(buf); } /* ------------------------------------------------------------------------ */ /* * Utility routine: compare spaces, collapsing whitespace */ int lib_strequal_collapse_spaces(const char *a, size_t a_len, const char *b, size_t b_len) { const char *a_end; const char *b_end; utf8_ptr ap, bp; /* calculate where the strings end */ a_end = a + a_len; b_end = b + b_len; /* keep going until we run out of strings */ for (ap.set((char *)a), bp.set((char *)b) ; ap.getptr() < a_end && bp.getptr() < b_end ; ) { /* check to see if we have whitespace in both strings */ if (is_space(ap.getch()) && is_space(bp.getch())) { /* skip all whitespace in both strings */ for (ap.inc() ; ap.getptr() < a_end && is_space(ap.getch()) ; ap.inc()) ; for (bp.inc() ; bp.getptr() < b_end && is_space(bp.getch()) ; bp.inc()) ; /* keep going */ continue; } /* if the characters here don't match, we don't have a match */ if (ap.getch() != bp.getch()) return FALSE; /* move on to the next character of each string */ ap.inc(); bp.inc(); } /* * if both strings ran out at the same time, we have a match; * otherwise, they're not the same */ return (ap.getptr() == a_end && bp.getptr() == b_end); } /* ------------------------------------------------------------------------ */ /* * Utility routine: do a case-insensitive comparison of two UTF-8 strings. * Returns strcmp-style results: negative if a < b, 0 if a == b, positive * if a > b. * * If 'bmatchlen' is null, it means that the two strings must have the same * number of characters. Otherwise, we'll return 0 (equal) if 'a' is a * leading substring of 'b', and fill in '*bmatchlen' with the length in * bytes of the 'b' string that we matched. This might differ from the * length of the 'a' string because of case folding. To match as a leading * substring, we have to match to a character boundary. E.g., we won't * match "weis" as a leading substring of "wei": while "weis" is indeed a * leading substring of "weiss", which is the case-folded version of * "wei", it doesn't end at a character boundary in the original. */ int t3_compare_case_fold( const char *a, size_t alen, const char *b, size_t blen, size_t *bmatchlen) { /* set up folded-case string readers for the two strings */ Utf8FoldStr ap(a, alen), bp(b, blen); /* scan until we find a mismatch or run out of one string */ while(ap.more() && bp.more()) { /* get the next character of each string */ wchar_t ach = ap.getch(), bch = bp.getch(); /* if they're different, return the sign difference */ if (ach != bch) return ach - bch; } /* * if 'a' ran out first, and we have a 'bmatchlen' pointer, then we're * being asked if 'a' is a leading substring of 'b', which it is - fill * in '*bmatchlen' with the length of 'b' that we matched, and return * ture */ if (bmatchlen != 0 && !ap.more() && bp.at_boundary()) { *bmatchlen = bp.getptr() - b; return 0; } /* which ran out first was shorter, so sorts first */ return ap.more() ? 1 : bp.more() ? -1 : 0; } /* * compare a wchar_t string against a utf-8 string with case folding */ int t3_compare_case_fold( const wchar_t *a, size_t alen, const char *b, size_t blen, size_t *bmatchlen) { /* set up folded-case string readers for the two strings */ CVmCaseFoldStr ap(a, alen); Utf8FoldStr bp(b, blen); /* scan until we find a mismatch or run out of one string */ while (ap.more() && bp.more()) { /* get the next character of each string */ wchar_t ach = ap.getch(), bch = bp.getch(); /* if they're different, return the sign difference */ if (ach != bch) return ach - bch; } /* * if 'a' ran out first, and we have a 'bmatchlen' pointer, then we're * being asked if 'a' is a leading substring of 'b', which it is - fill * in '*bmatchlen' with the length of 'b' that we matched, and return * ture */ if (bmatchlen != 0 && !ap.more() && bp.at_boundary()) { *bmatchlen = bp.getptr() - b; return 0; } /* which ran out first was shorter, so sorts first */ return ap.more() ? 1 : bp.more() ? -1 : 0; } /* ------------------------------------------------------------------------ */ /* * Compare the minimum number of characters in each string with case * folding. */ int t3_compare_case_fold_min( utf8_ptr &a, size_t &alen, utf8_ptr &b, size_t &blen) { /* if either is empty, there can be no match (unless both are empty) */ if (alen == 0 || blen == 0) return alen - blen; /* set up folded-case string readers for the two strings */ Utf8FoldStr ap(a.getptr(), alen), bp(b.getptr(), blen); /* * Scan until we're at a boundary in both strings. Note that we start * at a boundary, so always compare at least one character. */ do { /* get the next character of each string */ wchar_t ach = ap.getch(), bch = bp.getch(); /* if they're different, return the sign difference */ if (ach != bch) return ach - bch; } while (!ap.at_boundary() || !bp.at_boundary()); /* * If we made it to a boundary in each string without finding a * difference, we have a match. Advance each string past the matched * text. */ size_t ainc = ap.getptr() - a.getptr(); alen -= ainc; a.inc_bytes(ainc); size_t binc = bp.getptr() - b.getptr(); blen -= binc; b.inc_bytes(binc); /* return "equal" */ return 0; } int t3_compare_case_fold_min( utf8_ptr &a, size_t &alen, const wchar_t *&b, size_t &blen) { /* if either is empty, there can be no match (unless both are empty) */ if (alen == 0 || blen == 0) return alen - blen; /* set up folded-case string readers for the two strings */ Utf8FoldStr ap(a.getptr(), alen); CVmCaseFoldStr bp(b, blen); /* * Scan until we're at a boundary in both strings. Note that we start * at a boundary, so always compare at least one character. */ do { /* get the next character of each string */ wchar_t ach = ap.getch(), bch = bp.getch(); /* if they're different, return the sign difference */ if (ach != bch) return ach - bch; } while (!ap.at_boundary() || !bp.at_boundary()); /* * If we made it to a boundary in each string without finding a * difference, we have a match. Advance each string past the matched * text. */ size_t ainc = ap.getptr() - a.getptr(); alen -= ainc; a.inc_bytes(ainc); blen -= bp.getptr() - b; b = bp.getptr(); /* return "equal" */ return 0; } int t3_compare_case_fold_min( const wchar_t* &a, size_t &alen, const wchar_t* &b, size_t &blen) { /* if either is empty, there can be no match (unless both are empty) */ if (alen == 0 || blen == 0) return alen - blen; /* set up folded-case string readers for the two strings */ CVmCaseFoldStr ap(a, alen), bp(b, blen); /* * Scan until we're at a boundary in both strings. Note that we start * at a boundary, so always compare at least one character. */ do { /* get the next character of each string */ wchar_t ach = ap.getch(), bch = bp.getch(); /* if they're different, return the sign difference */ if (ach != bch) return ach - bch; } while (!ap.at_boundary() || !bp.at_boundary()); /* * If we got this far, we made it to a boundary in each string without * finding a difference, so we have a match. Advance each string past * the matched text. */ alen -= ap.getptr() - a; a = ap.getptr(); blen -= bp.getptr() - b; b = bp.getptr(); /* return "equal" */ return 0; } /* ------------------------------------------------------------------------ */ /* * Limited-length atoi */ int lib_atoi(const char *str, size_t len) { /* parse the sign, if present */ int s = 1; if (len >= 1 && *str == '-') s = -1, ++str, --len; else if (len >= 1 && *str == '+') ++str, --len; /* scan digits */ int acc; for (acc = 0 ; len > 0 && is_digit(*str) ; acc *= 10, acc += value_of_digit(*str), ++str, --len) ; /* apply the sign and return the result */ return s * acc; } /* * Limited-length atoi, with auto-advance of the string */ int lib_atoi_adv(const char *&str, size_t &len) { /* parse the sign, if present */ int s = 1; if (len >= 1 && *str == '-') s = -1, ++str, --len; else if (len >= 1 && *str == '+') ++str, --len; /* scan digits */ int acc; for (acc = 0 ; len > 0 && is_digit(*str) ; acc *= 10, acc += value_of_digit(*str), ++str, --len) ; /* apply the sign and return the result */ return s * acc; } /* ------------------------------------------------------------------------ */ /* * Find a version suffix in an identifier string. A version suffix * starts with the given character. If we don't find the character, * we'll return the default version suffix. In any case, we'll set * name_len to the length of the name portion, excluding the version * suffix and its leading separator. * * For example, with a '/' suffix, a versioned name string would look * like "tads-gen/030000" - the name is "tads_gen" and the version is * "030000". */ const char *lib_find_vsn_suffix(const char *name_string, char suffix_char, const char *default_vsn, size_t *name_len) { const char *vsn; /* find the suffix character, if any */ for (vsn = name_string ; *vsn != '\0' && *vsn != suffix_char ; ++vsn); /* note the length of the name portion */ *name_len = vsn - name_string; /* * skip the separator if we found one, to point vsn at the start of * the suffix string itself - it we didn't find the separator * character, use the default version string */ if (*vsn == suffix_char) ++vsn; else vsn = default_vsn; /* return the version string */ return vsn; } /* ------------------------------------------------------------------------ */ /* * allocating sprintf implementation */ char *t3sprintf_alloc(const char *fmt, ...) { /* package the arguments as a va_list and invoke our va_list version */ va_list args; va_start(args, fmt); char *str = t3vsprintf_alloc(fmt, args); va_end(args); /* return the allocated string */ return str; } /* * allocating vsprintf implementation */ char *t3vsprintf_alloc(const char *fmt, va_list args) { /* measure the required space - add in a byte for null termination */ size_t len = t3vsprintf(0, 0, fmt, args) + 1; /* allocate space */ char *buf = (char *)t3malloc(len); if (buf == 0) return 0; /* do the actual formatting */ t3vsprintf(buf, len, fmt, args); /* return the allocated buffer */ return buf; } /* * buffer-checked sprintf implementation */ size_t t3sprintf(char *buf, size_t buflen, const char *fmt, ...) { /* package the arguments as a va_list and invoke our va_list version */ va_list args; va_start(args, fmt); size_t len = t3vsprintf(buf, buflen, fmt, args); va_end(args); /* return the length */ return len; } /* check for 'th' suffix in a format code */ static const char *check_nth(const char *&fmt, int ival) { /* presume no suffix */ const char *nth = ""; /* check for the 'th' suffix in the format code */ if (fmt[1] == 't' && fmt[2] == 'h') { /* skip the extra format characters */ fmt += 2; /* 'th' suffix applies to most numbers */ nth = "th"; /* * check for 1st, 2nd, 3rd, 21st, 22nd, 23rd, etc; but note that * the the teens are all th's */ if (ival % 100 < 10 || ival % 100 > 20) { switch (ival % 10) { case 1: nth = "st"; break; case 2: nth = "nd"; break; case 3: nth = "rd"; break; } } } /* return the suffix we figured */ return nth; } /* * buffer-checked vsprintf implementation */ size_t t3vsprintf(char *buf, size_t buflen, const char *fmt, va_list args0) { size_t rem; size_t need = 0; char *dst; /* * make a private copy of the arguments, to ensure that we don't modify * the caller's copy (on some platforms, va_list is a reference type; * the caller might want to reuse their argument pointer for a two-pass * operation, such as a pre-format pass to measure how much space is * needed) */ va_list args; os_va_copy(args, args0); /* scan the buffer */ for (dst = buf, rem = buflen ; *fmt != '\0' ; ++fmt) { /* check for a format specifier */ if (*fmt == '%') { const char *fmt_start = fmt; const char *nth = ""; const char *txt; size_t txtlen; char buf[20]; int fld_wid = -1; int fld_prec = -1; char lead_char = ' '; int plus = FALSE; int approx = FALSE; int add_ellipsis = FALSE; int left_align = FALSE; size_t i; /* skip the '%' */ ++fmt; /* check for the "approximation" flag */ if (*fmt == '~') { approx = TRUE; ++fmt; } /* check for an explicit sign */ if (*fmt == '+') { plus = TRUE; ++fmt; } /* check for left alignment */ if (*fmt == '-') { left_align = TRUE; ++fmt; } /* if leading zeros are desired, note it */ if (*fmt == '0') lead_char = '0'; /* check for a field width specifier */ if (is_digit(*fmt)) { /* scan the digits */ for (fld_wid = 0 ; is_digit(*fmt) ; ++fmt) { fld_wid *= 10; fld_wid += value_of_digit(*fmt); } } else if (*fmt == '*') { /* the value is an integer taken from the arguments */ fld_wid = va_arg(args, int); /* skip the '*' */ ++fmt; } /* check for a precision specifier */ if (*fmt == '.') { /* skip the '.' */ ++fmt; /* check what we have */ if (*fmt == '*') { /* the value is an integer taken from the arguments */ fld_prec = va_arg(args, int); /* skip the '*' */ ++fmt; } else { /* scan the digits */ for (fld_prec = 0 ; is_digit(*fmt) ; ++fmt) { fld_prec *= 10; fld_prec += value_of_digit(*fmt); } } } /* check what follows */ switch (*fmt) { case '%': /* it's a literal '%' */ txt = "%"; txtlen = 1; break; case 's': /* the value is a (char *) */ txt = va_arg(args, char *); /* if it's null, show "(null)" */ if (txt == 0) txt = "(null)"; /* get the length, limiting it to the precision */ { const char *p; /* * Scan until we reach a null terminator, or until our * length reaches the maximum given by the precision * qualifier. */ for (txtlen = 0, p = txt ; *p != '\0' && (fld_prec == -1 || txtlen < (size_t)fld_prec) ; ++p, ++txtlen) ; } /* * if the 'approximation' flag is specified, limit the * string to 60 characters or the field width, whichever * is less */ if (approx) { size_t lim; /* the default limit is 60 characters */ lim = 60; /* * if a field width is specified, it overrides the * default - but take out three characters for the * '...' suffix */ if (fld_wid != -1 && (size_t)fld_wid > lim) lim = fld_wid - 3; /* if we're over the limit, shorten the string */ if (txtlen > lim) { /* shorten the string to the limit */ txtlen = lim; /* add an ellipsis to indicate the truncation */ add_ellipsis = TRUE; } } /* * if a field width was specified and we have the default * right alignment, pad to the left with spaces if the * width is greater than the actual length */ if (fld_wid != -1 && !left_align) { for ( ; (size_t)fld_wid > txtlen ; --fld_wid) { ++need; if (rem > 1) --rem, *dst++ = ' '; } } break; case 'P': /* * URL parameter - this is a (char*) value with url * encoding applied */ txt = va_arg(args, char *); /* if it's null, use an empty string */ if (txt == 0) txt = ""; /* use the field precision if given, or the string length */ txtlen = (fld_prec >= 0 ? fld_prec : strlen(txt)); /* encode the parameter and add it to the buffer */ for ( ; txtlen != 0 ; --txtlen, ++txt) { switch (*txt) { case '!': case '*': case '\'': case '(': case ')': case ';': case ':': case '@': case '&': case '=': case '+': case '$': case ',': case '/': case '?': case '#': case '[': case ']': case ' ': /* use % encoding for special characters */ sprintf(buf, "%%%02x", (unsigned)(uchar)*txt); need += 3; for (i = 0 ; i < 3 ; ++i) { if (rem > 1) *dst++ = buf[i], --rem; } break; default: /* copy anything else as-is */ need += 1; if (rem > 1) *dst++ = *txt, --rem; break; } } break; case 'c': /* the value is a (char) */ buf[0] = (char)va_arg(args, int); txt = buf; txtlen = 1; break; case 'd': /* the value is an int, formatted in decimal */ { int ival = va_arg(args, int); sprintf(buf, "%d", ival); /* check for '%dth' notation (1st, 2nd, etc) */ nth = check_nth(fmt, ival); } /* fall through to num_common: */ num_common: /* use the temporary buffer where we formatted the value */ txt = buf; txtlen = strlen(buf); /* * Pad with leading spaces or zeros if the requested * field width exceeds the actual size and we have the * default left alignment. */ if (fld_wid != -1 && !left_align && (size_t)fld_wid > txtlen) { /* * if we're showing an explicit sign, and we don't have * a leading '-' in the results, add it */ if (plus && txt[0] != '-') { /* add the '+' at the start of the output */ ++need; if (rem > 1) --rem, *dst++ = '+'; } /* * if we're showing leading zeros, and we have a * negative number, show the '-' first */ if (lead_char == '0' && txt[0] == '-') { /* add the '-' at the start of the output */ ++need; if (rem > 1) --rem, *dst++ = '-'; /* we've shown the '-', so skip it in the number */ ++txt; --txtlen; } /* add the padding */ for ( ; (size_t)fld_wid > txtlen ; --fld_wid) { ++need; if (rem > 1) --rem, *dst++ = lead_char; } } /* done */ break; case 'u': /* the value is an int, formatted as unsigned decimal */ sprintf(buf, "%u", va_arg(args, int)); goto num_common; case 'x': /* the value is an int, formatted in hex */ sprintf(buf, "%x", va_arg(args, int)); goto num_common; case 'o': /* the value is an int, formatted in hex */ sprintf(buf, "%o", va_arg(args, int)); goto num_common; case 'l': /* it's a 'long' value - get the second specifier */ switch (*++fmt) { case 'd': /* it's a long, formatted in decimal */ sprintf(buf, "%ld", va_arg(args, long)); goto num_common; case 'u': /* it's a long, formatted as unsigned decimal */ sprintf(buf, "%lu", va_arg(args, long)); goto num_common; case 'x': /* it's a long, formatted in hex */ sprintf(buf, "%lx", va_arg(args, long)); goto num_common; case 'o': /* it's a long, formatted in octal */ sprintf(buf, "%lo", va_arg(args, long)); goto num_common; default: /* bad specifier - show the literal text */ txt = "%"; txtlen = 1; fmt = fmt_start; break; } break; default: /* bad specifier - show the literal text */ txt = "%"; txtlen = 1; fmt = fmt_start; break; } /* add the text to the buffer */ for (i = txtlen ; i != 0 ; --i, ++txt) { ++need; if (rem > 1) --rem, *dst++ = *txt; } /* add the 'nth' suffix, if applicable */ for ( ; *nth != 0 ; ++nth) { ++need; if (rem > 1) --rem, *dst++ = *nth; } /* add an ellipsis if desired */ if (add_ellipsis) { /* add three '.' characters */ for (i = 3 ; i != 0 ; --i) { ++need; if (rem > 1) --rem, *dst++ = '.'; } /* for padding purposes, count the ellipsis */ txtlen += 3; } /* * if we have left alignment and the actual length is less * than the field width, pad to the right with spaces */ if (left_align && fld_wid != -1 && (size_t)fld_wid > txtlen) { /* add spaces to pad out to the field width */ for (i = fld_wid ; i > txtlen ; --i) { ++need; if (rem > 1) --rem, *dst++ = ' '; } } } else { /* it's not a format specifier - just copy it literally */ ++need; if (rem > 1) --rem, *dst++ = *fmt; } } /* add the trailing null */ if (rem != 0) *dst = '\0'; /* bracket the va_copy at the top of the function */ os_va_copy_end(args); /* return the needed length */ return need; } /* ------------------------------------------------------------------------ */ /* * Convert string to lower case */ void t3strlwr(char *p) { for ( ; *p != '\0' ; ++p) *p = (char)to_lower(*p); } /* ------------------------------------------------------------------------ */ /* * Debugging routines for memory management */ #ifdef T3_DEBUG #include #include #define T3_DEBUG_MEMGUARD #ifdef T3_DEBUG_MEMGUARD #define MEM_GUARD_PREFIX char guard[sizeof(mem_prefix_t *)]; #define MEM_GUARD_POST_BYTES sizeof(mem_prefix_t *) #else /* T3_DEBUG_MEMGUARD */ #define MEM_GUARD_PREFIX #define MEM_GUARD_BYTES 0 #define mem_check_guard(blk) #define mem_set_guard(blk) #endif /* T3_DEBUG_MEMGUARD */ /* * memory block prefix - each block we allocate has this prefix attached * just before the pointer that we return to the program */ struct mem_prefix_t { long id; size_t siz; mem_prefix_t *nxt; mem_prefix_t *prv; int alloc_type; OS_MEM_PREFIX MEM_GUARD_PREFIX }; #ifdef T3_DEBUG_MEMGUARD static void mem_make_guard(char *b, const mem_prefix_t *blk) { memcpy(b, &blk, sizeof(blk)); const static unsigned char x[] = { 0xcf, 0xfd, 0xef, 0xfb, 0xbf, 0xfc, 0xdf, 0xfe }; for (size_t i = 0 ; i < sizeof(blk) ; ++i) b[i] ^= x[i % countof(x)]; } static void mem_set_guard(mem_prefix_t *blk) { char b[sizeof(mem_prefix_t *)]; mem_make_guard(b, blk); memcpy(blk->guard, b, sizeof(b)); memcpy((char *)(blk + 1) + blk->siz, b, sizeof(b)); } static void mem_check_guard(const mem_prefix_t *blk) { char b[sizeof(mem_prefix_t *)]; mem_make_guard(b, blk); if (memcmp(blk->guard, b, sizeof(b)) != 0) fprintf(stderr, "pre guard bytes corrupted: addr=%lx, id=%ld, siz=%lu " OS_MEM_PREFIX_FMT "\n", (long)(blk + 1), blk->id, (unsigned long)blk->siz OS_MEM_PREFIX_FMT_VARS(blk)); if (memcmp((char *)(blk + 1) + blk->siz, b, sizeof(b)) != 0) fprintf(stderr, "post guard bytes corrupted: addr=%lx, id=%ld, siz=%lu " OS_MEM_PREFIX_FMT "\n", (long)(blk + 1), blk->id, (unsigned long)blk->siz OS_MEM_PREFIX_FMT_VARS(blk)); } #endif /* T3_DEBUG_MEMGUARD */ /* head and tail of memory allocation linked list */ static mem_prefix_t *mem_head = 0; static mem_prefix_t *mem_tail = 0; /* mutex for protecting the memory tracker list */ static OS_Mutex mem_mutex; /* * Check the integrity of the heap: traverse the entire list, and make * sure the forward and backward pointers match up. */ static void t3_check_heap() { mem_prefix_t *p; /* lock the memory mutex while accessing the memory block list */ mem_mutex.lock(); /* scan from the front */ for (p = mem_head ; p != 0 ; p = p->nxt) { /* * If there's a backwards pointer, make sure it matches up. If * there's no backwards pointer, make sure we're at the head of * the list. If this is the end of the list, make sure it * matches the tail pointer. */ if ((p->prv != 0 && p->prv->nxt != p) || (p->prv == 0 && p != mem_head) || (p->nxt == 0 && p != mem_tail)) fprintf(stderr, "\n--- heap corrupted ---\n"); } /* done with the list */ mem_mutex.unlock(); } /* * Allocate a block, storing it in a doubly-linked list of blocks and * giving the block a unique ID. */ void *t3malloc(size_t siz, int alloc_type) { static long id; static int check = 0; mem_prefix_t *mem; /* make sure the size doesn't overflow when we add the prefix */ if (siz + sizeof(mem_prefix_t) < siz) return 0; /* allocate the memory, including its prefix */ mem = (mem_prefix_t *)malloc(siz + sizeof(mem_prefix_t) + MEM_GUARD_POST_BYTES); /* if that failed, return failure */ if (mem == 0) return 0; /* set the guard bytes */ mem->alloc_type = alloc_type; mem->siz = siz; mem_set_guard(mem); /* lock the memory mutex while accessing the memory block list */ mem_mutex.lock(); /* set up the prefix */ mem->id = id++; mem->prv = mem_tail; os_mem_prefix_set(mem); mem->nxt = 0; if (mem_tail != 0) mem_tail->nxt = mem; else mem_head = mem; mem_tail = mem; /* done with the list */ mem_mutex.unlock(); /* check the heap for corruption if desired */ if (check) t3_check_heap(); /* return the caller's block, which immediately follows the prefix */ return (void *)(mem + 1); } /* * reallocate a block - to simplify, we'll allocate a new block, copy * the old block up to the smaller of the two block sizes, and delete * the old block */ void *t3realloc(void *oldptr, size_t newsiz) { void *newptr; size_t oldsiz; /* allocate a new block */ newptr = t3malloc(newsiz, T3MALLOC_TYPE_MALLOC); /* copy the old block into the new block */ oldsiz = (((mem_prefix_t *)oldptr) - 1)->siz; memcpy(newptr, oldptr, (oldsiz <= newsiz ? oldsiz : newsiz)); /* free the old block */ t3free(oldptr); /* return the new block */ return newptr; } /* free a block, removing it from the allocation block list */ void t3free(void *ptr, int alloc_type) { /* statics for debugging */ static int check = 0; static int double_check = 0; static int check_heap = 0; static long ckblk[] = { 0xD9D9D9D9, 0xD9D9D9D9, 0xD9D9D9D9 }; /* ignore freeing null */ if (ptr == 0) return; /* check the integrity of the entire heap if desired */ if (check_heap) t3_check_heap(); /* get the prefix */ mem_prefix_t *mem = ((mem_prefix_t *)ptr) - 1; size_t siz; /* * check that the call type matches the allocating call type (malloc, * new, or new[]) */ if (mem->alloc_type != alloc_type) fprintf(stderr, "\n--- memory block freed with wrong call type: " "block=%lx, size=%lu, id=%lu, alloc type=%d, free type=%d " "---\n", (unsigned long)ptr, (unsigned long)mem->siz, mem->id, mem->alloc_type, alloc_type); /* check for a pre-freed block */ if (memcmp(mem, ckblk, sizeof(ckblk)) == 0) { fprintf(stderr, "\n--- memory block freed twice: %lx ---\n", (unsigned long)ptr); return; } /* check the guard bytes for overwrites */ mem_check_guard(mem); /* lock the memory mutex while accessing the memory block list */ mem_mutex.lock(); /* if desired, check to make sure the block is in our list */ if (check) { mem_prefix_t *p; for (p = mem_head ; p != 0 ; p = p->nxt) { if (p == mem) break; } if (p == 0) fprintf(stderr, "\n--- memory block not found in t3free: %lx ---\n", (unsigned long)ptr); } /* unlink the block from the list */ if (mem->prv != 0) mem->prv->nxt = mem->nxt; else mem_head = mem->nxt; if (mem->nxt != 0) mem->nxt->prv = mem->prv; else mem_tail = mem->prv; /* * if we're being really cautious, check to make sure the block is * no longer in the list */ if (double_check) { mem_prefix_t *p; for (p = mem_head ; p != 0 ; p = p->nxt) { if (p == mem) break; } if (p != 0) fprintf(stderr, "\n--- memory block still in list after " "t3free ---\n"); } /* done with the list */ mem_mutex.unlock(); /* make it obvious that the memory is invalid */ siz = mem->siz; memset(mem, 0xD9, siz + sizeof(mem_prefix_t)); /* free the memory with the system allocator */ free((void *)mem); } /* * Default display lister callback */ static void fprintf_stderr(const char *msg) { fprintf(stderr, "%s", msg); } /* * Diagnostic routine to display the current state of the heap. This * can be called just before program exit to display any memory blocks * that haven't been deleted yet; any block that is still in use just * before program exit is a leaked block, so this function can be useful * to help identify and remove memory leaks. */ void t3_list_memory_blocks(void (*cb)(const char *)) { mem_prefix_t *mem; int cnt; char buf[128]; /* if there's no callback, use our own standard display lister */ if (cb == 0) cb = fprintf_stderr; /* display introductory message */ (*cb)("\n(T3VM) Memory blocks still in use:\n"); /* lock the memory mutex while accessing the memory block list */ mem_mutex.lock(); /* display the list of undeleted memory blocks */ for (mem = mem_head, cnt = 0 ; mem ; mem = mem->nxt, ++cnt) { sprintf(buf, " addr=%lx, id=%ld, siz=%lu" OS_MEM_PREFIX_FMT "\n", (long)(mem + 1), mem->id, (unsigned long)mem->siz OS_MEM_PREFIX_FMT_VARS(mem)); (*cb)(buf); } /* done with the mutex */ mem_mutex.unlock(); /* display totals */ sprintf(buf, "\nTotal blocks in use: %d\n", cnt); (*cb)(buf); } #ifdef T_WIN32 /* * Windows-specific additions to the memory header. We'll track the first * couple of return addresses from the stack, to make it easier to track * down where the allocation request came from. */ void os_mem_prefix_set(mem_prefix_t *mem) { /* * Trace back the call stack. In the standard Intel stack arrangement, * BP is the base pointer for the frame, and points to the enclosing * frame pointer. Just above BP is the return address. We're not * interested in our own return address, so skip the first frame. */ DWORD bp_; __asm mov bp_, ebp; bp_ = IsBadReadPtr((DWORD *)bp_, sizeof(bp_)) ? 0 : *(DWORD *)bp_; for (size_t i = 0 ; i < countof(mem->stk) && !IsBadReadPtr((DWORD *)bp_, sizeof(bp_)) && bp_ < *(DWORD *)bp_ ; ++i, bp_ = *(DWORD *)bp_) mem->stk[i].return_addr = ((DWORD *)bp_)[1]; } #endif /* T_WIN32 */ #endif /* T3_DEBUG */ qtads-2.1.7/tads3/t3std.h000066400000000000000000001042771265017072300151030ustar00rootroot00000000000000/* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name t3std.h - standard definitions Function Various standard definitions Notes None Modified 10/17/98 MJRoberts - creation (from TADS 2 lib.h) */ #ifndef T3_STD_INCLUDED #define T3_STD_INCLUDED #include #include #include #include #include #include #include "os.h" /* ------------------------------------------------------------------------ */ /* * err_throw() Return Handling * * Some compilers (such as MSVC 2007) are capable of doing global * optimizations that can detect functions that never return. err_throw() * is one such function: it uses longjmp() to jump out, so it never returns * to its caller. * * Most compilers can't detect this automatically, and C++ doesn't have a * standard way to declare a function that never returns. So on most * compilers, the compiler will assume that err_throw() returns, and thus * will generate a warning if err_throw() isn't followed by some proper * control flow statement. For example, in a function with a return value, * a code branch containing an err_throw() would still need a 'return * ' statement - without such a statement, the compiler would generate * an error about a branch without a value return. * * This creates a porting dilemma. On compilers that can detect that * err_throw() never returns, the presence of any statement in a code * branch after an err_throw() will cause an "unreachable code" error. For * all other compilers, the *absence* of such code will often cause a * different error ("missing return", etc). * * The only way I can see to deal with this is to use a compile-time * #define to select which type of compiler we're using, and use this to * insert or delete the proper dummy control flow statement after an * err_throw(). So: * * --- INSTRUCTIONS TO BASE CODE DEVELOPERS --- * * - after each err_throw() call, if the code branch needs some kind of * explicit termination (such as a "return val;" statement), code it with * the AFTER_ERR_THROW() macro. Since err_throw() never *actually* * returns, these will be dummy statements that will never be reached, but * the compiler might require their presence anyway because it doesn't know * better. * * --- INSTRUCTIONS TO PORTERS --- * * - if your compiler CAN detect that err_throw() never returns, define * COMPILER_DETECTS_THROW_NORETURN in your compiler command-line options; * * - otherwise, leave the symbol undefined. */ #ifdef COMPILER_DETECTS_THROW_NORETURN #define COMPILER_DETECTS_THROW_NORETURN_IN_VMERR #define AFTER_ERR_THROW(code) #else #define AFTER_ERR_THROW(code) code #endif /* * And the same idea as above, but for the special case where the compiler * detects this within the vmerr.cpp module (for static functions in the * same module), but not across modules. */ #ifdef COMPILER_DETECTS_THROW_NORETURN_IN_VMERR #define VMERR_AFTER_ERR_THROW(code) #else #define VMERR_AFTER_ERR_THROW(code) code #endif /* * os_term() return handling. This is similar to the longjmp() issue * above. Some compilers perform global optimizations that detect that * exit(), and functions that unconditionally call exit(), such as * os_term(), do not return. Since these compilers know that anything * following a call to exit() is unreachable, some will complain about if * anything follows an exit(). Compilers that *don't* do such * optimizations will complain if there *isn't* a 'return' after an exit() * if the containing function requires a return value, since as far as * they're concerned the function is falling off the end without returning * a value. So there's no solution at the C++ level; it's a compiler * variation that we have to address per compiler. If you get 'unreachable * code' errors in the generic code after calls to os_term(), define this * macro. Most platforms can leave it undefined. */ #ifdef COMPILER_DETECTS_OS_TERM_NORETURN #define AFTER_OS_TERM(code) #else #define AFTER_OS_TERM(code) code #endif /* * Some compilers (notably gcc >= 4.7.1) require that overloads for the * basic 'new' and 'delete' operators be declared with 'throw' clauses to * match the standard C++ library (that is, 'new' must be declared to throw * std::bad_alloc, and 'delete' must be declared to throw nothing). * Naturally, some other compilers (notably MSVC 2003) don't want the * 'throw' clauses. If your compiler wants the 'throw' declarations, * define NEW_DELETE_NEED_THROW in your makefile, otherwise omit it. Note * that some compilers (notably gcc < 4.7.1) don't care one way or the * other, so if your compiler doesn't complain, you probably don't need to * worry about this setting. */ #ifndef SYSTHROW #ifdef NEW_DELETE_NEED_THROW #define SYSTHROW(exc) exc #else #define SYSTHROW(exc) #endif #endif /* ------------------------------------------------------------------------ */ /* * T3 OS interface extensions. These are portable interfaces to * OS-dependent functionality, along the same lines as the functions * defined in tads2/osifc.h but specific to TADS 3. */ /* * Initialize the UI after loading the image file. The T3 image loader * calls this after it's finished loading a .t3 image file, but before * executing any code in the new game. This lets the local platform UI * perform any extra initialization that depends on inspecting the contents * of the loaded game. * * This isn't required to do anything; a valid implementation is an empty * stub routine. The purpose of this routine is to give the UI a chance to * customize the UI according to the details of the loaded game, before the * game starts running. In particular, the UI configuration might vary * according to which intrinsic classes or function sets are linked by the * game. For example, one set of windows might be used for a traditional * console game, while another is used for a Web UI game, the latter being * recognizable by linking the tads-net function set. */ void os_init_ui_after_load(class CVmBifTable *bif_table, class CVmMetaTable *meta_table); /* ------------------------------------------------------------------------ */ /* * Memory debugging */ /* in debug builds, we override operator new, which requires including */ #ifdef T3_DEBUG #include #endif /* for Windows debug builds, add stack trace info to allocation blocks */ #if defined(T3_DEBUG) && defined(T_WIN32) # define OS_MEM_PREFIX \ struct { \ DWORD return_addr; \ } stk[6]; void os_mem_prefix_set(struct mem_prefix_t *mem); # define OS_MEM_PREFIX_FMT ", return=(%lx, %lx, %lx, %lx, %lx, %lx)" # define OS_MEM_PREFIX_FMT_VARS(mem) \ , (mem)->stk[0].return_addr, \ (mem)->stk[1].return_addr, \ (mem)->stk[2].return_addr, \ (mem)->stk[3].return_addr, \ (mem)->stk[4].return_addr, \ (mem)->stk[5].return_addr #endif /* provide empty default definitions for system memory header add-ons */ #ifndef OS_MEM_PREFIX # define OS_MEM_PREFIX # define os_mem_prefix_set(mem) # define OS_MEM_PREFIX_FMT # define OS_MEM_PREFIX_FMT_VARS(mem) #endif /* ------------------------------------------------------------------------ */ /* * Types */ /* short-hand for various types */ #ifndef OS_UCHAR_DEFINED typedef unsigned char uchar; #endif #ifndef OS_USHORT_DEFINED typedef unsigned short ushort; #endif #ifndef OS_UINT_DEFINED typedef unsigned int uint; #endif #ifndef OS_ULONG_DEFINED typedef unsigned long ulong; #endif /* sizeof() extension macros */ #ifndef countof #define countof(array) (sizeof(array)/sizeof((array)[0])) #endif #define sizeof_field(struct_name, field) sizeof(((struct_name *)0)->field) /* * The types int16_t, uint16_t, int32_t, and uint32_t are defined by ANSI * C99 as EXACTLY the specified number of bits (e.g., int16 is a signed * 16-bit integer; uint32 is an unsigned 32-bit integer). * * Many modern compilers provide definitions for these types in . * When isn't available, ports must provide suitable typedefs in * the osxxx.h header - see tads2/osifc.h. * * Because these types have exact sizes, their limits are fixed, so we can * provide portable definitions here. */ #define INT16MAXVAL 32767 #define INT16MINVAL (-32768) #define UINT16MAXVAL 65535 #define INT32MAXVAL 2147483647L #define INT32MINVAL (-2147483647L-1) #define UINT32MAXVAL 4294967295U /* * Text character. We use ASCII and UTF-8 for most character string * representations, so our basic character type is 'char', which is defined * univerally as one byte. (UTF-8 uses varying numbers of bytes per * character, but its basic storage unit is bytes.) */ typedef char textchar_t; /* ------------------------------------------------------------------------ */ /* * Logical and Arithmetic right shifts. C99 deliberately leaves it up to * the implementation to define what happens to the high bits for a right * shift of a negative signed integer: they could be filled with 0s or 1s, * depending on the implementation. C99 is clear on the result for * unsigned values (the high bits are filled with 0s), however it's still * possible that we'll encounter an older implementation that doesn't * conform. * * There's one case where we care about ASHR vs RSHR being well defined, * and that's the VM's implementation of the OPC_LSHR and OPC_ASHR * instructions. We want these to be predictable on all platforms - we * insist on breaking the cycle of ambiguity; we don't want to pass along * this headache to our own users writing TADS programs. * * Nearly all modern compilers treat right shifts of signed operands as * arithmetic, and right shifts of unsigned operands as logical. So our * default implementation will take advantage of this to produce highly * efficient code. For platforms where the signed/unsigned behavior * doesn't work this way, we provide portable bit-twiddly versions that * will work, but are somewhat more overhead to implement. * * If you're on a platform that DOES provide the "modern" signed==ASHR / * unsigned==LSHR behavior, you don't have to do anything special - that's * the default. If your platform doesn't use the modern behavior, AND your * compiler's preprocessor does >> calculations the same way as generated * code, you also don't have to do anything, since our #if's below will * detect the situation and generate our portable explicit ASHR/LSHR code. * If your compiler doesn't use the "modern" behavior AND its preprocessor * uses different rules from generated code for >>, then you'll have to * define the preprocessor symbols OS_CUSTOM_ASHR and/or OS_CUSTOM_LSHR in * your makefile. * * You can test that your configuration is correct by compiling and running * the shr.t from the test suite (tads3/test/data). To further test the * detection conditions and custom macros, use test/test_shr.cpp. */ #if defined(OS_CUSTOM_ASHR) || ((-1 >> 1) != -1) /* signed a >> signed b != a ASHR b, so implement with bit masking */ inline int32_t t3_ashr(int32_t a, int32_t b) { int32_t mask = (~0 << (sizeof(int32_t)*CHAR_BIT - b)); return ((a >> b) | ((a & mask) ? mask : 0)); }; #else /* signed a >> signed b == a ASHR b, so we can use >> */ inline int32_t t3_ashr(int32_t a, int32_t b) { return a >> b; } #endif #if defined(OS_CUSTOM_LSHR) || ((ULONG_MAX >> 1UL) != ULONG_MAX/2) /* unsigned a >> unsigned b != a LSHR b, so implement with bit masking */ inline int32_t t3_lshr(int32_t a, int32_t b) { return ((a >> b) & ~(~0 << (sizeof(int32_t)*CHAR_BIT - b))); } #else /* * unsigned a >> unsigned b == a LSHR b, so we can use >>; note that we * explicit mask the left operand to 32 bits in case we're on a 64-bit or * larger platform, as the T3 VM itself is defined as a 32-bit machine */ inline int32_t t3_lshr(int32_t a, int32_t b) { return (int32_t)((uint32_t)a >> (uint32_t)b); } #endif /* ------------------------------------------------------------------------ */ /* * General portable utility macros */ /* clear a struture */ #define CLRSTRUCT(x) memset(&(x), 0, (size_t)sizeof(x)) #define CPSTRUCT(dst,src) memcpy(&(dst), &(src), (size_t)sizeof(dst)) /* TRUE and FALSE */ #ifndef TRUE # define TRUE 1 #endif /* TRUE */ #ifndef FALSE # define FALSE 0 #endif /* FALSE */ /* bitwise operations */ #define bit(va, bt) ((va) & (bt)) #define bis(va, bt) ((va) |= (bt)) #define bic(va, bt) ((va) &= ~(bt)) /* conditionally compile code if debugging is enabled */ #ifdef DEBUG # define IF_DEBUG(x) x #else /* DEBUG */ # define IF_DEBUG(x) #endif /* DEBUG */ /* offset within a structure of a member of the structure */ #ifndef offsetof # define offsetof(s_name, m_name) (size_t)&(((s_name *)0)->m_name) #endif /* offsetof */ /* * Read an unsigned 32-bit value from the portable external-file * representation. This is parallel to osrp4(), but explicitly reads an * unsigned value. The important thing is that we mask the result to 32 * bits, to prevent unwarranted sign extension on architectures with word * sizes greater than 32 bits (at the moment, this basically means 64-bit * machines, but it would apply to any >32-bit architecture). * * NB: as of TADS 3.1, osrp4() *should* be doing this automatically. It * should be reading a 32 bit value and interpreting it as unsigned, doing * any necessary zero extension to larger 'int' sizes. However, we're * keeping this for now to be sure. */ #define t3rp4u(p) ((ulong)(osrp4(p) & 0xFFFFFFFFU)) /* ------------------------------------------------------------------------ */ /* * Allocate space for a null-terminated string and save a copy of the * string */ char *lib_copy_str(const char *str); char *lib_copy_str(const char *str, size_t len); /* * allocate space for a string of a given length; we'll add in space for * a null terminator */ char *lib_alloc_str(size_t len); /* * Free a string previously allocated with lib_copy_str() or * lib_alloc_str() */ void lib_free_str(char *buf); /* ------------------------------------------------------------------------ */ /* * Safe strcpy with an explicit source length. Truncates the string to the * buffer size, and always null-terminates. Note that the source string is * NOT null-terminated - we copy the explicit length given, up to the * output size limit. */ inline void lib_strcpy(char *dst, size_t dstsiz, const char *src, size_t srclen) { if (dstsiz > 0) { size_t copylen = srclen; if (copylen > dstsiz - 1) copylen = dstsiz - 1; memcpy(dst, src, copylen); dst[copylen] = '\0'; } } /* * Safe strcpy - checks the output buffer size and truncates the string if * necessary; always null-terminates the result. */ inline void lib_strcpy(char *dst, size_t dstsiz, const char *src) { lib_strcpy(dst, dstsiz, src, strlen(src)); } /* * Compare two counted-length strings, with or without case sensitivity. */ inline int lib_strcmp(const char *str1, size_t len1, const char *str2, size_t len2) { int bylen = len1 - len2, bymem; if (bylen == 0) return memcmp(str1, str2, len1); else if (bylen < 0) bymem = memcmp(str1, str2, len1); else bymem = memcmp(str1, str2, len2); return (bymem != 0 ? bymem : bylen); } inline int lib_stricmp(const char *str1, size_t len1, const char *str2, size_t len2) { int bylen = len1 - len2, bymem; if (bylen == 0) return memicmp(str1, str2, len1); else if (bylen < 0) bymem = memicmp(str1, str2, len1); else bymem = memicmp(str1, str2, len2); return (bymem != 0 ? bymem : bylen); } /* * Compare a counted-length string to a regular C string, with or without * case sensitivity. */ inline int lib_strcmp(const char *str1, size_t len1, const char *str2) { return lib_strcmp(str1, len1, str2, strlen(str2)); } inline int lib_stricmp(const char *str1, size_t len1, const char *str2) { return lib_stricmp(str1, len1, str2, strlen(str2)); } /* ------------------------------------------------------------------------ */ /* * Limited-length strchr. Searches within the given string for the given * character; stops if we exhaust the length limit 'len' bytes or reach a * null character in the string. */ inline char *lib_strnchr(const char *src, size_t len, int ch) { /* search until we exhaust the length limit or reach a null byte */ for ( ; len != 0 && *src != '\0' ; --len, ++src) { /* if this is the character we're looking for, return the pointer */ if (*src == ch) return (char *)src; } /* didn't find it */ return 0; } /* ------------------------------------------------------------------------ */ /* * Limited-length atoi */ int lib_atoi(const char *str, size_t len); /* * Limited-length atoi, with auto-advance of the string */ int lib_atoi_adv(const char *&str, size_t &len); /* ------------------------------------------------------------------------ */ /* * Compare two strings, ignoring differences in whitespace between the * strings. Returns true if the strings are equal (other than * whitespace, false if not. * * Note that we do not ignore the *presence* of whitespace; we only * ignore differences in the amount of whitespace. For example, "login" * does not equal "log_in" (underscore = whitespace for these examples * only, to emphasize the spacing), because the first lacks whitespace * where the second has it; but "log_in" equals "log___in", because both * strings have whitespace, albeit in different amounts, in the same * place, and are otherwise the same. */ int lib_strequal_collapse_spaces(const char *a, size_t a_len, const char *b, size_t b_len); /* ------------------------------------------------------------------------ */ /* * Utility routine - compare UTF-8 strings with full Unicode case folding. * Returns <0 if a0 if a>b. * * If 'bmatchlen' is non-null, string 'a' can match as a leading substring * of string 'b'. If 'b' is identical to 'a' or contains 'a' as a leading * substring, we'll return 0 to indicate a match, and fill in '*bmatchlen' * with the number of bytes matched. * * If 'bmatchlen' is null, we'll do a straightforward string comparison, so * a 0 return means the two strings match exactly. */ int t3_compare_case_fold( const char *a, size_t alen, const char *b, size_t blen, size_t *bmatchlen); /* compare a UTF-8 string against a wchar_t* string */ int t3_compare_case_fold( const wchar_t *a, size_t alen, const char *b, size_t blen, size_t *bmatchlen); /* * Compare the minimum portions of two UTF-8 strings with case folding. * This compares the folded version of the first character of each string * to the other. If they match, we advance the pointers and lengths past * the matched text and return 0; otherwise we return < 0 if the first * string sorts before the second, > 0 if the first sorts after the second. * * In the simplest case, this matches one character from each string. * However, there are situations where the folded version of one character * can correspond to two characters of source text in the other string, * such as the German ess-zed: this will match "ss" in the source text in * the other string, consuming only one character (the ess-zed) in one * string but two ("ss") in the other. */ int t3_compare_case_fold_min(class utf8_ptr &a, size_t &alen, class utf8_ptr &b, size_t &blen); int t3_compare_case_fold_min(class utf8_ptr &a, size_t &alen, const wchar_t* &b, size_t &blen); int t3_compare_case_fold_min(const wchar_t* &a, size_t &alen, const wchar_t* &b, size_t &blen); /* ------------------------------------------------------------------------ */ /* * Find a version suffix in an identifier string. A version suffix * starts with the given character. If we don't find the character, * we'll return the default version suffix. In any case, we'll set * name_len to the length of the name portion, excluding the version * suffix and its leading separator. * * For example, with a '/' suffix, a versioned name string would look * like "tads-gen/030000" - the name is "tads_gen" and the version is * "030000". */ const char *lib_find_vsn_suffix(const char *name_string, char suffix_char, const char *default_vsn, size_t *name_len); /* ------------------------------------------------------------------------ */ /* * Unicode-compatible character classification functions. These * functions accept any Unicode character, but classify all non-ASCII * characters in the Unicode character set as unknown; hence, * is_digit(ch) will always return false for any non-ASCII character, * even if the character is considered a digit in the Unicode character * set, and to_upper(ch) will return ch for any non-ASCII character, * even if the character has a case conversion defined in the Unicode * set. * * Use the t3_is_xxx() and t3_to_xxx() functions defined vmuni.h for * classifications and conversions that operate over the entire Unicode * character set. */ /* determine if a character is an ASCII character */ inline int is_ascii(wchar_t c) { return (((unsigned int)c) <= 127); } /* determine if a character is an ASCII space */ inline int is_space(wchar_t c) { return (is_ascii(c) && isspace((char)c)); } /* determine if a character is an ASCII alphabetic character */ inline int is_alpha(wchar_t c) { return (is_ascii(c) && isalpha((char)c)); } /* determine if a character is an ASCII numeric character */ inline int is_digit(wchar_t c) { return (is_ascii(c) && isdigit((char)c)); } /* determine if a character is an ASCII octal numeric character */ inline int is_odigit(wchar_t c) { return (is_ascii(c) && isdigit((char)c) && c <= '7'); } /* determine if a character is an ASCII hex numeric character */ inline int is_xdigit(wchar_t c) { return (is_ascii(c) && (isdigit((char)c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))); } /* get the numeric value of a decimal digit character */ inline int value_of_digit(wchar_t c) { return (int)(c - '0'); } /* get the numeric value of an octal numeric character */ inline int value_of_odigit(wchar_t c) { return (int)(c - '0'); } /* get the numeric value of a hex numeric character */ inline int value_of_xdigit(wchar_t c) { /* * since our internal characters are always in unicode, we can take * advantage of the order of unicode characters to reduce the number * of comparisons we must make here */ return (int)(c >= 'a' ? c - 'a' + 10 : c >= 'A' ? c - 'A' + 10 : c - '0'); } /* convert a number 0-15 to a hex digit */ inline char int_to_xdigit(int i) { return ((i >= 0 && i < 10) ? '0' + i : (i >= 10 && i < 16 ) ? 'A' + i - 10 : '?'); } /* convert a byte to a pair of hex digits */ inline void byte_to_xdigits(char *buf, unsigned char b) { buf[0] = int_to_xdigit((b >> 4) & 0x0F); buf[1] = int_to_xdigit(b & 0x0F); } /* determine if a character is a symbol initial character */ inline int is_syminit(wchar_t c) { /* underscores and alphabetic characters can start symbols */ return (is_ascii(c) && (c == '_' || isalpha((char)c))); } /* determine if a character is a symbol non-initial character */ inline int is_sym(wchar_t c) { /* underscores, alphabetics, and digits can be in symbols */ return (is_ascii(c) && (c == '_' || isalpha((char)c) || isdigit((char)c))); } /* determine if a character is ASCII lower-case */ inline int is_lower(wchar_t c) { return (is_ascii(c) && islower((char)c)); } /* convert ASCII lower-case to upper-case */ inline wchar_t to_upper(wchar_t c) { return (is_ascii(c) ? toupper((char)c) : c); } inline wchar_t to_lower(wchar_t c) { return (is_ascii(c) ? tolower((char)c) : c); } /* convert a string to lower case */ void t3strlwr(char *p); /* ------------------------------------------------------------------------ */ /* * sprintf and vsprintf replacements. These versions provide subsets of * the full 'printf' format capabilities, but check for buffer overflow, * which the standard library's sprintf functions do not. * * NB: the 'args' parameter is effectively const, even though it's not * declared as such. That is, you can safely call t3vsprintf multiple * times with the same 'args' parameter without worrying that the contents * will be changed on platforms where va_list is a reference type. (It's * not declared const due to an implementation detail, specifically that * the routine internally needs to make a private copy with va_copy(), * which doesn't accept a const source value.) */ size_t t3sprintf(char *buf, size_t buflen, const char *fmt, ...); size_t t3vsprintf(char *buf, size_t buflen, const char *fmt, va_list args); /* * Automatic memory allocation versions of sprintf and vsprintf: we'll * measure the actual space needed, allocate a buffer, format the message * into the buffer, and return the allocated buffer pointer. The caller is * responsible for freeing the returned buffer via t3free(). */ char *t3sprintf_alloc(const char *fmt, ...); char *t3vsprintf_alloc(const char *fmt, va_list args); /* ------------------------------------------------------------------------ */ /* * Basic heap allocation functions. We don't call malloc and free * directly, but use our own cover functions; when we compile the system * for debugging, we use diagnostic memory allocators so that we can more * easily find memory mismanagement errors (such as leaks, multiple * deletes, and use after deletion). */ #define T3MALLOC_TYPE_MALLOC 1 #define T3MALLOC_TYPE_NEW 2 #define T3MALLOC_TYPE_NEWARR 3 #ifdef T3_DEBUG /* * Compiling in debug mode - use our diagnostic heap functions. * Override C++ operators new, new[], delete, and delete[] as well, so * that we can handle those allocations through our diagnostic heap * manager, too. */ void *t3malloc(size_t siz, int alloc_type); void *t3realloc(void *oldptr, size_t siz); void t3free(void *ptr, int alloc_type); inline void *t3malloc(size_t siz) { return t3malloc(siz, T3MALLOC_TYPE_MALLOC); } inline void *t3mallocnew(size_t siz) { return t3malloc(siz, T3MALLOC_TYPE_NEW); } inline void t3free(void *ptr) { t3free(ptr, T3MALLOC_TYPE_MALLOC); } void *operator new(size_t siz) SYSTHROW(throw (std::bad_alloc)); void *operator new[](size_t siz) SYSTHROW(throw (std::bad_alloc)); void operator delete(void *ptr) SYSTHROW(throw ()); void operator delete[](void *ptr) SYSTHROW(throw ()); /* * List all allocated memory blocks - displays heap information on stdout. * This can be called at program termination to detect un-freed memory * blocks, the existence of which could indicate a memory leak. * * If cb is provided, we'll display output through the given callback * function; otherwise we'll display the output directly on stderr. */ void t3_list_memory_blocks(void (*cb)(const char *msg)); #else /* T3_DEBUG */ /* * Compiling in production mode - use the system memory allocators * directly. Note that we go through the osmalloc() et. al. functions * rather than calling malloc() directly, so that individual ports can * use customized memory management where necessary or desirable. */ #define t3malloc(siz) (::osmalloc(siz)) #define t3mallocnew(siz) (::osmalloc(siz)) #define t3realloc(ptr, siz) (::osrealloc(ptr, siz)) #define t3free(ptr) (::osfree(ptr)) #define t3_list_memory_blocks(cb) #endif /* T3_DEBUG */ /* ------------------------------------------------------------------------ */ /* * A simple array list type. We keep an underlying array of elements, * automatically expanding the underlying array as needed to accomodate new * elements. */ /* array list element type codes */ #define ARRAY_LIST_ELE_INT 1 #define ARRAY_LIST_ELE_LONG 2 #define ARRAY_LIST_ELE_PTR 3 /* array list element - we can store various types here */ union array_list_ele_t { array_list_ele_t(int i) { intval = i; } array_list_ele_t(long l) { longval = l; } array_list_ele_t(void *p) { ptrval = p; } int intval; long longval; void *ptrval; /* compare to a given value for equality */ int equals(array_list_ele_t other, int typ) { return ((typ == ARRAY_LIST_ELE_INT && intval == other.intval) || (typ == ARRAY_LIST_ELE_LONG && longval == other.longval) || (typ == ARRAY_LIST_ELE_PTR && ptrval == other.ptrval)); } }; /* * The array list type */ class CArrayList { public: CArrayList() { /* we have nothing allocated yet */ arr_ = 0; cnt_ = 0; /* use default initial size and increment */ alloc_ = 16; inc_siz_ = 16; } CArrayList(size_t init_cnt, size_t inc_siz) { /* we have nothing allocated yet */ arr_ = 0; cnt_ = 0; /* remember the initial size and increment */ alloc_ = init_cnt; inc_siz_ = inc_siz; } virtual ~CArrayList() { /* delete our underlying array */ free_mem(arr_); } /* get the number of elements in the array */ size_t get_count() const { return cnt_; } /* get the element at the given index (no error checking) */ int get_ele_int(size_t idx) const { return arr_[idx].intval; } long get_ele_long(size_t idx) const { return arr_[idx].longval; } void *get_ele_ptr(size_t idx) const { return arr_[idx].ptrval; } /* find an element's index; returns -1 if not found */ int find_ele(int i) const { return find_ele(array_list_ele_t(i), ARRAY_LIST_ELE_INT); } int find_ele(long l) const { return find_ele(array_list_ele_t(l), ARRAY_LIST_ELE_LONG); } int find_ele(void *p) const { return find_ele(array_list_ele_t(p), ARRAY_LIST_ELE_PTR); } /* find an element's index; returns -1 if not found */ int find_ele(array_list_ele_t ele, int typ) const { size_t i; array_list_ele_t *p; /* scan for the element */ for (i = 0, p = arr_ ; i < cnt_ ; ++i, ++p) { /* if this is the element, return the index */ if (p->equals(ele, typ)) return (int)i; } /* didn't find it */ return -1; } /* add a new element */ void add_ele(int i) { add_ele(array_list_ele_t(i)); } void add_ele(long l) { add_ele(array_list_ele_t(l)); } void add_ele(void *p) { add_ele(array_list_ele_t(p)); } /* add a new element */ void add_ele(array_list_ele_t ele) { /* expand the array if necessary */ if (arr_ == 0) { /* we don't have an array yet, so allocate at the initial size */ init(); } if (cnt_ >= alloc_) { /* allocate at the new size */ arr_ = (array_list_ele_t *) realloc_mem(arr_, alloc_ * sizeof(arr_[0]), (alloc_ + inc_siz_) * sizeof(arr_[0])); /* remember the new size */ alloc_ += inc_siz_; } /* add the new element */ arr_[cnt_++] = ele; } /* remove one element by value; returns true if found, false if not */ void remove_ele(int i) { remove_ele(array_list_ele_t(i), ARRAY_LIST_ELE_INT); } void remove_ele(long l) { remove_ele(array_list_ele_t(l), ARRAY_LIST_ELE_LONG); } void remove_ele(void *p) { remove_ele(array_list_ele_t(p), ARRAY_LIST_ELE_PTR); } /* remove one element by value; returns true if found, false if not */ int remove_ele(array_list_ele_t ele, int typ) { size_t i; array_list_ele_t *p; /* scan for the element */ for (i = 0, p = arr_ ; i < cnt_ ; ++i, ++p) { /* if this is the element, remove it */ if (p->equals(ele, typ)) { /* remove the element at this index */ remove_ele(i); /* indicate that we found the element */ return TRUE; } } /* we didn't find the element */ return FALSE; } /* remove the element at the given index */ void remove_ele(size_t idx) { array_list_ele_t *p; /* move each following element down one slot */ for (p = arr_ + idx, ++idx ; idx < cnt_ ; ++idx, ++p) *p = *(p + 1); /* reduce the in-use count */ --cnt_; } /* clear the entire list */ void clear() { cnt_ = 0; } protected: /* * Initialize. This is called to set up the array at the initial size, * stored in alloc_, when we first need memory. Note that we defer * this until we actually need the memory for two reasons. First, we * can't call it from the constructor, because the vtable won't be * built at construction, and we need to call the virtual alloc_mem(). * Second, by waiting, we ensure that we won't allocate any memory if * our list is never actually needed. */ void init() { /* allocate the array */ arr_ = (array_list_ele_t *)alloc_mem(alloc_ * sizeof(arr_[0])); } /* memory management */ virtual void *alloc_mem(size_t siz) { return t3malloc(siz); } virtual void *realloc_mem(void *p, size_t oldsiz, size_t newsiz) { return t3realloc(p, newsiz); } virtual void free_mem(void *p) { t3free(p); } /* our array of elements */ array_list_ele_t *arr_; /* number of elements allocated */ size_t alloc_; /* number of elements currently in use */ size_t cnt_; /* increment size */ size_t inc_siz_; }; #endif /* T3_STD_INCLUDED */ qtads-2.1.7/tads3/tcerr.cpp000066400000000000000000000021641265017072300155040ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/TCERR.CPP,v 1.5 1999/07/11 00:46:52 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcerr.cpp - TADS 3 Compiler Messages Function Notes Modified 04/22/99 MJRoberts - Creation */ #include "tcerr.h" #include "tcerrnum.h" /* ------------------------------------------------------------------------ */ /* * Look up a message */ const char *tcerr_get_msg(int msgnum, int verbose) { const char *msg; /* look up the message in the compiler message array */ msg = err_get_msg(tc_messages, tc_message_count, msgnum, verbose); if (msg != 0) return msg; /* look up the message in the interpreter message array */ msg = err_get_msg(vm_messages, vm_message_count, msgnum, verbose); if (msg != 0) return msg; /* there's nowhere else to look - return failiure */ return 0; } qtads-2.1.7/tads3/tcerr.h000066400000000000000000000033331265017072300151500ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/TCERR.H,v 1.3 1999/05/17 02:52:27 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcerr.h - TADS 3 Compiler Error Management Function Notes Modified 04/22/99 MJRoberts - Creation */ #ifndef TCERR_H #define TCERR_H #include "vmerr.h" /* * Error Severity Levels. */ enum tc_severity_t { /* information only - not an error */ TC_SEV_INFO, /* * Pedantic warning - doesn't prevent compilation from succeeding, * and doesn't even necessarily indicate anything is wrong. We use * a separate severity level for these because some users will want * to be able to filter these out. */ TC_SEV_PEDANTIC, /* * warning - does not prevent compilation from succeeding, but * indicates a potential problem */ TC_SEV_WARNING, /* * error - compilation cannot be completed successfully, although it * may be possible to continue with compilation to the extent * necessary to check for any additional errors */ TC_SEV_ERROR, /* fatal - compilation must be immediately aborted */ TC_SEV_FATAL, /* internal error - compilation must be immediately aborted */ TC_SEV_INTERNAL }; /* English version of message array */ extern const err_msg_t tc_messages_english[]; extern size_t tc_message_count_english; /* error message array */ extern const err_msg_t *tc_messages; extern size_t tc_message_count; /* look up a message */ const char *tcerr_get_msg(int msgnum, int verbose); #endif /* TCERR_H */ qtads-2.1.7/tads3/tcerrmsg.cpp000066400000000000000000004456171265017072300162310ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcerrmsg.cpp - TADS 3 Compiler error message text Function Defines the error message strings for the TADS 3 compiler. The message strings are all isolated in this module to allow for easy replacement for translated versions. Notes Modified 05/13/00 MJRoberts - Creation */ #include "tcerr.h" #include "tcerrnum.h" /* ------------------------------------------------------------------------ */ /* * Error Messages - fixed English version; these can be replaced at * run-time by messages loaded from an external file, but we compile in an * English set as a fallback in case there's no message file. * * The messages must be sorted by message number, so that we can perform a * binary search to look up a message by number. */ const err_msg_t tc_messages_english[] = { { TCERR_LINE_MEM, "out of memory for line source", "Out of memory for line source. The source line may be too long, " "or you may need to make more memory available to the compiler " "by closing other applications." }, { TCERR_INV_PP_DIR, "invalid preprocessor directive", "\"%~.*s\" is not a valid preprocessor '#' directive." }, { TCERR_CANT_LOAD_CHARSET, "unable to load #charset", "Unable to load the character set specified in the #charset directive. " "Check the spelling of the character set name, and make sure that " "a character mapping file (with a name ending in \".TCM\") is " "available. Refer to the compiler's installation notes for your " "type of computer for details." }, { TCERR_UNEXPECTED_CHARSET, "unexpected or invalid #charset", "Unexpected or invalid #charset directive. This directive must " "be at the very beginning of the file, and must specify a character " "set name enclosed in double quotes." }, { TCERR_BAD_INC_SYNTAX, "syntax error in #include", "Invalid #include syntax - the filename must be enclosed with " "'\"' or '< >' characters." }, { TCERR_INC_NOT_FOUND, "cannot open include file \"%.*s\"", "The compiler cannot access the #include file \"%.*s\". " "Check the filename to ensure that it's spelled correctly, and check " "the compiler's include path setting (command line \"-I\" options) " "to ensure that the compiler is searching in the directory or folder " "containing the file. If the file exists, make sure that it's not " "being used by another application and that you have permission " "to read the file." }, { TCERR_REDUNDANT_INCLUDE, "file \"%.*s\" previously included; ignored", "The #include file \"%.*s\" has already been included. " "This redundant inclusion will be ignored." }, { TCERR_BAD_DEFINE_SYM, "invalid symbol \"%~.*s\" for #define", "Invalid symbol \"%~.*s\" for #define. A #define symbol must start " "with an ASCII letter or underscore symbol '_', and must contain " "only ASCII letters, digits, and underscores." }, { TCERR_MACRO_NO_RPAR, "missing ')' in macro parameter list", "The macro parameter list is missing a right parenthesis ')' at " "the end of the list." }, { TCERR_BAD_MACRO_ARG_NAME, "invalid macro parameter name \"%~.*s\"", "Invalid macro parameter name \"%~.*s\". A macro parameter name must " "start with an ASCII letter or underscore symbol '_', and must contain " "only ASCII letters, digits, and underscores." }, { TCERR_MACRO_EXP_COMMA, "expected ',' or ')' in macro parameter list (found \"%~.*s\")", "Expected a comma ',' or right parenthesis ')' in the macro " "parameter list, but found \"%~.*s\"." }, { TCERR_MACRO_REDEF, "redefinition of macro \"%~.*s\"", "Macro \"%~.*s\" has been previously defined; this new definition " "replaces the previous definition." }, { TCERR_UNKNOWN_PRAGMA, "unrecognized #pragma \"%~.*s\"; ignored", "Unrecognized #pragma \"%~.*s\"; ignored. (The compiler ignores " "#pragma directives that it doesn't understand, in case you're " "using a #pragma meant for another compiler, but if you meant " "the #pragma for this compiler, you need to correct a problem " "with it.)" }, { TCERR_BAD_PRAGMA_SYNTAX, "invalid syntax for #pragma", "Invalid syntax for #pragma." }, { TCERR_PP_EXTRA, "extra characters on line", "Extra characters found after the end of the preprocessor directive. " "Check the syntax and remove the extraneous characters at the " "end of the line." }, { TCERR_IF_NESTING_OVERFLOW, "#if nesting too deep", "#if/#ifdef nesting is too deep - you have specified more " "#if's within other #if's than the compiler allows. (The compiler " "has an internal limit on this nesting; you must simplify the " "nested #if structure of your source code.)" }, { TCERR_PP_ELSE_WITHOUT_IF, "#else without #if", "#else without a matching #if or #ifdef." }, { TCERR_PP_ENDIF_WITHOUT_IF, "#endif without #if", "#endif without a matching #if or #ifdef." }, { TCERR_PP_ELIF_WITHOUT_IF, "#elif without #if", "#elif without a matching #if." }, { TCERR_PP_INT_REQUIRED, "integer value required in preprocessor constant expression", "Incorrect value in preprocessor constant expression - an integer " "value is required." }, { TCERR_PP_INCOMP_TYPES, "incompatible types for comparison operator", "Incompatible types for comparison in preprocessor constant " "expression." }, { TCERR_PP_EXPR_EXTRA, "extra characters at end of line", "Extra characters found after end of preprocessor constant " "expression. Check the syntax of the expression and remove any " "extraneous characters at the end of the line." }, { TCERR_PP_DIV_ZERO, "divide by zero in constant expression", "Division by zero in preprocessor constant expression." }, { TCERR_PP_INVALID_VALUE, "expected number, symbol, or string (found \"%~.*s\")", "Expected a number, symbol, or single-quoted string in preprocessor " "constant expression, but found \"%~.*s\"." }, { TCERR_PP_UNTERM_STRING, "unterminated string in preprocessor constant expression", "Unterminated string found in a preprocessor constant expression. " "(A string in a preprocessor expression must be contained entirely " "on a single line.)" }, { TCERR_PP_UNMATCHED_LPAR, "unmatched '('", "Unmatched left parenthesis '(' in preprocessor constant expression." }, { TCERR_PP_BAD_NOT_VAL, "integer value required for '!' operator", "Integer value is required for '!' operator in preprocessor " "expression." }, { TCERR_PP_MACRO_ARG_RPAR_1LINE, "missing ')' in argument list for macro \"%~.*s\"", "Missing right parenthesis ')' in argument list in invocation of " "macro \"%~.*s\". The entire argument list must be on a single " "logical line for a line with a preprocessor directive (but you can " "extend the logical line over several physical lines by ending each " "line but the last with a backslash '\\')." }, { TCERR_PP_NO_MACRO_ARGS, "missing argument list for macro \"%~.*s\"", "An argument list must be specified in invocation of macro \"%~.*s\"." }, { TCERR_PP_FEW_MACRO_ARGS, "not enough arguments for macro \"%~.*s\"", "Not enough arguments are specified in invocation of macro \"%~.*s\"." }, { TCERR_PP_MACRO_ARG_RPAR, "missing ')' in argument list for macro \"%~.*s\"", "Missing right parenthesis ')' in argument list in " "invocation of macro \"%~.*s\"." }, { TCERR_PP_MANY_MACRO_ARGS, "too many arguments for macro \"%~.*s\"", "Too many arguments found in invocation of macro \"%~.*s\". (The " "macro was defined to take fewer arguments. Check the definition " "of the macro for proper usage, and make sure that all of the " "parentheses in the macro invocation are properly matched.)" }, { TCERR_PP_DEFINED_NO_SYM, "symbol required for defined() (found \"%~.*s\")", "Symbol required for defined() preprocessor operator " "(found \"%~.*s\" instead.)" }, { TCERR_PP_DEFINED_RPAR, "missing ')' in defined()", "Missing right parenthesis ')' in defined() preprocess operator." }, { TCERR_SYMBOL_TRUNCATED, "symbol \"%~.*s\" truncated to \"%~.*s\"", "The symbol \"%~.*s\" is too long; it has been truncated to \"%~.*s\". " "(The compiler limits the length of each symbol name; you must make " "this symbol name shorter.)" }, { TCERR_TOO_MANY_MAC_PARMS, "too many parameters for macro \"%~.*s\" (maximum %d)", "Too many formal parameters are defined for macro " "\"%~.*s\". (The compiler imposes a limit of %d parameters per macro.)" }, { TCERR_NO_STRBUF_MEM, "out of memory for string buffer", "Out of memory for string buffer. You may need to " "close other applications to make more memory available for " "the compiler." }, { TCERR_CANT_OPEN_SRC, "unable to open source file \"%.*s\"", "Unable to open source file \"%.*s\" - check that the filename " "is spelled correctly, and check that the file exists and that you " "have permission to open the file for reading." }, { TCERR_ERROR_DIRECTIVE, "#error : %.*s", "#error : %.*s" }, { TCERR_OUT_OF_MEM_MAC_EXP, "out of memory for macro expansion", "Out of memory for macro expansion. You may need to " "close other applications to make more memory available for " "the compiler." }, { TCERR_LINE_REQ_INT, "integer value required for #line", "An integer value is required for the line number in the #line " "directive." }, { TCERR_LINE_FILE_REQ_STR, "string value required for #line", "A string value is required for the filename in the #line directive." }, { TCERR_IF_WITHOUT_ENDIF, "#if without #endif (line %ld, file %.*s)", "The #if directive at line %ld of file %.*s has no matching #endif " "(a matching #endif is required in the same file as the #if)." }, { TCERR_UNSPLICE_NOT_CUR, "unsplicing invalid line", "Unsplicing invalid line." }, { TCERR_MULTI_UNSPLICE, "too much unsplicing", "Too much unsplicing." }, { TCERR_PP_ELIF_NOT_IN_SAME_FILE, "#elif without #if", "#elif without #if - an entire #if-#elif-#else-#endif sequence must " "all be contained within a single file; this #elif appears to " "correspond to a #if in the including file." }, { TCERR_PP_ELSE_NOT_IN_SAME_FILE, "#else without #if", "#else without #if - an entire #if-#elif-#else-#endif sequence must " "all be contained within a single file; this #else appears to " "correspond to a #if in the including file." }, { TCERR_PP_ENDIF_NOT_IN_SAME_FILE, "#endif without #if", "#endif without #if - an entire #if-#elif-#else-#endif sequence must " "all be contained within a single file; this #endif appears to " "correspond to a #if in the including file." }, { TCERR_REDEF_OP_DEFINED, "cannot #define reserved preprocessor symbol \"defined\"", "You cannot #define \"defined\" as a macro - this symbol is reserved " "as a preprocessor keyword and cannot be defined as a macro name." }, { TCERR_POSSIBLE_UNTERM_STR, "string appears unterminated (';' or '}' appears alone on line %ld)", "This string appears unterminated, because ';' or '}' appears on a line " "with no other non-blank characters (at line %ld) within the string. " "Check the string for proper termination; if the string is " "properly terminated and the ';' or '}' is meant to be part of the " "string, move the ';' or '}' onto the next or previous line to group " "it with at least one other non-whitespace character, so that it " "doesn't confuse the compiler" }, { TCERR_SRCLINE_TOO_LONG, "source line too long (exceeds maximum line length %ld bytes)", "This source line is too long - it exceeds the internal compiler " "limit of %ld bytes per source line. (The compiler's idea of the " "length of the logical source line may be longer than the line appears " "to be in the source file, because the compiler limit applies to the " "line after expansion of macros, assembly of all parts of any string " "that runs across several lines into a single line, and splicing of " "any lines that end in backslash characters '\\'. You must reduce " "the length of the logical source line; check in particular for any " "quoted strings that run across several lines. " }, { TCERR_INVALID_CHAR, "invalid character in input \"%~.*s\"", "Invalid character in input: \"%~.*s\". This character is not valid " "in any symbol name or as punctuation in source code; the character " "will be ignored. Check for missing quotes around a string, a missing " "ending quote for a string just before this point, or for " "a quote mark embedded within an earlier string (if you want to use " "a quote mark within a string, you must precede the quote mark with " "a backslash '\\')." }, { TCERR_PP_QC_MISSING_COLON, "missing colon ':' after conditional operator '?' in preprocessor " "expression", "The preprocessor constant conditional expression on this line is " "missing the colon ':' part. A question-mark operator '?' must be " "followed by the true-part, then a colon ':', then the false-part. " "Check the expression to ensure proper placement of parentheses, " "and check for other errors in the expression syntax. " }, { TCERR_PP_EXPR_NOT_CONST, "preprocessor expression is not a constant value", "The preprocessor expression given does not have a constant value. " "A preprocessor expression (in a #if, #elif, or #line directive) " "must use only numbers and strings, or #define symbols defined as " "numbers or strings. Preprocessor expressions cannot include " "variables, function calls, property references, or other values " "that do not have a constant value during compilation." }, { TCERR_CANT_LOAD_DEFAULT_CHARSET, "can't load mapping file for default character set \"%s\"", "The compiler cannot open the mapping file for the default " "character set, \"%s\". Make sure that a mapping file for this " "character set (with the same name as the character set plus the " "suffix \".TCM\") is properly installed on your system. Refer " "to the compiler's installation notes for your type of " "computer for details. You might need to re-install the compiler." }, { TCERR_MACRO_ELLIPSIS_REQ_RPAR, "'...' in macro formal parameter list must be followed by ')' - " "found \"%~.*s\"", "An ellipsis '...' in a macro formal parameter list can only be " "used with the last parameter to the macro, so it must be immediately " "followed by a close parenthesis ')' - the compiler found \"%~.*s\" " "instead. Check the macro definition syntax and insert the missing " "parenthesis." }, { TCERR_PP_FOREACH_TOO_DEEP, "#foreach/#ifempty/#ifnempty nesting too deep", "This line uses a macro whose expansion has too many nested uses " "of #foreach, #ifempty, and #ifnempty for its variable arguments. " "You must simplify the expansion text to reduce the nesting (in " "other words, you must reduce the number of these constructs that " "are contained within the expansion areas of others of these " "same constructs)." }, { TCERR_TRAILING_SP_AFTER_BS, "line ends with whitespace following backslash", "This line ends with one or more whitespace characters (space, tab, " "etc.) following a backslash '\\'. A backslash at the end of a line " "is most frequently used to indicate a continuation line, where " "a long preprocessor directive is broken up over several lines " "in the source file. To indicate line continuation, though, the " "backslash must be the very last character on the line; because of " "the extra whitespace after the backslash on this line, the " "preprocessor must treat the backslash as quoting the whitespace " "character that follows. If you meant to indicate line continuation, " "remove the trailing whitspace. If you actually intended to escape " "the whitespace character, you can suppress this warning by adding " "a comment (a simple '//' is adequate) at the end of the line." }, { TCERR_EXTRA_INC_SYNTAX, "extraneous characters following #include filename", "This #include line has extra characters after the name of the file. " "The only thing allowed on a #include line is the filename, enclosed " "in quotes (\"filename\") or angle brackets (). This line " "has extra characters after the filename specification. Check the " "syntax and remove the extraneous text. If the extra text is " "supposed to be a comment, make sure the comment syntax is correct." }, { TCERR_NESTED_COMMENT, "\"/*\" found within \"/* ... */\" - nested comments are not allowed", "The compiler found \"/*\" within a block comment (a \"/* ... */\" " "sequence). This type of comment cannot be nested. If you didn't " "intend to create a nested comment, the problem could be that the " "previous block comment is missing its \"*/\" end marker - you might " "want to check the previous comment to make sure it ended properly. " "If you did want to create a nested comment, consider using \"//\" " "comments instead, or you can use #if 0 ... #endif to comment out " "a large block." }, { TCERR_UNMAPPABLE_CHAR, "unmappable character in input", "The compiler encountered an \"unmappable\" character in the input. " "This is a character that's not defined as part of the source file " "character set you're using, so the compiler doesn't know how to " "interpret it. This can happen if you've declared the file to be " "in plain ASCII, via a #charset \"us-ascii\" directive, but the file " "actually contains characters outside of the plain ASCII range. " "The same can happen with the ISO-8859 (Latin-1, etc) character sets, " "since these do not define all possible character codes. Check for " "any accented letters or special symbols. If you don't see any of " "these, there might be invisible control characters or spaces causing " "the problem. If you are intentionally using accented characters, " "add a #charset directive to the start of the file to indicate the " "correct character set that your text editor is using when saving " "the source file." }, { TCERR_DECIMAL_IN_OCTAL, "decimal digit found in octal constant \"%.*s\"", "The compiler found a decimal digit (an 8 or a 9) within the octal " "constant value \"%.*s\". When you start a numeric constant with the " "digit zero (0), it signifies an octal constant - that is, a number " "written in base-8 notation. Octal numbers can only contain the " "digits 0 through 7, so you can't use an 8 or a 9 in this type of " "constant. If you didn't mean this to be interpreted as an octal " "value, simply remove the leading zero. Otherwise, remove the " "invalid digits from the octal number." }, { TCERR_LTGT_OBSOLETE, "the '<>' operator is obsolete - use '!=' instead", "The '<>' operator is obsolete - use '!=' instead. TADS 2 treated " "'<>' as equivalent to '!=', but TADS 3 doesn't allow '<>', because " "the varying operator syntax was sometimes confusing to people " "reading source code." }, { TCERR_INT_CONST_OV, "constant value exceeds integer range; promoted to BigNumber", "The numeric value specified is outside of the range that can be " "stored in the TADS integer type (-2147483648 to +2147483647), so it " "has been automatically promoted to a BigNumber (floating point) " "value. BigNumber values can represent much larger numbers than " "TADS integers, but some functions that require numeric arguments " "only accept integer values. If you're using this value in such a " "context, it might cause an error at run-time." }, { TCERR_BACKSLASH_SEQ, "invalid backslash escape sequence \\%c in string", "The backslash sequence \\%c is not valid." }, { TCERR_NON_ASCII_SYMBOL, "symbol \"%~.*s\" contains non-ASCII character (U+%04x)", "The symbol \"%~.*s\" contains one or more non-ASCII characters (the " "first is [Unicode] character U+%04x). Only plain ASCII characters " "can be used in symbols; accented letters and other non-ASCII " "characters aren't allowed." }, { TCERR_EMBEDDING_TOO_DEEP, "embedded expressions in strings are nested too deeply", "The embedded \"<< >>\" expressions in this string are nested too " "deeply. A nested embedding is a \"<< >>\" expression within " "a string that iself contains another string that has its own " "\"<< >>\" embedding, which in turn has another string with its " "own embedding, and so on. This is allowed, but only to a limited " "nesting depth. You will need to simplify the strings to reduce " "the depth." }, { TCERR_INTERNAL_EXPLAN, "Please report this error to the compiler's maintainer.", "This indicates a problem within the compiler itself. Please report " "this problem to the compiler's maintainer -- refer to the README file " "or release notes for contact information." }, { TCERR_INTERNAL_ERROR, "general internal error", "general internal error" }, { TCERR_MAKE_CANNOT_CREATE_SYM, "error creating symbol file \"%s\"", "Error creating symbol file \"%s\". Check to make sure the filename " "contains only valid characters, that the path is valid, and that " "you have the required permissions to create the file." }, { TCERR_MAKE_CANNOT_CREATE_OBJ, "error creating object file \"%s\"", "Error creating object file \"%s\". Check to make sure the filename " "contains only valid characters, that the path is valid, and that " "you have the required permissions to create the file." }, { TCERR_MAKE_CANNOT_CREATE_IMG, "error creating image file \"%s\"", "Error creating image file \"%s\". Check to make sure the filename " "contains only valid characters, that the path is valid, and that " "you have the required permissions to create the file." }, { TCERR_MAKE_CANNOT_OPEN_SYM, "error opening symbol file \"%s\" for reading", "Error opening symbol file \"%s\" for reading. Check that the " "file exists and that its name and path are valid." }, { TCERR_MAKE_CANNOT_OPEN_OBJ, "error opening object file \"%s\" for reading", "Error opening object file \"%s\" for reading. Check that the " "file exists and that its name and path are valid." }, { TCERR_MAKE_CANNOT_OPEN_IMG, "error opening image file \"%s\" for reading", "Error opening image file \"%s\" for reading. Since this is an " "intermediate file created during the build process, this probably " "indicates a problem with the filename path, directory permissions, " "or disk space." }, { TCERR_TOO_MANY_ERRORS, "too many errors (try fixing the first couple of errors and recompiling)", "Too many errors - aborting compilation. Don't panic! Your source " "code probably doesn't have nearly as many errors as the compiler " "is reporting. In all likelihood, the compiler got tripped up after " "the first couple of errors and hasn't been able to get itself " "re-synchronized with your code - it thinks there are errors only " "because it's trying to interpret the code in the wrong context. You " "should simply fix the first few errors, then try compiling your " "program again - chances are the compiler will get a lot further " "if you fix just the first few errors. If you want, you can save a " "copy of the current source code and send it to TADS's author, who " "might be able to make the compiler a little smarter about dealing " "with whatever is confusing it so badly." }, { TCERR_MODULE_NAME_COLLISION, "module %s the has same name as an existing module", "The module \"%s\" has the same name as an existing module elsewhere " "in the project. The root filename of each module must be unique, " "because the two modules' object files might otherwise overwrite one " "another. You must change the name of one of the modules so that " "each module has a unique name." }, { TCERR_MODULE_NAME_COLLISION_WITH_LIB, "module %s has the same name as an existing module from library \"%s\"", "The module %s has the same name as an existing module included " "from the library \"%s\". The root filename of each module must be " "unique, even for files included from libraries. If more than one " "module has the same root name, the modules' object files would " "overwrite one another. You must change the name of this module " "to a name not used anywhere else in the project." }, { TCERR_SOURCE_FROM_LIB, "\"%s\" (from library \"%s\")", "\"%s\" (from library \"%s\")" }, { TCERR_CANNOT_CREATE_DIR, "unable to create directory \"%s\"", "An error occurred creating the directory/folder \"%s\". This might " "mean that the name contains invalid characters, the disk is " "full, or you don't have the necessary permissions or privileges " "to create a new file in the parent folder." }, { TCERR_CONST_DIV_ZERO, "divide by zero in constant expression", "Division by zero in constant expression. (The compiler was trying " "to calculate the value of this expression because it appears to be " "constant. Check for any macros that might expand to zero, and check " "for proper placement of parentheses.)" }, { TCERR_NO_MEM_PRS_TREE, "out of memory - cannot allocate parse tree block", "Out of memory. (The compiler was trying to allocate space for a " "\"parse tree block,\" which stores an intermediate form of your " "program source code during compilation. If possible, make more " "memory available to the compiler by closing other applications or " "reconfiguring any background tasks, services, or device drivers " "that can be changed to use less memory.)" }, { TCERR_PRS_BLK_TOO_BIG, "parse block too big (size=%ld)", "Parse block too big (size=%ld)." }, { TCERR_INVALID_LVALUE, "invalid lvalue - cannot assign to expression on left of \"%s\"", "Invalid \"lvalue\". The expression on the left-hand side of the " "operator \"%s\" cannot be used as the destination of an assignment. " "You can only assign values to expressions such as local variables, " "object properties, and list elements. " "Check for missing or extra parentheses and operators. " }, { TCERR_QUEST_WITHOUT_COLON, "missing ':' in '?' conditional expression", "The ':' part of a '?' conditional expression is missing. A '?' " "expression uses the syntax (condition ? true-part : false-part). " "Check for missing parentheses or other errors in the " "condition or true-part expression." }, { TCERR_INVALID_UNARY_LVALUE, "invalid lvalue - cannot apply \"%s\" operator to expression", "The \"%s\" operator cannot be applied to this expression. This " "operator can only be applied to an expression that can be used in " "an assignment, such as local variables, object properties, and " "list elements. Check for missing or extra parentheses." }, { TCERR_DELETE_OBSOLETE, "operator 'delete' is obsolete; expression has no effect", "The 'delete' operator is obsolete. The compiler still accepts " "'delete' expressions, but they have no effect at run-time. (The " "run-time system now provides automatic deletion of objects as " "they become unreachable, so explicit deletion is no longer " "necessary. You can simply remove all 'delete' expressions from " "your program.)" }, { TCERR_NO_ADDRESS, "invalid address expression - can't apply '&' operator", "The unary '&' operator cannot be applied to this expression. You " "can only apply the '&' prefix operator to function names and property " "names. Check the expression after the '&' to make sure it is a valid " "function or property name, and check for other expression errors, " "such as unbalanced parentheses." }, { TCERR_CONST_UNARY_REQ_NUM, "unary '%s' operator requires numeric value in constant expression", "The '%s' operator must be followed by a numeric value in a constant " "expression. The value after the operator is a constant value, but " "is not a number." }, { TCERR_CONST_BINARY_REQ_NUM, "binary '%s' operator requires numeric value in constant expression", "The '%s' operator must be applied only to numeric values in a constant " "expression. Both of the values are constants, but both are not " "numbers." }, { TCERR_CONST_BINPLUS_INCOMPAT, "incompatible constant types for two-operand '+' operator", "The constant types in this expression are not compatible for use " "with the two-operand '+' operator. You can add two numbers, or add a " "non-list value to a string, or add a value to a list; other " "combinations are not allowed." }, { TCERR_EXPR_MISSING_RPAR, "expected ')' but found \"%~.*s\"", "This expression is missing a right parenthesis ')' - \"%~.*s\" is " "where the ')' should go. The parenthesis is required to " "match an earlier left parenthesis '('. Check to make sure the " "parentheses are properly balanced, and check for unterminated " "strings and missing operators." }, { TCERR_BAD_PRIMARY_EXPR, "expected integer, string, symbol, '[', or '(', but found \"%~.*s\"", "Invalid expression; expected an integer value, a string value (in " "single or double quotes), a symbolic name (such as a function, " "object, or property name), a list constant enclosed in square " "brackets '[ ]', or an expression in parentheses '( )', but " "found \"%~.*s\"." }, { TCERR_CONST_BAD_COMPARE, "incompatible types for comparison in constant expression", "This constant expression contains a comparison operator ('<', '<=', " "'>', or '>=') that attempts to compare values of incompatible types " "(you can compare an integer to an integer, a string to a string, " "or a floating-point value to another floating point value). Check " "the expression and correct the invalid comparison." }, { TCERR_EXPECTED_SEMI, "expected ';', but found \"%~.*s\"", "Expected a semicolon ';' but found \"%~.*s\". Please add the " "required semicolon. If a semicolon is already present, check " "for unbalanced parentheses or other expression errors." }, { TCERR_EXPECTED_STR_CONT, "expected '>>' and the string continuation, but found \"%~.*s\"", "Expected '>>' after the embedded expression, followed by the " "continuation of the string, but found \"%~.*s\" instead. Check the " "embedded expression (between '<<' and '>>') for errors, such as " "unbalanced parentheses, and check that the string is properly " "continued after a '>>' sequence." }, { TCERR_EXPECTED_ARG_COMMA, "expected ',' or ')' in argument list, but found \"%~.*s\"", "Expected a comma ',' or right parenthesis ')' in an argument list, " "but found \"%~.*s\". Arguments must be separated by commas, and " "the entire list must be enclosed in parentheses '( )'. Check for " "errors and unbalanced parentheses in the argument expression." }, { TCERR_EXPECTED_SUB_RBRACK, "expected ']' at end of subscript, but found \"%~.*s\"", "Expected a right square bracket ']' at the end of the subscript " "(index) expression, but found \"%~.*s\" instead. A ']' is required " "to match the '[' at the start of the subscript. Check for errors " "and unbalanced parentheses in the subscript expression." }, { TCERR_INVALID_PROP_EXPR, "expected property name or parenthesized expression after '.', " "found \"%~.*s\"", "Expected a property name or a parenthesized expression (which must " "evaluate at run-time to a property address value) after '.', but " "found \"%~.*s\", which is not a valid property-valued expression." }, { TCERR_LIST_MISSING_RBRACK, "expected ']' or a list element, but found \"%~.*s\"", "Expected to find a list element expression or a right square " "bracket ']' ending the list, but found \"%~.*s\" instead. " "The compiler will assume that this is the end of the list. " "Please insert the missing ']', or check for errors in the list " "expressions." }, { TCERR_LIST_EXTRA_RPAR, "found extraneous ')' in list; ignored", "Found an extra right parenthesis ')' where a list element " "expression or a right square bracket ']' ending the list should be. " "The compiler will assume that the ')' is extraneous and will " "ignore it. Please remove the extra ')' or check the list " "for unbalanced parentheses or other expression errors." }, { TCERR_LIST_EXPECT_COMMA, "expected ',' separating list elements, but found \"%~.*s\"", "A comma ',' must be used to separate each element of a list. " "The compiler found \"%~.*s\" where a comma should be. Please insert " "the missing comma between the list elements, or check for an " "error in the preceding list element expression." }, { TCERR_CONST_IDX_NOT_INT, "index value must be an integer in constant list index expression", "The list index expression has a constant value, but the list index " "is not a number. A list index must have a numeric value. Check " "the expression in the square brackets '[ ]' and correct any errors." }, { TCERR_CONST_IDX_RANGE, "index value out of range in constant list index expression", "The list index expression is out of range for the list. The index " "value must be a number from 1 to the number of elements in the " "list. Check the expression in the square brackets '[ ]' and " "correct the value." }, { TCERR_UNTERM_STRING, "unterminated string literal: string started with %c%~.*s%c", "Unterminated string. The string starting with %c%~.*s%c does not have a " "matching close quote before the end of the file. Please insert the " "missing quote mark. If the string looks properly terminated, check " "the previous string (or the previous few strings), since an unbalanced " "quote mark in an earlier string can sometimes propagate to later " "strings." }, { TCERR_EXPECTED_ARG_RPAR, "expected ')' at end of argument list, but found \"%~.*s\"", "Expected a right parenthesis ')' at the end of an argument list, " "but found \"%~.*s\". The compiler is assuming that this is the end " "of the statement. Please insert the missing parenthesis, or check " "the argument list for unbalanced parentheses or other errors." }, { TCERR_EXTRA_RPAR, "unexpected ')' found - ignored", "The expression contains an unbalanced right parenthesis ')'. " "The compiler will ignore the extra ')'. Remove the extra ')', " "or check the expression for other errors." }, { TCERR_EXTRA_RBRACK, "unexpected ']' found - ignored", "The expression contains an unbalanced right square bracket ']'. " "The compiler will ignore the extra ']'. Remove the extra ']', " "or check the expression for other errors." }, { TCERR_EXPECTED_OPERAND, "expected an operand, but found \"%~.*s\"", "The expression is missing an operand value - the compiler found " "\"%~.*s\" instead of a valid operand. Please check the expression and " "correct the error." }, { TCERR_PROPSET_REQ_STR, "expected property name pattern string after 'propertyset' - " "found \"%~.*s\"", "A property name pattern string, enclosed in single quotes, is " "required after the 'propertyset' keyword, but the compiler found " "\"%~.*s\" instead. Add the missing pattern string." }, { TCERR_INH_CLASS_SYNTAX, "\"inherited superclass\" syntax - expected '.', found \"%~.*s\"", "Invalid syntax in \"inherited class\" expression. The class name " "must be followed by '.', but the compiler found \"%~.*s\" instead. " "Check and correct the syntax." }, { TCERR_UNDEF_SYM, "undefined symbol \"%~.*s\"", "The symbol \"%~.*s\" is not defined. Check the spelling of the " "symbol name, and make sure that the corresponding local variable, " "object, or function definition is entered correctly. This error " "could be the result of a syntax error in the original declaration " "of the symbol; if the declaration has an error, correct that error " "first, then try recompiling." }, { TCERR_ASSUME_SYM_PROP, "undefined symbol \"%~.*s\" - assuming this is a property name", "The symbol \"%~.*s\" is undefined, but appears from context to be " "a property name. The compiler is assuming that this is a property. " "Check the spelling of the symbol. If this assumption is correct, " "you can avoid this warning by explicitly declaring a value to the " "property in an object definition rather than in method code." }, { TCERR_CONST_BINMINUS_INCOMPAT, "incompatible constant types for two-operand '-' operator", "The constant types in this expression are not compatible for use " "with the two-operand '-' operator. You can subtract one number " "from another, or subtract a value from a list; other combinations " "are not allowed." }, { TCERR_REQ_SYM_FORMAL, "expected a symbol in formal parameter list, but found \"%~.*s\"", "A symbol name is required for each formal parameter. The compiler " "found \"%~.*s\" instead of a symbol for the parameter name." }, { TCERR_REQ_COMMA_FORMAL, "expected a comma in formal parameter list, but found \"%~.*s\"", "The formal parameter list is missing a comma between two parameter " "names - a comma should come before \"%~.*s\" in the parameter list." }, { TCERR_MISSING_LAST_FORMAL, "missing parameter name at end of formal parameter list", "The last parameter name in the formal parameter list is missing. " "Insert a parameter name before the ')', or remove the extra comma " "at the end of the list." }, { TCERR_MISSING_RPAR_FORMAL, "missing right parenthesis ')' at end of formal parameter list - " "found \"%~.*s\"", "The right parenthesis ')' at the end of the formal parameter list " "is missing. The parenthesis should come before \"%~.*s\". " }, { TCERR_FORMAL_REDEF, "formal parameter \"%~.*s\" defined more than once", "The formal parameter name \"%~.*s\" is defined more than once in the " "parameter list. Each parameter name can be used only once in the " "same list. Remove the redundant variable, or change its name." }, { TCERR_EQ_WITH_METHOD_OBSOLETE, "'=' is not allowed with a method definition", "An equals sign '=' is not allowed in a method definition. You can " "only use '=' when defining a simple value for a property, not to " "define method code. (TADS 2 used '=' in methods, but this syntax is " "now obsolete.) Remove the '='." }, { TCERR_REQ_LBRACE_CODE, "expected '{' at start of method code body, but found \"%~.*s\"", "An open brace '{' was expected before a method's program code body, " "but the compiler found \"%~.*s\" instead." }, { TCERR_EOF_IN_CODE, "unexpected end of file in code block - '}' missing", "The compiler reached the end of the file before the current function " "or method was finished. A close brace '}' is probably missing - " "insert the close brace at the end of the function or method." }, { TCERR_REQ_LPAR_IF, "expected '(' after \"if\", but found \"%~.*s\"", "An open parenthesis '(' is required after the keyword \"if\" - " "the compiler found \"%~.*s\" instead. The compiler will assume " "that a parenthesis was intended. Please correct the syntax " "by inserting a parenthesis." }, { TCERR_MISPLACED_ELSE, "misplaced \"else\" - no corresponding \"if\" statement", "This \"else\" clause is invalid because it is not properly associated " "with an \"if\" statement. Most likely, this is because the group of " "statements following the \"if\" is not properly enclosed in braces " "'{ }', or because the braces just before the \"else\" aren't properly " "balanced, or because there are too many or too few semicolons ';' after " "the statement following the \"if\" and before the \"else\". Check " "braces to make sure they're properly balanced, and check the statement " "or statements before the \"else\" for proper syntax, especially " "for the correct number of terminating semicolons." }, { TCERR_MISPLACED_CASE, "misplaced \"case\" keyword - not in a \"switch\" statement", "This \"case\" clause is invalid because it is not part of a \"switch\" " "statement. The most likely cause is that braces before this \"case\" " "keyword aren't properly balanced. \"case\" labels must be enclosed " "directly by the \"switch\" - they cannot be enclosed within statements " "or braces inside the \"switch\". Check code preceding the \"case\" " "clause for proper syntax and balanced braces." }, { TCERR_MISPLACED_DEFAULT, "misplaced \"default\" keyword - not in a \"switch\" statement", "This \"default\" clause is invalid because it is not part of a " "\"switch\" statement. The most likely cause is that braces before " "this \"default\" keyword aren't properly balanced. A \"default\" " "label must be enclosed directly by the \"switch\" - it cannot be " "enclosed within a statement or braces within the \"switch\" body. " "Check code preceding the \"default\" clause for proper syntax " "and balanced braces." }, { TCERR_ELLIPSIS_NOT_LAST, "'...' cannot be followed by additional formal parameters", "An ellipsis '...' cannot be followed by additional parameters " "in an argument list. Move the '...' to the end of the parameter " "list, or remove the extraneous parameters after the ellipsis." }, { TCERR_LOCAL_REQ_COMMA, "expected ',' or ';' after local variable, but found \"%~.*s\"", "The compiler expected a comma ',' or semicolon ';' after a local " "variable declaration, but found \"%~.*s\" instead. If you're " "defining an additional variable, add a comma before the additional " "variable; if not, check for a missing semicolon." }, { TCERR_LOCAL_REQ_SYM, "expected symbol name in local variable declaration, but found \"%~.*s\"", "A symbol name is required in the local variable declaration, " "but the compiler found \"%~.*s\" instead. Check the syntax " "and correct the error." }, { TCERR_FUNC_REQ_SYM, "expected symbol after 'function', but found \"%~.*s\"", "A symbol name is required after the 'function' keyword, but the " "compiler found \"%~.*s\" instead. Check the function definition " "syntax." }, { TCERR_REQ_CODE_BODY, "expected ';', '(', or '{', but found \"%~.*s\"", "The compiler expected a left parenthesis '(' starting a formal " "parameter list, a left brace '{' starting a code body, or a semicolon " "';' terminating the statement, but found \"%~.*s\". Check the function " "definition syntax." }, { TCERR_REQ_FUNC_OR_OBJ, "expected function or object definition, but found \"%~.*s\"", "The compiler expected a function or object definition, but " "found \"%~.*s\". Check the syntax, and check for unbalanced " "braces '{ }' and other syntax errors preceding this line." }, { TCERR_RET_REQ_EXPR, "expected ';' or expression after \"return\", but found \"%~.*s\"", "The \"return\" keyword must be followed by an expression giving the " "value to return, or by a semicolon ';' if there is no value to " "return; the compiler found \"%~.*s\" instead. Check the syntax, and " "insert the missing semicolon or expression as appropriate." }, { TCERR_UNREACHABLE_CODE, "unreachable statement", "This statement cannot be reached, because the previous statement " "returns or throws an exception. This code will never be executed. " "Check the logic to determine if the code is necessary; if not, " "remove the code. If the code is necessary, you must determine " "why the code is unreachable and correct the program logic." }, { TCERR_RET_VAL_AND_VOID, "code has \"return\" statements both with and without values", "This code has \"return\" statements both with and without values. " "A function or method's \"return\" statements should consistently " "return values or not; these should not be mixed in a single " "function or method, because callers will not have predictable " "results when using the function's return value." }, { TCERR_RET_VAL_AND_IMP_VOID, "code has \"return\" with value but also falls off end", "This code has one or more \"return\" statements that explicitly " "return a value from the function, but also \"falls off\" the end " "of the function without a \"return\" statement, which will result " "in a return without a value. The last statement in the function " "or method should be a \"return\" with a value, for consistency " "with the other \"return\" statements." }, { TCERR_REQ_INTRINS_NAME, "expected function set name string after \"intrinsic\", " "but found \"%~.*s\"", "The \"intrinsic\" keyword must be followed by the global name of the " "function set, enclosed in single-quotes, but the compiler found " "\"%~.*s\" instead. Check the \"intrinsic\" statement syntax." }, { TCERR_REQ_INTRINS_LBRACE, "expected '{' after intrinsic name, but found \"%~.*s\"", "The function set listing for an \"intrinsic\" statement must be " "closed in braces '{ }'. The compiler found \"%~.*s\" after the " "function set name, where the open brace '{' should be. Check the " "syntax." }, { TCERR_EOF_IN_INTRINS, "end of file in \"intrinsic\" list - '}' is probably missing", "The compiler reached the end of the file while still scanning " "an \"intrinsic\" statement's function set listing. The closing " "brace '}' of the function set list is probably missing; check " "for the missing brace." }, { TCERR_REQ_INTRINS_LPAR, "expected '(' after function name in intrinsic list, but found \"%~.*s\"", "An open parenthesis '(' is required after the name of a function " "in an intrinsic function list, but the compiler found \"%~.*s\" " "instead. Check the syntax and insert the missing parenthesis." }, { TCERR_REQ_INTRINS_SYM, "expected function name in intrinsic list, but found \"%~.*s\"", "The compiler expected the name of a function in the intrinsic " "function list, but found \"%~.*s\" instead. Check the syntax of " "the statement, and check for unbalanced parentheses and braces." }, { TCERR_REQ_FOR_LPAR, "expected '(' after \"for\", but found \"%~.*s\"", "An open parenthesis '(' is required after the \"for\" keyword, " "but the compiler found \"%~.*s\" instead. Check the syntax " "and insert the missing parenthesis." }, { TCERR_LOCAL_REDEF, "local variable \"%~.*s\" defined more than once", "The local variable name \"%~.*s\" is defined more than once in this " "scope. Each local variable name can be used only once at the same " "level of braces '{ }'. Remove the redundant variable, or change its " "name." }, { TCERR_REQ_FOR_LOCAL_INIT, "initializer expected after local variable name in \"for\", but found " "\"%~.*s\"", "A local variable defined within a \"for\" statement's initialization " "clause requires an initializer expression. The compiler expected to " "find an assignment operator after the local variable name, but found " "\"%~.*s\" instead. Check the syntax, and add the missing initializer " "to the local variable definition." }, { TCERR_MISSING_FOR_INIT_EXPR, "missing expression after comma in \"for\" initializer", "An expression must follow a comma in a \"for\" initializer list. " "Check the expression, and remove the extra comma before the " "semicolon, or supply the missing expression." }, { TCERR_MISSING_FOR_PART, "missing expression in \"for\" statement - expected ';', found '%~.*s'", "A \"for\" statement requires three expressions, separated by " "semicolons ';'. This statement doesn't seem to have all of the required " "parts - it ends unexpectedly at \"%~.*s\". Add the missing " "parts or correct the syntax." }, { TCERR_REQ_FOR_INIT_COMMA, "expected ',' or ';' in \"for\" initializer, but found \"%~.*s\"", "A comma ',' or semicolon ';' was expected in the \"for\" statement's " "initializer list, but the compiler found \"%~.*s\" instead. Check " "the expression syntax." }, { TCERR_REQ_FOR_COND_SEM, "expected ';' after \"for\" condition, but found \"%~.*s\"", "A semicolon ';' was expected after the \"for\" statement's " "condition expression, but the compiler found \"%~.*s\" instead. " "Check the expression syntax." }, { TCERR_REQ_FOR_RPAR, "missing ')' at end of \"for\" expression list - found \"%~.*s\"", "A closing parenthesis ')' is required at the end of the \"for\" " "statement's expression list, but the compiler found \"%~.*s\" instead. " "Check the syntax, and insert the missing parenthesis, or correct " "unbalanced parentheses or other syntax errors earlier in the " "expression list." }, { TCERR_FOR_COND_FALSE, "\"for\" condition is always false - body and reinitializer are " "unreachable", "The condition of this \"for\" statement is always false, so the " "body and reinitialization expression will never be executed." }, { TCERR_REQ_WHILE_LPAR, "missing '(' after \"while\" - found \"%~.*s\"", "An open parenthesis '(' is required after the \"while\" keyword, but " "the compiler found \"%~.*s\" instead. Check the syntax and insert " "the missing parenthesis." }, { TCERR_REQ_WHILE_RPAR, "missing ')' after \"while\" expression - found \"%~.*s\"", "A close parenthesis ')' is required after the expression condition " "in a \"while\" statement, but the compiler found \"%~.*s\" instead. " "Check the syntax and insert the missing parenthesis." }, { TCERR_WHILE_COND_FALSE, "\"while\" condition is always false - loop body is unreachable", "The condition of this \"while\" statement is always false, so the " "body of the loop will never be executed." }, { TCERR_REQ_DO_WHILE, "expected \"while\" in \"do\" statement, but found \"%~.*s\"", "This \"do\" statement is missing the required \"while\" keyword " "immediately after the loop body. The compiler found \"%~.*s\" where " "the \"while\" keyword shuld be. Check the syntax and insert the " "missing \"while\" keyword." }, { TCERR_MISPLACED_CATCH, "misplaced \"catch\" clause - must be associated with \"try\"", "This \"catch\" clause is invalid because it is not part of a \"try\"" " statement. The most likely cause is that braces before this \"catch\"" " keyword aren't properly balanced. Check braces preceding the " "\"catch\" clause for proper syntax." }, { TCERR_MISPLACED_FINALLY, "misplaced \"finally\" clause - must be associated with \"try\"", "This \"finally\" clause is invalid because it is not part of a \"try\"" " statement. The most likely cause is that braces before this " "\"finally\" keyword aren't properly balanced. Check braces " "preceding the \"finally\" clause for proper syntax." }, { TCERR_REQ_SWITCH_LPAR, "missing '(' after \"switch\" - found \"%~.*s\"", "An open parenthesis '(' is required after the \"switch\" keyword, but " "the compiler found \"%~.*s\" instead. Check the syntax and insert " "the missing parenthesis." }, { TCERR_REQ_SWITCH_RPAR, "missing ')' after \"switch\" expression - found \"%~.*s\"", "A close parenthesis ')' is required after the controlling expression " "of a \"switch\" statement, but the compiler found \"%~.*s\" instead. " "Check the syntax and insert the missing parenthesis." }, { TCERR_REQ_SWITCH_LBRACE, "missing '{' after \"switch\" expression - found \"%~.*s\"", "An open brace '{' is required after the controlling expression " "of a \"switch\" statement, but the compiler found \"%~.*s\" instead. " "The body of the \"switch\" must be enclosed in braces '{ }'. " "Check the syntax and insert the missing parenthesis." }, { TCERR_UNREACHABLE_CODE_IN_SWITCH, "code before first \"case\" or \"default\" label in \"switch\" is " "not allowed", "This statement precedes the first \"case\" or \"default\" label " "in the \"switch\" body - all code within a \"switch\" body must " "be reachable from a \"case\" or \"default\" label. Even \"local\" " "declarations must be within a labelled section of the \"switch\" " "body. Check for a missing \"case\" label, or move the code so that " "it is outside the \"switch\" body or after a \"case\" label." }, { TCERR_EOF_IN_SWITCH, "end of file in \"switch\" body", "End of file found in \"switch\" body. The \"switch\" body's " "braces '{ }' are probably not properly balanced. Check the code " "within the \"switch\" body for unbalanced braces and other errors." }, { TCERR_CODE_LABEL_REDEF, "code label \"%~.*s\" already defined in this function or method", "The code label \"%~.*s\" is already defined in this function or " "method. A code label can be used only once within each function " "or method; code labels always have function- or method-level scope, " "even when they're nested within braces. Change the name of one of " "the conflicting labels so that the two labels have different names." }, { TCERR_REQ_CASE_COLON, "missing ':' after \"case\" expression - found \"%~.*s\"", "A colon ':' is required after the \"case\" expression, but the " "compiler found \"%~.*s\" instead. Check the expression for errors, " "and insert the missing colon after the expression." }, { TCERR_CASE_NOT_CONSTANT, "\"case\" expression has a non-constant value", "The expression in this \"case\" label does not have a constant " "value. \"case\" expressions must always be constants or expressions " "involving only constants. Check the expression and remove " "references to local variables, object properties, or any other " "non-constant values." }, { TCERR_REQ_DEFAULT_COLON, "missing ':' after \"default\" - found \"%~.*s\"", "A colon ':' is required after the \"default\" keyword, but the " "compiler found \"%~.*s\" instead. Insert the missing colon " "after the keyword." }, { TCERR_DEFAULT_REDEF, "this \"switch\" already has a \"default:\" label", "This \"switch\" statement already has a \"default:\" label. A " "\"switch\" statement can have at most one \"default:\" label. Remove " "the redundant label." }, { TCERR_TRY_WITHOUT_CATCH, "\"try\" statement has no \"catch\" or \"finally\" clauses", "This \"try\" statement has no \"catch\" or \"finally\" clauses. A " "\"try\" statement must have at least one such clause, since it is " "otherwise superfluous. Check for unbalanced braces in the body of " "the \"try\" block." }, { TCERR_REQ_CATCH_LPAR, "missing '(' after \"catch\" - found \"%~.*s\"", "An open parenthesis '(' is required after the \"catch\" keyword, but " "the compiler found \"%~.*s\" instead. Check the syntax and insert " "the missing parenthesis." }, { TCERR_REQ_CATCH_RPAR, "missing ')' after \"catch\" variable name - found \"%~.*s\"", "A close parenthesis ')' is required after the variable name " "of a \"switch\" clause, but the compiler found \"%~.*s\" instead. " "Check the syntax and insert the missing parenthesis." }, { TCERR_REQ_CATCH_CLASS, "expected class name in \"catch\" clause - found \"%~.*s\"", "The class name of the exception to catch is required in the " "\"catch\" clause, but the compiler found \"%~.*s\" instead. Check " "and correct the syntax." }, { TCERR_REQ_CATCH_VAR, "expected variable name in \"catch\" clause - found \"%~.*s\"", "The name of a local variable (which can either be an existing " "variable or can be a new variable implicitly defined by this use) " "is required in the \"catch\" clause, but the compiler found \"%~.*s\" " "instead. Check and correct the syntax." }, { TCERR_CATCH_VAR_NOT_LOCAL, "\"%~.*s\" is not a local variable, so is not a valid \"catch\" target " "variable", "The symbol \"%~.*s\" is defined as something other than a local " "variable, so it cannot be used as the target of this \"catch\" clause. " "Check for a conflicting symbol, and either remove the conflicting " "symbol or rename the \"catch\" variable." }, { TCERR_BREAK_REQ_LABEL, "label name expected after \"break\", but found \"%~.*s\"", "A label name was expected after the \"break\" keyword, but the " "compiler found \"%~.*s\" instead. This might indicate that a " "semicolon after \"break\" is missing. Check the syntax." }, { TCERR_CONT_REQ_LABEL, "label name expected after \"continue\", but found \"%~.*s\"", "A label name was expected after the \"continue\" keyword, but the " "compiler found \"%~.*s\" instead. This might indicate that a " "semicolon after \"continue\" is missing. Check the syntax." }, { TCERR_GOTO_REQ_LABEL, "label name expected after \"goto\", but found \"%~.*s\"", "A label name was expected after the \"goto\" keyword, but the " "compiler found \"%~.*s\" instead. Check the syntax and insert " "the missing label name." }, { TCERR_REDEF_AS_FUNC, "symbol \"%~.*s\" is already defined - can't redefine as function", "The symbol \"%~.*s\" is already defined, so you cannot use it " "as the name of a function here. This symbol is already being " "used as the name of an object or property elsewhere " "in your program. Change the name to a unique symbol." }, { TCERR_INVAL_EXTERN, "invalid \"extern\" type specifier \"%~.*s\"", "Invalid keyword \"%~.*s\" following \"extern\". The \"extern\" " "keyword must be followed by \"function\" or \"object\" to indicate " "the type of the external symbol to declare." }, { TCERR_EXTERN_NO_CODE_BODY, "code body is not allowed in an \"extern function\" declaration", "This \"extern function\" declaration is not valid because it has " "a left brace introducing a code body after the function prototype. " "An extern function declaration can have only the prototype, because " "it specifies that the actual code body of the function is defined " "in another module. Either remove the \"extern\" keyword or remove " "the code body." }, { TCERR_FUNC_REDEF, "function \"%~.*s\" is already defined", "The function \"%~.*s\" is already defined earlier in the program. " "Each function must have a unique name. Remove the redundant " "definition or change the name of one of the functions." }, { TCERR_INCOMPAT_FUNC_REDEF, "function \"%~.*s\" has an incompatible previous declaration", "The function \"%~.*s\" has an incompatible declaration previously " "in the program. The earlier definition could be from an \"extern\" " "declaration in this source file, or it could come from a symbol " "file for another module in your program. Each definition of " "a function must declare the same parameter list for the function. " "Check for other declarations of this function and correct the " "inconsistency." }, { TCERR_OBJDEF_REQ_COLON, "expected ':' after object name in object definition, but found \"%~.*s\"", "A colon ':' is required after the object name in an object " "definition, but the compiler found \"%~.*s\" instead. Check the " "object syntax and insert the missing colon." }, { TCERR_OBJDEF_REQ_SC, "expected superclass name in object definition, but found \"%~.*s\"", "The name of a superclass was expected in the object definition, " "but the compiler found \"%~.*s\". Check the object syntax; add or " "correct the superclass name, or correct other syntax errors." }, { TCERR_OBJDEF_OBJ_NO_SC, "superclasses cannot be specified with \"object\" as the base class", "This object specifies \"object\" as the base class, but also lists " "named superclasses. This is not valid -- a basic \"object\" " "definition cannot also specify named superclasses." }, { TCERR_REDEF_AS_OBJ, "symbol \"%~.*s\" is already defined - can't redefine as object", "The symbol \"%~.*s\" is already defined, so you cannot use it " "as the name of an object here. This symbol is already being " "used as the name of a function or property elsewhere " "in your program. Change the name to a unique symbol." }, { TCERR_OBJ_REDEF, "object \"%~.*s\" is already defined - can't redefine as object", "The object \"%~.*s\" is already defined earlier in the program. " "Each object must have a unique name. Remove the redundant " "definition or change the name of one of the objects." }, { TCERR_OBJDEF_REQ_PROP, "expected property name in object definition, but found \"%~.*s\"", "A property name was expected in the object definition, but the " "compiler found \"%~.*s\" instead. Check for a missing semicolon at the " "end of the object definition, and check for unbalanced " "braces prior to this line." }, { TCERR_REDEF_AS_PROP, "symbol \"%~.*s\" is already defined - can't redefine as property", "The symbol \"%~.*s\" is already defined, so you cannot use it as " "the name of a property here. The symbol is already being " "used as the name of an object or function elsewhere in the program. " "Change the name to a unique symbol." }, { TCERR_OBJDEF_REQ_PROPVAL, "expected property value or method after property name \"%~.*s\", " "but found \"%~.*s\"", "A property value or method was expected after the property " "name \"%~.*s\", but the compiler found \"%~.*s\". Check the syntax " "and supply a property value expression or method code in braces '{ }'."}, { TCERR_REPLACE_REQ_OBJ_OR_FUNC, "expected \"function\" or object name after \"replace\", but found " "\"%~.*s\"", "The keyword \"replace\" must be followed by \"function\" or by an " "object name, but the compiler found \"%~.*s\" instead." }, { TCERR_REPMODOBJ_UNDEF, "replace/modify cannot be used with an object not previously defined", "The \"replace\" and \"modify\" keywords cannot be used with an object " "that is not previously defined. The object must at least be defined " "as an \"extern\" object before it can be replaced or modified." }, { TCERR_DQUOTE_IN_EXPR, "a double-quoted string (\"%~.*s\") is not valid within an expression", "The double-quoted string value (\"%~.*s\") cannot be used within an " "expression, because a double-quoted string has no value. A " "double-quoted string indicates that you simply want to display " "the text of the string. Change " "the string's quotes to single quotes (') if you meant to use the " "string as a value in an expression." }, { TCERR_ASI_IN_COND, "assignment in condition (possible use of '=' where '==' was intended)", "The condition expression contains an assignment. This frequently " "indicates that the '=' (assignment) operator was used where the " "'==' (equality comparison) operator was intended. Check the condition " "to ensure that an assignment was actually intended. If the assignment " "is intentional, you can remove this warning by modifying the " "condition expression from the current form (x = y) to the form " "((x = y) != nil) or ((x = y) != 0) as appropriate, which will not " "change the meaning but will make it clear that the assignment is " "intentional." }, { TCERR_REPFUNC_UNDEF, "\"replace\"/\"modify\" cannot be used with a function not " "previously defined", "The \"replace\" and \"modify\" keywords cannot be used with a function " "that is not previously defined. The function must at least be defined " "as an \"extern\" function before it can be replaced." }, { TCERR_REPLACE_PROP_REQ_MOD_OBJ, "\"replace\" can be used with a property only in a \"modify\" object", "The \"replace\" keyword can be used with a property only in an " "object defined with the \"modify\" keyword. This object is not " "defined with \"modify\", so \"replace\" is not allowed with its " "property definitions." }, { TCERR_EXTERN_OBJ_REQ_SYM, "expected object name symbol in \"extern\" statement but found \"%~.*s\"", "An object name symbol is required after \"extern object\" or " "\"extern class\", but the compiler found \"%~.*s\". Check the syntax " "and supply the missing object name." }, { TCERR_PROP_REDEF_IN_OBJ, "property \"%~.*s\" already defined in object", "The property \"%~.*s\" is already defined in this object. An " "object can have at most one definition for a given property. Remove " "the redundant property definition." }, { TCERR_PROP_REQ_EQ, "'=' required between property name and value - found \"%~.*s\"", "An equals sign '=' is required to separate the property name and " "its value; the parser found \"%~.*s\" where the '=' should go. " "Check the syntax and supply the missing '='." }, { TCERR_LIST_EXPECT_ELEMENT, "extra list element expected after comma, but found end of list", "An additional list element was expected after the last comma in the " "list, but the compiler found the end of the list (a closing bracket " "']') instead. Check the list and either remove the unnecessary " "extra comma or add the missing list element." }, { TCERR_REQ_INTRINS_CLASS_NAME, "expected class name string after class name symbol, but found \"%~.*s\"", "The \"intrinsic class\" name must be followed by the global name " "of the metaclass, enclosed in single-quotes, but the compiler found " "\"%~.*s\" instead. Check the statement syntax." }, { TCERR_REQ_INTRINS_CLASS_LBRACE, "expected '{' after intrinsic class name, but found \"%~.*s\"", "The property listing for an \"intrinsic class\" statement must be " "closed in braces '{ }'. The compiler found \"%~.*s\" after the " "metaclass name, where the open brace '{' should be. Check the " "syntax." }, { TCERR_EOF_IN_INTRINS_CLASS, "end of file in \"intrinsic class\" list - '}' is probably missing", "The compiler reached the end of the file while still scanning " "an \"intrinsic class\" statement's property listing. The closing " "brace '}' of the list is probably missing; check " "for the missing brace." }, { TCERR_REQ_INTRINS_CLASS_PROP, "expected property name in intrinsic class list, but found \"%~.*s\"", "The compiler expected the name of a property in the intrinsic " "class property list, but found \"%~.*s\" instead. Check the syntax of " "the statement, and check for unbalanced braces." }, { TCERR_REQ_INTRINS_CLASS_NAME_SYM, "expected class name symbol after \"intrinsic class\", " "but found \"%~.*s\"", "The \"intrinsic class\" keywords must be followed by the class " "name symbol, but the compiler found \"%~.*s\" instead. Check " "the statement syntax." }, { TCERR_REDEF_INTRINS_NAME, "symbol \"%~.*s\" is already defined - can't redefine as intrinsic class", "The symbol \"%~.*s\" is already defined, so you cannot use it as " "the name of this intrinsic class. The symbol is already being " "used as the name of an object, function, or property elsewhere " "in the program. Change the name to a unique symbol." }, { TCERR_CANNOT_EVAL_METACLASS, "\"%~.*s\" is an intrinsic class name and cannot be evaluated " "in an expression", "The symbol \"%~.*s\" is an intrinsic class name. This symbol cannot " "be evaluated in an expression, because it has no value. You can " "only use this symbol in specific contexts where class names " "are permitted, such as with 'new'." }, { TCERR_DICT_SYNTAX, "expected 'property' or object name after 'dictionary', " "but found \"%~.*s\"", "The keyword 'property' or an object name symbol must follow " "the 'dictionary' keyword, but the compiler found \"%~.*s\" instead. " "Check the 'dictionary' statement syntax." }, { TCERR_DICT_PROP_REQ_SYM, "expected property name in 'dictionary property' list, but " "found \"%~.*s\"", "The name of a property was expected in the 'dictionary property' " "list, but the compiler found \"%~.*s\" instead. Check the " "statement syntax." }, { TCERR_DICT_PROP_REQ_COMMA, "expected comma in 'dictionary property' list, but found \"%~.*s\"", "A comma was expected after a property name symbol in a " "'dictionary property' list, but the compiler found \"%~.*s\" " "instead. Check the syntax of the property list and ensure that " "each item is separated from the next by a comma, and that the " "list ends with a semicolon." }, { TCERR_REDEF_AS_DICT, "redefining symbol \"%~.*s\" as dictionary object", "The symbol \"%~.*s\" cannot be used as a dictionary object, because " "it is already defined as a different type of object. You must change " "the name of this dictionary object, or change the name of the " "conflicting object definition." }, { TCERR_UNDEF_SYM_SC, "undefined symbol \"%~.*s\" (used as superclass of \"%~.*s\")", "The symbol \"%~.*s\" is undefined. This symbol is used as a superclass " "in the definition of the object \"%~.*s\". Check the object definition " "to ensure that the superclass name is spelled correctly, and check " "that the superclass's object definition is correct." }, { TCERR_VOCAB_REQ_SSTR, "vocabulary property requires string value, but found \"%~.*s\"", "A vocabulary property value must be one or more single-quoted " "string values, but the compiler found \"%~.*s\" instead. Check the " "property definition and use a single-quoted string value." }, { TCERR_VOCAB_NO_DICT, "vocabulary property cannot be defined - no dictionary is active", "A vocabulary property cannot be defined for this object because " "no dictionary is active. Insert a 'dictionary' statement prior " "to this object definition to establish the dictionary object that " "will be used to store this object's vocabulary." }, { TCERR_LISTPAR_NOT_LAST, "variable-argument list parameter must be the last parameter", "A variable-argument list parameter (a parameter enclosed in square " "brackets '[ ]') is required to be the last parameter in the " "parameter list. Remove the parameters following the list parameter." }, { TCERR_LISTPAR_REQ_RBRACK, "expected ']' after variable-argument list parameter, but found \"%~.*s\"", "A closing square bracket ']' was expected following the " "variable-argument list parameter name, but the compiler found " "\"%~.*s\" instead. Insert the missing bracket." }, { TCERR_LISTPAR_REQ_SYM, "variable-argument list parameter name expected, but found \"%~.*s\"", "A parameter name symbol was expected after the left square bracket '[', " "but the compiler found \"%~.*s\" instead. Check the syntax." }, { TCERR_DBG_NO_ANON_FUNC, "anonymous functions are not allowed in the debugger", "Anonymous functions are not allowed in the debugger." }, { TCERR_ANON_FUNC_REQ_NEW, "anonymous function requires 'new' before 'function' keyword", "An anonymous function definition requires the keyword 'new' " "before the keyword 'function'. This definition does not contain " "the 'new' keyword. Insert 'new' before 'function' in the " "definition." }, { TCERR_GRAMMAR_REQ_SYM, "expected symbol name after 'grammar', but found \"%~.*s\"", "A symbol giving the name of a production is required after " "the 'grammar' keyword, but the compiler found \"%~.*s\" instead. " "Check the statement syntax." }, { TCERR_GRAMMAR_REQ_COLON, "expected ':' after production name, but found \"%~.*s\"", "A colon ':' is required after the name of the production in " "a 'grammar' statement, but the compiler found \"%~.*s\" instead. " "Check the statement syntax and insert the missing colon." }, { TCERR_GRAMMAR_REQ_OBJ_OR_PROP, "object or property symbol required in 'grammar' token list " "(found \"%~.*s\")", "Symbols used in a token list in a 'grammar' statement must " "be property or object names. The symbol \"%~.*s\" is not a " "property or object name, so it cannot be used in the token list. " "Remove this symbol from the list or replace it with a symbol " "of the appropriate type." }, { TCERR_GRAMMAR_ARROW_REQ_PROP, "'->' in 'grammar' token list must be followed by a property name " "(found \"%~.*s\")", "An arrow '->' in a 'grammar' statement's token list must be " "followed by a property name, but the compiler found \"%~.*s\" " "instead. Check the statement syntax." }, { TCERR_GRAMMAR_INVAL_TOK, "invalid token \"%~.*s\" in 'grammar' token list", "The token \"%~.*s\" is not valid in a 'grammar' statement's token " "list. The token list must consist of property names, object names, " "and literal strings (in single quotes). Check the statement syntax." }, { TCERR_REDEF_AS_GRAMPROD, "redefining symbol \"%~.*s\" as grammar production object", "The symbol \"%~.*s\" cannot be used as a grammar production, because " "it is already defined as a different type of object. You must change " "the name of this production object, or change the name of the " "conflicting object definition." }, { TCERR_GRAMMAR_REQ_PROD, "object \"%~.*s\" is not valid in a grammar rule - only production " "names are allowed", "The object \"%~.*s\" cannot be used in a grammar rule. Only " "production names are allowed. A production name is a symbol " "that is used immediately after the 'grammar' keyword in a " "grammar rule definition." }, { TCERR_ENUM_REQ_SYM, "symbol expected in 'enum' list - found \"%~.*s\"", "A symbol name was expected in the 'enum' statement's list of " "enumerator symbols to define, but the compiler found \"%~.*s\" " "instead. Check the statement syntax." }, { TCERR_REDEF_AS_ENUM, "symbol \"%~.*s\" is already defined - can't be used as an enum name", "The symbol \"%~.*s\" is already defined, so it can't be used as an " "'enum' name. An 'enum' name cannot be used for any other global " "symbol, such as an object, function, or property name. Change the " "enum name, or change the conflicting symbol definition." }, { TCERR_ENUM_REQ_COMMA, "comma expected in 'enum' symbol list, but found \"%~.*s\"", "A comma was expected after a symbol name in the 'enum' statement's " "list of enumerator symbols, but the compiler found \"%~.*s\" " "instead. Check the statement syntax and insert the missing " "comma." }, { TCERR_GRAMMAR_BAD_ENUM, "enumerator \"%~.*s\" in 'grammar' list is not declared with " "'enum token'", "The enumerator \"%~.*s\" in the 'grammar' list was not originally " "declared with 'enum token'. Only 'enum token' enumerators can be " "used in 'grammar' token lists. Check the original definition of " "the enumerator and change it to 'enum token', or remove the " "enumerator from this 'grammar' list." }, { TCERR_GRAMMAR_STAR_NOT_END, "'*' must be the last token in a 'grammar' alternative list " "(found \"%~.*s\")", "A '*' must be the last token in a 'grammar' alternative list, " "because this specifies a match for any remaining input tokens. " "The compiler found \"%~.*s\" after the '*'. Check the grammar " "definition, and end the alternative immediately after the '*'." }, { TCERR_GRAMMAR_QUAL_NOT_FIRST, "grammar qualifiers must precede all tokens", "Grammar qualifiers (sequences enclosed in square brackets '[ ]' " "within a 'grammar' statement's item list) must precede all " "token items in an alternative. This statement contains a qualifier " "that appears after one or more token items. Move the qualifier " "to the start of the alternative's token list." }, { TCERR_GRAMMAR_QUAL_REQ_SYM, "keyword required after '[' in grammar qualifier - found \"%~.*s\"", "A keyword is required after the open bracket '[' in a grammar " "qualifier, but the compiler found \"%~.*s\" instead. The keyword " "must specify a valid grammar qualifier." }, { TCERR_BAD_GRAMMAR_QUAL, "invalid grammar qualifier \"%~.*s\"", "The grammar qualifier \"%~.*s\" is not valid. Check the statement " "syntax." }, { TCERR_GRAMMAR_QUAL_REQ_INT, "grammar qualifier \"[%s]\" requires integer constant value", "The grammar qualifier \"[%s]\" requires an integer constant value. " "The expression is missing, invalid, or does not evaluate to a " "constant integer value. Check the qualifier syntax." }, { TCERR_GRAMMAR_QUAL_REQ_RBRACK, "']' required after grammar qualifier - found \"%~.*s\"", "A closing bracket ']' is required at the end of a grammar " "qualifier, but the compiler found \"%~.*s\" instead. Check the " "syntax, and add the missing ']' or remove extraneous extra text." }, { TCERR_PLUSPROP_REQ_SYM, "symbol expected after '+ property', but found \"%~.*s\" instead", "A property symbol is required for the '+ property' statement, but the " "compiler found \"%~.*s\" instead. Check the syntax." }, { TCERR_PLUSOBJ_TOO_MANY, "too many '+' signs in object definition - location not defined", "This object definition has too many '+' signs. The immediately " "preceding object is not at a deep enough containment level for " "this many '+' signs. Check the previous object's location " "definition. You might need to move the immediately preceding " "object definition so that it doesn't come between this object " "and the preceding container you wish to refer to." }, { TCERR_OBJ_TPL_OP_REQ_PROP, "property name required after \"%s\" in object template - found \"%~.*s\"", "A property name is required after the \"%s\" token in the 'object " "template' statement, but the compiler found \"%~.*s\" instead. Check " "the statement syntax." }, { TCERR_OBJ_TPL_STR_REQ_PROP, "property name required in object template string - found %~.*s", "The contents of each string in an 'object " "template' statement must be a property name symbol, but the compiler " "found %~.*s in a string instead. Check the statement syntax." }, { TCERR_OBJ_TPL_REQ_RBRACK, "expected ']' after object template property name - found \"%~.*s\"", "A matching right square bracket ']' is required after a list property " "name in an 'object template' statement, but the compiler found " "\"%~.*s\" instead. Check the statement syntax." }, { TCERR_OBJ_TPL_BAD_TOK, "unexpected token \"%~.*s\" in object template", "The token \"%~.*s\" is invalid in an 'object template' statement. " "Each entry in the statement must be a property name in single or " "double quotes or square brackets, or one of the allowed operators " "('@', '+', '-', etc) followed by a property name." }, { TCERR_OBJ_TPL_SYM_NOT_PROP, "symbol \"%~.*s\" in object template is not a property", "The symbol \"%~.*s\" is used in an 'object template' statement where " "a property is required, but the symbol is not a property. Check " "for conflicting usage of the symbol (as an object or function name, " "for example)." }, { TCERR_OBJ_DEF_NO_TEMPLATE, "object definition does not match any template", "This object definition appears to use template notation, but it doesn't " "match any defined object template. Check your 'object template' " "definitions to make sure this object syntax is defined, or correct " "this object definition to match one of the defined templates. If " "this object is not intended to use a template at all, the object's " "first property definition probably has a syntax error, so check " "the object's property list syntax." }, { TCERR_OBJ_TPL_NO_VOCAB, "property \"%~.*s\" is a dictionary property - not valid in templates", "Property \"%~.*s\" is a dictionary property, which cannot be used " "in an object template." }, { TCERR_OBJ_TPL_PROP_DUP, "property \"%~.*s\" duplicated in object template", "The property \"%~.*s\" appears more than once in this 'object template' " "list. A given property can be used only once in a template." }, { TCERR_META_ALREADY_DEF, "intrinsic class has been previously defined as \"%~.*s\"", "This same intrinsic class has been previously defined with " "the name \"%~.*s\". An intrinsic class may only be defined with " "one class symbol. Remove one of the conflicting definitions." }, { TCERR_OBJ_DEF_REQ_SEM, "missing semicolon at end of object definition - found \"%~.*s\"", "This object definition is missing its closing semicolon ';' - the " "compiler found \"%~.*s\", which the compiler must assume is the start " "of a new statement or object definition. Insert the missing " "semicolon. If this is actually meant to be part of the object " "definition, there is a syntax error here - check and correct the " "syntax." }, { TCERR_EXPECTED_STMT_START, "expected start of statement, but found \"%~.*s\"", "The compiler expected to find the beginning of a statement, " "but found \"%~.*s\" instead. Check the statement syntax; check " "for preceding strings that weren't properly terminated, or extra " "or missing braces." }, { TCERR_MISSING_COLON_FORMAL, "missing colon at end of anonymous function formal list - found \"%~.*s\"", "The short-form of the anonymous function definition requires a colon " "':' at the end of the formal parameter list, but the compiler " "found \"%~.*s\" instead. Even if the function has no arguments, it " "still requires a colon immediately following the open brace '{'. " }, { TCERR_SEM_IN_SHORT_ANON_FN, "semicolon is not allowed in a short anonymous function", "This short-form anonymous function contains a semicolon, which is " "not allowed. A short-form anonymous function must consist of " "a single expression, and is terminated with the closing brace '}' " "with no semicolon between the expression and the brace." }, { TCERR_SHORT_ANON_FN_REQ_RBRACE, "semicolon is not allowed in a short-form anonymous function", "This short-form anonymous function's expression is followed by a " "semicolon, which is not allowed. A short-form anonymous function " "can contain only an expression, and ends with a right brace '}'." }, { TCERR_SHORT_ANON_FN_REQ_RBRACE, "missing '}' at end of anonymous function expression - found \"%~.*s\"", "There is no right brace '}' at the end of this short-form " "anonymous function. A short-form anonymous function must contain " "only an expression and end with '}'. Check the syntax." }, { TCERR_IN_REQ_LPAR, "missing '(' after 'in' operator - found \"%~.*s\"", "An open parenthesis '(' is required after the 'in' operator, but " "the compiler found \"%~.*s\". Check the syntax and insert the " "missing parenthesis." }, { TCERR_EXPECTED_IN_COMMA, "expected ',' or ')' in 'in' list, but found \"%~.*s\"", "Expected a comma ',' or right parenthesis ')' in the 'in' list, " "but found \"%~.*s\". The expressions in an 'in' list must be " "separated by commas, and the entire list must be enclosed in " "parentheses '( )'. Check for errors and unbalanced parentheses " "in the argument expression." }, { TCERR_EXPECTED_IN_RPAR, "expected ')' at end of 'in' list, but found \"%~.*s\"", "Expected a right parenthesis ')' at the end of the 'in' list, " "but found \"%~.*s\". The compiler is assuming that this is the end " "of the statement. Please insert the missing parenthesis, or check " "the argument list for unbalanced parentheses or other errors." }, { TCERR_CANNOT_MOD_OR_REP_TYPE, "objects of this type cannot be modified or replaced", "You cannot use 'modify' or 'replace' with an object of this type. " "Only objects and classes originally defined as ordinary objects " "can be modified or replaced." }, { TCERR_REQ_FOREACH_LPAR, "expected '(' after \"foreach\", but found \"%~.*s\"", "An open parenthesis '(' is required after the \"foreach\" keyword, " "but the compiler found \"%~.*s\" instead. Check the syntax " "and insert the missing parenthesis." }, { TCERR_MISSING_FOREACH_EXPR, "missing expression in \"foreach\" - found \"%~.*s\"", "This \"foreach\" statement is missing its iteration expression; " "the compiler found \"%~.*s\" when it was expecting the iteration " "expression of the form 'x in '. Check the " "syntax." }, { TCERR_FOREACH_REQ_IN, "\"in\" required in \"foreach\" - found \"%~.*s\"", "The keyword \"in\" was expected after the iteration variable " "expression in the this \"foreach\" statement, but the compiler " "found \"%~.*s\" instead. Check the syntax." }, { TCERR_REQ_FOREACH_RPAR, "missing ')' at end of \"foreach\" expression - found \"%~.*s\"", "A closing parenthesis ')' is required at the end of the \"foreach\" " "statement's expression, but the compiler found \"%~.*s\" instead. " "Check the syntax, and insert the missing parenthesis, or correct " "unbalanced parentheses or other syntax errors earlier in the " "expression." }, { TCERR_PROPDECL_REQ_SYM, "expected property name in 'property' list, but found \"%~.*s\"", "The name of a property was expected in the 'property' " "list, but the compiler found \"%~.*s\" instead. Check the " "statement syntax." }, { TCERR_PROPDECL_REQ_COMMA, "expected comma in 'property' list, but found \"%~.*s\"", "A comma was expected after a property name symbol in a " "'property' list, but the compiler found \"%~.*s\" " "instead. Check the syntax of the list and ensure that " "each item is separated from the next by a comma, and that the " "list ends with a semicolon." }, { TCERR_EXPORT_REQ_SYM, "expected symbol in 'export' statement, but found \"%~.*s\"", "A symbol name must follow the 'export' keyword, but the compiler " "found \"%~.*s\" instead. Check the syntax of the statement." }, { TCERR_EXPORT_EXT_TOO_LONG, "external name \"%~.*s\" in 'export' is too long", "The external name \"%~.*s\" in this 'export' statement is too long. " "External names are limited to the same maximum length as regular " "symbol names." }, { TCERR_UNTERM_OBJ_DEF, "unterminated object definition", "This object definition is not properly terminated - a semicolon ';' or " "closing brace '}' should appear at the end of the definition. The " "compiler found a new object definition or a statement that cannot " "be part of an object definition, but did not find the end of the " "current object definition. Insert the appropriate terminator, or " "check the syntax of the property definition. Note that this error " "is normally reported at the END of the unterminated object, so if " "the line number of the error refers to the start of a new object, " "it's probably the preceding object that is not property terminated." }, { TCERR_OBJ_DEF_REQ_RBRACE, "missing right brace at end of object definition - found \"%~.*s\"", "This object definition is missing its closing brace '}' - the " "compiler found \"%~.*s\", which the compiler must assume is the start " "of a new statement or object definition. Insert the missing " "brace. If this is actually meant to be part of the object " "definition, there is a syntax error here - check and correct the " "syntax." }, { TCERR_GRAMMAR_REQ_NAME_RPAR, "missing right parenthesis after name in 'grammar' - found \"%~.*s\"", "A right parenthsis ')' is required after the name tag in a 'grammar' " "statement, but the compiler found \"%~.*s\". Check the syntax and " "insert the missing parenthesis." }, { TCERR_PROPSET_REQ_LBRACE, "missing open brace '{' in propertyset definition - found \"%~.*s\"", "An open brace '{' is required to group the properties in the " "propertyset definition. The compiler found \"%~.*s\" where the " "open brace should be. Check the syntax and remove any extraneous " "characters or insert the missing open brace, as appropriate." }, { TCERR_GRAMMAR_REQ_RPAR_AFTER_GROUP, "missing right parentheses after group in 'grammar' - found \"%~.*s\"", "A right parenthesis ')' is required at the end of a parenthesized " "token group in a 'grammar' statement, but the compiler found " "\"%~.*s\". Check the syntax and insert the missing parenthesis." }, { TCERR_GRAMMAR_GROUP_ARROW_NOT_ALLOWED, "'->' is not allowed after parenthesized group in 'grammar' statement", "The arrow operator '->' is not allowed after a parenthesized group " "in a 'grammar' statement. A group is not a true sub-production, so " "it has no match object to assign to a property. If you want to " "create a run-time match object for the group, make the group into " "a rule for a separate, named production, and use the name of the " "production instead of the group in this rule." }, { TCERR_OBJ_DEF_CANNOT_USE_TEMPLATE, "cannot use template with an unimported extern class as superclass", "This object cannot be defined with template notation (property value " "constants immediately after the superclass name or names) because " "one or more of its superclasses are unimported 'extern' classes, or " "inherit from unimported extern classes. The compiler does not have " "any class relationship information about classes that are explicitly " "defined as external ('extern') and not imported from a symbol file " "that is part of the current build, so it cannot determine which " "class's template definition to use. You must define this object's " "properties using the normal 'name = value' notation rather than " "using a template." }, { TCERR_REPLACE_OBJ_REQ_SC, "base class list required for object 'replace' definition", "This 'replace' statement replaces an object with a new definition, " "but it does not have a superclass list. A superclass list is " "required for the replacement object definition." }, { TCERR_PROPSET_TOO_DEEP, "'propertyset' nesting is too deep", "This 'propertyset' definition is too deeply nested - it's inside " "too many other 'propertyset' definitions. You must reduce " "the nesting depth." }, { TCERR_PROPSET_TOK_TOO_LONG, "expanded property name in propertyset is too long", "This property name, expanded into its full name using the " "propertyset pattern string, is too long. You must shorten the " "fully expanded name by shortening the propertyset pattern, " "shortening this property's name, or reducing the propertyset " "nesting depth." }, { TCERR_PROPSET_INVAL_PAT, "propertyset pattern string \"%~.*s\" is invalid", "The propertyset pattern string \"%~.*s\" is not valid. A propertyset " "pattern string must consist of valid symbol characters plus " "exactly one asterisk '*' (which specifies where the property " "names within the propertyset are inserted into the pattern)." }, { TCERR_PROPSET_INVAL_FORMALS, "invalid formal parameters in propertyset definition", "The propertyset formal parameter list is invalid. The parameters " "must contain exactly one asterisk '*', specifying the argument " "position where additional per-property parameters are inserted " "into the common parameter list." }, { TCERR_CIRCULAR_CLASS_DEF, "circular class definition: %~.*s is a subclass of %~.*s", "This class is defined circularly: %~.*s is a subclass of %~.*s, " "so the latter cannot also be a subclass of the former. Check the " "definition of the base class. " }, { TCERR_GRAMMAR_LIST_REQ_PROP, "property name required in list in 'grammar', but found \"%~.*s\"", "A part-of-speech list within a 'grammar' rule definition is only " "allowed to contain property names, but the compiler found \"%~.*s\" " "instead. Check the syntax. " }, { TCERR_GRAMMAR_LIST_UNCLOSED, "missing '>' in 'grammar' property list - found \"%~.*s\"", "The grammar property list is missing the closing angle bracket '>'; " "the compiler found \"%~.*s\" in the list and will assume that the '>' " "was accidentally omitted. Insert the missing '>' or remove the " "extraneous text from the list. " }, { TCERR_CONST_IDX_INV_TYPE, "invalid indexed type in constant expression - list required", "Only a list value can be indexed in a constant expression. The " "constant value being indexed in this expression is not a list. " }, { TCERR_INVAL_TRANSIENT, "'transient' is not allowed here", "The 'transient' keyword is not allowed in this context. 'transient' " "can only be used to modify an object definition; it cannot be used " "with classes, functions, templates, or any other definitions. " }, { TCERR_GRAMMAR_MOD_REQ_TAG, "'modify/replace grammar' requires name-tag", "'modify grammar' and 'replace grammar' can only be used with a grammar " "rule that includes a name-tag (in parentheses after the production " "name). The name-tag must match a grammar rule defined previously. " }, { TCERR_REQ_INTRINS_SUPERCLASS_NAME, "expected intrinsic superclass name after colon, but found \"%~.*s\"", "The name of the intrinsic class's superclass was expected after " "the colon, but the compiler found \"%~.*s\" instead. Add the " "superclass name (or remove the colon). " }, { TCERR_INTRINS_SUPERCLASS_UNDEF, "intrinsic class superclass \"%~.*s\" is not defined", "The intrinsic class superclass \"%~.*s\" is not defined. You must " "define the superclass before you define any subclasses. " }, { TCERR_GRAMMAR_ENDS_WITH_OR, "grammar rule ends with '|' (add '()' if empty last rule is " "intentional)", "The grammar rule ends with '|'. This indicates that the rule matches " "a completely empty input. This is legal, but it's uncommon. " "If you really intend for this rule to match empty input, " "you can eliminate this warning by making the empty rule " "explicit, by using '()' for the empty match rule. " }, { TCERR_GRAMMAR_EMPTY, "grammar rule is empty (add '()' if this is intentional)", "The grammar rule is empty, which indicates that the rule matches " "a completely empty input. This is legal, but it's uncommon. " "If you really intend for this rule to match empty input, " "you can eliminate this warning by making the empty rule " "explicit, by using '()' for the empty match rule. " }, { TCERR_TEMPLATE_EMPTY, "empty template definition", "The template is empty. A template definition must include at " "least one property name." }, { TCERR_REQ_RPAR_IF, "expected ')' after the \"if\" condition, but found \"%~.*s\"", "A close parenthesis ')' is required after the condition expression " "in the \"if\" statement, to balance the required open parenthesis, " "but the compiler found \"%~.*s\" instead. The compiler will assume " "that a parenthesis was intended. Please correct the syntax " "by inserting a parenthesis." }, { TCERR_INTRINS_SUPERCLASS_NOT_INTRINS, "intrinsic class superclass \"%~.*s\" is not an intrinsic class", "The intrinsic class superclass \"%~.*s\" is not itself an intrinsic " "class. An intrinsic class can only be derived from another intrinsic " "class. " }, { TCERR_FUNC_REDEF_AS_MULTIMETHOD, "function \"%.*s\" is already defined - can't redefine as a multi-method", "The function \"%.*s\" is already defined as an ordinary function, " "so it can't be redefined here with typed parameters. If you intended " "for the original version to be a multi-method, add the 'multimethod' " "modifier to its definition, immediately after the closing parenthesis " "of the parameter list in the function definition, as in " "\"foo(x) multimethod { ... }\"." }, { TCERR_MULTIMETHOD_NOT_ALLOWED, "'multimethod' is not allowed here", "The 'multimethod' modifier is not allowed in this context. " "This modifier can only be used for top-level function definitions." }, { TCERR_MMPARAM_NOT_OBJECT, "parameter type \"%~.*s\" is not an object", "The declared parameter type \"%~.*s\" is not an object. Only object " "or class names can be used to declare parameter types. Check the " "definition of the function and make sure the type name is correct, " "and that the same name isn't used elsewhere as a non-object type." }, { TCERR_MMINH_MISSING_ARG_TYPE, "expected a type name in inherited<> type list, but found \"%~.*s\"", "An inherited<> type list requires a type name (a class or object " "name, or '*' or '...') in each position. The compiler found " "\"%~.*s\" where it expected to find a type. The type must be " "written as a simple name, with no parentheses or other punctuation." }, { TCERR_MMINH_MISSING_COMMA, "expected a comma in inherited<> type list, but found \"%~.*s\"", "The compiler found \"%~.*s\" where it expected to find a comma " "in an inherited<> type list. Each element in the list must be " "separated by a comma." }, { TCERR_MMINH_MISSING_GT, "missing '>' in inherited<> type list (found \"%~.*s\")", "An inherited<> type list must end with a '>'. The compiler " "found \"%~.*s\", and is assuming that the list ends here. If " "this isn't the end of the type list, check the syntax; otherwise, " "just add '>' at the end of the list." }, { TCERR_MMINH_MISSING_ARG_LIST, "argument list missing in inherited<> (expected '(', found \"%~.*s\")", "An inherited<> expression requires an argument list. The compiler " "found \"%~.*s\" it expected the opening '(' of the argument list. " "If the function doesn't take any arguments, use empty parentheses, " "'()', to indicate the empty argument list." }, { TCERR_NAMED_ARG_NO_ELLIPSIS, "a pass-by-name argument value cannot use an ellipsis '...'", "A named argument value (\"name: expression\") cannot be combined " "with an ellipsis '...'. The ellipsis expands a list value into " "a series of positional arguments. This isn't meaningful with a " "named argument, because a named argument is passed by name rather " "than by position." }, { TCERR_ARG_MUST_BE_OPT, "parameter '%.*s' must be optional because it follows an " "optional parameter", "The parameter variable '%.*s' in this function or method definition " "must be declared as optional, because it follows one or more other " "optional arguments in the list. Argument values are assigned to parameter " "names in left-to-right order at run-time, so once you have one optional " "parameter to the list, all of the parameters that follow it must be " "optional as well. You can make this parameter optional by adding " "a '?' suffix after the variable name, or by adding an '= expression' " "default value expression after the name." }, { TCERR_NAMED_ARG_NO_TYPE, "formal parameter '%.*s' is named - a type declaration isn't allowed", "The formal parameter '%.*s' is named (it uses a ':' suffix), so it " "can't have a type declaration. Multimethod selection is based " "strictly on the positional arguments; named parameters aren't " "used in the selection process, so they can't be declared with types." }, { TCERR_LOOKUP_LIST_EXPECTED_ARROW, "missing -> in %dth element in LookupTable list", "An arrow '->' was expected in the %dth element of the LookupTable " "list. This list appears to be a LookupTable list, because earlier " "elements use the Key->Value notation, so every element must given " "as a Key->Value pair." }, { TCERR_ARROW_IN_LIST, "unexpected -> found in list (after %dth element)", "An arrow '->' was found in a list expression, after the %dth element. " "The arrow isn't allowed here because the expression appears to be an " "ordinary list, not a LookupTable list. If you intended this to be " "a LookupTable list, every element must be given as a Key->Value pair." }, { TCERR_MISPLACED_ARROW_IN_LIST, "misplaced -> in LookupTable list after %dth element - expected comma", "A comma ',' should appear after the %dth element of the LookupTable " "list, but an arrow '->' was found instead. The arrow appears to be " "misplaced. Each Key->Value pair in a LookupTable list should be " "separated from the next by a comma." }, { TCERR_LOOKUP_LIST_EXPECTED_END_AT_DEF, "LookupTable list must end after default *-> value, but found \"%~.*s\"", "A LookupTable list must end after the default value (specified with " "\"*->Value\"), but the compiler found \"%~.*s\" where the closing " "bracket ']' should be. Rearrange the list so that the default " "value is in the final position in the list." }, { TCERR_OPER_IN_PROPSET, "an 'operator' property cannot be defined within a 'propertyset'", "An 'operator' property cannot be defined within a 'propertyset' " "group. You must move the 'operator' property definition outside " "of the group." }, { TCERR_EXPECTED_RBRACK_IN_OP, "expected ']' in 'operator []', found '%~.*s'", "The ']' in an 'operator []' property declaration was missing (the " "compiler found '%~.*s' where the ']' should go)." }, { TCERR_BAD_OP_OVERLOAD, "invalid overloaded operator '%~.*s'", "The 'operator' property type requires one of the overloadable " "operators, but the compiler found '%~.*s' where the operator " "should appear. You must specify one of the overloadable operator " "symbols: + - * / % ^ << >> ~ | & [] []=" }, { TCERR_OP_OVERLOAD_WRONG_FORMALS, "wrong number of parameters defined for overloaded operator (%d required)", "This 'operator' property has the wrong number of parameters. " "This overloaded operator requires %d parameter(s)." }, { TCERR_RT_CANNOT_DEFINE_GLOBALS, "new global symbols (\"%~.*s\") cannot be defined in " "run-time code compilation", "Code compiled during program execution cannot define new global " "symbols. The code being compiled attempted to define \"%~.*s\"." }, { TCERR_FOR_IN_NOT_LOCAL, "'for..in' variable %.*s is not defined as a local variable", "The variable \"%.*s\" is being used as the control variable " "of a 'for..in' statement, but this variable isn't defined as a " "local variable. Check that the variable name is correct. If so, " "you must add a 'local' statement defining this variable (or simply " "add the word 'local' before the variable name in the 'in' clause)." }, { TCERR_BAD_EMBED_END_TOK, "<<%.*s>> found in string without a matching %s", "The compiler found the embedded keyword <<%.*s>> in this string, " "but this wasn't preceded by a matching %s. Check that all " "keywords are present and spelled correctly. If you're using nested " "<>'s or the like, the problem might be that the begin/end " "aren't balanced properly for one of the nested levels." }, { TCERR_STRTPL_MISSING_LANGLE, "expected '<<' at start of string template, but found '%.*s'", "The compiler expected to find '<<' at the start of the string " "template definition (after the word 'template'), but found '%.*s'. " "Check the syntax, and insert the missing '<<'." }, { TCERR_STRTPL_MISSING_RANGLE, "expected '>>' at end of string template, but found '%.*s'", "The compiler expected to find '>>' at the end of the string " "template definition, but found '%.*s'. This symbol isn't allowed " "within a string template, so the compiler is guessing that this " "is really the end of the statement and that the template is missing " "its closing '>>'. Check the syntax. If you really meant to include " "this symbol in the template, you must change it to something else, " "since it's not an allowed template element. If you simply left off " "the closing '>>', add it at the end of the definition." }, { TCERR_STRTPL_MULTI_STAR, "only one '*' is allowed in a string template", "This string template definition has more than one '*' symbol. " "Only one '*' is allowed in a string template." }, { TCERR_STRTPL_FUNC_MISMATCH, "string template processor function %.*s() does not match template - " "must take %d parameter(s) and return a value", "The processor function %.*s() for this string template does not " "match the template usage. It must be defined as a function taking " "%d argument(s) and returning a value." }, { TCERR_DEFINED_SYNTAX, "invalid syntax for defined() operator - argument must be a symbol name", "The syntax for the defined() operator is invalid. This operator " "requires a single symbol name as the argument, in parentheses." }, { TCERR___OBJREF_SYNTAX, "invalid syntax for __objref() operator", "The syntax for the __objref() operator is invalid. This operator " "requires syntax of the form __objref(symbol) or __objref(symbol, mode), " "where the mode is 'warn' or 'error'." }, { TCERR_BAD_OP_FOR_FLOAT, "floating point values can't be used with this operator", "Floating point values can't be used with this operator. Only ordinary " "integer values can be used." }, { TCERR_INLINE_OBJ_REQ_LBRACE, "expected inline object property list starting with '{' (found '%.*s')", "An inline property definition must contain a property list enclosed " "in braces, '{ ... }'. The compiler found '%.*s' where it expected " "to see the left brace '{' at the start of the property list. Check " "the object definition syntax." }, { TCERR_CODEGEN_NO_MEM, "out of memory for code generation", "Out of memory. The compiler cannot allocate memory to generate " "the compiled code for the program. Make more memory available, " "if possible (by closing other applications, for example), then " "try running the compiler again." }, { TCERR_UNRES_TMP_FIXUP, "%d unresolved branches", "%d unresolved branches." }, { TCERR_WRITEAT_PAST_END, "attempting to write past end of code stream", "The compiler's code generator is attempting to " "write past the end of the code stream." }, { TCERR_SELF_NOT_AVAIL, "'self' is not valid in this context", "The 'self' object is not available in this context. You can only " "refer to 'self' within a method associated with an object; you cannot " "use 'self' in a function or in other code that is not part of an " "object's method." }, { TCERR_INH_NOT_AVAIL, "'inherited' is not valid in this context", "The 'inherited' construct is not available in this context. You " "can only use 'inherited' within a method associated with an object " "with a superclass." }, { TCERR_NO_ADDR_SYM, "cannot take address of \"%~.*s\"", "You cannot take the address of \"%~.*s\". You can only take the " "address of a function or a property." }, { TCERR_CANNOT_ASSIGN_SYM, "cannot assign to \"%~.*s\"", "You cannot assign a value to \"%~.*s\". You can only assign a value " "to an object property, to a local variable or parameter, or to a " "subscripted element of a list." }, { TCERR_CANNOT_EVAL_LABEL, "\"%~.*s\" is a label and cannot be evaluated in an expression", "The symbol \"%~.*s\" is a code label; this symbol cannot be " "evaluated in an expression. You can only use this symbol " "in a 'goto' statement." }, { TCERR_CANNOT_CALL_SYM, "cannot call \"%~.*s\" as a method or function", "The symbol \"%~.*s\" is not a valid method, function, or expression " "value. You cannot invoke this symbol as a method or function call. " "Check spelling, and check for missing operators or mismatched " "parentheses." }, { TCERR_PROP_NEEDS_OBJ, "cannot call property \"%~.*s\" without \"object.\" prefix - no \"self\"", "You cannot call the property \"%~.*s\" in this context without an " "explicit \"object.\" prefix, because no implied \"self\" object " "exists in this context. A \"self\" object exists only inside object " "method code. Check spelling and syntax to ensure you meant to call " "a property, and add the object qualifier if so." }, { TCERR_VOID_RETURN_IN_EXPR, "\"%~.*s\" has no return value - cannot use in an expression", "\"%~.*s\" has no return value, so it cannot be used in an expression " "(there's no value to use in further calculations). Check the " "definition of the function or method." }, { TCERR_INVAL_FUNC_ADDR, "'&' cannot be used with a function name (\"%~.*s\")", "The '&' operator cannot be used with function name \"%~.*s\". If you " "want a reference to the function, simply use the name by itself with " "no argument list. If you want to call the function, put the argument " "list in parentheses '( )' after the function name; if the function " "takes no arguments, use empty parentheses '()' after the function's " "name." }, { TCERR_INVAL_NEW_EXPR, "cannot apply operator 'new' to this expression", "You cannot apply operator 'new' to this expression. 'new' can " "only be applied to an object or metaclass name, followed by an " "optional argument list. Check for syntax errors." }, { TCERR_TOO_FEW_FUNC_ARGS, "too few arguments to function \"%~.*s\"", "Not enough arguments are specified in the call to the " "function \"%~.*s\". Check the definition of the function, " "and check for unbalanced parentheses or other syntax errors." }, { TCERR_TOO_MANY_FUNC_ARGS, "too many arguments to function \"%~.*s\"", "Too many arguments are specified in the call to the " "function \"%~.*s\". Check the definition of the function, " "and check for unbalanced parentheses or other syntax errors." }, { TCERR_SETPROP_NEEDS_OBJ, "cannot assign to property \"%~.*s\" without \"object.\" prefix - " "no \"self\"", "You cannot assign to the property \"%~.*s\" in this context without an " "explicit \"object.\" prefix, because no implied \"self\" object " "exists in this context. A \"self\" object exists only inside object " "method code. Check spelling and syntax to ensure you meant to assign " "to a property, and add the object qualifier if so." }, { TCERR_SYM_NOT_PROP, "invalid '.' expression - symbol \"%~.*s\" is not a property", "The symbol \"%~.*s\" cannot be used after a period '.' because this " "symbol is not a property. The period in a member evaluation " "expression must be followed by a property name, or by an expression " "that yields a property pointer value. Check spelling and check" "for syntax errors." }, { TCERR_INVAL_PROP_EXPR, "invalid '.' expression - right side is not a property", "This property evaluation expression is invalid. The period '.' " "in a property expression must be followed by a property name, " "or by an expression that yields a property pointer value. Check " "spelling and check for syntax errors." }, { TCERR_INH_NOT_OBJ, "illegal 'inherited' - \"%~.*s\" is not a class", "This 'inherited' expression is not valid, because \"%~.*s\" is not " "a class. 'inherited' must be followed by a period '.' or by the " "name of a superclass of this method's object. Check spelling and " "expression syntax." }, { TCERR_INVAL_OBJ_EXPR, "illegal expression - left of '.' is not an object", "The expression on the left of the period '.' is not an object value. " "The left of a '.' expression must be an object name, or an expression " "that evaluates to an object value. Check spelling and syntax." }, { TCERR_SYM_NOT_OBJ, "symbol \"%~.*s\" is not an object - illegal on left of '.'", "The symbol \"%~.*s\" is not an object, so you cannot use this " "symbol on the left side of a period '.' expression. The left side of a " "'.' expression must be an object name or an expression that evaluates " "to an object value. Check the spelling and syntax." }, { TCERR_IF_ALWAYS_TRUE, "\"if\" condition is always true - \"else\" part is unreachable", "The condition in this \"if\" statement is always true, which means " "that the code in the \"else\" clause will never be executed. If this " "is unintentional, check the condition to ensure it's correct." }, { TCERR_IF_ALWAYS_FALSE, "\"if\" condition is always false", "The condition in this \"if\" statement is always false, which means " "that the statement or statements following the \"if\" will never " "be executed. If this is unintentional, check the condition to " "ensure it's correct." }, { TCERR_INVALID_BREAK, "invalid \"break\" - not in a for/while/do/switch", "This \"break\" statement is invalid. A \"break\" can only appear " "in the body of a loop (\"for\", \"while\", or \"do\") or within a " "case in a \"switch\" statement." }, { TCERR_INVALID_CONTINUE, "invalid \"continue\" - not in a for/while/do", "This \"continue\" statement is invalid. A \"continue\" can only appear " "in the body of a loop (\"for\", \"while\", or \"do\")." }, { TCERR_MAIN_NOT_DEFINED, "function _main() is not defined", "No function named _main() is defined. This function must be defined, " "because _main() is the entrypoint to the program at run-time." }, { TCERR_MAIN_NOT_FUNC, "_main is not a function", "The global symbol \"_main\" does not refer to a function. This symbol " "must be defined as a function, because _main() is the entrypoint " "to the program at run-time." }, { TCERR_CATCH_EXC_NOT_OBJ, "exception class symbol \"%~.*s\" in \"catch\" clause is not an object", "The exception class symbol \"%~.*s\" used in this \"catch\" clause " "is not an object class. Only an object class can be used to " "specify the type of exception to catch." }, { TCERR_INVALID_BREAK_LBL, "invalid \"break\" - no label \"%~.*s\" on any enclosing statement", "This \"break\" is invalid because the label \"%~.*s\" does not " "refer to an enclosing statement. When a label is used with " "\"break\", the label must refer to an enclosing statement. " "Check for unbalanced braces, and check that the label is correct." }, { TCERR_INVALID_CONT_LBL, "invalid \"continue\" - no label \"%~.*s\" on any enclosing loop", "This \"continue\" is invalid because the label \"%~.*s\" does not " "refer to an enclosing statement. When a label is used with " "\"continue\", the label must refer directly to an enclosing loop " "(\"while\", \"for\", or \"do\") statement. Check for unbalanced " "braces, and check that the label is correct." }, { TCERR_INVALID_GOTO_LBL, "invalid \"goto\" - label \"%~.*s\" is not defined in this function " "or method", "This \"goto\" is invalid because the label \"%~.*s\" is not defined " "in this function or method. Check the label name and correct " "any misspelling, or insert the missing label definition." }, { TCERR_UNRESOLVED_EXTERN, "unresolved external reference \"%~.*s\"", "The symbol \"%~.*s\" was defined as an external reference but " "is not defined anywhere in the program. Determine where this " "symbol should be defined, and make sure that the source file " "that contains the definition is included in the compilation." }, { TCERR_UNREFERENCED_LABEL, "label \"%~.*s\" is not referenced", "The statement label \"%~.*s\" is not referenced in any \"goto\", " "\"break\", or \"continue\" statement. The label is unnecessary " "and can be removed." }, { TCERR_UNREFERENCED_LOCAL, "local variable \"%~.*s\" is never used", "The local variable \"%~.*s\" is never used. This local variable " "can be removed. Check to make sure that the local variable isn't " "hidden by another variable of the same name inside nested braces '{ }' " "within the code." }, { TCERR_UNASSIGNED_LOCAL, "no value is assigned to local variable \"%~.*s\"", "No value is assigned to the local variable \"%~.*s\". " "You should initialize the variable with a value before its first " "use to make the meaning of the code clear." }, { TCERR_UNUSED_LOCAL_ASSIGNMENT, "value is assigned to local variable \"%~.*s\" but the value is " "never used", "A value is assigned to the local variable \"%~.*s\", but the variable's " "value is never used. It is possible that the variable can be " "removed." }, { TCERR_SC_NOT_OBJECT, "invalid superclass \"%~.*s\" - not an object", "The superclass \"%~.*s\" is not valid, because the symbol does not " "refer to an object. Each superclass of an object must be a class " "or object." }, { TCERR_ARG_EXPR_HAS_NO_VAL, "argument expression %d yields no value", "The argument expression in position %d in the argument list yields no " "value. This usually indicates that the argument expression uses " "a double-quoted (self-printing) string where a single-quoted string " "was intended." }, { TCERR_ASI_EXPR_HAS_NO_VAL, "right side of assignment has no value", "The expression on the right side of the assignment operator yields " "no value. This usually indicates that the expression after the " "assignment operator uses a double-quoted (self-printing) string " "where a single-quoted string was intended." }, { TCERR_WRONG_ARGC_FOR_FUNC, "wrong number of arguments to function \"%~.*s\": %s required, %d actual", "The call to function \"%~.*s\" has the wrong number of arguments. " "The function requires %s argument(s), but this call has %d. Check " "the argument list." }, { TCERR_GOTO_INTO_FINALLY, "'goto' cannot transfer control into a 'finally' block", "This 'goto' statement transfers control to a label defined in a " "'finally' block, but the 'goto' statement is not within the same " "'finally' block. This type of transfer is illegal because it would " "leave the exit path from the 'finally' undefined." }, { TCERR_UNREFERENCED_PARAM, "formal parameter \"%~.*s\" is never used", "The formal parameter \"%~.*s\" is never used. In many cases this " "doesn't indicate a problem, since a method might receive parameters " "it doesn't actually need - the method interface might be inherited " "from a base class, or the extra parameters might be included " "for possible future use or for architectural reasons. You might " "want to check that the parameter really wasn't meant to be used; in " "particular, check to make sure that the parameter isn't hidden " "by another variable of the same name inside nested braces '{ }' " "within the code." }, { TCERR_GRAMPROD_HAS_NO_ALTS, "grammar production \"%~.*s\" has no alternatives defined", "The grammar production symbol \"%~.*s\" has no alternatives defined. " "This probably indicates that this symbol was accidentally " "misspelled when used as a sub-production in a grammar rule. Check " "for occurrences of this symbol in grammar rules, and correct the " "spelling if necessary. If the symbol is spelled correctly, add " "at least one grammar rule for this production." }, { TCERR_CANNOT_MOD_META_PROP, "intrinsic class property \"%~.*s\" cannot be modified", "The property \"%~.*s\" is defined in this intrinsic class, " "so it cannot be modified. You can only add new properties to " "an intrinsic class; you can never override, modify, or replace " "properties defined in the intrinsic class itself." }, { TCERR_FOREACH_NO_CREATEITER, "Container.createIterator is not defined - %s..in is not valid", "The intrinsic class Container's method createIterator is not defined, " "so the %s..in syntax cannot be used. This should be corrected if you " "#include or in this source file." }, { TCERR_FOREACH_NO_GETNEXT, "Iterator.getNext is not defined - %s..in cannot be used", "The intrinsic class Iterator's method getNext is not defined, " "so the '%s..in' statement cannot be used. This should be corrected if " "you #include or in this source file." }, { TCERR_FOREACH_NO_ISNEXTAVAIL, "Iterator.isNextAvailable is not defined - %s..in cannot be used", "The intrinsic class Iterator's method isNextAvailable is not defined, " "so the '%s..in' statement cannot be used. This should be corrected if " "you #include or in this source file." }, { TCERR_INVALID_TYPE_FOR_EXPORT, "symbol \"%~.*s\" is not a valid type for export", "The symbol \"%~.*s\" appears in an 'export' statement, but this " "symbol is not of a valid type for export. Only object and property " "names may be exported." }, { TCERR_DUP_EXPORT, "duplicate export for external name \"%~.*s\": \"%~.*s\" and \"%~.*s\"", "Invalid duplicate 'export' statements. The external name \"%~.*s\" " "has been given to the exported symbols \"%~.*s\" and \"%~.*s\". An " "external name can be given to only one symbol. You must remove " "one of the duplicate exports." }, { TCERR_DUP_EXPORT_AGAIN, "additional duplicate export for external name \"%~.*s\": \"%~.*s\"", "Additional duplicate export: the external name \"%~.*s\" has also " "been exported with symbol \"%~.*s\". Only one export per external " "name is allowed." }, { TCERR_TARGETPROP_NOT_AVAIL, "'targetprop' is not available in this context", "The 'targetprop' value is not available in this context. You can only " "refer to 'targetprop' within a method associated with an object; " "you cannot use 'targetprop' in a function or in other code that is " "not part of an object's method." }, { TCERR_TARGETOBJ_NOT_AVAIL, "'targetobj' is not available in this context", "The 'targetobj' value is not available in this context. You can only " "refer to 'targetobj' within a method associated with an object; " "you cannot use 'targetobj' in a function or in other code that is " "not part of an object's method." }, { TCERR_DEFININGOBJ_NOT_AVAIL, "'definingobj' is not available in this context", "The 'definingobj' value is not available in this context. You can only " "refer to 'definingobj' within a method associated with an object; " "you cannot use 'definingobj' in a function or in other code that is " "not part of an object's method." }, { TCERR_CANNOT_CALL_CONST, "this constant expression cannot be called as a function", "This constant expression cannot be called as a function. Check for " "a missing operator before an open parenthesis, since this can make " "an expression look like a function call. " }, { TCERR_REPLACED_NOT_AVAIL, "'replaced' is not available in this context", "The 'replaced' keyword is not available in this context. You can only " "use 'replaced' within a function you are defining with 'modify'. " }, { TCERR_GRAMPROD_TOO_MANY_ALTS, "grammar production \"%~.*s\" has too many (>65,535) rules", "The grammar production symbol \"%~.*s\" has too many rules - a " "single production is limited to 65,535 rules, taking into " "account all 'grammar' statements for the production. This probably " "indicate that the production has one or more grammar rules with " "deeply nested '|' alternatives. Check for very complex 'grammar' " "statements, and replace complex '|' structures with separate " "sub-productions." }, { TCERR_BAD_META_FOR_NEW, "invalid 'new' - cannot create an instance of this type", "The 'new' operator cannot be used to create an instance of this " "type of object. " }, { TCERR_MMINH_TYPE_NOT_OBJ, "'inherited<>' type list element \"%~.*s\" is not an object name", "Type names in an 'inherited<>' type list must be object or " "class names. \"%~.*s\" is not an object name, so it can't be used " "as a type name here." }, { TCERR_MMINH_BAD_CONTEXT, "'inherited<>' is not valid here; it can only be used in a multi-method", "'inherited<>' is not valid here. This syntax can only be used " "within a function defined as a multi-method." }, { TCERR_MMINH_MISSING_SUPPORT_FUNC, "'inherited<>' requires \"%~.*s\", which is not defined " "or not a function", "'inherited<>' requires the library function \"%~.*s\" to be included " "included in the build. This is not defined or is not a function. " "Check that the library file 'multmeth.t' is included in the build." }, { TCERR_MMINH_UNDEF_FUNC, "'inherited<>' function \"%~.*s\" undefined", "The function \"%~.*s\" with the specified inherited<> type list is " "not defined. The inherited<> type list must exactly match the " "definition of the function you wish to call." }, { TCERR_NAMED_PARAM_MISSING_FUNC, "named parameters require support function \"%~.*s\", which is undefined", "This program uses named parameters, so it depends on the built-in " "support function \"%~.*s\". This is not defined or is not a built-in " "function. Check that you're including the current version of the " "system header file 't3.h' in the build." }, { TCERR_OPT_PARAM_MISSING_FUNC, "optional parameters require support function \"%~.*s\", which is undefined", "This program uses optional parameters, so it depends on the built-in " "support function \"%~.*s\". This is not defined or is not a built-in " "function. Check that you're including the current version of the " "system header file 't3.h' in the build. (An optional parameter is " "a function or method argument declared with a '?' after the variable " "name, or with '=' and a default expression after the name." }, { TCERR_ONEOF_REQ_GENCLS, "<> requires class OneOfIndexGen to be defined in the program", "<> requires your program to define the class OneOfIndexGen. " "This class isn't defined. Make sure that you're using the current " "version of the system library file _main.t." }, { TCERR_ONEOF_REQ_GETNXT, "<> requires getNextIndex to be defined as a property", "<> requires your program to define the property getNextIndex, " "as part of class OneOfIndexGen. This symbol is used as a different " "type in the program. Make sure that you're using the current version " "of the sy stem library file _main.t" }, { TCERR_EXT_METACLASS, "intrinsic class '%.*s' is not defined in this module", "The intrinsic class '%.*s' is not defined in this source module. " "To refer to this class within this module, you must declare it. " "The usual way to declare an intrinsic class is simply to #include " "the system header file that defines the class, near the beginning " "of your source file." }, { TCERR_FUNC_CALL_NO_PROTO, "cannot call extern function %.*s without an argument list definition", "The function %.*s was declared 'extern' without an argument list " "definition. This function can't be called without declaring the " "argument list. You can declare the argument list in the 'extern' " "statement, but note that in most cases the problem is that the " "function itself is never defined within the program." }, { TCERR_UNDEF_METACLASS, "\"%.*s\" is not defined or is not an intrinsic class", "The compiler requires \"%.*s\" to be defined as an intrinsic class; " "the symbol is either undefined or is defined as a different type. " "Check that the system header that defines this class is #included " "in this source file, and that there are no conflicting definitions " "of the class name." }, { TCERR_SYMEXP_INV_TYPE, "invalid symbol type in symbol file", "Invalid symbol type in symbol file. The symbol file is either " "corrupted or was created by an incompatible version of the " "compiler. Regenerate the symbol file." }, { TCERR_SYMEXP_INV_SIG, "invalid symbol file signature", "Invalid symbol file signature. The symbol file is corrupted, " "was generated by an incompatible verison of the compiler, or is " "not a symbol file. Delete this symbol file and re-generate it " "from its source file. (If you do not have access to the source" "file, you must obtain an updated symbol file from the person or " "vendor who provided you with the original symbol file.)" }, { TCERR_SYMEXP_SYM_TOO_LONG, "symbol name too long in symbol file", "A symbol name stored in the symbol file is too long. The symbol " "file is either " "corrupted or was created by an incompatible version of the " "compiler. Regenerate the symbol file." }, { TCERR_SYMEXP_REDEF, "symbol \"%~.*s\" in imported symbol file is already defined; ignoring " "redefinition", "The symbol \"%~.*s\" is defined in the symbol file, but is already " "defined. This usually indicates that two symbol files that you're " "importing both define this symbol as a function, object, or property " "name (the two files might define the symbol as the same type, or as " "different types - it doesn't matter, because the symbol can only be " "used once for any of these types). You must resolve the conflict " "by renaming the symbol in one or more of the source files to make each " "name unique." }, { TCERR_OBJFILE_INV_SIG, "invalid object file signature", "Invalid object file signature. The object file is corrupted, " "was generated by an incompatible version of the compiler, or " "is not an object file. Delete this object file and re-compile " "it from its source file. (If you do not have access to the source " "file, you must obtain an updated object file from the person or " "vendor who provided you with the original object file." }, { TCERR_OBJFILE_TOO_MANY_IDS, "too many object/property ID's in object file", "This object file contains too many object or property ID's. The " "compiler cannot load this object file on this operating system. " "This usually happens only on 16-bit computers, and indicates that " "the object file exceeds the architectural limit of the compiler " "on this machine. You might be able to work around this by reducing " "the number of objects in this object file, which you can probably do " "by splitting the source file into multiple files and compiling each " "one into its own object file. If you're running on MS-DOS on a " "386 or higher processor, you can use a 32-bit version of the " "compiler, which does not have this limit." }, { TCERR_OBJFILE_OUT_OF_MEM, "out of memory loading object file", "Out of memory. The compiler cannot " "allocate memory necessary to load the object file. Make more " "memory available, if possible (by closing other applications, " "for example), then try running the compiler again." }, { TCERR_OBJFILE_INV_TYPE, "invalid symbol type in object file", "Invalid symbol type in object file. The object file is either " "corrupted or was created by an incompatible version of the " "compiler. Re-compile the object file." }, { TCERR_OBJFILE_REDEF_SYM_TYPE, "symbol \"%~.*s\" (type: %s) redefined (type: %s) in object file \"%s\"", "The symbol \"%~.*s\", which was originally defined of type %s, is " "redefined with type %s in object file \"%s\". A global symbol can " "be defined only once in the entire program. You must change one of " "the symbol's names in one of your source files to remove the " "conflict.\n\n" "If you recently changed the meaning of this symbol, you might " "simply need to do a full recompile - try building again with the -a " "option. " }, { TCERR_OBJFILE_REDEF_SYM, "symbol \"%~.*s\" (type: %s) redefined in object file \"%s\"", "The symbol \"%~.*s\" (of type %s) is redefined in object file \"%s\". " "A global symbol can be defined only once in the entire program. " "You must change one of names in one of your source files to " "remove the conflict." }, { TCERR_OBJFILE_BIF_INCOMPAT, "intrinsic function \"%~.*s\" has different definition in object file " "\"%s\"", "The intrinsic function \"%~.*s\" has a different definition in object " "file \"%s\" than in its previous definition. The intrinsic function " "sets used in your program must be identical in each object file. " "Check the \"intrinsic\" definition statements in each source file " "and ensure that they all match." }, { TCERR_OBJFILE_FUNC_INCOMPAT, "function \"%~.*s\" has different definition in object file " "\"%s\"", "The function \"%~.*s\" has a different definition in object " "file \"%s\" than in its previous definition. This probably indicates " "that one or more of your object or symbol files are out of date " "and must be recompiled." }, { TCERR_OBJFILE_INT_SYM_MISSING, "internal symbol reference \"%~.*s\" missing in object file \"%s\"", "The internal symbol reference \"%~.*s\" is missing in the object " "file \"%s\". This is an error in the object file and probably indicates" " that the file is corrupted. Delete the object file and recompile its " "source file." }, { TCERR_OBJFILE_INVAL_STREAM_ID, "invalid stream ID in object file \"%s\"", "The object file \"%s\" contains an invalid internal stream ID code. " "This is an error in the object file and probably indicates that " "the file is corrupted. Delete the object file and recompile its " "source file." }, { TCERR_OBJFILE_INVAL_OBJ_ID, "invalid object ID in object file \"%s\"", "The object file \"%s\" contains an invalid object ID code. " "This is an error in the object file and probably indicates that " "the file is corrupted. Delete the object file and recompile its " "source file." }, { TCERR_OBJFILE_INVAL_PROP_ID, "invalid property ID in object file \"%s\"", "The object file \"%s\" contains an invalid property ID code. " "This is an error in the object file and probably indicates that " "the file is corrupted. Delete the object file and recompile its " "source file." }, { TCERR_OBJFILE_INV_FN_OR_META, "invalid function set or metaclass data in object file \"%s\"", "The object file \"%s\" contains invalid function set or metaclass " "data. This is an error in the object file and probably indicates that " "the file is corrupted. Delete the object file and recompile its " "source file." }, { TCERR_OBJFILE_FNSET_CONFLICT, "conflicting intrinsic function set \"%s\" found in object file \"%s\"", "The intrinsic function set \"%s\" in object file \"%s\" conflicts with " "a previous intrinsic definition from other object files. Each " "object file must define an identical list of intrinsics, in " "the same order. Examine your source files and use the same " "intrinsic definitions in all files." }, { TCERR_OBJFILE_META_CONFLICT, "conflicting metaclass \"%s\" found in object file \"%s\"", "The metaclass \"%s\" in object file \"%s\" conflicts with " "a previous metaclass definition from other object files. Each " "object file must define an identical list of metaclasses, in " "the same order. Examine your source files and use the same " "metaclass definitions in all files." }, { TCERR_OBJFILE_MODREPOBJ_BEFORE_ORIG, "modified or replaced object \"%~.*s\" found in object file \"%s\" " "before original object definition was loaded", "The object \"%~.*s\" in object file \"%s\" is defined with \"replace\" " "or \"modify\", but this object file was loaded before the object file " "containing the original definition of the object. You must change " "the order in which you're loading the object files so that the " "object file containing the original definition of each object is " "loaded before the object's first modification or replacement." }, { TCERR_OBJFILE_REPFUNC_BEFORE_ORIG, "modified or replaced function \"%~.*s\" found in object file \"%s\" " "before original function definition was loaded", "The function \"%~.*s\" in object file \"%s\" is defined with " "\"modify\" or \"replace\", but this object file was loaded before the " "object file containing the original definition of the function. You " "must change the order in which you're loading the object files so " "that the object file containing the original definition of each " "function is loaded before the function's first replacement." }, { TCERR_CONSTRUCT_CANNOT_RET_VAL, "\"construct\" cannot return a value", "The \"construct\" method cannot return a value. Remove the " "return value expression." }, { TCERR_OBJFILE_NO_DBG, "object file \"%s\" has no debugger information " "(required to link for debug)", "The object file \"%s\" has no debugger information. In order to " "include debug information in the image file, all object files must be " "compiled with debugging information. Recompile the object file with " "debug information and then re-link the program." }, { TCERR_OBJFILE_METACLASS_PROP_CONFLICT, "intrinsic class property \"%~.*s\" does not match " "previous name (\"%~.*s\") in object file %s", "The intrinsic class property \"%~.*s\" does not match its previous " "name (\"%~.*s\") in object file %s. Each property defined in an " "intrinsic class must match in all object files where the " "intrinsic class is declared. You must recompile your source code " "using a single definition for the intrinsic class that is the same " "in each file." }, { TCERR_OBJFILE_METACLASS_IDX_CONFLICT, "intrinsic class \"%~.*s\" in object file %s out of order with " "previous definition", "The intrinsic class \"%~.*s\" is defined in object file %s in a " "different order than it appeared in previous object files. Each " "object file must define all of its intrinsic classes in the same " "order as all other object files. You must recompile your source " "code using a single set of intrinsic class definitions, all in the " "same order." }, { TCERR_OBJFILE_CANNOT_MOD_OR_REP_TYPE, "cannot modify or replace object of this type - \"%~.*s\" in " "object file %s", "You cannot use 'modify' or 'replace' to modify an object of this " "type. The symbol \"%~.*s\" is being modified or replaced in object " "file %s, but the symbol was previously defined using an incompatible " "type. You can only modify or replace ordinary objects." }, { TCERR_EXPORT_SYM_TOO_LONG, "exported symbol in object file is too long - object file may be " "corrupted", "An exported symbol in this object file is too long. This probably " "indicates a corrupted object file. Recompile the source code to " "create a new object file. If the object file was just compiled, " "this probably indicates an internal error in the compiler." }, { TCERR_OBJFILE_MACRO_SYM_TOO_LONG, "macro symbol too long in object file debug records - object file may " "be corrupted - object file %s", "A macro symbol name in the object file %s is too long. This probably " "indicates a corrupted object file. Recompile the source code to " "create a new object file. If the object file was just compiled, " "this probably indicates an internal error in the compiler. " }, { TCERR_OBJFILE_MMFUNC_INCOMPAT, "function \"%~.*s\" has conflicting definitions as multi-method and " "as regular function - object file %s", "The function \"%~*.s\" has a definition in object file \"%s\" that " "conflicts with one or more definitions in other object files. The " "function is defined both as an ordinary function and as a multi-method. " "The function must be defined exclusively as one or the other. If the " "ordinary function definition was meant to be a multi-method definition, " "add the \"multimethod\" modifier keyword immediately after the " "closing parenthesis of the parameter list in that definition." }, { TCERR_OBJFILE_MMPARAM_NOT_OBJECT, "parameter type \"%~.*s\" in multi-method \"%~.*s\" is not an object", "The declared parameter type \"%~.*s\" in the definition of the " "multi-method function \"%~.*s\" is not an object. Only object or " "class names can be used to declare parameter types. Check the " "definition of the function and make sure the type name is correct, " "and that the same name isn't used elsewhere as a non-object type." }, { TCERR_MISSING_MMREG, "library support function \"%s\" is missing or invalid - " "check that the system library is included in the build", "The system library function \"%s\" is missing or " "is not defined as a function. This function is part of the system " "run-time library, which is usually included by default in the build. " "Check that system.tl is included in the build and that the " "multi-method support module is not excluded." }, { TCERR_CONST_POOL_OVER_32K, "constant value exceeds 32k - program will not run on 16-bit machines", "This constant data item (string or list constant) exceeds 32k in " "length (as stored in the compiled program file). This program will " "not work on 16-bit computers. If you want the program to be able " "to run on 16-bit machines, break up the string or list into multiple " "values, and manipulate them separately." }, { TCERR_CONST_TOO_LARGE_FOR_PG, "string or list constant exceeds constant page size", "A string or list constant is too large to fit on " "a single constant page." }, { TCERR_CORRUPT_LIST, "corrupted internal list data", "Corrupted internal list data." }, { TCERR_GEN_CODE_INH, "attempting to generate 'inherited' node directly", "Attempting to generate 'inherited' node directly." }, { TCERR_GEN_UNK_CONST_TYPE, "unknown constant type in code generation", "Unknown constant type in code generation." }, { TCERR_GEN_BAD_LVALUE, "attempting to generate assignment for non-lvalue", "Attempting to generate assignment for non-lvalue." }, { TCERR_GEN_BAD_ADDR, "attempting to generate address for non-addressable", "Attempting to generate address for non-addressable." }, { TCERR_TOO_MANY_CALL_ARGS, "function call argument list exceeds machine limit (maximum 127)", "This function call has too many arguments; the T3 VM limits function " "and method calls to a maximum of 127 arguments. Check for missing " "parentheses if you didn't intend to pass this many arguments. If the " "function actually does take more than 127 arguments, you will have " "to modify your program to reduce the argument count; you could do " "this by grouping arguments into an object or list value, or by " "breaking the function into several simpler functions." }, { TCERR_TOO_MANY_CTOR_ARGS, "'new' argument list exceeds machine limit (maximum 126)", "This 'new' operator has too many arguments; the T3 VM limits " "constructor calls to a maximum of 126 arguments. Check for missing " "parentheses if you didn't intend to pass this many arguments. If the " "constructor actually does take more than 126 arguments, you will have " "to modify your program to reduce the argument count; you could do " "this by grouping arguments into an object or list value." }, { TCERR_GEN_CODE_DELEGATED, "attempting to generate 'delegated' node directly", "Attempting to generate 'delegated' node directly." }, { TCERR_CODE_POOL_OVER_32K, "code block size exceeds 32k - program will not run on 16-bit machines", "This code block (a function or method body) exceeds 32k in length " "(as stored in the compiled program file). This program will not work " "on 16-bit computers. If you want the program to be able to run on " "16-bit machines, break up this function or method into multiple " "pieces." }, { TCERR_GEN_BAD_CASE, "case/default without switch or non-constant case expression", "case/default without switch or non-constant case expression" }, { TCERR_CATCH_FINALLY_GEN_CODE, "catch/finally gen_code called directly", "catch/finally gen_code called directly" }, { TCERR_INVAL_PROP_CODE_GEN, "invalid property value type for code generation", "invalid property value type for code generation" }, { TCERR_RESERVED_EXPORT, "external name \"%~.*s\" illegal in 'export' - reserved for compiler use", "The external name \"%~.*s\" illegally appears in an 'export' statement. " "The compiler automatically provides an export for this symbol, " "so the program cannot explicitly export this name itself." }, { TCERR_EXPR_TOO_COMPLEX, "expression too complex", "The expression is too complex. If possible, simplify the " "expression by breaking it up into sub-expressions, storing " "intermediate values in local variables." }, { TCERR_CONSTRUCT_NOT_DEFINED, "property \"construct\" is not defined", "No property named \"construct\" is defined. Creating an object " "with operator 'new' invokes this property if defined." }, { TCERR_CONSTRUCT_NOT_PROP, "\"construct\" is not a property", "The symbol \"construct\" does not refer to a property. Creating " "an object with operator 'new' invokes this property if defined." }, { TCERR_FINALIZE_NOT_DEFINED, "property \"finalize\" is not defined", "No property named \"finalize\" is defined. This property is " "invoked when an object is about to be deleted during garbage " "collection." }, { TCERR_FINALIZE_NOT_PROP, "\"finalize\" is not a property", "The symbol \"finalize\" does not refer to a property. This property " "is invoked when an object is about to be deleted during garbage " "collection." }, { TCERR_BAD_JS_TPL, "invalid js template", "Invalid js template." }, { TCERR_JS_EXPR_EXPAN_OVF, "js template expansion overflow", "Overflow expanding js template." } }; /* english message count */ size_t tc_message_count_english = sizeof(tc_messages_english)/sizeof(tc_messages_english[0]); /* * the actual message array - we'll initialize this to the english list * that's linked in, but if we find an external message file, we'll use * the external file instead */ const err_msg_t *tc_messages = tc_messages_english; /* message count - initialize to the english message array count */ size_t tc_message_count = sizeof(tc_messages_english)/sizeof(tc_messages_english[0]); qtads-2.1.7/tads3/tcerrnum.h000066400000000000000000001272071265017072300156770ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/TCERRNUM.H,v 1.5 1999/07/11 00:46:53 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcerrnum.h - T3 Compiler Error Numbers Function Notes All T3 Compiler error numbers are in the range 10000-19999. Modified 04/15/99 MJRoberts - Creation */ #ifndef TCERRNUM_H #define TCERRNUM_H /* ------------------------------------------------------------------------ */ /* * Tokenizer and Preprocessor Errors */ /* out of memory for source line - source line is too long */ #define TCERR_LINE_MEM 10001 /* invalid preprocessor directive "%.*s" */ #define TCERR_INV_PP_DIR 10002 /* unable to load character set specified in #charset directive */ #define TCERR_CANT_LOAD_CHARSET 10003 /* * unexpected or invalid #charset - directive must be at the very * beginning of the file, and must specify a character set name enclosed * in double quotes */ #define TCERR_UNEXPECTED_CHARSET 10004 /* invalid #include syntax - required '"' or '< >' around filename */ #define TCERR_BAD_INC_SYNTAX 10005 /* #include file "%.*s" not found */ #define TCERR_INC_NOT_FOUND 10006 /* #include file "%.*s" previously included; redundant inclusion ignored */ #define TCERR_REDUNDANT_INCLUDE 10007 /* invalid symbol "%.*s" for #define */ #define TCERR_BAD_DEFINE_SYM 10008 /* out of memory for token text */ #define TCERR_NO_MEM_TOKEN 10009 /* missing ')' in macro parameter list */ #define TCERR_MACRO_NO_RPAR 10010 /* invalid macro parameter name "%.*s" */ #define TCERR_BAD_MACRO_ARG_NAME 10011 /* expected comma or ')' in macro parameter list */ #define TCERR_MACRO_EXP_COMMA 10012 /* redefinition of macro "%.*s" */ #define TCERR_MACRO_REDEF 10013 /* unrecognized pragma "%.*s" */ #define TCERR_UNKNOWN_PRAGMA 10014 /* pragma syntax error */ #define TCERR_BAD_PRAGMA_SYNTAX 10015 /* extra characters after end of preprocessor directive */ #define TCERR_PP_EXTRA 10016 /* #if nesting too deep */ #define TCERR_IF_NESTING_OVERFLOW 10017 /* #else without #if */ #define TCERR_PP_ELSE_WITHOUT_IF 10018 /* #endif without #if */ #define TCERR_PP_ENDIF_WITHOUT_IF 10019 /* #elif without #if */ #define TCERR_PP_ELIF_WITHOUT_IF 10020 /* numeric value required in preprocessor constant expression */ #define TCERR_PP_INT_REQUIRED 10021 /* incompatible types for comparison in preprocessor constant expression */ #define TCERR_PP_INCOMP_TYPES 10022 /* extra characters after end of preprocessor constant expression */ #define TCERR_PP_EXPR_EXTRA 10023 /* division by zero in preprocessor constant expression */ #define TCERR_PP_DIV_ZERO 10024 /* * expected number, symbol, or single-quoted string in preprocessor * constant expression, got "%.*s" */ #define TCERR_PP_INVALID_VALUE 10025 /* unterminated string */ #define TCERR_PP_UNTERM_STRING 10026 /* unmatched left parenthesis in preprocessor constant expression */ #define TCERR_PP_UNMATCHED_LPAR 10027 /* number or true/nil required for "!" operator in preprocessor expression */ #define TCERR_PP_BAD_NOT_VAL 10028 /* missing right paren in macro expansion (must be on same line for #) */ #define TCERR_PP_MACRO_ARG_RPAR_1LINE 10029 /* argument list must be provided in macro invocation */ #define TCERR_PP_NO_MACRO_ARGS 10030 /* not enough arguments in macro invocation */ #define TCERR_PP_FEW_MACRO_ARGS 10031 /* no close paren in macro argument list */ #define TCERR_PP_MACRO_ARG_RPAR 10032 /* too many arguments in macro invocation */ #define TCERR_PP_MANY_MACRO_ARGS 10033 /* symbol required for defined() preprocessor operator */ #define TCERR_PP_DEFINED_NO_SYM 10034 /* missing ')' in defined() preprocess operator */ #define TCERR_PP_DEFINED_RPAR 10035 /* symbol "%.*s" too long; truncated to "%.*s" */ #define TCERR_SYMBOL_TRUNCATED 10036 /* too many formal parameters defined for macro (maximum %d) */ #define TCERR_TOO_MANY_MAC_PARMS 10037 /* out of memory for string buffer */ #define TCERR_NO_STRBUF_MEM 10038 /* unable to open source file "%.*s" */ #define TCERR_CANT_OPEN_SRC 10039 /* #error - %.*s */ #define TCERR_ERROR_DIRECTIVE 10040 /* out of memory for macro expansion */ #define TCERR_OUT_OF_MEM_MAC_EXP 10041 /* integer value required for line number in #line */ #define TCERR_LINE_REQ_INT 10042 /* string value required for filename in #line */ #define TCERR_LINE_FILE_REQ_STR 10043 /* #if without #endif at line %ld in file %s */ #define TCERR_IF_WITHOUT_ENDIF 10044 /* internal error: unsplicing not from current line */ #define TCERR_UNSPLICE_NOT_CUR 10045 /* internal error: unsplicing more than once in same line */ #define TCERR_MULTI_UNSPLICE 10046 /* #elif not in same file as #if */ #define TCERR_PP_ELIF_NOT_IN_SAME_FILE 10047 /* #else not in same file as #if */ #define TCERR_PP_ELSE_NOT_IN_SAME_FILE 10048 /* #endif not in same file as #if */ #define TCERR_PP_ENDIF_NOT_IN_SAME_FILE 10049 /* cannot #define "defined" - symbol reserved as preprocessor operator */ #define TCERR_REDEF_OP_DEFINED 10050 /* string appears unterminated (';' or '}' appears alone on a line) */ #define TCERR_POSSIBLE_UNTERM_STR 10051 /* source line too long after macro expansion */ #define TCERR_SRCLINE_TOO_LONG 10052 /* invalid character */ #define TCERR_INVALID_CHAR 10053 /* ":" missing in "?" expression */ #define TCERR_PP_QC_MISSING_COLON 10054 /* preprocessor expression is not a constant value */ #define TCERR_PP_EXPR_NOT_CONST 10055 /* unable to load default character set "%s" */ #define TCERR_CANT_LOAD_DEFAULT_CHARSET 10056 /* '...' in macro formal parameter list must be followed by ')' */ #define TCERR_MACRO_ELLIPSIS_REQ_RPAR 10057 /* #foreach nesting too deep */ #define TCERR_PP_FOREACH_TOO_DEEP 10058 /* trailing whitespace after backslash */ #define TCERR_TRAILING_SP_AFTER_BS 10059 /* extraneous characters after #include filename */ #define TCERR_EXTRA_INC_SYNTAX 10060 /* nested comment? */ #define TCERR_NESTED_COMMENT 10061 /* unmappable character */ #define TCERR_UNMAPPABLE_CHAR 10062 /* decimal digit found in octal constant */ #define TCERR_DECIMAL_IN_OCTAL 10063 /* '<>' operator is obsolete */ #define TCERR_LTGT_OBSOLETE 10064 /* integer constant exceeds maximum value (promoting to BigNumber) */ #define TCERR_INT_CONST_OV 10065 /* unrecognized escape sequence \%c in string */ #define TCERR_BACKSLASH_SEQ 10066 /* non-ASCII character in symbol */ #define TCERR_NON_ASCII_SYMBOL 10067 /* embedded expressions in string nested too deeply */ #define TCERR_EMBEDDING_TOO_DEEP 10068 /* ------------------------------------------------------------------------ */ /* * General Messages */ /* internal error explanation */ #define TCERR_INTERNAL_EXPLAN 10500 /* * internal error - after we log an internal error, we'll throw this * generic error code; since internal errors are unrecoverable, any * frames that catch this error should simply clean up and terminate as * gracefully as possible */ #define TCERR_INTERNAL_ERROR 10501 /* * fatal error - after we log a fatal error, we'll throw this generic * error code; any frames that catch this error should simply clean up * and terminate */ #define TCERR_FATAL_ERROR 10502 /* ------------------------------------------------------------------------ */ /* * "Make" errors */ /* cannot create symbol file "%s" */ #define TCERR_MAKE_CANNOT_CREATE_SYM 10600 /* cannot create object file "%s" */ #define TCERR_MAKE_CANNOT_CREATE_OBJ 10601 /* cannot create image file "%s" */ #define TCERR_MAKE_CANNOT_CREATE_IMG 10602 /* cannot open symbol file "%s" for reading */ #define TCERR_MAKE_CANNOT_OPEN_SYM 10603 /* cannot open object file "%s" for reading */ #define TCERR_MAKE_CANNOT_OPEN_OBJ 10604 /* cannot open image file "%s" */ #define TCERR_MAKE_CANNOT_OPEN_IMG 10605 /* maximum error count exceeded */ #define TCERR_TOO_MANY_ERRORS 10606 /* same name used for more than one source module */ #define TCERR_MODULE_NAME_COLLISION 10607 /* same name used for module as for module from a given library */ #define TCERR_MODULE_NAME_COLLISION_WITH_LIB 10608 /* "source-file-name (from library library-name)" */ #define TCERR_SOURCE_FROM_LIB 10609 /* cannot create directory "%s" */ #define TCERR_CANNOT_CREATE_DIR 10610 /* ------------------------------------------------------------------------ */ /* * Parsing Errors */ /* divide by zero in constant expression */ #define TCERR_CONST_DIV_ZERO 11001 /* out of memory for parse tree block */ #define TCERR_NO_MEM_PRS_TREE 11002 /* parse tree node too large (size = %ld) (internal error) */ #define TCERR_PRS_BLK_TOO_BIG 11003 /* invalid lvalue (operator "%s") */ #define TCERR_INVALID_LVALUE 11004 /* expected ':' in '?' operator expression */ #define TCERR_QUEST_WITHOUT_COLON 11005 /* invalid lvalue for unary operator (++ or --) */ #define TCERR_INVALID_UNARY_LVALUE 11006 /* operator delete is obsolete */ #define TCERR_DELETE_OBSOLETE 11007 /* can't take address of expression */ #define TCERR_NO_ADDRESS 11008 /* invalid constant type for unary '%s': number required */ #define TCERR_CONST_UNARY_REQ_NUM 11009 /* invalid constant type for binary '%s': number required */ #define TCERR_CONST_BINARY_REQ_NUM 11010 /* incompatible constant types for '+' operator */ #define TCERR_CONST_BINPLUS_INCOMPAT 11011 /* missing ')' in expression */ #define TCERR_EXPR_MISSING_RPAR 11012 /* invalid primary expression */ #define TCERR_BAD_PRIMARY_EXPR 11013 /* constant types are not comparable */ #define TCERR_CONST_BAD_COMPARE 11014 /* expected ';', found "%.*s" */ #define TCERR_EXPECTED_SEMI 11015 /* expected ">>" and the continuation of the string, found "%.*s" */ #define TCERR_EXPECTED_STR_CONT 11016 /* expected ',' in argument list, found "%.*s" */ #define TCERR_EXPECTED_ARG_COMMA 11017 /* expected ']' in subscript, found "%.*s" */ #define TCERR_EXPECTED_SUB_RBRACK 11018 /* invalid expression after '.' - property name or expression required */ #define TCERR_INVALID_PROP_EXPR 11019 /* found "%.*s" in list, assuming that list is missing ']' */ #define TCERR_LIST_MISSING_RBRACK 11020 /* extraneous ')' in list */ #define TCERR_LIST_EXTRA_RPAR 11021 /* expected ',' separating list elements, but found "%.*s" */ #define TCERR_LIST_EXPECT_COMMA 11022 /* constant list index value must be an integer value */ #define TCERR_CONST_IDX_NOT_INT 11023 /* constant list index out of range */ #define TCERR_CONST_IDX_RANGE 11024 /* unterminated string (string started with %c%*.s%c) */ #define TCERR_UNTERM_STRING 11025 /* missing ')' in argument list */ #define TCERR_EXPECTED_ARG_RPAR 11026 /* extra ')' - ignoring */ #define TCERR_EXTRA_RPAR 11027 /* extra ']' - ignoring */ #define TCERR_EXTRA_RBRACK 11028 /* expected operand, found '%s' */ #define TCERR_EXPECTED_OPERAND 11029 /* expected property name pattern string after 'propertyset', found "%.*s" */ #define TCERR_PROPSET_REQ_STR 11030 /* invalid "inherited class" syntax - expected '.', found "%.*s" */ #define TCERR_INH_CLASS_SYNTAX 11031 /* undefined symbol "%.*s" */ #define TCERR_UNDEF_SYM 11032 /* undefined symbol "%.*s" - assuming that it's a property */ #define TCERR_ASSUME_SYM_PROP 11033 /* incompatible constant types for '-' operator */ #define TCERR_CONST_BINMINUS_INCOMPAT 11034 /* expected a symbol in formal parameter list, found "%.*s" */ #define TCERR_REQ_SYM_FORMAL 11035 /* expected a comma in formal parameter list, found "%.*s" */ #define TCERR_REQ_COMMA_FORMAL 11036 /* missing parameter at end of formal parameter list */ #define TCERR_MISSING_LAST_FORMAL 11037 /* missing right paren at end of formal parameter list (found "%.*s") */ #define TCERR_MISSING_RPAR_FORMAL 11038 /* formal parameter "%.*s" defined more than once */ #define TCERR_FORMAL_REDEF 11039 /* '=' in method definition is obsolete */ #define TCERR_EQ_WITH_METHOD_OBSOLETE 11040 /* '{' required at the start of code body - found "%.*s" */ #define TCERR_REQ_LBRACE_CODE 11041 /* unexpected end of file - '}' required */ #define TCERR_EOF_IN_CODE 11042 /* expected '(' after 'if', but found "%.*s" */ #define TCERR_REQ_LPAR_IF 11043 /* misplaced 'else' */ #define TCERR_MISPLACED_ELSE 11044 /* misplaced 'case' */ #define TCERR_MISPLACED_CASE 11045 /* misplaced 'default' */ #define TCERR_MISPLACED_DEFAULT 11046 /* ellipsis must be the last formal parameter */ #define TCERR_ELLIPSIS_NOT_LAST 11047 /* expected ',' or ';' after symbol, but found "%.*s" */ #define TCERR_LOCAL_REQ_COMMA 11048 /* expected symbol name in local variable definition, but found "%.*s" */ #define TCERR_LOCAL_REQ_SYM 11049 /* expected symbol name in function defintion */ #define TCERR_FUNC_REQ_SYM 11050 /* expected code body */ #define TCERR_REQ_CODE_BODY 11051 /* expected function or object definition */ #define TCERR_REQ_FUNC_OR_OBJ 11052 /* expected ';' or expression after 'return', but found "%.*s" */ #define TCERR_RET_REQ_EXPR 11053 /* unreachable code */ #define TCERR_UNREACHABLE_CODE 11054 /* code has 'return' statements both with and without values */ #define TCERR_RET_VAL_AND_VOID 11055 /* code has value returns but also falls off end */ #define TCERR_RET_VAL_AND_IMP_VOID 11056 /* function set name expected after 'intrinsic' keyword, but found "%.*s" */ #define TCERR_REQ_INTRINS_NAME 11057 /* open brace required ater intrinsic function set name */ #define TCERR_REQ_INTRINS_LBRACE 11058 /* end of file in 'intrinsic' function set list */ #define TCERR_EOF_IN_INTRINS 11059 /* missing open paren after function name in 'instrinsic' - found "%.*s" */ #define TCERR_REQ_INTRINS_LPAR 11060 /* expected function name in 'intrinsic' list, but found "%.*s" */ #define TCERR_REQ_INTRINS_SYM 11061 /* expected '(' after 'for', but found "%.*s" */ #define TCERR_REQ_FOR_LPAR 11062 /* local variable "%.*s" is already defined in this scope */ #define TCERR_LOCAL_REDEF 11063 /* initializer is required for "local" in "for" - found "%.*s" */ #define TCERR_REQ_FOR_LOCAL_INIT 11064 /* missing expression after ',' in "for" initializer */ #define TCERR_MISSING_FOR_INIT_EXPR 11065 /* missing expression in "for" statement */ #define TCERR_MISSING_FOR_PART 11066 /* ',' or ';' expected in "for" initializer, but found "%.*s" */ #define TCERR_REQ_FOR_INIT_COMMA 11067 /* ';' expected in "for" after condition expression, but found "%.*s" */ #define TCERR_REQ_FOR_COND_SEM 11068 /* missing ')' at end of 'for' statement expression list */ #define TCERR_REQ_FOR_RPAR 11069 /* "for" condition is always false - reinitializer and body are unreachable */ #define TCERR_FOR_COND_FALSE 11070 /* '(' missing after 'while' - found "%.*s" instead */ #define TCERR_REQ_WHILE_LPAR 11071 /* ')' missing at end of 'while' expression - found "%.*s" instead */ #define TCERR_REQ_WHILE_RPAR 11072 /* "while" condition is always false - body is unreachable */ #define TCERR_WHILE_COND_FALSE 11073 /* "while" missing in "do" statement (found "%.*s" instead) */ #define TCERR_REQ_DO_WHILE 11074 /* misplaced "catch" - not part of "try" */ #define TCERR_MISPLACED_CATCH 11075 /* misplaced "finally" - not part of "try" */ #define TCERR_MISPLACED_FINALLY 11076 /* '(' missing after 'switch' - found "%.*s" instead */ #define TCERR_REQ_SWITCH_LPAR 11077 /* ')' missing at end of 'switch' expression - found "%.*s" instead */ #define TCERR_REQ_SWITCH_RPAR 11078 /* '{' missing after 'switch' expression - found "%.*s" instead */ #define TCERR_REQ_SWITCH_LBRACE 11079 /* code preceding first 'case' or 'default' label in switch is not allowed */ #define TCERR_UNREACHABLE_CODE_IN_SWITCH 11080 /* end of file in switch body */ #define TCERR_EOF_IN_SWITCH 11081 /* duplicate code label "%.*s" */ #define TCERR_CODE_LABEL_REDEF 11082 /* ':' missing after 'case' expression - found "%.*s" instead */ #define TCERR_REQ_CASE_COLON 11083 /* 'case' expression has a non-constant value */ #define TCERR_CASE_NOT_CONSTANT 11084 /* ':' missing after 'default' - found "%.*s" instead */ #define TCERR_REQ_DEFAULT_COLON 11085 /* switch already has 'default' label */ #define TCERR_DEFAULT_REDEF 11086 /* 'try' has no 'catch' or 'finally' clauses */ #define TCERR_TRY_WITHOUT_CATCH 11087 /* '(' required after 'catch' - found "%.*s" instead */ #define TCERR_REQ_CATCH_LPAR 11088 /* ')' required after 'catch' variable name - found "%.*s" instead */ #define TCERR_REQ_CATCH_RPAR 11089 /* exception class name symbol required in 'catch' - found "%.*s" instead */ #define TCERR_REQ_CATCH_CLASS 11090 /* variable name symbol required in 'catch' - found "%.*s" instead */ #define TCERR_REQ_CATCH_VAR 11091 /* 'catch' target variable "%.*s" is not a local variable */ #define TCERR_CATCH_VAR_NOT_LOCAL 11092 /* label expected after 'break' - found "%.*s" */ #define TCERR_BREAK_REQ_LABEL 11093 /* label expected after 'continue' - found "%.*s" */ #define TCERR_CONT_REQ_LABEL 11094 /* label expected after 'goto' - found "%.*s" */ #define TCERR_GOTO_REQ_LABEL 11095 /* symbol "%.*s" is already defined - can't use as a new function name */ #define TCERR_REDEF_AS_FUNC 11096 /* expected 'function' or 'object' after 'extern', but found '%.*s' */ #define TCERR_INVAL_EXTERN 11097 /* code body is not allowed in an 'extern function' definition */ #define TCERR_EXTERN_NO_CODE_BODY 11098 /* function "%.*s" is already defined */ #define TCERR_FUNC_REDEF 11099 /* function "%.*s" has a previous incompatible definition */ #define TCERR_INCOMPAT_FUNC_REDEF 11100 /* expected ':' in object definition after object name, but found "%.*s" */ #define TCERR_OBJDEF_REQ_COLON 11101 /* expected superclass name in object definition, but found "%.*s" */ #define TCERR_OBJDEF_REQ_SC 11102 /* superclasses cannot be specified with 'object' as the base class */ #define TCERR_OBJDEF_OBJ_NO_SC 11103 /* symbol "%.*s" is already defined - can't redefine as an object */ #define TCERR_REDEF_AS_OBJ 11104 /* object "%.*s" is already defined */ #define TCERR_OBJ_REDEF 11105 /* expected property name in definition of object "%.*s", but found "%.*s" */ #define TCERR_OBJDEF_REQ_PROP 11106 /* redefining symbol "%.*s" as property */ #define TCERR_REDEF_AS_PROP 11107 /* expected prop value or method after property name %.*s, but found %.*s */ #define TCERR_OBJDEF_REQ_PROPVAL 11108 /* expected 'function' or object name after 'replace', but found '%.*s' */ #define TCERR_REPLACE_REQ_OBJ_OR_FUNC 11109 /* replace/modify cannot be used with an undefined object */ #define TCERR_REPMODOBJ_UNDEF 11110 /* double-quoted strings cannot be used within expressions */ #define TCERR_DQUOTE_IN_EXPR 11111 /* assignment in condition (possible use of '=' where '==' was intended) */ #define TCERR_ASI_IN_COND 11112 /* replace/modify cannot be used with an undefined function */ #define TCERR_REPFUNC_UNDEF 11113 /* 'replace' can only be applied to a property in a 'modify' object */ #define TCERR_REPLACE_PROP_REQ_MOD_OBJ 11114 /* expected object name symbol in 'extern' but found "%.*s" */ #define TCERR_EXTERN_OBJ_REQ_SYM 11115 /* property "%.*s" is already defined in object "%.*s" */ #define TCERR_PROP_REDEF_IN_OBJ 11116 /* '=' required between property name and value - found "%.*s" */ #define TCERR_PROP_REQ_EQ 11117 /* extra list element expected after comma, but found end of list */ #define TCERR_LIST_EXPECT_ELEMENT 11118 /* expected class name string after name string but found "%.*s" */ #define TCERR_REQ_INTRINS_CLASS_NAME 11119 /* open brace required ater intrinsic class name */ #define TCERR_REQ_INTRINS_CLASS_LBRACE 11120 /* end of file in 'intrinsic class' property list */ #define TCERR_EOF_IN_INTRINS_CLASS 11121 /* expected property name in 'intrinsic class' list, but found "%.*s" */ #define TCERR_REQ_INTRINS_CLASS_PROP 11122 /* expected class name symbol after 'intrinsic class' but found "%.*s" */ #define TCERR_REQ_INTRINS_CLASS_NAME_SYM 11123 /* redefining symbol "%.*s" as a metaclass name */ #define TCERR_REDEF_INTRINS_NAME 11124 /* cannot evaluate metaclass symbol */ #define TCERR_CANNOT_EVAL_METACLASS 11125 /* expected 'property' or object name after 'dictionary', but found "%.*s" */ #define TCERR_DICT_SYNTAX 11126 /* expected property name in 'dictionary property' list, but found "%.*s" */ #define TCERR_DICT_PROP_REQ_SYM 11127 /* expected comma in 'dictionary property' list, but found "%.*s" */ #define TCERR_DICT_PROP_REQ_COMMA 11128 /* redefining object "%.*s" as dictionary */ #define TCERR_REDEF_AS_DICT 11129 /* undefined symbol "%.*s" (used as superclass of "%.*s") */ #define TCERR_UNDEF_SYM_SC 11130 /* vocabulary property requires one or more single-quoted string values */ #define TCERR_VOCAB_REQ_SSTR 11131 /* vocabulary property cannot be used without a dictionary */ #define TCERR_VOCAB_NO_DICT 11132 /* list varargs parameter not last */ #define TCERR_LISTPAR_NOT_LAST 11133 /* expected ']' after list varargs parameter name, found "%.*s" */ #define TCERR_LISTPAR_REQ_RBRACK 11134 /* expected list varargs parameter name, but found "%.*s" */ #define TCERR_LISTPAR_REQ_SYM 11135 /* anonymous functions are not allowed in the debugger */ #define TCERR_DBG_NO_ANON_FUNC 11136 /* anonymous function requires 'new' */ #define TCERR_ANON_FUNC_REQ_NEW 11137 /* expected production name after 'grammar' */ #define TCERR_GRAMMAR_REQ_SYM 11138 /* expected colon after production name in 'grammar' statement */ #define TCERR_GRAMMAR_REQ_COLON 11139 /* expected object or property name in 'grammar' token list, found "%.*s" */ #define TCERR_GRAMMAR_REQ_OBJ_OR_PROP 11140 /* property name must follow '->' in 'grammar' statement, found "%.*s" */ #define TCERR_GRAMMAR_ARROW_REQ_PROP 11141 /* invalid token list entry "%.*s" */ #define TCERR_GRAMMAR_INVAL_TOK 11142 /* redefining object "%.*s" as grammar production */ #define TCERR_REDEF_AS_GRAMPROD 11143 /* object "%.*s" is not valid in a grammar list (only productions allowed) */ #define TCERR_GRAMMAR_REQ_PROD 11144 /* symbol expected in enum list, but found "%.*s" */ #define TCERR_ENUM_REQ_SYM 11145 /* symbol "%.*s" is already defined - can't use as an enum name */ #define TCERR_REDEF_AS_ENUM 11146 /* comma expected in 'enum' symbol list, but found "%.*s" */ #define TCERR_ENUM_REQ_COMMA 11147 /* enumerator "%.*s" in 'grammar' list must be declare with 'enum token' */ #define TCERR_GRAMMAR_BAD_ENUM 11148 /* '*' must be the last token in a 'grammar' alt list - found "%.*s" */ #define TCERR_GRAMMAR_STAR_NOT_END 11149 /* grammar qualifiers must precede all tokens in the alternative */ #define TCERR_GRAMMAR_QUAL_NOT_FIRST 11150 /* symbol required in grammar qualifier - found "%.*s" */ #define TCERR_GRAMMAR_QUAL_REQ_SYM 11151 /* invalid grammar qualifier "%.*s" */ #define TCERR_BAD_GRAMMAR_QUAL 11152 /* integer constant required for grammar qualifier "%s" */ #define TCERR_GRAMMAR_QUAL_REQ_INT 11153 /* ']' required at end of grammar qualifier - found "%.*s" */ #define TCERR_GRAMMAR_QUAL_REQ_RBRACK 11154 /* '+ property' statement requires symbol, found "%.*s" instead */ #define TCERR_PLUSPROP_REQ_SYM 11155 /* too many '+' signs - location is not defined */ #define TCERR_PLUSOBJ_TOO_MANY 11156 /* property name required after "%s" in object template, but found "%.*s" */ #define TCERR_OBJ_TPL_OP_REQ_PROP 11157 /* property name required in string in object template, but found "%.*s" */ #define TCERR_OBJ_TPL_STR_REQ_PROP 11158 /* ']' required after list property in object template, found "%.*s" */ #define TCERR_OBJ_TPL_REQ_RBRACK 11159 /* invalid 'object template' syntax - "%.*s" */ #define TCERR_OBJ_TPL_BAD_TOK 11160 /* symbol \"%.*s\" in object template is not a property */ #define TCERR_OBJ_TPL_SYM_NOT_PROP 11161 /* object definition does not match any object template */ #define TCERR_OBJ_DEF_NO_TEMPLATE 11162 /* dictionary properties are not valid in object templates */ #define TCERR_OBJ_TPL_NO_VOCAB 11163 /* property duplicated in template list */ #define TCERR_OBJ_TPL_PROP_DUP 11164 /* metaclass is already defined as intrinsic class "%.*s" */ #define TCERR_META_ALREADY_DEF 11165 /* missing ';' at end of object definition - found "%.*s" */ #define TCERR_OBJ_DEF_REQ_SEM 11166 /* expected a statement, but found "%.*s" */ #define TCERR_EXPECTED_STMT_START 11167 /* missing colon at end of short anonymous function formals - found "%.*s" */ #define TCERR_MISSING_COLON_FORMAL 11168 /* semicolon is not allowed in a short anonymous function */ #define TCERR_SEM_IN_SHORT_ANON_FN 11169 /* close brace missing at end of anonymous function */ #define TCERR_SHORT_ANON_FN_REQ_RBRACE 11170 /* '(' required after 'in' operator */ #define TCERR_IN_REQ_LPAR 11171 /* expected comma in 'in' list */ #define TCERR_EXPECTED_IN_COMMA 11172 /* missing ')' at end of 'in' list */ #define TCERR_EXPECTED_IN_RPAR 11173 /* cannot modify or replace objects of this type */ #define TCERR_CANNOT_MOD_OR_REP_TYPE 11174 /* expected '(' after 'foreach', but found "%.*s" */ #define TCERR_REQ_FOREACH_LPAR 11175 /* missing expression in 'foreach' - found "%.*s" */ #define TCERR_MISSING_FOREACH_EXPR 11176 /* 'in' required in 'foreach' - found "%.*s" */ #define TCERR_FOREACH_REQ_IN 11177 /* missing ')' at end of 'for' statement expression list */ #define TCERR_REQ_FOREACH_RPAR 11178 /* property declaration requires symbol, but found "%.*s" */ #define TCERR_PROPDECL_REQ_SYM 11179 /* property declaration requires comma, but found "%.*s" */ #define TCERR_PROPDECL_REQ_COMMA 11180 /* expected symbol in 'export' statement, but found "%.*s" */ #define TCERR_EXPORT_REQ_SYM 11181 /* external name "%.*s" too long in 'export' statement */ #define TCERR_EXPORT_EXT_TOO_LONG 11182 /* unterminated object definition */ #define TCERR_UNTERM_OBJ_DEF 11183 /* missing right brace at end of object definition - found "%.*s" */ #define TCERR_OBJ_DEF_REQ_RBRACE 11184 /* right paren required after name-tag in 'grammar' - found "%.*s" */ #define TCERR_GRAMMAR_REQ_NAME_RPAR 11185 /* open brace required for propertyset - found "%.*s" */ #define TCERR_PROPSET_REQ_LBRACE 11186 /* right paren expected at end of group - found "%.*s" */ #define TCERR_GRAMMAR_REQ_RPAR_AFTER_GROUP 11187 /* '->' not allowed after parenthesized group in 'grammar' statement */ #define TCERR_GRAMMAR_GROUP_ARROW_NOT_ALLOWED 11188 /* cannot use template with unimported extern class as superclass */ #define TCERR_OBJ_DEF_CANNOT_USE_TEMPLATE 11189 /* superclass list missing for 'replace' object definition */ #define TCERR_REPLACE_OBJ_REQ_SC 11190 /* propertyset nesting too deep */ #define TCERR_PROPSET_TOO_DEEP 11191 /* propertyset token expansion too large */ #define TCERR_PROPSET_TOK_TOO_LONG 11192 /* invalid pattern string for propertyset - "%.*s" */ #define TCERR_PROPSET_INVAL_PAT 11193 /* invalid propertyset formal parameter list */ #define TCERR_PROPSET_INVAL_FORMALS 11194 /* circular class definition - "%.*s" is a subclass of "%.*s" */ #define TCERR_CIRCULAR_CLASS_DEF 11195 /* property required in part-of-speech list in 'grammar', but found "%.*s" */ #define TCERR_GRAMMAR_LIST_REQ_PROP 11196 /* missing '>' in 'grammar' property list - found "%.*s" */ #define TCERR_GRAMMAR_LIST_UNCLOSED 11197 /* invalid indexed type in constant expression (expected list) */ #define TCERR_CONST_IDX_INV_TYPE 11198 /* 'transient' is not allowed here (expected object definition) */ #define TCERR_INVAL_TRANSIENT 11199 /* 'modify/replace grammar' can only be applied to tagged rule name */ #define TCERR_GRAMMAR_MOD_REQ_TAG 11200 /* intrinsic superclass name expected after colon, but found "%.*s" */ #define TCERR_REQ_INTRINS_SUPERCLASS_NAME 11201 /* intrinsic superclass "%.*s" undefined */ #define TCERR_INTRINS_SUPERCLASS_UNDEF 11202 /* grammar rule ends with '|' */ #define TCERR_GRAMMAR_ENDS_WITH_OR 11203 /* grammar rule is completely empty */ #define TCERR_GRAMMAR_EMPTY 11204 /* empty template definition */ #define TCERR_TEMPLATE_EMPTY 11205 /* expected ')' after 'if' condition, but found "%.*s" */ #define TCERR_REQ_RPAR_IF 11206 /* intrinsic class superclass "%.*s" is not an intrinsic class */ #define TCERR_INTRINS_SUPERCLASS_NOT_INTRINS 11207 /* function redefined as multimethod */ #define TCERR_FUNC_REDEF_AS_MULTIMETHOD 11208 /* "multimethod" declaration not allowed in this context */ #define TCERR_MULTIMETHOD_NOT_ALLOWED 11209 /* multi-method parameter type %.*s is not an object */ #define TCERR_MMPARAM_NOT_OBJECT 11210 /* type name missing in inherited<> type list */ #define TCERR_MMINH_MISSING_ARG_TYPE 11211 /* comma is missing in inherited<> type list */ #define TCERR_MMINH_MISSING_COMMA 11212 /* '>' is missing at end of inherited<> type list */ #define TCERR_MMINH_MISSING_GT 11213 /* argument list is missing after inherited<> */ #define TCERR_MMINH_MISSING_ARG_LIST 11214 /* "..." cannot be used with a named argument value */ #define TCERR_NAMED_ARG_NO_ELLIPSIS 11215 /* argument %.*s must be optional */ #define TCERR_ARG_MUST_BE_OPT 11216 /* named argument '%.*s' has a type - named arguments can't be typed */ #define TCERR_NAMED_ARG_NO_TYPE 11217 /* expected -> instead of ',' after %dth element in LookupTable list */ #define TCERR_LOOKUP_LIST_EXPECTED_ARROW 11218 /* found -> in a list that appeared to be an ordinary list */ #define TCERR_ARROW_IN_LIST 11219 /* misplaced -> in a LookupTable list after %dth element */ #define TCERR_MISPLACED_ARROW_IN_LIST 11220 /* expected end of list after default value */ #define TCERR_LOOKUP_LIST_EXPECTED_END_AT_DEF 11221 /* 'operator' not valid within a 'propertyset' */ #define TCERR_OPER_IN_PROPSET 11222 /* expected ']' in 'operator []' */ #define TCERR_EXPECTED_RBRACK_IN_OP 11223 /* invalid operator overloading */ #define TCERR_BAD_OP_OVERLOAD 11224 /* wrong number of arguments for overloaded operator */ #define TCERR_OP_OVERLOAD_WRONG_FORMALS 11225 /* can't define new global symbols at run-time */ #define TCERR_RT_CANNOT_DEFINE_GLOBALS 11226 /* 'for..in' variable %.*s is not defined as a local variable */ #define TCERR_FOR_IN_NOT_LOCAL 11227 /* * unexpected '%.*s' in << >> expression in string (mismatched 'end', * 'else', 'default', etc) */ #define TCERR_BAD_EMBED_END_TOK 11228 /* expected << at start of string template found '%.*s' */ #define TCERR_STRTPL_MISSING_LANGLE 11229 /* expected >> at end of string template, found '%.*s' */ #define TCERR_STRTPL_MISSING_RANGLE 11230 /* only one '*' is allowed in a string template */ #define TCERR_STRTPL_MULTI_STAR 11231 /* * wrong prototype for string template processor function %.*s - expected * %d parameters and a return value */ #define TCERR_STRTPL_FUNC_MISMATCH 11232 /* invalid defined() syntax */ #define TCERR_DEFINED_SYNTAX 11233 /* invalid __objref() syntax */ #define TCERR___OBJREF_SYNTAX 11234 /* invalid constant value type for operator */ #define TCERR_BAD_OP_FOR_FLOAT 11235 /* expected inline object property list starting with '{', found '%.*s' */ #define TCERR_INLINE_OBJ_REQ_LBRACE 11236 /* ------------------------------------------------------------------------ */ /* * General Code Generator Errors */ /* out of memory for code generation */ #define TCERR_CODEGEN_NO_MEM 11500 /* internal error: %d unresolved temporary label fixups */ #define TCERR_UNRES_TMP_FIXUP 11501 /* internal error: attempting to write past end of code stream */ #define TCERR_WRITEAT_PAST_END 11502 /* "self" is not valid in this context */ #define TCERR_SELF_NOT_AVAIL 11503 /* "inherited" is not valid in this context */ #define TCERR_INH_NOT_AVAIL 11504 /* no address for symbol "%.*s" */ #define TCERR_NO_ADDR_SYM 11505 /* cannot assign to symbol "%.*s" */ #define TCERR_CANNOT_ASSIGN_SYM 11506 /* cannot evaluate a code label ("%.*s") */ #define TCERR_CANNOT_EVAL_LABEL 11507 /* cannot call symbol "%.*s" */ #define TCERR_CANNOT_CALL_SYM 11508 /* cannot call property "%.*s" without explicit object (no "self") */ #define TCERR_PROP_NEEDS_OBJ 11509 /* "%.*s" has no return value to be used in an expression */ #define TCERR_VOID_RETURN_IN_EXPR 11510 /* '&' operator cannot be used with function name */ #define TCERR_INVAL_FUNC_ADDR 11511 /* cannot apply operator 'new' to this expression */ #define TCERR_INVAL_NEW_EXPR 11512 /* not enough arguments to function "%.*s" */ #define TCERR_TOO_FEW_FUNC_ARGS 11513 /* too many arguments to function "%.*s" */ #define TCERR_TOO_MANY_FUNC_ARGS 11514 /* assigning to "%.*s" needs object */ #define TCERR_SETPROP_NEEDS_OBJ 11515 /* symbol "%.*s" is not a property - illegal on right side of '.' */ #define TCERR_SYM_NOT_PROP 11516 /* invalid property expression - right side of '.' is not a property */ #define TCERR_INVAL_PROP_EXPR 11517 /* illegal inherited syntax - "%.*s" is not an object */ #define TCERR_INH_NOT_OBJ 11518 /* invalid object expression - left side of '.' is not an object */ #define TCERR_INVAL_OBJ_EXPR 11519 /* symbol "%.*s" is not an object - illegal on left of '.' */ #define TCERR_SYM_NOT_OBJ 11520 /* "if" condition is always true */ #define TCERR_IF_ALWAYS_TRUE 11521 /* "if" condition is always false */ #define TCERR_IF_ALWAYS_FALSE 11522 /* invalid "break" - not in a loop or switch */ #define TCERR_INVALID_BREAK 11523 /* invalid "continue" - not in a loop */ #define TCERR_INVALID_CONTINUE 11524 /* entrypoint function "_main" is not defined */ #define TCERR_MAIN_NOT_DEFINED 11525 /* "_main" is not a function */ #define TCERR_MAIN_NOT_FUNC 11526 /* exception class symbol "%.*s" in "catch" clause is not an object */ #define TCERR_CATCH_EXC_NOT_OBJ 11527 /* invalid 'break' - no label \"%.*s\" on any enclosing statement */ #define TCERR_INVALID_BREAK_LBL 11528 /* invalid 'continue' - no label \"%.*s\" on any enclosing statement */ #define TCERR_INVALID_CONT_LBL 11529 /* 'goto' label "%.*s" is not defined in this function */ #define TCERR_INVALID_GOTO_LBL 11530 /* unresolved external reference "%.*s" */ #define TCERR_UNRESOLVED_EXTERN 11531 /* label "%.*s" is never referenced */ #define TCERR_UNREFERENCED_LABEL 11532 /* local variable "%.*s" is never referenced */ #define TCERR_UNREFERENCED_LOCAL 11533 /* local variable "%.*s" never has a value assigned */ #define TCERR_UNASSIGNED_LOCAL 11534 /* local variable "%.*s" is assigned a value that is never used */ #define TCERR_UNUSED_LOCAL_ASSIGNMENT 11535 /* superclass "%.*s" is not an object */ #define TCERR_SC_NOT_OBJECT 11536 /* argument expression %d has no value */ #define TCERR_ARG_EXPR_HAS_NO_VAL 11537 /* expression on right of assignment operator has no value */ #define TCERR_ASI_EXPR_HAS_NO_VAL 11538 /* wrong number of arguments to function %.*s: %s required, %d actual */ #define TCERR_WRONG_ARGC_FOR_FUNC 11539 /* transfer into 'finally' block via 'goto' is not allowed */ #define TCERR_GOTO_INTO_FINALLY 11540 /* parameter "%.*s" is never referenced */ #define TCERR_UNREFERENCED_PARAM 11541 /* grammar production "%.*s" has no alternatives defined */ #define TCERR_GRAMPROD_HAS_NO_ALTS 11542 /* intrinsic class property "%.*s" cannot be modified */ #define TCERR_CANNOT_MOD_META_PROP 11543 /* Collection.createIterator() is not defined - foreach() cannot be used */ #define TCERR_FOREACH_NO_CREATEITER 11544 /* Iterator.getNext() is not defined - foreach() cannot be used */ #define TCERR_FOREACH_NO_GETNEXT 11545 /* Iterator.isNextAvailable() is not defined - foreach() cannot be used */ #define TCERR_FOREACH_NO_ISNEXTAVAIL 11546 /* invalid type for export */ #define TCERR_INVALID_TYPE_FOR_EXPORT 11547 /* duplicate export - extern name "%.*s", internal names "%.*s" and "%.*s" */ #define TCERR_DUP_EXPORT 11548 /* another duplicate export for extern name "%.*s", internal name "%.*s" */ #define TCERR_DUP_EXPORT_AGAIN 11549 /* "targetprop" is not valid in this context */ #define TCERR_TARGETPROP_NOT_AVAIL 11550 /* "targetobj" is not valid in this context */ #define TCERR_TARGETOBJ_NOT_AVAIL 11551 /* "definingobj" is not valid in this context */ #define TCERR_DEFININGOBJ_NOT_AVAIL 11552 /* cannot call constant expression */ #define TCERR_CANNOT_CALL_CONST 11553 /* "replaced" is not valid in this context */ #define TCERR_REPLACED_NOT_AVAIL 11554 /* grammar production "%.*s" has too many alternatives defined */ #define TCERR_GRAMPROD_TOO_MANY_ALTS 11555 /* cannot apply 'new' to this symbol (not a TadsObject object) */ #define TCERR_BAD_META_FOR_NEW 11556 /* inherited<> type token is not an object name */ #define TCERR_MMINH_TYPE_NOT_OBJ 11557 /* inherited<> invalid in this context */ #define TCERR_MMINH_BAD_CONTEXT 11558 /* multi-method 'inherited' missing support function %.*s */ #define TCERR_MMINH_MISSING_SUPPORT_FUNC 11559 /* inherited<> function not found */ #define TCERR_MMINH_UNDEF_FUNC 11560 /* named parameter support function %.*s missing */ #define TCERR_NAMED_PARAM_MISSING_FUNC 11561 /* optional parameter support function %.*s missing */ #define TCERR_OPT_PARAM_MISSING_FUNC 11562 /* <> requires class OneOfIndexGen to be defined */ #define TCERR_ONEOF_REQ_GENCLS 11563 /* <> requires getNextIndex to be a property name */ #define TCERR_ONEOF_REQ_GETNXT 11564 /* metaclass %.*s is not defined in this module */ #define TCERR_EXT_METACLASS 11565 /* cannot call extern function without an argument list prototype */ #define TCERR_FUNC_CALL_NO_PROTO 11566 /* %.*s is not defined or is not a metaclass */ #define TCERR_UNDEF_METACLASS 11567 /* ------------------------------------------------------------------------ */ /* * Errors related to symbol export files */ /* * invalid symbol type in symbol file - file is corrupted or was created * by an incompatible version of the compiler */ #define TCERR_SYMEXP_INV_TYPE 11700 /* invalid symbol file signature */ #define TCERR_SYMEXP_INV_SIG 11701 /* symbol name too long in symbol file */ #define TCERR_SYMEXP_SYM_TOO_LONG 11702 /* * symbol "%.*s" found in symbol file is already defined; ignoring the * redefinition */ #define TCERR_SYMEXP_REDEF 11703 /* ------------------------------------------------------------------------ */ /* * Errors related to object files */ /* invalid object file signature */ #define TCERR_OBJFILE_INV_SIG 11800 /* too many object/property ID's for this platform */ #define TCERR_OBJFILE_TOO_MANY_IDS 11801 /* out of memory loading object file */ #define TCERR_OBJFILE_OUT_OF_MEM 11802 /* invalid symbol type in object file */ #define TCERR_OBJFILE_INV_TYPE 11803 /* symbol %.*s (type %s) redefined (type %s) in object file "%s" */ #define TCERR_OBJFILE_REDEF_SYM_TYPE 11804 /* symbol %.*s (type %s) redefined in object file "%s" */ #define TCERR_OBJFILE_REDEF_SYM 11805 /* built-in function %.*s has different definition in object file "%s" */ #define TCERR_OBJFILE_BIF_INCOMPAT 11806 /* function %.*s has different definition in object file "%s" */ #define TCERR_OBJFILE_FUNC_INCOMPAT 11807 /* internal symbol reference \"%.*s\" missing in object file %s */ #define TCERR_OBJFILE_INT_SYM_MISSING 11808 /* invalid stream ID in object file %s */ #define TCERR_OBJFILE_INVAL_STREAM_ID 11809 /* invalid object ID in object file %s */ #define TCERR_OBJFILE_INVAL_OBJ_ID 11810 /* invalid property ID in object file %s */ #define TCERR_OBJFILE_INVAL_PROP_ID 11811 /* function set/metaclass entry is invalid in object file "%s" */ #define TCERR_OBJFILE_INV_FN_OR_META 11812 /* conflicting function set "%s" in object file "%s" */ #define TCERR_OBJFILE_FNSET_CONFLICT 11813 /* conflicting metaclass "%s" in object file "%s" */ #define TCERR_OBJFILE_META_CONFLICT 11814 /* modified or replaced object %.*s found before original in object file %s */ #define TCERR_OBJFILE_MODREPOBJ_BEFORE_ORIG 11815 /* replaced function %.*s found before original in object file %s */ #define TCERR_OBJFILE_REPFUNC_BEFORE_ORIG 11816 /* "construct" cannot return a value */ #define TCERR_CONSTRUCT_CANNOT_RET_VAL 11817 /* * object file doesn't contain debugging information (required because * we're linking for debugging) */ #define TCERR_OBJFILE_NO_DBG 11818 /* metaclass prop "%.*s" in does not match original "%.*s" in obj file %s */ #define TCERR_OBJFILE_METACLASS_PROP_CONFLICT 11819 /* metaclass index conflict in object file %s */ #define TCERR_OBJFILE_METACLASS_IDX_CONFLICT 11820 /* cannot modify or replace object of this type - %.*s in object file %s */ #define TCERR_OBJFILE_CANNOT_MOD_OR_REP_TYPE 11821 /* exported symbol too long in object file */ #define TCERR_EXPORT_SYM_TOO_LONG 11822 /* macro name too long in object file debug records */ #define TCERR_OBJFILE_MACRO_SYM_TOO_LONG 11823 /* conflicting definitions of %.*s as ordinary function and as multi-method */ #define TCERR_OBJFILE_MMFUNC_INCOMPAT 11824 /* multi-method parameter type %.*s in function %.*s is not an object */ #define TCERR_OBJFILE_MMPARAM_NOT_OBJECT 11825 /* multi-method registration function %s is missing or invalid */ #define TCERR_MISSING_MMREG 11826 /* ------------------------------------------------------------------------ */ /* * T3 Code Generator Errors */ /* constant string or list exceeds 32k - won't run on 16-bit machines */ #define TCERR_CONST_POOL_OVER_32K 15001 /* internal error: constant too large for page */ #define TCERR_CONST_TOO_LARGE_FOR_PG 15002 /* internal error: corrupted list */ #define TCERR_CORRUPT_LIST 15003 /* internal error: 'inherited' codegen */ #define TCERR_GEN_CODE_INH 15004 /* internal error: unknown constant type in code generation */ #define TCERR_GEN_UNK_CONST_TYPE 15005 /* internal error: attempting to generate code for non-lvalue */ #define TCERR_GEN_BAD_LVALUE 15006 /* internal error: attempting to generate address for non-addressable */ #define TCERR_GEN_BAD_ADDR 15007 /* too many arguments to function (maximum of 127 allowed) */ #define TCERR_TOO_MANY_CALL_ARGS 15008 /* too many arguments to constructor (maximum of 126 allowed) */ #define TCERR_TOO_MANY_CTOR_ARGS 15009 /* internal error: 'delegated' codegen */ #define TCERR_GEN_CODE_DELEGATED 15010 /* code block exceeds 32k - won't run on 16-bit machines */ #define TCERR_CODE_POOL_OVER_32K 15011 /* internal error: 'case' or 'default' out of place or non-const expression */ #define TCERR_GEN_BAD_CASE 15012 /* internal error: 'catch' or 'finally' gen_code called directly */ #define TCERR_CATCH_FINALLY_GEN_CODE 15013 /* internal error: property value has invalid type for code generation */ #define TCERR_INVAL_PROP_CODE_GEN 15014 /* symbol "%.*s" cannot be exported - reserved for compiler use */ #define TCERR_RESERVED_EXPORT 15015 /* expression too complex */ #define TCERR_EXPR_TOO_COMPLEX 15016 /* property 'construct' is not defined */ #define TCERR_CONSTRUCT_NOT_DEFINED 15017 /* 'construct' is not a property */ #define TCERR_CONSTRUCT_NOT_PROP 15018 /* property 'finalize' is not defined */ #define TCERR_FINALIZE_NOT_DEFINED 15019 /* 'finalize' is not a property */ #define TCERR_FINALIZE_NOT_PROP 15020 /* ------------------------------------------------------------------------ */ /* * Javascript code generator errors */ /* invalid expression template */ #define TCERR_BAD_JS_TPL 16000 /* expression template expansion overflow */ #define TCERR_JS_EXPR_EXPAN_OVF 16001 #endif /* TCERRNUM_H */ qtads-2.1.7/tads3/tcgen.cpp000066400000000000000000001002341265017072300154620ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/tcgen.cpp,v 1.4 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcgen.cpp - TADS 3 Compiler code generator support classes Function Notes Modified 05/09/99 MJRoberts - Creation */ #include #include #include "t3std.h" #include "os.h" #include "tcglob.h" #include "tcgen.h" #include "vmerr.h" #include "tcerrnum.h" #include "tctok.h" #include "tcprs.h" #include "tcmain.h" #include "vmfile.h" #include "tctarg.h" /* ------------------------------------------------------------------------ */ /* * Data/Code Stream Parser-Allocated Object */ /* * allocate via a parser memory allocator */ void *CTcCSPrsAllocObj::operator new(size_t siz, CTcPrsMem *allocator) { /* allocate via the allocator */ return allocator->alloc(siz); } /* ------------------------------------------------------------------------ */ /* * Data Stream */ /* * initialize */ CTcDataStream::CTcDataStream(char stream_id) { /* remember my ID */ stream_id_ = stream_id; /* nothing is allocated yet */ ofs_ = 0; obj_file_start_ofs_ = 0; pages_ = 0; page_slots_ = 0; page_cnt_ = 0; page_cur_ = 0; rem_ = 0; wp_ = 0; /* we have no anchors yet */ first_anchor_ = last_anchor_ = 0; /* create our parser memory allocator */ allocator_ = new CTcPrsMem(); } /* * delete */ CTcDataStream::~CTcDataStream() { size_t i; /* delete the page slots if we allocated any */ for (i = 0 ; i < page_cnt_ ; ++i) t3free(pages_[i]); /* delete the page slot array if we allocated it */ if (pages_ != 0) t3free(pages_); /* delete our label/fixup allocator */ delete allocator_; } /* * Reset */ void CTcDataStream::reset() { /* move the write pointer back to the start */ ofs_ = 0; obj_file_start_ofs_ = 0; /* back to the first page */ page_cur_ = 0; /* set up to write to the first page, if we have any pages at all */ if (pages_ != 0) { /* we have all of the first page available again */ wp_ = calc_addr(0); rem_ = TCCS_PAGE_SIZE; } /* reset the allocator */ allocator_->reset(); /* * forget all of the anchors (no need to delete them explicitly - * they were allocated from our allocator pool, which we've reset to * completely discard everything it contained) */ first_anchor_ = last_anchor_ = 0; } /* * Decrement the write offset */ void CTcDataStream::dec_ofs(int amount) { /* adjust the offset */ ofs_ -= amount; /* * calculate the new page we're on, since this may take us to a * different page */ page_cur_ = ofs_ / TCCS_PAGE_SIZE; /* calculate the remaining size in this page */ rem_ = TCCS_PAGE_SIZE - (ofs_ % TCCS_PAGE_SIZE); /* calculate the current write pointer */ wp_ = calc_addr(ofs_); } /* * Get a pointer to a block at a given offset and a given length. */ const char *CTcDataStream::get_block_ptr(ulong ofs, ulong requested_len, ulong *available_len) { size_t page_rem; /* * determine how much is left on the page containing the offset * after the given offset */ page_rem = TCCS_PAGE_SIZE - (ofs % TCCS_PAGE_SIZE); /* * if the amount remaining on the page is greater than the request * length, the available length is the entire request; otherwise, * the available length is the amount remaining on the page */ if (page_rem >= requested_len) *available_len = requested_len; else *available_len = page_rem; /* return the address at this offset */ return calc_addr(ofs); } /* * Write bytes to the stream at an earlier offset */ void CTcDataStream::write_at(ulong ofs, const char *buf, size_t len) { /* if we're writing to the current offset, use the normal writer */ if (ofs == ofs_) write(buf, len); /* * log an internal error, and skip writing anything, if the desired * range of offsets has not been previously written */ if (ofs + len > ofs_) G_tok->throw_internal_error(TCERR_WRITEAT_PAST_END); /* write the data to each page it spans */ while (len != 0) { size_t cur; /* * determine how much is left on the page containing the current * starting offset */ cur = TCCS_PAGE_SIZE - (ofs % TCCS_PAGE_SIZE); /* * figure out how much we can copy - copy the whole remaining * size, but no more than the amount remaining on this page */ if (cur > len) cur = len; /* copy the data */ memcpy(calc_addr(ofs), buf, cur); /* advance past this chunk */ len -= cur; ofs += cur; buf += cur; } } /* * Copy a chunk of the stream to the given buffer */ void CTcDataStream::copy_to_buf(char *buf, ulong start_ofs, ulong len) { /* read the data from each page that the block spans */ while (len != 0) { size_t cur; /* * determine how much is left on the page containing the current * starting offset */ cur = TCCS_PAGE_SIZE - (start_ofs % TCCS_PAGE_SIZE); /* * figure out how much we can copy - copy the whole remaining * size, but no more than the amount remaining on this page */ if (cur > len) cur = (size_t)len; /* copy the data */ memcpy(buf, calc_addr(start_ofs), cur); /* advance past this chunk */ len -= cur; start_ofs += cur; buf += cur; } } /* * Reserve space */ ulong CTcDataStream::reserve(size_t len) { ulong ret; /* we'll always return the offset current before the call */ ret = ofs_; /* if we have space on the current page, it's easy */ if (len <= rem_) { /* advance the output pointers */ ofs_ += len; wp_ += len; rem_ -= len; } else { /* keep going until we satisfy the request */ do { size_t cur; /* if necessary, allocate more memory */ if (rem_ == 0) alloc_page(); /* limit this chunk to the space remaining on the current page */ cur = len; if (cur > rem_) cur = rem_; /* skip past this chunk */ ofs_ += cur; wp_ += cur; rem_ -= cur; len -= cur; } while (len != 0); } /* return the starting offset */ return ret; } /* * Append data from another stream. The source stream is permanently * moved to the new stream, destroying the original stream. */ void CTcDataStream::append_stream(CTcDataStream *stream) { ulong rem; ulong ofs; ulong start_ofs; CTcStreamAnchor *anchor; CTcStreamAnchor *nxt; /* remember the starting offset of the copy in my stream */ start_ofs = get_ofs(); /* copy all data from the other stream */ for (ofs = 0, rem = stream->get_ofs() ; rem != 0 ; ) { ulong request; const char *ptr; ulong actual; /* * request as much as possible from the other stream, up to the * remaining length or 64k, whichever is smaller */ request = 65535; if (rem < request) request = rem; /* get the chunk from the source stream */ ptr = stream->get_block_ptr(ofs, request, &actual); /* * write this chunk (which we know is less than 64k and can thus * be safely cast to size_t, even on 16-bit machines) */ write(ptr, (size_t)actual); /* advance our counters */ rem -= actual; ofs += actual; } /* * Now copy all of the anchors from the source stream to our stream. * This will ensure that fixups in the other stream have * corresponding fixups in this stream. Note that we must adjust * the offset of each copied anchor by the offset of the start of * the copied data in our stream. */ for (anchor = stream->get_first_anchor() ; anchor != 0 ; anchor = nxt) { /* * remember the old link to the next anchor, since we're going * to move the anchor to my list and thus forget about its * position in the old list */ nxt = anchor->nxt_; /* adjust the anchor's offset */ anchor->ofs_ += start_ofs; /* unlink the anchor from its old stream */ anchor->nxt_ = 0; /* link it in to my anchor list */ if (last_anchor_ != 0) last_anchor_->nxt_ = anchor; else first_anchor_ = anchor; last_anchor_ = anchor; } } /* * Write bytes to the stream */ void CTcDataStream::write(const char *buf, size_t len) { /* * if possible, write it in one go (this is for efficiency, so that * we can avoid making a few comparisons in the most common case) */ if (len <= rem_) { /* write the data */ memcpy(wp_, buf, len); /* advance the output pointers */ ofs_ += len; wp_ += len; rem_ -= len; } else { /* keep going until we satisfy the request */ do { size_t cur; /* if necessary, allocate more memory */ if (rem_ == 0) alloc_page(); /* limit this chunk to the space remaining on the current page */ cur = len; if (cur > rem_) cur = rem_; /* copy it to the page */ memcpy(wp_, buf, cur); /* skip past the space written in the destination */ ofs_ += cur; wp_ += cur; rem_ -= cur; /* advance past the space in the source */ buf += cur; len -= cur; } while (len != 0); } } /* * allocate a new page */ void CTcDataStream::alloc_page() { /* * if we're coming back to a page that was previously allocated, we * need merely re-establish the existing page */ if (page_cur_ + 1 < page_cnt_) { /* move to the next page */ ++page_cur_; /* start writing at the start of the page */ wp_ = pages_[page_cur_]; rem_ = TCCS_PAGE_SIZE; /* we're done */ return; } /* * if we don't have room for a new page in the page array, expand * the page array */ if (page_cnt_ >= page_slots_) { /* increase the page slot count */ page_slots_ += 100; /* allocate or reallocate the page array */ if (pages_ == 0) pages_ = (char **)t3malloc(page_slots_ * sizeof(pages_[0])); else pages_ = (char **)t3realloc(pages_, page_slots_ * sizeof(pages_[0])); /* if that failed, throw an error */ if (pages_ == 0) err_throw(TCERR_CODEGEN_NO_MEM); } /* allocate the new page */ pages_[page_cnt_] = (char *)t3malloc(TCCS_PAGE_SIZE); /* throw an error if we couldn't allocate the page */ if (pages_[page_cnt_] == 0) err_throw(TCERR_CODEGEN_NO_MEM); /* start writing at the start of the new page */ wp_ = pages_[page_cnt_]; /* the entire page is free */ rem_ = TCCS_PAGE_SIZE; /* make the new page the current page */ page_cur_ = page_cnt_; /* count the new page */ ++page_cnt_; } /* * Add an absolute fixup for this stream at the current write offset. */ void CTcDataStream::add_abs_fixup(CTcAbsFixup **list_head) { /* add the fixup to the list at my current write location */ CTcAbsFixup::add_abs_fixup(list_head, this, get_ofs()); } /* * Add an anchor at the current offset. */ CTcStreamAnchor *CTcDataStream::add_anchor(CTcSymbol *owner_sym, CTcAbsFixup **fixup_list_head, ulong ofs) { CTcStreamAnchor *anchor; /* allocate the anchor, giving it our current offset */ anchor = new (allocator_) CTcStreamAnchor(owner_sym, fixup_list_head, ofs); /* append it to our list */ if (last_anchor_ != 0) last_anchor_->nxt_ = anchor; else first_anchor_ = anchor; last_anchor_ = anchor; /* return the new anchor */ return anchor; } /* * Find an anchor with the given stream offset */ CTcStreamAnchor *CTcDataStream::find_anchor(ulong ofs) const { CTcStreamAnchor *cur; /* scan the anchor list */ for (cur = first_anchor_ ; cur != 0 ; cur = cur->nxt_) { /* if this one has the desired offset, return it */ if (cur->get_ofs() == ofs) return cur; } /* didn't find it */ return 0; } /* * Write an object ID */ void CTcDataStream::write_obj_id(ulong obj_id) { /* * if there's an object ID fixup list, and this is a valid object * reference (not a 'nil' reference), add this reference */ if (G_keep_objfixups && obj_id != TCTARG_INVALID_OBJ) CTcIdFixup::add_fixup(&G_objfixup, this, get_ofs(), obj_id); /* write the ID */ write4(obj_id); } /* * Write an object ID self-reference */ void CTcDataStream::write_obj_id_selfref(CTcSymObj *obj_sym) { /* * Add a fixup list entry to the symbol. This type of reference * must be kept with the symbol rather than in the global list, * because we must apply this type of fixup each time we renumber * the symbol. */ obj_sym->add_self_ref_fixup(this, get_ofs()); /* write the ID to the stream */ write4(obj_sym->get_obj_id()); } /* * Write a property ID */ void CTcDataStream::write_prop_id(uint prop_id) { /* if there's an object ID fixup list, add this reference */ if (G_keep_propfixups) CTcIdFixup::add_fixup(&G_propfixup, this, get_ofs(), prop_id); /* write the ID */ write2(prop_id); } /* * Write an enumerator ID */ void CTcDataStream::write_enum_id(ulong enum_id) { /* if there's a fixup list, add this reference */ if (G_keep_enumfixups) CTcIdFixup::add_fixup(&G_enumfixup, this, get_ofs(), enum_id); /* write the ID */ write4(enum_id); } /* * Given a stream ID, get the stream */ CTcDataStream *CTcDataStream:: get_stream_from_id(char stream_id, const textchar_t *obj_fname) { switch(stream_id) { case TCGEN_DATA_STREAM: return G_ds; case TCGEN_CODE_STREAM: return G_cs_main; case TCGEN_STATIC_CODE_STREAM: return G_cs_static; case TCGEN_OBJECT_STREAM: return G_os; case TCGEN_ICMOD_STREAM: return G_icmod_stream; case TCGEN_BIGNUM_STREAM: return G_bignum_stream; case TCGEN_REXPAT_STREAM: return G_rexpat_stream; case TCGEN_STATIC_INIT_ID_STREAM: return G_static_init_id_stream; case TCGEN_LCL_VAR_STREAM: return G_lcl_stream; default: G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_INVAL_STREAM_ID, obj_fname); return 0; } } /* ------------------------------------------------------------------------ */ /* * Code Stream */ /* * create the code stream */ CTcCodeStream::CTcCodeStream(char stream_id) : CTcDataStream(stream_id) { /* no switch yet */ cur_switch_ = 0; /* no enclosing statement yet */ enclosing_ = 0; /* no code body being generated yet */ code_body_ = 0; /* start writing at offset zero */ ofs_ = 0; /* no symbol tables yet */ symtab_ = 0; goto_symtab_ = 0; /* no labels yet */ active_lbl_ = 0; free_lbl_ = 0; /* no fixups yet */ free_fixup_ = 0; /* allocate an initial set of line record pages */ line_pages_alloc_ = 0; line_pages_ = 0; alloc_line_pages(5); /* no line records in use yet */ line_cnt_ = 0; /* no local frame yet */ cur_frame_ = 0; } /* * destroy the code stream */ CTcCodeStream::~CTcCodeStream() { size_t i; /* release all active labels */ release_labels(); /* delete the line records pages */ for (i = 0 ; i < line_pages_alloc_ ; ++i) t3free(line_pages_[i]); /* delete the master list of pages */ t3free(line_pages_); } /* * Set the current local frame */ CTcPrsSymtab *CTcCodeStream::set_local_frame(CTcPrsSymtab *symtab) { /* remember the original local frame, so we can return it later */ CTcPrsSymtab *old_frame = cur_frame_; /* add the current byte code location to the outgoing frame */ if (old_frame != 0) old_frame->add_to_range(ofs_ - method_ofs_); /* remember the current frame */ cur_frame_ = symtab; /* add it to the local frame list for the method if necessary */ add_local_frame(symtab); /* add the current byte code location to the incoming frame */ symtab->add_to_range(ofs_ - method_ofs_); /* return the original local frame */ return old_frame; } /* * Reset */ void CTcCodeStream::reset() { /* inherit default */ CTcDataStream::reset(); /* clear the line records */ clear_line_recs(); /* * forget all of the labels and fixups - they're allocated from the * allocator pool, which we've completely reset now */ free_lbl_ = active_lbl_ = 0; free_fixup_ = 0; /* forget all of the symbol tables */ symtab_ = 0; goto_symtab_ = 0; /* forget the frame list */ frame_head_ = frame_tail_ = 0; cur_frame_ = 0; frame_cnt_ = 0; /* forget all of the statement settings */ cur_switch_ = 0; enclosing_ = 0; code_body_ = 0; /* presume 'self' is not available */ self_available_ = FALSE; } /* * Allocate line record pages */ void CTcCodeStream::alloc_line_pages(size_t number_to_add) { size_t siz; size_t i; /* create or expand the master page array */ siz = (line_pages_alloc_ + number_to_add) * sizeof(tcgen_line_page_t *); if (line_pages_ == 0) line_pages_ = (tcgen_line_page_t **)t3malloc(siz); else line_pages_ = (tcgen_line_page_t **)t3realloc(line_pages_, siz); /* allocate the new pages */ for (i = line_pages_alloc_ ; i < line_pages_alloc_ + number_to_add ; ++i) { /* allocate this page */ line_pages_[i] = (tcgen_line_page_t *) t3malloc(sizeof(tcgen_line_page_t)); } /* remember the new allocation */ line_pages_alloc_ += number_to_add; } /* * Allocate a new label object */ CTcCodeLabel *CTcCodeStream::alloc_label() { CTcCodeLabel *ret; /* if there's anything in the free list, use it */ if (free_lbl_ != 0) { /* take the first one off the free list */ ret = free_lbl_; /* unlink it from the list */ free_lbl_ = free_lbl_->nxt; } else { /* allocate a new label */ ret = new (allocator_) CTcCodeLabel; /* throw an error if allocation failed */ if (ret == 0) err_throw(TCERR_CODEGEN_NO_MEM); } /* add the label to the active list */ ret->nxt = active_lbl_; active_lbl_ = ret; /* return the allocated label */ return ret; } /* * Allocate a new fixup object */ CTcLabelFixup *CTcCodeStream::alloc_fixup() { CTcLabelFixup *ret; /* if there's anything in the free list, use it */ if (free_fixup_ != 0) { /* take the first one off the free list */ ret = free_fixup_; /* unlink it from the list */ free_fixup_ = free_fixup_->nxt; } else { /* allocate a new fixup */ ret = new (allocator_) CTcLabelFixup; /* throw an error if allocation failed */ if (ret == 0) err_throw(TCERR_CODEGEN_NO_MEM); } /* return the allocated fixup */ return ret; } /* * Release all active labels. If any labels are undefined, log an * internal error. */ void CTcCodeStream::release_labels() { int err_cnt; /* we haven't found any errors yet */ err_cnt = 0; /* run through the list of active labels */ while (active_lbl_ != 0) { CTcCodeLabel *lbl; /* pull this label off of the active list */ lbl = active_lbl_; active_lbl_ = active_lbl_->nxt; /* put this label on the free list */ lbl->nxt = free_lbl_; free_lbl_ = lbl; /* check for unresolved fixups */ while (lbl->fhead != 0) { CTcLabelFixup *fixup; /* pull this fixup off of the active list */ fixup = lbl->fhead; lbl->fhead = lbl->fhead->nxt; /* put this fixup on the free list */ fixup->nxt = free_fixup_; free_fixup_ = fixup; /* count the unresolved label */ ++err_cnt; } } /* * if we found any unresolved fixups, log the error; there's not * much point in logging each error individually, since this is an * internal compiler error that the user can't do anything about, * but at least give the user a count for compiler diagnostic * purposes */ if (err_cnt != 0) G_tcmain->log_error(0, 0, TC_SEV_INTERNAL, TCERR_UNRES_TMP_FIXUP, err_cnt); } /* * Allocate a new label at the current code offset */ CTcCodeLabel *CTcCodeStream::new_label_here() { CTcCodeLabel *lbl; /* allocate a new label */ lbl = alloc_label(); /* set the label's location to the current write position */ lbl->ofs = ofs_; lbl->is_known = TRUE; /* return the new label */ return lbl; } /* * Allocate a new forward-reference label */ CTcCodeLabel *CTcCodeStream::new_label_fwd() { CTcCodeLabel *lbl; /* allocate a new label */ lbl = alloc_label(); /* the label's location is not yet known */ lbl->ofs = 0; lbl->is_known = FALSE; /* return the new label */ return lbl; } /* * Define the position of a label, resolving any fixups associated with * the label. */ void CTcCodeStream::def_label_pos(CTcCodeLabel *lbl) { /* set the label's position */ lbl->ofs = ofs_; lbl->is_known = TRUE; /* resolve each fixup */ while (lbl->fhead != 0) { CTcLabelFixup *fixup; long diff; char buf[4]; /* pull this fixup off of the active list */ fixup = lbl->fhead; lbl->fhead = lbl->fhead->nxt; /* * calculate the offset from the fixup position to the label * position, applying the bias to the fixup position */ diff = lbl->ofs - (fixup->ofs + fixup->bias); /* convert the offset to the correct format and write it out */ if (fixup->is_long) { /* write an INT4 offset value */ oswp4(buf, diff); write_at(fixup->ofs, buf, 4); } else { /* write an INT2 offset value */ oswp2s(buf, diff); write_at(fixup->ofs, buf, 2); } /* add this fixup to the free list, since we're finished with it */ fixup->nxt = free_fixup_; free_fixup_ = fixup; } } /* * Determine if a label has a fixup at a particular offset */ int CTcCodeStream::has_fixup_at_ofs(CTcCodeLabel *lbl, ulong ofs) { CTcLabelFixup *fixup; /* scan for a match */ for (fixup = lbl->fhead ; fixup != 0 ; fixup = fixup->nxt) { /* if this is a match, indicate success */ if (fixup->ofs == ofs) return TRUE; } /* we didn't find a match */ return FALSE; } /* * Remove a label's fixup at a particular offset */ void CTcCodeStream::remove_fixup_at_ofs(CTcCodeLabel *lbl, ulong ofs) { CTcLabelFixup *fixup; CTcLabelFixup *prv; CTcLabelFixup *nxt; /* scan for a match */ for (prv = 0, fixup = lbl->fhead ; fixup != 0 ; prv = fixup, fixup = nxt) { /* remember the next one */ nxt = fixup->nxt; /* if this is a match, remove it */ if (fixup->ofs == ofs) { /* unlink this fixup from the list */ if (prv == 0) lbl->fhead = nxt; else prv->nxt = nxt; /* move it to the free list */ fixup->nxt = free_fixup_; free_fixup_ = fixup; } } } /* * Write an offset value to the given label */ void CTcCodeStream::write_ofs(CTcCodeLabel *lbl, int bias, int is_long) { /* if the label is known, write it; otherwise, generate a fixup */ if (lbl->is_known) { long diff; /* * calculate the branch offset from the current position, * applying the bias to the current position */ diff = lbl->ofs - (ofs_ + bias); /* convert the offset to the correct format and write it out */ if (is_long) write4(diff); else write2(diff); } else { CTcLabelFixup *fixup; /* allocate a fixup */ fixup = alloc_fixup(); /* set up the fixup data */ fixup->ofs = ofs_; fixup->bias = bias; fixup->is_long = is_long; /* link the fixup into the label's fixup list */ fixup->nxt = lbl->fhead; lbl->fhead = fixup; /* write a placeholder to the code stream */ if (is_long) write4(0); else write2(0); } } /* * Add a new line record at the current code offset */ void CTcCodeStream::add_line_rec(CTcTokFileDesc *file, long linenum) { /* if there's no file descriptor, there's nothing to add */ if (file == 0) return; /* compute the current offset, relative to the start of the method */ ulong cur_ofs = G_cs->get_ofs() - method_ofs_; /* presume we won't re-use the previous record */ int reuse = FALSE; /* * If we haven't added any code since the previous line record, * overwrite the previous record - it doesn't refer to any * executable code, so it's an unnecessary record. Similarly, if * the previous record is at the same source position, don't add a * separate line record for it, since the debugger won't be able to * treat the two lines separately. */ tcgen_line_t *rec; if (line_cnt_ > 0) { /* get the previous record */ rec = get_line_rec(line_cnt_ - 1); /* * if it refers to the same code offset, replace the old record * with one at this location */ if (rec->ofs == cur_ofs) reuse = TRUE; /* * if it has the identical source file location, don't bother * adding a new record at all */ if (rec->source_id == file->get_index() && rec->source_line == linenum) return; } /* if we're not re-using the previous record, allocate a new one */ if (!reuse) { /* * we need a new record - if we've used all the allocated space, * allocate more */ if (line_cnt_ >= line_pages_alloc_ * TCGEN_LINE_PAGE_SIZE) alloc_line_pages(5); /* get a pointer to the next available entry */ rec = get_line_rec(line_cnt_); /* count the new record */ ++line_cnt_; } /* store the code offset relative to the start of the current method */ rec->ofs = cur_ofs; /* store the file information */ rec->source_id = file->get_index(); rec->source_line = linenum; /* store the frame information */ rec->frame = cur_frame_; } /* * Get the nth line record */ tcgen_line_t *CTcCodeStream::get_line_rec(size_t n) { return &(line_pages_[n / TCGEN_LINE_PAGE_SIZE] ->lines[n % TCGEN_LINE_PAGE_SIZE]); } /* * Add a frame to the list of local frames in the method */ void CTcCodeStream::add_local_frame(CTcPrsSymtab *symtab) { /* * If this is the global symbol table, or it's null, or it's already * in a list, ignore it. Note that we can tell if the item is in a * list by checking its index value - a value of zero is never a * valid index and thus indicates that the item isn't in a list yet. */ if (symtab == G_prs->get_global_symtab() || symtab == 0 || symtab->get_list_index() != 0) return; /* link the frame in at the tail of our list */ symtab->set_list_next(0); if (frame_tail_ == 0) frame_head_ = symtab; else frame_tail_->set_list_next(symtab); frame_tail_ = symtab; /* count the new entry in the list */ ++frame_cnt_; /* * Set this frame's index in the list. Note that we've already * incremented the index value, so the first frame in the list will * have index 1, as is required. */ symtab->set_list_index(frame_cnt_); } /* ------------------------------------------------------------------------ */ /* * Data stream anchor */ /* * Get the length. We can deduce the length by subtracting our offset * from the next item's offset, or, if we're the last item, from the * length of the stream. */ ulong CTcStreamAnchor::get_len(CTcDataStream *ds) const { if (nxt_ != 0) { /* * there's another item after me - my length is the difference * between the next item's offset and my offset */ return (nxt_->ofs_ - ofs_); } else { /* I'm the last item - my length is whatever is left in the stream */ return (ds->get_ofs() - ofs_); } } /* * Set the finaly absoluate address, and apply fixups. The code * generator must invoke this during the link phase once this object's * final address is known. */ void CTcStreamAnchor::set_addr(ulong addr) { /* remember my address */ addr_ = addr; /* apply all outstanding fixups for this object */ CTcAbsFixup::fix_abs_fixup(*fixup_list_head_, addr); } /* ------------------------------------------------------------------------ */ /* * Absolute Fixup Object */ /* * Add an absolute fixup at the current stream location to a given fixup * list. */ void CTcAbsFixup::add_abs_fixup(CTcAbsFixup **list_head, CTcDataStream *ds, ulong ofs) { CTcAbsFixup *fixup; /* * create the fixup object - allocate it out of our fixup allocator * pool, since this fixup object has the same attributes (small and * long-lived) as other fixup objects */ fixup = (CTcAbsFixup *)G_prsmem->alloc(sizeof(CTcAbsFixup)); /* set the fixup location to the current offset */ fixup->ds = ds; fixup->ofs = ofs; /* link it in to the caller's list */ fixup->nxt = *list_head; *list_head = fixup; } /* * Fix up a fix-up list */ void CTcAbsFixup::fix_abs_fixup(CTcAbsFixup *list_head, ulong final_ofs) { CTcAbsFixup *cur; /* scan the list and fix up each entry */ for (cur = list_head ; cur != 0 ; cur = cur->nxt) { /* * fix this entry by writing the final offset in UINT4 format to * the target stream at the target offset */ cur->ds->write4_at(cur->ofs, final_ofs); } } /* ------------------------------------------------------------------------ */ /* * Object/property ID fixups */ /* * add a fixup to a list */ void CTcIdFixup::add_fixup(CTcIdFixup **list_head, class CTcDataStream *ds, ulong ofs, ulong id) { CTcIdFixup *fixup; /* create the new fixup object */ fixup = new (G_prsmem) CTcIdFixup(ds, ofs, id); /* link it in at the head of the list */ fixup->nxt_ = *list_head; *list_head = fixup; } /* * Apply this fixup */ void CTcIdFixup::apply_fixup(ulong final_id, size_t siz) { /* write the fixup */ if (siz == 2) ds_->write2_at(ofs_, (uint)final_id); else ds_->write4_at(ofs_, final_id); } qtads-2.1.7/tads3/tcgen.h000066400000000000000000001124171265017072300151350ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/tcgen.h,v 1.4 1999/07/11 00:46:58 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcgen.h - TADS 3 Compiler code generator support classes Function Notes Modified 05/08/99 MJRoberts - Creation */ #ifndef TCGEN_H #define TCGEN_H #include "t3std.h" #include "os.h" #include "tctargty.h" /* ------------------------------------------------------------------------ */ /* * Stream ID's - an ID is associated with each data stream, and is used * to identify the stream in an object file. These ID's are stored in * object files, so any changes will render old object files obsolete * and therefore require changing the object file signature's version * number. */ /* the constant pool data stream */ const char TCGEN_DATA_STREAM = 1; /* the primary code pool data stream */ const char TCGEN_CODE_STREAM = 2; /* the static object data stream */ const char TCGEN_OBJECT_STREAM = 3; /* * dictionary object data stream (not stored in an object file, because * dictionaries aren't generated until link time) */ const char TCGEN_DICT_STREAM = 4; /* * grammar production object data stream (not stored in an object file, * because grammar production objects aren't generated until link time) */ const char TCGEN_GRAMPROD_STREAM = 5; /* BigNumber data stream */ const char TCGEN_BIGNUM_STREAM = 6; /* IntrinsicClass data stream */ const char TCGEN_INTCLASS_STREAM = 7; /* intrinsic class modifier data stream */ const char TCGEN_ICMOD_STREAM = 8; /* static initializer code stream */ const char TCGEN_STATIC_CODE_STREAM = 9; /* * static initializer stream - contains a list of obj.prop identifiers * for static initialization */ const char TCGEN_STATIC_INIT_ID_STREAM = 10; /* local variable name stream - for local variable frame information */ const char TCGEN_LCL_VAR_STREAM = 11; /* RexPattern data stream */ const char TCGEN_REXPAT_STREAM = 12; /* ------------------------------------------------------------------------ */ /* * Data Stream. This object provides an in-memory byte stream. * * The data stream provides essentially a flat 32-bit address space, but * dynamically allocates memory as needed, and works on 16-bit machines. * The write pointer starts at offste zero, and is incremented by one * for each byte written. */ /* * size in bytes of each page - we'll use a size that will work in * 16-bit architectures */ const size_t TCCS_PAGE_SIZE = 65000; class CTcDataStream { public: CTcDataStream(char stream_id); virtual ~CTcDataStream(); /* * get my stream ID - this is assigned during stream creation and is * used to identify the stream in an object file */ char get_stream_id() const { return stream_id_; } /* * Given a stream ID (TCGEN_xxx_STREAM), get the stream object. * Logs an error if the stream ID is invalid. */ static CTcDataStream *get_stream_from_id(char stream_id, const textchar_t *obj_fname); /* get the current write pointer offset */ ulong get_ofs() const { return ofs_; } /* * decrement the write offset - this can be used to remove one or * more bytes from the stream (for peephole optimization, for * example) */ void dec_ofs(int amt); /* * Get a pointer to a block at a given offset and a given length. * The block might not be returned contiguously, so we return how * much data can be read at the returned pointer in * (*available_len). If (*available_len) on return is less than * requested_len, the caller must make a subsequent call, at (ofs + * *available_len) of length (requested_len - *available_len), to * read the next chunk; this process must be iterated until the * entire request has been satisfied. */ const char *get_block_ptr(ulong ofs, ulong requested_len, ulong *available_len); /* extract a chunk of the stream into the given buffer */ void copy_to_buf(char *buf, ulong start_ofs, ulong len); /* * Reserve space, advancing the write pointer without copying any * data. The space written must eventually be written with * write_at(), etc. Returns the offset of the start of the reserved * space (which will always simply be the current offset before the * call). */ ulong reserve(size_t len); /* * Append data from another stream. The old stream is destroyed by * this operation - the data are physically moved from the old * stream to the new stream. Fixup information is moved along with * the stream data. */ void append_stream(CTcDataStream *stream); /* write bytes to the stream */ void write(char b) { write(&b, 1); } void write(const char *buf, size_t len); /* write bytes at an earlier offset */ void write_at(ulong ofs, const char *buf, size_t len); void write_at(ulong ofs, char b) { write_at(ofs, &b, 1); } /* write a TADS portable UINT2 value */ void write2(uint val) { char buf[2]; /* convert the value to UINT2 format and write it out */ oswp2(buf, val); write(buf, 2); } /* write a UINT2 at a given offset */ void write2_at(ulong ofs, uint val) { char buf[2]; /* convert the value to UINT2 format and write it out */ oswp2(buf, val); write_at(ofs, buf, 2); } /* write a TADS portable UINT4 value */ void write4(ulong val) { char buf[4]; /* convert the value to UINT4 format and write it out */ oswp4(buf, val); write(buf, 4); } /* write a TADS portable UINT4 value at a given offset */ void write4_at(ulong ofs, ulong val) { char buf[4]; /* convert the value to UINT4 format and write it out */ oswp4(buf, val); write_at(ofs, buf, 4); } /* * Write an object ID at the current offset. If there's a global * object ID fixup list, we'll add this reference to the list. */ void write_obj_id(ulong obj_id); /* * Write an object ID self-reference. This must be used when a * modification of this object that assigns it a new object ID (not * due to linking, but due to explicit replacement with the 'modify' * statement) requires that this reference to the ID be changed to * match the new object ID rather than referring to the replacement * object. */ void write_obj_id_selfref(class CTcSymObj *obj_sym); /* * Write a property ID at the current offset. If there's a global * property ID fixup list, we'll add this reference to the list. */ void write_prop_id(uint prop_id); /* write an enum ID at the current offset, keeping fixups if needed */ void write_enum_id(ulong enum_id); /* get the byte at the given offset */ char get_byte_at(ulong ofs) { return *calc_addr(ofs); } /* read an INT2 value at the given offst */ int read2_at(ulong ofs) { char buf[2]; /* read the two bytes */ buf[0] = get_byte_at(ofs); buf[1] = get_byte_at(ofs + 1); return osrp2s(buf); } /* read a UINT2 value at the given offset */ uint readu2_at(ulong ofs) { char buf[2]; /* read the two bytes */ buf[0] = get_byte_at(ofs); buf[1] = get_byte_at(ofs + 1); return osrp2(buf); } /* read an INT4 value at the given offset */ int read4_at(ulong ofs) { char buf[4]; /* read the four bytes */ buf[0] = get_byte_at(ofs); buf[1] = get_byte_at(ofs + 1); buf[2] = get_byte_at(ofs + 2); buf[3] = get_byte_at(ofs + 3); return osrp4s(buf); } /* read a UINT4 value at the given offset */ uint readu4_at(ulong ofs) { char buf[4]; /* read the four bytes */ buf[0] = get_byte_at(ofs); buf[1] = get_byte_at(ofs + 1); buf[2] = get_byte_at(ofs + 2); buf[3] = get_byte_at(ofs + 3); return t3rp4u(buf); } /* * Add an absolute fixup at the current stream location to a given * absolute fixup list. We'll create a new fixup object, record our * current offset in the fixup so that we come back and fix this * location, and add the fixup object to the given list. */ void add_abs_fixup(struct CTcAbsFixup **list_head); /* get the head of my list of anchors */ struct CTcStreamAnchor *get_first_anchor() const { return first_anchor_; } /* * Add an anchor at the current offset. This should be used each * time an atomic item is written to the stream. * * If the fixup list head pointer is not null, we will store fixups * for the anchor in the given list. Otherwise, we'll provide an * internal list head in the anchor object. Objects that are * reachable through multiple references (such as an object * associated with a symbol table entry) must typically keep their * own fixup lists, because the fixup list might be needed before * and after the data stream object is created. Objects that can * only be reached through a single reference, which is created only * when the data stream object is created, can use the internal * fixup list instead. * * If an external fixup list is provided, the owning symbol table * entry must be provided. This is necessary so that, when writing * the anchor to an object file, we can also store a reference to * the symbol that owns the list; this allows the association to be * restored when the object file is loaded. */ struct CTcStreamAnchor *add_anchor(class CTcSymbol *owner_sym, struct CTcAbsFixup **fixup_list_head) { return add_anchor(owner_sym, fixup_list_head, get_ofs()); } /* add an anchor at the given offset */ struct CTcStreamAnchor *add_anchor(class CTcSymbol *owner_sym, struct CTcAbsFixup **fixup_list_head, ulong ofs); /* find an anchor at the given offset */ struct CTcStreamAnchor *find_anchor(ulong ofs) const; /* * Write the stream to an object file. Writes the stream data and * the list of anchors and their fixups. Every fixup should be * reachable from an anchor, and all of the anchors are in our * anchor list. */ void write_to_object_file(class CVmFile *fp); /* * Load an object file */ void load_object_file(class CVmFile *fp, const textchar_t *obj_fname); /* * Get/set the object file starting offset - this is the base offset * of the stream for the current object. Because streams can refer * to one another, the object file loader must set the starting * offset for all streams before reading the first stream for an * object file. The 'set' call simply sets the starting offset to * the current offset; this won't be affected by subsequent loading * into the stream, so we have a stable base address for the object * file data in the stream. */ ulong get_object_file_start_ofs() const { return obj_file_start_ofs_; } void set_object_file_start_ofs() { obj_file_start_ofs_ = ofs_; } /* * Reset - discard all data in the stream */ virtual void reset(); protected: /* allocate a new page */ void alloc_page(); /* * Calculate the memory address of a given byte, given the byte's * offset. The caller must be sure that the offset is less than the * current write position, since this routine does not allocate new * memory. */ char *calc_addr(ulong ofs) const { /* * get the page number by dividing the offset by the page size; * get the offset in the page from the remainder of the same * division */ return pages_[ofs / TCCS_PAGE_SIZE] + (ofs % TCCS_PAGE_SIZE); } /* current write offset */ ulong ofs_; /* start offset of the stream for the current object file's data */ ulong obj_file_start_ofs_; /* current write pointer */ char *wp_; /* space remaining on the current page */ size_t rem_; /* current page */ size_t page_cur_; /* array of pages */ char **pages_; /* number of page slots */ size_t page_slots_; /* number of pages allocated */ size_t page_cnt_; /* head and tail of my list of anchors */ struct CTcStreamAnchor *first_anchor_; struct CTcStreamAnchor *last_anchor_; /* * parser memory allocator - we use this for allocating label and * fixup objects; these objects fit the characteristics used for the * parser memory allocator, so we can get better memory management * efficiency by using this allocator */ class CTcPrsMem *allocator_; /* my stream ID, for identification in the object file */ char stream_id_; }; /* ------------------------------------------------------------------------ */ /* * Debugging line record. This record stores information on one * executable line of source code, associating the byte-code location of * the code with the source file ID and line number, plus the frame * identifier (which gives the local scope in effect for the line of * code). */ struct tcgen_line_t { /* * Byte-code offset of the first instruction for this source line. * This is expressed as an offset from the start of the method * header, which means that this value is relative and thus doesn't * need any adjustment for linking or any other changes to the * absolute location where the code is stored. */ uint ofs; /* * Source file ID and line number. The source file ID is the index * of the file descriptor in the tokenizer's master source file * list, so it must be adjusted when multiple object files are * linked together for the fact that the file descriptor indices can * change. */ int source_id; long source_line; /* * frame - this is the local scope frame for this line, which * specifies the local symbol table in effect for the line of code */ class CTcPrsSymtab *frame; }; /* number of line records per page */ const size_t TCGEN_LINE_PAGE_SIZE = 1024; /* * Debugging line record page. We allocate blocks of line records for * memory efficiency; this is a page of line numbers. */ struct tcgen_line_page_t { /* underlying line records */ tcgen_line_t lines[TCGEN_LINE_PAGE_SIZE]; }; /* ------------------------------------------------------------------------ */ /* * Code Stream. This object provides a place for code generators to * store byte code. A code stream is an extension of a byte stream, * with support for symbol table access; enclosing switch, loop, and * 'try' block tracking; forward-reference labels; and relative jump * generation. */ /* code stream class */ class CTcCodeStream: public CTcDataStream { public: CTcCodeStream(char stream_id); ~CTcCodeStream(); /* * Temporary label operations. A temporary label can be used to * generate forward or reverse jumps. */ /* allocate a new label at the current write offset */ struct CTcCodeLabel *new_label_here(); /* * allocate a forward-reference label; the position will be defined * later via the def_label_pos() method */ struct CTcCodeLabel *new_label_fwd(); /* * define the position of a label allocated as a forward reference * to be the current write offset */ void def_label_pos(struct CTcCodeLabel *lbl); /* * Remove a fixup at a particular location for a label. This can be * used if an instruction is being removed (for optimization, for * example). */ void remove_fixup_at_ofs(struct CTcCodeLabel *lbl, ulong ofs); /* * Check a label's fixup list to see if it has a fixup at a * particular code stream offset. Returns true if there is such a * fixup, false if not. */ int has_fixup_at_ofs(struct CTcCodeLabel *lbl, ulong ofs); /* * Release all active labels. Logs an internal error if any labels * are still forward-declared. */ void release_labels(); /* * Get the current enclosing statement. This is used to find the * target of a 'break' or 'continue', and is also used to invoke * 'finally' blocks when leaving a nested block. */ class CTPNStmEnclosing *get_enclosing() const { return enclosing_; } /* * Set the enclosing statement. During code generation, each time * an enclosing statement is encountered, it should be set as the * current enclosing statement for the duration of its code * generation, so that its subnodes can find it. This routine * returns the previous enclosing statement so that it can be * restored later. */ class CTPNStmEnclosing *set_enclosing(CTPNStmEnclosing *stm) { CTPNStmEnclosing *old_stm; /* save the old one */ old_stm = enclosing_; /* set the new one */ enclosing_ = stm; /* return the old one */ return old_stm; } /* * Set the current code body */ class CTPNCodeBody *set_code_body(CTPNCodeBody *cb) { CTPNCodeBody *old_cb; /* save the old one */ old_cb = code_body_; /* set the new one */ code_body_ = cb; /* return the old one */ return old_cb; } /* get the current code body being generated */ class CTPNCodeBody *get_code_body() const { return code_body_; } /* * Write the relative offset to the given label as a 2- or 4-byte * value in TADS portable INT2 or INT4 format. The 'bias' value * gives the offset from the current write pointer of the source * position; the relative value written is thus: * * (label - (current + bias)) */ void write_ofs2(struct CTcCodeLabel *lbl, int bias) { write_ofs(lbl, bias, FALSE); } void write_ofs4(struct CTcCodeLabel *lbl, int bias) { write_ofs(lbl, bias, TRUE); } /* * Set the enclosing "switch" statement node, returning the previous * one to allow for later restoration */ class CTPNStmSwitch *set_switch(class CTPNStmSwitch *sw) { class CTPNStmSwitch *old_sw; /* remember the current switch node for a moment */ old_sw = cur_switch_; /* set the new switch */ cur_switch_ = sw; /* return the previous one */ return old_sw; } /* get the current switch */ class CTPNStmSwitch *get_switch() const { return cur_switch_; } /* get the active symbol table */ class CTcPrsSymtab *get_symtab() const { return symtab_; } /* get the active 'goto' symbol table */ class CTcPrsSymtab *get_goto_symtab() const { return goto_symtab_; } /* set the active symbol table */ void set_symtab(class CTcPrsSymtab *symtab) { symtab_ = symtab; } /* set the active 'goto' symbol table */ void set_goto_symtab(class CTcPrsSymtab *symtab) { goto_symtab_ = symtab; } /* * get/self 'self' availability - if we're generating code for a * stand-alone function, 'self' is not available; if we're * generating code for an object method, 'self' is available */ int is_self_available() const { return self_available_; } void set_self_available(int f) { self_available_ = f; } /* add a debugging line record at the current byte-code offset */ void add_line_rec(class CTcTokFileDesc *file, long linenum); /* clear all line records */ void clear_line_recs() { line_cnt_ = 0; } /* get the number of line records */ size_t get_line_rec_count() const { return line_cnt_; } /* get the nth line record */ struct tcgen_line_t *get_line_rec(size_t n); /* set the starting offset of the current method */ void set_method_ofs(ulong ofs) { method_ofs_ = ofs; } /* clear the list of local frames in the current method */ void clear_local_frames() { /* clear the list */ frame_head_ = frame_tail_ = 0; /* reset the count */ frame_cnt_ = 0; } /* * Set the current local frame. This will add the frame to the * master list of frames for the current method if it's not already * there, and will establish the frame as the current local frame. * Returns the previous local frame, so that the caller can restore * the enclosing frame when leaving a nested frame. */ class CTcPrsSymtab *set_local_frame(class CTcPrsSymtab *symtab); /* get the local frame count for this method */ size_t get_frame_count() const { return frame_cnt_; } /* get the head of the frame list for the method */ class CTcPrsSymtab *get_first_frame() const { return frame_head_; } /* reset */ virtual void reset(); protected: /* * add a frame to the list of local frames; does nothing if the * frame is already in the list for this method */ void add_local_frame(class CTcPrsSymtab *symtab); /* allocate additional line record pages */ void alloc_line_pages(size_t number_to_add); /* allocate a label object */ struct CTcCodeLabel *alloc_label(); /* allocate a fixup object */ struct CTcLabelFixup *alloc_fixup(); /* * Write an offset to the given label. If is_long is true, we'll * write an INT4 offset; otherwise we'll write an INT2 offset value. */ void write_ofs(struct CTcCodeLabel *lbl, int bias, int is_long); /* head of list of active temporary labels */ struct CTcCodeLabel *active_lbl_; /* head of list of free temporary label objects */ struct CTcCodeLabel *free_lbl_; /* head of list of free label fixup objects */ struct CTcLabelFixup *free_fixup_; /* current symbol table */ class CTcPrsSymtab *symtab_; /* current 'goto' symbol table */ class CTcPrsSymtab *goto_symtab_; /* current "switch" statement */ class CTPNStmSwitch *cur_switch_; /* current enclosing statement */ class CTPNStmEnclosing *enclosing_; /* current code body being generated */ class CTPNCodeBody *code_body_; /* flag: 'self' is available in current code body */ unsigned int self_available_ : 1; /* array of line record pages */ tcgen_line_page_t **line_pages_; size_t line_pages_alloc_; /* number of line records actually used */ size_t line_cnt_; /* starting offst of current method */ ulong method_ofs_; /* head and tail of list of local frames for the current method */ class CTcPrsSymtab *frame_head_; class CTcPrsSymtab *frame_tail_; /* number of frames in the local method list so far */ size_t frame_cnt_; /* currently active local frame */ class CTcPrsSymtab *cur_frame_; }; /* ------------------------------------------------------------------------ */ /* * Code Stream Parser-Allocated Object - this is a base class for * objects allocated by a CTcPrsMem allocator. */ struct CTcCSPrsAllocObj { /* allocate via the parser allocator */ void *operator new(size_t siz, class CTcPrsMem *allocator); }; /* ------------------------------------------------------------------------ */ /* * Code label */ struct CTcCodeLabel: CTcCSPrsAllocObj { CTcCodeLabel() { /* clear members */ nxt = 0; ofs = 0; fhead = 0; is_known = FALSE; } /* next code label */ CTcCodeLabel *nxt; /* offset of the code associated with the label */ ulong ofs; /* * head of list of fixups for this label; this will always be null * once the label's offset is known, since we will resolve any * existing fixups as soon as the label is defined and will add no * further fixups after it's known, since we can generate correct * offsets directly */ struct CTcLabelFixup *fhead; /* flag: true -> offset is known */ uint is_known : 1; }; /* * Code label fixup. Each time we generate a jump to a code label that * hasn't been defined yet, we'll generate a fixup and attach it to the * label. The fixup records the location of the jump; as soon as the * label is defined, we'll go through all of the fixup records * associated with the label, and fill in the correct jump offset. */ struct CTcLabelFixup: CTcCSPrsAllocObj { CTcLabelFixup() { /* clear members */ nxt = 0; ofs = 0; bias = 0; is_long = FALSE; } /* next fixup in same list */ CTcLabelFixup *nxt; /* code offset of the jump in need of fixing */ ulong ofs; /* * bias to apply to jump offset (the value we will write is: * * (target - (ofs + bias)) */ int bias; /* long jump - true -> INT4 offset, false -> INT2 offset */ uint is_long : 1; }; /* ------------------------------------------------------------------------ */ /* * Absolute Fixup. Each time we generate a reference to an offset in a * stream, we must save the location so that we can go back and fix up * the offset after the final image file layout configuration. We can't * know until we've generated the entire program what the final location * of any stream is, because we won't know how large we're going to make * the pool pages until we've generated the whole program. * * Each absolute fixup is stored in a list anchored in the object that * owns the object in the stream to which the fixup refers. During the * image layout configuration phase, we go through the list of all pool * blocks, and figure out where the pool block will go in the image * file; once this is known, we apply all of the fixups for that * block by scanning the block's fixup list. */ struct CTcAbsFixup: CTcCSPrsAllocObj { /* next fixup in same list */ CTcAbsFixup *nxt; /* stream containing the reference */ class CTcDataStream *ds; /* location in stream 'ds' of the 4-byte pointer value to fix */ ulong ofs; /* * Add an absolute fixup, at a given offset in a given stream, to a * given list. */ static void add_abs_fixup(struct CTcAbsFixup **list_head, CTcDataStream *ds, ulong ofs); /* * Fix up a fix-up list, given the final address of the referenced * object. Scans the fix-up list and stores the final address at * each location in the list. */ static void fix_abs_fixup(struct CTcAbsFixup *list_head, ulong final_ofs); /* write a fixup list to an object file */ static void write_fixup_list_to_object_file(class CVmFile *fp, CTcAbsFixup *list_head); /* load a fixup list from an object file */ static void load_fixup_list_from_object_file(class CVmFile *fp, const textchar_t *obj_fname, CTcAbsFixup **list_head); }; /* ------------------------------------------------------------------------ */ /* * ID Fixup. We use this to track a reference to an object ID or * property ID. These fixups are needed when combining object files * that were compiled separately, since we must reconcile the ID name * spaces of the multiple files. */ /* translation datatypes */ enum tcgen_xlat_type { /* object ID's - translation table type is (tctarg_obj_id_t *) */ TCGEN_XLAT_OBJ, /* property ID's - translation table type is (tctarg_prop_id_t *) */ TCGEN_XLAT_PROP, /* enum's - translation table type is (ulong *) */ TCGEN_XLAT_ENUM }; /* * ID fixup class */ struct CTcIdFixup: CTcCSPrsAllocObj { /* initialize */ CTcIdFixup(class CTcDataStream *ds, ulong ofs, ulong id) { /* remember the data */ ds_ = ds; ofs_ = ofs; id_ = id; /* we're not in a list yet */ nxt_ = 0; } /* add a fixup to a fixup list */ static void add_fixup(CTcIdFixup **list_head, class CTcDataStream *ds, ulong ofs, ulong id); /* write a fixup list to an object file */ static void write_to_object_file(class CVmFile *fp, CTcIdFixup *head); /* * Load an ID fixup list from an object file. * * 'xlat' is the translation array; it consists of elements of the * appropriate type, depending on xlat_type. * * 'xlat_cnt' is the number of elements in the 'xlat' array. * * 'stream_element_size' is the size of the stream data elements * that we're fixing up. This can be 2 for a UINT2 value, or 4 for * a UINT4 value. * * 'fname' is the object filename, which we need for reporting * errors that we encounter in the fixup data. * * If 'fixup_list_head' is provided, it points to the head of a list * that we use to store new fixups. For each fixup we read, we * create a new fixup referring to the same element. This is needed * if the file we're reading will be turned back into another object * file at some point and needs relative object ID information to be * stored. */ static void load_object_file(class CVmFile *fp, const void *xlat, ulong xlat_cnt, tcgen_xlat_type xlat_type, size_t stream_element_size, const textchar_t *fname, CTcIdFixup **fixup_list_head); /* * apply the fixup, given the final ID to use, and the size of the * data item to write (2 for a UINT2, 4 for a UINT4) */ void apply_fixup(ulong final_id, size_t siz); /* the stream containing the reference to this object */ class CTcDataStream *ds_; /* the offset in the stream of the reference */ ulong ofs_; /* * the local ID - this is the ID that is used locally in the * separate file before it's translated to the final value global to * the combined files */ ulong id_; /* next fixup in the list */ CTcIdFixup *nxt_; }; /* ------------------------------------------------------------------------ */ /* * Data Stream Anchor. Each time we add an object to a data stream, we * must add an anchor to the stream. The anchor tracks the indivisible * object and all references (via a fixup list) to the object. */ struct CTcStreamAnchor: CTcCSPrsAllocObj { CTcStreamAnchor(class CTcSymbol *fixup_owner_sym, struct CTcAbsFixup **fixup_list_head, ulong stream_ofs) { /* we're not in a list yet */ nxt_ = 0; /* * if the caller provided an external fixup list head pointer, * use it; otherwise, use our internal fixup list */ if (fixup_list_head != 0) { /* use the external fixup list */ fixup_list_head_ = fixup_list_head; /* remember the owning symbol */ fixup_info_.fixup_owner_sym_ = fixup_owner_sym; } else { /* use our internal fixup list */ fixup_list_head_ = &fixup_info_.internal_fixup_head_; /* we have no fixup items in our internal list yet */ fixup_info_.internal_fixup_head_ = 0; } /* remember the stream offset */ ofs_ = stream_ofs; /* our pool address is not yet known */ addr_ = 0; /* we're not yet replaced */ replaced_ = FALSE; } /* * Detach from our symbol, switching to our internal fixup list. We * leave any fixups that were previously associated with our symbol * with the symbol, so this detaches us from any fixups currently * pointing to me. This is useful for replacing and modifying symbols, * since it allows the anchor for the original definition to be * dissociated from the symbol, so that the symbol can be reused with a * different anchor. */ void detach_from_symbol() { /* switch to our internal fixup list */ fixup_list_head_ = &fixup_info_.internal_fixup_head_; /* we have nothing in our list yet */ fixup_info_.internal_fixup_head_ = 0; } /* get my offset - this is the stream offset, not the final address */ ulong get_ofs() const { return ofs_; } /* * Get my final address - valid only after the linking phase (as * defined and performed by the target-specific code generator). * The exact meaning of this address is defined by the * target-specific code generator, and this value is meant for use * exclusively by the target code generator; the target-independent * part of the compiler should have no use for this value. */ ulong get_addr() const { return addr_; } /* * Set the address - this should be invoked by the target-specific * code generator during the link phase when the final address of * the stream object is known. This will store the address in the * anchor, and will apply all of the outstanding fixups for the * object. */ void set_addr(ulong addr); /* * Get the length. We don't separately track the length, because * the length can be deduced from the offset of the next item (or * from the length of the stream, when the item is the last item in * the stream). */ ulong get_len(class CTcDataStream *ds) const; /* * Get the symbol table entry that owns my fixup list, if the fixup * list is external. Returns null if the fixup list is internal. */ class CTcSymbol *get_fixup_owner_sym() const { return (fixup_list_head_ == &fixup_info_.internal_fixup_head_ ? 0 : fixup_info_.fixup_owner_sym_); } /* get/set the 'replaced' flag */ int is_replaced() const { return replaced_; } void set_replaced(int f) { replaced_ = f; } /* next anchor in same list */ CTcStreamAnchor *nxt_; /* * Pointer to fixup list head for the object - this is a list of the * fixups for references to this object. Because an object might * have a fixup list before the object is added to the data stream * (anything that is reachable through multiple references, such as * a function in a symbol table, could have such a fixup list), * we'll set this to refer to the external fixup list. Otherwise, * we'll set it to point to our own internal fixup list head. */ struct CTcAbsFixup **fixup_list_head_; /* * If we have an external fixup list, we need to store the symbol * table entry that owns the fixup list; otherwise, we must store * the fixup list head. Since we only need one or the other, * compress the storage into a union. */ union { /* * internal fixup list - this is used if no external fixup list * exists for the object */ struct CTcAbsFixup *internal_fixup_head_; /* owning symbol table entry, if the fixup list is external */ class CTcSymbol *fixup_owner_sym_; } fixup_info_; /* offset of this object in the stream */ ulong ofs_; /* * final absolute address - this won't be known until the layout of * the pool or data segment containing this object is calculated, * which happens during the link phase */ ulong addr_; /* * Flag: this code block has been replaced by another one. This is * set when the function or property that owns this code is replaced * by another implementation; when this is set, the code block * should not be written to the image file. */ unsigned int replaced_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Dynamic compiler interface to the live VM. The compiler uses this to * access VM resources required during compilation. */ class CTcVMIfc { public: virtual ~CTcVMIfc() { } /* * Add a generated object. This creates a live object in the VM and * returns its ID. */ virtual tctarg_obj_id_t new_obj(tctarg_obj_id_t cls) = 0; /* validate an object ID */ virtual int validate_obj(tctarg_obj_id_t obj) = 0; /* * Add a property value to a generated object. */ virtual void set_prop(tctarg_obj_id_t obj, tctarg_prop_id_t prop, const class CTcConstVal *val) = 0; /* validate a property ID */ virtual int validate_prop(tctarg_prop_id_t prop) = 0; /* validate a built-in function pointer */ virtual int validate_bif(uint set_index, uint func_index) = 0; /* validate a constant pool address for a string/list */ virtual int validate_pool_str(uint32_t pool_ofs) = 0; virtual int validate_pool_list(uint32_t pool_ofs) = 0; }; #endif /* TCGEN_H */ qtads-2.1.7/tads3/tcglob.cpp000066400000000000000000000010651265017072300156360ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/tcglob.cpp,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcglob.cpp - TADS 3 Compiler Globals Function Notes Modified 05/01/99 MJRoberts - Creation */ /* define storage for the globals */ #define TC_GLOB_DECLARE /* include the globals header */ #include "tcglob.h" qtads-2.1.7/tads3/tcglob.h000066400000000000000000000114661265017072300153110ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/tcglob.h,v 1.4 1999/07/11 00:46:58 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcglob.h - TADS 3 Compiler globals Function The TADS 3 Compiler uses a number of static variables that are shared by several subsystems. We define these variables as global variables for quick access, and to minimize the number of parameters that are passed around among subsystems. Notes Modified 05/01/99 MJRoberts - creation */ #ifndef TCGLOB_H #define TCGLOB_H /* * If we're not explicitly defining the storage for the globals, define * them as external - this lets everyone pick up external declarations * for all of the globals simply by including this file. */ #ifndef TC_GLOB_DECLARE #define TC_GLOB_DECLARE extern #endif /* host system interface */ TC_GLOB_DECLARE class CTcHostIfc *G_hostifc; /* main compiler driver */ TC_GLOB_DECLARE class CTcMain *G_tcmain; /* the parser */ TC_GLOB_DECLARE class CTcParser *G_prs; /* parse tree node list memory manager */ TC_GLOB_DECLARE class CTcPrsMem *G_prsmem; /* the tokenizer */ TC_GLOB_DECLARE class CTcTokenizer *G_tok; /* * Current code stream - this points to the currently active code stream * object. The active code stream can vary according to what kind of * code we're generating. */ TC_GLOB_DECLARE class CTcCodeStream *G_cs; /* primary generated code stream - for all normal code */ TC_GLOB_DECLARE class CTcCodeStream *G_cs_main; /* static initializer code stream */ TC_GLOB_DECLARE class CTcCodeStream *G_cs_static; /* local variable name stream */ TC_GLOB_DECLARE class CTcDataStream *G_lcl_stream; /* generated data (constant) stream */ TC_GLOB_DECLARE class CTcDataStream *G_ds; /* TADS-Object metaclass data stream */ TC_GLOB_DECLARE class CTcDataStream *G_os; /* Dictionary metaclass data stream */ TC_GLOB_DECLARE class CTcDataStream *G_dict_stream; /* GrammarProduction metaclass data stream */ TC_GLOB_DECLARE class CTcDataStream *G_gramprod_stream; /* BigNumber metaclass data stream */ TC_GLOB_DECLARE class CTcDataStream *G_bignum_stream; /* RexPattern metaclass data stream */ TC_GLOB_DECLARE class CTcDataStream *G_rexpat_stream; /* IntrinsicClass metaclass data stream */ TC_GLOB_DECLARE class CTcDataStream *G_int_class_stream; /* intrinsic class modifier metaclass data stream */ TC_GLOB_DECLARE class CTcDataStream *G_icmod_stream; /* static initializer obj.prop ID stream */ TC_GLOB_DECLARE class CTcDataStream *G_static_init_id_stream; /* target-specific code generator class */ TC_GLOB_DECLARE class CTcGenTarg *G_cg; /* * Run-time metaclass table. When we're doing dynamic compilation, the * interpreter will set this to its live metaclass table for the loaded * program. */ TC_GLOB_DECLARE class CVmMetaTable *G_metaclass_tab; /* * object ID fixup list head, and flag indicating whether to keep object * fixups */ TC_GLOB_DECLARE struct CTcIdFixup *G_objfixup; TC_GLOB_DECLARE int G_keep_objfixups; /* * property ID fixup list head, and flag indicating whether to keep * property ID fixups */ TC_GLOB_DECLARE struct CTcIdFixup *G_propfixup; TC_GLOB_DECLARE int G_keep_propfixups; /* * enumerator ID fixup list head, and flag indicating whether to keep * enumerator fixups */ TC_GLOB_DECLARE struct CTcIdFixup *G_enumfixup; TC_GLOB_DECLARE int G_keep_enumfixups; /* * Debug mode - if this is true, we're compiling for debugging, so we * must generate additional symbolic information for the debugger. */ TC_GLOB_DECLARE int G_debug; /* disassembly output stream, if disassembly display is desired */ TC_GLOB_DECLARE class CTcUnasOut *G_disasm_out; /* * Image file sizes. For the normal compiler, these are fixed quantities * based on the current VM version we're targeting. For the dynamic * compiler, these are adjusted to match the data from the loaded image * file; this is necessary to ensure that code we generate matches static * code loaded from the image. */ struct tc_image_info { /* size of the method header */ int mhdr; /* size of the exception table entry */ int exc_entry; /* debugger table header size */ int dbg_hdr; /* debugger line table entry size */ int dbg_line; /* debugger frame record size */ int dbg_frame; /* local symbol header size */ int lcl_hdr; /* debug record format version ID */ int dbg_fmt_vsn; }; TC_GLOB_DECLARE tc_image_info G_sizes; /* * Compiler interface to the live VM. In dynamic compilation mode, the VM * supplies the compiler with this object to provide the compiler with * access to VM resources. */ TC_GLOB_DECLARE class CTcVMIfc *G_vmifc; #endif /* VMGLOB_H */ qtads-2.1.7/tads3/tchost.h000066400000000000000000000053371265017072300153430ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/TCHOST.H,v 1.2 1999/05/17 02:52:27 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tchost.h - TADS 3 Compiler Host System Interface Function The host system interface is an abstract interface to services provided by the compiler host system. Notes Modified 04/22/99 MJRoberts - Creation */ #ifndef TCHOST_H #define TCHOST_H #include #include "tcerr.h" /* ------------------------------------------------------------------------ */ /* * Abstract Host Interface */ class CTcHostIfc { public: virtual ~CTcHostIfc() { } /* * Display a message, taking varargs parameters. The message is a * UTF-8 string. The message string is a printf-style format * string. This is used to display informational messages. */ void print_msg(const char *msg, ...) { va_list args; va_start(args, msg); v_print_msg(msg, args); va_end(args); } /* display a message with va_list-style varargs */ virtual void v_print_msg(const char *msg, va_list args) = 0; /* * Display a process step message. These work the same as * print_msg() and v_print_msg(), respectively, but display a * message indicating a process step. Some implementations might * want to display process step messages in a special manner; for * example, a GUI implementation might put the message in a status * bar or similar type of display rather than intermixed with other * messages. */ void print_step(const char *msg, ...) { va_list args; va_start(args, msg); v_print_step(msg, args); va_end(args); } /* display a process step message with va_list-style varargs */ virtual void v_print_step(const char *msg, va_list args) = 0; /* * Display an error message. These work the same way as print_msg() * and v_print_msg(), respectively, but display error messages * instead of informational messages. Some implementations might * want to direct the different types of messages to different * places; for example, a stdio implementation may want to send * print_msg messages to stdout, but print_err messages to stderr. */ void print_err(const char *msg, ...) { va_list args; va_start(args, msg); v_print_err(msg, args); va_end(args); } /* display an error with va_list arguments */ virtual void v_print_err(const char *msg, va_list args) = 0; }; #endif /* TCHOST_H */ qtads-2.1.7/tads3/tcmain.cpp000066400000000000000000000526471265017072300156530ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/TCMAIN.CPP,v 1.4 1999/07/11 00:46:53 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcmain.cpp - TADS 3 Compiler - main compiler driver Function Notes Modified 04/22/99 MJRoberts - Creation */ #include "t3std.h" #include "vmerr.h" #include "tcglob.h" #include "tcmain.h" #include "tcerr.h" #include "tctok.h" #include "utf8.h" #include "charmap.h" #include "tchost.h" #include "tcprs.h" #include "tcgen.h" #include "tctarg.h" #include "charmap.h" #include "resload.h" #include "tcunas.h" /* ------------------------------------------------------------------------ */ /* * statics */ /* references to the error subsystem */ int CTcMain::err_refs_ = 0; /* flag: we have failed to load external messages */ int CTcMain::err_no_extern_messages_ = FALSE; /* console output character mapper */ CCharmapToLocal *CTcMain::console_mapper_ = 0; /* ------------------------------------------------------------------------ */ /* * Initialize the error subsystem for the compiler */ void CTcMain::tc_err_init(size_t param_stack_size, CResLoader *res_loader) { /* initialize the error stack */ err_init(1024); /* if this is the first initializer, set things up */ if (err_refs_ == 0) { /* if we haven't loaded external compiler messages, load them */ if (!err_no_extern_messages_ && tc_messages == &tc_messages_english[0]) { osfildef *fp; /* try finding a message file */ fp = res_loader->open_res_file("t3make.msg", 0, "XMSG"); if (fp != 0) { /* try loading it */ err_load_message_file(fp, &tc_messages, &tc_message_count, &tc_messages_english[0], tc_message_count_english); /* done with the file */ osfcls(fp); } else { /* note the failure, so we don't try again */ err_no_extern_messages_ = FALSE; } } } /* count the reference depth */ ++err_refs_; } /* * terminate the error subsystem for the compiler */ void CTcMain::tc_err_term() { /* reduce the reference count */ --err_refs_; /* delete the error stack */ err_terminate(); /* if this is the last reference, clean things up */ if (err_refs_ == 0) { /* if we loaded an external message file, unload it */ err_delete_message_array(&tc_messages, &tc_message_count, &tc_messages_english[0], tc_message_count_english); } } /* ------------------------------------------------------------------------ */ /* * Initialize the compiler */ void CTcMain::init(CTcHostIfc *hostifc, CResLoader *res_loader, const char *default_charset) { /* initialize the error subsystem */ tc_err_init(1024, res_loader); /* remember the host interface */ G_hostifc = hostifc; /* perform static initializations on the parser symbol table class */ CTcPrsSymtab::s_init(); /* * Set the image file structure sizes for the current T3 VM spec we're * targeting. When we're doing dynamic compilation, the DynamicFunc * object will overwrite these with the actual settings for the loaded * image file before it invokes the code generator. */ G_sizes.mhdr = TCT3_METHOD_HDR_SIZE; G_sizes.exc_entry = TCT3_EXC_ENTRY_SIZE; G_sizes.dbg_hdr = TCT3_DBG_HDR_SIZE; G_sizes.dbg_line = TCT3_LINE_ENTRY_SIZE; G_sizes.lcl_hdr = TCT3_DBG_LCLSYM_HDR_SIZE; G_sizes.dbg_fmt_vsn = TCT3_DBG_FMT_VSN; G_sizes.dbg_frame = TCT3_DBG_FRAME_SIZE; /* assume we won't have a loaded image file metaclass table */ G_metaclass_tab = 0; /* we don't have a dynamic compiler interface yet */ G_vmifc = 0; /* create the compiler main object */ G_tcmain = new CTcMain(res_loader, default_charset); } /* * Terminate the compiler */ void CTcMain::terminate() { /* delete the tokenizer */ delete G_tok; G_tok = 0; /* delete the compiler main object */ delete G_tcmain; G_tcmain = 0; /* forget any object and property fixups */ G_objfixup = 0; G_propfixup = 0; G_enumfixup = 0; /* * make sure we explicitly turn the fixup flags on again if we want * them in a future run */ G_keep_objfixups = FALSE; G_keep_propfixups = FALSE; G_keep_enumfixups = FALSE; /* perform static termination on the parser symbol table class */ CTcPrsSymtab::s_terminate(); /* forget the host interface */ G_hostifc = 0; /* terminate the error subsystem */ tc_err_term(); } /* ------------------------------------------------------------------------ */ /* * set up the compiler */ CTcMain::CTcMain(CResLoader *res_loader, const char *default_charset) { char csbuf[OSFNMAX]; /* * if the caller didn't provide a default character set, ask the OS * what we should use */ if (default_charset == 0) { /* * ask the OS what to use for file contents, since we use this * character set to translate the text we read from source files */ os_get_charmap(csbuf, OS_CHARMAP_FILECONTENTS); /* use our OS-provided character set */ default_charset = csbuf; } /* if there's no static console output character map, create one */ if (console_mapper_ == 0) { char mapname[32]; /* get the console character set name */ os_get_charmap(mapname, OS_CHARMAP_DISPLAY); /* create a resource loader for the console character map */ console_mapper_ = CCharmapToLocal::load(res_loader, mapname); /* if that failed, create an ASCII mapper */ if (console_mapper_ == 0) console_mapper_ = CCharmapToLocal::load(res_loader, "us-ascii"); } /* remember our resource loader */ res_loader_ = res_loader; /* * set default options - minimum verbosity, no numeric error codes, * show standard warnings but not pedantic warnings, not test mode */ err_options_ = TCMAIN_ERR_WARNINGS; /* we have no warning suppression list yet */ suppress_list_ = 0; suppress_cnt_ = 0; /* remember our default character set */ default_charset_ = lib_copy_str(default_charset); /* create the tokenizer */ G_tok = new CTcTokenizer(res_loader_, default_charset_); /* * Create the parser and node memory pool. Create the memory pool * first, because the parser allocates objects out of the pool. */ G_prsmem = new CTcPrsMem(); G_prs = new CTcParser(); /* create the generator data stream (for constant data) */ G_ds = new CTcDataStream(TCGEN_DATA_STREAM); /* create the primary generator code stream */ G_cs_main = new CTcCodeStream(TCGEN_CODE_STREAM); /* create the static initializer code stream */ G_cs_static = new CTcCodeStream(TCGEN_STATIC_CODE_STREAM); /* make the primary code stream active */ G_cs = G_cs_main; /* create the generator object data stream */ G_os = new CTcDataStream(TCGEN_OBJECT_STREAM); /* create the intrinsic class modifier object data stream */ G_icmod_stream = new CTcDataStream(TCGEN_ICMOD_STREAM); /* create the dictionary object data stream */ G_dict_stream = new CTcDataStream(TCGEN_DICT_STREAM); /* create the grammar-production object data stream */ G_gramprod_stream = new CTcDataStream(TCGEN_GRAMPROD_STREAM); /* create the BigNumber object data stream */ G_bignum_stream = new CTcDataStream(TCGEN_BIGNUM_STREAM); /* create the RexPattern object data stream */ G_rexpat_stream = new CTcDataStream(TCGEN_REXPAT_STREAM); /* create the IntrinsicClass object data stream */ G_int_class_stream = new CTcDataStream(TCGEN_INTCLASS_STREAM); /* create the static initializer identifier stream */ G_static_init_id_stream = new CTcDataStream(TCGEN_STATIC_INIT_ID_STREAM); /* create the local variable name stream */ G_lcl_stream = new CTcDataStream(TCGEN_LCL_VAR_STREAM); /* create the target-specific code generator */ G_cg = new CTcGenTarg(); /* initialize the parser */ G_prs->init(); /* no errors or warnings yet */ error_count_ = 0; warning_count_ = 0; first_error_ = 0; first_warning_ = 0; /* set a fairly liberal maximum error limit */ max_error_count_ = 100; /* there's no disassembly output stream yet */ G_disasm_out = 0; } /* ------------------------------------------------------------------------ */ /* * delete the compiler driver */ CTcMain::~CTcMain() { /* if there's a disassembly stream, delete it */ if (G_disasm_out != 0) delete G_disasm_out; /* delete the various data streams */ delete G_cs_main; delete G_cs_static; delete G_ds; delete G_os; delete G_icmod_stream; delete G_dict_stream; delete G_gramprod_stream; delete G_bignum_stream; delete G_rexpat_stream; delete G_int_class_stream; delete G_static_init_id_stream; delete G_lcl_stream; /* delete the console output character map, if there is one */ if (console_mapper_ != 0) { /* release our reference on it */ console_mapper_->release_ref(); /* forget it (since it's static) */ console_mapper_ = 0; } /* delete the target-specific code generator */ delete G_cg; /* delete the parser and node memory pool */ delete G_prs; delete G_prsmem; /* delete the parser */ delete G_tok; /* delete our default character set name string */ lib_free_str(default_charset_); } /* ------------------------------------------------------------------------ */ /* * Log an error, part 1: show the message prefix. This should be called * before formatting the text of the message, and part 2 should be called * after formatting the message text. Returns a pointer to the message * template text. */ static const char *log_msg_internal_1( CTcTokFileDesc *linedesc, long linenum, int *err_counter, int *warn_counter, int *first_error, int *first_warning, unsigned long options, const int *suppress_list, size_t suppress_cnt, tc_severity_t severity, int err) { const char *msg; const char *prefix; /* * If this is a warning or a pedantic warning, and it's in the list of * suppressed messages, ignore it entirely. */ if (severity == TC_SEV_PEDANTIC || severity == TC_SEV_WARNING) { size_t rem; const int *p; /* scan the suppress list */ for (p = suppress_list, rem = suppress_cnt ; rem != 0 ; ++p, --rem) { /* check for a match */ if (*p == err) { /* it's in the suppress list - ignore the error */ return 0; } } } /* increment the appropriate counter */ switch(severity) { case TC_SEV_INFO: /* * we don't need to count informational messages, and no prefix is * required */ prefix = ""; break; case TC_SEV_PEDANTIC: /* * if we're not in "pedantic" mode, or we're not even showing * regular errors, ignore it */ if (!(options & TCMAIN_ERR_PEDANTIC) || !(options & TCMAIN_ERR_WARNINGS)) return 0; /* if this is the first warning, remember the code */ if (*warn_counter == 0 && first_warning != 0) *first_warning = err; /* count it */ ++(*warn_counter); /* set the prefix */ prefix = "warning"; break; case TC_SEV_WARNING: /* if we're suppressing warnings, ignore it */ if (!(options & TCMAIN_ERR_WARNINGS)) return 0; /* if this is the first warning, remember the code */ if (*warn_counter == 0 && first_warning != 0) *first_warning = err; /* count the warning */ ++(*warn_counter); /* use an appropriate prefix */ prefix = "warning"; break; case TC_SEV_ERROR: /* if this is the first error, remember the code */ if (*err_counter == 0 && first_error != 0) *first_error = err; /* count the error */ ++(*err_counter); /* use an appropriate prefix */ prefix = "error"; break; case TC_SEV_FATAL: /* if this is the first error, remember the code */ if (*err_counter == 0 && first_error != 0) *first_error = err; /* count this as an error */ ++(*err_counter); /* use an appropriate prefix */ prefix = "fatal error"; break; case TC_SEV_INTERNAL: /* if this is the first error, remember the code */ if (*err_counter == 0 && first_error != 0) *first_error = err; /* count this as an error */ ++(*err_counter); /* use an appropriate prefix */ prefix = "internal error"; break; } /* display the current parsing position, if available */ if (linedesc != 0) { const char *fname; char qu_buf[OSFNMAX*2 + 2]; /* get the filename from the source descriptor */ fname = linedesc->get_fname(); /* * if we're in test reporting mode, show only the root part of the * filename */ if ((options & TCMAIN_ERR_TESTMODE) != 0) fname = os_get_root_name((char *)fname); /* if they want quoted filenames, quote the filename */ if ((options & TCMAIN_ERR_FNAME_QU) != 0) { const char *src; char *dst; /* quote each character of the filename */ for (src = fname, qu_buf[0] = '"', dst = qu_buf + 1 ; *src != '\0' ; ) { /* if this is a quote character, stutter it */ if (*src == '"') *dst++ = '"'; /* add this character */ *dst++ = *src++; } /* add a closing quote and trailing null */ *dst++ = '"'; *dst = '\0'; /* use the quoted version of the filename */ fname = qu_buf; } /* show the filename and line number prefix */ G_hostifc->print_err("%s(%ld): ", fname, linenum); } /* display the error type prefix */ G_hostifc->print_err("%s", prefix); /* add the error number, if we're showing error numbers */ if ((options & TCMAIN_ERR_NUMBERS) != 0 && severity != TC_SEV_INFO) G_hostifc->print_err(" %u", (unsigned int)err); /* add a colon and a space for separation */ G_hostifc->print_err(": "); /* get the error message */ msg = tcerr_get_msg(err, (options & TCMAIN_ERR_VERBOSE) != 0); if (msg == 0) msg = "[Unable to find message text for this error code. " "This might indicate an internal problem with the compiler, " "or it might be caused by an installation problem that is " "preventing the compiler from finding an external message " "file that it requires.]"; /* return the message text, so the caller can format it with arguments */ return msg; } /* * Log an error, part 2: show the message suffix. */ static void log_msg_internal_2(unsigned long options, tc_severity_t severity) { /* * if we're in verbose mode, and this is an internal error, add the * internal error explanation text */ if (severity == TC_SEV_INTERNAL && (options & TCMAIN_ERR_VERBOSE) != 0) { const char *msg; /* get the internal error explanation text and display it */ msg = tcerr_get_msg(TCERR_INTERNAL_EXPLAN, TRUE); if (msg != 0) G_hostifc->print_err("\n%s", msg); } /* end the line */ G_hostifc->print_err((options & TCMAIN_ERR_VERBOSE) != 0 ? "\n\n" : "\n"); } /* ------------------------------------------------------------------------ */ /* * Print an error message. Word-wrap the message if we're in verbose mode. */ static void format_message(const char *msg, unsigned long options) { /* * if we're in verbose mode, word-wrap to 80 columns; otherwise just * print it as-is */ if ((options & TCMAIN_ERR_VERBOSE) != 0) { const char *p; const int line_wid = 79; /* start on a new line, to skip any prefix text */ G_hostifc->print_err("\n"); /* word-wrap to 80 columns */ for (p = msg ; *p != '\0' ; ) { const char *start; const char *sp; /* find the next word break */ for (sp = 0, start = p ; ; ) { /* if this is a space, note it */ if (*p == ' ') sp = p; /* * if we're at the end of the line, or we're over the line * width and we found a space, break here */ if (*p == '\0' || (p - start >= line_wid && sp != 0)) { /* if we've reached the end, print the rest */ if (*p == '\0') sp = p; /* trim off trailing spaces */ for ( ; sp > start && *(sp-1) == ' ' ; --sp) ; /* show this part */ G_hostifc->print_err("%.*s\n", sp - start, start); /* skip leading spaces */ for ( ; *sp == ' ' ; ++sp) ; /* if this is the end of the line, we're done */ if (*p == '\0') break; /* start over here */ start = p = sp; } else { /* this one fits - skip it */ ++p; } } } } else { /* display it */ G_hostifc->print_err("%s", msg); } } /* ------------------------------------------------------------------------ */ /* * log an error from an exception object */ void CTcMain::S_log_error(CTcTokFileDesc *linedesc, long linenum, int *err_counter, int *warn_counter, unsigned long options, const int *suppress_list, size_t suppress_cnt, tc_severity_t severity, CVmException *exc) { const char *msg; char msgbuf[1024]; /* show the prefix */ msg = log_msg_internal_1(linedesc, linenum, err_counter, warn_counter, 0, 0, options, suppress_list, suppress_cnt, severity, exc->get_error_code()); /* if the message is suppressed, we're done */ if (msg == 0) return; /* format the message using arguments stored in the exception */ err_format_msg(msgbuf, sizeof(msgbuf), msg, exc); /* print the error */ format_message(msgbuf, options); /* show the suffix */ log_msg_internal_2(options, severity); } /* ------------------------------------------------------------------------ */ /* * Log an error using va_list arguments */ void CTcMain::S_v_log_error(CTcTokFileDesc *linedesc, long linenum, int *err_counter, int *warn_counter, int *first_error, int *first_warning, unsigned long options, const int *suppress_list, size_t suppress_cnt, tc_severity_t severity, int err, va_list args) { const char *msg; char msgbuf[2048]; /* show the prefix */ msg = log_msg_internal_1(linedesc, linenum, err_counter, warn_counter, first_error, first_warning, options, suppress_list, suppress_cnt, severity, err); /* if the message is suppressed, we're done */ if (msg == 0) return; /* format the message using the va_list argument */ t3vsprintf(msgbuf, sizeof(msgbuf), msg, args); /* display it */ format_message(msgbuf, options); /* show the suffix */ log_msg_internal_2(options, severity); } /* ------------------------------------------------------------------------ */ /* * Check the current error count against the maximum error limit, and throw * a fatal error if we've reached the limit. */ void CTcMain::check_error_limit() { /* check the error count against the limit */ if (error_count_ > max_error_count_) { /* * raise the maximum error count a bit so that we don't encounter * another maximum error situation and loop on flagging the * too-many-errors error while trying to display a too-many-errors * error */ max_error_count_ = error_count_ + 100; /* display a message explaining the problem */ log_error(G_tok->get_last_desc(), G_tok->get_last_linenum(), TC_SEV_FATAL, TCERR_TOO_MANY_ERRORS); /* throw the generic fatal error, since we've logged this */ err_throw(TCERR_FATAL_ERROR); } } qtads-2.1.7/tads3/tcmain.h000066400000000000000000000234421265017072300153070ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/TCMAIN.H,v 1.3 1999/07/11 00:46:53 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcmain.h - TADS 3 Compiler - main compiler driver Function Notes Modified 04/22/99 MJRoberts - Creation */ #ifndef TCMAIN_H #define TCMAIN_H #include #include "t3std.h" #include "tcerr.h" /* * error display option flags */ #define TCMAIN_ERR_VERBOSE 0x00000001 /* use verbose messages */ #define TCMAIN_ERR_NUMBERS 0x00000002 /* include numeric error codes */ #define TCMAIN_ERR_WARNINGS 0x00000004 /* show warnings */ #define TCMAIN_ERR_PEDANTIC 0x00000008 /* show pendantic warnings */ #define TCMAIN_ERR_TESTMODE 0x00000010 /* testing mode */ #define TCMAIN_ERR_FNAME_QU 0x00000020 /* quote filenames */ /* * main compiler driver class */ class CTcMain { public: /* Initialize the compiler. Creates all global objects. */ static void init(class CTcHostIfc *hostifc, class CResLoader *res_loader, const char *default_charset); /* Terminate the compiler. Deletes all global objects. */ static void terminate(); /* initialize/terminate the error subsystem */ static void tc_err_init(size_t param_stack_size, class CResLoader *res_loader); static void tc_err_term(); /* log an error - varargs arguments - static routine */ static void S_log_error(class CTcTokFileDesc *linedesc, long linenum, int *err_counter, int *warn_counter, unsigned long options, const int *suppress_list, size_t suppress_cnt, tc_severity_t severity, int err, ...) { va_list args; /* pass through to our va_list-style handler */ va_start(args, err); S_v_log_error(linedesc, linenum, err_counter, warn_counter, 0, 0, options, suppress_list, suppress_cnt, severity, err, args); va_end(args); } /* log an error - arguments from a CVmException object */ static void S_log_error(class CTcTokFileDesc *linedesc, long linenum, int *err_counter, int *warn_counter, unsigned long options, const int *suppress_list, size_t suppress_cnt, tc_severity_t severity, struct CVmException *exc); /* show or don't show numeric error codes */ void set_show_err_numbers(int show) { if (show) err_options_ |= TCMAIN_ERR_NUMBERS; else err_options_ &= ~TCMAIN_ERR_NUMBERS; } /* set verbosity for error messages */ void set_verbosity(int verbose) { if (verbose) err_options_ |= TCMAIN_ERR_VERBOSE; else err_options_ &= ~TCMAIN_ERR_VERBOSE; } /* turn all warnings on or off */ void set_warnings(int show) { if (show) err_options_ |= TCMAIN_ERR_WARNINGS; else err_options_ &= ~TCMAIN_ERR_WARNINGS; } /* turn pedantic warnings on or off */ void set_pedantic(int show) { if (show) err_options_ |= TCMAIN_ERR_PEDANTIC; else err_options_ &= ~TCMAIN_ERR_PEDANTIC; } /* * Get/set regression test reporting mode. In test mode, we'll only * show the root name of each file in our status reports and error * messages. This is useful when running regression tests, because it * allows us to diff the compiler output against a reference log * without worrying about the local directory structure. */ int get_test_report_mode() const { return (err_options_ & TCMAIN_ERR_TESTMODE) != 0; } void set_test_report_mode(int flag) { if (flag) err_options_ |= TCMAIN_ERR_TESTMODE; else err_options_ &= ~TCMAIN_ERR_TESTMODE; } /* * Set the quoted filenames option. If this option is set, we'll quote * the filenames we report in our error messages; this makes it easier * for automated tools to parse the error messages, because it ensures * that the contents of a filename won't be mistaken for part of the * error message format. */ void set_quote_filenames(int flag) { if (flag) err_options_ |= TCMAIN_ERR_FNAME_QU; else err_options_ &= ~TCMAIN_ERR_FNAME_QU; } /* * Set an array of warning codes to suppress. We will suppress any * warnings and pedantic warnings whose error numbers appear in this * array; errors of greater severity will be shown even when they * appear here. * * The memory of this list is managed by the caller. We merely keep a * reference to the caller's array. The caller is responsible for * ensuring that the memory remains valid for as long as we're around. */ void set_suppress_list(const int *lst, size_t cnt) { /* remember the list and its size */ suppress_list_ = lst; suppress_cnt_ = cnt; } /* log an error - va_list-style arguments - static routine */ static void S_v_log_error(class CTcTokFileDesc *linedesc, long linenum, int *err_counter, int *warn_counter, int *first_error, int *first_warning, unsigned long options, const int *suppress_list, size_t suppress_cnt, tc_severity_t severity, int err, va_list args); /* log an error - varargs */ void log_error(class CTcTokFileDesc *linedesc, long linenum, tc_severity_t severity, int err, ...) { va_list args; /* pass through to our va_list-style handler */ va_start(args, err); v_log_error(linedesc, linenum, severity, err, args); va_end(args); } void v_log_error(class CTcTokFileDesc *linedesc, long linenum, tc_severity_t severity, int err, va_list args) { /* call our static routine */ S_v_log_error(linedesc, linenum, &error_count_, &warning_count_, &first_error_, &first_warning_, err_options_, suppress_list_, suppress_cnt_, severity, err, args); /* if we've exceeded the maximum error limit, throw a fatal error */ check_error_limit(); } /* get the error/warning count */ int get_error_count() const { return error_count_; } int get_warning_count() const { return warning_count_; } /* reset the error and warning counters */ void reset_error_counts() { /* clear the counters */ error_count_ = 0; warning_count_ = 0; /* clear the error/warning memories */ first_error_ = 0; first_warning_ = 0; } /* * get the first compilation error/warning code - we keep track of * these for times when we have limited error reporting capabilities * and can only report a single error, such as when we're compiling * an expression in the debugger */ int get_first_error() const { return first_error_; } int get_first_warning() const { return first_warning_; } /* * check the error count against the error limit, and throw a fatal * error if we've exceeded it */ void check_error_limit(); /* get the console output character mapper */ static class CCharmapToLocal *get_console_mapper() { return console_mapper_; } private: CTcMain(class CResLoader *res_loader, const char *default_charset); ~CTcMain(); /* error and warning count */ int error_count_; int warning_count_; /* first error/warning code we've encountered */ int first_error_; int first_warning_; /* * Maximum error limit - if we encounter more than this many errors * in a single compilation unit, we'll abort the compilation with a * fatal error. This at least limits the amount of garbage we'll * display if we run into a really bad cascading parsing error * situation where we just can't resynchronize. */ int max_error_count_; /* error options - this is a combination of TCMAIN_ERR_xxx flags */ unsigned long err_options_; /* * An array of warning messages to suppress. We'll only use this to * suppress warnings and pedantic warnings; errors of greater severity * will be displayed even if they appear in this list. The memory used * for this list is managed by our client; we just keep a reference to * the client's list. */ const int *suppress_list_; size_t suppress_cnt_; /* resource loader */ class CResLoader *res_loader_; /* default character set name */ char *default_charset_; /* * flag: we have tried loading an external compiler error message * file and failed; if we fail once during a session, we won't try * again, to avoid repeated searches for a message file during * compilations of multiple files */ static int err_no_extern_messages_; /* count of references to the error subsystem */ static int err_refs_; /* * The console character mapper. We use this to map error messages * to the console character set. This is a static so that we can * access it from the static error message printer routines. */ static class CCharmapToLocal *console_mapper_; }; #endif /* TCMAIN_H */ qtads-2.1.7/tads3/tcmake.h000066400000000000000000000714771265017072300153130ustar00rootroot00000000000000/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcmake.h - TADS 3 Compiler "Make" Engine Function The "Make" engine doesn't have a main program entrypoint; instead, this class is meant to make it easy to write a main entrypoint. The program main function must parse command line arguments, read a config file, get the parameters from a dialog, or use whatever other OS-specific mechanism it desires to obtain the compilation parameters. Given the parameters, this class makes it easy to compile the program. To compile a TADS 3 program, create a CTcMake object, then perform the following steps: - set the build options (source/symbol/object paths, debug mode, error options, etc) - add a module object for each source file - set the image file name - invoke build() Notes Modified 07/11/99 MJRoberts - Creation */ #ifndef TCMAKE_H #define TCMAKE_H #include "t3std.h" /* ------------------------------------------------------------------------ */ /* * String buffer object */ class CTcMakeStr { public: CTcMakeStr() { buf_ = 0; } ~CTcMakeStr() { lib_free_str(buf_); } /* get the string */ const textchar_t *get() const { return (buf_ != 0 ? buf_ : ""); } /* set the string */ void set(const textchar_t *str) { set(str, str == 0 ? 0 : strlen(str)); } void set(const textchar_t *str, size_t len) { lib_free_str(buf_); buf_ = lib_copy_str(str, len); } /* check to see if the string has been set - returns true if so */ int is_set() const { return buf_ != 0; } private: /* our string buffer */ textchar_t *buf_; }; /* ------------------------------------------------------------------------ */ /* * Module source types. This tells us how we resolved the given name of * the module to a filename. */ enum tcmod_source_t { /* "normal" - the module is found on the ordinary search path */ TCMOD_SOURCE_NORMAL = 1, /* the module comes from the system library */ TCMOD_SOURCE_SYSLIB = 2, /* the module comes from a specific user library */ TCMOD_SOURCE_LIB = 3 }; /* * Module list entry. Each module is represented by one of these * entries. * * A module defines a source file and its directly derived files: a * symbol file, and an object file. */ class CTcMakeModule { public: CTcMakeModule() { /* not in a list yet */ nxt_ = 0; /* presume we won't need recompilation */ needs_sym_recompile_ = FALSE; needs_obj_recompile_ = FALSE; /* by default, include all modules in the compilation */ exclude_ = FALSE; /* presume the module is from the ordinary search path */ source_type_ = TCMOD_SOURCE_NORMAL; /* we don't have a sequence number yet */ seqno_ = 0; } ~CTcMakeModule() { } /* get/set the next list entry */ CTcMakeModule *get_next() const { return nxt_; } void set_next(CTcMakeModule *nxt) { nxt_ = nxt; } /* get/set the sequence number */ int get_seqno() const { return seqno_; } void set_seqno(int n) { seqno_ = n; } /* * Set the module name. This will fill in the source, symbol, and * object filenames with names derived from the source name if those * names are not already defined. If any of the names are already * explicitly defined, this won't affect them. */ void set_module_name(const textchar_t *modname); /* * Get/set the original module name. The original module name is the * name as it was given on the command line, in the library source, or * wherever else it was originally specified. This is the name before * conversion to local filename conventions and before resolving to a * specific directory; this is useful in debugging records because it * can be more portable than the resolved filename. */ const textchar_t *get_orig_name() const { return orig_name_.get(); } void set_orig_name(const textchar_t *name) { orig_name_.set(name); } /* get/set the source filename */ const textchar_t *get_source_name() const { return src_.get(); } void set_source_name(const textchar_t *fname) { src_.set(fname); } /* * Get/set the "search" source filename. This is the filename we use * when we're trying to find the file in a search path. In some cases, * this might not be identical to the given filename; for example, when * we assume a relative path for a file, we won't use the assumed * relative path in the search name, since we only assumed the relative * path because we thought that would tell us where the file is. */ const textchar_t *get_search_source_name() const { /* * if there's an explicit search name, use it; otherwise, simply * use the normal source name */ if (search_src_.get() != 0 && search_src_.get()[0] != '\0') return search_src_.get(); else return src_.get(); } void set_search_source_name(const textchar_t *fname) { search_src_.set(fname); } /* get/set the symbol filename */ const textchar_t *get_symbol_name() const { return sym_.get(); } void set_symbol_name(const textchar_t *fname) { sym_.set(fname); } /* get/set the object filename */ const textchar_t *get_object_name() const { return obj_.get(); } void set_object_name(const textchar_t *fname) { obj_.set(fname); } /* get/set the symbol file recompilation flag */ int get_needs_sym_recompile() const { return needs_sym_recompile_; } void set_needs_sym_recompile(int flag) { needs_sym_recompile_ = flag; } /* get/set the object file recompilation flag */ int get_needs_obj_recompile() const { return needs_obj_recompile_; } void set_needs_obj_recompile(int flag) { needs_obj_recompile_ = flag; } /* get/set the exclusion flag */ int is_excluded() const { return exclude_; } void set_excluded(int exc) { exclude_ = exc; } /* get/set the library member URL */ const char *get_url() const { return url_.get(); } void set_url(const char *url) { url_.set(url); } /* get the module's source type */ tcmod_source_t get_source_type() const { return source_type_; } /* set the module's source to the system library */ void set_from_syslib() { source_type_ = TCMOD_SOURCE_SYSLIB; } /* * set the module's source to the given library, specified via * URL-style library path (so a module in library 'bar' that was in * turn in library 'foo' will have library path 'foo/bar') */ void set_from_lib(const textchar_t *libname, const textchar_t *url) { /* save the library's name and URL */ lib_name_.set(libname); lib_url_.set(url); /* remember that we're from a library */ source_type_ = TCMOD_SOURCE_LIB; } /* get our library URL */ const textchar_t *get_from_lib() const { return lib_name_.get(); } protected: /* next entry in the list */ CTcMakeModule *nxt_; /* source filename */ CTcMakeStr src_; /* * sequence number - this is simply an ordinal giving our position in * the list of modules making up the build */ int seqno_; /* path-searching source filename */ CTcMakeStr search_src_; /* the original name, before local file path resolution */ CTcMakeStr orig_name_; /* symbol filename */ CTcMakeStr sym_; /* object filename */ CTcMakeStr obj_; /* flag: requires recompilation of symbol/object file */ int needs_sym_recompile_; int needs_obj_recompile_; /* * flag: module is excluded from compilation (this is set when a * module is included by a library and then explicitly excluded from * the build) */ int exclude_; /* * Library member URL - this is the URL-style string naming the module * if it is a member of a library. This is not used except for * library members. This value is formed by adding the library prefix * for each enclosing sublibrary (not including the top-level library, * included from the command line or equivalent) to the "source:" * variable value that included this module from its library. A * library prefix is formed by adding the library prefix for each * enclosing sublibrary to the "library:" variable name that included * the library, plus a terminating "/". */ CTcMakeStr url_; /* the name of the enclosing library */ CTcMakeStr lib_name_; /* * library URL - this is the URL to our enclosing library, not * including the module name itself */ CTcMakeStr lib_url_; /* the source of the module */ enum tcmod_source_t source_type_; }; /* ------------------------------------------------------------------------ */ /* * Search path list entry - each entry in this list is a directory that * we'll search for a certain type of file (#include files, source files) */ class CTcMakePath { public: CTcMakePath(const textchar_t *path) { /* remember the path */ path_.set(path); /* we're not in a list yet */ nxt_ = 0; } /* get/set the path */ const textchar_t *get_path() const { return path_.get(); } void set_path(const textchar_t *path) { path_.set(path); } /* get/set the next list entry */ CTcMakePath *get_next() const { return nxt_; } void set_next(CTcMakePath *nxt) { nxt_ = nxt; } protected: /* our path string */ CTcMakeStr path_; /* next include path in the list */ CTcMakePath *nxt_; }; /* ------------------------------------------------------------------------ */ /* * Preprocessor symbol definition or undefinition item */ class CTcMakeDef { public: CTcMakeDef(const textchar_t *sym, const textchar_t *expan, int is_def) { /* remember the symbol and its expansion */ sym_.set(sym); expan_.set(expan); /* remember whether it's a definition or un-definition */ is_def_ = (is_def != 0); /* not in a list yet */ nxt_ = 0; } /* get my symbol */ const textchar_t *get_sym() const { return sym_.get(); } /* get my expansion text */ const textchar_t *get_expan() const { return expan_.get(); } /* get my define/undefine flag */ int is_def() const { return is_def_ != 0; } /* get/set the next list entry */ CTcMakeDef *get_next() const { return nxt_; } void set_next(CTcMakeDef *nxt) { nxt_ = nxt; } protected: /* next in the list */ CTcMakeDef *nxt_; /* the symbol to define or undefine */ CTcMakeStr sym_; /* the expansion text */ CTcMakeStr expan_; /* flag: true -> define the symbol, false -> undefine it */ unsigned int is_def_ : 1; }; /* ------------------------------------------------------------------------ */ /* * The program maintenance facility class. The program main entrypoint * constructs one of these objects, sets its parameters, then calls the * "build()" entrypoint to carry out the build instructions. * * The caller should not initialize or use compiler globals. This * object owns the compiler globals, and will create and destroy * compiler globals in the course of its processing. */ class CTcMake { public: CTcMake(); ~CTcMake(); /* * Set the default source file character set. Source and header * files that don't specify a character set (using a #charset * directive at the very start of the file) will be read using this * character set. If this isn't specified, we'll use the default * character set obtained from the OS. */ void set_source_charset(const textchar_t *charset) { /* delete any previous character set string */ lib_free_str(source_charset_); /* store the new character set name */ source_charset_ = lib_copy_str(charset); } /* * turn on/off source-level debugging - we'll generate the extra * information necessary for debugging the program */ void set_debug(int debug) { debug_ = debug; } /* set preprocess-only mode */ void set_pp_only(int pp_only) { pp_only_ = pp_only; } /* set list-include-files mode */ void set_list_includes(int f) { list_includes_mode_ = f; } /* set "clean" mode */ void set_clean_mode(int f) { clean_mode_ = f; } /* * set test reporting mode - in this mode, we suppress path names in * filenames in progress reports, so that the output is independent of * local path conventions */ void set_test_report_mode(int flag) { test_report_mode_ = flag; } /* * set status percentage mode - in this mode, we'll output special * status update lines with a percent-done indication, for use by * container environments such as Workbench */ void set_status_pct_mode(int flag) { status_pct_mode_ = flag; } /* set quoted filenames mode for error messages */ void set_err_quoted_fnames(int flag) { quoted_fname_mode_ = flag; } /* * turn on/off linking - by default, we'll compile and link, but the * linking phase can be turned off so we just compile sources to * object files */ void set_do_link(int do_link) { do_link_ = do_link; } /* * turn on preinit mode - by default, we'll run preinit if and only * if we're not in debug mode */ void set_preinit(int preinit) { preinit_ = preinit; explicit_preinit_ = TRUE; } /* turn sourceTextGroup property generation on or off */ void set_source_text_group_mode(int f) { src_group_mode_ = f; } /* turn verbose error messages on or off */ void set_verbose(int verbose) { verbose_ = verbose; } /* turn error number display on or off */ void set_show_err_numbers(int show) { show_err_numbers_ = show; } /* turn all warning messages on or off */ void set_warnings(int show) { show_warnings_ = show; } /* treat warnings as errors mode */ void set_warnings_as_errors(int f) { warnings_as_errors_ = f; } /* turn pedantic messages on or off */ void set_pedantic(int show) { pedantic_ = show; } /* * Set the list of warning messages to suppress. The caller is * responsible for maintaining this memory, keeping it valid as long as * we have a reference to it and deleting the memory when it's no * longer needed. We merely keep a reference to the caller's memory. */ void set_suppress_list(const int *lst, size_t cnt) { /* remember the caller's suppress list */ suppress_list_ = lst; suppress_cnt_ = cnt; } /* set the constant pool data XOR mask */ void set_data_xor_mask(uchar mask) { data_xor_mask_ = mask; } /* add a #include path entry */ void add_include_path(const textchar_t *path); /* add a #include path entry, if it's not already in our list */ void maybe_add_include_path(const textchar_t *path); /* add a source file path */ void add_source_path(const textchar_t *dir); /* * add a system include/source file path - system paths are always * searched after all of the regular paths, so effectively the items * in these lists come after all items in the add_include_path and * add_source_path lists, respectively */ class CTcMakePath *add_sys_include_path(const textchar_t *dir); class CTcMakePath *add_sys_source_path(const textchar_t *dir); /* * Set the "create directories" flag. This is false by default. If * set, we'll create the directories named in the various output * file/path options if they don't already exist. Specifically, we'll * create the directories named in set_symbol_dir(), set_object_dir(), * and set_image_file() options. */ void set_create_dirs(int flag) { create_dirs_ = flag; } /* * Set the symbol file directory. Any symbol file that doesn't have * an absolute path will default to this directory. */ void set_symbol_dir(const textchar_t *dir) { symdir_.set(dir); } /* * Set the object file directory. Any object file that doesn't have * an absolute path will default to this directory. */ void set_object_dir(const textchar_t *dir) { objdir_.set(dir); } /* set the assembly listing file */ void set_assembly_listing(osfildef *fp) { assembly_listing_fp_ = fp; } /* * Set the image file name */ void set_image_file(const textchar_t *fname) { image_fname_.set(fname); } /* get the image filename */ const char *get_image_file() const { return image_fname_.get(); } /* * Add a module (a module defines a source file and its directly * derived files: a symbol file and an object file). * * Once a module has been added to our list, we own the module. * We'll delete the module object when we're deleted. */ void add_module(CTcMakeModule *mod); void add_module_first(CTcMakeModule *mod); /* get the head and tail of the module list */ CTcMakeModule *get_first_module() const { return mod_head_; } CTcMakeModule *get_last_module() const { return mod_tail_; } /* * Add a module by filename. src_name must be specified, but * sym_name and obj_name can be null, in which case we'll use the * standard algorithm to derive these names from the source file * name. */ CTcMakeModule *add_module(const char *src_name, const char *sym_name, const char *obj_name) { return add_module(src_name, sym_name, obj_name, FALSE); } /* add a module at the head of the module list */ CTcMakeModule *add_module_first(const char *src_name, const char *sym_name, const char *obj_name) { return add_module(src_name, sym_name, obj_name, TRUE); } /* add a module at either the start or end of the module list */ CTcMakeModule *add_module(const char *src_name, const char *sym_name, const char *obj_name, int first); /* * Add a preprocessor symbol definition. If the expansion text is * null, we'll set the expansion text to "1" by default. */ void def_pp_sym(const textchar_t *sym, const textchar_t *expan); /* * Un-define a preprocessor symbol. */ void undef_pp_sym(const textchar_t *sym); /* look up a preprocessor symbol definition */ const char *look_up_pp_sym(const textchar_t *sym, size_t sym_len); /* * Build the program. This can be invoked once the options are set * and all of the modules have been added. This routine checks * dependencies and performs the build: * * - for each source file, generates a symbol file if the symbol file * is not up to date * * - after creating all symbol files: for each source file, compiles * the source to an object file if the object file isn't up to date * * - after creating all object files: links the object files to create * an image file, if the image file isn't up to date * * If any errors occur, we'll stop after the step in which the errors * occur. For example, if errors occur compiling an object file, * we'll stop after finishing with the object file and will not * proceed to any additional object file compilations. * * The 'host_interface' object must be provided by the caller. This * tells the compiler how to report error messages and otherwise * interact with the host environment. * * Fills in '*error_count' and '*warning_count' with the number of * errors and warnings, respectively, that occur during the * compilation. * * If 'force_build' is set, we'll build all derived files (symbols, * objects, and image), regardless of whether it appears necessary * based on file times. * * 'argv0' is the main program's argv[0], if available; a null pointer * can be passed in if argv[0] is not available (for example, if we're * not running in a command-line environment and the program's * executable filename is not available) */ void build(class CTcHostIfc *host_interface, int *error_count, int *warning_count, int force_build, int force_link, class CRcResList *res_list, const textchar_t *argv0); /* * Write our build configuration information to a symbol file. The * symbol file builder calls this to give us a chance to insert our * build configuration information into the symbol file. We include * all of the information we will need when re-loading the symbol file * to determine if the symbol file is up-to-date, so that we can * determine if we must rebuild the symbol file from the source or can * use it without rebuilding. */ void write_build_config_to_sym_file(class CVmFile *fp); /* * Compare our build configuration to the information saved in an * symbol file. Returns true if the configuration in the symbol file * matches our current configuration, false if not. A false return * indicates that recompilation is necessary, because something in the * configuration has changed. */ int compare_build_config_from_sym_file(const char *sym_fname, class CVmFile *fp); /* * Set the string capture file. If this is set, we'll write each * string token in the compiled text to this file, one string per * line. */ void set_string_capture(osfildef *fp) { string_fp_ = fp; } /* * Derive the source/symbol/object filenames for a module. Fills in * the buffer with the derived name. If the filenames don't have * paths explicitly specified, we'll use our default path settings to * build the full filename. * * The buffers are assumed to be OSFNMAX characters long, which should * be large enough for any valid filename. */ void get_srcfile(textchar_t *dst, CTcMakeModule *mod); void get_symfile(textchar_t *dst, CTcMakeModule *mod); void get_objfile(textchar_t *dst, CTcMakeModule *mod); /* * Create a directory if it doesn't already exist. If 'is_file' is * true, the path includes both a directory path and a filename, in * which case we'll create the directory containing the file as * specified in the path. If 'is_file' is false, the path specifies a * directory name directly, with no filename attached. If an error * occurs, we'll generate a message and count it in the error count. */ void create_dir(class CTcHostIfc *hostifc, const char *path, int is_file, int *errcnt); private: /* scan all modules for name collisions with other modules */ void check_all_module_collisions(class CTcHostIfc *hostifc, class CResLoader *res_loader, int *err_cnt, int *warn_cnt); /* check the given module for name collisions with other modules */ void check_module_collision(CTcMakeModule *mod); /* * read and compare a configuration string; returns true if the * string matches what's stored in the file, false if not */ int read_and_compare_config_str(CVmFile *fp, const textchar_t *str); /* preprocess a source file */ void preprocess_source(class CTcHostIfc *hostifc, class CResLoader *res_loader, const textchar_t *src_fname, CTcMakeModule *src_mod, int *error_count, int *warning_count); /* build a symbol file */ void build_symbol_file(class CTcHostIfc *hostifc, class CResLoader *res_loader, const textchar_t *src_fname, const textchar_t *sym_fname, CTcMakeModule *src_mod, int *error_count, int *warning_count); /* build an object file */ void build_object_file(class CTcHostIfc *hostifc, class CResLoader *res_loader, const textchar_t *src_fname, const textchar_t *obj_fname, CTcMakeModule *src_mod, int *error_count, int *warning_count); /* build the image file */ void build_image_file(class CTcHostIfc *hostifc, class CResLoader *res_loader, const textchar_t *image_fname, int *error_count, int *warning_count, class CVmRuntimeSymbols *runtime_symtab, class CVmRuntimeSymbols *runtime_macros, const char tool_data[4]); /* symbol enumeration callback: build runtime symbol table */ static void build_runtime_symtab_cb(void *ctx, class CTcSymbol *sym); /* symbol enumeration callback: build runtime macro table */ static void build_runtime_macro_cb(void *ctx, class CVmHashEntry *e); /* set compiler options in the G_tcmain object */ void set_compiler_options(); /* add a preprocessor symbol definition or undefinition */ void add_pp_def(const textchar_t *sym, const textchar_t *expan, int is_def); /* * Get the string to report for a filename in a progress report. If * we're in test reporting mode, we'll return the root name so that we * suppress all paths in progress reports; otherwise, we'll just return * the name as given. If we're quoting filenames in messages, we'll * quote the filename. */ const char *get_step_fname(char *buf, const char *fname); /* default source file character set */ char *source_charset_; /* flag: create output directories if they don't exist */ int create_dirs_; /* symbol file directory */ CTcMakeStr symdir_; /* object file diretory */ CTcMakeStr objdir_; /* assembly listing file */ osfildef *assembly_listing_fp_; /* image file name */ CTcMakeStr image_fname_; /* head and tail of module list */ CTcMakeModule *mod_head_; CTcMakeModule *mod_tail_; /* head and tail of source path list */ CTcMakePath *src_head_; CTcMakePath *src_tail_; /* * tail of regular source path list - this is where we insert regular * source path entries, which come before all system path entries */ CTcMakePath *nonsys_src_tail_; /* head and tail of #include path list */ CTcMakePath *inc_head_; CTcMakePath *inc_tail_; /* tail of regular (non-system) include path list */ CTcMakePath *nonsys_inc_tail_; /* head and tail of preprocessor symbol list */ CTcMakeDef *def_head_; CTcMakeDef *def_tail_; /* string capture file */ osfildef *string_fp_; /* true -> generate sourceTextGroup properties */ int src_group_mode_; /* true -> show verbose error messages */ int verbose_; /* true -> show error numbers with error messages */ int show_err_numbers_; /* true -> show warnings, false -> suppress all warnings */ int show_warnings_; /* true -> treat warnings as errors */ int warnings_as_errors_; /* true -> show "pedantic" warning messages */ int pedantic_; /* list of warning numbers to suppress */ const int *suppress_list_; size_t suppress_cnt_; /* true -> debug mode */ int debug_; /* true -> preprocess only */ int pp_only_; /* true -> #include list mode */ int list_includes_mode_; /* true -> "clean" mode */ int clean_mode_; /* true -> do linking after compiling */ int do_link_; /* true -> preinit mode */ int preinit_; /* * true -> obey 'preinit_' setting; otherwise, use default based on * 'debug_' setting */ int explicit_preinit_; /* * data pool XOR mask - we'll mask each byte of each constant data * pool with this byte when writing the image file, to obscure any * text strings in the constant data */ uchar data_xor_mask_; /* * true -> test reporting mode: suppress all paths from filenames * displayed in progress reports, in order to make the output * independent of local path name conventions */ int test_report_mode_; /* true -> percent-done reporting mode */ int status_pct_mode_; /* true -> use quoted filenames in error messages */ int quoted_fname_mode_; }; #endif /* TCMAKE_H */ qtads-2.1.7/tads3/tcpnbase.h000066400000000000000000000561021265017072300156320ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/TCPNBASE.H,v 1.3 1999/07/11 00:46:53 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcpn.h - Parse Node - base class Function Defines the target-independent base class for parse nodes Notes All expression parse nodes are derived from the target-specific subclass of this class. The target-independent base class is CTcPrsNodeBase; the target-specific class is CTcPrsNode. Modified 05/10/99 MJRoberts - Creation */ #ifndef TCPN_H #define TCPN_H #include "vmhash.h" /* ------------------------------------------------------------------------ */ /* * Parse Tree Allocation Object. This is a base class that can be used * for tree objects that are to be allocated from the parser node pool. */ class CTcPrsAllocObj { public: /* * Override operator new() - allocate all parse node objects out of * the parse node pool. */ void *operator new(size_t siz); }; /* ------------------------------------------------------------------------ */ /* * Parse Tree Expression Node - base class. As we parse an expression, * we build a tree of these objects to describe the source code. * * This class is subclassed for each type of parsing node: each type of * statement has a node type, some statements have helper node types for * parts of statements, and each expression operator has a node type. * These subclasses contain the information specific to the type of * parsing construct represented. * * Each parsing subclass is then further subclassed for each target * architecture. This final subclass contains the code generator for * the node in the target architecture. * * The target-independent base version of each subclass is called * CTPNXxxBase. The target-specific subclass derived from this base * class is CTPNXxx. For example, the final subclass for constant * nodes, which is derived from the target-independent base class * CTPNConstBase, is CTPNConst. (Note that each target uses the same * name for the final subclass, so we can only link one target * architecture into a given build of the compiler. Each additional * target requires a separate compiler executable with the appropriate * CTPNConst classes linked in.) */ class CTcPrsNodeBase: public CTcPrsAllocObj { public: /* * Generate code for the expression for the target architecture. * This method is defined only by the final target-specific * subclasses. * * This method is used to generate code to evaluate the expression * as an rvalue. * * If 'discard' is true, it indicates that any value yielded by the * expression will not be used, in which case the generated code * need not leave the result of the expression on the stack. We can * generate code more efficiently for certain types of expressions * when we know that we're evaluating them only for side effects. * For example, an assignment expression has a result value, but * this value need not be pushed onto the stack if it will simply be * discarded. Also, an operator like "+" that has no side effects * of its own can merely evaluate its operands for their side * effects, but need not compute its own result if that result would * simply be discarded. * * If 'for_condition' is true, it indicates that the result of the * expression will be used directly for a conditional of some kind * (for a "?:" operator, an "if" statement, a "while" statement, or * the like). In some cases, we can avoid extra conversions to some * values when they're going to be used directly for a comparison; * for example, the "&&" operator must return a true/nil value, but * the code generator may be able to avoid the extra conversion when * the value will be used for an "if" statement's conditional value. */ virtual void gen_code(int discard, int for_condition) = 0; /* * Get the constant value of the parse node, if available. Most * parse nodes have no constant value, so by default this returns * null. Only constant parse nodes can provide a constant value, so * they should override this. */ virtual class CTcConstVal *get_const_val() { return 0; } /* determine if the node has a constant value */ int is_const() { return get_const_val() != 0; } /* determine if I have a given constant integer value */ int is_const_int(int val) { return (is_const() && get_const_val()->get_type() == TC_CVT_INT && get_const_val()->get_val_int() == val); } /* * Set the constant value of the parse node from that of another * node. The caller must already have checked that this node and * the value being assigned are both valid constant values. */ void set_const_val(class CTcPrsNode *src) { /* set my constant value from the source's constant value */ get_const_val()->set(((CTcPrsNodeBase *)src)->get_const_val()); } /* * Is this value guaranteed to be a boolean value? Certain operators * produce boolean results regardless of the operands. In particular, * the comparison operators (==, !=, >, <, >=, <=) and the logical NOT * operator (!) always produce boolean results. */ virtual int is_bool() const { return FALSE; } /* * Check to see if this expression can possibly be a valid lvalue. * Return true if so, false if not. This check is made before * symbol resolution; when it is not certain whether or not a symbol * expression can be an lvalue, assume it can be at this point. By * default, we'll return false; operator nodes whose result can be * used as an lvalue should override this to return true. */ virtual int check_lvalue() const { return FALSE; } /* * Check to see if this expression is an valid lvalue, after * resolving symbols in the given scope. Returns true if so, false * if not. */ virtual int check_lvalue_resolved(class CTcPrsSymtab *symtab) const { return FALSE; } /* * Check to see if this expression can possibly be a valid address * value, so that the address-of ("&") operator can be applied. * Returns true if it is possible, false if not. The only type of * expression whose address can be taken is a simple symbol. The * address of a symbol can be taken only if the symbol is a function * or property name, but we won't know this at parse time, so we'll * indicate that any symbol is acceptable. By default, this returns * false, since the address of most expressions cannot be taken. */ virtual int has_addr() const { return FALSE; } /* * Check to see if this expression is an address expression of some * kind (i.e., of class CTPNAddrBase, or of a class derived from * CTPNAddrBase). Returns true if so, false if not. */ virtual int is_addr() const { return FALSE; } /* * Determine if this node is of type double-quoted string (dstring). * Returns true if so, false if not. By default, we return false. */ virtual int is_dstring() const { return FALSE; } /* * Determine if this node contains a dstring expression. This returns * true if the node is a dstring, a dstring embedding, or a comma node * containing a dstring and/or a dstring embedding. */ virtual int is_dstring_expr() const { return FALSE; } /* * Determine if this is a simple assignment operator node. Returns * true if so, false if not. By default, we return false. */ virtual int is_simple_asi() const { return FALSE; } /* * Determine if this node yields a value when evaluated. Returns * true if so, false if not. When it cannot be determined at * compile-time whether or not the node has a value (for example, * for a call to a pointer to a function whose return type is not * declared), this should indicate that a value is returned. * * Most nodes yield a value when executed, so we'll return true by * default. */ virtual int has_return_value() const { return TRUE; } /* * Determine if this node yields a return value when called as a * function. We assume by default that it does. */ virtual int has_return_value_on_call() const { return TRUE; } /* * Get the text of the symbol for this node, if any. If the node is * not some kind of symbol node, this returns null. */ virtual const textchar_t *get_sym_text() const { return 0; } virtual size_t get_sym_text_len() const { return 0; } /* check for a match to the given symbol text */ virtual int sym_text_matches(const char *str, size_t len) const { return FALSE; } /* * Fold constant expressions, given a finished symbol table. We do * most of our constant folding during the initial parsing, but some * constant folding must wait until the symbol table is finished; in * particular, we can't figure out what to do with symbols until we * know what the symbols mean. * * For most nodes, this function should merely recurse into subnodes * and fold constants. Nodes that are affected by symbol * resolution, directly or indirectly, should override this. * * For example, a list can change from unknown to constant during * this operation. If the list contains a symbol, the list will * initially be set to unknown, since the symbol could turn out to * be a property evaluation, which would be non-constant, or an * object name, which would be constant. * * Returns the folded version of the node, or simply 'this' if no * folding takes place. */ virtual class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) = 0; /* * generate a constant value node for the address of this node; * returns null if the symbol has no address */ virtual class CTcPrsNode *fold_addr_const(class CTcPrsSymtab *) { /* by default, we have no address */ return 0; } /* * Adjust the expression for use as a dynamic (run-time) compilation * expression expression. Dynamic expressions include debugger * expression evaluations and the run-time "eval()" facility. * * Code generation for dynamic expressions is somewhat different than * for normal expressions. This routine should allocate and return a * new node, if necessary, for dynamic use. If no changes are * necessary, simply return 'this'. * * If 'speculative' is true, the expression is being evaluated * speculatively by the debugger. This means that the user hasn't * explicitly asked for the expression to be evaluated, but rather the * debugger is making a guess that the expression might be of interest * to the user and is making an unsolicited attempt to offer it to the * user. Because the debugger is only guessing that the expression is * interesting, the expression must not be evaluated if it has any side * effects at all. */ virtual class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info); }; /* ------------------------------------------------------------------------ */ /* * Symbol Table Entry. Each symbol has an entry in one of the symbol * tables: * * - The global symbol table contains object, property, and built-in * functions from the default function set. * * - Local symbol tables contain local variables and parameters. Local * tables have block-level scope. * * - Label symbol tables contain code labels (for "goto" statements). * Label tables have function-level or method-level scope. */ /* * Basic symbol table entry. The target */ class CTcSymbolBase: public CVmHashEntryCS { public: CTcSymbolBase(const char *str, size_t len, int copy, tc_symtype_t typ) : CVmHashEntryCS(str, len, copy) { typ_ = typ; } /* allocate symbol entries from the parser memory pool */ void *operator new(size_t siz); /* get the symbol type */ tc_symtype_t get_type() const { return typ_; } /* get the symbol text and length */ const char *get_sym() const { return getstr(); } size_t get_sym_len() const { return getlen(); } /* * Generate a constant value node for this symbol, if possible; * returns null if the symbol does not evaluate to a compile-time * constant value. An object name, for example, evaluates to a * compile-time constant equal to the object reference; a property * name, in contrast, is (when not qualified by another operator) an * invocation of the property, hence must be executed at run time, * hence is not a compile-time constant. */ virtual class CTcPrsNode *fold_constant() { /* by default, a symbol's value is not a constant */ return 0; } /* * generate a constant value node for the address of this symbol; * returns null if the symbol has no address */ virtual class CTcPrsNode *fold_addr_const() { /* by default, a symbol has no address */ return 0; } /* determine if this symbol can be used as an lvalue */ virtual int check_lvalue() const { return FALSE; } /* determine if this symbol can have its address taken */ virtual int has_addr() const { return FALSE; } /* determine if I have a return value when evaluated */ virtual int has_return_value_on_call() const { return TRUE; } /* * Write the symbol to a symbol export file. By default, we'll * write the type and symbol name to the file. Some subclasses * might wish to override this to write additional data, or to write * something different or nothing at all (for example, built-in * function symbols are not written to a symbol export file). * * When a subclass does override this, it must write the type as a * UINT2 value as the first thing written to the file. The generic * file reader switches on this type code to determine what to call * to load the entry, then calls the subclass-specific loader to do * the actual work. * * Returns true if we wrote the symbol to the file, false if not. * (False doesn't indicate an error - it indicates that we chose not * to store the symbol because the symbol is not of a type that we * want to put in the export file.) */ virtual int write_to_sym_file(class CVmFile *fp); /* write the symbol name (with a UINT2 length prefix) to a file */ int write_name_to_file(class CVmFile *fp); /* * Write the symbol to an object file. By default, we'll write the * type and symbol name to the file. Some subclasses might wish to * override this to write additional data, or to write something * different or nothing at all (for example, built-in function * symbols are not written to an object file). * * When a subclass does override this, it must write the type as a * UINT2 value as the first thing written to the file. The generic * file reader switches on this type code to determine what to call * to load the entry, then calls the subclass-specific loader to do * the actual work. * * Returns true if we wrote the symbol to the file, false if not. * (False doesn't indicate an error - it indicates that we chose not * to store the symbol because the symbol is not of a type that we * want to put in the export file.) */ virtual int write_to_obj_file(class CVmFile *fp); /* * Write the symbol's cross references to the object file. This can * write references to other symbols by storing the other symbol's * index in the object file. Most symbols don't have any cross * references, so this does nothing by default. * * If this writes anything, the first thing written must be a UINT4 * giving the object file index of this symbol. On loading, we'll * read this and look up the loaded symbol. */ virtual int write_refs_to_obj_file(class CVmFile *) { return FALSE; } /* * perform basic writing to a file - this performs common work that * can be used for object or symbol files */ int write_to_file_gen(CVmFile *fp); /* * Read a symbol from a symbol file, returning the new symbol */ static class CTcSymbol *read_from_sym_file(class CVmFile *fp); /* * Load a symbol from an object file. Stores the symbol in the * global symbol table, and fills in the appropriate translation * mapping table when necessary. Returns zero on success; logs * error messages and return non-zero on failure. */ static int load_from_obj_file(class CVmFile *fp, const textchar_t *fname, tctarg_obj_id_t *obj_xlat, tctarg_prop_id_t *prop_xlat, ulong *enum_xlat); /* * Load references from the object file - reads the information that * write_refs_to_obj_file() wrote, except that the caller will have * read the first UINT4 giving the symbol's object file index before * calling this routine. */ virtual void load_refs_from_obj_file(class CVmFile *, const textchar_t * /*obj_fname*/, tctarg_obj_id_t * /*obj_xlat*/, tctarg_prop_id_t * /*prop_xlat*/) { /* by default, do nothing */ } /* * Log an object file loading conflict with this symbol. The given * type is the new type found in the object file of the given name. */ void log_objfile_conflict(const textchar_t *fname, tc_symtype_t new_type) const; /* * Get a pointer to the head of the fixup list for this symbol. * Symbols such as functions that keep a list of fixups for * references to the symbol must override this to provide a fixup * list head; by default, symbols keep no fixup list, so we'll just * return null. */ virtual struct CTcAbsFixup **get_fixup_list_anchor() { return 0; } /* * Set my code stream anchor object. By default, symbols don't keep * track of any stream anchors. Symbols that refer to code or data * stream locations directly must keep an anchor, since they must * keep track of their fixup list in order to fix up generated * references to the symbol. This must be overridden by any * subclasses that keep anchors. */ virtual void set_anchor(struct CTcStreamAnchor *) { } /* * Determine if this symbol is external and unresolved. By default, * a symbol cannot be external at all, so this will return false. * Subclasses for symbol types that can be external should override * this to return true if the symbol is an unresolved external * reference. */ virtual int is_unresolved_extern() const { return FALSE; } /* * Mark the symbol as referenced. Some symbol types keep track of * whether they've been referenced or not; those types can override * this to keep track. This method is called each time the symbol * is found in the symbol table via the find() or find_or_def() * methods. By default, we do nothing. */ virtual void mark_referenced() { } /* * Apply internal fixups. If the symbol keeps its own internal * fixup information, it can translate the fixups here. By default, * this does nothing. */ virtual void apply_internal_fixups() { } /* * Build dictionary entries for this symbol. Most symbols do * nothing here; objects which can have associated vocabulary words * should insert their vocabulary into the dictionary. */ virtual void build_dictionary() { } /* * Create a new "context variable" version of this symbol for use in * an anonymous function. This is only needed for symbols that can * exist in a local scope. */ virtual class CTcSymbol *new_ctx_var() const { return 0; } /* * Apply context variable conversion. If this symbol has not been * referenced, this should simply remove the symbol from the symbol * table. Otherwise, this should apply the necessary conversions to * the original symbol from which this symbol was created to ensure * that the original and this symbol share a context variable slot. * * Returns true if a conversion was performed (i.e., the symbol was * referenced), false if not. */ virtual int apply_ctx_var_conv(class CTcPrsSymtab *, class CTPNCodeBody *) { return FALSE; } /* * Finalize context variable conversion. This should do nothing if * the variable hasn't already been notified that it's a context * variable (how this happens varies by symbol type - see locals in * particular). This is called with the variable's own scope active * in the parser, so the final variable assignments for the symbol * can be made. */ virtual void finish_ctx_var_conv() { } /* * Check for local references. For variables that can exist in * local scope, such as locals, this will be called when all of the * code for the scope has been parsed; this should check to see if * the symbol has been referenced in the scope, and display an * appropriate warning message if not. */ virtual void check_local_references() { } /* * Add an entry for this symbol to a "runtime symbol table," which is * a symbol table that we can pass to the interpreter. This must be * overridden by each symbol type for each target architecture, * because the nature of the runtime symbol table varies by target * architecture. * * By default, this does nothing. Symbol types that don't need to * generate runtime symbol table entries don't need to override this. */ virtual void add_runtime_symbol(class CVmRuntimeSymbols *) { } protected: /* * Base routine to read from a symbol file - reads the symbol name. * Returns a pointer to the symbol name (stored in tokenizer memory * that will remain valid throughout the compilation) on success; on * failure, logs an error and returns null. */ static const char *base_read_from_sym_file(class CVmFile *fp); /* symbol type */ tc_symtype_t typ_; }; #endif /* TCPN_H */ qtads-2.1.7/tads3/tcpndrv.h000066400000000000000000004624531265017072300155250ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/TCPNDRV.H,v 1.4 1999/07/11 00:46:53 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcpndrv.h - parse node base classes derived from CTcPrsNode Function This file contains the intermediate classes derived from CTcPrsNode. CTcPrsNode is defined separately for each target code generator. These classes are separated into this header file so that they can be defined after the target-specific CTcPrsNode header has been included, which in turn must be included after the base parse node file, tcpnbase.h. Finally, after this file, the target-specific derived classes header can be included. The sequence is thus: tcpnbase.h - target-independent CTcPrsNodeBase ???.h - target-specific CTcPrsNode definition (file varies by target) tcpnint.h - target-independent intermediate classes ???.h - target-specific intermediate classes tcpndrv.h - target-independent derived CTPNXxxBase classes ???.h - target-specific derived CTPNXxx final classes Notes Modified 05/10/99 MJRoberts - Creation */ #ifndef TCPNDRV_H #define TCPNDRV_H /* ------------------------------------------------------------------------ */ /* * "argcount" node */ class CTPNArgcBase: public CTcPrsNode { public: /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) { /* simply return myself unchanged */ return this; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *) { return this; } }; /* ------------------------------------------------------------------------ */ /* * "self" node */ class CTPNSelfBase: public CTcPrsNode { public: /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) { /* simply return myself unchanged */ return this; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *) { return this; } }; /* ------------------------------------------------------------------------ */ /* * "targetprop" node */ class CTPNTargetpropBase: public CTcPrsNode { public: /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) { /* simply return myself unchanged */ return this; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *) { return this; } }; /* ------------------------------------------------------------------------ */ /* * "targetobj" node */ class CTPNTargetobjBase: public CTcPrsNode { public: /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) { /* simply return myself unchanged */ return this; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *) { return this; } }; /* ------------------------------------------------------------------------ */ /* * "definingobj" node */ class CTPNDefiningobjBase: public CTcPrsNode { public: /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *) { /* simply return myself unchanged */ return this; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *) { return this; } }; /* ------------------------------------------------------------------------ */ /* * "invokee" node */ class CTPNInvokeeBase: public CTcPrsNode { public: /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *) { return this; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *) { return this; } }; /* ------------------------------------------------------------------------ */ /* * "inherited" node */ class CTPNInhBase: public CTcPrsNode { public: CTPNInhBase() { typelist_ = 0; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) { /* simply return myself unchanged */ return this; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *) { return this; } /* get/set the formal type list */ class CTcFormalTypeList *get_typelist() const { return typelist_; } void set_typelist(class CTcFormalTypeList *l) { typelist_ = l; } protected: class CTcFormalTypeList *typelist_; }; /* * "inherited class" node - for (inherited superclass.method) construct */ class CTPNInhClassBase: public CTcPrsNode { public: CTPNInhClassBase(const char *sym, size_t len) { sym_ = sym; len_ = len; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) { /* simply return myself unchanged */ return this; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *) { return this; } protected: /* the superclass name symbol and its length */ const char *sym_; size_t len_; }; /* * "delegated" node - for (delegated expr.method) construct */ class CTPNDelegatedBase: public CTcPrsNode { public: CTPNDelegatedBase(CTcPrsNode *delegatee) { delegatee_ = delegatee; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) { /* fold constants in the delegatee */ delegatee_ = delegatee_->fold_constants(symtab); /* return myself otherwise unchanged */ return this; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { /* adjust the delegatee subexpression */ delegatee_ = delegatee_->adjust_for_dyn(info); /* return myself otherwise unchanged */ return this; } protected: /* the delegatee (the object to which we delegate the method) */ CTcPrsNode *delegatee_; }; /* * "replaced" node */ class CTPNReplacedBase: public CTcPrsNode { public: /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) { /* simply return myself unchanged */ return this; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *) { return this; } }; /* ------------------------------------------------------------------------ */ /* * Parse node for constant values (numbers, strings) */ class CTPNConstBase: public CTcPrsNode { public: CTPNConstBase(const CTcConstVal *val) { val_ = *val; } /* get the constant value of the node */ virtual class CTcConstVal *get_const_val() { return &val_; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) { /* simply return myself unchanged */ return this; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info); /* check to see if this is a boolean value */ virtual int is_bool() const { return val_.is_bool(); } protected: CTcConstVal val_; }; /* * The target must define a subclass, CTPNDebugConst, for use when * evaluating debugger expressions. */ /* class CTPNDebugConst: public CTPNConst ... */ /* ------------------------------------------------------------------------ */ /* * Address-of Operator */ class CTPNAddrBase: public CTPNUnary { public: CTPNAddrBase(CTcPrsNode *sub) : CTPNUnary(sub) { } /* this is an address expression */ virtual int is_addr() const { return TRUE; } /* * Determine if this address expression is the same as a given * address expression. Returns true if so, false if not. Sets * (*comparable) to true if the comparison is meaningful, false if * not; if (*comparable) is set to false, the caller should not * attempt to perform a compile-time comparison of the addresses, * and must defer the comparison to run-time. */ int is_addr_eq(const class CTPNAddr *, int *comparable) const; /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *) { return this; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); }; /* ------------------------------------------------------------------------ */ /* * Symbol node. This node handles symbols that are not resolved at * parse time, and whose resolution must be deferred to code generation. * Any symbol that doesn't resolve at parse time to something in local * scope uses this type of node. */ class CTPNSymBase: public CTcPrsNode { public: CTPNSymBase(const char *sym, size_t len) { /* remember my symbol name */ sym_ = sym; len_ = len; } /* * Assume that a simple symbol primary can be used as an lvalue. * Some symbols are not suitable as lvalues, but we can't know this * at parse time, so assume that an lvalue is okay for now. */ virtual int check_lvalue() const { return TRUE; } /* check to see if this is an lvalue, resolving in the given scope */ virtual int check_lvalue_resolved(class CTcPrsSymtab *symtab) const; /* * At parse time, assume that we can take the address of a simple * symbol primary. */ virtual int has_addr() const { return TRUE; } /* determine if I have a return value when called */ virtual int has_return_value_on_call() const; /* get my symbol information */ const char *get_sym_text() const { return sym_; } size_t get_sym_text_len() const { return len_; } int sym_text_matches(const char *sym, size_t len) const { return (len_ == len && memcmp(sym, sym_, len) == 0); } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* generate a constant node for my address value */ class CTcPrsNode *fold_addr_const(class CTcPrsSymtab *symtab); /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *); protected: /* my symbol */ const char *sym_; size_t len_; }; /* ------------------------------------------------------------------------ */ /* * Resolved symbol node. This node handles symbols that are resolve * during parsing. All local scope symbols, by the nature of the * language, are resolvable during parsing, because local scope symbols * must be declared before their first use. */ class CTPNSymResolvedBase: public CTcPrsNode { public: CTPNSymResolvedBase(CTcSymbol *sym) { /* remember my symbol table entry */ sym_ = sym; } /* get my symbol information */ const char *get_sym_text() const { return sym_->get_sym(); } size_t get_sym_text_len() const { return sym_->get_sym_len(); } int sym_text_matches(const char *sym, size_t len) const { return (sym_->get_sym_len() == len && memcmp(sym, sym_->get_sym(), len) == 0); } /* determine if I can be an lvalue */ virtual int check_lvalue() const { return sym_->check_lvalue(); } /* * check to see if this is an lvalue, resolving in the given scope - * this type of symbol is pre-resolved, so we can ignore the symbol * table and just ask our resoloved symbol directly */ virtual int check_lvalue_resolved(class CTcPrsSymtab *) const { return sym_->check_lvalue(); } /* determine if I have an address */ virtual int has_addr() const { return sym_->has_addr(); } /* determine if I have a return value when called as a function */ virtual int has_return_value_on_call() const { return sym_->has_return_value_on_call(); } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* generate a constant node for my address value */ class CTcPrsNode *fold_addr_const(class CTcPrsSymtab *symtab); /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *) { return this; } protected: /* the symbol table entry for this symbol */ class CTcSymbol *sym_; }; /* ------------------------------------------------------------------------ */ /* * Debugger local variable resolved symbol */ class CTPNSymDebugLocalBase: public CTcPrsNode { public: CTPNSymDebugLocalBase(const struct tcprsdbg_sym_info *info); /* locals can always be lvalues */ virtual int check_lvalue() const { return TRUE; } virtual int check_lvalue_resolved(class CTcPrsSymtab *) const { return TRUE; } /* determine if I have an address */ virtual int has_addr() const { return FALSE; } /* * Determine if I have a return value when called as a function. We * have to assume that we do, since we can't know in advance what the * variable will hold at run-time. If it holds a function with a * return value, we'll need to return the value; if it doesn't, the * function will effectively return nil, so returning the non-result * will do no harm. */ virtual int has_return_value_on_call() const { return TRUE; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *) { return this; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *) { return this; } protected: /* local variable ID */ uint var_id_; /* context array index for this variable, if I'm in a context */ int ctx_arr_idx_; /* stack frame index */ uint frame_idx_; /* is this a parameter? (if not, it's a local) */ uint is_param_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Argument List */ class CTPNArglistBase: public CTcPrsNode { public: CTPNArglistBase(int argc, class CTPNArg *list_head) { /* remember the argument count and list head */ argc_ = argc; list_ = list_head; } /* get/set the argument count */ int get_argc() const { return argc_; } void set_argc(int argc) { argc_ = argc; } /* * Get the head argument of the list. The other arguments can be * obtained by following the linked list from this one. Note that * we build some argument lists in reverse order, with the last * argument at the head of the list. */ class CTPNArg *get_arg_list_head() const { return list_; } /* set the argument list head */ void set_arg_list_head(class CTPNArg *head) { list_ = head; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info); protected: /* * argument count (this is simply the number of entries in the list, * but we keep it separately for easy access) */ int argc_; /* head of the argument list */ class CTPNArg *list_; }; /* ------------------------------------------------------------------------ */ /* * Argument list entry. Each list entry has the expression for the * argument and a pointer to the next element of the argument list. */ class CTPNArgBase: public CTcPrsNode { public: CTPNArgBase(CTcPrsNode *arg_expr) { /* remember the expression for this argument */ arg_expr_ = arg_expr; /* there's nothing else in our list yet */ next_arg_ = 0; /* assume it's not a list-to-varargs conversion */ is_varargs_ = FALSE; /* presume no argument name */ name_.settyp(TOKT_INVALID); } /* set the parameter name, for a "name: value" argument */ void set_name(const class CTcToken *tok); /* get the name and its length */ const char *get_name() const { return name_.get_text(); } const size_t get_name_len() const { return name_.get_text_len(); } /* is this a named parameter? */ int is_named_param() { return name_.gettyp() != TOKT_INVALID; } /* get the argument expression */ CTcPrsNode *get_arg_expr() const { return arg_expr_; } /* get the next argument in my list */ class CTPNArg *get_next_arg() const { return next_arg_; } /* set the next element in my list */ void set_next_arg(class CTPNArg *prv) { next_arg_ = prv; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { /* adjust the expression */ arg_expr_ = arg_expr_->adjust_for_dyn(info); /* return myself otherwise unchanged */ return this; } /* get/set list-to-varargs conversion flag */ int is_varargs() const { return is_varargs_; } void set_varargs(int f) { is_varargs_ = (f != 0); } protected: /* argument expression */ CTcPrsNode *arg_expr_; /* next argument in the list */ class CTPNArg *next_arg_; /* the parameter name, for a "name: expression" argument */ CTcToken name_; /* flag: this is a list-to-varargs parameter */ unsigned int is_varargs_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Function/Method Call node */ class CTPNCallBase: public CTcPrsNode { public: CTPNCallBase(CTcPrsNode *func, class CTPNArglist *arglist) { /* remember the expression giving the function or method to call */ func_ = func; /* remember the argument list */ arglist_ = arglist; } /* get the function to call */ CTcPrsNode *get_func() const { return func_; } /* get the argument list */ class CTPNArglist *get_arg_list() const { return arglist_; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* we have a return value if our function has a return value */ virtual int has_return_value() const { return func_->has_return_value_on_call(); } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info); protected: /* expression giving function or method to call */ CTcPrsNode *func_; /* argument list */ class CTPNArglist *arglist_; }; /* ------------------------------------------------------------------------ */ /* * double-quoted string node */ class CTPNDstrBase: public CTcPrsNode { public: CTPNDstrBase(const char *str, size_t len); /* get my string information */ const char *get_str() const { return str_; } size_t get_str_len() const { return len_; } /* I'm a double-quoted string node */ virtual int is_dstring() const { return TRUE; } virtual int is_dstring_expr() const { return TRUE; } /* fold constants - there's nothing extra to do here */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) { return this; } /* double-quoted strings have no value */ virtual int has_return_value() const { return FALSE; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info); protected: /* my string */ const char *str_; size_t len_; }; /* * The target must also define CTPNDebugDstr, a subclass of CTPNDstr. * This for use in debugger expressions. */ /* class CTPNDebugDstr: public CTPNDstr ... */ /* * Double-quoted string embedding. This node contains an expression * embedded with the "<< >>" notation inside a double-quoted string. * This type of expression requires an implicit say() call. */ class CTPNDstrEmbedBase: public CTPNUnary { public: CTPNDstrEmbedBase(CTcPrsNode *sub) : CTPNUnary(sub) { } /* this is part of a dstring expression */ virtual int is_dstring_expr() const { return TRUE; } /* we have no return value */ virtual int has_return_value() const { return FALSE; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { /* don't allow in speculative mode due to side effects */ if (info->speculative) err_throw(VMERR_BAD_SPEC_EVAL); /* inherit default unary operator handling */ return CTPNUnary::adjust_for_dyn(info); } }; /* ------------------------------------------------------------------------ */ /* * An embedded <> list in a string. */ class CTPNStrOneOfBase: public CTcPrsNode { public: /* * Create from a list of values, and the attribute string for selecting * a value on each invocation. The attributes string must be static * data, since we don't make a copy. Currently we only allow built-in * attribute selections, which we define in a static structure. */ CTPNStrOneOfBase(int dstr, class CTPNList *lst, class CTcSymObj *state_obj) { dstr_ = dstr; lst_ = lst; state_obj_ = state_obj; } /* this is part of a dstring expression */ virtual int is_dstring_expr() const { return TRUE; } /* adjust for dynamic compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info); /* folder constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* * The overall "one of" list has a return value if it's in a * single-quoted string, since the individual elements will themselves * be represented as single-quoted strings. If we're in a * double-quoted string, the individual elements are double-quoted * strings. */ int has_return_value() const { return !dstr_; } protected: /* type of string - double quoted or single quoted */ int dstr_; /* the underlying list of substrings to choose from */ class CTPNList *lst_; /* the state object (an anonymous OneOfIndexGen instance) */ class CTcSymObj *state_obj_; }; /* ------------------------------------------------------------------------ */ /* * List node. A list has an underlying linked list of elements. */ class CTPNListBase: public CTcPrsNode { public: CTPNListBase() { /* there are initially no elements */ cnt_ = 0; head_ = tail_ = 0; /* * initially, the list is constant, because it's just an empty * list; as we add elements, we'll check for constants, and * we'll clear the constant flag the first time we add a * non-constant element */ is_const_ = TRUE; /* presume it's not a lookup table list */ is_lookup_table_ = FALSE; /* set my constant value to point to myself */ const_val_.set_list((class CTPNList *)this); } /* add an element to the list with a given expression */ void add_element(CTcPrsNode *expr); /* remove each occurrence of a constant element from the list */ void remove_element(const class CTcConstVal *val); /* get the constant value of the list */ CTcConstVal *get_const_val() { /* * return our constant value only if we have one AND we're not a * lookup table - a lookup table requires a NEW operation at * run-time, so even if the underlying list of source values is * constant, the overall list value isn't */ return (is_const_ && !is_lookup_table_ ? &const_val_ : 0); } /* * get the first/last element - other elements can be reached by * traversing the linked list of elements starting with the head or * tail */ class CTPNListEle *get_head() const { return head_; } class CTPNListEle *get_tail() const { return tail_; } /* get the constant element at the given index */ CTcPrsNode *get_const_ele(int index); /* get the number of elements in the list */ int get_count() const { return cnt_; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info); /* mark it as a lookup table list */ void set_lookup_table() { is_lookup_table_ = TRUE; } protected: /* number of elements in the list */ int cnt_; /* head and tail of list of elements */ class CTPNListEle *head_; class CTPNListEle *tail_; /* * our constant value - this is valid only when all elements of the * list are constant, as indicated by the is_const_ paremeter */ CTcConstVal const_val_; /* flag: all elements of the list are constant */ unsigned int is_const_ : 1; /* flag: this is a LookupTable list */ unsigned int is_lookup_table_ : 1; }; /* * List Element. Each list element is represented by one of these * objects; these are in turn linked together into a list. */ class CTPNListEleBase: public CTcPrsNode { public: CTPNListEleBase(CTcPrsNode *expr) { /* remember the underlying expression */ expr_ = expr; /* this element isn't in a list yet */ next_ = 0; prev_ = 0; } /* get/set the next element */ class CTPNListEle *get_next() const { return next_; } void set_next(class CTPNListEle *nxt) { next_ = nxt; } /* get/set the previous element */ class CTPNListEle *get_prev() const { return prev_; } void set_prev(class CTPNListEle *prv) { prev_ = prv; } /* get the underlying expression */ CTcPrsNode *get_expr() const { return expr_; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) { /* fold constants in my expression */ expr_ = expr_->fold_constants(symtab); /* return myself unchanged (although my contents may be changed) */ return this; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { /* adjust my expression */ expr_ = expr_->adjust_for_dyn(info); /* return myself otherwise unchanged */ return this; } protected: /* the underlying expression */ CTcPrsNode *expr_; /* the next element of the list */ class CTPNListEle *next_; /* previous element of the list */ class CTPNListEle *prev_; }; /* ------------------------------------------------------------------------ */ /* * Member with no arguments */ class CTPNMemberBase: public CTcPrsNode { public: CTPNMemberBase(CTcPrsNode *obj_expr, CTcPrsNode *prop_expr, int prop_is_expr) { /* remember the subexpressions */ obj_expr_ = obj_expr; prop_expr_ = prop_expr; /* remember whether or not the property is an expression */ prop_is_expr_ = prop_is_expr; } /* a member with no arguments can always be used as an lvalue */ virtual int check_lvalue() const { return TRUE; } virtual int check_lvalue_resolved(class CTcPrsSymtab *) const { return TRUE; } /* get the subexpressions */ CTcPrsNode *get_obj_expr() const { return obj_expr_; } CTcPrsNode *get_prop_expr() const { return prop_expr_; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) { /* fold constants in the object and property expressions */ obj_expr_ = obj_expr_->fold_constants(symtab); prop_expr_ = prop_expr_->fold_constants(symtab); /* return myself with no further evaluation */ return this; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info); protected: /* object expression (left of '.') */ CTcPrsNode *obj_expr_; /* property expression (right of '.') */ CTcPrsNode *prop_expr_; /* flag: the property is an expression, not a simple property name */ unsigned int prop_is_expr_ : 1; }; /* * The target must also define another class, CTPNSpecDebugMember, a * subclass of CTPNMember. This new class must ensure at run-time that * the property evaluation has no side effects. */ /* class CTPNSpecDebugMember: public CTPNMember ... */ /* * Member with arguments - this is for a property evaluation with an * argument list. (Simple property evaluation without arguments is a * normal binary node subclass.) */ class CTPNMemArgBase: public CTcPrsNode { public: CTPNMemArgBase(CTcPrsNode *obj_expr, CTcPrsNode *prop_expr, int prop_is_expr, class CTPNArglist *arglist) { /* save the subexpressions */ obj_expr_ = obj_expr; prop_expr_ = prop_expr; arglist_ = arglist; /* remember whether or not the property is an expression */ prop_is_expr_ = prop_is_expr; } /* get the subexpressions */ CTcPrsNode *get_obj_expr() const { return obj_expr_; } CTcPrsNode *get_prop_expr() const { return prop_expr_; } class CTPNArglist *get_arg_list() const { return arglist_; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info); protected: /* object expression (left of '.') */ CTcPrsNode *obj_expr_; /* property expression (right of '.') */ CTcPrsNode *prop_expr_; /* argument list */ class CTPNArglist *arglist_; /* flag: the property is an expression, not a simple property name */ unsigned int prop_is_expr_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Subscript */ class CTPNSubscriptBase: public CTPNBin { public: CTPNSubscriptBase(CTcPrsNode *lhs, CTcPrsNode *rhs) : CTPNBin(lhs, rhs) { } /* a subscripted array element can be assigned into */ virtual int check_lvalue() const { return TRUE; } virtual int check_lvalue_resolved(class CTcPrsSymtab *) const { return TRUE; } /* fold constants */ class CTcPrsNode *fold_binop(); }; /* ------------------------------------------------------------------------ */ /* * Comma operator */ class CTPNCommaBase: public CTPNBin { public: CTPNCommaBase(CTcPrsNode *lhs, CTcPrsNode *rhs) : CTPNBin(lhs, rhs) { } /* fold constants */ class CTcPrsNode *fold_binop(); /* * a comma expression's value is the value of the right operand, so the * type is boolean if the right operand's type is boolean */ virtual int is_bool() const { return right_->is_bool(); } /* * determine if I have a value - I have a value if my right * subexpression has a value */ virtual int has_return_value() const { return right_->has_return_value(); } /* this is part of a dstring expression if either side is */ virtual int is_dstring_expr() const { return left_->is_dstring_expr() || right_->is_dstring_expr(); } }; /* ------------------------------------------------------------------------ */ /* * Add */ class CTPNAddBase: public CTPNBin { public: CTPNAddBase(CTcPrsNode *lhs, CTcPrsNode *rhs) : CTPNBin(lhs, rhs) { } /* fold constants */ class CTcPrsNode *fold_binop(); }; /* ------------------------------------------------------------------------ */ /* * Subtract */ class CTPNSubBase: public CTPNBin { public: CTPNSubBase(CTcPrsNode *lhs, CTcPrsNode *rhs) : CTPNBin(lhs, rhs) { } /* fold constants */ class CTcPrsNode *fold_binop(); }; /* ------------------------------------------------------------------------ */ /* * Equality Comparison */ class CTPNEqBase: public CTPNBin { public: CTPNEqBase(CTcPrsNode *lhs, CTcPrsNode *rhs) : CTPNBin(lhs, rhs) { } /* fold constants */ class CTcPrsNode *fold_binop(); /* comparison operators yield boolean results */ virtual int is_bool() const { return TRUE; } }; /* ------------------------------------------------------------------------ */ /* * Inequality Comparison */ class CTPNNeBase: public CTPNBin { public: CTPNNeBase(CTcPrsNode *lhs, CTcPrsNode *rhs) : CTPNBin(lhs, rhs) { } /* fold constants */ class CTcPrsNode *fold_binop(); /* comparison operators yield boolean results */ virtual int is_bool() const { return TRUE; } }; /* ------------------------------------------------------------------------ */ /* * 'is in' */ class CTPNIsInBase: public CTPNBin { public: CTPNIsInBase(CTcPrsNode *lhs, class CTPNArglist *rhs); /* fold constants */ class CTcPrsNode *fold_binop(); /* comparison operators yield boolean results */ virtual int is_bool() const { return TRUE; } protected: /* * flag: if true, we have a constant true value (because the left * side is a constant, and the right side list contains the same * constant) */ unsigned int const_true_ : 1; }; /* ------------------------------------------------------------------------ */ /* * 'not in' */ class CTPNNotInBase: public CTPNBin { public: CTPNNotInBase(CTcPrsNode *lhs, class CTPNArglist *rhs); /* fold constants */ class CTcPrsNode *fold_binop(); /* comparison operators yield boolean results */ virtual int is_bool() const { return TRUE; } /* * flag: if true, we have a constant true value (because the left * side is a constant, and the right side list is entirely constants * and doesn't contain the left side value) */ unsigned int const_false_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Logical AND operator node */ class CTPNAndBase: public CTPNBin { public: CTPNAndBase(CTcPrsNode *lhs, CTcPrsNode *rhs) : CTPNBin(lhs, rhs) { } /* a logical AND always yields a boolean */ virtual int is_bool() const { return TRUE; } /* fold constants */ class CTcPrsNode *fold_binop(); }; /* ------------------------------------------------------------------------ */ /* * Logical OR operator node */ class CTPNOrBase: public CTPNBin { public: CTPNOrBase(CTcPrsNode *lhs, CTcPrsNode *rhs) : CTPNBin(lhs, rhs) { } /* a logical OR always yields a boolean value */ virtual int is_bool() const { return TRUE; } /* fold constants */ class CTcPrsNode *fold_binop(); }; /* ------------------------------------------------------------------------ */ /* * Simple assignment operator */ class CTPNAsiBase: public CTPNBin { public: CTPNAsiBase(CTcPrsNode *lhs, CTcPrsNode *rhs) : CTPNBin(lhs, rhs) { } /* we're a simple assignment operator */ virtual int is_simple_asi() const { return TRUE; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { /* if it's speculative, don't allow it, because of side effects */ if (info->speculative) err_throw(VMERR_BAD_SPEC_EVAL); /* inherit default processing */ return CTPNBin::adjust_for_dyn(info); } }; /* ------------------------------------------------------------------------ */ /* * NOT operator node */ class CTPNNotBase: public CTPNUnary { public: CTPNNotBase(CTcPrsNode *sub) : CTPNUnary(sub) { } /* NOT always yields a boolean value */ virtual int is_bool() const { return TRUE; } /* fold constants */ class CTcPrsNode *fold_unop(); }; /* ------------------------------------------------------------------------ */ /* * If-nil node - operator ?? */ class CTPNIfnilBase: public CTcPrsNode { public: CTPNIfnilBase(class CTcPrsNode *first, class CTcPrsNode *second) { first_ = first; second_ = second; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* I have a return value if one or the other of my subnodes has a value */ virtual int has_return_value() const { return first_->has_return_value() || second_->has_return_value(); } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { /* adjust my subexpressions */ first_ = first_->adjust_for_dyn(info); second_ = second_->adjust_for_dyn(info); return this; } protected: class CTcPrsNode *first_; class CTcPrsNode *second_; }; /* ------------------------------------------------------------------------ */ /* * Conditional operator parse node */ class CTPNIfBase: public CTcPrsNode { public: CTPNIfBase(class CTcPrsNode *first, class CTcPrsNode *second, class CTcPrsNode *third) { first_ = first; second_ = second; third_ = third; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* add the branches */ void set_cond(CTcPrsNode *e) { first_ = e; } void set_then(CTcPrsNode *e) { second_ = e; } void set_else(CTcPrsNode *e) { third_ = e; } /* I have a return value if one or the other of my subnodes has a value */ virtual int has_return_value() const { return second_->has_return_value() || third_->has_return_value(); } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { /* adjust my subexpressions */ first_ = first_->adjust_for_dyn(info); second_ = second_->adjust_for_dyn(info); third_ = third_->adjust_for_dyn(info); /* return myself otherwise unchanged */ return this; } protected: class CTcPrsNode *first_; class CTcPrsNode *second_; class CTcPrsNode *third_; }; /* ------------------------------------------------------------------------ */ /* * Derived target-independent symbol classes */ /* * Undefined symbol. During symbol resolution (after all parsing is * completed), when we encounter a symbol that isn't defined, we can use * this symbol type to provide a placeholder. Creating an unknown * symbol is an error, so the program cannot be successfully compiled * once an undefined symbol is encountered, but this at least allows * compilation to proceed through the rest of the program to check for * other errors. */ class CTcSymUndefBase: public CTcSymbol { public: CTcSymUndefBase(const char *str, size_t len, int copy) : CTcSymbol(str, len, copy, TC_SYM_UNKNOWN) { } /* do not write this to a symbol file */ virtual int write_to_sym_file(class CVmFile *) { return FALSE; } /* do not write this to an object file */ virtual int write_to_obj_file(class CVmFile *) { return FALSE; } /* this can't be an lvalue */ virtual int check_lvalue() const { return FALSE; } }; /* * Function. At construction time, a function's actual code offset is * never known; we cannot know a function's code offset until after * we've finished code generation. */ class CTcSymFuncBase: public CTcSymbol { public: CTcSymFuncBase(const char *str, size_t len, int copy, int argc, int opt_argc, int varargs, int has_retval, int is_multimethod, int is_multimethod_base, int is_extern, int has_proto) : CTcSymbol(str, len, copy, TC_SYM_FUNC) { /* remember the interface information */ argc_ = argc; opt_argc_ = opt_argc; varargs_ = varargs; has_retval_ = has_retval; is_multimethod_ = is_multimethod; is_multimethod_base_ = is_multimethod_base; /* note whether it's external */ is_extern_ = is_extern; /* note whether it has a prototype */ has_proto_ = has_proto; /* no code stream anchor yet */ anchor_ = 0; /* no fixups yet */ fixups_ = 0; /* this isn't replacing an external function */ ext_replace_ = FALSE; /* we don't know our code body yet */ code_body_ = 0; /* there's no base modified symbol */ mod_base_ = 0; /* we haven't been modified yet */ mod_global_ = 0; /* assume it's not defined in this file */ mm_def_ = FALSE; } /* determine if I'm an unresolved external */ virtual int is_unresolved_extern() const { return is_extern_; } /* this can't be an lvalue */ virtual int check_lvalue() const { return FALSE; } /* * Get the argument count. If is_varargs() returns true, this is a * minimum argument count; otherwise, this is the exact number of * arguments required. */ int get_argc() const { return argc_; } int get_opt_argc() const { return opt_argc_; } int is_varargs() const { return varargs_; } /* is the argument list within the correct range? */ int argc_ok(int n) const { /* * the argument list is correct if it has at least the fixed * arguments, AND either we have varargs (in which case there's no * upper limit) or the argument count is less than the maximum * (which is the fixed plus optionals) */ return (n >= argc_ && (varargs_ || n <= argc_ + opt_argc_)); } /* build a descriptive message about the argument list, for errors */ const char *get_argc_desc(char *buf) { if (varargs_) sprintf(buf, "%d+", argc_); else if (opt_argc_ != 0) sprintf(buf, "%d-%d", argc_, argc_ + opt_argc_); else sprintf(buf, "%d", argc_); return buf; } /* get/set the 'extern' flag */ int is_extern() const { return is_extern_; } void set_extern(int f) { is_extern_ = f; } /* get/set the 'has prototype' flag */ int has_proto() const { return has_proto_; } void set_has_proto(int f) { has_proto_ = f; } /* get/set the multi-method flag */ int is_multimethod() const { return is_multimethod_; } void set_multimethod(int f) { is_multimethod_ = f; } /* get/set the multi-method base function flag */ int is_multimethod_base() const { return is_multimethod_base_; } void set_multimethod_base(int f) { is_multimethod_base_ = f; } /* is this multi-method defined in this file? */ int is_mm_def() const { return mm_def_; } void set_mm_def(int f) { mm_def_ = f; } /* get/set the external 'replace' flag */ int get_ext_replace() const { return ext_replace_; } void set_ext_replace(int f) { ext_replace_ = f; } /* get/set the modified base symbol */ class CTcSymFunc *get_mod_base() const { return mod_base_; } void set_mod_base(class CTcSymFunc *f) { mod_base_ = f; } /* * Get/set the global symbol. For a function replaced with 'modify', * this is the original symbol for the function, which is still in the * global symbol table. Otherwise, this is simply null. */ class CTcSymFunc *get_mod_global() const { return mod_global_; } void set_mod_global(class CTcSymFunc *f) { mod_global_ = f; } /* get/set my code body */ class CTPNCodeBody *get_code_body() const { return code_body_; } void set_code_body(class CTPNCodeBody *cb) { code_body_ = cb; } /* determine if the function has a return value */ int has_retval() const { return has_retval_; } /* the name of a function used alone yields its address */ class CTcPrsNode *fold_constant(); /* write some additional data to the symbol file */ virtual int write_to_sym_file(class CVmFile *fp); /* write some additional data to the object file */ virtual int write_to_obj_file(class CVmFile *fp); /* read from a symbol file */ static class CTcSymbol *read_from_sym_file(class CVmFile *fp); /* load from an object file */ static int load_from_obj_file(class CVmFile *fp, const textchar_t *fname); /* get my anchor */ struct CTcStreamAnchor *get_anchor() const { return anchor_; } /* * Set my anchor. This must be invoked during code generation when * our code body is generated. */ virtual void set_anchor(struct CTcStreamAnchor *anchor) { anchor_ = anchor; } /* get a pointer to my fixup list anchor */ virtual struct CTcAbsFixup **get_fixup_list_anchor() { return &fixups_; } /* add an absolute fixup for the symbol at the given stream offset */ void add_abs_fixup(class CTcDataStream *ds, ulong ofs); /* add an absolute fixup for the symbol at the current stream offset */ void add_abs_fixup(class CTcDataStream *ds); /* determine if I have a return value when called as a function */ virtual int has_return_value_on_call() const { return has_retval_; } /* get the number of allocated modified base function offsets */ int get_mod_base_offset_count() const { return mod_base_ofs_list_.get_count(); } /* get a modified base function offset by index */ ulong get_mod_base_offset(int idx) { return (ulong)mod_base_ofs_list_.get_ele_long(idx); } /* add a modified base function offset */ void add_mod_base_offset(ulong ofs) { mod_base_ofs_list_.add_ele((long)ofs); } /* clear the modified base offset list */ void clear_mod_base_offsets() { mod_base_ofs_list_.clear(); } /* * Set the absolute address. For debugger expressions, this sets the * code pool address specified in the debugger source expression. */ virtual void set_abs_addr(uint32_t addr) = 0; protected: /* head of the fixup list for our function's code */ struct CTcAbsFixup *fixups_; /* * my stream anchor - this records my byte stream data size, code * stream offset, and (after linking) final pool address */ struct CTcStreamAnchor *anchor_; /* the code body of the function */ class CTPNCodeBody *code_body_; /* argument count */ int argc_; /* number of additional optional arguments */ int opt_argc_; /* the original symbol we modified, if any */ class CTcSymFunc *mod_base_; /* global symbol, for a modified function */ class CTcSymFunc *mod_global_; /* array of modified base function code stream offsets */ CPrsArrayList mod_base_ofs_list_; /* flag: variable arguments (in which case argc_ is only a minimum) */ unsigned int varargs_ : 1; /* flag: the function has a return value (false -> void function) */ unsigned int has_retval_ : 1; /* flag: this function is external to us */ unsigned int is_extern_ : 1; /* flag: this function replaces an external function */ unsigned int ext_replace_ : 1; /* flag: this is a multi-method */ unsigned int is_multimethod_ : 1; /* flag: this is a multi-method base function */ unsigned int is_multimethod_base_ : 1; /* flag: this multi-method is defined in this file */ unsigned int mm_def_ : 1; /* * Flag: this function has a known prototype. An 'extern' function * declaration is allowed without a prototype, in which case the * function can be defined elsewhere with any prototype. */ unsigned int has_proto_ : 1; }; /* * metaclass symbol - property list entry */ class CTcSymMetaProp { public: CTcSymMetaProp(class CTcSymProp *prop, int is_static) { /* remember the property symbol */ prop_ = prop; /* remember whether it's static */ is_static_ = is_static; /* not in a list yet */ nxt_ = 0; } /* property symbol for this entry */ class CTcSymProp *prop_; /* next entry in the list */ CTcSymMetaProp *nxt_; /* flag: the property is declared as 'static' */ unsigned int is_static_ : 1; }; /* * Metaclass. * * Metaclasses must be declared per module. However, we store "soft" * references in symbol files, purely so that the defined() operator knows * whether a metaclass is included in the build. The soft references don't * contain any property table information, so we still need a full * 'intrinsic class' definition in each module. */ class CTcSymMetaclassBase: public CTcSymbol { public: CTcSymMetaclassBase(const char *str, size_t len, int copy, int meta_idx, tctarg_obj_id_t class_obj) : CTcSymbol(str, len, copy, TC_SYM_METACLASS) { /* remember the metaclass dependency list index */ meta_idx_ = meta_idx; /* we have no properties in our list yet */ prop_head_ = 0; prop_tail_ = 0; prop_cnt_ = 0; /* remember my class object */ class_obj_ = class_obj; /* we have no modification object yet */ mod_obj_ = 0; /* we don't have a superclass yet */ super_meta_ = 0; /* assume it's not an external symbol */ ext_ = FALSE; /* not yet referenced */ ref_ = FALSE; } /* this can't be an lvalue */ virtual int check_lvalue() const { return FALSE; } /* add a property to my list */ void add_prop(const char *txt, size_t len, const char *obj_fname, int is_static); void add_prop(class CTcSymProp *prop, int is_static); /* get the head of the property symbol list */ CTcSymMetaProp *get_prop_head() const { return prop_head_; } /* get the nth property ID from the metaclass method table */ CTcSymMetaProp *get_nth_prop(int n) const; /* get/set the metaclass dependency table index */ int get_meta_idx() const { return meta_idx_; } void set_meta_idx(int idx) { meta_idx_ = idx; } /* write to a symbol file */ virtual int write_to_sym_file(class CVmFile *fp); /* read from a symbol file */ static class CTcSymbol *read_from_sym_file(class CVmFile *fp); /* write some additional data to the object file */ virtual int write_to_obj_file(class CVmFile *fp); /* load from an object file */ static int load_from_obj_file(class CVmFile *fp, const textchar_t *fname, tctarg_obj_id_t *obj_xlat); /* get my class object */ tctarg_obj_id_t get_class_obj() const { return class_obj_; } /* get/set my modification object */ class CTcSymObj *get_mod_obj() const { return mod_obj_; } void set_mod_obj(CTcSymObj *obj) { mod_obj_ = obj; } /* get/set my intrinsic superclass */ class CTcSymMetaclass *get_super_meta() const { return super_meta_; } void set_super_meta(class CTcSymMetaclass *sc) { super_meta_ = sc; } /* * Get/set the external reference status. An external metaclass is one * that's defined in another module; we have visibility to it via a * reference in a symbol file. This definition isn't sufficient to * actually use the metaclass; the metaclass must be declared per * module via an explicitly included 'intrinsic class' statment. The * external reference is sufficient for the defined() operator, though, * so we can test whether a metaclass is part of the build within a * module that doesn't declare the metaclass. */ int is_ext() const { return ext_; } void set_ext(int f) { ext_ = f; } protected: /* our intrinsic superclass */ class CTcSymMetaclass *super_meta_; /* metaclass dependency table index */ int meta_idx_; /* list of properties */ CTcSymMetaProp *prop_head_; CTcSymMetaProp *prop_tail_; /* * modification object - this is the object that contains the user * methods added to the intrinsic class via 'modify' */ class CTcSymObj *mod_obj_; /* number of properties in our list */ int prop_cnt_; /* the object that represents the class */ tctarg_obj_id_t class_obj_; /* is this an external metaclass symbol? */ uint ext_ : 1; /* has our object been referenced in code generation? */ uint ref_ : 1; }; /* * Object. An object's ID number can be assigned as soon as the object * is defined during parsing. */ class CTcSymObjBase: public CTcSymbol { public: CTcSymObjBase(const char *str, size_t len, int copy, ulong obj_id, int is_extern, tc_metaclass_t meta, class CTcDictEntry *dict) : CTcSymbol(str, len, copy, TC_SYM_OBJ) { /* store the object ID */ obj_id_ = obj_id; /* note whether it's external */ is_extern_ = is_extern; /* we're not yet referenced */ ref_ = FALSE; /* we don't have a stream offset yet */ stream_ofs_ = 0xffffffff; /* we're don't 'modify' or 'replace' an external object */ ext_modify_ = ext_replace_ = FALSE; /* I haven't been modified yet */ modified_ = FALSE; /* presume we won't modify another object */ mod_base_sym_ = 0; /* presume we won't be modified by another object */ modifying_obj_.sym_ = 0; /* no parse tree yet */ obj_stm_ = 0; /* no self-reference fixups yet */ fixups_ = 0; /* no deleted properties yet */ first_del_prop_ = 0; /* set the metaclass */ metaclass_ = meta; /* set the dictionary */ dict_ = dict; /* not yet written to object file */ written_to_obj_ = FALSE; counted_in_obj_ = FALSE; /* no object file index yet */ obj_file_idx_ = 0; /* no vocabulary yet */ vocab_ = 0; /* no superclasses in our list yet */ sc_ = 0; /* no superclass names in our list yet */ sc_name_head_ = 0; sc_name_tail_ = 0; /* presume we're not a class */ is_class_ = FALSE; /* we haven't built our inherited vocabulary list yet */ vocab_inherited_ = FALSE; /* presume we're fully nonymous */ anon_ = FALSE; /* assume we will have no metaclass-specific extra data object */ meta_extra_ = 0; /* we're not yet explicitly based on the root object class */ sc_is_root_ = FALSE; /* no templates for this class yet */ template_head_ = template_tail_ = 0; /* we don't have a private grammar rule list yet */ grammar_entry_ = 0; /* presume it's not transient */ transient_ = FALSE; } /* this can't be an lvalue */ virtual int check_lvalue() const { return FALSE; } /* get/set my metaclass */ tc_metaclass_t get_metaclass() const { return metaclass_; } void set_metaclass(tc_metaclass_t meta) { metaclass_ = meta; } /* determine if I'm an unresolved external */ virtual int is_unresolved_extern() const { return is_extern_; } /* get/set the object ID */ ulong get_obj_id() const { return obj_id_; } void set_obj_id(ulong obj_id) { obj_id_ = obj_id; } /* * Get/set the 'extern' flag. If an object symbol is defined as * extern, it means that the object is referenced but not defined in * the current file. This is used for separate compilation; extern * objects must be resolved during linking. */ int is_extern() const { return is_extern_; } void set_extern(int f) { is_extern_ = f; } /* get the appropriate object data stream for this object */ class CTcDataStream *get_stream() { return get_stream_from_meta(metaclass_); } /* get/set my object stream offset */ ulong get_stream_ofs() const { return stream_ofs_; } void set_stream_ofs(ulong ofs) { stream_ofs_ = ofs; } /* get/set my object statement's parse tree */ class CTPNStmObject *get_obj_stm() const { return obj_stm_; } void set_obj_stm(class CTPNStmObject *obj_stm) { obj_stm_ = obj_stm; } /* get/set the external 'modify' flag */ int get_ext_modify() const { return ext_modify_; } void set_ext_modify(int f) { ext_modify_ = f; } /* get/set the external 'replace' flag */ int get_ext_replace() const { return ext_replace_; } void set_ext_replace(int f) { ext_replace_ = f; } /* * Get/Set the 'modify' base object. For a 'modify' object, this sets * the symbol (which is always a synthesized symbol) of the object * being modified. */ class CTcSymObj *get_mod_base_sym() const { return mod_base_sym_; } void set_mod_base_sym(class CTcSymObj *sym); /* * Get/set the modifiying object - this is the 'modify' object * that's modifying us. When we set this value, we'll also tell the * modified object that we are its modifier. */ class CTcSymObj *get_modifying_sym() const { return modifying_obj_.sym_; } void set_modifying_sym(CTcSymObj *sym) { modifying_obj_.sym_ = sym; } /* * Get/set the modifying object ID - the ID can be used instead of * the symbol, if desired. Note that only one or the other may be * stored, since they share the same data space. */ tctarg_obj_id_t get_modifying_obj_id() const { return modifying_obj_.id_; } void set_modifying_obj_id(tctarg_obj_id_t id) { modifying_obj_.id_ = id; } /* * get/set the 'modified' flag - this flag indicates that this is a * placeholder symbol for a modified object */ int get_modified() const { return modified_; } void set_modified(int f) { modified_ = f; } /* * Synthesize a placeholder symbol to use for modified objects. * This symbol keeps track of the original object after * modification. */ static class CTcSymObj *synthesize_modified_obj_sym(int anon); /* * synthesize the name of a modified object base class symbol given * the base class's object ID */ static void synthesize_modified_obj_name(char *buf, tctarg_obj_id_t obj_id); /* add a self-reference fixup */ void add_self_ref_fixup(class CTcDataStream *stream, ulong ofs); /* an object's value is a compile-time constant */ class CTcPrsNode *fold_constant(); /* * we can't take the address of an object - an object symbol yields a * reference to begin with, so taking its address is meaningless */ virtual int has_addr() const { return FALSE; } /* write some additional data to the symbol file */ virtual int write_to_sym_file(class CVmFile *fp); /* write some additional data to the object file */ virtual int write_to_obj_file(class CVmFile *fp); /* write myself as a modified base object to an object file */ int write_to_obj_file_as_modified(class CVmFile *fp); /* write references to the object file */ virtual int write_refs_to_obj_file(class CVmFile *fp); /* load references from the object file */ virtual void load_refs_from_obj_file(class CVmFile *fp, const textchar_t *fname, tctarg_obj_id_t *obj_xlat, tctarg_prop_id_t *prop_xlat); /* mark the object as referenced */ void mark_referenced() { ref_ = TRUE; } /* read from a symbol file */ static class CTcSymbol *read_from_sym_file(class CVmFile *fp); /* load from an object file */ static int load_from_obj_file(class CVmFile *fp, const textchar_t *fname, tctarg_obj_id_t *obj_xlat, int anon); /* load a modified base object from an object file */ static class CTcSymObj * load_from_obj_file_modbase(class CVmFile *fp, const textchar_t *fname, tctarg_obj_id_t *obj_xlat, const textchar_t *mod_name, size_t mod_name_len, int anon); /* get/set the head of my fixup list */ struct CTcIdFixup *get_fixups() const { return fixups_; } void set_fixups(struct CTcIdFixup *fixups) { fixups_ = fixups; } /* apply our internal fixups */ virtual void apply_internal_fixups(); /* add a deleted property entry */ void add_del_prop_entry(class CTcSymProp *prop) { add_del_prop_to_list(&first_del_prop_, prop); } /* get the head of my deleted property list */ class CTcObjPropDel *get_first_del_prop() const { return first_del_prop_; } /* set the head of my deleted property list, replacing any old list */ void set_del_prop_head(class CTcObjPropDel *list_head) { first_del_prop_ = list_head; } /* * Delete a property from 'modify' base objects. This must be * defined by the target-specific code subclass, because the action * of this routine is specific to the code generator. * * This carries out a link-time 'replace' on a property in a * 'modify' object. This object is the 'modify' object; we must * delete the property in all of the base classes of this object * that were modified by this 'modify' or by a 'modify' to which * this 'modify' indirectly applies. Do not delete the property * from this object - only from its modified base classes. Stop * upon reaching a non-modified base class, because this is the base * class of the original. */ virtual void delete_prop_from_mod_base(tctarg_prop_id_t prop_id) = 0; /* * Mark the compiled data for this object as a class object. This * must be defined by the target-specific code subclass, because we * need to modify the generated code for the object. * * This routine is called when an object loaded from an object file * is modified by an object in another object file, and hence * becomes a base class at link-time. We call this on the original, * which doesn't know it's being modified until link time. */ virtual void mark_compiled_as_class() = 0; /* get/set the symbol's containing dictionary */ class CTcDictEntry *get_dict() const { return dict_; } void set_dict(class CTcDictEntry *dict) { dict_ = dict; } /* get/set my object file index */ uint get_obj_file_idx() const { return obj_file_idx_; } void set_obj_file_idx(uint idx) { obj_file_idx_ = idx; } /* add a word to my vocabulary list */ void add_vocab_word(const char *txt, size_t len, tctarg_prop_id_t prop); /* delete a vocabulary property from my list (for 'replace') */ void delete_vocab_prop(tctarg_prop_id_t prop); /* get/set the vocabulary word list head */ class CTcVocabEntry *get_vocab_head() const { return vocab_; } void set_vocab_head(class CTcVocabEntry *entry) { vocab_ = entry; } /* build my dictionary */ virtual void build_dictionary(); /* inherit my vocabulary from superclasses (used during linking) */ void inherit_vocab(); /* add my vocabulary words to a subclass's vocabulary (for linking) */ void add_vocab_to_subclass(class CTcSymObj *sub); /* get/set my class flag (used and valid only during linking) */ int is_class() const { return is_class_ != 0; } void set_is_class(int is_class) { is_class_ = (is_class != 0); } /* get/set the resolved superclass list head (link-time only) */ class CTcObjScEntry *get_sc_head() const { return sc_; } void set_sc_head(class CTcObjScEntry *sc) { sc_ = sc; } /* get the named superclass list head (compile-time only) */ class CTPNSuperclass *get_sc_name_head() const { return sc_name_head_; } /* clear the named superclass list */ void clear_sc_names() { sc_name_head_ = sc_name_tail_ = 0; } /* copy the name list from another object symbol */ void copy_sc_names(CTcSymObjBase *other_obj) { /* point our entries directly to the other symbol's */ sc_name_head_ = other_obj->sc_name_head_; sc_name_tail_ = other_obj->sc_name_tail_; } /* * Add a named superclass. * * We keep a superclass list here, separately from the superclass list * that the associated object statement itself maintains, because we * want hierarchy information for external objects as well as objects * defined in the current module. An external object has only a * symbol table entry, so there's no object statement in which to * stash the object list for such objects. * * In addition, this symbolic superclass list isn't exactly the same * as the true superclass list, because the symbolic list doesn't * include the special unnamed objects created when 'modify' is used. * The list we keep here mirrors the apparent structure as represented * in the source code. */ void add_sc_name_entry(const char *txt, size_t len); /* * determine if I have the given superclass - returns true if the * given superclass is among my direct superclasses, or if any of my * direct superclasses has the given superclass */ int has_superclass(class CTcSymObj *sc_sym) const; /* get/set the anonymous flag */ int is_anon() const { return anon_ != 0; } void set_anon(int f) { anon_ = (f != 0); } /* get/set the metaclass extra data for this object */ void *get_meta_extra() const { return meta_extra_; } void set_meta_extra(void *ptr) { meta_extra_ = ptr; } /* * get/set the root-object-superclass flag - if this is set, it means * that the object was explicitly defined with 'object' (the root * object type) as its superclass */ int sc_is_root() const { return sc_is_root_; } void set_sc_is_root(int f) { sc_is_root_ = (f != 0); } /* add a class-specific template */ void add_template(class CTcObjTemplate *tpl); /* get the head/tail of my template list */ class CTcObjTemplate *get_first_template() const { return template_head_; } class CTcObjTemplate *get_last_template() const { return template_tail_; } /* * Create our grammar rule entry object. If we already have one, * we'll discard it in favor of the new one; this allows a 'replace' * or 'modify' to override any previously-defined grammar rules. */ class CTcGramProdEntry *create_grammar_entry( const char *prod_sym, size_t prod_sym_len); /* get my grammar rule list */ class CTcGramProdEntry *get_grammar_entry() const { return grammar_entry_; } /* * merge my grammar rule list (if any) into the global rule list for * the associated production object */ void merge_grammar_entry(); /* get/set transient status */ int is_transient() const { return transient_; } void set_transient() { transient_ = TRUE; } protected: /* add a deleted property entry to a list */ static void add_del_prop_to_list(class CTcObjPropDel **list_head, class CTcSymProp *prop); /* main object file writer routine */ int write_to_obj_file_main(CVmFile *fp); /* main object file loader routine */ static class CTcSymObj * load_from_obj_file_main(class CVmFile *fp, const textchar_t *fname, tctarg_obj_id_t *obj_xlat, const textchar_t *mod_name, size_t mod_name_len, int anon); /* get the stream for a given metaclass */ static class CTcDataStream *get_stream_from_meta(tc_metaclass_t meta); /* the object's ID */ ulong obj_id_; /* * the object stream (G_os, or other stream as appropriate) offset of * our data - this is valid after code generation if we're not * external */ ulong stream_ofs_; /* the parse tree for our object definition */ class CTPNStmObject *obj_stm_; /* head of our self-reference object ID fixup list */ struct CTcIdFixup *fixups_; /* * Head of list of deleted properties. This list is used only in * the top object in a stack of modified objects (i.e., this list is * attached to the latest 'modify' object), and only when the bottom * object in the stack is external to the current translation unit. * This is the list of objects that must be deleted at link time, * because they're external to the current translation unit. */ class CTcObjPropDel *first_del_prop_; /* * Base symbol for a 'modify' object. This is the synthesized * symbol for the object that this object modifies. */ class CTcSymObj *mod_base_sym_; /* * Modifying symbol for an object modified by a 'modify' object. * This points to the object that is modifying us - it's simply the * reverse link of mod_base_sym_. */ union { class CTcSymObj *sym_; tctarg_obj_id_t id_; } modifying_obj_; /* the dictionary holding my vocabulary words */ class CTcDictEntry *dict_; /* head of vocabulary list for this object */ class CTcVocabEntry *vocab_; /* * Head of superclass list - this list is used and valid ONLY during * linking; it is null during compilation. Because a class or object * can be defined before its superclasses, and can be defined in * separate a separate module from its superclass, we can't resolve * superclasses until link time. */ class CTcObjScEntry *sc_; /* * Superclass name list. This list is used and valid ONLY during * normal compilation, and keeps track of the symbolic name structure * of the class hiearchy. We don't resolve these names; we simply * keep track of the names as they appear in the source code. * * Note that the entries in this list must all have valid txt_ entries * - no entries with sym_ values are allowed in this list. */ class CTPNSuperclass *sc_name_head_; class CTPNSuperclass *sc_name_tail_; /* pointer to metaclass-specific extra data */ void *meta_extra_; /* the object's metaclass */ tc_metaclass_t metaclass_; /* list of templates associated with this class */ class CTcObjTemplate *template_head_; class CTcObjTemplate *template_tail_; /* object file index - used for serializing to the object file */ uint obj_file_idx_; /* grammar rule list, if we have one */ class CTcGramProdEntry *grammar_entry_; /* flag: this object is external to us */ uint is_extern_ : 1; /* flag: the object has been referenced */ uint ref_ : 1; /* * 'modify' and 'replace' flags - these indicate that this object * modifies/replaces an *external* object, hence the modify/replace * action must be performed at link time */ uint ext_modify_ : 1; uint ext_replace_ : 1; /* flag: I've been modified by another object */ uint modified_ : 1; /* flag: I'm a 'class' object (used and valid only during linking) */ uint is_class_ : 1; /* flag: this object has been written to the object file */ uint written_to_obj_ : 1; /* flag: this object has been counted as written to the object file */ uint counted_in_obj_ : 1; /* * flag: we've built our full vocabulary by explicitly inheriting * our superclass's words into our own list */ uint vocab_inherited_ : 1; /* flag: this object is anonymous */ uint anon_ : 1; /* * flag: we are explicitly defined with the root object ('object') as * our superclass */ uint sc_is_root_ : 1; /* flag: the object is transient */ uint transient_ : 1; }; /* * Object vocabulary word entry */ class CTcVocabEntry { public: CTcVocabEntry(const char *txt, size_t len, tctarg_prop_id_t prop) { /* * keep a reference to the text - assume it's in parser memory, * so it'll stay around for the duration of the compilation */ txt_ = txt; len_ = len; /* remember; the property */ prop_ = prop; /* not in a list yet */ nxt_ = 0; } /* text of word */ const char *txt_; size_t len_; /* property of the word association */ tctarg_prop_id_t prop_; /* next entry in list */ CTcVocabEntry *nxt_; }; /* * Object superclass list entry */ class CTcObjScEntry { public: CTcObjScEntry(class CTcSymObj *sym) { /* remember the object symbol */ sym_ = sym; /* not in a list yet */ nxt_ = 0; } /* object symbol */ class CTcSymObj *sym_; /* next superclass list entry */ CTcObjScEntry *nxt_; }; /* * Property. A property's ID can be assigned as soon as the property is * first used in an object definition. */ class CTcSymPropBase: public CTcSymbol { public: CTcSymPropBase(const char *str, size_t len, int copy, tctarg_prop_id_t prop) : CTcSymbol(str, len, copy, TC_SYM_PROP) { /* remember our property ID */ prop_ = prop; /* we're not referenced yet */ ref_ = FALSE; /* presume it's not a vocabulary property */ vocab_ = FALSE; /* presume it's not a weak property assumption */ weak_ = FALSE; } /* get the property ID */ tctarg_prop_id_t get_prop() const { return prop_; } /* we can get the address of a property */ class CTcPrsNode *fold_addr_const(); /* we can assign to a property */ virtual int check_lvalue() const { return TRUE; } /* we can take the address of a property */ virtual int has_addr() const { return TRUE; } /* mark the property as referenced */ void mark_referenced() { ref_ = TRUE; } /* get/set the vocabulary-property flag */ int is_vocab() const { return vocab_; } void set_vocab(int flag) { vocab_ = (flag != 0); } /* get/set the "weak" flag */ int is_weak() const { return weak_; } void set_weak(int flag) { weak_ = (flag != 0); } /* read/write symbol file */ static class CTcSymbol *read_from_sym_file(class CVmFile *fp); virtual int write_to_sym_file(class CVmFile *fp); /* write to an object file */ int write_to_obj_file(class CVmFile *fp); /* load from an object file */ static int load_from_obj_file(class CVmFile *fp, const textchar_t *fname, tctarg_prop_id_t *prop_xlat); protected: /* property ID */ tctarg_prop_id_t prop_; /* flag: the property is referenced */ unsigned int ref_ : 1; /* flag: this is a vocabulary property */ unsigned int vocab_ : 1; /* * Flag: this is a "weak" property definition. A weak definition is * one that can be overridden by a conflicting definition for the same * symbol using another type. Symbols used with "&" are assumed to be * properties, but only if we don't later find a conflicting * definition; the weak flag tells us that we can drop the property * assumption without complaint if we do find a different definition * for the symbol later. */ unsigned int weak_: 1; }; /* * Enumerator */ class CTcSymEnumBase: public CTcSymbol { public: CTcSymEnumBase(const char *str, size_t len, int copy, ulong id, int is_token) : CTcSymbol(str, len, copy, TC_SYM_ENUM) { /* remember my ID */ enum_id_ = id; /* remember if it's a 'token' enum */ is_token_ = is_token; /* not yet referenced */ ref_ = FALSE; } /* this can't be an lvalue */ virtual int check_lvalue() const { return FALSE; } /* mark the symbol as referenced */ void mark_referenced() { ref_ = TRUE; } /* an object's value is a compile-time constant */ class CTcPrsNode *fold_constant(); /* get my enumerator ID */ ulong get_enum_id() const { return enum_id_; } /* get/set the 'token' flag */ int is_token() const { return is_token_ != 0; } void set_is_token(int flag) { is_token_ = (flag != 0); } /* write to a symbol file */ virtual int write_to_sym_file(class CVmFile *fp); /* read from a symbol file */ static class CTcSymbol *read_from_sym_file(class CVmFile *fp); /* write to an object file */ int write_to_obj_file(class CVmFile *fp); /* load from an object file */ static int load_from_obj_file(class CVmFile *fp, const textchar_t *fname, ulong *enum_xlat); protected: /* my enumerator ID */ ulong enum_id_; /* flag: this is a 'token' enum */ unsigned int is_token_ : 1; /* referenced? */ unsigned int ref_ : 1; }; /* * local variable/parameter */ class CTcSymLocalBase: public CTcSymbol { public: CTcSymLocalBase(const char *str, size_t len, int copy, int is_param, int var_num); /* get the variable number */ int get_var_num() const { return var_num_; } /* true -> parameter, false -> local variable */ int is_param() const { return is_param_; } /* get/set the parameter position (0 is the leftmost argument) */ int get_param_index() const { return param_index_; } void set_param_index(int n) { param_index_ = n; } /* true -> named parameter */ int is_named_param() const { return is_named_param_; } void set_named_param(int f) { is_named_param_ = f; } /* true -> optional parameter */ int is_opt_param() const { return is_opt_param_; } void set_opt_param(int f) { is_opt_param_ = f; } /* get/set the default value expression */ CTcPrsNode *get_defval_expr() const { return defval_expr_; } int get_defval_seqno() const { return defval_seqno_; } void set_defval_expr(CTcPrsNode *expr, int seqno) { defval_expr_ = expr; defval_seqno_ = seqno; } /* * get/set "list parameter" flag - a list parameter is still a local, * but from the programmer's perspective acts like a parameter because * its role is to contain a list of varargs parameters beyond a certain * point in the argument list */ int is_list_param() const { return is_list_param_; } void set_list_param(int f) { is_list_param_ = f; } /* * true -> "context local" - A context local is a local variable * contained in a scope enclosing an anonymous function. Because * the lifetime of an anonymous function can exceed that of the * activation record that was active when the anonymous function * object was created, every local variable in a scope that is * referenced from within an anonymous function defined within that * scope must be stored in a separate context object, rather than in * the activation record. The context object is created along with * the activation record, but it is not destroyed with the * activation record; instead, a reference to the context object is * stored in the anonymous function, which ensures that the context * object stays reachable as long as the anonymous function object * itself is reachable. */ int is_ctx_local() const { return (is_ctx_local_ != 0); } /* * Establish the variable as a context local, assigning the property * ID of the context object that stores the variable's value, and * the local variable that contains the context object itself. */ void set_ctx_level(int ctx_level) { /* it's a context local now */ is_ctx_local_ = TRUE; /* remember the context level */ ctx_level_ = ctx_level; /* we don't know our context array index yet */ ctx_arr_idx_ = 0; } /* * get/set the original local in the enclosing scope for an * anonymous function's inherited local */ class CTcSymLocal *get_ctx_orig() const { return ctx_orig_; } void set_ctx_orig(class CTcSymLocal *orig) { ctx_orig_ = orig; } /* get my context array index */ int get_ctx_arr_idx() const; /* we can always assign to a local */ virtual int check_lvalue() const { return TRUE; } /* * get/set the 'used' flag - this tracks whether or not the * variable's value was referenced anywhere in the scope in which it * was defined */ int get_val_used() const { return val_used_; } void set_val_used(int f); /* * get/set the 'assigned' flag - this tracks whether or not the * variable has been assigned a value */ int get_val_assigned() const { return val_assigned_; } void set_val_assigned(int f); /* get the source location where the symbol was defined */ class CTcTokFileDesc *get_src_desc() const { return src_desc_; } long get_src_linenum() const { return src_linenum_; } /* get/set reference flag */ int is_referenced() const { return referenced_ != 0; } void mark_referenced() { referenced_ = TRUE; } /* check for references in local scope */ virtual void check_local_references(); /* create a new context copy */ virtual class CTcSymbol *new_ctx_var() const; /* apply context variable conversion */ virtual int apply_ctx_var_conv(class CTcPrsSymtab *symtab, class CTPNCodeBody *code_body); /* finish context variable conversion */ virtual void finish_ctx_var_conv(); /* convert this variable to a context variable */ void convert_to_ctx_var(int val_used, int val_assigned); protected: /* source location where the symbol was defined */ class CTcTokFileDesc *src_desc_; long src_linenum_; /* variable number - stack frame offset for local or parameter */ int var_num_; /* * Parameter index. This is the index within the formal parameter list * of a positional parameter variable. For regular positional * parameters, this is the same as var_num_. However, it's different * for *optional* parameters, because optional parameters are actually * stored in local variables, meaning that var_num_ is a *local* number * rather than an argument number. In order to generate the code that * loads the actual argument value into the local variable, we have to * keep track of both stack locations. This is where we keep track of * the parameter index. */ int param_index_; /* * context variable number - if this is a context variable, this is * the stack frame index of the variable containing the object * containing our value */ int ctx_var_num_; /* * Context level - this is the nesting level of the anonymous * function's context. A level of 0 indicates a local variable in * the current function; a level of 1 indicates a variable in the * enclosing scope; 2 is the next enclosing scope; and so on. */ int ctx_level_; /* * for a context variable, the index of this variable in the local * context array (zero means that it's not assigned) */ int ctx_arr_idx_; /* * for a variable within an anonymous function, the original local * variable symbol from the enclosing scope to which this variable * refers */ class CTcSymLocal *ctx_orig_; /* * For a formal parameter with a default value, the default value * expression, and the sequence number among the default expressions. * We process these expressions at function entry in left-to-right * order for the argument list, to ensure a predicatable order for * dependency resolution and side effects. */ class CTcPrsNode *defval_expr_; int defval_seqno_; /* true -> parameter, false -> local variable */ unsigned int is_param_ : 1; /* true -> named parameter */ unsigned int is_named_param_ : 1; /* true -> optional parametre */ unsigned int is_opt_param_ : 1; /* * true -> not a real parameter, but a "parameter list" local - this is * a local variable into which we deposit a list of the parameters * beyond a certain point, defined via the "[varname]" syntax in a * formal parameter list */ unsigned int is_list_param_ : 1; /* true -> this is a context local variable */ unsigned int is_ctx_local_ : 1; /* flag: the context variable number has been assigned */ unsigned int ctx_var_num_set_ : 1; /* true -> the value was referenced somewhere in its scope */ unsigned int val_used_ : 1; /* true -> the value was assigned */ unsigned int val_assigned_ : 1; /* true -> symbol has been referenced */ unsigned int referenced_ : 1; }; /* * Dynamic local. This is a symbol entry for a local variable accessed * through a StackFrameRef object, for dynamic compilation. */ class CTcSymDynLocalBase: public CTcSymbol { public: CTcSymDynLocalBase(const char *str, size_t len, int copy, tctarg_obj_id_t fref, int varnum, int ctxidx) : CTcSymbol(str, len, copy, TC_SYM_DYNLOCAL) { fref_ = fref; varnum_ = varnum; ctxidx_ = ctxidx; } protected: /* the StackFrameRef object giving the frame containing the variable */ tctarg_obj_id_t fref_; /* the variable number in the frame */ int varnum_; /* the context index (0 if it's an ordinary frame variable) */ int ctxidx_; }; /* * Built-in function */ class CTcSymBifBase: public CTcSymbol { public: CTcSymBifBase(const char *str, size_t len, int copy, ushort func_set_id, ushort func_idx, int has_retval, int min_argc, int max_argc, int varargs) : CTcSymbol(str, len, copy, TC_SYM_BIF) { func_set_id_ = func_set_id; func_idx_ = func_idx; has_retval_ = has_retval; min_argc_ = min_argc; max_argc_ = max_argc; varargs_ = varargs; } /* this can't be an lvalue */ virtual int check_lvalue() const { return FALSE; } /* get the function set ID and index in the function set */ ushort get_func_set_id() const { return func_set_id_; } ushort get_func_idx() const { return func_idx_; } /* * Get the minimum and maximum argument counts. If is_varargs() * returns true, there is no maximum, in which case get_max_argc() * is not meaningful. */ int get_min_argc() const { return min_argc_; } int get_max_argc() const { return max_argc_; } int is_varargs() const { return varargs_; } /* determine if the function has a return value */ int has_retval() const { return has_retval_; } /* * Do not write this to a symbol export file - this symbol is * external to us, hence we have no need to define it for other * modules to import. */ virtual int write_to_sym_file(class CVmFile *) { return FALSE; } /* write to an object file */ virtual int write_to_obj_file(class CVmFile *fp); /* load from an object file */ static int load_from_obj_file(class CVmFile *fp, const textchar_t *fname); protected: /* function set ID */ ushort func_set_id_; /* index within the function set */ ushort func_idx_; /* minimum and maximum argument count, and varargs flag */ int min_argc_; int max_argc_; unsigned int varargs_ : 1; /* true -> function has a return value, false -> void return */ unsigned int has_retval_ : 1; }; /* * External function */ class CTcSymExtfnBase: public CTcSymbol { public: CTcSymExtfnBase(const char *str, size_t len, int copy, int argc, int varargs, int has_retval) : CTcSymbol(str, len, copy, TC_SYM_EXTFN) { /* offset is not yet known */ argc_ = argc; varargs_ = varargs; has_retval_ = has_retval; } /* this can't be an lvalue */ virtual int check_lvalue() const { return FALSE; } /* * Get the argument count. If is_varargs() returns true, this is a * minimum argument count; otherwise, this is the exact number of * arguments required. */ int get_argc() const { return argc_; } int is_varargs() const { return varargs_; } /* determine if the function has a return value */ int has_retval() const { return has_retval_; } protected: /* argument count */ int argc_; /* flag: variable arguments (in which case argc_ is only a minimum) */ unsigned int varargs_ : 1; /* flag: function has a return value (false -> void function) */ unsigned int has_retval_ : 1; }; /* * Code ("goto") label. */ class CTcSymLabelBase: public CTcSymbol { public: CTcSymLabelBase(const char *str, size_t len, int copy) : CTcSymbol(str, len, copy, TC_SYM_LABEL) { /* the symbol is not yet referenced */ referenced_ = FALSE; /* we don't know the defining statement node yet */ stm_ = 0; } /* this can't be an lvalue */ virtual int check_lvalue() const { return FALSE; } /* get/set the defining statement node */ class CTPNStmLabel *get_stm() const { return stm_; } void set_stm(class CTPNStmLabel *stm) { stm_ = stm; } /* add control flow flags to our statement */ void add_control_flow_flags(ulong flags); protected: /* the defining statement's parse node */ class CTPNStmLabel *stm_; /* * flag: the symbol is referenced by at least one statement (we keep * track of this so that we warn when a label is defined but never * used as a target) */ unsigned int referenced_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Program Node. This is the top-level node in the tree; this node * contains the entire program as a sublist. */ class CTPNStmProg: public CTPNStm { public: CTPNStmProg(class CTPNStmTop *first_stm) { /* remember the head of our statement list */ first_stm_ = first_stm; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* * Generate the program code and write to the image file. After * parsing, if parsing is successful, the compiler can invoke this * routine to construct the image file; there is no need to invoke * gen_code() directly. */ void build_image(class CVmFile *image_fp, uchar xor_mask, const char tool_data[4]); /* * Generate the program code and write to the object file. After * parsing, if parsing is successful, the compiler can invoke this * routine to construct the object file. */ void build_object_file(class CVmFile *object_fp, class CTcMake *make_obj); /* generate code for the program */ virtual void gen_code(int discard, int for_condition); protected: /* * Generate code. Returns zero on success, non-zero on error. This * is a common routine used by build_image() and build_object_file() * to complete code generation after parsing. */ int gen_code_for_build(); /* head of our list */ class CTPNStmTop *first_stm_; }; /* ------------------------------------------------------------------------ */ /* * Top-level object - this is a base class for statements which can * appear as top-level definitions, such as functions and objects. * * This class should not need to be derived per target, so further * target-generic base classes are derived from this directly. */ class CTPNStmTop: public CTPNStm { public: CTPNStmTop() { /* we're not in a list yet */ next_stm_top_ = 0; } /* get/set the next top-level statement in the top-level list */ CTPNStmTop *get_next_stm_top() const { return next_stm_top_; } void set_next_stm_top(CTPNStmTop *nxt) { next_stm_top_ = nxt; } /* * Check the local symbol table. This is called after all of the * code has been generated for the entire program, which ensures * that all of the code that could affect any local symbols (even * those shared through context variables) has been finished. By * default we do nothing. */ virtual void check_locals() { } /* get/set the dynamic compilation run-time object ID */ tctarg_obj_id_t get_dyn_obj_id() const { return dyn_obj_id_; } void set_dyn_obj_id(tctarg_obj_id_t id) { dyn_obj_id_ = id; } protected: /* * Dynamic code compilation object ID. When we compile new source code * at run-time, the bytecode is stored in a DynamicFunc instance rather * than in the constant code pool, because the code pool is limited to * code loaded from the image file. Before generating code, the * dynamic compiler assigns a new DynamicFunc ID to each top-level * statement in the nested statement list. The DynamicFunc instances * aren't actually populated until the code is generated, but the * object numbers are assigned in advance to allow cross-references in * generated code. */ tctarg_obj_id_t dyn_obj_id_; /* next top-level statement in the top-level list */ CTPNStmTop *next_stm_top_; }; /* ------------------------------------------------------------------------ */ /* * Anonymous function */ class CTPNAnonFuncBase: public CTcPrsNode { public: CTPNAnonFuncBase(class CTPNCodeBody *code_body, int has_retval, int is_method) { /* remember my code body and return value status */ code_body_ = code_body; has_retval_ = has_retval; is_method_ = is_method; } /* no constant value */ virtual class CTcConstVal *get_const_val() { return 0; } /* I have a return value when called if the underlying code body does */ virtual int has_return_value_on_call() const { return has_retval_; } /* * fold constants - we don't have to fold constants in the * underlying code body, because its constants will be folded along * with other top-level statements */ virtual class CTcPrsNode *fold_constants(class CTcPrsSymtab *) { return this; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *) { return this; } /* mark as replaced/obsolete */ void set_replaced(int flag); protected: /* code body of the function */ class CTPNCodeBody *code_body_; /* true -> the underlying code body has a return value */ unsigned int has_retval_ : 1; /* true -> this is a method; false -> it's a function */ unsigned int is_method_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Code Body. A code body has local variables, goto labels, and a * compound statement. */ class CTPNCodeBodyBase: public CTPNStmTop { public: CTPNCodeBodyBase(class CTcPrsSymtab *lcltab, class CTcPrsSymtab *gototab, class CTPNStm *stm, int argc, int opt_argc, int varargs, int varargs_list, class CTcSymLocal *varargs_list_local, int local_cnt, int self_valid, struct CTcCodeBodyRef *enclosing_code_body); /* * Mark the code as having been replaced - this is used when this is * the code body for a function, and the function is replaced by * another implementation in the same translation unit. When we've * been replaced, we can't be referenced, hence we don't need to * generate any code. */ void set_replaced(int f) { replaced_ = f; } int is_replaced() const { return replaced_; } /* * Set our fixup list anchor - our creator must invoke this if we * are to use a fixup list associated with a symbol table entry. A * code body that can be reached from a symbol table entry (such as * a function name) must have its fixup list anchor associated with * the symbol table entry so that we can create the necessary * associations in an object file. */ void set_symbol_fixup_list(class CTcSymFunc *sym, struct CTcAbsFixup **fixup_list_anchor) { /* remember the information */ fixup_owner_sym_ = sym; fixup_list_anchor_ = fixup_list_anchor; } /* use our internal fixup list */ void use_internal_fixup_list() { /* we have no function symbol */ fixup_owner_sym_ = 0; /* use our internal list anchor */ fixup_list_anchor_ = &internal_fixups_; } /* get my associated function symbol, if there is one */ class CTcSymFunc *get_func_sym() const { return fixup_owner_sym_; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* * Check for unreferenced labels. Scans the 'goto' symbol table and * ensures that each labeled statement has been referenced; logs * a warning for each label that isn't referenced. */ void check_unreferenced_labels(); /* get a pointer to my fixup list head */ struct CTcAbsFixup **get_fixup_list_head() const { return fixup_list_anchor_; } /* set the location of the opening brace */ void set_start_location(class CTcTokFileDesc *desc, long linenum) { start_desc_ = desc; start_linenum_ = linenum; } /* set the location of the closing brace */ void set_end_location(class CTcTokFileDesc *desc, long linenum) { end_desc_ = desc; end_linenum_ = linenum; } /* add an absolute fixup for the symbol at the given stream offset */ void add_abs_fixup(class CTcDataStream *ds, ulong ofs); /* add an absolute fixup for the symbol at the current stream offset */ void add_abs_fixup(class CTcDataStream *ds); /* mark the code body as requiring a local variable context object */ void set_local_ctx(int var_num, int max_local_arr_idx) { /* note that we have a local context */ has_local_ctx_ = TRUE; /* * remember our the local variable number that contains our * context object */ local_ctx_var_ = var_num; /* remember our maximum context local array index */ local_ctx_arr_size_ = max_local_arr_idx; } /* does this function have a local context? */ int has_local_ctx() const { return has_local_ctx_; } /* * Get the context variable for a given recursion level, creating * the new variable if necessary. Level 1 is the first enclosing * scope, 2 is the next enclosing scope, and so on. */ int get_or_add_ctx_var_for_level(int level); /* * find a context variable given a requested level; returns true if * successful, false if not */ int get_ctx_var_for_level(int level, int *varnum); /* get the head of the local context list */ struct CTcCodeBodyCtx *get_ctx_head() const { return ctx_head_; } struct CTcCodeBodyCtx *get_ctx_tail() const { return ctx_tail_; } /* mark this as an anonymous method */ int is_anon_method() const { return is_anon_method_; } void set_anon_method(int f) { is_anon_method_ = f; } /* mark this as a dynamic function or method */ int is_dyn_func() const { return is_dyn_func_; } int is_dyn_method() const { return is_dyn_method_; } void set_dyn_func(int f) { is_dyn_func_ = f; } void set_dyn_method(int f) { is_dyn_method_ = f; } /* get/set 'self' reference status */ int self_referenced() const { return self_referenced_; } void set_self_referenced(int f) { self_referenced_ = f; } /* get/set full method context reference status */ int full_method_ctx_referenced() const { return full_method_ctx_referenced_; } void set_full_method_ctx_referenced(int f) { full_method_ctx_referenced_ = f; } /* get/set the local context need for 'self' */ int local_ctx_needs_self() const { return local_ctx_needs_self_; } void set_local_ctx_needs_self(int f) { local_ctx_needs_self_ = f; } /* get/set the local context need for the full method context */ int local_ctx_needs_full_method_ctx() const { return local_ctx_needs_full_method_ctx_; } void set_local_ctx_needs_full_method_ctx(int f) { local_ctx_needs_full_method_ctx_ = f; } /* get/set the operator overload flag */ int is_operator_overload() const { return op_overload_; } void set_operator_overload(int f) { op_overload_ = f; } /* * Get the base function symbol for a code body defining a modified * function (i.e., 'modify ...'). This is the function to * which 'replaced' refers within this code body and within nested code * bodies. */ class CTcSymFunc *get_replaced_func() const; /* get the immediately enclosing code body */ class CTPNCodeBody *get_enclosing() const; /* get my argument count */ int get_argc() const { return argc_; } int get_opt_argc() const { return opt_argc_; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { stm_ = (CTPNStm *)stm_->adjust_for_dyn(info); return this; } protected: /* enumerator callback for unreferenced label check */ static void unref_label_cb(void *ctx, class CTcSymbol *sym); /* get the outermost enclosing code cody */ class CTPNCodeBody *get_outermost_enclosing() const; /* source location of opening brace */ class CTcTokFileDesc *start_desc_; long start_linenum_; /* source location of the end of the code body (the closing '}') */ class CTcTokFileDesc *end_desc_; long end_linenum_; /* local symbol table for the code body */ class CTcPrsSymtab *lcltab_; /* goto symbol table for the code body */ class CTcPrsSymtab *gototab_; /* statement making up the code body */ class CTPNStm *stm_; /* * fixup list head pointer - this will be set to &internal_fixups_ * if we don't have a symbol table fixup list, or will point to our * symbol table fixup list if we have an external symbol providing * the fixup list */ struct CTcAbsFixup **fixup_list_anchor_; /* our symbol table entry, if our fixup list is defined there */ class CTcSymFunc *fixup_owner_sym_; /* * Our internal list head for our fixup list for references to this * code block. This is used only if we don't have an external fixup * list defined in a symbol table entry. */ struct CTcAbsFixup *internal_fixups_; /* enclosing code body pointer */ struct CTcCodeBodyRef *enclosing_code_body_; /* head/tail of anonymous function context list */ struct CTcCodeBodyCtx *ctx_head_; struct CTcCodeBodyCtx *ctx_tail_; /* number of local variable slots required */ int local_cnt_; /* number of arguments */ int argc_; /* number of additional optional arguments */ int opt_argc_; /* if function has a varargs-list local, this is the local ID */ class CTcSymLocal *varargs_list_local_; /* if function has a local context, this is its local ID */ int local_ctx_var_; /* * number of slots in the local context array, if we have a local * context at all */ int local_ctx_arr_size_; /* * flag: we have a varargs-list local (varargs_list_local_ has the * local variable symbol object) */ unsigned int varargs_list_ : 1; /* flag: function takes variable number of arguments */ unsigned int varargs_ : 1; /* flag: I've been replaced (and hence don't need codegen) */ unsigned int replaced_ : 1; /* * flag: the code body requires a local context - this is set if we * have any local variables that are stored in context objects for * use in anonymous functions */ unsigned int has_local_ctx_ : 1; /* flag: the local context requires 'self' */ unsigned int local_ctx_needs_self_ : 1; /* * flag: the local context requires the full method context (self, * targetprop, targetobj, definingobj) */ unsigned int local_ctx_needs_full_method_ctx_ : 1; /* flag: 'self' is valid in this code body */ unsigned int self_valid_ : 1; /* flags: 'self' and full method context referenced in this code body */ unsigned int self_referenced_ : 1; unsigned int full_method_ctx_referenced_ : 1; /* flag: this is an operator overload method */ unsigned int op_overload_ : 1; /* flag: this is an anonymous method */ unsigned int is_anon_method_ : 1; /* flags: this is a dynamic function/method */ unsigned int is_dyn_func_ : 1; unsigned int is_dyn_method_ : 1; }; /* * Context level information */ struct CTcCodeBodyCtx { CTcCodeBodyCtx() { } /* level number */ int level_; /* local variable containing the context object for this level */ int var_num_; /* next/previous level structure in list */ CTcCodeBodyCtx *nxt_; CTcCodeBodyCtx *prv_; }; /* * Code body reference. * * We don't create a CTPNCodeBody object for a code body we're parsing * until after we finish parsing it, so we can't establish a pointer to an * enclosing code body while we're parsing a nested code body within the * enclosing one. However, we want nested code body objects to be able to * refer to their enclosing code bodies. To deal with this, we create a * "code body reference" object for each code body at the start of parsing * the code body, and then fill it in with the code body object once we * finish parsing; nested code bodies can refer to the reference object * during parsing, so that after parsing they can get to the code body * itself. */ struct CTcCodeBodyRef { CTcCodeBodyRef() { ptr = 0; } /* pointer to enclosing code body */ class CTPNCodeBody *ptr; }; /* ------------------------------------------------------------------------ */ /* * Compound statement. A compound statement simply contains a list of * statements to be executed in sequence; this corresponds to a set of * statements enclosed in braces. A compound statement can have its own * set of local variables, since the enclosing braces constitute a local * scope. */ class CTPNStmCompBase: public CTPNStm { public: CTPNStmCompBase(class CTPNStm *first_stm, class CTcPrsSymtab *symtab); /* get my first statement */ class CTPNStm *get_first_stm() const { return first_stm_; } /* get the local scope's symbol table */ class CTcPrsSymtab *get_symtab() const { return symtab_; } /* set our own-scope flag */ void set_has_own_scope(int f) { has_own_scope_ = f; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* generate code for the compound statement */ virtual void gen_code(int discard, int for_condition); /* evaluate control flow for the statement list */ virtual unsigned long get_control_flow(int warn) const; /* * Determine if this statement has a code label. We have a code * label if our first statement has a code label. */ virtual int has_code_label() const { return (first_stm_ == 0 ? FALSE : first_stm_->has_code_label()); } /* get the location of the closing brace */ class CTcTokFileDesc *get_end_desc() const { return end_desc_; } long get_end_linenum() const { return end_linenum_; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *); protected: /* source location of closing brace */ class CTcTokFileDesc *end_desc_; long end_linenum_; /* first statement in our list of statements */ class CTPNStm *first_stm_; /* * our local symbol table; this is a private symbol table with the * symbols for our local scope if we have any, or is simply the * enclosing scope's symbol table if there are no variables in the * local scope */ class CTcPrsSymtab *symtab_; /* * flag: we have our own private symbol table (it's not the * enclosing scope's symbol table) */ unsigned int has_own_scope_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Null statement - this is the statement that results from an empty * pair of braces, or simply a semicolon. */ class CTPNStmNullBase: public CTPNStm { public: CTPNStmNullBase() { } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *) { return this; } /* generate code for the compound statement */ virtual void gen_code(int, int) { } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *) { return this; } }; /* ------------------------------------------------------------------------ */ /* * Expression statement. */ class CTPNStmExprBase: public CTPNStm { public: CTPNStmExprBase(class CTcPrsNode *expr) { /* remember my expression */ expr_ = expr; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* generate code for the compound statement */ virtual void gen_code(int discard, int for_condition); /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { expr_ = expr_->adjust_for_dyn(info); return this; } protected: /* our expression */ class CTcPrsNode *expr_; }; /* ------------------------------------------------------------------------ */ /* * Static property initializer statement. This is used to hold the * expression of a static initializer, just under the CTPNObjProp * element in the tree. */ class CTPNStmStaticPropInitBase: public CTPNStm { public: CTPNStmStaticPropInitBase(class CTcPrsNode *expr, tctarg_prop_id_t prop) { /* remember my expression and the property to be initialized */ expr_ = expr; prop_ = prop; } /* * fold constants - this is never used, because we always fold the * constants in the underlying expression before we create one of * these nodes, as this type of node is only created during constant * folding of the containing property initializer node */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *) { return this; } protected: /* our expression */ class CTcPrsNode *expr_; /* property we initialize */ tctarg_prop_id_t prop_; }; /* ------------------------------------------------------------------------ */ /* * 'if' statement */ class CTPNStmIfBase: public CTPNStm { public: CTPNStmIfBase(class CTcPrsNode *cond_expr, class CTPNStm *then_part, class CTPNStm *else_part) { /* remember the condition and the two statements */ cond_expr_ = cond_expr; then_part_ = then_part; else_part_ = else_part; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* evaluate control flow for the conditional */ virtual unsigned long get_control_flow(int warn) const; /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { if (then_part_ != 0) then_part_ = (CTPNStm *)then_part_->adjust_for_dyn(info); if (else_part_ != 0) else_part_ = (CTPNStm *)else_part_->adjust_for_dyn(info); return this; } protected: /* condition expression */ class CTcPrsNode *cond_expr_; /* 'then' and 'else' substatements */ class CTPNStm *then_part_; class CTPNStm *else_part_; }; /* ------------------------------------------------------------------------ */ /* * 'for' statement */ class CTPNStmForBase: public CTPNStmEnclosing { public: CTPNStmForBase(class CTcPrsNode *init_expr, class CTcPrsNode *cond_expr, class CTcPrsNode *reinit_expr, class CTPNForIn *in_exprs, class CTcPrsSymtab *symtab, class CTPNStmEnclosing *enclosing_stm) : CTPNStmEnclosing(enclosing_stm) { /* remember the statement parts */ init_expr_ = init_expr; cond_expr_ = cond_expr; reinit_expr_ = reinit_expr; in_exprs_ = in_exprs; symtab_ = symtab; /* no body yet */ body_stm_ = 0; /* * presume we use our parent's symbol table, not our own private * one */ has_own_scope_ = FALSE; } /* set the body */ void set_body(class CTPNStm *body) { body_stm_ = body; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* evaluate control flow for the loop */ virtual unsigned long get_control_flow(int warn) const; /* set our own-scope flag */ void set_has_own_scope(int f) { has_own_scope_ = f; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { if (init_expr_ != 0) init_expr_ = init_expr_->adjust_for_dyn(info); if (cond_expr_ != 0) cond_expr_ = cond_expr_->adjust_for_dyn(info); if (reinit_expr_ != 0) reinit_expr_ = reinit_expr_->adjust_for_dyn(info); if (body_stm_ != 0) body_stm_ = (CTPNStm *)body_stm_->adjust_for_dyn(info); return this; } protected: /* initialization expression */ class CTcPrsNode *init_expr_; /* loop condition expression */ class CTcPrsNode *cond_expr_; /* reinitialization expression */ class CTcPrsNode *reinit_expr_; /* * Head of list of "in" expressions (lval in collection, lval in * from..to). These appear as ordinary expressions within the * init_expr_ comma list, but we track them separately here, since they * implicitly generate code in the condition and reinit phases as well. */ class CTPNForIn *in_exprs_; /* body of the loop */ class CTPNStm *body_stm_; /* our local scope symbol table */ class CTcPrsSymtab *symtab_; /* flag: we have our own private symbol table (not our parent's) */ unsigned int has_own_scope_ : 1; }; /* ------------------------------------------------------------------------ */ /* * ' in ' node, for 'for' statements. Since 3.1, * regular 'for' statements can include 'in' clauses mixed in with other * initializer expressions. */ class CTPNVarInBase: public CTPNForIn { public: CTPNVarInBase(class CTcPrsNode *lval, class CTcPrsNode *expr, int iter_local_id) { lval_ = lval; expr_ = expr; iter_local_id_ = iter_local_id; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { lval_ = lval_->adjust_for_dyn(info); expr_ = expr_->adjust_for_dyn(info); return this; } protected: /* the lvalue for the control variable */ class CTcPrsNode *lval_; /* the collection expression */ class CTcPrsNode *expr_; /* private local variable for the iterator */ int iter_local_id_; }; /* * ' in .. ' node, for 'for' statements. Since 3.1, * 'for' statements can include 'in' clauses with range expressions using * '..'. */ class CTPNVarInRangeBase: public CTPNForIn { public: CTPNVarInRangeBase(class CTcPrsNode *lval, class CTcPrsNode *from_expr, class CTcPrsNode *to_expr, class CTcPrsNode *step_expr, int to_local_id, int step_local_id) { lval_ = lval; from_expr_ = from_expr; to_expr_ = to_expr; step_expr_ = step_expr; to_local_id_ = to_local_id; step_local_id_ = step_local_id; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { lval_ = lval_->adjust_for_dyn(info); from_expr_ = from_expr_->adjust_for_dyn(info); to_expr_ = to_expr_->adjust_for_dyn(info); if (step_expr_ != 0) step_expr_ = step_expr_->adjust_for_dyn(info); return this; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); protected: /* the lvalue for the control variable */ class CTcPrsNode *lval_; /* the 'from' and 'to' expressions */ class CTcPrsNode *from_expr_; class CTcPrsNode *to_expr_; /* the optional 'step' expression */ class CTcPrsNode *step_expr_; /* local variable number for the evaluated "to" expression */ int to_local_id_; /* * local variable number for the evaluated "step" expression - this is * used only if we have a non-constant step expression */ int step_local_id_; }; /* ------------------------------------------------------------------------ */ /* * 'foreach' statement */ class CTPNStmForeachBase: public CTPNStmEnclosing { public: CTPNStmForeachBase(class CTcPrsNode *iter_expr, class CTcPrsNode *coll_expr, class CTcPrsSymtab *symtab, class CTPNStmEnclosing *enclosing_stm, int iter_local_id) : CTPNStmEnclosing(enclosing_stm) { /* remember the statement parts */ iter_expr_ = iter_expr; coll_expr_ = coll_expr; symtab_ = symtab; /* no body yet */ body_stm_ = 0; /* remember my private iterator local */ iter_local_id_ = iter_local_id; /* * presume we use our parent's symbol table, not our own private * one */ has_own_scope_ = FALSE; } /* set the body */ void set_body(class CTPNStm *body) { body_stm_ = body; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* evaluate control flow for the loop */ virtual unsigned long get_control_flow(int warn) const; /* set our own-scope flag */ void set_has_own_scope(int f) { has_own_scope_ = f; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { iter_expr_ = iter_expr_->adjust_for_dyn(info); coll_expr_ = coll_expr_->adjust_for_dyn(info); body_stm_ = (CTPNStm *)body_stm_->adjust_for_dyn(info); return this; } protected: /* iteration lvalue expression */ class CTcPrsNode *iter_expr_; /* collection expression */ class CTcPrsNode *coll_expr_; /* body of the loop */ class CTPNStm *body_stm_; /* our local scope symbol table */ class CTcPrsSymtab *symtab_; /* ID of secret local variable containing the iterator object */ int iter_local_id_; /* flag: we have our own private symbol table (not our parent's) */ unsigned int has_own_scope_ : 1; }; /* ------------------------------------------------------------------------ */ /* * 'while' statement - this is an enclosing statement, because it can * interact with 'try' blocks with 'break' and 'continue' statements * used within the body of the loop */ class CTPNStmWhileBase: public CTPNStmEnclosing { public: CTPNStmWhileBase(class CTcPrsNode *cond_expr, class CTPNStmEnclosing *enclosing_stm) : CTPNStmEnclosing(enclosing_stm) { /* remember the statement parts */ cond_expr_ = cond_expr; /* no body yet */ body_stm_ = 0; } /* set the body */ void set_body(class CTPNStm *body) { body_stm_ = body; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* evaluate control flow for the loop */ virtual unsigned long get_control_flow(int warn) const; /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { cond_expr_ = cond_expr_->adjust_for_dyn(info); body_stm_ = (CTPNStm *)body_stm_->adjust_for_dyn(info); return this; } protected: /* loop condition expression */ class CTcPrsNode *cond_expr_; /* body of the loop */ class CTPNStm *body_stm_; }; /* ------------------------------------------------------------------------ */ /* * 'do-while' statement */ class CTPNStmDoWhileBase: public CTPNStmEnclosing { public: CTPNStmDoWhileBase(class CTPNStmEnclosing *enclosing_stm) : CTPNStmEnclosing(enclosing_stm) { /* no condition or body yet */ cond_expr_ = 0; body_stm_ = 0; /* * we don't know the location of the 'while' part yet, so assume * it's at the same location as the 'do' for now */ while_desc_ = file_; while_linenum_ = linenum_; } /* set the condition */ void set_cond(class CTcPrsNode *cond) { cond_expr_ = cond; } /* set the body */ void set_body(class CTPNStm *body) { body_stm_ = body; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* evaluate control flow for the loop */ virtual unsigned long get_control_flow(int warn) const; /* set the source location of the 'while' statement */ void set_while_pos(class CTcTokFileDesc *desc, long linenum) { while_desc_ = desc; while_linenum_ = linenum; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { cond_expr_ = cond_expr_->adjust_for_dyn(info); body_stm_ = (CTPNStm *)body_stm_->adjust_for_dyn(info); return this; } protected: /* source location of 'while' */ class CTcTokFileDesc *while_desc_; long while_linenum_; /* loop condition expression */ class CTcPrsNode *cond_expr_; /* body of the loop */ class CTPNStm *body_stm_; }; /* ------------------------------------------------------------------------ */ /* * 'break' statement */ class CTPNStmBreakBase: public CTPNStm { public: CTPNStmBreakBase() { /* no label yet */ lbl_ = 0; lbl_len_ = 0; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *) { /* we have nothing to fold - return unchangdd */ return this; } /* evaluate control flow for the statement */ virtual unsigned long get_control_flow(int warn) const; /* set my label */ void set_label(const class CTcToken *tok); /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { return this; } protected: /* my break-to label, if specified */ const textchar_t *lbl_; size_t lbl_len_; }; /* ------------------------------------------------------------------------ */ /* * 'continue' statement */ class CTPNStmContinueBase: public CTPNStm { public: CTPNStmContinueBase() { /* no label yet */ lbl_ = 0; lbl_len_ = 0; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *) { /* we have nothing to fold - return unchangdd */ return this; } /* evaluate control flow for the statement */ virtual unsigned long get_control_flow(int warn) const; /* set my label */ void set_label(const class CTcToken *tok); /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { return this; } protected: /* my break-to label, if specified */ const textchar_t *lbl_; size_t lbl_len_; }; /* ------------------------------------------------------------------------ */ /* * 'return' statement; if the expression node is null, this is a simple * void return */ class CTPNStmReturnBase: public CTPNStm { public: CTPNStmReturnBase(class CTcPrsNode *expr) { /* remember the expression */ expr_ = expr; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* evaluate control flow for the statement */ virtual unsigned long get_control_flow(int /*warn*/) const { /* * we return a value if we have an expression, otherwise we're * just a void return */ return (expr_ == 0 ? TCPRS_FLOW_RET_VOID : TCPRS_FLOW_RET_VAL); } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { expr_ = expr_->adjust_for_dyn(info); return this; } protected: /* the expression to return, or null for a void return */ class CTcPrsNode *expr_; }; /* ------------------------------------------------------------------------ */ /* * 'switch' statement. This is an 'enclosing' statement type because * 'break' can refer to the statement from within the code body. */ class CTPNStmSwitchBase: public CTPNStmEnclosing { public: CTPNStmSwitchBase(class CTPNStmEnclosing *enclosing_stm) : CTPNStmEnclosing(enclosing_stm) { /* we have no body or expression yet */ expr_ = 0; body_ = 0; /* we have no case or default labels yet */ case_cnt_ = 0; has_default_ = FALSE; } /* set my expression */ void set_expr(class CTcPrsNode *expr) { expr_ = expr; } /* set my body */ void set_body(class CTPNStm *body) { body_ = body; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* evaluate control flow for the statement */ virtual unsigned long get_control_flow(int warn) const; /* get the number of 'case' labels */ int get_case_cnt() const { return case_cnt_; } /* determine if there's a 'default' label */ int get_has_default() const { return has_default_ != 0; } /* increment the number of case labels */ void inc_case_cnt() { ++case_cnt_; } /* mark that the body has a 'default' label */ void set_has_default() { has_default_ = TRUE; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { expr_ = expr_->adjust_for_dyn(info); body_ = (CTPNStm *)body_->adjust_for_dyn(info); return this; } protected: /* the controlling expression */ class CTcPrsNode *expr_; /* body of the switch */ class CTPNStm *body_; /* number of case labels */ int case_cnt_; /* flag: we have a 'default' label */ unsigned int has_default_ : 1; }; /* ------------------------------------------------------------------------ */ /* * code label statement - a code label contains the statement that it * labels. This is an enclosing statement type because targeting a * label with a 'goto', 'break', or 'continue' requires that we be able * to find intermediate 'try' blocks. */ class CTPNStmLabelBase: public CTPNStmEnclosing { public: CTPNStmLabelBase(class CTcSymLabel *lbl, CTPNStmEnclosing *enclosing) : CTPNStmEnclosing(enclosing) { /* remember my label */ lbl_ = lbl; /* we have no enclosed statement yet */ stm_ = 0; /* we're not targeted with any 'break' or 'continue' statements yet */ control_flow_flags_ = 0; } /* set my enclosed statement */ void set_stm(class CTPNStm *stm) { stm_ = stm; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* evaluate control flow for the statement */ virtual unsigned long get_control_flow(int warn) const; /* I am a code label, thus I have a code label */ virtual int has_code_label() const { return TRUE; } /* * add explicit targeting flags (break, continue, goto) to my * statement */ void add_control_flow_flags(ulong flags); /* get my explicit control flow flag */ ulong get_explicit_control_flow_flags() const { return control_flow_flags_; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { stm_ = (CTPNStm *)adjust_for_dyn(info); return this; } protected: /* my label */ class CTcSymLabel *lbl_; /* my enclosed statement */ class CTPNStm *stm_; /* * My explicit control flow flags. Any time this label appears in a * 'break' or 'continue' statement enclosed within the labeled * statement, we'll set the appropriate flags here. When charting * the control flow through the statement, we will take these into * account after charting the flow through our body. */ ulong control_flow_flags_; }; /* ------------------------------------------------------------------------ */ /* * 'goto' statement */ class CTPNStmGotoBase: public CTPNStm { public: CTPNStmGotoBase(const textchar_t *lbl, size_t lbl_len) { /* * remember the label - we can store a reference to it without * copying, because the tokenizer keeps symbol token text around * throughout compilation */ lbl_ = lbl; lbl_len_ = lbl_len; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *) { /* 'goto' statements are unaffected by constant folding */ return this; } /* evaluate control flow for the statement */ virtual unsigned long get_control_flow(int warn) const; /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { return this; } protected: /* our code label text */ const textchar_t *lbl_; size_t lbl_len_; }; /* ------------------------------------------------------------------------ */ /* * 'case' label statement */ class CTPNStmCaseBase: public CTPNStm { public: CTPNStmCaseBase() { /* we have no statement or expression yet */ expr_ = 0; stm_ = 0; } /* set my expression */ void set_expr(class CTcPrsNode *expr) { expr_ = expr; } /* set my enclosed statement */ void set_stm(class CTPNStm *stm) { stm_ = stm; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* evaluate control flow for the statement */ virtual unsigned long get_control_flow(int warn) const; /* I am a code label, thus I have a code label */ virtual int has_code_label() const { return TRUE; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { expr_ = expr_->adjust_for_dyn(info); stm_ = (CTPNStm *)stm_->adjust_for_dyn(info); return this; } protected: /* my expression */ class CTcPrsNode *expr_; /* my enclosed statement */ class CTPNStm *stm_; }; /* ------------------------------------------------------------------------ */ /* * 'default' label statement */ class CTPNStmDefaultBase: public CTPNStm { public: CTPNStmDefaultBase() { /* we have no statement yet */ stm_ = 0; } /* set my enclosed statement */ void set_stm(class CTPNStm *stm) { stm_ = stm; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* evaluate control flow for the statement */ virtual unsigned long get_control_flow(int warn) const; /* I am a code label, thus I have a code label */ virtual int has_code_label() const { return TRUE; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { return this; } protected: /* my enclosed statement */ class CTPNStm *stm_; }; /* ------------------------------------------------------------------------ */ /* * 'try' statement */ class CTPNStmTryBase: public CTPNStmEnclosing { public: CTPNStmTryBase(CTPNStmEnclosing *enclosing) : CTPNStmEnclosing(enclosing) { /* we have no protected body yet */ body_stm_ = 0; /* we have no 'catch' or 'finally' clause yet */ first_catch_stm_ = last_catch_stm_ = 0; finally_stm_ = 0; } /* set my protected statement */ void set_body_stm(class CTPNStm *body_stm) { body_stm_ = body_stm; } /* add a catch to my list */ void add_catch(class CTPNStmCatch *catch_stm); /* set the 'finally' clause */ void set_finally(class CTPNStmFinally *finally_stm) { finally_stm_ = finally_stm; } /* determine if I have any 'catch' or 'finally' clauses */ int has_catch_or_finally() const { return (first_catch_stm_ != 0 || finally_stm_ != 0); } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* evaluate control flow for the statement list */ virtual unsigned long get_control_flow(int warn) const; /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info); protected: /* my enclosed statement */ class CTPNStm *body_stm_; /* head and tail of my 'catch' clause list */ class CTPNStmCatch *first_catch_stm_; class CTPNStmCatch *last_catch_stm_; /* my 'finally' clause, if any */ class CTPNStmFinally *finally_stm_; }; /* ------------------------------------------------------------------------ */ /* * 'catch' clause */ class CTPNStmCatchBase: public CTPNStm { public: CTPNStmCatchBase() { /* we have no statement yet */ body_ = 0; /* we don't know our exception class yet */ exc_class_ = 0; exc_class_len_ = 0; /* we don't know our exception variable yet */ exc_var_ = 0; /* we're not in a list yet */ next_catch_ = 0; /* we don't know our local symbol table yet */ symtab_ = 0; } /* set my enclosed statement */ void set_body(class CTPNStm *body) { body_ = body; } /* set my exception class name */ void set_exc_class(const class CTcToken *tok); /* set my exception variable */ void set_exc_var(class CTcSymLocal *var) { exc_var_ = var; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* evaluate control flow for the clause */ virtual unsigned long get_control_flow(int warn) const; /* get/set the next 'catch' clause in our list */ CTPNStmCatch *get_next_catch() const { return next_catch_; } void set_next_catch(CTPNStmCatch *nxt) { next_catch_ = nxt; } /* set my local-scope symbol table */ void set_symtab(class CTcPrsSymtab *symtab) { symtab_ = symtab; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { body_ = (CTPNStm *)body_->adjust_for_dyn(info); return this; } protected: /* the body of the 'catch' block */ class CTPNStm *body_; /* exception name */ const textchar_t *exc_class_; size_t exc_class_len_; /* exception variable (formal parameter to 'catch' block) */ class CTcSymLocal *exc_var_; /* next 'catch' clause in our list */ CTPNStmCatch *next_catch_; /* my local-scope symbol table (for my formal parameter) */ class CTcPrsSymtab *symtab_; }; /* ------------------------------------------------------------------------ */ /* * 'finally' clause */ class CTPNStmFinallyBase: public CTPNStmEnclosing { public: CTPNStmFinallyBase(CTPNStmEnclosing *enclosing, int exc_local_id, int jsr_local_id) : CTPNStmEnclosing(enclosing) { /* we have no body yet */ body_ = 0; /* remember our local variable ID's */ exc_local_id_ = exc_local_id; jsr_local_id_ = jsr_local_id; /* use my starting position as the ending position for now */ end_desc_ = file_; end_linenum_ = linenum_; } /* set my body */ void set_body(class CTPNStm *stm) { body_ = stm; } /* * set my ending source position (i.e., the position of the closing * brace of the block of code contained within the 'finally' block */ void set_end_pos(class CTcTokFileDesc *desc, long linenum) { end_desc_ = desc; end_linenum_ = linenum; } /* get the ending source position information */ class CTcTokFileDesc *get_end_desc() const { return end_desc_; } long get_end_linenum() const { return end_linenum_; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* evaluate control flow for the clause */ virtual unsigned long get_control_flow(int warn) const; /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { body_ = (CTPNStm *)body_->adjust_for_dyn(info); return this; } protected: /* my body */ class CTPNStm *body_; /* * our exception local ID - this is an unnamed local variable that * we use to store our exception parameter temporarily while we run * the 'finally' block */ int exc_local_id_; /* * our subroutine return address local ID - this is another unnamed * local that we use to store our subroutine return address */ int jsr_local_id_; /* * source position of end of the 'finally' clause - we keep track of * this location because code generated after the return from * calling the 'finally' clause is most sensibly thought of as being * at this location */ class CTcTokFileDesc *end_desc_; long end_linenum_; }; /* ------------------------------------------------------------------------ */ /* * 'throw' statement */ class CTPNStmThrowBase: public CTPNStm { public: CTPNStmThrowBase(class CTcPrsNode *expr) { /* remember the expression to be thrown */ expr_ = expr; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* evaluate control flow for the statement */ virtual unsigned long get_control_flow(int /*warn*/) const { /* this statement always exits by 'throw' (obviously) */ return TCPRS_FLOW_THROW; } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { expr_ = expr_->adjust_for_dyn(info); return this; } protected: /* expression to throw */ class CTcPrsNode *expr_; }; /* ------------------------------------------------------------------------ */ /* * 'dictionary' statement */ class CTPNStmDictBase: public CTPNStmTop { public: CTPNStmDictBase(class CTcDictEntry *dict) { /* remember the dictionary entry */ dict_ = dict; } /* fold constants - this has no effect on us */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *) { return this; } protected: /* the underlying dictionary entry */ class CTcDictEntry *dict_; }; /* ------------------------------------------------------------------------ */ /* * Parse node for object superclass list entries */ class CTPNSuperclass: public CTcPrsNode { public: CTPNSuperclass(const textchar_t *sym, size_t len) { /* remember my name */ sym_txt_ = sym; sym_len_ = len; /* we have a name instead of a symbol object */ sym_ = 0; /* I'm not in a list yet */ nxt_ = prv_ = 0; } CTPNSuperclass(class CTcSymbol *sym) { /* remember my symbol */ sym_ = sym; /* we have the symbol, so we don't need to store the name */ sym_txt_ = 0; sym_len_ = 0; /* I'm not in a list yet */ nxt_ = 0; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) { return this; } /* generate code for the compound statement */ virtual void gen_code(int discard, int for_condition) { } /* get my symbol */ class CTcSymbol *get_sym() const; /* get my text - valid only if we are storing text, not a symbol */ const char *get_sym_txt() const { return sym_txt_; } size_t get_sym_len() const { return sym_len_; } /* am I a subclass of the given class? */ int is_subclass_of(const CTPNSuperclass *sc) const; /* next/previous entry in my list */ CTPNSuperclass *nxt_, *prv_; protected: /* my symbol - if this isn't set, we'll use sym_txt_ instead */ class CTcSymbol *sym_; /* my name - we use this if sym_ is not set */ const textchar_t *sym_txt_; size_t sym_len_; }; /* * Superclass list */ class CTPNSuperclassList { public: CTPNSuperclassList() { head_ = tail_ = 0; dst_ = &head_; } void append(class CTPNSuperclass *sc) { /* set the 'next' pointer for the current tail */ *dst_ = sc; *(dst_ = &sc->nxt_) = 0; /* set the 'previous' pointer for the new entry */ sc->prv_ = tail_; tail_ = sc; } /* head and tail of our list */ class CTPNSuperclass *head_; class CTPNSuperclass *tail_; private: class CTPNSuperclass **dst_; }; /* * Property list */ class CTPNPropList { public: CTPNPropList() { first_ = last_ = 0; dst_ = &first_; cnt_ = 0; } /* append a property to our list */ void append(class CTPNObjProp *prop); /* delete a property from our list; returns true if found, false if not */ int del(class CTcSymProp *sym); /* head/tail of our property list */ class CTPNObjProp *first_; class CTPNObjProp *last_; /* number of properties in the list */ int cnt_; private: /* next destination for append() */ class CTPNObjProp **dst_; }; /* ------------------------------------------------------------------------ */ /* * Mix-in base for an object definer node. This is instantiated in the * top-level object statement class (CTPNStmObject) and the in-line object * definition expression node (CTPNInlineObject). */ class CTPNObjDef { public: CTPNObjDef() { /* presume there's no object symbol */ obj_sym_ = 0; /* presume we won't have an undescribed superclass */ undesc_sc_ = FALSE; /* presume we won't have a template usage error */ bad_template_ = FALSE; } /* is this a regular object or an inline object? */ virtual int is_inline_object() const = 0; /* set the 'undescribed class' flag */ int has_undesc_sc() const { return undesc_sc_; } void set_undesc_sc(int f) { undesc_sc_ = f; } /* get/set the 'bad template' flag */ int has_bad_template() const { return bad_template_; } void note_bad_template(int f) { bad_template_ = f; } /* add a property value */ class CTPNObjProp *add_prop( class CTcSymProp *prop_sym, class CTcPrsNode *expr, int replace, int is_static); /* * Add a method. 'expr' is the original expression for cases where the * code body is derived from a simple expression in the source code * rather than an explicit code block. Keeping the original expression * allows us to use a simple constant value if the expression ends up * folding to a constant. */ class CTPNObjProp *add_method( class CTcSymProp *prop_sym, class CTPNCodeBody *code_body, class CTcPrsNode *expr, int replace); /* * Add a method to an inline object. The method in this case is * represented as an anonymous method object. 'expr' is the original * expression for cases where the anonymous function is derived from a * simple expression in the source code rather than an explicit code * block. */ class CTPNObjProp *add_inline_method( class CTcSymProp *prop_sym, class CTPNAnonFunc *inline_method, class CTcPrsNode *expr, int replace); /* add a property as a nested object value */ virtual int parse_nested_obj_prop( class CTPNObjProp* &new_prop, int *err, struct tcprs_term_info *term_info, const class CTcToken *prop_tok, int replace) = 0; /* delete a property */ virtual void delete_property(class CTcSymProp *prop_sym) { proplist_.del(prop_sym); } /* get my superclass list */ CTPNSuperclassList &get_superclass_list() { return sclist_; } /* get my first superclass */ class CTPNSuperclass *get_first_sc() const { return sclist_.head_; } /* get my first property */ class CTPNObjProp *get_first_prop() const { return proplist_.first_; } /* get the object symbol */ class CTcSymObj *get_obj_sym() const { return obj_sym_; } /* * set my object symbol - when an object is modified (via the 'modify' * statement), the object tree can be moved to a new symbol */ void set_obj_sym(class CTcSymObj *obj_sym) { obj_sym_ = obj_sym; } /* fold constants in the property list */ void fold_proplist(class CTcPrsSymtab *symtab); protected: /* add an entry to my property list */ virtual void add_prop_entry(class CTPNObjProp *prop, int /*replace*/) { proplist_.append(prop); } /* object name symbol */ class CTcSymObj *obj_sym_; /* property list */ CTPNPropList proplist_; /* superclass list */ CTPNSuperclassList sclist_; /* * Flag: this object definition includes an undescribed superclass. * This indicates that we're based on a class that was explicitly * defined as 'extern', in which case it can't be used as the source of * a template, since we know nothing about the class other than that it * is indeed a class. */ unsigned int undesc_sc_ : 1; /* flag: this object definition used a template that wasn't matched */ unsigned int bad_template_ : 1; }; /* ------------------------------------------------------------------------ */ /* * object definition statement base class */ class CTPNStmObjectBase: public CTPNStmTop, public CTPNObjDef { public: CTPNStmObjectBase(class CTcSymObj *obj_sym, int is_class) { /* remember our defining global symbol */ obj_sym_ = obj_sym; /* we're not yet replaced by another object */ replaced_ = FALSE; /* we're not yet modified */ modified_ = FALSE; /* note whether I'm a class or an ordinary object instance */ is_class_ = is_class; /* presume it's not transient */ transient_ = FALSE; } /* this is a regular top-level object definition */ virtual int is_inline_object() const { return FALSE; } /* * mark the object as replaced - this indicates that another object * in the same translation unit has replaced this object, hence we * should not generate any code for this object */ void set_replaced(int f) { replaced_ = f; } /* * Mark the object as modified - this indicates that another object * in the same translation unit has modified this object. We'll * store this information in the object stream header data for use * at link time. */ void set_modified(int f) { modified_ = f; } /* add a superclass with the given name or symbol */ void add_superclass(const class CTcToken *tok); void add_superclass(class CTcSymbol *sym); /* determine if I'm a class object */ int is_class() const { return (is_class_ != 0); } /* * Delete a property value. This is used when a 'modify' object * defines a property with 'replace', so that the property defined in * the modified original object is removed entirely rather than left in * as an inherited property. */ virtual void delete_property(class CTcSymProp *prop_sym); /* * Add an implicit constructor. This should be called just before * code generation; we'll check to see if the object requires an * implicit constructor, and add one to the object if so. An object * provides an implicit constructor if it has multiple superclasses * and no explicit constructor. */ void add_implicit_constructor(); /* get/set the 'transient' status */ int is_transient() const { return transient_; } void set_transient() { transient_ = TRUE; } /* add a property as a nested object value */ virtual int parse_nested_obj_prop( class CTPNObjProp* &new_prop, int *err, struct tcprs_term_info *term_info, const class CTcToken *prop_tok, int replace); /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) { CTPNObjDef::fold_proplist(symtab); return this; } protected: /* add an entry to my property list */ virtual void add_prop_entry(class CTPNObjProp *prop, int replace); /* flag: I'm a class */ unsigned int is_class_ : 1; /* flag: I've been replaced by another object */ unsigned int replaced_ : 1; /* flag: I've been modified by another object */ unsigned int modified_ : 1; /* flag: the object is transient */ unsigned int transient_ : 1; }; /* ------------------------------------------------------------------------ */ /* * In-line object definition. This represents an object defined in-line as * part of an expression. This type of object definition creates a new * instance of the specified object when the expression is evaluated, using * anonymous methods for code properties. (Anonymous methods are the same * as anonymous functions, aka closures, except that they bind only to the * local variables in the lexically enclosing frame but not to the 'self' * context.) Using anonymous methods allows methods defined in the object * to read and write the lexically enclosing frame's local variables, so * that the entire object acts like a closure. */ class CTPNInlineObjectBase: public CTcPrsNode, public CTPNObjDef { public: /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) { CTPNObjDef::fold_proplist(symtab); return this; } /* this is an inline object definition */ virtual int is_inline_object() const { return TRUE; } /* parse a nested object property */ virtual int parse_nested_obj_prop( class CTPNObjProp* &new_prop, int *err, struct tcprs_term_info *term_info, const class CTcToken *prop_tok, int replace); /* adjust for dynamic execution */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info); }; /* ------------------------------------------------------------------------ */ /* * Parse node entry for a property value or method entry in an object * definition statement's property list. We can have either an * expression entry or a code body entry; we keep open the possibility * of either one in this single class (rather than subclassing for the * two possibilities) because we might have to transform from an * expression to a code body if we find a non-constant expression, which * we can't determine until constant-folding time. */ class CTPNObjPropBase: public CTPNStm { friend class CTPNObjDef; friend class CTPNStmObjectBase; friend class CTPNStmObject; friend class CTPNPropList; public: CTPNObjPropBase(class CTPNObjDef *objdef, class CTcSymProp *prop_sym, class CTcPrsNode *expr, class CTPNCodeBody *code_body, class CTPNAnonFunc *inline_method, int is_static) { /* remember the object and property information */ objdef_ = objdef; prop_sym_ = prop_sym; /* not in a property list yet */ nxt_ = 0; /* remember our expression and code body values */ expr_ = expr; code_body_ = code_body; inline_method_ = inline_method; /* remember if it's static */ is_static_ = is_static; /* by default, properties cannot be overwritten with redefinitions */ is_overwritable_ = FALSE; } /* get my property symbol */ class CTcSymProp *get_prop_sym() const { return prop_sym_; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab); /* adjust for dynamic execution */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info); /* get the next property of this object */ class CTPNObjProp *get_next_prop() const { return nxt_; } /* am I a constructor? */ int is_constructor() const; /* * Set/get flag indicating that we're overwritable. An overwritable * property can be superseded with a duplicate definition of the same * property in the same object. By default, it's an error to define * the same property more than once in the same object, but properties * defined with certain implicit notation can be overwritten with an * explicit redefinition. Specifically, a 'location' property added * with the '+' notation can be overwritten, and the automatic * 'sourceTextOrder' and 'sourceTextGroup' properties can be * overwritten. */ int is_overwritable() const { return is_overwritable_; } void set_overwritable() { is_overwritable_ = TRUE; } protected: /* my object statement */ class CTPNObjDef *objdef_; /* my property symbol */ class CTcSymProp *prop_sym_; /* next property in my list */ class CTPNObjProp *nxt_; /* my value expression */ class CTcPrsNode *expr_; /* symbol table in effect for this expression */ class CTcPrsSymtab *symtab_; /* my code body node */ class CTPNCodeBody *code_body_; /* for in-line objects, code is represented as an anonymous method */ class CTPNAnonFunc *inline_method_; /* am I static? */ unsigned int is_static_ : 1; /* am I overwritable? */ unsigned int is_overwritable_ : 1; }; /* * Deleted property object. This is used for an entry in a deleted * property list for a 'modify' object. */ class CTcObjPropDel: public CTcPrsAllocObj { public: CTcObjPropDel(class CTcSymProp *prop_sym) { /* save the property information */ prop_sym_ = prop_sym; /* not in a list yet */ nxt_ = 0; } /* the property to delete */ class CTcSymProp *prop_sym_; /* next in list */ CTcObjPropDel *nxt_; }; /* * Implicit constructor statement. */ class CTPNStmImplicitCtorBase: public CTPNStm { public: CTPNStmImplicitCtorBase(class CTPNStmObject *obj_stm) { /* remember my object statement */ obj_stm_ = obj_stm; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *) { return this; } protected: /* my object statement */ class CTPNStmObject *obj_stm_; }; #endif /* TCPNDRV_H */ qtads-2.1.7/tads3/tcpnint.h000066400000000000000000000315321265017072300155120ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/TCPNINT.H,v 1.3 1999/07/11 00:46:53 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcpnint.h - intermediate derived parse node classes Function Notes Modified 05/12/99 MJRoberts - Creation */ #ifndef TCPNINT_H #define TCPNINT_H /* ------------------------------------------------------------------------ */ /* * Generic unary parse node. Most unary node types don't differ at the * parser level, so target node types can be subclassed directly off of * this generic unary node class. */ class CTPNUnaryBase: public CTcPrsNode { public: CTPNUnaryBase(class CTcPrsNode *sub) { sub_ = sub; } /* get the subexpression */ class CTcPrsNode *get_sub_expr() const { return sub_; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) { class CTcPrsNode *ret; /* fold constants on my subexpression */ sub_ = sub_->fold_constants(symtab); /* try folding the unary operator */ ret = fold_unop(); /* * if we folded the operator, return the result; otherwise, * return the original operator tree for full code generation */ return (ret != 0 ? ret : this); } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { /* * if this expression has side effects, don't allow it to be * evaluated speculatively */ if (info->speculative && has_side_effects()) err_throw(VMERR_BAD_SPEC_EVAL); /* adjust my subexpression */ sub_ = sub_->adjust_for_dyn(info); /* return myself otherwise unchanged */ return this; } protected: /* fold constants for the operator */ virtual class CTcPrsNode *fold_unop() { return 0; } /* determine if I have side effects */ virtual int has_side_effects() const { return FALSE; } /* our subexpression */ class CTcPrsNode *sub_; }; /* * Define a constructor for a CTPNUnary target subclass. Target * subclasses can use this macro to define the standard CTPNUnary * subclass constructor - this is simply for conciseness in those * subclasses. */ #define CTPNUnary_ctor(scname) \ scname(class CTcPrsNode *sub) \ : CTPNUnary(sub) { } /* * define a CTPNUnary target subclass */ #define CTPNUnary_def(scname) \ class scname: public CTPNUnary \ { \ public: \ CTPNUnary_ctor(scname); \ void gen_code(int discard, int for_condition); \ } /* * define a CTPNUnary target subclass for a unary operator with side * effects */ #define CTPNUnary_side_def(scname) \ class scname: public CTPNUnary \ { \ public: \ CTPNUnary_ctor(scname); \ void gen_code(int discard, int for_condition); \ virtual int has_side_effects() const { return TRUE; } \ } /* ------------------------------------------------------------------------ */ /* * Generic binary parse node. Most binary node types don't differ at * the parser level, so target node types can be subclassed directly off * of this generic binary node class. */ class CTPNBinBase: public CTcPrsNode { public: CTPNBinBase(class CTcPrsNode *left, class CTcPrsNode *right) { left_ = left; right_ = right; } /* fold constants */ class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) { CTcPrsNode *ret; /* fold constants on my subexpressions */ left_ = left_->fold_constants(symtab); right_ = right_->fold_constants(symtab); /* try folding this operator, if we can */ ret = fold_binop(); /* * if we got a folded value, return it; otherwise, return * myself, unchanged except any subnode folding we did */ return (ret != 0 ? ret : this); } /* adjust for dynamic (run-time) compilation */ class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info) { /* if I have side effects, don't allow speculative evaluation */ if (info->speculative && has_side_effects()) err_throw(VMERR_BAD_SPEC_EVAL); /* adjust the left and right subexpressions */ left_ = left_->adjust_for_dyn(info); right_ = right_->adjust_for_dyn(info); /* return myself otherwise unchanged */ return this; } protected: /* perform folding on this binary operator, if possible */ virtual class CTcPrsNode *fold_binop() { return 0; } /* determine if I have side effects */ virtual int has_side_effects() const { return FALSE; } /* left and right operands */ class CTcPrsNode *left_; class CTcPrsNode *right_; }; /* * Define a constructor for a CTPNBin target subclass. Target * subclasses can use this macro to define the standard CTPNBin subclass * constructor - this is simply for conciseness in those subclasses. */ #define CTPNBin_ctor(scname) \ scname(class CTcPrsNode *left, class CTcPrsNode *right) \ : CTPNBin(left, right) { } /* * Define a full general class interface for a CTPNBin target subclass. * Most target subclasses will not need to add anything of their own, so * they can use this generic class definition macro. */ #define CTPNBin_def(scname) \ class scname: public CTPNBin \ { \ public: \ CTPNBin_ctor(scname); \ void gen_code(int discard, int for_condition); \ } /* * define a CTPNBin target subclass for a binary comparison operator */ #define CTPNBin_cmp_def(scname) \ class scname: public CTPNBin \ { \ public: \ CTPNBin_ctor(scname); \ void gen_code(int discard, int for_condition); \ virtual int is_bool() const { return TRUE; } \ } /* * define a CTPNBin target subclass for a binary operator with side * effects */ #define CTPNBin_side_def(scname) \ class scname: public CTPNBin \ { \ public: \ CTPNBin_ctor(scname); \ void gen_code(int discard, int for_condition); \ virtual int has_side_effects() const { return TRUE; } \ } /* ------------------------------------------------------------------------ */ /* * Generic statement node base. */ class CTPNStmBase: public CTcPrsNode { public: /* initialize at the tokenizer's current source file position */ CTPNStmBase(); /* initialize at the given source position */ CTPNStmBase(class CTcTokFileDesc *file, long linenum) { init(file, linenum); } /* set the file and line number */ void set_source_pos(class CTcTokFileDesc *file, long linenum) { /* remember the new line information */ file_ = file; linenum_ = linenum; } /* get the next statement after this one */ class CTPNStm *get_next_stm() const { return next_stm_; } /* set the next statement after this one */ void set_next_stm(class CTPNStm *nxt) { next_stm_ = nxt; } /* * log an error, using the source file location of this statement as * the location of the error */ void log_error(int errnum, ...) const; /* * log a warning, using the source file location of this statement * as the location of the warning */ void log_warning(int errnum, ...) const; /* get my source location information */ class CTcTokFileDesc *get_source_desc() const { return file_; } long get_source_linenum() const { return linenum_; } /* * Get the ending source location - for most statements, this is * identical to the normal source location information. For certain * complex statements, such as compound statements, this will * provide the source location where the statement's structure ends. */ virtual class CTcTokFileDesc *get_end_desc() const { return file_; } virtual long get_end_linenum() const { return linenum_; } /* * Determine the possible control flow routes through this * statement. Set or clear each flag as appropriate. * *. TCPRS_FLOW_NEXT - flow continues to the next statement *. TCPRS_FLOW_THROW - statement throws an exception *. TCPRS_FLOW_RET_VOID - statement returns no value *. TCPRS_FLOW_RET_VAL - statement returns a value *. TCPRS_FLOW_GOTO - control transfers to a code label *. TCPRS_FLOW_BREAK - control breaks out of the current loop/case *. TCPRS_FLOW_CONT - control goes to the top of the current loop * * Most statements simply proceed to the next statement in all * cases, so the default implementation simply sets the 'cont' flag * and clears the others. * * This routine is not meant to evaluate dependencies on called * functions or methods. Functions and methods that are called can * be assumed to return, with or without a value as needed for the * usage here. The possibility of VM exceptions due to run-time * error conditions can be ignored; we're only interested in * statements that explicitly throw exceptions. */ #define TCPRS_FLOW_NEXT 0x00000001 #define TCPRS_FLOW_THROW 0x00000002 #define TCPRS_FLOW_RET_VOID 0x00000004 #define TCPRS_FLOW_RET_VAL 0x00000008 #define TCPRS_FLOW_GOTO 0x00000010 #define TCPRS_FLOW_BREAK 0x00000020 #define TCPRS_FLOW_CONT 0x00000040 virtual unsigned long get_control_flow(int /*warn*/) const { /* by default, a statement continues to the next statement */ return TCPRS_FLOW_NEXT; } /* * Determine if this statement has a code label. By default, we * return false. */ virtual int has_code_label() const { return FALSE; } /* a statement has no return value */ virtual int has_return_value() const { return FALSE; } /* add a debugging line record for this statement */ void add_debug_line_rec(); protected: /* * Generate code for a sub-statement (such as the 'then' part of an * 'if', or a statement in a compount statement). This sets up the * error reporting location to refer to the sub-statement, then * generates code for the sub-statement normally. */ void gen_code_substm(class CTPNStm *substm); /* add a debugging line record at the given position */ void add_debug_line_rec(class CTcTokFileDesc *desc, long linenum); /* initialize */ void init(class CTcTokFileDesc *file, long linenum) { /* remember the file and line number containing the code */ file_ = file; linenum_ = linenum; /* there's not another statement after this one yet */ next_stm_ = 0; } /* next statement in execution order */ class CTPNStm *next_stm_; /* file and line number where this statement's source code appears */ class CTcTokFileDesc *file_; long linenum_; }; /* ------------------------------------------------------------------------ */ /* * Enclosing Statement Block object. This is a base class for * statements such as "try" blocks and code labels for which we must * keep a nesting list. A statement of this class has special needs for * exiting, via break, continue, goto, or return, the block of code that * the statement encloses * * NOTE: To avoid having to introduce yet another level of intermediate * #include file in our target-generic/target-specific layering, we * require that the target-specific intermediate include file define the * base class CTPNStmEnclosing for us. It must be defined as below, * with the addition of the target-specific definitions. Sorry about * any confusion this causes, but inserting a fourth include layer is * just going too far. */ // class CTPNStmEnclosing: public CTPNStm // { // public: // CTPNStmEnclosing(CTPNStmEnclosing *enclosing) // { // /* remember the statement that encloses me */ // enclosing_ = enclosing; // } // // protected: // /* the block enclosing this block */ // CTPNStmEnclosing *enclosing_; // }; /* ------------------------------------------------------------------------ */ /* * Base class for 'for' statement 'var in collection' and 'var in from..to' * expressions. We keep a separate list of these nodes for a 'for' * statement, for code generation purposes. These expression nodes reside * in the initializer clause of the 'for', but they implicitly generate * code in the condition and reinit phases as well. */ class CTPNForInBase: public CTcPrsNode { public: CTPNForInBase() { nxt_ = 0; } /* get/set the next list entry */ class CTPNForIn *getnxt() const { return nxt_; } void setnxt(class CTPNForIn *nxt) { nxt_ = nxt; } protected: /* next in list of 'in' clauses for this 'for' */ class CTPNForIn *nxt_; }; #endif /* TCPNINT_H */ qtads-2.1.7/tads3/tcprs.cpp000066400000000000000000010155161265017072300155260ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/TCPRS.CPP,v 1.5 1999/07/11 00:46:53 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcprs.cpp - TADS 3 Compiler Parser Function This parser module contains code required for any parser usage, both for a full compiler and for a debugger. Notes Modified 04/30/99 MJRoberts - Creation */ #include #include #include #include #include #include "os.h" #include "t3std.h" #include "tcprs.h" #include "tctarg.h" #include "tcgen.h" #include "vmhash.h" #include "tcmain.h" #include "vmfile.h" #include "tctok.h" #include "vmbignum.h" /* ------------------------------------------------------------------------ */ /* * Expression Operator parser objects. These objects are linked * together in a network that defines the order of precedence for * expression operators. * * These objects use static storage. This is acceptable, even though * these objects aren't all "const" qualified, because the compiler uses * a single global parser object; since there's only the one parser, * there only needs to be a single network of these objects. */ /* unary operator parser */ static CTcPrsOpUnary S_op_unary; /* factor group */ static const CTcPrsOpMul S_op_mul; static const CTcPrsOpDiv S_op_div; static const CTcPrsOpMod S_op_mod; static const CTcPrsOpBin *const S_grp_factor[] = { &S_op_mul, &S_op_div, &S_op_mod, 0 }; static const CTcPrsOpBinGroup S_op_factor(&S_op_unary, &S_op_unary, S_grp_factor); /* term group */ static const CTcPrsOpAdd S_op_add; static const CTcPrsOpSub S_op_sub; static const CTcPrsOpBin *const S_grp_term[] = { &S_op_add, &S_op_sub, 0 }; static const CTcPrsOpBinGroup S_op_term(&S_op_factor, &S_op_factor, S_grp_term); /* shift group */ static const CTcPrsOpShl S_op_shl; static const CTcPrsOpAShr S_op_ashr; static const CTcPrsOpLShr S_op_lshr; static const CTcPrsOpBin *const S_grp_shift[] = { &S_op_shl, &S_op_ashr, &S_op_lshr, 0 }; static const CTcPrsOpBinGroup S_op_shift(&S_op_term, &S_op_term, S_grp_shift); /* magnitude comparisons group */ static const CTcPrsOpGt S_op_gt; static const CTcPrsOpGe S_op_ge; static const CTcPrsOpLt S_op_lt; static const CTcPrsOpLe S_op_le; static const CTcPrsOpBin *const S_grp_relcomp[] = { &S_op_gt, &S_op_ge, &S_op_lt, &S_op_le, 0 }; static const CTcPrsOpBinGroup S_op_relcomp(&S_op_shift, &S_op_shift, S_grp_relcomp); /* * Equality/inequality comparison group. Note that the equality operator * is non-const because we want the option to change the operator on the * fly based on syntax mode - '==' in C-mode, '=' in TADS traditional mode. * (This was a feature of tads 2, but we have since deprecated it in tads * 3, so this ability is actually just vestigial at this point. No harm in * keeping around the code internally for it, though, since it's pretty * simple.) * * Note also that this is a special binary group - this one recognizes the * non-keyword operators 'is in' and 'not in'. */ static CTcPrsOpEq S_op_eq; static const CTcPrsOpNe S_op_ne; static const CTcPrsOpBin *const S_grp_eqcomp[] = { &S_op_eq, &S_op_ne, 0 }; static const CTcPrsOpBinGroupCompare S_op_eqcomp(&S_op_relcomp, &S_op_relcomp, S_grp_eqcomp); /* bitwise AND operator */ static const CTcPrsOpBAnd S_op_band(&S_op_eqcomp, &S_op_eqcomp); /* bitwise XOR operator */ static const CTcPrsOpBXor S_op_bxor(&S_op_band, &S_op_band); /* bitwise OR operator */ static const CTcPrsOpBOr S_op_bor(&S_op_bxor, &S_op_bxor); /* logical AND operator */ static const CTcPrsOpAnd S_op_and(&S_op_bor, &S_op_bor); /* logical OR operator */ static const CTcPrsOpOr S_op_or(&S_op_and, &S_op_and); /* if-nil operator ?? */ static const CTcPrsOpIfnil S_op_ifnil; /* conditional operator */ static const CTcPrsOpIf S_op_if; /* * assignment operator - note that this is non-const, because we must be * able to change the operator - '=' in C-mode, and ':=' in TADS * traditional mode */ static CTcPrsOpAsi S_op_asi; /* comma operator */ static const CTcPrsOpComma S_op_comma(&S_op_asi, &S_op_asi); /* ------------------------------------------------------------------------ */ /* * Embedded expression token list. We capture the tokens of an embedded * expression in a private list for a first scan, to compare against the * list of special embedding templates. If we determine that the embedding * is a simple expression, or that a portion of it is an expression, we'll * push the captured tokens back into the token stream via 'unget'. */ class CTcEmbedTokenList { public: CTcEmbedTokenList() { /* create the initial list entry */ wrt = head = new CTcTokenEle(); cnt = 0; } ~CTcEmbedTokenList() { /* delete the token list */ while (head != 0) { CTcTokenEle *nxt = head->getnxt(); delete head; head = nxt; } } /* reset the list */ void reset() { /* set the read and write pointers to the allocated list head */ wrt = head; cnt = 0; } /* get the number of tokens remaining */ int getcnt() const { return cnt; } /* get the head elemenet */ const CTcToken *get_head() const { return head; } /* remove the head element */ void unlink_head(CTcToken *tok) { if (head != 0) { /* copy the token to the caller's buffer */ G_tok->copytok(tok, head); /* unlink it */ CTcTokenEle *h = head; head = head->getnxt(); --cnt; /* delete it */ delete h; } else { /* no token available - return EOF */ tok->settyp(TOKT_EOF); } } /* add a token to the list */ void add_tok(const CTcToken *tok) { /* fill in the write token */ G_tok->copytok(wrt, tok); /* if this is the last item in the list, add another */ if (wrt->getnxt() == 0) { CTcTokenEle *ele = new CTcTokenEle(); ele->setprv(wrt); wrt->setnxt(ele); } /* advance the write pointer */ wrt = wrt->getnxt(); /* count it */ ++cnt; } /* * Unget the captured tokens, skipping the first n tokens and the last * m tokens. */ void unget(int n = 0, int m = 0) { /* skip the last 'm' tokens */ CTcTokenEle *ele; int i; for (ele = wrt->getprv(), i = cnt ; m != 0 && ele != 0 ; ele = ele->getprv(), --m, --i) ; /* unget tokens until we're down to the first 'n' */ for ( ; ele != 0 && i > n ; ele = ele->getprv(), --i) G_tok->unget(ele); } /* match a string of space-delimited tokens */ int match(const char *txt) { /* count the tokens */ int argn = 0; const char *p; for (p = txt ; *p != '\0' ; ++argn) { /* skip leading spaces */ for ( ; is_space(*p) ; ++p) ; /* scan to the next space */ for ( ; *p != '\0' && !is_space(*p) ; ++p) ; } /* if we don't have enough tokens to match the input, we can't match */ if (cnt < argn) return FALSE; /* check the arguments against the list */ CTcTokenEle *ele; int tokn; for (tokn = cnt, ele = head, p = txt ; tokn != 0 && argn != 0 ; ele = ele->getnxt(), --tokn, --argn) { /* get this argument */ for ( ; is_space(*p) ; ++p) ; const char *arg = p; /* find the end of the argument */ for ( ; *p != '\0' && !is_space(*p) ; ++p) ; size_t len = p - arg; /* check for special arguments */ if (arg[0] == '*') { /* * '*' - this matches one or more tokens in the token list * up to the last remaining arguments after the '*'. Skip * arguments until the token list and remaining argument * list are the same length. */ for ( ; tokn > argn ; ele = ele->getnxt(), --tokn) ; } else { /* it's a literal - check for a match */ if (!ele->text_matches(arg, len)) return FALSE; } } /* if the lists ran out at the same time, it's a match */ return (tokn == 0 && argn == 0); } /* match a string template definition */ int match(CTcStrTemplate *tpl) { /* if we don't have enough tokens to match, don't bother looking */ if (cnt < tpl->cnt) return FALSE; /* check the arguments against the list */ CTcTokenEle *ele, *tele; int tokn, tpln; for (tokn = cnt, tpln = tpl->cnt, ele = head, tele = tpl->head ; tokn != 0 && tpln != 0 ; ele = ele->getnxt(), tele = tele->getnxt(), --tokn, --tpln) { /* check for special arguments */ if (tele->text_matches("*", 1)) { /* * '*' - this matches one or more tokens in the token list * up to the last remaining arguments after the '*'. Skip * arguments until the token list and remaining argument * list are the same length. */ for ( ; tokn > tpln ; ele = ele->getnxt(), --tokn) ; } else { /* it's a literal - check for a match */ if (!ele->text_matches(tele->get_text(), tele->get_text_len())) return FALSE; } } /* if the lists ran out at the same time, it's a match */ return (tokn == 0 && tpln == 0); } /* * Reduce our token list to include only the tokens matching the '*' in * a template. This presumes that the template actually does match. */ void reduce(CTcStrTemplate *tpl) { /* find the first token in our list matching '*' in the template */ CTcTokenEle *src, *tele; int rem, trem; for (src = head, tele = tpl->head, rem = cnt, trem = tpl->cnt ; src != 0 && tele != 0 ; src = src->getnxt(), tele = tele->getnxt(), --rem, --trem) { /* stop when we reach '*' in the template */ if (tele->text_matches("*", 1)) break; } /* skip the '*' in the template */ trem -= 1; /* * The number of tokens matching '*' is the number left in our * list, minus the number left in the token list after the '*'. */ rem -= trem; cnt = rem; /* if we had any leading fixed tokens to remove, remove them */ if (src != head) { CTcTokenEle *dst; for (dst = head ; rem != 0 && src != 0 ; src = src->getnxt(), dst = dst->getnxt(), --rem) { /* copy this token */ G_tok->copytok(dst, src); } } /* move the write pointer to the proper position */ for (wrt = head, rem = cnt ; rem != 0 ; wrt = wrt->getnxt(), --rem) ; } protected: /* head of the allocated list */ CTcTokenEle *head; /* current write pointer */ CTcTokenEle *wrt; /* number of tokens currently in the list */ int cnt; }; /* ------------------------------------------------------------------------ */ /* * Main Parser */ /* * initialize the parser */ CTcParser::CTcParser() { size_t i; /* we don't have any module information yet */ module_name_ = 0; module_seqno_ = 0; /* start out in normal mode */ pp_expr_mode_ = FALSE; src_group_mode_ = FALSE; /* create the global symbol table */ global_symtab_ = new CTcPrsSymtab(0); /* no enclosing statement yet */ enclosing_stm_ = 0; /* no source location yet */ cur_desc_ = 0; cur_linenum_ = 0; /* no dictionaries yet */ dict_cur_ = 0; dict_head_ = dict_tail_ = 0; /* no object file dictionary list yet */ obj_dict_list_ = 0; obj_file_dict_idx_ = 0; /* no dictionary properties yet */ dict_prop_head_ = 0; /* no grammar productions yet */ gramprod_head_ = gramprod_tail_ = 0; /* no object file symbol list yet */ obj_sym_list_ = 0; obj_file_sym_idx_ = 0; /* no object templates yet */ template_head_ = template_tail_ = 0; /* allocate some initial template parsing space */ template_expr_max_ = 16; template_expr_ = (CTcObjTemplateInst *)G_prsmem-> alloc(sizeof(template_expr_[0]) * template_expr_max_); /* no string templates yet */ str_template_head_ = str_template_tail_ = 0; /* no locals yet */ local_cnt_ = max_local_cnt_ = 0; /* no local or goto symbol table yet */ local_symtab_ = 0; enclosing_local_symtab_ = 0; goto_symtab_ = 0; /* no debugger local symbol table yet */ debug_symtab_ = 0; /* not in a preprocessor constant expression */ pp_expr_mode_ = FALSE; /* assume we're doing full compilation */ syntax_only_ = FALSE; /* no nested top-level statements yet */ nested_stm_head_ = 0; nested_stm_tail_ = 0; /* no anonymous objects yet */ anon_obj_head_ = 0; anon_obj_tail_ = 0; /* no non-symbol objects yet */ nonsym_obj_head_ = 0; nonsym_obj_tail_ = 0; /* allocate an initial context variable property array */ ctx_var_props_size_ = 50; ctx_var_props_ = (tctarg_prop_id_t *) t3malloc(ctx_var_props_size_ * sizeof(tctarg_prop_id_t)); /* no context variable properties assigned yet */ ctx_var_props_cnt_ = 0; ctx_var_props_used_ = 0; /* * no context variable indices assigned yet - start at one higher * than the index at which we always store 'self' */ next_ctx_arr_idx_ = TCPRS_LOCAL_CTX_METHODCTX + 1; /* 'self' isn't valid yet */ self_valid_ = FALSE; /* start at enum ID 1 (let 0 serve as an invalid value) */ next_enum_id_ = 1; /* the '+' property is not yet defined */ plus_prop_ = 0; /* no exported symbols yet */ exp_head_ = exp_tail_ = 0; /* allocate an initial '+' stack */ plus_stack_alloc_ = 32; plus_stack_ = (CTPNStmObject **) t3malloc(plus_stack_alloc_ * sizeof(*plus_stack_)); /* clear out the stack */ for (i = 0 ; i < plus_stack_alloc_ ; ++i) plus_stack_[i] = 0; /* there's no current code body (function/method body) yet */ cur_code_body_ = 0; /* nothing in the local context has been referenced yet */ self_referenced_ = FALSE; local_ctx_needs_self_ = FALSE; full_method_ctx_referenced_ = FALSE; local_ctx_needs_full_method_ctx_ = FALSE; /* create the embedded expression list object */ embed_toks_ = new CTcEmbedTokenList(); } /* * Add a special built-in property symbol */ CTcSymProp *CTcParser::def_special_prop(int def, const char *name, tc_prop_id *idp) { /* we haven't created or found the property yet */ CTcSymProp *propsym = 0; /* define or look up the property, as required */ if (def) { /* allocate the ID */ tctarg_prop_id_t id = G_cg->new_prop_id(); /* create the symbol */ propsym = new CTcSymProp(name, strlen(name), FALSE, id); /* mark it as referenced, since the compiler itself uses it */ propsym->mark_referenced(); /* add it to the global symbol table */ global_symtab_->add_entry(propsym); } else { /* find the entry */ CTcSymbol *sym = global_symtab_->find(name, strlen(name)); /* check to see if we found a property symbol */ if (sym != 0 && sym->get_type() == TC_SYM_PROP) { /* got it - use the definition we found */ propsym = (CTcSymProp *)sym; } else { /* not found - create a dummy symbol for it */ propsym = new CTcSymProp(name, strlen(name), FALSE, TCTARG_INVALID_PROP); } } /* hand the property ID back to the caller if they want it */ if (idp != 0) *idp = (propsym != 0 ? propsym->get_prop() : TCTARG_INVALID_PROP); /* return the symbol */ return propsym; } /* * Initialize. This must be called after the code generator is set up. */ void CTcParser::init() { /* define the special properties */ cache_special_props(TRUE); } /* * Define or look up the special properties. If 'def' is true, we'll * create definitions; otherwise we'll look up the definitions in the * existing symbol table. The former case is for normal initialization of * a new compiler; the latter is for use in dynamic compilation, where the * global symbol table is provided by the running program. */ void CTcParser::cache_special_props(int def) { tc_prop_id propid; /* add a "construct" property for constructors */ constructor_sym_ = def_special_prop(def, "construct", &constructor_prop_); /* add a "finalize" property for finalizers */ def_special_prop(def, "finalize", &finalize_prop_); /* add some properties for grammar production match objects */ graminfo_prop_ = def_special_prop(def, "grammarInfo"); gramtag_prop_ = def_special_prop(def, "grammarTag"); /* add a "miscVocab" property for miscellaneous vocabulary words */ def_special_prop(def, "miscVocab", &propid); miscvocab_prop_ = (tc_prop_id)propid; /* add a "lexicalParent" property for a nested object's parent */ lexical_parent_sym_ = def_special_prop(def, "lexicalParent"); /* add a "sourceTextOrder" property */ src_order_sym_ = def_special_prop(def, "sourceTextOrder"); /* start the sourceTextOrder index at 1 */ src_order_idx_ = 1; /* add a "sourceTextGroup" property */ src_group_sym_ = def_special_prop(def, "sourceTextGroup"); /* we haven't created the sourceTextGroup referral object yet */ src_group_id_ = TCTARG_INVALID_OBJ; /* add a "sourceTextGroupName" property */ src_group_mod_sym_ = def_special_prop(def, "sourceTextGroupName"); /* add a "sourceTextGroupOrder" property */ src_group_seq_sym_ = def_special_prop(def, "sourceTextGroupOrder"); /* define the operator overload properties */ ov_op_add_ = def_special_prop(def, "operator +"); ov_op_sub_ = def_special_prop(def, "operator -"); ov_op_mul_ = def_special_prop(def, "operator *"); ov_op_div_ = def_special_prop(def, "operator /"); ov_op_mod_ = def_special_prop(def, "operator %"); ov_op_xor_ = def_special_prop(def, "operator ^"); ov_op_shl_ = def_special_prop(def, "operator <<"); ov_op_ashr_ = def_special_prop(def, "operator >>"); ov_op_lshr_ = def_special_prop(def, "operator >>>"); ov_op_bnot_ = def_special_prop(def, "operator ~"); ov_op_bor_ = def_special_prop(def, "operator |"); ov_op_band_ = def_special_prop(def, "operator &"); ov_op_neg_ = def_special_prop(def, "operator negate"); ov_op_idx_ = def_special_prop(def, "operator []"); ov_op_setidx_ = def_special_prop(def, "operator []="); } /* get the grammarTag property ID */ tc_prop_id CTcParser::get_grammarTag_prop() const { return gramtag_prop_ != 0 ? gramtag_prop_->get_prop() : TCTARG_INVALID_PROP; } /* get the grammarInfo property ID */ tc_prop_id CTcParser::get_grammarInfo_prop() const { return graminfo_prop_ != 0 ? graminfo_prop_->get_prop() : TCTARG_INVALID_PROP; } /* * destroy the parser */ CTcParser::~CTcParser() { /* * Note that we don't have to delete certain objects, because we * allocated them out of the parser memory pool and will be * automatically deleted when the pool is deleted. For example, we * don't have to delete any symbol tables, including the global * symbol table. */ /* delete the module name, if it's known */ lib_free_str(module_name_); /* delete the object file symbol fixup list, if present */ if (obj_sym_list_ != 0) t3free(obj_sym_list_); /* delete the object file dictionary fixup list, if present */ if (obj_dict_list_ != 0) t3free(obj_dict_list_); /* delete the context variable property list */ if (ctx_var_props_ != 0) t3free(ctx_var_props_); /* delete the export list */ while (exp_head_ != 0) { CTcPrsExport *nxt; /* remember the next entry, since we're deleting our pointer to it */ nxt = exp_head_->get_next(); /* delete this entry */ delete exp_head_; /* move on to the next */ exp_head_ = nxt; } /* delete the '+' stack */ t3free(plus_stack_); /* delete the embedding look-ahead list */ delete embed_toks_; } /* ------------------------------------------------------------------------ */ /* * Set the module information */ void CTcParser::set_module_info(const char *name, int seqno) { /* if we have a name stored already, delete the old one */ lib_free_str(module_name_); /* store the new name and sequence number */ module_name_ = lib_copy_str(name); module_seqno_ = seqno; } /* * Change the #pragma C mode. On changing this mode, we'll change the * assignment operator and equality operator tokens. If 'mode' is true, * we're in C mode; otherwise, we're in traditional TADS mode. * * #pragma C+: assignment is '=', equality is '==' *. #pragma C-: assignment is ':=', equality is '='. */ void CTcParser::set_pragma_c(int mode) { /* set the assignment operator */ S_op_asi.set_asi_op(mode ? TOKT_EQ : TOKT_ASI); /* set the equality comparison operator */ S_op_eq.set_eq_op(mode ? TOKT_EQEQ : TOKT_EQ); } /* * Parse an expression. This parses a top-level comma expression. */ CTcPrsNode *CTcParser::parse_expr() { /* parse a comma expression */ return S_op_comma.parse(); } /* * Parse a condition expression. Warns if the outermost operator is a * simple assignment. */ CTcPrsNode *CTcParser::parse_cond_expr() { CTcPrsNode *cond; /* parse the expression */ cond = parse_expr(); /* * if the outermost operator is a simple assignment, display an * error */ if (cond != 0 && cond->is_simple_asi() && !G_prs->get_syntax_only()) G_tok->log_warning(TCERR_ASI_IN_COND); /* return the result */ return cond; } /* * Parse an assignment expression. */ CTcPrsNode *CTcParser::parse_asi_expr() { /* parse an assignment expression */ return S_op_asi.parse(); } /* * Parse an expression or a double-quoted string expression */ CTcPrsNode *CTcParser::parse_expr_or_dstr(int allow_comma_expr) { /* * parse the appropriate kind of expression - if a comma expression is * allowed, parse that, otherwise parse an assignment expression (as * that's the next thing down the hierarchy from the comma operator) */ return (allow_comma_expr ? S_op_comma.parse() : S_op_asi.parse()); } /* * Parse a required semicolon */ int CTcParser::parse_req_sem() { const char eof_str[] = ""; /* check to see if we found the semicolon */ if (G_tok->cur() == TOKT_SEM) { /* success - skip the semicolon and tell the caller to proceed */ G_tok->next(); return 0; } /* * check what we have; the type of error we want to log depends on * what we find next */ switch(G_tok->cur()) { case TOKT_RPAR: /* log the extra ')' error */ G_tok->log_error(TCERR_EXTRA_RPAR); /* * we're probably in an expression that ended before the user * thought it should; skip the extraneous material up to the * next semicolon */ return skip_to_sem(); case TOKT_RBRACK: /* log the error */ G_tok->log_error(TCERR_EXTRA_RBRACK); /* skip up to the next semicolon */ return skip_to_sem(); case TOKT_EOF: /* * missing semicolon at end of file - log the missing-semicolon * error and tell the caller not to proceed, since there's * nothing left to parse */ G_tok->log_error(TCERR_EXPECTED_SEMI, (int)sizeof(eof_str)-1, eof_str); return 1; default: /* * the source is probably just missing a semicolon; log the * error, and tell the caller to proceed from the current * position */ G_tok->log_error_curtok(TCERR_EXPECTED_SEMI); return 0; } } /* * Skip to the next semicolon */ int CTcParser::skip_to_sem() { /* keep going until we find a semicolon or some other reason to stop */ for (;;) { /* see what we have next */ switch(G_tok->cur()) { case TOKT_EOF: /* end of file - tell the caller not to proceed */ return 1; case TOKT_SEM: /* * it's the semicolon at last - skip it and tell the caller * to proceed */ G_tok->next(); return 0; case TOKT_LBRACE: case TOKT_RBRACE: /* * Don't skip past braces - the caller probably simply left * out a semicolon at the end of a statement, and we've now * reached the next block start or end. Stop here and tell * the caller to proceed. */ return 0; default: /* skip anything else */ G_tok->next(); break; } } } /* * Parse an operator name. Call this when the current token is 'operator' * and an operator name is expected. We'll fill in 'tok' with the pseudo * property name of the operator ("operator +", etc). */ int CTcParser::parse_op_name(CTcToken *tok, int *op_argp) { const char *propname; int ok = TRUE; int op_args = 0; /* get the actual operator */ switch (G_tok->next()) { case TOKT_SYM: /* check for named operators */ if (G_tok->getcur()->text_matches("negate")) { propname = "operator negate"; op_args = 1; } else { /* unknown symbolic operator name */ G_tok->log_error_curtok(TCERR_BAD_OP_OVERLOAD); ok = FALSE; propname = "unknown_operator"; } break; case TOKT_PLUS: propname = "operator +"; op_args = 2; break; case TOKT_MINUS: propname = "operator -"; op_args = 2; break; case TOKT_TIMES: propname = "operator *"; op_args = 2; break; case TOKT_DIV: propname = "operator /"; op_args = 2; break; case TOKT_MOD: propname = "operator %"; op_args = 2; break; case TOKT_XOR: propname = "operator ^"; op_args = 2; break; case TOKT_SHL: propname = "operator <<"; op_args = 2; break; case TOKT_ASHR: propname = "operator >>"; op_args = 2; break; case TOKT_LSHR: propname = "operator >>>"; op_args = 2; break; case TOKT_BNOT: propname = "operator ~"; op_args = 1; break; case TOKT_OR: propname = "operator |"; op_args = 2; break; case TOKT_AND: propname = "operator &"; op_args = 2; break; case TOKT_LBRACK: /* we need at least a ']', and a '=' can follow */ if (G_tok->next() != TOKT_RBRACK) G_tok->log_error_curtok(TCERR_EXPECTED_RBRACK_IN_OP); /* check what follows that */ if (G_tok->next() == TOKT_EQ) { /* it's the assign-to-index operator []= */ propname = "operator []="; op_args = 3; } else { /* it's just the regular index operator [] */ propname = "operator []"; op_args = 2; /* put back our peek-ahead token */ G_tok->unget(); } break; default: /* it's not an operator we can override */ G_tok->log_error_curtok(TCERR_BAD_OP_OVERLOAD); propname = "unknown_operator"; ok = FALSE; break; } /* copy the property name to the token */ tok->set_text(propname, strlen(propname)); /* set the caller's argument counter if desired */ if (op_argp != 0) *op_argp = op_args; /* return the success/error indication */ return ok; } /* * Create a symbol node */ CTcPrsNode *CTcParser::create_sym_node(const textchar_t *sym, size_t sym_len) { /* * First, look up the symbol in local scope. Local scope symbols * can always be resolved during parsing, because the language * requires that local scope items be declared before their first * use. */ CTcPrsSymtab *symtab; CTcSymbol *entry = local_symtab_->find(sym, sym_len, &symtab); /* if we found it in local scope, return a resolved symbol node */ if (entry != 0 && symtab != global_symtab_) return new CTPNSymResolved(entry); /* if there's a debugger local scope, look it up there */ if (debug_symtab_ != 0) { /* look it up in the debug symbol table */ tcprsdbg_sym_info info; if (debug_symtab_->find_symbol(sym, sym_len, &info)) { /* found it - return a debugger local variable */ return new CTPNSymDebugLocal(&info); } } /* * We didn't find it in local scope, so the symbol cannot be resolved * until code generation - return an unresolved symbol node. Note a * possible implicit self-reference, since this could be a property of * 'self'. */ set_self_referenced(TRUE); return new CTPNSym(sym, sym_len); } /* * Add a nested top-level statement to our list */ void CTcParser::add_nested_stm(CTPNStmTop *stm) { /* link it into our list */ if (nested_stm_tail_ != 0) nested_stm_tail_->set_next_stm_top(stm); else nested_stm_head_ = stm; nested_stm_tail_ = stm; } /* * Add an anonymous object to our list */ void CTcParser::add_anon_obj(CTcSymObj *sym) { /* link it into our list */ if (anon_obj_tail_ != 0) anon_obj_tail_->nxt_ = sym; else anon_obj_head_ = sym; anon_obj_tail_ = sym; /* it's the last one */ sym->nxt_ = 0; /* mark the symbol as anonymous */ sym->set_anon(TRUE); } /* * Add a non-symbolic object to our list */ void CTcParser::add_nonsym_obj(tctarg_obj_id_t id) { tcprs_nonsym_obj *obj; /* allocate a link structure */ obj = new (G_prsmem) tcprs_nonsym_obj(id); /* link it into our list */ if (nonsym_obj_tail_ != 0) nonsym_obj_tail_->nxt_ = obj; else nonsym_obj_head_ = obj; nonsym_obj_tail_ = obj; } /* * Basic routine to read a length-prefixed symbol. Uses the given * temporary buffer, then stores the text in tokenizer memory (which * remains valid and available throughout compilation). If the length * exceeds the temporary buffer length, we'll flag the given error and * return null. The length return pointer can be null if the caller wants * the results null-terminated rather than returned with a counted length. * If the length pointer is given, the result will not be null-terminated. * */ const char *CTcParser::read_len_prefix_str (CVmFile *fp, char *tmp_buf, size_t tmp_buf_len, size_t *ret_len, int err_if_too_long) { size_t read_len; size_t alloc_len; /* read the length to read from the file */ read_len = (size_t)fp->read_uint2(); /* if we need null termination, add a byte to the allocation length */ alloc_len = read_len + (ret_len == 0 ? 1 : 0); /* if it won't fit in the temporary buffer, it's an error */ if (alloc_len > tmp_buf_len) { /* log the error and return failure */ G_tcmain->log_error(0, 0, TC_SEV_ERROR, err_if_too_long); return 0; } /* read the bytes into the temporary buffer */ fp->read_bytes(tmp_buf, read_len); /* add null termination if required, or set the return length if not */ if (ret_len == 0) tmp_buf[read_len] = '\0'; else *ret_len = read_len; /* store the result in the tokenizer's text list and return the result */ return G_tok->store_source(tmp_buf, alloc_len); } /* * Read a length prefixed string into a given buffer. Returns zero on * success, non-zero on failure. */ int CTcParser::read_len_prefix_str(CVmFile *fp, char *buf, size_t buf_len, int err_if_too_long) { size_t read_len; size_t alloc_len; /* read the length to read from the file */ read_len = (size_t)fp->read_uint2(); /* add a byte for null termination */ alloc_len = read_len + 1; /* if it won't fit in the temporary buffer, it's an error */ if (alloc_len > buf_len) { /* log the error and return failure */ G_tcmain->log_error(0, 0, TC_SEV_ERROR, err_if_too_long); return 1; } /* read the bytes into the caller's buffer */ fp->read_bytes(buf, read_len); /* add null termination */ buf[read_len] = '\0'; /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Add a generated object */ CTcSymObj *CTcParser::add_gen_obj(const char *clsname) { /* look up the class symbol */ CTcSymObj *cls = (CTcSymObj *)global_symtab_->find( clsname, strlen(clsname)); /* make sure we found an object symbol */ if (cls != 0 && cls->get_type() == TC_SYM_OBJ) { /* got it - go create the object */ return add_gen_obj(cls); } else { /* not an object - return failure */ return 0; } } /* * Add a generated object. */ CTcSymObj *CTcParser::add_gen_obj(CTcSymObj *cls) { /* check for dynamic compilation */ if (G_vmifc != 0) { /* create a live object in the VM, and wrap it in an anon symbol */ tctarg_obj_id_t clsid = (cls != 0 ? cls->get_obj_id() : TCTARG_INVALID_OBJ); return new CTcSymObj(".anon", 5, FALSE, G_vmifc->new_obj(clsid), FALSE, TC_META_TADSOBJ, 0); } else { /* static mode */ return add_gen_obj_stat(cls); } } /* * Add a constant property value to a generated object */ void CTcParser::add_gen_obj_prop( CTcSymObj *obj, const char *propn, const CTcConstVal *val) { /* look up the property - this counts as an explicit definition */ CTcSymbol *sym = G_prs->get_global_symtab() ->find_or_def_prop_explicit(propn, strlen(propn), FALSE); /* make sure we found it, and that it's a property symbol */ if (sym != 0 && sym->get_type() == TC_SYM_PROP) { /* it's a property symbol - cast it */ CTcSymProp *prop = (CTcSymProp *)sym; /* check for dynamic compilation */ if (G_vmifc != 0) { /* dynamic mode - add it to the live object in the VM */ G_vmifc->set_prop(obj->get_obj_id(), prop->get_prop(), val); } else { /* static compilation - add the value to the object statement */ add_gen_obj_prop_stat(obj, prop, val); } } } /* * add an integer property value to a generated object */ void CTcParser::add_gen_obj_prop(CTcSymObj *obj, const char *prop, int val) { /* set up a constant structure for the value */ CTcConstVal c; c.set_int(val); /* set the property */ add_gen_obj_prop(obj, prop, &c); } /* * add a string property value to a generated object */ void CTcParser::add_gen_obj_prop(CTcSymObj *obj, const char *prop, const char *val) { /* set up a constant structure for the value */ CTcConstVal c; c.set_sstr(val, strlen(val)); /* set the property */ add_gen_obj_prop(obj, prop, &c); } /* ------------------------------------------------------------------------ */ /* * Constant Value */ /* * set a string value */ void CTcConstVal::set_sstr(const char *val, size_t len) { /* store the type */ typ_ = TC_CVT_SSTR; /* store a pointer to the string */ val_.strval_.strval_ = val; val_.strval_.strval_len_ = len; /* for image file layout purposes, record the length of this string */ G_cg->note_str(len); } void CTcConstVal::set_sstr(const CTcToken *tok) { /* get the string from the token */ if (tok != 0) set_sstr(tok->get_text(), tok->get_text_len()); else set_sstr("", 0); } void CTcConstVal::set_sstr(uint32_t ofs) { typ_ = TC_CVT_SSTR; val_.strval_.strval_ = 0; val_.strval_.strval_len_ = 0; val_.strval_.pool_ofs_ = ofs; } /* * Set a regex string value */ void CTcConstVal::set_restr(const CTcToken *tok) { typ_ = TC_CVT_RESTR; val_.strval_.strval_ = tok->get_text(); val_.strval_.strval_len_ = tok->get_text_len(); val_.strval_.pool_ofs_ = 0; } /* * BigNumber string formatter buffer allocator. Allocates a buffer of the * required size from the parser memory pool. */ class CBigNumStringBufPrsAlo: public IBigNumStringBuf { public: virtual char *get_buf(size_t need) { return new (G_prsmem) char[need]; } }; /* set a floating point value from a token string */ void CTcConstVal::set_float(const char *str, size_t len, int promoted) { /* set the float */ typ_ = TC_CVT_FLOAT; val_.floatval_.txt_ = str; val_.floatval_.len_ = len; promoted_ = promoted; } /* set a floating point value from a vbignum_t */ void CTcConstVal::set_float(const vbignum_t *val, int promoted) { /* format the value */ CBigNumStringBufPrsAlo alo; const char *buf = val->format(&alo); /* * read the length from the buffer - vbignum_t::format() fills in the * buffer using the TADS String format, with a two-byte little-endian * (VMB_LEN) length prefix */ size_t len = vmb_get_len(buf); buf += VMB_LEN; /* store it */ set_float(buf, len, promoted); } /* set a floating point value from a promoted integer value */ void CTcConstVal::set_float(ulong i) { /* set up the bignum value */ vbignum_t b(i); /* store it as a promoted integer value */ set_float(&b, TRUE); } /* * Try demoting a float back to an int. This can be used after a * constant-folding operation to turn a previously promoted float back to * an int if the result of the calculation now fits the int type. */ void CTcConstVal::demote_float() { /* if this is a promoted integer, see if we can demote it back to int */ if (typ_ == TC_CVT_FLOAT && promoted_) { /* get the BigNumber value */ vbignum_t b(val_.floatval_.txt_, val_.floatval_.len_, 0); /* convert it to an integer */ int ov; int32_t i = b.to_int(ov); /* if it didn't overflow, demote the constant back to an int */ if (!ov) set_int(i); } } /* * set a list value */ void CTcConstVal::set_list(CTPNList *lst) { /* set the type */ typ_ = TC_CVT_LIST; /* remember the list */ val_.listval_.l_ = lst; /* for image file layout purposes, record the length of this list */ G_cg->note_list(lst->get_count()); } void CTcConstVal::set_list(uint32_t ofs) { typ_ = TC_CVT_LIST; val_.listval_.l_ = 0; val_.listval_.pool_ofs_ = ofs; } /* * Convert a value to a string */ const char *CTcConstVal::cvt_to_str(char *buf, size_t bufl, size_t *result_len) { /* check my type */ switch(typ_) { case TC_CVT_NIL: /* the result is "nil" */ if (bufl < 4) return 0; strcpy(buf, "nil"); *result_len = 3; return buf; case TC_CVT_TRUE: /* the result is "true" */ if (bufl < 5) return 0; strcpy(buf, "true"); *result_len = 4; return buf; case TC_CVT_SSTR: /* it's already a string */ *result_len = get_val_str_len(); return get_val_str(); case TC_CVT_INT: /* convert our signed integer value */ if (bufl < 12) return 0; sprintf(buf, "%ld", get_val_int()); *result_len = strlen(buf); return buf; case TC_CVT_FLOAT: /* we store these as strings */ *result_len = get_val_float_len(); return get_val_float(); default: /* can't convert other types */ return 0; } } /* is this a numeric value equal to zero? */ int CTcConstVal::equals_zero() const { /* check for integer zero */ if (typ_ == TC_CVT_INT && get_val_int() == 0) return TRUE; /* check for a float zero */ if (typ_ == TC_CVT_FLOAT) { vbignum_t v(get_val_float(), get_val_float_len(), 0); return v.is_zero(); } /* not zero */ return FALSE; } /* * Compare for equality to another constant value */ int CTcConstVal::is_equal_to(const CTcConstVal *val) const { CTPNListEle *ele1; CTPNListEle *ele2; /* check for float-int comparisons */ if (typ_ == TC_CVT_INT && val->get_type() == TC_CVT_FLOAT) { vbignum_t a(get_val_int()); vbignum_t b(val->get_val_float(), val->get_val_float_len(), 0); return a.compare(b) == 0; } if (typ_ == TC_CVT_FLOAT && val->get_type() == TC_CVT_INT) { vbignum_t a(get_val_float(), get_val_float_len(), 0); vbignum_t b(val->get_val_int()); return a.compare(b) == 0; } /* * if the types aren't equal, the values are not equal; otherwise, * check the various types */ if (typ_ != val->get_type()) { /* the types aren't equal, so the values are not equal */ return FALSE; } /* the types are the same; do the comparison based on the type */ switch(typ_) { case TC_CVT_UNK: /* unknown type; unknown values can never be equal */ return FALSE; case TC_CVT_TRUE: case TC_CVT_NIL: /* * nil==nil and true==true; since we know the types are the * same, the values are the same */ return TRUE; case TC_CVT_INT: /* compare the integers */ return (get_val_int() == val->get_val_int()); case TC_CVT_FLOAT: { vbignum_t a(get_val_float(), get_val_float_len(), 0); vbignum_t b(val->get_val_float(), val->get_val_float_len(), 0); return a.compare(b) == 0; } case TC_CVT_SSTR: /* compare the strings */ return (get_val_str_len() == val->get_val_str_len() && memcmp(get_val_str(), val->get_val_str(), get_val_str_len()) == 0); case TC_CVT_LIST: /* * if the lists don't have the same number of elements, they're * not equal */ if (get_val_list()->get_count() != val->get_val_list()->get_count()) return FALSE; /* * compare each element of each list; if they're all the same, * the values are the same */ ele1 = get_val_list()->get_head(); ele2 = val->get_val_list()->get_head(); for ( ; ele1 != 0 && ele2 != 0 ; ele1 = ele1->get_next(), ele2 = ele2->get_next()) { /* if these elements aren't equal, the lists aren't equal */ if (!ele1->get_expr()->get_const_val() ->is_equal_to(ele2->get_expr()->get_const_val())) return FALSE; } /* we didn't find any differences, so the lists are equal */ return TRUE; case TC_CVT_OBJ: /* if the object values are the same, the values match */ return (get_val_obj() == val->get_val_obj()); case TC_CVT_PROP: /* if the property values are the same, the values match */ return (get_val_prop() == val->get_val_prop()); case TC_CVT_FUNCPTR: /* * if both symbols are the same, the values match; otherwise, * they refer to different functions */ return (get_val_funcptr_sym() == val->get_val_funcptr_sym()); default: /* unknown type; return unequal */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Operator Parsers */ /* ------------------------------------------------------------------------ */ /* * Parse a left-associative binary operator */ CTcPrsNode *CTcPrsOpBin::parse() const { /* parse our left side - if that fails, return failure */ CTcPrsNode *lhs = left_->parse(); if (lhs == 0) return 0; /* keep going as long as we find our operator */ for (;;) { /* check my operator */ if (G_tok->cur() == get_op_tok()) { /* skip the matching token */ G_tok->next(); /* parse the right-hand side */ CTcPrsNode *rhs = right_->parse(); if (rhs == 0) return 0; /* try folding our subnodes into a constant value, if possible */ CTcPrsNode *const_tree = eval_constant(lhs, rhs); /* * if we couldn't calculate a constant value, build the tree * normally */ if (const_tree == 0) { /* * Build my tree, then proceed to parse any additional * occurrences of our operator, with the result of * applying this occurrence of the operator as the * left-hand side of the new operator. */ lhs = build_tree(lhs, rhs); } else { /* we got a constant value - use it as the result directly */ lhs = const_tree; } } else { /* * it's not my operator - return what we thought might have * been our left-hand side */ return lhs; } } } /* ------------------------------------------------------------------------ */ /* * Parse a group of left-associative binary operators at the same * precedence level */ CTcPrsNode *CTcPrsOpBinGroup::parse() const { /* parse our left side - if that fails, return failure */ CTcPrsNode *lhs = left_->parse(); if (lhs == 0) return 0; /* keep going as long as we find one of our operators */ while (find_and_apply_op(&lhs)) ; /* return the expression tree */ return lhs; } /* * Find an apply one of our operators to the already-parsed left-hand * side. Returns true if we found an operator, false if not. */ int CTcPrsOpBinGroup::find_and_apply_op(CTcPrsNode **lhs) const { /* check each operator at this precedence level */ for (const CTcPrsOpBin *const *op = ops_ ; *op != 0 ; ++op) { /* check this operator's token */ if (G_tok->cur() == (*op)->get_op_tok()) { /* skip the operator token */ G_tok->next(); /* parse the right-hand side */ CTcPrsNode *rhs = right_->parse(); if (rhs == 0) { /* error - cancel the entire expression */ *lhs = 0; return FALSE; } /* try folding our subnodes into a constant value */ CTcPrsNode *const_tree = (*op)->eval_constant(*lhs, rhs); /* * if we couldn't calculate a constant value, build the tree * normally */ if (const_tree == 0) { /* * build my tree, replacing the original left-hand side * with the new expression */ *lhs = (*op)->build_tree(*lhs, rhs); } else { /* we got a constant value - use it as the result */ *lhs = const_tree; } /* * Tell the caller to proceed to parse any additional * occurrences of our operator - this will apply the next * occurrence of the operator as the left-hand side of the * new operator. */ return TRUE; } } /* * if we got here, we didn't find an operator - tell the caller that * we've reached the end of this operator's possible span */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Comparison operator group */ CTcPrsNode *CTcPrsOpBinGroupCompare::parse() const { /* parse our left side - if that fails, return failure */ CTcPrsNode *lhs = left_->parse(); if (lhs == 0) return 0; /* keep going as long as we find one of our operators */ for (;;) { /* * try one of our regular operators - if we find it, go back for * another round to see if there's another operator following * the next expression */ if (find_and_apply_op(&lhs)) continue; /* * check for the 'is in' operator - 'is' and 'in' aren't * keywords, so we must check for symbol tokens with the text of * these context-sensitive keywords */ if (G_tok->cur() == TOKT_SYM && G_tok->getcur()->text_matches("is", 2)) { /* we have 'is' - get the next token and check if it's 'in' */ if (G_tok->next() == TOKT_SYM && G_tok->getcur()->text_matches("in", 2)) { /* scan the expression list */ CTPNArglist *rhs = parse_inlist(); if (rhs == 0) return 0; /* build the node */ lhs = new CTPNIsIn(lhs, rhs); /* * we've applied the 'is in' operator - go back for * another operator from the comparison group */ continue; } else { /* it's not 'is in' - throw back the token and keep looking */ G_tok->unget(); } } /* * Check for the 'not in' operator */ if (G_tok->cur() == TOKT_SYM && G_tok->getcur()->text_matches("not", 3)) { /* we have 'is' - get the next token and check if it's 'in' */ if (G_tok->next() == TOKT_SYM && G_tok->getcur()->text_matches("in", 2)) { /* scan the expression list */ CTPNArglist *rhs = parse_inlist(); if (rhs == 0) return 0; /* build the node */ lhs = new CTPNNotIn(lhs, rhs); /* * we've applied the 'is in' operator - go back for * another operator from the comparison group */ continue; } else { /* it's not 'is in' - throw back the token and keep looking */ G_tok->unget(); } } /* we didn't find any of our operators - we're done */ break; } /* return the expression */ return lhs; } /* * parse the list for the right-hand side of an 'is in' or 'not in' * expression */ CTPNArglist *CTcPrsOpBinGroupCompare::parse_inlist() const { int argc; CTPNArg *arg_head; CTPNArg *arg_tail; /* skip the second keyword token, and check for an open paren */ if (G_tok->next() == TOKT_LPAR) { /* skip the paren */ G_tok->next(); } else { /* * log an error, and keep going on the assumption that it was * merely omitted and the rest of the list is well-formed */ G_tok->log_error_curtok(TCERR_IN_REQ_LPAR); } /* keep going until we find the close paren */ for (argc = 0, arg_head = arg_tail = 0 ;; ) { CTcPrsNode *expr; CTPNArg *arg_cur; /* if this is the close paren, we're done */ if (G_tok->cur() == TOKT_RPAR) break; /* parse this expression */ expr = S_op_asi.parse(); if (expr == 0) return 0; /* count the argument */ ++argc; /* create a new argument node */ arg_cur = new CTPNArg(expr); /* * link the new node at the end of our list (this preserves the * order of the original list) */ if (arg_tail != 0) arg_tail->set_next_arg(arg_cur); else arg_head = arg_cur; arg_tail = arg_cur; /* we need to be looking at a comma or right paren */ if (G_tok->cur() == TOKT_RPAR) { /* that's the end of the list */ break; } else if (G_tok->cur() == TOKT_COMMA) { /* skip the comma and parse the next argument */ G_tok->next(); } else { /* * If we're at the end of the file, there's no point * proceding, so return failure. If we've reached something * that looks like a statement separator (semicolon, curly * brace), also return failure, since the problem is clearly * a missing right paren. Otherwise, assume that a comma * was missing and continue as though we have another * argument. */ switch(G_tok->cur()) { default: /* log an error */ G_tok->log_error_curtok(TCERR_EXPECTED_IN_COMMA); /* * if we're at the end of file, return what we have so * far; otherwise continue, assuming that they merely * left out a comma between two argument expressions */ if (G_tok->cur() == TOKT_EOF) return new CTPNArglist(argc, arg_head); break; case TOKT_SEM: case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_DSTR_MID: case TOKT_DSTR_END: /* * we're apparently at the end of the statement; flag * the error as a missing right paren, and return what * we have so far */ G_tok->log_error_curtok(TCERR_EXPECTED_IN_RPAR); return new CTPNArglist(argc, arg_head); } } } /* skip the closing paren */ G_tok->next(); /* create and return the argument list descriptor */ return new CTPNArglist(argc, arg_head); } /* ------------------------------------------------------------------------ */ /* * Comma Operator */ /* * try to evaluate a constant expression */ CTcPrsNode *CTcPrsOpComma::eval_constant(CTcPrsNode *left, CTcPrsNode *right) const { /* * if both sides are constants, the result is the constant on the * right side; we can't simply fold down to a right-side constant if * the left side is not constant, though, because we must still * evaluate the left side at run-time for any possible side effects */ if (left->is_const() && right->is_const()) { /* both are constants - simply return the right constant value */ return right; } else { /* * one or the other is non-constant, so we can't fold the * expression - return null to so indicate */ return 0; } } /* * build a subtree for the comma operator */ CTcPrsNode *CTcPrsOpComma::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNComma(left, right); } /* ------------------------------------------------------------------------ */ /* * logical OR operator */ /* * try to evaluate a constant expression */ CTcPrsNode *CTcPrsOpOr::eval_constant(CTcPrsNode *left, CTcPrsNode *right) const { /* check for constants */ if (left->is_const()) { CTcPrsNode *ret; /* * Check for constants. If the first expression is constant, * the result will always be either 'true' (if the first * expression's constant value is true), or the value of the * second expression (if the first expression's constant value * is 'nil'). * * Note that it doesn't matter whether or not the right side is * a constant. If the left is true, the right will never be * executed because of the short-circuit logic; if the left is * nil, the result will always be the result of the right value. */ if (left->get_const_val()->get_val_bool()) { /* * the left is true, so the result is always true, and the * right never gets executed */ ret = left; } else { /* the left is nil, so the result is the right value */ ret = right; } /* ensure the result is a boolean value */ if (ret->is_const()) { /* make it a true/nil constant value */ ret->get_const_val() ->set_bool(ret->get_const_val()->get_val_bool()); } else { /* boolean-ize the value at run-time as needed */ ret = new CTPNBoolize(ret); } /* return the result */ return ret; } else { /* * one or the other is non-constant, so we can't fold the * expression - return null to so indicate */ return 0; } } /* * build the subtree */ CTcPrsNode *CTcPrsOpOr::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNOr(left, right); } /* ------------------------------------------------------------------------ */ /* * logical AND operator */ /* * try to evaluate a constant expression */ CTcPrsNode *CTcPrsOpAnd::eval_constant(CTcPrsNode *left, CTcPrsNode *right) const { /* * Check for constants. If the first expression is constant, the * result will always be either 'nil' (if the first expression's * constant value is nil), or the value of the second expression (if * the first expression's constant value is 'true'). * * Note that it doesn't matter whether or not the right side is a * constant. If the left is nil, the right will never be executed * because of the short-circuit logic; if the left is true, the * result will always be the result of the right value. */ if (left->is_const()) { CTcPrsNode *ret; /* * The left value is a constant, so we can eliminate the &&. If * the left value is nil, the result is nil; otherwise, it's the * right half. */ if (left->get_const_val()->get_val_bool()) { /* the left side is true - the result is the right side */ ret = right; } else { /* * The left side is nil - the result is nil, and the right * side never gets executed. */ ret = left; } /* ensure the result is a boolean value */ if (ret->is_const()) { /* make it a true/nil constant value */ ret->get_const_val() ->set_bool(ret->get_const_val()->get_val_bool()); } else { /* boolean-ize the value at run-time as needed */ ret = new CTPNBoolize(ret); } /* return the result */ return ret; } else { /* * one or the other is non-constant, so we can't fold the * expression - return null to so indicate */ return 0; } } /* * build the subtree */ CTcPrsNode *CTcPrsOpAnd::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNAnd(left, right); } /* ------------------------------------------------------------------------ */ /* * Generic Comparison Operator parser base class */ /* * evaluate a constant expression */ CTcPrsNode *CTcPrsOpRel::eval_constant(CTcPrsNode *left, CTcPrsNode *right) const { /* check for constants */ if (left->is_const() && right->is_const()) { /* get the types */ CTcConstVal *c1 = left->get_const_val(); CTcConstVal *c2 = right->get_const_val(); tc_constval_type_t typ1 = c1->get_type(); tc_constval_type_t typ2 = c2->get_type(); /* determine what we're comparing */ int sense; if (typ1 == TC_CVT_INT && typ2 == TC_CVT_INT) { /* get the values */ long val1 = c1->get_val_int(); long val2 = c2->get_val_int(); /* calculate the sense of the integer comparison */ sense = (val1 < val2 ? -1 : val1 == val2 ? 0 : 1); } else if (typ1 == TC_CVT_SSTR && typ2 == TC_CVT_SSTR) { /* compare the string values */ sense = strcmp(c1->get_val_str(), c2->get_val_str()); } else if (typ1 == TC_CVT_FLOAT && typ2 == TC_CVT_FLOAT) { /* both are floats - get as BigNumber values */ vbignum_t a(c1->get_val_float(), c1->get_val_float_len(), 0); vbignum_t b(c2->get_val_float(), c2->get_val_float_len(), 0); sense = a.compare(b); } else if (typ1 == TC_CVT_FLOAT && typ2 == TC_CVT_INT) { /* float vs int */ vbignum_t a(c1->get_val_float(), c1->get_val_float_len(), 0); vbignum_t b(c2->get_val_int()); sense = a.compare(b); } else if (typ1 == TC_CVT_INT && typ2 == TC_CVT_FLOAT) { vbignum_t a(c1->get_val_int()); vbignum_t b(c2->get_val_float(), c2->get_val_float_len(), 0); sense = a.compare(b); } else { /* these types are incomparable */ G_tok->log_error(TCERR_CONST_BAD_COMPARE, G_tok->get_op_text(get_op_tok())); return 0; } /* set the result in the left value */ c1->set_bool(get_bool_val(sense)); /* return the updated left value */ return left; } else { /* * one or the other is non-constant, so we can't fold the * expression - return null to so indicate */ return 0; } } /* ------------------------------------------------------------------------ */ /* * greater-than operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpGt::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNGt(left, right); } /* ------------------------------------------------------------------------ */ /* * less-than operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpLt::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNLt(left, right); } /* ------------------------------------------------------------------------ */ /* * greater-or-equal operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpGe::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNGe(left, right); } /* ------------------------------------------------------------------------ */ /* * less-or-equal operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpLe::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNLe(left, right); } /* ------------------------------------------------------------------------ */ /* * General equality/inequality operators base class */ /* * evaluate a constant expression */ CTcPrsNode *CTcPrsOpEqComp::eval_constant(CTcPrsNode *left, CTcPrsNode *right) const { int ops_equal; /* check for constants */ if (left->is_const() && right->is_const()) { /* both sides are constants - determine if they're equal */ ops_equal = left->get_const_val() ->is_equal_to(right->get_const_val()); /* set the result in the left value */ left->get_const_val()->set_bool(get_bool_val(ops_equal)); /* return the updated left value */ return left; } else if (left->is_addr() && right->is_addr()) { CTcConstVal cval; int comparable; /* * both sides are addresses - if they're both addresses of the * same subexpression, then the values are comparable as * compile-time constants */ ops_equal = ((CTPNAddr *)left) ->is_addr_eq((CTPNAddr *)right, &comparable); /* if they're not comparable, we can't fold this as a constant */ if (!comparable) return 0; /* generate the appropriate boolean result for the comparison */ cval.set_bool(get_bool_val(ops_equal)); /* return a new constant node with the result */ return new CTPNConst(&cval); } else { /* * one or the other is non-constant, so we can't fold the * expression - return null to so indicate */ return 0; } } /* ------------------------------------------------------------------------ */ /* * equality operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpEq::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNEq(left, right); } /* ------------------------------------------------------------------------ */ /* * inequality operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpNe::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNNe(left, right); } /* ------------------------------------------------------------------------ */ /* * 'is in' operator */ /* * construct */ CTPNIsInBase::CTPNIsInBase(CTcPrsNode *lhs, class CTPNArglist *rhs) : CTPNBin(lhs, rhs) { /* presume we don't have a constant value */ const_true_ = FALSE; } /* * fold constants */ CTcPrsNode *CTPNIsInBase::fold_binop() { CTPNArglist *lst; CTPNArg *arg; CTPNArg *prv; CTPNArg *nxt; /* if the left-hand side isn't constant, there's nothing to do */ if (!left_->is_const()) return this; /* the right side is always an argument list */ lst = (CTPNArglist *)right_; /* look for the value in the arguments */ for (prv = 0, arg = lst->get_arg_list_head() ; arg != 0 ; arg = nxt) { /* remember the next argument, in case we eliminate this one */ nxt = arg->get_next_arg(); /* check to see if this argument is a constant */ if (arg->is_const()) { /* * This one's a constant, so check to see if we found the * left side value. If the left side equals this value, * note that we found the value. */ if (left_->get_const_val()->is_equal_to(arg->get_const_val())) { /* * The values are equal, so the result of the expression * is definitely 'true'. */ const_true_ = TRUE; /* * Because the 'is in' operator only evaluates operands * from the 'in' list until it finds one that matches, * any remaining operands will simply never be * evaluated. We can thus discard the rest of the * argument list. */ nxt = 0; } /* * We now know whether the left side equals this constant * list element. This is never going to change because both * values are constant, so there's no point in making this * same comparison over and over again at run-time. We can * thus eliminate this argument from the list. */ lst->set_argc(lst->get_argc() - 1); if (prv == 0) lst->set_arg_list_head(nxt); else prv->set_next_arg(nxt); } } /* * If the argument list is now completely empty, the result of the * expression is a constant. */ if (lst->get_arg_list_head() == 0) { /* set the left operand's value to our result */ left_->get_const_val()->set_bool(const_true_); /* return the constant value in place of the entire expression */ return left_; } /* we're not a constant, to return myself unchanged */ return this; } /* ------------------------------------------------------------------------ */ /* * 'not in' operator */ /* * construct */ CTPNNotInBase::CTPNNotInBase(CTcPrsNode *lhs, class CTPNArglist *rhs) : CTPNBin(lhs, rhs) { /* presume we don't have a constant value */ const_false_ = FALSE; } /* * fold constants for binary operator */ CTcPrsNode *CTPNNotInBase::fold_binop() { CTPNArglist *lst; CTPNArg *arg; CTPNArg *prv; CTPNArg *nxt; /* if the left-hand side isn't constant, there's nothing to do */ if (!left_->is_const()) return this; /* the right side is always an argument list */ lst = (CTPNArglist *)right_; /* look for the value in the arguments */ for (prv = 0, arg = lst->get_arg_list_head() ; arg != 0 ; arg = nxt) { /* remember the next argument, in case we eliminate this one */ nxt = arg->get_next_arg(); /* check to see if this argument is a constant */ if (arg->is_const()) { /* * This one's a constant, so check to see if we found the * left side value. If the left side equals this value, * note that we found the value. */ if (left_->get_const_val()->is_equal_to(arg->get_const_val())) { /* * The values are equal, so the result of the expression * is definitely 'nil'. */ const_false_ = TRUE; /* * Because the 'not in' operator only evaluates operands * from the 'in' list until it finds one that matches, * any remaining operands will simply never be * evaluated. We can thus discard the rest of the * argument list. */ nxt = 0; } /* * We now know whether the left side equals this constant * list element. This is never going to change because both * values are constant, so there's no point in making this * same comparison over and over again at run-time. We can * thus eliminate this argument from the list. */ lst->set_argc(lst->get_argc() - 1); if (prv == 0) lst->set_arg_list_head(nxt); else prv->set_next_arg(nxt); } } /* * If the argument list is now completely empty, the result of the * expression is a constant. */ if (lst->get_arg_list_head() == 0) { /* set the left operand's value to our result */ left_->get_const_val()->set_bool(!const_false_); /* return the constant value in place of the entire expression */ return left_; } /* we're not a constant, to return myself unchanged */ return this; } /* ------------------------------------------------------------------------ */ /* * General arithmetic operator base class */ /* * evaluate constant value */ CTcPrsNode *CTcPrsOpArith::eval_constant(CTcPrsNode *left, CTcPrsNode *right) const { /* check for constants */ if (left->is_const() && right->is_const()) { CTcConstVal *c1 = left->get_const_val(); CTcConstVal *c2 = right->get_const_val(); tc_constval_type_t typ1 = c1->get_type(); tc_constval_type_t typ2 = c2->get_type(); /* check for ints, floats, or a combination */ if (typ1 == TC_CVT_INT && typ2 == TC_CVT_INT) { /* calculate the int result */ int ov; long r = calc_result(c1->get_val_int(), c2->get_val_int(), ov); /* on overflow, recalculate using BigNumbers */ if (ov) { /* calculate the BigNumber result */ vbignum_t a(c1->get_val_int()); vbignum_t b(c2->get_val_int()); vbignum_t *c = calc_result(a, b); /* if successful, assign it back to the left operand */ if (c != 0) { c1->set_float(c, TRUE); delete c; } } else { /* success - assign the folded int to the left operand */ c1->set_int(r); } } else if (typ1 == TC_CVT_FLOAT && typ2 == TC_CVT_FLOAT) { /* calculate the BigNumber result */ vbignum_t a(c1->get_val_float(), c1->get_val_float_len(), 0); vbignum_t b(c2->get_val_float(), c2->get_val_float_len(), 0); vbignum_t *c = calc_result(a, b); /* assign it back to the left operand */ if (c != 0) { /* save the value and delete the calculation result */ c1->set_float(c, c1->is_promoted() && c2->is_promoted()); delete c; /* check for possible demotion back to int */ c1->demote_float(); } } else if (typ1 == TC_CVT_FLOAT && typ2 == TC_CVT_INT) { /* calculate the BigNumber result */ vbignum_t a(c1->get_val_float(), c1->get_val_float_len(), 0); vbignum_t b(c2->get_val_int()); vbignum_t *c = calc_result(a, b); /* assign it back to the left operand */ if (c != 0) { /* save the value and delete the calculation result */ c1->set_float(c, c1->is_promoted()); delete c; /* check for possible demotion back to int */ c1->demote_float(); } } else if (typ1 == TC_CVT_INT && typ2 == TC_CVT_FLOAT) { /* calculate the BigNumber result */ vbignum_t a(c1->get_val_int()); vbignum_t b(c2->get_val_float(), c2->get_val_float_len(), 0); vbignum_t *c = calc_result(a, b); /* assign it back to the left operand */ if (c != 0) { /* save the value and delete the calculation result */ c1->set_float(c, c2->is_promoted()); delete c; /* check for possible demotion back to int */ c1->demote_float(); } } else { /* incompatible types - log an error */ G_tok->log_error(TCERR_CONST_BINARY_REQ_NUM, G_tok->get_op_text(get_op_tok())); return 0; } /* return the updated left value */ return left; } else { /* * one or the other is non-constant, so we can't fold the * expression - return null to so indicate */ return 0; } } /* ------------------------------------------------------------------------ */ /* * bitwise OR operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpBOr::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNBOr(left, right); } /* ------------------------------------------------------------------------ */ /* * bitwise AND operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpBAnd::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNBAnd(left, right); } /* ------------------------------------------------------------------------ */ /* * bitwise XOR operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpBXor::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNBXor(left, right); } /* ------------------------------------------------------------------------ */ /* * shift left operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpShl::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNShl(left, right); } /* ------------------------------------------------------------------------ */ /* * arithmetic shift right operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpAShr::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNAShr(left, right); } /* ------------------------------------------------------------------------ */ /* * logical shift right operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpLShr::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNLShr(left, right); } /* ------------------------------------------------------------------------ */ /* * multiplication operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpMul::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNMul(left, right); } /* * calculate a constant float result */ vbignum_t *CTcPrsOpMul::calc_result( const vbignum_t &a, const vbignum_t &b) const { return a * b; } /* ------------------------------------------------------------------------ */ /* * division operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpDiv::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNDiv(left, right); } /* * evaluate constant integer result */ long CTcPrsOpDiv::calc_result(long a, long b, int &ov) const { /* check for divide-by-zero */ if (b == 0) { /* log a divide-by-zero error */ G_tok->log_error(TCERR_CONST_DIV_ZERO); /* the result isn't really meaningful, but return something anyway */ return 1; } /* * check for overflow - there's only one way that integer division can * overflow, which is to divide INT32MINVAL by -1 */ ov = (a == INT32MINVAL && b == 1); /* return the result */ return a / b; } /* * calculate a constant float result */ vbignum_t *CTcPrsOpDiv::calc_result( const vbignum_t &a, const vbignum_t &b) const { /* check for division by zero */ if (b.is_zero()) { /* log an error, and just return the left operand */ G_tok->log_error(TCERR_CONST_DIV_ZERO); return new vbignum_t(1L); } else { /* calculate the result */ return a / b; } } /* ------------------------------------------------------------------------ */ /* * modulo operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpMod::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNMod(left, right); } /* * evaluate constant result */ long CTcPrsOpMod::calc_result(long a, long b, int &ov) const { /* there's no way for integer modulo to overflow */ ov = FALSE; /* check for divide-by-zero */ if (b == 0) { /* log a divide-by-zero error */ G_tok->log_error(TCERR_CONST_DIV_ZERO); /* the result isn't really meaningful, but return something anyway */ return 1; } else { /* return the result */ return a % b; } } /* * calculate a constant float result */ vbignum_t *CTcPrsOpMod::calc_result( const vbignum_t &a, const vbignum_t &b) const { /* check for division by zero */ if (b.is_zero()) { /* log an error, and just return the left operand */ G_tok->log_error(TCERR_CONST_DIV_ZERO); return new vbignum_t(1L); } else { /* calculate the result */ return a % b; } } /* ------------------------------------------------------------------------ */ /* * subtraction operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpSub::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNSub(left, right); } /* * evaluate a constant value */ CTcPrsNode *CTcPrsOpSub::eval_constant(CTcPrsNode *left, CTcPrsNode *right) const { if (left->is_const() && right->is_const()) { tc_constval_type_t typ1, typ2; /* get the types */ typ1 = left->get_const_val()->get_type(); typ2 = right->get_const_val()->get_type(); /* check our types */ if ((typ1 == TC_CVT_INT || typ1 == TC_CVT_FLOAT) && (typ2 == TC_CVT_INT || typ2 == TC_CVT_FLOAT)) { /* int/float minus int/float - use the inherited handling */ return CTcPrsOpArith::eval_constant(left, right); } else if (typ1 == TC_CVT_LIST) { /* get the original list */ CTPNList *lst = left->get_const_val()->get_val_list(); /* * if the right side is a list, remove each element of that * list from the list on the left; otherwise, remove the * value on the right from the list on the left */ if (typ2 == TC_CVT_LIST) { /* remove each element of the rhs list from the lhs list */ CTPNListEle *ele; /* scan the list, adding each element */ for (ele = right->get_const_val() ->get_val_list()->get_head() ; ele != 0 ; ele = ele->get_next()) { /* add this element's underlying expression value */ lst->remove_element(ele->get_expr()->get_const_val()); } } else { /* remove the rhs value from the lhs list */ lst->remove_element(right->get_const_val()); } } else if (typ1 == TC_CVT_OBJ) { /* * assume it's an overloaded operator, in which case constant * evaluation isn't possible, but it could still be a legal * expression */ return 0; } else { /* these types are incompatible - log an error */ G_tok->log_error(TCERR_CONST_BINMINUS_INCOMPAT); return 0; } /* return the updated left side */ return left; } else { /* they're not constant - we can't generate a constant result */ return 0; } } /* * calculate a constant float result */ vbignum_t *CTcPrsOpSub::calc_result( const vbignum_t &a, const vbignum_t &b) const { return a - b; } /* ------------------------------------------------------------------------ */ /* * addition operator */ /* * evaluate constant value */ CTcPrsNode *CTcPrsOpAdd::eval_constant(CTcPrsNode *left, CTcPrsNode *right) const { /* check for constants */ if (left->is_const() && right->is_const()) { tc_constval_type_t typ1, typ2; /* get the types */ typ1 = left->get_const_val()->get_type(); typ2 = right->get_const_val()->get_type(); /* check our types */ if ((typ1 == TC_CVT_INT || typ1 == TC_CVT_FLOAT) && (typ2 == TC_CVT_INT || typ2 == TC_CVT_FLOAT)) { /* int/float plus int/float - use the inherited handling */ return CTcPrsOpArith::eval_constant(left, right); } else if (typ1 == TC_CVT_LIST) { /* get the original list */ CTPNList *lst = left->get_const_val()->get_val_list(); /* * if the right side is also a list, concatenate it onto the * left list; otherwise, just add the right side as a new * element to the existing list */ if (typ2 == TC_CVT_LIST) { CTPNListEle *ele; /* scan the list, adding each element */ for (ele = right->get_const_val() ->get_val_list()->get_head() ; ele != 0 ; ele = ele->get_next()) { /* add this element's underlying expression value */ lst->add_element(ele->get_expr()); } } else { /* add a new list element for the right side */ lst->add_element(right); } /* * this list is longer than the original(s); tell the parser * about it in case it's the longest list yet */ G_cg->note_list(lst->get_count()); } else if (typ1 == TC_CVT_SSTR || typ2 == TC_CVT_SSTR) { char buf1[128]; char buf2[128]; const char *str1, *str2; size_t len1, len2; char *new_str; /* if the second value is a list, we can't make a constant */ if (typ2 == TC_CVT_LIST) return 0; /* convert both values to strings if they're not already */ str1 = left->get_const_val() ->cvt_to_str(buf1, sizeof(buf1), &len1); str2 = right->get_const_val() ->cvt_to_str(buf2, sizeof(buf2), &len2); /* treat nil as an empty string */ if (typ2 == TC_CVT_NIL) str1 = "", len2 = 0; /* * if we couldn't convert one or the other, leave the result * non-constant */ if (str1 == 0 || str2 == 0) return 0; /* * allocate space in the node pool for the concatenation of * the two strings - if that fails, don't bother with the * concatenation */ new_str = (char *)G_prsmem->alloc(len1 + len2 + 1); if (new_str == 0) return 0; /* copy the two string values into the new space */ memcpy(new_str, str1, len1); memcpy(new_str + len1, str2, len2); new_str[len1 + len2] = '\0'; /* set the new value in the left node */ left->get_const_val()->set_sstr(new_str, len1 + len2); } else if (typ1 == TC_CVT_OBJ) { /* * assume it's an overloaded operator, in which case constant * evaluation isn't possible, but it could still be a legal * expression */ return 0; } else { /* these types are incompatible - log an error */ G_tok->log_error(TCERR_CONST_BINPLUS_INCOMPAT); return 0; } /* return the updated left value */ return left; } else { /* the values aren't constant, so the result isn't constant */ return 0; } } /* * calculate a constant float result */ vbignum_t *CTcPrsOpAdd::calc_result( const vbignum_t &a, const vbignum_t &b) const { return a + b; } /* * build the subtree */ CTcPrsNode *CTcPrsOpAdd::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNAdd(left, right); } /* ------------------------------------------------------------------------ */ /* * Assignment Operator Group */ /* * parse an assignment expression */ CTcPrsNode *CTcPrsOpAsi::parse() const { /* start by parsing a conditional subexpression */ CTcPrsNode *lhs = S_op_if.parse(); if (lhs == 0) return 0; /* get the next operator */ tc_toktyp_t curtyp = G_tok->cur(); /* check to see if it's an assignment operator of some kind */ switch(curtyp) { case TOKT_PLUSEQ: case TOKT_MINEQ: case TOKT_TIMESEQ: case TOKT_DIVEQ: case TOKT_MODEQ: case TOKT_ANDEQ: case TOKT_OREQ: case TOKT_XOREQ: case TOKT_SHLEQ: case TOKT_ASHREQ: case TOKT_LSHREQ: /* it's an assignment operator - process it */ break; default: /* check against the current simple-assignment operator */ if (curtyp == asi_op_) { /* it's an assignment operator - process it */ break; } else { /* * it's not an assignment - return the original * subexpression with no further elaboration */ return lhs; } } /* check for a valid lvalue */ if (!lhs->check_lvalue()) { /* log an error but continue parsing */ G_tok->log_error(TCERR_INVALID_LVALUE, G_tok->get_op_text(G_tok->cur())); } /* skip the assignment operator */ G_tok->next(); /* * Recursively parse an assignment subexpression. Do this * recursively rather than iteratively, because assignment operators * group right-to-left. By recursively parsing an assignment, our * right-hand side will contain all remaining assignment expressions * incorporated into it. */ CTcPrsNode *rhs = parse(); if (rhs == 0) return 0; /* build and return the result based on the operator type */ switch(curtyp) { case TOKT_PLUSEQ: lhs = new CTPNAddAsi(lhs, rhs); break; case TOKT_MINEQ: lhs = new CTPNSubAsi(lhs, rhs); break; case TOKT_TIMESEQ: lhs = new CTPNMulAsi(lhs, rhs); break; case TOKT_DIVEQ: lhs = new CTPNDivAsi(lhs, rhs); break; case TOKT_MODEQ: lhs = new CTPNModAsi(lhs, rhs); break; case TOKT_ANDEQ: lhs = new CTPNBAndAsi(lhs, rhs); break; case TOKT_OREQ: lhs = new CTPNBOrAsi(lhs, rhs); break; case TOKT_XOREQ: lhs = new CTPNBXorAsi(lhs, rhs); break; case TOKT_SHLEQ: lhs = new CTPNShlAsi(lhs, rhs); break; case TOKT_ASHREQ: lhs = new CTPNAShrAsi(lhs, rhs); break; case TOKT_LSHREQ: lhs = new CTPNLShrAsi(lhs, rhs); break; default: /* plain assignment operator */ lhs = new CTPNAsi(lhs, rhs); break; } /* return the result */ return lhs; } /* ------------------------------------------------------------------------ */ /* * Ifnil operator ?? */ CTcPrsNode *CTcPrsOpIfnil::parse() const { /* parse the conditional part */ CTcPrsNode *first = S_op_or.parse(); if (first == 0) return 0; /* if we're not looking at the '??' operator, we're done */ if (G_tok->cur() != TOKT_QQ) return first; /* skip the '??' operator */ G_tok->next(); /* * parse the second part, which can be any expression except ',', since * we have higher precedence than ',' */ CTcPrsNode *second = G_prs->parse_expr_or_dstr(FALSE); if (second == 0) return 0; /* if the first expression is constant, we can fold immediately */ if (first->is_const()) { /* if it's nil, yield the second value, otherwise yield the first */ return (first->get_const_val()->get_type() == TC_CVT_NIL ? second : first); } else { /* it's not a constant value - return a new if-nil node */ return new CTPNIfnil(first, second); } } /* ------------------------------------------------------------------------ */ /* * Tertiary Conditional Operator */ CTcPrsNode *CTcPrsOpIf::parse() const { /* parse the conditional part */ CTcPrsNode *first = S_op_ifnil.parse(); if (first == 0) return 0; /* if we're not looking at the '?' operator, we're done */ if (G_tok->cur() != TOKT_QUESTION) return first; /* skip the '?' operator */ G_tok->next(); /* * parse the second part, which can be any expression, including a * double-quoted string expression or a comma expression (even though * the '?:' operator overall has higher precedence than ',', we can't * steal away operands from a ',' before our ':' because that would * leave the ':' with nothing to go with) */ CTcPrsNode *second = G_prs->parse_expr_or_dstr(TRUE); if (second == 0) return 0; /* make sure we have the ':' after the second part */ if (G_tok->cur() != TOKT_COLON) { /* * log the error, but continue parsing as though we found the * ':' - if the ':' is simply missing, this will allow us to * recover and continue parsing the rest of the expression */ G_tok->log_error(TCERR_QUEST_WITHOUT_COLON); /* if we're at the end of file, there's no point in continuing */ if (G_tok->cur() == TOKT_EOF) return 0; } /* skip the ':' */ G_tok->next(); /* * parse the third part, which can be any other expression, including a * double-quoted string expression - but not a comma expression, since * we have higher precedence than ',' */ CTcPrsNode *third = G_prs->parse_expr_or_dstr(FALSE); if (third == 0) return 0; /* * If the condition is constant, we can choose the second or third * expression directly. It doesn't matter whether or not the second * and/or third parts are themselves constant, because a constant * condition means that we'll always execute only one of the * alternatives. */ if (first->is_const()) { /* * evaluate the conditional value as a true/false value, and * return the second part's constant if the condition is true, * or the third part's constant if the condition is false */ return (first->get_const_val()->get_val_bool() ? second : third); } else { /* it's not a constant value - return a new conditional node */ return new CTPNIf(first, second, third); } } /* ------------------------------------------------------------------------ */ /* * Bmbedded string expressions. */ /* * Embedded string tree generator. This abstracts the tree construction * for an embedded string, so that the same parser can be used for single * and double quoted strings. */ struct CTcEmbedBuilder { /* is this a double-quoted string builder? */ virtual int is_double() const = 0; /* create the initial node for the leading fixed part of the string */ virtual CTcPrsNode *lead_node() = 0; /* * Finish the tree. If we skipped creating a tree because we had an * empty string in the lead position, and we haven't added anything to * it, this should create a non-null node to represent the final tree, * so we know it's not an error. */ virtual CTcPrsNode *finish_tree(CTcPrsNode *cur) = 0; /* parse an embedded expression */ virtual CTcPrsNode *parse_expr() = 0; /* * Add an embedding: create a node combining the tree before an * expression, an embedded expression, and the current token * representing the continuation of the string after the expression. */ virtual CTcPrsNode *add_embedding(CTcPrsNode *cur, CTcPrsNode *sub) = 0; /* add a string segment (a "mid" or "end" segment) */ virtual CTcPrsNode *add_segment(CTcPrsNode *cur) = 0; /* string mid/end token types */ virtual tc_toktyp_t mid_tok() = 0; virtual tc_toktyp_t end_tok() = 0; }; /* * Embedded string tree generator for double-quoted strings. Double-quoted * strings with embeddings are converted to a series of comma expressions: * *. "one <> three <> five" *. -> "one ", say(two), " three ", say(four), " five"; * * Note that the embedded expressions are wrapped in "say()" calls, since * we have to make sure they generate output. Some embeddings will * generate output as a side effect rather than as a value result, but * that's fine; in that case they'll perform their side effect (i.e., * printing text) and then return nil, so the say() will add nothing to the * output stream. */ struct CTcEmbedBuilderDbl: CTcEmbedBuilder { /* we're the builder for double-quoted strings */ virtual int is_double() const { return TRUE; } /* string mid/end token types */ virtual tc_toktyp_t mid_tok() { return TOKT_DSTR_MID; } virtual tc_toktyp_t end_tok() { return TOKT_DSTR_END; } /* build the leading node */ virtual CTcPrsNode *lead_node() { /* * Create a node for the initial part of the string. This is just * an ordinary double-quoted string node. If the initial part of * the string is zero-length, don't create an initial node at all, * since this would just generate do-nothing code. */ if (G_tok->getcur()->get_text_len() != 0) { /* create the node for the initial part of the string */ return new CTPNDstr(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); } else { /* * the initial part of the string is empty, so we don't need a * node for this portion */ return 0; } } /* finish the tree */ virtual CTcPrsNode *finish_tree(CTcPrsNode *cur) { return (cur != 0 ? cur : new CTPNDstr("", 0)); } /* parse an embedded expression */ virtual CTcPrsNode *parse_expr() { /* * an expression embedded in a dstring can be any value expression, * or another dstring expression */ return G_prs->parse_expr_or_dstr(TRUE); } /* add another embedding */ virtual CTcPrsNode *add_embedding(CTcPrsNode *cur, CTcPrsNode *sub) { /* wrap the expresion in a "say()" embedding node */ sub = new CTPNDstrEmbed(sub); /* * Build a node representing everything so far: do this by * combining the sub-expression with everything preceding, using a * comma operator. This isn't necessary if there's nothing * preceding the sub-expression, since this means the * sub-expression itself is everything so far. */ if (cur != 0) return new CTPNComma(cur, sub); else return sub; } /* add a string segment */ virtual CTcPrsNode *add_segment(CTcPrsNode *cur) { /* * Combine the part so far with the next string segment, using a * comma operator. If the next string segment is empty, there's no * need to add anything for it. */ size_t len = G_tok->getcur()->get_text_len(); if (len != 0) { /* create a node for the new string segment */ CTcPrsNode *newstr = new CTPNDstr( G_tok->getcur()->get_text(), len); /* combine it into the part so far with a comma operator */ cur = new CTPNComma(cur, newstr); } /* return the new combined node */ return cur; } }; /* * Embedded string tree generator for single-quoted strings. * * Single-quoted strings with embeddings are translated as follows: * *. local a = 'one <> three <> five'; *. -> local a = ('one ' + (two) + ' three ' + (four) + ' five'); */ struct CTcEmbedBuilderSgl: CTcEmbedBuilder { /* we're the builder for single-quoted strings */ virtual int is_double() const { return FALSE; } /* string mid/end token types */ virtual tc_toktyp_t mid_tok() { return TOKT_SSTR_MID; } virtual tc_toktyp_t end_tok() { return TOKT_SSTR_END; } /* build the leading node */ virtual CTcPrsNode *lead_node() { /* * Create a string constant node for the first part of the string. * Note that we need this even if the string is empty, to guarantee * that the overall expression is treated as a string. */ return string_node(G_tok->getcur()); } virtual CTcPrsNode *finish_tree(CTcPrsNode *cur) { return (cur != 0 ? cur : string_node(0)); } /* create a constant string node from the current token */ CTcPrsNode *string_node(const CTcToken *tok) { CTcConstVal cval; cval.set_sstr(tok); return new CTPNConst(&cval); } /* parse an embedded expression */ virtual CTcPrsNode *parse_expr() { /* in a single-quoted string, we need a value expression */ return G_prs->parse_expr(); } /* add another embedding */ virtual CTcPrsNode *add_embedding(CTcPrsNode *cur, CTcPrsNode *sub) { /* * Combine the sub-expression with the preceding portion using an * addition operator. This will be interpreted as concatenation at * run-time since the left side is definitively a string. */ return new CTPNAdd(cur, sub); } /* add a string segment */ virtual CTcPrsNode *add_segment(CTcPrsNode *cur) { /* * Add the next string segment. We can omit it if it's zero * length, as this will add nothing to the result. */ if (G_tok->getcur()->get_text_len() != 0) { CTcConstVal cval; cval.set_sstr(G_tok->getcur()); cur = new CTPNAdd(cur, new CTPNConst(&cval)); } /* return the new combined node */ return cur; } }; /* * Embedding stack entry. Each nesting level keeps one of these structures * to track the enclosing constructs. */ struct CTcEmbedLevel { /* set up the level, linking to our parent */ CTcEmbedLevel(int typ, CTcEmbedLevel *parent) : typ(typ), parent(parent) { } /* level type */ int typ; /* level types */ static const int Outer = 0; static const int If = 1; static const int OneOf = 2; static const int FirstTime = 3; /* parent level */ CTcEmbedLevel *parent; /* find a type among my parents */ int is_in(int typ) { /* if this is my type or a parent's type, we're in this type */ return (typ == this->typ || (parent != 0 && parent->is_in(typ))); } }; /* * Parse an embedded expression in a string. This handles embedding for * both single-quoted and double-quoted strings, using the tree builder * object to handle the differences. The two have the same parsing syntax, * but they do generate different parse trees. */ CTcPrsNode *CTcPrsOpUnary::parse_embedding(CTcEmbedBuilder *b) { /* keep going until we reach the end of the string */ for (;;) { /* parse a series of embeddings */ int eos; CTcEmbedLevel level(CTcEmbedLevel::Outer, 0); CTcPrsNode *n = parse_embedding_list(b, eos, &level); /* if we encountered an error or the end of the string, we're done */ if (n == 0 || eos) return n; /* * We stopped parsing without reaching the end of the string, so we * must have encountered a token that terminates a nested structure * - <>, <>, <>, etc. Capture the current * embedding so we can check which type of end token it is. */ const char *open_kw; parse_embedded_end_tok(b, &level, &open_kw); G_tok->log_error(TCERR_BAD_EMBED_END_TOK, (int)G_tok->getcur()->get_text_len(), G_tok->getcur()->get_text(), open_kw); } } /* * Parse a series of embeddings */ CTcPrsNode *CTcPrsOpUnary::parse_embedding_list( CTcEmbedBuilder *b, int &eos, CTcEmbedLevel *parent) { /* we haven't reached the end of the string yet */ eos = FALSE; /* create the node for the leading string */ tc_toktyp_t curtyp = G_tok->cur(); CTcPrsNode *cur = b->lead_node(); G_tok->next(); /* * If we're starting with the final segment of the string, there's no * need to parse any further. We can enter with the final segment when * parsing sub-constructs such as <>. */ if (curtyp == b->end_tok()) { /* tell the caller we're at the end of the string */ eos = TRUE; /* return the lead node */ return b->finish_tree(cur); } /* keep going until we find the end of the string */ for (;;) { /* note the current error count */ int nerr = G_tcmain->get_error_count(); /* * Capture tokens up to the next string segment, so that we can * compare the tokens to the embedding templates. */ CTcEmbedTokenList *tl = G_prs->embed_toks_; capture_embedded(b, tl); /* * First, check the special built-in syntax: * *. <> *. <> *. <> */ CTcPrsNode *sub = 0; const char *open_kw = 0; int check_end = TRUE; if (tl->match("if *")) { /* parse the "if" list */ tl->unget(1, 0); sub = parse_embedded_if(b, FALSE, eos, parent); } else if (tl->match("unless *")) { /* parse the "unless" (which is just an inverted "if") */ tl->unget(1); sub = parse_embedded_if(b, TRUE, eos, parent); } else if (tl->match("one of")) { /* parse the "one of" list */ sub = parse_embedded_oneof(b, eos, parent); } else if (tl->match("first time")) { /* parse the "first time" */ sub = parse_embedded_firsttime(b, eos, parent); } else if (parse_embedded_end_tok(tl, parent, &open_kw)) { /* * We're at a terminator for a multi-part embedding construct. * Stop here and return what we have, with the token left at * the terminator. The caller is presumably parsing this * construct recursively, so it'll take it from here. */ tl->unget(); return b->finish_tree(cur); } else if (tl->getcnt() == 0) { /* * empty embedding, or we got here via error recovery; proceed * with the next segment without any embedding */ sub = 0; } else { /* * No special syntax, so treat it as an expression. First, * check for a sprintf format spec - e.g., <<%2d x>> */ CTcToken fmttok(TOKT_INVALID); if (tl->get_head()->gettyp() == TOKT_FMTSPEC) tl->unlink_head(&fmttok); /* parse the embedded expression */ sub = parse_embedded_expr(b, tl); if (sub == 0) return 0; /* we can't have an end token immediately after an expression */ check_end = FALSE; /* * if we have a format spec, wrap the expression in a * sprintf node as sprintf(fmtspec, sub) */ if (fmttok.gettyp() == TOKT_FMTSPEC) { /* * set up the argument list - fmtspec string, sub (note * that arg lists are built in reverse order) */ CTcConstVal fmtcv; fmtcv.set_sstr(&fmttok); CTPNArg *args = new CTPNArg(sub); args->set_next_arg(new CTPNArg(new CTPNConst(&fmtcv))); /* set up the sprintf call */ sub = new CTPNCall(new CTPNSym("sprintf", 7), new CTPNArglist(2, args)); } } /* if we have an embedding, add it */ if (sub != 0) cur = b->add_embedding(cur, sub); /* * after the expression, we must find either another string segment * with another embedded expression following, or the final string * segment; anything else is an error */ if (eos) { /* * We reached the end of the string in a sub-construct; there's * nothing more to add to the main string. Just return what we * have to the caller. */ return b->finish_tree(cur); } else if ((curtyp = G_tok->cur()) == b->mid_tok()) { /* * It's a string with yet another embedded expression. Add * this segment to the expression and proceed to the next * embedding. */ cur = b->add_segment(cur); G_tok->next(); } else if (curtyp == b->end_tok()) { /* * It's the last segment of the string. Add the final segment * to the string. */ cur = b->add_segment(cur); G_tok->next(); /* tell the caller we've reached the end, and return our tree */ eos = TRUE; return b->finish_tree(cur); } else if (check_end && parse_embedded_end_tok(b, parent, &open_kw)) { /* * We parsed a recursive list for <>, <>, etc. End * tokens are implied when missing, so an end token at one * level can implicitly end multiple levels. We stopped at * such a token without consuming it, so it's for our caller to * digest. Simply return what we have and let the caller take * it from here. */ return b->finish_tree(cur); } else if (G_tcmain->get_error_count() != nerr) { /* * We logged an error within a sub-expression, so it's not * surprising that we're not at a valid continuation token at * this point. Try syncing up with the source: look for a * continuation token or something that probably ends the * statement, such as a semicolon or right brace. */ for (;;) { /* if we're at a statement ender, stop here */ if (curtyp == TOKT_SEM || curtyp == TOKT_RBRACE) return b->finish_tree(cur); /* if at eof, return failure */ if (curtyp == TOKT_EOF) return 0; /* if we're at a continuation token, resume parsing */ if (curtyp == b->mid_tok() || curtyp == b->end_tok()) break; /* skip this token and keep looking for a sync point */ curtyp = G_tok->next(); } } else { /* anything else is invalid - log an error */ G_tok->log_error_curtok(TCERR_EXPECTED_STR_CONT); /* * Skip ahead until we find the next string segment or * something that looks like the end of the statement. */ tl->reset(); capture_embedded(b, tl); /* * If we stopped at the next segment, carry on. Otherwise, * assume they simply left off the end of the string and return * what we have. */ if (G_tok->cur() != b->mid_tok() && G_tok->cur() != b->end_tok()) return (cur != 0 ? cur : b->finish_tree(sub)); } } } /* * Parse a single embedded expression */ CTcPrsNode *CTcPrsOpUnary::parse_embedded_expr( CTcEmbedBuilder *b, CTcEmbedTokenList *tl) { /* search for a template for the expression */ for (CTcStrTemplate *st = G_prs->get_str_template_head() ; st != 0 ; st = st->nxt) { /* check for a match */ if (tl->match(st)) { /* * This template matches. Generate the code as a call to the * template's processor function. */ CTPNSymResolved *func = new CTPNSymResolved(st->func); /* * If the template has a '*', the tokens matching the star are * a sub-expression that we evaluate as the argument to the * function. Otherwise we call it with no arguments. */ CTPNArglist *arglist; if (st->star) { /* * If the template has any fixed tokens, reparse the * remaining tokens to see if they refer to a new template. * If the template was just '*', skip this, since we'd just * find the same match again. */ CTcPrsNode *sub; if (st->cnt > 1) { /* remove the tokens that matched the fixed part */ tl->reduce(st); /* reparse as a new embedded expression */ sub = parse_embedded_expr(b, tl); } else { /* the template is just '*', so parse a raw expression */ tl->unget(); sub = b->parse_expr(); } /* if we failed to parse a sub-expression, return failure */ if (sub == 0) return 0; /* create the argument list */ arglist = new CTPNArglist(1, new CTPNArg(sub)); } else { /* there's no argument list */ arglist = new CTPNArglist(0, 0); } /* create and return the call to the template function */ return new CTPNCall(func, arglist); } } /* * There's no template, so process it as an ordinary expression. Put * the captured token list back into the token stream, and parse an * expression node. */ tl->unget(); return b->parse_expr(); } /* * Capture an embedded expression to an embedded token list */ void CTcPrsOpUnary::capture_embedded(CTcEmbedBuilder *b, CTcEmbedTokenList *tl) { /* reset the list */ tl->reset(); /* we don't have any nested braces or parens yet */ int braces = 0, parens = 0, bracks = 0; /* run through the list */ for (;;) { /* check what we have */ switch (G_tok->cur()) { case TOKT_LBRACE: ++braces; break; case TOKT_RBRACE: /* * if we find an unbalanced close brace, assume that we've * reached the end of the statement without properly * terminating the string */ if (--braces < 0) return; break; case TOKT_LPAR: ++parens; break; case TOKT_RPAR: --parens; break; case TOKT_LBRACK: ++bracks; break; case TOKT_RBRACK: --bracks; break; case TOKT_SEM: /* * if we find a semicolon outside of braces, assume that we've * reached the end of the statement without properly * terminating the string */ if (braces == 0) return; break; case TOKT_EOF: return; default: /* stop if we've reached the next string segment */ if (G_tok->cur() == b->end_tok() || G_tok->cur() == b->mid_tok()) return; break; } /* add the token to the capture list and move on to the next */ tl->add_tok(G_tok->getcur()); G_tok->next(); } } /* * Parse an embedded "if" or "unless" construct. The two are the same, * except the "unless" inverts the condition. */ CTcPrsNode *CTcPrsOpUnary::parse_embedded_if( CTcEmbedBuilder *b, int unless, int &eos, CTcEmbedLevel *parent) { /* set up our embedding stack level */ CTcEmbedLevel level(CTcEmbedLevel::If, parent); /* parse the condition expression */ CTcPrsNode *cond = G_prs->parse_cond_expr(); if (cond == 0) return 0; /* if this is an "unless", invert the condition */ if (unless) cond = new CTPNNot(cond); /* parse the "then" list */ CTcPrsNode *then = parse_embedding_list(b, eos, &level); if (then == 0) return 0; /* * create our top-level 'if' node - leave the 'else' branch empty for * now; we'll build this out if we find an 'else' */ CTPNIf *top = new CTPNIf(cond, then, 0); /* * as we build out the 'else if' branches, we'll add elses to the tail * of the tree; this is currently the top node */ CTPNIf *tail = top; /* parse zero or more "else if" branches */ int found_else = FALSE; while (!eos && (G_tok->cur() == TOKT_ELSE || G_tok->cur_tok_matches("otherwise"))) { /* skip the "else"/"otherwise", and check for another "if" */ if (G_tok->next() == TOKT_IF || G_tok->cur_tok_matches("unless")) { /* * <> or <>. Note which sense * of the test we're using. */ unless = (G_tok->cur() != TOKT_IF); /* skip the "else"/"otherwise" and parse the condition */ G_tok->next(); cond = G_prs->parse_cond_expr(); if (cond == 0) return 0; /* if it's 'unless', invert the condition */ if (unless) cond = new CTPNNot(cond); /* parse the "then" list for this new branch */ then = parse_embedding_list(b, eos, &level); if (then == 0) return 0; /* add the new 'if' node to the 'else' of the tail of the tree */ CTPNIf *sub = new CTPNIf(cond, then, 0); tail->set_else(sub); /* this is the new tail, for adding the next 'else' */ tail = sub; } else { /* it's the final else/otherwise - parse the branch */ CTcPrsNode *sub = parse_embedding_list(b, eos, &level); if (sub == 0) return 0; /* add it as the else branch of the tail node of the tree */ tail->set_else(sub); /* no more clauses of the "if" can follow an "else" */ found_else = TRUE; break; } } /* * if we ended without an "else", add an implicit "nil" as the final * "else" branch */ if (!found_else) tail->set_else(b->finish_tree(0)); /* * If we're at an "end" token, this is the explicit close of the "if". * Otherwise, the "if" ended implicitly at the end of the string or at * a closing token for a containing structure. */ if (!eos && G_tok->cur_tok_matches("end")) G_tok->next(); /* return the condition tree */ return top; } /* * "one of" enders */ struct one_of_option { /* ending phrase */ const char *endph; /* list attribute string */ const char *attr; }; static one_of_option one_of_list[] = { { "purely at random", "rand" }, { "then purely at random", "seq,rand" }, { "at random" , "rand-norpt" }, { "then at random", "seq,rand-norpt" }, { "sticky random", "rand,stop" }, { "as decreasingly likely outcomes", "rand-wt" }, { "shuffled", "shuffle" }, { "then shuffled", "seq,shuffle" }, { "half shuffled", "shuffle2" }, { "then half shuffled", "seq,shuffle2" }, { "cycling", "seq" }, { "stopping", "seq,stop" } }; /* * Parse an embedded "one of" construct */ CTcPrsNode *CTcPrsOpUnary::parse_embedded_oneof( CTcEmbedBuilder *b, int &eos, CTcEmbedLevel *parent) { /* get the embedding token list capturer */ CTcEmbedTokenList *tl = G_prs->embed_toks_; /* set up our stack level */ CTcEmbedLevel level(CTcEmbedLevel::OneOf, parent); /* create a list node to hold the items */ CTPNList *lst = new CTPNList(); /* keep going until we get to the end of the list */ for (;;) { /* parse the next string or embedded expression */ CTcPrsNode *ele = parse_embedding_list(b, eos, &level); if (ele == 0) return 0; /* add this element to the list */ lst->add_element(ele); /* capture the ending token list */ tl->reset(); capture_embedded(b, tl); /* if we're not at an OR, this is the end of the list */ if (!tl->match("or") && !tl->match("||")) break; } /* * Search for a match to one of our endings. If we don't find one, use * "purely at random" as the default. */ const char *attrs = 0; for (size_t i = 0 ; i < countof(one_of_list) ; ++i) { /* check for a match */ if (tl->match(one_of_list[i].endph)) { /* got it - set this attribute and stop searching */ attrs = one_of_list[i].attr; break; } } /* * if we didn't find a match for the ending tokens, we must have the * ending for an enclosing structure; put the tokens back for the * enclosing structure to parse, and use "purely at random" as the * default */ if (attrs == 0) { tl->unget(); attrs = "rand"; } /* create the one-of list */ return create_oneof_node(b, lst, attrs); } /* * Create a one-of list node */ CTcPrsNode *CTcPrsOpUnary::create_oneof_node( CTcEmbedBuilder *b, CTPNList *lst, const char *attrs) { /* * If we're actually compiling (not just doing a symbol extraction * pass), create our list state object. This is an anonymous * TadsObject of class OneOfIndexGen, with the following properties: * *. numItems = number of items in the list (integer) *. listAttrs = our 'attrs' list attributes value (string) */ CTcSymObj *obj = 0; if (!G_prs->get_syntax_only()) { /* geneate our anonymous OneOfIndexGen instance */ obj = G_prs->add_gen_obj("OneOfIndexGen"); if (obj != 0) { G_prs->add_gen_obj_prop(obj, "numItems", lst->get_count()); G_prs->add_gen_obj_prop(obj, "listAttrs", attrs); } else { /* check to see if OneOfIndexGen is undefined */ CTcSymbol *cls = G_prs->get_global_symtab()->find( "OneOfIndexGen", 13); if (cls == 0 || cls->get_type() != TC_SYM_OBJ) G_tok->log_error(TCERR_ONEOF_REQ_GENCLS); } } /* return the <> node */ return new CTPNStrOneOf(b->is_double(), lst, obj); } /* * Parse an embedded "first time" structure */ CTcPrsNode *CTcPrsOpUnary::parse_embedded_firsttime( CTcEmbedBuilder *b, int &eos, CTcEmbedLevel *parent) { /* get the embedding token list capturer */ CTcEmbedTokenList *tl = G_prs->embed_toks_; /* set up our stack level */ CTcEmbedLevel level(CTcEmbedLevel::FirstTime, parent); /* parse the embedding */ CTcPrsNode *ele = parse_embedding_list(b, eos, &level); if (ele == 0) return 0; /* capture the ending token list */ tl->reset(); capture_embedded(b, tl); /* * check for our "only" list - if we don't match it, put the tokens * back, since they must be the ending list for an enclosing structure */ if (!tl->match("only")) tl->unget(); /* create this as equivalent to <>our item<><> */ CTPNList *lst = new CTPNList(); lst->add_element(ele); CTcConstVal cv; cv.set_sstr("", 0); lst->add_element(new CTPNConst(&cv)); /* create the <> node */ return create_oneof_node(b, lst, "seq,stop"); } /* * Check for an end token in an embedded expression construct. Reads from * the token stream, but restores the tokens when done. */ int CTcPrsOpUnary::parse_embedded_end_tok(CTcEmbedBuilder *b, CTcEmbedLevel *parent, const char **open_kw) { /* capture the current expression's token list */ CTcEmbedTokenList *tl = G_prs->embed_toks_; tl->reset(); capture_embedded(b, tl); /* parse the end token */ int ret = parse_embedded_end_tok(tl, parent, open_kw); /* un-get the captured tokens */ tl->unget(); /* return the result */ return ret; } /* * Check for an end token in an embedded expression construct. Compares * tokens in the given capture list. */ int CTcPrsOpUnary::parse_embedded_end_tok(CTcEmbedTokenList *tl, CTcEmbedLevel *parent, const char **open_kw) { /* presume we won't find a closing keyword */ *open_kw = "unknown"; /* * consider ending keywords to always be significant, regardless of * context, since these are unambiguous with valid expressions */ if (tl->match("else") || tl->match("else if *") || tl->match("else unless *")) { *open_kw = "<> or <>"; return TRUE; } /* check for "if" ending tokens - else, end */ if (tl->match("end") || tl->match("otherwise") || tl->match("otherwise if *") || tl->match("otherwise unless *")) { *open_kw = "<> or <>"; if (parent->is_in(CTcEmbedLevel::If)) return TRUE; } /* check for an "or", which delimits branches of a "one of" */ if (tl->match("or") || tl->match("||")) { *open_kw = "<>"; if (parent->is_in(CTcEmbedLevel::OneOf)) return TRUE; } /* check for "one of" ending tokens */ for (size_t i = 0 ; i < countof(one_of_list) ; ++i) { if (tl->match(one_of_list[i].endph)) { *open_kw = "<>"; if (parent->is_in(CTcEmbedLevel::OneOf)) return TRUE; break; } } /* check for an "only", which ends a "first time" */ if (tl->match("only")) { *open_kw = "<>"; if (parent->is_in(CTcEmbedLevel::FirstTime)) return TRUE; } /* didn't find a close token */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Embedded <> list in a string */ CTcPrsNode *CTPNStrOneOfBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { lst_ = (CTPNList *)lst_->adjust_for_dyn(info); return this; } CTcPrsNode *CTPNStrOneOfBase::fold_constants(CTcPrsSymtab *symtab) { lst_ = (CTPNList *)lst_->fold_constants(symtab); return this; } /* ------------------------------------------------------------------------ */ /* * Unary Operator Parser */ CTcPrsNode *CTcPrsOpUnary::parse() const { /* get the current token, which may be a prefix operator */ tc_toktyp_t op = G_tok->cur(); /* check for prefix operators */ switch(op) { case TOKT_AND: /* skip the '&' */ G_tok->next(); /* parse the address expression */ return parse_addr(); case TOKT_NOT: case TOKT_BNOT: case TOKT_PLUS: case TOKT_MINUS: case TOKT_INC: case TOKT_DEC: case TOKT_DELETE: { /* skip the operator */ G_tok->next(); /* * recursively parse the unary expression to which to apply the * operator */ CTcPrsNode *sub = parse(); if (sub == 0) return 0; /* apply the operator */ switch(op) { case TOKT_NOT: /* apply the NOT operator */ return parse_not(sub); case TOKT_BNOT: /* apply the bitwise NOT operator */ return parse_bnot(sub); case TOKT_PLUS: /* apply the unary positive operator */ return parse_pos(sub); case TOKT_MINUS: /* apply the unary negation operator */ return parse_neg(sub); case TOKT_INC: /* apply the pre-increment operator */ return parse_inc(TRUE, sub); case TOKT_DEC: /* apply the pre-decrement operator */ return parse_dec(TRUE, sub); case TOKT_DELETE: /* apply the deletion operator */ return parse_delete(sub); default: /* * we shouldn't ever get here, since this switch should * contain every case that brought us into it in the first * place */ return 0; } } default: /* it's not a unary prefix operator - parse a postfix expression */ return parse_postfix(TRUE, TRUE); } } /* * parse a unary NOT expression */ CTcPrsNode *CTcPrsOpUnary::parse_not(CTcPrsNode *subexpr) { /* try folding a constant value */ CTcPrsNode *ret = eval_const_not(subexpr); /* * if we got a constant result, return it; otherwise, create a NOT * node for code generation */ if (ret != 0) return ret; else return new CTPNNot(subexpr); } /* * evaluate a constant NOT expression */ CTcPrsNode *CTcPrsOpUnary::eval_const_not(CTcPrsNode *subexpr) { /* * if the underlying expression is a constant value, apply the * operator */ if (subexpr->is_const()) { /* set the new value */ subexpr->get_const_val() ->set_bool(!subexpr->get_const_val()->get_val_bool()); /* return the modified constant value */ return subexpr; } /* the result is not constant */ return 0; } /* * parse a unary bitwise NOT expression */ CTcPrsNode *CTcPrsOpUnary::parse_bnot(CTcPrsNode *subexpr) { /* * if the underlying expression is a constant value, apply the * operator */ if (subexpr->is_const()) { /* we need an integer - log an error if it's not */ if (subexpr->get_const_val()->get_type() != TC_CVT_INT) G_tok->log_error(TCERR_CONST_UNARY_REQ_NUM, G_tok->get_op_text(TOKT_BNOT)); else subexpr->get_const_val() ->set_int(~subexpr->get_const_val()->get_val_int()); /* return the updated value */ return subexpr; } /* create the bitwise NOT node */ return new CTPNBNot(subexpr); } /* * parse a unary address expression */ CTcPrsNode *CTcPrsOpUnary::parse_addr() const { /* * if it's a simple symbol, create an unresolved symbol node for it; * otherwise parse the entire expression */ CTcPrsNode *subexpr; if (G_tok->cur() == TOKT_SYM) { /* * create an unresolved symbol node - we'll resolve this during * code generation */ const CTcToken *tok = G_tok->getcur(); subexpr = new CTPNSym(tok->get_text(), tok->get_text_len()); /* * If the symbol is undefined, assume it's a property, but make the * definition "weak". Starting in 3.1, &x can be a property ID, a * function pointer, or a built-in function pointer, so it's no * longer safe to just assume it's a property. If we don't see the * symbol used in any other context, we'll assume it's a property, * but mark it as "weak" if it's not already defined. This will * allow any conflicting definition to override the property * assumption without complaint. */ G_prs->get_global_symtab()->find_or_def_prop_weak( tok->get_text(), tok->get_text_len(), FALSE); /* skip the symbol */ G_tok->next(); } else if (G_tok->cur() == TOKT_OPERATOR) { /* operator property - parse the operator name */ CTcToken proptok; if (G_prs->parse_op_name(&proptok)) { /* skip the last token of the operator name */ G_tok->next(); } /* create a symbol for it */ subexpr = new CTPNSym(proptok.get_text(), proptok.get_text_len()); } else { /* parse an expression */ subexpr = parse(); if (subexpr == 0) return 0; } /* * The underlying expression must be something that has an address; * if it's not, it's an error. */ if (!subexpr->has_addr()) { /* * can't take the address of the subexpression - log an error, * but continue parsing the expression anyway */ G_tok->log_error(TCERR_NO_ADDRESS); } /* create the address node */ return new CTPNAddr(subexpr); } /* * parse a unary arithmetic positive expression */ CTcPrsNode *CTcPrsOpUnary::parse_pos(CTcPrsNode *subexpr) { /* * if the underlying expression is a constant value, apply the * operator */ if (subexpr->is_const()) { /* if it's a float, a unary '+' has no effect at all */ if (subexpr->get_const_val()->get_type() == TC_CVT_FLOAT) return subexpr; /* we need an integer - log an error if it's not */ if (subexpr->get_const_val()->get_type() != TC_CVT_INT) G_tok->log_error(TCERR_CONST_UNARY_REQ_NUM, G_tok->get_op_text(TOKT_PLUS)); /* * positive-ing a value doesn't change the value, so return the * original constant */ return subexpr; } /* create the unary positive node */ return new CTPNPos(subexpr); } /* * parse a unary arithmetic negation expression */ CTcPrsNode *CTcPrsOpUnary::parse_neg(CTcPrsNode *subexpr) { /* * if the underlying expression is a constant value, apply the * operator */ if (subexpr->is_const()) { /* we need an integer or float */ CTcConstVal *cval = subexpr->get_const_val(); if (cval->get_type() == TC_CVT_INT) { /* * Negating an integer. There's a special overflow case to * check for: if we're negating INT32MINVAL, the result doesn't * fit in a signed integer, so we need to promote it to float. */ long l = cval->get_val_int(); if (l <= INT32MINVAL) { /* overflow - promote to BigNumber */ vbignum_t b((ulong)2147483648U); cval->set_float(&b, TRUE); } else { /* set the value negative in the subexpression */ cval->set_int(-l); } } else if (subexpr->get_const_val()->get_type() == TC_CVT_FLOAT) { /* get the original value */ const char *ctxt = cval->get_val_float(); size_t clen = cval->get_val_float_len(); /* if the old value is negative, simply remove the sign */ if (clen > 0 && ctxt[0] == '-') { /* keep the old value, removing the sign */ cval->set_float(ctxt + 1, clen - 1, cval->is_promoted()); } else { /* allocate new buffer for the float text plus a '-' */ char *new_txt = (char *)G_prsmem->alloc(clen + 1); /* insert the minus sign */ new_txt[0] = '-'; /* add the original string */ memcpy(new_txt + 1, ctxt, clen); /* update the subexpression's constant value to the new text */ cval->set_float(new_txt, clen + 1, cval->is_promoted()); } /* check for possible demotion back to int */ cval->demote_float(); } else { /* log the error */ G_tok->log_error(TCERR_CONST_UNARY_REQ_NUM, G_tok->get_op_text(TOKT_MINUS)); } /* return the modified constant value */ return subexpr; } /* create the unary negation node */ return new CTPNNeg(subexpr); } /* * parse a pre-increment expression */ CTcPrsNode *CTcPrsOpUnary::parse_inc(int pre, CTcPrsNode *subexpr) { /* require an lvalue */ if (!subexpr->check_lvalue()) { /* log an error, but continue parsing */ G_tok->log_error(TCERR_INVALID_UNARY_LVALUE, G_tok->get_op_text(TOKT_INC)); } /* apply the increment operator */ if (pre) return new CTPNPreInc(subexpr); else return new CTPNPostInc(subexpr); } /* * parse a pre-decrement expression */ CTcPrsNode *CTcPrsOpUnary::parse_dec(int pre, CTcPrsNode *subexpr) { /* require an lvalue */ if (!subexpr->check_lvalue()) { /* log an error, but continue parsing */ G_tok->log_error(TCERR_INVALID_UNARY_LVALUE, G_tok->get_op_text(TOKT_DEC)); } /* apply the pre-increment operator */ if (pre) return new CTPNPreDec(subexpr); else return new CTPNPostDec(subexpr); } /* * parse a unary allocation expression */ CTcPrsNode *CTcPrsOpUnary::parse_new(CTcPrsNode *subexpr, int is_transient) { /* create the allocation node */ return new CTPNNew(subexpr, is_transient); } /* * parse a unary deletion expression */ CTcPrsNode *CTcPrsOpUnary::parse_delete(CTcPrsNode *subexpr) { /* the delete operator is obsolete in TADS 3 - warn about it */ if (!G_prs->get_syntax_only()) G_tok->log_warning(TCERR_DELETE_OBSOLETE); /* create the deletion node */ return new CTPNDelete(subexpr); } /* * parse a postfix expression */ CTcPrsNode *CTcPrsOpUnary::parse_postfix(int allow_member_expr, int allow_call_expr) { /* parse a primary expression */ CTcPrsNode *sub = parse_primary(); if (sub == 0) return 0; /* keep going as long as we find postfix operators */ for (;;) { /* check for a postfix operator */ tc_toktyp_t op = G_tok->cur(); switch(op) { case TOKT_LPAR: /* left paren - function or method call */ if (allow_call_expr) { /* parse the function call expression */ sub = parse_call(sub); } else { /* call expressions aren't allowed - stop here */ return sub; } break; case TOKT_LBRACK: /* left square bracket - subscript */ sub = parse_subscript(sub); break; case TOKT_DOT: /* * Dot - member selection. If a member expression is allowed * by the caller, parse it; otherwise, just return the * expression up to this point. */ if (allow_member_expr) { /* * it's allowed - parse it and continue to look for other * postfix expressions following the member expression */ sub = parse_member(sub); } else { /* * member expressions aren't allowed - stop here, * returning the expression up to this point */ return sub; } break; case TOKT_INC: /* post-increment */ G_tok->next(); sub = parse_inc(FALSE, sub); break; case TOKT_DEC: /* post-decrement */ G_tok->next(); sub = parse_dec(FALSE, sub); break; default: /* it's not a postfix operator - return the result */ return sub; } /* if the last parse failed, return failure */ if (sub == 0) return 0; } } /* * Parse an argument list */ CTPNArglist *CTcPrsOpUnary::parse_arg_list() { /* skip the open paren */ G_tok->next(); /* keep going until we find the close paren */ int argc; CTPNArg *arg_head; for (argc = 0, arg_head = 0 ;; ) { /* presume it's not a named argument */ CTcToken param_name; param_name.settyp(TOKT_INVALID); /* if this is the close paren, we're done */ if (G_tok->cur() == TOKT_RPAR) break; /* check for a named argument: "symbol: expression" */ if (G_tok->cur() == TOKT_SYM) { /* remember the symbol, in case it is in fact a named argument */ param_name = *G_tok->getcur(); /* peek ahead - if a colon follows, it's a named argument */ if (G_tok->next() == TOKT_COLON) { /* * it's a named argument - we already have the name stashed * away for when we create the argument node, so simply * skip the colon so we can parse the argument value * expression */ G_tok->next(); } else { /* * No colon - it's a regular parameter expression. Put * back the token we peeked at, clear out the name token, * and keep parsing as normal. */ G_tok->unget(); param_name.settyp(TOKT_INVALID); } } /* * parse this argument expression - this is an 'assignment' * expression, since a comma is an argument separator in this * context rather than an operator */ CTcPrsNode *expr = S_op_asi.parse(); if (expr == 0) return 0; /* if this is a positional argument (not named), count it */ if (param_name.gettyp() != TOKT_SYM) ++argc; /* create a new argument node */ CTPNArg *arg_cur = new CTPNArg(expr); /* if it's a named argument, store the name in the argument node */ arg_cur->set_name(¶m_name); /* check to see if the argument is followed by an ellipsis */ if (G_tok->cur() == TOKT_ELLIPSIS) { /* this isn't allowed for a named argument */ if (param_name.gettyp() == TOKT_SYM) G_tok->log_error(TCERR_NAMED_ARG_NO_ELLIPSIS); /* skip the ellipsis */ G_tok->next(); /* mark the argument as a list-to-varargs parameter */ arg_cur->set_varargs(TRUE); } /* * Link the new node in at the beginning of our list - this will * ensure that the list is built in reverse order, which is the * order in which we push the arguments onto the stack. */ arg_cur->set_next_arg(arg_head); arg_head = arg_cur; /* we need to be looking at a comma, right paren, or ellipsis */ if (G_tok->cur() == TOKT_RPAR) { /* that's the end of the list */ break; } else if (G_tok->cur() == TOKT_COMMA) { /* skip the comma and parse the next argument */ G_tok->next(); } else { /* * If we're at the end of the file, there's no point * proceding, so return failure. If we've reached something * that looks like a statement separator (semicolon, curly * brace), also return failure, since the problem is clearly * a missing right paren. Otherwise, assume that a comma * was missing and continue as though we have another * argument. */ switch(G_tok->cur()) { default: /* log an error */ G_tok->log_error_curtok(TCERR_EXPECTED_ARG_COMMA); /* * if we're at the end of file, return what we have so * far; otherwise continue, assuming that they merely * left out a comma between two argument expressions */ if (G_tok->cur() == TOKT_EOF) return new CTPNArglist(argc, arg_head); break; case TOKT_SEM: case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_DSTR_MID: case TOKT_DSTR_END: /* * we're apparently at the end of the statement; flag * the error as a missing right paren, and return what * we have so far */ G_tok->log_error_curtok(TCERR_EXPECTED_ARG_RPAR); return new CTPNArglist(argc, arg_head); } } } /* skip the closing paren */ G_tok->next(); /* create and return the argument list descriptor */ return new CTPNArglist(argc, arg_head); } /* * Parse a function call expression */ CTcPrsNode *CTcPrsOpUnary::parse_call(CTcPrsNode *lhs) { /* parse the argument list */ CTPNArglist *arglist = parse_arg_list(); if (arglist == 0) return 0; /* check for the special "defined()" syntax */ if (lhs != 0 && lhs->sym_text_matches("defined", 7)) { /* make sure we have one argument that's a symbol */ CTcPrsNode *arg; if (arglist->get_argc() == 1 && (arg = arglist->get_arg_list_head()->get_arg_expr()) ->get_sym_text() != 0) { /* look up the symbol */ CTcSymbol *sym = G_prs->get_global_symtab()->find( arg->get_sym_text(), arg->get_sym_text_len()); /* * The result is a constant 'true' or 'nil' node, depending on * whether the symbol is defined. Note that this is a "compile * time constant" expression, not a true constant - flag it as * such so that we don't generate a warning if this value is * used as the conditional expression of an if, while, or for. */ CTcConstVal cval; cval.set_bool(sym != 0 && sym->get_type() != TC_SYM_UNKNOWN); cval.set_ctc(TRUE); return new CTPNConst(&cval); } else { /* invalid syntax */ G_tok->log_error(TCERR_DEFINED_SYNTAX); } } /* check for the special "__objref" syntax */ if (lhs != 0 && lhs->sym_text_matches("__objref", 8)) { /* assume we won't generate an error or warning if it's undefined */ int errhandling = 0; /* get the rightmost argument */ CTPNArg *curarg = arglist->get_arg_list_head(); /* if we have other than two arguments, it's an error */ if (arglist->get_argc() > 2) { G_tok->log_error(TCERR___OBJREF_SYNTAX); return lhs; } /* if we have two arguments, get the warning/error mode */ if (arglist->get_argc() == 2) { /* get the argument, and skip to the next to the left */ CTcPrsNode *arg = curarg->get_arg_expr(); curarg = curarg->get_next_arg(); /* it has to be "warn" or "error" */ if (arg->sym_text_matches("warn", 4)) { errhandling = 1; } else if (arg->sym_text_matches("error", 5)) { errhandling = 2; } else { G_tok->log_error(TCERR___OBJREF_SYNTAX); return lhs; } } /* the first argument must be a symbol */ CTcPrsNode *arg = curarg->get_arg_expr(); if (arg->get_sym_text() != 0) { /* look up the symbol */ CTcSymbol *sym = G_prs->get_global_symtab()->find( arg->get_sym_text(), arg->get_sym_text_len()); /* * The result is the object reference value if the symbol is * defined as an object, or nil if it's undefined or something * other than an object. */ CTcConstVal cval; if (sym != 0 && sym->get_type() == TC_SYM_OBJ) { /* it's an object - the result is the object symbol */ return arg; } else { /* log a warning or error, if applicable */ if (errhandling != 0 && !G_prs->get_syntax_only()) { /* note whether it's undefined or otherwise defined */ int errcode = (sym == 0 ? TCERR_UNDEF_SYM : TCERR_SYM_NOT_OBJ); /* log an error or warning, as desired */ if (errhandling == 1) G_tok->log_warning( errcode, (int)arg->get_sym_text_len(), arg->get_sym_text()); else G_tok->log_error( errcode, (int)arg->get_sym_text_len(), arg->get_sym_text()); } /* not defined or non-object - the result is nil */ cval.set_bool(FALSE); cval.set_ctc(TRUE); return new CTPNConst(&cval); } } else { /* invalid syntax */ G_tok->log_error(TCERR___OBJREF_SYNTAX); } } /* build and return the function call node */ return new CTPNCall(lhs, arglist); } /* * Parse a subscript expression */ CTcPrsNode *CTcPrsOpUnary::parse_subscript(CTcPrsNode *lhs) { /* skip the '[' */ G_tok->next(); /* parse the expression within the brackets */ CTcPrsNode *subscript = S_op_comma.parse(); if (subscript == 0) return 0; /* check for the matching ']' */ if (G_tok->cur() == TOKT_RBRACK) { /* got it - skip it */ G_tok->next(); } else { /* log an error, and forgive the missing ']' */ G_tok->log_error_curtok(TCERR_EXPECTED_SUB_RBRACK); } /* try folding constants */ CTcPrsNode *cval = eval_const_subscript(lhs, subscript); /* * if that worked, use the result; otherwise, build an expression * node to generate code for the subscript operator */ if (cval != 0) return cval; else return new CTPNSubscript(lhs, subscript); } /* * Evaluate a constant subscript value */ CTcPrsNode *CTcPrsOpUnary::eval_const_subscript( CTcPrsNode *lhs, CTcPrsNode *subscript) { /* assume we won't be able to evaluate this as a constant */ CTcPrsNode *c = 0; /* * if we're subscripting a constant list by a constant index value, * we can evaluate a constant result */ if (lhs->is_const() && subscript->is_const()) { /* check the type of value we're indexing */ switch (lhs->get_const_val()->get_type()) { case TC_CVT_LIST: /* * It's a constant list. Lists can only be indexed by integer * values. */ if (subscript->get_const_val()->get_type() == TC_CVT_INT) { /* * it's an integer - index the constant list by the * constant subscript to get the element value, which * replaces the entire list-and-index expression */ c = lhs->get_const_val()->get_val_list()->get_const_ele( subscript->get_const_val()->get_val_int()); } else { /* a list index must be an integer */ G_tok->log_error(TCERR_CONST_IDX_NOT_INT); } break; case TC_CVT_SSTR: case TC_CVT_OBJ: case TC_CVT_FUNCPTR: case TC_CVT_ANONFUNCPTR: case TC_CVT_FLOAT: /* * these types don't define indexing as a native operator, but * it's possible for this to be meaningful at run-time via * operator overloading; simply leave the constant expression * unevaluated so that we generate code to perform the index * operation at run-time */ break; default: /* other types definitely cannot be indexed */ G_tok->log_error(TCERR_CONST_IDX_INV_TYPE); break; } } /* return the constant result, if any */ return c; } /* * Parse a member selection ('.') expression. If no '.' is actually * present, then '.targetprop' is implied. */ CTcPrsNode *CTcPrsOpUnary::parse_member(CTcPrsNode *lhs) { CTcPrsNode *rhs; int rhs_is_expr; /* * If a '.' is present, skip it; otherwise, '.targetprop' is implied. */ if (G_tok->cur() == TOKT_DOT) { /* we have an explicit property expression - skip the '.' */ G_tok->next(); /* assume the property will be a simple symbol, not an expression */ rhs_is_expr = FALSE; /* we could have a symbol or a parenthesized expression */ switch(G_tok->cur()) { case TOKT_SYM: /* * It's a simple property name - create a symbol node. Note * that we must explicitly create an unresolved symbol node, * since we want to ignore any local variable with the same * name and look only in the global symbol table for a * property; we must hence defer resolving the symbol until * code generation. */ rhs = new CTPNSym(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* skip the symbol token */ G_tok->next(); /* proceed to check for an argument list */ break; case TOKT_LPAR: /* * It's a parenthesized expression - parse it. First, skip * the open paren - we don't want the sub-expression to go * beyond the close paren (if we didn't skip the open paren, * the open paren would be part of the sub-expression, hence * any postfix expression after the close paren would be * considered part of the sub-expression; this would be * invalid, since we might want to find a postfix actual * parameter list). */ G_tok->next(); /* remember that it's an expression */ rhs_is_expr = TRUE; /* parse the sub-expression */ rhs = S_op_comma.parse(); if (rhs == 0) return 0; /* require the close paren */ if (G_tok->cur() == TOKT_RPAR) { /* skip the closing paren */ G_tok->next(); } else { /* log the error */ G_tok->log_error_curtok(TCERR_EXPR_MISSING_RPAR); /* * if we're at a semicolon or end of file, we must be on * to the next statement - stop trying to parse this one * if so; otherwise, continue on the assumption that they * merely left out the close paren and what follows is * more expression for us to process */ if (G_tok->cur() == TOKT_SEM || G_tok->cur() == TOKT_EOF) return lhs; } break; case TOKT_TARGETPROP: /* * it's an unparenthesized "targetprop" expression - skip the * keyword */ G_tok->next(); /* * the property value is the result of evaluating * "targetprop", which is an expression */ rhs = new CTPNTargetprop(); rhs_is_expr = TRUE; /* note the reference to the extended method context */ G_prs->set_full_method_ctx_referenced(TRUE); /* go parse the rest */ break; default: /* anything else is invalid - log an error */ G_tok->log_error_curtok(TCERR_INVALID_PROP_EXPR); /* skip the errant token so we don't loop on it */ G_tok->next(); /* return what we have so far */ return lhs; } } else { /* there's no property specified, so '.targetprop' is implied */ rhs = new CTPNTargetprop(); rhs_is_expr = TRUE; /* * note the reference to the full method context (since * 'targetprop' is part of the extended method context beyond * 'self') */ G_prs->set_full_method_ctx_referenced(TRUE); } /* check for an argument list */ if (G_tok->cur() == TOKT_LPAR) { CTPNArglist *arglist; /* parse the argument list */ arglist = parse_arg_list(); if (arglist == 0) return 0; /* create and return a member-with-arguments node */ return new CTPNMemArg(lhs, rhs, rhs_is_expr, arglist); } else { /* * there's no argument list - create and return a simple member * node */ return new CTPNMember(lhs, rhs, rhs_is_expr); } } /* * Parse a primary expression */ CTcPrsNode *CTcPrsOpUnary::parse_primary() { CTcPrsNode *sub; CTcConstVal cval; tc_toktyp_t t; CTcToken tok, tok2; /* keep going until we find something interesting */ for (;;) { /* determine what we have */ switch(G_tok->cur()) { case TOKT_LBRACE: /* short form of anonymous function */ return parse_anon_func(TRUE, FALSE); case TOKT_METHOD: /* parse an anonymous method */ return parse_anon_func(FALSE, TRUE); case TOKT_FUNCTION: /* parse an anonymous function */ return parse_anon_func(FALSE, FALSE); case TOKT_OBJECT: /* in-line object definition */ return parse_inline_object(FALSE); case TOKT_NEW: /* skip the operator and check for 'function' or 'method' */ if ((t = G_tok->next()) == TOKT_FUNCTION) { /* it's an anonymous function definition - go parse it */ sub = parse_anon_func(FALSE, FALSE); } else if (t == TOKT_METHOD) { /* anonymous method */ sub = parse_anon_func(FALSE, TRUE); } else { /* check for the 'transient' keyword */ int trans = (G_tok->cur() == TOKT_TRANSIENT); if (trans) G_tok->next(); /* * it's an ordinary 'new' expression - parse the primary * making up the name */ sub = parse_primary(); /* if there's an argument list, parse the argument list */ if (G_tok->cur() == TOKT_LPAR) sub = parse_call(sub); /* create the 'new' node */ sub = parse_new(sub, trans); } return sub; case TOKT_LPAR: /* left parenthesis - skip it */ G_tok->next(); /* parse the expression */ sub = S_op_comma.parse(); if (sub == 0) return 0; /* require the matching right parenthesis */ if (G_tok->cur() == TOKT_RPAR) { /* skip the right paren */ G_tok->next(); } else { /* * log an error; assume that the paren is simply * missing, so continue with our work */ G_tok->log_error_curtok(TCERR_EXPR_MISSING_RPAR); } /* return the parenthesized expression */ return sub; case TOKT_NIL: /* nil - the result is the constant value nil */ cval.set_nil(); return_constant: /* skip the token */ G_tok->next(); /* return a constant node */ return new CTPNConst(&cval); case TOKT_TRUE: /* true - the result is the constant value true */ cval.set_true(); goto return_constant; case TOKT_INT: /* integer constant value */ cval.set_int(G_tok->getcur()->get_int_val()); goto return_constant; case TOKT_FLOAT: /* floating point constant value */ cval.set_float(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), FALSE); goto return_constant; case TOKT_BIGINT: /* an integer constant promoted to float due to overflow */ cval.set_float(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), TRUE); goto return_constant; case TOKT_SSTR: handle_sstring: /* single-quoted string - the result is a constant string value */ cval.set_sstr(G_tok->getcur()); goto return_constant; case TOKT_DSTR: /* * if we're in preprocessor expression mode, treat this the * same as a single-quoted string */ if (G_prs->get_pp_expr_mode()) goto handle_sstring; /* * a string implicitly references 'self', because we could run * through the default output method on the current object */ G_prs->set_self_referenced(TRUE); /* build a double-quoted string node */ sub = new CTPNDstr(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* skip the string */ G_tok->next(); /* return the new node */ return sub; case TOKT_RESTR: /* regular expression string */ cval.set_restr(G_tok->getcur()); goto return_constant; case TOKT_DSTR_START: /* a string implicitly references 'self' */ G_prs->set_self_referenced(TRUE); /* parse the embedding expression */ { CTcEmbedBuilderDbl builder; return parse_embedding(&builder); } case TOKT_SSTR_START: /* parse the embedded expression */ { CTcEmbedBuilderSgl builder; return parse_embedding(&builder); } case TOKT_LBRACK: /* parse the list */ return parse_list(); case TOKT_SYM: /* * symbol - the meaning of the symbol is not yet known, so * create an unresolved symbol node */ sub = G_prs->create_sym_node(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* skip the symbol token */ G_tok->next(); /* return the new node */ return sub; case TOKT_SELF: /* note the explicit self-reference */ G_prs->set_self_referenced(TRUE); /* generate the "self" node */ G_tok->next(); return new CTPNSelf(); case TOKT_REPLACED: /* generate the "replaced" node */ G_tok->next(); return new CTPNReplaced(); case TOKT_TARGETPROP: /* note the explicit extended method context reference */ G_prs->set_full_method_ctx_referenced(TRUE); /* generate the "targetprop" node */ G_tok->next(); return new CTPNTargetprop(); case TOKT_TARGETOBJ: /* note the explicit extended method context reference */ G_prs->set_full_method_ctx_referenced(TRUE); /* generate the "targetobj" node */ G_tok->next(); return new CTPNTargetobj(); case TOKT_DEFININGOBJ: /* note the explicit extended method context reference */ G_prs->set_full_method_ctx_referenced(TRUE); /* generate the "definingobj" node */ G_tok->next(); return new CTPNDefiningobj(); case TOKT_INVOKEE: /* generate the "invokee" node */ G_tok->next(); return new CTPNInvokee(); case TOKT_ARGCOUNT: /* generate the "argcount" node */ G_tok->next(); return new CTPNArgc(); case TOKT_INHERITED: /* parse the "inherited" operation */ return parse_inherited(); case TOKT_DELEGATED: /* parse the "delegated" operation */ return parse_delegated(); case TOKT_RPAR: /* extra right paren - log an error */ G_tok->log_error(TCERR_EXTRA_RPAR); /* skip it and go back for more */ G_tok->next(); break; case TOKT_RBRACK: /* extra right square bracket - log an error */ G_tok->log_error(TCERR_EXTRA_RBRACK); /* skip it and go back for more */ G_tok->next(); break; case TOKT_DSTR_MID: case TOKT_DSTR_END: case TOKT_SEM: case TOKT_RBRACE: /* * this looks like the end of the statement, but we expected * an operand - note the error and end the statement */ G_tok->log_error_curtok(TCERR_EXPECTED_OPERAND); /* * Synthesize a constant zero as the operand value. Do not * skip the current token, because it's almost certainly not * meant to be part of the expression; we want to stay put * so that the caller can resynchronize on this token. */ cval.set_int(G_tok->getcur()->get_int_val()); return new CTPNConst(&cval); case TOKT_POUND: /* * When evaluating a debugger expression, we accept a special * notation for certain types that can't always be represented * in ordinary source code: * *. #__obj n - object number n (n is in decimal) *. #__prop n - property number n *. #__sstr ofs - s-string at offset ofs (in decimal) *. #__list ofs - list at offset ofs *. #__enum n - enum n *. #__bifptr s i - built-in function pointer, set s, index i *. #__invalid - invalid type */ tok = *G_tok->copycur(); if (G_prs->is_debug_expr() && G_tok->next() == TOKT_SYM && ((tok2 = *G_tok->copycur()).text_matches("__obj", 5) || tok2.text_matches("__obj", 5) || tok2.text_matches("__prop", 6) || tok2.text_matches("__sstr", 6) || tok2.text_matches("__list", 6) || tok2.text_matches("__func", 6) || tok2.text_matches("__enum", 6) || tok2.text_matches("__bifptr", 8))) { /* get the integer value */ if (G_tok->next() == TOKT_INT) { /* get and skip the integer value */ long i = G_tok->getcur()->get_int_val(); G_tok->next(); /* check which symbol we have */ if (tok2.text_matches("__obj", 5)) { /* generate an object expression */ cval.set_obj(i, TC_META_UNKNOWN); return new CTPNDebugConst(&cval); } else if (tok2.text_matches("__prop", 6)) { /* generate a property expression */ cval.set_prop(i); return new CTPNDebugConst(&cval); } else if (tok2.text_matches("__sstr", 6)) { /* generate a constant-pool string expression */ cval.set_sstr(i); return new CTPNDebugConst(&cval); } else if (tok2.text_matches("__list", 6)) { /* generate a constant-pool list expression */ cval.set_list(i); return new CTPNDebugConst(&cval); } else if (tok2.text_matches("__func", 6)) { /* generate a fake symbol for the function */ CTcSymFunc *sym = new CTcSymFunc( ".anon", 5, FALSE, 0, 0, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE); /* set the absolute address */ sym->set_abs_addr(i); /* generate a code-pool function pointer expression */ cval.set_funcptr(sym); return new CTPNDebugConst(&cval); } else if (tok2.text_matches("__enum", 6)) { /* generate an enum expression */ cval.set_enum(i); return new CTPNDebugConst(&cval); } else if (tok2.text_matches("__bifptr", 8)) { /* get and skip the function index token */ long j = G_tok->getcur()->get_int_val(); G_tok->next(); /* set up a fake symbol for the function */ CTcSymBif *sym = new CTcSymBif( ".anon", 5, FALSE, (ushort)i, (ushort)j, FALSE, 0, 0, FALSE); /* generate a built-in function pointer expression */ cval.set_bifptr(sym); return new CTPNDebugConst(&cval); } } else { /* it's not a special sequence - put back the tokens */ G_tok->unget(&tok2); G_tok->unget(&tok); } } else { /* it's not one of our symbols - put back the '#' */ G_tok->unget(&tok); } /* * if we got this far, we didn't get a valid special debugger * value - complain about the '#' */ goto bad_primary; default: bad_primary: /* invalid primary expression - log the error */ G_tok->log_error_curtok(TCERR_BAD_PRIMARY_EXPR); /* * Skip the token that's causing the problem; this will * ensure that we don't loop indefinitely trying to figure * out what this token is about, and return a constant zero * value as the primary. */ G_tok->next(); /* synthesize a constant zero as the operand value */ cval.set_int(G_tok->getcur()->get_int_val()); goto return_constant; } } } /* * Parse an "inherited" expression */ CTcPrsNode *CTcPrsOpUnary::parse_inherited() { CTcPrsNode *lhs; /* skip the "inherited" keyword and check what follows */ switch(G_tok->next()) { case TOKT_SYM: /* * it's an "inherited superclass..." expression - set up the * "inherited superclass" node */ lhs = new CTPNInhClass(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* this passes method context information to the inheritor */ G_prs->set_full_method_ctx_referenced(TRUE); /* skip the superclass token */ G_tok->next(); /* parse the member expression portion normally */ break; case TOKT_LT: /* * '<' - this is the start of a multi-method type list. Parse the * list: type1 ',' type2 ',' ... '>', then the argument list to the * 'inherited' call. */ { /* create the formal type list */ CTcFormalTypeList *tl = new (G_prsmem)CTcFormalTypeList(); /* skip the '<' */ G_tok->next(); /* parse each list element */ for (int done = FALSE ; !done ; ) { switch (G_tok->cur()) { case TOKT_GT: /* end of the list - skip the '>', and we're done */ G_tok->next(); done = TRUE; break; case TOKT_ELLIPSIS: /* '...' */ tl->add_ellipsis(); /* this has to be the end of the list */ if (G_tok->next() == TOKT_GT) G_tok->next(); else G_tok->log_error_curtok(TCERR_MMINH_MISSING_GT); /* assume the list ends here in any case */ done = TRUE; break; case TOKT_SYM: /* a type token - add it to the list */ tl->add_typed_param(G_tok->getcur()); finish_type: /* skip the type token */ switch (G_tok->next()) { case TOKT_COMMA: /* another type follows */ G_tok->next(); break; case TOKT_GT: G_tok->next(); done = TRUE; break; case TOKT_SYM: case TOKT_ELLIPSIS: case TOKT_TIMES: /* probably just a missing comma */ G_tok->log_error_curtok(TCERR_MMINH_MISSING_COMMA); break; default: /* anything else is an error */ G_tok->log_error_curtok(TCERR_MMINH_MISSING_COMMA); G_tok->next(); break; } break; case TOKT_TIMES: /* '*' indicates an untyped parameter */ tl->add_untyped_param(); goto finish_type; case TOKT_LPAR: /* probably a missing '>' */ G_tok->log_error_curtok(TCERR_MMINH_MISSING_GT); done = TRUE; break; case TOKT_COMMA: /* probably a missing type */ G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_TYPE); G_tok->next(); break; case TOKT_SEM: case TOKT_RPAR: case TOKT_EOF: /* all of these indicate a premature end of the list */ G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_TYPE); return 0; default: /* anything else is an error */ G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_TYPE); G_prs->skip_to_sem(); return 0; } } /* the left-hand side is an "inherited" node, with the arg list */ lhs = new CTPNInh(); ((CTPNInh *)lhs)->set_typelist(tl); /* an inherited<> expression must have an argument list */ if (G_tok->cur() != TOKT_LPAR) { G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_LIST); G_prs->skip_to_sem(); return 0; } } break; default: /* * There's no explicit superclass name listed, so the left-hand * side of the '.' expression is the simple "inherited" node. */ lhs = new CTPNInh(); /* * Since we don't have an explicit superclass, we'll need the * method context at run-time to establish the next class in * inheritance order. Flag the need for the full method context. */ G_prs->set_full_method_ctx_referenced(TRUE); /* parse the member expression portion normally */ break; } /* parse and return the member expression */ return parse_member(lhs); } /* * Parse a "delegated" expression */ CTcPrsNode *CTcPrsOpUnary::parse_delegated() { /* 'delegated' always references 'self' */ G_prs->set_self_referenced(TRUE); /* skip the "delegated" keyword */ G_tok->next(); /* * Parse a postfix expression giving the delegatee. Don't allow * nested member subexpressions (unless they're enclosed in * parentheses, of course) - our implicit '.' postfix takes * precedence. Also, don't allow call subexpressions (unless enclosed * in parens), since a postfix argument list binds to the 'delegated' * expression, not to a subexpression involving a function/method * call. */ CTcPrsNode *target = parse_postfix(FALSE, FALSE); /* set up the "delegated" node */ CTcPrsNode *lhs = new CTPNDelegated(target); /* return the rest as a normal member expression */ return parse_member(lhs); } /* * Parse a list */ CTcPrsNode *CTcPrsOpUnary::parse_list() { /* assume this isn't a lookup table (with "key->val" pairs) */ int is_lookup_table = FALSE; /* we're not at the default value for a lookup table ("*->val") */ int at_def_val = FALSE; /* set up a nil value for adding placeholders (for error recovery) */ CTcConstVal nilval; nilval.set_nil(); /* skip the opening '[' */ G_tok->next(); /* * create the list expression -- we'll add elements to the list as * we parse the elements */ CTPNList *lst = new CTPNList(); /* scan all list elements */ for (;;) { /* check what we have */ switch(G_tok->cur()) { case TOKT_RBRACK: /* * that's the end of the list - skip the closing bracket and * return the finished list */ G_tok->next(); goto done; case TOKT_EOF: case TOKT_RBRACE: case TOKT_SEM: case TOKT_DSTR_MID: case TOKT_DSTR_END: /* * these would all seem to imply that the closing ']' was * missing from the list; flag the error and end the list * now */ G_tok->log_error_curtok(TCERR_LIST_MISSING_RBRACK); goto done; case TOKT_RPAR: /* * extra right paren - log an error, but then skip the paren * and try to keep parsing */ G_tok->log_error(TCERR_LIST_EXTRA_RPAR); G_tok->next(); break; case TOKT_TIMES: /* * If a -> follows, this is the default value for a lookup * table list. */ if (G_tok->next() == TOKT_ARROW) { /* skip the arrow and get to the value */ G_tok->next(); /* * if this is a non-lookup table list with other entries, * this is an error */ if (!is_lookup_table && lst->get_count() != 0) { /* log the error, but keep parsing the list */ G_tok->log_error(TCERR_ARROW_IN_LIST, lst->get_count()); } else { /* it's definitely a lookup list now */ is_lookup_table = TRUE; /* we're now on the default value */ at_def_val = TRUE; } } else { /* it's not *-> - put back the next token */ G_tok->unget(); } default: /* it must be the next element expression */ break; } /* * Attempt to parse another list element expression. Parse just * below a comma expression, because commas can be used to * separate list elements. */ CTcPrsNode *ele = S_op_asi.parse(); if (ele == 0) return 0; /* add the element to the list */ lst->add_element(ele); /* if this was the default value, the list should end here */ if (at_def_val && G_tok->cur() != TOKT_RBRACK) { /* log an error */ G_tok->log_error_curtok(TCERR_LOOKUP_LIST_EXPECTED_END_AT_DEF); /* we've left the default value */ at_def_val = FALSE; /* * if we're at a comma, add a nil value to the list to * resynchronize - they must want to add more Key->Value pairs */ if (G_tok->cur() == TOKT_COMMA) lst->add_element(new CTPNConst(&nilval)); } /* check what follows the element */ switch(G_tok->cur()) { case TOKT_COMMA: /* skip the comma introducing the next element */ G_tok->next(); /* * If this is a lookup table list, commas are only allowed at * even elements: [ODD -> EVEN, ODD -> EVEN...]. */ if (is_lookup_table && (lst->get_count() & 1) != 0) { /* log an error */ G_tok->log_error(TCERR_LOOKUP_LIST_EXPECTED_ARROW, (lst->get_count()+1)/2); /* * Add an implied nil element as the value. This will help * resynchronize with the source code in the most likely * case that they simply left out a ->value item, so that * we don't log alternating "expected comma" and "expected * arrow" messages on every subsequent delimiter. */ lst->add_element(new CTPNConst(&nilval)); } /* if a close bracket follows, we seem to have an extra comma */ if (G_tok->cur() == TOKT_RBRACK) { /* * log an error about the missing element, then end the * list here */ G_tok->log_error_curtok(TCERR_LIST_EXPECT_ELEMENT); goto done; } break; case TOKT_ARROW: /* skip the arrow */ G_tok->next(); /* * '->' indicates a lookup table key->value list. This is an * all-or-nothing proposition: it has to be on every pair of * elements, or on none of them. */ if (lst->get_count() == 1) { /* first element: this is now a lookup table list */ is_lookup_table = TRUE; } else if (!is_lookup_table) { /* * it's not the first element, and we haven't seen arrows * before, so this list shouldn't have arrows at all */ G_tok->log_error(TCERR_ARROW_IN_LIST, lst->get_count()); } else if (is_lookup_table && (lst->get_count() & 1) == 0) { /* * We have an even number of elements, so an arrow is not * allowed here under any circumstances. */ G_tok->log_error(TCERR_MISPLACED_ARROW_IN_LIST, lst->get_count()/2); /* * They probably just left out the key value accidentally, * so add a nil value as the key. This will avoid a * cascade of errors for subsequent delimiters if they did * just leave out one item. */ lst->add_element(new CTPNConst(&nilval)); } break; case TOKT_RBRACK: /* * we're done with the list - skip the bracket and return * the finished list */ G_tok->next(); /* * If this is a lookup table list, make sure we ended on an * even number - if not, we're missing a ->Value entry. The * exception is that if the last value was a default, we end on * an odd item. */ if (is_lookup_table && (lst->get_count() & 1) != 0 && !at_def_val) { /* log the error */ G_tok->log_error(TCERR_LOOKUP_LIST_EXPECTED_ARROW, (lst->get_count()+1)/2); } goto done; case TOKT_EOF: case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_SEM: case TOKT_DSTR_MID: case TOKT_DSTR_END: /* * these would all seem to imply that the closing ']' was * missing from the list; flag the error and end the list * now */ G_tok->log_error_curtok(TCERR_LIST_MISSING_RBRACK); goto done; default: /* * Anything else is an error - note that we expected a * comma, then proceed with parsing from the current token * as though we had found the comma (in all likelihood, the * comma was accidentally omitted). If we've reached the * end of the file, return what we have so far, since it's * pointless to keep looping. */ G_tok->log_error_curtok(TCERR_LIST_EXPECT_COMMA); /* give up on end of file, otherwise keep going */ if (G_tok->cur() == TOKT_EOF) goto done; break; } } done: /* tell the parser to note this list, in case it's the longest yet */ G_cg->note_list(lst->get_count()); /* if it's a lookup table list, mark it as such */ if (is_lookup_table) lst->set_lookup_table(); /* return the list */ return lst; } /* ------------------------------------------------------------------------ */ /* * Parse Allocation Object */ /* * memory allocator for parse nodes */ void *CTcPrsAllocObj::operator new(size_t siz) { /* allocate the space out of the node pool */ return G_prsmem->alloc(siz); } /* ------------------------------------------------------------------------ */ /* * Parse Tree space manager */ /* * create */ CTcPrsMem::CTcPrsMem() { /* we have no blocks yet */ head_ = tail_ = 0; /* allocate our first block */ alloc_block(); } CTcPrsMem::~CTcPrsMem() { /* delete all objects in our pool */ delete_all(); } /* * Save state, for later resetting */ void CTcPrsMem::save_state(tcprsmem_state_t *state) { /* save the pool information in the state structure */ state->tail = tail_; state->free_ptr = free_ptr_; state->rem = rem_; } /* * Reset to initial state */ void CTcPrsMem::reset() { /* delete all blocks */ delete_all(); /* re-allocate the initial block */ alloc_block(); /* fixups are allocated in parser memory, so they're gone now */ G_objfixup = 0; G_propfixup = 0; G_enumfixup = 0; } /* * Reset. This deletes all objects allocated out of the parser pool * since the state was saved. */ void CTcPrsMem::reset(const tcprsmem_state_t *state) { tcprsmem_blk_t *cur; /* * delete all of the blocks that were allocated after the last block * that existed when the state was saved */ for (cur = state->tail->next_ ; cur != 0 ; ) { tcprsmem_blk_t *nxt; /* remember the next block */ nxt = cur->next_; /* delete this block */ t3free(cur); /* move on to the next one */ cur = nxt; } /* re-establish the saved last block */ tail_ = state->tail; /* make sure the list is terminated at the last block */ tail_->next_ = 0; /* re-establish the saved allocation point in the last block */ free_ptr_ = state->free_ptr; rem_ = state->rem; } /* * Delete all parser memory. This deletes all objects allocated out of * parser memory, so the caller must be sure that all of these objects * are unreferenced. */ void CTcPrsMem::delete_all() { /* free all blocks */ while (head_ != 0) { tcprsmem_blk_t *nxt; /* remember the next block after this one */ nxt = head_->next_; /* free this block */ t3free(head_); /* move on to the next one */ head_ = nxt; } /* there's no tail now */ tail_ = 0; } /* * allocate a block */ void CTcPrsMem::alloc_block() { tcprsmem_blk_t *blk; /* * block size - pick a size that's large enough that we won't be * unduly inefficient (in terms of having tons of blocks), but still * friendly to 16-bit platforms (i.e., under 64k) */ const size_t BLOCK_SIZE = 65000; /* allocate space for the block */ blk = (tcprsmem_blk_t *)t3malloc(sizeof(tcprsmem_blk_t) + BLOCK_SIZE - 1); /* if that failed, throw an error */ if (blk == 0) err_throw(TCERR_NO_MEM_PRS_TREE); /* link in the block at the end of our list */ blk->next_ = 0; if (tail_ != 0) tail_->next_ = blk; else head_ = blk; /* the block is now the last block in the list */ tail_ = blk; /* * Set up to allocate out of our block. Make sure the free pointer * starts out on a worst-case alignment boundary; normally, the C++ * compiler will properly align our "buf_" structure member on a * worst-case boundary, so this calculation won't actually change * anything, but this will help ensure portability even to weird * compilers. */ free_ptr_ = (char *)osrndpt((unsigned char *)blk->buf_); /* * get the amount of space remaining in the block (in the unlikely * event that worst-case alignment actually moved the free pointer * above the start of the buffer, we'll have lost a little space in * the buffer for the alignment offset) */ rem_ = BLOCK_SIZE - (free_ptr_ - blk->buf_); } /* * Allocate space */ void *CTcPrsMem::alloc(size_t siz) { char *ret; size_t space_used; /* if there's not enough space available, allocate a new block */ if (siz > rem_) { /* allocate a new block */ alloc_block(); /* * if there's still not enough room, the request must exceed the * largest block we can allocate */ if (siz > rem_) G_tok->throw_internal_error(TCERR_PRS_BLK_TOO_BIG, (ulong)siz); } /* return the free pointer */ ret = free_ptr_; /* advance the free pointer past the space, rounding for alignment */ free_ptr_ = (char *)osrndpt((unsigned char *)free_ptr_ + siz); /* deduct the amount of space we consumed from the available space */ space_used = free_ptr_ - ret; if (space_used > rem_) rem_ = 0; else rem_ -= space_used; /* return the allocated space */ return ret; } /* ------------------------------------------------------------------------ */ /* * parse node base class */ /* * Make adjustments for dynamic evaluation */ CTcPrsNode *CTcPrsNodeBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* by default, a node can't be used in dynamic evaluation */ err_throw(VMERR_INVAL_DBG_EXPR); AFTER_ERR_THROW(return 0;) } /* ------------------------------------------------------------------------ */ /* * constant node */ /* * adjust for debugger use */ CTcPrsNode *CTPNConstBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* convert to a debugger-constant */ return new CTPNDebugConst(&val_); } /* ------------------------------------------------------------------------ */ /* * List parse node */ /* * add an element to a list */ void CTPNListBase::add_element(CTcPrsNode *expr) { CTPNListEle *ele; /* create a list element object for the new element */ ele = new CTPNListEle(expr); /* count the new entry */ ++cnt_; /* add the element to our linked list */ ele->set_prev(tail_); if (tail_ != 0) tail_->set_next(ele); else head_ = ele; tail_ = ele; /* * if the new element does not have a constant value, the list no * longer has a constant value (if it did before) */ if (!expr->is_const()) is_const_ = FALSE; } /* * remove each occurrence of a given constant value from the list */ void CTPNListBase::remove_element(const CTcConstVal *val) { CTPNListEle *cur; /* scan the list */ for (cur = head_ ; cur != 0 ; cur = cur->get_next()) { /* * if this element is constant, compare it to the value to be * removed; if it matches, remove it */ if (cur->get_expr()->is_const() && cur->get_expr()->get_const_val()->is_equal_to(val)) { /* set the previous element's forward pointer */ if (cur->get_prev() == 0) head_ = cur->get_next(); else cur->get_prev()->set_next(cur->get_next()); /* set the next element's back pointer */ if (cur->get_next() == 0) tail_ = cur->get_prev(); else cur->get_next()->set_prev(cur->get_prev()); /* decrement our element counter */ --cnt_; } } } /* * Get the constant value of the element at the given index. Logs an * error and returns null if there's no such element. */ CTcPrsNode *CTPNListBase::get_const_ele(int index) { CTPNListEle *ele; /* if the index is negative, it's out of range */ if (index < 1) { /* log the error and return failure */ G_tok->log_error(TCERR_CONST_IDX_RANGE); return 0; } /* scan the list for the given element */ for (ele = head_ ; ele != 0 && index > 1 ; ele = ele->get_next(), --index) ; /* if we ran out of elements, the index is out of range */ if (ele == 0 || index != 1) { G_tok->log_error(TCERR_CONST_IDX_RANGE); return 0; } /* return the element's constant value */ return ele->get_expr(); } /* * Fold constants */ CTcPrsNode *CTPNListBase::fold_constants(CTcPrsSymtab *symtab) { CTPNListEle *cur; int all_const; /* * if the list is already constant, there's nothing extra we need to * do */ if (is_const_) return this; /* presume the result will be all constants */ all_const = TRUE; /* run through my list and fold each element */ for (cur = head_ ; cur != 0 ; cur = cur->get_next()) { /* fold this element */ cur->fold_constants(symtab); /* * if this element is not a constant, the whole list cannot be * constant */ if (!cur->get_expr()->is_const()) all_const = FALSE; } /* if every element was a constant, the overall list is constant */ if (all_const) is_const_ = TRUE; /* return myself */ return this; } /* * Adjust for dynamic compilation */ CTcPrsNode *CTPNListBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { CTPNListEle *cur; /* run through my list and adjust each element */ for (cur = head_ ; cur != 0 ; cur = cur->get_next()) { /* adjust this element */ cur->adjust_for_dyn(info); } /* * force the list to be non-constant - in debugger mode, we have to * build the value we push as a dynamic object, never as an actual * constant, to ensure that the generated code can be deleted * immediately after being executed */ is_const_ = FALSE; /* return myself */ return this; } /* ------------------------------------------------------------------------ */ /* * If-nil operator ?? node base class */ /* * fold constants */ CTcPrsNode *CTPNIfnilBase::fold_constants(CTcPrsSymtab *symtab) { /* fold constants in the subnodes */ first_ = first_->fold_constants(symtab); second_ = second_->fold_constants(symtab); /* * if the first is now a constant, we can fold this entire expression * node by choosing the first or second node. Otherwise return myself * unchanged. */ if (first_->is_const()) { /* * if the first expression is nil, return the second, otherwise * return the first */ return (first_->get_const_val()->get_type() == TC_CVT_NIL ? second_ : first_); } else { /* we can't fold this node any further - return it unchanged */ return this; } } /* ------------------------------------------------------------------------ */ /* * conditional operator node base class */ /* * fold constants */ CTcPrsNode *CTPNIfBase::fold_constants(CTcPrsSymtab *symtab) { /* fold constants in the subnodes */ first_ = first_->fold_constants(symtab); second_ = second_->fold_constants(symtab); third_ = third_->fold_constants(symtab); /* * if the first is now a constant, we can fold this entire * expression node by choosing the second or third based on its * value; otherwise, return myself unchanged */ if (first_->is_const()) { /* * The condition is a constant - the result is the 'then' or 'else' * part, based on the condition's value. */ return (first_->get_const_val()->get_val_bool() ? second_ : third_); } else { /* we can't fold this node any further - return it unchanged */ return this; } } /* ------------------------------------------------------------------------ */ /* * Double-quoted string node - base class */ /* * create a double-quoted string node */ CTPNDstrBase::CTPNDstrBase(const char *str, size_t len) { /* remember the string */ str_ = str; len_ = len; /* * note the length in the parser, in case it's the longest string * we've seen so far */ G_cg->note_str(len); } /* * adjust for dynamic (run-time) compilation */ CTcPrsNode *CTPNDstrBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* * don't allow dstring evaluation in speculative mode, since we * can't execute anything with side effects in this mode */ if (info->speculative) err_throw(VMERR_BAD_SPEC_EVAL); /* return a debugger dstring node */ return new CTPNDebugDstr(str_, len_); } /* ------------------------------------------------------------------------ */ /* * Address-of parse node */ /* * fold constants */ CTcPrsNode *CTPNAddrBase::fold_constants(CTcPrsSymtab *symtab) { CTcPrsNode *ret; /* ask the symbol to generate a constant expression for its address */ ret = get_sub_expr()->fold_addr_const(symtab); /* * if we got a constant value, return it; otherwise, return myself * unchanged */ return (ret != 0 ? ret : this); } /* * determine if my address equals that of another node */ int CTPNAddrBase::is_addr_eq(const CTPNAddr *node, int *comparable) const { /* * If both sides are symbols, the addresses are equal if and only if * the symbols are identical. One symbol has exactly one meaning in * a given context, and no two symbols can have the same meaning. * (It's important that we be able to state this for all symbols, * because we can't necessarily know during parsing the meaning of a * given symbol, since the symbol could be a forward reference.) */ if (get_sub_expr()->get_sym_text() != 0 && node->get_sub_expr()->get_sym_text() != 0) { CTcPrsNode *sym1; CTcPrsNode *sym2; /* they're both symbols, so they're comparable */ *comparable = TRUE; /* they're the same if both symbols have the same text */ sym1 = get_sub_expr(); sym2 = node->get_sub_expr(); return (sym1->get_sym_text_len() == sym2->get_sym_text_len() && memcmp(sym1->get_sym_text(), sym2->get_sym_text(), sym1->get_sym_text_len()) == 0); } /* they're not comparable */ *comparable = FALSE; return FALSE; } /* ------------------------------------------------------------------------ */ /* * Symbol parse node base class */ /* * fold constants */ CTcPrsNode *CTPNSymBase::fold_constants(CTcPrsSymtab *symtab) { CTcSymbol *sym; CTcPrsNode *ret; /* * Look up my symbol. At this stage, don't assume a definition; * merely look to see if it's already known. We don't have enough * information to determine how we should define the symbol, so * leave it undefined until code generation if it's not already * known. */ sym = symtab->find(get_sym_text(), get_sym_text_len()); if (sym != 0) { /* ask the symbol to do the folding */ ret = sym->fold_constant(); /* if that succeeded, return it; otherwise, return unchanged */ return (ret != 0 ? ret : this); } else { /* not defined - return myself unchanged */ return this; } } /* * Fold my address to a constant node. If I have no address value, I'll * simply return myself unchanged. Note that it's an error if I have no * constant value, but we'll count on the code generator to report the * error, and simply ignore it for now. */ CTcPrsNode *CTPNSymBase::fold_addr_const(CTcPrsSymtab *symtab) { CTcSymbol *sym; /* look up my symbol; if we don't find it, don't define it */ sym = symtab->find(get_sym_text(), get_sym_text_len()); if (sym != 0) { /* we got a symbol - ask it to do the folding */ return sym->fold_addr_const(); } else { /* undefined symbol - there's no constant address value */ return 0; } } /* * Determine if I have a return value when called */ int CTPNSymBase::has_return_value_on_call() const { CTcSymbol *sym; /* try resolving my symbol */ sym = G_prs->get_global_symtab()->find(sym_, len_); /* * if we found a symbol, let it resolve the call; otherwise, assume * that we do have a return value */ if (sym != 0) return sym->has_return_value_on_call(); else return TRUE; } /* * Determine if I am a valid lvalue */ int CTPNSymBase::check_lvalue_resolved(class CTcPrsSymtab *symtab) const { CTcSymbol *sym; /* look up the symbol in the given scope */ sym = symtab->find(get_sym_text(), get_sym_text_len()); if (sym != 0) { /* ask the symbol what it thinks */ return sym->check_lvalue(); } else { /* it's undefined - can't be an lvalue */ return FALSE; } } /* * adjust for dynamic (run-time) compilation */ CTcPrsNode *CTPNSymBase::adjust_for_dyn(const tcpn_dyncomp_info *) { /* * If this symbol isn't defined in the global symbol table, we can't * evaluate this expression in the debugger - new symbols can never * be defined in the debugger, so there's no point in trying to hold * a forward reference as we normally would for an undefined symbol. * We need look only in the global symbol table because local * symbols will already have been resolved. */ if (G_prs->get_global_symtab()->find(sym_, len_) == 0) { /* log the error, to generate an appropriate message */ G_tok->log_error(TCERR_UNDEF_SYM, (int)len_, sym_); /* throw the error as well */ err_throw_a(TCERR_UNDEF_SYM, 2, ERR_TYPE_INT, (int)len_, ERR_TYPE_TEXTCHAR, sym_); } /* return myself unchanged */ return this; } /* ------------------------------------------------------------------------ */ /* * Resolved Symbol parse node base class */ /* * fold constants */ CTcPrsNode *CTPNSymResolvedBase::fold_constants(CTcPrsSymtab *symtab) { CTcPrsNode *ret; /* ask the symbol to generate the folded constant value */ ret = sym_->fold_constant(); /* if that succeeded, return it; otherwise, return myself unchanged */ return (ret != 0 ? ret : this); } /* * Fold my address to a constant node. If I have no address value, I'll * simply return myself unchanged. Note that it's an error if I have no * constant value, but we'll count on the code generator to report the * error, and simply ignore it for now. */ CTcPrsNode *CTPNSymResolvedBase::fold_addr_const(CTcPrsSymtab *symtab) { /* ask my symbol to generate the folded constant value */ return sym_->fold_addr_const(); } /* ------------------------------------------------------------------------ */ /* * Debugger local variable resolved symbol */ /* * construction */ CTPNSymDebugLocalBase::CTPNSymDebugLocalBase(const tcprsdbg_sym_info *info) { /* save the type information */ switch(info->sym_type) { case TC_SYM_LOCAL: var_id_ = info->var_id; ctx_arr_idx_ = info->ctx_arr_idx; frame_idx_ = info->frame_idx; is_param_ = FALSE; break; case TC_SYM_PARAM: var_id_ = info->var_id; ctx_arr_idx_ = 0; frame_idx_ = info->frame_idx; is_param_ = TRUE; break; default: /* other types are invalid */ assert(FALSE); break; } } /* ------------------------------------------------------------------------ */ /* * Argument List parse node base class */ /* * fold constants */ CTcPrsNode *CTPNArglistBase::fold_constants(CTcPrsSymtab *symtab) { /* fold each list element */ CTPNArg *cur; for (cur = get_arg_list_head() ; cur != 0 ; cur = cur->get_next_arg()) cur->fold_constants(symtab); /* return myself with no further folding */ return this; } /* * adjust for dynamic (run-time) compilation */ CTcPrsNode *CTPNArglistBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* adjust each argument list member */ CTPNArg *arg; for (arg = list_ ; arg != 0 ; arg = arg->get_next_arg()) { /* * adjust this argument; assume the argument node itself isn't * affected */ arg->adjust_for_dyn(info); } /* return myself otherwise unchanged */ return this; } /* ------------------------------------------------------------------------ */ /* * Argument List Entry parse node base class */ /* * fold constants */ CTcPrsNode *CTPNArgBase::fold_constants(CTcPrsSymtab *symtab) { /* fold my argument expression */ arg_expr_ = arg_expr_->fold_constants(symtab); /* return myself unchanged */ return this; } /* * Set the parameter name */ void CTPNArgBase::set_name(const CTcToken *tok) { /* remember the name token */ name_ = *tok; } /* ------------------------------------------------------------------------ */ /* * Member with no arguments */ /* * adjust for dynamic (run-time) compilation */ CTcPrsNode *CTPNMemberBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* adjust the object and property expressions */ obj_expr_ = obj_expr_->adjust_for_dyn(info); prop_expr_ = prop_expr_->adjust_for_dyn(info); /* return myself otherwise unchanged */ return this; } /* ------------------------------------------------------------------------ */ /* * Member with Arguments parse node base class */ /* * fold constants */ CTcPrsNode *CTPNMemArgBase::fold_constants(CTcPrsSymtab *symtab) { /* fold constants in the object and property expressions */ obj_expr_ = obj_expr_->fold_constants(symtab); prop_expr_ = prop_expr_->fold_constants(symtab); /* * fold constants in the argument list; an argument list node never * changes to a new node type during constant folding, so we don't * need to update the member */ arglist_->fold_constants(symtab); /* return myself with no further evaluation */ return this; } /* * adjust for dynamic (run-time) compilation */ CTcPrsNode *CTPNMemArgBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* don't allow in speculative mode due to possible side effects */ if (info->speculative) err_throw(VMERR_BAD_SPEC_EVAL); /* * adjust my object expression, property expression, and argument * list */ obj_expr_ = obj_expr_->adjust_for_dyn(info); prop_expr_ = prop_expr_->adjust_for_dyn(info); arglist_->adjust_for_dyn(info); /* return myself otherwise unchanged */ return this; } /* ------------------------------------------------------------------------ */ /* * Function/Method Call parse node base class */ /* * fold constants */ CTcPrsNode *CTPNCallBase::fold_constants(CTcPrsSymtab *symtab) { /* fold my function expression */ func_ = func_->fold_constants(symtab); /* fold my argument list */ arglist_->fold_constants(symtab); /* return myself unchanged */ return this; } /* * adjust for dynamic (run-time) compilation */ CTcPrsNode *CTPNCallBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* don't allow in speculative mode because of side effects */ if (info->speculative) err_throw(VMERR_BAD_SPEC_EVAL); /* adjust the function expression */ func_ = func_->adjust_for_dyn(info); /* adjust the argument list (assume it doesn't change) */ arglist_->adjust_for_dyn(info); /* return myself otherwise unchanged */ return this; } /* ------------------------------------------------------------------------ */ /* * Parser Symbol Table implementation */ /* static hash function */ CVmHashFunc *CTcPrsSymtab::hash_func_ = 0; /* * allocate parser symbol tables out of the parser memory pool */ void *CTcPrsSymtab::operator new(size_t siz) { return G_prsmem->alloc(siz); } /* * static initialization */ void CTcPrsSymtab::s_init() { /* create our static hash function */ if (hash_func_ == 0) hash_func_ = new CVmHashFuncCS(); } /* * static termination */ void CTcPrsSymtab::s_terminate() { /* delete our static hash function */ if (hash_func_ != 0) { delete hash_func_; hash_func_ = 0; } } /* * initialize */ CTcPrsSymtab::CTcPrsSymtab(CTcPrsSymtab *parent_scope) { size_t hash_table_size; /* * Create the hash table. If we're at global scope (parent_scope == * 0), create a large hash table, since we'll probably add lots of * symbols; otherwise, it's just a local table, which probably won't * have many entries, so create a small table. * * Note that we always use the static hash function object, hence * the table doesn't own the object. */ hash_table_size = (parent_scope == 0 ? 512 : 32); hashtab_ = new (G_prsmem) CVmHashTable(hash_table_size, hash_func_, FALSE, new (G_prsmem) CVmHashEntry *[hash_table_size]); /* remember the parent scope */ parent_ = parent_scope; /* we're not in a debugger frame list yet */ list_index_ = 0; list_next_ = 0; /* we don't have a generated byte code range yet */ start_ofs_ = end_ofs_ = 0; } /* * delete */ CTcPrsSymtab::~CTcPrsSymtab() { /* delete our underlying hash table */ delete hashtab_; } /* * Find a symbol, marking it as referenced if we find it. */ CTcSymbol *CTcPrsSymtab::find(const textchar_t *sym, size_t len, CTcPrsSymtab **symtab) { CTcSymbol *entry; /* find the symbol */ entry = find_noref(sym, len, symtab); /* if we found an entry, mark it as referenced */ if (entry != 0) entry->mark_referenced(); /* return the result */ return entry; } /* * Find a symbol. This base version does not affect the 'referenced' * status of the symbol we look up. */ CTcSymbol *CTcPrsSymtab::find_noref(const textchar_t *sym, size_t len, CTcPrsSymtab **symtab) { /* * Look for the symbol. Start in the current symbol table, and work * outwards to the outermost enclosing table. */ for (CTcPrsSymtab *curtab = this ; curtab != 0 ; curtab = curtab->get_parent()) { /* look for the symbol in this table */ CTcSymbol *entry = curtab->find_direct(sym, len); if (entry != 0) { /* * found it - if the caller wants to know about the symbol * table in which we actually found the symbol, return that * information */ if (symtab != 0) *symtab = curtab; /* return the symbol table entry we found */ return entry; } } /* we didn't find the symbol - return failure */ return 0; } /* * Find a symbol directly in this table */ CTcSymbol *CTcPrsSymtab::find_direct(const textchar_t *sym, size_t len) { /* return the entry from our hash table */ return (CTcSymbol *)get_hashtab()->find(sym, len); } /* * Add a symbol to the table */ void CTcPrsSymtab::add_entry(CTcSymbol *sym) { /* add it to the table */ get_hashtab()->add(sym); } /* * remove a symbol from the table */ void CTcPrsSymtab::remove_entry(CTcSymbol *sym) { /* remove it from the underyling hash table */ get_hashtab()->remove(sym); } /* * Add a formal parameter symbol */ CTcSymLocal *CTcPrsSymtab::add_formal(const textchar_t *sym, size_t len, int formal_num, int copy_str) { CTcSymLocal *lcl; /* * Make sure it's not already defined in our own symbol table - if * it is, log an error and ignore the redundant definition. (We * only care about our own scope, not enclosing scopes, since it's * perfectly fine to hide variables in enclosing scopes.) */ if (find_direct(sym, len) != 0) { /* log an error */ G_tok->log_error(TCERR_FORMAL_REDEF, (int)len, sym); /* don't add the symbol again */ return 0; } /* create the symbol entry */ lcl = new CTcSymLocal(sym, len, copy_str, TRUE, formal_num); /* add it to the table */ add_entry(lcl); /* return the new symbol */ return lcl; } /* * Add the current token as a local variable symbol, initially unreferenced * and uninitialized */ CTcSymLocal *CTcPrsSymtab::add_local(int local_num) { return add_local(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), local_num, FALSE, FALSE, FALSE); } /* * Add a local variable symbol */ CTcSymLocal *CTcPrsSymtab::add_local(const textchar_t *sym, size_t len, int local_num, int copy_str, int init_assigned, int init_referenced) { CTcSymLocal *lcl; /* * make sure the symbol isn't already defined in this scope; if it * is, log an error */ if (find_direct(sym, len) != 0) { /* log the error */ G_tok->log_error(TCERR_LOCAL_REDEF, (int)len, sym); /* don't create the symbol again - return the original definition */ return 0; } /* create the symbol entry */ lcl = new CTcSymLocal(sym, len, copy_str, FALSE, local_num); /* * if the symbol is initially to be marked as referenced or * assigned, mark it now */ if (init_assigned) lcl->set_val_assigned(TRUE); if (init_referenced) lcl->set_val_used(TRUE); /* add it to the table */ add_entry(lcl); /* return the new local */ return lcl; } /* * Add a code label ('goto') symbol */ CTcSymLabel *CTcPrsSymtab::add_code_label(const textchar_t *sym, size_t len, int copy_str) { CTcSymLabel *lbl; /* * make sure the symbol isn't already defined in this scope; if it * is, log an error */ if (find_direct(sym, len) != 0) { /* log the error */ G_tok->log_error(TCERR_CODE_LABEL_REDEF, (int)len, sym); /* don't create the symbol again - return the original definition */ return 0; } /* create the symbol entry */ lbl = new CTcSymLabel(sym, len, copy_str); /* add it to the table */ add_entry(lbl); /* return the new label */ return lbl; } /* * Find a symbol; if the symbol isn't defined, add a new entry according * to the action flag. Because we add a symbol entry if the symbol * isn't defined, this *always* returns a valid symbol object. */ CTcSymbol *CTcPrsSymtab::find_or_def(const textchar_t *sym, size_t len, int copy_str, tcprs_undef_action action) { /* * Look for the symbol. Start in the current symbol table, and work * outwards to the outermost enclosing table. */ for (CTcPrsSymtab *curtab = this ; ; curtab = curtab->get_parent()) { /* look for the symbol in this table */ CTcSymbol *entry = (CTcSymbol *)curtab->find_direct(sym, len); if (entry != 0) { /* mark the entry as referenced */ entry->mark_referenced(); /* * if this is a non-weak property definition, and this is a * property symbol, remove any existing weak flag from the * property symbol */ if (entry->get_type() == TC_SYM_PROP && (action == TCPRS_UNDEF_ADD_PROP || action == TCPRS_UNDEF_ADD_PROP_NO_WARNING)) ((CTcSymProp *)entry)->set_weak(FALSE); /* found it - return the symbol */ return entry; } /* * If there's no parent symbol table, the symbol is undefined. * Add a new symbol according to the action parameter. Note * that we always add the new symbol at global scope, hence we * add it to 'curtab', not 'this'. */ if (curtab->get_parent() == 0) { /* check which action we're being asked to perform */ switch(action) { case TCPRS_UNDEF_ADD_UNDEF: /* add an "undefined" entry - log an error */ G_tok->log_error(TCERR_UNDEF_SYM, (int)len, sym); /* create a new symbol of type undefined */ entry = new CTcSymUndef(sym, len, copy_str); /* finish up */ goto add_entry; case TCPRS_UNDEF_ADD_PROP: case TCPRS_UNDEF_ADD_PROP_NO_WARNING: case TCPRS_UNDEF_ADD_PROP_WEAK: { /* create a new symbol of type property */ CTcSymProp *prop = new CTcSymProp( sym, len, copy_str, G_cg->new_prop_id()); /* mark it as "weak" if desired */ if (action == TCPRS_UNDEF_ADD_PROP_WEAK) prop->set_weak(TRUE); /* use it as the new table entry */ entry = prop; } /* show a warning if desired */ if (action == TCPRS_UNDEF_ADD_PROP) G_tok->log_warning(TCERR_ASSUME_SYM_PROP, (int)len, sym); /* finish up */ goto add_entry; add_entry: /* add the new entry to the global table */ add_to_global_symtab(curtab, entry); /* return the new entry */ return entry; } } } } /* * Find a symbol. If the symbol is already defined as a "weak" property, * delete the existing symbol to make way for the overriding strong * definition. */ CTcSymbol *CTcPrsSymtab::find_delete_weak(const char *name, size_t len) { /* look up the symbol */ CTcSymbol *sym = find(name, len); /* if it's a weak property definition, delete it */ if (sym != 0 && sym->get_type() == TC_SYM_PROP && ((CTcSymProp *)sym)->is_weak()) { /* delete it and forget it */ remove_entry(sym); sym = 0; } /* return what we found */ return sym; } /* * Enumerate the entries in a symbol table */ void CTcPrsSymtab::enum_entries(void (*func)(void *, CTcSymbol *), void *ctx) { /* * Ask the hash table to perform the enumeration. We know that all * of our entries in the symbol table are CTcSymbol objects, so we * can coerce the callback function to the appropriate type without * danger. */ get_hashtab()->enum_entries((void (*)(void *, CVmHashEntry *))func, ctx); } /* * Scan the symbol table for unreferenced local variables */ void CTcPrsSymtab::check_unreferenced_locals() { /* skip the check if we're only parsing for syntax */ if (!G_prs->get_syntax_only()) { /* run the symbols through our unreferenced local check callback */ enum_entries(&unref_local_cb, this); } } /* * Enumeration callback to check for unreferenced locals */ void CTcPrsSymtab::unref_local_cb(void *, CTcSymbol *sym) { /* check the symbol */ sym->check_local_references(); } /* ------------------------------------------------------------------------ */ /* * Comma node */ /* * fold constants */ CTcPrsNode *CTPNCommaBase::fold_binop() { /* use the normal addition folder */ return S_op_comma.eval_constant(left_, right_); } /* ------------------------------------------------------------------------ */ /* * Addition parse node */ /* * Fold constants. We override the default fold_constants() for * addition nodes because addition constancy can be affected by symbol * resolution. In particular, if we resolve symbols in a list, the list * could turn constant, which could in turn make the result of an * addition operator with the list as an operand turn constant. */ CTcPrsNode *CTPNAddBase::fold_binop() { /* use the normal addition folder */ return S_op_add.eval_constant(left_, right_); } /* ------------------------------------------------------------------------ */ /* * Subtraction parse node */ /* * Fold constants. We override the default fold_constants() for the * subtraction node because subtraction constancy can be affected by * symbol resolution. In particular, if we resolve symbols in a list, * the list could turn constant, which could in turn make the result of * a subtraction operator with the list as an operand turn constant. */ CTcPrsNode *CTPNSubBase::fold_binop() { /* use the normal addition folder */ return S_op_sub.eval_constant(left_, right_); } /* ------------------------------------------------------------------------ */ /* * Equality Comparison parse node */ /* * fold constants */ CTcPrsNode *CTPNEqBase::fold_binop() { /* use the normal addition folder */ return S_op_eq.eval_constant(left_, right_); } /* ------------------------------------------------------------------------ */ /* * Inequality Comparison parse node */ /* * fold constants */ CTcPrsNode *CTPNNeBase::fold_binop() { /* use the normal addition folder */ return S_op_ne.eval_constant(left_, right_); } /* ------------------------------------------------------------------------ */ /* * Logical AND parse node */ /* * fold constants */ CTcPrsNode *CTPNAndBase::fold_binop() { /* use the normal addition folder */ return S_op_and.eval_constant(left_, right_); } /* ------------------------------------------------------------------------ */ /* * Logical OR parse node */ /* * fold constants */ CTcPrsNode *CTPNOrBase::fold_binop() { /* use the normal addition folder */ return S_op_or.eval_constant(left_, right_); } /* ------------------------------------------------------------------------ */ /* * NOT parse node */ /* * fold constants */ CTcPrsNode *CTPNNotBase::fold_unop() { /* use the normal addition folder */ return S_op_unary.eval_const_not(sub_); } /* ------------------------------------------------------------------------ */ /* * Subscript parse node */ /* * Fold constants. We override the default fold_constants() for * subscript nodes because subscript constancy can be affected by symbol * resolution. In particular, if we resolve symbols in a list, the list * could turn constant, which could in turn make the result of a * subscript operator with the list as an operand turn constant. */ /* ------------------------------------------------------------------------ */ /* * Equality Comparison parse node */ /* * fold constants */ CTcPrsNode *CTPNSubscriptBase::fold_binop() { /* use the normal addition folder */ return S_op_unary.eval_const_subscript(left_, right_); } /* ------------------------------------------------------------------------ */ /* * Parser Symbol Table Entry base class */ /* * Allocate symbol objects from the parse pool, since these objects have * all of the lifespan characteristics of pool objects. */ void *CTcSymbolBase::operator new(size_t siz) { return G_prsmem->alloc(siz); } /* ------------------------------------------------------------------------ */ /* * function symbol entry base */ /* * fold function name into a function address */ CTcPrsNode *CTcSymFuncBase::fold_constant() { CTcConstVal cval; /* set up the function pointer constant */ cval.set_funcptr((CTcSymFunc *)this); /* return a constant node with the function pointer */ return new CTPNConst(&cval); } /* * add an absolute fixup to my list */ void CTcSymFuncBase::add_abs_fixup(CTcDataStream *ds, ulong ofs) { /* ask the code body to add the fixup */ CTcAbsFixup::add_abs_fixup(&fixups_, ds, ofs); } /* * add an absolute fixup at the current stream offset */ void CTcSymFuncBase::add_abs_fixup(CTcDataStream *ds) { /* ask the code body to add the fixup */ CTcAbsFixup::add_abs_fixup(&fixups_, ds, ds->get_ofs()); } /* ------------------------------------------------------------------------ */ /* * local variable symbol entry base */ /* * initialize */ CTcSymLocalBase::CTcSymLocalBase(const char *str, size_t len, int copy, int is_param, int var_num) : CTcSymbol(str, len, copy, (is_param ? TC_SYM_PARAM : TC_SYM_LOCAL)) { /* remember the information */ var_num_ = var_num; param_index_ = 0; is_param_ = is_param; is_named_param_ = FALSE; is_opt_param_ = FALSE; is_list_param_ = FALSE; /* presume it's a regular stack variable (not a context local) */ is_ctx_local_ = FALSE; ctx_orig_ = 0; ctx_var_num_ = 0; ctx_level_ = 0; ctx_var_num_set_ = FALSE; /* presume there's no default value expression */ defval_expr_ = 0; /* so far, the value isn't used anywhere */ val_used_ = FALSE; val_assigned_ = FALSE; /* the symbol has not been referenced so far */ referenced_ = FALSE; /* remember where the symbol is defined in the source file */ G_tok->get_last_pos(&src_desc_, &src_linenum_); } /* * Mark the value of the variable as used */ void CTcSymLocalBase::set_val_used(int f) { /* note the new status */ val_used_ = f; /* if we have now assigned the value, propagate to the original */ if (f && ctx_orig_ != 0) ctx_orig_->set_val_used(TRUE); } /* * Mark the value of the variable as assigned */ void CTcSymLocalBase::set_val_assigned(int f) { /* note the new status */ val_assigned_ = f; /* if we have now assigned the value, propagate to the original */ if (f && ctx_orig_ != 0) ctx_orig_->set_val_assigned(TRUE); } /* * Check for references to this local */ void CTcSymLocalBase::check_local_references() { int err; tc_severity_t sev = TC_SEV_WARNING; /* * if this isn't an original, but is simply a copy of a variable * inherited from an enclosing scope (such as into an anonymous * function), don't bother even checking for errors - we'll let the * original symbol take care of reporting its own errors */ if (ctx_orig_ != 0) return; /* * Note if this is some kind of parameter. Some parameters are * represented as locals: varargs-list parameters, optional parameters, * and named parameters. We need to check for those kinds of * parameters specifically because they otherwise look like regular * local variables. */ int param = (is_param() || is_list_param() || is_opt_param() || is_named_param()); /* * If it's unreferenced or unassigned (or both), log an error; note * that a formal parameter is always assigned, since the value is * assigned by the caller. */ if (!get_val_used() && !get_val_assigned() && !param) { /* this is a regular local that was never assigned or referenced */ err = TCERR_UNREFERENCED_LOCAL; } else if (!get_val_used()) { if (param) { /* it's a parameter - generate only a pendantic error */ sev = TC_SEV_PEDANTIC; err = TCERR_UNREFERENCED_PARAM; } else { /* * this is a regular local with a value that was assigned and * never used */ err = TCERR_UNUSED_LOCAL_ASSIGNMENT; } } else if (!get_val_assigned() && !param) { /* it's used but never assigned */ err = TCERR_UNASSIGNED_LOCAL; } else { /* no error */ return; } /* * display the warning message, showing the error location as the * source line where the local was defined */ G_tcmain->log_error(get_src_desc(), get_src_linenum(), sev, err, (int)get_sym_len(), get_sym()); } /* * create a new context variable copy of this symbol */ CTcSymbol *CTcSymLocalBase::new_ctx_var() const { /* create a new local with the same name */ CTcSymLocal *lcl = new CTcSymLocal( get_sym(), get_sym_len(), FALSE, FALSE, 0); /* refer the copy back to the original (i.e., me) */ lcl->set_ctx_orig((CTcSymLocal *)this); /* set up the context variable information */ if (!is_ctx_local_) { /* * The original is a true local - we're at the first context * level, and we don't yet have a property assigned, since we * don't know if this variable is actually going to be * referenced. */ lcl->set_ctx_level(1); } else { /* * The original was already a context variable - we're at one * higher context level in this function, and we use the same * context property as the original did. */ lcl->set_ctx_level(ctx_level_ + 1); } /* return the new symbol */ return lcl; } /* * Apply context variable conversion */ int CTcSymLocalBase::apply_ctx_var_conv( CTcPrsSymtab *symtab, CTPNCodeBody *code_body) { /* * if this symbol isn't referenced, simply delete it from the table, * so that it doesn't get entered in the debug records; and there's * no need to propagate it back to the enclosing scope as a context * variable, since it's not referenced from this enclosed scope */ if (!referenced_) { /* remove the symbol from the table */ symtab->remove_entry(this); /* this variable doesn't need to be converted */ return FALSE; } /* * convert the symbol in the enclosing scope to a context local, if * it's not already */ if (ctx_orig_ != 0) { /* convert the original to a context symbol */ ctx_orig_->convert_to_ctx_var(get_val_used(), get_val_assigned()); /* * ask the code body for the context object's local variable for * our recursion level */ ctx_var_num_ = code_body->get_or_add_ctx_var_for_level(ctx_level_); /* note that we've set our context variable ID */ ctx_var_num_set_ = TRUE; /* this variable was converted */ return TRUE; } /* this variable wasn't converted */ return FALSE; } /* * convert this variable to a context variable */ void CTcSymLocalBase::convert_to_ctx_var(int val_used, int val_assigned) { /* if I'm not already a context local, mark me as a context local */ if (!is_ctx_local_) { /* mark myself as a context local */ is_ctx_local_ = TRUE; /* * we haven't yet assigned our local context variable, since * we're still processing the inner scope at this point; just * store placeholders for now so we know to come back and fix * this up */ ctx_var_num_ = 0; ctx_arr_idx_ = 0; } /* note that I've been referenced */ mark_referenced(); /* propagate the value-used and value-assigned flags */ if (val_used) set_val_used(TRUE); if (val_assigned) set_val_assigned(TRUE); /* propagate the conversion to the original symbol */ if (ctx_orig_ != 0) ctx_orig_->convert_to_ctx_var(val_used, val_assigned); } /* * finish the context variable conversion */ void CTcSymLocalBase::finish_ctx_var_conv() { /* * If this isn't already marked as a context variable, there's * nothing to do - this variable must not have been referenced from * an anonymous function yet, and hence can be kept in the stack. * * Similarly, if my context local variable number has been assigned * already, there's nothing to do - we must have been set to refer * back to a context variable in an enclosing scope (this can happen * in a nested anonymous function). */ if (!is_ctx_local_ || ctx_var_num_set_) return; /* * tell the parser to create a local context for this scope, if it * hasn't already */ G_prs->init_local_ctx(); /* use the local context variable specified by the parser */ ctx_var_num_ = G_prs->get_local_ctx_var(); ctx_var_num_set_ = TRUE; /* assign our array index */ if (ctx_arr_idx_ == 0) ctx_arr_idx_ = G_prs->alloc_ctx_arr_idx(); } /* * Get my context variable array index */ int CTcSymLocalBase::get_ctx_arr_idx() const { /* * if I'm based on an original symbol from another scope, use the * same property ID as the original symbol */ if (ctx_orig_ != 0) return ctx_orig_->get_ctx_arr_idx(); /* return my context property */ return ctx_arr_idx_; } /* ------------------------------------------------------------------------ */ /* * object symbol entry base */ /* * fold the symbol as a constant */ CTcPrsNode *CTcSymObjBase::fold_constant() { /* if it's not a TadsObject symbol, we can't fold it into a constant */ if (get_metaclass() != TC_META_TADSOBJ) return 0; /* set up the object constant */ CTcConstVal cval; cval.set_obj(get_obj_id(), get_metaclass()); /* return a constant node */ return new CTPNConst(&cval); } /* * Add a superclass name entry. */ void CTcSymObjBase::add_sc_name_entry(const char *txt, size_t len) { /* create the entry object */ CTPNSuperclass *entry = new CTPNSuperclass(txt, len); /* link it into our list */ if (sc_name_tail_ != 0) sc_name_tail_->nxt_ = entry; else sc_name_head_ = entry; sc_name_tail_ = entry; } /* * Check to see if I have a given superclass. */ int CTcSymObjBase::has_superclass(class CTcSymObj *sc_sym) const { /* * Scan my direct superclasses. For each one, check to see if my * superclass matches the given superclass, or if my superclass * inherits from the given superclass. */ for (CTPNSuperclass *entry = sc_name_head_ ; entry != 0 ; entry = entry->nxt_) { /* look up this symbol */ CTcSymObj *entry_sym = (CTcSymObj *)G_prs->get_global_symtab()->find( entry->get_sym_txt(), entry->get_sym_len()); /* * if the entry's symbol doesn't exist or isn't an object symbol, * skip it */ if (entry_sym == 0 || entry_sym->get_type() != TC_SYM_OBJ) continue; /* * if it matches the given superclass, we've found the given * superclass among our direct superclasses, so we definitely have * the given superclass */ if (entry_sym == sc_sym) return TRUE; /* * ask the symbol if the given class is among its direct or * indirect superclasses - if it's a superclass of my superclass, * it's also my superclass */ if (entry_sym->has_superclass(sc_sym)) return TRUE; } /* * we didn't find the given class anywhere among our superclasses or * their superclasses, so it must not be a superclass of ours */ return FALSE; } /* * Add a deleted property entry */ void CTcSymObjBase::add_del_prop_to_list(CTcObjPropDel **list_head, CTcSymProp *prop_sym) { /* create the new entry */ CTcObjPropDel *entry = new CTcObjPropDel(prop_sym); /* link it into my list */ entry->nxt_ = *list_head; *list_head = entry; } /* * Add a self-reference fixup */ void CTcSymObjBase::add_self_ref_fixup(CTcDataStream *stream, ulong ofs) { /* add a fixup to our list */ CTcIdFixup::add_fixup(&fixups_, stream, ofs, obj_id_); } /* * Add a word to my vocabulary */ void CTcSymObjBase::add_vocab_word(const char *txt, size_t len, tctarg_prop_id_t prop) { /* create a new vocabulary entry */ CTcVocabEntry *entry = new (G_prsmem) CTcVocabEntry(txt, len, prop); /* link it into my list */ entry->nxt_ = vocab_; vocab_ = entry; } /* * Delete a vocabulary property from my list (for 'replace') */ void CTcSymObjBase::delete_vocab_prop(tctarg_prop_id_t prop) { /* scan my list and delete each word defined for the given property */ for (CTcVocabEntry *prv = 0, *entry = vocab_, *nxt = 0 ; entry != 0 ; entry = nxt) { /* remember the next entry */ nxt = entry->nxt_; /* if this entry is for the given property, unlink it */ if (entry->prop_ == prop) { /* * it matches - unlink it from the list (note that we don't * have to delete the entry, because it's allocated in * parser memory and thus will be deleted when the parser is * deleted) */ if (prv != 0) prv->nxt_ = nxt; else vocab_ = nxt; /* * this entry is no longer in any list (we don't really have * to clear the 'next' pointer here, since nothing points to * 'entry' any more, but doing so will make it obvious that * the entry was removed from the list, which could be handy * during debugging from time to time) */ entry->nxt_ = 0; } else { /* * this entry is still in the list, so it's now the previous * entry for our scan */ prv = entry; } } } /* * Add my words to the dictionary, associating the words with the given * object. This can be used to add my own words to the dictionary or to * add my words to a subclass's dictionary. */ void CTcSymObjBase::inherit_vocab() { /* * if I've already inherited my superclass vocabulary, there's * nothing more we need to do */ if (vocab_inherited_) return; /* make a note that I've inherited my superclass vocabulary */ vocab_inherited_ = TRUE; /* inherit words from each superclass */ for (CTcObjScEntry *sc = sc_ ; sc != 0 ; sc = sc->nxt_) { /* make sure this superclass has built its inherited list */ sc->sym_->inherit_vocab(); /* add this superclass's words to my list */ sc->sym_->add_vocab_to_subclass((CTcSymObj *)this); } } /* * Add my vocabulary words to the given subclass's vocabulary list */ void CTcSymObjBase::add_vocab_to_subclass(CTcSymObj *sub) { /* add each of my words to the subclass */ for (CTcVocabEntry *entry = vocab_ ; entry != 0 ; entry = entry->nxt_) { /* add this word to my dictionary */ sub->add_vocab_word(entry->txt_, entry->len_, entry->prop_); } } /* * Set my base 'modify' object. This tells us the object that we're * modifying. */ void CTcSymObjBase::set_mod_base_sym(CTcSymObj *sym) { /* remember the object I'm modifying */ mod_base_sym_ = sym; /* * set the other object's link back to me, so it knows that I'm the * object that's modifying it */ if (sym != 0) sym->set_modifying_sym((CTcSymObj *)this); } /* * Get the appropriate stream for a given metaclass */ CTcDataStream *CTcSymObjBase::get_stream_from_meta(tc_metaclass_t meta) { switch(meta) { case TC_META_TADSOBJ: /* it's the regular object stream */ return G_os; case TC_META_ICMOD: /* intrinsic class modifier stream */ return G_icmod_stream; default: /* other metaclasses have no stream */ return 0; } } /* * Add a class-specific template */ void CTcSymObjBase::add_template(CTcObjTemplate *tpl) { /* link it in at the tail of our list */ if (template_tail_ != 0) template_tail_->nxt_ = tpl; else template_head_ = tpl; template_tail_ = tpl; } /* ------------------------------------------------------------------------ */ /* * metaclass symbol */ /* * add a property */ void CTcSymMetaclassBase::add_prop( const char *txt, size_t len, const char *obj_fname, int is_static) { /* see if this property is already defined */ CTcSymProp *prop_sym = (CTcSymProp *)G_prs->get_global_symtab()->find(txt, len); if (prop_sym != 0) { /* it's already defined - make sure it's a property */ if (prop_sym->get_type() != TC_SYM_PROP) { /* * it's something other than a property - log the * appropriate type of error, depending on whether we're * loading this from an object file or from source code */ if (obj_fname == 0) { /* creating from source - note the code location */ G_tok->log_error_curtok(TCERR_REDEF_AS_PROP); } else { /* loading from an object file */ G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_REDEF_SYM_TYPE, (int)len, txt, "property", obj_fname); } /* forget the symbol - it's not a property */ prop_sym = 0; } } else { /* add the property definition */ prop_sym = new CTcSymProp(txt, len, FALSE, G_cg->new_prop_id()); G_prs->get_global_symtab()->add_entry(prop_sym); } /* * if we found a valid property symbol, add it to the metaclass * property list */ if (prop_sym != 0) { /* * mark the symbol as referenced - even if we don't directly * make use of it, the metaclass table references this symbol */ prop_sym->mark_referenced(); /* add the property to the metaclass list */ add_prop(prop_sym, is_static); } } /* * add a property */ void CTcSymMetaclassBase::add_prop(class CTcSymProp *prop, int is_static) { /* create a new list entry for the property */ CTcSymMetaProp *entry = new (G_prsmem) CTcSymMetaProp(prop, is_static); /* link it at the end of our list */ if (prop_tail_ != 0) prop_tail_->nxt_ = entry; else prop_head_ = entry; prop_tail_ = entry; /* count the addition */ ++prop_cnt_; } /* * get the nth property in our table */ CTcSymMetaProp *CTcSymMetaclassBase::get_nth_prop(int n) const { /* traverse the list to the desired index */ CTcSymMetaProp *prop; for (prop = prop_head_ ; prop != 0 && n != 0 ; prop = prop->nxt_, --n) ; /* return the property */ return prop; } /* ------------------------------------------------------------------------ */ /* * property symbol entry base */ /* * fold an address constant */ CTcPrsNode *CTcSymPropBase::fold_addr_const() { /* set up the property pointer constant */ CTcConstVal cval; cval.set_prop(get_prop()); /* return a constant node */ return new CTPNConst(&cval); } /* ------------------------------------------------------------------------ */ /* * Enumerator symbol base */ /* * fold the symbol as a constant */ CTcPrsNode *CTcSymEnumBase::fold_constant() { /* set up the enumerator constant */ CTcConstVal cval; cval.set_enum(get_enum_id()); /* return a constant node */ return new CTPNConst(&cval); } /* ------------------------------------------------------------------------ */ /* * Parser dictionary hash table entry */ /* * add an item to my list of object associations */ void CVmHashEntryPrsDict::add_item(tc_obj_id obj, tc_prop_id prop) { /* search my list for an existing association to the same obj/prop */ CTcPrsDictItem *item; for (item = list_ ; item != 0 ; item = item->nxt_) { /* if it matches, we don't need to add this one again */ if (item->obj_ == obj && item->prop_ == prop) return; } /* not found - create a new item */ item = new (G_prsmem) CTcPrsDictItem(obj, prop); /* link it into my list */ item->nxt_ = list_; list_ = item; } /* ------------------------------------------------------------------------ */ /* * Code Body Parse Node */ /* * instantiate */ CTPNCodeBodyBase::CTPNCodeBodyBase( CTcPrsSymtab *lcltab, CTcPrsSymtab *gototab, CTPNStm *stm, int argc, int opt_argc, int varargs, int varargs_list, CTcSymLocal *varargs_list_local, int local_cnt, int self_valid, CTcCodeBodyRef *enclosing_code_body) { /* remember the data in the code body */ lcltab_ = lcltab; gototab_ = gototab; stm_ = stm; argc_ = argc; opt_argc_ = opt_argc; varargs_ = varargs; varargs_list_ = varargs_list; varargs_list_local_ = varargs_list_local; local_cnt_ = local_cnt; self_valid_ = self_valid; self_referenced_ = FALSE; full_method_ctx_referenced_ = FALSE; op_overload_ = FALSE; is_anon_method_ = FALSE; is_dyn_func_ = FALSE; is_dyn_method_ = FALSE; /* remember the enclosing code body */ enclosing_code_body_ = enclosing_code_body; /* presume we won't need a local context object */ has_local_ctx_ = FALSE; local_ctx_arr_size_ = 0; ctx_head_ = ctx_tail_ = 0; local_ctx_needs_self_ = FALSE; local_ctx_needs_full_method_ctx_ = FALSE; /* presume we have an internal fixup list */ fixup_owner_sym_ = 0; fixup_list_anchor_ = &internal_fixups_; /* no internal fixups yet */ internal_fixups_ = 0; /* we haven't been replaced yet */ replaced_ = FALSE; /* leave it up to the caller to set the starting location */ start_desc_ = 0; start_linenum_ = 0; /* * remember the source location of the closing brace, which should * be the current location when we're instantiated */ end_desc_ = G_tok->get_last_desc(); end_linenum_ = G_tok->get_last_linenum(); } /* * fold constants */ CTcPrsNode *CTPNCodeBodyBase::fold_constants(class CTcPrsSymtab *) { /* * fold constants in our compound statement, in the scope of our * local symbol table */ if (stm_ != 0) stm_->fold_constants(lcltab_); /* we are not directly changed by this operation */ return this; } /* * Check for unreferenced labels */ void CTPNCodeBodyBase::check_unreferenced_labels() { /* * enumerate our labels - skip this check if we're only parsing the * program for syntax */ if (gototab_ != 0 && !G_prs->get_syntax_only()) gototab_->enum_entries(&unref_label_cb, this); } /* * Callback for enumerating labels for checking for unreferenced labels */ void CTPNCodeBodyBase::unref_label_cb(void *, CTcSymbol *sym) { /* if it's a label, check it out */ if (sym->get_type() == TC_SYM_LABEL) { CTcSymLabel *lbl = (CTcSymLabel *)sym; /* * get its underlying statement, and make sure it has a * control-flow flag for goto, continue, or break */ if (lbl->get_stm() != 0) { ulong flags; /* * get the explicit control flow flags for this statement -- * these flags indicate the use of the label in a goto, * break, or continue statement */ flags = lbl->get_stm()->get_explicit_control_flow_flags(); /* * if the flags aren't set for at least one of the explicit * label uses, the label is unreferenced */ if ((flags & (TCPRS_FLOW_GOTO | TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT)) == 0) lbl->get_stm()->log_warning(TCERR_UNREFERENCED_LABEL, (int)lbl->get_sym_len(), lbl->get_sym()); } } } /* * add an absolute fixup to my list */ void CTPNCodeBodyBase::add_abs_fixup(CTcDataStream *ds, ulong ofs) { /* ask the code body to add the fixup */ CTcAbsFixup::add_abs_fixup(fixup_list_anchor_, ds, ofs); } /* * add an absolute fixup at the current stream offset */ void CTPNCodeBodyBase::add_abs_fixup(CTcDataStream *ds) { /* ask the code body to add the fixup */ CTcAbsFixup::add_abs_fixup(fixup_list_anchor_, ds, ds->get_ofs()); } /* * Get the context variable for a given level */ int CTPNCodeBodyBase::get_or_add_ctx_var_for_level(int level) { /* scan our list to see if the level is already assigned */ CTcCodeBodyCtx *ctx; for (ctx = ctx_head_ ; ctx != 0 ; ctx = ctx->nxt_) { /* if we've already set up this level, return its variable */ if (ctx->level_ == level) return ctx->var_num_; } /* we didn't find it - allocate a new level structure */ ctx = new (G_prsmem) CTcCodeBodyCtx(); /* set up its level and allocate a new variable and property for it */ ctx->level_ = level; ctx->var_num_ = G_prs->alloc_ctx_holder_var(); /* * allocating a new variable probably increased the maximum local * variable count - update our information from the parser */ local_cnt_ = G_prs->get_max_local_cnt(); /* link it into our list */ ctx->prv_ = ctx_tail_; ctx->nxt_ = 0; if (ctx_tail_ != 0) ctx_tail_->nxt_ = ctx; else ctx_head_ = ctx; ctx_tail_ = ctx; /* return the variable for the new level */ return ctx->var_num_; } /* * Find a local context for a given level */ int CTPNCodeBodyBase::get_ctx_var_for_level(int level, int *varnum) { /* if they want level zero, it's our local context */ if (level == 0) { /* set the variable ID to our local context variable */ *varnum = local_ctx_var_; /* return true only if we actually have a local context */ return has_local_ctx_; } /* scan our list to see if the level is already assigned */ for (CTcCodeBodyCtx *ctx = ctx_head_ ; ctx != 0 ; ctx = ctx->nxt_) { /* if we've already set up this level, return its variable */ if (ctx->level_ == level) { /* set the caller's variable number */ *varnum = ctx->var_num_; /* indicate that we found it */ return TRUE; } } /* didn't find it */ return FALSE; } /* * Get the immediately enclosing code body */ CTPNCodeBody *CTPNCodeBodyBase::get_enclosing() const { /* * if we have no enclosing code body reference, we have no enclosing * code body */ if (enclosing_code_body_ == 0) return 0; /* get the code body from my enclosing code body reference object */ return enclosing_code_body_->ptr; } /* * Get the outermost enclosing code body */ CTPNCodeBody *CTPNCodeBodyBase::get_outermost_enclosing() const { CTPNCodeBody *cur; CTPNCodeBody *nxt; /* * scan each enclosing code body until we find one without an enclosing * code body */ for (cur = 0, nxt = get_enclosing() ; nxt != 0 ; cur = nxt, nxt = nxt->get_enclosing()) ; /* return what we found */ return cur; } /* * Get the base function symbol for a code body defining a modified * function (i.e., 'modify ...'). This is the function to which * 'replaced' refers within this code body and within nested code bodies. */ class CTcSymFunc *CTPNCodeBodyBase::get_replaced_func() const { CTcSymFunc *b; CTPNCodeBody *enc; /* if we have an associated function symbol, it's the base function */ if ((b = get_func_sym()) != 0) return b->get_mod_base(); /* * if we have an enclosing code body, then 'replaced' here means the * same thing it does there, since we don't explicitly replace anything * here */ if ((enc = get_enclosing()) != 0) return enc->get_replaced_func(); /* if we haven't found anything yet, we don't have a base function */ return 0; } /* ------------------------------------------------------------------------ */ /* * Anonymous function */ /* * mark as replaced/obsolete */ void CTPNAnonFuncBase::set_replaced(int flag) { if (code_body_ != 0) code_body_->set_replaced(flag); } /* ------------------------------------------------------------------------ */ /* * Generic statement node */ /* * initialize at the tokenizer's current source file position */ CTPNStmBase::CTPNStmBase() { /* get the current source location from the parser */ init(G_prs->get_cur_desc(), G_prs->get_cur_linenum()); } /* * log an error at this statement's source file position */ void CTPNStmBase::log_error(int errnum, ...) const { va_list marker; /* display the message */ va_start(marker, errnum); G_tcmain->v_log_error(file_, linenum_, TC_SEV_ERROR, errnum, marker); va_end(marker); } /* * log a warning at this statement's source file position */ void CTPNStmBase::log_warning(int errnum, ...) const { va_list marker; /* display the message */ va_start(marker, errnum); G_tcmain->v_log_error(file_, linenum_, TC_SEV_WARNING, errnum, marker); va_end(marker); } /* * Generate code for a sub-statement */ void CTPNStmBase::gen_code_substm(CTPNStm *substm) { /* set the error reporting location to refer to the sub-statement */ G_tok->set_line_info(substm->get_source_desc(), substm->get_source_linenum()); /* generate code for the sub-statement */ substm->gen_code(TRUE, TRUE); /* restore the error reporting location to the main statement */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); } /* ------------------------------------------------------------------------ */ /* * superclass record */ /* * get my symbol */ CTcSymbol *CTPNSuperclass::get_sym() const { /* if we know the symbol already, return it directly */ if (sym_ != 0) return sym_; /* look up my symbol by name in the global symbol table */ return G_prs->get_global_symtab()->find(sym_txt_, sym_len_); } /* * am I a subclass of the given class? */ int CTPNSuperclass::is_subclass_of(const CTPNSuperclass *other) const { /* * if my name matches, we're a subclass (we are a subclass of * ourselves) */ if (other->sym_len_ == sym_len_ && memcmp(other->sym_txt_, sym_txt_, sym_len_) == 0) return TRUE; /* * We're a subclass if any of our superclasses are subclasses of the * given object. Get my object symbol, and make sure it's really a * tads-object - if it's not, we're definitely not a subclass of * anything. */ CTcSymObj *sym = (CTcSymObj *)get_sym(); if (sym == 0 || sym->get_type() != TC_SYM_OBJ || sym->get_metaclass() != TC_META_TADSOBJ) return FALSE; /* scan our symbol's superclass list for a match */ for (CTPNSuperclass *sc = sym->get_sc_name_head() ; sc != 0 ; sc = sc->nxt_) { /* * if this one's a subclass of the given class, we're a subclass * as well, since we're a subclass of this superclass */ if (sc->is_subclass_of(other)) return TRUE; } /* * we didn't find any superclass that's a subclass of the given * class, so we're not a subclass of the given class */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * 'return' statement */ /* * fold constants */ CTcPrsNode *CTPNStmReturnBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* fold constants in the expression, if we have one */ if (expr_ != 0) expr_ = expr_->fold_constants(symtab); /* we are not directly changed by this operation */ return this; } /* ------------------------------------------------------------------------ */ /* * Formal type list */ /* * add a typed parameter to the list - 'tok' is the symbol giving the type * name */ void CTcFormalTypeList::add_typed_param(const CTcToken *tok) { add(new (G_prsmem) CTcFormalTypeEle(tok->get_text(), tok->get_text_len())); } /* add an untyped parameter to the list */ void CTcFormalTypeList::add_untyped_param() { add(new (G_prsmem) CTcFormalTypeEle()); } /* add a list element */ void CTcFormalTypeList::add(CTcFormalTypeEle *ele) { /* link it into our list */ if (tail_ != 0) tail_->nxt_ = ele; else head_ = ele; tail_ = ele; ele->nxt_ = 0; } /* * create a decorated name token for the multi-method defined by the given * function name and our type list */ void CTcFormalTypeList::decorate_name(CTcToken *decorated_name, const CTcToken *func_base_name) { CTcFormalTypeEle *ele; size_t len; const char *p; /* figure out how much space we need for the decorated name */ for (len = func_base_name->get_text_len() + 1, ele = head_ ; ele != 0 ; ele = ele->nxt_) { /* add this type name's length, if there's a name */ if (ele->name_ != 0) len += ele->name_len_; /* add a semicolon after the type */ len += 1; } /* add "...;" if it's varargs */ if (varargs_) len += 4; /* allocate space for the name */ G_tok->reserve_source(len); /* start with the function name */ p = G_tok->store_source_partial(func_base_name->get_text(), func_base_name->get_text_len()); /* add a "*" separator for the multi-method indicator */ G_tok->store_source_partial("*", 1); /* add each type name */ for (ele = head_ ; ele != 0 ; ele = ele->nxt_) { /* add the type, if it has one (if not, leave the type empty) */ if (ele->name_ != 0) G_tok->store_source_partial(ele->name_, ele->name_len_); /* add a semicolon to terminate the parameter name */ G_tok->store_source_partial(";", 1); } /* add the varargs indicator ("...;"), if applicable */ if (varargs_) G_tok->store_source_partial("...;", 4); /* null-terminate it */ G_tok->store_source_partial("\0", 1); /* set the decorated token name */ decorated_name->settyp(TOKT_SYM); decorated_name->set_text(p, len); } /* formal list element - construction */ CTcFormalTypeEle::CTcFormalTypeEle(const char *name, size_t len) { name_ = new (G_prsmem) char[len + 1]; memcpy(name_, name, len); name_len_ = len; } qtads-2.1.7/tads3/tcprs.h000066400000000000000000004116011265017072300151650ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/tcprs.h,v 1.5 1999/07/11 00:46:58 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcprs.h - TADS 3 Compiler - parser Function Notes Modified 04/29/99 MJRoberts - Creation */ #ifndef TCPRS_H #define TCPRS_H #include #include "vmtype.h" #include "t3std.h" #include "tcglob.h" #include "tctok.h" #include "tctargty.h" #include "tcprstyp.h" /* ------------------------------------------------------------------------ */ /* * Object ID type */ typedef ulong tc_obj_id; /* * Property ID type */ typedef uint tc_prop_id; /* ------------------------------------------------------------------------ */ /* * scope data structure */ struct tcprs_scope_t { /* local symbol table */ class CTcPrsSymtab *local_symtab; /* enclosing scope's local symbol table */ class CTcPrsSymtab *enclosing_symtab; /* number of locals allocated in scope */ int local_cnt; }; /* ------------------------------------------------------------------------ */ /* * Code body parsing types. Each type of code body is essentially the * same with minor variations, so we use a common code body parser that * checks the parsing type to apply the variations. */ enum tcprs_codebodytype { /* a standard function or method code body */ TCPRS_CB_NORMAL, /* anonymous function */ TCPRS_CB_ANON_FN, /* anonymous method */ TCPRS_CB_ANON_METHOD, /* short-form anonymous function */ TCPRS_CB_SHORT_ANON_FN }; /* ------------------------------------------------------------------------ */ /* * the saved method context is always at index 1 in local variable context * arrays, when we're using local variable context arrays */ #define TCPRS_LOCAL_CTX_METHODCTX 1 /* ------------------------------------------------------------------------ */ /* * Parser */ class CTcParser { public: CTcParser(); ~CTcParser(); /* initialize - call this after the code generator is set up */ void init(); /* * reset - for dynamic code compilation in the interpreter, this resets * internal state for the start of a new compilation */ void reset() { /* forget any old local symbol table */ local_symtab_ = 0; /* clear out all of our lists */ nested_stm_head_ = nested_stm_tail_ = 0; anon_obj_head_ = anon_obj_tail_ = 0; nonsym_obj_head_ = nonsym_obj_tail_ = 0; exp_head_ = exp_tail_ = 0; enclosing_stm_ = 0; dict_cur_ = dict_head_ = dict_tail_ = 0; dict_prop_head_ = 0; gramprod_head_ = gramprod_tail_ = 0; template_head_ = template_tail_ = 0; cur_code_body_ = 0; /* clear out any symbol table references */ local_symtab_ = 0; enclosing_local_symtab_ = 0; goto_symtab_ = 0; } /* * define (def=TRUE) or look up (def=FALSE) a special compiler-defined * property */ class CTcSymProp *def_special_prop(int def, const char *name, tc_prop_id *idp = 0); /* * Set the module information. This tells us the module's name (as * it's given in the makefile (.t3m) or library (.tl) file) and its * sequence number (an ordinal giving its position in the list of * modules making up the overall program build). We use this * information in generating the sourceTextGroup object. */ void set_module_info(const char *name, int seqno); /* * Write an exported symbol file. An exported symbol file * facilitates separate compilation by providing a listing of the * symbols defined in another module. If module A depends on the * symbols from module B, the user can first create an exported * symbol file for module B, then can compile module A in the * presence of B's symbol file, without actually loading B, and * without manually entering a set of external definitions in module * A's source code. */ void write_symbol_file(class CVmFile *fp, class CTcMake *make_obj); /* * Seek to the start of the build configuration information in a symbol * file. The return value is the number of bytes stored in the build * configuration block; on return, the file object will have its seek * offset set to the first byte of the build configuration data. * Returns zero if the symbol file is invalid or does not contain any * configuration data. */ static ulong seek_sym_file_build_config_info(class CVmFile *fp); /* * Write the global table to an object file. */ void write_to_object_file(class CVmFile *fp); /* * Read an object file and load it into the global symbol table. We * will fill in the object and property ID translation tables * provided with the translated values for the object and property * symbols that we find in the object file. * * Returns zero on success; logs error messages and returns non-zero * on error. Note that a non-zero value should be returned only * when the file appears to be corrupted or an I/O error occurs; * errors involving conflicting symbols, or other problems that do * not prevent us from continuing to read the file in an orderly * fashion, should not return failure but should simply log the * error and continue; this way, we can detect any additional symbol * conflicts or other errors. This routine should return failure * only when it is not possible to continue reading the file. */ int load_object_file(class CVmFile *fp, const textchar_t *fname, tctarg_obj_id_t *obj_xlat, tctarg_prop_id_t *prop_xlat, ulong *enum_xlat); /* * Apply internal object/property ID fixups. This traverses the * symbol table and calls each symbol's apply_internal_fixups() * method. This can be called once after loading all object files. */ void apply_internal_fixups(); /* * Read an exported symbol file. Reads the file and loads the * global symbol table with the symbols in the file, with each * symbol marked as external. * * This can be used for separate compilation. If module A depends * on symbols in module B, first create a symbol file for module B, * then module A can be compiled simply be pre-loading B's symbol * file. Any symbol files that a module depends upon must be loaded * before the module is compiled - symbol file loading must precede * parsing. * * If any errors occur, we'll log the errors and return non-zero. * We'll return zero on success. */ int read_symbol_file(class CVmFile *fp); /* get the global symbol table */ class CTcPrsSymtab *get_global_symtab() const { return global_symtab_; } /* set the global symbol table */ class CTcPrsSymtab *set_global_symtab(class CTcPrsSymtab *t) { /* remember the old table */ class CTcPrsSymtab *old = global_symtab_; /* set the new table */ global_symtab_ = t; /* refresh the cache of special internal symbols */ cache_special_props(FALSE); /* return the old symbol table */ return old; } /* get the current local symbol table */ class CTcPrsSymtab *get_local_symtab() const { return local_symtab_; } /* get the 'goto' symbol table */ class CTcPrsSymtab *get_goto_symtab() const { return goto_symtab_; } /* set the current pragma C mode */ void set_pragma_c(int mode); /* turn preprocess expression mode on or off */ void set_pp_expr_mode(int f) { pp_expr_mode_ = f; } /* get the current preprocess expression mode flag */ int get_pp_expr_mode() const { return pp_expr_mode_; } /* set/get the sourceTextGroup mode */ void set_source_text_group_mode(int f); int get_source_text_group_mode() const { return src_group_mode_; } /* get/set the syntax-only mode flag */ int get_syntax_only() const { return syntax_only_; } void set_syntax_only(int f) { syntax_only_ = f; } /* get/set the debugger expression flag */ int is_debug_expr() const { return debug_expr_; } void set_debug_expr(int f) { debug_expr_ = f; } /* * Get the constructor and finalize property ID's - all constructors * and finalizers have these property ID's respectively */ tc_prop_id get_constructor_prop() const { return constructor_prop_; } tc_prop_id get_finalize_prop() const { return finalize_prop_; } /* get the constructor property symbol */ class CTcSymProp *get_constructor_sym() const { return constructor_sym_; } /* get the exported GrammarProd exported */ tc_prop_id get_grammarTag_prop() const; tc_prop_id get_grammarInfo_prop() const; /* * Check for unresolved external symbols. Scans the global symbol * table and logs an error for each unresolved external. Returns * true if any unresolved externals exist, false if not. */ int check_unresolved_externs(); /* * build the dictionaries - scans the global symbol table, and * inserts each object symbol's dictionary words into its * corresponding dictionary */ void build_dictionaries(); /* build the grammar productions */ void build_grammar_productions(); /* * Top-level parser. Parse functions, objects, and other top-level * definitions and declarations. */ class CTPNStmProg *parse_top(); /* * Parse a required semicolon. If the semicolon is present, we'll * simply skip it. If it's missing, we'll log an error and try to * resynchronize. If we find something that looks like it should go * at the end of an expression, we'll try to skip up to the next * semicolon; otherwise, we'll simply stay put. * * Returns zero if the caller should proceed, non-zero if we're at * end of file, in which case there's nothing more for the caller to * parse. */ static int parse_req_sem(); /* * Skip to the next semicolon, ignoring any tokens up to that point. * This can be used when the caller encounters an error that makes * it impossible to process the current statement further, and wants * to find the next semicolon in the hope that it will be a good * place to start again with the next statement. * * Returns zero if the caller should proceed, non-zero if we reach * the end of the file. */ static int skip_to_sem(); /* * Parse an expression. This parses a top-level "comma" expression. */ class CTcPrsNode *parse_expr(); /* * Parse a condition expression. This parses a top-level "comma" * expression, but displays a warning if the outermost operator in * the expression is an assignment, because such expressions are * very frequently meant as comparisons, but the '=' operator was * inadvertantly used instead of '=='. */ class CTcPrsNode *parse_cond_expr(); /* * Parse a value expression or a double-quoted string expression * (including a double-quoted string with embedded expressions). If * allow_comma_expr is true, we'll parse a comma expression; * otherwise, we'll parse an assignment expression. (A comma * expression is broader than an assignment expression, since the * comma separates assignment expressions.) */ class CTcPrsNode *parse_expr_or_dstr(int allow_comma_expr); /* * Parse an assignment expression - this is the next precedence * level down from comma expressions. In certain contexts, a * top-level comma expression is not allowed because a comma has a * separate meaning (in the initializer clause of a 'for' statement, * for example, or in a list element). */ class CTcPrsNode *parse_asi_expr(); /* parse an 'enum' top-level statement */ void parse_enum(int *err); /* parse a 'dictionary' top-level statement */ class CTPNStmTop *parse_dict(int *err); /* parse a 'grammar' top-level statement */ class CTPNStmTop *parse_grammar(int *err, int replace, int modify); /* parse a grammar token list (an alternative list) */ void parse_gram_alts(int *err, class CTcSymObj *gram_obj, class CTcGramProdEntry *prod, struct CTcGramPropArrows *arrows, class CTcGramAltFuncs *funcs); /* parse and flatten a set of grammar rules */ class CTcPrsGramNode *flatten_gram_rule(int *err); /* parse a 'grammar' OR node */ class CTcPrsGramNode *parse_gram_or(int *err, int level); /* parse a 'grammar' CAT node */ class CTcPrsGramNode *parse_gram_cat(int *err, int level); /* parse a 'grammar' qualifier int value */ int parse_gram_qual_int(int *err, const char *qual_name, int *stm_end); /* skip to the end of a mal-formed grammar qualifier */ void parse_gram_qual_skip(int *err, int *stm_end); /* * Parse a 'function' top-level statement. If 'is_extern' is true, * the function is being defined externally, so it should have no * code body defined here (just the prototype). If 'replace' is * true, we're replacing an existing function. * * If 'func_kw_present' is true, the 'function' keyword is present * and must be skipped; otherwise, the function definition elides * the 'function' keyword and starts directly with the function name * symbol. */ class CTPNStmTop *parse_function(int *err, int is_extern, int replace, int modify, int func_kw_present); /* parse an 'intrinsic' top-level statement */ class CTPNStmTop *parse_intrinsic(int *err); /* parse an 'intrinsic class' top-level statement */ class CTPNStmTop *parse_intrinsic_class(int *err); /* parse an 'extern' top-level statement */ void parse_extern(int *err); /* * parse an object or function defintion (this is called when the * first thing in a statement is a symbol; we must check what * follows to determine what type of definition it is) */ class CTPNStmTop *parse_object_or_func(int *err, int replace, int suppress_error, int *suppress_next_error); /* parse a template definition statement */ class CTPNStmTop *parse_template_def(int *err, const class CTcToken *class_tok); /* parse a string template definition statement */ class CTPNStmTop *parse_string_template_def(int *err); /* add a template definition */ void add_template_def(class CTcSymObj *class_sym, class CTcObjTemplateItem *item_head, size_t item_cnt); /* add inherited template definitions */ void add_inherited_templates(class CTcSymObj *sc_sym, class CTcObjTemplateItem *item_head, size_t item_cnt); /* * expand the 'inherited' keyword in a template for the given * superclass template and add the result to the template list for the * class */ void expand_and_add_inherited_template(class CTcSymObj *sc_sym, class CTcObjTemplateItem *items, class CTcObjTemplate *sc_tpl); /* * build a list of superclass templates, for expanding an 'inherited' * token in a template definition */ void build_super_template_list(struct inh_tpl_entry **list_head, struct inh_tpl_entry **list_tail, class CTcSymObj *sc_sym); /* parse an 'object' statement */ class CTPNStmTop *parse_object_stm(int *err, int is_transient); /* * parse an object definition that starts with a '+' string; this * also parses '+ property' statements */ class CTPNStmTop *parse_plus_object(int *err); /* * Parse an object definition. If 'replace' is true, this * definition is to replace a previous definition of the same * object; if 'modify' is true, this definition is to modify a * previous definition. If 'is_class' is true, the definition is * for a class, otherwise it's for a static instance. * * If the definition uses the '+' notation to set the location, * plus_cnt gives the number of '+' signs preceding the object * definition. */ class CTPNStmTop *parse_object(int *err, int replace, int modify, int is_class, int plus_cnt, int is_transient); /* find or define an object symbol */ CTcSymObj *find_or_def_obj(const char *tok_txt, size_t tok_len, int replace, int modify, int *is_class, class CTcSymObj **mod_orig_sym, class CTcSymMetaclass **meta_sym, int *is_transient); /* parse an anonymous object */ class CTPNStmObject *parse_anon_object(int *err, int plus_cnt, int is_nested, struct tcprs_term_info *term_info, int is_transient); /* * Parse an object body. We start parsing from the colon that * introduces the class list, and parse the class list and the * property list for the object. * * If 'is_anon' is true, this is an anonymous object. 'obj_sym' * should be null in this case. * * If 'is_nested' is true, this is a nested object defined in-line in * an object's property list. Note that is_nested implies is_anon, * since nested objects are always anonymous. * * If this is a 'modify' definition, 'mod_orig_tok' should be set up * with the synthesized symbol for the modified base object; * otherwise, 'mod_orig_tok' should be null. * * If 'meta_sym' is non-null, we're modifying an intrinsic class. * This imposes certain restrictions; in particular, we cannot modify * a method defined in the native interface to the class. */ class CTPNStmObject *parse_object_body( int *err, class CTcSymObj *obj_sym, int is_class, int is_anon, int is_grammar, int is_nested, int modify, class CTcSymObj *mod_orig_sym, int plus_cnt, class CTcSymMetaclass *meta_sym, struct tcprs_term_info *term_info, int is_transient); /* parse an object definition's superclass list */ void parse_superclass_list( class CTcSymObj *obj_sym, class CTPNSuperclassList &sclist); /* * Add a generated object. This is used for objects created implicitly * rather than defined in the source code. For the static compiler, * we'll create an anonymous object and set up its definition * statements. For the dynamic compiler, we'll actually create the VM * object directly. */ class CTcSymObj *add_gen_obj(const char *clsname); class CTcSymObj *add_gen_obj(class CTcSymObj *cls); class CTcSymObj *add_gen_obj() { return add_gen_obj((class CTcSymObj *)0); } /* add constant property values to a generated object */ void add_gen_obj_prop(class CTcSymObj *obj, const char *prop, int val); void add_gen_obj_prop(class CTcSymObj *obj, const char *prop, const char *val); void add_gen_obj_prop(class CTcSymObj *obj, const char *prop, const CTcConstVal *val); /* parse an object template instance in an object body */ void parse_obj_template(int *err, class CTPNObjDef *objdef, int is_inline); /* search a superclass list for a template match */ const class CTcObjTemplate *find_class_template(const class CTPNSuperclass *first_sc, class CTcObjTemplateInst *src, size_t src_cnt, const CTPNSuperclass **def_sc, int *undescribed_class); /* find a match for a given template in the given list */ const class CTcObjTemplate *find_template_match(const class CTcObjTemplate *first_tpl, class CTcObjTemplateInst *src, size_t src_cnt); /* * Match a template to a given actual template parameter list. Returns * true if we match, false if not. We'll fill in the actual list with * the property symbols that we matched; these values are only * meaningful if we return true to indicate a match. */ int match_template(const class CTcObjTemplateItem *tpl_head, class CTcObjTemplateInst *src, size_t src_cnt); /* get the first string template */ class CTcStrTemplate *get_str_template_head() const { return str_template_head_; } /* parse an object's property list */ int parse_obj_prop_list( int *err, class CTPNObjDef *objdef, class CTcSymMetaclass *meta_sym, int modify, int is_nested, int braces, int is_inline, struct tcprs_term_info *outer_term_info, struct tcprs_term_info *term_info); /* parse property definition within an object */ void parse_obj_prop( int *err, class CTPNObjDef *objdef, int replace, class CTcSymMetaclass *meta_sym, struct tcprs_term_info *term_info, struct propset_def *propset_stack, int propset_depth, int enclosing_obj_is_nested, int is_inline); /* parse a class definition */ class CTPNStmTop *parse_class(int *err); /* parse a 'modify' definition */ class CTPNStmTop *parse_modify(int *err); /* parse a 'replace' definition */ class CTPNStmTop *parse_replace(int *err); /* parse a 'property' statement */ void parse_property(int *err); /* parse an 'export' statement */ void parse_export(int *err); /* add an export for the given symbol; returns the new export record */ class CTcPrsExport *add_export(const char *sym, size_t sym_len); /* add an export record to our list */ void add_export_to_list(class CTcPrsExport *exp); /* get the head of the export list */ class CTcPrsExport *get_exp_head() const { return exp_head_; } /* * Parse a function or method body, starting with the formal parameter * list. If 'eq_before_brace' is set, we expect an '=' before the * opening brace of the code body, and we allow the expression syntax, * where an expression enclosed in parentheses can be used. * 'self_valid' indicates whether or not 'self' is valid in the context * of the code being compiled; for an object method, 'self' is usually * valid, while for a stand-alone function it isn't. */ class CTPNCodeBody *parse_code_body(int eq_before_brace, int is_obj_prop, int self_valid, int *p_argc, int *p_opt_argc, int *p_varargs, int *p_varargs_list, class CTcSymLocal ** p_varargs_list_local, int *has_retval, int *err, class CTcPrsSymtab *local_symtab, tcprs_codebodytype cb_type, struct propset_def *propset_stack, int propset_depth, struct CTcCodeBodyRef *enclosing, class CTcFormalTypeList **type_list); /* parse a nested code body (such as an anonymous function) */ class CTPNCodeBody *parse_nested_code_body( int eq_before_brace, int self_valid, int *p_argc, int *p_opt_argc, int *p_varargs, int *p_varargs_list, class CTcSymLocal **p_varargs_list_local, int *has_retval, int *err, class CTcPrsSymtab *local_symtab, tcprs_codebodytype cb_type); /* insert a propertyset expansion */ void insert_propset_expansion(struct propset_def *propset_stack, int propset_depth); /* parse a formal parameter list */ void parse_formal_list(int count_only, int opt_allowed, int *argc, int *opt_argc, int *varargs, int *varargs_list, class CTcSymLocal **varargs_list_local, int *err, int base_formal_num, int for_short_anon_func, class CTcFormalTypeList **type_list); /* * Parse a compound statement. If 'skip_lbrace' is true, we'll skip * the opening '{', otherwise the caller must already have skipped it. * If 'need_rbrace' is true, we require the block to be closed by an * '}', which we'll skip before returning; otherwise, the block can end * at end-of-file on the token stream. * * 'enclosing_symtab' is the enclosing scope's symbol table, and * 'local_symtab' is the symbol table for the new scope within the * compound statement; if the caller has not already allocated a new * symbol table for the inner scope, it should simply pass the same * value for both symbol tables. * * 'enclosing_switch' is the immediately enclosing switch statement, if * any. This is only set when we're parsing the immediate body of a * switch statement. */ class CTPNStmComp *parse_compound(int *err, int skip_lbrace, int need_rbrace, class CTPNStmSwitch *enclosing_switch, int use_enclosing_scope); /* parse a local variable definition */ class CTPNStm *parse_local(int *err); /* parse a local initializer */ class CTcPrsNode *parse_local_initializer(class CTcSymLocal *lcl, int *err); /* * Parse an individual statement. * * If 'compound_use_enclosing_scope' is true, then if the statement * is a compound statement (i.e., the current token is a left * brace), the compound statement will use the current scope rather * than creating its own scope. Normally, a compound statement * establishes its own scope, so that local variables can hide * locals and parameters defined outside the braces. In certain * cases, however, locals defined within the braces should share the * enclosing scope: at the top level of a function or method, for * example, the formal parameters and the locals within the function * body go in the same scope, so the function body's compound * statement doesn't create its own scope. */ class CTPNStm *parse_stm(int *err, class CTPNStmSwitch *enclosing_switch, int compound_use_enclosing_scope); /* parse a 'case' label */ class CTPNStm *parse_case(int *err, class CTPNStmSwitch *enclosing_switch); /* parse a 'default' label */ class CTPNStm *parse_default(int *err, class CTPNStmSwitch *enclosing_switch); /* parse an 'if' statement */ class CTPNStm *parse_if(int *err); /* parse a 'return' statement */ class CTPNStm *parse_return(int *err); /* parse a 'for' statement */ class CTPNStm *parse_for(int *err); /* parse an 'in' clause in a 'for' statement */ class CTcPrsNode *parse_for_in_clause( class CTcPrsNode *lval, class CTPNForIn *&head, class CTPNForIn *&tail); /* parse a 'foreach' statement */ class CTPNStm *parse_foreach(int *err); /* parse a 'break' statement */ class CTPNStm *parse_break(int *err); /* parse a 'continue' statement */ class CTPNStm *parse_continue(int *err); /* parse a 'while' */ class CTPNStm *parse_while(int *err); /* parse a 'do-while' */ class CTPNStm *parse_do_while(int *err); /* parse a 'switch' */ class CTPNStm *parse_switch(int *err); /* parse a 'goto' */ class CTPNStm *parse_goto(int *err); /* parse a 'try' */ class CTPNStm *parse_try(int *err); /* parse a 'throw' */ class CTPNStm *parse_throw(int *err); /* parse an 'operator' property name */ int parse_op_name(class CTcToken *tok, int *op_argp = 0); /* * Create a symbol node. We'll look up the symbol in local scope. * If we find the symbol in local scope, we'll return a resolved * symbol node for the local scope item. If the symbol isn't * defined in local scope, we'll return an unresolved symbol node, * so that the symbol's resolution can be deferred until code * generation. */ class CTcPrsNode *create_sym_node(const textchar_t *sym, size_t sym_len); /* * Get the source file descriptor and line number for the current * source line. We note this at the start of each statement, so * that a statement node constructed when we finish parsing the * statement can record the location of the start of the statement. */ class CTcTokFileDesc *get_cur_desc() const { return cur_desc_; } long get_cur_linenum() const { return cur_linenum_; } /* * Get/set the current enclosing statement. An enclosing statement * is a 'try' or 'label:' container. At certain times, we need to * know the current enclosing statement, or one of its enclosing * statements; for example, a 'break' with a label must find the * label in the enclosing statement list to know where to jump to * after the 'break', and must also know about all of the enclosing * 'try' blocks our to that point so that it can invoke their * 'finally' blocks. */ class CTPNStmEnclosing *get_enclosing_stm() const { return enclosing_stm_; } class CTPNStmEnclosing *set_enclosing_stm(class CTPNStmEnclosing *stm) { class CTPNStmEnclosing *old_enclosing; /* remember the current enclosing statement for a moment */ old_enclosing = enclosing_stm_; /* set the new enclosing statement */ enclosing_stm_ = stm; /* * return the previous enclosing statement - this allows the * caller to restore the previous enclosing statement upon * leaving a nested block, if that's why the caller is setting a * new enclosing statement */ return old_enclosing; } /* get the current code body reference object */ struct CTcCodeBodyRef *get_cur_code_body() const { return cur_code_body_; } /* determine if 'self' is valid in the current context */ int is_self_valid() const { return self_valid_; } /* * get/set the 'self' reference status - this indicates whether or not * 'self' has been referenced, explicitly via the 'self' * pseudo-variable or implicitly (such as via a property reference or * method call), in the code body currently being parsed */ int self_referenced() const { return self_referenced_; } void set_self_referenced(int f) { self_referenced_ = f; } /* * get/set the full method context reference status - this indicates * whether or not any of the method context variables (self, * targetprop, targetobj, definingobj) have been referenced, explicitly * or implicitly, in the code body currently being parsed */ int full_method_ctx_referenced() const { return full_method_ctx_referenced_; } void set_full_method_ctx_referenced(int f) { full_method_ctx_referenced_ = f; } /* * Get/set the flag indicating whether or not the local context of the * outermost code body needs 'self'. The outer code body needs 'self' * in the local context if any lexically nested code body requires * access to 'self'. */ int local_ctx_needs_self() const { return local_ctx_needs_self_; } void set_local_ctx_needs_self(int f) { local_ctx_needs_self_ = f; } /* * Get/set the flag indicating whether or not the local context of the * outermost code body needs the full method context stored in its * local context. The outer code body needs the full context stored if * any lexically nested code body requires access to any of the method * context variables besides 'self' (targetprop, targetobj, * definingobj). */ int local_ctx_needs_full_method_ctx() const { return local_ctx_needs_full_method_ctx_; } void set_local_ctx_needs_full_method_ctx(int f) { local_ctx_needs_full_method_ctx_ = f; } /* * Add a code label. This creates a 'goto' symbol table for the * current code body if one doesn't already exist */ class CTcSymLabel *add_code_label(const class CTcToken *tok); /* * Set the debugger local symbol table. Returns the previous symbol * table so that it can be restored if desired. */ class CTcPrsDbgSymtab *set_debug_symtab(class CTcPrsDbgSymtab *tab) { class CTcPrsDbgSymtab *old_tab; /* remember the original for later use */ old_tab = debug_symtab_; /* set the new table */ debug_symtab_ = tab; /* return the original */ return old_tab; } /* * given a (1-based) object file symbol index, get the symbol */ class CTcSymbol *get_objfile_sym(uint idx) { return (idx == 0 ? 0 : obj_sym_list_[idx - 1]); } /* * given a 1-based object file symbol index, get an object symbol; * if the symbol does not refer to an object, we'll return null */ class CTcSymObj *get_objfile_objsym(uint idx); /* * given an object file (1-based) object file dictionary index, get * the dictionary entry */ class CTcDictEntry *get_obj_dict(uint idx) { return (idx == 0 ? 0 : obj_dict_list_[idx - 1]); } /* add a dictionary object loaded from the object file */ void add_dict_from_obj_file(class CTcSymObj *sym); /* add a symbol object loaded from the object file */ void add_sym_from_obj_file(uint idx, class CTcSymbol *sym); /* * Get the next object file symbol index. Object file symbol * indices are used to relate symbols stored in the object file to * the corresponding symbol object in memory when the object file is * reloaded. */ uint get_next_obj_file_sym_idx() { /* return the next index, consuming the index value */ return obj_file_sym_idx_++; } /* * Get the next object file dictionary index. */ uint get_next_obj_file_dict_idx() { /* return the next index, consuming the index value */ return obj_file_dict_idx_++; } /* * add an anonymous function or other anonymous top-level statement * to our list of nested top-level statements */ void add_nested_stm(class CTPNStmTop *stm); /* get the head of the nested statement list */ class CTPNStmTop *get_first_nested_stm() const { return nested_stm_head_; } /* add an anonymous object to our list */ void add_anon_obj(class CTcSymObj *obj); /* add a non-symbolic object ID */ void add_nonsym_obj(tctarg_obj_id_t id); /* determine if the current code body has a local context */ int has_local_ctx() const { return has_local_ctx_ != 0; } /* get the local context variable number */ int get_local_ctx_var() const { return local_ctx_var_num_; } /* set up a local context, in preparation for a nested code body */ void init_local_ctx(); /* finish the local context, after parsing a nested code body */ void finish_local_ctx(CTPNCodeBody *cb, class CTcPrsSymtab *local_symtab); /* * allocate a context variable index - this assigns an array index * for a context variable within the context object that contains * the shared locals for its scope */ int alloc_ctx_arr_idx(); /* allocate a local for use as a local context holder */ int alloc_ctx_holder_var() { return alloc_local(); } /* get the maximum number of locals required in the function */ int get_max_local_cnt() const { return max_local_cnt_; } /* get the lexicalParent property symbol */ class CTcSymProp *get_lexical_parent_sym() const { return lexical_parent_sym_; } /* * find a grammar production symbol, adding a new one if needed, * returning the grammar production list entry for the object */ class CTcGramProdEntry *declare_gramprod(const char *sym, size_t len); /* find a grammar production list entry for a given object */ class CTcGramProdEntry *get_gramprod_entry(class CTcSymObj *sym); /* find a grammar production symbol, adding a new one if needed */ class CTcSymObj *find_or_def_gramprod(const char *txt, size_t len, class CTcGramProdEntry **entryp); /* allocate a new enumerator ID */ ulong new_enum_id() { return next_enum_id_++; } /* get the number of enumerator ID's allocated */ ulong get_enum_count() const { return next_enum_id_; } /* * Look up a property symbol, adding it if not yet defined. If the * symbol is defined as another type, we'll show an error if * show_err is true, and return null. */ CTcSymProp *look_up_prop(const class CTcToken *tok, int show_err); /* get the '+' property for tracking the location graph */ CTcSymProp *get_plus_prop() const { return plus_prop_; } /* * Read a length-prefixed string from a file. Copies the string into * tokenizer space (which is guaranteed valid throughout compilation), * and returns a pointer to the tokenizer copy. If ret_len is null, * we'll return a null-terminated string; otherwise, we'll return a * non-null-terminated string and set *ret_len to the length of the * string. * * The string must fit in the temporary buffer to be read, but the * permanent tokenizer copy is returned rather than the temp buffer. * If the string doesn't fit in the temp buffer (with null * termination, if null termination is requested), we'll log the given * error. */ static const char *read_len_prefix_str (CVmFile *fp, char *tmp_buf, size_t tmp_buf_len, size_t *ret_len, int err_if_too_long); /* * Read a length-prefixed string into the given buffer, null * terminating the result. If the string is too long for the buffer, * we'll flag the given error code and return non-zero. If * successful, we'll return zero. */ static int read_len_prefix_str(CVmFile *fp, char *buf, size_t buf_len, int err_if_too_long); /* get the miscVocab property symbol */ tctarg_prop_id_t get_miscvocab_prop() const { return miscvocab_prop_; } /* property symbols for the operator overload properties */ class CTcSymProp *ov_op_add_; class CTcSymProp *ov_op_sub_; class CTcSymProp *ov_op_mul_; class CTcSymProp *ov_op_div_; class CTcSymProp *ov_op_mod_; class CTcSymProp *ov_op_xor_; class CTcSymProp *ov_op_shl_; class CTcSymProp *ov_op_ashr_; class CTcSymProp *ov_op_lshr_; class CTcSymProp *ov_op_bnot_; class CTcSymProp *ov_op_bor_; class CTcSymProp *ov_op_band_; class CTcSymProp *ov_op_neg_; class CTcSymProp *ov_op_idx_; class CTcSymProp *ov_op_setidx_; /* embedded expression token capture list */ class CTcEmbedTokenList *embed_toks_; private: /* cache the special properties, defining them if desired */ void cache_special_props(int def); /* * Static compiler handling for object generation. These are * implemented only in the static compiler; they're stubbed out for the * dynamic compiler, because the dynamic compiler generates objects in * the live VM through the G_vmifc interface instead. */ CTcSymObj *add_gen_obj_stat(class CTcSymObj *cls); void add_gen_obj_prop_stat(class CTcSymObj *obj, class CTcSymProp *prop, const CTcConstVal *val); /* clear the anonymous function local context information */ void clear_local_ctx(); /* * begin a property expression, saving parser state for later * restoration with finish_prop_expr */ void begin_prop_expr( class CTcPrsPropExprSave *save_info, int is_static, int is_inline); /* * Finish a property expression, wrapping it in a code body if * necessary to allow for an embedded anonymous function. Returns * null if no wrapping is required, in which case the original * expression should continue to be used, or the non-null code body * wrapper if needed, in which case the original expression should be * discarded in favor of the fully wrapped code body. */ void finish_prop_expr( class CTcPrsPropExprSave *save_info, class CTcPrsNode* &expr, class CTPNCodeBody* &code_body, class CTPNAnonFunc* &inline_method, int is_static, int is_inline, class CTcSymProp *prop_sym); /* * callback for symbol table enumeration for writing a symbol export * file */ static void write_sym_cb(void *ctx, class CTcSymbol *sym); /* callback for symbol table enumeration for writing an object file */ static void write_obj_cb(void *ctx, class CTcSymbol *sym); /* callback for symbol table enumeration for writing cross references */ static void write_obj_ref_cb(void *ctx, class CTcSymbol *sym); /* callback for symbol table enumeration for named grammar rules */ static void write_obj_gram_cb(void *ctx, class CTcSymbol *sym); /* callback for symbol table enumeration for merging grammar rules */ static void build_grammar_cb(void *ctx, class CTcSymbol *sym); /* * Enter a scope. Upon entering, we'll remember the current local * variable data; on leaving, we'll restore the enclosing scope. */ void enter_scope(struct tcprs_scope_t *info) { /* remember the current scope information */ info->local_symtab = local_symtab_; info->enclosing_symtab = enclosing_local_symtab_; info->local_cnt = local_cnt_; /* * We haven't yet allocated a symbol table local to the new * scope -- we defer this until we actually need to insert a * symbol into the new scope. In order to detect when we need * to create our own local symbol table, we keep track of the * enclosing symbol table; when the local table is the same as * the enclosing table, and we need to insert a symbol, it means * that we must create a new table for the current scope. */ enclosing_local_symtab_ = local_symtab_; } /* leave a scope */ void leave_scope(struct tcprs_scope_t *info) { /* restore enclosing scope information */ local_symtab_ = info->local_symtab; enclosing_local_symtab_ = info->enclosing_symtab; /* return to the local count in the enclosing scope */ // $$$ we can't actually do this because variables could // be allocated after this scope ends, but need lifetimes // that overlap with the enclosed scope; what we actually // need to do, if we wanted to optimize things, would be // to allow this block of variables to be used in *disjoint* // scopes, but not again in enclosing scopes. We can easily, // though suboptimally, handle this by simply not allowing // the variables in the enclosed scope to be re-used at all // in the current code block. // local_cnt_ = info->local_cnt; } /* * Create a local symbol table in the current scope, if necessary. * If we've already created a local symbol table for the current * scope, this has no effect. */ void create_scope_local_symtab(); /* allocate a new local variable ID */ int alloc_local() { /* * if this exceeds the maximum depth in the block so far, note * the new maximum depth */ if (local_cnt_ + 1 > max_local_cnt_) max_local_cnt_ = local_cnt_ + 1; /* return the local number, and increment the counter */ return local_cnt_++; } /* find a dictionary symbol, adding a new one if needed */ class CTcDictEntry *declare_dict(const char *sym, size_t len); /* create a new dictionary list entry */ class CTcDictEntry *create_dict_entry(class CTcSymObj *sym); /* find a dictionary list entry for a given object */ class CTcDictEntry *get_dict_entry(class CTcSymObj *sym); /* create a new grammar production list entry */ class CTcGramProdEntry *create_gramprod_entry(class CTcSymObj *sym); /* symbol enumerator - look for unresolved external references */ static void enum_sym_extref(void *ctx, class CTcSymbol *sym); /* symbol enumerator - apply internal fixups */ static void enum_sym_internal_fixup(void *ctx, class CTcSymbol *sym); /* symbol enumerator - build dictionary */ static void enum_sym_dict(void *ctx, class CTcSymbol *sym); /* enumeration callback - context local conversion */ static void enum_for_ctx_locals(void *ctx, class CTcSymbol *sym); /* global symbol table */ class CTcPrsSymtab *global_symtab_; /* the constructor property ID and symbol */ tc_prop_id constructor_prop_; class CTcSymProp *constructor_sym_; /* the finalizer property ID */ tc_prop_id finalize_prop_; /* grammarInfo property symbol */ class CTcSymProp *graminfo_prop_; /* grammarTag property symbol */ class CTcSymProp *gramtag_prop_; /* miscVocab property ID */ tctarg_prop_id_t miscvocab_prop_; /* lexicalParent property symbol */ class CTcSymProp *lexical_parent_sym_; /* sourceTextOrder property symbol */ class CTcSymProp *src_order_sym_; /* sourceTextGroup property symbol */ class CTcSymProp *src_group_sym_; /* sourceTextGroupName, sourceTextGroupOrder */ class CTcSymProp *src_group_mod_sym_; class CTcSymProp *src_group_seq_sym_; /* * Source text order index. Each time we encounter an object * definition in the source code, we assign the current index value to * the object's 'sourceTextOrder' property, then we increment the * index. This provides the game program with information on the order * in which static objects appear in the source code, so that the * program can sort a collection of objects into their source file * order if desired. */ long src_order_idx_; /* * Source group object. If we're assigning source text group values, * we create an object for each source module to identify the module. */ tctarg_obj_id_t src_group_id_; /* * flag: in preprocessor constant expression mode; double-quoted * strings should be treated the same as single-quoted strings for * concatenation and comparisons */ uint pp_expr_mode_ : 1; /* * Is source text mode turned on? If this is true, we'll generate * sourceTextGroup properties for objects, otherwise we won't. */ uint src_group_mode_ : 1; /* * Flag: syntax-only mode. We use this mode to analyze the syntax * of the file without building the image; this is used, for * example, to build the exported symbol file for a source file. In * this mode, we'll suppress certain warnings and avoid doing work * that's not necessary for syntactic analysis; for example, we * won't show "unreachable code" errors. */ uint syntax_only_ : 1; /* * Flag: debugger expression mode. We accept some special internal * syntax in this mode that's not allowed in ordinary source code. */ uint debug_expr_ : 1; /* * Code block parsing state */ /* * 'goto' symbol table for the current code block - there's only one * 'goto' scope for an entire code block, so this never changes over * the course of a code block */ class CTcPrsSymtab *goto_symtab_; /* * Current local symbol table. Each inner scope that defines its * own local variables has its own local symbol table, nested within * the enclosing scope's. When leaving an inner scope, this should * always be restored to the local symbol table of the enclosing * scope. */ class CTcPrsSymtab *local_symtab_; /* * Enclosing local symbol table. If this is the same as * local_symtab_, it means that the current scope has not yet * created its own local symbol table. We defer this creation until * we find we actually need a local symbol table in a scope, since * most scopes don't define any of their own local variables. */ class CTcPrsSymtab *enclosing_local_symtab_; /* * Current debugger local symbol table. When we're compiling a * debugger expression, this will provide access to the current * local scope in the debug records. */ class CTcPrsDbgSymtab *debug_symtab_; /* * Number of local variables allocated so far in current code block * -- this reflects nesting to the current innermost scope, because * variables in inner scope are allocated in the same stack frame as * the enclosing scopes. When leaving an inner scope, this should * be restored */ int local_cnt_; /* * maximum local variable depth for the current code block -- this * reflects the maximum depth, including all inner scopes so far */ int max_local_cnt_; /* * Enclosing statement - this is the innermost 'try' or 'label:' * enclosing the current code. */ class CTPNStmEnclosing *enclosing_stm_; /* file descriptor and line number at start of current statement */ class CTcTokFileDesc *cur_desc_; long cur_linenum_; /* currently active dictionary */ class CTcDictEntry *dict_cur_; /* head and tail of dictionary list */ class CTcDictEntry *dict_head_; class CTcDictEntry *dict_tail_; /* head and tail of grammar production entry list */ class CTcGramProdEntry *gramprod_head_; class CTcGramProdEntry *gramprod_tail_; /* * array of symbols loaded from the object file - these are indexed * by the object file symbol index stored in symbol references in * the object file, allowing us to fix up references from one symbol * to another during loading */ class CTcSymbol **obj_sym_list_; /* * array of dictionary objects for the object file being loaded - * these are indexed by the dictionary index stored in symbol * references in the object file, allowing us to fix up references * from an object to its dictionary */ class CTcDictEntry **obj_dict_list_; /* next available object file dictionary index */ uint obj_file_dict_idx_; /* next available object file symbol index */ uint obj_file_sym_idx_; /* dictionary property list head */ class CTcDictPropEntry *dict_prop_head_; /* * Head and tail of list of nested top-level statements parsed for the * current top-level statement. This list includes anonymous * functions and nested objects, since these statements must * ultimately be linked into the top-level statement queue, but can't * be linked in while they're being parsed because of their nested * location in the recursive descent. We'll throw each new nested * top-level statement into this list as we parse them, then add this * list to the top-level statement list when we're done with the * entire program. */ class CTPNStmTop *nested_stm_head_; class CTPNStmTop *nested_stm_tail_; /* * Anonymous object list. This is a list of objects which are * defined without symbol names. */ class CTcSymObj *anon_obj_head_; class CTcSymObj *anon_obj_tail_; /* * Non-symbolic object list. This is a list of objects that are * defined without symbols at all. */ struct tcprs_nonsym_obj *nonsym_obj_head_; struct tcprs_nonsym_obj *nonsym_obj_tail_; /* * Object template list - this is the master list of templates for the * root object class. */ class CTcObjTemplate *template_head_; class CTcObjTemplate *template_tail_; /* * Object template instance parsing expression array. Each time we * define a new template, we'll make sure this array is long enough * for the longest defined template. We use this list when we're * parsing a template instance to keep track of the expressions in * the template instance - we can't know until we have the entire * list which template we're using, so we must keep track of the * entire list until we reach the end of the list. */ class CTcObjTemplateInst *template_expr_; size_t template_expr_max_; /* * String template list. String templates are unrelated to object * templates; these are for custom syntax in << >> expressions in * strings. */ class CTcStrTemplate *str_template_head_; class CTcStrTemplate *str_template_tail_; /* head and tail of exported symbol list */ class CTcPrsExport *exp_head_; class CTcPrsExport *exp_tail_; /* * Flag: current code body has a local variable context object. If * this is set, we must generate code that sets up the context * object on entry to the code body. */ unsigned int has_local_ctx_ : 1; /* local variable number of the code body's local variable context */ int local_ctx_var_num_; /* array of context variable property values */ tctarg_prop_id_t *ctx_var_props_; /* size of array */ size_t ctx_var_props_size_; /* number of context variable property values in the list */ size_t ctx_var_props_cnt_; /* * number of context variable property values assigned to the * current code body */ size_t ctx_var_props_used_; /* next available local variable context index */ int next_ctx_arr_idx_; /* reference to the current code body being parsed */ CTcCodeBodyRef *cur_code_body_; /* flag: 'self' is valid in current code body */ int self_valid_; /* * flag: 'self' is used (explicitly or implicitly, such as via a * property reference or method call) in the current code body */ int self_referenced_; /* * Flag: method context beyond 'self' (targetprop, targetobj, * definingobj) is referenced (explicitly or implicitly, such as via * 'inherited' or 'delegated') in the current code body. */ int full_method_ctx_referenced_; /* * Flags: the local context of the outermost code body requires * 'self'/the full method context to be stored. */ int local_ctx_needs_self_; int local_ctx_needs_full_method_ctx_; /* next available enumerator ID */ ulong next_enum_id_; /* * The '+' property - this is the property that defines the * containment graph for the purposes of the '+' syntax. */ class CTcSymProp *plus_prop_; /* * '+' property location stack. Each time the program defines an * object using the '+' notation to set the location, we'll update our * record here of the last object at that depth. Any time an object * is defined at depth N (i.e., using N '+' signs), its location is * set to the last object at depth N-1. An object with no '+' signs * is at depth zero. */ class CTPNStmObject **plus_stack_; size_t plus_stack_alloc_; /* * The module name and sequence number, if known. The module name is * the name as it appears on the command line, makefile (.t3m), or * library (.tl) file. The sequence number is an ordinal giving its * position in the list of modules making up the overall program build. * We use this information in generating the sourceTextGroup object. */ char *module_name_; int module_seqno_; }; /* ------------------------------------------------------------------------ */ /* * Grammar tree parser - property arrow list */ struct CTcGramPropArrows { CTcGramPropArrows() { cnt = 0; } /* maximum number of arrows */ static const size_t max_arrows = 100; /* array of property arrows */ class CTcSymProp *prop[max_arrows]; /* number of arrows */ size_t cnt; }; /* ------------------------------------------------------------------------ */ /* * Grammar tree parser - compiler interface functions. We abstract a * number of functions from the compiler interface to allow for differences * in behavior between regular static compilation and run-time construction * of grammar rules. */ class CTcGramAltFuncs { public: /* look up a property symbol, defining it if it's undefined */ virtual class CTcSymProp *look_up_prop( const class CTcToken *tok, int show_err) = 0; /* declare a grammar production symbol */ virtual class CTcGramProdEntry *declare_gramprod( const char *txt, size_t len) = 0; /* check the given enum for use as a production token */ virtual void check_enum_tok(class CTcSymEnum *enumsym) = 0; /* handle EOF in an alternative list */ virtual void on_eof(int *err) = 0; }; /* ------------------------------------------------------------------------ */ /* * Grammar tree node - base class */ class CTcPrsGramNode: public CTcTokenSource { public: CTcPrsGramNode() { /* we have no siblings yet */ next_ = 0; } /* get the next token - does nothing by default */ virtual const CTcToken *get_next_token() { return 0; } /* consolidate OR nodes at the top of the subtree */ virtual CTcPrsGramNode *consolidate_or() = 0; /* flatten CAT nodes together in the tree */ virtual void flatten_cat() { /* by default, do nothing */ } /* am I an "or" node? */ virtual int is_or() { return FALSE; } /* am I a "cat" node? */ virtual int is_cat() { return FALSE; } /* get my token - if I'm not a token node, returns null */ virtual const CTcToken *get_tok() const { return 0; } /* initialize expansion - by default, we do nothing */ virtual void init_expansion() { } /* * Advance to the next expansion state. Returns true if we 'carry' out * of the current item, which means that we were already at our last * state and hence are wrapping back to our first state. Returns false * if we advanced to a new state without wrapping back. * * By default, since normal items have only one alternative, we don't * do anything but return a 'carry, since each advance takes us back to * our single and initial state. */ virtual int advance_expansion() { return TRUE; } /* clone the current expansion subtree */ virtual CTcPrsGramNode *clone_expansion() const = 0; /* next sibling node */ CTcPrsGramNode *next_; }; /* ------------------------------------------------------------------------ */ /* * Statement termination information. This is used for certain nested * definition parsers, where a lack of termination in the nested * definition is to be interpreted as being actually caused by a lack of * termination of the enclosing definition. */ struct tcprs_term_info { /* initialize */ void init(class CTcTokFileDesc *desc, long linenum) { /* remember the current location */ desc_ = desc; linenum_ = linenum; /* no termination error yet */ unterm_ = FALSE; } /* * source location where original terminator might have been - this is * where we decided to go into a nested definition, so if it turns out * that the definintion shouldn't have been nested after all, there * was missing termination here */ class CTcTokFileDesc *desc_; long linenum_; /* * flag: termination was in fact missing in the nested definition; the * nested parser sets this to relay the problem to the caller */ int unterm_; }; /* ------------------------------------------------------------------------ */ /* * Object template list entry */ class CTcObjTemplate { public: CTcObjTemplate(class CTcObjTemplateItem *item_head, size_t item_cnt) { /* remember my item list */ items_ = item_head; item_cnt_ = item_cnt; /* not in a list yet */ nxt_ = 0; } /* head of list of template items */ class CTcObjTemplateItem *items_; /* number of items in the list */ size_t item_cnt_; /* next template in master list of templates */ CTcObjTemplate *nxt_; }; /* * Object template list item */ class CTcObjTemplateItem { public: CTcObjTemplateItem(class CTcSymProp *prop, tc_toktyp_t tok_type, int is_alt, int is_opt) { /* remember my defining information */ prop_ = prop; tok_type_ = tok_type; is_alt_ = is_alt; is_opt_ = is_opt; /* not in a list yet */ nxt_ = 0; } /* property that the item in this position defines */ class CTcSymProp *prop_; /* token type of item in this position */ tc_toktyp_t tok_type_; /* next item in this template's item list */ CTcObjTemplateItem *nxt_; /* flag: this item is an alternative to the previous item */ unsigned int is_alt_ : 1; /* flag: this item is optional */ unsigned int is_opt_ : 1; }; /* * Template item instance - we keep track of the actual parameters to a * template with these items. */ class CTcObjTemplateInst { public: /* * expression value for the actual parameter, as either a naked * expression (expr_) or as a code body (code_body_) - only one of * expr_ or code_body_ will be valid */ class CTcPrsNode *expr_; class CTPNCodeBody *code_body_; class CTPNAnonFunc *inline_method_; /* * the introductory token of the parameter - if the parameter is * introduced by an operator token, this will not be part of the * expression */ tc_toktyp_t def_tok_; /* the first token of the value */ CTcToken expr_tok_; /* * The property to which to assign this actual parameter value. This * isn't filled in until we match the full list to an actual template, * since we don't know the meanings of the parameters until we match * the actuals to an existing template in memory. */ class CTcSymProp *prop_; }; /* ------------------------------------------------------------------------ */ /* * 'propertyset' definition structure. Each property set defines a * property pattern and an optional argument list for the properties within * the propertyset group. */ struct propset_def { /* the property name pattern */ const char *prop_pattern; size_t prop_pattern_len; /* head of list of tokens in the parameter list */ struct propset_tok *param_tok_head; }; /* * propertyset token list entry */ struct propset_tok { propset_tok(const CTcToken *t) { /* copy the token */ this->tok = *t; /* we're not in a list yet */ nxt = 0; } /* the token */ CTcToken tok; /* next token in the list */ propset_tok *nxt; }; /* * Token source for parsing formal parameters using property set formal * lists. This retrieves tokens from a propertyset stack. */ class propset_token_source: public CTcTokenSource { public: propset_token_source() { /* nothing in our list yet */ nxt_tok = last_tok = 0; } /* get the next token */ virtual const CTcToken *get_next_token() { /* if we have another entry in our list, retrieve it */ if (nxt_tok != 0) { /* remember the token to return */ CTcToken *ret = &nxt_tok->tok; /* advance our internal position to the next token */ nxt_tok = nxt_tok->nxt; /* return the token */ return ret; } else { /* we have nothing more to return */ return 0; } } /* insert a token */ void insert_token(const CTcToken *tok); /* insert a token based on type */ void insert_token(tc_toktyp_t typ, const char *txt, size_t len); /* the next token we're to retrieve */ propset_tok *nxt_tok; /* tail of our list */ propset_tok *last_tok; }; /* maximum propertyset nesting depth */ const size_t MAX_PROPSET_DEPTH = 10; /* ------------------------------------------------------------------------ */ /* * String template entry. A string template defines custom syntax for * embedded << >> expressions in strings. */ class CTcStrTemplate { public: CTcStrTemplate() { /* initially clear the token list */ head = tail = 0; cnt = 0; star = FALSE; /* no function symbol yet */ func = 0; /* not in the global list yet */ nxt = 0; } /* append a token to the list */ void append_tok(const CTcToken *tok); /* the token list */ CTcTokenEle *head, *tail; /* number of tokens in the list */ int cnt; /* do we have a '*' token? */ int star; /* the processor function symbol */ class CTcSymFunc *func; /* next list element */ CTcStrTemplate *nxt; }; /* ------------------------------------------------------------------------ */ /* * Non-symbolic object list entry */ struct tcprs_nonsym_obj { tcprs_nonsym_obj(tctarg_obj_id_t id) { /* remember the ID */ id_ = id; /* not in a list yet */ nxt_ = 0; } /* ID of this object */ tctarg_obj_id_t id_; /* next entry in the list */ tcprs_nonsym_obj *nxt_; }; /* ------------------------------------------------------------------------ */ /* * Dictionary property list entry. Each time the source code defines a * dictionary property, we'll make an entry in this list. */ class CTcDictPropEntry { public: CTcDictPropEntry(class CTcSymProp *prop) { /* remember the property */ prop_ = prop; /* not in a list yet */ nxt_ = 0; /* not defined for current object yet */ defined_ = FALSE; } /* my property */ class CTcSymProp *prop_; /* next entry in list */ CTcDictPropEntry *nxt_; /* flag: the current object definition includes this property */ unsigned int defined_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Dictionary list entry. Each dictionary object gets an entry in this * list. */ class CTcDictEntry { public: CTcDictEntry(class CTcSymObj *sym); /* get/set my object file index */ uint get_obj_idx() const { return obj_idx_; } void set_obj_idx(uint idx) { obj_idx_ = idx; } /* get my object symbol */ class CTcSymObj *get_sym() const { return sym_; } /* get/set the next item in the list */ CTcDictEntry *get_next() const { return nxt_; } void set_next(CTcDictEntry *nxt) { nxt_ = nxt; } /* add a word to the table */ void add_word(const char *txt, size_t len, int copy, tc_obj_id obj, tc_prop_id prop); /* write my symbol to the object file if I haven't already done so */ void write_sym_to_obj_file(CVmFile *fp); /* get the hash table */ class CVmHashTable *get_hash_table() const { return hashtab_; } protected: /* enumeration callback - write to object file */ static void enum_cb_writeobj(void *ctx, class CVmHashEntry *entry); /* associated object symbol */ class CTcSymObj *sym_; /* * object file index (we use this to match up the dictionary objects * when we re-load the object file) */ uint obj_idx_; /* next item in the dictionary list */ CTcDictEntry *nxt_; /* hash table containing the word entries */ class CVmHashTable *hashtab_; }; /* * entry in a dictionary list */ struct CTcPrsDictItem { CTcPrsDictItem(tc_obj_id obj, tc_prop_id prop) { obj_ = obj; prop_ = prop; nxt_ = 0; } /* object */ tc_obj_id obj_; /* property */ tc_prop_id prop_; /* next entry in list */ CTcPrsDictItem *nxt_; }; /* * Parser dictionary hash table entry */ class CVmHashEntryPrsDict: public CVmHashEntryCS { public: CVmHashEntryPrsDict(const char *txt, size_t len, int copy) : CVmHashEntryCS(txt, len, copy) { /* nothing in my list yet */ list_ = 0; } /* add an item to my list */ void add_item(tc_obj_id obj, tc_prop_id prop); /* get the list head */ struct CTcPrsDictItem *get_list() const { return list_; } protected: /* list of object/property associations with this word */ struct CTcPrsDictItem *list_; }; /* ------------------------------------------------------------------------ */ /* * Grammar production list entry */ class CTcGramProdEntry { public: CTcGramProdEntry(class CTcSymObj *prod_obj); /* get my production object symbol */ class CTcSymObj *get_prod_sym() const { return prod_sym_; } /* get/set the next item in the list */ CTcGramProdEntry *get_next() const { return nxt_; } void set_next(CTcGramProdEntry *nxt) { nxt_ = nxt; } /* add an alternative */ void add_alt(class CTcGramProdAlt *alt); /* get the alternative list head */ class CTcGramProdAlt *get_alt_head() const { return alt_head_; } /* write to an object file */ void write_to_obj_file(class CVmFile *fp); /* load from an object file */ static void load_from_obj_file(class CVmFile *fp, const tctarg_prop_id_t *prop_xlat, const ulong *enum_xlat, class CTcSymObj *private_owner); /* move alternatives from my list to the given target list */ void move_alts_to(CTcGramProdEntry *new_entry); /* get/set explicitly-declared flag */ int is_declared() const { return is_declared_; } void set_declared(int f) { is_declared_ = f; } protected: /* associated production object symbol */ class CTcSymObj *prod_sym_; /* next item in the list */ CTcGramProdEntry *nxt_; /* head and tail of alternative list */ class CTcGramProdAlt *alt_head_; class CTcGramProdAlt *alt_tail_; /* * flag: this production was explicitly declared (this means that we * will consider it valid at link time even if it has no alternatives * defined) */ unsigned int is_declared_ : 1; }; /* * Grammar production alternative. Each grammar production has one or * more alternatives that, when matched, generate the production. */ class CTcGramProdAlt { public: CTcGramProdAlt(class CTcSymObj *obj_sym, class CTcDictEntry *dict); /* get/set my score */ int get_score() const { return score_; } void set_score(int score) { score_ = score; } /* get/set my badness */ int get_badness() const { return badness_; } void set_badness(int badness) { badness_ = badness; } /* get my processor object symbol */ class CTcSymObj *get_processor_obj() const { return obj_sym_; } /* get/set the next list element */ CTcGramProdAlt *get_next() const { return nxt_; } void set_next(CTcGramProdAlt *nxt) { nxt_ = nxt; } /* add a token to my list */ void add_tok(class CTcGramProdTok *tok); /* get the head of my token list */ class CTcGramProdTok *get_tok_head() const { return tok_head_; } /* write to an object file */ void write_to_obj_file(class CVmFile *fp); /* load from an object file */ static CTcGramProdAlt * load_from_obj_file(class CVmFile *fp, const tctarg_prop_id_t *prop_xlat, const ulong *enum_xlat); /* get the dictionary in effect when the alternative was defined */ class CTcDictEntry *get_dict() const { return dict_; } protected: /* head and tail of our token list */ class CTcGramProdTok *tok_head_; class CTcGramProdTok *tok_tail_; /* dictionary in effect when alternative was defined */ class CTcDictEntry *dict_; /* the processor object associated with this alternative */ class CTcSymObj *obj_sym_; /* next alternative in our production */ CTcGramProdAlt *nxt_; /* score */ int score_; /* badness */ int badness_; }; /* grammar production token types */ enum tcgram_tok_type { /* unknown */ TCGRAM_UNKNOWN, /* match a production (given by the production object) */ TCGRAM_PROD, /* match a part of speech (given by the dictionary property) */ TCGRAM_PART_OF_SPEECH, /* match a literal string */ TCGRAM_LITERAL, /* token-type match */ TCGRAM_TOKEN_TYPE, /* free-floating end-of-string */ TCGRAM_STAR, /* match one of several parts of speech */ TCGRAM_PART_OF_SPEECH_LIST }; /* * Grammar production alternative token */ class CTcGramProdTok { public: CTcGramProdTok() { /* not in a list yet */ nxt_ = 0; /* no type yet */ typ_ = TCGRAM_UNKNOWN; /* no property association yte */ prop_assoc_ = TCTARG_INVALID_PROP; } /* get/set my next element */ CTcGramProdTok *get_next() const { return nxt_; } void set_next(CTcGramProdTok *nxt) { nxt_ = nxt; } /* set me to match a production object */ void set_match_prod(class CTcSymObj *obj) { /* remember the production object */ typ_ = TCGRAM_PROD; val_.obj_ = obj; } /* set me to match a token type */ void set_match_token_type(ulong enum_id) { /* remember the token enum ID */ typ_ = TCGRAM_TOKEN_TYPE; val_.enum_id_ = enum_id; } /* set me to match a dictionary property */ void set_match_part_of_speech(tctarg_prop_id_t prop) { /* remember the part of speech */ typ_ = TCGRAM_PART_OF_SPEECH; val_.prop_ = prop; } /* * set me to match a list of parts of speech; each part of speech must * be separately added via add_match_part_ele() */ void set_match_part_list(); /* add an element to the part-of-speech match list */ void add_match_part_ele(tctarg_prop_id_t prop); /* set me to match a literal string */ void set_match_literal(const char *txt, size_t len) { /* remember the string */ typ_ = TCGRAM_LITERAL; val_.str_.txt_ = txt; val_.str_.len_ = len; } /* set me to match a free-floating end-of-string */ void set_match_star() { /* set the type */ typ_ = TCGRAM_STAR; } /* get my type */ tcgram_tok_type get_type() const { return typ_; } /* get my value */ class CTcSymObj *getval_prod() const { return val_.obj_; } tctarg_prop_id_t getval_part_of_speech() const { return val_.prop_; } const char *getval_literal_txt() const { return val_.str_.txt_; } const size_t getval_literal_len() const { return val_.str_.len_; } ulong getval_token_type() const { return val_.enum_id_; } size_t getval_part_list_len() const { return val_.prop_list_.len_; } tctarg_prop_id_t getval_part_list_ele(size_t idx) const { return val_.prop_list_.arr_[idx]; } /* * get/set my property association - this is the property to which * the actual match to the rule is assigned when we match the rule */ tctarg_prop_id_t get_prop_assoc() const { return prop_assoc_; } void set_prop_assoc(tctarg_prop_id_t prop) { prop_assoc_ = prop; } /* write to an object file */ void write_to_obj_file(class CVmFile *fp); /* load from an object file */ static CTcGramProdTok * load_from_obj_file(class CVmFile *fp, const tctarg_prop_id_t *prop_xlat, const ulong *enum_xlat); protected: /* next token in my list */ CTcGramProdTok *nxt_; /* my type - this specifies how this token matches */ tcgram_tok_type typ_; /* match specification - varies according to my type */ union { /* object - for matching a production */ class CTcSymObj *obj_; /* property - for matching a part of speech */ tctarg_prop_id_t prop_; /* token enum id - for matching a token type */ ulong enum_id_; /* literal string */ struct { const char *txt_; size_t len_; } str_; /* list of vocabulary elements */ struct { /* number of array entries allocated */ size_t alo_; /* number of array entries actually used */ size_t len_; /* array of entries */ tctarg_prop_id_t *arr_; } prop_list_; } val_; /* property association */ tctarg_prop_id_t prop_assoc_; }; /* ------------------------------------------------------------------------ */ /* * Exported symbol record */ class CTcPrsExport { public: /* create with the given compiler symbol */ CTcPrsExport(const char *sym, size_t sym_len) { /* remember my name */ sym_ = sym; sym_len_ = sym_len; /* * we don't yet have an explicit external name, so export using * the internal name */ ext_name_ = sym; ext_len_ = sym_len; /* we're not in a list yet */ nxt_ = 0; } /* set the external name */ void set_extern_name(const char *txt, size_t len) { ext_name_ = txt; ext_len_ = len; } /* get the symbol name and length */ const char *get_sym() const { return sym_; } size_t get_sym_len() const { return sym_len_; } /* get the external name and length */ const char *get_ext_name() const { return ext_name_; } size_t get_ext_len() const { return ext_len_; } /* get/set the next entry in the list */ CTcPrsExport *get_next() const { return nxt_; } void set_next(CTcPrsExport *nxt) { nxt_ = nxt; } /* write to an object file */ void write_to_obj_file(class CVmFile *fp); /* read from an object file */ static CTcPrsExport *read_from_obj_file(class CVmFile *fp); /* determine if my external name matches the given export's */ int ext_name_matches(const CTcPrsExport *exp) const { return (exp->get_ext_len() == get_ext_len() && memcmp(exp->get_ext_name(), get_ext_name(), get_ext_len()) == 0); } /* determine if my name matches the given string */ int ext_name_matches(const char *txt) const { return (get_ext_len() == get_strlen(txt) && memcmp(get_ext_name(), txt, get_ext_len()) == 0); } /* determine if my name matches the leading substring */ int ext_name_starts_with(const char *txt) const { return (get_ext_len() >= get_strlen(txt) && memcmp(get_ext_name(), txt, get_strlen(txt)) == 0); } /* determine if my symbol name matches the given export's */ int sym_matches(const CTcPrsExport *exp) const { return (exp->get_sym_len() == get_sym_len() && memcmp(exp->get_sym(), get_sym(), get_sym_len()) == 0); } protected: /* symbol name - this is the internal compiler symbol being exported */ const char *sym_; size_t sym_len_; /* external name - this is the name visible to the VM loader */ const char *ext_name_; size_t ext_len_; /* next in list */ CTcPrsExport *nxt_; }; /* ------------------------------------------------------------------------ */ /* * Parser Symbol Table. The parser maintains a hierarchy of symbol * tables; a local symbol table can be nested inside an enclosing * scope's symbol table, and so on up to the top-level block scope, * which is enclosed by the global scope. In addition, at function * scope there's a separate table for "goto" labels. */ /* find_or_def actions for undefined symbols */ enum tcprs_undef_action { /* if undefined, add an "undefined" entry unconditionally */ TCPRS_UNDEF_ADD_UNDEF, /* add a "property" entry unconditionally, but warn about it */ TCPRS_UNDEF_ADD_PROP, /* add a "property" entry unconditionally, with no warning */ TCPRS_UNDEF_ADD_PROP_NO_WARNING, /* add a "weak" property entry without warning */ TCPRS_UNDEF_ADD_PROP_WEAK }; /* parser symbol table */ class CTcPrsSymtab { public: CTcPrsSymtab(CTcPrsSymtab *parent_scope); virtual ~CTcPrsSymtab(); /* allocate parser symbol tables out of the parser memory pool */ void *operator new(size_t siz); /* * perform static initialization/termination - call once at program * startup and shutdown (respectively) */ static void s_init(); static void s_terminate(); /* get the enclosing scope's symbol table */ CTcPrsSymtab *get_parent() const { return parent_; } /* find a symbol; returns null if the symbol isn't defined */ class CTcSymbol *find(const textchar_t *sym, size_t len) { return find(sym, len, 0); } class CTcSymbol *find(const textchar_t *sym) { return find(sym, strlen(sym), 0); } /* * Find a symbol; returns null if the symbol isn't defined. If * symtab is not null, we'll fill it in with the actual symbol table * in which we found the symbol; this might be an enclosing symbol * table, since we search up the enclosing scope list. */ class CTcSymbol *find(const textchar_t *sym, size_t len, CTcPrsSymtab **symtab); /* find a symbol without changing its referenced status */ class CTcSymbol *find_noref(const textchar_t *sym, size_t len, CTcPrsSymtab **symtab); /* * Find a symbol; if the symbol isn't defined, log an error and add * the symbol as type "undefined". Because we add a symbol entry if * the symbol isn't defined, this *always* returns a valid symbol * object. */ class CTcSymbol *find_or_def_undef(const char *sym, size_t len, int copy_str) { return find_or_def(sym, len, copy_str, TCPRS_UNDEF_ADD_UNDEF); } /* * Find a symbol; if the symbol isn't defined, log a warning and * define the symbol as type property. Because we add an entry if * the symbol isn't defined, this *always* returns a valid symbol * object. */ class CTcSymbol *find_or_def_prop(const char *sym, size_t len, int copy_str) { return find_or_def(sym, len, copy_str, TCPRS_UNDEF_ADD_PROP); } /* * Find a symbol; if the symbol isn't defined, define the symbol as * type property with no warning. This should be used when it is * unambiguous that a symbol is meant as a property name. Because we * add an entry if the symbol isn't defined, this *always* returns a * valid symbol object. */ class CTcSymbol *find_or_def_prop_explicit( const char *sym, size_t len, int copy_str) { return find_or_def(sym, len, copy_str, TCPRS_UNDEF_ADD_PROP_NO_WARNING); } /* * Find a symbol; if the symbol isn't defined, define it as a property * without warning, but flag it as a "weak" definition. */ class CTcSymbol *find_or_def_prop_weak( const char *sym, size_t len, int copy_str) { return find_or_def(sym, len, copy_str, TCPRS_UNDEF_ADD_PROP_WEAK); } /* * Find a symbol. If the symbol isn't defined, and a "self" object * is available, define the symbol as a property. If the symbol * isn't defined an no "self" object is available, add an * "undefined" entry for the symbol. */ class CTcSymbol *find_or_def_prop_implied(const char *sym, size_t len, int copy_str, int is_self_avail) { return find_or_def(sym, len, copy_str, is_self_avail ? TCPRS_UNDEF_ADD_PROP : TCPRS_UNDEF_ADD_UNDEF); } /* * Find a symbol. If the symbol is already defined as a "weak" * property, delete it to make way for the new definition. */ class CTcSymbol *find_delete_weak(const char *sym, size_t len); /* add a formal parameter symbol */ class CTcSymLocal *add_formal(const textchar_t *sym, size_t len, int formal_num, int copy_str); /* add a local variable symbol */ class CTcSymLocal *add_local(const textchar_t *sym, size_t len, int local_num, int copy_str, int init_assigned, int init_referenced); /* * add the current token as a local symbol, initially unassigned and * unreferenced */ class CTcSymLocal *add_local(int local_num); /* add a 'goto' symbol */ class CTcSymLabel *add_code_label(const textchar_t *sym, size_t len, int copy_str); /* enumerate entries in the table through a callback */ void enum_entries(void (*func)(void *, class CTcSymbol *), void *ctx); /* * Scan the symbol table and check for unreferenced locals. Logs an * error for each unreferenced or unassigned local. */ void check_unreferenced_locals(); /* * Get/set my debugging list index - this is the index of this table * in the list for this function or method. The index values start * at 1 - a value of zero indicates that the symbol table isn't part * of any list. */ int get_list_index() const { return list_index_; } void set_list_index(int n) { list_index_ = n; } /* get/set the next entry in the linked list */ CTcPrsSymtab *get_list_next() const { return list_next_; } void set_list_next(CTcPrsSymtab *nxt) { list_next_ = nxt; } /* * The low-level virtual interface. All searching and manipulation of * the underlying hash table goes through these routines. This allows * subclasses to implement the symbol table as a view of another data * structure. * * The compiler itself just uses the underlying hash table directly. * The dynamic compiler for interpreter "eval()" functionality uses a * customized version that creates a view of an underlying user-code * LookupTable object. */ /* find a symbol directly in this table, without searching parents */ virtual class CTcSymbol *find_direct(const textchar_t *sym, size_t len); /* add an entry to the table */ virtual void add_entry(class CTcSymbol *sym); /* remove an entry */ virtual void remove_entry(class CTcSymbol *sym); /* expand my byte code range to include the given location */ void add_to_range(int ofs) { /* * if we don't have a starting offset yet, or this is before it, * this is the new start offset */ if (start_ofs_ == 0 || ofs < start_ofs_) start_ofs_ = ofs; /* * if we don't have an ending offset yet, or this is after it, this * is the ending offset */ if (ofs > end_ofs_) end_ofs_ = ofs; } /* get my bytecode offset range */ int get_start_ofs() const { return start_ofs_; } int get_end_ofs() const { return end_ofs_; } protected: /* add an entry to a global symbol table */ static void add_to_global_symtab(CTcPrsSymtab *tab, CTcSymbol *entry); /* get the underlying hash table */ class CVmHashTable *get_hashtab() const { return hashtab_; } /* enumeration callback - check for unreferenced locals */ static void unref_local_cb(void *ctx, class CTcSymbol *sym); /* * find a symbol, or define a new symbol, according to the given * action mode, if the symbol is undefined */ class CTcSymbol *find_or_def(const textchar_t *sym, size_t len, int copy_str, tcprs_undef_action action); /* enclosing scope (parent) symbol table */ CTcPrsSymtab *parent_; /* hash table */ class CVmHashTable *hashtab_; /* hash function */ static class CVmHashFunc *hash_func_; /* * Byte code range covered by this frame. This is the range of offsets * within the method where this frame is in effect. The range is * inclusive of the start offset and exclusive of the end offset. */ int start_ofs_; int end_ofs_; /* * Next symbol table in local scope chain. For each function or * method, we keep a simple linear list of the local scopes so that * they can be written to the debugging records. We also keep an * index value giving its position in the list, so that we can store * references to the table using the list index. */ CTcPrsSymtab *list_next_; int list_index_; }; /* ------------------------------------------------------------------------ */ /* * Debugger symbol table interface. This is an abstract interface that * debuggers can implement to allow us to search for symbols that are * obtained from a compiled program's debugger records. To keep the * compiler independent of the target architecture and the debugger's * own internal structures, we define this abstract interface that the * debugger must implement. * * Since this type of symbol table is provided by a debugger as a view * on the symbol information in a previously compiled program, the * parser naturally has no need to add symbols to the table; hence the * only required operations are symbol lookups. */ class CTcPrsDbgSymtab { public: /* * Get information on a symbol. Returns true if the symbol is * found, false if not. If we find the symbol, fills in the * information structure with the appropriate data. */ virtual int find_symbol(const textchar_t *sym, size_t len, struct tcprsdbg_sym_info *info) = 0; }; /* * Debugger local symbol information structure */ struct tcprsdbg_sym_info { /* symbol type */ enum tc_symtype_t sym_type; /* local/parameter number */ uint var_id; /* context variable index - 0 if it's not a context local */ int ctx_arr_idx; /* stack frame index */ uint frame_idx; }; /* ------------------------------------------------------------------------ */ /* * Parse Tree storage manager. * * The parse tree has some special characteristics that make it * desirable to use a special memory manager for it. First, the parse * tree consists of many small objects, so we would like to have as * little overhead per object for memory tracking as possible. Second, * parse tree objects all have a similar lifetime: we create the entire * parse tree as we scan the source, then use it to generate target * code, then discard the whole thing. * * To manage memory efficiently for the parse tree, we define our own * memory manager for parse tree objects. The memory manager is very * simple, fast, and has minimal per-object overhead. We simply * maintain a list of large blocks, then suballocate requests out of the * large blocks. Each time we run out of space in a block, we allocate * a new block. We do not keep track of any extra tracking information * per node, so a node cannot be individually freed; however, the entire * block list can be freed at once, which is exactly the behavior we * want. */ class CTcPrsMem { public: CTcPrsMem(); ~CTcPrsMem(); /* allocate storage */ void *alloc(size_t siz); /* save the current pool state, for later resetting */ void save_state(struct tcprsmem_state_t *state); /* * reset the pool to the given state - delete all objects allocated * in the pool since the state was saved */ void reset(const struct tcprsmem_state_t *state); /* reset to initial state */ void reset(); private: /* delete all parser memory */ void delete_all(); /* allocate a new block */ void alloc_block(); /* head of list of memory blocks */ struct tcprsmem_blk_t *head_; /* tail of list and current memory block */ struct tcprsmem_blk_t *tail_; /* current allocation offset in last block */ char *free_ptr_; /* remaining space available in last block */ size_t rem_; }; /* * state-saving structure */ struct tcprsmem_state_t { /* current tail of memory block list */ struct tcprsmem_blk_t *tail; /* current allocation offset in last block */ char *free_ptr; /* current remaining space in last block */ size_t rem; }; /* * Provide an overridden operator new for allocating objects explicitly * from the pool */ inline void *operator new(size_t siz, CTcPrsMem *pool) { return pool->alloc(siz); } /* * provide an array operator new as well */ inline void *operator new[](size_t siz, CTcPrsMem *pool) { return pool->alloc(siz); } /* * parse tree memory block */ struct tcprsmem_blk_t { /* next block in the list */ tcprsmem_blk_t *next_; /* * This block's byte array (the array extends off the end of the * structure). */ char buf_[1]; }; /* ------------------------------------------------------------------------ */ /* * Special array list subclass that uses parser memory */ class CPrsArrayList: public CArrayList { protected: /* * override the memory management functions to use parser memory */ virtual void *alloc_mem(size_t siz) { /* allocate from the parser pool */ return G_prsmem->alloc(siz); } virtual void *realloc_mem(void *p, size_t oldsiz, size_t newsiz) { void *pnew; /* allocate a new block from the parser pool */ pnew = G_prsmem->alloc(newsiz); /* copy from the old block to the new block */ memcpy(pnew, p, oldsiz); /* return the new block */ return pnew; } virtual void free_mem(void *p) { /* * do nothing - the parser pool automatically frees everything as a * block when terminating the parser */ } }; /* ------------------------------------------------------------------------ */ /* * Expression Constant Value object. This object is used to express the * value of a constant expression. */ class CTcConstVal { public: CTcConstVal() { /* the type is unknown */ typ_ = TC_CVT_UNK; /* assume it's a true constant, not just a compile-time constant */ ctc_ = FALSE; } /* * determine if this is a constant value - it is a constant if it * has any known value */ int is_const() const { return (typ_ != TC_CVT_UNK); } /* * set the type to unknown - this indicates that there is no valid * value, which generally means that the associated expression does * not have a constant value */ void set_unknown() { typ_ = TC_CVT_UNK; } /* set from another value */ void set(const CTcConstVal *val) { /* copy the type */ typ_ = val->typ_; /* copy the value */ val_ = val->val_; } /* set an integer value */ void set_int(long val) { typ_ = TC_CVT_INT; val_.intval_ = val; } /* set a floating-point value */ void set_float(const char *val, size_t len, int promoted); /* set a floating-point value from a vbignum_t */ void set_float(const class vbignum_t *val, int promoted); /* set a floating-point value promoted from an integer */ void set_float(ulong i); /* * Check to see if a promoted float value can be demoted back to int. * If the value is a float that was flagged as promoted from an int * constant, and the value fits in an int32, we'll demote the value * back to an int. This has no effect if the value isn't a float, * wasn't promoted, or doesn't fit in an int. This can be used after a * constant-folding operation is applied to a value that was at some * point promoted from int to see if the promotion is no longer * required. */ void demote_float(); /* set an enumerator value */ void set_enum(ulong val) { typ_ = TC_CVT_ENUM; val_.enumval_ = val; } /* set a single-quoted string value */ void set_sstr(const char *val, size_t len); void set_sstr(const CTcToken *tok); /* set a regex string value (R'...' or R"...") */ void set_restr(const CTcToken *tok); /* set a list value */ void set_list(class CTPNList *lst); /* for the debugger only: set a pre-resolved constant pool value */ void set_sstr(uint32_t ofs); void set_list(uint32_t ofs); /* set an object reference value */ void set_obj(ulong obj, enum tc_metaclass_t meta) { typ_ = TC_CVT_OBJ; val_.objval_.id_ = obj; val_.objval_.meta_ = meta; } /* set a property pointer value */ void set_prop(uint prop) { typ_ = TC_CVT_PROP; val_.propval_ = prop; } /* set a function pointer value */ void set_funcptr(class CTcSymFunc *sym) { typ_ = TC_CVT_FUNCPTR; val_.funcptrval_ = sym; } /* set an anonymous function pointer value */ void set_anon_funcptr(class CTPNCodeBody *code_body) { typ_ = TC_CVT_ANONFUNCPTR; val_.codebodyval_ = code_body; } /* set a built-in function pointer value */ void set_bifptr(class CTcSymBif *sym) { typ_ = TC_CVT_BIFPTR; val_.bifptrval_ = sym; } /* set a nil/true value */ void set_nil() { typ_ = TC_CVT_NIL; } void set_true() { typ_ = TC_CVT_TRUE; } /* * Set a vocabulary list placeholder. This has no actual value * during compilation; instead, this is just a placeholder. During * linking, we'll replace each of these with a list of strings * giving the actual vocabulary for the property. */ void set_vocab_list() { typ_ = TC_CVT_VOCAB_LIST; } /* set a nil/true value based on a boolean value */ void set_bool(int val) { typ_ = (val ? TC_CVT_TRUE : TC_CVT_NIL); } /* is this a boolean value? */ int is_bool() const { return typ_ == TC_CVT_NIL || typ_ == TC_CVT_TRUE; } /* get my type */ tc_constval_type_t get_type() const { return typ_; } /* get my int value (no type checking) */ long get_val_int() const { return val_.intval_; } /* get my floating point value (no type checking) */ const char *get_val_float() const { return val_.floatval_.txt_; } size_t get_val_float_len() const { return val_.floatval_.len_; } /* was the value promoted to float from int due to overflow? */ int is_promoted() const { return promoted_; } /* get my enumerator value (no type checking) */ ulong get_val_enum() const { return val_.enumval_; } /* get my string value (no type checking) */ const char *get_val_str() const { return val_.strval_.strval_; } size_t get_val_str_len() const { return val_.strval_.strval_len_; } /* get my list value (no type checking) */ class CTPNList *get_val_list() const { return val_.listval_.l_; } /* * for debugger expressions only: the string/list as a pre-resolved * constant pool address */ uint32_t get_val_str_ofs() const { return val_.strval_.pool_ofs_; } uint32_t get_val_list_ofs() const { return val_.listval_.pool_ofs_; } /* get my object reference value (no type checking) */ ulong get_val_obj() const { return val_.objval_.id_; } enum tc_metaclass_t get_val_obj_meta() const { return val_.objval_.meta_; } /* get my property pointer value (no type checking) */ uint get_val_prop() const { return val_.propval_; } /* get my function pointer symbol value (no type checking) */ class CTcSymFunc *get_val_funcptr_sym() const { return val_.funcptrval_; } /* get my anonymous function pointer value (no type checking) */ class CTPNCodeBody *get_val_anon_func_ptr() const { return val_.codebodyval_; } /* get my built-in function pointer symbol value (noi type checking) */ class CTcSymBif *get_val_bifptr_sym() const { return val_.bifptrval_; } /* * Determine if this value equals a given constant value. Returns * true if so, false if not. We'll set (*can_compare) to true if * the values are comparable, false if the comparison is not * meaningful. */ int is_equal_to(const CTcConstVal *val) const; /* * Convert an integer, nil, or true value to a string. Fills in the * buffer with the result of the conversion if the value wasn't * already a string. If the value is already a string, we'll simply * return a pointer to the original string without making a copy. * Returns null if the value is not convertible to a string. */ const char *cvt_to_str(char *buf, size_t bufl, size_t *result_len); /* * Get my true/nil value. Returns false if the value is nil or zero, * true if it's anything else. */ int get_val_bool() const { return !(typ_ == TC_CVT_NIL || equals_zero()); } /* is this is a numeric value equal to zero? */ int equals_zero() const; /* * Set/get the compile-time constant flag. A compile-time constant is * a value that's constant at compile-time, but which can vary from one * compilation to the next. The defined() and __objref() operators * have this property. */ void set_ctc(int f) { ctc_ = f; } int is_ctc() const { return ctc_; } private: /* my type */ tc_constval_type_t typ_; union { /* integer value (valid when typ_ == TC_CVT_INT) */ long intval_; /* floating-point value (valid when typ_ == TC_CVT_FLOAT) */ struct { const char *txt_; size_t len_; } floatval_; /* enumerator value (valid when typ_ == TC_CVT_ENUM) */ ulong enumval_; /* * String value (valid when typ_ == TC_CVT_TYPE_SSTR). We need * to know the length separately, because the underyling string * may not be null-terminated. */ struct { const char *strval_; size_t strval_len_; /* * For debugger expressions only: the pre-resolved constant * pool address in the live running program of a string or list * expression. This type is indicated by setting the value * data type to string or list, and setting the token value * pointer for that type to null. */ uint32_t pool_ofs_; } strval_; /* my list value */ struct { class CTPNList *l_; /* for debugger expressions only: the pre-resolved pool address */ uint32_t pool_ofs_; } listval_; /* property ID value */ uint propval_; /* object reference value */ struct { ulong id_; enum tc_metaclass_t meta_; } objval_; /* * function pointer value - we store the underlying symbol, * since function pointers are generally not resolved until late * in the compilation */ class CTcSymFunc *funcptrval_; /* built-in function pointer value */ class CTcSymBif *bifptrval_; /* * code body pointer value - we store the underlying code body * for anonymous functions */ class CTPNCodeBody *codebodyval_; } val_; /* * Is this a compile-time constant value? A compile-time constant is a * value that has a fixed constant value as of compile time, but could * vary from one compilation to another. The defined() operator * produces this type of constant, for example. * * The main distinction between a true constant and a compile-time * constant is that true constants generate warnings when they produce * invariant code, such as when a true constant is used as the * condition of an 'if'. Compile-time constants are specifically for * producing code that's invariant once compiled, but which can vary * across compilations, allowing for more sophisticated configuration * management. For example, defined() makes it possible to produce * code that only gets compiled when a particular symbol is included in * the build, so that code in module A can refer to symbols defined in * module B when module B is included in the build, but will * automatically omit the referring code when module B is omitted. */ uint ctc_ : 1; /* * Is this a promoted value? This is set to true for promotions from * int to BigNumber that are due to overflows. This isn't set for * values explicitly entered as floating point values or that were * constant-folded from expressions containing explicit floats. */ uint promoted_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Assignment Types. */ enum tc_asitype_t { /* simple assignment: x = 1 */ TC_ASI_SIMPLE, /* add to: x += 1 */ TC_ASI_ADD, /* subtract from: x -= 1 */ TC_ASI_SUB, /* multiply by: x *= 1 */ TC_ASI_MUL, /* divide by: x /= 1 */ TC_ASI_DIV, /* modulo: x %= 1 */ TC_ASI_MOD, /* bitwise-and with: x &= 1 */ TC_ASI_BAND, /* bitwise-or with: x |= 1 */ TC_ASI_BOR, /* bitwise-xor with: x ^= 1 */ TC_ASI_BXOR, /* shift left: x <<= 1 */ TC_ASI_SHL, /* arithmetic shift right: x >>= 1 */ TC_ASI_ASHR, /* logical shift right: x >>>= 1 */ TC_ASI_LSHR, /* pre-increment */ TC_ASI_PREINC, /* pre-decrement */ TC_ASI_PREDEC, /* post-increment */ TC_ASI_POSTINC, /* post-decrement */ TC_ASI_POSTDEC, /* * Subscript assignment: []= * * This isn't actually an operator in the language, but subscripted * assignments are handled specially because of the way subscript * assignment generates a new container value. So a[b] = c is actually * treated as a = a.operator[]=(b, c). */ TC_ASI_IDX }; /* ------------------------------------------------------------------------ */ /* * Formal parameter type list. For functions with declared formal * parameter types (such as multi-methods), we use this class to keep the * list of type names in the parameter list. */ class CTcFormalTypeList { public: CTcFormalTypeList() { /* no entries in our type list yet */ head_ = tail_ = 0; /* assume this isn't a varargs list */ varargs_ = FALSE; } ~CTcFormalTypeList() { } /* create the decorated name */ void decorate_name(CTcToken *decorated_name, const CTcToken *func_base_name); /* get the first parameter in the list */ class CTcFormalTypeEle *get_first() const { return head_; } /* add a typed variable to the list */ void add_typed_param(const CTcToken *tok); /* add an untyped variable to the list */ void add_untyped_param(); /* add 'n' untyped variables to the list */ void add_untyped_params(int n) { while (n-- > 0) add_untyped_param(); } /* add a trailing ellispsis (varargs indicator) */ void add_ellipsis() { varargs_ = TRUE; } protected: /* add a new list element */ void add(class CTcFormalTypeEle *ele); /* add/tail of parameter list */ class CTcFormalTypeEle *head_, *tail_; /* is this a varargs list? */ int varargs_; }; /* formal parameter type list entry */ class CTcFormalTypeEle { public: CTcFormalTypeEle() { name_ = 0; } CTcFormalTypeEle(const char *name, size_t len); ~CTcFormalTypeEle() { } /* next element in list */ CTcFormalTypeEle *nxt_; /* type name */ char *name_; size_t name_len_; }; /* ------------------------------------------------------------------------ */ /* * Expression Operator Parsers. We construct a tree of these operator * parsers so that we can express the expression grammar in a relatively * compact and declarative notation. */ /* * basic operator parser */ class CTcPrsOp { public: /* * Parse an expression with this operator. Logs an error and * returns non-zero if the expression is not valid; on success, * returns zero. * * Fills in *val with the constant value, if any, of the expression. * If the expression does not have a constant value, *val's type * will be set to TC_CVT_UNKNOWN to indicate this. * * Returns a parse node if successful, or null if an error occurs * and the operator parser is unable to make a guess about what was * intended. */ virtual class CTcPrsNode *parse() const = 0; }; /* * generic left-associative binary operator */ class CTcPrsOpBin: public CTcPrsOp { public: CTcPrsOpBin() { /* no left or right subexpression specified */ left_ = right_ = 0; /* as-yet unknown operator token */ op_tok_ = TOKT_INVALID; } CTcPrsOpBin(tc_toktyp_t typ) { /* remember my operator token */ op_tok_ = typ; } CTcPrsOpBin(const CTcPrsOp *left, const CTcPrsOp *right, tc_toktyp_t typ) { /* remember my left and right sub-operators */ left_ = left; right_ = right; /* remember my operator token */ op_tok_ = typ; } /* parse the binary expression */ class CTcPrsNode *parse() const; /* build a new tree out of our left-hand and right-hand subtrees */ virtual class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const = 0; /* * Try evaluating a constant result. If the two values can be * combined with the operator to yield a constant value result, * create a new parse node for the constant value (or update one of * the given subnodes) and return it. If we can't provide a * constant value, return null. * * By default, we'll indicate that the expression does not have a * valid constant value. */ virtual class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const { /* indicate that we cannot synthesize a constant value */ return 0; } /* get/set my token */ tc_toktyp_t get_op_tok() const { return op_tok_; } void set_op_tok(tc_toktyp_t tok) { op_tok_ = tok; } protected: /* operator that can be parsed for my left-hand side */ const CTcPrsOp *left_; /* operator that can be parsed for my right-hand side */ const CTcPrsOp *right_; /* my operator token */ tc_toktyp_t op_tok_; }; /* * Binary Operator Group. This is a group of operators at a common * precedence level. The group has an array of binary operators that * are all at the same level of precedence; we'll evaluate the left * suboperator, then check the token in the input stream against each of * our group's operators, applying the one that matches, if one matches. */ class CTcPrsOpBinGroup: public CTcPrsOp { public: CTcPrsOpBinGroup(const CTcPrsOp *left, const CTcPrsOp *right, const class CTcPrsOpBin *const *ops) { /* remember my left and right suboperators */ left_ = left; right_ = right; /* remember the operators in my group */ ops_ = ops; } /* parse the expression */ class CTcPrsNode *parse() const; protected: /* find and apply an operator to the parsed left-hand side */ int find_and_apply_op(CTcPrsNode **lhs) const; /* my left and right suboperators */ const CTcPrsOp *left_; const CTcPrsOp *right_; /* group of binary operators at this precedence level */ const class CTcPrsOpBin *const *ops_; }; /* * Binary operator group for comparison operators. This is a similar to * other binary groups, but also includes the special "is in" and "not * in" operators. */ class CTcPrsOpBinGroupCompare: public CTcPrsOpBinGroup { public: CTcPrsOpBinGroupCompare(const class CTcPrsOp *left, const class CTcPrsOp *right, const class CTcPrsOpBin *const *ops) : CTcPrsOpBinGroup(left, right, ops) { } class CTcPrsNode *parse() const; protected: /* parse the 'in' list portion of the expression */ class CTPNArglist *parse_inlist() const; }; /* comma operator */ class CTcPrsOpComma: public CTcPrsOpBin { public: CTcPrsOpComma(const CTcPrsOp *left, const CTcPrsOp *right) : CTcPrsOpBin(left, right, TOKT_COMMA) { } /* evaluate constant result */ class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const; /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; }; /* logical OR */ class CTcPrsOpOr: public CTcPrsOpBin { public: CTcPrsOpOr(const CTcPrsOp *left, const CTcPrsOp *right) : CTcPrsOpBin(left, right, TOKT_OROR) { } /* evaluate constant result */ class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const; /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; }; /* logical AND */ class CTcPrsOpAnd: public CTcPrsOpBin { public: CTcPrsOpAnd(const CTcPrsOp *left, const CTcPrsOp *right) : CTcPrsOpBin(left, right, TOKT_ANDAND) { } /* evaluate constant result */ class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const; /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; }; /* general magnitude comparison operators */ class CTcPrsOpRel: public CTcPrsOpBin { public: CTcPrsOpRel(tc_toktyp_t typ) : CTcPrsOpBin(typ) { } /* evaluate constant result */ class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* * Get the result true/false value, given the result of the * comparison. For example, if this is a greater-than operator, * this should return TRUE if comp > 0, FALSE otherwise. */ virtual int get_bool_val(int comparison_value) const = 0; }; /* comparison - greater than */ class CTcPrsOpGt: public CTcPrsOpRel { public: CTcPrsOpGt() : CTcPrsOpRel(TOKT_GT) { } /* get the boolean value for a comparison sense */ int get_bool_val(int comp) const { return comp > 0; } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; }; /* comparison - greater than or equal to */ class CTcPrsOpGe: public CTcPrsOpRel { public: CTcPrsOpGe() : CTcPrsOpRel(TOKT_GE) { } /* get the boolean value for a comparison sense */ int get_bool_val(int comp) const { return comp >= 0; } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; }; /* comparison - less than */ class CTcPrsOpLt: public CTcPrsOpRel { public: CTcPrsOpLt() : CTcPrsOpRel(TOKT_LT) { } /* get the boolean value for a comparison sense */ int get_bool_val(int comp) const { return comp < 0; } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; }; /* comparison - less than or equal to */ class CTcPrsOpLe: public CTcPrsOpRel { public: CTcPrsOpLe() : CTcPrsOpRel(TOKT_LE) { } /* get the boolean value for a comparison sense */ int get_bool_val(int comp) const { return comp <= 0; } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; }; /* * Equality/inequality comparison */ class CTcPrsOpEqComp: public CTcPrsOpBin { public: CTcPrsOpEqComp(tc_toktyp_t typ) : CTcPrsOpBin(typ) { } /* evaluate constant result */ class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* get the boolean value to use if the operands are equal */ virtual int get_bool_val(int ops_equal) const = 0; }; /* * Equality comparison */ class CTcPrsOpEq: public CTcPrsOpEqComp { public: /* start out in C mode - use '==' operator by default */ CTcPrsOpEq() : CTcPrsOpEqComp(TOKT_EQEQ) { } /* set the current equality operator */ void set_eq_op(tc_toktyp_t op) { op_tok_ = op; } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; /* get the boolean value to use if the operands are equal */ virtual int get_bool_val(int ops_equal) const { return ops_equal; } }; /* * Inequality comparison */ class CTcPrsOpNe: public CTcPrsOpEqComp { public: CTcPrsOpNe() : CTcPrsOpEqComp(TOKT_NE) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; /* get the boolean value to use if the operands are equal */ virtual int get_bool_val(int ops_equal) const { return !ops_equal; } }; /* * binary arithmetic operators */ class CTcPrsOpArith: public CTcPrsOpBin { public: CTcPrsOpArith(tc_toktyp_t typ) : CTcPrsOpBin(typ) { } CTcPrsOpArith(const CTcPrsOp *left, const CTcPrsOp *right, tc_toktyp_t typ) : CTcPrsOpBin(left, right, typ) { } /* evaluate constant result */ class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate the result of the operand applied to constant int values */ virtual long calc_result(long val1, long val2, int &ov) const = 0; /* calculate the result for constant float values */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const = 0; }; /* bitwise OR */ class CTcPrsOpBOr: public CTcPrsOpArith { public: CTcPrsOpBOr(const CTcPrsOp *left, const CTcPrsOp *right) : CTcPrsOpArith(left, right, TOKT_OR) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate a constant integer result */ virtual long calc_result(long val1, long val2, int &ov) const { ov = FALSE; return val1 | val2; } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const { G_tok->log_error(TCERR_BAD_OP_FOR_FLOAT); return 0; } }; /* bitwise XOR */ class CTcPrsOpBXor: public CTcPrsOpArith { public: CTcPrsOpBXor(const CTcPrsOp *left, const CTcPrsOp *right) : CTcPrsOpArith(left, right, TOKT_XOR) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate the result */ virtual long calc_result(long val1, long val2, int &ov) const { ov = FALSE; return val1 ^ val2; } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const { G_tok->log_error(TCERR_BAD_OP_FOR_FLOAT); return 0; } }; /* bitwise AND */ class CTcPrsOpBAnd: public CTcPrsOpArith { public: CTcPrsOpBAnd(const CTcPrsOp *left, const CTcPrsOp *right) : CTcPrsOpArith(left, right, TOKT_AND) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate the result */ virtual long calc_result(long val1, long val2, int &ov) const { ov = FALSE; return val1 & val2; } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const { G_tok->log_error(TCERR_BAD_OP_FOR_FLOAT); return 0; } }; /* * shift left */ class CTcPrsOpShl: public CTcPrsOpArith { public: CTcPrsOpShl() : CTcPrsOpArith(TOKT_SHL) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: long calc_result(long a, long b, int &ov) const { ov = FALSE; return a << b; } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const { G_tok->log_error(TCERR_BAD_OP_FOR_FLOAT); return 0; } }; /* * arithmetic shift right */ class CTcPrsOpAShr: public CTcPrsOpArith { public: CTcPrsOpAShr() : CTcPrsOpArith(TOKT_ASHR) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: long calc_result(long a, long b, int &ov) const { ov = FALSE; return t3_ashr(a, b); } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const { G_tok->log_error(TCERR_BAD_OP_FOR_FLOAT); return 0; } }; /* * logical shift right */ class CTcPrsOpLShr: public CTcPrsOpArith { public: CTcPrsOpLShr() : CTcPrsOpArith(TOKT_LSHR) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate a constant integer result */ long calc_result(long a, long b, int &ov) const { ov = FALSE; return t3_lshr(a, b); } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const { G_tok->log_error(TCERR_BAD_OP_FOR_FLOAT); return 0; } }; /* * multiply */ class CTcPrsOpMul: public CTcPrsOpArith { public: CTcPrsOpMul() : CTcPrsOpArith(TOKT_TIMES) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate a constant integer result */ long calc_result(long a, long b, int &ov) const { int64_t prod = a * b; ov = (prod > (int64_t)INT32MAXVAL || prod < (int64_t)INT32MINVAL); return (long)prod; } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const; }; /* * divide */ class CTcPrsOpDiv: public CTcPrsOpArith { public: CTcPrsOpDiv() : CTcPrsOpArith(TOKT_DIV) { } CTcPrsOpDiv(tc_toktyp_t tok) : CTcPrsOpArith(tok) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate a constant integer result */ long calc_result(long a, long b, int &ov) const; /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const; }; /* * mod - inherit from divide operator to pick up divide-by-zero checking */ class CTcPrsOpMod: public CTcPrsOpDiv { public: CTcPrsOpMod() : CTcPrsOpDiv(TOKT_MOD) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate a constant integer result */ long calc_result(long a, long b, int &ov) const; /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const; }; /* * add */ class CTcPrsOpAdd: public CTcPrsOpArith { public: CTcPrsOpAdd() : CTcPrsOpArith(TOKT_PLUS) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; /* evaluate constant result */ class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate a constant integer result */ long calc_result(long a, long b, int &ov) const { int32_t sum = (int32_t)(a + b); ov = (a >= 0 ? b > 0 && sum < a : b < 0 && sum > a); return sum; } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const; }; /* * subtract */ class CTcPrsOpSub: public CTcPrsOpArith { public: CTcPrsOpSub() : CTcPrsOpArith(TOKT_MINUS) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; /* evaluate constant result */ class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate a constant integer result */ long calc_result(long a, long b, int &ov) const { int32_t diff = (int32_t)(a - b); ov = (a >= 0 ? b < 0 && diff < a : b > 0 && diff > a); return diff; } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const; }; /* * Unary Operators */ class CTcPrsOpUnary: public CTcPrsOp { public: class CTcPrsNode *parse() const; /* * evaluate a constant subscript expression; returns a constant * parse node expression if the subscript can be evaluated to a * compile-time constant, or null if not */ static class CTcPrsNode *eval_const_subscript(class CTcPrsNode *lhs, class CTcPrsNode *subscript); /* * evaluate a constant NOT expression; returns a constant parse node * expression if the logical negation can be evaluated to a * compile-time constant, or null if not */ static class CTcPrsNode *eval_const_not(class CTcPrsNode *lhs); /* parse a string with embedded expressions */ static class CTcPrsNode *parse_embedding(struct CTcEmbedBuilder *builder); /* parse a list */ static class CTcPrsNode *parse_list(); /* parse a primary expression */ static class CTcPrsNode *parse_primary(); /* parse an anonymous function */ static class CTPNAnonFunc *parse_anon_func(int short_form, int is_method); /* parse an in-line object definition */ static class CTPNInlineObject *parse_inline_object(int has_colon); protected: /* embedded expression: parse an expression list */ static CTcPrsNode *parse_embedding_list( struct CTcEmbedBuilder *b, int &eos, struct CTcEmbedLevel *parent); /* embedded expression: parse an <> or <> embedding */ static CTcPrsNode *parse_embedded_if( struct CTcEmbedBuilder *b, int unless, int &eos, struct CTcEmbedLevel *parent); /* parse a single embedded expression */ static CTcPrsNode *parse_embedded_expr( struct CTcEmbedBuilder *b, class CTcEmbedTokenList *tl); /* embedded expression: parse an <> embedding */ static CTcPrsNode *parse_embedded_oneof( struct CTcEmbedBuilder *b, int &eos, struct CTcEmbedLevel *parent); /* embedded expression: parse an <>...<> embedding */ static CTcPrsNode *parse_embedded_firsttime( struct CTcEmbedBuilder *b, int &eos, struct CTcEmbedLevel *parent); /* create the parse node for a <> structure */ static CTcPrsNode *create_oneof_node( struct CTcEmbedBuilder *b, class CTPNList *lst, const char *attrs); /* capture an embedded expression to a saved token list */ static void capture_embedded(struct CTcEmbedBuilder *b, class CTcEmbedTokenList *tl); /* * Match an end token for an embedded expression construct - <>, * <>, etc. This version parses captured tokens. */ static int parse_embedded_end_tok(class CTcEmbedTokenList *tl, struct CTcEmbedLevel *parent, const char **open_kw); /* parse an end token directly from the token stream */ static int parse_embedded_end_tok(struct CTcEmbedBuilder *b, struct CTcEmbedLevel *parent, const char **open_kw); /* parse a logical NOT operator */ static class CTcPrsNode *parse_not(CTcPrsNode *sub); /* parse a bitwise NOT operator */ static class CTcPrsNode *parse_bnot(CTcPrsNode *sub); /* parse an address-of operator */ class CTcPrsNode *parse_addr() const; /* parse an arithmetic positive operator */ static class CTcPrsNode *parse_pos(CTcPrsNode *sub); /* parse an arithmetic negative operator */ static class CTcPrsNode *parse_neg(CTcPrsNode *sub); /* parse a pre- or post-increment operator */ static class CTcPrsNode *parse_inc(int pre, CTcPrsNode *sub); /* parse a pre- or post-decrement operator */ static class CTcPrsNode *parse_dec(int pre, CTcPrsNode *sub); /* parse a 'new' operator */ static class CTcPrsNode *parse_new(CTcPrsNode *sub, int is_transient); /* parse a 'delete' operator */ static class CTcPrsNode *parse_delete(CTcPrsNode *sub); /* parse a postfix expression */ static class CTcPrsNode *parse_postfix(int allow_member_expr, int allow_call_expr); /* parse a function or method call */ static class CTcPrsNode *parse_call(CTcPrsNode *lhs); /* parse an argument list */ static class CTPNArglist *parse_arg_list(); /* parse a subscript */ static class CTcPrsNode *parse_subscript(CTcPrsNode *lhs); /* parse a member selection ('.' operator) */ static class CTcPrsNode *parse_member(CTcPrsNode *lhs); /* parse an "inherited" expression */ static class CTcPrsNode *parse_inherited(); /* parse a "delegated" expression */ static class CTcPrsNode *parse_delegated(); }; /* * if-nil operator ?? */ class CTcPrsOpIfnil: public CTcPrsOp { public: CTcPrsOpIfnil() { } class CTcPrsNode *parse() const; }; /* * tertiary conditional operator */ class CTcPrsOpIf: public CTcPrsOp { public: CTcPrsOpIf() { } class CTcPrsNode *parse() const; }; /* * Assignment operators (including the regular assignment, "="/":=", * plus all calculate-and-assign operators: "+=", "-=", etc) */ class CTcPrsOpAsi: public CTcPrsOp { public: CTcPrsOpAsi() { /* start out with the C-mode simple assignment operator */ asi_op_ = TOKT_EQ; } /* parse an assignment */ class CTcPrsNode *parse() const; /* set the current simple assignment operator */ void set_asi_op(tc_toktyp_t tok) { asi_op_ = tok; } private: /* current simple assignment operator */ tc_toktyp_t asi_op_; }; #endif /* TCPRS_H */ qtads-2.1.7/tads3/tcprs_rt.cpp000066400000000000000000000045641265017072300162330ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcprs_rt.cpp - stubs for parser functions not needed in run-time Function Interpreters that include "eval()" functionality require a portion of the compiler, including code body and expression compilation. This module provides stubs for functions that aren't needed for interpreter configurations, but which are referenced from the parts of the compiler we do need, to resolve symbols at link time. Many of these stubs are simply never called in interpreter builds, because the conditions that would trigger their invocation can't occur; others just need to provide defaults or other simplified functionality when called; others generate errors, because the functionality they provide is specifically not supported in interpreter builds. Notes Modified 02/02/00 MJRoberts - Creation */ #include "tctarg.h" #include "tcprs.h" #include "vmerr.h" #include "vmerrnum.h" #include "tctok.h" /* ------------------------------------------------------------------------ */ /* * Simple stubs for functions not used in the dynamic compiler */ /* * Debug records aren't needed when we're compiling code in the debugger * itself - debugger-generated code can't itself be debugged */ void CTPNStmBase::add_debug_line_rec() { } void CTPNStmBase::add_debug_line_rec(CTcTokFileDesc *, long) { } /* * Add an entry to the global symbol table. We can't define new global * symbols in an interactive debug session, so this does nothing. */ void CTcPrsSymtab::add_to_global_symtab(CTcPrsSymtab *, CTcSymbol *) { } int CTPNStmObjectBase::parse_nested_obj_prop( CTPNObjProp* &, int *, tcprs_term_info *, const CTcToken *, int) { return FALSE; } /* * Set the sourceTextGroup mode. This doesn't apply in the debugger, since * #pragma isn't allowed. */ void CTcParser::set_source_text_group_mode(int) { } /* * we don't need static object generation, since we use G_vmifc interface * to generate live objects in the VM */ CTcSymObj *CTcParser::add_gen_obj_stat(CTcSymObj *) { return 0; } void CTcParser::add_gen_obj_prop_stat( CTcSymObj *, CTcSymProp *, const CTcConstVal *) { } qtads-2.1.7/tads3/tcprsnf.cpp000066400000000000000000000056451265017072300160530ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcprsnf.cpp - TADS 3 Compiler Parser - "no files" module Function This module can be linked with programs that don't require any symbol or object file read/write functionality. This module contains dummy entrypoints for functions and methods that would normally be linked from tcprsfil.cpp, which contains the actual implementations of these methods. This is needed for interpreter builds that link in the compiler for "eval()" functionality, which doesn't require any intermediate file creation or loading. Notes Modified 04/30/99 MJRoberts - Creation */ #include #include #include #include #include "os.h" #include "t3std.h" #include "tcprs.h" #include "tctarg.h" #include "tcgen.h" #include "vmhash.h" #include "tcmain.h" #include "vmfile.h" #include "tcmake.h" /* ------------------------------------------------------------------------ */ /* * Object file readers */ void CTcSymObjBase::load_refs_from_obj_file( CVmFile *, const char *, tctarg_obj_id_t *, tctarg_prop_id_t *) { assert(FALSE); } /* ------------------------------------------------------------------------ */ /* * Object file writers */ int CTcSymbolBase::write_to_obj_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymObjBase::write_to_obj_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymObjBase::write_refs_to_obj_file(class CVmFile *) { assert(FALSE); return FALSE; } int CTcSymPropBase::write_to_obj_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymFuncBase::write_to_obj_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymEnumBase::write_to_obj_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymMetaclassBase::write_to_obj_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymBifBase::write_to_obj_file(CVmFile *) { assert(FALSE); return 0; } /* ------------------------------------------------------------------------ */ /* * Symbol file writers */ int CTcSymbolBase::write_to_sym_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymObjBase::write_to_sym_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymFuncBase::write_to_sym_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymEnumBase::write_to_sym_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymPropBase::write_to_sym_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymMetaclassBase::write_to_sym_file(CVmFile *) { assert(FALSE); return 0; } /* ------------------------------------------------------------------------ */ /* * build the dictionary */ void CTcSymObjBase::build_dictionary() { assert(FALSE); } qtads-2.1.7/tads3/tcprsnl.cpp000066400000000000000000000031051265017072300160460ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcprsnl.cpp - TADS 3 Compiler Parser - "no linker" module Function This module can be linked with programs that don't require any linker functionality, such as for debugger or interpreter-with-eval builds. The module provides dummy entrypoints for functions and methods that would normally be linked from tcprsimg.cpp, so that we don't have to drag in that whole file (and its dependencies) for configurations that don't need its functionality. Notes Modified 04/30/99 MJRoberts - Creation */ #include #include #include #include #include "os.h" #include "t3std.h" #include "tcprs.h" #include "tctarg.h" #include "tcgen.h" #include "vmhash.h" #include "tcmain.h" #include "vmfile.h" #include "tcmake.h" void CTcSymObjBase::apply_internal_fixups() { assert(FALSE); } void CTcSymObj::delete_prop_from_mod_base(tctarg_prop_id_t) { assert(FALSE); } void CTcSymObj::mark_compiled_as_class() { assert(FALSE); } int CTcSymbolBase::load_from_obj_file(CVmFile *, const textchar_t *, tctarg_obj_id_t *, tctarg_prop_id_t *, ulong *) { assert(FALSE); return 0; } void CTcMake::write_build_config_to_sym_file(CVmFile *fp) { assert(FALSE); } qtads-2.1.7/tads3/tcprsstm.cpp000066400000000000000000011246701265017072300162540ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/TCPRSSTM.CPP,v 1.1 1999/07/11 00:46:54 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2010 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcprs.cpp - TADS 3 Compiler Parser - statement node classes Function This parser module includes statement node classes that are needed for parsing executable code bodies - the insides of functions and methods. This doesn't include the outer program-level structures, such as object definitions, or the inner expression structures. This code is needed for interpreters that include "eval()" functionality, for dynamic construction of new executable code. Notes Modified 04/30/99 MJRoberts - Creation */ #include #include #include #include "os.h" #include "t3std.h" #include "utf8.h" #include "tcprs.h" #include "tctarg.h" #include "tcgen.h" #include "vmhash.h" #include "tcmain.h" #include "vmfile.h" #include "tcmake.h" /* ------------------------------------------------------------------------ */ /* * Clear the local anonymous function shared context information */ void CTcParser::clear_local_ctx() { /* we don't have a local context */ has_local_ctx_ = FALSE; /* there is no local context local yet */ local_ctx_var_num_ = 0; /* no variable properties are used */ ctx_var_props_used_ = 0; /* * start the context array index at the next entry after the slot we * always use to store the method context of the enclosing lexical * scope */ next_ctx_arr_idx_ = TCPRS_LOCAL_CTX_METHODCTX + 1; /* we haven't yet referenced 'self' or any other method context yet */ self_referenced_ = FALSE; full_method_ctx_referenced_ = FALSE; /* we don't yet need the method context in the local context */ local_ctx_needs_self_ = FALSE; local_ctx_needs_full_method_ctx_ = FALSE; } /* * Set up the current code body with a local variable context */ void CTcParser::init_local_ctx() { /* if I already have a local variable context, there's nothing to do */ if (has_local_ctx_) return; /* note that we now require a local variable context object */ has_local_ctx_ = TRUE; /* create the local context variable */ local_ctx_var_num_ = alloc_local(); } /* * Allocate a context variable index */ int CTcParser::alloc_ctx_arr_idx() { return next_ctx_arr_idx_++; } /* * enumeration callback context */ struct enum_locals_ctx { /* symbol table containing context locals */ CTcPrsSymtab *symtab; /* code body */ CTPNCodeBody *code_body; }; /* * Enumeration callback - find local variables inherited from enclosing * scopes (for anonymous functions) */ void CTcParser::enum_for_ctx_locals(void *ctx0, CTcSymbol *sym) { /* cast the context */ enum_locals_ctx *ctx = (enum_locals_ctx *)ctx0; /* tell this symbol to apply its local variable conversion */ sym->apply_ctx_var_conv(ctx->symtab, ctx->code_body); } /* * Set up the local context for access to enclosing scope locals. Call * this after parsing a code body to set up the hooks between the new code * body's proxy symbol table for enclosing scopes and the actual enclosing * local scopes. */ void CTcParser::finish_local_ctx(CTPNCodeBody *cb, CTcPrsSymtab *local_symtab) { /* if we have a local context, mark the code body accordingly */ if (has_local_ctx_) cb->set_local_ctx(local_ctx_var_num_, next_ctx_arr_idx_ - 1); /* * If the caller passed in a local symbol table, check the table for * context variables from enclosing scopes, and assign the local holder * for each such variable. */ if (local_symtab != 0) { /* * consider only the outermost local table, since that's where the * shared locals reside */ CTcPrsSymtab *tab, *par; for (tab = local_symtab_, par = tab->get_parent() ; par != 0 && par != global_symtab_ ; tab = par, par = par->get_parent()) ; /* enumerate the variables */ enum_locals_ctx ctx; ctx.symtab = tab; ctx.code_body = cb; tab->enum_entries(&enum_for_ctx_locals, &ctx); } } /* ------------------------------------------------------------------------ */ /* * Parse a formal parameter list. If 'count_only' is true, we're only * interested in counting the formals, and we don't bother adding them * to any symbol table. If 'opt_allowed' is true, a parameter name can * be followed by a question mark token to mark the parameter as * optional. * * '*argc' returns with the number of parameters. 'opt_argc' can be * null; if it's not null, '*opt_argc' returns with the number of * parameters marked as optional. * * 'base_formal_num' is the starting formal parameter number to use in * creating symbol table entries. This is meaningful only if * 'count_only' is false. * * The caller must already have checked for an open paren and skipped * it. */ void CTcParser::parse_formal_list( int count_only, int opt_allowed, int *argc, int *opt_argc, int *varargs, int *varargs_list, CTcSymLocal **varargs_list_local, int *err, int base_formal_num, int for_short_anon_func, CTcFormalTypeList **type_list) { CTcSymLocal *lcl = 0; CTcToken varname; int named_param = FALSE, opt_param = FALSE; int is_typed = FALSE; int defval_cnt = 0; /* * choose the end token - if this is a normal argument list, the * ending token is a right parenthesis; for a short-form anonymous * function, it's a colon */ tc_toktyp_t end_tok = (for_short_anon_func ? TOKT_COLON : TOKT_RPAR); int missing_end_tok_err = (for_short_anon_func ? TCERR_MISSING_COLON_FORMAL : TCERR_MISSING_RPAR_FORMAL); /* no arguments yet */ *argc = 0; if (opt_argc != 0) *opt_argc = 0; *varargs = FALSE; *varargs_list = FALSE; /* no error yet */ *err = FALSE; /* we've only just begun */ int done = FALSE; /* check for an empty list */ if (G_tok->cur() == end_tok) { /* the list is empty - we're already done */ done = TRUE; /* skip the closing token */ G_tok->next(); } /* keep going until done */ while (!done) { tc_toktyp_t suffix_tok = TOKT_INVALID; CTcPrsNode *defval_expr = 0; /* see what comes next */ switch(G_tok->cur()) { case TOKT_ELLIPSIS: /* it's an ellipsis - note that we have varargs */ *varargs = TRUE; parse_ellipsis: /* add the varargs indicator to the formal type list */ if (type_list != 0 && *type_list != 0) (*type_list)->add_ellipsis(); /* the next token must be the close paren */ if (G_tok->next() == end_tok) { /* we've reached the end of the list */ goto handle_end_tok; } else { /* this is an error - guess about the problem */ switch(G_tok->cur()) { case TOKT_COMMA: /* * we seem to have more in the list - log an error, * but continue parsing the list */ G_tok->next(); G_tok->log_error(TCERR_ELLIPSIS_NOT_LAST); break; default: /* * anything else is probably a missing right paren - * provide it and exit the formal list */ done = TRUE; G_tok->log_error_curtok(missing_end_tok_err); break; } } break; case TOKT_LBRACK: /* * varargs with named list variable for last parameter - * this generates setup code that stores the arguments from * this one forward in a list to be stored in this variable */ /* note that we have varargs */ *varargs = TRUE; /* note it in the type list as well */ if (type_list != 0 && *type_list != 0) (*type_list)->add_ellipsis(); /* skip the bracket and check that a symbol follows */ switch(G_tok->next()) { case TOKT_RBRACK: /* * empty brackets - treat this as identical to an * ellipsis; since they didn't name the varargs list * parameter, it's just a varargs indication */ goto parse_ellipsis; case TOKT_SYM: /* if we're creating a symbol table, add the symbol */ if (!count_only) { int local_id; /* create a local scope if we haven't already */ create_scope_local_symtab(); /* note that we have a varargs list parameter */ *varargs_list = TRUE; /* * insert the new variable as a local - it's not a * formal, since we're going to take the actuals, * make a list, and store them in this local; the * formal in this position will not be named, since * this is a varargs function */ local_id = alloc_local(); *varargs_list_local = local_symtab_->add_local( G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), local_id, FALSE, TRUE, FALSE); /* mark it as a list parameter */ (*varargs_list_local)->set_list_param(TRUE); } /* a close bracket must follow */ switch (G_tok->next()) { case TOKT_RBRACK: /* skip the close bracket, and check for the end token */ if (G_tok->next() == end_tok) { /* that's it - the list is done */ goto handle_end_tok; } /* we didn't find the end token - guess about the error */ switch (G_tok->cur()) { case TOKT_COMMA: /* * they seem to want another parameter - this is * an error, but keep parsing anyway */ G_tok->log_error(TCERR_LISTPAR_NOT_LAST); break; case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_LPAR: case TOKT_EOF: /* the ending token was probably just forgotten */ G_tok->log_error_curtok(missing_end_tok_err); /* presume that the argument list is done */ done = TRUE; break; default: /* end token is missing - log an error */ G_tok->log_error_curtok(missing_end_tok_err); /* * skip the errant token; if the ending token * follows this one, skip it as well, since * there's probably just a stray extra token * before the ending token */ if (G_tok->next() == end_tok) G_tok->next(); /* presume they simply left out the paren */ done = TRUE; break; } break; case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_EOF: /* they must have left out the bracket and paren */ G_tok->log_error_curtok(TCERR_LISTPAR_REQ_RBRACK); done = TRUE; break; default: /* note the missing bracket */ G_tok->log_error_curtok(TCERR_LISTPAR_REQ_RBRACK); /* skip the errant token and assume we're done */ G_tok->next(); done = TRUE; break; } break; default: /* log an error */ G_tok->log_error_curtok(TCERR_LISTPAR_REQ_SYM); /* if this is the ending token, we're done */ if (G_tok->cur() == end_tok) goto handle_end_tok; /* * if we're not on something that looks like the end of * the parameter list, skip the errant token */ switch(G_tok->cur()) { case TOKT_RBRACE: case TOKT_LBRACE: case TOKT_EOF: /* looks like they left out the closing markers */ done = TRUE; break; default: /* * skip the errant token and continue - they * probably have more argument list following */ G_tok->next(); break; } break; } break; case TOKT_SYM: /* * If a formal type list is allowed, check to see if the next * token is a symbol - if it is, the current token is the type, * and the next token is the variable name. */ is_typed = FALSE; if (type_list != 0) { /* save the initial token */ CTcToken init_tok = *G_tok->copycur(); /* check the next one */ if (G_tok->next() == TOKT_SYM) { /* * We have another symbol, so the first token is the * type name, and the current token is the param name. * Add the type to the formal type list. */ /* first, make sure we've created the type list */ if (*type_list == 0) { /* * We haven't created one yet - create it now. * Since we're just getting around to creating the * list, add untyped elements for the parameters * we've seen so far - they all must have been * untyped, since we create the list on * encountering the first typed parameter. */ *type_list = new (G_prsmem) CTcFormalTypeList(); (*type_list)->add_untyped_params(*argc); } /* add this typed parameter */ (*type_list)->add_typed_param(&init_tok); /* flag it as typed */ is_typed = TRUE; /* * make sure the type symbol is defined as a class or * object */ CTcSymbol *clsym = global_symtab_->find( init_tok.get_text(), init_tok.get_text_len()); if (clsym == 0) { /* it's not defined - add it as an external object */ clsym = new CTcSymObj( init_tok.get_text(), init_tok.get_text_len(), FALSE, G_cg->new_obj_id(), TRUE, TC_META_TADSOBJ, 0); /* mark it as referenced */ clsym->mark_referenced(); /* add it to the table */ global_symtab_->add_entry(clsym); } else if (clsym->get_type() != TC_SYM_OBJ && clsym->get_type() != TC_SYM_METACLASS) { /* it's something other than an object */ G_tok->log_error(TCERR_MMPARAM_NOT_OBJECT, (int)init_tok.get_text_len(), init_tok.get_text()); } } else { /* there's no type name - back up to the param name */ G_tok->unget(); /* * We need to add an untyped element to the type list, * but don't do so quite yet - we need to make sure * that this isn't a named argument parameter. Only * positional elements go in the type list. For now, * simply flag it as non-typed. */ is_typed = FALSE; } } /* remember and skip the symbol name */ varname = *G_tok->getcur(); G_tok->next(); /* check for a colon, which might indicate a named parameter */ named_param = FALSE; if (G_tok->cur() == TOKT_COLON) { /* it looks like a named param so far */ named_param = TRUE; /* * There's one case where a colon *doesn't* indicate a * named parameter, which is when it's the closing colon of * a short-form anonymous function parameter list. For * those, the colon is a name marker only if it's followed * by a continuation of the formal list: specifically, * another parameter (so, a comma), or ':' to indicate the * actual end of the list. If anything else follows, the * colon is the terminator rather than part of the formal * list. */ if (for_short_anon_func) { /* check the next token */ switch (G_tok->next()) { case TOKT_COMMA: case TOKT_COLON: /* * More list follows, so this is indeed a named * parameter marker. */ break; default: /* * We don't have more list, so this colon is the * end marker for the formal list. Put it back and * continue. */ named_param = FALSE; G_tok->unget(); break; } } else { /* * for regular argument lists, this can only be a named * parameter indicator; skip it */ G_tok->next(); } /* named parameters can't be typed */ if (named_param && is_typed) { G_tok->log_error(TCERR_NAMED_ARG_NO_TYPE, (int)varname.get_text_len(), varname.get_text()); } } /* * check for an optionality flag (a '?' suffix) or a default * value ('= ') */ opt_param = FALSE; suffix_tok = G_tok->cur(); defval_expr = 0; if (opt_allowed && (suffix_tok == TOKT_QUESTION || suffix_tok == TOKT_EQ)) { /* it's optional */ opt_param = TRUE; /* count the optional argument */ if (opt_argc != 0 && !named_param) ++(*opt_argc); /* skip the suffix token */ G_tok->next(); /* if it was an '=', parse the default value expression */ if (suffix_tok == TOKT_EQ) { /* * parse the expression - comma has precedence in this * context as an argument separator, so we need to * parse this as an assignment expression (this is for * precedence reasons, NOT because it's syntactically * like an assignment - the '=' postfix to the argument * name isn't part of the expression we're parsing) */ defval_expr = parse_asi_expr(); } } /* * if any previous arguments are optional, all subsequent * arguments must be optional as well */ if (opt_allowed && opt_argc != 0 && *opt_argc != 0 && !opt_param && !named_param) { /* flag it as an error */ G_tok->log_error(TCERR_ARG_MUST_BE_OPT, (int)varname.get_text_len(), varname.get_text()); } /* if we're creating symbol table entries, add this symbol */ lcl = 0; if (!count_only) { /* * create a new local symbol table if we don't already have * one */ create_scope_local_symtab(); /* * Insert the new variable for the formal. If it's a named * parameter or an optional parameter, it's not a real * formal, since it doesn't point directly to a parameter * slot in the stack frame - it points instead to a regular * local variable slot that we have to set up with * generated code in the function prolog. */ if (named_param || opt_param) { /* it's a pseudo-formal - create a local for it */ lcl = local_symtab_->add_local( varname.get_text(), varname.get_text_len(), alloc_local(), FALSE, FALSE, FALSE); /* set the special flags */ lcl->set_named_param(named_param); lcl->set_opt_param(opt_param); /* remember the default value expression, if any */ if (defval_expr != 0) lcl->set_defval_expr(defval_expr, defval_cnt++); } else { /* insert the new local variable for the formal */ lcl = local_symtab_->add_formal( varname.get_text(), varname.get_text_len(), base_formal_num + *argc, FALSE); } /* * If it's a position parameter, set its parameter index. * Since optional parameters are actually stored as local * variables, we need to separately track the parameter * list index so that we can load the value into the local * on function entry. */ if (!named_param && lcl != 0) lcl->set_param_index(*argc); } /* * If it's not a named parameter, count it. Named parameters * don't count as formals since they're not part of the * positional list and aren't in the parameter area of the * stack frame. */ if (!named_param) { /* count it in the positional list */ ++(*argc); /* if it was untyped, add it to the type list */ if (type_list != 0 && *type_list != 0 && !is_typed) (*type_list)->add_untyped_param(); } /* check for the closing token */ if (G_tok->cur() == end_tok) { /* it's the closing token - skip it and stop scanning */ goto handle_end_tok; } /* check what follows */ switch (G_tok->cur()) { case TOKT_COMMA: /* skip the comma and continue */ G_tok->next(); break; case TOKT_SEM: case TOKT_RBRACE: case TOKT_LBRACE: case TOKT_EQ: case TOKT_EOF: /* * We've obviously left the list - the problem is * probably that we're missing the right paren. Catch * it in the main loop. */ break; case TOKT_SYM: /* * they seem to have left out a comma - keep parsing * from the symbol token */ G_tok->log_error_curtok(TCERR_REQ_COMMA_FORMAL); break; default: /* anything else is an error */ G_tok->log_error_curtok(TCERR_REQ_COMMA_FORMAL); /* skip the errant token and continue */ G_tok->next(); break; } /* done with the formal */ break; case TOKT_SEM: case TOKT_RBRACE: case TOKT_LBRACE: case TOKT_EOF: /* * We've obviously left the list - the problem is probably * that we're missing the right paren. Log an error and * stop scanning. */ G_tok->log_error_curtok(missing_end_tok_err); done = TRUE; break; default: /* check to see if it's the ending token */ if (G_tok->cur() == end_tok) { /* * they seem to have put in a comma followed by the * ending token - it's probably just a stray extra comma */ G_tok->log_error(TCERR_MISSING_LAST_FORMAL); /* skip the paren and stop scanning */ G_tok->next(); done = TRUE; break; } /* * If this is a short-form anonymous function's parameter * list, they probably forgot the colon - generate a more * specific error for this case, and assume the list ends * here. */ if (for_short_anon_func) { /* tell them they left out the colon */ G_tok->log_error_curtok(TCERR_MISSING_COLON_FORMAL); /* presume the argument list was meant to end here */ done = TRUE; break; } /* * anything else is probably just an extraneous token; skip * it and go on */ G_tok->log_error_curtok(TCERR_REQ_SYM_FORMAL); G_tok->next(); break; handle_end_tok: /* we've reached the end token - skip it, and we're done */ G_tok->next(); done = TRUE; break; } } /* * Check for the "multimethod" modifier, if allowed. It's allowed if a * formal type list is allowed and this is a normal full parameter list * (i.e., specified in parentheses rather than as an anonymous function * formal list). */ if (G_tok->cur() == TOKT_SYM && G_tok->cur_tok_matches("multimethod", 11)) { /* make sure it's allowed */ if (type_list != 0 && end_tok == TOKT_RPAR) { /* it's allowed - create the type list if we haven't already */ if (*type_list == 0) { /* create the type list */ *type_list = new (G_prsmem) CTcFormalTypeList(); /* add untyped parameters for the ones we've defined */ (*type_list)->add_untyped_params(*argc); /* mark it as varargs if applicable */ if (*varargs) (*type_list)->add_ellipsis(); } } else { /* it's not allowed - flag it as an error */ G_tok->log_error(TCERR_MULTIMETHOD_NOT_ALLOWED); } /* skip the token */ G_tok->next(); } /* deduct the optional arguments from the fixed arguments */ if (opt_argc != 0) *argc -= *opt_argc; } /* ------------------------------------------------------------------------ */ /* * Parse a nested code body (such as an anonymous function code body) */ CTPNCodeBody *CTcParser::parse_nested_code_body( int eq_before_brace, int self_valid, int *p_argc, int *p_opt_argc, int *p_varargs, int *p_varargs_list, CTcSymLocal **p_varargs_list_local, int *p_has_retval, int *err, CTcPrsSymtab *local_symtab, tcprs_codebodytype cb_type) { /* remember the original parser state */ CTcPrsSymtab *old_local_symtab = local_symtab_; CTcPrsSymtab *old_enclosing_local_symtab = enclosing_local_symtab_; CTPNStmEnclosing *old_enclosing_stm = enclosing_stm_; CTcPrsSymtab *old_goto_symtab = goto_symtab_; int old_local_cnt = local_cnt_; int old_max_local_cnt = max_local_cnt_; int old_has_local_ctx = has_local_ctx_; int old_local_ctx_var_num = local_ctx_var_num_; int old_ctx_var_props_used = ctx_var_props_used_; int old_next_ctx_arr_idx = next_ctx_arr_idx_; int old_self_valid = self_valid_; int old_self_referenced = self_referenced_; int old_full_method_ctx_referenced = full_method_ctx_referenced_; int old_local_ctx_needs_self = local_ctx_needs_self_; int old_local_ctx_needs_full_method_ctx = local_ctx_needs_full_method_ctx_; CTcCodeBodyRef *old_cur_code_body = cur_code_body_; /* parse the code body */ CTPNCodeBody *code_body = parse_code_body( eq_before_brace, FALSE, self_valid, p_argc, p_opt_argc, p_varargs, p_varargs_list, p_varargs_list_local, p_has_retval, err, local_symtab, cb_type, 0, 0, cur_code_body_, 0); /* restore the parser state */ cur_code_body_ = old_cur_code_body; local_symtab_ = old_local_symtab; enclosing_local_symtab_ = old_enclosing_local_symtab; enclosing_stm_ = old_enclosing_stm; goto_symtab_ = old_goto_symtab; local_cnt_ = old_local_cnt; max_local_cnt_ = old_max_local_cnt; has_local_ctx_ = old_has_local_ctx; local_ctx_var_num_ = old_local_ctx_var_num; ctx_var_props_used_ = old_ctx_var_props_used; next_ctx_arr_idx_ = old_next_ctx_arr_idx; self_valid_ = old_self_valid; self_referenced_ = old_self_referenced; full_method_ctx_referenced_= old_full_method_ctx_referenced; local_ctx_needs_self_ = old_local_ctx_needs_self; local_ctx_needs_full_method_ctx_ = old_local_ctx_needs_full_method_ctx; /* return the code body we parsed */ return code_body; } /* ------------------------------------------------------------------------ */ /* * Parse a function or method body * * op_args is non-zero if this is an operator overload property. This * encodes the possible usages of the operator: bit 0x01 is set if this can * be a unary operator, bit 0x02 if it can be binary, and bit 0x4 is set if * it can be trinary. Most operators have only one bit set, but some have * multiple uses, such as "-" which can be unary (-3) or binary (5-2). The * number of arguments is always one less than the number of operands, * because 'self' is always the primary operand (the sole operand of a * unary, the left operand of a binary, or the leftmost operand of a * trinary). */ CTPNCodeBody *CTcParser::parse_code_body( int eq_before_brace, int is_obj_prop, int self_valid, int *p_argc, int *p_opt_argc, int *p_varargs, int *p_varargs_list, CTcSymLocal **p_varargs_list_local, int *p_has_retval, int *err, CTcPrsSymtab *local_symtab, tcprs_codebodytype cb_type, struct propset_def *propset_stack, int propset_depth, CTcCodeBodyRef *enclosing_code_body, CTcFormalTypeList **type_list) { /* * create a new code body reference - this will let nested code bodies * refer back to the code body object we're about to parse, even though * we won't create the actual code body object until we're done parsing * the entire code body */ cur_code_body_ = new (G_prsmem) CTcCodeBodyRef(); /* note if we're parsing some kind of anonymous function */ int parsing_anon_fn = (cb_type == TCPRS_CB_ANON_FN || cb_type == TCPRS_CB_SHORT_ANON_FN); /* remember the 'self' validity */ self_valid_ = self_valid; /* presume we will not need a local variable context object */ clear_local_ctx(); /* * Set the outer local symbol table. If the caller has provided us * with an explicit pre-constructed local symbol table, use that; * otherwise, use the global symbol table, since we have no locals * of our own yet. */ local_symtab_ = (local_symtab == 0 ? global_symtab_ : local_symtab); enclosing_local_symtab_ = (local_symtab_->get_parent() == 0 ? global_symtab_ : local_symtab_->get_parent()); /* there's no enclosing statement yet */ enclosing_stm_ = 0; /* * defer creating a 'goto' symbol table until we encounter a label * or a 'goto' */ goto_symtab_ = 0; /* no locals yet */ local_cnt_ = 0; max_local_cnt_ = 0; /* no formals yet */ int formal_num = 0; int opt_formal_num = 0; int varargs = FALSE; int varargs_list = FALSE; CTcSymLocal *varargs_list_local = 0; /* we haven't built our statement yet */ CTPNStmComp *stm = 0; long start_line = 0; CTcTokFileDesc *start_desc = 0; /* check for a short anonymous function, which uses unusual syntax */ if (cb_type == TCPRS_CB_SHORT_ANON_FN) { /* we're at the opening brace now */ G_tok->get_last_pos(&start_desc, &start_line); /* * a short-form anonymous function always has an argument list, * but it uses special notation: the argument list is simply the * first thing after the function's open brace, and ends with a * colon */ parse_formal_list(FALSE, TRUE, &formal_num, &opt_formal_num, &varargs, &varargs_list, &varargs_list_local, err, 0, TRUE, 0); if (*err) return 0; /* * The contents of a short-form anonymous function are simply an * expression, whose value is implicitly returned by the function. * Alternatively, it can start with a list of "local" clauses, to * define local variables. */ CTcPrsNode *expr = 0; while (G_tok->cur() == TOKT_LOCAL) { /* we need a variable name symbol */ if (G_tok->next() == TOKT_SYM) { /* add the symbol */ CTcSymLocal *lcl = local_symtab_->add_local(alloc_local()); /* check for an initializer */ CTcPrsNode *subexpr = 0; if (G_tok->next() == TOKT_ASI || G_tok->cur() == TOKT_EQ) { /* parse the initializer */ subexpr = parse_local_initializer(lcl, err); /* combine it with the expression we had so far */ expr = (expr == 0 ? subexpr : new CTPNComma(expr, subexpr)); } /* if there's a comma, skip it */ if (G_tok->cur() == TOKT_COMMA) G_tok->next(); } else { /* invalid 'local' syntax */ G_tok->log_error_curtok(TCERR_LOCAL_REQ_SYM); } } /* if there's anything left, parse the simple expression */ if (expr == 0 || G_tok->cur() != TOKT_RBRACE) { /* parse the rest of the expression */ CTcPrsNode *subexpr = parse_expr_or_dstr(TRUE); /* combine it with the local initializers, if any */ expr = (expr == 0 ? subexpr : new CTPNComma(expr, subexpr)); } /* * The next token must be the closing brace ('}') of the function. * If the next token is a semicolon, it's an error, but it's * probably just a superfluous semicolon that we can ignore. */ if (G_tok->cur() == TOKT_SEM) { /* log an error explaining the problem */ G_tok->log_error(TCERR_SEM_IN_SHORT_ANON_FN); /* skip the semicolon */ G_tok->next(); } /* check for the brace */ switch (G_tok->cur()) { case TOKT_RBRACE: /* this is what we want - skip it and continue */ G_tok->next(); break; case TOKT_EOF: /* log an error and give up */ G_tok->log_error_curtok(TCERR_SHORT_ANON_FN_REQ_RBRACE); *err = 1; return 0; default: /* log an error, assuming they simply forgot the '}' */ G_tok->log_error_curtok(TCERR_SHORT_ANON_FN_REQ_RBRACE); break; } /* * This anonymous function syntax implicitly returns the value of * the expression, so generate a 'return' statement node that * returns the expression. If the expression has no return value, * we're simply evaluating it for side-effects, so wrap it in a * simple 'expression' statement. */ CTPNStm *ret_stm; if (expr->has_return_value()) ret_stm = new CTPNStmReturn(expr); else ret_stm = new CTPNStmExpr(expr); /* put the 'return' statement inside a compound statement */ stm = new CTPNStmComp(ret_stm, local_symtab_); } else { /* * If we have a propertyset stack, set up an inserted token stream * with the expanded token list for the formals, combining the * formals from the enclosing propertyset definitions with the * formals defined here. */ if (propset_depth != 0) insert_propset_expansion(propset_stack, propset_depth); /* * if we have an explicit left parenthesis, or an implied formal * list from an enclosing propertyset, parse the list */ if (G_tok->cur() == TOKT_LPAR) { /* skip the open paren */ G_tok->next(); /* * Parse the formal list. Add the symbols to the local * table (hence 'count_only' = false), and don't allow * optional arguments. */ parse_formal_list(FALSE, TRUE, &formal_num, &opt_formal_num, &varargs, &varargs_list, &varargs_list_local, err, 0, FALSE, type_list); if (*err) return 0; } /* parse an equals sign, if present */ if (G_tok->cur() == TOKT_EQ) { /* * An equals sign after a formal parameter list can be used if * the 'eq_before_brace' flag is set. Otherwise, if we're * defining an object property, this is an error, since it's * obsolete TADS 2 syntax that we no longer allow - because * this is a change in syntax, we want to catch it * specifically so we can provide good diagnostic information * for it. */ if (eq_before_brace && G_tok->cur() == TOKT_EQ) { /* it's allowed - skip the '=' */ G_tok->next(); } else if (is_obj_prop) { /* obsolete tads 2 syntax - flag the error */ G_tok->log_error(TCERR_EQ_WITH_METHOD_OBSOLETE); /* * skip the '=' so we can continue parsing the rest of the * code body without cascading errors */ G_tok->next(); } else { /* * it's not a situation where we allow '=' specifically, * or where we know why it might be present erroneously - * let it go for now, as we'll flag the error in the * normal compound statement parsing */ } } /* check for '(' syntax */ //$$$ /* require the '{' */ switch (G_tok->cur()) { case TOKT_LBRACE: parse_body: /* note the location of the opening brace */ G_tok->get_last_pos(&start_desc, &start_line); /* parse the compound statement */ stm = parse_compound(err, TRUE, TRUE, 0, TRUE); break; case TOKT_SEM: case TOKT_RBRACE: /* * we seem to have found the end of the object definition, or * the end of a code body - treat it as an empty code body */ G_tok->log_error_curtok(TCERR_REQ_LBRACE_CODE); stm = new CTPNStmComp(0, 0); break; default: /* * the '{' was missing - log an error, but proceed from the * current token on the assumption that they merely left out * the open brace */ G_tok->log_error_curtok(TCERR_REQ_LBRACE_CODE); goto parse_body; } } /* if that failed, return the error */ if (*err || stm == 0) return 0; /* * determine how the statement exits, and generate any internal flow * warnings within the body code */ unsigned long flow_flags = stm->get_control_flow(TRUE); /* * Warn if the function has both explicit void and value returns. * If not, check to see if it continues; if so, it implicitly * returns a void value by falling off the end, so warn if it both * falls off the end and returns a value somewhere else. Suppress * this warning if this is a syntax check only. */ if (!G_prs->get_syntax_only()) { if ((flow_flags & TCPRS_FLOW_RET_VAL) != 0 && (flow_flags & TCPRS_FLOW_RET_VOID) != 0) { /* it has explicit void and value returns */ stm->log_warning(TCERR_RET_VAL_AND_VOID); } else if ((flow_flags & TCPRS_FLOW_RET_VAL) != 0 && (flow_flags & TCPRS_FLOW_NEXT) != 0) { /* it has explicit value returns, and implicit void return */ stm->log_warning(TCERR_RET_VAL_AND_IMP_VOID); } } /* if the caller is interested, return the interface details */ if (p_argc != 0) *p_argc = formal_num; if (p_opt_argc != 0) *p_opt_argc = opt_formal_num; if (p_varargs != 0) *p_varargs = varargs; if (p_varargs_list != 0) *p_varargs_list = varargs_list; if (p_varargs_list_local != 0) *p_varargs_list_local = varargs_list_local; if (p_has_retval) *p_has_retval = ((flow_flags & TCPRS_FLOW_RET_VAL) != 0); /* create a code body node for the result */ CTPNCodeBody *body_stm = new CTPNCodeBody( local_symtab_, goto_symtab_, stm, formal_num, opt_formal_num, varargs, varargs_list, varargs_list_local, max_local_cnt_, self_valid, enclosing_code_body); /* store this new statement in the current code body reference object */ cur_code_body_->ptr = body_stm; /* save the starting location */ body_stm->set_start_location(start_desc, start_line); /* * set the end location in the new code body to the end location in * the underlying compound statement */ body_stm->set_end_location(stm->get_end_desc(), stm->get_end_linenum()); /* set up the local context for access to enclosing scope locals */ finish_local_ctx(body_stm, local_symtab); /* * if 'self' is valid, and we're parsing an anonymous function, and we * have any references in this code body to any method context * variables (self, targetprop, targetobj, definingobj), make certain * that the code body has a context at level 1, so that it can pick up * our method context */ if (self_valid && parsing_anon_fn && (self_referenced_ || full_method_ctx_referenced_)) body_stm->get_or_add_ctx_var_for_level(1); /* mark the code body for references to the method context */ body_stm->set_self_referenced(self_referenced_); body_stm->set_full_method_ctx_referenced(full_method_ctx_referenced_); /* * mark the code body for inclusion in any local context of the method * context */ body_stm->set_local_ctx_needs_self(local_ctx_needs_self_); body_stm->set_local_ctx_needs_full_method_ctx( local_ctx_needs_full_method_ctx_); /* return the new body statement */ return body_stm; } /* * Parse a compound statement */ CTPNStmComp *CTcParser::parse_compound( int *err, int skip_lbrace, int need_rbrace, CTPNStmSwitch *enclosing_switch, int use_enclosing_scope) { /* save the current line information for later */ CTcTokFileDesc *file; long linenum; G_tok->get_last_pos(&file, &linenum); /* skip the '{' if we're on one and the caller wants us to */ if (skip_lbrace && G_tok->cur() == TOKT_LBRACE) G_tok->next(); /* enter a scope */ tcprs_scope_t scope_data; if (!use_enclosing_scope) enter_scope(&scope_data); /* we don't have any statements in our sublist yet */ CTPNStm *first_stm = 0; CTPNStm *last_stm = 0; CTPNStm *cur_stm = 0; /* presume we won't find the closing brace */ int skip_rbrace = FALSE; /* keep going until we reach the closing '}' */ for (int done = FALSE ; !done ; ) { /* check what we've found */ switch (G_tok->cur()) { case TOKT_RBRACE: /* it's our closing brace - we're done */ done = TRUE; cur_stm = 0; /* note that we must still skip the closing brace */ skip_rbrace = TRUE; /* stop scanning statements */ break; case TOKT_EOF: /* * if we're at end of file, and we don't need a right brace to * end the block, consider this the end of the block */ if (!need_rbrace) { done = TRUE; cur_stm = 0; skip_rbrace = FALSE; break; } else { /* it's an error */ G_tok->log_error(TCERR_EOF_IN_CODE); cur_stm = 0; done = TRUE; break; } break; default: /* parse a statement */ cur_stm = parse_stm(err, enclosing_switch, FALSE); /* if an error occurred, stop parsing */ if (*err) done = TRUE; break; } /* if we parsed a statement, add it to our list */ if (cur_stm != 0) { /* link the statement at the end of our list */ if (last_stm != 0) last_stm->set_next_stm(cur_stm); else first_stm = cur_stm; last_stm = cur_stm; } } /* if there's no statement, make the body a null statement */ if (first_stm == 0) first_stm = new CTPNStmNull(); /* build the compound statement node */ CTPNStmComp *comp_stm = new CTPNStmComp(first_stm, local_symtab_); /* set some additional information if we created a statement */ if (comp_stm != 0) { /* set the statement's line to the start of the compound */ comp_stm->set_source_pos(file, linenum); /* note whether or not we have our own private scope */ comp_stm->set_has_own_scope(!use_enclosing_scope && (local_symtab_ != scope_data.local_symtab)); } /* if necessary, skip the closing brace */ if (skip_rbrace) G_tok->next(); /* leave the local scope */ if (!use_enclosing_scope) leave_scope(&scope_data); /* return the compound statement object */ return comp_stm; } /* * Create a local symbol table for the current scope, if necessary */ void CTcParser::create_scope_local_symtab() { /* * if our symbol table is the same as the enclosing symbol table, we * must create our own table */ if (local_symtab_ == enclosing_local_symtab_) { /* * Create our own local symbol table, replacing the current one * - we saved the enclosing one already when we entered the * scope, so we'll restore it on our way out. The new local * symbol table has the enclosing symbol table as its parent * scope. */ local_symtab_ = new CTcPrsSymtab(local_symtab_); } } /* * Parse a local variable definition */ CTPNStm *CTcParser::parse_local(int *err) { int done; CTPNStm *first_stm; CTPNStm *last_stm; /* we have no initializer statements yet */ first_stm = last_stm = 0; /* skip the 'local' keyword */ G_tok->next(); /* keep going until we reach the closing semicolon */ for (done = FALSE ; !done ; ) { /* we need a symbol name */ if (G_tok->cur() == TOKT_SYM) { const char *sym; size_t symlen; CTcSymLocal *lcl; CTPNStm *stm; CTcPrsNode *expr; /* get the symbol string from the token */ sym = G_tok->getcur()->get_text(); symlen = G_tok->getcur()->get_text_len(); /* add the new local variable to our symbol table */ lcl = local_symtab_->add_local(sym, symlen, alloc_local(), FALSE, FALSE, FALSE); /* skip the symbol and check for an initial value assignment */ switch (G_tok->next()) { case TOKT_EQ: case TOKT_ASI: /* parse the initializer */ expr = parse_local_initializer(lcl, err); /* if we didn't get a statement, we can't proceed */ if (expr == 0) { done = TRUE; break; } /* create a statement for the assignment */ stm = new CTPNStmExpr(expr); /* * set the statement's source location according to the * current source location - if we have multiple * initializers over several lines, this will allow the * debugger to step through the individual * initializations */ stm->set_source_pos(G_tok->get_last_desc(), G_tok->get_last_linenum()); /* add the statement to our list */ if (last_stm != 0) last_stm->set_next_stm(stm); else first_stm = stm; last_stm = stm; /* done */ break; default: /* there's nothing more to do with this variable */ break; } /* * check what follows - we can have a comma to introduce * another local variable, or a semicolon to end the * statement */ switch(G_tok->cur()) { case TOKT_COMMA: /* skip the comma and go on to the next variable */ G_tok->next(); break; case TOKT_SEM: /* skip the semicolon, and stop scanning */ G_tok->next(); done = TRUE; break; case TOKT_SYM: /* * they probably just left out a comma - assume the * comma is there and keep going */ G_tok->log_error_curtok(TCERR_LOCAL_REQ_COMMA); break; default: /* * these almost certainly indicate that they left out a * semicolon - report the error and continue from here */ G_tok->log_error_curtok(TCERR_LOCAL_REQ_COMMA); done = TRUE; break; } } else { /* symbol required - log the error */ G_tok->log_error_curtok(TCERR_LOCAL_REQ_SYM); /* determine how to proceed based on what we have */ switch(G_tok->cur()) { case TOKT_COMMA: /* * they probably just put in an extra comma - skip it * and keep trying to parse the local list */ G_tok->next(); break; case TOKT_SEM: /* that's the end of the statement */ G_tok->next(); done = TRUE; break; case TOKT_EOF: /* set the error flag and stop scanning */ *err = TRUE; done = TRUE; break; default: /* try skipping this token and trying again */ G_tok->next(); break; } } } /* * if we have one statement, return it; if we have more than one, * return a compound statement to contain the list; if we have * nothing, return nothing */ if (first_stm == 0) return 0; else if (first_stm == last_stm) return first_stm; else return new CTPNStmComp(first_stm, local_symtab_); } /* * Parse a local variable initializer */ CTcPrsNode *CTcParser::parse_local_initializer(CTcSymLocal *lcl, int *err) { /* * skip the assignment operator and parse the expression (which * cannot use the comma operator) */ G_tok->next(); CTcPrsNode *expr = parse_asi_expr(); /* if that failed, return failure */ if (expr == 0) return 0; /* * if we have a valid local, return a new expression node for the * assignment; otherwise just return the expression, since we have * nothing to assign to */ return (lcl != 0 ? new CTPNAsi(new CTPNSymResolved(lcl), expr) : expr); } /* * Parse a statement */ CTPNStm *CTcParser::parse_stm(int *err, CTPNStmSwitch *enclosing_switch, int compound_use_enclosing_scope) { CTcToken tok; /* * remember where the statement starts - when we create the * statement object, it will refer to these values to set its * internal memory of the statement's source file location */ cur_desc_ = G_tok->get_last_desc(); cur_linenum_ = G_tok->get_last_linenum(); /* see what we have */ try_again: switch(G_tok->cur()) { case TOKT_EOF: /* unexpected end of file - log an error */ G_tok->log_error(TCERR_EOF_IN_CODE); /* set the caller's error flag */ *err = TRUE; /* there's no statement to return, obviously */ return 0; case TOKT_DSTR_MID: case TOKT_DSTR_END: case TOKT_RBRACE: /* * we shouldn't be looking at any of these at the start of a * statement */ G_tok->log_error_curtok(TCERR_EXPECTED_STMT_START); G_tok->next(); return 0; case TOKT_SEM: /* * null statement - this doesn't generate any code; simply skip * the semicolon and keep going */ G_tok->next(); /* this doesn't generate any code */ return 0; case TOKT_LOCAL: /* if we don't have our own local symbol table, create one */ create_scope_local_symtab(); /* parse the local variable definition and return the result */ return parse_local(err); case TOKT_LBRACE: /* it's a compound statement */ return parse_compound(err, TRUE, TRUE, 0, compound_use_enclosing_scope); case TOKT_IF: /* parse an if statement */ return parse_if(err); case TOKT_RETURN: /* parse a return statement */ return parse_return(err); case TOKT_FOR: /* parse a for statement */ return parse_for(err); case TOKT_FOREACH: /* parse a foreach statement */ return parse_foreach(err); case TOKT_WHILE: /* parse a while statement */ return parse_while(err); case TOKT_DO: /* parse a do-while */ return parse_do_while(err); case TOKT_SWITCH: /* parse a switch */ return parse_switch(err); case TOKT_GOTO: /* parse a 'goto' */ return parse_goto(err); case TOKT_BREAK: return parse_break(err); case TOKT_CONTINUE: return parse_continue(err); case TOKT_TRY: return parse_try(err); case TOKT_THROW: return parse_throw(err); case TOKT_CATCH: /* misplaced 'catch' clause - log an error */ G_tok->log_error(TCERR_MISPLACED_CATCH); /* * skip the following open paren, class name, variable name, and * closing paren, as long as we find all of these */ if (G_tok->next() == TOKT_LPAR && G_tok->next() == TOKT_SYM && G_tok->next() == TOKT_SYM && G_tok->next() == TOKT_RPAR) G_tok->next(); /* there's no valid statement to return */ return 0; case TOKT_FINALLY: /* misplaced 'finally' clause - log an error */ G_tok->log_error(TCERR_MISPLACED_FINALLY); /* skip the 'finally' keyword, and return failure */ G_tok->next(); return 0; case TOKT_ELSE: /* * misplaced 'else' clause - log an error, skip the 'else' * keyword, and proceed with what follows */ G_tok->log_error(TCERR_MISPLACED_ELSE); G_tok->next(); return 0; case TOKT_CASE: /* * if we're in a 'switch', it's a valid 'case' label; otherwise, * it's misplaced */ if (enclosing_switch != 0) { /* parse the 'case' label */ return parse_case(err, enclosing_switch); } else { /* * not directly within a 'switch', so this is a misplaced * 'case' keyword - log an error */ G_tok->log_error(TCERR_MISPLACED_CASE); /* skip the 'case' keyword */ G_tok->next(); /* assume there's an expression here, and skip that as well */ parse_expr(); /* if there's a colon, skip it, too */ if (G_tok->cur() == TOKT_COLON) G_tok->next(); /* proceed from here */ return 0; } case TOKT_DEFAULT: /* allow this only if we're directly in a 'switch' body */ if (enclosing_switch != 0) { /* parse the 'default' label */ return parse_default(err, enclosing_switch); } else { /* misplaced 'default' keyword - log an error */ G_tok->log_error(TCERR_MISPLACED_DEFAULT); /* skip the 'default' keyword; if there's a colon, skip it, too */ if (G_tok->next() == TOKT_COLON) G_tok->next(); /* proceed from here */ return 0; } case TOKT_SYM: /* * It's a symbol. First, check for a label. This requires that * we look ahead one token, because we have to look at the next * token to see if it's a colon; if it's not, we have to back up * and parse the symbol as the start of an expression. So, * remember the current symbol token, then look at what follows. */ tok = *G_tok->copycur(); if (G_tok->next() == TOKT_COLON) { CTPNStmEnclosing *old_enclosing; CTPNStmLabel *label_stm; CTcSymLabel *lbl; CTPNStm *stm; /* it's a label - create a symbol table entry for it */ lbl = add_code_label(&tok); /* create the labeled statement node */ label_stm = new CTPNStmLabel(lbl, enclosing_stm_); /* skip the colon */ G_tok->next(); /* * set our new label to be the enclosing label for * everything contained within its statement */ old_enclosing = set_enclosing_stm(label_stm); /* parse the labeled statement */ stm = parse_stm(err, enclosing_switch, FALSE); /* restore our enclosing statement */ set_enclosing_stm(old_enclosing); /* if parsing the labeled statement failed, give up */ if (*err) return 0; /* connect to the label to the statement it labels */ label_stm->set_stm(stm); /* point the label symbol to its statement node */ if (lbl != 0) lbl->set_stm(label_stm); /* return the labeled statement node */ return label_stm; } /* * it's not a label - push the colon back into the input stream * so that we read it again, then parse this as an ordinary * expression */ G_tok->unget(); goto do_parse_expr; case TOKT_RPAR: /* * they probably had too many close parens in something like a * 'for' or 'if' statement - flag the error */ G_tok->log_error(TCERR_EXTRA_RPAR); /* skip the extra paren and go back for another try */ G_tok->next(); goto try_again; default: do_parse_expr: /* anything else must be the start of an expression */ { /* parse the expression */ CTcPrsNode *expr = parse_expr_or_dstr(TRUE); /* the statement must be terminated with a semicolon */ if (parse_req_sem()) { /* set the error flag */ *err = TRUE; /* there's no statement to return */ return 0; } /* * if we successfully parsed an expression, create a * statement node for the expression; if expr is null, the * expression parser will already have issued an error, so * we can simply ignore the failed expression and continue * to the next statement */ if (expr != 0) return new CTPNStmExpr(expr); else return 0; } } } /* * Add a 'goto' label symbol to the current code body */ CTcSymLabel *CTcParser::add_code_label(const CTcToken *tok) { /* if there's no 'goto' symbol table, create one */ if (goto_symtab_ == 0) goto_symtab_ = new CTcPrsSymtab(0); /* create the label and return it */ return goto_symtab_->add_code_label(tok->get_text(), tok->get_text_len(), FALSE); } /* * Parse an 'if' statement */ CTPNStm *CTcParser::parse_if(int *err) { CTcPrsNode *cond_expr; CTPNStm *if_stm; CTPNStm *then_stm; CTPNStm *else_stm; CTcTokFileDesc *file; long linenum; /* save the starting line information for later */ G_tok->get_last_pos(&file, &linenum); /* skip the 'if' keyword, and require the open paren */ if (G_tok->next() == TOKT_LPAR) { /* skip the left paren */ G_tok->next(); } else { /* * log an error, but proceed on the assumption that they simply * left out the paren */ G_tok->log_error_curtok(TCERR_REQ_LPAR_IF); } /* parse the expression */ cond_expr = parse_cond_expr(); /* if that failed, return failure */ if (cond_expr == 0) { *err = TRUE; return 0; } /* require the close paren */ if (G_tok->cur() == TOKT_RPAR) { /* skip it */ G_tok->next(); } else { /* * log an error, then proceed assuming that the paren was merely * omitted */ G_tok->log_error_curtok(TCERR_REQ_RPAR_IF); } /* parse the true-part statement */ then_stm = parse_stm(err, 0, FALSE); /* if an error occurred, return failure */ if (*err) return 0; /* check for 'else' */ if (G_tok->cur() == TOKT_ELSE) { /* skip the 'else' keyword */ G_tok->next(); /* parse the false-part statement */ else_stm = parse_stm(err, 0, FALSE); /* if an error occurred, return failure */ if (*err) return 0; } else { /* there's no 'else' part */ else_stm = 0; } /* create and return the 'if' statement node */ if_stm = new CTPNStmIf(cond_expr, then_stm, else_stm); /* set the original statement position in the node */ if_stm->set_source_pos(file, linenum); /* return the 'if' statement node */ return if_stm; } /* * Parse a 'return' statement */ CTPNStm *CTcParser::parse_return(int *err) { CTPNStm *stm; /* skip the 'return' keyword and see what we have */ switch(G_tok->next()) { case TOKT_SEM: /* * end of the statement - this is a void return; skip the * semicolon, and return a void return statement node */ G_tok->next(); stm = new CTPNStmReturn(0); break; case TOKT_LBRACE: case TOKT_RBRACE: /* * they probably just left out a semicolon - flag the error, and * continue parsing from this token */ G_tok->log_error_curtok(TCERR_RET_REQ_EXPR); return 0; default: /* it's a return with an expression - parse the expression */ stm = new CTPNStmReturn(parse_expr()); /* make sure we're on a semicolon */ if (parse_req_sem()) { *err = TRUE; return 0; } /* done */ break; } /* return the statement node we created */ return stm; } /* * Parse a 'for' statement */ CTPNStm *CTcParser::parse_for(int *err) { /* save the current line information for later */ CTcTokFileDesc *file; long linenum; G_tok->get_last_pos(&file, &linenum); /* * enter a scope, in case we create a local symbol table for local * variables defined within the 'for' statement */ tcprs_scope_t scope_data; enter_scope(&scope_data); /* parse the open paren */ if (G_tok->next() == TOKT_LPAR) { /* skip it */ G_tok->next(); } else { /* log an error, and proceed, assuming it was simply left out */ G_tok->log_error_curtok(TCERR_REQ_FOR_LPAR); } /* we don't have any of the expressions yet */ CTcPrsNode *init_expr = 0; CTcPrsNode *cond_expr = 0; CTcPrsNode *reinit_expr = 0; int found_in = FALSE; /* "in" expression list */ CTPNForIn *in_head = 0, *in_tail = 0; /* parse the initializer list */ for (int done = FALSE ; !done ; ) { /* presume we won't find an expression on this round */ CTcPrsNode *expr = 0; /* check what we have */ switch(G_tok->cur()) { case TOKT_LOCAL: /* * if we haven't created our own symbol table local to the * 'for' loop, do so now */ create_scope_local_symtab(); /* skip the 'local' keyword and get the local name */ if (G_tok->next() == TOKT_SYM) { /* add the local symbol */ CTcSymLocal *lcl = local_symtab_->add_local(alloc_local()); /* check for the required initializer */ switch(G_tok->next()) { case TOKT_ASI: case TOKT_EQ: /* parse the initializer */ expr = parse_local_initializer(lcl, err); break; case TOKT_SYM: /* check for an 'in' expression */ if (G_tok->getcur()->text_matches("in", 2)) { /* parse the 'var in expr' or 'var in from..to' */ G_tok->next(); expr = parse_for_in_clause( new CTPNSymResolved(lcl), in_head, in_tail); /* note the "in" */ found_in = TRUE; /* if that failed, stop scanning the initializer */ if (expr == 0) done = TRUE; } else { /* anything else is an error */ goto req_asi; } break; default: req_asi: /* log an error - an initializer is required */ G_tok->log_error_curtok(TCERR_REQ_FOR_LOCAL_INIT); break; } } else { /* * the 'local' statement isn't constructed properly - * this is difficult to recover from intelligently, so * just log an error and keep going from here */ G_tok->log_error_curtok(TCERR_LOCAL_REQ_SYM); break; } break; case TOKT_SEM: /* it's a semicolon - we're done with the initializer list */ done = TRUE; /* * if we have an expression already, it means that the * previous token was a comma - this is an error, since we * have a missing expression; log the error but continue * anyway */ if (init_expr != 0) G_tok->log_error(TCERR_MISSING_FOR_INIT_EXPR); break; case TOKT_RPAR: /* if we found an "in", we can end early */ if (found_in) { done = TRUE; break; } /* otherwise, fall through to the missing part error... */ /* FALL THROUGH */ case TOKT_LBRACE: case TOKT_RBRACE: /* premature end of the list - log an error and stop */ G_tok->log_error_curtok(TCERR_MISSING_FOR_PART); done = TRUE; break; default: /* * This must be an expression - parse it. Parse an * assignment expression, not a comma expression, because we * must check for a "local" clause after each comma. */ expr = parse_asi_expr(); /* if that failed, stop scanning the "for" */ if (expr == 0) done = TRUE; /* check for an 'in' expression */ if (G_tok->cur() == TOKT_SYM && G_tok->getcur()->text_matches("in", 2)) { /* skip the "in" and parse the collection/range */ G_tok->next(); expr = parse_for_in_clause(expr, in_head, in_tail); /* note the "in" */ found_in = TRUE; /* if that failed, stop parsing the initializer */ if (expr == 0) done = TRUE; } /* done with this clause */ break; } /* * if we got an expression, add it into the initializer * expression under construction by adding it under a "comma" * node */ if (expr != 0) { /* * if there's an expression, build a comma expression for * the expression so far plus the new expression; otherwise, * the new expression becomes the entire expression so far */ if (init_expr != 0) init_expr = new CTPNComma(init_expr, expr); else init_expr = expr; } /* if we're done, we can stop now */ if (done) break; /* * we must have a semicolon or comma after each initializer * expression */ switch(G_tok->cur()) { case TOKT_SEM: /* that's the end of the statement - stop now */ done = TRUE; break; case TOKT_COMMA: /* skip the comma and parse the next initializer */ G_tok->next(); break; case TOKT_RPAR: /* if we found an "in" clause, we only need an initializer */ if (found_in) { done = TRUE; break; } /* otherwise, fall through to the error case... */ /* FALL THROUGH */ case TOKT_LBRACE: case TOKT_RBRACE: /* log an error, and stop parsing the expression list */ G_tok->log_error_curtok(TCERR_MISSING_FOR_PART); done = TRUE; break; default: /* log an error */ G_tok->log_error_curtok(TCERR_REQ_FOR_INIT_COMMA); /* skip the errant token and keep going */ G_tok->next(); break; } } /* * if we successfully found the ';' at the end of the initializer * list, parse the condition expression */ if (G_tok->cur() == TOKT_SEM) { int cont_to_reinit; /* presume we'll want to continue to the reinit expression */ cont_to_reinit = TRUE; /* skip the ';' */ G_tok->next(); /* if the condition isn't empty, parse it */ if (G_tok->cur() != TOKT_SEM) cond_expr = parse_cond_expr(); /* require the ';' after the expression */ switch(G_tok->cur()) { case TOKT_SEM: /* it's fine - keep going from here */ G_tok->next(); break; case TOKT_RPAR: case TOKT_LBRACE: case TOKT_RBRACE: /* missing part */ G_tok->log_error_curtok(TCERR_MISSING_FOR_PART); /* don't bother trying to find a reinitialization expression */ cont_to_reinit = FALSE; break; default: /* * we seem to be missing the semicolon; keep going from * here, assuming that they simply left out the semicolon * between the condition and reinitializer expressions */ G_tok->log_error_curtok(TCERR_REQ_FOR_COND_SEM); break; } /* * if we're to continue to the reinitializer, parse it; there is * no reinitialization expression if the next token is a right * paren */ if (cont_to_reinit && G_tok->cur() != TOKT_RPAR) { /* parse the expression */ reinit_expr = parse_expr(); } /* make sure we have the right paren */ if (G_tok->cur() == TOKT_RPAR) { /* skip the paren */ G_tok->next(); } else { /* * log an error, and try parsing the body from here, on the * assumption that they simply forgot about the right paren * and jumped right into the body */ G_tok->log_error_curtok(TCERR_REQ_FOR_RPAR); } } else if (G_tok->cur() == TOKT_RPAR) { /* * We already found the right paren - early, so we logged an error * if necessary (it's an error in most cases, but *not* if there's * an 'in' clause in the initializer list). Simply skip it now so * that we can proceed to the body of the 'for'. */ G_tok->next(); } /* create the "for" node */ CTPNStmFor *for_stm = new CTPNStmFor( init_expr, cond_expr, reinit_expr, in_head, local_symtab_, enclosing_stm_); /* set the 'for' to enclose its body */ CTPNStmEnclosing *old_enclosing = set_enclosing_stm(for_stm); /* parse the body of the "for" loop */ CTPNStm *body_stm = parse_stm(err, 0, FALSE); /* restore the old enclosing statement */ set_enclosing_stm(old_enclosing); /* if that failed, return failure */ if (*err) return 0; /* set the body of the 'for' */ for_stm->set_body(body_stm); /* set the original statement position in the node */ for_stm->set_source_pos(file, linenum); /* set the own-scope flag */ for_stm->set_has_own_scope(local_symtab_ != scope_data.local_symtab); /* exit any local scope we created */ leave_scope(&scope_data); /* return the "for" node */ return for_stm; } /* * Parse a for..in clause. This parses the part after the "in"; we accept * either a simple expression, or a range (expr .. expr). We return the * expression node representing the clause. It's up to the caller to look * up the local variable before the "in" and pass it to us. */ CTcPrsNode *CTcParser::parse_for_in_clause( CTcPrsNode *lval, CTPNForIn *&head, CTPNForIn *&tail) { /* * Parse an assignment expression. We parse from this point in the * grammar because the entire 'in' clause can be part of a comma * expression, so we want to stop if we reach a comma rather than * treating the comma as part of our own expression. */ CTcPrsNode *expr = parse_asi_expr(); /* if that failed, return failure */ if (expr == 0) return 0; /* if the next token is "..", we have a "for var in from..to" range */ CTPNForIn *in_expr; if (G_tok->cur() == TOKT_DOTDOT) { /* skip the ".." and parse the "to" expression */ G_tok->next(); CTcPrsNode *toExpr = parse_asi_expr(); /* if that failed, return failure */ if (toExpr == 0) return 0; /* check for the optional "step" expression */ CTcPrsNode *stepExpr = 0; if (G_tok->cur() == TOKT_SYM && G_tok->getcur()->text_matches("step", 4)) { /* skip the "step" and parse the expression */ G_tok->next(); stepExpr = parse_asi_expr(); } /* * allocate variables for the "to" and "step" expressions, but only * if they're non-constant */ int to_local = (toExpr->is_const() ? -1 : alloc_local()); int step_local = (stepExpr == 0 || stepExpr->is_const() ? -1 : alloc_local()); /* build the "for x in range" node */ in_expr = new CTPNVarInRange(lval, expr, toExpr, stepExpr, to_local, step_local); } else { /* it's a plain old "for x in collection" */ in_expr = new CTPNVarIn(lval, expr, alloc_local()); } /* link it into the list */ if (tail != 0) tail->setnxt(in_expr); else head = in_expr; tail = in_expr; /* return the new expression */ return in_expr; } /* * 'for var in expr' - fold constants */ CTcPrsNode *CTPNVarInBase::fold_constants(CTcPrsSymtab *symtab) { /* fold each element */ lval_ = lval_->fold_constants(symtab); expr_ = expr_->fold_constants(symtab); /* we can't fold the overall 'in' expression itself */ return this; } /* * 'for var in from .. to' - fold constants */ CTcPrsNode *CTPNVarInRangeBase::fold_constants(CTcPrsSymtab *symtab) { /* fold each element */ lval_ = lval_->fold_constants(symtab); from_expr_ = from_expr_->fold_constants(symtab); to_expr_ = to_expr_->fold_constants(symtab); if (step_expr_ != 0) step_expr_ = step_expr_->fold_constants(symtab); /* we can't fold the overall 'in' expression itself */ return this; } /* * Parse a 'foreach' statement */ CTPNStm *CTcParser::parse_foreach(int *err) { tcprs_scope_t scope_data; CTcPrsNode *iter_expr; CTcPrsNode *coll_expr; CTPNStm *body_stm; CTPNStmForeach *foreach_stm; CTcTokFileDesc *file; long linenum; CTPNStmEnclosing *old_enclosing; /* save the current line information for later */ G_tok->get_last_pos(&file, &linenum); /* * enter a scope, in case we create a local symbol table for local * variables defined within the 'for' statement */ enter_scope(&scope_data); /* parse the open paren */ if (G_tok->next() == TOKT_LPAR) { /* skip it */ G_tok->next(); } else { /* log an error, and proceed, assuming it was simply left out */ G_tok->log_error_curtok(TCERR_REQ_FOREACH_LPAR); } /* we don't have the iterator lvalue or collection expression yet */ iter_expr = 0; coll_expr = 0; /* check for 'local' before the iteration variable */ switch (G_tok->cur()) { case TOKT_LOCAL: /* * if we haven't created our own symbol table local to the 'for' * loop, do so now */ create_scope_local_symtab(); /* skip the 'local' keyword and get the local name */ if (G_tok->next() == TOKT_SYM) { /* add the local symbol */ local_symtab_->add_local(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), alloc_local(), FALSE, FALSE, FALSE); } else { /* log the error */ G_tok->log_error_curtok(TCERR_LOCAL_REQ_SYM); } /* go handle the local as the iteration expression */ goto do_expr; case TOKT_LPAR: case TOKT_SYM: do_expr: /* parse the iterator lvalue expression */ iter_expr = parse_expr(); if (iter_expr == 0) { *err = TRUE; return 0; } break; default: /* premature end of the list - log an error and stop */ G_tok->log_error_curtok(TCERR_MISSING_FOREACH_EXPR); return 0; } /* require the 'in' keyword */ if (G_tok->cur() != TOKT_SYM || !G_tok->getcur()->text_matches("in", 2)) { /* log an error */ G_tok->log_error_curtok(TCERR_FOREACH_REQ_IN); /* see what we have */ switch(G_tok->cur()) { case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_SEM: case TOKT_EOF: /* probably end of statement */ return 0; case TOKT_RPAR: /* * probably an extra paren in the variable expression - skip * the paren and continue */ G_tok->next(); break; default: /* probably just left out 'in' - continue from here */ break; } } else { /* skip the 'in' */ G_tok->next(); } /* parse the collection expression */ coll_expr = parse_expr(); if (coll_expr == 0) { *err = TRUE; return 0; } /* make sure we have the close paren */ if (G_tok->cur() != TOKT_RPAR) { /* * log the error, but continue from here on the assumption that * they simply left out the paren */ G_tok->log_error_curtok(TCERR_REQ_FOREACH_RPAR); } else { /* skip the paren */ G_tok->next(); } /* * create the "foreach" node, allocating a private local variable * for holding the iterator object */ foreach_stm = new CTPNStmForeach(iter_expr, coll_expr, local_symtab_, enclosing_stm_, alloc_local()); /* set the "foreach" node to enclose its body */ old_enclosing = set_enclosing_stm(foreach_stm); /* parse the body of the loop */ body_stm = parse_stm(err, 0, FALSE); /* restore the old enclosing statement */ set_enclosing_stm(old_enclosing); /* if that failed, return failure */ if (*err) return 0; /* set the body of the 'for' */ foreach_stm->set_body(body_stm); /* set the original statement position in the node */ foreach_stm->set_source_pos(file, linenum); /* set the own-scope flag */ foreach_stm->set_has_own_scope(local_symtab_ != scope_data.local_symtab); /* exit any local scope we created */ leave_scope(&scope_data); /* return the new statement node */ return foreach_stm; } /* * Parse a 'break' statement */ CTPNStm *CTcParser::parse_break(int *err) { CTPNStmBreak *brk_stm; /* create the 'break' statement */ brk_stm = new CTPNStmBreak(); /* skip the 'break' keyword and check what follows */ switch(G_tok->next()) { case TOKT_SYM: /* set the label in the statement */ brk_stm->set_label(G_tok->getcur()); /* skip the label token */ G_tok->next(); break; case TOKT_SEM: /* keep going - we'll skip it in a moment */ break; case TOKT_LBRACE: case TOKT_RBRACE: /* * they almost certainly simply left off the semicolon - don't * bother with a "label expected" error, since the real error is * most likely just "missing semicolon" */ break; default: /* log the error */ G_tok->log_error_curtok(TCERR_BREAK_REQ_LABEL); break; } /* parse the required terminating semicolon */ if (parse_req_sem()) { *err = TRUE; return 0; } /* return the 'break' node */ return brk_stm; } /* * Parse a 'continue' statement */ CTPNStm *CTcParser::parse_continue(int *err) { CTPNStmContinue *cont_stm; /* create the 'break' statement */ cont_stm = new CTPNStmContinue(); /* skip the 'continue' keyword and check what follows */ switch(G_tok->next()) { case TOKT_SYM: /* set the label in the statement */ cont_stm->set_label(G_tok->getcur()); /* skip the label token */ G_tok->next(); break; case TOKT_SEM: /* keep going - we'll skip it in a moment */ break; case TOKT_LBRACE: case TOKT_RBRACE: /* * they almost certainly simply left off the semicolon - don't * bother with a "label expected" error, since the real error is * most likely just "missing semicolon" */ break; default: /* log the error */ G_tok->log_error_curtok(TCERR_CONT_REQ_LABEL); break; } /* parse the required terminating semicolon */ if (parse_req_sem()) { *err = TRUE; return 0; } /* return the 'continue' node */ return cont_stm; } /* * Parse a 'while' statement */ CTPNStm *CTcParser::parse_while(int *err) { CTcPrsNode *expr; CTPNStm *body_stm; CTPNStmWhile *while_stm; CTPNStmEnclosing *old_enclosing; /* skip the 'while' and check for the open paren */ if (G_tok->next() == TOKT_LPAR) { /* skip the paren */ G_tok->next(); } else { /* * log an error, and proceed on the assumption that the paren * was simply left out and the statement is otherwise * well-formed */ G_tok->log_error_curtok(TCERR_REQ_WHILE_LPAR); } /* parse the condition expression */ expr = parse_cond_expr(); if (expr == 0) { *err = TRUE; return 0; } /* create the 'while' statement node */ while_stm = new CTPNStmWhile(expr, enclosing_stm_); /* check for the close paren */ if (G_tok->cur() == TOKT_RPAR) { /* skip the paren */ G_tok->next(); } else { /* log an error, and continue from here */ G_tok->log_error_curtok(TCERR_REQ_WHILE_RPAR); } /* set the 'while' to enclose its body */ old_enclosing = set_enclosing_stm(while_stm); /* parse the loop body */ body_stm = parse_stm(err, 0, FALSE); /* restore the old enclosing statement */ set_enclosing_stm(old_enclosing); /* give up on error */ if (*err) return 0; /* set the body */ while_stm->set_body(body_stm); /* that's it - build and return the 'while' node */ return while_stm; } /* * Parse a 'do-while' statement */ CTPNStm *CTcParser::parse_do_while(int *err) { CTPNStm *body_stm; CTcPrsNode *expr; CTPNStmDoWhile *do_stm; CTPNStmEnclosing *old_enclosing; /* create the statement object */ do_stm = new CTPNStmDoWhile(enclosing_stm_); /* skip the 'do' keyword */ G_tok->next(); /* set the 'do' to be the enclosing statement */ old_enclosing = set_enclosing_stm(do_stm); /* parse the loop body */ body_stm = parse_stm(err, 0, FALSE); /* restore the enclosing statement */ set_enclosing_stm(old_enclosing); /* return on failure */ if (*err) return 0; /* require the 'while' keyword */ if (G_tok->cur() == TOKT_WHILE) { /* skip the 'while' */ G_tok->next(); } else { /* * no 'while' keyword - there's no obvious way to correct this, * so simply ignore the 'do' statement and keep going from here, * on the assumption that they inadvertantly started a new * statement without finishing the 'do' */ G_tok->log_error_curtok(TCERR_REQ_DO_WHILE); } /* require the open paren */ if (G_tok->cur() == TOKT_LPAR) G_tok->next(); else G_tok->log_error_curtok(TCERR_REQ_WHILE_LPAR); /* parse the expression */ expr = parse_cond_expr(); if (expr == 0) { *err = TRUE; return 0; } /* require the close paren */ if (G_tok->cur() == TOKT_RPAR) G_tok->next(); else G_tok->log_error_curtok(TCERR_REQ_WHILE_RPAR); /* set the condition expression and body in the 'do' node */ do_stm->set_cond(expr); do_stm->set_body(body_stm); /* * remember the location of the 'while' part, since this part * generates code */ do_stm->set_while_pos(G_tok->get_last_desc(), G_tok->get_last_linenum()); /* parse the required closing semicolon */ if (parse_req_sem()) { *err = TRUE; return 0; } /* return the new 'do-while' node */ return do_stm; } /* * Parse a 'switch' statement */ CTPNStm *CTcParser::parse_switch(int *err) { CTcPrsNode *expr; CTPNStmSwitch *switch_stm; CTPNStm *body_stm; int skip; int unreachable_error_shown; CTPNStmEnclosing *old_enclosing; /* create the switch statement object */ switch_stm = new CTPNStmSwitch(enclosing_stm_); /* skip the 'switch' and check for the left paren */ if (G_tok->next() == TOKT_LPAR) { /* skip the left paren */ G_tok->next(); } else { /* log an error, and assume the paren is simply missing */ G_tok->log_error_curtok(TCERR_REQ_SWITCH_LPAR); } /* parse the controlling expression */ expr = parse_expr(); if (expr == 0) { *err = TRUE; return 0; } /* set expression in the switch statement node */ switch_stm->set_expr(expr); /* check for and skip the close paren */ if (G_tok->cur() == TOKT_RPAR) { /* the right paren is present - skip it */ G_tok->next(); } else { /* log an error, and keep going from here */ G_tok->log_error_curtok(TCERR_REQ_SWITCH_RPAR); } /* check for and skip the brace */ if (G_tok->cur() == TOKT_LBRACE) { /* it's there - skip it */ G_tok->next(); } else { /* * log an error, and keep going on the assumption that the brace * is simply missing but the switch body is otherwise correct */ G_tok->log_error_curtok(TCERR_REQ_SWITCH_LBRACE); } /* * The first thing in the switch body must be a 'case', 'default', * or closing brace. Other statements preceding the first 'case' or * 'default' label within the switch body are not allowed, because * they would be unreachable. Keep skipping statements until we get * to one of these. */ for (skip = TRUE, unreachable_error_shown = FALSE ; skip ; ) { /* see what we have */ switch(G_tok->cur()) { case TOKT_CASE: case TOKT_DEFAULT: case TOKT_RBRACE: /* this is what we're looking for */ skip = FALSE; break; case TOKT_EOF: /* end of file within the switch - log an error */ G_tok->log_error(TCERR_EOF_IN_SWITCH); /* return failure */ *err = TRUE; return 0; default: /* * for anything else, log an error explaining that the code * is unreachable - do this only once, no matter how many * unreachable statements precede the first case label */ if (!unreachable_error_shown && !G_prs->get_syntax_only()) { /* show the error */ G_tok->log_error(TCERR_UNREACHABLE_CODE_IN_SWITCH); /* * note that we've shown the error, so we don't show it * again if more unreachable statements follow */ unreachable_error_shown = TRUE; } /* parse (and ignore) this statement */ parse_stm(err, switch_stm, FALSE); if (*err != 0) return 0; /* keep looking for the first label */ break; } } /* the 'switch' is the enclosing statement for children */ old_enclosing = set_enclosing_stm(switch_stm); /* parse the switch body */ body_stm = parse_compound(err, FALSE, TRUE, switch_stm, FALSE); /* restore the enclosing statement */ set_enclosing_stm(old_enclosing); /* if we failed to parse the compound statement, give up */ if (*err) return 0; /* connect the switch to its body */ switch_stm->set_body(body_stm); /* return the switch statement node */ return switch_stm; } /* * Parse a 'case' label */ CTPNStm *CTcParser::parse_case(int *err, CTPNStmSwitch *enclosing_switch) { CTcPrsNode *expr; CTPNStm *stm; CTPNStmCase *case_stm; /* skip the 'case' keyword */ G_tok->next(); /* create the 'case' statement node */ case_stm = new CTPNStmCase(); /* parse the expression */ expr = parse_expr(); if (expr == 0) { *err = TRUE; return 0; } /* store the expression in the case statement node */ case_stm->set_expr(expr); /* require the colon */ if (G_tok->cur() == TOKT_COLON) { /* skip the colon */ G_tok->next(); } else { /* log the error */ G_tok->log_error_curtok(TCERR_REQ_CASE_COLON); } /* * parse the labeled statement - it's directly within this same * enclosing switch, because a case label doesn't create a new * expression nesting level (hence another 'case' label immediately * following without an intervening statement is perfectly valid) */ stm = parse_stm(err, enclosing_switch, FALSE); /* set the statement in the case node */ case_stm->set_stm(stm); /* count the 'case' label in the 'switch' node */ enclosing_switch->inc_case_cnt(); /* return the case node */ return case_stm; } /* * Parse a 'default' label */ CTPNStm *CTcParser::parse_default(int *err, CTPNStmSwitch *enclosing_switch) { CTPNStm *stm; CTPNStmDefault *default_stm; /* create the 'default' statement node */ default_stm = new CTPNStmDefault(); /* * if the enclosing 'switch' already has a 'default' label, it's an * error; continue anyway, since we still want to finish parsing the * syntax */ if (enclosing_switch->get_has_default()) G_tok->log_error(TCERR_DEFAULT_REDEF); /* mark the switch as having a 'default' case */ enclosing_switch->set_has_default(); /* skip the 'default' node, and require the colon */ if (G_tok->next() == TOKT_COLON) { /* skip the colon */ G_tok->next(); } else { /* * log an error, and keep going, assuming that the token was * accidentally omitted */ G_tok->log_error_curtok(TCERR_REQ_DEFAULT_COLON); } /* * parse the labeled statement - it's directly within this same * enclosing switch, because a 'default' label doesn't create a new * expression nesting level (hence another 'case' label immediately * following without an intervening statement is perfectly valid) */ stm = parse_stm(err, enclosing_switch, FALSE); /* set the statement in the 'default' node */ default_stm->set_stm(stm); /* return the 'default' node */ return default_stm; } /* * Parse a 'goto' statement */ CTPNStm *CTcParser::parse_goto(int *err) { CTPNStmGoto *goto_stm; /* skip the 'goto' keyword, and demand that a symbol follows */ if (G_tok->next() == TOKT_SYM) { /* create the parse node for the 'goto' statement */ goto_stm = new CTPNStmGoto(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* skip the symbol */ G_tok->next(); } else { /* log the error */ G_tok->log_error(TCERR_GOTO_REQ_LABEL); /* no statement */ goto_stm = 0; } /* parse the required semicolon */ if (parse_req_sem()) { *err = TRUE; return 0; } /* return the statement node */ return goto_stm; } /* * Parse a 'try-catch-finally' statement */ CTPNStm *CTcParser::parse_try(int *err) { CTPNStmTry *try_stm; CTPNStmEnclosing *old_enclosing; CTPNStm *body_stm; /* create the 'try' statement node */ try_stm = new CTPNStmTry(enclosing_stm_); /* * the 'try' is the enclosing statement for the duration of its * protected code block */ old_enclosing = set_enclosing_stm(try_stm); /* skip the 'try' */ G_tok->next(); /* parse the body of the 'try' block */ body_stm = parse_stm(err, 0, FALSE); /* restore the previous enclosing statement */ set_enclosing_stm(old_enclosing); /* if parsing the body failed, stop now */ if (*err) return 0; /* add the body to the 'try' */ try_stm->set_body_stm(body_stm); /* * check for 'catch' clauses - there could be several, so keep going * until we stop seeing 'catch' keywords */ while (G_tok->cur() == TOKT_CATCH) { int catch_has_err; CTPNStm *catch_body; CTPNStmCatch *catch_stm; tcprs_scope_t scope_data; /* create a local scope for the 'catch' clause */ enter_scope(&scope_data); create_scope_local_symtab(); /* create the 'catch' statement node */ catch_stm = new CTPNStmCatch(); /* * set the 'catch' clause's source position independently of the * overall 'try' statement, so that the debugger can track entry * into this clause */ catch_stm->set_source_pos(G_tok->get_last_desc(), G_tok->get_last_linenum()); /* presume we'll parse this successfully */ catch_has_err = FALSE; /* skip the 'catch' keyword and check for the left paren */ if (G_tok->next() == TOKT_LPAR) { /* skip the paren */ G_tok->next(); } else { /* log the error */ G_tok->log_error_curtok(TCERR_REQ_CATCH_LPAR); } /* get the exception class token */ if (G_tok->cur() == TOKT_SYM) { /* set the class name in the 'catch' clause */ catch_stm->set_exc_class(G_tok->getcur()); /* move on */ G_tok->next(); } else { /* flag the problem */ G_tok->log_error_curtok(TCERR_REQ_CATCH_CLASS); /* unless this is a close paren, skip the errant token */ if (G_tok->cur() != TOKT_RPAR) G_tok->next(); /* note the error */ catch_has_err = TRUE; } /* get the variable name token */ if (G_tok->cur() == TOKT_SYM) { CTcSymLocal *var; /* * create the local variable - note that this variable is * implicitly assigned when the 'catch' clause is entered, so * mark it as initially assigned; we don't care if the * variable is ever used, so also mark it as used */ var = local_symtab_->add_local(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), alloc_local(), FALSE, TRUE, TRUE); /* set the variable in the 'catch' clause */ if (!catch_has_err) catch_stm->set_exc_var(var); /* move on */ G_tok->next(); } else { /* flag the problem */ G_tok->log_error_curtok(TCERR_REQ_CATCH_VAR); /* unless this is a close paren, skip the errant token */ if (G_tok->cur() != TOKT_RPAR) G_tok->next(); /* note the error */ catch_has_err = TRUE; } /* check for the close paren */ if (G_tok->cur() == TOKT_RPAR) { /* skip the paren */ G_tok->next(); } else { /* * log the error and continue, assuming that the paren is * simply missing and things are otherwise okay */ G_tok->log_error_curtok(TCERR_REQ_CATCH_RPAR); } /* * parse the 'catch' statement block - we've already established * a special scope for the 'catch' block, so don't start a new * scope if the block contains a compound statement */ catch_body = parse_stm(err, 0, TRUE); /* leave the special 'catch' scope */ leave_scope(&scope_data); /* if the statement block failed, give up now */ if (*err) return 0; /* add the 'catch' clause to the 'try' if we were successful */ if (!catch_has_err) { /* set the 'catch' node's body */ catch_stm->set_body(catch_body); /* set the local scope in the 'catch' */ catch_stm->set_symtab(local_symtab_); /* add the 'catch' to the 'try' */ try_stm->add_catch(catch_stm); } } /* check for a 'finally' clause */ if (G_tok->cur() == TOKT_FINALLY) { CTPNStmFinally *fin_stm; CTPNStm *fin_body; tcprs_scope_t scope_data; CTPNStmEnclosing *old_enclosing; /* * the locals we allocate for the 'finally' are in the finally's * own scope - the slots can be reused later */ enter_scope(&scope_data); /* create the 'finally' node */ fin_stm = new CTPNStmFinally(enclosing_stm_, alloc_local(), alloc_local()); /* * set the 'finally' clause's source position - we want this * clause to have its own source position independent of the * 'try' statement of which it is a part, so that the debugger * can keep track of entry into this clause's generated code */ fin_stm->set_source_pos(G_tok->get_last_desc(), G_tok->get_last_linenum()); /* skip the 'finally' keyword */ G_tok->next(); /* set the 'finally' to enclose its body */ old_enclosing = set_enclosing_stm(fin_stm); /* parse the 'finally' statement block */ fin_body = parse_stm(err, 0, FALSE); /* set the 'finally' block's closing position, if present */ if (fin_body != 0) { fin_stm->set_end_pos( fin_body->get_end_desc(), fin_body->get_end_linenum()); } /* restore the enclosing statement */ set_enclosing_stm(old_enclosing); /* we're done with the special scope */ leave_scope(&scope_data); /* if that failed, give up now */ if (*err) return 0; /* set the 'finally' node's body */ fin_stm->set_body(fin_body); /* add the 'finally' to the 'try' */ try_stm->set_finally(fin_stm); } /* make sure we have at least one 'catch' or 'finally' clause */ if (!try_stm->has_catch_or_finally()) try_stm->log_error(TCERR_TRY_WITHOUT_CATCH); /* return the 'try' statement node */ return try_stm; } /* * Parse a 'throw' statement */ CTPNStm *CTcParser::parse_throw(int *err) { CTcPrsNode *expr; CTPNStmThrow *throw_stm; /* skip the 'throw' keyword */ G_tok->next(); /* parse the expression to be thrown */ expr = parse_expr(); if (expr == 0) { *err = TRUE; return 0; } /* create the statement node */ throw_stm = new CTPNStmThrow(expr); /* require a terminating semicolon */ if (parse_req_sem()) { *err = TRUE; return 0; } /* return the statement node */ return throw_stm; } /* ------------------------------------------------------------------------ */ /* * Unary operator parsing */ /* * Anonymous function symbol table preparer. This class creates the lcoal * symbol table for an anonymous function or method. */ class CAnonFuncSymtabPrep { public: /* * Create a local symbol table for an anonymous function or method. */ CTcPrsSymtab *create_symtab() { /* * Create a new local symbol table to represent the enclosing scope * of the new nested scope. Unlike most nested scopes, we can't * simply plug in the current scope's symbol table - instead, we * have to build a special representation of the enclosing scope to * handle the "closure" behavior. The enclosing scope's local * variable set effectively becomes an object rather than a simple * stack frame. The enclosing lexical scope and the anonymous * function then both reference the shared locals object. * * This synthesized enclosing scope will directly represent all of * the nested scopes up but not including to the root global scope. * The global scope doesn't need the special closure * representation, since it's already shared among all lexical * scopes anyway. Since we're not linking in to the enclosing * scope list the way we normally would, we need to explicitly link * in the global scope. To do this, we can simply make the global * scope the enclosing scope of our synthesized outer scope table. * */ enc_symtab = new CTcPrsSymtab(G_prs->get_global_symtab()); /* * fill the new local symbol table with the inherited local symbols * from the current local scope and any enclosing scopes - but stop * when we reach the global scope, since this is already shared by * all scopes and doesn't need any special closure representation */ for (CTcPrsSymtab *tab = G_prs->get_local_symtab() ; tab != 0 && tab != G_prs->get_global_symtab() ; tab = tab->get_parent()) { /* enumerate entries in this table */ tab->enum_entries(&enum_for_anon, this); } /* * Create the local symbol table for *within* the new anonymous * fucntion scope. This is another local symbol table, this time * nested within the table containing the locals shared from the * enclosing scope. This one will contain any formals defined for * the anonymous function, which hide inherited locals from the * enclosing scope. */ return new CTcPrsSymtab(enc_symtab); } /* * Finish the symbol table. Call this after parsing the code body, to * convert referenced locals in enclosing scopes to context locals. */ void finish() { /* * Enumerate all of the entries in our scope once again - this * time, we want to determine if there are any variables that were * not previously referenced from anonymous functions but have been * now; we need to convert all such variables to context locals. */ for (CTcPrsSymtab *tab = G_prs->get_local_symtab() ; tab != 0 && tab != G_prs->get_global_symtab() ; tab = tab->get_parent()) { /* enumerate entries in this table */ tab->enum_entries(&enum_for_anon2, this); } } private: /* * enumeration callback, phase 1: add a proxy context local to our * "enclosing" symbol table for each actual local defined in a parent * scope */ static void enum_for_anon(void *ctx, CTcSymbol *sym) { /* get 'this' from the context */ CAnonFuncSymtabPrep *self = (CAnonFuncSymtabPrep *)ctx; /* * If this symbol is already in our table, another symbol from an * enclosed scope hides it, so ignore this one. Note that we're * only interested in the symbols defined directly in our table - * we hide symbols defined in the enclosing global scope, so we * don't care if they're already defined there. */ if (self->enc_symtab->find_direct( sym->get_sym(), sym->get_sym_len()) != 0) return; /* create a context-variable copy */ CTcSymbol *new_sym = sym->new_ctx_var(); /* if we got a new symbol, add it to the new symbol table */ if (new_sym != 0) self->enc_symtab->add_entry(new_sym); } /* * enumeration callback, phase 2: convert locals in enclosing scopes * that are actually referenced in the anonymous function to context * locals */ static void enum_for_anon2(void *ctx, CTcSymbol *sym) { /* get 'this' from the context */ CAnonFuncSymtabPrep *self = (CAnonFuncSymtabPrep *)ctx; /* * If this symbol isn't in the anonymous function's local symbol * table, the anonymous function didn't end up using it. The fact * that it's in the enclosing table in this case simply means that * it's a context variable in the enclosing context. */ if (self->enc_symtab->find_direct( sym->get_sym(), sym->get_sym_len()) == 0) return; /* ask the symbol to apply the necessary conversion */ sym->finish_ctx_var_conv(); } /* enclosing symbol table */ CTcPrsSymtab *enc_symtab; }; /* * Parse an anonymous function */ CTPNAnonFunc *CTcPrsOpUnary::parse_anon_func(int short_form, int is_method) { /* * our code body type can be an anonymous function, a short anonymous * function, or an anonymous method */ tcprs_codebodytype cb_type = (short_form ? TCPRS_CB_SHORT_ANON_FN : G_tok->cur() == TOKT_METHOD ? TCPRS_CB_ANON_METHOD : TCPRS_CB_ANON_FN); /* skip the initial token */ G_tok->next(); /* prepare the enclosing local symbol table for the new nested scope */ CAnonFuncSymtabPrep symprep; CTcPrsSymtab *lcltab = symprep.create_symtab(); /* * Parse the code body. * * If it's a function (not a method), it shares the method context from * the lexically enclosing scope, so it has access to 'self' and other * method context variables if and only if the enclosing scope does. * If it's a method, it has access to the live method context, so the * enclosing scope's status is irrelevant. */ int err = 0; int has_retval; CTPNCodeBody *code_body = G_prs->parse_nested_code_body( FALSE, is_method || G_prs->is_self_valid(), 0, 0, 0, 0, 0, &has_retval, &err, lcltab, cb_type); /* if that failed, return failure */ if (code_body == 0 || err != 0) return 0; /* * If this is an anonymous function (not an anonymous method), and the * nested code body references 'self' or the full method context, then * so does the enclosing code body, and we need these variables in the * local context for the topmost enclosing code body. * * If this is an anonymous method, references to the method context are * to the live frame at the time of invocation, not to the saved * lexical frame at the time of creation. So references from within * the method's code body don't create references on the enclosing * scope, and they don't require any shared context information. */ if (!is_method) { if (code_body->self_referenced()) { G_prs->set_self_referenced(TRUE); G_prs->set_local_ctx_needs_self(TRUE); } if (code_body->full_method_ctx_referenced()) { G_prs->set_full_method_ctx_referenced(TRUE); G_prs->set_local_ctx_needs_full_method_ctx(TRUE); } } else { /* mark it as a method */ code_body->set_anon_method(TRUE); } /* finish the symbol table */ symprep.finish(); /* * if this is a function (not a method), and there's a 'self' object in * the enclosing context, and we referenced 'self' or the full method * context in the nested code body, we'll definitely need a local * context, so make sure we have one initialized even if we don't have * any local variables shared */ if (!is_method && G_prs->is_self_valid() && (code_body->self_referenced() || code_body->full_method_ctx_referenced())) { /* initialize a local context here, in the enclosing level */ G_prs->init_local_ctx(); } /* * add the code body to the parser's master list of nested top-level * statements for the current program */ G_prs->add_nested_stm(code_body); /* return a new anonymous function node */ return new CTPNAnonFunc(code_body, has_retval, is_method); } /* * Parse an in-line object definition. */ class CTPNInlineObject *CTcPrsOpUnary::parse_inline_object(int has_colon) { /* create the inline object node */ CTPNInlineObject *obj = new CTPNInlineObject(); /* if the caller didn't already check for a colon, do so now */ if (!has_colon && G_tok->next() == TOKT_COLON) { /* note and skip the colon */ has_colon = TRUE; G_tok->next(); } /* if there's a colon, parse the superclass list */ if (has_colon) G_prs->parse_superclass_list(0, obj->get_superclass_list()); /* next we need a left brace '{' */ if (G_tok->cur() != TOKT_LBRACE) { G_tok->log_error_curtok(TCERR_INLINE_OBJ_REQ_LBRACE); return 0; } /* skip the brace */ G_tok->next(); /* parse the property list */ int err = FALSE; tcprs_term_info outer_term; if (!G_prs->parse_obj_prop_list( &err, obj, 0, FALSE, FALSE, TRUE, TRUE, &outer_term, &outer_term)) return 0; /* return the object node */ return obj; } /* ------------------------------------------------------------------------ */ /* * State save structure for parsing property expressions */ class CTcPrsPropExprSave { public: unsigned int has_local_ctx_ : 1; int local_ctx_var_num_; size_t ctx_var_props_used_; int next_ctx_arr_idx_; int local_cnt_; int max_local_cnt_; int self_referenced_; int self_valid_; int full_method_ctx_referenced_; int local_ctx_needs_self_; int local_ctx_needs_full_method_ctx_; struct CTcCodeBodyRef *cur_code_body_; CTPNStmEnclosing *enclosing_stm_; class CTcPrsSymtab *local_symtab_; class CTcPrsSymtab *enclosing_local_symtab_; class CTcPrsSymtab *goto_symtab_; CAnonFuncSymtabPrep inline_prep_; }; /* ------------------------------------------------------------------------ */ /* * Parse an object superclass list */ void CTcParser::parse_superclass_list( CTcSymObj *obj_sym, CTPNSuperclassList &sclist) { /* scan the list */ for (int done = FALSE ; !done ; ) { /* we need a symbol */ switch(G_tok->cur()) { case TOKT_SYM: /* a symbol must be a superclass name - look it up */ { const CTcToken *tok = G_tok->getcur(); CTcSymObj *sc_sym = (CTcSymObj *)get_global_symtab()->find( tok->get_text(), tok->get_text_len()); /* * If this symbol is defined, and it's an object, check to * make sure this won't set up a circular class definition * - so, make sure the base class isn't the same as the * object being defined, and that it doesn't inherit from * the object being defined. */ if (sc_sym != 0 && obj_sym != 0 && sc_sym->get_type() == TC_SYM_OBJ && (sc_sym == obj_sym || sc_sym->has_superclass(obj_sym))) { /* * this is a circular class definition - complain about * it and don't add it to my superclass list */ G_tok->log_error(TCERR_CIRCULAR_CLASS_DEF, (int)sc_sym->get_sym_len(), sc_sym->get_sym(), (int)obj_sym->get_sym_len(), obj_sym->get_sym()); } else { /* it's good - add the new superclass to our list */ sclist.append(new CTPNSuperclass( tok->get_text(), tok->get_text_len())); /* * add it to the symbol's superclass name list as well * - we use this for keeping track of the hierarchy in * the symbol file for compile-time access */ if (obj_sym != 0) obj_sym->add_sc_name_entry( tok->get_text(), tok->get_text_len()); } /* skip the symbol and see what follows */ switch (G_tok->next()) { case TOKT_COMMA: /* we have another superclass following */ G_tok->next(); break; default: /* no more superclasses */ done = TRUE; break; } } break; case TOKT_OBJECT: /* * it's a basic object definition - make sure other * superclasses weren't specified */ if (sclist.head_ != 0) G_tok->log_error(TCERR_OBJDEF_OBJ_NO_SC); /* * mark the object as having an explicit superclass of the root * object class */ if (obj_sym != 0) obj_sym->set_sc_is_root(TRUE); /* * skip the 'object' keyword and we're done - there's no * superclass list */ G_tok->next(); done = TRUE; break; default: /* premature end of the object list */ G_tok->log_error_curtok(TCERR_OBJDEF_REQ_SC); /* stop here */ done = TRUE; break; } } } /* * Parse an object template instance at the beginning of an object body */ void CTcParser::parse_obj_template(int *err, CTPNObjDef *objdef, int is_inline) { /* check the current token for a template use */ switch(G_tok->cur()) { case TOKT_SSTR: case TOKT_SSTR_START: case TOKT_DSTR: case TOKT_DSTR_START: case TOKT_LBRACK: case TOKT_AT: case TOKT_PLUS: case TOKT_MINUS: case TOKT_TIMES: case TOKT_DIV: case TOKT_MOD: case TOKT_ARROW: case TOKT_AND: case TOKT_NOT: case TOKT_BNOT: case TOKT_COMMA: /* we have an object template */ break; default: /* it's not a template - simply return without parsing anything */ return; } /* parse the expressions until we reach the end of the template */ size_t cnt; CTcObjTemplateInst *p; int done; CTcPrsPropExprSave save_info; for (cnt = 0, p = template_expr_, done = FALSE ; !done ; ++cnt) { /* * remember the statment start location, in case we have a * template element that generates code (such as a double-quoted * string with an embedded expression) */ cur_desc_ = G_tok->get_last_desc(); cur_linenum_ = G_tok->get_last_linenum(); /* * note the token, so that we can figure out which template we * are using */ p->def_tok_ = G_tok->cur(); /* assume this will also be the first token of the value expression */ p->expr_tok_ = *G_tok->copycur(); /* we don't have any expression yet */ p->expr_ = 0; p->code_body_ = 0; p->inline_method_ = 0; /* prepare to parse a property value expression */ begin_prop_expr(&save_info, FALSE, is_inline); /* check to see if this is another template item */ switch(G_tok->cur()) { case TOKT_SSTR: /* single-quoted string - parse just the string */ p->expr_ = CTcPrsOpUnary::parse_primary(); break; case TOKT_SSTR_START: /* start of single-quoted embedded expression string - parse it */ p->expr_ = CTcPrsOpUnary::parse_primary(); /* treat it as a regular string for template matching */ p->def_tok_ = TOKT_SSTR; break; case TOKT_DSTR: /* string - parse it */ p->expr_ = parse_expr_or_dstr(TRUE); break; case TOKT_DSTR_START: /* start of a double-quoted embedded expression string */ p->expr_ = parse_expr_or_dstr(TRUE); /* * treat it as a regular double-quoted string for the * purposes of matching the template */ p->def_tok_ = TOKT_DSTR; break; case TOKT_LBRACK: /* it's a list */ p->expr_ = CTcPrsOpUnary::parse_list(); break; case TOKT_AT: case TOKT_PLUS: case TOKT_MINUS: case TOKT_TIMES: case TOKT_DIV: case TOKT_MOD: case TOKT_ARROW: case TOKT_AND: case TOKT_NOT: case TOKT_BNOT: case TOKT_COMMA: /* skip the operator token */ G_tok->next(); /* the value expression starts with this token */ p->expr_tok_ = *G_tok->copycur(); /* a primary expression must follow */ p->expr_ = CTcPrsOpUnary::parse_primary(); break; case TOKT_EOF: /* end of file - return and let the caller deal with it */ return; default: /* anything else ends the template list */ done = TRUE; /* don't count this item after all */ --cnt; break; } /* * check for embedded anonymous functions, and wrap the expression * in a code body if necessary */ finish_prop_expr( &save_info, p->expr_, p->code_body_, p->inline_method_, FALSE, is_inline, 0); /* * move on to the next expression slot if we have room (if we * don't, we won't match anything anyway; just keep writing over * the last slot so that we can at least keep parsing entries) */ if (cnt + 1 < template_expr_max_) ++p; } /* we have no matching template yet */ const CTcObjTemplate *tpl = 0; const CTPNSuperclass *def_sc = 0; /* presume we don't have any undescribed classes in our hierarchy */ int undesc_class = FALSE; /* * Search for the template, using the normal inheritance rules that we * use at run-time: start with the first superclass and look for a * match; if we find a match, look at subsequent superclasses to look * for one that overrides the match. */ if (objdef != 0) { /* search our superclasses for a match */ tpl = find_class_template( objdef->get_first_sc(), template_expr_, cnt, &def_sc, &undesc_class); /* remember the 'undescribed class' status */ objdef->set_undesc_sc(undesc_class); } /* if we didn't find a match, look for a root object match */ if (tpl == 0 && !undesc_class) tpl = find_template_match(template_head_, template_expr_, cnt); /* if we didn't find a match, it's an error */ if (tpl == 0) { /* * Note the error, but don't report it yet. It might be that we * failed to find a template match simply because one of our * superclass names was misspelled. If that's the case, then the * missing template is the least of our problems, and it's not * worth reporting since it's probably just a side effect of the * missing superclass (that is, once the superclass misspelling is * corrected and the code is re-compiled, we might find that the * template is correct after all, since we'll know which class to * scan for the needed template.) At code generation time, we'll * be able to resolve the superclasses and find out what's really * going on, so that we can flag the appropriate error. */ if (objdef != 0) objdef->note_bad_template(TRUE); /* ignore the template instance */ return; } /* if there's no object statement, there's nothing left to do */ if (objdef == 0) return; /* * we know we have a matching template, so populate our actual * parameter list with the property identifiers for the matching * template */ match_template(tpl->items_, template_expr_, cnt); /* define the property values according to the template */ for (p = template_expr_ ; cnt != 0 ; ++p, --cnt) { /* add this property */ if (p->code_body_ != 0) objdef->add_method(p->prop_, p->code_body_, p->expr_, FALSE); else if (p->inline_method_ != 0) objdef->add_inline_method( p->prop_, p->inline_method_, p->expr_, FALSE); else if (p->expr_ != 0) objdef->add_prop(p->prop_, p->expr_, FALSE, FALSE); } } /* ------------------------------------------------------------------------ */ /* * search a class for a template match */ const CTcObjTemplate *CTcParser:: find_class_template(const CTPNSuperclass *first_sc, CTcObjTemplateInst *src, size_t src_cnt, const CTPNSuperclass **def_sc, int *undesc_class) { /* scan each superclass in the list for a match */ const CTPNSuperclass *sc; const CTcObjTemplate *tpl; for (tpl = 0, sc = first_sc ; sc != 0 ; sc = sc->nxt_) { /* find the symbol for this superclass */ CTcSymObj *sc_sym = (CTcSymObj *)get_global_symtab()->find( sc->get_sym_txt(), sc->get_sym_len()); /* if there's no symbol, or it's not a tads-object, give up */ if (sc_sym == 0 || sc_sym->get_type() != TC_SYM_OBJ || sc_sym->get_metaclass() != TC_META_TADSOBJ) { /* * this class has an invalid superclass - just give up without * issuing any errors now, since we'll have plenty to say * about this when building the object file data */ return 0; } /* find a match in this superclass hierarchy */ const CTcObjTemplate *cur_tpl = find_template_match( sc_sym->get_first_template(), src, src_cnt); /* see what we found */ const CTPNSuperclass *cur_def_sc; if (cur_tpl != 0) { /* we found it - note the current defining superclass */ cur_def_sc = sc; } else { /* * If this one has no superclass list, and it's not explicitly * a subclass of the root class, then this is an undescribed * class and cannot be used with templates at all. A class is * undescribed when it is explicitly declared as 'extern', and * does not have a definition in any imported symbol file in * the current compilation. If this is the case, flag it so * the caller will know we have an undescribed class. * * Note that we only set this flag if we failed to find a * template. A template can still be used if a matching * template is explicitly defined on the class in this * compilation unit, since in that case we don't need to look * up the inheritance hierarchy for the class. That's why we * set the flag here, only after we have failed to find a * template for the object. */ if (sc_sym->get_sc_name_head() == 0 && !sc_sym->sc_is_root()) { /* tell the caller we have an undescribed class */ *undesc_class = TRUE; /* * there's no need to look any further, since any matches * we might find among our other superclasses would be in * doubt because of the lack of information about this * earlier class, which might override later superclasses * if we knew more about it */ return 0; } /* we didn't find it - search superclasses of this class */ cur_tpl = find_class_template(sc_sym->get_sc_name_head(), src, src_cnt, &cur_def_sc, undesc_class); /* * if we have an undescribed class among our superclasses, * we're implicitly undescribed as well - if that's the case, * there's no need to look any further, so return failure */ if (*undesc_class) return 0; } /* if we found a match, see if we want to keep it */ if (cur_tpl != 0) { /* * if this is our first match, note it; if it's not, see if it * overrides the previous match */ if (tpl == 0) { /* this is the first match - definitely keep it */ tpl = cur_tpl; *def_sc = cur_def_sc; } else { /* * if the current source object descends from the previous * source object, this definition overrides the previous * definition, so keep it rather than the last one */ if (cur_def_sc->is_subclass_of(*def_sc)) { /* it overrides the previous one - keep the new one */ tpl = cur_tpl; *def_sc = cur_def_sc; } } } } /* return the best match we found */ return tpl; } /* ------------------------------------------------------------------------ */ /* * Find a matching template in the given template list */ const CTcObjTemplate *CTcParser:: find_template_match(const CTcObjTemplate *first_tpl, CTcObjTemplateInst *src, size_t src_cnt) { /* find the matching template */ for (const CTcObjTemplate *tpl = first_tpl ; tpl != 0 ; tpl = tpl->nxt_) { /* check for a match */ if (match_template(tpl->items_, src, src_cnt)) { /* it's a match - return this template */ return tpl; } } /* we didn't find a match */ return 0; } /* ------------------------------------------------------------------------ */ /* * Match a template to an actual template parameter list. */ int CTcParser::match_template(const CTcObjTemplateItem *tpl_head, CTcObjTemplateInst *src, size_t src_cnt) { /* check each element of the list */ CTcObjTemplateInst *p; const CTcObjTemplateItem *item; size_t rem; for (p = src, rem = src_cnt, item = tpl_head ; item != 0 && rem != 0 ; item = item->nxt_) { /* * Note whether or not this item is optional. Every element of an * alternative group must have the same optional status, so we need * only note the status of the first item if this is a group. */ int is_opt = item->is_opt_; /* * Scan each alternative in the current group. Note that if we're * not in an alternative group, the logic is the same: we won't * have any 'alt' flags, so we'll just scan a single item. */ int match; for (match = FALSE ; ; item = item->nxt_) { /* if this one matches, note the match */ if (item->tok_type_ == p->def_tok_) { /* note the match */ match = TRUE; /* this is the property to assign for the actual */ p->prop_ = item->prop_; } /* * If this one is not marked as an alternative, we're done. * The last item of an alternative group is identified by * having its 'alt' flag cleared. Also, if we somehow have an * ill-formed list, where we don't have a terminating * non-flagged item, we can stop now as well. */ if (!item->is_alt_ || item->nxt_ == 0) break; } /* check to see if the current item is optional */ if (is_opt) { /* * The item is optional. If it matches, try it both ways: * first try matching the item, then try skipping it. If we * can match this item and still match the rest of the string, * take that interpretation; otherwise, if we can skip this * item and match the rest of the string, take *that* * interpretation. If we can't match it either way, we don't * have a match. * * First, check to see if we can match the item and still match * the rest of the string. */ if (match && match_template(item->nxt_, p + 1, rem - 1)) { /* we have a match */ return TRUE; } /* * Matching this optional item doesn't let us match the rest of * the string, so try it with this optional item omitted - in * other words, just match the rest of the string, including * the current source item, to the rest of the template, * *excluding* the current optional item. * * There's no need to recurse to do this; simply continue * iterating, but do NOT skip the current source item. */ } else { /* * It's not optional, so if it doesn't match, the whole * template fails to match; if it does match, simply proceed * through the rest of the template. */ if (!match) return FALSE; /* we matched, so consume this source item */ ++p; --rem; } } /* skip any trailing optional items in the template list */ while (item != 0 && item->is_opt_) item = item->nxt_; /* * it's a match if and only if we reached the end of both lists at the * same time */ return (item == 0 && rem == 0); } /* ------------------------------------------------------------------------ */ /* * Begin a property expression */ void CTcParser::begin_prop_expr( CTcPrsPropExprSave *save_info, int is_static, int is_inline) { /* save the current parser state */ save_info->has_local_ctx_ = has_local_ctx_; save_info->local_ctx_var_num_ = local_ctx_var_num_; save_info->ctx_var_props_used_ = ctx_var_props_used_; save_info->next_ctx_arr_idx_ = next_ctx_arr_idx_; save_info->local_cnt_ = local_cnt_; save_info->max_local_cnt_ = max_local_cnt_; save_info->self_valid_ = self_valid_; save_info->self_referenced_ = self_referenced_; save_info->cur_code_body_ = cur_code_body_; save_info->full_method_ctx_referenced_ = full_method_ctx_referenced_; save_info->local_ctx_needs_self_ = local_ctx_needs_self_; save_info->local_ctx_needs_full_method_ctx_ = local_ctx_needs_full_method_ctx_; save_info->enclosing_stm_ = enclosing_stm_; save_info->enclosing_local_symtab_ = enclosing_local_symtab_; save_info->local_symtab_ = local_symtab_; save_info->goto_symtab_ = goto_symtab_; /* * we've saved the local context information, so clear it out for the * next parse job */ clear_local_ctx(); /* set up the local symbol table for the property expression */ if (is_inline) { /* * It's an inline object definition. For a normal (non-static) * property, the expression is treated as though it were wrapped in * an anonymous method, so that it can access the enclosing scope's * locals. We need to set up the usual anonymous function proxy * symbol table to allow access to the enclosing scope. For a * static property, the property expression is evaluated * immediately when we evaluate the overall inline object * expression. Static evaluation is also done in the context of * the enclosing scope, but since it happens during the enclosing * scope's lifetime, we don't have to worry about setting up a * proxy symbol table; we can simply access the locals directly * while they're still alive, so for the static case we just leave * the stack context exactly like it already is. */ if (!is_static) { /* * ordinary non-static expression - we need an anonymous * function context so that the expression can access the * enclosing scope's locals even after the enclosing scope * returns to its caller */ local_symtab_ = save_info->inline_prep_.create_symtab(); if ((enclosing_local_symtab_ = local_symtab_->get_parent()) == 0) enclosing_local_symtab_ = global_symtab_; self_valid_ = TRUE; } } else { /* * This is a regular top-level object definition, so there's no * enclosing scope. Clear out the code body and the local symbol * table so that we parse in global context. */ cur_code_body_ = 0; local_symtab_ = global_symtab_; enclosing_local_symtab_ = global_symtab_; } /* start a new parsing context */ enclosing_stm_ = 0; goto_symtab_ = 0; /* no locals yet */ local_cnt_ = 0; max_local_cnt_ = 0; } /* * Finish a property expression, checking for anonymous functions and * wrapping the expression in a code body if necesssary. If the expression * contains an anonymous function which needs to share context with its * enclosing scope, we need to build the code body wrapper immediately so * that we can capture the context information, which is stored in the * parser object itself (i.e,. 'this'). */ void CTcParser::finish_prop_expr( CTcPrsPropExprSave *save_info, CTcPrsNode* &expr, CTPNCodeBody* &cb, CTPNAnonFunc* &inline_method, int is_static, int is_inline, CTcSymProp *prop_sym) { /* presume we won't need to create a code body or inline method */ cb = 0; inline_method = 0; /* * If we have a local context, we have to set up a code body in order * to initialize the local context at run-time. If this is an inline * object definition, and it's not a static property, we need to turn * the expression into an anonymous method, so we also need to wrap it * in a code body. */ if (expr != 0 && (has_local_ctx_ || (is_inline && !is_static))) { /* * We need to wrap the expression in a code body to make the * property into a method. First, wrap the expression in an * appropriate statement so that we can put it into a code body. */ CTPNStm *stm; if (is_static) { /* * it's a static initializer - wrap it in a static initializer * statement node */ stm = new CTPNStmStaticPropInit(expr, prop_sym->get_prop()); } else if (expr->has_return_value()) { /* normal property value - wrap it in a 'return' */ stm = new CTPNStmReturn(expr); } else { /* * it's an expression that yields no value, such as a * double-quoted string expression or a call to a void * function; just use the expression itself */ stm = new CTPNStmExpr(expr); } /* wrap the expression statement in a code body */ cb = new CTPNCodeBody( local_symtab_, goto_symtab_, stm, 0, 0, FALSE, FALSE, 0, local_cnt_, self_valid_, save_info->cur_code_body_); /* set up the local context for access to enclosing scope locals */ finish_local_ctx(cb, local_symtab_); /* mark the code body for references to the method context */ cb->set_self_referenced(self_referenced_); cb->set_full_method_ctx_referenced(full_method_ctx_referenced_); /* mark the code body for inclusion in any local context */ cb->set_local_ctx_needs_self(local_ctx_needs_self_); cb->set_local_ctx_needs_full_method_ctx( local_ctx_needs_full_method_ctx_); /* * if this is a non-static property in an inline object * definition, turn the expression into an anonymous method */ if (is_inline && !is_static) { /* mark it as an anonymous method */ cb->set_anon_method(TRUE); /* add the code body to the master list of nested functions */ add_nested_stm(cb); /* wrap the code body in an anonymous method */ inline_method = new CTPNAnonFunc( cb, expr->has_return_value(), TRUE); /* the anonymous method supersedes the code body */ cb = 0; } } /* restore the saved parser state */ has_local_ctx_ = save_info->has_local_ctx_; local_ctx_var_num_ = save_info->local_ctx_var_num_; ctx_var_props_used_ = save_info->ctx_var_props_used_; next_ctx_arr_idx_ = save_info->next_ctx_arr_idx_; local_cnt_ = save_info->local_cnt_; max_local_cnt_ = save_info->max_local_cnt_; cur_code_body_ = save_info->cur_code_body_; self_valid_ = save_info->self_valid_; self_referenced_ = save_info->self_referenced_; full_method_ctx_referenced_ = save_info->full_method_ctx_referenced_; local_ctx_needs_self_ = save_info->local_ctx_needs_self_; local_ctx_needs_full_method_ctx_ = save_info->local_ctx_needs_full_method_ctx_; enclosing_stm_ = save_info->enclosing_stm_; enclosing_local_symtab_ = save_info->enclosing_local_symtab_; local_symtab_ = save_info->local_symtab_; goto_symtab_ = save_info->goto_symtab_; /* * if this is a non-static property of an inline object, finish the * anonymous method proxy symbol table for enclosing scopes */ if (is_inline && !is_static) save_info->inline_prep_.finish(); } /* ------------------------------------------------------------------------ */ /* * property set token source */ void propset_token_source::insert_token(const CTcToken *tok) { /* create a new link entry and initialize it */ propset_tok *cur = new (G_prsmem) propset_tok(tok); /* link it into our list */ if (last_tok != 0) last_tok->nxt = cur; else nxt_tok = cur; last_tok = cur; } void propset_token_source::insert_token( tc_toktyp_t typ, const char *txt, size_t len) { /* set up the token object */ CTcToken tok; tok.settyp(typ); tok.set_text(txt, len); /* insert it */ insert_token(&tok); } /* ------------------------------------------------------------------------ */ /* * Set up the inserted token stream for a propertyset invocation */ void CTcParser::insert_propset_expansion(struct propset_def *propset_stack, int propset_depth) { /* * First, determine if we have any added formals from propertyset * definitions. */ int i; int formals_found; for (formals_found = FALSE, i = 0 ; i < propset_depth ; ++i) { /* if this one has formals, so note */ if (propset_stack[i].param_tok_head != 0) { /* note it, and we need not look further */ formals_found = TRUE; break; } } /* * If we found formals from property sets, we must expand them into the * token stream. */ if (formals_found) { /* insert an open paren at the start of the expansion list */ propset_token_source tok_src; tok_src.insert_token(TOKT_LPAR, "(", 1); /* we don't yet need a leading comma */ int need_comma = FALSE; /* * Add the tokens from each propertyset in the stack, from the * outside in, until we reach an asterisk in each stack. */ for (i = 0 ; i < propset_depth ; ++i) { propset_tok *cur; /* add the tokens from the stack element */ for (cur = propset_stack[i].param_tok_head ; cur != 0 ; cur = cur->nxt) { /* * If we need a comma before the next real element, add it * now. */ if (need_comma) { tok_src.insert_token(TOKT_COMMA, ",", 1); need_comma = FALSE; } /* * If this is a comma and the next item is the '*', omit * the comma - if we have nothing more following, we want * to suppress the comma. */ if (cur->tok.gettyp() == TOKT_COMMA && cur->nxt != 0 && cur->nxt->tok.gettyp() == TOKT_TIMES) { /* * it's the comma before the star - simply stop here * for this list, but note that we need a comma before * any additional formals that we add in the future */ need_comma = TRUE; break; } /* * if it's the '*' for this list, stop here, since we want * to insert the next level in before we add these tokens */ if (cur->tok.gettyp() == TOKT_TIMES) break; /* insert it into our expansion list */ tok_src.insert_token(&cur->tok); } } /* * If we have explicit formals in the true input stream, add them, * up to but not including the close paren. */ if (G_tok->cur() == TOKT_LPAR) { /* skip the open paren */ G_tok->next(); /* check for a non-empty list */ if (G_tok->cur() != TOKT_RPAR) { /* * the list is non-empty - if we need a comma, add it now */ if (need_comma) tok_src.insert_token(TOKT_COMMA, ",", 1); /* we will need a comma at the end of this list */ need_comma = TRUE; } /* * copy everything up to but not including the close paren to * the expansion list */ while (G_tok->cur() != TOKT_RPAR && G_tok->cur() != TOKT_EOF) { /* insert this token into our expansion list */ tok_src.insert_token(G_tok->getcur()); /* skip it */ G_tok->next(); } /* skip the closing paren */ if (G_tok->cur() == TOKT_RPAR) G_tok->next(); } /* * Finish the expansion by adding the parts of each propertyset * list after the '*' to the expansion list. Copy from the inside * out, since we want to unwind the nesting from outside in that we * did to start with. */ for (i = propset_depth ; i != 0 ; ) { /* move down to the next level */ --i; /* find the '*' in this list */ propset_tok *cur; for (cur = propset_stack[i].param_tok_head ; cur != 0 && cur->tok.gettyp() != TOKT_TIMES ; cur = cur->nxt) ; /* if we found the '*', skip it */ if (cur != 0) cur = cur->nxt; /* * also skip the comma following the '*', if present - we'll * explicitly insert the needed extra comma if we actually find * we need one */ if (cur != 0 && cur->tok.gettyp() == TOKT_COMMA) cur = cur->nxt; /* insert the remainder of the list into the expansion list */ for ( ; cur != 0 ; cur = cur->nxt) { /* if we need a comma, add it now */ if (need_comma) { tok_src.insert_token(TOKT_COMMA, ",", 1); need_comma = FALSE; } /* insert this token */ tok_src.insert_token(&cur->tok); } } /* add the closing paren at the end of the expansion list */ tok_src.insert_token(TOKT_RPAR, ")", 1); /* * We've fully expanded the formal list. Now all that remains is * to insert the expanded token list into the token input stream, * so that we read from the expanded list instead of from the * original token stream. */ G_tok->set_external_source(&tok_src); } } /* ------------------------------------------------------------------------ */ /* * Parse an object definition's property list */ int CTcParser::parse_obj_prop_list( int *err, CTPNObjDef *objdef, CTcSymMetaclass *meta_sym, int modify, int is_nested, int braces, int is_inline, tcprs_term_info *outer_term_info, tcprs_term_info *term_info) { /* we aren't in a propertyset definition yet */ propset_def propset_stack[MAX_PROPSET_DEPTH]; int propset_depth = 0; /* * Parse the property list. Keep going until we get to the closing * semicolon. */ for (int done = FALSE ; !done ; ) { /* * Remember the statement start location. Some types of * property definitions result in implicit statements being * created, so we must record the statement start position for * those cases. */ cur_desc_ = G_tok->get_last_desc(); cur_linenum_ = G_tok->get_last_linenum(); /* presume no 'replace' on this property */ int replace = FALSE; /* parse the property definition */ switch(G_tok->cur()) { case TOKT_PROPERTYSET: { propset_def *cur_def; propset_def dummy_def; /* * It's a property set definition. These definitions * nest; make sure we have space left in our stack. */ if (propset_depth >= MAX_PROPSET_DEPTH) { /* * nested too deeply - flag the error if this is the * one that pushes us over the edge */ if (propset_depth == MAX_PROPSET_DEPTH) G_tok->log_error(TCERR_PROPSET_TOO_DEEP); /* * keep going with a syntactic parse despite the * problems - increment the current depth, but use a * dummy definition object, since we have no more * stack entries to store the real data */ ++propset_depth; cur_def = &dummy_def; } else { /* set up to use the next available stack entry */ cur_def = &propset_stack[propset_depth]; /* consume this stack entry */ ++propset_depth; } /* skip the keyword and get the property name pattern */ if (G_tok->next() == TOKT_SSTR) { /* note the pattern string */ cur_def->prop_pattern = G_tok->getcur()->get_text(); cur_def->prop_pattern_len = G_tok->getcur()->get_text_len(); /* * Validate the pattern. It must consist of valid * token characters, except for a single asterisk, * which it must have. */ int star_cnt = 0, inval_cnt = 0; utf8_ptr p((char *)cur_def->prop_pattern); size_t rem = cur_def->prop_pattern_len; for ( ; rem != 0 ; p.inc(&rem)) { /* check this character */ if (p.getch() == '*') ++star_cnt; else if (!is_sym(p.getch())) ++inval_cnt; } /* * if the first character isn't an asterisk, it must * be a valid initial symbol character */ if (cur_def->prop_pattern_len != 0) { /* get the first character */ wchar_t firstch = utf8_ptr::s_getch( cur_def->prop_pattern); /* * if it's not a '*', it must be a valid initial * token character */ if (firstch != '*' && !is_syminit(firstch)) ++inval_cnt; } /* * if it has no '*' or more than one '*', or it has * invalid characters, flag it as invalid */ if (star_cnt != 1 || inval_cnt != 0) G_tok->log_error_curtok(TCERR_PROPSET_INVAL_PAT); } else { /* not a string; flag an error, but try to keep going */ G_tok->log_error_curtok(TCERR_PROPSET_REQ_STR); /* we have no pattern string */ cur_def->prop_pattern = ""; cur_def->prop_pattern_len = 0; } /* we have no parameter token list yet */ cur_def->param_tok_head = 0; /* * skip the pattern string, and see if we have an argument * list */ if (G_tok->next() == TOKT_LPAR) { /* * Current parsing state: 0=start, 1=after symbol or * '*', 2=after comma, 3=done */ int state = 0; /* number of '*' tokens */ int star_cnt = 0; /* start with an empty token list */ propset_tok *last = 0; /* * We have a formal parameter list that is to be * applied to each property in the set. Parse tokens * up to the closing paren, stashing them in our list. */ for (G_tok->next() ; state != 3 ; ) { /* check the token */ switch (G_tok->cur()) { case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_SEM: /* * A brace or semicolon - assume that the right * paren was accidentally omitted. Flag the * error and consider the list to be finished. */ G_tok->log_error_curtok( TCERR_MISSING_RPAR_FORMAL); /* switch to 'done' state */ state = 3; break; case TOKT_RPAR: /* * Right paren. This ends the list. If we're * in state 2 ('after comma'), the paren is out * of place, so flag it as an error - but still * count it as ending the list. */ if (state == 2) G_tok->log_error(TCERR_MISSING_LAST_FORMAL); /* skip the paren */ G_tok->next(); /* switch to state 'done' */ state = 3; break; case TOKT_SYM: case TOKT_TIMES: /* * Formal parameter name or '*'. We can accept * these in states 'start' and 'after comma' - * in those states, just go add the token to * the list we're gathering. In state 'after * formal', this is an error - a comma should * have come between the two parameters. */ if (state == 1) { /* * 'after formal' - a comma must be * missing. Flag the error, but then * proceed as though the comma had been * there. */ G_tok->log_error_curtok( TCERR_REQ_COMMA_FORMAL); } /* switch to state 'after formal' */ state = 1; add_to_list: { /* create a list entry for the current token */ propset_tok *cur = new (G_prsmem) propset_tok( G_tok->getcur()); /* add it to the token list */ if (last != 0) last->nxt = cur; else cur_def->param_tok_head = cur; last = cur; /* if it's a '*', count it */ if (G_tok->cur() == TOKT_TIMES) ++star_cnt; /* skip it */ G_tok->next(); } break; case TOKT_COMMA: /* * In state 'after formal', a comma just goes * into our token list. A comma is invalid in * any other state. */ if (state == 1) { /* switch to state 'after comma' */ state = 2; /* go add the token to our list */ goto add_to_list; } else { /* otherwise, go handle as a bad token */ goto bad_token; } default: bad_token: /* generate an error according to our state */ switch (state) { case 0: case 2: /* * 'start' or 'after comma' - we expected a * formal name */ G_tok->log_error_curtok(TCERR_REQ_SYM_FORMAL); break; case 1: /* 'after formal' - expected a comma */ G_tok->log_error_curtok( TCERR_REQ_COMMA_FORMAL); break; } /* skip the token and keep going */ G_tok->next(); break; } } /* make sure we have exactly one star */ if (star_cnt != 1) G_tok->log_error(TCERR_PROPSET_INVAL_FORMALS); } /* require the open brace for the set */ if (G_tok->cur() == TOKT_LBRACE) G_tok->next(); else G_tok->log_error_curtok(TCERR_PROPSET_REQ_LBRACE); } /* proceed to parse the properties within */ break; case TOKT_SEM: /* * If we're using brace notation (i.e., the property list is * surrounded by braces), a semicolon inside the property list * is ignored. If we're using regular notation, a semicolon * ends the property list. */ if (braces || propset_depth != 0) { /* * we're using brace notation, or we're inside the braces * of a propertyset, so semicolons are ignored */ } else { /* * we're using regular notation, so this semicolon * terminates the property list and the object body - note * that we're done parsing the object body */ done = TRUE; } /* in any case, skip the semicolon and carry on */ G_tok->next(); break; case TOKT_CLASS: case TOKT_EXTERN: case TOKT_MODIFY: case TOKT_DICTIONARY: case TOKT_PROPERTY: case TOKT_PLUS: case TOKT_INC: case TOKT_INTRINSIC: case TOKT_OBJECT: case TOKT_GRAMMAR: case TOKT_ENUM: /* * All of these probably indicate the start of a new * statement - they probably just left off the closing * semicolon of the current object. Flag the error and * return, on the assumption that we'll find a new statement * starting here. */ G_tok->log_error_curtok(braces || propset_depth != 0 ? TCERR_OBJ_DEF_REQ_RBRACE : TCERR_OBJ_DEF_REQ_SEM); done = TRUE; break; case TOKT_SYM: /* * Property definition. For better recovery from certain * common errors, check a couple of scenarios. * * Look ahead one token. If the next token is a colon, then * we have a nested object definition (the property is * assigned to a reference to an anonymous object about to be * defined in-line). This could also indicate a missing * semicolon (or close brace) immediately before the symbol * we're taking as a property name - the symbol we just parsed * could actually be intended as the next object's name, and * the colon introduces its superclass list. If the new * object's property list isn't enclosed in braces, this is * probably what happened. * * If the next token is '(', '{', or '=', it's almost * certainly a property definition, so proceed on that * assumption. * * If the next token is a symbol, we have one of two probable * errors. Either we have a missing '=' in a property * definition, or this is a new anonymous object definition, * and the current object definition is missing its * terminating semicolon. If the current symbol is a class * name, it's almost certainly a new object definition; * otherwise, assume it's really a property definition, and * we're just missing the '='. */ /* look ahead at the next token */ switch(G_tok->next()) { case TOKT_LPAR: case TOKT_LBRACE: case TOKT_EQ: /* * It's almost certainly a property definition, no * matter what the current symbol looks like. Put back * the token and proceed. */ G_tok->unget(); break; case TOKT_COLON: /* * "symbol: " - a nested object definition, or what's * meant to be a new object definition, with the * terminating semicolon or brace of the current object * missing. Assume for now it's correct, in which case * it's a nested object definition; put back the token and * proceed. */ G_tok->unget(); break; case TOKT_SYM: case TOKT_AT: case TOKT_PLUS: case TOKT_MINUS: case TOKT_TIMES: case TOKT_DIV: case TOKT_MOD: case TOKT_ARROW: case TOKT_AND: case TOKT_NOT: case TOKT_BNOT: case TOKT_COMMA: { /* * Two symbols in a row, or a symbol followed by * what might be a template operator - either an * equals sign was left out, or this is a new object * definition. Put back the token and check what * kind of symbol we had in the first place. */ G_tok->unget(); CTcSymbol *sym = global_symtab_->find( G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* * if it's an object symbol, we almost certainly * have a new object definition */ if (sym != 0 && sym->get_type() == TC_SYM_OBJ) { /* log the error */ G_tok->log_error_curtok( braces || propset_depth != 0 ? TCERR_OBJ_DEF_REQ_RBRACE : TCERR_OBJ_DEF_REQ_SEM); /* assume the object is finished */ done = TRUE; } } break; default: /* * anything else is probably an error, but it's hard to * guess what the error is; proceed on the assumption * that it's going to be a property definition, and let * the property definition parser take care of analyzing * the problem */ G_tok->unget(); break; } /* * if we decided that the object is finished, cancel the * property definition parsing */ if (done) break; parse_normal_prop: /* * initialize my termination information object with the * current location (note that we don't necessarily initialize * the current termination info, since only the outermost one * matters; if ours happens to be active, it's because it's * the outermost) */ outer_term_info->init(G_tok->get_last_desc(), G_tok->get_last_linenum()); /* go parse the property definition */ parse_obj_prop( err, objdef, replace, meta_sym, term_info, propset_stack, propset_depth, is_nested, is_inline); /* if we ran into an unrecoverable error, give up */ if (*err) return FALSE; /* * if we encountered a termination error, assume the current * object was intended to be terminated, so stop here */ if (term_info->unterm_) { /* * we found what looks like the end of the object within * the property parser - assume we're done with this * object */ done = TRUE; } /* done */ break; case TOKT_OPERATOR: /* * Operator overload property - parse this as a normal property * (the property parser takes care of the 'operator' syntax * specialization). */ goto parse_normal_prop; case TOKT_REPLACE: /* 'replace' is only allowed with 'modify' objects */ if (!modify) G_tok->log_error(TCERR_REPLACE_PROP_REQ_MOD_OBJ); /* skip the 'replace' keyword */ G_tok->next(); /* set the 'replace' flag for the property parser */ replace = TRUE; /* go parse the normal property definition */ goto parse_normal_prop; case TOKT_RBRACE: /* * If we're in a 'propertyset' definition, this ends the * current propertyset. If the property list is enclosed in * braces, this brace terminates the property list and the * object body. Otherwise, it's an error. */ if (propset_depth != 0) { /* pop a propertyset level */ --propset_depth; /* * skip the brace, and proceed with parsing the rest of * the object */ G_tok->next(); } else if (braces) { /* we're done with the object body */ done = TRUE; /* skip the brace */ G_tok->next(); } else { /* * this property list isn't enclosed in braces, so this is * completely unexpected - treat it the same as any other * invalid token */ goto inval_token; } break; case TOKT_EOF: default: inval_token: /* anything else is invalid */ G_tok->log_error(TCERR_OBJDEF_REQ_PROP, (int)G_tok->getcur()->get_text_len(), G_tok->getcur()->get_text()); /* skip the errant token and continue */ G_tok->next(); /* if we're at EOF, abort */ if (G_tok->cur() == TOKT_EOF) { *err = TRUE; return FALSE; } break; } } /* success */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Parse a property definition within an object definition. * * 'obj_is_nested' indicates that the enclosing object is a nested object. * This tells us that we can't allow the lexicalParent property to be * manually defined, since we will automatically define it explicitly for * the enclosing object by virtue of its being nested. */ void CTcParser::parse_obj_prop( int *err, CTPNObjDef *objdef, int replace, CTcSymMetaclass *meta_sym, tcprs_term_info *term_info, propset_def *propset_stack, int propset_depth, int obj_is_nested, int is_inline) { /* we haven't created the new property yet */ CTPNObjProp *new_prop = 0; /* presume it's not a static property definition */ int is_static = FALSE; /* remember the property token */ CTcToken prop_tok = *G_tok->copycur(); /* * if we're in a property set, apply the property set pattern to the * property name */ if (propset_depth != 0) { /* we can't define operator overloads here */ if (prop_tok.gettyp() == TOKT_OPERATOR) G_tok->log_error(TCERR_OPER_IN_PROPSET); /* * Build the real property name based on all enclosing propertyset * pattern strings. Start with the current (innermost) pattern, * then apply the next pattern out, and so on. * * Note that if the current nesting depth is greater than the * maximum nesting depth, ignore the illegally deep levels and * just start with the deepest legal level. */ int i = propset_depth; if (i > MAX_PROPSET_DEPTH) i = MAX_PROPSET_DEPTH; /* start with the current token */ size_t explen = G_tok->getcur()->get_text_len(); char expbuf[TOK_SYM_MAX_LEN]; memcpy(expbuf, G_tok->getcur()->get_text(), explen); /* iterate through the propertyset stack */ for (propset_def *cur = &propset_stack[i-1] ; i > 0 ; --i, --cur) { char tmpbuf[TOK_SYM_MAX_LEN]; utf8_ptr src; utf8_ptr dst; size_t rem; /* if we'd exceed the maximum token length, stop here */ if (explen + cur->prop_pattern_len > TOK_SYM_MAX_LEN) { /* complain about it */ G_tok->log_error(TCERR_PROPSET_TOK_TOO_LONG); /* stop the expansion */ break; } /* copy the pattern up to the '*' */ for (src.set((char *)cur->prop_pattern), dst.set(tmpbuf), rem = cur->prop_pattern_len ; rem != 0 && src.getch() != '*' ; src.inc(&rem)) { /* copy this character */ dst.setch(src.getch()); } /* if we found a '*', skip it */ if (rem != 0 && src.getch() == '*') src.inc(&rem); /* insert the expansion from the last round here */ memcpy(dst.getptr(), expbuf, explen); /* advance our output pointer */ dst.set(dst.getptr() + explen); /* copy the remainder of the pattern string */ if (rem != 0) { /* copy the remaining bytes */ memcpy(dst.getptr(), src.getptr(), rem); /* advance the output pointer past the copied bytes */ dst.set(dst.getptr() + rem); } /* copy the result back to the expansion buffer */ explen = dst.getptr() - tmpbuf; memcpy(expbuf, tmpbuf, explen); } /* store the new token in tokenizer memory */ prop_tok.set_text(G_tok->store_source(expbuf, explen), explen); } /* presume we won't find a valid symbol for the property token */ CTcSymProp *prop_sym = 0; /* presume it's not an operator overload property */ int op_args = 0; /* if this is an 'operator' property, the syntax is special */ if (prop_tok.gettyp() == TOKT_OPERATOR) parse_op_name(&prop_tok, &op_args); /* * look up the property token as a symbol, to see if it's already * defined - we don't want to presume it's a property quite yet, but * we can at least look to see if it's defined as something else */ CTcSymbol *sym = global_symtab_->find( prop_tok.get_text(), prop_tok.get_text_len()); /* skip it and see what comes next */ switch (G_tok->next()) { case TOKT_LPAR: case TOKT_LBRACE: parse_method: /* look up the symbol */ prop_sym = look_up_prop(&prop_tok, TRUE); /* vocabulary properties can't be code */ if (prop_sym != 0 && prop_sym->is_vocab()) G_tok->log_error_curtok(TCERR_VOCAB_REQ_SSTR); /* * A left paren starts a formal parameter list, a left brace starts * a code body - in either case, we have a method, so parse the * code body. If we're parsing an inline object definition, parse * it as an anonymous method definition instead. */ if (is_inline) { /* inline object - parse an anonymous method */ G_tok->unget(); CTPNAnonFunc *m = CTcPrsOpUnary::parse_anon_func(FALSE, TRUE); /* add this as a property expression */ if (m != 0) new_prop = objdef->add_inline_method(prop_sym, m, 0, replace); } else { /* top-level object - parse a code body */ int argc; int opt_argc; int varargs; int varargs_list; CTcSymLocal *varargs_list_local; int has_retval; CTPNCodeBody *code_body = parse_code_body( FALSE, TRUE, TRUE, &argc, &opt_argc, &varargs, &varargs_list, &varargs_list_local, &has_retval, err, 0, TCPRS_CB_NORMAL, propset_stack, propset_depth, 0, 0); /* check for errors in the code body */ if (*err) return; /* * if this is an operator, check arguments, and pick the * correct operator for the number of operands for operators * with multiple operand numbers */ if (op_args != 0) { /* mark the code body as an operator overload method */ if (code_body != 0) code_body->set_operator_overload(TRUE); /* varargs aren't allowed */ if (varargs) { G_tok->log_error( TCERR_OP_OVERLOAD_WRONG_FORMALS, op_args - 1); } else if (argc != op_args - 1) { /* not a valid combination of operator and arguments */ G_tok->log_error( TCERR_OP_OVERLOAD_WRONG_FORMALS, op_args - 1); } } /* add the method definition */ if (prop_sym != 0) new_prop = objdef->add_method(prop_sym, code_body, 0, replace); } break; case TOKT_SEM: case TOKT_RBRACE: case TOKT_RPAR: case TOKT_RBRACK: case TOKT_COMMA: /* log the error */ G_tok->log_error(TCERR_OBJDEF_REQ_PROPVAL, (int)prop_tok.get_text_len(), prop_tok.get_text(), (int)G_tok->getcur()->get_text_len(), G_tok->getcur()->get_text()); /* if it's anything other than a semicolon, skip it */ if (G_tok->cur() != TOKT_SEM) G_tok->next(); break; case TOKT_COLON: /* * It's a nested object definition. The value of the property is a * reference to an anonymous object defined in-line after the * colon. The in-line object definition uses the "braces" version * of the standard syntax: after the colon we have the class list, * an open brace, the property list, and a close brace. * * Note that we hold off defining the property symbol for as long * as possible, since we want to make sure that we don't have a * syntax error where this was intended to be a whole new top-level * object definition, but the terminator was accidentally left off * the previous object. In this case, the new object definition * would look like a nested object rather than a new top-level * object. This is probably the case if the symbol before the * colon is already known as an object name. */ { /* * If the symbol before the colon is already known as an object * name, it must be intended as a new object rather than a * nested object, meaning that the terminator is missing from * the previous object definition. If this is the case, flag * the termination error and proceed to parse the new object * definition. */ if (sym != 0 && sym->get_type() == TC_SYM_OBJ) { /* * flag the unterminated object error - note that the * location where termination should have occurred is * where the caller tells us */ G_tcmain->log_error(term_info->desc_, term_info->linenum_, TC_SEV_ERROR, TCERR_UNTERM_OBJ_DEF); /* unget the colon so we can parse the new object def */ G_tok->unget(); /* flag the termination error for the caller */ term_info->unterm_ = TRUE; /* we're done */ return; } /* skip the colon */ G_tok->next(); /* parse the nested object property */ if (!objdef->parse_nested_obj_prop( new_prop, err, term_info, &prop_tok, replace)) return; } /* done */ break; case TOKT_EQ: /* skip the '=' */ G_tok->next(); /* * if a brace follows, this must be using the obsolete TADS 2 * notation, in which an equals sign followed a property name even * if a method was being defined; if this is the case, generate an * error, but then proceed to treat what follows as a method */ if (G_tok->cur() == TOKT_LBRACE) { /* log the error... */ G_tok->log_error(TCERR_EQ_WITH_METHOD_OBSOLETE); /* ...but then go ahead and parse it as a method anyway */ goto parse_method; } /* go parse the value */ goto parse_value; default: /* * an '=' is required after a value property name; log the error * and proceed, assuming that the '=' was left out but that the * property value is otherwise well-formed */ G_tok->log_error_curtok(TCERR_PROP_REQ_EQ); parse_value: { /* look up the property symbol */ prop_sym = look_up_prop(&prop_tok, TRUE); /* note if it's a vocabulary property */ int is_vocab_prop = (prop_sym != 0 && prop_sym->is_vocab()); /* we don't know whether we have a constant or method yet */ CTPNCodeBody *code_body = 0; CTcPrsNode *expr = 0; CTPNAnonFunc *inline_method = 0; /* * if this is a vocabulary property, perform special vocabulary * list parsing - a vocabulary property can have one or more * single-quoted strings that we store in a list, but the list * does not have to be enclosed in brackets */ if (is_vocab_prop && G_tok->cur() == TOKT_SSTR) { /* * set the property to a vocab list placeholder for now in * the object - we'll replace it during linking with the * actual vocabulary list */ CTcConstVal cval; cval.set_vocab_list(); expr = new CTPNConst(&cval); /* if I have no dictionary, it's an error */ if (dict_cur_ == 0) G_tok->log_error(TCERR_VOCAB_NO_DICT); /* get the object symbol */ CTcSymObj *obj_sym = (objdef != 0 ? objdef->get_obj_sym() : 0); /* parse the list entries */ for (;;) { /* add the word to our vocabulary list */ if (obj_sym != 0) obj_sym->add_vocab_word( G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), prop_sym->get_prop()); /* * skip the string; if another string doesn't * immediately follow, we're done with the implied list * */ if (G_tok->next() != TOKT_SSTR) break; } } else { /* if it's a vocabulary property, other types aren't allowed */ if (is_vocab_prop) G_tok->log_error_curtok(TCERR_VOCAB_REQ_SSTR); /* check for the 'static' keyword */ if (G_tok->cur() == TOKT_STATIC) { /* note that it's static */ is_static = TRUE; /* skip the 'static' keyword */ G_tok->next(); } /* prepare to parse the property value expression */ CTcPrsPropExprSave save_info; begin_prop_expr(&save_info, is_static, is_inline); /* * parse the expression (which can be a double-quoted * string instead of a value expression) */ expr = parse_expr_or_dstr(TRUE); if (expr == 0) { *err = TRUE; return; } /* * finish the expression, and check to see if it needs * dynamic evaluation (if so, it will yield a code body) */ finish_prop_expr( &save_info, expr, code_body, inline_method, is_static, is_inline, prop_sym); } /* if we have a valid property and expression, add it */ if (prop_sym != 0) { /* add the expression or code body, as appropriate */ if (code_body != 0) { /* method */ new_prop = objdef->add_method( prop_sym, code_body, expr, replace); } else if (inline_method != 0) { /* inline method */ new_prop = objdef->add_inline_method( prop_sym, inline_method, expr, replace); } else if (expr != 0) { /* constant expression */ new_prop = objdef->add_prop( prop_sym, expr, replace, is_static); } } } /* done with the property */ break; } /* make sure that the object doesn't already define this propery */ if (sym != 0) { /* presume it's not a duplicate */ int is_dup = FALSE; /* * if the enclosing object is nested, and this is the lexicalParent * property, then it's defined automatically by the compiler and * thus can't be redefined explicitly */ if (obj_is_nested && sym == lexical_parent_sym_) is_dup = TRUE; /* if we didn't already find a duplicate, scan the existing list */ if (!is_dup) { /* scan the property list for this object */ for (CTPNObjProp *obj_prop = objdef->get_first_prop() ; obj_prop != 0 ; obj_prop = obj_prop->get_next_prop()) { /* * if it matches (and it's not the one we just added), log * an error */ if (obj_prop != new_prop && obj_prop->get_prop_sym() == sym) { /* * if this property is overwritable, we're allowed to * replace it with a new value; otherwise, it's an * illegal duplicate */ if (obj_prop->is_overwritable()) { /* * we're allowed to overwrite it with an explicit * redefinition; simply remove the old property * from the list so we can add the new one */ objdef->delete_property(obj_prop->get_prop_sym()); } else { /* note that we found a duplicate property */ is_dup = TRUE; } /* no need to continue looking */ break; } } } /* if we found a duplicate, log an error */ if (is_dup) G_tok->log_error(TCERR_PROP_REDEF_IN_OBJ, (int)sym->get_sym_len(), sym->get_sym()); } /* * if we're modifying an intrinsic class, make sure the property isn't * defined in the class's native interface */ if (meta_sym != 0 && prop_sym != 0) { /* check this intrinsic class and all of its superclasses */ for (CTcSymMetaclass *cur_meta = meta_sym ; cur_meta != 0 ; cur_meta = cur_meta->get_super_meta()) { /* scan the list of native methods */ for (CTcSymMetaProp *mprop = cur_meta->get_prop_head() ; mprop != 0 ; mprop = mprop->nxt_) { /* if it matches, flag an error */ if (mprop->prop_ == prop_sym) { /* log the error */ G_tok->log_error(TCERR_CANNOT_MOD_META_PROP, prop_sym->get_sym_len(), prop_sym->get_sym()); /* no need to look any further */ break; } } } } /* * if this is a dictionary property, note that it's been defined for * this object */ if (prop_sym != 0 && prop_sym->is_vocab()) { /* scan the list of dictionary properties for this one */ for (CTcDictPropEntry *entry = dict_prop_head_ ; entry != 0 ; entry = entry->nxt_) { /* check this one for a match */ if (entry->prop_ == prop_sym) { /* mark this entry as defined for this object */ entry->defined_ = TRUE; /* no need to look any further */ break; } } } } /* ------------------------------------------------------------------------ */ /* * Look up a property symbol. If the symbol isn't defined, add it. If * it's defined as some other kind of symbol, we'll log an error and return * null. */ CTcSymProp *CTcParser::look_up_prop(const CTcToken *tok, int show_err) { /* look up the symbol */ CTcSymProp *prop = (CTcSymProp *)global_symtab_->find( tok->get_text(), tok->get_text_len()); /* if it's already defined, make sure it's a property */ if (prop != 0) { /* make sure it's a property */ if (prop->get_type() != TC_SYM_PROP) { /* log an error if appropriate */ if (show_err) G_tok->log_error(TCERR_REDEF_AS_PROP, (int)tok->get_text_len(), tok->get_text()); /* we can't use the symbol, so forget it */ prop = 0; } } else { /* create the new property symbol */ prop = new CTcSymProp(tok->get_text(), tok->get_text_len(), FALSE, G_cg->new_prop_id()); /* add it to the global symbol table */ global_symtab_->add_entry(prop); /* mark it as referenced */ prop->mark_referenced(); } /* return the property symbol */ return prop; } /* ------------------------------------------------------------------------ */ /* * Mix-in base class for object definitions (statement type or in-line * expression type) */ /* * Add a property value */ CTPNObjProp *CTPNObjDef::add_prop( CTcSymProp *prop_sym, CTcPrsNode *expr, int replace, int is_static) { /* create the new property item */ CTPNObjProp *prop = new CTPNObjProp(this, prop_sym, expr, 0, 0, is_static); /* link it into my list */ add_prop_entry(prop, replace); /* return the new property to our caller */ return prop; } /* * Add a method */ CTPNObjProp *CTPNObjDef::add_method( CTcSymProp *prop, CTPNCodeBody *code_body, CTcPrsNode *expr, int replace) { /* create a new property */ CTPNObjProp *objprop = new CTPNObjProp(this, prop, 0, code_body, 0, FALSE); /* link it into my list */ add_prop_entry(objprop, replace); /* return it */ return objprop; } /* * Add a method to an in-line object */ CTPNObjProp *CTPNObjDef::add_inline_method( CTcSymProp *prop, CTPNAnonFunc *inline_method, CTcPrsNode *expr, int replace) { /* create a new property */ CTPNObjProp *objprop = new CTPNObjProp( this, prop, expr, 0, inline_method, FALSE); /* link it into my list */ add_prop_entry(objprop, replace); /* return it */ return objprop; } /* * fold constants */ void CTPNObjDef::fold_proplist(CTcPrsSymtab *symtab) { /* fold constants in each of our property list entries */ for (CTPNObjProp *prop = proplist_.first_ ; prop != 0 ; prop = prop->nxt_) prop->fold_constants(symtab); } /* ------------------------------------------------------------------------ */ /* * Object Definition Statement */ /* * add a superclass given the name */ void CTPNStmObjectBase::add_superclass(const CTcToken *tok) { /* add a new superclass list entry to my list */ sclist_.append(new CTPNSuperclass(tok->get_text(), tok->get_text_len())); } /* * add a superclass given the object symbol */ void CTPNStmObjectBase::add_superclass(CTcSymbol *sym) { /* add a new superclass entry to my list */ sclist_.append(new CTPNSuperclass(sym)); } /* * Add an implicit constructor, if one is required. */ void CTPNStmObjectBase::add_implicit_constructor() { /* count the superclasses */ int sc_cnt; CTPNSuperclass *sc; for (sc_cnt = 0, sc = get_first_sc() ; sc != 0 ; sc = sc->nxt_, ++sc_cnt) ; /* * If the object has multiple superclasses and doesn't have an explicit * constructor, add an implicit constructor that simply invokes each * superclass constructor using the same argument list sent to our * constructor. */ if (sc_cnt > 1) { /* scan the property list for a constructor property */ CTPNObjProp *prop; int has_constructor; for (prop = proplist_.first_, has_constructor = FALSE ; prop != 0 ; prop = prop->nxt_) { /* * if this is a constructor, note that the object has an * explicit constructor */ if (prop->is_constructor()) { /* we have a consructor */ has_constructor = TRUE; /* that's all we needed to know - no need to keep looking */ break; } } /* if we don't have a constructor, add one */ if (!has_constructor) { /* * Create a placeholder symbol for the varargs list variable - * we don't really need a symbol for this, but we do need the * symbol object so we can generate code for it properly. Note * that a varargs list variable is a local, not a formal. */ CTcSymLocal *varargs_local = new CTcSymLocal( "*", 1, FALSE, FALSE, 0); /* create the pseudo-statement for the implicit constructor */ CTPNStmImplicitCtor *stm = new CTPNStmImplicitCtor( (CTPNStmObject *)this); /* * Create a code body to hold the statement. The code body has * one local symbol (the vararg list formal parameter), and is * defined by the 'constructor' property. */ CTPNCodeBody *cb = new CTPNCodeBody( G_prs->get_global_symtab(), G_prs->get_goto_symtab(), stm, 0, 0, TRUE, TRUE, varargs_local, 1, TRUE, 0); /* * set the implicit constructor's source file location to the * source file location of the start of the object definition */ cb->set_source_pos(file_, linenum_); /* add it to the object's property list */ add_method(G_prs->get_constructor_sym(), cb, 0, FALSE); } } } /* * Add an entry to my property list */ void CTPNStmObjectBase::add_prop_entry(CTPNObjProp *prop, int replace) { /* inherit the default handling */ CTPNObjDef::add_prop_entry(prop, replace); /* check for replacement */ if (replace && !G_prs->get_syntax_only()) { /* start at the current object */ CTPNStmObject *stm = (CTPNStmObject *)this; /* iterate through all previous versions of the object */ for (;;) { /* * Get this object's superclass - since this is a 'modify' * object, its only superclass is object it directly modifies. * If there's no superclass, the modified base object must have * been undefined, so we have errors to begin with - ignore the * replacement in this case. */ CTPNSuperclass *sc = stm->get_first_sc(); if (sc == 0) break; /* get the symbol for the superclass */ CTcSymObj *sym = (CTcSymObj *)sc->get_sym(); /* * if this object is external, we must mark the property for * replacement so that we replace it at link time, after we've * loaded and resolved the external original object */ if (sym->is_extern()) { /* * Add an entry to my list of properties to delete in my * base objects at link time. */ obj_sym_->add_del_prop_entry(prop->get_prop_sym()); /* * no need to look any further for modified originals -- we * won't find any more of them in this translation unit, * since they must all be external from here on up the * chain */ break; } /* * Get this symbol's object parse tree - since the symbol isn't * external, and because we can only modify objects that we've * already seen, the parse tree must be present. If the parse * tree isn't here, stop now; we might be parsing for syntax * only in this case. */ stm = sym->get_obj_stm(); if (stm == 0) break; /* if this object isn't modified, we're done */ if (!sym->get_modified()) break; /* delete the property in the original object */ stm->delete_property(prop->get_prop_sym()); } } } /* * Delete a property */ void CTPNStmObjectBase::delete_property(CTcSymProp *prop_sym) { /* delete the symbol from the property list */ if (proplist_.del(prop_sym)) { /* found it - delete the property from our vocabulary list as well */ if (obj_sym_ != 0) obj_sym_->delete_vocab_prop(prop_sym->get_prop()); } } /* ------------------------------------------------------------------------ */ /* * Inline object */ /* * Parse a property defining a nested object. In an inline object, a * nested object is treated as an expression containing an inline object: * we evaluate the property expression at the time of instantiating the * enclosing inline object, so this has the effect of creating the nested * object at the same time we create the enclosing inline object. */ int CTPNInlineObjectBase::parse_nested_obj_prop( class CTPNObjProp* &new_prop, int *err, tcprs_term_info *term_info, const class CTcToken *prop_tok, int replace) { /* * Parse the inline object. The caller has already parsed and skipped * a colon, so tell the inline object parser that we're already past * the colon. */ CTPNInlineObject *objdef = CTcPrsOpUnary::parse_inline_object(TRUE); if (objdef == 0) { *err = TRUE; return FALSE; } /* look up the property symbol */ CTcSymProp *prop_sym = G_prs->look_up_prop(prop_tok, TRUE); if (prop_sym != 0 && prop_sym->is_vocab()) G_tok->log_error(TCERR_VOCAB_REQ_SSTR, 1, ":"); /* if we have a valid property, add it */ if (prop_sym != 0) new_prop = add_prop(prop_sym, objdef, replace, FALSE); /* * Define the lexicalParent property in the nested object with a * reference back to the parent object. */ CTcConstVal cval; cval.set_obj(get_obj_sym() != 0 ? get_obj_sym()->get_obj_id() : TCTARG_INVALID_OBJ, get_obj_sym() != 0 ? get_obj_sym()->get_metaclass() : TC_META_UNKNOWN); CTcPrsNode *cexpr = new CTPNConst(&cval); objdef->add_prop(G_prs->get_lexical_parent_sym(), cexpr, FALSE, FALSE); /* success */ return TRUE; } /* * adjust for dynamic compilation */ CTcPrsNode *CTPNInlineObjectBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* adjust each property expression */ for (CTPNObjProp *prop = proplist_.first_ ; prop != 0 ; prop = prop->get_next_prop()) prop->adjust_for_dyn(info); /* we did our updates in-place */ return this; } /* ------------------------------------------------------------------------ */ /* * Object property list */ /* * append a property */ void CTPNPropList::append(CTPNObjProp *prop) { *dst_ = prop; *(dst_ = &prop->nxt_) = 0; last_ = prop; ++cnt_; } /* * delete a property */ int CTPNPropList::del(CTcSymProp *prop_sym) { /* scan our and look for a property matching the given symbol */ for (CTPNObjProp **prvp = &first_, *cur = *prvp ; cur != 0 ; prvp = &cur->nxt_, cur = *prvp) { /* check for a match */ if (cur->get_prop_sym() == prop_sym) { /* this is the one to delete - unlink it from the list */ *prvp = cur->nxt_; /* if that left the list empty, clear the tail pointer */ if (first_ == 0) last_ = 0; /* make sure the append pointer points to the tail's nxt_ */ dst_ = (last_ != 0 ? &last_->nxt_ : &first_); /* decrement the property count */ --cnt_; /* indicate that we found it */ return TRUE; } } /* didn't find it */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Object Property node */ /* * fold constants */ CTcPrsNode *CTPNObjPropBase::fold_constants(CTcPrsSymtab *symtab) { /* * if we have a code body or inline method, check for an underlying * expression that the method wraps */ if ((code_body_ != 0 || inline_method_ != 0) && expr_ != 0) { /* fold the expression */ expr_ = expr_->fold_constants(symtab); /* if it's now a constant, drop the code body/method */ if (expr_->is_const() || expr_->is_dstring()) { if (code_body_ != 0) { code_body_->set_replaced(TRUE); code_body_ = 0; } if (inline_method_ != 0) { inline_method_->set_replaced(TRUE); inline_method_ = 0; } } else { /* * the expression is non-constant, so we need to generate the * code body for run-time evaluation; we can now drop the * expression, since the code wrapper supersedes it */ expr_ = 0; } } /* if we have an inline method or regular method, fold its constants */ if (inline_method_ != 0) inline_method_->fold_constants(symtab); else if (code_body_ != 0) code_body_->fold_constants(symtab); /* if we have an expression, fold it */ if (expr_ != 0) { /* * set this statement's source line location to be the current * location for error reporting */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* fold constants in our expression */ expr_ = expr_->fold_constants(symtab); /* * If the resulting expression isn't a constant value, we need to * convert the expression to a method, since we need to evaluate * the value at run-time with code. Create an expression statement * to hold the expression, then create a code body to hold the * expression statement. Don't do this for inline objects, though; * expression properties in inline objects are evaluated at the * time the object is created, so they don't get made into methods. */ if (!objdef_->is_inline_object() && !expr_->is_const() && !expr_->is_dstring()) { /* * Generate an implicit 'return' for a regular expression, or * an implicit static initializer for a static property. */ CTPNStm *stm; if (is_static_) { /* * it's a static initializer - generate a static * initializer expression */ stm = new CTPNStmStaticPropInit(expr_, prop_sym_->get_prop()); } else if (expr_->has_return_value()) { /* * create a 'return' statement to return the value of the * expression */ stm = new CTPNStmReturn(expr_); } else { /* * It has no return value, so it must be something like a * double-quoted string or a call to a void function. Wrap * this in an 'expression statement' so that we evaluate * the expression for its side effects but discard any * return value. */ stm = new CTPNStmExpr(expr_); } /* create the code body */ code_body_ = new CTPNCodeBody( G_prs->get_global_symtab(), 0, stm, 0, 0, FALSE, FALSE, 0, 0, TRUE, 0); /* * forget about our expression, since it's now incorporated * into the code body */ expr_ = 0; /* * set the source file location in both the code body and the * expression statement nodes to use our own source file * location */ stm->set_source_pos(file_, linenum_); code_body_->set_source_pos(file_, linenum_); } } /* we're not changed directly by this */ return this; } /* * adjust for dynamic execution */ CTcPrsNode *CTPNObjPropBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* adjust my various elements */ if (code_body_ != 0) code_body_->adjust_for_dyn(info); if (inline_method_ != 0) inline_method_->adjust_for_dyn(info); if (expr_ != 0) expr_ = expr_->adjust_for_dyn(info); /* we did our adjustments in-place */ return this; } /* * Am I a constructor? */ int CTPNObjPropBase::is_constructor() const { /* * I'm a constructor if I have a code body, and my property is the * constructor property. */ return (code_body_ != 0 && prop_sym_ != 0 && prop_sym_->get_prop() == G_prs->get_constructor_prop()); } /* ------------------------------------------------------------------------ */ /* * compound statement base class */ /* * instantiate */ CTPNStmCompBase::CTPNStmCompBase(CTPNStm *first_stm, CTcPrsSymtab *symtab) { /* remember the first statement in our statement list */ first_stm_ = first_stm; /* remember our local symbol table */ symtab_ = symtab; /* * presume we don't have our own private symbol table, but will * simply use our parent's */ has_own_scope_ = FALSE; /* * remember the source location of the closing brace, which should * be the current location when we're instantiated */ end_desc_ = G_tok->get_last_desc(); end_linenum_ = G_tok->get_last_linenum(); } /* * fold constants - we simply fold constants in each of our statements */ CTcPrsNode *CTPNStmCompBase::fold_constants(class CTcPrsSymtab *symtab) { /* iterate through our statements and fold constants in each one */ for (CTPNStm *cur = first_stm_ ; cur != 0 ; cur = cur->get_next_stm()) { /* * set this statement's source line location to be the current * location for error reporting */ G_tok->set_line_info(cur->get_source_desc(), cur->get_source_linenum()); /* * Fold constants, using the enclosing symbol table (not the * local symbol table - during code generation we can resolve * only to the global scope, since local scope symbols must * always be resolved during parsing). We assume that statement * nodes are never replaced during constant folding, so we * ignore the result of this call and keep our original * statement list entry. */ cur->fold_constants(symtab); } /* * although nodes within our subtree might have been changed, this * compound statement itself is unchanged by constant folding */ return this; } /* * adjust for dynamic (run-time) compilation */ CTcPrsNode *CTPNStmCompBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* adjust each statement in our list */ for (CTPNStm *cur = first_stm_ ; cur != 0 ; cur = cur->get_next_stm()) { /* * adjust this statement - we assume that statements are never * replaced by this operation, so we just keep our existing list */ cur->adjust_for_dyn(info); } /* return myself */ return this; } /* * Generate code */ void CTPNStmCompBase::gen_code(int, int) { CTPNStm *cur; CTcPrsSymtab *old_frame; /* set my local scope symbol frame, if necessary */ old_frame = G_cs->set_local_frame(symtab_); /* set the code location for the start of the group */ add_debug_line_rec(); /* * iterate through our statements and generate code for each in * sequence */ for (cur = first_stm_ ; cur != 0 ; cur = cur->get_next_stm()) { /* * set this statement's source line location to be the current * location for error reporting */ G_tok->set_line_info(cur->get_source_desc(), cur->get_source_linenum()); /* * Generate code for the statement. Note that we generate in * the scope of the enclosing symbol table, because we never * resolve symbols to local scope during code generation - local * scope symbols must be resolved during parsing, because these * symbols must always be declared before first being used * * We have no use for the results of any expressions, so discard * = true; we don't care about the form of any logical operator * results, so use the looser "for condition" rules */ cur->gen_code(TRUE, TRUE); } /* set the source location for the end of the group */ add_debug_line_rec(end_desc_, end_linenum_); /* check for unreferenced local variables */ if (symtab_ != 0 && has_own_scope_) { /* * set our line information to be current again, so that any * unreferenced local errors are reported at the start of the * compound statement */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); } /* restore the enclosing local frame */ old_frame = G_cs->set_local_frame(old_frame); } /* * Chart the control flow through the statement list */ unsigned long CTPNStmCompBase::get_control_flow(int warn) const { /* * presume we will not find a 'throw' or 'return', and that we'll be * able to reach the next statement after the list */ unsigned long flags = TCPRS_FLOW_NEXT; /* * iterate through the statements in our list, since they'll be * executed in the same sequence at run-time */ for (CTPNStm *cur = first_stm_ ; cur != 0 ; cur = cur->get_next_stm()) { /* get this statement's control flow characteristics */ unsigned long flags1 = cur->get_control_flow(warn); /* * OR each of 'throws', 'returns void', and 'returns value', * since anything a reachable statement does to return or throw, * the overall list can do to leave. Likewise, the 'break', * 'goto', and 'continue' flags push up into the enclosing * statement. */ flags |= (flags1 & (TCPRS_FLOW_THROW | TCPRS_FLOW_RET_VOID | TCPRS_FLOW_RET_VAL | TCPRS_FLOW_GOTO | TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT)); /* * if this statement can't continue to the next statement, the * remainder of this compound statement is unreachable, unless * it has a code label */ if (!(flags1 & TCPRS_FLOW_NEXT)) { /* * we can't reach the next statement; unless we find a label * at some point after this, we won't be able to reach any * of the remaining statements, hence we won't be able to * reach the statement after our list */ flags &= ~TCPRS_FLOW_NEXT; /* * If another statement follows, check to see if it's * reachable by label, because it's not reachable by any * other means */ CTPNStm *nxt = cur->get_next_stm(); if (nxt != 0) { /* * check to see if this statement has a label; if not, * warn that it is unreachable */ if (warn && !nxt->has_code_label() && !G_prs->get_syntax_only()) nxt->log_warning(TCERR_UNREACHABLE_CODE); /* skip ahead until we find something with a label */ while (nxt != 0 && !nxt->has_code_label()) { /* remember the previous statement */ cur = nxt; /* move on to the next statement */ nxt = nxt->get_next_stm(); } /* * if we found a reachable statement again, we might be * able to continue from the end of the list after all */ if (nxt != 0) flags |= TCPRS_FLOW_NEXT; } } } /* return the result */ return flags; } /* ------------------------------------------------------------------------ */ /* * expression statement base class */ /* * fold constants */ CTcPrsNode *CTPNStmExprBase::fold_constants(class CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* * fold constants in our expression, replacing our expression with * the folded version */ expr_ = expr_->fold_constants(symtab); /* * although our expression subtree might have been changed, this * compound statement itself is unchanged by constant folding */ return this; } /* * Generate code */ void CTPNStmExprBase::gen_code(int, int) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* add a debug line record for the statement */ add_debug_line_rec(); /* * generate code in our expression - the result will not be used in any * further calculation, so discard it; and since we don't care about * the return value, use the looser "for condition" rules for any * top-level logical operators in the expression so that we don't * bother applying any extra conversions to a value we're just * discarding anyway */ expr_->gen_code(TRUE, TRUE); } /* ------------------------------------------------------------------------ */ /* * 'if' statement */ /* * fold constants */ CTcPrsNode *CTPNStmIfBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* * fold constants in the condition, replacing the expression with * the folded expression */ cond_expr_ = cond_expr_->fold_constants(symtab); /* fold constants in the substatements */ if (then_part_ != 0) then_part_ = (CTPNStm *)then_part_->fold_constants(symtab); if (else_part_ != 0) else_part_ = (CTPNStm *)else_part_->fold_constants(symtab); /* we are not directly changed by this operation */ return this; } /* * Chart the control flow through the conditional */ unsigned long CTPNStmIfBase::get_control_flow(int warn) const { unsigned long flags1; unsigned long flags2; /* * if my condition is constant, our control flow is determined * entirely by the branch we'll take */ if (cond_expr_ != 0 && cond_expr_->is_const()) { CTPNStm *part; /* * We have a constant expression - if it's true, our control * flow is determined by the 'then' part, otherwise it's * determines by the 'else' part. */ part = (cond_expr_->get_const_val()->get_val_bool() ? then_part_ : else_part_); /* * if the part determining the control flow is a null statement, * we'll simply continue to the next statement; otherwise, the * result is the control flow of the determining statement */ if (part == 0) { /* it's a null statement, which merely continues */ return TCPRS_FLOW_NEXT; } else { /* our control flow is this statement's control flow */ return part->get_control_flow(warn); } } /* * The control flow through the 'if' is a combination of the control * flow through the two parts. If one of the parts is a void * statement, it continues to the next statement after the 'if'. */ /* check the 'then' part if present */ flags1 = (then_part_ != 0 ? then_part_->get_control_flow(warn) : TCPRS_FLOW_NEXT); /* check the 'else' part if present */ flags2 = (else_part_ != 0 ? else_part_->get_control_flow(warn) : TCPRS_FLOW_NEXT); /* combine the parts to get the possible control flows */ return flags1 | flags2; } /* ------------------------------------------------------------------------ */ /* * "for" statement node */ /* * fold constants */ CTcPrsNode *CTPNStmForBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* * fold constants in each of our expressions, replacing the * expressions with the folded versions */ if (init_expr_ != 0) init_expr_ = init_expr_->fold_constants(symtab); if (cond_expr_ != 0) cond_expr_ = cond_expr_->fold_constants(symtab); if (reinit_expr_ != 0) reinit_expr_ = reinit_expr_->fold_constants(symtab); /* fold constants in the body statement */ if (body_stm_ != 0) body_stm_ = (CTPNStm *)body_stm_->fold_constants(symtab); /* we are not directly changed by this operation */ return this; } /* * Chart the control flow through the loop */ unsigned long CTPNStmForBase::get_control_flow(int warn) const { unsigned long flags; /* * If we have a condition with a constant false value, and there's * either a reinitialization expression or a loop body, warn about the * unreachable code. If the body is directly reachable via a code * label, we don't need this warning. */ if (cond_expr_ != 0 && cond_expr_->is_const() && !cond_expr_->get_const_val()->get_val_bool() && (reinit_expr_ != 0 || body_stm_ != 0) && (body_stm_ != 0 && !body_stm_->has_code_label()) && !G_prs->get_syntax_only()) { /* log a warning if desired */ if (warn && !cond_expr_->get_const_val()->is_ctc()) log_warning(TCERR_FOR_COND_FALSE); /* this will simply continue to the next statement */ return TCPRS_FLOW_NEXT; } /* check for a body */ if (body_stm_ != 0) { /* determine how our body can exit */ flags = body_stm_->get_control_flow(warn); } else { /* * We have no body, so it's entirely up to the loop - we'll * evaluate those parameters below, but for now clear all flags */ flags = 0; } /* * ignore any 'next' flag that comes out of the body, because 'next' * from the last statement in the body actually takes us back to the * top of the loop */ flags &= ~TCPRS_FLOW_NEXT; /* * if there's a 'break' in the body, we can continue to the next * statement, since a break in the body takes us to the statement * after the 'for' */ if ((flags & TCPRS_FLOW_BREAK) != 0) flags |= TCPRS_FLOW_NEXT; /* * If we have a condition, and either the condition is a constant * false value or has a non-constant value, assume that the * condition can possibly become false and hence that the loop can * exit, and hence that the next statement is reachable. */ if (cond_expr_ != 0 && (!cond_expr_->is_const() || (cond_expr_->is_const() && !cond_expr_->get_const_val()->get_val_bool()))) flags |= TCPRS_FLOW_NEXT; /* * If we have any "in" clauses, we implicitly have a condition that * allows the loop to terminate. */ if (in_exprs_ != 0) flags |= TCPRS_FLOW_NEXT; /* * clear the 'break' and 'continue' flags, since we capture them in * our own scope - if our child breaks or continues, it won't affect * the "for" loop's container */ flags &= ~(TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT); /* return the flags */ return flags; } /* ------------------------------------------------------------------------ */ /* * "foreach" statement node */ /* * fold constants */ CTcPrsNode *CTPNStmForeachBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* * fold constants in each of our expressions, replacing the * expressions with the folded versions */ if (iter_expr_ != 0) iter_expr_ = iter_expr_->fold_constants(symtab); if (coll_expr_ != 0) coll_expr_ = coll_expr_->fold_constants(symtab); /* fold constants in the body statement */ if (body_stm_ != 0) body_stm_ = (CTPNStm *)body_stm_->fold_constants(symtab); /* we are not directly changed by this operation */ return this; } /* * Chart the control flow through the loop */ unsigned long CTPNStmForeachBase::get_control_flow(int warn) const { unsigned long flags; /* check for a body */ if (body_stm_ != 0) { /* determine how our body can exit */ flags = body_stm_->get_control_flow(warn); /* * add in the 'next' flag, because collection iterations always * terminate */ flags |= TCPRS_FLOW_NEXT; /* * clear the 'break' and 'continue' flags, since we capture them * in our own scope - if our child breaks or continues, it won't * affect the container of the 'foreach' itself */ flags &= ~(TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT); } else { /* * we have no body, so it's entirely up to the loop, which * simply iterates over the list and goes to the next statement */ flags = TCPRS_FLOW_NEXT; } /* return the flags */ return flags; } /* ------------------------------------------------------------------------ */ /* * "while" statement node */ /* * fold constants */ CTcPrsNode *CTPNStmWhileBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* * fold constants in our condition expression, replacing the * expression with the folded version */ if (cond_expr_ != 0) cond_expr_ = cond_expr_->fold_constants(symtab); /* fold constants in the body statement */ if (body_stm_ != 0) body_stm_ = (CTPNStm *)body_stm_->fold_constants(symtab); /* we are not directly changed by this operation */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmWhileBase::get_control_flow(int warn) const { unsigned long flags; /* * If we have a condition with a constant false value, and we have a * non-empty body, and the body isn't reachable via a label, warn * about the unreachable code */ if (cond_expr_->is_const() && !cond_expr_->get_const_val()->get_val_bool() && body_stm_ != 0 && !body_stm_->has_code_label() && G_prs->get_syntax_only()) { /* log a warning if desired */ if (warn && !cond_expr_->get_const_val()->is_ctc()) log_warning(TCERR_WHILE_COND_FALSE); /* this will simply continue to the next statement */ return TCPRS_FLOW_NEXT; } /* check for a body */ if (body_stm_ != 0) { /* determine how our body can exit */ flags = body_stm_->get_control_flow(warn); } else { /* * We have no body, so it's entirely up to the loop - we'll * evaluate those parameters below, but for now clear all flags */ flags = 0; } /* * ignore any 'next' flag that comes out of the body, because 'next' * from the last statement in the body actually takes us back to the * top of the loop */ flags &= ~TCPRS_FLOW_NEXT; /* * if there's a 'break' in the body, we can continue to the next * statement, since a break in the body takes us to the statement * after the 'for' */ if ((flags & TCPRS_FLOW_BREAK) != 0) flags |= TCPRS_FLOW_NEXT; /* * if the condition is a constant false value, or has a non-constant * value, assume that the condition can possibly become false and * hence that the loop can exit, and hence that the next statement * is reachable */ if (!cond_expr_->is_const() || (cond_expr_->is_const() && !cond_expr_->get_const_val()->get_val_bool())) flags |= TCPRS_FLOW_NEXT; /* * clear the 'break' and 'continue' flags, since we capture them in * our own scope - if our child breaks or continues, it won't affect * the loop's container */ flags &= ~(TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT); /* return the flags */ return flags; } /* ------------------------------------------------------------------------ */ /* * "do-while" statement node */ /* * fold constants */ CTcPrsNode *CTPNStmDoWhileBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* * fold constants in our condition expression, replacing the * expression with the folded version */ if (cond_expr_ != 0) cond_expr_ = cond_expr_->fold_constants(symtab); /* fold constants in the body statement */ if (body_stm_ != 0) body_stm_ = (CTPNStm *)body_stm_->fold_constants(symtab); /* we are not directly changed by this operation */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmDoWhileBase::get_control_flow(int warn) const { unsigned long flags; /* check for a body */ if (body_stm_ != 0) { /* determine how our body can exit */ flags = body_stm_->get_control_flow(warn); } else { /* * We have no body, so it's entirely up to the loop - we'll * evaluate those parameters below, but for now clear all flags */ flags = 0; } /* * If we have a 'next' flag, it means that control can flow out from * the end of the last statement in the loop. That in turn means that * we can reach the code where we evaluate the condition. So, in order * for flow to proceed out of the loop, we have to be able to reach the * condition, and then the condition has to be capable of being false * (because if it's always true, we'll simply go back to the start of * the loop every time we hit the condition). */ if ((flags & TCPRS_FLOW_NEXT) != 0) { /* * The 'next' flag up to this point only means that control can * reach the condition at the bottom of the loop. Since we want to * tell our caller whether or not control can flow *out* of the * loop, this is now irrelevant, so clear the 'next' flag for now. */ flags &= ~TCPRS_FLOW_NEXT; /* * Okay, we now know we can reach the condition at the bottom of * the loop. If the condition is a constant false value, we know * that we can flow out of the loop, since we know we'll hit the * condition, find that it's false, and exit the loop. Likewise, * if the condition hasa non-constant value, assume that it can * possibly become false and hence that the loop can exit, and * hence that the next statement is reachable. */ if (!cond_expr_->is_const() || (cond_expr_->is_const() && !cond_expr_->get_const_val()->get_val_bool())) flags |= TCPRS_FLOW_NEXT; } /* * if there's a 'break' in the body, we can continue to the next * statement, since a break in the body takes us to the statement * after the 'while' */ if ((flags & TCPRS_FLOW_BREAK) != 0) flags |= TCPRS_FLOW_NEXT; /* * clear the 'break' and 'continue' flags, since we capture them in * our own scope - if our child breaks or continues, it won't affect * the loop's container */ flags &= ~(TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT); /* return the flags */ return flags; } /* ------------------------------------------------------------------------ */ /* * "switch" statement node */ /* * fold constants */ CTcPrsNode *CTPNStmSwitchBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* fold constants in our expression and body */ expr_ = expr_->fold_constants(symtab); if (body_ != 0) body_ = (CTPNStm *)body_->fold_constants(symtab); /* we're unchanged by constant folding */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmSwitchBase::get_control_flow(int warn) const { unsigned long flags; /* * Control flow through a switch is controlled by control flow * through the compound statement. If the compound statement can * break, then we flow to the next statement. If the compound * statement can reach the next statement, then so can we. */ flags = (body_ != 0 ? body_->get_control_flow(warn) : TCPRS_FLOW_NEXT); /* * check for a reachable break in the body - a break means the next * statement is reachable */ if ((flags & TCPRS_FLOW_BREAK) != 0) { /* * break takes us to the next statement, and doesn't propagate * up to a break in any enclosing statement */ flags |= TCPRS_FLOW_NEXT; flags &= ~TCPRS_FLOW_BREAK; } /* * If the switch has no 'default' case, it is almost certain that we * can reach the next statement after the switch, because we can * probably assume that not every case is accounted for and hence we * have the equivalent of a null 'default' case. * * Note that we can't know for sure that every case hasn't been * accounted for, because we do not have datatype information for the * controlling expression; even if we did know the controlling * expression's datatype, it is impossible to enumerate every possible * value for expressions of certain datatypes (such as lists and * strings, which have effectively infinite ranges). However, it is a * reasonable assumption that not every possible case has been * accounted for, */ if (!get_has_default()) flags |= TCPRS_FLOW_NEXT; /* return the flags */ return flags; } /* ------------------------------------------------------------------------ */ /* * code label statement node */ /* * fold constants */ CTcPrsNode *CTPNStmLabelBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* fold constants in our labeled statement */ if (stm_ != 0) stm_ = (CTPNStm *)stm_->fold_constants(symtab); /* we're unchanged by constant folding */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmLabelBase::get_control_flow(int warn) const { ulong flags; /* * if we have an underlying statement, control flow is the same as * the statement's control flow; if we don't have a statement (i.e., * our labeled statement is an empty statement), we flow to the next * statement */ flags = (stm_ != 0 ? stm_->get_control_flow(warn) : TCPRS_FLOW_NEXT); /* * if we have an explicit 'break' flag in our control flow flags, it * means that we were targeted specifically with a 'break' statement * contained within our body - this transfers control to the next * statement after me, therefore my next statement is reachable in * this case */ if ((control_flow_flags_ & TCPRS_FLOW_BREAK) != 0) flags |= TCPRS_FLOW_NEXT; /* return the result */ return flags; } /* * add control flow flags */ void CTPNStmLabelBase::add_control_flow_flags(ulong flags) { /* add the flags to any we already have */ control_flow_flags_ |= flags; } /* ------------------------------------------------------------------------ */ /* * 'goto' statement node */ /* * Chart the control flow through the statement */ unsigned long CTPNStmGotoBase::get_control_flow(int warn) const { CTcSymbol *sym; /* find our target statement */ if (G_prs->get_goto_symtab() != 0 && (sym = G_prs->get_goto_symtab()->find(lbl_, lbl_len_)) != 0 && sym->get_type() == TC_SYM_LABEL) { /* * we found our label - mark its statement as having a 'goto' * targeted at it */ ((CTcSymLabel *)sym)->add_control_flow_flags(TCPRS_FLOW_GOTO); } /* indicate that we jump somewhere */ return TCPRS_FLOW_GOTO; } /* ------------------------------------------------------------------------ */ /* * 'case' label statement node */ /* * fold constants */ CTcPrsNode *CTPNStmCaseBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* fold constants in our expression */ expr_ = expr_->fold_constants(symtab); /* if the expression isn't a constant, it's an error */ if (!expr_->is_const()) log_error(TCERR_CASE_NOT_CONSTANT); /* fold constants in our labeled statement */ if (stm_ != 0) stm_ = (CTPNStm *)stm_->fold_constants(symtab); /* we're unchanged by constant folding */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmCaseBase::get_control_flow(int warn) const { /* * if we have an underlying statement, control flow is the same as * the statement's control flow; if we don't have a statement (i.e., * our labeled statement is an empty statement), we flow to the next * statement */ return (stm_ != 0 ? stm_->get_control_flow(warn) : TCPRS_FLOW_NEXT); } /* ------------------------------------------------------------------------ */ /* * 'default' label statement node */ /* * fold constants */ CTcPrsNode *CTPNStmDefaultBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* fold constants in our labeled statement */ if (stm_ != 0) stm_ = (CTPNStm *)stm_->fold_constants(symtab); /* we're unchanged by constant folding */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmDefaultBase::get_control_flow(int warn) const { /* * if we have an underlying statement, control flow is the same as * the statement's control flow; if we don't have a statement (i.e., * our labeled statement is an empty statement), we flow to the next * statement */ return (stm_ != 0 ? stm_->get_control_flow(warn) : TCPRS_FLOW_NEXT); } /* ------------------------------------------------------------------------ */ /* * 'try' statement */ /* * add a 'catch' clause */ void CTPNStmTryBase::add_catch(CTPNStmCatch *catch_stm) { /* link it in at the end of our list */ if (last_catch_stm_ != 0) last_catch_stm_->set_next_catch(catch_stm); else first_catch_stm_ = catch_stm; /* it's now the last catch statement */ last_catch_stm_ = catch_stm; catch_stm->set_next_catch(0); /* it's the last statement in its group */ catch_stm->set_next_stm(0); } /* * fold constants */ CTcPrsNode *CTPNStmTryBase::fold_constants(CTcPrsSymtab *symtab) { CTPNStmCatch *cur_catch; /* fold constants in our protected code */ if (body_stm_ != 0) body_stm_->fold_constants(symtab); /* fold constants in our catch list */ for (cur_catch = first_catch_stm_ ; cur_catch != 0 ; cur_catch = cur_catch->get_next_catch()) { /* fold constants; assume this will not change the statement */ cur_catch->fold_constants(symtab); } /* fold constants in our 'finally' clause */ if (finally_stm_ != 0) finally_stm_->fold_constants(symtab); /* we're unchanged by constant folding */ return this; } /* * adjust for dynamic (run-time) compilation */ CTcPrsNode *CTPNStmTryBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* adjust our body statement */ if (body_stm_ != 0) body_stm_ = (CTPNStm *)body_stm_->adjust_for_dyn(info); /* adjust each item in our catch list */ for (CTPNStmCatch *cur_catch = first_catch_stm_ ; cur_catch != 0 ; cur_catch = cur_catch->get_next_catch()) { /* adjust this item; assume it won't replace the node */ cur_catch->adjust_for_dyn(info); } /* adjust our 'finally' clause */ if (finally_stm_ != 0) finally_stm_ = (CTPNStmFinally *)finally_stm_->adjust_for_dyn(info); /* we're unchanged by constant folding */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmTryBase::get_control_flow(int warn) const { unsigned long flags; unsigned long fin_flags; CTPNStmCatch *cur_catch; /* * First, determine the control flow through the protected code. If * it exits, we either exit or proceed to the 'finally' clause. */ flags = (body_stm_ != 0 ? body_stm_->get_control_flow(warn) : TCPRS_FLOW_NEXT); /* * Next, figure out what the various 'catch' clauses can do. We * assume that each 'catch' clause is enterable, since any exception * could occur within the protected code (whether it explicitly * throws or not - it could call a function or method that throws). * So, we can simply OR together with the main block's flags all of * the ways the different 'catch' clauses can exit. */ for (cur_catch = first_catch_stm_ ; cur_catch != 0 ; cur_catch = cur_catch->get_next_catch()) { /* OR in the flags for this 'catch' block */ flags |= cur_catch->get_control_flow(warn); } /* * If we have a 'finally' clause, it is definitely enterable, since * every other way of leaving the protected code goes through it. * Find how it can exit. */ fin_flags = (finally_stm_ != 0 ? finally_stm_->get_control_flow(warn) : TCPRS_FLOW_NEXT); /* * If the 'finally' block can't flow to next, then neither can the * main/catch blocks. In addition, the main/catch blocks can't * 'break', 'continue', 'goto', or 'return' either, because the * 'finally' block will not allow the other blocks to proceed with * whatever transfers they attempt. So, if 'next' isn't set in the * 'finally' flags, clear out all of these flags from the main/catch * flags. */ if (!(fin_flags & TCPRS_FLOW_NEXT)) flags &= ~(TCPRS_FLOW_NEXT | TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT | TCPRS_FLOW_RET_VAL | TCPRS_FLOW_RET_VOID | TCPRS_FLOW_GOTO); /* * Add in the remaining flags from the 'finally' block, since * whatever it does, the overall statement does. However, ignore * 'next' flags in the 'finally' block, because if the 'finally' * code goes to next, it just means that we keep doing whatever we * were doing in the main block. */ fin_flags &= ~TCPRS_FLOW_NEXT; flags |= fin_flags; /* return the result */ return flags; } /* ------------------------------------------------------------------------ */ /* * 'catch' clause */ /* * set our exception class name */ void CTPNStmCatchBase::set_exc_class(const CTcToken *tok) { /* * remember the token's contents - the tokenizer keeps token symbol * strings in memory throughout the compilation, so we can simply * store a direct reference to the string */ exc_class_ = tok->get_text(); exc_class_len_ = tok->get_text_len(); } /* * fold constants */ CTcPrsNode *CTPNStmCatchBase::fold_constants(CTcPrsSymtab *symtab) { /* fold constants in our body */ if (body_ != 0) body_ = (CTPNStm *)body_->fold_constants(symtab); /* we're unchanged by constant folding */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmCatchBase::get_control_flow(int warn) const { /* * our control flow is that of the body */ return (body_ != 0 ? body_->get_control_flow(warn) : TCPRS_FLOW_NEXT); } /* ------------------------------------------------------------------------ */ /* * 'finally' clause */ /* * fold constants */ CTcPrsNode *CTPNStmFinallyBase::fold_constants(CTcPrsSymtab *symtab) { /* fold constants in our body */ if (body_ != 0) body_ = (CTPNStm *)body_->fold_constants(symtab); /* we're unchanged by constant folding */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmFinallyBase::get_control_flow(int warn) const { /* our control flow is that of the body */ return (body_ != 0 ? body_->get_control_flow(warn) : TCPRS_FLOW_NEXT); } /* ------------------------------------------------------------------------ */ /* * 'throw' statement */ /* * fold constants */ CTcPrsNode *CTPNStmThrowBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* fold constants in our expression */ expr_ = expr_->fold_constants(symtab); /* we're unchanged by constant folding */ return this; } /* ------------------------------------------------------------------------ */ /* * 'break' statement */ /* * set the label */ void CTPNStmBreakBase::set_label(const CTcToken *tok) { /* * token text is kept in memory throughout compilation, so we can * simply store a reference to the text without copying it */ lbl_ = tok->get_text(); lbl_len_ = tok->get_text_len(); } /* * chart control flow */ ulong CTPNStmBreakBase::get_control_flow(int warn) const { /* * If we don't have a label, we break out of our enclosing control * structure. If we have a label, we act like a 'goto' statement. */ if (lbl_ == 0) { /* no label - we simply break out of the enclosing loop or switch */ return TCPRS_FLOW_BREAK; } else { CTcSymbol *sym; /* * We have a label - we act like a 'goto' statement. Find our * target statement and mark it as having a targeted 'break'. */ if (G_prs->get_goto_symtab() != 0 && (sym = G_prs->get_goto_symtab()->find(lbl_, lbl_len_)) != 0 && sym->get_type() == TC_SYM_LABEL) { /* * we found our label - mark its statement as having a * 'break' targeted at it */ ((CTcSymLabel *)sym)->add_control_flow_flags(TCPRS_FLOW_BREAK); } /* indicate that we jump somewhere else */ return TCPRS_FLOW_GOTO; } } /* ------------------------------------------------------------------------ */ /* * 'continue' statement */ /* * set the label */ void CTPNStmContinueBase::set_label(const CTcToken *tok) { /* * token text is kept in memory throughout compilation, so we can * simply store a reference to the text without copying it */ lbl_ = tok->get_text(); lbl_len_ = tok->get_text_len(); } /* * chart control flow */ ulong CTPNStmContinueBase::get_control_flow(int warn) const { /* * If we don't have a label, we continue in our enclosing control * structure. If we have a label, we act like a 'goto' statement. */ if (lbl_ == 0) { /* no label - we simply continue with the enclosing loop */ return TCPRS_FLOW_CONT; } else { CTcSymbol *sym; /* * We have a label - we act like a 'goto' statement. Find our * target statement and mark it as having a targeted 'continue'. */ if (G_prs->get_goto_symtab() != 0 && (sym = G_prs->get_goto_symtab()->find(lbl_, lbl_len_)) != 0 && sym->get_type() == TC_SYM_LABEL) { /* * we found our label - mark its statement as having a * 'continue' targeted at it */ ((CTcSymLabel *)sym)->add_control_flow_flags(TCPRS_FLOW_CONT); } /* indicate that we jump somewhere else */ return TCPRS_FLOW_GOTO; } } /* ------------------------------------------------------------------------ */ /* * code ('goto') label base */ /* * Add control flow flags to my statement */ void CTcSymLabelBase::add_control_flow_flags(ulong flags) { if (stm_ != 0) stm_->add_control_flow_flags(flags); } /* ------------------------------------------------------------------------ */ /* * Grammar tree node subclasses */ /* * Grammar tree node - base class for nodes with children (OR, cat) */ class CTcPrsGramNodeWithChildren: public CTcPrsGramNode { public: CTcPrsGramNodeWithChildren() { /* no subitems yet */ sub_head_ = sub_tail_ = 0; } /* add a sub-item */ void add_sub_item(CTcPrsGramNode *sub) { /* add it to the end of the list */ if (sub_tail_ != 0) sub_tail_->next_ = sub; else sub_head_ = sub; /* it's now the last item */ sub_tail_ = sub; sub->next_ = 0; } virtual void flatten_cat() { CTcPrsGramNode *cur; /* flatten CAT nodes in our subnodes */ for (cur = sub_head_ ; cur != 0 ; cur = cur->next_) cur->flatten_cat(); } /* initialize expansion */ virtual void init_expansion() { CTcPrsGramNode *cur; /* initialize subnodes */ for (cur = sub_head_ ; cur != 0 ; cur = cur->next_) cur->init_expansion(); } /* head and tail of list of subnodes */ CTcPrsGramNode *sub_head_; CTcPrsGramNode *sub_tail_; }; /* * Grammar tree node - CAT node. This represents a string of * concatenated tokens. */ class CTcPrsGramNodeCat: public CTcPrsGramNodeWithChildren { public: /* I'm a CAT node */ virtual int is_cat() { return TRUE; } /* * Consolidate all of the OR's at the top of the tree. If we have any * OR subnodes, we'll rewrite ourselves as an OR of the concatenations * of all of the sub-OR's in all combinations. */ virtual CTcPrsGramNode *consolidate_or(); /* * Flatten CAT nodes together. If we have any CAT nodes below us, * move their contents into our own list directly. */ virtual void flatten_cat() { CTcPrsGramNode *prv; CTcPrsGramNode *cur; CTcPrsGramNode *nxt; /* * Check each child. For each child that's a CAT node, move its * contents directly into our list, removing the redundant * intervening CAT node. */ for (prv = 0, cur = sub_head_ ; cur != 0 ; cur = nxt) { /* note the next item, in case we fiddle with the list */ nxt = cur->next_; /* flatten the CAT items below this one */ cur->flatten_cat(); /* if this item is a CAT, move its contents directly into us */ if (cur->is_cat()) { CTcPrsGramNodeCat *cur_cat; /* cast it to a CAT node */ cur_cat = (CTcPrsGramNodeCat *)cur; /* if it's empty, just remove it; otherwise, link it in */ if (cur_cat->sub_head_ != 0) { /* link from the previous item to the sublist head */ if (prv == 0) { /* * this is the first item - put it at the start of * our list */ sub_head_ = cur_cat->sub_head_; } else { /* link it after the previous item */ prv->next_ = cur_cat->sub_head_; } /* link from the tail of the sublist to the next item */ cur_cat->sub_tail_->next_ = nxt; /* if it's the last item, set our tail pointer */ if (sub_tail_ == cur) sub_tail_ = cur_cat->sub_tail_; /* * the tail of the sublist is now the previous item in * the list for the purposes of the next iteration */ prv = cur_cat->sub_tail_; } else { /* unlink it from our list */ if (prv != 0) prv->next_ = nxt; else sub_head_ = nxt; /* update the tail pointer if removing the last item */ if (sub_tail_ == cur_cat) sub_tail_ = prv; /* note that the previous item is unchanged */ } } else { /* * it's not a CAT, so we're not changing it; this is the * previous item for the next iteration */ prv = cur; } } } /* clone the current expansion subtree */ virtual CTcPrsGramNode *clone_expansion() const { /* create a new 'cat' node */ CTcPrsGramNodeCat *new_cat = new (G_prsmem) CTcPrsGramNodeCat(); /* add a clone of each of my children and add it to my replica */ for (CTcPrsGramNode *cur = sub_head_ ; cur != 0 ; cur = cur->next_) new_cat->add_sub_item(cur->clone_expansion()); /* return my replica */ return new_cat; } /* clone this node (without any children) */ virtual CTcPrsGramNodeWithChildren *clone_this_node() { return new (G_prsmem) CTcPrsGramNodeCat(); } /* initialize expansion */ virtual void init_expansion() { /* set to expand from our first child */ cur_sub_ = sub_head_; /* inherit default to initialize subnodes */ CTcPrsGramNodeWithChildren::init_expansion(); } /* get the next token in a token expansion */ const CTcToken *get_next_token() { /* advance to the next subitem */ cur_sub_ = cur_sub_->next_; /* * If there's anything left, return it; otherwise, return null. * During final token expansion, a 'cat' node never has anything * but tokens beneath it, since we move all the 'or' nodes to a * single 'or' at the top of the tree and flatten out the 'cat' * nodes to a single level under that; so, we can simply return * the token directly from the next underlying node. */ return (cur_sub_ != 0 ? cur_sub_->get_tok() : 0); } /* * get my current token - we'll just return the token from the current * subitem */ const CTcToken *get_tok() const { return (cur_sub_ != 0 ? cur_sub_->get_tok() : 0); } /* current subitem in expansion */ CTcPrsGramNode *cur_sub_; }; /* * Grammar tree node - OR node. This represents a set of alternatives. */ class CTcPrsGramNodeOr: public CTcPrsGramNodeWithChildren { public: /* I'm an "or" node */ virtual int is_or() { return TRUE; } /* * Consolidate all of the OR's at the top of the tree. */ virtual CTcPrsGramNode *consolidate_or() { CTcPrsGramNode *cur; CTcPrsGramNode *nxt; CTcPrsGramNode *old_head; /* * Consolidate OR's in all subtrees. If any of our immediate * children turn into OR's, simply pull their children into our OR * list directly - OR(a, OR(b, c)) is equivalent to OR(a, b, c). * * Before we build the new list, stash away our entire subtree, * since we'll rebuild the tree from the updated versions. */ old_head = sub_head_; sub_head_ = sub_tail_ = 0; /* run through our list and build the new, consolidated list */ for (cur = old_head ; cur != 0 ; cur = nxt) { CTcPrsGramNode *new_sub; /* remember the next item in the old list */ nxt = cur->next_; /* consolidate OR's in the subtree */ new_sub = cur->consolidate_or(); /* * if this is an OR node, add its children directly to us; * otherwise, add the node itself */ if (new_sub->is_or()) { CTcPrsGramNodeOr *new_or; CTcPrsGramNode *chi; CTcPrsGramNode *next_chi; /* cast it */ new_or = (CTcPrsGramNodeOr *)new_sub; /* add each of the sub-OR's children as our direct children */ for (chi = new_or->sub_head_ ; chi != 0 ; chi = next_chi) { /* remember the next child in the old sub-list */ next_chi = chi->next_; /* move it directly to our list */ add_sub_item(chi); } /* the old OR item is now empty of children */ new_or->sub_head_ = new_or->sub_tail_ = 0; } else { /* it's not an OR - add it directly to our list */ add_sub_item(new_sub); } } /* return myself with no further changes */ return this; } /* initialize expansion - set up at the first alternative */ virtual void init_expansion() { /* start at the first alternative */ cur_alt_ = sub_head_; /* initialize the first alternative for expansion */ cur_alt_->init_expansion(); /* we didn't just do an 'or' */ just_did_or_ = FALSE; /* we haven't yet returned the first token */ before_first_ = TRUE; } /* advance to the next alternative for expansion */ virtual int advance_expansion() { /* advance to the next state */ cur_alt_ = cur_alt_->next_; /* * if that was the last state, wrap back to the first state and * indicate a 'carry'; otherwise, indicate no carry */ if (cur_alt_ == 0) { /* we ran out of states - wrap back to the first one */ cur_alt_ = sub_head_; /* initialize expansion in the new subitem */ cur_alt_->init_expansion(); /* indicate that we've wrapped and should carry forward */ return TRUE; } else { /* initialize expansion in the new subitem */ cur_alt_->init_expansion(); /* this is another valid state - no carry */ return FALSE; } } /* clone the current expansion subtree */ virtual CTcPrsGramNode *clone_expansion() const { /* return a replica of the current alternative being expanded */ return cur_alt_->clone_expansion(); } /* get the next token in a token expansion */ const CTcToken *get_next_token() { const CTcToken *tok; /* * If we returned the synthesized '|' token between alternatives * last time, advance to the next alternative and return its first * token. */ if (just_did_or_) { /* we no longer just did an '|' */ just_did_or_ = FALSE; /* advance to the next alternative */ cur_alt_ = cur_alt_->next_; /* if there is no next alternative, there's nothing to return */ if (cur_alt_ == 0) return 0; /* initialize the new alternative */ cur_alt_->init_expansion(); /* if this alternative has no subitems, return another '|' */ if (cur_alt_->get_tok() == 0) { /* flag the '|' */ just_did_or_ = TRUE; /* if this was the last alternative, we're done */ if (cur_alt_->next_ == 0) return 0; /* * return the '|' again (we know it's already set up, * because we only get here if we did an '|' previously) */ return &or_tok_; } /* return its current token */ return cur_alt_->get_tok(); } /* * Get the first or next token in the current alternative, * depending on where we are. */ if (before_first_) { /* we're before the first token - get the current token */ tok = cur_alt_->get_tok(); /* we're now past the first token */ before_first_ = FALSE; } else { /* we're past the first token, so get the next one */ tok = cur_alt_->get_next_token(); } /* if we got a token from the current alternative, return it */ if (tok != 0) return tok; /* * We've run out of tokens in this alternative - synthesize an OR * token ('|') and return it, so the caller will know a new * top-level alternative follows. * * Do NOT synthesize an OR token after our last alternative. */ if (cur_alt_->next_ == 0) { /* this was our last alternative - we're done */ return 0; } /* we have another alternative - synthesize an OR */ or_tok_.settyp(TOKT_OR); or_tok_.set_text("|", 1); /* flag that we just did an '|' */ just_did_or_ = TRUE; /* return the synthesized '|' token */ return &or_tok_; } /* current alternative being expanded */ CTcPrsGramNode *cur_alt_; /* * my synthesized token for returning the '|' token between * alternatives during expansion */ CTcToken or_tok_; /* flag: we just returned the 'or' between two alternatives */ unsigned int just_did_or_ : 1; /* flag: we haven't yet returned the first token */ unsigned int before_first_ : 1; }; /* * Consolidate all of the OR's at the top of the tree. If we have any OR * subnodes, we'll rewrite ourselves as an OR of the concatenations of all * of the sub-OR's in all combinations. */ CTcPrsGramNode *CTcPrsGramNodeCat::consolidate_or() { CTcPrsGramNode *old_head; CTcPrsGramNodeOr *new_or; CTcPrsGramNode *cur; CTcPrsGramNode *nxt; int or_cnt; /* * Consolidate OR's in all subtrees. Before we do, stash away our * entire subtree, since we'll rebuild the tree from the updated * versions. */ old_head = sub_head_; sub_head_ = sub_tail_ = 0; /* run through our old list and build the new, consolidated list */ for (cur = old_head ; cur != 0 ; cur = nxt) { /* remember the next item in the old list */ nxt = cur->next_; /* consolidate the subtree and add it to our new list */ add_sub_item(cur->consolidate_or()); } /* * Count of OR nodes - if we don't have any, we don't have to make any * changes. Since we've already consolidated all OR's out of * sub-nodes into a single node at the top of each subtree, we don't * have to worry about OR's below our immediate children. */ for (or_cnt = 0, cur = sub_head_ ; cur != 0 ; cur = cur->next_) { /* if it's an OR node, count it */ if (cur->is_or()) ++or_cnt; } /* if we have no OR nodes, we need no changes */ if (or_cnt == 0) return this; /* create a new OR node to contain the list of expansions */ new_or = new (G_prsmem) CTcPrsGramNodeOr(); /* set up each OR node with the next alternative */ for (cur = sub_head_ ; cur != 0 ; cur = cur->next_) cur->init_expansion(); /* * Iterate through all of the possibilities. For each iteration, * we'll build the currently selected alternative, then we'll advance * by one alternative. We'll keep doing this until we've advanced * through all of the alternatives. */ for (;;) { CTcPrsGramNodeCat *new_cat; /* create a new CAT node for the current selected alternative */ new_cat = new (G_prsmem) CTcPrsGramNodeCat(); /* clone and add each current alternative to the new CAT node */ for (cur = sub_head_ ; cur != 0 ; cur = cur->next_) new_cat->add_sub_item(cur->clone_expansion()); /* * we've finished this entire expansion, so add it to the new main * OR we're building */ new_or->add_sub_item(new_cat); /* * Go through the list of subitems and advance to the next OR * state. Stop when we reach the first item that advances without * a 'carry'. If we're still carrying when we reach the last * item, we know we've wrapped around back to the first * alternative and hence are done. */ for (cur = sub_head_ ; cur != 0 ; cur = cur->next_) { /* advance this one; if it doesn't carry, we're done */ if (!cur->advance_expansion()) break; } /* * if we carried past the last item, we've wrapped around back to * the first alternative, so we're done */ if (cur == 0) break; } /* return the new main OR we built */ return new_or; } /* * Grammar tree node - token node. This is a terminal node representing a * single token. */ class CTcPrsGramNodeTok: public CTcPrsGramNode { public: CTcPrsGramNodeTok(const CTcToken *tok) { /* remember the token */ G_tok->copytok(&tok_, tok); } /* consolidate OR's */ virtual CTcPrsGramNode *consolidate_or() { /* since I'm a terminal node, there's nothing to do here */ return this; } /* clone the current expansion subtree */ virtual CTcPrsGramNode *clone_expansion() const { /* return a new token node with my same token value */ return new (G_prsmem) CTcPrsGramNodeTok(&tok_); } /* get my token */ virtual const CTcToken *get_tok() const { return &tok_; } /* my token */ CTcToken tok_; }; /* * Parse a 'grammar' statement's alternative list. */ void CTcParser::parse_gram_alts(int *err, CTcSymObj *gram_obj, CTcGramProdEntry *prod, CTcGramPropArrows *arrows, CTcGramAltFuncs *funcs) { /* set up the first alternative and add it to the production */ CTcGramProdAlt *alt = new (G_prsmem) CTcGramProdAlt(gram_obj, dict_cur_); if (prod != 0) prod->add_alt(alt); /* flatten the grammar rules */ CTcPrsGramNode *tree = flatten_gram_rule(err); if (tree == 0 || *err != 0) return; /* * install the tree as the nested token source, so that we read tokens * from our expanded set of grammar rules rather than from the original * input stream */ G_tok->set_external_source(tree); /* parse the token specification list */ for (int done = FALSE ; !done ; ) { CTcGramProdTok *tok; /* see what we have */ switch(G_tok->cur()) { case TOKT_LBRACK: { int skip_to_close; /* presume we won't need to skip anything */ skip_to_close = FALSE; /* make sure we're at the start of the alternative */ if (alt->get_tok_head() != 0) G_tok->log_warning(TCERR_GRAMMAR_QUAL_NOT_FIRST); /* skip the open bracket and make sure a symbol follows */ if (G_tok->next() == TOKT_SYM) { /* check what qualifier we have */ if (G_tok->cur_tok_matches("badness", 7)) { int val; /* parse the integer-valued qualifier */ val = parse_gram_qual_int(err, "badness", &done); if (*err != 0) return; /* set the badness for the alternative */ alt->set_badness(val); } else { /* not a recognized qualifier */ G_tok->log_error_curtok(TCERR_BAD_GRAMMAR_QUAL); /* skip remaining tokens to the ']' */ skip_to_close = TRUE; } /* check for proper close if we're is okay so far */ if (!skip_to_close) { /* make sure we're at the close bracket */ if (G_tok->cur() != TOKT_RBRACK) { /* log an error */ G_tok->log_error_curtok( TCERR_GRAMMAR_QUAL_REQ_RBRACK); /* skip to the bracket */ skip_to_close = TRUE; } else { /* skip the bracket */ G_tok->next(); } } } else { /* log the error */ G_tok->log_error_curtok(TCERR_GRAMMAR_QUAL_REQ_SYM); /* skip to the end */ skip_to_close = TRUE; } /* * If we encountered an error, skip ahead until we find * a ']' or something that looks like the end of the * statement. */ if (skip_to_close) { /* skip to the end of the qualifier */ parse_gram_qual_skip(err, &done); if (*err) return; } } /* done */ break; case TOKT_COLON: /* this is the end of the list - we're done */ G_tok->next(); done = TRUE; break; case TOKT_OR: /* * alternator - create a new alternative and add it to the * production's list */ alt = new (G_prsmem) CTcGramProdAlt(gram_obj, dict_cur_); if (prod != 0) prod->add_alt(alt); /* skip the '|' token and proceed to the next token list */ G_tok->next(); break; case TOKT_SSTR: case TOKT_DSTR: /* * quoted string (single or double) - this gives a literal * string to match */ tok = new (G_prsmem) CTcGramProdTok(); tok->set_match_literal(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* add it to the current alternative's list */ alt->add_tok(tok); /* * go check for an arrow (to assign the matching token to a * property) */ goto check_for_arrow; case TOKT_LT: /* * part-of-speech list - this gives a list, enclosed in angle * brackets, of part-of-speech properties that can be matched * by the token */ /* skip the open angle bracket */ G_tok->next(); /* set up a new token with a part-of-speech list */ tok = new (G_prsmem) CTcGramProdTok(); tok->set_match_part_list(); /* add it to the current alternative's list */ alt->add_tok(tok); /* keep going until we reach the closing bracket */ for (;;) { CTcSymbol *sym; /* see what we have */ switch(G_tok->cur()) { case TOKT_GT: /* * closing angle bracket - we're done, so go check for * an arrow to assign the match to a property */ goto check_for_arrow; case TOKT_EOF: /* log the error */ G_tok->log_error_curtok(TCERR_GRAMMAR_INVAL_TOK); /* give up */ *err = TRUE; return; case TOKT_SYM: /* * symbol - look it up, defining it as a property if * it's not already defined as a property */ sym = global_symtab_->find_or_def_prop_explicit( G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), FALSE); /* make sure it's a property */ if (sym != 0 && sym->get_type() == TC_SYM_PROP) { CTcSymProp *propsym = (CTcSymProp *)sym; /* add the property to the token's list */ tok->add_match_part_ele(propsym->get_prop()); } else { /* flag an error */ G_tok->log_error_curtok(TCERR_GRAMMAR_LIST_REQ_PROP); } /* we're done with the symbol, so skip it */ G_tok->next(); /* back for more */ break; default: /* * anything else is an error; assume the '>' was * missing, so just flag the error and then act as * though we reached the end of the list */ G_tok->log_error_curtok(TCERR_GRAMMAR_LIST_UNCLOSED); goto check_for_arrow; } } /* NOT REACHED */ case TOKT_SYM: /* * symbol token - this gives a part-of-speech or * sub-production match, depending on whether the symbol * refers to a property (in which case we have a * part-of-speech match) or an object (in which case we have a * sub-production match). */ { CTcSymbol *sym; /* look it up in the global symbol table */ sym = global_symtab_->find(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* * if it's not defined, and we're not in syntax-only mode, * provide a default definition of the symbol as a * production object */ if (sym == 0 && !syntax_only_) { /* it's undefined - presume it's a production */ CTcGramProdEntry *sub_prod = funcs->declare_gramprod( G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* if the symbol if still undefined it's an error */ if (sub_prod == 0) return; /* get the production's symbol */ sym = sub_prod->get_prod_sym(); } /* check what kind of symbol we have */ if (sym == 0) { /* * we're in parse-only mode and we have no symbol - * we can just ignore this until we're really * compiling */ tok = 0; } else if (sym->get_type() == TC_SYM_OBJ) { /* we know it's an object symbol - cast it */ CTcSymObj *objsym = (CTcSymObj *)sym; /* make sure it's a production */ if (objsym->get_metaclass() != TC_META_GRAMPROD) G_tok->log_error_curtok(TCERR_GRAMMAR_REQ_PROD); /* create the production token */ tok = new (G_prsmem) CTcGramProdTok(); tok->set_match_prod(objsym); } else if (sym->get_type() == TC_SYM_PROP) { CTcSymProp *propsym = (CTcSymProp *)sym; /* create the part-of-speech token */ tok = new (G_prsmem) CTcGramProdTok(); tok->set_match_part_of_speech(propsym->get_prop()); } else if (sym->get_type() == TC_SYM_ENUM) { /* get the enum symbol properly cast */ CTcSymEnum *enumsym = (CTcSymEnum *)sym; /* enforce the 'enum token' rule if applicable */ funcs->check_enum_tok(enumsym); /* create the token-type token */ tok = new (G_prsmem) CTcGramProdTok(); tok->set_match_token_type(enumsym->get_enum_id()); } else { /* it's an error */ G_tok->log_error_curtok(TCERR_GRAMMAR_REQ_OBJ_OR_PROP); /* no token */ tok = 0; } } /* if we have a token, add it to the alternative's list */ if (tok != 0) alt->add_tok(tok); check_for_arrow: /* skip the symbol and check for a '->' specification */ if (G_tok->next() == TOKT_ARROW) { /* skip the arrow and make sure a symbol follows */ if (G_tok->next() == TOKT_SYM) { CTcSymbol *sym; /* look it up */ sym = funcs->look_up_prop(G_tok->getcur(), FALSE); /* set the association if we got a symbol */ if (sym != 0) { CTcSymProp *propsym = (CTcSymProp *)sym; size_t i; /* set the property association for the token */ if (tok != 0) tok->set_prop_assoc(propsym->get_prop()); /* * Add the property to our list of assigned * properties - we'll use this to build the * grammar info list for the match object. Only * add the property if it's not already in the * list. */ for (i = 0 ; i < arrows->cnt ; ++i) { /* if we found it, stop looking */ if (arrows->prop[i] == propsym) break; } /* * if we didn't find it, and we have room for * another, add it */ if (i == arrows->cnt && arrows->cnt < arrows->max_arrows) { /* it's not there - add it */ arrows->prop[arrows->cnt++] = propsym; } } else { /* log an error */ G_tok->log_error_curtok(TCERR_GRAMMAR_ARROW_REQ_PROP); } /* skip the symbol */ G_tok->next(); } else { /* it's an error */ G_tok->log_error_curtok(TCERR_GRAMMAR_ARROW_REQ_PROP); } } break; case TOKT_TIMES: /* free-floating end token */ tok = new (G_prsmem) CTcGramProdTok(); tok->set_match_star(); /* add it to the current alternative's list */ alt->add_tok(tok); /* skip the star */ G_tok->next(); /* * this must be the last token in the alternative, so we * must have a ':' or '|' following */ if (G_tok->cur() != TOKT_OR && G_tok->cur() != TOKT_COLON) G_tok->log_error_curtok(TCERR_GRAMMAR_STAR_NOT_END); break; case TOKT_EOF: /* let the compiler interface decide whether it's an error */ funcs->on_eof(err); if (*err) return; /* in any case, it's the end of the object */ done = TRUE; break; case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_SEM: /* * they probably meant to end the statement, even though * this isn't the time or place to do so - log an error and * stop scanning */ G_tok->log_error_curtok(TCERR_GRAMMAR_INVAL_TOK); done = TRUE; break; default: /* log an error */ G_tok->log_error_curtok(TCERR_GRAMMAR_INVAL_TOK); /* * skip the offending token, and hope that they merely * inserted something invalid */ G_tok->next(); break; } } } /* * Flatten a set of grammar rules. Each time we find a parenthesized * alternator, we'll expand the alternatives, until we have no * parenthesized alternators. */ CTcPrsGramNode *CTcParser::flatten_gram_rule(int *err) { /* first, build the top-level 'or' tree */ CTcPrsGramNode *tree = parse_gram_or(err, 0); if (tree == 0 || *err != 0) return 0; /* move all of the OR's to a single OR at the top of the tree */ tree = tree->consolidate_or(); /* flatten CAT nodes in the resulting tree */ tree->flatten_cat(); /* * if the top-level node isn't an OR, insert an OR at the top - this * makes the tree always follow the same shape, which makes it easier * to step through the tokens in the expansion */ if (!tree->is_or()) { CTcPrsGramNodeOr *new_or; /* create an OR node for the root of the tree */ new_or = new (G_prsmem) CTcPrsGramNodeOr(); /* insert our old root as the single alternative under the OR */ new_or->add_sub_item(tree); /* the OR is now the root of the tree */ tree = new_or; } /* start reading from the first token in the tree */ tree->init_expansion(); /* return the tree */ return tree; } /* * Parse an OR expression in a grammar rule. Returns an OR node * representing the expression. */ CTcPrsGramNode *CTcParser::parse_gram_or(int *err, int level) { CTcPrsGramNodeOr *or_node; CTcPrsGramNode *sub; /* build our 'or' node */ or_node = new (G_prsmem) CTcPrsGramNodeOr(); /* if the rule is completely empty, warn about it */ if (level == 0 && G_tok->cur() == TOKT_COLON && !syntax_only_) G_tok->log_warning(TCERR_GRAMMAR_EMPTY); /* parse our initial 'cat' subnode */ sub = parse_gram_cat(err, level + 1); /* abort on error */ if (sub == 0 || *err != 0) return 0; /* add the subnode to our 'or' list */ or_node->add_sub_item(sub); /* keep going as long as we have more rules appended with '|' */ while (G_tok->cur() == TOKT_OR) { /* skip the '|' */ G_tok->next(); /* * if we're at the top level, and the next token is the closing * ':', warn about the empty last rule */ if (level == 0 && G_tok->cur() == TOKT_COLON && !syntax_only_) G_tok->log_warning(TCERR_GRAMMAR_ENDS_WITH_OR); /* parse the 'cat' subnode */ sub = parse_gram_cat(err, level + 1); /* abort on error */ if (sub == 0 || *err != 0) return 0; /* add the subnode to our 'or' list */ or_node->add_sub_item(sub); } /* return the 'or' list */ return or_node; } /* * Parse a concatenation expression in a grammar rule. Returns a CAT node * representing the expression. */ CTcPrsGramNode *CTcParser::parse_gram_cat(int *err, int level) { CTcPrsGramNodeCat *cat_node; CTcPrsGramNode *sub; /* build our concatenation node */ cat_node = new (G_prsmem) CTcPrsGramNodeCat(); /* add tokens to the cat list until we find the end of the list */ for (;;) { /* see what we have */ switch(G_tok->cur()) { case TOKT_LPAR: /* skip the paren */ G_tok->next(); /* parse the OR expression */ sub = parse_gram_or(err, level + 1); /* if that failed, abort */ if (sub == 0 || *err != 0) return 0; /* add the 'OR' to our concatenation expression */ cat_node->add_sub_item(sub); /* make sure we're at the close paren */ if (G_tok->cur() != TOKT_RPAR) G_tok->log_error_curtok(TCERR_GRAMMAR_REQ_RPAR_AFTER_GROUP); else G_tok->next(); /* * make sure we don't have an arrow immediately following; an * arrow isn't allowed after a close paren, because a group * isn't a true subproduction */ if (G_tok->cur() == TOKT_ARROW) G_tok->log_error(TCERR_GRAMMAR_GROUP_ARROW_NOT_ALLOWED); /* done with the 'or' expression */ break; case TOKT_OR: /* * the 'or' has lower precedence than concatenation, so we've * reached the end of the concatenation expression - simply * return what we have so far and let the caller figure out * where to go next */ return cat_node; case TOKT_COLON: case TOKT_SEM: case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_EOF: case TOKT_RPAR: /* we've reached the end of the rule */ return cat_node; default: /* anything else is simply concatenated to the list so far */ cat_node->add_sub_item( new (G_prsmem) CTcPrsGramNodeTok(G_tok->getcur())); /* skip the token and keep scanning */ G_tok->next(); break; } } } /* * Parse a grammar qualifier integer value */ int CTcParser::parse_gram_qual_int(int *err, const char *qual_name, int *stm_end) { CTcPrsNode *expr; CTcConstVal *cval; /* skip the qualifier name */ G_tok->next(); /* check for a missing expression */ if (G_tok->cur() == TOKT_RBRACK) { /* don't bother looking for an expression - it's not there */ expr = 0; } else { /* parse an expression */ expr = parse_expr(); } /* * if we didn't get an expression, or it doesn't have a constant * value, or the constant value is something other than an integer, * it's an error */ if (expr == 0 || (cval = expr->get_const_val()) == 0 || cval->get_type() != TC_CVT_INT) { /* log an error */ G_tok->log_error(TCERR_GRAMMAR_QUAL_REQ_INT, qual_name); /* skip to the closing bracket */ parse_gram_qual_skip(err, stm_end); /* we don't have a value to return; use zero by default */ return 0; } else { /* return the constant expression value */ return cval->get_val_int(); } } /* * Skip to the end of a grammar qualifier. This is used when a syntax * error occurs and we abandon parsing the rest of the qualifier. */ void CTcParser::parse_gram_qual_skip(int *err, int *stm_end) { /* scan until we find the end */ for (;;) { switch(G_tok->next()) { case TOKT_RBRACK: /* * that's the end of the mal-formed qualifier; skip the * bracket and keep going from here */ G_tok->next(); break; case TOKT_EOF: case TOKT_RBRACE: case TOKT_LBRACE: case TOKT_SEM: /* * probably the end of the statement - stop scanning, and * set the 'stm_end' flag to tell the caller that we're done * parsing the entire statement */ *stm_end = TRUE; return; default: /* skip everything else and just keep going */ break; } } } /* ------------------------------------------------------------------------ */ /* * Grammar production list entry */ CTcGramProdEntry::CTcGramProdEntry(CTcSymObj *prod_sym) { /* remember my object symbol */ prod_sym_ = prod_sym; /* not in a list yet */ nxt_ = 0; /* no alternatives yet */ alt_head_ = alt_tail_ = 0; /* not explicitly declared yet */ is_declared_ = FALSE; } /* * Add an alternative */ void CTcGramProdEntry::add_alt(CTcGramProdAlt *alt) { /* link it at the end of my list */ if (alt_tail_ != 0) alt_tail_->set_next(alt); else alt_head_ = alt; alt_tail_ = alt; /* this is now the last element in our list */ alt->set_next(0); } /* * Move my alternatives to a new owner */ void CTcGramProdEntry::move_alts_to(CTcGramProdEntry *new_entry) { CTcGramProdAlt *alt; CTcGramProdAlt *nxt; /* move each of my alternatives */ for (alt = alt_head_ ; alt != 0 ; alt = nxt) { /* remember the next alternative, since we're unlinking this one */ nxt = alt->get_next(); /* unlink this one from the list */ alt->set_next(0); /* link this one into the new owner's list */ new_entry->add_alt(alt); } /* there's nothing left in our list */ alt_head_ = alt_tail_ = 0; } /* ------------------------------------------------------------------------ */ /* * Grammar production alternative */ CTcGramProdAlt::CTcGramProdAlt(CTcSymObj *obj_sym, CTcDictEntry *dict) { /* remember the associated processor object */ obj_sym_ = obj_sym; /* remember the default dictionary currently in effect */ dict_ = dict; /* nothing in our token list yet */ tok_head_ = tok_tail_ = 0; /* we don't have a score or badness yet */ score_ = 0; badness_ = 0; /* we're not in a list yet */ nxt_ = 0; } void CTcGramProdAlt::add_tok(CTcGramProdTok *tok) { /* link the token at the end of my list */ if (tok_tail_ != 0) tok_tail_->set_next(tok); else tok_head_ = tok; tok_tail_ = tok; /* there's nothing after this token */ tok->set_next(0); } /* ------------------------------------------------------------------------ */ /* * Grammar production token object */ /* * Initialize with a part-of-speech list */ void CTcGramProdTok::set_match_part_list() { const size_t init_alo = 10; /* remember the type */ typ_ = TCGRAM_PART_OF_SPEECH_LIST; /* we have nothing in the list yet */ val_.prop_list_.len_ = 0; /* set the initial allocation size */ val_.prop_list_.alo_ = init_alo; /* allocate the initial list */ val_.prop_list_.arr_ = (tctarg_prop_id_t *)G_prsmem->alloc( init_alo * sizeof(val_.prop_list_.arr_[0])); } /* * Add a property to our part-of-speech match list */ void CTcGramProdTok::add_match_part_ele(tctarg_prop_id_t prop) { /* if necessary, re-allocate the array at a larger size */ if (val_.prop_list_.len_ == val_.prop_list_.alo_) { tctarg_prop_id_t *oldp; /* bump up the size a bit */ val_.prop_list_.alo_ += 10; /* remember the current list long enough to copy it */ oldp = val_.prop_list_.arr_; /* reallocate it */ val_.prop_list_.arr_ = (tctarg_prop_id_t *)G_prsmem->alloc( val_.prop_list_.alo_ * sizeof(val_.prop_list_.arr_[0])); /* copy the old list into the new one */ memcpy(val_.prop_list_.arr_, oldp, val_.prop_list_.len_ * sizeof(val_.prop_list_.arr_[0])); } /* * we now know we have space for the new element, so add it, bumping up * the length counter to account for the addition */ val_.prop_list_.arr_[val_.prop_list_.len_++] = prop; } qtads-2.1.7/tads3/tcprstyp.h000066400000000000000000000122111265017072300157140ustar00rootroot00000000000000/* $Header$ */ /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcprstyp.h - TADS 3 compiler - parser type definitions Function Defines some types for the TADS 3 compiler's parser. Separated from the main parser include file to reduce the amount of the parser that has to be included simply for the type definitions. Notes Modified 04/09/00 MJRoberts - Creation */ #ifndef TCPRSTYP_H #define TCPRSTYP_H /* ------------------------------------------------------------------------ */ /* * Expression evaluation constant value types. As we evaluate an * expression, we'll attempt to evaluate the constant elements of the * expression, so that the code we generate has any constant expressions * computed at compile-time rather than executed at run-time. */ enum tc_constval_type_t { TC_CVT_UNK, /* unknown type or non-constant value */ TC_CVT_NIL, /* nil */ TC_CVT_TRUE, /* true */ TC_CVT_INT, /* integer value */ TC_CVT_SSTR, /* single-quoted string value */ TC_CVT_RESTR, /* regex string value (R'...' or R"...") */ TC_CVT_LIST, /* list constant */ TC_CVT_OBJ, /* object reference */ TC_CVT_PROP, /* property pointer */ TC_CVT_FUNCPTR, /* function pointer */ TC_CVT_VOCAB_LIST, /* vocabulary list placeholder */ TC_CVT_ANONFUNCPTR, /* anonymous function pointer */ TC_CVT_ENUM, /* enumerator */ TC_CVT_FLOAT, /* floating point number */ TC_CVT_BIFPTR /* pointer to built-in function */ }; /* ------------------------------------------------------------------------ */ /* * Symbol types. These values are stored in symbol export files, so the * order of these entries must not be changed. If new entries are to be * added, they must be added at the end of the list, and existing * entries must not be deleted (instead, make an existing entry * obsolete, but leave its slot occupied). */ enum tc_symtype_t { /* unknown */ TC_SYM_UNKNOWN = 0, /* function */ TC_SYM_FUNC, /* object */ TC_SYM_OBJ, /* property */ TC_SYM_PROP, /* local variable */ TC_SYM_LOCAL, /* parameter */ TC_SYM_PARAM, /* built-in function */ TC_SYM_BIF, /* external function */ TC_SYM_EXTFN, /* code label */ TC_SYM_LABEL, /* metaclass */ TC_SYM_METACLASS, /* enumerator */ TC_SYM_ENUM, /* 'grammar token' */ TC_SYM_GRAMTOK, /* dynamic local (enclosing local for DynamicFunc compilation) */ TC_SYM_DYNLOCAL }; /* ------------------------------------------------------------------------ */ /* * Object metaclasses. These are the *internal* identifiers we use for the * T3 metaclasses that the compiler knows about. * * These identifiers don't correspond to anything at run-time or in the VM * - they're not "dependency table index" values (see the T3 VM spec), for * example. These are simply identifiers we use internally to keep track * of the metaclass of each static object we define. Note that we don't * need to list all of the metaclasses here; we only need internal * identifiers for the metaclasses which the compiler instantiates as * static objects. Beyond these, the source program can define any number * of additional metaclasses, for which we'll happily generate code for * *run-time* instantiation. But for static instantiation, we only can - * and only need to - generate code for this small set of metaclasses of * which the compiler itself is aware. */ enum tc_metaclass_t { /* invalid/unknown */ TC_META_UNKNOWN = -1, /* TADS object */ TC_META_TADSOBJ = 0, /* dictionary */ TC_META_DICT, /* grammar production */ TC_META_GRAMPROD, /* intrinsic class modifier */ TC_META_ICMOD }; /* ------------------------------------------------------------------------ */ /* * CTcPrsNode::adjust_for_dyn() information structure */ struct tcpn_dyncomp_info { tcpn_dyncomp_info() { debugger = FALSE; speculative = FALSE; stack_level = 0; } tcpn_dyncomp_info(int debugger, int speculative, int stack_level) { this->debugger = debugger; this->speculative = speculative; this->stack_level = stack_level; } /* true -> debugger evaluation */ int debugger; /* true -> speculative evaluation mode */ int speculative; /* * stack level - 0 is the active level, 1 is the first enclosing level, * and so on */ int stack_level; }; #endif /* TCPRSTYP_H */ qtads-2.1.7/tads3/tcsrc.cpp000066400000000000000000000621101265017072300155000ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/TCSRC.CPP,v 1.3 1999/07/11 00:46:55 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcsrc.cpp - source file reader Function Notes Modified 04/13/99 MJRoberts - Creation */ #include #include #include "os.h" #include "t3std.h" #include "tcsrc.h" #include "tcglob.h" #include "charmap.h" #include "vmdatasrc.h" /* ------------------------------------------------------------------------ */ /* * Source files */ /* * Construction */ CTcSrcFile::CTcSrcFile(osfildef *fp, class CCharmapToUni *mapper) { /* remember my source file */ fp_ = new CVmFileSource(fp); /* net yet at end of file */ at_eof_ = FALSE; /* there's no data in the buffer yet */ rem_ = 0; /* remember my character mapper */ mapper_ = mapper; } /* * Deletion */ CTcSrcFile::~CTcSrcFile() { /* close my source file */ if (fp_ != 0) delete fp_; /* release my character mapper */ if (mapper_ != 0) mapper_->release_ref(); } #if 0 // we don't currently need this, but keep the source in case it // becomes interesting later // /* ------------------------------------------------------------------------ */ /* * Open a plain ASCII file, with no #charset marker. */ CTcSrcFile *CTcSrcFile::open_plain(const char *filename) { osfildef *fp; char buf[5]; size_t siz; /* * open the file in binary mode, since we do all of the newline * interpretation explicitly */ if ((fp = osfoprb(filename, OSFTTEXT)) == 0) return 0; /* read the first few bytes of the file */ siz = osfrbc(fp, buf, sizeof(buf)); /* check for a 3-byte UTF-8 marker */ if (siz >= 3 && (uchar)buf[0] == 0xEF && (uchar)buf[1] == 0xBB && (uchar)buf[2] == 0xBF) { /* * seek to the byte after the marker, so that our caller won't see * the marker */ osfseek(fp, 3, OSFSK_SET); /* return a source file reader with a utf-8 mapper */ return new CTcSrcFile(fp, new CCharmapToUniUTF8()); } /* if we read at least two bytes, try auto-detecting UCS-2 */ if (siz >= 2) { /* if the first bytes are 0xFF 0xFE, it's UCS-2 low-byte first */ if ((unsigned char)buf[0] == 0xFF && (unsigned char)buf[1] == 0xFE) { /* seek to the byte after the marker */ osfseek(fp, 2, OSFSK_SET); /* return a reader with a little-endian mapper */ return new CTcSrcFile(fp, new CCharmapToUniUcs2Little()); } /* if the first bytes are 0xFE 0xFF, it's UCS-2 high-byte first */ if ((unsigned char)buf[0] == 0xFE && (unsigned char)buf[1] == 0xFF) { /* seek to the byte after the marker */ osfseek(fp, 2, OSFSK_SET); /* return a reader with a little-endian mapper */ return new CTcSrcFile(fp, new CCharmapToUniUcs2Big()); } } /* * there are no Unicode markers, so our only remaining option is plain * ASCII - return a source file object with a plain ASCII mapper */ return new CTcSrcFile(fp, new CCharmapToUniASCII()); } #endif /* ------------------------------------------------------------------------ */ /* * Open a plain ASCII source file. */ CTcSrcFile *CTcSrcFile::open_ascii(const char *filename) { osfildef *fp; /* * open the file in binary mode, since we do all of the newline * interpretation explicitly */ if ((fp = osfoprb(filename, OSFTTEXT)) == 0) return 0; /* return a source reader with a plain ASCII mapper */ return new CTcSrcFile(fp, new CCharmapToUniASCII()); } /* ------------------------------------------------------------------------ */ /* * Open a source file */ CTcSrcFile *CTcSrcFile::open_source(const char *filename, class CResLoader *res_loader, const char *default_charset, int *charset_error, int *default_charset_error) { char buf[275]; size_t siz; osfildef *fp; long startofs; CCharmapToUni *mapper; /* presume we won't find an invalid #charset directive */ *charset_error = FALSE; /* presume we'll have no problem with the default character set */ *default_charset_error = FALSE; /* * open the file in binary mode, so that we can scan the first few * bytes to see if we can detect the character set from information * at the beginning of the file */ fp = osfoprb(filename, OSFTTEXT); /* if we couldn't open the file, return failure */ if (fp == 0) return 0; /* note the starting offset in the file */ startofs = osfpos(fp); /* read the first few bytes of the file */ siz = osfrbc(fp, buf, sizeof(buf)); /* check for a 3-byte UTF-8 byte-order marker */ if (siz >= 3 && (uchar)buf[0] == 0xEF && (uchar)buf[1] == 0xBB && (uchar)buf[2] == 0xBF) { char *p; size_t rem; uint skip; /* skip at least the three-byte marker sequence */ skip = 3; /* * check for a #charset marker for utf-8 - this would be redundant, * but we'll allow it */ p = buf + 3; rem = siz - 3; if (rem > 9 && memcmp(p, "#charset ", 9) == 0) { /* skip spaces */ for (p += 9, rem -= 9 ; rem != 0 && (*p == ' ' || *p == '\t') ; ++p, --rem); /* check for valid character set markers */ if (rem >= 7 && memicmp(p, "\"utf-8\"", 7) == 0) { /* skip the whole sequence */ skip = (p + 7) - buf; } else if (rem >= 6 && memicmp(p, "\"utf8\"", 6) == 0) { /* skip the whole sequence */ skip = (p + 6) - buf; } } /* seek past the character set markers */ osfseek(fp, startofs + skip, OSFSK_SET); /* return a new utf-8 decoder */ return new CTcSrcFile(fp, new CCharmapToUniUTF8()); } /* if we read at least two bytes, try auto-detecting unicode */ if (siz >= 2) { CTcSrcFile *srcf; const char *const *cs_names; int bige; /* presume we won't find a byte-order marker */ srcf = 0; /* if the first bytes are 0xFF 0xFE, it's UCS-2 low-byte first */ if ((unsigned char)buf[0] == 0xFF && (unsigned char)buf[1] == 0xFE) { static const char *names[] = { "unicodel", "utf-16le", 0 }; /* create a UCS-2 little-endian reader */ srcf = new CTcSrcFile(fp, new CCharmapToUniUcs2Little()); bige = FALSE; cs_names = names; } /* if the first bytes are 0xFE 0xFF, it's UCS-2 high-byte first */ if ((unsigned char)buf[0] == 0xFE && (unsigned char)buf[1] == 0xFF) { static const char *names[] = { "unicodeb", "utf-16be", 0 }; /* create a UCS-2 little-endian reader */ srcf = new CTcSrcFile(fp, new CCharmapToUniUcs2Big()); bige = TRUE; cs_names = names; } /* if we found the byte-order marker, we know the character set */ if (srcf != 0) { uint skip; /* we at least want to skip the byte-order marker */ skip = 2; /* check to see if we have a '#charset' directive */ if (ucs_str_starts_with(buf + 2, siz - 2, "#charset ", bige, FALSE)) { char *p; size_t rem; /* scan past following spaces */ for (p = buf + 2 + 18, rem = siz - 2 - 18 ; rem >= 2 && (ucs_char_eq(p, ' ', bige, FALSE) || ucs_char_eq(p, '\t', bige, FALSE)) ; p += 2, rem -= 2) ; /* check for a '"' */ if (rem >= 2 && ucs_char_eq(p, '"', bige, FALSE)) { const char *const *n; /* skip the '"' */ p += 2; rem -= 2; /* * check for a match to any of the valid names for this * character set */ for (n = cs_names ; *n != 0 ; ++n) { /* if it's a match, stop scanning */ if (ucs_str_starts_with(p, rem, *n, bige, TRUE)) { size_t l; /* get the length of the name */ l = strlen(*n) * 2; /* check for a close quote */ if (rem >= l + 2 && ucs_char_eq(p + l, '"', bige, FALSE)) { /* skip the name and the quote */ p += l + 2; rem -= l + 2; /* skip the source text to this point */ skip = p - buf; /* stop scanning */ break; } } } } } /* seek just past the character set indicators */ osfseek(fp, startofs + skip, OSFSK_SET); /* return the file */ return srcf; } } /* * It doesn't appear to use UCS-2 encoding (at least, the file * doesn't start with a byte-order sensing sequence). Check to see * if the file starts with "#charset " in ASCII single-byte * characters. */ if (siz >= 9 && memcmp(buf, "#charset ", 9) == 0) { char *p; size_t rem; /* skip the #charset string and any following spaces */ for (p = buf + 9, rem = siz - 9 ; rem > 0 && (*p == ' ' || *p == '\t') ; ++p, --rem) ; /* make sure we're looking at a '"' */ if (rem != 0 && *p == '"') { char *charset_name; /* skip the open quote */ ++p; --rem; /* remember where the character set name starts */ charset_name = p; /* * find the closing quote, which must occur before a CR or * LF character */ for ( ; rem > 0 && *p != '"' && *p != 10 && *p != 13 ; ++p, --rem) ; /* make sure we found a matching quote */ if (rem != 0 && *p == '"') { /* seek just past the #charset string */ osfseek(fp, startofs + (p - buf) + 1, OSFSK_SET); /* * put a null terminator at the end of the character set * name */ *p = '\0'; /* create a mapper */ mapper = CCharmapToUni::load(res_loader, charset_name); /* * if that succeeded, return a reader for the mapper; * otherwise, simply proceed as though no #charset had * been present, so that we create a default mapper */ if (mapper != 0) { /* success - return a reader */ return new CTcSrcFile(fp, mapper); } else { /* tell the caller the #charset was invalid */ *charset_error = TRUE; } } } } /* * we didn't find any sensing codes, so seek back to the start of * the file */ osfseek(fp, startofs, OSFSK_SET); /* * We couldn't identify the file's character set based on anything * in the file, so create a mapper for the given default character * set. If there's not even a default character set defined, create * a plain ASCII mapper. */ if (default_charset != 0) mapper = CCharmapToUni::load(res_loader, default_charset); else mapper = new CCharmapToUniASCII(); /* check to see if we created a mapper */ if (mapper != 0) { /* return a source file reader based on the mapper */ return new CTcSrcFile(fp, mapper); } else { /* * we failed to create a mapper for the default character set - * flag the problem */ *default_charset_error = TRUE; /* close the input file */ osfcls(fp); /* return failure */ return 0; } } /* ------------------------------------------------------------------------ */ /* * Read a line of text from the file. */ size_t CTcSrcFile::read_line(char *buf, size_t bufl) { char *dst; /* start out writing to the start of the caller's buffer */ dst = buf; /* * Keep going until we run out of input file, fill up the buffer, or * reach the end of a line */ for (;;) { char *src; /* read some more data if our buffer is empty */ if (rem_ == 0) { /* load another buffer-full */ rem_ = mapper_->read_file(fp_, buf_, sizeof(buf_)); /* * If we didn't read anything, we've reached the end of the * file. If we've already copied anything into the caller's * buffer, null-terminate their buffer and return success; * otherwise, return failure, since the caller has already * read everything available from the file. */ if (rem_ == 0) { /* * Remember that we've reached the end of the file. * We're about to return the last of the data, so the * caller will not need to call us again (although it's * legal if they do - we'll just return a zero length on * the next call). */ at_eof_ = TRUE; /* check if we've copied anything to the caller's buffer */ if (buf == dst) { /* the caller's buffer is empty - return end of file */ return 0; } else { /* null-terminate the caller's buffer */ *dst++ = '\0'; /* * return the number of bytes copied, including the null * terminator */ return (dst - buf); } } /* start over at the beginning of the buffer */ p_ = buf_; } /* * Scan the input buffer one character (not byte) at a time. * Keep track of how much many bytes we've skipped. Stop when * we reach a CR or LF character, or when skipping another * character would exceed the remaining capacity of the caller's * buffer, or when we run out of data in our input buffer. */ for (src = p_ ; rem_ > 0 ; ) { size_t csiz; /* get the length of the current character */ csiz = utf8_ptr::s_charsize(*src); /* * if this character plus a null terminator wouldn't fit in * the output buffer, stop scanning */ if (csiz >= bufl) { /* * There's no more room in the caller's buffer. Copy * what we've scanned so far to the output buffer and * null-terminate the buffer. */ memcpy(dst, p_, src - p_); /* advance past the copied bytes and write the null byte */ dst += (src - p_); *dst++ = '\0'; /* advance the buffer read pointer over the copied bytes */ p_ = src; /* return success - indicate the number of bytes copied */ return (dst - buf); } /* * If it's a newline character of some kind, we're done with * this line. Note that we can just check the byte directly, * since if it's a multi-byte character, we'll never mistake * the first byte for a single-byte newline or carriage return * character, since a UTF-8 lead byte always has the high bit * set. * * Also treat the Unicode character 0x2028 (line separator) as * a newline. */ if (*src == '\n' || *src == '\r' || utf8_ptr::s_getch(src) == 0x2028) { char nl; /* copy what we've scanned so far to the caller's buffer */ memcpy(dst, p_, src - p_); /* advance past the copied bytes */ dst += src - p_; /* * add a newline to the caller's buffer -- always add a * '\n' newline, regardless of what kind of newline * sequence we found in the input; also add a null * terminator */ *dst++ = '\n'; *dst++ = '\0'; /* remember which type of newline we found */ nl = *src; /* advance past the newline */ p_ = src + csiz; rem_ -= csiz; /* * If the input buffer is empty, read more, so that we * can check the next character after the newline * character. */ if (rem_ == 0) { /* read more data */ rem_ = mapper_->read_file(fp_, buf_, sizeof(buf_)); /* start over at the start of the buffer */ p_ = buf_; } /* * Check for a paired newline character. If we found a * CR, check for an LF; if we found an LF, check for a * CR. This will ensure that we will recognize * essentially any newline character sequence for any * platform - this will accept CR, LF, CR-LF, or LF-CR * sequences. */ if (rem_ != 0 && ((nl == '\n' && *p_ == '\r') || (nl == '\r' && *p_ == '\n'))) { /* it's a paired newline - skip the second character */ ++p_; --rem_; } /* we've finished this line - return success */ return dst - buf; } /* skip this character in the input and proceed */ src += csiz; rem_ -= csiz; /* consider this character consumed in the caller's buffer */ bufl -= csiz; } /* * We've exhausted the current input buffer, without filling the * caller's buffer. Copy what we've skipped so far into the * caller's buffer. */ memcpy(dst, p_, src - p_); /* * Advance the output pointer past the data we just copied, then * continue looping to read more data from the input file. */ dst += src - p_; } } /* ------------------------------------------------------------------------ */ /* * Buffer reader source object */ /* * allocate */ CTcSrcMemory::CTcSrcMemory(const char *buf, size_t len, CCharmapToUni *mapper) { /* * Allocate a buffer for a UTF8-encoded copy of the buffer - * allocate three bytes per byte of the original, since this is the * worst case for expansion of the encoding. Allocate one extra * byte to ensure we have space for a null terminator. */ size_t alo_len = len*3; buf_alo_ = (char *)t3malloc(alo_len + 1); /* map the buffer */ char *p = buf_alo_; mapper->map(&p, &alo_len, buf, len); /* null-terminate the translated buffer */ *p = '\0'; /* start reading at the start of the translated buffer */ buf_ = buf_alo_; } /* * delete */ CTcSrcMemory::~CTcSrcMemory() { /* free our buffer */ t3free(buf_alo_); } /* * read next line */ size_t CTcSrcMemory::read_line(char *buf, size_t bufl) { char *dst; const char *src; /* if there's nothing left in our buffer, return EOF */ if (*buf_ == '\0') return 0; /* start out writing to the start of the caller's buffer */ dst = buf; /* * Scan the input buffer one character (not byte) at a time. Keep * track of how much many bytes we've skipped. Stop when we reach a * CR or LF character, or when skipping another character would * exceed the remaining capacity of the caller's buffer, or when we * run out of data in our input buffer. */ for (src = buf_ ; *src != '\0' ; ) { size_t csiz; /* get the length of the current character */ csiz = utf8_ptr::s_charsize(*src); /* * if this character plus a null terminator wouldn't fit in the * output buffer, stop scanning */ if (csiz >= bufl) { /* * There's no more room in the caller's buffer. Copy what * we've scanned so far to the output buffer and * null-terminate the buffer. */ memcpy(dst, buf_, src - buf_); /* advance past the copied bytes and write the null byte */ dst += (src - buf_); *dst++ = '\0'; /* advance the buffer read pointer over the copied bytes */ buf_ = src; /* return success - indicate the number of bytes copied */ return (dst - buf); } /* * If it's a newline character of some kind, we're done with this * line. Note that we can just check the byte directly, since if * it's a multi-byte character, we'll never mistake the first byte * for a single-byte newline or carriage return character, since a * UTF-8 lead byte always has the high bit set. Allow Unicode * character 0x2028 (line separator) as a newline as well. */ if (*src == '\n' || *src == '\r' || utf8_ptr::s_getch(src) == 0x2028) { char nl; /* copy what we've scanned so far to the caller's buffer */ memcpy(dst, buf_, src - buf_); /* advance past the copied bytes */ dst += src - buf_; /* * add a newline to the caller's buffer -- always add a '\n' * newline, regardless of what kind of newline sequence we * found in the input; also add a null terminator */ *dst++ = '\n'; *dst++ = '\0'; /* remember which type of newline we found */ nl = *src; /* advance past the newline */ buf_ = src + csiz; /* * Check for a paired newline character. If we found a CR, * check for an LF; if we found an LF, check for a CR. This * will ensure that we will recognize essentially any * newline character sequence for any platform - this will * accept CR, LF, CR-LF, or LF-CR sequences. */ if ((nl == '\n' && *buf_ == '\r') || (nl == '\r' && *buf_ == '\n')) { /* it's a paired newline - skip the second character */ ++buf_; } /* we've finished this line - return its length */ return dst - buf; } /* skip this character in the input and proceed */ src += csiz; /* consider this space consumed in the caller's buffer */ bufl -= csiz; } /* * We've exhausted the input buffer, without filling the caller's * buffer. Copy what we've skipped so far into the caller's buffer. */ memcpy(dst, buf_, src - buf_); dst += src - buf_; /* null-terminate the result buffer */ *dst++ = '\0'; /* advance our input pointer to the new (EOF) position */ buf_ = src; /* return the buffer length */ return dst - buf; } qtads-2.1.7/tads3/tcsrc.h000066400000000000000000000213411265017072300151460ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/TCSRC.H,v 1.3 1999/07/11 00:46:55 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcsrc.h - TADS3 compiler source file reader Function Provides I/O on source files, translating character sets between the file's character set and the internal UTF8 representation. A source file's character set is determined as follows: - If the first two bytes of the file are 0xFF, 0xFE, the file is taken as Unicode UCS-2 encoding, 16 bits per character, with low-order bytes stored first in each byte pair making up a character. (The special Unicode marker character is 0xFEFF, and the character 0xFFFE is specifically illegal. So, if we see FF FE in that order, we know that it must be taken to mean 0xFEFF, hence high byte second, because the other possibility - high byte first - would yield an invalid character.) - If the first two bytes of the file are 0xFE, 0xFF, the file is taken as Unicode UCS-2 encoding, 16 bits per character, with high-order bytes stored first. - If the first nine bytes of the contain the ASCII string "#charset" followed by an ASCII 0x20 byte, we obtain the name of the character set stored in the file by skipping past any following 0x20 or 0x09 bytes. The next character must be a double quote (0x22) character. We will scan characters up to the next double quote (0x22) character, which must occur within 256 bytes or before any CR or LF (0x0D, 0x0A) characters. The characters between the quotes will be taken as the character set name. - If none of the above information is found at the beginning of the file, we take the file to be in the current global default character set. Notes Modified 04/12/99 MJRoberts - Creation */ #ifndef TCSRC_H #define TCSRC_H #include /* ------------------------------------------------------------------------ */ /* * Generic source object */ class CTcSrcObject { public: CTcSrcObject() { } virtual ~CTcSrcObject() { } /* * Read the next line. Fills in the buffer with a null-terminated * string, ending in a newline if the line fits in the buffer, or * ending without a newline if not. Returns zero at end of file. */ virtual size_t read_line(char *buf, size_t buflen) = 0; /* have we reached the end of the file? */ virtual int at_eof() const = 0; }; /* ------------------------------------------------------------------------ */ /* * Source File base class */ class CTcSrcFile: public CTcSrcObject { public: /* * Create the source file reader. We take ownership of the mapper * object, so we'll delete it when we're deleted. */ CTcSrcFile(osfildef *fp, class CCharmapToUni *mapper); /* delete */ virtual ~CTcSrcFile(); /* * Read a line of text from the file. On success, returns the * length of the line read, including the null terminator character * and any newline character. At end of file or other error, * returns zero. Because the result is always null-terminated, a * return value of zero will never occur except on error. * * The result will be a UTF-8 string, and will always be * null-terminated. If the source line fits into the buffer, a * newline character ('\n') will be the last character of the * string. If the line is too long for the buffer, the result will * end with something other than a newline character, and the next * read_line() call will retrieve the remainder of the line (or as * much of the remainder as will fit into the buffer for that call). * The one exception is that, if the file does not end in a newline, * the last line will be returned without a newline; this condition * can be distinguished by the non-zero return value of the * subsequent call to read_line(). * * This routine translates from the source character set to UTF-8, * and automatically translates newline conventions. We handle CR, * LF, CR-LF, or LF-CR newlines (CR is ASCII 13, LF is ASCII 10). */ size_t read_line(char *buf, size_t bufl); /* determine if we've reached end of file */ int at_eof() const { return at_eof_; } /* * Open a source file. We'll scan the beginning of the file to * determine what type of source file reader to use, then create an * appropriate source file reader subclass to read the file. We * expect the filename to be limited to ASCII characters. * * If we can't identify the character set that the file uses, we'll * use the given default character set. If no default character set * is given, we'll create a plain ASCII reader. * * If we encounter a #charset directive, and we can't load the * desired character set map, we'll set *charset_error to true; * otherwise, we'll set *charset_error to false. Note that * *charset_error will be set to false if there's simply no #charset * directive. * * If we fail to open the default character set, we'll return null * and set *default_charset_error to true. */ static CTcSrcFile *open_source(const char *filename, class CResLoader *res_loader, const char *default_charset, int *charset_error, int *default_charset_error); /* * Open a plain ASCII source file or a Unicode file. This doesn't look * for a #charset marker, but it does check for Unicode byte-order * markers. If we find a Unicode byte-order marker, we'll read the * file using the suitable Unicode mapper; otherwise we'll read it * using a plain ASCII mapper. */ static CTcSrcFile *open_plain(const char *filename); /* * Open a plain ASCII source file. */ static CTcSrcFile *open_ascii(const char *filename); protected: /* * match the leading substring of a unicode utf-16 string to a given * ascii string */ static int ucs_str_starts_with(const char *ustr, size_t ulen, const char *astr, int big_endian, int case_fold) { /* compare each character of the unicode string to the ascii string */ for ( ; ulen >= 2 && *astr != '\0' ; ustr += 2, ulen -= 2, ++astr) { /* if the characters don't match, we don't have a match */ if (!ucs_char_eq(ustr, *astr, big_endian, case_fold)) return FALSE; } /* * if we reached the end of the ASCII string, we have a match; * otherwise, we ran out of the Unicode string before we ran out of * the ASCII string, so we don't have a match */ return (*astr == 0); } /* does a Unicode character match an ASCII character? */ static int ucs_char_eq(const char *ustr, char ac, int big_endian, int case_fold) { uchar lo, hi; uint uc; /* get this unicode character, translating its endianness */ if (big_endian) hi = (uchar)*ustr, lo = (uchar)*(ustr + 1); else lo = (uchar)*ustr, hi = (uchar)*(ustr + 1); uc = (hi << 8) + lo; /* if it's outside of ASCII range, we obviously can't match */ if (uc > 127) return FALSE; /* if we're folding case, convert both to lower case */ if (case_fold) ac = (char)tolower(ac), uc = tolower((char)uc); /* compare the characters */ return (ac == (char)uc); } /* end-of-file flag */ unsigned int at_eof_ : 1; /* my source file */ class CVmDataSource *fp_; /* read buffer */ char buf_[1024]; /* amount of data in the buffer */ size_t rem_; /* current position in buffer */ char *p_; /* my character mapper */ class CCharmapToUni *mapper_; }; /* ------------------------------------------------------------------------ */ /* * Memory buffer-based source reader */ class CTcSrcMemory: public CTcSrcObject { public: CTcSrcMemory(const char *buf, size_t len, class CCharmapToUni *mapper); ~CTcSrcMemory(); /* read the next line */ size_t read_line(char *buf, size_t bufl); /* determine if we've reached end of file */ int at_eof() const { return (*buf_ == '\0'); } private: /* allocated buffer */ char *buf_alo_; /* current buffer pointer */ const char *buf_; }; #endif /* TCSRC_H */ qtads-2.1.7/tads3/tct3.cpp000066400000000000000000012656521265017072300152600ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/tct3.cpp,v 1.5 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3.cpp - TADS 3 Compiler - T3 VM Code Generator Function Generate code for the T3 VM Notes Modified 05/08/99 MJRoberts - Creation */ #include #include #include "t3std.h" #include "os.h" #include "tcprs.h" #include "tct3.h" #include "tcgen.h" #include "vmtype.h" #include "vmwrtimg.h" #include "vmfile.h" #include "tcmain.h" #include "tcerr.h" #include "vmbignum.h" #include "vmrunsym.h" #include "vmhash.h" #include "vmmeta.h" #include "tct3unas.h" #include "vmop.h" /* ------------------------------------------------------------------------ */ /* * Named argument call table. The code body maintains a list of tables, * one per call with named arguments. At the end of the code body, we * generate the tables as part of the instruction stream. */ struct CTcNamedArgTab { CTcNamedArgTab(const CTcNamedArgs *args) { /* remember the argument list */ arglist = args->args; cnt = args->cnt; /* create a code label for the table's opcode stream location */ lbl = G_cs->new_label_fwd(); /* not in a list yet */ nxt = 0; } /* generate this table */ void generate() { /* define the label location and start the table */ CTcPrsNode::def_label_pos(lbl); G_cg->write_op(OPC_NAMEDARGTAB); /* write a placeholder for the table size */ ulong start = G_cs->get_ofs(); G_cs->write2(0); /* write the number of arguments in the list */ G_cs->write2(cnt); /* write placeholders for the index entries */ ulong idx = G_cs->get_ofs(), idx0 = idx; for (CTPNArg *arg = arglist->get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg()) { /* if it's a named argument, write an index placeholder */ if (arg->is_named_param()) G_cs->write2(0); } /* write a final index marker, for computing the last item's length */ G_cs->write2(0); /* write the strings */ for (CTPNArg *arg = arglist->get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg()) { /* if it's a named argument, write it out */ if (arg->is_named_param()) { /* write its index pointer */ G_cs->write2_at(idx, G_cs->get_ofs() - idx0); /* write the string */ G_cs->write(arg->get_name(), arg->get_name_len()); /* advance the index pointer */ idx += 2; } } /* write the final index marker */ G_cs->write2_at(idx, G_cs->get_ofs() - idx0); /* go back and fill in the table length prefix */ G_cs->write2_at(start, G_cs->get_ofs() - start - 2); } /* argument list */ const CTPNArglist *arglist; /* number of named arguments */ int cnt; /* code label for the table's location in the opcode stream */ CTcCodeLabel *lbl; /* next in list */ CTcNamedArgTab *nxt; }; /* ------------------------------------------------------------------------ */ /* * T3 Target Code Generator class */ /* * initialize the code generator */ CTcGenTarg::CTcGenTarg() { int i; /* * we haven't written any instructions yet - fill the pipe with * no-op instructions so that we don't think we can combine the * first instruction with anything previous */ last_op_ = OPC_NOP; second_last_op_ = OPC_NOP; /* * we haven't seen any strings or lists yet - set the initial * maximum lengths to zero */ max_str_len_ = 0; max_list_cnt_ = 0; /* we haven't generated any code yet */ max_bytecode_len_ = 0; /* there are no metaclasses defined yet */ meta_head_ = meta_tail_ = 0; meta_cnt_ = 0; /* no function sets defined yet */ fnset_head_ = fnset_tail_ = 0; fnset_cnt_ = 0; /* * Add the built-in metaclass entries. The order of these entries * is fixed to match the TCT3_METAID_xxx constants - if this order * changes, those constants must change to match. */ add_meta("tads-object"); add_meta("list"); add_meta("dictionary2/030000"); add_meta("grammar-production/030000"); add_meta("vector"); add_meta("anon-func-ptr"); add_meta("int-class-mod/030000"); add_meta("lookuptable/030003"); /* * Set up the initial translation table for translating from our * internal metaclass index values - TCT3_METAID_xxx - to the *actual* * image file dependency index. When we're compiling a program, these * are simply in our own internal order - index N in our scheme is * simply index N in the actual image file - because we get to lay out * exactly what the dependency table looks like. However, we need the * translation table for when we're being used as part of the debugger * - in those cases, we don't get to dictate the dependency table * layout, because the image file (and thus the dependency table) might * have been generated by a different version of the compiler. In * those cases, the image file loader will have to set up the * translation information for us. */ for (i = 0 ; i <= TCT3_METAID_LAST ; ++i) predef_meta_idx_[i] = i; /* start at the first valid property ID */ next_prop_ = 1; /* start at the first valid object ID */ next_obj_ = 1; /* allocate an initial sort buffer */ sort_buf_size_ = 4096; sort_buf_ = (char *)t3malloc(sort_buf_size_); /* not in a constructor */ in_constructor_ = FALSE; /* not in an operator overload */ in_op_overload_ = FALSE; /* no debug line record pointers yet */ debug_line_cnt_ = 0; debug_line_head_ = debug_line_tail_ = 0; /* normal (static compiler, non-debug) evaluation mode */ eval_for_dyn_ = FALSE; eval_for_debug_ = FALSE; speculative_ = FALSE; debug_stack_level_ = 0; /* no multi-method initializer object yet */ mminit_obj_ = VM_INVALID_OBJ; /* create the string interning hash table */ strtab_ = new CVmHashTable(1024, new CVmHashFuncCS(), TRUE); } /* * delete the code generator */ CTcGenTarg::~CTcGenTarg() { /* delete all of the metaclass list entries */ while (meta_head_ != 0) { tc_meta_entry *nxt; /* remember the next item */ nxt = meta_head_->nxt; /* delete this item */ t3free(meta_head_); /* move on */ meta_head_ = nxt; } /* delete all of the function set list entries */ while (fnset_head_ != 0) { tc_fnset_entry *nxt; /* remember the next item */ nxt = fnset_head_->nxt; /* delete this item */ t3free(fnset_head_); /* move on */ fnset_head_ = nxt; } /* delete our sort buffer */ t3free(sort_buf_); /* delete the debug line record pointers */ while (debug_line_head_ != 0) { tct3_debug_line_page *nxt; /* remember the next one */ nxt = debug_line_head_->nxt; /* delete this one */ t3free(debug_line_head_); /* move on */ debug_line_head_ = nxt; } /* delete the string table */ delete strtab_; } /* start loading the image file metaclass dependency table */ void CTcGenTarg::start_image_file_meta_table() { int i; /* * clear out all of the entries - set them to -1 to indicate that * they're invalid */ for (i = 0 ; i <= TCT3_METAID_LAST ; ++i) predef_meta_idx_[i] = -1; } /* * Load an image file metaclass dependency table. When we're being used in * a debugger, the image loader must call this with the run-time dependency * table to establish the translation from our internal metaclass * identifiers (TCT3_METAID_xxx) to the actual run-time index values. */ void CTcGenTarg::load_image_file_meta_table( const char *nm, size_t len, int idx) { /* * Check the name against our known names. If it matches one of the * known types, store the actual index in our translation table under * our internal index entry. If it's not a known name, ignore it - we * only care about the names we actually pre-define, because those are * the only ones we need to generate our own code for. */ if (len == 11 && memcmp(nm, "tads-object", 11) == 0) predef_meta_idx_[TCT3_METAID_TADSOBJ] = idx; else if (len == 4 && memcmp(nm, "list", 4) == 0) predef_meta_idx_[TCT3_METAID_LIST] = idx; else if (len == 11 && memcmp(nm, "dictionary2", 11) == 0) predef_meta_idx_[TCT3_METAID_DICT] = idx; else if (len == 18 && memcmp(nm, "grammar-production", 18) == 0) predef_meta_idx_[TCT3_METAID_GRAMPROD] = idx; else if (len == 6 && memcmp(nm, "vector", 6) == 0) predef_meta_idx_[TCT3_METAID_VECTOR] = idx; else if (len == 13 && memcmp(nm, "anon-func-ptr", 13) == 0) predef_meta_idx_[TCT3_METAID_ANONFN] = idx; else if (len == 13 && memcmp(nm, "int-class-mod", 13) == 0) predef_meta_idx_[TCT3_METAID_ICMOD] = idx; else if (len == 11 && memcmp(nm, "lookuptable", 11) == 0) predef_meta_idx_[TCT3_METAID_LOOKUP_TABLE] = idx; } /* * End the image file metaclass dependency table. */ void CTcGenTarg::end_image_file_meta_table() { int i; /* * Scan the metaclass translation table and make sure they've all been * set. If any are unset, it means that these metaclasses aren't * available; since we depend upon these metaclasses, this means that * we can't generate code interactively, so we can't act as a debugger. */ for (i = 0 ; i <= TCT3_METAID_LAST ; ++i) { /* if this entry hasn't been set properly, abort */ if (predef_meta_idx_[i] == -1) err_throw(VMERR_IMAGE_INCOMPAT_VSN_DBG); } } /* * Add an entry to the metaclass dependency table */ int CTcGenTarg::add_meta(const char *nm, size_t len, CTcSymMetaclass *sym) { tc_meta_entry *ent; size_t extra_len; const char *extra_ptr; const char *p; size_t rem; /* * if the name string doesn't contain a slash, allocate enough space * to add an implied version suffix of "/000000" */ for (p = nm, rem = len ; rem != 0 && *p != '/' ; ++p, --rem) ; if (rem == 0) { /* we didn't find a version suffix - add space for one */ extra_len = 7; extra_ptr = "/000000"; } else { /* * there's already a version suffix - but make sure we have * space for a six-character string */ if (rem < 7) { /* add zeros to pad out to a six-place version string */ extra_len = 7 - rem; extra_ptr = "000000"; } else { /* we need nothing extra */ extra_len = 0; extra_ptr = 0; } } /* allocate a new entry for the item */ ent = (tc_meta_entry *)t3malloc(sizeof(tc_meta_entry) + len + extra_len); if (ent == 0) err_throw(TCERR_CODEGEN_NO_MEM); /* copy the name into the entry */ memcpy(ent->nm, nm, len); /* add any extra version suffix information */ if (extra_len != 0) memcpy(ent->nm + len, extra_ptr, extra_len); /* null-terminate the name string in the entry */ ent->nm[len + extra_len] = '\0'; /* remember the symbol */ ent->sym = sym; /* link the entry in at the end of the list */ ent->nxt = 0; if (meta_tail_ != 0) meta_tail_->nxt = ent; else meta_head_ = ent; meta_tail_ = ent; /* count the entry, returning the index of the entry in the list */ return meta_cnt_++; } /* * Find a metaclass index given the global identifier. */ tc_meta_entry *CTcGenTarg::find_meta_entry(const char *nm, size_t len, int update_vsn, int *entry_idx) { tc_meta_entry *ent; int idx; size_t name_len; const char *p; const char *vsn; size_t vsn_len; size_t rem; /* find the version suffix, if any */ for (rem = len, p = nm ; rem != 0 && *p != '/' ; --rem, ++p) ; /* note the length of the name portion (up to the '/') */ name_len = len - rem; /* note the version string, if there is one */ if (rem != 0) { vsn = p + 1; vsn_len = rem - 1; } else { vsn = 0; vsn_len = 0; } /* search the existing entries */ for (idx = 0, ent = meta_head_ ; ent != 0 ; ent = ent->nxt, ++idx) { size_t ent_name_len; char *ent_vsn; /* find the version suffix in the entry */ for (ent_vsn = ent->nm ; *ent_vsn != '\0' && *ent_vsn != '/' ; ++ent_vsn) ; /* the name is the part up to the '/' */ ent_name_len = ent_vsn - ent->nm; /* note the length of the name and the version suffix */ if (*ent_vsn == '/') { /* the version is what follows the '/' */ ++ent_vsn; } else { /* there is no version suffix */ ent_vsn = 0; } /* if this is the one, return it */ if (ent_name_len == name_len && memcmp(ent->nm, nm, name_len) == 0) { /* * if this version number is higher than the version number * we previously recorded, remember the new, higher version * number */ if (update_vsn && ent_vsn != 0 && strlen(ent_vsn) == vsn_len && memcmp(vsn, ent_vsn, vsn_len) > 0) { /* store the new version string */ memcpy(ent_vsn, vsn, vsn_len); } /* tell the caller the index, and return the entry */ *entry_idx = idx; return ent; } } /* we didn't find it */ return 0; } /* * Get the property ID for the given method table index in the given * metaclass. The metaclass ID is given as the internal metaclass ID * (without the "/version" suffix), not as the external class name. * * The method table index is 0-based. */ CTcPrsNode *CTcGenTarg::get_metaclass_prop(const char *name, ushort idx) const { vm_prop_id_t propid; /* * If we have a loaded metaclass table from the running program, use * it. This will be the case if we're doing a dynamic or debugging * compilation. If we do, use it, since it has the direct information * on the loaded classes. * * If there's no metaclass table, this is an ordinary static * compilation, so get the metaclass information from the symbol table. */ if (G_metaclass_tab != 0) { /* look up the metaclass in the table */ vm_meta_entry_t *entry = G_metaclass_tab->get_entry_by_id(name); /* if we didn't find the entry, return failure */ if (entry == 0) return 0; /* * Get the property from the table. Note that xlat_func() uses a * 1-based index. */ if ((propid = entry->xlat_func(idx + 1)) == VM_INVALID_PROP) return 0; } else { /* static compilation - look up the metaclass in the symbol table */ CTcSymMetaclass *sym = G_cg->find_meta_sym(name, strlen(name)); if (sym == 0) return 0; /* get the metaclass method table entry at the given index */ CTcSymMetaProp *mprop = sym->get_nth_prop(idx); if (mprop == 0 || mprop->prop_ == 0) return 0; /* get the property ID from the method table entry */ propid = mprop->prop_->get_prop(); } /* set up a constant value for the property ID */ CTcConstVal pv; pv.set_prop(propid); /* return a constant parse node for the property ID */ return new CTPNConst(&pv); } /* * Find a metaclass symbol given the global identifier */ class CTcSymMetaclass *CTcGenTarg::find_meta_sym(const char *nm, size_t len) { tc_meta_entry *ent; int idx; /* find the entry */ ent = find_meta_entry(nm, len, TRUE, &idx); /* * if we found it, return the associated metaclass symbol; if * there's no entry, there's no symbol */ if (ent != 0) return ent->sym; else return 0; } /* * Find or add a metaclass entry */ int CTcGenTarg::find_or_add_meta(const char *nm, size_t len, CTcSymMetaclass *sym) { tc_meta_entry *ent; int idx; /* find the entry */ ent = find_meta_entry(nm, len, TRUE, &idx); /* if we found it, return the index */ if (ent != 0) { /* * found it - if it didn't already have a symbol mapping, use * the new symbol; if there is a symbol in the table entry * already, however, do not change it */ if (ent->sym == 0) ent->sym = sym; /* return the index */ return idx; } /* we didn't find an existing entry - add a new one */ return add_meta(nm, len, sym); } /* * Get the symbol for a given metaclass dependency table entry */ CTcSymMetaclass *CTcGenTarg::get_meta_sym(int meta_idx) { tc_meta_entry *ent; /* find the list entry at the given index */ for (ent = meta_head_ ; ent != 0 && meta_idx != 0 ; ent = ent->nxt, --meta_idx) ; /* if we didn't find the entry, do nothing */ if (ent == 0 || meta_idx != 0) return 0; /* return this entry's symbol */ return ent->sym; } /* * Get the external name for the given metaclass index */ const char *CTcGenTarg::get_meta_name(int meta_idx) const { tc_meta_entry *ent; /* find the list entry at the given index */ for (ent = meta_head_ ; ent != 0 && meta_idx != 0 ; ent = ent->nxt, --meta_idx) ; /* if we didn't find the entry, do nothing */ if (ent == 0 || meta_idx != 0) return 0; /* return this entry's external name */ return ent->nm; } /* * Set the symbol for a given metaclass dependency table entry */ void CTcGenTarg::set_meta_sym(int meta_idx, class CTcSymMetaclass *sym) { tc_meta_entry *ent; /* find the list entry at the given index */ for (ent = meta_head_ ; ent != 0 && meta_idx != 0 ; ent = ent->nxt, --meta_idx) ; /* if we didn't find the entry, do nothing */ if (ent == 0 || meta_idx != 0) return; /* set this entry's symbol */ ent->sym = sym; } /* * Add an entry to the function set dependency table */ ushort CTcGenTarg::add_fnset(const char *nm, size_t len) { tc_fnset_entry *ent; const char *sl; size_t sl_len; ushort idx; /* find the version part of the new name, if present */ for (sl = nm, sl_len = len ; sl_len != 0 && *sl != '/' ; ++sl, --sl_len) ; /* look for an existing entry with the same name prefix */ for (idx = 0, ent = fnset_head_ ; ent != 0 ; ent = ent->nxt, ++idx) { char *ent_sl; /* find the version part of this entry's name, if present */ for (ent_sl = ent->nm ; *ent_sl != '\0' && *ent_sl != '/' ; ++ent_sl) ; /* check to see if the prefixes match */ if (ent_sl - ent->nm == sl - nm && memcmp(ent->nm, nm, sl - nm) == 0) { /* * This one matches. Keep the one with the higher version * number. If one has a version number and the other doesn't, * keep the one with the version number. */ if (*ent_sl == '/' && sl_len != 0) { /* * Both have version numbers - keep the higher version. * Limit the version length to 6 characters plus the * slash. */ if (sl_len > 7) sl_len = 7; /* check if the new version number is higher */ if (memcmp(sl, ent_sl, sl_len) > 0) { /* the new one is higher - copy it over the old one */ memcpy(ent_sl, sl, sl_len); } /* * in any case, we're going to keep the existing entry, so * we're done - just return the existing entry's index */ return idx; } else if (*ent_sl == '/') { /* * only the old entry has a version number, so keep it and * ignore the new definition - this means we're done, so * just return the existing item's index */ return idx; } else { /* * Only the new entry has a version number, so store the * new version number. To do this, simply copy the new * entry over the old entry, but limit the version number * field to 7 characters including the slash. */ if (sl_len > 7) len -= (sl_len - 7); /* copy the new value */ memcpy(ent->nm, nm, len); /* done - return the existing item's index */ return idx; } } } /* * Allocate a new entry for the item. Always allocate space for a * version number, even if the entry doesn't have a version number - * if the part from the slash on is 7 characters or more, add nothing, * else add enough to pad it out to seven characters. */ ent = (tc_fnset_entry *)t3malloc(sizeof(tc_fnset_entry) + len + (sl_len < 7 ? 7 - sl_len : 0)); if (ent == 0) err_throw(TCERR_CODEGEN_NO_MEM); /* copy the name into the entry */ memcpy(ent->nm, nm, len); ent->nm[len] = '\0'; /* link the entry in at the end of the list */ ent->nxt = 0; if (fnset_tail_ != 0) fnset_tail_->nxt = ent; else fnset_head_ = ent; fnset_tail_ = ent; /* count the entry, returning the index of the entry in the list */ return fnset_cnt_++; } /* * get a function set's name given its index */ const char *CTcGenTarg::get_fnset_name(int idx) const { tc_fnset_entry *ent; /* scan the linked list to find the given index */ for (ent = fnset_head_ ; idx != 0 && ent != 0 ; ent = ent->nxt, --idx) ; /* return the one we found */ return ent->nm; } /* * Determine if we can skip an opcode because it is unreachable from the * previous instruction. */ int CTcGenTarg::can_skip_op() { /* * if the previous instruction was a return or throw of some kind, * we can skip any subsequent opcodes until a label is defined */ switch(last_op_) { case OPC_RET: case OPC_RETVAL: case OPC_RETTRUE: case OPC_RETNIL: case OPC_THROW: case OPC_JMP: case OPC_LRET: /* it's a return, throw, or jump - this new op is unreachable */ return TRUE; default: /* this new op is reachable */ return FALSE; } } /* * Remove the last JMP instruction */ void CTcGenTarg::remove_last_jmp() { /* a JMP instruction is three bytes long, so back up three bytes */ G_cs->dec_ofs(3); } /* * Add a line record */ void CTcGenTarg::add_line_rec(CTcTokFileDesc *file, long linenum) { /* include line records only in debug mode */ if (G_debug) { /* * clear the peephole, to ensure that the line boundary isn't * blurred by code optimization */ clear_peephole(); /* add the record to the code stream */ G_cs->add_line_rec(file, linenum); } } /* * Write an opcode to the output stream. We'll watch for certain * combinations of opcodes being generated, and apply peephole * optimization when we see sequences that can be collapsed to more * efficient single instructions. */ void CTcGenTarg::write_op(uchar opc) { int prv_len; int op_len; /* write the new opcode byte to the output stream */ G_cs->write((char)opc); /* we've only written one byte so far for the current instruction */ op_len = 1; /* presume the previous instruction length is just one byte */ prv_len = 1; /* * check for pairs of instructions that we can reduce to more * efficient single instructions */ try_combine: switch(opc) { case OPC_DUP: /* combine GETR0 + DUP -> DUPR0 */ if (last_op_ == OPC_GETR0) { opc = OPC_DUPR0; goto combine; } break; case OPC_DISC: /* combine DISC+DISC -> DISC1<2> */ if (last_op_ == OPC_DISC) { /* * Switch the old DISC to DISC1<2>. We're deleting a one-byte * op and replacing a one-byte op with a two-byte op, so we're * going from two bytes to two bytes total - no net deletion * (op_len = 0). The new DISC1 is a two-byte instruction * (prv_len). */ op_len = 0; prv_len = 2; opc = OPC_DISC1; /* write the <2> operand, overwriting the second DISC */ G_cs->write_at(G_cs->get_ofs() - 1, 2); /* go replace the op */ goto combine; } break; case OPC_DISC1: /* combine DISC1 + DISC -> DISC1 */ if (last_op_ == OPC_DISC && (uchar)G_cs->get_byte_at(G_cs->get_ofs() - 2) != 0xff) { /* up the DISC1 operand */ G_cs->write_at(G_cs->get_ofs() - 2, G_cs->get_byte_at(G_cs->get_ofs() - 2) + 1); /* keep the DISC1 */ opc = OPC_DISC1; prv_len = 2; /* apply the combination */ goto combine; } break; case OPC_JF: /* * if the last instruction was a comparison, we can use the * opposite compare-and-jump instruction */ switch(last_op_) { case OPC_NOT: /* invert the sense of the test */ opc = OPC_JT; goto combine; combine: /* * delete the new opcode we wrote, since we're going to combine * it with the preceding opcode */ G_cs->dec_ofs(op_len); /* overwrite the preceding opcode with the new combined opcode */ G_cs->write_at(G_cs->get_ofs() - prv_len, opc); /* roll back our internal peephole */ last_op_ = second_last_op_; second_last_op_ = OPC_NOP; /* * we've deleted our own opcode, so the current (most recent) * instruction in the output stream has the length of the * current opcode */ op_len = prv_len; /* presume the previous opcode is one byte again */ prv_len = 1; /* * go back for another try, since we may be able to do a * three-way combination (for example, GT/NOT/JT would * change to GT/JF, which would in turn change to JLE) */ goto try_combine; case OPC_EQ: opc = OPC_JNE; goto combine; case OPC_NE: opc = OPC_JE; goto combine; case OPC_LT: opc = OPC_JGE; goto combine; case OPC_LE: opc = OPC_JGT; goto combine; case OPC_GT: opc = OPC_JLE; goto combine; case OPC_GE: opc = OPC_JLT; goto combine; case OPC_GETR0: opc = OPC_JR0F; goto combine; } break; case OPC_JE: /* * if we just pushed nil, convert the PUSHNIL + JE to JNIL, since * we simply want to jump if a value is nil */ if (last_op_ == OPC_PUSHNIL) { /* convert it to a jump-if-nil */ opc = OPC_JNIL; goto combine; } break; case OPC_JNE: /* if we just pushed nil, convert to JNOTNIL */ if (last_op_ == OPC_PUSHNIL) { /* convert to jump-if-not-nil */ opc = OPC_JNOTNIL; goto combine; } break; case OPC_JT: /* * if the last instruction was a comparison, we can use a * compare-and-jump instruction */ switch(last_op_) { case OPC_NOT: /* invert the sense of the test */ opc = OPC_JF; goto combine; case OPC_EQ: opc = OPC_JE; goto combine; case OPC_NE: opc = OPC_JNE; goto combine; case OPC_LT: opc = OPC_JLT; goto combine; case OPC_LE: opc = OPC_JLE; goto combine; case OPC_GT: opc = OPC_JGT; goto combine; case OPC_GE: opc = OPC_JGE; goto combine; case OPC_GETR0: opc = OPC_JR0T; goto combine; } break; case OPC_NOT: /* * If the previous instruction was a comparison test of some * kind, we can invert the sense of the test. If the previous * instruction was a BOOLIZE op, we can eliminate it entirely, * because the NOT will perform the same conversion before * negating the value. If the previous was a NOT, we're * inverting an inversion; we can simply perform a single * BOOLIZE to get the same effect. */ switch(last_op_) { case OPC_EQ: opc = OPC_NE; goto combine; case OPC_NE: opc = OPC_EQ; goto combine; case OPC_GT: opc = OPC_LE; goto combine; case OPC_GE: opc = OPC_LT; goto combine; case OPC_LT: opc = OPC_GE; goto combine; case OPC_LE: opc = OPC_GT; goto combine; case OPC_BOOLIZE: opc = OPC_NOT; goto combine; case OPC_NOT: opc = OPC_BOOLIZE; goto combine; } break; case OPC_RET: /* * If we're writing a return instruction immediately after * another return instruction, we can skip the additional * instruction, since it will never be reached. This case * typically arises only when we generate the catch-all RET * instruction at the end of a function. */ switch(last_op_) { case OPC_RET: case OPC_RETVAL: case OPC_RETNIL: case OPC_RETTRUE: /* simply suppress this additional RET instruction */ return; } break; case OPC_RETNIL: /* we don't need to write two RETNIL's in a row */ if (last_op_ == OPC_RETNIL) return; break; case OPC_RETTRUE: /* we don't need to write two RETTRUE's in a row */ if (last_op_ == OPC_RETTRUE) return; break; case OPC_RETVAL: /* check the last opcode */ switch(last_op_) { case OPC_GETR0: /* * if we just pushed R0 onto the stack, we can compress the * GETR0 + RETVAL sequence into a simple RET, since RET leaves * the R0 value unchanged */ opc = OPC_RET; goto combine; case OPC_PUSHNIL: /* PUSHNIL + RET can be converted to RETNIL */ opc = OPC_RETNIL; goto combine; case OPC_PUSHTRUE: /* PUSHTRUE + RET can be converted to RETTRUE */ opc = OPC_RETTRUE; goto combine; } break; case OPC_SETLCL1: /* combine GETR0 + SETLCL1 -> SETLCL1R0 */ if (last_op_ == OPC_GETR0) { /* generate a combined SETLCL1R0 */ opc = OPC_SETLCL1R0; goto combine; } /* combine DUP + SETLCL1 -> GETSETLCL1 */ if (last_op_ == OPC_DUP) { opc = OPC_GETSETLCL1; goto combine; } /* combine DUPR0 + SETLCL1 -> GETSETLCL1R0 */ if (last_op_ == OPC_DUPR0) { opc = OPC_GETSETLCL1R0; goto combine; } break; break; case OPC_GETPROP: /* check the previous instruction for combination possibilities */ switch(last_op_) { case OPC_GETLCL1: /* get property of one-byte-addressable local */ opc = OPC_GETPROPLCL1; /* overwrite the preceding two-byte instruction */ prv_len = 2; goto combine; case OPC_GETLCLN0: case OPC_GETLCLN1: case OPC_GETLCLN2: case OPC_GETLCLN3: case OPC_GETLCLN4: case OPC_GETLCLN5: /* add a byte to pad things to the same length as a GETLCL1 */ G_cs->write(0); /* write the local number operand of the GETPROPLCL1 */ G_cs->write_at(G_cs->get_ofs() - 2, last_op_ - OPC_GETLCLN0); /* get property of one-byte-addressable local */ opc = OPC_GETPROPLCL1; /* overwrite the preceding now-two-byte instruction */ prv_len = 2; goto combine; case OPC_GETR0: /* get property of R0 */ opc = OPC_GETPROPR0; goto combine; } break; case OPC_CALLPROP: /* check the previous instruction */ switch(last_op_) { case OPC_GETR0: /* call property of R0 */ opc = OPC_CALLPROPR0; goto combine; } break; case OPC_INDEX: /* we can combine small integer constants with INDEX */ switch(last_op_) { case OPC_PUSH_0: case OPC_PUSH_1: /* * We can combine these into IDXINT8, but we must write an * extra byte for the index value. Go back and plug in the * extra index value byte, and add another byte at the end of * the stream to compensate for the insertion. (We're just * going to remove and overwrite everything after the inserted * byte, so don't bother actually fixing up that part with real * data; we merely need to make sure we have the right number * of bytes in the stream.) */ G_cs->write_at(G_cs->get_ofs() - 1, last_op_ == OPC_PUSH_0 ? 0 : 1); G_cs->write(0); /* combine the instructions */ opc = OPC_IDXINT8; prv_len = 2; goto combine; case OPC_PUSHINT8: /* combine the PUSHINT8 + INDEX into IDXINT8 */ opc = OPC_IDXINT8; prv_len = 2; goto combine; } break; case OPC_IDXINT8: /* we can replace GETLCL1 + IDXINT8 with IDXLCL1INT8 */ if (last_op_ == OPC_GETLCL1) { /* add the index operand at the second byte after the GETLCL1 */ uchar idx = G_cs->get_byte_at(G_cs->get_ofs() - op_len + 1); G_cs->write_at(G_cs->get_ofs() - op_len, idx); /* * we're going from GETLCL1 IDXINT8 (4 bytes) to * IDXLCL1INT8 (3 bytes), for a net deletion of one * byte - but that's only if we've already written the * operand, so the net deletion is actually op_len-1 */ op_len -= 1; /* go back and combine into what's now a three-byte opcode */ opc = OPC_IDXLCL1INT8; prv_len = 3; goto combine; } if (last_op_ >= OPC_GETLCLN0 && last_op_ <= OPC_GETLCLN5) { /* write the local# at the byte after the GETLCLNx */ G_cs->write_at(G_cs->get_ofs() - op_len, last_op_ - OPC_GETLCLN0); /* * if we haven't already written the IDXINT8 operand, write a * placeholder for it, since 'combine:' can't add bytes */ if (op_len == 1) G_cs->write(0); /* * We're going from GETLCLNx IDXINT8 (3 bytes) to * IDXLCL1INT8 (3 bytes), so there's no net deletion. */ op_len = 0; /* go back and combine into the three-byte index-by-local op */ opc = OPC_IDXLCL1INT8; prv_len = 3; goto combine; } break; case OPC_SETIND: /* we can replace GETLCL1 + + SETIND with SETINDLCL1I8 */ if (second_last_op_ == OPC_GETLCL1 || (second_last_op_ >= OPC_GETLCLN0 && second_last_op_ <= OPC_GETLCLN5)) { int idx, lcl; int getlcl_len, push_len; /* get the local number and the GETLCL instruction length */ if (second_last_op_ == OPC_GETLCL1) { /* it's GETLCL1 - two bytes */ getlcl_len = 2; } else { /* no operands - the local is encoded in the opcode itself */ lcl = second_last_op_ - OPC_GETLCLN0; getlcl_len = 1; } /* check the middle opcode for an int push */ switch(last_op_) { case OPC_PUSHINT8: /* get the index from the PUSHINT8 */ idx = (int)(uchar)G_cs->get_byte_at(G_cs->get_ofs() - 2); push_len = 2; /* get the local, if it's a GETLCL1 */ if (second_last_op_ == OPC_GETLCL1) lcl = G_cs->get_byte_at(G_cs->get_ofs() - push_len - 2); /* * PUSHINT8 pushes a signed value, but SETINDLCL1I8 uses an * unsigned value, so make sure the PUSHINT8 value is in * range */ if (idx < 127) goto combine_setind; /* not combinable */ break; case OPC_PUSHINT: /* get the int value */ idx = G_cs->read4_at(G_cs->get_ofs() - 5); push_len = 5; /* get the local, if it's a GETLCL1 */ if (second_last_op_ == OPC_GETLCL1) lcl = G_cs->get_byte_at(G_cs->get_ofs() - push_len - 2); /* if the index is 0-255, we can combine the instructions */ if (idx >= 0 && idx <= 255) goto combine_setind; /* not combinable */ break; case OPC_PUSH_0: case OPC_PUSH_1: /* it's a one-byte PUSH */ idx = last_op_ - OPC_PUSH_0; push_len = 1; /* get the local, if it's a GETLCL1 */ if (second_last_op_ == OPC_GETLCL1) lcl = G_cs->get_byte_at(G_cs->get_ofs() - push_len - 2); combine_setind: /* write the operands of the SETINDLCLI8 */ G_cs->write_at( G_cs->get_ofs() - getlcl_len - push_len, (uchar)lcl); G_cs->write_at( G_cs->get_ofs() - getlcl_len - push_len + 1, (uchar)idx); /* * Go back and do the combine. Remove GETLCL, * PUSH, and SETIND (getlcl_len + push_len + 1 bytes), * and replace them with SETINDLCL1I8 (3 bytes). * The net deletion goes in op_len, and the new length goes * in prv_len. Also, since we're combining three * instructions into one, we're losing our second-to-last * opcode. */ opc = OPC_SETINDLCL1I8; second_last_op_ = OPC_NOP; op_len = getlcl_len + push_len - 2; prv_len = 3; goto combine; } } break; default: /* write this instruction as-is */ break; } /* remember the last opcode we wrote */ second_last_op_ = last_op_; last_op_ = opc; } /* * Write a CALLPROP instruction, combining with preceding opcodes if * possible. */ void CTcGenTarg::write_callprop(int argc, int varargs, vm_prop_id_t prop) { /* * if the previous instruction was GETLCL1, combine it with the * CALLPROP to form a single CALLPROPLCL1 instruction */ if (last_op_ == OPC_GETLCL1 || (last_op_ >= OPC_GETLCLN0 && last_op_ <= OPC_GETLCLN5)) { uchar lcl; /* if it was a GETLCLNx, expand it out to a GETLCL1 */ if (last_op_ == OPC_GETLCL1) { /* it's already a GETLCL1 - just get the index */ lcl = G_cs->get_byte_at(G_cs->get_ofs() - 1); /* back up and delete the GETLCL1 instruction */ G_cs->dec_ofs(2); } else { /* it's a GETLCLNx - infer the local number from the opcode */ lcl = last_op_ - OPC_GETLCLN0; /* delete the instruction */ G_cs->dec_ofs(1); } /* roll back the peephole for the instruction deletion */ last_op_ = second_last_op_; second_last_op_ = OPC_NOP; /* write the varargs modifier if appropriate */ if (varargs) write_op(OPC_VARARGC); /* write the CALLPROPLCL1 */ write_op(OPC_CALLPROPLCL1); G_cs->write((char)argc); G_cs->write(lcl); G_cs->write_prop_id(prop); } else { /* generate the varargs modifier if appropriate */ if (varargs) write_op(OPC_VARARGC); /* we have arguments - generate a CALLPROP */ write_op(OPC_CALLPROP); G_cs->write((char)argc); G_cs->write_prop_id(prop); } /* callprop removes arguments and the object */ note_pop(argc + 1); } /* * Note a string's length */ void CTcGenTarg::note_str(size_t len) { /* if it's the longest so far, remember it */ if (len > max_str_len_) { /* * flag an warning the length plus overhead would exceed 32k * (only do this the first time we cross this limit) */ if (len > (32*1024 - VMB_LEN) && max_str_len_ <= (32*1024 - VMB_LEN)) G_tok->log_warning(TCERR_CONST_POOL_OVER_32K); /* remember the length */ max_str_len_ = len; } } /* * note a list's length */ void CTcGenTarg::note_list(size_t element_count) { /* if it's the longest list so far, remember it */ if (element_count > max_list_cnt_) { /* flag a warning if the stored length would be over 32k */ if (element_count > ((32*1024 - VMB_LEN) / VMB_DATAHOLDER) && max_list_cnt_ <= ((32*1024 - VMB_LEN) / VMB_DATAHOLDER)) G_tok->log_warning(TCERR_CONST_POOL_OVER_32K); /* remember the length */ max_list_cnt_ = element_count; } } /* * Note a bytecode block length */ void CTcGenTarg::note_bytecode(ulong len) { /* if it's the longest bytecode block yet, remember it */ if (len > max_bytecode_len_) { /* flag a warning the first time we go over 32k */ if (len >= 32*1024 && max_bytecode_len_ < 32*1024) G_tok->log_warning(TCERR_CODE_POOL_OVER_32K); /* remember the new length */ max_bytecode_len_ = len; } } /* * Hash table entry for an interned string. This keeps track of a string * that we've previously generated to the data segment, for reuse if we * need to generate another copy of the same string literal. */ class CInternEntry: public CVmHashEntryCS { public: CInternEntry(const char *str, size_t len, CTcStreamAnchor *anchor) : CVmHashEntryCS(str, len, FALSE) { anchor_ = anchor; } /* the string's fixup list anchor */ CTcStreamAnchor *anchor_; }; /* * Add a string to the constant pool */ void CTcGenTarg::add_const_str(const char *str, size_t len, CTcDataStream *ds, ulong ofs) { /* * If it's a short string, look to see if we've already written it. If * so, we can reuse the previous copy. The odds of a string being * duplicated generally decrease rapidly with the length of the string, * and the cost of checking for an old copy increases, so don't bother * if the string is over a threshold length. */ const size_t intern_threshold = 40; if (len < intern_threshold) { /* look for an old copy */ CInternEntry *e = (CInternEntry *)strtab_->find(str, len); /* * If we found it, simply add a fixup for the new use to the * existing fixup list. This will cause the caller to point the * new literal to the existing copy, without generating a duplicate * copy in the data segment. */ if (e != 0) { /* * add a fixup to the existing string's fixup list for the * caller's new reference */ CTcAbsFixup::add_abs_fixup(e->anchor_->fixup_list_head_, ds, ofs); /* we're done */ return; } } /* * Add an anchor for the item, and add a fixup for the reference * from ds@ofs to the item. */ CTcStreamAnchor *anchor = G_ds->add_anchor(0, 0); CTcAbsFixup::add_abs_fixup(anchor->fixup_list_head_, ds, ofs); /* * If this is a short string, add it to our list of interned strings. * This will let us reuse this copy of the string in the data segment * if we need to generate another literal later with identical * contents. */ if (len < intern_threshold) strtab_->add(new CInternEntry(str, len, anchor)); /* write the length prefix */ G_ds->write2(len); /* write the string bytes */ G_ds->write(str, len); /* note the length of the string stored */ note_str(len); } /* * Add a list to the constant pool */ void CTcGenTarg::add_const_list(CTPNList *lst, CTcDataStream *ds, ulong ofs) { int i; CTPNListEle *cur; ulong dst; CTcStreamAnchor *anchor; /* * Add an anchor for the item, and add a fixup for the reference * from ds@ofs to the item. */ anchor = G_ds->add_anchor(0, 0); CTcAbsFixup::add_abs_fixup(anchor->fixup_list_head_, ds, ofs); /* * Reserve space for the list. We need to do this first, because * the list might contain elements which themselves must be written * to the data stream; we must therefore reserve space for the * entire list before we start writing its elements. */ dst = G_ds->reserve(2 + lst->get_count()*VMB_DATAHOLDER); /* set the length prefix */ G_ds->write2_at(dst, lst->get_count()); dst += 2; /* store the elements */ for (i = 0, cur = lst->get_head() ; cur != 0 ; ++i, cur = cur->get_next(), dst += VMB_DATAHOLDER) { CTcConstVal *ele; /* get this element */ ele = cur->get_expr()->get_const_val(); /* write it to the element buffer */ write_const_as_dh(G_ds, dst, ele); } /* make sure the list wasn't corrupted */ if (i != lst->get_count()) G_tok->throw_internal_error(TCERR_CORRUPT_LIST); /* note the number of elements in the list */ note_list(lst->get_count()); } /* * Generate a BigNumber object */ vm_obj_id_t CTcGenTarg::gen_bignum_obj( const char *txt, size_t len, int promoted) { /* if the value was promoted from integer due to overflow, warn */ if (promoted) G_tok->log_warning(TCERR_INT_CONST_OV); /* write to BigNumber object stream */ CTcDataStream *str = G_bignum_stream; /* generate a new object ID for the BigNumber */ vm_obj_id_t id = new_obj_id(); /* * add the object ID to the non-symbol object list - this is * necessary to ensure that the object ID is fixed up during linking */ G_prs->add_nonsym_obj(id); /* * Get the BigNumber representation, inferring the radix. The * tokenizer can pass us integer strings with '0x' (hex) or '0' (octal) * prefixes when the numbers they contain are too big for the 32-bit * signed integer type. Floating-point values never have a radix * prefix. */ size_t bnlen; char *bn = CVmObjBigNum::parse_str_alo(bnlen, txt, len, 0); /* write the OBJS header - object ID plus byte count for metaclass data */ str->write_obj_id(id); str->write2(bnlen); /* write the BigNumber contents */ str->write(bn, bnlen); /* free the BigNumber value */ delete [] bn; /* return the new object ID */ return id; } /* * Generate a RexPattern object */ vm_obj_id_t CTcGenTarg::gen_rexpat_obj(const char *txt, size_t len) { /* write to RexPattern object stream */ CTcDataStream *rs = G_rexpat_stream; /* generate a new object ID for the RexPattern object */ vm_obj_id_t id = new_obj_id(); /* add the object ID to the non-symbol object list */ G_prs->add_nonsym_obj(id); /* * write the OBJS header - object ID plus byte count for metaclass data * (a DATAHOLDER pointing to the constant string) */ rs->write_obj_id(id); rs->write2(VMB_DATAHOLDER); /* * Add the source text as an ordinary constant string. Generate its * fixup in the RexPattern stream, in the DATAHOLDER we're about to * write. */ add_const_str(txt, len, rs, rs->get_ofs() + 1); /* * Set up the constant string value. Use zero as the pool offset, * since the fixup for the constant string will fill this in when the * pool layout is finalized. */ vm_val_t val; val.set_sstring(0); /* write the RexPattern data (a DATAHOLDER for the const string) */ char buf[VMB_DATAHOLDER]; vmb_put_dh(buf, &val); rs->write(buf, VMB_DATAHOLDER); /* return the new object ID */ return id; } /* * Convert a constant value from a CTcConstVal (compiler internal * representation) to a vm_val_t (interpreter representation). */ void CTcGenTarg::write_const_as_dh(CTcDataStream *ds, ulong ofs, const CTcConstVal *src) { vm_val_t val; char buf[VMB_DATAHOLDER]; /* convert according to the value's type */ switch(src->get_type()) { case TC_CVT_NIL: val.set_nil(); break; case TC_CVT_TRUE: val.set_true(); break; case TC_CVT_INT: val.set_int(src->get_val_int()); break; case TC_CVT_FLOAT: /* generate the BigNumber object */ val.set_obj(gen_bignum_obj(src->get_val_float(), src->get_val_float_len(), src->is_promoted())); /* add a fixup for the object ID */ if (G_keep_objfixups) CTcIdFixup::add_fixup(&G_objfixup, ds, ofs + 1, val.val.obj); break; case TC_CVT_SSTR: /* * Store the string in the constant pool. Note that our fixup * is at the destination stream offset plus one, since the * DATAHOLDER has the type byte followed by the offset value. */ add_const_str(src->get_val_str(), src->get_val_str_len(), ds, ofs + 1); /* * set the offset to zero for now - the fixup that * add_const_str() generates will take care of supplying the * real value */ val.set_sstring(0); break; case TC_CVT_RESTR: /* generate the RexPattern object */ val.set_obj(gen_rexpat_obj(src->get_val_str(), src->get_val_str_len())); /* add a fixup for the object ID */ if (G_keep_objfixups) CTcIdFixup::add_fixup(&G_objfixup, ds, ofs + 1, val.val.obj); break; case TC_CVT_LIST: /* * Store the sublist in the constant pool. Our fixup is at the * destination stream offset plus one, since the DATAHOLDER has * the type byte followed by the offset value. */ add_const_list(src->get_val_list(), ds, ofs + 1); /* * set the offset to zero for now - the fixup that * add_const_list() generates will take care of supplying the * real value */ val.set_list(0); break; case TC_CVT_OBJ: /* set the object ID value */ val.set_obj((vm_obj_id_t)src->get_val_obj()); /* * add a fixup (at the current offset plus one, for the type * byte) if we're keeping object ID fixups */ if (G_keep_objfixups) CTcIdFixup::add_fixup(&G_objfixup, ds, ofs + 1, src->get_val_obj()); break; case TC_CVT_ENUM: /* set the enum value */ val.set_enum(src->get_val_enum()); /* add a fixup */ if (G_keep_enumfixups) CTcIdFixup::add_fixup(&G_enumfixup, ds, ofs + 1, src->get_val_enum()); break; case TC_CVT_PROP: /* set the property ID value */ val.set_propid((vm_prop_id_t)src->get_val_prop()); /* * add a fixup (at the current offset plus one, for the type * byte) if we're keeping property ID fixups */ if (G_keep_propfixups) CTcIdFixup::add_fixup(&G_propfixup, ds, ofs + 1, src->get_val_prop()); break; case TC_CVT_FUNCPTR: /* * use a placeholder value of zero for now - a function's final * address is never known until after all code generation has * been completed (the fixup will take care of supplying the * correct value when the time comes) */ val.set_fnptr(0); /* * Add a fixup. The fixup is at the destination stream offset * plus one, because the DATAHOLDER has a type byte followed by * the function pointer value. */ src->get_val_funcptr_sym()->add_abs_fixup(ds, ofs + 1); break; case TC_CVT_ANONFUNCPTR: /* use a placeholder of zero for now, until we fix up the pointer */ val.set_fnptr(0); /* add a fixup for the code body */ src->get_val_anon_func_ptr()->add_abs_fixup(ds, ofs + 1); break; case TC_CVT_VOCAB_LIST: /* * it's an internal vocabulary list type - this is used as a * placeholder only, and will be replaced during linking with an * actual vocabulary string list */ val.typ = VM_VOCAB_LIST; break; case TC_CVT_UNK: case TC_CVT_BIFPTR: /* unknown - ignore it */ break; } /* write the vm_val_t in DATA_HOLDER format into the stream */ vmb_put_dh(buf, &val); ds->write_at(ofs, buf, VMB_DATAHOLDER); } /* * Write a DATAHOLDER at the current offset in a stream */ void CTcGenTarg::write_const_as_dh(CTcDataStream *ds, const CTcConstVal *src) { /* write to the current stream offset */ write_const_as_dh(ds, ds->get_ofs(), src); } /* * Notify that parsing is finished */ void CTcGenTarg::parsing_done() { /* nothing special to do */ } /* * notify the code generator that we're replacing an object */ void CTcGenTarg::notify_replace_object(ulong stream_ofs) { uint flags; /* set the 'replaced' flag in the flags prefix */ flags = G_os->read2_at(stream_ofs); flags |= TCT3_OBJ_REPLACED; G_os->write2_at(stream_ofs, flags); } /* * Set the starting offset of the current method */ void CTcGenTarg::set_method_ofs(ulong ofs) { /* tell the exception table object about it */ get_exc_table()->set_method_ofs(ofs); /* remember it in the code stream */ G_cs->set_method_ofs(ofs); } /* ------------------------------------------------------------------------ */ /* * Method header generator */ /* * Open a method */ void CTcGenTarg::open_method( CTcCodeStream *stream, CTcSymbol *fixup_owner_sym, CTcAbsFixup **fixup_list_head, CTPNCodeBody *code_body, CTcPrsSymtab *goto_tab, int argc, int opt_argc, int varargs, int is_constructor, int is_op_overload, int is_self_available, tct3_method_gen_ctx *ctx) { /* set the code stream as the current code generator output stream */ G_cs = ctx->stream = stream; /* set the method properties in the code generator */ set_in_constructor(is_constructor); set_in_op_overload(is_op_overload); stream->set_self_available(is_self_available); /* we obviously can't combine any past instructions */ clear_peephole(); /* clear the old line records */ stream->clear_line_recs(); /* clear the old frame list */ stream->clear_local_frames(); /* clear the old exception table */ get_exc_table()->clear_table(); /* reset the stack depth counters */ reset_sp_depth(); /* there are no enclosing 'switch' or block statements yet */ stream->set_switch(0); stream->set_enclosing(0); /* * remember where the method header starts - we'll need to come back * and fix up some placeholder entries in "Close" */ ctx->method_ofs = stream->get_ofs(); /* set the method start offset in the code generator */ set_method_ofs(ctx->method_ofs); /* set up a fixup anchor for the new method */ ctx->anchor = stream->add_anchor(fixup_owner_sym, fixup_list_head); /* set the anchor in the associated symbol, if applicable */ if (fixup_owner_sym != 0) fixup_owner_sym->set_anchor(ctx->anchor); /* * Generate the function header. At the moment, we don't know the * stack usage, exception table offset, or debug record offset, since * these all come after the byte code; we won't know how big the byte * code is until after we generate it. For now, write zero bytes as * placeholders for these slots; we'll come back and fix them up to * their real values after we've generated the byte code. */ stream->write(argc | (varargs ? 0x80 : 0)); /* argument count */ stream->write((char)opt_argc); /* additional optional argument count */ stream->write2(0); /* number of locals - won't know until after codegen */ stream->write2(0); /* total stack - won't know until after codegen */ stream->write2(0); /* exception table offset - presume none */ stream->write2(0); /* debug record offset - presume no debug records */ /* * If the header size in the globals is greater than the fixed size we * write, pad out the remainder with zero bytes. */ for (int i = G_sizes.mhdr - 10 ; i > 0 ; --i) stream->write(0); /* remember the starting offset of the code */ ctx->code_start_ofs = stream->get_ofs(); /* set the current code body being generated */ ctx->old_code_body = stream->set_code_body(code_body); /* get the 'goto' symbol table for this function */ stream->set_goto_symtab(goto_tab); } /* * Close a method */ void CTcGenTarg::close_method(int local_cnt, CTcPrsSymtab *local_symtab, CTcTokFileDesc *end_desc, long end_linenum, tct3_method_gen_ctx *ctx, CTcNamedArgTab *named_arg_tab_head) { /* get the output code stream from the context */ CTcCodeStream *stream = ctx->stream; /* * Generate a 'return' opcode with a default 'nil' return value - this * will ensure that code that reaches the end of the procedure returns * normally. If this is a constructor, return the 'self' object rather * than nil. * * We only need to generate this epilogue if the next instruction would * be reachable. If it's not reachable, then the code explicitly took * care of all types of exits. */ if (!can_skip_op()) { /* * add a line record for the implied return at the last source line * of the code body */ add_line_rec(end_desc, end_linenum); /* write the appropriate return */ if (is_in_constructor()) { /* we're in a constructor - return 'self' */ write_op(OPC_PUSHSELF); write_op(OPC_RETVAL); } else { /* * Normal method/function - return without a value (explicitly * set R0 to nil, though, so we don't return something returned * from a called function). */ write_op(OPC_RETNIL); } } /* generate any named argument tables */ for (CTcNamedArgTab *nt = named_arg_tab_head ; nt != 0 ; nt = nt->nxt) nt->generate(); /* * release labels allocated for the code block; this will log an error * if any labels are undefined */ stream->release_labels(); /* * Eliminate jump-to-jump sequences in the generated code. Don't * bother if we've found any errors, as the generated code will not * necessarily be valid if this is the case. */ if (G_tcmain->get_error_count() == 0) remove_jumps_to_jumps(stream, ctx->code_start_ofs); /* note the code block's end point */ ctx->code_end_ofs = stream->get_ofs(); /* * Fix up the local variable count in the function header. We might * allocate extra locals for internal use while generating code, so we * must wait until after generating our code before we know the final * local count. */ stream->write2_at(ctx->method_ofs + 2, local_cnt); /* * Fix up the total stack space indicator in the function header. The * total stack size must include the locals, as well as stack space * needed for intermediate computations. */ stream->write2_at(ctx->method_ofs + 4, get_max_sp_depth() + local_cnt); /* * Before writing the exception table, mark the endpoint of the range * of the main local scope and any enclosing scopes. */ for (CTcPrsSymtab *t = local_symtab ; t != 0 ; t = t->get_parent()) t->add_to_range(G_cs->get_ofs() - ctx->method_ofs); /* * Generate the exception table, if we have one. If we have no * exception records, leave the exception table offset set to zero to * indicate that there is no exception table for the method. */ if (get_exc_table()->get_entry_count() != 0) { /* * write the exception table offset - it's at the current offset in * the code */ stream->write2_at(ctx->method_ofs + 6, stream->get_ofs() - ctx->method_ofs); /* write the table */ get_exc_table()->write_to_code_stream(); } } /* * Clean up after generating a method */ void CTcGenTarg::close_method_cleanup(tct3_method_gen_ctx *ctx) { /* * Tell the code generator our code block byte length so that it can * keep track of the longest single byte-code block; it will use this * to choose the code pool page size when generating the image file. */ note_bytecode(ctx->anchor->get_len(ctx->stream)); /* we're no longer in a constructor, if we ever were */ set_in_constructor(FALSE); /* likewise an operator overload method */ set_in_op_overload(FALSE); /* clear the current code body */ ctx->stream->set_code_body(ctx->old_code_body); /* always leave the main code stream active by default */ G_cs = G_cs_main; } /* ------------------------------------------------------------------------ */ /* * Do post-CALL cleanup */ void CTcGenTarg::post_call_cleanup(const CTcNamedArgs *named_args) { /* write the named argument table pointer, if necessary */ if (named_args != 0 && named_args->cnt != 0) { /* * We need a named argument table. Add the table to the curren * code body, which will generate a label for us to use to * reference the table. */ CTcCodeLabel *tab_lbl = G_cs->get_code_body() ->add_named_arg_tab(named_args); /* write the op: NamedArgPtr , */ G_cg->write_op(OPC_NAMEDARGPTR); G_cs->write((char)named_args->cnt); G_cs->write_ofs2(tab_lbl, 0); /* NamedArgPtr pops the arguments when executed */ G_cg->note_pop(named_args->cnt); } } /* ------------------------------------------------------------------------ */ /* * Run through the generated code stream starting at the given offset (and * running up to the current offset), and eliminate jump-to-jump sequences: * * - whenever we find any jump instruction that points directly to an * unconditional jump, we'll change the first jump so that it points to * the target of the second jump, saving the unnecessary stop at the * intermediate jump * * - whenever we find an unconditional jump to any return or throw * instruction, we'll replace the jump with a copy of the target * return/throw instruction. */ void CTcGenTarg::remove_jumps_to_jumps(CTcCodeStream *str, ulong start_ofs) { ulong ofs; ulong end_ofs; uchar prv_op; /* * scan the code stream starting at the given offset, continuing * through the current offset */ prv_op = OPC_NOP; for (ofs = start_ofs, end_ofs = str->get_ofs() ; ofs < end_ofs ; ) { uchar op; ulong target_ofs; ulong orig_target_ofs; uchar target_op; int done; int chain_len; /* check the byte code instruction at the current location */ switch(op = str->get_byte_at(ofs)) { case OPC_RETVAL: /* * If our previous opcode was PUSHTRUE or PUSHNIL, we can * replace the previous opcode with RETTRUE or RETNIL. This * sequence can occur when we generate conditional code that * returns a value; in such cases, we sometimes can't elide * the PUSHx/RETVAL sequence during the original code * generation because the RETVAL itself is the target of a * label and thus must be retained as a separate instruction. * Converting the PUSHTRUE or PUSHNIL here won't do any harm, * as we'll still leave the RETVAL as a separate instruction. * Likewise, if the previous instruction was GET_R0, we can * change it to a simple RET. */ switch(prv_op) { case OPC_PUSHTRUE: /* convert the PUSHTRUE to a RETTRUE */ str->write_at(ofs - 1, OPC_RETTRUE); break; case OPC_PUSHNIL: /* convert the PUSHNIL to a RETNIL */ str->write_at(ofs - 1, OPC_RETNIL); break; case OPC_GETR0: /* convert the GETR0 to a RET */ str->write_at(ofs - 1, OPC_RET); break; } /* skip the RETVAL */ ofs += 1; break; case OPC_PUSHSTRI: /* * push in-line string: we have a UINT2 operand giving the * length in bytes of the string, followed by the bytes of the * string, so read the uint2 and then skip that amount plus * three additional bytes (one for the opcode, two for the * uint2 itself) */ ofs += 3 + str->readu2_at(ofs+1); break; case OPC_SWITCH: /* * Switch: we have a UINT2 giving the number of cases, * followed by the cases, followed by an INT2; each case * consists of a DATAHOLDER plus a UINT2, for a total of 7 * bytes. The total is thus 5 bytes (the opcode, the case * count UINT2, the final INT2) plus 7 bytes times the number * of cases. */ ofs += 1 + 2 + (VMB_DATAHOLDER + 2)*str->readu2_at(ofs+1) + 2; break; case OPC_NAMEDARGTAB: /* * NamedArgTab - the first operand is a UINT2 giving the length * of the table */ ofs += 1 + 2 + str->readu2_at(ofs+1); break; case OPC_JMP: /* * Unconditional jump: check for a jump to a RETURN of any * kind or a THROW. If the destination consists of either of * those, replace the JMP with the target instruction. If the * destination is an unconditional JMP, iteratively check its * destination. */ orig_target_ofs = target_ofs = ofs + 1 + str->read2_at(ofs + 1); /* * Iterate through any chain of JMP's we find. Abort if we * try following a chain longer than 20 jumps, in case we * should encounter any circular chains. */ for (done = FALSE, chain_len = 0 ; !done && chain_len < 20 ; ++chain_len) { switch(target_op = str->get_byte_at(target_ofs)) { case OPC_RETVAL: /* * Check for a special sequence that we can optimize * even better than the usual. If we have a GETR0 * followed by a JMP to a RETVAL, then we can * eliminate the JMP *and* the GETR0, and just convert * the GETR0 to a RET. */ if (prv_op == OPC_GETR0) { /* * The GETR0 is the byte before the original JMP: * simply replace it with a RET. Note that we can * leave the original jump intact, in case anyone * else is pointing to it. */ str->write_at(ofs - 1, OPC_RET); /* we're done iterating the chain of jumps-to-jumps */ done = TRUE; } else { /* handle the same as any return instruction */ goto any_RET; } break; case OPC_RETNIL: case OPC_RETTRUE: case OPC_RET: case OPC_THROW: any_RET: /* * it's a THROW or RETURN of some kind - simply copy * it to the current slot; write NOP's over our jump * offset operand, to make sure the code continues to * be deterministically readable */ str->write_at(ofs, target_op); str->write_at(ofs + 1, (uchar)OPC_NOP); str->write_at(ofs + 2, (uchar)OPC_NOP); /* we're done iterating the chain of jumps-to-jumps */ done = TRUE; break; case OPC_JMP: /* * We're jumping to another jump - there's no reason * to stop at the intermediate jump instruction, since * we can simply jump directly to the destination * address. Calculate the new target address, and * continue iterating, in case this jumps to something * we can further optimize away. */ target_ofs = target_ofs + 1 + str->read2_at(target_ofs + 1); /* * if it's a jump to the original location, it must be * some unreachable code that generated a circular * jump; ignore it in this case */ if (target_ofs == ofs) done = TRUE; /* proceed */ break; default: /* * For anything else, we're done with any chain of * jumps to jumps. If we indeed found a new target * address, rewrite the original JMP instruction so * that it jumps directly to the end of the chain * rather than going through the intermediate jumps. */ if (target_ofs != orig_target_ofs) str->write2_at(ofs + 1, target_ofs - (ofs + 1)); /* we're done iterating */ done = TRUE; break; } } /* whatever happened, skip past the jump */ ofs += 3; /* done */ break; case OPC_JT: case OPC_JF: case OPC_JE: case OPC_JNE: case OPC_JGT: case OPC_JGE: case OPC_JLT: case OPC_JLE: case OPC_JST: case OPC_JSF: case OPC_JNIL: case OPC_JNOTNIL: case OPC_JR0T: case OPC_JR0F: /* * We have a jump (conditional or otherwise). Check the * target instruction to see if it's an unconditional jump; if * so, then we can jump straight to the target of the second * jump, since there's no reason to stop at the intermediate * jump instruction on our way to the final destination. Make * this check iteratively, so that we eliminate any chain of * jumps to jumps and land at our final non-jump instruction * in one go. */ orig_target_ofs = target_ofs = ofs + 1 + str->read2_at(ofs + 1); for (done = FALSE, chain_len = 0 ; !done && chain_len < 20 ; ++chain_len) { uchar target_op; /* get the target opcode */ target_op = str->get_byte_at(target_ofs); /* * if the target is an unconditional JMP, we can retarget * the original instruction to jump directly to the target * of the target JMP, bypassing the target JMP entirely and * thus avoiding some unnecessary work at run-time */ if (target_op == OPC_JMP) { /* * retarget the original jump to go directly to the * target of the target JMP */ target_ofs = target_ofs + 1 + str->read2_at(target_ofs + 1); /* * continue scanning for more opportunities, as the new * target could also point to something we can bypass */ continue; } /* * Certain combinations are special. If the original * opcode was a JST or JSF, and the target is a JT or JF, * we can recode the sequence so that the original opcode * turns into a more efficient JT or JF and jumps directly * past the JT or JF. If we have a JST or JSF jumping to a * JST or JSF, we can also recode that sequence to bypass * the second jump. In both cases, we can recode the * sequence because the original jump will unequivocally * determine the behavior at the target jump in such a way * that we can compact the sequence into a single jump. */ switch(op) { case OPC_JSF: /* * the original is a JSF: we can recode a jump to a * JSF, JST, JF, or JT */ switch(target_op) { case OPC_JSF: /* * We're jumping to another JSF. Since the * original jump will only reach the target jump if * the value on the top of the stack is false, and * will then leave this same value on the stack to * be tested again with the target JSF, we know the * target JSF will perform its jump and leave the * stack unchanged again. So, we can simply * retarget the original jump to the target of the * target JSF. */ target_ofs = target_ofs + 1 + str->read2_at(target_ofs + 1); /* keep scanning for additional opportunities */ break; case OPC_JST: case OPC_JT: /* * We're jumping to a JST or a JT. Since the JSF * will only reach the JST/JT on a false value, we * know the JST/JT will NOT jump - we know for a * fact it will pop the non-true stack element and * proceed without jumping. Therefore, we can * avoid saving the value from the original JSF, * which means we can recode the original as the * simpler JF (which doesn't bother saving the * false value), and jump on false directly to the * instruction after the target JST/JT. */ str->write_at(ofs, (uchar)OPC_JF); op = OPC_JF; /* jump to the instruction after the target JST/JT */ target_ofs += 3; /* keep looking for more jumps */ break; case OPC_JF: /* * We're jumping to a JF: we know the JF will jump, * because we had to have - and then save - a false * value for the JSF to reach the JF in the first * place. Since we know for a fact the target JF * will remove the false value and jump to its * target, we can bypass the target JF by recoding * the original instruction as a simpler JF and * jumping directly to the target of the target JF. */ str->write_at(ofs, (uchar)OPC_JF); op = OPC_JF; /* jump to the tartet of the target JF */ target_ofs = target_ofs + 1 + str->read2_at(target_ofs + 1); /* keep scanning for more jumps */ break; default: /* can't make any assumptions about other targets */ done = TRUE; break; } break; case OPC_JST: /* * the original is a JST: recode it if the target is a * JSF, JST, JF, or JT */ switch(target_op) { case OPC_JST: /* JST jumping to JST: jump to the target's target */ target_ofs = target_ofs + 1 + str->read2_at(target_ofs + 1); /* keep looking */ break; case OPC_JSF: case OPC_JF: /* * JST jumping to JSF/JF: the JSF/JF will * definitely pop the stack and not jump (since the * original JST will have left a true value on the * stack), so we can recode the JST as a more * efficient JT and jump to the instruction after * the JSF/JF target */ str->write_at(ofs, (uchar)OPC_JT); op = OPC_JT; /* jump to the instruction after the target */ target_ofs += 3; /* keep looking */ break; case OPC_JT: /* * JST jumping to JT: the JT will definitely pop * and jump, so we can recode the original as a * simpler JT and jump to the target's target */ str->write_at(ofs, (uchar)OPC_JT); op = OPC_JT; /* jump to the target of the target */ target_ofs = target_ofs + 1 + str->read2_at(target_ofs + 1); /* keep scanning */ break; default: /* can't make any assumptions about other targets */ done = TRUE; break; } break; default: /* * we can't make assumptions about anything else, so * we've come to the end of the road - stop scanning */ done = TRUE; break; } } /* * if we found a chain of jumps, replace our original jump * target with the final jump target, bypassing the * intermediate jumps */ if (target_ofs != orig_target_ofs) str->write2_at(ofs + 1, target_ofs - (ofs + 1)); /* skip past the jump */ ofs += 3; /* done */ break; default: /* * everything else is a fixed-size instruction, so simply * consult our table of instruction lengths to determine the * offset of the next instruction */ ofs += CVmOpcodes::op_siz[op]; break; } /* remember the preceding opcode */ prv_op = op; } } /* ------------------------------------------------------------------------ */ /* * Generic T3 node */ /* * generate a jump-ahead instruction, returning a new label which serves * as the jump destination */ CTcCodeLabel *CTcPrsNode::gen_jump_ahead(uchar opc) { /* * check to see if we should suppress the jump for peephole * optimization */ if (G_cg->can_skip_op()) return 0; /* emit the opcode */ G_cg->write_op(opc); /* allocate a new label */ CTcCodeLabel *lbl = G_cs->new_label_fwd(); /* * write the forward offset to the label (this will generate a fixup * record attached to the label, so that we'll come back and fix it * up when the real offset is known) */ G_cs->write_ofs2(lbl, 0); /* return the forward label */ return lbl; } /* * Allocate a new label at the current write position */ CTcCodeLabel *CTcPrsNode::new_label_here() { /* * suppress any peephole optimizations at this point -- someone * could jump directly to this instruction, so we can't combine an * instruction destined for this point with anything previous */ G_cg->clear_peephole(); /* create and return a label at the current position */ return G_cs->new_label_here(); } /* * define the position of a code label */ void CTcPrsNode::def_label_pos(CTcCodeLabel *lbl) { /* if the label is null, ignore it */ if (lbl == 0) return; /* * try eliminating a jump-to-next-instruction sequence: if the last * opcode was a JMP to this label, remove the last instruction * entirely */ if (G_cg->get_last_op() == OPC_JMP && G_cs->has_fixup_at_ofs(lbl, G_cs->get_ofs() - 2)) { /* remove the fixup pointing to the preceding JMP */ G_cs->remove_fixup_at_ofs(lbl, G_cs->get_ofs() - 2); /* the JMP is unnecessary - remove it */ G_cg->remove_last_jmp(); } /* define the label position and apply the fixup */ G_cs->def_label_pos(lbl); /* * whenever we define a label, we must suppress any peephole * optimizations at this point - someone could jump directly to this * instruction, so we can't combine an instruction destined for this * point with anything previous */ G_cg->clear_peephole(); } /* * Generate code for an if-else conditional test. The default * implementation is to evaluate the expression, and jump to the false * branch if the expression is false (or jump to the true part if the * expression is true and there's no false part). */ void CTcPrsNode::gen_code_cond(CTcCodeLabel *then_label, CTcCodeLabel *else_label) { /* generate our expression code */ gen_code(FALSE, TRUE); /* * if we have a 'then' part, jump to the 'then' part if the condition * is true; otherwise, jump to the 'else' part if the condition is * false */ if (then_label != 0) { /* we have a 'then' part, so jump if true to the 'then' part */ G_cg->write_op(OPC_JT); G_cs->write_ofs2(then_label, 0); } else { /* we have an 'else' part, so jump if false to the 'else' */ G_cg->write_op(OPC_JF); G_cs->write_ofs2(else_label, 0); } /* the JF or JT pops an element off the stack */ G_cg->note_pop(); } /* * generate code for assignment to this node */ int CTcPrsNode::gen_code_asi(int, int phase, tc_asitype_t, const char *, CTcPrsNode *, int ignore_error, int, void **) { /* * If ignoring errors, the caller is trying to assign if possible but * doesn't require it to be possible; simply return false to indicate * that nothing happened if this is the case. Also ignore errors on * phase 2, since we'll already have reported an error on phase 1. */ if (ignore_error || phase > 1) return FALSE; /* we should never get here - throw an internal error */ G_tok->throw_internal_error(TCERR_GEN_BAD_LVALUE); AFTER_ERR_THROW(return FALSE;) } /* * generate code for taking the address of this node */ void CTcPrsNode::gen_code_addr() { /* we should never get here - throw an internal error */ G_tok->throw_internal_error(TCERR_GEN_BAD_ADDR); } /* * Generate code to call the expression as a function or method. */ void CTcPrsNode::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* function/method calls are never valid in speculative mode */ if (G_cg->is_speculative()) err_throw(VMERR_BAD_SPEC_EVAL); /* * For default nodes, assume that the result of evaluating the * expression contained in the node is a method or function pointer. * First, generate code to evaluate the expression, which should * yield an appropriate pointer value. */ gen_code(FALSE, FALSE); /* * if we have a varargs list, modify the call instruction that * follows to make it a varargs call */ if (varargs) { /* swap the top of the stack to get the arg counter back on top */ G_cg->write_op(OPC_SWAP); /* write the varargs modifier */ G_cg->write_op(OPC_VARARGC); } /* generate an indirect function call through the pointer */ G_cg->write_op(OPC_PTRCALL); G_cs->write((char)argc); /* PTRCALL pops the arguments plus the function pointer */ G_cg->note_pop(argc + 1); /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* * if the caller isn't going to discard the return value, push the * result, which is sitting in R0 */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * Generate code for operator 'new' applied to this node */ void CTcPrsNode::gen_code_new(int, int, int, CTcNamedArgs *, int, int) { /* operator 'new' cannot be applied to a default node */ G_tok->log_error(TCERR_INVAL_NEW_EXPR); } /* * Generate code for a member evaluation */ void CTcPrsNode::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* evaluate my own expression to yield the object value */ gen_code(FALSE, FALSE); /* if we have an argument counter, put it back on top */ if (varargs) G_cg->write_op(OPC_SWAP); /* use the generic code to generate the rest */ s_gen_member_rhs(discard, prop_expr, prop_is_expr, argc, varargs, named_args); } /* * Generic code to generate the rest of a member expression after the * left side of the '.' has been generated. This can be used for cases * where the left of the '.' is an arbitrary expression, and hence must * be evaluated at run-time. */ void CTcPrsNode::s_gen_member_rhs(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { vm_prop_id_t prop; /* we can't call methods with arguments in speculative mode */ if (G_cg->is_speculative() && (argc != 0 || (named_args != 0 && named_args->cnt != 0))) err_throw(VMERR_BAD_SPEC_EVAL); /* get or generate the property ID value */ prop = prop_expr->gen_code_propid(FALSE, prop_is_expr); /* * if we got a property ID, generate a simple GETPROP or CALLPROP * instruction; otherwise, generate a PTRCALLPROP instruction */ if (prop != VM_INVALID_PROP) { /* * we have a constant property ID - generate a GETPROP or * CALLPROP with the property ID as constant data */ if (argc == 0) { /* no arguments - generate a GETPROP */ G_cg->write_op(G_cg->is_speculative() ? OPC_GETPROPDATA : OPC_GETPROP); G_cs->write_prop_id(prop); /* this pops an object element */ G_cg->note_pop(); } else { /* write the CALLPROP instruction */ G_cg->write_callprop(argc, varargs, prop); } } else { if (G_cg->is_speculative()) { /* * speculative - use PTRGETPROPDATA to ensure we don't cause * any side effects */ G_cg->write_op(OPC_PTRGETPROPDATA); } else { /* * if we have a varargs list, modify the call instruction * that follows to make it a varargs call */ if (varargs) { /* swap to get the arg counter back on top */ G_cg->write_op(OPC_SWAP); /* write the varargs modifier */ G_cg->write_op(OPC_VARARGC); } /* a property pointer is on the stack - write a PTRCALLPROP */ G_cg->write_op(OPC_PTRCALLPROP); G_cs->write((int)argc); } /* * ptrcallprop/ptrgetpropdata removes arguments, the object, and * the property */ G_cg->note_pop(argc + 2); } /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if we're not discarding the result, push it from R0 */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * Generate code to get the property ID of the expression. */ vm_prop_id_t CTcPrsNode::gen_code_propid(int check_only, int is_expr) { /* * simply evaluate the expression normally, anticipating that this * will yield a property ID value at run-time */ if (!check_only) gen_code(FALSE, FALSE); /* tell the caller that there's no constant ID available */ return VM_INVALID_PROP; } /* ------------------------------------------------------------------------ */ /* * "self" */ /* * generate code */ void CTPNSelf::gen_code(int discard, int) { /* it's an error if we're not in a method context */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_SELF_NOT_AVAIL); /* if we're not discarding the result, push the "self" object */ if (!discard) { G_cg->write_op(OPC_PUSHSELF); G_cg->note_push(); } } /* * evaluate a property */ void CTPNSelf::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { vm_prop_id_t prop; /* make sure "self" is available */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_SELF_NOT_AVAIL); /* don't allow arguments in speculative eval mode */ if (G_cg->is_speculative() && (argc != 0 || (named_args != 0 && named_args->cnt != 0))) err_throw(VMERR_BAD_SPEC_EVAL); /* generate the property value */ prop = prop_expr->gen_code_propid(FALSE, prop_is_expr); /* * if we got a property ID, generate a simple GETPROPSELF or * CALLPROPSELF; otherwise, generate a PTRCALLPROPSELF */ if (prop != VM_INVALID_PROP) { /* * we have a constant property ID - generate a GETPROPDATA, * GETPROPSELF, or CALLPROPSELF with the property ID as * immediate data */ if (G_cg->is_speculative()) { /* speculative - use GETPROPDATA */ G_cg->write_op(OPC_PUSHSELF); G_cg->write_op(OPC_GETPROPDATA); G_cs->write_prop_id(prop); /* we pushed one element (self) and popped it back off */ G_cg->note_push(); G_cg->note_pop(); } else if (argc == 0) { /* no arguments - generate a GETPROPSELF */ G_cg->write_op(OPC_GETPROPSELF); G_cs->write_prop_id(prop); } else { /* add a varargs modifier if appropriate */ if (varargs) G_cg->write_op(OPC_VARARGC); /* we have arguments - generate a CALLPROPSELF */ G_cg->write_op(OPC_CALLPROPSELF); G_cs->write((char)argc); G_cs->write_prop_id(prop); /* this removes arguments */ G_cg->note_pop(argc); } } else { /* * a property pointer is on the stack - use PTRGETPROPDATA or * PTRCALLPROPSELF, depending on the speculative mode */ if (G_cg->is_speculative()) { /* speculative - use PTRGETPROPDATA after pushing self */ G_cg->write_op(OPC_PUSHSELF); G_cg->write_op(OPC_PTRGETPROPDATA); /* we pushed self then removed self and the property ID */ G_cg->note_push(); G_cg->note_pop(2); } else { /* * if we have a varargs list, modify the call instruction * that follows to make it a varargs call */ if (varargs) { /* swap to get the arg counter back on top */ G_cg->write_op(OPC_SWAP); /* write the varargs modifier */ G_cg->write_op(OPC_VARARGC); } /* a prop pointer is on the stack - write a PTRCALLPROPSELF */ G_cg->write_op(OPC_PTRCALLPROPSELF); G_cs->write((int)argc); /* this removes arguments and the property pointer */ G_cg->note_pop(argc + 1); } } /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if the result is needed, push it */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * generate code for an object before a '.' */ vm_obj_id_t CTPNSelf::gen_code_obj_predot(int *is_self) { /* make sure "self" is available */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_SELF_NOT_AVAIL); /* tell the caller that this is "self" */ *is_self = TRUE; return VM_INVALID_OBJ; } /* ------------------------------------------------------------------------ */ /* * "replaced" */ /* * evaluate 'replaced' on its own - this simply yields a function pointer * to the modified base code */ void CTPNReplaced::gen_code(int discard, int for_condition) { /* get the modified base function symbol */ CTcSymFunc *mod_base = G_cs->get_code_body()->get_replaced_func(); /* make sure we're in a 'modify func()' context */ if (mod_base == 0) G_tok->log_error(TCERR_REPLACED_NOT_AVAIL); /* this expression yields a pointer to the modified base function */ G_cg->write_op(OPC_PUSHFNPTR); /* add a fixup for the current code location */ if (mod_base != 0) mod_base->add_abs_fixup(G_cs); /* write a placeholder offset - arbitrarily use zero */ G_cs->write4(0); /* note the push */ G_cg->note_push(); } /* * 'replaced()' call - this invokes the modified base code */ void CTPNReplaced::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* get the modified base function symbol */ CTcSymFunc *mod_base = G_cs->get_code_body()->get_replaced_func(); /* make sure we're in a 'modify func()' context */ if (mod_base == 0) G_tok->log_error(TCERR_REPLACED_NOT_AVAIL); /* write the varargs modifier if appropriate */ if (varargs) G_cg->write_op(OPC_VARARGC); /* generate the call instruction and argument count */ G_cg->write_op(OPC_CALL); G_cs->write((char)argc); /* generate a fixup for the call to the modified base code */ if (mod_base != 0) mod_base->add_abs_fixup(G_cs); /* add a placeholder for the function address */ G_cs->write4(0); /* call removes arguments */ G_cg->note_pop(argc); /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* make sure the argument count is correct */ if (mod_base != 0 && !mod_base->argc_ok(argc)) { char buf[128]; G_tok->log_error(TCERR_WRONG_ARGC_FOR_FUNC, (int)mod_base->get_sym_len(), mod_base->get_sym(), mod_base->get_argc_desc(buf), argc); } /* if we're not discarding, push the return value from R0 */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * "targetprop" */ /* * generate code */ void CTPNTargetprop::gen_code(int discard, int) { /* it's an error if we're not in a method context */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_TARGETPROP_NOT_AVAIL); /* if we're not discarding the result, push the target property ID */ if (!discard) { G_cg->write_op(OPC_PUSHCTXELE); G_cs->write(PUSHCTXELE_TARGPROP); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * "targetobj" */ /* * generate code */ void CTPNTargetobj::gen_code(int discard, int) { /* it's an error if we're not in a method context */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_TARGETOBJ_NOT_AVAIL); /* if we're not discarding the result, push the target object ID */ if (!discard) { G_cg->write_op(OPC_PUSHCTXELE); G_cs->write(PUSHCTXELE_TARGOBJ); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * "definingobj" */ /* * generate code */ void CTPNDefiningobj::gen_code(int discard, int) { /* it's an error if we're not in a method context */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_DEFININGOBJ_NOT_AVAIL); /* if we're not discarding the result, push the defining object ID */ if (!discard) { G_cg->write_op(OPC_PUSHCTXELE); G_cs->write(PUSHCTXELE_DEFOBJ); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * "invokee" */ /* * generate code */ void CTPNInvokee::gen_code(int discard, int) { /* if we're not discarding the result, push the invokee value */ if (!discard) { G_cg->write_op(OPC_PUSHCTXELE); G_cs->write(PUSHCTXELE_INVOKEE); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * "inherited" */ void CTPNInh::gen_code(int, int) { /* * we should never be asked to generate an "inherited" node * directly; these nodes should always be generated as part of * member evaluation */ G_tok->throw_internal_error(TCERR_GEN_CODE_INH); } /* * evaluate a property */ void CTPNInh::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { vm_prop_id_t prop; /* don't allow 'inherited' in speculative evaluation mode */ if (G_cg->is_speculative()) err_throw(VMERR_BAD_SPEC_EVAL); /* * If we're in a multi-method context, inherited() has a different * meaning from the ordinary method context meaning. */ for (CTPNCodeBody *cb = G_cs->get_code_body() ; cb != 0 ; cb = cb->get_enclosing()) { /* check for a multi-method context */ CTcSymFunc *f = cb->get_func_sym(); if (f != 0 && f->is_multimethod()) { /* generate the multi-method inherited() code */ gen_code_mminh(f, discard, prop_expr, prop_is_expr, argc, varargs, named_args); /* we're done */ return; } } /* * make sure "self" is available - we obviously can't inherit * anything if we're not in an object's method */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_SELF_NOT_AVAIL); /* a type list ('inherited<>') is invalid for regular method inheritance */ if (typelist_ != 0) G_tok->log_error(TCERR_MMINH_BAD_CONTEXT); /* generate the property value */ prop = prop_expr->gen_code_propid(FALSE, prop_is_expr); /* * if we got a property ID, generate a simple INHERIT; * otherwise, generate a PTRINHERIT */ if (prop != VM_INVALID_PROP) { /* generate a varargs modifier if necessary */ if (varargs) G_cg->write_op(OPC_VARARGC); /* we have a constant property ID - generate a regular INHERIT */ G_cg->write_op(OPC_INHERIT); G_cs->write((char)argc); G_cs->write_prop_id(prop); /* this removes arguments */ G_cg->note_pop(argc); } else { /* * if we have a varargs list, modify the call instruction that * follows to make it a varargs call */ if (varargs) { /* swap the top of the stack to get the arg counter back on top */ G_cg->write_op(OPC_SWAP); /* write the varargs modifier */ G_cg->write_op(OPC_VARARGC); } /* a property pointer is on the stack - write a PTRINHERIT */ G_cg->write_op(OPC_PTRINHERIT); G_cs->write((int)argc); /* this removes arguments and the property pointer */ G_cg->note_pop(argc + 1); } /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if the result is needed, push it */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } void CTPNInh::gen_code_mminh(CTcSymFunc *func, int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* * If the function has an associated global symbol from a modification, * use the global symbol instead. The modified function doesn't have a * real symbol, so we can't use it; but fortunately, the global symbol * applies to all modified versions as well, since they all inherit * from the same base function. */ while (func->get_mod_global() != 0) func = func->get_mod_global(); /* * There are two forms of inherited() for multi-methods. * * inherited(args) - this form takes an explicit type list, to * invoke a specific overridden version. * * inherited(args) - this form invokes the next inherited version, * determined dynamically at run-time. */ if (typelist_ == 0) { /* * It's the inherited(args) format. * * Call _multiMethodCallInherited(fromFunc, args). We've already * pushed the args list, so just add the source function argument * (which is simply our defining function). */ func->gen_code(FALSE); /* look up _multiMethodCallInherited */ CTcSymFunc *mmci = (CTcSymFunc *)G_prs->get_global_symtab()->find( "_multiMethodCallInherited", 25); if (mmci == 0 || mmci->get_type() != TC_SYM_FUNC) { /* undefined or incorrectly defined - log an error */ G_tok->log_error(TCERR_MMINH_MISSING_SUPPORT_FUNC, 25, "_multiMethodCallInherited"); } else { /* generate the call */ mmci->gen_code_call(discard, argc + 1, varargs, named_args); } } else { /* * It's the inherited(args) format. */ /* * Get the base name for the function. 'func' is the decorated * name for the containing function, which is of the form * 'Base*type1;type2...'. The base name is the part up to the * asterisk. */ const char *nm = func->getstr(), *p; size_t rem = func->getlen(); for (p = nm ; rem != 0 && *p != '*' ; ++p, --rem) ; /* make a token for the base name */ CTcToken btok; btok.set_text(nm, p - nm); /* build the decorated name for the target function */ CTcToken dtok; typelist_->decorate_name(&dtok, &btok); /* look up the decorated name */ CTcSymFunc *ifunc = (CTcSymFunc *)G_prs->get_global_symtab()->find( dtok.get_text(), dtok.get_text_len()); /* if we found it, call it */ if (ifunc != 0 && ifunc->get_type() == TC_SYM_FUNC) { /* generate the call */ ifunc->gen_code_call(discard, argc, varargs, named_args); } else { /* function not found */ G_tok->log_error(TCERR_MMINH_UNDEF_FUNC, (int)(p - nm), nm); } } } /* ------------------------------------------------------------------------ */ /* * "inherited class" */ void CTPNInhClass::gen_code(int discard, int for_condition) { /* * we should never be asked to generate an "inherited" node * directly; these nodes should always be generated as part of * member evaluation */ G_tok->throw_internal_error(TCERR_GEN_CODE_INH); } /* * evaluate a property */ void CTPNInhClass::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { vm_prop_id_t prop; CTcSymbol *objsym; vm_obj_id_t obj; /* * make sure "self" is available - we obviously can't inherit * anything if we're not in an object's method */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_SELF_NOT_AVAIL); /* don't allow 'inherited' in speculative evaluation mode */ if (G_cg->is_speculative()) err_throw(VMERR_BAD_SPEC_EVAL); /* get the superclass name symbol */ objsym = G_cs->get_symtab()->find_or_def_undef(sym_, len_, FALSE); /* if it's not an object, we can't inherit from it */ obj = objsym->get_val_obj(); if (obj == VM_INVALID_OBJ) { G_tok->log_error(TCERR_INH_NOT_OBJ, (int)len_, sym_); return; } /* generate the property value */ prop = prop_expr->gen_code_propid(FALSE, prop_is_expr); /* * if we got a property ID, generate a simple EXPINHERIT; otherwise, * generate a PTREXPINHERIT */ if (prop != VM_INVALID_PROP) { /* add a varargs modifier if needed */ if (varargs) G_cg->write_op(OPC_VARARGC); /* we have a constant property ID - generate a regular EXPINHERIT */ G_cg->write_op(OPC_EXPINHERIT); G_cs->write((char)argc); G_cs->write_prop_id(prop); G_cs->write_obj_id(obj); /* this removes argumnts */ G_cg->note_pop(argc); } else { /* * if we have a varargs list, modify the call instruction that * follows to make it a varargs call */ if (varargs) { /* swap the top of the stack to get the arg counter back on top */ G_cg->write_op(OPC_SWAP); /* write the varargs modifier */ G_cg->write_op(OPC_VARARGC); } /* a property pointer is on the stack - write a PTREXPINHERIT */ G_cg->write_op(OPC_PTREXPINHERIT); G_cs->write((int)argc); G_cs->write_obj_id(obj); /* this removes arguments and the property pointer */ G_cg->note_pop(argc + 1); } /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if the result is needed, push it */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * "delegated" */ void CTPNDelegated::gen_code(int discard, int for_condition) { /* * we should never be asked to generate a "delegated" node directly; * these nodes should always be generated as part of member evaluation */ G_tok->throw_internal_error(TCERR_GEN_CODE_DELEGATED); } /* * evaluate a property */ void CTPNDelegated::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { vm_prop_id_t prop; /* * make sure "self" is available - we obviously can't delegate * anything if we're not in an object's method */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_SELF_NOT_AVAIL); /* don't allow 'delegated' in speculative evaluation mode */ if (G_cg->is_speculative()) err_throw(VMERR_BAD_SPEC_EVAL); /* generate the delegatee expression */ delegatee_->gen_code(FALSE, FALSE); /* if we have an argument counter, put it back on top */ if (varargs) G_cg->write_op(OPC_SWAP); /* generate the property value */ prop = prop_expr->gen_code_propid(FALSE, prop_is_expr); /* * if we got a property ID, generate a simple DELEGATE; otherwise, * generate a PTRDELEGATE */ if (prop != VM_INVALID_PROP) { /* add a varargs modifier if needed */ if (varargs) G_cg->write_op(OPC_VARARGC); /* we have a constant property ID - generate a regular DELEGATE */ G_cg->write_op(OPC_DELEGATE); G_cs->write((char)argc); G_cs->write_prop_id(prop); /* this removes arguments and the object value */ G_cg->note_pop(argc + 1); } else { /* * if we have a varargs list, modify the call instruction that * follows to make it a varargs call */ if (varargs) { /* swap the top of the stack to get the arg counter back on top */ G_cg->write_op(OPC_SWAP); /* write the varargs modifier */ G_cg->write_op(OPC_VARARGC); } /* a property pointer is on the stack - write a PTRDELEGATE */ G_cg->write_op(OPC_PTRDELEGATE); G_cs->write((int)argc); /* this removes arguments, the object, and the property pointer */ G_cg->note_pop(argc + 2); } /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if the result is needed, push it */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * "argcount" */ void CTPNArgc::gen_code(int discard, int) { /* generate the argument count, if we're not discarding */ if (!discard) { if (G_cg->is_eval_for_debug()) { /* generate a debug argument count evaluation */ G_cg->write_op(OPC_GETDBARGC); G_cs->write2(G_cg->get_debug_stack_level()); } else { /* generate the normal argument count evaluation */ G_cg->write_op(OPC_GETARGC); } /* we push one element */ G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * constant */ void CTPNConst::gen_code(int discard, int) { /* if we're discarding the value, do nothing */ if (discard) return; /* generate the appropriate type of push for the value */ switch(val_.get_type()) { case TC_CVT_NIL: G_cg->write_op(OPC_PUSHNIL); break; case TC_CVT_TRUE: G_cg->write_op(OPC_PUSHTRUE); break; case TC_CVT_INT: /* write the push-integer instruction */ s_gen_code_int(val_.get_val_int()); /* s_gen_code_int notes a push, which we'll do also, so cancel it */ G_cg->note_pop(); break; case TC_CVT_FLOAT: /* write the push-object instruction for the BigNumber object */ G_cg->write_op(OPC_PUSHOBJ); /* generate the BigNumber object and write its ID */ G_cs->write_obj_id(G_cg->gen_bignum_obj( val_.get_val_float(), val_.get_val_float_len(), val_.is_promoted())); break; case TC_CVT_RESTR: /* write the push-object instruction for the RexPattern object */ G_cg->write_op(OPC_PUSHOBJ); /* generate the RexPattern object and write its ID */ G_cs->write_obj_id(G_cg->gen_rexpat_obj( val_.get_val_str(), val_.get_val_str_len())); break; case TC_CVT_SSTR: /* generate the string */ s_gen_code_str(&val_); /* s_gen_code_int notes a push, which we'll do also, so cancel it */ G_cg->note_pop(); break; case TC_CVT_LIST: /* write the instruction */ G_cg->write_op(OPC_PUSHLST); /* * add the list to the constant pool, creating a fixup at the * current code stream location */ G_cg->add_const_list(val_.get_val_list(), G_cs, G_cs->get_ofs()); /* * write a placeholder address - this will be corrected by the * fixup that add_const_list() created for us */ G_cs->write4(0); break; case TC_CVT_OBJ: /* generate the object ID */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(val_.get_val_obj()); break; case TC_CVT_PROP: /* generate the property address */ G_cg->write_op(OPC_PUSHPROPID); G_cs->write_prop_id(val_.get_val_prop()); break; case TC_CVT_ENUM: /* generate the enum value */ G_cg->write_op(OPC_PUSHENUM); G_cs->write_enum_id(val_.get_val_enum()); break; case TC_CVT_FUNCPTR: /* generate the function pointer instruction */ G_cg->write_op(OPC_PUSHFNPTR); /* add a fixup for the function address */ val_.get_val_funcptr_sym()->add_abs_fixup(G_cs); /* write out a placeholder - arbitrarily use zero */ G_cs->write4(0); break; case TC_CVT_ANONFUNCPTR: /* generate the function pointer instruction */ G_cg->write_op(OPC_PUSHFNPTR); /* add a fixup for the code body address */ val_.get_val_anon_func_ptr()->add_abs_fixup(G_cs); /* write our a placeholder */ G_cs->write4(0); break; default: /* anything else is an internal error */ G_tok->throw_internal_error(TCERR_GEN_UNK_CONST_TYPE); } /* all of these push a value */ G_cg->note_push(); } int CTPNConst::gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *, int ignore_errors, int, void **) { /* * If ignoring errors, just indicate that we can't do the assignment. * Also ignore errors on phase 2, since we'll already have generated an * error during phase 1. */ if (ignore_errors || phase > 1) return FALSE; /* * We can't assign to a constant. The parser will catch most errors of * this type before we ever get here, but for expressions that resolve * to global symbols (such as objects or functions), it can't know * until code generation time that the expression won't be assignable. */ G_tok->log_error(typ == TC_ASI_PREINC || typ == TC_ASI_PREDEC || typ == TC_ASI_POSTINC || typ == TC_ASI_POSTDEC ? TCERR_INVALID_UNARY_LVALUE : TCERR_INVALID_LVALUE, op); return FALSE; } /* * generate code to push an integer value */ void CTPNConst::s_gen_code_int(long intval) { /* push the smallest format that will fit the value */ if (intval == 0) { /* write the special PUSH_0 instruction */ G_cg->write_op(OPC_PUSH_0); } else if (intval == 1) { /* write the special PUSH_1 instruction */ G_cg->write_op(OPC_PUSH_1); } else if (intval < 127 && intval >= -128) { /* it fits in eight bits */ G_cg->write_op(OPC_PUSHINT8); G_cs->write((char)intval); } else { /* it doesn't fit in 8 bits - use a full 32 bits */ G_cg->write_op(OPC_PUSHINT); G_cs->write4(intval); } /* however we did it, we left one value on the stack */ G_cg->note_push(); } /* * Generate code to push a symbol's name as a string constant value */ void CTPNConst::s_gen_code_str(const CTcSymbol *sym) { /* generate the string for the symbol name */ s_gen_code_str(sym->get_sym(), sym->get_sym_len()); } /* * Generate code to push a symbol's name as a string constant value, * according to the code generation mode. */ void CTPNConst::s_gen_code_str_by_mode(const CTcSymbol *sym) { /* check the mode */ if (G_cg->is_eval_for_dyn()) { /* dynamic evaluation - use in-line strings */ CTPNDebugConst::s_gen_code_str(sym->get_sym(), sym->get_sym_len()); } else { /* static compilation - use the constant pool */ s_gen_code_str(sym->get_sym(), sym->get_sym_len()); } } /* * Generate code to push a string constant value */ void CTPNConst::s_gen_code_str(const CTcConstVal *val) { s_gen_code_str(val->get_val_str(), val->get_val_str_len()); } /* * Generate code to push a string constant value */ void CTPNConst::s_gen_code_str(const char *str, size_t len) { /* write the instruction to push a constant pool string */ G_cg->write_op(OPC_PUSHSTR); /* note the push */ G_cg->note_push(); /* * add the string to the constant pool, creating a fixup at the current * code stream location */ G_cg->add_const_str(str, len, G_cs, G_cs->get_ofs()); /* * write a placeholder address - this will be corrected by the fixup * that add_const_str() created for us */ G_cs->write4(0); } /* * Generate code to apply operator 'new' to the constant. We can apply * 'new' only to constant object values. */ void CTPNConst::gen_code_new(int discard, int argc, int varargs, CTcNamedArgs *named_args, int /*from_call*/, int is_transient) { /* check the type */ switch(val_.get_type()) { case TC_CVT_OBJ: /* * Treat this the same as any other 'new' call. An object symbol * folded into a constant is guaranteed to be of metaclass * TadsObject - that's the only kind of symbol we'll ever fold this * way. */ CTcSymObj::s_gen_code_new(discard, val_.get_val_obj(), val_.get_val_obj_meta(), argc, varargs, named_args, is_transient); break; default: /* can't apply 'new' to other constant values */ G_tok->log_error(TCERR_INVAL_NEW_EXPR); break; } } /* * Generate code to make a function call to this expression. If we're * calling a function, we can generate this directly. */ void CTPNConst::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* check our type */ switch(val_.get_type()) { case TC_CVT_FUNCPTR: /* generate a call to our function symbol */ val_.get_val_funcptr_sym()->gen_code_call( discard, argc, varargs, named_args); break; default: /* other types cannot be called */ G_tok->log_error(TCERR_CANNOT_CALL_CONST); break; } } /* * generate a property ID expression */ vm_prop_id_t CTPNConst::gen_code_propid(int check_only, int is_expr) { /* check the type */ switch(val_.get_type()) { case TC_CVT_PROP: /* return the constant property ID */ return (vm_prop_id_t)val_.get_val_prop(); default: /* other values cannot be used as properties */ if (!check_only) G_tok->log_error(TCERR_INVAL_PROP_EXPR); return VM_INVALID_PROP; } } /* * Generate code for a member evaluation */ void CTPNConst::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* check our constant type */ switch(val_.get_type()) { case TC_CVT_OBJ: /* call the object symbol code to do the work */ CTcSymObj::s_gen_code_member(discard, prop_expr, prop_is_expr, argc, val_.get_val_obj(), varargs, named_args); break; case TC_CVT_LIST: case TC_CVT_SSTR: case TC_CVT_RESTR: case TC_CVT_FLOAT: /* * list/string/BigNumber constant - generate our value as * normal, then use the standard member generation */ gen_code(FALSE, FALSE); /* if we have an argument counter, put it back on top */ if (varargs) G_cg->write_op(OPC_SWAP); /* use standard member generation */ CTcPrsNode::s_gen_member_rhs(discard, prop_expr, prop_is_expr, argc, varargs, named_args); break; default: G_tok->log_error(TCERR_INVAL_OBJ_EXPR); break; } } /* * generate code for an object before a '.' */ vm_obj_id_t CTPNConst::gen_code_obj_predot(int *is_self) { /* we're certainly not "self" */ *is_self = FALSE; /* if I don't have an object value, this is illegal */ if (val_.get_type() != TC_CVT_OBJ) { G_tok->log_error(TCERR_INVAL_OBJ_EXPR); return VM_INVALID_OBJ; } /* report our constant object value */ return val_.get_val_obj(); } /* ------------------------------------------------------------------------ */ /* * debugger constant */ void CTPNDebugConst::gen_code(int discard, int for_condition) { /* if we're discarding the value, do nothing */ if (discard) return; /* generate the appropriate type of push for the value */ switch(val_.get_type()) { case TC_CVT_SSTR: if (val_.get_val_str() == 0) { /* validate the string address */ if (G_vmifc && !G_vmifc->validate_pool_str(val_.get_val_str_ofs())) { /* treat it as nil */ G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); break; } /* we have a pre-resolved pool offset */ G_cg->write_op(OPC_PUSHSTR); G_cs->write4(val_.get_val_str_ofs()); G_cg->note_push(); } else { /* write the in-line string instruction */ s_gen_code_str(val_.get_val_str(), val_.get_val_str_len()); } break; case TC_CVT_LIST: if (val_.get_val_list() == 0) { /* validate the list address */ if (G_vmifc && !G_vmifc->validate_pool_list(val_.get_val_list_ofs())) { /* treat it as nil */ G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); break; } /* we have a pre-resolved pool offset */ G_cg->write_op(OPC_PUSHLST); G_cs->write4(val_.get_val_list_ofs()); G_cg->note_push(); } else { /* we should never have a regular constant list when debugging */ assert(FALSE); } break; case TC_CVT_FUNCPTR: /* generate the function pointer instruction */ G_cg->write_op(OPC_PUSHFNPTR); /* * write the actual function address - no need for fixups in the * debugger, since everything's fully resolved */ G_cs->write4(val_.get_val_funcptr_sym()->get_code_pool_addr()); /* note the value push */ G_cg->note_push(); break; case TC_CVT_BIFPTR: /* validate the property ID if possible */ if (G_vmifc && !G_vmifc->validate_bif( val_.get_val_bifptr_sym()->get_func_set_id(), val_.get_val_bifptr_sym()->get_func_idx())) { /* treat it as nil */ G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); break; } /* generate the built-in function pointer instruction */ G_cg->write_op(OPC_PUSHBIFPTR); G_cs->write2(val_.get_val_bifptr_sym()->get_func_idx()); G_cs->write2(val_.get_val_bifptr_sym()->get_func_set_id()); /* note the value push */ G_cg->note_push(); break; case TC_CVT_ANONFUNCPTR: /* * we should never see an anonymous function pointer constant in * the debugger - these will always be represented as objects * intead */ assert(FALSE); break; case TC_CVT_FLOAT: { /* * find the 'BigNumber' metaclass - if it's not defined, we * can't create BigNumber values */ CTcSymMetaclass *sym = (CTcSymMetaclass *)G_prs->get_global_symtab()->find( "BigNumber", 9); if (sym == 0 || sym->get_type() != TC_SYM_METACLASS) err_throw(VMERR_INVAL_DBG_EXPR); /* push the floating value as an immediate string */ G_cg->write_op(OPC_PUSHSTRI); G_cs->write2(val_.get_val_str_len()); G_cs->write(val_.get_val_str(), val_.get_val_str_len()); /* create the new BigNumber object from the string */ G_cg->write_op(OPC_NEW2); G_cs->write2(1); G_cs->write2(sym->get_meta_idx()); /* retrieve the value */ G_cg->write_op(OPC_GETR0); /* * note the net push of one value (we pushed the argument, * popped the argument, and pushed the new object) */ G_cg->note_push(); } break; case TC_CVT_RESTR: { /* find the RexPattern metaclass */ CTcSymMetaclass *sym = (CTcSymMetaclass *)G_prs->get_global_symtab()->find( "RexPattern", 10); if (sym == 0 || sym->get_type() != TC_SYM_METACLASS) err_throw(VMERR_INVAL_DBG_EXPR); /* push the pattern source string value as an immediate string */ G_cg->write_op(OPC_PUSHSTRI); G_cs->write2(val_.get_val_str_len()); G_cs->write(val_.get_val_str(), val_.get_val_str_len()); /* create the new RexPattern object from the string */ G_cg->write_op(OPC_NEW2); G_cs->write2(1); G_cs->write2(sym->get_meta_idx()); /* retrieve the value */ G_cg->write_op(OPC_GETR0); /* * note the net push of one value (we pushed the argument, * popped the argument, and pushed the new object) */ G_cg->note_push(); } break; case TC_CVT_OBJ: /* validate the object ID if possible */ if (G_vmifc && !G_vmifc->validate_obj(val_.get_val_obj())) { /* treat it as nil */ G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); break; } /* use the default handling */ goto use_default; case TC_CVT_PROP: /* validate the property ID if possible */ if (G_vmifc && !G_vmifc->validate_prop((tctarg_prop_id_t)val_.get_val_prop())) { /* treat it as nil */ G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); break; } /* use the default handling */ goto use_default; default: use_default: /* handle normally for anything else */ CTPNConst::gen_code(discard, for_condition); break; } } /* * Generate a string value in a debug/dynamic code context. This generates * an in-line string rather than a constant pool reference. */ void CTPNDebugConst::s_gen_code_str(const char *str, size_t len) { /* write the PUSHSTRI */ G_cg->write_op(OPC_PUSHSTRI); G_cs->write2(len); G_cs->write(str, len); /* note the value push */ G_cg->note_push(); } /* ------------------------------------------------------------------------ */ /* * Generic Unary Operator */ /* * Generate a unary-operator opcode. We assume that the opcode has no * side effects other than to compute the result, so we do not generate * the opcode at all if 'discard' is true; we do, however, always * generate code for the subexpression to ensure that its side effects * are performed. * * In most cases, the caller simply should pass through its 'discard' * status, since the result of the subexpression is generally needed * only when the result of the enclosing expression is needed. * * In most cases, the caller should pass FALSE for 'for_condition', * because applying an operator to the result generally requires that * the result be properly converted for use as a temporary value. * However, when the caller knows that its own opcode will perform the * same conversions that a conditional opcode would, 'for_condition' * should be TRUE. In most cases, the caller's own 'for_condition' * status is not relevant and should thus not be passed through. */ void CTPNUnary::gen_unary(uchar opc, int discard, int for_condition) { /* * Generate the operand. Pass through the 'discard' status to the * operand - if the result of the parent operator is being * discarded, then so is the result of this subexpression. In * addition, pass through the caller's 'for_condition' disposition. */ sub_->gen_code(discard, for_condition); /* apply the operator if we're not discarding the result */ if (!discard) G_cg->write_op(opc); } /* ------------------------------------------------------------------------ */ /* * Generic Binary Operator */ /* * Generate a binary-operator opcode. * * In most cases, the caller's 'discard' status should be passed * through, since the results of the operands are usually needed if and * only if the results of the enclosing expression are needed. * * In most cases, the caller should pass FALSE for 'for_condition'. * Only when the caller knows that the opcode will perform the same * conversions as a BOOLIZE instruction should it pass TRUE for * 'for_condition'. */ void CTPNBin::gen_binary(uchar opc, int discard, int for_condition) { /* * generate the operands, passing through the discard and * conditional status */ left_->gen_code(discard, for_condition); right_->gen_code(discard, for_condition); /* generate our operand if we're not discarding the result */ if (!discard) { /* apply the operator */ G_cg->write_op(opc); /* * binary operators all remove two values and push one, so there's * a net pop */ G_cg->note_pop(); } } /* * Generate a binary-operator opcode for a compound assignment. This omits * generating the left operand, because the compound assignment operator * will already have done that. */ void CTPNBin::gen_binary_ca(uchar opc) { /* generate the right operand (the left is already on the stack) */ right_->gen_code(FALSE, FALSE); /* generate the operator */ G_cg->write_op(opc); /* a binary op removes two values and pushes one */ G_cg->note_pop(); } /* ------------------------------------------------------------------------ */ /* * logical NOT */ void CTPNNot::gen_code(int discard, int) { /* * Generate the subexpression and apply the NOT opcode. Note that * we can compute the subexpression as though we were applying a * condition, because the NOT opcode takes exactly the same kind of * input as any condition opcode; we can thus avoid an extra * conversion in some cases. */ gen_unary(OPC_NOT, discard, TRUE); } /* ------------------------------------------------------------------------ */ /* * Boolean-ize operator */ void CTPNBoolize::gen_code(int discard, int for_condition) { /* * If the result will be used for a conditional, there's no need to * generate an instruction to convert the value to boolean. The opcode * that will be used for the condition will perform exactly the same * conversions that this opcode would apply; avoid the redundant work * in this case, and simply generate the underlying expression * directly. */ if (for_condition) { /* generate the underlying expression without modification */ sub_->gen_code(discard, for_condition); /* done */ return; } /* * if the sub-expression is already guaranteed to be a boolean value, * there's nothing we need to do */ if (sub_->is_bool()) return; /* * Generate the subexpression and apply the BOOLIZE operator. Since * we're explicitly boolean-izing the value, there's no need for the * subexpression to do the same thing, so the subexpression can * pretend it's generating for a conditional. */ gen_unary(OPC_BOOLIZE, discard, TRUE); } /* ------------------------------------------------------------------------ */ /* * bitwise NOT */ void CTPNBNot::gen_code(int discard, int) { gen_unary(OPC_BNOT, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * arithmetic positive */ void CTPNPos::gen_code(int discard, int) { /* * simply generate our operand, since the operator itself has no * effect */ sub_->gen_code(discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * unary arithmetic negative */ void CTPNNeg::gen_code(int discard, int) { gen_unary(OPC_NEG, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * pre-increment */ void CTPNPreInc::gen_code(int discard, int) { /* use the static handler */ CTPNPreInc_gen_code(sub_, discard); } /* static pre-inc code generator, for sharing with other nodes */ void CTPNPreInc_gen_code(CTcPrsNode *sub, int discard) { /* ask the subnode to generate it */ void *ctx; if (!sub->gen_code_asi(discard, 1, TC_ASI_PREINC, "++", 0, FALSE, TRUE, &ctx)) { /* increment the value at top of stack */ G_cg->write_op(OPC_INC); /* * generate a simple assignment back to the subexpression; if * we're using the value, let the simple assignment leave its * value on the stack, since the result is the value *after* the * increment */ sub->gen_code_asi(discard, 2, TC_ASI_PREINC, "++", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * pre-decrement */ void CTPNPreDec::gen_code(int discard, int) { /* ask the subnode to generate it */ void *ctx; if (!sub_->gen_code_asi(discard, 1, TC_ASI_PREDEC, "--", 0, FALSE, TRUE, &ctx)) { /* decrement the value at top of stack */ G_cg->write_op(OPC_DEC); /* * generate a simple assignment back to the subexpression; if * we're using the value, let the simple assignment leave its * value on the stack, since the result is the value *after* the * decrement */ sub_->gen_code_asi(discard, 2, TC_ASI_PREDEC, "--", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * post-increment */ void CTPNPostInc::gen_code(int discard, int) { /* ask the subnode to generate it */ void *ctx; if (!sub_->gen_code_asi(discard, 1, TC_ASI_POSTINC, "++", 0, FALSE, TRUE, &ctx)) { /* * if we're keeping the result, duplicate the value at top of * stack prior to the increment - since this is a * post-increment, the result is the value *before* the * increment */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* increment the value at top of stack */ G_cg->write_op(OPC_INC); /* * Generate a simple assignment back to the subexpression. * Discard the result of this assignment, regardless of whether * the caller wants the result of the overall expression, * because we've already pushed the actual result, which is the * original value before the increment operation. */ sub_->gen_code_asi(TRUE, 2, TC_ASI_POSTINC, "++", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * post-decrement */ void CTPNPostDec::gen_code(int discard, int) { /* ask the subnode to generate it */ void *ctx; if (!sub_->gen_code_asi(discard, 1, TC_ASI_POSTDEC, "--", 0, FALSE, TRUE, &ctx)) { /* * if we're keeping the result, duplicate the value at top of * stack prior to the decrement - since this is a * post-decrement, the result is the value *before* the * decrement */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* decrement the value at top of stack */ G_cg->write_op(OPC_DEC); /* * Generate a simple assignment back to the subexpression. * Discard the result of this assignment, regardless of whether * the caller wants the result of the overall expression, * because we've already pushed the actual result, which is the * original value before the decrement operation. */ sub_->gen_code_asi(TRUE, 2, TC_ASI_POSTDEC, "--", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * operator 'new' */ void CTPNNew::gen_code(int discard, int /*for condition*/) { /* * ask my subexpression to generate the code - at this point we * don't know the number of arguments, so pass in zero for now */ sub_->gen_code_new(discard, 0, FALSE, 0, FALSE, transient_); } /* ------------------------------------------------------------------------ */ /* * operator 'delete' */ void CTPNDelete::gen_code(int, int) { /* 'delete' generates no code for T3 VM */ } /* ------------------------------------------------------------------------ */ /* * comma operator */ void CTPNComma::gen_code(int discard, int for_condition) { /* * Generate each side's code. Note that the left side is *always* * discarded, regardless of whether the result of the comma operator * will be discarded. After we generate our subexpressions, there's * nothing left to do, since the comma operator itself doesn't * change anything - we simply use the right operand result as our * result. * * Pass through the 'for_condition' status to the right operand, * since we pass through its result to the caller. For the left * operand, treat it as a condition - we don't care about the result * value, so don't bother performing any extra conversions on it. */ left_->gen_code(TRUE, TRUE); right_->gen_code(discard, for_condition); } /* ------------------------------------------------------------------------ */ /* * logical OR (short-circuit logic) */ void CTPNOr::gen_code(int discard, int for_condition) { CTcCodeLabel *lbl; /* * First, evaluate the left-hand side; we need the result even if * we're discarding the overall expression, since we will check the * result to see if we should even evaluate the right-hand side. * We're using the value for a condition, so don't bother * boolean-izing it. */ left_->gen_code(FALSE, TRUE); /* * If the left-hand side is true, there's no need to evaluate the * right-hand side (and, in fact, we're not even allowed to evaluate * the right-hand side because of the short-circuit logic rule). * So, if the lhs is true, we want to jump around the code to * evaluate the rhs, saving the 'true' result if we're not * discarding the overall result. */ lbl = gen_jump_ahead(discard ? OPC_JT : OPC_JST); /* * Evaluate the right-hand side. We don't need to save the result * unless we need the result of the overall expression. Generate * the value as though we were going to booleanize it ourselves, * since we'll do just that (hence pass for_condition = TRUE). */ right_->gen_code(discard, TRUE); /* * If we discarded the result, we generated a JT which explicitly * popped a value. If we didn't discard the result, we generated a * JST; this may or may not pop the value. However, if it doesn't * pop the value (save on true), it will bypass the right side * evaluation, and will thus "pop" that value in the sense that it * will never be pushed. So, note a pop either way. */ G_cg->note_pop(); /* define the label for the jump over the rhs */ def_label_pos(lbl); /* * If the result is not going to be used directly for a condition, we * must boolean-ize the value. This isn't necessary if both of the * operands are already guaranteed to be boolean values. */ if (!for_condition && !(left_->is_bool() && right_->is_bool())) G_cg->write_op(OPC_BOOLIZE); } /* * Generate code for the short-circuit OR when used in a condition. We can * use the fact that we're being used conditionally to avoid actually * pushing the result value onto the stack, instead simply branching to the * appropriate point in the enclosing control structure instead. */ void CTPNOr::gen_code_cond(CTcCodeLabel *then_label, CTcCodeLabel *else_label) { CTcCodeLabel *internal_then; /* * First, generate the conditional code for our left operand. If the * condition is true, we can short-circuit the rest of the expression * by jumping directly to the 'then' label. If the caller provided a * 'then' label, we can jump directly to the caller's 'then' label; * otherwise, we must synthesize our own internal label, which we'll * define at the end of our generated code so that we'll fall through * on true to the enclosing code. In any case, we want to fall through * if the condition is false, so that control will flow to the code for * our right operand if the left operand is false. */ internal_then = (then_label == 0 ? G_cs->new_label_fwd() : then_label); left_->gen_code_cond(internal_then, 0); /* * Now, generate code for our right operand. We can generate this code * using the caller's destination labels directly: if we reach this * code at all, it's because the left operand was false, in which case * the result is simply the value of the right operand. */ right_->gen_code_cond(then_label, else_label); /* * If we created an internal 'then' label, it goes at the end of our * generated code: this ensures that we fall off the end of our code * if the left subexpression is true, which is what the caller told us * they wanted when they gave us a null 'then' label. If the caller * gave us an explicit 'then' label, we'll have jumped there directly * if the first subexpression was true. */ if (then_label == 0) def_label_pos(internal_then); } /* ------------------------------------------------------------------------ */ /* * logical AND (short-circuit logic) */ void CTPNAnd::gen_code(int discard, int for_condition) { /* * first, evaluate the left-hand side; we need the result even if * we're discarding the overall expression, since we will check the * result to see if we should even evaluate the right-hand side */ left_->gen_code(FALSE, TRUE); /* * If the left-hand side is false, there's no need to evaluate the * right-hand side (and, in fact, we're not even allowed to evaluate * the right-hand side because of the short-circuit logic rule). * So, if the lhs is false, we want to jump around the code to * evaluate the rhs, saving the false result if we're not discarding * the overall result. */ CTcCodeLabel *lbl = gen_jump_ahead(discard ? OPC_JF : OPC_JSF); /* * Evaluate the right-hand side. We don't need to save the result * unless we need the result of the overall expression. */ right_->gen_code(discard, TRUE); /* define the label for the jump over the rhs */ def_label_pos(lbl); /* * If we discarded the result, we generated a JF which explicitly * popped a value. If we didn't discard the result, we generated a * JSF; this may or may not pop the value. However, if it doesn't * pop the value (save on false), it will bypass the right side * evaluation, and will thus "pop" that value in the sense that it * will never be pushed. So, note a pop either way. */ G_cg->note_pop(); /* * If the result is not going to be used directly for a condition, we * must boolean-ize the value. This isn't necessary if both of the * sub-expressions are already guaranteed to be boolean values. */ if (!for_condition && !(left_->is_bool() && right_->is_bool())) G_cg->write_op(OPC_BOOLIZE); } /* * Generate code for the short-circuit AND when used in a condition. We * can use the fact that we're being used conditionally to avoid actually * pushing the result value onto the stack, instead simply branching to the * appropriate point in the enclosing control structure instead. */ void CTPNAnd::gen_code_cond(CTcCodeLabel *then_label, CTcCodeLabel *else_label) { CTcCodeLabel *internal_else; /* * First, generate the conditional code for our left operand. If the * condition is false, we can short-circuit the rest of the expression * by jumping directly to the 'else' label. If the caller provided an * 'else' label, we can jump directly to the caller's 'else' label; * otherwise, we must synthesize our own internal label, which we'll * define at the end of our generated code so that we'll fall through * on false to the enclosing code. In any case, we want to fall * through if the condition is true, so that control will flow to the * code for our right operand if the left operand is true. */ internal_else = (else_label == 0 ? G_cs->new_label_fwd() : else_label); left_->gen_code_cond(0, internal_else); /* * Now, generate code for our right operand. We can generate this code * using the caller's destination labels directly: if we reach this * code at all, it's because the left operand was true, in which case * the result is simply the value of the right operand. */ right_->gen_code_cond(then_label, else_label); /* * If we created an internal 'else' label, it goes at the end of our * generated code: this ensures that we fall off the end of our code * if the left subexpression is false, which is what the caller told * us they wanted when they gave us a null 'else' label. If the * caller gave us an explicit 'else' label, we'll have jumped there * directly if the first subexpression was false. */ if (else_label == 0) def_label_pos(internal_else); } /* ------------------------------------------------------------------------ */ /* * bitwise OR */ void CTPNBOr::gen_code(int discard, int) { gen_binary(OPC_BOR, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * bitwise AND */ void CTPNBAnd::gen_code(int discard, int) { gen_binary(OPC_BAND, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * bitwise XOR */ void CTPNBXor::gen_code(int discard, int) { gen_binary(OPC_XOR, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * greater-than */ void CTPNGt::gen_code(int discard, int) { gen_binary(OPC_GT, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * greater-or-equal */ void CTPNGe::gen_code(int discard, int) { gen_binary(OPC_GE, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * less-than */ void CTPNLt::gen_code(int discard, int) { gen_binary(OPC_LT, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * less-or-equal */ void CTPNLe::gen_code(int discard, int) { gen_binary(OPC_LE, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * compare for equality */ void CTPNEq::gen_code(int discard, int) { gen_binary(OPC_EQ, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * compare for inequality */ void CTPNNe::gen_code(int discard, int) { gen_binary(OPC_NE, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * 'is in' */ void CTPNIsIn::gen_code(int discard, int) { CTPNArglist *lst; CTPNArg *arg; CTcCodeLabel *lbl_found; CTcCodeLabel *lbl_done; /* allocate our 'found' label */ lbl_found = G_cs->new_label_fwd(); /* * allocate our 'done' label - we only need to do this if we don't * have a constant true value and we're not discarding the result */ if (!const_true_ && !discard) lbl_done = G_cs->new_label_fwd(); /* generate my left-side expression */ left_->gen_code(FALSE, FALSE); /* the right side is always an argument list */ lst = (CTPNArglist *)right_; /* compare to each element in the list on the right */ for (arg = lst->get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg()) { /* * duplicate the left-side value, so we don't have to generate * it again for this comparison */ G_cg->write_op(OPC_DUP); /* generate this list element */ arg->gen_code(FALSE, FALSE); /* if they're equal, jump to the 'found' label */ G_cg->write_op(OPC_JE); G_cs->write_ofs2(lbl_found, 0); /* we pushed one more (DUP) and popped two (JE) */ G_cg->note_push(1); G_cg->note_pop(2); } /* * Generate the code that comes at the end of all of tests when we * fail to find any matches - we simply discard the left-side value * from the stack, push our 'nil' value, and jump to the end label. * * If we have a constant 'true' value, there's no need to do any of * this, because we know that, even after testing all of our * non-constant values, there's a constant value that makes the * entire expression true, and we can thus just fall through to the * 'found' code. * * If we're discarding the result, there's no need to push a * separate value for the result, so we can just fall through to the * common ending code in this case. */ if (!const_true_ && !discard) { G_cg->write_op(OPC_DISC); G_cg->write_op(OPC_PUSHNIL); G_cg->write_op(OPC_JMP); G_cs->write_ofs2(lbl_done, 0); } /* * Generate the 'found' code - this discards the left-side value and * pushes our 'true' result. Note that there's no reason to push * our result if we're discarding it. */ def_label_pos(lbl_found); G_cg->write_op(OPC_DISC); /* * if we're discarding the result, just note the pop of the left * value; otherwise, push our result */ if (discard) G_cg->note_pop(); else G_cg->write_op(OPC_PUSHTRUE); /* our 'done' label is here, if we needed one */ if (!const_true_ && !discard) def_label_pos(lbl_done); } /* ------------------------------------------------------------------------ */ /* * 'not in' */ void CTPNNotIn::gen_code(int discard, int) { CTPNArglist *lst; CTPNArg *arg; CTcCodeLabel *lbl_found; CTcCodeLabel *lbl_done; /* allocate our 'found' label */ lbl_found = G_cs->new_label_fwd(); /* * allocate our 'done' label - we only need to do this if we don't * have a constant false value */ if (!const_false_ && !discard) lbl_done = G_cs->new_label_fwd(); /* generate my left-side expression */ left_->gen_code(FALSE, FALSE); /* the right side is always an argument list */ lst = (CTPNArglist *)right_; /* compare to each element in the list on the right */ for (arg = lst->get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg()) { /* * duplicate the left-side value, so we don't have to generate * it again for this comparison */ G_cg->write_op(OPC_DUP); /* generate this list element */ arg->gen_code(FALSE, FALSE); /* if they're equal, jump to the 'found' label */ G_cg->write_op(OPC_JE); G_cs->write_ofs2(lbl_found, 0); /* we pushed one more (DUP) and popped two (JE) */ G_cg->note_push(1); G_cg->note_pop(2); } /* * Generate the code that comes at the end of all of tests when we * fail to find any matches - we simply discard the left-side value * from the stack, push our 'true' value, and jump to the end label. * * If we have a constant 'nil' value, however, there's no need to do * any of this, because we know that, even after testing all of our * non-constant values, there's a matching constant value that makes * the entire expression false (because 'not in' is false if we find * a match), and we can thus just fall through to the 'found' code. */ if (!const_false_ && !discard) { G_cg->write_op(OPC_DISC); G_cg->write_op(OPC_PUSHTRUE); G_cg->write_op(OPC_JMP); G_cs->write_ofs2(lbl_done, 0); } /* * generate the 'found' code - this discards the left-side value and * pushes our 'nil' result (because the result of 'not in' is false * if we found the value) */ def_label_pos(lbl_found); G_cg->write_op(OPC_DISC); /* push the result, or note the pop if we're just discarding it */ if (discard) G_cg->note_pop(); else G_cg->write_op(OPC_PUSHNIL); /* our 'done' label is here, if we needed one */ if (!const_false_ && !discard) def_label_pos(lbl_done); } /* ------------------------------------------------------------------------ */ /* * bit-shift left */ void CTPNShl::gen_code(int discard, int) { gen_binary(OPC_SHL, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * arithmetic shift right '>>' */ void CTPNAShr::gen_code(int discard, int) { gen_binary(OPC_ASHR, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * logical shift right '>>>' */ void CTPNLShr::gen_code(int discard, int) { gen_binary(OPC_LSHR, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * multiply */ void CTPNMul::gen_code(int discard, int) { /* if either side is zero or one, we can apply special handling */ if (left_->is_const_int(0)) { /* evaluate the right for side effects and discard the result */ right_->gen_code(TRUE, TRUE); /* the result is zero */ G_cg->write_op(OPC_PUSH_0); G_cg->note_push(); /* done */ return; } else if (right_->is_const_int(0)) { /* evaluate the left for side effects and discard the result */ left_->gen_code(TRUE, TRUE); /* the result is zero */ G_cg->write_op(OPC_PUSH_0); G_cg->note_push(); /* done */ return; } else if (left_->is_const_int(1)) { /* * evaluate the right side - it's the result; note that, because * of the explicit multiplication, we must compute logical * results using assignment (not 'for condition') rules */ right_->gen_code(discard, FALSE); /* done */ return; } else if (right_->is_const_int(1)) { /* evaluate the right side - it's the result */ left_->gen_code(discard, FALSE); /* done */ return; } /* apply generic handling */ gen_binary(OPC_MUL, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * divide */ void CTPNDiv::gen_code(int discard, int for_cond) { /* if dividing by 1, we can skip the whole thing (except side effects) */ if (right_->is_const_int(1)) { /* * simply generate the left side for side effects; actually * doing the arithmetic has no effect */ left_->gen_code(discard, for_cond); return; } /* if the left side is zero, the result is always zero */ if (left_->is_const_int(0)) { /* evaluate the right for side effects, but discard the result */ right_->gen_code(TRUE, TRUE); /* the result is zero */ G_cg->write_op(OPC_PUSH_0); G_cg->note_push(); return; } /* use generic code generation */ gen_binary(OPC_DIV, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * modulo */ void CTPNMod::gen_code(int discard, int for_condition) { /* if dividing by 1, we can skip the whole thing (except side effects) */ if (right_->is_const_int(1)) { /* * simply generate the left side for side effects; actually * doing the arithmetic has no effect */ left_->gen_code(discard, for_condition); /* the result is zero */ G_cg->write_op(OPC_PUSH_0); G_cg->note_push(); return; } /* if the left side is zero, the result is always zero */ if (left_->is_const_int(0)) { /* evaluate the right for side effects, but discard the result */ right_->gen_code(TRUE, TRUE); /* the result is zero */ G_cg->write_op(OPC_PUSH_0); G_cg->note_push(); return; } /* use generic processing */ gen_binary(OPC_MOD, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * subtract */ void CTPNSub::gen_code(int discard, int for_cond) { /* check for subtracting 1, which we can accomplish more efficiently */ if (right_->is_const_int(1)) { /* * We're subtracting one - use decrement. The decrement * operator itself has no side effects, so we can pass through * the 'discard' status to the subnode. */ left_->gen_code(discard, FALSE); /* apply decrement if we're not discarding the result */ if (!discard) G_cg->write_op(OPC_DEC); } else { /* we can't do anything special - use the general-purpose code */ gen_binary(OPC_SUB, discard, FALSE); } } /* ------------------------------------------------------------------------ */ /* * add */ void CTPNAdd::gen_code(int discard, int) { /* check for adding 1, which we can accomplish more efficiently */ if (right_->is_const_int(1)) { /* * We're adding one - use increment. The increment operator * itself has no side effects, so we can pass through the * 'discard' status to the subnode. */ left_->gen_code(discard, FALSE); /* apply increment if we're not discarding the result */ if (!discard) G_cg->write_op(OPC_INC); } else { /* we can't do anything special - use the general-purpose code */ gen_binary(OPC_ADD, discard, FALSE); } } /* ------------------------------------------------------------------------ */ /* * simple assignment */ void CTPNAsi::gen_code(int discard, int) { /* * Ask the left subnode to generate a simple assignment to the value * on the right. Simple assignments cannot be refused, so we don't * need to try to do any assignment work ourselves. */ left_->gen_code_asi(discard, 1, TC_ASI_SIMPLE, "=", right_, FALSE, TRUE, 0); } /* ------------------------------------------------------------------------ */ /* * add and assign */ void CTPNAddAsi::gen_code(int discard, int) { /* use the static handler */ CTPNAddAsi_gen_code(left_, right_, discard); } /* static handler, for sharing with other node types */ void CTPNAddAsi_gen_code(CTcPrsNode *left, CTcPrsNode *right, int discard) { /* * ask the left subnode to generate an add-and-assign; if it can't, * handle it generically */ void *ctx; if (!left->gen_code_asi(discard, 1, TC_ASI_ADD, "+=", right, FALSE, TRUE, &ctx)) { /* * There's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused. We've already generated * the left side above, so generate the right side. */ right->gen_code(FALSE, FALSE); /* generate the add (which pops two and adds one -> net one pop) */ G_cg->write_op(OPC_ADD); G_cg->note_pop(); /* generate the simple assignment to the lhs */ left->gen_code_asi(discard, 2, TC_ASI_ADD, "+=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * subtract and assign */ void CTPNSubAsi::gen_code(int discard, int) { /* * ask the left subnode to generate a subtract-and-assign; if it * can't, handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_SUB, "-=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_SUB); left_->gen_code_asi(discard, 2, TC_ASI_SUB, "-=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * multiply and assign */ void CTPNMulAsi::gen_code(int discard, int) { /* * ask the left subnode to generate a multiply-and-assign; if it * can't, handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_MUL, "*=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_MUL); left_->gen_code_asi(discard, 2, TC_ASI_MUL, "*=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * divide and assign */ void CTPNDivAsi::gen_code(int discard, int) { /* * ask the left subnode to generate a divide-and-assign; if it * can't, handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_DIV, "/=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_DIV); left_->gen_code_asi(discard, 2, TC_ASI_DIV, "/=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * modulo and assign */ void CTPNModAsi::gen_code(int discard, int) { /* * ask the left subnode to generate a mod-and-assign; if it can't, * handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_MOD, "%=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_MOD); left_->gen_code_asi(discard, 2, TC_ASI_MOD, "%=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * bitwise-AND and assign */ void CTPNBAndAsi::gen_code(int discard, int) { /* * ask the left subnode to generate an AND-and-assign; if it can't, * handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_BAND, "&=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_BAND); left_->gen_code_asi(discard, 2, TC_ASI_BAND, "&=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * bitwise-OR and assign */ void CTPNBOrAsi::gen_code(int discard, int) { /* * ask the left subnode to generate an OR-and-assign; if it can't, * handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_BOR, "|=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_BOR); left_->gen_code_asi(discard, 2, TC_ASI_BOR, "|=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * bitwise-XOR and assign */ void CTPNBXorAsi::gen_code(int discard, int) { /* * ask the left subnode to generate an XOR-and-assign; if it can't, * handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_BXOR, "^=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_XOR); left_->gen_code_asi(discard, 2, TC_ASI_BXOR, "^=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * bit-shift left and assign */ void CTPNShlAsi::gen_code(int discard, int) { /* * ask the left subnode to generate an shift-left-and-assign; if it * can't, handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_SHL, "<<=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_SHL); left_->gen_code_asi(discard, 2, TC_ASI_SHL, "<<=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * arithmetic shift right and assign '>>=' */ void CTPNAShrAsi::gen_code(int discard, int) { /* * ask the left subnode to generate a shift-right-and-assign; if it * can't, handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_ASHR, ">>=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_ASHR); left_->gen_code_asi(discard, 2, TC_ASI_ASHR, ">>=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * logical shift right and assign '>>>=' */ void CTPNLShrAsi::gen_code(int discard, int) { /* * ask the left subnode to generate a shift-right-and-assign; if it * can't, handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_LSHR, ">>>=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_LSHR); left_->gen_code_asi(discard, 2, TC_ASI_LSHR, ">>>=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * subscript a list/array value */ void CTPNSubscript::gen_code(int discard, int) { gen_binary(OPC_INDEX, discard, FALSE); } /* * assign to a subscripted value */ int CTPNSubscript::gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int, int xplicit, void **ctx) { void *sctx; /* * An assignment to a subscript expression is not what it appears. * It's actually a compound assignment that evaluates the subscript * expression, assigns the RHS, and then assigns the *result* of that * first assignment to the left operand of the index operator. In * other words, a[b]=c is effectively rewritten as this: * *. a = a.operator[]=(b, c) * * which, by analogy with the other compound assignment operators, we * can rewrite as * *. a []== (b, c) */ if (typ == TC_ASI_SIMPLE) { int rhs_depth; int extra_disc = 0; /* * Simple assignment. Generate the value to assign to the element * - that's the right side of the assignment operator. If rhs is * null, it means the caller has already done this. */ if (rhs != 0) { /* generate code for the RHS */ rhs->gen_code(FALSE, FALSE); } /* * if we're not discarding the result, duplicate the value to be * assigned, so that we leave a copy on the stack after we're * finished. This is necessary because the SETIND instruction will * consume one copy of the value. */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* note the stack depth where the RHS value resides */ rhs_depth = G_cg->get_sp_depth(); /* * Generate the value to be subscripted - that's my left-hand side. * Generate this as phase 1 of a two-phase assignment, with the * special pseudo-operator '[]='. Ignore the return value; no one * can fully handle this type of assignment in phase 1. Also, note * that we don't yet have the right-hand value to assign - it's the * result of the SETIND yet to come. */ left_->gen_code_asi(FALSE, 1, TC_ASI_IDX, op, 0, TRUE, xplicit, &sctx); /* * The phase 1 []= code could have left context information on the * stack that it will need in phase 2, in addition to the LHS value * that we need for the SETIND we're about to generate. For the * SETIND, we need the RHS value at index 1, just above the LHS * value. So if the new depth is more than 1 greater than the RHS * depth, we need to do some stack rearrangement. * * If phase 1 left *nothing* on the stack, it means that the value * being indexed can't handle assignments at all, as in * self[i]=val. This is acceptable: we can still perform the []= * portion to assign to the indexed element, but we won't be able * to do the second part, which is assigning the result of the []= * operator to the lhs of the [] operator. We'll simply ignore * that second part in this case. */ if (G_cg->get_sp_depth() == rhs_depth) { /* * the lhs of [] is not assignable; simply generate the lhs so * that we can index it */ left_->gen_code(FALSE, FALSE); } else if (G_cg->get_sp_depth() > rhs_depth + 1) { /* figure the distance to the RHS */ int dist = G_cg->get_sp_depth() - rhs_depth; /* the instruction can only accommodate a distance up to 255 */ if (dist > 255) G_tok->log_error(TCERR_EXPR_TOO_COMPLEX); /* re-push the RHS */ G_cg->write_op(OPC_GETSPN); G_cs->write((uchar)dist); G_cg->note_push(); /* * swap it with the left operand of the [] to get everything in * the proper order */ G_cg->write_op(OPC_SWAP); /* * the original RHS we pushed is now superfluous, so we'll need * to explicitly pop it when we're done */ extra_disc += 1; } /* generate the index value - that's my right-hand side */ right_->gen_code(FALSE, FALSE); /* generate the assign-to-indexed-value opcode */ G_cg->write_op(OPC_SETIND); /* setind pops three and pushes one - net of pop 2 */ G_cg->note_pop(2); /* * The top value now on the stack is the new container value. The * new container will be different from the old container in some * cases (with lists, for example, because we must create a new * list object to contain the modified list value). Therefore, if * my left-hand side is an lvalue, we must assign the new container * to the left-hand side - this makes something like "x[1] = 5" * actually change the value in "x" if "x" is a local variable. If * my left-hand side isn't an lvalue, don't bother with this step, * and simply discard the new container value. * * Handle the assignment to the new container as phase 2 of the * '[]=' pseudo-operator that we set up above. This ensures that * the left operand had a chance to save any sub-expression values, * which it can now reuse. * * Regardless of whether we're keeping the result of the overall * expression, we're definitely not keeping the result of assigning * the new container - the result of the assignment is the value * assigned, not the container. Thus, discard = true in this call. * * * There's a special case that's handled through the peep-hole * optimizer: if we are assigning to a local variable and indexing * with a constant integer value, we will have converted the whole * operation to a SETINDLCL1I8. That instruction takes care of * assigning the value back to the rvalue, so we don't need to * generate a separate rvalue assignment. */ if (G_cg->get_last_op() == OPC_SETINDLCL1I8) { /* * no assignment is necessary - we just need to account for the * difference in the stack arrangement with this form of the * assignment, which is that we don't leave the container value * on the stack */ G_cg->note_pop(); } else if (!left_->gen_code_asi(TRUE, 2, TC_ASI_IDX, op, 0, TRUE, xplicit, &sctx)) { /* no assignment is possible; discard the new container value */ G_cg->write_op(OPC_DISC); G_cg->note_pop(); } /* discard any extra stack elements */ while (extra_disc-- != 0) { G_cg->write_op(OPC_DISC); G_cg->note_pop(); } /* handled */ return TRUE; } else if (phase == 1) { /* * Compound assignment, phase 1. Generate the lvalue's value, and * also leave the indexee and index values on the stack separately * for use later in doing the assignment to indexee[index]. */ /* * Generate the value to be subscripted - that's my left-hand side. * Generate it as phase 1 of assignment through my pseudo-operator * []=. */ left_->gen_code_asi(FALSE, 1, TC_ASI_IDX, op, 0, TRUE, xplicit, &sctx); /* * save the context in the caller's context holder, so that it'll * get passed back to us on the second pass */ *ctx = sctx; /* generate the index value - that's my right-hand side */ right_->gen_code(FALSE, FALSE); /* duplicate them both so we have them later, for the assignment */ G_cg->write_op(OPC_DUP2); G_cg->note_push(2); /* generate the INDEX operator to compute indexee[index] */ G_cg->write_op(OPC_INDEX); G_cg->note_pop(); /* * If the caller pushed a RHS value for its own use in assigning, */ /* tell the caller to proceed (i.e., we didn't finish the job) */ return FALSE; } else if (phase == 2) { /* * Compound assignment, phase 2. We now have the combined value to * be assigned on the stack, followed by the indexee and index * values on the stack. */ /* restore the left-side phase 1 context from the caller */ sctx = *ctx; /* if the assigned value will be further assigned, save a copy */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* * The stack is now (bottom to top) INDEXEE INDEX RHS RHS (if * !discard) or INDEXEE INDEX RHS (if discard). SETIND needs it to * be (RHS) RHS INDEXEE INDEX. Swap the top one or two elements * with the next two to rearrange into SETIDX order. */ if (discard) { /* INDEXEE INDEX RHS -> RHS INDEX INDEXEE */ G_cg->write_op(OPC_SWAPN); G_cs->write(0); G_cs->write(2); /* -> RHS INDEXEE INDEX */ G_cg->write_op(OPC_SWAP); } else { /* swap INDEXEE INDEX with RHS RHS */ G_cg->write_op(OPC_SWAP2); } /* assign the rhs to the indexed value */ G_cg->write_op(OPC_SETIND); G_cg->note_pop(2); /* * The new container value is now on the stack; assign it to our * left-hand side. Note that the SETINDLCL1I8 optimization (see * the simple assignment case above) is impossible here, because we * had to generate the code to carry out the combination operator * (the '+' in '+=', for example) before the SETIND. There's thus * nothing for the SETIND to combine with. * * Generate as phase 2 of the []= assignment we started in phase 1. */ if (!left_->gen_code_asi(TRUE, 2, TC_ASI_IDX, op, 0, TRUE, xplicit, &sctx)) { /* no assignment was necessary; discard the container value */ G_cg->write_op(OPC_DISC); G_cg->note_pop(); } /* handled */ return TRUE; } else { /* ignore any other phases */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * If-nil operator */ void CTPNIfnil::gen_code(int discard, int for_condition) { /* generate the first operand */ first_->gen_code(FALSE, FALSE); /* check to see if we're keeping the result or merely testing it */ if (!discard) { /* we're keep the result - make a copy for the JNOTNIL */ G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* if it's not nil, we're done - jump to the end of the operator */ CTcCodeLabel *lbl_end = gen_jump_ahead(OPC_JNOTNIL); G_cg->note_pop(); /* * We're on the 'nil' branch now, meaning we're not going to yield the * first value after all. If we're yielding anything, discard the * extra yielding copy we made of the first value. */ if (!discard) { G_cg->write_op(OPC_DISC); G_cg->note_pop(); } /* generate the 'nil' branch, yielding the second operand */ second_->gen_code(discard, for_condition); /* this is the end of the test - generate the end label */ def_label_pos(lbl_end); } /* ------------------------------------------------------------------------ */ /* * conditional operator */ void CTPNIf::gen_code(int discard, int for_condition) { /* * Generate the condition value - we need the value regardless of * whether the overall result is going to be used, because we need * it to determine which branch to take. Generate the subexpression * for a condition, so that we don't perform any extra unnecessary * conversions on it. */ first_->gen_code(FALSE, TRUE); /* if the condition is false, jump to the 'else' expression part */ CTcCodeLabel *lbl_else = gen_jump_ahead(OPC_JF); /* JF pops a value */ G_cg->note_pop(); /* * Generate the 'then' expression part. Only request a return value if * it has one AND we're not discarding it. If it doesn't return a * value, and we actually need one, we'll supply a default 'nil' value * next. This value will be our yielded value (in this branch, * anyway), so pass through the for-condition flag. */ second_->gen_code(discard || !second_->has_return_value(), for_condition); /* * If this expression has no return value, and we need the return * value, supply nil as the result. */ if (!discard && !second_->has_return_value()) { G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); } /* unconditionally jump over the 'else' part */ CTcCodeLabel *lbl_end = gen_jump_ahead(OPC_JMP); /* set the label for the 'else' part */ def_label_pos(lbl_else); /* * Generate the 'else' part. Only request a return value if it has one * AND we're not discarding it. Pass through 'discard' and * 'for_condition', since this result is our result. */ third_->gen_code(discard || !third_->has_return_value(), for_condition); /* * If this expression has no return value, and we need the return * value, supply nil as the result. */ if (!discard && !third_->has_return_value()) { G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); } /* * Because of the jump, we only evaluate one of the two expressions * we generated, so note an extra pop for the branch we didn't take. * Note that if either one pushes a value, both will, since we'll * explicitly have pushed nil for the one that doesn't generate a * value to keep the stack balanced on both branches. * * If neither of our expressions yields a value, don't pop anything * extra, since we won't think we've pushed two values in the course * of generating the two expressions. */ if (second_->has_return_value() || third_->has_return_value()) G_cg->note_pop(); /* set the label for the end of the expression */ def_label_pos(lbl_end); } /* ------------------------------------------------------------------------ */ /* * symbol */ void CTPNSym::gen_code(int discard, int) { /* * Look up the symbol; if it's undefined, add a default property * symbol entry if possible. Then ask the symbol to generate the * code. */ G_cs->get_symtab() ->find_or_def_prop_implied(get_sym_text(), get_sym_text_len(), FALSE, G_cs->is_self_available()) ->gen_code(discard); } /* * assign to a symbol */ int CTPNSym::gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx) { /* * Look up the symbol; if it's undefined and there's a "self" object * available, define it as a property by default, since a property * is the only kind of symbol that we could possibly assign to * without having defined anywhere in the program. Once we have the * symbol, tell it to generate the code for assigning to it. */ return G_cs->get_symtab() ->find_or_def_prop_implied(get_sym_text(), get_sym_text_len(), FALSE, G_cs->is_self_available()) ->gen_code_asi(discard, phase, typ, op, rhs, ignore_errors, xplicit, ctx); } /* * take the address of the symbol */ void CTPNSym::gen_code_addr() { /* * Look up our symbol in the global symbol table, then ask the * resulting symbol to generate the appropriate code. If the symbol * isn't defined, assume it's a property. * * Note that we look only in the global symbol table, because local * symbols have no address value. So, even if the symbol is defined in * the local table, ignore the local definition and look at the global * definition. */ G_prs->get_global_symtab() ->find_or_def_prop_explicit(get_sym_text(), get_sym_text_len(), FALSE) ->gen_code_addr(); } /* * call the symbol */ void CTPNSym::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* * Look up our symbol in the symbol table, then ask the resulting * symbol to generate the appropriate call. The symbol is * implicitly a property (if in a method context), since that's the * only kind of undefined symbol that we could be calling. */ G_cs->get_symtab() ->find_or_def_prop_implied(get_sym_text(), get_sym_text_len(), FALSE, G_cs->is_self_available()) ->gen_code_call(discard, argc, varargs, named_args); } /* * generate code for 'new' */ void CTPNSym::gen_code_new(int discard, int argc, int varargs, CTcNamedArgs *named_args, int /*from_call*/, int is_transient) { /* * Look up our symbol, then ask the resulting symbol to generate the * 'new' code. If the symbol is undefined, add an 'undefined' entry * to the table; we can't implicitly create an object symbol. */ G_cs->get_symtab() ->find_or_def_undef(get_sym_text(), get_sym_text_len(), FALSE) ->gen_code_new(discard, argc, varargs, named_args, is_transient); } /* * generate a property ID expression */ vm_prop_id_t CTPNSym::gen_code_propid(int check_only, int is_expr) { CTcSymbol *sym; CTcPrsSymtab *symtab; /* * Figure out where to look for the symbol. If the symbol was given * as an expression (in other words, it was explicitly enclosed in * parentheses), look it up in the local symbol table, since it * could refer to a local. Otherwise, it must refer to a property, * so look only in the global table. * * If the symbol isn't defined already, define it as a property now. * Because the symbol is explicitly on the right side of a member * evaluation, we can define it as a property whether or not there's * a valid "self" in this context. */ if (is_expr) { /* it's an expression - look it up in the local symbol table */ symtab = G_cs->get_symtab(); } else { /* it's a simple symbol - look only in the global symbol table */ symtab = G_prs->get_global_symtab(); } /* * look it up (note that this will always return a valid symbol, * since it will create one if we can't find an existing entry) */ sym = symtab->find_or_def_prop(get_sym_text(), get_sym_text_len(), FALSE); /* ask the symbol to generate the property reference */ return sym->gen_code_propid(check_only, is_expr); } /* * generate code for a member expression */ void CTPNSym::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* * Look up the symbol, and let it do the work. There's no * appropriate default for the symbol, so leave it undefined if we * can't find it. */ G_cs->get_symtab() ->find_or_def_undef(get_sym_text(), get_sym_text_len(), FALSE) ->gen_code_member(discard, prop_expr, prop_is_expr, argc, varargs, named_args); } /* * generate code for an object before a '.' */ vm_obj_id_t CTPNSym::gen_code_obj_predot(int *is_self) { /* * Look up the symbol, and let it do the work. There's no default * type for the symbol, so leave it undefined if we don't find it. */ return G_cs->get_symtab() ->find_or_def_undef(get_sym_text(), get_sym_text_len(), FALSE) ->gen_code_obj_predot(is_self); } /* ------------------------------------------------------------------------ */ /* * resolved symbol */ void CTPNSymResolved::gen_code(int discard, int) { /* let the symbol handle it */ sym_->gen_code(discard); } /* * assign to a symbol */ int CTPNSymResolved::gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx) { /* let the symbol handle it */ return sym_->gen_code_asi(discard, phase, typ, op, rhs, ignore_errors, xplicit, ctx); } /* * take the address of the symbol */ void CTPNSymResolved::gen_code_addr() { /* let the symbol handle it */ sym_->gen_code_addr(); } /* * call the symbol */ void CTPNSymResolved::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* let the symbol handle it */ sym_->gen_code_call(discard, argc, varargs, named_args); } /* * generate code for 'new' */ void CTPNSymResolved::gen_code_new(int discard, int argc, int varargs, CTcNamedArgs *named_args, int /*from_call*/, int is_transient) { /* let the symbol handle it */ sym_->gen_code_new(discard, argc, varargs, named_args, is_transient); } /* * generate a property ID expression */ vm_prop_id_t CTPNSymResolved::gen_code_propid(int check_only, int is_expr) { /* let the symbol handle it */ return sym_->gen_code_propid(check_only, is_expr); } /* * generate code for a member expression */ void CTPNSymResolved::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* let the symbol handle it */ sym_->gen_code_member(discard, prop_expr, prop_is_expr, argc, varargs, named_args); } /* * generate code for an object before a '.' */ vm_obj_id_t CTPNSymResolved::gen_code_obj_predot(int *is_self) { /* let the symbol handle it */ return sym_->gen_code_obj_predot(is_self); } /* ------------------------------------------------------------------------ */ /* * Debugger local variable symbol */ /* * generate code to evaluate the variable */ void CTPNSymDebugLocal::gen_code(int discard, int for_condition) { /* if we're not discarding the value, push the local */ if (!discard) { /* generate the debugger local/parameter variable instruction */ G_cg->write_op(is_param_ ? OPC_GETDBARG : OPC_GETDBLCL); G_cs->write2(var_id_); G_cs->write2(frame_idx_); /* note that we pushed the value */ G_cg->note_push(); /* if it's a context local, get the value from the context array */ if (ctx_arr_idx_ != 0) { CTPNConst::s_gen_code_int(ctx_arr_idx_); G_cg->write_op(OPC_INDEX); /* * the 'index' operation pops two values and pushes one, for a * net of one pop */ G_cg->note_pop(); } } } /* * generate code for assigning to this variable */ int CTPNSymDebugLocal::gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *, CTcPrsNode *rhs, int, int, void **) { /* check what we're doing */ if (typ == TC_ASI_SIMPLE || phase == 2) { /* * Simple assignment, or phase 2 of a compound assignment. In * either case, just assign the rvalue to the variable. */ if (rhs != 0) rhs->gen_code(FALSE, FALSE); /* * if we're not discarding the result, duplicate the value so we'll * have a copy after the assignment */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* check for a context property */ if (ctx_arr_idx_ == 0) { /* * generate the debug-local-set instruction - the operands are * the variable number and the stack frame index */ G_cg->write_op(is_param_ ? OPC_SETDBARG : OPC_SETDBLCL); G_cs->write2(var_id_); G_cs->write2(frame_idx_); } else { /* get the local containing our context object */ G_cg->write_op(OPC_GETDBLCL); G_cs->write2(var_id_); G_cs->write2(frame_idx_); /* set the actual variable value in the context object */ CTPNConst::s_gen_code_int(ctx_arr_idx_); G_cg->write_op(OPC_SETIND); G_cg->write_op(OPC_DISC); /* * we did three pops (SETIND), then a push (SETIND), then a pop * (DISC) - this is a net of three extra pops */ G_cg->note_pop(3); } /* the debug-local-set removes the rvalue from the stack */ G_cg->note_pop(); /* handled */ return TRUE; } else if (phase == 1) { /* * Compound assignment, phase 1. Simply generate our value and let * the caller proceed with the generic combination operator. */ gen_code(FALSE, FALSE); return FALSE; } else { /* ignore other phases */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Double-quoted string. The 'discard' status is irrelevant, because we * evaluate double-quoted strings for their side effects. */ void CTPNDstr::gen_code(int discard, int) { /* if we're not discarding the value, it's an error */ if (!discard) G_tok->log_error(TCERR_DQUOTE_IN_EXPR, (int)len_, str_); /* generate the instruction to display it */ G_cg->write_op(OPC_SAY); /* add the string to the constant pool, creating a fixup here */ G_cg->add_const_str(str_, len_, G_cs, G_cs->get_ofs()); /* write a placeholder value, which will be corrected by the fixup */ G_cs->write4(0); } /* ------------------------------------------------------------------------ */ /* * Double-quoted debug string */ void CTPNDebugDstr::gen_code(int, int) { /* generate code to push the in-line string */ G_cg->write_op(OPC_PUSHSTRI); G_cs->write2(len_); G_cs->write(str_, len_); /* write code to display the value */ G_cg->write_op(OPC_SAYVAL); /* note that we pushed the string and then popped it */ G_cg->note_push(); G_cg->note_pop(); } /* ------------------------------------------------------------------------ */ /* * Double-quoted string embedding */ /* * create an embedding */ CTPNDstrEmbed::CTPNDstrEmbed(CTcPrsNode *sub) : CTPNDstrEmbedBase(sub) { } /* * Generate code for a double-quoted string embedding */ void CTPNDstrEmbed::gen_code(int, int) { /* note the stack depth before generating the expression */ int orig_depth = G_cg->get_sp_depth(); /* * Generate code for the embedded expression. If the expression has a * return value, generate the value so that it can be displayed in the * string; but don't request a value if it doesn't have one, as a * return value is optional in this context. This is a normal value * invocation, not a conditional, so we need any applicable normal * value conversions. */ sub_->gen_code(!sub_->has_return_value(), FALSE); /* * If the code generation left anything on the stack, generate code * to display the value via the default display function. */ if (G_cg->get_sp_depth() > orig_depth) { /* add a SAYVAL instruction */ G_cg->write_op(OPC_SAYVAL); /* SAYVAL pops the argument value */ G_cg->note_pop(); } } /* ------------------------------------------------------------------------ */ /* * embedded <> list */ void CTPNStrOneOf::gen_code(int discard, int for_condition) { /* * There are two components to the <> list. First, there's the * list of strings. Second, there's an anonymous OneOfIndexGen object * that we create; this object holds our state and generates our index * values. * * In the simple case, the list is just a constant list of constant * strings. However, if the enclosing string is a dstring, each of * these will be a dstring; and even if the outer string is an sstring, * these could contain further embedded expressions, in which case they * won't be constant. * * If the list is constant, we generate list[generator.getNextIndex()]. * This selects a string from the list and leaves it on the stack, * which is just what our parent node wants. * * If the list isn't constant, we generate a switch on * generator.getNextIndex(), and then fill in the case table from the * list: each case label is the index, and each case's body is the code * generated for the list element, followed by a 'break' to the end of * the switch. The effect once again is to pick an item from the list * and push its value onto the stack. */ /* look up the getNextIndex property */ CTcSymProp *propsym = (CTcSymProp *)G_prs->get_global_symtab()-> find_or_def_prop_explicit("getNextIndex", 12, FALSE); if (propsym->get_type() != TC_SYM_PROP) { G_tok->log_error(TCERR_ONEOF_REQ_GETNXT); return; } /* synthesize a constant value for the property */ CTcConstVal propc; propc.set_prop(propsym->get_prop()); CTPNConst *getNextIndex = new CTPNConst(&propc); /* generate the list */ CTcConstVal *c = lst_->get_const_val(); if (c != 0) { /* the list is a constant - push the list value */ lst_->gen_code(FALSE, FALSE); /* call generator.getNextIndex() to get the next index */ state_obj_->gen_code_member(FALSE, getNextIndex, FALSE, 0, FALSE, 0); /* index the list (this pops two values and pushes one) */ G_cg->write_op(OPC_INDEX); G_cg->note_pop(); } else { /* the list is non-constant - start by generating the index */ state_obj_->gen_code_member(FALSE, getNextIndex, FALSE, 0, FALSE, 0); /* note the number of list elements */ int i, cnt = lst_->get_count(); /* generate the SWITCH */ G_cg->write_op(OPC_SWITCH); G_cs->write2(cnt); G_cg->note_pop(); /* * Build the case table. Each label is an index value; leave the * jump offset zero for now, since we'll have to fill it in as we * generate the code. */ ulong caseofs = G_cs->get_ofs(); for (i = 1 ; i <= cnt ; ++i) { /* write the case label as the 1-based index value */ char buf[VMB_DATAHOLDER]; vm_val_t val; val.set_int(i); vmb_put_dh(buf, &val); G_cs->write(buf, VMB_DATAHOLDER); /* write a placeholder for the jump offset */ G_cs->write2(0); } /* write a placeholder for the default case */ G_cs->write2(0); /* create a forward label for the 'break' to the end of the switch */ CTcCodeLabel *brklbl = G_cs->new_label_fwd(); /* * Now run through the table and generate the code for each list * element. */ CTPNListEle *ele; for (i = 1, ele = lst_->get_head() ; ele != 0 && i <= cnt ; ++i, caseofs += VMB_DATAHOLDER + 2, ele = ele->get_next()) { /* set the jump to this case in the case table */ G_cs->write2_at(caseofs + VMB_DATAHOLDER, G_cs->get_ofs() - (caseofs + VMB_DATAHOLDER)); /* generate the code for this list element */ ele->get_expr()->gen_code(discard, for_condition);; /* generate a jump to the end of the table */ G_cg->write_op(OPC_JMP); G_cs->write_ofs2(brklbl, 0); /* * since all of these branches proceed in parallel, only one * push counts, so don't count every iteration - pretend that * we've already popped the value that this case generated */ if (!discard) G_cg->note_pop(); } /* write the default case - just push nil */ G_cs->write2_at(caseofs, G_cs->get_ofs() - caseofs); if (!discard) { G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); } /* this is the end of the switch - set the 'break' label here */ def_label_pos(brklbl); } } /* ------------------------------------------------------------------------ */ /* * Create or define an internal compiler-defined function in the program. */ static CTcSymFunc *get_internal_func(const char *nm, int argc, int opt_argc, int varargs, int has_retval) { /* get the symbol length */ size_t len = strlen(nm); /* look up the function symbol */ CTcSymFunc *fn = (CTcSymFunc *)G_prs->get_global_symtab()->find_delete_weak(nm, len); if (fn == 0) { /* it's not defined - create an implied declaration for it */ fn = new CTcSymFunc(nm, len, FALSE, argc, opt_argc, varargs, has_retval, FALSE, FALSE, TRUE, TRUE); /* add it to the global symbol table */ G_prs->get_global_symtab()->add_entry(fn); } else if (fn->get_type() != TC_SYM_FUNC) { /* it's defined, but not as a function - this is an error */ G_tok->log_error(TCERR_REDEF_AS_FUNC, (int)len, nm); fn = 0; } /* return the function */ return fn; } /* * Get an internal built-in function by name */ static CTcSymBif *get_internal_bif(const char *nm, int err) { /* look up the symbol */ size_t len = strlen(nm); CTcSymBif *fn = (CTcSymBif *)G_prs->get_global_symtab()->find(nm, len); /* if it wasn't found, flag an error */ if (fn == 0) G_tok->log_error(err, (int)len, nm); /* return the function */ return fn; } /* * Call an internal built-in function by name. This is for simple calls * with fixed arguments and no named parameters, keeping the return value. */ static void call_internal_bif(const char *nm, int err, int argc) { /* look up the function */ CTcSymBif *fn = get_internal_bif(nm, err); /* if we got it, call it */ if (fn != 0) fn->gen_code_call(FALSE, argc, FALSE, 0); } /* ------------------------------------------------------------------------ */ /* * Argument list */ void CTPNArglist::gen_code_arglist(int *varargs, CTcNamedArgs &named_args) { CTPNArg *arg; int i; int fixed_cnt; int pushed_varargs_counter; /* * scan the argument list for varargs - if we have any, we must * treat all of them as varargs */ for (*varargs = FALSE, fixed_cnt = named_args.cnt = 0, named_args.args = this, arg = get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg()) { /* if this is a varargs argument, we have varargs */ if (arg->is_varargs()) { /* note it */ *varargs = TRUE; } else if (arg->is_named_param()) { /* count the named argument */ named_args.cnt += 1; } else { /* count another fixed positional argument */ ++fixed_cnt; } } /* * If we have named arguments, push them ahead of the position * arguments. */ if (named_args.cnt != 0) { /* * Run through the argument list again, evaluating only named * arguments on this pass. */ for (i = argc_, arg = get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg(), --i) { /* if it's a named argument, push it */ if (arg->is_named_param()) arg->gen_code(FALSE, FALSE); } } /* * Push each positional argument in the list. The arguments on the * stack go in right-to-left order. The parser builds the internal * list of argument expressions in reverse order, so we must merely * follow the list from head to tail. * * We need each argument value to be pushed (hence discard = false), * and we need the assignable value of each argument expression (hence * for_condition = false). */ for (pushed_varargs_counter = FALSE, i = argc_, arg = get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg(), --i) { int depth; /* skip named arguments on this pass */ if (arg->is_named_param()) continue; /* note the stack depth before generating the value */ depth = G_cg->get_sp_depth(); /* * check for varargs - if this is first varargs argument, push * the counter placeholder */ if (arg->is_varargs() && !pushed_varargs_counter) { /* * write code to push the fixed argument count - we can use * this as a starting point, since we always know we have * this many argument to start with; we'll dynamically add * in the variable count at run-time */ CTPNConst::s_gen_code_int(fixed_cnt); /* note that we've pushed the counter */ pushed_varargs_counter = TRUE; /* * we will take the extra value off when we evaluate the * varargs counter, so simply count it as removed now */ G_cg->note_pop(); } /* generate the argument's code */ arg->gen_code(FALSE, FALSE); /* * if we've pushed the variable argument counter value onto the * stack, and this a fixed argument, swap the top two stack * elements to get the argument counter back to the top of the * stack; if this is a varargs argument there's no need, since * it will have taken care of this */ if (pushed_varargs_counter && !arg->is_varargs()) G_cg->write_op(OPC_SWAP); /* ensure that it generated something */ if (G_cg->get_sp_depth() <= depth) G_tok->log_error(TCERR_ARG_EXPR_HAS_NO_VAL, i); } } /* * ------------------------------------------------------------------------ */ /* * argument list entry */ void CTPNArg::gen_code(int, int) { /* * Generate the argument expression. We need the value (hence * discard = false), and we need the assignable value (hence * for_condition = false). */ get_arg_expr()->gen_code(FALSE, FALSE); /* * if this is a list-to-varargs conversion, generate the conversion * instruction */ if (is_varargs_) { /* write the opcode */ G_cg->write_op(OPC_MAKELSTPAR); /* note the extra push and pop for the argument count */ G_cg->note_push(); G_cg->note_pop(); } } /* ------------------------------------------------------------------------ */ /* * function/method call */ /* * create */ CTPNCall::CTPNCall(CTcPrsNode *func, class CTPNArglist *arglist) : CTPNCallBase(func, arglist) { /* the T3 instruction set limits calls to 127 arguments */ if (arglist->get_argc() > 127) G_tok->log_error(TCERR_TOO_MANY_CALL_ARGS); } /* * generate code */ void CTPNCall::gen_code(int discard, int for_condition) { /* check for special functions */ if (get_func()->sym_text_matches("rand", 4) && gen_code_rand(discard, for_condition)) return; /* push the argument list */ int varargs; CTcNamedArgs named_args; get_arg_list()->gen_code_arglist(&varargs, named_args); /* generate an appropriate call instruction */ get_func()->gen_code_call(discard, get_arg_list()->get_argc(), varargs, &named_args); } /* * Generate code for a call to 'rand'. If we have multiple arguments, * rand() is special because it doesn't evaluate all of its arguments. * Instead, it picks an argument at random, and evaluates only that single * selected argument, yielding the result. */ int CTPNCall::gen_code_rand(int discard, int for_condition) { /* get our argument list, and the number of arguments */ CTPNArglist *args = get_arg_list(); int argc = args->get_argc(); /* if we have zero or one argument, use the normal call */ if (argc < 2) return FALSE; /* if we have variable arguments, use the normal call */ CTPNArg *arg; for (arg = args->get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg()) { if (arg->is_varargs()) return FALSE; } /* * We have a fixed number of arguments greater than one, so this is the * special form of the rand() call. First, generate an ordinary call * to rand() to select a number from 0 to argc-1. */ CTcNamedArgs named_args; named_args.cnt = 0; CTPNConst::s_gen_code_int(argc); get_func()->gen_code_call(FALSE, 1, FALSE, &named_args); /* generate the SWITCH */ G_cg->write_op(OPC_SWITCH); G_cs->write2(argc); G_cg->note_pop(); /* * Build the case table. The rand() call gave us an integer from 0 to * argc-1 inclusive, so our case labels are simply the argument index * values, starting at zero. Note that the argument list is stored * right to left, so the index values of the list elements run from * argc-1 down to 0. Since we're making a random selection anyway the * order shouldn't really matter; however, for the sake of regression * tests for pre-3.1 games, we want to use the same index ordering that * rand() did in the old version, where rand() itself chose one of its * pre-evaluated arguments. When running in regression mode with a * fixed random number stream, using the same index ordering will * ensure that we get the same results when running existing tests. */ ulong caseofs = G_cs->get_ofs(); int i; for (i = argc - 1 ; i >= 0 ; --i) { /* write the case label as the 0-based argument index */ char buf[VMB_DATAHOLDER]; vm_val_t val; val.set_int(i); vmb_put_dh(buf, &val); G_cs->write(buf, VMB_DATAHOLDER); /* write a placeholder for the jump offset */ G_cs->write2(0); } /* write a placeholder for the default case */ G_cs->write2(0); /* create a forward label for the 'break' to the end of the switch */ CTcCodeLabel *brklbl = G_cs->new_label_fwd(); /* generate the argument expressions */ for (arg = args->get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg(), caseofs += VMB_DATAHOLDER + 2) { /* set the jump to this case in the case table */ G_cs->write2_at(caseofs + VMB_DATAHOLDER, G_cs->get_ofs() - (caseofs + VMB_DATAHOLDER)); /* generate the argument expression */ arg->get_arg_expr()->gen_code(discard, for_condition); /* generate a jump to the end of the table */ G_cg->write_op(OPC_JMP); G_cs->write_ofs2(brklbl, 0); /* we generate the branches in parallel, so only one push counts */ if (!discard) G_cg->note_pop(); } /* write the default case - just push nil if we need a value */ G_cs->write2_at(caseofs, G_cs->get_ofs() - caseofs); if (!discard) { G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); } /* this is the end of the switch - set the 'break' label here */ def_label_pos(brklbl); /* tell the caller that we handled the code generation */ return TRUE; } /* * Generate code for operator 'new'. A 'new' with an argument list * looks like a function call: NEW(CALL(object-contents, ARGLIST(...))). */ void CTPNCall::gen_code_new(int discard, int argc, int varargs, CTcNamedArgs *named_args, int from_call, int is_transient) { /* * if this is a recursive call from another 'call' node, it's not * allowed - we'd be trying to use the result of a call as the base * class of the 'new', which is illegal */ if (from_call) { G_tok->log_error(TCERR_INVAL_NEW_EXPR); return; } /* use our own named_args structure if the caller didn't provide one */ CTcNamedArgs my_named_args; if (named_args == 0) named_args = &my_named_args; /* generate the argument list */ get_arg_list()->gen_code_arglist(&varargs, *named_args); /* generate the code for the 'new' call */ get_func()->gen_code_new(discard, get_arg_list()->get_argc(), varargs, named_args, TRUE, is_transient); } /* ------------------------------------------------------------------------ */ /* * member property evaluation */ void CTPNMember::gen_code(int discard, int) { /* ask the object expression to generate the code */ get_obj_expr()->gen_code_member(discard, get_prop_expr(), prop_is_expr_, 0, FALSE, 0); } /* context for two-phase compound assignment to a member expression */ struct member_ca_ctx { member_ca_ctx(int is_self, vm_obj_id_t obj, vm_prop_id_t prop) { this->is_self = is_self; this->obj = obj; this->prop = prop; } int is_self; vm_obj_id_t obj; vm_prop_id_t prop; }; /* * assign to member expression */ int CTPNMember::gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int ignore_errors, int, void **ctx) { int is_self; vm_obj_id_t obj; vm_prop_id_t prop; /* if this is phase 2 of a compound assignment, retrieve the context */ if (phase == 2) { /* cast the generic context pointer to our structure */ member_ca_ctx *cctx = (member_ca_ctx *)*ctx; /* retrieve the context information */ is_self = cctx->is_self; obj = cctx->obj; prop = cctx->prop; /* we're done with the context object - delete it */ delete cctx; } /* * if this is a simple assignment, start by generating the right-hand * side, unless the caller has already done so */ if (typ == TC_ASI_SIMPLE && rhs != 0) rhs->gen_code(FALSE, FALSE); /* * If this is a simple assignment, or it's phase 2 of a two-phase * assignment, we now have the RHS at top of stack, and we're about to * perform the actual assignment. If the caller wants to further use * the assigned value, push a copy, since we'll consume the RHS value * currently on the stack with our SETPROP or related instruction. */ if (!discard && (typ == TC_ASI_SIMPLE || phase == 2)) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* * If this is a simple assignment, or phase 1 of a two-phase compound * assignment, generate the object and property expressions. */ if (typ == TC_ASI_SIMPLE || phase == 1) { /* * Determine what we have on the left: we could have self, a * constant object value, or any other expression. */ obj = get_obj_expr()->gen_code_obj_predot(&is_self); /* * determine what kind of property expression we have - don't * generate any code for now, since we may need to generate some * more code ahead of the property generation */ prop = get_prop_expr()->gen_code_propid(TRUE, prop_is_expr_); /* determine what we need to do based on the operands */ if (prop == VM_INVALID_PROP) { /* * We're assigning through a property pointer -- we must * generate a PTRSETPROP instruction. * * Before we generate the property expression, we must generate * the object expression. If we got a constant object, we must * generate code to push that object value; otherwise, the code * to generate the object value is already generated. */ if (is_self) { /* self - generate code to push the "self" value */ G_cg->write_op(OPC_PUSHSELF); G_cg->note_push(); } else if (obj != VM_INVALID_OBJ) { /* constant object - generate code to push the value */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(obj); G_cg->note_push(); } /* generate the property value expression */ get_prop_expr()->gen_code_propid(FALSE, prop_is_expr_); /* * If this is phase 1 of a two-phase compound assignment, we'll * need the object and property values that we've just stacked * again in phase 2. Save separate copies via DUP2. */ if (typ != TC_ASI_SIMPLE) { G_cg->write_op(OPC_DUP2); G_cg->note_push(2); } } else { /* * We're assigning to a fixed property ID. If the object * expression was not 'self' or a fixed object ID, we have an * object value on the stack; otherwise we have nothing on the * stack. * * For a two-phase assignment, we'll need the object value * again during the second phase, so if we did indeed stack a * value, make a copy of it for our use in phase 2. */ if (typ != TC_ASI_SIMPLE && !is_self && obj == VM_INVALID_OBJ) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } } } /* * If this is phase 1 of a two-phase assignment, stop here: we've * stacked the necessary obj and prop information for the actual * assignment codegen in phase 2, and we've stacked the obj.prop value * for the combination operator to access. Save context information * and return, telling the caller to proceed with the unrolled * operation. */ if (typ != TC_ASI_SIMPLE && phase == 1) { /* save the object and property information in the phase context */ *ctx = new member_ca_ctx(is_self, obj, prop); /* evaluate the property */ if (prop == VM_INVALID_PROP) { /* property pointer - call with zero arguments */ G_cg->write_op(OPC_PTRCALLPROP); G_cs->write(0); /* this pops the object and property */ G_cg->note_pop(2); } else { /* constant property */ if (is_self) { /* get property of self */ G_cg->write_op(OPC_GETPROPSELF); G_cs->write_prop_id(prop); } else if (obj != VM_INVALID_OBJ) { /* constant object */ G_cg->write_op(OPC_OBJGETPROP); G_cs->write_obj_id(obj); G_cs->write_prop_id(prop); } else { /* it's an object expression */ G_cg->write_op(OPC_GETPROP); G_cs->write_prop_id(prop); /* this pops the object value */ G_cg->note_pop(); } } /* all of these leave the value in R0 - retrieve it */ G_cg->write_op(OPC_GETR0); G_cg->note_push(); /* tell the caller to proceed with the unrolled assignment */ return FALSE; } /* * If we're doing a simple assignment, or this is phase 2 of a compound * assignment, it's time to do the actual assignment. */ if (typ == TC_ASI_SIMPLE || phase == 2) { /* determine how to make the assignment based on the operands */ if (prop == VM_INVALID_PROP) { /* * If this is phase 2 of a compound assignment, get the stack * back in order. We currently have (from bottom to top) obj * prop RHS RHS (if !discard), or obj prop RHS (if discard). * Swap the top one or two elements for the next two. */ if (typ != TC_ASI_SIMPLE && phase == 2) { if (discard) { /* obj prop RHS -> RHS prop obj */ G_cg->write_op(OPC_SWAPN); G_cs->write(0); G_cs->write(2); /* RHS prop obj -> RHS obj prop */ G_cg->write_op(OPC_SWAP); } else { /* obj prop RHS RHS -> RHS RHS obj prop */ G_cg->write_op(OPC_SWAP2); } } /* generate the PTRSETPROP instruction */ G_cg->write_op(OPC_PTRSETPROP); /* ptrsetprop removes three elements */ G_cg->note_pop(3); } else { /* constant property value */ /* * If this is phase 2, and we have an object on the stack, we * need to rearrange the stack into the proper order: * * !discard: stack = (obj RHS RHS), and we need (RHS RHS obj). * * discard: stack = (obj RHS), and we need (obj RHS). Just do * a SWAP. * * If there's no object on the stack, there's no need to do any * rearrangement. */ if (phase == 2 && !is_self && obj == VM_INVALID_OBJ) { if (discard) { /* obj RHS -> RHS obj */ G_cg->write_op(OPC_SWAP); } else { /* obj RHS RHS -> RHS RHS obj */ G_cg->write_op(OPC_SWAPN); G_cs->write(0); G_cs->write(2); } } /* * We have several instructions to choose from. If we're * assigning to a property of "self", use SETPROPSELF. If * we're assigning to a constant object, use OBJSETPROP. * Otherwise, use the plain SETPROP. */ if (is_self) { /* write the SETPROPSELF */ G_cg->write_op(OPC_SETPROPSELF); G_cs->write_prop_id(prop); /* setpropself removes the RHS value */ G_cg->note_pop(); } else if (obj != VM_INVALID_OBJ) { /* write the OBJSETPROP */ G_cg->write_op(OPC_OBJSETPROP); G_cs->write_obj_id(obj); G_cs->write_prop_id(prop); /* objsetprop removes the RHS value */ G_cg->note_pop(); } else { /* * write the normal SETPROP; we already generated the code * to push the object value, so it's where it should be */ G_cg->write_op(OPC_SETPROP); G_cs->write_prop_id(prop); /* setprop removes the value and the object */ G_cg->note_pop(2); } } } /* we're done with the operation */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * member with argument list */ void CTPNMemArg::gen_code(int discard, int) { /* push the argument list */ int varargs; CTcNamedArgs named_args; get_arg_list()->gen_code_arglist(&varargs, named_args); /* ask the object expression to generate the code */ get_obj_expr()->gen_code_member(discard, get_prop_expr(), prop_is_expr_, get_arg_list()->get_argc(), varargs, &named_args); } /* ------------------------------------------------------------------------ */ /* * construct a list */ void CTPNList::gen_code(int discard, int for_condition) { /* * Before we construct the list dynamically, check to see if the * list is constant. If it is, we need only built the list in the * constant pool, and push its offset. */ if (is_const()) { /* push the value only if we're not discarding it */ if (!discard) { /* write the instruction */ G_cg->write_op(OPC_PUSHLST); /* add the list to the constant pool */ G_cg->add_const_list(this, G_cs, G_cs->get_ofs()); /* * write a placeholder address, which will be corrected by * the fixup that add_const_list() created */ G_cs->write4(0); /* note the push */ G_cg->note_push(); } } else { /* * It's not a constant list, so we must generate code to construct * a list dynamically. Push each element of the list. We need * each value (hence discard = false), and we require the * assignable value of each expression (hence for_condition = * false). Push the argument list in reverse order, since the * run-time metaclass requires this ordering. */ for (CTPNListEle *ele = get_tail() ; ele != 0 ; ele = ele->get_prev()) ele->gen_code(FALSE, FALSE); /* generate a NEW instruction for an object of metaclass LIST */ if (get_count() <= 255) { /* the count will fit in one byte - use the short form */ G_cg->write_op(OPC_NEW1); G_cs->write((char)get_count()); G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_LIST)); } else { /* count doesn't fit in one byte - use the long form */ G_cg->write_op(OPC_NEW2); G_cs->write2(get_count()); G_cs->write2(G_cg->get_predef_meta_idx(TCT3_METAID_LIST)); } /* new1/new2 remove arguments */ G_cg->note_pop(get_count()); /* if we're not discarding the value, push it */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * If this is a lookup table list, and we're not discarding the value, * create the actual LookupTable object from the list source. If we're * discarding the value there's no need to create the object, since * doing so will have no meaningful side effects. */ if (!discard && is_lookup_table_) { /* * To construct the lookup table, we simply pass the list of [key, * value, key, value...] elements to the LookupTable constructor. * The list is already on the stack, so simply call the constructor * with one argument. */ G_cg->write_op(OPC_NEW1); G_cs->write(1); G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_LOOKUP_TABLE)); /* NEW1 removes arguments */ G_cg->note_pop(1); /* push the result */ G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * list element */ void CTPNListEle::gen_code(int discard, int for_condition) { /* generate the subexpression */ expr_->gen_code(discard, for_condition); } /* ------------------------------------------------------------------------ */ /* * Basic T3-specific symbol class */ /* * generate code to take the address of a symbol - in general, we cannot * take the address of a symbol, so we'll just log an error */ void CTcSymbol::gen_code_addr() { G_tok->log_error(TCERR_NO_ADDR_SYM, (int)get_sym_len(), get_sym()); } /* * generate code to assign to the symbol - in general, we cannot assign * to a symbol, so we'll just log an error */ int CTcSymbol::gen_code_asi(int, int phase, tc_asitype_t, const char *, class CTcPrsNode *, int ignore_error, int, void **) { /* * If we're ignoring errors, simply return false to indicate that * nothing happened. Ignore errors on phase 2, since we'll already * have generated an error on phase 1. */ if (ignore_error || phase > 1) return FALSE; /* log the error */ G_tok->log_error(TCERR_CANNOT_ASSIGN_SYM, (int)get_sym_len(), get_sym()); /* * even though we didn't generate anything, this has been fully * handled - the caller shouldn't attempt to generate any additional * code for this */ return TRUE; } /* * Generate code for calling the symbol. By default, we can't call a * symbol. */ void CTcSymbol::gen_code_call(int, int, int, CTcNamedArgs *) { /* log an error */ G_tok->log_error(TCERR_CANNOT_CALL_SYM, (int)get_sym_len(), get_sym()); } /* * Generate code for operator 'new' */ void CTcSymbol::gen_code_new(int, int, int, CTcNamedArgs *, int) { G_tok->log_error(TCERR_INVAL_NEW_EXPR); } /* * evaluate a property ID */ vm_prop_id_t CTcSymbol::gen_code_propid(int check_only, int is_expr) { /* by default, a symbol cannot be used as a property ID */ if (!check_only) G_tok->log_error(TCERR_SYM_NOT_PROP, (int)get_sym_len(), get_sym()); /* we can't return a valid property ID */ return VM_INVALID_PROP; } /* * evaluate a member expression */ void CTcSymbol::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* by default, a symbol cannot be used as an object expression */ G_tok->log_error(TCERR_SYM_NOT_OBJ, (int)get_sym_len(), get_sym()); } /* * generate code for an object expression before a '.' */ vm_obj_id_t CTcSymbol::gen_code_obj_predot(int *is_self) { /* by default, a symbol cannot be used as an object expression */ G_tok->log_error(TCERR_SYM_NOT_OBJ, (int)get_sym_len(), get_sym()); /* indicate that we don't have a constant object */ *is_self = FALSE; return VM_INVALID_OBJ; } /* ------------------------------------------------------------------------ */ /* * T3-specific function symbol class */ /* * evaluate the symbol */ void CTcSymFunc::gen_code(int discard) { /* * function address are always unknown during code generation; * generate a placeholder instruction and add a fixup record for it */ G_cg->write_op(OPC_PUSHFNPTR); /* add a fixup for the current code location */ add_abs_fixup(G_cs); /* write a placeholder offset - arbitrarily use zero */ G_cs->write4(0); /* note the push */ G_cg->note_push(); } /* * take the address of the function */ void CTcSymFunc::gen_code_addr() { /* '&function' is the same as 'function' with no arguments */ gen_code(FALSE); } /* * call the symbol */ void CTcSymFunc::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* we can't call a function that doesn't have a prototype */ if (!has_proto_) { G_tok->log_error(TCERR_FUNC_CALL_NO_PROTO, (int)get_sym_len(), get_sym()); return; } /* * If this is a multi-method base function, a call to the function is * actually a call to _multiMethodCall('name', args). */ if (is_multimethod_base_) { /* make a list out of the arguments */ if (varargs) { G_cg->write_op(OPC_VARARGC); G_cs->write((char)argc); } else if (argc <= 255) { G_cg->write_op(OPC_NEW1); G_cs->write((char)argc); } else { G_cg->write_op(OPC_NEW2); G_cs->write2(argc); } G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_LIST)); G_cg->note_pop(argc); G_cg->write_op(OPC_GETR0); G_cg->note_push(); /* add the base function pointer argument */ CTcConstVal funcval; funcval.set_funcptr(this); CTPNConst func(&funcval); func.gen_code(FALSE, FALSE); /* look up _multiMethodCall */ CTcSymFunc *mmc = get_internal_func( "_multiMethodCall", 1, 0, TRUE, TRUE); /* * Generate the call. Note that there are always two arguments at * this point: the base function pointer, and the argument list. * The argument list is just one argument because we've already * constructed a list out of it. */ if (mmc != 0) mmc->gen_code_call(discard, 2, FALSE, named_args); } else { /* write the varargs modifier if appropriate */ if (varargs) G_cg->write_op(OPC_VARARGC); /* generate the call instruction and argument count */ G_cg->write_op(OPC_CALL); G_cs->write((char)argc); /* check the mode */ if (G_cg->is_eval_for_dyn()) { /* * dynamic (run-time) compilation - we know the absolute * address already, since all symbols are pre-resolved in the * debugger */ G_cs->write4(get_code_pool_addr()); } else { /* * Normal compilation - we won't know the function's address * until after generation is completed, so add a fixup for the * current location, then write a placeholder for the offset * field. */ add_abs_fixup(G_cs); G_cs->write4(0); } /* call removes arguments */ G_cg->note_pop(argc); /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* make sure the argument count is correct */ if (!argc_ok(argc)) { char buf[128]; G_tok->log_error(TCERR_WRONG_ARGC_FOR_FUNC, (int)get_sym_len(), get_sym(), get_argc_desc(buf), argc); } /* if we're not discarding, push the return value from R0 */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } } /* * Get my code pool address. Valid only after linking. */ ulong CTcSymFunc::get_code_pool_addr() const { /* check for an absolute address */ if (abs_addr_valid_) { /* * we have an absolute address - this means the symbol was * loaded from a fully-linked image file (specifically, from the * debug records) */ return abs_addr_; } else { /* * we don't have an absolute address, so our address must have * been determined through a linking step - get the final * address from the anchor */ return anchor_->get_addr(); } } /* * add a runtime symbol table entry */ void CTcSymFunc::add_runtime_symbol(CVmRuntimeSymbols *symtab) { vm_val_t val; /* add an entry for our absolute address */ val.set_fnptr(get_code_pool_addr()); symtab->add_sym(get_sym(), get_sym_len(), &val); } /* ------------------------------------------------------------------------ */ /* * T3-specific object symbol class */ /* * evaluate the symbol */ void CTcSymObj::gen_code(int discard) { /* write code to push the object ID */ if (!discard) { /* push the object */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(obj_id_); /* note the push */ G_cg->note_push(); } } /* * take the address of the object */ void CTcSymObj::gen_code_addr() { /* act as though we were pushing the object ID directly */ gen_code(FALSE); } /* * Generate a 'new' expression */ void CTcSymObj::gen_code_new(int discard, int argc, int varargs, CTcNamedArgs *named_args, int is_transient) { /* use our static generator */ s_gen_code_new(discard, obj_id_, metaclass_, argc, varargs, named_args, is_transient); } /* * Generate a 'new' expression. (This is a static method so that this * code can be used by all of the possible expression types to which * 'new' can be applied.) * * This type of generation applies only to objects of metaclass TADS * Object. */ void CTcSymObj::s_gen_code_new(int discard, vm_obj_id_t obj_id, tc_metaclass_t meta, int argc, int varargs, CTcNamedArgs *named_args, int is_transient) { /* * push the base class object - this is always the first argument * (hence last pushed) to the metaclass constructor */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(obj_id); /* note the push */ G_cg->note_push(); /* * note that we can only allow 126 arguments to a constructor, * because we must add the implicit superclass argument */ if (argc > 126) G_tok->log_error(TCERR_TOO_MANY_CTOR_ARGS); /* * if we have varargs, swap the top stack elements to get the * argument count back on top, and then generate the varargs * modifier opcode */ if (varargs) { /* swap the top stack elements to get argc back to the top */ G_cg->write_op(OPC_SWAP); /* * increment the argument count to account for the superclass * object argument */ G_cg->write_op(OPC_INC); /* write the varargs modifier opcode */ G_cg->write_op(OPC_VARARGC); } /* * write the NEW instruction - since we always add TADS Object to * our metaclass table before we start compiling any code, we know * it always has a small metaclass number and will always fit in the * short form of the instruction * * Note that the actual argument count we generate is one higher * than the source code argument list, because we add the implicit * first argument to the metaclass constructor */ G_cg->write_op(is_transient ? OPC_TRNEW1 : OPC_NEW1); G_cs->write((char)(argc + 1)); /* write out the dependency table index for the metaclass */ switch (meta) { case TC_META_TADSOBJ: G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_TADSOBJ)); break; default: /* we can't use 'new' on symbols of other metaclasses */ G_tok->log_error(TCERR_BAD_META_FOR_NEW); G_cs->write(0); break; } /* new1 removes the arguments */ G_cg->note_pop(argc + 1); /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* * if they're not discarding the value, push the new object * reference, which will be in R0 when the constructor returns */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * Generate code for a member expression */ void CTcSymObj::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { s_gen_code_member(discard, prop_expr, prop_is_expr, argc, obj_id_, varargs, named_args); } /* * Static method to generate code for a member expression. This is * static so that constant object nodes can share it. */ void CTcSymObj::s_gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, vm_obj_id_t obj_id, int varargs, CTcNamedArgs *named_args) { vm_prop_id_t prop; /* * generate the property expression - don't generate the code right * now even if code generation is necessary, because this isn't the * right place for it; for now, simply check to determine if we're * going to need to generate any code for the property expression */ prop = prop_expr->gen_code_propid(TRUE, prop_is_expr); /* don't allow method calls with arguments in speculative mode */ if (argc != 0 && G_cg->is_speculative()) err_throw(VMERR_BAD_SPEC_EVAL); /* check for a constant property value */ if (prop != VM_INVALID_PROP) { /* generate an OBJGETPROP or OBJCALLPROP as appropriate */ if (G_cg->is_speculative()) { /* speculative evaluation - use GETPROPDATA */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(obj_id); G_cg->write_op(OPC_GETPROPDATA); G_cs->write_prop_id(prop); /* we pushed the object, then popped it */ G_cg->note_push(); G_cg->note_pop(); } else if (argc == 0) { /* no arguments - use OBJGETPROP */ G_cg->write_op(OPC_OBJGETPROP); G_cs->write_obj_id(obj_id); G_cs->write_prop_id(prop); } else { /* generate a varargs modifier if needed */ if (varargs) G_cg->write_op(OPC_VARARGC); /* arguments - use OBJCALLPROP */ G_cg->write_op(OPC_OBJCALLPROP); G_cs->write((char)argc); G_cs->write_obj_id(obj_id); G_cs->write_prop_id(prop); /* objcallprop removes arguments */ G_cg->note_pop(argc); } } else { /* * non-constant property value - we must first push the object * value, then push the property value, then write a PTRCALLPROP * instruction */ /* generate the object push */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(obj_id); /* note the pushes */ G_cg->note_push(); /* keep the argument counter on top if necessary */ if (varargs) G_cg->write_op(OPC_SWAP); /* generate the property push */ prop_expr->gen_code_propid(FALSE, prop_is_expr); /* generate the PTRCALLPROP or PTRGETPROPDATA */ if (G_cg->is_speculative()) { /* speculative - use the data-only property evaluation */ G_cg->write_op(OPC_PTRGETPROPDATA); } else { /* * if we have a varargs list, modify the call instruction * that follows to make it a varargs call */ if (varargs) { /* swap to get the arg counter back on top */ G_cg->write_op(OPC_SWAP); /* write the varargs modifier */ G_cg->write_op(OPC_VARARGC); } /* normal - call the property */ G_cg->write_op(OPC_PTRCALLPROP); G_cs->write((int)argc); } /* ptrcallprop removes the arguments, the object, and the property */ G_cg->note_pop(argc + 2); } /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if they want the result, push it onto the stack */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * generate code for an object before a '.' */ vm_obj_id_t CTcSymObj::gen_code_obj_predot(int *is_self) { /* return our constant object reference */ *is_self = FALSE; return obj_id_; } /* * add a runtime symbol table entry */ void CTcSymObj::add_runtime_symbol(CVmRuntimeSymbols *symtab) { vm_val_t val; /* add our entry */ val.set_obj(obj_id_); symtab->add_sym(get_sym(), get_sym_len(), &val); } /* ------------------------------------------------------------------------ */ /* * Invokable object */ void CTcSymFuncObj::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* write the varargs modifier if appropriate */ if (varargs) G_cg->write_op(OPC_VARARGC); /* push my value as the function pointer */ gen_code(discard); /* generate the pointer-call instruction and argument count */ G_cg->write_op(OPC_PTRCALL); G_cs->write((char)argc); /* ptrcall removes arguments and the function pointer */ G_cg->note_pop(argc + 1); /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if we're not discarding, push the return value from R0 */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * T3-specific property symbol class */ /* * evaluate the symbol */ void CTcSymProp::gen_code(int discard) { /* * Evaluating a property is equivalent to calling the property on * the "self" object with no arguments. If there's no "self" * object, an unqualified property evaluation is not possible, so * log an error if this is the case. */ if (!G_cs->is_self_available()) { G_tok->log_error(TCERR_PROP_NEEDS_OBJ, (int)get_sym_len(), get_sym()); return; } if (G_cg->is_speculative()) { /* push 'self', then evaluate the property in data-only mode */ G_cg->write_op(OPC_PUSHSELF); G_cg->write_op(OPC_GETPROPDATA); G_cs->write_prop_id(prop_); /* we pushed the 'self' value then popped it again */ G_cg->note_push(); G_cg->note_pop(); } else { /* generate the call to 'self' */ G_cg->write_op(OPC_GETPROPSELF); G_cs->write_prop_id(prop_); } /* if they're not discarding the value, push the result */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * evaluate a member expression */ void CTcSymProp::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* generate code to evaluate the property */ gen_code(FALSE); /* if we have an argument counter, put it back on top */ if (varargs) G_cg->write_op(OPC_SWAP); /* use the standard member generation */ CTcPrsNode::s_gen_member_rhs(discard, prop_expr, prop_is_expr, argc, varargs, named_args); } /* * take the address of the property */ void CTcSymProp::gen_code_addr() { /* write code to push the property ID */ G_cg->write_op(OPC_PUSHPROPID); G_cs->write_prop_id(prop_); /* note the push */ G_cg->note_push(); } /* * assign to a property, implicitly of the "self" object */ int CTcSymProp::gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *, class CTcPrsNode *rhs, int /*ignore_errors*/, int /*explicit*/, void ** /*ctx*/) { /* if there's no "self" object, we can't make this assignment */ if (!G_cs->is_self_available()) { /* log an error if it's phase 1 */ if (phase == 1) G_tok->log_error(TCERR_SETPROP_NEEDS_OBJ, (int)get_sym_len(), get_sym()); /* * indicate that we're finished, since there's nothing more we * can do here */ return TRUE; } /* check what we're doing */ if (typ == TC_ASI_SIMPLE) { /* * Simple assignment. Generate the right-hand side's expression * for assignment, unless the caller has already done so. */ if (rhs != 0) rhs->gen_code(FALSE, FALSE); /* * if we're not discarding the value, make a copy - we'll consume a * copy in the SETPROP instruction, so we need one more copy to * return to the enclosing expression */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* * write the SETPROP instruction - use the special form to assign * to "self" */ G_cg->write_op(OPC_SETPROPSELF); G_cs->write_prop_id(prop_); /* setpropself removes the value */ G_cg->note_pop(); /* handled */ return TRUE; } else if (phase == 1) { /* * Phase 1 of a compound assignment. Generate the property value * so that the compound operator can combine it with the RHS. */ gen_code(FALSE); /* proceed with the unrolled assignment */ return FALSE; } else if (phase == 2) { /* * Phase 2 of a compound assignment. The combined value of * (self.prop RHS) is on the stack, so complete the * assignment. */ /* if the caller will want to use the RHS value, make a copy */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* write the SETPROP */ G_cg->write_op(OPC_SETPROPSELF); G_cs->write_prop_id(prop_); /* setpropself removes the value */ G_cg->note_pop(); /* done */ return TRUE; } else { /* other phases aren't handled */ return FALSE; } } /* * call the symbol */ void CTcSymProp::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* * if there's no "self", we can't invoke a property without an * explicit object reference */ if (!G_cs->is_self_available()) { G_tok->log_error(TCERR_PROP_NEEDS_OBJ, (int)get_sym_len(), get_sym()); return; } /* don't allow calling with arguments in speculative mode */ if (G_cg->is_speculative() && (argc != 0 || (named_args != 0 && named_args->cnt != 0))) err_throw(VMERR_BAD_SPEC_EVAL); /* generate code to invoke the property of "self" */ if (G_cg->is_speculative()) { /* push 'self', then get the property in data-only mode */ G_cg->write_op(OPC_PUSHSELF); G_cg->write_op(OPC_GETPROPDATA); G_cs->write_prop_id(get_prop()); /* we pushed 'self' then popped it again */ G_cg->note_push(); G_cg->note_pop(); } else if (argc == 0) { /* use the instruction with no arguments */ G_cg->write_op(OPC_GETPROPSELF); G_cs->write_prop_id(get_prop()); } else { /* write the varargs modifier if appropriate */ if (varargs) G_cg->write_op(OPC_VARARGC); /* use the instruction with arguments */ G_cg->write_op(OPC_CALLPROPSELF); G_cs->write((char)argc); G_cs->write_prop_id(get_prop()); /* callpropself removes arguments */ G_cg->note_pop(argc); } /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if we're not discarding, push the return value from R0 */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * generate a property ID expression */ vm_prop_id_t CTcSymProp::gen_code_propid(int check_only, int is_expr) { /* * If I'm to be treated as an expression (which indicates that the * property symbol is explicitly enclosed in parentheses in the * original source code expression), then I must evaluate this * property of self. Otherwise, I yield literally the property ID. */ if (is_expr) { /* generate code unless we're only checking */ if (!check_only) { /* evaluate this property of self */ G_cg->write_op(OPC_GETPROPSELF); G_cs->write_prop_id(get_prop()); /* leave the result on the stack */ G_cg->write_op(OPC_GETR0); G_cg->note_push(); } /* tell the caller to use the stack value */ return VM_INVALID_PROP; } else { /* simple '.prop' - return my property ID as a constant value */ return get_prop(); } } /* * add a runtime symbol table entry */ void CTcSymProp::add_runtime_symbol(CVmRuntimeSymbols *symtab) { vm_val_t val; /* add our entry */ val.set_propid(get_prop()); symtab->add_sym(get_sym(), get_sym_len(), &val); } /* ------------------------------------------------------------------------ */ /* * Enumerator symbol */ /* * evaluate the symbol */ void CTcSymEnum::gen_code(int discard) { if (!discard) { /* generate code to push the enum value */ G_cg->write_op(OPC_PUSHENUM); G_cs->write_enum_id(get_enum_id()); /* note the push */ G_cg->note_push(); } } /* * add a runtime symbol table entry */ void CTcSymEnum::add_runtime_symbol(CVmRuntimeSymbols *symtab) { vm_val_t val; /* add our entry */ val.set_enum(get_enum_id()); symtab->add_sym(get_sym(), get_sym_len(), &val); } /* ------------------------------------------------------------------------ */ /* * T3-specific local variable/parameter symbol class */ /* * generate code to evaluate the symbol */ void CTcSymLocal::gen_code(int discard) { /* generate code to push the local, if we're not discarding it */ if (!discard) { /* * generate as a context local if required, otherwise as an * ordinary local variable */ if (is_ctx_local_) { /* generate the context array lookup */ if (ctx_var_num_ <= 255 && get_ctx_arr_idx() <= 255) { /* we can do this whole operation with one instruction */ G_cg->write_op(OPC_IDXLCL1INT8); G_cs->write((uchar)ctx_var_num_); G_cs->write((uchar)get_ctx_arr_idx()); /* this pushes one value */ G_cg->note_push(); } else { /* get our context array */ s_gen_code_getlcl(ctx_var_num_, FALSE); /* get our value from the context array */ CTPNConst::s_gen_code_int(get_ctx_arr_idx()); G_cg->write_op(OPC_INDEX); /* the INDEX operation removes two values and pushes one */ G_cg->note_pop(); } } else { /* generate as an ordinary local */ s_gen_code_getlcl(get_var_num(), is_param()); } } /* * Mark the value as referenced, whether or not we're generating the * code - the value has been logically referenced in the program * even if the result of evaluating it isn't needed. */ set_val_used(TRUE); } /* * generate code to push a local onto the stack */ void CTcSymLocal::s_gen_code_getlcl(int var_num, int is_param) { /* use the shortest form of the instruction that we can */ if (is_param && var_num < 4) { /* arguments 0-3 have dedicated opcodes */ G_cg->write_op(OPC_GETARGN0 + var_num); } else if (!is_param && var_num < 6) { /* locals 0-5 have dedicated opcodes */ G_cg->write_op(OPC_GETLCLN0 + var_num); } else if (var_num <= 255) { /* 8-bit local number - use the one-byte form */ G_cg->write_op(is_param ? OPC_GETARG1 : OPC_GETLCL1); G_cs->write((char)var_num); } else { /* local number won't fit in 8 bits - use the two-byte form */ G_cg->write_op(is_param ? OPC_GETARG2 : OPC_GETLCL2); G_cs->write2(var_num); } /* note the push */ G_cg->note_push(); } /* * assign a value */ int CTcSymLocal::gen_code_asi( int discard, int phase, tc_asitype_t typ, const char *, class CTcPrsNode *rhs, int ignore_errors, int xplicit, void **) { /* * if this is an explicit assignment, mark the variable as having had a * value assigned to it */ if (xplicit) set_val_assigned(TRUE); /* * if the assignment is anything but simple, this references the * value as well */ if (typ != TC_ASI_SIMPLE) set_val_used(TRUE); /* * If this is a context variable, use standard assignment (i.e., * generate the result first, then generate a simple assignment to the * variable). Otherwise, we might be able to generate a fancy * combined calculate-and-assign sequence, depending on the type of * assignment calculation we're performing. */ if (is_ctx_local_ && typ != TC_ASI_SIMPLE && phase == 1) { /* * It's a context local, and it's not a simple assignment, so we * can't perform any special calculate-and-assign sequence. For * phase 1, generate the value and tell the caller to calculate the * full result first and then try again using simple assignment. */ gen_code(FALSE); return FALSE; } /* * If this is phase 2 of a compound assignment, treat it the same as a * simple assignment. The RHS is on the stack, and we simply need to * complete the assignment to the local. */ if (phase == 2) typ = TC_ASI_SIMPLE; /* * check the type of assignment - we can optimize the code * generation to use more compact instruction sequences for certain * types of assignments */ int adding; switch(typ) { case TC_ASI_SIMPLE: /* * Simple assignment to local/parameter. Check for some special * cases: when assigning a constant value of 0, 1, or nil to a * local, we can generate a short instruction */ if (!is_param() && !is_ctx_local_ && rhs != 0 && rhs->is_const()) { CTcConstVal *cval; /* get the constant value */ cval = rhs->get_const_val(); /* check for nil and 0 or 1 values */ if (cval->get_type() == TC_CVT_NIL) { /* it's nil - generate NILLCL1 or NILLCL2 */ if (get_var_num() <= 255) { G_cg->write_op(OPC_NILLCL1); G_cs->write((char)get_var_num()); } else { G_cg->write_op(OPC_NILLCL2); G_cs->write2(get_var_num()); } /* if not discarding, leave nil on the stack */ if (!discard) { G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); } /* handled */ return TRUE; } else if (cval->get_type() == TC_CVT_INT && (cval->get_val_int() == 0 || cval->get_val_int() == 1)) { int ival; /* get the integer value */ ival = cval->get_val_int(); /* 0 or 1 - generate ZEROLCLn or ONELCLn */ if (get_var_num() <= 255) { G_cg->write_op(ival == 0 ? OPC_ZEROLCL1 : OPC_ONELCL1); G_cs->write((char)get_var_num()); } else { G_cg->write_op(ival == 0 ? OPC_ZEROLCL2 : OPC_ONELCL2); G_cs->write2(get_var_num()); } /* if not discarding, leave the value on the stack */ if (!discard) { G_cg->write_op(ival == 0 ? OPC_PUSH_0 : OPC_PUSH_1); G_cg->note_push(); } /* handled */ return TRUE; } } /* * If we got here, we can't generate a specialized constant * assignment - so, first, generate the right-hand side's value * normally. (If no 'rhs' is specified, the value is already on * the stack.) */ if (rhs != 0) rhs->gen_code(FALSE, FALSE); /* leave an extra copy of the value on the stack if not discarding */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* now assign the value at top of stack to the variable */ gen_code_setlcl(); /* handled */ return TRUE; case TC_ASI_ADD: adding = TRUE; goto add_or_sub; case TC_ASI_SUB: adding = FALSE; add_or_sub: /* if this is a parameter, there's nothing special we can do */ if (is_param()) { if (phase == 1) gen_code(FALSE); return FALSE; } /* * Add/subtract to a local/parameter. If the right-hand side is a * constant integer value, we might be able to generate a special * instruction to add/subtract it. */ if (rhs != 0 && adding && rhs->is_const() && rhs->get_const_val()->get_type() == TC_CVT_INT) { long ival; /* get the integer value to assign */ ival = rhs->get_const_val()->get_val_int(); /* * if the right-hand side's integer value fits in one byte, * generate the short (8-bit) instruction; otherwise, * generate the long (32-bit) format */ if (ival == 1) { /* adding one - increment the local */ G_cg->write_op(OPC_INCLCL); G_cs->write2(get_var_num()); } else if (ival == -1) { /* subtracting one - decrement the local */ G_cg->write_op(OPC_DECLCL); G_cs->write2(get_var_num()); } else if (ival <= 127 && ival >= -128 && get_var_num() <= 255) { /* fits in 8 bits - use the 8-bit format */ G_cg->write_op(OPC_ADDILCL1); G_cs->write((char)get_var_num()); G_cs->write((char)ival); } else { /* * either the value or the variable number doesn't fit * in 8 bits - use the 32-bit format */ G_cg->write_op(OPC_ADDILCL4); G_cs->write2(get_var_num()); G_cs->write4(ival); } } else { /* * We don't have a special instruction for the right side, * so generate it normally and add/subtract the value. (If * there's no 'rhs' value specified, it means that the value * is already on the stack, so there's nothing extra for us * to generate.) */ if (rhs != 0) rhs->gen_code(FALSE, FALSE); /* write the ADDTOLCL instruction */ G_cg->write_op(adding ? OPC_ADDTOLCL : OPC_SUBFROMLCL); G_cs->write2(get_var_num()); /* addtolcl/subfromlcl remove the rvalue */ G_cg->note_pop(); } /* * if not discarding, push the result onto the stack; do this by * simply evaluating the local, which is the simplest and most * efficient way to obtain the result of the computation */ if (!discard) gen_code(FALSE); /* handled */ return TRUE; case TC_ASI_PREINC: /* if this is a parameter, there's nothing special we can do */ if (is_param()) { if (phase == 1) gen_code(FALSE); return FALSE; } /* generate code to increment the local */ G_cg->write_op(OPC_INCLCL); G_cs->write2(get_var_num()); /* if we're not discarding, push the local's new value */ if (!discard) gen_code(FALSE); /* handled */ return TRUE; case TC_ASI_POSTINC: /* if this is a parameter, there's nothing special we can do */ if (is_param()) { if (phase == 1) gen_code(FALSE); return FALSE; } /* * if we're not discarding, push the local's value prior to * incrementing it - this will be the result we'll leave on the * stack */ if (!discard) gen_code(FALSE); /* generate code to increment the local */ G_cg->write_op(OPC_INCLCL); G_cs->write2(get_var_num()); /* handled */ return TRUE; case TC_ASI_PREDEC: /* if this is a parameter, there's nothing special we can do */ if (is_param()) { if (phase == 1) gen_code(FALSE); return FALSE; } /* generate code to decrement the local */ G_cg->write_op(OPC_DECLCL); G_cs->write2(get_var_num()); /* if we're not discarding, push the local's new value */ if (!discard) gen_code(FALSE); /* handled */ return TRUE; case TC_ASI_POSTDEC: /* if this is a parameter, there's nothing special we can do */ if (is_param()) { if (phase == 1) gen_code(FALSE); return FALSE; } /* * if we're not discarding, push the local's value prior to * decrementing it - this will be the result we'll leave on the * stack */ if (!discard) gen_code(FALSE); /* generate code to decrement the local */ G_cg->write_op(OPC_DECLCL); G_cs->write2(get_var_num()); /* handled */ return TRUE; default: /* * For other special assignment types, simply generate the value of * the local, and tell the caller to proceed with the unrolled * assignment. */ gen_code(FALSE); return FALSE; } } /* * generate code to assigin the value at top of stack to the local * variable */ void CTcSymLocal::gen_code_setlcl() { /* check to see if we're a context local (as opposed to a stack local) */ if (is_ctx_local_) { /* generate the assignment using the appropriate sequence */ if (ctx_var_num_ <= 255 && get_ctx_arr_idx() <= 255) { /* we can fit this in a single instruction */ G_cg->write_op(OPC_SETINDLCL1I8); G_cs->write((uchar)ctx_var_num_); G_cs->write((uchar)get_ctx_arr_idx()); /* this pops the value being assigned */ G_cg->note_pop(); } else { /* get our context array */ s_gen_code_getlcl(ctx_var_num_, FALSE); /* set our value in the context array */ CTPNConst::s_gen_code_int(get_ctx_arr_idx()); G_cg->write_op(OPC_SETIND); G_cg->write_op(OPC_DISC); /* * the SETIND pops three values and pushes one (for a net two * pops), and the DISC pops one more value, so our total is * three pops */ G_cg->note_pop(3); } } else { /* we're just a plain stack variable */ gen_code_setlcl_stk(); } } /* * Generate code to store the value at the top of the stack into the given * local stack slot. Note that this routine will not work with a context * local - it only works if the variable is known to be a stack variable. */ void CTcSymLocal::s_gen_code_setlcl_stk(int var_num, int is_param) { /* use the shortest form that will fit our variable index */ if (var_num <= 255) { /* use the one-byte instruction */ G_cg->write_op(is_param ? OPC_SETARG1 : OPC_SETLCL1); G_cs->write((char)var_num); } else { /* big number - use the two-byte instruction */ G_cg->write_op(is_param ? OPC_SETARG2 : OPC_SETLCL2); G_cs->write2(var_num); } /* the setarg/setlcl ops remove the rvalue */ G_cg->note_pop(); } /* * call the symbol */ void CTcSymLocal::gen_code_call( int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* * to call a local, we'll simply evaluate the local normally, then * call through the resulting (presumed) property or function * pointer value */ gen_code(FALSE); /* * if we have a varargs list, modify the call instruction that * follows to make it a varargs call */ if (varargs) { /* swap the top of the stack to get the arg counter back on top */ G_cg->write_op(OPC_SWAP); /* write the varargs modifier */ G_cg->write_op(OPC_VARARGC); } /* don't allow this at all in speculative mode */ if (G_cg->is_speculative()) err_throw(VMERR_BAD_SPEC_EVAL); /* call the result as a function or method pointer */ G_cg->write_op(OPC_PTRCALL); G_cs->write((char)argc); /* ptrcall removes the arguments and the function pointer */ G_cg->note_pop(argc + 1); /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if we're not discarding the value, push the result */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * generate a property ID expression */ vm_prop_id_t CTcSymLocal::gen_code_propid(int check_only, int /*is_expr*/) { /* * treat the local as a property-valued expression; generate the * code for the local, then tell the caller that no constant value * is available, since the local's property ID value should be on * the stack */ if (!check_only) gen_code(FALSE); /* tell the caller to use the stack value */ return VM_INVALID_PROP; } /* * evaluate a member expression */ void CTcSymLocal::gen_code_member( int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* generate code to evaluate the local */ gen_code(FALSE); /* if we have an argument counter, put it back on top */ if (varargs) G_cg->write_op(OPC_SWAP); /* use the standard member generation */ CTcPrsNode::s_gen_member_rhs(discard, prop_expr, prop_is_expr, argc, varargs, named_args); } /* * write to a debug record */ int CTcSymLocal::write_to_debug_frame(int test_only) { /* if this is test-only mode, just note that we will write it */ if (test_only) return TRUE; /* * For version 2+ static compilation, write the symbol name * out-of-line, to the separate stream for local variable names. This * allows us to consolidate the names so that each name is stored only * once in the file. This saves a lot of space since there are a few * common local names that appear over and over. Version 1 always * wrote names in-line, and we also have to write the names in-line * when generating dynamic code, since we can't add to the constant * pool in this case. * * Also, store all 1- and 2-character names in-line. 1-character names * are smaller if stored in-line since they'll take only three bytes * (two bytes for the length, one byte for the string) vs four bytes * for a shared pool pointer. 2-char names are a wash on the frame * storage, but it's still more efficient to store them in-line because * we avoid also creating the constant pool entry for them. */ int inl = (len_ <= 2 || G_sizes.dbg_fmt_vsn < 2 || G_cg->is_eval_for_dyn()); /* * write my ID - if we're a context variable, we want to write the * context variable ID; otherwise write our stack location as normal */ if (is_ctx_local_) G_cs->write2(ctx_var_num_); else G_cs->write2(var_num_); /* compute my flags */ int flags = 0; if (is_param_) flags |= 0x0001; if (is_ctx_local_) flags |= 0x0002; if (!inl) flags |= 0x0004; /* write my flags */ G_cs->write2(flags); /* write my local context array index */ G_cs->write2(get_ctx_arr_idx()); /* add zeros to pad any future version information for the target VM */ for (int i = G_sizes.lcl_hdr - 6 ; i > 0 ; --i) G_cs->write(0); /* * write the name - either to the separate stream for local names, or * inline if necessary */ if (inl) { /* write the symbol name to the table in-line */ G_cs->write2(len_); G_cs->write(str_, len_); } else { /* add a fixup from the table to the local stream */ CTcStreamAnchor *anchor = G_lcl_stream->add_anchor(0, 0); CTcAbsFixup::add_abs_fixup(anchor->fixup_list_head_, G_cs, G_cs->get_ofs()); /* add the placeholder to the table (this is what we fix up) */ G_cs->write4(0); /* write the name to the local stream */ G_lcl_stream->write2(len_); G_lcl_stream->write(str_, len_); } /* we did write this symbol */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Dynamic code local. This is a symbol for a local variable in an active * stack frame during dynamic compilation. */ /* * generate code to evaluate the variable's value */ void CTcSymDynLocal::gen_code(int discard) { /* there are no side effects, so do nothing if discarding the value */ if (discard) return; /* * to retrieve a dynamic frame local, we simply need to evaluate * fref[index], where 'fref' is the StackFrameRef object and 'index' is * an integer giving the frame index of the variable */ /* push the frame object */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(fref_); G_cg->note_push(); /* push the variable's frame index */ CTPNConst::s_gen_code_int(varnum_); /* index the frame object */ G_cg->write_op(OPC_INDEX); /* INDEX pops two items and pushes one for a net of one pop */ G_cg->note_pop(1); /* * if this is a context local, we now have the context object, so we * need to further index it by the variable's context index */ if (ctxidx_ != 0) { /* index the context object by the context index */ CTPNConst::s_gen_code_int(ctxidx_); G_cg->write_op(OPC_INDEX); /* INDEX pops two items and pushes one */ G_cg->note_pop(1); } } /* * generate code to assign to the variable */ int CTcSymDynLocal::gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *, class CTcPrsNode *rhs, int, int, void **) { /* if this is a complex assignment, use the default two-phase process */ if (typ != TC_ASI_SIMPLE && phase == 1) { /* * generate our value, and tell the caller to calculate the * compound expression result and assign the value with a separate * call */ gen_code(FALSE); return FALSE; } /* if the rhs isn't already on the stack, generate it */ if (rhs != 0) rhs->gen_code(FALSE, FALSE); /* * if we're not disarding the rhs value, make an extra copy, as we'll * consume our copy in the performing the assignment */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* * to assign to a local variable, perform a set-index operation through * the frame object - that is, evaluate "fref[index] = value" */ /* push the frame object */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(fref_); G_cg->note_push(); /* push the variable's frame index */ CTPNConst::s_gen_code_int(varnum_); /* * if this is a context local, frame[index] yields the context object, * so we need to retrieve that before proceeding */ if (ctxidx_ != 0) { /* get frame[varnum] - that gives us the context object */ G_cg->write_op(OPC_INDEX); /* INDEX pops two items and pushes one for a net 1 pop */ G_cg->note_pop(); /* * now push the context index - the actual assignment will be * ctxobj[ctxidx] = rhs */ CTPNConst::s_gen_code_int(ctxidx_); } /* perform the assignment */ G_cg->write_op(OPC_SETIND); /* * Discard the new container value. This is the result of doing an * index-assign through the frame object, which is simply the original * frame object; even if it were changing, it's not something we'd need * to store anywhere since this is an internal indexing operation. */ G_cg->write_op(OPC_DISC); /* * the SETIND pops the rhs, index, and indexee, and pushes the * container, which we then discard, for a net pop of 3 */ G_cg->note_pop(3); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Built-in function symbol */ /* * Evaluate the symbol. Invoking a built-in function without an * argument list is simply a call to the built-in function with no * arguments. */ void CTcSymBif::gen_code(int discard) { /* generate a call */ gen_code_call(discard, 0, FALSE, 0); } /* * Generate code to call the built-in function */ void CTcSymBif::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* don't allow calling built-in functions in speculative mode */ if (G_cg->is_speculative()) err_throw(VMERR_BAD_SPEC_EVAL); /* check for minimum and maximum arguments */ if (argc < min_argc_) { G_tok->log_error(TCERR_TOO_FEW_FUNC_ARGS, (int)get_sym_len(), get_sym()); } else if (!varargs_ && argc > max_argc_) { G_tok->log_error(TCERR_TOO_MANY_FUNC_ARGS, (int)get_sym_len(), get_sym()); } /* write the varargs modifier if appropriate */ if (varargs) G_cg->write_op(OPC_VARARGC); /* generate the call */ if (get_func_set_id() < 4 && get_func_idx() < 256) { uchar short_ops[] = { OPC_BUILTIN_A, OPC_BUILTIN_B, OPC_BUILTIN_C, OPC_BUILTIN_D }; /* * it's one of the first 256 functions in one of the first four * function sets - we can generate a short instruction */ G_cg->write_op(short_ops[get_func_set_id()]); G_cs->write((char)argc); G_cs->write((char)get_func_idx()); } else { /* it's not in the default set - use the longer instruction */ if (get_func_idx() < 256) { /* low function index - write the short form */ G_cg->write_op(OPC_BUILTIN1); G_cs->write((char)argc); G_cs->write((char)get_func_idx()); } else { /* big function index - write the long form */ G_cg->write_op(OPC_BUILTIN2); G_cs->write((char)argc); G_cs->write2(get_func_idx()); } /* write the function set ID */ G_cs->write((char)get_func_set_id()); } /* the built-in functions always remove arguments */ G_cg->note_pop(argc); /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* * if they're not discarding the value, push it - the value is * sitting in R0 after the call returns */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * take the address of the built-in function */ void CTcSymBif::gen_code_addr() { /* generate PUSHBIFPTR */ G_cg->write_op(OPC_PUSHBIFPTR); G_cs->write2(func_idx_); G_cs->write2(func_set_id_); /* this pushes one element */ G_cg->note_push(); } /* * add a runtime symbol table entry */ void CTcSymBif::add_runtime_symbol(CVmRuntimeSymbols *symtab) { vm_val_t val; /* add an entry for our absolute address */ val.set_bifptr(func_set_id_, func_idx_); symtab->add_sym(get_sym(), get_sym_len(), &val); } /* ------------------------------------------------------------------------ */ /* * External function symbol */ /* * evaluate the symbol */ void CTcSymExtfn::gen_code(int /*discard*/) { //$$$ to be implemented assert(FALSE); } /* * generate a call to the symbol */ void CTcSymExtfn::gen_code_call(int /*discard*/, int /*argc*/, int /*varargs*/, CTcNamedArgs * /*named_args*/) { //$$$ to be implemented assert(FALSE); } /* ------------------------------------------------------------------------ */ /* * Code Label symbol */ /* * evaluate the symbol */ void CTcSymLabel::gen_code(int discard) { /* it's not legal to evaluate a code label; log an error */ G_tok->log_error(TCERR_CANNOT_EVAL_LABEL, (int)get_sym_len(), get_sym()); } /* ------------------------------------------------------------------------ */ /* * Metaclass symbol */ /* * generate code for evaluating the symbol */ void CTcSymMetaclass::gen_code(int discard) { /* * mark it as referenced - if the metaclass wasn't defined in this file * but was merely imported from a symbol file, we now need to write it * to the object file anyway, since its class object ID has been * referenced */ ref_ = TRUE; /* * the metaclass name refers to the IntrinsicClass instance * associated with the metaclass */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(class_obj_); /* note the push */ G_cg->note_push(); } /* * generate code for operator 'new' applied to the metaclass */ void CTcSymMetaclass::gen_code_new(int discard, int argc, int varargs, CTcNamedArgs *named_args, int is_transient) { /* if this is an external metaclass, we can't generate code */ if (ext_) { G_tok->log_error(TCERR_EXT_METACLASS, (int)get_sym_len(), get_sym()); return; } /* if we have varargs, write the modifier */ if (varargs) G_cg->write_op(OPC_VARARGC); if (meta_idx_ <= 255 && argc <= 255) { G_cg->write_op(is_transient ? OPC_TRNEW1 : OPC_NEW1); G_cs->write((char)argc); G_cs->write((char)meta_idx_); } else { G_cg->write_op(is_transient ? OPC_TRNEW1 : OPC_NEW2); G_cs->write2(argc); G_cs->write2(meta_idx_); } /* new1/new2 remove arguments */ G_cg->note_pop(argc); /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if we're not discarding the value, push it */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * generate a member expression */ void CTcSymMetaclass::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* if this is an external metaclass, we can't generate code */ if (ext_) { G_tok->log_error(TCERR_EXT_METACLASS, (int)get_sym_len(), get_sym()); return; } /* generate code to push our class object onto the stack */ gen_code(FALSE); /* if we have an argument counter, put it back on top */ if (varargs) G_cg->write_op(OPC_SWAP); /* use the standard member generation */ CTcPrsNode::s_gen_member_rhs(discard, prop_expr, prop_is_expr, argc, varargs, named_args); } /* * add a runtime symbol table entry */ void CTcSymMetaclass::add_runtime_symbol(CVmRuntimeSymbols *symtab) { /* don't do this for external metaclasses */ if (ext_) return; /* add our entry */ vm_val_t val; val.set_obj(get_class_obj()); symtab->add_sym(get_sym(), get_sym_len(), &val); } /* ------------------------------------------------------------------------ */ /* * Exception Table */ /* * create */ CTcT3ExcTable::CTcT3ExcTable() { /* allocate an initial table */ exc_alloced_ = 1024; table_ = (CTcT3ExcEntry *)t3malloc(exc_alloced_ * sizeof(table_[0])); /* no entries are in use yet */ exc_used_ = 0; /* method offset is not yet known */ method_ofs_ = 0; } /* * add an entry to our table */ void CTcT3ExcTable::add_catch(ulong protected_start_ofs, ulong protected_end_ofs, ulong exc_obj_id, ulong catch_block_ofs) { CTcT3ExcEntry *entry; /* if necessary, expand our table */ if (exc_used_ == exc_alloced_) { /* expand the table a bit */ exc_alloced_ += 1024; /* reallocate the table at the larger size */ table_ = (CTcT3ExcEntry *) t3realloc(table_, exc_alloced_ * sizeof(table_[0])); } /* * set up the new entry - store the offsets relative to the method * header start address */ entry = table_ + exc_used_; entry->start_ofs = protected_start_ofs - method_ofs_; entry->end_ofs = protected_end_ofs - method_ofs_; entry->exc_obj_id = exc_obj_id; entry->catch_ofs = catch_block_ofs - method_ofs_; /* consume the new entry */ ++exc_used_; } /* * write our exception table to the code stream */ void CTcT3ExcTable::write_to_code_stream() { CTcT3ExcEntry *entry; size_t i; /* write the number of entries as a UINT2 */ G_cs->write2(exc_used_); /* write the entries */ for (i = 0, entry = table_ ; i < exc_used_ ; ++i, ++entry) { /* write this entry */ G_cs->write2(entry->start_ofs); G_cs->write2(entry->end_ofs); G_cs->write_obj_id(entry->exc_obj_id); G_cs->write2(entry->catch_ofs); /* pad any excess beyond the v1 size with zero bytes */ for (int j = G_sizes.exc_entry - 10 ; j > 0 ; --j) G_cs->write(0); } } /* ------------------------------------------------------------------------ */ /* * Parameter default value enumeration context. */ struct defval_ctx { defval_ctx() { nv = 0; nvalo = 128; v = new CTcSymLocal *[nvalo]; } ~defval_ctx() { delete [] v; } void add(CTcSymLocal *l) { /* if there's not room, expand the array */ if (nv == nvalo) { CTcSymLocal **vnew = new CTcSymLocal*[nvalo + 128]; memcpy(vnew, v, nvalo * sizeof(*vnew)); delete [] v; v = vnew; nvalo += 128; } /* add it */ v[nv++] = l; } /* sort the list in ascending order of sequence number */ void sort() { qsort(v, nv, sizeof(*v), &compare); } static int compare(const void *a0, const void *b0) { const CTcSymLocal *a = *(const CTcSymLocal **)a0; const CTcSymLocal *b = *(const CTcSymLocal **)b0; return a->get_defval_seqno() - b->get_defval_seqno(); } /* generate code to initialize an optional positional parameter */ static void gen_opt_positional(CTcSymLocal *lcl) { /* * The code we want to generate is this, in pseudo code (where 'n' * is our position among the arguments): * *. if (n <= argcount) *. lcl = getArg(n) *. else *. lcl = default_expression; * * The 'else' applies only if there's a default expression with the * local; otherwise we simply leave it with the nil initial value. */ /* if n > argcount... */ CTPNConst::s_gen_code_int(lcl->get_param_index() + 1); G_cg->write_op(OPC_GETARGC); G_cg->note_push(); /* ...jump to the 'not found' branch... */ CTcCodeLabel *nf_lbl = G_cs->new_label_fwd(); G_cg->write_op(OPC_JGT); G_cs->write_ofs2(nf_lbl, 0); G_cg->note_pop(2); /* getArg(n) */ CTPNConst::s_gen_code_int(lcl->get_param_index() + 1); call_internal_bif("getArg", TCERR_OPT_PARAM_MISSING_FUNC, 1); /* if there's a default value, generate it */ if (lcl->get_defval_expr() != 0) { /* we're done with the 'then' branch - jump to the assignment */ CTcCodeLabel *asi_lbl = G_cs->new_label_fwd(); G_cg->write_op(OPC_JMP); G_cs->write_ofs2(asi_lbl, 0); /* define the 'not found' label - come here to set the default */ CTcPrsNode::def_label_pos(nf_lbl); /* generate the default expression */ lcl->get_defval_expr()->gen_code(FALSE, FALSE); /* we're ready to do the assignment */ CTcPrsNode::def_label_pos(asi_lbl); /* * since we're sharing the assignment (coming up next) with the * main branch, we need to account for the extra push on this * branch */ G_cg->note_pop(); } /* * Assign the value we decided upon to the local. Don't count this * as an explicit assignment of a value for warning purposes; this * is effectively the same kind of assignment as the kind that * binds an actual argument to this formal parameter, so it * shouldn't count as any more explicit an assignment than that * does. */ lcl->gen_code_asi(TRUE, 1, TC_ASI_SIMPLE, "=", 0, FALSE, FALSE, 0); /* * if there was no default value, the 'not found' branch jumps * here, past the assignment, since we have no value to assign */ if (lcl->get_defval_expr() == 0) CTcPrsNode::def_label_pos(nf_lbl); } /* array of local variables with default values */ CTcSymLocal **v; /* allocated size of 'v' array */ int nvalo; /* number of entries in 'v' array */ int nv; }; /* ------------------------------------------------------------------------ */ /* * Code body */ /* * generate code */ void CTPNCodeBody::gen_code(int, int) { /* if I've been replaced, don't bother generating any code */ if (replaced_) return; /* * Open the method header. * * Generate to the static stream if this is a static initializer * method, otherwise to the main stream. * * Anchor the fixups in the associated symbol table entry, if any. We * maintain our own fixup list if we don't have a symbol, otherwise we * use the one from our symbol table entry - in either case, we have to * keep track of it ourselves, because a code body might be reachable * through multiple references (a function, for example, has a global * symbol table entry - fixups referencing us might already have been * created by the time we generate our code). */ tct3_method_gen_ctx gen_ctx; G_cg->open_method( is_static_ ? G_cs_static : G_cs_main, fixup_owner_sym_, fixup_list_anchor_, this, gototab_, argc_, opt_argc_, varargs_, is_constructor_, op_overload_, self_valid_, &gen_ctx); /* * Add a line record at the start of the method for all of the * generated method prolog code. Some prolog code can throw errors, so * we want a line record at the start of the code body to indicate the * location of any such errors. */ if (start_desc_ != 0) G_cs->add_line_rec(start_desc_, start_linenum_); /* * Add each local symbol table enclosing the code body's primary * local symbol table to the frame list. The outermost code body * table can be outside the primary code body table for situations * such as anonymous functions. Since these tables are outside of * any statements, we must explicitly add them to ensure that they * are assigned debugging frame ID's and are written to the debug * data. */ if (lcltab_ != 0) { /* add each frame outside the primary frame to the code gen list */ for (CTcPrsSymtab *tab = lcltab_->get_parent() ; tab != 0 ; tab = tab->get_parent()) G_cs->set_local_frame(tab); } /* the method's local symbol table is now the active symbol table */ G_cs->set_local_frame(lcltab_); /* if we have a local context, initialize it */ if (has_local_ctx_) { /* write code to create the new Vector to store the context locals */ CTPNConst::s_gen_code_int(local_ctx_arr_size_); G_cg->write_op(OPC_DUP); G_cg->write_op(OPC_NEW1); G_cs->write(2); G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_VECTOR)); /* retrieve the object value */ G_cg->write_op(OPC_GETR0); /* * we duplicated the vector size argument, then we popped it and * pushed the object; so we have a maximum of one extra push and a * net of zero */ G_cg->note_push(); G_cg->note_pop(); /* store the new object in the context local variable */ CTcSymLocal::s_gen_code_setlcl_stk(local_ctx_var_, FALSE); /* * go through our symbol table, and copy each parameter that's * also a context local into its context local slot */ if (lcltab_ != 0) lcltab_->enum_entries(&enum_for_param_ctx, this); } /* * If we have a varargs-list parameter, generate the code to set up * the list value from the actual parameters. Note that we must do * this after we set up the local context, in case the varargs list * parameter variable is a context local, in which case it will need * to be stored in the context, in which case we need the context to * be initialized first. */ if (varargs_list_) { /* generate the PUSHPARLST instruction to create the list */ G_cg->write_op(OPC_PUSHPARLST); G_cs->write((uchar)argc_); /* * we pushed at least one value (the list); we don't know how many * others we might have pushed, but it doesn't matter because the * interpreter is responsible for checking for stack space */ G_cg->note_push(); /* store the list in our varargs parameter list local */ varargs_list_local_->gen_code_setlcl(); } /* * generate other special parameter setup code: named arguments and * default values */ if (lcltab_ != 0) { /* * generate bindings for named parameters and optional positional * parameters that don't have default expressions, and build the * array of optional parameters with default expressions */ defval_ctx dctx; lcltab_->enum_entries(&enum_for_named_params, &dctx); /* sort the default value list in left-to-right parameter order */ dctx.sort(); /* generate the default value assignments */ for (int i = 0 ; i < dctx.nv ; ++i) { /* get this local */ CTcSymLocal *lcl = dctx.v[i]; /* generate the proper code depending on the parameter type */ if (lcl->is_named_param()) { /* * Named parameter with a default. * * If the default is a constant, call t3GetNamedArg(name, * defVal). This returns the argument value, or the * default value if the argument isn't defined. * * If the default value expression isn't a constant, we * can't evaluate it until we know whether or not the * argument is defined, since the expression could have * side effects that we don't want to trigger unless we * really need to evaluate the expression. So in this * case, we have to look up the argument first, and set the * default only the argument doesn't exist. * * Note that we could handle both cases with the * non-constant handling, since it would make no difference * semantically to defer evaluating a constant until after * checking to see if the argument exists. However, the * separate version for constants yields faster and smaller * byte code, so we use it as an optimization for a common * case. */ if (lcl->get_defval_expr()->is_const()) { /* constant default - use t3GetNamedArg(name, defval) */ lcl->get_defval_expr()->gen_code(FALSE, FALSE); CTPNConst::s_gen_code_str_by_mode(lcl); call_internal_bif("t3GetNamedArg", TCERR_NAMED_PARAM_MISSING_FUNC, 2); } else { /* * Non-constant default value. We don't want to * evaluate the argument unless necessary in this case, * so start by calling t3GetNamedArg(name). This will * throw an error if the argument doesn't exist, so do * this in an implied try/catch block. If we return * without an error, simply proceed with the value * returned. If an error is thrown, catch the error * and *then* evaluate the default value. */ ulong start_ofs = G_cs->get_ofs(); CTPNConst::s_gen_code_str_by_mode(lcl); call_internal_bif("t3GetNamedArg", TCERR_NAMED_PARAM_MISSING_FUNC, 1); /* that's the end of the implied 'try' section */ ulong end_ofs = G_cs->get_ofs() - 1; /* * On a successful return, the result value will be at * top of stack, so just jump past the evaluation of * the default value. Since we're done with this * branch, clear what it left on the stack, since the * 'catch' branch will leave the same thing on the * stack in parallel. */ CTcCodeLabel *asi_lbl = gen_jump_ahead(OPC_JMP); G_cg->note_pop(); /* this is the start of our implied 'catch' block */ G_cg->get_exc_table()->add_catch( start_ofs, end_ofs, VM_INVALID_OBJ, G_cs->get_ofs()); G_cg->note_push(); /* discard the exception */ G_cg->write_op(OPC_DISC); G_cg->note_pop(); /* generate the default value expression */ lcl->get_defval_expr()->gen_code(FALSE, FALSE); /* this is the common 'assign' branch point */ def_label_pos(asi_lbl); } /* assign the top-of-stack value to the local */ lcl->gen_code_asi(TRUE, 1, TC_ASI_SIMPLE, "=", 0, FALSE, FALSE, 0); } else { /* it's a positional parameter - generate it */ dctx.gen_opt_positional(lcl); } } } /* * If this is a dynamic function (a DynamicFunc object), and it's * defined with the 'function' keyword (not 'method'), and it has a * 'self', we need to set up the method context to point to the * enclosing frame object's method context. */ if (G_cg->is_eval_for_dyn() && is_dyn_func_ && self_valid_ && (self_referenced_ || full_method_ctx_referenced_)) { /* * We need the method context. For dyanmic code, the invokee is * the DynamicFunc, and the DynamicFunc makes the context available * via its indexed value [1]. */ G_cg->write_op(OPC_PUSHCTXELE); G_cs->write(PUSHCTXELE_INVOKEE); CTPNConst::s_gen_code_int(1); G_cg->write_op(OPC_INDEX); /* we pushed the invokee and popped it again with the INDEX */ G_cg->note_push(); G_cg->note_pop(); /* * The invokee will store the appropriate object in its [1] * element, according to which type of method context we need. * Generate the appropriate 'set' for it. */ if (full_method_ctx_referenced_) { /* load the full method context */ G_cg->write_op(OPC_LOADCTX); } else { /* load the 'self' object */ G_cg->write_op(OPC_SETSELF); } /* that pops the context object */ G_cg->note_pop(); } /* * Generate code to initialize each enclosing-context-pointer local - * these variables allow us to find the context objects while we're * running inside this function. * * Before 3.1, we had to generate context level 1 last, because this * level sets 'self' for an anonymous function to the saved 'self' from * the context. Starting with 3.1, the stack frame has a separate * entry for the invokee, so we no longer conflate the anonymous * function object with 'self'. */ CTcCodeBodyCtx *cur_ctx; int ctx_idx; for (ctx_idx = 0, cur_ctx = ctx_head_ ; cur_ctx != 0 ; cur_ctx = cur_ctx->nxt_, ++ctx_idx) { /* * Get this context value, stored in invokee[n+2]. Note that the * context object indices start at 2 because the FUNCPTR value for * the code itself is at index 1. */ G_cg->write_op(OPC_PUSHCTXELE); G_cs->write(PUSHCTXELE_INVOKEE); CTPNConst::s_gen_code_int(ctx_idx + 2); G_cg->write_op(OPC_INDEX); /* * we pushed the object, then popped the object and index and * pushed the indexed value - this is a net of no change with one * maximum push */ G_cg->note_push(); G_cg->note_pop(); /* * If this is context level 1, and this context has a 'self', and * we need either 'self' or the full method context from the * lexically enclosing scope, generate code to load the self or the * full method context (as appropriate) from our local context. * * The enclosing method context is always stored in the context at * level 1, because this is inherently shared context for all * enclosed lexical scopes. We thus only have to worry about this * for context level 1. */ if (cur_ctx->level_ == 1 && !is_anon_method_ && self_valid_ && (self_referenced_ || full_method_ctx_referenced_)) { CTPNCodeBody *outer; /* * we just put our context object on the stack in preparation * for storing it - make a duplicate copy of it for our own * purposes */ G_cg->write_op(OPC_DUP); G_cg->note_push(); /* get the saved method context from the context object */ CTPNConst::s_gen_code_int(TCPRS_LOCAL_CTX_METHODCTX); G_cg->write_op(OPC_INDEX); /* * Load the context. We must check the outermost context to * determine what it stored, because we must load whatever it * stored. */ if ((outer = get_outermost_enclosing()) != 0 && outer->local_ctx_needs_full_method_ctx()) { /* load the full method context */ G_cg->write_op(OPC_LOADCTX); } else { /* load the 'self' object */ G_cg->write_op(OPC_SETSELF); } /* * we popped two values and pushed one in the INDEX, then * popped a value in the LOADCTX or SETSELF: the net is removal * of two elements and no additional maximum depth */ G_cg->note_pop(2); } /* store the context value in the appropriate local variable */ CTcSymLocal::s_gen_code_setlcl_stk(cur_ctx->var_num_, FALSE); } /* * if we created our own local context, and we have a 'self' object, * and we need access to the 'self' object or the full method context * from anonymous functions that refer to the local context, generate * code to store the appropriate data in the local context */ if (has_local_ctx_ && self_valid_ && (local_ctx_needs_self_ || local_ctx_needs_full_method_ctx_)) { /* * Check to see what we need. If we're marked as needing the full * method context, OR our outermost enclosing context has the full * context, we need to generate the full context. Intermediate * nesting levels of anonymous functions can need less context than * inner levels, but the context we actually generate at the outer * level depends on the innermost function's needs. So we simply * need to follow the lead of the outermost level, since that's * what the inner levels will all do. */ CTPNCodeBody *outer = get_outermost_enclosing(); if (local_ctx_needs_full_method_ctx_ || (outer != 0 && outer->local_ctx_needs_full_method_ctx())) { /* * we need the full method context - generate code to store it * and push a reference to it onto the stack */ G_cg->write_op(OPC_STORECTX); } else { /* we only need 'self' - push it */ G_cg->write_op(OPC_PUSHSELF); } /* we just pushed one value */ G_cg->note_push(); /* assign the value to the context variable */ if (local_ctx_var_ <= 255 && TCPRS_LOCAL_CTX_METHODCTX <= 255) { /* we can make the assignment with a single instruction */ G_cg->write_op(OPC_SETINDLCL1I8); G_cs->write((uchar)local_ctx_var_); G_cs->write(TCPRS_LOCAL_CTX_METHODCTX); /* that pops one value */ G_cg->note_pop(); } else { /* get the context object */ CTcSymLocal::s_gen_code_getlcl(local_ctx_var_, FALSE); /* store the data in the local context object */ CTPNConst::s_gen_code_int(TCPRS_LOCAL_CTX_METHODCTX); G_cg->write_op(OPC_SETIND); /* discard the indexed result */ G_cg->write_op(OPC_DISC); /* * the SETIND pops three values and pushes one, then we pop one * more with the DISC - this is a net three pops with no extra * maximum depth */ G_cg->note_pop(3); } } /* generate the compound statement, if we have one */ if (stm_ != 0) stm_->gen_code(TRUE, TRUE); #ifdef T3_DEBUG if (G_cg->get_sp_depth() != 0) { printf("---> stack depth is %d after block codegen (line %ld)!\n", G_cg->get_sp_depth(), end_linenum_); if (fixup_owner_sym_ != 0) printf("---> code block for %.*s\n", (int)fixup_owner_sym_->get_sym_len(), fixup_owner_sym_->get_sym()); } #endif /* close the method */ G_cg->close_method(local_cnt_, lcltab_, end_desc_, end_linenum_, &gen_ctx, named_arg_tables_); /* remember the head of the nested symbol table list */ first_nested_symtab_ = G_cs->get_first_frame(); /* * Generate debug records. If we're in debug mode, include the full * symbolic debugging information, including source line locations. * For release builds, include only the local variable symbols and * frame information; we need those even in release builds, for * reflection purposes. * * If we're generating this code for a debugger evaluation, omit * symbols. We don't need reflection support for debugger expressions, * since there doesn't seem to be any practical reason you'd need it * there. */ if (!G_cg->is_eval_for_debug()) build_debug_table(gen_ctx.method_ofs, G_debug); /* check for unreferenced labels and issue warnings */ check_unreferenced_labels(); /* show the disassembly of the code block if desired */ if (G_disasm_out != 0) show_disassembly(gen_ctx.method_ofs, gen_ctx.code_start_ofs, gen_ctx.code_end_ofs); /* clean up globals for the end of the method */ G_cg->close_method_cleanup(&gen_ctx); } /* * disassembly stream source implementation */ class CTcUnasSrcCodeBody: public CTcUnasSrc { public: CTcUnasSrcCodeBody(CTcCodeStream *str, unsigned long code_start_ofs, unsigned long code_end_ofs) { /* remember the stream */ str_ = str; /* start at the starting offset */ cur_ofs_ = code_start_ofs; /* remember the ending offset */ end_ofs_ = code_end_ofs; } /* read the next byte */ int next_byte(char *ch) { /* if there's anything left, return it */ if (cur_ofs_ < end_ofs_) { /* return the next byte */ *ch = str_->get_byte_at(cur_ofs_); ++cur_ofs_; return 0; } else { /* indicate end of file */ return 1; } } /* get the current offset */ ulong get_ofs() const { return cur_ofs_; } protected: /* code stream */ CTcCodeStream *str_; /* current offset */ unsigned long cur_ofs_; /* offset of end of code stream */ unsigned long end_ofs_; }; /* * Show the disassembly of this code block */ void CTPNCodeBody::show_disassembly(unsigned long start_ofs, unsigned long code_start_ofs, unsigned long code_end_ofs) { int argc; int locals; int total_stk; unsigned exc_rel; unsigned dbg_rel; /* first, dump the header */ argc = (unsigned char)G_cs->get_byte_at(start_ofs); locals = G_cs->readu2_at(start_ofs + 2); total_stk = G_cs->readu2_at(start_ofs + 4); exc_rel = G_cs->readu2_at(start_ofs + 6); dbg_rel = G_cs->readu2_at(start_ofs + 8); G_disasm_out->print("%8lx .code\n", start_ofs); G_disasm_out->print(" .argcount %d%s\n", (argc & 0x7f), (argc & 0x80) != 0 ? "+" : ""); G_disasm_out->print(" .locals %d\n", locals); G_disasm_out->print(" .maxstack %d\n", total_stk); /* set up a code stream reader and dump the code stream */ CTcUnasSrcCodeBody src(G_cs, code_start_ofs, code_end_ofs); CTcT3Unasm::disasm(&src, G_disasm_out); /* show the exception table, if there is one */ if (exc_rel != 0) { unsigned long exc_ofs; unsigned long exc_end_ofs; /* get the starting address */ exc_ofs = start_ofs + exc_rel; /* * get the length - it's the part up to the debug records, or the * part up to the current code offset if there are no debug records */ exc_end_ofs = (dbg_rel != 0 ? start_ofs + dbg_rel : G_cs->get_ofs()); /* show the table */ G_disasm_out->print(".exceptions\n"); CTcUnasSrcCodeBody exc_src(G_cs, exc_ofs, exc_end_ofs); CTcT3Unasm::show_exc_table(&exc_src, G_disasm_out, start_ofs); } /* add a blank line at the end */ G_disasm_out->print("\n"); } /* * Check for unreferenced local variables */ void CTPNCodeBody::check_locals() { CTcPrsSymtab *tab; /* check for unreferenced locals in each nested scope */ for (tab = first_nested_symtab_ ; tab != 0 ; tab = tab->get_list_next()) { /* check this table */ tab->check_unreferenced_locals(); } } /* * local symbol table enumerator for checking for parameter symbols that * belong in the local context */ void CTPNCodeBody::enum_for_param_ctx(void *, class CTcSymbol *sym) { /* if this is a local, check it further */ if (sym->get_type() == TC_SYM_LOCAL || sym->get_type() == TC_SYM_PARAM) { CTcSymLocal *lcl = (CTcSymLocal *)sym; /* * if it's a parameter, and it's also a context variable, its * value needs to be moved into the context */ if (lcl->is_param() && lcl->is_ctx_local()) { /* get the actual parameter value from the stack */ CTcSymLocal::s_gen_code_getlcl(lcl->get_var_num(), TRUE); /* * Store the value in the context variable. This is an implied * assignment equivalent to binding a formal to its actual * value. */ lcl->gen_code_asi(TRUE, 1, TC_ASI_SIMPLE, "=", 0, TRUE, FALSE, 0); } } } /* * local symbol table enumerator for generating named argument bindings */ void CTPNCodeBody::enum_for_named_params(void *ctx, class CTcSymbol *sym) { /* * If this is a local, and it's a named parameter, generate code to * bind it. If it's optional and has a default value expression, skip * it on this pass, since we need to generate default expressions in * the order in which they appear in the argument list to ensure that * dependencies are resolved properly and side effects happen in the * proper order. */ if (sym->get_type() == TC_SYM_LOCAL) { /* get the symbol, properly cast */ CTcSymLocal *lcl = (CTcSymLocal *)sym; /* check what we have */ if (lcl->get_defval_expr() != 0) { /* * It's an optional parameter (positional or named) with a * default value. We can't generate the binding yet, since we * need to generate these in left-to-right order, and the table * enumeration is in hash-table order instead. So just add it * to the context list, which we'll sort into the proper * left-right sequence later. */ ((defval_ctx *)ctx)->add(lcl); } else if (lcl->is_named_param()) { /* * It's a named parameter - generate the binding code. If it's * optional, call t3GetNamedArg(name, nil) to use the default * nil value if it's not defined. If it's not optional, use * t3GetNamedArg(name) to throw an error on failure. */ int fargc = 1; if (lcl->is_opt_param()) { /* optional parameter - call t3GetNamedArg(argname, nil) */ G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); ++fargc; } /* push the parameter name and call the appropriate builtin */ CTPNConst::s_gen_code_str_by_mode(sym); call_internal_bif("t3GetNamedArg", TCERR_NAMED_PARAM_MISSING_FUNC, fargc); /* * Assign the result to the local. This is an implied * assignment equivalent to binding an actual argument value to * this formal. */ lcl->gen_code_asi(TRUE, 1, TC_ASI_SIMPLE, "=", 0, FALSE, FALSE, 0); } else if (lcl->is_opt_param()) { /* * It's an optional parameter without a default value (we know * there's no default, since we handled that case above). * Generate code to load it if present. */ defval_ctx::gen_opt_positional(lcl); } } } /* * Add a named argument call table to the list. This is invoked each time * we generate a call that uses named parameters. We defer generation of * the argument name tables until the end of the method, to avoid having to * jump past inline tables while executing opcodes. */ CTcCodeLabel *CTPNCodeBody::add_named_arg_tab(const CTcNamedArgs *named_args) { /* allocate the tracking object */ CTcNamedArgTab *tab = new (G_prsmem) CTcNamedArgTab(named_args); /* link it into our list */ tab->nxt = named_arg_tables_; named_arg_tables_ = tab; /* return the code label for the table */ return tab->lbl; } /* ------------------------------------------------------------------------ */ /* * Debugger records and local symbol table */ /* local symbol enumeration callback context */ struct write_local_to_debug_frame_ctx { /* are we just counting the entries? */ int count_only; /* number of symbols written so far */ int count; }; /* * Callback for symbol table enumeration - write a local variable entry to * the code stream for a debug frame record. */ void CTPNCodeBody::write_local_to_debug_frame(void *ctx0, CTcSymbol *sym) { write_local_to_debug_frame_ctx *ctx; /* cast our context */ ctx = (write_local_to_debug_frame_ctx *)ctx0; /* write it out */ if (sym->write_to_debug_frame(ctx->count_only)) { /* we wrote the symbol - count it */ ++(ctx->count); } } /* * Build the debug information table for a code body */ void CTPNCodeBody::build_debug_table(ulong start_ofs, int include_lines) { size_t i; /* * Count up the frames that contain local variables. Frames that * contain no locals can be elided to reduce the size of the file, * since they carry no information. */ CTcPrsSymtab *frame; int frame_cnt; for (frame_cnt = 0, frame = G_cs->get_first_frame() ; frame != 0 ; frame = frame->get_list_next()) { /* enumerate the entries through our counting callback */ write_local_to_debug_frame_ctx cbctx; cbctx.count = 0; cbctx.count_only = TRUE; frame->enum_entries(&write_local_to_debug_frame, &cbctx); /* check to see if it has any locals */ if (cbctx.count != 0) { /* it has locals - renumber it to the current list position */ ++frame_cnt; frame->set_list_index(frame_cnt); } else { /* no locals - flag it as empty by setting its list index to 0 */ frame->set_list_index(0); } } /* * If we're not writing line records, and we don't have any frames with * local variables, skip writing the debug table. The debug table in * this case is for local variable reflection information only, so if * we don't have any locals, we don't need the table at all. */ if (!include_lines && frame_cnt == 0) return; /* fix up the debug record offset in the prolog to point here */ G_cs->write2_at(start_ofs + 8, G_cs->get_ofs() - start_ofs); /* * Write the debug table header. In the current VM version, this is * empty, so we have nothing to write. However, if we're dynamically * targeting a newer version, pad out any expected header space with * zeros. */ for (int j = G_sizes.dbg_hdr ; j > 0 ; --j) G_cs->write(0); /* * Add this offset to our list of line records. If we're creating an * object file, upon re-loading the object file, we'll need to go * through all of the line record tables and fix up the file references * to the final file numbering system at link time, so we need this * memory of where the line record are. We'll also need to fix up the * local variable name records to point to the consolidated set of * strings in the constant pool. */ G_cg->add_debug_line_table(G_cs->get_ofs()); /* if desired, write the source code location ("line") records */ if (include_lines) { /* write the number of line records */ G_cs->write2(G_cs->get_line_rec_count()); /* write the line records themselves */ for (i = 0 ; i < G_cs->get_line_rec_count() ; ++i) { /* get this record */ tcgen_line_t *rec = G_cs->get_line_rec(i); /* write the offset of the statement's first opcode */ G_cs->write2(rec->ofs); /* write the source file ID and line number */ G_cs->write2(rec->source_id); G_cs->write4(rec->source_line); /* * Get the frame for the line record. If the frame is empty, * use the parent frame instead, since we omit empty frames. */ CTcPrsSymtab *fr = rec->frame; while (fr != 0 && fr->get_list_index() == 0) fr = fr->get_parent(); /* write the frame ID */ G_cs->write2(fr == 0 ? 0 : fr->get_list_index()); /* add any padding expected in the target VM version */ for (int j = G_sizes.dbg_line - 10 ; j > 0 ; --j) G_cs->write(0); } } else { /* no line records - write zero as the line count */ G_cs->write2(0); } /* * write a placeholder pointer to the next byte after the end of the * frame table */ ulong post_ptr_ofs = G_cs->get_ofs(); G_cs->write2(0); /* write the frame count */ G_cs->write2(frame_cnt); /* * Write a placeholder frame index table. We will come back and fix up * this table as we actually write out the frames, but we don't * actually know how big the individual frame records will be yet, so * we can only write placeholders for them for now. First, note where * the frame index table begins. */ ulong index_ofs = G_cs->get_ofs(); /* write the placeholder index entries */ for (i = 0 ; i < (size_t)frame_cnt ; ++i) G_cs->write2(0); /* write the individual frames */ for (frame = G_cs->get_first_frame() ; frame != 0 ; frame = frame->get_list_next()) { /* * if this frame has a zero list index, it means that it contains * no writable symbols, so we can omit it from the debug table */ if (frame->get_list_index() == 0) continue; /* * go back and fill in the correct offset (from the entry itself) * in the index table entry for this frame */ G_cs->write2_at(index_ofs, G_cs->get_ofs() - index_ofs); /* move on to the next index entry */ index_ofs += 2; /* * Get the effective parent. If the actual parent doesn't have a * list index, it's empty, so we're not writing it. Find the * nearest parent of the parent that we'll actually write, and use * that as our effective parent. */ CTcPrsSymtab *par; for (par = frame->get_parent() ; par != 0 && par->get_list_index() == 0 ; par = par->get_parent()) ; /* write the ID of the enclosing frame */ G_cs->write2(par != 0 ? par->get_list_index() : 0); /* * write a placeholder for the count of the number of entries in * the frame, and remember where the placeholder is so we can come * back and fix it up later */ ulong count_ofs = G_cs->get_ofs(); G_cs->write2(0); /* add the bytecode range covered, if there's room in the format */ if (G_sizes.dbg_frame >= 8) { G_cs->write2(frame->get_start_ofs()); G_cs->write2(frame->get_end_ofs()); } /* pad out any extra space */ for (int j = G_sizes.dbg_frame - 8 ; j > 0 ; --j) G_cs->write(0); /* initialize the enumeration callback context */ write_local_to_debug_frame_ctx cbctx; cbctx.count = 0; cbctx.count_only = FALSE; /* write this frame table's entries */ frame->enum_entries(&write_local_to_debug_frame, &cbctx); /* go back and fix up the symbol count */ G_cs->write2_at(count_ofs, cbctx.count); } /* * go back and fill in the post-pointer offset - this is a pointer to * the next byte after the end of the frame table; write the offset * from the post-pointer field to the current location */ G_cs->write2_at(post_ptr_ofs, G_cs->get_ofs() - post_ptr_ofs); /* * write the required UINT4 zero value after the frame table - this * is a placeholder for future expansion (if we add more information * to the debug table later, this value will be non-zero to indicate * the presence of the additional information) */ G_cs->write4(0); } /* ------------------------------------------------------------------------ */ /* * 'return' statement */ /* * generate code */ void CTPNStmReturn::gen_code(int, int) { int val_on_stack; int need_gen; /* add a line record */ add_debug_line_rec(); /* presume we'll generate a value */ need_gen = TRUE; val_on_stack = FALSE; /* generate the return value expression, if appropriate */ if (expr_ != 0) { /* * it's an error if we're in a constructor, because a * constructor implicitly always returns 'self' */ if (G_cg->is_in_constructor()) log_error(TCERR_CONSTRUCT_CANNOT_RET_VAL); /* check for a constant expression */ if (expr_->is_const()) { switch(expr_->get_const_val()->get_type()) { case TC_CVT_NIL: case TC_CVT_TRUE: /* * we can use special constant return instructions for * these, so there's no need to generate the value */ need_gen = FALSE; break; default: /* * other types don't have constant-return opcodes, so we * must generate the expression code */ need_gen = TRUE; break; } } /* if necessary, generate the value */ if (need_gen) { int depth; /* note the initial stack depth */ depth = G_cg->get_sp_depth(); /* * Generate the value. We are obviously not discarding the * value, and since returning a value is equivalent to * assigning the value, we must use the stricter assignment * (not 'for condition') rules for logical expressions */ expr_->gen_code(FALSE, FALSE); /* note whether we actually left a value on the stack */ val_on_stack = (G_cg->get_sp_depth() > depth); } else { /* * we obviously aren't leaving a value on the stack if we * don't generate anything */ val_on_stack = FALSE; } } /* * Before we return, let any enclosing statements generate any code * necessary to leave their scope (in particular, we must invoke * 'finally' handlers in any enclosing 'try' blocks). * * Note that we generated the expression BEFORE we call any * 'finally' handlers. This is necessary because something we call * in the course of evaluating the return value could have thrown an * exception; if we were to call the 'finally' clauses before * generating the return value, we could invoke the 'finally' clause * twice (once explicitly, once in the handling of the thrown * exception), which would be incorrect. By generating the * 'finally' calls after the return expression, we're sure that the * 'finally' blocks are invoked only once - either through the * throw, or else now, after there's no more possibility of a * 'throw' before the return. */ if (G_cs->get_enclosing() != 0) { int did_save_retval; uint fin_ret_lcl; /* * if we're going to generate any subroutine calls, and we have * a return value on the stack, we need to save the return value * in a local to make sure the calculated value isn't affected * by the subroutine call */ if (val_on_stack && G_cs->get_enclosing()->will_gen_code_unwind_for_return() && G_cs->get_code_body() != 0) { /* allocate a local variable to save the return value */ fin_ret_lcl = G_cs->get_code_body()->alloc_fin_ret_lcl(); /* save the return value in a stack temporary for a moment */ CTcSymLocal::s_gen_code_setlcl_stk(fin_ret_lcl, FALSE); /* * note that we saved the return value, so we can retrieve * it later */ did_save_retval = TRUE; } else { /* note that we didn't save the return value */ did_save_retval = FALSE; } /* generate the unwind */ G_cs->get_enclosing()->gen_code_unwind_for_return(); /* if we saved the return value, retrieve it */ if (did_save_retval) CTcSymLocal::s_gen_code_getlcl(fin_ret_lcl, FALSE); } /* check for an expression to return */ if (G_cg->is_in_constructor()) { /* we're in a constructor - return 'self' */ G_cg->write_op(OPC_PUSHSELF); G_cg->write_op(OPC_RETVAL); } else if (expr_ == 0) { /* * there's no expression - generate a simple void return (but * explicitly return nil, so we don't return something left in * R0 from a previous function call we made) */ G_cg->write_op(OPC_RETNIL); } else { /* check for a constant expression */ if (expr_->is_const()) { switch(expr_->get_const_val()->get_type()) { case TC_CVT_NIL: /* generate a RETNIL instruction */ G_cg->write_op(OPC_RETNIL); break; case TC_CVT_TRUE: /* generate a RETTRUE instruction */ G_cg->write_op(OPC_RETTRUE); break; default: break; } } /* * if we needed code generation to evaluate the return value, we * now need to return the value */ if (need_gen) { /* * Other types don't have constant-return opcodes. We * already generated the expression value (before invoking * the enclosing 'finally' handlers, if any), so the value * is on the stack, and all we need to do is return it. * * If we didn't actually leave a value on the stack, we'll * just return nil. */ if (val_on_stack) { /* generate the return-value opcode */ G_cg->write_op(OPC_RETVAL); /* RETVAL removes an element from the stack */ G_cg->note_pop(); } else { /* * The depth didn't change - they must have evaluated an * expression involving a dstring or void function. * Return nil instead of the non-existent value. */ G_cg->write_op(OPC_RETNIL); } } } } /* ------------------------------------------------------------------------ */ /* * Static property initializer statement */ void CTPNStmStaticPropInit::gen_code(int, int) { int depth; /* add a line record */ add_debug_line_rec(); /* note the initial stack depth */ depth = G_cg->get_sp_depth(); /* generate the expression, keeping the generated value */ expr_->gen_code(FALSE, FALSE); /* ensure that we generated a value; if we didn't, push nil by default */ if (G_cg->get_sp_depth() <= depth) { /* push a default nil value */ G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); } /* * duplicate the value on the stack, so we can assign it to * initialize the property and also return it */ G_cg->write_op(OPC_DUP); G_cg->note_push(); /* write the SETPROPSELF to initialize the property */ G_cg->write_op(OPC_SETPROPSELF); G_cs->write_prop_id(prop_); /* SETPROPSELF removes the value */ G_cg->note_pop(); /* return the value (which we duplicated on the stack) */ G_cg->write_op(OPC_RETVAL); /* RETVAL removes the value */ G_cg->note_pop(); } /* ------------------------------------------------------------------------ */ /* * Object Definition Statement */ /* * generate code */ void CTPNStmObject::gen_code(int, int) { /* if this object has been replaced, don't generate any code for it */ if (replaced_) return; /* add an implicit constructor if necessary */ add_implicit_constructor(); /* get the appropriate stream for generating the data */ CTcDataStream *str = obj_sym_->get_stream(); /* clear the internal flags */ uint internal_flags = 0; /* * if we're a modified object, set the 'modified' flag in the object * header */ if (modified_) internal_flags |= TCT3_OBJ_MODIFIED; /* set the 'transient' flag if appropriate */ if (transient_) internal_flags |= TCT3_OBJ_TRANSIENT; /* clear the object flags */ uint obj_flags = 0; /* * If we're specifically marked as a 'class' object, or we're a * modified object, set the 'class' flag in the object flags. */ if (is_class_ || modified_) obj_flags |= TCT3_OBJFLG_CLASS; /* remember our starting offset in the object stream */ ulong start_ofs = str->get_ofs(); /* * store our stream offset in our defining symbol, for storage in * the object file */ obj_sym_->set_stream_ofs(start_ofs); /* write our internal flags */ str->write2(internal_flags); /* * First, write the per-object image file "OBJS" header - each * object starts with its object ID and the number of bytes in the * object's metaclass-specific data. For now, write zero as a * placeholder for our data size. Note that this is a * self-reference: it must be modified if the object is renumbered. */ str->write_obj_id_selfref(obj_sym_); str->write2(0); /* write a placeholder for the superclass count */ str->write2(0); /* write the fixed property count */ str->write2(proplist_.cnt_); /* write the object flags */ str->write2(obj_flags); /* * First, go through the superclass list and verify that each * superclass is actually an object. */ int bad_sc = FALSE; int sc_cnt = 0; for (CTPNSuperclass *sc = get_first_sc() ; sc != 0 ; sc = sc->nxt_) { /* look up the superclass in the global symbol table */ CTcSymObj *sc_sym = (CTcSymObj *)sc->get_sym(); /* make sure it's defined, and that it's really an object */ if (sc_sym == 0) { /* not defined */ log_error(TCERR_UNDEF_SYM_SC, (int)sc->get_sym_len(), sc->get_sym_txt(), (int)obj_sym_->get_sym_len(), obj_sym_->get_sym()); /* note that we have an invalid superclass */ bad_sc = TRUE; } else if (sc_sym->get_type() != TC_SYM_OBJ) { /* log an error */ log_error(TCERR_SC_NOT_OBJECT, (int)sc_sym->get_sym_len(), sc_sym->get_sym()); /* note that we have an invalid superclass */ bad_sc = TRUE; } else { /* count the superclass */ ++sc_cnt; /* write the superclass to the object header */ str->write_obj_id(sc_sym->get_obj_id()); } } /* * If we detected a 'bad template' error when we were parsing the * object definition, and all of our superclasses are valid, report the * template error. * * Do not report this error if we have any undefined or invalid * superclasses, because (1) we've already reported one error for this * object definition (the bad superclass error), and (2) the missing * template is likely just a consequence of the bad superclass, since * we can't have scanned the proper superclass's list of templates if * they didn't tell us the correct superclass to start with. When they * fix the superclass list and re-compile the code, it's likely that * this will fix the template problem as well, since we'll probably be * able to find the template give the corrected superclass list. * * If we found an undescribed class anywhere in our hierarchy, a * template simply cannot be used with this object; otherwise, the * error is that we failed to find a suitable template */ if (has_bad_template() && !bad_sc) log_error(has_undesc_sc() ? TCERR_OBJ_DEF_CANNOT_USE_TEMPLATE : TCERR_OBJ_DEF_NO_TEMPLATE); /* go back and write the superclass count to the header */ str->write2_at(start_ofs + TCT3_TADSOBJ_HEADER_OFS, sc_cnt); /* * Write the properties. We're required to write the properties in * sorted order of property ID, but we can't do that yet, because * the property ID's aren't finalized until after linking. For now, * just write them out in the order in which they were defined. */ for (CTPNObjProp *prop = proplist_.first_ ; prop != 0 ; prop = prop->nxt_) { /* make sure we have a valid property symbol */ if (prop->get_prop_sym() != 0) { /* write the property ID */ str->write_prop_id(prop->get_prop_sym()->get_prop()); /* generate code for the property */ prop->gen_code(FALSE, FALSE); } } /* * go back and write the size of our metaclass-specific data - this * goes at offset 4 in the T3 generic metaclass header */ str->write2_at(start_ofs + TCT3_META_HEADER_OFS + 4, str->get_ofs() - (start_ofs + TCT3_META_HEADER_OFS + 6)); } /* * Check for unreferenced local variables */ void CTPNStmObject::check_locals() { /* check for unreferenced locals for each property */ for (CTPNObjProp *prop = proplist_.first_ ; prop != 0 ; prop = prop->nxt_) prop->check_locals(); } qtads-2.1.7/tads3/tct3.h000066400000000000000000001211341265017072300147060ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/TCT3.H,v 1.5 1999/07/11 00:46:55 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3.h - TADS 3 compiler - T3 Virtual Machine Code Generator Function Notes Modified 04/30/99 MJRoberts - Creation */ #ifndef TCT3_H #define TCT3_H #include "t3std.h" #include "tcprs.h" #include "vmop.h" #include "vmtype.h" #include "tct3ty.h" /* ------------------------------------------------------------------------ */ /* * include the T3-specific CVmRuntimeSymbols class definition */ #include "vmrunsym.h" /* ------------------------------------------------------------------------ */ /* * include the T3-specific final parse node classes */ #include "tct3drv.h" /* ------------------------------------------------------------------------ */ /* * Define some internal compiler datatypes - any type * VM_FIRST_INVALID_TYPE or higher is not used by the VM and can thus be * used for our own internal types. */ const vm_datatype_t VM_VOCAB_LIST = VM_MAKE_INTERNAL_TYPE(0); /* ------------------------------------------------------------------------ */ /* * Data structure sizes. These are the sizes of various data structures * that we write to the image file; these values are global to the * entire image file, and are constants of the current file format. */ /* method header size */ #define TCT3_METHOD_HDR_SIZE 10 /* exception table entry size */ #define TCT3_EXC_ENTRY_SIZE 10 /* debugger line record entry size */ #define TCT3_LINE_ENTRY_SIZE 10 /* debug table header size */ #define TCT3_DBG_HDR_SIZE 0 /* debugger local symbol record size */ #define TCT3_DBG_LCLSYM_HDR_SIZE 6 /* debugger frame record header size */ #define TCT3_DBG_FRAME_SIZE 8 /* debugger record format version */ #define TCT3_DBG_FMT_VSN 2 /* ------------------------------------------------------------------------ */ /* * Object file header flags. */ #define TCT3_OBJHDR_DEBUG 0x0001 /* ------------------------------------------------------------------------ */ /* * Object Stream prefix flags. Each object we write to the object * stream starts with a prefix byte that we use to store some extra flag * information about the object. */ /* object has been replaced - do not write to image file */ #define TCT3_OBJ_REPLACED 0x0001 /* object has been modified */ #define TCT3_OBJ_MODIFIED 0x0002 /* object is transient */ #define TCT3_OBJ_TRANSIENT 0x0004 /* ------------------------------------------------------------------------ */ /* * T3 metaclass object stream header sizes */ /* * internal header size - this is an extra header the compiler adds for * each object in an object stream * * UINT2 compiler_flags */ const size_t TCT3_OBJ_INTERNHDR_SIZE = 2; /* offset of the internal header from the start of the object data */ const size_t TCT3_OBJ_INTERNHDR_OFS = 0; /* offset of internal header flags */ const size_t TCT3_OBJ_INTERNHDR_FLAGS_OFS = TCT3_OBJ_INTERNHDR_OFS; /* * T3 generic metaclass header - this is the header on every metaclass * in a T3 image file 'OBJS' block. * * UINT4 object_id *. UINT2 metaclass_specific_byte_count */ const size_t TCT3_META_HEADER_SIZE = 6; /* * large metaclass header size - this is the same as the standard * header, but uses a 32-bit size field rather than the 16-bit size * field */ const size_t TCT3_LARGE_META_HEADER_SIZE = 8; /* offset of generic metaclass header from the start of an object's data */ const size_t TCT3_META_HEADER_OFS = TCT3_OBJ_INTERNHDR_OFS + TCT3_OBJ_INTERNHDR_SIZE; /* ------------------------------------------------------------------------ */ /* * tads-object metaclass object stream header sizes */ /* * tads-object header - each object with metaclass tads-object defines * this header * *. UINT2 superclass_count *. UINT2 property_count *. UINT2 object_flags */ const size_t TCT3_TADSOBJ_HEADER_SIZE = 6; /* offset of the tads-object header from the start of an object's data */ const size_t TCT3_TADSOBJ_HEADER_OFS = TCT3_META_HEADER_OFS + TCT3_META_HEADER_SIZE; /* * offset to the superclass table, which immediately follows the * tads-object header */ const size_t TCT3_TADSOBJ_SC_OFS = TCT3_TADSOBJ_HEADER_OFS + TCT3_TADSOBJ_HEADER_SIZE; /* * size of a property table entry * *. UINT2 property_ID *. DATAHOLDER value */ const size_t TCT3_TADSOBJ_PROP_SIZE = 2 + VMB_DATAHOLDER; /* * T3 object flags */ /* class flag - object is a class, not an instance */ #define TCT3_OBJFLG_CLASS 0x0001 /* ------------------------------------------------------------------------ */ /* * Metaclass List Entry. This list keeps track of the metaclasses that * the image file is dependent upon, and the dynamic link mapping * between metaclass ID in the image file and the universally unique * metaclass name. */ struct tc_meta_entry { /* next entry in the list */ tc_meta_entry *nxt; /* * metaclass symbol object, if present - we get the property list * from the metaclass symbol */ class CTcSymMetaclass *sym; /* external (universally unique) metaclass name */ char nm[1]; }; /* * Fixed System Metaclasses. The compiler must generate code for these * metaclasses directly, so it pre-loads the metaclass dependency table * with these metaclasses at initialization. Because these entries are * always loaded into the table in the same order, they have fixed table * indices that we can define as constants here. */ /* TADS Object Metaclass */ const int TCT3_METAID_TADSOBJ = 0; /* list metaclass */ const int TCT3_METAID_LIST = 1; /* dictionary metaclass */ const int TCT3_METAID_DICT = 2; /* grammar production metaclass */ const int TCT3_METAID_GRAMPROD = 3; /* vector metaclass */ const int TCT3_METAID_VECTOR = 4; /* anonymous function pointer */ const int TCT3_METAID_ANONFN = 5; /* intrinsic class modifiers */ const int TCT3_METAID_ICMOD = 6; /* lookup table */ const int TCT3_METAID_LOOKUP_TABLE = 7; /* * IMPORTANT!!! When adding new entries to this list of pre-defined * metaclasses, you must: * * - update the 'last' constant below * * - add the new entry to CTcGenTarg::load_image_file_meta_table() */ /* last metaclass ID - adjust when new entries are added */ const int TCT3_METAID_LAST = 7; /* ------------------------------------------------------------------------ */ /* * Function set dependency list entry */ struct tc_fnset_entry { /* next entry in the list */ tc_fnset_entry *nxt; /* external (universally unique) function set name */ char nm[1]; }; /* ------------------------------------------------------------------------ */ /* * Exception Table builder. This object keeps track of the entries in * an exception table under construction, so that the exception table * for a function can be written to the code stream after all of the * code in the function has been generated. */ class CTcT3ExcTable { public: CTcT3ExcTable(); ~CTcT3ExcTable() { /* if we've allocated a table, delete it */ if (table_ != 0) t3free(table_); } /* * Set the current function or method's start offset. The code * generator for the function body should set this to the code * stream offset of the start of the method header; this allows us * to calculate the offsets of protected code and 'catch' blocks. * * Important: this is the code stream offset (G_cs->get_ofs()), not * the final code pool address. We need only relative offsets, so * the code stream offset suffices (and is available much earlier in * the code generation process). */ void set_method_ofs(ulong ofs) { method_ofs_ = ofs; } /* * Add a 'catch' entry. The offsets are all code stream offsets * (G_cs->get_ofs() values). */ void add_catch(ulong protected_start_ofs, ulong protected_end_ofs, ulong exc_obj_id, ulong catch_block_ofs); /* * write our exception table to the code stream - writes to the G_cs * global code stream object */ void write_to_code_stream(); /* get the number of entries */ size_t get_entry_count() const { return exc_used_; } /* clear the exception table - remove all entries */ void clear_table() { exc_used_ = 0; } protected: /* the starting offset of this method header */ ulong method_ofs_; /* exception table */ struct CTcT3ExcEntry *table_; /* number of entries used/allocated in our table */ size_t exc_used_; size_t exc_alloced_; }; /* * Exception table entry */ struct CTcT3ExcEntry { /* start/end offset (from start of method header) of protected code */ ulong start_ofs; ulong end_ofs; /* object ID of exception class caught */ ulong exc_obj_id; /* 'catch' block starting offset (from start of method header) */ ulong catch_ofs; }; /* ------------------------------------------------------------------------ */ /* * Data Stream Page Layout Manager. This works with a CTcDataStream * object (such as the constant pool or the code pool) to divide the * stream into pages for the image file. */ class CTcStreamLayout { public: CTcStreamLayout() { /* we don't know anything about our layout yet */ page_size_ = 0; page_cnt_ = 0; } /* * Calculate my layout, given the maximum object size. This can be * called once the entire stream has been generated, hence the size * of the largest indivisible item in the stream is known. This * will apply all fixups throughout the stream. * * If this is the first stream for this layout, is_first is true. * If we're adding more pages, is_first is false, and max_len is * ignored (so the caller must ensure that the max_len provided on * laying out the first stream for this page set is adequate for all * streams added to this layout). */ void calc_layout(class CTcDataStream *ds, ulong max_len, int is_first); /* * Write the stream(s) to an image file. We'll write the pool * definition block and the pool pages. This cannot be called until * after calc_layout() has been called for all streams, because we * must apply all fixups throughout the entire image before we can * write out anything. */ void write_to_image(class CTcDataStream **ds_array, size_t ds_cnt, class CVmImageWriter *image_writer, int pool_id, uchar xor_mask); /* page size */ ulong page_size_; /* number of pages used */ size_t page_cnt_; }; /* ------------------------------------------------------------------------ */ /* * Debug line list page. We keep a linked list of these pages, and * allocate new entries out of the last page. We keep going until the * last page is filled up, then allocate a new page. */ const size_t TCT3_DEBUG_LINE_PAGE_SIZE = 1024; const size_t TCT3_DEBUG_LINE_REC_SIZE = 5; struct tct3_debug_line_page { /* next page in list */ tct3_debug_line_page *nxt; /* * Entries on this page (each entry is a debug line record offset in * the code stream). Each entry consists of one byte for the code * stream identifier (TCGEN_xxx_STREAM) and four bytes for a * portable UINT4 with the offset in the stream. */ uchar line_ofs[TCT3_DEBUG_LINE_PAGE_SIZE * TCT3_DEBUG_LINE_REC_SIZE]; }; /* ------------------------------------------------------------------------ */ /* * T3-specific code generator helper class. This class provides a set * of static functions that are useful for T3 code generation. */ class CTcGenTarg { public: /* initialize the code generator */ CTcGenTarg(); /* destroy the code generator object */ ~CTcGenTarg(); /* * Set the run-time metaclass dependency table index for a given * metaclass, identified by 'name' (a string of length 'len'). 'idx' * is the run-time metaclass dependency index. * * When we're operating as part of an interactive debugger, the image * loader must call this for each entry in the metaclass dependency * table loaded from the image file. This allows us to fix up our * internal notion of the metaclass indices so that we generate code * compatible with the actual loaded image file. * * The protocol is as follows: call start_image_file_meta_table(), then * call load_image_file_meta_table() on each entry in the table, then * call end_image_file_meta_table(). */ void start_image_file_meta_table(); void load_image_file_meta_table(const char *nm, size_t len, int idx); void end_image_file_meta_table(); /* * Allocate a new global property ID. */ tctarg_prop_id_t new_prop_id() { return next_prop_++; } /* * Allocate a new global object ID. */ tctarg_obj_id_t new_obj_id() { return next_obj_++; } /* * add a metaclass to the dependency table - returns the index of * the metaclass in the table */ int add_meta(const char *meta_extern_name, size_t len, class CTcSymMetaclass *sym); int add_meta(const char *nm, class CTcSymMetaclass *sym) { return add_meta(nm, strlen(nm), sym); } int add_meta(const char *nm) { return add_meta(nm, strlen(nm), 0); } /* * Find a metaclass entry, adding it if it's not already there. If * the metaclass is already defined, and it has an associated * symbol, we will not change the associated symbol - this will let * the caller detect that the metaclass has been previously defined * for a different symbol, which is usually an error. */ int find_or_add_meta(const char *nm, size_t len, class CTcSymMetaclass *sym); /* * Get the property ID for the given method table index in the given * metaclass. The metaclass ID is given as the internal metaclass ID * (without the "/version" suffix), not as the external class name. * Returns a parse node for the property, or null if it's not found. */ CTcPrsNode *get_metaclass_prop(const char *name, ushort idx) const; /* get a metaclass symbol by the metaclass's global identifier */ class CTcSymMetaclass *find_meta_sym(const char *nm, size_t len); /* * Find the metaclass table entry for a given global identifier. If * update_vsn is true, we'll update the entry stored in the table to * the given version number if the given name's version number is * higher than the one in the table. If we find an entry, we'll * fill in *entry_idx with the entry's index. */ tc_meta_entry *find_meta_entry(const char *nm, size_t len, int update_vsn, int *entry_idx); /* get/set the symbol for a given metaclass */ class CTcSymMetaclass *get_meta_sym(int meta_idx); void set_meta_sym(int meta_idx, class CTcSymMetaclass *sym); /* get the number of metaclasses */ int get_meta_cnt() const { return meta_cnt_; } /* get the external (universally unique) name for the given metaclass */ const char *get_meta_name(int idx) const; /* * Get the dependency table index for the given pre-defined metaclass, * specified by the TCT3_METAID_xxx value. */ int get_predef_meta_idx(int id) const { return predef_meta_idx_[id]; } /* * Add a function set to the dependency table - returns the index of * the function set in the table */ ushort add_fnset(const char *fnset_extern_name, size_t len); ushort add_fnset(const char *fnset_extern_name) { return add_fnset(fnset_extern_name, strlen(fnset_extern_name)); } /* get the name of a function set given its index */ const char *get_fnset_name(int idx) const; /* get the number of defined function sets */ int get_fnset_cnt() const { return fnset_cnt_; } /* * Notify the code generator that parsing is finished. This should * be called after parsing and before code generation begins. */ void parsing_done(); /* * Note a string value's length. This should be invoked during the * parsing phase for each constant string value. We'll keep track * of the largest constant data in the file, so that after parsing * is finished, we'll know the minimum size we need for each * constant pool page. This doesn't actually allocate any space in * the constant pool; this merely keeps track of the longest string * we'll eventually need to store. */ void note_str(size_t len); /* * Note number of elements in a constant list value. This is the * list equivalent of note_str(). */ void note_list(size_t element_count); /* * Note the length of a code block's byte code. This should be * invoked during code generation for each code block; we'll keep * track of the longest byte code block, so that after code * generation is complete, we'll know the minimum size we need for * each code pool page. */ void note_bytecode(ulong len); /* * Notify the code generator that we're replacing an object (via the * "replace" statement) at the given stream offset. We'll mark the * data in the stream as deleted so that we don't write it to the * image file. */ void notify_replace_object(ulong stream_ofs); /* * Write to an object file. The compiler calls this after all * parsing and code generation are completed to write an object * file, which can then be linked with other object files to create * an image file. */ void write_to_object_file(class CVmFile *object_fp, class CTcMake *make_obj); /* * Load an object file. Returns zero on success, non-zero on error. */ int load_object_file(CVmFile *fp, const textchar_t *fname); /* * Write the image file. The compiler calls this after all parsing * and code generation are completed to write an image file. We * must apply all fixups, assign the code and constant pool layouts, * and write the data to the image file. */ void write_to_image(class CVmFile *image_fp, uchar data_xor_mask, const char tool_data[4]); /* generate synthesized code during linking */ void build_synthesized_code(); /* generate code for a dictionary object */ void gen_code_for_dict(class CTcDictEntry *dict); /* generate code for a grammar production object */ void gen_code_for_gramprod(class CTcGramProdEntry *prod); /* get the maximum string/list/bytecode lengths */ size_t get_max_str_len() const { return max_str_len_; } size_t get_max_list_cnt() const { return max_list_cnt_; } size_t get_max_bytecode_len() const { return max_bytecode_len_; } /* * Add a debug line record. If we're in debug mode, this will clear * the peephole optimizer to ensure that the line record doesn't get * confused due to compression of opcodes. */ void add_line_rec(class CTcTokFileDesc *file, long linenum); /* write an opcode to the output stream */ void write_op(uchar opc); /* write a CALLPROP instruction */ void write_callprop(int argc, int varargs, vm_prop_id_t prop); /* * Determine if we can skip an opcode for peephole optimization. * We'll look at the previous opcode to determine if this opcode is * reachable, and we'll indicate that we should suppress the new * opcode if not. */ int can_skip_op(); /* * Add a string to the constant pool, and create a fixup for the * item for a reference from the given stream at the given offset. */ void add_const_str(const char *str, size_t len, class CTcDataStream *ds, ulong ofs); /* * Add a list to the constant pool, and create a fixup for the item * for a reference from the given stream at the given offset. */ void add_const_list(class CTPNList *lst, class CTcDataStream *ds, ulong ofs); /* * Write a constant value (in the compiler's internal * representation, a CTcConstVal structure) to a given buffer in T3 * image file DATA_HOLDER format. Write at a given offset, or at * the current write offset. */ void write_const_as_dh(class CTcDataStream *ds, ulong ofs, const class CTcConstVal *src); void write_const_as_dh(class CTcDataStream *ds, const class CTcConstVal *src); /* * Clear the peephole optimizer state. This must be invoked * whenever a jump label is defined. We can't combine an * instruction at a jump destination with anything previous: the * instruction at a jump destination must be generated as-is, rather * than being combined with the preceding instruction, since someone * could jump directly to it. */ void clear_peephole() { last_op_ = OPC_NOP; second_last_op_ = OPC_NOP; } /* get the last opcode we generated */ uchar get_last_op() const { return last_op_; } /* * Remove the last JMP instruction. This is used when we detect * that we just generated a JMP ahead to the very next instruction, * in which case we can eliminate the JMP, since it has no effect. */ void remove_last_jmp(); /* * Stack depth counting. While we're generating code for a code * block (a function or method), we'll keep track of our stack push * and pop operations, so that we can monitor the maximum stack * depth. In order for the stack depth to be calculable at compile * time, the code generator must take care that each individual * statement is stack-neutral (i.e, the stack comes out of each * statement at the same depth as when it entered the statement), so * that jumps, iterations, and other variables we can't analyze * statically can be ignored. */ /* * reset the stack depth counters - call this at the start * generating of each code block */ void reset_sp_depth() { sp_depth_ = max_sp_depth_ = 0; } /* * get the maximum stack depth for the current function - use this * when finished generating a code block to determine the maximum * stack space needed by the code block */ int get_max_sp_depth() const { return max_sp_depth_; } /* get the current stack depth */ int get_sp_depth() const { return sp_depth_; } /* record a push - increments the current stack depth */ void note_push() { note_push(1); } /* record a push of a given number of stack elements */ void note_push(int cnt) { sp_depth_ += cnt; if (sp_depth_ > max_sp_depth_) max_sp_depth_ = sp_depth_; } /* record a pop - decrements the current stack depth */ void note_pop() { note_pop(1); } /* record a pop of a given number of stack elements */ void note_pop(int cnt) { sp_depth_ -= cnt; } /* record a full stack reset back to function entry conditions */ void note_rst() { sp_depth_ = 0; } /* * do post-call cleanup: generate a named argument table pointer if * needed, remove the named arguments from the stack */ void post_call_cleanup(const struct CTcNamedArgs *named_args); /* * Open/close a method/function. "Open" generates a placeholder method * header and sets up our generator globals to prepare for a new * method. "Close" goes back and fills in the final method header * based on the code generated since "Open". */ void open_method(class CTcCodeStream *stream, class CTcSymbol *fixup_owner_sym, struct CTcAbsFixup **fixup_list_head, class CTPNCodeBody *code_body, class CTcPrsSymtab *goto_tab, int argc, int opt_argc, int varargs, int is_constructor, int is_op_overload, int is_self_available, struct tct3_method_gen_ctx *ctx); void close_method(int local_cnt, class CTcPrsSymtab *local_symtab, class CTcTokFileDesc *end_desc, long end_linenum, struct tct3_method_gen_ctx *ctx, struct CTcNamedArgTab *named_arg_tab_head); void close_method_cleanup(struct tct3_method_gen_ctx *ctx); /* * Generate a TadsObject header to a data stream */ void open_tadsobj(struct tct3_tadsobj_ctx *ctx, CTcDataStream *stream, vm_obj_id_t obj_id, int sc_cnt, int prop_cnt, unsigned int internal_flags, unsigned int vm_flags); void close_tadsobj(struct tct3_tadsobj_ctx *ctx); /* * Linker support: ensure that the given intrinsic class has a modifier * object. If there's no modifier, we'll create one and add code for * it to the intrinsic class modifier stream. */ void linker_ensure_mod_obj(CTcSymMetaclass *mc_sym); void linker_ensure_mod_obj(const char *name, size_t len); /* * get my exception table object - this is used to construct a * method's exception table during code generation, and to write the * table to the code stream */ CTcT3ExcTable *get_exc_table() { return &exc_table_; } /* determine if we're compiling a constructor */ int is_in_constructor() const { return in_constructor_; } void set_in_constructor(int f) { in_constructor_ = f; } /* determine if we're compiling an operator overload method */ int is_in_op_overload() const { return in_op_overload_; } void set_in_op_overload(int f) { in_op_overload_ = f; } /* * set the method offset - the code body object calls this when it's * about to start generating code to let us know the offset of the * current method */ void set_method_ofs(ulong ofs); /* * Add a debug line table to our list. We keep track of all of the * debug line record tables in the program, so that we can store the * list in the object file. We need this information in the object * file because each debug line record table in an object file must * be fixed up at link time after loading the object file. */ void add_debug_line_table(ulong ofs); /* * Set dynamic (run-time) compilation mode. This mode must be used for * code compiled during program execution, such as for an "eval()" * facility. In dynamic compilation, all pool addresses are already * resolved from the loaded program, and we can't add anything to any * of the constant or code pools. */ void set_dyn_eval() { eval_for_dyn_ = TRUE; } /* * Set debug evaluation mode. If 'speculative' is true, it means * that we're generating an expression for speculative evaluation, * in which case the evaluation must fail if it would have any side * effects (such as calling a method, displaying a string, or * assigning a value). 'stack_level' is the enclosing stack level * at which to evaluate the expression; 0 is the last active * non-debug stack level, 1 is the first enclosing level, and so on. */ void set_debug_eval(int speculative, int level) { /* note that we're evaluating for the debugger */ eval_for_debug_ = TRUE; /* this is a special type of run-time dynamic compilation */ eval_for_dyn_ = TRUE; /* note the speculative mode */ speculative_ = speculative; /* note the stack level */ debug_stack_level_ = level; } /* set normal evaluation mode */ void set_normal_eval() { eval_for_debug_ = eval_for_dyn_ = FALSE; } /* determine if we're in dynamic/debugger evaluation mode */ int is_eval_for_dyn() const { return eval_for_dyn_; } int is_eval_for_debug() const { return eval_for_debug_; } /* determine if we're in speculative evaluation mode */ int is_speculative() const { return eval_for_debug_ && speculative_; } /* get the active debugger stack level */ int get_debug_stack_level() const { return debug_stack_level_; } /* * Generate a BigNumber object, returning the object ID. The input * text gives the source representation of the number. */ vm_obj_id_t gen_bignum_obj(const char *txt, size_t len, int promoted); /* generate a RexPattern object */ vm_obj_id_t gen_rexpat_obj(const char *txt, size_t len); private: /* eliminate jump-to-jump sequences */ void remove_jumps_to_jumps(class CTcCodeStream *str, ulong start_ofs); /* * Calculate pool layouts. This is called after all code generation * is completed; at this point, the T3 code generator can determine * how the code pages will be laid out, since we now know the size * of the largest single chunk of code. * * We'll fill in *first_static_page with the page number in the code * pool of the first page of code containing static initializers. * We group all of the static initializer code together at the end * of the code pool to allow the pre-initialization re-writer to * omit all of the static code pages from the final image file. */ void calc_pool_layouts(size_t *first_static_page); /* * Write a TADS object stream to the image file. This routine will * fix up the property table in each object to put the table in * sorted order. */ void write_tads_objects_to_image(class CTcDataStream *obj_stream, class CVmImageWriter *image_writer, int metaclass_idx); /* * write the TADS objects of one particular type - transient or * persistent - to the image file */ void write_tads_objects_to_image(CTcDataStream *os, CVmImageWriter *image_writer, int meta_idx, int trans); /* * Write an object stream of non-TADS objects to the image file. * This writes the objects as-is, without looking into their * contents at all. */ void write_nontads_objs_to_image(class CTcDataStream *obj_stream, class CVmImageWriter *image_writer, int metaclass_idx, int large_obs); /* * Sort an object's property table, and compress the table to remove * deleted properties. Returns the final size of the object data to * write to the image file, which could differ from the original * size, because we might remove property slots from the property * data. If we do change the size of the property table, we'll * update the stream data to reflect the new property count and * metaclass data size. */ size_t sort_object_prop_table(class CTcDataStream *obj_stream, ulong start_ofs); /* write the function-set dependency table to an object file */ void write_funcdep_to_object_file(class CVmFile *fp); /* write the metaclass dependency table to an object file */ void write_metadep_to_object_file(class CVmFile *fp); /* load the function set dependency table from an object file */ void load_funcdep_from_object_file(class CVmFile *fp, const textchar_t *fname); /* load the metaclass dependency table from an object file */ void load_metadep_from_object_file(class CVmFile *fp, const textchar_t *fname); /* look up a required or optional property by name */ vm_prop_id_t look_up_prop(const char *propname, int required, int err_if_undef, int err_if_not_prop); /* build the IntrinsicClass instances */ void build_intrinsic_class_objs(CTcDataStream *str); /* build the source file line maps */ void build_source_line_maps(); /* build the local symbol records */ void build_local_symbol_records(class CTcCodeStream *cs, class CVmHashTable *tab); /* build the multi-method initializer list */ void build_multimethod_initializers(); /* symbol table enumerator callback for the multi-method initializers */ static void multimethod_init_cb(void *ctx, CTcSymbol *sym); /* symbol table enumerator callback for the multi-method stubs */ static void multimethod_stub_cb(void *ctx, CTcSymbol *sym); /* write an overloaded operator property export */ void write_op_export(CVmImageWriter *image_writer, class CTcSymProp *prop); /* write the static initializer list to the image file */ void write_static_init_list(CVmImageWriter *image_writer, ulong main_cs_size); /* write the list of source file descriptors to an image file */ void write_sources_to_image(class CVmImageWriter *image_writer); /* write the global symbol table to an object file */ void write_global_symbols_to_image(class CVmImageWriter *image_writer); /* write the method header list to the image file */ void write_method_list_to_image(class CVmImageWriter *image_writer); /* write macro definitions to the image file */ void write_macros_to_image(class CVmImageWriter *image_writer); /* write the list of source file descriptors to an object file */ void write_sources_to_object_file(class CVmFile *fp); /* * read the list of sources from an object file, adding the sources * to the tokenizer's internal list */ void read_sources_from_object_file(class CVmFile *fp); /* load debug records from an object file */ void load_debug_records_from_object_file(class CVmFile *fp, const textchar_t *fname, ulong main_cs_start_ofs, ulong static_cs_start_ofs); /* fix up a debug line record table for the object file */ void fix_up_debug_line_table(class CTcCodeStream *cs, ulong line_table_ofs, int first_filedesc); /* hash table enumerator callback - generate dictionary code */ static void enum_dict_gen_cb(void *ctx, class CVmHashEntry *entry); /* most recent opcodes we've written, for peephole optimization */ uchar last_op_; uchar second_last_op_; /* maximum constant string length seen during parsing */ size_t max_str_len_; /* maximum list element count seen during parsing */ size_t max_list_cnt_; /* maximum byte code block generated during code generation */ size_t max_bytecode_len_; /* head and tail of metaclass list */ tc_meta_entry *meta_head_; tc_meta_entry *meta_tail_; /* number of entries in metaclass list so far */ int meta_cnt_; /* head and tail of function set list */ tc_fnset_entry *fnset_head_; tc_fnset_entry *fnset_tail_; /* number of function sets in the list */ int fnset_cnt_; /* next available global property ID */ vm_prop_id_t next_prop_; /* next available global object ID */ vm_obj_id_t next_obj_; /* current stack depth */ int sp_depth_; /* maximum stack depth in current code block */ int max_sp_depth_; /* exception table for current code block */ CTcT3ExcTable exc_table_; /* constant pool layout manager */ CTcStreamLayout const_layout_; /* code pool layout manager */ CTcStreamLayout code_layout_; /* first/last page of debug line list */ tct3_debug_line_page *debug_line_head_; tct3_debug_line_page *debug_line_tail_; /* total number of debug line list entries used so far */ ulong debug_line_cnt_; /* * Object ID of the multi-method static initializer object. This will * be set by build_multimethod_initializers() if we end up creating any * registration code. */ vm_obj_id_t mminit_obj_; /* * property sorting buffer - this is space we allocate to copy an * object's property table for sorting */ char *sort_buf_; size_t sort_buf_size_; /* flag: we're currently compiling a constructor */ uint in_constructor_ : 1; /* flag: we're currently compiling an operator overload method */ uint in_op_overload_ : 1; /* flag: we're generating code for dynamic (run-time) compilation */ uint eval_for_dyn_ : 1; /* flag: we're generating an expression for debugger use */ uint eval_for_debug_ : 1; /* flag: we're generating a debugger speculative evaluation expression */ uint speculative_ : 1; /* * debugger active stack context level - valid when eval_for_debug_ * is true */ int debug_stack_level_; /* * String interning hash table. This is a table of short string * literals that we've generated into the data segment. Whenever we * generate a new string literal, we'll check this table to see if * we've previously stored the identical string. If so, we'll simply * use the original string rather than generating a new copy. This can * reduce the size of the object file by eliminating duplication of * common short strings. * * Note that string interning has the potential to change run-time * semantics in some other languages, but not in TADS. There are two * common issues in other languages. First, in C/C++, a string * constant is technically a writable buffer. This means that two * distinct string literals in the source code really need to be * treated as distinct memory locations, even if they have identical * contents, because the program might write to one buffer and expect * the other to remain unchanged. This doesn't apply to TADS because * string literals are strictly read-only. Second, strings in many * languages are objects with reference semantics for comparisons; two * distinct source-code strings must therefore have distinct reference * identities. String comparisons in TADS are always by value, so * there's no need for distinct references if two strings have * identical contents. */ class CVmHashTable *strtab_; /* * Static table of image file metaclass dependency indices for the * pre-defined metaclasses (i.e., the metaclasses that the compiler * specifically generates code for). When we compile a program, these * are determined by the compiler simply according to the order in * which it builds its own initial table of the known metaclasses. * When we're debugging, we need to get these values from the image * file. * * This is a simple translation table - we translate from our internal * index (a TCT3_METAID_xxx value) to the corresponding dependency * index in the actual image file. So, we just need as many of these * as there TCT3_METAID_xxx indices. We never need these for any * additional metaclasses that might exist - we only care about the * ones that the compiler specifically knows about in advance. */ int predef_meta_idx_[TCT3_METAID_LAST + 1]; }; /* ------------------------------------------------------------------------ */ /* * Method generator context */ struct tct3_method_gen_ctx { /* output code stream */ class CTcCodeStream *stream; /* stream anchor */ struct CTcStreamAnchor *anchor; /* method header offset in stream */ ulong method_ofs; /* starting and ending code offset in stream */ ulong code_start_ofs; ulong code_end_ofs; /* enclosing code body */ class CTPNCodeBody *old_code_body; }; /* ------------------------------------------------------------------------ */ /* * TadsObject header writer context */ struct tct3_tadsobj_ctx { /* start of object header in data stream */ ulong obj_ofs; /* data stream to which we're writing the object */ CTcDataStream *stream; }; /* ------------------------------------------------------------------------ */ /* * Named arguments information. This keeps track of the named arguments * for a generated call. */ struct CTcNamedArgs { /* number of named arguments */ int cnt; /* argument list */ class CTPNArglist *args; }; #endif /* TCT3_H */ qtads-2.1.7/tads3/tct3_d.cpp000066400000000000000000000010201265017072300155330ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3_d.cpp - stubs for functions not needed in debugger builds Function Notes Modified 02/02/00 MJRoberts - Creation */ #include "tctarg.h" #include "tcprs.h" void CTcGenTarg::add_debug_line_table(ulong) { } void CTPNObjProp::check_locals() { } qtads-2.1.7/tads3/tct3base.h000066400000000000000000000402511265017072300155410ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/TCT3BASE.H,v 1.3 1999/07/11 00:46:57 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3base.h - base parse node classes for T3 code generator Function Notes Modified 05/10/99 MJRoberts - Creation */ #ifndef TCT3BASE_H #define TCT3BASE_H #include "tcprs.h" /* include the target-independent base classes */ #include "tcpnbase.h" /* ------------------------------------------------------------------------ */ /* * Base parse node class */ class CTcPrsNode: public CTcPrsNodeBase { public: /* * Generate code to assign into the expression as an lvalue. By * default, we'll throw an error; a default node should never be called * upon to generate code as an assignment target, because the * impossibility of this condition should be detected during parsing. * Each class that overrides check_lvalue() to return true must * override this to generate code for an assignment. * * 'typ' specifies the type of assignment - this specifies the * assignment operator being generated. * * 'phase' indicates what part of the assignment we're generating. For * a simple assignment, this routine will be called only once, with * phase set to 1. For a compound assignment, this routine will be * called twice. The first call is phase 1. This can do one of two * things: * * - Generate the entire assignment code, and return TRUE to indicate * that the assignment has been handled. The caller will generate no * more code. This should be used when the particular combination of * assignment type and lvalue can be specially optimized in the * bytecode. * * - Generate code to evaluate the lvalue's value, and prepare the * stack for eventual assignment to the lvalue, and return FALSE. The * caller in this case will then generate generic code to combine the * lvalue and rvalue. For example, with operator '+=', the caller will * generate the ADD instruction to compute 'lvalue + rvalue'. The * caller will finally call the routine again with phase set to 2. * * During phase 2, the routine must generate code to assign the rvalue * at top of stack to the lvalue. * * Returns true if the assignment type was handled, false if not. * Simple assignment must always be handled, but composite assignments * (such as "+=" or "*=") can refused. If the assignment isn't * handled, the caller must perform a generic calculation to compute * the result of the assignment (for example, for "+=", the caller must * generate code compute the sum of the right-hand side and the * original value of the left-hand side), then call gen_code_asi() * again to generate a simple assignment. * * In general, gen_code_asi() must generate code for complex * assignments itself only when it can take advantage of special * opcodes that would result in more efficient generated code. When no * special opcodes are available for the assignment, there's no need * for special coding here. * * If 'ignore_error' is true, this should not generate an error if this * node is not a suitable lvalue, but should simply return false. * * 'explicit' is true if this is an explicit assignment by user code, * false if it's an assignment generated automatically by the compiler. * Implicit assignments don't count for the purposes of unused value * warnings. These warnings are primarily to alert the user that they * could delete an unnecessary assignment from their code, or * conversely that they might have forgotten to do something with the * value that the assignment would suggest they intended to do, but * with a compiler-generated assignment there's nothing for the user to * delete to fix the warning. * * 'ctx' is the address of a 'void *' variable (a generic pointer). * This is for the callee's use during a two-phase compound operator * assignment, for passing itself context information between the * phases. On phase 1, the callee can allocate a structure and store * it at *ctx. On phase 2, the callee can then look at this structure * to retrieve information saved from the first phase. The callee is * responsible for deleting the structure on the second phase. For * single-phase TC_ASI_SIMPLE calls, the caller can simply pass a null * pointer for ctx, so the callee must not attempt to store anything * here during a simple assignment call. */ virtual int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, class CTcPrsNode *rhs, int ignore_error, int xplicit, void **ctx); /* * Generate code to take the address of the expression. By default, * we'll throw an internal error; a default node should never be * called upon to generate code to take its address, because we * shouldn't be able to parse such a thing. Each class that * overrides has_addr() to return true must override this to * generate address code. */ virtual void gen_code_addr(); /* * Generate an if-then-else condition test. This is called on the * controlling expression of an if-then-else, and can also be called on * subexpressions of short-circuit operators (|| and &&). The purpose * of this type of code generation is to avoid pushing the result of * the controlling expression onto the stack when this can be avoided, * and generate a jump to the appropriate if-else branch directly. * * Either then_label or else_label will be non-null; the one that's * null directly follows the condition test, so we are simply to fall * through to that code rather than jump to it explicitly. */ virtual void gen_code_cond(struct CTcCodeLabel *then_label, struct CTcCodeLabel *else_label); /* * Generate code to call the expression as a function or method. * The caller will already have generated code to push the argument * list; this routine only needs to generate code to make the call. * * By default, we'll assume that the result of evaluating the * expression is a method or function pointer, so we'll generate a * call-indirect instruction to call the result of evaluating the * expression. */ virtual void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* * Generate code to apply operator 'new' to the expression. By * default, 'new' cannot be applied to an expression; nodes that * allow operator 'new' must override this. */ virtual void gen_code_new(int discard, int argc, int varargs, struct CTcNamedArgs *named_args, int from_call, int is_transient); /* * Generate code to evaluate a member expression on this object * expression, calling the property expression given. By default, * we'll evaluate our own expression to yield the object value, then * get the property expression (either as a constant or by * generating code to yield a property pointer), then we'll invoke * that code. Nodes should override this where more specific * instructions can be generated. * * If 'prop_is_expr' is true, the property expression (prop_expr) is * a parenthesized expression; otherwise, it's a simple property * symbol. (We need this information because a symbol enclosed in * parentheses would be otherwise indistinguishable from a plain * symbol, but the two syntax cases differ in their behavior: a * plain symbol is always a property name, whereas a symbol in * parentheses can be a local variable.) */ virtual void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* * Generate code for an object on the left side of a '.' expression. * If possible, return the constant object rather than generating * code. If the expression refers to "self", set (*is_self) to true * and return VM_INVALID_OBJ; otherwise, if the expression refers to * a constant object reference, set (*is_self) to false and return * the constant object value; otherwise, generate code for the * expression, set (*is_self) to false, and return VM_INVALID_OBJ to * indicate that the value must be obtained from the generated code * at run-time. * * By default, we'll use generated code to get the value. */ virtual vm_obj_id_t gen_code_obj_predot(int *is_self) { /* generate the expression */ gen_code(FALSE, FALSE); /* tell the caller that the value is not a compile-time constant */ *is_self = FALSE; return VM_INVALID_OBJ; } /* * generic generation for a member expression after the left side * has been generated */ static void s_gen_member_rhs(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* * Get the property ID of this expression. If the property ID is * available as a constant value, this doesn't generate any code and * simply returns the constant property ID value. If this * expression requires run-time evaluation, we'll generate code for * the expression and return VM_INVALID_PROP to indicate that a * constant property ID is not available. * * If 'check_only' is true, this routine should only check to see * whether a constant property value is available and return the * appropriate indication, but should not generate any code in any * case. Errors should also be suppressed in this case, because the * routine will always be called again to perform the actual * generation, at which point any errors can be logged. * * If 'is_expr' is true, it means that this property was given as an * expression by being explicitly enclosed in parentheses. If * 'is_expr' is false, it means that the property was given as a * simple symbol name. For cases where the property expression is a * symbol, this distinction is important because we must resolve an * unparenthesized symbol as a property name, even if it's hidden by * a local variable. */ virtual vm_prop_id_t gen_code_propid(int check_only, int is_expr); /* * generate a jump-ahead instruction, returning a new label which * serves as the jump destination */ static struct CTcCodeLabel *gen_jump_ahead(uchar opc); /* define the position of a code label */ static void def_label_pos(struct CTcCodeLabel *lbl); /* allocate a new label at the current write position */ static struct CTcCodeLabel *new_label_here(); }; /* ------------------------------------------------------------------------ */ /* * Basic T3-specific symbol class */ class CTcSymbol: public CTcSymbolBase { public: CTcSymbol(const char *str, size_t len, int copy, tc_symtype_t typ) : CTcSymbolBase(str, len, copy, typ) { } /* * get the object value for the symbol, if the symbol has an object * value; returns VM_INVALID_OBJ if the symbol is not an object */ virtual vm_obj_id_t get_val_obj() const { return VM_INVALID_OBJ; } /* generate code to evaluate the symbol */ virtual void gen_code(int discard) = 0; /* * Generate code to assign to the symbol. By default, we'll generate * an error indicating that the symbol cannot be assigned into. As * with CTcPrsNode::gen_code_asi(), this returns true if the assignment * was generated, false if the caller must generate a generic * assignment; simple assignment must always return true. * * If 'rhs' is null, the caller has already generated the value to be * assigned, so this code doesn't need to do that. Otherwise, this * code must generate the rvalue code. The reason that 'rhs' is passed * down at all is that we can sometimes optimize the type of opcode we * generate according to the type of value being assigned, especially * when a constant value is being assigned. * * If 'ignore_error' is true, this should not log an error if the value * cannot be assigned. * * 'xplicit' is true if this is an explicit assignment by user code, * false if it's an assignment generated automatically by the compiler. * Implicit assignments don't count for the purposes of unused value * warnings. These warnings are primarily to alert the user that they * could delete an unnecessary assignment from their code, or * conversely that they might have forgotten to do something with the * value that the assignment would suggest they intended to do, but * with a compiler-generated assignment there's nothing for the user to * delete to fix the warning. */ virtual int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, class CTcPrsNode *rhs, int ignore_error, int xplicit, void **ctx); /* * Generate code for taking the address of the symbol. By default, * we'll generate an error indicating that the symbol's address * cannot be taken. */ virtual void gen_code_addr(); /* * Generate code to call the symbol. By default, we can't call a * symbol. */ virtual void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* * generate code for operator 'new' applied to the symbol, with the * given number of arguments; by default, we can't generate any such * code */ virtual void gen_code_new(int discard, int argc, int varargs, struct CTcNamedArgs *named_args, int is_transient); /* evaluate a property ID */ virtual vm_prop_id_t gen_code_propid(int check_only, int is_expr); /* generate code for a member expression */ virtual void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ virtual vm_obj_id_t gen_code_obj_predot(int *is_self); /* * Write the symbol to a local frame debugging record in the code * stream. Returns true if we wrote the symbol, false if not. By * default, we'll write nothing and return false, since most symbols * aren't used in local symbol tables. */ virtual int write_to_debug_frame(int test_only) { return FALSE; } /* * Write the symbol to the global symbol table in the debugging * records in an image file. Returns true if we wrote the symbol, * false if not. By default, we'll write nothing and return false, * since by default symbols don't go in the image file's global * symbol table. */ virtual int write_to_image_file_global(class CVmImageWriter *) { return FALSE; } }; #endif /* TCT3BASE_H */ qtads-2.1.7/tads3/tct3drv.h000066400000000000000000002054101265017072300154220ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/TCT3DRV.H,v 1.4 1999/07/11 00:46:57 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3drv.h - derived final T3-specific parse node classes Function Notes Modified 05/10/99 MJRoberts - Creation */ #ifndef TCT3DRV_H #define TCT3DRV_H #include /* include our T3-specific intermediate classes */ #include "tct3int.h" /* include the target-independent derived classes */ #include "tcpndrv.h" /* ------------------------------------------------------------------------ */ /* * "self" */ class CTPNSelf: public CTPNSelfBase { public: /* generate code */ void gen_code(int discard, int for_condition); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self); }; /* ------------------------------------------------------------------------ */ /* * "replaced" */ class CTPNReplaced: public CTPNReplacedBase { public: /* generate code */ void gen_code(int discard, int for_condition); /* generate a function call expression */ void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); }; /* ------------------------------------------------------------------------ */ /* * "targetprop" */ class CTPNTargetprop: public CTPNTargetpropBase { public: /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * "targetobj" */ class CTPNTargetobj: public CTPNTargetobjBase { public: /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * "definingobj" */ class CTPNDefiningobj: public CTPNDefiningobjBase { public: /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * "invokee" */ class CTPNInvokee: public CTPNInvokeeBase { public: /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * "inherited" */ class CTPNInh: public CTPNInhBase { public: /* generate code */ void gen_code(int discard, int for_condition); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); protected: /* generate a multi-method inherited() call */ void gen_code_mminh(CTcSymFunc *func, int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); }; /* * "inherited" with explicit superclass */ class CTPNInhClass: public CTPNInhClassBase { public: CTPNInhClass(const char *sym, size_t len) : CTPNInhClassBase(sym, len) { } /* generate code */ void gen_code(int discard, int for_condition); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); }; /* * "delegated" */ class CTPNDelegated: public CTPNDelegatedBase { public: CTPNDelegated(CTcPrsNode *delegatee) : CTPNDelegatedBase(delegatee) { } /* generate code */ void gen_code(int discard, int for_condition); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); }; /* ------------------------------------------------------------------------ */ /* * "argcount" node */ class CTPNArgc: public CTPNArgcBase { public: void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * constant node */ class CTPNConst: public CTPNConstBase { public: CTPNConst(const CTcConstVal *val) : CTPNConstBase(val) { } /* generate code for the constant */ void gen_code(int discard, int for_condition); /* generate code for operator 'new' applied to this expression */ void gen_code_new(int discard, int argc, int varargs, struct CTcNamedArgs *named_args, int from_call, int is_transient); /* generate code for assigning to this expression */ int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); /* evaluate a property ID */ vm_prop_id_t gen_code_propid(int check_only, int is_expr); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* generate a function call expression */ void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self); /* generate code to push an integer constant */ static void s_gen_code_int(long intval); /* generate code to push a string constant */ static void s_gen_code_str(const CTcConstVal *val); static void s_gen_code_str(const char *str, size_t len); /* generate code to push a symbol's name as a string constant */ static void s_gen_code_str(const class CTcSymbol *sym); /* * generate code to push a string, using the correct code for the * current compilation mode (static vs dynamic) */ static void s_gen_code_str_by_mode(const class CTcSymbol *sym); }; /* * Debugger constant node */ class CTPNDebugConst: public CTPNConst { public: CTPNDebugConst(CTcConstVal *val) : CTPNConst(val) { } /* debugger constants aren't actual constants - they require code gen */ CTcConstVal *get_const_val() { return 0; } /* generate code for the constant */ void gen_code(int discard, int for_condition); /* generate a constant string */ static void s_gen_code_str(const char *str, size_t len); }; /* ------------------------------------------------------------------------ */ /* * Unary Operators */ /* bitwise NOT */ CTPNUnary_def(CTPNBNot); /* arithmetic positive */ CTPNUnary_def(CTPNPos); /* arithmetic negative */ CTPNUnary_def(CTPNNeg); /* pre-increment */ CTPNUnary_side_def(CTPNPreInc); void CTPNPreInc_gen_code(class CTcPrsNode *sub, int discard); /* pre-decrement */ CTPNUnary_side_def(CTPNPreDec); /* post-increment */ CTPNUnary_side_def(CTPNPostInc); /* post-decrement */ CTPNUnary_side_def(CTPNPostDec); /* delete */ CTPNUnary_side_def(CTPNDelete); /* boolean-ize */ CTPNUnary_def(CTPNBoolize); /* ------------------------------------------------------------------------ */ /* * NEW operator */ class CTPNNew: public CTPNUnary { public: CTPNNew(class CTcPrsNode *sub, int is_transient) : CTPNUnary(sub) { /* remember 'transient' status */ transient_ = is_transient; } /* generate code */ void gen_code(int discard, int for_condition); /* note that we have side effects */ virtual int has_side_effects() const { return TRUE; } protected: /* flag: it's a 'new transient' operator */ int transient_; }; /* ------------------------------------------------------------------------ */ /* * NOT operator */ class CTPNNot: public CTPNNotBase { public: CTPNNot(CTcPrsNode *sub) : CTPNNotBase(sub) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Binary Operators */ class CTPNComma: public CTPNCommaBase { public: CTPNComma(class CTcPrsNode *lhs, class CTcPrsNode *rhs) : CTPNCommaBase(lhs, rhs) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* bitwise OR */ CTPNBin_def(CTPNBOr); /* bitwise AND */ CTPNBin_def(CTPNBAnd); /* bitwise XOR */ CTPNBin_def(CTPNBXor); /* greater than */ CTPNBin_cmp_def(CTPNGt); /* greater or equal */ CTPNBin_cmp_def(CTPNGe); /* less than */ CTPNBin_cmp_def(CTPNLt); /* less or equal */ CTPNBin_cmp_def(CTPNLe); /* bit shift left */ CTPNBin_def(CTPNShl); /* arithmetic shift right */ CTPNBin_def(CTPNAShr); /* logical shift right */ CTPNBin_def(CTPNLShr); /* multiply */ CTPNBin_def(CTPNMul); /* divide */ CTPNBin_def(CTPNDiv); /* modulo */ CTPNBin_def(CTPNMod); /* ------------------------------------------------------------------------ */ /* * Addition */ class CTPNAdd: public CTPNAddBase { public: CTPNAdd(class CTcPrsNode *left, class CTcPrsNode *right) : CTPNAddBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Subtraction */ class CTPNSub: public CTPNSubBase { public: CTPNSub(class CTcPrsNode *left, class CTcPrsNode *right) : CTPNSubBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Equality Comparison */ class CTPNEq: public CTPNEqBase { public: CTPNEq(class CTcPrsNode *left, class CTcPrsNode *right) : CTPNEqBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Inequality Comparison */ class CTPNNe: public CTPNNeBase { public: CTPNNe(class CTcPrsNode *left, class CTcPrsNode *right) : CTPNNeBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'is in' */ class CTPNIsIn: public CTPNIsInBase { public: CTPNIsIn(class CTcPrsNode *left, class CTPNArglist *right) : CTPNIsInBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'not in' */ class CTPNNotIn: public CTPNNotInBase { public: CTPNNotIn(class CTcPrsNode *left, class CTPNArglist *right) : CTPNNotInBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Logical AND (short-circuit logic) */ class CTPNAnd: public CTPNAndBase { public: CTPNAnd(class CTcPrsNode *left, class CTcPrsNode *right) : CTPNAndBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); /* generate a condition */ virtual void gen_code_cond(struct CTcCodeLabel *then_label, struct CTcCodeLabel *else_label); }; /* ------------------------------------------------------------------------ */ /* * Logical OR (short-circuit logic) */ class CTPNOr: public CTPNOrBase { public: CTPNOr(class CTcPrsNode *left, class CTcPrsNode *right) : CTPNOrBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); /* generate a condition */ virtual void gen_code_cond(struct CTcCodeLabel *then_label, struct CTcCodeLabel *else_label); }; /* ------------------------------------------------------------------------ */ /* * Assignment Operators */ /* simple assignment */ class CTPNAsi: public CTPNAsiBase { public: CTPNAsi(class CTcPrsNode *left, class CTcPrsNode *right) : CTPNAsiBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* add and assign */ CTPNBin_side_def(CTPNAddAsi); void CTPNAddAsi_gen_code(class CTcPrsNode *left, class CTcPrsNode *right, int discard); /* subtract and assign */ CTPNBin_side_def(CTPNSubAsi); /* multiple and assign */ CTPNBin_side_def(CTPNMulAsi); /* divide and assign */ CTPNBin_side_def(CTPNDivAsi); /* modulo and assign */ CTPNBin_side_def(CTPNModAsi); /* bitwise AND and assign */ CTPNBin_side_def(CTPNBAndAsi); /* bitwise OR and assign */ CTPNBin_side_def(CTPNBOrAsi); /* bitwise XOR and assign */ CTPNBin_side_def(CTPNBXorAsi); /* bit shift left and assign */ CTPNBin_side_def(CTPNShlAsi); /* arithmetic shift right and assign */ CTPNBin_side_def(CTPNAShrAsi); /* logical shift right and assign */ CTPNBin_side_def(CTPNLShrAsi); /* ------------------------------------------------------------------------ */ /* * Array/list Subscript */ class CTPNSubscript: public CTPNSubscriptBase { public: CTPNSubscript(CTcPrsNode *lhs, CTcPrsNode *rhs) : CTPNSubscriptBase(lhs, rhs) { } /* generate code */ void gen_code(int discard, int for_condition); /* generate code to assign a value to the symbol */ int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); }; /* ------------------------------------------------------------------------ */ /* * Address-of Operator */ class CTPNAddr: public CTPNAddrBase { public: CTPNAddr(CTcPrsNode *sub) : CTPNAddrBase(sub) { } /* generate code */ void gen_code(int discard, int for_condition) { /* * if we're not discarding the value, tell the subnode to * generate its address; if the value is being discarded, do * nothing, since taking an address has no side effects */ if (!discard) sub_->gen_code_addr(); } /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args) { /* * let the subnode do the work, since "&obj" is the same as * "obj" in any context */ sub_->gen_code_member(discard, prop_expr, prop_is_expr, argc, varargs, named_args); } /* evaluate a property ID */ vm_prop_id_t gen_code_propid(int check_only, int is_expr) { /* * let the subexpression handle it, so that we treat "x.&prop" * the same as "x.prop" */ return sub_->gen_code_propid(check_only, is_expr); } /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self) { /* * let the subexpression handle it, since "x.&prop" is the same * as "x.prop" */ return sub_->gen_code_obj_predot(is_self); } }; /* ------------------------------------------------------------------------ */ /* * If-nil operator ?? */ class CTPNIfnil: public CTPNIfnilBase { public: CTPNIfnil(class CTcPrsNode *first, class CTcPrsNode *second) : CTPNIfnilBase(first, second) { } /* generate code for the conditional */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Conditional Operator */ class CTPNIf: public CTPNIfBase { public: CTPNIf(class CTcPrsNode *first, class CTcPrsNode *second, class CTcPrsNode *third) : CTPNIfBase(first, second, third) { } /* generate code for the conditional */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Symbol node */ class CTPNSym: public CTPNSymBase { public: CTPNSym(const char *sym, size_t len) : CTPNSymBase(sym, len) { } /* generate code for the symbol evaluated as an rvalue */ void gen_code(int discard, int for_condition); /* generate code to assign a value to the symbol */ int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); /* generate code to take the address of the symbol */ void gen_code_addr(); /* generate code to call the symbol */ void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* generate code for operator 'new' applied to this expression */ void gen_code_new(int discard, int argc, int varargs, struct CTcNamedArgs *named_args, int from_call, int is_transient); /* evaluate a property ID */ vm_prop_id_t gen_code_propid(int check_only, int is_expr); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self); }; /* ------------------------------------------------------------------------ */ /* * Resolved symbol */ class CTPNSymResolved: public CTPNSymResolvedBase { public: CTPNSymResolved(CTcSymbol *sym) : CTPNSymResolvedBase(sym) { } /* generate code for the symbol evaluated as an rvalue */ void gen_code(int discard, int for_condition); /* generate code to assign a value to the symbol */ int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); /* generate code to take the address of the symbol */ void gen_code_addr(); /* generate code to call the symbol */ void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* generate code for operator 'new' applied to this expression */ void gen_code_new(int discard, int argc, int varargs, struct CTcNamedArgs *named_args, int from_call, int is_transient); /* evaluate a property ID */ vm_prop_id_t gen_code_propid(int check_only, int is_expr); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self); }; /* ------------------------------------------------------------------------ */ /* * Resolved debugger local variable symbol */ class CTPNSymDebugLocal: public CTPNSymDebugLocalBase { public: CTPNSymDebugLocal(const struct tcprsdbg_sym_info *info) : CTPNSymDebugLocalBase(info) { } /* generate code */ void gen_code(int discard, int for_condition); /* generate code for assigning into this node */ int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, class CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); }; /* ------------------------------------------------------------------------ */ /* * double-quoted string expression */ class CTPNDstr: public CTPNDstrBase { public: CTPNDstr(const char *str, size_t len) : CTPNDstrBase(str, len) { } void gen_code(int discard, int for_condition); }; /* * debug version of string */ class CTPNDebugDstr: public CTPNDstr { public: CTPNDebugDstr(const char *str, size_t len) : CTPNDstr(str, len) { } void gen_code(int discard, int for_condition); }; /* * double-quoted string embedding */ class CTPNDstrEmbed: public CTPNDstrEmbedBase { public: CTPNDstrEmbed(CTcPrsNode *sub); void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Embedded <> list in a string */ class CTPNStrOneOf: public CTPNStrOneOfBase { public: CTPNStrOneOf(int dstr, class CTPNList *lst, class CTcSymObj *state_obj) : CTPNStrOneOfBase(dstr, lst, state_obj) { } void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Argument List */ class CTPNArglist: public CTPNArglistBase { public: CTPNArglist(int argc, class CTPNArg *list_head) : CTPNArglistBase(argc, list_head) { } void gen_code(int, int) { /* * this isn't used - we always generate explicitly via * gen_code_arglist() */ assert(FALSE); } /* generate code for an argument list */ void gen_code_arglist(int *varargs, struct CTcNamedArgs &named_args); }; /* * Argument List Entry */ class CTPNArg: public CTPNArgBase { public: CTPNArg(CTcPrsNode *expr) : CTPNArgBase(expr) { } void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Function/method call */ class CTPNCall: public CTPNCallBase { public: CTPNCall(CTcPrsNode *func, class CTPNArglist *arglist); /* generate code */ void gen_code(int discard, int for_condition); /* generate code for operator 'new' applied to this expression */ void gen_code_new(int discard, int argc, int varargs, struct CTcNamedArgs *named_args, int from_call, int is_transient); /* generate code for a call to 'rand' */ int gen_code_rand(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Member expression with no arguments */ class CTPNMember: public CTPNMemberBase { public: CTPNMember(CTcPrsNode *lhs, CTcPrsNode *rhs, int rhs_is_expr) : CTPNMemberBase(lhs, rhs, rhs_is_expr) { } /* generate code */ void gen_code(int discard, int for_condition); /* generate code to assign a value to the symbol */ int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); }; /* * Member evaluation with argument list */ class CTPNMemArg: public CTPNMemArgBase { public: CTPNMemArg(CTcPrsNode *obj_expr, CTcPrsNode *prop_expr, int prop_is_expr, class CTPNArglist *arglist) : CTPNMemArgBase(obj_expr, prop_expr, prop_is_expr, arglist) { } void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * List */ class CTPNList: public CTPNListBase { public: void gen_code(int discard, int for_condition); }; /* * List Element */ class CTPNListEle: public CTPNListEleBase { public: CTPNListEle(CTcPrsNode *expr) : CTPNListEleBase(expr) { } void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Derived T3-specific symbol classes */ /* * undefined symbol */ class CTcSymUndef: public CTcSymUndefBase { public: CTcSymUndef(const char *str, size_t len, int copy) : CTcSymUndefBase(str, len, copy) { } /* * generate code to evaluate the symbol; since it's undefined, this * doesn't generate any code at all */ virtual void gen_code(int) { } /* * Generate code to assign to the symbol. The symbol is undefined, * so we can't generate any code; but we'll indicate to the caller * that the assignment is fully processed anyway, since there's no * need for the caller to try generating any code either. */ virtual int gen_code_asi(int, int phase, tc_asitype_t, const char *, class CTcPrsNode *, int, int, void **) { return TRUE; } /* * Generate code for taking the address of the symbol. The symbol * has no address, so this generates no code. */ virtual void gen_code_addr() { } /* * generate code to call the symbol - we can't generate any code for * this, but don't bother issuing an error since we will have shown * an error for the undefined symbol in the first place */ virtual void gen_code_call(int, int, int, struct CTcNamedArgs *) { } /* * generate code for operator 'new' applied to this expression - we * can't generate any code, but suppress errors as usual */ void gen_code_new(int, int, int, struct CTcNamedArgs *, int) { } /* generate a member expression */ void gen_code_member(int, class CTcPrsNode *, int, int, int, struct CTcNamedArgs *) { } /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self) { *is_self = FALSE; return VM_INVALID_OBJ; } }; /* * function */ class CTcSymFunc: public CTcSymFuncBase { public: CTcSymFunc(const char *str, size_t len, int copy, int argc, int opt_argc, int varargs, int has_retval, int is_multimethod, int is_mm_base, int is_extern, int has_proto) : CTcSymFuncBase(str, len, copy, argc, opt_argc, varargs, has_retval, is_multimethod, is_mm_base, is_extern, has_proto) { /* * we don't have a valid absolute address - by default, we use * the anchor */ abs_addr_valid_ = FALSE; abs_addr_ = 0; } /* generate code to evaluate the symbol */ virtual void gen_code(int discard); /* we can take the address of this symbol type */ virtual void gen_code_addr(); /* generate a call */ virtual void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* * Get my code pool address. This is valid only after the linking * phase has been completed and all fixups have been applied. (The * normal use for this is to generate image file elements not * subject to fixup, such as the entrypoint record. Normally, * references to the symbol that require the code pool address would * simply generate a fixup rather than asking for the true address, * since a fixup can be generated at any time during code * generation.) */ ulong get_code_pool_addr() const; /* write the symbol to an image file's global symbol table */ int write_to_image_file_global(class CVmImageWriter *image_writer); /* * Set the absolute code pool address. This can only be used when * the symbol is loaded from a fully-linked image file; in most * cases, this routine is not needed, because the function's address * will be kept with its associated anchor to allow for relocation * of the function during the linking process. */ void set_abs_addr(uint32_t addr) { /* remember the address */ abs_addr_ = addr; /* note that the absolute address is valid */ abs_addr_valid_ = TRUE; } /* add a runtime symbol table entry */ void add_runtime_symbol(class CVmRuntimeSymbols *symtab); protected: /* * Absolute code pool address, and a flag indicating whether the * address is valid. When we load a function symbol from a * fully-linked image file (specifically, when we load the symbol * from the debug records of an image file), the address of the * function is fully resolved and doesn't require fixups via the * anchor mechanism. In these cases, we'll simply remember the * resolved address here. */ ulong abs_addr_; uint abs_addr_valid_ : 1; }; /* * metaclass */ class CTcSymMetaclass: public CTcSymMetaclassBase { public: CTcSymMetaclass(const char *str, size_t len, int copy, int meta_idx, tctarg_obj_id_t class_obj) : CTcSymMetaclassBase(str, len, copy, meta_idx, class_obj) { } /* generate code to evaluate the symbol */ virtual void gen_code(int discard); /* generate code for operator 'new' */ void gen_code_new(int discard, int argc, int varargs, struct CTcNamedArgs *named_args, int is_transient); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self) { /* simply generate our code as normal */ gen_code(FALSE); /* indicate that the caller must use generated code */ *is_self = FALSE; return VM_INVALID_OBJ; } /* write the symbol to an image file's global symbol table */ int write_to_image_file_global(class CVmImageWriter *image_writer); /* * fix up the inheritance chain in the modifier objects - each * object file has its own independent inheritance list, so we can * only build the complete and connected list after loading them all */ void fix_mod_obj_sc_list(); /* add a runtime symbol table entry */ void add_runtime_symbol(class CVmRuntimeSymbols *symtab); }; /* * object */ class CTcSymObj: public CTcSymObjBase { public: CTcSymObj(const char *str, size_t len, int copy, vm_obj_id_t obj_id, int is_extern, tc_metaclass_t meta, class CTcDictEntry *dict) : CTcSymObjBase(str, len, copy, obj_id, is_extern, meta, dict) { } /* we have a valid object value */ virtual vm_obj_id_t get_val_obj() const { return get_obj_id(); } /* generate code to evaluate the symbol */ virtual void gen_code(int discard); /* we can take the address of this symbol type */ virtual void gen_code_addr(); /* generate code for operator 'new' */ void gen_code_new(int discard, int argc, int varargs, struct CTcNamedArgs *named_args, int is_transient); /* static code to generate code for 'new' */ static void s_gen_code_new(int discard, vm_obj_id_t obj_id, tc_metaclass_t meta, int argc, int varargs, struct CTcNamedArgs *named_args, int is_transient); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* static code to generate a member expression */ static void s_gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, vm_obj_id_t obj_id, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self); /* delete a property from our modified base classes */ virtual void delete_prop_from_mod_base(tctarg_prop_id_t prop_id); /* mark the compiled data for the object as a 'class' object */ virtual void mark_compiled_as_class(); /* write the symbol to an image file's global symbol table */ int write_to_image_file_global(class CVmImageWriter *image_writer); /* build my dictionary */ virtual void build_dictionary(); /* add a runtime symbol table entry */ void add_runtime_symbol(class CVmRuntimeSymbols *symtab); }; /* * Function-like object symbol. This is an object that's invokable as a * function, such as an anonymous function object or a DynamicFunc * instance. These occur in the dynamic compiler only. */ class CTcSymFuncObj: public CTcSymObj { public: CTcSymFuncObj(const char *str, size_t len, int copy, vm_obj_id_t obj_id, int is_extern, tc_metaclass_t meta, class CTcDictEntry *dict) : CTcSymObj(str, len, copy, obj_id, is_extern, meta, dict) { } /* generate a call */ virtual void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); }; /* * property */ class CTcSymProp: public CTcSymPropBase { public: CTcSymProp(const char *str, size_t len, int copy, tctarg_prop_id_t prop) : CTcSymPropBase(str, len, copy, prop) { } /* generate code to evaluate the symbol */ virtual void gen_code(int discard); /* we can take the address of this symbol type */ virtual void gen_code_addr(); /* assign to the property */ virtual int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, class CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); /* generate a call */ virtual void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* evaluate a property ID */ vm_prop_id_t gen_code_propid(int check_only, int is_expr); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self) { /* simply generate our code as normal */ gen_code(FALSE); /* indicate that the caller must use generated code */ *is_self = FALSE; return VM_INVALID_OBJ; } /* write the symbol to an image file's global symbol table */ int write_to_image_file_global(class CVmImageWriter *image_writer); /* add a runtime symbol table entry */ void add_runtime_symbol(class CVmRuntimeSymbols *symtab); }; /* * Enumerator */ class CTcSymEnum: public CTcSymEnumBase { public: CTcSymEnum(const char *str, size_t len, int copy, ulong id, int is_token) : CTcSymEnumBase(str, len, copy, id, is_token) { } /* generate code */ virtual void gen_code(int discard); /* write the symbol to an image file's global symbol table */ int write_to_image_file_global(class CVmImageWriter *image_writer); /* add a runtime symbol table entry */ void add_runtime_symbol(class CVmRuntimeSymbols *symtab); }; /* * local variable/parameter */ class CTcSymLocal: public CTcSymLocalBase { public: CTcSymLocal(const char *str, size_t len, int copy, int is_param, int var_num) : CTcSymLocalBase(str, len, copy, is_param, var_num) { } /* generate code to evaluate the symbol */ virtual void gen_code(int discard); /* generate code to store the value at top of stack in the variable */ void gen_code_setlcl_stk() { s_gen_code_setlcl_stk(var_num_, is_param_); } /* generate code to get a local with a given ID */ static void s_gen_code_getlcl(int local_id, int is_param); /* generate code to store a local with a given ID */ static void s_gen_code_setlcl_stk(int local_id, int is_param); /* generate code to store the value at top of stack in the local */ void gen_code_setlcl(); /* assign to the variable */ virtual int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, class CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); /* * generate a call - invoking a local as a function assumes that the * local contains a method or function pointer */ virtual void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* evaluate a property ID */ vm_prop_id_t gen_code_propid(int check_only, int is_expr); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self) { /* simply generate our code as normal */ gen_code(FALSE); /* indicate that the caller must use generated code */ *is_self = FALSE; return VM_INVALID_OBJ; } /* write the symbol to a debug frame */ virtual int write_to_debug_frame(int test_only); }; /* * Run-time dynamic code local variable symbol. This is the symbol type * for a local accessed via a StackFrameRef object, for use in DynamicFunc * compilations. */ class CTcSymDynLocal: public CTcSymDynLocalBase { public: CTcSymDynLocal(const char *str, size_t len, int copy, tctarg_obj_id_t fref, int varnum, int ctxidx) : CTcSymDynLocalBase(str, len, copy, fref, varnum, ctxidx) { } /* generate code */ void gen_code(int discard); /* generate code for assigning into this node */ int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, class CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); }; /* * built-in function */ class CTcSymBif: public CTcSymBifBase { public: CTcSymBif(const char *str, size_t len, int copy, ushort func_set_id, ushort func_idx, int has_retval, int min_argc, int max_argc, int varargs) : CTcSymBifBase(str, len, copy, func_set_id, func_idx, has_retval, min_argc, max_argc, varargs) { } /* generate code to evaluate the symbol */ virtual void gen_code(int discard); /* generate a call */ virtual void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* generate code for an address '&' operator */ virtual void gen_code_addr(); /* write the symbol to an image file's global symbol table */ int write_to_image_file_global(class CVmImageWriter *image_writer); /* add a runtime symbol table entry */ void add_runtime_symbol(class CVmRuntimeSymbols *symtab); }; /* * external function */ class CTcSymExtfn: public CTcSymExtfnBase { public: CTcSymExtfn(const char *str, size_t len, int copy, int argc, int varargs, int has_retval) : CTcSymExtfnBase(str, len, copy, argc, varargs, has_retval) { } /* generate code to evaluate the symbol */ virtual void gen_code(int discard); /* generate a call */ virtual void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* write the symbol to an image file's global symbol table */ int write_to_image_file_global(class CVmImageWriter *image_writer); }; /* * code label */ class CTcSymLabel: public CTcSymLabelBase { public: CTcSymLabel(const char *str, size_t len, int copy) : CTcSymLabelBase(str, len, copy) { } /* generate code to evaluate the symbol */ virtual void gen_code(int discard); }; /* ------------------------------------------------------------------------ */ /* * Anonymous function */ class CTPNAnonFunc: public CTPNAnonFuncBase { public: CTPNAnonFunc(class CTPNCodeBody *code_body, int has_retval, int is_method) : CTPNAnonFuncBase(code_body, has_retval, is_method) { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Code Body */ class CTPNCodeBody: public CTPNCodeBodyBase { public: CTPNCodeBody(class CTcPrsSymtab *lcltab, class CTcPrsSymtab *gototab, class CTPNStm *stm, int argc, int opt_argc, int varargs, int varargs_list, class CTcSymLocal *varargs_list_local, int local_cnt, int self_valid, struct CTcCodeBodyRef *enclosing_code_body) : CTPNCodeBodyBase(lcltab, gototab, stm, argc, opt_argc, varargs, varargs_list, varargs_list_local, local_cnt, self_valid, enclosing_code_body) { /* presume I'm not a constructor */ is_constructor_ = FALSE; /* presume I won't need a 'finally' return value holder local */ allocated_fin_ret_lcl_ = FALSE; fin_ret_lcl_ = 0; /* presume it won't be static */ is_static_ = FALSE; /* no nested symbol table list yet */ first_nested_symtab_ = 0; /* no named argument tables yet */ named_arg_tables_ = 0; } /* * Mark the code body as belonging to a constructor. A constructor * must return its 'self' object as the return value. */ void set_constructor(int f) { is_constructor_ = f; } /* * mark the code body as static - this indicates that it is a static * initializer */ void set_static() { is_static_ = TRUE; } /* generate code */ virtual void gen_code(int discard, int for_condition); /* check locals */ virtual void check_locals(); /* * Allocate a local variable for temporarily saving the expression * value of a 'return' statement while calling the enclosing * 'finally' block(s). Returns the variable ID. Only one such * local is needed per code body, so if we've already allocated one * for another 'return' statement, we'll just return the same one we * allocated previously. */ uint alloc_fin_ret_lcl() { /* if we haven't allocated it yet, do so now */ if (!allocated_fin_ret_lcl_) { /* use the next available local */ fin_ret_lcl_ = local_cnt_++; /* note that we've allocated it */ allocated_fin_ret_lcl_ = TRUE; } /* return the local ID */ return fin_ret_lcl_; } /* * Add a named argument table to the code body. Each call that has * named arguments requires one of these tables. We keep a list of * tables, and generate them all at the end of the method. Returns a * label that can be used to write a forward reference offset to the * table's offset in the code stream. */ struct CTcCodeLabel *add_named_arg_tab( const struct CTcNamedArgs *named_args); protected: /* * callback for enumerating local frame symbol table entries - write * each entry to the code stream as a debug record */ static void write_local_to_debug_frame(void *ctx, class CTcSymbol *sym); /* * enumerator for checking for parameter symbols that belong in the * local context */ static void enum_for_param_ctx(void *ctx, class CTcSymbol *sym); /* enumerator for generating named parameter bindings */ static void enum_for_named_params(void *ctx, class CTcSymbol *sym); /* write the debug information table to the code stream */ void build_debug_table(ulong start_ofs, int include_lines); /* show disassembly */ void show_disassembly(unsigned long start_ofs, unsigned long code_start_ofs, unsigned long code_end_ofs); /* head of list of nested symbol tables */ class CTcPrsSymtab *first_nested_symtab_; /* head of list of named argument tables */ struct CTcNamedArgTab *named_arg_tables_; /* * ID of local variable for temporarily storing the expression value * of a 'return' statement while calling the enclosing 'finally' * block(s); valid only when allocated_fin_ret_lcl_ is true */ uint fin_ret_lcl_; /* flag: I'm a constructor */ uint is_constructor_ : 1; /* * flag: I've allocated a local for saving the expression value of a * 'return' statement while calling the enclosing 'finally' block(s) */ uint allocated_fin_ret_lcl_ : 1; /* flag: I'm a static property initializer */ uint is_static_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Compound Statement */ class CTPNStmComp: public CTPNStmCompBase { public: CTPNStmComp(class CTPNStm *first_stm, class CTcPrsSymtab *symtab) : CTPNStmCompBase(first_stm, symtab) { } }; /* ------------------------------------------------------------------------ */ /* * Null statement */ class CTPNStmNull: public CTPNStmNullBase { public: CTPNStmNull() { } }; /* ------------------------------------------------------------------------ */ /* * Expression statement */ class CTPNStmExpr: public CTPNStmExprBase { public: CTPNStmExpr(class CTcPrsNode *expr) : CTPNStmExprBase(expr) { } }; /* ------------------------------------------------------------------------ */ /* * Static property initializer statement */ class CTPNStmStaticPropInit: public CTPNStmStaticPropInitBase { public: CTPNStmStaticPropInit(class CTcPrsNode *expr, tctarg_prop_id_t prop) : CTPNStmStaticPropInitBase(expr, prop) { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'if' statement */ class CTPNStmIf: public CTPNStmIfBase { public: CTPNStmIf(class CTcPrsNode *cond_expr, class CTPNStm *then_part, class CTPNStm *else_part) : CTPNStmIfBase(cond_expr, then_part, else_part) { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'for' statement */ class CTPNStmFor: public CTPNStmForBase { public: CTPNStmFor(class CTcPrsNode *init_expr, class CTcPrsNode *cond_expr, class CTcPrsNode *reinit_expr, class CTPNForIn *in_exprs, class CTcPrsSymtab *symtab, class CTPNStmEnclosing *enclosing_stm) : CTPNStmForBase(init_expr, cond_expr, reinit_expr, in_exprs, symtab, enclosing_stm) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for 'break' */ virtual int gen_code_break(const textchar_t *lbl, size_t lbl_len) { return gen_code_break_loop(break_lbl_, lbl, lbl_len); } /* generate code for 'continue' */ virtual int gen_code_labeled_continue() { return gen_code_continue_loop(cont_lbl_, 0, 0); } /* generate code for a labeled 'continue' */ virtual int gen_code_continue(const textchar_t *lbl, size_t lbl_len) { return gen_code_continue_loop(cont_lbl_, lbl, lbl_len); } protected: /* our 'break' label */ struct CTcCodeLabel *break_lbl_; /* our 'continue' label */ struct CTcCodeLabel *cont_lbl_; }; /* ------------------------------------------------------------------------ */ /* * ' in ' node, for 'for' statements */ class CTPNVarIn: public CTPNVarInBase { public: CTPNVarIn(class CTcPrsNode *lval, class CTcPrsNode *expr, int iter_local_id) : CTPNVarInBase(lval, expr, iter_local_id) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate the condition part of the 'for' */ virtual void gen_forstm_cond(struct CTcCodeLabel *endlbl); /* generate the reinit part of the 'for' */ virtual void gen_forstm_reinit(); /* * Generate the iterator initializer. This is a static method so that * it can be shared by for..in and foreach..in. 'kw' is the keyword * for the statement type that we're generating ("for" or "foreach"), * so that we can show the appropriate statement type in any error * messages. */ static void gen_iter_init(class CTcPrsNode *coll_expr, int iter_local_id, const char *kw); /* * Generate the iterator condition. This is a static method so that it * can be shared by for..in and foreach..in. */ static void gen_iter_cond(class CTcPrsNode *lval, int iter_local_id, struct CTcCodeLabel *&endlbl, const char *kw); }; /* * ' in .. ' node, for 'for' statements */ class CTPNVarInRange: public CTPNVarInRangeBase { public: CTPNVarInRange(class CTcPrsNode *lval, class CTcPrsNode *from_expr, class CTcPrsNode *to_expr, class CTcPrsNode *step_expr, int to_local_id, int step_local_id) : CTPNVarInRangeBase(lval, from_expr, to_expr, step_expr, to_local_id, step_local_id) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate the condition part of the 'for' */ virtual void gen_forstm_cond(struct CTcCodeLabel *endlbl); /* generate the reinit part of the 'for' */ virtual void gen_forstm_reinit(); }; /* ------------------------------------------------------------------------ */ /* * 'for' statement */ class CTPNStmForeach: public CTPNStmForeachBase { public: CTPNStmForeach(class CTcPrsNode *iter_expr, class CTcPrsNode *coll_expr, class CTcPrsSymtab *symtab, class CTPNStmEnclosing *enclosing_stm, int iter_local_id) : CTPNStmForeachBase(iter_expr, coll_expr, symtab, enclosing_stm, iter_local_id) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for 'break' */ virtual int gen_code_break(const textchar_t *lbl, size_t lbl_len) { return gen_code_break_loop(break_lbl_, lbl, lbl_len); } /* generate code for 'continue' */ virtual int gen_code_labeled_continue() { return gen_code_continue_loop(cont_lbl_, 0, 0); } /* generate code for a labeled 'continue' */ virtual int gen_code_continue(const textchar_t *lbl, size_t lbl_len) { return gen_code_continue_loop(cont_lbl_, lbl, lbl_len); } protected: /* our 'break' label */ struct CTcCodeLabel *break_lbl_; /* our 'continue' label */ struct CTcCodeLabel *cont_lbl_; }; /* ------------------------------------------------------------------------ */ /* * 'break' statement */ class CTPNStmBreak: public CTPNStmBreakBase { public: CTPNStmBreak() { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'continue' statement */ class CTPNStmContinue: public CTPNStmContinueBase { public: CTPNStmContinue() { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'while' statement */ class CTPNStmWhile: public CTPNStmWhileBase { public: CTPNStmWhile(class CTcPrsNode *cond_expr, class CTPNStmEnclosing *enclosing_stm) : CTPNStmWhileBase(cond_expr, enclosing_stm) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for 'break' */ virtual int gen_code_break(const textchar_t *lbl, size_t lbl_len) { return gen_code_break_loop(break_lbl_, lbl, lbl_len); } /* generate code for 'continue' */ virtual int gen_code_labeled_continue() { return gen_code_continue_loop(cont_lbl_, 0, 0); } /* generate code for a labeled 'continue' */ virtual int gen_code_continue(const textchar_t *lbl, size_t lbl_len) { return gen_code_continue_loop(cont_lbl_, lbl, lbl_len); } protected: /* our 'break' label */ struct CTcCodeLabel *break_lbl_; /* our 'continue' label */ struct CTcCodeLabel *cont_lbl_; }; /* ------------------------------------------------------------------------ */ /* * 'do-while' statement */ class CTPNStmDoWhile: public CTPNStmDoWhileBase { public: CTPNStmDoWhile(class CTPNStmEnclosing *enclosing_stm) : CTPNStmDoWhileBase(enclosing_stm) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for 'break' */ virtual int gen_code_break(const textchar_t *lbl, size_t lbl_len) { return gen_code_break_loop(break_lbl_, lbl, lbl_len); } /* generate code for 'continue' */ virtual int gen_code_labeled_continue() { return gen_code_continue_loop(cont_lbl_, 0, 0); } /* generate code for a labeled 'continue' */ virtual int gen_code_continue(const textchar_t *lbl, size_t lbl_len) { return gen_code_continue_loop(cont_lbl_, lbl, lbl_len); } protected: /* our 'break' label */ struct CTcCodeLabel *break_lbl_; /* our 'continue' label */ struct CTcCodeLabel *cont_lbl_; }; /* ------------------------------------------------------------------------ */ /* * 'return' statement */ class CTPNStmReturn: public CTPNStmReturnBase { public: CTPNStmReturn(class CTcPrsNode *expr) : CTPNStmReturnBase(expr) { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'switch' statement */ class CTPNStmSwitch: public CTPNStmSwitchBase { public: CTPNStmSwitch(class CTPNStmEnclosing *enclosing_stm) : CTPNStmSwitchBase(enclosing_stm) { /* the 'case' and 'default' slot pointers aren't valid yet */ case_slot_ofs_ = 0; default_slot_ofs_ = 0; } /* generate code */ virtual void gen_code(int discard, int for_condition); /* * Get the next case slot offset, and consume the slot. Each call * consumes one slot. This is valid only during code generation in * the switch body; 'case' labels use this to figure out where to * write their data. */ ulong alloc_case_slot() { ulong ret; /* note the value to return */ ret = case_slot_ofs_; /* move our internal counter to the next slot */ case_slot_ofs_ += VMB_DATAHOLDER + VMB_UINT2; /* return the allocated slot */ return ret; } /* get the code offset of the 'default' case slot */ ulong get_default_slot() const { return default_slot_ofs_; } /* generate code for 'break' */ virtual int gen_code_break(const textchar_t *lbl, size_t lbl_len) { /* * if there's no label, and we don't have a 'break' label * ourselves, it must mean that we're processing an unreachable * 'break' - we can simply do nothing in this case and pretend we * succeeded */ if (lbl == 0 && break_lbl_ == 0) return TRUE; /* break to our break label */ return gen_code_break_loop(break_lbl_, lbl, lbl_len); } protected: /* * code offset of the next 'case' slot to be allocated from the * switch's case table - this is only valid during generation of the * body, because we set this up during generation of the switch * itself */ ulong case_slot_ofs_; /* code offset of the 'default' slot */ ulong default_slot_ofs_; /* our 'break' label */ struct CTcCodeLabel *break_lbl_; }; /* ------------------------------------------------------------------------ */ /* * code label statement */ class CTPNStmLabel: public CTPNStmLabelBase { public: CTPNStmLabel(class CTcSymLabel *lbl, class CTPNStmEnclosing *enclosing); /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for 'break' */ virtual int gen_code_break(const textchar_t *lbl, size_t lbl_len); /* generate code for 'continue' */ virtual int gen_code_continue(const textchar_t *lbl, size_t lbl_len); /* * Generate code for a labeled 'continue'. We'll simply generate * code for a labeled continue in our enclosed statement. * * (This is normally a routine that *we* call on our contained * statement in response to a 'continue' being targeted at this * label. This can be invoked on us, however, when a statement has * multiple labels, and the outer label is mentioned in the 'break' * -- the outer label will call this routine on the inner label, * which is us, so we simply need to pass it along to the enclosed * statement.) */ virtual int gen_code_labeled_continue() { return (stm_ != 0 ? stm_->gen_code_labeled_continue() : FALSE); } /* get my 'goto' label */ struct CTcCodeLabel *get_goto_label(); protected: /* the byte label to jump to this statement */ struct CTcCodeLabel *goto_label_; /* the byte label to break from this statement */ struct CTcCodeLabel *break_label_; }; /* ------------------------------------------------------------------------ */ /* * 'case' label statement */ class CTPNStmCase: public CTPNStmCaseBase { public: CTPNStmCase() { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'default' label statement */ class CTPNStmDefault: public CTPNStmDefaultBase { public: CTPNStmDefault() { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'try' block */ class CTPNStmTry: public CTPNStmTryBase { public: CTPNStmTry(class CTPNStmEnclosing *enclosing) : CTPNStmTryBase(enclosing) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for 'break' */ virtual int gen_code_break(const textchar_t *lbl, size_t lbl_len); /* generate code for 'continue' */ virtual int gen_code_continue(const textchar_t *lbl, size_t lbl_len); /* * Generate the code necessary to unwind for returning. We must * generate a call to our 'finally' code, then unwind any enclosing * statements. */ virtual void gen_code_unwind_for_return() { /* call 'finally' on the way out */ gen_jsr_finally(); /* inherit default to unwind enclosing statements */ CTPNStmTryBase::gen_code_unwind_for_return(); } /* determine if we'll generate code for unwinding from a return */ virtual int will_gen_code_unwind_for_return() const { /* we'll generate a call to our 'finally' block if we have one */ if (finally_stm_ != 0) return TRUE; /* we'll also generate code if any enclosing statement does */ return CTPNStmTryBase::will_gen_code_unwind_for_return(); } /* generate the code necessary to transfer out of the block */ virtual void gen_code_for_transfer_out() { /* call 'finally' on the way out */ gen_jsr_finally(); } /* * generate a local subroutine call to our 'finally' block, if we * have a 'finally' block - this should be invoked whenever code * within the protected block or a 'catch' block executes a break, * continue, return, or goto out of the protected region, or when * merely falling off the end of the protected block or a 'catch' * block. */ void gen_jsr_finally(); /* get my 'finally' label */ struct CTcCodeLabel *get_finally_lbl() const { return finally_lbl_; } protected: /* our 'finally' label */ struct CTcCodeLabel *finally_lbl_; }; /* ------------------------------------------------------------------------ */ /* * 'catch' */ class CTPNStmCatch: public CTPNStmCatchBase { public: CTPNStmCatch() { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for the 'catch' clause */ virtual void gen_code_catch(ulong protected_start_ofs, ulong protected_end_ofs); }; /* ------------------------------------------------------------------------ */ /* * 'finally' */ class CTPNStmFinally: public CTPNStmFinallyBase { public: CTPNStmFinally(CTPNStmEnclosing *enclosing, int exc_local_id, int jsr_local_id) : CTPNStmFinallyBase(enclosing, exc_local_id, jsr_local_id) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for the 'finally' clause */ virtual void gen_code_finally(ulong protected_start_ofs, ulong protected_end_ofs, class CTPNStmTry *try_stm); /* check for transfer in via 'goto' statements */ virtual int check_enter_by_goto(class CTPNStmGoto *goto_stm, class CTPNStmLabel *target); }; /* ------------------------------------------------------------------------ */ /* * 'throw' statement */ class CTPNStmThrow: public CTPNStmThrowBase { public: CTPNStmThrow(class CTcPrsNode *expr) : CTPNStmThrowBase(expr) { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'goto' statement */ class CTPNStmGoto: public CTPNStmGotoBase { public: CTPNStmGoto(const textchar_t *lbl, size_t lbl_len) : CTPNStmGotoBase(lbl, lbl_len) { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'dictionary' statement */ class CTPNStmDict: public CTPNStmDictBase { public: CTPNStmDict(class CTcDictEntry *dict) : CTPNStmDictBase(dict) { } /* generate code */ virtual void gen_code(int, int) { } }; /* ------------------------------------------------------------------------ */ /* * Object Definition */ class CTPNStmObject: public CTPNStmObjectBase { public: CTPNStmObject(class CTcSymObj *obj_sym, int is_class) : CTPNStmObjectBase(obj_sym, is_class) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* check locals */ virtual void check_locals(); /* * given the stream offset for the start of the object data in the * stream, get information on the object from the stream data: * *. - get the number of properties in the stream data *. - set the number of properties in the stream data, updating the * stored data size in the metaclass header in the stream; returns * the new data size *. - get the number of superclasses in the stream data *. - get a superclass object ID *. - get the offset of the first property in the stream data *. - get the offset of the property at the given index *. - get the ID of the object's property at the given index *. - set the ID of the object's property at the given index *. - get the object flags from the tads-object header *. - set the object flags in the tads-object heade */ static uint get_stream_prop_cnt(class CTcDataStream *stream, ulong obj_ofs); static size_t set_stream_prop_cnt(class CTcDataStream *stream, ulong obj_ofs, uint prop_cnt); static uint get_stream_sc_cnt(class CTcDataStream *stream, ulong obj_ofs); static vm_obj_id_t get_stream_sc(class CTcDataStream *stream, ulong obj_ofs, uint sc_idx); static void set_stream_sc(class CTcDataStream *stream, ulong obj_ofs, uint sc_idx, vm_obj_id_t new_sc); static ulong get_stream_first_prop_ofs(class CTcDataStream *stream, ulong obj_ofs); static ulong get_stream_prop_ofs(class CTcDataStream *stream, ulong obj_ofs, uint prop_idx); static vm_prop_id_t get_stream_prop_id(class CTcDataStream *stream, ulong obj_ofs, uint prop_idx); static enum vm_datatype_t get_stream_prop_type(class CTcDataStream *stream, ulong obj_ofs, uint prop_idx); static ulong get_stream_prop_val_ofs(class CTcDataStream *stream, ulong obj_ofs, uint prop_idx); static void set_stream_prop_id(class CTcDataStream *stream, ulong obj_ofs, uint prop_idx, vm_prop_id_t new_id); static uint get_stream_obj_flags(class CTcDataStream *stream, ulong obj_ofs); static void set_stream_obj_flags(class CTcDataStream *stream, ulong obj_ofs, uint flags); }; /* * Inline object definition (defined within an expression) */ class CTPNInlineObject: public CTPNInlineObjectBase { public: /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* * Property Value list entry */ class CTPNObjProp: public CTPNObjPropBase { public: CTPNObjProp(class CTPNObjDef *objdef, class CTcSymProp *prop_sym, class CTcPrsNode *expr, class CTPNCodeBody *code_body, class CTPNAnonFunc *inline_method, int is_static) : CTPNObjPropBase( objdef, prop_sym, expr, code_body, inline_method, is_static) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for an inline object instantiation */ void gen_code_inline_obj(); /* check locals */ void check_locals(); protected: /* generate a setMethod() call for inline object creation */ void gen_setMethod(); }; /* * Implicit constructor */ class CTPNStmImplicitCtor: public CTPNStmImplicitCtorBase { public: CTPNStmImplicitCtor(class CTPNStmObject *obj_stm) : CTPNStmImplicitCtorBase(obj_stm) { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; #endif /* TCT3DRV_H */ qtads-2.1.7/tads3/tct3int.h000066400000000000000000000242011265017072300154160ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/TCT3INT.H,v 1.3 1999/07/11 00:46:57 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3int.h - T3-specific intermediate parse node classes Function Notes Modified 05/12/99 MJRoberts - Creation */ #ifndef TCT3INT_H #define TCT3INT_H /* include our T3-specific base classes */ #include "tct3base.h" /* include the target-independent intermediate classes */ #include "tcpnint.h" /* ------------------------------------------------------------------------ */ /* * Unary Operator Expression node */ class CTPNUnary: public CTPNUnaryBase { public: CTPNUnary(CTcPrsNode *subexpr) : CTPNUnaryBase(subexpr) { } /* * Generate code for a unary operator with no side effects. We'll * generate code for the subexpression, then generate the given * opcode. * * If 'discard' is true, we won't generate the opcode. We know the * opcode has no side effects, so if the result of the calculation * isn't going to be used, there's no point in calculating it in the * first place. */ void gen_unary(uchar opc, int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Binary Operator Expression node */ class CTPNBin: public CTPNBinBase { public: CTPNBin(CTcPrsNode *lhs, CTcPrsNode *rhs) : CTPNBinBase(lhs, rhs) { } /* * Generate code for a binary operator with no side effects. We'll * generate code for the left operand, then for the right operand, * then generate the opcode. * * If 'discard' is true, we won't generate the opcode. We know that * the opcode has no side effects, so if the result of the * calculation isn't needed, we need not apply the opcode; we simply * want to evaluate the subexpressions for any side effects they * might have. */ void gen_binary(uchar opc, int discard, int for_condition); /* * Generate code for a binary operator as part of a compound * assignment. For example, this is used to generate the '+' portion * of '+='. The caller must already have generated code to push the * left operand; we generate the right operand plus the opcode to apply * the operator. */ void gen_binary_ca(uchar opc); }; /* ------------------------------------------------------------------------ */ /* * generic statement */ class CTPNStm: public CTPNStmBase { public: /* initialize at the tokenizer's current source file position */ CTPNStm() : CTPNStmBase() { } /* initialize at the given source position */ CTPNStm(class CTcTokFileDesc *file, long linenum) : CTPNStmBase(file, linenum) { } /* * Generate code for a labeled 'continue'. These are called when * this statement is labeled, and the same label is used in a * 'continue' statement. * * Returns true if successful, false if not. This should return * false if the statement is not suitable for a 'continue'. By * default, we simply return false, since most statements cannot be * used as the target of a 'continue'. */ virtual int gen_code_labeled_continue() { return FALSE; } }; /* ------------------------------------------------------------------------ */ /* * Enclosing Statement class: this is the base class for statements that * have special needs for exiting due to break, continue, goto, and * return. Refer to tcpnint.h for details. */ class CTPNStmEnclosing: public CTPNStm { public: CTPNStmEnclosing(CTPNStmEnclosing *enclosing) { /* remember the statement that encloses me */ enclosing_ = enclosing; } /* get the enclosing statement */ CTPNStmEnclosing *get_enclosing() const { return enclosing_; } /* * Generate code for a 'break' to a given label, or an unlabeled * 'break' if lbl is null. Returns true if the break was fully * processed, false if not. If any work is necessary to unwind the * exception stack to break out of this block, that should be done * here. If this node doesn't actually accomplish the break, it * should recursively invoke the next enclosing statement's routine * to do the work. * * By default, we'll simply invoke the enclosing handler, if there * is one, or return failure (i.e., false) if not. */ virtual int gen_code_break(const textchar_t *lbl, size_t lbl_len) { if (enclosing_) return enclosing_->gen_code_break(lbl, lbl_len); else return 0; } /* * Generate code for a 'continue' to a given label, or an unlabeled * 'continue' if lbl is null. Same rules as gen_code_break(). * * By default, we'll simply invoke the enclosing handler, if there * is one, or return failure (i.e., false) if not. */ virtual int gen_code_continue(const textchar_t *lbl, size_t lbl_len) { if (enclosing_) return enclosing_->gen_code_continue(lbl, lbl_len); else return 0; } /* * Generate the code necessary to unwind the stack for returning * through this enclosing statement. This should generate any code * necessary (such as a call to a 'finally' block, for a 'try' * statement), then call the next enclosing statement to do the same * thing. By default, this does nothing except invoke the enclosing * statement, since most enclosing statements don't need to generate * any code to handle a 'return' through them. */ virtual void gen_code_unwind_for_return() { /* if there's an enclosing statement, ask it to unwind itself */ if (enclosing_ != 0) enclosing_->gen_code_unwind_for_return(); } /* * Determine if we'll generate any code to unwind the stack for * returning through this enclosing statement. This should return * true if any code will be generated by a call to * gen_code_unwind_for_return(). By default, we'll return whatever * our enclosing statement does. */ virtual int will_gen_code_unwind_for_return() const { /* * return what the enclosing statement does, or false if we're * the outermost statement */ return (enclosing_ != 0 ? enclosing_->will_gen_code_unwind_for_return() : FALSE); } /* * Generate the code necessary to unwind the stack for executing a * 'goto' to the given label statement. We first determine if the * target label is contained within this statement; if it is, * there's nothing we need to do, because we're transferring control * within the same enclosing statement. * * If control is being transferred outside of this statement (i.e., * the label is not contained within this statement), we must * generate the necessary code to leave the block; for example, a * 'try' block must generate a call to its 'finally' block. We must * then invoke this same function on our enclosing statement to let * it do the same work. */ void gen_code_unwind_for_goto(class CTPNStmGoto *goto_stm, class CTPNStmLabel *target); /* * Generate code for transferring control out of this statement. We * invoke this from gen_code_unwind_for_goto() when we determine * that the 'goto' target is not enclosed within this statement. By * default we do nothing; statements such as 'try' that must do work * on transferring control must override this to generate the * appropriate code. */ virtual void gen_code_for_transfer_out() { } /* * Check to see if we are allowed to transfer in to this block via * 'goto' statements. If so, simply return TRUE. If we don't allow * this type of transfer, we should log an appropriate error, then * return FALSE. Note that the function should both log an error * and return FALSE if the transfer isn't allowed; the return value * allows the caller to ensure that only one error is displayed for * this type of problem even if the target label is nested within * several levels of blocks that don't allow transfers in. * * By default, we'll simply return TRUE, since most block types * allow this type of transfer. */ virtual int check_enter_by_goto(class CTPNStmGoto * /*goto_stm*/, class CTPNStmLabel * /*target*/) { return TRUE; } protected: /* * generate code for a break - this can be used as a service routine * by loop/switch statements */ int gen_code_break_loop(CTcCodeLabel *code_label, const textchar_t *lbl, size_t lbl_len); /* * generate code for a continue - this can be used as a service * routine by loop/switch statements */ int gen_code_continue_loop(CTcCodeLabel *code_label, const textchar_t *lbl, size_t lbl_len); /* the block enclosing this block */ CTPNStmEnclosing *enclosing_; }; /* ------------------------------------------------------------------------ */ /* * Base class for 'for' statement 'var in collection' and 'var in from..to' * expressions. We keep a separate list of these nodes for a 'for' * statement, for code generation purposes. These expression nodes reside * in the initializer clause of the 'for', but they implicitly generate * code in the condition and reinit phases as well. */ class CTPNForIn: public CTPNForInBase { public: CTPNForIn() { } /* generate the condition part of the 'for' */ virtual void gen_forstm_cond(struct CTcCodeLabel *endlbl) = 0; /* generate the reinit part of the 'for' */ virtual void gen_forstm_reinit() = 0; protected: }; #endif /* TCT3INT_H */ qtads-2.1.7/tads3/tct3nl.cpp000066400000000000000000000046751265017072300156050ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3nl.cpp - T3 NO LINKER module Function This module can be linked with programs that don't require any linker functionality. This module contains dummy entrypoints for functions and methods that would normally be linked from tct3img.cpp, which contains the actual implementations of these methods. Notes Modified 11/22/99 MJRoberts - Creation */ #include "t3std.h" #include "os.h" #include "tct3.h" /* ------------------------------------------------------------------------ */ /* * Symbol table entry routines for writing a symbol to the global symbol * table in the debug records in the image file. When we don't include * linking functionality in a program, these entrypoints will never be * called (but they're needed to link anything that references a * CTcSymbol subclass, since they're virtual functions in those classes) */ /* * write the symbol to an image file's global symbol table */ int CTcSymFunc::write_to_image_file_global(class CVmImageWriter *) { assert(FALSE); return FALSE; } /* * write the symbol to an image file's global symbol table */ int CTcSymObj::write_to_image_file_global(class CVmImageWriter *) { assert(FALSE); return FALSE; } /* * build the dictionary */ void CTcSymObj::build_dictionary() { assert(FALSE); } /* * write the symbol to an image file's global symbol table */ int CTcSymProp::write_to_image_file_global(class CVmImageWriter *) { assert(FALSE); return FALSE; } /* * write the symbol to an image file's global symbol table */ int CTcSymEnum::write_to_image_file_global(class CVmImageWriter *) { assert(FALSE); return FALSE; } /* * write the symbol to an image file's global symbol table */ int CTcSymMetaclass::write_to_image_file_global(class CVmImageWriter *) { assert(FALSE); return FALSE; } /* * write the symbol to an image file's global symbol table */ int CTcSymBif::write_to_image_file_global(class CVmImageWriter *) { assert(FALSE); return FALSE; } /* * write the symbol to an image file's global symbol table */ int CTcSymExtfn::write_to_image_file_global(class CVmImageWriter *) { assert(FALSE); return FALSE; } qtads-2.1.7/tads3/tct3stm.cpp000066400000000000000000002303321265017072300157660ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/TCT3STM.CPP,v 1.1 1999/07/11 00:46:57 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3stm.cpp - TADS 3 Compiler - T3 VM Code Generator - statement classes Function Generate code for the T3 VM. This file contains statement classes, in order to segregate the code generation classes required for the full compiler from those required for subsets that require only expression parsing (such as debuggers). Notes Modified 05/08/99 MJRoberts - Creation */ #include #include "t3std.h" #include "os.h" #include "tcprs.h" #include "tct3.h" #include "tcgen.h" #include "vmtype.h" #include "vmwrtimg.h" #include "vmfile.h" #include "tcmain.h" #include "tcerr.h" /* ------------------------------------------------------------------------ */ /* * 'if' statement */ /* * generate code */ void CTPNStmIf::gen_code(int, int) { /* add a line record */ add_debug_line_rec(); /* * if the condition has a constant value, don't bother generating * code for both branches */ if (cond_expr_->is_const()) { int val; /* determine whether it's true or false */ val = cond_expr_->get_const_val()->get_val_bool(); /* * Warn about it if it's always false (in which case the 'then' * code is unreachable); or it's always true and we have an * 'else' part (since the 'else' part is unreachable). Don't * warn if it's true and there's no 'else' part, since this * merely means that there's some redundant source code, but * will have no effect on the generated code. */ if (!val) { /* * It's false - the 'then' part cannot be executed. If this * isn't a compile-time constant expression, warn about it. */ if (!cond_expr_->get_const_val()->is_ctc()) log_warning(TCERR_IF_ALWAYS_FALSE); /* generate the 'else' part if there is one */ if (else_part_ != 0) gen_code_substm(else_part_); } else { /* it's true - the 'else' part cannot be executed */ if (else_part_ != 0) log_warning(TCERR_IF_ALWAYS_TRUE); /* generate the 'then' part */ if (then_part_ != 0) gen_code_substm(then_part_); } /* we're done */ return; } /* * If both the 'then' and 'else' parts are null statements, we're * evaluating the condition purely for side effects. Simply * evaluate the condition in this case, since there's no need to so * much as test the condition once evaluated. */ if (then_part_ == 0 && else_part_ == 0) { /* generate the condition, discarding the result */ cond_expr_->gen_code(TRUE, TRUE); /* we're done */ return; } /* * The condition is non-constant, and we have at least one subclause, * so we must evaluate the condition expression. To minimize the * amount of jumping, check whether we have a true part, else part, or * both, and generate the branching accordingly. */ if (then_part_ != 0) { CTcCodeLabel *lbl_else; CTcCodeLabel *lbl_end; /* * We have a true part, so we will want to evaluate the expression * and jump past the true part if the expression is false. Create * a label for the false branch. */ lbl_else = G_cs->new_label_fwd(); /* generate the condition expression */ cond_expr_->gen_code_cond(0, lbl_else); /* generate the 'then' part */ gen_code_substm(then_part_); /* if there's an 'else' part, generate it */ if (else_part_ != 0) { /* at the end of the 'then' part, jump past the 'else' part */ lbl_end = gen_jump_ahead(OPC_JMP); /* this is the start of the 'else' part */ def_label_pos(lbl_else); /* generate the 'else' part */ gen_code_substm(else_part_); /* set the label for the jump over the 'else' part */ def_label_pos(lbl_end); } else { /* * there's no 'else' part - set the label for the jump past the * 'then' part */ def_label_pos(lbl_else); } } else { CTcCodeLabel *lbl_end; /* * There's no 'then' part, so there must be an 'else' part (we * wouldn't have gotten this far if both 'then' and 'else' are * empty). To minimize branching, evaluate the condition and jump * past the 'else' part if the condition is true, falling through * to the 'else' part otherwise. Create a label for the end of the * statement, which is also the empty 'then' part. */ lbl_end = G_cs->new_label_fwd(); /* evaluate the condition and jump to the end if it's true */ cond_expr_->gen_code_cond(lbl_end, 0); /* generate the 'else' part */ gen_code_substm(else_part_); /* set the label for the jump over the 'else' part */ def_label_pos(lbl_end); } } /* ------------------------------------------------------------------------ */ /* * 'for' statement */ /* * generate code */ void CTPNStmFor::gen_code(int, int) { CTcCodeLabel *top_lbl; CTcCodeLabel *end_lbl; CTcCodeLabel *cont_lbl; CTPNStmEnclosing *old_enclosing; CTcPrsSymtab *old_frame; /* set my local frame if necessary */ old_frame = G_cs->set_local_frame(symtab_); /* * add a line record - note that we add the line record after * setting up the local frame, so that the 'for' statement itself * appears within its own inner scope */ add_debug_line_rec(); /* push the enclosing statement */ old_enclosing = G_cs->set_enclosing(this); /* if there's an initializer expression, generate it */ if (init_expr_ != 0) init_expr_->gen_code(TRUE, TRUE); /* set the label for the top of the loop */ top_lbl = new_label_here(); /* allocate a forward label for 'continue' jumps */ cont_lbl = G_cs->new_label_fwd(); /* allocate a forward label for the end of the loop */ end_lbl = G_cs->new_label_fwd(); /* generate the implicit conditions for any 'in' clauses */ for (CTPNForIn *i = in_exprs_ ; i != 0 ; i = i->getnxt()) i->gen_forstm_cond(end_lbl); /* * If there's an explicit condition clause, generate its code, jumping * to the end of the loop if the condition is false. */ if (cond_expr_ != 0) cond_expr_->gen_code_cond(0, end_lbl); /* * set our labels, so that 'break' and 'continue' statements in our * body will know where to go */ break_lbl_ = end_lbl; cont_lbl_ = cont_lbl; /* if we have a body, generate it */ if (body_stm_ != 0) gen_code_substm(body_stm_); /* * add another line record - we're now generating code again for the * original 'for' line, even though it's after the body */ //$$$ add_debug_line_rec(); /* this is where we come for 'continue' statements */ def_label_pos(cont_lbl); /* generate the reinitializer expression, if we have one */ if (reinit_expr_ != 0) reinit_expr_->gen_code(TRUE, TRUE); /* generate the implicit reinitializers for any 'in' clauses */ for (CTPNForIn *i = in_exprs_ ; i != 0 ; i = i->getnxt()) i->gen_forstm_reinit(); /* jump back to the top of the loop */ G_cg->write_op(OPC_JMP); G_cs->write_ofs2(top_lbl, 0); /* * we're at the end of the loop - this is where we jump for 'break' * and when the condition becomes false */ def_label_pos(end_lbl); /* restore the enclosing statement */ G_cs->set_enclosing(old_enclosing); /* restore the enclosing local scope */ G_cs->set_local_frame(old_frame); } /* ------------------------------------------------------------------------ */ /* * 'for var in expr' node */ /* * Generate code. This is called during the initializer phase of the 'for' * that we're part of. We create the collection enumerator and assign the * first value from the enumerator to the lvalue. */ void CTPNVarIn::gen_code(int, int) { /* generate the iterator initializer */ gen_iter_init(expr_, iter_local_id_, "for"); } /* * Static method: generate the iterator initializer */ void CTPNVarIn::gen_iter_init(CTcPrsNode *coll_expr, int iter_local_id, const char *kw) { /* * Look up the createIterator property of the Collection metaclass. * This property is defined by the Collection specification as the * property in the first (zeroeth) slot in the method table for * Collection. If Collection isn't defined, or this slot isn't * defined, it's an error. */ CTcPrsNode *create_iter = G_cg->get_metaclass_prop("collection", 0); /* if we didn't find the property, it's an error */ if (create_iter != VM_INVALID_PROP) { /* * generate a call to the createIterator() property on the * collection expression */ if (coll_expr != 0) coll_expr->gen_code_member(FALSE, create_iter, FALSE, 0, FALSE, 0); /* assign the result to the internal iterator stack local */ CTcSymLocal::s_gen_code_setlcl_stk(iter_local_id, FALSE); } else { /* tell them about the problem */ G_tok->log_error(TCERR_FOREACH_NO_CREATEITER, kw); } } /* * Generate code for the condition portion of the 'for'. We check to see * if there's a next value: if so we fetch it into our lvalue, it not we * break the loop. */ void CTPNVarIn::gen_forstm_cond(CTcCodeLabel *endlbl) { /* generate the code */ gen_iter_cond(lval_, iter_local_id_, endlbl, "for"); } /* * Generate code for the condition portion of a for..in or a foreach..in. */ void CTPNVarIn::gen_iter_cond(CTcPrsNode *lval, int iter_local_id, CTcCodeLabel *&end_lbl, const char *kw) { #if 1 /* if the caller didn't provide an end-of-loop label, create one */ if (end_lbl == 0) end_lbl = G_cs->new_label_fwd(); /* * Generate the ITERNEXT , . This pushes * the next iterator value if available, otherwise jumps to the end * label (without pushing anything onto the stack). */ G_cg->write_op(OPC_ITERNEXT); G_cs->write2(iter_local_id); G_cs->write_ofs2(end_lbl, 0); /* we're on the looping branch - ITERNEXT pushed the next value */ G_cg->note_push(); /* assign the next value to the loop control lvalue */ if (lval != 0) lval->gen_code_asi(TRUE, 1, TC_ASI_SIMPLE, "=", 0, FALSE, TRUE, 0); else { G_cg->write_op(OPC_DISC); G_cg->note_pop(); } #else /* * Look up Iterator.getNext() (iterator metaclass method index 0) and * Iterator.isNextAvailable() (index 1). */ CTcPrsNode *get_next = G_cg->get_metaclass_prop("iterator", 0); CTcPrsNode *next_avail = G_cg->get_metaclass_prop("iterator", 1); /* generate the isNextAvailable test */ if (next_avail != 0) { /* get the internal iterator local */ CTcSymLocal::s_gen_code_getlcl(iter_local_id, FALSE); /* generate a call to the property */ CTcPrsNode::s_gen_member_rhs( FALSE, next_avail, FALSE, 0, FALSE, FALSE); /* * generate a jump to the end of the loop, creating the label if * the caller didn't already */ if (end_lbl != 0) { /* jump to the caller's end label */ G_cg->write_op(OPC_JF); G_cs->write_ofs2(end_lbl, 0); } else { /* we need a new label */ end_lbl = gen_jump_ahead(OPC_JF); } /* the JF pops an element off the stack */ G_cg->note_pop(); } else { /* this property is required to be defined - this is an error */ G_tok->log_error(TCERR_FOREACH_NO_ISNEXTAVAIL, kw); /* * generate an arbitrary 'end' label if the caller didn't already * create one - we're not going to end up generating valid code * anyway, but since we're not going to abort code generation, * it'll avoid problems elsewhere if we have a valid label assigned */ if (end_lbl == 0) end_lbl = new_label_here(); } /* generate the code to get the next element of the iteration */ if (get_next != 0) { /* get the internal iterator local */ CTcSymLocal::s_gen_code_getlcl(iter_local_id, FALSE); /* generate a call to the property */ CTcPrsNode::s_gen_member_rhs(FALSE, get_next, FALSE, 0, FALSE, FALSE); /* assign the result to the iterator lvalue */ if (lval != 0) lval->gen_code_asi(TRUE, 1, TC_ASI_SIMPLE, "=", 0, FALSE, TRUE, 0); } else { /* this property is required to be defined - this is an error */ G_tok->log_error(TCERR_FOREACH_NO_GETNEXT, kw); } #endif } /* * Generate code for the reinit portion of the 'for'. We do nothing on * this step; we do the test and fetch as one operation in the condition * instead, since the iterator test has to be done ahead of the fetch. */ void CTPNVarIn::gen_forstm_reinit() { /* do nothing on this phase */ } /* ------------------------------------------------------------------------ */ /* * 'for var in from..to' node */ /* * Generate code. This is called during the initializer pharse of the * 'for' that we're part of. We evaluate the 'from' and 'to' expressions, * assign the 'from' to the lvalue, and save the 'to' in a private local * variable. */ void CTPNVarInRange::gen_code(int, int) { /* assign the 'from' expression to the lvalue */ lval_->gen_code_asi(TRUE, 1, TC_ASI_SIMPLE, "=", from_expr_, FALSE, TRUE, 0); /* * if the 'to' expression is non-constant, evaluate it and save it in a * local, so that we don't have to re-evaluate it on each iteration */ if (!to_expr_->is_const()) { /* evaluate the 'to' expression */ to_expr_->gen_code(FALSE, FALSE); /* assign it to our stack local */ CTcSymLocal::s_gen_code_setlcl_stk(to_local_id_, FALSE); } /* * likewise, if there's a non-constant 'step' expression, evaluate it * and store it in our step stack local */ if (step_expr_ != 0 && !step_expr_->is_const()) { /* generate the step expression value */ step_expr_->gen_code(FALSE, FALSE); /* generate the assignment to our local */ CTcSymLocal::s_gen_code_setlcl_stk(step_local_id_, FALSE); /* replace the step expression with the local */ step_expr_ = new CTPNSymResolved(new CTcSymLocal( ".step", 5, FALSE, FALSE, step_local_id_)); } } /* * Generate code for the condition portion of the 'for'. We test our * lvalue to see if we've passed the 'to' limit, in which case we break out * of the loop. */ void CTPNVarInRange::gen_forstm_cond(CTcCodeLabel *endlbl) { /* push the lvalue */ lval_->gen_code(FALSE, FALSE); /* * push the 'to' expression - if it's constant, generate the constant * expression value, otherwise get the local */ if (to_expr_->is_const()) to_expr_->gen_code(FALSE, FALSE); else CTcSymLocal::s_gen_code_getlcl(to_local_id_, FALSE); /* * If the step expression is a constant, compare based on the sign of * the constant expression: if positive, we stop when the lval is * greater than the 'to' limit; if negative, we stop when the lval is * less than to 'to' limit. If the expression isn't a constant, we * have to test the sign at run-time. */ int s = 0; if (step_expr_ == 0) { /* no step expression -> default step is 1, sign is positive */ s = 1; } else if (step_expr_->is_const()) { /* explicit step expression with a constant value - check the type */ switch (step_expr_->get_const_val()->get_type()) { case TC_CVT_INT: /* integer - get its sign */ s = step_expr_->get_const_val()->get_val_int() < 0 ? -1 : 1; break; case TC_CVT_FLOAT: /* BigNumber value - check for a '-' */ s = step_expr_->get_const_val()->get_val_float()[0] == '-' ? -1 : 1; break; default: /* others will have to be interpreted at run-time */ s = 0; break; } } /* * if we have a known sign, generate a fixed test, otherwise generate a * run-time test */ if (s > 0) { /* positive constant - we're looping up to the 'to' limit */ G_cg->write_op(OPC_JGT); G_cs->write_ofs2(endlbl, 0); /* JGT pops 2 */ G_cg->note_pop(2); } else if (s < 0) { /* negative constant - we're looping down to the 'to' limit */ G_cg->write_op(OPC_JLT); G_cs->write_ofs2(endlbl, 0); /* JLT pops 2 */ G_cg->note_pop(2); } else { /* * Variable step - generate a run-time test: * *. ; test the sign of the step expression: if step < 0 goto $1 *. push *. push 0 *. jlt $1 *. ; positive branch: if lval > to goto endlbl else goto $2 *. jgt endlbl *. jmp $2 *. ; negative branch: if lval < to goto endlbl *. $1: *. jlt endlbl *. ; end of test *. $2: */ /* push ; push 0; jlt $1 */ step_expr_->gen_code(FALSE, FALSE); G_cg->write_op(OPC_PUSH_0); G_cg->note_push(); CTcCodeLabel *l1 = gen_jump_ahead(OPC_JLT); G_cg->note_pop(2); /* positive branch: jgt endlbl; jmp $2 */ G_cg->write_op(OPC_JGT); G_cs->write_ofs2(endlbl, 0); CTcCodeLabel *l2 = gen_jump_ahead(OPC_JMP); /* negative branch: $1: jlt endlbl */ def_label_pos(l1); G_cg->write_op(OPC_JLT); G_cs->write_ofs2(endlbl, 0); /* $2: */ def_label_pos(l2); /* * we took one or the other of the positive or negative branch, and * each one did a JxT that popped two elements */ G_cg->note_pop(2); } } /* * Generate code for the reinit portion of the 'for'. We add 'step' to our * lvalue. */ void CTPNVarInRange::gen_forstm_reinit() { /* * If we have a step expression, generate "lval += step". Otherwise * just generate "++lval". */ if (step_expr_ != 0) { /* generate "lval += step" */ CTPNAddAsi_gen_code(lval_, step_expr_, TRUE); } else { /* increment the control variable */ CTPNPreInc_gen_code(lval_, TRUE); } } /* ------------------------------------------------------------------------ */ /* * 'foreach' statement */ /* * generate code */ void CTPNStmForeach::gen_code(int, int) { /* set my local frame if necessary */ CTcPrsSymtab *old_frame = G_cs->set_local_frame(symtab_); /* * add a line record - note that we add the line record after * setting up the local frame, so that the 'for' statement itself * appears within its own inner scope */ add_debug_line_rec(); /* push the enclosing statement */ CTPNStmEnclosing *old_enclosing = G_cs->set_enclosing(this); /* if there's a collection expression, generate it */ if (coll_expr_ != 0) CTPNVarIn::gen_iter_init(coll_expr_, iter_local_id_, "foreach"); /* set the label for the top of the loop */ CTcCodeLabel *top_lbl = new_label_here(); /* check for and fetch the next value */ CTcCodeLabel *end_lbl = 0; CTPNVarIn::gen_iter_cond(iter_expr_, iter_local_id_, end_lbl, "foreach"); /* * set our labels, so that 'break' and 'continue' statements in our * body will know where to go */ break_lbl_ = end_lbl; cont_lbl_ = top_lbl; /* if we have a body, generate it */ if (body_stm_ != 0) gen_code_substm(body_stm_); /* * add another line record - we're now generating code again for the * original 'foreach' line, even though it's after the body */ //$$$ add_debug_line_rec(); /* jump back to the top of the loop */ G_cg->write_op(OPC_JMP); G_cs->write_ofs2(top_lbl, 0); /* * we're at the end of the loop - this is where we jump for 'break' * and when the condition becomes false */ if (end_lbl != 0) def_label_pos(end_lbl); /* restore the enclosing statement */ G_cs->set_enclosing(old_enclosing); /* restore the enclosing local scope */ G_cs->set_local_frame(old_frame); } /* ------------------------------------------------------------------------ */ /* * 'while' statement */ /* * generate code */ void CTPNStmWhile::gen_code(int, int) { CTcCodeLabel *top_lbl; CTcCodeLabel *end_lbl; CTPNStmEnclosing *old_enclosing; /* add a line record */ add_debug_line_rec(); /* push the enclosing statement */ old_enclosing = G_cs->set_enclosing(this); /* set the label for the top of the loop */ top_lbl = new_label_here(); /* generate a label for the end of the loop */ end_lbl = G_cs->new_label_fwd(); /* generate the condition, jumping to the end of the loop if false */ cond_expr_->gen_code_cond(0, end_lbl); /* * set the 'break' and 'continue' label in our node, so that 'break' * and 'continue' statements in subnodes can find the labels during * code generation */ break_lbl_ = end_lbl; cont_lbl_ = top_lbl; /* if we have a body, generate it */ if (body_stm_ != 0) gen_code_substm(body_stm_); /* * add another line record - the jump back to the top of the loop is * part of the 'while' itself */ //$$$ add_debug_line_rec(); /* jump back to the top of the loop */ G_cg->write_op(OPC_JMP); G_cs->write_ofs2(top_lbl, 0); /* * we're at the end of the loop - this is where we jump for 'break' * and when the condition becomes false */ def_label_pos(end_lbl); /* restore the enclosing statement */ G_cs->set_enclosing(old_enclosing); } /* ------------------------------------------------------------------------ */ /* * 'do-while' statement */ /* * generate code */ void CTPNStmDoWhile::gen_code(int, int) { CTcCodeLabel *top_lbl; CTcCodeLabel *end_lbl; CTcCodeLabel *cont_lbl; CTPNStmEnclosing *old_enclosing; /* add a line record */ add_debug_line_rec(); /* push the enclosing statement */ old_enclosing = G_cs->set_enclosing(this); /* set the label for the top of the loop */ top_lbl = new_label_here(); /* create a label for after the loop, for any enclosed 'break's */ end_lbl = G_cs->new_label_fwd(); /* * create a label for just before the expression, for any enclosed * 'continue' statements */ cont_lbl = G_cs->new_label_fwd(); /* set our 'break' and 'continue' labels in our node */ break_lbl_ = end_lbl; cont_lbl_ = cont_lbl; /* if we have a body, generate it */ if (body_stm_ != 0) gen_code_substm(body_stm_); /* set the debug source position to the 'while' clause's location */ add_debug_line_rec(while_desc_, while_linenum_); /* put the 'continue' label here, just before the condition */ def_label_pos(cont_lbl); /* * Generate the condition. If the condition is true, jump back to the * top label; otherwise fall through out of the loop structure. */ cond_expr_->gen_code_cond(top_lbl, 0); /* we're past the end of the loop - this is where we jump for 'break' */ def_label_pos(end_lbl); /* restore the enclosing statement */ G_cs->set_enclosing(old_enclosing); } /* ------------------------------------------------------------------------ */ /* * 'break' statement */ /* * generate code */ void CTPNStmBreak::gen_code(int, int) { /* add a line record */ add_debug_line_rec(); /* * ask the enclosing statement to do the work - if there's no * enclosing statement, or none of the enclosing statements can * perform the break, it's an error */ if (G_cs->get_enclosing() == 0 || !G_cs->get_enclosing()->gen_code_break(lbl_, lbl_len_)) { /* * log the error - if there's a label, the problem is that we * couldn't find the label, otherwise it's that we can't perform * a 'break' here at all */ if (lbl_ == 0) G_tok->log_error(TCERR_INVALID_BREAK); else G_tok->log_error(TCERR_INVALID_BREAK_LBL, (int)lbl_len_, lbl_); } } /* ------------------------------------------------------------------------ */ /* * 'continue' statement */ /* * generate code */ void CTPNStmContinue::gen_code(int, int) { /* add a line record */ add_debug_line_rec(); /* * ask the enclosing statement to do the work - if there's no * enclosing statement, or none of the enclosing statements can * perform the break, it's an error */ if (G_cs->get_enclosing() == 0 || !G_cs->get_enclosing()->gen_code_continue(lbl_, lbl_len_)) { /* * log the error - if there's a label, the problem is that we * couldn't find the label, otherwise it's that we can't perform * a 'break' here at all */ if (lbl_ == 0) G_tok->log_error(TCERR_INVALID_CONTINUE); else G_tok->log_error(TCERR_INVALID_CONT_LBL, (int)lbl_len_, lbl_); } } /* ------------------------------------------------------------------------ */ /* * 'switch' statement */ /* * generate code */ void CTPNStmSwitch::gen_code(int, int) { CTPNStmSwitch *enclosing_switch; int i; char buf[VMB_DATAHOLDER + VMB_UINT2]; CTcCodeLabel *end_lbl; CTPNStmEnclosing *old_enclosing; /* add a line record */ add_debug_line_rec(); /* push the enclosing statement */ old_enclosing = G_cs->set_enclosing(this); /* * Generate the controlling expression. We want to keep the value, * hence 'discard' is false, and we need assignment (not 'for * condition') conversion rules, because we're going to use the * value in direct comparisons */ expr_->gen_code(FALSE, FALSE); /* make myself the current innermost switch */ enclosing_switch = G_cs->set_switch(this); /* * if we can flow out of the switch, allocate a label for the end of * the switch body */ if ((get_control_flow(FALSE) & TCPRS_FLOW_NEXT) != 0) end_lbl = G_cs->new_label_fwd(); else end_lbl = 0; /* the end label is the 'break' location for subnodes */ break_lbl_ = end_lbl; /* * Write my SWITCH opcode, and the placeholder case table. We'll * fill in the case table with its real values as we encounter the * cases in the course of generating the code. For now, all we know * is the number of cases we need to put into the table. */ G_cg->write_op(OPC_SWITCH); /* the SWITCH opcode pops the controlling expression value */ G_cg->note_pop(); /* write the number of cases */ G_cs->write2(case_cnt_); /* * remember where the first case slot is - the 'case' parse nodes * will use this to figure out where to write their slot data */ case_slot_ofs_ = G_cs->get_ofs(); /* * Write the placeholder case slots - each case slot gets a * DATA_HOLDER for the case value, plus an INT2 for the branch * offset. For now, completely zero each case slot. */ memset(buf, 0, VMB_DATAHOLDER + VMB_UINT2); for (i = 0 ; i < case_cnt_ ; ++i) G_cs->write(buf, VMB_DATAHOLDER + VMB_UINT2); /* write a placeholder for the default jump */ if (has_default_) { /* * remember where the 'default' slot is, so that the 'default' * parse node can figure out where to write its branch offset */ default_slot_ofs_ = G_cs->get_ofs(); /* * Write the placeholder for the 'default' slot - this just gets * an INT2 for the 'default' jump offset. As with the case * labels, just zero it for now; we'll fill it in later when we * encounter the 'default' case. */ G_cs->write2(0); } else { /* * there's no default slot, so the 'default' slot is simply a * jump to the end of the switch body - generate a jump ahead to * our end label */ G_cs->write_ofs2(end_lbl, 0); } /* * generate the switch body - this will fill in the case table as we * encounter the 'case' nodes in the parse tree */ if (body_ != 0) gen_code_substm(body_); /* * We're past the body - if we have an end label, set it here. (We * won't have created an end label if control can't flow out of the * switch; this allows us to avoid generating unreachable instructions * after the switch, which would only increase the code size for no * reason.) */ if (end_lbl != 0) def_label_pos(end_lbl); /* restore the enclosing switch */ G_cs->set_switch(enclosing_switch); /* restore the enclosing statement */ G_cs->set_enclosing(old_enclosing); } /* ------------------------------------------------------------------------ */ /* * 'case' label statement */ /* * generate code */ void CTPNStmCase::gen_code(int, int) { ulong slot_ofs; ulong jump_ofs; /* * we must have an active 'switch' statement, and our expression * value must be a constant -- if either of these is not true, we * have an internal error of some kind, because we should never get * this far if these conditions weren't true */ if (G_cs->get_switch() == 0 || !expr_->is_const()) G_tok->throw_internal_error(TCERR_GEN_BAD_CASE); /* allocate our case slot from the enclosing 'switch' statement */ slot_ofs = G_cs->get_switch()->alloc_case_slot(); /* write the case table entry as a DATAHOLDER value */ G_cg->write_const_as_dh(G_cs, slot_ofs, expr_->get_const_val()); /* * Add the jump offset. This is the offset from this INT2 entry in * our case slot to the current output offset. The INT2 is offset * from the start of our slot by the DATAHOLDER value. */ jump_ofs = G_cs->get_ofs() - (slot_ofs + VMB_DATAHOLDER); G_cs->write2_at(slot_ofs + VMB_DATAHOLDER, (int)jump_ofs); /* * because we can jump here (via the case table), we cannot allow * peephole optimizations from past instructions - clear the * peephole */ G_cg->clear_peephole(); /* generate our substatement, if we have one */ if (stm_ != 0) gen_code_substm(stm_); } /* ------------------------------------------------------------------------ */ /* * 'default' label statement */ /* * generate code */ void CTPNStmDefault::gen_code(int, int) { ulong slot_ofs; char buf[VMB_UINT2]; ulong jump_ofs; /* * we must have an active 'switch' statement -- if we don't, we have * an internal error of some kind, because we should never have * gotten this far */ if (G_cs->get_switch() == 0) G_tok->throw_internal_error(TCERR_GEN_BAD_CASE); /* ask the switch where our slot goes */ slot_ofs = G_cs->get_switch()->get_default_slot(); /* * Set the jump offset. This is the offset from our slot entry in * the case table to the current output offset. */ jump_ofs = G_cs->get_ofs() - slot_ofs; oswp2(buf, (int)jump_ofs); /* write our slot entry to the case table */ G_cs->write_at(slot_ofs, buf, VMB_UINT2); /* * because we can jump here (via the case table), we cannot allow * peephole optimizations from past instructions - clear the * peephole */ G_cg->clear_peephole(); /* generate our substatement, if we have one */ if (stm_ != 0) gen_code_substm(stm_); } /* ------------------------------------------------------------------------ */ /* * code label statement */ /* * ininitialize */ CTPNStmLabel::CTPNStmLabel(CTcSymLabel *lbl, CTPNStmEnclosing *enclosing) : CTPNStmLabelBase(lbl, enclosing) { /* * we don't have a 'goto' label yet - we'll allocate it on demand * during code generation (labels are local in scope to a code body * so we can't allocate this until code generation begins for our * containing code body) */ goto_label_ = 0; /* * we don't yet have a 'break' label - we'll allocate this when * someone first refers to it */ break_label_ = 0; } /* * get our code label */ CTcCodeLabel *CTPNStmLabel::get_goto_label() { /* if we don't have a label already, allocate it */ if (goto_label_ == 0) goto_label_ = G_cs->new_label_fwd(); /* return the label */ return goto_label_; } /* * generate code */ void CTPNStmLabel::gen_code(int, int) { CTPNStmEnclosing *old_enclosing; /* push the enclosing statement */ old_enclosing = G_cs->set_enclosing(this); /* * Define our label position - this is where we come if someone does * a 'goto' to this label. (Note that we might not have a 'goto' * label defined yet - if we weren't forward-referenced by a 'goto' * statement, we won't have a label defined. Call get_goto_label() * to ensure we create a label if it doesn't already exist.) */ def_label_pos(get_goto_label()); /* * add the source location of the label - this probably will have no * effect, since we don't generate any code for the label itself, * but it's harmless so do it anyway to guard against weird cases */ add_debug_line_rec(); /* * generate code for the labeled statement, discarding any * calculated value */ if (stm_ != 0) gen_code_substm(stm_); /* * If we have a 'break' label, it means that code within our labeled * statement (i.e., nested within the label) did a 'break' to leave * the labeled statement. The target of the break is the next * statement after the labeled statement, which comes next, so * define the label here. */ if (break_label_ != 0) def_label_pos(break_label_); /* restore the enclosing statement */ G_cs->set_enclosing(old_enclosing); } /* * generate code for a 'break' */ int CTPNStmLabel::gen_code_break(const textchar_t *lbl, size_t lbl_len) { /* * If the 'break' doesn't specify a label, inherit the default * handling, since we're not a default 'break' target. If there's a * label, and the label isn't our label, also inherit the default, * since the target lies somewhere else. */ if (lbl == 0 || G_cs->get_goto_symtab() == 0 || G_cs->get_goto_symtab()->find(lbl, lbl_len) != lbl_) return CTPNStmLabelBase::gen_code_break(lbl, lbl_len); /* * if we don't yet have a 'break' label defined, define one now * (it's a forward declaration, because we won't know where it goes * until we finish generating the entire body of the statement * contained in the label) */ if (break_label_ == 0) break_label_ = G_cs->new_label_fwd(); /* jump to the label */ G_cg->write_op(OPC_JMP); G_cs->write_ofs2(break_label_, 0); /* handled */ return TRUE; } /* * generate code for a 'continue' */ int CTPNStmLabel::gen_code_continue(const textchar_t *lbl, size_t lbl_len) { /* * If there's no label, inherit the default handling, since we're * not a default 'continue' target. If there's a label, and the * label isn't our label, also inherit the default, since the target * lies somewhere else. */ if (lbl == 0 || G_cs->get_goto_symtab() == 0 || G_cs->get_goto_symtab()->find(lbl, lbl_len) != lbl_) return CTPNStmLabelBase::gen_code_continue(lbl, lbl_len); /* * It's a 'continue' with my label - ask my enclosed statement to do * the work; return failure if I have no enclosed statement. Note * that we use a special call - generate a *labeled* continue - to * let the statement know that it must perform the 'continue' * itself and cannot defer to enclosing statements. */ if (stm_ != 0) return stm_->gen_code_labeled_continue(); else return FALSE; } /* ------------------------------------------------------------------------ */ /* * 'try' */ /* * generate code */ void CTPNStmTry::gen_code(int, int) { ulong start_ofs; ulong end_ofs; CTPNStmCatch *cur_catch; CTcCodeLabel *end_lbl; CTPNStmEnclosing *old_enclosing; int finally_never_returns; /* we have no end label yet */ end_lbl = 0; /* * add the source location of the 'try' - it probably won't be * needed, because we don't generate any code before the protected * body, but it's harmless and makes sure we have a good source * location in weird cases */ add_debug_line_rec(); /* push the enclosing statement */ old_enclosing = G_cs->set_enclosing(this); /* * If we have a 'finally' clause, we must allocate a * forward-reference code label for it. We need to be able to reach * the 'finally' clause throughout generation of the protected code * and the 'catch' blocks. */ if (finally_stm_ != 0) finally_lbl_ = G_cs->new_label_fwd(); else finally_lbl_ = 0; /* note where the protected code begins */ start_ofs = G_cs->get_ofs(); /* generate the protected code */ if (body_stm_ != 0) gen_code_substm(body_stm_); /* * Check to see if we have a 'finally' block that never returns. If we * have a 'finally' block, and it doesn't flow to its next statement, * then our LJSR's to the 'finally' block will never return. */ finally_never_returns = (finally_stm_ != 0 && (finally_stm_->get_control_flow(FALSE) & TCPRS_FLOW_NEXT) == 0); /* * if there's a "finally" clause, we must generate a local subroutine * call to the "finally" block */ gen_jsr_finally(); /* * We must now jump past the "catch" and "finally" code blocks. If the * "finally" block itself doesn't flow to the next statement, then * there's no need to do this, since we'll never be reached here. If * there's no "finally" block, then we won't have LJSR'd anywhere, so * this code is definitely reachable. */ if (!finally_never_returns) end_lbl = gen_jump_ahead(OPC_JMP); /* * Note where the protected code ends - it ends at one byte below * the current write offset, because the current write offset is the * next byte we'll write. The code range we store in the exception * table is inclusive of the endpoints. */ end_ofs = G_cs->get_ofs() - 1; /* generate the 'catch' blocks */ for (cur_catch = first_catch_stm_ ; cur_catch != 0 ; cur_catch = cur_catch->get_next_catch()) { /* generate the 'catch' block */ cur_catch->gen_code_catch(start_ofs, end_ofs); /* call the 'finally' block after the 'catch' finishes */ gen_jsr_finally(); /* * If there's a finally block, or there's another 'catch' after me, * generate a jump past the remaining catch/finally blocks. * * If we do have a finally that doesn't flow to the next statement * (i.e., we throw or return out of the finally), then there's no * need to generate a jump, since we'll never come back here from * the finally block. */ if (!finally_never_returns && (finally_stm_ != 0 || cur_catch->get_next_catch() != 0)) { /* * if we have no end label yet, generate one now - we might * not have one because we might not have been able to reach * any previous jump to the end of the catch (because we threw * or returned out of the end of all blocks to this point, for * example) */ if (end_lbl == 0) { /* we have no label - generate a new one now */ end_lbl = gen_jump_ahead(OPC_JMP); } else { /* we already have an end label - generate a jump to it */ G_cg->write_op(OPC_JMP); G_cs->write_ofs2(end_lbl, 0); } } } /* * Restore the enclosing statement. We enclose the protected code * and all of the 'catch' blocks, because all of these must leave * through the 'finally' handler. We do not, however, enclose the * 'finally' handler itself - once it's entered, we do not invoke it * again as it leaves. */ G_cs->set_enclosing(old_enclosing); /* generate the 'finally' block, if we have one */ if (finally_stm_ != 0) { /* * Generate the 'finally' code. The 'finally' block is executed * for the 'try' block plus all of the 'catch' blocks, so the * ending offset is the current position (less one byte, since * the range is inclusive), which encompasses all of the 'catch' * blocks */ finally_stm_->gen_code_finally(start_ofs, G_cs->get_ofs() - 1, this); } /* * we're now past all of the "catch" and "finally" blocks, so we can * define the jump label for jumping past those blocks (we make this * jump from the end of the protected code) - note that we might not * have actually generated the label, since we might never have * reached any code which jumped to it */ if (end_lbl != 0) def_label_pos(end_lbl); } /* * Generate code for a 'break' within our protected code or a 'catch'. * We'll first generate a call to our 'finally' block, if we have one, * then let the enclosing statement handle the break. */ int CTPNStmTry::gen_code_break(const textchar_t *lbl, size_t lbl_len) { /* if we have a 'finally' block, invoke it as a local subroutine call */ gen_jsr_finally(); /* * If there's an enclosing statement, let it generate the break; if * there's not, return failure, because we're not a meaningful * target for break. */ if (enclosing_) return enclosing_->gen_code_break(lbl, lbl_len); else return FALSE; } /* * Generate code for a 'break' within our protected code or a 'catch'. * We'll first generate a call to our 'finally' block, if we have one, * then let the enclosing statement handle the break. */ int CTPNStmTry::gen_code_continue(const textchar_t *lbl, size_t lbl_len) { /* if we have a 'finally' block, invoke it as a local subroutine call */ gen_jsr_finally(); /* * if there's an enclosing statement, let it generate the continue; * if there's not, return failure, because we're not a meaningful * target for continue */ if (enclosing_) return enclosing_->gen_code_continue(lbl, lbl_len); else return FALSE; } /* * Generate a local subroutine call to our 'finally' block, if we have * one. This should be used when executing a break, continue, goto, or * return out of the protected code or a 'catch' block, or when merely * falling off the end of the protected code or 'catch' block. */ void CTPNStmTry::gen_jsr_finally() { /* if we have a 'finally', call it */ if (finally_lbl_ != 0) { /* generate the local subroutine call */ G_cg->write_op(OPC_LJSR); G_cs->write_ofs2(finally_lbl_, 0); /* * the LJSR pushes a value, which is then immediately popped (we * must note the push and pop because it affects our maximum * stack depth requirement) */ G_cg->note_push(); G_cg->note_pop(); /* * whatever follows the LJSR is logically at the end of the * 'finally' block */ add_debug_line_rec(finally_stm_->get_end_desc(), finally_stm_->get_end_linenum()); } } /* ------------------------------------------------------------------------ */ /* * 'catch' */ /* * generate code */ void CTPNStmCatch::gen_code(int, int) { /* this can't be called directly - use gen_code_catch() instead */ G_tok->throw_internal_error(TCERR_CATCH_FINALLY_GEN_CODE); } /* * generate code for the 'catch' */ void CTPNStmCatch::gen_code_catch(ulong start_prot_ofs, ulong end_prot_ofs) { CTcSymbol *sym; ulong exc_obj_id; CTcPrsSymtab *old_frame; /* add the source location of the 'catch' clause */ add_debug_line_rec(); /* set the local scope */ old_frame = G_cs->set_local_frame(symtab_); /* look up the object defining the class of exceptions to catch */ sym = G_cs->get_symtab()->find_or_def_undef(exc_class_, exc_class_len_, FALSE); /* assume we won't find a valid object ID */ exc_obj_id = VM_INVALID_OBJ; /* if it's an object, get its ID */ if (sym->get_type() == TC_SYM_OBJ) { /* get its object ID */ exc_obj_id = ((CTcSymObj *)sym)->get_obj_id(); } else if (sym->get_type() != TC_SYM_UNKNOWN) { /* * it's defined, but it's not an object - log an error (note * that we don't log an error if the symbol is undefined, * because find_or_def_undef() will already have logged an error * for us) */ log_error(TCERR_CATCH_EXC_NOT_OBJ, (int)exc_class_len_, exc_class_); } /* add our exception table entry */ G_cg->get_exc_table()->add_catch(start_prot_ofs, end_prot_ofs, exc_obj_id, G_cs->get_ofs()); /* don't allow any peephole optimizations to affect this offset */ G_cg->clear_peephole(); /* * the VM automatically pushes a value onto the stack to perform the * 'catch' */ G_cg->note_push(); /* * generate a SETLCL for our formal parameter, so that the exception * object is stored in our local variable */ exc_var_->gen_code_setlcl(); /* generate code for our statement, if we have one */ if (body_ != 0) gen_code_substm(body_); /* restore the enclosing local scope */ G_cs->set_local_frame(old_frame); } /* ------------------------------------------------------------------------ */ /* * 'finally' */ /* * generate code */ void CTPNStmFinally::gen_code(int, int) { /* this can't be called directly - use gen_code_finally() instead */ G_tok->throw_internal_error(TCERR_CATCH_FINALLY_GEN_CODE); } /* * generate code for the 'finally' */ void CTPNStmFinally::gen_code_finally(ulong start_prot_ofs, ulong end_prot_ofs, CTPNStmTry *try_stm) { CTPNStmEnclosing *old_enclosing; /* * set the source location for our prolog code to the 'finally' * clause's start */ add_debug_line_rec(); /* push the enclosing statement */ old_enclosing = G_cs->set_enclosing(this); /* * add our exception table entry - use the invalid object ID as a * special flag to indicate that we catch all exceptions */ G_cg->get_exc_table()->add_catch(start_prot_ofs, end_prot_ofs, VM_INVALID_OBJ, G_cs->get_ofs()); /* don't allow any peephole optimizations to affect this offset */ G_cg->clear_peephole(); /* the VM pushes the exception onto the stack before calling us */ G_cg->note_push(); /* * When we get called due to an exception, we want to run the * 'finally' code block and then re-throw the exception. First, store * the exception parameter in our special local stack slot that we * allocated specifically for the purpose of being a temporary holder * for this value. */ CTcSymLocal::s_gen_code_setlcl_stk(exc_local_id_, FALSE); /* call the 'finally' block */ try_stm->gen_jsr_finally(); /* * After the 'finally' block returns, we must re-throw the * exception. Retrieve the contents of our local where we stashed * the exception object and re-throw the exception. */ CTcSymLocal::s_gen_code_getlcl(exc_local_id_, FALSE); /* re-throw the exception - this pops the exception object */ G_cg->write_op(OPC_THROW); G_cg->note_pop(); /* * set the source location to the 'finally' clause once again, since * we changed the source location in the course of generating the * catch-all handler */ add_debug_line_rec(); /* this is where the 'finally' code block begins - define our label */ def_label_pos(try_stm->get_finally_lbl()); /* * The 'finally' block is the target of LJSR instructions, since we * must run the 'finally' block's code from numerous code paths. * The first thing we must do is pop the return address and stash it * in a local variable. (We note an extra push, since the LJSR will * have pushed the value before transferring control here.) */ G_cg->note_push(); CTcSymLocal::s_gen_code_setlcl_stk(jsr_local_id_, FALSE); /* generate the code block, if there is one */ if (body_ != 0) gen_code_substm(body_); /* return from the 'finally' subroutine */ G_cg->write_op(OPC_LRET); G_cs->write2(jsr_local_id_); /* restore the enclosing statement */ G_cs->set_enclosing(old_enclosing); } /* * It is not legal to enter a 'finally' block via a 'goto' statement, * because there is no valid way to exit the 'finally' block in this * case. */ int CTPNStmFinally::check_enter_by_goto(CTPNStmGoto *goto_stm, CTPNStmLabel *) { /* this is illegal - log an error */ goto_stm->log_error(TCERR_GOTO_INTO_FINALLY); /* indicate that it's not allowed */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * 'throw' statement */ /* * generate code */ void CTPNStmThrow::gen_code(int, int) { /* add a line record */ add_debug_line_rec(); /* * generate our expression - we use the result (discard = false), * and we are effectively assigning the result, so we can't use the * 'for condition' rules */ expr_->gen_code(FALSE, FALSE); /* generate the 'throw' */ G_cg->write_op(OPC_THROW); /* 'throw' pops the expression from the stack */ G_cg->note_pop(); } /* ------------------------------------------------------------------------ */ /* * 'goto' statement */ /* * generate code */ void CTPNStmGoto::gen_code(int, int) { CTcSymbol *sym; CTPNStmLabel *label_stm; /* add a line record */ add_debug_line_rec(); /* * look up our label symbol in the 'goto' table for the function, * and get the label statement node from the label */ if (G_cs->get_goto_symtab() == 0 || (sym = G_cs->get_goto_symtab()->find(lbl_, lbl_len_)) == 0 || sym->get_type() != TC_SYM_LABEL || (label_stm = ((CTcSymLabel *)sym)->get_stm()) == 0) { /* log an error */ log_error(TCERR_INVALID_GOTO_LBL, (int)lbl_len_, lbl_); /* give up */ return; } /* * Tell any enclosing statements to unwind their 'try' blocks for a * transfer to the given label. We only need to go as far as the * most deeply nested enclosing statement we have in common with the * label, because we'll be transferring control entirely within the * confines of that enclosing statement. */ if (G_cs->get_enclosing() != 0) { /* generate the unwinding code */ G_cs->get_enclosing()->gen_code_unwind_for_goto(this, label_stm); } else { CTPNStmEnclosing *enc; /* * The 'goto' isn't enclosed in any statements. This means that * we are entering every block that contains the target label. * Some blocks don't allow entering via 'goto', so we must check * at this point to see if any of the enclosing blocks are * problematic. */ for (enc = label_stm ; enc != 0 ; enc = enc->get_enclosing()) { /* * make sure we're allowed to enter this statement - if not, * stop scanning, so that we display only one such error */ if (!enc->check_enter_by_goto(this, label_stm)) break; } } /* generate a jump to the label */ G_cg->write_op(OPC_JMP); G_cs->write_ofs2(label_stm->get_goto_label(), 0); } /* ------------------------------------------------------------------------ */ /* * Generic enclosing statement node */ /* * Generate code for a break, given the target code label object and the * target label symbol, if any. This can be used for any of the looping * statement types. */ int CTPNStmEnclosing::gen_code_break_loop(CTcCodeLabel *code_label, const textchar_t *lbl, size_t lbl_len) { /* * If the statement is labeled, let the enclosing statement handle * it -- since it's labeled, we can't assume the statement refers to * us without searching for the enclosing label. */ if (lbl != 0) { /* if there's an enclosing statement, let it handle it */ if (enclosing_ != 0) return enclosing_->gen_code_break(lbl, lbl_len); /* * there's no enclosing statement, and we can't handle this * because it has an explicit label attached - indicate that no * break has been generated and give up */ return FALSE; } /* * It's unlabeled, so we can take it by default as the nearest * enclosing statement for which 'break' makes sense -- generate the * jump to the given code label. */ G_cg->write_op(OPC_JMP); G_cs->write_ofs2(code_label, 0); /* we have generated the break */ return TRUE; } /* * Generate code for a continue, given the target code label object and * the target label symbol, if any. This can be used for any of the * looping statement types. */ int CTPNStmEnclosing::gen_code_continue_loop(CTcCodeLabel *code_label, const textchar_t *lbl, size_t lbl_len) { /* * If the statement is labeled, let the enclosing statement handle * it -- since it's labeled, we can't assume the statement refers to * us without searching for the enclosing label. */ if (lbl != 0) { /* if there's an enclosing statement, let it handle it */ if (enclosing_ != 0) return enclosing_->gen_code_continue(lbl, lbl_len); /* * there's no enclosing statement, and we can't handle this * because it has an explicit label attached - indicate that no * continue has been generated and give up */ return FALSE; } /* * it's unlabeled, so we can take it by default as the nearest * enclosing statement for which 'continue' makes sense -- generate * the jump to the given code label */ G_cg->write_op(OPC_JMP); G_cs->write_ofs2(code_label, 0); /* we have generated the continue */ return TRUE; } /* * Generate the code necessary to unwind the stack for executing a * 'goto' to the given labeled statement> */ void CTPNStmEnclosing::gen_code_unwind_for_goto(CTPNStmGoto *goto_stm, CTPNStmLabel *target) { CTPNStmEnclosing *enc; /* * Detmerine if the target statement is enclosed within this * statement. If it is, we do not need to unwind from this * statement or any of its enclosing statements, because control * will remain within this statement. * * To make this determination, start at the target label and search * up its list of enclosing statements. If we reach 'this', we know * that we enclose the target. If we reach the outermost enclosing * statement, we know that we do not enclose the taret. */ for (enc = target ; enc != 0 ; enc = enc->get_enclosing()) { /* * if we found ourself in the list of enclosing statements * around the target label, the label is contained within us, * hence we do not need to generate any code to leave, because * we're not leaving - simply return immediately without looking * any further */ if (enc == this) { /* * 'this' is the common ancestor of both the 'goto' and the * target label, so we're not transferring control in or out * of 'this' or any enclosing statement. However, we are * transfering control IN through all of the statements that * enclose the label up to but not including 'this'. * * Some types of statements do not allow control transfers * in to enclosed labels - in particular, we can't use * 'goto' to transfer control into a 'finally' clause. * Check all statements that enclose the label up to but not * including 'this', and make sure they will allow a * transfer in. * * Note that we make this check now, only after we've found * the common ancestor, because we can't tell if we're * actually entering any blocks until we find the common * ancestor. */ for (enc = target ; enc != 0 && enc != this ; enc = enc->get_enclosing()) { /* make sure we're allowed to enter this statement */ if (!enc->check_enter_by_goto(goto_stm, target)) break; } /* * we're not transferring out of this statement or any * enclosing statement, since the source 'goto' and the * target label are both contained within 'this' - we're * done unwinding for the transfer */ return; } } /* generate code to transfer out of this statement */ gen_code_for_transfer_out(); /* check for an enclosing statement */ if (enclosing_ != 0) { /* * We are enclosed by another statement or statements. This * means that we haven't found a common ancestor yet, so we * might be leaving the enclosing block as well - continue on to * our enclosing statement. */ enclosing_->gen_code_unwind_for_goto(goto_stm, target); } else { /* * This is the outermost significant enclosing statement, which * means that we are transferring control into a completely * unrelated block. As a result, we will enter every statement * that encloses the target label. * * We must check each block we're entering to see if it allows * entry by 'goto' statements. Since we now know there is no * common ancestor, and thus that we're entering every block * enclosing the target label, we must check every block * enclosing the target label to see if they allow transfers in * via 'goto' statements. */ for (enc = target ; enc != 0 ; enc = enc->get_enclosing()) { /* make sure we're allowed to enter this statement */ if (!enc->check_enter_by_goto(goto_stm, target)) break; } } } /* ------------------------------------------------------------------------ */ /* * Anonymous function */ /* * generate code */ void CTPNAnonFunc::gen_code(int discard, int) { /* if we're discarding the value, don't bother generating the code */ if (discard) return; /* * Push each context object - these are the additional arguments to * the anonymous function pointer object's constructor beyond the * function pointer itself. Note that we must push the arguments in * reverse order of our list, since arguments are always pushed from * last to first. */ int argc = 0; for (CTcCodeBodyCtx *cur_ctx = code_body_->get_ctx_tail() ; cur_ctx != 0 ; cur_ctx = cur_ctx->prv_, ++argc) { /* * find our context matching this context - the caller's * contexts are all one level lower than the callee's contexts, * because the caller is at the next recursion level out */ int our_varnum; if (!G_cs->get_code_body() ->get_ctx_var_for_level(cur_ctx->level_ - 1, &our_varnum)) { /* this should never happen */ assert(FALSE); } /* * push this context object - to do this, simply retrieve the * value of the local variable in our frame that contains this * context level */ CTcSymLocal::s_gen_code_getlcl(our_varnum, FALSE); } /* * The first argument (and thus last pushed) to the constructor is the * constant function pointer that refers to the code of the anonymous * function. * * For regular static compilation, the function pointer is a simple * code offset constant. For run-time compilation (in the debugger or * interpreter "eval()" facility), it's a DynamicFunc representing the * dynamically compiled code. */ if (G_cg->is_eval_for_dyn()) { /* * Dynamic (run-time) compilation - the bytecode is in a * dynamically created DynamicFunc instance. The dynamic compiler * assigns object IDs for the anonymous function code body before * generating any code for any referencing object, so we simply * generate a PUSHOBJ for the code body's object ID. */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(code_body_->get_dyn_obj_id()); } else { /* conventional static compiler - the bytecode is in the code pool */ G_cg->write_op(OPC_PUSHFNPTR); code_body_->add_abs_fixup(G_cs); G_cs->write4(0); } /* count the argument */ ++argc; /* note the push of the function pointer argument */ G_cg->note_push(); /* * If we have a local context, we need to create an anonymous function * metaclass object that combines the function pointer with the local * context. If we don't have a local context, this is a simple static * function that we can call directly, in which case we're already done * since we've pushed the function pointer. */ if (argc > 1 || code_body_->has_local_ctx()) { /* * We have a local context, so we need create to create the * anonymous function metaclass object. Generate the NEW. */ if (argc <= 255) { G_cg->write_op(OPC_NEW1); G_cs->write((char)argc); G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_ANONFN)); } else { G_cg->write_op(OPC_NEW2); G_cs->write2(argc); G_cs->write2(G_cg->get_predef_meta_idx(TCT3_METAID_ANONFN)); } /* push the object value */ G_cg->write_op(OPC_GETR0); /* the 'new' popped the arguments, then we pushed the result */ G_cg->note_pop(argc - 1); } } /* ------------------------------------------------------------------------ */ /* * Implicit constructor */ void CTPNStmImplicitCtor::gen_code(int /*discard*/, int /*for_condition*/) { /* * Generate a call to inherit each superclass constructor. Pass the * same argument list we received by expanding the varargs list * parameter in local 0. */ for (CTPNSuperclass *sc = obj_stm_->get_first_sc() ; sc != 0 ; sc = sc->nxt_) { /* * if this one is valid, generate code to call its constructor - * it's valid if it has an object symbol */ CTcSymObj *sc_sym = (CTcSymObj *)sc->get_sym(); if (sc_sym != 0 && sc_sym->get_type() == TC_SYM_OBJ) { /* push the argument counter so far (no other arguments) */ G_cg->write_op(OPC_PUSH_0); G_cg->note_push(); /* get the varargs list local */ CTcSymLocal::s_gen_code_getlcl(0, FALSE); /* convert it to varargs */ G_cg->write_op(OPC_MAKELSTPAR); /* note the extra push and pop for the argument count */ G_cg->note_push(); G_cg->note_pop(); /* it's a varargs call */ G_cg->write_op(OPC_VARARGC); /* generate an EXPINHERIT to this superclass */ G_cg->write_op(OPC_EXPINHERIT); G_cs->write(0); /* varargs -> argc ignored */ G_cs->write_prop_id(G_prs->get_constructor_prop()); G_cs->write_obj_id(sc_sym->get_obj_id()); /* * this removes arguments (the varargs list variable and * argument count) */ G_cg->note_pop(2); } } } /* ------------------------------------------------------------------------ */ /* * Object Property list entry - value node */ void CTPNObjProp::gen_code(int, int) { vm_val_t val; char buf[VMB_DATAHOLDER]; /* get the correct data stream */ CTcDataStream *str = objdef_->get_obj_sym()->get_stream(); /* set the current source location for error reporting */ G_tok->set_line_info(file_, linenum_); /* generate code for our expression or our code body, as appropriate */ if (code_body_ != 0) { /* if this is a constructor, mark the code body accordingly */ if (prop_sym_->get_prop() == G_prs->get_constructor_prop()) code_body_->set_constructor(TRUE); /* if it's static, do some extra work */ if (is_static_) { /* mark the code body as static */ code_body_->set_static(); /* * add the obj.prop to the static ID stream, so the VM knows to * invoke this initializer at start-up */ G_static_init_id_stream ->write_obj_id(objdef_->get_obj_sym()->get_obj_id()); G_static_init_id_stream ->write_prop_id(prop_sym_->get_prop()); } /* tell our code body to generate the code */ code_body_->gen_code(FALSE, FALSE); /* * Set up our code offset value. Write a code offset of zero for * now, since we won't know the correct offset until link time. */ val.set_codeofs(0); /* * Add a fixup to the code body's fixup list for our dataholder, so * that we fix up the property value when we link. Note that the * fixup is one byte into our object stream from the current * offset, because the first byte is the type. */ CTcAbsFixup::add_abs_fixup(code_body_->get_fixup_list_head(), str, str->get_ofs() + 1); /* write out our value in DATAHOLDER format */ vmb_put_dh(buf, &val); str->write(buf, VMB_DATAHOLDER); } else if (expr_ != 0) { /* * if my value is constant, write out a dataholder for the constant * value to the stream; otherwise, write out our code and store a * pointer to the code */ if (expr_->is_const()) { /* write the constant value to the object stream */ G_cg->write_const_as_dh(str, str->get_ofs(), expr_->get_const_val()); } else if (expr_->is_dstring()) { /* it's a double-quoted string node */ CTPNDstr *dstr = (CTPNDstr *)expr_; /* * Add the string to the constant pool. Note that the fixup * will be one byte from the current object stream offset, * since we need to write the type byte first. */ G_cg->add_const_str(dstr->get_str(), dstr->get_str_len(), str, str->get_ofs() + 1); /* * Set up the dstring value. Use a zero placeholder for now; * add_const_str() already added a fixup for us that will * supply the correct value at link time. */ val.set_dstring(0); vmb_put_dh(buf, &val); str->write(buf, VMB_DATAHOLDER); } else { /* we should never get here */ G_tok->throw_internal_error(TCERR_INVAL_PROP_CODE_GEN); } } } /* * Generate code for an inline object instantiation */ void CTPNObjProp::gen_code_inline_obj() { /* set the current source location for error reporting */ G_tok->set_line_info(file_, linenum_); /* generate code for our expression or our code body, as appropriate */ if (inline_method_ != 0) { /* * Code as an in-line method. This means that the code was * specified as an explicit method in braces; we compile it as an * anonymous method so that it can access locals in enclosing * scopes. Generate the code to construct the closure object, then * call obj.setMethod(prop, anonMethod) to bind it as a method of * the object. This will make it act like a regular method, in * that evaluating the property will call the method. */ inline_method_->gen_code(FALSE, FALSE); /* generate the setMethod */ gen_setMethod(); } else if (expr_ != 0) { /* check the type of expression */ if (expr_->is_dstring()) { /* * Double-quoted string - generate obj.setMethod(prop, string) */ /* push the string contents as a regular sstring constant */ CTPNDstr *dstr = (CTPNDstr *)expr_; CTPNConst::s_gen_code_str(dstr->get_str(), dstr->get_str_len()); /* get the setMethod property */ gen_setMethod(); } else { /* * Expression, constant or live. Generate the code to evaluate * the expression; this runs at the moment of evaluating the * overall inline object expression, so we caputre the value of * the expression at the time we create the inline object * instance (and fix the value at that point - this isn't a * method that's invoked when the property is evaluated). */ /* push the new object referene */ G_cg->write_op(OPC_DUP); G_cg->note_push(); /* it's a constant value - do a setprop; start with my value */ expr_->gen_code(FALSE, FALSE); /* if it doesn't have a return value, set the property to nil */ if (!expr_->has_return_value()) { G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); } /* get the object and value into the proper order */ G_cg->write_op(OPC_SWAP); /* generate the setprop for . = */ G_cg->write_op(OPC_SETPROP); G_cs->write_prop_id(prop_sym_->get_prop()); G_cg->note_pop(2); } } } /* * Generate a setMethod call. The caller must push the value for the * setMethod, and the object ID must be on the stack just above that. */ void CTPNObjProp::gen_setMethod() { /* look up the setMethod property */ CTcSymProp *setm = (CTcSymProp *)G_prs->get_global_symtab()->find( "setMethod", 9); if (setm == 0 || setm->get_type() != TC_SYM_PROP) { G_tok->log_error(TCERR_SYM_NOT_PROP, 9, "setMethod"); return; } /* push the property ID */ G_cg->write_op(OPC_PUSHPROPID); G_cs->write_prop_id(prop_sym_->get_prop()); G_cg->note_push(); /* re-push the object reference (it's on the stack above the value) */ G_cg->write_op(OPC_GETSPN); G_cs->write(2); G_cg->note_push(); /* generate .setMethod(, string) */ G_cg->write_op(OPC_CALLPROP); G_cs->write(2); G_cs->write_prop_id(setm->get_prop()); G_cg->note_pop(3); } /* ------------------------------------------------------------------------ */ /* * Inline object definition */ /* * Generate code. An inline object definition creates a new instance of * the object on each evaluation, and creates a new instance of each * anonymous method contained in the property list. The result of the * expression is the new object reference. */ void CTPNInlineObject::gen_code(int discard, int for_condition) { /* * First, create the object via TadsObject.createInstanceOf(), with the * list of superclasses as the arguments. Use the no-constructor form * of the call, since we'll invoke the new object's construct() method * directly instead. */ /* * push the superclass references, from last to first (standard * right-to-left order for the method arguments) */ int sccnt = 0; for (CTPNSuperclass *sc = sclist_.tail_ ; sc != 0 ; sc = sc->prv_, ++sccnt) sc->get_sym()->gen_code(FALSE); /* look up TadsObject */ CTcPrsSymtab *tab = G_prs->get_global_symtab(); CTcSymMetaclass *tadsobj = (CTcSymMetaclass *)tab->find("TadsObject", 10); if (tadsobj == 0 || tadsobj->get_type() != TC_SYM_METACLASS) { G_tok->log_error(TCERR_UNDEF_METACLASS, 10, "TadsObject"); return; } /* look up createInstanceOf */ const char *cioname = "createInstanceOf"; size_t ciolen = 16; CTcSymProp *cio = (CTcSymProp *)tab->find(cioname, ciolen); if (cio == 0 || cio->get_type() != TC_SYM_PROP) { G_tok->log_error(TCERR_SYM_NOT_PROP, (int)ciolen, cioname); return; } /* call TadsObject.createInstanceOf() */ G_cg->write_op(OPC_OBJCALLPROP); G_cs->write((char)sccnt); G_cs->write_obj_id(tadsobj->get_class_obj()); G_cs->write_prop_id(cio->get_prop()); G_cg->note_pop(sccnt); /* push the new object reference as the result of the overall expression */ G_cg->write_op(OPC_GETR0); G_cg->note_push(); /* assume we won't find a constructor among the properties */ int has_ctor = FALSE; /* install each property or method */ for (CTPNObjProp *prop = proplist_.first_ ; prop != 0 ; prop = prop->get_next_prop()) { /* generate the property/method setup */ prop->gen_code_inline_obj(); /* note if it's a constructor */ if (prop->get_prop_sym()->get_prop() == G_prs->get_constructor_prop()) has_ctor = TRUE; } /* if there's an explicit constructor defined, call it */ if (has_ctor) { G_cg->write_op(OPC_DUP); G_cg->write_op(OPC_CALLPROP); G_cs->write(0); G_cs->write_prop_id(G_prs->get_constructor_prop()); } } qtads-2.1.7/tads3/tct3ty.h000066400000000000000000000016051265017072300152630ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/TCT3TY.H,v 1.1 1999/07/11 00:46:57 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3ty.h - T3 Target-specific Type definitions Function Notes Modified 07/09/99 MJRoberts - Creation */ #ifndef TCT3TY_H #define TCT3TY_H #include "vmtype.h" /* ------------------------------------------------------------------------ */ /* * target type specifications */ /* global object ID */ typedef vm_obj_id_t tctarg_obj_id_t; /* global property ID */ typedef vm_prop_id_t tctarg_prop_id_t; /* invalid object/property ID's */ const tctarg_obj_id_t TCTARG_INVALID_OBJ = VM_INVALID_OBJ; const tctarg_prop_id_t TCTARG_INVALID_PROP = VM_INVALID_PROP; #endif /* TCT3TY_H */ qtads-2.1.7/tads3/tct3unas.cpp000066400000000000000000000573611265017072300161420ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/TCT3UNAS.CPP,v 1.3 1999/07/11 00:46:57 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3unas.cpp - TADS 3 Compiler - T3 Unassembler Function Notes Modified 05/10/99 MJRoberts - Creation */ #include "t3std.h" #include "vmop.h" #include "tcunas.h" #include "tct3unas.h" #include "vmtype.h" /* ------------------------------------------------------------------------ */ /* * disassemble a code stream */ void CTcT3Unasm::disasm(CTcUnasSrc *src, CTcUnasOut *out) { /* keep going until we run out of source material */ for (;;) { char ch; /* get the next byte; stop if we've reached the end of the source */ if (src->next_byte(&ch)) break; /* disassemble this instruction */ disasm_instr(src, out, ch); } } /* ------------------------------------------------------------------------ */ /* * T3 Instruction Information Array */ t3_instr_info_t CTcT3Unasm::instr_info[] = { /* 0x00 */ { "db 0", 0,}, /* 0x01 */ { "push_0", 0 }, /* 0x02 */ { "push_1", 0 }, /* 0x03 */ { "pushint8", 1, { T3OP_TYPE_8S }}, /* 0x04 */ { "pushint", 1, { T3OP_TYPE_32S }}, /* 0x05 */ { "pushstr", 1, { T3OP_TYPE_STR }}, /* 0x06 */ { "pushlst", 1, { T3OP_TYPE_LIST }}, /* 0x07 */ { "pushobj", 1, { T3OP_TYPE_OBJ }}, /* 0x08 */ { "pushnil", 0 }, /* 0x09 */ { "pushtrue", 0 }, /* 0x0a */ { "pushpropid", 1, { T3OP_TYPE_PROP }}, /* 0x0b */ { "pushfnptr", 1, { T3OP_TYPE_CODE_ABS }}, /* 0x0c */ { "pushstri", 1, { T3OP_TYPE_INLINE_STR }}, /* 0x0d */ { "pushparlst", 1, { T3OP_TYPE_8U }}, /* 0x0e */ { "makelstpar", 0 }, /* 0x0f */ { "pushenum", 1, { T3OP_TYPE_ENUM }}, /* 0x10 */ { "pushbifptr", 2, { T3OP_TYPE_16U, T3OP_TYPE_16U }}, /* 0x11 */ { "db 0x11", 0 }, /* 0x12 */ { "db 0x12", 0 }, /* 0x13 */ { "db 0x13", 0 }, /* 0x14 */ { "db 0x14", 0 }, /* 0x15 */ { "db 0x15", 0 }, /* 0x16 */ { "db 0x16", 0 }, /* 0x17 */ { "db 0x17", 0 }, /* 0x18 */ { "db 0x18", 0 }, /* 0x19 */ { "db 0x19", 0 }, /* 0x1a */ { "db 0x1a", 0 }, /* 0x1b */ { "db 0x1b", 0 }, /* 0x1c */ { "db 0x1c", 0 }, /* 0x1d */ { "db 0x1d", 0 }, /* 0x1e */ { "db 0x1e", 0 }, /* 0x1f */ { "db 0x1f", 0 }, /* 0x20 */ { "neg", 0 }, /* 0x21 */ { "bnot", 0 }, /* 0x22 */ { "add", 0 }, /* 0x23 */ { "sub", 0 }, /* 0x24 */ { "mul", 0 }, /* 0x25 */ { "band", 0 }, /* 0x26 */ { "bor", 0 }, /* 0x27 */ { "shl", 0 }, /* 0x28 */ { "shr", 0 }, /* 0x29 */ { "xor", 0 }, /* 0x2a */ { "div", 0 }, /* 0x2b */ { "mod", 0 }, /* 0x2c */ { "not", 0 }, /* 0x2d */ { "boolize", 0 }, /* 0x2e */ { "inc", 0 }, /* 0x2f */ { "dec", 0 }, /* 0x30 */ { "db 0x30", 0 }, /* 0x31 */ { "db 0x31", 0 }, /* 0x32 */ { "db 0x32", 0 }, /* 0x33 */ { "db 0x33", 0 }, /* 0x34 */ { "db 0x34", 0 }, /* 0x35 */ { "db 0x35", 0 }, /* 0x36 */ { "db 0x36", 0 }, /* 0x37 */ { "db 0x37", 0 }, /* 0x38 */ { "db 0x38", 0 }, /* 0x39 */ { "db 0x39", 0 }, /* 0x3a */ { "db 0x3a", 0 }, /* 0x3b */ { "db 0x3b", 0 }, /* 0x3c */ { "db 0x3c", 0 }, /* 0x3d */ { "db 0x3d", 0 }, /* 0x3e */ { "db 0x3e", 0 }, /* 0x3f */ { "db 0x3f", 0 }, /* 0x40 */ { "eq", 0 }, /* 0x41 */ { "ne", 0 }, /* 0x42 */ { "lt", 0 }, /* 0x43 */ { "le", 0 }, /* 0x44 */ { "gt", 0 }, /* 0x45 */ { "ge", 0 }, /* 0x46 */ { "db 0x46", 0 }, /* 0x47 */ { "db 0x47", 0 }, /* 0x48 */ { "db 0x48", 0 }, /* 0x49 */ { "db 0x49", 0 }, /* 0x4a */ { "db 0x4a", 0 }, /* 0x4b */ { "db 0x4b", 0 }, /* 0x4c */ { "db 0x4c", 0 }, /* 0x4d */ { "db 0x4d", 0 }, /* 0x4e */ { "db 0x4e", 0 }, /* 0x4f */ { "db 0x4f", 0 }, /* 0x50 */ { "retval", 0 }, /* 0x51 */ { "retnil", 0 }, /* 0x52 */ { "rettrue", 0 }, /* 0x53 */ { "db 0x53", 0 }, /* 0x54 */ { "ret", 0 }, /* 0x55 */ { "db 0x55", 0 }, /* 0x56 */ { "namedargptr", 2, { T3OP_TYPE_8U, T3OP_TYPE_16U }}, /* 0x57 */ { "namedargtab", 0 }, /* varying-length operands */ /* 0x58 */ { "call", 2, { T3OP_TYPE_8U, T3OP_TYPE_CODE_ABS }}, /* 0x59 */ { "ptrcall", 1, { T3OP_TYPE_8U }}, /* 0x5a */ { "db 0x5a", 0 }, /* 0x5b */ { "db 0x5b", 0 }, /* 0x5c */ { "db 0x5c", 0 }, /* 0x5d */ { "db 0x5d", 0 }, /* 0x5e */ { "db 0x5e", 0 }, /* 0x5f */ { "db 0x5f", 0 }, /* 0x60 */ { "getprop", 1, { T3OP_TYPE_PROP }}, /* 0x61 */ { "callprop", 2, { T3OP_TYPE_8U, T3OP_TYPE_PROP }}, /* 0x62 */ { "ptrcallprop", 1, { T3OP_TYPE_8U }}, /* 0x63 */ { "getpropself", 1, { T3OP_TYPE_PROP }}, /* 0x64 */ { "callpropself", 2, { T3OP_TYPE_8U, T3OP_TYPE_PROP }}, /* 0x65 */ { "ptrcallpropself", 1, { T3OP_TYPE_8U }}, /* 0x66 */ { "objgetprop", 2, { T3OP_TYPE_OBJ, T3OP_TYPE_PROP }}, /* 0x67 */ { "objcallprop", 3, { T3OP_TYPE_8U, T3OP_TYPE_OBJ, T3OP_TYPE_PROP }}, /* 0x68 */ { "getpropdata", 1, { T3OP_TYPE_PROP }}, /* 0x69 */ { "ptrgetpropdata", 0 }, /* 0x6a */ { "getproplcl1", 2, { T3OP_TYPE_8U, T3OP_TYPE_PROP }}, /* 0x6b */ { "callproplcl1", 3, { T3OP_TYPE_8U, T3OP_TYPE_8U, T3OP_TYPE_PROP }}, /* 0x6c */ { "getpropr0", 1, { T3OP_TYPE_PROP }}, /* 0x6d */ { "callpropr0", 2, { T3OP_TYPE_8U, T3OP_TYPE_PROP }}, /* 0x6e */ { "db 0x6e", 0 }, /* 0x6f */ { "db 0x6f", 0 }, /* 0x70 */ { "db 0x70", 0 }, /* 0x71 */ { "db 0x71", 0 }, /* 0x72 */ { "inherit", 2, { T3OP_TYPE_8U, T3OP_TYPE_PROP }}, /* 0x73 */ { "ptrinherit", 1, { T3OP_TYPE_8U }}, /* 0x74 */ { "expinherit", 3, { T3OP_TYPE_8U, T3OP_TYPE_PROP, T3OP_TYPE_OBJ }}, /* 0x75 */ { "ptrexpinherit", 2, { T3OP_TYPE_8U, T3OP_TYPE_OBJ }}, /* 0x76 */ { "varargc", 0 }, /* 0x77 */ { "delegate", 2, { T3OP_TYPE_8U, T3OP_TYPE_PROP }}, /* 0x78 */ { "ptrdelegate", 1, { T3OP_TYPE_PROP }}, /* 0x79 */ { "db 0x79", 0 }, /* 0x7a */ { "swap2", 0 }, /* 0x7b */ { "swapn", 2, { T3OP_TYPE_8U, T3OP_TYPE_8U }}, /* 0x7c */ { "getargn0", 0 }, /* 0x7d */ { "argargn1", 0 }, /* 0x7e */ { "getargn2", 0 }, /* 0x7f */ { "getargn3", 0 }, /* 0x80 */ { "getlcl1", 1, { T3OP_TYPE_8U }}, /* 0x81 */ { "getlcl2", 1, { T3OP_TYPE_16U }}, /* 0x82 */ { "getarg1", 1, { T3OP_TYPE_8U }}, /* 0x83 */ { "getarg2", 1, { T3OP_TYPE_16U }}, /* 0x84 */ { "pushself", 0 }, /* 0x85 */ { "getdblcl", 2, { T3OP_TYPE_16U, T3OP_TYPE_16U }}, /* 0x86 */ { "getdbarg", 2, { T3OP_TYPE_16U, T3OP_TYPE_16U }}, /* 0x87 */ { "getargc", 0 }, /* 0x88 */ { "dup", 0 }, /* 0x89 */ { "disc", 0 }, /* 0x8a */ { "disc1", 1, { T3OP_TYPE_8U }}, /* 0x8b */ { "getr0", 0 }, /* 0x8c */ { "getdbargc", 1, { T3OP_TYPE_16U }}, /* 0x8d */ { "swap", 0 }, /* 0x8e */ { "pushctxele", 1, { T3OP_TYPE_CTX_ELE }}, /* 0x8f */ { "dup2", 0 }, /* 0x90 */ { "switch", 0 }, /* varying-length operands */ /* 0x91 */ { "jmp", 1, { T3OP_TYPE_CODE_REL }}, /* 0x92 */ { "jt", 1, { T3OP_TYPE_CODE_REL }}, /* 0x93 */ { "jf", 1, { T3OP_TYPE_CODE_REL }}, /* 0x94 */ { "je", 1, { T3OP_TYPE_CODE_REL }}, /* 0x95 */ { "jne", 1, { T3OP_TYPE_CODE_REL }}, /* 0x96 */ { "jgt", 1, { T3OP_TYPE_CODE_REL }}, /* 0x97 */ { "jge", 1, { T3OP_TYPE_CODE_REL }}, /* 0x98 */ { "jlt", 1, { T3OP_TYPE_CODE_REL }}, /* 0x99 */ { "jle", 1, { T3OP_TYPE_CODE_REL }}, /* 0x9a */ { "jst", 1, { T3OP_TYPE_CODE_REL }}, /* 0x9b */ { "jsf", 1, { T3OP_TYPE_CODE_REL }}, /* 0x9c */ { "ljsr", 1, { T3OP_TYPE_CODE_REL }}, /* 0x9d */ { "lret", 1, { T3OP_TYPE_16U }}, /* 0x9e */ { "jnil", 1, { T3OP_TYPE_CODE_REL }}, /* 0x9f */ { "jnotnil", 1, { T3OP_TYPE_CODE_REL }}, /* 0xa0 */ { "jr0t", 1, { T3OP_TYPE_CODE_REL }}, /* 0xa1 */ { "jr0f", 1, { T3OP_TYPE_CODE_REL }}, /* 0xa2 */ { "iternext", 2, { T3OP_TYPE_16U, T3OP_TYPE_CODE_REL }}, /* 0xa3 */ { "db 0xa3", 0 }, /* 0xa4 */ { "db 0xa4", 0 }, /* 0xa5 */ { "db 0xa5", 0 }, /* 0xa6 */ { "getspn", 1, { T3OP_TYPE_8U }}, /* 0xa7 */ { "db 0xa7", 0 }, /* 0xa8 */ { "db 0xa8", 0 }, /* 0xa9 */ { "db 0xa9", 0 }, /* 0xaa */ { "getlcln1", 0 }, /* 0xab */ { "getlcln1", 0 }, /* 0xac */ { "getlcln2", 0 }, /* 0xad */ { "getlcln3", 0 }, /* 0xae */ { "getlcln4", 0 }, /* 0xaf */ { "getlcln5", 0 }, /* 0xb0 */ { "say", 1, { T3OP_TYPE_STR }}, /* 0xb1 */ { "builtin_a", 2, { T3OP_TYPE_8U, T3OP_TYPE_8U }}, /* 0xb2 */ { "builtin_b", 2, { T3OP_TYPE_8U, T3OP_TYPE_8U }}, /* 0xb3 */ { "builtin_c", 2, { T3OP_TYPE_8U, T3OP_TYPE_8U }}, /* 0xb4 */ { "builtin_d", 2, { T3OP_TYPE_8U, T3OP_TYPE_8U }}, /* 0xb5 */ { "builtin1", 3, { T3OP_TYPE_8U, T3OP_TYPE_8U, T3OP_TYPE_8U }}, /* 0xb6 */ { "builtin2", 0, { T3OP_TYPE_16U, T3OP_TYPE_8U, T3OP_TYPE_8U }}, /* 0xb7 */ { "callext", 0 }, /* 0xb8 */ { "throw", 0 }, /* 0xb9 */ { "sayval", 0 }, /* 0xba */ { "index", 0 }, /* 0xbb */ { "idxlcl1int8", 2, { T3OP_TYPE_8U, T3OP_TYPE_8U }}, /* 0xbc */ { "idxint8", 1, { T3OP_TYPE_8U }}, /* 0xbd */ { "db 0xbd", 0 }, /* 0xbe */ { "db 0xbe", 0 }, /* 0xbf */ { "db 0xbf", 0 }, /* 0xc0 */ { "new1", 2, { T3OP_TYPE_8U, T3OP_TYPE_8U }}, /* 0xc1 */ { "new2", 2, { T3OP_TYPE_16U, T3OP_TYPE_8U }}, /* 0xc2 */ { "trnew1", 2, { T3OP_TYPE_8U, T3OP_TYPE_8U }}, /* 0xc3 */ { "trnew2", 2, { T3OP_TYPE_16U, T3OP_TYPE_8U }}, /* 0xc4 */ { "db 0xc4", 0 }, /* 0xc5 */ { "db 0xc5", 0 }, /* 0xc6 */ { "db 0xc6", 0 }, /* 0xc7 */ { "db 0xc7", 0 }, /* 0xc8 */ { "db 0xc8", 0 }, /* 0xc9 */ { "db 0xc9", 0 }, /* 0xca */ { "db 0xca", 0 }, /* 0xcb */ { "db 0xcb", 0 }, /* 0xcc */ { "db 0xcc", 0 }, /* 0xcd */ { "db 0xcd", 0 }, /* 0xce */ { "db 0xce", 0 }, /* 0xcf */ { "db 0xcf", 0 }, /* 0xd0 */ { "inclcl", 1, { T3OP_TYPE_16U }}, /* 0xd1 */ { "declcl", 1, { T3OP_TYPE_16U }}, /* 0xd2 */ { "addilcl1", 2, { T3OP_TYPE_8U, T3OP_TYPE_8S }}, /* 0xd3 */ { "addilcl4", 2, { T3OP_TYPE_16U, T3OP_TYPE_32S }}, /* 0xd4 */ { "addtolcl", 1, { T3OP_TYPE_16U }}, /* 0xd5 */ { "subfromlcl", 1, { T3OP_TYPE_16U }}, /* 0xd6 */ { "zerolcl1", 1, { T3OP_TYPE_8U }}, /* 0xd7 */ { "zerolcl2", 1, { T3OP_TYPE_16U }}, /* 0xd8 */ { "nillcl1", 1, { T3OP_TYPE_8U }}, /* 0xd9 */ { "nillcl2", 1, { T3OP_TYPE_16U }}, /* 0xda */ { "onelcl1", 1, { T3OP_TYPE_8U }}, /* 0xdb */ { "onelcl2", 1, { T3OP_TYPE_16U }}, /* 0xdc */ { "db 0xdc", 0 }, /* 0xdd */ { "db 0xdd", 0 }, /* 0xde */ { "db 0xde", 0 }, /* 0xdf */ { "db 0xdf", 0 }, /* 0xe0 */ { "setlcl1", 1, { T3OP_TYPE_8U }}, /* 0xe1 */ { "setlcl2", 1, { T3OP_TYPE_16U }}, /* 0xe2 */ { "setarg1", 1, { T3OP_TYPE_8U }}, /* 0xe3 */ { "setarg2", 1, { T3OP_TYPE_16U }}, /* 0xe4 */ { "setind", 0 }, /* 0xe5 */ { "setprop", 1, { T3OP_TYPE_PROP }}, /* 0xe6 */ { "ptrsetprop", 0 }, /* 0xe7 */ { "setpropself", 1, { T3OP_TYPE_PROP }}, /* 0xe8 */ { "objsetprop", 2, { T3OP_TYPE_OBJ, T3OP_TYPE_PROP }}, /* 0xe9 */ { "setdblcl", 2, { T3OP_TYPE_16U, T3OP_TYPE_16U }}, /* 0xea */ { "setdbarg", 2, { T3OP_TYPE_16U, T3OP_TYPE_16U }}, /* 0xeb */ { "setself", 0 }, /* 0xec */ { "loadctx", 0 }, /* 0xed */ { "storectx", 0 }, /* 0xee */ { "setlcl1r0", 1, { T3OP_TYPE_8U }}, /* 0xef */ { "setindlcl1i8", 2, { T3OP_TYPE_8U, T3OP_TYPE_8U }}, /* 0xf0 */ { "db 0xf0", 0 }, /* 0xf1 */ { "bp", 0 }, /* 0xf2 */ { "nop", 0 }, /* 0xf3 */ { "db 0xf3", 0 }, /* 0xf4 */ { "db 0xf4", 0 }, /* 0xf5 */ { "db 0xf5", 0 }, /* 0xf6 */ { "db 0xf6", 0 }, /* 0xf7 */ { "db 0xf7", 0 }, /* 0xf8 */ { "db 0xf8", 0 }, /* 0xf9 */ { "db 0xf9", 0 }, /* 0xfa */ { "db 0xfa", 0 }, /* 0xfb */ { "db 0xfb", 0 }, /* 0xfc */ { "db 0xfc", 0 }, /* 0xfd */ { "db 0xfd", 0 }, /* 0xfe */ { "db 0xfe", 0 }, /* 0xff */ { "db 0xff", 0 } }; /* ------------------------------------------------------------------------ */ /* * disassemble an instruction */ void CTcT3Unasm::disasm_instr(CTcUnasSrc *src, CTcUnasOut *out, char ch_op) { t3_instr_info_t *info; int i; ulong acc; char ch[10]; ulong prv_ofs = src->get_ofs(); /* get the information on this instruction */ info = &instr_info[(int)(uchar)ch_op]; out->print("%8lx %-14.14s ", src->get_ofs() - 1, info->nm); /* check the instruction type */ switch((uchar)ch_op) { case OPC_SWITCH: /* * It's a switch instruction - special handling is required, * since this instruction doesn't fit any of the normal * patterns. First, get the number of elements in the case * table - this is a UINT2 value at the start of the table. */ src->next_byte(ch); src->next_byte(ch+1); acc = osrp2(ch); /* display the count */ out->print("0x%x\n", (uint)acc); /* display the table */ for (i = 0 ; i < (int)acc ; ++i) { const char *dt; char valbuf[128]; const char *val = valbuf; /* note the current offset */ prv_ofs = src->get_ofs(); /* read the DATAHOLDER value */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); src->next_byte(ch+4); /* read the jump offset */ src->next_byte(ch+5); src->next_byte(ch+6); /* * stop looping if the offset hasn't changed - this probably * means we're stuck trying to interpret as a "switch" some * data at the end of the stream that happens to look like a * switch but really isn't (such as an exception table, or * debug records) */ if (src->get_ofs() == prv_ofs) break; /* show the value */ switch(ch[0]) { case VM_NIL: dt = "nil"; val = ""; break; case VM_TRUE: dt = "true"; val = ""; break; case VM_OBJ: dt = "object"; sprintf(valbuf, "0x%08lx", t3rp4u(ch+1)); break; case VM_PROP: dt = "prop"; sprintf(valbuf, "0x%04x", osrp2(ch+1)); break; case VM_INT: dt = "int"; sprintf(valbuf, "0x%08lx", t3rp4u(ch+1)); break; case VM_ENUM: dt = "enum"; sprintf(valbuf, "0x%08lx", t3rp4u(ch+1)); break; case VM_SSTRING: dt = "sstring"; sprintf(valbuf, "0x%08lx", t3rp4u(ch+1)); break; case VM_LIST: dt = "list"; sprintf(valbuf, "0x%08lx", t3rp4u(ch+1)); break; case VM_FUNCPTR: dt = "funcptr"; sprintf(valbuf, "0x%08lx", t3rp4u(ch+1)); break; default: dt = "???"; val = "???"; break; } /* show the slot data */ out->print(" 0x%08lx %-8.8s(%-10.10s) " "-> +0x%04lx (0x%08lx)\n", src->get_ofs() - 7, dt, val, osrp2(ch+5), src->get_ofs() - 2 + osrp2s(ch+5)); } /* read and show the 'default' jump offset */ src->next_byte(ch); src->next_byte(ch+1); out->print(" 0x%08lx default " "-> +0x%04lx (0x%08lx)\n", src->get_ofs() - 2, osrp2(ch), src->get_ofs() - 2 + osrp2s(ch)); /* done */ break; case OPC_NAMEDARGTAB: /* named argument table */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); acc = osrp2(ch); out->print("len=0x%x, cnt=0x%x [", (uint)acc, (uint)(uchar)*ch); acc = (uchar)*ch; /* show the names */ for (i = 0 ; i < (int)acc ; ++i) { /* get the length */ src->next_byte(ch); src->next_byte(ch); uint len = osrp2(ch); /* get the name */ char name[128]; uint j; for (j = 0 ; j < len && j < sizeof(name) ; ++j) src->next_byte(name + j); /* show the name */ out->print("%s%.*s", i > 0 ? ", " : ":", (int)j, name); /* skip any excess portion of the name */ for ( ; j < len ; ++j) src->next_byte(ch); } /* end the list, and we're done */ out->print("]"); break; default: /* show all parameters */ for (i = 0 ; i < info->op_cnt ; ++i) { /* add a separator if this isn't the first one */ if (i != 0) out->print(", "); /* display the operand */ switch(info->op_type[i]) { case T3OP_TYPE_8S: /* 8-bit signed integer */ src->next_byte(ch); out->print("0x%x", (int)ch[0]); break; case T3OP_TYPE_8U: /* 8-bit unsigned integer */ src->next_byte(ch); out->print("0x%x", (uint)(uchar)ch[0]); break; case T3OP_TYPE_16S: /* 16-bit signed integer */ src->next_byte(ch); src->next_byte(ch+1); acc = osrp2s(ch); out->print("0x%x", (int)acc); break; case T3OP_TYPE_16U: /* 16-bit unsigned integer */ src->next_byte(ch); src->next_byte(ch+1); acc = osrp2(ch); out->print("0x%x", (uint)acc); break; case T3OP_TYPE_32S: /* 32-bit signed integer */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); acc = t3rp4u(ch); out->print("0x%lx", acc); break; case T3OP_TYPE_32U: /* 32-bit unsigned integer */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); acc = t3rp4u(ch); out->print("0x%lx", acc); break; case T3OP_TYPE_STR: /* string offset */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); acc = t3rp4u(ch); out->print("string(0x%lx)", acc); break; case T3OP_TYPE_LIST: /* list offset */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); acc = t3rp4u(ch); out->print("list(0x%lx)", acc); break; case T3OP_TYPE_CODE_ABS: /* 32-bit absolute code address */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); acc = t3rp4u(ch); out->print("code(0x%08lx)", acc); break; case T3OP_TYPE_CODE_REL: /* 16-bit relative code address */ src->next_byte(ch); src->next_byte(ch+1); acc = osrp2s(ch); if ((long)acc < 0) out->print("-0x%04x (0x%08lx)", -(int)acc, src->get_ofs() - 2 + acc); else out->print("+0x%04x (0x%08lx)", (int)acc, src->get_ofs() - 2 + acc); break; case T3OP_TYPE_OBJ: /* object ID */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); acc = t3rp4u(ch); out->print("object(0x%lx)", acc); break; case T3OP_TYPE_PROP: /* property ID */ src->next_byte(ch); src->next_byte(ch+1); acc = osrp2(ch); out->print("property(0x%x)", (uint)acc); break; case T3OP_TYPE_ENUM: /* enum */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); acc = t3rp4u(ch); out->print("enum(0x%lx)", acc); break; case T3OP_TYPE_CTX_ELE: /* context element identifier */ src->next_byte(ch); switch(ch[0]) { case PUSHCTXELE_TARGPROP: out->print("targetprop"); break; case PUSHCTXELE_TARGOBJ: out->print("targetobj"); break; case PUSHCTXELE_DEFOBJ: out->print("definingobj"); break; default: out->print("0x%x", (int)ch[0]); break; } break; case T3OP_TYPE_INLINE_STR: /* get the string length */ src->next_byte(ch); src->next_byte(ch+1); /* show it */ out->print("string(inline)"); /* skip the string */ for (acc = osrp2(ch) ; acc != 0 ; --acc) src->next_byte(ch); /* done */ break; default: out->print("...unknown type..."); break; } } /* end the line */ out->print("\n"); } } /* ------------------------------------------------------------------------ */ /* * Show an exception table */ void CTcT3Unasm::show_exc_table(class CTcUnasSrc *src, class CTcUnasOut *out, unsigned long base_ofs) { unsigned entries; char ch[10]; /* read the number of entries */ src->next_byte(ch); src->next_byte(ch+1); /* show the entries */ for (entries = osrp2(ch) ; entries != 0 ; --entries) { unsigned long start_ofs; unsigned long end_ofs; unsigned exc_obj_id; unsigned long catch_ofs; /* read the code start offset */ src->next_byte(ch); src->next_byte(ch+1); start_ofs = base_ofs + osrp2(ch); /* read the code end offset */ src->next_byte(ch); src->next_byte(ch+1); end_ofs = base_ofs + osrp2(ch); /* read the object ID */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); exc_obj_id = base_ofs + t3rp4u(ch); /* read the catch offset */ src->next_byte(ch); src->next_byte(ch+1); catch_ofs = base_ofs + osrp2(ch); /* show it */ out->print(" from %lx to %lx object %x catch %lx\n", start_ofs, end_ofs, exc_obj_id, catch_ofs); } } qtads-2.1.7/tads3/tct3unas.h000066400000000000000000000052641265017072300156020ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/TCT3UNAS.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3unas.h - TADS 3 Compiler - T3 Unassembler Function Takes a T3 byte-code stream and disassembles it to a printable display Notes Modified 05/10/99 MJRoberts - Creation */ #ifndef TCT3UNAS_H #define TCT3UNAS_H #include "tcunas.h" /* ------------------------------------------------------------------------ */ /* * operand types, for instruction descriptions */ enum t3_oper_type_t { /* 8-bit signed integer */ T3OP_TYPE_8S, /* 8-bit unsigned integer */ T3OP_TYPE_8U, /* 16-bit signed integer */ T3OP_TYPE_16S, /* 16-bit unsigned integer */ T3OP_TYPE_16U, /* 32-bit signed integer */ T3OP_TYPE_32S, /* 32-bit unsigned integer */ T3OP_TYPE_32U, /* string offset */ T3OP_TYPE_STR, /* list offset */ T3OP_TYPE_LIST, /* absolute (32-bit) code address */ T3OP_TYPE_CODE_ABS, /* relative (16-bit) code address */ T3OP_TYPE_CODE_REL, /* object ID */ T3OP_TYPE_OBJ, /* property ID */ T3OP_TYPE_PROP, /* enum ID */ T3OP_TYPE_ENUM, /* context element type */ T3OP_TYPE_CTX_ELE, /* in-line string */ T3OP_TYPE_INLINE_STR }; /* ------------------------------------------------------------------------ */ /* * Instruction information - this structure describes one T3 byte-code * instruction. */ struct t3_instr_info_t { /* name of the instruction */ const char *nm; /* number of operands */ int op_cnt; /* * operand types (allow for a fixed upper limit to the number of * operands) */ t3_oper_type_t op_type[3]; }; /* ------------------------------------------------------------------------ */ /* * T3 Disassembler */ class CTcT3Unasm { public: /* disassemble a byte stream */ static void disasm(class CTcUnasSrc *src, class CTcUnasOut *out); /* show an exception table */ static void show_exc_table(class CTcUnasSrc *src, class CTcUnasOut *out, unsigned long base_ofs); /* get the instruction information array (read-only) */ static const t3_instr_info_t *get_instr_info() { return instr_info; } protected: /* disassemble an instruction */ static void disasm_instr(class CTcUnasSrc *src, class CTcUnasOut *out, char ch); /* instruction information array */ static t3_instr_info_t instr_info[]; }; #endif /* TCT3UNAS_H */ qtads-2.1.7/tads3/tctarg.h000066400000000000000000000076261265017072300153260ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/TCTARG.H,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tctarg.h - TADS 3 Compiler Target Selector Function Notes Modified 04/30/99 MJRoberts - Creation */ #ifndef TCTARG_H #define TCTARG_H /* ------------------------------------------------------------------------ */ /* * Target Selection. * * SEE ALSO: tctargty.h - target type header selector * * As the parser scans the source stream, it constructs a parse tree * that represent the parsed form of the source. After the parsing * pass, the parse tree contains all of the information necessary to * generate code for the translation. * * The parse tree objects are actually the code generators. So, as the * scanner parses the source file, it must create parse tree objects for * the appropriate target architecture. However, we want to keep the * scanner independent of the target architecture -- we want the same * scanner to be usable for any target architecture for which we can * provide a code generator. * * To accomplish this, we define a base class for parse tree nodes; the * scanner is only interested in this base class interface. Then, for * each target architecture, we create a subclass of each parse tree * node that contains the code generator for that node type for the * target. * * However, the scanner must still know enough to create the appropriate * subclass of each parse tree node. This file contains the target * selection switch that mediates the independence of the scanner from * the target code generator, but still allows the scanner to create the * correct type of parse tree nodes for the desired target. For each * parse tree node type that the scanner must create, we #define a * generic symbol to a target-specific subclass. The scanner uses the * generic symbol, but we expand the macro when compiling the compiler * to the correct target. * * Because the target selection is made through macros, the target * architecture is fixed at compile time. However, the same scanner * source code can be compiled into multiple target compilers. */ /* * Before including this file, #define the appropriate target type. * This should usually be done in the makefile, since this is a * compile-time selection. * * To add a new code generator, define the appropriate subclasses in a * new file. Add a new #ifdef-#include sequence below that includes the * subclass definitions for your code generator. * * Note that each target uses the same names for the final subclasses. * We choose the target at link time when building the compiler * executable, so we must compile and link only a single target * architecture into a given build of the compiler. */ /* * make sure TC_TARGET_DEFINED__ isn't defined, so we can use it to * sense whether we defined a code generator or not */ #undef TCTARG_TARGET_DEFINED__ /* ------------------------------------------------------------------------ */ /* * T3 Virtual Machine Code Generator */ #ifdef TC_TARGET_T3 #include "tct3.h" #define TCTARG_TARGET_DEFINED__ #endif /* ------------------------------------------------------------------------ */ /* * JavaScript code generator */ #ifdef TC_TARGET_JS #include "tcjs.h" #define TCTARG_TARGET_DEFINED__ #endif /* ------------------------------------------------------------------------ */ /* * ensure that a code generator was defined - if not, the compilation * cannot proceed */ #ifndef TCTARG_TARGET_DEFINED__ #error No code generator target is defined. A code generator must be defined in your makefile. See tctarg.h for details. #endif #endif /* TCTARG_H */ qtads-2.1.7/tads3/tctargty.h000066400000000000000000000031351265017072300156720ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/TCTARGTY.H,v 1.1 1999/07/11 00:46:58 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tctarg.h - TADS 3 Compiler Target Selector - target types Function Notes Modified 04/30/99 MJRoberts - Creation */ #ifndef TCTARGTY_H #define TCTARGTY_H /* ------------------------------------------------------------------------ */ /* * Target Selection - type definitions * * This is a target selector for the target types header. This follows * the same mechanism that tctarg.h uses - refer to that file for * details. */ /* * make sure TC_TARGET_DEFINED__ isn't defined, so we can use it to * sense whether we defined a code generator or not */ #undef TCTARGTY_TARGET_DEFINED__ /* ------------------------------------------------------------------------ */ /* * T3 Virtual Machine Code Generator */ #ifdef TC_TARGET_T3 #include "tct3ty.h" #define TCTARGTY_TARGET_DEFINED__ #endif /* * Javascript Code Generator */ #ifdef TC_TARGET_JS #include "tcjsty.h" #define TCTARGTY_TARGET_DEFINED__ #endif /* ------------------------------------------------------------------------ */ /* * ensure that a code generator was defined - if not, the compilation * cannot proceed */ #ifndef TCTARGTY_TARGET_DEFINED__ #error No code generator target is defined. A code generator must be defined in your makefile. See tctarg.h for details. #endif #endif /* TCTARGTY_H */ qtads-2.1.7/tads3/tctok.cpp000066400000000000000000011025521265017072300155140ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/tctok.cpp,v 1.5 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tctok.cpp - TADS3 compiler tokenizer Function Notes The tokenizer features an integrated C-style preprocessor. The preprocessor is integrated into the tokenizer for efficiency; since the preprocessor uses the same lexical structure as the the TADS language, we need only tokenize the input stream once, and the result can be used both for preprocessing and for parsing. Modified 04/12/99 MJRoberts - Creation */ #include #include #include #include #include "os.h" #include "t3std.h" #include "vmerr.h" #include "vmhash.h" #include "tcerr.h" #include "tcerrnum.h" #include "tctok.h" #include "tcsrc.h" #include "tcmain.h" #include "tchost.h" #include "tcprs.h" #include "tctarg.h" #include "charmap.h" #include "vmdatasrc.h" #include "vmfile.h" /* ------------------------------------------------------------------------ */ /* * Standard macro table. This implements the interface using a standard * hash table object. */ class CTcBasicMacroTable: public CTcMacroTable { public: CTcBasicMacroTable(int hash_table_size, CVmHashFunc *hash_function, int own_hash_func) : tab(hash_table_size, hash_function, own_hash_func) { } virtual void add(CVmHashEntry *entry) { tab.add(entry); } virtual void remove(CVmHashEntry *entry) { tab.remove(entry); } virtual CVmHashEntry *find(const char *str, size_t len) { return tab.find(str, len); } virtual void enum_entries(void (*func)(void *, CVmHashEntry *), void *ctx) { tab.enum_entries(func, ctx); } virtual void debug_dump() { tab.debug_dump(); } private: /* our hash table */ CVmHashTable tab; }; /* ------------------------------------------------------------------------ */ /* * string embedded expression context */ void tok_embed_ctx::start_expr(wchar_t qu, int triple, int report) { if (level < countof(stk)) { s = stk + level; s->enter(qu, triple); } else if (report) { G_tok->log_error(TCERR_EMBEDDING_TOO_DEEP); } ++level; } /* ------------------------------------------------------------------------ */ /* * Initialize the tokenizer */ CTcTokenizer::CTcTokenizer(CResLoader *res_loader, const char *default_charset) { int i; os_time_t timer; struct tm *tblk; const char *tstr; char timebuf[50]; struct kwdef { const char *kw_text; tc_toktyp_t kw_tok_id; }; static const kwdef kwlist[] = { { "self", TOKT_SELF }, { "targetprop", TOKT_TARGETPROP }, { "targetobj", TOKT_TARGETOBJ }, { "definingobj", TOKT_DEFININGOBJ }, { "inherited", TOKT_INHERITED }, { "delegated", TOKT_DELEGATED }, { "argcount", TOKT_ARGCOUNT }, { "if", TOKT_IF }, { "else", TOKT_ELSE }, { "for", TOKT_FOR }, { "while", TOKT_WHILE }, { "do", TOKT_DO }, { "switch", TOKT_SWITCH }, { "case", TOKT_CASE }, { "default", TOKT_DEFAULT }, { "goto", TOKT_GOTO }, { "break", TOKT_BREAK }, { "continue", TOKT_CONTINUE }, // { "and", TOKT_AND }, // { "or", TOKT_OR }, // { "not", TOKT_NOT }, { "function", TOKT_FUNCTION }, { "return", TOKT_RETURN }, { "local", TOKT_LOCAL }, { "object", TOKT_OBJECT }, { "nil", TOKT_NIL }, { "true", TOKT_TRUE }, { "pass", TOKT_PASS }, { "external", TOKT_EXTERNAL }, { "extern", TOKT_EXTERN }, { "formatstring", TOKT_FORMATSTRING }, { "class", TOKT_CLASS }, { "replace", TOKT_REPLACE }, { "modify", TOKT_MODIFY }, { "new", TOKT_NEW }, // { "delete", TOKT_DELETE }, { "throw", TOKT_THROW }, { "try", TOKT_TRY }, { "catch", TOKT_CATCH }, { "finally", TOKT_FINALLY }, { "intrinsic", TOKT_INTRINSIC }, { "dictionary", TOKT_DICTIONARY }, { "grammar", TOKT_GRAMMAR }, { "enum", TOKT_ENUM }, { "template", TOKT_TEMPLATE }, { "static", TOKT_STATIC }, { "foreach", TOKT_FOREACH }, { "export", TOKT_EXPORT }, { "propertyset", TOKT_PROPERTYSET }, { "transient", TOKT_TRANSIENT }, { "replaced", TOKT_REPLACED }, { "property", TOKT_PROPERTY }, { "operator", TOKT_OPERATOR }, { "method", TOKT_METHOD }, { "invokee", TOKT_INVOKEE }, // { "void", TOKT_VOID }, // { "int", TOKT_INT }, // { "string", TOKT_STRING }, // { "list", TOKT_LIST }, // { "boolean", TOKT_BOOLEAN }, // { "any", TOKT_ANY }, /* end-of-table marker */ { 0, TOKT_INVALID } }; const kwdef *kwp; /* remember my resource loader */ res_loader_ = res_loader; /* there's no stream yet */ str_ = 0; /* no external source yet */ ext_src_ = 0; /* start numbering the file descriptors at zero */ next_filedesc_id_ = 0; /* there are no file descriptors yet */ desc_head_ = 0; desc_tail_ = 0; desc_list_ = 0; desc_list_cnt_ = desc_list_alo_ = 0; /* empty out the input line buffer */ clear_linebuf(); /* start out with a minimal line buffer size */ linebuf_.ensure_space(4096); expbuf_.ensure_space(4096); /* set up at the beginning of the input line buffer */ start_new_line(&linebuf_, 0); /* remember the default character set */ default_charset_ = lib_copy_str(default_charset); /* we don't have a default character mapper yet */ default_mapper_ = 0; /* create an input mapper for the default character set, if specified */ if (default_charset != 0) default_mapper_ = CCharmapToUni::load(res_loader, default_charset); /* * if the default character set wasn't specified, or we failed to * load a mapper for the specified character set, use a plain ASCII * mapper */ if (default_mapper_ == 0) default_mapper_ = new CCharmapToUniASCII(); /* presume we're not in preprocessor-only mode */ pp_only_mode_ = FALSE; /* presume we're not in list-includes mode */ list_includes_mode_ = FALSE; /* presume we're not in test report mode */ test_report_mode_ = FALSE; /* allow preprocessing directives */ allow_pp_ = TRUE; /* there are no previously-included files yet */ prev_includes_ = 0; /* by default, use the "collapse" mode for newlines in strings */ string_newline_spacing_ = NEWLINE_SPACING_COLLAPSE; /* start out with ALL_ONCE mode off */ all_once_ = FALSE; /* by default, ignore redundant includes without warning */ warn_on_ignore_incl_ = FALSE; /* there are no include path entries yet */ incpath_head_ = incpath_tail_ = 0; /* not in a quoted string yet */ in_quote_ = '\0'; in_triple_ = FALSE; /* not in a #if block yet */ if_sp_ = 0; if_false_level_ = 0; /* not processing a preprocessor constant expression */ in_pp_expr_ = FALSE; /* we don't have a current or appended line yet */ last_desc_ = 0; last_linenum_ = 0; appended_desc_ = 0; appended_linenum_ = 0; /* allocate the first token-list block */ init_src_block_list(); /* create the #define and #undef symbol tables */ defines_ = new CTcBasicMacroTable(512, new CVmHashFuncCS(), TRUE); undefs_ = new CVmHashTable(64, new CVmHashFuncCS(), TRUE); /* create the special __LINE__ and __FILE__ macros */ defines_->add(new CTcHashEntryPpLINE(this)); defines_->add(new CTcHashEntryPpFILE(this)); /* get the current time and date */ timer = os_time(0); tblk = os_localtime(&timer); tstr = asctime(tblk); /* * add the __DATE__ macro - the format is "Mmm dd yyyy", where "Mmm" * is the three-letter month name generated by asctime(), "dd" is * the day of the month, with a leading space for numbers less than * ten, and "yyyy" is the year. */ sprintf(timebuf, "'%.3s %2d %4d'", tstr + 4, tblk->tm_mday, tblk->tm_year + 1900); add_define("__DATE__", timebuf); /* add the __TIME__ macro - 24-hour "hh:mm:ss" format */ sprintf(timebuf, "'%.8s'", tstr + 11); add_define("__TIME__", timebuf); /* * Allocate a pool of macro resources. The number we start with is * arbitrary, since we'll add more as needed, but we want to try to * allocate enough up front that we avoid time-consuming memory * allocations later. On the other hand, we don't want to * pre-allocate a huge number of objects that we'll never use. */ for (macro_res_avail_ = 0, macro_res_head_ = 0, i = 0 ; i < 7 ; ++i) { CTcMacroRsc *rsc; /* allocate a new object */ rsc = new CTcMacroRsc(); /* add it onto the master list */ rsc->next_ = macro_res_head_; macro_res_head_ = rsc; /* add it onto the available list */ rsc->next_avail_ = macro_res_avail_; macro_res_avail_ = rsc; } /* create the keyword hash table */ kw_ = new CVmHashTable(64, new CVmHashFuncCS(), TRUE); /* populate the keyword table */ for (kwp = kwlist ; kwp->kw_text != 0 ; ++kwp) kw_->add(new CTcHashEntryKw(kwp->kw_text, kwp->kw_tok_id)); /* no ungot tokens yet */ unget_head_ = unget_cur_ = 0; /* no string capture file */ string_fp_ = 0; string_fp_map_ = 0; /* there's no current token yet */ curtok_.settyp(TOKT_NULLTOK); curtok_.set_text("", 16); } /* * Initialize the source save block list */ void CTcTokenizer::init_src_block_list() { /* allocate the first source block */ src_cur_ = src_head_ = new CTcTokSrcBlock(); /* set up to write into the first block */ src_ptr_ = src_head_->get_buf(); src_rem_ = TCTOK_SRC_BLOCK_SIZE; } /* ------------------------------------------------------------------------ */ /* * Delete the tokenizer */ CTcTokenizer::~CTcTokenizer() { /* delete all streams */ delete_source(); /* delete the string capture file */ if (string_fp_ != 0) delete string_fp_; /* delete all file descriptors */ while (desc_head_ != 0) { /* remember the next descriptor */ CTcTokFileDesc *nxt = desc_head_->get_next(); /* delete this one */ delete desc_head_; /* move on to the next one */ desc_head_ = nxt; } /* delete the unget list */ unget_cur_ = 0; while (unget_head_ != 0) { /* remember the next element */ CTcTokenEle *nxt = unget_head_->getnxt(); /* delete this element */ delete unget_head_; /* advance to the next element */ unget_head_ = nxt; } /* delete the file descriptor index array */ if (desc_list_ != 0) t3free(desc_list_); /* delete our default character set string copy */ lib_free_str(default_charset_); /* release our reference on our default character mapper */ default_mapper_->release_ref(); /* forget about all of our previous include files */ while (prev_includes_ != 0) { tctok_incfile_t *nxt; /* remember the next file */ nxt = prev_includes_->nxt; /* delete this one */ t3free(prev_includes_); /* move on to the next one */ prev_includes_ = nxt; } /* delete the include path list */ while (incpath_head_ != 0) { tctok_incpath_t *nxt; /* remember the next entry in the path */ nxt = incpath_head_->nxt; /* delete this entry */ t3free(incpath_head_); /* move on to the next one */ incpath_head_ = nxt; } /* delete the macro resources */ while (macro_res_head_ != 0) { CTcMacroRsc *nxt; /* remember the next one */ nxt = macro_res_head_->next_; /* delete this one */ delete macro_res_head_; /* move on to the next one */ macro_res_head_ = nxt; } /* delete the token list */ delete src_head_; /* delete the #define and #undef symbol tables */ delete defines_; delete undefs_; /* delete the keyword hash table */ delete kw_; /* if we created a mapping for the string capture file, release it */ if (string_fp_map_ != 0) string_fp_map_->release_ref(); } /* ------------------------------------------------------------------------ */ /* * Clear the line buffer */ void CTcTokenizer::clear_linebuf() { /* clear the buffer */ linebuf_.clear_text(); /* reset our read point to the start of the line buffer */ p_.set(linebuf_.get_buf()); } /* ------------------------------------------------------------------------ */ /* * Get a textual representation of an operator token */ const char *CTcTokenizer::get_op_text(tc_toktyp_t op) { struct tokname_t { tc_toktyp_t typ; const char *nm; }; static const tokname_t toknames[] = { { TOKT_EOF, "" }, { TOKT_SYM, "" }, { TOKT_INT, "" }, { TOKT_SSTR, "" }, { TOKT_DSTR, "" }, { TOKT_DSTR_START, "" }, { TOKT_DSTR_MID, "" }, { TOKT_DSTR_END, "" }, { TOKT_RESTR, "" }, { TOKT_LPAR, "(" }, { TOKT_RPAR, ")" }, { TOKT_COMMA, "," }, { TOKT_DOT, "." }, { TOKT_LBRACE, "{" }, { TOKT_RBRACE, "}", }, { TOKT_LBRACK, "[", }, { TOKT_RBRACK, "]", }, { TOKT_EQ, "=", }, { TOKT_EQEQ, "==", }, { TOKT_ASI, ":=" }, { TOKT_PLUS, "+" }, { TOKT_MINUS, "-" }, { TOKT_TIMES, "*" }, { TOKT_DIV, "/", }, { TOKT_MOD, "%" }, { TOKT_GT, ">" }, { TOKT_LT, "<" }, { TOKT_GE, ">=" }, { TOKT_LE, "<=" }, { TOKT_NE, "!=" }, { TOKT_ARROW, "->" }, { TOKT_COLON, ":" }, { TOKT_SEM, ";" }, { TOKT_AND, "&" }, { TOKT_ANDAND, "&&" }, { TOKT_OR, "|" }, { TOKT_OROR, "||" }, { TOKT_XOR, "^" }, { TOKT_SHL, "<<" }, { TOKT_ASHR, ">>" }, { TOKT_LSHR, ">>>" }, { TOKT_INC, "++" }, { TOKT_DEC, "--" }, { TOKT_PLUSEQ, "+=" }, { TOKT_MINEQ, "-=" }, { TOKT_TIMESEQ, "*=" }, { TOKT_DIVEQ, "/=" }, { TOKT_MODEQ, "%=" }, { TOKT_ANDEQ, "&=" }, { TOKT_OREQ, "|=" }, { TOKT_XOREQ, "^=" }, { TOKT_SHLEQ, "<<=" }, { TOKT_ASHREQ, ">>=" }, { TOKT_LSHREQ, ">>>=" }, { TOKT_NOT, "! (not)" }, { TOKT_BNOT, "~" }, { TOKT_POUND, "#" }, { TOKT_POUNDPOUND, "##" }, { TOKT_POUNDAT, "#@" }, { TOKT_ELLIPSIS, "..." }, { TOKT_QUESTION, "?" }, { TOKT_QQ, "??" }, { TOKT_COLONCOLON, "::" }, { TOKT_FLOAT, "" }, { TOKT_BIGINT, "" }, { TOKT_AT, "@" }, { TOKT_DOTDOT, ".." }, { TOKT_SELF, "self" }, { TOKT_TARGETPROP, "targetprop" }, { TOKT_TARGETOBJ, "targetobj" }, { TOKT_DEFININGOBJ, "definingobj" }, { TOKT_INHERITED, "inherited" }, { TOKT_DELEGATED, "delegated" }, { TOKT_IF, "if" }, { TOKT_ELSE, "else" }, { TOKT_FOR, "for" }, { TOKT_WHILE, "while" }, { TOKT_DO, "do" }, { TOKT_SWITCH, "switch" }, { TOKT_CASE, "case" }, { TOKT_DEFAULT, "default" }, { TOKT_GOTO, "goto" }, { TOKT_BREAK, "break" }, { TOKT_CONTINUE, "continue" }, { TOKT_FUNCTION, "function" }, { TOKT_RETURN, "return" }, { TOKT_LOCAL, "local" }, { TOKT_OBJECT, "object" }, { TOKT_NIL, "nil" }, { TOKT_TRUE, "true" }, { TOKT_PASS, "pass" }, { TOKT_EXTERNAL, "external" }, { TOKT_EXTERN, "extern" }, { TOKT_FORMATSTRING, "formatstring" }, { TOKT_CLASS, "class" }, { TOKT_REPLACE, "replace" }, { TOKT_MODIFY, "modify" }, { TOKT_NEW, "new" }, // { TOKT_DELETE, "delete" }, { TOKT_THROW, "throw" }, { TOKT_TRY, "try" }, { TOKT_CATCH, "catch" }, { TOKT_FINALLY, "finally" }, { TOKT_INTRINSIC, "intrinsic" }, { TOKT_DICTIONARY, "dictionary" }, { TOKT_GRAMMAR, "grammar" }, { TOKT_ENUM, "enum" }, { TOKT_TEMPLATE, "template" }, { TOKT_STATIC, "static" }, { TOKT_FOREACH, "foreach" }, { TOKT_EXPORT, "export" }, { TOKT_PROPERTYSET, "propertyset" }, { TOKT_TRANSIENT, "transient" }, { TOKT_REPLACED, "replaced" }, { TOKT_PROPERTY, "property" }, { TOKT_OPERATOR, "operator" }, { TOKT_METHOD, "method" }, { TOKT_INVOKEE, "invokee" }, // { TOKT_VOID, "void" }, // { TOKT_INTKW, "int" }, // { TOKT_STRING, "string" }, // { TOKT_LIST, "list" }, // { TOKT_BOOLEAN, "boolean" }, // { TOKT_ANY, "any"}, { TOKT_INVALID, 0 } }; const tokname_t *p; /* search for the token */ for (p = toknames ; p->nm != 0 ; ++p) { /* if this is our token, return the associated name string */ if (p->typ == op) return p->nm; } /* we didn't find it */ return ""; } /* ------------------------------------------------------------------------ */ /* * Reset the tokenizer. Delete the current source object and all of the * saved source text. This can be used after compilation of a unit * (such as a debugger expression) is completed and the intermediate * parser state is no longer needed. */ void CTcTokenizer::reset() { /* delete the source object */ delete_source(); /* delete saved token text */ if (src_head_ != 0) { /* delete the list */ delete src_head_; /* re-initialize the source block list */ init_src_block_list(); } } /* ------------------------------------------------------------------------ */ /* * Delete the source file, if any, including any parent include files. */ void CTcTokenizer::delete_source() { /* delete the current stream and all enclosing parents */ while (str_ != 0) { CTcTokStream *nxt; /* remember the next stream in the list */ nxt = str_->get_parent(); /* delete this stream */ delete str_; /* move up to the next one */ str_ = nxt; } /* there are no more streams */ str_ = 0; } /* ------------------------------------------------------------------------ */ /* * Set up to read a source file. Returns zero on success, or a non-zero * error code on failure. */ int CTcTokenizer::set_source(const char *src_filename, const char *orig_name) { CTcTokFileDesc *desc; CTcSrcFile *src; int charset_error; int default_charset_error; /* empty out the input line buffer */ clear_linebuf(); /* set up at the beginning of the input line buffer */ start_new_line(&linebuf_, 0); /* create a reader for the source file */ src = CTcSrcFile::open_source(src_filename, res_loader_, default_charset_, &charset_error, &default_charset_error); if (src == 0) { /* if we had a problem loading the default character set, log it */ if (default_charset_error) log_error(TCERR_CANT_LOAD_DEFAULT_CHARSET, default_charset_); /* return failure */ return TCERR_CANT_OPEN_SRC; } /* find or create a file descriptor for this filename */ desc = get_file_desc(src_filename, strlen(src_filename), FALSE, orig_name, strlen(orig_name)); /* * Create a stream to read the source file. The new stream has no * parent, because this is the top-level source file, and was not * included from any other file. */ str_ = new CTcTokStream(desc, src, 0, charset_error, if_sp_); /* success */ return 0; } /* * Set up to read source code from a memory buffer */ void CTcTokenizer::set_source_buf(const char *buf, size_t len) { CTcSrcMemory *src; /* empty out the input line buffer */ clear_linebuf(); /* reset the scanning state to the start of a brand new stream */ in_pp_expr_ = FALSE; last_linenum_ = 0; unsplicebuf_.clear_text(); in_quote_ = '\0'; in_triple_ = FALSE; comment_in_embedding_.reset(); macro_in_embedding_.reset(); main_in_embedding_.reset(); if_sp_ = 0; if_false_level_ = 0; unget_cur_ = 0; /* set up at the beginning of the input line buffer */ start_new_line(&linebuf_, 0); /* create a reader for the memory buffer */ src = new CTcSrcMemory(buf, len, default_mapper_); /* * Create a stream to read the source file. The new stream has no * parent, because this is the top-level source file, and was not * included from any other file. */ str_ = new CTcTokStream(0, src, 0, 0, if_sp_); } /* ------------------------------------------------------------------------ */ /* * Stuff text into the source stream. */ void CTcTokenizer::stuff_text(const char *txt, size_t len, int expand) { CTcTokString expbuf; int p_ofs; /* if desired, expand macros */ if (expand) { /* expand macros in the text, storing the result in 'expbuf' */ expand_macros(&expbuf, txt, len); /* use the expanded version as the stuffed text now */ txt = expbuf.get_text(); len = expbuf.get_text_len(); } /* get the current p_ offset */ p_ofs = p_.getptr() - curbuf_->get_text(); /* insert the text into the buffer */ curbuf_->insert(p_ofs, txt, len); /* reset p_ in case the curbuf_ buffer was reallocated for expansion */ start_new_line(curbuf_, p_ofs); } /* ------------------------------------------------------------------------ */ /* * Find or create a file descriptor for a given filename */ CTcTokFileDesc *CTcTokenizer::get_file_desc(const char *fname, size_t fname_len, int always_create, const char *orig_fname, size_t orig_fname_len) { CTcTokFileDesc *orig_desc; CTcTokFileDesc *desc; /* presume we won't find an original descriptor in the list */ orig_desc = 0; /* * Search the list of existing descriptors to find one that matches. * Do this regardless of whether we're allowed to re-use an existing * one or not - even if we're creating a new one unconditionaly, we * need to know if there's an earlier copy that already exists so we * can associate the new one with the original. */ for (desc = desc_head_ ; desc != 0 ; desc = desc->get_next()) { /* check for a name match */ if (strlen(desc->get_fname()) == fname_len && memcmp(desc->get_fname(), fname, fname_len) == 0) { /* * if we're allowed to return an existing descriptor, return * this one, since it's for the same filename */ if (!always_create) return desc; /* * we have to create a new descriptor even though we have an * existing one - remember the original so we can point the * new one back to the original */ orig_desc = desc; /* * no need to look any further - we've found the first * instance of this filename in our list */ break; } } /* we didn't find a match - create a new descriptor */ desc = new CTcTokFileDesc(fname, fname_len, next_filedesc_id_++, orig_desc, orig_fname, orig_fname_len); /* link it in at the end of the master list */ desc->set_next(0); if (desc_tail_ == 0) desc_head_ = desc; else desc_tail_->set_next(desc); desc_tail_ = desc; /* expand our array index if necessary */ if (desc_list_cnt_ >= desc_list_alo_) { size_t siz; /* allocate or expand the array */ desc_list_alo_ += 10; siz = desc_list_alo_ * sizeof(desc_list_[0]); if (desc_list_ == 0) desc_list_ = (CTcTokFileDesc **)t3malloc(siz); else desc_list_ = (CTcTokFileDesc **)t3realloc(desc_list_, siz); } /* add the new array entry */ desc_list_[desc_list_cnt_++] = desc; /* return it */ return desc; } /* ------------------------------------------------------------------------ */ /* * Add an include path entry. Each new entry goes at the end of the * list, after all previous entries. */ void CTcTokenizer::add_inc_path(const char *path) { tctok_incpath_t *entry; /* create a new path list entry */ entry = (tctok_incpath_t *)t3malloc(sizeof(tctok_incpath_t) + strlen(path)); /* store the path in the entry */ strcpy(entry->path, path); /* link this entry at the end of our list */ if (incpath_tail_ != 0) incpath_tail_->nxt = entry; else incpath_head_ = entry; incpath_tail_ = entry; entry->nxt = 0; } /* ------------------------------------------------------------------------ */ /* * Set the string capture file. */ void CTcTokenizer::set_string_capture(osfildef *fp) { /* delete any old capture file */ if (string_fp_ != 0) delete string_fp_; /* * Remember the new capture file. Use a duplicate handle, since we * pass ownership of the handle to the CVmFileSource object (i.e., * it'll close the handle when done). */ string_fp_ = new CVmFileSource(osfdup(fp, "w")); /* * if we don't already have a character mapping to translate from * our internal unicode characters back into the source file * character set, create one now */ if (string_fp_map_ == 0) { /* try creating a mapping for the default character set */ if (default_charset_ != 0) string_fp_map_ = CCharmapToLocal::load(res_loader_, default_charset_); /* if we couldn't create the mapping, use a default ASCII mapping */ if (string_fp_map_ == 0) string_fp_map_ = CCharmapToLocal::load(res_loader_, "us-ascii"); } } /* ------------------------------------------------------------------------ */ /* * Get the next token in the input stream, reading additional lines from * the source file as needed. */ tc_toktyp_t CTcTokenizer::next() { /* the current token is about to become the previous token */ prvtok_ = curtok_; /* if there's an un-got token, return it */ if (unget_cur_ != 0) { /* get the current unget token */ curtok_ = *unget_cur_; /* we've now consumed this ungotten token */ unget_cur_ = unget_cur_->getprv(); /* return the new token's type */ return curtok_.gettyp(); } /* if there's an external source, get its next token */ if (ext_src_ != 0) { const CTcToken *ext_tok; /* get the next token from the external source */ ext_tok = ext_src_->get_next_token(); /* check to see if we got a token */ if (ext_tok == 0) { /* * restore the current token in effect before this source was * active */ curtok_ = *ext_src_->get_enclosing_curtok(); /* * this source has no more tokens - restore the enclosing * source, and keep going so we try getting a token from it */ ext_src_ = ext_src_->get_enclosing_source(); /* return the token type */ return curtok_.gettyp(); } else { /* we got a token - copy it to our internal token buffer */ curtok_ = *ext_tok; /* return its type */ return curtok_.gettyp(); } } /* keep going until we get a valid token */ for (;;) { /* * read the next token from the current line, applying * appropriate string translations and storing strings and * symbols in the source block list */ tc_toktyp_t typ = next_on_line_xlat_keep(); /* if it's the "null" token, skip it and read another token */ if (typ == TOKT_NULLTOK) continue; /* if we found a valid token, we're done - return the token */ if (typ != TOKT_EOF) return typ; /* * if we're at the end of a preprocess line, don't read another * line - just return end of file */ if (p_.getch() == TOK_END_PP_LINE) return TOKT_EOF; /* * we've reached the end of the line - read another line, * applying preprocessing directives and expanding macros as * needed */ if (read_line_pp()) { /* no more lines are available - return end of file */ return TOKT_EOF; } } } /* ------------------------------------------------------------------------ */ /* * clear external token sources, returning to the true input stream */ void CTcTokenizer::clear_external_sources() { /* * restore the current token as it was before the outermost external * source was first established */ if (ext_src_ != 0) { CTcTokenSource *outer; /* find the outermost source */ for (outer = ext_src_ ; outer->get_enclosing_source() != 0 ; outer = ext_src_->get_enclosing_source()) ; /* restore its original next token */ curtok_ = *ext_src_->get_enclosing_curtok(); } /* there's no external source now */ ext_src_ = 0; } /* ------------------------------------------------------------------------ */ /* * Make a safely storable copy of the current token. */ const CTcToken *CTcTokenizer::copycur() { /* if it's already a type that we store safely, return the current token */ if (is_tok_safe(curtok_.gettyp())) return getcur(); /* save the current token's text in permanent tokenizer memory */ curtok_.set_text(store_source(curtok_.get_text(), curtok_.get_text_len()), curtok_.get_text_len()); /* return the current token, now that we've made it safe */ return &curtok_; } /* * Make a safely storable copy of a given token. */ void CTcTokenizer::copytok(CTcToken *dst, const CTcToken *src) { /* start with an exact copy of the token */ *dst = *src; /* if the token is a symbol, it already has a safe copy */ if (is_tok_safe(src->gettyp())) return; /* save the token's text in permanent tokenizer memory */ dst->set_text(store_source(dst->get_text(), dst->get_text_len()), dst->get_text_len()); } /* * Is the given token type "safe"? A safe token is one whose text is * always saved in the tokenizer source list, which means that the text * pointer is safe as long as the tokenizer object exists. A non-safe * token is one whose text pointer points directly into the current line * buffer, meaning that its text buffer is only guaranteed to last until * the next token fetch. */ int CTcTokenizer::is_tok_safe(tc_toktyp_t typ) { switch (typ) { case TOKT_SYM: case TOKT_SSTR: case TOKT_SSTR_START: case TOKT_SSTR_MID: case TOKT_SSTR_END: case TOKT_DSTR: case TOKT_DSTR_START: case TOKT_DSTR_MID: case TOKT_DSTR_END: case TOKT_RESTR: case TOKT_FLOAT: case TOKT_BIGINT: /* these types are always stored in the source list */ return TRUE; default: /* other types are not always safely stored */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Check to see if the current token matches the given text */ int CTcTokenizer::cur_tok_matches(const char *txt, size_t len) { /* if the length matches, and the text matches exactly, it matches */ return (getcur()->get_text_len() == len && memcmp(getcur()->get_text(), txt, len) == 0); } /* ------------------------------------------------------------------------ */ /* * Look ahead to see if we match a pair of symbol tokens */ int CTcTokenizer::look_ahead(const char *s1, const char *s2) { /* if the first token doesn't match, the sequence doesn't match */ if (cur() != TOKT_SYM || !cur_tok_matches(s1, strlen(s1))) return FALSE; /* check the next token - if it matches, we have a sequence match */ if (next() == TOKT_SYM && cur_tok_matches(s2, strlen(s2))) { /* got it - skip the second token and return success */ next(); return TRUE; } /* * no match - but we've already read the next token, so put it back * before we return failure */ unget(); return FALSE; } /* * Peek ahead - same as look_ahead, but doesn't skip anything on a * successful match. */ int CTcTokenizer::peek_ahead(const char *s1, const char *s2) { /* if the first token doesn't match, the sequence doesn't match */ if (cur() != TOKT_SYM || !cur_tok_matches(s1, strlen(s1))) return FALSE; /* check the next token - if it matches, we have a sequence match */ int match = (next() == TOKT_SYM && cur_tok_matches(s2, strlen(s2))); /* whatever happened, un-get the second token, since we're just peeking */ unget(); /* return the match indication */ return match; } /* ------------------------------------------------------------------------ */ /* * Un-get the current token */ void CTcTokenizer::unget() { /* unget, backing up to the internally saved previous token */ unget(&prvtok_); } /* * Un-get the current token and back up to the specified previous token. */ void CTcTokenizer::unget(const CTcToken *prv) { /* push the current token onto the unget stack */ push(&curtok_); /* go back to the previous token */ curtok_ = *prv; /* the internally saved previous token is no longer valid */ prvtok_.settyp(TOKT_INVALID); } /* * Push a token into the stream */ void CTcTokenizer::push(const CTcToken *tok) { /* if the unget list is empty, create the initial entry */ if (unget_head_ == 0) unget_head_ = new CTcTokenEle(); /* advance to the next slot in the list */ if (unget_cur_ == 0) { /* no last ungot token - set up at the head of the list */ unget_cur_ = unget_head_; } else if (unget_cur_->getnxt() != 0) { /* there's another slot available - advance to it */ unget_cur_ = unget_cur_->getnxt(); } else { /* the list is full - we need to allocate a new slot */ CTcTokenEle *newele = new CTcTokenEle(); /* link it at the end of the list */ unget_cur_->setnxt(newele); newele->setprv(unget_cur_); /* advance to it */ unget_cur_ = newele; } /* save the pushed token */ unget_cur_->set(*tok); } /* ------------------------------------------------------------------------ */ /* * Assume that we should have just found a '>>' terminating an embedded * expression in a string. If possible, back out the previous token and * re-scan it as though it had started with '>>'. * * This is to be called by a higher-level parser when it determines that, * syntactically, we should have found the '>>' leaving an embedded * expression. */ void CTcTokenizer::assume_missing_str_cont() { xlat_string_to_src(&main_in_embedding_, TRUE); } /* ------------------------------------------------------------------------ */ /* * Skip whitespace and macro expansion markers */ void CTcTokenizer::skip_ws_and_markers(utf8_ptr *p) { /* keep going until we find something interesting */ for (;;) { wchar_t cur; /* get the current character */ cur = p->getch(); /* * if it's a macro expansion end marker, skip it as though it * were whitespace; otherwise, if it's whitespace, skip it; * otherwise, we're done skipping leading whitespace */ if (cur == TOK_MACRO_EXP_END) { /* skip the embedded pointer value that follows */ p->set(p->getptr() + 1 + sizeof(CTcHashEntryPp *)); } else if (is_space(cur)) { /* skip the space */ p->inc(); } else { /* it's not whitespace or equivalent - we're done */ return; } } } /* ------------------------------------------------------------------------ */ /* * Get the next token from the input stream, operating on the current * line only. */ tc_toktyp_t CTcTokenizer::next_on_line(utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec, int expanding) { tc_toktyp_t typ; /* skip whitespace */ skip_ws_and_markers(p); /* remember where the token starts */ utf8_ptr start = *p; /* get the initial character */ wchar_t cur = p->getch(); /* if there's nothing left in the current line, return EOF */ if (cur == '\0') { /* indicate end of file */ typ = TOKT_EOF; goto done; } /* skip the initial character */ p->inc(); /* presume the token will not be marked as fully macro-expanded */ tok->set_fully_expanded(FALSE); /* see what we have */ switch(cur) { case TOK_MACRO_FORMAL_FLAG: /* * this is a two-byte formal parameter sequence in a macro * expansion - skip the second byte of the two-byte sequence, * and return the special token type for this sequence */ typ = TOKT_MACRO_FORMAL; /* * skip the second byte - note that we want to skip exactly one * byte, regardless of what the byte looks like as a utf-8 * partial character, since it's not a utf-8 character at all */ p->set(p->getptr() + 1); break; case TOK_MACRO_FOREACH_FLAG: /* * this is the special macro '#foreach' flag - return it as a * special pseudo-token */ typ = TOKT_MACRO_FOREACH; break; case TOK_MACRO_IFEMPTY_FLAG: /* #ifempty macro flag */ typ = TOKT_MACRO_IFEMPTY; break; case TOK_MACRO_IFNEMPTY_FLAG: /* #ifnempty macro flag */ typ = TOKT_MACRO_IFNEMPTY; break; case TOK_MACRO_ARGCOUNT_FLAG: /* it's the special macro '#argcount' flag */ typ = TOKT_MACRO_ARGCOUNT; break; case TOK_FULLY_EXPANDED_FLAG: /* set the token flag indicating that it has been fully expanded */ tok->set_fully_expanded(TRUE); /* the token symbol starts at the byte after the flag byte */ start = p->getptr(); /* read the first character of the symbol */ cur = p->getch(); p->inc(); /* tokenize the symbol that follows */ goto tokenize_symbol; case TOK_END_PP_LINE: /* * Preprocess line-ending marker - when we reach the end of a * preprocessor line, we can't read another source line, because * a preprocessor directive consists of only a single logical * source line. Once we see this, return end-of-file until the * caller explicitly reads a new source line. * * Keep the read pointer stuck on this flag byte, so that we * return end-of-file on a subsequent attempt to get the next * token. */ *p = start; typ = TOKT_EOF; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { /* * Start out with the leading digit in the accumulator. Note * that the character set internally is always UTF-8. */ ulong acc = value_of_digit(cur); /* * The overflow limit differs for positive and negative values, * but we can't be sure at this point which we have, since even * if there's a '-' before the number, it could be a * subtraction operator rather than a negation operator. We * can't know until we've folded constants whether the number * is positive or negative. Use the lower limit for now; the * compiler will un-promote values after constant folding if * they end up fitting after all, which will handle the case of * INT32MINVAL, whose absolute value is higher than this limit. */ const ulong ovlimit = 2147483647U; /* * If it's a leading zero, treat as octal or hex. '0x' means * hex; otherwise, '0' means octal. */ if (cur == '0') { /* check for hex - if it's not hex, it's octal */ if (p->getch() == 'x' || p->getch() == 'X') { /* skip the 'x' */ p->inc(); /* * scan the hex number - keep going until we find * something that's not a hex digit */ for (;;) { /* get this character */ cur = p->getch(); /* if it's not a hex digit, stop scanning */ if (!is_xdigit(cur)) break; /* check for 32-bit integer overflow */ if ((acc & 0xF0000000) != 0) goto do_int_overflow; /* * Shift the accumulator and add this digit's value. * Note that we can save a test - if the character is * >= lower-case 'a', we know it's not an upper-case * letter because the lower-case letters all have * values above the upper-case letters in UTF-8 * encoding (which we always use as the internal * character set). Since we already know it's a * valid hex digit (we wouldn't be here if it * weren't), we can just check to see if it's at * least lower-case 'a', and we automatically know * then whether it's in the 'a'-'f' range or the * 'A'-'F' range. */ acc *= 16; acc += value_of_xdigit(cur); /* move on */ p->inc(); } } else { /* scan octal digits */ for ( ; is_odigit(p->getch()) ; p->inc()) { /* check for overflow */ if ((acc & 0xE0000000) != 0) goto do_int_overflow; /* add the digit */ acc = 8*acc + value_of_odigit(p->getch()); } /* * If we stopped on a digit outside of the octal range, * consume any remaining digits, and flag it as an * error. Leaving subsequent decimal digits as a * separate token tends to be confusing, since in most * cases the inclusion of decimal digits means that the * user didn't really intend this to be an octal number * after all. For instance, the leading zero might be * there for formatting reasons, and the user simply * forgot to take into account that it triggers octal * interpretation. */ if (is_digit(p->getch())) { /* skip subsequent digits */ for (p->inc() ; is_digit(p->getch()) ; p->inc()) ; /* flag the error */ if (!expanding) log_error(TCERR_DECIMAL_IN_OCTAL, p->getptr() - start.getptr(), start.getptr()); } } } else { /* scan decimal digits */ for ( ; is_digit(p->getch()) ; p->inc()) { /* check for integer overflow in the shift */ if (acc > 214748364) goto do_int_overflow; /* shift the accumulator */ acc *= 10; /* get the digit */ int d = value_of_digit(p->getch()); /* check for overflow adding the digit */ if ((unsigned)acc > ovlimit - d) goto do_int_overflow; /* add the digit */ acc += d; } } /* * If we stopped at a decimal point or an exponent, it's a * floating point number. This doesn't count if we have two * periods in a row - ".." - because that's either a range * marker operator or an ellipsis operator. */ if (p->getch() == '.' && p->getch_at(1) != '.') { typ = TOKT_FLOAT; goto do_float; } else if (p->getch() == 'e' || p->getch() == 'E') { typ = TOKT_FLOAT; goto do_float; } /* it's an integer value */ typ = TOKT_INT; /* set the integer value */ tok->set_int_val(acc); } break; do_int_overflow: /* mark it as an integer that overflowed into a float type */ typ = TOKT_BIGINT; do_float: { /* haven't found a decimal point yet */ int found_decpt = FALSE; int is_hex = FALSE; /* start over at the beginning of the number */ *p = start; /* skip any leading sign */ if (p->getch() == '-') p->inc(); /* skip any leading "0x" */ if (typ == TOKT_BIGINT && p->getch() == '0') { wchar_t c1 = p->getch_at(1); if (c1 == 'x' || c1 == 'X') { is_hex = TRUE; p->inc_by(2); } } /* parse the rest of the float */ for ( ; ; p->inc()) { /* get this character and move on */ cur = p->getch(); /* see what we have */ if (is_digit(cur)) { /* we have another digit; just keep going */ } else if (is_hex && is_xdigit(cur)) { /* we have another hex digit, so keep going */ } else if (!found_decpt && !is_hex && cur == '.') { /* it's the decimal point - note it and keep going */ found_decpt = TRUE; /* if it started as a promoted int, it's really a float */ if (typ == TOKT_BIGINT) typ = TOKT_FLOAT; } else if (cur == 'e' || cur == 'E') { /* it might not be an exponent - look ahead to find out */ utf8_ptr p2 = *p; p2.inc(); /* if we have a sign, skip it */ if ((cur = p2.getch()) == '-' || cur == '+') p2.inc(); /* we need at least one digit to make an exponent */ if (!is_digit(p2.getch())) break; /* skip digits */ while (is_digit(p2.getch())) p2.inc(); /* advance to the end of the exponent */ *p = p2; /* if it started as a promoted int, it's now a float */ if (typ == TOKT_BIGINT) typ = TOKT_FLOAT; /* the end of the exponent is the end of the number */ break; } else { /* everything else ends the number */ break; } } } break; case '"': case '\'': *p = start; return tokenize_string(p, tok, ec, expanding); case '(': typ = TOKT_LPAR; if (ec != 0) ec->parens(1); break; case ')': typ = TOKT_RPAR; if (ec != 0) ec->parens(-1); break; case ',': typ = TOKT_COMMA; break; case '.': /* check for '..', '...', and floating-point numbers */ if (p->getch() == '.') { /* it's either '..' or '...' */ p->inc(); if (p->getch() == '.') { p->inc(); typ = TOKT_ELLIPSIS; } else typ = TOKT_DOTDOT; } else if (is_digit(p->getch())) { typ = TOKT_FLOAT; goto do_float; } else typ = TOKT_DOT; break; case '{': typ = TOKT_LBRACE; if (ec != 0) ec->parens(1); break; case '}': typ = TOKT_RBRACE; if (ec != 0) ec->parens(-1); break; case '[': typ = TOKT_LBRACK; if (ec != 0) ec->parens(1); break; case ']': typ = TOKT_RBRACK; if (ec != 0) ec->parens(-1); break; case '=': /* check for '==' */ if (p->getch() == '=') { p->inc(); typ = TOKT_EQEQ; } else typ = TOKT_EQ; break; case ':': /* check for '::' */ if (p->getch() == ':') { p->inc(); typ = TOKT_COLONCOLON; } else typ = TOKT_COLON; break; case '?': /* check for '??' */ if (p->getch() == '?') { p->inc(); typ = TOKT_QQ; } else typ = TOKT_QUESTION; break; case '+': /* check for '++' and '+=' */ if (p->getch() == '+') { p->inc(); typ = TOKT_INC; } else if (p->getch() == '=') { p->inc(); typ = TOKT_PLUSEQ; } else typ = TOKT_PLUS; break; case '-': /* check for '--', '->' and '-=' */ if (p->getch() == '-') { p->inc(); typ = TOKT_DEC; } else if (p->getch() == '=') { p->inc(); typ = TOKT_MINEQ; } else if (p->getch() == '>') { p->inc(); typ = TOKT_ARROW; } else typ = TOKT_MINUS; break; case '*': /* check for '*=' */ if (p->getch() == '=') { p->inc(); typ = TOKT_TIMESEQ; } else typ = TOKT_TIMES; break; case '/': /* check for '/=' */ if (p->getch() == '=') { p->inc(); typ = TOKT_DIVEQ; } else typ = TOKT_DIV; break; case '%': /* check for '%=' */ if (p->getch() == '=') { p->inc(); typ = TOKT_MODEQ; } else typ = TOKT_MOD; break; case '>': /* check for '>>=', '>>' and '>=' */ if (p->getch() == '=') { p->inc(); typ = TOKT_GE; } else if (p->getch() == '>') { /* check for the end of an embedded expression */ if (ec != 0 && ec->in_expr() && ec->parens() == 0) { *p = start; return tokenize_string(p, tok, ec, expanding); } /* check for '>>>' and '>>=' */ p->inc(); if (p->getch() == '>') { /* skip the '>' and check for '>>>=' */ p->inc(); if (p->getch() == '=') { p->inc(); typ = TOKT_LSHREQ; } else typ = TOKT_LSHR; } else if (p->getch() == '=') { p->inc(); typ = TOKT_ASHREQ; } else typ = TOKT_ASHR; } else typ = TOKT_GT; break; case '<': /* check for '<<=', '<<', '<>', and '<=' */ if (p->getch() == '=') { p->inc(); typ = TOKT_LE; } else if (p->getch() == '<') { /* check for '<<=' */ p->inc(); if (p->getch() == '=') { p->inc(); typ = TOKT_SHLEQ; } else typ = TOKT_SHL; } #if 0 else if (p->getch() == '>') { /* '<>' is obsolete */ if (!expanding) log_error(TCERR_LTGT_OBSOLETE); /* ... but for now proceed as though it's != */ p->inc(); typ = TOKT_NE; } #endif else typ = TOKT_LT; break; case ';': typ = TOKT_SEM; break; case '&': /* check for '&&' and '&=' */ if (p->getch() == '&') { p->inc(); typ = TOKT_ANDAND; } else if (p->getch() == '=') { p->inc(); typ = TOKT_ANDEQ; } else typ = TOKT_AND; break; case '|': /* check for '||' and '|=' */ if (p->getch() == '|') { p->inc(); typ = TOKT_OROR; } else if (p->getch() == '=') { p->inc(); typ = TOKT_OREQ; } else typ = TOKT_OR; break; case '^': /* check for '^=' */ if (p->getch() == '=') { p->inc(); typ = TOKT_XOREQ; } else typ = TOKT_XOR; break; case '!': /* check for '!=' */ if (p->getch() == '=') { p->inc(); typ = TOKT_NE; } else typ = TOKT_NOT; break; case '~': typ = TOKT_BNOT; break; case '@': typ = TOKT_AT; break; case '#': /* check for '##' and '#@' */ if (p->getch() == '#') { p->inc(); typ = TOKT_POUNDPOUND; } else if (p->getch() == '@') { p->inc(); typ = TOKT_POUNDAT; } else typ = TOKT_POUND; break; case 'R': /* check for regular expression string syntax */ if (p->getch() == '"' || p->getch() == '\'') { /* * It's a regular expression string. Tokenize the string * portion as normal, but mark it as a regex string rather than * a regular string. */ *p = start; return tokenize_string(p, tok, ec, expanding); } /* not a regex string - fall through to default case */ default: /* check to see if it's a symbol */ if (is_syminit(cur) || !is_ascii(cur)) { tokenize_symbol: wchar_t non_ascii = 0; const char *stop = 0; /* note if we're starting with a non-ASCII character */ if (!is_ascii(cur)) non_ascii = cur; /* * scan the identifier (note that we've already skipped the * first character, so we start out at length = 1) */ for (;;) { /* * If it's a non-ASCII character, assume it's an accented * character that's meant (erroneously) to be part of the * symbol name; note the error, but keep the character in * the symbol even though it's illegal, since this is more * likely to keep us synchronized with the intended token * structure of the source. * * Otherwise, stop on any non-symbol character. */ wchar_t ch = p->getch(); if (!is_ascii(ch)) { if (non_ascii == 0) non_ascii = ch; } else if (!is_sym(ch)) break; /* skip this character */ const char *prv = p->getptr(); p->inc(); /* if this character would push us over the limit, end here */ if (stop == 0 && p->getptr() - start.getptr() >= TOK_SYM_MAX_LEN) stop = prv; } /* if we found any non-ASCII characters, flag an error */ if (non_ascii != 0) log_error(TCERR_NON_ASCII_SYMBOL, (int)(p->getptr() - start.getptr()), start.getptr(), (int)non_ascii); /* if we truncated the symbol, issue a warning */ if (stop != 0 && !expanding) log_warning(TCERR_SYMBOL_TRUNCATED, (int)(p->getptr() - start.getptr()), start.getptr(), (int)(stop - start.getptr()), start.getptr()); /* it's a symbol */ typ = TOKT_SYM; } else { /* invalid token */ typ = TOKT_INVALID; } break; } done: /* set the type */ tok->settyp(typ); /* set the text */ tok->set_text(start.getptr(), p->getptr() - start.getptr()); /* return the type */ return typ; } /* * get the next token, limiting to the length of the source buffer */ tc_toktyp_t CTcTokenizer::next_on_line(const CTcTokString *srcbuf, utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec, int expanding) { /* * save the embedding context, in case the next token isn't part of the * narrowed section of the buffer we're scanning */ tok_embed_ctx oldec = *ec; /* get the next token */ next_on_line(p, tok, ec, expanding); /* if the token is past the end of the line, return EOF */ if (tok->get_text() >= srcbuf->get_text_end()) { /* set the token to indicate end of line */ tok->settyp(TOKT_EOF); /* set the token to point to the end of the buffer */ tok->set_text(srcbuf->get_text_end(), 0); /* restore the embedding context */ *ec = oldec; } /* return the token type */ return tok->gettyp(); } /* * Get the next token on the line, translating escapes in strings. This * updates the line buffer in-place to incorporate the translated string * text. */ tc_toktyp_t CTcTokenizer::next_on_line_xlat(utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec) { /* skip whitespace */ skip_ws_and_markers(p); /* if this is a string, translate escapes */ switch(p->getch()) { case '"': case '\'': /* translate the string */ return xlat_string(p, tok, ec); case '>': /* if we're in an embedding, check for '>>' */ if (ec != 0 && ec->in_expr() && ec->parens() == 0 && p->getch_at(1) == '>') return tokenize_string(p, tok, ec, FALSE); /* use the default case */ goto do_normal; case 'R': /* check for a regex string */ if (p->getch_at(1) == '"' || p->getch_at(1) == '\'') return tokenize_string(p, tok, ec, FALSE); /* not a regex string - fall through to the default handling */ default: do_normal: /* for anything else, use the default tokenizer */ return next_on_line(p, tok, ec, FALSE); } } /* * Look up a keyword */ int CTcTokenizer::look_up_keyword(const CTcToken *tok, tc_toktyp_t *kwtok) { CTcHashEntryKw *kw; /* look it up in the keyword table */ kw = (CTcHashEntryKw *)kw_->find(tok->get_text(), tok->get_text_len()); if (kw != 0) { /* we found the keyword - set 'kw' to the keyword token id */ *kwtok = kw->get_tok_id(); /* tell the caller we found it */ return TRUE; } else { /* tell the caller it's not a keyword */ return FALSE; } } /* * Get the next token on the line, translating escape sequences in * strings, and storing strings and symbols in the source block list. * This routine also translates keywords for token types. */ tc_toktyp_t CTcTokenizer::next_on_line_xlat_keep() { tc_toktyp_t typ; /* keep going until we find a valid symbol */ for (;;) { /* skip whitespace and macro expansion flags */ skip_ws_and_markers(&p_); /* see what we have */ switch(p_.getch()) { case '"': case '\'': /* it's a string - translate and save it */ return xlat_string_to_src(&main_in_embedding_, FALSE); case '>': /* if we're in an embedding, this is the end of it */ if (main_in_embedding_.in_expr() && main_in_embedding_.parens() == 0 && p_.getch_at(1) == '>') return xlat_string_to_src(&main_in_embedding_, FALSE); /* use the normal parsing */ goto do_normal; case 'R': /* check for a regex string */ if (p_.getch_at(1) == '"' || p_.getch_at(1) == '\'') return xlat_string_to_src(&main_in_embedding_, FALSE); /* not a regex string - fall through to the default case */ default: do_normal: /* for anything else, use the default tokenizer */ typ = next_on_line(&p_, &curtok_, &main_in_embedding_, FALSE); /* check the token type */ switch(typ) { case TOKT_SYM: /* symbol */ { const char *p; CTcHashEntryKw *kw; /* look it up in the keyword table */ kw = (CTcHashEntryKw *)kw_->find(curtok_.get_text(), curtok_.get_text_len()); if (kw != 0) { /* replace the token with the keyword token type */ typ = kw->get_tok_id(); curtok_.settyp(typ); } else { /* ordinary symbol - save the text */ p = store_source(curtok_.get_text(), curtok_.get_text_len()); /* * change the token's text to point to the * source block, so that this token's text * pointer will remain permanently valid (the * original copy, in the source line buffer, * will be overwritten as soon as we read * another source line; we don't want the caller * to have to worry about this, so we return the * permanent copy) */ curtok_.set_text(p, curtok_.get_text_len()); } } break; case TOKT_FLOAT: case TOKT_BIGINT: /* floating-point number (or promoted large integer) */ { const char *p; /* * save the text so that it remains permanently * valid - we keep track of floats by the original * text, and let the code generator produce the * appropriate object file representation */ p = store_source(curtok_.get_text(), curtok_.get_text_len()); curtok_.set_text(p, curtok_.get_text_len()); } break; case TOKT_INVALID: /* * check for unmappable characters - these will show up as * Unicode U+FFFD, the "replacement character"; log it as * 'unmappable' if applicable, otherwise as an invalid * character */ if (utf8_ptr::s_getch(curtok_.get_text()) == 0xfffd) log_error_curtok(TCERR_UNMAPPABLE_CHAR); else log_error_curtok(TCERR_INVALID_CHAR); /* skip this character */ p_.inc(); /* keep going */ continue; default: break; } } /* return the type */ return typ; } } /* * Translate the string at the current token position in the input * stream to the source block list. */ tc_toktyp_t CTcTokenizer::xlat_string_to_src( tok_embed_ctx *ec, int force_embed_end) { /* * Reserve space for the entire rest of the line. This is * conservative, in that we will definitely need less space than * this. This might cause us to waste a little space here and * there, since we will over-allocate when we have a short string * early in a long line, but this will save us the time of scanning * the string twice just to see how long it is. */ reserve_source(curbuf_->get_text_len() - (p_.getptr() - curbuf_->get_text())); /* translate into the source block */ tc_toktyp_t typ = xlat_string_to( src_ptr_, &p_, &curtok_, ec, force_embed_end); /* commit the space in the source block */ commit_source(curtok_.get_text_len() + 1); /* return the string token */ return typ; } /* * Translate a string, setting up the token structure for the string, * and writing the translated version of the string directly over the * original source buffer of the string. * * Since a translated string can only shrink (because a translated * escape sequence is always shorter than the original source version), * we don't need a separate buffer, but can simply translate into the * source buffer, overwriting the original string as we go. */ tc_toktyp_t CTcTokenizer::xlat_string(utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec) { /* * write the translated string over the original string's text, * starting at the character after the quote */ char *dst = p->getptr() + 1; /* translate the string into our destination buffer */ return xlat_string_to(dst, p, tok, ec, FALSE); } /* * Count a run of consecutive quotes */ static int count_quotes(const utf8_ptr *p, wchar_t qu) { /* count quotes */ int cnt = 0; for (utf8_ptr qp((utf8_ptr *)p) ; qp.getch() == qu ; qp.inc(), ++cnt); /* return the count */ return cnt; } /* * Translate a string, setting up the token structure for the string. * We'll update the line buffer in-place to incorporate the translated * string text. */ tc_toktyp_t CTcTokenizer::xlat_string_to( char *dstp, utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec, int force_embed_end) { /* set up our output utf8 pointer */ utf8_ptr dst(dstp); /* note the open quote character */ wchar_t qu = p->getch(); /* set the appropriate string token type */ tok->settyp(qu == '"' ? TOKT_DSTR : qu == '\'' ? TOKT_SSTR : qu == 'R' ? TOKT_RESTR : ec != 0 ? ec->endtok() : TOKT_INVALID); /* if it's a regex string, skip the 'R' and note the actual quote type */ if (qu == 'R') { p->inc(); qu = p->getch(); } /* * If we're at a quote (rather than at '>>' for continuing from an * embedded expression), count consecutive open quotes, to determine if * we're in a triple-quoted string. */ int triple = FALSE; if (qu == '"' || qu == '\'') { /* count the consecutive open quotes */ if (count_quotes(p, qu) >= 3) { /* skip past the additional two open quotes */ p->inc(); p->inc(); /* note that we're in a triple-quoted string */ triple = TRUE; } } /* skip the open quote */ p->inc(); /* check for the end of an embedded expression (forced or actual) */ if (force_embed_end) { /* * they want us to assume the embedding ends here, regardless of * what we're looking at - act the same as though we had * actually seen '>>', but don't skip any input (in fact, back * up one, since we already skipped one character for what we * had thought was the open quote */ p->dec(); /* restore the enclosing string context */ qu = ec->qu(); triple = ec->triple(); tok->settyp(ec->endtok()); /* clear the caller's in-embedding status */ ec->end_expr(); } else if (qu == '>' && ec->parens() == 0) { /* skip the second '>' */ p->inc(); /* restore the enclosing string context */ qu = ec->qu(); triple = ec->triple(); /* end the expression */ ec->end_expr(); } /* scan the string and translate quotes */ for (;;) { /* get this character */ wchar_t cur = p->getch(); /* if this is the matching quote, we're done */ if (cur == qu) { /* * If we're in a triple-quote string, count consecutive quotes. * Triple quotes are greedy: if we have N>3 quotes in a row, * the first N-3 are inside the string, and the last 3 are the * terminating quotes. */ if (triple) { /* we need at least three quotes to end the string */ int qcnt = count_quotes(p, qu); if (qcnt >= 3) { /* copy all but the last three quotes to the output */ for ( ; qcnt > 3 ; --qcnt, p->inc()) dst.setch(qu); /* skip the three quotes */ p->inc_by(3); /* done with the string */ break; } } else { /* * It's an ordinary string, which ends with just one * matching quote, so we're done no matter what follows. * Skip the quote and stop scanning. */ p->inc(); break; } } /* * if we find an end-of-line within the string, it's an error - * we should always splice strings together onto a single line * before starting to tokenize the line */ if (cur == '\0') { /* set the token's text pointer */ size_t bytelen = dst.getptr() - dstp; tok->set_text(dstp, bytelen); /* null-terminate the result string */ dst.setch('\0'); /* * get the length of the unterminated string so far, but for * error logging, limit the length to twenty characters -- * we just want to give the user enough information to find * the string in error, without making the error message * huge */ utf8_ptr dp(dstp); size_t charlen = dp.len(bytelen); if (charlen > 20) bytelen = dp.bytelen(20); /* * Check for a special heuristic case. If the string was of * zero length, and we have something sitting in our * unsplice buffer, here's what probably happened: the input * was missing a ">>" sequence at the end of an embedded * expression, and the parser told us to put it back in. We * had earlier decided we needed to splice up to a quote to * end what looked to us like an unterminated string. If * this is the case, we and the parser are working at cross * purposes; the parser is smarter than we are, so we should * synchronize with it. */ if (tok->get_text_len() == 0 && qu == '"' && unsplicebuf_.get_text_len() != 0) { /* * we must have spliced a line to finish a string - * insert the quote into the splice buffer, and ignore * it here */ /* * make sure there's room for one more character (plus a * null byte) */ unsplicebuf_.ensure_space(unsplicebuf_.get_text_len() + 2); /* get the buffer pointer */ char *buf = unsplicebuf_.get_buf(); /* make room for the '"' */ memmove(buf + 1, buf, unsplicebuf_.get_text_len()); unsplicebuf_.set_text_len(unsplicebuf_.get_text_len() + 1); /* add the '"' */ *buf = '"'; /* * return the 'null token' to tell the caller to try * again - do not log an error at this point */ return TOKT_NULLTOK; } /* log the error */ log_error(TCERR_UNTERM_STRING, (char)qu, (int)bytelen, dstp, (char)qu); /* return the string type */ return tok->gettyp(); } /* if this is an escape, translate it */ if (cur == '\\') { /* translate the escape */ xlat_escape(&dst, p, qu, triple); continue; } else if (ec != 0 && tok->gettyp() != TOKT_RESTR && cur == '<' && p->getch_at(1) == '<') { /* * It's the start of an embedded expression - change the type * to so indicate. If we think we have a regular SSTR or DSTR, * switch to the appropriate START type, since the part up to * here is actually the starting fragment of a string with an * embedded expression. If we think we're in an END section, * switch to the corresponding MID section, since we're parsing * a fragment that already followed an embedding. The only * other possibility is that we're in a MID section, in which * case just stay in the same MID section. */ tc_toktyp_t tt = tok->gettyp(); tok->settyp(tt == TOKT_DSTR ? TOKT_DSTR_START : tt == TOKT_DSTR_END ? TOKT_DSTR_MID : tt == TOKT_SSTR ? TOKT_SSTR_START : tt == TOKT_SSTR_END ? TOKT_SSTR_MID : tt); /* skip the << */ p->inc_by(2); /* * Check for a '%' sprintf-style formatting sequence. If we * have a '%' immediately after the second '<', it's a sprintf * format code. */ if (p->getch() == '%') { /* remember the starting point of the format string */ utf8_ptr fmt(p); /* scan the format spec */ scan_sprintf_spec(p); /* translate escapes */ utf8_ptr dst(&fmt); xlat_escapes(&dst, &fmt, p); /* push the format spec into the token stream */ CTcToken ftok; ftok.set_text(fmt.getptr(), dst.getptr() - fmt.getptr()); ftok.settyp(TOKT_FMTSPEC); push(&ftok); } /* tell the caller we're in an embedding */ ec->start_expr(qu, triple, TRUE); /* stop scanning */ break; } /* copy this character to the output position */ dst.setch(cur); /* get the next character */ p->inc(); } /* set the token's text pointer */ tok->set_text(dstp, dst.getptr() - dstp); /* null-terminate the result string */ dst.setch('\0'); /* return the string type */ return tok->gettyp(); } /* * Translate all escapes in a string */ void CTcTokenizer::xlat_escapes(utf8_ptr *dst, const utf8_ptr *srcp, const utf8_ptr *endp) { /* set up writable copy of the source */ utf8_ptr p(srcp); /* scan the string */ while (p.getptr() < endp->getptr()) { /* check for an escape */ wchar_t ch = p.getch(); if (ch == '\\') { /* escape - translate it */ xlat_escape(dst, &p, 0, FALSE); } else { /* ordinary character - copy it as is */ dst->setch(ch); p.inc(); } } } /* * Skip a \ escape sequence */ void CTcTokenizer::skip_escape(utf8_ptr *p) { if (p->getch() == '\\') { /* translate an escape into an empty buffer */ char buf[10]; utf8_ptr dst(buf); xlat_escape(&dst, p, 0, FALSE); } else { /* just skip the character */ p->inc(); } } /* * Translate a \ escape sequence */ void CTcTokenizer::xlat_escape(utf8_ptr *dst, utf8_ptr *p, wchar_t qu, int triple) { int i, acc; /* get the character after the escape */ p->inc(); wchar_t cur = p->getch(); /* see what we have */ switch(cur) { case '^': /* caps - 0x000F */ cur = 0x000F; break; case '"': case '\'': /* * If we're in triple-quote mode, and this is the matching quote, a * single backslash escapes a whole run of consecutive quotes, so * skip the whole run. */ if (triple && cur == qu) { /* copy and skip all consecutive quotes */ for ( ; p->getch() == qu ; dst->setch(cur), p->inc()) ; /* we're done */ return; } break; case 'v': /* miniscules - 0x000E */ cur = 0x000E; break; case 'b': /* blank line - 0x000B */ cur = 0x000B; break; case ' ': /* quoted space - 0x0015 */ cur = 0x0015; break; case 'n': /* newline - explicitly use Unicode 10 character */ cur = 10; break; case 'r': /* return - explicitly use Unicode 13 character */ cur = 13; break; case 't': /* tab - explicitly use Unicode 9 character */ cur = 9; break; case 'u': /* * Hex unicode character number. Read up to 4 hex digits that * follow the 'u', and use that as a Unicode character ID. */ for (i = 0, acc = 0, p->inc() ; i < 4 ; ++i, p->inc()) { /* get the next character */ cur = p->getch(); /* * if it's another hex digit, add it into the accumulator; * otherwise, we're done */ if (is_xdigit(cur)) acc = 16*acc + value_of_xdigit(cur); else break; } /* use the accumulated value as the character number */ dst->setch((wchar_t)acc); /* we've already skipped ahead to the next character, so we're done */ return; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': /* * Octal ASCII character number. Accumulate up to three octal * numbers, and use the result as a character ID. */ for (i = 0, acc = 0 ; i < 3 ; ++i, p->inc()) { /* get the next character */ cur = p->getch(); /* * if it's another digit, and it would leave our result in the * 0-255 range, count it; if not, we're done */ if (is_odigit(cur)) { /* compute the new value */ long new_acc = 8*acc + value_of_odigit(cur); /* if this would be too high, don't count it */ if (new_acc > 255) break; else acc = new_acc; } else break; } /* use the accumulated value as the character number */ dst->setch((wchar_t)acc); /* * continue with the current character, since we've already skipped * ahead to the next one */ return; case 'x': /* * Hex ASCII character number. Read up to two hex digits as a * character number. */ for (i = 0, acc = 0, p->inc() ; i < 2 ; ++i, p->inc()) { /* get the next character */ cur = p->getch(); /* * if it's another hex digit, add it into the accumulator; * otherwise, we're done */ if (is_xdigit(cur)) acc = 16*acc + value_of_xdigit(cur); else break; } /* use the accumulated value as the character number */ dst->setch((wchar_t)acc); /* * continue with the current character, since we've already skipped * ahead to the next one */ return; case '<': case '>': case '\\': /* just copy these literally */ break; default: /* log a pedantic error */ log_pedantic(TCERR_BACKSLASH_SEQ, cur); /* copy anything else as-is, including the backslash */ dst->setch('\\'); break; } /* set the output character */ dst->setch(cur); /* skip the current character */ p->inc(); } /* * Scan a sprintf format spec */ void CTcTokenizer::scan_sprintf_spec(utf8_ptr *p) { /* skip the '%' */ p->inc(); /* scan the flags section */ for (int done = FALSE ; !done ; ) { switch (p->getch()) { case '[': /* skip digits and the closing ']' */ for (p->inc() ; is_digit(p->getch()) ; p->inc()) ; if (p->getch() == ']') p->inc(); break; case '_': /* padding spec - skip this and the next character */ p->inc(); if (p->getch() != 0) skip_escape(p); break; case '-': case '+': case ' ': case ',': case '#': /* format spec - just skip it */ p->inc(); break; default: /* anything else means we're done with the flags */ done = TRUE; break; } } /* scan the width */ for ( ; is_digit(p->getch()) ; p->inc()) ; /* scan the precision */ if (p->getch() == '.') for (p->inc() ; is_digit(p->getch()) ; p->inc()) ; /* the next character is the format spec - skip it */ if (p->getch() != 0) skip_escape(p); } /* * Skip a string, setting up the token structure for the string. This * routine only parses to the end of the line; if the line ends with the * string unterminated, we'll flag an error. */ tc_toktyp_t CTcTokenizer::tokenize_string( utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec, int expanding) { /* remember where the text starts */ const char *start = p->getptr(); /* check for a regex string - R'...' or R"..." */ int regex = (p->getch() == 'R'); if (regex) p->inc(); /* note the quote type, for matching later */ wchar_t qu = p->getch(); /* skip the quote in the input */ p->inc(); /* check for triple quotes */ int triple = FALSE; if ((qu == '"' || qu == '\'') && count_quotes(p, qu) >= 2) { /* note the triple quotes */ triple = TRUE; /* skip the open quotes */ p->inc(); p->inc(); } /* determine the token type based on the quote type */ tc_toktyp_t typ; int allow_embedding; switch(qu) { case '\'': /* single-quoted string */ typ = TOKT_SSTR; allow_embedding = (ec != 0); break; case '"': /* regular double-quoted string */ typ = TOKT_DSTR; allow_embedding = (ec != 0); break; case '>': /* get the ending type for the embedding */ if (ec != 0) { /* return to the enclosing string context */ typ = ec->endtok(); qu = ec->qu(); triple = ec->triple(); /* allow more embeddings */ allow_embedding = TRUE; /* exit the expression context */ ec->end_expr(); } else { /* we can only finish an embedding if we were already in one */ typ = TOKT_INVALID; qu = '"'; allow_embedding = FALSE; } /* skip the extra '>' character */ p->inc(); break; default: /* anything else is invalid */ typ = TOKT_INVALID; qu = '"'; allow_embedding = FALSE; break; } /* if it's a regex string, it has special handling */ if (regex) { /* embeddings aren't allowed in regex strings */ allow_embedding = FALSE; /* the token type is 'regex string' */ typ = TOKT_RESTR; } /* this is where the string's contents start */ const char *contents_start = p->getptr(); /* we don't know where it ends yet */ const char *contents_end; /* scan the string */ for (;;) { /* get the current character */ wchar_t cur = p->getch(); /* see what we have */ if (cur == '\\') { /* escape sequence - skip an extra character */ p->inc(); /* * if we're in a triple-quoted string, and this matches the * quote type, a single '\' escapes all consecutive quotes */ if (triple && p->getch() == qu) { /* skip the whole run of quotes */ for ( ; p->getch() == qu ; p->inc()) ; /* take it from the top, as we've already skipped them all */ continue; } } else if (cur == '<' && allow_embedding && p->getch_at(1) == '<') { /* * it's the start of an embedded expression - return the * appropriate embedded string part type */ typ = (typ == TOKT_DSTR ? TOKT_DSTR_START : typ == TOKT_DSTR_END ? TOKT_DSTR_MID : typ == TOKT_SSTR ? TOKT_SSTR_START : typ == TOKT_SSTR_END ? TOKT_SSTR_MID : typ); /* remember that we're in an embedding in the token stream */ ec->start_expr(qu, triple, !expanding); /* this is where the contents end */ contents_end = p->getptr(); /* skip the two embedding characters */ p->inc(); p->inc(); /* we're done - set the text in the token */ tok->set_text(start, p->getptr() - start); /* done */ break; } else if (cur == qu) { /* * if we're in a triple-quoted string, it ends at a triple * quote, except that any additional consecutive quotes go * inside the string rather than outside */ if (triple) { /* we need at least three quotes in a row to end the string */ int qcnt = count_quotes(p, qu); if (qcnt >= 3) { /* the contents include any quotes before the last 3 */ p->inc_by(qcnt - 3); contents_end = p->getptr(); /* skip the three close quotes */ p->inc_by(3); } else { /* it's not ending; skip the quotes and carry on */ p->inc_by(qcnt); continue; } } else { /* note where the string ends */ contents_end = p->getptr(); /* skip the closing quote */ p->inc(); } /* we're done - set the text in the token */ tok->set_text(start, p->getptr() - start); /* done */ break; } else if (cur == '\0') { /* this is where the contents end */ contents_end = p->getptr(); /* * We have an unterminated string. If we're evaluating a * preprocessor constant expression, log an error; otherwise * let it go for now, since we'll catch the error during the * normal tokenizing pass for parsing. */ if (G_tok->in_pp_expr_) log_error(TCERR_PP_UNTERM_STRING); /* set the partial text */ tok->set_text(start, p->getptr() - start); /* end of line - return with the string unfinished */ break; } /* skip this character of input */ p->inc(); } /* * if we're not in preprocessor mode, and we're saving string text, * write the string to the string text output file */ if (!G_tok->in_pp_expr_ && G_tok->string_fp_ != 0 && contents_start != contents_end) { /* write the line, translating back to the source character set */ G_tok->string_fp_map_ ->write_file(G_tok->string_fp_, contents_start, (size_t)(contents_end - contents_start)); /* add a newline */ G_tok->string_fp_->write("\n", 1); } /* set the type in the token */ tok->settyp(typ); /* return the token type */ return tok->gettyp(); } /* ------------------------------------------------------------------------ */ /* * Read a source line and handle preprocessor directives. This routine * will transparently handle #include, #define, and other directives; * when this routine returns, the input buffer will have a line of text * that contains no # directive. * * Returns zero on success, non-zero upon reaching the end of the input. */ int CTcTokenizer::read_line_pp() { /* * Read the next line from the input. If that fails, return an end * of file indication. */ int ofs = read_line(FALSE); if (ofs == -1) return 1; /* * before we process comments, note whether or not the line started * out within a character string */ int started_in_string = (in_quote_ != '\0'); /* set up our source pointer to the start of the new line */ start_new_line(&linebuf_, ofs); /* skip leading whitespace */ while (is_space(p_.getch())) p_.inc(); /* * If this line begins with a '#', process the directive. Ignore * any initial '#' if the line started off in a string. */ if (!started_in_string && p_.getch() == '#' && allow_pp_) { struct pp_kw_def { const char *kw; int process_in_false_if; void (CTcTokenizer::*func)(); }; static pp_kw_def kwlist[] = { { "charset", FALSE, &CTcTokenizer::pp_charset }, { "pragma", FALSE, &CTcTokenizer::pp_pragma }, { "include", FALSE, &CTcTokenizer::pp_include }, { "define", FALSE, &CTcTokenizer::pp_define }, { "if", TRUE, &CTcTokenizer::pp_if }, { "ifdef", TRUE, &CTcTokenizer::pp_ifdef }, { "ifndef", TRUE, &CTcTokenizer::pp_ifndef }, { "else", TRUE, &CTcTokenizer::pp_else }, { "elif", TRUE, &CTcTokenizer::pp_elif }, { "endif", TRUE, &CTcTokenizer::pp_endif }, { "error", FALSE, &CTcTokenizer::pp_error }, { "undef", FALSE, &CTcTokenizer::pp_undef }, { "line", FALSE, &CTcTokenizer::pp_line }, { 0, 0, 0 } }; pp_kw_def *kwp; const char *kwtxt; size_t kwlen; /* skip the '#' */ p_.inc(); /* * If the line ended inside a comment, read the next line until * we're no longer in a comment. The ANSI C preprocessor rules * say that a newline in a comment should not be treated as a * lexical newline, so pretend that the next line is part of the * preprocessor line in such a case. */ while (str_->is_in_comment()) { size_t p_ofs; /* remember the current offset in the line buffer */ p_ofs = p_.getptr() - linebuf_.get_buf(); /* append another line - stop at the end of the stream */ if (read_line(TRUE) == -1) break; /* restore the line pointer, in case the buffer moved */ start_new_line(&linebuf_, p_ofs); } /* read the directive */ next_on_line(); /* * if we've reached the end of the line, it's a null directive; * simply return an empty line */ if (curtok_.gettyp() == TOKT_EOF) { clear_linebuf(); return 0; } /* get the text and length of the keyword */ kwtxt = curtok_.get_text(); kwlen = curtok_.get_text_len(); /* if it's not a symbol, it's not a valid directive */ if (curtok_.gettyp() != TOKT_SYM) { /* log the error and return an empty line */ log_error(TCERR_INV_PP_DIR, (int)kwlen, kwtxt); clear_linebuf(); return 0; } /* determine which keyword we have, and process it */ for (kwp = kwlist ; kwp->kw != 0 ; ++kwp) { /* is this our keyword? */ if (strlen(kwp->kw) == kwlen && memcmp(kwtxt, kwp->kw, kwlen) == 0) { /* * This is our directive. * * If we're in the false branch of a #if block, only * process the directive if it's a kind of directive * that we should process in false #if branches. The * only directives that we process in #if branches are * those that would affect the #if branching, such as a * #endif or a nested #if. */ if (!in_false_if() || kwp->process_in_false_if) { /* invoke the handler to process the directive */ (this->*(kwp->func))(); } else { /* * we're in a #if branch not taken - simply clear * the buffer */ clear_linebuf(); } /* we don't need to look any further */ break; } } /* * if we didn't find the keyword, log an error and otherwise * ignore the entire line */ if (kwp->kw == 0) log_error(TCERR_INV_PP_DIR, (int)kwlen, kwtxt); /* * Preprocessor lines must always be entirely self-contained. * Therefore, it's not valid for a string to start on a * preprocessor line and continue onto subsequent lines. If * we're marked as being inside a string, there must have been * an error on the preprocessor line. Simply clear the * in-string flag; we don't need to issue an error at this * point, since the preprocessor line handler should have * already caught the problem and reported an error. */ in_quote_ = '\0'; } else { /* * There's no preprocessor directive. * * If we're in a false #if branch, return an empty line. We * return an empty line rather than skipping to the next line so * that the caller sees the same number of lines as are in the * original source. */ if (in_false_if()) { /* * it's a #if not taken - we don't want to compile the line * at all, so just clear it out */ clear_linebuf(); expbuf_.clear_text(); } else { /* * If we ended the line in a string, splice additional lines * onto the end of this line until we find the end of the * string, then unsplice the part after the end of the * string. */ if (in_quote_ != '\0') { /* splice additional lines to finish the quote */ splice_string(); } /* * Expand macros in the line, splicing additional source * lines if necessary to fill out any incomplete actual * parameter lists. */ start_new_line(&linebuf_, 0); expand_macros_curline(TRUE, FALSE, FALSE); } /* store the line in the appropriate place */ if (pp_only_mode_) { /* * we're only preprocessing - store the macro-expanded line * back in the line buffer so that the caller can read out * the final preprocessed text */ linebuf_.copy(expbuf_.get_text(), expbuf_.get_text_len()); } else { /* * We're compiling - simply read subsequent tokens out of * the expansion buffer. */ start_new_line(&expbuf_, 0); } } /* return success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Read the next line from the input file. Returns a pointer to the * start of the newly-read data on success, or null if we reach the end * of the input. * * If 'append' is true, we'll add the line on to the end of the existing * buffer; otherwise, we'll overwrite what's in the buffer. * * The only preprocessing performed in this routine is line-splicing. * Any line that ends with a backslash character will be spliced with * the following line, with the backslash and newline removed. * * The new line will be stored in our internal buffer, and will be * null-terminated with the trailing newline removed. * * If we reach the end of the current file, and there's an enclosing * file, we'll resume reading from the enclosing file. Hence, when this * routine returns non-zero, it indicates that we've reached the end of * the entire source, not just of the current file. */ int CTcTokenizer::read_line(int append) { /* if there's no input stream, indicate end-of-file */ if (str_ == 0) return -1; /* if we're not appending, clear out the line buffer */ if (!append) { /* start with an empty line */ clear_linebuf(); /* note the current input position */ last_desc_ = str_->get_desc(); last_linenum_ = str_->get_next_linenum(); } /* note where the new data starts */ size_t len = linebuf_.get_text_len(); size_t start_len = len; /* * if there's anything in the unsplice buffer, use it as the new * line */ if (unsplicebuf_.get_text_len() != 0) { /* * Copy the unsplice buffer as the current line. Note that we * don't have to worry about any of the complicated cases, such * as whether or not it ends with a newline or a backslash, * because the unspliced line was already processed as an input * line when we read it in the first place. */ linebuf_.append(unsplicebuf_.get_text(), unsplicebuf_.get_text_len()); /* clear the unsplice buffer, since it's been consumed now */ unsplicebuf_.clear_text(); /* * make the current line the appended line - if we're * unsplicing, it means that we appended, so the current line is * now the line from which the last appended text came */ last_desc_ = appended_desc_; last_linenum_ = appended_linenum_; /* return the offset of the new text */ return start_len; } /* if we're appending, note where the appendage is coming from */ if (append) { /* remember the last source line appended */ appended_desc_ = str_->get_desc(); appended_linenum_ = str_->get_next_linenum(); } /* keep going until we finish reading the input line */ for ( ;; ) { /* read a line of text from the input file */ size_t curlen = str_->get_src()->read_line( linebuf_.get_buf() + len, linebuf_.get_buf_size() - len); /* check for end of file */ if (curlen == 0) { /* * We've reached the end of the current input stream. If * we've already read anything into the current line, it * means that the file ended in mid-line, without a final * newline character; ignore this and proceed with the line * as it now stands in this case. */ if (len > start_len) break; /* * We've finished with this stream. If there's a parent * stream, return to it; otherwise, we're at the end of the * source. */ /* * if we didn't close all of the #if/#ifdef levels opened * within this file, flag one or more errors */ while (if_sp_ > str_->get_init_if_level()) { /* get the filename from the #if stack */ const char *fname = if_stack_[if_sp_ - 1].desc->get_fname(); /* if we're in test reporting mode, use the root name only */ if (test_report_mode_) fname = os_get_root_name((char *)fname); /* log the error */ log_error(TCERR_IF_WITHOUT_ENDIF, if_stack_[if_sp_ - 1].linenum, (int)strlen(fname), fname); /* discard the #if level */ pop_if(); } /* remember the old stream */ CTcTokStream *old_str = str_; /* return to the parent stream, if there is one */ str_ = str_->get_parent(); /* delete the old stream now that we're done with it */ delete old_str; /* note the new file the line will be coming from */ if (!append && str_ != 0) { last_desc_ = str_->get_desc(); last_linenum_ = str_->get_next_linenum(); } /* if there's no stream, return end of file */ if (str_ == 0) return -1; /* * restore the #pragma newline_spacing mode that was in effect * when we interrupted the parent stream */ string_newline_spacing_ = str_->get_newline_spacing(); /* if there's a parser, notify it of the new pragma C mode */ #if 0 // #pragma C is not currently used if (G_prs != 0) G_prs->set_pragma_c(str_->is_pragma_c()); #endif /* go back to read the next line from the parent */ continue; } /* set the new length of the buffer contents */ len += curlen - 1; linebuf_.set_text_len(len); /* * Check the result to see if it ends in a newline. If not, it * means either that we don't have room in the buffer for the * full source line, or we've reached the last line in the file, * and it doesn't end with a newline. * * Note that the file reader will always supply us with '\n' * newlines, regardless of the local operating system * conventions. * * Also, check to see if the line ends with '\\'. If so, remove * the '\\' character and read the next line, since this * indicates that the logical line continues onto the next * newline-deliminted line. */ if (len != 0 && linebuf_.get_text()[len - 1] != '\n') { /* * There's no newline, hence the file reader wasn't able to * fit the entire line into our buffer, or else we've read * the last line in the file and there's no newline at the * end. If we haven't reached the end of the file, expand * our line buffer to make room to read more from this same * line. */ if (!str_->get_src()->at_eof()) linebuf_.expand(); } else if (len > 1 && linebuf_.get_text()[len - 2] == '\\') { /* * There's a backslash at the end of the line, so they want * to continue this logical line. Remove the backslash, and * read the next line onto the end of the current line. * * Note that we must remove two characters from the end of * the line (and tested for buf_[len-2] above) because we * have both a backslash and a newline at the end of the * line. */ len -= 2; linebuf_.set_text_len(len); /* count reading the physical line */ str_->count_line(); } else { /* remove the newline from the buffer */ if (len != 0) { --len; linebuf_.set_text_len(len); } /* count reading the line */ str_->count_line(); /* done */ break; } } /* * remove comments from the newly-read material - this replaces each * comment by a single whitespace character */ process_comments(start_len); /* * we've successfully read a line -- return the offset of the start of * the newly-read text */ return start_len; } /* * Un-splice a line at the given point. This breaks the current source * line in two, keeping the part before the given point as the current * line, but making the part from the given point to the end of the line * a new source line. We'll put the new source line into a special * holding buffer, and then fetch this part as a new line the next time * we read a line in read_line(). */ void CTcTokenizer::unsplice_line(const char *new_line_start) { /* make sure the starting point is within the current line */ if (!(new_line_start >= linebuf_.get_text() && new_line_start <= linebuf_.get_text() + linebuf_.get_text_len())) { /* note the error - this is an internal problem */ throw_internal_error(TCERR_UNSPLICE_NOT_CUR); return; } /* calculate the length of the part we're keeping */ size_t keep_len = new_line_start - linebuf_.get_text(); /* * prepend the remainder of the current line into the unsplice buffer * (we prepend it because the unsplice line is text that comes after * the current line - so anything in the current line comes before * anything already in the unsplice buffer) */ unsplicebuf_.prepend(new_line_start, linebuf_.get_text_len() - keep_len); /* cut off the current line at the given point */ linebuf_.set_text_len(keep_len); } /* ------------------------------------------------------------------------ */ /* * Store text in the source array */ const char *CTcTokenizer::store_source(const char *txt, size_t len) { /* reserve space for the text */ reserve_source(len); /* store it */ const char *p = store_source_partial(txt, len); /* add a null terminator */ static const char nt[1] = { '\0' }; store_source_partial(nt, 1); /* return the pointer to the stored space */ return p; } /* * Store partial source; use this AFTER reserving the necessary space. If * you want null-termination, be sure to reserve the extra byte for that * and include it in the string. This can be used to build a string piece * by piece; we simply add the text without null-terminating it. */ const char *CTcTokenizer::store_source_partial(const char *txt, size_t len) { /* remember where the string starts */ const char *p = src_ptr_; /* store the text */ memcpy(src_ptr_, txt, len); /* advance the source block write position and length */ src_ptr_ += len; src_rem_ -= len; /* return the storage pointer */ return p; } /* * Reserve space for text in the source array. This always reserves the * requested amount of space, plus an extra byte for null termination. */ void CTcTokenizer::reserve_source(size_t len) { /* * if we don't have enough space for this line in the current source * block, start a new block */ if (len + 1 > src_rem_) { CTcTokSrcBlock *blk; /* * if the line is too long for a source block, throw a fatal * error */ if (len + 1 > TCTOK_SRC_BLOCK_SIZE) throw_fatal_error(TCERR_SRCLINE_TOO_LONG, (long)TCTOK_SRC_BLOCK_SIZE); /* allocate a new block */ blk = new CTcTokSrcBlock(); /* link it into our list */ src_cur_->set_next(blk); /* it's now the current block */ src_cur_ = blk; /* start writing at the start of this block */ src_rem_ = TCTOK_SRC_BLOCK_SIZE; src_ptr_ = blk->get_buf(); } } /* * Commit space previously reserved and now used in the source block * list */ void CTcTokenizer::commit_source(size_t len) { /* advance the write position past the committed text */ src_ptr_ += len; src_rem_ -= len; } /* ------------------------------------------------------------------------ */ /* * Expand macros in the current line from the current source pointer, * filling in expbuf_ with the expanded result. */ int CTcTokenizer::expand_macros_curline(int read_more, int allow_defined, int append_to_expbuf) { int err; /* expand macros in the current line */ err = expand_macros(&linebuf_, &p_, &expbuf_, read_more, allow_defined, append_to_expbuf); /* if that failed, return an error */ if (err != 0) return err; /* * if we're in preprocessor mode, clean up the text for human * consumption by removing our various expansion flags */ if (pp_only_mode_) remove_expansion_flags(&expbuf_); /* return the result */ return err; } /* ------------------------------------------------------------------------ */ /* * Remove the special internal macro expansion flags from an expanded macro * buffer. */ void CTcTokenizer::remove_expansion_flags(CTcTokString *buf) { utf8_ptr p; char *src; char *dst; /* * Scan the expansion buffer and remove all of the no-more-expansion * flag bytes - we're done expanding the macro now, so we don't need * this information any longer. When we're writing out the * preprocessed source for human viewing, we don't want to leave these * internal markers in the expanded source. */ for (src = dst = buf->get_buf(), p.set(src) ; p.getch() != '\0' ; ) { /* if this isn't a macro flag, copy it */ if (p.getch() == TOK_MACRO_EXP_END) { /* skip the flag byte and the following embedded pointer */ src += 1 + sizeof(CTcHashEntryPp *); p.set(src); } else if (p.getch() == TOK_FULLY_EXPANDED_FLAG) { /* skip the flag byte */ ++src; p.set(src); } else { /* skip this character */ p.inc(); /* copy the bytes of this character as-is */ while (src < p.getptr()) *dst++ = *src++; } } /* set the new buffer length */ buf->set_text_len(dst - buf->get_buf()); } /* ------------------------------------------------------------------------ */ /* * Expand macros in the current line, reading additional source lines if * necessary. * * 'src' is a pointer to the start of the text to expand; it must point * into the 'srcbuf' buffer. If 'src' is null, we'll simply start at * the beginning of the source buffer. */ int CTcTokenizer::expand_macros(CTcTokString *srcbuf, utf8_ptr *src, CTcTokString *expbuf, int read_more, int allow_defined, int append) { tc_toktyp_t typ; CTcToken tok; CTcTokString *subexp; size_t startofs; utf8_ptr local_src; CTcTokStringRef local_srcbuf; CTcMacroRsc *res; int err; /* presume success */ err = 0; /* get a macro expansion resource object */ res = alloc_macro_rsc(); if (res == 0) return 1; /* get our subexpression buffer from the resource object */ subexp = &res->line_exp_; /* if there's no source buffer or source pointer, provide one */ if (srcbuf == 0) { /* * there's no source buffer - provide our own non-allocated * buffer tied to the caller's buffer */ local_srcbuf.set_buffer(src->getptr(), strlen(src->getptr())); srcbuf = &local_srcbuf; } else if (src == 0) { /* * there's no source pointer - start at the beginning of the * source buffer */ local_src.set((char *)srcbuf->get_text()); src = &local_src; } /* clear the expansion buffer, unless we're appending to the buffer */ if (!append) expbuf->clear_text(); /* * Make sure we have room for a copy of the source line. This is an * optimization for the simple case where we'll just copy the source * line unchanged, so that we don't have to repeatedly expand the * buffer; we will, however, expand the buffer dynamically later, if * this pre-allocation should prove to be insufficient. */ expbuf->ensure_space(expbuf->get_text_len() + srcbuf->get_text_len()); /* note the starting offset, if we have an underlying string buffer */ startofs = src->getptr() - srcbuf->get_text(); /* read the first token */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); /* scan through the tokens on the line, looking for macros to expand */ while (typ != TOKT_EOF) { /* * if it's a symbol, and it hasn't already been marked as fully * expanded, look it up in the #define table */ if (typ == TOKT_SYM && !tok.get_fully_expanded()) { CTcHashEntryPp *entry; /* * Look up the symbol in the #define symbol table. If we * find it, expand the macro. Otherwise, if the "defined" * operator is active, check for that. * * Do not expand the macro if we find that it has already * been expanded on a prior scan through the current text. */ entry = find_define(tok.get_text(), tok.get_text_len()); if ((entry != 0 && !scan_for_prior_expansion(*src, srcbuf->get_text_end(), entry)) || (allow_defined && tok.get_text_len() == 7 && memcmp(tok.get_text(), "defined", 7) == 0)) { size_t macro_ofs; size_t rem_len; int expanded; /* get the offset of the macro token in the source buffer */ macro_ofs = tok.get_text() - srcbuf->get_text(); /* expand it into our sub-expansion buffer */ if (entry != 0) { /* expand the macro */ err = expand_macro(res, subexp, srcbuf, src, macro_ofs, entry, read_more, allow_defined, &expanded); } else { /* parse and expand the defined() operator */ err = expand_defined(subexp, srcbuf, src); /* "defined" always expands if there's not an error */ expanded = TRUE; } /* if an error occurred, return failure */ if (err) goto done; /* * if we expanded something, append everything we * skipped preceding the macro, then rescan; otherwise, * just keep going without a rescan */ if (expanded) { /* copy the preceding text to the output */ expbuf->append(srcbuf->get_text() + startofs, macro_ofs - startofs); } else { /* * we didn't expand - get the next token after the * macro */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); /* continue processing from this token */ continue; } /* * We must now insert the expansion into the source * buffer at the current point, and re-scan the * expansion, *along with* the rest of the original * source line (this is how ANSI C specifies the * process). * * If we can read more, we must be reading out of the * main input line buffer, so insert the expansion text * directly into the original source stream, and * continue reading out of the source stream; this will * simplify the case where we must read more data from * the file in the course of the expansion. If we can't * read more, simply copy the remainder of the current * input line onto the expanded macro and use it as the * new input buffer. */ /* get the current offset in the source line */ startofs = src->getptr() - srcbuf->get_text(); /* figure out how much is left on the current line */ rem_len = srcbuf->get_text_len() - startofs; /* check to see if we can read more */ if (read_more) { /* * we're reading from the original line input buffer * -- insert the expansion into the source buffer at * the current point, replacing the original macro * text */ /* make sure we have room for adding the expansion text */ srcbuf->ensure_space(macro_ofs + rem_len + subexp->get_text_len()); /* make sure src is still pointing to the right place */ src->set(srcbuf->get_buf() + macro_ofs); /* move the remainder of the current line to make room */ memmove(srcbuf->get_buf() + macro_ofs + subexp->get_text_len(), srcbuf->get_buf() + startofs, rem_len); /* insert the expansion text */ memcpy(srcbuf->get_buf() + macro_ofs, subexp->get_buf(), subexp->get_text_len()); /* set the new source length */ srcbuf->set_text_len(macro_ofs + rem_len + subexp->get_text_len()); /* the new starting offset is the current position */ startofs = macro_ofs; /* get the next token */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); /* continue processing from this token */ continue; } else { /* * we're reading from a read-only buffer -- add the * remainder of the source to the expansion buffer, * and recursively parse the remainder */ subexp->append(srcbuf->get_text() + startofs, rem_len); /* * evaluate the remainder recursively and append it * to the expansion already in progress */ err = expand_macros(subexp, 0, expbuf, FALSE, allow_defined, TRUE); /* we're done */ goto done; } } } /* get the next token */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); } /* add the remainder of the source to the output */ expbuf->append(srcbuf->get_text() + startofs, tok.get_text() - startofs - srcbuf->get_text()); done: /* release our macro resource object */ release_macro_rsc(res); /* return the result */ return err; } /* * Allocate a macro resource object. If we're out of resource objects * in the pool, we'll add another object to the pool. */ CTcMacroRsc *CTcTokenizer::alloc_macro_rsc() { CTcMacroRsc *rsc; /* * if there's anything in the available list, take the first item * off the list and return it */ if (macro_res_avail_ != 0) { /* remember the item to return */ rsc = macro_res_avail_; /* remove it from the list */ macro_res_avail_ = macro_res_avail_->next_avail_; /* return it */ return rsc; } /* there's nothing on the available list - allocate a new item */ rsc = new CTcMacroRsc(); /* if that failed, return failure */ if (rsc == 0) { log_error(TCERR_OUT_OF_MEM_MAC_EXP); return 0; } /* add it onto the master list */ rsc->next_ = macro_res_head_; macro_res_head_ = rsc; /* return it */ return rsc; } /* * Release a macro resource, returning it to the pool */ void CTcTokenizer::release_macro_rsc(CTcMacroRsc *rsc) { /* put it back at the head of the available list */ rsc->next_avail_ = macro_res_avail_; macro_res_avail_ = rsc; } /* * Scan a buffer for a prior-expansion flag for a given macro. We'll * look through the buffer for a TOK_MACRO_EXP_END byte that mentions * the given symbol table entry; we'll return true if found, false if * not. True means that the symbol has already been expanded on a prior * scan of the text, so it should not be re-expanded now. */ int CTcTokenizer::scan_for_prior_expansion(utf8_ptr src, const char *src_end, const CTcHashEntryPp *entry) { /* scan the buffer for the expansion flag byte */ while (src.getptr() < src_end) { /* if this is the flag, check what follows */ if (src.getch() == TOK_MACRO_EXP_END) { CTcHashEntryPp *flag_entry; /* read the entry from the buffer */ memcpy(&flag_entry, src.getptr() + 1, sizeof(flag_entry)); /* if it matches, indicate that we found it */ if (entry == flag_entry) return TRUE; /* it's not a match - keep scanning after this flag sequence */ src.set(src.getptr() + 1 + sizeof(flag_entry)); } else { /* it's not the flag - skip this character */ src.inc(); } } /* we didn't find it */ return FALSE; } /* * Go through a macro expansion and translate from end-of-expansion * markers to individual token full-expansion markers. This is used * after we leave a recursion level to convert expanded text into text * suitable for use in further expansion at an enclosing recursion * level. */ void CTcTokenizer::mark_full_exp_tokens(CTcTokString *dstbuf, const CTcTokString *srcbuf, int append) const { utf8_ptr p; CTcToken tok; const char *start; tok_embed_ctx ec; /* clear the output buffer if we're not appending to existing text */ if (!append) dstbuf->clear_text(); /* remember the starting point */ start = srcbuf->get_text(); /* scan the source buffer */ p.set((char *)start); for (;;) { CTcHashEntryPp *cur_entry; tc_toktyp_t typ; char ch; /* get the next token; stop at the end of the line */ typ = next_on_line(srcbuf, &p, &tok, &ec, TRUE); if (typ == TOKT_EOF) break; /* * if this macro token is being expanded, and it's not already * marked for no more expansion, mark it */ if (typ == TOKT_SYM && !tok.get_fully_expanded() && (cur_entry = find_define(tok.get_text(), tok.get_text_len())) != 0 && scan_for_prior_expansion(p, srcbuf->get_text_end(), cur_entry)) { /* * This token has been fully expanded in the substitution * buffer but hasn't yet been marked as such - we must * insert the fully-expanded marker. First, add up to the * current point to the output buffer. */ if (tok.get_text() > start) dstbuf->append(start, tok.get_text() - start); /* add the fully-expanded marker */ ch = TOK_FULLY_EXPANDED_FLAG; dstbuf->append(&ch, 1); /* the new starting point is the start of the symbol token */ start = tok.get_text(); } } /* copy any remaining text to the output */ if (tok.get_text() > start) dstbuf->append(start, tok.get_text() - start); /* * Remove any macro expansion end markers from the output buffer. * We don't want to leave these around, because they don't apply to * the enclosing buffer into which we'll substitute this result. * Note that we've already ensured that these markers will be * respected for the substitution text by inserting "fully expanded" * markers in front of each token to which any of the markers we're * removing should apply. */ remove_end_markers(dstbuf); } /* * Remove end markers from a buffer */ void CTcTokenizer::remove_end_markers(CTcTokString *buf) { char *src; char *dst; utf8_ptr p; /* scan the buffer */ for (src = dst = buf->get_buf(), p.set(src) ; p.getptr() < buf->get_text_end() ; ) { /* check for our flag */ if (p.getch() == TOK_MACRO_EXP_END) { /* skip the flag byte and the following embedded pointer */ src += 1 + sizeof(CTcHashEntryPp *); p.set(src); } else { /* skip this character */ p.inc(); /* copy the bytes of this character as-is */ while (src < p.getptr()) *dst++ = *src++; } } /* set the new buffer size */ buf->set_text_len(dst - buf->get_buf()); } /* * Expand the macro at the current token in the current line. * * 'src' is a pointer to the current position in 'srcbuf'. We'll update * 'src' to point to the next token after macro or its actual parameters * list, if it has one. */ int CTcTokenizer::expand_macro(CTcMacroRsc *rsc, CTcTokString *expbuf, const CTcTokString *srcbuf, utf8_ptr *src, size_t macro_srcbuf_ofs, CTcHashEntryPp *entry, int read_more, int allow_defined, int *expanded) { CTcTokString *subexp; size_t argofs[TOK_MAX_MACRO_ARGS]; size_t arglen[TOK_MAX_MACRO_ARGS]; size_t startofs; const char *start; const char *end; int err; char flagbuf[1 + sizeof(entry)]; /* presume we won't do any expansion */ *expanded = FALSE; /* get our resources */ subexp = &rsc->macro_exp_; /* remember our parsing starting offset */ startofs = src->getptr() - srcbuf->get_text(); /* clear the expansion output buffer */ expbuf->clear_text(); /* if the macro has arguments, scan the actuals */ if (entry->has_args()) { int found_actuals; /* read the macro arguments */ if (parse_macro_actuals(srcbuf, src, entry, argofs, arglen, read_more, &found_actuals)) { err = 1; goto done; } /* * If we found no actuals, then this wasn't really an invocation * of the macro after all - a function-like macro invoked with * no arguments is simply not replaced. Store the original text * in the output buffer and return success. */ if (!found_actuals) { /* copy the original text */ expbuf->copy(srcbuf->get_text() + macro_srcbuf_ofs, startofs - macro_srcbuf_ofs); /* * restore the source read pointer to where it was when we * started */ src->set((char *)srcbuf->get_text() + startofs); /* return success */ err = 0; goto done; } } /* * if there are arguments, replace the macro and substitute actuals * for the formals; otherwise, just copy the replacement text * directly */ if (entry->get_argc() != 0) { /* substitute the actuals */ if (substitute_macro_actuals(rsc, subexp, entry, srcbuf, argofs, arglen, allow_defined)) { err = 1; goto done; } /* set up to parse from the expansion buffer */ start = subexp->get_text(); end = start + subexp->get_text_len(); } else { /* * use our local source buffer that simply references the * original expansion text, rather than making a copy of the * expansion text */ start = entry->get_expansion(); end = start + entry->get_expan_len(); } /* copy the expansion into the output buffer */ expbuf->copy(start, end - start); /* * After the end of the expansion sequence, insert the * fully-expanded flag plus a pointer to the symbol table entry that * we just expanded. This will allow us to detect during the * re-scan of the expansion text that this symbol has already been * expanded, in which case we must suppress further expansion of the * symbol. This allows us to follow the ANSI C rules for recursive * macro usage. */ flagbuf[0] = TOK_MACRO_EXP_END; memcpy(&flagbuf[1], &entry, sizeof(entry)); expbuf->append(flagbuf, sizeof(flagbuf)); /* indicate that we expanded the macro */ *expanded = TRUE; /* success */ err = 0; done: /* return the result */ return err; } /* * Parse a macro's actual parameter list, filling in the given hash * table with the arguments. Returns zero on success, non-zero on * error. 'entry' is the macro's defining symbol table entry. */ int CTcTokenizer::parse_macro_actuals(const CTcTokString *srcbuf, utf8_ptr *src, const CTcHashEntryPp *entry, size_t argofs[TOK_MAX_MACRO_ARGS], size_t arglen[TOK_MAX_MACRO_ARGS], int read_more, int *found_actuals) { tc_toktyp_t typ; CTcToken tok; int argc; int spliced; int i; /* presume we're not going to do any line splicing */ spliced = FALSE; /* no arguments parsed yet */ argc = 0; /* get the next token after the macro symbol */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); /* splice another line if necessary */ if (typ == TOKT_EOF && read_more) { /* splice a line */ typ = actual_splice_next_line(srcbuf, src, &tok); /* note the splice */ spliced = TRUE; } /* if we didn't find an open paren, there's no actual list after all */ if (typ != TOKT_LPAR) { /* tell the caller we didn't find any actuals */ *found_actuals = FALSE; /* if we spliced a line, unsplice it at the current token */ if (spliced) unsplice_line(tok.get_text()); /* return success */ return 0; } /* remember the offset of the start of the first argument */ argofs[argc] = tok.get_text() + tok.get_text_len() - srcbuf->get_text(); /* skip the open paren */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); /* read the arguments */ while (typ != TOKT_RPAR) { utf8_ptr p; int paren_depth, bracket_depth, brace_depth; int sp_cnt; /* if we have too many arguments, it's an error */ if ((argc >= entry->get_argc() && !entry->has_varargs()) || argc >= TOK_MAX_MACRO_ARGS) { /* log the error */ log_error(TCERR_PP_MANY_MACRO_ARGS, (int)entry->getlen(), entry->getstr()); /* scan ahead to to close paren or end of line */ while (typ != TOKT_RPAR && typ != TOKT_EOF) typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); /* done scanning arguments */ break; } /* * Skip tokens until we find the end of the argument. An argument * ends at: * * - a comma outside of nested parens, square brackets, or curly * braces * * - a close paren that doesn't match an open paren found earlier * in the same argument */ paren_depth = bracket_depth = brace_depth = 0; for (;;) { /* * If it's a comma, and we're not in any sort of nested * brackets (parens, square brackets, or curly braces), the * comma ends the argument. A comma within any type of * brackets is part of the argument text. */ if (typ == TOKT_COMMA && paren_depth == 0 && brace_depth == 0 && bracket_depth == 0) break; /* * If it's a close paren, and it doesn't match an earlier open * paren in the same argument, it's the end of the argument. */ if (typ == TOKT_RPAR && paren_depth == 0) break; /* * if it's an open or close paren, brace, or bracket, adjust * the depth accordingly */ switch(typ) { case TOKT_LPAR: ++paren_depth; break; case TOKT_RPAR: --paren_depth; break; case TOKT_LBRACE: ++brace_depth; break; case TOKT_RBRACE: --brace_depth; break; case TOKT_LBRACK: ++bracket_depth; break; case TOKT_RBRACK: --bracket_depth; break; default: break; } /* get the next token */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); /* * if we're at the end of the line, and we're allowed to * read more, splice the next line onto the current line */ if (typ == TOKT_EOF && read_more) { /* splice a line */ typ = actual_splice_next_line(srcbuf, src, &tok); /* note that we've done some line splicing */ spliced = TRUE; } /* if we've reached the end of the file, stop */ if (typ == TOKT_EOF) break; } /* if we've reached the end of the file, stop */ if (typ == TOKT_EOF) break; /* remove any trailing whitespace from the actual's text */ sp_cnt = 0; p.set((char *)tok.get_text()); while (p.getptr() > srcbuf->get_text() + argofs[argc]) { wchar_t ch; /* move to the prior character */ p.dec(); /* if it's not a space, stop looking */ ch = p.getch(); if (!is_space(ch)) { /* * advance past this character so that we keep it in the * expansion */ p.inc(); /* * if this last character was a backslash, and we removed * at least one space following it, keep the one space * that immediately follows the backslash, since that * space is part of the backslash's two-character escape * sequence */ if (ch == '\\' && sp_cnt != 0) p.inc(); /* stop scanning */ break; } /* that's one more trailing space we've removed - count it */ ++sp_cnt; } /* note the argument length */ arglen[argc] = (p.getptr() - srcbuf->get_text()) - argofs[argc]; /* count the argument */ ++argc; /* check for another argument */ if (typ == TOKT_COMMA) { /* remember the offset of the start of this argument */ argofs[argc] = tok.get_text() + tok.get_text_len() - srcbuf->get_text(); /* skip the comma and go back for another argument */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); } else if (typ == TOKT_RPAR) { /* * No need to look any further. Note that we don't want to * get another token, since we're done parsing the input * now, and we want to leave the token stream positioned for * the caller just after the extent of the macro, which, in * the case of this function-like macro, ends with the * closing paren. */ break; } } /* if we didn't find the right paren, flag the error */ if (typ != TOKT_RPAR) { log_error(read_more ? TCERR_PP_MACRO_ARG_RPAR : TCERR_PP_MACRO_ARG_RPAR_1LINE, (int)entry->getlen(), entry->getstr()); return 1; } /* remove leading and trailing whitespace from each argument */ for (i = 0 ; i < argc ; ++i) { const char *start; const char *end; utf8_ptr p; size_t del_len; int sp_cnt; /* figure the limits of the argument text */ start = srcbuf->get_text() + argofs[i]; end = start + arglen[i]; /* remove leading whitespace */ for (p.set((char *)start) ; p.getptr() < end && is_space(p.getch()) ; p.inc()) ; /* set the new offset and length */ del_len = p.getptr() - start; argofs[i] += del_len; arglen[i] -= del_len; start += del_len; /* remove trailing whitespace */ p.set((char *)end); sp_cnt = 0; while (p.getptr() > start) { wchar_t ch; /* go to the prior character */ p.dec(); /* if it's not whitespace, keep it */ ch = p.getch(); if (!is_space(ch)) { /* put the character back */ p.inc(); /* * if this is a backslash, and a space follows, keep the * immediately following space, since it's part of the * backslash sequence */ if (ch == '\\' && sp_cnt != 0) p.inc(); /* we're done scanning */ break; } /* count another removed trailing space */ ++sp_cnt; } /* adjust the length */ arglen[i] -= (end - p.getptr()); } /* * if we did any line splicing, cut off the rest of the line and * push it back into the logical input stream as a new line - this * will allow better error message positioning if errors occur in * the remainder of the line, since this means we'll only * artificially join onto one line the part of the new line that * contained the macro parameters */ if (spliced) unsplice_line(tok.get_text() + tok.get_text_len()); /* make sure we found enough arguments */ if (argc < entry->get_min_argc()) { /* fill in the remaining arguments with empty strings */ for ( ; argc < entry->get_argc() ; ++argc) { argofs[argc] = 0; arglen[argc] = 0; } /* note the error, but proceed with empty arguments */ log_warning(TCERR_PP_FEW_MACRO_ARGS, (int)entry->getlen(), entry->getstr()); } /* * if we have varargs, always supply an empty marker for the last * argument */ if (entry->has_varargs() && argc < TOK_MAX_MACRO_ARGS) { argofs[argc] = 0; arglen[argc] = 0; } /* success - we found an actual parameter list */ *found_actuals = TRUE; return 0; } /* * Splice a line for macro actual parameters. Sets the source pointer * to the start of the new line. Reads the first token on the spliced * line and returns it. * * We will splice new lines until we find a non-empty line or reach the * end of the input. If this returns EOF, it indicates that we've * reached the end of the entire input. */ tc_toktyp_t CTcTokenizer::actual_splice_next_line( const CTcTokString *srcbuf, utf8_ptr *src, CTcToken *tok) { /* add a space onto the end of the current line */ linebuf_.append(" ", 1); /* keep going until we find a non-empty line */ for (;;) { int new_line_ofs; tc_toktyp_t typ; /* splice the next line onto the current line */ new_line_ofs = read_line(TRUE); /* * make sure we read additional lines as needed to complete any * strings left open at the end of the line */ if (in_quote_ != '\0') splice_string(); /* if there was no more, return end of file */ if (new_line_ofs == -1) return TOKT_EOF; /* set the source to the start of the additional line */ src->set((char *)linebuf_.get_text() + new_line_ofs); /* get the next token */ typ = next_on_line(srcbuf, src, tok, ¯o_in_embedding_, TRUE); /* if we didn't get EOF, it means we found a non-empty line */ if (typ != TOKT_EOF) return typ; } } /* * Substitute the actual parameters in a macro's expansion */ int CTcTokenizer::substitute_macro_actuals( CTcMacroRsc *rsc, CTcTokString *subexp, CTcHashEntryPp *entry, const CTcTokString *srcbuf, const size_t *argofs, const size_t *arglen, int allow_defined) { const char *start; utf8_ptr expsrc; CTcToken prvtok; CTcToken prvprvtok; CTcToken tok; tc_toktyp_t typ; CTcTokString *actual_exp_buf; const size_t expand_max = 10; static struct expand_info_t { /* type of expansion (#foreach, #ifempty, #ifnempty) */ tc_toktyp_t typ; /* * flag: this is an iterator type (if this is true, the varargs * formal should be expanded to the current argument given by our * 'arg' member; if this is false, the varargs formal should be * expanded as the full varargs list) */ int is_iterator; /* the marker character that delimits the foreach arguments */ wchar_t delim; /* location of start of expansion region for foreach */ utf8_ptr start; /* current argument index */ int arg; /* the current expansion part (0 = first part, etc) */ int part; } expand_stack[expand_max], *expand_sp; /* get the actual expansion buffer from the resource object */ actual_exp_buf = &rsc->actual_exp_buf_; /* * Scan the replacement text for formals, and replace each formal * with the actual. Set up a pointer at the start of the expansion * text. */ start = entry->get_expansion(); expsrc.set((char *)start); /* we don't yet have a previous token */ prvtok.settyp(TOKT_EOF); prvprvtok.settyp(TOKT_EOF); /* clear the expansion buffer */ subexp->clear_text(); /* we have no #foreach/#ifempty/#ifnempty stack yet */ expand_sp = expand_stack; /* scan the tokens in the expansion text */ for (typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE) ; typ != TOKT_EOF ; ) { /* * check to see if we've reached the end of a * #foreach/#ifempty/#ifnempty */ if (expand_sp != expand_stack) { /* check to see if we're at the delimiter */ if (utf8_ptr::s_getch(tok.get_text()) == (expand_sp-1)->delim) { /* copy the prior expansion so far */ if (tok.get_text() > start) subexp->append(start, tok.get_text() - start); /* go back to the start of the token */ expsrc.set((char *)tok.get_text()); /* see what kind of token we're expanding */ switch((expand_sp-1)->typ) { case TOKT_MACRO_FOREACH: /* it's a #foreach - process the appropriate part */ switch ((expand_sp-1)->part) { case 0: /* * We've been doing the first part, which is the * main expansion per actual. This delimiter thus * introduces the 'between' portion, which we copy * between each iteration, but not after the last * iteration. So, if we've just done the last * actual, skip this part entirely; otherwise, * keep going, using this part. */ if (argofs[(expand_sp-1)->arg + 1] == 0) { /* skip this one remaining part */ skip_delimited_group(&expsrc, 1); /* we're finished with the iteration */ goto end_foreach; } else { /* * we have more arguments, so we want to * expand this part - skip the deliter and * keep going */ expsrc.inc(); /* we're now in the next part of the iterator */ (expand_sp-1)->part++; } break; case 1: /* * We've reached the end of the entire #foreach * string, so we're done with this iteration. * Skip the delimiter. */ expsrc.inc(); end_foreach: /* * if we have more arguments, start over with the * next iteration; otherwise, pop the #foreach * level */ if (argofs[(expand_sp-1)->arg + 1] == 0) { /* no more arguments - pop the #foreach level */ --expand_sp; } else { /* we have more arguments - move to the next */ (expand_sp-1)->arg++; /* go back to the start of the expansion */ expsrc = (expand_sp-1)->start; /* we have no previous token for pasting ops */ prvtok.settyp(TOKT_EOF); prvprvtok.settyp(TOKT_EOF); /* we're back in the first part of the iterator */ (expand_sp-1)->part = 0; } break; } break; case TOKT_MACRO_IFEMPTY: case TOKT_MACRO_IFNEMPTY: /* * #ifempty or #ifnempty - we've reached the end of * the conditional text, so simply pop a level and * keep going after the delimiter */ /* skip the delimiter */ expsrc.inc(); /* pop a level */ --expand_sp; /* done */ break; default: break; } /* the next chunk starts here */ start = expsrc.getptr(); /* get the next token */ typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE); /* we have the next token, so back and process it */ continue; } } /* if it's a #foreach marker, start a #foreach iteration */ if (typ == TOKT_MACRO_FOREACH && entry->has_varargs()) { /* copy the prior expansion so far */ if (tok.get_text() > start) subexp->append(start, tok.get_text() - start); /* push a #foreach level, if possible */ if (expand_sp - expand_stack >= expand_max) { /* * we can't create another level - log an error and ignore * this new level */ log_error(TCERR_PP_FOREACH_TOO_DEEP); } else if (argofs[entry->get_argc() - 1] == 0) { /* * we have no actuals for the variable part of the * formals, so we must iterate zero times through the * #foreach part - in other words, simply skip ahead to * the end of the #foreach */ skip_delimited_group(&expsrc, 2); } else { /* remember and skip the marker character */ expand_sp->delim = expsrc.getch(); expsrc.inc(); /* set the expansion type */ expand_sp->typ = typ; /* * remember the position where the #foreach started, since * we need to come back here for each use of the variable */ expand_sp->start = expsrc; /* we're an iterator type */ expand_sp->is_iterator = TRUE; /* * Start at the first argument in the variable part of the * argument list. The last formal corresponds to the * first variable argument. */ expand_sp->arg = entry->get_argc() - 1; /* we're in the main expansion part of the expression */ expand_sp->part = 0; /* push the new level */ ++expand_sp; } /* the next chunk starts here */ start = expsrc.getptr(); /* get the next token */ typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE); /* we have the next token, so back and process it */ continue; } /* if it's a varargs #ifempty or #ifnempty flag, expand it */ if ((typ == TOKT_MACRO_IFEMPTY || typ == TOKT_MACRO_IFNEMPTY) && entry->has_varargs()) { int is_empty; int expand; /* copy the prior expansion so far */ if (tok.get_text() > start) subexp->append(start, tok.get_text() - start); /* determine if the varargs list is empty or not */ is_empty = (argofs[entry->get_argc() - 1] == 0); /* * decide whether or not expand it, according to the empty * state and the flag type */ expand = ((is_empty && typ == TOKT_MACRO_IFEMPTY) || (!is_empty && typ == TOKT_MACRO_IFNEMPTY)); /* * if we're going to expand it, push a level; otherwise, just * skip the entire expansion */ if (expand) { /* make sure we have room for another level */ if (expand_sp - expand_stack >= expand_max) { /* no room - log an error and ignore the new level */ log_error(TCERR_PP_FOREACH_TOO_DEEP); } else { /* remember and skip the delimiter */ expand_sp->delim = expsrc.getch(); expsrc.inc(); /* * we're not an iterator type, so inherit the * enclosing level's meaning of the varargs formal */ if (expand_sp - expand_stack == 0) { /* outermost level - use the whole varargs list */ expand_sp->is_iterator = FALSE; } else { /* use the enclosing level's meaning */ expand_sp->is_iterator = (expand_sp-1)->is_iterator; expand_sp->arg = (expand_sp-1)->arg; } /* set the expansion type */ expand_sp->typ = typ; /* push the new level */ ++expand_sp; } } else { /* not expanding - just skip the entire expansion */ skip_delimited_group(&expsrc, 1); } /* the next chunk starts here */ start = expsrc.getptr(); /* get the next token */ typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE); /* we have the next token, so back and process it */ continue; } /* if it's a varargs #argcount indicator, expand it */ if (typ == TOKT_MACRO_ARGCOUNT && entry->has_varargs()) { char buf[20]; int i; /* copy the prior expansion so far */ if (tok.get_text() > start) subexp->append(start, tok.get_text() - start); /* * count the number of arguments after and including the * variable argument placeholder */ for (i = entry->get_argc() - 1 ; argofs[i] != 0 ; ++i) ; /* make a string out of the variable argument count */ sprintf(buf, "%d", i - (entry->get_argc() - 1)); /* add the argument count to the output buffer */ subexp->append(buf, strlen(buf)); /* the next chunk starts after the #argcount */ start = expsrc.getptr(); /* get the next token */ typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE); /* we have the next token, so back and process it */ continue; } /* if it's a symbol, check for an actual */ if (typ == TOKT_MACRO_FORMAL) { const char *p; int argnum; size_t argnum_len; int pasting; int pasting_at_left, pasting_at_right; int stringize; char stringize_qu; tc_toktyp_t stringize_type; CTcToken paste_at_right_tok; /* assume we'll copy up to the start of this token */ p = tok.get_text(); /* * get the index of the actual in the argument vector -- * this is given by the second byte of the special macro * parameter flag token */ argnum = (int)(uchar)tok.get_text()[1] - 1; /* * If we have varargs, and this is the varargs argument, and * the current #foreach stack level indicates that we're * iterating through the varargs list, treat this as a * reference to the current argument in the iteration. */ if (expand_sp != expand_stack && argnum == entry->get_argc() - 1 && (expand_sp-1)->is_iterator) { /* * we're on a #foreach iterator, and this is the varargs * formal - use the current #foreach iteration element * instead */ argnum = (expand_sp-1)->arg; } /* * Get the length of this argument. If we have varargs, and * this is the last formal, which is the placeholder for the * variable argument list, and we're not in a #foreach * iterator, the value is the value of the entire string of * variable arguments, including the commas. */ if (expand_sp == expand_stack && entry->has_varargs() && argnum == entry->get_argc() - 1) { int i; /* * It's the full varargs list - use the length from the * first varargs argument to the last. Find the last * argument. */ for (i = argnum ; i < TOK_MAX_MACRO_ARGS && argofs[i] != 0 ; ++i) ; /* * The full list length is the distance from the offset of * the first to the end of the last. If there are no * varargs arguments at all, the length is zero. */ if (i == argnum) argnum_len = 0; else argnum_len = argofs[i-1] + arglen[i-1] - argofs[argnum]; } else { /* * it's not the full varargs list, so just use the length * of this single actual */ argnum_len = arglen[argnum]; } /* assume we won't do any token pasting or stringizing */ pasting = pasting_at_left = pasting_at_right = FALSE; stringize = FALSE; /* * if the previous token was a token-pasting operator, * remove it and any preceding whitespace from the source * material, since we want to append the actual parameter * text directly after the preceding token */ check_paste_left: if (prvtok.gettyp() == TOKT_POUNDPOUND) { wchar_t prv_ch; /* * note that we have token pasting - we're pasting * something to the left of this token (since we had a * "##" before this token */ pasting = TRUE; pasting_at_left = TRUE; /* go back to the ## token */ p = prvtok.get_text(); /* remove any preceding whitespace */ for (prv_ch = 0 ; p > start ; ) { const char *prvp; /* get the previous character */ prvp = utf8_ptr::s_dec((char *)p); prv_ch = utf8_ptr::s_getch((char *)prvp); /* if it's not a space, we're done */ if (!is_space(prv_ch)) break; /* move back over this character */ p = prvp; } /* * Weird special case: if the previous character was a * comma, and the formal we're pasting is a variable * argument formal (i.e., the last formal in a varargs * macro), and the varargs list is empty, then remove the * comma. This is a handy shorthand notation that allows * the varargs list to be added to a comma-delimited list, * such as a function call's actuals or the contents of a * list. */ if (prv_ch == ',' && entry->has_varargs() && argnum == entry->get_argc() - 1 && argofs[argnum] == 0) { /* * it's the special case - move back one more * character to delete the comma */ p = utf8_ptr::s_dec((char *)p); } } else if (prvtok.gettyp() == TOKT_POUND || prvtok.gettyp() == TOKT_POUNDAT) { /* go back to the # token */ p = prvtok.get_text(); /* note that we have stringizing */ stringize = TRUE; stringize_type = prvtok.gettyp(); stringize_qu = (prvtok.gettyp() == TOKT_POUND ? '"' : '\''); /* go back one more token */ prvtok = prvprvtok; prvprvtok.settyp(TOKT_EOF); /* * go back and check for pasting again, since we could * be pasting to a stringized token */ goto check_paste_left; } /* copy the prior expansion so far */ if (p > start) subexp->append(start, p - start); /* remember the symbol as the previous token */ prvprvtok = prvtok; prvtok = tok; /* get the next token after the formal */ typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE); /* * If it's followed by a token-pasting operator, we need to * paste the next token directly onto the end of the text we * just added to the buffer, skipping any intervening * whitespace; otherwise, we want to start adding again at * the next character after the original token. */ if (typ == TOKT_POUNDPOUND) { utf8_ptr old_expsrc; CTcToken old_tok; /* note that we have pasting to the right of this token */ pasting = TRUE; pasting_at_right = TRUE; /* remember where we started */ old_expsrc = expsrc; /* remember the current token for a moment */ old_tok = tok; /* skip to the next token after the ## */ typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE); /* remember the token we're pasting to the right */ paste_at_right_tok = tok; /* check for pasting to a stringizer */ if (stringize && typ == stringize_type) { /* * leave the ## in the stream for now - we'll fix it * up when we stringize the next token, rather than * doing so now */ expsrc = old_expsrc; tok = old_tok; } else { /* * remember that we have a token-pasting operator, * so that we can tell that we're pasting when we * look at the next token */ prvprvtok = prvtok; prvtok = old_tok; } /* start next text from here */ start = tok.get_text(); } else { /* Start at the end of the symbol token */ start = prvtok.get_text() + prvtok.get_text_len(); } /* * If we're not doing any pasting, recursively expand macros * in the actual expansion text. If we're pasting, do not * expand any macros in the expansion, since we want to do * the pasting before we do any expanding. */ if (pasting && stringize) { int add_open; int add_close; /* presume we'll include the open and close quotes */ add_close = TRUE; add_open = TRUE; /* * If we're pasting to the left, and the buffer so far * ends in the same quote we're adding to this token, * combine the strings by removing the preceding quote * and not adding the open quote on the new string */ if (subexp->get_text_len() > 0 && *(subexp->get_text_end() - 1) == stringize_qu) { /* remove the close quote from the expansion so far */ subexp->set_text_len(subexp->get_text_len() - 1); /* don't add the open quote to the new string */ add_open = FALSE; } /* * If we're pasting to the right, and we have a string * of the same type following, or we will be pasting a * stringizing pair, paste the two strings together to * form one string by removing the close quote from this * string and the open quote from the next string */ if (pasting_at_right && *tok.get_text() == stringize_qu) add_close = FALSE; /* * We're both stringizing this argument and pasting * another token - first stringize the actual. */ stringize_macro_actual(subexp, srcbuf->get_text() + argofs[argnum], argnum_len, stringize_qu, add_open, add_close); /* * if we decided to remove the closing quote, we want to * remove the open quote from the following string as * well - copy in the following string without its open * quote */ if (!add_close) { /* * append the following token without its first * character (its open quote) */ subexp->append(tok.get_text() + 1, tok.get_text_len() - 1); /* move on to the next token */ prvprvtok = prvtok; prvtok = tok; typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE); /* start from the new token */ start = tok.get_text(); } } else if (pasting) { const char *argp; size_t len; int done; wchar_t quote_char; /* get the actual argument information */ argp = srcbuf->get_text() + argofs[argnum]; len = argnum_len; /* * if we're pasting to the left of this token, and the * token starts with a fully-expanded flag, remove the * flag - we're making up a new token out of this and * what comes before, so the token that we fully * expanded is disappearing, so the fully-expanded * status no longer applies */ if (pasting_at_left && *argp == TOK_FULLY_EXPANDED_FLAG) { /* skip the flag */ ++argp; --len; } /* presume we won't find any quoted strings */ quote_char = 0; /* * check for string concatenation to the left - if we're * concatenating two strings of the same type, remove * the adjacent quotes to make it a single string */ if (pasting_at_left && subexp->get_text_len() > 0 && (*argp == '\'' || *argp == '"') && *(subexp->get_text_end() - 1) == *argp) { /* remove the close quote from the expansion so far */ subexp->set_text_len(subexp->get_text_len() - 1); /* remember the quote character */ quote_char = *argp; /* don't add the open quote to the new string */ ++argp; --len; } /* presume we won't have to do anything special */ done = FALSE; /* * If we're pasting at the right, also remove any * fully-expanded flag just before the last token in the * expansion. */ if (pasting_at_right) { CTcToken old_tok; CTcToken tok; utf8_ptr p; /* scan for the final token in the expansion string */ p.set((char *)argp); old_tok.settyp(TOKT_INVALID); while (p.getptr() < argp + len) { /* * get another token - stop at EOF or if we go * past the bounds of the expansion text */ if (next_on_line(&p, &tok, ¯o_in_embedding_, TRUE) == TOKT_EOF || tok.get_text() >= argp + len) break; /* remember the previous token */ old_tok = tok; } /* * if the final token is a symbol, and it has the * fully-expanded flag, we must omit the flag from * the appended text */ if (old_tok.gettyp() == TOKT_SYM && old_tok.get_fully_expanded()) { /* * append up to but not including the flag byte * preceding the final token */ subexp->append(argp, tok.get_text() - 1 - argp); /* * append from the last token to the end of the * expansion, skipping the flag byte */ subexp->append(tok.get_text(), len - (tok.get_text() - argp)); /* we've done the appending */ done = TRUE; } else if (quote_char != 0 && paste_at_right_tok.get_text_len() != 0 && *paste_at_right_tok.get_text() == quote_char) { /* * we're pasting two strings together - append * up to but not including the close quote */ subexp->append(argp, len - 1); /* * append the next token, but do not include the * open quote */ subexp->append(paste_at_right_tok.get_text() + 1, paste_at_right_tok.get_text_len() - 1); /* * restart after the right token, since we've * now fully processed that token */ start = paste_at_right_tok.get_text() + paste_at_right_tok.get_text_len(); /* we're done */ done = TRUE; } } /* * append the actual without expansion, if we haven't * already handled it specially */ if (!done) subexp->append(argp, len); } else if (stringize) { /* stringize the actual */ stringize_macro_actual(subexp, srcbuf->get_text() + argofs[argnum], argnum_len, stringize_qu, TRUE, TRUE); } else { CTcTokStringRef actual_src_buf; /* recursively expand macros in the actual text */ actual_src_buf. set_buffer(srcbuf->get_text() + argofs[argnum], argnum_len); if (expand_macros(&actual_src_buf, 0, actual_exp_buf, FALSE, allow_defined, FALSE)) return 1; /* * Append the expanded actual, marking any * fully-expanded tokens as such and removing * end-of-expansion markers. * * We can't leave end-of-expansion markers in the * expanded actual text, because end-of-expansion * markers apply only to the current recursion level, * and we've now exited the actual's recursion level. * However, we must not expand further anything in the * actual's expansion that has already been fully * expanded. To achieve both of these goals, we switch * here from marking the run of text (with the end * marker) to marking individual tokens. */ mark_full_exp_tokens(subexp, actual_exp_buf, TRUE); } /* we've already read the next token, so proceed */ continue; } /* remember the current token as the previous token */ prvprvtok = prvtok; prvtok = tok; /* get the next token of the expansion */ typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE); } /* copy the remaining replacement text */ subexp->append(start, tok.get_text() - start); /* success */ return 0; } /* * Skip the source of a delimited macro expansion area (#foreach, * #ifempty, #ifnempty). */ void CTcTokenizer::skip_delimited_group(utf8_ptr *p, int parts_to_skip) { wchar_t delim; /* get the delimiter character */ delim = p->getch(); /* * if the delimiter put us at the end of the line, there's nothing to * skip */ if (delim == 0 || delim == TOK_END_PP_LINE) return; /* skip the delimiter */ p->inc(); /* keep going until we've skipped the desired number of parts */ while (parts_to_skip != 0) { wchar_t ch; /* read the next character */ ch = p->getch(); /* if it's the end of the line, give up */ if (ch == 0 || ch == TOK_END_PP_LINE) { /* * we ran out of input before reaching the delimiter, so this * is implicitly the end of it */ return; } /* check what we have */ if (ch == delim) { /* that's one less part to skip */ --parts_to_skip; /* skip it */ p->inc(); } else if (ch == TOK_MACRO_FOREACH_FLAG) { /* it's a nested #foreach - skip all of its parts */ skip_delimited_group(p, 2); } else if (ch == TOK_MACRO_IFEMPTY_FLAG || ch == TOK_MACRO_IFNEMPTY_FLAG) { /* nested #ifempty or #ifnempty - skip its expansion */ skip_delimited_group(p, 1); } else { /* it's nothing special to us - skip it */ p->inc(); } } } /* * Stringize a macro actual parameter value into a macro expansion * buffer */ void CTcTokenizer::stringize_macro_actual(CTcTokString *expbuf, const char *actual_val, size_t actual_len, char quote_char, int add_open_quote, int add_close_quote) { utf8_ptr src; const char *start; int in_inner_quote; wchar_t inner_quote_char; wchar_t prvch; /* add the open quote if desired */ if (add_open_quote) expbuf->append("e_char, 1); /* remember the start of the current segment */ start = actual_val; /* * add the characters of the actual parameter value, quoting any * quotes or backslashes */ for (src.set((char *)actual_val), in_inner_quote = FALSE, inner_quote_char = '\0', prvch = '\0' ; src.getptr() < actual_val + actual_len ; ) { wchar_t cur; /* get this character */ cur = src.getch(); /* compress runs of whitespace to single spaces */ if (is_space(cur) && prvch != '\\') { /* append up to this character */ if (src.getptr() > start) expbuf->append(start, src.getptr() - start); /* find the next non-space character */ for ( ; src.getptr() < actual_val + actual_len ; src.inc()) { if (!is_space(src.getch())) break; } /* * if we're not at the start or end of the string, add a * single space to replace the entire run of whitespace -- * don't do this at the start or end of the string, since * we must remove leading and trailing whitespace */ if (prvch != '\0' && src.getptr() < actual_val + actual_len) expbuf->append(" ", 1); /* note that the previous character is a space */ prvch = cur; /* this is the new starting point */ start = src.getptr(); /* proceed - we're already at the next character */ continue; } /* * Check to see if we need to quote this character. Quote any * quote mark matching the enclosing quotes; also quote any * backslash that occurs within nested quotes within the source * material, but not backslashes that occur originally outside * quotes. */ if (cur == quote_char || (cur == '\\' && in_inner_quote)) { /* append the segment up to (but not including) this character */ if (src.getptr() > start) expbuf->append(start, src.getptr() - start); /* add an extra backslash */ expbuf->append("\\", 1); /* remember the start of the next segment */ start = src.getptr(); } /* * if this is a quote character, and it's not itself escaped, * reverse our in-quote flag */ if (prvch != '\\') { /* * If we're in an inner quote, and it's a match for the open * inner quote, we're no longer in a quote. Otherwise, if * we're not in quotes and this is some kind of quote, enter * the new quotes. */ if (in_inner_quote && cur == inner_quote_char) { /* we're leaving the inner quoted string */ in_inner_quote = FALSE; } else if (!in_inner_quote && (cur == '"' || cur == '\'')) { /* we're entering a new inner quoted string */ in_inner_quote = TRUE; inner_quote_char = cur; } } /* remember this as the previous character */ prvch = cur; /* move on to the next character */ src.inc(); } /* if there's anything in the final segment, append it */ if (src.getptr() > start) expbuf->append(start, src.getptr() - start); /* add the close quote if desired */ if (add_close_quote) expbuf->append("e_char, 1); } /* * Expand a "defined" preprocessor operator */ int CTcTokenizer::expand_defined(CTcTokString *subexp, const CTcTokString *srcbuf, utf8_ptr *src) { CTcToken tok; tc_toktyp_t typ; int paren; int found; /* get the next token */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, FALSE); /* note whether we have an open paren; if we do, skip it */ paren = (typ == TOKT_LPAR); if (paren) typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, FALSE); /* get the symbol */ if (typ != TOKT_SYM) { log_error(TCERR_PP_DEFINED_NO_SYM, (int)tok.get_text_len(), tok.get_text()); return 1; } /* look to see if the symbol is defined */ found = (find_define(tok.get_text(), tok.get_text_len()) != 0); /* expand the macro to "1" if found, "0" if not */ subexp->copy(found ? "1" : "0", 1); /* check for and skip the matching close paren */ if (paren) { /* require the closing paren */ if (next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, FALSE) != TOKT_RPAR) { /* generate an error if we don't find it */ log_error(TCERR_PP_DEFINED_RPAR); return 1; } } /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Process comments. Replaces each character of a comment with a space. */ void CTcTokenizer::process_comments(size_t start_ofs) { utf8_ptr src; utf8_ptr dst; /* we haven't found a backslash followed by trailing space yet */ int trailing_sp_after_bs = FALSE; /* * Scan the line. When inside a comment, replace each character of * the comment with a space. When outside comments, simply copy * characters intact. * * Note that we need a separate src and dst pointer, because the * character length of the original and replaced characters may * change. Fortunately, the length will never do anything but * shrink or stay the same, since the only change we make is to * insert spaces, which are always one byte apiece in UTF-8; we can * therefore update the buffer in place. */ for (src.set(linebuf_.get_buf() + start_ofs), dst.set(linebuf_.get_buf() + start_ofs) ; src.getch() != '\0' ; src.inc()) { /* get the current character */ wchar_t cur = src.getch(); /* check to see if we're in a comment */ if (str_->is_in_comment()) { /* * check to see if the comment is ending, or if we have an * apparent nested comment (which isn't allowed) */ if (cur == '*' && src.getch_at(1) == '/') { /* * skip an extra character of the source - we'll skip * one in the main loop, so we only need to skip one * more now */ src.inc(); /* we're no longer in a comment */ str_->set_in_comment(FALSE); } else if (cur == '/' && src.getch_at(1) == '*') { /* looks like a nested comment - warn about it */ if (!G_prs->get_syntax_only()) log_warning(TCERR_NESTED_COMMENT); } /* continue without copying anything from inside the comment */ continue; } else if (in_quote_ != '\0') { /* see what we have */ if (cur == '\\') { /* * It's a backslash sequence -- copy the backslash to * the output, and skip it. Note that we don't have to * worry about the line ending with a backslash, since * the line reader will already have considered that to * be a line splice. */ src.inc(); dst.setch(cur); /* get the next character, so we copy it directly */ cur = src.getch(); /* * if we're in a triple-quoted string, and this is an * escaped quote, the backslash escapes a whole run of * consecutive quotes that follow */ if (in_triple_ && cur == in_quote_) { /* copy and skip any run of quotes, minus the last one */ for (int qcnt = count_quotes(&src, cur) ; qcnt > 1 ; dst.setch(cur), src.inc(), --qcnt) ; } } else if (cur == in_quote_) { /* This is the close quote. Check for triple quotes. */ if (in_triple_) { /* triple-quoted - we need three quotes in a row */ int qcnt = count_quotes(&src, cur); if (qcnt >= 3) { /* copy and skip all but the last in the run */ for (int i = 1 ; i < qcnt ; ++i, src.inc()) dst.setch(cur); /* close the string */ in_quote_ = '\0'; } } else { /* regular string, so it ends at the matching quote */ in_quote_ = '\0'; } } else if (cur == '<' && src.getch_at(1) == '<') { /* * it's an embedded expression starting point - skip the * first of the '<' characters (the enclosing loop will * skip the second one) */ src.inc(); /* we're in an embedding now */ comment_in_embedding_.start_expr(in_quote_, in_triple_, FALSE); /* the string is done for now */ in_quote_ = '\0'; /* copy the extra '<' to the output */ dst.setch('<'); } } else { /* * Monitor the stream for a backslash followed by trailing * spaces. If this is a backslash, note that we might have a * backslash with trailing spaces; if it's a space, we might * still have this, so leave the flag alone; if it's anything * else, clear the flag, since we've found something other * than backslashes and spaces. */ if (cur == '\\') trailing_sp_after_bs = TRUE; else if (!is_space(cur)) trailing_sp_after_bs = FALSE; /* check to see if we're starting a comment */ if (cur == '/') { switch(src.getch_at(1)) { case '*': /* note that we're starting a comment */ str_->set_in_comment(TRUE); /* * replace the starting slash with a space - this * will effectively replace the entire comment with * a single space, since we won't copy anything else * from inside the comment */ cur = ' '; break; case '/': /* * comment to end of line - we can terminate the * line at the opening slash and return immediately, * because the entire rest of the line is to be * ignored */ dst.setch('\0'); return; default: /* not a comment - copy it as-is */ break; } } else if (cur == '"' || cur == '\'') { /* it's the start of a new string */ in_quote_ = cur; /* check for triple quotes */ in_triple_ = (count_quotes(&src, cur) >= 3); /* if in triple quotes, copy and skip the extra two qutoes */ if (in_triple_) { src.inc_by(2); dst.setch(cur); dst.setch(cur); } } else if (cur < 0x09) { /* * it's a special flag character - we need to guarantee * that this character never occurs in input (it * shouldn't anyway, since it's a control character), so * translate it to a space */ cur = ' '; } else if (comment_in_embedding_.in_expr() && (cur == '(' || cur == ')')) { /* adjust the paren level in an embedded expression */ comment_in_embedding_.parens(cur == '(' ? 1 : -1); } else if (comment_in_embedding_.in_expr() && comment_in_embedding_.parens() == 0 && cur == '>' && src.getch_at(1) == '>') { /* it's the end of an embedded expression */ in_quote_ = comment_in_embedding_.qu(); in_triple_ = comment_in_embedding_.triple(); comment_in_embedding_.end_expr(); /* skip the extra '>' and copy it to the output */ src.inc(); dst.setch('>'); } } /* set the current character in the output */ dst.setch(cur); } /* set the updated line buffer length */ linebuf_.set_text_len(dst.getptr() - linebuf_.get_buf()); /* * if we found a backslash with nothing following but whitespace, flag * a warning, since they might have meant the backslash as a line * continuation signal, but we're not interpreting it that way because * of the trailing whitespace */ if (trailing_sp_after_bs) log_warning(TCERR_TRAILING_SP_AFTER_BS); } /* * Splice strings. Splice additional lines onto the current line until * we find the end of the string. */ void CTcTokenizer::splice_string() { /* presume we'll find proper termination */ char unterm = '\0'; /* * remember the current in-quote and in-embedding status, as of the * end of the current line - when we splice, the line reader will * update these to the status at the end of the newly-read material, * but we want to scan from the beginning of the newly-read material */ wchar_t in_quote = in_quote_; int in_triple = in_triple_; tok_embed_ctx old_ec = comment_in_embedding_; /* note if the previous line ended with an explicit \n sequence */ int explicit_nl = (linebuf_.get_text_len() >= 2 && memcmp(linebuf_.get_text_end() - 2, "\\n", 2) == 0); /* keep going until we find the end of the string */ utf8_ptr p((char *)0); for (;;) { /* apply the newline spacing mode */ switch (string_newline_spacing_) { case NEWLINE_SPACING_COLLAPSE: /* * Replace the newline and any subsequent run of whitespace * characters with a single space, unless the last line ended * with an explicit newline. */ if (!explicit_nl) linebuf_.append(" ", 1); break; case NEWLINE_SPACING_DELETE: /* delete the newline and subsequent whitespace */ break; case NEWLINE_SPACING_PRESERVE: /* * preserve the newline and subsequent whitespace exactly as * written in the source code, so convert the newline to an * explicit \n sequence (in source form, since we're building a * source line at this point) */ linebuf_.append("\\n", 2); break; } /* splice another line */ int new_line_ofs = read_line(TRUE); /* if we reached end of file, there's no more splicing we can do */ if (new_line_ofs == -1) break; /* get a pointer to the new text */ char *new_line_p = (char *)linebuf_.get_text() + new_line_ofs; p.set(new_line_p); /* * If we're in COLLAPSE or DELETE mode for newline spacing, skip * leading spaces in the new line. However, override this if the * previous line ended with an explicit \n sequence; this tells us * that the line break is explicitly part of the string and that * the subsequent whitespace is to be preserved. */ if (string_newline_spacing_ != NEWLINE_SPACING_PRESERVE && !explicit_nl) for ( ; is_space(p.getch()) ; p.inc()) ; /* check to see if the newly spliced text ends in an explicit \n */ explicit_nl = (linebuf_.get_text_len() - new_line_ofs >= 2 && memcmp(linebuf_.get_text_end() - 2, "\\n", 2) == 0); /* if we skipped any spaces, remove them from the text */ if (p.getptr() > new_line_p) { /* calculate the length of the rest of the line */ size_t rem = linebuf_.get_text_len() - (p.getptr() - linebuf_.get_buf()); /* calculate the new length of the line */ size_t new_len = (new_line_p - linebuf_.get_buf()) + rem; /* move the rest of the line down over the spaces */ memmove(new_line_p, p.getptr(), rem); /* set the new length */ linebuf_.set_text_len(new_len); } /* * If the new line contains only "}" or ";", presume that the * string is unterminated and terminate it here. (This * heuristic could flag well-formed strings as erroneous, but * users can always work around this by moving these characters * onto lines that contain at least one other non-whitespace * character.) */ p.set(new_line_p); if (p.getch() == '}' || p.getch() == ';') { /* skip trailing whitespace */ for (p.inc() ; is_space(p.getch()) ; p.inc()) ; /* * if there's nothing else on the line, presume it's an * unterminated string */ if (p.getch() == '\0') { /* log the error */ log_error(TCERR_POSSIBLE_UNTERM_STR, appended_linenum_); /* remember that it's unterminated */ unterm = (char)in_quote; /* * since we're adding a presumed close quote that never * appears in the text, we need to figure the new * in-string status for the line; clear the in-quote * flag, and re-scan comments from the current point on * the line */ in_quote_ = '\0'; process_comments(new_line_p - linebuf_.get_buf()); /* we're done - unsplice from the start of the new line */ p.set(new_line_p); goto done; } } /* scan for the end of the string */ for (p.set(new_line_p) ; ; p.inc()) { /* get this character */ wchar_t cur = p.getch(); /* see what we have */ if (cur == '\\') { /* it's a backslash sequence - skip the extra character */ p.inc(); /* * in a triple-quoted string, a backslash escapes a whole * run of consecutive quote characters */ if (in_triple && p.getch() == in_quote) { /* skip all but the last consecutive quote */ int qcnt = count_quotes(&p, in_quote); p.inc_by(qcnt - 1); } else if (p.getch() == '<') { /* skip a run of <'s */ for ( ; p.getch() == '<' ; p.inc()) ; } } else if (cur == in_quote) { /* it's our quote character - check for triple quotes */ if (in_triple) { /* in a triple-quoted string - we need 3+ to end it */ int qcnt = count_quotes(&p, cur); if (qcnt >= 3) { /* we have at least 3 - skip them and exit */ p.inc_by(qcnt); goto done; } } else { /* regular string - skip it and exit the string */ p.inc(); goto done; } } else if (cur == '<' && p.getch_at(1) == '<') { /* * it's an embedded expression starter - skip the '<<' * sequence and stop scanning */ p.inc(); p.inc(); /* check for a '%' sprintf code */ if (p.getch() == '%') scan_sprintf_spec(&p); /* done */ goto done; } else if (cur == '\0') { /* end of line - go back and splice another line */ break; } } } done: /* if we actually spliced anything, unsplice it at the current point */ if (p.getptr() != 0) unsplice_line(p.getptr()); /* if we found an unterminated string, supply implicit termination */ if (unterm != '\0') linebuf_.append(&unterm, 1); } /* ------------------------------------------------------------------------ */ /* * Process a #pragma directive */ void CTcTokenizer::pp_pragma() { struct pp_kw_def { const char *kw; void (CTcTokenizer::*func)(); }; static pp_kw_def kwlist[] = { // { "c", &CTcTokenizer::pragma_c }, -- obsolete { "once", &CTcTokenizer::pragma_once }, { "all_once", &CTcTokenizer::pragma_all_once }, { "message", &CTcTokenizer::pragma_message }, { "newline_spacing", &CTcTokenizer::pragma_newline_spacing }, { "sourceTextGroup", &CTcTokenizer::pragma_source_text_group }, { 0, 0 } }; pp_kw_def *kwp; size_t kwlen; /* get the pragma keyword */ if (next_on_line() != TOKT_SYM) { log_warning(TCERR_UNKNOWN_PRAGMA, (int)curtok_.get_text_len(), curtok_.get_text()); return; } /* get the keyword length */ kwlen = curtok_.get_text_len(); /* scan the pragma list */ for (kwp = kwlist ; kwp->kw != 0 ; ++kwp) { /* is this our keyword? */ if (strlen(kwp->kw) == kwlen && memicmp(curtok_.get_text(), kwp->kw, kwlen) == 0) { /* this is our keyword - invoke the handler */ (this->*(kwp->func))(); /* we're done */ return; } } /* we didn't find it - generate a warning */ log_warning(TCERR_UNKNOWN_PRAGMA, kwlen, curtok_.get_text()); } #if 0 // #pragma C is not currently used /* * Process a #pragma C directive */ void CTcTokenizer::pragma_c() { tc_toktyp_t tok; int new_pragma_c; /* get the next token */ tok = next_on_line(); /* * "+" or empty (end of line or whitespace) indicates C mode; "-" * indicates standard mode */ if (tok == TOKT_PLUS || tok == TOKT_EOF) new_pragma_c = TRUE; else if (tok == TOKT_MINUS) new_pragma_c = FALSE; else { log_warning(TCERR_BAD_PRAGMA_SYNTAX); new_pragma_c = str_->is_pragma_c(); } /* * retain the pragma in the result if we're in preprocess-only mode, * otherwise remove it */ if (!pp_only_mode_) clear_linebuf(); /* set the mode in the stream */ str_->set_pragma_c(new_pragma_c); /* if there's a parser, notify it of the change */ if (G_prs != 0) G_prs->set_pragma_c(new_pragma_c); } #endif /* * Process a #pragma once directive */ void CTcTokenizer::pragma_once() { /* add this file to the ONCE list */ add_include_once(str_->get_desc()->get_fname()); /* don't retain this pragma in the result */ clear_linebuf(); } /* * Process a #pragma all_once directive */ void CTcTokenizer::pragma_all_once() { tc_toktyp_t tok; /* get the next token */ tok = next_on_line(); /* * "+" or empty (end of line or whitespace) indicates ALL_ONCE mode; * '-' indicates standard mode */ if (tok == TOKT_PLUS || tok == TOKT_EOF) all_once_ = TRUE; else if (tok == TOKT_MINUS) all_once_ = FALSE; else log_warning(TCERR_BAD_PRAGMA_SYNTAX); /* don't retain this pragma in the result */ clear_linebuf(); } /* * Process a #pragma message directive */ void CTcTokenizer::pragma_message() { size_t startofs; /* * copy the source line through the "message" token to the macro * expansion buffer - we don't want to expand that part, but we want * it to appear in the expansion, so just copy the original */ startofs = (curtok_.get_text() + curtok_.get_text_len() - linebuf_.get_text()); expbuf_.copy(linebuf_.get_text(), startofs); /* expand macros; don't allow reading additional lines */ if (expand_macros_curline(FALSE, FALSE, TRUE)) { clear_linebuf(); return; } /* * If we're in normal compilation mode, display the message. If we're * in preprocess-only mode, simply retain the message in the * preprocessed result, so that it shows up when the result is * compiled. * * Ignore messages in list-includes mode. */ if (!pp_only_mode_ && !list_includes_mode_) { /* set up at the first post-processed token */ start_new_line(&expbuf_, startofs); /* if there's an open paren, skip it */ if (next_on_line_xlat(0) == TOKT_LPAR) next_on_line_xlat(0); else log_warning(TCERR_BAD_PRAGMA_SYNTAX); /* keep going until we reach the closing paren */ while (curtok_.gettyp() != TOKT_RPAR && curtok_.gettyp() != TOKT_EOF) { /* display this token */ switch(curtok_.gettyp()) { case TOKT_SSTR: case TOKT_DSTR: case TOKT_SYM: /* display the text of the token */ msg_str(curtok_.get_text(), curtok_.get_text_len()); break; case TOKT_INT: /* display the integer */ msg_long(curtok_.get_int_val()); break; default: /* ignore anything else */ break; } /* get the next token */ next_on_line_xlat(0); } /* end the line */ msg_str("\n", 1); /* remove the message from the result text */ clear_linebuf(); } else { /* preprocessing - copy expanded text to line buffer */ linebuf_.copy(expbuf_.get_text(), expbuf_.get_text_len()); } } /* * Process a #pragma newline_spacing(on/off) directive */ void CTcTokenizer::pragma_newline_spacing() { newline_spacing_mode_t f; /* if we're in preprocess-only mode, just pass the pragma through */ if (pp_only_mode_) return; /* get the '(' token and the on/off token */ if (next_on_line() != TOKT_LPAR || next_on_line() != TOKT_SYM) { log_warning(TCERR_BAD_PRAGMA_SYNTAX); goto done; } /* note the new mode flag */ if (curtok_.get_text_len() == 2 && memcmp(curtok_.get_text(), "on", 2) == 0) { /* 'on' - this is the old version of 'collapse' */ f = NEWLINE_SPACING_COLLAPSE; } else if (curtok_.get_text_len() == 8 && memcmp(curtok_.get_text(), "collapse", 8) == 0) { f = NEWLINE_SPACING_COLLAPSE; } else if (curtok_.get_text_len() == 3 && memcmp(curtok_.get_text(), "off", 3) == 0) { /* 'off' - the old version of 'delete' */ f = NEWLINE_SPACING_DELETE; } else if (curtok_.get_text_len() == 6 && memcmp(curtok_.get_text(), "delete", 6) == 0) { f = NEWLINE_SPACING_DELETE; } else if (curtok_.get_text_len() == 8 && memcmp(curtok_.get_text(), "preserve", 8) == 0) { f = NEWLINE_SPACING_PRESERVE; } else { log_warning(TCERR_BAD_PRAGMA_SYNTAX); goto done; } /* make sure we have the ')' token */ if (next_on_line() != TOKT_RPAR) { log_warning(TCERR_BAD_PRAGMA_SYNTAX); goto done; } /* set the new mode */ string_newline_spacing_ = f; done: /* done - discard this line buffer */ clear_linebuf(); } /* * Process a #pragma sourceTextGroup(on/off) directive */ void CTcTokenizer::pragma_source_text_group() { tc_toktyp_t tok; int f; /* if we're in preprocess-only mode, just pass the pragma through */ if (pp_only_mode_) return; /* get the '(' token and the on/off token, if present */ if ((tok = next_on_line()) == TOKT_EOF) { /* no on/off - by default it's on */ f = TRUE; } else if (tok == TOKT_LPAR && next_on_line() == TOKT_SYM) { /* get the on/off mode */ if (curtok_.get_text_len() == 2 && memcmp(curtok_.get_text(), "on", 2) == 0) { /* it's 'on' */ f = TRUE; } else if (curtok_.get_text_len() == 3 && memcmp(curtok_.get_text(), "off", 3) == 0) { /* it's 'off' */ f = FALSE; } else { log_warning(TCERR_BAD_PRAGMA_SYNTAX); goto done; } /* make sure we have the ')' token */ if (next_on_line() != TOKT_RPAR) { log_warning(TCERR_BAD_PRAGMA_SYNTAX); goto done; } } else { /* anything else is invalid syntax */ log_warning(TCERR_BAD_PRAGMA_SYNTAX); goto done; } /* set the new mode in the parser */ G_prs->set_source_text_group_mode(f); done: /* done - discard this line buffer */ clear_linebuf(); } /* ------------------------------------------------------------------------ */ /* * Process a #charset directive */ void CTcTokenizer::pp_charset() { /* * Encountering a #charset directive within the tokenizer is always * an error. If the file opener managed to use a #charset, we'll * never see it, because the file opener will have skipped it before * giving us the file. * * If we flagged a #charset error when opening the file, indicate * that the problem is that the character set given was unloadable; * otherwise, the problem is that #charset is in the wrong place. */ log_error(str_ != 0 && str_->get_charset_error() ? TCERR_CANT_LOAD_CHARSET : TCERR_UNEXPECTED_CHARSET); /* don't retain this pragma in the result */ clear_linebuf(); } /* ------------------------------------------------------------------------ */ /* * Process a #include directive */ void CTcTokenizer::pp_include() { wchar_t match; int is_local; int is_absolute; utf8_ptr fname; CTcSrcFile *new_src; int charset_error; int default_charset_error; char full_name[OSFNMAX]; char lcl_name[OSFNMAX]; int found; CTcTokFileDesc *desc; int expand; utf8_ptr start; /* presume we'll expand macros */ expand = TRUE; /* * Check to see if expansion is needed. Macro expansion is needed * only if the source line is not of one of the following forms: * *. #include "filename" *. #include */ for (start = p_ ; is_space(p_.getch()) ; p_.inc()) ; switch(p_.getch()) { case '<': /* look for a matching '>' */ match = '>'; goto find_match; case '"': /* look for a matching '"' */ match = '"'; goto find_match; find_match: /* find the matching character */ for (p_.inc() ; p_.getch() != '\0' && p_.getch() != match ; p_.inc()) ; /* if we found it, check for other characters on the line */ if (p_.getch() == match) { /* skip the matching character */ p_.inc(); /* skip whitespace */ while (is_space(p_.getch())) p_.inc(); /* * make sure there's nothing else on the line - if not, it's * one of the approved formats, so there's no need to do * macro expansion */ if (p_.getch() == 0) expand = FALSE; } break; } /* go back to read from the original starting point */ p_ = start; /* expand macros if necessary */ if (expand) { /* do the expansion */ if (expand_macros_curline(FALSE, FALSE, FALSE)) { /* clear the buffer and abort */ clear_linebuf(); return; } /* * remove any expansion flags, so that we don't have to worry about * parsing or skipping them */ remove_expansion_flags(&expbuf_); /* read from the expansion buffer */ start_new_line(&expbuf_, 0); } /* skip leading whitespace */ for ( ; is_space(p_.getch()) ; p_.inc()) ; /* we have to be looking at at '"' or '<' character */ if (p_.getch() == '"') { /* look for a matching quote, and look for a local file */ match = '"'; is_local = TRUE; } else if (p_.getch() == '<') { /* look for a matching angle bracket, and look for a system file */ match = '>'; is_local = FALSE; } else { /* invalid syntax - log an error and ignore the line */ log_error(TCERR_BAD_INC_SYNTAX); clear_linebuf(); return; } /* skip the open quote, and remember where the filename starts */ p_.inc(); fname = p_; /* find the matching quote */ for ( ; p_.getch() != '\0' && p_.getch() != match ; p_.inc()) ; /* if we didn't find the match, log an error and ignore the line */ if (p_.getch() == '\0') { log_error(TCERR_BAD_INC_SYNTAX); clear_linebuf(); return; } else { /* * We found the close quote. Before we parse the filename, make * one last check: if there's anything further on the line apart * from whitespace, it's extraneous, so issue a warning. */ /* remember where the close quote is */ utf8_ptr closep = p_; /* skip it, and then skip any trailing whitespace */ for (p_.inc() ; is_space(p_.getch()) ; p_.inc()) ; /* if we're not at the end of the line, issue a warning */ if (p_.getch() != '\0') log_warning(TCERR_EXTRA_INC_SYNTAX); /* * Null-terminate the filename. (We know there's nothing else * interesting in the buffer after the filename at this point, so * we don't care about overwriting the quote or anything that might * come after it.) */ closep.setch('\0'); } /* check to see if the filename is absolute */ is_absolute = os_is_file_absolute(fname.getptr()); /* we have yet to find the file */ found = FALSE; /* * in case the name is in portable URL notation, convert from URL * notation to local notation; we'll consider this form of the name * first, and only if we can't find it in this form will we try * treating the name as using local filename conventions */ os_cvt_url_dir(lcl_name, sizeof(lcl_name), fname.getptr()); /* * Search for the included file. * * First, if it's a local file (in quotes rather than angle * brackets), start the search in the directory containing the * current file, then look in the directory containing the parent * file, and so on. If we fail to find it, proceed as for a * non-local file. */ if (is_local && last_desc_ != 0) { CTcTokStream *cur_str; char pathbuf[OSFNMAX]; /* start with the current file, and search parents */ for (cur_str = str_ ; cur_str != 0 ; cur_str = cur_str->get_parent()) { /* get the path to the current file */ os_get_path_name(pathbuf, sizeof(pathbuf), last_desc_->get_fname()); /* * try the URL-converted name first - this takes precedence * over a local interpretation of the name */ os_build_full_path(full_name, sizeof(full_name), pathbuf, lcl_name); if (!osfacc(full_name)) { found = TRUE; break; } /* if it's a relative local name, try again with local naming */ if (!is_absolute) { /* * build the full filename, treating the name as using * local system conventions */ os_build_full_path(full_name, sizeof(full_name), pathbuf, fname.getptr()); /* if we found it, so note and stop searching */ if (!osfacc(full_name)) { found = TRUE; break; } } } } /* * If we still haven't found the file (or if it's a non-local file, * in angle brackets), search the include path. */ if (!found) { tctok_incpath_t *inc_path; /* scan the include path */ for (inc_path = incpath_head_ ; inc_path != 0 ; inc_path = inc_path->nxt) { /* try the URL-converted local name first */ os_build_full_path(full_name, sizeof(full_name), inc_path->path, lcl_name); if (!osfacc(full_name)) { found = TRUE; break; } /* try with the local name, if it's a relative local name */ if (!is_absolute) { /* build the full name for the file in this directory */ os_build_full_path(full_name, sizeof(full_name), inc_path->path, fname.getptr()); /* if we found it, stop searching */ if (!osfacc(full_name)) { found = TRUE; break; } } } } /* * If the filename specified an absolute path, and we didn't find a * file with any of the local interpretations, look at the absolute * path. Note that our portable URL-style notation doesn't allow * absolute notation, so we use only the exact name as specified in * the #include directive as the absolute form. */ if (is_absolute && !found) { /* use the original filename as the full name */ strcpy(full_name, fname.getptr()); /* try finding the file */ found = !osfacc(full_name); } /* * we have our copy of the filename now; we don't want to retain * this directive in the preprocessed source, so clear out the line * buffer now */ clear_linebuf(); /* * if we didn't find the file anywhere, show an error and ignore the * #include directive */ if (!found) { log_error(TCERR_INC_NOT_FOUND, (int)strlen(fname.getptr()), fname.getptr()); return; } /* * Check the list of included files that are marked for inclusion * only once. If we've already included this file, ignore this * redundant inclusion. Check based on the full filename that we * resolved from the search path. */ if (find_include_once(full_name)) { /* log an error if appropriate */ if (warn_on_ignore_incl_) log_warning(TCERR_REDUNDANT_INCLUDE, (int)strlen(full_name), full_name); /* ignore this #include directive */ return; } /* open a file source to read the file */ new_src = CTcSrcFile::open_source(full_name, res_loader_, default_charset_, &charset_error, &default_charset_error); /* if we couldn't open the file, log an error and ignore the line */ if (new_src == 0) { /* * if the error was due to the default character set, log that * problem; otherwise, log the general file-open problem */ if (default_charset_error) log_error(TCERR_CANT_LOAD_DEFAULT_CHARSET, default_charset_); else log_error(TCERR_INC_NOT_FOUND, (int)strlen(full_name), full_name); /* we can go no further */ return; } /* get the descriptor for the source file */ desc = get_file_desc(full_name, strlen(full_name), FALSE, fname.getptr(), fname.getptr() != 0 ? strlen(fname.getptr()) : 0); /* * remember the current #pragma newline_spacing mode, so we can restore * it when we reinstate the current stream */ str_->set_newline_spacing(string_newline_spacing_); /* * Create and install the new file reader stream object. By * installing it as the current reader, we'll activate it so that * the next line read will come from the new stream. Note that the * current stream becomes the parent of the new stream, so that we * revert to the current stream when the new stream is exhausted; * this will allow us to pick up reading from the current stream at * the next line after the #include directive when we've finished * including the new file. */ str_ = new CTcTokStream(desc, new_src, str_, charset_error, if_sp_); /* * If we're in ALL_ONCE mode, it means that every single file we * include should be included only once. */ if (all_once_) add_include_once(full_name); /* * if we're in list-includes mode, write the name of the include file * to the standard output */ if (list_includes_mode_) G_hostifc->print_msg("#include %s\n", full_name); } /* ------------------------------------------------------------------------ */ /* * Add a file to the include-once list. Once a file is in this list, we * won't include it again. */ void CTcTokenizer::add_include_once(const char *fname) { tctok_incfile_t *prvinc; /* if the file is already in the list, don't add it again */ if (find_include_once(fname)) return; /* create a new entry for the filename */ prvinc = (tctok_incfile_t *)t3malloc(sizeof(tctok_incfile_t) + strlen(fname)); /* save the filename */ strcpy(prvinc->fname, fname); /* link the new entry into our list */ prvinc->nxt = prev_includes_; prev_includes_ = prvinc; } /* * Find a file in the list of files to be included only once. Returns * true if the file is in the list, false if not. */ int CTcTokenizer::find_include_once(const char *fname) { tctok_incfile_t *prvinc; /* search the list */ for (prvinc = prev_includes_ ; prvinc != 0 ; prvinc = prvinc->nxt) { /* if this one matches, we found it, so return true */ if (os_file_names_equal(fname, prvinc->fname)) return TRUE; } /* we didn't find the file */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Process a #define directive */ void CTcTokenizer::pp_define() { const char *macro_name; size_t macro_len; const char *argv[TOK_MAX_MACRO_ARGS]; size_t argvlen[TOK_MAX_MACRO_ARGS]; int argc; int has_args; const char *expan; size_t expan_len; CTcHashEntryPp *entry, *old_entry; int has_varargs; /* get the macro name */ if (next_on_line() != TOKT_SYM) { log_error(TCERR_BAD_DEFINE_SYM, (int)curtok_.get_text_len(), curtok_.get_text()); clear_linebuf(); return; } /* make a copy of the macro name */ macro_name = curtok_.get_text(); macro_len = curtok_.get_text_len(); /* no arguments yet */ argc = 0; /* presume we won't find a varargs marker */ has_varargs = FALSE; /* * If there's a '(' immediately after the macro name, without any * intervening whitespace, it has arguments; otherwise, it has no * arguments. Note which case we have. */ if (p_.getch() == '(') { int done; tc_toktyp_t tok; /* note that we have an argument list */ has_args = TRUE; /* assume we're not done yet */ done = FALSE; /* skip the paren and get the next token */ p_.inc(); tok = next_on_line(); /* check for an empty argument list */ if (tok == TOKT_RPAR) { /* note that we're done with the arguments */ done = TRUE; } /* scan the argument list */ while (!done) { /* if we have too many arguments, it's an error */ if (argc >= TOK_MAX_MACRO_ARGS) { log_error(TCERR_TOO_MANY_MAC_PARMS, macro_name, macro_len, TOK_MAX_MACRO_ARGS); clear_linebuf(); return; } /* if we're at the end of the macro, it's an error */ if (tok == TOKT_EOF) { /* log the error and ignore the line */ log_error(TCERR_MACRO_NO_RPAR); clear_linebuf(); return; } /* check for a valid initial symbol character */ if (tok != TOKT_SYM) { log_error_curtok(TCERR_BAD_MACRO_ARG_NAME); clear_linebuf(); return; } /* remember the argument name */ argvlen[argc] = curtok_.get_text_len(); argv[argc++] = curtok_.get_text(); /* get the next token */ tok = next_on_line(); /* make sure we have a comma or paren following */ if (tok == TOKT_COMMA) { /* we have more arguments - skip the comma */ tok = next_on_line(); } else if (tok == TOKT_ELLIPSIS) { /* skip the ellipsis */ tok = next_on_line(); /* note the varargs marker */ has_varargs = TRUE; /* this must be the last argument */ if (tok != TOKT_RPAR) { /* log the error */ log_error_curtok(TCERR_MACRO_ELLIPSIS_REQ_RPAR); /* discard the line and give up */ clear_linebuf(); return; } /* that's the last argument - we can stop now */ done = TRUE; } else if (tok == TOKT_RPAR) { /* no more arguments - note that we can stop now */ done = TRUE; } else { /* invalid argument - log an error and discard the line */ log_error_curtok(TCERR_MACRO_EXP_COMMA); clear_linebuf(); return; } } } else { /* * there are no arguments - the macro's expansion starts * immediately after the end of the name and any subsequent * whitespace */ has_args = FALSE; } /* skip whitespace leading up to the expansion */ while (is_space(p_.getch())) p_.inc(); /* the rest of the line is the expansion */ expan = p_.getptr(); /* don't allow defining "defined" */ if (macro_len == 7 && memcmp(macro_name, "defined", 7) == 0) { /* log an error */ log_error(TCERR_REDEF_OP_DEFINED); /* don't retain the directive in the preprocessed result */ clear_linebuf(); /* ignore the definition */ return; } /* get the length of the expansion text */ expan_len = strlen(expan); /* * remove any trailing whitespace from the expansion text; however, * leave a trailing space if it's preceded by a backslash */ while (expan_len > 0 && is_space(expan[expan_len-1]) && !(expan_len > 1 && expan[expan_len-2] == '\\')) --expan_len; /* create an entry for the new macro */ entry = new CTcHashEntryPpDefine(macro_name, macro_len, TRUE, has_args, argc, has_varargs, argv, argvlen, expan, expan_len); /* * Check the symbol table to see if this symbol is already defined with * a different expansion. If so, show a warning, but honor the new * definition. */ old_entry = find_define(macro_name, macro_len); if (old_entry != 0) { /* * Check for a trivial redefinition - if the number of arguments is * the same, and the type (object-like or function-like) is the * same, and the expansion string is identical, there's no need to * warn, because the redefinition has no effect and can thus be * safely ignored. Note that we must ignore any differences in the * whitespace in the expansions for this comparision. * * Compare the *parsed* versions of the expansions. This ensures * that we take into account changes to parameter names. For * example, the following two macros are equivalent, even though * their unparsed source texts are different, because they'd still * have identical expansions for any given parameter value: * *. #define A(x) x *. #define A(y) y * * Likewise, the following two macros are NOT equivalent, even * though their unparsed source texts are identical, because they * could have different expansions for a given parameter value: * *. #define A(x) x *. #define A(y) x */ if ((old_entry->has_args() != 0) == (has_args != 0) && old_entry->get_argc() == argc && lib_strequal_collapse_spaces(entry->get_expansion(), entry->get_expan_len(), old_entry->get_expansion(), old_entry->get_expan_len())) { /* it's a non-trivial redefinition - ignore it */ delete entry; goto done; } /* log a warning about the redefinition */ log_warning(TCERR_MACRO_REDEF, (int)macro_len, macro_name); /* remove and delete the old entry */ defines_->remove(old_entry); /* if the item isn't already in the #undef table, add it */ if (find_undef(macro_name, macro_len) == 0) { /* * move the entry to the #undef table so that we can keep track * of the fact that this macro's definition has changed in the * course of the compilation */ undefs_->add(old_entry); } else { /* * the name is already in the #undef table, so we don't need * another copy - just forget about the old entry entirely */ delete old_entry; } } /* add it to the hash table */ defines_->add(entry); done: /* don't retain the directive in the preprocessed source */ clear_linebuf(); } /* ------------------------------------------------------------------------ */ /* * Process a #ifdef directive */ void CTcTokenizer::pp_ifdef() { /* process the ifdef/ifndef with a positive sense */ pp_ifdef_or_ifndef(TRUE); } /* * Process a #ifndef directive */ void CTcTokenizer::pp_ifndef() { /* process the ifdef/ifndef with a negative sense */ pp_ifdef_or_ifndef(FALSE); } /* * Process a #ifdef or #ifndef. If 'sense' is true, we'll take the * branch if the symbol is defined (hence #ifdef), otherwise we'll take * it if the symbol isn't defined (hence #ifndef). */ void CTcTokenizer::pp_ifdef_or_ifndef(int sense) { char macro_name[TOK_SYM_MAX_BUFFER]; int found; tok_if_t state; /* make sure we have a valid symbol */ if (pp_get_lone_ident(macro_name, sizeof(macro_name))) { /* clear the line buffer */ clear_linebuf(); /* * push a true if to avoid cascading errors for matching #endif * or #else */ push_if(TOKIF_IF_YES); /* we're done */ return; } /* check to see if it's defined */ found = (find_define(macro_name, strlen(macro_name)) != 0); /* * if we found it and they wanted it found, or we didn't find it and * they didn't want it found, take a true branch; otherwise, take a * false branch */ if ((sense != 0) == (found != 0)) state = TOKIF_IF_YES; else state = TOKIF_IF_NO; /* push the new #if state */ push_if(state); /* don't retain the directive in the preprocessed source */ clear_linebuf(); } /* ------------------------------------------------------------------------ */ /* * Process a #if directive */ void CTcTokenizer::pp_if() { CTcConstVal val; /* expand macros; don't allow reading additional lines */ if (expand_macros_curline(FALSE, TRUE, FALSE)) goto do_error; /* * we don't need the original source line any more, and we don't * want to copy it to the preprocessed output, so clear it */ clear_linebuf(); /* parse out of the expansion buffer */ start_new_line(&expbuf_, 0); /* parse the preprocessor expression */ if (pp_parse_expr(&val, TRUE, TRUE, TRUE)) { /* * we can't get a value; treat the expression as true and * continue parsing, so that we don't throw off the #if nesting * level */ val.set_bool(TRUE); } /* push the new state according to the value of the expression */ push_if(val.get_val_bool() ? TOKIF_IF_YES : TOKIF_IF_NO); /* done */ return; do_error: /* clear the line buffer */ clear_linebuf(); /* * push a true if - even though we can't evaluate the condition, we * can at least avoid a cascade of errors for the matching #endif * and #else */ push_if(TOKIF_IF_YES); } /* ------------------------------------------------------------------------ */ /* * Process a #elif directive */ void CTcTokenizer::pp_elif() { CTcConstVal val; /* expand macros; don't allow reading additional lines */ if (expand_macros_curline(FALSE, TRUE, FALSE)) { clear_linebuf(); return; } /* parse out of the expansion buffer */ start_new_line(&expbuf_, 0); /* parse the preprocessor expression */ if (pp_parse_expr(&val, TRUE, TRUE, TRUE)) { clear_linebuf(); return; } /* * make sure that the #elif occurs in the same file as the * corresponding #if */ if (if_sp_ <= str_->get_init_if_level()) { /* log the error */ log_error(TCERR_PP_ELIF_NOT_IN_SAME_FILE); /* clear the text and abort */ clear_linebuf(); return; } /* check the current #if state */ switch(get_if_state()) { case TOKIF_IF_YES: /* * we just took the #if branch, so don't take this or any * subsequent #elif or #else branch, regardless of the value of * the condition - set the state to DONE to indicate that we're * skipping everything through the endif */ change_if_state(TOKIF_IF_DONE); break; case TOKIF_IF_NO: /* * We haven't yet taken a #if or #elif branch, so we can take * this branch if its condition is true. If this branch's * condition is false, stay with NO so that we will consider * future #elif and #else branches. */ if (val.get_val_bool()) change_if_state(TOKIF_IF_YES); break; case TOKIF_IF_DONE: /* * we've already taken a #if or #elif branch, so we must ignore * this and subsequent #elif and #else branches until we get to * our #endif - just stay in state DONE */ break; case TOKIF_NONE: case TOKIF_ELSE_YES: case TOKIF_ELSE_NO: /* * we're not in a #if branch at all, or we're inside a #else; a * #elif is not legal here */ log_error(TCERR_PP_ELIF_WITHOUT_IF); break; } /* don't retain the directive in the preprocessed source */ clear_linebuf(); } /* ------------------------------------------------------------------------ */ /* * Process a #else directive */ void CTcTokenizer::pp_else() { /* make sure there's nothing but whitespace on the line */ if (next_on_line() != TOKT_EOF) log_error(TCERR_PP_EXTRA); /* * make sure that the #else occurs in the same file as the * corresponding #if */ if (if_sp_ <= str_->get_init_if_level()) { /* log the error */ log_error(TCERR_PP_ELSE_NOT_IN_SAME_FILE); /* clear the text and abort */ clear_linebuf(); return; } /* check our current #if state */ switch(get_if_state()) { case TOKIF_IF_YES: case TOKIF_IF_DONE: /* * we've already taken a true #if branch, so we don't want to * process the #else part - switch to a false #else branch */ change_if_state(TOKIF_ELSE_NO); break; case TOKIF_IF_NO: /* * we haven't yet found a true #if branch, so take the #else * branch -- switch to a true #else branch */ change_if_state(TOKIF_ELSE_YES); break; case TOKIF_NONE: case TOKIF_ELSE_YES: case TOKIF_ELSE_NO: /* * we're not in a #if at all, or we're in a #else - log an error * and ignore it */ log_error(TCERR_PP_ELSE_WITHOUT_IF); break; } /* don't retain the directive in the preprocessed source */ clear_linebuf(); } /* ------------------------------------------------------------------------ */ /* * Process a #endif directive */ void CTcTokenizer::pp_endif() { /* make sure the rest of the line is blank */ if (next_on_line() != TOKT_EOF) log_error(TCERR_PP_EXTRA); /* ignore the rest of the line */ clear_linebuf(); /* if we're not in a #if in the same file it's an error */ if (if_sp_ == 0) { log_error(TCERR_PP_ENDIF_WITHOUT_IF); return; } else if (if_sp_ <= str_->get_init_if_level()) { log_error(TCERR_PP_ENDIF_NOT_IN_SAME_FILE); return; } /* pop a #if level */ pop_if(); /* don't retain the directive in the preprocessed source */ clear_linebuf(); } /* ------------------------------------------------------------------------ */ /* * Process a #error directive */ void CTcTokenizer::pp_error() { size_t startofs; /* * copy the source line through the "error" token to the macro * expansion buffer - we don't want to expand that part, but we want * it to appear in the expansion, so just copy the original */ startofs = (curtok_.get_text() + curtok_.get_text_len() - linebuf_.get_text()); expbuf_.copy(linebuf_.get_text(), startofs); /* expand macros; don't allow reading additional lines */ if (expand_macros_curline(FALSE, FALSE, TRUE)) { clear_linebuf(); return; } /* clean up any expansion flags embedded in the buffer */ remove_expansion_flags(&expbuf_); /* * If we're in preprocess-only mode, simply retain the text in the * processed result, so that the error is processed on a subsequent * compilation of the result; otherwise, display the error. * * Ignore #error directives in list-includes mode as well. */ if (!pp_only_mode_ && !list_includes_mode_) { /* display the error */ log_error(TCERR_ERROR_DIRECTIVE, (int)expbuf_.get_text_len() - startofs, expbuf_.get_text() + startofs); /* clear the directive from the result */ clear_linebuf(); } else { /* preprocessing - copy expanded text to line buffer */ linebuf_.copy(expbuf_.get_text(), expbuf_.get_text_len()); } } /* ------------------------------------------------------------------------ */ /* * Process a #undef directive */ void CTcTokenizer::pp_undef() { char macro_name[TOK_SYM_MAX_BUFFER]; /* get the macro name */ if (pp_get_lone_ident(macro_name, sizeof(macro_name))) { clear_linebuf(); return; } /* remove it */ undefine(macro_name); /* don't retain the directive in the preprocessed source */ clear_linebuf(); } /* * Programmatically delete a preprocesor symbol */ void CTcTokenizer::undefine(const char *sym, size_t len) { CTcHashEntryPp *entry; /* * find the macro - if it wasn't defined, silently ignore it, since * it's legal to #undef a symbol that wasn't previously defined */ entry = find_define(sym, len); if (entry != 0 && entry->is_undefable()) { /* remove it */ defines_->remove(entry); /* if it's not already in the #undef table, move it there */ if (find_undef(sym, len) == 0) { /* move it to the #undef table */ undefs_->add(entry); } else { /* * the name is already in the #undef table, so we don't need to * add it again - we can forget about this entry entirely */ delete entry; } } } /* ------------------------------------------------------------------------ */ /* * Process a #line directive */ void CTcTokenizer::pp_line() { CTcConstVal val_line; CTcConstVal val_fname; CTcTokFileDesc *desc; /* expand macros; don't allow reading additional lines */ if (expand_macros_curline(FALSE, TRUE, FALSE)) { clear_linebuf(); return; } /* * we don't need the original source line any more, and we don't * want to copy it to the preprocessed output, so clear it */ clear_linebuf(); /* set up to parse from the expansion */ start_new_line(&expbuf_, 0); /* evaluate the line number expression */ if (pp_parse_expr(&val_line, TRUE, FALSE, TRUE)) return; /* if it's not an integer constant, it's an error */ if (val_line.get_type() != TC_CVT_INT) { log_error(TCERR_LINE_REQ_INT); return; } /* evaluate the filename expression */ if (pp_parse_expr(&val_fname, FALSE, TRUE, TRUE)) return; /* the filename must be a string expression */ if (val_fname.get_type() != TC_CVT_SSTR) { log_error(TCERR_LINE_FILE_REQ_STR); return; } /* find or create a descriptor for the filename */ desc = get_file_desc(val_fname.get_val_str(), val_fname.get_val_str_len(), FALSE, 0, 0); /* set the new line number and descriptor in the current stream */ if (str_ != 0) { str_->set_next_linenum(val_line.get_val_int()); str_->set_desc(desc); } /* * retain the pragma in the result if we're in preprocess-only mode, * otherwise remove it */ if (!pp_only_mode_) clear_linebuf(); } /* ------------------------------------------------------------------------ */ /* * Look up a symbol in the #define symbol table */ CTcHashEntryPp *CTcTokenizer::find_define(const char *sym, size_t len) const { /* look it up in the #define symbol table and return the result */ return (CTcHashEntryPp *)defines_->find(sym, len); } /* * Look up a symbol in the #undef table */ CTcHashEntryPp *CTcTokenizer::find_undef(const char *sym, size_t len) const { /* look it up in the #define symbol table and return the result */ return (CTcHashEntryPp *)undefs_->find(sym, len); } /* * Add a preprocessor macro definition */ void CTcTokenizer::add_define(const char *sym, size_t len, const char *expansion, size_t expan_len) { CTcHashEntryPp *entry; /* create an entry for the macro, with no argument list */ entry = new CTcHashEntryPpDefine(sym, len, TRUE, FALSE, 0, FALSE, 0, 0, expansion, expan_len); /* add the new entry to the table */ defines_->add(entry); } /* * Add a preprocessor macro definition */ void CTcTokenizer::add_define(CTcHashEntryPp *entry) { /* add the entry to our symbol table */ defines_->add(entry); } /* * parse an expression */ int CTcTokenizer::pp_parse_expr(CTcConstVal *val, int read_first, int last_on_line, int add_line_ending) { CTcPrsNode *expr_tree; char ch; /* add the line ending marker if required */ if (add_line_ending) { /* * append the special end-of-preprocess-line to the macro * expansion buffer */ ch = TOK_END_PP_LINE; expbuf_.append(&ch, 1); } /* * note that we're pasing a preprocessor expression; this affects * error logging in certain cases */ in_pp_expr_ = TRUE; /* * parse the expression in preprocessor mode, so that double-quoted * strings can be concatenated and compared */ G_prs->set_pp_expr_mode(TRUE); /* get the first token on the line if desired */ if (read_first) next(); /* parse the expression */ expr_tree = G_prs->parse_expr(); /* make sure we're at the end of the line if desired */ if (last_on_line && next() != TOKT_EOF) log_error(TCERR_PP_EXPR_EXTRA); /* if we added the special pp-line-ending marker, remove it */ if (add_line_ending) { /* * the marker is always the last character - remove it simply by * shortening the buffer by a character */ expbuf_.set_text_len(expbuf_.get_text_len() - 1); } /* return to normal expression mode */ G_prs->set_pp_expr_mode(FALSE); /* return to normal tokenizing mode */ in_pp_expr_ = FALSE; /* if we didn't get a valid expression, return failure */ if (expr_tree == 0) return 1; /* make sure we got a constant */ if (!expr_tree->is_const()) { log_error(TCERR_PP_EXPR_NOT_CONST); return 1; } /* fill in the caller's value */ *val = *expr_tree->get_const_val(); /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * #define enumeration callback context */ struct def_enum_cb_t { /* original callback function */ void (*cb)(void *, CTcHashEntryPp *); /* original callback context */ void *ctx; }; /* * #define enumeration callback. This is a simple impedence matcher on the * way to the real callbac; we cast the generic hash entry type to the * CTcHashEntryPp subclass for the benefit of the real callback. */ static void enum_defines_cb(void *ctx0, CVmHashEntry *entry) { def_enum_cb_t *ctx; /* get our real context */ ctx = (def_enum_cb_t *)ctx0; /* invoke the real callback, casting the entry reference appropriately */ (*ctx->cb)(ctx->ctx, (CTcHashEntryPp *)entry); } /* * Enumerate the entries in the #define table through a callback */ void CTcTokenizer::enum_defines(void (*cb)(void *, CTcHashEntryPp *), void *ctx) { def_enum_cb_t myctx; /* set up our impedence-matcher context with the real callback info */ myctx.cb = cb; myctx.ctx = ctx; /* enumerate through our impedence-matcher callback */ defines_->enum_entries(&enum_defines_cb, &myctx); } /* ------------------------------------------------------------------------ */ /* * Get a lone identifier for a preprocessor directive. The identifier * must be the only thing left on the line; we'll generate an error if * extra characters follow on the line. * * If there's no identifier on the line, or there's more information * after the identifier, logs an error and returns non-zero; returns * zero on success. */ int CTcTokenizer::pp_get_lone_ident(char *buf, size_t bufl) { /* get the next token, and make sure it's a symbol */ if (next_on_line() != TOKT_SYM) { log_error_curtok(TCERR_BAD_DEFINE_SYM); return 1; } /* return an error if it doesn't fit */ if (curtok_.get_text_len() > bufl) return 1; /* copy the text */ memcpy(buf, curtok_.get_text(), curtok_.get_text_len()); buf[curtok_.get_text_len()] = '\0'; /* make sure there's nothing else on the line but whitespace */ if (next_on_line() != TOKT_EOF) { log_error(TCERR_PP_EXTRA); return 1; } /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Push a new #if level */ void CTcTokenizer::push_if(tok_if_t state) { /* if we're out of space in the stack, throw a fatal error */ if (if_sp_ == TOK_MAX_IF_NESTING) throw_fatal_error(TCERR_IF_NESTING_OVERFLOW); /* * if we're in a nested #if in a false #if, increase the nested * false #if level */ if (in_false_if()) ++if_false_level_; /* push the state, remembering where the #if was defined */ if_stack_[if_sp_].desc = last_desc_; if_stack_[if_sp_].linenum = last_linenum_; if_stack_[if_sp_++].state = state; } /* * Pop a #if level */ void CTcTokenizer::pop_if() { /* if we're in a nested #if in a false #if, pop the nesting level */ if (if_false_level_ != 0) --if_false_level_; /* pop the main if level */ if (if_sp_ != 0) --if_sp_; } /* ------------------------------------------------------------------------ */ /* * Log an error */ void CTcTokenizer::log_error(int errnum, ...) { va_list marker; /* display the message */ va_start(marker, errnum); G_tcmain->v_log_error(G_tok->get_last_desc(), G_tok->get_last_linenum(), TC_SEV_ERROR, errnum, marker); va_end(marker); } /* * Log an error with the current token's text as the parameter data, * suitable for use with a "%.*s" display format entry */ void CTcTokenizer::log_error_curtok(int errnum) { /* * display the message, passing "%.*s" parameter data for the * current token text: an integer giving the length of the token * text, and a pointer to the token text */ log_error_or_warning_curtok(TC_SEV_ERROR, errnum); } /* * Log an error or warning for the current token */ void CTcTokenizer::log_error_or_warning_curtok(tc_severity_t sev, int errnum) { /* log the error with our current token */ log_error_or_warning_with_tok(sev, errnum, getcur()); } /* * Log an error or warning with the given token */ void CTcTokenizer::log_error_or_warning_with_tok( tc_severity_t sev, int errnum, const CTcToken *tok) { const char *tok_txt; size_t tok_len; char buf[128]; const char *prefix; const char *suffix; utf8_ptr src; utf8_ptr dst; size_t rem; size_t outchars; /* see what we have */ switch(tok->gettyp()) { case TOKT_SSTR: /* show the string in quotes, but limit the length */ prefix = "'"; suffix = "'"; goto format_string; case TOKT_DSTR: prefix = "\""; suffix = "\""; goto format_string; case TOKT_DSTR_START: prefix = "\""; suffix = "<<"; goto format_string; case TOKT_DSTR_MID: prefix = ">>"; suffix = "<<"; goto format_string; case TOKT_DSTR_END: prefix = ">>"; suffix = "\""; goto format_string; case TOKT_RESTR: prefix = "R'"; suffix = "'"; goto format_string; format_string: /* set the prefix */ strcpy(buf, prefix); /* * show the string, but limit the length, and convert control * characters to escaped representation */ src.set((char *)tok->get_text()); rem = tok->get_text_len(); for (dst.set(buf + strlen(buf)), outchars = 0 ; rem != 0 && outchars < 20 ; src.inc(&rem), ++outchars) { /* if this is a control character, escape it */ if (src.getch() < 32) { dst.setch('\\'); switch(src.getch()) { case 10: dst.setch('n'); break; case 13: dst.setch('r'); break; case 0x000F: dst.setch('^'); break; case 0x000E: dst.setch('v'); break; case 0x000B: dst.setch('b'); break; case 0x0015: dst.setch(' '); break; case 9: dst.setch('t'); break; default: dst.setch('x'); dst.setch('0' + int_to_xdigit((src.getch() >> 12) & 0xf)); dst.setch('0' + int_to_xdigit((src.getch() >> 8) & 0xf)); dst.setch('0' + int_to_xdigit((src.getch() >> 4) & 0xf)); dst.setch('0' + int_to_xdigit((src.getch()) & 0xf)); break; } } else { /* put this character as-is */ dst.setch(src.getch()); } } /* if there's more string left, add "..." */ if (rem != 0) { dst.setch('.'); dst.setch('.'); dst.setch('.'); } /* add the suffix */ strcpy(dst.getptr(), suffix); /* use this buffer as the token string to display */ tok_txt = buf; tok_len = strlen(tok_txt); break; case TOKT_EOF: /* show a special "" marker */ tok_txt = ""; tok_len = strlen(tok_txt); break; default: /* just show the current token text */ tok_txt = tok->get_text(); tok_len = tok->get_text_len(); break; } /* log the error */ G_tcmain->log_error(get_last_desc(), get_last_linenum(), sev, errnum, tok_len, tok_txt); } /* * Log a warning */ void CTcTokenizer::log_warning(int errnum, ...) { va_list marker; /* display the message */ va_start(marker, errnum); G_tcmain->v_log_error(G_tok->get_last_desc(), G_tok->get_last_linenum(), TC_SEV_WARNING, errnum, marker); va_end(marker); } /* * Log a pedantic warning */ void CTcTokenizer::log_pedantic(int errnum, ...) { va_list marker; /* display the message */ va_start(marker, errnum); G_tcmain->v_log_error(G_tok->get_last_desc(), G_tok->get_last_linenum(), TC_SEV_PEDANTIC, errnum, marker); va_end(marker); } /* * Log a warning with the current token's text as the parameter data, * suitable for use with a "%.*s" display format entry */ void CTcTokenizer::log_warning_curtok(int errnum) { /* * display the warning message, passing "%.*s" parameter data for * the current token text: an integer giving the length of the token * text, and a pointer to the token text */ log_error_or_warning_curtok(TC_SEV_WARNING, errnum); } /* * Log and throw an internal error */ void CTcTokenizer::throw_internal_error(int errnum, ...) { va_list marker; /* display the message */ va_start(marker, errnum); G_tcmain->v_log_error(G_tok->get_last_desc(), G_tok->get_last_linenum(), TC_SEV_INTERNAL, errnum, marker); va_end(marker); /* throw the generic internal error, since we've logged this */ err_throw(TCERR_INTERNAL_ERROR); } /* * Log and throw a fatal error */ void CTcTokenizer::throw_fatal_error(int errnum, ...) { va_list marker; /* display the message */ va_start(marker, errnum); G_tcmain->v_log_error(G_tok->get_last_desc(), G_tok->get_last_linenum(), TC_SEV_FATAL, errnum, marker); va_end(marker); /* throw the generic fatal error, since we've logged this */ err_throw(TCERR_FATAL_ERROR); } /* * display a string value */ void CTcTokenizer::msg_str(const char *str, size_t len) const { /* display the string through the host interface */ G_hostifc->print_msg("%.*s", (int)len, str); } /* * display a numeric value */ void CTcTokenizer::msg_long(long val) const { /* display the number through the host interface */ G_hostifc->print_msg("%ld", val); } /* ------------------------------------------------------------------------ */ /* * Tokenizer Input Stream implementation */ /* * create a token input stream */ CTcTokStream::CTcTokStream(CTcTokFileDesc *desc, CTcSrcObject *src, CTcTokStream *parent, int charset_error, int init_if_level) { /* remember the underlying source file */ src_ = src; /* remember the file descriptor */ desc_ = desc; /* remember the containing stream */ parent_ = parent; /* the next line to read is line number 1 */ next_linenum_ = 1; /* remember if there was a #charset error */ charset_error_ = charset_error; /* we're not in a comment yet */ in_comment_ = FALSE; /* remember the starting #if level */ init_if_level_ = init_if_level; #if 0 // #pragma C is not currently used /* * start out in parent's pragma C mode, or in non-C mode if we have * no parent */ if (parent != 0) pragma_c_ = parent->is_pragma_c(); else pragma_c_ = TRUE; #endif } /* * delete a token input stream */ CTcTokStream::~CTcTokStream() { /* we own the underlying file, so delete it */ if (src_ != 0) delete src_; } /* ------------------------------------------------------------------------ */ /* * File Descriptor */ /* * Get the length of a string with each instance of the given quote * character escaped with a backslash. We'll also count the escapes we * need for each backslash. */ static size_t get_quoted_len(const char *str, wchar_t qu) { utf8_ptr p; size_t len; /* * scan the string for instances of the quote mark; each one adds an * extra byte to the length needed, since each one requires a * backslash character to escape the quote mark */ for (p.set((char *)str), len = strlen(str) ; p.getch() != '\0' ; p.inc()) { wchar_t ch; /* * check to see if this character is quotable - it is quotable if * it's a backslash or it's the quote character we're escaping */ ch = p.getch(); if (ch == qu || ch == '\\') { /* * we need to escape this character, so add a byte for the * backslash we'll need to insert */ ++len; } } /* return the length we calculated */ return len; } /* * Build a quoted string. Fills in dst with the source string with each * of the given quote marks and each backslash escaped with a backslash. * Use get_quoted_len() to determine how much space to allocate for the * destination buffer. */ static void build_quoted_str(char *dstbuf, const char *src, wchar_t qu) { utf8_ptr p; utf8_ptr dst; /* scan the source string for escapable characters */ for (p.set((char *)src), dst.set(dstbuf), dst.setch(qu) ; p.getch() != '\0' ; p.inc()) { wchar_t ch; /* get this source character */ ch = p.getch(); /* add a quote if we have a backslash or the quote character */ if (ch == '\\' || ch == qu) { /* add a backslash to escape the character */ dst.setch('\\'); } /* add the character */ dst.setch(ch); } /* add the close quote and trailing null */ dst.setch(qu); dst.setch('\0'); } /* * create a file descriptor */ CTcTokFileDesc::CTcTokFileDesc(const char *fname, size_t fname_len, int index, CTcTokFileDesc *orig_desc, const char *orig_fname, size_t orig_fname_len) { const char *rootname; /* no source pages are allocated yet */ src_pages_ = 0; src_pages_alo_ = 0; /* remember the first instance of this filename in the list */ orig_ = orig_desc; /* there's nothing else in our chain yet */ next_ = 0; /* remember my index in the master list */ index_ = index; /* if there's a filename, save a copy of the name */ fname_ = lib_copy_str(fname, fname_len); /* if there's an original filename save it as well */ orig_fname_ = lib_copy_str(orig_fname, orig_fname_len); /* * get the root filename, since we need to build a quoted version of * that as well as of the basic filename */ rootname = os_get_root_name(fname_); /* * Allocate space for the quoted versions of the filename - make room * for the filename plus the quotes (one on each end) and a null * terminator byte. */ dquoted_fname_ = (char *)t3malloc(get_quoted_len(fname_, '"') + 3); squoted_fname_ = (char *)t3malloc(get_quoted_len(fname_, '\'') + 3); dquoted_rootname_ = (char *)t3malloc(get_quoted_len(rootname, '"') + 3); squoted_rootname_ = (char *)t3malloc(get_quoted_len(rootname, '\'') + 3); /* build the quoted version of the name */ build_quoted_str(dquoted_fname_, fname_, '"'); build_quoted_str(squoted_fname_, fname_, '\''); build_quoted_str(dquoted_rootname_, rootname, '"'); build_quoted_str(squoted_rootname_, rootname, '\''); } /* * delete the descriptor */ CTcTokFileDesc::~CTcTokFileDesc() { /* delete the filename and original filename strings */ lib_free_str(fname_); lib_free_str(orig_fname_); /* delete the quotable filename strings */ t3free(dquoted_fname_); t3free(squoted_fname_); t3free(dquoted_rootname_); t3free(squoted_rootname_); /* delete each source page we've allocated */ if (src_pages_ != 0) { size_t i; /* go through the index array and delete each allocated page */ for (i = 0 ; i < src_pages_alo_ ; ++i) { /* if this page was allocated, delete it */ if (src_pages_[i] != 0) t3free(src_pages_[i]); } /* delete the source page index array */ t3free(src_pages_); } } /* * Source page structure. Each page tracks a block of source lines. */ const size_t TCTOK_SRC_PAGE_CNT = 1024; struct CTcTokSrcPage { /* * Array of line entries on this page. Each entry is zero if it * hasn't been assigned yet, and contains the absolute image file * address of the generated code for the source line if it has been * assigned. */ ulong ofs[TCTOK_SRC_PAGE_CNT]; }; /* * Add a source line */ void CTcTokFileDesc::add_source_line(ulong linenum, ulong line_addr) { size_t page_idx; size_t idx; /* get the index of the page containing this source line */ page_idx = linenum / TCTOK_SRC_PAGE_CNT; /* get the index of the entry within the page */ idx = linenum % TCTOK_SRC_PAGE_CNT; /* * determine if our page index table is large enough, and expand it * if not */ if (page_idx >= src_pages_alo_) { size_t siz; size_t new_alo; /* allocate or expand the source pages array */ new_alo = page_idx + 16; siz = new_alo * sizeof(src_pages_[0]); if (src_pages_ == 0) src_pages_ = (CTcTokSrcPage **)t3malloc(siz); else src_pages_ = (CTcTokSrcPage **)t3realloc(src_pages_, siz); /* clear the new part */ memset(src_pages_ + src_pages_alo_, 0, (new_alo - src_pages_alo_) * sizeof(src_pages_[0])); /* remember the new allocation size */ src_pages_alo_ = new_alo; } /* if this page isn't allocated, do so now */ if (src_pages_[page_idx] == 0) { /* allocate the new page */ src_pages_[page_idx] = (CTcTokSrcPage *) t3malloc(sizeof(CTcTokSrcPage)); /* clear it */ memset(src_pages_[page_idx], 0, sizeof(CTcTokSrcPage)); } /* * if this source line entry has been previously set, don't change * it; otherwise, store the new setting */ if (src_pages_[page_idx]->ofs[idx] == 0) src_pages_[page_idx]->ofs[idx] = line_addr; } /* * Enumerate source lines */ void CTcTokFileDesc::enum_source_lines(void (*cbfunc)(void *, ulong, ulong), void *cbctx) { size_t page_idx; CTcTokSrcPage **pg; /* loop over all of the pages */ for (page_idx = 0, pg = src_pages_ ; page_idx < src_pages_alo_ ; ++page_idx, ++pg) { size_t i; ulong linenum; ulong *p; /* if this page is not populated, skip it */ if (*pg == 0) continue; /* calculate the starting line number for this page */ linenum = page_idx * TCTOK_SRC_PAGE_CNT; /* loop over the entries on this page */ for (i = 0, p = (*pg)->ofs ; i < TCTOK_SRC_PAGE_CNT ; ++i, ++p, ++linenum) { /* if this entry has been set, call the callback */ if (*p != 0) (*cbfunc)(cbctx, linenum, *p); } } } /* ------------------------------------------------------------------------ */ /* * #define symbol table hash entry */ /* * create an entry */ CTcHashEntryPpDefine::CTcHashEntryPpDefine(const textchar_t *str, size_t len, int copy, int has_args, int argc, int has_varargs, const char **argv, const size_t *argvlen, const char *expansion, size_t expan_len) : CTcHashEntryPp(str, len, copy) { /* copy the argument list if necessary */ has_args_ = has_args; has_varargs_ = has_varargs; argc_ = argc; if (argc != 0) { int i; /* allocate the argument list */ argv_ = (char **)t3malloc(argc * sizeof(*argv_)); /* allocate the parameters hash table */ params_table_ = new CVmHashTable(16, new CVmHashFuncCS(), TRUE); /* allocate the entry list */ arg_entry_ = (CTcHashEntryPpArg **) t3malloc(argc * sizeof(arg_entry_[0])); /* copy the arguments */ for (i = 0 ; i < argc ; ++i) { CTcHashEntryPpArg *entry; /* copy the argument name */ argv_[i] = lib_copy_str(argv[i], argvlen[i]); /* * Create the hash entries for this parameters. We'll use * this entry to look up tokens in the expansion text for * matches to the formal names when expanding the macro. * * Note that we'll refer directly to our local copy of the * argument name, so we don't need to make another copy in * the hash entry. */ entry = new CTcHashEntryPpArg(argv_[i], argvlen[i], FALSE, i); params_table_->add(entry); /* add it to our by-index list */ arg_entry_[i] = entry; } } else { /* no arguments */ argv_ = 0; params_table_ = 0; arg_entry_ = 0; } /* save the original version of the expansion */ orig_expan_ = lib_copy_str(expansion, expan_len); orig_expan_len_ = expan_len; /* parse the expansion, and save the parsed result */ parse_expansion(argvlen); } /* * Parse the expansion text. */ void CTcHashEntryPpDefine::parse_expansion(const size_t *argvlen) { /* * If there are arguments, scan the expansion for formal parameter * names. For each one we find, replace it with the special * TOK_MACRO_FORMAL_FLAG character followed by a one-byte value giving * the argument index. This special sequence is less costly to find * when we're expanding the macros - by doing the search here, we only * need to do it once, rather than each time we expand the macro. */ if (argc_ != 0) { utf8_ptr src; size_t dstofs; tc_toktyp_t typ; CTcToken tok; const char *start; tok_embed_ctx ec; size_t expan_len = orig_expan_len_; /* * Generate our modified expansion text in the tokenizer's macro * definition scratch buffer. Initially, make sure we have room * for a copy of the text; we'll resize the buffer later if we find * we need even more. */ CTcTokString *defbuf = &G_tok->defbuf_; defbuf->ensure_space(expan_len); /* scan for argument names, and replace them */ for (start = orig_expan_, dstofs = 0, src.set((char *)orig_expan_) ;; ) { /* get the next token */ typ = CTcTokenizer::next_on_line(&src, &tok, &ec, FALSE); /* if we've reached the end of the expansion, we're done */ if (typ == TOKT_EOF) break; /* * If this is a formal parameter name, we'll replace it with a * special two-byte sequence; otherwise, we'll keep it * unchanged. */ if (typ == TOKT_SYM) { /* look up the symbol in our argument table */ CTcHashEntryPpArg *entry = (CTcHashEntryPpArg *)params_table_->find( tok.get_text(), tok.get_text_len()); /* check if we found it */ if (entry != 0) { /* get the argument number from the entry */ int i = entry->get_argnum(); /* get the length of the formal name */ size_t arg_len = argvlen[i]; /* * the normal replacement length for a formal parameter * is two bytes - one byte for the flag, and one for * the formal parameter index */ size_t repl_len = 2; /* by default, the flag byte is the formal flag */ char flag_byte = TOK_MACRO_FORMAL_FLAG; /* * Check for special varargs control suffixes. If we * matched the last argument name, and this is a * varargs macro, we might have a suffix. */ if (has_varargs_ && i == argc_ - 1 && src.getch() == '#') { /* check for the various suffixes */ if (memcmp(src.getptr() + 1, "foreach", 7) == 0 && !is_sym(src.getch_at(8))) { /* * include the suffix length in the token * length */ arg_len += 8; /* * the flag byte is the #foreach flag, which is * a one-byte sequence */ flag_byte = TOK_MACRO_FOREACH_FLAG; repl_len = 1; } else if (memcmp(src.getptr() + 1, "argcount", 8) == 0 && !is_sym(src.getch_at(9))) { /* * include the suffix length in the token * length */ arg_len += 9; /* * the flag byte is the #argcount flag, which * is a one-byte sequence */ flag_byte = TOK_MACRO_ARGCOUNT_FLAG; repl_len = 1; } else if (memcmp(src.getptr() + 1, "ifempty", 7) == 0 && !is_sym(src.getch_at(8))) { /* include the length */ arg_len += 8; /* set the one-byte flag */ flag_byte = TOK_MACRO_IFEMPTY_FLAG; repl_len = 1; } else if (memcmp(src.getptr() + 1, "ifnempty", 8) == 0 && !is_sym(src.getch_at(9))) { /* include the length */ arg_len += 9; /* set the one-byte flag */ flag_byte = TOK_MACRO_IFNEMPTY_FLAG; repl_len = 1; } } /* * calculate the new length - we're removing the * argument name and adding the replacement string in * its place */ size_t new_len = expan_len + repl_len - arg_len; /* * we need two bytes for the replacement - if this is * more than we're replacing, make sure we have room * for the extra */ if (new_len > expan_len) defbuf->ensure_space(new_len); /* * copy everything up to but not including the formal * name */ if (tok.get_text() > start) { /* store the text */ memcpy(defbuf->get_buf() + dstofs, start, tok.get_text() - start); /* move past the stored text in the output */ dstofs += tok.get_text() - start; } /* the next segment starts after this token */ start = tok.get_text() + arg_len; /* store the flag byte */ defbuf->get_buf()[dstofs++] = flag_byte; /* * If appropriate, store the argument index - this * always fits in one byte because our hard limit on * formal parameters is less than 128 per macro. Note * that we add one to the index so that we never store * a zero byte, to avoid any potential confusion with a * null terminator byte. */ if (repl_len > 1) defbuf->get_buf()[dstofs++] = (char)(i + 1); /* remember the new length */ expan_len = new_len; } } } /* copy the last segment */ if (tok.get_text() > start) memcpy(defbuf->get_buf() + dstofs, start, tok.get_text() - start); /* set the new length */ defbuf->set_text_len(expan_len); /* save the parsed expansion */ expan_ = lib_copy_str(defbuf->get_text()); expan_len_ = expan_len; } else { /* * There are no arguments, so there's no parsing we need to do. * Just use the original as the parsed expansion. */ expan_ = orig_expan_; expan_len_ = orig_expan_len_; } } /* * delete */ CTcHashEntryPpDefine::~CTcHashEntryPpDefine() { int i; /* delete the argument list */ if (argv_ != 0) { /* delete each argument string */ for (i = 0 ; i < argc_ ; ++i) lib_free_str(argv_[i]); /* delete the argument vector */ t3free(argv_); /* delete the argument entry list */ t3free(arg_entry_); /* delete the hash table */ delete params_table_; } /* delete the expansion */ lib_free_str(expan_); if (orig_expan_ != expan_) lib_free_str(orig_expan_); } /* * __LINE__ static buffer */ char CTcHashEntryPpLINE::buf_[20]; /* ------------------------------------------------------------------------ */ /* * Load macro definitions from a file. */ int CTcTokenizer::load_macros_from_file(CVmStream *fp, CTcTokLoadMacErr *err_handler) { long cnt; long i; size_t curarg; char *argv[TOK_MAX_MACRO_ARGS]; size_t argvlen[TOK_MAX_MACRO_ARGS]; size_t maxarg; int result; char *expan; size_t expmaxlen; /* we haven't allocated any argument buffers yet */ maxarg = 0; /* allocate an initial expansion buffer */ expmaxlen = 1024; expan = (char *)t3malloc(expmaxlen); /* presume success */ result = 0; /* read the number of macros */ cnt = fp->read_uint4(); /* read each macro */ for (i = 0 ; i < cnt ; ++i) { char namebuf[TOK_SYM_MAX_LEN]; size_t namelen; int flags; size_t argc; size_t explen; CTcHashEntryPp *entry; int has_args; int has_varargs; /* read the name's length */ namelen = fp->read_uint2(); if (namelen > sizeof(namebuf)) { /* log an error through the handler */ err_handler->log_error(1); /* give up - we can't read any more of the file */ result = 1; goto done; } /* read the name */ fp->read_bytes(namebuf, namelen); /* read and decode the flags */ flags = fp->read_uint2(); has_args = ((flags & 1) != 0); has_varargs = ((flags & 2) != 0); /* read the number of arguments, and read each argument */ argc = fp->read_uint2(); for (curarg = 0 ; curarg < argc ; ++curarg) { /* read the length, and make sure it's valid */ argvlen[curarg] = fp->read_uint2(); if (argvlen[curarg] > TOK_SYM_MAX_LEN) { /* log an error */ err_handler->log_error(2); /* give up - we can't read any more of the file */ result = 2; goto done; } /* * if we haven't allocated a buffer for this argument slot yet, * allocate it now; allocate the buffer at the maximum symbol * size, so we can reuse the same buffer for an argument of * other macros we read later */ while (curarg >= maxarg) argv[maxarg++] = (char *)t3malloc(TOK_SYM_MAX_LEN); /* read the argument text */ fp->read_bytes(argv[curarg], argvlen[curarg]); } /* read the expansion size */ explen = (size_t)fp->read_uint4(); /* expand the expansion buffer if necessary */ if (explen > expmaxlen) { /* * overshoot a bit, so that we won't have to reallocate again * if we find a slightly larger expansion for a future macro */ expmaxlen = explen + 512; /* allocate the new buffer */ expan = (char *)t3realloc(expan, expmaxlen); } /* read the expansion */ fp->read_bytes(expan, explen); /* * Before we create the entry, check to see if there's an existing * entry with the same name. */ entry = find_define(namebuf, namelen); if (entry != 0) { /* * We have another entry. If the entry is exactly the same, * then we can simply skip the current entry, because we simply * want to keep one copy of each macro that's defined * identically in mutiple compilation macros. If the entry is * different from the new one, delete both - a macro which * appears in two or more compilation units with different * meanings is NOT a global macro, and thus we can't include it * in the debugging records. */ if (entry->is_pseudo() || entry->has_args() != has_args || entry->has_varargs() != has_varargs || entry->get_argc() != (int)argc || entry->get_orig_expan_len() != explen || memcmp(entry->get_orig_expansion(), expan, explen) != 0) { /* * The existing entry is different from the new entry, so * the macro has different meanings in different * compilation units, hence we cannot keep *either* * definition in the debug records. Delete the existing * macro, and do not create the new macro. If the existing * macro is a pseudo-macro, keep the old one (since it's * provided by the compiler itself), but still discard the * new one. */ if (!entry->is_pseudo()) undefine(namebuf, namelen); } else { /* * The new entry is identical to the old one, so keep it. * We only need one copy of the entry, though, so simply * keep the old one - there's no need to create a new entry * for the object file data. */ } } else { /* * There's no existing macro with the same name, so create a * new entry based on the object file data. */ entry = new CTcHashEntryPpDefine(namebuf, namelen, TRUE, has_args, argc, has_varargs, (const char **)argv, argvlen, expan, explen); /* add it to the preprocessor's macro symbol table */ add_define(entry); } } done: /* free the argument buffers we allocated */ for (curarg = 0 ; curarg < maxarg ; ++curarg) t3free(argv[curarg]); /* free the expansion buffer */ t3free(expan); /* success */ return result; } /* ------------------------------------------------------------------------ */ /* * Callback context for writing enumerated #define symbols to a file */ struct write_macro_ctx_t { /* object file we're writing to */ CVmFile *fp; /* number of symbols written so far */ unsigned long cnt; }; /* * Enumeration callback for writing the #define symbols to a file */ static void write_macros_cb(void *ctx0, CTcHashEntryPp *entry) { write_macro_ctx_t *ctx = (write_macro_ctx_t *)ctx0; int flags; int i; CVmFile *fp = ctx->fp; /* * if this is a pseudo-macro (such as __LINE__ or __FILE__), ignore it * - these macros do not have permanent global definitions, so they're * not usable in the debugger */ if (entry->is_pseudo()) return; /* * If the macro was ever redefined or undefined, ignore it - the * debugger can only use truly global macros, which are macros that * have stable meanings throughout the compilation units where they * appear (and which do not have different meanings in different * compilation units, but that's not our concern at the moment). The * preprocessor keeps an "undef" table of everything undefined * (explicitly, or implicitly via redefinition), so look up this macro * in the undef table, and ignore the macro if it we find it. */ if (G_tok->find_undef(entry->getstr(), entry->getlen()) != 0) return; /* count this macro */ ctx->cnt++; /* write the macro's name */ fp->write_uint2(entry->getlen()); fp->write_bytes(entry->getstr(), entry->getlen()); /* write the flag bits */ flags = 0; if (entry->has_args()) flags |= 1; if (entry->has_varargs()) flags |= 2; fp->write_uint2(flags); /* write the number of arguments, and write each argument */ fp->write_uint2(entry->get_argc()); for (i = 0 ; i < entry->get_argc() ; ++i) { CTcHashEntryPpArg *arg; /* get the argument */ arg = entry->get_arg_entry(i); /* write the parameter name */ fp->write_uint2(arg->getlen()); fp->write_bytes(arg->getstr(), arg->getlen()); } /* write the expansion */ fp->write_uint4(entry->get_orig_expan_len()); fp->write_bytes(entry->get_orig_expansion(), entry->get_orig_expan_len()); } /* * Write all #define symbols to a file, for debugging purposes. Writes * only symbols that have never been undefined or redefined, since the * debugger can only make use of global symbols (i.e., symbols with * consistent meanings through all compilation units in which they * appear). */ void CTcTokenizer::write_macros_to_file_for_debug(CVmFile *fp) { long pos; long endpos; write_macro_ctx_t ctx; /* write a placeholder for the symbol count */ pos = fp->get_pos(); fp->write_uint4(0); /* write the symbols */ ctx.fp = fp; ctx.cnt = 0; enum_defines(&write_macros_cb, &ctx); /* go back and fix up the symbol count */ endpos = fp->get_pos(); fp->set_pos(pos); fp->write_uint4(ctx.cnt); /* seek back to where we left off */ fp->set_pos(endpos); } qtads-2.1.7/tads3/tctok.h000066400000000000000000002451211265017072300151600ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/tctok.h,v 1.5 1999/07/11 00:46:59 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tctok.h - TADS3 compiler tokenizer and preprocessor Function Notes The tokenizer is layered with the preprocessor, so that the preprocessor can deal with include files, macro expansion, and preprocessor directives. Modified 04/12/99 MJRoberts - Creation */ #ifndef TCTOK_H #define TCTOK_H #include #include #include #include "os.h" #include "t3std.h" #include "utf8.h" #include "vmhash.h" #include "vmerr.h" #include "tcerr.h" #include "tcerrnum.h" /* ------------------------------------------------------------------------ */ /* * Constants */ /* maximum length of a symbol name, in characters */ const size_t TOK_SYM_MAX_LEN = 80; /* * Maximum buffer required to hold a symbol, in bytes. Each UTF-8 * character may take up three bytes, plus we need a null terminator * byte. */ const size_t TOK_SYM_MAX_BUFFER = (3*TOK_SYM_MAX_LEN + 1); /* maximum #if nesting level */ const size_t TOK_MAX_IF_NESTING = 100; /* maximum number of parameters per macro */ const int TOK_MAX_MACRO_ARGS = 128; /* * Special token flag characters - these are a characters that can't * occur in an input file (we guarantee this by converting any * occurrences of this character to a space on reading input). We use * these to flag certain special properties of tokens in the input * buffer. * * We use ASCII characters in the control range (0x01 (^A) through 0x1A * (^Z), excluding 0x09 (tab), 0x0A (LF), 0x0D (CR), and 0x0C (Page * Feed); a well-formed source file would never use any of these * characters in input. Even if it does, we won't get confused, since * we'll always translate these to a space if we find them in input; but * choosing characters that *should* never occur in valid input will * ensure that we never alter the meaning of valid source by this * translation. */ /* * macro parameter flag - we use this in the internal storage of a * #define expansion to flag where the formal parameters are mentioned, * so that we can substitute the actuals when expanding the macro */ const char TOK_MACRO_FORMAL_FLAG = 0x01; /* * Token fully expanded flag. Whenever we detect that a particular * token has been fully expanded in the course of a particular macro * expansion, we'll insert this byte before the token; on subsequent * re-scans, whenever we see this flag, we'll realize that the token * needs no further consideration of expansion. */ const char TOK_FULLY_EXPANDED_FLAG = 0x02; /* * Macro substitution end marker. Each time we expand a macro, we'll * insert immediately after the macro expansion a special pseudo-token, * consisting of this flag followed by a pointer to the symbol table * entry for the symbol expanded. As we expand macros, we'll check to * see if any of these special flags appear in the buffer after the * macro about to be expanded. If we find such a flag matching the * symbol about to be expanded, we'll know the symbol has already been * fully expanded on a previous scan and thus must not be expanded * again. */ const char TOK_MACRO_EXP_END = 0x03; /* * End-of-line flag. This serves as a local end-of-file marker for * preprocessor lines. Because preprocessor lines must be considered in * isolation, we need some way when parsing one to tell the tokenizer * not to try to read another line when it reaches the end of the * current line. This flag serves this purpose: when the tokenizer * encounters one of these flags, it will simply return end-of-file * until the caller explicitly reads a new source line. */ const char TOK_END_PP_LINE = 0x04; /* * "#foreach" marker flag. This marks the presence of a #foreach token in * a macro's expansion. We leave the text of the expansion area intact, * but we replace the #foreach token with this marker character. */ const char TOK_MACRO_FOREACH_FLAG = 0x05; /* * "#argcount" marker flag. This marks the presence of a #argcount token * in a macro's expansion. */ const char TOK_MACRO_ARGCOUNT_FLAG = 0x06; /* * "#ifempty" and #ifnempty" marker flags */ const char TOK_MACRO_IFEMPTY_FLAG = 0x07; const char TOK_MACRO_IFNEMPTY_FLAG = 0x08; /* * Macro format version number. The compiler sets up a predefined macro * (__TADS_MACRO_FORMAT_VERSION) with this information. Since 3.1, the * macro table is visible to user code via t3GetGlobalSymbols() (using the * T3PreprocMacros selector), and this information includes the parsed * format with the embedded flag codes. The macro information can be used * in DynamicFunc compilation at run-time. If we ever make incompatible * changes to the internal format, future interpreters will have to * recognize older versions so that they can make the necessary * translations. By embedding the version information in the table, we * make this recognition possible. */ #define TCTOK_MACRO_FORMAT_VERSION 1 /* ------------------------------------------------------------------------ */ /* * Macro table. This is a virtualized version of our basic hash table, to * allow specialized versions that provide views on top of other table * structures. */ class CTcMacroTable { public: virtual ~CTcMacroTable() { } /* add an entry */ virtual void add(CVmHashEntry *entry) = 0; /* remove an entry */ virtual void remove(CVmHashEntry *entry) = 0; /* find an entry */ virtual class CVmHashEntry *find(const char *str, size_t len) = 0; /* enumerate entries */ virtual void enum_entries( void (*func)(void *ctx, class CVmHashEntry *entry), void *ctx) = 0; /* dump the hash table for debugging purposes */ virtual void debug_dump() = 0; }; /* ------------------------------------------------------------------------ */ /* * #if state */ enum tok_if_t { TOKIF_NONE, /* not in a #if block at all */ TOKIF_IF_YES, /* processing a true #if branch */ TOKIF_IF_NO, /* processing a false #if branch */ TOKIF_IF_DONE, /* done with true #if/#elif; skip #elif's and #else */ TOKIF_ELSE_YES, /* processing a true #else branch */ TOKIF_ELSE_NO /* processing a false #else branch */ }; /* * #if stack entry */ struct tok_if_info_t { /* state */ tok_if_t state; /* file descriptor and line number of starting #if */ class CTcTokFileDesc *desc; long linenum; }; /* ------------------------------------------------------------------------ */ /* * Token Types */ enum tc_toktyp_t { TOKT_INVALID, /* invalid token */ TOKT_NULLTOK, /* null token - caller should read another token */ TOKT_EOF, /* end of file */ TOKT_MACRO_FORMAL, /* formal parameter replacement placeholder */ TOKT_MACRO_FOREACH, /* macro varargs #foreach placeholder */ TOKT_MACRO_ARGCOUNT, /* macro varargs #argcount placeholder */ TOKT_MACRO_IFEMPTY, /* #ifempty macro placeholder */ TOKT_MACRO_IFNEMPTY, /* #ifnempty macro placeholder */ TOKT_SYM, /* symbolic name */ TOKT_INT, /* integer */ TOKT_SSTR, /* single-quoted string */ TOKT_SSTR_START, /* start of an sstring with embedding - '...<< */ TOKT_SSTR_MID, /* middle of an sstring with embedding - >>...<< */ TOKT_SSTR_END, /* end of an sstring with embedding - >>...' */ TOKT_DSTR, /* double-quoted string */ TOKT_DSTR_START, /* start of a dstring with embedding - "...<< */ TOKT_DSTR_MID, /* middle of a dstring with embedding - >>...<< */ TOKT_DSTR_END, /* end of a dstring with embedding - >>..." */ TOKT_RESTR, /* regular expression string - R'...' or R"..." */ TOKT_LPAR, /* left paren '(' */ TOKT_RPAR, /* right paren ')' */ TOKT_COMMA, /* comma ',' */ TOKT_DOT, /* period '.' */ TOKT_LBRACE, /* left brace '{' */ TOKT_RBRACE, /* right brace '}' */ TOKT_LBRACK, /* left square bracket '[' */ TOKT_RBRACK, /* right square bracket ']' */ TOKT_EQ, /* equals sign '=' */ TOKT_EQEQ, /* double-equals sign '==' */ TOKT_ASI, /* colon-equals assignment operator ':=' */ TOKT_PLUS, /* plus sign '+' */ TOKT_MINUS, /* minus sign '-' */ TOKT_TIMES, /* multiplication symbol '*' */ TOKT_DIV, /* division symbol '/' */ TOKT_MOD, /* modulo '%' */ TOKT_GT, /* greater-than sign '>' */ TOKT_LT, /* less-than sign '<' */ TOKT_GE, /* greater-or-equal sign '>=' */ TOKT_LE, /* less-or-equal sign '<=' */ TOKT_NE, /* not-equals sign '!=' or '<>' */ TOKT_ARROW, /* arrow symbol '->' */ TOKT_COLON, /* colon ':' */ TOKT_SEM, /* semicolon ';' */ TOKT_AND, /* bitwise AND '&' */ TOKT_ANDAND, /* logical AND '&&' */ TOKT_OR, /* bitwise OR '|' */ TOKT_OROR, /* logical OR '||' */ TOKT_XOR, /* bitwise XOR '^' */ TOKT_SHL, /* shift left '<<' */ TOKT_ASHR, /* arithmetic shift right '>>' */ TOKT_LSHR, /* logical shift right '>>>' */ TOKT_INC, /* increment '++' */ TOKT_DEC, /* decrement '--' */ TOKT_PLUSEQ, /* plus-equals '+=' */ TOKT_MINEQ, /* minus-equals '-=' */ TOKT_TIMESEQ, /* times-equals '*=' */ TOKT_DIVEQ, /* divide-equals '/=' */ TOKT_MODEQ, /* mod-equals '%=' */ TOKT_ANDEQ, /* and-equals '&=' */ TOKT_OREQ, /* or-equals '|=' */ TOKT_XOREQ, /* xor-equals '^=' */ TOKT_SHLEQ, /* shift-left-and-assign '<<=' */ TOKT_ASHREQ, /* arithmetic shift-right-and-assign '>>=' */ TOKT_LSHREQ, /* logical shift-right-and-assign '>>>=' */ TOKT_NOT, /* logical not '!' */ TOKT_BNOT, /* bitwise not '~' */ TOKT_POUND, /* pound '#' */ TOKT_POUNDPOUND, /* double-pound '##' */ TOKT_POUNDAT, /* pound-at '#@' */ TOKT_ELLIPSIS, /* ellipsis '...' */ TOKT_QUESTION, /* question mark '?' */ TOKT_QQ, /* double question mark '??' */ TOKT_COLONCOLON, /* double-colon '::' */ TOKT_FLOAT, /* floating-point number */ TOKT_BIGINT, /* an integer promoted to a float due to overflow */ TOKT_AT, /* at-sign */ TOKT_DOTDOT, /* range marker '..' */ TOKT_FMTSPEC, /* sprintf format spec for <<%fmt expr>> */ /* keywords */ TOKT_SELF, TOKT_INHERITED, TOKT_ARGCOUNT, TOKT_IF, TOKT_ELSE, TOKT_FOR, TOKT_WHILE, TOKT_DO, TOKT_SWITCH, TOKT_CASE, TOKT_DEFAULT, TOKT_GOTO, TOKT_BREAK, TOKT_CONTINUE, TOKT_FUNCTION, TOKT_RETURN, TOKT_LOCAL, TOKT_OBJECT, TOKT_NIL, TOKT_TRUE, TOKT_PASS, TOKT_EXTERNAL, TOKT_EXTERN, TOKT_FORMATSTRING, TOKT_CLASS, TOKT_REPLACE, TOKT_MODIFY, TOKT_NEW, TOKT_DELETE, TOKT_THROW, TOKT_TRY, TOKT_CATCH, TOKT_FINALLY, TOKT_INTRINSIC, TOKT_DICTIONARY, TOKT_GRAMMAR, TOKT_ENUM, TOKT_TEMPLATE, TOKT_STATIC, TOKT_FOREACH, TOKT_EXPORT, TOKT_DELEGATED, TOKT_TARGETPROP, TOKT_PROPERTYSET, TOKT_TARGETOBJ, TOKT_DEFININGOBJ, TOKT_TRANSIENT, TOKT_REPLACED, TOKT_PROPERTY, TOKT_OPERATOR, TOKT_METHOD, TOKT_INVOKEE /* type names - formerly reserved but later withdrawn */ // TOKT_VOID, // TOKT_INTKW, // TOKT_STRING, // TOKT_LIST, // TOKT_BOOLEAN, // TOKT_ANY }; /* ------------------------------------------------------------------------ */ /* * Source Block. As we read the source file, we need to keep quoted * strings and symbol names around for later reference, in case they're * needed after reading more tokens and flushing the line buffer. We'll * copy needed text into our source blocks, which we keep in memory * throughout the compilation, so that we can be certain we can * reference these strings at any time. */ /* size of a source block */ const size_t TCTOK_SRC_BLOCK_SIZE = 50000; /* source block class */ class CTcTokSrcBlock { public: CTcTokSrcBlock() { /* no next block yet */ nxt_ = 0; } ~CTcTokSrcBlock() { /* delete the next block in line */ if (nxt_ != 0) delete nxt_; } /* get/set the next block */ CTcTokSrcBlock *get_next() const { return nxt_; } void set_next(CTcTokSrcBlock *blk) { nxt_ = blk; } /* get a pointer to the block's buffer */ char *get_buf() { return buf_; } private: /* the next block in the list */ CTcTokSrcBlock *nxt_; /* bytes of the list entry */ char buf_[TCTOK_SRC_BLOCK_SIZE]; }; /* ------------------------------------------------------------------------ */ /* * String Buffer. We use these buffers for reading input lines and * expanding macros. */ class CTcTokString { public: CTcTokString() { /* no buffer yet */ buf_ = 0; buf_len_ = 0; buf_size_ = 0; } virtual ~CTcTokString() { /* delete our buffer */ if (buf_ != 0) t3free(buf_); } /* ensure that a given amount of space if available */ virtual void ensure_space(size_t siz) { /* make sure there's room for the requested size plus a null byte */ if (buf_size_ < siz + 1) { /* increase to the next 4k increment */ buf_size_ = (siz + 4095 + 1) & ~4095; /* allocate or re-allocate the buffer */ if (buf_ == 0) buf_ = (char *)t3malloc(buf_size_); else buf_ = (char *)t3realloc(buf_, buf_size_); /* throw an error if that failed */ if (buf_ == 0) err_throw(TCERR_NO_STRBUF_MEM); } } /* expand the buffer */ void expand() { /* expand to the next 4k increment */ ensure_space(buf_size_ + 4096); } /* get the text and the length of the text */ const char *get_text() const { return buf_; } size_t get_text_len() const { return buf_len_; } /* get the end of the text */ const char *get_text_end() const { return buf_ + buf_len_; } /* append text to the buffer */ virtual void append(const char *p) { append(p, strlen(p)); } virtual void append(const char *p, size_t len) { /* make sure we have space available */ ensure_space(buf_len_ + len); /* copy the text onto the end of our buffer */ memcpy(buf_ + buf_len_, p, len); /* add it to the length of the text */ buf_len_ += len; /* null-terminte it */ buf_[buf_len_] = '\0'; } /* prepend text */ virtual void prepend(const char *p) { prepend(p, strlen(p)); } virtual void prepend(const char *p, size_t len) { /* make sure we have enough space */ ensure_space(buf_len_ + len); /* * move the existing text (including the null terminator) up in the * buffer to make room for the prepended text */ memmove(buf_ + len, buf_, buf_len_ + 1); /* copy the new text to the start of the buffer */ memcpy(buf_, p, len); /* count the new size */ buf_len_ += len; } /* * Append a string to the buffer, enclosing the text in single or * double quote (as given by 'qu', which must be either '"' or '\'') * and backslash-escaping any occurrences of the same quote character * found within the string. */ void append_qu(char qu, const char *p) { append_qu(qu, p, strlen(p)); } void append_qu(char qu, const char *p, size_t len) { const char *start; /* append the open quote */ append(&qu, 1); /* scan for quotes we'll need to escape */ while (len != 0) { size_t rem; /* skip to the next quote */ for (start = p, rem = len ; rem != 0 && *p != qu ; ++p, --rem) ; /* insert the chunk up to the quote */ if (p != start) append(start, p - start); /* if we did find a quote, append it with a backslash escape */ if (rem != 0) { /* append the backslash and the quote */ append("\\", 1); append(&qu, 1); /* skip the quote in the source */ ++p; --rem; } /* we now only have 'rem' left to consider */ len = rem; } /* finally, append the closing quote */ append(&qu, 1); } /* insert text into the buffer at the given offset */ virtual void insert(int ofs, const char *p, size_t len) { /* check to see if there's anything after the insertion point */ if ((size_t)ofs >= buf_len_) { /* * there's nothing after the insertion point, so this is simply * equivalent to 'append' - go do the append, and we're done */ append(p, len); return; } /* ensure there's space for the added text */ ensure_space(buf_len_ + len); /* * Move the existing text after the insertion point just far enough * to make room for the new text. Include the null terminator. */ memmove(buf_ + ofs + len, buf_ + ofs, buf_len_ - ofs + 1); /* copy the new text in at the given offset */ memcpy(buf_ + ofs, p, len); /* include the new text in our length */ buf_len_ += len; } /* copy text into the buffer, replacing existing text */ virtual void copy(const char *p, size_t len) { /* ensure we have enough space */ ensure_space(len); /* copy the text */ memcpy(buf_, p, len); /* set our length */ buf_len_ = len; /* null-terminate it */ buf_[buf_len_] = '\0'; } /* clear any existing text */ virtual void clear_text() { /* zero the length */ buf_len_ = 0; /* put a null terminator at the start of the buffer if possible */ if (buf_size_ > 0) buf_[0] = '\0'; } /* get the buffer, for copying text directly into it */ virtual char *get_buf() const { return buf_; } size_t get_buf_size() const { return buf_size_; } /* * Set the text length - use this after copying directly into the * buffer to set the length, excluding the null terminator. We'll * add a null terminator at the given length. */ virtual void set_text_len(size_t len) { /* set the new length */ buf_len_ = len; /* add a null terminator after the new length */ if (len < buf_size_) buf_[len] = '\0'; } protected: /* buffer */ char *buf_; /* size of the buffer */ size_t buf_size_; /* length of the text in the buffer (excluding trailing null) */ size_t buf_len_; }; /* * String buffer subclass for a non-allocated string that merely * references another buffer. This can be used anywhere a CTcString is * required, but does not require any allocation. * * These objects can only be used in 'const' contexts: the underlying * buffer cannot be changed or expanded, since we do not own the * underlying buffer. */ class CTcTokStringRef: public CTcTokString { public: CTcTokStringRef() { /* we have no referenced buffer yet */ buf_ = 0; buf_size_ = 0; buf_len_ = 0; } ~CTcTokStringRef() { /* we don't own the underlying buffer, so simply forget about it */ buf_ = 0; } /* we can't make any changes to the underlying buffer */ void ensure_space(size_t) { } void append(const char *) { assert(FALSE); } void append(const char *, size_t) { assert(FALSE); } void prepend(const char *) { assert(FALSE); } void prepend(const char *, size_t) { assert(FALSE); } void insert(int, const char *, size_t) { assert(FALSE); } void copy(const char *, size_t) { assert(FALSE); } void clear_text() { assert(FALSE); } char *get_buf() const { assert(FALSE); return 0; } void set_text_len(size_t) { assert(FALSE); } /* set my underlying buffer */ void set_buffer(const char *buf, size_t len) { buf_ = (char *)buf; buf_size_ = len + 1; buf_len_ = len; } }; /* ------------------------------------------------------------------------ */ /* * String embedding context. This keeps track of the token structure for * embedded expressions within strings using << >>. */ struct tok_embed_level { /* parenthesis depth within the expression */ int parens; /* token type to switch back to on ending the string */ tc_toktyp_t endtok; /* the quote character for the enclosing string */ wchar_t qu; /* true -> the enclosing string is a triple-quoted string */ int triple; void enter(wchar_t qu, int triple) { this->parens = 0; this->endtok = (qu == '"' ? TOKT_DSTR_END : TOKT_SSTR_END); this->qu = qu; this->triple = triple; } }; struct tok_embed_ctx { tok_embed_ctx() { reset(); } void reset() { level = 0; s = 0; } void start_expr(wchar_t qu, int triple, int report); void end_expr() { if (level > 0) --level; if (level == 0) s = 0; else if (level < countof(stk)) s = stk + level - 1; } /* are we in an embedded expression? */ int in_expr() const { return level != 0; } /* paren nesting at current level */ int parens() const { return in_expr() ? s->parens : 0; } /* inc/dec paren nesting level */ void parens(int inc) { if (in_expr()) { if ((s->parens += inc) < 0) s->parens = 0; } } /* ending token type at current level */ tc_toktyp_t endtok() const { return in_expr() ? s->endtok : TOKT_INVALID; } /* ending quote at current level */ wchar_t qu() const { return in_expr() ? s->qu : 0; } /* ending quote is triple quote at current level */ int triple() const { return in_expr() ? s->triple : FALSE; } /* nesting level */ int level; /* stack pointer */ tok_embed_level *s; /* stack */ tok_embed_level stk[10]; }; /* ------------------------------------------------------------------------ */ /* * Token */ class CTcToken { public: CTcToken() { } CTcToken(tc_toktyp_t typ) : typ_(typ) { } /* get/set the token type */ tc_toktyp_t gettyp() const { return typ_; } void settyp(tc_toktyp_t typ) { typ_ = typ; } /* get/set the fully-expanded flag */ int get_fully_expanded() const { return fully_expanded_; } void set_fully_expanded(int flag) { fully_expanded_ = flag; } /* get/set the text pointer */ const char *get_text() const { return text_; } size_t get_text_len() const { return text_len_; } void set_text(const char *txt, size_t len) { text_ = txt; text_len_ = len; } /* get/set the integer value */ ulong get_int_val() const { return int_val_; } void set_int_val(ulong val) { typ_ = TOKT_INT; int_val_ = val; } /* * compare the text to the given string - returns true if the text * matches, false if not */ int text_matches(const char *txt, size_t len) const { return (len == text_len_ && memcmp(txt, text_, len) == 0); } int text_matches(const char *txt) const { return text_matches(txt, txt != 0 ? strlen(txt) : 0); } /* copy from a another token */ void set(const CTcToken &tok) { typ_ = tok.typ_; text_ = tok.text_; text_len_ = tok.text_len_; int_val_ = tok.int_val_; fully_expanded_ = tok.fully_expanded_; } private: /* token type */ tc_toktyp_t typ_; /* * Pointer to the token's text. This is a pointer into the * tokenizer's symbol table or into the token list itself, so this * pointer is valid as long as the tokenizer and its token list are * valid. */ const char *text_; size_t text_len_; /* integer value - valid when the token type is TOKT_INT */ ulong int_val_; /* * flag: the token has been fully expanded, and should not be * expanded further on any subsequent rescan for macros */ uint fully_expanded_ : 1; }; /* * Token list entry. This is a generic linked list element containing a * token. */ class CTcTokenEle: public CTcToken { public: CTcTokenEle() { nxt_ = prv_ = 0; } /* get/set the next element */ CTcTokenEle *getnxt() const { return nxt_; } void setnxt(CTcTokenEle *nxt) { nxt_ = nxt; } /* get/set the previous element */ CTcTokenEle *getprv() const { return prv_; } void setprv(CTcTokenEle *prv) { prv_ = prv; } protected: /* next/previous token in list */ CTcTokenEle *nxt_; CTcTokenEle *prv_; }; /* ------------------------------------------------------------------------ */ /* * Macro Expansion Resource object. This object is a collection of * resources that are needed for a macro expansion. To avoid frequent * allocating and freeing of these resources, we keep a pool of these * objects around so that we can re-use them as needed. We'll * dynamically expand the pool as necessary, so this doesn't impose any * pre-set limits; it simply avoids lots of memory allocation activity. */ class CTcMacroRsc { public: CTcMacroRsc() { /* we're not in any lists yet */ next_avail_ = 0; next_ = 0; } /* buffer for expansion of the whole line */ CTcTokString line_exp_; /* buffer for expansion of current macro on line */ CTcTokString macro_exp_; /* buffer for expansion of an actual parameter value */ CTcTokString actual_exp_buf_; /* next resource object in the "available" list */ CTcMacroRsc *next_avail_; /* next resource object in the master list */ CTcMacroRsc *next_; }; /* ------------------------------------------------------------------------ */ /* * Abstract token source interface. This is used to allow external code * to inject their own substreams into the main token stream. */ class CTcTokenSource { public: virtual ~CTcTokenSource() { } /* * Get the next token from the source. Returns null if there are no * more tokens. */ virtual const CTcToken *get_next_token() = 0; /* set the enclosing external token source and current token */ void set_enclosing_source(CTcTokenSource *src, const CTcToken *tok) { /* remember the enclosing source */ enclosing_src_ = src; /* remember the current token */ enclosing_curtok_ = *tok; } /* get the enclosing external token source */ CTcTokenSource *get_enclosing_source() const { return enclosing_src_; } /* get the token that was current when this source was inserted */ const CTcToken *get_enclosing_curtok() const { return &enclosing_curtok_; } protected: /* the enclosing external token source */ CTcTokenSource *enclosing_src_; /* * the current token in effect enclosing this source - this is the * token that comes immediately after the source's tokens, because a * source is inserted before the current token */ CTcToken enclosing_curtok_; }; /* ------------------------------------------------------------------------ */ /* * Newline spacing modes. The newline spacing mode controls how line * breaks are handled within strings. */ enum newline_spacing_mode_t { /* delete: a newline and immediately following whitespace are deleted */ NEWLINE_SPACING_DELETE = 0, /* * collapse: a newline and immediately following whitespace are * replaced with a single space character */ NEWLINE_SPACING_COLLAPSE = 1, /* * preserve: newlines and subsequent whitespace are preserved exactly * as written in the source code */ NEWLINE_SPACING_PRESERVE = 2 }; /* ------------------------------------------------------------------------ */ /* * Tokenizer. This object reads a file and constructs a representation * of the file as a token list in memory. The tokenizer interprets * preprocessor directives and expands macros. */ class CTcTokenizer { friend class CTcHashEntryPpDefine; public: /* * Create the tokenizer and start reading from the given file. The * default character set is generally specified by the user (on the * compiler command line, for example), or obtained from the * operating system. */ CTcTokenizer(class CResLoader *res_loader, const char *default_charset); /* destroy the tokenizer */ ~CTcTokenizer(); /* * Reset the tokenizer. Deletes the current source object and all * saved token text. This can be used after compilation of a unit * is completed and the intermediate parser state can be completely * discarded. */ void reset(); /* * Set the source file. 'src_filename' is the fully-resolved local * filename of the source file; 'orig_name' is the original name as * given on the command line, in the makefile, or wherever it came * from. We keep track of the original name so that we can pass * information to the debugger indicating the name as it was originally * given; this is more useful than the resolved filename, because we * might want to run the debugger on another machine with a different * local directory structure. */ int set_source(const char *src_filename, const char *orig_name); /* set the source to a memory buffer */ void set_source_buf(const char *buf) { set_source_buf(buf, buf != 0 ? strlen(buf) : 0); } void set_source_buf(const char *buf, size_t len); /* * Add a #include directory to the include path. We search the * include path in the order in which they were defined. */ void add_inc_path(const char *path); /* * Set preprocess-only mode. In this mode, we'll retain * preprocessor directives that will be needed if the preprocessed * result is itself compiled; for example, we'll retain #line, * #pragma C, #error, and #pragma message directives. */ void set_mode_pp_only(int flag) { pp_only_mode_ = flag; } /* * Set list-includes mode. In this mode, we'll simply scan source * files and write to the standard output a list of the names of all * of the #include files. */ void set_list_includes_mode(int flag) { list_includes_mode_ = flag; } /* * Get/set the test-report mode. In this mode, we'll expand __FILE__ * macros with the root name only. */ int get_test_report_mode() const { return test_report_mode_; } void set_test_report_mode(int flag) { test_report_mode_ = flag; } /* enable or disable preprocessing directives */ void enable_pp(int enable) { allow_pp_ = enable; } /* get the type of the current token */ tc_toktyp_t cur() const { return curtok_.gettyp(); } /* get the next token, reading a new line of source if necessary */ tc_toktyp_t next(); /* * Un-get the current token and back up to the previous token. The * previous token becomes the current token, and the current token is * saved on an internal stack to be fetched again after the returned * token. * * With no arguments, this backs up to the internally stored previous * token. This allows callers to look ahead by one token without * making a list of saved tokens. * * With an argument, this backs up to a prior token saved by the * caller. This can be used to back up arbitrarily many tokens - it's * up to the caller to save ungettable tokens and push them back into * the stream. Tokens must be ungotten in reverse order (e.g., get A, * B, C, D, unget D, C, B, A). Note that the argument is the PREVIOUS * token to restore, not the current token. * * Tokens un-got with this routine are accessible only to next(), not * to any of the lower-level token readers. */ void unget(); void unget(const CTcToken *prv); /* * Push a token into the token stream. The given token becomes the * next token to be retrieved. This doesn't affect the current token. */ void push(const CTcToken *tok); /* get the current token */ const class CTcToken *getcur() const { return &curtok_; } /* * Copy the current token. This makes a copy of the token's text in * tokenizer source memory, to ensure that the reference to the text * buffer the caller is keeping will remain valid forever. */ const class CTcToken *copycur(); /* make a safely storable copy of a given token */ void copytok(class CTcToken *dst, const class CTcToken *src); /* check to see if the current token matches the given text */ int cur_tok_matches(const char *txt, size_t len); int cur_tok_matches(const char *txt) { return cur_tok_matches(txt, strlen(txt)); } /* * Try looking ahead to match a pair of symbols. If the current token * is a TOKT_SYM token matching sym1, read the next token to see if * it's another TOKT_SYM matching sym2. If so, skip the second token * and return true. If not, unget tthe second (so the original token * is current again) and return false. */ int look_ahead(const char *sym1, const char *sym2); /* * peek ahead - same as look_ahead, but doesn't skip anything even on * successfully matching the token pair */ int peek_ahead(const char *sym1, const char *sym2); /* * Set an external token source. We'll read tokens from this source * until it is exhausted, at which point we'll revert to the enclosing * source. * * The new source is inserted before the current token, so the current * token will become current once again when this source is exhausted. * We'll automatically advance to the next token, which (unless we * have an ungotten token stashed) will go to the first token in the * new source. */ void set_external_source(CTcTokenSource *src) { /* * store the old source in the new source, so we can restore the * old source when we have exhausted the new source */ src->set_enclosing_source(ext_src_, &curtok_); /* set the new external source */ ext_src_ = src; /* skip to the next token */ next(); } /* clear all external sources, returning to the real token stream */ void clear_external_sources(); /* * assume that we should have found '>>' sequence after an embedded * expression in a string - used by parsers to resynchronize after * an apparent syntax error */ void assume_missing_str_cont(); /* define a macro */ void add_define(const char *sym, size_t len, const char *expansion, size_t expan_len); void add_define(const char *sym, const char *expansion, size_t expan_len) { add_define(sym, strlen(sym), expansion, expan_len); } void add_define(const char *sym, const char *expansion) { add_define(sym, strlen(sym), expansion, strlen(expansion)); } /* add a macro, given the symbol entry */ void add_define(class CTcHashEntryPp *entry); /* undefine a previously defined macro */ void undefine(const char *sym, size_t len); void undefine(const char *sym) { undefine(sym, strlen(sym)); } /* find a #define symbol */ class CTcHashEntryPp *find_define(const char *sym, size_t len) const; /* find an #undef symbol */ class CTcHashEntryPp *find_undef(const char *sym, size_t len) const; /* enumerate all of the #define symbols through a callback */ void enum_defines(void (*func)(void *ctx, class CTcHashEntryPp *entry), void *ctx); /* read the next line and handle preprocessor directives */ int read_line_pp(); /* get the file descriptor and line number of the last line read */ class CTcTokFileDesc *get_last_desc() const { return last_desc_; } long get_last_linenum() const { return last_linenum_; } void get_last_pos(class CTcTokFileDesc **desc, long *linenum) const { *desc = last_desc_; *linenum = last_linenum_; } /* * set the current file descriptor and line number -- this can be * used to force the line position to a previously-saved value * (during code generation, for example) for error-reporting and * debug-record purposes */ void set_line_info(class CTcTokFileDesc *desc, long linenum) { last_desc_ = desc; last_linenum_ = linenum; } /* * Parse a preprocessor constant expression. We always parse out of * the macro expansion buffer (expbuf_), but the caller must set p_ * to point to the starting point on the expansion line prior to * calling this routine. * * If 'read_first' is true, we'll read a token into curtok_ before * parsing; otherwise, we'll assume the caller has already primed * the pump by reading the first token. * * If 'last_on_line' is true, we'll flag an error if anything is * left on the line after we finish parsing the expression. * * If 'add_line_ending' is true, we'll add an end-of-line marker to * the expansion buffer, so that the tokenizer won't attempt to read * past the end of the line. Since a preprocessor expression must * be contained entirely on a single logical line, we must never try * to read past the end of the current line when parsing a * preprocessor expression. */ int pp_parse_expr(class CTcConstVal *result, int read_first, int last_on_line, int add_line_ending); /* log an error, optionally with parameters */ static void log_error(int errnum, ...); /* * log an error with the current token text as the parameter, * suitable for a "%.*s" format list entry (hence we'll provide two * parameters: an integer with the length of the token text, and a * pointer to the token text string) */ void log_error_curtok(int errnum); /* log a warning, optionally with parameters */ static void log_warning(int errnum, ...); /* log a pedantic warning */ static void log_pedantic(int errnum, ...); /* log a warning with the current token as the parameter */ void log_warning_curtok(int errnum); /* log a warning or error for the current token */ void log_error_or_warning_curtok(tc_severity_t sev, int errnum); /* log a warning or error for a given token */ void log_error_or_warning_with_tok(tc_severity_t sev, int errnum, const CTcToken *tok); /* * log then throw a fatal error (this is different from an internal * error in that it indicates an unrecoverable error in the input; * an internal error indicates that something is wrong with the * compiler itself) */ static void throw_fatal_error(int errnum, ...); /* * log then throw an internal error (internal errors are always * fatal: these indicate that something has gone wrong in the * compiler, and are equivalent to an assert failure) */ static void throw_internal_error(int errnum, ...); /* display a string/number value */ void msg_str(const char *str, size_t len) const; void msg_long(long val) const; /* get the current line */ const char *get_cur_line() const { return linebuf_.get_text(); } size_t get_cur_line_len() const { return linebuf_.get_text_len(); } /* get/set the #define hash table */ class CTcMacroTable *get_defines_table() const { return defines_; } class CTcMacroTable *set_defines_table(class CTcMacroTable *tab) { CTcMacroTable *old_tab = defines_; defines_ = tab; return old_tab; } /* * look up a token as a keyword; returns true and fills in 'kw' with * the keyword token ID if the token is in fact a keyword, or * returns false if it's not a keyword */ int look_up_keyword(const CTcToken *tok, tc_toktyp_t *kw); /* * Get the next token on the line, filling in the token object. * Advances the pointer to the character immediately following the * token. * * If the token is a string, and the string contains backslash * sequences, we'll modify the source string by translating each * backslash sequences; for example, a "\n" sequence is changed into an * ASCII 10. * * 'expanding' indicates whether or not we're in the initial macro * expansion pass. If this is true, we'll suppress error messages * during this pass, as we'll encounter the same tokens again when we * parse the expanded form of the line. */ static tc_toktyp_t next_on_line(utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec, int expanding); /* * Get the text of an operator token. Returns a pointer to a * constant, static, null-terminated string, suitable for use in * error messages. */ static const char *get_op_text(tc_toktyp_t op); /* * Store text in the source list. Text stored here is available * throughout compilation. This routine automatically reserves the * space needed, so do not call 'reserve' or 'commit' separately. */ const char *store_source(const char *txt, size_t len); /* reserve space for text in the source list */ void reserve_source(size_t len); /* * Store a piece of text into pre-reserved space in the source list. * This can be used to build up a string from several pieces. You must * call 'reserve' first to allocate the space, and you must explicitly * add a null terminator at the end of the string. Do not call * 'commit'; this automatically commits the space as each substring is * added. */ const char *store_source_partial(const char *txt, size_t len); /* * Get the index of the next source file descriptor that will be * created. The linker can use this information to fix up * references to file descriptors in an object file when loading * multiple object files. */ int get_next_filedesc_index() const { return next_filedesc_id_; } /* get the number of source file descriptors in the master list */ int get_filedesc_count() const { return next_filedesc_id_; } /* get the file descriptor at the given (0-based) index */ class CTcTokFileDesc *get_filedesc(size_t idx) const { /* return the array entry at the index, if the index is valid */ return (idx < desc_list_cnt_ ? desc_list_[idx] : 0); } /* get the head of the master source file descriptor list */ class CTcTokFileDesc *get_first_filedesc() const { return desc_head_; } /* * Create a new file descriptor and add it to the master list. This * creates the new descriptor unconditionally, even if a descriptor * for the same source file already exists. */ class CTcTokFileDesc *create_file_desc(const char *fname, size_t len) { return get_file_desc(fname, len, TRUE, fname, len); } /* * Set the string capture file. Once this is set, we'll write the * contents of each string token that we encounter to this file, * with a newline after each token. */ void set_string_capture(osfildef *fp); /* write macros to a file, for debugger use */ void write_macros_to_file_for_debug(class CVmFile *fp); /* * Load macros from a file. If any errors occur, we'll flag them * through the error handler object and return a non-zero value. * Returns zero on success. */ int load_macros_from_file(class CVmStream *fp, class CTcTokLoadMacErr *err_handler); /* receive notification that the compiler is done with all parsing */ void parsing_done() { /* forget any input file position */ set_line_info(0, 0); } /* * Stuff text into the tokenizer source stream. The new text is * inserted at the current read pointer, so that the next token we * fetch will come from the start of the inserted text. If 'expand' is * true, we'll expand macros in the text; if not, we'll insert the text * exactly as is with no macro expansion. */ void stuff_text(const char *txt, size_t len, int expand); private: /* skip whitespace and token markers */ static void skip_ws_and_markers(utf8_ptr *p); /* * get the next token on the line; if we go past the end of the * string buffer, we'll return EOF */ static tc_toktyp_t next_on_line(const CTcTokString *srcbuf, utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec, int expanding); /* * get the next token on the current line, updating the internal * character position pointer to point just past the token, and filling * in the internal current token object with the toen data */ tc_toktyp_t next_on_line() { return next_on_line(&p_, &curtok_, 0, FALSE); } /* get the next token on the line, with string translation */ tc_toktyp_t next_on_line_xlat(tok_embed_ctx *ec) { return next_on_line_xlat(&p_, &curtok_, ec); } /* * get the next token, translating strings and storing string and * symbol text in the source block list */ tc_toktyp_t next_on_line_xlat_keep(); /* * get the next token on the line, translating strings to internal * format */ tc_toktyp_t next_on_line_xlat(utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec); /* * translate a string to internal format by converting escape * sequences; overwrites the original buffer */ tc_toktyp_t xlat_string(utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec); /* * translate a string into a given buffer; if 'force_embed_end' is * true, we'll act as though we're continuing the string after the * '>>' after an embedded expression, no matter what the actual * input looks like */ tc_toktyp_t xlat_string_to(char *dst, utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec, int force_embed_end); /* * Translate a string, saving the translated version in the source * block list. If 'force_end_embed' is true, we'll act as though we * were looking at '>>' (or, more precisely, we'll act as though * '>>' immediately preceded the current input), regardless of what * the actual input looks like. */ tc_toktyp_t xlat_string_to_src(tok_embed_ctx *ec, int force_end_embed); /* * is the given token type "safe" (i.e., stored in the tokenizer source * list, so that its text pointer is permanently valid; non-safe tokens * have valid text pointers only until the next token fetch) */ int is_tok_safe(tc_toktyp_t typ); /* initialize the source block list */ void init_src_block_list(); /* delete current source file, including all including parents */ void delete_source(); /* * Read the next line; processes comments, but does not expand macros * or parse preprocessor directives. This always reads into linebuf_; * the return value is the offset within linebuf_ of the new text. A * return value of -1 indicates that we're at end of file. */ int read_line(int append); /* * Set the source read pointer to the start of a new line, given the * CTcTokString object containing the buffer, and the offset within * that buffer. */ void start_new_line(CTcTokString *str, int ofs) { /* remember the buffer we're reading out of */ curbuf_ = str; /* set the read pointer to the start of the new line's text */ p_.set((char *)str->get_text() + ofs); } /* unsplice text from the current line and make it the next line */ void unsplice_line(const char *new_line_start); /* * Commit space in the source list - this is used when text is directly * stored after reserving space. The size reserved may be greater than * the size committed, because it is sometimes more efficient to make a * guess that may overestimate the amount we actually end up needing. */ void commit_source(size_t len); /* parse a string */ static tc_toktyp_t tokenize_string( utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec, int expanding_macros); /* process comments */ void process_comments(size_t start_ofs); /* splice lines for a string that runs across multiple lines */ void splice_string(); /* scan a sprintf format spec */ void scan_sprintf_spec(utf8_ptr *p); /* translate a \ escape sequence */ void xlat_escape(utf8_ptr *dst, utf8_ptr *p, wchar_t qu, int triple); /* skip an ordinary character or \ escape sequence */ void skip_escape(utf8_ptr *p); /* translate escape sequences in a string segment */ void xlat_escapes(utf8_ptr *dstp, const utf8_ptr *srcp, const utf8_ptr *endp); /* expand macros in the current line */ int expand_macros_curline(int read_more, int allow_defined, int append_to_expbuf); /* * Expand the macros in the given text, filling in the given * CTcTokString with the results. The expansion will clear out any * existing text in the result buffer. Returns zero on success, or * non-zero on error. */ int expand_macros(class CTcTokString *dest, const char *str, size_t len) { CTcTokStringRef srcbuf; /* set up a CTcTokString for the source */ srcbuf.set_buffer(str, len); /* go expand macros */ return expand_macros(&srcbuf, 0, dest, FALSE, FALSE, FALSE); } /* expand all of the macros in the given text */ int expand_macros(class CTcTokString *srcbuf, utf8_ptr *src, class CTcTokString *expbuf, int read_more, int allow_defined, int append); /* expand the macro at the current token on the current line */ int expand_macro(class CTcMacroRsc *res, class CTcTokString *expbuf, const class CTcTokString *srcbuf, utf8_ptr *src, size_t macro_srcbuf_ofs, CTcHashEntryPp *entry, int read_more, int allow_defined, int *expanded); /* * Remove our special expansion flags from an expanded macro buffer. * This can be called after all expansion has been completed to clean * up the buffer for human consumption. */ void remove_expansion_flags(CTcTokString *buf); /* scan for a prior expansion of a macro within the current context */ static int scan_for_prior_expansion(utf8_ptr src, const char *src_end, const class CTcHashEntryPp *entry); /* remove end-of-macro-expansion flags from a buffer */ static void remove_end_markers(class CTcTokString *buf); /* change a buffer to use individual token full-expansion markers */ void mark_full_exp_tokens(CTcTokString *dstbuf, const class CTcTokString *srcbuf, int append) const; /* allocate a macro expansion resource */ class CTcMacroRsc *alloc_macro_rsc(); /* release a macro expansion resource */ void release_macro_rsc(class CTcMacroRsc *rsc); /* * Parse the actual parameters to a macro. Fills in argofs[] and * arglen[] with the offsets (from srcbuf->get_buf()) and lengths, * respectively, of each actual parameter's text. */ int parse_macro_actuals(const class CTcTokString *srcbuf, utf8_ptr *src, const CTcHashEntryPp *macro_entry, size_t argofs[TOK_MAX_MACRO_ARGS], size_t arglen[TOK_MAX_MACRO_ARGS], int read_more, int *found_actuals); /* splice the next line for reading more macro actuals */ tc_toktyp_t actual_splice_next_line(const CTcTokString *srcbuf, utf8_ptr *src, CTcToken *tok); /* substitute the actual parameters in a macro's expansion */ int substitute_macro_actuals(class CTcMacroRsc *rsc, class CTcTokString *subexp, CTcHashEntryPp *macro_entry, const class CTcTokString *srcbuf, const size_t *argofs, const size_t *arglen, int allow_defined); /* stringize a macro actual parameter into an expansion buffer */ void stringize_macro_actual(class CTcTokString *expbuf, const char *actual_val, size_t actual_len, char quote_char, int add_open_quote, int add_close_quote); /* skip a delimited macro expansion area (#foreach, #ifempty, etc) */ void skip_delimited_group(utf8_ptr *p, int parts_to_skip); /* expand a defined() preprocessor operator */ int expand_defined(class CTcTokString *subexp, const class CTcTokString *srcbuf, utf8_ptr *src); /* add a file to the list of files to be included only once */ void add_include_once(const char *fname); /* find a file in the list of files to be included only once */ int find_include_once(const char *fname); /* process a #pragma directive */ void pp_pragma(); /* process a #charset directive */ void pp_charset(); /* process a #include directive */ void pp_include(); /* process a #define directive */ void pp_define(); /* process a #if directive */ void pp_if(); /* process a #ifdef directive */ void pp_ifdef(); /* process a #ifdef directive */ void pp_ifndef(); /* process a #ifdef or #ifndef */ void pp_ifdef_or_ifndef(int sense); /* process a #else directive */ void pp_else(); /* process a #elif directive */ void pp_elif(); /* process a #endif directive */ void pp_endif(); /* process a #error directive */ void pp_error(); /* process a #undef directive */ void pp_undef(); /* process a #line directive */ void pp_line(); /* get a lone identifier for a preprocessor directive */ int pp_get_lone_ident(char *buf, size_t bufl); /* process a #pragma C directive */ // void pragma_c(); - not currently used /* process a #pragma once directive */ void pragma_once(); /* process a #pragma all_once directive */ void pragma_all_once(); /* process a #pragma message directive */ void pragma_message(); /* process a #pragma newline_spacing(on/off) directive */ void pragma_newline_spacing(); /* process a #pragma sourceTextGroup directive */ void pragma_source_text_group(); /* * Determine if we're in a false #if branch. If we're inside a #if * block, and the state is either IF_NO, IF_DONE, or ELSE_NO, or * we're inside a #if nested within any negative branch, we're in a * not-taken branch of a #if block. */ int in_false_if() const { return (if_sp_ != 0 && (if_false_level_ != 0 || if_stack_[if_sp_ - 1].state == TOKIF_IF_NO || if_stack_[if_sp_ - 1].state == TOKIF_IF_DONE || if_stack_[if_sp_ - 1].state == TOKIF_ELSE_NO)); } /* push a new #if level with the given state */ void push_if(tok_if_t state); /* get the current #if state */ tok_if_t get_if_state() const { if (if_sp_ == 0) return TOKIF_NONE; else return if_stack_[if_sp_ - 1].state; } /* switch the current #if level to the given state */ void change_if_state(tok_if_t state) { if (if_sp_ != 0) if_stack_[if_sp_ - 1].state = state; } /* pop the current #if level */ void pop_if(); /* * Find or create a descriptor for the given filename. 'fname' is * the full file system path specifying the file. 'orig_fname' is * the filename as originally specified by the user, if different; * in the case of #include files, this indicates the name that was * specified in the directive itself, whereas 'fname' is the actual * filename that resulted from searching the include path for the * given name. */ class CTcTokFileDesc *get_file_desc(const char *fname, size_t fname_len, int always_create, const char *orig_fname, size_t orig_fname_len); /* clear the line buffer */ void clear_linebuf(); /* flag: ALL_ONCE mode - we include each file only once */ unsigned int all_once_ : 1; /* flag: warn on ignoring a redundant #include file */ unsigned int warn_on_ignore_incl_ : 1; /* * Flag: in preprocess-only mode. In this mode, we'll leave certain * preprocessor directives intact in the source, since they'll be * needed in a subsequent compilation of the preprocessed source. * For example, we'll leave #line directives, #pragma C, #error, and * #pragma message directives in the preprocessed result. */ unsigned int pp_only_mode_ : 1; /* * Flag: in test reporting mode. In this mode, we'll expand __FILE__ * macros with the root name only. */ unsigned int test_report_mode_ : 1; /* * Flag: in preprocess-for-includes mode. In this mode, we'll do * nothing except run the preprocessor and generate a list of the * header files that are included, along with header files they * include, and so on. */ unsigned int list_includes_mode_ : 1; /* * flag: we're parsing a preprocessor constant expression (for a * #if, for example; this doesn't apply to simple macro expansion) */ unsigned int in_pp_expr_ : 1; /* mode for handling newlines in strings */ newline_spacing_mode_t string_newline_spacing_; /* resource loader */ class CResLoader *res_loader_; /* * name of our default character set - this is generally specified * by the user (on the compiler command line, for example), or * obtained from the operating system */ char *default_charset_; /* input (to unicode) character mapper for the default character set */ class CCharmapToUni *default_mapper_; /* head of list of previously-included files */ struct tctok_incfile_t *prev_includes_; /* head and tail of include path list */ struct tctok_incpath_t *incpath_head_; struct tctok_incpath_t *incpath_tail_; /* file descriptor and line number of last line read */ class CTcTokFileDesc *last_desc_; long last_linenum_; /* file descriptor and line number of last line appended */ class CTcTokFileDesc *appended_desc_; long appended_linenum_; /* current input stream */ class CTcTokStream *str_; /* master list of file descriptors */ class CTcTokFileDesc *desc_head_; class CTcTokFileDesc *desc_tail_; /* * array of file descriptors (we keep the list in both an array and * a linked list, since we need both sequential and indexed access; * this isn't a lot of trouble since we never need to remove an * entry from the list) */ class CTcTokFileDesc **desc_list_; /* number of entries in desc_list_ */ size_t desc_list_cnt_; /* number of slots allocated in desc_list_ array */ size_t desc_list_alo_; /* next file descriptor ID to be assigned */ int next_filedesc_id_; /* pointer to current position in current line */ utf8_ptr p_; /* * The CTcTokString object containing the current line. This is the * buffer object we're currently reading from, and will be either * linebuf_ or expbuf_. p_ always points into this buffer. */ CTcTokString *curbuf_; /* raw file input buffer */ CTcTokString linebuf_; /* * unsplice buffer - we'll put any unspliced text into this buffer, * then read it back at the next read_line() */ CTcTokString unsplicebuf_; /* macro expansion buffer */ CTcTokString expbuf_; /* macro definition parsing buffer */ CTcTokString defbuf_; /* * Flag: in a string. If this is '\0', we're not in a string; * otherwise, this is the quote character that ends the string. */ wchar_t in_quote_; /* flag: the in_quote_ string we're in is triple quoted */ int in_triple_; /* embedded expression context in comment */ tok_embed_ctx comment_in_embedding_; /* embedded expression context within a macro expansion */ tok_embed_ctx macro_in_embedding_; /* embedded expression context for the main token stream */ tok_embed_ctx main_in_embedding_; /* * #if state stack. if_sp_ is the index of the next nesting slot; * if if_sp_ is zero, it means that we're not in a #if at all. * * Separately, the if_false_level_ is the level of #if's contained * within a false #if branch. This is separate because, once we're * in a false #if branch, everything within it is false. */ int if_sp_; tok_if_info_t if_stack_[TOK_MAX_IF_NESTING]; int if_false_level_; /* source block list head */ CTcTokSrcBlock *src_head_; /* current (and last) source block */ CTcTokSrcBlock *src_cur_; /* pointer to next available byte in the current source block */ char *src_ptr_; /* number of bytes remaining in the current source block */ size_t src_rem_; /* current token */ CTcToken curtok_; /* previous token (for unget) */ CTcToken prvtok_; /* head of allocated list of unget token slots */ CTcTokenEle *unget_head_; /* * Last unget token. This is a pointer into the unget list, to the * element containing the last ungotten token. The next next() will * reinstate this token and move the pointer back into the list; the * next unget() will move the pointer forward and fill in the next * element. This is null if there are no ungotten tokens. */ CTcTokenEle *unget_cur_; /* the external token source, if any */ CTcTokenSource *ext_src_; /* symbol table for #define symbols */ class CTcMacroTable *defines_; /* * symbol table for symbols explicitly undefined; we keep track of * these so that we can exclude anything ever undefined from the debug * macro records, since only static global macros can be handled in the * debug records */ class CVmHashTable *undefs_; /* symbol table for TADS keywords */ class CVmHashTable *kw_; /* head of macro resource pool list */ class CTcMacroRsc *macro_res_head_; /* head of list of available macro resources */ class CTcMacroRsc *macro_res_avail_; /* * string capture file - if this is non-null, we'll capture all of * the strings we read to this file, one string per line */ class CVmDataSource *string_fp_; /* character mapper for writing to the string capture file */ class CCharmapToLocal *string_fp_map_; /* true -> allow preprocessor directives */ unsigned int allow_pp_; }; /* ------------------------------------------------------------------------ */ /* * Error handler interface. Callers of load_macros_from_file() in * CTcTokenizer must provide an implementation of this interface to handle * errors that occur while loading macros. */ class CTcTokLoadMacErr { public: /* * Flag an error. The error codes are taken from the following list: * * 1 - a macro name symbol in the file is too long (it exceeds the * maximum symbol length for the preprocessor) * * 2 - a formal parameter name is too long */ virtual void log_error(int err) = 0; }; /* ------------------------------------------------------------------------ */ /* * Tokenizer File Descriptor. Each unique source file has a separate * file descriptor, which keeps track of the file's name. */ class CTcTokFileDesc { public: /* create a file descriptor */ CTcTokFileDesc(const char *fname, size_t fname_len, int index, CTcTokFileDesc *orig_desc, const char *orig_fname, size_t orig_fname_len); /* delete the descriptor */ ~CTcTokFileDesc(); /* get the filename */ const char *get_fname() const { return fname_; } /* get the original filename string */ const char *get_orig_fname() const { return orig_fname_; } /* * get the filename as a double-quoted string (backslashes and * double-quotes will be escaped with backslashes) */ const char *get_dquoted_fname() const { return dquoted_fname_; } /* * get the root filename (i.e., with no path prefix) as a * double-quoted string */ const char *get_dquoted_rootname() const { return dquoted_rootname_; } /* get the filename as a single-quoted string */ const char *get_squoted_fname() const { return squoted_fname_; } /* get the root filename as a single-quoted string */ const char *get_squoted_rootname() const { return squoted_rootname_; } /* get/set the next file descriptor in the descriptor chain */ CTcTokFileDesc *get_next() const { return next_; } void set_next(CTcTokFileDesc *nxt) { next_ = nxt; } /* get my index in the master list */ int get_index() const { return index_; } /* get the original descriptor for this file in the list */ CTcTokFileDesc *get_orig() const { return orig_; } /* * get the list index of the original entry (returns my own list * index if I am the original entry) */ int get_orig_index() const { return orig_ == 0 ? index_ : orig_->get_index(); } /* * Add a source line position to our list. We keep an index of the * byte-code address for each executable source line, so that * debuggers can find the compiled code corresponding to a source * location. The image builder gives us this information during the * linking process. The address is the absolute location in the * image file of the executable code for the given source line (the * first line in the file is numbered 1). */ void add_source_line(ulong linenum, ulong line_addr); /* * Enumerate the source lines, calling the callback for each one. * We will only enumerate source lines which actually have an * associated code location - source lines that generated no * executable code are skipped. We'll enumerate the lines in * ascending order of line number, and each line number will appear * only once. */ void enum_source_lines(void (*cbfunc)(void *ctx, ulong linenum, ulong byte_code_addr), void *cbctx); private: /* index in the master list */ int index_; /* filename string - this is the actual file system filename */ char *fname_; /* * original filename string, if different from fname_ - this is the * filename as specified by the user, before it was adjusted with * include paths or other extra location information */ char *orig_fname_; /* double-quoted version of the filename */ char *dquoted_fname_; /* single-quoted version of the filename */ char *squoted_fname_; /* single-quoted version of the root filename */ char *squoted_rootname_; /* double-quoted version of the root filename */ char *dquoted_rootname_; /* next descriptor in the master descriptor list */ CTcTokFileDesc *next_; /* * The original file descriptor with the same filename. If we * create multiple descriptors for the same filename (because, for * example, the same header is included in several different object * files), we'll keep track of the original descriptor for the file * in all of the copies. */ CTcTokFileDesc *orig_; /* source line pages */ struct CTcTokSrcPage **src_pages_; /* number of source line page slots allocated */ size_t src_pages_alo_; }; /* ------------------------------------------------------------------------ */ /* * Tokenizer Input Stream */ class CTcTokStream { public: /* create a token stream */ CTcTokStream(class CTcTokFileDesc *desc, class CTcSrcObject *src, CTcTokStream *parent, int charset_error, int init_if_level); /* delete the stream */ ~CTcTokStream(); /* get/set the associated file descriptor */ class CTcTokFileDesc *get_desc() const { return desc_; } void set_desc(class CTcTokFileDesc *desc) { desc_ = desc; } /* get the underlying source file */ class CTcSrcObject *get_src() const { return src_; } /* get the line number of the next line to be read */ long get_next_linenum() const { return next_linenum_; } /* set the next line number */ void set_next_linenum(long l) { next_linenum_ = l; } /* get the enclosing stream */ CTcTokStream *get_parent() const { return parent_; } /* count having read a line */ void count_line() { ++next_linenum_; } /* was there a #charset error when opening the file? */ int get_charset_error() const { return charset_error_; } /* get/set the in-comment status */ int is_in_comment() const { return in_comment_; } void set_in_comment(int f) { in_comment_ = f; } /* get/set the pragma C mode */ // int is_pragma_c() const { return pragma_c_; } // void set_pragma_c(int f) { pragma_c_ = f; } /* get/set if nesting level at the start of the file */ int get_init_if_level() const { return init_if_level_; } void set_init_if_level(int level) { init_if_level_ = level; } /* get/set the newline spacing mode */ newline_spacing_mode_t get_newline_spacing() const { return newline_spacing_; } void set_newline_spacing(newline_spacing_mode_t f) { newline_spacing_ = f; } private: /* file descriptor associated with this file */ class CTcTokFileDesc *desc_; /* the underlying source reader */ class CTcSrcObject *src_; /* * the enclosing stream - this is the stream that #include'd the * current stream */ CTcTokStream *parent_; /* line number of next line to be read */ ulong next_linenum_; /* #if nesting level at the start of the file */ int init_if_level_; /* newline_spacing mode when the stream was stacked */ newline_spacing_mode_t newline_spacing_; /* flag: we were unable to load the map in the #charset directive */ uint charset_error_ : 1; /* the stream is in a multi-line comment */ uint in_comment_ : 1; /* flag: we're in #pragma C+ mode */ // uint pragma_c_ : 1; - #pragma C is not currently used }; /* ------------------------------------------------------------------------ */ /* * Keyword Hash Table Entry */ class CTcHashEntryKw: public CVmHashEntryCS { public: CTcHashEntryKw(const textchar_t *str, tc_toktyp_t tokid) : CVmHashEntryCS(str, strlen(str), FALSE) { /* save the token ID for the keyword */ tokid_ = tokid; } /* get the token ID */ tc_toktyp_t get_tok_id() const { return tokid_; } private: /* our token ID */ tc_toktyp_t tokid_; }; /* ------------------------------------------------------------------------ */ /* * basic #define symbol table entry */ class CTcHashEntryPp: public CVmHashEntryCS { public: CTcHashEntryPp(const textchar_t *str, size_t len, int copy) : CVmHashEntryCS(str, len, copy) { /* by default, we have no arguments */ has_args_ = FALSE; has_varargs_ = FALSE; argc_ = 0; argv_ = 0; params_table_ = 0; } /* get the expansion text */ virtual const char *get_expansion() const = 0; virtual size_t get_expan_len() const = 0; /* get the original expansion text, before parsing */ virtual const char *get_orig_expansion() const { return get_expansion(); } virtual size_t get_orig_expan_len() const { return get_expan_len(); } /* certain special macros (__LINE__, __FILE__) aren't undef'able */ virtual int is_undefable() const { return TRUE; } /* * most macros are real symbols, created by #define's, but some are * special pseudo-macros, like __LINE__ and __FILE__, that the * preprocessor provides */ virtual int is_pseudo() const { return FALSE; } /* does the macro have an argument list? */ int has_args() const { return has_args_; } /* get the number of arguments */ int get_argc() const { return argc_; } /* do we have a variable number of arguments? */ int has_varargs() const { return has_varargs_; } /* * get the minimum number of allowed arguments - if we have varargs, * this is one less than the number of formals listed, since the last * formal can correspond to any number of actuals, including zero */ int get_min_argc() const { return has_varargs_ ? argc_ - 1 : argc_; } /* get the name of an argument by position (0 = first argument) */ const char *get_arg_name(int idx) const { return argv_[idx]; } /* get the parameter hash table entry for the parameter */ class CTcHashEntryPpArg *get_arg_entry(int idx) const { return arg_entry_[idx]; } /* get the parameters hash table */ const CVmHashTable *get_params_table() const { return params_table_; } protected: /* argument list */ char **argv_; /* list of parameter hash entries */ class CTcHashEntryPpArg **arg_entry_; /* parameter hash table */ CVmHashTable *params_table_; /* argument count */ int argc_; /* flag: the macro has a parameter list */ uint has_args_ : 1; /* * flag: the parameter list takes a variable number of arguments; if * this is set, then argc_ is one greater than the minimum number of * arguments required, and the last formal receives the varying part * of the actual parameter list, which can contain zero or more * actuals */ uint has_varargs_ : 1; }; /* * #define symbol hash table entry */ class CTcHashEntryPpDefine: public CTcHashEntryPp { public: /* * Create the hash entry. argc is the number of arguments to the * macro, and argv is an array of pointers to null-terminated * strings with the argument names, in the order defined in the * macro. * * If has_args is false, the macro does not take a parameter list at * all. Note that it is possible for has_args to be true and argc * to be zero, because a macro can be defined to take an argument * list with no arguments (i.e., empty parens). A macro with an * empty argument list is distinct from a macro with no argument * list: in the former case, the empty parens are required, and are * removed from the input stream and replaced with the macro's * expansion. * * We'll make a copy of the argument list vector, strings, and * expansion text, so the caller is free to forget all of that after * creating the entry instance. */ CTcHashEntryPpDefine(const textchar_t *str, size_t len, int copy, int has_args, int argc, int has_varargs, const char **argv, const size_t *argvlen, const char *expansion, size_t expan_len); ~CTcHashEntryPpDefine(); /* * Get the expansion text and its length. This is the parsed version * of the expansion, with occurrences of the formal parameters replaced * by TOK_MACRO_FORMAL_FLAG sequences, and the various '#' sequences * replaced by their corresponding flags. */ const char *get_expansion() const { return expan_; } size_t get_expan_len() const { return expan_len_; } /* get the original expansion - the original text before parsing */ const char *get_orig_expansion() const { return orig_expan_; } size_t get_orig_expan_len() const { return orig_expan_len_; } private: /* parse the expansion */ void parse_expansion(const size_t *argvlen); /* expansion (parsed version) */ char *expan_; size_t expan_len_; /* original expansion (before parsing) */ char *orig_expan_; size_t orig_expan_len_; }; /* * Hash table entry for __FILE__ and __LINE__ */ class CTcHashEntryPpSpecial: public CTcHashEntryPp { public: CTcHashEntryPpSpecial(CTcTokenizer *tok, const char *str) : CTcHashEntryPp(str, strlen(str), FALSE) { /* remember my tokenizer */ tok_ = tok; } /* these special macros are not undef'able */ virtual int is_undefable() const { return FALSE; } /* special macros are pseudo-macros provided by the preprocessor */ virtual int is_pseudo() const { return TRUE; } protected: /* my tokenizer */ CTcTokenizer *tok_; }; class CTcHashEntryPpFILE: public CTcHashEntryPpSpecial { public: CTcHashEntryPpFILE(CTcTokenizer *tok) : CTcHashEntryPpSpecial(tok, "__FILE__") { } /* our expansion is the current filename, in single quotes */ const char *get_expansion() const { return get_base_text(); } size_t get_expan_len() const { return strlen(get_base_text()); } private: /* get our expansion base text */ const char *get_base_text() const { /* * if we're in test-report mode, use the root name only; * otherwise, use the full name with path */ if (tok_->get_last_desc() == 0) return ""; else if (tok_->get_test_report_mode()) return tok_->get_last_desc()->get_squoted_rootname(); else return tok_->get_last_desc()->get_squoted_fname(); } }; class CTcHashEntryPpLINE: public CTcHashEntryPpSpecial { public: CTcHashEntryPpLINE(CTcTokenizer *tok) : CTcHashEntryPpSpecial(tok, "__LINE__") { } /* our expansion is the line number as a decimal string */ const char *get_expansion() const { gen_expansion(tok_); return buf_; } size_t get_expan_len() const { gen_expansion(tok_); return strlen(buf_); } private: /* generate the expansion text into our internal buffer */ static void gen_expansion(CTcTokenizer *tok) { sprintf(buf_, "%ld", tok->get_last_linenum()); } /* internal buffer */ static char buf_[20]; }; /* * Hash entry for preprocessor arguments */ class CTcHashEntryPpArg: public CVmHashEntryCS { public: CTcHashEntryPpArg(const char *str, size_t len, int copy, int argnum) : CVmHashEntryCS(str, len, copy) { /* remember the argument number */ argnum_ = argnum; } /* get my argument number */ int get_argnum() const { return argnum_; } private: /* argument number */ int argnum_; }; /* ------------------------------------------------------------------------ */ /* * Previously-included file list entry. Each time we include a file, * we'll add an entry to a list of files; in the future, we'll consult * this list to ensure that we don't include the same file again. */ struct tctok_incfile_t { /* next entry in the list of previously-included files */ tctok_incfile_t *nxt; /* name of this file (we'll allocate memory to hold the name) */ char fname[1]; }; /* ------------------------------------------------------------------------ */ /* * Include path list entry. This structure defines one include path; we * maintain a list of these structures. */ struct tctok_incpath_t { /* next entry in the list */ tctok_incpath_t *nxt; /* path */ char path[1]; }; #endif /* TCTOK_H */ qtads-2.1.7/tads3/tcunas.h000066400000000000000000000060061265017072300153260ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/TCUNAS.H,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcunas.h - TADS 3 Compiler Unassembler Function Notes Modified 05/10/99 MJRoberts - Creation */ #ifndef TCUNAS_H #define TCUNAS_H #include #include #include "t3std.h" #include "tcgen.h" /* ------------------------------------------------------------------------ */ /* * byte-code source for unassembler */ class CTcUnasSrc { public: virtual ~CTcUnasSrc() { } /* * read the next byte; returns zero on success, non-zero at the end * of the byte stream */ virtual int next_byte(char *ch) = 0; /* get the current offset */ virtual ulong get_ofs() const = 0; }; /* * code stream implementation of byte code source */ class CTcUnasSrcCodeStr: public CTcUnasSrc { public: CTcUnasSrcCodeStr(CTcCodeStream *str) { /* remember our underlying code stream */ str_ = str; /* start at the first byte of the code stream */ ofs_ = 0; } /* read from the code stream */ int next_byte(char *ch) { /* if there's anything left, return the byte and bump the pointer */ if (ofs_ < str_->get_ofs()) { *ch = str_->get_byte_at(ofs_); ++ofs_; return 0; } /* indicate end of file */ return 1; } /* get the current offset */ ulong get_ofs() const { return ofs_; } protected: /* underlying code stream object */ CTcCodeStream *str_; /* current read offset in code stream */ ulong ofs_; }; /* ------------------------------------------------------------------------ */ /* * output stream for unassembler */ class CTcUnasOut { public: virtual ~CTcUnasOut() { } /* write a line of text to the output, printf-style */ virtual void print(const char *fmt, ...) = 0; }; /* * stdio implementation of output stream - writes data to standard * output */ class CTcUnasOutStdio: public CTcUnasOut { public: void print(const char *fmt, ...) { va_list va; /* display the data on the standard output */ va_start(va, fmt); vprintf(fmt, va); va_end(va); } }; /* * Text file (osfildef) implementation of output stream. The file handle * is managed by the caller. */ class CTcUnasOutFile: public CTcUnasOut { public: CTcUnasOutFile(osfildef *fp) { fp_ = fp; } void print(const char *fmt, ...) { char buf[1024]; va_list va; /* format the text */ va_start(va, fmt); t3vsprintf(buf, sizeof(buf), fmt, va); va_end(va); /* write the formatted text to the file */ os_fprintz(fp_, buf); } protected: /* our file handle */ osfildef *fp_; }; #endif /* TCUNAS_H */ qtads-2.1.7/tads3/utf8.cpp000066400000000000000000000110011265017072300152410ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/utf8.cpp,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name utf8.cpp - UTF-8 implementation Function Notes Modified 10/17/98 MJRoberts - Creation */ #include "utf8.h" /* ------------------------------------------------------------------------ */ /* * encode a string of wide characters into the buffer */ size_t utf8_ptr::setwchars(const wchar_t *src, size_t src_count, size_t bufsiz) { /* loop through the source and store the characters */ size_t outbytes; for (outbytes = 0 ; src_count > 0 ; --src_count, ++src) { /* figure out how many bytes we need for this character */ size_t curbytes = s_wchar_size(*src); /* add it to the total output size */ outbytes += curbytes; /* if we have room, add it to the buffer */ if (bufsiz >= curbytes) { /* store it */ setch(*src); /* deduct this space from the remaining buffer size */ bufsiz -= curbytes; } else { /* * there's no room for this - make sure we don't store * anything more (since we might have room for a shorter * character later, but that would put a gap in the output * string - better to just truncate here) */ bufsiz = 0; } } /* return the total output size used (or needed) */ return outbytes; } /* ------------------------------------------------------------------------ */ /* * encode a null-terminated string of wide characters into the buffer */ size_t utf8_ptr::setwcharsz(const wchar_t *src, size_t bufsiz) { /* loop through the source and store the characters */ size_t outbytes; for (outbytes = 0 ; *src != 0 ; ++src) { /* figure out how many bytes we need for this character */ size_t curbytes = s_wchar_size(*src); /* add it to the total output size */ outbytes += curbytes; /* if we have room, add it to the buffer */ if (bufsiz >= curbytes) { /* store it */ setch(*src); /* deduct this space from the remaining buffer size */ bufsiz -= outbytes; } else { /* * there's no room for this - make sure we don't store * anything more (since we might have room for a shorter * character later, but that would put a gap in the output * string - better to just truncate here) */ bufsiz = 0; } } /* return the total output size used (or needed) */ return outbytes; } /* ------------------------------------------------------------------------ */ /* * Compare this string to the given string */ int utf8_ptr::s_compare_to(const char *p1, size_t bytelen1, const char *p2, size_t bytelen2) { /* keep going until one or the other string runs out of bytes */ while (bytelen1 != 0 && bytelen2 != 0) { wchar_t c1, c2; size_t siz1, siz2; /* get the current character from each string */ c1 = s_getch(p1); c2 = s_getch(p2); /* compare them */ if (c1 > c2) return 1; else if (c1 < c2) return -1; /* get the size of each character */ siz1 = s_charsize(*p1); siz2 = s_charsize(*p2); /* decrement each counter by the byte size of this character */ bytelen1 -= siz1; bytelen2 -= siz2; /* advance to the next character in each string */ p1 += siz1; p2 += siz2; } /* * we didn't find any character differences, but one string is * longer than the other -- if they ran out at the same time, * they're identical; otherwise, the one that ran out first is the * lesser one */ if (bytelen2 != 0) { /* the first one ran out first, so the first one sorts earlier */ return -1; } else if (bytelen1 != 0) { /* the second one ran out first, so the first one sort later */ return 1; } else { /* they both ran out at the same time */ return 0; } } qtads-2.1.7/tads3/utf8.h000066400000000000000000000577101265017072300147270ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/utf8.h,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name utf8.h - UTF-8 character string manipulation Function Notes Modified 10/16/98 MJRoberts - Creation */ #ifndef UTF8_H #define UTF8_H #include #include "vmuni.h" /* ------------------------------------------------------------------------ */ /* * UTF-8 character string pointer. * * Note that this class deviates from the normal naming convention where * each class begins with a capital 'C'. Since this class is so * low-level, and is used so much like the (char *) type, it seems more * proper to give it a name as though it were a typedef for a native type. * * If ever there was a time when operator overloading is indicated, this * would be it. We could overload increment and decrement operators, for * example, to step through the string. However, I just plain don't like * operator overloading, so I do not use it here. Instead, we use * explicit method names to avoid obfuscating the code as overloaded * operators would. It's a trade-off: it's less concise this way, but * less obscure. * * Note the important distinction between "byte" and "character": a byte * is the basic multi-bit unit of native storage, and a character * represents the basic lexical unit; a character may be composed of more * than one byte. */ class utf8_ptr { public: /* create a UTF-8 string pointer, with no initial underlying string */ utf8_ptr() { p_ = 0; } /* * Create a UTF-8 string pointer with an underlying string. The * pointer must point to the first byte of a valid character. */ utf8_ptr(char *str) { set(str); } /* create from another utf8 pointer */ utf8_ptr(const utf8_ptr *p) { set(p->p_); } /* * Set the pointer to a new underlying buffer. The pointer must * point to the first byte of a valid character if there are already * characters in the buffer. */ void set(char *str) { p_ = str; } void set(const utf8_ptr *p) { p_ = p->p_; } /* * Get the character at the current position */ wchar_t getch() const { return s_getch(p_); } /* * Get the character at a given character offset from the current * position. The offset must be positive. */ wchar_t getch_at(size_t ofs) const { return s_getch_at(p_, ofs); } /* * Get the character preceding the current character by the given * amount. The offset must be positive. getch_before(1) returns * the character preceding the current character, (2) returns the * character two positions before the current character, and so on. */ wchar_t getch_before(size_t ofs) const { return s_getch_before(p_, ofs); } /* * Encode a character into the buffer at the current position, and * increment the pointer past the character. */ void setch(wchar_t ch) { /* store the character and advance the buffer pointer */ p_ += s_putch(p_, ch); } /* * encode a character into the buffer, incrementing the pointer and * decrementing a remaining length count */ void setch(wchar_t ch, size_t *rem) { size_t len = s_putch(p_, ch); p_ += len; *rem -= len; } /* call setch() for each character in a null-terminated string */ void setch_str(const char *str) { for ( ; *str != '\0' ; ++str) p_ += s_putch(p_, *str); } /* * Encode a string of wide characters into the buffer. We'll * increment our pointer so that it points to the next available * character when we're done. Returns the number of bytes used for * the encoding. * * 'src_count' is the number of wide characters in the source string. * * 'bufsiz' gives the size remaining in the underlying buffer. If * we run out of space, we won't encode any more characters, but we * will still return the total number of bytes required to encode * the string. */ size_t setwchars(const wchar_t *src, size_t src_count, size_t bufsiz); /* * Encode a null-terminated string of wide-characters into our buffer. * Works like setwchars(), but stops at the null terminator in the * source rather than taking a character count. * * This routine doesn't store a trailing null in the result string. My * string pointer is left at the next character after the last copied * character. */ size_t setwcharsz(const wchar_t *src, size_t bufsiz); /* increment the pointer by one character */ void inc() { p_ = s_inc(p_); } /* * increment the pointer by one character, and decrement a remaining * length counter accordingly */ void inc(size_t *rem) { /* calculate the increment amount */ char *p = s_inc(p_); /* decrement the length counter by the change */ *rem -= (p - p_); /* save the new pointer value */ p_ = p; } /* increment by a give number of characters */ void inc_by(size_t cnt) { for ( ; cnt > 0 ; inc(), --cnt) ; } /* decrement the pointer by one character */ void dec() { p_ = s_dec(p_); } /* decrement poniter and increment the remaining size to compensate */ void dec(size_t *rem) { /* calculate the decrement amount */ char *p = s_dec(p_); /* decrement the length counter by the change */ *rem += (p_ - p); /* save the new pointer value */ p_ = p; } /* increment/decrement by a byte count */ void inc_bytes(size_t cnt) { p_ += cnt; } void dec_bytes(size_t cnt) { p_ -= cnt; } /* * Determine if the current character is a continuation character. * Returns 1 if so, 0 if not. */ int is_continuation() const { return s_is_continuation(p_); } /* * count the number of characters in the given number of bytes, * starting at the current byte */ size_t len(size_t bytecnt) const { return s_len(p_, bytecnt); } /* get the byte size of the current character */ size_t charsize() const { return s_charsize(*p_); } /* * Get the number of bytes in the given number of characters * starting at the current position. */ size_t bytelen(size_t charcnt) const { return s_bytelen(p_, charcnt); } /* * count the number of characters to the null terminator */ size_t lenz() const { char *p; size_t cnt; /* increment until we find a null byte */ for (cnt = 0, p = p_ ; *p != 0 ; p = s_inc(p), ++cnt) ; /* return the result */ return cnt; } /* get the current pointer position */ char *getptr() const { return p_; } /* -------------------------------------------------------------------- */ /* * Static methods */ /* * Compare two UTF-8 strings. Returns a value less than zero if the * first string is lexically less than the second string (i.e., the * first string sorts ahead of the second string), zero if the two * strings are identical, or a value greater than zero if the first * string is lexically greater than the second string. */ static int s_compare_to(const char *p1, size_t bytelen1, const char *p2, size_t bytelen2); /* get the character at the given byte pointer */ static wchar_t s_getch(const char *p) { /* * If the high bit is 0, it's a one-byte sequence encoding the * value in the low seven bits. */ if ((*p & 0x80) == 0) return (((unsigned char)*p) & 0x7f); /* * If the high two bytes are 110, it's a two-byte sequence, with * the high-order 5 bits in the low 5 bits of the first byte, and * the low-order six bits in the low 6 bits of the second byte. */ if ((*p & 0xE0) == 0xC0) return (((((unsigned char)*p) & 0x1F) << 6) + (((unsigned char)*(p + 1)) & 0x3F)); /* * Otherwise, we have a three-byte sequence: the high-order 4 bits * are in the low-order 5 bits of the first byte, the next 6 bits * are in the low-order 6 bits of the second byte, and the * low-order 6 bits are in the low-order 6 bits of the third byte. */ return (((((unsigned char)*p) & 0x0F) << 12) + ((((unsigned char)*(p + 1)) & 0x3F) << 6) + (((unsigned char)*(p + 2)) & 0x3F)); } /* * get the character at a given positive character offset from a * byte pointer */ static wchar_t s_getch_at(const char *p, size_t ofs) { /* skip the given number of characters */ for ( ; ofs != 0 ; --ofs, p += s_charsize(*p)) ; /* return the character at the current position */ return s_getch(p); } /* * get the character preceding the current character by the given * number of positions; the offset value must be positive */ static wchar_t s_getch_before(const char *p, size_t ofs) { /* skip backwards the given number of characters */ for ( ; ofs != 0 ; --ofs) { /* * back up by one to three bytes, until we find no more * continuation flags */ --p; p -= s_is_continuation(p); p -= s_is_continuation(p); } /* return the character at the current position */ return s_getch(p); } /* * Write a given wchar_t value to the given byte pointer. The * caller must already have checked (via s_wchar_size) that there's * enough room in the buffer for this character's UTF-8 * representation. * * Returns the number of bytes stored. */ static size_t s_putch(char *p, wchar_t ch) { /* check the range to determine how to encode it */ if (ch <= 0x7f) { /* * it's in the range 0x0000 to 0x007f - encode the low-order * 7 bits in one byte */ *p = (char)(ch & 0x7f); return 1; } else if (ch <= 0x07ff) { /* * It's in the range 0x0080 to 0x07ff - encode it in two * bytes. The high-order 5 bits go in the first byte after * the two-byte prefix of 110, and the low-order 6 bits go in * the second byte after the continuation prefix of 10. */ *p++ = (char)(0xC0 | ((ch >> 6) & 0x1F)); *p = (char)(0x80 | (ch & 0x3F)); return 2; } else { /* * It's in the range 0x0800 to 0xffff - encode it in three * bytes. The high-order 4 bits go in the first byte after * the 1110 prefix, the next 6 bits go in the second byte * after the 10 continuation prefix, and the low-order 6 bits * go in the third byte after another 10 continuation prefix. */ *p++ = (char)(0xE0 | ((ch >> 12) & 0x0F)); *p++ = (char)(0x80 | ((ch >> 6) & 0x3F)); *p = (char)(0x80 | (ch & 0x3F)); return 3; } } /* increment a pointer to a buffer, returning the result */ static char *s_inc(const char *p) { /* * increment the pointer by the size of the current character * and return the result */ return (char *)p + s_charsize(*p); } /* get the size of the character at the given byte pointer */ static size_t s_charsize(char c) { unsigned int ch; /* * Check the top three bits. If the pattern is 111xxxxx, we're * pointing to a three-byte sequence. If the pattern is * 110xxxxx, we're pointing to a two-byte sequence. If it's * 0xxxxxxx, it's a one-byte sequence. * * We're being somewhat clever (tricky, anyway) here at the * expense of clarity. To avoid conditionals, we're doing some * tricky bit masking and shifting, since these operations are * extremely fast on most machines. We figure out our increment * using the bit patterns above to generate masks, then shift * these around to produce 1's or 0's, then add up all of the * mask calculations to get our final increment. * * The size is always at least 1 byte, so we start out with an * increment of 1. * * Next, we note that character sizes other than 1 always * require the high bit to be set. So, the rest is all ANDed * with (byte & 80) shifted right by seven OR'ed to the same * thing shifted right by six, which will give us a bit mask of * 0 when the high bit is clear and 3 when it's set. * * Next, we'll pick out that third bit (xx1xxxxx or xx0xxxxx) by * AND'ing with 0x20. We'll shift this right by 5, to give us 1 * if we have a three-byte sequence. * * We'll then add 1 to this, so we'll have a result of 1 for a * two-byte sequence, 2 for a three-byte sequence. */ ch = (unsigned int)(unsigned char)c; return (1 + ((((ch & 0x80) >> 7) | ((ch & 0x80) >> 6)) & (1 + ((ch & 0x20) >> 5)))); } /* count the number of characters in the given number of bytes */ static size_t s_len(const char *p, size_t bytecnt) { /* get the ending pointer */ const char *end = p + bytecnt; /* increment until we run out of bytes */ size_t cnt; for (cnt = 0 ; p < end ; p = s_inc(p), ++cnt) ; /* return the result */ return cnt; } /* count the number of bytes in the given number of characters */ static size_t s_bytelen(const char *str, size_t charcnt) { /* skip the given number of characters */ const char *p; for (p = str ; charcnt != 0 ; p = s_inc(p), --charcnt) ; /* return the number of bytes we skipped */ return (p - str); } /* * get the number of bytes required to encode a given wchar_t in * UTF-8 format */ static size_t s_wchar_size(wchar_t ch) { /* * characters 0-0x7f take up one byte; characters 0x80-0x7ff * take up two bytes; all others take up three bytes */ return (ch < 0x80 ? 1 : (ch < 0x800 ? 2 : 3)); } /* * get the number of bytes needed for the utf-8 mapping of a * null-terminated wchar_t string */ static size_t s_wstr_size(const wchar_t *str) { /* add up the byte lengths of the characters in 'str' */ size_t bytes = 0; for ( ; *str != 0 ; bytes += s_wchar_size(*str++)) ; /* return the total */ return bytes; } /* decrement a pointer by one character, returning the result */ static char *s_dec(char *p) { /* * Going backwards, we can't tell that we're on a start byte * until we get there - there's no context to tell us which byte * of a multi-byte sequence we're on, except that we can tell * whether or not we're on the first byte or an extra byte. So, * decrement the pointer by a byte; if we're not on a start * byte, decrement by another byte; if we're still not on a * start byte, decrement it again. * * Since the longest possible sequence is three bytes, we'll * unroll the loop and simply check twice to see if we're done * yet. */ --p; p -= s_is_continuation(p); p -= s_is_continuation(p); /* return the result */ return p; } /* * Determine if the current byte is a continuation byte. Returns 1 * if this is a continuation byte, 0 if not. */ static int s_is_continuation(const char *p) { /* * Continuation bytes have the pattern 10xxxxxx. Initial bytes * never have this pattern. So, if a byte ANDed with 0xC0 is * 0x80 (i.e., the high two bits have the exact pattern '10'), * we're on a continuation byte. * * To avoid conditionals, which can be expensive because they * require branching, we'll play more bit mask tricks: we'll * compute a value that's 1 when the high two bits are '10', and * is zero otherwise, and then subtract that from the current * pointer. To figure this value, we'll mask the byte with 0x80 * to pick out the high bit, and shift this right seven bits. * This will give us 1 for 1xxxxxxx. Then, we'll mask the byte * with 0x40, which will pick out the second bit, invert the * resulting bit pattern, AND it again with 0x40, and shift it * right six bits. This will give us 1 for x0xxxxxx. We'll AND * this with the previous calculation, which will give us 1 for * 10xxxxxx and 0 for anything else. */ unsigned int ch = (unsigned int)(unsigned char)*p; return (((ch & 0x80) >> 7) & (((~(ch & 0x40)) & 0x40) >> 6)); } /* * Truncate a string to the given byte length, ensuring that only * whole characters are included in the result. Takes the proposed * truncated length, and returns the actual length to use. The * returned length will be less than or equal to the proposed * length; if the returned length is less than the proposed length, * it means that the proposed length would have cut off a multi-byte * character, so the actual length had to be shorter to ensure that * no bytes of the final character were included. */ static size_t s_trunc(const char *p, size_t len) { /* * if the length is zero, no adjustment is needed - you * obviously can't divide zero bytes */ if (len == 0) return 0; /* * Get a pointer to the start of the last byte within the * proposed truncated byte region. Note that the last byte in * the buffer is at index (len-1), since the byte at index (len) * is the next byte after the truncated region. */ const char *last_ch = p + len - 1; /* * Decrement this byte pointer until we get to the start of the * character that contains the final byte. Since a character * can never be more than three bytes long, we need decrement * our pointer a maximum of two times. */ last_ch -= s_is_continuation(last_ch); last_ch -= s_is_continuation(last_ch); /* * figure the number of bytes of the last character that are * actually in the truncated region - this is simply the number * of bytes from where we are now to the end of the region */ size_t last_ch_len = len - (last_ch - p); /* * Now compute the actual size of this last character. If the * last character's actual size is the same as the truncated * size, then the last character fits exactly and we can return * the proposed length unchanged. If the last character's * required length is more than the truncated length, it means * that the truncation has cut off the last character so that * not all of its bytes fit, and hence we cannot include ANY of * the last character's bytes in the result. */ if (last_ch_len >= s_charsize(*last_ch)) { /* the last character fits in the truncation - we're fine */ return len; } else { /* * the last character doesn't fit - truncate so that none of * the last character's bytes are included */ return (last_ch - p); } } /* * Convert a utf8 string to a wchar_t string, with null termination. * Returns the number of wchar_t characters in the string. If the * given buffer isn't sufficient, we'll only convert as many characters * as will fit, but we'll still return the full length of the string. */ static size_t to_wcharz(wchar_t *buf, size_t bufcnt, const char *str, size_t len) { /* we need at least one character for null termination */ if (bufcnt > 0) { /* convert the string, reserving space for a null */ size_t cnt = to_wchar(buf, bufcnt - 1, str, len); /* add the null termination */ buf[bufcnt-1] = 0; /* return the character count */ return cnt; } else { /* * the output buffer is zero length, so we can't convert * anything and can't null terminate; just calculate the length */ return to_wchar(buf, bufcnt, str, len); } } /* * Convert a utf8 string to a wchar_t string, without null termination. */ static size_t to_wchar(wchar_t *buf, size_t bufcnt, const char *str, size_t len) { /* scan the string */ int cnt = 0; for (utf8_ptr p((char *)str) ; len != 0 ; p.inc(&len), ++cnt) { /* if there's room, add this character to the output buffer */ if (bufcnt > 0) { *buf++ = p.getch(); --bufcnt; } } /* return the character count */ return cnt; } private: /* the buffer pointer */ char *p_; }; /* ------------------------------------------------------------------------ */ /* * Case folding utf8 string reader */ class Utf8FoldStr { public: Utf8FoldStr(const char *p, size_t bytelen) { /* set up at the start of the string */ this->p.set((char *)p); this->rem = bytelen; /* null-terminate our special one-byte identity conversion buffer */ ie[1] = 0; /* start without anything loaded */ fpbase = ie; fp = &ie[1]; } /* get the current byte offset */ const char *getptr() const { return p.getptr(); } /* is a character available? */ int more() const { return rem != 0 || *fp != 0 || fp == ie; } /* are we at a character boundary in the original string? */ int at_boundary() const { return fp != fpbase && *fp == 0; } /* get the next character */ wchar_t getch() { /* if the expansion is exhausted, expand the next source character */ if (fp != ie && *fp == 0) { /* if there's nothing left in the source string, we're done */ if (rem == 0) return 0; /* get the next source character */ wchar_t ch = p.getch(); p.inc(&rem); /* get its case-folded expansion */ if ((fp = t3_to_fold(ch)) == 0) { ie[0] = ch; fpbase = fp = ie; } } /* return the next expansion character */ return *fp++; } private: /* current position in case folding expansion of current 's' character */ const wchar_t *fp; /* start of current expansion */ const wchar_t *fpbase; /* buffer for identity expansions */ wchar_t ie[2]; /* pointer into original string source, and remaining length in bytes */ utf8_ptr p; size_t rem; }; #endif /* UTF8_H */ qtads-2.1.7/tads3/vmanonfn.cpp000066400000000000000000000053761265017072300162170ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmanonfn.cpp - anonymous function metaclass Function Notes Modified 03/21/00 MJRoberts - Creation */ #include "t3std.h" #include "vmobj.h" #include "vmanonfn.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmrun.h" #include "vmstack.h" #include "vmpredef.h" /* ------------------------------------------------------------------------ */ /* * Statics */ /* metaclass registration object */ static CVmMetaclassAnonFn metaclass_reg_obj; CVmMetaclass *CVmObjAnonFn::metaclass_reg_ = &metaclass_reg_obj; /* ------------------------------------------------------------------------ */ /* * create from stack arguments */ vm_obj_id_t CVmObjAnonFn::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id; vm_val_t funcptr; CVmObjAnonFn *new_obj; uint idx; /* at least one argument is required (the function pointer) */ if (argc < 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* retrieve our function pointer argument */ G_stk->pop(&funcptr); if (funcptr.typ == VM_FUNCPTR) { /* it's a regular function pointer - accept it */ } else if (funcptr.typ == VM_OBJ && vm_objp(vmg_ funcptr.val.obj)->get_invoker(vmg_ 0)) { /* it's a pointer to an invokable object - accept it */ } else { /* it's not a valid function pointer */ err_throw(VMERR_FUNCPTR_VAL_REQD); } /* create the new object */ id = vm_new_id(vmg_ FALSE, TRUE, FALSE); /* create the new object, giving it one slot per constructor argument */ new_obj = new (vmg_ id) CVmObjAnonFn(vmg_ argc); /* set the first element to our function pointer */ new_obj->set_element(0, &funcptr); /* set the remaining elements to the context objects */ for (idx = 1 ; idx < argc ; ++idx) { vm_val_t val; /* pop this value */ G_stk->pop(&val); /* set the element */ new_obj->set_element(idx, &val); } /* return the new object ID */ return id; } /* * Invoke */ int CVmObjAnonFn::get_invoker(VMG_ vm_val_t *val) { /* our first vector element is our function pointer */ if (val != 0) { /* get the function pointer */ get_element(0, val); /* if this is itself an invokable value, get its invoker */ if (val->typ == VM_OBJ) return vm_objp(vmg_ val->val.obj)->get_invoker(vmg_ val); } /* we are indeed invokable */ return TRUE; } qtads-2.1.7/tads3/vmanonfn.h000066400000000000000000000076771265017072300156720ustar00rootroot00000000000000/* $Header$ */ /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmanonfn.h - anonymous function metaclass Function The anonymous function metaclass is a subclass of the Vector metaclass that provides encapsulation of an anonymous function and the context it shares with its enclosing scope. Notes Modified 03/21/00 MJRoberts - Creation */ #ifndef VMANONFN_H #define VMANONFN_H #include #include "vmtype.h" #include "vmobj.h" #include "vmvec.h" #include "vmglob.h" /* * Anonymous Function Metaclass */ class CVmObjAnonFn: public CVmObjVector { friend class CVmMetaclassAnonFn; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObjVector::is_of_metaclass(meta)); } /* is the given object an anonymous function object? */ static int is_anonfn_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* we don't look like a list to user code */ int is_listlike(VMG_ vm_obj_id_t) { return FALSE; } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* invoke */ int get_invoker(VMG_ vm_val_t *val); /* check for equality - compare strictly by reference */ int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int) const { /* return true if the other value is a reference to this object */ return (val->typ == VM_OBJ && val->val.obj == self); } /* calculate a hash value */ uint calc_hash(VMG_ vm_obj_id_t self, int) const { /* we compare by reference, so hash by object ID */ return (uint)(((ulong)self & 0xffff) ^ (((ulong)self & 0xffff0000) >> 16)); } protected: /* create an empty object */ CVmObjAnonFn() { ext_ = 0; } /* create with the given number of elements */ CVmObjAnonFn(VMG_ size_t cnt) : CVmObjVector(vmg_ cnt) { /* set to our full initial size */ set_element_count(cnt); } }; /* ------------------------------------------------------------------------ */ /* * Registration table objects */ class CVmMetaclassAnonFn: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "anon-func-ptr/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjAnonFn(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjAnonFn(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjAnonFn::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* fall back directly on the vector static property evaluator */ return CVmObjVector::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* I'm a Vector subclass */ CVmMetaclass *get_supermeta_reg() const { return CVmObjVector::metaclass_reg_; } }; #endif /* VMANONFN_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjAnonFn) qtads-2.1.7/tads3/vmbif.cpp000066400000000000000000000520521265017072300154710ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/vmbif.cpp,v 1.3 1999/07/11 00:46:59 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbif.cpp - built-in function set table implementation Function Notes Modified 12/05/98 MJRoberts - Creation */ #include #include #include "t3std.h" #include "utf8.h" #include "charmap.h" #include "vmtype.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmglob.h" #include "vmbif.h" #include "vmbifreg.h" #include "vmstr.h" #include "vmobj.h" #include "vmrun.h" #include "vmcset.h" #include "vmfilnam.h" /* ------------------------------------------------------------------------ */ /* * Create the function set table with a given number of initial entries */ CVmBifTable::CVmBifTable(size_t init_entries) { /* allocate space for our entries */ if (init_entries != 0) { /* allocate the space */ table_ = (vm_bif_entry_t **) t3malloc(init_entries * sizeof(table_[0])); names_ = (char **) t3malloc(init_entries * sizeof(names_[0])); } else { /* we have no entries */ table_ = 0; names_ = 0; } /* no entries are defined yet */ count_ = 0; /* remember the allocation size */ alloc_ = init_entries; } /* ------------------------------------------------------------------------ */ /* * Delete the table */ CVmBifTable::~CVmBifTable() { /* free the table, if we ever allocated one */ if (table_ != 0) t3free(table_); /* free the function set names list, if we allocated it */ if (names_ != 0) t3free(names_); } /* ------------------------------------------------------------------------ */ /* * Clear all entries from the table */ void CVmBifTable::clear(VMG0_) { size_t i; /* detach each function set */ if (table_ != 0) { for (i = 0 ; i < count_ ; ++i) { if (table_[i] != 0) table_[i]->unload_image(vmg_ i); } } /* delete the 'names' entries, if we allocated any */ if (names_ != 0) { /* free each element */ for (i = 0 ; i < count_ ; ++i) lib_free_str(names_[i]); } /* * Reset the entry counter. Note that this doesn't affect any * allocation; we keep a separate count of the number of table slots * we have allocated. Table slots don't have any additional * associated memory, so we don't need to worry about cleaning * anything up at this point. */ count_ = 0; } /* ------------------------------------------------------------------------ */ /* * Ensure that we have space for a given number of entries */ void CVmBifTable::ensure_space(size_t entries, size_t increment) { /* if we don't have enough space, allocate more */ if (entries >= alloc_) { size_t new_table_size; size_t new_names_size; /* increase the allocation size by the given increment */ alloc_ += increment; /* if it's still too small, bump it up to the required size */ if (alloc_ < entries) alloc_ = entries; /* compute the new sizes */ new_table_size = alloc_ * sizeof(table_[0]); new_names_size = alloc_ * sizeof(names_[0]); /* * if we have a table already, reallocate it at the larger size; * otherwise, allocate a new table */ if (table_ != 0) { table_ = (vm_bif_entry_t **)t3realloc(table_, new_table_size); names_ = (char **)t3realloc(names_, new_names_size); } else { table_ = (vm_bif_entry_t **)t3malloc(new_table_size); names_ = (char **)t3malloc(alloc_ * new_names_size); } } } /* ------------------------------------------------------------------------ */ /* * Add an entry to the table */ void CVmBifTable::add_entry(VMG_ const char *func_set_id) { vm_bif_entry_t *entry; const char *vsn; size_t name_len; /* ensure we have space for one more entry */ ensure_space(count_ + 1, 5); /* find the version suffix in the name, if any */ vsn = lib_find_vsn_suffix(func_set_id, '/', "000000", &name_len); /* look up the function set by name */ for (entry = G_bif_reg_table ; entry->func_set_id != 0 ; ++entry) { const char *entry_vsn; size_t entry_name_len; /* find the version number in this entry */ entry_vsn = lib_find_vsn_suffix(entry->func_set_id, '/', "000000", &entry_name_len); /* see if this is a match */ if (name_len == entry_name_len && memcmp(func_set_id, entry->func_set_id, name_len) == 0) { /* * make sure the version provided in the VM is at least as * high as the requested version */ if (strcmp(vsn, entry_vsn) > 0) { /* it's out of date - throw "function set too old" */ err_throw_a(VMERR_FUNCSET_TOO_OLD, 4, ERR_TYPE_TEXTCHAR, func_set_id, ERR_TYPE_TEXTCHAR, entry_vsn, ERR_TYPE_FUNCSET, func_set_id, ERR_TYPE_VERSION_FLAG); } /* * It's a match - add the new entry. Simply keep a pointer * to the static table entry. */ table_[count_] = entry; /* store the new name element as well */ names_[count_] = lib_copy_str(func_set_id); /* complete the linkage to the image file */ entry->link_to_image(vmg_ count_); /* count the new entry */ ++count_; /* we're done */ return; } } /* we didn't find it - handle it according to our resolution mode */ add_entry_unresolved(vmg_ func_set_id); } /* ------------------------------------------------------------------------ */ /* * Look up an entry by name */ const vm_bif_entry_t *CVmBifTable::get_entry(const char *name) { /* find the version suffix in the name, if any */ size_t namelen; const char *vsn = lib_find_vsn_suffix(name, '/', "000000", &namelen); /* scan the table */ for (size_t i = 0 ; i < count_ ; ++i) { /* find the version number in this entry */ size_t enamelen; const char *evsn = lib_find_vsn_suffix( names_[i], '/', "000000", &enamelen); /* compare names */ if (namelen == enamelen && memcmp(name, names_[i], namelen) == 0) { /* the names match - compare versions */ if (strcmp(vsn, evsn) <= 0) { /* * the loaded version is at least as new as the requested * version, so we have a match */ return table_[i]; } else { /* * this function set is loaded, but the linked version is * older than the requested version, so it counts as not * loaded */ return 0; } } } /* didn't find a match */ return 0; } /* ------------------------------------------------------------------------ */ /* * Validate an entry */ int CVmBifTable::validate_entry(uint set_index, uint func_index) { /* validate that the set index is in range */ if (set_index >= count_) return FALSE; /* get the function set, and make sure it's valid */ vm_bif_entry_t *entry = table_[set_index]; if (entry == 0) return FALSE; /* validate that the function index is in range for the set */ if (func_index > entry->func_count) return FALSE; /* make sure there's a function pointer in the descriptor */ vm_bif_desc *desc = &entry->func[func_index]; if (desc->func == 0) return FALSE; /* it's a valid entry */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Function Set helper functions */ /* * check arguments; throws an error if the argument count doesn't match * the given value */ void CVmBif::check_argc(VMG_ uint argc, uint needed_argc) { if (argc != needed_argc) err_throw(VMERR_WRONG_NUM_OF_ARGS); } /* * check arguments; throws an error if the argument count is outside of * the given range */ void CVmBif::check_argc_range(VMG_ uint argc, uint argc_min, uint argc_max) { if (argc < argc_min || argc > argc_max) err_throw(VMERR_WRONG_NUM_OF_ARGS); } /* * return a string value */ void CVmBif::retval_str(VMG_ const char *str) { retval_str(vmg_ str, strlen(str)); } void CVmBif::retval_str(VMG_ const char *str, size_t len) { vm_obj_id_t str_obj; /* create a string to hold the return value */ str_obj = CVmObjString::create(vmg_ FALSE, str, len); /* return the string */ retval_obj(vmg_ str_obj); } /* * return a string value that came from the UI character set */ void CVmBif::retval_ui_str(VMG_ const char *str) { retval_ui_str(vmg_ str, strlen(str)); } void CVmBif::retval_ui_str(VMG_ const char *str, size_t len) { /* make a string object from the UI string, and return it */ retval_obj(vmg_ str_from_ui_str(vmg_ str, len)); } /* * create a new string object from a string in the UI character set */ vm_obj_id_t CVmBif::str_from_ui_str(VMG_ const char *str) { return str_from_ui_str(vmg_ str, strlen(str)); } vm_obj_id_t CVmBif::str_from_ui_str(VMG_ const char *str, size_t len) { /* figure out how much space we need */ char *outp = 0; size_t outlen = 0; outlen = G_cmap_from_ui->map(0, &outlen, str, len); /* allocate a string of that size */ vm_obj_id_t str_id = CVmObjString::create(vmg_ FALSE, outlen); CVmObjString *str_obj = (CVmObjString *)vm_objp(vmg_ str_id); /* map the string into the new string buffer */ outp = str_obj->cons_get_buf(); G_cmap_from_ui->map(&outp, &outlen, str, len); /* return the new string object */ return str_id; } /* * return an object value */ void CVmBif::retval_obj(VMG_ vm_obj_id_t obj) { if (obj == VM_INVALID_OBJ) G_interpreter->get_r0()->set_nil(); else G_interpreter->get_r0()->set_obj(obj); } /* * return a property value */ void CVmBif::retval_prop(VMG_ vm_prop_id_t prop) { G_interpreter->get_r0()->set_propid(prop); } /* * return an integer value */ void CVmBif::retval_int(VMG_ long val) { G_interpreter->get_r0()->set_int(val); } /* * return true */ void CVmBif::retval_true(VMG0_) { G_interpreter->get_r0()->set_true(); } /* * return nil */ void CVmBif::retval_nil(VMG0_) { G_interpreter->get_r0()->set_nil(); } /* * return a boolean value - nil if false, true if true */ void CVmBif::retval_bool(VMG_ int val) { G_interpreter->get_r0()->set_logical(val); } /* * return a function pointer value */ void CVmBif::retval_fnptr(VMG_ pool_ofs_t ofs) { G_interpreter->get_r0()->set_fnptr(ofs); } /* * return a value */ void CVmBif::retval(VMG_ const vm_val_t *val) { *G_interpreter->get_r0() = *val; } /* * return a built-in function pointer */ void CVmBif::retval_bifptr(VMG_ ushort set_idx, ushort func_idx) { G_interpreter->get_r0()->set_bifptr(set_idx, func_idx); } /* * return the value at top of stack */ void CVmBif::retval_pop(VMG0_) { G_stk->pop(G_interpreter->get_r0()); } /* ------------------------------------------------------------------------ */ /* * Pop a string value */ const char *CVmBif::pop_str_val(VMG0_) { /* pop the value */ vm_val_t val; G_stk->pop(&val); /* interpret it as a string */ return get_str_val(vmg_ &val); } /* get a string value */ const char *CVmBif::get_str_val(VMG_ const vm_val_t *val) { CVmObject *obj; const char *p; /* see what we have */ switch(val->typ) { case VM_SSTRING: /* it's a constant string - get the constant pool pointer */ return G_const_pool->get_ptr(val->val.ofs); case VM_OBJ: /* get the object */ obj = vm_objp(vmg_ val->val.obj); /* get the string value, if it has one */ p = obj->get_as_string(vmg0_); /* if it has a string value, return it */ if (p != 0) return p; /* we didn't get a string value */ break; default: /* other types don't have a string value */ break; } /* if we got here, the value isn't a string */ err_throw(VMERR_STRING_VAL_REQD); AFTER_ERR_THROW(return 0;) } /* ------------------------------------------------------------------------ */ /* * Pop a list value */ const char *CVmBif::pop_list_val(VMG0_) { vm_val_t val; CVmObject *obj; const char *p; /* pop the value */ G_stk->pop(&val); /* see what we have */ switch(val.typ) { case VM_LIST: /* it's a constant list - get the constant pool pointer */ return G_const_pool->get_ptr(val.val.ofs); case VM_OBJ: /* get the object */ obj = vm_objp(vmg_ val.val.obj); /* get the list value, if it has one */ p = obj->get_as_list(); /* if it has a list value, return it */ if (p != 0) return p; /* we didn't get a list value */ break; default: /* other types don't have a list value */ break; } /* if we got here, the value isn't a list */ err_throw(VMERR_LIST_VAL_REQD); AFTER_ERR_THROW(return 0;) } /* ------------------------------------------------------------------------ */ /* * Pop a string into a buffer, and null-terminate the result. */ void CVmBif::pop_str_val_buf(VMG_ char *buf, size_t buflen) { /* pop the top value from the stack and copy its string value */ vm_val_t val; G_stk->pop(&val); get_str_val_buf(vmg_ buf, buflen, &val); } /* * Get a string value into a buffer, null-terminating the result */ void CVmBif::get_str_val_buf(VMG_ char *buf, size_t buflen, const vm_val_t *val) { /* pop the string value */ const char *strp = get_str_val(vmg_ val); /* * get the length, but limit it to our buffer size, less one byte * for null termination */ size_t copy_len = vmb_get_len(strp); if (copy_len > buflen - 1) copy_len = utf8_ptr::s_trunc(strp + VMB_LEN, buflen - 1); /* copy the string */ memcpy(buf, strp + VMB_LEN, copy_len); /* null-terminate the result */ buf[copy_len] = '\0'; } /* ------------------------------------------------------------------------ */ /* * Pop a filename value. The source value can be string or FileName * object. */ void CVmBif::pop_fname_val(VMG_ char *buf, size_t buflen) { get_fname_val(vmg_ buf, buflen, G_stk->get(0)); G_stk->discard(1); } void CVmBif::get_fname_val(VMG_ char *buf, size_t buflen, const vm_val_t *val) { CVmObjFileName *fn; const char *str; if ((fn = vm_val_cast(CVmObjFileName, val)) != 0) { /* get the FileName object's path as a local filename string */ get_str_val_fname(vmg_ buf, buflen, fn->get_path_string()); } else if ((str = val->get_as_string(vmg0_)) != 0) { /* get the string value as a local filename string */ get_str_val_fname(vmg_ buf, buflen, str); } else { err_throw(VMERR_BAD_VAL_BIF); } } /* * Pop a string into a buffer, translating the string into the filename * character set and null-terminating the result. */ void CVmBif::pop_str_val_fname(VMG_ char *buf, size_t buflen) { /* pop the string and translate to a filename */ get_str_val_fname(vmg_ buf, buflen, pop_str_val(vmg0_)); } /* * Get a string into a buffer, translting the string into the filename * character set and null-terminating the result. */ void CVmBif::get_str_val_fname(VMG_ char *buf, size_t buflen, const char *str) { /* get the length */ size_t copy_len = vmb_get_len(str); /* * map it into the local filename character set and store the result * in the output buffer - reserve one byte for the null termination * byte */ copy_len = G_cmap_to_fname->map_utf8(buf, buflen - 1, str + VMB_LEN, copy_len, 0); /* null-terminate the result */ buf[copy_len] = '\0'; } /* * Pop a string into a buffer, translating the string into the user * interface character set and null-terminating the result. */ char *CVmBif::pop_str_val_ui(VMG_ char *buf, size_t buflen) { /* pop the string value */ const char *strp = pop_str_val(vmg0_); /* decode and skip the length prefix */ size_t copy_len = vmb_get_len(strp); strp += VMB_LEN; /* * if they didn't allocate any space for the buffer, allocate one on * the caller's behalf; otherwise map the string into the caller's * buffer */ if (buflen == 0) { /* the caller didn't provide a buffer, so allocate one */ copy_len = G_cmap_to_ui->map_utf8_alo(&buf, strp, copy_len); } else { /* the caller provided a buffer, so use it for the mapping result */ copy_len = G_cmap_to_ui->map_utf8(buf, buflen - 1, strp, copy_len, 0); } /* null-terminate the result */ buf[copy_len] = '\0'; /* return the buffer */ return buf; } /* ------------------------------------------------------------------------ */ /* * Pop an integer value */ int CVmBif::pop_int_val(VMG0_) { /* pop a number */ vm_val_t val; G_interpreter->pop_int(vmg_ &val); /* return the value */ return (int)val.val.intval; } int CVmBif::pop_int_or_nil(VMG_ int defval) { /* check for nil */ if (G_stk->get(0)->typ == VM_NIL) { /* it's nil - discard the nil and return the default value */ G_stk->discard(); return defval; } else { /* not nil - return the integer value */ return pop_int_val(vmg0_); } } /* * Pop a long integer value */ int32_t CVmBif::pop_long_val(VMG0_) { /* pop a number */ vm_val_t val; G_interpreter->pop_int(vmg_ &val); /* return the value */ return val.val.intval; } /* * Pop a true/nil logical value */ int CVmBif::pop_bool_val(VMG0_) { /* pop a value */ vm_val_t val; G_stk->pop(&val); /* check the type */ switch(val.typ) { case VM_NIL: /* nil - interpret this as false */ return FALSE; case VM_TRUE: /* true */ return TRUE; case VM_INT: /* integer - return true if it's nonzero */ return (val.val.intval != 0); default: /* anything else is unacceptable */ err_throw(VMERR_BAD_TYPE_BIF); /* * (for the compiler's benefit, which doesn't know err_throw * doesn't return and thus might want to warn about our failure to * return a value) */ AFTER_ERR_THROW(return FALSE;) } } /* * Pop an object reference value */ vm_obj_id_t CVmBif::pop_obj_val(VMG0_) { /* pop an object reference */ vm_val_t val; G_interpreter->pop_obj(vmg_ &val); /* return the value */ return val.val.obj; } /* * Pop a property ID value */ vm_prop_id_t CVmBif::pop_propid_val(VMG0_) { /* pop a property ID */ vm_val_t val; G_interpreter->pop_prop(vmg_ &val); /* return the value */ return val.val.prop; } /* * Pop a CharacterSet argument. This can also be given as a string with * the name of a character set, in which case we'll create a CharacterSet * object for the given name. */ vm_obj_id_t CVmBif::pop_charset_obj(VMG0_) { /* pop the argument */ vm_val_t val; G_stk->pop(&val); /* get the character set object from the argument value */ return get_charset_obj(vmg_ &val); } vm_obj_id_t CVmBif::get_charset_obj(VMG_ int stk_idx) { /* get the character set object from the value at the stack index */ return get_charset_obj(vmg_ G_stk->get(stk_idx)); } vm_obj_id_t CVmBif::get_charset_obj(VMG_ const vm_val_t *val) { const char *str; /* * check to see if it's a CharacterSet object; if it's not, it must be * a string giving the character set name */ if (val->typ == VM_OBJ && CVmObjCharSet::is_charset(vmg_ val->val.obj)) { /* it's a CharacterSet object - return it */ return val->val.obj; } else if ((str = val->get_as_string(vmg0_)) != 0) { /* it's a string - create a mapper for the given character set name */ return CVmObjCharSet::create( vmg_ FALSE, str + VMB_LEN, vmb_get_len(str)); } else if (val->typ == VM_NIL) { /* nil simply means no character set */ return VM_INVALID_OBJ; } else { /* invalid argument */ err_throw(VMERR_BAD_TYPE_BIF); AFTER_ERR_THROW(return VM_INVALID_OBJ); } } qtads-2.1.7/tads3/vmbif.h000066400000000000000000000367051265017072300151450ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/vmbif.h,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbif.h - built-in function interface Function Provides the interface to built-in function sets. The host application environment may provide more than one set of built-in functions. Each function set is identified by a universally unique identifier; the program image specifies one or more function sets using these identifiers and maps each set to an image-specific index. At run-time, the byte-code program calls built-in functions by specifying a function set index and a function index within the set. A particular function set may use more than one universal identifier, because of version evolution. If new functions are added to a function set, the new function set can continue to use its original universal identifier, as long as the function set continues to provide the original set of functions as a subset of its new vector, and as long as the functions in the original set are at the same index positions in the modified function set. Notes Modified 12/05/98 MJRoberts - Creation */ #ifndef VMBIF_H #define VMBIF_H #include "t3std.h" #include "vmglob.h" #include "vmtype.h" #include "vmfunc.h" /* ------------------------------------------------------------------------ */ /* * Image function set table. We maintain one global function set table, * which we create when we load the image file. */ class CVmBifTable { public: /* * Create a table with a given number of initial entries. The table * may be expanded in the future if necessary, but if the caller can * predict the maximum number of entries required, we can * preallocate the table at its final size and thus avoid the * overhead and memory fragmentation of expanding the table. */ CVmBifTable(size_t init_entries); /* delete the table */ ~CVmBifTable(); /* clear all entries from the table */ void clear(VMG0_); /* * Add an entry to the table, given the function set identifier (a * string giving the universally unique name for the function set). * Fills in the next available slot. Throws an error if the * function set is not present in the system. A function set may * not be present because it's a newer version than this * implementation provides, or because this particular host * application environment does not provide the function set. */ void add_entry(VMG_ const char *func_set_id); /* get the total number of entries in the table */ size_t get_count() const { return count_; } /* get the descriptor for a function; returns null if it's invalid */ const struct vm_bif_desc *get_desc(uint set_index, uint func_index); /* validate an entry */ int validate_entry(uint set_index, uint func_index); /* get the entry at the given index */ const struct vm_bif_entry_t *get_entry(uint idx) { return idx < count_ ? table_[idx] : 0; } /* * Look up an entry by function set name. If the name has a "/nnnnnn" * version suffix, we'll return null if the loaded version is older * than the requested version. */ const struct vm_bif_entry_t *get_entry(const char *name); /* call the given function from the given function set */ void call_func(VMG_ uint set_index, uint func_index, uint argc); private: /* * Ensure we have space for a given number of entries, allocating * more if necessary. If we must allocate more space, we'll * increase the current allocation size by at least the given * increment (more if necessary to bring it up to the required * size). */ void ensure_space(size_t entries, size_t increment); /* * Add an unresolved function set to the table, or throw an error if * this isn't allowed. If we require resolution of function sets at * load time, this should simply throw an error; otherwise, this * should make a null entry in the function set table so that we can * recognize the missing function set if one of its functions is * ever invoked. */ void add_entry_unresolved(VMG_ const char *func_set_id); /* the table array - we keep an array of pointers */ struct vm_bif_entry_t **table_; /* * Name array - this is a list of the global function set names; * each entry corresponds to the entry of the table_ array at the * same index. We use this only for call-time resolution, so that * we can report the name of an unavailable function set when a * function from the unavailable set is invoked. */ char **names_; /* number of entries defined in the table */ ushort count_; /* number of entries allocated for the table */ size_t alloc_; }; /* ------------------------------------------------------------------------ */ /* * Built-in function descriptor. This tells us how to call a function, and * provides reflection data on the function. */ struct vm_bif_desc { /* * The function entrypoint. * * Each function has a common C interface, because the functions take * arguments and return values on the VM stack. The order of the * functions within the vector is defined by the function set * specification; a function set which uses a particular universal * identifier must always conform to the specification for that * universal identifier. * * For each function, 'argc' is the number of actual parameters passed * to the function by the caller. The function receives its parameters * from the VM stack; the first argument is at the top of the stack, * the second argument is the next item on the stack, and so on. The * function must remove all of the arguments from the stack before * returning, and must push a return value if appropriate. */ void (*func)(VMG_ uint argc); /* minimum number of arguments required */ int min_argc; /* additional optional arguments */ int opt_argc; /* are additional variadic arguments allowed? */ int varargs; /* * VM_BIFPTR value for this function. During image file loading, we * fill this in for each function, so that the function can create a * pointer to itself during execution as needed. */ vm_val_t bifptr; /* * Synthetic method header. We build this during the initialization * process to mimic a byte-code method header. This makes bif pointers * fit in with the rest of the type system by letting CVmFuncPtr pull * out reflection information using the same format as for byte-code * methods. */ char synth_hdr[VMFUNC_HDR_MIN_SIZE]; /* fill in the synthetic header */ void init_synth_hdr() { /* set everything to zeros to start with */ memset(synth_hdr, 0, sizeof(synth_hdr)); /* * Fill in the base arguments field. This is an 8-bit field * containing the minimum number of arguments, with the high bit * (0x80) set if it's varargs. */ synth_hdr[0] = (char)(min_argc | (varargs ? 0x80 : 0)); /* set the optional argument count field */ synth_hdr[1] = (char)opt_argc; /* * We can leave all of the other fields zeroed out: * * - locals, total_stack: the caller doesn't allocate a standard * stack frame for a built-in, so these are unused * * - exception_table_ofs: we don't have any bytecode, so we * obviously don't have a bytecode exception table; leave it zeroed * out to indicate the absence of a table * * - debug_ofs: we don't have any debugger information since we * don't have any bytecode; zero indicates no records */ } }; /* ------------------------------------------------------------------------ */ /* * Function set table entry. This contains information on the function * set at one index in the table. */ struct vm_bif_entry_t { /* * Function set identifier - a string of 7-bit ASCII characters, one * byte per character, in the range 32 to 126 inclusive, of length 1 * to 255 characters, null-terminated. */ const char *func_set_id; /* number of functions in the function set */ size_t func_count; /* the function vector */ vm_bif_desc *func; /* "attach" function for the set - perform static initialization */ void (*attach)(VMG0_); /* "detach" function for the set - perform static cleanup */ void (*detach)(VMG0_); /* link to the image file */ void link_to_image(VMG_ ushort set_idx) { /* initialize the function set */ (*attach)(vmg0_); /* link each function entry */ for (ushort i = 0 ; i < func_count ; ++i) { /* set up the bifptr value for the function */ func[i].bifptr.set_bifptr(set_idx, i); /* generate the synthetic method header for the function */ func[i].init_synth_hdr(); } } /* termination */ void unload_image(VMG_ int set_idx) { /* perform static cleanup */ (*detach)(vmg0_); } }; /* ------------------------------------------------------------------------ */ /* * Base class for function set collections. This class provides some * utility functions that intrinsics might find useful. */ class CVmBif { public: /* * Attach to the function set. The image file loader calls this during * program load for each function set linked into the image file. This * gives the function set a chance to to any static initialization. We * only call this when the function set is actually going to be used, * which avoids unnecessarily allocation resources when the function * set isn't needed. * * Note that this is a static function, but it's kind of like a "class * virtual" in that the loader explicitly calls the suitable * Subclass::attach() and Subclass::detach() for each CVmBif subclass * linked from the image file. If you don't define an overriding * static in a subclass, the compiler will simply link to this base * class method. */ static void attach(VMG0_) { } /* * Detach from the function set. The system calls this during program * termination to let the function set do any static cleanup of * resources allocated during attach(). */ static void detach(VMG0_) { } /* * check arguments; throws an error if the argument count doesn't * match the given value */ static void check_argc(VMG_ uint argc, uint needed_argc); /* * check arguments; throws an error if the argument count is outside * of the given range */ static void check_argc_range(VMG_ uint argc, uint argc_min, uint argc_max); /* pop an integer/long value */ static int pop_int_val(VMG0_); static int32_t pop_long_val(VMG0_); /* pop an integer value, or use a default if the argument is nil */ static int pop_int_or_nil(VMG_ int defval); /* pop a true/nil logical value */ static int pop_bool_val(VMG0_); /* pop an object ID value */ static vm_obj_id_t pop_obj_val(VMG0_); /* pop a property ID value */ static vm_prop_id_t pop_propid_val(VMG0_); /* * Pop a string or list value, returning a pointer to the * string/list data. If the value is a constant string or constant * list, as appropriate, we'll return the constant pool pointer; if * the value is an object of metaclass String or List, as * appropriate, we'll return the metaclass data. Throws an error if * the value is of any other type. */ static const char *pop_str_val(VMG0_); static const char *pop_list_val(VMG0_); /* get a value as a string */ static const char *get_str_val(VMG_ const vm_val_t *val); /* * Pop a null-terminated string into the given buffer. If the * string is too long for the buffer, we'll truncate it to the given * size. */ static void pop_str_val_buf(VMG_ char *buf, size_t buflen); static void get_str_val_buf(VMG_ char *buf, size_t buflen, const vm_val_t *val); /* * Pop a string value, returning the result as a filename in the given * buffer. The returned filename is null-terminated and converted to * the local filename character set. The source value must be a * string. */ static void pop_str_val_fname(VMG_ char *buf, size_t buflen); static void get_str_val_fname(VMG_ char *buf, size_t buflen, const char *strp); /* * Pop a filename value, returning the result as a filename in the * given buffer. The returned filename is null-terminated and * converted to the local filename character set. The source value can * be a string or FileName object. */ static void pop_fname_val(VMG_ char *buf, size_t buflen); static void get_fname_val(VMG_ char *buf, size_t buflen, const vm_val_t *val); /* * Pop a null-terminated string into the given buffer, converting the * string to the UI character set. Null-terminates the resulting * string. If the given buffer is null, we'll allocate a buffer with * t3malloc() and return it; the caller is responsible for freeing the * buffer with t3free(). In any case, returns the buffer into which we * store the results. */ static char *pop_str_val_ui(VMG_ char *buf, size_t buflen); /* create a string object from a C-style string in the UI character set */ static vm_obj_id_t str_from_ui_str(VMG_ const char *str); static vm_obj_id_t str_from_ui_str(VMG_ const char *str, size_t len); /* * Get/pop a CharacterSet argument from the stack. If the argument is * an object, it must be a CharacterSet object. If it's a string, * we'll create a CharacterSet object based on the given character set * name. We'll also allow nil if desired. */ static vm_obj_id_t pop_charset_obj(VMG0_); static vm_obj_id_t get_charset_obj(VMG_ int stk_idx); /* get a character set object from a value */ static vm_obj_id_t get_charset_obj(VMG_ const vm_val_t *val); /* * Return a value */ static void retval(VMG_ const struct vm_val_t *val); static void retval_nil(VMG0_); static void retval_true(VMG0_); static void retval_bool(VMG_ int val); static void retval_int(VMG_ long val); static void retval_obj(VMG_ vm_obj_id_t obj); static void retval_prop(VMG_ vm_prop_id_t prop); static void retval_str(VMG_ const char *str); static void retval_str(VMG_ const char *str, size_t len); static void retval_ui_str(VMG_ const char *str); static void retval_ui_str(VMG_ const char *str, size_t len); static void retval_fnptr(VMG_ pool_ofs_t func); static void retval_bifptr(VMG_ ushort set_idx, ushort func_idx); /* return the value at top of stack */ static void retval_pop(VMG0_); }; #endif /* VMBIF_H */ qtads-2.1.7/tads3/vmbifl.cpp000066400000000000000000000043211265017072300156410ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbifl.cpp - built-in function - Load-time resolution Function This is a version of the built-in function interface for resolving built-ins on loading the image file. This version makes no checks on the availability of a function when it's invoked. This version can be used in a normal stand-alone interpreter. A version of the interpreter that's used to complete compilation by running 'preinit' should use the call-time resolution version instead. Notes Modified 07/21/99 MJRoberts - Creation */ #include #include #include "t3std.h" #include "vmtype.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmglob.h" #include "vmbif.h" #include "vmbifreg.h" #include "vmstr.h" #include "vmobj.h" #include "vmrun.h" /* ------------------------------------------------------------------------ */ /* * Call the given function from the given function set. */ void CVmBifTable::call_func(VMG_ uint set_index, uint func_index, uint argc) { /* * find the function set in the registration table, get the function * descriptor from the set's function table, and invoke the function * pointer from the function descriptor */ (*table_[set_index]->func[func_index].func)(vmg_ argc); } /* * Get a function's descriptor */ const vm_bif_desc *CVmBifTable::get_desc(uint set_index, uint func_index) { /* * find the function in the registration table, and return the function * descriptor from the set's function table */ return &table_[set_index]->func[func_index]; } /* * Handle adding a function set entry that's unresolvable at load-time */ void CVmBifTable::add_entry_unresolved(VMG_ const char *func_set_id) { /* this is the static-link version, so an unresolved entry is an error */ err_throw_a(VMERR_UNKNOWN_FUNC_SET, 3, ERR_TYPE_TEXTCHAR, func_set_id, ERR_TYPE_FUNCSET, func_set_id, ERR_TYPE_VERSION_FLAG); } qtads-2.1.7/tads3/vmbifreg.h000066400000000000000000000012471265017072300156340ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/vmbifreg.h,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbifreg.h - built-in function set registry definitions Function Notes Modified 12/05/98 MJRoberts - Creation */ #ifndef VMBIFREG_H #define VMBIFREG_H #include "vmbif.h" /* ------------------------------------------------------------------------ */ /* * Declare the global static table */ extern vm_bif_entry_t G_bif_reg_table[]; #endif /* VMBIFREG_H */ qtads-2.1.7/tads3/vmbifregx.cpp000066400000000000000000000043201265017072300163520ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/vmbifreg.cpp,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbifregx.cpp - built-in function set registry, with tads-io-ext Function Defines the built-in functions that are linked in to this implementation of the VM. This version of the registry includes tads-io-ext, the extended I/O function set. Notes Modified 03/02/2005 MJRoberts - Creation */ #include "vmbifreg.h" /* ------------------------------------------------------------------------ */ /* * Include the function set vector definitions. Define * VMBIF_DEFINE_VECTOR so that the headers all generate vector * definitions. */ #define VMBIF_DEFINE_VECTOR #include "vmbiftad.h" #include "vmbiftio.h" #include "vmbift3.h" #include "vmbiftix.h" // !!! INCLUDE HOST-SPECIFIC FUNCTION SET HEADERS HERE ("vmbifxxx.h") /* done with the vector definition */ #undef VMBIF_DEFINE_VECTOR #define MAKE_ENTRY(entry_name, cls) \ { entry_name, countof(cls::bif_table), cls::bif_table, \ &cls::attach, &cls::detach } /* ------------------------------------------------------------------------ */ /* * The function set registration table. Each entry in the table * provides the definition of one function set, keyed by the function * set's universally unique identifier. */ vm_bif_entry_t G_bif_reg_table[] = { /* T3 VM system function set */ MAKE_ENTRY("t3vm/010006", CVmBifT3), /* T3 VM Testing interface */ MAKE_ENTRY("t3vmTEST/010000", CVmBifT3Test), /* TADS generic data manipulation functions */ MAKE_ENTRY("tads-gen/030008", CVmBifTADS), /* TADS input/output functions */ MAKE_ENTRY("tads-io/030007", CVmBifTIO), /* TADS extended input/output functions (if the platform supports them) */ MAKE_ENTRY("tads-io-ext/030000", CVmBifTIOExt), // !!! ADD ANY HOST-SPECIFIC FUNCTION SETS HERE /* end of table marker */ { 0, 0, 0 } }; /* we don't need the MAKE_ENTRY macro any longer */ #undef MAKE_ENTRY qtads-2.1.7/tads3/vmbift3.cpp000066400000000000000000001117461265017072300157460ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMBIFTAD.CPP,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbift3.cpp - T3 VM system interface function set Function Notes Modified 04/05/99 MJRoberts - Creation */ #include #include #include "utf8.h" #include "vmbif.h" #include "vmbift3.h" #include "vmstack.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmglob.h" #include "vmpool.h" #include "vmobj.h" #include "vmrun.h" #include "vmstr.h" #include "vmvsn.h" #include "vmimage.h" #include "vmlst.h" #include "vmtobj.h" #include "vmfunc.h" #include "vmpredef.h" #include "vmsrcf.h" #include "charmap.h" #include "vmlookup.h" #include "vmimport.h" #include "vmmeta.h" #include "vmfref.h" /* * run the garbage collector */ void CVmBifT3::run_gc(VMG_ uint argc) { /* no arguments are allowed */ check_argc(vmg_ argc, 0); /* run the garbage collector */ G_obj_table->gc_full(vmg0_); } /* * set the SAY instruction's handler function */ #define SETSAY_NO_FUNC 1 #define SETSAY_NO_METHOD 2 void CVmBifT3::set_say(VMG_ uint argc) { vm_val_t *arg = G_stk->get(0); vm_val_t val; /* one argument is required */ check_argc(vmg_ argc, 1); /* check to see if we're setting the default display method */ if (arg->typ == VM_PROP || (arg->typ == VM_INT && arg->val.intval == SETSAY_NO_METHOD)) { vm_prop_id_t prop; /* * the return value is the old property pointer (or * SETSAY_NO_METHOD if there was no valid property set previously) */ prop = G_interpreter->get_say_method(); if (prop != VM_INVALID_PROP) retval_prop(vmg_ prop); else retval_int(vmg_ SETSAY_NO_METHOD); /* get the new value */ G_stk->pop(&val); /* if it's SETSAY_NO_METHOD, set it to the invalid prop ID */ if (val.typ == VM_INT) val.set_propid(VM_INVALID_PROP); /* set the method */ G_interpreter->set_say_method(val.val.prop); } else if (arg->typ == VM_FUNCPTR || arg->typ == VM_OBJ || arg->typ == VM_BIFPTR || (arg->typ == VM_INT && arg->val.intval == SETSAY_NO_FUNC)) { /* * the return value is the old function (or SETSAY_NO_FUNC if the * old function was nil) */ G_interpreter->get_say_func(&val); if (val.typ != VM_NIL) retval(vmg_ &val); else retval_int(vmg_ SETSAY_NO_FUNC); /* get the new function value */ G_stk->pop(&val); /* if it's SETSAY_NO_FUNC, set the function to nil */ if (val.typ == VM_INT) val.set_nil(); /* set the new function */ G_interpreter->set_say_func(vmg_ &val); } else { /* invalid type */ err_throw(VMERR_BAD_TYPE_BIF); } } /* * get the VM version number */ void CVmBifT3::get_vm_vsn(VMG_ uint argc) { /* no arguments are allowed */ check_argc(vmg_ argc, 0); /* set the integer return value */ retval_int(vmg_ T3VM_VSN_NUMBER); } /* * get the VM identification string */ void CVmBifT3::get_vm_id(VMG_ uint argc) { /* no arguments are allowed */ check_argc(vmg_ argc, 0); /* set the integer return value */ retval_str(vmg_ T3VM_IDENTIFICATION); } /* * get the VM banner string */ void CVmBifT3::get_vm_banner(VMG_ uint argc) { /* no arguments are allowed */ check_argc(vmg_ argc, 0); /* return the string */ retval_str(vmg_ T3VM_BANNER_STRING); } /* * get the 'preinit' status - true if preinit, nil if normal */ void CVmBifT3::get_vm_preinit_mode(VMG_ uint argc) { /* no arguments allowed */ check_argc(vmg_ argc, 0); /* return the preinit mode */ retval_int(vmg_ G_preinit_mode); } /* * get the runtime symbol table */ void CVmBifT3::get_global_symtab(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 0, 1); /* if there's an argument, it specifies which table to retrieve */ int which = 1; if (argc >= 1) which = pop_int_val(vmg0_); /* return the desired table */ switch (which) { case 1: /* return the loader's symbol table object, if any */ retval_obj(vmg_ G_image_loader->get_reflection_symtab()); break; case 2: /* return the macro table, if any */ retval_obj(vmg_ G_image_loader->get_reflection_macros()); break; case 3: /* other values are allowed but simply return nil */ retval_nil(vmg0_); break; } } /* * allocate a new property ID */ void CVmBifT3::alloc_new_prop(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 0); /* allocate and return a new property ID */ retval_prop(vmg_ G_image_loader->alloc_new_prop(vmg0_)); } /* * get a stack trace */ #define T3_GST_LOCALS 0x0001 #define T3_GST_FREFS 0x0002 void CVmBifT3::get_stack_trace(VMG_ uint argc) { int single_level = 0; int level; vm_val_t *fp; vm_val_t lst_val; CVmObjList *lst; const uchar *entry_addr; ulong method_ofs; vm_val_t stack_info_cls; int want_named_args = FALSE; int want_locals = FALSE; int want_frefs = FALSE; int flags = 0; const vm_rcdesc *rc; /* check arguments */ check_argc_range(vmg_ argc, 0, 2); /* get the imported stack information class */ stack_info_cls.set_obj(G_predef->stack_info_cls); if (stack_info_cls.val.obj == VM_INVALID_OBJ) { /* * there's no stack information class - we can't return any * meaningful information, so just return nil */ retval_nil(vmg0_); return; } /* * look up T3StackInfo.construct() to determine how many arguments it * wants */ { int min_args, opt_args, varargs; if (vm_objp(vmg_ stack_info_cls.val.obj)->get_prop_interface( vmg_ stack_info_cls.val.obj, G_predef->obj_construct, min_args, opt_args, varargs)) { /* check to see how many extra arguments they want */ want_named_args = (min_args + opt_args >= 7 || varargs); want_locals = (min_args + opt_args >= 8 || varargs); want_frefs = (min_args + opt_args >= 9 || varargs); } } /* check to see if we're fetching a single level or the full trace */ if (argc >= 1) { /* * Get the single level, and adjust to a 0 base. If the level is * nil, we're still getting all levels. */ if (G_stk->get(0)->typ == VM_NIL) { /* it's nil - get all levels */ G_stk->discard(); } else { /* get the level number */ single_level = pop_int_val(vmg0_); /* make sure it's in range */ if (single_level <= 0) err_throw(VMERR_BAD_VAL_BIF); /* we won't need a return list */ lst_val.set_obj_or_nil(VM_INVALID_OBJ); lst = 0; } } /* get the flags argument, if present */ if (argc >= 2) flags = pop_int_val(vmg0_); /* if we're not doing a single level, we need a list for the result */ if (!single_level) { /* * We're returning a full list, so we need to allocate the list for * the return value. First, count stack levels to see how big a * list we'll need. */ fp = G_interpreter->get_frame_ptr(); entry_addr = G_interpreter->get_entry_ptr(); method_ofs = G_interpreter->get_method_ofs(); for (level = 0 ; fp != 0 ; fp = G_interpreter->get_enclosing_frame_ptr(vmg_ fp), ++level) { /* add an extra level for each system call */ if (method_ofs == 0 && entry_addr != 0) ++level; /* get the return address */ entry_addr = G_interpreter->get_enclosing_entry_ptr_from_frame(vmg_ fp); method_ofs = G_interpreter->get_return_ofs_from_frame(vmg_ fp); } /* create the list */ lst_val.set_obj(CVmObjList::create(vmg_ FALSE, level)); lst = (CVmObjList *)vm_objp(vmg_ lst_val.val.obj); /* * we create other objects while building this list, so the gc * could run - clear the list to ensure it contains valid data */ lst->cons_clear(); /* protect the list from garbage collection while we work */ G_stk->push(&lst_val); /* flag that we're doing the whole stack */ single_level = -1; } else { /* adjust the level to a 0-based index */ single_level -= 1; } /* set up at the current function */ fp = G_interpreter->get_frame_ptr(); entry_addr = G_interpreter->get_entry_ptr(); method_ofs = G_interpreter->get_method_ofs(); rc = 0; /* traverse the frames */ for (level = 0 ; fp != 0 ; ++level) { int fr_argc; int i; vm_obj_id_t def_obj; vm_val_t info_self; vm_val_t info_func; vm_val_t info_obj; vm_val_t info_prop; vm_val_t info_args; vm_val_t info_locals; vm_val_t info_srcloc; vm_val_t info_frameref; CVmObjList *arglst; vm_val_t ele; CVmFuncPtr func_ptr; int gc_cnt = 0; int info_argc = 0; /* if we're looking for a single level, and this isn't it, skip it */ if (single_level >= 0 && level != single_level) goto done_with_level; /* * start with the information values to nil - we'll set the * appropriate ones when we find out what we have */ info_func.set_nil(); info_obj.set_nil(); info_prop.set_nil(); info_self.set_nil(); info_locals.set_nil(); info_frameref.set_nil(); /* get the number of arguments to the function in this frame */ fr_argc = G_interpreter->get_argc_from_frame(vmg_ fp); /* set up a function pointer for the method's entry address */ func_ptr.set(entry_addr); /* get the current frame's defining object */ def_obj = G_interpreter->get_defining_obj_from_frame(vmg_ fp); /* check for special method offsets */ switch (method_ofs) { case VMRUN_RET_OP: /* the real return address is one past the last argument */ method_ofs = G_interpreter->get_param_from_frame(vmg_ fp, argc) ->val.intval; break; case VMRUN_RET_OP_ASILCL: /* the real return address is two past the last argument */ method_ofs = G_interpreter->get_param_from_frame(vmg_ fp, argc+1) ->val.intval; break; } /* determine whether it's an object.prop or a function call */ if (method_ofs == 0) { /* * A zero method offset indicates a recursive VM invocation * from a native function. Presume we have no information on * the caller. */ info_self.set_nil(); fr_argc = 0; /* check for a native caller context */ if (rc != 0) { /* check which kind of native caller we have */ if (rc->bifptr.typ != VM_NIL) { /* we have a built-in function at this level */ info_func = rc->bifptr; } else if (rc->self.typ != VM_NIL) { /* it's an intrinsic class method - get the 'self' */ info_obj = info_self = rc->self; /* get the metaclass */ CVmMetaclass *mc; switch (info_obj.typ) { case VM_OBJ: /* get the metaclass from the object */ mc = vm_objp(vmg_ info_obj.val.obj) ->get_metaclass_reg(); break; case VM_LIST: /* list constant - use the List metaclass */ mc = CVmObjList::metaclass_reg_; break; case VM_SSTRING: /* string constant - use the String metaclass */ mc = CVmObjString::metaclass_reg_; break; default: /* other types don't have metaclasses */ mc = 0; break; } /* get the registration table entry */ vm_meta_entry_t *me = mc == 0 ? 0 : G_meta_table->get_entry_from_reg(mc->get_reg_idx()); /* get the metaclass and property from the entry */ if (me != 0) { /* set 'obj' to the IntrinsicClass object */ info_obj.set_obj(me->class_obj_); /* get the property ID */ info_prop.set_propid(me->xlat_func(rc->method_idx)); } } } } else if (def_obj == VM_INVALID_OBJ) { /* there's no defining object, so this is a function call */ func_ptr.get_fnptr(vmg_ &info_func); } else { /* it's an object.prop invocation */ info_obj.set_obj(def_obj); // $$$ walk up to base modified obj? info_prop.set_propid( G_interpreter->get_target_prop_from_frame(vmg_ fp)); /* get the 'self' in this frame */ info_self.set_obj(G_interpreter->get_self_from_frame(vmg_ fp)); } /* * build the argument list and source location, except for system * routines */ if (method_ofs != 0 || rc != 0) { /* allocate a list object to store the argument list */ int ac = (rc != 0 ? rc->argc : fr_argc); info_args.set_obj(CVmObjList::create(vmg_ FALSE, ac)); arglst = (CVmObjList *)vm_objp(vmg_ info_args.val.obj); /* push the argument list for gc protection */ G_stk->push(&info_args); ++gc_cnt; /* build the argument list */ for (i = 0 ; i < ac ; ++i) { /* add this element to the argument list */ const vm_val_t *v = (rc != 0 ? rc->argp - i : G_interpreter->get_param_from_frame(vmg_ fp, i)); arglst->cons_set_element(i, v); } /* get the source location */ get_source_info(vmg_ entry_addr, method_ofs, &info_srcloc); /* * if they want locals, and this isn't a recursive native * caller, retrieve them */ if (rc == 0 && (((flags & T3_GST_LOCALS) != 0 && want_locals) || ((flags & T3_GST_FREFS) != 0 && want_frefs))) { /* get the locals */ get_stack_locals(vmg_ fp, entry_addr, method_ofs, (flags & T3_GST_LOCALS) != 0 && want_locals ? &info_locals : 0, (flags & T3_GST_FREFS) != 0 && want_frefs ? &info_frameref : 0); /* * that leaves the LookupTable and StackFrameDesc on the * stack, so note that we need to discard the stack level * when we're done with it */ if (info_locals.typ == VM_OBJ) ++gc_cnt; if (info_frameref.typ == VM_OBJ) ++gc_cnt; } } else { /* * it's a system routine - no argument information is * available, so return nil rather than an empty list to to * indicate the absence */ info_args.set_nil(); /* there's obviously no source location for system code */ info_srcloc.set_nil(); } /* * We have all of the information on this level now, so create the * information object for the level. This is an object of the * exported stack-info class, which is a TadsObject type. */ /* start with the original complement of arguments */ info_argc = 7; /* * if we have a modern T3StackInfo object, push the locals, * named argument elements, and frame reference object */ if (want_frefs) { G_stk->push(&info_frameref); ++info_argc; } if (want_named_args) { /* * the constructor has a slot for named arguments - push either * a table or nil, depending... */ vm_val_t *argp; const uchar *t = 0; /* if the flags request locals, retrieve the named arguments */ if ((flags & T3_GST_LOCALS) != 0) t = CVmRun::get_named_args_from_frame(vmg_ fp, &argp); /* * if we do in fact have named arguments, build a lookup table * copy and push it; otherwise just push nil */ if (t != 0) { /* get the number of table entries */ int n = osrp2(t); t += 2; /* create a lookup table for the arguments */ G_stk->push()->set_obj(CVmObjLookupTable::create( vmg_ FALSE, n <= 8 ? 8 : n <= 32 ? 32 : 64, n)); CVmObjLookupTable *lt = (CVmObjLookupTable *)vm_objp( vmg_ G_stk->get(0)->val.obj); /* * Populate the lookup table with the named arguments. The * compiler builds the table in the order pushed, which is * right to left. Lookup tables preserve the order in * which elements are added, and reflect this order in key * lists, so to that extent the order of building the * lookup table matters. For readability of the generated * list, in case it's presented to the user, build the * table in left-to-right order, which is the reverse of * the table order in the bytecode table. */ argp += n - 1; for (int i = (n-1)*2 ; i >= 0 ; i -= 2, --argp) { /* get the name pointer and length from the index */ uint ofs = osrp2(t + i), nxtofs = osrp2(t + i + 2); const char *name = (const char *)t + ofs; size_t len = nxtofs - ofs; /* create a string from the name */ vm_val_t str; str.set_obj(CVmObjString::create(vmg_ FALSE, name, len)); /* add it to the table */ lt->add_entry(vmg_ &str, argp); } } else { /* there are no named arguments - push nil */ G_stk->push()->set_nil(); } /* count the argument */ ++info_argc; } if (want_locals) { G_stk->push(&info_locals); ++info_argc; } /* push the rest of the arguments */ G_stk->push(&info_srcloc); G_stk->push(&info_args); G_stk->push(&info_self); G_stk->push(&info_prop); G_stk->push(&info_obj); G_stk->push(&info_func); G_stk->push(&stack_info_cls); ele.set_obj(CVmObjTads::create_from_stack(vmg_ 0, info_argc)); /* discard the gc protection items */ G_stk->discard(gc_cnt); /* * if we're fetching a single level, this is it - return the new * stack info object and we're done */ if (single_level >= 0) { /* return the single level object */ retval_obj(vmg_ ele.val.obj); /* we're done */ return; } /* add the new element to our list */ lst->cons_set_element(level, &ele); done_with_level: /* * If this is a system call level, and we're not in debug mode, * this recursive frame contains the entry address for the caller, * but not the calling byte-code address. Stay on the current * level in this case. */ if (method_ofs == 0 && entry_addr != 0) { /* * This is a recursive caller, and we have a valid entry * address for the prior frame. Stay in the current frame, and * retrieve the actual return address from the calling frame. */ if (rc != 0) { /* get the actual return address from the recursive context */ method_ofs = rc->get_return_addr() - entry_addr; /* * we're now in the bytecode part of the frame, so forget * the recursive context */ rc = 0; } else { /* no recursive context - use a fake return address */ method_ofs = G_interpreter->get_funchdr_size(); } } else { /* move up to the enclosing frame */ entry_addr = G_interpreter->get_enclosing_entry_ptr_from_frame(vmg_ fp); method_ofs = G_interpreter->get_return_ofs_from_frame(vmg_ fp); rc = G_interpreter->get_rcdesc_from_frame(vmg_ fp); fp = G_interpreter->get_enclosing_frame_ptr(vmg_ fp); } } /* return the list */ retval_obj(vmg_ lst_val.val.obj); /* discard our gc protection */ G_stk->discard(); } /* * Get the local variable table for a given stack level. We'll fill in * 'retval' with a LooupTable object containing the local values keyed by * symbol name, or nil if we can't find the locals. We'll also push the * object onto the stack for gc protection, so the caller must pop it when * done with it. */ void CVmBifT3::get_stack_locals(VMG_ vm_val_t *fp, const uchar *entry_addr, ulong method_ofs, vm_val_t *local_tab, vm_val_t *frameref_obj) { /* presume we won't be able to find source information for the location */ if (local_tab != 0) local_tab->set_nil(); if (frameref_obj != 0) frameref_obj->set_nil(); /* we haven't found any relevant frames yet */ int best = 0; /* set up a debug table pointer for the function or method */ CVmDbgTablePtr dp; if (dp.set(entry_addr)) { /* search for the innermost frame containing the method offset */ int frame_cnt = dp.get_frame_count(vmg0_); for (int i = 1 ; i <= frame_cnt ; ++i) { /* get this frame */ CVmDbgFramePtr fp; dp.set_frame_ptr(vmg_ &fp, i); /* check to see if this contains method_ofs */ uint start_ofs = fp.get_start_ofs(vmg0_); uint end_ofs = fp.get_end_ofs(vmg0_); if (method_ofs >= start_ofs && method_ofs <= end_ofs) { /* * This is a relevant frame. If we don't have any frames * yet, or this one is nested within the best we've found * so far, this is the best frame so far. */ if (best == 0 || fp.is_nested_in(vmg_ &dp, best)) best = i; } } } /* get the frame-reference, if the caller wants one */ if (frameref_obj != 0) { /* get the slot */ vm_val_t *fro = G_interpreter->get_frameref_slot(vmg_ fp); /* if there's not already a StackFrameRef object there, create one */ if (fro->typ == VM_NIL) { /* create the StackFrameRef and store it in the stack slot */ fro->set_obj(CVmObjFrameRef::create(vmg_ fp, entry_addr)); } /* create the StackFrameDesc to return to the caller */ frameref_obj->set_obj(CVmObjFrameDesc::create( vmg_ fro->val.obj, best, method_ofs)); /* push it for gc protection */ G_stk->push(frameref_obj); } /* if they didn't want the local variable table, we're done */ if (local_tab == 0) return; /* create a lookup table for the locals */ local_tab->set_obj(CVmObjLookupTable::create(vmg_ FALSE, 32, 64)); CVmObjLookupTable *t = (CVmObjLookupTable *)vm_objp(vmg_ local_tab->val.obj); /* save it on the stack for gc protection */ G_stk->push(local_tab); /* if we didn't find a best frame, there are no locals to retrieve */ if (best == 0) return; /* set up a pointer to our winning frame */ CVmDbgFramePtr dfp; dp.set_frame_ptr(vmg_ &dfp, best); /* walk up the list of frames from the innermost to outermost */ for (;;) { /* set up a pointer to the first symbol */ CVmDbgFrameSymPtr symp; dfp.set_first_sym_ptr(vmg_ &symp); /* scan this frame's local symbol list */ int sym_cnt = dfp.get_sym_count(); for (int i = 0 ; i < sym_cnt ; ++i, symp.inc(vmg0_)) { vm_val_t name, val; /* get the symbol name */ symp.get_str_val(vmg_ &name); /* * if this symbol is already in the table, skip it - we work * from inner to outer frames, and inner frames hide symbols in * outer frames */ if (t->index_check(vmg_ &val, &name)) continue; /* get the value from the frame */ G_interpreter->get_local_from_frame(vmg_ &val, fp, &symp); /* add this to the lookup table */ t->add_entry(vmg_ &name, &val); } /* get the enclosing frame - 0 means no parent */ int parent = dfp.get_enclosing_frame(); if (parent == 0) break; /* set up dfp to point to the parent frame */ dp.set_frame_ptr(vmg_ &dfp, parent); } } /* * Get the source file information for a given code pool offset. If debug * records aren't available for the given location, returns nil. Returns * a list containing the source file information: the first element is a * string giving the name of the file, and the second element is an * integer giving the line number in the file. Returns nil if no source * information is available for the given byte code location. */ void CVmBifT3::get_source_info(VMG_ const uchar *entry_addr, ulong method_ofs, vm_val_t *retval) { CVmFuncPtr func_ptr; CVmDbgLinePtr line_ptr; const uchar *stm_start, *stm_end; CVmObjList *lst; vm_val_t ele; CVmSrcfEntry *srcf; CVmObjString *str; const char *fname; size_t map_len; /* presume we won't be able to find source information for the location */ retval->set_nil(); /* set up a debug table pointer for the function or method */ func_ptr.set(entry_addr); /* get the debug information for the given location */ if (!CVmRun::get_stm_bounds(vmg_ &func_ptr, method_ofs, &line_ptr, &stm_start, &stm_end)) { /* no source information available - return failure */ return; } /* get the source file record - if we can't find it, return failure */ srcf = (G_srcf_table != 0 ? G_srcf_table->get_entry(line_ptr.get_source_id()) : 0); if (srcf == 0) return; /* * Create a list for the return value. The return list has two * elements: the name of the source file containing this code, and the * line number in the file. */ retval->set_obj(CVmObjList::create(vmg_ FALSE, 2)); lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* clear the list, in case gc runs during construction */ lst->cons_clear(); /* push the list for gc protection */ G_stk->push(retval); /* get the filename string */ fname = srcf->get_name(); /* * determine how long the string will be when translated to utf8 from * the local filename character set */ map_len = G_cmap_from_fname->map_str(0, 0, fname); /* * create a string value to hold the filename, and store it in the * first element of the return list (note that this automatically * protects the new string from garbage collection, by virtue of the * list referencing the string and the list itself being protected) */ ele.set_obj(CVmObjString::create(vmg_ FALSE, map_len)); lst->cons_set_element(0, &ele); /* map the string into the buffer we allocated for it */ str = (CVmObjString *)vm_objp(vmg_ ele.val.obj); G_cmap_from_fname->map_str(str->cons_get_buf(), map_len, fname); /* set the second element of the list to the source line number */ ele.set_int(line_ptr.get_source_line()); lst->cons_set_element(1, &ele); /* discard our gc protection */ G_stk->discard(); } /* * Look up a named argument. If 'mandatory' is set, we throw an error if * we can't find a resolution. */ void CVmBifT3::get_named_arg(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 1, 2); /* get the name we're looking for */ const char *name = G_stk->get(0)->get_as_string(vmg0_); if (name == 0) err_throw(VMERR_STRING_VAL_REQD); /* get the length and buffer pointer */ size_t namelen = vmb_get_len(name); name += VMB_LEN; /* * Scan the stack for named parameter tables. A named parameter table * is always in the calling frame at the stack slot just beyond the * last argument. */ for (vm_val_t *fp = G_interpreter->get_frame_ptr() ; fp != 0 ; fp = G_interpreter->get_enclosing_frame_ptr(vmg_ fp)) { /* check for a table in this frame */ vm_val_t *argp; const uchar *t = CVmRun::get_named_args_from_frame(vmg_ fp, &argp); if (t != 0) { /* get the number of table entries */ int n = osrp2(t); t += 2; /* scan the table for the name */ for (int i = 0 ; n >= 0 ; --n, i += 2, ++argp) { /* get this element's offset, and figure its length */ uint eofs = osrp2(t + i); uint elen = osrp2(t + i + 2) - eofs; /* check for a match */ if (elen == namelen && memcmp(name, t + eofs, elen) == 0) { /* found it - return the value */ retval(vmg_ argp); /* discard arguments and return */ G_stk->discard(argc); return; } } } } /* * The argument is undefined. If a default value was supplied, simply * return the default value. Otherwise throw an error. */ if (argc >= 2) { /* a default value was supplied - simply return it */ retval(vmg_ G_stk->get(1)); /* discard arguments */ G_stk->discard(argc); } else { /* no default value - throw an error */ err_throw_a(VMERR_MISSING_NAMED_ARG, 1, ERR_TYPE_TEXTCHAR_LEN, name, namelen); } } /* * Retrieve a list of the named argument names. */ void CVmBifT3::get_named_arg_list(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 0); /* create the result list; we'll expand as necessary later */ G_stk->push()->set_obj(CVmObjList::create(vmg_ FALSE, 10)); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ G_stk->get(0)->val.obj); /* clear it out, since we're building it incrementally */ lst->cons_clear(); /* we haven't added any elements yet */ int idx = 0; /* scan the stack and populate the name list from the tables we find */ for (vm_val_t *fp = G_interpreter->get_frame_ptr() ; fp != 0 ; fp = G_interpreter->get_enclosing_frame_ptr(vmg_ fp)) { /* look for a named argument table in this frame */ vm_val_t *argp; const uchar *t = CVmRun::get_named_args_from_frame(vmg_ fp, &argp); if (t != 0) { /* get the number of table entries */ int n = osrp2(t); t += 2; /* * Build the list. The compiler generates the list in * right-to-left order (the order of pushing the arguments). * For readability, reverse this: generate the list left to * right, so that it appears in the original source code order. */ argp += n - 1; for (int i = (n-1)*2 ; i >= 0 ; i -= 2, --argp) { /* get this string's offset and figure its length */ uint ofs = osrp2(t + i); uint len = osrp2(t + i + 2) - ofs; /* create a string from the name */ vm_val_t str; str.set_obj(CVmObjString::create( vmg_ FALSE, (const char *)t + ofs, len)); /* add it to the list */ lst->cons_ensure_space(vmg_ idx, 10); lst->cons_set_element(idx, &str); ++idx; } } } /* set the final list length */ lst->cons_set_len(idx); /* keep only the unique elements */ lst->cons_uniquify(vmg0_); /* return the results */ retval_pop(vmg0_); } /* ------------------------------------------------------------------------ */ /* * T3 VM Test function set. This function set contains internal test * and debug functions. These functions are not meant for use by * "normal" programs - they provide internal access to certain VM state * that is not useful or meaningful except for testing and debugging the * VM itself. */ /* * Get an object's internal ID. Takes an object instance and returns an * integer giving the object's VM ID number. This is effectively an * address that can be used to refer to the object. Because this value * is returned as an integer, it is NOT a reference to the object for * the purposes of garbage collection or finalization. */ void CVmBifT3Test::get_obj_id(VMG_ uint argc) { vm_val_t val; /* one argument required */ check_argc(vmg_ argc, 1); /* get the object value */ G_interpreter->pop_obj(vmg_ &val); /* return the object ID as an integer */ retval_int(vmg_ (long)val.val.obj); } /* * Get an object's garbage collection state. Takes an object ID (NOT an * object reference -- this is the integer value returned by get_obj_id) * and returns a bit mask with the garbage collector state. * * (retval & 0x000F) gives the free state. 0 is free, 1 is in use. * * (retval & 0x00F0) gives the reachable state. 0x00 is unreachable, * 0x10 is finalizer-reachable, and 0x20 is fully reachable. * * (retval & 0x0F00) gives the finalizer state. 0x000 is unfinalizable, * 0x100 is finalizable, and 0x200 is finalized. * * (retval & 0xF000) gives the object ID validity. 0 is valid, 0xF000 * is invalid. */ void CVmBifT3Test::get_obj_gc_state(VMG_ uint argc) { vm_val_t val; /* one argument required */ check_argc(vmg_ argc, 1); /* pop the string */ G_interpreter->pop_int(vmg_ &val); /* return the internal garbage collector state of the object */ retval_int(vmg_ (long)G_obj_table->get_obj_internal_state(val.val.intval)); } /* * Get the Unicode character code of the first character of a string */ void CVmBifT3Test::get_charcode(VMG_ uint argc) { const char *str; /* one argument required */ check_argc(vmg_ argc, 1); /* get the object ID as an integer */ str = pop_str_val(vmg0_); /* * if the string is empty, return nil; otherwise, return the Unicode * character code of the first character */ if (vmb_get_len(str) == 0) { /* empty string - return nil */ retval_nil(vmg0_); } else { /* * get the character code of the first character and return it * as an integer */ retval_int(vmg_ (int)utf8_ptr::s_getch(str + VMB_LEN)); } } qtads-2.1.7/tads3/vmbift3.h000066400000000000000000000102611265017072300154010ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/vmbiftad.h,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbift3.h - function set definition - T3 VM system interface Function Notes Modified 12/06/98 MJRoberts - Creation */ #ifndef VMBIFTAD_H #define VMBIFTAD_H #include "vmbif.h" #endif /* VMBIFTAD_H */ /* ------------------------------------------------------------------------ */ /* * T3 VM function set intrinsics */ class CVmBifT3: public CVmBif { public: /* function vector */ static vm_bif_desc bif_table[]; /* run the garbage collector */ static void run_gc(VMG_ uint argc); /* set the SAY instruction's handler function */ static void set_say(VMG_ uint argc); /* get the VM version number */ static void get_vm_vsn(VMG_ uint argc); /* get the VM identification string */ static void get_vm_id(VMG_ uint argc); /* get the VM banner string */ static void get_vm_banner(VMG_ uint argc); /* get the 'preinit' status - true if preinit, nil if normal */ static void get_vm_preinit_mode(VMG_ uint argc); /* debug trace functions */ static void debug_trace(VMG_ uint argc); /* get the global symbol table, if available */ static void get_global_symtab(VMG_ uint argc); /* allocate a new property ID */ static void alloc_new_prop(VMG_ uint argc); /* get a stack trace */ static void get_stack_trace(VMG_ uint argc); /* look up a named argument */ static void get_named_arg(VMG_ uint argc); /* get a named argument list */ static void get_named_arg_list(VMG_ uint argc); protected: /* * service routine - retrieve source information for a given code * location */ static void get_source_info(VMG_ const uchar *entry_addr, ulong method_ofs, vm_val_t *retval); /* get the local variables for a stack level */ static void get_stack_locals(VMG_ vm_val_t *fp, const uchar *entry_addr, ulong method_ofs, vm_val_t *local_tab, vm_val_t *frameref_obj); }; /* * T3 VM Test function set - test and debug function set */ class CVmBifT3Test: public CVmBif { public: /* function vector */ static vm_bif_desc bif_table[]; /* get an instance's unique identifier number */ static void get_obj_id(VMG_ uint argc); /* get an instance's garbage collection state */ static void get_obj_gc_state(VMG_ uint argc); /* get the Unicode character code for the first character of a string */ static void get_charcode(VMG_ uint argc); }; /* ------------------------------------------------------------------------ */ /* * debug_trace mode flags */ /* check to see if the debugger is present */ #define T3DBG_CHECK 1 /* break into the debugger */ #define T3DBG_BREAK 2 /* write a message to the debug log */ #define T3DBG_LOG 3 /* ------------------------------------------------------------------------ */ /* * Function set vector. Define this only if VMBIF_DEFINE_VECTOR has * been defined, so that this file can be included for the prototypes * alone without defining the function vector. */ #ifdef VMBIF_DEFINE_VECTOR vm_bif_desc CVmBifT3::bif_table[] = { { &CVmBifT3::run_gc, 0, 0, FALSE }, { &CVmBifT3::set_say, 0, 0, FALSE }, { &CVmBifT3::get_vm_vsn, 0, 0, FALSE }, { &CVmBifT3::get_vm_id, 0, 0, FALSE }, { &CVmBifT3::get_vm_banner, 0, 0, FALSE }, { &CVmBifT3::get_vm_preinit_mode, 0, 0, FALSE }, { &CVmBifT3::debug_trace, 1, 0, TRUE }, { &CVmBifT3::get_global_symtab, 0, 0, FALSE }, { &CVmBifT3::alloc_new_prop, 0, 0, FALSE }, { &CVmBifT3::get_stack_trace, 0, 1, FALSE }, { &CVmBifT3::get_named_arg, 1, 1, FALSE }, { &CVmBifT3::get_named_arg_list, 0, 0, FALSE } }; vm_bif_desc CVmBifT3Test::bif_table[] = { { &CVmBifT3Test::get_obj_id, 1, 0, FALSE }, { &CVmBifT3Test::get_obj_gc_state, 1, 0, FALSE }, { &CVmBifT3Test::get_charcode, 1, 0, FALSE } }; #endif /* VMBIF_DEFINE_VECTOR */ qtads-2.1.7/tads3/vmbiftad.cpp000066400000000000000000004050011265017072300161560ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMBIFTAD.CPP,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbiftad.cpp - TADS built-in function set for T3 VM Function Notes Modified 04/05/99 MJRoberts - Creation */ #include #include #include #include "t3std.h" #include "os.h" #include "utf8.h" #include "vmuni.h" #include "vmbiftad.h" #include "vmstack.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmglob.h" #include "vmpool.h" #include "vmobj.h" #include "vmstr.h" #include "vmlst.h" #include "vmrun.h" #include "vmregex.h" #include "vmundo.h" #include "vmfile.h" #include "vmsave.h" #include "vmbignum.h" #include "vmfunc.h" #include "vmpat.h" #include "vmtobj.h" #include "vmimport.h" #include "vmpredef.h" #include "vmlookup.h" #include "vmfilobj.h" #include "vmnetfil.h" #include "vmbytarr.h" #include "vmcrc.h" #include "vmfindrep.h" /* ------------------------------------------------------------------------ */ /* * Initialize the TADS intrinsics global state */ CVmBifTADSGlobals::CVmBifTADSGlobals(VMG0_) { /* allocate our regular expression parser */ rex_parser = new CRegexParser(); rex_searcher = new CRegexSearcherSimple(rex_parser); /* * Allocate a global variable to hold the most recent regular * expression search string. We need this in a global so that the last * search string is always protected from garbage collection; we must * keep the string because we might need it to extract a group-match * substring. */ last_rex_str = G_obj_table->create_global_var(); /* ISAAC is the default RNG */ rng_id = VMBT_RNGID_ISAAC; /* * Set the random number seed to a fixed starting value (this value * is arbitrary; we chose it by throwing dice). If the program * wants another sequence, it can manually change this by calling * the randomize() intrinsic in our function set, which seeds the * generator with an OS-dependent starting value (usually based on * the system's real-time clock, to ensure that each run will use a * different starting value). */ lcg_rand_seed = 024136543305; /* create the ISAAC context structure */ isaac_ctx = (struct isaacctx *)t3malloc(sizeof(struct isaacctx)); /* initialize with a fixed seed vector */ isaac_init(isaac_ctx, FALSE); /* create the Mersenne Twister object */ mt_ctx = new CVmMT19937(); } /* * delete the TADS intrinsics global state */ CVmBifTADSGlobals::~CVmBifTADSGlobals() { /* delete our regular expression searcher and parser */ delete rex_searcher; delete rex_parser; /* * note that we leave our last_rex_str global variable undeleted here, * as we don't have access to G_obj_table (as there's no VMG_ to a * destructor); this is okay, since the object table will take care of * deleting the variable for us when the object table itself is deleted */ /* delete the ISAAC context */ t3free(isaac_ctx); /* delete the Mersenne Twister context */ delete mt_ctx; } /* ------------------------------------------------------------------------ */ /* * datatype - get the datatype of a given value */ void CVmBifTADS::datatype(VMG_ uint argc) { vm_val_t val; vm_val_t retval; /* check arguments */ check_argc(vmg_ argc, 1); /* pop the value */ G_stk->pop(&val); /* return the appropriate value for this type */ retval.set_datatype(vmg_ &val); retval_int(vmg_ retval.val.intval); } /* ------------------------------------------------------------------------ */ /* * getarg - get the given argument to the current procedure */ void CVmBifTADS::getarg(VMG_ uint argc) { int idx; /* check arguments */ check_argc(vmg_ argc, 1); /* get the argument index value */ idx = pop_int_val(vmg0_); /* if the argument index is out of range, throw an error */ if (idx < 1 || idx > G_interpreter->get_cur_argc(vmg0_)) err_throw(VMERR_BAD_VAL_BIF); /* return the parameter value */ *G_interpreter->get_r0() = *G_interpreter->get_param(vmg_ idx - 1); } /* ------------------------------------------------------------------------ */ /* * firstobj - get the first object instance */ void CVmBifTADS::firstobj(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 0, 2); /* enumerate objects starting with object 1 in the master object table */ enum_objects(vmg_ argc, (vm_obj_id_t)1); } /* * nextobj - get the next object instance after a given object */ void CVmBifTADS::nextobj(VMG_ uint argc) { vm_val_t val; vm_obj_id_t prv_obj; /* check arguments */ check_argc_range(vmg_ argc, 1, 3); /* get the previous object */ G_interpreter->pop_obj(vmg_ &val); prv_obj = val.val.obj; /* * Enumerate objects starting with the next object in the master * object table after the given object. Reduce the argument count by * one, since we've removed the preceding object. */ enum_objects(vmg_ argc - 1, prv_obj + 1); } /* enum_objects flags */ #define VMBIFTADS_ENUM_INSTANCES 0x0001 #define VMBIFTADS_ENUM_CLASSES 0x0002 /* * Common handler for firstobj/nextobj object iteration */ void CVmBifTADS::enum_objects(VMG_ uint argc, vm_obj_id_t start_obj) { vm_val_t val; vm_obj_id_t sc; vm_obj_id_t obj; unsigned long flags; /* presume no superclass filter will be specified */ sc = VM_INVALID_OBJ; /* presume we're enumerating instances only */ flags = VMBIFTADS_ENUM_INSTANCES; /* * check arguments - we can optionally have two more arguments: a * superclass whose instances/subclasses we are to enumerate, and an * integer giving flag bits */ if (argc == 2) { /* pop the object */ G_interpreter->pop_obj(vmg_ &val); sc = val.val.obj; /* pop the flags */ flags = pop_long_val(vmg0_); } else if (argc == 1) { /* check to see if it's an object or the flags integer */ switch (G_stk->get(0)->typ) { case VM_INT: /* it's the flags */ flags = pop_long_val(vmg0_); break; case VM_OBJ: /* it's the superclass filter */ G_interpreter->pop_obj(vmg_ &val); sc = val.val.obj; break; default: /* invalid argument type */ err_throw(VMERR_BAD_TYPE_BIF); } } /* presume we won't find anything */ retval_nil(vmg0_); /* * starting with the given object, scan objects until we find one * that's valid and matches our superclass, if one was provided */ for (obj = start_obj ; obj < G_obj_table->get_max_used_obj_id() ; ++obj) { /* * If it's valid, and it's not an intrinsic class modifier object, * consider it further. Skip intrinsic class modifiers, since * they're not really separate objects; they're really part of the * intrinsic class they modify, and all of the properties and * methods of a modifier object are reachable through the base * intrinsic class. */ if (G_obj_table->is_obj_id_valid(obj) && !CVmObjIntClsMod::is_intcls_mod_obj(vmg_ obj)) { /* * if it's a class, skip it if the flags indicate classes are * not wanted; if it's an instance, skip it if the flags * indicate that instances are not wanted */ if (vm_objp(vmg_ obj)->is_class_object(vmg_ obj)) { /* it's a class - skip it if classes are not wanted */ if ((flags & VMBIFTADS_ENUM_CLASSES) == 0) continue; } else { /* it's an instance - skip it if instances are not wanted */ if ((flags & VMBIFTADS_ENUM_INSTANCES) == 0) continue; } /* * if a superclass was specified, and it matches, we have a * winner */ if (sc != VM_INVALID_OBJ) { /* if the object matches, return it */ if (vm_objp(vmg_ obj)->is_instance_of(vmg_ sc)) { retval_obj(vmg_ obj); break; } } else { /* * We're enumerating all objects - but skip List and String * object, as we expose these as special types. */ if (vm_objp(vmg_ obj)->get_as_list() == 0 && vm_objp(vmg_ obj)->get_as_string(vmg0_) == 0) { retval_obj(vmg_ obj); break; } } } } } /* ------------------------------------------------------------------------ */ /* * Abstract RNG algorithm interface */ class IVmBifTadsRNG { public: virtual ~IVmBifTadsRNG() { } /* get the next random number */ virtual uint32_t rand() = 0; /* seed the generator from random data */ virtual void seed_random() = 0; /* seed from an int */ virtual void seed_int(int32_t i) { /* by default, turn this into a string and seed with the string */ char buf[40]; oswp4(buf, i); t3sprintf(buf+4, sizeof(buf)-4, "%ld", (long)i); seed_str(buf, 4 + strlen(buf+4)); } /* seed from a string */ virtual void seed_str(const char *str, size_t len) { /* by default, hash the string to an int and seed with the int */ CVmCRC32 crc; crc.scan_bytes(str, len); seed_int((int32_t)crc.get_crc_val()); } /* get the state into a ByteArray object */ void get_state(VMG_ vm_val_t *val) { size_t len = get_state_size(); if (len == 0) { /* simple int32_t state */ val->set_int(get_state_int()); } else { /* allocate a buffer */ char *buf = new char[len]; /* make sure we delete the allocated buffer */ err_try { /* get the state into our buffer */ get_state_buf(buf); /* create a ByteArray from the state vector */ val->set_obj(CVmObjByteArray::create_from_bytes( vmg_ FALSE, buf, len)); } err_finally { /* done with the buffer */ delete [] buf; } err_end; } } /* put the state from a ByteArray object */ void put_state(VMG_ vm_val_t *val) { /* determine what to do based on the state length */ size_t len = get_state_size(); if (len == 0) { /* simple int32_t state */ put_state_int(val->num_to_int(vmg0_)); } else { /* retrieve the ByteArray object */ CVmObjByteArray *barr = vm_val_cast(CVmObjByteArray, val); /* check that it matches the expected state buffer size */ unsigned long cnt = barr->get_element_count(); if (cnt != len) err_throw(VMERR_BAD_VAL_BIF); /* retrieve the bytes and restore the state */ char *buf = new char[len]; err_try { /* retrieve the bytes */ barr->copy_to_buf((unsigned char *)buf, 1, len); /* restore the state */ put_state_buf(buf); } err_finally { delete [] buf; } err_end; } } protected: /* * Get the size of the state vector, in bytes. Return 0 if we can * store the state in an int32_t. */ virtual size_t get_state_size() = 0; /* get/put state as an int32_t */ virtual int32_t get_state_int() = 0; virtual void put_state_int(int32_t i) = 0; /* save/restore current state vector to/from a buffer */ virtual void get_state_buf(char *buf) = 0; virtual void put_state_buf(const char *buf) = 0; }; /* ------------------------------------------------------------------------ */ /* * ISAAC random number generator */ class vmbt_isaac_ifc: public IVmBifTadsRNG { public: vmbt_isaac_ifc(VMG0_) { ctx = G_bif_tads_globals->isaac_ctx; } isaacctx *ctx; virtual uint32_t rand() { return isaac_rand(ctx); } /* seed from random data */ virtual void seed_random() { /* generate random bytes into the ISAAC rsl array */ os_gen_rand_bytes((unsigned char *)ctx->rsl, sizeof(ctx->rsl)); /* initialize from the rsl array */ isaac_init(ctx, TRUE); } /* seed from a string value */ virtual void seed_str(const char *str, size_t len) { /* * Copy the string value into the rsl array; copy as much as will * fit, and zero the rest. (The point here is to be deterministic, * so we want this to be the same every time with a given seed - we * don't want to include random data left behind from previous * iterations.) */ if (len > sizeof(ctx->rsl)) len = sizeof(ctx->rsl); memset(ctx->rsl, 0, sizeof(ctx->rsl)); memcpy(ctx->rsl, str, len); /* initialize with the rsl data */ isaac_init(ctx, TRUE); } virtual int32_t get_state_int() { return 0; } virtual void put_state_int(int32_t) { } virtual size_t get_state_size() { return isaac_get_state(ctx, 0); } virtual void get_state_buf(char *buf) { isaac_get_state(ctx, buf); } virtual void put_state_buf(const char *buf) { isaac_set_state(ctx, buf); } }; /* ------------------------------------------------------------------------ */ /* * Linear Congruential Random-Number Generator. This generator uses an * algorithm from Knuth, The Art of Computer Programming, Volume 2, p. * 170, with parameters chosen from the same book for their good * statistical properties and efficiency on 32-bit hardware. */ class vmbt_lcg_ifc: public IVmBifTadsRNG { public: vmbt_lcg_ifc(VMG0_) { seedp = &G_bif_tads_globals->lcg_rand_seed; } virtual uint32_t rand() { const uint32_t a = 1664525L; const uint32_t c = 1; /* * Generate the next random value using the linear congruential * method described in Knuth, The Art of Computer Programming, * volume 2, p170. * * Use 2^32 as m, hence (n mod m) == (n & 0xFFFFFFFF). This is * efficient and is well-suited to 32-bit machines, works fine on * larger machines, and will even work on 16-bit machines as long * as the compiler can provide us with 32-bit arithmetic (which we * assume extensively elsewhere anyway). * * We use a = 1664525, a multiplier which has very good results * with the Spectral Test (see Knuth p102) with our choice of m. * * Use c = 1, since this trivially satisfies Knuth's requirements * about common factors. * * Note that the result of the multiplication might overflow a * 32-bit ulong for values of lcg_rand_seed that are not small. * This doesn't matter, since if it does, the machine will * naturally truncate high-order bits to yield the result mod 2^32. * So, on a 32-bit machine, the (&0xFFFFFFFF) part is superfluous, * but it's harmless and is needed for machines with a larger word * size. */ *seedp = (int32_t)(((a * (uint32_t)*seedp) + c) & 0xFFFFFFFF); return (uint32_t)*seedp; } virtual void seed_random() { long l; os_rand(&l); *seedp = (int32_t)l; } virtual void seed_int(int32_t i) { *seedp = i; } virtual int32_t get_state_int() { return *seedp; } virtual void put_state_int(int32_t i) { *seedp = i; } virtual size_t get_state_size() { return 0; } virtual void get_state_buf(char *) { } virtual void put_state_buf(const char *) { } int32_t *seedp; }; /* ------------------------------------------------------------------------ */ /* * Bit-shift generator. This is from Knuth, The Art of Computer * Programming, volume 2. This generator is designed to produce random * strings of bits and isn't suitable for use as a general-purpose RNG. * * Linear congruential generators aren't ideal for generating random bits; * their statistical properties are better suited for generating values * over a full integer range. This generator is specially designed to * produce random bits, so it could be a useful complement to an LCG RNG. * * Since this isn't a good general RNG, we don't currently enable it. * We're keeping the code here, ifdef'd out, in case we decide to enable it * in the future - which seems really unlikely, since ISAAC seems to be * good at generating both full-range integers and random bits, leaving * little reason to have a dedicated random bit generator. */ #ifdef VMBIFTADS_RNG_BITSHIFT static uint32_t bits_rng_next(VMG0_) { int top_bit = (G_bif_tads_globals->bits_rand_seed & 0x8000000); G_bif_tads_globals->bits_rand_seed <<= 1; if (top_bit) G_bif_tads_globals->bits_rand_seed ^= 035604231625; return G_bif_tads_globals->bits_rand_seed & 1; } #endif /* VMBIFTADS_RNG_BITSHIFT */ /* ------------------------------------------------------------------------ */ /* * Mersenne Twister MT19937 RNG algorithm */ class vmbt_mt_ifc: public IVmBifTadsRNG { public: vmbt_mt_ifc(VMG0_) { mt = G_bif_tads_globals->mt_ctx; } CVmMT19937 *mt; virtual uint32_t rand() { return mt->rand(); } virtual void seed_random() { mt->seed_random(); } virtual void seed_int(int32_t i) { mt->seed(i); } virtual void seed_str(const char *str, size_t len) { mt->seed(str, len); } virtual int32_t get_state_int() { return 0; } virtual void put_state_int(int32_t i) { } virtual size_t get_state_size() { return mt->get_state_size(); } virtual void get_state_buf(char *buf) { mt->get_state(buf); } virtual void put_state_buf(const char *buf) { mt->put_state(buf); } }; /* ------------------------------------------------------------------------ */ /* * RNG interface selector. This sets up each type of RNG interface, and * returns the one for the given ID. This object can be constructed on the * stack for minimal memory management hassle. */ class CVmRNGSelector { public: CVmRNGSelector(VMG0_) : isaac(vmg0_), lcg(vmg0_), mt(vmg0_) { } IVmBifTadsRNG *get(int id) { switch (id) { case VMBT_RNGID_ISAAC: return &isaac; case VMBT_RNGID_LCG: return &lcg; case VMBT_RNGID_MT19937: return &mt; default: err_throw(VMERR_BAD_VAL_BIF); AFTER_ERR_THROW(return 0;) } } vmbt_isaac_ifc isaac; vmbt_lcg_ifc lcg; vmbt_mt_ifc mt; }; /* ------------------------------------------------------------------------ */ /* * randomize - seed the random number generator. This has several formats: * *. randomize() - select the default RNG (ISAAC) and initialize it *. with random seed data. * *. randomize(nil) - retrieve the current state of the random number *. generator. Returns a list: [id, state], where 'id' *. is the generator ID, and 'state' is an opaque value *. representing the generator's internal state. * *. randomize([state]) - restores the generator and internal state *. returned from a previous call to randomize(nil). * *. randomize(id) - select the RNG identified by 'id'. * *. randomize(id, nil) - select the given RNG, and initialize it with *. random seed data. * *. randomize(id, val) - select the given RNG, and initialize it with *. the given fixed seed data. The seed can be provided *. as an integer or string value. */ void CVmBifTADS::randomize(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 0, 2); if (argc == 0) { /* * No arguments - select the default random number generator * (ISAAC) and initialize it with random seed data. * * Load the ISAAC initialization vector with some truly random data * from the operating system. */ G_bif_tads_globals->rng_id = VMBT_RNGID_ISAAC; vmbt_isaac_ifc s Pvmg0_P; s.seed_random(); /* no return value */ retval_nil(vmg0_); } else if (argc == 1 && G_stk->get(0)->typ == VM_NIL) { /* * randomize(nil) - retrieve the current RNG state. This returns a * list with two elements: [id, state]. Set up the return list. */ vm_obj_id_t lstid = CVmObjList::create(vmg_ FALSE, 2); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lstid); lst->cons_clear(); G_stk->push()->set_obj(lstid); /* retval[1] = the active RNG ID */ vm_val_t ele; ele.set_int(G_bif_tads_globals->rng_id); lst->cons_set_element(0, &ele); /* retval[2] = the active RNG's saved state vector */ CVmRNGSelector sel Pvmg0_P; sel.get(G_bif_tads_globals->rng_id)->get_state(vmg_ &ele); lst->cons_set_element(1, &ele); /* discard gc protection and return the new list */ G_stk->discard(1); retval_obj(vmg_ lstid); } else if (argc == 1 && G_stk->get(0)->is_listlike(vmg0_)) { /* * Randomize([state]) - restores a previous RNG state. The first * list element is the RNG ID; the second is the saved state data, * in an RNG-specific format. */ vm_val_t idele, state; G_stk->get(0)->ll_index(vmg_ &idele, 1); G_stk->get(0)->ll_index(vmg_ &state, 2); int id = idele.num_to_int(vmg0_); /* set the state for the selected generator */ CVmRNGSelector sel Pvmg0_P; sel.get(id)->put_state(vmg_ &state); /* valid ID - select the generator */ G_bif_tads_globals->rng_id = id; /* no return value */ retval_nil(vmg0_); } else if ((argc == 1 || argc == 2) && G_stk->get(0)->is_numeric(vmg0_)) { /* * randomize(id) - select the RNG identified by 'id' *. randomize(id, nil) - select and seed with random data *. randomize(id, val) - select and seed with fixed data */ /* get the ID, and get the generator interface for it */ int id = G_stk->get(0)->num_to_int(vmg0_); CVmRNGSelector sel Pvmg0_P; IVmBifTadsRNG *rng = sel.get(id); /* * Determine whether and how we're seeding the RNG. If there's * only the one argument, we're not seeding it. If there's a * second argument, and it's nil, we're seeding with OS random * data. Otherwise we're seeding with the fixed data in the second * argument, which must be an integer or string value. */ if (argc == 2) { /* get the seed, and sense its type */ vm_val_t *seed = G_stk->get(1); const char *str; if (seed->typ == VM_NIL) { /* nil - use random data from the OS */ rng->seed_random(); } else if (seed->is_numeric(vmg0_)) { /* fixed integer seed value */ rng->seed_int(seed->num_to_int(vmg0_)); } else if ((str = seed->get_as_string(vmg0_)) != 0) { /* string seed value */ rng->seed_str(str + VMB_LEN, vmb_get_len(str)); } else { /* other types are invalid */ err_throw(VMERR_BAD_TYPE_BIF); } } /* if we got this far, the ID was valid, so select the RNG */ G_bif_tads_globals->rng_id = id; /* no return value */ retval_nil(vmg0_); } else { /* invalid arguments */ err_throw(VMERR_WRONG_NUM_OF_ARGS); } } /* * LCG randomize - seed the random-number generator */ //void CVmBifTADS::randomize(VMG_ uint argc) //{ // /* check arguments */ // check_argc(vmg_ argc, 0); // // /* seed the generator */ // os_rand(&G_bif_tads_globals->lcg_rand_seed); //} /* ------------------------------------------------------------------------ */ /* * Get the next random number from the selected RNG */ static uint32_t rng_next(VMG0_) { CVmRNGSelector sel Pvmg0_P; return sel.get(G_bif_tads_globals->rng_id)->rand(); } /* ------------------------------------------------------------------------ */ /* * Map a random 32-bit number to a smaller range 0..range-1. * * Use the "multiplication" method Knuth describes in TAoCP Vol 2 section * 3.4.2. This treats the source value as a fractional value in the range * [0..1); we multiply this fractional value by the upper bound to get a * linearly distributed number [0..range). To turn a 32-bit integer into a * fractional value in the range [0..1), divide by 2^32. * * We do this calculation entirely with 32-bit integer arithmetic. The * nominal calculation we're performing is: * *. rand_val = (ulong)((((double)rand_val) / 4294967296.0) *. * (double)range); * * To do the arithmetic entirely with integers, we refactor this by first * calculating the 64-bit product of rand_val * range, then dividing the * product by 2^32. This isn't possible to do directly with 32-bit ints, * of course. But the division is particularly easy because it's the same * as a 32-bit right-shift. So the trick is to factor out the low-order * 32-bits of the product and the high-order 32-bits, which we can do with * some bit shifting. */ static inline ulong rand_range(ulong rand_val, ulong range) { /* calculate the high-order 32 bits of (rand_val / 2^32 * range) */ ulong hi = (((rand_val >> 16) & 0xffff) * ((range >> 16) & 0xffff)) + ((((rand_val >> 16) & 0xffff) * (range & 0xffff)) >> 16) + (((rand_val & 0xffff) * ((range >> 16) & 0xffff)) >> 16); /* calculate the low-order 32 bits */ ulong lo = ((((rand_val >> 16) & 0xffff) * (range & 0xffff)) & 0xffff) + (((rand_val & 0xffff) * ((range >> 16) & 0xffff)) & 0xffff) + ((((rand_val & 0xffff) * (range & 0xffff)) >> 16) & 0xffff); /* add the carry from the low part into the high part to get the result */ return hi + (lo >> 16); } /* calculate a random number in the range [lower, upper] */ static inline ulong rand_range(ulong rand_val, ulong lower, ulong upper) { return lower + rand_range(rand_val, upper - lower + 1); } /* random number with two ranges */ static inline ulong rand_range(ulong rand_val, ulong lo1, ulong hi1, ulong lo2, ulong hi2) { rand_val = rand_range(rand_val, (hi1 - lo1 + 1) + (hi2 - lo2 + 1)); return rand_val + (rand_val <= hi1 - lo1 ? lo1 : lo2 - (hi1 - lo1 + 1)); } /* random number with three ranges */ static inline ulong rand_range(ulong rand_val, ulong lo1, ulong hi1, ulong lo2, ulong hi2, ulong lo3, ulong hi3) { rand_val = rand_range(rand_val, (hi1 - lo1 + 1) + (hi2 - lo2 + 1) + (hi3 - lo3 + 1)); return rand_val + (rand_val <= hi1 - lo1 ? lo1 : rand_val <= hi1 - lo1 + hi2 - lo2 + 1 ? lo2 - (hi1 - lo1 + 1) : lo3 - (hi1 - lo1 + 1) - (hi2 - lo2 + 1)); } /* ------------------------------------------------------------------------ */ /* * Random string template parser */ class RandStrParser { public: /* initialize - parse the template string */ RandStrParser(const char *src, size_t len); /* delete */ ~RandStrParser(); /* execute the template and return a string object result */ void exec(VMG_ vm_val_t *result); /* get the current character */ wchar_t getch() { return rem > 0 ? p.getch() : 0; } /* parse an integer value */ int parseInt() { /* parse digits in the input */ int acc; for (acc = 0 ; more() && is_digit(getch()) ; skip()) { /* add this digit into the accumulator */ acc *= 10; acc += value_of_digit(getch()); } /* return the accumulator value */ return acc; } /* skip the current character */ void skip() { if (rem != 0) p.inc(&rem); } /* is there more input? */ int more() const { return rem != 0; } /* get the number of bytes remaining */ size_t getrem() const { return rem; } protected: /* source string pointer and remaining length */ utf8_ptr p; size_t rem; /* root of the parse tree */ class RandStrNode *tree; }; class RandStrNode { public: RandStrNode() { firstChild = lastChild = 0; nextSibling = 0; } virtual ~RandStrNode() { while (firstChild != 0) { RandStrNode *nxt = firstChild->nextSibling; delete firstChild; firstChild = nxt; } } /* calculate the maximum length for the generated string for this node */ virtual int maxlen() = 0; /* generate the string for this node */ virtual void generate(VMG_ wchar_t *&dst) = 0; /* add a child */ void addChild(RandStrNode *chi) { if (lastChild != 0) lastChild->nextSibling = chi; else firstChild = chi; lastChild = chi; } /* tree links */ RandStrNode *firstChild, *lastChild; RandStrNode *nextSibling; }; /* * range element - this covers a single character or single 'a-z' range * within a [character list] expression */ class RandStrRange: public RandStrNode { public: RandStrRange(wchar_t c1, wchar_t c2) { if (c2 > c1) this->c1 = c1, this->c2 = c2; else this->c1 = c2, this->c2 = c1; } virtual int maxlen() { return 1; } virtual void generate(VMG_ wchar_t *&dst) { } /* low and high end of the character range, inclusive */ wchar_t c1, c2; }; /* literal string - this is a string of characters enclosed in quotes */ class RandStrLit: public RandStrNode { public: RandStrLit(class RandStrParser *p) { /* * Allocate our buffer. To make this easy, we use the total byt * length of the string remaining. This is more than we'll * actually ever need, on two counts: we could have more bytes than * characters in the remaining source, since some characters could * be multi-byte; and the quoted section probably isn't the whole * rest of the string. So we'll waste a little memory. But this * object is short-lived and the wasted space is usually trivial, * so it's not worth the additional work to be more precise in * allocating the buffer. */ buf = new wchar_t[p->getrem()]; len = 0; /* skip the open quote, then parse the contents */ for (p->skip() ; p->more() ; p->skip()) { wchar_t ch; /* if we're at a close quote, we might be done */ if ((ch = p->getch()) == '"') { /* skip the quote */ p->skip(); /* if it's not stuttered, we're done */ if ((ch = p->getch()) != '"') break; } /* add this character to our buffer */ buf[len++] = ch; } } ~RandStrLit() { delete [] buf; } virtual int maxlen() { return len; } virtual void generate(VMG_ wchar_t *&dst) { /* copy our string */ memcpy(dst, buf, len*sizeof(*dst)); /* advance the pointer */ dst += len; } protected: /* our buffer */ wchar_t *buf; /* number of charactres in the buffer */ size_t len; }; /* atom - this is a single character specifier or a [character list] */ class RandStrAtom: public RandStrNode { public: RandStrAtom(class RandStrParser *p) { /* we don't have a character class or literal character yet */ ch = 0; isLit = FALSE; /* we have no character list items yet */ listChars = 0; /* check for a character list of the form [abcw-z] */ if (p->getch() == '[') { /* keep going until we reach the ']' or end of string */ for (p->skip() ; p->more() && p->getch() != ']' ; ) { /* get the next character */ wchar_t c1 = p->getch(); /* check for quoting */ if (c1 == '%') { /* skip the '%' and get the quoted character */ p->skip(); c1 = (p->more() ? p->getch() : '%'); } /* skip the character */ p->skip(); /* if the next character is '-', we have a range expression */ if (p->getch() == '-') { /* skip the '-' */ p->skip(); /* if there's a '%', skip it as well */ if (p->getch() == '%') p->skip(); /* get the upper bound character */ wchar_t c2 = (p->more() ? p->getch() : c1); /* skip the second character */ p->skip(); /* add the range */ addChild(new RandStrRange(c1, c2)); listChars += (c2 > c1 ? c2 - c1 : c1 - c2) + 1; } else { /* it's just this character in the range */ addChild(new RandStrRange(c1, c1)); listChars += 1; } } /* skip the ']' */ p->skip(); } else if (p->getch() == '%') { /* * quoted character expression - skip the '%' and store the * single character */ p->skip(); ch = p->more() ? p->getch() : '%'; /* note that this is a literal character expression */ isLit = TRUE; /* skip the character */ p->skip(); } else { /* character class expression - store it and skip it */ ch = p->getch(); p->skip(); } } /* an atom generates exactly one character */ virtual int maxlen() { return 1; } /* generate the atom */ virtual void generate(VMG_ wchar_t *&dst) { /* * If we have a character list expression, choose a character from * the list. Otherwise, generate a character according to our * character class code. */ wchar_t outc; if (listChars != 0) { /* pick a random index in our range */ wchar_t n = (wchar_t)rand_range(rng_next(vmg0_), listChars); /* find the range containing this character */ for (RandStrNode *chi = firstChild ; chi != 0 ; chi = chi->nextSibling) { /* cast the child - we know it's a range node */ RandStrRange *r = (RandStrRange *)chi; /* if the index is in this range, we've found it */ if (n <= r->c2 - r->c1) { outc = r->c1 + n; break; } /* deduct the size of this range from the index */ n -= (r->c2 - r->c1 + 1); } } else if (isLit) { /* literal character expression */ outc = ch; } else { /* no list, so generate a character based on the class code */ ulong rand_val = rng_next(vmg0_); switch (ch) { case 'i': /* printable ASCII character 32-126 */ outc = (wchar_t)rand_range(rand_val, 32, 126); break; case 'l': /* printable Latin-1 character 32-126, 160-255 */ outc = (wchar_t)rand_range(rand_val, 32, 126, 160, 255); break; case 'u': /* * Printable Unicode character. This excludes undefined * character, the private use area, and the control * characters. */ for (;;) { /* exclude the control and private use ranges */ outc = (wchar_t)rand_range( rand_val, 32, 127, 160, 0xDFFF, 0xF900, 0xFFFE); /* if it's a unicode character, we're set */ if (t3_is_unichar(ch)) break; /* pick a new random number */ rand_val = rng_next(vmg0_); } break; case 'd': /* decimal digit 0-9 */ outc = (wchar_t)rand_range(rand_val, '0', '9'); break; case 'X': /* upper-case hex digit 0-9 A-F */ outc = (wchar_t)rand_range(rand_val, '0', '9', 'A', 'F'); break; case 'x': /* lower-case hex digit 0-9 a-f */ outc = (wchar_t)rand_range(rand_val, '0', '9', 'a', 'f'); break; case 'A': /* letter A-Z */ outc = (wchar_t)rand_range(rand_val, 'A', 'Z'); break; case 'a': /* letter a-z */ outc = (wchar_t)rand_range(rand_val, 'a', 'z'); break; case 'b': /* random byte value 0-255 */ outc = (wchar_t)rand_range(rand_val, 0, 255); break; case 'c': /* mixed-case letter A-Z a-z */ outc = (wchar_t)rand_range(rand_val, 'a', 'z', 'A', 'Z'); break; case 'z': /* mixed-case letter or number 0-9 a-z A-Z */ outc = (wchar_t)rand_range(rand_val, '0', '9', 'a', 'z', 'A', 'Z'); break; default: /* no character */ return; } } /* store the output character */ *dst++ = outc; } protected: /* for a single-character expression, the character */ wchar_t ch; /* is 'ch' a literal character, or a character code expression? */ int isLit; /* number of characters included in all of [character list] elements */ int listChars; }; /* * repeat group - this is an item optionally followed by a {repeat count}, * a '*', or a '?' */ class RandStrRepeat: public RandStrNode { public: RandStrRepeat(RandStrParser *p); /* * If we have a finite upper bound, our maximum length is our child * item length times our maximum repeat count. If there's no upper * bound, our output is in principle infinite, but the probability of * the string being at least a given length N is (2/3)^N, so the odds * drop off very rapidly as N increases; at N=25, we're down to one in * 25,000. Return 50, and use this as a hard cap in the generator. */ static const int MAX_UNBOUNDED = 50; virtual int maxlen() { return firstChild->maxlen() * (hi < 0 ? MAX_UNBOUNDED : hi); } /* generate */ virtual void generate(VMG_ wchar_t *&dst) { if (hi >= 0) { /* * Finite upper bound. Pick a random repeat count from lo to * hi, and generate our child that many times. */ for (ulong cnt = rand_range(rng_next(vmg0_), lo, hi) ; cnt != 0 ; --cnt) firstChild->generate(vmg_ dst); } else { /* no upper bound - start with the fixed lower bound */ int i; for (i = 0 ; i < lo ; ++i) firstChild->generate(vmg_ dst); /* * Now add additional items one at a time, with 67% probability * for each added item. Stop when we fail the roll. */ for (i = 0 ; rand_range(rng_next(vmg0_), 0, 99) < 67 && i < MAX_UNBOUNDED ; ++i) firstChild->generate(vmg_ dst); } } /* * Repeat range. If hi is negative, there's no upper bound. Rather * than picking a number from 0 to infinity uniformly (which is * obviously impractical, since the expectation value is infinite), we * have a 50% chance of adding each additional item. */ int lo, hi; }; /* concatenation list - this is a list of repeat groups */ class RandStrCatList: public RandStrNode { public: RandStrCatList(RandStrParser *p) { /* * parse repeat groups until we reach the end of the string, the * end of the alternative, or the end of the parenthesized group */ while (p->more() && p->getch() != ')' && p->getch() != '|') addChild(new RandStrRepeat(p)); } /* * we generate a concatenation of our children, so our length is the * sum of our child lengths */ virtual int maxlen() { int sum = 0; for (RandStrNode *chi = firstChild ; chi != 0 ; sum += chi->maxlen(), chi = chi->nextSibling) ; return sum; } /* generate */ virtual void generate(VMG_ wchar_t *&dst) { /* generate each child sequentially */ for (RandStrNode *chi = firstChild ; chi != 0 ; chi = chi->nextSibling) chi->generate(vmg_ dst); } }; /* alternative list - this is a list of CatList items separated by '|' */ class RandStrAltList: public RandStrNode { public: RandStrAltList(RandStrParser *p, int isOuterExpr) { /* no alternatives yet */ altCount = 0; /* * parse alternatives until we reach the end of the string or a * close paren (unless we're the outermost expression, in which * case we treat ')' as an ordinary character since there's no * enclosing group to close) */ while (p->more() && (isOuterExpr || p->getch() != ')')) { /* parse the next alternative and add it to our child list */ addChild(new RandStrCatList(p)); altCount += 1; /* if there's a '|', skip it */ if (p->getch() == '|') p->skip(); } } /* * when we generate, we choose one child to generate, so our maximum * length is the highest of our individual child lengths */ virtual int maxlen() { int lmax = 0; for (RandStrNode *chi = firstChild ; chi != 0 ; chi = chi->nextSibling) { int l = chi->maxlen(); if (l > lmax) lmax = l; } return lmax; } /* generate */ virtual void generate(VMG_ wchar_t *&dst) { /* pick one of our alternatives at random */ int n = rand_range(rng_next(vmg0_), altCount); /* find it and generate it */ RandStrNode *chi; for (chi = firstChild ; n != 0 && chi != 0 ; --n, chi = chi->nextSibling) ; /* generate the expression */ if (chi != 0) chi->generate(vmg_ dst); } /* number of alternatives */ int altCount; }; /* * repeat group - this is an atom or parenthesized expression, optionally * followed by a postfix repeat count */ RandStrRepeat::RandStrRepeat(RandStrParser *p) { /* if we have an open paren, parse the enclosed alt list */ if (p->getch() == '(') { /* skip the open paren */ p->skip(); /* parse the alt list */ addChild(new RandStrAltList(p, FALSE)); /* if there's a close paren, skip it */ if (p->getch() == ')') p->skip(); } else if (p->getch() == '"') { /* parse the literal string */ addChild(new RandStrLit(p)); } else { /* no parens or quotes, so this is a simple character atom */ addChild(new RandStrAtom(p)); } /* check for {n}, {n-m}, *, or ? */ if (p->getch() == '{') { /* skip the '{' */ p->skip(); /* parse the low end of the range */ lo = p->parseInt(); /* if we're at a ',', get the high end of the range */ if (p->getch() == ',') { /* there's a separate high part - get it */ for (p->skip() ; is_space(p->getch()) ; p->skip()) ; /* * if the high part is missing, there's no upper bound; * otherwise parse the finite upper bound */ if (p->getch() == '}' || p->getch() == '\0') { /* -1 means "infinity" */ hi = -1; } else { /* parse the upper bound */ hi = p->parseInt(); /* make sure hi > lo */ if (hi < lo) { int tmp = hi; hi = lo; lo = tmp; } } } else { /* there's only the one number, so it's the low and high */ hi = lo; } /* skip the '}' */ if (p->getch() == '}') p->skip(); } else if (p->getch() == '+') { /* one or more */ lo = 1; hi = -1; p->skip(); } else if (p->getch() == '*') { /* zero or more */ lo = 0; hi = -1; p->skip(); } else if (p->getch() == '?') { /* zero or one */ lo = 0; hi = 1; p->skip(); } else { /* no repeat count - generate exactly once */ lo = hi = 1; } } /* * main parser implementation */ RandStrParser::RandStrParser(const char *src, size_t len) { /* set up our string pointer */ this->p.set((char *)src); this->rem = len; /* parse the tree starting at the top of the recursive descent */ tree = new RandStrAltList(this, TRUE); } RandStrParser::~RandStrParser() { /* delete the parse tree */ delete tree; } void RandStrParser::exec(VMG_ vm_val_t *result) { /* calculate the maximum length of the result */ size_t len = tree->maxlen(); /* if the maximum length is zero, just return an empty string */ if (len == 0) { result->set_obj(CVmObjString::create(vmg_ FALSE, 0)); return; } /* allocate a wchar_t buffer of the maximum length */ wchar_t *buf = new wchar_t[len]; /* generate the string */ wchar_t *dst = buf; tree->generate(vmg_ dst); /* build the return string */ result->set_obj(CVmObjString::create(vmg_ FALSE, buf, dst - buf)); /* done with the temporary buffer */ delete [] buf; } /* ------------------------------------------------------------------------ */ /* * rand - generate a random number, or choose an element randomly from a * list of values or from our list of arguments. * * With one integer argument N, we choose a random number from 0 to N-1. * * With one list argument, we choose a random element of the list. * * With multiple arguments, we choose one argument at random and return * its value. Note that, because this is an ordinary built-in function, * all of our arguments will be fully evaluated. */ void CVmBifTADS::rand(VMG_ uint argc) { int32_t range; int use_range; int choose_an_arg = FALSE; int choose_an_ele = FALSE; ulong rand_val; /* determine the desired range of values based on the arguments */ if (argc == 0) { /* * if no argument is given, produce a random number in our full * range - clear the 'use_range' flag to so indicate */ use_range = FALSE; } else if (argc == 1 && G_stk->get(0)->typ == VM_INT) { /* we're returning a number in the range 0..(arg-1) */ range = G_stk->get(0)->val.intval; use_range = TRUE; /* discard the argument */ G_stk->discard(); } else if (argc == 1 && G_stk->get(0)->is_listlike(vmg0_) && (range = G_stk->get(0)->ll_length(vmg0_)) >= 0) { /* use the range of 1..length */ use_range = TRUE; /* note that we're choosing from a list-like object */ choose_an_ele = TRUE; /* note - leave the object on the stack as gc protection */ } else if (argc == 1 && G_stk->get(0)->get_as_string(vmg0_) != 0) { /* * It's a string, giving a template for generating a new random * string. First, get the string argument. */ const char *tpl = G_stk->get(0)->get_as_string(vmg0_); size_t tplbytes = vmb_get_len(tpl); tpl += VMB_LEN; #if 1 /* parse it */ RandStrParser *rsp = new RandStrParser(tpl, tplbytes); err_try { /* generate the string */ vm_val_t ret; rsp->exec(vmg_ &ret); /* return it */ retval(vmg_ &ret); } err_finally { /* delete our parser on the way out */ delete rsp; } err_end; #else /* figure its character length */ utf8_ptr tplp((char *)tpl); size_t tplchars = tplp.len(tplbytes); /* allocate temporary space for the result, as wide characters */ wchar_t *buf = new wchar_t[tplchars], *dst; /* run through the template and generate random characters */ for (dst = buf ; tplbytes != 0 ; ) { /* generate a random number for this character */ rand_val = rng_next(vmg0_); /* get and skip the next template character */ wchar_t tch = tplp.getch(); tplp.inc(&tplbytes); /* translate the template character */ wchar_t ch; switch (tch) { case '.': /* printable ASCII character 32-126 */ ch = (wchar_t)rand_range(rand_val, 32, 126); break; case '?': /* printable Latin-1 character 32-126, 160-255 */ ch = (wchar_t)rand_range(rand_val, 32, 126, 160, 255); break; case '*': /* * Printable Unicode character. This excludes undefined * character, the private use area, and the control * characters. */ for (;;) { /* exclude the control and private use ranges */ ch = (wchar_t)rand_range( rand_val, 32, 127, 160, 0xDFFF, 0xF900, 0xFFFE); /* if it's a unicode character, we're set */ if (t3_is_unichar(ch)) break; /* pick a new random number */ rand_val = rng_next(vmg0_); } break; case '9': /* digit 0-9 */ ch = (wchar_t)rand_range(rand_val, '0', '9'); break; case 'X': /* upper-case hex digit 0-9 A-F */ ch = (wchar_t)rand_range(rand_val, '0', '9', 'A', 'F'); break; case 'x': /* lower-case hex digit 0-9 a-f */ ch = (wchar_t)rand_range(rand_val, '0', '9', 'a', 'f'); break; case 'A': /* letter A-Z */ ch = (wchar_t)rand_range(rand_val, 'A', 'Z'); break; case 'a': /* letter a-z */ ch = (wchar_t)rand_range(rand_val, 'a', 'z'); break; case 'b': /* random byte value 0-255 */ ch = (wchar_t)rand_range(rand_val, 0, 255); break; case 'c': /* mixed-case letter A-Z a-z */ ch = (wchar_t)rand_range(rand_val, 'a', 'z', 'A', 'Z'); break; case 'z': /* mixed-case letter or number 0-9 a-z A-Z */ ch = (wchar_t)rand_range(rand_val, '0', '9', 'a', 'z', 'A', 'Z'); break; case '[': /* * Character range. Scan the range to count the number of * characters included. */ { /* * allocate a list of range descriptors - use 'len2' as * the range count, since at the most we could have one * range per byte (but it'll usually be less) */ struct rdesc { int set(wchar_t c) { start = end = c; return 1; } int set(wchar_t a, wchar_t b) { if (b > a) start = a, end = b; else start = b, end = a; return end - start + 1; } wchar_t start; wchar_t end; }; rdesc *ranges = new rdesc[tplbytes]; int nranges = 0, nchars = 0; /* scan for the closing ']' */ for ( ; tplbytes != 0 && tplp.getch() != ']' ; tplp.inc(&tplbytes)) { /* check for escapes */ wchar_t rch = tplp.getch(); if (rch == '%') { tplp.inc(&tplbytes); rch = tplp.getch(); } /* if the next character is '-', it's a range */ if (tplbytes > 1 && tplp.getch_at(1) == '-') { /* skip the current character and the '-' */ tplp.inc(&tplbytes); tplp.inc(&tplbytes); /* if the next character is quoted, skip the % */ if (tplbytes > 1 && tplp.getch() == '%') tplp.inc(&tplbytes); /* * The range count includes everything from * 'rch' to the current character. */ if (tplbytes != 0) nchars += ranges[nranges++].set( rch, tplp.getch()); } else { /* it's a single character range */ nchars += ranges[nranges++].set(rch); } /* if we're out of characters, we're done */ if (tplbytes == 0) break; } /* pick a number from 0 to rcnt */ ch = (wchar_t)rand_range(rand_val, nchars); /* find the character */ for (int i = 0 ; i < nranges ; ++i) { /* if it's in the current range, apply it */ const rdesc *r = &ranges[i]; if (ch <= r->end - r->start) { ch += r->start; break; } /* * it's not in this range, so deduct the range * length from the remainder and keep going */ ch -= r->end - r->start + 1; } /* done with the range list */ delete [] ranges; /* skip the closing ']' */ if (tplbytes != 0 && tplp.getch() == ']') tplp.inc(&tplbytes); } break; case '%': /* copy the next character unchanged */ if (tplbytes != 0) { ch = tplp.getch(); tplp.inc(&tplbytes); } else ch = '%'; break; default: /* no character */ continue; } /* save it in our temp buffer, and note its utf8 size */ *dst++ = ch; } /* convert the temp buffer to a real string */ retval_obj(vmg_ CVmObjString::create(vmg_ FALSE, buf, dst - buf)); /* done with the temporary buffer */ delete [] buf; #endif /* discard the argument */ G_stk->discard(); /* we're done */ return; } else { /* * produce a random number in the range 0..(argc-1) so that we * can select one of our arguments */ range = argc; use_range = TRUE; /* note that we should choose an argument value */ choose_an_arg = TRUE; } /* get the next random number */ rand_val = rng_next(vmg0_); /* * Calculate our random value in the range 0..(range-1). If range * == 0, simply choose a value across our full range. */ if (use_range) rand_val = rand_range(rand_val, range); /* * Return the appropriate value, depending on our argument list */ if (choose_an_arg) { /* return the selected argument */ retval(vmg_ G_stk->get((int)rand_val)); /* discard all of the arguments */ G_stk->discard(argc); } else if (choose_an_ele) { vm_val_t val; /* get the selected element */ if (range == 0) { /* there are no elements to choose from, so return nil */ val.set_nil(); } else { /* get the selected list element */ G_stk->get(0)->ll_index(vmg_ &val, rand_val + 1); } /* set the result */ retval(vmg_ &val); /* discard our gc protection */ G_stk->discard(); } else { /* simply return the random number */ retval_int(vmg_ (long)rand_val); } } /* ------------------------------------------------------------------------ */ /* * toString - convert to string */ void CVmBifTADS::toString(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 1, 3); /* get the argument */ vm_val_t *val = G_stk->get(0); /* if there's a radix specified, pop it as well */ int radix = (argc >= 2 ? G_stk->get(1)->num_to_int(vmg0_) : 10); /* the radix must be from 2 to 36 */ if (radix < 2 || radix > 36) err_throw(VMERR_BAD_VAL_BIF); /* assume unsigned */ int flags = TOSTR_UNSIGNED;; /* * If the 'isSigned' argument is present, pop it as a bool; otherwise * treat the value as signed if the radix is decimal, otherwise * unsigned. */ if (argc >= 3 ? G_stk->get(2)->get_logical_only() : radix == 10) flags &= ~TOSTR_UNSIGNED; /* convert the value */ toString(vmg_ G_interpreter->get_r0(), val, radix, flags); /* discard arguments */ G_stk->discard(argc); } /* * explicitly convert to string */ void CVmBifTADS::toString(VMG_ vm_val_t *result, const vm_val_t *val, int radix, int flags) { /* do the basic string conversion */ char buf[50]; const char *p = CVmObjString::cvt_to_str( vmg_ result, buf, sizeof(buf), val, radix, flags); /* save the new string on the stack to protect from garbage collection */ G_stk->push(result); /* * if the return value wasn't already a new object, create a string * from the return value */ if (result->typ != VM_OBJ) { /* we just have a string in a buffer - create a new string from it */ result->set_obj(CVmObjString::create( vmg_ FALSE, p + VMB_LEN, vmb_get_len(p))); } /* discard gc protection */ G_stk->discard(1); } /* * toInteger - convert to an integer */ void CVmBifTADS::toInteger(VMG_ uint argc) { /* convert as an integer only */ toIntOrNum(vmg_ argc, TRUE); } /* * toNumber - convert to an integer or BigNumber */ void CVmBifTADS::toNumber(VMG_ uint argc) { /* convert as integer or BigNumber */ toIntOrNum(vmg_ argc, FALSE); } /* * Common handler for toInteger and toNumber */ void CVmBifTADS::toIntOrNum(VMG_ uint argc, int int_only) { /* check arguments */ check_argc_range(vmg_ argc, 1, 2); /* check for BigNumber values */ vm_val_t *valp = G_stk->get(0); if (valp->typ == VM_OBJ && CVmObjBigNum::is_bignum_obj(vmg_ valp->val.obj)) { /* * If we only want integer results, try converting the BigNumber to * an integer. Otherwise, simply return the BigNumber as-is. */ if (int_only) { /* we only want an integer result - convert to int */ long intval = ((CVmObjBigNum *)vm_objp(vmg_ valp->val.obj)) ->convert_to_int(); /* return the integer value */ retval_int(vmg_ intval); } else { /* BigNumber results are okay - just return the BigNumber */ retval(vmg_ valp); } /* discard arguments, and we're done */ G_stk->discard(argc); return; } /* if it's already an integer, just return the same value */ if (valp->typ == VM_INT) { /* just return the argument value */ retval_int(vmg_ valp->val.intval); /* discard arguments and return */ G_stk->discard(argc); return; } /* if it's true or nil, convert to 1 or 0 */ if (valp->typ == VM_TRUE || valp->typ == VM_NIL) { /* return 1 for true, 0 for nil */ retval_int(vmg_ valp->typ == VM_TRUE ? 1 : 0); G_stk->discard(argc); return; } /* the only other type of value we can convert is a string */ const char *strp = G_stk->get(0)->get_as_string(vmg0_); if (strp == 0) err_throw(VMERR_STRING_VAL_REQD); /* get the string length and buffer pointer */ size_t len = vmb_get_len(strp); strp += VMB_LEN; /* if there's a radix specified, pop it as well; the default is 10 */ int radix = 10; if (argc >= 2) { /* get the radix from the stack */ radix = G_stk->get(1)->num_to_int(vmg0_); /* make sure it's in the valid range */ if (radix < 2 || radix > 36) err_throw(VMERR_BAD_VAL_BIF); } /* get a version of the string stripped of leading and trailing spaces */ const char *p2 = strp; size_t len2 = len; for ( ; len2 != 0 && is_space(*p2) ; ++p2, --len2) ; for ( ; len2 != 0 && is_space(*(p2 + len2 - 1)) ; --len2) ; /* * Check for "nil" and "true", ignoring leading and trailing spaces; if * it matches either of those, return the corresponding boolean value. * Otherwise, parse the string as an integer value in the given radix. */ if (len2 == 3 && memcmp(p2, "nil", 3) == 0) { /* the value for "nil" is 0 */ retval_int(vmg_ 0); } else if (len2 == 4 && memcmp(p2, "true", 3) == 0) { /* the value for "true" is 1 */ retval_int(vmg_ 1); } else { /* parse the string as an integer (orBigNumber if it's too large) */ vm_val_t val; CVmObjString::parse_num_val(vmg_ &val, strp, len, radix, int_only); retval(vmg_ &val); } /* discard arguments */ G_stk->discard(argc); } /* ------------------------------------------------------------------------ */ /* * put an integer value in a constant list, advancing the list write * pointer */ static void put_list_int(char **dstp, long intval) { /* set up the integer value */ vm_val_t val; val.set_int(intval); /* write it to the list */ vmb_put_dh(*dstp, &val); /* advance the output pointer */ *dstp += VMB_DATAHOLDER; } /* * put an object value in a constant list, advancing the list write * pointer */ static void put_list_obj(char **dstp, vm_obj_id_t objval) { vm_val_t val; /* set up the integer value */ val.set_obj(objval); /* write it to the list */ vmb_put_dh(*dstp, &val); /* advance the output pointer */ *dstp += VMB_DATAHOLDER; } /* * Given a time_t value, create a list of components giving the local time * corresponding to the time_t, using the getTime(GetDateAndTime) format. * Returns the object ID of the new list. * * The list format is [year, month, day, day-of-week, day-of-year, hour, * minute, second, seconds-since-1970]. */ vm_obj_id_t CVmBifTADS::format_datetime_list(VMG_ os_time_t timer) { /* note the starting stack pointer, so we can discard gc protection */ vm_val_t *gsp = G_stk->get_sp(); /* convert the time_t to the local time */ struct tm *tblock = os_localtime(&timer); /* start the list buffer - set the length (9 elements) */ char buf[80]; vmb_put_len(buf, 9); char *dst = buf + VMB_LEN; /* add the time elements to the list */ put_list_int(&dst, tblock->tm_year + 1900); put_list_int(&dst, tblock->tm_mon + 1); put_list_int(&dst, tblock->tm_mday); put_list_int(&dst, tblock->tm_wday + 1); put_list_int(&dst, tblock->tm_yday + 1); put_list_int(&dst, tblock->tm_hour); put_list_int(&dst, tblock->tm_min); put_list_int(&dst, tblock->tm_sec); /* * if the timer is over 0x7fffffff, we can't represent it as an * int32_t, so store it as a BigNumber value */ if (timer <= 0x7fffffffU) { /* it'll fit in an ordinary 32-bit signed int */ put_list_int(&dst, (uint32_t)timer); } else { /* it won't fit an int32, so convert to BigNumber */ vm_obj_id_t bn; if (sizeof(timer) <= 4) { /* os_time_t must be a uint32 */ bn = CVmObjBigNum::createu(vmg_ FALSE, (ulong)timer, 10); } else { /* os_time_t must be 64 bits (or larger) */ bn = CVmObjBigNum::create_int64( vmg_ FALSE, (uint32_t)(timer >> 32), (uint32_t)(timer & 0xFFFFFFFF)); } /* save it on the stack for gc protection */ G_stk->push()->set_obj(bn); /* add it to the list */ put_list_obj(&dst, bn); } /* create a list from the formatted buffer, and return its object ID */ vm_obj_id_t lst = CVmObjList::create(vmg_ FALSE, buf); /* discard gc protection */ G_stk->set_sp(gsp); /* return the list */ return lst; } /* * get the current time */ void CVmBifTADS::gettime(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 0, 1); /* if there's an argument, get the type of time value to return */ int typ; if (argc == 1) { /* get the time type code */ typ = pop_int_val(vmg0_); } else { /* use the default type */ typ = 1; } /* check the type */ switch(typ) { case 1: /* * GetTimeDateAndTime - return the current time and date as a list * of time elements */ /* get the current system time and format it into our list */ retval_obj(vmg_ format_datetime_list(vmg_ os_time(NULL))); /* done */ break; case 2: /* * They want the high-precision system timer value, which returns * the time in milliseconds from an arbitrary zero point. */ { unsigned long t; static unsigned long t_zero; static int t_zero_set = FALSE; /* retrieve the raw time from the operating system */ t = os_get_sys_clock_ms(); /* * We only have 31 bits of precision in our result (since we * must fit the value into a signed integer), so we can only * represent time differences of about 23 days. Now, the * value from the OS could be at any arbitrary point in our * 23-day range, so there's a nontrivial probability that the * raw OS value is near enough to the wrapping point that a * future call to this same function during the current * session could encounter the wrap condition. The caller is * likely to be confused by this, because the time difference * from this call to that future call would appear to be * negative. * * There's obviously no way we can eliminate the possibility * of a negative time difference if the current program * session lasts more than 23 days of continuous execution. * Fortunately, it seems unlikely that most sessions will be * so long, which gives us a way to reduce the likelihood that * the program will encounter a wrapped timer: we can adjust * the zero point of the timer to the time of the first call * to this function. That way, the timer will wrap only if * the program session runs continuously until the timer's * range is exhausted. */ if (!t_zero_set) { /* this is the first call - remember the zero point */ t_zero = t; t_zero_set = TRUE; } /* * Adjust the time by subtracting the zero point from the raw * OS timer. This will give us the number of milliseconds * from our zero point. * * If the system timer has wrapped since our zero point, we'll * get what looks like a negative number; but what we really * have is a large positive number with a borrow from an * unrepresented higher-precision portion, so the fact that * this value is negative doesn't matter - it will still be * sequential when treated as unsigned. */ t -= t_zero; /* * whatever we got, keep only the low-order 31 bits, since we * only have 31 bits in which to represent an unsigned value */ t &= 0x7fffffff; /* return the value we've calculated */ retval_int(vmg_ t); } break; default: err_throw(VMERR_BAD_VAL_BIF); } } /* ------------------------------------------------------------------------ */ /* * re_match - match a regular expression to a string */ void CVmBifTADS::re_match(VMG_ uint argc) { const char *str; utf8_ptr p; size_t len; int match_len; vm_val_t *v1, *v2, *v3; int start_idx; CVmObjPattern *pat_obj = 0; const char *pat_str = 0; /* check arguments */ check_argc_range(vmg_ argc, 2, 3); /* * make copies of the arguments, so we can pop the values without * actually removing them from the stack - leave the originals on the * stack for gc protection */ v1 = G_stk->get(0); v2 = G_stk->get(1); v3 = (argc >= 3 ? G_stk->get(2) : 0); G_stk->push(v2); G_stk->push(v1); /* note the starting index, if given */ start_idx = 1; if (v3 != 0) { /* check the type */ if (v3->typ != VM_INT) err_throw(VMERR_BAD_TYPE_BIF); /* get the value */ start_idx = (int)v3->val.intval; } /* * remember the last search string (the second argument), and reset any * old group registers (since they'd point into the previous string) */ G_bif_tads_globals->last_rex_str->val = *v2; G_bif_tads_globals->rex_searcher->clear_group_regs(); /* * check what we have for the pattern - we could have either a string * giving the regular expression, or a RexPattern object with the * compiled pattern */ if (G_stk->get(0)->typ == VM_OBJ && CVmObjPattern::is_pattern_obj(vmg_ G_stk->get(0)->val.obj)) { vm_val_t pat_val; /* get the pattern object */ G_stk->pop(&pat_val); pat_obj = (CVmObjPattern *)vm_objp(vmg_ pat_val.val.obj); } else { /* get the pattern string */ pat_str = pop_str_val(vmg0_); } /* get the string to match */ str = pop_str_val(vmg0_); len = vmb_get_len(str); p.set((char *)str + VMB_LEN); /* if the starting index is negative, it's from the end of the string */ start_idx += (start_idx < 0 ? (int)p.len(len) : -1); /* skip to the starting index */ for ( ; start_idx > 0 && len != 0 ; --start_idx, p.inc(&len)) ; /* match the pattern */ if (pat_obj != 0) { /* match the compiled pattern object */ match_len = G_bif_tads_globals->rex_searcher-> match_pattern(pat_obj->get_pattern(vmg0_), str + VMB_LEN, p.getptr(), len); } else { /* match the pattern to the regular expression string */ match_len = G_bif_tads_globals->rex_searcher-> compile_and_match(pat_str + VMB_LEN, vmb_get_len(pat_str), str + VMB_LEN, p.getptr(), len); } /* check for a match */ if (match_len >= 0) { /* we got a match - calculate the character length of the match */ retval_int(vmg_ (long)p.len(match_len)); } else { /* no match - return nil */ retval_nil(vmg0_); } /* discard the arguments */ G_stk->discard(argc); } /* ------------------------------------------------------------------------ */ /* * Common handler for re_search() and re_search_back() */ template inline void CVmBifTADS::re_search_common(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 2, 3); /* * make copies of the arguments, so we can pop the values without * actually removing them from the stack - leave the originals on the * stack for gc protection */ vm_val_t *v1 = G_stk->get(0); vm_val_t *v2 = G_stk->get(1); vm_val_t *v3 = (argc >= 3 ? G_stk->get(2) : 0); G_stk->push(v2); G_stk->push(v1); /* note the starting index, if given */ int start_idx = (dir > 0 ? 1 : 0); if (v3 != 0) { /* check the type */ if (v3->typ != VM_INT) err_throw(VMERR_BAD_TYPE_BIF); /* get the value */ start_idx = (int)v3->val.intval; } /* * remember the last search string (the second argument), and clear out * any old group registers (since they'd point into the old string) */ G_bif_tads_globals->last_rex_str->val = *v2; G_bif_tads_globals->rex_searcher->clear_group_regs(); /* check to see if we have a RexPattern object or an uncompiled string */ const char *pat_str = 0; CVmObjPattern *pat_obj = 0; if (G_stk->get(0)->typ == VM_OBJ && CVmObjPattern::is_pattern_obj(vmg_ G_stk->get(0)->val.obj)) { vm_val_t pat_val; /* get the pattern object */ G_stk->pop(&pat_val); pat_obj = (CVmObjPattern *)vm_objp(vmg_ pat_val.val.obj); } else { /* get the pattern string */ pat_str = pop_str_val(vmg0_); } /* get the string to search for the pattern */ const char *str = pop_str_val(vmg0_); utf8_ptr p((char *)str + VMB_LEN); size_t len = vmb_get_len(str); /* if the starting index is negative, it's from the end of the string */ start_idx += (start_idx < 0 ? (int)p.len(len) : start_idx == 0 && dir < 0 ? (int)p.len(len) : -1); /* skip to the starting index */ for (int i = start_idx ; i > 0 && len != 0 ; --i, p.inc(&len)) ; /* search for the pattern */ int match_idx; int match_len; if (pat_obj != 0) { /* try finding the compiled pattern */ match_idx = (dir > 0 ? G_bif_tads_globals->rex_searcher->search_for_pattern( pat_obj->get_pattern(vmg0_), str + VMB_LEN, p.getptr(), len, &match_len) : G_bif_tads_globals->rex_searcher->search_back_for_pattern( pat_obj->get_pattern(vmg0_), str + VMB_LEN, p.getptr(), len, &match_len)); } else { /* try finding the regular expression string pattern */ match_idx = (dir > 0 ? G_bif_tads_globals->rex_searcher->compile_and_search( pat_str + VMB_LEN, vmb_get_len(pat_str), str + VMB_LEN, p.getptr(), len, &match_len) : G_bif_tads_globals->rex_searcher->compile_and_search_back( pat_str + VMB_LEN, vmb_get_len(pat_str), str + VMB_LEN, p.getptr(), len, &match_len)); } /* check for a match */ if (match_idx >= 0) { /* * We found a match - calculate the character index of the match * offset, adjusted to a 1-base. The character index is simply the * number of characters in the part of the string up to the match * index. Note that we have to add the starting index to get the * actual index in the overall string, since 'p' points to the * character at the starting index. Also note that when searching * backwards, the match index is the number of characters *before* * the starting point. */ size_t char_idx; if (dir > 0) char_idx = p.len(match_idx) + start_idx + 1; else char_idx = utf8_ptr::s_len( str + VMB_LEN, start_idx - match_idx) + 1; /* calculate the character length of the match */ utf8_ptr matchp(p.getptr() + dir*match_idx); size_t char_len = matchp.len(match_len); /* allocate a string containing the match */ vm_obj_id_t match_str_obj = CVmObjString::create(vmg_ FALSE, matchp.getptr(), match_len); /* push it momentarily as protection against garbage collection */ G_stk->push()->set_obj(match_str_obj); /* * set up a 3-element list to contain the return value: * [match_start_index, match_length, match_string] */ char buf[VMB_LEN + VMB_DATAHOLDER * 3]; vmb_put_len(buf, 3); char *dst = buf + VMB_LEN; put_list_int(&dst, (long)char_idx); put_list_int(&dst, (long)char_len); put_list_obj(&dst, match_str_obj); /* allocate and return the list */ retval_obj(vmg_ CVmObjList::create(vmg_ FALSE, buf)); /* we no longer need the garbage collection protection */ G_stk->discard(); } else { /* no match - return nil */ retval_nil(vmg0_); } /* discard the arguments */ G_stk->discard(argc); } /* * re_search - search for a substring matching a regular expression * within a string */ void CVmBifTADS::re_search(VMG_ uint argc) { re_search_common<1>(vmg_ argc); } /* * re_search_back - search backwards for a regular expression */ void CVmBifTADS::re_search_back(VMG_ uint argc) { re_search_common<-1>(vmg_ argc); } /* ------------------------------------------------------------------------ */ /* * re_group - get the string matching a group in the most recent regular * expression search or match */ void CVmBifTADS::re_group(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 1); /* get the group number to retrieve */ int groupno = pop_int_val(vmg0_); /* group 0 is the special group for the overall match */ const re_group_register *reg; if (groupno == 0) { /* get the special register for the overall match */ reg = G_bif_tads_globals->rex_searcher->get_last_match(); } else { /* make sure it's in range */ if (groupno < 1 || groupno > RE_GROUP_REG_CNT) err_throw(VMERR_BAD_VAL_BIF); /* adjust from a 1-base to a 0-base */ --groupno; /* if the group doesn't exist in the pattern, return nil */ if (groupno >= G_bif_tads_globals->rex_searcher->get_group_cnt()) { retval_nil(vmg0_); return; } /* get the group register */ reg = G_bif_tads_globals->rex_searcher->get_group_reg(groupno); } /* * get the previous search string - get a pointer directly to the * contents of the string */ const char *last_str = G_bif_tads_globals->last_rex_str->val.get_as_string(vmg0_); /* if the group wasn't set, or there's no last string, return nil */ if (last_str == 0 || reg->start_ofs == -1 || reg->end_ofs == -1) { retval_nil(vmg0_); return; } /* set up for a list with three elements */ char buf[VMB_LEN + 3*VMB_DATAHOLDER]; vmb_put_len(buf, 3); char *dst = buf + VMB_LEN; /* get the starting offset from the group register */ int start_byte_ofs = reg->start_ofs; /* * The first element is the character index of the group text in the * source string. Calculate the character index by adding 1 to the * character length of the text preceding the group; calculate the * character length from the byte length of that string. Note that the * starting in the group register is stored from the starting point of * the search, not the start of the string, so we need to add in the * starting point in the search. */ utf8_ptr p((char *)last_str + VMB_LEN); put_list_int(&dst, p.len(start_byte_ofs) + 1); /* * The second element is the character length of the group text. * Calculate the character length from the byte length. */ p.set(p.getptr() + start_byte_ofs); put_list_int(&dst, p.len(reg->end_ofs - reg->start_ofs)); /* * The third element is the string itself. Create a new string * containing the matching substring. */ vm_obj_id_t strobj = CVmObjString::create( vmg_ FALSE, p.getptr(), reg->end_ofs - reg->start_ofs); put_list_obj(&dst, strobj); /* save the string on the stack momentarily to protect against GC */ G_stk->push()->set_obj(strobj); /* create and return the list value */ retval_obj(vmg_ CVmObjList::create(vmg_ FALSE, buf)); /* we no longer need the garbage collector protection */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * rexReplace() - replace one or all occurrences of a regular expression in * a given subject string with a given replacement string. This uses the * common find/replace handler defined in vmfindrep.h. */ void CVmBifTADS::re_replace(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 3, 6); /* * do the replacement - the subject string is the 2nd stack argument * (at stack offset 1), so the replacement string argument is at stack * offset 2 */ vm_find_replace( vmg_ G_interpreter->get_r0(), argc, G_stk->get(1), 0); /* discard arguments */ G_stk->discard(argc); } /* ------------------------------------------------------------------------ */ /* * savepoint - establish an undo savepoint */ void CVmBifTADS::savepoint(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 0); /* establish the savepoint */ G_undo->create_savept(vmg0_); } /* * undo - undo changes to most recent savepoint */ void CVmBifTADS::undo(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 0); /* if no undo is available, return nil to indicate that we can't undo */ if (G_undo->get_savept_cnt() == 0) { /* we can't undo */ retval_nil(vmg0_); } else { /* undo to the savepoint */ G_undo->undo_to_savept(vmg0_); /* tell the caller that we succeeded */ retval_true(vmg0_); } } /* ------------------------------------------------------------------------ */ /* * save */ void CVmBifTADS::save(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 1, 2); /* get the filename argument */ const vm_val_t *filespec = G_stk->get(0); /* * if there's a metadata table argument, fetch it (but leave the value * on the stack for gc protection) */ CVmObjLookupTable *metatab = 0; if (argc >= 2) { /* it must be a LookupTable object, or nil */ vm_val_t *v = G_stk->get(1); if (v->typ == VM_NIL) { /* nil, so there's no table - just discard the argument */ G_stk->discard(); } else { /* it's not nil, so it has to be a LookupTable object */ if (!CVmObjLookupTable::is_lookup_table_obj(vmg_ v->val.obj)) err_throw(VMERR_BAD_TYPE_BIF); /* get the lookup table object */ metatab = (CVmObjLookupTable *)vm_objp(vmg_ v->val.obj); } } /* set up for network storage server access, if applicable */ vm_rcdesc rc(vmg_ "saveGame", bif_table, 15, G_stk->get(0), argc); CVmNetFile *netfile = CVmNetFile::open( vmg_ filespec, &rc, NETF_WRITE | NETF_CREATE | NETF_TRUNC, OSFTT3SAV, "application/x-t3vm-state"); /* open the file and save the game */ CVmFile *file = 0; err_try { /* validate file safety */ CVmObjFile::check_safety_for_open(vmg_ netfile, VMOBJFILE_ACCESS_WRITE); /* open the file */ osfildef *fp = osfoprwtb(netfile->lclfname, OSFTT3SAV); if (fp == 0) err_throw(VMERR_CREATE_FILE); /* set up the file writer */ file = new CVmFile(); file->set_file(fp, 0); /* save the state */ CVmSaveFile::save(vmg_ file, metatab); /* close the file */ delete file; file = 0; } err_catch_disc { /* close the file if it's still open */ if (file != 0) delete file; /* abandon the network file */ if (netfile != 0) netfile->abandon(vmg0_); /* rethrow the error */ err_rethrow(); } err_end; /* close out the network file */ netfile->close(vmg0_); /* discard arguments */ G_stk->discard(argc); /* no return value */ retval_nil(vmg0_); } /* * restore */ void CVmBifTADS::restore(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 1); /* get the filename or spec */ const vm_val_t *filespec = G_stk->get(0); /* set up for network storage server access, if applicable */ vm_rcdesc rc(vmg_ "restoreGame", bif_table, 16, G_stk->get(0), argc); CVmNetFile *netfile = CVmNetFile::open( vmg_ filespec, &rc, NETF_READ, OSFTT3SAV, "application/x-t3vm-state"); /* open the file and restore the game */ CVmFile *file = 0; int err = 0; err_try { /* validate file safety */ CVmObjFile::check_safety_for_open(vmg_ netfile, VMOBJFILE_ACCESS_READ); /* open the file */ osfildef *fp = osfoprb(netfile->lclfname, OSFTT3SAV); if (fp == 0) err_throw(VMERR_FILE_NOT_FOUND); /* set up the file reader */ file = new CVmFile(fp, 0); /* restore the state; throw an exception on error */ if ((err = CVmSaveFile::restore(vmg_ file)) != 0) err_throw(err); } err_finally { /* close our local file */ if (file != 0) delete file; /* close the network file */ if (netfile != 0) netfile->close(vmg0_); } err_end; /* discard arguments */ G_stk->discard(argc); /* no return value */ retval_nil(vmg0_); } /* * restart */ void CVmBifTADS::restart(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 0); /* reset the VM to the image file's initial state */ CVmSaveFile::reset(vmg0_); /* no return value */ retval_nil(vmg0_); } /* ------------------------------------------------------------------------ */ /* * Get the maximum value from a set of argument */ void CVmBifTADS::get_max(VMG_ uint argc) { uint i; vm_val_t cur_max; /* make sure we have at least one argument */ if (argc < 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* start with the first argument as the presumptive maximum */ cur_max = *G_stk->get(0); /* if there's one list-like argument, get the max of the list elements */ if (argc == 1 && cur_max.is_listlike(vmg0_)) { /* get the list length */ vm_val_t lst = cur_max; uint cnt = lst.ll_length(vmg0_); /* if it's a zero-element list, it's an error */ if (cnt == 0) err_throw(VMERR_BAD_VAL_BIF); /* get the first element as the tentative maximum */ lst.ll_index(vmg_ &cur_max, 1); /* compare each additional list element */ for (i = 2 ; i <= cnt ; ++i) { /* compare the current element and keep it if it's the highest */ vm_val_t ele; lst.ll_index(vmg_ &ele, i); if (ele.compare_to(vmg_ &cur_max) > 0) cur_max = ele; } } else { /* compare each argument in turn */ for (i = 1 ; i < argc ; ++i) { /* * compare this value to the maximum so far; if this value is * greater, it becomes the new maximum so far */ if (G_stk->get(i)->compare_to(vmg_ &cur_max) > 0) cur_max = *G_stk->get(i); } } /* discard the arguments */ G_stk->discard(argc); /* return the maximum value */ retval(vmg_ &cur_max); } /* * Get the minimum value from a set of argument */ void CVmBifTADS::get_min(VMG_ uint argc) { uint i; vm_val_t cur_min; /* make sure we have at least one argument */ if (argc < 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* start with the first argument as the presumptive minimum */ cur_min = *G_stk->get(0); /* if there's one list-like argument, get the max of the list elements */ if (argc == 1 && cur_min.is_listlike(vmg0_)) { /* get the list length */ vm_val_t lst = cur_min; uint cnt = lst.ll_length(vmg0_); /* if it's a zero-element list, it's an error */ if (cnt == 0) err_throw(VMERR_BAD_VAL_BIF); /* get the first element as the tentative maximum */ lst.ll_index(vmg_ &cur_min, 1); /* compare each additional list element */ for (i = 2 ; i <= cnt ; ++i) { /* compare the current element and keep it if it's the highest */ vm_val_t ele; lst.ll_index(vmg_ &ele, i); if (ele.compare_to(vmg_ &cur_min) < 0) cur_min = ele; } } else { /* compare each argument in turn */ for (i = 1 ; i < argc ; ++i) { /* * compare this value to the minimum so far; if this value is * less, it becomes the new minimum so far */ if (G_stk->get(i)->compare_to(vmg_ &cur_min) < 0) cur_min = *G_stk->get(i); } } /* discard the arguments */ G_stk->discard(argc); /* return the minimum value */ retval(vmg_ &cur_min); } /* ------------------------------------------------------------------------ */ /* * makeString - construct a string by repeating a character; by * converting a unicode code point to a string; or by converting a list * of unicode code points to a string */ void CVmBifTADS::make_string(VMG_ uint argc) { vm_val_t val; long rpt; vm_obj_id_t new_str_obj; CVmObjString *new_str; size_t new_str_len; char *new_strp; const char *strp = 0; int lst_len = -1; size_t i; utf8_ptr dst; /* check arguments */ check_argc_range(vmg_ argc, 1, 2); /* get the base value */ G_stk->pop(&val); /* if there's a repeat count, get it */ rpt = (argc >= 2 ? pop_long_val(vmg0_) : 1); /* if the repeat count is less than zero, it's an error */ if (rpt < 0) err_throw(VMERR_BAD_VAL_BIF); /* leave the original value on the stack to protect it from GC */ G_stk->push(&val); /* * see what we have, and calculate how much space we'll need for the * result string */ switch(val.typ) { case VM_LIST: /* it's a list of integers giving unicode character values */ lst_len = val.ll_length(vmg0_); do_list: /* * Run through the list and get the size of each character, so * we can determine how long the string will have to be. */ for (new_str_len = 0, i = 1 ; (int)i <= lst_len ; ++i) { /* get this element */ vm_val_t ele_val; val.ll_index(vmg_ &ele_val, i); /* if it's not an integer, it's an error */ if (ele_val.typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); /* add this character's byte size to the string size */ new_str_len += utf8_ptr::s_wchar_size((wchar_t)ele_val.val.intval); } break; case VM_SSTRING: /* get the string pointer */ strp = G_const_pool->get_ptr(val.val.ofs); do_string: /* * it's a string - the output length is the same as the input * length */ new_str_len = vmb_get_len(strp); break; case VM_INT: /* * it's an integer giving a unicode character value - we just * need enough space to store this particular character */ new_str_len = utf8_ptr::s_wchar_size((wchar_t)val.val.intval); break; case VM_OBJ: /* check to see if it's a string */ if ((strp = val.get_as_string(vmg0_)) != 0) goto do_string; /* check to see if it's a list */ if (val.is_listlike(vmg0_) && (lst_len = val.ll_length(vmg0_)) >= 0) goto do_list; /* it's invalid */ err_throw(VMERR_BAD_TYPE_BIF); break; default: /* other types are invalid */ err_throw(VMERR_BAD_TYPE_BIF); break; } /* * if the length times the repeat count would be over the maximum * 16-bit string length, it's an error */ if (new_str_len * rpt > 0xffffL - VMB_LEN) err_throw(VMERR_BAD_VAL_BIF); /* multiply the length by the repeat count */ new_str_len *= rpt; /* allocate the string and gets its buffer */ new_str_obj = CVmObjString::create(vmg_ FALSE, new_str_len); new_str = (CVmObjString *)vm_objp(vmg_ new_str_obj); new_strp = new_str->cons_get_buf(); /* set up the destination pointer */ dst.set(new_strp); /* run through the number of iterations requested */ for ( ; rpt != 0 ; --rpt) { /* build one iteration of the string, according to the type */ if (lst_len >= 0) { /* run through the list */ for (i = 1 ; i <= (size_t)lst_len ; ++i) { /* get this element */ vm_val_t ele_val; val.ll_index(vmg_ &ele_val, i); /* add this character to the string */ dst.setch((wchar_t)ele_val.val.intval); } } else if (strp != 0) { /* copy the string's contents into the output string */ memcpy(dst.getptr(), strp + VMB_LEN, vmb_get_len(strp)); /* advance past the bytes we copied */ dst.set(dst.getptr() + vmb_get_len(strp)); } else { /* set this int value */ dst.setch((wchar_t)val.val.intval); } } /* return the new string */ retval_obj(vmg_ new_str_obj); /* discard the GC protection */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * makeList - construct a list by repeating a given value a given number of * times */ void CVmBifTADS::make_list(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 1, 2); /* get the value to use for each list element */ vm_val_t val; G_stk->pop(&val); /* if there's a repeat count, get it */ long rpt = (argc >= 2 ? pop_long_val(vmg0_) : 1); /* if the repeat count is less than zero, it's an error */ if (rpt < 0) err_throw(VMERR_BAD_VAL_BIF); /* leave the original value on the stack to protect it from GC */ G_stk->push(&val); /* allocate our return list */ vm_obj_id_t lst_obj = CVmObjList::create(vmg_ FALSE, rpt); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lst_obj); /* fill in the list with the repeated value */ for (int i = 0 ; i < rpt ; ++i) lst->cons_set_element(i, &val); /* return the new list */ retval_obj(vmg_ lst_obj); /* discard the GC protection */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * getFuncParams */ void CVmBifTADS::get_func_params(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 1); /* set up a method header pointer for the function pointer argument */ CVmFuncPtr hdr; if (!hdr.set(vmg_ G_stk->get(0))) err_throw(VMERR_FUNCPTR_VAL_REQD); /* * Allocate our return list. We need three elements: [minArgs, * optionalArgs, isVarargs]. */ vm_obj_id_t lst_obj = CVmObjList::create(vmg_ FALSE, 3); /* get the list object, properly cast */ CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lst_obj); /* set the minimum argument count */ vm_val_t val; val.set_int(hdr.get_min_argc()); lst->cons_set_element(0, &val); /* set the optional argument count */ val.set_int(hdr.get_opt_argc()); lst->cons_set_element(1, &val); /* set the varargs flag */ val.set_logical(hdr.is_varargs()); lst->cons_set_element(2, &val); /* return the list */ retval_obj(vmg_ lst_obj); } /* ------------------------------------------------------------------------ */ /* * sprintf */ /* bufprintf format string pointer */ struct bpptr { bpptr(const char *p, size_t len) { this->p = p; this->len = len; } bpptr(bpptr &src) { set(src); } /* set from another pointer */ void set(bpptr &src) { this->p = src.p; this->len = src.len; } /* do we have more in our buffer? */ int more() const { return len != 0; } /* get the current character */ char getch() const { return len != 0 ? *p : '\0'; } /* get and skip the current character */ char skipch() { if (len != 0) { char ch = *p; ++p, --len; return ch; } else return '\0'; } /* * match a character: if we match, return true and skip the character, * otherwise just return false */ int match_skip(char ch) { if (getch() == ch) { inc(); return TRUE; } else return FALSE; } /* skip the current character */ void inc() { if (len != 0) ++p, --len; } /* get the next wide character */ wchar_t getwch() const { return len != 0 ? utf8_ptr::s_getch(p) : 0; } /* get and skip the next wide character */ wchar_t skipwch() { wchar_t ch = getwch(); incwch(); return ch; } /* skip the next wide character */ void incwch() { if (len != 0) { size_t clen = utf8_ptr::s_charsize(*p); if (clen > len) clen = len; p += clen; len -= clen; } } /* parse an integer value */ int atoi() { int acc = 0; while (is_digit(getch())) { acc *= 10; acc += value_of_digit(skipch()); } return acc; } /* parse an integer value; return -1 if there's no integer here */ int check_atoi() { return is_digit(getch()) ? atoi() : -1; } /* current string pointer */ const char *p; /* remaining length in bytes */ size_t len; }; /* format_int() flags */ #define FI_UNSIGNED 0x0001 #define FI_CAPS 0x0002 /* '%' formatting options */ struct fmtopts { fmtopts() { sign = '\0'; group = 0; prec = -1; width = -1; left_align = FALSE; pad = ' '; pound = FALSE; } /* * sign character to show for positive numbers: '\0' to show nothing, ' * ' to show a space, '+' to show a plus */ char sign; /* group charater, or '\0' if not grouping */ wchar_t group; /* width (%10d); -1 means no width spec */ int width; /* precision (%.10s); -1 means no precision spec */ int prec; /* true -> left align the value in its field; false -> right align */ int left_align; /* padding character; default is space */ wchar_t pad; /* * '#' flag: e E f g G -> always use a decimal point; g G -> keep * trailing zeros; x X o -> use 0x/0X/0 prefix for non-zero values */ int pound; }; /* bufprintf output writer */ struct bpwriter { bpwriter(VMG_ CVmObjString *str) { this->vmg = VMGLOB_ADDR; this->str = str; this->dst = str->cons_get_buf(); } /* close - set the final string length */ void close() { VMGLOB_PTR(vmg); str->cons_shrink_buffer(vmg_ dst); } /* write a character */ void putch(char ch) { VMGLOB_PTR(vmg); dst = str->cons_append(vmg_ dst, &ch, 1, 64); } /* write a UTF8 character */ void putwch(wchar_t ch) { VMGLOB_PTR(vmg); dst = str->cons_append(vmg_ dst, ch, 64); } /* write a UTF8 character multiple times */ void putwch(wchar_t ch, int cnt) { while (cnt-- != 0) putwch(ch); } /* write a string */ void puts(const char *str) { puts(str, strlen(str)); } void puts(const char *str, size_t len) { VMGLOB_PTR(vmg); dst = this->str->cons_append(vmg_ dst, str, len, 64); } /* format a string value */ void format_string(VMG_ const vm_val_t *val, const fmtopts &opts) { /* if we don't have a string value, cast it to string */ vm_val_t strval; val->cast_to_string(vmg_ &strval); G_stk->push(&strval); /* get the string buffer and length */ const char *str = strval.get_as_string(vmg0_); size_t bytes = vmb_get_len(str); str += VMB_LEN; /* get the length of the string in characters */ utf8_ptr p((char *)str); size_t chars = p.len(bytes); /* If the string is longer than the precision, truncate it */ if (opts.prec >= 0 && (int)chars > opts.prec) { /* limit the length (in both chars and bytes) to the precision */ chars = opts.prec; bytes = p.bytelen(chars); } /* write it out with the appropriate padding and alignment */ format_with_padding(vmg_ str, bytes, chars, opts); /* discard our gc protection */ G_stk->discard(); } /* format a character value */ void format_char(VMG_ const vm_val_t *val) { /* check what we have */ const char *str = val->get_as_string(vmg0_); if (str != 0) { /* it's a string - show the first character */ size_t len = vmb_get_len(str); str += VMB_LEN; if (len != 0) { /* write the first character */ putwch(utf8_ptr::s_getch(str)); } else { /* empty string - write a null character */ putch((char)0); } } else { /* it's not a string - get the integer value */ int i = val->cast_to_int(vmg0_); /* make sure it's in range */ if (i < 0 || i > 65535) err_throw(VMERR_NUM_OVERFLOW); /* write the character value */ putwch((wchar_t)i); } } /* format an internal UTF-8 length+string, with padding and alignment */ void format_with_padding(VMG_ const char *str, const fmtopts &opts) { /* figure the byte length and get the buffer */ size_t bytes = vmb_get_len(str); str += VMB_LEN; /* figure the character length */ utf8_ptr p((char *)str); size_t chars = p.len(bytes); /* write it out */ format_with_padding(vmg_ str, bytes, chars, opts); } /* format a UTF-8 string value, adding padding and alignment */ void format_with_padding(VMG_ const char *str, size_t bytes, size_t chars, const fmtopts &opts) { /* * Figure the amount of padding: if the width is larger than the * string length (in characters), pad by the difference. */ int npad = (opts.width > (int)chars ? opts.width - chars : 0); /* if right-aligning, write the padding */ if (!opts.left_align) putwch(opts.pad, npad); /* write the string */ puts(str, bytes); /* if left-aligning, write the padding */ if (opts.left_align) putwch(opts.pad, npad); } /* format an integer value */ void format_int(VMG_ const vm_val_t *val, int radix, const char *type_prefix, const fmtopts &opts, int flags = 0) { /* a stack buffer for conversions that can use it */ char buf[256]; /* check the type */ switch (val->typ) { case VM_NIL: /* format nil as zero */ format_int(vmg_ "0", 1, type_prefix, opts, flags); break; case VM_TRUE: /* format true as one */ format_int(vmg_ "1", 1, type_prefix, opts, flags); break; case VM_INT: { /* set flags - round to integer, unsigned if applicable */ int cvtflags = TOSTR_ROUND; if ((flags & FI_UNSIGNED) != 0) cvtflags |= TOSTR_UNSIGNED; /* get the basic string representation of the integer */ const char *p = CVmObjString::cvt_int_to_str( buf, sizeof(buf), val->val.intval, radix, cvtflags); /* apply our extra formatting to the basic string rep */ format_int(vmg_ p + VMB_LEN, vmb_get_len(p), type_prefix, opts, flags); } break; default: { /* * figure the string conversion flags: round to integer, * and use an unsigned interpretation if the format is * unsigned */ int tsflags = TOSTR_ROUND; if (flags & FI_UNSIGNED) tsflags |= TOSTR_UNSIGNED; /* cast the value to a numeric type */ vm_val_t num; val->cast_to_num(vmg_ &num); G_stk->push(&num); /* ...thence to string, to get a printable representation */ vm_val_t str; const char *p = CVmObjString::cvt_to_str( vmg_ &str, buf, sizeof(buf), &num, radix, tsflags); G_stk->push(&str); /* apply our extra formatting to the basic string rep */ format_int(vmg_ p + VMB_LEN, vmb_get_len(p), type_prefix, opts, flags); /* discard the GC protection */ G_stk->discard(2); } break; } } /* * Format an integer value for which we've generated a basic string * buffer value. This applies our extra formatting - padding, plus * sign, alignment, case conversion. */ void format_int(VMG_ const char *p, size_t len, const char *type_prefix, const fmtopts &opts, int flags) { /* if they're trying to pawn off an empty string on us, use "0" */ if (p == 0 || len == 0) p = "0", len = 1; /* note if the value is all zeros */ int zero = TRUE; const char *p2 = p; for (size_t i = 0 ; i < len ; ++i) { if (*p2 != '0') { zero = FALSE; break; } } /* * get the number of digits: assume that the whole thing is digits * except for a leading minus sign */ int digits = len; if (*p == '-') --digits; /* * Figure the display width required. Start with the length of the * string. If we the "sign" option is set and we don't have a "-" * sign, add space for a "+" sign. If the "group" option is set, * add a comma for each group of three digits. If the '#' flag is * set, add the type prefix if the value is nonzero. */ int dispwid = len; if (opts.sign != '\0' && *p != '-') dispwid += 1; if (opts.group != 0) dispwid += ((len - (*p == '-' ? 1 : 0)) - 1)/3; if (opts.pound && type_prefix != 0 && !zero) dispwid += strlen(type_prefix); /* * If there's a precision setting, it means that we're to add * leading zeros to bring the number of digits up to the * precision. */ if (digits < opts.prec) dispwid += opts.prec - digits; /* * Figure the amount of padding. If there's an explicit width spec * in the options, and the display width is less than the width * spec, pad by the differene. */ int padcnt = (opts.width > dispwid ? opts.width - dispwid : 0); /* if they want right alignment, add padding characters before */ if (!opts.left_align) putwch(opts.pad, padcnt); /* add the + sign if needed */ if (opts.sign && *p != '-') putch(opts.sign); /* if there's a '-' sign, write it */ if (*p == '-') putch(*p++), --len; /* add the type prefix */ if (opts.pound && type_prefix != 0 && !zero) puts(type_prefix); /* add the leading zeros for the precision */ for (int i = digits ; i < opts.prec ; ++i) putch('0'); /* write the digits, adding grouping commas and converting case */ for (int dig = 0 ; len != 0 ; ++p, --len, ++dig) { /* * if this isn't the first digit, and we have a multiple of * three digits remaining, and we're using grouping, add the * group comma */ if (opts.group != 0 && dig != 0 && len % 3 == 0) putwch(opts.group); /* write this character, converting case as needed */ if (is_digit(*p)) putch(*p); else if ((flags & FI_CAPS) != 0) putch((char)toupper(*p)); else putch((char)tolower(*p)); } /* if they want left alignment, add padding characters after */ if (opts.left_align) putwch(opts.pad, padcnt); } /* format a Roman numeral */ void format_roman(VMG_ const vm_val_t *val, const fmtopts &opts, int flags) { char buf[40]; /* get the integer value */ int32_t i = val->cast_to_int(vmg0_); /* * use Roman numerals if it's in the range 1-4999, otherwise just * treat it like '%d' */ if (i >= 1 && i <= 4999) { /* Roman numeral conversion chart */ static const struct { const char *numeral; int val; } r[] = { { "m", 1000 }, { "cm", 900 }, { "d", 500 }, { "cd", 400 }, { "c", 100 }, { "xc", 90 }, { "l", 50 }, { "x", 10 }, { "ix", 9 }, { "v", 5 }, { "iv", 4 }, { "i", 1 } }; /* * convert by repeatedly appending the highest-value Roman * numeral less than or equal to the number, deducting each * Roman numeral value from the remaining number balance until * we reach zero */ for (int ri = 0 ; i != 0 && ri < countof(r) ; ) { /* if this one fits, append this Roman numeral */ if (r[ri].val <= i) { /* append this numeral, converting case if needed */ for (const char *p = r[ri].numeral ; *p != 0 ; ++p) { if ((flags & FI_CAPS) != 0) putch((char)toupper(*p)); else putch(*p); } /* deduct its value from the remaining balance */ i -= r[ri].val; } else { /* this numeral is too large - move on to the next one */ ++ri; } } } else { t3sprintf(buf, sizeof(buf), "%ld", (long)i); format_int(vmg_ buf, strlen(buf), 0, opts, 0); } } /* format a floating-point value */ void format_float(VMG_ const vm_val_t *val, char type_spec, const fmtopts &opts) { /* a stack buffer for conversions that can use it */ char buf[256]; /* * Use the precision specified, with a default of 6 digits; assume * that there's no maximum number of digits. */ int prec = (opts.prec >= 0 ? opts.prec : 6); int maxdigs = -1; /* figure the formatter flags */ ulong flags = VMBN_FORMAT_LEADING_ZERO; /* if the sign option is set, always show a sign */ if (opts.sign == '+') flags |= VMBN_FORMAT_SIGN; else if (opts.sign == ' ') flags |= VMBN_FORMAT_POS_SPACE; /* * if the '#' flag is set, always show a decimal point, and keep * trailing zeros with 'g' and 'G' */ if (opts.pound) { flags |= VMBN_FORMAT_POINT; if (type_spec == 'g' || type_spec == 'G') flags |= VMBN_FORMAT_TRAILING_ZEROS; } /* for 'E' or 'G', use capital 'E' for the exponent indicator */ if (type_spec == 'E' || type_spec == 'G') flags |= VMBN_FORMAT_EXP_CAP; /* * for 'e' or 'E', always use scientific notation, with a sign * symbol in the exponent value */ if (type_spec == 'e' || type_spec == 'E') flags |= VMBN_FORMAT_EXP | VMBN_FORMAT_EXP_SIGN; /* if the type code is 'g' or 'G', use "compact" notation */ if (type_spec == 'g' || type_spec == 'G') { /* set the compact notation flag */ flags |= VMBN_FORMAT_COMPACT | VMBN_FORMAT_EXP_SIGN; /* the precision is the number of significant digits to show */ flags |= VMBN_FORMAT_MAXSIG; maxdigs = prec; prec = -1; } /* cast the value to a numeric type */ vm_val_t num; val->cast_to_num(vmg_ &num); G_stk->push(&num); /* check the type */ switch (num.typ) { case VM_INT: { /* format the integer as though it were a float */ const char *p = CVmObjBigNum::cvt_int_to_string_buf( buf, sizeof(buf), num.val.intval, maxdigs, -1, prec, 3, flags); /* write it out, adding padding and alignment */ format_with_padding(vmg_ p, opts); } break; case VM_OBJ: /* if it's an object, it should be a BigNumber */ if (CVmObjBigNum::is_bignum_obj(vmg_ num.val.obj)) { /* get the BigNumber object */ CVmObjBigNum *bn = (CVmObjBigNum *)vm_objp(vmg_ num.val.obj); /* format the BigNumber */ vm_val_t str; const char *p = bn->cvt_to_string_buf( vmg_ &str, buf, sizeof(buf), maxdigs, -1, prec, 3, flags); G_stk->push(&str); /* write it out, adding padding and alignment */ format_with_padding(vmg_ p, opts); /* discard gc protection */ G_stk->discard(); } break; default: break; } /* discard our gc protection */ G_stk->discard(); } /* result string */ CVmObjString *str; /* current write pointer */ char *dst; /* VM globals */ vm_globals *vmg; }; /* * Internal sprintf formatter. The arguments are on the stack in the usual * function call order, with the first argument at top of stack. We'll * allocate a new String object to hold the result. */ static void tsprintf(VMG_ vm_val_t *retval, const char *fmtp, size_t fmtl, int arg0, int argc) { /* set up a format string reader */ bpptr fmt(fmtp, fmtl); /* * Create a string to hold the result. Use 150% the length of the * format string as a guess, with a minimum of 64 characters; we'll * expand this as needed as we go. */ size_t init_len = (fmtl < 43 ? 64 : fmtl*3/2); retval->set_obj(CVmObjString::create(vmg_ FALSE, init_len)); /* push it for gc protection */ G_stk->push(retval); /* adjust the argument base for our additions */ arg0 += 1; /* set up an output writer */ bpwriter dst(vmg_ (CVmObjString *)vm_objp(vmg_ retval->val.obj)); /* set up a nil value for missing arguments */ vm_val_t nil_val; nil_val.set_nil(); /* * Scan the format string. The string is in utf8 format as always, but * our format codes are all plain ASCII characters, so a simple byte * scan is perfectly adequate. */ for (int argpos = 1 ; fmt.more() ; ) { /* check for format codes */ char ch = fmt.skipch(); if (ch == '%') { /* format specifier - remember where the '%' was */ const char *pct = fmt.p - 1; /* set up our initial default options */ fmtopts opts; int argno = -1; /* parse flags until we're out of them */ for (int flags_done = FALSE ; !flags_done ; ) { /* check the next character */ switch (fmt.getch()) { case '[': /* argument number specifier - "[digits]" */ fmt.inc(); argno = fmt.atoi(); fmt.match_skip(']'); break; case '+': /* note the sign specifier */ opts.sign = '+'; fmt.inc(); break; case ' ': /* note the blank-for-plus specifier */ opts.sign = ' '; fmt.inc(); break; case ',': /* note the group specifier */ opts.group = ','; fmt.inc(); break; case '-': /* note the left-alignment specifier */ opts.left_align = TRUE; fmt.inc(); break; case '_': /* padding spec - the next charater is the pad char */ fmt.inc(); opts.pad = fmt.skipwch(); break; case '#': /* pound flag - special flag per type */ opts.pound = TRUE; fmt.inc(); break; default: /* it's not an option flag, so we're done with flags */ flags_done = TRUE; break; } } /* * Next comes the width, but there's one more flag character * that has a special position just before the width: '0', to * specify leading zero padding. */ if (fmt.match_skip('0')) { /* obey this only if we're in right-align mode */ if (!opts.left_align) opts.pad = '0'; } /* check for a width specifier */ opts.width = fmt.check_atoi(); /* check for a precision specifier */ if (fmt.match_skip('.')) opts.prec = fmt.atoi(); /* we're at the type specifier */ char type_spec = fmt.skipch(); /* presume we'll use an argument */ int used_arg = TRUE; /* retrieve the current argument value */ int argi = (argno > 0 ? argno : argpos); const vm_val_t *val = (argi <= argc ? G_stk->get(arg0 + argi - 1) : &nil_val); /* apply the substitution based on the type */ switch (type_spec) { case '%': /* literal % - copy it */ dst.putch('%'); /* this doesn't use an argument */ used_arg = FALSE; break; case 'b': /* number -> binary integer */ dst.format_int(vmg_ val, 2, 0, opts, FI_UNSIGNED); break; case 'c': /* number -> Unicode character */ dst.format_char(vmg_ val); break; case 'd': /* number -> decimal integer */ dst.format_int(vmg_ val, 10, 0, opts); break; case 'r': /* number -> roman numeral (lowercase) */ dst.format_roman(vmg_ val, opts, 0); break; case 'R': /* number -> roman numeral (uppercase) */ dst.format_roman(vmg_ val, opts, FI_CAPS); break; case 'u': /* number -> decimal integer, unsigned interpretation */ dst.format_int(vmg_ val, 10, 0, opts, FI_UNSIGNED); break; case 'e': case 'E': case 'f': case 'g': case 'G': /* number -> floating point */ dst.format_float(vmg_ val, type_spec, opts); break; case 'o': /* number -> octal integer */ dst.format_int(vmg_ val, 8, "0", opts, FI_UNSIGNED); break; case 's': /* string */ dst.format_string(vmg_ val, opts); break; case 'x': /* number -> hex integer, lowercase letters */ dst.format_int(vmg_ val, 16, "0x", opts, FI_UNSIGNED); break; case 'X': /* number -> hex integer, uppercase letters */ dst.format_int(vmg_ val, 16, "0X", opts, FI_UNSIGNED | FI_CAPS); break; default: /* anything else is invalid - just copy the source % string */ dst.puts(pct, fmt.p - pct); /* we didn't use an argument after all */ used_arg = FALSE; break; } /* if we used a positional argument, count it */ if (used_arg && argno < 0) ++argpos; } else { /* just copy this byte verbatim */ dst.putch(ch); } } /* set the final result string length */ dst.close(); /* done with our gc protection */ G_stk->discard(); } /* * sprintf */ void CVmBifTADS::sprintf(VMG_ uint argc) { /* we need at least one argument */ if (argc < 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* get the format string */ const char *fmt = G_stk->get(0)->get_as_string(vmg0_); if (fmt == 0) err_throw(VMERR_STRING_VAL_REQD); /* get the length and string buffer */ size_t fmtl = vmb_get_len(fmt); fmt += VMB_LEN; /* do the sprintf */ vm_val_t retv; tsprintf(vmg_ &retv, fmt, fmtl, 1, argc - 1); /* return the new string */ retval(vmg_ &retv); /* discard arguments */ G_stk->discard(argc); } /* ------------------------------------------------------------------------ */ /* * abs() - absolute value */ void CVmBifTADS::get_abs(VMG_ uint argc) { /* we need exactly one argument */ if (argc != 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* check the argument type */ switch (G_stk->get(0)->typ) { case VM_INT: /* integer */ { /* get the integer */ int32_t i = G_stk->get(0)->val.intval; /* if it's negative, negate it */ if (i < 0) i = -i; /* return the result */ retval_int(vmg_ i); } break; case VM_OBJ: /* object */ { /* try a BigNumber cast */ CVmObjBigNum *b = vm_val_cast(CVmObjBigNum, G_stk->get(0)); if (b != 0) { /* get the absolute value of the BigNumber */ b->abs_val(vmg_ G_interpreter->get_r0(), G_stk->get(0)->val.obj); } else { /* invalid type */ err_throw(VMERR_BAD_TYPE_BIF); } } break; default: err_throw(VMERR_BAD_TYPE_BIF); } /* discard arguments */ G_stk->discard(argc); } /* ------------------------------------------------------------------------ */ /* * sgn() - sign */ void CVmBifTADS::get_sgn(VMG_ uint argc) { /* we need exactly one argument */ if (argc != 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* check the argument type */ switch (G_stk->get(0)->typ) { case VM_INT: /* integer */ { /* get the integer */ int32_t i = G_stk->get(0)->val.intval; /* compute the sgn value */ retval_int(vmg_ i < 0 ? -1 : i == 0 ? 0 : 1); } break; case VM_OBJ: /* object */ { /* try a BigNumber cast */ CVmObjBigNum *b = vm_val_cast(CVmObjBigNum, G_stk->get(0)); if (b != 0) { /* compute the sgn value */ b->sgn_val(vmg_ G_interpreter->get_r0(), G_stk->get(0)->val.obj); } else { /* invalid type */ err_throw(VMERR_BAD_TYPE_BIF); } } break; default: err_throw(VMERR_BAD_TYPE_BIF); } /* discard arguments */ G_stk->discard(argc); } /* ------------------------------------------------------------------------ */ /* * concat() */ void CVmBifTADS::concat(VMG_ uint argc) { /* * First, run through the arguments. Convert each one that's not * already a string to a string, and count up the lengths. */ size_t len = 0; for (uint i = 0 ; i < argc ; ++i) { /* cast this value to a string */ vm_val_t newval; const char *str = G_stk->get(i)->cast_to_string(vmg_ &newval); /* add its length */ len += vmb_get_len(str); /* replace it in the stack, in case we did a conversion */ *G_stk->get(i) = newval; } /* allocate the result string */ vm_obj_id_t retobj = CVmObjString::create(vmg_ FALSE, len); CVmObjString *retstr = vm_objid_cast(CVmObjString, retobj); /* get its construction buffer */ char *dst = retstr->cons_get_buf(); /* run through the arguments again, copying them into the result buffer */ for (uint i = 0 ; i < argc ; ++i) { /* get this value as a string (it's already been cast) */ const char *src = G_stk->get(i)->get_as_string(vmg0_); /* parse and skip the length prefix */ size_t len = vmb_get_len(src); src += VMB_LEN; /* add the string to the output buffer */ memcpy(dst, src, len); dst += len; } /* return the result string */ retval_obj(vmg_ retobj); /* discard arguments */ G_stk->discard(argc); } qtads-2.1.7/tads3/vmbiftad.h000066400000000000000000000171161265017072300156310ustar00rootroot00000000000000/* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbiftad.h - function set definition - TADS function set Function Notes Modified 12/06/98 MJRoberts - Creation */ #ifndef VMBIFTAD_H #define VMBIFTAD_H #include "os.h" #include "vmbif.h" #include "utf8.h" /* ------------------------------------------------------------------------ */ /* * Include headers for our selectable Random Number Generator algorithms. */ #include "vmisaac.h" #include "vmmersenne.h" /* ------------------------------------------------------------------------ */ /* * TADS function set built-in functions */ class CVmBifTADS: public CVmBif { public: /* function vector */ static vm_bif_desc bif_table[]; /* * General functions */ static void datatype(VMG_ uint argc); static void getarg(VMG_ uint argc); static void firstobj(VMG_ uint argc); static void nextobj(VMG_ uint argc); static void randomize(VMG_ uint argc); static void rand(VMG_ uint argc); static void toString(VMG_ uint argc); static void toInteger(VMG_ uint argc); static void toNumber(VMG_ uint argc); static void gettime(VMG_ uint argc); static void re_match(VMG_ uint argc); static void re_search(VMG_ uint argc); static void re_group(VMG_ uint argc); static void re_replace(VMG_ uint argc); static void savepoint(VMG_ uint argc); static void undo(VMG_ uint argc); static void save(VMG_ uint argc); static void restore(VMG_ uint argc); static void restart(VMG_ uint argc); static void get_max(VMG_ uint argc); static void get_min(VMG_ uint argc); static void make_string(VMG_ uint argc); static void get_func_params(VMG_ uint argc); static void sprintf(VMG_ uint argc); static void make_list(VMG_ uint argc); static void get_abs(VMG_ uint argc); static void get_sgn(VMG_ uint argc); static void concat(VMG_ uint argc); static void re_search_back(VMG_ uint argc); /* internal toString interface */ static void toString(VMG_ vm_val_t *retval, const vm_val_t *srcval, int radix, int flags); /* format a date-and-time list per getTime(GetDateAndTime) */ static vm_obj_id_t format_datetime_list(VMG_ os_time_t timer); protected: /* common handler for re_search() and re_search_back() */ template inline static void re_search_common(VMG_ uint argc); /* enumerate objects (common handler for firstobj and nextobj) */ static void enum_objects(VMG_ uint argc, vm_obj_id_t start_obj); /* common handler for toInteger and toNumber */ static void toIntOrNum(VMG_ uint argc, int int_only); }; /* ------------------------------------------------------------------------ */ /* * Random Number Generator ID values for randomize(id, ...) */ #define VMBT_RNGID_ISAAC 1 #define VMBT_RNGID_LCG 2 #define VMBT_RNGID_MT19937 3 #define VMBT_RNGID_BITSHIFT 4 /* ------------------------------------------------------------------------ */ /* * Global information for the TADS intrinsics. We allocate this * structure with the VM global variables - G_bif_tads_globals contains * the structure. */ class CVmBifTADSGlobals { public: /* creation */ CVmBifTADSGlobals(VMG0_); /* deletion */ ~CVmBifTADSGlobals(); /* regular expression parser and searcher */ class CRegexParser *rex_parser; class CRegexSearcherSimple *rex_searcher; /* * global variable for the last regular expression search string (we * need to hold onto this because we might need to extract group-match * substrings from it) */ struct vm_globalvar_t *last_rex_str; /* the currently active RNG ID (VMBT_RNGID_xxx) */ int rng_id; /* -------------------------------------------------------------------- */ /* * Linear Congruential Random Number Generator state - this is just a * 32-bit seed value. */ int32_t lcg_rand_seed; /* -------------------------------------------------------------------- */ /* * Bit-Shift Random Number Generator state */ #ifdef VMBIFTADS_RNG_BITSHIFT /* bit-shift generator seed value */ int32_t bits_rand_seed; #endif /* VMBIFTADS_RNG_BITSHIFT */ /* -------------------------------------------------------------------- */ /* * ISAAC Random Number Generator state */ struct isaacctx *isaac_ctx; /* -------------------------------------------------------------------- */ /* * Mersenne Twister MT19937 Random Number Generator state */ class CVmMT19937 *mt_ctx; }; /* end of section protected against multiple inclusion */ #endif /* VMBIFTAD_H */ /* ------------------------------------------------------------------------ */ /* * TADS function set vector. Define this only if VMBIF_DEFINE_VECTOR has * been defined, so that this file can be included for the prototypes alone * without defining the function vector. * * Note that this vector is specifically defined outside of the section of * the file protected against multiple inclusion. */ #ifdef VMBIF_DEFINE_VECTOR /* TADS general data manipulation functions */ vm_bif_desc CVmBifTADS::bif_table[] = { { &CVmBifTADS::datatype, 1, 0, FALSE }, /* 0 */ { &CVmBifTADS::getarg, 1, 0, FALSE }, /* 1 */ { &CVmBifTADS::firstobj, 0, 2, FALSE }, /* 2 */ { &CVmBifTADS::nextobj, 1, 2, FALSE }, /* 3 */ { &CVmBifTADS::randomize, 0, 0, FALSE }, /* 4 */ { &CVmBifTADS::rand, 0, 0, TRUE }, /* 5 */ { &CVmBifTADS::toString, 1, 2, FALSE }, /* 6 */ { &CVmBifTADS::toInteger, 1, 1, FALSE }, /* 7 */ { &CVmBifTADS::gettime, 0, 1, FALSE }, /* 8 */ { &CVmBifTADS::re_match, 2, 1, FALSE }, /* 9 */ { &CVmBifTADS::re_search, 2, 1, FALSE }, /* 10 */ { &CVmBifTADS::re_group, 1, 0, FALSE }, /* 11 */ { &CVmBifTADS::re_replace, 3, 2, FALSE }, /* 12 */ { &CVmBifTADS::savepoint, 0, 0, FALSE }, /* 13 */ { &CVmBifTADS::undo, 0, 0, FALSE }, /* 14 */ { &CVmBifTADS::save, 1, 0, FALSE }, /* 15 */ { &CVmBifTADS::restore, 1, 0, FALSE }, /* 16 */ { &CVmBifTADS::restart, 0, 0, FALSE }, /* 17 */ { &CVmBifTADS::get_max, 1, 0, TRUE }, /* 18 */ { &CVmBifTADS::get_min, 1, 0, TRUE }, /* 19 */ { &CVmBifTADS::make_string, 1, 1, FALSE }, /* 20 */ { &CVmBifTADS::get_func_params, 1, 0, FALSE }, /* 21 */ { &CVmBifTADS::toNumber, 1, 1, FALSE }, /* 23 */ { &CVmBifTADS::sprintf, 1, 0, TRUE }, /* 24 */ { &CVmBifTADS::make_list, 1, 1, FALSE }, /* 25 */ { &CVmBifTADS::get_abs, 1, 0, FALSE }, /* 26 */ { &CVmBifTADS::get_sgn, 1, 0, FALSE }, /* 27 */ { &CVmBifTADS::concat, 0, 0, TRUE }, /* 28 */ { &CVmBifTADS::re_search_back, 2, 1, FALSE } /* 29 */ }; #endif /* VMBIF_DEFINE_VECTOR */ qtads-2.1.7/tads3/vmbiftio.cpp000066400000000000000000002175451265017072300162170ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbiftio.cpp - TADS Input/Output functions Function Notes Modified 02/08/00 MJRoberts - Creation */ #include #include #include #include "t3std.h" #include "os.h" #include "utf8.h" #include "charmap.h" #include "vmbiftio.h" #include "vmstack.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmglob.h" #include "vmpool.h" #include "vmobj.h" #include "vmstr.h" #include "vmlst.h" #include "vmrun.h" #include "vmfile.h" #include "vmconsol.h" #include "vmstrres.h" #include "vmvsn.h" #include "vmhost.h" #include "vmpredef.h" #include "vmcset.h" #include "vmfilobj.h" #include "vmfilnam.h" #include "vmnetfil.h" #include "vmnet.h" /* ------------------------------------------------------------------------ */ /* * Display a value */ void CVmBifTIO::say(VMG_ uint argc) { /* write the value to the main console */ say_to_console(vmg_ G_console, argc); } /* * Display the value or values at top of stack to the given console */ void CVmBifTIO::say_to_console(VMG_ CVmConsole *console, uint argc) { vm_val_t val; const char *str; char buf[30]; size_t len; vm_val_t new_str; /* presume we won't need to create a new string value */ new_str.set_nil(); /* display each argument */ for ( ; argc != 0 ; --argc) { /* get our argument */ G_stk->pop(&val); /* see what we have */ switch(val.typ) { case VM_SSTRING: /* get the string */ str = G_const_pool->get_ptr(val.val.ofs); /* the original value is our string */ new_str = val; disp_str: /* push the string to protect from garbage collection */ G_stk->push(&new_str); /* display the string through the output formatter */ console->format_text(vmg_ str + 2, osrp2(str)); /* discard the saved string now that we no longer need it */ G_stk->discard(); /* done */ break; case VM_OBJ: /* convert it to a string */ str = vm_objp(vmg_ val.val.obj) ->cast_to_string(vmg_ val.val.obj, &new_str); /* go display it */ goto disp_str; case VM_LIST: /* convert to a string */ str = val.cast_to_string(vmg_ &new_str); goto disp_str; case VM_INT: /* convert it to a string */ sprintf(buf + 2, "%ld", (long)val.val.intval); /* set its length */ len = strlen(buf + 2); oswp2(buf, len); /* display it */ str = buf; goto disp_str; case VM_NIL: /* display nothing */ break; default: /* other types are invalid */ err_throw(VMERR_BAD_TYPE_BIF); } } } /* ------------------------------------------------------------------------ */ /* * Logging - turn on or off output text capture */ #define LOG_SCRIPT 1 #define LOG_CMD 2 #define LOG_EVENT 3 void CVmBifTIO::logging(VMG_ uint argc) { /* presume success */ int ok = TRUE; /* check arguments */ check_argc_range(vmg_ argc, 1, 2); /* get the arguments: filename, log type */ const vm_val_t *filespec = G_stk->get(0); int log_type = (argc >= 2 ? G_stk->get(1)->num_to_int(vmg0_) : LOG_SCRIPT); /* * if they passed us nil, turn off logging; otherwise, start logging * to the filename given by the string */ if (filespec->typ == VM_NIL) { /* turn off the appropriate type of logging */ switch(log_type) { case LOG_SCRIPT: G_console->close_log_file(vmg0_); break; case LOG_CMD: case LOG_EVENT: G_console->close_command_log(vmg0_); break; default: err_throw(VMERR_BAD_VAL_BIF); } } else { /* set up the recursive call context */ vm_rcdesc rc(vmg_ "setLogFile", bif_table, 1, G_stk->get(0), argc); /* open the appropriate log file */ switch(log_type) { case LOG_SCRIPT: ok = !G_console->open_log_file(vmg_ filespec, &rc); break; case LOG_CMD: case LOG_EVENT: ok = !G_console->open_command_log( vmg_ filespec, &rc, log_type == LOG_EVENT); break; default: err_throw(VMERR_BAD_VAL_BIF); } } /* discard arguments */ G_stk->discard(argc); /* return true on success, nil on error */ retval_bool(vmg_ ok); } /* ------------------------------------------------------------------------ */ /* * clearscreen - clear the main display screen */ void CVmBifTIO::clearscreen(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 0); /* ask the console to clear the screen */ G_console->clear_window(vmg0_); } /* ------------------------------------------------------------------------ */ /* * more - show MORE prompt */ void CVmBifTIO::more(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 0); /* ignore in web host mode */ if (G_net_config != 0) return; /* * if we're reading from a script, ignore this request - these types of * interactive pauses are irrelevant when reading a script, since we're * getting our input non-interactively */ if (G_console->is_reading_script()) return; /* flush the display output */ G_console->flush_all(vmg_ VM_NL_NONE); /* show the MORE prompt */ G_console->show_more_prompt(vmg0_); } /* ------------------------------------------------------------------------ */ /* * input - get a line of input from the keyboard */ void CVmBifTIO::input(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 0); /* read a line of text from the keyboard */ char buf[256]; if (G_console->read_line(vmg_ buf, sizeof(buf))) { /* end of file - return nil */ retval_nil(vmg0_); } else { /* return the string */ retval_str(vmg_ buf); } } /* ------------------------------------------------------------------------ */ /* * inputkey - read a keystroke */ void CVmBifTIO::inputkey(VMG_ uint argc) { char buf[32]; char c[10]; size_t len; int evt; static const int filter[] = { OS_EVT_KEY }; /* check arguments */ check_argc(vmg_ argc, 0); /* check for script input */ if (G_console->read_event_script( vmg_ &evt, buf, sizeof(buf), filter, sizeof(filter)/sizeof(filter[0]), 0)) { /* we got a key from the script */ retval_ui_str(vmg_ buf); /* log the event */ G_console->log_event(vmg_ OS_EVT_KEY, buf, strlen(buf), FALSE); /* done */ return; } /* flush any output */ G_console->flush_all(vmg_ VM_NL_INPUT); /* there's no console in web host mode - simply return eof */ if (G_net_config != 0) { retval_ui_str(vmg_ "[eof]"); return; } /* get a keystroke */ c[0] = (char)os_getc_raw(); len = 1; /* if it's an extended key, map it specially */ if (c[0] == 0) { char extc; /* get the second part of the sequence */ extc = (char)os_getc_raw(); /* map the extended key */ map_ext_key(vmg_ buf, (unsigned char)extc); } else { /* continue fetching bytes until we have a full character */ while (!raw_key_complete(vmg_ c, len) && len + 1 < sizeof(c)) { /* * We don't yet have enough bytes for a complete character, so * read another raw byte. The keyboard driver should already * have queued up all of the bytes we need to complete the * character sequence, so there should never be a delay from * os_getc_raw() here - it should simply return the next byte * of the sequence immediately. */ c[len++] = (char)os_getc_raw(); } c[len] = '\0'; /* * translate the key from the local character set to UTF-8, and map * the extended key code to the portable representation */ map_raw_key(vmg_ buf, c, len); } /* reset the [MORE] counter */ G_console->reset_line_count(FALSE); /* log the event */ G_console->log_event(vmg_ OS_EVT_KEY, buf, strlen(buf), TRUE); /* return the string */ retval_str(vmg_ buf); } /* ------------------------------------------------------------------------ */ /* * inputevent - read an event */ void CVmBifTIO::inputevent(VMG_ uint argc) { int use_timeout; unsigned long timeout; os_event_info_t info; int evt; int ele_count; vm_obj_id_t lst_obj; CVmObjList *lst; char keyname[32]; vm_val_t val; int from_script = FALSE; static const int filter[] = { OS_EVT_KEY, OS_EVT_TIMEOUT, OS_EVT_NOTIMEOUT, OS_EVT_HREF, OS_EVT_EOF, OS_EVT_COMMAND }; /* check arguments */ check_argc_range(vmg_ argc, 0, 1); /* if there's a timeout argument, get it */ if (argc == 0) { /* there's no timeout */ use_timeout = FALSE; timeout = 0; } else if (G_stk->get(0)->typ == VM_NIL) { /* the timeout is nil, which is the same as no timeout */ use_timeout = FALSE; timeout = 0; /* discard the nil timeout value */ G_stk->discard(); } else { /* pop the timeout value */ timeout = pop_long_val(vmg0_); /* note that we have a timeout to use */ use_timeout = TRUE; } /* check for script input */ if (G_console->read_event_script( vmg_ &evt, info.href, sizeof(info.href), filter, sizeof(filter)/sizeof(filter[0]), 0)) { /* we got a script event - note it */ from_script = TRUE; /* translate certain events */ switch (evt) { case OS_EVT_COMMAND: /* read the numeric parameter */ info.cmd_id = atoi(info.href); break; } } else if (G_net_config != 0) { /* there's no console in web host mode - return eof */ evt = OS_EVT_EOF; } else { /* flush any buffered output */ G_console->flush_all(vmg_ VM_NL_INPUT); /* reset the [MORE] counter */ G_console->reset_line_count(FALSE); /* read an event from the OS layer */ evt = os_get_event(timeout, use_timeout, &info); } /* figure out how big a list we need to allocate */ switch(evt) { case OS_EVT_KEY: /* * we need two elements - one for the event type code, one for the * keystroke string */ ele_count = 2; break; case OS_EVT_COMMAND: /* we need a second element for the command ID */ ele_count = 2; break; case OS_EVT_HREF: /* * we need two elements - one for the event type code, one for the * HREF string */ ele_count = 2; break; default: /* for anything else, we need only the event type code element */ ele_count = 1; break; } /* create the return list */ lst_obj = CVmObjList::create(vmg_ FALSE, ele_count); lst = (CVmObjList *)vm_objp(vmg_ lst_obj); lst->cons_clear(); /* save the list on the stack to protect against garbage collection */ val.set_obj(lst_obj); G_stk->push(&val); /* fill in the first element with the event type code */ val.set_int(evt); lst->cons_set_element(0, &val); /* set additional elements, according to the event type */ switch(evt) { case OS_EVT_KEY: /* map the extended or ordinary key, as appropriate */ if (from_script) { /* we got a key from the script - it's in the 'href' field */ val.set_obj(str_from_ui_str(vmg_ info.href)); /* log the event */ G_console->log_event( vmg_ OS_EVT_KEY, info.href, strlen(info.href), FALSE); } else if (info.key[0] == 0) { /* it's an extended key */ map_ext_key(vmg_ keyname, info.key[1]); /* create a string for the key name */ val.set_obj(CVmObjString::create( vmg_ FALSE, keyname, strlen(keyname))); /* log the event */ G_console->log_event( vmg_ OS_EVT_KEY, keyname, strlen(keyname), TRUE); } else { char c[4]; size_t len; /* fetch more bytes until we have a complete character */ for (c[0] = (char)info.key[0], len = 1 ; !raw_key_complete(vmg_ c, len) && len < sizeof(c) ; ) { /* * Read another input event. The keyboard driver should * already have queued up all of the bytes needed to * complete this character sequence, so there should never * be a delay from os_get_event() here - it should simply * return immediately with another OS_EVT_KEY event with * the next byte of the sequence. */ evt = os_get_event(0, FALSE, &info); /* * if it's not a keystroke event, something's wrong - * ignore the event and stop trying to read the remaining * bytes of the character sequence */ if (evt != OS_EVT_KEY) break; /* store the next byte of the sequence */ c[len++] = (char)info.key[0]; } /* it's an ordinary key - map it */ map_raw_key(vmg_ keyname, c, len); /* create a string for the key name */ val.set_obj(CVmObjString::create( vmg_ FALSE, keyname, strlen(keyname))); /* log the event */ G_console->log_event( vmg_ OS_EVT_KEY, keyname, strlen(keyname), TRUE); } /* add it to the list */ lst->cons_set_element(1, &val); break; case OS_EVT_HREF: /* create the string for the href text */ val.set_obj(str_from_ui_str(vmg_ info.href)); /* add it to the list */ lst->cons_set_element(1, &val); /* log it */ G_console->log_event(vmg_ OS_EVT_HREF, info.href, strlen(info.href), FALSE); break; case OS_EVT_COMMAND: /* the second element is the command ID code */ val.set_int(info.cmd_id); lst->cons_set_element(1, &val); /* log it */ { char buf[20]; sprintf(buf, "%d", info.cmd_id); G_console->log_event(vmg_ OS_EVT_COMMAND, buf, strlen(buf), TRUE); } break; default: /* other event types have no extra data */ G_console->log_event(vmg_ evt); break; } /* return the list */ retval_obj(vmg_ lst_obj); /* we can drop the garbage collection protection now */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * Service routine: Map an "extended" keystroke from raw os_getc_raw() * notation to a UTF-8 key name. The caller should pass the second byte of * the extended two-byte raw sequence. */ int CVmBifTIO::map_ext_key(VMG_ char *namebuf, int extc) { /* * Portable key names for the extended keystrokes. We map the extended * key codes to these strings, so that the TADS code can access arrow * keys and the like. */ static const char *ext_key_names[] = { "[up]", /* CMD_UP - 1 */ "[down]", /* CMD_DOWN - 2 */ "[right]", /* CMD_RIGHT - 3 */ "[left]", /* CMD_LEFT - 4 */ "[end]", /* CMD_END - 5 */ "[home]", /* CMD_HOME - 6 */ "[del-eol]", /* CMD_DEOL - 7 */ "[del-line]", /* CMD_KILL - 8 */ "[del]", /* CMD_DEL - 9 */ "[scroll]", /* CMD_SCR - 10 */ "[page up]", /* CMD_PGUP - 11 */ "[page down]", /* CMD_PGDN - 12 */ "[top]", /* CMD_TOP - 13 */ "[bottom]", /* CMD_BOT - 14 */ "[f1]", /* CMD_F1 - 15 */ "[f2]", /* CMD_F2 - 16 */ "[f3]", /* CMD_F3 - 17 */ "[f4]", /* CMD_F4 - 18 */ "[f5]", /* CMD_F5 - 19 */ "[f6]", /* CMD_F6 - 20 */ "[f7]", /* CMD_F7 - 21 */ "[f8]", /* CMD_F8 - 22 */ "[f9]", /* CMD_F9 - 23 */ "[f10]", /* CMD_F10 - 24 */ "[?]", /* invalid key - CMD_CHOME - 25 */ "[tab]", /* CMD_TAB - 26 */ "[?]", /* invalid key - shift-F2 - 27 */ "[?]", /* not used (obsoleted) - 28 */ "[word-left]", /* CMD_WORD_LEFT - 29 */ "[word-right]", /* CMD_WORD_RIGHT - 30 */ "[del-word]", /* CMD_WORDKILL - 31 */ "[eof]", /* CMD_EOF - 32 */ "[break]", /* CMD_BREAK - 33 */ "[insert]" /* CMD_INS - 34 */ }; /* if it's in the key name array, use the array entry */ if (extc >= 1 && extc <= (int)sizeof(ext_key_names)/sizeof(ext_key_names[0])) { /* use the array name */ strcpy(namebuf, ext_key_names[extc - 1]); return TRUE; } /* if it's in the ALT key range, generate an ALT key name */ if ((unsigned char)extc >= CMD_ALT && (unsigned char)extc <= CMD_ALT + 25) { /* generate an ALT key name */ strcpy(namebuf, "[alt-?]"); namebuf[5] = (char)(extc - CMD_ALT + 'a'); return TRUE; } /* it's not a valid key - use '[?]' as the name */ strcpy(namebuf, "[?]"); return FALSE; } /* * Service routine: Map a keystroke from the raw notation, consisting of a * normal keystroke in the local character set or an extended command key * using a CMD_xxx code, to UTF-8. If the keystroke is a control character * or any CMD_xxx code, we'll map the key to a high-level keystroke name * enclosed in square brackets. */ int CVmBifTIO::map_raw_key(VMG_ char *namebuf, const char *c, size_t len) { size_t outlen; /* if it's a control character, give it a portable key name */ if (len == 1 && ((c[0] >= 1 && c[0] <= 27) || c[0] == 127)) { switch(c[0]) { case 10: case 13: /* * return an ASCII 10 (regardless of local newline conventions * - this is the internal string representation, which we * define to use ASCII 10 to represent a newline everywhere) */ namebuf[0] = 10; namebuf[1] = '\0'; return TRUE; case 9: /* return ASCII 9 for TAB characters */ namebuf[0] = 9; namebuf[1] = '\0'; return TRUE; case 8: case 127: /* return '[bksp]' for backspace/del characters */ strcpy(namebuf, "[bksp]"); return TRUE; case 27: /* return '[esc]' for the escape key */ strcpy(namebuf, "[esc]"); return TRUE; default: /* return '[ctrl-X]' for other control characters */ strcpy(namebuf, "[ctrl-?]"); namebuf[6] = (char)(c[0] + 'a' - 1); return TRUE; } } /* map the character to wide Unicode */ outlen = 32; G_cmap_from_ui->map(&namebuf, &outlen, c, len); /* null-terminate the result */ *namebuf = '\0'; /* successfully mapped */ return TRUE; } /* * Service routine: determine if a raw byte sequence forms a complete * character in the local character set. */ int CVmBifTIO::raw_key_complete(VMG_ const char *c, size_t len) { /* ask the local character mapper if it's a complete character */ return G_cmap_from_ui->is_complete_char(c, len); } /* ------------------------------------------------------------------------ */ /* * Standard system button labels for bifinpdlg() */ #define BIFINPDLG_LBL_OK 1 #define BIFINPDLG_LBL_CANCEL 2 #define BIFINPDLG_LBL_YES 3 #define BIFINPDLG_LBL_NO 4 /* * inputdialog - run a dialog */ void CVmBifTIO::inputdialog(VMG_ uint argc) { int icon_id; char prompt[256]; char label_buf[256]; vm_val_t label_val[10]; const char *labels[10]; int lst_cnt; int std_btns; int btn_cnt; char *dst; size_t dstrem; int default_resp; int cancel_resp; int resp; char numbuf[32]; /* check arguments */ check_argc(vmg_ argc, 5); /* get the icon number */ icon_id = pop_int_val(vmg0_); /* get the prompt string */ pop_str_val_ui(vmg_ prompt, sizeof(prompt)); /* there aren't any buttons yet */ btn_cnt = 0; /* check for the button type */ if (G_stk->get(0)->typ == VM_INT) { /* get the standard button set ID */ std_btns = pop_int_val(vmg0_); } else if (G_stk->get(0)->is_listlike(vmg0_) && (lst_cnt = G_stk->get(0)->ll_length(vmg0_)) >= 0) { int i; vm_val_t *valp; /* we're not using any standard button set */ std_btns = 0; /* * run through the list and get the button items into our array * (we do this rather than traversing the list directly so that * we don't have to worry about a constant list's data being * paged out) */ vm_val_t *lst = G_stk->get(0); /* limit the number of elements to our private array size */ if (lst_cnt > (int)sizeof(label_val)/sizeof(label_val[0])) lst_cnt = sizeof(label_val)/sizeof(label_val[0]); /* copy the list */ for (i = 1, valp = label_val ; i <= lst_cnt ; ++i, ++valp) lst->ll_index(vmg_ valp, i); /* done with the list - discard it */ G_stk->discard(); /* set up to write into our label buffer */ dst = label_buf; dstrem = sizeof(label_buf); /* now build our internal button list from the array elements */ for (i = 0, valp = label_val ; i < lst_cnt ; ++i, ++valp) { const char *p; /* * We could have a number or a string in each element. If * the element is a number, it refers to a standard label. * If it's a string, use the string directly. */ if ((p = valp->get_as_string(vmg0_)) != 0) { size_t copy_len; /* * it's a string - make a copy in the label buffer, * making sure to leave space for null termination */ copy_len = vmb_get_len(p); if (copy_len > dstrem - 1) copy_len = utf8_ptr::s_trunc(p + VMB_LEN, dstrem - 1); memcpy(dst, p + VMB_LEN, copy_len); /* null-terminate the buffer */ dst[copy_len++] = '\0'; /* set this button to point to the converted text */ labels[btn_cnt++] = dst; /* skip past this label */ dst += copy_len; dstrem -= copy_len; } else if (valp->typ == VM_INT) { int id; int resid; char rscbuf[128]; /* it's a standard system label ID - get the ID */ id = (int)valp->val.intval; /* translate it to the appropriate string resource */ switch(id) { case BIFINPDLG_LBL_OK: resid = VMRESID_BTN_OK; break; case BIFINPDLG_LBL_CANCEL: resid = VMRESID_BTN_CANCEL; break; case BIFINPDLG_LBL_YES: resid = VMRESID_BTN_YES; break; case BIFINPDLG_LBL_NO: resid = VMRESID_BTN_NO; break; default: resid = 0; break; } /* * if we got a valid resource ID, load the resource; * otherwise, skip this button */ if (resid != 0 && !os_get_str_rsc(resid, rscbuf, sizeof(rscbuf))) { /* set this button to point to the converted text */ labels[btn_cnt++] = dst; /* convert the resource text to UTF-8 */ G_cmap_from_ui->map(&dst, &dstrem, rscbuf, strlen(rscbuf)); /* null-terminate the converted text */ *dst++ = '\0'; --dstrem; } } } } else { /* invalid button type */ err_throw(VMERR_BAD_TYPE_BIF); } /* get the default response */ if (G_stk->get(0)->typ == VM_NIL) { /* discard the nil argument */ G_stk->discard(); /* there's no default response */ default_resp = 0; } else { /* get the default response index */ default_resp = pop_int_val(vmg0_); } /* get the cancel response */ if (G_stk->get(0)->typ == VM_NIL) { /* discard the nil argument */ G_stk->discard(); /* there's no cancel response */ cancel_resp = 0; } else { /* get the cancel response index */ cancel_resp = pop_int_val(vmg0_); } /* check for script input */ static int filter[] = { VMCON_EVT_DIALOG }; int evt; if (G_console->read_event_script( vmg_ &evt, numbuf, sizeof(numbuf), filter, sizeof(filter)/sizeof(filter[0]), 0)) { /* we got a script response - no need to show the dialog */ resp = atoi(numbuf); /* log the event */ G_console->log_event(vmg_ VMCON_EVT_DIALOG, numbuf, strlen(numbuf), FALSE); } else if (G_net_config != 0) { /* there's no console in web host mode - return eof */ resp = 0; } else { /* flush output before showing the dialog */ G_console->flush_all(vmg_ VM_NL_INPUT); /* show the dialog */ resp = G_console->input_dialog(vmg_ icon_id, prompt, std_btns, labels, btn_cnt, default_resp, cancel_resp); /* log the event */ sprintf(numbuf, "%d", resp); G_console->log_event(vmg_ VMCON_EVT_DIALOG, numbuf, strlen(numbuf), TRUE); } /* return the result */ retval_int(vmg_ resp); } /* ------------------------------------------------------------------------ */ /* * askfile - ask for a filename via a standard file dialog */ void CVmBifTIO::askfile(VMG_ uint argc) { char prompt[256]; int dialog_type; os_filetype_t file_type; int result; char fname[OSFNMAX*3 + 1]; vm_obj_id_t lst_obj; CVmObjList *lst; vm_val_t val; int from_script = FALSE; char warning[OSFNMAX + 255] = ""; int from_ui = FALSE; /* check arguments */ check_argc(vmg_ argc, 4); /* get the prompt string */ pop_str_val_ui(vmg_ prompt, sizeof(prompt)); /* get the dialog type and file type */ dialog_type = pop_int_val(vmg0_); file_type = (os_filetype_t)pop_int_val(vmg0_); /* * Pop and discard the flags. (This argument isn't used currently; * it's just there in case we need some option flags in the future. * Pop it as an integer to ensure the caller specified the correct * type, but discard the value.) */ (void)pop_long_val(vmg0_); /* check for a script response */ static int filter[] = { VMCON_EVT_FILE }; int evt; unsigned long attrs; if (G_console->read_event_script( vmg_ &evt, fname, sizeof(fname), filter, countof(filter), &attrs)) { int ok = TRUE; /* we got a response from the script */ from_script = TRUE; result = (fname[0] != '\0' ? OS_AFE_SUCCESS : OS_AFE_CANCEL); /* * If this is a "save" prompt, and the OVERWRITE flag isn't set, * and the file already exists, show an interactive warning that * we're about to ovewrite the file. */ if (ok && dialog_type == OS_AFP_SAVE && result == OS_AFE_SUCCESS && (attrs & VMCON_EVTATTR_OVERWRITE) == 0 && CVmNetFile::exists(vmg_ fname, 0)) { /* the file exists - warn about the overwrite */ ok = FALSE; t3sprintf(warning, sizeof(warning), "OV The script might overwrite the file %s. ", fname); } /* * If this is a "save" prompt, check to see if we can write the * file, and warn if not. */ if (ok && dialog_type == OS_AFP_SAVE && result == OS_AFE_SUCCESS && !CVmNetFile::can_write(vmg_ fname, 0)) { /* try creating the file */ osfildef *fp = osfopwb(fname, file_type); /* if that succeeded, undo the creation; otherwise warn */ if (fp != 0) { /* it worked - close and delete the test file */ osfcls(fp); osfdel(fname); } else { /* didn't work - warn about it */ ok = FALSE; t3sprintf(warning, sizeof(warning), "WR The script is attempting to write file %s, " "but that file cannot be written.", fname); } } /* * If this is an "open" prompt, and the file isn't readable, warn * about it. */ if (ok && dialog_type == OS_AFP_OPEN && result == OS_AFE_SUCCESS && !CVmNetFile::exists(vmg_ fname, 0)) { ok = FALSE; t3sprintf(warning, sizeof(warning), "RD The script is attempting to open file %s, " "but this file doesn't exist or isn't readable.", fname); } /* * If we generated a warning, and we're not in Web UI mode, display * the warning as a Yes/No/Cancel console input dialog. We can't * do this in Web UI mode, since we can't use the regular console * in Web mode. Instead, we'll return the warning information to * the caller for display. */ if (!ok && G_net_config == 0) { char fullprompt[OSFNMAX + 255 + 150]; /* build the full prompt */ t3sprintf(fullprompt, sizeof(fullprompt), "%s Do you wish to proceed? Select Yes to " "proceed with this file, No to choose a different " "file, or Cancel to stop replaying this script.", warning + 3); /* * display a dialog - note that this goes directly to user, * bypassing the active script, since this is a question about * how to handle a problem in the script */ show_warning: switch (G_console->input_dialog( vmg_ OS_INDLG_ICON_WARNING, fullprompt, OS_INDLG_YESNOCANCEL, 0, 0, 2, 3, TRUE)) { case 1: /* yes - proceed with fname */ break; case 2: /* no - ask for a new file */ result = G_console->askfile( vmg_ prompt, strlen(prompt), fname, sizeof(fname), dialog_type, file_type, TRUE); /* this didn't come from the script after all */ from_script = FALSE; /* * If they canceled the file selection, go back to the * warning dialog; if the dialog itself failed, cancel the * script entirely. If they selected a file, proceed with * their new file replacing the script input. Note that we * don't have to repeat the tests above on the new file, * since the user explicitly entered it and thus presumably * knows that it's the one they really want to use. */ if (result == OS_AFE_CANCEL) goto show_warning; else if (result == OS_AFE_FAILURE) close_script_file(vmg0_); /* handled */ break; case 3: /* cancel - stop the script playback */ close_script_file(vmg0_); /* indicate cancellation */ result = OS_AFE_CANCEL; break; } /* we've displayed the warning, so don't return it */ warning[0] = '\0'; } } else if (G_net_config != 0) { #ifdef TADSNET /* * If we're running in the local stand-alone configuration, we have * a bit of a special case: we're accessing files on the local file * system, but we're presenting the rest of the program's UI * through a browser window, which might be in a separate child * process; we have to present the file dialog UI in the same * manner. We have a special OS function for this situation. * * This only applies in the stand-alone configuration, where we * have no "hostname" parameter in the net config. If we have a * hostname, it means that we're operating in full client/server * mode, in which case we go through the Web UI directly. */ if (G_net_config->get("hostname") == 0) { result = osnet_askfile(prompt, fname, sizeof(fname), dialog_type, file_type); } else #endif { /* there's no console in web host mode - return eof */ result = OS_AFE_FAILURE; } } else { /* ask for a file via the console UI */ result = G_console->askfile( vmg_ prompt, strlen(prompt), fname, sizeof(fname), dialog_type, file_type); /* this file came from the console UI */ from_ui = TRUE; } /* * Create the return list. In all cases, we need one element for the * status code. On success, we need three addition elements (the * filename string, the descriptive text [future expansion], and the * warning message text). */ lst_obj = CVmObjList::create(vmg_ FALSE, result == OS_AFE_SUCCESS ? 4 : 1); lst = (CVmObjList *)vm_objp(vmg_ lst_obj); lst->cons_clear(); /* save it on the stack as protection against garbage collection */ val.set_obj(lst_obj); G_stk->push(&val); /* set the first element to the result code */ val.set_int(result); lst->cons_set_element(0, &val); /* add the extra elements for the success case */ if (result == OS_AFE_SUCCESS) { char *fnamep = fname, *fname2 = 0; err_try { /* if the name came from a script, map from the local char set */ if (from_script) { G_cmap_from_ui->map_str_alo(&fname2, fname); fnamep = fname2; } /* create a FileName object to represent the filename */ val.set_obj(CVmObjFileName::create_from_local( vmg_ fnamep, strlen(fnamep))); /* * if the name came from the console UI, flag the FileName as * being user-selected, which allows it to override the file * safety settings; since the user manually selected the name, * they implicitly granted permission to use the file for the * type of operation proposed by the dialog */ if (from_ui) { vm_objid_cast(CVmObjFileName, val.val.obj)->set_from_ui( dialog_type); } } err_finally { lib_free_str(fname2); } err_end; /* store the string as the second list element */ lst->cons_set_element(1, &val); /* * Add nil for the descriptive text, since we don't currently * implement this feature. (This is a future enhancement that some * system file selector dialogs might provide, allowing the user to * enter descriptive text in addition to the filename. The Web UI * storage server already uses this to save user descriptive text * with saved game files.) */ val.set_nil(); lst->cons_set_element(2, &val); /* * Add the script warning message, or nil if there's no warning. * The warning message contains the filename text from the script, * so translate it to the local character set. */ if (warning[0] != '\0') val.set_obj(str_from_ui_str(vmg_ warning)); else val.set_nil(); lst->cons_set_element(3, &val); } /* log the event */ if (result == OS_AFE_SUCCESS) G_console->log_event(vmg_ VMCON_EVT_FILE, fname, strlen(fname), FALSE); else G_console->log_event(vmg_ VMCON_EVT_FILE); /* return the list */ retval_obj(vmg_ lst_obj); /* we no longer need the garbage collector protection */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * timeDelay - pause for a specified interval */ void CVmBifTIO::timedelay(VMG_ uint argc) { long delay_ms; /* check arguments */ check_argc(vmg_ argc, 1); /* get the delay time in milliseconds */ delay_ms = pop_long_val(vmg0_); /* flush any pending output */ G_console->flush_all(vmg_ VM_NL_NONE); /* ask the system code to pause */ os_sleep_ms(delay_ms); } /* ------------------------------------------------------------------------ */ /* * systemInfo */ void CVmBifTIO::sysinfo(VMG_ uint argc) { int info; long result; /* make sure we have at least one argument */ if (argc < 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* get the information type code */ info = pop_int_val(vmg0_); /* see what we have */ switch(info) { case SYSINFO_SYSINFO: /* there are no additional arguments for this information type */ check_argc(vmg_ argc, 1); /* system information is supported in this version - return true */ retval_true(vmg0_); break; case SYSINFO_VERSION: /* there are no additional arguments for this information type */ check_argc(vmg_ argc, 1); /* return the VM version string, formatted as a string */ { char buf[30]; sprintf(buf, "%d.%d.%d", (int)((T3VM_VSN_NUMBER >> 16) & 0xffff), (int)((T3VM_VSN_NUMBER >> 8) & 0xff), (int)(T3VM_VSN_NUMBER & 0xff)); retval_str(vmg_ buf); } break; case SYSINFO_OS_NAME: /* there are no additional arguments for this information type */ check_argc(vmg_ argc, 1); /* return the OS name as a string */ retval_str(vmg_ OS_SYSTEM_NAME); break; case SYSINFO_HTML: case SYSINFO_JPEG: case SYSINFO_PNG: case SYSINFO_WAV: case SYSINFO_MIDI: case SYSINFO_WAV_MIDI_OVL: case SYSINFO_WAV_OVL: case SYSINFO_PREF_IMAGES: case SYSINFO_PREF_SOUNDS: case SYSINFO_PREF_MUSIC: case SYSINFO_PREF_LINKS: case SYSINFO_MPEG: case SYSINFO_MPEG1: case SYSINFO_MPEG2: case SYSINFO_MPEG3: case SYSINFO_LINKS_HTTP: case SYSINFO_LINKS_FTP: case SYSINFO_LINKS_NEWS: case SYSINFO_LINKS_MAILTO: case SYSINFO_LINKS_TELNET: case SYSINFO_PNG_TRANS: case SYSINFO_PNG_ALPHA: case SYSINFO_OGG: case SYSINFO_MNG: case SYSINFO_MNG_TRANS: case SYSINFO_MNG_ALPHA: case SYSINFO_TEXT_COLORS: case SYSINFO_TEXT_HILITE: case SYSINFO_BANNERS: case SYSINFO_INTERP_CLASS: /* there are no additional arguments for these information types */ check_argc(vmg_ argc, 1); /* ask the OS layer for the information */ if (os_get_sysinfo(info, 0, &result)) { /* we got a valid result - return it */ retval_int(vmg_ result); } else { /* * the information type is not known to the OS layer - * return nil to indicate that the information isn't * available */ retval_nil(vmg0_); } break; case SYSINFO_HTML_MODE: /* * This sysinfo flag is explicitly not used in TADS 3, since we're * always in HTML mode. (We make this case explicit to call * attention to the fact that it was not accidentally omitted, but * is intentionally not used.) */ /* fall through to default case */ default: /* * Other codes fail harmlessly with a nil return value. Pop all * remaining arguments and return nil. (Note that we discard * only (argc-1) arguments because we've already popped the * first argument.) */ G_stk->discard(argc - 1); retval_nil(vmg0_); break; } } /* ------------------------------------------------------------------------ */ /* * status_mode - set the status line mode */ void CVmBifTIO::status_mode(VMG_ uint argc) { int mode; /* check arguments */ check_argc(vmg_ argc, 1); /* pop the mode value */ mode = pop_int_val(vmg0_); /* set the new status mode in the console */ G_console->set_statusline_mode(vmg_ mode); } /* ------------------------------------------------------------------------ */ /* * status_right - set the string in the right half of the status line */ void CVmBifTIO::status_right(VMG_ uint argc) { char msg[256]; /* check arguments */ check_argc(vmg_ argc, 1); /* pop the status string */ pop_str_val_ui(vmg_ msg, sizeof(msg)); /* set the string */ os_strsc(msg); } /* ------------------------------------------------------------------------ */ /* * res_exists - check to see if an external resource can be loaded * through the host application */ void CVmBifTIO::res_exists(VMG_ uint argc) { const char *res_name; int result; /* check arguments */ check_argc(vmg_ argc, 1); /* pop the resource name */ res_name = pop_str_val(vmg0_); /* ask the host application if the resource can be loaded */ result = G_host_ifc->resfile_exists(res_name + VMB_LEN, osrp2(res_name)); /* return the result */ retval_bool(vmg_ result); } /* ------------------------------------------------------------------------ */ /* * set_script_file flags */ /* read in 'quiet' mode - do not dipslay output while reading the script */ #define VMBIFTADS_SCRIPT_QUIET 0x0001 /* turn off 'more' mode while reading the script */ #define VMBIFTADS_SCRIPT_NONSTOP 0x0002 /* the script is an "event" script - this is a query-only flag */ #define VMBIFTADS_SCRIPT_EVENT 0x0004 /* * set_script_file special request codes */ #define VMBIFTADS_SCRIPTREQ_GET_STATUS 0x7000 /* * set_script_file - open a command input scripting file */ void CVmBifTIO::set_script_file(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 1, 2); /* * If the filename is nil, close the current script file. If the * "filename" is a number, it's a special request. Otherwise, the * filename must be a string giving the name of the file to open. */ if (G_stk->get(0)->typ == VM_NIL) { /* discard the nil filename */ G_stk->discard(); /* pop the flags if present - they're superfluous in this case */ if (argc >= 2) G_stk->discard(); /* close the script file */ close_script_file(vmg0_); /* this form always succeeds */ retval_true(vmg0_); } else if (G_stk->get(0)->typ == VM_INT) { /* get the request code */ int flags = pop_int_val(vmg0_); /* any additional argument is superfluous in this case */ if (argc >= 2) G_stk->discard(); /* check the request */ switch (flags) { case VMBIFTADS_SCRIPTREQ_GET_STATUS: /* get the current script reading status */ if (G_console->is_reading_script()) { /* a script is in progress - return the script flags */ retval_int(vmg_ (G_console->is_quiet_script() ? VMBIFTADS_SCRIPT_QUIET : 0) | (G_console->is_moremode_script() ? 0 : VMBIFTADS_SCRIPT_NONSTOP) | (G_console->is_event_script() ? VMBIFTADS_SCRIPT_EVENT : 0)); } else { /* not reading a script - return nil */ retval_nil(vmg0_); } break; default: /* other flags are invalid */ err_throw(VMERR_BAD_VAL_BIF); } } else { /* get the filename argument */ const vm_val_t *filespec = G_stk->get(0); /* if they provided flags, get the flags */ int flags = (argc >= 2 ? G_stk->get(1)->num_to_int(vmg0_) : 0); /* set up our recursive call descriptor */ vm_rcdesc rc(vmg_ "setScriptFile", bif_table, 15, G_stk->get(0), argc); /* open the script file */ int ok = !G_console->open_script_file( vmg_ filespec, &rc, (flags & VMBIFTADS_SCRIPT_QUIET) != 0, !(flags & VMBIFTADS_SCRIPT_NONSTOP)); /* discard arguments */ G_stk->discard(argc); /* return true on success, nil on failure */ retval_bool(vmg_ ok); } } /* * close the script file */ void CVmBifTIO::close_script_file(VMG0_) { int old_more_mode; /* close the script */ old_more_mode = G_console->close_script_file(vmg0_); /* restore the MORE mode in effect when the script was opened */ G_console->set_more_state(old_more_mode); } /* ------------------------------------------------------------------------ */ /* * get_charset selectors */ /* display character set */ #define VMBIFTADS_CHARSET_DISPLAY 0x0001 /* file system character set for filenames */ #define VMBIFTADS_CHARSET_FILENAME 0x0002 /* typical character set for text file contents */ #define VMBIFTADS_CHARSET_FILECONTENTS 0x0003 /* * get_charset - get a local character set name */ void CVmBifTIO::get_charset(VMG_ uint argc) { char csname[32]; int which; /* check arguments */ check_argc(vmg_ argc, 1); /* get the character set selector */ which = pop_int_val(vmg0_); /* map the selector to the appropriate value */ switch(which) { case VMBIFTADS_CHARSET_DISPLAY: /* * if there was an explicit character set parameter specified at * start-up time, use that */ if (G_disp_cset_name != 0) { /* there's an explicit parameter - return it */ retval_str(vmg_ G_disp_cset_name); /* we're done */ return; } /* no explicit setting - use the OS default character set */ which = OS_CHARMAP_DISPLAY; break; case VMBIFTADS_CHARSET_FILENAME: which = OS_CHARMAP_FILENAME; break; case VMBIFTADS_CHARSET_FILECONTENTS: which = OS_CHARMAP_FILECONTENTS; break; default: /* others are unrecognized; simply return nil for these */ retval_nil(vmg0_); return; } /* get the character set */ os_get_charmap(csname, which); /* return a string with the name */ retval_str(vmg_ csname); } /* ------------------------------------------------------------------------ */ /* * flush_output - flush the display output */ void CVmBifTIO::flush_output(VMG_ uint argc) { /* we take no arguments */ check_argc(vmg_ argc, 0); /* flush output */ G_console->flush(vmg_ VM_NL_NONE); /* immediately update the display */ G_console->update_display(vmg0_); } /* ------------------------------------------------------------------------ */ /* * input_timeout - get a line of input from the keyboard, with an optional * timeout */ void CVmBifTIO::input_timeout(VMG_ uint argc) { char buf[256]; long timeout; int use_timeout; int evt; int ele_count; vm_obj_id_t lst_obj; CVmObjList *lst; vm_val_t val; /* check arguments */ check_argc_range(vmg_ argc, 0, 1); /* if there's a timeout argument, retrieve it */ if (argc == 0) { /* no arguments - there's no timeout */ use_timeout = FALSE; timeout = 0; } else if (G_stk->get(0)->typ == VM_NIL) { /* * there's a timeout argument, but it's nil, so this means there's * no timeout */ use_timeout = FALSE; timeout = 0; /* discard the argument */ G_stk->discard(); } else { /* we have a timeout specified */ use_timeout = TRUE; timeout = pop_long_val(vmg0_); } /* read a line of text from the keyboard */ evt = G_console->read_line_timeout(vmg_ buf, sizeof(buf), timeout, use_timeout); /* figure out how big a list we'll return */ switch(evt) { case OS_EVT_LINE: case OS_EVT_TIMEOUT: /* two elements - the event type, and the line of text */ ele_count = 2; break; default: /* for anything else, we need only the event type code */ ele_count = 1; break; } /* create the return list */ lst_obj = CVmObjList::create(vmg_ FALSE, ele_count); lst = (CVmObjList *)vm_objp(vmg_ lst_obj); lst->cons_clear(); /* save the list on the stack to protect against garbage collection */ val.set_obj(lst_obj); G_stk->push(&val); /* fill in the first element with the event type code */ val.set_int(evt); lst->cons_set_element(0, &val); /* set additional elements, according to the event type */ switch(evt) { case OS_EVT_LINE: case OS_EVT_TIMEOUT: /* the second element is the line of text we read */ val.set_obj(CVmObjString::create(vmg_ FALSE, buf, strlen(buf))); lst->cons_set_element(1, &val); break; default: /* other event types have no extra data */ break; } /* return the list */ retval_obj(vmg_ lst_obj); /* we can drop the garbage collection protection now */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * input_cancel - cancel input previously interrupted by timeout */ void CVmBifTIO::input_cancel(VMG_ uint argc) { vm_val_t val; int reset; /* check arguments */ check_argc(vmg_ argc, 1); /* get the 'reset' flag */ G_stk->pop(&val); reset = (val.typ == VM_TRUE); /* cancel the line */ G_console->read_line_cancel(vmg_ reset); } /* ------------------------------------------------------------------------ */ /* * Banner Window Functions */ /* * create a banner */ void CVmBifTIO::banner_create(VMG_ uint argc) { int parent_id; int other_id; int where; int wintype; int align; int siz; int siz_units; unsigned long style; int hdl; /* check arguments */ check_argc_range(vmg_ argc, 7, 8); /* retrieve the 'parent' parameter */ if (argc == 7) { /* * there's no parent argument - this is an obsolete format for the * arguments, but accept it for now, and simply treat it as * equivalent to a nil parent */ parent_id = 0; } else if (G_stk->get(0)->typ == VM_NIL) { /* parent is nil - use zero as the ID and discard the nil */ parent_id = 0; G_stk->discard(); } else { /* retrieve the parent ID */ parent_id = pop_int_val(vmg0_); } /* retrieve the 'where' parameter */ where = pop_int_val(vmg0_); /* retrieve the 'other' parameter, if it's needed for the 'where' */ switch(where) { case OS_BANNER_BEFORE: case OS_BANNER_AFTER: /* we need another banner ID for the relative insertion point */ other_id = pop_int_val(vmg0_); break; default: /* we don't need 'other' for this insertion point */ other_id = 0; G_stk->discard(); break; } /* retrieve the window type argument */ wintype = pop_int_val(vmg0_); /* retrieve the alignment argument */ align = pop_int_val(vmg0_); /* retrieve the size (as a percentage of the full screen size) */ if (G_stk->get(0)->typ == VM_NIL) { /* nil size - use zero for the size */ siz = 0; siz_units = OS_BANNER_SIZE_ABS; /* discard the size and size units */ G_stk->discard(); G_stk->discard(); } else { /* retrieve the size and size units as integer values */ siz = pop_int_val(vmg0_); siz_units = pop_int_val(vmg0_); } /* retrieve the flags */ style = pop_long_val(vmg0_); if (G_net_config != 0) { /* there's no console in web host mode - return failure */ retval_nil(vmg0_); return; } /* try creating the banner */ hdl = G_console->get_banner_manager()->create_banner( vmg_ parent_id, where, other_id, wintype, align, siz, siz_units, style); /* * If we succeeded, return the handle; otherwise, return nil. A banner * handle of zero indicates failure. */ if (hdl != 0) retval_int(vmg_ hdl); else retval_nil(vmg0_); } /* * delete a banner */ void CVmBifTIO::banner_delete(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 1); /* delete the banner */ G_console->get_banner_manager()->delete_banner(vmg_ pop_int_val(vmg0_)); } /* * clear a window */ void CVmBifTIO::banner_clear(VMG_ uint argc) { int id; CVmConsole *console; /* check arguments */ check_argc(vmg_ argc, 1); /* get the banner ID */ id = pop_int_val(vmg0_); /* get the banner - if it's invalid, throw an error */ console = G_console->get_banner_manager()->get_console(id); if (console == 0) err_throw(VMERR_BAD_VAL_BIF); /* tell the console that we're clearing it */ console->clear_window(vmg0_); } /* * write values to a banner */ void CVmBifTIO::banner_say(VMG_ uint argc) { CVmConsole *console; /* check arguments */ check_argc_range(vmg_ argc, 1, 32767); /* get the banner - if it's invalid, throw an error */ console = G_console->get_banner_manager()->get_console(pop_int_val(vmg0_)); if (console == 0) err_throw(VMERR_BAD_VAL_BIF); /* * write the argument(s) to the console (note that the first argument, * which we've already retrieved, is the console handle, so don't count * it among the arguments to display) */ say_to_console(vmg_ console, argc - 1); } /* * flush text to a banner */ void CVmBifTIO::banner_flush(VMG_ uint argc) { CVmConsole *console; /* check arguments */ check_argc(vmg_ argc, 1); /* get the banner - if it's invalid, throw an error */ console = G_console->get_banner_manager()->get_console(pop_int_val(vmg0_)); if (console == 0) err_throw(VMERR_BAD_VAL_BIF); /* flush the console */ console->flush(vmg_ VM_NL_NONE); /* immediately update the display */ console->update_display(vmg0_); } /* * set the banner size */ void CVmBifTIO::banner_set_size(VMG_ uint argc) { int id; void *hdl; int siz; int siz_units; int is_advisory; /* check arguments */ check_argc(vmg_ argc, 4); /* get the banner ID */ id = pop_int_val(vmg0_); /* get the size and size units */ siz = pop_int_val(vmg0_); siz_units = pop_int_val(vmg0_); /* get the is-advisory flag */ is_advisory = pop_bool_val(vmg0_); /* get the banner - if it's invalid, throw an error */ hdl = G_console->get_banner_manager()->get_os_handle(id); if (hdl == 0) err_throw(VMERR_BAD_VAL_BIF); /* set the size */ os_banner_set_size(hdl, siz, siz_units, is_advisory); } /* * size a banner to its contents in one or both dimensions */ void CVmBifTIO::banner_size_to_contents(VMG_ uint argc) { int id; void *hdl; CVmConsole *console; /* check arguments */ check_argc(vmg_ argc, 1); /* get the banner ID */ id = pop_int_val(vmg0_); /* get the banner - if it's invalid, throw an error */ hdl = G_console->get_banner_manager()->get_os_handle(id); console = G_console->get_banner_manager()->get_console(id); if (hdl == 0 || console == 0) err_throw(VMERR_BAD_VAL_BIF); /* make sure we've flushed any pending output to the banner */ console->flush(vmg_ VM_NL_NONE); /* set the size */ os_banner_size_to_contents(hdl); } /* * move the output position in a text grid banner */ void CVmBifTIO::banner_goto(VMG_ uint argc) { int id; void *hdl; CVmConsole *console; int row, col; /* check arguments */ check_argc(vmg_ argc, 3); /* get the banner ID */ id = pop_int_val(vmg0_); /* get the coordinates */ row = pop_int_val(vmg0_); col = pop_int_val(vmg0_); /* make sure the values are valid */ if (row < 1 || col < 1) err_throw(VMERR_BAD_VAL_BIF); /* get the banner - if it's invalid, throw an error */ hdl = G_console->get_banner_manager()->get_os_handle(id); console = G_console->get_banner_manager()->get_console(id); if (hdl == 0 || console == 0) err_throw(VMERR_BAD_VAL_BIF); /* * make sure we've flushed and discarded any pending output (since we * don't want any pending output to show up at the new cursor position) */ console->flush(vmg_ VM_NL_NONE); console->empty_buffers(vmg0_); /* move the cursor, adjusting from 1-based to 0-based coordinates */ os_banner_goto(hdl, row - 1, col - 1); } /* * set the text color in a banner */ void CVmBifTIO::banner_set_text_color(VMG_ uint argc) { int id; void *hdl; CVmConsole *console; os_color_t fg, bg; /* check arguments */ check_argc(vmg_ argc, 3); /* get the banner ID */ id = pop_int_val(vmg0_); /* get the foreground and background color values */ fg = (os_color_t)pop_long_val(vmg0_); bg = (os_color_t)pop_long_val(vmg0_); /* get the banner - if it's invalid, throw an error */ hdl = G_console->get_banner_manager()->get_os_handle(id); console = G_console->get_banner_manager()->get_console(id); if (hdl == 0 || console == 0) err_throw(VMERR_BAD_VAL_BIF); /* set the text output color in the console's formatter */ console->set_text_color(vmg_ fg, bg); } /* * set the screen color in a banner */ void CVmBifTIO::banner_set_screen_color(VMG_ uint argc) { int id; void *hdl; CVmConsole *console; os_color_t color; /* check arguments */ check_argc(vmg_ argc, 2); /* get the banner ID */ id = pop_int_val(vmg0_); /* get the body color */ color = (os_color_t)pop_long_val(vmg0_); /* get the banner - if it's invalid, throw an error */ hdl = G_console->get_banner_manager()->get_os_handle(id); console = G_console->get_banner_manager()->get_console(id); if (hdl == 0 || console == 0) err_throw(VMERR_BAD_VAL_BIF); /* set the body color in the console */ console->set_body_color(vmg_ color); } /* service routine: store an integer in a list under construction */ static void set_list_int(CVmObjList *lst, size_t idx, long intval) { vm_val_t val; /* set the value */ val.set_int(intval); /* store it in the list */ lst->cons_set_element(idx, &val); } /* * get information on a banner */ void CVmBifTIO::banner_get_info(VMG_ uint argc) { int id; void *hdl; os_banner_info_t info; CVmConsoleBanner *console; /* check arguments */ check_argc(vmg_ argc, 1); /* get the banner ID */ id = pop_int_val(vmg0_); /* get the banner - if it's invalid, throw an error */ hdl = G_console->get_banner_manager()->get_os_handle(id); console = G_console->get_banner_manager()->get_console(id); if (hdl == 0 || console == 0) err_throw(VMERR_BAD_VAL_BIF); /* get information on the banner */ if (console->get_banner_info(&info)) { vm_obj_id_t lst_obj; CVmObjList *lst; vm_val_t val; /* set up a return list with space for six entries */ lst_obj = CVmObjList::create(vmg_ FALSE, 6); lst = (CVmObjList *)vm_objp(vmg_ lst_obj); /* save the list on the stack to protect against garbage collection */ val.set_obj(lst_obj); G_stk->push(&val); /* * return the values: [align, style, rows, columns, pix_height, * pix_width] */ set_list_int(lst, 0, info.align); set_list_int(lst, 1, info.style); set_list_int(lst, 2, info.rows); set_list_int(lst, 3, info.columns); set_list_int(lst, 4, info.pix_height); set_list_int(lst, 5, info.pix_width); /* return the list */ retval_obj(vmg_ lst_obj); /* discard our gc protection */ G_stk->discard(); } else { /* no information available - return nil */ retval_nil(vmg0_); } } /* ------------------------------------------------------------------------ */ /* * Log Console Functions */ /* * create a log console */ void CVmBifTIO::log_console_create(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 3); /* get the arguments: log file name, character set, and width */ const vm_val_t *filespec = G_stk->get(0); const vm_val_t *charset = G_stk->get(1); int width = G_stk->get(2)->num_to_int(vmg0_); /* * Retrieve the character mapper, which can be given as either a * CharacterSet object or a string giving the character set name. */ CCharmapToLocal *cmap = 0; if (charset->typ == VM_NIL) { /* nil - use the default log file character set */ cmap = G_cmap_to_log; cmap->add_ref(); } else if (charset->typ == VM_OBJ && CVmObjCharSet::is_charset(vmg_ charset->val.obj)) { /* it's a CharacterSet object - pop the reference */ vm_obj_id_t obj = charset->val.obj; /* retrieve the character mapper from the character set */ cmap = ((CVmObjCharSet *)vm_objp(vmg_ obj))->get_to_local(vmg0_); cmap->add_ref(); } else { /* it's not a CharacterSet, so it must be a character set name */ const char *str = charset->get_as_string(vmg0_); if (str == 0) err_throw(VMERR_BAD_TYPE_BIF); /* get the length and skip the length prefix */ size_t len = vmb_get_len(str); str += VMB_LEN; /* get a null-terminated version of the name */ char *nm = lib_copy_str(str, len); /* * Create a character mapping for the given name. Note that this * will automatically add a reference to the mapper on our behalf, * so we don't have to add our own extra reference. */ cmap = CCharmapToLocal::load(G_host_ifc->get_sys_res_loader(), nm); /* done with the null-terminated version of the name string */ lib_free_str(nm); } /* if we didn't get a character map, use us-ascii by default */ if (cmap == 0) cmap = CCharmapToLocal::load(G_host_ifc->get_sys_res_loader(), "us-ascii"); /* open the file and start logging */ osfildef *fp = 0; CVmNetFile *nf = 0; int hdl = 0; err_try { /* create the network file descriptor */ vm_rcdesc rc(vmg_ "logConsoleCreate", bif_table, 30, G_stk->get(0), argc); nf = CVmNetFile::open(vmg_ filespec, &rc, NETF_NEW, OSFTLOG, "text/plain"); /* make sure the file safety level allows the operation */ CVmObjFile::check_safety_for_open(vmg_ nf, VMOBJFILE_ACCESS_WRITE); /* open the file for writing (in text mode) */ fp = osfopwt(nf->lclfname, OSFTLOG); /* if that failed, we can't contine */ if (fp == 0) G_interpreter->throw_new_class(vmg_ G_predef->file_creation_exc, 0, "error creating log file"); /* create the log console */ hdl = G_console->get_log_console_manager()->create_log_console( vmg_ nf, fp, cmap, width); /* we've handed off the file information to the console */ nf = 0; fp = 0; } err_finally { /* * release our character map reference - if we succeeded in * creating the log console, it will have added its own reference * by now */ cmap->release_ref(); /* close the file if we opened it and didn't hand it off */ if (fp != 0) osfcls(fp); /* if we have a network file object, abandon it */ if (nf != 0) nf->abandon(vmg0_); } err_end; /* discard arguments */ G_stk->discard(argc); /* * If we succeeded, return the handle; otherwise, return nil. A handle * of zero indicates failure. */ if (hdl != 0) retval_int(vmg_ hdl); else retval_nil(vmg0_); } /* * close (delete) a log console */ void CVmBifTIO::log_console_close(VMG_ uint argc) { int handle; CVmConsole *console; /* check arguments */ check_argc(vmg_ argc, 1); /* get the handle */ handle = pop_int_val(vmg0_); /* get the console based on the handle */ console = G_console->get_log_console_manager()->get_console(handle); if (console == 0) err_throw(VMERR_BAD_VAL_BIF); /* flush the console */ console->flush(vmg_ VM_NL_NONE); /* delete the console */ G_console->get_log_console_manager()->delete_log_console(vmg_ handle); /* no return value */ retval_nil(vmg0_); } /* * write values to a log console */ void CVmBifTIO::log_console_say(VMG_ uint argc) { int hdl; CVmConsole *console; /* check arguments */ check_argc_range(vmg_ argc, 1, 32767); /* get the console handle */ hdl = pop_int_val(vmg0_); /* * if it's the special value -1, it means that we want to write to the * main console's log file; otherwise, it's a log console that we * previously created explicitly via log_console_create() */ if (hdl == -1) { /* use the main log */ console = new CVmConsoleMainLog(); } else { /* get the console by handle - if it's invalid, throw an error */ console = G_console->get_log_console_manager()->get_console(hdl); if (console == 0) err_throw(VMERR_BAD_VAL_BIF); } /* * write the argument(s) to the console (note that the first argument, * which we've already retrieved, is the console handle, so don't count * it among the arguments to display) */ err_try { say_to_console(vmg_ console, argc - 1); } err_finally { /* if we created a "main console" object, we're done with it */ if (hdl == -1) console->delete_obj(vmg0_); } err_end; } /* ------------------------------------------------------------------------ */ /* * Log an input event obtained from an external source, such as the Web UI */ void CVmBifTIO::log_input_event(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 1); /* retrieve the list */ const vm_val_t *lst = G_stk->get(0); if (!lst->is_listlike(vmg0_)) err_throw(VMERR_BAD_TYPE_BIF); /* make sure we have at least one element */ int len = lst->ll_length(vmg0_); if (len < 1) err_throw(VMERR_BAD_VAL_BIF); /* retrieve the event type code or tag name from the first element */ vm_val_t ele1; lst->ll_index(vmg_ &ele1, 1); /* if there's a second element, retrieve the parameter string */ const char *param = 0; size_t paramlen = 0; if (len >= 2) { /* there's a parameter - retrieve it */ vm_val_t ele2; lst->ll_index(vmg_ &ele2, 2); /* * Get its string value. If it's a filename object, use the * filename path string. */ CVmObjFileName *fname_param = vm_val_cast(CVmObjFileName, &ele2); if (fname_param != 0) param = fname_param->get_path_string(); else param = ele2.get_as_string(vmg0_); /* if we didn't get a parameter string value, it's an error */ if (param == 0) err_throw(VMERR_BAD_VAL_BIF); /* get the parameter length and buffer pointer */ paramlen = vmb_get_len(param); param += VMB_LEN; } /* log the event, according to the type of the event code */ if (ele1.is_numeric(vmg0_)) { /* it's an integer event type code - log it */ G_console->log_event( vmg_ ele1.num_to_int(vmg0_), param, paramlen, TRUE); } else if (ele1.get_as_string(vmg0_) != 0) { /* it's a string event tag - retrieve it in the UI character set */ char tag[128]; G_stk->push(&ele1); pop_str_val_ui(vmg_ tag, sizeof(tag)); /* log the event with the given tag */ G_console->log_event(vmg_ tag, param, paramlen, TRUE); } else { /* other types are invalid */ err_throw(VMERR_BAD_VAL_BIF); } /* discard arguments */ G_stk->discard(1); /* no return value */ retval_nil(vmg0_); } qtads-2.1.7/tads3/vmbiftio.h000066400000000000000000000206751265017072300156600ustar00rootroot00000000000000/* $Header: d:/cvsroot/tads/tads3/vmbiftad.h,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbiftad.h - function set definition - TADS I/O function set Function Notes Modified 12/06/98 MJRoberts - Creation */ #ifndef VMBIFTIO_H #define VMBIFTIO_H #include "os.h" #include "vmbif.h" #include "utf8.h" /* ------------------------------------------------------------------------ */ /* * TADS I/O function set built-in functions */ class CVmBifTIO: public CVmBif { public: /* function vector */ static vm_bif_desc bif_table[]; /* * Input/output functions */ static void say(VMG_ uint argc); static void logging(VMG_ uint argc); static void clearscreen(VMG_ uint argc); static void more(VMG_ uint argc); static void input(VMG_ uint argc); static void inputkey(VMG_ uint argc); static void inputevent(VMG_ uint argc); static void inputdialog(VMG_ uint argc); static void askfile(VMG_ uint argc); static void timedelay(VMG_ uint argc); static void sysinfo(VMG_ uint argc); static void status_mode(VMG_ uint argc); static void status_right(VMG_ uint argc); static void res_exists(VMG_ uint argc); static void set_script_file(VMG_ uint argc); static void get_charset(VMG_ uint argc); static void flush_output(VMG_ uint argc); static void input_timeout(VMG_ uint argc); static void input_cancel(VMG_ uint argc); static void log_input_event(VMG_ uint argc); /* * banner window functions */ static void banner_create(VMG_ uint argc); static void banner_delete(VMG_ uint argc); static void banner_say(VMG_ uint argc); static void banner_clear(VMG_ uint argc); static void banner_flush(VMG_ uint argc); static void banner_size_to_contents(VMG_ uint argc); static void banner_goto(VMG_ uint argc); static void banner_set_text_color(VMG_ uint argc); static void banner_set_screen_color(VMG_ uint argc); static void banner_get_info(VMG_ uint argc); static void banner_set_size(VMG_ uint argc); /* * log console functions */ static void log_console_create(VMG_ uint argc); static void log_console_close(VMG_ uint argc); static void log_console_say(VMG_ uint argc); protected: /* * Map an "extended" keystroke from raw (os_getc_raw, os_get_event) to * portable UTF-8 representation. An extended keystroke is one of the * CMD_xxx key sequences, which the os_xxx layer represents by * returning a null (zero) byte followed by a CMD_xxx code. The caller * should simply pass the second byte of the sequence here, and we'll * provide a suitable key name. The buffer that 'buf' points to must * be at least 32 bytes long. * * - For ALT keys, we'll return a name like '[alt-x]'. * * - For CMD_xxx codes, we'll return a key name enclosed in square * brackets; for example, for a left cursor arrow key, we'll return * '[left]'. * * - For unknown CMD_xxx keys, we'll return '[?]'. */ static int map_ext_key(VMG_ char *buf, int extc); /* * Map a keystroke from raw (os_getc_raw) notation to a portable UTF-8 * representation. Takes an array of bytes giving the local character, * which might be represented as a multi-byte sequence. The caller is * responsible for calling raw_key_complete() to determine when enough * bytes have been fetched from the osifc layer to form a complete * character. * * - For backspace (ctrl-H or ASCII 127), we'll return '[bksp]'. * * - For ASCII 10 or 13, we'll return ASCII 10 ('\n') * * - For ASCII 9, we'll return ASCII 9 ('\t'). * * - For other control characters, we'll return a name like '[ctrl-z]'. * * - For Escape, we'll return '[esc]'. * * - For any non-control character that isn't a CMD_xxx command code, * we'll map the character to UTF-8 and return the resulting single * character (which might, of course, be more than one byte long). * * Returns true if we found a valid mapping for the key, false if not * (in which case the buffer will be filled in with '[?]'. The return * buffer is always null-terminated. */ static int map_raw_key(VMG_ char *buf, const char *c, size_t len); /* * Determine if the given byte sequence constitutes a complete * character in the local character set. Returns true if so, false if * not. Callers can use this to determine how many bytes must be * fetched from the keyboard to complete an entire character sequence * in the local character set, which can be important when using a * multi-byte character set. */ static int raw_key_complete(VMG_ const char *c, size_t len); /* display one or more arguments to a given console */ static void say_to_console(VMG_ class CVmConsole *console, uint argc); /* close the current script file */ static void close_script_file(VMG0_); }; #endif /* VMBIFTIO_H */ /* ------------------------------------------------------------------------ */ /* * TADS I/O function set vector. Define this only if VMBIF_DEFINE_VECTOR * has been defined, so that this file can be included for the prototypes * alone without defining the function vector. * * Note that this vector is specifically defined outside of the section of * the file protected against multiple inclusion. */ #ifdef VMBIF_DEFINE_VECTOR /* TADS input/output functions */ vm_bif_desc CVmBifTIO::bif_table[] = { { &CVmBifTIO::say, 1, 0, TRUE }, /* 0 */ { &CVmBifTIO::logging, 1, 1, FALSE }, /* 1 */ { &CVmBifTIO::clearscreen, 0, 0, FALSE }, /* 2 */ { &CVmBifTIO::more, 0, 0, FALSE }, /* 3 */ { &CVmBifTIO::input, 0, 0, FALSE }, /* 4 */ { &CVmBifTIO::inputkey, 0, 0, FALSE }, /* 5 */ { &CVmBifTIO::inputevent, 0, 1, FALSE }, /* 6 */ { &CVmBifTIO::inputdialog, 5, 0, FALSE }, /* 7 */ { &CVmBifTIO::askfile, 4, 0, FALSE }, /* 8 */ { &CVmBifTIO::timedelay, 1, 0, FALSE }, /* 9 */ { &CVmBifTIO::sysinfo, 1, 0, TRUE }, /* 10 */ { &CVmBifTIO::status_mode, 1, 0, FALSE }, /* 11 */ { &CVmBifTIO::status_right, 1, 0, FALSE }, /* 12 */ { &CVmBifTIO::res_exists, 1, 0, FALSE }, /* 13 */ { &CVmBifTIO::set_script_file, 1, 1, FALSE }, /* 14 */ { &CVmBifTIO::get_charset, 1, 0, FALSE }, /* 15 */ { &CVmBifTIO::flush_output, 0, 0, FALSE }, /* 16 */ { &CVmBifTIO::input_timeout, 0, 1, FALSE }, /* 17 */ { &CVmBifTIO::input_cancel, 1, 0, FALSE }, /* 18 */ { &CVmBifTIO::banner_create, 8, 0, FALSE }, /* 19 */ { &CVmBifTIO::banner_delete, 1, 0, FALSE }, /* 20 */ { &CVmBifTIO::banner_clear, 1, 0, FALSE }, /* 21 */ { &CVmBifTIO::banner_say, 1, 0, TRUE }, /* 22 */ { &CVmBifTIO::banner_flush, 1, 0, FALSE }, /* 23 */ { &CVmBifTIO::banner_size_to_contents, 1, 0, FALSE }, /* 24 */ { &CVmBifTIO::banner_goto, 3, 0, FALSE }, /* 25 */ { &CVmBifTIO::banner_set_text_color, 3, 0, FALSE }, /* 26 */ { &CVmBifTIO::banner_set_screen_color, 2, 0, FALSE }, /* 27 */ { &CVmBifTIO::banner_get_info, 1, 0, FALSE }, /* 28 */ { &CVmBifTIO::banner_set_size, 4, 0, FALSE }, /* 29 */ { &CVmBifTIO::log_console_create, 3, 0, FALSE }, /* 30 */ { &CVmBifTIO::log_console_close, 1, 0, FALSE }, /* 31 */ { &CVmBifTIO::log_console_say, 1, 0, TRUE }, /* 32 */ { &CVmBifTIO::log_input_event, 1, 0, FALSE } /* 33 */ }; #endif /* VMBIF_DEFINE_VECTOR */ qtads-2.1.7/tads3/vmbiftix.cpp000066400000000000000000000104001265017072300162050ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* Copyright (c) 2005 by Michael J. Roberts. All Rights Reserved. */ /* Name vmbiftix.cpp - TADS Input/Output Extensions function set Function Notes Modified 03/02/05 MJRoberts - Creation */ #include #include #include #include "t3std.h" #include "os.h" #include "osifcext.h" #include "utf8.h" #include "charmap.h" #include "vmbiftix.h" #include "vmstack.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmglob.h" #include "vmpool.h" #include "vmobj.h" #include "vmstr.h" #include "vmlst.h" #include "vmrun.h" #include "vmhost.h" #include "vmconsol.h" #include "vmvec.h" /* ------------------------------------------------------------------------ */ /* * Show a popup menu */ void CVmBifTIOExt::show_popup_menu(VMG_ uint argc) { int x, y, default_pos; char *txt; os_event_info_t evt; int ret; int elecnt; vm_obj_id_t lst_obj; CVmObjList *lst; vm_val_t val; /* check arguments */ check_argc(vmg_ argc, 3); /* get the x,y coordinates */ if (G_stk->get(0)->typ == VM_NIL) { /* nil x,y - use default position */ default_pos = TRUE; x = y = 0; /* discard the nil x,y values */ G_stk->discard(2); } else { /* pop the x,y positions */ x = pop_int_val(vmg0_); y = pop_int_val(vmg0_); } /* get the HTML text for the contents of the window */ txt = pop_str_val_ui(vmg_ 0, 0); /* flush the console display output */ G_console->flush_all(vmg_ VM_NL_NONE); /* show the window */ ret = os_show_popup_menu(default_pos, x, y, txt, strlen(txt), &evt); /* free the HTML text buffer we allocated */ t3free(txt); /* see what we have */ switch (ret) { case OSPOP_FAIL: case OSPOP_CANCEL: case OSPOP_EOF: default: elecnt = 1; break; case OSPOP_HREF: elecnt = 2; break; } /* allocate the return list */ lst_obj = CVmObjList::create(vmg_ FALSE, elecnt); lst = (CVmObjList *)vm_objp(vmg_ lst_obj); lst->cons_clear(); /* protect the list from garbage collection */ val.set_obj(lst_obj); G_stk->push(&val); /* set the first element to the main return code */ val.set_int(ret); lst->cons_set_element(0, &val); /* set additional elements according to the return code */ switch (ret) { case OSPOP_HREF: /* add the HREF element */ val.set_obj(str_from_ui_str(vmg_ evt.href)); lst->cons_set_element(1, &val); break; default: /* there aren't any other elements for other return codes */ break; } /* return the list */ retval_obj(vmg_ lst_obj); /* discard the GC protection */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * Enable one system menu command */ static void enable_sys_menu_cmd_item(VMG_ const vm_val_t *idval, int stat) { int id; /* retrieve the ID as an integer */ G_stk->push(idval); id = CVmBif::pop_int_val(vmg0_); /* set it */ os_enable_cmd_event(id, stat); } /* * Enable/disable system menu commands */ void CVmBifTIOExt::enable_sys_menu_cmd(VMG_ uint argc) { vm_val_t *valp; int stat; int cnt; /* check arguments */ check_argc(vmg_ argc, 2); /* the second argument is the new status - retrieve it as an integer */ G_stk->push(G_stk->get(1)); stat = pop_int_val(vmg0_); /* * The first argument is either a single menu ID, or a list of menu * IDs. Check what we have. */ valp = G_stk->get(0); if (valp->is_listlike(vmg0_) && (cnt = valp->ll_length(vmg0_)) >= 0) { /* set the status for each element */ for (int i = 1 ; i <= cnt ; ++i) { /* get this element value */ vm_val_t ele; valp->ll_index(vmg_ &ele, i); /* set the status */ enable_sys_menu_cmd_item(vmg_ &ele, stat); } } else if (valp->typ == VM_INT) { /* it's a single value - handle it individually */ enable_sys_menu_cmd_item(vmg_ valp, stat); } /* discard the arguments, and we're done */ G_stk->discard(2); } qtads-2.1.7/tads3/vmbiftix.h000066400000000000000000000025571265017072300156700ustar00rootroot00000000000000/* Copyright (c) 2005 by Michael J. Roberts. All Rights Reserved. */ /* Name vmbiftix.h - function set definition - TADS I/O Extensions Function The I/O Extensions function set provides access to some UI functionality beyond the basic tads-io set. Notes Modified 03/02/05 MJRoberts - Creation */ #ifndef VMBIFTIX_H #define VMBIFTIX_H #include "os.h" #include "vmbif.h" #include "utf8.h" class CVmBifTIOExt: public CVmBif { public: /* function vector */ static vm_bif_desc bif_table[]; /* show a popup menu */ static void show_popup_menu(VMG_ uint argc); /* enable/disable system menu command */ static void enable_sys_menu_cmd(VMG_ uint argc); }; #endif /* VMBIFTIX_H */ /* ------------------------------------------------------------------------ */ /* * Function set vector. Define this only if VMBIF_DEFINE_VECTOR has been * defined, so that this file can be included for the prototypes alone * without defining the function vector. * * Note that this vector is specifically defined outside of the section of * the file protected against multiple inclusion. */ #ifdef VMBIF_DEFINE_VECTOR /* TADS input/output extension functions */ vm_bif_desc CVmBifTIOExt::bif_table[] = { { &CVmBifTIOExt::show_popup_menu, 3, 0, FALSE }, { &CVmBifTIOExt::enable_sys_menu_cmd, 2, 0, FALSE } }; #endif /* VMBIF_DEFINE_VECTOR */ qtads-2.1.7/tads3/vmbignum.cpp000066400000000000000000005512231265017072300162160ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbignum.cpp - big number metaclass Function Notes Modified 02/18/00 MJRoberts - Creation */ #include #include #include #include #include #include #include #include "t3std.h" #include "vmtype.h" #include "vmmcreg.h" #include "vmbignum.h" #include "vmobj.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmfile.h" #include "vmpool.h" #include "vmstack.h" #include "utf8.h" #include "vmstr.h" #include "vmbif.h" #include "vmmeta.h" #include "vmlst.h" #include "vmdatasrc.h" /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassBigNum metaclass_reg_obj; CVmMetaclass *CVmObjBigNum::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjBigNum:: *CVmObjBigNum::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjBigNum::getp_undef, &CVmObjBigNum::getp_format, &CVmObjBigNum::getp_equal_rnd, &CVmObjBigNum::getp_get_prec, &CVmObjBigNum::getp_set_prec, &CVmObjBigNum::getp_frac, &CVmObjBigNum::getp_whole, &CVmObjBigNum::getp_round_dec, &CVmObjBigNum::getp_abs, &CVmObjBigNum::getp_ceil, &CVmObjBigNum::getp_floor, &CVmObjBigNum::getp_get_scale, &CVmObjBigNum::getp_scale, &CVmObjBigNum::getp_negate, &CVmObjBigNum::getp_copy_sign, &CVmObjBigNum::getp_is_neg, &CVmObjBigNum::getp_remainder, &CVmObjBigNum::getp_sin, &CVmObjBigNum::getp_cos, &CVmObjBigNum::getp_tan, &CVmObjBigNum::getp_deg2rad, &CVmObjBigNum::getp_rad2deg, &CVmObjBigNum::getp_asin, &CVmObjBigNum::getp_acos, &CVmObjBigNum::getp_atan, &CVmObjBigNum::getp_sqrt, &CVmObjBigNum::getp_ln, &CVmObjBigNum::getp_exp, &CVmObjBigNum::getp_log10, &CVmObjBigNum::getp_pow, &CVmObjBigNum::getp_sinh, &CVmObjBigNum::getp_cosh, &CVmObjBigNum::getp_tanh, &CVmObjBigNum::getp_pi, &CVmObjBigNum::getp_e, &CVmObjBigNum::getp_numType }; /* constant value 1 */ const unsigned char CVmObjBigNum::one_[] = { /* number of digits - we just need one to represent the integer 1 */ 0x01, 0x00, /* exponent */ 0x01, 0x00, /* flags */ 0x00, /* mantissa - just one digit is needed, but pad out the byte with zero */ 0x10 }; #if 0 /* * Pi to 2048 digits */ const unsigned char CVmObjBigNum::pi_[] = { /* number of digits of precision */ 0x00, 0x08, /* base-10 exponent */ 0x01, 0x00, /* flags */ 0x00, /* * the first 2048 decimal digits of pi, packed two to a byte (typed * in from memory - I hope I got everything right :) */ /* 1-100 */ 0x31, 0x41, 0x59, 0x26, 0x53, 0x58, 0x97, 0x93, 0x23, 0x84, 0x62, 0x64, 0x33, 0x83, 0x27, 0x95, 0x02, 0x88, 0x41, 0x97, 0x16, 0x93, 0x99, 0x37, 0x51, 0x05, 0x82, 0x09, 0x74, 0x94, 0x45, 0x92, 0x30, 0x78, 0x16, 0x40, 0x62, 0x86, 0x20, 0x89, 0x98, 0x62, 0x80, 0x34, 0x82, 0x53, 0x42, 0x11, 0x70, 0x67, /* 101-200 */ 0x98, 0x21, 0x48, 0x08, 0x65, 0x13, 0x28, 0x23, 0x06, 0x64, 0x70, 0x93, 0x84, 0x46, 0x09, 0x55, 0x05, 0x82, 0x23, 0x17, 0x25, 0x35, 0x94, 0x08, 0x12, 0x84, 0x81, 0x11, 0x74, 0x50, 0x28, 0x41, 0x02, 0x70, 0x19, 0x38, 0x52, 0x11, 0x05, 0x55, 0x96, 0x44, 0x62, 0x29, 0x48, 0x95, 0x49, 0x30, 0x38, 0x19, /* 201-300 */ 0x64, 0x42, 0x88, 0x10, 0x97, 0x56, 0x65, 0x93, 0x34, 0x46, 0x12, 0x84, 0x75, 0x64, 0x82, 0x33, 0x78, 0x67, 0x83, 0x16, 0x52, 0x71, 0x20, 0x19, 0x09, 0x14, 0x56, 0x48, 0x56, 0x69, 0x23, 0x46, 0x03, 0x48, 0x61, 0x04, 0x54, 0x32, 0x66, 0x48, 0x21, 0x33, 0x93, 0x60, 0x72, 0x60, 0x24, 0x91, 0x41, 0x27, /* 301-400 */ 0x37, 0x24, 0x58, 0x70, 0x06, 0x60, 0x63, 0x15, 0x58, 0x81, 0x74, 0x88, 0x15, 0x20, 0x92, 0x09, 0x62, 0x82, 0x92, 0x54, 0x09, 0x17, 0x15, 0x36, 0x43, 0x67, 0x89, 0x25, 0x90, 0x36, 0x00, 0x11, 0x33, 0x05, 0x30, 0x54, 0x88, 0x20, 0x46, 0x65, 0x21, 0x38, 0x41, 0x46, 0x95, 0x19, 0x41, 0x51, 0x16, 0x09, /* 401-500 */ 0x43, 0x30, 0x57, 0x27, 0x03, 0x65, 0x75, 0x95, 0x91, 0x95, 0x30, 0x92, 0x18, 0x61, 0x17, 0x38, 0x19, 0x32, 0x61, 0x17, 0x93, 0x10, 0x51, 0x18, 0x54, 0x80, 0x74, 0x46, 0x23, 0x79, 0x96, 0x27, 0x49, 0x56, 0x73, 0x51, 0x88, 0x57, 0x52, 0x72, 0x48, 0x91, 0x22, 0x79, 0x38, 0x18, 0x30, 0x11, 0x94, 0x91, /* 501-600 */ 0x29, 0x83, 0x36, 0x73, 0x36, 0x24, 0x40, 0x65, 0x66, 0x43, 0x08, 0x60, 0x21, 0x39, 0x49, 0x46, 0x39, 0x52, 0x24, 0x73, 0x71, 0x90, 0x70, 0x21, 0x79, 0x86, 0x09, 0x43, 0x70, 0x27, 0x70, 0x53, 0x92, 0x17, 0x17, 0x62, 0x93, 0x17, 0x67, 0x52, 0x38, 0x46, 0x74, 0x81, 0x84, 0x67, 0x66, 0x94, 0x05, 0x13, /* 601-700 */ 0x20, 0x00, 0x56, 0x81, 0x27, 0x14, 0x52, 0x63, 0x56, 0x08, 0x27, 0x78, 0x57, 0x71, 0x34, 0x27, 0x57, 0x78, 0x96, 0x09, 0x17, 0x36, 0x37, 0x17, 0x87, 0x21, 0x46, 0x84, 0x40, 0x90, 0x12, 0x24, 0x95, 0x34, 0x30, 0x14, 0x65, 0x49, 0x58, 0x53, 0x71, 0x05, 0x07, 0x92, 0x27, 0x96, 0x89, 0x25, 0x89, 0x23, /* 701-800 */ 0x54, 0x20, 0x19, 0x95, 0x61, 0x12, 0x12, 0x90, 0x21, 0x96, 0x08, 0x64, 0x03, 0x44, 0x18, 0x15, 0x98, 0x13, 0x62, 0x97, 0x74, 0x77, 0x13, 0x09, 0x96, 0x05, 0x18, 0x70, 0x72, 0x11, 0x34, 0x99, 0x99, 0x99, 0x83, 0x72, 0x97, 0x80, 0x49, 0x95, 0x10, 0x59, 0x73, 0x17, 0x32, 0x81, 0x60, 0x96, 0x31, 0x85, /* 801-900 */ 0x95, 0x02, 0x44, 0x59, 0x45, 0x53, 0x46, 0x90, 0x83, 0x02, 0x64, 0x25, 0x22, 0x30, 0x82, 0x53, 0x34, 0x46, 0x85, 0x03, 0x52, 0x61, 0x93, 0x11, 0x88, 0x17, 0x10, 0x10, 0x00, 0x31, 0x37, 0x83, 0x87, 0x52, 0x88, 0x65, 0x87, 0x53, 0x32, 0x08, 0x38, 0x14, 0x20, 0x61, 0x71, 0x77, 0x66, 0x91, 0x47, 0x30, /* 901-1000 */ 0x35, 0x98, 0x25, 0x34, 0x90, 0x42, 0x87, 0x55, 0x46, 0x87, 0x31, 0x15, 0x95, 0x62, 0x86, 0x38, 0x82, 0x35, 0x37, 0x87, 0x59, 0x37, 0x51, 0x95, 0x77, 0x81, 0x85, 0x77, 0x80, 0x53, 0x21, 0x71, 0x22, 0x68, 0x06, 0x61, 0x30, 0x01, 0x92, 0x78, 0x76, 0x61, 0x11, 0x95, 0x90, 0x92, 0x16, 0x42, 0x01, 0x98, /* 1001-1100 */ 0x93, 0x80, 0x95, 0x25, 0x72, 0x01, 0x06, 0x54, 0x85, 0x86, 0x32, 0x78, 0x86, 0x59, 0x36, 0x15, 0x33, 0x81, 0x82, 0x79, 0x68, 0x23, 0x03, 0x01, 0x95, 0x20, 0x35, 0x30, 0x18, 0x52, 0x96, 0x89, 0x95, 0x77, 0x36, 0x22, 0x59, 0x94, 0x13, 0x89, 0x12, 0x49, 0x72, 0x17, 0x75, 0x28, 0x34, 0x79, 0x13, 0x15, /* 1101-1200 */ 0x15, 0x57, 0x48, 0x57, 0x24, 0x24, 0x54, 0x15, 0x06, 0x95, 0x95, 0x08, 0x29, 0x53, 0x31, 0x16, 0x86, 0x17, 0x27, 0x85, 0x58, 0x89, 0x07, 0x50, 0x98, 0x38, 0x17, 0x54, 0x63, 0x74, 0x64, 0x93, 0x93, 0x19, 0x25, 0x50, 0x60, 0x40, 0x09, 0x27, 0x70, 0x16, 0x71, 0x13, 0x90, 0x09, 0x84, 0x88, 0x24, 0x01, /* 1201-1300 */ 0x28, 0x58, 0x36, 0x16, 0x03, 0x56, 0x37, 0x07, 0x66, 0x01, 0x04, 0x71, 0x01, 0x81, 0x94, 0x29, 0x55, 0x59, 0x61, 0x98, 0x94, 0x67, 0x67, 0x83, 0x74, 0x49, 0x44, 0x82, 0x55, 0x37, 0x97, 0x74, 0x72, 0x68, 0x47, 0x10, 0x40, 0x47, 0x53, 0x46, 0x46, 0x20, 0x80, 0x46, 0x68, 0x42, 0x59, 0x06, 0x94, 0x91, /* 1301-1400 */ 0x29, 0x33, 0x13, 0x67, 0x70, 0x28, 0x98, 0x91, 0x52, 0x10, 0x47, 0x52, 0x16, 0x20, 0x56, 0x96, 0x60, 0x24, 0x05, 0x80, 0x38, 0x15, 0x01, 0x93, 0x51, 0x12, 0x53, 0x38, 0x24, 0x30, 0x03, 0x55, 0x87, 0x64, 0x02, 0x47, 0x49, 0x64, 0x73, 0x26, 0x39, 0x14, 0x19, 0x92, 0x72, 0x60, 0x42, 0x69, 0x92, 0x27, /* 1401-1500 */ 0x96, 0x78, 0x23, 0x54, 0x78, 0x16, 0x36, 0x00, 0x93, 0x41, 0x72, 0x16, 0x41, 0x21, 0x99, 0x24, 0x58, 0x63, 0x15, 0x03, 0x02, 0x86, 0x18, 0x29, 0x74, 0x55, 0x57, 0x06, 0x74, 0x98, 0x38, 0x50, 0x54, 0x94, 0x58, 0x85, 0x86, 0x92, 0x69, 0x95, 0x69, 0x09, 0x27, 0x21, 0x07, 0x97, 0x50, 0x93, 0x02, 0x95, /* 1501-1600 */ 0x53, 0x21, 0x16, 0x53, 0x44, 0x98, 0x72, 0x02, 0x75, 0x59, 0x60, 0x23, 0x64, 0x80, 0x66, 0x54, 0x99, 0x11, 0x98, 0x81, 0x83, 0x47, 0x97, 0x75, 0x35, 0x66, 0x36, 0x98, 0x07, 0x42, 0x65, 0x42, 0x52, 0x78, 0x62, 0x55, 0x18, 0x18, 0x41, 0x75, 0x74, 0x67, 0x28, 0x90, 0x97, 0x77, 0x72, 0x79, 0x38, 0x00, /* 1601-1700 */ 0x08, 0x16, 0x47, 0x06, 0x00, 0x16, 0x14, 0x52, 0x49, 0x19, 0x21, 0x73, 0x21, 0x72, 0x14, 0x77, 0x23, 0x50, 0x14, 0x14, 0x41, 0x97, 0x35, 0x68, 0x54, 0x81, 0x61, 0x36, 0x11, 0x57, 0x35, 0x25, 0x52, 0x13, 0x34, 0x75, 0x74, 0x18, 0x49, 0x46, 0x84, 0x38, 0x52, 0x33, 0x23, 0x90, 0x73, 0x94, 0x14, 0x33, /* 1701-1800 */ 0x34, 0x54, 0x77, 0x62, 0x41, 0x68, 0x62, 0x51, 0x89, 0x83, 0x56, 0x94, 0x85, 0x56, 0x20, 0x99, 0x21, 0x92, 0x22, 0x18, 0x42, 0x72, 0x55, 0x02, 0x54, 0x25, 0x68, 0x87, 0x67, 0x17, 0x90, 0x49, 0x46, 0x01, 0x65, 0x34, 0x66, 0x80, 0x49, 0x88, 0x62, 0x72, 0x32, 0x79, 0x17, 0x86, 0x08, 0x57, 0x84, 0x38, /* 1801-1900 */ 0x38, 0x27, 0x96, 0x79, 0x76, 0x68, 0x14, 0x54, 0x10, 0x09, 0x53, 0x88, 0x37, 0x86, 0x36, 0x09, 0x50, 0x68, 0x00, 0x64, 0x22, 0x51, 0x25, 0x20, 0x51, 0x17, 0x39, 0x29, 0x84, 0x89, 0x60, 0x84, 0x12, 0x84, 0x88, 0x62, 0x69, 0x45, 0x60, 0x42, 0x41, 0x96, 0x52, 0x85, 0x02, 0x22, 0x10, 0x66, 0x11, 0x86, /* 1901-2000 */ 0x30, 0x67, 0x44, 0x27, 0x86, 0x22, 0x03, 0x91, 0x94, 0x94, 0x50, 0x47, 0x12, 0x37, 0x13, 0x78, 0x69, 0x60, 0x95, 0x63, 0x64, 0x37, 0x19, 0x17, 0x28, 0x74, 0x67, 0x76, 0x46, 0x57, 0x57, 0x39, 0x62, 0x41, 0x38, 0x90, 0x86, 0x58, 0x32, 0x64, 0x59, 0x95, 0x81, 0x33, 0x90, 0x47, 0x80, 0x27, 0x59, 0x00, /* 2001-2048 */ 0x99, 0x46, 0x57, 0x64, 0x07, 0x89, 0x51, 0x26, 0x94, 0x68, 0x39, 0x83, 0x52, 0x59, 0x57, 0x09, 0x82, 0x58, 0x22, 0x62, 0x05, 0x22, 0x48, 0x94 }; #endif /* ------------------------------------------------------------------------ */ /* * The BigNumber internal cache. This is meant to be private to the * vmbignum module, but since the module is actually split between * vmbignum.cpp and vmbignumlib.cpp, we need to use a C++ global. We use * the S_ naming prefix to emphasize that it's conceptually a "static" * variable, private to this two-file module. */ extern CVmBigNumCache *S_bignum_cache; /* ------------------------------------------------------------------------ */ /* * Static creation methods. These routines allocate an object ID and * create a new object. */ /* create dynamically using stack arguments */ vm_obj_id_t CVmObjBigNum::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id; vm_val_t *val; size_t digits; const char *strval = 0; const CVmObjBigNum *objval = 0; /* check arguments */ if (argc < 1 || argc > 2) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* * check the first argument, which gives the source value - this can be * an integer, a string, or another BigNumber value */ val = G_stk->get(0); if (val->typ == VM_INT) { /* we'll use the integer value */ } else if (val->typ == VM_OBJ && is_bignum_obj(vmg_ val->val.obj)) { /* we'll use the BigNumber value as the source */ objval = (CVmObjBigNum *)vm_objp(vmg_ val->val.obj); } else if ((strval = val->get_as_string(vmg0_)) != 0) { /* we'll parse the string value */ } else { /* it's not a source type we accept */ err_throw(VMERR_NUM_VAL_REQD); } /* * get the precision value, if provided; if not, infer it from the * source value */ if (argc > 1) { /* make sure it's an integer */ if (G_stk->get(1)->typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); /* retrieve the value */ digits = (size_t)G_stk->get(1)->val.intval; } else if (strval != 0) { /* parse the string to infer the precision */ digits = precision_from_string( strval + VMB_LEN, vmb_get_len(strval), 10); } else if (objval != 0) { /* use the same precision as the source BigNumber value */ digits = get_prec(objval->get_ext()); } else { /* use a default precision */ digits = 32; } /* create the value */ if (strval != 0) { /* create the value from the string */ id = vm_new_id(vmg_ FALSE, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ strval + VMB_LEN, vmb_get_len(strval), digits); } else if (objval != 0) { vm_val_t new_val; /* create the value based on the BigNumber value */ round_val(vmg_ &new_val, objval->get_ext(), digits, TRUE); /* return the new object ID */ id = new_val.val.obj; } else { /* create the value based on the integer value */ id = vm_new_id(vmg_ FALSE, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ (long)val->val.intval, digits); } /* discard arguments */ G_stk->discard(argc); /* return the new object */ return id; } /* create with a given precision */ vm_obj_id_t CVmObjBigNum::create(VMG_ int in_root_set, size_t digits) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ digits); return id; } /* create from an integer value */ vm_obj_id_t CVmObjBigNum::create(VMG_ int in_root_set, long val, size_t digits) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ val, digits); return id; } /* create from an unsigned integer value */ vm_obj_id_t CVmObjBigNum::createu(VMG_ int in_root_set, ulong val, size_t digits) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ digits); set_uint_val(((CVmObjBigNum *)vm_objp(vmg_ id))->get_ext(), val); return id; } /* create from a 64-bit unsigned int expressed as two 32-bit segments */ vm_obj_id_t CVmObjBigNum::create_int64(VMG_ int in_root_set, uint32_t hi, uint32_t lo) { /* * Store into our portable 8-byte format: the portable format is * little-endian, so simply store the low part in the first four bytes, * and the high part in the second four bytes. */ char buf[8]; oswp4(buf, lo); oswp4(buf+4, hi); /* * Now create the value from the buffer. (This could be implemented a * little more efficiently by working directly from the integers, but * this is certainly not on any critical paths, and the create_rp8 code * already exists and works.) */ return create_rp8(vmg_ in_root_set, buf); } /* create from a 64-bit unsigned int expressed in portable 8-byte format */ vm_obj_id_t CVmObjBigNum::create_rp8(VMG_ int in_root_set, const char *buf) { /* * create the BigNumber object big enough to hold a 64-bit integer * value, which can have up to 20 digits */ vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ 20); CVmObjBigNum *n = (CVmObjBigNum *)vm_objp(vmg_ id); /* set the value */ n->set_rp8((const uchar *)buf); /* the source value is unsigned, so it's always non-negative */ set_neg(n->get_ext(), FALSE); /* return the object */ return id; } /* create from a 64-bit signed int */ vm_obj_id_t CVmObjBigNum::create_rp8s(VMG_ int in_root_set, const char *buf) { /* * create the BigNumber object big enough to hold a 64-bit integer * value, which can have up to 20 digits */ vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ 20); CVmObjBigNum *n = (CVmObjBigNum *)vm_objp(vmg_ id); /* make a private copy of the source value */ uchar p[8]; memcpy(p, buf, 8); /* * If the value is negative, as indicated by the sign bit on the most * significant byte, get the absolute value by computing the two's * complement. This makes the arithmetic easy because we can do the * bit twiddling with unsigned values, and just set the BigNum sign bit * when we're done with all of that. * * Note that this 2's complement arithmetic is truly portable - it * doesn't matter whether the local machine's native int type is * big-endian or little-endian or some bizarre interleaved-endian, or * if it uses 2's complement notation or some other wacky bygone scheme * (1's complement, sign bit only, etc). The reason this code is * portable regardless of the local notation is that we're not * operating on local int types: we're operating on a buffer that's in * a universal notation that we define as 64-bit 2's complement * little-endian. We're doing our work at the byte level, so as long * as the local machine has 8+-bit bytes (which is a requirement for * the platform to have a C compiler), this code is portable. */ int neg = (p[7] & 0x80) != 0; if (neg) twos_complement_p8(p); /* set the 64-bit int value */ n->set_rp8(p); /* set the sign bit */ set_neg(n->get_ext(), neg); /* return the object */ return id; } /* set the value from an unsigned 64-bit little-endian buffer */ void CVmObjBigNum::set_rp8(const uchar *p) { /* * set up temporary registers with enough precision for 64-bit ints (we * need at most 20 digits for these values) */ char tmp1[5 + 20] = { 20, 0, 0, 0, 0 }; char tmp2[5 + 20] = { 20, 0, 0, 0, 0 }; /* * shift in bits 24 at a time - this makes the math fit in 32-bit ints * so that we don't have to worry about int overflows */ set_uint_val(get_ext(), (((ulong)p[7]) << 16) | (((ulong)p[6]) << 8) | p[5]); mul_by_long(get_ext(), 0x01000000); set_uint_val(tmp1, (((ulong)p[4]) << 16) | (((ulong)p[3]) << 8) | p[2]); compute_sum_into(tmp2, get_ext(), tmp1); mul_by_long(tmp2, 0x10000); set_uint_val(tmp1, (((ulong)p[1]) << 8) | p[0]); compute_sum_into(get_ext(), tmp2, tmp1); } /* create from a string value */ vm_obj_id_t CVmObjBigNum::create(VMG_ int in_root_set, const char *str, size_t len, size_t digits) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ str, len, digits); return id; } /* create from a string value, inferring the required precision */ vm_obj_id_t CVmObjBigNum::create(VMG_ int in_root_set, const char *str, size_t len) { int digits = precision_from_string(str, len, 10); vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ str, len, digits); return id; } /* create from a string value in a given radix */ vm_obj_id_t CVmObjBigNum::create_radix( VMG_ int in_root_set, const char *str, size_t len, int radix) { int digits = precision_from_string(str, len, radix); vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); if (radix == 10) new (vmg_ id) CVmObjBigNum(vmg_ str, len, digits); else { new (vmg_ id) CVmObjBigNum(vmg_ digits); ((CVmObjBigNum *)vm_objp(vmg_ id))->set_str_val(str, len, radix); } return id; } /* create from a double value, with enough precision for a native double */ vm_obj_id_t CVmObjBigNum::create(VMG_ int in_root_set, double val) { int digits = DBL_DIG + 2; vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ val, digits); return id; } /* create from a double value, with specified precision */ vm_obj_id_t CVmObjBigNum::create(VMG_ int in_root_set, double val, size_t digits) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ val, digits); return id; } /* * Create from a BER-encoded compressed integer value */ vm_obj_id_t CVmObjBigNum::create_from_ber(VMG_ int in_root_set, const char *buf, size_t len) { /* * Create the object. The source value has 7 bits of precision per * byte. We need log(2)/log(10) digits of precision per bit of * precision, which is about 0.30103 digits per bit. */ int prec = (int)(len*7*0.30103 + 0.5); vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ prec); CVmObjBigNum *n = (CVmObjBigNum *)vm_objp(vmg_ id); char *ext = n->get_ext(); /* allocate two temporary registers for intermediate sums */ char *t1, *t2; uint hdl1, hdl2; alloc_temp_regs((size_t)prec, 2, &t1, &hdl1, &t2, &hdl2); /* zero the accumulator (start with t1) */ set_zero(t1); /* set up a stack register for the base-128 digits */ char dig[5 + 3] = { 3, 0, 0, 0, 0 }; /* starting at the most significant byte, shift in the bits */ for (size_t i = 0 ; i < len ; ++i) { /* multiply the accumulator by 2^7 */ mul_by_long(t1, 128); /* add in the next base-128 digit */ set_uint_val(dig, buf[i] & 0x7f); compute_sum_into(t2, t1, dig); /* swap registers so that t2 becomes the accumulator */ char *tt = t1; t1 = t2; t2 = tt; } /* copy the accumulator into the result */ copy_val(ext, t1, FALSE); /* done with the temporary registers */ release_temp_regs(2, hdl1, hdl2); /* return the new ID */ return id; } /* * Create from a floating point value encoded in an IEEE 754-2008 binary * interchange format, little-endian order. We accept bit sizes of 16, 32, * 64, and 128 (half, single, double, and quad precision). * */ vm_obj_id_t CVmObjBigNum::create_from_ieee754( VMG_ int in_root_set, const char *buf, int bits) { /* * Create the object. Use the decimal equivalent precision of the IEEE * 754 object. Note that we use 17 digits for doubles; the binary type * stores 53 bits of mantissa == 15.95 decimal digits, but there's a * pathological case at 1+epsilon, which is 1 + 2e-16, which requires * 17 decimal digits to store. */ int prec = (bits == 16 ? 4 : bits == 32 ? 8 : bits == 64 ? 17 : bits == 128 ? 35 : 8); vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ prec); CVmObjBigNum *n = (CVmObjBigNum *)vm_objp(vmg_ id); /* decode the IEEE 754 value */ n->set_ieee754_value(vmg_ buf, bits); /* return the new object id */ return id; } /* ------------------------------------------------------------------------ */ /* * Cast a value to BigNumber */ void CVmObjBigNum::cast_to_bignum(VMG_ vm_val_t *bnval, const vm_val_t *srcval) { const char *str; if (srcval->typ == VM_INT) { /* create from the integer value */ bnval->set_obj(create( vmg_ FALSE, (long)srcval->val.intval, (size_t)10)); } else if ((str = srcval->get_as_string(vmg0_)) != 0) { /* get the string length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* create from the string value */ bnval->set_obj(create(vmg_ FALSE, str, len)); } else if (srcval->typ == VM_OBJ && is_bignum_obj(vmg_ srcval->val.obj)) { /* it's already a BigNumber - just return the same value */ bnval->set_obj(srcval->val.obj); } else { /* can't cast to BigNumber */ err_throw(VMERR_NO_BIGNUM_CONV); } } /* * Promote an integer value to BigNumber */ void CVmObjBigNum::promote_int(VMG_ vm_val_t *val) const { val->set_obj(create(vmg_ FALSE, (long)val->val.intval, (size_t)10)); } /* ------------------------------------------------------------------------ */ /* * Constructors. These are called indirectly through our static * creation methods. */ /* * Create with no extension */ CVmObjBigNum::CVmObjBigNum() { /* no extension */ ext_ = 0; } /* * Create with a given precision */ CVmObjBigNum::CVmObjBigNum(VMG_ size_t digits) { /* allocate space */ alloc_bignum(vmg_ digits); } /* * Create with a given integer value */ CVmObjBigNum::CVmObjBigNum(VMG_ long val, size_t digits) { /* allocate space */ alloc_bignum(vmg_ digits); /* set the value */ set_int_val(ext_, val); } /* * Create with a given string as the source value */ CVmObjBigNum::CVmObjBigNum(VMG_ const char *str, size_t len, size_t digits) { /* allocate space */ alloc_bignum(vmg_ digits); /* set the value */ set_str_val(str, len); } /* * Create from a double value */ CVmObjBigNum::CVmObjBigNum(VMG_ double val, size_t digits) { /* allocate space */ alloc_bignum(vmg_ digits); /* set the value */ set_double_val(ext_, val); } /* ------------------------------------------------------------------------ */ /* * Delete */ void CVmObjBigNum::notify_delete(VMG_ int in_root_set) { /* * free our extension - do this only if it's not in the root set, * because extension will be directly in the image data for a root * set object */ if (ext_ != 0 && !in_root_set) G_mem->get_var_heap()->free_mem(ext_); } /* ------------------------------------------------------------------------ */ /* * Allocate space for a given precision */ void CVmObjBigNum::alloc_bignum(VMG_ size_t digits) { /* allocate space for the given number of elements */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(calc_alloc(digits), this); /* set the precision */ set_prec(ext_, digits); /* initialize the value to zero */ set_int_val(ext_, 0); /* clear the flags */ ext_[VMBN_FLAGS] = 0; } /* ------------------------------------------------------------------------ */ /* * Write to a 'data' mode file */ int CVmObjBigNum::write_to_data_file(CVmDataSource *fp) { char buf[16]; /* write the number of digits (i.e., the precision) */ oswp2(buf, get_prec(ext_)); if (fp->write(buf, 2)) return 1; /* write our entire extension */ if (fp->write(ext_, calc_alloc(get_prec(ext_)))) return 1; /* success */ return 0; } /* * Read from a 'data' mode file and instantiate a new BigNumber object to * hold the result */ int CVmObjBigNum::read_from_data_file(VMG_ vm_val_t *retval, CVmDataSource *fp) { char buf[16]; size_t prec; CVmObjBigNum *bignum; /* read the precision */ if (fp->read(buf, 2)) return 1; prec = osrp2(buf); /* create a BigNumber with the required precision */ retval->set_obj(create(vmg_ FALSE, prec)); bignum = (CVmObjBigNum *)vm_objp(vmg_ retval->val.obj); /* read the bytes into the new object's extension */ if (fp->read(bignum->get_ext(), calc_alloc(prec))) return 1; /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Set my value to a string */ void CVmObjBigNum::set_str_val(const char *str, size_t len) { /* parse the string into my extension */ parse_str_into(ext_, str, len); } /* * Set my string value with a given radix. If the radix is decimal, we'll * use the regular floating point parser. Otherwise we'll parse it as an * integer value in the given radix. */ void CVmObjBigNum::set_str_val(const char *str, size_t len, int radix) { /* parse the string into my extension using the given radix */ parse_str_into(ext_, str, len, radix); } /* ------------------------------------------------------------------------ */ /* * IEEE 754-2008 binary interchange format buffer manipulation. We arrange * our buffer in little-endian order to match the idiosyncratic TADS * pack/unpack formats, but otherwise we use the standard IEEE formats. */ /* IEEE 754-2008 buffer */ struct ieee754 { ieee754(char *buf, int bits) { /* remember the buffer and bit size */ this->buf = (unsigned char *)buf; this->bits = bits; this->bytes = bits/8; /* get the mantissa size (in bits) for the format size */ mbits = (bits == 16 ? 10 : bits == 32 ? 23 : bits == 64 ? 52 : bits == 128 ? 112 : 0); if (mbits == 0) err_throw(VMERR_NO_DOUBLE_CONV); /* the exponent uses the leftover bits, minus the sign bit */ ebits = bits - mbits - 1; /* figure the exponent bias - it's the halfway point for the range */ ebias = (1 << (ebits - 1)) - 1; /* figure the exponent range */ emin = 1 - ebias; emax = ebias; } /* * Get the number of decimal digits this type can represent. The * decimal precision is fractional, since the IEEE type is binary; we * round up to the next integer. There's a */ int decimal_digits() { return bits == 16 ? 4 : bits == 32 ? 8 : bits == 64 ? 17 : bits == 128 ? 35 : 0; } /* get the maximum number of decimal digits in the exponent */ int decimal_exp_digits() { return ebits <= 5 ? 2 : ebits <= 7 ? 3 : ebits <= 11 ? 4 : 5; } /* is the value infinity? */ int is_infinity() const { /* infinity has an exponent of emax+1 and a zero mantissa */ return get_exp() == emax + 1 && is_mantissa_zero(); } /* set Infinity */ void set_infinity(int neg) { /* * Infinity is represented with the maximum exponent plus one, and * the mantissa set to all zeros. Infinities can be positive or * negative, so set the sign bit. */ memset(buf, 0, bytes); set_exp(emax + 1); set_sign(neg); } /* is the value zero? */ int is_zero() const { /* * a zero is represented by all bits zero except possibly the sign * bit, which is 0 for +0 and 1 for -0 */ int sum = 0; for (int i = 0 ; i < bytes - 1 ; ++i) sum += buf[i]; return sum == 0 && (buf[bytes-1] & 0x7f) == 0; } /* set Zero */ void set_zero(int neg) { /* * Zero is represented with an exponent of zero and all zero bits * in the mantissa. Zeros can be positive or negative, so set the * sign bit. */ memset(buf, 0, bytes); set_sign(neg); } /* is this a NAN value? */ int is_nan() const { /* a NAN value has an exponent of emax+1 and a non-zero mantissa */ return get_exp() == emax + 1 && !is_mantissa_zero(); } /* is the mantissa zero? */ int is_mantissa_zero() const { /* add up the full bytes of the mantissa, plus the partial high byte */ int imsb = mantissa_msb_index(); int sum = buf[imsb] & mantissa_msb_mask(); for (int i = 0 ; i < imsb ; sum += buf[i++]) ; /* the mantissa is zero if the sum of the bytes is zero */ return (sum == 0); } /* get the bit mask for the partial high-order byte of the mantissa */ unsigned char mantissa_msb_mask() const { if (ebits <= 7) return (0x7f >> ebits); else if (ebits <= 15) return (0xff >> (ebits - 7)); else { assert(FALSE); return 0; } } /* get the index of the most significant byte of the mantissa */ int mantissa_msb_index() const { if (ebits <= 7) return bytes - 1; else if (ebits <= 15) return bytes - 2; else { assert(FALSE); return 0; } } /* set NAN (not a number) */ void set_nan() { /* * NAN (Not A Number) is represented with the maximum exponent plus * one, and a non-zero mantissa. */ set_exp(emax + 1); set_mbit(0, 1); } /* get the sign bit - false -> positive, true -> negative */ int get_sign() const { return (buf[bytes-1] & 0x80) != 0; } /* set the sign bit */ void set_sign(int neg) { if (neg) buf[bytes-1] |= 0x80; else buf[bytes-1] &= 0x7f; } /* get the byte index and bit mask for a mantissa bit */ inline void get_mbit_ptr(int &byteidx, unsigned char &mask, int n) const { /* figure the raw bit vector index, past the sign and exponent bits */ n += 1 + ebits; /* get the byte index (little-endian order) */ byteidx = bytes - 1 - n/8; /* get the bit index within the byte, and the corresponding bit mask */ n = 7 - (n % 8); mask = 1 << n; } /* set the nth bit of the mantissa (0 is the high-order bit) */ void set_mbit(int n, int bit) { /* get the mantissa byte index and mask */ int byteidx; unsigned char mask; get_mbit_ptr(byteidx, mask, n); /* set or clear the bit */ if (bit) buf[byteidx] |= mask; else buf[byteidx] &= ~mask; } /* * Round up - add 1 to the lowest bit. Returns true if we carried to * out of the highest order bit, false if not. * * Carrying means that we carried into the implied "1" above the first * stored bit. That bumps that bit to 0 and carries to the next bit, * so we now have an implied "10" before the first bit. The caller * must renormalize simply by incrementing the exponent. (The caller * doesn't have to worry about whether this is a normal or subnormal * number, because bumping the exponent works for both. For * subnormals, we carry into the implied "0" above the first bit, which * bumps it to 1, which puts us in the normalized range, which we * represent by bumping the exponent up one to emin.) */ int round_up() { /* add to the low byte, and carry as needed to whole bytes */ int imsb = mantissa_msb_index(); for (int i = 0 ; i < imsb ; ++i) { if (buf[i] == 0xff) { /* this overflows the byte - zero it and carry to the next */ buf[i] = 0; } else { /* no overflow - increment this byte and return "no carry" */ buf[i] += 1; return FALSE; } } /* * if we got this far, we've carried into the most significant * byte, which is a partial byte - we need to use the msb bit mask * for this arithmetic */ unsigned char mask = mantissa_msb_mask(); int b = (buf[imsb] + 1) & mask; buf[imsb] &= ~mask; buf[imsb] |= b; /* if we wrapped that to zero, we carried into the implied lead bit */ return (b == 0); } int get_mbit(int n) const { /* get the mantissa byte index and mask */ int byteidx; unsigned char mask; get_mbit_ptr(byteidx, mask, n); /* return the bit value */ return (buf[byteidx] & mask) != 0; } /* set the exponent, adjusting for the bias */ void set_exp(int e) { /* add the bias, and set the raw exponent */ set_raw_exp(ebias + e); } /* set the raw exponent, without the bias adjustment */ void set_raw_exp(int e) { int mask; if (ebits <= 7) { /* it fits entirely in the first byte */ mask = (0xff << (7 - ebits)) & 0x7f; buf[bytes-1] &= ~mask; buf[bytes-1] |= (e << (7 - ebits)) & mask; } else if (ebits <= 15) { /* it fits in the first two bytes */ buf[bytes-1] &= 0x80; buf[bytes-1] |= (e >> (ebits - 7)) & 0x7f; mask = (0xff << (15 - ebits)) & 0xff; buf[bytes-2] &= ~mask; buf[bytes-2] |= (e << (15 - ebits)) & mask; } else { /* we don't currently handle anything above quad precision */ assert(FALSE); } } /* get the exponent, adjusting for the bias */ int get_exp() const { return get_raw_exp() - ebias; } /* get the raw exponent */ int get_raw_exp() const { int mask; if (ebits <= 7) { /* it fits entirely in the first byte */ mask = (0xff << (7 - ebits)) & 0x7f; return (int)(buf[bytes-1] & mask) >> (7 - ebits); } else if (ebits <= 15) { /* it fits in the first two bytes */ int e = (int)(buf[bytes-1] & 0x7f) << (ebits - 7); mask = (0xff << (15 - ebits)) & 0xff; return e | (int)(buf[bytes-2] & mask) >> (15 - ebits); } else { /* we don't currently handle anything above quad precision */ assert(FALSE); return 0; } } /* overall bit size (16, 32, 64, or 128) */ int bits; /* overall byte length */ int bytes; /* mantissa size in bits */ int mbits; /* exponent size in bits */ int ebits; /* minimum and maximum exponent values, and exponent bias */ int emin, emax; int ebias; /* our buffer - the caller provides this */ unsigned char *buf; }; /* ------------------------------------------------------------------------ */ /* * Convert to IEEE 754-2008 binary interchange format. 'bits' is the * number of bits in the format; we accept all of the standard format sizes * (16, 32, 64, 128). Our output conforms to the IEEE standard for the * format, except that we generate the buffer in the reverse byte order; * this is for consistency with the idiosyncratic TADS pack/unpack formats, * which use little-endian ordering for consistency with the standard TADS * integer interchange formats. To get the fully standard format, simply * reverse the byte order of the buffer we return. */ void CVmObjBigNum::convert_to_ieee754(VMG_ char *buf, int bits, int &ov) { /* no overflow yet */ ov = FALSE; /* set up the buffer descriptor */ ieee754 val(buf, bits); /* * Check for zero, infinity, and NAN. These all have special * representations that don't follow from the arithmetic algorithm * below. */ if (is_zero(ext_)) { val.set_zero(get_neg(ext_)); return; } else if (is_infinity(ext_)) { val.set_infinity(get_neg(ext_)); return; } else if (is_nan(ext_)) { val.set_nan(); return; } /* * Figure the precision we need for our intermediate calculations. Our * most demanding calculation is the log2 of the value; we need to be * able to represent the full precision of the binary type in the * *fractional* part of the log2 result. The log2 result can have up * to the maximum exponent as the whole part, so the precision we need * for this calculation is (decimal digits of mantissa) + (decimal * digits of exponent). Add a couple of guard digits as usual so that * we don't lose significant precision in rounding in intermediate * calculations. */ int prec = val.decimal_digits() + val.decimal_exp_digits() + 2; /* * Set up some registers for intermediate calculations. We don't have * to allocate these since we know the upper bound to the precision * we'll need (namely 35+2 for the 128-bit type). */ char t1[5+37] = { (char)prec, 0, 0, 0, 0 }; char t2buf[5+37] = { (char)prec, 0, 0, 0, 0 }, *t2 = t2buf; char t3buf[5+37] = { (char)prec, 0, 0, 0, 0 }, *t3 = t3buf; /* * The target format represents the number as A*2^B. Call our value N. * Observe that N == 2^(log2(N)) for any N. Let's separate out log2(N) * into its whole and fractional parts - call them W and F: so log2(N) * == W+F, which makes N == 2^(W+F) == 2^W * 2^F == 2^F * 2^W. Recall * that the format we're looking for is A*2^B, which we now have: A is * 2^F, and B is W. * * That will give us *a* binary representation of the number, but not * necessarily the canonical representation: we have to normalize by * choosing the exponent such that the implied (but not stored) first * bit of the mantissa is 1. The ln2 calculation will get us close; we * then need to adjust the exponent once we start pulling out the bits * of the mantissa. */ /* get/cache ln2 to the required precision */ const char *ln2 = cache_ln2(prec); /* * Get the absolute value of the argument. Our format and the IEEE * format both represent the value as a combination of an absolute * value and a sign, so the only thing we need to do with the sign is * to set the same sign on the result as on the input. For the log * calculation, though, we need to be working with a positive value. */ copy_val(t2, ext_, TRUE); set_neg(t2, FALSE); /* calculate t1=ln(self), t2=t1/ln(2) == ln2(self) */ compute_ln_into(t1, t2); compute_quotient_into(t2, 0, t1, ln2); /* * Get the whole part as an integer - this is the "B" value as above. * Note that this will definitely fit in a 32-bit integer type: * BigNumber stores absolute values in the range 10^-32768 to 10^32767, * so log2(self) can range from about -108000 to +108000. */ copy_val(t1, t2, FALSE); compute_whole(t1); int32_t b = convert_to_int_base(t1, ov); if (get_neg(t1)) b = -b; /* * Calculate 2^fractional part - this is the "A" value as above. 2^y = * e^(y*ln2), so calculate t2*ln2 into t3, then e^t3 back into t2. */ compute_frac(t2); compute_prod_into(t1, t2, ln2); compute_exp_into(t2, t1); /* * If 'b' is less than emin-1 (the subnormal exponent value), we need * to raise it back up to emin-1. Do this by halving the mantissa and * incrementing 'b', repeating until 'b' is emin-1. * * If 'b' is less than emin-1 by more than the bit precision of the * target type, we won't have any bits to store, so store zero. */ if (b < val.emin - 1 - val.mbits) { val.set_zero(get_neg(ext_)); return; } while (b < val.emin - 1) { div_by_long(t2, 2); b += 1; } /* * Extract the bits of the mantissa. Do this by repeatedly shifting * and subtracting: starting with divisor = 1, if decimal mantissa >= * divisor, subtract divisor and set the current bit in the output to * 1, otherwise set the output bit to 0; set divisor = divisor/2, * repeat until we've filled the output mantissa. A is 2^fraction, * where -1 < fraction < 1, so 0.5 < A < 2. * * Normalization requires that the output mantissa start with an * implied 1. This means that we ignore leading zeros; for each * leading zero, we decrement b but don't set an output bit. When we * reach the first 1 bit, we don't store it, either, since it's implied * by normalization. However, if we reach emin-1 for 'b', we're in the * subnormalization range, which means we can't decrement the exponent * any further; instead, start storing leading zeros. (Subnormal * means that the number is stored with an implied *0* leading bit, and * the special stored exponent value of emin-1, which implies an actual * exponent of emin.) */ copy_val(t1, (const char *)one_, FALSE); int mbit, sig; for (mbit = 0, sig = FALSE ; mbit < val.mbits ; div_by_long(t1, 2)) { /* the current bit is 1 if t2 >= t1 */ if (compare_abs(t2, t1) >= 0) { /* * it's a 1 bit - if it's the first 1 bit, note that we've * found a significant bit, but don't store it, since the first * bit is implied in the output format */ if (sig || b == val.emin - 1) val.set_mbit(mbit++, 1); else sig = TRUE; /* subtract t2 from t1 */ compute_abs_diff_into(t3, t2, t1); /* swap t3 and t2 to keep the accumulator named t2 */ char *ttmp = t2; t2 = t3; t3 = ttmp; } else { /* * It's a zero bit - store it if we've found a 1 already, OR * the exponent is already at the subnormal value. Otherwise * just decrement the exponent. */ if (sig || b == val.emin - 1) val.set_mbit(mbit++, 0); else --b; } } /* * Round the result: if the remainder is less than half of the last * bit's value (i.e., t2 < t1), round down (i.e., do nothing). If the * remainder is greater than half of the last bit's value (i.e. t2 > * t1), round up (add 1 to the low-order bit). If the remainder is * exactly half of the last bit's value, round up if the last bit was a * 1, down if it was a 0. */ int r = compare_abs(t2, t1); if (r > 0 || (r == 0 && val.get_mbit(val.mbits-1))) { /* round up; if that carries out, increment the exponent */ if (val.round_up()) ++b; } /* if 'b' wound up above the maximum for the format, it's an overflow */ if (b > val.emax) { val.set_infinity(get_neg(ext_)); ov = TRUE; return; } /* store the final exponent and sign */ val.set_exp(b); val.set_sign(get_neg(ext_)); } /* ------------------------------------------------------------------------ */ /* * Convert from IEEE 754-2008 format to BigNumber format. This takes an * IEEE 754-2008 binary interchange buffer in the given bit size (16, 32, * 64, or 128) and loads the value into the BigNumber. The buffer is in * the standard IEEE format, except that it's little endian, for * consistency with the TADS pack/unpack formats. To convert a buffer in * the fully standard IEEE format, simply reverse the byte order before * converting. */ void CVmObjBigNum::set_ieee754_value(VMG_ const char *buf, int bits) { /* set up the buffer descriptor */ ieee754 val((char *)buf, bits); /* check for special values */ if (val.is_zero()) { set_zero(ext_); set_neg(ext_, val.get_sign()); return; } else if (val.is_nan()) { set_type(ext_, VMBN_T_NAN); return; } else if (val.is_infinity()) { set_type(ext_, VMBN_T_INF); set_neg(ext_, val.get_sign()); return; } /* * figure the precision for our intermediate calculations based on the * precision of the source format in decimal digits */ int prec = val.decimal_digits() + 2; /* * Set up some registers for intermediate calculations. We don't have * to allocate these since we know the upper bound to the precision * we'll need (namely 35+2 for the 128-bit type). */ char t1[5+37] = { (char)prec, 0, 0, 0, 0 }; char t2buf[5+37] = { (char)prec, 0, 0, 0, 0 }, *t2 = t2buf; char t3buf[5+37] = { (char)prec, 0, 0, 0, 0 }, *t3 = t3buf; /* * The IEEE value is represented as A * 2^B, for some non-zero value of * A. If the exponent is emin-1, A is represented as 0.bbbbb, where * the b's are bits of the mantissa. For any other exponent, A is * represented as 1.bbbbb. To compute our value, then, start with 2^B * in register t1 and zero in the accumulator. If the leading bit is 1 * (exponent >= emin), add r1 to the accumulator. Divide t1 by 2 and * loop: if the current bit is set, add t1 to the accumulator. Repeat * for each bit of the mantissa. */ /* calculate t1 = 2^B = e^(B*ln2) */ const char *ln2 = cache_ln2(prec); int e = val.get_exp(); set_int_val(t1, e); compute_prod_into(t2, t1, ln2); compute_exp_into(t1, t2); /* * If b > emin-1, there's an implied 1 bit for the whole part. If b == * emin-1, we have a subnormal number, meaning that the implied lead * bit is 0, and the actual exponent is val.emin. */ if (e > val.emin - 1) { /* normalized - add the implied leading "1" bit */ copy_val(t3, t1, FALSE); } else { /* * subnormal - the implied leading bit is "0", but the actual * exponent value is emin - one higher than it appears */ set_zero(t3); mul_by_long(t1, 2); } /* add each bit of the mantissa */ for (int i = 0 ; i < val.mbits ; ++i) { /* divide the power-of-two multiplier by 2 for the next lower place */ div_by_long(t1, 2); /* if this mantissa bit is 1, add t1 to the accumulator */ if (val.get_mbit(i)) { /* t2 <- acc + power of two */ compute_sum_into(t2, t3, t1); /* swap t2 and t3 so that t3 is the accumulator again */ char *ttmp = t3; t3 = t2; t2 = ttmp; } } /* set the sign and normalize the value */ set_neg(t3, val.get_sign()); normalize(t3); /* copy the result into our extension */ copy_val(ext_, t3, TRUE); } /* ------------------------------------------------------------------------ */ /* * Create a string representation of the number */ const char *CVmObjBigNum::cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const { /* use my static method */ return cvt_to_string(vmg_ self, new_str, ext_, 100, -1, -1, -1, 0, 0); } /* * Convert to string with a given radix */ const char *CVmObjBigNum::explicit_to_string( VMG_ vm_obj_id_t self, vm_val_t *new_str, int radix, int flags) const { /* * If they want a non-decimal conversion, and we're a whole integer * without a fractional part, generate the integer representation in * the given radix. If we have a fractional part, or the requested * radix is decimal, use the floating-point formatter instead. * * Also use an integer conversion if we've been specfically asked for * an integer conversion. */ if ((radix != 10 && is_frac_zero(ext_)) || (flags & TOSTR_ROUND) != 0) { /* * it's all integer, and we have a non-decimal radix - format as an * integer */ return cvt_to_string_in_radix(vmg_ self, new_str, radix); } else { /* * no radix, or we have a fraction - use the basic floating-point * formatter with default options */ return cvt_to_string(vmg_ self, new_str, ext_, 100, -1, -1, -1, 0, 0); } } /* ------------------------------------------------------------------------ */ /* * String buffer allocator */ char *CBigNumStringBufAlo::get_buf(size_t need) { /* use the fixed buffer if it's big enough */ if (len >= need) return buf; /* the fixed buffer isn't big enough, so allocate a new string */ if (strval != 0) { VMGLOB_PTR(vmg); return CVmObjString::alloc_str_buf(vmg_ strval, 0, 0, need); } /* the buffer's too small, and we can't allocate, so fail */ return 0; } /* ------------------------------------------------------------------------ */ /* * convert to a string, storing the result in the given buffer if it'll * fit, otherwise in a new string */ const char *CVmObjBigNum::cvt_to_string_buf( VMG_ vm_val_t *new_str, char *buf, size_t buflen, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags) { /* set up an allocator */ CBigNumStringBufAlo alo(vmg_ new_str, buf, buflen); /* convert to a string into our buffer */ return cvt_to_string_gen( &alo, ext_, max_digits, whole_places, frac_digits, exp_digits, flags, 0, 0); } /* ------------------------------------------------------------------------ */ /* * Convert to a string, creating a new string object to hold the result */ const char *CVmObjBigNum::cvt_to_string( VMG_ vm_obj_id_t self, vm_val_t *new_str, const char *ext, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags, vm_val_t *lead_fill) { /* push a self-reference for gc protection during allocation */ G_stk->push()->set_obj(self); /* set up an allocator */ CBigNumStringBufAlo alo(vmg_ new_str); /* if there's a lead fill parameter, get its string value */ const char *lead_fill_str = 0; size_t lead_fill_len = 0; if (lead_fill != 0 && lead_fill->typ != VM_NIL) { /* get the string value */ lead_fill_str = lead_fill->get_as_string(vmg0_); /* if it's not a string, it's an error */ if (lead_fill_str == 0) err_throw(VMERR_STRING_VAL_REQD); /* read and skip the length prefix */ lead_fill_len = vmb_get_len(lead_fill_str); lead_fill_str += VMB_LEN; /* if the length is zero, ignore the lead fill string entirely */ if (lead_fill_len == 0) lead_fill_str = 0; } /* * convert to a string - don't pass in a buffer, since we want to * create a new string to hold the result */ const char *ret = cvt_to_string_gen( &alo, ext, max_digits, whole_places, frac_digits, exp_digits, flags, lead_fill_str, lead_fill_len); /* discard our gc protection */ G_stk->discard(); /* return the result */ return ret; } /* ------------------------------------------------------------------------ */ /* * Convert an integer value to a string in the given radix (2-36) */ const char *CVmObjBigNum::cvt_to_string_in_radix( VMG_ vm_obj_id_t self, vm_val_t *new_str, int radix) const { /* check for special values: zeros, NaNs, and infinities */ const char *fixed = 0; int neg = get_neg(ext_); if (is_zero(ext_)) { /* show zero as "0" in all bases */ fixed = "0"; } else if (is_infinity(ext_)) { /* * show infinity as "1.#INF" for positive or unsigned, "-1.#INF" * for negative infinity */ fixed = (neg ? "-1.#INF" : "1.#INF"); } else if (is_nan(ext_)) { /* not a number */ fixed = "1.#NAN"; } else { /* make a temporary copy of the number */ uint thdl; char *tmp = alloc_temp_reg(get_prec(ext_), &thdl); err_try { /* copy the value into our temporary register */ copy_val(tmp, ext_, FALSE); /* if there's a fractional part, round to integer */ round_to_int(tmp); /* * It's an ordinary non-zero number. Figure how many digits * the integer portion will take in the given radix. This is * fairly simple: a D digit number in decimal requires * D/log10(R) digits in base R, rounding up. Our storage * format makes it easy to determine D - it's simply our * base-10 exponent. */ int len = (int)ceil(get_exp(ext_) / log10((double)radix)); /* if the value is zero, we need one digit for the zero */ if (len == 0) len = 1; /* if it's negative, add space for the '-' */ if (neg) len += 1; /* create the string */ new_str->set_obj(CVmObjString::create(vmg_ FALSE, len)); CVmObjString *str = (CVmObjString *)vm_objp(vmg_ new_str->val.obj); char *buf = str->cons_get_buf(), *p = buf; /* add the '-' sign if negative */ if (neg) { *p++ = '-'; ++buf; --len; } /* generate the digits */ while (!is_zero(tmp) && p - buf < len) { /* get the next digit */ unsigned long rem; div_by_long(tmp, radix, &rem); /* add it to the output */ *p++ = (char)(rem < 10 ? rem + '0' : rem - 10 + 'A'); } /* * We just generated the digits in reverse order. Reverse the * string so that they're in the right order. */ char *p1, *p2, tmp; for (p1 = buf, p2 = p - 1 ; p2 > p1 ; --p2, ++p1) tmp = *p1, *p1 = *p2, *p2 = tmp; /* if we generated no digits, generate a zero */ if (p == buf) *p++ = '0'; /* set the final length, in case rounding left us off by one */ str->cons_set_len(p - str->cons_get_buf()); } err_finally { /* release our temporary register */ release_temp_reg(thdl); } err_end; } /* if we came up with a fixed source string, create the string object */ if (fixed != 0) new_str->set_obj(CVmObjString::create( vmg_ FALSE, fixed, strlen(fixed))); /* return the new string buffer */ return new_str->get_as_string(vmg0_); } /* ------------------------------------------------------------------------ */ /* * save to a file */ void CVmObjBigNum::save_to_file(VMG_ class CVmFile *fp) { size_t prec; /* get our precision */ prec = get_prec(ext_); /* write the data */ fp->write_bytes(ext_, calc_alloc(prec)); } /* * restore from a file */ void CVmObjBigNum::restore_from_file(VMG_ vm_obj_id_t, CVmFile *fp, CVmObjFixup *) { size_t prec; /* read the precision */ prec = fp->read_uint2(); /* free any existing extension */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* allocate the space */ alloc_bignum(vmg_ prec); /* store our precision */ set_prec(ext_, prec); /* read the contents */ fp->read_bytes(ext_ + VMBN_EXP, calc_alloc(prec) - VMBN_EXP); } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjBigNum::set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { /* we have no properties to set */ err_throw(VMERR_INVALID_SETPROP); } /* * get a static property */ int CVmObjBigNum::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* translate the property into a function vector index */ switch(G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop)) { case VMOBJBN_GET_PI: return s_getp_pi(vmg_ result, argc); case VMOBJBN_GET_E: return s_getp_e(vmg_ result, argc); default: /* * we don't define this one - inherit the default from the base * object metaclass */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } } /* * get a property */ int CVmObjBigNum::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the function */ if ((this->*func_table_[func_idx])(vmg_ self, val, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling */ return CVmObject::get_prop(vmg_ prop, val, self, source_obj, argc); } /* * Property evaluator - formatString */ int CVmObjBigNum::getp_format(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { int orig_argc = (argc != 0 ? *argc : 0); int max_digits = -1; uint flags = 0; int whole_places = -1; int frac_digits = -1; int exp_digits = -1; vm_val_t *lead_fill = 0; static CVmNativeCodeDesc desc(0, 6); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the maximum digit count */ if (orig_argc >= 1) max_digits = CVmBif::pop_int_or_nil(vmg_ -1); /* get the flags */ if (orig_argc >= 2) flags = CVmBif::pop_int_or_nil(vmg_ 0); /* get the whole places */ if (orig_argc >= 3) whole_places = CVmBif::pop_int_or_nil(vmg_ -1); /* get the fraction digits */ if (orig_argc >= 4) frac_digits = CVmBif::pop_int_or_nil(vmg_ -1); /* get the exponent digits */ if (orig_argc >= 5) exp_digits = CVmBif::pop_int_or_nil(vmg_ -1); /* * get the lead fill string if provided (leave it on the stack to * protect against garbage collection) */ if (orig_argc >= 6) lead_fill = G_stk->get(0); /* format the number, which will build the return string */ cvt_to_string(vmg_ self, retval, ext_, max_digits, whole_places, frac_digits, exp_digits, flags, lead_fill); /* discard the lead fill string, if provided */ if (lead_fill != 0) G_stk->discard(); /* handled */ return TRUE; } /* * Property eval - equal after rounding */ int CVmObjBigNum::getp_equal_rnd(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_val_t val2; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the value to compare */ G_stk->pop(&val2); /* convert it to BigNumber */ if (!cvt_to_bignum(vmg_ self, &val2)) { /* it's not a BigNumber, so it's not equal */ retval->set_nil(); } else { int ret; /* test for equality */ ret = compute_eq_round(vmg_ ext_, get_objid_ext(vmg_ val2.val.obj)); /* set the return value */ retval->set_logical(ret); } /* handled */ return TRUE; } /* * property eval - get the precision */ int CVmObjBigNum::getp_get_prec(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* return an integer giving my precision */ retval->set_int(get_prec(ext_)); /* handled */ return TRUE; } /* * property eval - set the precision */ int CVmObjBigNum::getp_set_prec(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { size_t digits; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the number of digits for rounding */ digits = CVmBif::pop_int_val(vmg0_); /* if I'm not a number, return myself unchanged */ if (is_nan(ext_)) { retval->set_obj(self); return TRUE; } /* push a self-reference while we're working */ G_stk->push()->set_obj(self); /* create the rounded value */ round_val(vmg_ retval, ext_, digits, TRUE); /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * get pi (static method) */ int CVmObjBigNum::s_getp_pi(VMG_ vm_val_t *val, uint *argc) { size_t prec; char *new_ext; const char *pi; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(val, argc, &desc)) return TRUE; /* get the precision argument */ prec = CVmBif::pop_int_val(vmg0_); /* allocate the result */ val->set_obj(create(vmg_ FALSE, prec)); new_ext = get_objid_ext(vmg_ val->val.obj); /* cache pi to the required precision */ pi = cache_pi(prec); /* return the value */ copy_val(new_ext, pi, TRUE); /* handled */ return TRUE; } /* * get e (static method) */ int CVmObjBigNum::s_getp_e(VMG_ vm_val_t *val, uint *argc) { size_t prec; char *new_ext; const char *e; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(val, argc, &desc)) return TRUE; /* get the precision argument */ prec = CVmBif::pop_int_val(vmg0_); /* allocate the result */ val->set_obj(create(vmg_ FALSE, prec)); new_ext = get_objid_ext(vmg_ val->val.obj); /* cache e to the required precision */ e = cache_e(prec); /* return the value */ copy_val(new_ext, e, TRUE); /* handled */ return TRUE; } /* * Set up for a zero-argument operation that returns a BigNumber result. * Returns true if the argument check indicates that the caller should * simply return to its caller, false if the caller should proceed. * * After checking the argument count, we'll proceed to set up the return * value as per setup_getp_retval(). */ int CVmObjBigNum::setup_getp_0(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, char **new_ext) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* set up the return value */ return setup_getp_retval(vmg_ self, retval, new_ext, get_prec(ext_)); } /* * Set up for a one-argument operation that takes a BigNumber value as * the argument and returns a BigNumber result. Does the work of * setup_getp_0, but also pops the argument value and converts it to a * BigNumber (throwing an error if the value is not convertible). * * Fills in val2 with the argument value, and fills in *ext2 with val2's * extension buffer. * * The result value will have the larger of the precisions of self and * the other value, unless use_self_prec is set, in which case we'll use * self's precision unconditionally. * * If either argument is not a number, we'll set the return value to the * not-a-number argument unchanged, and return true. */ int CVmObjBigNum::setup_getp_1(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, char **new_ext, vm_val_t *val2, const char **ext2, int use_self_prec) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the argument value */ G_stk->pop(val2); /* convert it to BigNumber */ if (!cvt_to_bignum(vmg_ self, val2)) { /* it's not a BigNumber - throw an error */ err_throw(VMERR_BAD_TYPE_BIF); } /* get the other value's extension */ *ext2 = get_objid_ext(vmg_ val2->val.obj); /* if the other value is not a number, return it as the result */ if (is_nan(*ext2)) { retval->set_obj(val2->val.obj); return TRUE; } /* * if val2's precision is higher than ours, use it, unless we've * been specifically told to use our own precision for the result */ size_t prec = get_prec(ext_); if (!use_self_prec && get_prec(*ext2) > prec) prec = get_prec(*ext2); /* push the other result to protect it from garbage collection */ G_stk->push(val2); /* allocate the return value */ if (setup_getp_retval(vmg_ self, retval, new_ext, prec)) { /* discard the val2 reference we pushed for gc protection */ G_stk->discard(); /* tell the caller we're done */ return TRUE; } /* tell the caller to proceed */ return FALSE; } /* * Set up for an operation that returns a BigNumber result. Returns * true if we finished the operation, in which case the caller should * simply return; returns false if the operation should still be carried * out, in which case the caller should proceed as normal. * * If the 'self' value is not-a-number, we'll return it as the result * (and return true to indicate that no further processing is required). * * If we return false, we'll have pushed a reference to 'self' onto the * stack for protection against garbage collection. The caller must * discard this reference before returning. We push no such * self-reference if we return true. * * In addition, if we return false, we'll fill in '*new_ext' with a * pointer to the extension buffer for the return value object that we * allocate. We'll allocate the return value with the same precision as * 'self'. * * Note that the caller should ensure that any argument sare removed * from the stack before calling this routine, since we might push the * self-reference onto the stack. */ int CVmObjBigNum::setup_getp_retval(VMG_ vm_obj_id_t self, vm_val_t *retval, char **new_ext, size_t prec) { /* if I'm not a number, return myself unchanged */ if (is_nan(ext_)) { retval->set_obj(self); return TRUE; } /* push a self-reference while we're working */ G_stk->push()->set_obj(self); /* create a new number with the same precision as the original */ retval->set_obj(create(vmg_ FALSE, prec)); *new_ext = get_objid_ext(vmg_ retval->val.obj); /* tell the caller to proceed */ return FALSE; } /* * property eval - get the fractional part */ int CVmObjBigNum::getp_frac(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; /* check arguments and allocate the result value */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* make a copy in the new object, and get the fractional portion */ memcpy(new_ext, ext_, calc_alloc(get_prec(ext_))); compute_frac(new_ext); /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property eval - get the whole part, with no rounding */ int CVmObjBigNum::getp_whole(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; /* check arguments and allocate the result value */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* make a copy in the new object, and get the whole part */ memcpy(new_ext, ext_, calc_alloc(get_prec(ext_))); compute_whole(new_ext); /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property eval - round to a given number of decimal places */ int CVmObjBigNum::getp_round_dec(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { int places; char *new_ext; int post_idx; int exp = get_exp(ext_); size_t prec = get_prec(ext_); static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the number of digits for rounding */ places = CVmBif::pop_int_val(vmg0_); /* set up the return value */ if (setup_getp_retval(vmg_ self, retval, &new_ext, prec)) return TRUE; /* make a copy in the new object */ memcpy(new_ext, ext_, calc_alloc(prec)); /* * Determine if the first digit we're lopping off is actually * represented in the number or not. This digit position is the * exponent plus the number of decimal places after the decimal to * keep - if this value is at least zero and less than the * precision, it's part of the number. */ post_idx = places + exp; if (post_idx < 0) { /* * the digit after the last digit to keep is actually before the * beginning of the number, so the result of the rounding is * simply zero */ set_zero(new_ext); } else if (post_idx >= (int)prec) { /* * the digit after the last digit to keep is past the end of the * represented digits, so rounding has no effect at all - we'll * simply return the same number */ } else { /* round it */ round_to(new_ext, post_idx); /* normalize the result */ normalize(new_ext); } /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property eval - get the absolute value */ int CVmObjBigNum::getp_abs(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* compute the absolute value */ abs_val(vmg_ retval, self); /* handled */ return TRUE; } /* * absolute value */ int CVmObjBigNum::abs_val(VMG_ vm_val_t *retval, vm_obj_id_t self) { /* * If I'm not an ordinary number or an infinity, or I'm already * non-negative, return myself unchanged. Note that we change negative * infinity to infinity, even though this might not make a great deal * of sense mathematically. */ if (!get_neg(ext_) || (get_type(ext_) != VMBN_T_NUM && get_type(ext_) != VMBN_T_INF)) { retval->set_obj(self); return TRUE; } /* push a self-reference while we're working */ G_stk->push()->set_obj(self); /* * if I'm negative infinity, we don't need any precision in the new * value; otherwise use my original precision */ size_t prec = (is_infinity(ext_) ? 1 : get_prec(ext_)); /* create a new number with the same precision as the original */ retval->set_obj(create(vmg_ FALSE, prec)); char *new_ext = get_objid_ext(vmg_ retval->val.obj); /* make a copy in the new object */ memcpy(new_ext, ext_, calc_alloc(prec)); /* set the sign to positive */ set_neg(new_ext, FALSE); /* remove my self-reference */ G_stk->discard(); /* success */ return TRUE; } /* * compute the sgn value */ int CVmObjBigNum::sgn_val(VMG_ vm_val_t *retval, vm_obj_id_t self) { /* figure the sgn value */ if (is_zero(ext_)) retval->set_int(0); else if (get_neg(ext_)) retval->set_int(-1); else retval->set_int(1); /* success */ return TRUE; } /* * property eval - ceiling (least integer greater than or equal to self) */ int CVmObjBigNum::getp_ceil(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t idx; int exp = get_exp(ext_); size_t prec = get_prec(ext_); int frac_zero; static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* check arguments and allocate the result value */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* make a copy in the new object */ memcpy(new_ext, ext_, calc_alloc(prec)); /* determine if the fractional part is non-zero */ frac_zero = is_frac_zero(new_ext); /* check what we have */ if (exp <= 0) { /* * it's an entirely fractional number - the result is zero if * the number is negative or zero, one if the number is positive */ if (get_neg(new_ext) || frac_zero) { /* -1 < x <= 0 -> ceil(x) = 0 */ set_zero(new_ext); } else { /* 0 < x < 1 -> ceil(x) = 1 */ copy_val(new_ext, get_one(), FALSE); } } else { /* clear digits after the decimal point */ for (idx = (size_t)exp ; idx < prec ; ++idx) set_dig(new_ext, idx, 0); /* * if there's a fractional part and it's positive, increment the * number */ if (!frac_zero && !get_neg(new_ext)) increment_abs(new_ext); } /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property eval - floor (greatest integer <= self) */ int CVmObjBigNum::getp_floor(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t idx; int exp = get_exp(ext_); size_t prec = get_prec(ext_); int frac_zero; /* check arguments and allocate the result value */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* make a copy in the new object */ memcpy(new_ext, ext_, calc_alloc(prec)); /* determine if the fractional part is non-zero */ frac_zero = is_frac_zero(new_ext); /* check what we have */ if (exp <= 0) { /* * it's an entirely fractional number - the result is zero if * the number is positive or zero, -1 if the number is negative */ if (!get_neg(new_ext) || frac_zero) { /* 0 <= x < 1 -> floor(x) = 0 */ set_zero(new_ext); } else { /* -1 < x < 0 -> floor(x) = -1 */ copy_val(new_ext, get_one(), FALSE); set_neg(new_ext, TRUE); } } else { /* clear digits after the decimal point */ for (idx = (size_t)exp ; idx < prec ; ++idx) set_dig(new_ext, idx, 0); /* * if there's a fractional part and the number is negative, * increment the number's absolute value */ if (!frac_zero && get_neg(new_ext)) increment_abs(new_ext); } /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property eval - getScale */ int CVmObjBigNum::getp_get_scale(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* check the type */ if (is_nan(ext_)) { /* it's not a number - return nil for the scale */ retval->set_nil(); } else { /* return an integer giving the number's scale */ retval->set_int(get_exp(ext_)); } /* handled */ return TRUE; } /* * property eval - scale */ int CVmObjBigNum::getp_scale(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t prec = get_prec(ext_); int scale; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the scaling argument */ scale = CVmBif::pop_int_val(vmg0_); /* set up the return value */ if (setup_getp_retval(vmg_ self, retval, &new_ext, prec)) return TRUE; /* copy the value */ memcpy(new_ext, ext_, calc_alloc(prec)); /* adjust the exponent by the scale factor */ set_exp(new_ext, get_exp(new_ext) + scale); /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* * property eval - negate */ int CVmObjBigNum::getp_negate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* negate the value */ neg_val(vmg_ retval, self); /* handled */ return TRUE; } /* * property eval - copy sign */ int CVmObjBigNum::getp_copy_sign(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_val_t val2; char *new_ext; const char *ext2; size_t prec = get_prec(ext_); if (setup_getp_1(vmg_ self, retval, argc, &new_ext, &val2, &ext2, TRUE)) return TRUE; /* make a copy of my value in the new object */ memcpy(new_ext, ext_, calc_alloc(prec)); /* set the sign from the other object */ set_neg(new_ext, get_neg(ext2)); /* * normalize it (this is important when the value was zero to start * with, since zero is always represented without a negative sign) */ normalize(new_ext); /* remove the GC protection */ G_stk->discard(2); /* handled */ return TRUE; } /* * property eval - isNegative */ int CVmObjBigNum::getp_is_neg(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* if I'm not an ordinary number or an infinity, I'm not negative */ if (get_type(ext_) != VMBN_T_NUM && get_type(ext_) != VMBN_T_INF) { /* I'm not negative, so return nil */ retval->set_nil(); } else { /* set the return value to true if I'm negative, nil if not */ retval->set_logical(get_neg(ext_)); } /* handled */ return TRUE; } /* * property eval - remainder */ int CVmObjBigNum::getp_remainder(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_val_t val2; const char *ext2; char *quo_ext; char *rem_ext; vm_val_t rem_val; vm_val_t quo_val; CVmObjList *lst; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the divisor */ G_stk->pop(&val2); /* convert it to BigNumber */ if (!cvt_to_bignum(vmg_ self, &val2)) { /* it's not a BigNumber - throw an error */ err_throw(VMERR_BAD_TYPE_BIF); } /* get the divisor's extension */ ext2 = get_objid_ext(vmg_ val2.val.obj); /* push 'self' and the other value to protect against GC */ G_stk->push()->set_obj(self); G_stk->push(&val2); /* create a quotient result value, and push it for safekeeping */ quo_ext = compute_init_2op(vmg_ &quo_val, ext_, ext2); G_stk->push(&quo_val); /* create a remainder result value, and push it for safekeeping */ rem_ext = compute_init_2op(vmg_ &rem_val, ext_, ext2); G_stk->push(&rem_val); /* * create a list for the return value - it will have two elements: * the quotient and the remainder */ retval->set_obj(CVmObjList::create(vmg_ FALSE, 2)); lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* set the list elements */ lst->cons_set_element(0, &quo_val); lst->cons_set_element(1, &rem_val); /* calculate the quotient */ compute_quotient_into(quo_ext, rem_ext, ext_, ext2); /* remove the GC protection */ G_stk->discard(4); /* handled */ return TRUE; } /* * property eval - sine */ int CVmObjBigNum::getp_sin(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t prec = get_prec(ext_); uint hdl1, hdl2, hdl3, hdl4, hdl5, hdl6, hdl7; char *ext1, *ext2, *ext3, *ext4, *ext5, *ext6, *ext7; int neg_result; const char *pi; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* cache pi */ pi = cache_pi(prec + 3); /* * Allocate our temporary registers. We'll use 1 and 2 to calculate * x^n - we'll start with x^(n-2) in one, and multiply by x^2 to put * the result in the other. 3 we'll use to store n!. 4 we'll use * to store the result of x^n/n!, and 5 and 6 we'll swap as the * master accumulator. 7 we'll use to store x^2. * * Allocate the temporary registers with more digits of precision * than we need in the result, to ensure that accumulated rounding * errors don't affect the result. */ alloc_temp_regs(prec + 3, 7, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5, &ext6, &hdl6, &ext7, &hdl7); /* protect against errors so we definitely free the registers */ err_try { char *tmp; /* copy our initial value to ext1 */ copy_val(ext1, ext_, FALSE); /* * Note that sin(-x) = -sin(x). If x < 0, note the negative * sign and then negate the value so that we calculate sin(x) * and return the negative of the result. */ neg_result = get_neg(ext1); set_neg(ext1, FALSE); /* calculate 2*pi */ copy_val(ext7, pi, TRUE); mul_by_long(ext7, 2); /* * Because we'll use a Taylor series around 0 to calculate the * result, we want our value as close to the expansion point (0) * as possible to speed up convergence of the series. * Fortunately this is especially easy for sin() because of the * periodic nature of the function. * * First, note that sin(2*pi*i + x) = sin(x) for all integers i, * so we can reduce the argument mod 2*pi until it's in the * range 0 <= x < 2*pi (we might have to do this multiple times * if the number's scale exceeds its precision). Note that we * already made sure the number is positive. */ while (compare_abs(ext1, ext7) > 0) { /* divide by 2*pi, storing the remainder in r2 */ compute_quotient_into(ext6, ext2, ext1, ext7); /* swap r2 into r1 for the next round */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * Next, note that sin(x+pi) = -sin(x). If x > pi, negate the * result (again if necessary) and reduce the argument by pi. * This will reduce our range to 0 <= x <= pi. */ copy_val(ext7, pi, TRUE); if (compare_abs(ext1, ext7) > 0) { /* negate the result */ neg_result = !neg_result; /* subtract pi from the argument */ compute_abs_diff_into(ext2, ext1, ext7); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * Use the fact that sin(x + pi) = -sin(x) once again: if x > * pi/2, subtract pi from x to adjust the range to -pi/2 <= x <= * pi/2. */ div_by_long(ext7, 2); if (compare_abs(ext1, ext7) > 0) { /* negate the result */ neg_result = !neg_result; /* subtract pi from the argument */ copy_val(ext7, pi, TRUE); compute_abs_diff_into(ext2, ext1, ext7); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * once again, reduce our range using the sign equivalence - * this will limit our range to 0 <= x <= pi/2 */ if (get_neg(ext1)) neg_result = !neg_result; set_neg(ext1, FALSE); /* * Next, observe that sin(x+pi/2) = cos(x). If x > pi/4, * calculate using the cosine series instead of the sine series. */ copy_val(ext7, pi, TRUE); div_by_long(ext7, 4); if (compare_abs(ext1, ext7) > 0) { /* calculate pi/2 */ copy_val(ext7, pi, TRUE); div_by_long(ext7, 2); /* * subtract pi/2 - this will give us a value in the range * -pi/4 <= x <= pi/4 */ compute_abs_diff_into(ext2, ext1, ext7); /* cos(-x) = cos(x), so we can ignore the sign */ set_neg(ext1, FALSE); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; /* calculate the cosine series */ calc_cos_series(vmg_ new_ext, ext1, ext2, ext3, ext4, ext5, ext6, ext7); } else { /* * We now have a value in the range 0 <= x <= pi/4, which * will converge quickly with our Taylor series */ calc_sin_series(vmg_ new_ext, ext1, ext2, ext3, ext4, ext5, ext6, ext7); } /* negate the result if necessary */ if (neg_result) set_neg(new_ext, !get_neg(new_ext)); /* normalize the result */ normalize(new_ext); } err_finally { /* release our temporary registers */ release_temp_regs(7, hdl1, hdl2, hdl3, hdl4, hdl5, hdl6, hdl7); } err_end; /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property eval - cosine. This works very much the same way as * getp_sin() - refer to the more extensive comments in that routine for * more detail on the algorithm. */ int CVmObjBigNum::getp_cos(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t prec = get_prec(ext_); uint hdl1, hdl2, hdl3, hdl4, hdl5, hdl6, hdl7; char *ext1, *ext2, *ext3, *ext4, *ext5, *ext6, *ext7; int neg_result; const char *pi; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* cache pi to our working precision */ pi = cache_pi(prec + 3); /* allocate our temporary registers, as per sin() */ alloc_temp_regs(prec + 3, 7, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5, &ext6, &hdl6, &ext7, &hdl7); /* protect against errors so we definitely free the registers */ err_try { char *tmp; /* presume the result sign will be correct */ neg_result = FALSE; /* copy our initial value to ext1 */ copy_val(ext1, ext_, FALSE); /* note that cos(-x) = cos(x) - if x < 0, use -x */ set_neg(ext1, FALSE); /* reduce the argument modulo 2*pi */ copy_val(ext7, pi, TRUE); mul_by_long(ext7, 2); while (compare_abs(ext1, ext7) > 0) { /* divide by 2*pi, storing the remainder in r2 */ compute_quotient_into(ext6, ext2, ext1, ext7); /* swap r2 into r1 for the next round */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * Next, note that cos(x+pi) = -cos(x). If x > pi, negate the * result (again if necessary) and reduce the argument by pi. * This will reduce our range to 0 <= x <= pi. */ copy_val(ext7, pi, TRUE); if (compare_abs(ext1, ext7) > 0) { /* negate the result */ neg_result = !neg_result; /* subtract pi from the argument */ compute_abs_diff_into(ext2, ext1, ext7); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * Use the fact that cos(x + pi) = -cos(x) once again: if x > * pi/2, subtract pi from x to adjust the range to -pi/2 <= x <= * pi/2. */ div_by_long(ext7, 2); if (compare_abs(ext1, ext7) > 0) { /* negate the result */ neg_result = !neg_result; /* subtract pi from the argument */ copy_val(ext7, pi, TRUE); compute_abs_diff_into(ext2, ext1, ext7); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * once again, reduce our range using the sign equivalence - * this will limit our range to 0 <= x <= pi/2 */ set_neg(ext1, FALSE); /* * Next, observe that cos(x+pi/2) = -sin(x). If x > pi/4, * calculate using the sine series instead of the cosine series. */ copy_val(ext7, pi, TRUE); div_by_long(ext7, 4); if (compare_abs(ext1, ext7) > 0) { /* negate the result */ neg_result = !neg_result; /* calculate pi/2 */ copy_val(ext7, pi, TRUE); div_by_long(ext7, 2); /* * subtract pi/2 - this will give us a value in the range * -pi/4 <= x <= pi/4 */ compute_abs_diff_into(ext2, ext1, ext7); /* sin(-x) = -sin(x) */ if (get_neg(ext1)) neg_result = !neg_result; set_neg(ext1, FALSE); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; /* calculate the sine series */ calc_sin_series(vmg_ new_ext, ext1, ext2, ext3, ext4, ext5, ext6, ext7); } else { /* * We now have a value in the range 0 <= x <= pi/4, which * will converge quickly with our Taylor series */ calc_cos_series(vmg_ new_ext, ext1, ext2, ext3, ext4, ext5, ext6, ext7); } /* negate the result if necessary */ if (neg_result) set_neg(new_ext, !get_neg(new_ext)); /* normalize the result */ normalize(new_ext); } err_finally { /* release our temporary registers */ release_temp_regs(7, hdl1, hdl2, hdl3, hdl4, hdl5, hdl6, hdl7); } err_end; /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property eval - tangent. We calculate the sine and cosine, then * compute the quotient to determine the tangent. This routine works * very much like the sin() and cos() routines; refer to getp_sin() for * more thorough documentation. */ int CVmObjBigNum::getp_tan(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t prec = get_prec(ext_); uint hdl1, hdl2, hdl3, hdl4, hdl5, hdl6, hdl7, hdl8, hdl9; char *ext1, *ext2, *ext3, *ext4, *ext5, *ext6, *ext7, *ext8, *ext9; int neg_result; int invert_result; const char *pi; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* cache pi to the working precision */ pi = cache_pi(prec + 3); /* * Allocate our temporary registers for sin() and cos() * calculations, plus two extra: one to hold the sine while we're * calculating the cosine, and the other to hold the cosine result. * * (We could make do with fewer registers by copying values around, * but if the numbers are of very high precision it's much cheaper * to allocate more registers, since the registers come out of the * register cache and probably won't require any actual memory * allocation.) */ alloc_temp_regs(prec + 3, 9, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5, &ext6, &hdl6, &ext7, &hdl7, &ext8, &hdl8, &ext9, &hdl9); /* protect against errors so we definitely free the registers */ err_try { char *tmp; /* presume we won't have to invert our result */ invert_result = FALSE; /* copy our initial value to ext1 */ copy_val(ext1, ext_, FALSE); /* note that tan(-x) = -tan(x) - if x < 0, use -x */ neg_result = get_neg(ext1); set_neg(ext1, FALSE); /* reduce the argument modulo 2*pi */ copy_val(ext7, pi, TRUE); mul_by_long(ext7, 2); while (compare_abs(ext1, ext7) > 0) { /* divide by 2*pi, storing the remainder in r2 */ compute_quotient_into(ext6, ext2, ext1, ext7); /* swap r2 into r1 for the next round */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * Next, note that tan(x+pi) = tan(x). So, if x > pi, the * argument by pi. This will reduce our range to 0 <= x <= pi. */ copy_val(ext7, pi, TRUE); if (compare_abs(ext1, ext7) > 0) { /* subtract pi from the argument */ compute_abs_diff_into(ext2, ext1, ext7); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * Use the fact that tan(x + pi) = tan(x) once again: if x > * pi/2, subtract pi from x to adjust the range to -pi/2 <= x <= * pi/2. */ div_by_long(ext7, 2); if (compare_abs(ext1, ext7) > 0) { /* subtract pi from the argument */ copy_val(ext7, pi, TRUE); compute_abs_diff_into(ext2, ext1, ext7); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * once again, reduce our range using the sign equivalence - * this will limit our range to 0 <= x <= pi/2 */ if (get_neg(ext1)) neg_result = !neg_result; set_neg(ext1, FALSE); /* * Next, observe that tan(x+pi/2) = 1/tan(x). If x > pi/4, * invert the result. */ copy_val(ext7, pi, TRUE); div_by_long(ext7, 4); if (compare_abs(ext1, ext7) > 0) { /* calculate pi/2 */ copy_val(ext7, pi, TRUE); div_by_long(ext7, 2); /* subtract pi/2 to get into range -pi/4 <= x <= pi/4 */ compute_abs_diff_into(ext2, ext1, ext7); /* sin(-x) = -sin(x) */ if (get_neg(ext1)) neg_result = !neg_result; set_neg(ext1, FALSE); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; /* note that we must invert the result */ invert_result = TRUE; } /* * make a copy of our argument value in ext9 for safekeeping * while we're calculating the sine */ copy_val(ext9, ext1, FALSE); /* * We now have a value in the range 0 <= x <= pi/4, which will * converge quickly with our Taylor series for sine and cosine. * This will also ensure that both sin and cos are non-negative, * so the sign we calculated for the tangent is all that matters * for the sign. * * First, Calculate the sine and store the result in r8. */ calc_sin_series(vmg_ ext8, ext1, ext2, ext3, ext4, ext5, ext6, ext7); /* * Calculate the cosine and store the result in r1. Note that * we saved the argument value in ext9 while we were working on * the sine, so we can now use that value as the argument here. * ext1 was trashed by the sine calculation, so just use it as * the output register here. */ calc_cos_series(vmg_ ext1, ext9, ext2, ext3, ext4, ext5, ext6, ext7); /* calculate the quotient sin/cos, or cos/sin if inverted */ if (invert_result) compute_quotient_into(new_ext, 0, ext1, ext8); else compute_quotient_into(new_ext, 0, ext8, ext1); /* negate the result if necessary */ set_neg(new_ext, neg_result); /* normalize the result */ normalize(new_ext); } err_finally { /* release our temporary registers */ release_temp_regs(9, hdl1, hdl2, hdl3, hdl4, hdl5, hdl6, hdl7, hdl8, hdl9); } err_end; /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - convert degrees to radians */ int CVmObjBigNum::getp_deg2rad(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t prec = get_prec(ext_); uint hdl1; char *ext1; const char *pi; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* cache pi to our required precision */ pi = cache_pi(prec + 2); /* * allocate a temporary register for pi/180 - give it some extra * precision */ alloc_temp_regs(prec + 2, 1, &ext1, &hdl1); /* get pi to our precision */ copy_val(ext1, pi, TRUE); /* divide pi by 180 */ div_by_long(ext1, 180); /* multiply our value by pi/180 */ compute_prod_into(new_ext, ext_, ext1); /* release our temporary registers */ release_temp_regs(1, hdl1); /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - convert radians to degrees */ int CVmObjBigNum::getp_rad2deg(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t prec = get_prec(ext_); uint hdl1; char *ext1; const char *pi; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* cache pi to our working precision */ pi = cache_pi(prec + 2); /* allocate a temporary register for pi/180 */ alloc_temp_regs(prec + 2, 1, &ext1, &hdl1); /* catch errors so we can be sure to free registers */ err_try { /* get pi to our precision */ copy_val(ext1, pi, TRUE); /* divide pi by 180 */ div_by_long(ext1, 180); /* divide by pi/180 */ compute_quotient_into(new_ext, 0, ext_, ext1); } err_finally { /* release our temporary registers */ release_temp_regs(1, hdl1); } err_end; /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - arcsine */ int CVmObjBigNum::getp_asin(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* calculate and return the arcsine */ return calc_asincos(vmg_ self, retval, argc, FALSE); } /* * property evaluator - arccosine */ int CVmObjBigNum::getp_acos(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* calculate and return the arcsine */ return calc_asincos(vmg_ self, retval, argc, TRUE); } /* * Common property evaluator routine - arcsine and arccosine. arcsin * and arccos are related by arccos(x) = pi/2 - arcsin(x). So, to * calculate an arccos, we can simply calculate the arcsin, then * subtract the result from pi/2. */ int CVmObjBigNum::calc_asincos(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int is_acos) { char *new_ext; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* calculate the arcsin/arccos into the result */ calc_asincos_into(new_ext, ext_, is_acos); /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * Calculate the arcsine or arccosine into the given result variable */ void CVmObjBigNum::calc_asincos_into(char *dst, const char *src, int is_acos) { size_t prec = get_prec(dst); uint hdl1, hdl2, hdl3, hdl4, hdl5; char *ext1, *ext2, *ext3, *ext4, *ext5; const char *pi; /* cache pi to our working precision */ pi = cache_pi(prec + 3); /* * allocate our temporary registers - use some extra precision over * what we need for the result, to reduce the effect of accumulated * rounding error */ alloc_temp_regs(prec + 3, 5, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5); /* catch errors so we release our temp registers */ err_try { char *tmp; static const unsigned char one_over_sqrt2[] = { /* precision = 10, scale = 0, flags = 0 */ 10, 0, 0, 0, 0, 0x70, 0x71, 0x06, 0x78, 0x14 }; int use_sqrt; int sqrt_neg; /* get the initial value of x into our accumulator, r1 */ copy_val(ext1, src, FALSE); /* * check to see if the absolute value of the argument is greater * than 1 - if it is, it's not valid */ copy_val(ext2, get_one(), FALSE); if (compare_abs(ext1, ext2) > 0) err_throw(VMERR_OUT_OF_RANGE); /* presume we won't need to use the sqrt(1-x^2) method */ use_sqrt = FALSE; /* * Check to see if the absolute value of the argument is greater * than 1/sqrt(2). If it is, the series expansion converges too * slowly (as slowly as never, if the value is exactly 1). To * speed things up, in these cases calculate pi/2 - * asin(sqrt(1-x^2)) instead, which is equivalent but gives us a * smaller asin() argument for quicker series convergence. * * We don't need to compare to 1/sqrt(2) in great precision; * just use a few digits. */ copy_val(ext2, (const char *)one_over_sqrt2, TRUE); if (compare_abs(ext1, ext2) > 0) { /* flag that we're using the sqrt method */ use_sqrt = TRUE; /* note the sign - we'll need to apply this to the result */ sqrt_neg = get_neg(ext1); /* compute x^2 into r2 */ compute_prod_into(ext2, ext1, ext1); /* subtract r2 from 1 (by adding -r2 to 1), storing in r4 */ copy_val(ext3, get_one(), FALSE); make_negative(ext2); compute_sum_into(ext4, ext3, ext2); /* compute sqrt(1-x^2) (which is sqrt(r4)) into r1 */ compute_sqrt_into(ext1, ext4); } /* compute the arcsine */ ext1 = calc_asin_series(ext1, ext2, ext3, ext4, ext5); /* if we're using the sqrt method, finish the sqrt calculation */ if (use_sqrt) { /* calculate pi/2 */ copy_val(ext2, pi, TRUE); div_by_long(ext2, 2); /* compute pi/2 - r1 by negating r1 and adding it */ negate(ext1); compute_sum_into(ext3, ext2, ext1); /* negate the result if the original value was negative */ if (sqrt_neg) negate(ext3); /* swap the result back into r1 */ tmp = ext1; ext1 = ext3; ext3 = tmp; } /* * We now have the arcsine in r1. If we actually wanted the * arccosine, subtract the arcsine from pi/2 to yield the * arccosine. */ if (is_acos) { /* get pi/2 into r2 */ copy_val(ext2, pi, TRUE); div_by_long(ext2, 2); /* negate r1 to get -asin */ negate(ext1); /* add -asin to r2 to yield the arccosine in r3 */ compute_sum_into(ext3, ext2, ext1); /* swap the result back into ext1 */ tmp = ext3; ext3 = ext1; ext1 = tmp; } /* store the result, rounding if necessary */ copy_val(dst, ext1, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(5, hdl1, hdl2, hdl3, hdl4, hdl5); } err_end; } /* * Calculate the asin series expansion. This should only be called with * argument values with absolute value less than 1/sqrt(2), because the * series converges very slowly for larger values. For operands above * 1/sqrt(2), the caller should instead compute the equivalent value * * +/- (pi/2 - asin(sqrt(1-x^2))). * * (+ if x > 0, - if x < 0). * * The argument value is in ext1, and we return a pointer to the * register that contains the result (which will be one of the input * registers). We use all of the input registers as scratchpads, so * their values are not retained. */ char *CVmObjBigNum::calc_asin_series(char *ext1, char *ext2, char *ext3, char *ext4, char *ext5) { ulong n; /* get the current power of x (1) into the x power register, r2 */ copy_val(ext2, ext1, FALSE); /* * compute x^2 into r3 - we'll multiply the previous power by this * to get the next power (we need x^1, x^3, x^5, etc) */ compute_prod_into(ext3, ext1, ext1); /* start at the first term */ n = 1; /* keep going until we have enough precision in the result */ for (;;) { ulong i; char *tmp; /* move on to the next term */ n += 2; /* * compute the next weirdness factor into r4: * * 1*3*5*...*(n-2) *. ----------------- *. 2*4*6*...*(n-1)*n */ /* start out with 1 in r4 */ copy_val(ext4, get_one(), FALSE); /* multiply by odd numbers up to but not including 'n' */ for (i = 3 ; i < n ; i += 2) mul_by_long(ext4, i); /* * Divide by even numbers up to and including n-1. Note that we * use div_by_long() because it's faster than using a BigNumber * divisor. div_by_long() has a limit of ULONG_MAX/10 for the * divisor; we shouldn't have to worry about exceeding that, since * our maximum precision is 64k digits. The series will converge * to our maximum precision long before 'i' gets close to * ULONG_MAX/10. (Even if not, computing billions of series terms * would take so long that we'd never get there anyway.) */ for (i = 2 ; i < n ; i += 2) div_by_long(ext4, i); /* divide by n */ div_by_long(ext4, n); /* * compute the next power of x - multiply our current power of x * (r2) by x^2 (r3) */ compute_prod_into(ext5, ext2, ext3); /* swap r5 into r2 - this new power of x is now current */ tmp = ext5; ext5 = ext2; ext2 = tmp; /* * multiply the current x power by the current weirdness factor * - this will yield the current term into r5 */ compute_prod_into(ext5, ext2, ext4); /* * if this value is too small to show up in our accumulator, * we're done */ if (is_zero(ext5) || get_exp(ext1) - get_exp(ext5) > (int)get_prec(ext1)) break; /* * we can trash the weird factor now - use it as a scratch pad * for adding the accumulator so far (r1) to this term */ compute_sum_into(ext4, ext1, ext5); /* swap the result into r1, since it's the new accumulator */ tmp = ext4; ext4 = ext1; ext1 = tmp; } /* return the accumulator register */ return ext1; } /* * property evaluator - arctangent */ int CVmObjBigNum::getp_atan(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t prec = get_prec(ext_); uint hdl1, hdl2, hdl3, hdl4, hdl5; char *ext1, *ext2, *ext3, *ext4, *ext5; const char *pi; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* cache pi to our working precision */ pi = cache_pi(prec + 3); /* allocate some temporary registers */ alloc_temp_regs(prec + 3, 5, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5); /* catch errors so we can be sure to free registers */ err_try { /* * If x has absolute value close to 1, either above or below, * our two series don't converge very rapidly. Happily, we can * fall back on an alternative in these case by using the * relation * * arctan(x) = (+/-)arccos(1/sqrt(x^2+1)) * * (The sign of the result is the same as the sign of x.) Since * we already have routines for arccosine and square root, this * calculation is easy. * * If x doesn't have absolute value close to 1, use the * appropriate series, since they converge rapidly. */ if (get_exp(ext_) < -1 || get_exp(ext_) > 2) { int term_neg; ulong n; /* * exponent less than -1 -> the number has a small absolute * value - use the small-x series expansion: * * x - x^3/3 + x^5/5 - x^7/7 ... * * OR * * exponent greater than 2 -> the number has a large * absolute value, so the large-x series expansion should * converge quickly: * * +/- pi/2 - 1/x + 1/3x^3 - 1/5x^5 ... * * (the sign of the leading pi/2 term is positive if x is * positive, negative if x is negative) * * We can commonize these expressions by defining x' = x for * small x and x' = 1/x for large x, defining C as 0 for * small x and +/-pi/2 for large X, defining N as +1 for * small x and -1 for large x, and rewriting the series as: * * C + Nx' - Nx'^3/3 + Nx'^5/5 + ... */ /* check for large or small value */ if (get_exp(ext_) < 0) { /* small number - start with zero in the accumulator (r1) */ set_zero(ext1); /* get the current power of x' into r2 - this is simply x */ copy_val(ext2, ext_, FALSE); /* the first term (x) is positive */ term_neg = FALSE; } else { /* large number - start with pi/2 in the accumulator (r1) */ copy_val(ext1, pi, TRUE); div_by_long(ext1, 2); /* if x is negative, make that -pi/2 */ set_neg(ext1, get_neg(ext_)); /* get 1/x into r2 - this is our x' term value */ compute_quotient_into(ext2, 0, get_one(), ext_); /* the first term (1/x) is negative */ term_neg = TRUE; } /* start at the first term */ n = 1; /* * get x'^2 into r3 - we'll use this to calculate each * subsequent term (we need x', x'^3, x'^5, etc) */ compute_prod_into(ext3, ext2, ext2); /* iterate until we reach the desired precision */ for (;;) { char *tmp; /* copy the current power term from r2 into r4 */ copy_val(ext4, ext2, FALSE); /* * divide by the current term constant (we'll never have * to compute billions of terms to reach our maximum * possible 64k digits of precision, so 'n' will always be * way less than the div_by_long limit of ULONG_MAX/10) */ div_by_long(ext4, n); /* negate this term if necessary */ if (term_neg) set_neg(ext4, !get_neg(ext4)); /* * if we're under the radar on precision, stop looping * (don't stop on the first term, since the accumulator * hasn't been fully primed yet) */ if (n != 1 && (is_zero(ext4) || (get_exp(ext1) - get_exp(ext4) > (int)get_prec(ext1)))) break; /* compute the sum of the accumulator and this term */ compute_sum_into(ext5, ext1, ext4); /* swap the result back into the accumulator */ tmp = ext1; ext1 = ext5; ext5 = tmp; /* * move on to the next term - advance n by 2 and swap * the term sign */ n += 2; term_neg = !term_neg; /* * compute the next power term - multiply r2 (the latest * x' power) by r3 (x'^2) */ compute_prod_into(ext4, ext2, ext3); /* swap r4 back into r2 - this is now the latest power */ tmp = ext2; ext2 = ext4; ext4 = tmp; } /* store the result, rounding as needed */ copy_val(new_ext, ext1, TRUE); } else { /* * We have a value of x from .01 to 10 - in this range, the * rewrite using arccosine will give us excellent precision. */ /* calculate x^2 into r1 */ compute_prod_into(ext1, ext_, ext_); /* add one (x^2 has to be positive, so don't worry about sign) */ increment_abs(ext1); /* take the square root and put the result in r2 */ compute_sqrt_into(ext2, ext1); /* divide that into 1, and put the result back in r1 */ compute_quotient_into(ext1, 0, get_one(), ext2); /* * Compute the arccosine of this value - the result is the * arctangent, so we can store it directly in the output * register. */ calc_asincos_into(new_ext, ext1, TRUE); /* if the input was negative, invert the sign of the result */ if (get_neg(ext_)) negate(new_ext); } } err_finally { /* release our temporary registers */ release_temp_regs(5, hdl1, hdl2, hdl3, hdl4, hdl5); } err_end; /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - square root */ int CVmObjBigNum::getp_sqrt(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* compute the square root into the result */ compute_sqrt_into(new_ext, ext_); /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - natural logarithm */ int CVmObjBigNum::getp_ln(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; /* zero or negative values are not allowed */ if (is_zero(ext_) || get_neg(ext_)) err_throw(VMERR_OUT_OF_RANGE); /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* compute the natural logarithm */ compute_ln_into(new_ext, ext_); /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - exp (exponentiate e, the base of the natural * logarithm, to the power of this number) */ int CVmObjBigNum::getp_exp(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* compute exp(x) */ compute_exp_into(new_ext, ext_); /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - log10 (base-10 logarithm) */ int CVmObjBigNum::getp_log10(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t prec = get_prec(ext_); uint hdl1, hdl2, hdl3; char *ext1, *ext2, *ext3; const char *ln10; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* cache ln(10) to the required precision */ ln10 = cache_ln10(prec + 3); /* allocate some temporary registers */ alloc_temp_regs(prec + 3, 3, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3); /* catch errors so we can be sure to free registers */ err_try { /* compute the natural logarithm of the number */ compute_ln_into(ext1, ext_); /* get ln(10), properly rounded */ copy_val(ext2, ln10, TRUE); /* compute ln(x)/ln(10) to yield log10(x) */ compute_quotient_into(ext3, 0, ext1, ext2); /* store the result, rounding as needed */ copy_val(new_ext, ext3, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(3, hdl1, hdl2, hdl3); } err_end; /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - pow(y) - calculate x^y */ int CVmObjBigNum::getp_pow(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_val_t val2; const char *val2_ext; char *new_ext; size_t prec; uint hdl1, hdl2; char *ext1, *ext2; /* check arguments and allocate the result value */ if (setup_getp_1(vmg_ self, retval, argc, &new_ext, &val2, &val2_ext, FALSE)) return TRUE; /* use the precision of the result */ prec = get_prec(new_ext); /* * Check for a special case: if the number we're exponentiating is * zero, the result is 0 for any positive exponent, and an error for * any non-positive exponent (0^0 is undefined, and 0^n where n<0 is * equivalent to 1/0^n == 1/0, which is a divide-by-zero error). */ if (is_zero(ext_)) { /* 0^0 is undefined */ if (is_zero(val2_ext)) err_throw(VMERR_OUT_OF_RANGE); /* 0^negative is a divide by zero error */ if (get_neg(val2_ext)) err_throw(VMERR_DIVIDE_BY_ZERO); /* set the result to one, and we're done */ set_zero(new_ext); goto done; } /* allocate some temporary registers */ alloc_temp_regs(prec + 3, 2, &ext1, &hdl1, &ext2, &hdl2); /* catch errors so we can be sure to free registers */ err_try { int result_neg; /* * If a = e^b, then b = ln(a). This means that x^y = e^ln(x^y) * = e^(y * ln(x)). So, we can compute the result in terms of * natural logarithm and exponentiation of 'e', for which we * have primitives we can call. */ /* * If x is negative, we can only exponentiate the value to * integer powers. In this case, we can substitute x' = -x * (hence x' will be positive), and rewrite the expression as * * (-1)^y * (x')^y * * We can only calculate (-1)^y for integer values of y, since * the result is complex if y is not an integer. */ if (get_neg(ext_)) { size_t idx; int units_dig; /* copy x into r2 */ copy_val(ext2, ext_, FALSE); /* * calculate x' = (-x) - since x is negative, this will * guarantee that x' is positive */ set_neg(ext2, FALSE); /* * make sure y is an integer - start at the first digit * after the decimal point and check for any non-zero digits */ idx = (get_exp(val2_ext) < 0 ? 0 : (size_t)get_exp(val2_ext)); for ( ; idx < get_prec(val2_ext) ; ++idx) { /* if this digit isn't a zero, it's not an integer */ if (get_dig(val2_ext, idx) != 0) { /* y isn't an integer, so we can't calculate (-1)^y */ err_throw(VMERR_OUT_OF_RANGE); } } /* get the first digit to the left of the decimal point */ if (get_exp(val2_ext) <= 0 || (size_t)get_exp(val2_ext) > get_prec(val2_ext)) { /* the units digit isn't represented - zero is implied */ units_dig = 0; } else { /* get the digit */ units_dig = get_dig(val2_ext, (size_t)get_exp(val2_ext) - 1); } /* * if the units digit is even, the result will be positive; * if it's odd, the result will be negative */ result_neg = ((units_dig & 1) != 0); /* calculate ln(x') into r1 */ compute_ln_into(ext1, ext2); } else { /* calculate ln(x) into r1 */ compute_ln_into(ext1, ext_); /* the result will be positive */ result_neg = FALSE; } /* calculate y * ln(x) into r2 */ compute_prod_into(ext2, val2_ext, ext1); /* calculate exp(r2) = exp(y*ln(x)) = x^y into r1 */ compute_exp_into(ext1, ext2); /* negate the result if we had a negative x and an odd power */ if (result_neg) negate(ext1); /* save the result, rounding as needed */ copy_val(new_ext, ext1, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(2, hdl1, hdl2); } err_end; done: /* discard the GC protection */ G_stk->discard(2); /* handled */ return TRUE; } /* * property evaluator - sinh */ int CVmObjBigNum::getp_sinh(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* calculate the hyperbolic sine using the common evaluator */ return calc_sinhcosh(vmg_ self, retval, argc, FALSE, FALSE); } /* * property evaluator - cosh */ int CVmObjBigNum::getp_cosh(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* calculate the hyperbolic cosine using the common evaluator */ return calc_sinhcosh(vmg_ self, retval, argc, TRUE, FALSE); } int CVmObjBigNum::getp_tanh(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* calculate the hyperbolic tangent using the common evaluator */ return calc_sinhcosh(vmg_ self, retval, argc, FALSE, TRUE); } /* * common property evaluator - compute a hyperbolic sine or cosine, or * do both to calculate a hyperbolic tangent */ int CVmObjBigNum::calc_sinhcosh(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int is_cosh, int is_tanh) { char *new_ext; /* check arguments and allocate the return value */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* calculate the result */ compute_sinhcosh_into(new_ext, ext_, is_cosh, is_tanh); /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Compute the fractional part of a number (replacing the value in place). * This effectively subtracts the integer portion of the number, leaving * only the fractional portion. */ void CVmObjBigNum::compute_frac(char *ext) { /* get the exponent and precision */ int exp = get_exp(ext); size_t prec = get_prec(ext); /* clear out the first n digits, where n is the exponent */ for (size_t idx = 0 ; idx < prec && (int)idx < exp ; ++idx) set_dig(ext, idx, 0); /* normalize the result */ normalize(ext); } /* * Get the whole part of a number (replacing the value in place). This * truncates the number to its integer portion, with no rounding. */ void CVmObjBigNum::compute_whole(char *ext) { /* get the exponent and precision */ int exp = get_exp(ext); size_t prec = get_prec(ext); /* check what we have */ if (exp <= 0) { /* it's an entirely fractional number - the result is zero */ set_zero(ext); } else { /* clear digits after the decimal point */ for (size_t idx = (size_t)exp ; idx < prec ; ++idx) set_dig(ext, idx, 0); /* normalize the result */ normalize(ext); } } /* ------------------------------------------------------------------------ */ /* * property evaluator - numType: get the number type information */ /* * number types defined in bignum.h - these are part of the public API, so * they can't be changed */ #define NumTypeNum 0x0001 #define NumTypeNAN 0x0002 #define NumTypePInf 0x0004 #define NumTypeNInf 0x0008 #define NumTypePZero 0x0010 #define NumTypeNZero 0x0020 int CVmObjBigNum::getp_numType(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* set the appropriate flags */ switch (get_type(ext_)) { case VMBN_T_NUM: retval->set_int(NumTypeNum | (is_zero(ext_) ? (get_neg(ext_) ? NumTypeNZero : NumTypePZero) : 0)); break; case VMBN_T_INF: retval->set_int(get_neg(ext_) ? NumTypeNInf : NumTypePInf); break; case VMBN_T_NAN: default: retval->set_int(NumTypeNAN); break; } /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Compute a natural logarithm */ void CVmObjBigNum::compute_ln_into(char *dst, const char *src) { uint hdl1, hdl2, hdl3, hdl4, hdl5; char *ext1, *ext2, *ext3, *ext4, *ext5; size_t prec = get_prec(dst); const char *ln10; /* cache the value of ln(10) */ ln10 = cache_ln10(prec + 3); /* if the source value is zero, it's an error */ if (is_zero(src)) err_throw(VMERR_OUT_OF_RANGE); /* allocate some temporary registers */ alloc_temp_regs(prec + 3, 5, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5); /* catch errors so we can be sure to free registers */ err_try { int src_exp; /* * Observe that we store our values as x = a*10^b. We can thus * rewrite ln(x) as ln(a*10^b) = ln(a)+ln(10^b) = * log(a)+b*ln(10). a is the mantissa, which due to * normalization is in the range 0.1 <= a < 1.0. Thus, a is an * ideal argument for the Taylor series. So, we can simply * compute ln(mantissa), then add it to ln(10)*exponent. */ /* copy our argument into r1 */ copy_val(ext1, src, FALSE); /* * remember the original exponent, and set the exponent of the * series argument to zero - we'll correct for this later by * adding the log of the exponent to the series result */ src_exp = get_exp(ext1); set_exp(ext1, 0); /* * The series expansion is especially efficient for values near * 1.0. We know the value is now in the range 0.1 <= a < 1.0. So * if the lead digit of the mantissa is 1, which means the value is * 0.1 and change, multiply by ten (thus making it 1.0 and change), * and adjust the exponent accordingly. */ if (get_dig(ext1, 0) == 1) { set_exp(ext1, 1); --src_exp; } /* compute the series expansion */ ext1 = compute_ln_series_into(ext1, ext2, ext3, ext4, ext5); /* add in the input exponent, properly adjusted */ if (src_exp != 0) { /* get ln10 into r2 */ copy_val(ext2, ln10, TRUE); /* apply the exponent's sign if it's negative */ if (src_exp < 0) { set_neg(ext2, TRUE); src_exp = -src_exp; } /* multiply by the exponent */ mul_by_long(ext2, src_exp); /* add this value to the series expansion value */ compute_sum_into(ext3, ext1, ext2); /* use this as the result */ ext1 = ext3; } /* copy the result, rounding if needed */ copy_val(dst, ext1, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(5, hdl1, hdl2, hdl3, hdl4, hdl5); } err_end; } /* * Compute the natural log series. The argument value, initially in * ext1, should be adjusted to a small value before this is called to * ensure quick series convergence. */ char *CVmObjBigNum::compute_ln_series_into(char *ext1, char *ext2, char *ext3, char *ext4, char *ext5) { /* start at the first term of the series */ ulong n = 1; /* subtract one from r1 to yield (x-1) in r2 */ compute_abs_diff_into(ext2, ext1, get_one()); /* add one to r1 to yield (x+1) */ increment_abs(ext1); /* compute (x-1)/(x+1) into r3 - this will be our current power */ compute_quotient_into(ext3, 0, ext2, ext1); /* * compute ((x-1)/(x+1))^2 into r4 - we'll multiply r3 by this on * each iteration to produce the next required power, since we only * need the odd powers */ compute_prod_into(ext4, ext3, ext3); /* start out with the first power in our accumulator (r1) */ copy_val(ext1, ext3, FALSE); /* iterate until we have a good enough answer */ for (;;) { /* compute the next power into r2 */ compute_prod_into(ext2, ext3, ext4); /* copy the result into our current power register, r3 */ copy_val(ext3, ext2, FALSE); /* advance n */ n += 2; /* * divide the power by n (we'll never have to compute billions of * terms to get our maximum 64k digits of precision, so 'n' will * always be way less than the div_by_long limit of ULONG_MAX/10) */ div_by_long(ext2, n); /* if it's too small to notice, we're done */ if (is_zero(ext2) || get_exp(ext1) - get_exp(ext2) > (int)get_prec(ext1)) break; /* compute the sum with our accumulator into r5 */ compute_sum_into(ext5, ext1, ext2); /* swap r5 and r1 - the new sum is our new accumulator */ char *tmp = ext5; ext5 = ext1; ext1 = tmp; } /* multiply the result of the series by 2 */ mul_by_long(ext1, 2); /* return the register containing the result */ return ext1; } /* * Compute e^x, where e is the base of the natural logarithm (2.818...) */ void CVmObjBigNum::compute_exp_into(char *dst, const char *src) { uint hdl1, hdl2, hdl3, hdl4, hdl5, hdl6; char *ext1, *ext2, *ext3, *ext4, *ext5, *ext6; size_t prec = get_prec(dst); const char *ln10; /* get the constant value of ln10 to the required precision */ ln10 = cache_ln10(prec + 3); /* allocate temporary registers */ alloc_temp_regs(prec + 3, 6, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5, &ext6, &hdl6); /* catch errors so we can be sure to free registers */ err_try { char *tmp; ulong n; ulong n_fact; int use_int_fact; long new_exp; size_t idx; size_t decpt_idx; int twos; /* * We want to calculate e^x. Observe that a^x = e^(x*ln(x)), so * 10^x = e^(x*ln(10)). We can rewrite our desired expression * e^x as * * e^[(x/ln(10)) * ln(10)] * * which is thus * * 10^(x / ln(10)) * * We store our numbers as a mantissa times 10 raised to an * integer exponent. Clearly the exponent of 10 in the formula * above is not always an integer (in fact, it's extremely rare * that it would be an integer), so we still have more work to * do. What we must do is obtain an integer exponent. So, let * us define y = x/ln(10); we can now rewrite the above as * * 10^(int(y) + frac(y)) * * where int(y) is the integer portion of y and frac(y) is the * fractional portion (y - int(y)). We can rewrite the above as * * 10^frac(y) * 10^int(y) * * which we can further rewrite as * * e^(frac(y)*ln(10)) * 10^int(y) * * We haven't made the problem of finding an exponential * disappear, but we've reduced the argument to a very * manageable range, which is important because it makes the * Taylor series converge quickly. Furthermore, it's extremely * inexpensive to separate out the problem like this, since it * falls quite naturally out of the representation we use, so it * doesn't add much overhead to do this preparation work. */ /* first, calculate x/ln(10) into r1 */ compute_quotient_into(ext1, 0, src, ln10); /* * compute the integer portion of x/ln(10) - it has to fit in a * 16-bit integer, (-32768 to +32767), because this is going to * be the exponent of the result (or roughly so, anyway) */ decpt_idx = get_exp(ext1) >= 0 ? (size_t)get_exp(ext1) : 0; for (new_exp = 0, idx = 0 ; idx < decpt_idx ; ++idx) { int dig; /* * get this digit if it's represented; if not, it's an * implied trailing zero */ dig = (idx < get_prec(ext1) ? get_dig(ext1, idx) : 0); /* add this digit into the accumulator */ new_exp *= 10; new_exp += dig; /* * Make sure we're still in range. Note that, because our * representation is 0.dddd*10^x, we need one more factor of * ten than you'd think here, the adjust of the range from * the expected -32768..32767 */ if (new_exp > (get_neg(ext1) ? 32769L : 32766L)) err_throw(VMERR_NUM_OVERFLOW); /* * zero out this digit, so that when we're done r1 has the * fractional part only */ if (idx < get_prec(ext1)) set_dig(ext1, idx, 0); } /* negate the exponent value if the source value is negative */ if (get_neg(ext1)) new_exp = -new_exp; /* normalize the fractional part, which remains in ext1 */ normalize(ext1); /* * Multiply it by ln10, storing the result in r3. This is the * value we'll use with the Taylor series. */ compute_prod_into(ext3, ext1, ln10); /* * While our input value is greater than 0.5, divide it by two to * make it smaller than 0.5. This will speed up the series * convergence. When we're done, we'll correct for the divisions * by squaring the result the same number of times that we halved * 'x', because e^2x = (e^x)^2. */ copy_val(ext1, get_one(), FALSE); div_by_long(ext1, 2); for (twos = 0 ; compare_abs(ext3, ext1) > 0 ; ++twos) div_by_long(ext3, 2); /* * Start with 1+x in our accumulator (r1). This unrolls the * trivial first two steps of the loop, where n=0 (term=1) and n=1 * (term=x). */ copy_val(ext2, get_one(), FALSE); compute_sum_into(ext1, ext2, ext3); /* get term n=2 (x^2) into the current-power register (r2) */ compute_prod_into(ext2, ext3, ext3); /* start with 2 in our factorial register (r4) */ copy_val(ext4, get_one(), FALSE); mul_by_long(ext4, 2); /* start at term n=2, n! = 2 */ n = 2; n_fact = 2; use_int_fact = TRUE; /* go until we reach the required precision */ for (;;) { /* for efficiency, try integer division */ if (use_int_fact) { /* * we can still fit the factorial in an integer - divide * by the integer value of n!, since it's a lot faster */ copy_val(ext5, ext2, FALSE); div_by_long(ext5, n_fact); /* calculate the next n! integer, if it'll fit in a long */ if (n_fact > LONG_MAX/10/(n+1)) { /* * it'll be too big next time - we'll have to start * using the full quotient calculation */ use_int_fact = FALSE; } else { /* it'll still fit - calculate the next n! */ n_fact *= (n+1); } } else { /* compute x^n/n! (r2/r4) into r5 */ compute_quotient_into(ext5, 0, ext2, ext4); } /* if we're below the required precision, we're done */ if (is_zero(ext5) || get_exp(ext1) - get_exp(ext5) > (int)get_prec(ext1)) break; /* compute the sum of the accumulator and this term into r6 */ compute_sum_into(ext6, ext1, ext5); /* swap the result into the accumulator */ tmp = ext1; ext1 = ext6; ext6 = tmp; /* on to the next term */ ++n; /* compute the next factorial value */ mul_by_long(ext4, n); /* compute the next power of x' into r5 */ compute_prod_into(ext5, ext2, ext3); /* swap the result into our current-power register (r2) */ tmp = ext2; ext2 = ext5; ext5 = tmp; } /* square the result as many times as we halved the argument */ for ( ; twos != 0 ; --twos) { /* compute the square of r1 into r2 */ compute_prod_into(ext2, ext1, ext1); /* swap the result back into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * set up our 10's exponent value - this is simply 1*10^new_exp, * which we calculated earlier (which we represent as * 0.1*10^(new_exp+1) */ copy_val(ext2, get_one(), FALSE); set_exp(ext2, (int)(new_exp + 1)); /* multiply by the 10's exponent value */ compute_prod_into(ext3, ext1, ext2); /* copy the result into the output register, rounding as needed */ copy_val(dst, ext3, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(6, hdl1, hdl2, hdl3, hdl4, hdl5, hdl6); } err_end; } /* ------------------------------------------------------------------------ */ /* * Compute a hyperbolic sine or cosine */ void CVmObjBigNum::compute_sinhcosh_into(char *dst, const char *src, int is_cosh, int is_tanh) { size_t prec = get_prec(dst); uint hdl1, hdl2, hdl3, hdl4; char *ext1, *ext2, *ext3, *ext4; /* allocate some temporary registers */ alloc_temp_regs(prec + 3, 4, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4); /* catch errors so we can be sure to free registers */ err_try { /* * sinh(x) = (e^x - e^(-x))/2 * * cosh(x) = (e^x + e^(-x))/2 * * The two differ only in the sign of the e^(-x) term, so most * of the calculation is the same for both. */ /* compute e^x into r1 */ compute_exp_into(ext1, src); /* * rather than calculating e^-x separately, simply invert our * e^x value to yield e^-x (a simple division is much quicker * than calculating another exponent, which involves an entire * taylor series expansion) */ copy_val(ext3, get_one(), FALSE); compute_quotient_into(ext2, 0, ext3, ext1); /* * if we're calculating the tanh, we'll want both the sinh and * cosh values */ if (is_tanh) { /* add the terms to get the cosh */ compute_sum_into(ext4, ext1, ext2); /* subtract ext2 to get the sinh */ negate(ext2); compute_sum_into(ext3, ext1, ext2); /* tanh is the quotient of sinh/cosh */ compute_quotient_into(ext1, 0, ext3, ext4); /* our result is in ext1 - set ext3 to point there */ ext3 = ext1; } else { /* * if this is sinh, the e^-x term is subtracted; if it's * cosh, it's added */ if (!is_cosh) negate(ext2); /* compute r1 + r2 into r3 (e^x +/- e^(-x)) */ compute_sum_into(ext3, ext1, ext2); /* halve the result */ div_by_long(ext3, 2); } /* store the result */ copy_val(dst, ext3, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(4, hdl1, hdl2, hdl3, hdl4); } err_end; } /* ------------------------------------------------------------------------ */ /* * Cache the natural logarithm of 10 to the given precision and return * the value */ const char *CVmObjBigNum::cache_ln10(size_t prec) { char *ext; int expanded; uint hdl1, hdl2, hdl3, hdl4, hdl5; char *ext1, *ext2, *ext3, *ext4, *ext5; static const unsigned char ten[] = { /* number of digits */ 0x01, 0x00, /* exponent (0.1 * 10^2 = 10) */ 0x02, 0x00, /* flags */ 0x00, /* mantissa */ 0x10 }; /* * round up the precision a bit to ensure that we don't have to * repeatedly recalculate this value if we're asked for a cluster of * similar precisions */ prec = (prec + 7) & ~7; /* get the ln10 cache register */ ext = S_bignum_cache->get_ln10_reg(calc_alloc(prec), &expanded); /* if that failed, throw an error */ if (ext == 0) err_throw(VMERR_OUT_OF_MEMORY); /* * if we didn't have to allocate more memory, and the value in the * register has at least the required precision, return the cached * value */ if (!expanded && get_prec(ext) >= prec) return ext; /* * we have reallocated the register, or we just didn't have enough * precision in the old value - set the new precision */ set_prec(ext, prec); /* allocate some temporary registers */ alloc_temp_regs(prec + 3, 5, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5); /* catch errors so we can be sure to free registers */ err_try { /* * Compute sqrt(10) - 10 is too large for the series to converge, * but sqrt(10) is good. We'll correct for this later by doubling * the result of the series expansion, which gives us the correct * result: ln(a^b) = b*ln(a), and sqrt(x) = x^(1/2), hence * ln(sqrt(x)) = ln(x)/2, which means that ln(x) = 2*ln(sqrt(x)). * * Note that we have to do this the hard way, by explicitly doing * the ln series rather than just calling compute_ln_into() to get * the value directly. Why? Because compute_ln_into() needs the * cached value of ln(10) to do its work. If we called * compute_ln_into() here, we'd get stuck in a recursion loop. */ /* compute sqrt(10), for quick series convergence */ copy_val(ext2, (const char *)ten, FALSE); compute_sqrt_into(ext1, ext2); /* compute the series expansion */ ext1 = compute_ln_series_into(ext1, ext2, ext3, ext4, ext5); /* double the result (to adjust for the sqrt) */ mul_by_long(ext1, 2); /* store the result in the cache */ copy_val(ext, ext1, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(5, hdl1, hdl2, hdl3, hdl4, hdl5); } err_end; /* return the register pointer */ return ext; } /* ------------------------------------------------------------------------ */ /* * Cache the natural logarithm of 2 to the given precision and return the * value */ const char *CVmObjBigNum::cache_ln2(size_t prec) { char *ext; int expanded; static const unsigned char two[] = { 0x01, 0x00, 0x01, 0x00, 0x00, 0x20 }; /* round up the precision to minimize recalculations */ prec = (prec + 7) & ~7; /* get the ln2 cache register */ ext = S_bignum_cache->get_ln2_reg(calc_alloc(prec), &expanded); if (ext == 0) err_throw(VMERR_OUT_OF_MEMORY); /* if we had a cached value with enough precision, return it */ if (!expanded && get_prec(ext) >= prec) return ext; /* reallocated - set the new precision and recalculate ln2 */ set_prec(ext, prec); compute_ln_into(ext, (const char *)two); /* return the register pointer */ return ext; } /* ------------------------------------------------------------------------ */ /* * Cache pi to the required precision */ const char *CVmObjBigNum::cache_pi(size_t prec) { char *ext; int expanded; uint hdl1, hdl2, hdl3, hdl4, hdl5; char *ext1, *ext2, *ext3, *ext4, *ext5; /* * round up the precision a bit to ensure that we don't have to * repeatedly recalculate this value if we're asked for a cluster of * similar precisions */ prec = (prec + 7) & ~7; /* get the pi cache register */ ext = S_bignum_cache->get_pi_reg(calc_alloc(prec), &expanded); /* if that failed, throw an error */ if (ext == 0) err_throw(VMERR_OUT_OF_MEMORY); /* * if we didn't have to allocate more memory, and the value in the * register has at least the required precision, return the cached * value */ if (!expanded && get_prec(ext) >= prec) return ext; /* * we have reallocated the register, or we just didn't have enough * precision in the old value - set the new precision */ set_prec(ext, prec); /* allocate some temporary registers */ alloc_temp_regs(prec + 3, 5, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5); /* catch errors so we can be sure to free registers */ err_try { /* * Compute the arctangent of 1. To do this, rewrite arctan(x) = * arccos(1/sqrt(1+x^2)), so x=1 gives us arccos(1/sqrt(2)). * * Now, arccos(x) = pi/2 - arcsin(x), but this would defeat the * purpose since we want to calculate pi, thus we don't want to * depend upon pi in our calculations. Fortunately, arcsin(x) = * pi/2 - arcsin(sqrt(1-x^2)), hence * * pi/2 - arcsin(x) = pi/2 - (pi/2 - arcsin(sqrt(1-x^2))) *. = arcsin(sqrt(1-x^2)) * * So, we can rewrite arccos(1/sqrt(2)) as arcsin(sqrt(1/2)). */ /* compute 1/2 into r2 */ copy_val(ext2, get_one(), FALSE); div_by_long(ext2, 2); /* compute sqrt(1/2) into r1 */ compute_sqrt_into(ext1, ext2); /* calculate the arcsin series for sqrt(1/2) */ ext1 = calc_asin_series(ext1, ext2, ext3, ext4, ext5); /* multiply the result by 4 */ mul_by_long(ext1, 4); /* store the result in the cache */ copy_val(ext, ext1, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(5, hdl1, hdl2, hdl3, hdl4, hdl5); } err_end; /* return the register pointer */ return ext; } /* ------------------------------------------------------------------------ */ /* * Cache e to the required precision */ const char *CVmObjBigNum::cache_e(size_t prec) { /* * round up the precision a bit to ensure that we don't have to * repeatedly recalculate this value if we're asked for a cluster of * similar precisions */ prec = (prec + 7) & ~7; /* get the e cache register */ int expanded; char *ext = S_bignum_cache->get_e_reg(calc_alloc(prec), &expanded); /* if that failed, throw an error */ if (ext == 0) err_throw(VMERR_OUT_OF_MEMORY); /* * if we didn't have to allocate more memory, and the value in the * register has at least the required precision, return the cached * value */ if (!expanded && get_prec(ext) >= prec) return ext; /* * we have reallocated the register, or we just didn't have enough * precision in the old value - set the new precision */ set_prec(ext, prec); /* exponentiate the base of the natural logarithm to the power 1 */ compute_exp_into(ext, get_one()); /* return the register pointer */ return ext; } /* ------------------------------------------------------------------------ */ /* * Compute a square root. We use Newton's method (a reliable old method * for extracting square roots, made better by the fact that, unlike the * method's inventor, we are fortunate to have an electronic computer to * carry out the tedious parts of the calculation for us). */ void CVmObjBigNum::compute_sqrt_into(char *dst, const char *src) { uint hdl1, hdl2, hdl3, hdl4; char *ext1, *ext2, *ext3, *ext4; size_t dst_prec = get_prec(dst); /* if the value is negative, it's an error */ if (get_neg(src)) err_throw(VMERR_OUT_OF_RANGE); /* allocate our scratchpad registers */ alloc_temp_regs(get_prec(dst) + 3, 4, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4); /* catch errors so we can free our registers */ err_try { /* * Compute our initial guess. Since our number is represented as * * (n * 10^exp), * * the square root can be written as * * sqrt(n) * sqrt(10^exp) = sqrt(n) * 10^(exp/2) * * Approximate sqrt(n) as simply n for our initial guess. This * will get us to the right order of magnitude plus or minus * one, so we should converge pretty quickly. * * If we have an odd exponent, round up and divide the mantissa * by 2 - this will be something like 0.456e7, which can be * written as 4.56e6, whose square root is about 2e3, or .2e4. * * If we have an even exponent, multiply the mantissa by 2. * This will be something like .456e8, whose square root is * about .67e4. * * Note that it's well worth the trouble to make a good initial * approximation, even with the multiply/divide, because these * operations with longs are much more efficient than the full * BigNum divide we'll have to do on each iteration. */ copy_val(ext1, src, TRUE); if ((get_exp(ext1) & 1) != 0) { /* odd exponent - round up and divide the mantissa by two */ set_exp(ext1, (get_exp(ext1) + 1)/2); div_by_long(ext1, 2); } else { /* even exponent - multiply mantissa by two */ set_exp(ext1, get_exp(ext1)/2); mul_by_long(ext1, 2); } /* iterate until we get close enough to the solution */ for (;;) { char *tmp; size_t idx; /* * Calculate the next iteration's approximation, noting that r1 * contains the current iteration's value p: * * p' = p/2 + src/2p = (p + src/p)/2 * * Note that if p == 0, we can't compute src/p, so we can't * iterate any further. */ if (is_zero(ext1)) break; /* calculate src/p into r3 */ compute_quotient_into(ext3, 0, src, ext1); /* compute p + src/p into r4 */ compute_sum_into(ext4, ext1, ext3); /* compute (p + src/p)/2 into r4 */ div_by_long(ext4, 2); /* * check for convergence - if the new value equals the old * value to the precision requested for the result, we are * at the limit of our ability to distinguish differences in * future terms, so we can stop */ if (get_neg(ext1) == get_neg(ext4) && get_exp(ext1) == get_exp(ext4)) { /* * they're the same sign and magnitude - compare the * digits to see where they first differ */ for (idx = 0 ; idx < dst_prec + 1 ; ++idx) { /* if they differ here, stop scanning */ if (get_dig(ext1, idx) != get_dig(ext4, idx)) break; } /* * if we didn't find any difference up to the output * precision plus one digit (for rounding), further * iteration will be of no value */ if (idx >= dst_prec + 1) break; } /* swap the new value into r1 for the next round */ tmp = ext1; ext1 = ext4; ext4 = tmp; } /* * copy the last iteration's value into the destination, * rounding as needed */ copy_val(dst, ext1, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(4, hdl1, hdl2, hdl3, hdl4); } err_end; } /* ------------------------------------------------------------------------ */ /* * Calculate a Taylor expansion for sin(). The argument is in ext1, and * we'll store the result in new_ext. ext1 through ext7 are temporary * registers that we'll overwrite with intermediate values. * * Before calling this function, the caller should reduce the value in * ext1 to the range -pi/4 <= ext1 <= pi/4 to ensure quick convergence * of the series. */ void CVmObjBigNum::calc_sin_series(VMG_ char *new_ext, char *ext1, char *ext2, char *ext3, char *ext4, char *ext5, char *ext6, char *ext7) { ulong n; int neg_term; char *tmp; /* start with 1! (i.e., 1) in r3 */ copy_val(ext3, get_one(), FALSE); /* * calculate x^2 into r7 - we need x, x^3, x^5, etc, so we can just * multiply the last value by x^2 to get the next power we need */ compute_prod_into(ext7, ext1, ext1); /* start with x (reduced mod 2pi) in r5 (our accumulator) */ copy_val(ext5, ext1, FALSE); /* start at term n=1 in our expansion */ n = 1; /* the first term is positive */ neg_term = FALSE; /* go until we have a precise enough value */ for (;;) { /* * move on to the next term: multiply r1 by x^2 into r2 to yield * the next power of x that we require */ compute_prod_into(ext2, ext1, ext7); /* swap r1 and r2 - r2 has the next power we need now */ tmp = ext1; ext1 = ext2; ext2 = tmp; /* * multiply r3 by (n+1)*(n+2) - it currently has n!, so this * will yield (n+2)! */ mul_by_long(ext3, (n+1)*(n+2)); /* advance n to the next term */ n += 2; /* each term swaps signs */ neg_term = !neg_term; /* divide r1 by r3 to yield the next term in our series in r4 */ compute_quotient_into(ext4, 0, ext1, ext3); /* * if this value is too small to count in our accumulator, we're * done */ if (is_zero(ext4) || get_exp(ext5) - get_exp(ext4) > (int)get_prec(ext4)) break; /* * invert the sign of the term in r4 if this is a negative term */ if (neg_term) set_neg(ext4, !get_neg(ext4)); /* add r4 to our running accumulator in r5, yielding r6 */ compute_sum_into(ext6, ext5, ext4); /* swap r5 and r6 to put the accumulated value back into r5 */ tmp = ext5; ext5 = ext6; ext6 = tmp; } /* we're done - store the result, rounding if necessary */ copy_val(new_ext, ext5, TRUE); } /* * Calculate a Taylor expansion for cos(). The argument is in ext1, and * we'll store the result in new_ext. ext1 through ext7 are temporary * registers that we'll overwrite with intermediate values. * * Before calling this function, the caller should reduce the value in * ext1 to the range -pi/4 <= ext1 <= pi/4 to ensure quick convergence * of the series. */ void CVmObjBigNum::calc_cos_series(VMG_ char *new_ext, char *ext1, char *ext2, char *ext3, char *ext4, char *ext5, char *ext6, char *ext7) { ulong n; int neg_term; char *tmp; /* start with 2! (i.e., 2) in r3 */ copy_val(ext3, get_one(), FALSE); set_dig(ext3, 0, 2); /* * calculate x^2 into r7 - we need x^2, x^4, x^6, etc, so we can * just multiply the last value by x^2 to get the next power we need */ compute_prod_into(ext7, ext1, ext1); /* * the first power we need is x^2, so copy it into r1 (our current * power of x register) */ copy_val(ext1, ext7, FALSE); /* * start with 1 in r5, the accumulator (the first term of the series * is the constant value 1) */ copy_val(ext5, get_one(), FALSE); /* * start at term n=2 in our expansion (the first term is just the * constant value 1, so we can start at the second term) */ n = 2; /* * the first term we calculate (i.e., the second term in the actual * series) is negative */ neg_term = TRUE; /* go until we have a precise enough value */ for (;;) { /* divide r1 by r3 to yield the next term in our series in r4 */ compute_quotient_into(ext4, 0, ext1, ext3); /* * if this value is too small to count in our accumulator, we're * done */ if (is_zero(ext4) || get_exp(ext5) - get_exp(ext4) > (int)get_prec(ext4)) break; /* * invert the sign of the term in r4 if this is a negative term */ if (neg_term) set_neg(ext4, !get_neg(ext4)); /* add r4 to our running accumulator in r5, yielding r6 */ compute_sum_into(ext6, ext5, ext4); /* swap r5 and r6 to put the accumulated value back into r5 */ tmp = ext5; ext5 = ext6; ext6 = tmp; /* * move on to the next term: multiply r1 by x^2 into r2 to yield * the next power of x that we require */ compute_prod_into(ext2, ext1, ext7); /* swap r1 and r2 to put our next required power back in r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; /* * multiply r3 by (n+1)*(n+2) - it currently has n!, so this * will yield (n+2)! */ mul_by_long(ext3, (n+1)*(n+2)); /* advance n to the next term */ n += 2; /* each term swaps signs */ neg_term = !neg_term; } /* we're done - store the result, rounding if necessary */ copy_val(new_ext, ext5, TRUE); } /* ------------------------------------------------------------------------ */ /* * add a value */ int CVmObjBigNum::add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { vm_val_t val2; /* convert it */ val2 = *val; if (!cvt_to_bignum(vmg_ self, &val2)) { /* this type is not convertible to BigNumber, so we can't add it */ err_throw(VMERR_BAD_TYPE_ADD); } /* push 'self' and the other value to protect against GC */ G_stk->push()->set_obj(self); G_stk->push(&val2); /* compute the sum */ compute_sum(vmg_ result, ext_, get_objid_ext(vmg_ val2.val.obj)); /* discard the GC protection items */ G_stk->discard(2); /* handled */ return TRUE; } /* * subtract a value */ int CVmObjBigNum::sub_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { vm_val_t val2; /* convert it */ val2 = *val; if (!cvt_to_bignum(vmg_ self, &val2)) { /* this type is not convertible to BigNumber, so we can't use it */ err_throw(VMERR_BAD_TYPE_SUB); } /* push 'self' and the other value to protect against GC */ G_stk->push()->set_obj(self); G_stk->push(&val2); /* compute the difference */ compute_diff(vmg_ result, ext_, get_objid_ext(vmg_ val2.val.obj)); /* discard the GC protection items */ G_stk->discard(2); /* handled */ return TRUE; } /* * multiply a value */ int CVmObjBigNum::mul_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { vm_val_t val2; /* convert it */ val2 = *val; if (!cvt_to_bignum(vmg_ self, &val2)) { /* this type is not convertible to BigNumber, so we can't add it */ err_throw(VMERR_BAD_TYPE_ADD); } /* push 'self' and the other value to protect against GC */ G_stk->push()->set_obj(self); G_stk->push(&val2); /* compute the product */ compute_prod(vmg_ result, ext_, get_objid_ext(vmg_ val2.val.obj)); /* discard the GC protection items */ G_stk->discard(2); /* handled */ return TRUE; } /* * divide a value */ int CVmObjBigNum::div_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { /* convert it */ vm_val_t val2 = *val; if (!cvt_to_bignum(vmg_ self, &val2)) { /* this type is not convertible to BigNumber, so we can't add it */ err_throw(VMERR_BAD_TYPE_ADD); } /* push 'self' and the other value to protect against GC */ G_stk->push()->set_obj(self); G_stk->push(&val2); /* compute the quotient */ compute_quotient(vmg_ result, ext_, get_objid_ext(vmg_ val2.val.obj)); /* discard the GC protection items */ G_stk->discard(2); /* handled */ return TRUE; } /* * negate the number */ int CVmObjBigNum::neg_val(VMG_ vm_val_t *result, vm_obj_id_t self) { char *new_ext; size_t prec = get_prec(ext_); /* * If I'm not an ordinary number or an infinity, return myself * unchanged. Note that we change sign for an infinity, even though * this might not make a great deal of sense mathematically. * * If I'm zero, likewise return myself unchanged. Negative zero is * still zero. */ if ((get_type(ext_) != VMBN_T_NUM && get_type(ext_) != VMBN_T_INF) || is_zero(ext_)) { /* return myself unchanged */ result->set_obj(self); return TRUE; } /* push a self-reference while we're working */ G_stk->push()->set_obj(self); /* if I'm an infinity, we don't need any precision in the result */ if (is_infinity(ext_)) prec = 1; /* create a new number with the same precision as the original */ result->set_obj(create(vmg_ FALSE, prec)); new_ext = get_objid_ext(vmg_ result->val.obj); /* make a copy in the new object */ memcpy(new_ext, ext_, calc_alloc(prec)); /* reverse the sign */ set_neg(new_ext, !get_neg(new_ext)); /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * check a value for equality */ int CVmObjBigNum::equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int /*depth*/) const { vm_val_t val2; int ret; /* if the other value is a reference to self, we certainly match */ if (val->typ == VM_OBJ && val->val.obj == self) return TRUE; /* convert it */ val2 = *val; if (!cvt_to_bignum(vmg_ self, &val2)) { /* this type is not convertible to BigNumber - it's not equal */ return FALSE; } /* push our values for safekeeping from the garbage collector */ G_stk->push()->set_obj(self); G_stk->push(&val2); /* check for equality and return the result */ ret = compute_eq_exact(ext_, get_objid_ext(vmg_ val2.val.obj)); /* discard the stacked values */ G_stk->discard(2); /* return the result */ return ret; } /* * Hash value calculation */ uint CVmObjBigNum::calc_hash(VMG_ vm_obj_id_t self, int /*depth*/) const { uint i; uint hash; /* add up the digits in the number */ for (hash = 0, i = 0 ; i < get_prec(ext_) ; ++i) { /* add this digit into the hash so far */ hash += get_dig(ext_, i); } /* add in the exponent as well */ hash += (uint)get_exp(ext_); /* return the combined hash */ return hash; } /* * compare to another value */ int CVmObjBigNum::compare_to(VMG_ vm_obj_id_t self, const vm_val_t *val) const { vm_val_t val2; /* convert it */ val2 = *val; if (!cvt_to_bignum(vmg_ self, &val2)) { /* this type is not convertible to BigNumber - it's not comparable */ err_throw(VMERR_INVALID_COMPARISON); } /* compare the values */ return compare_ext(ext_, get_objid_ext(vmg_ val2.val.obj)); } /* ------------------------------------------------------------------------ */ /* * Initialize for a computation involving two operands. Checks the * operands for non-number values; if either is NAN or INF, allocates a * result value that is the same non-number type and returns null. If * both are valid numbers, we'll allocate a result value with precision * equal to the greater of the precisions of the operands, and we'll * return a pointer to the new object's extension buffer. */ char *CVmObjBigNum::compute_init_2op(VMG_ vm_val_t *result, const char *ext1, const char *ext2) { size_t new_prec; char *new_ext; /* get the greater precision - this is the precision of the result */ new_prec = get_prec(ext1); if (get_prec(ext2) > new_prec) new_prec = get_prec(ext2); /* * if either operand is not an ordinary number, we need minimal * precision to represent the result, since we don't actually need * to store a number */ if (is_nan(ext1) || is_nan(ext2)) new_prec = 1; /* allocate a new object with the required precision */ result->set_obj(create(vmg_ FALSE, new_prec)); /* get the extension buffer */ new_ext = get_objid_ext(vmg_ result->val.obj); /* check the first value for NAN/INF conditions */ if (get_type(ext1) != VMBN_T_NUM) { /* set the result to the same non-number type and sign */ set_type(new_ext, get_type(ext1)); set_neg(new_ext, get_neg(ext1)); /* indicate that no further calculation is necessary */ return 0; } /* check the second number for NAN/INF conditions */ if (get_type(ext2) != VMBN_T_NUM) { /* set the result to the same non-number type and sign */ set_type(new_ext, get_type(ext2)); set_neg(new_ext, get_neg(ext2)); /* indicate that no further calculation is necessary */ return 0; } /* the operands are valid - return the new extension buffer */ return new_ext; } /* * Compute a sum */ void CVmObjBigNum::compute_sum(VMG_ vm_val_t *result, const char *ext1, const char *ext2) { char *new_ext; /* allocate our result value */ new_ext = compute_init_2op(vmg_ result, ext1, ext2); /* we're done if we had a non-number operand */ if (new_ext == 0) return; /* compute the sum into the result */ compute_sum_into(new_ext, ext1, ext2); } /* * Compute a difference */ void CVmObjBigNum::compute_diff(VMG_ vm_val_t *result, const char *ext1, const char *ext2) { /* allocate our result value */ char *new_ext = compute_init_2op(vmg_ result, ext1, ext2); /* we're done if we had a non-number operand */ if (new_ext == 0) return; /* compute the difference into the result object */ compute_diff_into(new_ext, ext1, ext2); } /* * Compute a product */ void CVmObjBigNum::compute_prod(VMG_ vm_val_t *result, const char *ext1, const char *ext2) { char *new_ext; /* allocate our result value */ new_ext = compute_init_2op(vmg_ result, ext1, ext2); /* we're done if we had a non-number operand */ if (new_ext == 0) return; /* compute the product */ compute_prod_into(new_ext, ext1, ext2); } /* * Compute a quotient */ void CVmObjBigNum::compute_quotient(VMG_ vm_val_t *result, const char *ext1, const char *ext2) { char *new_ext; /* allocate our result value */ new_ext = compute_init_2op(vmg_ result, ext1, ext2); /* we're done if we had a non-number operand */ if (new_ext == 0) return; /* compute the quotient */ compute_quotient_into(new_ext, 0, ext1, ext2); } /* * Determine if two values are equal, rounding the value with greater * precision to the shorter precision if the two are not of equal * precision */ int CVmObjBigNum::compute_eq_round(VMG_ const char *ext1, const char *ext2) { size_t prec1 = get_prec(ext1); size_t prec2 = get_prec(ext2); const char *shorter; const char *longer; char *tmp_ext; uint tmp_hdl; int ret; /* * allocate a temporary register with a rounded copy of the more * precise of the values */ if (prec1 > prec2) { /* the first one is longer */ longer = ext1; shorter = ext2; } else if (prec1 < prec2) { /* the second one is longer */ longer = ext2; shorter = ext1; } else { /* they're the same - do an exact comparison */ return compute_eq_exact(ext1, ext2); } /* get a temp register for rounding the longer value */ alloc_temp_regs(get_prec(shorter), 1, &tmp_ext, &tmp_hdl); /* make a rounded copy */ copy_val(tmp_ext, longer, TRUE); /* compare the rounded copy of the longer value to the shorter value */ ret = compute_eq_exact(shorter, tmp_ext); /* release the temporary register */ release_temp_regs(1, tmp_hdl); /* return the result */ return ret; } /* * Make an exact comparison for equality. If one value is more precise * than the other, we'll implicitly extend the shorter value to the * right with trailing zeros. */ int CVmObjBigNum::compute_eq_exact(const char *ext1, const char *ext2) { const char *longer; size_t min_prec; size_t max_prec; size_t prec1; size_t prec2; size_t idx; /* * if either is not an ordinary number, they are never equal to any * other value (note that this means INF != INF and NAN != NAN, * which is reasonable because these values cannot be meaningfully * compared; one NAN might mean something totally different from * another, and likewise various infinities are not comparable) */ if (is_nan(ext1) || is_nan(ext2)) return FALSE; /* figure out if one is more precise than the other */ prec1 = get_prec(ext1); prec2 = get_prec(ext2); if (prec1 > prec2) { /* ext1 is longer */ longer = ext1; max_prec = prec1; min_prec = prec2; } else if (prec2 > prec1) { /* ext2 is longer */ longer = ext2; max_prec = prec2; min_prec = prec1; } else { /* they're the same */ longer = 0; min_prec = max_prec = prec1; } /* if the signs aren't the same, the numbers are not equal */ if (get_neg(ext1) != get_neg(ext2)) return FALSE; /* if the exponents aren't equal, the numbers are not equal */ if (get_exp(ext1) != get_exp(ext2)) return FALSE; /* * compare digits up to the smaller precision, then make sure that * the larger-precision value's digits are all zeros from there out */ for (idx = 0 ; idx < min_prec ; ++idx) { /* if they don't match, return not-equal */ if (get_dig(ext1, idx) != get_dig(ext2, idx)) return FALSE; } /* check the longer one to make sure it's all zeros */ if (longer != 0) { /* scan the remainder of the longer one */ for ( ; idx < max_prec ; ++idx) { /* if this digit is non-zero, it's not a match */ if (get_dig(longer, idx) != 0) return FALSE; } } /* it's a match */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Round a value */ const char *CVmObjBigNum::round_val(VMG_ vm_val_t *new_val, const char *ext, size_t digits, int always_create) { /* presume we need rounding */ int need_round = TRUE; /* * if the value is already no longer than the requested precision, * return the original value; similarly, if we don't have to do any * rounding to truncate to the requested precision, do not change * the original object; likewise, don't bother changing anything if * it's not a number */ if (get_type(ext) != VMBN_T_NUM || !get_round_dir(ext, digits)) { if (always_create) { /* * we must create a new object regardless, but it won't need * rounding */ need_round = FALSE; } else { /* return the original value */ new_val->set_nil(); return ext; } } /* allocate a new object with the requested precision */ new_val->set_obj(create(vmg_ FALSE, digits)); char *new_ext = get_objid_ext(vmg_ new_val->val.obj); /* copy the sign, exponent, and type information */ set_prec(new_ext, digits); set_neg(new_ext, get_neg(ext)); set_exp(new_ext, get_exp(ext)); set_type(new_ext, get_type(ext)); /* * if we don't need rounding, just truncate the old mantissa and * return the result */ if (!need_round) { /* if the new size is smaller, truncate it */ if (digits <= get_prec(ext)) { /* copy the mantissa up to the requested new size */ memcpy(new_ext + VMBN_MANT, ext + VMBN_MANT, (digits + 1)/2); } else { /* it's growing - simply copy the old value */ copy_val(new_ext, ext, FALSE); } /* return the new value */ return new_ext; } /* copy the mantissa up to the requested new precision */ memcpy(new_ext + VMBN_MANT, ext + VMBN_MANT, (digits + 1)/2); /* round it up */ round_up_abs(new_ext, digits); /* return the new extension */ return new_ext; } /* * Convert a value to a big number value */ int CVmObjBigNum::cvt_to_bignum(VMG_ vm_obj_id_t self, vm_val_t *val) const { /* if it's an integer, convert it to a BigNum value */ if (val->typ == VM_INT) { /* * put my own value on the stack to ensure I'm not garbage * collected when creating the new object */ G_stk->push()->set_obj(self); /* it's an integer - convert it to a BigNum */ val->set_obj(create(vmg_ FALSE, (long)val->val.intval, 32)); /* done protecting my object reference */ G_stk->discard(); } /* if it's not a BigNumber object, we can't handle it */ if (val->typ != VM_OBJ || (vm_objp(vmg_ val->val.obj)->get_metaclass_reg() != metaclass_reg_)) { /* indicate that conversion was unsuccessful */ return FALSE; } /* successful conversion */ return TRUE; } qtads-2.1.7/tads3/vmbignum.h000066400000000000000000002113411265017072300156550ustar00rootroot00000000000000/* $Header$ */ /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbignum.h - big number metaclass Function Notes Modified 02/18/00 MJRoberts - Creation */ #ifndef VMBIGNUM_H #define VMBIGNUM_H #include #include #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" /* ------------------------------------------------------------------------ */ /* * Big number temporary register cache. Because big number values are * of arbitrary precision, we can't know in advance how much space we'll * need for temporary values. Instead, we keep this cache; each time we * need a temporary register, we'll look to see if we have one available * with sufficient precision, and allocate a new register if not. */ /* internal register descriptor */ struct CVmBigNumCacheReg { /* clear out the register values */ void clear() { buf_ = 0; siz_ = 0; nxt_ = 0; } /* * Allocate memory for this register. Returns true if we had to * allocate memory, false if the register was already at the given * size. */ int alloc_mem(size_t siz) { /* * if I'm already at least this large, there's no need to change * anything */ if (siz_ >= siz) return FALSE; /* * round up the size a bit - this will avoid repeatedly * reallocating at slightly different sizes, which could * fragment the heap quite a bit; we'll use a little more memory * than the caller actually asked for, but if they come back and * ask for slightly more next time, an additional allocation * probably won't be necessary, which will save memory in the * long run */ siz = (siz + 63) & ~63; /* delete any existing memory */ free_mem(); /* remember the new size */ siz_ = siz; /* allocate the memory */ buf_ = (char *)t3malloc(siz_); /* indicate that we allocated memory */ return TRUE; } /* free the memory associated with the register, if any */ void free_mem() { /* if we have a buffer, delete it */ if (buf_ != 0) t3free(buf_); } /* register's buffer */ char *buf_; /* size of register's buffer */ size_t siz_; /* next register in list */ CVmBigNumCacheReg *nxt_; }; /* * register cache */ class CVmBigNumCache { public: CVmBigNumCache(size_t max_regs); ~CVmBigNumCache(); /* * Allocate a temporary register with a minimum of the given byte * size. Returns a pointer to the register's buffer, and fills in * '*hdl' with the handle of the register, which must be used to * release the register. */ char *alloc_reg(size_t siz, uint *hdl); /* release a previously-allocated register */ void release_reg(uint hdl); /* release all registers */ void release_all(); /* * Get a special dedicated constant value register, reallocating it * to the required precision if it's not already available at the * required precision or greater. We'll set '*expanded' to true if * we had to expand the register, false if not. */ /* get the constant ln(10) register */ char *get_ln10_reg(size_t siz, int *expanded) { return alloc_reg(&ln10_, siz, expanded); } /* get the constant ln(2) register */ char *get_ln2_reg(size_t siz, int *expanded) { return alloc_reg(&ln2_, siz, expanded); } /* get the constant pi register */ char *get_pi_reg(size_t siz, int *expanded) { return alloc_reg(&pi_, siz, expanded); } /* get the constant e register */ char *get_e_reg(size_t siz, int *expanded) { return alloc_reg(&e_, siz, expanded); } /* get the DBL_MAX register */ char *get_dbl_max_reg(size_t siz, int *expanded) { return alloc_reg(&dbl_max_, siz, expanded); } /* get the DBL_MIN register */ char *get_dbl_min_reg(size_t siz, int *expanded) { return alloc_reg(&dbl_min_, siz, expanded); } private: /* make sure a register is allocated to a given size, and return it */ char *alloc_reg(CVmBigNumCacheReg *reg, size_t siz, int *expanded) { /* make sure the register satisfies the size requested */ *expanded = reg->alloc_mem(siz); /* return the register's buffer */ return reg->buf_; } /* our register array */ CVmBigNumCacheReg *reg_; /* head of free register list */ CVmBigNumCacheReg *free_reg_; /* head of unallocated register list */ CVmBigNumCacheReg *unalloc_reg_; /* constant register for ln(10) */ CVmBigNumCacheReg ln10_; /* constant register for ln(2) */ CVmBigNumCacheReg ln2_; /* constant register for pi */ CVmBigNumCacheReg pi_; /* constant register for e */ CVmBigNumCacheReg e_; /* constant registers for DBL_MAX and DBL_MIN */ CVmBigNumCacheReg dbl_max_; CVmBigNumCacheReg dbl_min_; /* maximum number of registers we can create */ size_t max_regs_; }; /* ------------------------------------------------------------------------ */ /* * We store a BigNumber value as a varying-length string of BCD-encoded * digits; we store two digits in each byte. Our bytes are stored from * most significant to least significant, and each byte has the more * significant half in the high part of the byte. * * UINT2 number_of_digits *. INT2 exponent *. BYTE flags *. BYTE most_significant_byte *. ... *. BYTE least_significant_byte * * Note that the number of bytes of the varying length mantissa string * is equal to (number_of_digits+1)/2, because one byte stores two * digits. * * The flags are: * * (flags & 0x0001) - sign bit; zero->non-negative, nonzero->negative * * (flags & 0x0006): *. 0x0000 -> normal number *. 0x0002 -> NOT A NUMBER *. 0x0004 -> INFINITY (sign bit indicates sign of infinity) *. 0x0006 -> reserved for future use * * (flags & 0x0008) - zero bit; if set, the number's value is zero * * All other flag bits are reserved and should be set to zero. * * The exponent field gives the base-10 exponent of the number. This is * a signed quantity; a negative value indicates that the mantissa is to * be divided by (10 ^ abs(exponent)), and a positive value indicates * that the mantissa is to be multiplied by (10 ^ exponent). * * There is an implicit decimal point before the first byte of the * mantissa. */ /* byte offsets in byte string of various parts */ #define VMBN_PREC 0 #define VMBN_EXP 2 #define VMBN_FLAGS 4 #define VMBN_MANT 5 /* flags masks */ #define VMBN_F_NEG 0x0001 /* negative sign bit flag */ #define VMBN_F_TYPE_MASK 0x0006 /* number type mask */ #define VMBN_F_ZERO 0x0008 /* zero flag */ /* number types */ #define VMBN_T_NUM 0x0000 /* ordinary number */ #define VMBN_T_NAN 0x0002 /* NOT A NUMBER */ #define VMBN_T_INF 0x0004 /* INFINITY (negative or positive) */ #define VMBN_T_RSRVD 0x0006 /* reserved */ /* ------------------------------------------------------------------------ */ /* * Flags for cvt_to_string */ /* always show a sign, even if positive */ #define VMBN_FORMAT_SIGN 0x0001 /* always use exponential format */ #define VMBN_FORMAT_EXP 0x0002 /* always show a sign in the exponent */ #define VMBN_FORMAT_EXP_SIGN 0x0004 /* always show at least a zero before the decimal point */ #define VMBN_FORMAT_LEADING_ZERO 0x0008 /* always show a decimal point */ #define VMBN_FORMAT_POINT 0x0010 /* show the exponential 'e' (if any) in upper-case */ #define VMBN_FORMAT_EXP_CAP 0x0020 /* insert commas to denote thousands, millions, etc */ #define VMBN_FORMAT_COMMAS 0x0020 /* show a leading space if the number is positive */ #define VMBN_FORMAT_POS_SPACE 0x0040 /* use European-style formatting */ #define VMBN_FORMAT_EUROSTYLE 0x0080 /* * Use scientific notation if it's more compact, a la C printf 'g' format. * This automatically uses exponent notation if the exponent is less then * -4 or greater than or equal to the number of digits after the decimal * point. */ #define VMBN_FORMAT_COMPACT 0x0100 /* max_digits counts only significant digits, not leading zeros */ #define VMBN_FORMAT_MAXSIG 0x0200 /* keep trailing zeros to fill out the max_digits count */ #define VMBN_FORMAT_TRAILING_ZEROS 0x0400 /* ------------------------------------------------------------------------ */ /* * BigNumber metaclass - intrinsic function vector indices */ enum vmobjbn_meta_fnset { /* undefined function */ VMOBJBN_UNDEF = 0, /* format to a string */ VMOBJBN_FORMAT = 1, /* equal after rounding? */ VMOBJBN_EQUAL_RND = 2, /* getPrecision */ VMOBJBN_GET_PREC = 3, /* setPrecision */ VMOBJBN_SET_PREC = 4, /* getFraction */ VMOBJBN_FRAC = 5, /* getWhole */ VMOBJBN_WHOLE = 6, /* round to a given number of decimal places */ VMOBJBN_ROUND_DEC = 7, /* absolute value */ VMOBJBN_ABS = 8, /* ceiling */ VMOBJBN_CEIL = 9, /* floor */ VMOBJBN_FLOOR = 10, /* getScale */ VMOBJBN_GETSCALE = 11, /* scale */ VMOBJBN_SCALE = 12, /* negate */ VMOBJBN_NEGATE = 13, /* copySignFrom */ VMOBJBN_COPYSIGN = 14, /* isNegative */ VMOBJBN_ISNEG = 15, /* getRemainder */ VMOBJBN_REMAINDER = 16, /* sine */ VMOBJBN_SIN = 17, /* cosine */ VMOBJBN_COS = 18, /* sine */ VMOBJBN_TAN = 19, /* degreesToRadians */ VMOBJBN_D2R = 20, /* radiansToDegrees */ VMOBJBN_R2D = 21, /* arcsine */ VMOBJBN_ASIN = 22, /* arccosine */ VMOBJBN_ACOS = 23, /* arctangent */ VMOBJBN_ATAN = 24, /* sqrt */ VMOBJBN_SQRT = 25, /* natural log */ VMOBJBN_LN = 26, /* exp */ VMOBJBN_EXP = 27, /* log10 */ VMOBJBN_LOG10 = 28, /* power */ VMOBJBN_POW = 29, /* hyperbolic sine */ VMOBJBN_SINH = 30, /* hyperbolic cosine */ VMOBJBN_COSH = 31, /* hyperbolic tangent */ VMOBJBN_TANH = 32, /* get pi (static method) */ VMOBJBN_GET_PI = 33, /* get e (static method) */ VMOBJBN_GET_E = 34, /* numType */ VMOBJN_numType = 35 }; /* ------------------------------------------------------------------------ */ /* * String formatter output buffer interface. */ class IBigNumStringBuf { public: /* * Get the string buffer to use, given the required string length. If * it's not possible to get a buffer of the required size, returns * null, in which case the formatter will return failure as well. * * The formatter always fills in the first two bytes of the result * buffer with a VMB_LEN length prefix, TADS-string style. The space * for the two-byte prefix is included in the 'need' value passed. */ virtual char *get_buf(size_t need) = 0; }; /* * Simple output buffer with a fixed, pre-allocated string. */ class CBigNumStringBufFixed: public IBigNumStringBuf { public: CBigNumStringBufFixed(char *buf, size_t len) : buf(buf), len(len) { } char *get_buf(size_t need) { return len >= need ? buf : 0; } char *buf; size_t len; }; /* * Output buffer using an optional pre-allocated buffer, allocating a new * CVmObjString object if the fixed buffer isn't big enough. The caller * can recover the new string value from our 'strval' member after the * formatting is done; this is set to nil if the fixed buffer provided is * sufficient. */ class CBigNumStringBufAlo: public CBigNumStringBufFixed { public: /* create with no buffer - always allocates a new string */ CBigNumStringBufAlo(VMG_ vm_val_t *strval) : CBigNumStringBufFixed(0, 0), strval(strval) { vmg = VMGLOB_ADDR; if (strval != 0) strval->set_nil(); } /* * create with a buffer - uses the buffer if it's big enough, otherwise * allocates a new String object, storing the object in our 'strval' * member */ CBigNumStringBufAlo(VMG_ vm_val_t *strval, char *buf, size_t len) : CBigNumStringBufFixed(buf, len), strval(strval) { vmg = VMGLOB_ADDR; if (strval != 0) strval->set_nil(); } /* get the buffer */ char *get_buf(size_t need); /* the caller's OUT variable to receive the allocated string, if needed */ vm_val_t *strval; /* stashed globals */ vm_globals *vmg; }; /* ------------------------------------------------------------------------ */ /* * we have to forward-declare bignum_t, since it uses templates */ template class bignum_t; /* ------------------------------------------------------------------------ */ /* * Big Number metaclass */ class CVmObjBigNum: public CVmObject { friend class CVmMetaclassBigNum; template friend class bignum_t; friend class vbignum_t; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* initialize/terminate the BigNumber cache */ static void init_cache(); static void term_cache(); /* * write to a 'data' mode file - returns zero on success, non-zero on * I/O or other error */ int write_to_data_file(class CVmDataSource *fp); /* * Read from a 'data' mode file, creating a new BigNumber object to * hold the result. Returns zero on success, non-zero on failure. On * success, *retval will be filled in with the new BigNumber object. */ static int read_from_data_file( VMG_ vm_val_t *retval, class CVmDataSource *fp); /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* call a static property */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop); /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* create with a given precision */ static vm_obj_id_t create(VMG_ int in_root_set, size_t digits); /* create from a given integer value */ static vm_obj_id_t create(VMG_ int in_root_set, long val, size_t digits); /* create from a given unsigned integer value */ static vm_obj_id_t createu(VMG_ int in_root_set, ulong val, size_t digits); /* create from a given string value, with a given precision */ static vm_obj_id_t create(VMG_ int in_root_set, const char *str, size_t len, size_t digits); /* create from a double, with enough precision for a native double */ static vm_obj_id_t create(VMG_ int in_root_set, double val); /* create from a double, with a given precision */ static vm_obj_id_t create(VMG_ int in_root_set, double val, size_t digits); /* create from a bignum_t */ template static vm_obj_id_t create( VMG_ int in_root_set, const bignum_t *b) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ prec); CVmObjBigNum *n = (CVmObjBigNum *)vm_objp(vmg_ id); copy_val(n->ext_, b->ext, FALSE); return id; } /* create from a bignum_t */ template static vm_obj_id_t create( VMG_ int in_root_set, const bignum_t &b) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ prec); CVmObjBigNum *n = (CVmObjBigNum *)vm_objp(vmg_ id); copy_val(n->ext_, b.ext, FALSE); return id; } /* * Create from a 64-bit integer in portable little-endian notation. * This is the 64-bit int equivalent of osrp2(), osrp4(), etc - the * name is meant to parallel those names. The buffer must be stored in * our standard portable format: little-endian, two's complement. * create_rp8() treats the value as unsigned, create_rp8s() treats it * as signed. */ static vm_obj_id_t create_rp8(VMG_ int in_root_set, const char *buf); static vm_obj_id_t create_rp8s(VMG_ int in_root_set, const char *buf); /* * Create from a 64-bit integer expressed as two 32-bit portions. We * currently only provide an unsigned version, because (a) we don't * have an immediate need for signed, and (b) breaking a signed 64-bit * value into two 32-bit partitions requires making assumptions about * the native integer representation (2's complement, 1s' complement, * etc) that we'll probably have to farm out to os_xxx code. Until we * encounter a need we'll avoid the signed version. The unsigned * version will work with any binary integer representation, which * makes it all but universal (it won't work on a BCD machine, for * example... but would anyone ever notice, or care?). */ static vm_obj_id_t create_int64(VMG_ int in_root_set, uint32_t hi, uint32_t lo); /* create from a BER-encoded compressed unsigned integer buffer */ static vm_obj_id_t create_from_ber(VMG_ int in_root_set, const char *buf, size_t len); /* create from an IEEE 754-2008 binary interchange format buffer */ static vm_obj_id_t create_from_ieee754( VMG_ int in_root_set, const char *buf, int bits); /* * create from a string value, using the precision required to hold the * value in the string */ static vm_obj_id_t create(VMG_ int in_root_set, const char *str, size_t len); /* * create from a string value in a given radix, using the precision * required to hold the value in the string */ static vm_obj_id_t create_radix(VMG_ int in_root_set, const char *str, size_t len, int radix); /* determine if an object is a BigNumber */ static int is_bignum_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* * Cast a value to a BigNumber. We can convert strings, integers, and * of course BigNumber values. Fills in bnval with the BigNumber * value, which will be newly allocated if the source isn't already a * BigNumber (in which case bnval is just a copy of srcval). */ static void cast_to_bignum(VMG_ vm_val_t *bnval, const vm_val_t *srcval); /* * Parse a string value into an extension buffer. If radix is 0, we * infer the radix using radix_from_string() rules. */ static void parse_str_into(char *ext, const char *str, size_t len); static void parse_str_into(char *ext, const char *str, size_t len, int radix); /* parse a string, allocating a buffer with 'new char[]' */ static char *parse_str_alo(size_t &buflen, const char *str, size_t len, int radix); /* * Get the precision required to hold the number with the given string * representation. If radix is 0, we infer the radix using * radix_from_string() rules. */ static int precision_from_string(const char *str, size_t len); static int precision_from_string(const char *str, size_t len, int radix); /* * Parse a string to determine its radix. If the string starts with * '0x' or '0', and contains no decimal point or 'E' exponent * indicator, it's a hex or octal integer, respectively. Otherwise * it's a decimal integer or floating point value. */ static int radix_from_string(const char *str, size_t len); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* undo operations - we are immutable and hence keep no undo */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { } void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* mark references - we have no references so this does nothing */ void mark_refs(VMG_ uint) { } /* remove weak references */ void remove_stale_weak_refs(VMG0_) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t) { ext_ = (char *)ptr; } /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixup); /* add a value */ int add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* subtract a value */ int sub_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* multiply */ int mul_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* divide */ int div_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* negate */ int neg_val(VMG_ vm_val_t *result, vm_obj_id_t self); /* get the absolute value */ int abs_val(VMG_ vm_val_t *result, vm_obj_id_t self); /* get the sgn value (-1 if negative, 0 if zero, 1 if positive) */ int sgn_val(VMG_ vm_val_t *result, vm_obj_id_t self); /* check a value for equality */ int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const; /* calculate a hash value for the number */ uint calc_hash(VMG_ vm_obj_id_t self, int depth) const; /* compare to another value */ int compare_to(VMG_ vm_obj_id_t /*self*/, const vm_val_t *) const; /* * Create a string representation of the number. We'll use a * default format that uses no more than a maximum number of * characters to represent the string. We'll avoid exponential * format if possible, but we'll fall back on exponential format if * the non-exponential result would exceed our default maximum * length. */ virtual const char *cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const; /* * Explicitly cast to string with a given radix */ virtual const char *explicit_to_string( VMG_ vm_obj_id_t self, vm_val_t *new_str, int radix, int flags) const; /* * Static method to convert big number data to a string. We'll create * a new string object and store a reference in new_str, returning a * pointer to its data buffer. * * max_digits is the maximum number of digits we should produce. If * our precision is greater than this would allow, we'll round. If we * have more digits before the decimal point than this would allow, * we'll use exponential notation. * * If the flag VMBN_FORMAT_MAXSIG is set, max_digits is taken as the * maximum number of *significant* digits to display. That is, we won't * count leading zeros against the maximum. * * whole_places is the number of places before the decimal point that * we should produce. This is a minimum; if we need more places (and * we're not in exponential notation), we'll take the additional * places. If, however, we don't manage to fill this quota, we'll pad * with spaces to the left. We ignore whole_places in exponential * format. * * frac_digits is the number of digits after the decimal point that we * should produce. We'll round if we have more precision than this * would allow, or pad with zeros if we don't have enough precision. * If frac_digits is -1, we will produce as many fractional digits as * we need up to the max_digits limit. * * If the VMBN_FORMAT_EXP flag isn't set, we'll format the number * without an exponent as long as we have enough space in max_digits * for the part before the decimal point, and we have enough space in * max_digits and frac_digits that a number with a small absolute value * wouldn't show up as all zeros. * * If the VMBN_FORMAT_POINT flag is set, we'll show a decimal point for * all numbers. Otherwise, if frac_digits is zero, or frac_digits is * -1 and the number has no fractional part, we'll suppress the decimal * point. This doesn't matter when frac_digits is greater than zero, * or it's -1 and there's a fractional part to display. * * If exp_digits is non-zero, it specifies the minimum number of digits * to display in the exponent. We'll pad with zeros to make this many * digits if necessary. * * If lead_fill is provided, it must be a string value. We'll fill the * string with the characters from this string, looping to repeat the * string if necessary. If this string isn't provided, we'll use * leading spaces. This is only needed if the whole_places value * requires us to insert filler. * * Note that VMG_ can be passed as VMGNULL_ (null global context) for * the cvt_to_string_buf() functions IF 'new_str' and 'lead_fill' are * null. */ static const char *cvt_to_string( VMG_ vm_obj_id_t self, vm_val_t *new_str, const char *ext, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags, vm_val_t *lead_fill); /* format the value into the given buffer */ const char *cvt_to_string_buf( char *buf, size_t buflen, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags); /* format BigNumber data (without a 'self') into the given buffer */ static const char *cvt_to_string_buf( char *buf, size_t buflen, const char *ext, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags, const char *lead_fill, size_t lead_fill_len); /* * format the value into the given buffer, or into a new String if the * value overflows the buffer */ const char *cvt_to_string_buf( VMG_ vm_val_t *new_str, char *buf, size_t buflen, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags); /* * format a regular integer value into a buffer as though it were a * BigNumber value */ static const char *cvt_int_to_string_buf( char *buf, size_t buflen, int32_t intval, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags); /* format as an integer in a given radix */ const char *cvt_to_string_in_radix( VMG_ vm_obj_id_t self, vm_val_t *new_str, int radix) const; /* get the fractional part/whole part */ static void compute_frac(char *ext); static void compute_whole(char *ext); /* compute a sum */ static void compute_sum(VMG_ vm_val_t *result, const char *ext1, const char *ext2); /* compute a difference */ static void compute_diff(VMG_ vm_val_t *result, const char *ext1, const char *ext2); /* compute a product */ static void compute_prod(VMG_ vm_val_t *result, const char *ext1, const char *ext2); /* compute a quotient */ static void compute_quotient(VMG_ vm_val_t *result, const char *ext1, const char *ext2); /* compute a remainder */ static void compute_rem(VMG_ vm_val_t *result, const char *ext1, const char *ext2); /* compute a natural logarithm */ static void compute_ln_into(char *dst, const char *src); /* compute e^x */ static void compute_exp_into(char *dst, const char *src); /* compute a hyperbolic sine or cosine */ static void compute_sinhcosh_into(char *dst, const char *src, int is_cosh, int is_tanh); /* * Determine if two values are exactly equal. If one value has more * precision than the other, we'll implicitly extend the shorter * value with trailing zeros. */ static int compute_eq_exact(const char *ext1, const char *ext2); /* * Determine if two values are equal with rounding. If one value is * less precise than the other, we'll round the more precise value * to the shorter precision, and compare the shorter number to the * rounded longer number. */ static int compute_eq_round(VMG_ const char *ext1, const char *ext2); /* * Create a rounded value, rounding to the given precision. If * always_create is true, we'll create a new number regardless of * whether rounding is required; otherwise, when the caller can * simply treat the old value as truncated, we'll set new_val to nil * and return the original value. */ static const char *round_val(VMG_ vm_val_t *new_val, const char *ext, size_t digits, int always_create); /* set my value to a given integer value */ static void set_int_val(char *ext, long val); static void set_uint_val(char *ext, ulong val); /* set from a portable little-endian 64-bit unsigned int buffer */ void set_rp8(const uchar *p); /* set my value to the given double */ static void set_double_val(char *ext, double val); /* set my value to a given string */ void set_str_val(const char *str, size_t len); void set_str_val(const char *str, size_t len, int radix); /* get my data pointer */ char *get_ext() const { return ext_; } /* BigNumber is a numeric type */ virtual int is_numeric() const { return TRUE; } /* cast to integer */ virtual long cast_to_int(VMG0_) const { /* return the integer conversion */ return convert_to_int(); } /* get my integer value */ virtual int get_as_int(long *val) const { /* return the integer conversion */ *val = convert_to_int(); return TRUE; } /* get my double value */ virtual int get_as_double(VMG_ double *val) const { *val = convert_to_double(); return TRUE; } /* promote an integer to a BigNumber */ virtual void promote_int(VMG_ vm_val_t *val) const; /* * Convert to an integer value (signed or unsigned). We set 'ov' to * true if the value overflows the integer type. */ int32_t convert_to_int(int &ov) const { return ext_to_int(ext_, ov); } uint32_t convert_to_uint(int &ov) const; /* * convert to integer; sets 'ov' to true if the result doesn't fit an * int32 */ static int32_t ext_to_int(const char *ext, int &ov); /* convert to integer, throwing an error on overflow */ int32_t convert_to_int() const { int ov; int32_t l = convert_to_int(ov); if (ov) err_throw(VMERR_NUM_OVERFLOW); return l; } uint32_t convert_to_uint() const { int ov; uint32_t l = convert_to_uint(ov); if (ov) err_throw(VMERR_NUM_OVERFLOW); return l; } /* cast to numeric */ virtual void cast_to_num(VMG_ vm_val_t *val, vm_obj_id_t self) const { /* we're already numeric - just return myself unchanged */ val->set_obj(self); } /* convert to double */ double convert_to_double() const { return ext_to_double(ext_); } static double ext_to_double(const char *ext); /* * Convert to the IEEE 754-2008 binary interchange format with the * given bit size. These are a portable formats, which are not * necessarily the same as any of the local system's native floating * point types (although most modern hardware does use this format for * its native types, modulo byte order). The defined sizes are 16, 32, * 64, and 128 bits; 32 bits corresponds to the single-precision (C * "float") type, and 64 bits corresponds to the double-precision (C * "double") type. We assume that the buffer is big enough for the * requested bit size. We use the standard IEEE format, *except* that * we use little-endian byte order for consistency with the TADS * pack/unpack formats. */ void convert_to_ieee754(VMG_ char *buf, int bits, int &ov); /* set the value from an IEEE 754 buffer */ void set_ieee754_value(VMG_ const char *buf, int bits); /* * Write a 64-bit integer representation, using the standard TADS * portable integer format: 8-bit bytes, little-endian, two's * complement. These are analogous to the osifc routines oswp2(), * oswp2s(), etc., thus the names. * * (Why don't we need more osifc routines for these? Because BigNumber * internal representation is all under the control of the portable * code, and the byte buffer representation is portable as well. We * don't need osifc code for a portable-to-portable conversion. The * integer routines do need osifc code because they convert to and from * the local platform integer format, which obviously varies. Even * that could be done portably in principle, by using integer * arithmetic; but we don't do it that way, because osifc code can be * ruthlessly efficient on most platforms by directly accessing the * byte structure of the local int type rather than doing a bunch of * arithmetic. There's no such efficiency gain possible with * BigNumbers, which aren't native anywhere.) */ void wp8(char *buf, int &ov) const; void wp8s(char *buf, int &ov) const; /* write a BER compressed integer representation */ void encode_ber(char *buf, size_t buflen, size_t &result_len, int &ov); protected: /* create with no extension */ CVmObjBigNum(); /* create with a given precision */ CVmObjBigNum(VMG_ size_t digits); /* create with a given precision, initializing with an integer value */ CVmObjBigNum(VMG_ long val, size_t digits); /* create with a given precision, initializing with a string value */ CVmObjBigNum(VMG_ const char *str, size_t len, size_t digits); /* create with a given precision, initializing with a double value */ CVmObjBigNum(VMG_ double val, size_t digits); /* convert a value to a BigNumber */ int cvt_to_bignum(VMG_ vm_obj_id_t self, vm_val_t *val) const; /* get the magnitude of the integer conversion, ignoring the sign bit */ static uint32_t convert_to_int_base(const char *ext, int &ov); /* * general string conversion routine */ static const char *cvt_to_string_gen( IBigNumStringBuf *buf, const char *ext, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags, const char *lead_fill, size_t lead_fill_len); /* property evaluator - undefined property */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - formatString */ int getp_format(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - equalRound */ int getp_equal_rnd(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - getPrecision */ int getp_get_prec(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - setPrecision */ int getp_set_prec(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - getFraction */ int getp_frac(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - getWhole */ int getp_whole(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - roundToDecimal */ int getp_round_dec(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - absolute value */ int getp_abs(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - ceiling */ int getp_ceil(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - floor */ int getp_floor(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - getScale */ int getp_get_scale(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - scale */ int getp_scale(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - negate */ int getp_negate(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - copySignFrom */ int getp_copy_sign(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - isNegative */ int getp_is_neg(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - remainder */ int getp_remainder(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - sine */ int getp_sin(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - cosine */ int getp_cos(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - tangent */ int getp_tan(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - radiansToDegrees */ int getp_rad2deg(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - degreesToRadians */ int getp_deg2rad(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - arcsine */ int getp_asin(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - arccosine */ int getp_acos(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - arcsine */ int getp_atan(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - square root */ int getp_sqrt(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - natural log */ int getp_ln(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - exp */ int getp_exp(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - log10 */ int getp_log10(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - power */ int getp_pow(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - hyperbolic sine */ int getp_sinh(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - hyperbolic cosine */ int getp_cosh(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - hyperbolic tangent */ int getp_tanh(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - get pi */ int getp_pi(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc) { return s_getp_pi(vmg_ val, argc); } /* property evaluator - get e */ int getp_e(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc) { return s_getp_e(vmg_ val, argc); } /* property evaluator - get the number type */ int getp_numType(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc); /* static property evaluator - get pi */ static int s_getp_pi(VMG_ vm_val_t *val, uint *argc); /* static property evaluator - get e */ static int s_getp_e(VMG_ vm_val_t *val, uint *argc); /* set up for a getp operation with zero arguments */ int setup_getp_0(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, char **new_ext); /* set up for a getp operation with one argument */ int setup_getp_1(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, char **new_ext, vm_val_t *val2, const char **val2_ext, int use_self_prec); /* set up a return value for a getp operation */ int setup_getp_retval(VMG_ vm_obj_id_t self, vm_val_t *retval, char **new_ext, size_t prec); /* common property evaluator for asin and acos */ int calc_asincos(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int is_acos); /* common property evaluator - sinh, cosh, and tanh */ int calc_sinhcosh(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int is_cosh, int is_tanh); /* calculate asin or acos into the given buffer */ static void calc_asincos_into(char *new_ext, const char *ext, int is_acos); /* * Calculate the arcsin series expansion; valid only for small * values of x (0 < x < 1/sqrt(2)). The argument value is in ext1, * and we return a pointer to the register containing the result. */ static char *calc_asin_series(char *ext1, char *ext2, char *ext3, char *ext4, char *ext5); /* * Compute the ln series expansion. This is valid only for small * arguments; the argument is in ext1 initially. Returns a pointer * to the register containing the result */ static char *compute_ln_series_into(char *ext1, char *ext2, char *ext3, char *ext4, char *ext5); /* allocate space for a given number of decimal digits of precision */ void alloc_bignum(VMG_ size_t prec); /* calculate how much space we need for a given precision */ static size_t calc_alloc(size_t prec) { return (2 + 2 + 1) + ((prec + 1)/2); } /* initialize a computation for a two-operand operator */ static char *compute_init_2op(VMG_ vm_val_t *result, const char *ext1, const char *ext2); /* compute a square root */ static void compute_sqrt_into(char *new_ext, const char *ext); /* compute the sum of two operands into the given buffer */ static void compute_sum_into(char *new_ext, const char *ext1, const char *ext2); /* * Compute the sum of the absolute values of the operands into the * given buffer. The result is always positive. The result buffer * must have a precision at least as large as the larger of the two * input precisions. */ static void compute_abs_sum_into(char *new_ext, const char *ext1, const char *ext2); /* compute the difference of two operands into the given buffer */ static void compute_diff_into(char *new_ext, const char *ext1, const char *ext2); /* * Compute the difference of the absolute values of the operands * into the given buffer. The result is positive if the first value * is larger than the second, negative if the first value is smaller * than the second. The result buffer must have precision at least * as large as the larger of the two input precisions. */ static void compute_abs_diff_into(char *new_ext, const char *ext1, const char *ext2); /* * Compute the product of the two values into the given buffer. The * result buffer must have precision at least as large as the larger * of the two input precisions. */ static void compute_prod_into(char *new_ext, const char *ext1, const char *ext2); /* * Compute the quotient of the two values into the given buffer. If * new_rem_ext is not null, we'll store the remainder there. */ static void compute_quotient_into(char *new_ext, char *new_rem_ext, const char *ext1, const char *ext2); /* compare extensions - return 0 if equal, <0 if a0 if a>b */ static int compare_ext(const char *a, const char *b); /* * Compare the absolute values of two numbers. If the first is * greater than the second, we'll return a positive result. If the * two are equal, we'll return zero. If the first is less than the * second, we'll return a negative result. This routine ignores NAN * and INF values, so the caller must ensure that only ordinary * numbers are passed to this routine. */ static int compare_abs(const char *ext1, const char *ext2); /* get/set the digit precision */ static size_t get_prec(const char *ext) { return osrp2(ext + VMBN_PREC); } static void set_prec(char *ext, size_t prec) { oswp2(ext + VMBN_PREC, prec); } /* get/set the exponent */ static int get_exp(const char *ext) { return osrp2s(ext + VMBN_EXP); } static void set_exp(char *ext, int exp) { oswp2s(ext + VMBN_EXP, exp); } /* get the negative sign flag */ static int get_neg(const char *ext) { return (ext[VMBN_FLAGS] & VMBN_F_NEG) != 0; } /* set/clear negative sign flag */ static void set_neg(char *ext, int neg) { if (neg) ext[VMBN_FLAGS] |= VMBN_F_NEG; else ext[VMBN_FLAGS] &= ~VMBN_F_NEG; } /* get the number type */ static int get_type(const char *ext) { return ext[VMBN_FLAGS] & VMBN_F_TYPE_MASK; } /* set the number type (to a VMBN_T_xxx value) */ static void set_type(char *ext, int typ) { /* clear the old number type */ ext[VMBN_FLAGS] &= ~VMBN_F_TYPE_MASK; /* set the new number type */ ext[VMBN_FLAGS] |= typ; } /* get a digit at a particular index (0 = most significant) */ static unsigned int get_dig(const char *ext, size_t i) { /* get the digit pair containing our digit */ unsigned int pair = ext[VMBN_MANT + i/2]; /* * If it's an even index, we need the high half. Otherwise, we * need the low half. * * This is a bit tricky, all to avoid a condition branch. If * the index is even, (i & 1) will be 0, otherwise (i & 1) will * be 1. So, (1 - (i & 1)) will be 1 if even, 0 if odd. That * quantity shifted left twice will hence be 4 if the index is * even, 0 if the index is odd. Thus, we'll shift the pair * right by 4 if the index is even, yielding the high part, or * shift right by 0 if the index is odd, keeping the low part. */ pair >>= ((1 - (i & 1)) << 2); /* mask to one digit */ return (pair & 0x0f); } /* set a digit at a particular index */ static void set_dig(char *ext, size_t i, unsigned int dig) { unsigned char mask; /* make sure our input digit is just a digit */ dig &= 0x0F; /* * If it's an even index, we need to store our digit in the high * half. Otherwise, we need to store it in the low half. So, * if we're storing in an even index, shift our number left 4 * bits so that it's in the high half of its low byte; * otherwise, leave the number as-is. */ dig <<= ((1 - (i & 1)) << 2); /* * We need a mask that we can AND the current value with to * preserve the half we're not changing, but clear the other * half. So, we need 0x0F if we're setting the high half (even * index), or 0xF0 if we're setting the low half (odd index). * Use the same trick as above, with the shift sense inverted, * so generate our mask. */ mask = (0x0F << ((i & 1) << 2)); /* mask out our part from the pair */ ext[VMBN_MANT + i/2] &= mask; /* OR in our digit now that we've masked the place clear */ ext[VMBN_MANT + i/2] |= (unsigned char)dig; } /* shift mantissa left/right, leaving the exponent unchanged */ static void shift_left(char *ext, unsigned int shift); static void shift_right(char *ext, unsigned int shift); /* multiply a number by a long integer value */ static void mul_by_long(char *ext, unsigned long val); /* * Divide a number by a long integer value. * * Important: 'val' must not exceed ULONG_MAX/10. Our algorithm * computes integer dividends from ext's digits; these can range up to * 10*val and have to fit in a ulong, thus the ULONG_MAX/10 limit on * val. */ static void div_by_long(char *ext, unsigned long val, unsigned long *remp = 0); /* divide by 2^32 */ static void div_by_2e32(char *ext, uint32_t *remainder); /* * store the portable 64-bit integer representation of the absolute * value of this number in the given buffer */ void wp8abs(char *buf, int &ov) const; /* compute the 2's complement of a p8 (64-bit little-endian int) buffer */ static void twos_complement_p8(unsigned char *buf); /* increment a number's absolute value */ static void increment_abs(char *ext); /* round for digits dropped during a calculation */ static void round_for_dropped_digits( char *ext, int trail_dig, int trail_val); /* get the OR sum of digits from 'd' to least significant */ static int get_ORdigs(const char *ext, int d); /* round an extension to the nearest integer */ static void round_to_int(char *ext); /* round an extension to the given number of digits */ static void round_to(char *ext, int digits); /* * get the rounding direction for rounding to the given number of * digits: 1 means round up, 0 means round down, so you can simply add * the return value to the last digit you're keeping */ static int get_round_dir(const char *ext, int digits); /* * round up: add 1 to the least significant digit of the number we're * keeping (if not specified, we're keep all digits) */ static void round_up_abs(char *ext); static void round_up_abs(char *ext, int keep_digits); /* * copy a value - if the new value has greater precision than the * old value, we'll extend with zeros in the new least significance * digits; if the new value has smaller precision than the old * value, and 'round' is false, we'll simply truncate the value to * the new precision. If 'round' is true and we're reducing the * precision, we'll round up the value instead of truncating it. */ static void copy_val(char *dst, const char *src, int round); /* normalize a number */ static void normalize(char *ext); /* set a number to zero */ static void set_zero(char *ext) { /* set the exponent to one */ set_exp(ext, 1); /* set the zero flag */ ext[VMBN_FLAGS] |= VMBN_F_ZERO; /* set the sign to non-negative */ set_neg(ext, FALSE); /* set the type to ordinary number */ set_type(ext, VMBN_T_NUM); /* set the mantissa to all zeros */ memset(ext + VMBN_MANT, 0, (get_prec(ext) + 1)/2); } /* determine if the number equals zero */ static int is_zero(const char *ext) { return (ext[VMBN_FLAGS] & VMBN_F_ZERO) != 0; } /* negate a value */ static void negate(char *ext) { /* only change the sign if the value is non-zero */ if (!is_zero(ext)) { /* it's not zero - invert the sign */ set_neg(ext, !get_neg(ext)); } } /* make a value negative */ static void make_negative(char *ext) { /* only set the sign if the value is non-zero */ if (!is_zero(ext)) set_neg(ext, TRUE); } /* check to see if the fractional part is zero */ static int is_frac_zero(const char *ext); /* * check for NAN and INF conditions - returns true if the number is * a NAN or INF, false if it's an ordinary number */ static int is_nan(const char *ext) { /* if it's anything but an ordinary number, indicate NAN */ return (get_type(ext) != VMBN_T_NUM); } /* check for infinities */ static int is_infinity(const char *ext) { return get_type(ext) == VMBN_T_INF; } /* calculate a Taylor series for sin(x) */ void calc_sin_series(VMG_ char *new_ext, char *ext1, char *ext2, char *ext3, char *ext4, char *ext5, char *ext6, char *ext7); /* calculate a Taylor series for cos(x) */ void calc_cos_series(VMG_ char *new_ext, char *ext1, char *ext2, char *ext3, char *ext4, char *ext5, char *ext6, char *ext7); /* * given an object number known to refer to a CVmObjBigNum object, get * the object's extension */ static char *get_objid_ext(VMG_ vm_obj_id_t obj_id) { /* get the object pointer, cast it, and get the extension */ return get_objid_obj(vmg_ obj_id)->get_ext(); } /* * given an object number known to refer to a CVmObjBigNum object, get * the object pointer */ static CVmObjBigNum *get_objid_obj(VMG_ vm_obj_id_t obj_id) { /* get the object pointer and cast it */ return (CVmObjBigNum *)vm_objp(vmg_ obj_id); } /* allocate a temporary register */ static char *alloc_temp_reg(size_t prec, uint *hdl); /* * Allocate a set of temporary registers; throws an error on * failure. For each register, there is an additional pair of * arguments: a (char **) to receive a pointer to the register * memory, and a (uint *) to receive the register handle. */ static void alloc_temp_regs(size_t prec, size_t cnt, ...); /* * Release a set of temporary registers. For each register, there * is a uint argument giving the handle of the register to release. */ static void release_temp_regs(size_t cnt, ...); /* release a temporary register */ static void release_temp_reg(uint hdl); /* * Get the natural logarithm of 10 to the required precision. We'll * return the cached value if available, or compute and cache the * constant to (at least) the required precision if not. */ static const char *cache_ln10(size_t prec); /* get ln(2) */ static const char *cache_ln2(size_t prec); /* cache pi to the required precision */ static const char *cache_pi(size_t prec); /* cache e to the required precision */ static const char *cache_e(size_t prec); /* get the constant value 1 */ static const char *get_one() { return (const char *)one_; } /* cache DBL_MAX, DBL_MIN */ static const char *cache_dbl_max(); static const char *cache_dbl_min(); /* constant value 1 */ static const unsigned char one_[]; /* property evaluation function table */ static int (CVmObjBigNum::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassBigNum: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "bignumber/030001"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjBigNum(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjBigNum(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjBigNum::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjBigNum::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; /* ------------------------------------------------------------------------ */ /* * A C++ template class for doing BigNumber arithmetic on the stack with * fixed-precision buffers. This can be used for high-precision arithmetic * in C++ without creating any garbage-collected BigNumber objects. It's * not quite as flexible as the BigNumber class itself, but it allows C++ * code to perform decimal arithmetic with higher precision than doubles * when needed. */ template class bignum_t { friend class CVmObjBigNum; public: bignum_t() { init(); } bignum_t(long i) { init(); set(i); } bignum_t(double d) { init(); set(d); } bignum_t(VMG_ const vm_val_t *val) { init(); set(vmg_ val); } template bignum_t(const bignum_t b) { init(); set(b); } template bignum_t(const bignum_t *b) { init(); set(*b); } void set(long i) { CVmObjBigNum::set_int_val(ext, i); } void set(double d) { CVmObjBigNum::set_double_val(ext, d); } template void set(const bignum_t &b) { CVmObjBigNum::copy_val(ext, b.ext, TRUE); } void set(VMG_ const vm_val_t *val) { CVmObjBigNum *b; if (val->typ == VM_INT) CVmObjBigNum::set_int_val(ext, val->val.intval); else if ((b = vm_val_cast(CVmObjBigNum, val)) != 0) CVmObjBigNum::copy_val(ext, b->get_ext(), TRUE); else if (val->is_numeric(vmg0_)) CVmObjBigNum::set_double_val(ext, val->num_to_double(vmg0_)); else err_throw(VMERR_NUM_VAL_REQD); } /* cast to int/double */ operator int32_t() { int ov; int32_t ret = CVmObjBigNum::ext_to_int(ext, ov); if (ov) err_throw(VMERR_NUM_OVERFLOW); return ret; } operator double() { return CVmObjBigNum::ext_to_double(ext); } /* * addition operators */ bignum_t operator +(long l) const { bignum_t bl(l); return *this + bl; } bignum_t operator +(double d) const { bignum_t bd(d); return *this + bd; } template bignum_t operator +(const bignum_t &b) const { bignum_t<(precb > prec ? precb : prec)> result; CVmObjBigNum::compute_sum_into(result.ext, ext, b.ext); return result; } bignum_t &operator +=(long l) { set(*this + l); return *this; } bignum_t &operator +=(double d) { set(*this + d); return *this; } template bignum_t &operator +=(bignum_t &b) { set(*this + b); return *this; } template bignum_t &operator +=(bignum_t b) { set(*this + b); return *this; } /* * subtraction operators */ bignum_t operator -(long l) const { bignum_t bl(l); return *this - bl; } bignum_t operator -(double d) const { bignum_t bd(d); return *this - bd; } template bignum_t operator -(bignum_t &b) const { bignum_t<(precb > prec ? precb : prec)> result; CVmObjBigNum::compute_diff_into(result.ext, ext, b.ext); return result; } bignum_t &operator -=(long l) { set(*this - l); return *this; } bignum_t &operator -=(double d) { set(*this - d); return *this; } template bignum_t &operator -=(bignum_t &b) { set(*this - b); return *this; } template bignum_t &operator -=(bignum_t b) { set(*this - b); return *this; } /* * Negation */ bignum_t operator -() { bignum_t result(this); CVmObjBigNum::negate(result.ext); return result; } /* * multiplication operators */ bignum_t operator *(long l) const { bignum_t bl(l); return *this * bl; } bignum_t operator *(double d) const { bignum_t bd(d); return *this * bd; } template bignum_t operator *(bignum_t &b) const { bignum_t<(precb > prec ? precb : prec)> result; CVmObjBigNum::compute_prod_into(result.ext, ext, b.ext); return result; } bignum_t &operator *=(long l) { set(*this * l); return *this; } bignum_t &operator *=(double d) { set(*this * d); return *this; } template bignum_t &operator *=(bignum_t &b) { set(*this * b); return *this; } template bignum_t &operator *=(bignum_t b) { set(*this * b); return *this; } /* * division operators */ bignum_t operator /(long l) const { bignum_t bl(l); return *this / bl; } bignum_t operator /(double d) const { bignum_t bd(d); return *this / bd; } template bignum_t operator /(bignum_t &b) const { bignum_t<(precb > prec ? precb : prec)> result; CVmObjBigNum::compute_quotient_into(result.ext, 0, ext, b.ext); return result; } bignum_t &operator /=(long l) { set(*this / l); return *this; } bignum_t &operator /=(double d) { set(*this / d); return *this; } template bignum_t &operator /=(bignum_t &b) { set(*this / b); return *this; } template bignum_t &operator /=(bignum_t b) { set(*this / b); return *this; } /* * modulo operators */ bignum_t operator %(long l) const { bignum_t bl(l); return *this % bl; } bignum_t operator %(double d) const { bignum_t bd(d); return *this % bd; } template bignum_t operator %(bignum_t &b) const { bignum_t<(precb > prec ? precb : prec)> quo, rem; CVmObjBigNum::compute_quotient_into(quo.ext, rem.ext, ext, b.ext); return rem; } template bignum_t &operator %=(bignum_t &b) { set(*this % b); return *this; } template bignum_t &operator %=(bignum_t b) { set(*this % b); return *this; } /* * formatting */ void format(char *buf, size_t buflen) { CVmObjBigNum::cvt_to_string_buf( buf, buflen, ext, -1, -1, -1, -1, VMBN_FORMAT_POINT, 0, 0); } void format(char *buf, size_t buflen, int maxdigits, int fracdigits) { CVmObjBigNum::cvt_to_string_buf( buf, buflen, ext, maxdigits, -1, fracdigits, -1, VMBN_FORMAT_POINT, 0, 0); } protected: void init() { oswp2(ext, prec); ext[VMBN_FLAGS] = 0; } char ext[VMBN_MANT + (prec+1)/2]; }; /* * Yet another C++ BigNumber adapter class, this time for variable * precision BigNumber values allocated on the C++ heap. This is similar * to bignum_t, but allows the precision to be determined at * run-time. The trade-off is that this type must be allocated on the heap * due to the dynamic precision. */ class vbignum_t { public: /* create from native types */ vbignum_t(long l) { init(10); set(l); } vbignum_t(ulong l) { init(10); set(l); } vbignum_t(double d) { init(18); set(d); } vbignum_t(const char *str, size_t len) { init(CVmObjBigNum::precision_from_string(str, len)); CVmObjBigNum::parse_str_into(ext_, str, len); } vbignum_t(const char *str, size_t len, int radix) { init(CVmObjBigNum::precision_from_string(str, len, radix)); CVmObjBigNum::parse_str_into(ext_, str, len, radix); } /* delete */ ~vbignum_t() { delete [] ext_; } /* set from native types */ void set(long l) { CVmObjBigNum::set_int_val(ext_, l); } void set(ulong l) { CVmObjBigNum::set_uint_val(ext_, l); } void set(double d) { CVmObjBigNum::set_double_val(ext_, d); } /* get my precision in digits */ int prec() const { return CVmObjBigNum::get_prec(ext_); } /* is the value zero? */ int is_zero() const { return CVmObjBigNum::is_zero(ext_); } /* get the value as an int32; sets 'ov' to true on overflow */ int32_t to_int(int &ov) { return CVmObjBigNum::ext_to_int(ext_, ov); } /* format */ const char *format(IBigNumStringBuf *buf) const { return CVmObjBigNum::cvt_to_string_gen( buf, ext_, -1, -1, -1, -1, VMBN_FORMAT_POINT, 0, 0); } const char *format(IBigNumStringBuf *buf, int maxdigits, int fracdigits) const { return CVmObjBigNum::cvt_to_string_gen( buf, ext_, maxdigits, -1, fracdigits, -1, VMBN_FORMAT_POINT, 0, 0); } const char *format(char *buf, size_t buflen) const { return CVmObjBigNum::cvt_to_string_buf( buf, buflen, ext_, -1, -1, -1, -1, VMBN_FORMAT_POINT, 0, 0); } const char *format(char *buf, size_t buflen, int maxdigits, int fracdigits) const { return CVmObjBigNum::cvt_to_string_buf( buf, buflen, ext_, maxdigits, -1, fracdigits, -1, VMBN_FORMAT_POINT, 0, 0); } /* cast to int/double */ operator int32_t() { int ov; int32_t ret = CVmObjBigNum::ext_to_int(ext_, ov); if (ov) err_throw(VMERR_NUM_OVERFLOW); return ret; } operator double() { return CVmObjBigNum::ext_to_double(ext_); } /* compare - returns 0 if equal, <0 if this0 if this>b */ int compare(const vbignum_t &b) const { return CVmObjBigNum::compare_ext(ext_, b.ext_); } /* addition */ vbignum_t *operator +(long l) const { vbignum_t bl(l); return *this + bl; } vbignum_t *operator +(const vbignum_t &b) const { vbignum_t *sum = new vbignum_t(); sum->init(maxprec(this, &b)); CVmObjBigNum::compute_sum_into(sum->ext_, ext_, b.ext_); return sum; } /* subtraction */ vbignum_t *operator -(long l) const { vbignum_t bl(l); return *this - bl; } vbignum_t *operator -(const vbignum_t &b) const { vbignum_t *res = new vbignum_t(); res->init(maxprec(this, &b)); CVmObjBigNum::compute_diff_into(res->ext_, ext_, b.ext_); return res; } /* multiplication */ vbignum_t *operator *(long l) const { vbignum_t bl(l); return *this * bl; } vbignum_t *operator *(const vbignum_t &b) const { vbignum_t *res = new vbignum_t(); res->init(maxprec(this, &b)); CVmObjBigNum::compute_prod_into(res->ext_, ext_, b.ext_); return res; } /* division */ vbignum_t *operator /(long l) const { vbignum_t bl(l); return *this / bl; } vbignum_t *operator /(const vbignum_t &b) const { vbignum_t *res = new vbignum_t(), rem; res->init(maxprec(this, &b)); rem.init(2); CVmObjBigNum::compute_quotient_into(res->ext_, rem.ext_, ext_, b.ext_); return res; } /* remainder */ vbignum_t *operator %(long l) const { vbignum_t bl(l); return *this % bl; } vbignum_t *operator %(const vbignum_t &b) const { vbignum_t *res = new vbignum_t(), quo; res->init(maxprec(this, &b)); quo.init(2); CVmObjBigNum::compute_quotient_into(quo.ext_, res->ext_, ext_, b.ext_); return res; } /* get the higher of two numbers' precisions */ static int maxprec(const vbignum_t *a, const vbignum_t *b) { return a->prec() > b->prec() ? a->prec() : b->prec(); } protected: /* create with no extension */ vbignum_t() { ext_ = 0; } /* initialize with a given precision, allocating the extension */ void init(int prec) { ext_ = new char[CVmObjBigNum::calc_alloc(prec)]; oswp2(ext_, prec); ext_[VMBN_FLAGS] = 0; } /* extension buffer - allocated with new char[prec] */ char *ext_; }; #endif /* VMBIGNUM_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjBigNum) qtads-2.1.7/tads3/vmbignumlib.cpp000066400000000000000000003716661265017072300167200ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* Copyright (c) 2012 by Michael J. Roberts. All Rights Reserved. */ /* Name vmbignumlib.cpp - BigNumber numerics library Function This module contains the low-level numerics library for the BigNumber object. The routines here operate on the internal byte-array representation of a BigNumber value, without the CVmObjBigNum wrapper object, so this module can be used as a library for high-precision arithmetic without dragging in the whole TADS 3 VM. Notes Modified 06/20/12 MJRoberts - Creation */ #include #include #include #include #include #include #include #include "t3std.h" #include "utf8.h" #include "vmtype.h" #include "vmbignum.h" /* ------------------------------------------------------------------------ */ /* * convert to a string, storing the result in the given buffer */ const char *CVmObjBigNum::cvt_to_string_buf( char *buf, size_t buflen, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags) { /* convert to a string into our buffer */ CBigNumStringBufFixed alo(buf, buflen); return cvt_to_string_gen(&alo, ext_, max_digits, whole_places, frac_digits, exp_digits, flags, 0, 0); } /* * Convert to a string, storign the result in the given buffer */ const char *CVmObjBigNum::cvt_to_string_buf( char *buf, size_t buflen, const char *ext, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags, const char *lead_fill, size_t lead_fill_len) { /* convert to a string into our buffer */ CBigNumStringBufFixed alo(buf, buflen); return cvt_to_string_gen(&alo, ext, max_digits, whole_places, frac_digits, exp_digits, flags, lead_fill, lead_fill_len); } /* * Convert an integer value to a string, storing the result in the buffer. * This can be used to apply the whole set of floating-point formatting * options to an ordinary integer value, without going to the trouble of * creating a BigNumber object. */ const char *CVmObjBigNum::cvt_int_to_string_buf( char *buf, size_t buflen, int32_t intval, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags) { /* * Set up a stack register with the number. Allow 20 digits of * precision so that this can extend to 64-bit ints in the future. */ char tmp[5 + 20] = { 20, 0, 0, 0, 0 }; set_int_val(tmp, intval); /* * set the actual precision to equal the exponent, since zeros after * the decimal point aren't significant for an integer source */ set_prec(tmp, get_exp(tmp)); /* convert to a string into our buffer */ CBigNumStringBufFixed alo(buf, buflen); return cvt_to_string_gen(&alo, tmp, max_digits, whole_places, frac_digits, exp_digits, flags, 0, 0); } /* * Common routine to create a string representation. If buf is null, * we'll allocate a new string object, filling in new_str with the * object reference; otherwise, we'll format into the given buffer. */ const char *CVmObjBigNum::cvt_to_string_gen( IBigNumStringBuf *bufalo, const char *ext, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags, const char *lead_fill, size_t lead_fill_len) { char plus_sign = '\0'; int always_sign_exp = ((flags & VMBN_FORMAT_EXP_SIGN) != 0); int always_exp = ((flags & VMBN_FORMAT_EXP) != 0); int lead_zero = ((flags & VMBN_FORMAT_LEADING_ZERO) != 0); int always_show_pt = ((flags & VMBN_FORMAT_POINT) != 0); int exp_caps = ((flags & VMBN_FORMAT_EXP_CAP) != 0); int pos_lead_space = ((flags & VMBN_FORMAT_POS_SPACE) != 0); int commas = ((flags & VMBN_FORMAT_COMMAS) != 0); int euro = ((flags & VMBN_FORMAT_EUROSTYLE) != 0); size_t req_chars; int prec = (int)get_prec(ext); int exp = get_exp(ext); int dig_before_pt; int dig_after_pt; int idx; unsigned int dig; char *p; int exp_carry; int show_pt; int whole_padding; int non_sci_digs; char decpt_char = (euro ? ',' : '.'); char comma_char = (euro ? '.' : ','); char *tmp_ext = 0; uint tmp_hdl; int max_sig = ((flags & VMBN_FORMAT_MAXSIG) != 0); int all_out_cnt, sig_out_cnt; /* we haven't written out any digits yet */ all_out_cnt = sig_out_cnt = 0; /* note the sign character to use for positive numbers, if any */ if (pos_lead_space) plus_sign = ' '; else if ((flags & VMBN_FORMAT_SIGN) != 0) plus_sign = '+'; /* * If we're not required to use exponential notation, but we don't * have enough max_digits places for the part before the decimal * point, use exponential anyway. (The number of digits before the * decimal point is simply the exponent if it's greater than zero, * or zero otherwise.) */ if (max_digits >= 0 && exp > max_digits) always_exp = TRUE; /* * If we're not required to use exponential notation, but our * absolute value is so small that we wouldn't show anything * "0.00000..." (i.e., we'd have too many zeros after the decimal * point to show any actual digits of our number), use exponential * notation. If our exponent is negative, its absolute value is the * number of zeros we'd show after the decimal point before the * first non-zero digit. */ if (max_digits >= 0 && exp < 0 && (-exp > max_digits || (frac_digits != -1 && -exp > frac_digits))) always_exp = TRUE; /* * If we're the "compact" mode flag is set, use scientific notation if * the displayed decimal exponent is less than -4 or greater than the * number of digits after the decimal point. (The displayed exponent * is one lower than the internal exponent, since the normalized format * has no digits before the decimal point but the displayed scientific * notation format shows one whole-part digit.) */ if ((flags & VMBN_FORMAT_COMPACT) != 0 && (exp < -3 || (max_digits >= 0 && exp - 1 > max_digits))) always_exp = TRUE; /* calculate how many digits we'd need in non-scientific notation */ if (exp < 0) { /* we have leading zeros before the first significant digit */ non_sci_digs = -exp + prec; } else if (exp > prec) { /* we have trailing zeros after the last significant digit */ non_sci_digs = exp + prec; } else { /* * we have no leading or trailing zeros to represent - only the * digits actually stored need to be displayed */ non_sci_digs = prec; } /* * Figure out how much space we need for our string. Start with the * length of the basic digit string, which is the smaller of max_digits * or the actual space we need for non-scientific notation. */ if (max_digits >= 0 && max_digits < non_sci_digs) { /* max_digits limits the number of characters we can show */ req_chars = max_digits; /* *. ..but if it only counts significant digits, and the exponent * is negative, we might also have to show all those leading zeros * over and above the max_digits limit */ if (max_sig && exp < 0) req_chars += -exp; } else { /* there's no cap on digits, so we could have to show them all */ req_chars = non_sci_digs; } /* * Now add overhead space for the sign symbol, a leading zero, a * decimal point, an 'E' for the exponent, an exponent sign, and up to * five digits for the exponent (16-bit integer -> -32768 to 32767). * Also allow an extra character in case we need to add a digit due to * rounding. */ req_chars += 11; /* * Make sure we leave enough room for the lead fill string - if they * specified a number of whole places, and we're not using * exponential format, we'll insert lead fill characters before the * first non-zero whole digit. */ if (!always_exp && whole_places != -1 && exp < whole_places) { int extra; int char_size; /* * if the exponent is negative, we'll pad by the full amount; * otherwise, we'll just pad by the difference between the * number of places needed and the exponent */ extra = whole_places; if (exp > 0) extra -= exp; /* * Add the extra bytes: one byte per character if we're using * the default space padding, or up to three bytes per character * if a lead string was specified (each unicode character can * take up to three bytes) */ char_size = (lead_fill != 0 ? 3 : 1); req_chars += extra * char_size; /* * add space for each padding character we could insert into a * comma position (there's at most one comma per three fill * characters) */ if (commas) req_chars += ((extra + 2)/3) * char_size; } /* * If we're using commas, and we're not using scientific notation, * add space for a comma for each three digits before the decimal * point */ if (commas && !always_exp) { /* add space for the commas */ req_chars += ((exp + 2) / 3); } /* * if they requested a specific minimum number of exponent digits, * and that number is greater than the allowance of 5 we already * provided, add the extra space needed */ if (exp_digits > 5) req_chars += (exp_digits - 5); /* * If they requested a specific number of digits after the decimal * point, make sure we have room for those digits. */ if (frac_digits != -1) req_chars += frac_digits; /* now that we know how much space we need, get the output buffer */ char *buf = bufalo->get_buf(req_chars + VMB_LEN); /* if that failed, return failure */ if (buf == 0) return 0; /* check for special values */ switch(get_type(ext)) { case VMBN_T_NUM: /* normal number - proceed */ break; case VMBN_T_NAN: /* not a number - show "1.#NAN" */ strcpy(buf + VMB_LEN, "1.#NAN"); vmb_put_len(buf, 6); return buf; case VMBN_T_INF: /* positive or negative infinity */ if (get_neg(ext)) { memcpy(buf + VMB_LEN, "-1.#INF", 7); vmb_put_len(buf, 7); } else { memcpy(buf + VMB_LEN, "1.#INF", 6); vmb_put_len(buf, 6); } return buf; default: /* other values are not valid */ memcpy(buf + VMB_LEN, "1.#???", 6); vmb_put_len(buf, 6); return buf; } /* * Allocate a temporary buffer to contain a copy of the number. At * most, we'll have to show max_digits of the number, or the current * precision, whichever is lower. */ { int new_prec; /* * limit the new precision to the maximum digits to be shown, or * our existing precision, whichever is lower */ if (max_digits < 0) new_prec = prec; else { new_prec = max_digits; if (prec < new_prec) new_prec = prec; } /* allocate the space */ alloc_temp_regs((size_t)new_prec, 1, &tmp_ext, &tmp_hdl); /* copy the value to the temp register, rounding the value */ copy_val(tmp_ext, ext, TRUE); /* note the new precision */ prec = new_prec; /* forget the original number and use the rounded version */ ext = tmp_ext; } start_over: /* * note the exponent, in case we rounded or otherwise adjusted the * temporary number */ exp = get_exp(ext); /* presume we won't carry into the exponent */ exp_carry = FALSE; /* no whole-part padding yet */ whole_padding = 0; /* * Figure out where the decimal point goes in the display. If we're * displaying in exponential format, we'll always display exactly * one digit before the decimal point. Otherwise, we'll display the * number given by our exponent if it's positive, or zero or one * (depending on lead_zero) if it's negative or zero. */ if (always_exp) dig_before_pt = 1; else if (exp > 0) dig_before_pt = exp; else dig_before_pt = 0; /* * if the digits before the decimal point exceed our maximum number * of digits allowed, we'll need to use exponential format */ if (max_digits >= 0 && dig_before_pt > max_digits) { always_exp = TRUE; goto start_over; } /* * Limit digits after the decimal point according to the maximum digits * allowed and the number we'll show before the decimal point. If we * have an explicit number of fractional digits to show, that overrides * the limit. */ if (max_digits >= 0) dig_after_pt = max_digits - dig_before_pt; else if (frac_digits >= 0) dig_after_pt = frac_digits; else if (prec > dig_before_pt) dig_after_pt = prec - dig_before_pt; else dig_after_pt = 0; /* start writing after the buffer length prefix */ p = buf + VMB_LEN; /* * if we're not in exponential mode, add leading spaces for the * unused whole places */ if (!always_exp && dig_before_pt < whole_places) { int cnt; size_t src_rem; utf8_ptr src; utf8_ptr dst; int idx; /* start with the excess whole places */ cnt = whole_places - dig_before_pt; /* if we're going to add a leading zero, that's one less space */ if (dig_before_pt == 0 && lead_zero) --cnt; /* * Increase the count by the number of comma positions in the * padding area. */ if (commas) { /* scan over the positions to fill and count commas */ for (idx = dig_before_pt ; idx < whole_places ; ++idx) { /* if this is a comma position, note it */ if ((idx % 3) == 0) ++cnt; } } /* set up our read and write pointers */ src.set((char *)lead_fill); src_rem = lead_fill_len; dst.set(p); /* add (and count) the leading spaces */ for ( ; cnt != 0 ; --cnt, ++whole_padding) { wchar_t ch; /* * if we have a lead fill string, read from it; otherwise, * just use a space */ if (lead_fill != 0) { /* if we've exhausted the string, start over */ if (src_rem == 0) { src.set((char *)lead_fill); src_rem = lead_fill_len; } /* get the next character */ ch = src.getch(); /* skip this character */ src.inc(&src_rem); } else { /* no lead fill string - insert a space by default */ ch = ' '; } /* write this character */ dst.setch(ch); } /* resynchronize from our utf8 pointer */ p = dst.getptr(); } /* * if the number is negative, or we're always showing a sign, add * the sign; if we're not adding a sign, but we're showing a leading * space for positive numbers, add the leading space */ if (get_neg(ext)) *p++ = '-'; else if (plus_sign != '\0') *p++ = plus_sign; /* * if we have no digits before the decimal, and we're adding a * leading zero, add it now */ if (dig_before_pt == 0 && lead_zero) { /* add the leading zero */ *p++ = '0'; /* * If we have a total digit limit, reduce the limit on the digits * after the decimal point, since this leading zero comes out of * our overall digit budget. This doesn't apply if max_digits is * the number of significant digits. */ if (max_digits >= 0 && !max_sig) --dig_after_pt; /* count it in the output counter for all digits */ all_out_cnt++; } /* * If we have limited the number of digits that we'll allow after the * decimal point, due to the limit on the total number of digits and * the number of digits we need to display before the decimal, start * over in exponential format to ensure we get the after-decimal * display we want. * * Note that we won't bother going into exponential format if the * number of digits before the decimal point is zero or one, because * exponential format won't give us any more room - in such cases we * simply have an impossible request. */ if (max_digits >=0 && !always_exp && frac_digits != -1 && dig_after_pt < frac_digits && dig_before_pt > 1) { /* switch to exponential format and start over */ always_exp = TRUE; goto start_over; } /* display the digits before the decimal point */ for (idx = 0 ; idx < dig_before_pt && idx < prec ; ++idx) { /* * if this isn't the first digit, and we're adding commas, and * an even multiple of three more digits follow, insert a comma */ if (idx != 0 && commas && !always_exp && ((dig_before_pt - idx) % 3) == 0) *p++ = comma_char; /* get this digit */ dig = get_dig(ext, idx); /* add it to the string */ *p++ = (dig + '0'); /* count it */ all_out_cnt++; if (dig != 0 || sig_out_cnt != 0) sig_out_cnt++; } /* if we ran out of precision, add zeros */ for ( ; idx < dig_before_pt ; ++idx) { /* add the zero */ *p++ = '0'; /* count it */ all_out_cnt++; if (sig_out_cnt != 0) sig_out_cnt++; } /* * Add the decimal point. Show the decimal point unless * always_show_pt is false, and either frac_digits is zero, or * frac_digits is -1 and we have no fractional part. */ if (always_show_pt) { /* always showing the point */ show_pt = TRUE; } else { if (frac_digits > 0) { /* we're showing fractional digits - always show a point */ show_pt = TRUE; } else if (frac_digits == 0) { /* we're showing no fractional digits, so suppress the point */ show_pt = FALSE; } else { /* * for now assume we'll show the point; we'll take it back * out if we don't encounter anything but zeros */ show_pt = TRUE; } } /* if we're showing the fractional part, show it */ if (show_pt) { int frac_len; int frac_lim; char *last_non_zero; /* * remember the current position as the last trailing non-zero - * if we don't find anything but zeros and decide to remove the * trailing zeros, we'll also remove the decimal point by * coming back here */ last_non_zero = p; /* add the point */ *p++ = decpt_char; /* if we're always showing a decimal point, we can't remove it */ if (always_show_pt) last_non_zero = p; /* if frac_digits is -1, there's no limit */ if (frac_digits == -1) frac_lim = dig_after_pt; else frac_lim = frac_digits; /* * further limit the fractional digits according to the maximum * digit allowance */ if (frac_lim > dig_after_pt) frac_lim = dig_after_pt; /* no fractional digits output yet */ frac_len = 0; /* * if we haven't yet reached the first non-zero digit, display * as many zeros as necessary */ if (idx == 0 && exp < 0) { int cnt; /* * display leading zeros based no the exponent: if exp is * zero, we don't need any; if exp is -1, we need one; if * exp is -2, we need two, and so on */ for (cnt = exp ; cnt != 0 && frac_len < frac_lim ; ++cnt) { /* add a zero */ *p++ = '0'; /* * if we're counting all digits (not just significant * digits), count this digit against the total */ if (!max_sig) ++frac_len; /* count it in the total digits out */ all_out_cnt++; } } /* add the fractional digits */ for ( ; idx < prec && frac_len < frac_lim ; ++idx, ++frac_len) { /* get this digit */ dig = get_dig(ext, idx); /* add it */ *p++ = (dig + '0'); /* * if it's not a zero, note the location - if we decide to * trim trailing zeros, we'll want to keep at least this * much, since this is a significant trailing digit */ if (dig != 0) last_non_zero = p; /* count it */ all_out_cnt++; if (dig != 0 || sig_out_cnt != 0) sig_out_cnt++; } /* * If the "keep trailing zeros" flag is set, and there's a * max_digits quota that we haven't filled, add trailing zeros as * needed. */ if ((flags & VMBN_FORMAT_TRAILING_ZEROS) != 0 && max_digits >= 0) { /* * fill out the request: if max_digits is stated in significant * digits, count the number of significant digits we've written * so far, otherwise count all digits written so far */ for (int i = max_sig ? sig_out_cnt : all_out_cnt ; i < max_digits ; ++i) { /* add a digit */ *p++ = '0'; /* count it */ all_out_cnt++; if (sig_out_cnt != 0) sig_out_cnt++; } } /* * add the trailing zeros if we ran out of precision before we * filled the requested number of places */ if (frac_digits != -1) { /* fill out the remaining request length with zeros */ for ( ; frac_len < frac_lim ; ++frac_len) { /* add a zero */ *p++ = '0'; /* count it */ all_out_cnt++; if (sig_out_cnt != 0) sig_out_cnt++; } } else if (!(flags & VMBN_FORMAT_TRAILING_ZEROS)) { char *p1; /* * if we're removing the decimal point, we're not showing a * fractional part after all - so note */ if (last_non_zero < p && *last_non_zero == decpt_char) show_pt = FALSE; /* * We can use whatever length we like, so remove meaningless * trailing zeros. Before we do this, though, make sure we * aren't rounding up the last trailing zero - if we need to * round the last digit up, the final zero is really a 1. */ if (p > last_non_zero && get_round_dir(ext, idx)) { /* * That last zero is significant after all, since we're * going to round it up to a 1 for display. Don't actually * do the rounding now; simply note that the last zero is * significant so that we don't drop the digits leading up * to it. */ last_non_zero = p; } /* * We've checked for rounding in the last digit, so we can now * safely remove meaningless trailing zeros. If this leaves a * completely empty buffer, not counting a sign and/or a * decimal point, it means that we have a fractional number * that we're showing without an exponent, and the number of * digits we had for display was insufficient to reach the * first non-zero fractional digit. In this case, simply show * '0' (or '0.', if a decimal point is required) as the result. * To find out, scan for digits. */ p = last_non_zero; for (p1 = buf + VMB_LEN ; p1 < p && !is_digit(*p1) ; ++p1) ; /* if we didn't find any digits, add/insert a '0' */ if (p1 == p) { /* * if there's a decimal point, insert the '0' before it; * otherwise, just append the zero */ if (p > buf + VMB_LEN && *(p-1) == decpt_char) { *(p-1) = '0'; *p++ = decpt_char; } else *p++ = '0'; } } } /* if necessary, round up at the last digit */ if (get_round_dir(ext, idx)) { char *rp; int need_carry; int found_pt; int dig_cnt; /* * go back through the number and add one to the last digit, * carrying as needed */ for (dig_cnt = 0, found_pt = FALSE, need_carry = TRUE, rp = p - 1 ; rp >= buf + VMB_LEN ; rp = utf8_ptr::s_dec(rp)) { /* if this is a digit, bump it up */ if (is_digit(*rp)) { /* count it */ ++dig_cnt; /* if it's 9, we'll have to carry; otherwise it's easy */ if (*rp == '9') { /* set it to zero and keep going to do the carry */ *rp = '0'; /* * if we haven't found the decimal point yet, and * we're not required to show a certain number of * fractional digits, we can simply remove this * trailing zero */ if (show_pt && !found_pt && frac_digits == -1) { /* remove the trailing zero */ p = utf8_ptr::s_dec(p); /* remove it from the digit count */ --dig_cnt; } } else { /* bump it up one */ ++(*rp); /* no more carrying is needed */ need_carry = FALSE; /* we don't need to look any further */ break; } } else if (*rp == decpt_char) { /* note that we found the decimal point */ found_pt = TRUE; } } /* * If we got to the start of the number and we still need a * carry, we must have had all 9's. In this case, we need to * redo the entire number, since all of the layout (commas, * leading spaces, etc) can change - it's way too much work to * try to back-patch all of this stuff, so we'll just bump the * number up and reformat it from scratch. */ if (need_carry) { /* truncate at this digit, and round up what we're keeping */ set_dig(tmp_ext, idx, 0); round_up_abs(tmp_ext, idx); /* * if this pushes us over the maximum digit range, switch to * scientific notation */ if (max_digits >= 0 && dig_cnt + 1 > max_digits) always_exp = TRUE; /* go back and start over */ goto start_over; } } /* add the exponent */ if (always_exp) { int disp_exp; /* add the 'E' */ *p++ = (exp_caps ? 'E' : 'e'); /* * calculate the display exponent - it's one less than the * actual exponent, because we put the point after one digit */ disp_exp = exp - 1; /* * if we carried into the exponent due to rounding, increase the * exponent by one */ if (exp_carry) ++disp_exp; /* add the sign */ if (disp_exp < 0) { *p++ = '-'; disp_exp = -disp_exp; } else if (always_sign_exp) *p++ = '+'; /* * if the exponent is zero, just put zero (unless a more * specific format was requested) */ if (disp_exp == 0 && exp_digits == -1) { /* add the zero exponent */ *p++ = '0'; } else { char expbuf[20]; char *ep; int dig_cnt; /* build the exponent in reverse */ for (dig_cnt = 0, ep = expbuf + sizeof(expbuf) ; disp_exp != 0 ; disp_exp /= 10, ++dig_cnt) { /* move back one character */ --ep; /* add one digit */ *ep = (disp_exp % 10) + '0'; } /* if necessary, add leading zeros to the exponent */ if (exp_digits != -1 && exp_digits > dig_cnt) { for ( ; dig_cnt < exp_digits ; ++dig_cnt) *p++ = '0'; } /* copy the exponent into the output */ for ( ; ep < expbuf + sizeof(expbuf) ; ++ep) *p++ = *ep; } } /* if we allocated a temporary register, free it */ if (tmp_ext != 0) release_temp_regs(1, tmp_hdl); /* set the string length and return the buffer */ vmb_put_len(buf, p - buf - VMB_LEN); return buf; } /* ------------------------------------------------------------------------ */ /* * Parse a string into an allocated buffer. */ char *CVmObjBigNum::parse_str_alo(size_t &buflen, const char *str, size_t len, int radix) { /* figure the precision */ int prec = precision_from_string(str, len, radix); /* allocate a buffer */ char *ext = new char[buflen = calc_alloc(prec)]; set_prec(ext, prec); /* parse the number */ parse_str_into(ext, str, len, radix); /* return the new buffer */ return ext; } /* ------------------------------------------------------------------------ */ /* * Parse a string with the given radix. */ void CVmObjBigNum::parse_str_into(char *ext, const char *str, size_t len, int radix) { /* if the radix is 0, infer the radix from the string */ int inferred_radix = 0; if (radix == 0) radix = inferred_radix = radix_from_string(str, len); /* if it's decimal, parse as a floating point value */ if (radix == 10) { parse_str_into(ext, str, len); return; } /* get the precision */ size_t prec = get_prec(ext); /* set the initial value to zero */ set_type(ext, VMBN_T_NUM); memset(ext + VMBN_MANT, 0, (prec + 1)/2); /* set up to scan the string */ utf8_ptr p((char *)str); size_t rem = len; /* skip leading spaces */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* check for a sign */ int neg = FALSE; if (rem != 0 && p.getch() == '+') { /* skip the sign */ p.inc(&rem); } else if (rem != 0 && p.getch() == '-') { /* note the sign and skip it */ neg = TRUE; p.inc(&rem); } /* skip spaces after the sign */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* if we inferred the radix as hax, skip any '0x' marker */ if (inferred_radix == 16 && rem != 0 && p.getch() == '0' && (p.getch_at(1) == 'x' || p.getch_at(1) == 'X')) { /* skip the '0x' prefix */ p.inc(&rem); p.inc(&rem); } /* allocate a temporary register for swapping with the accumulator */ uint thdl; char *tmp = alloc_temp_reg(prec, &thdl); char *t1 = ext, *t2 = tmp; /* parse the digits */ int significant = FALSE; for ( ; rem != 0 ; p.inc(&rem)) { /* get the next character */ wchar_t ch = p.getch(); /* get the digit value of this character */ int d; if (ch >= '0' && ch <= '9') d = ch - '0'; else if (ch >= 'A' && ch <= 'Z') d = ch - 'A' + 10; else if (ch >= 'a' && ch <= 'z') d = ch - 'a' + 10; else break; /* if the digit isn't within the radix, it's not a digit after all */ if (d >= radix) break; /* if it's not a leading zero, it's significant */ if (ch != '0') significant = TRUE; /* if it's a significant digit, add it to the accumulator */ if (significant) { /* * Set up a stack register with the digit value. The radix can * be up to 36, so the digit value can be up to 35, so we need * up to two decimal digits to represent the digit value. */ char dreg[5 + 2] = { 2, 0, 0, 0, 0 }; set_uint_val(dreg, d); /* shift the accumulator and add the digit */ mul_by_long(t1, radix); compute_abs_sum_into(t2, t1, dreg); /* swap the accumulators */ char *tt = t1; t1 = t2; t2 = tt; } } /* if we ended up with the value in 'tmp', copy it into our result */ if (t1 != ext) copy_val(ext, t1, FALSE); /* free our temporary register */ release_temp_reg(thdl); /* set the sign */ set_neg(ext, neg); } /* ------------------------------------------------------------------------ */ /* * Parse a string value into an extension */ void CVmObjBigNum::parse_str_into(char *ext, const char *str, size_t len) { /* get the precision */ size_t prec = get_prec(ext); /* set the type to number */ set_type(ext, VMBN_T_NUM); /* initially zero the mantissa */ memset(ext + VMBN_MANT, 0, (prec + 1)/2); /* set up to scan the string */ utf8_ptr p((char *)str); size_t rem = len; /* initialize the exponent to zero */ int exp = 0; /* presume it will be positive */ int neg = FALSE; /* skip leading spaces */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* check for a sign */ if (rem != 0 && p.getch() == '+') { /* skip the sign */ p.inc(&rem); } else if (rem != 0 && p.getch() == '-') { /* note the sign and skip it */ neg = TRUE; p.inc(&rem); } /* set the sign */ set_neg(ext, neg); /* skip spaces after the sign */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* we haven't yet found a significant digit */ int significant = FALSE; /* shift the digits into the value */ int pt = FALSE; for (size_t idx = 0 ; rem != 0 ; p.inc(&rem)) { wchar_t ch; /* get this character */ ch = p.getch(); /* check for a digit */ if (is_digit(ch)) { /* * if it's not a zero, we're definitely into the significant * part of the number */ if (ch != '0') significant = TRUE; /* * if it's significant, add it to the number - skip leading * zeros, since they add no information to the number */ if (significant) { /* if we're not out of precision, add the digit */ if (idx < prec) { /* set the next digit */ set_dig(ext, idx, value_of_digit(ch)); /* move on to the next digit position */ ++idx; } /* * that's another factor of 10 if we haven't found the * decimal point (whether or not we're out of precision to * actually store the digit, count the increase in the * exponent) */ if (!pt) ++exp; } else if (pt) { /* * we haven't yet found a significant digit, so this is a * leading zero, but we have found the decimal point - this * reduces the exponent by one */ --exp; } } else if (ch == '.' && !pt) { /* * this is the decimal point - note it; from now on, we won't * increase the exponent as we add digits */ pt = TRUE; } else if (ch == 'e' || ch == 'E') { int acc; int exp_neg = FALSE; /* exponent - skip the 'e' */ p.inc(&rem); /* check for a sign */ if (rem != 0 && p.getch() == '+') { /* skip the sign */ p.inc(&rem); } else if (rem != 0 && p.getch() == '-') { /* skip it and note it */ p.inc(&rem); exp_neg = TRUE; } /* parse the exponent */ for (acc = 0 ; rem != 0 ; p.inc(&rem)) { wchar_t ch; /* if this is a digit, add it to the exponent */ ch = p.getch(); if (is_digit(ch)) { /* accumulate the digit */ acc *= 10; acc += value_of_digit(ch); } else { /* that's it for the exponent */ break; } } /* if it's negative, apply the sign */ if (exp_neg) acc = -acc; /* add the exponent to the one we've calculated */ exp += acc; /* after the exponent, we're done with the whole number */ break; } else { /* it looks like we've reached the end of the number */ break; } } /* set the exponent */ set_exp(ext, exp); /* normalize the number */ normalize(ext); } /* ------------------------------------------------------------------------ */ /* * Parse a string to determine the radix. If it has a '0x' prefix, it's in * hex. If it has a '0' prefix and no decimal point, 'E', or '8' or '9', * it's octal. Otherwise it's decimal. */ int CVmObjBigNum::radix_from_string(const char *str, size_t len) { /* set up to scan the string */ utf8_ptr p((char *)str); size_t rem = len; /* skip leading spaces */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* skip the sign if present */ if (rem != 0 && (p.getch() == '-' || p.getch() == '+')) p.inc(&rem); /* skip more spaces after the sign */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* check for a radix indicator */ if (rem != 0 && p.getch() == '0') { /* * There's a leading zero, so it could be hex or octal. If there's * an 'x', it's definitely hex. Otherwise, it's octal as long as * it's in purely integer format - that is, no decimal point or 'E' * exponent marker. */ p.inc(&rem); if (rem != 0 && (p.getch() == 'x' || p.getch() == 'X')) { /* 0x prefix, so it's definitely hex */ return 16; } /* * There's a leading 0 but no 'x', so it could be octal or decimal, * depending on what's in the rest of the string. */ for ( ; rem != 0 ; p.inc(&rem)) { switch (p.getch()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': /* octal digit, so we can't rule anything out yet */ continue; case '.': case 'e': case 'E': /* * decimal point or exponent marker, so it's a floating * point value, which can only be decimal */ return 10; case '8': case '9': /* an 8 or a 9 can only appear in a decimal number */ return 10; default: /* * Anything else is invalid within a number, whether octal * integer or floating point, so we've reached the end of * the number. Since we didn't encounter anything that * makes the number a floating point value or decimal * integer, we have an octal integer. */ return 8; } } /* * we reached the end of the number without finding anything that * would rule out treating it as an octal integer, so that's what * it is */ return 8; } else { /* no leading '0', so it's decimal */ return 10; } } /* ------------------------------------------------------------------------ */ /* * Parse a string representation of a number in the given radix to * determine the precision required to hold the value. If 'radix' is 0, we * infer the radix from the string: if it has a '0' prefix and no decimal * point or 'E', it's an octal integer; if it has a '0x' prefix, it's a hex * integer; otherwise it's decimal and can be an integer or a floating * point value. */ int CVmObjBigNum::precision_from_string(const char *str, size_t len, int radix) { /* if the radix is zero, infer the radix from the string */ int inferred_radix = 0; if (radix == 0) radix = inferred_radix = radix_from_string(str, len); /* for decimal values, parse the full floating point format */ if (radix == 10) return precision_from_string(str, len); /* set up to scan the string */ utf8_ptr p((char *)str); size_t rem = len; /* skip leading spaces */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* skip the sign if present */ if (rem != 0 && (p.getch() == '-' || p.getch() == '+')) p.inc(&rem); /* skip more spaces after the sign */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* if we inferred the radix as hax, skip any '0x' marker */ if (inferred_radix == 16 && rem != 0 && p.getch() == '0' && (p.getch_at(1) == 'x' || p.getch_at(1) == 'X')) { /* skip the '0x' prefix */ p.inc(&rem); p.inc(&rem); } /* scan digits */ int prec, significant = FALSE; for (prec = 0 ; rem != 0 ; p.inc(&rem)) { /* get this character */ wchar_t ch = p.getch(); /* get the digit value of this character */ int d; if (ch >= '0' && ch <= '9') d = ch - '0'; else if (ch >= 'A' && ch <= 'Z') d = ch - 'A' + 10; else if (ch >= 'a' && ch <= 'z') d = ch - 'a' + 10; else break; /* if the digit isn't within the radix, it's not a digit after all */ if (d >= radix) break; /* it's significant if it's not a leading zero */ if (ch != '0') significant = TRUE; /* * if we have found a significant digit so far, count this one - do * not count leading zeros, whether they occur before or after the * decimal point */ if (significant) ++prec; } /* * Figure the decimal precision required to hold a number in the given * radix. This is a simple linear conversion: N base-R digits require * log(R)/log(10) decimal digits. Using log10 as our canonical log * base means that log(R)/log(10) == log(R)/1 == log(R). We of course * have to round up for any fractional digits. */ return (int)ceil(prec * log10((double)radix)); } /* * Parse a string representation of a number to determine the precision * required to hold the value. */ int CVmObjBigNum::precision_from_string(const char *str, size_t len) { /* set up to scan the string */ utf8_ptr p((char *)str); size_t rem = len; /* skip leading spaces */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* skip the sign if present */ if (rem != 0 && (p.getch() == '-' || p.getch() == '+')) p.inc(&rem); /* skip more spaces after the sign */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* we haven't yet found a significant leading digit */ int significant = FALSE; /* scan digits */ size_t prec; int pt; int digcnt; for (prec = 0, digcnt = 0, pt = FALSE ; rem != 0 ; p.inc(&rem)) { /* get this character */ wchar_t ch = p.getch(); /* see what we have */ if (is_digit(ch)) { /* * if it's not a zero, note that we've found a significant * digit */ if (ch != '0') significant = TRUE; /* * if we have found a significant digit so far, count this one * - do not count leading zeros, whether they occur before or * after the decimal point */ if (significant) ++prec; /* count the digit in any case */ ++digcnt; } else if (ch == '.' && !pt) { /* decimal point - note it and keep going */ pt = TRUE; } else if (ch == 'e' || ch == 'E') { /* exponent - that's the end of the mantissa */ break; } else { /* we've reached the end of the number */ break; } } /* * if the precision is zero, the number must be zero - use the actual * number of digits for the default precision, so that a value * specified as "0.0000000" has eight digits of precision */ if (prec == 0) prec = digcnt; /* return the precision necessary to store the entire string */ return prec; } /* ------------------------------------------------------------------------ */ /* * Set my value to the given double */ void CVmObjBigNum::set_double_val(char *ext, double val) { /* get the precision */ size_t prec = get_prec(ext); /* set the type to number */ set_type(ext, VMBN_T_NUM); /* set the sign bit */ if (val < 0) { set_neg(ext, TRUE); val = -val; } else set_neg(ext, FALSE); /* zero the mantissa */ memset(ext + VMBN_MANT, 0, (prec + 1)/2); /* * Initialize our exponent 'exp' to the integer portion of log10(val). * If val is a power of 10, exp == log10(val) is an integer, and val is * exactly one times 10^exp (e.g., if val is 1000, log10(val) == 3.0, * and val == 1*10^3). If val isn't a power of 10, 10^exp is the * nearest power of 10 below val. For example, log10(1500) == 3.176, * so exp == int(log10(1500)) == 3, so val = 1.5*10^3. In other words, * the value can be represented as some number between 1 and 10 times * 10^exp. Our storage format is close to this: we store the mantissa * as a value between 0 and 1. So what we *really* want for the * exponent is log10(val)+1 - this will give us an exponent that we can * multiply by some number between 0 and 1 to recreate 'val'. */ int exp = (int)log10(val) + 1; set_exp(ext, exp); /* * We know that val divided by 10^exp is between 0 and 1. So the most * significant digit of val is val/10^(exp-1). We can then divide the * remainder of that calculation by 10^(exp-2) to get the next digit, * and the remainder of that by 10^(exp-3) for the next digit, and so * on. Keep going until we've filled out our digits. */ double base = pow(10.0, exp - 1); for (size_t i = 0 ; i < prec ; ++i, base /= 10.0) { /* * get the most significant remaining digit by dividing by the * log10 exponent of the current value */ int dig = (int)(val / base); set_dig(ext, i, dig); /* get the remainder */ val -= dig*base; } /* normalize the number */ normalize(ext); } /* ------------------------------------------------------------------------ */ /* * Set my value to a given integer value */ void CVmObjBigNum::set_int_val(char *ext, long val) { if (val < 0) { set_uint_val(ext, -val); set_neg(ext, TRUE); } else { set_uint_val(ext, val); } } void CVmObjBigNum::set_uint_val(char *ext, ulong val) { size_t prec; int exp; /* get the precision */ prec = get_prec(ext); /* set the type to number */ ext[VMBN_FLAGS] = 0; set_type(ext, VMBN_T_NUM); /* the value is unsigned, so it's non-negative */ set_neg(ext, FALSE); /* initially zero the mantissa */ memset(ext + VMBN_MANT, 0, (prec + 1)/2); /* initialize the exponent to zero */ exp = 0; /* shift the integer value into the value */ while (val != 0) { unsigned int dig; /* get the low-order digit of the value */ dig = (unsigned int)(val % 10); /* shift the value one place */ val /= 10; /* * shift our number one place right to accommodate this next * higher-order digit */ shift_right(ext, 1); /* set the new most significant digit */ set_dig(ext, 0, dig); /* that's another factor of 10 */ ++exp; } /* set the exponent */ set_exp(ext, exp); /* normalize the number */ normalize(ext); } /* ------------------------------------------------------------------------ */ /* * compare extensions */ int CVmObjBigNum::compare_ext(const char *a, const char *b) { /* if either one is not a number, they can't be compared */ if (is_nan(a) || is_nan(b)) err_throw(VMERR_INVALID_COMPARISON); /* if the signs differ, the positive one is greater */ if (get_neg(a) != get_neg(b)) { /* * if a is negative, it's the lesser number; otherwise it's the * greater number */ return (get_neg(a) ? -1 : 1); } /* the signs are the same - compare the absolute values */ int ret = compare_abs(a, b); /* * If the numbers are negative, and abs(a) > abs(b), a is actually the * lesser number; if they're negative and abs(a) < abs(b), a is the * greater number. So, if a is negative, invert the sense of the * result. Otherwise, both numbers are positive, so the sense of the * absolute value comparison is the same as the sense of the actual * comparison, so just return the result directly. */ return (get_neg(a) ? -ret : ret); } /* ------------------------------------------------------------------------ */ /* * Compare the absolute values of two numbers */ int CVmObjBigNum::compare_abs(const char *ext1, const char *ext2) { size_t idx; int zero1 = is_zero(ext1); int zero2 = is_zero(ext2); size_t prec1 = get_prec(ext1); size_t prec2 = get_prec(ext2); size_t max_prec; size_t min_prec; const char *max_ext; int result; /* * if one is zero and the other is not, the one that's not zero has a * larger absolute value */ if (zero1) return (zero2 ? 0 : -1); if (zero2) return (zero1 ? 0 : 1); /* * if the exponents differ, the one with the larger exponent is the * larger number (this is necessarily true because we keep all numbers * normalized) */ if (get_exp(ext1) > get_exp(ext2)) return 1; else if (get_exp(ext1) < get_exp(ext2)) return -1; /* * The exponents are equal, so we must compare the mantissas digit by * digit */ /* get the larger of the two precisions */ min_prec = prec2; max_prec = prec1; max_ext = ext1; if (prec2 > max_prec) { min_prec = prec1; max_prec = prec2; max_ext = ext2; } /* * The digits are in order from most significant to least significant, * which means we can use memcmp to compare the common parts. However, * we can only compare an even number of digits this way, so round down * the common precision if it's odd. */ if (min_prec > 1 && (result = memcmp(ext1 + VMBN_MANT, ext2 + VMBN_MANT, min_prec/2)) != 0) { /* * they're different in the common memcmp-able parts, so return the * memcmp result */ return result; } /* if the common precision is odd, compare the final common digit */ if ((min_prec & 1) != 0 && (result = ((int)get_dig(ext1, min_prec-1) - (int)get_dig(ext2, min_prec-1))) != 0) return result; /* * the common parts of the mantissas are identical; check each * remaining digit of the longer one to see if any are non-zero */ for (idx = min_prec ; idx < max_prec ; ++idx) { /* * if this digit is non-zero, the longer one is greater, because * the shorter one has an implied zero in this position */ if (get_dig(max_ext, idx) != 0) return (int)prec1 - (int)prec2; } /* they're identical */ return 0; } /* ------------------------------------------------------------------------ */ /* * Compute a sum into the given buffer */ void CVmObjBigNum::compute_sum_into(char *new_ext, const char *ext1, const char *ext2) { /* check to see if the numbers have the same sign */ if (get_neg(ext1) == get_neg(ext2)) { /* * the two numbers have the same sign, so the sum has the same sign * as the two values, and the magnitude is the sum of the absolute * values of the operands */ /* compute the sum of the absolute values */ compute_abs_sum_into(new_ext, ext1, ext2); /* set the sign to match that of the operand */ set_neg(new_ext, get_neg(ext1)); } else { /* * one is positive and the other is negative - subtract the * absolute value of the negative one from the absolute value of * the positive one; the sign will be set correctly by the * differencing */ if (get_neg(ext1)) compute_abs_diff_into(new_ext, ext2, ext1); else compute_abs_diff_into(new_ext, ext1, ext2); } } /* ------------------------------------------------------------------------ */ /* * Compute the sum of two absolute values into the given buffer. */ void CVmObjBigNum::compute_abs_sum_into(char *new_ext, const char *ext1, const char *ext2) { int exp1 = get_exp(ext1), exp2 = get_exp(ext2), new_exp; int prec1 = get_prec(ext1), prec2 = get_prec(ext2); int new_prec = get_prec(new_ext); /* if one or the other is identically zero, return the other value */ if (is_zero(ext1)) { /* ext1 is zero - return ext2 */ copy_val(new_ext, ext2, TRUE); return; } else if (is_zero(ext2)) { /* ext2 is zero - return ext1 */ copy_val(new_ext, ext1, TRUE); return; } /* * start the new value with the larger of the two exponents - this * will have the desired effect of dropping the least significant * digits if any digits must be dropped */ new_exp = exp1; if (exp2 > new_exp) new_exp = exp2; set_exp(new_ext, new_exp); /* compute the digit positions at the bounds of each of our values */ int hi1 = exp1 - 1; int lo1 = exp1 - prec1; int hi2 = exp2 - 1; int lo2 = exp2 - prec2; int hi3 = new_exp - 1; int lo3 = new_exp - new_prec; /* * If one of the values provides a digit one past the end of our * result, remember that as the trailing digit that we're going to * drop. We'll check this when we're done to see if we need to * round the number. Since the result has precision at least as * large as the larger of the two inputs, we can only be dropping * significant digits from one of the two inputs - we can't be * cutting off both inputs. */ int trail_dig = 0, trail_val = 0; if (lo3 - 1 >= lo1 && lo3 - 1 <= hi1) { /* remember the digit */ trail_dig = get_dig(ext1, exp1 - (lo3-1) - 1); trail_val = get_ORdigs(ext1, exp1 - (lo3-1)); } else if (lo3 - 1 >= lo2 && lo3 - 1 <= hi2) { /* remember the digit */ trail_dig = get_dig(ext2, exp2 - (lo3-1) - 1); trail_val = get_ORdigs(ext2, exp2 - (lo3-1)); } /* no carry yet */ int carry = 0; /* add the digits */ for (int pos = lo3 ; pos <= hi3 ; ++pos) { /* start with the carry */ int acc = carry; /* add the first value digit if it's in range */ if (pos >= lo1 && pos <= hi1) acc += get_dig(ext1, exp1 - pos - 1); /* add the second value digit if it's in range */ if (pos >= lo2 && pos <= hi2) acc += get_dig(ext2, exp2 - pos - 1); /* check for carry */ if (acc > 9) { /* reduce the accumulator and set the carry */ acc -= 10; carry = 1; } else { /* no carry */ carry = 0; } /* set the digit in the result */ set_dig(new_ext, new_exp - pos - 1, acc); } /* * If we have a carry at the end, we must carry it out to a new * digit. In order to do this, we must shift the whole number right * one place, then insert the one. */ if (carry) { /* * remember the last digit of the result, which we won't have * space to store after the shift */ trail_val |= trail_dig; trail_dig = get_dig(new_ext, new_prec - 1); /* shift the result right */ shift_right(new_ext, 1); /* increase the exponent to compensate for the shift */ set_exp(new_ext, get_exp(new_ext) + 1); /* set the leading 1 */ set_dig(new_ext, 0, 1); } /* the sum of two absolute values is always positive */ set_neg(new_ext, FALSE); /* * round up if the trailing digits are >5000..., or exactly 5000... * and the last digit is odd */ round_for_dropped_digits(new_ext, trail_dig, trail_val); /* normalize the number */ normalize(new_ext); } /* ------------------------------------------------------------------------ */ /* * Compute a difference into a buffer */ void CVmObjBigNum::compute_diff_into(char *result, const char *ext1, const char *ext2) { /* check to see if the numbers have the same sign */ if (get_neg(ext1) == get_neg(ext2)) { /* * The two numbers have the same sign, so the difference is the * difference in the magnitudes, and has a sign depending on the * order of the difference and the signs of the original values. * If both values are positive, the difference is negative if the * second value is larger than the first. If both values are * negative, the difference is negative if the second value has * larger absolute value than the first. */ /* compute the difference in magnitudes */ compute_abs_diff_into(result, ext1, ext2); /* * if the original values were negative, then the sign of the * result is the opposite of the sign of the difference of the * absolute values; otherwise, it's the same as the sign of the * difference of the absolute values */ if (get_neg(ext1)) negate(result); } else { /* * one is positive and the other is negative - the result has the * sign of the first operand, and has magnitude equal to the sum of * the absolute values */ /* compute the sum of the absolute values */ compute_abs_sum_into(result, ext1, ext2); /* set the sign of the result to that of the first operand */ if (!is_zero(result)) set_neg(result, get_neg(ext1)); } } /* ------------------------------------------------------------------------ */ /* * Compute the difference of two absolute values into the given buffer */ void CVmObjBigNum::compute_abs_diff_into(char *new_ext, const char *ext1, const char *ext2) { int max_exp; int lo1, hi1; int lo2, hi2; int lo3, hi3; int pos; int result_neg = FALSE; int borrow; /* if we're subtracting zero or from zero, it's easy */ if (is_zero(ext2)) { /* * we're subtracting zero from another value - the result is * simply the first value */ copy_val(new_ext, ext1, TRUE); return; } else if (is_zero(ext1)) { /* * We're subtracting a value from zero - we know the value we're * subtracting is non-zero (we already checked for that case * above), and we're only considering the absolute values, so * simply return the negative of the absolute value of the * second operand. */ copy_val(new_ext, ext2, TRUE); set_neg(new_ext, TRUE); return; } /* * Compare the absolute values of the two operands. If the first * value is larger than the second, subtract them in the given * order. Otherwise, reverse the order and note that the result is * negative. */ if (compare_abs(ext1, ext2) < 0) { const char *tmp; /* the result will be negative */ result_neg = TRUE; /* swap the order of the subtraction */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * start the new value with the larger of the two exponents - this * will have the desired effect of dropping the least significant * digits if any digits must be dropped */ max_exp = get_exp(ext1); if (get_exp(ext2) > max_exp) max_exp = get_exp(ext2); set_exp(new_ext, max_exp); /* compute the digit positions at the bounds of each of our values */ hi1 = get_exp(ext1) - 1; lo1 = get_exp(ext1) - get_prec(ext1); hi2 = get_exp(ext2) - 1; lo2 = get_exp(ext2) - get_prec(ext2); hi3 = get_exp(new_ext) - 1; lo3 = get_exp(new_ext) - get_prec(new_ext); /* start off with no borrow */ borrow = 0; /* * Check for borrowing from before the least significant digit * position in common to both numbers */ if (lo3-1 >= lo1 && lo3-1 <= hi1) { /* * In this case, we would be dropping precision from the end of * the top number. This case should never happen - the only way * it could happen is for the bottom number to extend to the * left of the top number at the most significant end. This * cannot happen because the bottom number always has small * magnitude by this point (we guarantee this above). So, we * should never get here. */ assert(FALSE); } else if (lo3-1 >= lo2 && lo3-1 <= hi2) { size_t idx; /* * We're dropping precision from the bottom number, so we want * to borrow into the subtraction if the rest of the number is * greater than 5xxxx. If it's exactly 5000..., do not borrow, * since the result would end in 5 and we'd round up. * * If the next digit is 6 or greater, we know for a fact we'll * have to borrow. If the next digit is 4 or less, we know for * a fact we won't have to borrow. If the next digit is 5, * though, we must look at the rest of the number to see if * there's anything but trailing zeros. */ idx = (size_t)(get_exp(ext2) - (lo3-1) - 1); if (get_dig(ext2, idx) >= 6) { /* definitely borrow */ borrow = 1; } else if (get_dig(ext2, idx) == 5) { /* borrow only if we have something non-zero following */ for (++idx ; idx < get_prec(ext2) ; ++idx) { /* if it's non-zero, we must borrow */ if (get_dig(ext2, idx) != 0) { /* note the borrow */ borrow = 1; /* no need to keep scanning */ break; } } } } /* subtract the digits from least to most significant */ for (pos = lo3 ; pos <= hi3 ; ++pos) { int acc; /* start with zero in the accumulator */ acc = 0; /* start with the top-line value if it's represented here */ if (pos >= lo1 && pos <= hi1) acc = get_dig(ext1, get_exp(ext1) - pos - 1); /* subtract the second value digit if it's represented here */ if (pos >= lo2 && pos <= hi2) acc -= get_dig(ext2, get_exp(ext2) - pos - 1); /* subtract the borrow */ acc -= borrow; /* check for borrow */ if (acc < 0) { /* increase the accumulator */ acc += 10; /* we must borrow from the next digit up */ borrow = 1; } else { /* we're in range - no need to borrow */ borrow = 0; } /* set the digit in the result */ set_dig(new_ext, get_exp(new_ext) - pos - 1, acc); } /* set the sign of the result as calculated earlier */ set_neg(new_ext, result_neg); /* normalize the number */ normalize(new_ext); } /* ------------------------------------------------------------------------ */ /* * Compute the product of the values into the given buffer */ void CVmObjBigNum::compute_prod_into(char *new_ext, const char *ext1, const char *ext2) { size_t prec1 = get_prec(ext1); size_t prec2 = get_prec(ext2); size_t new_prec = get_prec(new_ext); size_t idx1; size_t idx2; size_t out_idx; size_t start_idx; int out_exp; /* start out with zero in the accumulator */ memset(new_ext + VMBN_MANT, 0, (new_prec + 1)/2); /* * Initially write output in the same 'column' where the top number * ends, so we start out with the same scale as the top number. */ start_idx = get_prec(ext1); /* * initial result exponent is the sum of the exponents, minus the * number of digits in the bottom number (effectively, this lets us * treat the bottom number as a whole number by scaling it enough to * make it whole, soaking up the factors of ten into decimal point * relocation) */ out_exp = get_exp(ext1) + get_exp(ext2) - prec2; /* there's no trailing accumulator digit yet */ int trail_dig = 0, trail_val = 0; /* * Loop over digits in the bottom number, from least significant to * most significant - we'll multiply each digit of the bottom number * by the top number and add the result into the accumulator. */ for (idx2 = prec2 ; idx2 != 0 ; ) { int carry; int dig; int ext2_dig; /* no carry yet on this round */ carry = 0; /* start writing this round at the output start index */ out_idx = start_idx; /* move to the next digit */ --idx2; /* get this digit of ext2 */ ext2_dig = get_dig(ext2, idx2); /* * if this digit of ext2 is non-zero, multiply the digits of * ext1 by the digit (obviously if the digit is zero, there's no * need to iterate over the digits of ext1) */ if (ext2_dig != 0) { /* * loop over digits in the top number, from least to most * significant */ for (idx1 = prec1 ; idx1 != 0 ; ) { /* move to the next digit of the top number */ --idx1; /* move to the next digit of the accumulator */ --out_idx; /* * compute the product of the current digits, and add in * the carry from the last digit, then add in the * current accumulator digit in this position */ dig = (get_dig(ext1, idx1) * ext2_dig) + carry + get_dig(new_ext, out_idx); /* figure the carry to the next digit */ carry = (dig / 10); dig = dig % 10; /* store the new digit */ set_dig(new_ext, out_idx, dig); } } /* * Shift the result accumulator right in preparation for the * next round. One exception: if this is the last (most * significant) digit of the bottom number, and there's no * carry, there's no need to shift the number, since we'd just * normalize away the leading zero anyway */ if (idx2 != 0 || carry != 0) { /* remember the trailing digit that we're going to drop */ trail_val |= trail_dig; trail_dig = get_dig(new_ext, new_prec - 1); /* shift the accumulator */ shift_right(new_ext, 1); /* increase the output exponent */ ++out_exp; /* insert the carry as the lead digit */ set_dig(new_ext, 0, carry); } } /* set the result exponent */ set_exp(new_ext, out_exp); /* * set the sign - if both inputs have the same sign, the output is * positive, otherwise it's negative */ set_neg(new_ext, get_neg(ext1) != get_neg(ext2)); /* if the trailing digit is 5 or greater, round up */ round_for_dropped_digits(new_ext, trail_dig, trail_val); /* normalize the number */ normalize(new_ext); } /* ------------------------------------------------------------------------ */ /* * Compute a quotient into the given buffer. If new_rem_ext is * non-null, we'll save the remainder into this buffer. We calculate * the remainder only to the precision of the quotient. */ void CVmObjBigNum::compute_quotient_into(char *new_ext, char *new_rem_ext, const char *ext1, const char *ext2) { char *rem_ext; uint rem_hdl; char *rem_ext2; uint rem_hdl2; int quo_exp; size_t quo_idx; size_t quo_prec = get_prec(new_ext); size_t dvd_prec = get_prec(ext1); size_t dvs_prec = get_prec(ext2); char *dvs_ext; uint dvs_hdl; char *dvs_ext2; uint dvs_hdl2; int lead_dig_set; int zero_remainder; int acc; size_t rem_prec; /* if the divisor is zero, throw an error */ if (is_zero(ext2)) err_throw(VMERR_DIVIDE_BY_ZERO); /* if the dividend is zero, the result is zero */ if (is_zero(ext1)) { /* set the result to zero */ set_zero(new_ext); /* if they want the remainder, it's zero also */ if (new_rem_ext != 0) set_zero(new_rem_ext); /* we're done */ return; } /* * Calculate the precision we need for the running remainder. We * must retain enough precision in the remainder to calculate exact * differences, so we need the greater of the precisions of the * dividend and the divisor, plus enough extra digits for the * maximum relative shifting. We'll have to shift at most one * extra digit, but use two to be extra safe. */ rem_prec = dvd_prec; if (rem_prec < dvs_prec) rem_prec = dvs_prec; rem_prec += 2; /* * Make sure the precision is at least three, since it simplifies * our digit approximation calculation. */ if (rem_prec < 3) rem_prec = 3; /* * Allocate two temporary registers for the running remainder, and * one more for the multiplied divisor. Note that we allocate the * multiplied divisor at our higher precision so that we don't lose * digits in our multiplier calculations. */ alloc_temp_regs(rem_prec, 3, &rem_ext, &rem_hdl, &rem_ext2, &rem_hdl2, &dvs_ext2, &dvs_hdl2); /* * Allocate another temp register for the shifted divisor. Note * that we need a different precision here, which is why we must * make a separate allocation call */ err_try { /* make the additional allocation */ alloc_temp_regs(dvs_prec, 1, &dvs_ext, &dvs_hdl); } err_catch_disc { /* delete the first group of registers we allocated */ release_temp_regs(3, rem_hdl, rem_hdl2, dvs_hdl2); /* re-throw the exception */ err_rethrow(); } err_end; /* the dividend is the initial value of the running remainder */ copy_val(rem_ext, ext1, TRUE); /* copy the initial divisor into our temp register */ copy_val(dvs_ext, ext2, TRUE); /* we haven't set a non-zero leading digit yet */ lead_dig_set = FALSE; /* * scale the divisor so that the divisor and dividend have the same * exponent, and absorb the multiplier in the quotient */ quo_exp = get_exp(ext1) - get_exp(ext2) + 1; set_exp(dvs_ext, get_exp(ext1)); /* we don't have a zero remainder yet */ zero_remainder = FALSE; /* * if the result of the floating point division is entirely fractional, * the dividend is the remainder, and the quotient is zero */ if (new_rem_ext != 0 && quo_exp <= 0) { /* copy the initial remainder into the output remainder */ copy_val(new_rem_ext, rem_ext, TRUE); /* set the quotient to zero */ set_zero(new_ext); /* we have the result - no need to do any more work */ goto done; } /* * Loop over each digit of precision of the quotient. */ for (quo_idx = 0 ; ; ) { int rem_approx, dvs_approx; int dig_approx; char *tmp; int exp_diff; /* start out with 0 in our digit accumulator */ acc = 0; /* * Get the first few digits of the remainder, and the first few * digits of the divisor, rounding up the divisor and rounding * down the remainder. Compute the quotient - this will give us * a rough guess and a lower bound for the current digit's * value. */ rem_approx = (get_dig(rem_ext, 0)*100 + get_dig(rem_ext, 1)*10 + get_dig(rem_ext, 2)); dvs_approx = (get_dig(dvs_ext, 0)*100 + (dvs_prec >= 2 ? get_dig(dvs_ext, 1) : 0)*10 + (dvs_prec >= 3 ? get_dig(dvs_ext, 2) : 0) + 1); /* adjust for differences in the scale */ exp_diff = get_exp(rem_ext) - get_exp(dvs_ext); if (exp_diff > 0) { /* the remainder is larger - scale it up */ for ( ; exp_diff > 0 ; --exp_diff) rem_approx *= 10; } else if (exp_diff <= -3) { /* * The divisor is larger by more than 10^3, which means that * the result of our integer division is definitely going to * be zero, so there's no point in actually doing the * calculation. This is only a special case because, for * sufficiently large negative differences, we'd have to * multiply our divisor approximation by 10 so many times * that we'd overflow a native int type, at which point we'd * get 0 in the divisor, which would result in a * divide-by-zero. To avoid this, just put 1000 in our * divisor, which is definitely larger than anything we can * have in rem_ext at this point (since it was just three * decimal digits, after all). */ dvs_approx = 1000; } else if (exp_diff < 0) { /* the divisor is larger - scale it up */ for ( ; exp_diff < 0 ; ++exp_diff) dvs_approx *= 10; } /* calculate our initial guess for this digit */ dig_approx = rem_approx / dvs_approx; /* * If this digit is above 2, it'll save us a lot of work to * subtract digit*divisor once, rather than iteratively * deducting the divisor one time per iteration. (It costs us a * little to calculate the digit*divisor product, so we don't * want to do this for very small digit values.) */ if (dig_approx > 2) { /* put the approximate digit in the accumulator */ acc = dig_approx; /* make a copy of the divisor */ copy_val(dvs_ext2, dvs_ext, FALSE); /* * multiply it by the guess for the digit - we know this * will always be less than or equal to the real value * because of the way we did the rounding */ mul_by_long(dvs_ext2, (ulong)dig_approx); /* subtract it from the running remainder */ compute_abs_diff_into(rem_ext2, rem_ext, dvs_ext2); /* if that leaves zero in the remainder, note it */ if (is_zero(rem_ext2)) { zero_remainder = TRUE; break; } /* * swap the remainder registers, since rem_ext2 is now the * new running remainder value */ tmp = rem_ext; rem_ext = rem_ext2; rem_ext2 = tmp; /* * Now we'll finish up the job by subtracting the divisor * from the remainder as many more times as necessary for * the remainder to fall below the divisor. We can't be * exact at this step because we're not considering all of * the digits, but we should only have one more subtraction * remaining at this point. */ } /* * subtract the divisor from the running remainder as many times * as we can */ for ( ; ; ++acc) { int comp_res; /* compare the running remainder to the divisor */ comp_res = compare_abs(rem_ext, dvs_ext); if (comp_res < 0) { /* * the remainder is smaller than the divisor - we have * our result for this digit */ break; } else if (comp_res == 0) { /* note that we have a zero remainder */ zero_remainder = TRUE; /* count one more subtraction */ ++acc; /* we have our result for this digit */ break; } /* subtract it */ compute_abs_diff_into(rem_ext2, rem_ext, dvs_ext); /* * swap the remainder registers, since rem_ext2 is now the * new running remainder value */ tmp = rem_ext; rem_ext = rem_ext2; rem_ext2 = tmp; } /* store this digit of the quotient */ if (quo_idx < quo_prec) { /* store the digit */ set_dig(new_ext, quo_idx, acc); } else if (quo_idx == quo_prec) { /* set the quotient's exponent */ set_exp(new_ext, quo_exp); /* * This is the last digit, which we calculated for rounding * purposes only. If it's greater than 5, round up. If it's * exactly 5 and we have a non-zero remainder, round up. If * it's exactly 5 and we have a zero remainder, round up if the * last digit is odd. */ round_for_dropped_digits(new_ext, acc, !zero_remainder); /* we've reached the rounding digit - we can stop now */ break; } /* * if this is a non-zero digit, we've found a significant * leading digit */ if (acc != 0) lead_dig_set = TRUE; /* * if we've found a significant leading digit, move to the next * digit of the quotient; if not, adjust the quotient exponent * down one, and keep preparing to write the first digit at the * first "column" of the quotient */ if (lead_dig_set) ++quo_idx; else --quo_exp; /* * If we have an exact result (a zero remainder), we're done. * * Similarly, if we've reached the units digit, and the caller * wants the remainder, stop now - the caller wants an integer * result for the quotient, which we now have. * * Similarly, if we've reached the rounding digit, and the * caller wants the remainder, skip the rounding step - the * caller wants an unrounded result for the quotient so that the * quotient times the divisor plus the remainder equals the * dividend. */ if (zero_remainder || (new_rem_ext != 0 && ((int)quo_idx == quo_exp || quo_idx == quo_prec))) { /* zero any remaining digits of the quotient */ for ( ; quo_idx < quo_prec ; ++quo_idx) set_dig(new_ext, quo_idx, 0); /* set the quotient's exponent */ set_exp(new_ext, quo_exp); /* that's it */ break; } /* divide the divisor by ten */ set_exp(dvs_ext, get_exp(dvs_ext) - 1); } /* store the remainder if the caller wants the value */ if (new_rem_ext != 0) { /* save the remainder into the caller's buffer */ if (zero_remainder) { /* the remainder is exactly zero */ set_zero(new_rem_ext); } else { /* copy the running remainder */ copy_val(new_rem_ext, rem_ext, TRUE); /* the remainder has the same sign as the dividend */ set_neg(new_rem_ext, get_neg(ext1)); /* normalize the remainder */ normalize(new_rem_ext); } } /* * the quotient is positive if both the divisor and dividend have * the same sign, negative if they're different */ set_neg(new_ext, get_neg(ext1) != get_neg(ext2)); /* normalize the quotient */ normalize(new_ext); done: /* release the temporary registers */ release_temp_regs(4, rem_hdl, rem_hdl2, dvs_hdl, dvs_hdl2); } /* ------------------------------------------------------------------------ */ /* * The BigNumber cache is implemented as an ordinary static. This can be * shared among VM instances (since it stores mathematical constants and * provides some simple memory management facilities), so it's not * necessary to use the VM global scheme (which is only needed for data * that must be isolated per VM instance). This simplifies interfaces to * many of the low-level routines by letting us omit the VMG_ parameter, * and allows many BigNumber features to be accessed without having to link * in the whole VM global subsystem. This last bit lets us use BigNumber * calculations in the compiler, for example, which is useful for constant * folding. */ CVmBigNumCache *S_bignum_cache = 0; /* number of references to the cache */ static int S_bignum_cache_refs = 0; /* ------------------------------------------------------------------------ */ /* * Cache creation/deletion */ void CVmObjBigNum::init_cache() { if (S_bignum_cache_refs++ == 0) S_bignum_cache = new CVmBigNumCache(32); } void CVmObjBigNum::term_cache() { if (--S_bignum_cache_refs == 0) { delete S_bignum_cache; S_bignum_cache = 0; } } /* ------------------------------------------------------------------------ */ /* * Allocate a temporary register */ char *CVmObjBigNum::alloc_temp_reg(size_t prec, uint *hdl) { /* allocate a register with enough space for the desired precision */ char *p = S_bignum_cache->alloc_reg(calc_alloc(prec), hdl); /* if that succeeded, initialize the memory */ if (p != 0) { /* set the desired precision */ set_prec(p, prec); /* initialize the flags */ p[VMBN_FLAGS] = 0; } /* return the register memory */ return p; } /* ------------------------------------------------------------------------ */ /* * release a temporary register */ void CVmObjBigNum::release_temp_reg(uint hdl) { /* release the register to the cache */ S_bignum_cache->release_reg(hdl); } /* ------------------------------------------------------------------------ */ /* * Allocate a group of temporary registers */ void CVmObjBigNum::alloc_temp_regs(size_t prec, size_t cnt, ...) { va_list marker; size_t i; int failed; char **ext_ptr; uint *hdl_ptr; /* set up to read varargs */ va_start(marker, cnt); /* no failures yet */ failed = FALSE; /* scan the varargs list */ for (i = 0 ; i < cnt ; ++i) { /* get the next argument */ ext_ptr = va_arg(marker, char **); hdl_ptr = va_arg(marker, uint *); /* allocate a register */ *ext_ptr = alloc_temp_reg(prec, hdl_ptr); /* if this allocation failed, note it, but keep going for now */ if (*ext_ptr == 0) failed = TRUE; } /* done reading argument */ va_end(marker); /* if we had any failures, free all of the registers we allocated */ if (failed) { /* restart reading the varargs */ va_start(marker, cnt); /* scan the varargs and free the successfully allocated registers */ for (i = 0 ; i < cnt ; ++i) { /* get the next argument */ ext_ptr = va_arg(marker, char **); hdl_ptr = va_arg(marker, uint *); /* free this register if we successfully allocated it */ if (*ext_ptr != 0) release_temp_reg(*hdl_ptr); } /* done reading varargs */ va_end(marker); /* throw the error */ err_throw(VMERR_BIGNUM_NO_REGS); } } /* ------------------------------------------------------------------------ */ /* * Release a block of temporary registers */ void CVmObjBigNum::release_temp_regs(size_t cnt, ...) { /* start reading the varargs */ va_list marker; va_start(marker, cnt); /* scan the varargs and free the listed registers */ for (size_t i = 0 ; i < cnt ; ++i) { uint hdl; /* get the next handle */ hdl = va_arg(marker, uint); /* free this register */ release_temp_reg(hdl); } /* done reading varargs */ va_end(marker); } /* ------------------------------------------------------------------------ */ /* * Round the extension to the nearest integer */ void CVmObjBigNum::round_to_int(char *ext) { /* * the exponent is equal to the number of digits before the decimal * point, so that's the number of digits we want to keep */ round_to(ext, get_exp(ext)); } /* * Round the extension to the given number of digits */ void CVmObjBigNum::round_to(char *ext, int digits) { /* if we're dropping digits, and we need to round up, round up */ int prec = get_prec(ext); if (digits < prec) { /* note if we need to round up */ int r = get_round_dir(ext, digits); /* zero the digits beyond the last one we're keeping */ for (int i = digits ; i < prec ; ++i) set_dig(ext, i, 0); /* round up if necessary */ if (r) round_up_abs(ext, digits); } } /* * get the OR sum of digits from the given starting place to least * significant */ int CVmObjBigNum::get_ORdigs(const char *ext, int d) { int sum, prec = get_prec(ext); for (sum = 0 ; d < prec ; sum |= get_dig(ext, d++)) ; return sum; } /* * Determine the direction to round a value to the given number of digits. * Returns 1 to round up, 0 to round down - this is effectively the value * to add at the last digit we're keeping. */ int CVmObjBigNum::get_round_dir(const char *ext, int digits) { /* if the dropped digit is beyond the precision, there's no rounding */ int prec = get_prec(ext); if (digits >= prec) return 0; /* we can't round to negative digits */ if (digits < 0) digits = 0; /* * if the first dropped digit is greater than 5, round up; if it's less * than 5, round down; otherwise we have to break the tie */ int d = get_dig(ext, digits); if (d > 5) return 1; if (d < 5) return 0; /* * it's a 5 - if there are any non-zero digits after this point, the * value is greater than 5, so round up */ for (int i = digits + 1 ; i < prec ; ++i) { /* if it's a non-zero digit, round up */ if (get_dig(ext, i) != 0) return 1; } /* * It's exactly 5000...., so we need to break the tie by rounding to * the nearest even digit in the last digit we're keeping. Get that * last digit. */ d = (digits > 0 ? get_dig(ext, digits - 1) : 0); /* * If the digit is already even, round down (return 0) to stay at that * even value. If it's odd, round up (return 1) to get to the next * even digit. (d & 1) happens to be 1 if it's odd, 0 if it's even. */ return d & 1; } /* * Round a number based on dropped digits lost during a calculation. * 'trail_dig' is the most significant dropped digit, and 'trail_val' is * the "|" sum of all of the less significant dropped digits. */ void CVmObjBigNum::round_for_dropped_digits( char *ext, int trail_dig, int trail_val) { if (trail_dig > 5 || (trail_dig == 5 && trail_val > 0) || (trail_dig == 5 && trail_val == 0 && (get_dig(ext, get_prec(ext)-1) & 1) != 0)) round_up_abs(ext); } /* * Round a number up. This adds 1 starting at the least significant digit * of the number. */ void CVmObjBigNum::round_up_abs(char *ext) { round_up_abs(ext, get_prec(ext)); } /* * Round a number up. This adds 1 starting at the least significant digit * we're keeping. */ void CVmObjBigNum::round_up_abs(char *ext, int keep_digits) { /* start with a "carry" into the least significant digit */ int carry = TRUE; /* propagate the carry through the number's digits */ for (int idx = keep_digits ; idx != 0 ; ) { /* move to the next more significant digit */ --idx; /* add the carry */ int d = get_dig(ext, idx); if (d < 9) { /* bump it up */ set_dig(ext, idx, d + 1); /* no carrying required, so we can stop now */ carry = FALSE; break; } else { /* it's a '9', so bump it up to '0' and carry to the next digit */ set_dig(ext, idx, 0); } } /* * If we carried out of the top digit, we must have had all nines, in * which case we now have all zeros. Put a 1 in the first digit and * increase the exponent to renormalize. */ if (carry) { /* shift the mantissa */ shift_right(ext, 1); /* increase the exponent */ set_exp(ext, get_exp(ext) + 1); /* set the lead digit to 1 */ set_dig(ext, 0, 1); } /* we know the value is non-zero now */ ext[VMBN_FLAGS] &= ~VMBN_F_ZERO; } /* ------------------------------------------------------------------------ */ /* * Copy a value, extending with zeros if expanding, or truncating or * rounding, as desired, if the precision changes */ void CVmObjBigNum::copy_val(char *dst, const char *src, int round) { size_t src_prec = get_prec(src); size_t dst_prec = get_prec(dst); /* check to see if we're growing or shrinking */ if (dst_prec > src_prec) { /* * it's growing - copy the entire old mantissa, plus the flags, * sign, and exponent */ memcpy(dst + VMBN_EXP, src + VMBN_EXP, (VMBN_MANT - VMBN_EXP) + (src_prec + 1)/2); /* clear the balance of the mantissa */ memset(dst + VMBN_MANT + (src_prec + 1)/2, 0, (dst_prec + 1)/2 - (src_prec + 1)/2); /* * clear the digit just after the last digit we copied - we might * have missed this with the memset, since it only deals with * even-numbered pairs of digits */ set_dig(dst, src_prec, 0); } else { /* it's shrinking - truncate the mantissa to the new length */ memcpy(dst + VMBN_EXP, src + VMBN_EXP, (VMBN_MANT - VMBN_EXP) + (dst_prec + 1)/2); /* check for rounding */ if (round && get_round_dir(src, dst_prec)) round_up_abs(dst); } } /* ------------------------------------------------------------------------ */ /* * Multiply by an integer constant value */ void CVmObjBigNum::mul_by_long(char *ext, unsigned long val) { size_t idx; size_t prec = get_prec(ext); unsigned long carry = 0; /* * start at the least significant digit and work up through the digits */ for (idx = prec ; idx != 0 ; ) { unsigned long prod; /* move to the next digit */ --idx; /* * compute the product of this digit and the given value, and add * in the carry from the last digit */ prod = (val * get_dig(ext, idx)) + carry; /* set this digit to the residue mod 10 */ set_dig(ext, idx, prod % 10); /* carry the rest */ carry = prod / 10; } /* if we have a carry left over, shift it in */ int dropped_dig = 0, dropped_val = 0; while (carry != 0) { /* * Note if the previous dropped digit is non-zero, then note the * newly dropped digit. dropped_val tells us if there's anything * non-zero after the last digit, in case the last digit turns out * to be a 5. */ dropped_val |= dropped_dig; dropped_dig = get_dig(ext, prec - 1); /* shift the number and adjust the exponent */ shift_right(ext, 1); set_exp(ext, get_exp(ext) + 1); /* shift in this digit of the carry */ set_dig(ext, 0, carry % 10); /* take it out of the carry */ carry /= 10; } /* * round up if the dropped digits are >5000..., or they're exactly * 5000... and the last digit we're keeping is odd */ round_for_dropped_digits(ext, dropped_dig, dropped_val); /* normalize the result */ normalize(ext); } /* * Divide the magnitude of the number by 2^32. This is a special case of * div_by_long() for splitting a number into 32-bit chunks. Returns with * the quotient in 'ext', and the remainder in 'remp'. */ void CVmObjBigNum::div_by_2e32(char *ext, uint32_t *remp) { size_t in_idx; size_t out_idx; int sig; size_t prec = get_prec(ext); unsigned long rem = 0; int exp = get_exp(ext); /* if it's entirely fractional, the result is 0 with remainder 0 */ if (exp <= 0) { *remp = 0; set_zero(ext); return; } /* start at the most significant digit and work down */ for (rem = 0, sig = FALSE, in_idx = out_idx = 0 ; out_idx < prec ; ++in_idx) { /* * Multiply the remainder by 10. 10 == (2+8), so this is the sum * of rem<<1 and rem<<3. This is handy because it makes it * relatively easy to calculate the overflow from the 32-bit * accumulator on 32-bit platforms: the overflow is the sum of the * high bit and the high three bits, plus the carry from the low * part. The low part has a carry of 1 if rem<<1 + rem<<3 exceeds * 0xffffffff, which we can calculate in 32 bits as rem<<1 > * 0xffffffff - rem<<3. */ ulong rem1 = (rem << 1) & 0xffffffff; ulong rem3 = (rem << 3) & 0xffffffff; ulong hi = ((rem >> 31) & 0x00000001) + ((rem >> 29) & 0x00000007) + (rem1 > 0xffffffff - rem3 ? 1 : 0); rem = (rem1 + rem3) & 0xffffffff; /* * Add the current digit into the remainder. We again need to * carry the overflow into the high part. */ ulong dig = (in_idx < prec ? get_dig(ext, in_idx) : 0); hi += dig > 0xffffffff - rem ? 1 : 0; rem = (rem + dig) & 0xffffffff; /* * We now have the quotient of rem/2^32 in 'hi', and the remainder * in 'rem'. */ int quo = (int)hi; /* if the quotient is non-zero, we've found a significant digit */ if (quo != 0) sig = TRUE; /* * if we've found a significant digit, store it; otherwise, just * reduce the exponent to account for an implied leading zero that * we won't actually store */ if (sig) { /* store the digit */ set_dig(ext, out_idx++, quo); /* * if we've reached the decimal place, stop here, since we're * doing integer division */ if ((int)out_idx == exp) break; } else { /* all leading zeros so far - adjust the exponent */ set_exp(ext, --exp); /* if this leaves us with a fractional result, we're done */ if (exp == 0) break; } /* * If we've reached the last input digit and the remainder is zero, * we're done - fill out the rest of the number with trailing zeros * and stop looping. If we've reached the decimal point in the * output, stop here, since we're doing an integer division. */ if (rem == 0 && in_idx >= prec) { /* if we don't have any significant digits, we have a zero */ if (!sig) { set_zero(ext); out_idx = prec; } /* we have our result */ break; } } /* fill out the rest of the number with zeros */ for ( ; out_idx < prec ; ++out_idx) set_dig(ext, out_idx, 0); /* normalize the result */ normalize(ext); /* pass back the remainder */ *remp = rem; } /* * Divide by an integer constant value. Note that 'val' must not exceed * ULONG_MAX/10, because we have to be able to compute intermediate integer * dividend values up to 10*val. */ void CVmObjBigNum::div_by_long(char *ext, unsigned long val, unsigned long *remp) { size_t in_idx; size_t out_idx; int sig; size_t prec = get_prec(ext); unsigned long rem = 0; int exp = get_exp(ext); /* * if we're doing integer division, and the dividend is entirely * fractional, the result is 0 with remainder 0 */ if (remp != 0 && exp <= 0) { *remp = 0; set_zero(ext); return; } /* start at the most significant digit and work down */ for (rem = 0, sig = FALSE, in_idx = out_idx = 0 ; out_idx < prec ; ++in_idx) { /* * shift this digit into the remainder - if we're past the end of * the number, shift in an implied trailing zero */ rem *= 10; rem += (in_idx < prec ? get_dig(ext, in_idx) : 0); /* calculate the quotient and the next remainder */ long quo = rem / val; rem %= val; /* if the quotient is non-zero, we've found a significant digit */ if (quo != 0) sig = TRUE; /* * if we've found a significant digit, store it; otherwise, just * reduce the exponent to account for an implied leading zero that * we won't actually store */ if (sig) { /* store the digit */ set_dig(ext, out_idx++, (int)quo); /* * if we've reached the decimal place, and the caller wants the * remainder, stop here - the caller wants the integer quotient * and remainder rather than the full quotient */ if (remp != 0 && (int)out_idx == exp) break; } else { /* all leading zeros so far - adjust the exponent */ set_exp(ext, --exp); /* * if we're doing integer division, and this leaves us with a * fractional result, we're done */ if (remp != 0 && exp == 0) break; } /* * if we've reached the last input digit and the remainder is zero, * we're done - fill out the rest of the number with trailing zeros * and stop looping */ if (rem == 0 && in_idx >= prec) { /* check to see if we have any significant digits */ if (!sig) { /* no significant digits - the result is zero */ set_zero(ext); out_idx = prec; } /* we have our result */ break; } } /* fill out any remaining output digits with zeros */ for ( ; out_idx < prec ; ++out_idx) set_dig(ext, out_idx, 0); /* pass back the remainder if desired */ if (remp != 0) { /* hand back the integer remainder */ *remp = rem; } else { /* * We're computing the full-precision quotient, not the quotient * and remainder. Round up if the remainder is more than half the * divisor, or it's exactly half the divisor and the last digit is * odd. * * (This is effectively testing to see if the next digits are * 5000... or higher. The next digit is the whole part of * remainder*10/val, so it's >=5 if remainder/val >= 0.5, which is * to say remainder >= val/2, or remainder*2 >= val.) */ if (rem*2 > val || (rem*2 == val && (get_dig(ext, prec-1) & 1))) round_up_abs(ext); } /* normalize the result */ normalize(ext); } /* ------------------------------------------------------------------------ */ /* * Normalize a number - shift it so that the first digit is non-zero. If * the number is zero, set the exponent to zero and clear the sign bit. */ void CVmObjBigNum::normalize(char *ext) { int idx; int prec = get_prec(ext); int all_zero; int nonzero_idx = 0; /* no work is necessary for anything but ordinary numbers */ if (is_nan(ext)) return; /* check for an all-zero mantissa */ for (all_zero = TRUE, idx = 0 ; idx < prec ; ++idx) { /* check this digit */ if (get_dig(ext, idx) != 0) { /* note the location of the first non-zero digit */ nonzero_idx = idx; /* note that the number isn't all zeros */ all_zero = FALSE; /* no need to keep searching */ break; } } /* if it's zero or underflows, set the canonical zero format */ if (all_zero || get_exp(ext) - nonzero_idx < -32768) { /* set the value to zero */ set_zero(ext); } else { /* clear the zero flag */ ext[VMBN_FLAGS] &= ~VMBN_F_ZERO; /* shift the mantissa left to make the first digit non-zero */ if (nonzero_idx != 0) shift_left(ext, nonzero_idx); /* decrease the exponent to account for the mantissa shift */ set_exp(ext, get_exp(ext) - nonzero_idx); } } /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ /* * Shift the value left (multiply by 10^shift) */ void CVmObjBigNum::shift_left(char *ext, unsigned int shift) { size_t prec = get_prec(ext); size_t i; /* do nothing with a zero shift */ if (shift == 0) return; /* if it's an even shift, it's especially easy */ if ((shift & 1) == 0) { /* simply move the bytes left by the required amount */ for (i = 0 ; i + shift/2 < (prec+1)/2 ; ++i) ext[VMBN_MANT + i] = ext[VMBN_MANT + i + shift/2]; /* zero the remaining digits */ for ( ; i < (prec+1)/2 ; ++i) ext[VMBN_MANT + i] = 0; /* * be sure to zero the last digit - if we have an odd precision, we * will have copied the garbage digit from the final half-byte */ set_dig(ext, prec - shift, 0); } else { /* apply the shift to each digit */ for (i = 0 ; i + shift < prec ; ++i) { unsigned int dig; /* get this source digit */ dig = get_dig(ext, i + shift); /* set this destination digit */ set_dig(ext, i, dig); } /* zero the remaining digits */ for ( ; i < prec ; ++i) set_dig(ext, i, 0); } } /* * Shift the value right (divide by 10^shift) */ void CVmObjBigNum::shift_right(char *ext, unsigned int shift) { size_t prec = get_prec(ext); size_t i; /* if it's an even shift, it's especially easy */ if ((shift & 1) == 0) { /* simply move the bytes left by the required amount */ for (i = (prec+1)/2 ; i > shift/2 ; --i) ext[VMBN_MANT + i-1] = ext[VMBN_MANT + i-1 - shift/2]; /* zero the leading digits */ for ( ; i > 0 ; --i) ext[VMBN_MANT + i-1] = 0; } else { /* apply the shift to each digit */ for (i = prec ; i > shift ; --i) { unsigned int dig; /* get this source digit */ dig = get_dig(ext, i-1 - shift); /* set this destination digit */ set_dig(ext, i-1, dig); } /* zero the remaining digits */ for ( ; i >0 ; --i) set_dig(ext, i-1, 0); } } /* * Increment a number */ void CVmObjBigNum::increment_abs(char *ext) { size_t idx; int exp = get_exp(ext); int carry; /* start at the one's place, if represented */ idx = (exp <= 0 ? 0 : (size_t)exp); /* * if the units digit is to the right of the number (i.e., the number's * scale is large), there's nothing to do */ if (idx > get_prec(ext)) return; /* increment digits */ for (carry = TRUE ; idx != 0 ; ) { int dig; /* move to the next digit */ --idx; /* get the digit value */ dig = get_dig(ext, idx); /* increment it, checking for carry */ if (dig == 9) { /* increment it to zero and keep going to carry */ set_dig(ext, idx, 0); } else { /* increment this digit */ set_dig(ext, idx, dig + 1); /* there's no carry out */ carry = FALSE; /* done */ break; } } /* if we carried past the end of the number, insert the leading 1 */ if (carry) { /* * if we still haven't reached the units position, shift right * until we do */ while (get_exp(ext) < 0) { /* shift it right and adjust the exponent */ shift_right(ext, 1); set_exp(ext, get_exp(ext) + 1); } /* shift the number right and adjust the exponent */ shift_right(ext, 1); set_exp(ext, get_exp(ext) + 1); /* insert the leading 1 */ set_dig(ext, 0, 1); } /* we know the value is now non-zero */ ext[VMBN_FLAGS] &= ~VMBN_F_ZERO; } /* * Determine if the fractional part is zero */ int CVmObjBigNum::is_frac_zero(const char *ext) { size_t idx; int exp = get_exp(ext); size_t prec = get_prec(ext); /* start at the first fractional digit, if represented */ idx = (exp <= 0 ? 0 : (size_t)exp); /* scan the digits for a non-zero digit */ for ( ; idx < prec ; ++idx) { /* if this digit is non-zero, the fraction is non-zero */ if (get_dig(ext, idx) != 0) return FALSE; } /* we didn't find any non-zero fractional digits */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Convert to an integer value */ int32_t CVmObjBigNum::ext_to_int(const char *ext, int &ov) { /* get the magnitude */ ov = FALSE; uint32_t m = convert_to_int_base(ext, ov); /* apply the sign and check limits */ if (get_neg(ext)) { /* * a T3 VM int is a 32-bit signed value (irrespective of the local * platform int size), so the magnitude of a negative value can't * exceed 2147483648 */ if (m > 2147483648U) ov = TRUE; /* negate the number and return it */ return -(long)m; } else { /* * a T3 VM positive int value can't exceed 2147483647L (regardless * of the local platform int size) */ if (m > 2147483647U) ov = TRUE; /* return the value */ return (long)m; } } /* * Convert to unsigned int */ uint32_t CVmObjBigNum::convert_to_uint(int &ov) const { /* negative numbers can't be converted to unsigned */ ov = get_neg(ext_); /* return the magnitude */ return convert_to_int_base(ext_, ov); } /* * Get the magnitude of an integer value, ignoring the sign */ uint32_t CVmObjBigNum::convert_to_int_base(const char *ext, int &ov) { size_t prec = get_prec(ext); int exp = get_exp(ext); size_t idx; size_t stop_idx; int round_inc; /* start the accumulator at zero */ uint32_t acc = 0; /* * if the exponent is negative, it means that the first non-zero digit * is in the second position after the decimal point, so even after * rounding, the result will always be zero */ if (exp < 0) return 0; /* get the rounding direction for truncating at the decimal point */ round_inc = get_round_dir(ext, exp); /* stop at the first fractional digit */ if (exp <= 0) { /* all digits are fractional */ stop_idx = 0; } else if ((size_t)exp < prec) { /* stop at the first fractional digit */ stop_idx = (size_t)exp; } else { /* all of the digits are in the whole part */ stop_idx = prec; } /* convert the integer part digit by digit */ if (stop_idx > 0) { /* loop over digits */ for (idx = 0 ; idx < stop_idx ; ++idx) { /* get this digit */ int dig = get_dig(ext, idx); /* make sure that shifting the accumulator won't overflow */ ov |= (acc > UINT32MAXVAL/10); /* shift the accumulator */ acc *= 10; /* make sure this digit won't overflow the 32-bit VM int type */ ov |= (acc > (UINT32MAXVAL - dig)); /* add the digit */ acc += dig; } } /* make sure rounding won't overflow */ ov |= (acc > UINT32MAXVAL - round_inc); /* return the result adjusted for rounding */ return acc + round_inc; } /* * Convert to a 64-bit portable representation, signed */ void CVmObjBigNum::wp8s(char *buf, int &ov) const { /* note if the value is negative */ int neg = get_neg(ext_); /* generate the absolute value */ wp8abs(buf, ov); /* if it's negative, compute the 2's complement of the buffer */ if (neg) { /* take the 2's complement */ twos_complement_p8((unsigned char *)buf); /* if it didn't come out negative, we have an overflow */ ov |= ((buf[7] & 0x80) == 0); } else { /* positive - the sign bit is set, we overflowed */ ov |= (buf[7] & 0x80); } } /* * Convert to a 64-bit portable representation, unsigned */ void CVmObjBigNum::wp8(char *buf, int &ov) const { /* generate the absolute value */ wp8abs(buf, ov); /* * if the value is negative, we can't represent it as unsigned; take * the two's complement in case they want the truncated version of the * value, and set the overflow flag */ if (get_neg(ext_)) { ov = TRUE; twos_complement_p8((unsigned char *)buf); } } /* * generate the portable 64-bit integer representation of the absolute * value of the number */ void CVmObjBigNum::wp8abs(char *buf, int &ov) const { /* * as a rough cut, if the number is greater than 10^30, it's definitely * too big for a 64-bit integer buffer */ ov = (get_exp(ext_) > 30); /* * Make a local copy of the value - we need at most 20 digits of * precision, since we only want the integer part. */ char tmp[5 + 20] = { 20, 0, 0, 0, 0 }; copy_val(tmp, ext_, TRUE); /* * Divide the value by 2^32. This splits the value into two 32-bit * halves for us, which are then easy to arrange into the buffer with * integer arithmetic. */ uint32_t lo, hi; div_by_2e32(tmp, &lo); hi = convert_to_int_base(tmp, ov); /* * store the low part in the first four bytes, the high part in the * next four bytes */ oswp4(buf, lo); oswp4(buf + 4, hi); } /* * Encode the integer portion of the number as a BER compressed integer. */ void CVmObjBigNum::encode_ber(char *buf, size_t buflen, size_t &outlen, int &ov) { /* BER can only store unsigned integers */ ov = get_neg(ext_); /* clear the output buffer */ outlen = 0; /* make a temporary copy of the value in a temp register */ uint thdl; char *tmp = alloc_temp_reg(get_prec(ext_), &thdl); copy_val(tmp, ext_, FALSE); /* keep going until the number is zero or we run out of space */ int z; while (!(z = is_zero(tmp)) && outlen < buflen) { /* get the low-order 7 bits by getting the remainder mod 128 */ ulong rem; div_by_long(tmp, 128, &rem); /* store it */ buf[outlen++] = (char)rem; } /* release the temporary register */ release_temp_reg(thdl); /* if we ran out of buffer before we ran out of digits, we overflowed */ ov |= (!z); /* if we wrote nothing, write a trivial zero value */ if (outlen == 0) buf[outlen++] = 0; /* * We stored the bytes from least significant to most significant, but * the standard format is the other way around. Reverse the bytes. */ int i, j; for (i = 0, j = (int)outlen - 1 ; i < j ; ++i, --j) { char tmpc = buf[i]; buf[i] = buf[j]; buf[j] = tmpc; } /* set the high bit in every byte except the last one */ for (i = 0 ; i < (int)outlen - 1 ; ++i) buf[i] |= 0x80; } /* * Calculate the 2's complement of a portable 8-byte integer buffer */ void CVmObjBigNum::twos_complement_p8(unsigned char *p) { size_t i; int carry; for (i = 0, carry = 1 ; i < 8 ; ++i) { p[i] = (~p[i] + carry) & 0xff; carry &= (p[i] == 0); } } /* * Convert to double */ double CVmObjBigNum::ext_to_double(const char *ext) { /* note the precision and sign */ size_t prec = get_prec(ext); int is_neg = get_neg(ext); /* if we're not a number (INF, NAN), it's an error */ if (get_type(ext) != VMBN_T_NUM) err_throw(VMERR_NO_DOUBLE_CONV); /* * if our absolute value is too large to store in a double, throw an * overflow error */ if (compare_abs(ext, cache_dbl_max()) > 1) err_throw(VMERR_NUM_OVERFLOW); /* * Our value is represented as .abcdef * 10^exp, where a is our first * digit, b is our second digit, etc. Start off the accumulator with * our most significant digit, a. The actual numeric value of this * digit is a*10^(exp-1) - one less than the exponent, because of the * implied decimal point at the start of the mantissa. */ double base = pow(10.0, get_exp(ext) - 1); double acc = get_dig(ext, 0) * base; /* * Now add in the remaining digits, starting from the second most * significant. * * We might have more precision than a double can hold. Digits added * beyond the precision of the double will have no effect on the double * value, since a double will naturally drop digits at the less * significant end when we exceed its precision. There's no point in * adding digits beyond that point. The maximum number of digits a * native double can hold is given by the float.h constant DBL_DIG; go * a couple of digits beyond this anyway, since many float packages * perform intermediate calculations at slightly higher precision and * can thus benefit from an extra digit or two for rounding purposes. */ size_t max_digits = (prec > DBL_DIG + 2 ? DBL_DIG + 2 : prec); for (size_t idx = 1 ; idx < max_digits ; ++idx) { /* get this digit */ int dig = get_dig(ext, idx); /* adjust the exponent base for this digit */ base /= 10.0; /* add the digit */ acc += dig * base; } /* apply the sign */ if (is_neg) acc = -acc; /* return the result */ return acc; } /* ------------------------------------------------------------------------ */ /* * Cache DBL_MAX */ const char *CVmObjBigNum::cache_dbl_max() { /* get the cache register; use the platform precision for a double */ size_t prec = DBL_DIG; int expanded; char *ext = S_bignum_cache->get_dbl_max_reg(calc_alloc(prec), &expanded); /* if that failed, throw an error */ if (ext == 0) err_throw(VMERR_OUT_OF_MEMORY); /* if we got a previously cached value, return it */ if (!expanded && get_prec(ext) >= prec) return ext; /* we allocated or reallocated the register, so set the new precision */ set_prec(ext, prec); /* store DBL_MAX in the register */ set_double_val(ext, DBL_MAX); /* return the register */ return ext; } /* * Cache DBL_MIN */ const char *CVmObjBigNum::cache_dbl_min() { /* get the cache register; use the platform precision for a double */ size_t prec = DBL_DIG; int expanded; char *ext = S_bignum_cache->get_dbl_min_reg(calc_alloc(prec), &expanded); /* if that failed, throw an error */ if (ext == 0) err_throw(VMERR_OUT_OF_MEMORY); /* if we got a previously cached value, return it */ if (!expanded && get_prec(ext) >= prec) return ext; /* we allocated or reallocated the register, so set the new precision */ set_prec(ext, prec); /* store DBL_MIN in the register */ set_double_val(ext, DBL_MIN); /* return the register */ return ext; } /* ------------------------------------------------------------------------ */ /* * Internal cache. The cache stores frequently used constants (pi, e, * DBL_MAX), and also provides memory for temporary registers for * intermediate values in calculations. */ /* * initialize */ CVmBigNumCache::CVmBigNumCache(size_t max_regs) { CVmBigNumCacheReg *p; size_t i; /* allocate our register array */ reg_ = (CVmBigNumCacheReg *)t3malloc(max_regs * sizeof(reg_[0])); /* remember the number of registers */ max_regs_ = max_regs; /* clear the list heads */ free_reg_ = 0; unalloc_reg_ = 0; /* we haven't actually allocated any registers yet - clear them out */ for (p = reg_, i = max_regs ; i != 0 ; ++p, --i) { /* clear this register descriptor */ p->clear(); /* link it into the unallocated list */ p->nxt_ = unalloc_reg_; unalloc_reg_ = p; } /* we haven't allocated the constants registers yet */ ln10_.clear(); ln2_.clear(); pi_.clear(); e_.clear(); dbl_max_.clear(); dbl_min_.clear(); } /* * delete */ CVmBigNumCache::~CVmBigNumCache() { CVmBigNumCacheReg *p; size_t i; /* delete each of our allocated registers */ for (p = reg_, i = max_regs_ ; i != 0 ; ++p, --i) p->free_mem(); /* free the register list array */ t3free(reg_); /* free the constant value registers */ ln10_.free_mem(); ln2_.free_mem(); pi_.free_mem(); e_.free_mem(); dbl_max_.free_mem(); dbl_min_.free_mem(); } /* * Allocate a register */ char *CVmBigNumCache::alloc_reg(size_t siz, uint *hdl) { CVmBigNumCacheReg *p; CVmBigNumCacheReg *prv; /* * search the free list for an available register satisfying the size * requirements */ for (p = free_reg_, prv = 0 ; p != 0 ; prv = p, p = p->nxt_) { /* if it satisfies the size requirements, return it */ if (p->siz_ >= siz) { /* unlink it from the free list */ if (prv == 0) free_reg_ = p->nxt_; else prv->nxt_ = p->nxt_; /* it's no longer in the free list */ p->nxt_ = 0; /* return it */ *hdl = (uint)(p - reg_); return p->buf_; } } /* * if there's an unallocated register, allocate it and use it; * otherwise, reallocate the smallest free register */ if (unalloc_reg_ != 0) { /* use the first unallocated register */ p = unalloc_reg_; /* unlink it from the list */ unalloc_reg_ = unalloc_reg_->nxt_; } else if (free_reg_ != 0) { CVmBigNumCacheReg *min_free_p; CVmBigNumCacheReg *min_free_prv = 0; /* search for the smallest free register */ for (min_free_p = 0, p = free_reg_, prv = 0 ; p != 0 ; prv = p, p = p->nxt_) { /* if it's the smallest so far, remember it */ if (min_free_p == 0 || p->siz_ < min_free_p->siz_) { /* remember it */ min_free_p = p; min_free_prv = prv; } } /* use the smallest register we found */ p = min_free_p; /* unlink it from the list */ if (min_free_prv == 0) free_reg_ = p->nxt_; else min_free_prv->nxt_ = p->nxt_; } else { /* there are no free registers - return failure */ return 0; } /* * we found a register that was either previously unallocated, or was * previously allocated but was too small - allocate or reallocate the * register at the new size */ p->alloc_mem(siz); /* return the new register */ *hdl = (uint)(p - reg_); return p->buf_; } /* * Release a register */ void CVmBigNumCache::release_reg(uint hdl) { CVmBigNumCacheReg *p = ®_[hdl]; /* add the register to the free list */ p->nxt_ = free_reg_; free_reg_ = p; } /* * Release all registers */ void CVmBigNumCache::release_all() { size_t i; /* mark each of our registers as not in use */ for (i = 0 ; i < max_regs_ ; ++i) release_reg(i); } qtads-2.1.7/tads3/vmbt3_nd.cpp000066400000000000000000000044151265017072300161020ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbt3_nd.cpp - T3 VM system interface function set - non-debugger version Function Notes Modified 03/11/00 MJRoberts - Creation */ #include #include "t3std.h" #include "vmtype.h" #include "vmbif.h" #include "vmbift3.h" #include "vmstack.h" #include "vmrun.h" #include "charmap.h" #include "vmdatasrc.h" #include "vmlog.h" /* * Debug Trace */ void CVmBifT3::debug_trace(VMG_ uint argc) { /* make sure we have at least one argument */ if (argc < 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* pop the flags and see what we're being asked to do */ switch(pop_int_val(vmg0_)) { case T3DBG_CHECK: /* check arguments */ check_argc(vmg_ argc, 1); /* we're just being asked if the debugger is present - it's not */ retval_nil(vmg0_); break; case T3DBG_BREAK: /* check arguments */ check_argc(vmg_ argc, 1); /* tell the caller there's no debugger */ retval_nil(vmg0_); break; case T3DBG_LOG: { /* check arguments and retrieve the string */ check_argc(vmg_ argc, 2); const char *str = G_stk->get(0)->get_as_string(vmg0_); if (str == 0) err_throw(VMERR_BAD_TYPE_BIF); /* decode and skip the length prefix */ int len = vmb_get_len(str); str += VMB_LEN; /* convert the string to the local file character set */ char *lstr; size_t llen = G_cmap_to_file->map_utf8_alo(&lstr, str, len); /* log the message */ vm_log(vmg_ lstr, llen); /* done with the mapped string */ t3free(lstr); /* discard the string */ G_stk->discard(); } /* no return value */ retval_nil(vmg0_); break; default: /* anything else just returns nil, to allow for future expansion */ G_stk->discard(argc - 1); retval_nil(vmg0_); break; } } qtads-2.1.7/tads3/vmbytarr.cpp000066400000000000000000002227121265017072300162360ustar00rootroot00000000000000/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbytarr.cpp - TADS 3 ByteArray intrinsic class Function Notes Modified 06/05/01 MJRoberts - Creation */ #include #include #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" #include "vmbytarr.h" #include "vmbif.h" #include "vmfile.h" #include "vmerrnum.h" #include "vmerr.h" #include "vmstack.h" #include "vmmeta.h" #include "vmundo.h" #include "vmrun.h" #include "charmap.h" #include "utf8.h" #include "vmstr.h" #include "vmcset.h" #include "vmdatasrc.h" #include "vmpack.h" #include "sha2.h" #include "md5.h" /* ------------------------------------------------------------------------ */ /* * Byte Array data source */ class CVmByteArraySource: public CVmDataSource { public: /* set up a data source on the entire range of the array */ CVmByteArraySource(VMG_ vm_obj_id_t arr) { /* remember the array and globals */ this->vmg = VMGLOB_ADDR; this->arr_id = arr; this->arr = (CVmObjByteArray *)vm_objp(vmg_ arr); /* we provide access to the entire range of the array */ this->idx = 0; this->start_idx = 0; this->end_idx = this->arr->get_element_count(); } /* set up a data source on a slice of the array */ CVmByteArraySource(VMG_ vm_obj_id_t arr, long start_idx, long len) { /* remember the array and globals */ this->vmg = VMGLOB_ADDR; this->arr_id = arr; this->arr = (CVmObjByteArray *)vm_objp(vmg_ arr); /* remember our window */ this->idx = start_idx; this->start_idx = start_idx; this->end_idx = start_idx + len; /* limit the window to the actual available size */ if ((unsigned long)end_idx > this->arr->get_element_count()) end_idx = this->arr->get_element_count(); } CVmDataSource *clone(VMG_ const char * /*mode*/) { return new CVmByteArraySource( vmg_ arr_id, start_idx, end_idx - start_idx); } /* read bytes; returns 0 on success, non-zero on error */ virtual int read(void *buf, size_t len) { /* limit the read to the available length */ size_t rdlen = (idx + (long)len > end_idx ? (size_t)(end_idx - idx) : len); /* copy the requested bytes starting at the current index */ size_t copied = arr->copy_to_buf((unsigned char *)buf, idx + 1, rdlen); /* move past the bytes copied */ idx += copied; /* we were successful if we copied exactly the requested length */ return copied != len; } /* read bytes; returns the number of bytes read */ virtual int readc(void *buf, size_t len) { /* limit the read to the available length */ size_t rdlen = (idx + (long)len > end_idx ? (size_t)(end_idx - idx) : len); /* copy the requested bytes */ size_t copied = arr->copy_to_buf((unsigned char *)buf, idx + 1, rdlen); /* move past the bytes copied */ idx += copied; /* return the number of bytes actually read */ return copied; } /* write bytes; returns 0 on success, non-zero on error */ virtual int write(const void *buf, size_t len) { /* establish global access */ VMGLOB_PTR(vmg); /* limit the write to the available length */ size_t wrtlen = (idx + (long)len > end_idx ? (size_t)(end_idx - idx) : len); /* copy the requested bytes (saving undo) */ size_t copied = arr->copy_from_buf_undo( vmg_ arr_id, (const unsigned char *)buf, idx + 1, wrtlen); /* move past the bytes copied */ idx += copied; /* we were successful if we copied exactly the requested length */ return copied != len; } /* get the length of our slice of the array in bytes */ virtual long get_size() { return end_idx - start_idx; } /* get the current seek location */ virtual long get_pos() { return idx - start_idx; } /* set the current seek location - 'mode' is an OSFSK_xxx mode */ virtual int seek(long ofs, int mode) { switch (mode) { case OSFSK_SET: idx = start_idx + ofs; break; case OSFSK_CUR: idx += ofs; break; case OSFSK_END: idx = end_idx + ofs; break; default: return 1; } /* make sure it's in range */ if (idx < start_idx) idx = start_idx; else if (idx > end_idx) idx = end_idx; /* success */ return 0; } /* flush - returns 0 on success, non-zero on error */ virtual int flush() { return 0; } /* close the underlying system resource */ virtual void close() { } protected: /* our underlying array */ vm_obj_id_t arr_id; CVmObjByteArray *arr; /* current read/write index in the array */ long idx; /* the bounds of the slice of the array we present as the file */ long start_idx; long end_idx; /* we need globals in some interface routines without the VMG_ param */ vm_globals *vmg; }; /* ------------------------------------------------------------------------ */ /* * Integer format codes. A full format code is given by a bitwise * combination of size, byte order, and signedness. */ /* integer sizes */ #define FmtSizeMask 0x000F #define FmtInt8 0x0000 #define FmtInt16 0x0001 #define FmtInt32 0x0002 /* integer byte orders */ #define FmtOrderMask 0x00F0 #define FmtLittleEndian 0x0000 #define FmtBigEndian 0x0010 /* integer signedness */ #define FmtSignedMask 0x0F00 #define FmtSigned 0x0000 #define FmtUnsigned 0x0100 /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassByteArray metaclass_reg_obj; CVmMetaclass *CVmObjByteArray::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjByteArray:: *CVmObjByteArray::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjByteArray::getp_undef, /* 0 */ &CVmObjByteArray::getp_length, /* 1 */ &CVmObjByteArray::getp_subarray, /* 2 */ &CVmObjByteArray::getp_copy_from, /* 3 */ &CVmObjByteArray::getp_fill_val, /* 4 */ &CVmObjByteArray::getp_to_string, /* 5 */ &CVmObjByteArray::getp_read_int, /* 6 */ &CVmObjByteArray::getp_write_int, /* 7 */ &CVmObjByteArray::getp_packBytes, /* 8 */ &CVmObjByteArray::getp_unpackBytes, /* 9 */ &CVmObjByteArray::getp_sha256, /* 10 */ &CVmObjByteArray::getp_digestMD5 /* 11 */ }; /* static property indices */ const int PROPIDX_packBytes = 8; /* ------------------------------------------------------------------------ */ /* * Create from stack */ vm_obj_id_t CVmObjByteArray::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id = VM_INVALID_OBJ; CVmObjByteArray *arr; unsigned long cnt; vm_val_t *arg1; const char *str; /* check our arguments */ if (argc < 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* see what we have for the first argument */ arg1 = G_stk->get(0); if (arg1->typ == VM_INT) { /* * it's a simple count argument - make sure we only have one * argument */ if (argc != 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* get the number of elements */ cnt = (unsigned long)arg1->val.intval; /* create the array with the given number of elements */ id = vm_new_id(vmg_ FALSE, FALSE, FALSE); arr = new (vmg_ id) CVmObjByteArray(vmg_ cnt); /* set each element to zero */ arr->fill_with(0, 1, cnt); } else if ((str = arg1->get_as_string(vmg0_)) != 0) { /* * String argument - we're creating a byte array from the string. * We can map it from a character set, or we can just stuff the * character values into bytes. Check arguments - we must have one * or two of them (the string, and the optional character set) */ if (argc < 1 || argc > 2) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* fetch the character mapper, if there is one */ const vm_val_t *cmap = (argc >= 2 ? G_stk->get(1) : 0); /* create the byte array */ id = create_from_string(vmg_ G_stk->get(0), str, cmap); } else if (arg1->typ == VM_OBJ && is_byte_array(vmg_ arg1->val.obj)) { unsigned long src_idx; unsigned long src_cnt; /* remember the source array */ CVmObjByteArray *src_arr = (CVmObjByteArray *)vm_objp(vmg_ arg1->val.obj); /* get the count from the array */ src_cnt = src_arr->get_element_count(); /* * check for the optional actual element count, and the optional * starting index */ if (argc == 1) { /* no size specified - use the same size as the original */ cnt = src_cnt; /* start at the first element */ src_idx = 1; } else { /* make sure it's an integer */ if (G_stk->get(1)->typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); /* use the specified starting index */ src_idx = (unsigned long)G_stk->get(1)->val.intval; /* if the index is below 1, force it to 1 */ if (src_idx < 1) src_idx = 1; /* check for the starting element argument */ if (argc >= 3) { /* make sure it's an integer */ if (G_stk->get(2)->typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); /* use the specified element count */ cnt = (unsigned long)G_stk->get(2)->val.intval; /* make sure we don't have any extra arguments */ if (argc > 3) err_throw(VMERR_WRONG_NUM_OF_ARGS); } else { /* * no count specified - use the number of elements in the * original remaining after the starting index */ if (src_idx > src_cnt) cnt = 0; else cnt = src_cnt + 1 - src_idx; } } /* create the new array */ id = vm_new_id(vmg_ FALSE, FALSE, FALSE); arr = new (vmg_ id) CVmObjByteArray(vmg_ cnt); /* copy the source array */ arr->copy_from(1, src_arr, src_idx, cnt); } else { /* invalid argument */ err_throw(VMERR_BAD_TYPE_BIF); } /* discard arguments */ G_stk->discard(argc); /* return the new object */ return id; } /* ------------------------------------------------------------------------ */ /* * Create a new byte array from a string, mapping through the given * character set object if present, or just stuffing the character values * into bytes if not. */ vm_obj_id_t CVmObjByteArray::create_from_string( VMG_ const vm_val_t *strval, const char *str, const vm_val_t *mapval) { /* we can't allocate our object until we know the mapped byte count */ vm_obj_id_t id = VM_INVALID_OBJ; CVmObjByteArray *arr = 0; /* get the string's length and skip the length prefix */ size_t len = vmb_get_len(str); str += VMB_LEN; /* * We have two modes: map to bytes via a character set, or stuff * unmapped character values into bytes. Check which mode we're using. */ if (mapval == 0 || mapval->typ == VM_NIL) { /* * No character mapper - just stuff each character into a byte. If * a character is outside the 0..255 range, it's an error. */ /* count the number of characters */ utf8_ptr p((char *)str); size_t clen = p.len(len); /* allocate the array - each string character is one array byte */ id = create(vmg_ FALSE, clen); arr = (CVmObjByteArray *)vm_objp(vmg_ id); /* copy bytes into the array */ for (int idx = 1 ; len != 0 ; ) { /* translate a buffer-full */ unsigned char buf[256]; size_t cur = 0; for ( ; len != 0 && cur < sizeof(buf) ; p.inc(&len)) { /* get the next character; make sure it fits in a byte */ wchar_t ch = p.getch(); if (ch > 255) err_throw(VMERR_NUM_OVERFLOW); /* save it */ buf[cur++] = (unsigned char)ch; } /* copy this chunk to the array */ arr->cons_copy_from_buf(buf, idx, cur); idx += cur; } } else { /* interpret the character set argument */ vm_obj_id_t mapid = CVmBif::get_charset_obj(vmg_ mapval); /* get the to-local mapping from the character set */ CCharmapToLocal *mapper = ((CVmObjCharSet *)vm_objp(vmg_ mapid))->get_to_local(vmg0_); /* * first, do a mapping with a null output buffer to determine how * many bytes we need for the mapping */ size_t src_bytes_used; size_t byte_len = mapper->map_utf8(0, 0, str, len, &src_bytes_used); /* allocate a new ByteArray with the required number of bytes */ id = CVmObjByteArray::create(vmg_ FALSE, byte_len); arr = (CVmObjByteArray *)vm_objp(vmg_ id); /* convert it again, this time storing the bytes */ for (size_t out_idx = 1 ; len != 0 ; ) { char buf[128]; /* convert a buffer-full */ byte_len = mapper->map_utf8(buf, sizeof(buf), str, len, &src_bytes_used); /* store the bytes in the byte array */ arr->cons_copy_from_buf((unsigned char *)buf, out_idx, byte_len); /* advance past the output bytes we used */ out_idx += byte_len; /* advance past the source bytes we used */ str += src_bytes_used; len -= src_bytes_used; } } /* return the new array's object ID */ return id; } /* ------------------------------------------------------------------------ */ /* * Create from binary data */ vm_obj_id_t CVmObjByteArray::create_from_bytes( VMG_ int in_root_set, const char *buf, size_t len) { /* allocate the array */ vm_obj_id_t id = create(vmg_ in_root_set, len); CVmObjByteArray *arr = (CVmObjByteArray *)vm_objp(vmg_ id); /* copy the bytes into the array */ arr->cons_copy_from_buf((const unsigned char *)buf, 1, len); /* return the new array's object ID */ return id; } /* ------------------------------------------------------------------------ */ /* * Create with no contents */ vm_obj_id_t CVmObjByteArray::create(VMG_ int in_root_set) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjByteArray(); return id; } /* * Create with the given element count */ vm_obj_id_t CVmObjByteArray::create(VMG_ int in_root_set, unsigned long ele_count) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjByteArray(vmg_ ele_count); return id; } /* ------------------------------------------------------------------------ */ /* * Instantiate */ CVmObjByteArray::CVmObjByteArray(VMG_ unsigned long ele_count) { /* allocate space */ alloc_array(vmg_ ele_count); } /* ------------------------------------------------------------------------ */ /* * Allocate space */ void CVmObjByteArray::alloc_array(VMG_ unsigned long ele_count) { unsigned long ele_rem; size_t slot_cnt; size_t alloc_size; size_t i; /* * figure out how many first-level page tables we need - each * first-level page table refers to 256M bytes (8k pages per * second-level page table times 32k bytes per page), so we need one * page table pointer per 256 megabytes == 2^15*2^32 == 2^28. */ slot_cnt = (ele_count == 0 ? 0 : ((size_t)((ele_count - 1) >> 28) + 1)); /* * allocate the extension - 4 bytes for the element count plus one * (char **) per slot */ alloc_size = 4 + slot_cnt*sizeof(char **); ext_ = (char *)G_mem->get_var_heap()->alloc_mem(alloc_size, this); /* set the element count */ set_element_count(ele_count); /* allocate the second-level page tables */ for (ele_rem = ele_count, i = 0 ; i < slot_cnt ; ++i) { unsigned char **pgtab; unsigned long pgcnt; size_t pg; /* * Determine how many pages we need in this page table. Each page * holds 32k bytes, so we need one page per 32k bytes remaining to * allocate. However, each page table can hold only up to 8k page * pointers, so limit this table to 8k pages. */ pgcnt = ((ele_rem - 1) >> 15) + 1; if (pgcnt > 8*1024) pgcnt = 8*1024; /* allocate this second-level page table */ pgtab = (unsigned char **)t3malloc( (size_t)(pgcnt * sizeof(unsigned char *))); /* set this page table pointer */ get_page_table_array()[i] = pgtab; /* allocate the pages in this table */ for (pg = 0 ; pg < pgcnt ; ++pg) { size_t pgsiz; /* * calculate the size for this page - the maximum size of a * page is 32k, but allocate only the amount remaining if we * have less than 32k left to allocate */ pgsiz = 32*1024; if (pgsiz > ele_rem) pgsiz = (size_t)ele_rem; /* deduct this page's size from the remaining element count */ ele_rem -= pgsiz; /* allocate and store this page */ pgtab[pg] = (unsigned char *)t3malloc(pgsiz); } } } /* ------------------------------------------------------------------------ */ /* * Notify of deletion */ void CVmObjByteArray::notify_delete(VMG_ int /*in_root_set*/) { size_t slot; unsigned char ***slotp; size_t slot_cnt; size_t bytes_rem; /* if we have no extension, there's nothing to do */ if (ext_ == 0) return; /* calculate the number of second-level page table slots */ slot_cnt = (size_t)(get_element_count() == 0 ? 0 : ((get_element_count() >> 28) + 1)); /* we have all of the bytes in the array left do delete */ bytes_rem = get_element_count(); /* traverse the list of second-level page tables and delete each one */ for (slot = 0, slotp = get_page_table_array() ; slot < slot_cnt ; ++slot, ++slotp) { size_t pg; unsigned char **pgp; /* traverse each page in this page table */ for (pg = 0, pgp = *slotp ; pg < 8*1024 && bytes_rem != 0 ; ++pg, ++pgp) { /* delete this page */ t3free(*pgp); /* * deduct this page's size from the remaining bytes to delete * - the page size is up to 32k, but no more than the * remaining size */ if (bytes_rem > 32*1024) bytes_rem -= 32*1024; else bytes_rem = 0; } /* delete this page table */ t3free(*slotp); } /* free our extension */ G_mem->get_var_heap()->free_mem(ext_); } /* ------------------------------------------------------------------------ */ /* * Cast to string. Maps to string treating the bytes as Latin-1 * characters. */ const char *CVmObjByteArray::cast_to_string( VMG_ vm_obj_id_t self, vm_val_t *new_str) const { /* set up a source stream to read from the byte array into the string */ CVmByteArraySource src(vmg_ self); /* create the string */ new_str->set_obj(CVmObjString::create_latin1(vmg_ FALSE, &src)); /* return the new string's byte buffer */ return new_str->get_as_string(vmg0_); } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjByteArray::set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * Get a static property */ int CVmObjByteArray::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* get the function table index */ int idx = G_meta_table->prop_to_vector_idx( metaclass_reg_->get_reg_idx(), prop); /* check for static methods we define in ByteArray */ switch (idx) { case PROPIDX_packBytes: /* the static version of packBytes() */ return static_packBytes(vmg_ result, argc); default: /* not one of ours - defer to the superclass */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } } /* ------------------------------------------------------------------------ */ /* * get a property */ int CVmObjByteArray::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property index to an index into our function table */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * ByteArray private undo record - saved byte block */ struct bytearray_undo_bytes { /* next block in the list */ bytearray_undo_bytes *nxt_; /* the bytes - overallocated to the required size */ unsigned char buf_[1]; }; /* * ByteArray private undo record */ class bytearray_undo_rec { public: bytearray_undo_rec(CVmObjByteArray *arr, unsigned long start_idx, unsigned long cnt) { unsigned long idx; unsigned long rem; bytearray_undo_bytes *tail; /* remember the starting index and length of the saved data */ idx_ = start_idx; cnt_ = cnt; /* store the original data in a linked list of byte blocks */ for (idx = start_idx, rem = cnt, save_head_ = tail = 0 ; rem != 0 ; ) { bytearray_undo_bytes *p; size_t cur; /* allocate up to 32k, or the amount remaining if lower */ cur = 32768; if (cur > rem) cur = (size_t)rem; /* allocate a new block */ p = (bytearray_undo_bytes *)t3malloc( sizeof(bytearray_undo_bytes) + cur - 1); /* link this block into our list */ if (tail != 0) tail->nxt_ = p; else save_head_ = p; tail = p; p->nxt_ = 0; /* copy bytes into this block */ arr->copy_to_buf(p->buf_, idx, cur); /* move past the copied bytes */ idx += cur; rem -= cur; } } ~bytearray_undo_rec() { bytearray_undo_bytes *cur; bytearray_undo_bytes *nxt; /* delete our list of saved byte blocks */ for (cur = save_head_ ; cur != 0 ; cur = nxt) { /* * remember the next one, as we're about to lose the current * one's memory */ nxt = cur->nxt_; /* delete this one */ t3free(cur); } } /* copy our data back into an array */ void apply_undo(CVmObjByteArray *arr) { bytearray_undo_bytes *cur; unsigned long idx; unsigned long rem; /* delete our list of saved byte blocks */ for (cur = save_head_, idx = idx_, rem = cnt_ ; rem != 0 ; ) { size_t copy_len; /* limit the copy to our block size (32k) */ copy_len = 32768; if (copy_len > rem) copy_len = (size_t)rem; /* copy this block into the array */ arr->copy_from_buf(cur->buf_, idx, copy_len); /* move past the copied data */ cur = cur->nxt_; idx += copy_len; rem -= copy_len; } } /* starting index */ unsigned long idx_; /* length */ unsigned long cnt_; /* head of list of blocks of saved data */ bytearray_undo_bytes *save_head_; }; /* * Save undo for a change to a range of the array */ void CVmObjByteArray::save_undo(VMG_ vm_obj_id_t self, unsigned long start_idx, unsigned long cnt) { bytearray_undo_rec *rec; vm_val_t oldval; /* create our key record - this contains the entire original value */ rec = new bytearray_undo_rec(this, start_idx, cnt); /* we don't use the old value for anything; use nil as a dummy */ oldval.set_nil(); /* add the undo record */ if (!G_undo->add_new_record_ptr_key(vmg_ self, rec, &oldval)) { /* failed to save the undo - discard our private key record */ delete rec; } } /* * Apply undo */ void CVmObjByteArray::apply_undo(VMG_ struct CVmUndoRecord *gen_rec) { /* apply our private undo record, if present */ if (gen_rec->id.ptrval != 0) { bytearray_undo_rec *rec; /* get my private record */ rec = (bytearray_undo_rec *)gen_rec->id.ptrval; /* apply the undo in the record to self */ rec->apply_undo(this); /* delete the record now that it's been applied */ delete rec; /* clear the pointer so we know it's gone */ gen_rec->id.ptrval = 0; } } /* * Discard undo */ void CVmObjByteArray::discard_undo(VMG_ struct CVmUndoRecord *rec) { /* delete our extra information record if present */ if (rec->id.ptrval != 0) { /* free the record */ t3free((bytearray_undo_rec *)rec->id.ptrval); /* clear the pointer so we know it's gone */ rec->id.ptrval = 0; } } /* ------------------------------------------------------------------------ */ /* * load from an image file */ void CVmObjByteArray::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load from the image data */ load_image_data(vmg_ ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload the object from image data */ void CVmObjByteArray::reload_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t siz) { /* load the image data */ load_image_data(vmg_ ptr, siz); } /* * Load from image data */ void CVmObjByteArray::load_image_data(VMG_ const char *ptr, size_t siz) { unsigned long cnt; unsigned long idx; /* if we already have memory allocated, free it */ notify_delete(vmg_ FALSE); /* get the new array size */ cnt = t3rp4u(ptr); /* make sure the size isn't larger than we'd expect */ if (siz > 4 + cnt) siz = 4 + cnt; /* allocate memory at the new size as indicated in the image data */ alloc_array(vmg_ cnt); /* if the size is smaller than we'd expect, set extra elements to nil */ if (siz < VMB_LEN + (VMB_DATAHOLDER * cnt)) { /* fill everything with zeros to start with */ fill_with(0, 1, cnt); } /* copy the bytes */ for (ptr += 4, siz -= 4, idx = 1 ; siz != 0 ; ) { unsigned char *dstp; size_t chunk_size; size_t avail; /* get the next chunk */ dstp = get_ele_ptr(idx, &avail); /* limit this chunk size to the remaining copy size */ chunk_size = avail; if (chunk_size > siz) chunk_size = siz; /* copy this chunk */ memcpy(dstp, ptr, chunk_size); /* advance past this chunk */ idx += chunk_size; ptr += chunk_size; siz -= chunk_size; } } /* ------------------------------------------------------------------------ */ /* * save to a file */ void CVmObjByteArray::save_to_file(VMG_ class CVmFile *fp) { unsigned long rem; unsigned long idx; /* write the element count */ fp->write_int4(get_element_count()); /* write the bytes in chunks */ for (idx = 1, rem = get_element_count() ; rem != 0 ; ) { size_t avail; size_t chunk; unsigned char *p; /* get the next chunk */ p = get_ele_ptr(idx, &avail); /* limit this copy to the remaining bytes */ chunk = avail; if (chunk > rem) chunk = (size_t)rem; /* write this chunk */ fp->write_bytes((char *)p, chunk); /* advance past this chunk */ idx += chunk; rem -= chunk; } } /* * restore from a file */ void CVmObjByteArray::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *) { unsigned long ele_cnt; unsigned long idx; unsigned long rem; /* read the element count */ ele_cnt = fp->read_uint4(); /* allocate or reallocate as needed */ if (ext_ == 0) { /* we're not yet allocated - allocate now */ alloc_array(vmg_ ele_cnt); } else { /* already allocated - if it's a different size, reallocate */ if (get_element_count() != ele_cnt) { /* delete the old array */ notify_delete(vmg_ FALSE); /* allocate a new one */ alloc_array(vmg_ ele_cnt); } } /* read the data */ for (idx = 1, rem = ele_cnt ; rem != 0 ; ) { size_t avail; size_t chunk; unsigned char *p; /* get the next chunk */ p = get_ele_ptr(idx, &avail); /* limit this copy to the remaining bytes */ chunk = avail; if (chunk > rem) chunk = (size_t)rem; /* read this chunk */ fp->read_bytes((char *)p, chunk); /* advance past this chunk */ idx += chunk; rem -= chunk; } } /* ------------------------------------------------------------------------ */ /* * Retrieve the value at the given index */ int CVmObjByteArray::index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val) { /* get the index */ int32_t idx = index_val->num_to_int(vmg0_); /* make sure it's in range */ if (idx < 1 || (uint32_t)idx > get_element_count()) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* get a pointer to the desired element */ size_t avail; unsigned char *p = get_ele_ptr(idx, &avail); /* return the value as an integer */ result->set_int((int)(unsigned short)*p); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * set an indexed element of the array */ int CVmObjByteArray::set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val) { /* get the index value as an integer */ int32_t idx = index_val->num_to_int(vmg0_); /* make sure it's in range - 1 to our element count, inclusive */ if (idx < 1 || (uint32_t)idx > get_element_count()) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* save undo for the change */ save_undo(vmg_ self, idx, 1); /* get the new value as an integer */ int32_t new_byte = new_val->num_to_int(vmg0_); /* make sure it's in range */ if (new_byte < 0 || new_byte > 255) err_throw(VMERR_OUT_OF_RANGE); /* get a pointer to the desired element */ size_t avail; unsigned char *p = get_ele_ptr(idx, &avail); /* set the value */ *p = (unsigned char)new_byte; /* the result is the original array value */ new_container->set_obj(self); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Compare for equality */ int CVmObjByteArray::equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int /*depth*/) const { CVmObjByteArray *other; unsigned long idx; unsigned long rem; /* if it's a self-reference, it's certainly equal */ if (val->typ == VM_OBJ && val->val.obj == self) return TRUE; /* if it's not another byte array, it's not equal */ if (val->typ != VM_OBJ || !is_byte_array(vmg_ val->val.obj)) return FALSE; /* we know it's another byte array - cast it */ other = (CVmObjByteArray *)vm_objp(vmg_ val->val.obj); /* if it's not of the same length, it's not equal */ if (other->get_element_count() != get_element_count()) return FALSE; /* compare the arrays */ for (idx = 1, rem = get_element_count() ; rem != 0 ; ) { unsigned char *p1; unsigned char *p2; size_t avail1; size_t avail2; size_t chunk; /* get the next chunk of each array */ p1 = get_ele_ptr(idx, &avail1); p2 = other->get_ele_ptr(idx, &avail2); /* if the chunk sizes aren't the same, there's some problem */ assert(avail1 == avail2); /* limit the chunk size to the remaining size */ chunk = avail1; if (chunk > rem) chunk = (size_t)rem; /* if the chunks differ, the arrays differ */ if (memcmp(p1, p2, chunk) != 0) return FALSE; /* advance past the chunk */ idx += avail1; rem -= avail1; } /* we found no differences */ return TRUE; } /* * Calculate a hash value */ uint CVmObjByteArray::calc_hash(VMG_ vm_obj_id_t self, int /*depth*/) const { unsigned long idx; unsigned long rem; uint hash; /* add up the bytes in the array */ for (hash = 0, idx = 1, rem = get_element_count() ; rem != 0 ; ) { unsigned char *p; size_t avail; size_t chunk; /* get the next chunk */ p = get_ele_ptr(idx, &avail); /* limit the chunk size to the remaining size */ chunk = avail; if (chunk > rem) chunk = (size_t)rem; /* advance our counters for this chunk */ idx += chunk; rem -= chunk; /* add up the bytes in this part */ for ( ; chunk != 0 ; --chunk, ++p) hash += *p; } /* return the result */ return hash; } /* ------------------------------------------------------------------------ */ /* * Copy bytes from an array */ void CVmObjByteArray::copy_from(unsigned long dst_idx, CVmObjByteArray *src_arr, unsigned long src_idx, unsigned long cnt) { /* if we're moving zero bytes, there's nothing to do */ if (cnt == 0) return; /* make sure we don't overrun our array */ if (dst_idx > get_element_count()) cnt = 0; else if (dst_idx + cnt - 1 > get_element_count()) cnt = get_element_count() + 1 - dst_idx; /* * If the source and destination objects are the same, and the source * and destination regions overlap, we must take care to move the * bytes in such a way that we don't overwrite parts of the source in * the course of moving the bytes. */ if (src_arr == this && src_idx + cnt - 1 >= dst_idx && src_idx <= dst_idx + cnt - 1) { /* the regions overlap - use the overlap-safe move routine */ move_bytes(dst_idx, src_idx, cnt); /* done */ return; } /* continue until we exhaust the count */ while (cnt != 0) { size_t src_avail; size_t src_chunk_size; unsigned char *srcp; /* if we're past the end of the source array, stop copying */ if (src_idx > src_arr->get_element_count()) break; /* get the next source chunk */ srcp = src_arr->get_ele_ptr(src_idx, &src_avail); /* limit this chunk size to the remaining copy size */ src_chunk_size = src_avail; if (src_chunk_size > cnt) src_chunk_size = (size_t)cnt; /* limit this chunk to the remaining source array size */ if (src_idx + src_chunk_size - 1 > src_arr->get_element_count()) src_chunk_size = src_arr->get_element_count() + 1 - src_idx; /* copy this chunk into the destination */ copy_from_buf(srcp, dst_idx, src_chunk_size); /* move past this chunk in the source */ cnt -= src_chunk_size; src_idx += src_chunk_size; /* move past this chunk in the destination */ dst_idx += src_chunk_size; } /* * if there's any copying size left, we ran out of source array bytes * - fill the balance of the destination array with zeros */ if (cnt != 0) fill_with(0, dst_idx, cnt); } /* * Move bytes within our array. This is safe even if the source and * destination regions overlap. */ void CVmObjByteArray::move_bytes(unsigned long dst_idx, unsigned long src_idx, unsigned long cnt) { size_t src_avail; size_t dst_avail; unsigned char *srcp; unsigned char *dstp; /* * If the destination is before the source, we're moving bytes down, * so we must start at the low end and work forwards through the * array. If the destination is after the source, we're moving bytes * up, so we must start at the high end and work backwards through the * array. */ if (dst_idx < src_idx) { /* * Moving bytes down in the array - start at the low end and work * forwards through the array. Get the starting pointers and * available lengths. */ srcp = get_ele_ptr(src_idx, &src_avail); dstp = get_ele_ptr(dst_idx, &dst_avail); /* keep going until we've moved all of the bytes requested */ while (cnt != 0) { size_t move_len; /* * figure the largest amount we can move - we can move the * smallest of the remaining requested move size, the source * chunk, and the destination chunk */ move_len = cnt; if (move_len > src_avail) move_len = src_avail; if (move_len > dst_avail) move_len = dst_avail; /* move the data */ memmove(dstp, srcp, move_len); /* advance all of the counters by the move size */ srcp += move_len; dstp += move_len; cnt -= move_len; src_avail -= move_len; dst_avail -= move_len; src_idx += move_len; dst_idx += move_len; /* stop if we're done */ if (cnt == 0) break; /* if the source chunk is at an end, get the next one */ if (src_avail == 0) srcp = get_ele_ptr(src_idx, &src_avail); /* if the destination chunk is at an end, get the next one */ if (dst_avail == 0) dstp = get_ele_ptr(dst_idx, &dst_avail); } } else { /* * We're to move bytes up in the array - start at the high end and * work backwards through the array. Advance each index to one * past the last byte of its range. */ src_idx += cnt; dst_idx += cnt; /* get the chunk pointers */ srcp = get_ele_ptr(src_idx - 1, &src_avail) + 1; dstp = get_ele_ptr(dst_idx - 1, &dst_avail) + 1; /* * since we're working backwards, we actually want to know the * number of bytes on the page *before* the current pointers, so * subtract the available spaces from the size of the page to get * the available space preceding each */ src_avail = 32*1024 - src_avail + 1; dst_avail = 32*1024 - dst_avail + 1; /* keep going until we've moved all of the requested bytes */ while (cnt != 0) { size_t move_len; /* * figure the largest amount we can move - we can move the * smallest of the remaining requested move size, the source * chunk, and the destination chunk */ move_len = cnt; if (move_len > src_avail) move_len = src_avail; if (move_len > dst_avail) move_len = dst_avail; /* move the data */ memmove(dstp - move_len, srcp - move_len, move_len); /* advance all of the counters by the move size */ srcp -= move_len; dstp -= move_len; cnt -= move_len; src_avail -= move_len; dst_avail -= move_len; src_idx -= move_len; dst_idx -= move_len; /* stop if we're done */ if (cnt == 0) break; /* if we've exhausted the source chunk, get the next one */ if (src_avail == 0) { srcp = get_ele_ptr(src_idx - 1, &src_avail) + 1; src_avail = 32*1024 - src_avail + 1; } /* if we've exhausted the destination chunk, get the next one */ if (dst_avail == 0) { dstp = get_ele_ptr(dst_idx - 1, &dst_avail) + 1; dst_avail = 32*1024 - dst_avail + 1; } } } } /* ------------------------------------------------------------------------ */ /* * Fill a 1-based index range with the given value */ void CVmObjByteArray::fill_with(unsigned char val, unsigned long start_idx, unsigned long cnt) { unsigned long idx; unsigned long rem; /* ensure we don't overrun the array */ if (start_idx > get_element_count()) cnt = 0; else if (start_idx + cnt - 1 > get_element_count()) cnt = get_element_count() + 1 - start_idx; /* continue until we exhaust the count */ for (idx = start_idx, rem = cnt ; rem != 0 ; ) { size_t avail; size_t chunk_size; unsigned char *p; /* get the next chunk */ p = get_ele_ptr(idx, &avail); /* limit this chunk size to the remaining size to fill */ chunk_size = avail; if (chunk_size > rem) chunk_size = (size_t)rem; /* fill this chunk */ memset(p, val, chunk_size); /* skip this chunk */ rem -= chunk_size; idx += chunk_size; } } /* ------------------------------------------------------------------------ */ /* * property evaluator - length */ int CVmObjByteArray::getp_length(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* return the length */ retval->set_int(get_element_count()); /* handled */ return TRUE; } /* * property evaluator - subarray */ int CVmObjByteArray::getp_subarray(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(1, 1); unsigned long idx; unsigned long cnt; CVmObjByteArray *arr; /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* get the starting index */ idx = CVmBif::pop_int_val(vmg0_); /* force it to be in range */ if (idx < 1) idx = 1; else if (idx > get_element_count() + 1) idx = get_element_count() + 1; /* if there's a count, get it */ if (argc >= 2) { /* get the explicit count */ cnt = CVmBif::pop_int_val(vmg0_); } else { /* use the entire rest of the array */ cnt = get_element_count(); } /* limit the count to the available size */ if (idx > get_element_count()) cnt = 0; else if (idx + cnt - 1 > get_element_count()) cnt = get_element_count() + 1 - idx; /* push a self-reference while we're working for gc protection */ G_stk->push()->set_obj(self); /* allocate a new array to hold the result */ retval->set_obj(create(vmg_ FALSE, cnt)); arr = (CVmObjByteArray *)vm_objp(vmg_ retval->val.obj); /* copy the data from our array into the new one */ arr->copy_from(1, this, idx, cnt); /* discard our self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - copy from another byte array */ int CVmObjByteArray::getp_copy_from(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(4); unsigned long dst_idx; unsigned long src_idx; unsigned long cnt; vm_obj_id_t src_arr_id; CVmObjByteArray *src_arr; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the source array */ src_arr_id = CVmBif::pop_obj_val(vmg0_); /* make sure it is indeed an array */ if (!is_byte_array(vmg_ src_arr_id)) err_throw(VMERR_BAD_TYPE_BIF); /* we know it's the right type, so cast it */ src_arr = (CVmObjByteArray *)vm_objp(vmg_ src_arr_id); /* get the starting source index */ src_idx = CVmBif::pop_int_val(vmg0_); /* force it to be in range */ if (src_idx < 1) src_idx = 1; else if (src_idx > src_arr->get_element_count() + 1) src_idx = src_arr->get_element_count() + 1; /* get the destination index */ dst_idx = CVmBif::pop_int_val(vmg0_); /* force it to be within range */ if (dst_idx < 1) dst_idx = 1; else if (dst_idx > get_element_count() + 1) dst_idx = get_element_count() + 1; /* get the count */ cnt = CVmBif::pop_int_val(vmg0_); /* limit the copying to the available destination space */ if (dst_idx > get_element_count()) cnt = 0; else if (dst_idx + cnt - 1 > get_element_count()) cnt = get_element_count() + 1 - dst_idx; /* save undo for the change */ save_undo(vmg_ self, dst_idx, cnt); /* copy the data from the source array into our array */ copy_from(dst_idx, src_arr, src_idx, cnt); /* the result is 'self' */ retval->set_obj(self); /* handled */ return TRUE; } /* * property evaluator - fill with a value */ int CVmObjByteArray::getp_fill_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(1, 2); unsigned long idx; unsigned long cnt; long fill_val; /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* get the value with which to fill elements */ fill_val = CVmBif::pop_int_val(vmg0_); if (fill_val < 0 || fill_val > 255) err_throw(VMERR_OUT_OF_RANGE); /* get the starting index, if provided */ if (argc >= 2) { /* get the index */ idx = CVmBif::pop_int_val(vmg0_); /* force it to be in range */ if (idx < 1) idx = 1; else if (idx > get_element_count() + 1) idx = get_element_count() + 1; } else { /* fill from the first element */ idx = 1; } /* get the count, if provided */ if (argc >= 3) { /* get the count */ cnt = CVmBif::pop_int_val(vmg0_); } else { /* fill the entire array */ cnt = get_element_count(); } /* force the length to be in range */ if (idx > get_element_count()) cnt = 0; else if (idx + cnt - 1 > get_element_count()) cnt = get_element_count() + 1 - idx; /* save undo for the change */ save_undo(vmg_ self, idx, cnt); /* fill with the given value */ fill_with((unsigned char)fill_val, idx, cnt); /* the result is 'self' */ retval->set_obj(self); /* handled */ return TRUE; } /* * property evaluator - convert to string */ int CVmObjByteArray::getp_to_string(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(0, 3); unsigned long idx; unsigned long cnt; vm_val_t charset; CCharmapToUni *mapper = 0; size_t str_len; CVmObjString *str; /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* get the character set object */ charset.set_nil(); if (argc >= 1) charset.set_obj_or_nil(CVmBif::pop_charset_obj(vmg0_)); /* if we got a CharacterSet, get its to-unicode mapper */ if (charset.typ != VM_NIL) mapper = ((CVmObjCharSet *)vm_objp(vmg_ charset.val.obj)) ->get_to_uni(vmg0_); /* if there's a starting index, retrieve it */ if (argc >= 2) { /* retrieve the starting index */ idx = CVmBif::pop_int_val(vmg0_); /* force it to be in range */ if (idx < 1) idx = 1; else if (idx > get_element_count() + 1) idx = get_element_count() + 1; } else { /* start at the first byte */ idx = 1; } /* if there's a length, retrieve it */ if (argc >= 3) { /* retrieve the length */ cnt = CVmBif::pop_int_val(vmg0_); } else { /* use all remaining characters */ cnt = get_element_count(); } /* force the length to be in range */ if (idx > get_element_count()) cnt = 0; else if (idx + cnt - 1 > get_element_count()) cnt = get_element_count() + 1 - idx; /* push a self-ref for gc protection, and a ref to the character set */ G_stk->push()->set_obj(self); G_stk->push(&charset); /* map via the character mapper, or as raw unicode character values */ if (mapper != 0) { /* * We have a character set mapper. First, measure the required * buffer length by mapping to a null buffer. */ str_len = map_to_string(idx, cnt, 0, 0, mapper); /* allocate a string of the required length */ retval->set_obj(CVmObjString::create(vmg_ FALSE, str_len)); str = (CVmObjString *)vm_objp(vmg_ retval->val.obj); /* map the string, actually storing the bytes this time */ map_to_string(idx, cnt, str, str_len, mapper); } else { /* there's no mapper - treat our bytes as unicode character codes */ CVmByteArraySource src(vmg_ self, idx - 1, cnt); retval->set_obj(CVmObjString::create_latin1(vmg_ FALSE, &src)); } /* discard the gc protection */ G_stk->discard(2); /* handled */ return TRUE; } /* * Run a range of bytes through a character mapper to produce string data, * optionally storing the string data in a string object. Returns the * number of bytes in the resulting string. */ size_t CVmObjByteArray::map_to_string(unsigned long idx, unsigned long len, CVmObjString *str, size_t str_len, CCharmapToUni *mapper) { size_t str_total; size_t buf_len; unsigned char buf[128]; char *dst; /* get the string's buffer pointer */ dst = (str != 0 ? str->cons_get_buf() : 0); /* go through the source bytes a bit at a time */ for (str_total = 0, buf_len = 0 ; len != 0 ; ) { size_t copy_len; size_t str_cur; size_t partial_len; /* * fill up the buffer, up to the remaining source length or the * buffer's capacity, whichever is lower */ copy_len = sizeof(buf) - buf_len; if (copy_len > len) copy_len = (size_t)len; /* copy the bytes to our staging buffer */ copy_to_buf(buf, idx, copy_len); /* add the copied bytes into the buffer length */ buf_len += copy_len; /* advance past the copied bytes in the source */ idx += copy_len; len -= copy_len; /* translate the bytes through the character set mapping */ str_cur = mapper->map2(&dst, &str_len, (char *)buf, buf_len, &partial_len); /* * if this would push us over the maximum string size, we can't * convert the data */ if (str_cur > OSMALMAX - str_total - VMB_LEN) err_throw(VMERR_OUT_OF_MEMORY); /* add the current length into the total string length */ str_total += str_cur; /* copy the partial last character bytes to the start of the buffer */ if (partial_len != 0) memmove(buf, buf + buf_len - partial_len, partial_len); /* the buffer now contains only the partial character bytes */ buf_len = partial_len; } /* return the total string length */ return str_total; } /* * Copy bytes from the array to a buffer */ size_t CVmObjByteArray::copy_to_buf(unsigned char *buf, unsigned long idx, size_t len) const { /* we haven't copied any bytes so far */ size_t actual = 0; /* limit it to the actual available space */ unsigned long contlen = get_element_count(); if (idx > contlen) len = 0; else if (idx + len > contlen) len = contlen - idx + 1; /* keep going until we satisfy the request */ while (len != 0) { size_t avail; unsigned char *p; size_t copy_len; /* get the next chunk */ p = get_ele_ptr(idx, &avail); /* if there's nothing left, stop */ if (avail == 0) break; /* copy the available bytes or the reamining desired bytes */ copy_len = avail; if (copy_len > len) copy_len = len; /* copy the bytes */ memcpy(buf, p, copy_len); /* advance past the copied bytes */ buf += copy_len; idx += copy_len; len -= copy_len; actual += copy_len; } /* return the actual size copied */ return actual; } /* * Copy bytes from a buffer into the array, saving undo */ size_t CVmObjByteArray::copy_from_buf_undo(VMG_ vm_obj_id_t self, const unsigned char *buf, unsigned long idx, size_t len) { /* limit the size to the remaining length from 'idx' */ unsigned long avail_len = get_element_count() - idx + 1; if (len > avail_len) len = (size_t)avail_len; /* save undo */ save_undo(vmg_ self, idx, len); /* copy the bytes */ copy_from_buf(buf, idx, len); /* return the copy length */ return len; } /* * Copy bytes from a buffer into the array */ void CVmObjByteArray::copy_from_buf(const unsigned char *buf, unsigned long idx, size_t len) { /* keep going until we satisfy the request */ while (len != 0) { size_t avail; unsigned char *p; size_t copy_len; /* get the next chunk */ p = get_ele_ptr(idx, &avail); /* copy the available bytes or the remaining desired bytes */ copy_len = avail; if (copy_len > len) copy_len = len; /* copy the bytes */ memcpy(p, buf, copy_len); /* advance past the copied bytes */ buf += copy_len; idx += copy_len; len -= copy_len; } } /* ------------------------------------------------------------------------ */ /* * Swap byte order */ static void swap_bytes(unsigned char *buf, int len) { int i, j; for (i = 0, j = len - 1 ; i < j ; ++i, --j) { unsigned char tmp = buf[i]; buf[i] = buf[j]; buf[j] = tmp; } } /* ------------------------------------------------------------------------ */ /* * property evaluator - read an integer */ int CVmObjByteArray::getp_read_int(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(2); unsigned int idx; unsigned int fmt; long result = 0; size_t siz; unsigned char cbuf[4]; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the starting index and format code */ idx = CVmBif::pop_int_val(vmg0_); fmt = CVmBif::pop_int_val(vmg0_); /* get the size from the format */ switch (fmt & FmtSizeMask) { case FmtInt8: default: siz = 1; break; case FmtInt16: siz = 2; break; case FmtInt32: siz = 4; break; } /* check that the index is in range */ if (idx < 1 || idx + siz - 1 > get_element_count()) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* make a copy of the bytes in cbuf[] for easy access */ copy_to_buf(cbuf, idx, siz); /* read the value */ switch (siz) { case 1: /* 8-bit integer - all that matters is the signedness */ if ((fmt & FmtSignedMask) == FmtSigned) result = (long)(int)(char)cbuf[0]; else result = cbuf[0]; break; case 2: /* * 16-bit integer. Our standard portable order is little-endian, * so swap bytes if it's big-endian. */ if ((fmt & FmtOrderMask) == FmtBigEndian) swap_bytes(cbuf, 2); /* it's little-endian now, so pull out the signed or unsigned value */ if ((fmt & FmtSignedMask) == FmtSigned) result = osrp2s(cbuf); else result = osrp2(cbuf); break; case 4: /* * 32-bit integer. Convert to our standard little-endian ordering * if it's currently big-endian, by swapping the byte order. */ if ((fmt & FmtOrderMask) == FmtBigEndian) swap_bytes(cbuf, 4); /* it's little-endian now, so pull out the signed or unsigned value */ if ((fmt & FmtSignedMask) == FmtSigned) result = osrp4s(cbuf); else result = osrp4(cbuf); break; } /* return the result */ retval->set_int(result); /* handled */ return TRUE; } /* * property evaluator - write an integer */ int CVmObjByteArray::getp_write_int(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(3); unsigned int idx; unsigned int fmt; long val; size_t siz; unsigned char cbuf[4]; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the starting index, format code, and value to write */ idx = CVmBif::pop_int_val(vmg0_); fmt = CVmBif::pop_int_val(vmg0_); val = CVmBif::pop_long_val(vmg0_); /* get the size from the format */ switch (fmt & FmtSizeMask) { case FmtInt8: default: siz = 1; break; case FmtInt16: siz = 2; break; case FmtInt32: siz = 4; break; } /* check that the index is in range */ if (idx < 1 || idx + siz - 1 > get_element_count()) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* write the value to cbuf[] */ switch (siz) { case 1: /* 8-bit integer */ cbuf[0] = (char)(val & 0xFF); break; case 2: /* 16-bit integer - start with our standard little-endian format */ if ((fmt & FmtSignedMask) == FmtSigned) oswp2s(cbuf, (int)val); else oswp2(cbuf, (int)val); break; case 4: /* 32-bit integer - start with our standard little-endian format */ if ((fmt & FmtSignedMask) == FmtSigned) oswp4s(cbuf, val); else oswp4(cbuf, val); break; } /* swap bytes if they want big-endian */ if (siz > 1 && (fmt & FmtOrderMask) == FmtBigEndian) swap_bytes(cbuf, siz); /* save undo for the change */ save_undo(vmg_ self, idx, siz); /* store the byte representation we've constructed */ copy_from_buf(cbuf, idx, siz); /* there's no return value */ retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Write bytes from the specified region of the array to a file. Returns * zero on success, non-zero on failure. */ int CVmObjByteArray::write_to_file(CVmDataSource *fp, unsigned long start_idx, unsigned long len) const { unsigned long rem; unsigned long idx; /* make sure the starting index is valid */ if (start_idx < 1) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* * if the starting index is past the end of the array, there's nothing * to do - just return success */ if (start_idx > get_element_count()) return 0; /* * limit the request to the number of bytes available after the * starting index */ if (start_idx + len - 1 > get_element_count()) len = get_element_count() - start_idx + 1; /* keep going until we satisfy the request or run into a problem */ for (idx = start_idx, rem = len ; rem != 0 ; ) { unsigned char *p; size_t avail; size_t chunk; /* get the next chunk */ p = get_ele_ptr(idx, &avail); /* limit writing to the amount remaining of the requested size */ chunk = avail; if (chunk > rem) chunk = (size_t)rem; /* * write out this chunk - if an error occurs, abort with a failure * indication */ if (fp->write(p, chunk)) return 1; /* move our counters past this chunk */ idx += chunk; rem -= chunk; } /* we satisfied the request without problems - indicate success */ return 0; } /* * Read bytes from the file into the specified region of the array. * Returns the number of bytes actually read. */ unsigned long CVmObjByteArray::read_from_file( VMG_ vm_obj_id_t self, CVmDataSource *fp, unsigned long start_idx, unsigned long len, int undo) { unsigned long rem; unsigned long idx; unsigned long total; /* make sure the starting index is at least 1 */ if (start_idx < 1) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* * if the starting index is past the end of the array, there's nothing * to do - just return success */ if (start_idx > get_element_count()) return 0; /* if the requested length is zero, consider it a success */ if (len == 0) return 0; /* * limit the request to the number of bytes available after the * starting index */ if (start_idx + len - 1 > get_element_count()) len = get_element_count() - start_idx + 1; /* save undo */ if (undo) save_undo(vmg_ self, start_idx, len); /* keep going until we satisfy the request or run into a problem */ for (idx = start_idx, rem = len, total = 0 ; rem != 0 ; ) { unsigned char *p; size_t avail; size_t chunk; size_t cur_read; /* get the next chunk */ p = get_ele_ptr(idx, &avail); /* limit reading to the amount of the request remaining */ chunk = avail; if (chunk > rem) chunk = (size_t)rem; /* read as much as we can of this chunk */ cur_read = fp->readc(p, chunk); /* add this amount into the total so far */ total += cur_read; /* * if we didn't get as much as we asked for, we must have reached * the end of the file before satisfying the request - there's * nothing more to be read in this case, so we can stop looping */ if (cur_read != chunk) break; /* move our counters past this chunk */ idx += chunk; rem -= chunk; } /* return the total amount we read */ return total; } /* ------------------------------------------------------------------------ */ /* * Write to a 'data' mode file */ int CVmObjByteArray::write_to_data_file(CVmDataSource *fp) { char buf[16]; /* write the number of bytes in our array */ oswp4(buf, get_element_count()); if (fp->write(buf, 4)) return 1; /* write the bytes */ return write_to_file(fp, 1, get_element_count()); } /* * Read from a 'data' mode file */ int CVmObjByteArray::read_from_data_file(VMG_ vm_val_t *retval, CVmDataSource *fp) { char buf[16]; CVmObjByteArray *arr; unsigned long len; /* read the number of bytes in the array */ if (fp->read(buf, 4)) return 1; len = t3rp4u(buf); /* create a new ByteArray to hold the result */ retval->set_obj(create(vmg_ FALSE, len)); arr = (CVmObjByteArray *)vm_objp(vmg_ retval->val.obj); /* read the bytes (it's a new object, so don't save undo) */ if (arr->read_from_file(vmg_ retval->val.obj, fp, 1, len, FALSE) != len) { /* * we didn't manage to read all of the bytes - since the value was * tagged with the correct number of bytes, end-of-file in the * middle of the bytes indicates a corrupted file, so return * failure */ return 1; } /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * property evaluator - packBytes */ int CVmObjByteArray::getp_packBytes( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = oargc != 0 ? *oargc : 0; static CVmNativeCodeDesc desc(2, 0, TRUE); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* retrieve the starting index from the first argument */ long idx = CVmBif::pop_long_val(vmg0_); --argc; /* limit the index to 1..length */ if (idx < 1) idx = 1; else if ((ulong)idx > get_element_count()) idx = get_element_count(); /* adjust to a zero-based index */ --idx; /* set up a data source for the byte array */ CVmByteArraySource dst(vmg_ self); /* seek to the starting index */ dst.seek(idx, OSFSK_SET); /* do the packing */ CVmPack::pack(vmg_ 0, argc, &dst); /* discard the arguments */ G_stk->discard(argc); /* return the number of bytes written */ retval->set_int(dst.get_pos() - idx); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - static ByteArray.packBytes(). This version of the * method creates a new ByteArray object containing the packed bytes. */ int CVmObjByteArray::static_packBytes(VMG_ vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = oargc != 0 ? *oargc : 0; static CVmNativeCodeDesc desc(1, 0, TRUE); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* set up an in-memory data stream to receive the packed data */ CVmMemorySource *dst = new CVmMemorySource(0L); err_try { /* do the packing */ CVmPack::pack(vmg_ 0, argc, dst); /* create a byte array to hold the packed data */ long len = dst->get_size(); retval->set_obj(create(vmg_ FALSE, len)); CVmObjByteArray *arr = (CVmObjByteArray *)vm_objp(vmg_ retval->val.obj); /* * copy the bytes from the stream to the byte array; it's a new * object, so there's no undo to save */ dst->seek(0, OSFSK_SET); arr->read_from_file(vmg_ retval->val.obj, dst, 1, len, FALSE); } err_finally { /* done with the data stream */ delete dst; } err_end; /* discard the arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - unpackBytes */ int CVmObjByteArray::getp_unpackBytes( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = oargc != 0 ? *oargc : 0; static CVmNativeCodeDesc desc(2); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* retrieve the starting index from the first argument */ long idx = CVmBif::pop_long_val(vmg0_); --argc; /* limit the index to 1..length */ if (idx < 1) idx = 1; else if ((ulong)idx > get_element_count()) idx = get_element_count(); /* get the format string, but leave it on the stack for gc protection */ const char *fmt = G_stk->get(0)->get_as_string(vmg0_); /* get the format string length and buffer pointer */ size_t fmtlen = vmb_get_len(fmt); fmt += VMB_LEN; /* set up a data source for the byte array */ CVmByteArraySource src(vmg_ self); /* seek to the starting index */ src.seek(idx - 1, OSFSK_SET); /* do the unpacking */ CVmPack::unpack(vmg_ retval, fmt, fmtlen, &src); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - sha256 */ int CVmObjByteArray::getp_sha256( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = oargc != 0 ? *oargc : 0; static CVmNativeCodeDesc desc(0, 2); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the starting index and length */ long cnt = get_element_count(); long idx = argc >= 1 ? CVmBif::pop_int_val(vmg0_) : 1; long len = argc >= 2 ? CVmBif::pop_int_val(vmg0_) : cnt; /* set up a data source on the array, and seek to the starting index */ CVmByteArraySource src(vmg_ self); src.seek(idx - 1, OSFSK_SET); /* calculate the hash */ char buf[65]; sha256_datasrc(buf, &src, len); /* return the string */ retval->set_obj(CVmObjString::create(vmg_ FALSE, buf, 64)); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - digestMD5 */ int CVmObjByteArray::getp_digestMD5( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = oargc != 0 ? *oargc : 0; static CVmNativeCodeDesc desc(0, 2); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the starting index and length */ long cnt = get_element_count(); long idx = argc >= 1 ? CVmBif::pop_int_val(vmg0_) : 1; long len = argc >= 2 ? CVmBif::pop_int_val(vmg0_) : cnt; /* limit the starting index to the available range */ if (idx < 1) idx = 1; else if (idx > cnt + 1) idx = cnt + 1; /* limit the length to the range remaining */ if (len < 0) len = 0; else if (len > cnt - idx + 1) len = cnt - idx + 1; /* set up the hash accumulator */ md5_state_t ctx; md5_init(&ctx); /* feed the selected range of bytes into the hash */ while (len > 0) { /* get the next chunk */ size_t avail; unsigned char *p = get_ele_ptr(idx, &avail); /* use the whole chunk, up to the amount remaining in the request */ size_t cur = (len < (long)avail ? (size_t)len : avail); /* feed this chunk into the hash */ md5_append(&ctx, p, cur); /* deduct this chunk from the remaining length */ len -= cur; } /* calculate the hash result */ const int HASH_BYTES = 16; unsigned char hash[HASH_BYTES]; md5_finish(&ctx, hash); /* convert the binary hash to printable hex digits */ char buf[HASH_BYTES*2], *bufp = buf; for (int i = 0 ; i < HASH_BYTES ; ++i, bufp += 2) byte_to_xdigits(bufp, hash[i]); /* return the string */ retval->set_obj(CVmObjString::create(vmg_ FALSE, buf, HASH_BYTES*2)); /* handled */ return TRUE; } qtads-2.1.7/tads3/vmbytarr.h000066400000000000000000000416111265017072300157000ustar00rootroot00000000000000/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbytarr.h - T3 ByteArray metaclass Function Notes Modified 06/05/01 MJRoberts - Creation */ #ifndef VMBYTARR_H #define VMBYTARR_H #include #include "os.h" #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" /* ------------------------------------------------------------------------ */ /* * A ByteArray is simply an array of byte values. This class provides a * simple, fast mechanism to store blocks of binary data, so it is not a * subclass of Array and is not a Collection. * * The image file data for a byte array is simple: * * UINT4 number of bytes *. BYTE bytes[1..number_of_bytes] * * Internally, we store the array data in chunks of 32k each. Our * extension is a first-level page table, pointing to the chunks: * * UINT4 number of elements *. unsigned char **page0 *. unsigned char **page1 *. ... * * Each pageN pointer points to a second-level page table, which consists * of (up to) 8192 pointers to the actual pages. Since a page is 32k, and * we can store 8k pointers per second-level table, each second-level * table is capable of referencing 256MB. By design, we can store up to * 4GB, so we need at most 16 second-level tables. * * The extension is allocated according to the actual number of * second-level tables we require for the element count. Each * second-level page is allocated to 8192*sizeof(char *), except the last * second-level page, which is allocated to N*sizeof(char *) where N is * the number of elements required in the last second-level table. Each * page is allocated to 32K bytes, except the last, which is allocated to * the actual size needed. * * To access an element at index i, we calculate s1 (the page table * selector) as i/(32k*8k) == i/256M; s2 (the page selector within the * selected page table) as (i%256M)/32k; and s3 (the byte selector within * the page) as i%32k. The byte is then accessed as * * page[s1][s2][s3] */ class CVmObjByteArray: public CVmObject { friend class CVmMetaclassByteArray; friend class bytearray_undo_rec; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop); /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self) { /* * we reference no other objects and cannot ourselves be converted * to constant data, so there's nothing to do here */ } /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self) { /* * we reference no data and cannot be converted to constant data, * so there's nothing to do */ } /* cast to string - assumes the bytes as Latin-1 characters */ virtual const char *cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const; /* create an array with no initial contents */ static vm_obj_id_t create(VMG_ int in_root_set); /* create an array with a given number of elements */ static vm_obj_id_t create(VMG_ int in_root_set, unsigned long element_count); /* create an array by mapping a string through a character set */ static vm_obj_id_t create_from_string( VMG_ const vm_val_t *strval, const char *str, const vm_val_t *mapval); /* create from binary data */ static vm_obj_id_t create_from_bytes( VMG_ int in_root_set, const char *buf, size_t len); /* * determine if an object is a byte array - it is if the object's * virtual metaclass registration index matches the class's static * index */ static int is_byte_array(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* undo operations */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *rec); void discard_undo(VMG_ struct CVmUndoRecord *); /* our data are just bytes - we reference nothing */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } void mark_refs(VMG_ uint /*state*/) { } void remove_stale_weak_refs(VMG0_) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* reload from the image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixup); /* index the array */ int index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val); /* set an indexed element of the array */ int set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val); /* * Check a value for equality. We will match another byte array with * the same number of elements and the same value for each element. */ int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const; /* calculate a hash value for the array */ uint calc_hash(VMG_ vm_obj_id_t self, int depth) const; /* * assume that we've been changed since loading, if we came from the * image file */ int is_changed_since_load() const { return TRUE; } /* get the number of elements in the array */ unsigned long get_element_count() const { return t3rp4u(get_ext_ptr()); } /* get the byte at a given 1-based index */ unsigned char get_element(unsigned long idx) const { size_t avail; return *get_ele_ptr(idx, &avail); } /* * construction: copy (without undo) bytes from a buffer into the byte * array */ void cons_copy_from_buf(const unsigned char *buf, unsigned long idx, size_t cnt) { /* copy the bytes into our array */ copy_from_buf(buf, idx, cnt); } /* * Write the specified region of the array to a file. Returns zero on * success, non-zero on failure. */ int write_to_file(class CVmDataSource *fp, unsigned long start_idx, unsigned long len) const; /* * Read bytes from a file into a region of the array, replacing * existing bytes in the array; saves undo for the change. Returns * the number of bytes actually read from the file, which will be less * than the number of bytes requested if we reach the end of the file * before satisfying the request. */ unsigned long read_from_file(VMG_ vm_obj_id_t self, class CVmDataSource *fp, unsigned long start_idx, unsigned long len, int save_undo); /* * write to a 'data' mode file - returns zero on success, non-zero on * failure */ int write_to_data_file(class CVmDataSource *fp); /* * read from a 'data' mode file, creating a new ByteArray object to * hold the bytes from the file */ static int read_from_data_file(VMG_ vm_val_t *retval, class CVmDataSource *fp); /* * Copy bytes into a buffer, starting at the given 1-based index. * Returns the number of bytes actually copied (this might be less than * the requested size, because the ByteArray might not have enough * contents to fill the request). */ size_t copy_to_buf(unsigned char *buf, unsigned long idx, size_t cnt) const; /* * Copy bytes from a buffer into the array at the given 1-based index, * saving undo. Returns the actual number of bytes copied, which might * be less than the requested size, since the ByteArray might not be * big enough to accommodate the write. */ size_t copy_from_buf_undo(VMG_ vm_obj_id_t self, const unsigned char *buf, unsigned long idx, size_t cnt); protected: /* load image data */ virtual void load_image_data(VMG_ const char *ptr, size_t siz); /* create a list with no initial contents */ CVmObjByteArray() { ext_ = 0; } /* * create a list with a given number of elements, for construction * of the list element-by-element */ CVmObjByteArray(VMG_ unsigned long byte_count); /* get a pointer to my extension */ const char *get_ext_ptr() const { return ext_; } /* * get my extension data pointer for construction purposes - this is * a writable pointer, so that a caller can fill our data buffer * during construction */ char *cons_get_ext_ptr() const { return ext_; } /* allocate space for the array, given the number of elements */ void alloc_array(VMG_ unsigned long element_count); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - length */ int getp_length(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - subarray */ int getp_subarray(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - copy from another byte array */ int getp_copy_from(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - fill with a value */ int getp_fill_val(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - convert to string */ int getp_to_string(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - read integer */ int getp_read_int(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - write integer */ int getp_write_int(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - packBytes */ int getp_packBytes(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - unpackBytes */ int getp_unpackBytes(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - static ByteArray.unpackBytes */ static int static_packBytes(VMG_ vm_val_t *retval, uint *argc); /* property evaluator - sha256 */ int getp_sha256(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - digestMD5 */ int getp_digestMD5(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* * Given a 1-based index, get a pointer to the byte at the index, and * the number of contiguous bytes available starting with that byte. * The available byte count doesn't take into account a short last * page, but simply returns the maximum number of bytes that would be * available on the page if it were allocated to full size; the caller * is responsible for ensuring that there is no reading or writing * past the end of the array. */ unsigned char *get_ele_ptr(unsigned long idx, size_t *bytes_avail) const { size_t s1; size_t s2; size_t s3; /* convert to a zero-based index */ --idx; /* * calculate the page table index - since each page holds 32k * bytes and each page table points to 8k pages, divide by 32k*8k * == 2^15*2^13 == 2^28 */ s1 = idx >> 28; /* * calculate the page index within the page table - each page * holds 32k, so calculate the excess from the page table selector * (i.e, idx % 32k*8k) and then divide by 32k == 2^15 */ s2 = (idx & 0x0FFFFFFF) >> 15; /* * calculate the page offset - this is simply the excess from the * page index */ s3 = idx & 0x7FFF; /* * Each page holds 32k, so the number of contiguous bytes starting * at this byte is 32k less the index. */ *bytes_avail = (32*1024) - s3; /* * dereference the extension to get the page table, deference the * page table to get the page, and index the page by the offset */ return get_page_table_ptr(s1)[s2] + s3; } /* * Given a page table selector, return a pointer to the selected page * table. */ unsigned char **get_page_table_ptr(size_t s) const { return get_page_table_array()[s]; } /* * Get a pointer to the page table array */ unsigned char ***get_page_table_array() const { /* the page table array starts after the element count */ return (unsigned char ***)(ext_ + 4); } /* fill the given (1-based index) range with the given byte value */ void fill_with(unsigned char val, unsigned long start_idx, unsigned long cnt); /* copy bytes from another byte array into this one */ void copy_from(unsigned long dst_idx, CVmObjByteArray *src_arr, unsigned long src_start_idx, unsigned long cnt); /* move bytes within this array */ void move_bytes(unsigned long dst_idx, unsigned long src_idx, unsigned long cnt); /* copy bytes from a buffer into the array */ void copy_from_buf(const unsigned char *buf, unsigned long idx, size_t cnt); /* map to a string */ size_t map_to_string(unsigned long idx, unsigned long len, class CVmObjString *str, size_t str_len, class CCharmapToUni *mapper); /* save undo for a change to a range of the array */ void save_undo(VMG_ vm_obj_id_t self, unsigned long start_idx, unsigned long cnt); /* set the number of bytes in the array */ void set_element_count(unsigned long cnt) { oswp4(cons_get_ext_ptr(), cnt); } /* property evaluation function table */ static int (CVmObjByteArray::*func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassByteArray: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "bytearray/030002"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjByteArray(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjByteArray(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjByteArray::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjByteArray:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMBYTARR_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjByteArray) qtads-2.1.7/tads3/vmcfgfl.cpp000066400000000000000000000007641265017072300160150ustar00rootroot00000000000000/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmcfgfl.cpp - "flat" constant pool configuration Function Notes Modified 09/19/02 MJRoberts - Creation */ #include "vminit.h" #include "vmglob.h" /* * initialize */ void vm_initialize(struct vm_globals **vmg, const vm_init_options *opts) { vm_init_flat(vmg, opts); } qtads-2.1.7/tads3/vmcoll.cpp000066400000000000000000000074501265017072300156640ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmcoll.cpp - collection metaclass Function Notes Modified 04/22/00 MJRoberts - Creation */ #include #include "vmtype.h" #include "vmobj.h" #include "vmcoll.h" #include "vmglob.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmmeta.h" #include "vmstack.h" #include "vmrun.h" /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassCollection metaclass_reg_obj; CVmMetaclass *CVmObjCollection::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjCollection:: *CVmObjCollection::func_table_[])(VMG_ vm_val_t *retval, const vm_val_t *self_val, uint *argc) = { &CVmObjCollection::getp_undef, &CVmObjCollection::getp_create_iter, &CVmObjCollection::getp_create_live_iter }; /* ------------------------------------------------------------------------ */ /* * Get a property */ int CVmObjCollection::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { vm_val_t self_val; /* set up the 'self' value */ self_val.set_obj(self); /* use the constant collection version */ if (const_get_coll_prop(vmg_ prop, retval, &self_val, source_obj, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * Get a property of a constant value */ int CVmObjCollection::const_get_coll_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, const vm_val_t *self_val, vm_obj_id_t *src_obj, uint *argc) { uint func_idx; /* presume no source object */ *src_obj = VM_INVALID_OBJ; /* translate the property index to an index into our function table */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ retval, self_val, argc)) return TRUE; /* not found */ return FALSE; } /* * Create an iterator */ int CVmObjCollection::getp_create_iter(VMG_ vm_val_t *retval, const vm_val_t *self_val, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* push a self-reference for gc protection */ G_stk->push(self_val); /* create the iterator */ new_iterator(vmg_ retval, self_val); /* discard the gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* * Create a live iterator */ int CVmObjCollection::getp_create_live_iter(VMG_ vm_val_t *retval, const vm_val_t *self_val, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* push a self-reference for gc protection */ G_stk->push(self_val); /* create the "live" iterator */ new_live_iterator(vmg_ retval, self_val); /* discard the gc protection */ G_stk->discard(); /* handled */ return TRUE; } qtads-2.1.7/tads3/vmcoll.h000066400000000000000000000111201265017072300153160ustar00rootroot00000000000000/* $Header$ */ /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmcoll.h - T3 Collection base class Function A Collection is the base class for List, Array, and other objects providing a collection of objects that can be iterated via an Iterator. Collection is an abstract base class: it cannot be instantiated, and thus has no image-file or state-file representation. Notes Modified 04/22/00 MJRoberts - Creation */ #ifndef VMCOLL_H #define VMCOLL_H #include #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" #include "vmerr.h" #include "vmerrnum.h" class CVmObjCollection: public CVmObject { friend class CVmMetaclassCollection; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * constant value property evaluator - this allows us to evaluate a * property for an object value or for a constant value using the * same code */ int const_get_coll_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, const vm_val_t *self_val, vm_obj_id_t *src_obj, uint *argc); protected: /* * Create an iterator for this collection. Fills in *retval with a * reference to the new iterator object. This iterator must refer * to an immutable snapshot of the collection. */ virtual void new_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val) = 0; /* * Create a "live" iterator for this collection. Fills in *retval * with a reference to the new iterator object. This iterator must * refer to the original "live" collection. */ virtual void new_live_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val) = 0; /* property evaluator - undefined property */ int getp_undef(VMG_ vm_val_t *, const vm_val_t *, uint *) { return FALSE; } /* property evaluator - create iterator */ int getp_create_iter(VMG_ vm_val_t *retval, const vm_val_t *self_val, uint *argc); /* property evaluator - create live iterator */ int getp_create_live_iter(VMG_ vm_val_t *retval, const vm_val_t *self_val, uint *argc); /* property evaluation function table */ static int (CVmObjCollection::*func_table_[])(VMG_ vm_val_t *retval, const vm_val_t *self_val, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassCollection: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "collection/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { err_throw(VMERR_BAD_STATIC_NEW); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { err_throw(VMERR_BAD_STATIC_NEW); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { err_throw(VMERR_BAD_DYNAMIC_NEW); AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjCollection:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMCOLL_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjCollection) qtads-2.1.7/tads3/vmconhmp.cpp000066400000000000000000001765541265017072300162330ustar00rootroot00000000000000/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmconhmp.cpp - T3 VM Console - HTML mini-parser Function This is the console HTML "mini-parser," which provides some minimal interpretation of HTML markups for text-only underlying output streams. Note that the mini-parser is included even in full HTML-enabled versions of TADS (such as HTML TADS or HyperTADS), because we still need text-only interpretation of HTML markups for log-file targets. Notes Modified 06/08/02 MJRoberts - Creation */ #include #include #include #include #include #include #include "wchar.h" #include "os.h" #include "t3std.h" #include "utf8.h" #include "vmuni.h" #include "vmconsol.h" #include "vmglob.h" #include "charmap.h" #include "vmhash.h" /* ------------------------------------------------------------------------ */ /* * Horizontal Tab object. We use these objects to record tab stops * defined with in our HTML mini-parser. * * This is a hash table entry subclass because we use a hash table of tab * entries, keyed by ID strings. Note that we use a case-sensitive hash * table entry - clients must convert ID's to lower-case if they don't * want case sensitivity. * * Note that this code looks a bit cavalier with unicode vs ASCII, but * this is actually safe. The underlying hash table code uses * counted-length strings, so it doesn't care about embedded null bytes. * We therefore can pass a wchar_t buffer to the underlying hash table, * even though the underlying hash table uses char's, because we multiply * our length by the size of a wchar_t to get the byte length. */ class CVmFmtTabStop: public CVmHashEntryCS { public: CVmFmtTabStop(const char *id, size_t idlen) : CVmHashEntryCS(id, idlen, TRUE) { /* we don't know our column yet */ column_ = 0; } /* the column where the tab is defined */ int column_; /* * Find a tabstop object with a given ID, optionally creating a new * object if an existing one isn't found. */ static CVmFmtTabStop *find(CVmHashTable *hashtab, wchar_t *id, int create) { /* * Get the length - in characters AND in bytes - of the new ID. * We mostly work with the new ID in bytes rather than wchar_t's, * because the underlying hash table does everything in bytes. */ /* * Convert the new ID to folded case. The hash table doesn't deal * with unicode case conversions, so we use a case-sensitive hash * table and simply make sure we only give it lower-case * characters. */ size_t flen = fold_case(0, id); wchar_t *fid = 0; if (flen != 0) fid = new wchar_t[flen+1]; CVmFmtTabStop *entry = 0; err_try { /* do the folding if applicable */ if (fid != 0) { fold_case(fid, id); id = fid; } /* get the folded ID length in characters and bytes */ size_t id_chars = wcslen(id); size_t id_bytes = id_chars * sizeof(wchar_t); /* look for an existing tab-stop entry with the same ID */ entry = (CVmFmtTabStop *)hashtab->find((char *)id, id_bytes); /* if we didn't find it, and they want to create it, do so */ if (entry == 0 && create) hashtab->add(entry = new CVmFmtTabStop((char *)id, id_bytes)); } err_finally { /* if we allocated the ID string, release it */ if (fid != 0) delete [] fid; } err_end; /* return what we found */ return entry; } /* get the case folding of a string */ static size_t fold_case(wchar_t *dst, const wchar_t *str) { size_t dstlen = 0; for ( ; *str != 0 ; ++str) { /* * get the case folding of the current character, defaulting to * an identity mapping if there's no folding defined */ const wchar_t *f = t3_to_fold(*str); wchar_t fi[2] = { *str, 0 }; if (f == 0) f = fi; /* copy it to the output if there's an output buffer */ for ( ; *f != 0 ; ++f, ++dstlen) { if (dst != 0) *dst++ = *f; } } /* add the null */ if (dst != 0) *dst = 0; /* return the length (excluding the null) */ return dstlen; } }; /* ------------------------------------------------------------------------ */ /* * HTML entity mapping table. When we're in non-HTML mode, we keep our own * expansion table so that we can map HTML entity names into the local * character set. * * The entries in this table must be in sorted order (by HTML entity name), * because we use a binary search to find an entity name in the table. */ /* * '&' character name definition structure */ struct amp_tbl_t { /* entity name */ const char *cname; /* HTML Unicode character value */ wchar_t html_cval; }; /* * table of '&' character name sequences */ static const struct amp_tbl_t amp_tbl[] = { { "AElig", 198 }, { "Aacute", 193 }, { "Abreve", 258 }, { "Acirc", 194 }, { "Agrave", 192 }, { "Alpha", 913 }, { "Aogon", 260 }, { "Aring", 197 }, { "Atilde", 195 }, { "Auml", 196 }, { "Beta", 914 }, { "Cacute", 262 }, { "Ccaron", 268 }, { "Ccedil", 199 }, { "Chi", 935 }, { "Dagger", 8225 }, { "Dcaron", 270 }, { "Delta", 916 }, { "Dstrok", 272 }, { "ETH", 208 }, { "Eacute", 201 }, { "Ecaron", 282 }, { "Ecirc", 202 }, { "Egrave", 200 }, { "Eogon", 280 }, { "Epsilon", 917 }, { "Eta", 919 }, { "Euml", 203 }, { "Gamma", 915 }, { "Iacute", 205 }, { "Icirc", 206 }, { "Igrave", 204 }, { "Iota", 921 }, { "Iuml", 207 }, { "Kappa", 922 }, { "Lacute", 313 }, { "Lambda", 923 }, { "Lcaron", 317 }, { "Lstrok", 321 }, { "Mu", 924 }, { "Nacute", 323 }, { "Ncaron", 327 }, { "Ntilde", 209 }, { "Nu", 925 }, { "OElig", 338 }, { "Oacute", 211 }, { "Ocirc", 212 }, { "Odblac", 336 }, { "Ograve", 210 }, { "Omega", 937 }, { "Omicron", 927 }, { "Oslash", 216 }, { "Otilde", 213 }, { "Ouml", 214 }, { "Phi", 934 }, { "Pi", 928 }, { "Prime", 8243 }, { "Psi", 936 }, { "Racute", 340 }, { "Rcaron", 344 }, { "Rho", 929 }, { "Sacute", 346 }, { "Scaron", 352 }, { "Scedil", 350 }, { "Sigma", 931 }, { "THORN", 222 }, { "Tau", 932 }, { "Tcaron", 356 }, { "Tcedil", 354 }, { "Theta", 920 }, { "Uacute", 218 }, { "Ucirc", 219 }, { "Udblac", 368 }, { "Ugrave", 217 }, { "Upsilon", 933 }, { "Uring", 366 }, { "Uuml", 220 }, { "Xi", 926 }, { "Yacute", 221 }, { "Yuml", 376 }, { "Zacute", 377 }, { "Zcaron", 381 }, { "Zdot", 379 }, { "Zeta", 918 }, { "aacute", 225 }, { "abreve", 259 }, { "acirc", 226 }, { "acute", 180 }, { "aelig", 230 }, { "agrave", 224 }, { "alefsym", 8501 }, { "alpha", 945 }, { "amp", '&' }, { "and", 8743 }, { "ang", 8736 }, { "aogon", 261 }, { "aring", 229 }, { "asymp", 8776 }, { "atilde", 227 }, { "auml", 228 }, { "bdquo", 8222 }, { "beta", 946 }, { "breve", 728 }, { "brvbar", 166 }, { "bull", 8226 }, { "cacute", 263 }, { "cap", 8745 }, { "caron", 711 }, { "ccaron", 269 }, { "ccedil", 231 }, { "cedil", 184 }, { "cent", 162 }, { "chi", 967 }, { "circ", 710 }, { "clubs", 9827 }, { "cong", 8773 }, { "copy", 169 }, { "crarr", 8629 }, { "cup", 8746 }, { "curren", 164 }, { "dArr", 8659 }, { "dagger", 8224 }, { "darr", 8595 }, { "dblac", 733 }, { "dcaron", 271 }, { "deg", 176 }, { "delta", 948 }, { "diams", 9830 }, { "divide", 247 }, { "dot", 729 }, { "dstrok", 273 }, { "eacute", 233 }, { "ecaron", 283 }, { "ecirc", 234 }, { "egrave", 232 }, { "emdash", 8212 }, { "empty", 8709 }, { "emsp", 0x2003 }, { "endash", 8211 }, { "ensp", 0x2002 }, { "eogon", 281 }, { "epsilon", 949 }, { "equiv", 8801 }, { "eta", 951 }, { "eth", 240 }, { "euml", 235 }, { "exist", 8707 }, { "figsp", 0x2007 }, { "fnof", 402 }, { "forall", 8704 }, { "fpmsp", 0x2005 }, { "frac12", 189 }, { "frac14", 188 }, { "frac34", 190 }, { "frasl", 8260 }, { "gamma", 947 }, { "ge", 8805 }, { "gt", '>' }, { "hArr", 8660 }, { "hairsp", 0x200a }, { "harr", 8596 }, { "hearts", 9829 }, { "hellip", 8230 }, { "iacute", 237 }, { "icirc", 238 }, { "iexcl", 161 }, { "igrave", 236 }, { "image", 8465 }, { "infin", 8734 }, { "int", 8747 }, { "iota", 953 }, { "iquest", 191 }, { "isin", 8712 }, { "iuml", 239 }, { "kappa", 954 }, { "lArr", 8656 }, { "lacute", 314 }, { "lambda", 955 }, { "lang", 9001 }, { "laquo", 171 }, { "larr", 8592 }, { "lcaron", 318 }, { "lceil", 8968 }, { "ldq", 8220 }, { "ldquo", 8220 }, { "le", 8804 }, { "lfloor", 8970 }, { "lowast", 8727 }, { "loz", 9674 }, { "lsaquo", 8249 }, { "lsq", 8216 }, { "lsquo", 8216 }, { "lstrok", 322 }, { "lt", '<' }, { "macr", 175 }, { "mdash", 8212 }, { "micro", 181 }, { "middot", 183 }, { "minus", 8722 }, { "mu", 956 }, { "nabla", 8711 }, { "nacute", 324 }, { "nbsp", 0x00A0 }, /* == 160 decimal */ { "ncaron", 328 }, { "ndash", 8211 }, { "ne", 8800 }, { "ni", 8715 }, { "not", 172 }, { "notin", 8713 }, { "nsub", 8836 }, { "ntilde", 241 }, { "nu", 957 }, { "oacute", 243 }, { "ocirc", 244 }, { "odblac", 337 }, { "oelig", 339 }, { "ogon", 731 }, { "ograve", 242 }, { "oline", 8254 }, { "omega", 969 }, { "omicron", 959 }, { "oplus", 8853 }, { "or", 8744 }, { "ordf", 170 }, { "ordm", 186 }, { "oslash", 248 }, { "otilde", 245 }, { "otimes", 8855 }, { "ouml", 246 }, { "para", 182 }, { "part", 8706 }, { "permil", 8240 }, { "perp", 8869 }, { "phi", 966 }, { "pi", 960 }, { "piv", 982 }, { "plusmn", 177 }, { "pound", 163 }, { "prime", 8242 }, { "prod", 8719 }, { "prop", 8733 }, { "psi", 968 }, { "puncsp", 0x2008 }, { "quot", '"' }, { "rArr", 8658 }, { "racute", 341 }, { "radic", 8730 }, { "rang", 9002 }, { "raquo", 187 }, { "rarr", 8594 }, { "rcaron", 345 }, { "rceil", 8969 }, { "rdq", 8221 }, { "rdquo", 8221 }, { "real", 8476 }, { "reg", 174 }, { "rfloor", 8971 }, { "rho", 961 }, { "rsaquo", 8250 }, { "rsq", 8217 }, { "rsquo", 8217 }, { "sacute", 347 }, { "sbquo", 8218 }, { "scaron", 353 }, { "scedil", 351 }, { "sdot", 8901 }, { "sect", 167 }, { "shy", 173 }, { "sigma", 963 }, { "sigmaf", 962 }, { "sim", 8764 }, { "spades", 9824 }, { "spmsp", 0x2006 }, { "sub", 8834 }, { "sube", 8838 }, { "sum", 8721 }, { "sup", 8835 }, { "sup1", 185 }, { "sup2", 178 }, { "sup3", 179 }, { "supe", 8839 }, { "szlig", 223 }, { "tau", 964 }, { "tcaron", 357 }, { "tcedil", 355 }, { "there4", 8756 }, { "theta", 952 }, { "thetasym", 977 }, { "thinsp", 0x2009 }, { "thorn", 254 }, { "tilde", 732 }, { "times", 215 }, { "tpmsp", 0x2004 }, { "trade", 8482 }, { "uArr", 8657 }, { "uacute", 250 }, { "uarr", 8593 }, { "ucirc", 251 }, { "udblac", 369 }, { "ugrave", 249 }, { "uml", 168 }, { "upsih", 978 }, { "upsilon", 965 }, { "uring", 367 }, { "uuml", 252 }, { "weierp", 8472 }, { "xi", 958 }, { "yacute", 253 }, { "yen", 165 }, { "yuml", 255 }, { "zacute", 378 }, { "zcaron", 382 }, { "zdot", 380 }, { "zeta", 950 }, { "zwnbsp", 0xfeff }, { "zwsp", 0x200b } }; /* * Color names. For text-mode interpreters, we parse certain tag * attributes that can specify colors, so we must recognize the basic set * of HTML color names for these attributes. */ struct color_tbl_t { /* name of the color */ const wchar_t *name; /* osifc color code */ os_color_t color_code; }; static const color_tbl_t color_tbl[] = { { L"black", os_rgb_color(0x00, 0x00, 0x00) }, { L"white", os_rgb_color(0xFF, 0xFF, 0xFF) }, { L"red", os_rgb_color(0xFF, 0x00, 0x00) }, { L"blue", os_rgb_color(0x00, 0x00, 0xFF) }, { L"green", os_rgb_color(0x00, 0x80, 0x00) }, { L"yellow", os_rgb_color(0xFF, 0xFF, 0x00) }, { L"cyan", os_rgb_color(0x00, 0xFF, 0xFF) }, { L"aqua", os_rgb_color(0x00, 0xFF, 0xFF) }, { L"magenta", os_rgb_color(0xFF, 0x00, 0xFF) }, { L"silver", os_rgb_color(0xC0, 0xC0, 0xC0) }, { L"gray", os_rgb_color(0x80, 0x80, 0x80) }, { L"maroon", os_rgb_color(0x80, 0x00, 0x00) }, { L"purple", os_rgb_color(0x80, 0x00, 0x80) }, { L"fuchsia", os_rgb_color(0xFF, 0x00, 0xFF) }, { L"lime", os_rgb_color(0x00, 0xFF, 0x00) }, { L"olive", os_rgb_color(0x80, 0x80, 0x00) }, { L"navy", os_rgb_color(0x00, 0x00, 0x80) }, { L"teal", os_rgb_color(0x00, 0x80, 0x80) }, /* the parameterized colors */ { L"text", OS_COLOR_P_TEXT }, { L"bgcolor", OS_COLOR_P_TEXTBG }, { L"statustext", OS_COLOR_P_STATUSLINE }, { L"statusbg", OS_COLOR_P_STATUSBG }, { L"input", OS_COLOR_P_INPUT }, /* end-of-table marker */ { 0, 0 } }; /* ------------------------------------------------------------------------ */ /* * Parse the COLOR attribute of a FONT tag. Sets *result to the parsed * color value. Returns true if we found a valid value, false if not. */ int CVmFormatter::parse_color_attr(VMG_ const wchar_t *val, os_color_t *result) { const color_tbl_t *p; /* * if the value starts with '#', it's a hex RGB value; otherwise, it's * a color name */ if (*val == '#') { unsigned long hexval; /* parse the value as a hex number */ for (hexval = 0, ++val ; *val != '\0' && is_xdigit(*val) ; ++val) { /* add this digit */ hexval <<= 4; hexval += (t3_is_digit(*val) ? *val - '0' : *val >= 'a' && *val <= 'f' ? *val - 'a' + 0x0A : *val - 'A' + 0x0A); } /* * os_color_t uses the same encoding as HTML, so just return the * value as given (ensuring that it doesn't overflow the 24 bits * assigned to our RGB encoding) */ *result = (os_color_t)(hexval & 0x00FFFFFF); return TRUE; } else { /* scan the color table for a match for the name */ for (p = color_tbl ; p->name != 0 ; ++p) { /* if the name matches, use it */ if (CVmCaseFoldStr::wstreq(val, p->name)) { /* this is the color - make it the current buffer color */ *result = p->color_code; /* indicate that we found a valid color value */ return TRUE; } } } /* indicate that we didn't find a valid color value */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Find a tab */ CVmFmtTabStop *CVmFormatter::find_tab(wchar_t *id, int create) { /* if we don't have a hash table yet, create one */ if (tabs_ == 0) tabs_ = new CVmHashTable(64, new CVmHashFuncCS(), TRUE); /* find the tab */ return CVmFmtTabStop::find(tabs_, id, create); } /* ------------------------------------------------------------------------ */ /* * Expand the pending tab. */ void CVmFormatter::expand_pending_tab(VMG_ int allow_anon) { int txtlen; int spaces; int i; int col; /* check the alignment */ switch(pending_tab_align_) { case VMFMT_TAB_LEFT: /* * left-aligned tabs are never pending, so this should never * happen */ assert(FALSE); return; case VMFMT_TAB_NONE: /* there's no pending tab, so there's nothing to do */ return; case VMFMT_TAB_RIGHT: /* * It's a right-aligned tab. If we have a TO, expand the tab with * enough spaces to push the text up to the defined tab's column; * otherwise, insert spaces to push the text to the full line * width. */ if (pending_tab_entry_ != 0) { /* * if we're not past the tab, insert enough spaces to get us * there; if we're already past the tab, insert nothing */ if (pending_tab_entry_->column_ > linecol_) spaces = pending_tab_entry_->column_ - linecol_; else spaces = 0; } else if (allow_anon) { /* push the text to the full line width */ spaces = console_->get_line_width() - 1 - linecol_; } else { /* it's anonymous, but these aren't allowed - ignore it */ spaces = 0; } break; case VMFMT_TAB_CENTER: /* * It's a center-aligned tab. If we have a TO, expand the tab * with enough spaces to center the text on the defined tab's * column; otherwise, center the text between the tab's starting * column and the full line width. */ txtlen = linecol_ - pending_tab_start_; if (pending_tab_entry_ != 0) { int startcol; /* * find the column where the text would have to start for the * text to be centered over the tab's column */ startcol = pending_tab_entry_->column_ - (txtlen / 2); /* * if that's past the starting column, insert enough spaces to * get us there; otherwise, do nothing, as we'd have to start * to the left of the leftmost place we can put the text */ if (startcol > pending_tab_start_) spaces = startcol - pending_tab_start_; else spaces = 0; } else if (allow_anon) { /* center the text in the remaining space to the line width */ spaces = ((console_->get_line_width() - pending_tab_start_) - txtlen) / 2; } else { /* it's anonymous, but these aren't allowed - ignore it */ spaces = 0; } break; case VMFMT_TAB_DECIMAL: /* * Decimal tab. Scan the text after the tab, looking for the * first occurrence of the decimal point character. */ for (col = pending_tab_start_, i = linepos_ - (linecol_ - col) ; i < linepos_ ; ++i, ++col) { /* if this is the decimal point character, stop looking */ if (linebuf_[i] == pending_tab_dp_) break; } /* * insert enough spaces to put the decimal point character in the * defined tab's column; if the decimal point character is already * past the tab's column, do nothing */ if (pending_tab_entry_->column_ > col) spaces = pending_tab_entry_->column_ - col; else spaces = 0; break; } /* insert the spaces into the buffer where the appeared */ if (spaces > 0) { /* * Calculate the index in the buffer of the insertion point. To * do this, start with linepos_, which the current output index in * the buffer, and subtract the difference between linecol_ and * the tab starting column, since this will give us the number of * characters in the buffer since the tab insertion point. */ i = linepos_ - (linecol_ - pending_tab_start_); /* * open up the line buffer and line color buffer by the number of * spaces we're adding */ memmove(linebuf_ + i + spaces, linebuf_ + i, (linepos_ - i + 1)*sizeof(linebuf_[0])); memmove(colorbuf_ + i + spaces, colorbuf_ + i, (linepos_ - i + 1)*sizeof(colorbuf_[0])); /* advance the line and column positions for the insertion */ linecol_ += spaces; linepos_ += spaces; /* insert the spaces */ for ( ; spaces != 0 ; --spaces, ++i) { /* insert a space of the same color active at the */ linebuf_[i] = ' '; colorbuf_[i] = pending_tab_color_; } } /* we've handled the pending tab, so we can forget about it */ pending_tab_align_ = VMFMT_TAB_NONE; } /* ------------------------------------------------------------------------ */ /* * Resume text-only HTML mini-parser. This is called when we start writing * a new string and discover that we're parsing inside an HTML tag in our * mini-parser. * * Returns the next character after the run of text we parse. */ wchar_t CVmFormatter::resume_html_parsing(VMG_ wchar_t c, const char **sp, size_t *slenp) { /* keep going until we run out of characters */ while (c != L'\0') { /* * If we're parsing HTML here, and we're inside a tag, skip * characters until we reach the end of the tag. */ if (html_parse_state_ != VMCON_HPS_NORMAL) { wchar_t *dst; /* check our parsing mode */ switch(html_parse_state_) { case VMCON_HPS_TAG: /* * keep skipping up to the closing '>', but note when we * enter any quoted section */ switch(c) { case '>': /* we've reached the end of the tag */ html_parse_state_ = VMCON_HPS_NORMAL; /* if we have a deferred
, process it now */ switch(html_defer_br_) { case HTML_DEFER_BR_NONE: /* no deferred
*/ break; case HTML_DEFER_BR_FLUSH: flush(vmg_ VM_NL_NEWLINE); break; case HTML_DEFER_BR_BLANK: write_blank_line(vmg0_); break; } /* no more deferred
pending */ html_defer_br_ = HTML_DEFER_BR_NONE; /* if we're parsing a tag, finish it */ if (html_in_tab_) { /* check the attributes of the */ if (new_tab_entry_ != 0 || new_tab_align_ != VMFMT_TAB_NONE) { /* * We have either a , maybe with an * ALIGN attribute, or at least a . * * If there's no TO, then we use the default * treatment based on the alignment: for * ALIGN=RIGHT, the text up to the end of the * line is aligned at the right edge of the * display; for ALIGN=CENTER, it's centered * within the margins. For other alignments, * a TAB without a TO attribute has no effect. * * Check for ALIGN without TO, and ignore it * if it's not CENTER or RIGHT alignment. */ if (new_tab_entry_ == 0 && new_tab_align_ != VMFMT_TAB_RIGHT && new_tab_align_ != VMFMT_TAB_CENTER) { /* * meaningless alignment for without * a TO attribute - ignore the tab */ } else if (new_tab_align_ == VMFMT_TAB_LEFT || new_tab_align_ == VMFMT_TAB_NONE) { int delta; /* * This is a left-aligned tab (explicitly * or implicitly: if no alignment is * specified, default to left alignment). * This is easy: just insert spaces to the * desired column. */ delta = new_tab_entry_->column_ - linecol_; if (delta > 0) write_tab(vmg_ delta, 0); } else { /* * For other alignments, we won't know how * much space to insert until we find the * the next or the end of the line. * * Remember the starting column of the * pending tab. When we reach another * , or we reach the end of the line, * we'll insert spaces at this point as * needed. Remember the current color, so * we can use the color active at the * tag when we insert the spaces * expanding the tab. * * Also remember the characteristics * specified in the tag (alignment, * decimal-point alignment character), so * we'll be able to apply the desired * characteristics when we expand the tab. */ pending_tab_start_ = linecol_; pending_tab_color_ = cur_color_; pending_tab_entry_ = new_tab_entry_; pending_tab_align_ = new_tab_align_; pending_tab_dp_ = new_tab_dp_; /* default to '.' as the DP aligner */ if (pending_tab_align_ == VMFMT_TAB_DECIMAL && pending_tab_dp_ == L'\0') pending_tab_dp_ = L'.'; } } else { /* * it's just a with no attributes - * default to , so that it * behaves like a simple '\t' */ write_tab(vmg_ 0, 4); } } /* we're not parsing inside a tag any more */ html_allow_alt_ = FALSE; html_allow_color_ = FALSE; html_in_body_ = FALSE; html_in_tab_ = FALSE; html_in_wrap_ = FALSE; /* done */ break; case '"': /* enter a double-quoted string */ html_parse_state_ = VMCON_HPS_DQUOTE; break; case '\'': /* enter a single-quoted string */ html_parse_state_ = VMCON_HPS_SQUOTE; break; default: /* if it's alphabetic, start an attribute name */ if (t3_is_alpha(c)) { /* note that we're parsing an attribute name */ html_parse_state_ = VMCON_HPS_ATTR_NAME; /* empty the buffer */ attrname_[0] = L'\0'; /* go back and re-read it in the new mode */ continue; } break; } break; case VMCON_HPS_DQUOTE: /* if we've reached the closing quote, return to tag state */ if (c == '"') html_parse_state_ = VMCON_HPS_TAG; break; case VMCON_HPS_SQUOTE: /* if we've reached the closing quote, return to tag state */ if (c == '\'') html_parse_state_ = VMCON_HPS_TAG; break; case VMCON_HPS_ATTR_NAME: /* * parsing an attribute name; resume at the last character * of the buffer so far */ dst = attrname_ + wcslen(attrname_); /* add characters to the attribute name */ while (t3_is_alpha(c) || t3_is_digit(c)) { /* store this character if there's room */ if (dst + 1 < attrname_ + CVFMT_MAX_ATTR_NAME) *dst++ = c; /* get the next character */ c = next_wchar(sp, slenp); } /* null-terminate the result so far */ *dst++ = '\0'; /* * if we've reached the end of the string, stop here, * staying in the same mode */ if (c == '\0') break; /* skip any whitespace */ while (t3_is_whitespace(c)) c = next_wchar(sp, slenp); /* if we found an '=', switch to parsing the value */ if (c == '=') { /* switch to value mode */ html_parse_state_ = VMCON_HPS_ATTR_VAL; /* empty the value buffer */ attrval_[0] = L'\0'; attr_qu_ = L'\0'; /* proceed to the next character */ break; } else { /* * There's no value explicitly given, so the value is * implicitly the same as the attribute name. */ wcscpy(attrval_, attrname_); /* this takes us back to tag mode */ html_parse_state_ = VMCON_HPS_TAG; /* go process the implicit attribute value */ goto process_attr_val; } case VMCON_HPS_ATTR_VAL: /* reading attribute value - pick up where we left off */ dst = attrval_ + wcslen(attrval_); /* if we haven't started the name yet, skip whitespace */ if (attr_qu_ == 0 && attrval_[0] == L'\0') { while (t3_is_whitespace(c)) c = next_wchar(sp, slenp); } /* if we're out of characters, we're done */ if (c == 0) break; /* if this is the first character, check for a quote */ if (attrval_[0] == L'\0' && (c == L'"' || c == L'\'')) { /* note the quote */ attr_qu_ = c; /* skip it */ c = next_wchar(sp, slenp); if (c == 0) break; } /* keep going until we reach the end of the attribute */ while (c != 0) { /* if this is our quote character, we're done */ if (attr_qu_ != 0 && c == attr_qu_) { /* skip the quote */ c = next_wchar(sp, slenp); /* return to tag mode */ html_parse_state_ = VMCON_HPS_TAG; /* stop scanning */ break; } /* * if it's a space or a '>', and the attribute isn't * quoted, this marks the end of the attribute */ if (attr_qu_ == 0 && (t3_is_whitespace(c) || c == '>')) { /* return to tag mode */ html_parse_state_ = VMCON_HPS_TAG; /* stop scanning */ break; } /* store the character if we have room */ if (dst + 1 < attrval_ + CVFMT_MAX_ATTR_VAL) *dst++ = c; /* read the next character */ c = next_wchar(sp, slenp); } /* null-terminate the destination */ *dst = L'\0'; /* * if we're still in value-parsing mode, it means that we * reached the end of the string without reaching a value * delimiter - simply stop scanning and wait for more */ if (html_parse_state_ == VMCON_HPS_ATTR_VAL) break; process_attr_val: /* * We have our tag and value, so see if we recognize it, * and it's meaningful in the context of the current tag. */ if (html_defer_br_ != HTML_DEFER_BR_NONE && CVmCaseFoldStr::wstreq(attrname_, L"height")) { int ht; /* * If the height is zero, always treat this as a * non-blanking flush. If it's one, treat it as we * originally planned to. If it's greater than one, * add n blank lines. */ ht = wtoi(attrval_); if (ht == 0) { /* always use non-blanking flush */ html_defer_br_ = HTML_DEFER_BR_FLUSH; } else if (ht == 1) { /* keep original setting */ } else { /* * write out the desired number of blank lines */ for ( ; ht > 0 ; --ht) write_blank_line(vmg0_); } } else if (html_allow_alt_ && !html_in_ignore_ && CVmCaseFoldStr::wstreq(attrname_, L"alt")) { /* write out the ALT string */ buffer_wstring(vmg_ attrval_); } else if (html_allow_color_ && CVmCaseFoldStr::wstreq(attrname_, L"color")) { os_color_t new_color; /* parse the color, and use it as the new foreground */ if (parse_color_attr(vmg_ attrval_, &new_color)) cur_color_.fg = new_color; } else if (html_allow_color_ && CVmCaseFoldStr::wstreq(attrname_, L"bgcolor")) { os_color_t new_color; /* parse the color, and use it as the new foreground */ if (parse_color_attr(vmg_ attrval_, &new_color)) cur_color_.bg = new_color; } else if (html_in_body_ && CVmCaseFoldStr::wstreq(attrname_, L"text")) { os_color_t new_color; /* parse the color, and use it as the new foreground */ if (parse_color_attr(vmg_ attrval_, &new_color)) cur_color_.fg = new_color; } else if (html_in_body_ && CVmCaseFoldStr::wstreq(attrname_, L"bgcolor")) { os_color_t new_color; /* parse the color, and use it as the new background */ if (parse_color_attr(vmg_ attrval_, &new_color)) set_os_body_color(new_color); } else if (html_in_tab_) { /* check the attribute name */ if (CVmCaseFoldStr::wstreq(attrname_, L"id")) { /* * We're defining a new tab at the current output * position. Find or create a tab object with the * given ID, and set its position to the current * column position. (If we already have an entry * with the same name, the new definition replaces * the old definition, so just change the existing * entry.) */ CVmFmtTabStop *entry = find_tab(attrval_, TRUE); /* define the entry in the current column */ entry->column_ = linecol_; /* this finishes the */ html_in_tab_ = FALSE; } else if (CVmCaseFoldStr::wstreq(attrname_, L"to")) { /* * find the tab object with the given ID, and * remember it; when we're done parsing the tag, * we'll look at this to generate our tab output */ new_tab_entry_ = find_tab(attrval_, FALSE); /* if we didn't find it, ignore this */ if (new_tab_entry_ == 0) html_in_tab_ = FALSE; } else if (CVmCaseFoldStr::wstreq(attrname_, L"indent")) { /* * it's a simple - this simply * inserts the given number of spaces */ write_tab(vmg_ wtoi(attrval_), 0); /* this finishes the */ html_in_tab_ = FALSE; } else if (CVmCaseFoldStr::wstreq(attrname_, L"multiple")) { /* * it's a simple - this simply * inserts spaces to the given column multiple */ write_tab(vmg_ wtoi(attrval_), 0); /* this finishes the */ html_in_tab_ = FALSE; } else if (CVmCaseFoldStr::wstreq(attrname_, L"align")) { /* note the alignment */ if (CVmCaseFoldStr::wstreq(attrval_, L"left")) new_tab_align_ = VMFMT_TAB_LEFT; else if (CVmCaseFoldStr::wstreq(attrval_, L"right")) new_tab_align_ = VMFMT_TAB_RIGHT; else if (CVmCaseFoldStr::wstreq(attrval_, L"center")) new_tab_align_ = VMFMT_TAB_CENTER; else if (CVmCaseFoldStr::wstreq(attrval_, L"decimal")) new_tab_align_ = VMFMT_TAB_DECIMAL; else { /* invalid - ignore the */ html_in_tab_ = FALSE; } } else if (CVmCaseFoldStr::wstreq(attrname_, L"dp")) { /* note the decimal point character */ new_tab_dp_ = attrval_[0]; } } else if (html_in_wrap_) { /* it's a WRAP tag - check the attribute */ if (CVmCaseFoldStr::wstreq(attrname_, L"word")) { /* word wrap mode - clear the 'any' wrap flag */ cur_flags_ &= ~VMCON_OBF_BREAK_ANY; } else if (CVmCaseFoldStr::wstreq(attrname_, L"char")) { /* character wrap mode - set the 'any' flag */ cur_flags_ |= VMCON_OBF_BREAK_ANY; } } /* * since we already read the next character, simply loop * back immediately */ continue; default: /* ignore other states */ break; } /* * move on to the next character, and start over with the * new character */ c = next_wchar(sp, slenp); continue; } /* * If we're in a title, and this isn't the start of a new tag, * skip the character - we suppress all regular text output * inside a ... sequence. */ if (html_in_ignore_) { /* check for the start of a new tag */ if (c == '<') { /* stop skipping - return to normal parsing */ return c; } else { wchar_t curCh; /* check for entities */ if (c == '&') { /* parse the entity (fetches the next character) */ c = parse_entity(vmg_ &curCh, sp, slenp); } else { /* use this character literally */ curCh = c; /* get the next character */ c = next_wchar(sp, slenp); } /* * if we're gathering a title, and there's room in the * title buffer for more (always leaving room for a null * terminator), add this to the title buffer */ if (html_in_title_ && html_title_ptr_ != 0) { size_t rem, orig_rem; /* * figure the remaining title buffer space, leaving * room for a terminating null byte */ orig_rem = rem = html_title_buf_size_ - 1; /* * map this character into the local character set and * add it to the buffer */ if (G_cmap_to_ui->map(curCh, &html_title_ptr_, &rem) > orig_rem) { /* * it didn't fit - add null termination and close * out the buffer (we might have to add more than * one byte, because a single character might take * up multiple bytes in the local character set; to * ensure that we don't skip a wide character then * later add a narrower character, close out the * entire buffer when first we encounter a * character that doesn't fit) */ while (html_title_ptr_ < html_title_buf_ + html_title_buf_size_) *html_title_ptr_++ = '\0'; } } } /* don't display anything in an ignore section */ continue; } /* * we didn't find any HTML parsing we need to do with this * character, so return the character */ return c; } /* return the character that made us stop parsing */ return c; } /* ------------------------------------------------------------------------ */ /* * Parse the beginning HTML markup. This is called when we are scanning a * '<' or '&' character in output text, and we're in HTML mode, and the * underlying target doesn't support HTML parsing. Returns the next * character to process after we finish our initial parsing. */ wchar_t CVmFormatter::parse_html_markup(VMG_ wchar_t c, const char **sp, size_t *slenp) { /* check what we have */ if (c == '<') { const size_t MAX_TAG_SIZE = 50; wchar_t tagbuf[MAX_TAG_SIZE]; wchar_t *dst; int is_end_tag; /* skip the opening '<' */ c = next_wchar(sp, slenp); /* note if this is a closing tag */ if (c == '/' || c == '\\') { /* it's an end tag - note it and skip the slash */ is_end_tag = TRUE; c = next_wchar(sp, slenp); } else is_end_tag = FALSE; /* * find the end of the tag name - the tag continues to the next * space, '>', or end of line */ for (dst = tagbuf ; c != '\0' && c != ' ' && c != '>' ; c = next_wchar(sp, slenp)) { /* add this to the tag buffer if it fits */ if (dst + 1 < tagbuf + MAX_TAG_SIZE) *dst++ = c; } /* null-terminate the tag name */ *dst = '\0'; /* * Check to see if we recognize the tag. We only recognize a few * simple tags that map easily to character mode. */ if (CVmCaseFoldStr::wstreq(tagbuf, L"br")) { /* * line break - if there's anything buffered up, just flush the * current line, otherwise write out a blank line */ if (html_in_ignore_) /* suppress in ignore mode */; else if (linepos_ != 0) html_defer_br_ = HTML_DEFER_BR_FLUSH; else html_defer_br_ = HTML_DEFER_BR_BLANK; } else if (CVmCaseFoldStr::wstreq(tagbuf, L"b") || CVmCaseFoldStr::wstreq(tagbuf, L"i") || CVmCaseFoldStr::wstreq(tagbuf, L"em") || CVmCaseFoldStr::wstreq(tagbuf, L"strong")) { /* suppress in ignore mode */ if (!html_in_ignore_) { /* check to see if this is a start or end tag */ if (!is_end_tag) { int attr; /* it's a start tag - push the current attributes */ push_color(); /* figure the attribute we're adding */ switch(tagbuf[0]) { case 'b': case 'B': attr = OS_ATTR_BOLD; break; case 'i': case 'I': attr = OS_ATTR_ITALIC; break; case 'e': case 'E': attr = OS_ATTR_EM; break; case 's': case 'S': attr = OS_ATTR_STRONG; break; } /* add the attribute */ cur_color_.attr |= attr; } else { /* it's an end tag - just pop the saved attributes */ pop_color(); } } } else if (CVmCaseFoldStr::wstreq(tagbuf, L"p")) { /* paragraph - send out a blank line */ if (!html_in_ignore_) write_blank_line(vmg0_); } else if (CVmCaseFoldStr::wstreq(tagbuf, L"tab") && !html_in_ignore_) { if (!html_in_ignore_) { /* if we have a pending tab, handle it */ expand_pending_tab(vmg_ TRUE); /* start a tab definition */ html_in_tab_ = TRUE; /* the new tab has no known characteristics yet */ new_tab_entry_ = 0; new_tab_align_ = VMFMT_TAB_NONE; new_tab_dp_ = L'\0'; } } else if (CVmCaseFoldStr::wstreq(tagbuf, L"img") || CVmCaseFoldStr::wstreq(tagbuf, L"sound")) { /* IMG and SOUND - allow ALT attributes */ html_allow_alt_ = TRUE; } else if (CVmCaseFoldStr::wstreq(tagbuf, L"font")) { /* FONT - look for COLOR attribute */ if (html_in_ignore_) { /* do nothing while ignoring tags */ } else if (!is_end_tag) { /* enable the COLOR attribute */ html_allow_color_ = TRUE; /* push the current color, in case we change it */ push_color(); } else { /* restore the color in the enclosing text */ pop_color(); } } else if (CVmCaseFoldStr::wstreq(tagbuf, L"body")) { /* BODY - look for BGCOLOR and TEXT attributes */ if (!html_in_ignore_ && !is_end_tag) { /* note that we're parsing a BODY tag */ html_in_body_ = TRUE; } } else if (CVmCaseFoldStr::wstreq(tagbuf, L"hr")) { int rem; if (!html_in_ignore_) { /* start a new line */ flush(vmg_ VM_NL_NEWLINE); /* write out underscores to the display width */ for (rem = console_->get_line_width() - 1 ; rem > 0 ; ) { const size_t DASHBUFLEN = 100; wchar_t dashbuf[DASHBUFLEN]; wchar_t *p; size_t cur; /* do as much as we can on this pass */ cur = rem; if (cur > DASHBUFLEN - 1) cur = DASHBUFLEN - 1; /* deduct this from the total */ rem -= cur; /* fill the buffer with dashes */ for (p = dashbuf ; cur != 0 ; --cur) *p++ = '-'; *p = '\0'; /* write it out */ buffer_wstring(vmg_ dashbuf); } /* put a blank line after the underscores */ write_blank_line(vmg0_); } } else if (CVmCaseFoldStr::wstreq(tagbuf, L"q")) { wchar_t htmlchar; if (!html_in_ignore_) { /* if it's an open quote, increment the level */ if (!is_end_tag) ++html_quote_level_; /* add the open quote */ htmlchar = (!is_end_tag ? ((html_quote_level_ & 1) == 1 ? 8220 : 8216) : ((html_quote_level_ & 1) == 1 ? 8221 : 8217)); /* write out the HTML quote character */ buffer_char(vmg_ htmlchar); /* if it's a close quote, decrement the level */ if (is_end_tag) --html_quote_level_; } } else if (CVmCaseFoldStr::wstreq(tagbuf, L"title")) { /* * Turn ignore mode on or off as appropriate, and turn on or * off title mode as well. */ if (is_end_tag) { /* * note that we're leaving an ignore section and a title * section */ --html_in_ignore_; --html_in_title_; /* * If we're no longer in a title, and we have a buffer * where we've gathered up the title string, call the OS * layer to tell it the title string, in case it wants to * change the window title or otherwise make use of the * title. (Note that some streams don't bother keeping * track of the title at all, so we might not have a title * buffer.) */ if (html_in_title_ == 0 && html_title_ptr_ != 0) { /* null-terminate the title string */ *html_title_ptr_ = '\0'; /* tell the OS about the title */ if (!html_in_ignore_) set_title_in_os(html_title_buf_); } } else { /* * if we aren't already in a title, set up to capture the * title into the title buffer */ if (!html_in_title_) html_title_ptr_ = html_title_buf_; /* * note that we're in a title and in an ignore section, * since nothing within gets displayed */ ++html_in_ignore_; ++html_in_title_; } } else if (CVmCaseFoldStr::wstreq(tagbuf, L"aboutbox") || CVmCaseFoldStr::wstreq(tagbuf, L"banner")) { /* turn ignore mode on or off as appropriate */ if (is_end_tag) --html_in_ignore_; else ++html_in_ignore_; } else if (CVmCaseFoldStr::wstreq(tagbuf, L"log")) { /* handle the tag according to our stream subclass */ process_log_tag(is_end_tag); } else if (CVmCaseFoldStr::wstreq(tagbuf, L"nolog")) { /* handle the tag according to our stream subclass */ process_nolog_tag(is_end_tag); } else if (CVmCaseFoldStr::wstreq(tagbuf, L"wrap")) { /* note that we have a 'wrap' tag to process */ if (!html_in_ignore_) html_in_wrap_ = TRUE; } else if (CVmCaseFoldStr::wstreq(tagbuf, L"pre")) { /* count the nesting level if starting PRE mode */ if (!is_end_tag) ++html_pre_level_; /* surround the PRE block with blank lines */ write_blank_line(vmg0_); /* count the nesting level if ending PRE mode */ if (is_end_tag && html_pre_level_ != 0) --html_pre_level_; } /* suppress everything up to the next '>' */ html_parse_state_ = VMCON_HPS_TAG; /* * return the next character - since we're now in TAG mode, we'll * resume the HTML mini-parser to parse the contents of the tag */ return c; } else if (c == '&') { /* parse the HTML markup */ wchar_t ent; c = parse_entity(vmg_ &ent, sp, slenp); /* translate it and write it out */ buffer_char(vmg_ ent); /* proceed with the next character */ return c; } else { /* * we have nothing special to do with this character - simply * buffer it and move on to the next character */ buffer_char(vmg_ c); return next_wchar(sp, slenp); } } /* ------------------------------------------------------------------------ */ /* * Parse an HTML entity markup. Call this with the current character * pointer at the '&'. We'll parse the entity name and return it in *ent. * The return value is the next character to be parsed after the entity. */ wchar_t CVmFormatter::parse_entity(VMG_ wchar_t *ent, const char **sp, size_t *slenp) { const size_t MAXAMPLEN = 10; char ampbuf[MAXAMPLEN]; char *dst; const char *orig_s; size_t orig_slen; const struct amp_tbl_t *ampptr; size_t lo, hi, cur; wchar_t c; /* * remember where the part after the '&' begins, so we can come back * here later if necessary */ orig_s = *sp; orig_slen = *slenp; /* get the character after the ampersand */ c = next_wchar(sp, slenp); /* if it's numeric, parse the number */ if (c == '#') { uint val; /* skip the '#' */ c = next_wchar(sp, slenp); /* check for hex */ if (c == 'x' || c == 'X') { /* skip the 'x' */ c = next_wchar(sp, slenp); /* read the hex number */ for (val = 0 ; is_xdigit(c) ; c = next_wchar(sp, slenp)) { /* accumulate this digit into the value */ val *= 16; if (is_digit(c)) val += c - '0'; else if (c >= 'a' && c <= 'f') val += c - 'a' + 10; else val += c - 'A' + 10; } } else { /* read the number */ for (val = 0 ; is_digit(c) ; c = next_wchar(sp, slenp)) { /* accumulate this digit into the value */ val *= 10; val += c - '0'; } } /* if we found a ';' at the end, skip it */ if (c == ';') c = next_wchar(sp, slenp); /* return the entity from the numeric character value */ *ent = (wchar_t)val; /* we're done with this character */ return c; } /* * Parse the sequence after the '&'. Parse up to the closing * semicolon, or any non-alphanumeric, or until we fill up the buffer. */ for (dst = ampbuf ; c != '\0' && (is_digit(c) || is_alpha(c)) && dst < ampbuf + MAXAMPLEN - 1 ; c = next_wchar(sp, slenp)) { /* copy this character to the name buffer */ *dst++ = (char)c; } /* null-terminate the name */ *dst = '\0'; /* do a binary search for the name */ lo = 0; hi = sizeof(amp_tbl)/sizeof(amp_tbl[0]) - 1; for (;;) { int diff; /* if we've converged, look no further */ if (lo > hi || lo >= sizeof(amp_tbl)/sizeof(amp_tbl[0])) { ampptr = 0; break; } /* split the difference */ cur = lo + (hi - lo)/2; ampptr = &_tbl[cur]; /* see where we are relative to the target item */ diff = strcmp(ampptr->cname, ampbuf); if (diff == 0) { /* this is it */ break; } else if (lo >= hi) { /* failed to find it */ ampptr = 0; break; } else if (diff > 0) { /* this one is too high - check the lower half */ hi = (cur == hi ? hi - 1 : cur); } else { /* this one is too low - check the upper half */ lo = (cur == lo ? lo + 1 : cur); } } /* skip to the appropriate next character */ if (c == ';') { /* name ended with semicolon - skip the semicolon */ c = next_wchar(sp, slenp); } else if (ampptr != 0) { int skipcnt; /* found the name - skip its exact length */ skipcnt = strlen(ampptr->cname); for (*sp = orig_s, *slenp = orig_slen ; skipcnt != 0 ; c = next_wchar(sp, slenp), --skipcnt) ; /* * that positions us on the last character of the entity name; skip * one more, so that we're on the character after the entity name */ c = next_wchar(sp, slenp); } /* if we found the entry, write out the character */ if (ampptr != 0) { /* we found it - return the character value */ *ent = ampptr->html_cval; } else { /* * we didn't find it - ignore the entire sequence, and just write * it out verbatim; for our caller's purposes, the result of the * parse is just the '&' itself */ *ent = '&'; /* now go back and scan from the next character after the ampersand */ *sp = orig_s; *slenp = orig_slen; c = next_wchar(sp, slenp); } /* return the next character */ return c; } /* ------------------------------------------------------------------------ */ /* * Service routine - read a number represented as a base-10 string of wide * characters */ int CVmFormatter::wtoi(const wchar_t *p) { long acc; int neg = FALSE; /* skip any leading whitespace */ while (t3_is_whitespace(*p)) ++p; /* check for a leading sign */ if (*p == '+') { /* positive sign - skip it */ ++p; } else if (*p == '-') { /* negative sign - note it and skip it */ neg = TRUE; ++p; } /* scan the digits */ for (acc = 0 ; is_digit(*p) ; ++p) { /* shift the accumulator and add this digit */ acc *= 10; acc += value_of_digit(*p); } /* apply the sign */ if (neg) acc = -acc; /* return the result */ return acc; } qtads-2.1.7/tads3/vmconhtm.cpp000066400000000000000000000043361265017072300162230ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmconnom.cpp - TADS 3 console input/output - HTML version Function There are three possible configurations for the output formatter. For each configuration, there is one extra file that must be linked into the system when building an application; the choice of this file determines the configuration of the system. The three choices are: Text only, MORE mode enabled - vmconmor.cpp Text only, OS-level MORE handling - vmconnom.cpp HTML mode - vmconhtm.cpp Notes Modified 09/06/99 MJRoberts - Creation */ #include #include "os.h" #include "t3std.h" #include "vmconsol.h" /* ------------------------------------------------------------------------ */ /* * This version uses OS-level MORE prompting (actually, the HTML * renderer handles the prompting, but from our perspective the HTML * renderer looks like the OS layer), so indicate that we do not handle * the MORE prompt in the output formatter */ int CVmFormatter::formatter_more_mode() const { return FALSE; } /* ------------------------------------------------------------------------ */ /* * This is an HTML-enabled version, so we turn on the HTML-target flag */ int CVmFormatter::get_init_html_target() const { return TRUE; } /* ------------------------------------------------------------------------ */ /* * This is an HTML-enabled version. Pass through HTML start/end operations * to the OS-layer renderer. */ void CVmFormatterMain::start_html_in_os() { os_start_html(); } void CVmFormatterMain::end_html_in_os() { os_end_html(); } /* * The OS layer (i.e., the HTML renderer) handles line wrapping */ int CVmFormatterMain::get_os_line_wrap() { return TRUE; } /* ------------------------------------------------------------------------ */ /* * This version handles MORE prompting in the OS code, so call the OS * code when we need to display a MORE prompt */ void CVmConsole::show_con_more_prompt(VMG0_) { os_more_prompt(); } qtads-2.1.7/tads3/vmconsol.cpp000066400000000000000000003665351265017072300162440ustar00rootroot00000000000000#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1987, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmconsol.cpp - TADS 3 console input reader and output formatter Function Provides console input and output for the TADS 3 built-in function set for the T3 VM, including the output formatter. T3 uses the UTF-8 character set to represent character strings. The OS functions use the local character set. We perform the mapping between UTF-8 and the local character set within this module, so that OS routines see local characters only, not UTF-8. This code is based on the TADS 2 output formatter, but has been substantially reworked for C++, Unicode, and the slightly different TADS 3 formatting model. Notes Returns None Modified 08/25/99 MJRoberts - created from TADS 2 output formatter */ #include #include #include #include #include #include "wchar.h" #include "os.h" #include "t3std.h" #include "utf8.h" #include "charmap.h" #include "vmuni.h" #include "vmconsol.h" #include "vmglob.h" #include "vmhash.h" #include "vmdatasrc.h" #include "vmnetfil.h" #include "vmfilobj.h" #include "vmerr.h" #include "vmobj.h" /* ------------------------------------------------------------------------ */ /* * Log-file formatter subclass implementation */ /* * Open a new log file */ int CVmFormatterLog::open_log_file(VMG_ const char *fname) { CVmNetFile *nf = 0; err_try { /* create the network file descriptor */ nf = CVmNetFile::open( vmg_ fname, 0, NETF_NEW, OSFTLOG, "text/plain"); } err_catch_disc { nf = 0; } err_end; /* open the log file using the network file descriptor */ return open_log_file(vmg_ nf); } int CVmFormatterLog::open_log_file(VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc) { CVmNetFile *nf = 0; err_try { /* create the network file descriptor */ nf = CVmNetFile::open(vmg_ filespec, rc, NETF_NEW, OSFTLOG, "text/plain"); /* validate file safety */ CVmObjFile::check_safety_for_open(vmg_ nf, VMOBJFILE_ACCESS_WRITE); } err_catch_disc { /* if we got a file, it must be a safety exception - rethrow it */ if (nf != 0) { nf->abandon(vmg0_); err_rethrow(); } } err_end; /* open the log file using the network file descriptor */ int err = open_log_file(vmg_ nf); /* if that succeeded, remember the file spec in our global variable */ if (!err) { /* create our VM global if we don't have one already */ if (logglob_ == 0) logglob_ = G_obj_table->create_global_var(); /* remember the file spec */ logglob_->val = *filespec; } /* return the result */ return err; } int CVmFormatterLog::open_log_file(VMG_ CVmNetFile *nf) { /* close any existing log file */ if (close_log_file(vmg0_)) { if (nf != 0) nf->abandon(vmg0_); return 1; } /* if there's no network file spec, return failure */ if (nf == 0) return 1; /* reinitialize */ init(); /* remember the network file descriptor */ lognf_ = nf; /* open the local file */ logfp_ = osfopwt(lognf_->lclfname, OSFTLOG); /* if we couldn't open the file, abandon the network file descriptor */ if (logfp_ == 0) { lognf_->abandon(vmg0_); lognf_ = 0; } /* return success if we successfully opened the file, failure otherwise */ return (logfp_ == 0); } /* * Set the log file to a file opened by the caller */ int CVmFormatterLog::set_log_file(VMG_ CVmNetFile *nf, osfildef *fp) { /* close any existing log file */ if (close_log_file(vmg0_)) return 1; /* reinitialize */ init(); /* remember the file */ logfp_ = fp; lognf_ = nf; /* success */ return 0; } /* * Close the log file */ int CVmFormatterLog::close_log_file(VMG0_) { /* presume success */ int err = FALSE; /* if we have a file, close it */ if (logfp_ != 0) { /* close and forget the handle */ osfcls(logfp_); logfp_ = 0; } /* forget the log network file descriptor, if we have one */ if (lognf_ != 0) { err_try { lognf_->close(vmg0_); } err_catch_disc { /* flag the error, but otherwise discard the exception */ err = TRUE; } err_end; /* forget the descriptor, as we've just deleted it */ lognf_ = 0; } /* clear our global for the file spec */ if (logglob_ != 0) logglob_->val.set_nil(); /* success */ return err; } /* ------------------------------------------------------------------------ */ /* * Base Formatter */ /* * deletion */ CVmFormatter::~CVmFormatter() { /* if we have a table of horizontal tabs, delete it */ if (tabs_ != 0) delete tabs_; /* forget the character mapper */ set_charmap(0); } /* * set a new character mapper */ void CVmFormatter::set_charmap(CCharmapToLocal *cmap) { /* add a reference to the new mapper, if we have one */ if (cmap != 0) cmap->add_ref(); /* release our reference on any old mapper */ if (cmap_ != 0) cmap_->release_ref(); /* remember the new mapper */ cmap_ = cmap; } /* * Write out a line. Text we receive is in the UTF-8 character set. */ void CVmFormatter::write_text(VMG_ const wchar_t *txt, size_t cnt, const vmcon_color_t *colors, vm_nl_type nl) { /* * Check the "script quiet" mode - this indicates that we're reading * a script and not echoing output to the display. If this mode is * on, and we're writing to the display, suppress this write. If * the mode is off, or we're writing to a non-display stream (such * as a log file stream), show the output as normal. */ if (!console_->is_quiet_script() || !is_disp_stream_) { char local_buf[128]; char *dst; size_t rem; /* * Check to see if we've reached the end of the screen, and if so * run the MORE prompt. Note that we don't show a MORE prompt * unless we're in "formatter more mode," since if we're not, then * the OS layer code is taking responsibility for pagination * issues. * * Note that we suppress the MORE prompt if we're showing a * continuation of a line already partially shown. We only want to * show a MORE prompt at the start of a new line. * * Skip the MORE prompt if this stream doesn't use it. */ if (formatter_more_mode() && console_->is_more_mode() && !is_continuation_ && linecnt_ + 1 >= console_->get_page_length()) { /* set the standard text color */ set_os_text_color(OS_COLOR_P_TEXT, OS_COLOR_P_TEXTBG); set_os_text_attr(0); /* display the MORE prompt */ console_->show_more_prompt(vmg0_); /* restore the current color scheme */ set_os_text_color(os_color_.fg, os_color_.bg); set_os_text_attr(os_color_.attr); } /* count the line if a newline follows */ if (nl != VM_NL_NONE && nl != VM_NL_NONE_INTERNAL) ++linecnt_; /* convert and display the text */ for (dst = local_buf, rem = sizeof(local_buf) - 1 ; cnt != 0 ; ) { size_t cur; size_t old_rem; wchar_t c; /* * if this character is in a new color, write out the OS-level * color switch code */ if (colors != 0 && !colors->equals(&os_color_)) { /* * null-terminate and display what's in the buffer so far, * so that we close out all of the remaining text in the * old color and attributes */ *dst = '\0'; print_to_os(local_buf); /* reset to the start of the local output buffer */ dst = local_buf; rem = sizeof(local_buf) - 1; /* set the text attributes, if they changed */ if (colors->attr != os_color_.attr) set_os_text_attr(colors->attr); /* set the color, if it changed */ if (colors->fg != os_color_.fg || colors->bg != os_color_.bg) set_os_text_color(colors->fg, colors->bg); /* * Whatever happened, set our new color internally as the * last color we sent to the OS. Even if we didn't * actually do anything, we'll at least know we won't have * to do anything more until we find another new color. */ os_color_ = *colors; } /* get this character */ c = *txt; /* * translate non-breaking spaces into ordinary spaces if the * underlying target isn't HTML-based */ if (!html_target_ && c == 0x00A0) c = ' '; /* try storing another character */ old_rem = rem; cur = (cmap_ != 0 ? cmap_ : G_cmap_to_ui)->map(c, &dst, &rem); /* if that failed, flush the buffer and try again */ if (cur > old_rem) { /* null-terminate the buffer */ *dst = '\0'; /* display the text */ print_to_os(local_buf); /* reset to the start of the local output buffer */ dst = local_buf; rem = sizeof(local_buf) - 1; } else { /* we've now consumed this character of input */ ++txt; --cnt; if (colors != 0) ++colors; } } /* if we have a partially-filled buffer, display it */ if (dst > local_buf) { /* null-terminate and display the buffer */ *dst = '\0'; print_to_os(local_buf); } /* write the appropriate type of line termination */ switch(nl) { case VM_NL_NONE: case VM_NL_INPUT: case VM_NL_NONE_INTERNAL: /* no line termination is needed */ break; case VM_NL_NEWLINE: /* write a newline */ print_to_os(html_target_ && html_pre_level_ == 0 ? "
\n" : "\n"); break; case VM_NL_OSNEWLINE: /* * the OS will provide a newline, but add a space to make it * explicit that we can break the line here */ print_to_os(" "); break; } } } /* ------------------------------------------------------------------------ */ /* * Flush the current line to the display, using the given type of line * termination. * * VM_NL_NONE: flush the current line but do not start a new line; more * text will follow on the current line. This is used, for example, to * flush text after displaying a prompt and before waiting for user * input. * * VM_NL_INPUT: acts like VM_NL_NONE, except that we flush everything, * including trailing spaces. * * VM_NL_NONE_INTERNAL: same as VM_NL_NONE, but doesn't flush at the OS * level. This is used when we're only flushing our buffers in order to * clear out space internally, not because we want the underlying OS * renderer to display things immediately. This distinction is * important in HTML mode, since it ensures that the HTML parser only * sees well-formed strings when flushing. * * VM_NL_NEWLINE: flush the line and start a new line by writing out a * newline character. * * VM_NL_OSNEWLINE: flush the line as though starting a new line, but * don't add an actual newline character to the output, since the * underlying OS display code will handle this. Instead, add a space * after the line to indicate to the OS code that a line break is * possible there. (This differs from VM_NL_NONE in that VM_NL_NONE * doesn't add anything at all after the line.) */ void CVmFormatter::flush(VMG_ vm_nl_type nl) { int cnt; vm_nl_type write_nl; /* null-terminate the current output line buffer */ linebuf_[linepos_] = '\0'; /* * Expand any pending tab. Allow "anonymous" tabs only if we're * flushing because we're ending the line normally; if we're not * ending the line, we can't handle tabs that depend on the line * ending. */ expand_pending_tab(vmg_ nl == VM_NL_NEWLINE); /* * note number of characters to display - assume we'll display all of * the characters in the buffer */ cnt = wcslen(linebuf_); /* * Trim trailing spaces, unless we're about to read input or are doing * an internal flush. (Show trailing spaces when reading input, since * we won't be able to revise the layout after this point. Don't trim * on an internal flush either, as this kind of flushing simply empties * out our buffer exactly as it is.) */ if (nl != VM_NL_INPUT && nl != VM_NL_NONE_INTERNAL) { /* * look for last non-space character, but keep any spaces that come * before an explicit non-breaking flag */ for ( ; cnt > 0 && linebuf_[cnt-1] == ' ' ; --cnt) { /* don't remove this character if it's marked as non-breaking */ if ((flagbuf_[cnt-1] & VMCON_OBF_NOBREAK) != 0) break; } /* * if we're actually doing a newline, discard the trailing spaces * for good - we don't want them at the start of the next line */ if (nl == VM_NL_NEWLINE) linepos_ = cnt; } /* check the newline mode */ switch(nl) { case VM_NL_NONE: case VM_NL_NONE_INTERNAL: /* no newline - just flush out what we have */ write_nl = VM_NL_NONE; break; case VM_NL_INPUT: /* no newline - flush out what we have */ write_nl = VM_NL_NONE; /* on input, reset the HTML parsing state */ html_passthru_state_ = VMCON_HPS_NORMAL; break; case VM_NL_NEWLINE: /* * We're adding a newline. We want to suppress redundant * newlines -- we reduce any run of consecutive vertical * whitespace to a single newline. So, if we have anything in * this line, or we didn't already just write a newline, write * out a newline now; otherwise, write nothing. */ if (linecol_ != 0 || !just_did_nl_ || html_pre_level_ > 0) { /* add the newline */ write_nl = VM_NL_NEWLINE; } else { /* * Don't write out a newline after all - the line buffer is * empty, and we just wrote a newline, so this is a * redundant newline that we wish to suppress (so that we * collapse a run of vertical whitespace down to a single * newline). */ write_nl = VM_NL_NONE; } break; case VM_NL_OSNEWLINE: /* * we're going to depend on the underlying OS output layer to do * line breaking, so we won't add a newline, but we will add a * space, so that the underlying OS layer knows we have a word * break here */ write_nl = VM_NL_OSNEWLINE; break; } /* * display the line, as long as we have something buffered to * display; even if we don't, display it if our column is non-zero * and we didn't just do a newline, since this must mean that we've * flushed a partial line and are just now doing the newline */ if (cnt != 0 || (linecol_ != 0 && !just_did_nl_) || html_pre_level_ > 0) { /* write it out */ write_text(vmg_ linebuf_, cnt, colorbuf_, write_nl); } /* check the line ending */ switch (nl) { case VM_NL_NONE: case VM_NL_INPUT: /* we're not displaying a newline, so flush what we have */ flush_to_os(); /* * the subsequent buffer will be a continuation of the current * text, if we've displayed anything at all here */ is_continuation_ = (linecol_ != 0); break; case VM_NL_NONE_INTERNAL: /* * internal buffer flush only - subsequent text will be a * continuation of the current line, if there's anything on the * current line */ is_continuation_ = (linecol_ != 0); break; default: /* we displayed a newline, so reset the column position */ linecol_ = 0; /* the next buffer starts a new line on the display */ is_continuation_ = FALSE; break; } /* * Move any trailing characters we didn't write in this go to the start * of the buffer. */ if (cnt < linepos_) { size_t movecnt; /* calculate how many trailing characters we didn't write */ movecnt = linepos_ - cnt; /* move the characters, colors, and flags */ memmove(linebuf_, linebuf_ + cnt, movecnt * sizeof(linebuf_[0])); memmove(colorbuf_, colorbuf_ + cnt, movecnt * sizeof(colorbuf_[0])); memmove(flagbuf_, flagbuf_ + cnt, movecnt * sizeof(flagbuf_[0])); } /* move the line output position to follow the preserved characters */ linepos_ -= cnt; /* * If we just output a newline, note it. If we didn't just output a * newline, but we did write out anything else, note that we're no * longer at the start of a line on the underlying output device. */ if (nl == VM_NL_NEWLINE) just_did_nl_ = TRUE; else if (cnt != 0) just_did_nl_ = FALSE; /* * if the current buffering color doesn't match the current osifc-layer * color, then we must need to flush just the new color/attribute * settings (this can happen when we have changed the attributes in * preparation for reading input, since we won't have any actual text * to write after the color change) */ if (!cur_color_.equals(&os_color_)) { /* set the text attributes in the OS window, if they changed */ if (cur_color_.attr != os_color_.attr) set_os_text_attr(cur_color_.attr); /* set the color in the OS window, if it changed */ if (cur_color_.fg != os_color_.fg || cur_color_.bg != os_color_.bg) set_os_text_color(cur_color_.fg, cur_color_.bg); /* set the new osifc color */ os_color_ = cur_color_; } } /* ------------------------------------------------------------------------ */ /* * Clear out our buffers */ void CVmFormatter::empty_buffers(VMG0_) { /* reset our buffer pointers */ linepos_ = 0; linecol_ = 0; linebuf_[0] = '\0'; just_did_nl_ = FALSE; is_continuation_ = FALSE; /* there's no pending tab now */ pending_tab_align_ = VMFMT_TAB_NONE; /* start out at the first line */ linecnt_ = 0; /* reset the HTML lexical state */ html_passthru_state_ = VMCON_HPS_NORMAL; } /* ------------------------------------------------------------------------ */ /* * Immediately update the display window */ void CVmFormatter::update_display(VMG0_) { /* update the display window at the OS layer */ os_update_display(); } /* ------------------------------------------------------------------------ */ /* * Display a blank line to the stream */ void CVmFormatter::write_blank_line(VMG0_) { /* flush the stream */ flush(vmg_ VM_NL_NEWLINE); /* if generating for an HTML display target, add an HTML line break */ if (html_target_) write_text(vmg_ L"
", 4, 0, VM_NL_NONE); /* write out a blank line */ write_text(vmg_ L"", 0, 0, VM_NL_NEWLINE); } /* ------------------------------------------------------------------------ */ /* * Generate a tab for a "\t" sequence in the game text, or a or sequence parsed in our mini-parser. * * Standard (non-HTML) version: we'll generate enough spaces to take us to * the next tab stop. * * HTML version: if we're in native HTML mode, we'll just generate the * equivalent HTML; if we're not in HTML mode, we'll generate a hard tab * character, which the HTML formatter will interpret as a . */ void CVmFormatter::write_tab(VMG_ int indent, int multiple) { int maxcol; /* check to see what the underlying system is expecting */ if (html_target_) { char buf[40]; /* * the underlying system is HTML - generate an appropriate * sequence to produce the desired effect */ sprintf(buf, "", indent != 0 ? "INDENT" : "MULTIPLE", indent != 0 ? indent : multiple); /* write it out */ buffer_string(vmg_ buf); } else if (multiple != 0) { /* get the maximum column */ maxcol = get_buffer_maxcol(); /* * We don't have an HTML target, and we have a tab to an every-N * stop: expand the tab with spaces. Keep going until we reach * the next tab stop of the given multiple. */ do { /* stop if we've reached the maximum column */ if (linecol_ >= maxcol) break; /* add another space */ linebuf_[linepos_] = ' '; flagbuf_[linepos_] = cur_flags_; colorbuf_[linepos_] = cur_color_; /* advance one character in the buffer */ ++linepos_; /* advance the column counter */ ++linecol_; } while ((linecol_ + 1) % multiple != 0); } else if (indent != 0) { /* * We don't have an HTML target, and we just want to add a given * number of spaces. Simply write out the given number of spaces, * up to our maximum column limit. */ for (maxcol = get_buffer_maxcol() ; indent != 0 && linecol_ < maxcol ; --indent) { /* add another space */ linebuf_[linepos_] = ' '; flagbuf_[linepos_] = cur_flags_; colorbuf_[linepos_] = cur_color_; /* advance one character in the buffer and one column */ ++linepos_; ++linecol_; } } } /* ------------------------------------------------------------------------ */ /* * Flush a line */ void CVmFormatter::flush_line(VMG_ int padding) { /* * check to see if we're using the underlying display layer's line * wrapping */ if (os_line_wrap_) { /* * In the HTML version, we don't need the normal *MORE* * processing, since the HTML layer will handle that. * Furthermore, we don't need to provide actual newline breaks * -- that happens after the HTML is parsed, so we don't have * enough information here to figure out actual line breaks. * So, we'll just flush out our buffer whenever it fills up, and * suppress newlines. * * Similarly, if we have OS-level line wrapping, don't try to * figure out where the line breaks go -- just flush our buffer * without a trailing newline whenever the buffer is full, and * let the OS layer worry about formatting lines and paragraphs. * * If we're using padding, use newline mode VM_NL_OSNEWLINE. If * we don't want padding (which is the case if we completely * fill up the buffer without finding any word breaks), write * out in mode VM_NL_NONE, which just flushes the buffer exactly * like it is. */ flush(vmg_ padding ? VM_NL_OSNEWLINE : VM_NL_NONE_INTERNAL); } else { /* * Normal mode - we process the *MORE* prompt ourselves, and we * are responsible for figuring out where the actual line breaks * go. Use flush() to generate an actual newline whenever we * flush out our buffer. */ flush(vmg_ VM_NL_NEWLINE); } } /* ------------------------------------------------------------------------ */ /* * Write a character to an output stream. The character is provided to us * as a wide Unicode character. */ void CVmFormatter::buffer_char(VMG_ wchar_t c) { const wchar_t *exp; size_t exp_len; /* check for a display expansion */ exp = (cmap_ != 0 ? cmap_ : G_cmap_to_ui)->get_expansion(c, &exp_len); if (exp != 0) { /* write each character of the expansion */ for ( ; exp_len != 0 ; ++exp, --exp_len) buffer_expchar(vmg_ *exp); } else { /* there's no expansion - buffer the character as-is */ buffer_expchar(vmg_ c); } } /* * Write an expanded character to an output stream. */ void CVmFormatter::buffer_expchar(VMG_ wchar_t c) { /* presume the character takes up only one column */ int cwid = 1; /* presume we'll use the current flags for the new character */ unsigned char cflags = cur_flags_; /* assume it's not a quoted space */ int qspace = FALSE; /* * Check for some special characters. * * If we have an underlying HTML renderer, keep track of the HTML * lexical state, so we know if we're in a tag or in ordinary text. We * can pass through all of the special line-breaking and spacing * characters to the underlying HTML renderer. * * If our underlying renderer is a plain text renderer, we actually * parse the HTML ourselves, so HTML tags will never make it this far - * the caller will already have interpreted any HTML tags and removed * them from the text stream, passing only the final plain text to us. * However, with a plain text renderer, we have to do all of the work * of line breaking, so we must look at the special spacing and * line-break control characters. */ if (html_target_) { /* * track the lexical state of the HTML stream going to the * underlying renderer */ switch (html_passthru_state_) { case VMCON_HPS_MARKUP_END: case VMCON_HPS_NORMAL: /* check to see if we're starting a markup */ if (c == '&') html_passthru_state_ = VMCON_HPS_ENTITY_1ST; else if (c == '<') { html_passthru_tagp_ = html_passthru_tag_; html_passthru_state_ = VMCON_HPS_TAG_START; } else html_passthru_state_ = VMCON_HPS_NORMAL; break; case VMCON_HPS_ENTITY_1ST: /* check to see what kind of entity we have */ if (c == '#') html_passthru_state_ = VMCON_HPS_ENTITY_NUM_1ST; else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) html_passthru_state_ = VMCON_HPS_ENTITY_NAME; else html_passthru_state_ = VMCON_HPS_NORMAL; break; case VMCON_HPS_ENTITY_NUM_1ST: /* check to see what kind of number we have */ if (c == 'x' || c == 'X') html_passthru_state_ = VMCON_HPS_ENTITY_HEX; else if (c >= '0' && c <= '9') html_passthru_state_ = VMCON_HPS_ENTITY_DEC; else html_passthru_state_ = VMCON_HPS_NORMAL; break; case VMCON_HPS_ENTITY_HEX: /* see if we're done with hex digits */ if (c == ';') html_passthru_state_ = VMCON_HPS_MARKUP_END; else if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F')) html_passthru_state_ = VMCON_HPS_NORMAL; break; case VMCON_HPS_ENTITY_DEC: /* see if we're done with decimal digits */ if (c == ';') html_passthru_state_ = VMCON_HPS_MARKUP_END; else if (c < '0' || c > '9') html_passthru_state_ = VMCON_HPS_NORMAL; break; case VMCON_HPS_ENTITY_NAME: /* see if we're done with alphanumerics */ if (c == ';') html_passthru_state_ = VMCON_HPS_MARKUP_END; else if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9')) html_passthru_state_ = VMCON_HPS_NORMAL; break; case VMCON_HPS_TAG_START: /* start of a tag, before the name - check for the name start */ if (c == '/' && html_passthru_tagp_ == html_passthru_tag_) { /* * note the initial '/', but stay in TAG_START state, so * that we skip any spaces between the '/' and the tag name */ *html_passthru_tagp_++ = '/'; } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { /* start of the tag name */ html_passthru_state_ = VMCON_HPS_TAG_NAME; *html_passthru_tagp_++ = (char)c; } else if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { /* * ignore whitespace between '<' and the tag name - simply * stay in TAG_START mode */ } else { /* anything else is invalid - must not be a tag after all */ html_passthru_state_ = VMCON_HPS_NORMAL; } break; case VMCON_HPS_TAG_NAME: /* tag name - check for continuation */ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { /* gather the tag name if it fits */ if (html_passthru_tagp_ - html_passthru_tag_ + 1 < sizeof(html_passthru_tag_)) *html_passthru_tagp_++ = (char)c; } else { /* end of the tag name */ *html_passthru_tagp_ = '\0'; html_passthru_state_ = VMCON_HPS_TAG; goto do_tag; } break; case VMCON_HPS_TAG: do_tag: /* see if we're done with the tag, or entering quoted material */ if (c == '>') { /* switch to end of markup mode */ html_passthru_state_ = VMCON_HPS_MARKUP_END; /* note if entering or exiting a PRE tag */ if (stricmp(html_passthru_tag_, "pre") == 0) ++html_pre_level_; else if (stricmp(html_passthru_tag_, "/pre") == 0 && html_pre_level_ != 0) --html_pre_level_; } else if (c == '/' && html_passthru_tagp_ != html_passthru_tag_ && *(html_passthru_tagp_ - 1) != '/') { /* add the '/' to the end of the tag name */ if (html_passthru_tagp_ - html_passthru_tag_ + 1 < sizeof(html_passthru_tag_)) { *html_passthru_tagp_++ = (char)c; *html_passthru_tagp_ = '\0'; } } else if (c == '"') html_passthru_state_ = VMCON_HPS_DQUOTE; else if (c == '\'') html_passthru_state_ = VMCON_HPS_SQUOTE; break; case VMCON_HPS_SQUOTE: /* see if we're done with the quoted material */ if (c == '\'') html_passthru_state_ = VMCON_HPS_TAG; break; case VMCON_HPS_DQUOTE: /* see if we're done with the quoted material */ if (c == '"') html_passthru_state_ = VMCON_HPS_TAG; break; default: /* ignore other states */ break; } } else { /* check for special characters */ switch(c) { case 0x00AD: /* * The Unicode "soft hyphen" character. This indicates a * point at which we can insert a hyphen followed by a soft * line break, if it's a convenient point to break the line; * if we don't choose to break the line here, the soft hyphen * is invisible. * * Don't buffer anything at all; instead, just flag the * preceding character as being a soft hyphenation point, so * that we can insert a hyphen there when we get around to * breaking the line. */ if (linepos_ != 0) flagbuf_[linepos_ - 1] |= VMCON_OBF_SHY; /* we don't need to buffer anything, so we're done */ return; case 0xFEFF: /* * The Unicode zero-width non-breaking space. This indicates * a point at which we cannot break the line, even if we could * normally break here. Flag the preceding character as a * non-breaking point. Don't buffer anything for this * character, as it's not rendered; it merely controls line * breaking. */ if (linepos_ != 0) flagbuf_[linepos_ - 1] |= VMCON_OBF_NOBREAK; /* we don't buffer anything, so we're done */ return; case 0x200B: /* zero-width space */ case 0x200a: /* hair space */ case 0x2008: /* punctuation space */ /* * Zero-width space: This indicates an explicitly allowed * line-breaking point, but is rendered as invisible. Flag the * preceding character as an OK-to-break point, but don't * buffer anything, as the zero-width space isn't rendered. * * Hair and punctuation spaces: Treat these very thin spaces as * invisible in a fixed-width font. These are normally used * for subtle typographical effects in proportionally-spaced * fonts; for example, for separating a right single quote from * an immediately following right double quote (as in a * quotation within a quotation: I said, "type 'quote'"). When * translating to fixed-pitch type, these special spacing * effects aren't usually necessary or desirable because of the * built-in space in every character cell. * * These spaces cancel any explicit non-breaking flag that * precedes them, since they cause the flag to act on the * space's left edge, while leaving the right edge open for * breaking. Since we don't actually take up any buffer space, * push our right edge's breakability back to the preceding * character. */ if (linepos_ != 0) { flagbuf_[linepos_ - 1] &= ~VMCON_OBF_NOBREAK; flagbuf_[linepos_ - 1] |= VMCON_OBF_OKBREAK; } /* we don't buffer anything, so we're done */ return; case 0x00A0: /* non-breaking space - buffer it as given */ break; case 0x0015: /* special internal quoted space character */ case 0x2005: /* four-per-em space */ case 0x2006: /* six-per-em space */ case 0x2007: /* figure space */ case 0x2009: /* thin space */ /* * Treat all of these as non-combining spaces, and render them * all as single ordinary spaces. In text mode, we are * limited to a monospaced font, so we can't render any * differences among these various thinner-than-normal spaces. */ qspace = TRUE; c = ' '; break; case 0x2002: /* en space */ case 0x2004: /* three-per-em space */ /* * En space, three-per-em space - mark these as non-combining, * and render them as a two ordinary spaces. In the case of * an en space, we really do want to take up the space of two * ordinary spaces; for a three-per-em space, we want about a * space and a half, but since we're dealing with a monospaced * font, we have to round up to a full two spaces. */ qspace = TRUE; cwid = 2; c = ' '; break; case 0x2003: /* em space - mark it as non-combining */ qspace = TRUE; /* render this as three ordinary spaces */ cwid = 3; c = ' '; break; default: /* * Translate any horizontal whitespace character to a regular * space character. Note that, once this is done, we don't * need to worry about calling t3_is_space() any more - we can * just check that we have a regular ' ' character. */ if (t3_is_space(c)) { /* convert it to an ordinary space */ c = ' '; /* if we're in obey-whitespace mode, quote the space */ qspace = obey_whitespace_ || html_pre_level_ > 0; } break; } } /* if it's a quoted space, mark it as such in the buffer flags */ if (qspace) cflags |= VMCON_OBF_QSPACE; /* * Check for the caps/nocaps flags - but only if our HTML lexical state * in the underlying text stream is plain text, because we don't want * to apply these flags to alphabetic characters that are inside tag or * entity text. */ if (html_passthru_state_ == VMCON_HPS_NORMAL) { if ((capsflag_ || allcapsflag_) && t3_is_alpha(c)) { /* * capsflag or allcapsflag is set, so render this character in * title case or upper case, respectively. For the ordinary * capsflag, use title case rather than upper case, since * capsflag conceptually only applies to a single letter, and * some Unicode characters represent ligatures of multiple * letters. In such cases we only want to capitalize the first * letter in the ligature, which is exactly what we get when we * convert it to the title case. * * Start by consuming the capsflag. */ capsflag_ = FALSE; /* get the appropriate expansion */ const wchar_t *u = allcapsflag_ ? t3_to_upper(c) : t3_to_title(c); /* * If there's no expansion, continue with the original * character. If it's a single-character expansion, continue * with the replacement character. If it's a 1:N expansion, * recursively buffer the expansion. */ if (u != 0 && u[0] != 0) { if (u[1] != 0) { /* 1:N expansion - handle it recursively */ buffer_wstring(vmg_ u); return; } else { /* 1:1 expansion - continue with the new character */ c = u[0]; } } } else if (nocapsflag_ && t3_is_alpha(c)) { /* lower-casing the character - consume the flag */ nocapsflag_ = FALSE; /* get the expansion */ const wchar_t *l = t3_to_lower(c); /* * recursively handle 1:N expansions; otherwise continue with * the replacement character, if there is one */ if (l != 0 && l[0] != 0) { if (l[1] != 0) { /* 1:N expansion - handle it recursively */ buffer_wstring(vmg_ l); return; } else { /* 1:1 expansion - continue with the new character */ c = l[0]; } } } } /* * If this is a space of some kind, we might be able to consolidate it * with a preceding character. If the display layer is an HTML * renderer, pass spaces through intact, and let the HTML parser handle * whitespace compression. */ if (c == ' ' && html_pre_level_ == 0) { /* ignore ordinary whitespace at the start of a line */ if (linecol_ == 0 && !qspace) return; /* * Consolidate runs of whitespace. Ordinary whitespace is * subsumed into any type of quoted spaces, but quoted spaces do * not combine. */ if (linepos_ > 0) { wchar_t prv; /* get the previous character */ prv = linebuf_[linepos_ - 1]; /* * if the new character is an ordinary (combining) whitespace * character, subsume it into any preceding space character */ if (!qspace && prv == ' ') return; /* * if the new character is a quoted space, and the preceding * character is a non-quoted space, subsume the preceding * space into the new character */ if (qspace && prv == ' ' && !(flagbuf_[linepos_ - 1] & VMCON_OBF_QSPACE)) { /* remove the preceding ordinary whitespace */ --linepos_; --linecol_; } } } /* if the new character fits in the line, add it */ if (linecol_ + cwid < get_buffer_maxcol()) { /* buffer this character */ buffer_rendered(c, cflags, cwid); /* we're finished processing the character */ return; } /* * The line would overflow if this character were added. * * If we're trying to output any kind of breakable space, just add it * to the line buffer for now; we'll come back later and figure out * where to break upon buffering the next non-space character. This * ensures that we don't carry trailing space (even trailing en or em * spaces) to the start of the next line if we have an explicit * newline before the next non-space character. */ if (c == ' ') { /* * We're adding a space, so we'll figure out the breaking later, * when we output the next non-space character. If the preceding * character is any kind of space, don't bother adding the new * one, since any contiguous whitespace at the end of the line has * no effect on the line's appearance. */ if (linebuf_[linepos_ - 1] == ' ') { /* * We're adding a space to a line that already ends in a * space, so we don't really need to add the character. * However, reflect the virtual addition in the output column * position, since the space does affect our column position. * We know that we're adding the new space even though we have * a space preceding, since we wouldn't have gotten this far * if we were going to collapse the space with a run of * whitespace. */ } else { /* the line doesn't already end in space, so add the space */ linebuf_[linepos_] = ' '; flagbuf_[linepos_] = cflags; colorbuf_[linepos_] = cur_color_; /* advance one character in the buffer */ ++linepos_; } /* * Adjust the column position for the added space. Note that we * adjust by the rendered width of the new character even though * we actually added only one character; we only add one character * to the buffer to avoid buffer overflow, but the column position * needs adjustment by the full rendered width. The fact that the * actual buffer size and rendered width no longer match isn't * important because the difference is entirely in invisible * whitespace at the right end of the line. */ linecol_ += cwid; /* done for now */ return; } /* * We're adding something other than an ordinary space to the line, * and the new character won't fit, so we must find an appropriate * point to break the line. * * First, add the new character to the buffer - it could be * significant in how we calculate the break position. (Note that we * allocate the buffer with space for one extra character after * reaching the maximum line width, so we know we have room for this.) */ linebuf_[linepos_] = c; flagbuf_[linepos_] = cur_flags_; /* * if the underlying OS layer is doing the line wrapping, just flush * out the buffer; don't bother trying to do any line wrapping * ourselves, since this work would just be redundant with what the OS * layer has to do anyway */ if (os_line_wrap_) { /* flush the line, adding no padding after it */ flush_line(vmg_ FALSE); /* * we've completely cleared out the line buffer, so reset all of * the line buffer counters */ linepos_ = 0; linecol_ = 0; linebuf_[0] = '\0'; is_continuation_ = FALSE; /* we're done */ goto done_with_wrapping; } /* * Scan backwards, looking for a break position. Start at the current * column: we know we can fit everything up to this point on a line on * the underlying display, so this is the rightmost possible position * at which we could break the line. Keep going until we find a * breaking point or reach the left edge of the line. */ int shy, i; for (i = linepos_, shy = FALSE ; i >= 0 ; --i) { unsigned char f; unsigned char prvf; /* * There are two break modes: word-break mode and break-anywhere * mode. The modes are applied to each character, via the buffer * flags. * * In word-break mode, we can break at any ordinary space, at a * soft hyphen, just after a regular hyphen, or at any explicit * ok-to-break point; but we can't break after any character * marked as a no-break point. * * In break-anywhere mode, we can break between any two * characters, except that we can't break after any character * marked as a no-break point. */ /* get the current character's flags */ f = flagbuf_[i]; /* get the preceding character's flags */ prvf = (i > 0 ? flagbuf_[i-1] : 0); /* * if the preceding character is marked as a no-break point, we * definitely can't break here, so keep looking */ if ((prvf & VMCON_OBF_NOBREAK) != 0) continue; /* * if the preceding character is marked as an explicit ok-to-break * point, we definitely can break here */ if ((prvf & VMCON_OBF_OKBREAK) != 0) break; /* * If the current character is in a run of break-anywhere text, * then we can insert a break just before the current character. * Likewise, if the preceding character is in a run of * break-anywhere text, we can break just after the preceding * character, which is the same as breaking just before the * current character. * * Note that we must test for both cases to properly handle * boundaries between break-anywhere and word-break text. If * we're switching from word-break to break-anywhere text, the * current character will be marked as break-anywhere, so if we * only tested the previous character, we'd miss this transition. * If we're switching from break-anywhere to word-break text, the * previous character will be marked as break-anywhere, so we'd * miss the fact that we could break right here (rather than * before the previous character) if we didn't test it explicitly. */ if ((f & VMCON_OBF_BREAK_ANY) != 0 || (i > 0 && (prvf & VMCON_OBF_BREAK_ANY) != 0)) break; /* * If the preceding character is marked as a soft hyphenation * point, and we're not at the rightmost position, we can break * here with hyphenation. We can't break with hyphenation at the * last position because hyphenation requires us to actually * insert a hyphen character, and we know that at the last * position we don't have room for inserting another character. */ if (i > 0 && i < linepos_ && (prvf & VMCON_OBF_SHY) != 0) { /* note that we're breaking at a soft hyphen */ shy = TRUE; /* we can break here */ break; } /* * we can break to the left of a space (i.e., we can break before * the current character if the current character is a space) */ if (linebuf_[i] == ' ') break; /* * We can also break to the right of a space. We need to check * for this case separately from checking that the current * charatcer is a space (which breaks to the left of the space), * because we could have a no-break marker on one side of the * space but not on the other side. */ if (i > 0 && linebuf_[i-1] == ' ') break; /* * If we're to the right of a hyphen, we can break here. However, * don't break in the middle of a set of consecutive hyphens * (i.e., we don't want to break up "--" sequences). */ if (i > 0 && linebuf_[i-1] == '-' && linebuf_[i] != '-') break; } /* check to see if we found a good place to break */ if (i < 0) { /* * We didn't find a good place to break. If the underlying * console allows overrunning the line width, simply add the * character, even though it overflows; otherwise, force a break * at the line width, even though it doesn't occur at a natural * breaking point. * * In any case, don't let our buffer fill up beyond its maximum * size. */ if (!console_->allow_overrun() || linepos_ + 1 >= OS_MAXWIDTH) { /* * we didn't find any good place to break, and the console * doesn't allow us to overrun the terminal width - flush the * entire line as-is, breaking arbitrarily in the middle of a * word */ flush_line(vmg_ FALSE); /* * we've completely cleared out the line buffer, so reset all * of the line buffer counters */ linepos_ = 0; linecol_ = 0; linebuf_[0] = '\0'; is_continuation_ = FALSE; } } else { wchar_t tmpbuf[OS_MAXWIDTH]; vmcon_color_t tmpcolor[OS_MAXWIDTH]; unsigned char tmpflags[OS_MAXWIDTH]; size_t tmpchars; int nxti; /* null-terminate the line buffer */ linebuf_[linepos_] = '\0'; /* trim off leading spaces on the next line after the break */ for (nxti = i ; linebuf_[nxti] == ' ' ; ++nxti) ; /* * The next line starts after the break - save a copy. We actually * have to save a copy of the trailing material outside the buffer, * since we might have to overwrite the trailing part of the buffer * to expand tabs. */ tmpchars = wcslen(&linebuf_[nxti]); memcpy(tmpbuf, &linebuf_[nxti], tmpchars*sizeof(tmpbuf[0])); memcpy(tmpcolor, &colorbuf_[nxti], tmpchars*sizeof(tmpcolor[0])); memcpy(tmpflags, &flagbuf_[nxti], tmpchars*sizeof(tmpflags[0])); /* if we're breaking at a soft hyphen, insert a real hyphen */ if (shy) linebuf_[i++] = '-'; /* trim off trailing spaces */ for ( ; i > 0 && linebuf_[i-1] == ' ' ; --i) { /* stop if we've reached a non-breaking point */ if ((flagbuf_[i-1] & VMCON_OBF_NOBREAK) != 0) break; } /* terminate the buffer after the break point */ linebuf_[i] = '\0'; /* write out everything up to the break point */ flush_line(vmg_ TRUE); /* move the saved start of the next line into the line buffer */ memcpy(linebuf_, tmpbuf, tmpchars*sizeof(tmpbuf[0])); memcpy(colorbuf_, tmpcolor, tmpchars*sizeof(tmpcolor[0])); memcpy(flagbuf_, tmpflags, tmpchars*sizeof(tmpflags[0])); linecol_ = linepos_ = tmpchars; } done_with_wrapping: /* add the new character to buffer */ buffer_rendered(c, cflags, cwid); } /* * Write a rendered character to an output stream buffer. This is a * low-level internal routine that we call from buffer_expchar() to put * the final rendition of a character into a buffer. * * Some characters render as multiple copies of a single character; 'wid' * gives the number of copies to store. The caller is responsible for * ensuring that the rendered representation fits in the buffer and in the * available line width. */ void CVmFormatter::buffer_rendered(wchar_t c, unsigned char flags, int wid) { unsigned char flags_before; /* note whether or not we have a break before us */ flags_before = (linepos_ > 0 ? flagbuf_[linepos_-1] & VMCON_OBF_NOBREAK : 0); /* add the character the given number of times */ for ( ; wid != 0 ; --wid) { /* buffer the character */ linebuf_[linepos_] = c; flagbuf_[linepos_] = flags; colorbuf_[linepos_] = cur_color_; /* * if this isn't the last part of the character, carry forward any * no-break flag from the previous part of the character; this will * ensure that a no-break to the left of the sequence applies to * the entire sequence */ if (wid > 1) flagbuf_[linepos_] |= flags_before; /* advance one character in the buffer */ ++linepos_; /* adjust our column counter */ ++linecol_; } } /* ------------------------------------------------------------------------ */ /* * write out a UTF-8 string */ void CVmFormatter::buffer_string(VMG_ const char *txt) { /* write out each character in the string */ for ( ; utf8_ptr::s_getch(txt) != 0 ; txt += utf8_ptr::s_charsize(*txt)) buffer_char(vmg_ utf8_ptr::s_getch(txt)); } /* * write out a wide unicode string */ void CVmFormatter::buffer_wstring(VMG_ const wchar_t *txt) { /* write out each wide character */ for ( ; *txt != L'\0' ; ++txt) buffer_char(vmg_ *txt); } /* ------------------------------------------------------------------------ */ /* * Get the next wide unicode character in a UTF8-encoded string, and * update the string pointer and remaining length. Returns zero if no * more characters are available in the string. */ wchar_t CVmFormatter::next_wchar(const char **s, size_t *len) { wchar_t ret; size_t charsize; /* if there's nothing left, return a null terminator */ if (*len == 0) return 0; /* get this character */ ret = utf8_ptr::s_getch(*s); /* advance the string pointer and length counter */ charsize = utf8_ptr::s_charsize(**s); *len -= charsize; *s += charsize; /* render embedded null bytes as spaces */ if (ret == 0) ret = ' '; /* return the result */ return ret; } /* ------------------------------------------------------------------------ */ /* * Display a string of a given length. The text is encoded as UTF-8 * characters. */ int CVmFormatter::format_text(VMG_ const char *s, size_t slen) { /* get the first character */ wchar_t c = next_wchar(&s, &slen); /* if we have anything to show, show it */ while (c != '\0') { /* * first, process the character through our built-in text-only HTML * mini-parser, if our HTML mini-parser state indicates that we're * in the midst of parsing a tag */ if (html_parse_state_ != VMCON_HPS_NORMAL || (html_in_ignore_ && c != '&' && c != '<')) { /* run our HTML parsing until we finish the tag */ c = resume_html_parsing(vmg_ c, &s, &slen); /* proceed with the next character */ continue; } /* check for special characters */ switch(c) { case 10: /* newline */ flush(vmg_ VM_NL_NEWLINE); break; case 9: /* tab - write an ordinary every-4-columns tab */ write_tab(vmg_ 0, 4); break; case 0x000B: /* \b - blank line */ write_blank_line(vmg0_); break; case 0x000F: /* capitalize next character */ capsflag_ = TRUE; nocapsflag_ = FALSE; break; case 0x000E: /* un-capitalize next character */ nocapsflag_ = TRUE; capsflag_ = FALSE; break; case '<': case '&': /* HTML markup-start character - process it */ if (html_target_ || literal_mode_) { /* * The underlying OS renderer interprets HTML mark-up * sequences, OR we're processing all text literally; in * either case, we don't need to perform any * interpretation. Simply pass through the character as * though it were any other. */ goto normal_char; } else { /* * The underlying target does not accept HTML sequences. * It appears we're at the start of an "&" entity or a tag * sequence, so parse it, remove it, and replace it (if * possible) with a text-only equivalent. */ c = parse_html_markup(vmg_ c, &s, &slen); /* go back and process the next character */ continue; } break; case 0x0015: /* our own quoted space character */ case 0x00A0: /* non-breaking space */ case 0x00AD: /* soft hyphen */ case 0xFEFF: /* non-breaking zero-width space */ case 0x2002: /* en space */ case 0x2003: /* em space */ case 0x2004: /* three-per-em space */ case 0x2005: /* four-per-em space */ case 0x2006: /* six-per-em space */ case 0x2007: /* figure space */ case 0x2008: /* punctuation space */ case 0x2009: /* thin space */ case 0x200a: /* hair space */ case 0x200b: /* zero-width space */ /* * Special Unicode characters. For HTML targets, write these * as &# sequences - this bypasses character set translation * and ensures that the HTML parser will see them as intended. */ if (html_target_) { char buf[15]; char *p; /* * it's an HTML target - render these as &# sequences; * generate the decimal representation of 'c' (in reverse * order, hence start with the terminating null byte and * the semicolon) */ p = buf + sizeof(buf) - 1; *p-- = '\0'; *p-- = ';'; /* generate the decimal representation of 'c' */ for ( ; c != 0 ; c /= 10) *p-- = (c % 10) + '0'; /* add the '&#' sequence */ *p-- = '#'; *p = '&'; /* write out the sequence */ buffer_string(vmg_ p); } else { /* for non-HTML targets, treat these as normal */ goto normal_char; } break; default: normal_char: /* normal character - write it out */ buffer_char(vmg_ c); break; } /* move on to the next character */ c = next_wchar(&s, &slen); } /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Initialize the display object */ CVmConsole::CVmConsole() { /* no script file yet */ script_sp_ = 0; /* no command log file yet */ command_fp_ = 0; command_nf_ = 0; command_glob_ = 0; /* assume we'll double-space after each period */ doublespace_ = TRUE; /* presume we'll have no log stream */ log_str_ = 0; log_enabled_ = FALSE; } /* * Delete the display object */ void CVmConsole::delete_obj(VMG0_) { /* close any active script file(s) */ while (script_sp_ != 0) close_script_file(vmg0_); /* close any active command log file */ close_command_log(vmg0_); /* delete the log stream if we have one */ if (log_str_ != 0) log_str_->delete_obj(vmg0_); /* delete this object */ delete this; } /* ------------------------------------------------------------------------ */ /* * Display a string of a given byte length */ int CVmConsole::format_text(VMG_ const char *p, size_t len) { /* display the string */ disp_str_->format_text(vmg_ p, len); /* if there's a log file, write to the log file as well */ if (log_enabled_) log_str_->format_text(vmg_ p, len); /* indicate success */ return 0; } /* * Display a string on the log stream only */ int CVmConsole::format_text_to_log(VMG_ const char *p, size_t len) { /* if there's a log file, write to it; otherwise ignore the whole thing */ if (log_enabled_) log_str_->format_text(vmg_ p, len); /* indicate success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Set the text color */ void CVmConsole::set_text_color(VMG_ os_color_t fg, os_color_t bg) { /* set the color in our main display stream */ disp_str_->set_text_color(vmg_ fg, bg); } /* * Set the body color */ void CVmConsole::set_body_color(VMG_ os_color_t color) { /* set the color in the main display stream */ disp_str_->set_os_body_color(color); } /* ------------------------------------------------------------------------ */ /* * Display a blank line */ void CVmConsole::write_blank_line(VMG0_) { /* generate the newline to the standard display */ disp_str_->write_blank_line(vmg0_); /* if we're logging, generate the newline to the log file as well */ if (log_enabled_) log_str_->write_blank_line(vmg0_); } /* ------------------------------------------------------------------------ */ /* * outcaps() - sets an internal flag which makes the next letter output * a capital, whether it came in that way or not. Set the same state in * both formatters (standard and log). */ void CVmConsole::caps() { disp_str_->caps(); if (log_enabled_) log_str_->caps(); } /* * outnocaps() - sets the next letter to a miniscule, whether it came in * that way or not. */ void CVmConsole::nocaps() { disp_str_->nocaps(); if (log_enabled_) log_str_->nocaps(); } /* * obey_whitespace() - sets the obey-whitespace mode */ int CVmConsole::set_obey_whitespace(int f) { int ret; /* note the original display stream status */ ret = disp_str_->get_obey_whitespace(); /* set the stream status */ disp_str_->set_obey_whitespace(f); if (log_enabled_) log_str_->set_obey_whitespace(f); /* return the original status of the display stream */ return ret; } /* ------------------------------------------------------------------------ */ /* * Open a log file */ int CVmConsole::open_log_file(VMG_ const char *fname) { /* pass the file to the log stream, if we have one */ return log_str_ != 0 ? log_str_->open_log_file(vmg_ fname) : 1; } int CVmConsole::open_log_file(VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc) { /* pass the file to the log stream, if we have one */ return log_str_ != 0 ? log_str_->open_log_file(vmg_ filespec, rc) : 1; } /* * Close the log file */ int CVmConsole::close_log_file(VMG0_) { /* if there's no log stream, there's obviously no file open */ if (log_str_ == 0) return 1; /* tell the log stream to close its file */ return log_str_->close_log_file(vmg0_); } #if 0 //$$$ /* * This code is currently unused. However, I'm leaving it in for now - * the algorithm takes a little thought, so it would be nicer to be able * to uncomment the existing code should we ever need it in the future. */ /* ------------------------------------------------------------------------ */ /* * Write UTF-8 text explicitly to the log file. This can be used to add * special text (such as prompt text) that would normally be suppressed * from the log file. When more mode is turned off, we don't * automatically copy text to the log file; any text that the caller * knows should be in the log file during times when more mode is turned * off can be explicitly added with this function. * * If nl is true, we'll add a newline at the end of this text. The * caller should not include any newlines in the text being displayed * here. */ void CVmConsole::write_to_logfile(VMG_ const char *txt, int nl) { /* if there's no log file, there's nothing to do */ if (logfp_ == 0) return; /* write the text in the log file character set */ write_to_file(logfp_, txt, G_cmap_to_log); /* add a newline if desired */ if (nl) { /* add a normal newline */ os_fprintz(logfp_, "\n"); /* if the logfile is an html target, write an HTML line break */ if (log_str_ != 0 && log_str_->is_html_target()) os_fprintz(logfp_, "
\n"); } /* flush the output */ osfflush(logfp_); } /* * Write text to a file in the given character set */ void CVmConsole::write_to_file(osfildef *fp, const char *txt, CCharmapToLocal *map) { size_t txtlen = strlen(txt); /* * convert the text from UTF-8 to the local character set and write the * converted text to the log file */ while (txtlen != 0) { char local_buf[128]; size_t src_bytes_used; size_t out_bytes; /* convert as much as we can (leaving room for a null terminator) */ out_bytes = map->map_utf8(local_buf, sizeof(local_buf), txt, txtlen, &src_bytes_used); /* null-terminate the result */ local_buf[out_bytes] = '\0'; /* write the converted text */ os_fprintz(fp, local_buf); /* skip the text we were able to convert */ txt += src_bytes_used; txtlen -= src_bytes_used; } } #endif /* 0 */ /* ------------------------------------------------------------------------ */ /* * Reset the MORE line counter. This should be called whenever user * input is read, since stopping to read user input makes it unnecessary * to show another MORE prompt until the point at which input was * solicited scrolls off the screen. */ void CVmConsole::reset_line_count(int clearing) { /* reset the MORE counter in the display stream */ disp_str_->reset_line_count(clearing); } /* ------------------------------------------------------------------------ */ /* * Flush the output line. We'll write to both the standard display and * the log file, as needed. */ void CVmConsole::flush(VMG_ vm_nl_type nl) { /* flush the display stream */ disp_str_->flush(vmg_ nl); /* flush the log stream, if we have an open log file */ if (log_enabled_) log_str_->flush(vmg_ nl); } /* ------------------------------------------------------------------------ */ /* * Clear our buffers */ void CVmConsole::empty_buffers(VMG0_) { /* tell the formatter to clear its buffer */ disp_str_->empty_buffers(vmg0_); /* same with the log stream, if applicable */ if (log_enabled_) log_str_->empty_buffers(vmg0_); } /* ------------------------------------------------------------------------ */ /* * Immediately update the display */ void CVmConsole::update_display(VMG0_) { /* update the display for the main display stream */ disp_str_->update_display(vmg0_); } /* ------------------------------------------------------------------------ */ /* * Open a script file */ int CVmConsole::open_script_file(VMG_ const char *fname, int quiet, int script_more_mode) { CVmNetFile *nf = 0; err_try { /* create the network file descriptor */ nf = CVmNetFile::open( vmg_ fname, 0, NETF_READ, OSFTCMD, "text/plain"); } err_catch_disc { /* failed - no network file */ nf = 0; } err_end; /* open the script on the network file */ return open_script_file(vmg_ nf, 0, quiet, script_more_mode); } int CVmConsole::open_script_file(VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc, int quiet, int script_more_mode) { CVmNetFile *nf = 0; err_try { /* create the network file descriptor */ nf = CVmNetFile::open( vmg_ filespec, rc, NETF_READ, OSFTCMD, "text/plain"); /* validate file safety */ CVmObjFile::check_safety_for_open(vmg_ nf, VMOBJFILE_ACCESS_READ); } err_catch_disc { /* if we got a file, it must be a safety exception - rethrow it */ if (nf != 0) { nf->abandon(vmg0_); err_rethrow(); } } err_end; /* open the script on the network file */ return open_script_file(vmg_ nf, filespec, quiet, script_more_mode); } int CVmConsole::open_script_file(VMG_ CVmNetFile *nf, const vm_val_t *filespec, int quiet, int script_more_mode) { int evt; char buf[50]; /* if the network file open failed, return an error */ if (nf == 0) return 1; /* open the local file */ osfildef *fp = osfoprt(nf->lclfname, OSFTCMD); /* if that failed, silently ignore the request */ if (fp == 0) { /* abandon the network file descriptor and return failure */ nf->abandon(vmg0_); return 1; } /* read the first line to see if it looks like an event script */ if (osfgets(buf, sizeof(buf), fp) != 0 && strcmp(buf, "\n") == 0) { /* remember that it's an event script */ evt = TRUE; } else { /* * it's not an event script, so it must be a regular command-line * script - rewind it so we read the first line again as a regular * input line */ evt = FALSE; osfseek(fp, 0, OSFSK_SET); } /* if there's an enclosing script, inherit its modes */ if (script_sp_ != 0) { /* * if the enclosing script is quiet, force the nested script to be * quiet as well */ if (script_sp_->quiet) quiet = TRUE; /* * if the enclosing script is nonstop, force the nested script to * be nonstop as well */ if (!script_sp_->more_mode) script_more_mode = FALSE; } /* push the new script file onto the stack */ script_sp_ = new script_stack_entry( vmg_ script_sp_, set_more_state(script_more_mode), nf, fp, filespec, script_more_mode, quiet, evt); /* turn on NONSTOP mode in the OS layer if applicable */ if (!script_more_mode) os_nonstop_mode(TRUE); /* success */ return 0; } /* * Close the current script file */ int CVmConsole::close_script_file(VMG0_) { script_stack_entry *e; /* if we have a file, close it */ if ((e = script_sp_) != 0) { int ret; /* close the file */ osfcls(e->fp); err_try { /* close the network file */ e->netfile->close(vmg0_); } err_catch_disc { /* * Ignore any error - since we're reading the file, the chances * of anything going wrong are pretty minimal anyway; but even * if an error did occur, our interface just isn't set up to do * anything meaningful with it. */ } err_end; /* pop the stack */ script_sp_ = e->enc; /* restore the enclosing level's MORE mode */ os_nonstop_mode(!e->old_more_mode); /* * return the MORE mode in effect before we started reading the * script file */ ret = e->old_more_mode; /* delete the stack level */ e->delobj(vmg0_); /* return the result */ return ret; } else { /* * there's no script file - just return the current MORE mode, * since we're not making any changes */ return is_more_mode(); } } /* ------------------------------------------------------------------------ */ /* * Script stack element */ script_stack_entry::script_stack_entry( VMG_ script_stack_entry *encp, int old_more, class CVmNetFile *netfile, osfildef *outfp, const vm_val_t *filespec, int new_more, int is_quiet, int is_event_script) { /* remember the stack level settings */ this->enc = encp; this->old_more_mode = old_more; this->netfile = netfile; this->fp = outfp; this->more_mode = new_more; this->quiet = is_quiet; this->event_script = is_event_script; /* if we have a file spec, create a global for it for gc protection */ this->filespec = 0; if (filespec != 0) { this->filespec = G_obj_table->create_global_var(); this->filespec->val = *filespec; } } void script_stack_entry::delobj(VMG0_) { /* we're done with our file spec global, if we had one */ if (filespec != 0) G_obj_table->delete_global_var(filespec); /* delete myself */ delete this; } /* ------------------------------------------------------------------------ */ /* * Open a command log file */ int CVmConsole::open_command_log(VMG_ const char *fname, int event_script) { CVmNetFile *nf = 0; err_try { /* create the network file descriptor */ nf = CVmNetFile::open(vmg_ fname, 0, NETF_NEW, OSFTCMD, "text/plain"); } err_catch_disc { /* failed - no network file */ nf = 0; } err_end; /* create the command log */ return open_command_log(vmg_ nf, event_script); } int CVmConsole::open_command_log(VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc, int event_script) { CVmNetFile *nf = 0; err_try { /* create the network file descriptor */ nf = CVmNetFile::open(vmg_ filespec, rc, NETF_NEW, OSFTCMD, "text/plain"); /* validate file safety */ CVmObjFile::check_safety_for_open(vmg_ nf, VMOBJFILE_ACCESS_WRITE); } err_catch_disc { /* if we got a file, it must be a safety exception - rethrow it */ if (nf != 0) { nf->abandon(vmg0_); err_rethrow(); } } err_end; /* create the command log */ int err = open_command_log(vmg_ nf, event_script); /* if that succeeded, save the filespec in our VM global */ if (!err) { /* create our VM global if we don't have one already */ if (command_glob_ == 0) command_glob_ = G_obj_table->create_global_var(); /* remember the file spec */ command_glob_->val = *filespec; } /* return the result */ return err; } int CVmConsole::open_command_log(VMG_ CVmNetFile *nf, int event_script) { /* close any existing command log file */ close_command_log(vmg0_); /* if the network file open failed, return failure */ if (nf == 0) return 1; /* open the file */ osfildef *fp = osfopwt(nf->lclfname, OSFTCMD); if (fp != 0) { /* success - remember the file handle and net descriptor */ command_nf_ = nf; command_fp_ = new CVmFileSource(fp); } else { /* failed - abandon the network file */ nf->abandon(vmg0_); } /* note the type */ command_eventscript_ = event_script; /* if it's an event script, write the file type tag */ if (event_script && command_fp_ != 0) { command_fp_->writez("\n"); command_fp_->flush(); } /* return success if we successfully opened the file */ return (command_fp_ == 0); } /* * close the active command log file */ int CVmConsole::close_command_log(VMG0_) { /* if there's a command log file, close it */ if (command_fp_ != 0) { /* close the file */ delete command_fp_; /* close the network file */ err_try { command_nf_->close(vmg0_); } err_catch_disc { /* * ignore any errors - our interface doesn't give us any * meaningful way to return error information */ } err_end; /* forget the file */ command_fp_ = 0; } /* clear out our file spec global, if we have one */ if (command_glob_ != 0) command_glob_->val.set_nil(); /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Read a line of input from the console. Fills in the buffer with a * null-terminated string in the UTF-8 character set. Returns zero on * success, non-zero on end-of-file. */ int CVmConsole::read_line(VMG_ char *buf, size_t buflen, int bypass_script) { /* cancel any previous interrupted input */ read_line_cancel(vmg_ TRUE); try_again: /* use the timeout version, with no timeout specified */ switch(read_line_timeout(vmg_ buf, buflen, 0, FALSE, bypass_script)) { case OS_EVT_LINE: /* success */ return 0; case VMCON_EVT_END_QUIET_SCRIPT: /* * end of script - we have no way to communicate this result back * to our caller, so simply ignore the result and ask for another * line */ goto try_again; default: /* anything else is an error */ return 1; } } /* ------------------------------------------------------------------------ */ /* * translate an OS_EVT_XXX code to an event script file tag */ static const char *evt_to_tag(int evt) { switch (evt) { case OS_EVT_KEY: return "key"; case OS_EVT_TIMEOUT: return "timeout"; case OS_EVT_HREF: return "href"; case OS_EVT_NOTIMEOUT: return "notimeout"; case OS_EVT_EOF: return "eof"; case OS_EVT_LINE: return "line"; case OS_EVT_COMMAND: return "command"; case VMCON_EVT_END_QUIET_SCRIPT: return "endqs"; case VMCON_EVT_DIALOG: return "dialog"; case VMCON_EVT_FILE: return "file"; default: return ""; } } /* ------------------------------------------------------------------------ */ /* * Log an event to the output script. The parameter is in the UI character * set. */ int CVmConsole::log_event(VMG_ int evt, const char *param, size_t paramlen, int param_is_utf8) { /* translate the event code to a tag, and log the event */ log_event(vmg_ evt_to_tag(evt), param, paramlen, param_is_utf8); /* return the event type code */ return evt; } void CVmConsole::log_event(VMG_ const char *tag, const char *param, size_t paramlen, int param_is_utf8) { /* if there's a script file, log the event */ if (command_fp_ != 0) { /* if the tag has < > delimiters, remove them */ size_t taglen = strlen(tag); if (tag[0] == '<') ++tag, --taglen; if (taglen != 0 && tag[taglen-1] == '>') --taglen; /* write the event in the proper format for the script type */ if (command_eventscript_) { /* * It's an event script, so we write all event types. Check * for certain special parameter translations. */ if (taglen == 3 && memicmp(tag, "key", 3) == 0) { /* * key event - for characters that would look like * whitespace characters if we wrote them as-is, translate * to [xxx] key names */ if (param != 0 && paramlen == 1) { switch (param[0]) { case '\n': param = "[enter]"; paramlen = 7; break; case '\t': param = "[tab]"; paramlen = 5; break; case ' ': param = "[space]"; paramlen = 7; break; } } } /* * if the event doesn't have parameters, ignore any parameter * provided by the caller */ if ((taglen == 9 && memicmp(tag, "notimeout", 9) == 0) || (taglen == 3 && memicmp(tag, "eof", 3) == 0)) param = 0; /* if we have a non-empty tag, write the event */ if (taglen != 0) { /* write the tag, in the local character set */ G_cmap_to_ui->write_file(command_fp_, "<", 1); G_cmap_to_ui->write_file(command_fp_, tag, strlen(tag)); G_cmap_to_ui->write_file(command_fp_, ">", 1); /* add the parameter, if present */ if (param != 0) { if (param_is_utf8) G_cmap_to_ui->write_file(command_fp_, param, paramlen); else command_fp_->write(param, paramlen); } /* add the newline */ G_cmap_to_ui->write_file(command_fp_, "\n", 1); /* flush the output */ command_fp_->flush(); } } else { /* * It's a plain old command-line script. If the event is an * input-line event, record it; otherwise leave it out, as this * script file format can't represent any other event types. */ if (taglen == 4 && memicmp(tag, "line", 4) == 0 && param != 0) { /* write the ">" prefix */ G_cmap_to_ui->write_file(command_fp_, ">", 1); /* add the command line */ if (param_is_utf8) G_cmap_to_ui->write_file(command_fp_, param, paramlen); else command_fp_->write(param, paramlen); /* add the newline */ G_cmap_to_ui->write_file(command_fp_, "\n", 1); /* flush the output */ command_fp_->flush(); } } } } /* ------------------------------------------------------------------------ */ /* * Static variables for input state. We keep these statically, because we * might need to use the values across a series of read_line_timeout calls * if timeouts occur. */ /* original 'more' mode, before input began */ static int S_old_more_mode; /* flag: input is pending from an interrupted read_line_timeout invocation */ static int S_read_in_progress; /* local buffer for reading input lines */ static char S_read_buf[256]; /* * Read a line of input from the console, with an optional timeout value. */ int CVmConsole::read_line_timeout(VMG_ char *buf, size_t buflen, unsigned long timeout, int use_timeout, int bypass_script) { /* no event yet */ int evt = OS_EVT_NONE; /* we haven't received any script input yet */ int got_script_input = FALSE; /* * presume we won't echo the text to the display; in most cases, it * will be echoed to the display in the course of reading it from * the keyboard */ int echo_text = FALSE; /* remember the initial MORE mode */ S_old_more_mode = is_more_mode(); /* * If we're not resuming an interrupted read already in progress, * initialize some display settings. */ if (!S_read_in_progress) { /* * Turn off MORE mode if it's on - we don't want a MORE prompt * showing up in the midst of user input. */ S_old_more_mode = set_more_state(FALSE); /* * flush the output; don't start a new line, since we might have * displayed a prompt that is to be on the same line with the user * input */ flush_all(vmg_ VM_NL_INPUT); } /* * if there's a script file, read from it, unless the caller has * specifically asked us to bypass it */ if (script_sp_ != 0 && !bypass_script) { read_script: /* note whether we're in quiet mode */ int was_quiet = script_sp_->quiet; /* try reading a line from the script file */ if (read_line_from_script(S_read_buf, sizeof(S_read_buf), &evt)) { /* we successfully read input from the script */ got_script_input = TRUE; /* * if we're not in quiet mode, make a note to echo the text to * the display */ if (!script_sp_->quiet) echo_text = TRUE; } else { /* * End of script file - return to reading from the enclosing * level (i.e., the enclosing script, or the keyboard if this * is the outermost script). The return value from * close_script_file() is the MORE mode that was in effect * before we started reading the script file; we'll use this * when we restore the enclosing MORE mode so that we restore * the pre-script MORE mode when we return. */ S_old_more_mode = close_script_file(vmg0_); /* note the new 'quiet' mode */ int is_quiet = (script_sp_ != 0 && script_sp_->quiet); /* * if we're still reading from a script (which means we closed * the old script and popped out to an enclosing script), and * the 'quiet' mode hasn't changed, simply go back for another * read */ if (script_sp_ != 0 && is_quiet == was_quiet) goto read_script; /* * temporarily turn off MORE mode, in case we read from the * keyboard */ set_more_state(FALSE); /* flush any output we generated while reading the script */ flush(vmg_ VM_NL_NONE); /* * If we were in quiet mode but no longer are, let the caller * know we've finished reading a script, so that the caller can * set up the display properly for reading from the keyboard. * * If we weren't in quiet mode, we'll simply proceed to the * normal keyboard reading; when not in quiet mode, no special * display fixup is needed. */ if (was_quiet && !is_quiet) { /* return to the old MORE mode */ set_more_state(S_old_more_mode); /* add a blank line to the log file, if necessary */ if (log_enabled_) log_str_->print_to_os("\n"); /* note in the streams that we've read an input line */ disp_str_->note_input_line(); if (log_str_ != 0) log_str_->note_input_line(); /* * generate a synthetic "end of script" event to let the * caller know we're switching back to regular keyboard * reading */ return log_event(vmg_ VMCON_EVT_END_QUIET_SCRIPT); } /* * Note that we do not have an event yet - we've merely closed * the script file, and now we're going to continue by reading * a line from the keyboard instead. The call to * close_script_file() above will have left script_sp_ == 0, so * we'll shortly read an event from the keyboard. Thus 'evt' * is still not set to any value, because we do not yet have an * event - this is intentional. */ } } /* * if we're not reading from a scripot, reset the MORE line counter, * since we're reading user input at the current point and shouldn't * pause for a MORE prompt until the text we're reading has scrolled * off the screen */ if (script_sp_ == 0) reset_line_count(FALSE); /* reading is now in progress */ S_read_in_progress = TRUE; /* if we didn't get input from a script, read from the keyboard */ if (!got_script_input) { /* * If we're in network mode, return EOF to indicate that no console * input is available. Network programs can only call this routine * to read script input, and aren't allowed to read from the * regular keyboard. */ if (G_net_config != 0) { read_line_done(vmg0_); return OS_EVT_EOF; } /* read a line from the keyboard */ evt = os_gets_timeout((uchar *)S_read_buf, sizeof(S_read_buf), timeout, use_timeout); /* * If that failed because timeout is not supported on this * platform, and the caller didn't actually want to use a timeout, * try again with an ordinary os_gets(). If they wanted to use a * timeout, simply return the NOTIMEOUT indication to our caller. */ if (evt == OS_EVT_NOTIMEOUT && !use_timeout) { /* perform an ordinary untimed input */ if (os_gets((uchar *)S_read_buf, sizeof(S_read_buf)) != 0) { /* success */ evt = OS_EVT_LINE; } else { /* error reading input */ evt = OS_EVT_EOF; } } /* * If we actually read a line, notify the display stream that we * read text from the console - it might need to make some * internal bookkeeping adjustments to account for the fact that * we moved the write position around on the display. * * Don't note the input if we timed out, since we haven't finished * reading the line yet in this case. */ if (evt == OS_EVT_LINE) { disp_str_->note_input_line(); if (log_str_ != 0) log_str_->note_input_line(); } } /* if we got an error, return it */ if (evt == OS_EVT_EOF) { set_more_state(S_old_more_mode); read_line_done(vmg0_); return log_event(vmg_ evt); } /* * Convert the text from the local UI character set to UTF-8. Reserve * space in the output buffer for the null terminator. */ char *outp = buf; size_t outlen = buflen - 1; G_cmap_from_ui->map(&outp, &outlen, S_read_buf, strlen(S_read_buf)); /* add the null terminator */ *outp = '\0'; /* * If we need to echo the text (because we read it from a script file), * do so now. Never echo text in the network configuration, since we * don't use the local console UI in this mode. */ if (echo_text && G_net_config == 0) { /* show the text */ format_text(vmg_ buf); /* add a newline */ format_text(vmg_ "\n"); } /* if we finished reading the line, do our line-finishing work */ if (evt == OS_EVT_LINE) read_line_done(vmg0_); /* * Log and return the event. Note that we log events in the UI * character set, so we want to simply use the original, untranslated * input buffer. */ return log_event(vmg_ evt, S_read_buf, strlen(S_read_buf), FALSE); } /* * Cancel an interrupted input. */ void CVmConsole::read_line_cancel(VMG_ int reset) { /* reset the underling OS layer */ os_gets_cancel(reset); /* do our line-ending work */ read_line_done(vmg0_); } /* * Perform line-ending work. This is used when we finish reading a line * in read_line_timeout(), or when we cancel an interrupted line, thus * finishing the line, in read_line_cancel(). */ void CVmConsole::read_line_done(VMG0_) { /* if we have a line in progress, finish it off */ if (S_read_in_progress) { /* set the original 'more' mode */ set_more_state(S_old_more_mode); /* * Write the input line, followed by a newline, to the log file. * Note that the text is still in the local character set, so we * can write it directly to the log file. * * If we're reading from a script file in "echo" mode, skip this. * When reading from a script file in "echo" mode, we will manually * copy the input commands to the main console, which will * automatically copy to the main log file. If we're in quiet * scripting mode, though, we won't do that, so we do need to * capture the input explicitly here. */ if (log_enabled_ && (script_sp_ == 0 || script_sp_->quiet)) { log_str_->print_to_os(S_read_buf); log_str_->print_to_os("\n"); } /* note in the streams that we've read an input line */ disp_str_->note_input_line(); if (log_str_ != 0) log_str_->note_input_line(); /* clear the in-progress flag */ S_read_in_progress = FALSE; } } /* ------------------------------------------------------------------------ */ /* * Read an input event from the script file. If we're reading an event * script file, we'll read the next event and return TRUE; if we're not * reading a script file, or the script file is a command-line script * rather than an event script, we'll simply return FALSE. * * If the event takes a parameter, we'll read the parameter into 'buf'. * The value is returned in the local character set, so the caller will * need to translate it to UTF-8. * * If 'filter' is non-null, we'll only return events of the types in the * filter list. */ int CVmConsole::read_event_script(VMG_ int *evt, char *buf, size_t buflen, const int *filter, int filter_cnt, unsigned long *attrs) { /* * if we're not reading a script, or it's not an event script, skip * this */ if (script_sp_ == 0 || !script_sp_->event_script) return FALSE; /* get the script file */ osfildef *fp = script_sp_->fp; /* keep going until we find something */ for (;;) { /* read the next event */ if (!read_script_event_type(evt, attrs)) { /* end of the script - close it */ set_more_state(close_script_file(vmg0_)); /* if there's no more script file, there's no event */ if (script_sp_ == 0) return FALSE; /* go back for the next event */ fp = script_sp_->fp; continue; } /* if it's not in the filter list, skip it */ if (filter != 0) { int i, found; /* look for a match in our filter list */ for (i = 0, found = FALSE ; i < filter_cnt ; ++i) { if (filter[i] == *evt) { found = TRUE; break; } } /* if we didn't find it, skip this line */ if (!found) { skip_script_line(fp); continue; } } /* if there's a buffer, read the rest of the line */ if (buf != 0) { /* read the parameter into the buffer, and return the result */ if (!read_script_param(buf, buflen, fp)) return FALSE; /* if this is an OS_EVT_KEY event, translate special keys */ if (*evt == OS_EVT_KEY) { if (strcmp(buf, "[enter]") == 0) strcpy(buf, "\n"); else if (strcmp(buf, "[tab]") == 0) strcpy(buf, "\t"); else if (strcmp(buf, "[space]") == 0) strcpy(buf, " "); } } else { /* no result buffer - just skip anything left on the line */ skip_script_line(fp); } /* success */ return TRUE; } } /* * Read a or attribute token from a script file. Returns the * character after the end of the token. */ static int read_script_token(char *buf, size_t buflen, osfildef *fp) { char *p; int c; /* skip leading whitespace */ for (c = osfgetc(fp) ; isspace(c) ; c = osfgetc(fp)) ; /* read from the file until we reach the end of the token */ for (p = buf ; p < buf + buflen - 1 && c != '>' && !isspace(c) && c != '\n' && c != '\r' && c != EOF ; ) { /* store this character */ *p++ = (char)c; /* get the next one */ c = osfgetc(fp); } /* null-terminate the token */ *p = '\0'; /* return the character that ended the token */ return c; } /* * Read the next event type from current event script file. This leaves * the file positioned at the parameter data for the event, if any. * Returns FALSE if we reach end of file without finding an event. */ int CVmConsole::read_script_event_type(int *evt, unsigned long *attrs) { /* clear the caller's attribute flags, if provided */ if (attrs != 0) *attrs = 0; /* if there's no script, there's no event */ if (script_sp_ == 0) return FALSE; /* get the file */ osfildef *fp = script_sp_->fp; /* if it's a command-line script, there are only line input events */ if (!script_sp_->event_script) { /* keep going until we find an input line */ for (;;) { /* read the first character of the line */ int c = osfgetc(fp); if (c == '>') { /* we found a line input event */ *evt = OS_EVT_LINE; return TRUE; } else if (c == EOF) { /* end of file - give up */ return FALSE; } else { /* * anything else is just a comment line - just skip it and * keep looking */ skip_script_line(fp); } } } /* keep going until we find an event tag */ for (;;) { int c; char tag[32]; static const struct { const char *tag; int evt; } *tp, tags[] = { { "key", OS_EVT_KEY }, { "timeout", OS_EVT_TIMEOUT }, { "notimeout", OS_EVT_NOTIMEOUT }, { "eof", OS_EVT_EOF }, { "line", OS_EVT_LINE }, { "command", OS_EVT_COMMAND }, { "endqs", VMCON_EVT_END_QUIET_SCRIPT }, { "dialog", VMCON_EVT_DIALOG }, { "file", VMCON_EVT_FILE }, { 0, 0 } }; /* check the start of the line to make sure it's an event */ c = osfgetc(fp); /* if at EOF, return failure */ if (c == EOF) return FALSE; /* if it's not an event code line, skip the line */ if (c != '<') { skip_script_line(fp); continue; } /* read the event type (up to the '>') */ c = read_script_token(tag, sizeof(tag), fp); /* check for attributes */ while (isspace(c)) { char attr[32]; static const struct { const char *name; unsigned long flag; } *ap, attrlist[] = { { "overwrite", VMCON_EVTATTR_OVERWRITE }, { 0, 0 } }; /* read the attribute name */ c = read_script_token(attr, sizeof(attr), fp); /* if the name is empty, stop */ if (attr[0] == '\0') break; /* look up the token */ for (ap = attrlist ; ap->name != 0 ; ++ap) { /* check for a match */ if (stricmp(attr, ap->name) == 0) { /* if the caller wants the flag, set it */ if (attrs != 0) *attrs |= ap->flag; /* no need to look any further */ break; } } /* if we're at the '>' or at end of line or file, stop */ if (c == '>' || c == '\n' || c == '\r' || c == EOF) break; } /* if it's not a well-formed tag, ignore it */ if (c != '>') { skip_script_line(fp); continue; } /* look up the tag */ for (tp = tags ; tp->tag != 0 ; ++tp) { /* check for a match to this tag name */ if (stricmp(tp->tag, tag) == 0) { /* got it - return the event type */ *evt = tp->evt; return TRUE; } } /* we don't recognize the tag name; skip the line and keep looking */ skip_script_line(fp); } } /* * Skip to the next script line */ void CVmConsole::skip_script_line(osfildef *fp) { int c; /* read until we find the end of the current line */ for (c = osfgetc(fp) ; c != EOF && c != '\n' && c != '\r' ; c = osfgetc(fp)) ; } /* * read the rest of the current script line into the given buffer */ int CVmConsole::read_script_param(char *buf, size_t buflen, osfildef *fp) { /* ignore zero-size buffer requests */ if (buflen == 0) return FALSE; /* read characters until we run out of buffer or reach a newline */ for (;;) { /* get the next character */ int c = osfgetc(fp); /* if it's a newline or end of file, we're done */ if (c == '\n' || c == EOF) { /* null-terminate the buffer */ *buf = '\0'; /* indicate success */ return TRUE; } /* * if there's room in the buffer, add the character - always leave * one byte for the null terminator */ if (buflen > 1) { *buf++ = (char)c; --buflen; } } } /* * Read a line of text from the script file, if there is one. Returns TRUE * on success, FALSE if we reach the end of the script file or encounter * any other error. */ int CVmConsole::read_line_from_script(char *buf, size_t buflen, int *evt) { /* if there's no script file, return failure */ if (script_sp_ == 0) return FALSE; /* get the file from the script stack */ osfildef *fp = script_sp_->fp; /* keep going until we find a line that we like */ for (;;) { /* read the script according to its type ('event' or 'line input') */ if (script_sp_->event_script) { /* read to the next event */ if (!read_script_event_type(evt, 0)) return FALSE; /* check the event code */ switch (*evt) { case OS_EVT_LINE: case OS_EVT_TIMEOUT: /* * it's one of our line input events - read the line (or * partial line, in the case of TIMEOUT) */ return read_script_param(buf, buflen, fp); default: /* * it's not our type of event - skip the rest of the line * and keep looking */ skip_script_line(fp); break; } } else { /* * We have a basic line-input script rather than an event * script. Each input line starts with a '>'; everything else * is a comment. * * Read the first character on the line. If it's not a * newline, there's more text on the same line, so read the * rest and determine what to do. */ int c = osfgetc(fp); if (c == '>') { /* it's a command line - read it */ *evt = OS_EVT_LINE; return read_script_param(buf, buflen, fp); } else if (c == EOF) { /* end of file */ return FALSE; } else if (c == '\n' || c == '\r') { /* blank line - continue on to the next line */ } else { /* it's not a command line - just skip it and keep looking */ skip_script_line(fp); } } } } /* ------------------------------------------------------------------------ */ /* * Main System Console */ /* * create */ CVmConsoleMain::CVmConsoleMain(VMG0_) { /* create the system banner manager */ banner_manager_ = new CVmBannerManager(); /* create the log console manager */ log_console_manager_ = new CVmLogConsoleManager(); /* create and initialize our display stream */ main_disp_str_ = new CVmFormatterMain(this, 256); main_disp_str_->init(); /* initially send text to the main display stream */ disp_str_ = main_disp_str_; /* * Create our log stream. The main console always has a log stream, * even when it's not in use, so that we can keep the log stream's * state synchronized with the display stream in preparation for * activation. */ log_str_ = new CVmFormatterLog(this, 80); /* * use the default log file character mapper - on some systems, files * don't use the same character set as the display */ log_str_->set_charmap(G_cmap_to_log); /* initialize the log stream */ log_str_->init(); /* * the log stream is initially enabled (this is separate from the log * file being opened; it merely indicates that we send output * operations to the log stream for processing) */ log_enabled_ = TRUE; /* we don't have a statusline formatter until asked for one */ statline_str_ = 0; /* reset statics */ S_read_in_progress = FALSE; S_read_buf[0] = '\0'; } /* * delete */ void CVmConsoleMain::delete_obj(VMG0_) { /* delete the system banner manager */ banner_manager_->delete_obj(vmg0_); /* delete the system log console manager */ log_console_manager_->delete_obj(vmg0_); /* delete the display stream */ main_disp_str_->delete_obj(vmg0_); /* delete the statusline stream, if we have one */ if (statline_str_ != 0) statline_str_->delete_obj(vmg0_); /* do the inherited work */ CVmConsole::delete_obj(vmg0_); } /* * Clear the window */ void CVmConsoleMain::clear_window(VMG0_) { /* flush and empty our output buffer */ flush(vmg_ VM_NL_NONE); empty_buffers(vmg0_); /* clear the main window */ oscls(); /* reset the MORE line counter in the display stream */ disp_str_->reset_line_count(TRUE); } /* * Set statusline mode */ void CVmConsoleMain::set_statusline_mode(VMG_ int mode) { CVmFormatterDisp *str; /* * if we're switching into statusline mode, and we don't have a * statusline stream yet, create one */ if (mode && statline_str_ == 0) { /* create and initialize the statusline stream */ statline_str_ = new CVmFormatterStatline(this); statline_str_->init(); } /* get the stream selected by the new mode */ if (mode) str = statline_str_; else str = main_disp_str_; /* if this is already the active stream, we have nothing more to do */ if (str == disp_str_) return; /* make the new stream current */ disp_str_ = str; /* * check which mode we're switching to, so we can do some extra work * specific to each mode */ if (mode) { /* * we're switching to the status line, so disable the log stream - * statusline text is never sent to the log, since the log reflects * only what was displayed in the main text area */ log_enabled_ = FALSE; } else { /* * we're switching back to the main stream, so flush the statusline * so we're sure the statusline text is displayed */ /* end the line */ statline_str_->format_text(vmg_ "\n", 1); /* flush output */ statline_str_->flush(vmg_ VM_NL_NONE); /* re-enable the log stream, if we have one */ if (log_str_ != 0) log_enabled_ = TRUE; } /* switch at the OS layer */ os_status(mode); } /* * Flush everything */ void CVmConsoleMain::flush_all(VMG_ vm_nl_type nl) { /* flush our primary console */ flush(vmg_ nl); /* * Flush each banner we're controlling. Note that we explicitly flush * the banners with newline mode 'NONE', regardless of the newline mode * passed in by the caller: the caller's mode is for the primary * console, but for the banners we just want to make sure they're * flushed out normally, since whatever we're doing in the primary * console that requires flushing doesn't concern the banners. */ banner_manager_->flush_all(vmg_ VM_NL_NONE); } /* ------------------------------------------------------------------------ */ /* * Handle manager */ /* initialize */ CVmHandleManager::CVmHandleManager() { size_t i; /* allocate an initial array of handle slots */ handles_max_ = 32; handles_ = (void **)t3malloc(handles_max_ * sizeof(*handles_)); /* all slots are initially empty */ for (i = 0 ; i < handles_max_ ; ++i) handles_[i] = 0; } /* delete the object - this is the public destructor interface */ void CVmHandleManager::delete_obj(VMG0_) { size_t i; /* * Delete each remaining object. Note that we need to call the virtual * delete_handle_object routine, so we must do this before reaching the * destructor (once in the base class destructor, we no longer have * access to the subclass virtuals). */ for (i = 0 ; i < handles_max_ ; ++i) { /* if this banner is still valid, delete it */ if (handles_[i] != 0) delete_handle_object(vmg_ i + 1, handles_[i]); } /* delete the object */ delete this; } /* destructor */ CVmHandleManager::~CVmHandleManager() { /* delete the handle pointer array */ t3free(handles_); } /* * Allocate a new handle */ int CVmHandleManager::alloc_handle(void *item) { size_t slot; /* scan for a free slot */ for (slot = 0 ; slot < handles_max_ ; ++slot) { /* if this one is free, use it */ if (handles_[slot] == 0) break; } /* if we didn't find a free slot, extend the array */ if (slot == handles_max_) { size_t i; /* allocate a larger array */ handles_max_ += 32; handles_ = (void **) t3realloc(handles_, handles_max_ * sizeof(*handles_)); /* clear out the newly-allocated slots */ for (i = slot ; i < handles_max_ ; ++i) handles_[i] = 0; } /* store the new item in our pointer array */ handles_[slot] = item; /* * convert the slot number to a handle by adjusting it to a 1-based * index, and return the result */ return slot + 1; } /* ------------------------------------------------------------------------ */ /* * Banner manager */ /* * Create a banner */ int CVmBannerManager::create_banner(VMG_ int parent_id, int where, int other_id, int wintype, int align, int siz, int siz_units, unsigned long style) { void *handle; void *parent_handle; void *other_handle; CVmConsoleBanner *item; /* get the parent handle, if provided */ parent_handle = get_os_handle(parent_id); /* get the 'other' handle, if we need it for the 'where' */ switch(where) { case OS_BANNER_BEFORE: case OS_BANNER_AFTER: /* retrieve the handle for the other_id */ other_handle = get_os_handle(other_id); break; default: /* we don't need 'other' for other 'where' modes */ other_handle = 0; break; } /* try creating the OS-level banner window */ handle = os_banner_create(parent_handle, where, other_handle, wintype, align, siz, siz_units, style); /* if we couldn't create the OS-level window, return failure */ if (handle == 0) return 0; /* create the new console */ item = new CVmConsoleBanner(handle, wintype, style); /* allocate a handle for the new banner, and return the handle */ return alloc_handle(item); } /* * Delete or orphan a banner window. Deleting and orphaning both sever * all ties from the banner manager (and thus from the T3 program) to the * banner. Deleting a banner actually gets deletes it at the OS level; * orphaning the banner severs our ties, but hands the banner over to the * OS to do with as it pleases. On some implementations, the OS will * continue to display the banner after it's orphaned to allow the final * display configuration to remain visible even after the program has * terminated. */ void CVmBannerManager::delete_or_orphan_banner(VMG_ int banner, int orphan) { CVmConsoleBanner *item; void *handle; /* if the banner is invalid, ignore the request */ if ((item = (CVmConsoleBanner *)get_object(banner)) == 0) return; /* get the OS-level banner handle */ handle = item->get_os_handle(); /* delete the banner item */ item->delete_obj(vmg0_); /* clear the slot */ clear_handle(banner); /* delete the OS-level banner */ if (orphan) os_banner_orphan(handle); else os_banner_delete(handle); } /* * Get the OS-level handle for the given banner */ void *CVmBannerManager::get_os_handle(int banner) { CVmConsoleBanner *item; /* if the banner is invalid, return failure */ if ((item = (CVmConsoleBanner *)get_object(banner)) == 0) return 0; /* return the handle from the slot */ return item->get_os_handle(); } /* * Flush all banners */ void CVmBannerManager::flush_all(VMG_ vm_nl_type nl) { size_t slot; /* flush each banner */ for (slot = 0 ; slot < handles_max_ ; ++slot) { /* if this slot has a valid banner, flush it */ if (handles_[slot] != 0) ((CVmConsoleBanner *)handles_[slot])->flush(vmg_ nl); } } /* ------------------------------------------------------------------------ */ /* * Banner Window Console */ CVmConsoleBanner::CVmConsoleBanner(void *banner_handle, int win_type, unsigned long style) { CVmFormatterBanner *str; os_banner_info_t info; int obey_whitespace = FALSE; int literal_mode = FALSE; /* remember our OS-level banner handle */ banner_ = banner_handle; /* get osifc-level information on the banner */ if (!os_banner_getinfo(banner_, &info)) info.os_line_wrap = FALSE; /* * If it's a text grid window, don't do any line wrapping. Text grids * simply don't have any line wrapping, so we don't want to impose any * at the formatter level. Set the formatter to "os line wrap" mode, * to indicate that the formatter doesn't do wrapping - even though * the underlying OS banner window won't do any wrapping either, the * lack of line wrapping counts as OS handling of line wrapping. */ if (win_type == OS_BANNER_TYPE_TEXTGRID) { /* do not wrap lines in the formatter */ info.os_line_wrap = TRUE; /* use literal mode, and obey whitespace literally */ literal_mode = TRUE; obey_whitespace = TRUE; } /* create and initialize our display stream */ disp_str_ = str = new CVmFormatterBanner(banner_handle, this, win_type, style); str->init_banner(info.os_line_wrap, obey_whitespace, literal_mode); /* remember our window type */ win_type_ = win_type; } /* * Deletion */ void CVmConsoleBanner::delete_obj(VMG0_) { /* delete our display stream */ disp_str_->delete_obj(vmg0_); /* do the inherited work */ CVmConsole::delete_obj(vmg0_); } /* * Clear the banner window */ void CVmConsoleBanner::clear_window(VMG0_) { /* flush and empty our output buffer */ flush(vmg_ VM_NL_NONE); empty_buffers(vmg0_); /* clear our underlying system banner */ os_banner_clear(banner_); /* tell our display stream to zero its line counter */ disp_str_->reset_line_count(TRUE); } /* * Get banner information */ int CVmConsoleBanner::get_banner_info(os_banner_info_t *info) { int ret; /* get the OS-level information */ ret = os_banner_getinfo(banner_, info); /* make some adjustments if we got valid information back */ if (ret) { /* * check the window type for further adjustments we might need to * make to the data returned from the OS layer */ switch(win_type_) { case OS_BANNER_TYPE_TEXTGRID: /* * text grids don't support alignment, even if the * underlying OS banner says we do, because we simply don't * support (or any other HTML markups) in a text grid * window */ info->style &= ~OS_BANNER_STYLE_TAB_ALIGN; break; default: /* other types don't require any adjustments */ break; } } /* return the success indication */ return ret; } /* ------------------------------------------------------------------------ */ /* * Log file console manager */ /* * create a log console */ int CVmLogConsoleManager::create_log_console( VMG_ CVmNetFile *nf, osfildef *fp, CCharmapToLocal *cmap, int width) { CVmConsoleLog *con; /* create the new console */ con = new CVmConsoleLog(vmg_ nf, fp, cmap, width); /* allocate a handle for the new console and return the handle */ return alloc_handle(con); } /* * delete log a console */ void CVmLogConsoleManager::delete_log_console(VMG_ int handle) { CVmConsoleLog *con; /* if the handle is invalid, ignore the request */ if ((con = (CVmConsoleLog *)get_object(handle)) == 0) return; /* delete the console */ con->delete_obj(vmg0_); /* clear the slot */ clear_handle(handle); } /* ------------------------------------------------------------------------ */ /* * Log file console */ CVmConsoleLog::CVmConsoleLog(VMG_ CVmNetFile *nf, osfildef *fp, class CCharmapToLocal *cmap, int width) { CVmFormatterLog *str; /* create our display stream */ disp_str_ = str = new CVmFormatterLog(this, width); /* set the file */ str->set_log_file(vmg_ nf, fp); /* set the character mapper */ str->set_charmap(cmap); } /* * destroy */ void CVmConsoleLog::delete_obj(VMG0_) { /* delete our display stream */ disp_str_->delete_obj(vmg0_); /* do the inherited work */ CVmConsole::delete_obj(vmg0_); } qtads-2.1.7/tads3/vmconsol.h000066400000000000000000002177431265017072300157050ustar00rootroot00000000000000/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmconsol.h - TADS 3 console input reader and output formatter Function Provides console input and output for the TADS 3 built-in function set for the T3 VM. T3 uses the UTF-8 character set to represent character strings. The OS functions use the local character set. We perform the mapping between UTF-8 and the local character set within this module, so that OS routines see local characters only, not UTF-8. This code is based on the TADS 2 output formatter, but has been substantially reworked for C++, Unicode, and the slightly different TADS 3 formatting model. Notes Modified 09/04/99 MJRoberts - Creation */ #ifndef VMCONSOL_H #define VMCONSOL_H #include #include #include "wchar.h" #include "os.h" #include "t3std.h" #include "vmtype.h" #include "vmglob.h" /* ------------------------------------------------------------------------ */ /* * Synthetic console events. These are events we generate alongside * OS_EVT_xxx codes; we start these at 10000 to ensure that we don't * overlap any current or future OS_EVT_xxx event codes. */ #define VMCON_EVT_END_QUIET_SCRIPT 10000 /* end quiet script playback */ #define VMCON_EVT_DIALOG 10001 /* result from dialog */ #define VMCON_EVT_FILE 10002 /* result from file dialog */ /* * Event attributes. Some scriptable events have extra attributes * associated with them in addition to the main payload. These attributes * take the syntactic form of HTML tag attributes, so in principal we could * have arbitrarily many attributes, each of which have arbitrary string * values. However, at the moment, we only have a small number of * attributes, and each attribute's value is merely a boolean, so it's * adequate to represent these in the call interface with bit flags. */ /* OVERWRITE, as in */ #define VMCON_EVTATTR_OVERWRITE 0x00000001 /* ------------------------------------------------------------------------ */ /* * Newline type codes. These specify how we are to perform line * breaking after writing out text. */ enum vm_nl_type { /* * no line separation at all - write out this text and subsequent * text as part of the same line with no separators */ VM_NL_NONE, /* * flushing in preparation for input - don't show any line separation, * and make sure that we display everything in the buffer, including * trailing spaces */ VM_NL_INPUT, /* break the line at the end of this text and start a newline */ VM_NL_NEWLINE, /* OS line separation - add a space after the text */ VM_NL_OSNEWLINE, /* * flushing internal buffers only: no line separation, and do not * flush to underlying OS level yet */ VM_NL_NONE_INTERNAL }; /* ------------------------------------------------------------------------ */ /* * Handle manager. This is a simple class for mapping system objects to * integers, which we give as "handles" to byte code callers. We give only * the integer handles to byte code to ensure that handles given back to us * by the byte code are valid; if we handed back raw pointers to the byte * code, it could call us with random garbage, and we'd have no way to * protect against it. */ class CVmHandleManager { public: CVmHandleManager(); /* * delete the object (use this rather than calling the destructor * directly, since we need to call some virtuals in the course of * preparing for deletion */ void delete_obj(VMG0_); protected: /* delete */ virtual ~CVmHandleManager(); /* allocate a slot for a new item */ int alloc_handle(void *item); /* clear the given handle's slot */ void clear_handle(int handle) { /* if the handle is valid, clear its associated slot */ if (is_valid_handle(handle)) handles_[handle - 1] = 0; } /* is the given handle valid? */ int is_valid_handle(int handle) { /* * it's valid if it's within range for the allocated slots, and * there's a non-null object in the handle's slot */ return (handle >= 1 && (size_t)handle <= handles_max_ && handles_[handle - 1] != 0); } /* get the object for the given handle */ void *get_object(int handle) { /* * If the handle is valid, get the item from the slot; the handle * is indexed from 1, so decrement it to get the array index of the * object for the handle. If the handle is invalid, return null. */ return is_valid_handle(handle) ? handles_[handle - 1] : 0; } /* * Delete the item in a slot, in preparation for destroying the handle * manager itself - each subclass must override this to do the * appropriate work on termination. */ virtual void delete_handle_object(VMG_ int handle, void *obj) = 0; /* array of window banners */ void **handles_; size_t handles_max_; }; /* ------------------------------------------------------------------------ */ /* * Banner manager. This keeps track of banner windows outside of the main * console window. */ class CVmBannerManager: public CVmHandleManager { public: CVmBannerManager() { } /* * Create a banner window. This creates an OS-level banner window, and * creates a console object to format its output. Returns a banner * handle that can be used to refer to the window. Banner handle zero * is invalid and indicates failure. * * 'parent_id' is the banner ID of the parent of the new banner. The * new banner is created as a child of the given parent. If parent_id * is zero, then the new banner is created as a child of the main * window. The parent determines how the new window is laid out: the * new window's display area is carved out of the display area of the * parent. * * 'where' is OS_BANNER_FIRST, OS_BANNER_LAST, OS_BANNER_BEFORE, or * OS_BANNER_AFTER. 'other_id' is the banner ID of an existing child * of the given parent, for the relative insertion point; this is * ignored for OS_BANNER_FIRST and OS_BANNER_LAST. * * 'wintype' is an OS_BANNER_TYPE_xxx code giving the type of window to * be created. * * 'siz' is the size, in units specified by 'siz_units', an * OS_BANNER_SIZE_xxx value. * * 'style' is a combination of OS_BANNER_STYLE_xxx flags. */ int create_banner(VMG_ int parent_id, int where, int other_id, int wintype, int align, int siz, int siz_units, unsigned long style); /* delete a banner */ void delete_banner(VMG_ int banner) { delete_or_orphan_banner(vmg_ banner, FALSE); } /* * Get the OS-level handle for the banner - this handle can be used to * call the os_banner_xxx functions directly. */ void *get_os_handle(int banner); /* get the banner's console object */ class CVmConsoleBanner *get_console(int banner) { /* the object behind the handle is the console */ return (CVmConsoleBanner *)get_object(banner); } /* flush all banners */ void flush_all(VMG_ vm_nl_type nl); protected: /* delete the object in a slot, in preparation for deleting the manager */ virtual void delete_handle_object(VMG_ int handle, void *) { /* * delete the banner object, but orphan the system-level banner - * this will allow the system-level banner to remain visible even * after VM termination, in case the host application continues * running even after the VM exits */ delete_or_orphan_banner(vmg_ handle, TRUE); } /* delete or orphan a banner window */ void delete_or_orphan_banner(VMG_ int banner, int orphan); }; /* ------------------------------------------------------------------------ */ /* * Log console manager. This keeps track of log consoles, which are * consoles created specifically for capturing text to a log file. */ class CVmLogConsoleManager: public CVmHandleManager { public: CVmLogConsoleManager() { } /* create a log console - returns the console handle */ int create_log_console(VMG_ class CVmNetFile *nf, osfildef *fp, class CCharmapToLocal *cmap, int width); /* delete a log console */ void delete_log_console(VMG_ int handle); /* get the log's console object */ class CVmConsoleLog *get_console(int banner) { /* the object behind the handle is the console */ return (CVmConsoleLog *)get_object(banner); } protected: /* delete the object associated with a handle */ virtual void delete_handle_object(VMG_ int handle, void *) { /* delete the console */ delete_log_console(vmg_ handle); } }; /* * Log console manager item. We use these to track individual log * consoles. */ class CVmLogConsoleItem { public: CVmLogConsoleItem(const char *fname, class CCharmapToLocal *cmap); ~CVmLogConsoleItem(); /* get the console */ class CVmConsoleLog *get_console() const { return console_; } protected: /* my console object */ class CVmConsoleLog *console_; }; /* ------------------------------------------------------------------------ */ /* * Script stack entry */ struct script_stack_entry { script_stack_entry(VMG_ script_stack_entry *encp, int old_more, class CVmNetFile *netfile, osfildef *outfp, const vm_val_t *filespec, int new_more, int is_quiet, int is_event_script); void delobj(VMG0_); /* the enclosing stack level */ script_stack_entry *enc; /* network file descriptor for our script file */ class CVmNetFile *netfile; /* the script file at this level */ osfildef *fp; /* the MORE mode that was in effect before this script file */ int old_more_mode; /* the MORE mode in effect during this script */ int more_mode; /* are we reading quietly from this script? */ int quiet; /* is this an event script? */ int event_script; /* VM global variable, for gc protection for our file spec */ struct vm_globalvar_t *filespec; private: ~script_stack_entry() { } }; /* ------------------------------------------------------------------------ */ /* * Console. A console corresponds to device that shows information to * and reads text from the user. On a text system, the console is * simply the terminal. On a graphical system, the console is usually * an application window. */ class CVmConsole { public: CVmConsole(); virtual void delete_obj(VMG0_); /* write out a null-terminated UTF-8 string */ int format_text(VMG_ const char *p) { /* get its length and write it out */ return format_text(vmg_ p, strlen(p)); } /* write out a UTF-8 string of a given byte length */ virtual int format_text(VMG_ const char *p, size_t len); /* format text explicitly to the log file, if any */ int format_text_to_log(VMG_ const char *p, size_t len); /* display a blank line */ void write_blank_line(VMG0_); /* set the whitespace mode (returns the old whitespace mode) */ int set_obey_whitespace(int f); /* set the text color */ void set_text_color(VMG_ os_color_t fg, os_color_t bg); /* set the body color */ void set_body_color(VMG_ os_color_t color); /* set the caps flag - capitalize the next character */ void caps(); /* set the nocaps flag - make the next letter miniscule */ void nocaps(); /* flush the output with the given newline type */ void flush(VMG_ vm_nl_type nl); /* flush the output, ending the current line and starting a new line */ void flush(VMG0_) { flush(vmg_ VM_NL_NEWLINE); } /* empty our buffers */ void empty_buffers(VMG0_); /* clear the window */ virtual void clear_window(VMG0_) = 0; /* * Flush all windows we control. By default, we just flush our own * window; consoles that manage multiple windows should flush their * managed windows here as well. */ virtual void flush_all(VMG_ vm_nl_type nl) { flush(vmg_ nl); } /* immediately update the display window */ void update_display(VMG0_); /* * Open a new log file. Closes any previous log file. If the file * already exists, we'll overwrite it with the new log information, * otherwise we'll create a new file. Returns zero on success, * non-zero on failure. */ int open_log_file(VMG_ const char *fname); int open_log_file(VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc); /* * close any existing log file - returns zero on success, non-zero * on failure */ int close_log_file(VMG0_); /* * Open a new command log file. We'll log commands (and only * commands) to the command log file. Returns zero on success, * non-zero on failure. */ int open_command_log(VMG_ const char *fname, int event_script); int open_command_log(VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc, int event_script); /* close the command log file, if there is one */ int close_command_log(VMG0_); /* * Set the current MORE mode. Returns the old state. The state is * true if we show MORE prompts, false if not. The state will be * false if the underlying OS display layer handles prompting, so a * return of false doesn't necessarily mean that MORE prompts are * never shown, but merely that we don't handle MORE prompts in the * output formatter itself. */ virtual int set_more_state(int state) = 0; /* determine if we're in MORE mode */ virtual int is_more_mode() const = 0; /* get the line width of the display device */ virtual int get_line_width() const = 0; /* * Do we allow overrunning the line width when we can't find a natural * breaking point (at a whitespace character, for example) such that * we can fit some text within the line width? * * If this returns false, then we'll force a newline when we reach the * line width, even if doing so breaks up a single word that doesn't * have a natural breaking point within. */ virtual int allow_overrun() const = 0; /* get the page length of the display device */ virtual int get_page_length() const = 0; /* get/set the double-space flag (for periods and other punctuation) */ int get_doublespace() const { return doublespace_; } void set_doublespace(int f) { doublespace_ = f; } /* reset the MORE prompt line count */ void reset_line_count(int clearing); /* * check to see if we're reading from a script input file - returns * true if so, false if reading from the user (via the keyboard or * other input device) */ int is_reading_script() const { return (script_sp_ != 0); } /* * check to see if we're reading quietly from a script - if we're * reading from a script, and this flag is true, we suppress all * output */ int is_quiet_script() const { return (script_sp_ != 0 && script_sp_->quiet); } /* is the script in MORE mode? */ int is_moremode_script() const { return (script_sp_ != 0 && script_sp_->more_mode); } /* is the script an type? */ int is_event_script() const { return (script_sp_ != 0 && script_sp_->event_script); } /* * Open a script file. If 'quiet' is true, no output is displayed * while the script is being processed. If 'script_more_mode' is true, * MORE mode is in effect while processing the script, otherwise MORE * mode is turned off while processing the script (to leave things as * they are, simply pass in is_more_mode() for this argument). * * Returns 0 on success, non-zero on error. */ int open_script_file(VMG_ const char *fname, int quiet, int script_more_mode); int open_script_file(VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc, int quiet, int script_more_mode); /* * Close the script file. Returns the original MORE mode that was * in effect before the script file was opened; this MORE mode * should be restored. */ int close_script_file(VMG0_); /* * Read a line of text from the keyboard. Fills in the buffer with * a null-terminated UTF-8 string. Returns zero on success, * non-zero on end-of-file reading the console (which usually * indicates that the user has closed the application, so we're in * the process of terminating; it might also indicate that the * user's terminal has been detached, in which case we also probably * can't do much except terminate). */ int read_line(VMG_ char *buf, size_t buflen, int bypass_script = FALSE); /* * Read a line of input with optional timeout. Fills in the buffer * with a null-terminated UTF-8 string. Returns an OS_EVT_xxx code, * according to how the input was terminated: * * OS_EVT_LINE - the user pressed Return to enter the text *. OS_EVT_TIMEOUT - the timeout expired before the user pressed Return *. OS_EVT_EOF - an error occurred reading the input * * This routine is a cover for the low-level os_gets_timeout(), and * behaves essentially the same way. Note in particular that if this * routine returns OS_EVT_TIMEOUT, then our read_line_cancel() routine * must be called before any output or other display changes can be * made, with the exception that another call to read_line_timeout() * is always allowed. */ int read_line_timeout(VMG_ char *buf, size_t buflen, unsigned long timeout, int use_timeout, int bypass_script = FALSE); /* * Cancel a line of input in progress, which was interrupted by a * timeout in read_line_timeout(). If 'reset' is true, we'll forget * any editing state from the prior line. */ void read_line_cancel(VMG_ int reset); /* * Display a file dialog. This routine works exactly the same way * as os_askfile(), but is implemented here to allow for a formatted * text interface on systems where no dialog is available. */ int askfile(VMG_ const char *prompt, size_t prompt_len, char *reply, size_t replen, int dialog_type, os_filetype_t file_type, int bypass_script = FALSE); /* * Display a system dialog. This routine works exactly the same way * as os_input_dialog(), but is implemented here to allow for a * formatted text interface on systems where no dialog is available. */ int input_dialog(VMG_ int icon_id, const char *prompt, int standard_button_set, const char **buttons, int button_count, int default_index, int cancel_index, int bypass_script = FALSE); /* show the MORE prompt and wait for the user to acknowledge it */ virtual void show_more_prompt(VMG0_) = 0; /* * Log an event. This saves the event to the current script log, if * there is one, in the proper format for the script. We return the * event code. * * 'evt' is the event type, as an OS_EVT_xxx or VMCON_EVT_xxx code. * * 'param' can be given in the local UI character set or in UTF-8 - * specify which it is via 'param_is_utf8'. We write the file in the * local UI character set, so if the parameter is given in UTF-8, we * have to translate it. */ int log_event(VMG_ int evt, const char *param, size_t paramlen, int param_is_utf8); int log_event(VMG_ int evt) { return log_event(vmg_ evt, 0, 0, FALSE); } /* log an event with the given event type tag */ void log_event(VMG_ const char *tag, const char *param, size_t paramlen, int param_is_utf8); /* read an event from an event script */ int read_event_script(VMG_ int *evt, char *buf, size_t buflen, const int *filter, int filter_cnt, unsigned long *attrs); protected: /* the destructor is protected - use delete_obj() to delete */ virtual ~CVmConsole() { } /* * Service routine - show MORE prompt on this console. This can be * called from show_more_prompt() when a MORE prompt is desired at all * in the subclassed console. */ void show_con_more_prompt(VMG0_); /* read a line from the script file */ int read_line_from_script(char *buf, size_t buflen, int *evt); /* read the type tag from the next script event */ int read_script_event_type(int *evt, unsigned long *attrs); /* skip to the next line of the script */ void skip_script_line(osfildef *fp); /* * read a script parameter - this reads the rest of the line into the * given buffer, and skips to the start of the next line in the script; * returns true on success, false if we reach EOF before reading * anything */ int read_script_param(char *buf, size_t buflen, osfildef *fp); /* internal routine to terminate line reading */ void read_line_done(VMG0_); /* write utf-8 text to a file, mapping to the given file character set */ void write_to_file(osfildef *fp, const char *txt, class CCharmapToLocal *map); /* open a comand log file - internal version */ int open_command_log(VMG_ class CVmNetFile *netfile, int event_script); /* open a script file - internal version */ int open_script_file(VMG_ class CVmNetFile *netfile, const vm_val_t *filspec, int quiet, int script_more_mode); /* our current display stream */ class CVmFormatter *disp_str_; /* our log stream - this stream is written to the log file, if any */ class CVmFormatterLog *log_str_; /* * Flag: the log stream is enabled. We can temporarily disable the * log stream, such as when writing to the statusline stream. */ unsigned int log_enabled_ : 1; /* * Flag: display two spaces after a period-like punctuation mark. * This should be true if the output should have two spaces after a * period, question mark, or exclamation point, false for a single * space. This should generally be true for fixed-width fonts, * false for proportional fonts, although some users might prefer to * use single-spacing even for fixed-width fonts. */ unsigned int doublespace_ : 1; /* * flag: the command log is an event script; if this is set, we log all * input events in the tagged file format, otherwise we * log just command lines in the old-style ">line" format */ unsigned int command_eventscript_ : 1; /* * Script-input stack. Each time we open a script, we create a new * stack entry object and link it at the head of the list. So, the * head of the list is the current state, the next element is the * enclosing state, and so on. */ script_stack_entry *script_sp_; /* command log file, if there is one */ class CVmDataSource *command_fp_; class CVmNetFile *command_nf_; struct vm_globalvar_t *command_glob_; }; /* ------------------------------------------------------------------------ */ /* * Main system console. This console is attached to the OS-level primary * console. */ class CVmConsoleMain: public CVmConsole { public: CVmConsoleMain(VMG0_); /* delete */ void delete_obj(VMG0_); /* get the system banner manager */ class CVmBannerManager *get_banner_manager() const { return banner_manager_; } /* get the system log console manager */ class CVmLogConsoleManager *get_log_console_manager() const { return log_console_manager_; } /* * Switch in or out of statusline mode. When we're running on the text * implementation of the OS layer, we must explicitly switch modes * between the main text stream and statusline stream. 'mode' is true * to switch to statusline mode, false to switch back to main text * mode. */ void set_statusline_mode(VMG_ int mode); /* clear the window */ virtual void clear_window(VMG0_); /* set MORE mode */ virtual int set_more_state(int state) { int old_state; /* remember the old state */ old_state = G_os_moremode; /* set the new mode */ G_os_moremode = state; /* return the previous state */ return old_state; } /* get the MORE mode */ virtual int is_more_mode() const { return G_os_moremode; } /* * Flush everything - this flushes not only the main console, but any * banner windows we're managing. This should be called before pausing * for input or for a timed delay, to make sure that buffered output in * all windows is shown. */ void flush_all(VMG_ vm_nl_type nl); /* get the line width of the display device */ virtual int get_line_width() const { return G_os_linewidth; } /* do not allow overrunning the line width on the main console */ virtual int allow_overrun() const { return FALSE; } /* get the page length of the display device */ virtual int get_page_length() const { return G_os_pagelength; } /* show the MORE prompt */ virtual void show_more_prompt(VMG0_) { show_con_more_prompt(vmg0_); } protected: /* main text area display stream */ class CVmFormatterMain *main_disp_str_; /* statusline display stream */ class CVmFormatterStatline *statline_str_; /* * The system banner window manager. Since the main console is * inherently a singleton (as there's only one OS-level primary * console), we keep track of the banner manager. */ class CVmBannerManager *banner_manager_; /* the system log console manager */ class CVmLogConsoleManager *log_console_manager_; }; /* ------------------------------------------------------------------------ */ /* * Banner-window console. */ class CVmConsoleBanner: public CVmConsole { public: /* create */ CVmConsoleBanner(void *banner_handle, int win_type, unsigned long style); /* delete */ void delete_obj(VMG0_); /* retrieve our OS-level banner handle */ void *get_os_handle() const { return banner_; } /* get banner information */ int get_banner_info(os_banner_info_t *info); /* clear the window */ virtual void clear_window(VMG0_); /* set MORE mode */ virtual int set_more_state(int state) { /* banners never change the global MORE mode state */ return is_more_mode(); } /* get the MORE mode - return the global mode flag */ virtual int is_more_mode() const { return G_os_moremode; } /* show the MORE prompt - does nothing for a banner window */ virtual void show_more_prompt(VMG0_) { show_con_more_prompt(vmg0_); } /* get the line width of the display device */ virtual int get_line_width() const { return os_banner_get_charwidth(banner_); } /* allow overrunning the line width in a banner */ virtual int allow_overrun() const { return TRUE; } /* get the page length of the display device, for MORE mode */ virtual int get_page_length() const { return os_banner_get_charheight(banner_); } protected: /* our underlying OS-level banner handle */ void *banner_; /* our window type (an OS_BANNER_TYPE_xxx code) */ int win_type_; }; /* ------------------------------------------------------------------------ */ /* * Common base class for log-only consoles */ class CVmConsoleLogBase: public CVmConsole { public: /* clear the window - do nothing on a log console */ virtual void clear_window(VMG0_) { } /* set MORE mode - doesn't apply to us */ virtual int set_more_state(int state) { return FALSE; } /* show the MORE prompt - does nothing */ virtual void show_more_prompt(VMG0_) { } /* get the MORE mode */ virtual int is_more_mode() const { return FALSE; } /* get the line width of the display device */ virtual int get_line_width() const { return G_os_linewidth; } /* allow overrunning the line width in a lot file */ virtual int allow_overrun() const { return TRUE; } /* * get the page length of the display device - this is arbitrary, since * we don't use MORE mode anyway */ virtual int get_page_length() const { return 55; } }; /* * Log console. This is used to create a console that has no display * presence, but simply captures its output directly to a log file. (This * is similar to the log file attached to a regular display console, but * this kind of console ONLY has the log file.) */ class CVmConsoleLog: public CVmConsoleLogBase { public: /* create */ CVmConsoleLog(VMG_ CVmNetFile *nf, osfildef *fp, class CCharmapToLocal *cmap, int width); void delete_obj(VMG0_); protected: }; /* * A special console that sends output to the *main* console's log file. * This allows code to be written with a generic console pointer, rather * than a test checking for the main console or some other console; just * plug this in when there's not another console pointer and output will go * to the main console automatically. */ class CVmConsoleMainLog: public CVmConsoleLogBase { public: /* create */ CVmConsoleMainLog() { } /* we send text to the main console's log */ int format_text(VMG_ const char *p, size_t len) { /* send the text to the main console's log file */ return G_console->format_text_to_log(vmg_ p, len); } protected: ~CVmConsoleMainLog() { } }; /* ------------------------------------------------------------------------ */ /* * constants */ /* * HTML lexical analysis modes. We use these modes for two purposes: * * First, for tracking our state while doing our own HTML parsing, which we * do when the underlying renderer handles only plain text (in which case * we must interpret and remove any HTML tags from the stream before * sending the stream on to the underlying renderer). * * Second, for tracking the lexical state in the underlying renderer, when * the underlying renderer handles full HTML interpretation. In this case, * we simply pass HTML tags through to the underlying renderer; but even * though we don't interpret the tags, we do keep track of the lexical * structure of the text, so that we can tell when we're inside a tag and * when we're in ordinary text. Certain operations we apply (such as case * conversions with "\^" and "\v" sequences) apply only to ordinary text, * so we need to know what's what in the stream text. */ enum vmconsole_html_state { VMCON_HPS_NORMAL, /* normal text, not in a tag */ VMCON_HPS_TAG_START, /* parsing the start of a tag */ VMCON_HPS_TAG_NAME, /* parsing a tag name */ VMCON_HPS_TAG, /* parsing inside a tag */ VMCON_HPS_ATTR_NAME, /* parsing an attribute name */ VMCON_HPS_ATTR_VAL, /* parsing an attribute value */ VMCON_HPS_SQUOTE, /* in a single-quoted string in a tag */ VMCON_HPS_DQUOTE, /* in a double-quoted string in a tag */ VMCON_HPS_ENTITY_1ST, /* first character in an entity name */ VMCON_HPS_ENTITY_NUM_1ST, /* first character of a numeric entitiy */ VMCON_HPS_ENTITY_HEX, /* in a hexadecimal entitiy number */ VMCON_HPS_ENTITY_DEC, /* in a decimal entity number */ VMCON_HPS_ENTITY_NAME, /* in a named entity */ VMCON_HPS_MARKUP_END /* at last character of a markup */ }; /* * HTML parsing mode flag for
tags. We defer these until we've * read the full tag in order to obey an HEIGHT attribute we find. When * we encounter a
, we figure out whether we think we'll need a * flush or a blank line; if we find a HEIGHT attribute, we may change * this opinion. */ enum vmconsole_html_br_mode { HTML_DEFER_BR_NONE, /* no pending
*/ HTML_DEFER_BR_FLUSH, /* only need to flush output */ HTML_DEFER_BR_BLANK /* need a blank line */ }; /* * Color/attribute information. Each character is tagged with its current * color and attributes. */ struct vmcon_color_t { /* foreground color */ os_color_t fg; /* background color */ os_color_t bg; /* the current OS_ATTR_xxx attributes */ int attr; /* check for equality with another color structure */ int equals(const vmcon_color_t *other) const { return (fg == other->fg && bg == other->bg && attr == other->attr); } }; /* ------------------------------------------------------------------------ */ /* * Output Buffer Flags. Each character in the output buffer has an * associated flag value associated with it; the flag value is a * combination of the bit flags defined here. */ /* * Unbreakable character point. This indicates that a line break is not * allowed to follow this character, even if the text at this point would * ordinarily allow a soft line break. Note that this does NOT override a * hard line break (i.e., an explicit newline); it merely prevents a soft * line break from occurring immediately after this character. * * We apply this flag to the character immediately preceding an explicit * non-breaking space in the text stream. */ #define VMCON_OBF_NOBREAK 0x01 /* * Breakable character point. This indicates that a line break is allowed * to follow this character, even if the text at this point would not * ordinarily allow a soft line break. * * We apply this flag to the character immediately preceding an explicit * zero-width breakable space in the text stream. */ #define VMCON_OBF_OKBREAK 0x02 /* * Break-anywhere mode. This indicates that this character is part of a * run of break-anywhere text. In break-anywhere text, we're allowed to * break lines between any two characters, except adjacent to explicit * non-breaking spaces in the text stream. * * Note that the NOBREAK flag overrides this flag, because this flag * merely indicates the mode, while the NOBREAK flag indicates an explicit * non-breaking indicator. */ #define VMCON_OBF_BREAK_ANY 0x04 /* * Soft hyphenation point. This flag indicates that the text can be * hyphenated immediately following this character, which is to say that * we can insert a hyphen following this character and break the line * after the hyphen. If we do not hyphenate here, the soft hyphen has no * effect; in particular, no hyphen character appears in the output stream * when a soft hyphen is not used as a line break point. */ #define VMCON_OBF_SHY 0x08 /* * Quoted space. This indicates that this is a space character that * doesn't collapse in runs of contiguous whitespace. */ #define VMCON_OBF_QSPACE 0x10 /* ------------------------------------------------------------------------ */ /* * Output formatter stream interface. A formatter stream performs * formatting on a stream of displayed text. * * A given stream of display text might be fed into more than one * formatter stream. For example, if logging is turned on for a * console, we'll feed the same text to the console's main formatting * stream, which will end up being displayed to the user, and also to * the log file's formatting stream, which will end up written out to * the log file. * * Note that the formatter interface is internal to the console system. * Client code should never need to refer to a formatter object, but * should instead call the console object (CVmConsole). */ /* tag alignment types */ enum vmfmt_tab_align_t { VMFMT_TAB_NONE, VMFMT_TAB_LEFT, VMFMT_TAB_CENTER, VMFMT_TAB_RIGHT, VMFMT_TAB_DECIMAL }; /* maximum formatter state stack depth */ const size_t CVMFMT_STACK_MAX = 25; /* maximum depth of color stack */ const size_t CVFMT_CLRSTK_MAX = 25; /* maximum length of an attribute name/value */ const size_t CVFMT_MAX_ATTR_NAME = 40; const size_t CVFMT_MAX_ATTR_VAL = 256; /* output stream information structure (forward declaration) */ typedef struct out_stream_info out_stream_info; /* output formatter stream */ class CVmFormatter { public: CVmFormatter(class CVmConsole *console) { /* remember our display object */ console_ = console; /* there's no title buffer by default */ html_title_buf_ = 0; html_title_buf_size_ = 0; /* we have no horizontal tab table yet (for the HTML mini-parser) */ tabs_ = 0; /* no character mapper yet */ cmap_ = 0; } /* * delete the object (call this instead of calling the destructor * directly, since some subclasses need global access) */ virtual void delete_obj(VMG0_) { delete this; } /* initialize */ virtual void init() { /* start out at the first column */ linepos_ = 0; linecol_ = 0; linebuf_[0] = '\0'; is_continuation_ = FALSE; /* start out in normal text color */ cur_color_.fg = OS_COLOR_P_TEXT; cur_color_.bg = OS_COLOR_P_TRANSPARENT; cur_color_.attr = 0; os_color_ = cur_color_; /* no pending tab yet */ pending_tab_align_ = VMFMT_TAB_NONE; /* start out at the first line */ linecnt_ = 0; /* we're not in either "caps", "nocaps", or "allcaps" mode yet */ capsflag_ = nocapsflag_ = allcapsflag_ = FALSE; /* * set the initial buffer flags: start out in word-break (not * break-anywhere) more */ cur_flags_ = 0; /* * start in normal spacing mode - treat runs of whitespace as * equivalent to a single space */ obey_whitespace_ = FALSE; /* start out in HTML interpretation mode */ literal_mode_ = FALSE; /* presume we won't have OS-level line wrapping */ os_line_wrap_ = FALSE; /* we haven't flushed a new line yet */ just_did_nl_ = FALSE; /* assume that the underlying system is not HTML-enabled */ html_target_ = FALSE; /* presume this target accepts OS highlighting sequences */ plain_text_target_ = FALSE; /* * start out in "normal" lexical state in our parser and in the * underlying output stream */ html_parse_state_ = VMCON_HPS_NORMAL; html_passthru_state_ = VMCON_HPS_NORMAL; /* not in an ignored tag yet (-> nesting depth is zero) */ html_in_ignore_ = 0; /* not in title mode yet (-> nesting depth is zero) */ html_in_title_ = 0; /* not yet deferring line breaks */ html_defer_br_ = HTML_DEFER_BR_NONE; /* not yet in quotes (-> nesting depth is zero) */ html_quote_level_ = 0; /* not in a PRE section */ html_pre_level_ = 0; /* we're not parsing inside any tags yet */ html_allow_alt_ = FALSE; html_allow_color_ = FALSE; html_in_body_ = FALSE; html_in_tab_ = FALSE; html_in_wrap_ = FALSE; /* assume we're a normal display stream */ is_disp_stream_ = TRUE; /* no stacked colors yet */ color_sp_ = 0; } /* get/set obey-whitespace mode */ int get_obey_whitespace() const { return obey_whitespace_ != 0; } void set_obey_whitespace(int f) { obey_whitespace_ = (f != 0); } /* turn on CAPS mode */ void caps() { /* turn on CAPS mode */ capsflag_ = TRUE; /* turn off the NOCAPS and ALLCAPS modes */ nocapsflag_ = FALSE; allcapsflag_ = FALSE; } /* turn on NOCAPS mode */ void nocaps() { /* turn on NOCAPS mode */ nocapsflag_ = TRUE; /* turn off CAPS and ALLCAPS mode */ capsflag_ = FALSE; allcapsflag_ = FALSE; } /* turn on or off ALLCAPS mode */ void allcaps(int all_caps) { /* set the ALLCAPS flag */ allcapsflag_ = all_caps; /* clear the CAPS and NOCAPS flags */ capsflag_ = FALSE; nocapsflag_ = FALSE; } /* * Display a string of a given byte-length. The text is given in * UTF-8 format. We'll interpret embedded control codes (newlines, * blank lines, caps flags, etc); depending on the display mode, we * might also interpret HTML markup sequences. */ int format_text(VMG_ const char *s, size_t len); /* set the text attributes for subsequent format_text() displays */ void set_text_attr(VMG_ int attr) { /* remember the new text attributes */ cur_color_.attr = attr; } /* set the text color for subsequent format_text() displays */ void set_text_color(VMG_ os_color_t fg, os_color_t bg) { /* remember the new text colors */ cur_color_.fg = fg; cur_color_.bg = bg; } /* write a blank line to the stream */ void write_blank_line(VMG0_); /* * Flush the current line to the display, using the given type of * line termination. */ void flush(VMG_ vm_nl_type nl); /* clear our buffers */ void empty_buffers(VMG0_); /* immediately update the display window */ void update_display(VMG0_); /* * determine if I'm an HTML target - this returns true if the * underyling renderer accepts HTML, in which case we write HTML * markups directly to the os_xxx routines (os_printz, for example). */ int is_html_target() const { return html_target_; } /* reset the MORE prompt line counter */ virtual void reset_line_count(int clearing) { linecnt_ = 0; } /* * Note that we read a line from the keyboard with echo. Reading a * line displays a carriage return without our help, taking us back to * the first column - we need to know this so that we can deal * properly with output on the following line. */ void note_input_line() { /* * a CR/LF will have been echoed automatically by the input * reader, which takes us back to the first column */ linecol_ = 0; /* note that we effectively just wrote a newline */ just_did_nl_ = TRUE; } /* -------------------------------------------------------------------- */ /* * Virtual interface to underlying OS renderer */ /* turn HTML mode on/off in the underlying OS-level renderer */ virtual void start_html_in_os() = 0; virtual void end_html_in_os() = 0; /* set the text color */ virtual void set_os_text_color(os_color_t fg, os_color_t bg) = 0; /* set the body color */ virtual void set_os_body_color(os_color_t) = 0; /* set text attributes */ virtual void set_os_text_attr(int attr) = 0; /* * display text to the OS window; the text is given in the local * character set */ virtual void print_to_os(const char *txt) = 0; /* flush the underlying OS-level rendere */ virtual void flush_to_os() = 0; /* set the window title in the OS layer */ virtual void set_title_in_os(const char *txt) = 0; /* set a new character mapper */ void set_charmap(class CCharmapToLocal *cmap); protected: virtual ~CVmFormatter(); /* * Write a line (or a partial line) of text to the stream, using the * indicated line breaking. The text is given as wide Unicode * characters. */ void write_text(VMG_ const wchar_t *txt, size_t cnt, const vmcon_color_t *colors, vm_nl_type nl); /* write a tab to the stream */ void write_tab(VMG_ int indent, int multiple); /* flush the current line, with or without padding */ void flush_line(VMG_ int padding); /* * Buffer a character of output. The character is presented to us as a * wide Unicode character. We'll expand this character with the local * character mapping's expansion rules, then add the expansion to our * output buffer, performing word-wrapping as needed. */ void buffer_char(VMG_ wchar_t c); /* * buffer an expanded character - we'll buffer the given unicode * character, with no further expansion */ void buffer_expchar(VMG_ wchar_t c); /* buffer a rendered expanded character */ void buffer_rendered(wchar_t c, unsigned char flags, int wid); /* * Buffer a string of output to the stream. The string is in UTF-8 * format. */ void buffer_string(VMG_ const char *txt); /* buffer a wide unicode character string */ void buffer_wstring(VMG_ const wchar_t *txt); /* get the next wide unicode character in a UTF8-encoded string */ static wchar_t next_wchar(const char **s, size_t *len); /* * Determine if we should use MORE mode in the formatter layer; if * this returns true, we handle MORE prompting ourselves, otherwise * we handle it through the OS layer. * * The default version of this function is implemented in the output * formatter configuration module. This function can also be * overridden in a subclass (for example, a log stream never uses * MORE mode, no matter what configuration we're using). */ virtual int formatter_more_mode() const; /* * Get the maximum column to store in our internal buffer. If we're * doing our own line breaking, we'll break off the buffer at the * actual line width of the underlying console. If we're doing OS * line wrapping, we'll simply fill up our internal buffer to its * maximum size, since the flushing points are irrelevant to the line * wrapping the underlying OS console will be doing and hence we might * as well buffer as much as we can for efficiency. */ virtual int get_buffer_maxcol() const { /* * if the OS is doing the line wrapping, use the full buffer; * otherwise, use the actual line width from the underyling * console */ if (os_line_wrap_) return OS_MAXWIDTH; else return console_->get_line_width(); } /* * Determine if the underlying stream interprets HTML markups. We * call this during initialization to set our html_target_ member * (we cache the result since we check it frequently). * * This is implemented in the formatter configuration module. */ virtual int get_init_html_target() const; /* -------------------------------------------------------------------- */ /* * HTML mini-parser. This only needs to be implemented when linking * with non-HTML osifc implementations; when osifc provides HTML * parsing, we don't need to do any HTML parsing here, so these can use * dummy implementations. */ /* * Resume text-only HTML mini-parser. This is called when we start * writing a new string and discover that we're parsing inside an HTML * tag in our mini-parser. * * Returns the next character after the run of text we parse. */ wchar_t resume_html_parsing(VMG_ wchar_t c, const char **sp, size_t *slenp); /* parse an HTML entity ('&') markup */ wchar_t parse_entity(VMG_ wchar_t *ent, const char **sp, size_t *slenp); /* * Parse the beginning HTML markup. This is called when we are * scanning a '<' or '&' character in output text, and we're in HTML * mode, and the underlying target doesn't support HTML parsing. * Returns the next character to process after we finish our initial * parsing. */ wchar_t parse_html_markup(VMG_ wchar_t c, const char **sp, size_t *slenp); /* * Expand any pending tab. This should be called when we're doing * HTML mini-parsing, and we see a tag or we reach the end of an * output line. We'll expand any pending RIGHT/CENTER tab by going * back to the tab's location and inserting the necessary number of * spaces now that we know the extent of the text affected. * * If 'allow_anon' is true, we will allow "anonymous" tabs, which is * to say tabs with no ID attribute. Anonymous tabs allow text to be * tabbed relative to the full width of the line, but these are * meaningful only with normal line endings; when a line is flushed * before the end of the line is reached (because the line will be * used for a prompt, for example), anonymous tabs should not be * expanded with the line. */ void expand_pending_tab(VMG_ int allow_anon); /* * find a tab definition object; if 'create' it true and the specified * tab doesn't exist, we'll create a new tab object */ class CVmFmtTabStop *find_tab(wchar_t *id, int create); /* service routine - translate a wide character string to an integer */ static int wtoi(const wchar_t *p); /* parse a COLOR attribute of a FONT tag */ int parse_color_attr(VMG_ const wchar_t *val, os_color_t *result); /* push the current color for later restoration */ void push_color() { /* * add a level to the color stack, if possible; if it's not * possible, assume we've lost an end tag somewhere, so rotate the * entire stack down a level */ if (color_sp_ == CVFMT_CLRSTK_MAX) { /* take everything down a level */ memmove(color_stack_, color_stack_ + 1, (CVFMT_CLRSTK_MAX - 1)*sizeof(color_stack_[0])); --color_sp_; } /* save the current color in the stack */ color_stack_[color_sp_++] = cur_color_; } /* pop the current color */ void pop_color() { /* * if we're at the bottom of the stack, we must have had too many * close tags; ignore the extra operation */ if (color_sp_ != 0) { /* restore the next color down */ cur_color_ = color_stack_[--color_sp_]; } } /* * Process a tag. By default, we ignore this tag entirely. * Log streams should hide the contents of this tag. */ virtual void process_nolog_tag(int /*is_end_tag*/) { /* by default, ignore this tag */ } /* * Process a tag. By default, we hide the contents of this tag; * this tag marks text that is to be shown only in a log stream. */ virtual void process_log_tag(int is_end_tag) { /* turn hiding on or off as appropriate */ if (is_end_tag) --html_in_ignore_; else ++html_in_ignore_; } /* -------------------------------------------------------------------- */ /* * Member variables */ /* the console that owns this formatter stream */ class CVmConsole *console_; /* our character mapper */ class CCharmapToLocal *cmap_; /* current line position and output column */ int linepos_; int linecol_; /* * flag: the current buffer is a "continuation" line; that is, we've * already flushed a partial line to the display without moving to a * new line vertically, and the current buffer will be displayed on the * same line on the terminal, to the right of the previously-displayed * material */ int is_continuation_; /* number of lines on the screen (since last MORE prompt) */ int linecnt_; /* * Output buffer. We keep the output buffer as wide Unicode * characters, and translate to the local character set when we * flush the buffer and send the text to the os_xxx routines for * display. */ wchar_t linebuf_[OS_MAXWIDTH + 1]; /* * Output buffer character colors. We keep track of the display color * of each character in the buffer. */ vmcon_color_t colorbuf_[OS_MAXWIDTH + 1]; /* * output buffer flags - we keep a flag value for each character in * the output buffer */ unsigned char flagbuf_[OS_MAXWIDTH + 1]; /* current attribute name/value buffers */ wchar_t attrname_[CVFMT_MAX_ATTR_NAME]; wchar_t attrval_[CVFMT_MAX_ATTR_VAL]; wchar_t attr_qu_; /* current color of characters being added to our buffer */ vmcon_color_t cur_color_; /* color of last OS output (via write_text) */ vmcon_color_t os_color_; /* stack of color attributes, saved for nested tags */ vmcon_color_t color_stack_[CVFMT_CLRSTK_MAX]; size_t color_sp_; /* * Current output character flags. This is the base value to write to * flagbuf_ for each character we output, given the current mode * settings. */ unsigned char cur_flags_; /* obey-whitespace mode - treat whitespace as literal */ unsigned int obey_whitespace_ : 1; /* literal mode - ignore HTML markups, pass everything literally */ unsigned int literal_mode_ : 1; /* CAPS mode - next character output is converted to upper-case */ unsigned int capsflag_ : 1; /* NOCAPS mode - next character output is converted to lower-case */ unsigned int nocapsflag_ : 1; /* ALLCAPS mode - all characters output are converted to upper-case */ unsigned int allcapsflag_ : 1; /* flag indicating that we just flushed a new line */ unsigned int just_did_nl_ : 1; /* * Flag indicating that the underlying output system wants to * receive its output as HTML. * * If this is true, we'll pass through HTML to the underlying output * system, and in addition generate HTML sequences for certain * TADS-native escapes (for example, we'll convert the "\n" sequence * to a
sequence). * * If this is false, we'll do just the opposite: we'll remove HTML * from the output stream and convert it into normal text sequences. */ unsigned int html_target_ : 1; /* * Flag indicating that the target uses plain text. If this flag is * set, we won't add the OS escape codes for highlighted characters. */ unsigned int plain_text_target_ : 1; /* * flag: the underlying OS layer handles line wrapping, so we never * need to write newlines when flushing our line buffer except when * we want to indicate a hard line break */ unsigned int os_line_wrap_ : 1; /* * Current lexical analysis state for our own HTML parsing. This is * used to track our HTML state when we have an underlying plain text * renderer. */ vmconsole_html_state html_parse_state_; /* * Current lexical analysis mode for the text stream going to the * underlying renderer. This is used to track the lexical structure of * the stream when we're passing HTML tags through to the underlying * renderer. */ vmconsole_html_state html_passthru_state_; /* last tag name */ char html_passthru_tag_[32]; char *html_passthru_tagp_; /*
defer mode */ vmconsole_html_br_mode html_defer_br_; /* * HTML "ignore" mode - we suppress all output when parsing the * contents of a or <ABOUTBOX> tag. This is a counter that * keeps track of the nesting depth for ignored tags. */ int html_in_ignore_; /* * HTML <TITLE> mode - when we're in this mode, we're gathering the * title (i.e., we're inside a <TITLE> tag's contents). We'll copy * characters to the title buffer rather than the normal output * buffer, and then call os_set_title() when we reach the * tag. This is a counter that keeps track of the nesting depth of * tags. */ int html_in_title_; /* buffer for the title */ char *html_title_buf_; size_t html_title_buf_size_; /* pointer to next available character in title buffer */ char *html_title_ptr_; /* * quoting level - this is a counter that keeps track of the nesting * depth of <Q> tags */ int html_quote_level_; /* PRE nesting depth */ int html_pre_level_; /* * Parsing mode flag for ALT attributes. If we're parsing a tag * that allows ALT, such as IMG or SOUND, we'll set this flag, then * insert the ALT text if we encounter it during parsing. */ unsigned int html_allow_alt_ : 1; /* parsing mode flag for COLOR attributes */ unsigned int html_allow_color_ : 1; /* parsing a BODY tag's attributes */ unsigned int html_in_body_ : 1; /* parsing a TAB tag's attributes */ unsigned int html_in_tab_ : 1; /* parsing a WRAP tag's attributes */ unsigned int html_in_wrap_ : 1; /* hash table of <TAB> objects */ class CVmHashTable *tabs_; /* characteristics of TAB tag we're defining */ class CVmFmtTabStop *new_tab_entry_; vmfmt_tab_align_t new_tab_align_; wchar_t new_tab_dp_; /* * Characteristics of pending tab. We must handle this tab when we * reach the next <TAB> or the end of the current line. If * pending_tab_align_ is VMFMT_TAB_NONE, it indicates that there is no * pending tab (since a pending tab always requires alignment). */ vmfmt_tab_align_t pending_tab_align_; class CVmFmtTabStop *pending_tab_entry_; wchar_t pending_tab_dp_; /* * starting column of pending tab - this is the output column where we * were writing when the pending tab was encountered, so this is the * column where we insert spaces for the tab */ int pending_tab_start_; /* * color/attributes active at start of pending tab - this is the color * we'll use for the spaces we insert when we insert the tab */ vmcon_color_t pending_tab_color_; /* * Flag: this is a display stream. Other types of streams (such as * log file streams) should set this to false. */ unsigned int is_disp_stream_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Formatter for display windows */ class CVmFormatterDisp: public CVmFormatter { public: CVmFormatterDisp(class CVmConsole *console) : CVmFormatter(console) { } /* initialize */ void init() { /* inherit base class initialization */ CVmFormatter::init(); /* * if we're compiled for HTML mode, set the standard output * stream so that it knows it has an HTML target - this will * ensure that HTML tags are passed through to the underlying * stream, and that we generate HTML equivalents for our own * control sequences */ html_target_ = get_init_html_target(); /* * since we always use HTML mode, turn on HTML mode in the * underlying OS window if our underlying OS renderer is HTML-aware */ if (html_target_) start_html_in_os(); } }; /* ------------------------------------------------------------------------ */ /* * Formatter subclass for the main display */ class CVmFormatterMain: public CVmFormatterDisp { public: CVmFormatterMain(class CVmConsole *console, size_t html_title_buf_size) : CVmFormatterDisp(console) { /* allocate a title buffer */ html_title_buf_size_ = html_title_buf_size; if (html_title_buf_size_ != 0) html_title_buf_ = (char *)t3malloc(html_title_buf_size_); } /* initialize */ void init() { /* inherit base class initialization */ CVmFormatterDisp::init(); /* remember the OS line wrap setting from the console */ os_line_wrap_ = get_os_line_wrap(); } /* set the window title in the OS layer */ virtual void set_title_in_os(const char *txt) { /* set the window title */ os_set_title(txt); } protected: ~CVmFormatterMain() { /* delete our title buffer */ if (html_title_buf_ != 0) t3free(html_title_buf_); } /* * Determine if the main console uses OS-level line wrapping - if this * is returns true, then an output formatter on this console will not * insert a newline at the end of a line that it's flushing for word * wrapping, but will instead let the underlying OS display layer * handle the wrapping. * * The OS line wrapping status is a PERMANENT feature of the console, * so it is safe for the formatter to query this during initialization * and cache the value. */ static int get_os_line_wrap(); /* turn HTML mode on/off in the underlying OS-level renderer */ virtual void start_html_in_os(); virtual void end_html_in_os(); /* display text directly to the OS renderer */ virtual void print_to_os(const char *txt) { /* display the text on the primary OS console */ os_printz(txt); } /* flush the underlying OS-level renderer */ virtual void flush_to_os() { os_flush(); } /* * set text attributes for subsequent text directly in the underlying * OS window */ virtual void set_os_text_attr(int attr) { /* if the target isn't in 'plain' mode, set the attributes */ if (!plain_text_target_) os_set_text_attr(attr); } /* set the text color in the underlying OS window */ virtual void set_os_text_color(os_color_t fg, os_color_t bg) { /* * if the target is in 'plain' mode, don't use colors; otherwise, * ask the console to do the work */ if (!plain_text_target_) os_set_text_color(fg, bg); } /* set the "body" color in the underlying OS window */ virtual void set_os_body_color(os_color_t color) { /* if not in "plain" mode, set the color */ if (!plain_text_target_) os_set_screen_color(color); } }; /* ------------------------------------------------------------------------ */ /* * Formatter subclass for the status line */ class CVmFormatterStatline: public CVmFormatterDisp { public: CVmFormatterStatline(class CVmConsole *console) : CVmFormatterDisp(console) { } /* we never use 'more' mode in the status line */ virtual int formatter_more_mode() const { return FALSE; } /* HTML mode has no effect on the status line */ virtual void start_html_in_os() { } virtual void end_html_in_os() { } /* text colors and attributes are not used in the status line */ virtual void set_os_text_color(os_color_t, os_color_t) { } virtual void set_os_body_color(os_color_t) { } virtual void set_os_text_attr(int) { } /* text displayed in the status line goes directly to the main console */ virtual void print_to_os(const char *txt) { os_printz(txt); } /* flushing the status line simply flushes the main text stream */ virtual void flush_to_os() { os_flush(); } /* titles have no effect in the status line */ virtual void set_title_in_os(const char *) { } }; /* ------------------------------------------------------------------------ */ /* * Formatter subclass for banner windows */ class CVmFormatterBanner: public CVmFormatterDisp { public: CVmFormatterBanner(void *banner, class CVmConsole *console, int win_type, unsigned long style) : CVmFormatterDisp(console) { /* remember my OS banner handle */ banner_ = banner; /* remember my window type */ win_type_ = win_type; /* remember the banner style */ style_ = style; } /* initialize */ void init_banner(int os_line_wrap, int obey_whitespace, int literal_mode) { /* inherit base class initialization */ CVmFormatterDisp::init(); /* remember the OS line wrapping mode in our underlying OS window */ os_line_wrap_ = os_line_wrap; /* remember the whitespace setting */ obey_whitespace_ = obey_whitespace; /* remember the literal-mode setting */ literal_mode_ = literal_mode; } /* set the window title in the OS layer (does nothing for a banner) */ virtual void set_title_in_os(const char *) { } /* reset the MORE prompt line counter */ virtual void reset_line_count(int clearing) { /* * To ensure we always keep a line of context when we page-forward * from a MORE prompt, start the line counter at 2. Note that we * do this in banner windows, but not in the main window, because - * for historical reasons - the OS layer tells us the *paging* size * for the main window, but the *actual* height for banner window. * Note that we don't want to handle this adjustment in our own * page length calculation, because doing so would cause the * *first* MORE prompt (from a cleared window) to show up too * early. * * When we're clearing the screen, reset to zero, since we have no * context to retain. */ linecnt_ = (clearing ? 0 : 1); } protected: /* * Use MORE mode in a banner window if we have the MORE-mode banner * window style, AND the base display banner would use MORE mode. The * latter check tests to see if we handle MORE mode at the OS level or * in the formatter. */ virtual int formatter_more_mode() const { return ((style_ & OS_BANNER_STYLE_MOREMODE) != 0 && CVmFormatterDisp::formatter_more_mode()); } /* display text directly to the OS renderer */ virtual void print_to_os(const char *txt) { /* display the text on our OS banner window */ os_banner_disp(banner_, txt, strlen(txt)); } /* turn HTML mode on/off in the underlying OS-level renderer */ virtual void start_html_in_os() { os_banner_start_html(banner_); } virtual void end_html_in_os() { os_banner_end_html(banner_); } /* set the text color */ virtual void set_os_text_color(os_color_t fg, os_color_t bg) { /* set the color in the banner */ os_banner_set_color(banner_, fg, bg); } /* set the body color */ virtual void set_os_body_color(os_color_t color) { /* set the color in the banner */ os_banner_set_screen_color(banner_, color); } /* set text attributes */ virtual void set_os_text_attr(int attr) { /* set the attributes on the underlying OS-level banner */ os_banner_set_attr(banner_, attr); } /* flush the underlying OS-level renderer */ virtual void flush_to_os() { /* flush the OS banner window */ os_banner_flush(banner_); } /* determine if the target supports HTML */ virtual int get_init_html_target() const { /* if we're a text grid, the underlying window does not use HTML */ if (win_type_ == OS_BANNER_TYPE_TEXTGRID) return FALSE; /* defer to the inherited determination in other cases */ return CVmFormatterDisp::get_init_html_target(); } /* my OS banner handle */ void *banner_; /* my OS window type (OS_BANNER_TYPE_xxx) */ int win_type_; /* banner style (a combination of OS_BANNER_STLE_xxx bit flags) */ unsigned long style_; }; /* ------------------------------------------------------------------------ */ /* * Formatter subclass for the log file */ class CVmFormatterLog: public CVmFormatter { friend class CVmConsole; friend class CVmConsoleLog; public: CVmFormatterLog(class CVmConsole *console, int width) : CVmFormatter(console) { /* we have no log file yet */ lognf_ = 0; logfp_ = 0; logglob_ = 0; /* remember our width */ width_ = width; } void delete_obj(VMG0_) { /* close the log file */ close_log_file(vmg0_); /* do the basic deletion */ CVmFormatter::delete_obj(vmg0_); } /* initialize */ void init() { /* inherit base class initialization */ CVmFormatter::init(); /* use plain text in the log file stream */ plain_text_target_ = TRUE; /* we're not a display stream */ is_disp_stream_ = FALSE; /* we're not an HTML formatter */ html_target_ = FALSE; /* * we use our own internal line wrapping, since our underlying * display layer is simply dumping to a text file */ os_line_wrap_ = FALSE; /* no log file yet */ logfp_ = 0; } /* don't use MORE mode in a log stream */ int formatter_more_mode() const { return FALSE; } /* get my width */ virtual int get_buffer_maxcol() const { return width_; } protected: /* log streams do not support HTML at the OS level */ virtual void start_html_in_os() { } virtual void end_html_in_os() { } /* set the attributes in the underlying stream */ virtual void set_os_text_attr(int) { /* log streams are plain text - they don't support attributes */ } /* set the color in the underlying stream */ virtual void set_os_text_color(os_color_t, os_color_t) { /* log streams are plain text - they don't support colors */ } /* set the body color */ virtual void set_os_body_color(os_color_t) { /* log streams are plain text - they don't support colors */ } /* display text to the underlying OS device */ virtual void print_to_os(const char *txt) { /* display the text through the log file */ if (logfp_ != 0) { os_fprintz(logfp_, txt); osfflush(logfp_); } } /* flush the underlying OS-level rendere */ virtual void flush_to_os() { } /* set the window title in the OS layer - no effect for log streams */ virtual void set_title_in_os(const char *) { } /* open a log file */ int open_log_file(VMG_ const char *fname); int open_log_file(VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc); int open_log_file(VMG_ class CVmNetFile *netfile); /* set the log file to a file previously opened */ int set_log_file(VMG_ CVmNetFile *nf, osfildef *fp); /* close the log file */ int close_log_file(VMG0_); /* * Process a <nolog> tag. Since we're a log stream, we hide the * contents of this tag. */ virtual void process_nolog_tag(int is_end_tag) { /* turn hiding on or off as appropriate */ if (is_end_tag) --html_in_ignore_; else ++html_in_ignore_; } /* * Process a <log> tag. Since we're a log stream, we show the contents * of this tag, so we can simply parse and ignore it. */ virtual void process_log_tag(int /*is_end_tag*/) { } /* my log file handle and network file descriptor */ osfildef *logfp_; class CVmNetFile *lognf_; struct vm_globalvar_t *logglob_; /* the maximum width to use for our lines */ int width_; }; #endif /* VMCONSOL_H */ �����������������������������qtads-2.1.7/tads3/vmcrc.cpp�������������������������������������������������������������������������0000664�0000000�0000000�00000010307�12650170723�0015475�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2003 by Michael J. Roberts. All Rights Reserved. */ /* Name vmcrc.cpp - CRC-32 calculator Function Notes Modified 06/21/03 MJRoberts - Creation */ #include "vmcrc.h" /* * add the given buffer into the checksum */ void CVmCRC32::scan_bytes(const void *ptr, size_t len) { static unsigned long tab[] = { 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL }; const unsigned char *p; /* add the bytes into the CRC accumulator */ for (p = (const unsigned char *)ptr ; len != 0 ; ++p, --len) acc_ = tab[(acc_ ^ *p) & 0xff] ^ (acc_ >> 8); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmcrc.h���������������������������������������������������������������������������0000664�0000000�0000000�00000001240�12650170723�0015136�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2003 by Michael J. Roberts. All Rights Reserved. */ /* Name vmcrc.h - compute a CRC-32 checksum of a data stream Function Notes Modified 06/21/03 MJRoberts - Creation */ #ifndef VMCRC_H #define VMCRC_H #include <stdlib.h> class CVmCRC32 { public: CVmCRC32() { /* start with zero in the accumulator */ acc_ = 0; } /* add the given buffer into the checksum */ void scan_bytes(const void *ptr, size_t len); /* retrieve the current checksum value */ unsigned long get_crc_val() const { return acc_; } protected: /* the checksum accumulator */ unsigned long acc_; }; #endif /* VMCRC_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmcset.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000042545�12650170723�0015675�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmcset.cpp - T3 CharacterSet metaclass Function Notes Modified 06/06/01 MJRoberts - Creation */ #include <stdlib.h> #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" #include "vmcset.h" #include "vmbif.h" #include "vmfile.h" #include "vmerrnum.h" #include "vmerr.h" #include "vmstack.h" #include "vmmeta.h" #include "vmrun.h" #include "charmap.h" #include "vmstr.h" #include "vmpredef.h" #include "vmrun.h" #include "vmhost.h" /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassCharSet metaclass_reg_obj; CVmMetaclass *CVmObjCharSet::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjCharSet:: *CVmObjCharSet::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjCharSet::getp_undef, &CVmObjCharSet::getp_get_name, &CVmObjCharSet::getp_is_known, &CVmObjCharSet::getp_is_mappable, &CVmObjCharSet::getp_is_rt_mappable }; /* ------------------------------------------------------------------------ */ /* * Create from stack */ vm_obj_id_t CVmObjCharSet::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id; vm_val_t *arg1; const char *charset_name; /* check our arguments */ if (argc != 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* get the name of the character set */ arg1 = G_stk->get(0); charset_name = arg1->get_as_string(vmg0_); if (charset_name == 0) err_throw(VMERR_BAD_TYPE_BIF); /* create the character set object */ id = vm_new_id(vmg_ FALSE, FALSE, FALSE); new (vmg_ id) CVmObjCharSet(vmg_ charset_name + VMB_LEN, vmb_get_len(charset_name)); /* discard arguments */ G_stk->discard(argc); /* return the new object */ return id; } /* ------------------------------------------------------------------------ */ /* * Create with no contents */ vm_obj_id_t CVmObjCharSet::create(VMG_ int in_root_set) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjCharSet(); return id; } /* * Create with the given character set name */ vm_obj_id_t CVmObjCharSet::create(VMG_ int in_root_set, const char *charset_name, size_t charset_name_len) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjCharSet(vmg_ charset_name, charset_name_len); return id; } /* ------------------------------------------------------------------------ */ /* * Instantiate */ CVmObjCharSet::CVmObjCharSet(VMG_ const char *charset_name, size_t charset_name_len) { /* allocate and initialize our extension */ ext_ = 0; alloc_ext(vmg_ charset_name, charset_name_len); } /* * Allocate and initialize our extension */ void CVmObjCharSet::alloc_ext(VMG_ const char *charset_name, size_t charset_name_len) { size_t alloc_size; vmobj_charset_ext_t *extp; CResLoader *res_ldr; /* if we already have an extension, delete it */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* * compute the size we need - note that we use the one fixed byte of * the structure's name element as the extra byte we need for null * termination of the name */ alloc_size = sizeof(vmobj_charset_ext_t) + charset_name_len; /* allocate space for our extension structure */ ext_ = (char *)G_mem->get_var_heap()->alloc_mem(alloc_size, this); /* cast the extension to our structure type */ extp = (vmobj_charset_ext_t *)ext_; /* store the character set name and length, null-terminating the name */ extp->charset_name_len = charset_name_len; memcpy(extp->charset_name, charset_name, charset_name_len); extp->charset_name[charset_name_len] = '\0'; /* presume we won't be able to load the character sets */ extp->to_local = 0; extp->to_uni = 0; /* get the resource loader */ res_ldr = G_host_ifc->get_sys_res_loader(); /* if we have a resource loader, load the mappings */ if (res_ldr != 0) { /* load the unicode-to-local mapping */ extp->to_local = CCharmapToLocal::load(res_ldr, extp->charset_name); /* load the local-to-unicode mapping */ extp->to_uni = CCharmapToUni::load(res_ldr, extp->charset_name); } } /* ------------------------------------------------------------------------ */ /* * Notify of deletion */ void CVmObjCharSet::notify_delete(VMG_ int /*in_root_set*/) { /* release our mapper objects */ if (ext_ != 0) { /* release the to-local character mapper */ if (get_ext_ptr()->to_local != 0) get_ext_ptr()->to_local->release_ref(); /* release the to-unicode character mapper */ if (get_ext_ptr()->to_uni != 0) get_ext_ptr()->to_uni->release_ref(); /* free our extension */ G_mem->get_var_heap()->free_mem(ext_); } } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjCharSet::set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * get a property */ int CVmObjCharSet::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property index to an index into our function table */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * load from an image file */ void CVmObjCharSet::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* initialize with the character set name from the image file */ alloc_ext(vmg_ ptr + VMB_LEN, vmb_get_len(ptr)); } /* ------------------------------------------------------------------------ */ /* * save to a file */ void CVmObjCharSet::save_to_file(VMG_ class CVmFile *fp) { /* write the name length */ fp->write_uint2(get_ext_ptr()->charset_name_len); /* write the bytes of the name */ fp->write_bytes(get_ext_ptr()->charset_name, get_ext_ptr()->charset_name_len); } /* * restore from a file */ void CVmObjCharSet::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *) { char buf[128]; size_t len; size_t read_len; /* read the length of the character set name */ len = fp->read_uint2(); /* limit the reading to the length of the buffer */ read_len = len; if (read_len > sizeof(buf)) read_len = sizeof(buf); /* read the name, up to the buffer length */ fp->read_bytes(buf, read_len); /* skip any bytes we couldn't fit in the buffer */ if (len > read_len) fp->set_pos(fp->get_pos() + len - read_len); /* initialize from the saved data */ alloc_ext(vmg_ buf, read_len); } /* ------------------------------------------------------------------------ */ /* * Compare for equality */ int CVmObjCharSet::equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int /*depth*/) const { CVmObjCharSet *other; const vmobj_charset_ext_t *ext; const vmobj_charset_ext_t *other_ext; /* if it's a self-reference, it's certainly equal */ if (val->typ == VM_OBJ && val->val.obj == self) return TRUE; /* if it's not another character set, it's not equal */ if (val->typ != VM_OBJ || !is_charset(vmg_ val->val.obj)) return FALSE; /* we know it's another character set - cast it */ other = (CVmObjCharSet *)vm_objp(vmg_ val->val.obj); /* get my extension and the other extension */ ext = get_ext_ptr(); other_ext = other->get_ext_ptr(); /* it's equal if it has the same name (ignoring case) */ return (ext->charset_name_len == other_ext->charset_name_len && memicmp(ext->charset_name, other_ext->charset_name, ext->charset_name_len) == 0); } /* * Calculate a hash value */ uint CVmObjCharSet::calc_hash(VMG_ vm_obj_id_t self, int /*depth*/) const { uint hash; size_t rem; const char *p; /* add up the bytes in the array */ for (hash = 0, rem = get_ext_ptr()->charset_name_len, p = get_ext_ptr()->charset_name ; rem != 0 ; --rem, ++p) { /* add this character into the hash */ hash += *p; } /* return the result */ return hash; } /* ------------------------------------------------------------------------ */ /* * property evaluator - get the character set name */ int CVmObjCharSet::getp_get_name(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* create a new string for the name */ retval->set_obj(CVmObjString::create(vmg_ FALSE, get_ext_ptr()->charset_name, get_ext_ptr()->charset_name_len)); /* handled */ return TRUE; } /* * property evaluator - is known */ int CVmObjCharSet::getp_is_known(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* * it's known if both of our character mappers are non-null; if either * is null, the character set is not known on this platform */ retval->set_logical(get_ext_ptr()->to_local != 0 && get_ext_ptr()->to_uni != 0); /* handled */ return TRUE; } /* * property evaluator - check a character or a string for mappability */ int CVmObjCharSet::getp_is_mappable(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(1); vm_val_t arg; const char *str; CCharmapToLocal *to_local; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the local mapping */ to_local = get_to_local(vmg0_); /* get the argument and check what type we have */ G_stk->pop(&arg); if (to_local == 0) { /* no mapping is available, so it's not mappable */ retval->set_nil(); } else if ((str = arg.get_as_string(vmg0_)) != 0) { size_t len; utf8_ptr p; /* get the length and skip the length prefix */ len = vmb_get_len(str); str += VMB_LEN; /* presume every character will be mappable */ retval->set_true(); /* check each character for mappability */ for (p.set((char *)str) ; len != 0 ; p.inc(&len)) { /* check to see if this character is mappable */ if (!to_local->is_mappable(p.getch())) { /* * The character isn't mappable - this is an * all-or-nothing check, so if one isn't mappable we * return false. Set the nil return and stop looking. */ retval->set_nil(); break; } } } else if (arg.typ == VM_INT) { /* * Check if the integer character value is mappable. If it's out * of the 16-bit unicode range (0..0xffff), it's not mappable; * otherwise, ask the character mapper. */ if (arg.val.intval < 0 || arg.val.intval > 0xffff) { /* it's out of the valid unicode range, so it's not mappable */ retval->set_nil(); } else { /* ask the character mapper */ retval->set_logical(to_local->is_mappable( (wchar_t)arg.val.intval)); } } /* handled */ return TRUE; } /* * property evaluator - check a character or a string to see if it has a * round-trip mapping. A round-trip mapping is one where the unicode * characters can be mapped to the local character set, then back to * unicode, yielding the exact original unicode string. */ int CVmObjCharSet::getp_is_rt_mappable(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(1); vm_val_t arg; const char *str; CCharmapToLocal *to_local; CCharmapToUni *to_uni; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the local and unicode mappings */ to_local = get_to_local(vmg0_); to_uni = get_to_uni(vmg0_); /* get the argument and check what type we have */ G_stk->pop(&arg); if (to_local == 0 || to_uni == 0) { /* no character sets, so it's not mappable */ retval->set_nil(); } else if ((str = arg.get_as_string(vmg0_)) != 0) { size_t len; utf8_ptr p; /* get the length and skip the length prefix */ len = vmb_get_len(str); str += VMB_LEN; /* presume every character will be mappable */ retval->set_true(); /* check each character for mappability */ for (p.set((char *)str) ; len != 0 ; p.inc(&len)) { /* check for round-trip mappability */ if (!is_rt_mappable(p.getch(), to_local, to_uni)) { /* nope - return false */ retval->set_nil(); break; } } } else if (arg.typ == VM_INT) { /* check the integer character for mappability */ if (arg.val.intval < 0 || arg.val.intval > 0xffff) { /* it's out of the valid unicode range, so it's not mappable */ retval->set_nil(); } else { /* ask the character mapper */ retval->set_logical(is_rt_mappable( (wchar_t)arg.val.intval, to_local, to_uni)); } } /* handled */ return TRUE; } /*------------------------------------------------------------------------ */ /* * Determine if a character has a round-trip mapping. */ int CVmObjCharSet::is_rt_mappable(wchar_t c, CCharmapToLocal *to_local, CCharmapToUni *to_uni) { char lclbuf[16]; char unibuf[16]; size_t lcllen; size_t unilen; char *p; /* if there's no local mapping, it's obviously not mappable */ if (!to_local->is_mappable(c)) return FALSE; /* * If there's an expansion in the mapping to the local set, then there * can't be a round-trip mapping. Expansions are inherently one-way * because they produce multiple local characters for a single unicode * character, and the reverse mapping has no way to group those * multiple local characters back into a single unicode character. */ if (to_local->get_expansion(c, &lcllen) != 0) return FALSE; /* get the local mapping */ lcllen = to_local->map_char(c, lclbuf, sizeof(lclbuf)); /* map it back to unicode */ p = unibuf; unilen = sizeof(unibuf); unilen = to_uni->map(&p, &unilen, lclbuf, lcllen); /* * if the unicode mapping is one character that exactly matches the * original input character, then we have a valid round-trip mapping */ return (unilen == utf8_ptr::s_wchar_size(c) && utf8_ptr::s_getch(unibuf) == c); } /*------------------------------------------------------------------------ */ /* * Get the unicode-to-local character set mapper */ CCharmapToLocal *CVmObjCharSet::get_to_local(VMG0_) const { /* if there's no mapper, throw an exception */ if (get_ext_ptr()->to_local == 0) { /* throw an UnknownCharacterSetException */ G_interpreter->throw_new_class(vmg_ G_predef->charset_unknown_exc, 0, "unknown character set"); } /* return the mapper */ return get_ext_ptr()->to_local; } /* * Get the local-to-unicode character set mapper */ CCharmapToUni *CVmObjCharSet::get_to_uni(VMG0_) const { /* if there's no mapper, throw an exception */ if (get_ext_ptr()->to_uni == 0) { /* throw an UnknownCharacterSetException */ G_interpreter->throw_new_class(vmg_ G_predef->charset_unknown_exc, 0, "unknown character set"); } /* return the mapper */ return get_ext_ptr()->to_uni; } �����������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmcset.h��������������������������������������������������������������������������0000664�0000000�0000000�00000021046�12650170723�0015333�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmcset.h - T3 CharacterSet metaclass Function Notes Modified 06/06/01 MJRoberts - Creation */ #ifndef VMCSET_H #define VMCSET_H #include <stdlib.h> #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" /* ------------------------------------------------------------------------ */ /* * A CharacterSet is a simple encapsulation of a pair of CCharmap * character mappings: one mapping from Unicode to a local character set, * and one mapping in the reverse direction. A CharacterSet is * parameterized on creation by the name of the mapping, using the * standard CCharmap names. * * In an image file, a CharacterSet contains simply the standard CCharmap * name of the mapping: * * UINT2 length-in-bytes *. BYTE name[] * * On creation, we will create the pair of CCharmap objects, if the name * of the mapping is valid. It is legal to create a CharacterSet with an * unknown mapping, but such a character set object cannot be used to * perform mappings. * * CharacterSet objects are constants at run-time. */ class CVmObjCharSet: public CVmObject { friend class CVmMetaclassCharSet; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* explicitly inherit our superclass handling */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *, vm_obj_id_t /*self*/) { /* we can't be converted to constant data */ } /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *, vm_obj_id_t /*self*/) { /* * we don't reference any data and can't be converted to constant * data ourselves, so there's nothing to do here */ } /* create with no initial contents */ static vm_obj_id_t create(VMG_ int in_root_set); /* create with the given character set name */ static vm_obj_id_t create(VMG_ int in_root_set, const char *charset_name, size_t charset_name_len); /* determine if an object is a CharacterSet */ static int is_charset(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* undo operations */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { } /* we reference nothing */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } void mark_refs(VMG_ uint /*state*/) { } void remove_stale_weak_refs(VMG0_) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* * Check a value for equality. We will match another byte array with * the same number of elements and the same value for each element. */ int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const; /* calculate a hash value for the array */ uint calc_hash(VMG_ vm_obj_id_t self, int depth) const; /* our data are constant - we never change */ int is_changed_since_load() const { return FALSE; } /* * Get the to-local and to-unicode mappers. If the mapper isn't * available, we'll throw an UnknownCharacterSetException. */ class CCharmapToLocal *get_to_local(VMG0_) const; class CCharmapToUni *get_to_uni(VMG0_) const; protected: /* create with no initial contents */ CVmObjCharSet() { ext_ = 0; } /* create from the given character set name */ CVmObjCharSet(VMG_ const char *charset_name, size_t charset_name_len); /* allocate and initialize */ void alloc_ext(VMG_ const char *charset_name, size_t charset_name_len); /* get a pointer to my extension */ const struct vmobj_charset_ext_t *get_ext_ptr() const { return (vmobj_charset_ext_t *)ext_; } /* does the given unicode character have a round-trip mapping? */ static int is_rt_mappable(wchar_t c, class CCharmapToLocal *to_local, class CCharmapToUni *to_uni); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - get the character set name */ int getp_get_name(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* determine if the mapping is known */ int getp_is_known(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* * property evaluator - determine if the a character code (given as an * integer) or the characters in a string can be mapped from Unicode * to this local character set */ int getp_is_mappable(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* * property evaluator - determine if the character code (given as an * integer) or the characters in a string have a round-trip mapping * from Unicode to local and back */ int getp_is_rt_mappable(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluation function table */ static int (CVmObjCharSet::*func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* * Our extension structure */ struct vmobj_charset_ext_t { /* unicode-to-local mapping object */ class CCharmapToLocal *to_local; /* local-to-unicode mapping object */ class CCharmapToUni *to_uni; /* length of character set name */ size_t charset_name_len; /* name of the character set */ char charset_name[1]; }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassCharSet: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "character-set/030001"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjCharSet(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjCharSet(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjCharSet::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjCharSet::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMCSET_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjCharSet) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmdatasrc.h�����������������������������������������������������������������������0000664�0000000�0000000�00000064746�12650170723�0016034�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmdatasrc.h - Data Source object Function CVmDataSource is an abstract class representing a read/write data source, such as a file, block of memory, etc. The data source object has an interface essentially identical to the osifc file interface, but can be implemented with a variety of underlying data sources. Notes Modified 05/05/10 MJRoberts - Creation */ #ifndef VMDATASRC_H #define VMDATASRC_H #include <string.h> #include "t3std.h" #include "osifcnet.h" #include "vmerr.h" #include "vmfile.h" #include "vmerrnum.h" /* ------------------------------------------------------------------------ */ /* * Abstract file interface. This allows the File intrinsic class to * present a common interface on multiple underlying data sources. */ class CVmDataSource { public: virtual ~CVmDataSource() { } /* read bytes - returns 0 on success, non-zero on EOF or error */ virtual int read(void *buf, size_t len) = 0; /* read bytes - returns the number of bytes read; 0 means EOF or error */ virtual int readc(void *buf, size_t len) = 0; /* write bytes - returns 0 on success, non-zero on error */ virtual int write(const void *buf, size_t len) = 0; /* write a null-terminated string */ virtual int writez(const char *str) { return write(str, strlen(str)); } /* get the length of the file in bytes */ virtual long get_size() = 0; /* get the current seek location */ virtual long get_pos() = 0; /* set the current seek location - 'mode' is an OSFSK_xxx mode */ virtual int seek(long ofs, int mode) = 0; /* flush - returns 0 on success, non-zero on error */ virtual int flush() = 0; /* close the underlying system resource */ virtual void close() = 0; /* * Read a line of text (fgets() semantics). This default * implementation reads a character at a time from the underlying * source; subclasses should override this if they can implement it * more efficiently. */ virtual char *read_line(char *buf, size_t len) { /* if we have no bytes left, return null for EOF */ if (get_pos() >= get_size()) return 0; /* read bytes until we encounter a CR, LF, or CR-LF */ char *dst = buf; while (len > 0 && get_pos() < get_size()) { /* read the next character */ char ch; if (read(&ch, 1)) return 0; /* check for a line terminator */ if (ch == 10 || ch == 13) { /* * add a newline to the result (it's always '\n' in the * result buffer regardless of the type of newline in the * source) */ *dst++ = '\n'; --len; /* * if this is a CR-LF sequence, skip both characters; * otherwise just skip the single CR or LF */ char ch2; if (ch == 13 && get_pos() < get_size() && !read(&ch2, 1) && ch2 != 10) { /* it's not a CR-LF - un-get the second character */ seek(get_pos() - 1, OSFSK_SET); } /* we're done */ break; } /* copy this byte */ *dst++ = ch; /* count it against the remaining buffer length */ --len; } /* add a null terminator if possible */ if (len != 0) *dst = '\0'; /* return the original buffer pointer */ return buf; } /* read a line of text into an allocated buffer */ char *read_line_alo() { char *ret = 0; size_t retlen = 0; /* keep going until we satisfy the request */ for (;;) { /* read a chunk; if at EOF, we're done */ char buf[1024]; if (read_line(buf, sizeof(buf)) == 0) break; /* get the length of this chunk */ size_t len = strlen(buf); /* allocate or expand the buffer to accommodate the new text */ if (ret == 0) ret = (char *)t3malloc(len + 1); else ret = (char *)t3realloc(ret, retlen + len + 1); /* fail if out of memory */ if (ret == 0) return 0; /* add the new chunk to the result (including the null) */ memcpy(ret + retlen, buf, len + 1); retlen += len; /* if the buffer ends with a newline, we're done */ if (retlen != 0 && ret[retlen-1] == '\n') break; } /* return our buffer */ return ret; } /* * Create a duplicate data source. * * 'mode' is a simplified version of the stdio fopen() mode string. * The first letter is 'r' for read access, 'w' for write access. This * can be followed by '+' for both read and write access; 'r+' and 'w+' * are identical, both indicating read/write access. This can * optionally be followed by 't' for text mode or 'b' for binary mode; * text mode is assumed if not specified. */ virtual CVmDataSource *clone(VMG_ const char *mode) = 0; }; /* ------------------------------------------------------------------------ */ /* * Basic OS file data source. */ class CVmFileSource: public CVmDataSource { public: CVmFileSource(osfildef *fp) { this->fp = fp; } ~CVmFileSource() { if (fp != 0) close(); } /* read bytes - returns 0 on success, non-zero on EOF or error */ virtual int read(void *buf, size_t len) { return osfrb(fp, buf, len); } /* read bytes - returns 0 on success, non-zero on EOF or error */ virtual int readc(void *buf, size_t len) { return osfrbc(fp, buf, len); } /* read a line of text (fgets() semantics) */ virtual char *read_line(char *buf, size_t len) { return osfgets(buf, len, fp); } /* write bytes - returns 0 on success, non-zero on error*/ virtual int write(const void *buf, size_t len) { return osfwb(fp, buf, len); } /* get the length of the file in bytes */ virtual long get_size() { /* remember the current seek location */ long oldpos = osfpos(fp); /* seek to the end */ osfseek(fp, 0, OSFSK_END); /* the current position is the file size */ long siz = osfpos(fp); /* seek back to where we started */ osfseek(fp, oldpos, OSFSK_SET); /* return the size */ return siz; } /* get the current seek location */ virtual long get_pos() { return osfpos(fp); } /* set the current seek location - 'mode' is an OSFSK_xxx mode */ virtual int seek(long ofs, int mode) { return osfseek(fp, ofs, mode); } /* flush - returns 0 on success, non-zero on error */ virtual int flush() { return osfflush(fp); } /* close the underlying system resource */ virtual void close() { osfcls(fp); fp = 0; } /* clone the source */ virtual CVmDataSource *clone(VMG_ const char *mode) { /* duplicate our underlying file handle */ osfildef *fpdup = osfdup(fp, mode); if (fpdup == 0) return 0; /* return a new source wrapping the duplicated file handle */ return new CVmFileSource(fpdup); } protected: /* the underlying file */ osfildef *fp; }; /* ------------------------------------------------------------------------ */ /* * Resource file data source. A resource file is a contiguous byte range * within a larger file. */ class CVmResFileSource: public CVmDataSource { public: CVmResFileSource(osfildef *fp, unsigned long start, long end) { this->fp = fp; this->start = start; this->end = end; } ~CVmResFileSource() { if (fp != 0) close(); } /* clone the source */ virtual CVmDataSource *clone(VMG_ const char *mode) { /* duplicate our underlying file handle */ osfildef *fpdup = osfdup(fp, mode); if (fpdup == 0) return 0; /* return a new source wrapping the duplicated file handle */ return new CVmResFileSource(fpdup, start, end); } /* read bytes - returns 0 on success, non-zero on EOF or error */ virtual int read(void *buf, size_t len) { /* if this would take us past the end, we can't fulfill the request */ unsigned long pos = osfpos(fp); if (pos + len > end) return 1; /* do the read */ return osfrb(fp, buf, len); } /* read a line of text (fgets() semantics) */ virtual char *read_line(char *buf, size_t len) { /* if we're already past the end, we can't fulfill the request */ unsigned long pos = osfpos(fp); if (pos >= end) return 0; /* read a line */ char *ret = osfgets(buf, len, fp); if (ret == 0) return 0; /* * if that moved us past the end of our segment, go back and * re-read the exact number of bytes remaining */ if ((unsigned long)osfpos(fp) > end) { /* seek back to where we started */ osfseek(fp, pos, OSFSK_SET); /* read the remaining bytes in the resource (up to buflen) */ size_t r = end - pos > len ? (long)len : (size_t)(end - pos); if (osfrb(fp, buf, r)) return 0; /* if that didn't fill the buffer, null-terminate it */ if (r < len) buf[r] = '\0'; } /* return the result */ return buf; } /* read bytes - returns 0 on success, non-zero on EOF or error */ virtual int readc(void *buf, size_t len) { /* if we're already past the end, we can't read anything */ unsigned long pos = osfpos(fp); if (pos >= end) return 0; /* limit the read to the available file size */ unsigned long limit = end - pos; if (len > limit) len = (size_t)limit; /* do the read */ return osfrbc(fp, buf, len); } /* write bytes - resource files are read-only */ virtual int write(const void *buf, size_t len) { return 1; } /* get the length of the file in bytes */ virtual long get_size() { return end - start; } /* get the current seek location */ virtual long get_pos() { return osfpos(fp) - start; } /* set the current seek location - 'mode' is an OSFSK_xxx mode */ virtual int seek(long ofs, int mode) { switch (mode) { case OSFSK_SET: do_set: /* check that 'ofs' is in range */ if (ofs < 0 || (unsigned long)ofs > end - start) return 1; /* seek to the offset */ return osfseek(fp, start + ofs, OSFSK_SET); case OSFSK_CUR: /* set the offset relative to the current position */ ofs += get_pos(); goto do_set; case OSFSK_END: /* set the offset relative to the end of the file */ ofs += (end - start); goto do_set; default: /* invalid mode */ return 1; } } /* flush - we're read-only, so this just returns success */ virtual int flush() { return 0; } /* close the underlying system resource */ virtual void close() { osfcls(fp); fp = 0; } protected: /* the underlying file */ osfildef *fp; /* the byte range within the resource file */ unsigned long start, end; }; /* ------------------------------------------------------------------------ */ /* * Read-only string data source. This takes a buffer in memory and * presents a read-only file interface to it. */ class CVmStringSource: public CVmDataSource { public: CVmStringSource(const char *mem, long len) { this->mem = mem; this->memlen = len; this->pos = 0; } virtual ~CVmStringSource() { } virtual CVmDataSource *clone(VMG_ const char * /*mode*/) { return new CVmStringSource(mem, memlen); } /* read bytes - returns 0 on success, non-zero on EOF or error */ virtual int read(void *buf, size_t len) { /* make sure there's enough data to satisfy the request */ if (pos + (long)len > memlen) return 1; /* copy the data */ memcpy(buf, mem + pos, len); /* advance the read pointer */ pos += len; /* success */ return 0; } /* read bytes - returns the number of bytes read; 0 means EOF or error */ virtual int readc(void *buf, size_t len) { /* limit the read to the available length */ long rem = memlen - pos; if ((long)len > rem) len = rem; /* copy the bytes */ memcpy(buf, mem + pos, len); /* advance the read pointer */ pos += len; /* return the amount read */ return (int)len; } /* read a line of text (fgets() semantics) */ virtual char *read_line(char *buf, size_t len) { /* if we have no bytes left, return null for EOF */ if (pos == memlen) return 0; /* read bytes until we encounter a CR, LF, or CR-LF */ char *dst = buf; while (pos < memlen && len > 0) { /* check for a line terminator */ if (mem[pos] == 10 || mem[pos] == 13) { /* * add a newline to the result (it's always '\n' in the * result buffer regardless of the type of newline in the * source) */ *dst++ = '\n'; --len; /* * if this is a CR-LF sequence, skip both characters; * otherwise just skip the single CR or LF */ ++pos; if (pos < memlen && mem[pos-1] == 13 && mem[pos] == 10) ++pos; /* we're done */ break; } /* copy this byte */ *dst++ = mem[pos++]; /* count it against the remaining buffer length */ --len; } /* add a null terminator if possible */ if (len != 0) *dst = '\0'; /* return the original buffer pointer */ return buf; } /* write bytes - we're read-only, so this is an error */ virtual int write(const void *buf, size_t len) { return 1; } /* get the length of the file in bytes */ virtual long get_size() { return memlen; } /* get the current seek location */ virtual long get_pos() { return pos; } /* set the current seek location - 'mode' is an OSFSK_xxx mode */ virtual int seek(long ofs, int mode) { switch (mode) { case OSFSK_SET: do_set: /* make sure it's in range */ if (ofs < 0 || ofs > memlen) return 1; /* set the new position */ pos = ofs; /* success */ return 0; case OSFSK_CUR: /* calculate the absolute position and go do the set */ ofs += pos; goto do_set; case OSFSK_END: /* calculate the absolute position and go do the set */ ofs += memlen; goto do_set; default: /* invalid mode */ return 1; } } /* flush - we're read-only, so this is a no-op; just return success */ virtual int flush() { return 0; } /* close the underlying system resource; there's nothing for us to do */ virtual void close() { } protected: /* our memory buffer, and the number of bytes in the buffer */ const char *mem; long memlen; /* current read position */ long pos; }; /* ------------------------------------------------------------------------ */ /* * Private string source - makes a private copy of a string buffer */ class CVmPrivateStringSource: public CVmStringSource { public: CVmPrivateStringSource(const char *mem, long len) : CVmStringSource(0, len) { this->bufobj = new CVmRefCntBuf(mem, len); this->mem = bufobj->buf_; } CVmPrivateStringSource(CVmRefCntBuf *bufobj, long len) : CVmStringSource(bufobj->buf_, len) { this->bufobj = bufobj; } CVmDataSource *clone(VMG_ const char * /*mode*/) { return new CVmPrivateStringSource(bufobj, memlen); } ~CVmPrivateStringSource() { bufobj->release_ref(); } protected: CVmRefCntBuf *bufobj; }; /* ------------------------------------------------------------------------ */ /* * Memory data source. This automatically allocates additional buffer * space as needed. */ /* page block for a memory source */ struct CVmMemorySourceBlock { CVmMemorySourceBlock(long ofs) { this->ofs = ofs; nxt = 0; } /* block length */ static const int BlockLen = 4096; /* next block in the list */ CVmMemorySourceBlock *nxt; /* byte offset in the overall stream of the start of this block */ long ofs; /* block buffer */ char buf[BlockLen]; }; /* page list for a memory source */ class CVmMemorySourceList: public CVmRefCntObj { public: static const int BlockLen = CVmMemorySourceBlock::BlockLen; CVmMemorySourceList(long init_len) { /* no blocks yet */ first_block_ = last_block_ = 0; /* always allocate at least the initial block */ add_block(); init_len -= BlockLen; /* set the seek position to the start of the first block */ cur_block_ = first_block_; cur_block_ofs_ = 0; /* add more blocks until we've satisfied the length request */ for ( ; init_len > 0 ; init_len -= BlockLen) add_block(); /* the file is currently empty */ len_ = 0; } /* read bytes */ int read(void *buf, size_t len) { /* * if the request would take us past the current content length, * fail immediately, since we know we can't satisfy the request */ if (cur_block_->ofs + cur_block_ofs_ + (long)len > len_) return 1; /* * do the read; return success == 0 if the amount read exactly * matches the requested length */ return (size_t)readc(buf, len) != len; } /* read bytes */ int readc(void *buf0, size_t len) { /* get the buffer pointer as a character pointer */ char *buf = (char *)buf0; /* we haven't ready anything yet */ size_t bytes_read = 0; /* limit the length to the remaining bytes in the object */ long rem = len_ - get_pos(); if ((long)len > rem) len = (size_t)rem; /* keep going until we satisfy the request */ while (len != 0) { /* figure how much we can read from the current block */ size_t rem = BlockLen - cur_block_ofs_; size_t cur = (len < rem ? len : rem); /* read that much */ if (cur != 0) { /* copy bytes to the output buffer */ memcpy(buf, cur_block_->buf + cur_block_ofs_, cur); /* move past the bytes in the current block */ cur_block_ofs_ += cur; /* adjust our request counters past this chunk */ len -= cur; buf += cur; /* count it in the total read so far */ bytes_read += cur; } /* if that satisfies the request, we're done */ if (len == 0) break; /* if this is the last block, we're done */ if (cur_block_->nxt == 0) return bytes_read; /* advance to the start of the next block */ cur_block_ = cur_block_->nxt; cur_block_ofs_ = 0; } /* return the amount we managed to read */ return bytes_read; } /* write bytes */ int write(const void *buf0, size_t len) { /* get the buffer pointer as a character pointer */ const char *buf = (const char *)buf0; /* keep going until we satisfy the request */ for (;;) { /* figure how much we can write to the current block */ size_t rem = BlockLen - cur_block_ofs_; size_t cur = (len < rem ? len : rem); /* write that much */ if (cur != 0) { memcpy(cur_block_->buf + cur_block_ofs_, buf, cur); cur_block_ofs_ += cur; len -= cur; buf += cur; } /* if that satisfied the request, we're done */ if (len == 0) break; /* if this is the last block, add another */ if (cur_block_->nxt == 0) { /* add the block */ add_block(); /* if that didn't work, fail */ if (cur_block_->nxt == 0) return 1; } /* advance to the start of the next block */ cur_block_ = cur_block_->nxt; cur_block_ofs_ = 0; } /* * if the seek pointer is past the current content length, we've * written past the old end of the file and thus expanded the file * - note the new length */ if (cur_block_->ofs + cur_block_ofs_ > len_) len_ = cur_block_->ofs + cur_block_ofs_; /* success */ return 0; } /* get the length of the stream's contents */ long get_size() { return len_; } /* get the current seek offset */ long get_pos() { /* * figure the seek position as the current block's base offset plus * the current offset within that block */ return cur_block_->ofs + cur_block_ofs_; } /* set the seek offset */ int seek(long pos, int mode) { /* figure the absolute position based on the mode */ switch (mode) { case OSFSK_SET: /* from the beginning - use pos as given */ break; case OSFSK_CUR: /* from the current position */ pos += get_pos(); break; case OSFSK_END: /* from the end */ pos += get_size(); break; default: /* other modes are invalid */ return 1; } /* limit the position to the file's bounds */ if (pos < 0) { /* at start of file */ pos = 0; } else if (pos >= len_) { /* * At end of file. This is a special case for setting the * block pointer, because we could be positioned at the last * byte of the last block, which we won't find in our scan. * Explicitly set the position here. */ pos = len_; cur_block_ = last_block_; cur_block_ofs_ = pos - last_block_->ofs; /* we've set the block position, so skip the usual search */ return 0; } /* find the block containing the seek offset */ CVmMemorySourceBlock *b; for (b = first_block_ ; b != 0 ; b = b->nxt) { /* if the offset is within this block, we're done */ if (pos >= b->ofs && pos < b->ofs + BlockLen) { /* set the current pointers */ cur_block_ = b; cur_block_ofs_ = pos - b->ofs; /* stop scanning */ break; } } /* success */ return 0; } protected: ~CVmMemorySourceList() { /* delete our block list */ CVmMemorySourceBlock *cur, *nxt; for (cur = first_block_ ; cur != 0 ; cur = nxt) { nxt = cur->nxt; delete cur; } } /* add a block to the end of the list */ void add_block() { long ofs = last_block_ == 0 ? 0 : last_block_->ofs + BlockLen; CVmMemorySourceBlock *b = new CVmMemorySourceBlock(ofs); if (last_block_ != 0) last_block_->nxt = b; else first_block_ = b; last_block_ = b; } /* current seek block and offset */ CVmMemorySourceBlock *cur_block_; long cur_block_ofs_; /* total file length */ long len_; /* head/tail of block list */ CVmMemorySourceBlock *first_block_; CVmMemorySourceBlock *last_block_; }; /* * Paged memory source */ class CVmMemorySource: public CVmDataSource { public: CVmMemorySource(long init_len) { /* create our underlying page list */ pl = new CVmMemorySourceList(init_len); } /* clone the data source */ CVmDataSource *clone(VMG_ const char * /*mode*/) { /* return a new data source wrapper for our page list */ return new CVmMemorySource(pl); } ~CVmMemorySource() { /* release our underlying page list */ pl->release_ref(); } /* read bytes */ virtual int read(void *buf, size_t len) { return pl->read(buf, len); } /* read bytes */ virtual int readc(void *buf, size_t len) { return pl->readc(buf, len); } /* write bytes */ virtual int write(const void *buf, size_t len) { return pl->write(buf, len); } /* get the length of the stream's contents */ virtual long get_size() { return pl->get_size(); } /* get the current seek offset */ virtual long get_pos() { return pl->get_pos(); } /* set the seek offset */ virtual int seek(long pos, int mode) { return pl->seek(pos, mode); } /* flush - we don't buffer, so this does nothing */ virtual int flush() { return 0; } /* * close - we don't have underlying system resources (other than * our memory buffers), so there's nothing to do here */ virtual void close() { } protected: CVmMemorySource(CVmMemorySourceList *pl) { pl->add_ref(); this->pl = pl; } /* our underlying page list */ CVmMemorySourceList *pl; }; #endif /* VMDATASRC_H */ ��������������������������qtads-2.1.7/tads3/vmdate.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000374624�12650170723�0015662�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2012 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmdate.cpp - CVmObjDate object Function Implements the Date intrinsic class Notes Modified 01/23/12 MJRoberts - Creation */ #include <stdlib.h> #include <string.h> #include <assert.h> #include <time.h> #include <math.h> #include <ctype.h> #include <limits.h> #include <stdarg.h> #include "t3std.h" #include "vmdate.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmbignum.h" #include "vmbif.h" #include "vmmeta.h" #include "vmstack.h" #include "vmrun.h" #include "vmstr.h" #include "vmlst.h" #include "vmvec.h" #include "vmlookup.h" #include "utf8.h" #include "vmuni.h" #include "vmtz.h" #include "vmtzobj.h" /* ------------------------------------------------------------------------ */ /* * Round a double to int32 */ static int32_t round_int32(double d) { double i; return (int32_t)(modf(d, &i) >= 0.5 ? (d >= 0 ? ceil(d) : floor(d)) : (d < 0 ? ceil(d) : floor(d))); } /* ------------------------------------------------------------------------ */ /* * get the current date/time in UTC */ void caldate_t::now(int32_t &dayno, int32_t &daytime) { /* get the current date and time in Unix Epoch format */ os_time_t s; long ns; os_time_ns(&s, &ns); /* * 's' is the number of seconds since the Unix Epoch, 1/1/1970 UTC. * Get our day number: divide by seconds per day to get days past the * Unix Epoch, then adjust to our Epoch (3/1/0000 UTC) by adding the * number of days from our Epoch to the Unix Epoch. */ dayno = (int32_t)(s/(24*60*60) + caldate_t::UNIX_EPOCH_DAYNO); /* * convert 's' to the time of day, adding in the fractional portion * from 'ns' but only keeping millisecond precision */ daytime = (uint32_t)((s % (24*60*60))*1000 + ns/1000000); } /* * normalize a date/time value: if the daytime value is before 0000 hours * or after 2400 hours, bring it back within bounds and adjust the day * number */ void caldate_t::normalize(int32_t &dayno, int32_t &daytime) { /* if we're outside the 0000-2400 bounds, adjust the time and day */ ldiv_t q = ldiv(daytime, 24*60*60*1000); dayno += (int32_t)q.quot; if ((daytime = (int32_t)q.rem) < 0) { daytime += 24*60*60*1000; dayno -= 1; } } /* ------------------------------------------------------------------------ */ /* * Locale string retrieval */ class CVmDateLocale { public: CVmDateLocale(VMG0_) { /* get the state Vector from the Date static class state holder */ const vm_val_t *val = vm_classobj_for(CVmObjDate)->get_class_state(); vecid = val->typ == VM_OBJ ? val->val.obj : VM_INVALID_OBJ; vec = vm_objid_cast(CVmObjVector, vecid); } /* get a locale string */ const char *get(VMG_ size_t &len, int idx) { /* if it's negative, it's invalid */ if (idx < 0) { len = 0; return 0; } /* try getting it from the state vector */ if (vec != 0 && idx < (int)vec->get_element_count()) { /* get the element */ vm_val_t ele; vec->get_element(idx, &ele); /* if it's a string, return it */ const char *str = ele.get_as_string(vmg0_); if (str != 0) { len = vmb_get_len(str); return str + VMB_LEN; } } /* we didn't find it in the state vector; return the default */ if (idx < ndefaults) { len = strlen(defaults[idx]); return defaults[idx]; } /* invalid entry */ len = 0; return 0; } /* * Index a locale list. If 'sticky' is true, the last item actually * present in the list will be returned if the index is past the end of * the list. */ const char *index_list(VMG_ size_t &itemlen, int item, int idx, int sticky) { /* get the item */ size_t len; const char *str = get(vmg_ len, item); /* if we didn't find it, return failure */ if (str == 0) { itemlen = 0; return 0; } /* skip items (delimited by commas) until we reach the target index */ for ( ; idx > 0 && len != 0 ; --idx, ++str, --len) { /* find the next comma */ const char *comma = lib_strnchr(str, len, ','); if (comma == 0) { /* * No more commas, so the index is past the end of the * list. If 'sticky' is true, use the last item; otherwise * return "not found". */ if (sticky) break; else { itemlen = 0; return 0; } } /* advance to the comma */ len -= comma - str; str = comma; } /* we have the item; measure its length, up to the next '=' or ',' */ const char *p; for (p = str, itemlen = 0 ; len != 0 && *p != '=' && *p != ',' ; --len, ++p, ++itemlen) ; /* return the item pointer */ return str; } /* set a locale string */ void set(VMG_ CVmUndo *undo, int idx, const vm_val_t *val) { /* if we don't have a vector, create one */ if (vec == 0) { /* create the vector */ vecid = CVmObjVector::create(vmg_ FALSE, ndefaults); vec = vm_objid_cast(CVmObjVector, vecid); /* initialize all elements to nil */ vm_val_t nil; nil.set_nil(); for (int i = 0 ; i < ndefaults ; ++i) vec->append_element(vmg_ vecid, &nil); /* store the vector in the class object's state holder */ vm_val_t val; val.set_obj(vecid); vm_classobj_for(CVmObjDate)->set_class_state(&val); } /* if it's out of bounds, it's an error */ if (idx < 0 || idx >= ndefaults) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* set the specified element */ vec->set_element_undo(vmg_ vecid, idx, val); } /* defaults */ static const char *defaults[]; static const int ndefaults; /* our state Vector */ vm_obj_id_t vecid; CVmObjVector *vec; }; /* list of default locale settings */ const char *CVmDateLocale::defaults[] = { /* 0 - month full names */ "January,February,March,April,May,June,July,August," "September,October,November,December", /* 1 - month name abbreviations */ "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep=Sept,Oct,Nov,Dec", /* 2 - weekday full names */ "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", /* 3 - weekday abbreviations */ "Sun,Mon,Tue,Wed,Thu,Fri,Sat", /* 4 - AM/PM indicators (upper, lower) */ "AM=A.M.,PM=P.M.,am,pm", /* 5 - era (AD/BC) indicators */ "AD=A.D.=CE=C.E.,BC=B.C.=BCE=B.C.E.", /* 6 - parsing format filter - 'us' or 'eu' */ "us", /* 7 - ordinal suffixes */ "st,nd,rd,th,st,nd,rd", /* 8 - timestamp format string */ "%a %b %#d %T %Y", /* 9 - time format string */ "%H:%M:%S", /* 10 - default date format */ "%m/%d/%Y", /* 11 - short date format */ "%m/%d/%y", /* 12 - 12-hour clock format */ "%#I:%M:%S %P", /* 13 - 24-hour clock format */ "%H:%M", /* 14 - 24-hour clock with seconds */ "%H:%M:%S" }; const int CVmDateLocale::ndefaults = countof(CVmDateLocale::defaults); /* locale indices */ static const int LC_MONTH = 0; static const int LC_MON = 1; static const int LC_WEEKDAY = 2; static const int LC_WKDY = 3; static const int LC_AMPM = 4; static const int LC_ERA = 5; static const int LC_PARSE_FILTER = 6; static const int LC_ORDSUF = 7; static const int LC_FMT_TIMESTAMP = 8; static const int LC_FMT_TIME = 9; static const int LC_FMT_DATE = 10; static const int LC_FMT_SHORTDATE = 11; static const int LC_FMT_12HOUR = 12; static const int LC_FMT_24HOUR = 13; static const int LC_FMT_24HOURSECS = 14; /* * Figure the ordinal suffix index (in LC_ORDSUF) for a given number. The * suffix indices are: * *. 0 = 1st *. 1 = 2nd *. 2 = 3rd *. 3 = Nth *. 4 = X1st (e.g., 21st, 31st, 121st) *. 5 = X2nd *. 6 = X3rd */ static int ordinal_index(int n) { /* get the last digit */ int lastdig = n % 10; /* * if it's in the teens, or the last digit is 0 or 4-9, use the generic * Nth suffix */ if (lastdig == 0 || lastdig >= 4 || (n > 10 && n < 20)) return 3; /* if it's in the 1-3 range, use 1st/2nd/3rd; otherwise use X1st/etc */ return lastdig + (n < 10 ? -1 : 3); } /* ------------------------------------------------------------------------ */ /* * Allocate an extension structure */ vm_date_ext *vm_date_ext::alloc_ext(VMG_ CVmObjDate *self) { /* calculate how much space we need */ size_t siz = sizeof(vm_date_ext); /* allocate the memory */ vm_date_ext *ext = (vm_date_ext *)G_mem->get_var_heap()->alloc_mem( siz, self); /* return the new extension */ return ext; } /* ------------------------------------------------------------------------ */ /* * CVmObjDate object statics */ /* metaclass registration object */ static CVmMetaclassDate metaclass_reg_obj; CVmMetaclass *CVmObjDate::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjDate::*CVmObjDate::func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjDate::getp_undef, /* 0 */ &CVmObjDate::getp_parseDate, /* 1 */ &CVmObjDate::getp_parseJulianDate, /* 2 */ &CVmObjDate::getp_formatDate, /* 3 */ &CVmObjDate::getp_formatJulianDate, /* 4 */ &CVmObjDate::getp_compareTo, /* 5 */ &CVmObjDate::getp_getDate, /* 6 */ &CVmObjDate::getp_getJulianDay, /* 7 */ &CVmObjDate::getp_getJulianDate, /* 8 */ &CVmObjDate::getp_getISOWeekDate, /* 9 */ &CVmObjDate::getp_getTime, /* 10 */ &CVmObjDate::getp_addInterval, /* 11 */ &CVmObjDate::getp_findWeekday, /* 12 */ &CVmObjDate::getp_setLocaleInfo /* 13 */ }; /* static property indices */ const int VMDATE_PARSEDATE = 1; const int VMDATE_PARSEJULIANDATE = 2; const int VMDATE_SETLOCALINFO = 13; /* ------------------------------------------------------------------------ */ /* * Construction */ CVmObjDate::CVmObjDate(VMG_ int32_t dayno, uint32_t daytime) { /* allocate our extension structure */ vm_date_ext *ext = vm_date_ext::alloc_ext(vmg_ this); ext_ = (char *)ext; /* fill it in */ ext->dayno = dayno; ext->daytime = daytime; } /* ------------------------------------------------------------------------ */ /* * Get my date and time in the given local time zone */ const char *CVmObjDate::get_local_time( int32_t &dayno, int32_t &daytime, int32_t &ofs, CVmTimeZone *tz) const { /* get my UTC date and time */ dayno = get_ext()->dayno; daytime = get_ext()->daytime; /* convert to my local time zone */ return tz->utc_to_local(dayno, daytime, ofs); } /* ------------------------------------------------------------------------ */ /* * Date/time parser */ /* string capture item */ struct date_parse_string { date_parse_string() { p = 0; len = 0; } void set(const char *p, size_t len) { this->p = p; this->len = len; } const char *p; size_t len; }; /* * Parse results structure. This keeps track of which information we've * filled in so far. */ struct date_parse_result { date_parse_result(multicaldate_t *cal) { /* remember my calendar interface */ this->cal = cal; /* * Initialize most elements to invalid, out-of-bounds values to * indicate they haven't been set yet. INT_MIN satisfies this for * all of these, so for the sake of clarity use it for all of them. * For the month and day, only positive values are meaningful, so * any negative value will suffice as the "invalid" token. The * hour, minute, second, and milliseconds must be non-negative to * be valid, so any negative value is invalid. The time zone * offset must be between -24 hours (-24*60 minutes) and +24 hours * (+24*60 minutes), so a large negative value is invalid. The * year can mathematically be any integer value, but the ultimate * representation of the date can only hold a 32-bit day number, * which limits the year number to about +/-5 million; so INT_MIN, * which is about minus 2 billion on a 32-bit machine, is a * negative year that we can't represent. (This shouldn't be a big * loss; prehistoric dates aren't very meaningful anyway, as there * are by definition no historical records to correlate with * calculated dates.) The timezone pointer is different in that * it's a pointer, so null is the natural 'undefined' there. */ era = 0; yy = INT_MIN; yy_needs_century = FALSE; mm = INT_MIN; dd = INT_MIN; ampm = 0; hh = INT_MIN; mi = INT_MIN; ss = INT_MIN; ms = INT_MIN; tzofs = INT_MIN; w = INT_MIN; tz = 0; } date_parse_result(date_parse_result &src) { memcpy(this, &src, sizeof(*this)); } /* translate a time from local to UTC using our parsed timezone info */ void local_to_utc(int32_t &dayno, int32_t &daytime) { /* * If we have a specific timezone offset, use the offset; otherwise * translate via the time zone object. * * Note that it's possible for a parsed date to specify both an * offset and a zone. If a seasonal zone abbreviation like "PDT" * is used, it tells us both the timezone (America/Los_Angeles in * the case of PDT) and the offset (-7 hours for PDT). The offset * overrides the implied offset for the timezone because it * specifies a particular seasonal time setting; if only the * timezone is specified, we use the wall clock time for the zone. * For example, "12/1/2000 3:00 America/Los_Angeles" has a -8 hour * offset, since Pacific Standard Time is in effect in this zone in * December, whereas "12/1/2000 3:00 PDT" has a -7 hour offset, * since it's specfically a PDT time even though the wall clock * time in Los Angeles in December is Pacific Standard Time. */ if (has_tzofs()) { /* we have an offset - it overrides the time zone */ daytime -= tzofs; caldate_t::normalize(dayno, daytime); } else { /* no offset, so use the timezone */ tz->local_to_utc(dayno, daytime); } } /* translate from UTC to local using our parsed timezone info */ void utc_to_local(int32_t &dayno, int32_t &daytime) { if (has_tzofs()) { daytime += tzofs; caldate_t::normalize(dayno, daytime); } else { int32_t ofs; tz->utc_to_local(dayno, daytime, ofs); } } /* * set a default date - this sets any undefined date components * individually from the given date */ void def_date(int32_t dayno) { /* convert the day number to a calendar date */ cal->set_dayno(dayno); /* apply the era to the year, if both are present */ if (era != 0 && has_year()) { switch (era) { case 1: /* * astronomical '+' notation - the internal year number * matches the nominal year */ break; case 2: /* * AD - the internal year matches the nominal year, but an * explicit AD overrides the need for a century; e.g., AD * 87 is pretty clear that we're talking about an * historical date in the first century */ yy_needs_century = FALSE; break; case -1: /* * astronomical '-' notation - the internal year number * matches the nominal negative year number, since we use * the same notation internally */ yy = -yy; break; case -2: /* BC - 1 BC is internal year 0, 2 BC is year -1, etc */ yy = -yy + 1; yy_needs_century = FALSE; break; } } /* set the default year, month, and day from the given date */ def_year(cal->y()); def_month(cal->m()); def_day(cal->d()); /* * If the year was specified without a century, choose the century * such that it yields the year nearest to the current year. This * is equivalent to finding the year within 50 years of the current * year. For 2012, this makes the range 1962 to 2061 - so '62 is * taken to be 1962, '85 is 1985, but '15 is 2015, and 61 is 2061. */ if (yy_needs_century) { yy += ((cal->y() / 100) * 100); if (yy < cal->y() - 50) yy += 100; if (yy > cal->y() + 49) yy -= 100; } } /* * set a default time - this sets any undefined time components * individually from the given time */ void def_time(int m, int h, int s, int ms) { /* apply the PM indicator to the time, if present and in range */ if (ampm == 1 && hh == 12) hh = 0; else if (ampm == 2 && hh < 12) hh += 12; def_hour(h); def_minute(m); def_second(s); def_msec(ms); } /* compute the day number */ int32_t dayno() const { /* if there's an ISO weekday, convert it to a calendar date */ if (has_iso_week() && has_year()) { /* * Calculate the "correction factor" - this is the ISO weekday * number of January 4 of the year, plus 3. Note that this is * explicitly defined as Jan 4 *Gregorian* by the standard, so * this isn't relative to the calendar we're parsing for. */ caldate_t jan4(yy, 1, 4); int corr = jan4.iso_weekday() + 3; /* * Set the date to "January 'ord'", where 'ord' is the ISO * ordinal day number calculated from the weekday and * correction factor. This won't necessarily be a valid date * in January, but caldate_t handles overflows in the day by * carrying into the month and year as needed. */ caldate_t cd(yy, 1, w - corr); return cd.dayno(); } else { /* set the calendar date to our year, month, and day */ cal->set_date(yy, mm, dd); /* return the day number from the calendar date */ return cal->dayno(); } } /* compute the time of day */ int32_t daytime() const { return hh*60*60*1000 + mi*60*1000 + ss*1000 + ms; } /* * Set defaults for the various components. These will set the * component only if it hasn't been set to a valid value already. */ void def_year(int y) { if (!has_year()) yy = y; } void def_month(int m) { if (!has_month()) mm = m; } void def_day(int d) { if (!has_day()) dd = d; } void def_hour(int h) { if (!has_hour()) hh = h; } void def_minute(int m) { if (!has_minute()) mi = m; } void def_second(int s) { if (!has_second()) ss = s; } void def_msec(int m) { if (!has_msec()) ms = m; } void def_tzofs(int t) { if (!has_tzofs()) tzofs = t; } void def_tz(CVmTimeZone *z) { if (!has_tz()) tz = z; } /* default to the local time zone */ void def_local_tz(VMG0_) { if (!has_tz()) tz = G_tzcache->get_local_zone(vmg0_); } /* determine if the various elements are set */ int has_era() const { return era != 0; } int has_year() const { return yy != INT_MIN; } int has_month() const { return mm != INT_MIN; } int has_day() const { return dd != INT_MIN; } int has_iso_week() const { return w != INT_MIN; } int has_ampm() const { return ampm != 0; } int has_hour() const { return hh != INT_MIN; } int has_minute() const { return mi != INT_MIN; } int has_second() const { return ss != INT_MIN; } int has_msec() const { return ms != INT_MIN; } int has_tzofs() const { return tzofs != INT_MIN; } int has_tz() const { return tz != 0; } /* the calendar we're using to parse the date */ multicaldate_t *cal; /* era: 1 = '+', 2 = AD, -1 = '-', -2 = BC */ int era; /* date - year, month, day */ int yy; int mm; int dd; /* * ISO 8601 week+day - this is the value W*7+D from a date in ISO 8601 * format, where W is the week number (1-53) and D is the day of week * number (1-7). */ int w; /* flag: the year was specified without a century */ int yy_needs_century; /* time - hour, minute, second, milliseconds on 24-hour clock */ int ampm; int hh; int mi; int ss; int ms; /* timezone */ CVmTimeZone *tz; /* time zone by GMT offset, in milliseconds */ int tzofs; /* string capture items for the various components */ date_parse_string pera, pyy, pmm, pdd, pdoy, pw; date_parse_string pampm, phh, pmi, pss, pms, punix, ptz; }; /* match a format element in a template string */ static inline int match_fmt(const char *fmt, const char *fmtend, const char *ref) { size_t len = strlen(ref); return (len == (size_t)(fmtend - fmt) && memcmp(ref, fmt, len) == 0); } /* * match a digit string; the template gives pairs of digits with the bounds * to match - e.g., "09" matches one digit from 0 to 9, "12" matches one * digit from 1 to 2, "0209" matches 00 to 29, etc. We can also match ',' * to a period or comma. */ static int match_digits(int &acc, const char *&str, size_t &len, const char *tpl, date_parse_string &capture) { /* set up a private pointer to the string */ const char *p = str; size_t rem = len; /* match each template pair */ acc = 0; for ( ; *tpl != '\0' ; tpl += 2, ++p, --rem) { /* check for the special match to a period or comma */ if (*tpl == ',') { /* back up to make up for the normal double increment */ --tpl; /* allow it to match, but don't require it */ if (rem != 0 && (*p == ',' || *p == '.')) continue; /* it doesn't match; simply say on the same input character */ --p, ++rem; continue; } /* if this input char isn't a digit, we don't have a match */ if (rem == 0 || !is_digit(*p)) return FALSE; /* if this digit is out of range, it's not a match */ if (*p < tpl[0] || *p > tpl[1]) return FALSE; /* matched this digit - add it to the accumulator */ acc *= 10; acc += value_of_digit(*p); } /* set the capture data */ capture.p = str; capture.len = p - str; /* advance the caller's string pointers past the match */ str = p; len = rem; /* return success */ return TRUE; } /* * match literal text */ static int match_lit(const char *&str, size_t &len, const char *lit, size_t litlen, date_parse_string &capture) { /* if there's no literal, there's no match */ if (lit == 0) return 0; /* if the template starts with "^", it means do not fold case */ int fold_case = TRUE; if (litlen != 0 && *lit == '^') fold_case = FALSE, ++lit, --litlen; /* try matching the template text */ size_t matchlen; if (fold_case) { /* compare the leading substring of 'str' with case folding */ if (t3_compare_case_fold(lit, litlen, str, len, &matchlen) != 0) return FALSE; } else if (len >= litlen) { /* compare exactly as given with no case folding */ if (memcmp(lit, str, litlen) != 0) return FALSE; matchlen = litlen; } else { /* it's too short to match */ return FALSE; } /* get a pointer to the next character after the end of the match */ utf8_ptr p((char *)str + matchlen); size_t rem = len - matchlen; /* make sure we ended on a suitable boundary */ if (rem != 0 && litlen != 0) { /* get the last character of the matched text */ wchar_t last = p.getch_before(1); wchar_t next = p.getch(); /* if we ended on alphabetic, make sure the next is non-alpha */ if (isalpha(last) && isalpha(next)) return FALSE; /* if we ended on a number, maek sure the next is non-numeric */ if (isdigit(last) && isdigit(next)) return FALSE; } /* set the capture data */ capture.p = str; capture.len = p.getptr() - str; /* advance the caller's string pointers past the match */ str = p.getptr(); len = rem; /* return success */ return TRUE; } static int match_lit(const char *&str, size_t &len, const char *lit, date_parse_string &capture) { return match_lit(str, len, lit, lit != 0 ? strlen(lit) : 0, capture); } /* * Match literal text from a list. The list is given as a string of * comma-separated elements, ending with a semicolon. Elements can have * aliases specified with '=', as in "sep=sept, ...". Spaces after commas * are ignored. */ static int match_list(int &acc, const char *&str, size_t &len, const char *lst, size_t lstlen, date_parse_string &capture) { /* no matches yet */ int bestlen = 0; int bestidx = 0; /* skip leading spaces */ for ( ; lstlen != 0 && is_space(*lst) ; ++lst, --lstlen) ; /* keep going until we're out of list */ for (int idx = 1 ; lstlen != 0 ; ) { /* find the next delimiter */ const char *p = lst; size_t rem = lstlen; for ( ; rem != 0 && *p != '=' && *p != ',' && *p != ';' ; ++p, --rem) ; /* try matching this text */ const char *curstr = str; size_t curlen = len; date_parse_string part; if (p - lst != 0 && match_lit(curstr, curlen, lst, p - lst, part)) { /* if this is the best one so far, remember it */ if (curstr - str > bestlen) { bestlen = curstr - str; bestidx = idx; } } /* * If we're at an '=', an alias for this same item follows, so keep * going without upping the index. If we're at a comma, we're on * to the next item. If we're at a ';' or the end of the string, * it's the end of the list, so we've failed to find a match. */ if (rem == 0 || *p == ';') { /* end of the list */ break; } else if (*p == ',') { /* next list item follows - up the index */ ++idx; /* skip the comma and any spaces before the next item */ for (++p, --rem ; rem != 0 && is_space(*p) ; ++p, --rem) ; } else if (*p == '=') { /* alias - just skip the '=' */ ++p, --rem; } /* on to the next item */ lst = p; lstlen = rem; } /* if we found a match, return it */ if (bestlen != 0) { capture.p = str; capture.len = bestlen; acc = bestidx; str += bestlen; len -= bestlen; return TRUE; } else return FALSE; } /* * match a timezone offset - [GMT] +/- hh [:][mi] [:][ss] */ static int match_tzofs(VMG_ date_parse_result *res, const char *&str, size_t &len) { const char *p = str; size_t rem = len; /* first, skip "gmt" if present */ date_parse_string part; int gmtlit = match_lit(p, rem, "^GMT", part); /* get the sign */ if (rem == 0 || (*p != '+' && *p != '-')) return FALSE; int s = (*p == '+' ? 1 : -1); /* skip the sign */ ++p, --rem; /* * Get the "hh". If the "GMT" literal was present, allow a single * digit; otherwise require the two-digit format. */ int hh; if (!match_digits(hh, p, rem, "0509", part) && (!gmtlit || !match_digits(hh, p, rem, "09", part))) return FALSE; /* * check for ":" - if present, the minutes must be present, otherwise * they're optional */ int mi = 0; if (rem != 0 && *p == ':') { /* skip the ":", then require the minutes */ ++p, --rem; if (!match_digits(mi, p, rem, "0509", part)) return FALSE; } else { /* no ":", so the minuts are optional */ match_digits(mi, p, rem, "0509", part); } /* likewise, check for ":ss" */ int ss = 0; if (rem != 0 && *p == ':') { ++p, --rem; if (!match_digits(ss, p, rem, "0509", part)) return FALSE; } else { match_digits(ss, p, rem, "0509", part); } /* success - figure the offset in seconds */ int32_t ofs = s * ((hh*60*60) + (mi*60) + ss); /* look up the zone by offset */ res->tz = G_tzcache->get_gmtofs_zone(vmg_ ofs); /* also set the explicit offset value (in milliseconds) */ res->tzofs = ofs * 1000; /* set the capture data */ res->ptz.p = str; res->ptz.len = p - str; /* advance the caller's vars past the parsed tzofs and return success */ str = p; len = rem; return TRUE; } /* * Match a timezone by name */ static int match_tzname(VMG_ date_parse_result *res, const char *&str, size_t &len) { /* check for a zone name string ("America/Los_Angeles", etc) */ if (len != 0 && isalpha(*str)) { const char *p = str; size_t rem = len; for ( ; rem != 0 ; ++p, --rem) { /* allow letters, or / or _ if followed by a letter */ if (isalpha(*p) || ((*p == '_' || *p == '/') && rem > 1 && isalpha(p[1]))) continue; /* otherwise we're done */ break; } /* if it contains a slash, look up the zoneinfo database name */ CVmTimeZone *tz = 0; if (lib_strnchr(str, p - str, '/') != 0) { if ((tz = G_tzcache->get_db_zone(vmg_ str, p - str)) != 0) { /* success - this is a valid timezone name */ res->ptz.p = str; res->ptz.len = p - str; str = p; len = rem; res->tz = tz; return TRUE; } } /* didn't find a zone name; try an abbreviation */ int32_t tzofs; tz = G_tzcache->get_zone_by_abbr(vmg_ &tzofs, str, p - str); if (tz != 0) { /* * Success - we found an abbreviation; this usually gives us * both a timezone and a specific offset, since an abbreviation * is specific to standard or daylight time. However, some * abbreviations don't specify an offset, since they're used in * the same zone for both standard and daylight time (e.g., * Australian EST); these are flagged by tzofs coming back as * INT32MINVAL. */ res->ptz.p = str; res->ptz.len = p - str; str = p; len = rem; res->tz = tz; if (tzofs != INT32MINVAL) res->tzofs = tzofs; return TRUE; } } /* not a time zone */ return FALSE; } /* * Parse a date/time string against a given template */ int CVmObjDate::parse_string_fmt(VMG_ date_parse_result *res, const char *&str, size_t &len, const char *fmt, size_t fmtl, CVmDateLocale *lc) { /* skip leading spaces and tabs in the source string */ for ( ; len != 0 && is_space(*str) ; ++str, --len) ; /* run through the format string */ while (len != 0) { /* skip leading spaces in the format string */ for ( ; fmtl != 0 && *fmt == ' ' ; --fmtl, ++fmt) ; /* stop if we're at the end of the string */ if (fmtl == 0) break; /* find the next space or end of string */ const char *sp = lib_strnchr(fmt, fmtl, ' '); sp = (sp != 0 ? sp : fmt + fmtl); /* parse the type */ switch (fmt[0]) { case 'd': if (match_fmt(fmt, sp, "d")) { /* one- or two-digit day of month */ int acc; if (match_digits(acc, str, len, "0209", res->pdd) || match_digits(acc, str, len, "3301", res->pdd) || match_digits(acc, str, len, "09", res->pdd)) res->dd = acc; else return FALSE; } else if (match_fmt(fmt, sp, "dd")) { /* two-digit day of month */ int acc; if (match_digits(acc, str, len, "0009", res->pdd) || match_digits(acc, str, len, "1209", res->pdd) || match_digits(acc, str, len, "3301", res->pdd)) res->dd = acc; else return FALSE; } else if (match_fmt(fmt, sp, "ddth")) { /* one/two-digit day of month, plus optional ordinal suffix */ int acc; if (match_digits(acc, str, len, "0209", res->pdd) || match_digits(acc, str, len, "3301", res->pdd) || match_digits(acc, str, len, "09", res->pdd)) res->dd = acc; else return FALSE; /* get the locale ordinal for the digit */ size_t ordl; const char *ord = lc->index_list( vmg_ ordl, LC_ORDSUF, ordinal_index(acc), TRUE); /* compare it */ date_parse_string pdd; if (match_lit(str, len, ord, ordl, pdd)) { /* matched - add this into the capture data */ res->pdd.len += pdd.len; } } else if (match_fmt(fmt, sp, "doy")) { /* day of year */ int acc; if (match_digits(acc, str, len, "000019", res->pdoy) || match_digits(acc, str, len, "001909", res->pdoy) || match_digits(acc, str, len, "120909", res->pdoy) || match_digits(acc, str, len, "330509", res->pdoy) || match_digits(acc, str, len, "336606", res->pdoy)) { /* * set the date to January 'doy' - this isn't * necessarily a valid calendar date in January, but * caldate_t handles overflows in the day by carrying * to the month and year as needed */ res->mm = 1; res->dd = acc; } } else goto other; break; case 'm': if (match_fmt(fmt, sp, "month")) { /* * Month by long name, short name, or Roman numeral. Get * the locale strings for the month names; the Roman * numerals are fixed, so we can set up a static string for * those. */ size_t mm_long_len, mm_abbr_len; const char *mm_long = lc->get(vmg_ mm_long_len, LC_MONTH); const char *mm_abbr = lc->get(vmg_ mm_abbr_len, LC_MON); static const char *romans = "^I,^II,^III,^IV,^V,^VI,^VII,^VIII,^IX,^X,^XI,^XII;"; /* try each variation */ int acc; if (match_list(acc, str, len, mm_long, mm_long_len, res->pmm) || match_list( acc, str, len, mm_abbr, mm_abbr_len, res->pmm) || match_list( acc, str, len, romans, strlen(romans), res->pmm)) res->mm = acc; else return FALSE; /* specifying a month makes the default day the 1st */ res->def_day(1); } else if (match_fmt(fmt, sp, "mon")) { /* month by short name */ size_t mm_abbr_len; const char *mm_abbr = lc->get(vmg_ mm_abbr_len, LC_MON); int acc; if (match_list(acc, str, len, mm_abbr, mm_abbr_len, res->pmm)) res->mm = acc; else return FALSE; /* specifying a month makes the default day the 1st */ res->def_day(1); } else if (match_fmt(fmt, sp, "m")) { /* one- or two-digit month */ int acc; if (match_digits(acc, str, len, "0009", res->pmm) || match_digits(acc, str, len, "1102", res->pmm) || match_digits(acc, str, len, "09", res->pmm)) res->mm = acc; else return FALSE; /* specifying a month makes the default day the 1st */ res->def_day(1); } else if (match_fmt(fmt, sp, "mm")) { /* two-digit month */ int acc; if (match_digits(acc, str, len, "0009", res->pmm) || match_digits(acc, str, len, "1102", res->pmm)) res->mm = acc; else return FALSE; /* specifying a month makes the default day the 1st */ res->def_day(1); } else if (match_fmt(fmt, sp, "mi")) { /* two-digit minutes */ int acc; if (match_digits(acc, str, len, "0509", res->pmi)) res->mi = acc; else return FALSE; /* specifying the minute makes the default ss.frac zero */ res->def_second(0); res->def_msec(0); } else goto other; break; case 'W': if (match_fmt(fmt, sp, "W")) { /* * ISO week - a two-digit number giving the week of the * year, optionally followed by a day number */ int w; if (match_digits(w, str, len, "0019", res->pw) || match_digits(w, str, len, "1409", res->pw) || match_digits(w, str, len, "5503", res->pw)) { /* check for a weekday following */ int d = 1; if (len >= 2 && *str == '-' && strchr("1234567", str[1]) != 0) { d = value_of_digit(str[1]); str += 2, len -= 2; res->pw.len += 2; } else if (len >= 1 && strchr("1234567", str[0]) != 0) { d = value_of_digit(str[0]); str += 1, len -= 1; res->pw.len += 1; } /* matched */ res->w = w*7 + d; return TRUE; } else return FALSE; } else goto other; break; case 'y': if (match_fmt(fmt, sp, "y")) { /* one- to seven-digit year */ int acc; if (match_digits(acc, str, len, "09,090909,090909", res->pyy) || match_digits(acc, str, len, "090909,090909", res->pyy) || match_digits(acc, str, len, "0909,090909", res->pyy) || match_digits(acc, str, len, "09090909", res->pyy) || match_digits(acc, str, len, "090909", res->pyy)) res->yy = acc; else if (match_digits(acc, str, len, "0909", res->pyy) || match_digits(acc, str, len, "09", res->pyy)) { res->yy = acc; res->yy_needs_century = TRUE; if (res->yy >= 5879650 || res->yy <= -5879650) err_throw(VMERR_NUM_OVERFLOW); } else return FALSE; /* specifying the year makes the default date 1/1 */ res->def_month(1); res->def_day(1); } else if (match_fmt(fmt, sp, "yy")) { /* two-digit year; century comes from the reference year */ int acc; if (match_digits(acc, str, len, "0909", res->pyy)) { res->yy = acc; res->yy_needs_century = TRUE; } else return FALSE; /* specifying the year makes the default date 1/1 */ res->def_month(1); res->def_day(1); } else if (match_fmt(fmt, sp, "yyyy")) { /* four-digit year */ int acc; if (match_digits(acc, str, len, "09090909", res->pyy)) res->yy = acc; else return FALSE; /* specifying the year makes the default date 1/1 */ res->def_month(1); res->def_day(1); } else if (match_fmt(fmt, sp, "ye")) { /* * Year with optional era designator (AD/BC). The era can * come before or after the year, with spaces in between. */ size_t era_len; const char *era_lst = lc->get(vmg_ era_len, LC_ERA); int idx; int era = 0; if (match_list(idx, str, len, era_lst, era_len, res->pera)) { /* note the era - 2 for AD, -2 for BC */ res->era = era = (idx == 1 ? 2 : -2); /* allow spaces */ for ( ; len != 0 && isspace(*str) ; ++str, --len) ; } /* match the year - this part is required */ int acc; if (match_digits(acc, str, len, "09,090909,090909", res->pyy) || match_digits(acc, str, len, "090909,090909", res->pyy) || match_digits(acc, str, len, "0909,090909", res->pyy) || match_digits(acc, str, len, "09090909", res->pyy) || match_digits(acc, str, len, "090909", res->pyy) || match_digits(acc, str, len, "0909", res->pyy) || match_digits(acc, str, len, "09", res->pyy)) res->yy = acc; else return FALSE; /* we accept large year numbers, so check range */ if (res->yy >= 5879650 || res->yy <= -5879650) err_throw(VMERR_NUM_OVERFLOW); /* if we didn't have a prefixed era, check for a postfix era */ if (era == 0) { /* skip optional spaces */ const char *str2 = str; size_t len2 = len; for ( ; len2 != 0 && isspace(*str2) ; ++str2, --len2) ; /* check for the era */ if (match_list( idx, str2, len2, era_lst, era_len, res->pera)) { /* found it - set the era identifier */ res->era = era = (idx == 1 ? 2 : -2); /* advance past it in the main string */ str = str2; len = len2; } } /* * if we matched one or two digits, with no era, we need a * default century */ if (era == 0 && res->pyy.len <= 2) res->yy_needs_century = TRUE; } else goto other; break; case 'e': if (match_fmt(fmt, sp, "era")) { /* AD/BC era designator */ size_t era_len; const char *era_lst = lc->get(vmg_ era_len, LC_ERA); int idx; if (match_list(idx, str, len, era_lst, era_len, res->pera)) { /* set the era to 2 for AD or -2 for BC */ res->era = (idx == 1 ? 2 : -2); } else return FALSE; } else goto other; break; case 'h': if (match_fmt(fmt, sp, "h")) { /* one- or two-digit hours */ int acc; if (match_digits(acc, str, len, "0019", res->phh) || match_digits(acc, str, len, "1102", res->phh) || match_digits(acc, str, len, "09", res->phh)) res->hh = acc; else return FALSE; /* specifying the hour makes the default mm:ss.frac zero */ res->def_minute(0); res->def_second(0); res->def_msec(0); } else if (match_fmt(fmt, sp, "hh")) { /* two-digit hours */ int acc; if (match_digits(acc, str, len, "0109", res->phh) || match_digits(acc, str, len, "2204", res->phh)) res->hh = acc; else return FALSE; /* specifying the hour makes the default minute zero */ res->def_minute(0); res->def_second(0); res->def_msec(0); } else goto other; break; case 'i': if (match_fmt(fmt, sp, "i")) { /* one- or two-digit minutes */ int acc; if (match_digits(acc, str, len, "0509", res->pmi) || match_digits(acc, str, len, "09", res->pmi)) res->mi = acc; else return FALSE; /* specifying the minute makes the default ss.frac zero */ res->def_second(0); res->def_msec(0); } else goto other; break; case 's': if (match_fmt(fmt, sp, "ss")) { /* two-digit seconds */ int acc; if (match_digits(acc, str, len, "0509", res->pss)) res->ss = acc; else return FALSE; /* specifying the second makes the default fraction zero */ res->def_msec(0); } else if (match_fmt(fmt, sp, "s")) { /* one- or two-digit seconds */ int acc; if (match_digits(acc, str, len, "0509", res->pss) || match_digits(acc, str, len, "09", res->pss)) res->ss = acc; else return FALSE; /* specifying the second makes the default fraction zero */ res->def_msec(0); } else if (match_fmt(fmt, sp, "ssfrac")) { /* fractional seconds */ if (len >= 1 && isdigit(str[1])) { /* * parse digits - accumulate up to four digits so that * we can round to milliseconds */ res->pms.p = str; res->pms.len = 0; int acc = 0, mul = 10000, trailing = 0; for ( ; len != 0 && isdigit(*str) ; ++str, --len, ++res->pms.len, mul /= 10) { /* * add the next digit only if significant, * otherwise note any non-zero trailing digits */ if (mul > 1) acc *= 10, acc += value_of_digit(*str); else if (*str != '0') trailing = 1; } /* apply the multiplier if non-zero */ if (mul != 0) acc *= mul; /* we have 10*ms - divide by 10 to get ms */ int lastdig = acc % 10; acc /= 10; /* * round up if the last digit is 6+, 5+ with a non-zero * digit following, or exactly 5 with an odd second * digit */ if (lastdig > 5 || (lastdig == 5 && (trailing || ((acc % 10) & 1) != 0))) ++acc; /* store as integer milliseconds */ res->ms = acc; } else return FALSE; } else goto other; break; case 'u': if (match_fmt(fmt, sp, "unix")) { /* * Unix timestamp value - any number of digits giving a * positive or negative offset in seconds from the Unix * Epoch (1/1/1970) */ int s = 1; if (len >= 2 && str[0] == '-' && is_digit(str[1])) { res->punix.p = str, res->punix.len = 1; s = -1, ++str, --len; } else if (len >= 1 && is_digit(str[0])) { res->punix.p = str, res->punix.len = 0; } else return FALSE; /* * Parse digits; use a double in case we have more than a * 32-bit int's worth. (We only work in whole integers * here, but we might need more precision than an int32. A * double is enough for any day we can represent: we have a * 32-bit day; 86400 seconds per day is about 17 additional * bits, for a total of 49. An IEEE double has a 52-bit * mantissa, so a double can exactly represent a Unix * timestamp for any day number we can represent.) */ double acc = 0.0; for ( ; len > 0 && is_digit(*str) ; ++str, --len, ++res->punix.len) { acc *= 10.0; acc += value_of_digit(*str); } /* apply the sign */ acc *= s; /* split into days and seconds */ double days = floor(acc / (24.*60.*60.)); int32_t secs = (int32_t)fmod(acc, 24.*60.*60.); /* make sure it fits an int32 */ if (days > INT32MAXVAL) return FALSE; /* * convert to a calendar date, adjusting from the Unix * Epoch to our Epoch */ res->cal->set_dayno( (int32_t)days + caldate_t::UNIX_EPOCH_DAYNO); /* set the time and day in the parse results */ res->yy = res->cal->y(); res->mm = res->cal->m(); res->dd = res->cal->d(); res->hh = secs/(60*60); res->mi = (secs/60) % 60; res->ss = secs % 60; res->ms = 0; /* Unix timestamps are relative to UTC */ res->tzofs = 0; } else goto other; break; case 'a': if (match_fmt(fmt, sp, "ampm")) { /* month by short name */ size_t ampm_len; const char *ampm_str = lc->get(vmg_ ampm_len, LC_AMPM); int acc; if (match_list(acc, str, len, ampm_str, ampm_len, res->pampm)) res->ampm = acc; else return FALSE; } else goto other; break; case 't': if (match_fmt(fmt, sp, "tz")) { /* timezone by name, abbreviation, or offset */ if (!match_tzofs(vmg_ res, str, len) && !match_tzname(vmg_ res, str, len)) return FALSE; } else goto other; break; case 'g': if (match_fmt(fmt, sp, "gmtofs")) { /* timezone offset from GMT */ if (!match_tzofs(vmg_ res, str, len)) return FALSE; } else goto other; break; default: other: if (match_fmt(fmt, sp, "+-")) { /* * Astronomical-notation era designator - this is the * system where the year before AD 1 is Year 0, the year * before that -1, etc. Positive years can optionally have * a plus sign. */ if (match_lit(str, len, "+", res->pera)) res->era = 1; else if (match_lit(str, len, "-", res->pera)) res->era = -1; else return FALSE; } else { /* * Anything else is a list of literals to match. If it * ends with '*', we can match zero or more; if it ends * with '+', we can match one or more; otherwise we match * exactly one. */ const char *fmtend = sp; char suffix = *(sp-1); int mincnt = (suffix == '*' || suffix == '?' ? 0 : 1); int maxcnt = (suffix == '*' || suffix == '+' ? 65535 : 1); fmtend -= (strchr("*?+", suffix) != 0 ? 1 : 0); /* scan the character list */ int cnt = 0; utf8_ptr strp((char *)str); while (cnt < maxcnt && len != 0) { int found = FALSE; utf8_ptr fmtp((char *)fmt); for ( ; fmtp.getptr() < fmtend ; fmtp.inc()) { /* get the current template and source characters */ wchar_t fc = fmtp.getch(); wchar_t sc = strp.getch(); /* * check for specials: \ quotes the next, _ is a * space */ if (fc == '\\' && fmtp.getptr() < fmtend) { fmtp.inc(); fc = fmtp.getch(); } else if (fc == '_') { fc = ' '; } /* check for a match */ if (fc == sc) { found = TRUE; ++cnt; strp.inc(&len); str = strp.getptr(); break; } } if (!found) break; } /* if we didn't match the minimum number, it's a mismatch */ if (cnt < mincnt) return FALSE; } break; } /* advance past this field */ fmtl -= sp - fmt; fmt = sp; } /* if we didn't exhaust the format, we have no match */ if (fmtl != 0) return FALSE; /* we matched the format - skip trailing spaces */ for ( ; len != 0 && is_space(*str) ; ++str, --len) ; /* return success */ return TRUE; } /* custom/builtin format string iterator */ struct format_iter { format_iter(VMG_ const char **builtin, size_t builtin_cnt, const vm_val_t *custom) { /* save the source lists */ this->builtin = builtin; this->builtin_cnt = builtin_cnt; this->custom = custom; /* presume we'll start in the custom list */ this->mode = 0; /* figure how many builtin items we have, if any */ if (custom == 0 || custom->typ == VM_NIL) { /* no custom items - just use the builtin list */ this->custom_cnt = 0; this->mode = 1; } else if (custom->get_as_string(vmg0_) != 0) this->custom_cnt = 1; else if (custom->is_listlike(vmg0_)) this->custom_cnt = custom->ll_length(vmg0_); else err_throw(VMERR_BAD_TYPE_BIF); /* start at the top of each list */ this->bidx = 0; this->cidx = 0; } /* get the next string */ const char *next(VMG_ size_t &len) { /* assume we won't find anythign */ len = 0; /* keep going until we find something */ for (;;) { if (mode == 0) { /* custom list - if exhausted, we're done */ if (cidx >= custom_cnt) return 0; /* get the next custom item, according to the source type */ vm_val_t ele; if (custom->is_listlike(vmg0_)) { /* get the next list element */ custom->ll_index(vmg_ &ele, ++cidx); /* if it's nil, switch to the builtin list */ if (ele.typ == VM_NIL) { mode = 1; continue; } } else { /* it must be a single string */ ele = *custom; } /* retrieve the string value */ const char *str = ele.get_as_string(vmg0_); if (str != 0) { len = vmb_get_len(str); str += VMB_LEN; } return str; } else { /* builtin list - if exhausted, return to the custom list */ if (bidx >= builtin_cnt) { mode = 0; continue; } /* return the next builtin item */ len = strlen(builtin[bidx]); return builtin[bidx++]; } } } /* current list we're reading from - 0=custom, 1=builtin */ int mode; /* builtin list, and current index in list */ const char **builtin; size_t builtin_cnt; size_t bidx; /* custom string or list, and current index in list */ const vm_val_t *custom; size_t custom_cnt; size_t cidx; }; /* * Parse a date string in a constructor argument. */ int CVmObjDate::parse_date_string( VMG_ int32_t &dayno, int32_t &daytime, const char *str, const vm_val_t *custom, CVmDateLocale *lc, multicaldate_t *cal, int32_t refday, int32_t reftime, CVmTimeZone *reftz, date_parse_result *resultp, date_parse_string *fmtlist, int *nfmtlist) { /* get the string length and buffer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* format list */ static const char *fmt[] = { /* US-style dates, with the month first */ "[us]m / d / y", "[us]m / d", "[us]m - d", "[us]m . d", "[us]m .\t- d .\t- y", /* European-style date formats, with day/month order */ "[eu]d / m / y", "[eu]d / m", "[eu]d - m", "[eu]d . m", "[eu]d .\t- m .\t- y", /* match some special computer formats first */ "yyyy mm dd", /* 8-digit year month day */ "yyyy .? doy", /* PostreSQL year with day-of-year */ /* universal date formats */ "m / y", "m - y", "month _.\t-* ddth _,.\t+ ye", "ddth _.\t-* month _,.\t-* ye", "month _.\t-* ddth", "ddth _.\t-* month", "month - dd - ye", "month _\t.-* y", "month", "+- y - mm - dd", "y / m / d", "y - m - d", "y / m", "y - m", "ye _\t.-* month _\t.-* d", "ye _\t.-* month", "ye - month - dd", "yyyy", "era _* y", "y _* era", /* time formats */ "h _? ampm", "h : mi", "h .: mi _? ampm", "h .: mi .: ss _? ampm", "h : mi : ss", "h : mi : ss .: ssfrac _? ampm", "h : mi : ss .: ssfrac", "tT? hh .: mi", "tT? hh mi", "tT? hh .: mi .: ss", "tT? hh mi SS", "tT? hh .: mi .: ss _? tz", "tT? hh .: mi .: ss . ssfrac", "tz", /* special formats (ISO, other software, etc) */ "d / mon / yyyy : hh : mi : ss _ gmtofs", /* Unix log files */ "yyyy : mm : dd _ hh : mi : ss", /* EXIF */ "yyyy -? \\W W", /* ISO year with ISO week */ "yyyy - mm - dd \\T hh : mi : ss . ssfrac", /* SOAP */ "yyyy - mm - dd \\T hh : mi : ss . ssfrac gmtofs", /* SOAP */ "@ unix", /* Unix timestamp */ "yyyy mm dd \\T hh : mi : ss", /* XMLRPC */ "yyyy mm dd \\t hh mi ss", /* XMLRPC - compact */ "yyyy - m - d \\T h : i : s" /* WDDX */ }; /* get the template filter in "[xx]" format */ size_t filterlen; const char *filter = lc->get(vmg_ filterlen, LC_PARSE_FILTER); /* keep separate results for each pass */ date_parse_result result(cal); /* * Run through the format list as many times as it takes to either * consume the whole source string, or fail to find a matching format. * We have multiple format parts that can be specified individually or * in combination (time, date, time + date, date + time, etc). */ int fcapi; for (fcapi = 0 ; len != 0 ; ++fcapi) { /* we haven't found a best result for this pass yet */ int bestlen = 0; date_parse_result bestres(cal); /* search the list for a match */ format_iter fi(vmg_ fmt, countof(fmt), custom); size_t curfmtl; const char *curfmt; while ((curfmt = fi.next(vmg_ curfmtl)) != 0) { /* set up at the current position */ const char *curstr = str; size_t curlen = len; /* remember the original format string */ const char *curfmt0 = curfmt; size_t curfmtl0 = curfmtl; /* if this is a "[us]" or "[eu]" local entry, filter it */ if (curfmtl >= 3 && curfmt[0] == '[') { /* get the ']' */ const char *rb = lib_strnchr(curfmt+1, curfmtl-1, ']'); /* * if we found it, and the contents of the brackets don't * match the locale filter string, filter out this format */ if (rb != 0 && (rb - curfmt - 1 != (int)filterlen || memicmp(curfmt + 1, filter, filterlen) != 0)) continue; /* skip the filter string */ curfmtl -= rb - curfmt + 1; curfmt = rb + 1; } /* start with the results from previous passes */ date_parse_result curres(result); /* try matching this format at the current position */ if (parse_string_fmt(vmg_ &curres, curstr, curlen, curfmt, curfmtl, lc)) { /* if this is the longest match of this pass, keep it */ int reslen = curstr - str; if (reslen > bestlen) { /* remember this as the best result so far */ bestlen = reslen; bestres = curres; /* remember the format string it matches, if desired */ if (nfmtlist != 0 && fcapi < *nfmtlist) fmtlist[fcapi].set(curfmt0, curfmtl0); } } } /* if we didn't find a match on this round, we've failed */ if (bestlen == 0) return FALSE; /* skip what we matched for the best result of this pass */ str += bestlen; len -= bestlen; /* keep the best result for this pass */ result = bestres; /* skip spaces and other separator characters */ for ( ; len != 0 && strchr(" \t;,:", *str) != 0 ; ++str, --len) ; } /* default to the reference time zone */ result.def_tz(reftz); /* adjust the reference time to the appropriate local time zone */ result.utc_to_local(refday, reftime); /* set missing date elements to the corresponding reference date values */ result.def_date(refday); /* set any missing time elements to midnight or zero past the hour/min */ result.def_time(0, 0, 0, 0); /* convert to our internal day and time representation */ dayno = result.dayno(); daytime = result.daytime(); caldate_t::normalize(dayno, daytime); /* translate the final time value from the specified local time to UTC */ result.local_to_utc(dayno, daytime); /* copy back the results if the caller's interested */ if (resultp != 0) memcpy(resultp, &result, sizeof(result)); /* tell the caller how many format strings we matched, if desired */ if (nfmtlist != 0) *nfmtlist = fcapi; /* success */ return TRUE; } /* * Parse constructor arguments for a (...Y,M,D) or (...Y,M,D,HH,MI,SS,MS) * argument list. This parses the arguments from the stack and fills in * the day number and time of day values accordingly. We don't make any * time zone adjustments; we just return the value as specified. */ static void parse_ymd_args(VMG_ int argofs, int argc, int32_t &dayno, int32_t &daytime) { /* get the year, month, and day */ int32_t y = G_stk->get(argofs + 0)->num_to_int(vmg0_); int32_t m = G_stk->get(argofs + 1)->num_to_int(vmg0_); int32_t d = G_stk->get(argofs + 2)->num_to_int(vmg0_); /* figure the day number for the given calendar date */ caldate_t cd(y, m, d); dayno = cd.dayno(); /* * if this is the 7-argument version, get the time; otherwise it's * implicitly midnight on the given date */ daytime = 0; if (argc == 7) { /* get the hour and minute as integers, and seconds */ int hh = G_stk->get(argofs + 3)->num_to_int(vmg0_); int mm = G_stk->get(argofs + 4)->num_to_int(vmg0_); int ss = G_stk->get(argofs + 5)->num_to_int(vmg0_); int ms = G_stk->get(argofs + 6)->num_to_int(vmg0_); /* * Combine the time components into 'daytime' in milliseconds. * This could be a very large number, so work in double until we * can normalize it. Note that we're working in whole numbers only * (no fractions), so a double will be exact - all we're worried * about here is the extra integer precision it can hold, not the * fractional part. */ double dt = hh*60.*60.*1000. + mm*60.*1000. + ss*1000. + ms; /* figure the overflow into the day number (and make sure it fits) */ double dd = floor(dt / (24.*60.*60.*1000.)); dt = fmod(dt, 24.*60.*60.*1000.); /* apply the overflow to the day number, and make sure it fits */ dd += dayno; if (dd < INT32MINVAL || dd > INT32MAXVAL) err_throw(VMERR_NUM_OVERFLOW); /* it's all normalized and in range, so convert back to int32 */ dayno = (int32_t)dd; daytime = (int32_t)dt; } /* normalize the result */ caldate_t::normalize(dayno, daytime); } /* ------------------------------------------------------------------------ */ /* * Create with a given timestamp */ vm_obj_id_t CVmObjDate::create(VMG_ int in_root_set, int32_t dayno, int32_t daytime) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjDate(vmg_ dayno, daytime); return id; } /* ------------------------------------------------------------------------ */ /* * Create from an os_time_t timestamp */ vm_obj_id_t CVmObjDate::create_from_time_t(VMG_ int in_root_set, os_time_t t) { /* * turn the time_t into days and milliseconds after the Unix Epoch (use * doubles to ensure we don't overflow an int) */ double d = (double)t; double dn = floor(d / (24*60*60)); double dms = (d - (dn * (24*60*60))) * 1000; /* adjust to our internal Epoch */ dn += caldate_t::UNIX_EPOCH_DAYNO; /* we have the time in our own format now, so go create the object */ return create(vmg_ in_root_set, (int32_t)dn, (int32_t)dms); } /* ------------------------------------------------------------------------ */ /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjDate::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { int32_t dayno; int32_t daytime; const char *str; /* check for the various argument lists */ if (argc == 0) { /* no arguments - get the current date and time in the local zone */ caldate_t::now(dayno, daytime); } else if ((argc >= 1 && argc <= 3) && (str = G_stk->get(0)->get_as_string(vmg0_)) != 0) { /* * The first argument is a date in string format. The optional * second argument is the reference time zone; the optional third * argument is the reference date. */ /* get the reference time zone */ CVmTimeZone *reftz; if (argc >= 2 && G_stk->get(1)->typ != VM_NIL) reftz = get_tz_arg(vmg_ 1, argc); else reftz = G_tzcache->get_local_zone(vmg0_); /* get the reference date */ int32_t refday, reftime; if (argc >= 3 && G_stk->get(2)->typ != VM_NIL) { /* get the reference Date object */ CVmObjDate *refdate = vm_val_cast(CVmObjDate, G_stk->get(2)); if (refdate == 0) err_throw(VMERR_BAD_TYPE_BIF); /* take the reference point from the Date object */ refday = refdate->get_ext()->dayno; reftime = refdate->get_ext()->daytime; } else { /* no reference date, so use the current time */ caldate_t::now(refday, reftime); } /* parse the format */ CVmDateLocale lc Pvmg0_P; gregcaldate_t cal; if (!parse_date_string(vmg_ dayno, daytime, str, 0, &lc, &cal, refday, reftime, reftz, 0, 0, 0)) err_throw(VMERR_BAD_VAL_BIF); } else if (argc == 2 && G_stk->get(0)->is_numeric(vmg0_) && (str = G_stk->get(1)->get_as_string(vmg0_)) != 0) { /* check the type code */ double dn, dms; if (vmb_get_len(str) == 1 && (str[2] == 'U' || str[2] == 'u')) { /* * 'U' - Unix time, as seconds past 1/1/1970, UTC. Get the * argument as a double, and convert it to our day numbering * scheme by dividing by seconds per day and adding the Unix * Epoch's day number. (An ordinary double is big enough to * hold the largest Unix seconds-since-Epoch value we can * represent: our range is limited by the 32-bit day number, so * in terms of seconds that's (2^31-1)*86400, which needs about * 49 bits. A standard double's mantissa is 52 bits, so it can * represent any integer in our range exactly.) */ double d = G_stk->get(0)->num_to_double(vmg0_); dn = floor(d / (24*60*60)); dms = (d - (dn * (24*60*60))) * 1000; dn += caldate_t::UNIX_EPOCH_DAYNO; } else if (vmb_get_len(str) == 1 && (str[2] == 'J' || str[2] == 'j')) { /* * 'J' - Julian day number; get the value, and adjust it to our * Epoch by subtracting the Julian day number of our Epoch. * The whole part is our day number; the fractional part is the * fraction of a day past midnight (the .5 in the Epoch * adjustment recalibrates from noon to midnight). */ bignum_t<32> bdayno(vmg_ G_stk->get(0)); bdayno -= 1721119.5; dn = floor((double)bdayno); dms = (double)((bdayno - dn) * (long)(24*60*60*1000)); } else { /* unrecognized code */ err_throw(VMERR_BAD_VAL_BIF); } /* make sure the day number fits an int32 */ if (dn > INT32MAXVAL || dn < INT32MINVAL) err_throw(VMERR_NUM_OVERFLOW); dayno = (int32_t)dn; /* convert milliseconds to int32 */ daytime = round_int32(dms); } else if ((argc == 3 || argc == 7) && G_stk->get(0)->is_numeric(vmg0_) && G_stk->get(1)->is_numeric(vmg0_) && G_stk->get(2)->is_numeric(vmg0_) && (argc == 3 || (G_stk->get(3)->is_numeric(vmg0_) && G_stk->get(4)->is_numeric(vmg0_) && G_stk->get(5)->is_numeric(vmg0_) && G_stk->get(6)->is_numeric(vmg0_)))) { /* parse the Y/M/D or Y/M/D HH:MI:SS arguments */ parse_ymd_args(vmg_ 0, argc, dayno, daytime); /* convert from the local time zone to UTC */ G_tzcache->get_local_zone(vmg0_)->local_to_utc(dayno, daytime); } else if ((argc == 4 || argc == 8) && G_stk->get(0)->is_numeric(vmg0_) && G_stk->get(1)->is_numeric(vmg0_) && G_stk->get(2)->is_numeric(vmg0_) && (argc == 4 || (G_stk->get(3)->is_numeric(vmg0_) && G_stk->get(4)->is_numeric(vmg0_) && G_stk->get(5)->is_numeric(vmg0_) && G_stk->get(6)->is_numeric(vmg0_)))) { /* parse the Y/M/D or Y/M/D HH:MI:SS arguments */ parse_ymd_args(vmg_ 0, argc - 1, dayno, daytime); /* convert from the given time zone to UTC */ get_tz_arg(vmg_ argc - 1, argc)->local_to_utc(dayno, daytime); } else { /* wrong arguments */ err_throw(VMERR_BAD_TYPE_BIF); } /* create the object */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, FALSE, FALSE); new (vmg_ id) CVmObjDate(vmg_ dayno, daytime); /* discard arguments */ G_stk->discard(argc); /* return the new ID */ return id; } /* ------------------------------------------------------------------------ */ /* * notify of deletion */ void CVmObjDate::notify_delete(VMG_ int /*in_root_set*/) { /* free our additional data, if we have any */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjDate::set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) { /* no settable properties - throw an error */ err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * get a property */ int CVmObjDate::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from our base class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * get a static (class) property */ int CVmObjDate::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* look up the function */ uint midx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* check for our static properties */ switch (midx) { case VMDATE_PARSEDATE: return s_getp_parseDate(vmg_ result, argc); case VMDATE_PARSEJULIANDATE: return s_getp_parseJulianDate(vmg_ result, argc); case VMDATE_SETLOCALINFO: return s_getp_setLocaleInfo(vmg_ result, argc); } /* not one of hours; inherit the superclass handling */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* ------------------------------------------------------------------------ */ /* * cast to a string */ const char *CVmObjDate::cast_to_string( VMG_ vm_obj_id_t self, vm_val_t *newstr) const { /* format the date into a buffer */ char buf[64]; format_string_buf(vmg_ buf, sizeof(buf)); /* create the return string */ newstr->set_obj(CVmObjString::create(vmg_ FALSE, buf, strlen(buf))); return newstr->get_as_string(vmg0_); } /* * Format into a string buffer using the default representation - for * implied conversions, toString, and the debugger */ void CVmObjDate::format_string_buf(VMG_ char *buf, size_t buflen) const { /* get the day number and time of day in local time */ int32_t dayno, daytime, tzofs; const char *tzabbr = get_local_time( dayno, daytime, tzofs, G_tzcache->get_local_zone(vmg0_)); /* convert the day number to a calendar date */ gregcaldate_t date(dayno); /* format using a default template */ CVmDateLocale lc Pvmg0_P; format_date(vmg_ buf, buflen, "%Y-%m-%d %H:%M:%S", 17, &lc, &date, dayno, daytime, tzabbr, tzofs); } /* ------------------------------------------------------------------------ */ /* * Date arithmetic - add an integer or BigNumber: adds the given number of * days to the date. */ int CVmObjDate::add_val(VMG_ vm_val_t *result, vm_obj_id_t /*self*/, const vm_val_t *val) { /* get my date and time */ int32_t dayno = get_ext()->dayno; int32_t daytime = get_ext()->daytime; /* the other value must be something numeric */ if (val->typ == VM_INT) { /* it's a simple integer - add it as a number of days */ dayno += val->val.intval; } else { /* it's not an int; calculate in the bignum domain */ bignum_t<32>d(vmg_ val); /* make sure the integer portion won't overflow */ double dd = floor((double)d); if (dd + dayno > INT32MAXVAL || dd + dayno < INT32MINVAL) err_throw(VMERR_NUM_OVERFLOW); /* add the whole part to my day number */ dayno += (int32_t)dd; /* * convert the fractional part to milliseconds (by multiplying it * by the number of milliseconds in a day), then add it to my time * value */ daytime += round_int32((double)((d - dd) * (long)(24L*60*60*1000))); /* normalize, to carry time overflow/underflow into the date */ caldate_t::normalize(dayno, daytime); } /* return a new Date object representing the result */ result->set_obj(create(vmg_ FALSE, dayno, daytime)); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Date arithmetic - subtract an integer or BigNumber to subtract a number * of days from the date; or subtract another Date value to calculate the * number of days between the dates. */ int CVmObjDate::sub_val(VMG_ vm_val_t *result, vm_obj_id_t /*self*/, const vm_val_t *val) { /* get my date and time */ int32_t dayno = get_ext()->dayno; int32_t daytime = get_ext()->daytime; /* the other value must be a Date or something numeric */ if (val->typ == VM_OBJ && is_date_obj(vmg_ val->val.obj)) { /* get the other date's contents */ CVmObjDate *vdate = (CVmObjDate *)vm_objp(vmg_ val->val.obj); int32_t vdayno = vdate->get_ext()->dayno; int32_t vdaytime = vdate->get_ext()->daytime; /* we need fractional days, so calculate as bignum_t */ bignum_t<32> d1((long)dayno), t1((long)daytime); bignum_t<32> d2((long)vdayno), t2((long)vdaytime); d1 += (t1 / (long)(24*60*60*1000)); d2 += (t2 / (long)(24*60*60*1000)); /* return a BigNumber containing the result */ result->set_obj(CVmObjBigNum::create(vmg_ FALSE, d1 - d2)); /* handled (we don't want to return a Date, as the other paths do) */ return TRUE; } else if (val->typ == VM_INT) { /* it's a simple integer - subtract the number of days */ dayno -= val->val.intval; } else { /* it's not an int; calculate in the bignum domain */ bignum_t<32> d(vmg_ val); /* make sure the integer portion won't overflow */ double dd = floor((double)d); if (dd - dayno > INT32MAXVAL || dd - dayno < INT32MINVAL) err_throw(VMERR_NUM_OVERFLOW); /* add the whole part to my day number */ dayno -= (int32_t)dd; /* * convert the fractional part to milliseconds (by multiplying it * by the number of milliseconds in a day), then subtract it from * my time value */ daytime -= round_int32((double)((d - dd) * (long)(24L*60*60*1000))); /* normalize, to carry time overflow/underflow into the date */ caldate_t::normalize(dayno, daytime); } /* return a new Date object representing the result */ result->set_obj(create(vmg_ FALSE, dayno, daytime)); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Check two Date values for equality */ int CVmObjDate::equals(VMG_ vm_obj_id_t, const vm_val_t *val, int) const { if (val->typ == VM_OBJ && is_date_obj(vmg_ val->val.obj)) { /* it's a Date - get the two day number and time values */ CVmObjDate *vdate = (CVmObjDate *)vm_objp(vmg_ val->val.obj); int32_t day1 = get_ext()->dayno, day2 = vdate->get_ext()->dayno; int32_t time1 = get_ext()->daytime, time2 = vdate->get_ext()->daytime; /* we're equal if we represent the same date and time */ return day1 == day2 && time1 == time2; } else { /* we're not equal to any other type */ return FALSE; } } /* * Compare two Date values */ int CVmObjDate::compare_to(VMG_ vm_obj_id_t, const vm_val_t *val) const { /* check the other object's type */ if (val->typ == VM_OBJ && is_date_obj(vmg_ val->val.obj)) { /* it's a Date - get the two day number and time values */ CVmObjDate *vdate = (CVmObjDate *)vm_objp(vmg_ val->val.obj); int32_t day1 = get_ext()->dayno, day2 = vdate->get_ext()->dayno; int32_t time1 = get_ext()->daytime, time2 = vdate->get_ext()->daytime; /* compare by date and time */ return (day1 != day2 ? day1 - day2 : time1 - time2); } else { /* we can't compare to other types */ err_throw(VMERR_INVALID_COMPARISON); AFTER_ERR_THROW(return 0;) } } /* ------------------------------------------------------------------------ */ /* * load from an image file */ void CVmObjDate::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from the image file */ void CVmObjDate::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); } /* * load or reload data from the image */ void CVmObjDate::load_image_data(VMG_ const char *ptr, size_t siz) { /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* allocate the extension */ vm_date_ext *ext = vm_date_ext::alloc_ext(vmg_ this); ext_ = (char *)ext; /* read the image data */ ext->dayno = osrp4s(ptr); ext->daytime = osrp4(ptr+4); } /* * save to a file */ void CVmObjDate::save_to_file(VMG_ class CVmFile *fp) { /* get our extension */ vm_date_ext *ext = get_ext(); /* write the extension data */ fp->write_int4(ext->dayno); fp->write_uint4(ext->daytime); } /* * restore from a file */ void CVmObjDate::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *) { /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* allocate the extension structure */ vm_date_ext *ext = vm_date_ext::alloc_ext(vmg_ this); ext_ = (char *)ext; /* read the data */ ext->dayno = fp->read_int4(); ext->daytime = fp->read_uint4(); } /* ------------------------------------------------------------------------ */ /* * Construct a list of integer values for return from a method */ static void make_int_list(VMG_ vm_val_t *retval, int cnt, ...) { /* create the list */ retval->set_obj(CVmObjList::create(vmg_ FALSE, cnt)); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* start the varargs scan */ va_list args; va_start(args, cnt); /* add the elements */ for (int i = 0 ; i < cnt ; ++i) { vm_val_t ele; ele.set_int(va_arg(args, int)); lst->cons_set_element(i, &ele); } /* done with the varargs */ va_end(args); } /* * Add a string to a return list */ static void enlist_str(VMG_ CVmObjList *lst, int idx, const char *str, size_t len) { /* set up a new string, or nil if the string is null */ vm_val_t ele; if (str != 0) ele.set_obj(CVmObjString::create(vmg_ FALSE, str, len)); else ele.set_nil(); /* add the list element */ lst->cons_set_element(idx, &ele); } /* add a string from a date_parse_string */ static void enlist_str(VMG_ CVmObjList *lst, int idx, const date_parse_string &str) { enlist_str(vmg_ lst, idx, str.p, str.len); } /* ------------------------------------------------------------------------ */ /* * parseDate method (static) */ int CVmObjDate::s_getp_parseDate(VMG_ vm_val_t *retval, uint *oargc) { /* parse the date using the Gregorian calendar */ gregcaldate_t cal; return common_parseDate(vmg_ retval, oargc, &cal); } /* * parseJulianDate method (static) */ int CVmObjDate::s_getp_parseJulianDate(VMG_ vm_val_t *retval, uint *oargc) { /* parse the date using the Julian calendar */ julcaldate_t cal; return common_parseDate(vmg_ retval, oargc, &cal); } /* * common parseDate handler for parseDate, parseJulianDate */ int CVmObjDate::common_parseDate(VMG_ vm_val_t *retval, uint *oargc, multicaldate_t *cal) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1, 3, FALSE); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* set up a locale state interface */ CVmDateLocale lc Pvmg0_P; /* retrieve the string to parse */ const char *src = G_stk->get(0)->get_as_string(vmg0_); if (src == 0) err_throw(VMERR_BAD_VAL_BIF); /* retrieve the format string/list argument, if present */ const vm_val_t *custom = 0; if (argc >= 2 && G_stk->get(1)->typ != VM_NIL) custom = G_stk->get(1); /* retrieve the reference date, if present */ int32_t refday, reftime; if (argc >= 3 && G_stk->get(2)->typ != VM_NIL) { /* get the reference date */ CVmObjDate *refdate = vm_val_cast(CVmObjDate, G_stk->get(2)); if (refdate == 0) err_throw(VMERR_BAD_TYPE_BIF); /* get its contents */ refday = refdate->get_ext()->dayno; reftime = refdate->get_ext()->daytime; } else { /* no reference date argument - get the current date and time */ caldate_t::now(refday, reftime); } /* retrieve the reference time zone, if present */ CVmTimeZone *reftz = get_tz_arg(vmg_ 3, argc); /* parse the date */ int32_t dayno, daytime; date_parse_result result(cal); date_parse_string fmt_match[20]; int fmt_match_cnt = countof(fmt_match); if (parse_date_string(vmg_ dayno, daytime, src, custom, &lc, cal, refday, reftime, reftz, &result, fmt_match, &fmt_match_cnt)) { /* create the return list */ retval->set_obj(CVmObjList::create(vmg_ FALSE, 5)); G_stk->push(retval); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); lst->cons_clear(); /* element [1] is the return date */ vm_val_t ele; ele.set_obj(CVmObjDate::create(vmg_ FALSE, dayno, daytime)); lst->cons_set_element(0, &ele); /* [2] is the TimeZone object, if applicable */ ele.set_nil(); if (result.tz != reftz) ele.set_obj(CVmObjTimeZone::create(vmg_ result.tz)); lst->cons_set_element(1, &ele); /* [3] is the fixed timezone offset in seconds, if applicable */ ele.set_nil(); if (result.has_tzofs()) ele.set_int(result.tzofs / 1000); lst->cons_set_element(2, &ele); /* [4] next is the format string matched */ ele.set_obj(CVmObjList::create(vmg_ FALSE, fmt_match_cnt)); lst->cons_set_element(3, &ele); CVmObjList *flst = (CVmObjList *)vm_objp(vmg_ ele.val.obj); flst->cons_clear(); /* populate the format string list */ for (int fi = 0 ; fi < fmt_match_cnt ; ++fi) enlist_str(vmg_ flst, fi, fmt_match[fi]); /* [5] is a sublist with the source substrings */ ele.set_obj(CVmObjList::create(vmg_ FALSE, 13)); lst->cons_set_element(4, &ele); CVmObjList *slst = (CVmObjList *)vm_objp(vmg_ ele.val.obj); slst->cons_clear(); /* now add the date component source strings */ enlist_str(vmg_ slst, 0, result.pera); enlist_str(vmg_ slst, 1, result.pyy); enlist_str(vmg_ slst, 2, result.pmm); enlist_str(vmg_ slst, 3, result.pdd); enlist_str(vmg_ slst, 4, result.pdoy); enlist_str(vmg_ slst, 5, result.pw); enlist_str(vmg_ slst, 6, result.pampm); enlist_str(vmg_ slst, 7, result.phh); enlist_str(vmg_ slst, 8, result.pmi); enlist_str(vmg_ slst, 9, result.pss); enlist_str(vmg_ slst, 10, result.pms); enlist_str(vmg_ slst, 11, result.punix); enlist_str(vmg_ slst, 12, result.ptz); /* done with the list gc protection */ G_stk->discard(); } else { /* return nil */ retval->set_nil(); } /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * formatDate method */ int CVmObjDate::getp_formatDate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { gregcaldate_t cd; return formatDateGen(vmg_ self, retval, oargc, &cd); } /* * formatJulianDate method */ int CVmObjDate::getp_formatJulianDate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { julcaldate_t cd; return formatDateGen(vmg_ self, retval, oargc, &cd); } /* * general date formatter */ int CVmObjDate::formatDateGen(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc, multicaldate_t *date) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the format string */ const char *fmt = G_stk->get(0)->get_as_string(vmg0_); if (fmt == 0) err_throw(VMERR_BAD_TYPE_BIF); /* get the format string length and buffer pointer */ size_t fmtlen = vmb_get_len(fmt); fmt += VMB_LEN; /* get my date and time in local time */ CVmTimeZone *tz = get_tz_arg(vmg_ 1, argc); int32_t dayno, daytime, tzofs; const char *tzabbr = get_local_time(dayno, daytime, tzofs, tz); /* convert the day number to a calendar date */ date->set_dayno(dayno); /* do a formatting pass to determine how much space we need */ CVmDateLocale lc Pvmg0_P; size_t buflen = format_date( vmg_ 0, 0, fmt, fmtlen, &lc, date, dayno, daytime, tzabbr, tzofs); /* create a string for the result */ retval->set_obj(CVmObjString::create(vmg_ FALSE, buflen)); CVmObjString *str = vm_objid_cast(CVmObjString, retval->val.obj); /* format the string into the buffer */ format_date(vmg_ str->cons_get_buf(), buflen, fmt, fmtlen, &lc, date, dayno, daytime, tzabbr, tzofs); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* macro: write a character to the output buffer if there's room */ #define wrtch(ch) (++outlen, buflen > 0 ? *buf++ = ch, --buflen : 0) /* macros: write a null-terminated/counted length string */ #define wrtstr(str) wrtstrl(str, strlen(str)) #define wrttstr(str) wrtstrl(str + VMB_LEN, vmb_get_len(str)) #define wrtstrl(str, len) _wrtstrl(buf, buflen, outlen, str, len) /* service routine: write a counted-length string */ static void _wrtstrl(char *&buf, size_t &buflen, size_t &outlen, const char *str, size_t len) { /* if it fits, copy the whole thing; otherwise copy as much as fits */ if (len <= buflen) { memcpy(buf, str, len); buf += len; buflen -= len; } else if (buflen != 0) { memcpy(buf, str, buflen); buf += buflen; buflen = 0; } /* count it all in the output */ outlen += len; } /* macro: write an n-digit number */ #define wrtnum(v, ndigits) \ _wrtnum(buf, buflen, outlen, v, ndigits, pound, leadsp, roman) /* service routine: write an n-digit number */ static void _wrtnum(char *&buf, size_t &buflen, size_t &outlen, int v, int ndigits, int pound, char leadsp, int roman) { char numbuf[20]; int i = 0; int neg = FALSE; /* note the sign */ if (v < 0) { wrtch('-'); v = -v; neg = TRUE; } /* if desired, generate a Roman numeral, if it's in range (1-4999) */ if (roman && !neg && v >= 1 && v <= 4999) { static const struct { const char *r; int val; } r[] = { { "M", 1000 }, { "CM", 900 }, { "D", 500 }, { "CD", 400 }, { "C", 100 }, { "XC", 90 }, { "L", 50 }, { "X", 10 }, { "IX", 9 }, { "V", 5 }, { "IV", 4 }, { "I", 1 } }; for (int i = 0 ; v != 0 && i < countof(r) ; ) { /* if this numeral fits, append it */ if (r[i].val <= v) { /* it fits - write it and deduct it from the balance */ wrtstr(r[i].r); v -= r[i].val; } else { /* doesn't fit - move on to the next lower numeral */ ++i; } } return; } /* generate digits */ do { div_t q = div(v, 10); v = q.quot; numbuf[i++] = '0' + q.rem; } while (v != 0); /* * if we didn't generate as many digits as the field requires, add * leading zeros or spaces, as desired */ if (i < ndigits && (leadsp != 0 || !pound)) { /* add leading digits or the specified spaces */ char lead = leadsp != 0 ? leadsp : '0'; for (int j = i ; j < ndigits ; ++j) wrtch(lead); } /* write the digits */ for ( ; i > 0 ; --i, wrtch(numbuf[i])) ; } /* write a locale string list item */ #define wrtlistitem(item, idx, sticky) \ _wrtlistitem(vmg_ buf, buflen, outlen, lc, item, idx, sticky) static void _wrtlistitem(VMG_ char *&buf, size_t &buflen, size_t &outlen, CVmDateLocale *lc, int item, int idx, int sticky) { /* get the list item */ size_t len; const char *str = lc->index_list(vmg_ len, item, idx, sticky); /* write the item */ for ( ; len != 0 ; ++str, --len) wrtch(*str); } /* * Internal date formatter. Formats into a buffer; if no buffer is * provided, counts the length required. Note that the date is specified * in the *local* time zone - the caller must adjust to local time before * calling. Null-terminates the result if there's room, but doesn't * include the null terminator in the size count. * * Most of our format codes are the same as for C/C++/php strftime, which * are mostly the same as MySQL DATE_FORMAT(). We support all of the * strftime formats, with the same letter codes, except for the php tab * (%t) and newline (%n), which I don't see any good reason to include when * we could just as well use \t and \n. We support all of the MySQL format * options, although several have different codes (MySQL mostly follows the * strftime codes, though). */ size_t CVmObjDate::format_date(VMG_ char *buf, size_t buflen, const char *fmt, size_t fmtlen, CVmDateLocale *lc, const multicaldate_t *date, int32_t dayno, int32_t daytime, const char *tzabbr, int32_t tzofs) const { const char *subfmt; size_t subfmtl; /* we haven't written anything to the buffer yet */ size_t outlen = 0; /* scan the format string */ for ( ; fmtlen != 0 ; ++fmt, --fmtlen) { if (*fmt == '%' && fmtlen >= 2) { /* skip the '%' */ ++fmt, --fmtlen; /* parse flags */ int pound = FALSE; int minus = FALSE; int roman = FALSE; char leadsp = 0; for (int found_flag = TRUE ; found_flag && fmtlen >= 2 ; ) { /* assume we won't find another flag on this pass */ found_flag = FALSE; /* check for the '#' flag (meaning varies by format code) */ if (fmtlen >= 2 && *fmt == '#') { pound = found_flag = TRUE; ++fmt, --fmtlen; } /* ' ' and '\ ' (replace leading zeros with spaces) */ if (fmtlen >= 2 && (*fmt == ' ' || *fmt == 0x15)) { leadsp = *fmt; found_flag = TRUE; ++fmt, --fmtlen; } /* check for '-' flag */ if (fmtlen >= 2 && *fmt == '-') { minus = found_flag = TRUE; ++fmt, --fmtlen; } /* check for '&' flag */ if (fmtlen >= 2 && *fmt == '&') { roman = found_flag = TRUE; ++fmt, --fmtlen; } } /* get the format code */ switch (*fmt) { case 'a': /* abbreviated day name */ wrtlistitem(LC_WKDY, date->weekday(), FALSE); break; case 'A': /* full day name */ wrtlistitem(LC_WEEKDAY, date->weekday(), FALSE); break; case 'd': /* two-digit day of month (or one digit with '#') */ wrtnum(date->d(), 2); break; case 'j': /* day of year 001-366, three digits with leading zeros */ { /* * we can calculate the day of the year by subtracting * the day number of Jan 1 from the given date (and * adding 1 to get into range 1-366) */ caldate_t jan1(date->y(), 1, 1); int j = dayno - jan1.dayno() + 1; wrtnum(j, 3); } break; case 'J': /* * Julian day number (the 4713 BC kind); '#' suppresses the * fractional part */ { /* get the day and time */ long dn = get_dayno(), dt = get_daytime(); /* if the time is past noon, it's on the next day */ if (dt > 12*60*60*1000) dn += 1, dt -= 12*60*60*1000; /* * figure the combined date/time value, adjusting the * day number to the Julian day Epoch */ bignum_t<32> bdn(dn), bdt(dt); bdn += 1721119L; bdn += (bdt / (long)(24*60*60*1000)); /* format it and write it */ char jbuf[64]; bdn.format(jbuf, sizeof(jbuf), -1, 10); wrttstr(jbuf); } break; case 'u': /* day of week 1-7 Monday-Sunday (ISO weekday) */ wrtnum(date->iso_weekday(), 1); break; case 'w': /* day of week 0-6 Sunday-Saturday */ wrtnum(date->weekday(), 1); break; case 't': /* day of month with ordinal suffix (non-strftime) */ wrtnum(date->d(), 1); wrtlistitem(LC_ORDSUF, ordinal_index(date->d()), TRUE); break; case 'U': /* week number, 00-53, week 1 starting with first Sunday */ wrtnum(date->weekno(0), 2); break; case 'W': /* week number, 00-53, week 1 starting with first Monday */ wrtnum(date->weekno(1), 2); break; case 'V': /* ISO-8601:1988 week number of the year */ wrtnum(date->iso_weekno(0), 2); break; case 'b': /* abbreviated month name */ wrtlistitem(LC_MON, date->m() - 1, FALSE); break; case 'B': /* full month name */ wrtlistitem(LC_MONTH, date->m() - 1, FALSE); break; case 'm': /* two-digit month 01-12 (one digit with #) */ wrtnum(date->m(), 2); break; case 'C': /* two-digit century (e.g., 19 for 1900-1999) */ wrtnum(date->y() / 100, 2); break; case 'g': case 'G': /* ISO-8601:1988 year ('g'=2 digits, 'G'=4 digits) */ { /* ISO-8601:1988 week number of the year */ int iy; date->iso_weekno(&iy); wrtnum(iy, *fmt == 'g' ? 2 : 4); } break; case 'y': /* two-digit year */ wrtnum(ldiv(date->y(), 100).rem, 2); break; case 'Y': /* four-digit year */ wrtnum(date->y(), 4); break; case 'e': case 'E': /* * Year with AD/BC era before/after the year number. For * 'e', the era is always after, or always before on '-'. * For 'E', the era is AD before/BC after, or reversed on * '-'. */ { /* figure the era: positive years are AD, <=0 are BC */ int idx = 0; // assume AD int yy = date->y(); if (yy <= 0) { idx = 1; // BC yy = -yy + 1; } /* * Figure the display order: "%-e" puts the era first * in all case; "%E" puts AD first; "%-E" puts BC first */ if ((*fmt == 'e' && minus) || (*fmt == 'E' && !minus && idx == 0) || (*fmt == 'E' && minus && idx == 1)) { /* era first */ wrtlistitem(LC_ERA, idx, FALSE); wrtch(' '); wrtnum(yy, 1); } else { /* year first */ wrtnum(yy, 1); wrtch(' '); wrtlistitem(LC_ERA, idx, FALSE); } } break; case 'H': /* two-digit hour, 24-hour clock */ wrtnum(daytime/(60*60*1000), 2); break; case 'I': /* two-digit hour, 12-hour clock */ { int hh = daytime/(60*60*1000); if (hh == 0) hh = 12; else if (hh >= 13) hh -= 12; wrtnum(hh, 2); } break; case 'M': /* two-digit minute */ wrtnum(daytime/(60*1000) % 60, 2); break; case 'P': /* upper-case AM or PM */ wrtlistitem(LC_AMPM, daytime/(60*60*1000) >= 12 ? 1 : 0, FALSE); break; case 'p': /* lower-case am or pm */ wrtlistitem(LC_AMPM, daytime/(60*60*1000) >= 12 ? 3 : 2, FALSE); break; case 'r': /* full 12-hour clock time: %I:%M:%S %P */ subfmt = lc->get(vmg_ subfmtl, LC_FMT_12HOUR); goto do_subfmt; do_subfmt: { /* recursively format the new format string */ size_t l = format_date( vmg_ buf, buflen, subfmt, subfmtl, lc, date, dayno, daytime, tzabbr, tzofs); /* count it in our output length */ outlen += l; /* advance our buffer pointer past the copied text */ if (buflen >= l) buflen -= l, buf += l; else buf += buflen, buflen = 0; } break; case 'R': /* 24-hour clock time w/minutes: %H:%M */ subfmt = lc->get(vmg_ subfmtl, LC_FMT_24HOUR); goto do_subfmt; case 'S': /* two-digit seconds */ wrtnum(daytime/1000 % 60, 2); break; case 'N': /* three-digit milliseconds (non-strftime) */ wrtnum(daytime % 1000, 3); break; case 'T': /* 24-hour clock time w/seconds: %H:%M:%S */ subfmt = lc->get(vmg_ subfmtl, LC_FMT_24HOURSECS); goto do_subfmt; case 'X': /* locale time representation without the date */ subfmt = lc->get(vmg_ subfmtl, LC_FMT_TIME); goto do_subfmt; case 'z': /* time zone abbreviation */ { for (const char *p = tzabbr ; *p != '\0' ; ++p) wrtch(*p); } break; case 'Z': /* time zone UTC offset, four digits (+0500) */ { int32_t o = tzofs; if (o < 0) wrtch('-'), o = -o; else wrtch('+'); wrtnum(o/(60*60*1000), 2); pound = FALSE; wrtnum(o/(60*1000) % 60, 2); } break; case 'c': /* preferred locale date and time stamp */ subfmt = lc->get(vmg_ subfmtl, LC_FMT_TIMESTAMP); goto do_subfmt; case 'D': /* short date - %m/%d/%y */ subfmt = lc->get(vmg_ subfmtl, LC_FMT_SHORTDATE); goto do_subfmt; case 'F': /* database-style date: %Y-%m-%d */ subfmt = "%Y-%m-%d"; subfmtl = 8; goto do_subfmt; case 's': /* Unix Epoch timestamp as an integer */ { /* * The Unix timestamp is the number of seconds after * (or before, if negative) 1/1/1970 00:00 UTC. First * calculate the number of days after (before) 1/1/1970 * by subtracting the Unix Epoch day number from our * internal day number; then multiply that by the * number of seconds in a day (24*60*60) to get the * Unix timestamp of midnight on that day, then add the * number of seconds into the day ('daytime' stores * milliseconds, so divide it by 1000). * * Note that we need to adjust back to UTC by * subtracting the time zone offset. * * As we've discussed elsewhere in this file, a double * is big enough for the Unix timestamp value for any * day number we can store, and represents it exactly * since we're working in whole numbers. */ double utime = (dayno - caldate_t::UNIX_EPOCH_DAYNO) * 24.*60.*60. + daytime/1000 - tzofs/1000; /* format it to a buffer and copy to the output */ char ubuf[60]; sprintf(ubuf, "%.0lf", utime); wrtstr(ubuf); } break; case 'x': /* preferred locale date representation */ subfmt = lc->get(vmg_ subfmtl, LC_FMT_DATE); goto do_subfmt; case '%': /* a literal % */ wrtch('%'); break; default: /* * anything else is an error; copy the whole %x literally * to make the unparsed format character apparent */ wrtch('%'); wrtch(*fmt); break; } } else { /* it's an ordinary character, so copy it literally */ wrtch(*fmt); } } /* add a nul if there's room (but don't count it in the result length) */ if (buflen > 0) *buf++ = '\0'; /* return the total space needed */ return outlen; } /* done with our local macros */ #undef wrtch #undef wrt2dig /* ------------------------------------------------------------------------ */ /* * compareTo method */ int CVmObjDate::getp_compareTo(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the other date */ CVmObjDate *other = vm_val_cast(CVmObjDate, G_stk->get(0)); if (other == 0) err_throw(VMERR_BAD_TYPE_BIF); /* get the respective dates */ int32_t dayno = get_ext()->dayno; int32_t daytime = get_ext()->daytime; int32_t odayno = other->get_ext()->dayno; int32_t odaytime = other->get_ext()->daytime; /* do the comparison */ retval->set_int(dayno != odayno ? dayno - odayno : daytime - odaytime); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getDate method */ int CVmObjDate::getp_getDate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get my date in local time */ int32_t dayno = get_local_date(get_tz_arg(vmg_ 0, argc)); /* express it as a calendar date */ caldate_t cd(dayno); /* return [year, month, monthday, weekday] */ make_int_list(vmg_ retval, 4, cd.y, cd.m, cd.d, cd.weekday() + 1); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getJulianDay method */ int CVmObjDate::getp_getJulianDay(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the Julian day number for midnight UTC on my day number */ caldate_t cd(get_ext()->dayno); bignum_t<32> jday(cd.julian_dayno()); /* add my fraction of a day past midnight UTC */ bignum_t<32> jt((long)get_ext()->daytime); jday += jt / (long)(24*60*60*1000); /* return a BigNumber result */ retval->set_obj(CVmObjBigNum::create(vmg_ FALSE, &jday)); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getJulianDate method */ int CVmObjDate::getp_getJulianDate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get my date in local time */ int32_t dayno = get_local_date(get_tz_arg(vmg_ 0, argc)); /* get the julian date */ caldate_t cd(dayno); int y, m, d; cd.julian_date(y, m, d); /* * Return [year, month, monthday, weekday]. Note that there's no * separate Julian weekday calculation, since the Julian calendar and * Gregorian agree on the day of the week for every day. */ make_int_list(vmg_ retval, 4, y, m, d, cd.weekday() + 1); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getISOWeekDate method */ int CVmObjDate::getp_getISOWeekDate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get my date in local time */ int32_t dayno = get_local_date(get_tz_arg(vmg_ 0, argc)); /* express it as a calendar date */ caldate_t cd(dayno); /* return [iso year, iso week, iso day] */ int iy; int iw = cd.iso_weekno(&iy); make_int_list(vmg_ retval, 3, iy, iw, cd.iso_weekday()); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getTime method */ int CVmObjDate::getp_getTime(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get my date and time in local time */ int32_t dayno, daytime; get_local_time(dayno, daytime, get_tz_arg(vmg_ 0, argc)); /* return [hour, minute, second, msec] */ make_int_list(vmg_ retval, 4, daytime/(24*60*60*1000), daytime/(60*60*1000) % 60, daytime/(60*1000) % 60, daytime % 1000); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * addInterval method */ int CVmObjDate::getp_addInterval(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the interval list */ vm_val_t *lst = G_stk->get(0); if (!lst->is_listlike(vmg0_)) err_throw(VMERR_BAD_TYPE_BIF); /* retrieve my date as a calendar object */ int32_t dayno = get_ext()->dayno; caldate_t cd(dayno); /* retrieve my time as a double, to allow for overflows from int32 */ double daytime = get_ext()->daytime; /* add each element from the list */ int cnt = lst->ll_length(vmg0_); for (int i = 1 ; i <= cnt ; ++i) { /* get this interval element */ vm_val_t ele; lst->ll_index(vmg_ &ele, i); /* treat nil as zero */ if (ele.typ == VM_NIL) continue; /* add it to the appropriate date or time component */ switch (i) { case 1: cd.y += ele.num_to_int(vmg0_); break; case 2: cd.m += ele.num_to_int(vmg0_); break; case 3: cd.d += ele.num_to_int(vmg0_); break; case 4: daytime += ele.num_to_double(vmg0_)*60.*60.*1000.; break; case 5: daytime += ele.num_to_double(vmg0_)*60.*1000.; break; case 6: daytime += ele.num_to_double(vmg0_)*1000.; break; } } /* carry overflows from the time into the day */ double day_carry = floor(daytime / (24.*60.*60.*1000.)); daytime -= day_carry * (24.*60.*60.*1000.); if (day_carry + cd.d > INT32MAXVAL || day_carry + cd.d < INT32MINVAL) err_throw(VMERR_NUM_OVERFLOW); cd.d += (int32_t)day_carry; /* return the new date, using our same local timezone */ retval->set_obj(create(vmg_ FALSE, cd.dayno(), (int32_t)daytime)); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * findWeekday method */ int CVmObjDate::getp_findWeekday(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(2, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the day of week and 'which' arguments */ int32_t wday = G_stk->get(0)->num_to_int(vmg0_); int32_t which = G_stk->get(1)->num_to_int(vmg0_); /* check the weekday and 'which' ranges */ if (wday < 1 || wday > 7 || which == 0) err_throw(VMERR_BAD_VAL_BIF); /* adjust wday to 0=Sunday */ wday -= 1; /* get my date in local time */ CVmTimeZone *tz = get_tz_arg(vmg_ 2, argc); int32_t dayno = get_local_date(tz); /* figure my weekday */ caldate_t cd(dayno); int my_wday = cd.weekday(); /* figure the difference between the target day and my day, mod 7 */ int delta = (7 + wday - my_wday) % 7; /* * Go forward that many days, or backwards (7 - delta), to get the * first occurrence before/after my date. Then go forwards or * backwards by additional weeks as needed. */ if (which > 0) dayno += delta + (which-1)*7; else dayno -= ((7 - delta) % 7) + (-which-1)*7; /* return a new date at midnight on the given day */ int32_t daytime = 0; tz->local_to_utc(dayno, daytime); retval->set_obj(create(vmg_ FALSE, dayno, daytime)); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Get a TimeZone argument. If the argument is nil or omitted, we'll * return the default local system TimeZone object. If it's a TimeZone * object, we'll return that as given. If it's a string or integer, we'll * construct a TimeZone object using that value per the TimeZone * constructors. * * 'argn' is the argument number to fetch; 'argc' is the actual number of * arguments passed to the method. */ CVmTimeZone *CVmObjDate::get_tz_arg(VMG_ uint argn, uint argc) { /* if the argument is nil or missing, use the default local zone */ vm_val_t *argp = G_stk->get(argn); if (argn == argc || argp->typ == VM_NIL) return G_tzcache->get_local_zone(vmg0_); /* check for a TimeZone object */ CVmObjTimeZone *tzobj; if (argn < argc && (tzobj = vm_val_cast(CVmObjTimeZone, argp)) != 0) return tzobj->get_tz(); /* otherwise, accept anything that the TimeZone constructor accepts */ return CVmObjTimeZone::parse_ctor_args(vmg_ argp, argc - argn); } /* ------------------------------------------------------------------------ */ /* * setLocaleInfo method (static method) */ int CVmObjDate::s_getp_setLocaleInfo(VMG_ vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 0, TRUE); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* set up a locale state interface */ CVmDateLocale lc Pvmg0_P; /* parse arguments */ vm_val_t *argp = G_stk->get(0); if (argc == 1 && argp->is_listlike(vmg0_)) { /* list of the first N elements */ int n = argp->ll_length(vmg0_); for (int i = 0 ; i < n ; ++i) { /* get the element */ vm_val_t ele; argp->ll_index(vmg_ &ele, i+1); /* set the next index */ lc.set(vmg_ G_undo, i, &ele); } } else if ((argc & 0x0001) == 0) { /* even number of arguments; treat them Type, String pairs */ for (uint i = 0 ; i < argc ; i += 2, argp -= 2) lc.set(vmg_ G_undo, argp->num_to_int(vmg0_), argp - 1); } else { /* unknown arguments */ err_throw(VMERR_WRONG_NUM_OF_ARGS); } /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } ������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmdate.h��������������������������������������������������������������������������0000664�0000000�0000000�00000055202�12650170723�0015313�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 2012 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmdate.h - CVmObjDate object Function Defines the Date intrinsic class Notes Modified 01/23/12 MJRoberts - Creation */ #ifndef VMDATE_H #define VMDATE_H #include <math.h> #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" /* ------------------------------------------------------------------------ */ /* * Service class for calendar date conversions. This converts between * calendar dates and day number values, using our Epoch of March 1, year * 0. */ struct caldate_t { /* create at the Epoch (3/1/0000 midnight UTC) */ caldate_t() : y(0), m(3), d(1) { } /* create from a calendar date */ caldate_t(int y, int m, int d) : y(y), m(m), d(d) { } /* create from a day number */ caldate_t(int32_t dayno) { set_dayno(dayno); } /* set to the current date (UTC) */ void set_now() { int32_t dayno, daytime; now(dayno, daytime); set_dayno(dayno); } /* get the Julian day number */ double julian_dayno() const { return dayno() + 1721119.5; } /* figure the date on the Julian calendar */ void julian_date(int &jy, int &jm, int &jd) { int32_t z = (int32_t)floor(julian_dayno() + 0.5); int32_t a = z; int32_t b = a + 1524; int32_t c = (int32_t)floor((b - 122.1) / 365.25); int32_t d = (int32_t)floor(365.25 * c); int32_t e = (int32_t)floor((b - d) / 30.6001); jm = e < 14 ? e - 1 : e - 13; jy = jm > 2 ? c - 4716 : c - 4715; jd = b - d - (int32_t)floor(30.6001 * e); } /* set my internal (Gregorian) date from a Julian date */ void set_julian_date(int jy, int jm, int jd) { /* adjust the month to 1-12 range */ div_t mq = div(jm - 1, 12); if (mq.rem <= 0) mq.rem += 12, mq.quot -= 1; jm = mq.rem + 1; jy += mq.quot; /* * Calculate the Julian day number for the julian date, then * subtract our Epoch's Julian day number to get our day number. */ if (jm <= 2) { jy -= 1; jm += 12; } set_dayno((int32_t)(floor(365.25 * (jy + 4716)) + floor(30.6001 * (jm + 1)) + jd - 1524.5 - 1721119.5)); } /* set from a day number */ void set_dayno(int32_t dayno) { int32_t y = (int32_t)floor((10000*(double)dayno + 14780)/3652425.0); int32_t d = dayno - (365*y + divfl(y, 4) - divfl(y, 100) + divfl(y, 400)); if (d < 0) { y -= 1; d = dayno - (365*y + divfl(y, 4) - divfl(y, 100) + divfl(y, 400)); } int32_t m = (100*d + 52)/3060; this->y = y + (m+2)/12; this->m = (m + 2)%12 + 1; this->d = d - (m*306 + 5)/10 + 1; } /* * Normalize the date. This takes the current m/d/y setting, * calculates the day number it represents, and then figure the m/d/y * for that day number. This ensures that a date entered with an * overflow in one of the fields (e.g., "February 30") is translated * into a proper calendar date. */ void normalize_date() { set_dayno(dayno()); } /* * Integer divide-and-get-floor calculation - i.e., round towards * negative infinity. This is the same as ordinary C integer division * for positive numbers, but for negative numbers most C * implementations round towards zero. */ static inline int32_t divfl(int32_t a, int32_t b) { ldiv_t ld = ldiv(a, b); return (ld.rem < 0 ? ld.quot - 1 : ld.quot); } /* get the day number of this calendar date */ int32_t dayno() const { /* * Adjust the month to our odd range where the year starts in * March: 0 = March, 1 = April, ... 11 = Feb. (If this yields a * negative remainder, adjust to a positive index by going back a * year.) */ div_t mq = div(this->m - 3, 12); if (mq.rem < 0) mq.rem += 12, mq.quot -= 1; int m = mq.rem; int y = this->y + mq.quot; return 365*y + divfl(y, 4) - divfl(y, 100) + divfl(y, 400) + (m*306 + 5)/10 + (d-1); } /* day number of the Unix Epoch (1/1/1970 UTC) */ static const int32_t UNIX_EPOCH_DAYNO = 719468; /* get the weekday for this date (0=Sunday, 1=Monday, etc) */ int weekday() const { const static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; int yy = y - (m < 3 ? 1 : 0); return (yy + yy/4 - yy/100 + yy/400 + t[m-1] + d) % 7; } /* get the ISO weekday for this date (1=Monday, ... 7=Sunday) */ int iso_weekday() const { int w = weekday(); return w == 0 ? 7 : w; } /* * Figure the week number, where week 1 starts on the first <weekday> * of the year (0=Sunday). */ int weekno(int weekday) const { /* * Figure days until the first <weekday> of the year: that's the * start of week 1, so the day number of the start of week 1 is the * jan1 day number plus this delta. The start of week 0 is 7 days * before that. */ caldate_t jan1(y, 1, 1); int delta = (7 + weekday - jan1.weekday()) % 7; int week0_dayno = jan1.dayno() + delta - 7; /* figure which week we're in and return the result */ return (dayno() - week0_dayno)/7; } /* * Figure the ISO-8601 week number of this date. Week 1 is the week * containing the year's first Thursday, and the first day of each week * is Monday. Fills in 'year' (if provided) with the year the week * belongs to, which might be the previous or next calendar year. */ int iso_weekno(int *year) const { /* * Find the Thursday of the current ISO week. To do this, figure * our weekday, subtract the ISO weekday number minus 1 to get the * nearest preceding Monday (the start of the ISO week), then add 3 * to get the following Thursday. The year containing the Thursday * of a given week is by definition the year which that whole week * (starting on Monday) belongs to. */ int wday = iso_weekday(); int32_t this_thu_dayno = dayno() - (wday-1) + 3; caldate_t this_thu(this_thu_dayno); /* the year containing the Thursday is the week's calendar year */ if (year != 0) *year = this_thu.y; /* * Find the first Thursday of the calendar year we just calculated. * ISO week 1 of a given calendar year is the week that contains * the first Thursday of the year. So find the first Thursday on * or after Jan 1. */ caldate_t jan1(this_thu.y, 1, 1); int jan1_wday = jan1.weekday(); int32_t first_thu_dayno = jan1.dayno() + ((11 - jan1_wday) % 7); /* * Calculate the number of weeks (== the number of days divided by * seven) between the Thursday of the target week and the Thursday * of the first week. This gives us the difference in week * numbers; since the first week is week #1, adding 1 to the * difference gives us the target week number. */ return ((this_thu_dayno - first_thu_dayno)/7) + 1; } /* get the current system date/time (UTC) */ static void now(int32_t &dayno, int32_t &daytime); /* normalize a date/time to bring the time within 00:00-24:00 */ static void normalize(int32_t &dayno, int32_t &daytime); /* calendar year, month (1=January), and day of month */ int y; int m; int d; }; /* ------------------------------------------------------------------------ */ /* * Multi-calendar interface. This virtualizes the calculations in * caldate_t for different calendars. */ struct multicaldate_t { virtual ~multicaldate_t() { } virtual int32_t dayno() const = 0; virtual void set_dayno(int32_t d) = 0; virtual int weekday() const = 0; virtual int iso_weekday() const = 0; virtual int iso_weekno(int *year) const = 0; /* get the day number of January 1 of year 'y' */ virtual int32_t jan1_dayno() const = 0; /* * get the week number for this date, for the week starting on the * given weekday */ int weekno(int weekday) const { /* * Figure days until the first <weekday> of the year: that's the * start of week 1, so the day number of the start of week 1 is the * jan1 day number plus this delta. The start of week 0 is 7 days * before that. */ caldate_t jan1(jan1_dayno()); int delta = (7 + weekday - jan1.weekday()) % 7; int week0_dayno = jan1.dayno() + delta - 7; /* figure which week we're in and return the result */ return (dayno() - week0_dayno)/7; } virtual void set_date(int y, int m, int d) = 0; int y() const { return y_; } int m() const { return m_; } int d() const { return d_; } protected: /* year, month, day on the subclass calendar */ int y_, m_, d_; }; struct gregcaldate_t: multicaldate_t { gregcaldate_t() { } gregcaldate_t(int32_t d) { gregcaldate_t::set_dayno(d); } virtual int32_t dayno() const { return cd.dayno(); } virtual void set_dayno(int32_t d) { cd.set_dayno(d); y_ = cd.y; d_ = cd.d; m_ = cd.m; } virtual void set_date(int y, int m, int d) { y_ = cd.y = y; m_ = cd.m = m; d_ = cd.d = d; } virtual int32_t jan1_dayno() const { caldate_t jan1(y_, 1, 1); return jan1.dayno(); } virtual int weekday() const { return cd.weekday(); } virtual int iso_weekday() const { return cd.iso_weekday(); } virtual int iso_weekno(int *year) const { return cd.iso_weekno(year); } caldate_t cd; }; struct julcaldate_t: multicaldate_t { virtual int32_t dayno() const { return cd.dayno(); } virtual void set_dayno(int32_t d) { cd.set_dayno(d); cd.julian_date(y_, m_, d_); } virtual void set_date(int y, int m, int d) { cd.set_julian_date(y, m, d); y_ = y; m_ = m; d_ = d; } virtual int32_t jan1_dayno() const { caldate_t jan1; jan1.set_julian_date(y_, 1, 1); return jan1.dayno(); } virtual int weekday() const { return cd.weekday(); } virtual int iso_weekday() const { return cd.iso_weekday(); } virtual int iso_weekno(int *year) const { return cd.iso_weekno(year); } caldate_t cd; }; /* ------------------------------------------------------------------------ */ /* * A Date value is stored internally as two 32-bit integers: the day number * of the date, which is the number of days since March 1, year 0, midnight * UTC; and the time of day as the number of milliseconds past midnight * (UTC) on that day. * * The image file data block is arranged as follows: * *. UINT4 day_number *. UINT2 time_of_day_ms */ /* ------------------------------------------------------------------------ */ /* * Forward declarations */ typedef struct date_parse_result date_parse_result; /* ------------------------------------------------------------------------ */ /* * Our in-memory extension data structure. This contains the date (as a * Gregorian day number since March 1, 0000 UTC), time of day (as the * number of milliseconds past midnight), and the TimeZone object that we * use to determine the local time zone for formatting the date as a string * or extracting calendar or clock elements. */ struct vm_date_ext { /* allocate the structure */ static vm_date_ext *alloc_ext(VMG_ class CVmObjDate *self); /* day number - number of days since March 1, year 0000 UTC */ int32_t dayno; /* time of day - number of milliseconds past midnight UTC on dayno*/ int32_t daytime; }; /* ------------------------------------------------------------------------ */ /* * CVmObjDate intrinsic class definition */ class CVmObjDate: public CVmObject { friend class CVmMetaclassDate; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is this a CVmObjDate object? */ static int is_date_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* get my date and time values */ int32_t get_dayno() const { return get_ext()->dayno; } int32_t get_daytime() const { return get_ext()->daytime; } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* create with a given timestamp */ static vm_obj_id_t create(VMG_ int in_root_set, int32_t dayno, int32_t daytime); /* create from an os_time_t value */ static vm_obj_id_t create_from_time_t(VMG_ int in_root_set, os_time_t t); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* cast to a string */ const char *cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *newstr) const; /* format into a string buffer */ void format_string_buf(VMG_ char *buf, size_t buflen) const; /* format using a template */ size_t format_date(VMG_ char *buf, size_t buflen, const char *fmt, size_t fmtlen, class CVmDateLocale *lc, const multicaldate_t *date, int32_t dayno, int32_t daytime, const char *tzabbr, int32_t tzofs) const; /* date arithmetic - add an integer or BigNumber to add days */ int add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* * date arithmetic - subtract an integer or BigNumber to subtract days * from the date, or subtract another Date to calculate the number of * days between the dates */ int sub_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* compare two dates */ int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const; int compare_to(VMG_ vm_obj_id_t self, const vm_val_t *val) const; /* * receive savepoint notification - we don't keep any * savepoint-relative records, so we don't need to do anything here */ void notify_new_savept() { } /* apply an undo record - we're immutable, so there's no undo */ void apply_undo(VMG_ struct CVmUndoRecord *) { } /* discard an undo record */ void discard_undo(VMG_ struct CVmUndoRecord *) { } /* mark our undo record references */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* mark our references (we have none) */ void mark_refs(VMG_ uint) { } /* remove weak references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* Date objects are immutable, so they can't change after loading */ int is_changed_since_load() const { return FALSE; } protected: /* create a with no initial contents */ CVmObjDate() { ext_ = 0; } /* create with a given timestamp */ CVmObjDate(VMG_ int32_t dayno, uint32_t daytime); /* get my extension data */ vm_date_ext *get_ext() const { return (vm_date_ext *)ext_; } /* get my date/time in the given time zone; returns the format abbr */ const char *get_local_time( int32_t &dayno, int32_t &daytime, class CVmTimeZone *tz) const { int32_t ofs; return get_local_time(dayno, daytime, ofs, tz); } /* * get my date/time in the given time zone, filling in the time zone's * offset from UTC in milliseconds */ const char *get_local_time( int32_t &dayno, int32_t &daytime, int32_t &tzofs, class CVmTimeZone *tz) const; /* get my date (as a day number since 1/1/0000) in the local time zone */ int32_t get_local_date(class CVmTimeZone *tz) const { int32_t dayno, daytime; (void)get_local_time(dayno, daytime, tz); return dayno; } /* get a TimeZone argument to one of our methods */ static class CVmTimeZone *get_tz_arg(VMG_ uint argn, uint argc); /* load or reload image data */ void load_image_data(VMG_ const char *ptr, size_t siz); /* internal parsing routines */ static int parse_string_fmt( VMG_ date_parse_result *res, const char *&str, size_t &len, const char *fmt, size_t fmtlen, class CVmDateLocale *lc); static int parse_date_string( VMG_ int32_t &dayno, int32_t &daytime, const char *str, const vm_val_t *custom, class CVmDateLocale *lc, multicaldate_t *cal, int32_t refday, int32_t reftime, CVmTimeZone *reftz, struct date_parse_result *resultp, struct date_parse_string *fmtlist, int *nfmtlist); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* parseDate method */ int getp_parseDate(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc) { return s_getp_parseDate(vmg_ retval, argc); } /* static property evaluator for parseDate */ static int s_getp_parseDate(VMG_ vm_val_t *retval, uint *argc); /* parseJulianDate method */ int getp_parseJulianDate(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc) { return s_getp_parseJulianDate(vmg_ retval, argc); } /* static property evaluator for parseDate */ static int s_getp_parseJulianDate(VMG_ vm_val_t *retval, uint *argc); /* common handler for parseDate, parseJulianDate */ static int common_parseDate(VMG_ vm_val_t *retval, uint *argc, multicaldate_t *cal); /* general handler for formatDate, formatJulianDate */ int formatDateGen(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, multicaldate_t *date); /* formatDate method */ int getp_formatDate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* formatJulianDate method */ int getp_formatJulianDate( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* compareTo */ int getp_compareTo(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getDate method */ int getp_getDate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getJulianDay method */ int getp_getJulianDay( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getJulianDate method */ int getp_getJulianDate( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getISOWeekDate */ int getp_getISOWeekDate( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getTime method */ int getp_getTime(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* addInterval method */ int getp_addInterval(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* findWeekday method */ int getp_findWeekday(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* setLocaleInfo method */ int getp_setLocaleInfo(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc) { return s_getp_setLocaleInfo(vmg_ retval, argc); } /* static property evaluator for setLocalInfo */ static int s_getp_setLocaleInfo(VMG_ vm_val_t *retval, uint *argc); /* property evaluation function table */ static int (CVmObjDate::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * CVmObjDate metaclass registration table object */ class CVmMetaclassDate: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "date/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjDate(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjDate(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjDate::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjDate::call_stat_prop( vmg_ result, pc_ptr, argc, prop); } int set_stat_prop(VMG_ class CVmUndo *, vm_obj_id_t /*self*/, vm_val_t * /* class_state */, vm_prop_id_t /*prop*/, const vm_val_t * /*val*/) { return TRUE; } }; #endif /* VMDATE_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjDate) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmdbg.h���������������������������������������������������������������������������0000664�0000000�0000000�00000074202�12650170723�0015133�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmdbg.h - T3 VM debugging API Function Provides an interface to debugging operations in the VM Notes Modified 11/23/99 MJRoberts - Creation */ #ifndef VMDBG_H #define VMDBG_H #include "vmglob.h" #include "vmfunc.h" #include "vmhash.h" #include "vmobj.h" #include "tcprstyp.h" #include "osifcnet.h" /* ------------------------------------------------------------------------ */ /* * Internal breakpoint tracking record */ struct CVmDebugBp { /* create */ CVmDebugBp(); /* delete */ ~CVmDebugBp(); /* notify of VM termination */ void do_terminate(VMG0_); /* get/set the in-use flag */ int is_in_use() const { return in_use_; } void set_in_use(int f) { in_use_ = f; } /* get/set the disabled flag */ int is_disabled() const { return disabled_; } void set_disabled(int f) { disabled_ = f; } /* * set the breakpoint's information - returns zero on success, * non-zero if an error occurs (such as compiling the condition * expression) */ int set_info(VMG_ const uchar *code_addr, const char *cond, int change, int disabled, char *errbuf, size_t errbuflen); /* get my code address */ const uchar *get_code_addr() const { return code_addr_; } /* * Set the condition text - returns zero on success, non-zero if an * error occurs compiling the expression. If 'change' is true, then we * break when the condition changes; otherwise, we break when the * condition becomes true. */ int set_condition(VMG_ const char *cond, int change, char *errbuf, size_t errbuflen); /* * delete the breakpoint - remove the breakpoint from the code and * mark it as unused */ void do_delete(VMG0_); /* * Set or remove the BP instruction at my code address. If 'always' * is set, we'll update the instruction regardless of whether the * debugger has control or not; normally, we'll only update the * instruction when the debugger doesn't have control, since we * don't leave breakpoint instructions in the code at other times. */ void set_bp_instr(VMG_ int set, int always); /* check to see if I have a condition */ int has_condition() const { return has_cond_ && compiled_cond_ != 0; } /* * Is our condition a stop-on-change condition? This returns true if * so, false if this we instead have a stop-when-true condition. Note * that this information is not meaningful unless has_condition() * returns true. */ int stop_on_change() const { return stop_on_change_; } /* * evaluate the condition - return true if the condition evaluates * to true (non-zero integer, true, or any object, string, or list * value), false if not */ int eval_cond(VMG0_); /* * determine if this is a global breakpoint - a global breakpoint is * one that is not associated with a code location (indicated by a * code address of zero) */ int is_global() const { return code_addr_ == 0; } private: /* code address of breakpoint */ const uchar *code_addr_; /* condition expression */ char *cond_; /* length of buffer allocated for cond, if any */ size_t cond_buf_len_; /* code object with compiled expression */ vm_globalvar_t *compiled_cond_; /* * The previous value of the condition. If this is a stop-on-change * condition, we'll stop as soon as the current value of the expression * differs from this saved value. If we have a condition, but we * haven't yet computed the "old" value, this will have the 'empty' * type. */ vm_globalvar_t *prv_val_; /* * original instruction byte at this breakpoint (we replace the * instruction byte with the BP instruction, but we must remember * the original for when we clear or disable the breakpoint) */ uchar orig_instr_; /* flag: breakpoint is in use */ uint in_use_ : 1; /* flag: breakpoint has a conditional expression */ uint has_cond_ : 1; /* flag: our condition is stop-on-change, not stop-when-true */ uint stop_on_change_ : 1; /* flag: breakpoint is disabled */ uint disabled_ : 1; }; /* maximum number of breakpoints */ const size_t VMDBG_BP_MAX = 100; /* ------------------------------------------------------------------------ */ /* * Structure for saving debugger step modes. This is used to save and * restore the step modes before doing a recursive evaluation. */ struct vmdbg_step_save_t { int old_step; int old_step_in; int old_step_out; }; /* ------------------------------------------------------------------------ */ /* * Debugger API object */ class CVmDebug { friend class CVmRun; friend struct CVmDebugBp; public: CVmDebug(VMG0_); ~CVmDebug(); /* * Initialize. The VM will call this after setting up all of the VM * globals, but before loading the image file. */ void init(VMG_ const char *image_filename); /* * Initialization phase 2 - this is called just after we finish * loading the image file. The debugger can perform any final * set-up that can't be handled until after the image is loaded. */ void init_after_load(VMG0_); /* * Terminate. The VM will call this when shutting down, before * deleting any of the globals. */ void terminate(VMG0_); /* * Determine if the loaded program was compiled for debugging. * We'll check to see if the image loader found a GSYM (global * symbol table) block in the file. */ int image_has_debug_info(VMG0_) const; /* determine if the debugger has control */ int is_in_debugger() const { return in_debugger_ != 0; } /* * Get the break event object. Routines that wait for OS_Event list * should include this in their event lists, so that they unblock * immediately if the user requests a manual debugger break. */ class OS_Event *get_break_event() { break_event_->add_ref(); return break_event_; } /* -------------------------------------------------------------------- */ /* * Add an entry to a reverse-lookup hash table. The image file * loader must call this function for each object, function, and * property symbol it loads from the debug records in an image file. */ void add_rev_sym(const char *sym, size_t sym_len, tc_symtype_t sym_type, ulong sym_val); /* * Look up a symbol given the type and identifier. Returns a * pointer to a null-terminated string giving the name of the * symbol, or null if the given identifier doesn't have an * associated symbol. */ const char *objid_to_sym(vm_obj_id_t objid) const { return find_rev_sym(obj_rev_table_, (ulong)objid); } const char *propid_to_sym(vm_prop_id_t propid) const { return find_rev_sym(prop_rev_table_, (ulong)propid); } const char *funcaddr_to_sym(pool_ofs_t func_addr) const { return find_rev_sym(func_rev_table_, (ulong)func_addr); } const char *funchdr_to_sym(VMG_ const uchar *func_addr, char *buf) const; const char *enum_to_sym(ulong enum_id) const { return find_rev_sym(enum_rev_table_, enum_id); } const char *bif_to_sym(uint setidx, uint funcidx) const { ulong idx = ((ulong)setidx << 16) | funcidx; return find_rev_sym(bif_rev_table_, idx); } /* * Given a symbol name, get the final modifying object. If the symbol * is a synthesized modified base object, we'll find the actual symbol * that was used in the source code with 'modify.' */ const char *get_modifying_sym(const char *base_sym) const; /* -------------------------------------------------------------------- */ /* * Method header list */ /* allocate the method header list */ void alloc_method_header_list(ulong cnt); /* * Given a code pool address, find the method header containing the * address. This searches the method header list for the nearest * method header whose address is less than the given address. */ const uchar *find_method_header(VMG_ const uchar *addr); /* get the number of method headers */ ulong get_method_header_cnt() const { return method_hdr_cnt_; } /* set the given method header list entry */ void set_method_header(ulong idx, ulong addr) { /* store the given entry */ method_hdr_[idx] = addr; } /* get the given method header entry */ ulong get_method_header(ulong idx) const { return method_hdr_[idx]; } /* -------------------------------------------------------------------- */ /* * Get information on the source location at a given stack level. * If successful, fills in the filename pointer and line number and * returns zero. If no source information is available at the stack * level, returns non-zero to indicate failure. * * Level 0 is the current stack level, 1 is the enclosing frame, and * so on. */ int get_source_info(VMG_ const char **fname, unsigned long *linenum, int level) const; /* * Enumerate local variables at the given stack level - 0 is the * current stack level, 1 is the enclosing frame, and so on. */ void enum_locals(VMG_ void (*cbfunc)(void *, const char *, size_t), void *cbctx, int level); /* * Build a stack trace listing, invoking the callback for each level * of the stack trace. If 'source_info' is true, we'll include the * source file name and line number for each point in the trace. */ void build_stack_listing(VMG_ void (*cbfunc)(void *ctx, const char *str, int strl), void *cbctx, int source_info); /* -------------------------------------------------------------------- */ /* * Breakpoints */ /* * Toggle a breakpoint at the given code location. Returns zero on * success, non-zero on failure. If successful, fills in *bpnum * with the breakpoint ID, and fills in *did_set with true if we set * a breakpoint, false if we deleted an existing breakpoint at the * location. cond is null if the breakpoint is unconditional, or a * pointer to a character string giving the source code for an * expression that must evaluate to true when the breakpoint is * encountered for the breakpoint to suspend execution. */ int toggle_breakpoint(VMG_ const uchar *code_addr, const char *cond, int change, int *bpnum, int *did_set, char *errbuf, size_t errbuflen); /* toggle the disabled status of a breakpoint */ void toggle_breakpoint_disable(VMG_ int bpnum); /* set a breakpoint's disabled status */ void set_breakpoint_disable(VMG_ int bpnum, int disable); /* determine if a breakpoint is disabled - returns true if so */ int is_breakpoint_disabled(VMG_ int bpnum); /* set a breakpoint's condition expression - returns 0 on success */ int set_breakpoint_condition(VMG_ int bpnum, const char *cond, int change, char *errbuf, size_t errbuflen); /* delete a breakpoint given the breakpoint ID */ void delete_breakpoint(VMG_ int bpnum); /* -------------------------------------------------------------------- */ /* * Set execution mode. These calls do not immediately resume * execution, but merely set the mode that will be in effect when * execution resumes. These calls can only be made while the * program is stopped. */ /* * Set single-step mode to STEP IN. In this mode, we will stop as * soon as we reach a new statement. */ void set_step_in() { /* set single-step and step-in modes */ single_step_ = TRUE; step_in_ = TRUE; step_out_ = FALSE; /* we're in run mode again */ break_event_->reset(); } /* * Set DEBUG TRACE mode. This activates single-step mode from within a * VM intrinsic. */ void set_debug_trace() { /* * set single-step mode so that we stop as soon as we're back in * byte code */ set_step_in(); /* * clear any current statement location, so that we stop * immediately as soon as we get back to byte code, even if our * last debugger entry was in the same line of code */ cur_stm_start_ = cur_stm_end_ = 0; } /* * Set single-step mode for a 'break' key (Ctrl-C, Ctrl+Break, etc., * according to local conventions). This asynchronously interrupts the * interpreter and breaks into the debugger while the program is * running, which is useful to break out of infinite loops or very * lengthy processing. */ void set_break_stop() { /* set single-step-in mode */ set_step_in(); /* break out of any event wait */ break_event_->signal(); /* * Forget our last execution location, so that we'll stop again * even if we haven't moved from our last break location. Since * we're explicitly breaking execution, we want to stop whether * we've gone anywhere or not. */ cur_stm_start_ = cur_stm_end_ = 0; } /* * Set single-step mode to STEP OVER. In this mode, we will stop as * soon as we reach a new statement at the same stack level as the * current statement or at an enclosing stack level. */ void set_step_over(VMG0_); /* * Set single-step mode to STEP OUT. In this mode, we will stop as * soon as we reach a new statement at a stack level enclosing the * current statement's stack level. */ void set_step_out(VMG0_); /* * Set single-step mode to GO. In this mode, we won't stop until we * reach a breakpoint. */ void set_go() { /* clear single-step mode */ single_step_ = FALSE; step_in_ = FALSE; step_out_ = FALSE; break_event_->reset(); } /* * Save the step mode in preparation for a recursive execution (of a * debugger expression), and set the mode for the recursive * execution. Turns off stepping modes. */ void prepare_for_eval(vmdbg_step_save_t *info) { /* save the original execution mode */ info->old_step = single_step_; info->old_step_in = step_in_; info->old_step_out = step_out_; /* set execution mode to RUN */ single_step_ = FALSE; step_in_ = FALSE; step_out_ = FALSE; } /* restore original execution modes after recursive execution */ void restore_from_eval(vmdbg_step_save_t *info) { single_step_ = info->old_step; step_in_ = info->old_step_in; step_out_ = info->old_step_out; } /* * format a value, allocating space (the caller must free the space, * using t3free) */ char *format_val(VMG_ const struct vm_val_t *val); /* format a value into a buffer */ size_t format_val(VMG_ char *buf, size_t buflen, const struct vm_val_t *val); /* format a value in the special __value# format */ void format_special(VMG_ char *buf, size_t buflen, const struct vm_val_t *val); /* -------------------------------------------------------------------- */ /* * Set the execution pointer to a new location. The new code * address is given as an absolute code pool address. The new * location must be within the current method. Updates * *exec_ofs_ptr with the method offset of the new location. */ int set_exec_ofs(const uchar **exec_ptr, const uchar *code_addr) { /* set the method offset pointer */ *exec_ptr = code_addr; /* success */ return 0; } /* * Determine if a code location is within the current active method. * Returns true if so, false if not. */ int is_in_current_method(VMG_ const uchar *code_addr); /* -------------------------------------------------------------------- */ /* * Evaluate an expression. Returns zero on success, non-zero on * failure. 'level' is the stack level at which to evaluate the * expression - 0 is the currently active method, 1 is its caller, * and so on. */ int eval_expr(VMG_ char *result, size_t result_len, const char *expr, int level, int *is_lval, int *is_openable, void (*aggcb)(void *, const char *, int, const char *), void *aggctx, int speculative); /* * Compile an expression. Fills in *code_obj with a handle to the * code pool object containing the compiled byte-code. If dst_buf * is non-null, we will format a message describing any error that * occurs into the buffer. * * We'll compile the expression using the given local symbol table * and the given active stack level. Note that the local symbol * table specified need not be the local symbol table for the given * active stack level, since we're only compiling, not evaluating, * the expression; for example, when compiling a condition * expression for a breakpoint, we'll normally compile the * expression in the context of the breakpoint's source location, * which might not even be in the active stack when the condition is * compiled. */ void compile_expr(VMG_ const char *expr, int level, class CVmDbgSymtab *local_symtab, int self_valid, int speculative, int *is_lval, vm_obj_id_t *code_obj, struct CVmDynCompResults *results); /* -------------------------------------------------------------------- */ /* * Get/set the UI context. This information is for use by the UI * code. We don't use the UI context for anything; we just maintain * it so that the UI can keep track of what's going on between * calls. */ void *get_ui_ctx() const { return ui_ctx_; } void set_ui_ctx(void *ctx) { ui_ctx_ = ctx; } protected: /* -------------------------------------------------------------------- */ /* * CVmRun API - the byte-code execution loop uses these routines */ /* * Step into the debugger - the byte-code execution loop calls this * just before executing each instruction when the interpreter is in * single-step mode, or when a breakpoint is encountered. We'll * activate the debugger user interface if necessary. This returns * in order to resume execution. bp is true if we encountered a * breakpoint, false if we're single-stepping. */ void step(VMG_ const uchar **pc_ptr, const uchar *entry, int bp, int error_code); /* * Step through a return. This doesn't actually stop in the debugger, * but for the purposes of STEP OVER, notes that we're leaving a frame * level. This is unnecessary in most cases, since we'd do a step() at * the next instruction in the caller anyway. However, if the caller * is native code that will turn around and call another byte-code * routine, this ensures that we notice that we've left one byte-code * routine and entered another. If we didn't do this, and the next * routine called had more arguments, we'd miss single-stepping into * the next routine because we'd think we were at an enclosing level * rather than a peer level. */ void step_return(VMG0_); /* * synchronize the internal execution point - acts like a step() * without actually stopping */ void sync_exec_pos(VMG_ const uchar *pc_ptr, const uchar *entry); /* * Get single-step mode - returns true if we're stopping at each * instruction, false if we're running until we hit a breakpoint. * The byte-code execution loop should call step() on each * instruction if this returns true. * * We must also single-step through code if there are any active * global breakpoints, even when the user is not interactively * single-stepping. */ int is_single_step() const { return single_step_ || global_bp_cnt_ != 0; } /* -------------------------------------------------------------------- */ /* * Internal operations */ /* * Get information on the execution location at the given stack * level */ int get_stack_level_info(VMG_ int level, class CVmFuncPtr *func_ptr, class CVmDbgLinePtr *line_ptr, const uchar **stm_start, const uchar **stm_end) const; /* look up a symbol in one of our reverse mapping tables */ const char *find_rev_sym( const class CVmHashTable *hashtab, ulong val) const; /* find a breakpoint given a code address */ CVmDebugBp *find_bp(const uchar *code_addr); /* allocate a new breakpoint record */ CVmDebugBp *alloc_bp(); /* * suspend/resume breakpoints - this doesn't affect the permanent * status of any breakpoint, but simply allows for removal of BP * instructions from the code while the debugger has control */ void suspend_all_bps(VMG0_); void restore_all_bps(VMG0_); private: /* flag: debugger has control */ unsigned int in_debugger_ : 1; /* * single-step mode - if this is true, we're stepping through code; * otherwise, we're running until we hit a breakpoint */ unsigned int single_step_ : 1; /* * step-in mode - if this is true, and single_step_ is true, we'll * break as soon as we reach a new statement anywhere; otherwise, * we'll stop only if we reach a new statement at the same stack * level as the current statement (in other words, we're stepping * over subroutine calls made by the current statement) */ unsigned int step_in_ : 1 ; /* * Step-out mode - if this is true, and single_step_ is true, we'll * break as soon as we reach a new statement outside the current * call level. (This mode flag is actually only important for * native code interaction, because we actually know to stop on a * step-out using step-over with an enclosing stack level.) */ unsigned int step_out_ : 1; /* * step-over-breakpoint mode - if this is true, we're stepping over * a breakpoint */ unsigned int step_over_bp_ : 1; /* * Original step flags - these store the step flags that will be in * effect after we finish a step_over_bp operation */ unsigned int orig_single_step_ : 1; unsigned int orig_step_in_ : 1; unsigned int orig_step_out_ : 1; /* * flag: we've been initialized during program load; when this flag is * set, we'll have to make corresponding uninitializations when the * program terminates */ unsigned int program_inited_ : 1; /* breakpoint being stepped over */ CVmDebugBp *step_over_bp_bp_; /* * Step-resume stack frame level. When step_in_ is false, we won't * resume stepping code until the frame pointer is at this level or * an enclosing level. */ size_t step_frame_depth_; /* * Method header for the current function. We set this each time we * enter the debugger via step(). */ CVmFuncPtr func_ptr_; /* * Debug records pointer for the current function. We set this each * time we enter the debugger via step(). Note that we must keep * track of whether the debug pointer is valid, because some * functions might not have debug tables at all. */ CVmDbgTablePtr dbg_ptr_; unsigned int dbg_ptr_valid_ : 1; /* function header pointer for current function */ const uchar *entry_; /* current program counter */ const uchar *pc_; /* debugger line records for current statement */ CVmDbgLinePtr cur_stm_line_; /* * Current statement boundaries. We set this each time we enter the * debugger via step(). We usually step through code until we reach * the beginning of a new statement; we use these boundaries to * determine when we leave the current statement. As long as the * current byte-code offset is within these boundaries, (inclusive), * we know we're within the same statement. */ const uchar *cur_stm_start_; const uchar *cur_stm_end_; /* * User interface context. We maintain this location for use by the * UI code to store its context information. */ void *ui_ctx_; /* * Reverse-mapping hash tables for various symbol types. These hash * tables allow us to find the symbol for a given object ID, * property ID, or function address. */ class CVmHashTable *obj_rev_table_; class CVmHashTable *prop_rev_table_; class CVmHashTable *func_rev_table_; class CVmHashTable *enum_rev_table_; class CVmHashTable *bif_rev_table_; /* breakpoints */ CVmDebugBp bp_[VMDBG_BP_MAX]; /* * Number of global breakpoints in effect (when this is non-zero, we * must trace through code even in 'go' mode, so we can evaluate the * global breakpoints repeatedly and thereby catch when the first * one hits). This only counts enabled global breakpoints - if a * global breakpoint is disabled it must be removed from this count, * since we won't need to consider it when checking for hits. */ int global_bp_cnt_; /* host interface (for the compiler's use) */ class CTcHostIfcDebug *hostifc_; /* * method header list - this is a list of the method headers in the * program, sorted by address */ ulong *method_hdr_; ulong method_hdr_cnt_; /* * Debugger break event. We'll signal this whenever the user tells us * to break into the program. Event wait routines (such as * getNetEvent()) can include this in their wait list so that they're * interrupted immediately on a debug break. */ class OS_Event *break_event_; }; /* ------------------------------------------------------------------------ */ /* * Debugger programmatic interface to the user interface. This is to be * implemented by each UI subsystem. */ class CVmDebugUI { public: /* * Initialize. We'll call this once before entering any other * functions, so that the UI code can set up its private * information. The UI can create a private context and store a * pointer via G_debugger->set_ui_ctx(). */ static void init(VMG_ const char *image_filename); /* * Initialization, phase 2 - this is called just after we've * finished loading the image file. */ static void init_after_load(VMG0_); /* * Terminate. We'll call this before terminating, to allow the UI * code to release any resources it allocated. */ static void terminate(VMG0_); /* * Invoke the UI main command loop entrypoint. The engine calls * this whenever we hit a breakpoint or reach a single-step point. * This routine should not return until it is ready to let the * program continue execution. */ static void cmd_loop(VMG_ int bp_number, int error_code, const uchar **pc); }; /* ------------------------------------------------------------------------ */ /* * Hash table entry for reverse-mapping hash tables. These tables allow * the debugger to look up a symbol name given the symbol's value: an * object ID, a property ID, or a function address. We map each of * these types of values to a ulong value, which we use as the hash key. */ class CVmHashEntryDbgRev: public CVmHashEntry { public: /* * Construct the entry. sym_val is the symbol's value (an object * ID, a property ID, or a function address) coerced to a ulong. */ CVmHashEntryDbgRev(ulong sym_val, const char *sym, size_t len); ~CVmHashEntryDbgRev(); /* check for a match */ virtual int matches(const char *str, size_t len) const; /* get the symbol name for this entry */ const char *get_sym() const { return sym_; } size_t get_sym_len() const { return sym_len_; } protected: char *sym_; size_t sym_len_; }; /* * Hash function for reverse mapping tables. This hash function doesn't * make any assumptions about the range of character values, since we're * using sequences of raw binary bytes for hash keys. */ class CVmHashFuncDbgRev: public CVmHashFunc { public: unsigned int compute_hash(const char *str, size_t len) const; }; /* ------------------------------------------------------------------------ */ /* * Condition compilation macros. Some files can be compiled for * stand-alone use or use within a debugger application; when compiled * stand-alone, certain debugger-related operations are removed, which * can improve performance over the debugger-enabled version. */ #ifdef VM_DEBUGGER /* * DEBUGGER-ENABLED VERSION */ /* include debugger-only code */ #define VM_IF_DEBUGGER(x) x /* do NOT include non-debugger-only code */ #define VM_IF_NOT_DEBUGGER(x) #else /* VM_DEBUGGER */ /* * STAND-ALONE (NON-DEBUGGER) VERSION */ /* do NOT include debugger-only code in a stand-alone version */ #define VM_IF_DEBUGGER(x) /* include non-debugger-only code */ #define VM_IF_NOT_DEBUGGER(x) x #endif /* VM_DEBUGGER */ #endif /* VMDBG_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmdict.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000231551�12650170723�0015657�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmdict.cpp - dictionary metaclass Function Notes Modified 01/24/00 MJRoberts - Creation */ #include <stdlib.h> #include <string.h> #include "t3std.h" #include "vmdict.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmhash.h" #include "vmbif.h" #include "vmmeta.h" #include "vmstack.h" #include "vmstr.h" #include "vmlst.h" #include "vmrun.h" #include "vmstrcmp.h" #include "vmpredef.h" /* ------------------------------------------------------------------------ */ /* * Dictionary undo record. Each time we change the dictionary, we track * the change with one of these records - we attach this record to the * standard undo record via the 'ptrval' field. */ struct dict_undo_rec { /* action type */ enum dict_undo_action action; /* object value stored in dictionary record */ vm_obj_id_t obj; /* vocabulary property for word association */ vm_prop_id_t prop; /* length of the word */ size_t len; /* text of the word */ char txt[1]; }; /* ------------------------------------------------------------------------ */ /* * Generic comparator-based hash */ class CVmHashFuncComparator: public CVmHashFunc { public: CVmHashFuncComparator(VMG_ vm_obj_id_t obj) { globals_ = VMGLOB_ADDR; comparator_ = obj; } /* compute the hash value */ unsigned int compute_hash(const char *str, size_t len) const { vm_val_t val; vm_val_t cobj_val; VMGLOB_PTR(globals_); /* set up a recursive call context */ vm_rcdesc rc; rc.init_ret(vmg_ "HashComparator.calcHash"); /* create a string object to represent the argument, and push it */ G_stk->push()->set_obj(CVmObjString::create(vmg_ FALSE, str, len)); /* invoke the calcHash method */ cobj_val.set_obj(comparator_); G_interpreter->get_prop(vmg_ 0, &cobj_val, G_predef->calc_hash_prop, &cobj_val, 1, &rc); /* retrieve the result from R0 */ val = *G_interpreter->get_r0(); /* we need an integer value from the method */ if (val.typ == VM_INT) { /* return the hash value from the comparator */ return val.val.intval; } else { /* no or invalid hash value - throw an error */ err_throw(VMERR_BAD_TYPE_BIF); AFTER_ERR_THROW(return FALSE;) } } private: /* my comparator object */ vm_obj_id_t comparator_; /* system globals */ vm_globals *globals_; }; /* * StringComparator-based hash */ class CVmHashFuncStrComp: public CVmHashFunc { public: CVmHashFuncStrComp(CVmObjStrComp *comp) { comparator_ = comp; } /* compute the hash value */ unsigned int compute_hash(const char *str, size_t len) const { /* ask the StringComparator to do the work */ return comparator_->calc_str_hash(str, len); } private: /* my StringComparator object */ CVmObjStrComp *comparator_; }; /* ------------------------------------------------------------------------ */ /* * dictionary object statics */ /* metaclass registration object */ static CVmMetaclassDict metaclass_reg_obj; CVmMetaclass *CVmObjDict::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjDict:: *CVmObjDict::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjDict::getp_undef, /* 0 */ &CVmObjDict::getp_set_comparator, /* 1 */ &CVmObjDict::getp_find, /* 2 */ &CVmObjDict::getp_add, /* 3 */ &CVmObjDict::getp_del, /* 4 */ &CVmObjDict::getp_is_defined, /* 5 */ &CVmObjDict::getp_for_each_word, /* 6 */ &CVmObjDict::getp_correct /* 7 */ }; /* ------------------------------------------------------------------------ */ /* * Dictionary object implementation */ /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjDict::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id; vm_obj_id_t comp; CVmObjDict *obj; /* check arguments */ if (argc != 0 && argc != 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* get the Comparator, but leave it on the stack as gc protection */ if (argc == 0 || G_stk->get(0)->typ == VM_NIL) comp = VM_INVALID_OBJ; else if (G_stk->get(0)->typ == VM_OBJ) comp = G_stk->get(0)->val.obj; else err_throw(VMERR_BAD_TYPE_BIF); /* * allocate the object ID - this type of construction never creates * a root object */ id = vm_new_id(vmg_ FALSE, TRUE, TRUE); /* create the object */ obj = new (vmg_ id) CVmObjDict(vmg0_); /* set the comparator */ obj->set_comparator(vmg_ comp); /* build an empty initial hash table */ obj->create_hash_table(vmg0_); /* * mark the object as modified since image load, since it doesn't * come from the image file at all */ obj->get_ext()->modified_ = TRUE; /* discard our gc protection */ G_stk->discard(); /* return the new ID */ return id; } /* ------------------------------------------------------------------------ */ /* * constructor */ CVmObjDict::CVmObjDict(VMG0_) { /* allocate our extension structure from the variable heap */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(sizeof(vm_dict_ext), this); /* we have no image data yet */ get_ext()->image_data_ = 0; get_ext()->image_data_size_ = 0; /* no hash table yet */ get_ext()->hashtab_ = 0; /* no Trie yet */ get_ext()->trie_ = 0; /* no non-image entries yet */ get_ext()->modified_ = FALSE; /* no comparator yet */ get_ext()->comparator_ = VM_INVALID_OBJ; set_comparator_type(vmg_ VM_INVALID_OBJ); } /* ------------------------------------------------------------------------ */ /* * notify of deletion */ void CVmObjDict::notify_delete(VMG_ int /*in_root_set*/) { /* free our additional data */ if (ext_ != 0) { /* free our hash table */ if (get_ext()->hashtab_ != 0) delete get_ext()->hashtab_; /* free our Trie */ if (get_ext()->trie_ != 0) delete get_ext()->trie_; /* free the extension */ G_mem->get_var_heap()->free_mem(ext_); } } /* ------------------------------------------------------------------------ */ /* * Set the comparator object */ void CVmObjDict::set_comparator(VMG_ vm_obj_id_t obj) { /* if this is the same as the current comparator, there's nothing to do */ if (obj == get_ext()->comparator_) return; /* remember the new comparator */ get_ext()->comparator_ = obj; /* figure out what kind of compare functions to use */ set_comparator_type(vmg_ obj); /* rebuild the hash tale */ if (get_ext()->hashtab_ != 0) create_hash_table(vmg0_); } /* * Set the string-compare function based on the given comparator. */ void CVmObjDict::set_comparator_type(VMG_ vm_obj_id_t obj) { /* check what kind of object we have */ if (obj == VM_INVALID_OBJ) { /* there's no comparator at all, so simply use an exact comparison */ get_ext()->comparator_type_ = VMDICT_COMP_NONE; } else if (CVmObjStrComp::is_strcmp_obj(vmg_ obj)) { /* it's a StringComparator */ get_ext()->comparator_type_ = VMDICT_COMP_STRCOMP; } else { /* it's a generic comparator object */ get_ext()->comparator_type_ = VMDICT_COMP_GENERIC; } /* remember the object pointer, if there is one */ get_ext()->comparator_obj_ = (obj == VM_INVALID_OBJ ? 0 : vm_objp(vmg_ obj)); } /* ------------------------------------------------------------------------ */ /* * Rebuild the hash table. If there's an existing hash table, we'll move * the entries from the old table to the new table, and delete the old * table. */ void CVmObjDict::create_hash_table(VMG0_) { CVmHashTable *new_tab; CVmHashFunc *hash_func; /* * Create our hash function. If we have a comparator object, base the * hash function on the comparator; use a special hash function if we * specifically have a StringComparator, since we can call these * directly for better efficiency. If we have no comparator, create a * generic exact string match hash function. */ if (get_ext()->comparator_ != VM_INVALID_OBJ) { /* * use our special StringComparator hash function if possible; * otherwise, use a generic comparator hash function */ if (CVmObjStrComp::is_strcmp_obj(vmg_ get_ext()->comparator_)) { /* create a StringComparator hash function */ hash_func = new CVmHashFuncStrComp( (CVmObjStrComp *)vm_objp(vmg_ get_ext()->comparator_)); } else { /* create a generic comparator hash function */ hash_func = new CVmHashFuncComparator( vmg_ get_ext()->comparator_); } } else { /* create a simple exact-match hash */ hash_func = new CVmHashFuncCS(); } /* create the hash table */ new_tab = new CVmHashTable(256, hash_func, TRUE); /* if we had a previous hash table, move its contents to the new table */ if (get_ext()->hashtab_ != 0) { /* copy the old hash table to the new one */ get_ext()->hashtab_->move_entries_to(new_tab); /* delete the old hash table */ delete get_ext()->hashtab_; } /* if we had a previous trie, get rid of it */ if (get_ext()->trie_ != 0) { delete get_ext()->trie_; get_ext()->trie_ = 0; } /* store the new hash table in the extension */ get_ext()->hashtab_ = new_tab; } /* ------------------------------------------------------------------------ */ /* * Check for a match between two strings, using the current comparator. * Returns true if the strings match, false if not; fills in *result_val * with the actual result value from the comparator's matchValues() * function. */ int CVmObjDict::match_strings(VMG_ const vm_val_t *valstrval, const char *valstr, size_t vallen, const char *refstr, size_t reflen, vm_val_t *result_val) { vm_val_t cobj_val; /* check what kind of comparator we have */ switch(get_ext()->comparator_type_) { case VMDICT_COMP_NONE: /* no comparator - compare the strings byte-for-byte */ if (vallen == reflen && memcmp(valstr, refstr, vallen) == 0) { /* match - return true */ result_val->set_int(1); return TRUE; } else { /* no match - return false */ result_val->set_int(0); return FALSE; } case VMDICT_COMP_STRCOMP: /* match the value directly with the StringComparator */ result_val->set_int( ((CVmObjStrComp *)get_ext()->comparator_obj_)->match_strings( valstr, vallen, refstr, reflen)); /* we matched if the result was non-zero */ return result_val->val.intval; case VMDICT_COMP_GENERIC: /* 2nd param: push the reference string, as a string object */ G_stk->push()->set_obj( CVmObjString::create(vmg_ FALSE, refstr, reflen)); /* 1st param: push the value string */ if (valstrval != 0) { /* push the value string as given */ G_stk->push(valstrval); } else { /* no value string was given - create one and push it */ G_stk->push()->set_obj( CVmObjString::create(vmg_ FALSE, valstr, vallen)); } { /* set up a recursive call context */ vm_rcdesc rc; rc.init_ret(vmg_ "HashComparator.matchValues"); /* call the comparator's matchValues method */ cobj_val.set_obj(get_ext()->comparator_); G_interpreter->get_prop(vmg_ 0, &cobj_val, G_predef->match_values_prop, &cobj_val, 2, &rc); } /* get the result from R0 */ *result_val = *G_interpreter->get_r0(); /* we matched if the result isn't 0 or nil */ return !(result_val->typ == VM_NIL || (result_val->typ == VM_INT && result_val->val.intval == 0)); } /* we should never get here, but just in case... */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Calculate a hash value with the current comparator */ unsigned int CVmObjDict::calc_str_hash(VMG_ const vm_val_t *valstrval, const char *valstr, size_t vallen) { vm_val_t result; vm_val_t cobj_val; /* check what kind of comparator we have */ switch(get_ext()->comparator_type_) { case VMDICT_COMP_NONE: /* no comparator - use the hash table's basic hash calculation */ return get_ext()->hashtab_->compute_hash(valstr, vallen); case VMDICT_COMP_STRCOMP: /* calculate the hash directly with the StringComparator */ return ((CVmObjStrComp *)get_ext()->comparator_obj_)->calc_str_hash( valstr, vallen); case VMDICT_COMP_GENERIC: /* push the value string */ if (valstrval != 0) { /* push the value string as given */ G_stk->push(valstrval); } else { /* no value string was given - create one and push it */ G_stk->push()->set_obj( CVmObjString::create(vmg_ FALSE, valstr, vallen)); } { /* set up a recursive call context */ vm_rcdesc rc; rc.init_ret(vmg_ "HashComparator.calcHash"); /* call the comparator object's calcHash method */ cobj_val.set_obj(get_ext()->comparator_); G_interpreter->get_prop(vmg_ 0, &cobj_val, G_predef->calc_hash_prop, &cobj_val, 1, &rc); } /* get the result from R0 */ result = *G_interpreter->get_r0(); /* make sure it's an integer */ if (result.typ != VM_INT) err_throw(VMERR_BAD_TYPE_BIF); /* return the result */ return (unsigned int)result.val.intval; } /* we should never get here, but just in case... */ return 0; } /* ------------------------------------------------------------------------ */ /* * Set a property. We have no settable properties, so simply signal an * error indicating that the set-prop call is invalid. */ void CVmObjDict::set_prop(VMG_ CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { /* no settable properties - throw an error */ err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * get a property */ int CVmObjDict::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from the base object class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * evaluate property: set the comparator */ int CVmObjDict::getp_set_comparator(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc) { static CVmNativeCodeDesc desc(1); vm_obj_id_t comp; /* check arguments */ if (get_prop_check_argc(val, argc, &desc)) return TRUE; /* get the comparator object */ switch(G_stk->get(0)->typ) { case VM_NIL: comp = VM_INVALID_OBJ; break; case VM_OBJ: comp = G_stk->get(0)->val.obj; break; default: err_throw(VMERR_BAD_TYPE_BIF); } /* if there's a global undo object, add undo for the change */ if (G_undo != 0) { dict_undo_rec *undo_rec; /* create an undo record with the original comparator */ undo_rec = alloc_undo_rec(DICT_UNDO_COMPARATOR, 0, 0); undo_rec->obj = get_ext()->comparator_; /* add the undo record */ add_undo_rec(vmg_ self, undo_rec); } /* set our new comparator object */ set_comparator(vmg_ comp); /* mark the object as modified since load */ get_ext()->modified_ = TRUE; /* discard arguments */ G_stk->discard(); /* no return value - just return nil */ val->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * result entry for find_ctx */ struct find_entry { /* matching object */ vm_obj_id_t obj; /* match result code */ vm_val_t match_result; }; /* * page of results for find_ctx */ const int FIND_ENTRIES_PER_PAGE = 32; struct find_entry_page { /* next in list of pages */ find_entry_page *nxt; /* array of entries */ find_entry entry[FIND_ENTRIES_PER_PAGE]; }; /* * enumeration callback context for getp_find */ struct find_ctx { /* set up a context that adds results to our internal list */ find_ctx(VMG_ CVmObjDict *dict_obj, const vm_val_t *val, const char *str, size_t len, vm_prop_id_t prop) { /* remember the dictionary object we're searching */ dict = dict_obj; /* remember the globals */ globals = VMGLOB_ADDR; /* keep a pointer to the search string value */ this->strval = val; this->strp = str; this->strl = len; /* remember the property */ voc_prop = prop; /* no results yet */ results_head = 0; results_tail = 0; result_cnt = 0; /* * set the free pointer to the end of the non-existent current * page, so we know we have to allocate a new page before adding * the first entry */ result_free = FIND_ENTRIES_PER_PAGE; } ~find_ctx() { /* free our list of results pages */ while (results_head != 0) { /* remember the current page */ find_entry_page *cur = results_head; /* move on to the next page */ results_head = results_head->nxt; /* delete the current page */ t3free(cur); } } /* add a result, expanding the result array if necessary */ void add_result(vm_obj_id_t obj, const vm_val_t *match_result) { /* * if we have no pages, or we're out of room on the current page, * allocate a new page */ if (result_free == FIND_ENTRIES_PER_PAGE) { find_entry_page *pg; /* allocate a new page */ pg = (find_entry_page *)t3malloc(sizeof(find_entry_page)); /* link it in at the end of the list */ pg->nxt = 0; if (results_tail != 0) results_tail->nxt = pg; else results_head = pg; results_tail = pg; /* reset the free pointer to the start of the new page */ result_free = 0; } /* add this result */ results_tail->entry[result_free].obj = obj; results_tail->entry[result_free].match_result = *match_result; ++result_free; ++result_cnt; } /* make our results into a list */ vm_obj_id_t results_to_list(VMG0_) { vm_obj_id_t lst_id; CVmObjList *lst; find_entry_page *pg; int idx; /* * Allocate a list of the appropriate size. We need two list * entries per result, since we need to store the object and the * match result code for each result. */ lst_id = CVmObjList::create(vmg_ FALSE, result_cnt * 2); lst = (CVmObjList *)vm_objp(vmg_ lst_id); /* add all entries */ for (idx = 0, pg = results_head ; idx < result_cnt ; pg = pg->nxt) { find_entry *ep; int pg_idx; /* add each entry on this page */ for (ep = pg->entry, pg_idx = 0 ; idx < result_cnt && pg_idx < FIND_ENTRIES_PER_PAGE ; ++idx, ++pg_idx, ++ep) { vm_val_t ele; /* add this entry to the list */ ele.set_obj(ep->obj); lst->cons_set_element(idx*2, &ele); lst->cons_set_element(idx*2 + 1, &ep->match_result); } } /* return the list */ return lst_id; } /* head/tail of list of result pages */ find_entry_page *results_head; find_entry_page *results_tail; /* next available entry on current results page */ int result_free; /* total number of result */ int result_cnt; /* the string to find */ const vm_val_t *strval; const char *strp; size_t strl; /* vocabulary property value to match */ vm_prop_id_t voc_prop; /* VM globals */ vm_globals *globals; /* dictionary object we're searching */ CVmObjDict *dict; }; /* * Callback for getp_find hash match enumeration */ void CVmObjDict::find_cb(void *ctx0, CVmHashEntry *entry0) { find_ctx *ctx = (find_ctx *)ctx0; CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0; vm_val_t match_val; VMGLOB_PTR(ctx->globals); /* if this entry matches the search string, process it */ if (ctx->dict->match_strings(vmg_ ctx->strval, ctx->strp, ctx->strl, entry->getstr(), entry->getlen(), &match_val)) { vm_dict_entry *cur; /* process the items under this entry */ for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_) { /* * If it matches the target property, it's a hit. If we're * searching for any property at all (indicated by the * context property being set to VM_INVALID_PROP), count * everything. */ if (cur->prop_ == ctx->voc_prop || ctx->voc_prop == VM_INVALID_PROP) { /* process the result */ ctx->add_result(cur->obj_, &match_val); } } } } /* ------------------------------------------------------------------------ */ /* * property evaluation - look up a word */ int CVmObjDict::getp_find(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc) { static CVmNativeCodeDesc desc(1, 1); uint orig_argc = (argc != 0 ? *argc : 0); vm_val_t *arg0; vm_val_t *arg1; const char *strp; size_t strl; vm_prop_id_t voc_prop; unsigned int hash; /* check arguments */ if (get_prop_check_argc(val, argc, &desc)) return TRUE; /* * make sure the first argument is a string, but leave it on the stack * for gc protection */ arg0 = G_stk->get(0); if ((strp = arg0->get_as_string(vmg0_)) == 0) err_throw(VMERR_STRING_VAL_REQD); /* scan and skip the string's length prefix */ strl = vmb_get_len(strp); strp += VMB_LEN; /* * get the property ID, which can be omitted or specified as nil to * indicate any property */ if (orig_argc < 2) voc_prop = VM_INVALID_PROP; else if ((arg1 = G_stk->get(1))->typ == VM_NIL) voc_prop = VM_INVALID_PROP; else if (arg1->typ == VM_PROP) voc_prop = arg1->val.prop; else err_throw(VMERR_PROPPTR_VAL_REQD); /* calculate the hash value */ hash = calc_str_hash(vmg_ arg0, strp, strl); /* enumerate everything that matches the string's hash code */ find_ctx ctx(vmg_ this, arg0, strp, strl, voc_prop); get_ext()->hashtab_->enum_hash_matches(hash, &find_cb, &ctx); /* build a list out of the results, and return the list */ val->set_obj(ctx.results_to_list(vmg0_)); /* discard arguments */ G_stk->discard(orig_argc); /* success */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluation - add a word */ int CVmObjDict::getp_add(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc) { vm_obj_id_t obj; vm_val_t str_val; vm_prop_id_t voc_prop; CVmObject *objp; int lstcnt; const char *str; static CVmNativeCodeDesc desc(3); /* check arguments */ if (get_prop_check_argc(val, argc, &desc)) return TRUE; /* pop the object, word string, and property ID arguments */ obj = CVmBif::pop_obj_val(vmg0_); G_stk->pop(&str_val); voc_prop = CVmBif::pop_propid_val(vmg0_); /* ensure that the object value isn't a string or list */ objp = vm_objp(vmg_ obj); if (objp->get_as_list() != 0 || objp->get_as_string(vmg0_) != 0) err_throw(VMERR_OBJ_VAL_REQD); /* if the string argument is a list, add each word from the list */ if (str_val.is_listlike(vmg0_) && (lstcnt = str_val.ll_length(vmg0_)) >= 0) { /* iterate over the list, using 1-based indices */ for (int idx = 1 ; idx <= lstcnt ; ++idx) { /* get the next value from the list */ vm_val_t ele_val; str_val.ll_index(vmg_ &ele_val, idx); /* get the next string value */ if ((str = ele_val.get_as_string(vmg0_)) == 0) err_throw(VMERR_STRING_VAL_REQD); /* set this string */ add_word(vmg_ self, str, obj, voc_prop); } } else if ((str = str_val.get_as_string(vmg0_)) != 0) { /* add the string */ add_word(vmg_ self, str, obj, voc_prop); } else { /* string value required */ err_throw(VMERR_STRING_VAL_REQD); } /* there's no return value */ val->set_nil(); /* success */ return TRUE; } /* * Service routine for getp_add() - add a single string */ void CVmObjDict::add_word(VMG_ vm_obj_id_t self, const char *str, size_t len, vm_obj_id_t obj, vm_prop_id_t voc_prop) { /* add the entry */ int added = add_hash_entry(vmg_ str, len, TRUE, obj, voc_prop, FALSE); /* * if there's a global undo object, and we actually added a new * entry, add undo for the change */ if (added && G_undo != 0) { dict_undo_rec *undo_rec; /* create the undo record */ undo_rec = alloc_undo_rec(DICT_UNDO_ADD, str, len); undo_rec->obj = obj; undo_rec->prop = voc_prop; /* add the undo record */ add_undo_rec(vmg_ self, undo_rec); } /* mark the object as modified since load */ get_ext()->modified_ = TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluation - delete a word */ int CVmObjDict::getp_del(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc) { vm_obj_id_t obj; vm_val_t str_val; vm_prop_id_t voc_prop; int lstcnt; const char *str; static CVmNativeCodeDesc desc(3); /* check arguments */ if (get_prop_check_argc(val, argc, &desc)) return TRUE; /* pop the object, word string, and property ID arguments */ obj = CVmBif::pop_obj_val(vmg0_); G_stk->pop(&str_val); voc_prop = CVmBif::pop_propid_val(vmg0_); /* if the string argument is a list, add each word from the list */ if (str_val.is_listlike(vmg0_) && (lstcnt = str_val.ll_length(vmg0_)) >= 0) { /* iterate over the list, using 1-based indices */ for (int idx = 1 ; idx <= lstcnt ; ++idx) { /* get the next value from the list */ vm_val_t ele_val; str_val.ll_index(vmg_ &ele_val, idx); /* get the next string value */ if ((str = ele_val.get_as_string(vmg0_)) == 0) err_throw(VMERR_STRING_VAL_REQD); /* remove this string */ del_word(vmg_ self, str, obj, voc_prop); } } else if ((str = str_val.get_as_string(vmg0_)) != 0) { /* remove the string */ del_word(vmg_ self, str, obj, voc_prop); } else { /* string value required */ err_throw(VMERR_STRING_VAL_REQD); } /* there's no return value */ val->set_nil(); /* success */ return TRUE; } /* * Service routine for getp_del - delete a string value */ void CVmObjDict::del_word(VMG_ vm_obj_id_t self, const char *str, size_t len, vm_obj_id_t obj, vm_prop_id_t voc_prop) { /* delete this entry */ int deleted = del_hash_entry(vmg_ str, len, obj, voc_prop); /* * if there's a global undo object, and we actually deleted an * entry, add undo for the change */ if (deleted && G_undo != 0) { dict_undo_rec *undo_rec; /* create the undo record */ undo_rec = alloc_undo_rec(DICT_UNDO_DEL, str, len); undo_rec->obj = obj; undo_rec->prop = voc_prop; /* add the undo record */ add_undo_rec(vmg_ self, undo_rec); } /* mark the object as modified since load */ get_ext()->modified_ = TRUE; } /* ------------------------------------------------------------------------ */ /* * callback context for is_defined enumeration */ struct isdef_ctx { isdef_ctx(VMG_ CVmObjDict *dict_obj, vm_val_t *filter, const vm_val_t *val, const char *str, size_t len) { /* remember the VM globals and dictionary object */ globals = VMGLOB_ADDR; dict = dict_obj; /* we haven't yet found a match */ found = FALSE; /* remember the string */ this->strval = *val; this->strp = str; this->strl = len; /* remember the filter function */ filter_func = filter; } /* flag: we've found a match */ int found; /* string value we're matching */ vm_val_t strval; const char *strp; size_t strl; /* our filter callback function, if any */ const vm_val_t *filter_func; /* VM globals */ vm_globals *globals; /* dictionary we're searching */ CVmObjDict *dict; /* recursive native caller context */ vm_rcdesc rc; }; /* * Callback for getp_is_defined hash match enumeration */ void CVmObjDict::isdef_cb(void *ctx0, CVmHashEntry *entry0) { isdef_ctx *ctx = (isdef_ctx *)ctx0; CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0; vm_val_t match_val; VMGLOB_PTR(ctx->globals); /* * if we've already found a match, don't bother checking anything else * - it could be non-trivial to do the string match, so we can save * some work by skipping that if we've already found a match * previously */ if (ctx->found) return; /* * if this entry matches the search string, and it has at least one * defined object/property associated with it, count it as a match */ if (entry->get_head() != 0 && ctx->dict->match_strings(vmg_ &ctx->strval, ctx->strp, ctx->strl, entry->getstr(), entry->getlen(), &match_val)) { /* if there's a filter function to invoke, check with it */ if (ctx->filter_func->typ != VM_NIL) { vm_val_t *valp; /* push the match value parameter */ G_stk->push(&match_val); /* call the filter callback */ G_interpreter->call_func_ptr(vmg_ ctx->filter_func, 1, &ctx->rc, 0); /* get the result */ valp = G_interpreter->get_r0(); /* if it's 0 or nil, it's a rejection of the match */ if (valp->typ == VM_NIL || (valp->typ == VM_INT && valp->val.intval == 0)) { /* it's a rejection of the value - don't count it */ } else { /* it's an acceptance of the value - count it as found */ ctx->found = TRUE; } } else { /* there's no filter, so everything matches */ ctx->found = TRUE; } } } /* * property evaluation - check to see if a word is defined */ int CVmObjDict::getp_is_defined(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc) { static CVmNativeCodeDesc desc(1, 1); uint orig_argc = (argc == 0 ? 0 : *argc); vm_val_t *arg0; const char *strp; size_t strl; vm_val_t filter; unsigned int hash; /* check arguments */ if (get_prop_check_argc(val, argc, &desc)) return TRUE; /* * make sure the argument is a string, but leave it on the stack for * gc protection */ arg0 = G_stk->get(0); if ((strp = arg0->get_as_string(vmg0_)) == 0) err_throw(VMERR_STRING_VAL_REQD); /* read and skip the string's length prefix */ strl = vmb_get_len(strp); strp += VMB_LEN; /* if there's a second argument, it's the filter function */ if (orig_argc >= 2) { /* retrieve the filter */ filter = *G_stk->get(1); } else { /* there's no filter */ filter.set_nil(); } /* calculate the hash code */ hash = calc_str_hash(vmg_ arg0, strp, strl); /* enumerate matches for the string */ isdef_ctx ctx(vmg_ this, &filter, arg0, strp, strl); ctx.rc.init(vmg_ "Dict.isDefined", self, 5, arg0, orig_argc); get_ext()->hashtab_->enum_hash_matches(hash, &isdef_cb, &ctx); /* if we found any matches, return true; otherwise, return nil */ val->set_logical(ctx.found); /* discard arguments */ G_stk->discard(orig_argc); /* success */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * callback context for forEachWord enumerator */ struct for_each_word_enum_cb_ctx { /* callback function value */ vm_val_t *cb_val; /* globals */ vm_globals *vmg; /* recursive native caller context */ vm_rcdesc rc; }; /* * callback for forEachWord enumerator */ static void for_each_word_enum_cb(void *ctx0, CVmHashEntry *entry0) { for_each_word_enum_cb_ctx *ctx = (for_each_word_enum_cb_ctx *)ctx0; CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0; size_t list_idx; vm_obj_id_t str_obj; /* set up access to the VM globals through my stashed context */ VMGLOB_PTR(ctx->vmg); /* create a string object for this entry's word string */ str_obj = CVmObjString::create(vmg_ FALSE, entry->getstr(), entry->getlen()); /* scan the entry's list of word associations */ for (list_idx = 0 ; ; ++list_idx) { vm_dict_entry *p; size_t i; /* * Find the list entry at the current list index. Note that we * scan the list on each iteration for the current index because * we don't want to keep track of any pointers between iterations; * this insulates us from any changes to the underlying list made * in the callback. */ for (i = 0, p = entry->get_head() ; i < list_idx && p != 0 ; p = p->nxt_, ++i) ; /* * if we didn't find a list entry at this index, we're done with * this hashtable entry */ if (p == 0) break; /* * Invoke the callback on this list entry. The arguments to the * callback are the object, the string, and the vocabulary * property of the association: * * func(obj, str, prop) * * Note that the order of the arguments is the same as for * addWord() and removeWord(). */ /* push the vocabulary property of the association */ G_stk->push()->set_propid(p->prop_); /* push the string of the entry */ G_stk->push()->set_obj(str_obj); /* push the object of the association */ G_stk->push()->set_obj(p->obj_); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ ctx->cb_val, 3, &ctx->rc, 0); } } /* * property evaluation - enumerate each word association */ int CVmObjDict::getp_for_each_word(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(1); vm_val_t *cb_val; int orig_argc = (argc != 0 ? *argc : 0); for_each_word_enum_cb_ctx ctx; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get a pointer to the callback value, but leave it on the stack */ cb_val = G_stk->get(0); /* push a self-reference for gc protection while we're working */ G_stk->push()->set_obj(self); /* * enumerate the entries in our underlying hashtable - use the "safe" * enumerator to ensure that we're insulated from any changes to the * hashtable that the callback wants to make */ ctx.vmg = VMGLOB_ADDR; ctx.cb_val = cb_val; ctx.rc.init(vmg_ "Dict.forEachWord", self, 6, cb_val, orig_argc); get_ext()->hashtab_->safe_enum_entries(for_each_word_enum_cb, &ctx); /* discard arguments and gc protection */ G_stk->discard(2); /* there's no return value */ retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Correction list entry. Each time we match a word in the spelling * correction generator, we'll add one of these entries to our list of * matched words. This lets us check for words that we've already found * (since it's often possible to find a match via more than one editing * route). */ struct corr_word { void *operator new(size_t siz, size_t strl) { return t3mallocnew(siz + (strl-1)*sizeof(wchar_t)); } void operator delete(void *ptr) { t3free(ptr); } corr_word(wchar_t *str, size_t strl, int dist, int repl, corr_word *nxt) { memcpy(this->str, str, strl*sizeof(wchar_t)); this->strl = strl; this->dist = dist; this->repl = repl; this->nxt = nxt; } /* next in list */ corr_word *nxt; /* best edit distance found for this word */ int dist; /* number of character replacements */ int repl; /* text of the word (we overallocate the structure to make room) */ size_t strl; wchar_t str[1]; }; /* correction types */ enum corr_type { NoChange, Insertion, Deletion, Replacement, Transposition }; /* * Correction stack element. This represents a state in the correction * process. */ struct corr_state { /* * Allocate space. During the state processing, we need to add one * character to the last state to form certain subsequent states, so * always overallocate our string by one character. This allows us to * build the next-state strings in place in our buffer. */ void *operator new(size_t siz, size_t strl) { return t3mallocnew(siz + strl*sizeof(wchar_t)); } void operator delete(void *ptr) { t3free(ptr); } corr_state(vmdict_TrieNode *root) { init(0, 0, 0, 0, 0, root, NoChange, 0); } corr_state(const wchar_t *str, size_t strl, int dist, int repl, size_t ipos, vmdict_TrieNode *node, corr_type typ, corr_state *nxt) { init(str, strl, dist, repl, ipos, node, typ, nxt); } void init(const wchar_t *str, size_t strl, int dist, int repl, size_t ipos, vmdict_TrieNode *node, corr_type typ, corr_state *nxt) { memcpy(this->str, str, strl*sizeof(wchar_t)); this->strl = strl; this->dist = dist; this->repl = repl; this->ipos = ipos; this->node = node; this->typ = typ; this->nxt = nxt; } /* next state in the stack */ corr_state *nxt; /* edit distance so far */ int dist; /* number of replacement transitions */ int repl; /* current character index in the input string */ size_t ipos; /* current Trie node in the dictionary */ vmdict_TrieNode *node; /* type of last transition */ corr_type typ; /* the string (we overallocate the structure to make room) */ size_t strl; wchar_t str[1]; }; /* correction state stack */ struct corr_stack { corr_stack(vmdict_TrieNode *root) { /* start the stack with an empty initial state */ top = new (0) corr_state(root); } int empty() const { return top == 0; } void push(const wchar_t *str, size_t strl, int dist, int repl, size_t ipos, vmdict_TrieNode *node, corr_type typ) { top = new (strl) corr_state( str, strl, dist, repl, ipos, node, typ, top); } corr_state *pop() { /* retrieve and unlink the top state */ corr_state *ret = top; top = top->nxt; /* return the state we unlinked */ return ret; } /* top of the stack */ corr_state *top; }; /* * property evaluation - get a list of possible corrections for a * misspelled word */ int CVmObjDict::getp_correct(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { const char *str; size_t strl; int max_dist; static CVmNativeCodeDesc desc(2); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the word string and maximum edit distance */ str = CVmBif::pop_str_val(vmg0_); max_dist = CVmBif::pop_int_val(vmg0_); /* get the string length and character pointer */ strl = vmb_get_len(str); str += VMB_LEN; /* for easier processing, convert the string to a wchar_t array */ size_t wcnt = utf8_ptr::to_wcharz(0, 0, str, strl); wchar_t *wstr = new wchar_t[wcnt+1]; utf8_ptr::to_wcharz(wstr, wcnt+1, str, strl); /* if we have a StringComparator, fetch it */ CVmObjStrComp *cmp = 0; size_t trunc_len = 0; if (get_ext()->comparator_type_ == VMDICT_COMP_STRCOMP) { /* get the comparator */ cmp = (CVmObjStrComp *)get_ext()->comparator_obj_; /* note the truncation length */ trunc_len = cmp->trunc_len(); } /* if we don't have the Trie yet, build it */ if (get_ext()->trie_ == 0) build_trie(vmg0_); /* create a stack with the initial empty state */ corr_stack stk(get_ext()->trie_); /* start with an empty result list */ corr_word *results = 0; /* process states until the stack is empty */ while (!stk.empty()) { /* pop the next state */ corr_state *s = stk.pop(); /* * Check for an 'accept' state. This is a state where we've * exhausted the input word, and we're at a Trie node that has at * least one word defined. */ if (s->node->word_cnt != 0 && s->ipos == wcnt) { /* * We've found a matching word (i.e., a candidate dictionary * match for the misspelled input word). Scan the list of * candidates we've already found to see if we've already * matched this word. */ corr_word *w; for (w = results ; w != 0 ; w = w->nxt) { if (w->strl == s->strl && memcmp(w->str, s->str, s->strl*sizeof(wchar_t)) == 0) break; } /* if we didn't find an entry, add a new one */ if (w == 0) results = w = new (s->strl) corr_word( s->str, s->strl, s->dist, s->repl, results); /* keep the lowest edit distance we've found so far */ if (s->dist < w->dist || (s->dist == w->dist && s->repl < w->repl)) { w->dist = s->dist; w->repl = s->repl; } } /* * If we have any edit distance left, try an insertion (i.e., the * input word has an extra letter relative to the dictionary word). * Don't do insertions directly after deletions, though, since * they'd just cancel out. */ if (s->dist < max_dist && s->ipos < wcnt && s->typ != Deletion) stk.push(s->str, s->strl, s->dist + 1, s->repl, s->ipos + 1, s->node, Insertion); /* try each possible dictionary transition from here */ for (vmdict_TrieNode *chi = s->node->chi ; chi != 0 ; chi = chi->nxt) { /* * Build the next-state string. Note that we can just add the * character directly to the current state's string buffer, * since we always allocate state structures with one extra * character of padding for just this purpose. */ s->str[s->strl] = chi->ch; /* * Check for a match to the current character. If we have a * StringComparator, ask the comparator to check the match. * Otherwise just check for an exact character match. */ size_t matchlen = 0; if (s->ipos < wcnt) { if (cmp != 0) { /* we have a comparator - check it for the match */ matchlen = cmp->match_chars( wstr + s->ipos, wcnt - s->ipos, chi->ch); } else { /* no comparator - do a simple character comparison */ if (wstr[s->ipos] == chi->ch) matchlen = 1; } /* if we have a match, push a state for it */ if (matchlen != 0) stk.push(s->str, s->strl + 1, s->dist, s->repl, s->ipos + matchlen, chi, NoChange); } else { /* * we've reached the end of the input; but if the current * edit string is at least the truncation length in the * string comparator, consider this a character match as * well */ if (trunc_len != 0 && s->strl >= trunc_len && s->ipos >= trunc_len) stk.push(s->str, s->strl + 1, s->dist, s->repl, s->ipos, chi, NoChange); } /* if we have any edit distance remaining, try corrections */ if (s->dist < max_dist) { /* try a replaced letter */ if (s->ipos < wcnt && matchlen == 0) stk.push(s->str, s->strl + 1, s->dist + 1, s->repl + 1, s->ipos + 1, chi, Replacement); /* try a deletion (i.e., the input is missing a character) */ if (s->typ != Insertion) stk.push(s->str, s->strl + 1, s->dist + 1, s->repl, s->ipos, chi, Deletion); } /* * if we just did a replacement edit, check to see if it's * actually a transposition */ if (s->typ == Replacement && s->ipos > 0 && s->ipos < wcnt && wstr[s->ipos - 1] == chi->ch && wstr[s->ipos] == s->str[s->strl - 1]) stk.push(s->str, s->strl + 1, s->dist, s->repl - 1, s->ipos + 1, chi, Transposition); } /* we've finished processing the current state */ delete s; } /* done with the wchar_t version of the word */ delete [] wstr; /* * Count up the number of result entries. Only count words with a * non-zero edit distance - words with a zero edit distance are already * matches for the input word, so we can't consider them to be * corrections for it. */ corr_word *r; int i; for (r = results, i = 0 ; r != 0 ; r = r->nxt) { /* if this word has a non-zero edit distance, count it */ if (r->dist != 0) ++i; } /* allocate a List to hold the results */ vm_obj_id_t lst_id = CVmObjList::create(vmg_ FALSE, i); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lst_id); lst->cons_clear(); /* push it for gc protection */ G_stk->push()->set_obj(lst_id); /* create a List [word, distance] for each result */ for (r = results, i = 0 ; r != 0 ; r = r->nxt) { vm_val_t val; /* * If this word has a zero edit distance, skip it. A word with a * zero edit distance is already a match for the original, so it * can't be considered a correction. */ if (r->dist == 0) continue; /* create the sublist */ vm_obj_id_t sub_id = CVmObjList::create(vmg_ FALSE, 3); CVmObjList *sub = (CVmObjList *)vm_objp(vmg_ sub_id); /* add it to the main list */ val.set_obj(sub_id); lst->cons_set_element(i, &val); /* measure the utf8 space needed for the string */ utf8_ptr rp; size_t rplen = rp.setwchars(r->str, r->strl, 0); /* create the word string */ vm_obj_id_t lstr_id = CVmObjString::create(vmg_ FALSE, rplen); CVmObjString *lstr = (CVmObjString *)vm_objp(vmg_ lstr_id); /* encode it */ rp.set(lstr->cons_get_buf()); rp.setwchars(r->str, r->strl, rplen); /* add it to the sublist */ val.set_obj(lstr_id); sub->cons_set_element(0, &val); /* add the edit distance */ val.set_int(r->dist); sub->cons_set_element(1, &val); /* add the character replacement count */ val.set_int(r->repl); sub->cons_set_element(2, &val); /* count it */ ++i; } /* delete the result list */ while (results != 0) { /* unlink this element */ r = results; results = results->nxt; /* delete it */ delete r; } /* return the list value */ retval->set_obj(lst_id); /* done with the gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Create an undo record */ dict_undo_rec *CVmObjDict::alloc_undo_rec(dict_undo_action action, const char *txt, size_t len) { size_t alloc_size; dict_undo_rec *rec; /* * compute the allocation size - start with the structure size, and * add in the length we need to store the string if one is present */ alloc_size = sizeof(dict_undo_rec) - 1; if (txt != 0) alloc_size += len; /* allocate the record */ rec = (dict_undo_rec *)t3malloc(alloc_size); /* set the action */ rec->action = action; /* if there's a word, copy the text */ if (txt != 0) memcpy(rec->txt, txt, len); /* save the word length */ rec->len = len; /* presume we won't store an object or property */ rec->obj = VM_INVALID_OBJ; rec->prop = VM_INVALID_PROP; /* return the new record */ return rec; } /* * add an undo record */ void CVmObjDict::add_undo_rec(VMG_ vm_obj_id_t self, dict_undo_rec *rec) { vm_val_t val; /* add the record with an empty value */ val.set_empty(); if (!G_undo->add_new_record_ptr_key(vmg_ self, rec, &val)) { /* * we didn't add an undo record, so our extra undo information * isn't actually going to be stored in the undo system - hence * we must delete our extra information */ t3free(rec); } } /* * apply undo */ void CVmObjDict::apply_undo(VMG_ CVmUndoRecord *undo_rec) { if (undo_rec->id.ptrval != 0) { dict_undo_rec *rec; /* get my undo record */ rec = (dict_undo_rec *)undo_rec->id.ptrval; /* take the appropriate action */ switch(rec->action) { case DICT_UNDO_ADD: /* we added the word, so we must now delete it */ del_hash_entry(vmg_ rec->txt, rec->len, rec->obj, rec->prop); break; case DICT_UNDO_DEL: /* we deleted the word, so now we must add it */ add_hash_entry(vmg_ rec->txt, rec->len, TRUE, rec->obj, rec->prop, FALSE); break; case DICT_UNDO_COMPARATOR: /* we changed the comparator object, so change it back */ set_comparator(vmg_ rec->obj); break; } /* discard my record */ t3free(rec); /* clear the pointer in the undo record so we know it's gone */ undo_rec->id.ptrval = 0; } } /* * discard extra undo information */ void CVmObjDict::discard_undo(VMG_ CVmUndoRecord *rec) { /* delete our extra information record */ if (rec->id.ptrval != 0) { /* free the record */ t3free((dict_undo_rec *)rec->id.ptrval); /* clear the pointer so we know it's gone */ rec->id.ptrval = 0; } } /* * mark references in an undo record */ void CVmObjDict::mark_undo_ref(VMG_ CVmUndoRecord *undo_rec) { /* check our private record if we have one */ if (undo_rec->id.ptrval != 0) { dict_undo_rec *rec; /* get my undo record */ rec = (dict_undo_rec *)undo_rec->id.ptrval; /* take the appropriate action */ switch(rec->action) { case DICT_UNDO_COMPARATOR: /* the 'obj' entry is the old comparator object */ G_obj_table->mark_all_refs(rec->obj, VMOBJ_REACHABLE); break; case DICT_UNDO_ADD: case DICT_UNDO_DEL: /* * these actions use only weak references, so there's nothing * to do here */ break; } } } /* * remove stale weak references from an undo record */ void CVmObjDict::remove_stale_undo_weak_ref(VMG_ CVmUndoRecord *undo_rec) { /* check our private record if we have one */ if (undo_rec->id.ptrval != 0) { dict_undo_rec *rec; /* get my undo record */ rec = (dict_undo_rec *)undo_rec->id.ptrval; /* take the appropriate action */ switch(rec->action) { case DICT_UNDO_ADD: case DICT_UNDO_DEL: /* check to see if our object is being deleted */ if (rec->obj != VM_INVALID_OBJ && G_obj_table->is_obj_deletable(rec->obj)) { /* * the object is being deleted - since we only weakly * reference our objects, we must forget about this * object now, which means we can forget this entire * record */ t3free(rec); undo_rec->id.ptrval = 0; } break; case DICT_UNDO_COMPARATOR: /* this action does not use weak references */ break; } } } /* ------------------------------------------------------------------------ */ /* * callback context for weak reference function */ struct enum_hashtab_ctx { /* global context pointer */ vm_globals *vmg; /* dictionary object */ CVmObjDict *dict; }; /* * Mark references */ void CVmObjDict::mark_refs(VMG_ uint state) { /* if I have a comparator object, mark it as referenced */ if (get_ext()->comparator_ != VM_INVALID_OBJ) G_obj_table->mark_all_refs(get_ext()->comparator_, state); } /* * Remove weak references that have become stale. For each object for * which we have a weak reference, check to see if the object is marked * for deletion; if so, remove our reference to the object. */ void CVmObjDict::remove_stale_weak_refs(VMG0_) { enum_hashtab_ctx ctx; /* iterate over our hash table */ ctx.vmg = VMGLOB_ADDR; ctx.dict = this; get_ext()->hashtab_->enum_entries(&remove_weak_ref_cb, &ctx); } /* * enumeration callback to eliminate stale weak references */ void CVmObjDict::remove_weak_ref_cb(void *ctx0, CVmHashEntry *entry0) { enum_hashtab_ctx *ctx = (enum_hashtab_ctx *)ctx0; CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0; vm_dict_entry *cur; vm_dict_entry *nxt; /* set up for global access through the context */ VMGLOB_PTR(ctx->vmg); /* scan the entry's list */ for (cur = entry->get_head() ; cur != 0 ; cur = nxt) { /* remember the next entry in case we delete this one */ nxt = cur->nxt_; /* check to see if this object is free - if so, remove this record */ if (G_obj_table->is_obj_deletable(cur->obj_)) { /* if we have a trie, delete the trie entry */ if (ctx->dict->get_ext()->trie_ != 0) ctx->dict->get_ext()->trie_->del_word( entry->getstr(), entry->getlen()); /* delete the entry */ entry->del_entry(ctx->dict->get_ext()->hashtab_, cur->obj_, cur->prop_); } } } /* ------------------------------------------------------------------------ */ /* * load from an image file */ void CVmObjDict::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* remember where our image data comes from */ get_ext()->image_data_ = ptr; get_ext()->image_data_size_ = siz; /* build the hash table from the image data */ build_hash_from_image(vmg0_); /* * register for post-load initialization, as we might need to rebuild * our hash table once we have access to the comparator object */ G_obj_table->request_post_load_init(self); } /* * Post-load initialization */ void CVmObjDict::post_load_init(VMG_ vm_obj_id_t self) { vm_obj_id_t comp; /* * Rebuild the hash table with the comparator, if we have one. When * we load, reset, or restore, we build a tentative hash table with no * comparator (i.e., the default exact literal comparator), because we * can't assume we have access to the comparator object (as it might * be loaded after us). So, we register for post-load initialization, * and then we go back and re-install the comparator for real, * rebuilding the hash table with the actual comparator. */ if ((comp = get_ext()->comparator_) != VM_INVALID_OBJ) { /* ensure the comparator object is initialized */ G_obj_table->ensure_post_load_init(vmg_ comp); /* set the comparator object type, now that it's loaded */ set_comparator_type(vmg_ comp); /* * force a rebuild the hash table, so that we build it with the * comparator properly installed */ create_hash_table(vmg0_); } } /* * Build the hash table from the image data */ void CVmObjDict::build_hash_from_image(VMG0_) { uint cnt; uint i; const char *p; const char *endp; vm_obj_id_t comp; /* start reading at the start of the image data */ p = get_ext()->image_data_; endp = p + get_ext()->image_data_size_; /* read the comparator object ID */ comp = (vm_obj_id_t)t3rp4u(p); p += 4; /* * Do NOT install the actual comparator object at this point, but * simply build the table tentatively with a nil comparator. We can't * assume that the comparator object has been loaded yet (as it might * be loaded after us), so we cannot use it to build the hash table. * We have to do something with the data, though, so build the hash * table tentatively with no comparator; we'll rebuild it again at * post-load-init time with the real comparator. * * (This isn't as inefficient as it sounds, as we retain all of the * original hash table entries when we rebuild the table. All we end * up doing is scanning the entries again to recompute their new hash * values, and reallocating the hash table object itself.) */ get_ext()->comparator_ = VM_INVALID_OBJ; set_comparator_type(vmg_ VM_INVALID_OBJ); /* create the new hash table */ create_hash_table(vmg0_); /* read the entry count */ cnt = osrp2(p); p += 2; /* scan the entries */ for (i = 0 ; p < endp && i < cnt ; ++i) { vm_obj_id_t obj; vm_prop_id_t prop; uint key_len; uint item_cnt; const char *key; CVmHashEntryDict *entry; char key_buf[256]; char *keyp; size_t key_rem; /* read the key length */ key_len = *(unsigned char *)p++; /* remember the key pointer */ key = p; p += key_len; /* copy the key to our buffer for decoding */ memcpy(key_buf, key, key_len); /* decode the key */ for (keyp = key_buf, key_rem = key_len ; key_rem != 0 ; --key_rem, ++keyp) *keyp ^= 0xBD; /* read the key from the decoded buffer now */ key = key_buf; /* read the item count */ item_cnt = osrp2(p); p += 2; /* find an existing entry for this key */ entry = (CVmHashEntryDict *)get_ext()->hashtab_->find(key, key_len); /* if there's no existing entry, add one */ if (entry == 0) { /* create a new entry */ entry = new CVmHashEntryDict(key, key_len, TRUE, TRUE); /* add it to the table */ get_ext()->hashtab_->add(entry); } /* read the items */ for ( ; item_cnt != 0 ; --item_cnt) { /* read the object ID and property ID */ obj = (vm_obj_id_t)t3rp4u(p); prop = (vm_prop_id_t)osrp2(p + 4); /* * add the entry - this entry is from the image file, so * mark it as such */ entry->add_entry(obj, prop, TRUE); /* move on to the next item */ p += 6; } } /* * Now that we're done building the table, remember the comparator. We * can't set the type yet, because the object might not be loaded yet - * defer this until post-load initialization time. */ get_ext()->comparator_ = comp; } /* ------------------------------------------------------------------------ */ /* * Callback context for trie-building enumeration */ struct trie_cb_ctx { vmdict_TrieNode *t; }; /* hash table enumeration callback for building the trie */ static void trie_cb(void *ctx0, CVmHashEntry *entry0) { /* get the context and hash entries, properly cast */ trie_cb_ctx *ctx = (trie_cb_ctx *)ctx0; CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0; /* add this word to the trie */ ctx->t->add_word(entry->getstr(), entry->getlen()); } /* * Build the Trie from the hash table */ void CVmObjDict::build_trie(VMG0_) { trie_cb_ctx ctx; /* if we already have a trie, there's nothing to do */ if (get_ext()->trie_ != 0) return; /* create the root node */ ctx.t = get_ext()->trie_ = new vmdict_TrieNode(0, 0); /* enumerate the hash table to build the trie */ get_ext()->hashtab_->enum_entries(trie_cb, &ctx); } /* ------------------------------------------------------------------------ */ /* * Trie operations */ /* add a word to the Trie */ void vmdict_TrieNode::add_word(const char *str, size_t len) { vmdict_TrieNode *n; utf8_ptr p((char *)str); /* scan the string and walk the Trie */ for (n = this ; len != 0 ; p.inc(&len)) { /* get this character */ wchar_t ch = p.getch(); /* find the child node for this letter */ vmdict_TrieNode *chi; for (chi = n->chi ; chi != 0 && chi->ch != ch ; chi = chi->nxt) ; /* if there's no existing child for this letter, add one */ if (chi == 0) n->chi = chi = new vmdict_TrieNode(n->chi, ch); /* advance to this child node */ n = chi; } /* 'n' is the final node for this word, so count the word there */ n->word_cnt += 1; } /* find a node for a given word */ vmdict_TrieNode *vmdict_TrieNode::find_word(const char *str, size_t len) { vmdict_TrieNode *n; utf8_ptr p((char *)str); /* scan the string and walk the Trie */ for (n = this ; len != 0 ; p.inc(&len)) { /* get this character */ wchar_t ch = p.getch(); /* find the child node for this letter */ vmdict_TrieNode *chi; for (chi = n->chi ; chi != 0 && chi->ch != ch ; chi = chi->nxt) ; /* if we didn't find a child, the word isn't in the trie */ if (chi == 0) return 0; /* advance to this child node */ n = chi; } /* we've reached the final node for the word - return it */ return n; } /* delete a word from the Trie */ void vmdict_TrieNode::del_word(const char *str, size_t len) { /* find the final node for this word */ vmdict_TrieNode *n = find_word(str, len); /* if we found it, decrement its word count */ if (n != 0 && n->word_cnt != 0) n->word_cnt -= 1; } /* ------------------------------------------------------------------------ */ /* * Add an entry */ int CVmObjDict::add_hash_entry(VMG_ const char *p, size_t len, int copy, vm_obj_id_t obj, vm_prop_id_t prop, int from_image) { CVmHashEntryDict *entry; /* find an existing entry for this key */ entry = (CVmHashEntryDict *)get_ext()->hashtab_->find(p, len); /* if we didn't find an entry, add one */ if (entry == 0) { /* create an entry */ entry = new CVmHashEntryDict(p, len, copy, from_image); /* add it to the table */ get_ext()->hashtab_->add(entry); /* if we have a trie, add it to the trie */ if (get_ext()->trie_ != 0) get_ext()->trie_->add_word(p, len); } /* add the obj/prop to the entry's item list */ return entry->add_entry(obj, prop, from_image); } /* ------------------------------------------------------------------------ */ /* * delete an entry */ int CVmObjDict::del_hash_entry(VMG_ const char *str, size_t len, vm_obj_id_t obj, vm_prop_id_t voc_prop) { CVmHashEntryDict *entry; /* find the hash table entry for the word */ entry = (CVmHashEntryDict *)get_ext()->hashtab_->find(str, len); /* if we found it, delete the obj/prop entry */ if (entry != 0) { /* if we have a trie, delete the trie entry */ if (get_ext()->trie_ != 0) get_ext()->trie_->del_word(str, len); /* delete the hash table entry */ return entry->del_entry(get_ext()->hashtab_, obj, voc_prop); } /* we didn't find anything to delete */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * restore to image file state */ void CVmObjDict::reset_to_image(VMG_ vm_obj_id_t self) { /* delete the hash table */ if (get_ext()->hashtab_ != 0) { /* delete it */ delete get_ext()->hashtab_; /* forget it */ get_ext()->hashtab_ = 0; } /* delete the Trie */ if (get_ext()->trie_ != 0) { delete get_ext()->trie_; get_ext()->trie_ = 0; } /* rebuild the hash table from the image file data */ build_hash_from_image(vmg0_); /* * register for post-load initialization, as we might need to rebuild * our hash table once we have access to the comparator object */ G_obj_table->request_post_load_init(self); } /* ------------------------------------------------------------------------ */ /* * determine if the object has been changed since it was loaded */ int CVmObjDict::is_changed_since_load() const { /* * return true if our 'modified' flag is true - if it is, we must have * been changed since we were loaded */ return (get_ext()->modified_ != 0); } /* ------------------------------------------------------------------------ */ /* * save-file enumeration context */ struct save_file_ctx { /* file object */ CVmFile *fp; /* entry count */ ulong cnt; /* globals */ vm_globals *vmg; }; /* * save to a file */ void CVmObjDict::save_to_file(VMG_ CVmFile *fp) { long cnt_pos; long end_pos; save_file_ctx ctx; /* write the current comparator object */ fp->write_uint4(get_ext()->comparator_); /* remember the starting seek position and write a placeholder count */ cnt_pos = fp->get_pos(); fp->write_uint4(0); /* enumerate the entries to write out each one */ ctx.fp = fp; ctx.cnt = 0; ctx.vmg = VMGLOB_ADDR; get_ext()->hashtab_->enum_entries(&save_file_cb, &ctx); /* remember our position for a moment */ end_pos = fp->get_pos(); /* go back and write out the symbol count prefix */ fp->set_pos(cnt_pos); fp->write_uint4(ctx.cnt); /* seek back to the end */ fp->set_pos(end_pos); } /* * save file enumeration callback */ void CVmObjDict::save_file_cb(void *ctx0, CVmHashEntry *entry0) { save_file_ctx *ctx = (save_file_ctx *)ctx0; CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0; vm_dict_entry *cur; uint cnt; /* set up global access */ VMGLOB_PTR(ctx->vmg); /* write out this entry's name */ ctx->fp->write_uint2(entry->getlen()); ctx->fp->write_bytes(entry->getstr(), entry->getlen()); /* count the items in this entry's list */ for (cnt = 0, cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_) { /* * if the referenced object is persistent, count this item; * don't count items that refer to non-persistent items, because * these objects will not be saved to the file and hence will * not be present when the saved state is restored */ if (G_obj_table->is_obj_persistent(cur->obj_)) ++cnt; } /* * if there are no saveable items for this entry, do not write out * the entry at all */ if (cnt == 0) return; /* count this entry */ ++ctx->cnt; /* write the item count */ ctx->fp->write_uint2(cnt); /* write out each item in this entry's list */ for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_) { /* if this item's object is persistent, write out the item */ if (G_obj_table->is_obj_persistent(cur->obj_)) { /* write out the object and property for this entry */ ctx->fp->write_uint4((long)cur->obj_); ctx->fp->write_uint2((int)cur->prop_); } } } /* ------------------------------------------------------------------------ */ /* * restore from a file */ void CVmObjDict::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { ulong cnt; vm_obj_id_t comp; /* delete the old hash table if we have one */ if (get_ext()->hashtab_ != 0) { delete get_ext()->hashtab_; get_ext()->hashtab_ = 0; } /* delete the Trie if we have one */ if (get_ext()->trie_ != 0) { delete get_ext()->trie_; get_ext()->trie_ = 0; } /* * Read the comparator and fix it up to the new object numbering * scheme, but do not install it (as it might not be loaded yet). */ comp = fixups->get_new_id(vmg_ (vm_obj_id_t)fp->read_uint4()); /* create the new, empty hash table */ create_hash_table(vmg0_); /* read the number of symbols */ cnt = fp->read_uint4(); /* read the symbols */ for ( ; cnt != 0 ; --cnt) { uint len; uint read_len; char buf[256]; uint item_cnt; /* read the symbol length */ len = fp->read_uint2(); /* limit the reading to our buffer size */ read_len = len; if (read_len > sizeof(buf)) read_len = sizeof(buf); /* read the string */ fp->read_bytes(buf, read_len); /* skip any extra data */ if (len > read_len) fp->set_pos(fp->get_pos() + len - read_len); /* read the item count */ item_cnt = fp->read_uint2(); /* read the items */ for ( ; item_cnt != 0 ; --item_cnt) { vm_obj_id_t obj; vm_prop_id_t prop; /* read the object and property ID's */ obj = (vm_obj_id_t)fp->read_uint4(); prop = (vm_prop_id_t)fp->read_uint2(); /* translate the object ID through the fixup table */ obj = fixups->get_new_id(vmg_ obj); /* add the entry, if it refers to a valid object */ if (obj != VM_INVALID_OBJ) add_hash_entry(vmg_ buf, len, TRUE, obj, prop, FALSE); } } /* * install the comparator (we can't set its type yet, though, because * the object might not be loaded yet) */ get_ext()->comparator_ = comp; set_comparator_type(vmg_ VM_INVALID_OBJ); /* * If we had to restore it, we'll have to save it if the current state * is later saved, even if we don't modify it again. The fact that * this version was saved before means this version has to be saved * from now on. */ get_ext()->modified_ = TRUE; /* * register for post-load initialization, so that we can rebuild the * hash table with the actual comparator if necessary */ G_obj_table->request_post_load_init(self); } /* ------------------------------------------------------------------------ */ /* * impedence-matcher callback context */ struct enum_word_props_ctx { /* client callback to invoke */ void (*cb_func)(VMG_ void *ctx, vm_prop_id_t prop, const vm_val_t *match_val); /* string value to match */ const vm_val_t *strval; const char *strp; size_t strl; /* client callback context */ void *cb_ctx; /* globals */ vm_globals *globals; /* the dictionary object we're searching */ CVmObjDict *dict; }; /* * enum_word_props hash table enumeration callback */ void CVmObjDict::enum_word_props_cb(void *ctx0, CVmHashEntry *entry0) { enum_word_props_ctx *ctx = (enum_word_props_ctx *)ctx0; CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0; vm_val_t match_val; VMGLOB_PTR(ctx->globals); /* if this entry matches the search string, process it */ if (ctx->dict->match_strings(vmg_ ctx->strval, ctx->strp, ctx->strl, entry->getstr(), entry->getlen(), &match_val)) { vm_dict_entry *cur; /* process the items under this entry */ for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_) { /* invoke the callback */ (*ctx->cb_func)(vmg_ ctx->cb_ctx, cur->prop_, &match_val); } } } /* * Enumerate the properties for which a word is defined */ void CVmObjDict::enum_word_props(VMG_ void (*cb_func)(VMG_ void *, vm_prop_id_t, const vm_val_t *), void *cb_ctx, const vm_val_t *strval, const char *strp, size_t strl) { enum_word_props_ctx ctx; unsigned int hash; /* set up the enumeration callback context */ ctx.strval = strval; ctx.strp = strp; ctx.strl = strl; ctx.cb_func = cb_func; ctx.cb_ctx = cb_ctx; ctx.globals = VMGLOB_ADDR; ctx.dict = this; /* calculate the hash code */ hash = calc_str_hash(vmg_ strval, strp, strl); /* enumerate the matches */ get_ext()->hashtab_->enum_hash_matches(hash, &enum_word_props_cb, &ctx); } �������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmdict.h��������������������������������������������������������������������������0000664�0000000�0000000�00000047463�12650170723�0015333�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmdict.h - VM 'Dictionary' metaclass Function Notes Modified 01/24/00 MJRoberts - Creation */ #ifndef VMDICT_H #define VMDICT_H #include <stdlib.h> #include <string.h> #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmhash.h" #include "utf8.h" /* forward-declare the class */ class CVmObjDict; /* ------------------------------------------------------------------------ */ /* * undo action codes */ enum dict_undo_action { /* we added this word to the dictionary (undo by deleting it) */ DICT_UNDO_ADD, /* we deleted this word from the dictionary (undo by adding it back) */ DICT_UNDO_DEL, /* we change the comparator object */ DICT_UNDO_COMPARATOR }; /* ------------------------------------------------------------------------ */ /* * The image file data block is arranged as follows: * *. UINT4 comparator_object_id *. UINT2 load_image_entry_count *. entry 1 *. entry 2 *. ... *. entry N * * Each entry has the following structure: * *. UCHAR key_string_byte_length *. key_string (UTF-8 characters, not null terminated, XOR'ed with 0xBD) *. UINT2 number of sub-entries *. sub-entry 1 *. sub-entry 2 *. etc * * Each sub-entry is structured like this: * * UINT4 associated_object_id *. UINT2 defining_property_id * * Note that each byte of the key string is XOR'ed with the arbitrary * byte value 0xBD. This is simply to provide a minimal level of * obfuscation in the image file to prevent casual browsing of the image * contents. */ /* * Separately, we maintain a hash table and entries in the hash table. We * don't attempt to keep the hash table in a portable format, which means * that we have to rebuild the hash table on restoring a saved state file. */ /* comparator object types */ enum vm_dict_comp_type { VMDICT_COMP_NONE, /* no comparator object */ VMDICT_COMP_STRCOMP, /* StringComparator */ VMDICT_COMP_GENERIC /* generic comparator object */ }; /* * Dictionary object extension - we use this memory (in the variable * heap) to keep track of our image data and our hash table. */ struct vm_dict_ext { /* pointer to load image data, if any */ const char *image_data_; size_t image_data_size_; /* pointer to our hash table (in the system heap) */ class CVmHashTable *hashtab_; /* flag: the table has been changed since image load */ ulong modified_; /* our comparator object, if any */ vm_obj_id_t comparator_; CVmObject *comparator_obj_; /* type of comparator */ vm_dict_comp_type comparator_type_; /* Trie of our entries, for spelling correction */ struct vmdict_TrieNode *trie_; }; /* ------------------------------------------------------------------------ */ /* * For spelling correction, we maintain a Trie on the dictionary in * parallel to the hash table. */ /* * Trie Node - a node in the trie tree. */ struct vmdict_TrieNode { vmdict_TrieNode(vmdict_TrieNode *nxt, wchar_t c) { /* remember the transition character from our parent to us */ ch = c; /* we don't have any words in this node yet */ word_cnt = 0; /* remember our next sibling link */ this->nxt = nxt; /* we don't have any children yet */ chi = 0; } ~vmdict_TrieNode() { /* delete the subtree */ while (chi != 0) { /* unlink this child */ vmdict_TrieNode *n = chi; chi = chi->nxt; /* delete it */ delete n; } } /* add a word */ void add_word(const char *str, size_t len); /* find a word */ vmdict_TrieNode *find_word(const char *str, size_t len); /* delete a word */ void del_word(const char *str, size_t len); /* * Transition character from parent to this node. Append this * character to the string that reached the parent node to get the * string that reaches the child node. */ wchar_t ch; /* * Number of words at this node. Since we use the Trie only for * spelling correction, we don't care about any of the other data * stored per word; we can look that up through the hash table. All we * care about is whether or not a given node has any words associated * with it. Since the same word can be entered in the dictionary * several times, we need to keep a count - that way, we can track * dynamic removal of words by matching deletions against insertions. */ int word_cnt; /* the head of the list of child nodes */ vmdict_TrieNode *chi; /* our next sibling */ vmdict_TrieNode *nxt; }; /* ------------------------------------------------------------------------ */ /* * Dictionary object interface */ class CVmObjDict: public CVmObject { friend class CVmMetaclassDict; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* determine if an object is a Dictionary object */ static int is_dictionary_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* receive notification of a new undo savepoint */ void notify_new_savept() { } /* apply undo */ void apply_undo(VMG_ struct CVmUndoRecord *rec); /* discard additional information associated with an undo record */ void discard_undo(VMG_ struct CVmUndoRecord *rec); /* mark a reference in undo */ void mark_undo_ref(VMG_ struct CVmUndoRecord *rec); /* remove stale weak references from an undo record */ void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *rec); /* mark references */ void mark_refs(VMG_ uint); /* remove weak references */ void remove_stale_weak_refs(VMG0_); /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t siz); /* perform post-load initialization */ void post_load_init(VMG_ vm_obj_id_t self); /* restore to image file state */ void reset_to_image(VMG_ vm_obj_id_t /*self*/); /* determine if the object has been changed since it was loaded */ int is_changed_since_load() const; /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* enumerate the properties for which a word is defined */ void enum_word_props(VMG_ void (*cb_func)(VMG_ void *, vm_prop_id_t, const vm_val_t *), void *cb_ctx, const vm_val_t *strval, const char *strp, size_t strl); /* get/set my comparator object */ vm_obj_id_t get_comparator() const { return get_ext()->comparator_; } void set_comparator(VMG_ vm_obj_id_t obj); /* * Match a pair of strings. * * 'valstrval' is the vm_val_t with the string value, if one is * available; if not, this can be given as null and we'll synthesize a * new string object from 'valstr' and 'vallen' if one is needed. We * don't require the caller to synthesize a string object because we * might not need one; we'll create one only if one is actually needed. * * 'valstr' and 'vallen' directly give the text of the value string, * and 'refstr' and 'reflen' likewise directly give the text of the * reference string. */ int match_strings(VMG_ const vm_val_t *valstrval, const char *valstr, size_t vallen, const char *refstr, size_t reflen, vm_val_t *result_val); /* * Calculate the hash value for a string. As in match_strings(), * 'valstrval' can be passed as null if no vm_val_t for the string text * is available, in which case we'll synthesize a new string object * from 'valstr' and 'vallen' if one is needed. */ unsigned int calc_str_hash(VMG_ const vm_val_t *valstrval, const char *valstr, size_t vallen); /* add a string/obj/prop association from a VMB_LEN+buffer string */ void add_word(VMG_ vm_obj_id_t self, const char *str, vm_obj_id_t obj, vm_prop_id_t prop) { add_word(vmg_ self, str + VMB_LEN, vmb_get_len(str), obj, prop); } /* add a string/obj/prop association */ void add_word(VMG_ vm_obj_id_t self, const char *str, size_t len, vm_obj_id_t obj, vm_prop_id_t voc_prop); /* remove a string/obj/prop association, given a VMB_LEN+buffer string */ void del_word(VMG_ vm_obj_id_t self, const char *str, vm_obj_id_t obj, vm_prop_id_t prop) { del_word(vmg_ self, str + VMB_LEN, vmb_get_len(str), obj, prop); } /* remove a string/obj/prop association */ void del_word(VMG_ vm_obj_id_t self, const char *str, size_t len, vm_obj_id_t obj, vm_prop_id_t voc_prop); protected: CVmObjDict(VMG0_); /* set the comparator type */ void set_comparator_type(VMG_ vm_obj_id_t obj); /* create or re-create the hash table */ void create_hash_table(VMG0_); /* fill the hash table with entries from the image data */ void build_hash_from_image(VMG0_); /* build the Trie from the hash table */ void build_trie(VMG0_); /* property evaluation - undefined property */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* enumeration callback for getp_find */ static void find_cb(void *ctx, class CVmHashEntry *entry); /* enumeration callback for getp_isdef */ static void isdef_cb(void *ctx, class CVmHashEntry *entry); /* enumeration callback for enum_word_props */ static void enum_word_props_cb(void *ctx, class CVmHashEntry *entry); /* property evaluation - set the comparator object */ int getp_set_comparator(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc); /* property evaluation - findWord */ int getp_find(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc); /* property evaluation - addWord */ int getp_add(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluation - delWord */ int getp_del(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluation - isWordDefined */ int getp_is_defined(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluation - forEachWord */ int getp_for_each_word(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluation - find corrections for a misspelled word */ int getp_correct(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get my extension, properly cast */ vm_dict_ext *get_ext() const { return (vm_dict_ext *)ext_; } /* * add an entry; returns true if successful, false if the entry * already exists, in which case no new entry is made */ int add_hash_entry(VMG_ const char *p, size_t len, int copy, vm_obj_id_t obj, vm_prop_id_t prop, int from_image); /* delete an entry */ int del_hash_entry(VMG_ const char *p, size_t len, vm_obj_id_t obj, vm_prop_id_t prop); /* callback for hash table enumeration - delete stale weak refs */ static void remove_weak_ref_cb(void *ctx, class CVmHashEntry *entry); /* callback for hash table enumeration - save file */ static void save_file_cb(void *ctx, class CVmHashEntry *entry); /* callback for hash table enumeration - convert to constant data */ static void cvt_const_cb(void *ctx, class CVmHashEntry *entry); /* callback for hash table enumeration - rebuild image, phase 1 */ static void rebuild_cb_1(void *ctx, class CVmHashEntry *entry); /* callback for hash table enumeration - rebuild image, phase 2 */ static void rebuild_cb_2(void *ctx, class CVmHashEntry *entry); /* allocate an undo record */ struct dict_undo_rec *alloc_undo_rec(enum dict_undo_action action, const char *txt, size_t len); /* add a record to the global undo stream */ void add_undo_rec(VMG_ vm_obj_id_t self, struct dict_undo_rec *rec); /* function table */ static int (CVmObjDict::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * hash table list entry - each hash entry has a list of associated * objects; this structure represents an entry in one of these lists */ struct vm_dict_entry { vm_dict_entry(vm_obj_id_t obj, vm_prop_id_t prop, int from_image) { obj_ = obj; prop_ = prop; from_image_ = from_image; nxt_ = 0; } /* object ID of this entry */ vm_obj_id_t obj_; /* defining property ID */ vm_prop_id_t prop_; /* next entry in the list */ vm_dict_entry *nxt_; /* flag: entry is from the image file */ int from_image_; }; /* * Dictionary object hash table entry */ class CVmHashEntryDict: public CVmHashEntryCS { public: CVmHashEntryDict(const char *str, size_t len, int copy, int from_image) : CVmHashEntryCS(str, len, copy || from_image) { /* nothing in our item list yet */ list_ = 0; } ~CVmHashEntryDict() { /* delete all of our entries */ while (list_ != 0) { vm_dict_entry *nxt; /* note the next entry */ nxt = list_->nxt_; /* delete the current entry */ delete list_; /* move on to the next entry */ list_ = nxt; } } /* get the first entry in our list */ vm_dict_entry *get_head() const { return list_; } /* * add an entry to our list - returns true if we added the entry, * false if an entry with the same object ID was already present, in * which case we will ignore the addition */ int add_entry(vm_obj_id_t obj, vm_prop_id_t prop, int from_image) { vm_dict_entry *entry; vm_dict_entry *prv; /* * check to see if this entry is already in our list - if it is, * don't bother adding the redundant definition */ for (prv = 0, entry = list_ ; entry != 0 ; entry = entry->nxt_) { /* * if this entry matches the object ID and property ID, * ignore the addition */ if (entry->obj_ == obj && entry->prop_ == prop) return FALSE; /* * if this entry matches the property, remember it as the * insertion point, so that we keep all definitions for the * same word with the same property together in the list */ if (entry->prop_ == prop) prv = entry; } /* create a list entry */ entry = new vm_dict_entry(obj, prop, from_image); /* * link it in after the insertion point, or at the head of our * list if we didn't find any other insertion point */ if (prv != 0) { /* we found an insertion point - link it in */ entry->nxt_ = prv->nxt_; prv->nxt_ = entry; } else { /* no insertion point - link it at the head of our list */ entry->nxt_ = list_; list_ = entry; } /* we added the entry */ return TRUE; } /* * Delete all entries matching a given object ID from our list. * Returns true if any entries were deleted, false if not. */ int del_entry(CVmHashTable *table, vm_obj_id_t obj, vm_prop_id_t prop) { vm_dict_entry *cur; vm_dict_entry *nxt; vm_dict_entry *prv; int found; /* find the entry in our list */ for (found = FALSE, prv = 0, cur = list_ ; cur != 0 ; prv = cur, cur = nxt) { /* remember the next entry */ nxt = cur->nxt_; /* if this is our entry, delete it */ if (cur->obj_ == obj && cur->prop_ == prop) { /* unlink this entry */ if (prv != 0) prv->nxt_ = nxt; else list_ = nxt; /* delete this entry */ delete cur; /* note that we found at least one entry to delete */ found = TRUE; } } /* if our list is now empty, delete myself from the table */ if (list_ == 0) { /* remove myself from the table */ table->remove(this); /* delete myself */ delete this; } /* tell the caller whether we found anything to delete */ return found; } protected: /* list of associated objects */ vm_dict_entry *list_; }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassDict: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "dictionary2/030001"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjDict(vmg0_); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjDict(vmg0_); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjDict::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjDict::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMDICT_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjDict) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmdynfunc.cpp���������������������������������������������������������������������0000664�0000000�0000000�00000173240�12650170723�0016402�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2010 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmdynfunc.cpp - DynamicFunc implementation Function Notes Modified 12/13/09 MJRoberts - Creation */ #include <stdlib.h> #include <string.h> #include <assert.h> #include "t3std.h" #include "os.h" #include "charmap.h" #include "vmdynfunc.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmbif.h" #include "vmmeta.h" #include "vmstack.h" #include "vmrun.h" #include "vmlst.h" #include "vmstr.h" #include "vmrun.h" #include "vmgram.h" #include "vmdict.h" #include "vmtobj.h" #include "vmpredef.h" #include "vmimport.h" #include "vmfunc.h" #include "vmfref.h" #include "utf8.h" #include "vmop.h" #include "tctarg.h" #include "tcglob.h" #include "tctok.h" #include "tcprs.h" #include "tcmain.h" #include "resload.h" #include "tchost.h" #include "vmhost.h" #include "tcgen.h" #include "vmimage.h" #include "vmpool.h" /* ------------------------------------------------------------------------ */ /* * Allocate an extension structure */ vm_dynfunc_ext *vm_dynfunc_ext::alloc_ext( VMG_ CVmDynamicFunc *self, size_t bytecode_len, int obj_ref_cnt) { /* * Calculate how much space we need: we need the base structure, plus * space for the object references, plus the dynamic object header, and * the byte code array. */ size_t siz = sizeof(vm_dynfunc_ext) + (obj_ref_cnt - 1)*sizeof_field(vm_dynfunc_ext, obj_refs[0]) + VMCO_PREFIX_LENGTH + bytecode_len; /* allocate the memory */ vm_dynfunc_ext *ext = (vm_dynfunc_ext *)G_mem->get_var_heap()->alloc_mem( siz, self); /* remember the sizes */ ext->obj_ref_cnt = obj_ref_cnt; ext->bytecode_len = bytecode_len; /* we don't have any source text yet */ ext->src.set_nil(); /* we don't have a method context object yet */ ext->method_ctx.set_nil(); /* return the new extension */ return ext; } /* ------------------------------------------------------------------------ */ /* * DynamicFunc statics */ /* metaclass registration object */ static CVmMetaclassDynamicFunc metaclass_reg_obj; CVmMetaclass *CVmDynamicFunc::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmDynamicFunc::*CVmDynamicFunc::func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmDynamicFunc::getp_undef, &CVmDynamicFunc::getp_get_source }; /* ------------------------------------------------------------------------ */ /* * DynamicFunc metaclass implementation */ /* * construction */ CVmDynamicFunc::CVmDynamicFunc(VMG_ vm_obj_id_t self, const vm_val_t *src, size_t bytecode_len, int obj_ref_cnt) { /* allocate our extension structure */ vm_dynfunc_ext *ext = vm_dynfunc_ext::alloc_ext( vmg_ this, bytecode_len, obj_ref_cnt); /* store it in the object */ ext_ = (char *)ext; /* save the source value */ if (src != 0) ext->src = *src; /* set up the code object prefix header */ vmb_put_objid(ext->get_prefix_ptr(), self); } /* * create */ vm_obj_id_t CVmDynamicFunc::create(VMG_ int in_root_set, vm_obj_id_t globals, vm_obj_id_t locals, vm_obj_id_t macros, const vm_val_t *srcval, const char *src, size_t src_len) { /* get the compiler interface */ CVmDynamicCompiler *comp = CVmDynamicCompiler::get(vmg0_); /* compile the code and create a code object */ CVmDynCompResults results; vm_obj_id_t id = comp->compile( vmg_ in_root_set, globals, locals, macros, srcval, src, src_len, DCModeAuto, 0, &results); /* if an error occurred, throw the error */ if (id == VM_INVALID_OBJ) results.throw_error(vmg0_); /* return the new ID */ return id; } /* * create */ vm_obj_id_t CVmDynamicFunc::create(VMG_ int in_root_set, vm_obj_id_t globals, vm_obj_id_t locals, vm_obj_id_t macros, const vm_val_t *src) { /* get the source value as a string */ const char *src_txt = src->get_as_string(vmg0_); /* if it's not a string value, it's an error */ if (src_txt == 0) err_throw(VMERR_BAD_TYPE_BIF); /* create the object */ return create(vmg_ in_root_set, globals, locals, macros, src, src_txt + VMB_LEN, vmb_get_len(src_txt)); } /* * Create dynamically using stack arguments. This takes the source code as * a string, compiles it, and creates a DynamicFunc encapsulating the byte * code. */ vm_obj_id_t CVmDynamicFunc::create_from_stack( VMG_ const uchar **pc_ptr, uint argc) { /* check arguments */ if (argc < 1 || argc > 4) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* get the source code argument (leave on stack for gc protection) */ const vm_val_t *src = G_stk->get(0); /* get the global symbol table object, if any */ vm_obj_id_t globals = VM_INVALID_OBJ; if (argc >= 2) { /* it has to be an object or nil */ if (G_stk->get(1)->typ == VM_OBJ) globals = G_stk->get(1)->val.obj; else if (G_stk->get(1)->typ != VM_NIL) err_throw(VMERR_BAD_TYPE_BIF); } /* get the local symbol table object, if any */ vm_obj_id_t locals = VM_INVALID_OBJ; if (argc >= 3) { /* it has to be an object or nil */ if (G_stk->get(2)->typ == VM_OBJ) locals = G_stk->get(2)->val.obj; else if (G_stk->get(2)->typ != VM_NIL) err_throw(VMERR_BAD_TYPE_BIF); } /* check for macros */ vm_obj_id_t macros = VM_INVALID_OBJ; if (argc >= 4) { /* it has to be an object or nil */ if (G_stk->get(3)->typ == VM_OBJ) macros = G_stk->get(3)->val.obj; else if (G_stk->get(3)->typ != VM_NIL) err_throw(VMERR_BAD_TYPE_BIF); } /* allocate the object ID and create the object */ vm_obj_id_t id = create(vmg_ FALSE, globals, locals, macros, src); /* discard arguments */ G_stk->discard(argc); /* return the new ID */ return id; } /* * notify of deletion */ void CVmDynamicFunc::notify_delete(VMG_ int /*in_root_set*/) { /* free our additional data, if we have any */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* * Retrieve a DynamicFunc ID from a bytecode method prefix */ vm_obj_id_t CVmDynamicFunc::get_obj_from_prefix(VMG_ const uchar *p) { /* * A DynamicFunc stores a method header prefix just before the start of * the header, and the prefix stores the associated DynamicFunc ID. * Read the prefix header. * * (Technically, this has some risk of reading outside the bounds of * valid program memory, because we don't know the origin of 'p'. * Callers should never attempt calling this routine with arbitrary * pointers. Only pass in pointers that are known to refer to valid * method headers, AND that aren't part of the static Code Pool. The * only other kind of valid method header pointer is a DynamicFunc, so * as long as callers obey these rules this dereference should be * safe.) */ vm_obj_id_t id = vmb_get_objid((const char *)p - VMCO_PREFIX_LENGTH); /* verify with the memory manager that this is a valid object ID */ if (!G_obj_table->is_obj_id_valid(id)) return VM_INVALID_OBJ; /* it's a valid object; make sure it's really of type DynamicFunc */ if (!is_dynfunc_obj(vmg_ id)) return VM_INVALID_OBJ; /* * It's a valid DynamicFunc: verify that it's really the one it claims * to be. If it is, the object's bytecode pointer should match 'p'. */ CVmDynamicFunc *co = (CVmDynamicFunc *)vm_objp(vmg_ id); if (co->get_ext()->get_bytecode_ptr() == (const char *)p) { /* we have a winner! */ return id; } /* it's not our object */ return VM_INVALID_OBJ; } /* * get a property */ int CVmDynamicFunc::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from our base class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * Invoke */ int CVmDynamicFunc::get_invoker(VMG_ vm_val_t *val) { /* * Return a pointer to the start of our byte code. The actual method * header starts after our prefix. */ if (val != 0) val->set_codeptr(get_ext()->get_bytecode_ptr()); /* we're invokable */ return TRUE; } /* * Index the object. * * A DynamicFunc is an invokee, and as such it can be referenced by * generated code for special internal operations. We use indexing to * retrieve special information: * * [1] is the 'self' object or method context object for the enclosing * stack frame. The compiler uses this to establish the method context for * a dynamic method that's compiled in the context of an enclosing frame, * in analogy to an anonymous function that accesses its enclosing lexical * scope's method context. */ int CVmDynamicFunc::index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val) { /* check the type */ if (index_val->typ == VM_INT) { /* it's an integer index - check the value */ switch (index_val->val.intval) { case 1: /* return our method context */ *result = get_ext()->method_ctx; return TRUE; default: /* other values are not allowed */ break; } } /* if we didn't handle it already, it's an invalid index */ err_throw(VMERR_INDEX_OUT_OF_RANGE); AFTER_ERR_THROW(return TRUE;) } /* * load from an image file */ void CVmDynamicFunc::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ self, ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from the image file */ void CVmDynamicFunc::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ self, ptr, siz); } /* * load or reload data from the image */ void CVmDynamicFunc::load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* read the bytecode length */ size_t bytecode_len = osrp2(ptr); ptr += 2; /* read the object reference count */ int obj_ref_cnt = osrp2(ptr); ptr += 2; /* allocate the extension */ vm_dynfunc_ext *ext = vm_dynfunc_ext::alloc_ext( vmg_ this, bytecode_len, obj_ref_cnt); ext_ = (char *)ext; /* save the method context object */ vmb_get_dh(ptr, &ext->method_ctx); ptr += VMB_DATAHOLDER; /* save the source string value */ vmb_get_dh(ptr, &ext->src); ptr += VMB_DATAHOLDER; /* set up the dynamic code prefix header */ vmb_put_objid(ext->get_prefix_ptr(), self); /* copy the byte code */ memcpy(ext->get_bytecode_ptr(), ptr, bytecode_len); ptr += bytecode_len; /* load the object reference offsets */ for (int i = 0 ; i < obj_ref_cnt ; ++i, ptr += 2) ext->obj_refs[i] = osrp2(ptr); } /* * save to a file */ void CVmDynamicFunc::save_to_file(VMG_ class CVmFile *fp) { char buf[VMB_DATAHOLDER]; vm_dynfunc_ext *ext = get_ext(); /* write our source code length */ fp->write_uint2(ext->bytecode_len); /* write our object reference count */ fp->write_uint2(ext->obj_ref_cnt); /* write the method context object */ vmb_put_dh(buf, &ext->method_ctx); fp->write_bytes(buf, VMB_DATAHOLDER); /* write our source object value */ vmb_put_dh(buf, &ext->src); fp->write_bytes(buf, VMB_DATAHOLDER); /* write the bytecode data */ fp->write_bytes(ext->get_bytecode_ptr(), ext->bytecode_len); /* write the object reference list */ for (int i = 0 ; i < ext->obj_ref_cnt ; ++i) fp->write_uint2(ext->obj_refs[i]); } /* * restore from a file */ void CVmDynamicFunc::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { char buf[VMB_DATAHOLDER]; /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* read the bytecode length and object reference count */ size_t bytecode_len = fp->read_uint2(); int obj_ref_cnt = fp->read_uint2(); /* allocate the extension */ vm_dynfunc_ext *ext = vm_dynfunc_ext::alloc_ext( vmg_ this, bytecode_len, obj_ref_cnt); ext_ = (char *)ext; /* read the method context object */ fp->read_bytes(buf, VMB_DATAHOLDER); fixups->fix_dh(vmg_ buf); vmb_get_dh(buf, &ext->method_ctx); /* read the source object */ fp->read_bytes(buf, VMB_DATAHOLDER); fixups->fix_dh(vmg_ buf); vmb_get_dh(buf, &ext->src); /* set up the dynamic object code prefix header */ vmb_put_objid(ext->get_prefix_ptr(), self); /* read the bytecode */ char *bc = ext->get_bytecode_ptr(); fp->read_bytes(bc, bytecode_len); /* read the object references, fixing each one up in the bytecode */ for (int i = 0 ; i < obj_ref_cnt ; ++i) { /* read and save the offset of this reference */ uint ofs = ext->obj_refs[i] = fp->read_uint2(); /* fix up the object ID in the bytecode data */ fixups->fix_vmb_obj(vmg_ bc + ofs); } } /* * mark references */ void CVmDynamicFunc::mark_refs(VMG_ uint state) { /* * Get my extension, and only proceed if it's non-empty. For most * object types this isn't an issue, because the only time we create * empty objects of most types is during loading, when gc is disabled. * But we do create long-lived empty code objects in the course of * compilation, because we need a placeholder for the object ID ahead * of the compilation. */ vm_dynfunc_ext *ext = get_ext(); if (ext != 0) { /* mark my context object as referenced */ if (ext->method_ctx.typ == VM_OBJ) G_obj_table->mark_all_refs(ext->method_ctx.val.obj, state); /* mark my source code string as referenced */ if (ext->src.typ == VM_OBJ) G_obj_table->mark_all_refs(ext->src.val.obj, state); /* mark each object reference in the bytecode stream */ char *bc = ext->get_bytecode_ptr(); for (int i = 0 ; i < ext->obj_ref_cnt ; ++i) { /* * Read the object ID from the bytecode. The obj_refs[] array * entry isn't the actual reference, but rather the offset of * the reference in the bytecode stream. The reference itself * is a portable OBJECT_ID field. */ vm_obj_id_t ref = vmb_get_objid(bc + ext->obj_refs[i]); G_obj_table->mark_all_refs(ref, state); } } } /* * property evaluator - get the source code string */ int CVmDynamicFunc::getp_get_source(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* return the source code object */ *retval = get_ext()->src; /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Custom host interface. This host interface captures error messages * generated during compilation so that we can return them to the program. */ class CTcHostIfcDynComp: public CTcHostIfc { public: CTcHostIfcDynComp() { /* allocate an initial buffer */ erralo_ = 1024; errmsg_ = (char *)t3malloc(erralo_); /* initially clear the message buffer */ reset_messages(); } ~CTcHostIfcDynComp() { /* free our buffer */ t3free(errmsg_); } /* clear the message buffer */ void reset_messages() { errmsg_[0] = '\0'; p_ = errmsg_; } /* get the text of the first error message since we cleared the buffer */ const char *get_error_msg() const { return errmsg_; } /* * CTcHostIfc interface */ /* display an informational message */ virtual void v_print_msg(const char *, va_list) { /* ignore informational messages */ } /* display a process step message */ virtual void v_print_step(const char *, va_list) { /* ignore process step messages */ } /* display an error message */ virtual void v_print_err(const char *msg, va_list args) { /* format the message */ char *f = t3vsprintf_alloc(msg, args); if (f == 0) return; /* get the length */ size_t len = strlen(f); /* expand our buffer if necessary */ size_t curlen = p_ - errmsg_; size_t need = curlen + len + 1; if (need > erralo_) { erralo_ = need + 1024; char *newbuf = (char *)t3realloc(errmsg_, erralo_); if (newbuf == 0) return; /* switch to the new buffer */ errmsg_ = newbuf; p_ = errmsg_ + curlen; } /* append the message */ memcpy(p_, f, len + 1); /* bump the write pointer */ p_ += len; /* done with the allocated message text */ t3free(f); } private: /* our error message buffer */ char *errmsg_; /* allocated buffer size */ size_t erralo_; /* pointer to next available byte of message buffer */ char *p_; }; /* ------------------------------------------------------------------------ */ /* * Dynamic compiler symbol table interface. This provides the compiler * with global symbols specified via an indexable object. This is usually * a LookupTable, but any object that can be indexed by symbol name string * will work. The actual compiler symbols retrieved from * t3GetGlobalSymbols() can be used, or the program can provide its own * custom symbol table. * * We implement the symbol table as a view of the user-code object, with * caching in our own hash table. To match the interface, we have to * create a CTcSymbol object for each symbol we successfully look up. * Rather than creating redundant copies of these objects, we cache them in * our own hash table. */ class CVmDynFuncSymtab: public CTcPrsSymtab { public: /* initialize with a source object */ CVmDynFuncSymtab(VMG_ vm_obj_id_t globals, vm_obj_id_t locals) : CTcPrsSymtab(0) { /* remember the globals */ gptr_ = VMGLOB_ADDR; /* remember my symbol table objects */ globals_ = globals; locals_ = locals; } /* find a symbol - implementation of CTcPrsDbgSymtab interface */ virtual CTcSymbol *find_direct(const textchar_t *sym, size_t len) { /* first, check the cache to see if we've already looked it up */ CTcSymbol *ret = CTcPrsSymtab::find_direct(sym, len); if (ret != 0) return ret; /* * Didn't find it - check the user-code tables. */ /* try our local symbol table */ ret = find_local(sym, len); /* if that didn't work, try our global symbol table */ if (ret == 0) ret = find_global(sym, len); /* if we found a symbol, add it to the cache */ if (ret != 0) CTcPrsSymtab::add_entry(ret); /* return the symbol */ return ret; } /* add an entry */ virtual void add_entry(CTcSymbol *sym) { /* flag an error */ G_tok->log_error(TCERR_RT_CANNOT_DEFINE_GLOBALS, (int)sym->get_sym_len(), sym->get_sym()); } private: /* look up a symbol in our local symbol table */ CTcSymbol *find_local(const textchar_t *sym, size_t len) { /* if there's no user-code table, there's nothing to search */ if (locals_ == VM_INVALID_OBJ) return 0; /* establish access to globals */ VMGLOB_PTR(gptr_); /* * if it's a StackFrameDesc object, look up the symbol; otherwise * try to treat it as a list-like object, and check each element of * the list */ if (CVmObjFrameDesc::is_framedesc_obj(vmg_ locals_)) { /* look up the value in our single table */ return find_local(sym, len, locals_); } else { /* assume it's a list - set up an object value */ vm_val_t lt; lt.set_obj(locals_); /* check each list element */ int n = lt.ll_length(vmg0_); for (int i = 1 ; i <= n ; ++i) { /* get this element */ vm_val_t ele; lt.ll_index(vmg_ &ele, i); /* if it's a StackFrameDesc object, check it */ if (ele.typ == VM_OBJ && CVmObjFrameDesc::is_framedesc_obj(vmg_ ele.val.obj)) { /* look up the value in this table */ CTcSymbol *ret = find_local(sym, len, ele.val.obj); /* if we found it, return the value */ if (ret != 0) return ret; } } /* we didn't find a value */ return 0; } } /* find a symbol in a given local table */ CTcSymbol *find_local(const textchar_t *sym, size_t len, vm_obj_id_t fref) { /* establish access to globals */ VMGLOB_PTR(gptr_); /* get the frame descriptor object */ CVmObjFrameDesc *fp = (CVmObjFrameDesc *)vm_objp(vmg_ fref); /* look up the symbol */ CVmDbgFrameSymPtr info; if (fp->find_local(vmg_ sym, len, &info)) { /* return a new dynamic local symbol */ return new CTcSymDynLocal( sym, len, FALSE, fp->get_frame_ref_id(), fp->get_frame_ref(vmg0_)->get_var_frame_index(&info), info.is_ctx_local() ? info.get_ctx_arr_idx() : 0); } /* didn't find it - return failure */ return 0; } /* look up a symbol in our global table */ CTcSymbol *find_global(const textchar_t *sym, size_t len) { /* if there's no user-code table, there's nothing to search */ if (globals_ == VM_INVALID_OBJ) return 0; /* presume we won't find a symbol */ CTcSymbol *ret = 0; /* establish access to globals */ VMGLOB_PTR(gptr_); /* create a string for the symbol name; stack it for gc protection */ vm_val_t symstr; symstr.set_obj(CVmObjString::create(vmg_ FALSE, sym, len)); G_stk->push(&symstr); /* look up the symbol string in the table */ vm_val_t symval; vm_objp(vmg_ globals_)->index_val_ov(vmg_ &symval, globals_, &symstr); G_stk->push(&symval); /* check what we found */ switch (symval.typ) { case VM_OBJ: /* it's an object - map it to CTcSymObj or CTcSymMetaclass */ if (CVmObjClass::is_intcls_obj(vmg_ symval.val.obj)) { /* * it's an IntrinsicClass object - this represents a * metaclass, so map it to a CTcSymMetaclass symbol */ CVmObjClass *cl = (CVmObjClass *)vm_objp(vmg_ symval.val.obj); ret = new CTcSymMetaclass(sym, len, FALSE, cl->get_meta_idx(), symval.val.obj); } else if (vm_objp(vmg_ symval.val.obj)->get_invoker(vmg_ 0)) { /* * it's an invokable object - create a function-like object * symbol for it */ ret = new CTcSymFuncObj(sym, len, FALSE, symval.val.obj, FALSE, TC_META_UNKNOWN, 0); } else { /* * It's not an IntrinsicClass object, so it's a regular * CTcSymObj symbol. Check for special metaclasses that * the compiler is aware of. */ tc_metaclass_t meta = TC_META_UNKNOWN; if (CVmObjTads::is_tadsobj_obj(vmg_ symval.val.obj)) meta = TC_META_TADSOBJ; else if (CVmObjGramProd::is_gramprod_obj(vmg_ symval.val.obj)) meta = TC_META_GRAMPROD; else if (CVmObjDict::is_dictionary_obj(vmg_ symval.val.obj)) meta = TC_META_DICT; else if (CVmObjIntClsMod::is_intcls_mod_obj(vmg_ symval.val.obj)) meta = TC_META_ICMOD; /* create the object symbol */ ret = new CTcSymObj(sym, len, FALSE, symval.val.obj, FALSE, meta, 0); } break; case VM_PROP: /* it's a property ID - map to a CTcSymProp */ ret = new CTcSymProp(sym, len, FALSE, symval.val.prop); break; case VM_FUNCPTR: /* it's a function pointer */ { /* get the method header, for the parameter information */ CVmFuncPtr fp(vmg_ symval.val.ofs); /* set up the resolved function symbol */ ret = new CTcSymFunc( sym, len, FALSE, fp.get_min_argc(), fp.get_opt_argc(), fp.is_varargs(), TRUE, FALSE, FALSE, FALSE, TRUE); /* we know the absolute code address - set it */ ((CTcSymFunc *)ret)->set_abs_addr(symval.val.ofs); } break; case VM_ENUM: /* enumerated constant */ ret = new CTcSymEnum(sym, len, FALSE, symval.val.enumval, FALSE); break; case VM_BIFPTR: /* built-in function pointer */ ret = 0; { /* decode the function pointer */ ushort set_idx = symval.val.bifptr.set_idx; ushort func_idx = symval.val.bifptr.func_idx; /* get the function descriptor */ const vm_bif_desc *desc = G_bif_table->get_desc(set_idx, func_idx); /* if there's a valid descriptor, set up the symbol */ if (desc != 0 && desc->func != 0) { /* found it - set up the symbol */ ret = new CTcSymBif(sym, len, FALSE, set_idx, func_idx, TRUE, desc->min_argc, desc->min_argc + desc->opt_argc, desc->varargs); } } break; default: /* * it's either not in the table or it's not a type we can map * to a compiler symbol - return 'not found' */ ret = 0; break; } /* done with the gc protection */ G_stk->discard(2); /* return the symbol */ return ret; } /* VM globals */ vm_globals *gptr_; /* the local and global symbol table objects */ vm_obj_id_t globals_; vm_obj_id_t locals_; }; /* ------------------------------------------------------------------------ */ /* * Dynamic compiler macro table. This works a lot like our global symbol * table, to provide program-defined macros to the tokenizer during dynamic * compilation. */ class CVmDynFuncMacros: public CTcMacroTable { public: CVmDynFuncMacros(VMG_ vm_obj_id_t tab) : hash_(512, new CVmHashFuncCS(), TRUE) { /* remember the globals */ globals_ = VMGLOB_ADDR; /* remember my symbol table object */ tab_ = tab; } /* find an entry */ CVmHashEntry *find(const char *sym, size_t len) { CVmHashEntry *entry; /* first, check to see if it's our underlying table */ entry = hash_.find(sym, len); if (entry != 0) return entry; /* if there's no user-code table, fail */ if (tab_ == VM_INVALID_OBJ) return 0; /* establish access to globals */ VMGLOB_PTR(globals_); /* create a string for the symbol name; stack it for gc protection */ vm_val_t symstr; symstr.set_obj(CVmObjString::create(vmg_ FALSE, sym, len)); G_stk->push(&symstr); /* look up the symbol string in the table */ vm_val_t symval; vm_objp(vmg_ tab_)->index_val_ov(vmg_ &symval, tab_, &symstr); G_stk->push(&symval); /* it has to be a list, and it has to have at least three elements */ const char *lst = symval.get_as_list(vmg0_); if (lst != 0 && vmb_get_len(lst) >= 3) { vm_val_t ele, subele; const char *exp, *arglst; uint flags = 0; int argc; const char **argv = 0; size_t *argvlen = 0; int i; /* the first element is the expansion string */ CVmObjList::index_list(vmg_ &ele, lst, 1); if ((exp = ele.get_as_string(vmg0_)) == 0) goto done; /* the second element is the argument list */ CVmObjList::index_list(vmg_ &ele, lst, 2); if ((arglst = ele.get_as_list(vmg0_)) == 0) goto done; /* figure the argument count */ argc = vmb_get_len(arglst); /* build the argument vector */ if (argc != 0) { /* allocate the string and length arrays */ argv = new const char*[argc]; argvlen = new size_t[argc]; /* populate the arrays */ for (i = 0 ; i < argc ; ++i) { /* retrieve this argument name string */ CVmObjList::index_list(vmg_ &subele, arglst, i+1); const char *n = subele.get_as_string(vmg0_); if (n == 0) goto done; /* add it to the string and length arrays */ argv[i] = n + VMB_LEN; argvlen[i] = vmb_get_len(n); } } /* the third element is the flags */ CVmObjList::index_list(vmg_ &ele, lst, 3); if (!ele.is_numeric(vmg0_)) goto done; flags = ele.num_to_int(vmg0_); /* create the entry */ entry = new CTcHashEntryPpDefine( sym, len, TRUE, (flags & 1) != 0, argc, (flags & 2) != 0, argv, argvlen, exp + VMB_LEN, vmb_get_len(exp)); /* add the entry to our cache */ hash_.add(entry); done: /* free the argument list */ if (argv != 0) delete [] argv; if (argvlen != 0) delete [] argvlen; } /* done with our gc protection */ G_stk->discard(2); /* return what we found */ return entry; } /* add an entry - ignore */ void add(CVmHashEntry *) { } /* remove an entry - ignore */ void remove(CVmHashEntry *) { } /* enumerate entries - ignore */ void enum_entries(void (*)(void *, class CVmHashEntry *), void *) { } /* dump to a file - ignore */ void debug_dump() { } private: /* VM globals */ vm_globals *globals_; /* the user LookupTable object */ vm_obj_id_t tab_; /* our cache of symbols we've already translated */ CVmHashTable hash_; }; /* ------------------------------------------------------------------------ */ /* * Grammar production alternative parser - compiler interface for dynamic * compilation */ class CTcGramAltFuncsDyn: public CTcGramAltFuncs { public: /* initialize with the parser object */ CTcGramAltFuncsDyn(CTcPrsSymtab *symtab) { this->symtab = symtab; } /* look up a property - look up or add to the global symbols */ CTcSymProp *look_up_prop(const CTcToken *tok, int show_err) { /* look up the symbol */ CTcSymProp *prop = (CTcSymProp *)symtab->find( tok->get_text(), tok->get_text_len()); /* if it's already defined, make sure it's a property */ if (prop != 0) { /* make sure it's a property */ if (prop->get_type() != TC_SYM_PROP) { /* log an error if appropriate */ if (show_err) { G_tok->log_error( TCERR_REDEF_AS_PROP, (int)tok->get_text_len(), tok->get_text()); } /* we can't use the symbol, so forget it */ prop = 0; } } else { /* * undefined - we can't add it for dynamic compilation, so log * an error */ if (show_err) { G_tok->log_error( TCERR_UNDEF_SYM, (int)tok->get_text_len(), tok->get_text()); } } /* return the property */ return prop; } /* declare a grammar production symbol */ CTcGramProdEntry *declare_gramprod(const char *txt, size_t len) { /* * we can't create symbols in the dynamic compiler - log an error * and return failure */ G_tok->log_error(TCERR_UNDEF_SYM, (int)len, txt); return 0; } /* * Check an enum for use as a production token. For dynamic * compilation, we don't enforce the 'enum token' requirement that the * static compiler applies, because we don't have any information on * the 'enum token' attribute in the basic run-time symbol table. */ void check_enum_tok(class CTcSymEnum *) { } /* * EOF in the middle of an alternative - when we're parsing a * stand-alone grammar rule list, the rule list is the whole input * text, so EOF just means we've reached the end */ void on_eof(int * /*err*/) { } /* the parser's global symbol table */ CTcPrsSymtab *symtab; }; /* ------------------------------------------------------------------------ */ /* * Dynamic compiler interface to the VM. */ class CVmDynFuncVMIfc: public CTcVMIfc { public: CVmDynFuncVMIfc(VMG0_) { /* remember the globals */ vmg = VMGLOB_ADDR; /* we haven't saved any objects on the stack yet */ gc_cnt = 0; } /* create a live object in the VM */ virtual tctarg_obj_id_t new_obj(tctarg_obj_id_t cls) { /* establish access to globals */ VMGLOB_PTR(vmg); /* create a TadsObject instance */ int argc = 0; if (cls != VM_INVALID_OBJ) { G_stk->push_obj(vmg_ cls); ++argc; } vm_obj_id_t id = CVmObjTads::create_from_stack(vmg_ 0, argc); /* push it for gc protection */ G_stk->push_check()->set_obj(id); ++gc_cnt; /* return the new ID */ return id; } /* validate an object value */ virtual int validate_obj(tctarg_obj_id_t obj) { VMGLOB_PTR(vmg); return G_obj_table->is_obj_id_valid(obj); } /* add a property value to an object */ virtual void set_prop(tctarg_obj_id_t obj, tctarg_prop_id_t prop, const CTcConstVal *c) { /* establish access to globals */ VMGLOB_PTR(vmg); /* set up a value based on the constant value */ vm_val_t val; switch (c->get_type()) { case TC_CVT_TRUE: val.set_true(); break; case TC_CVT_INT: val.set_int(c->get_val_int()); break; case TC_CVT_SSTR: val.set_obj(CVmObjString::create( vmg_ FALSE, c->get_val_str(), c->get_val_str_len())); break; case TC_CVT_OBJ: val.set_obj(c->get_val_obj()); break; case TC_CVT_PROP: val.set_propid((vm_prop_id_t)c->get_val_prop()); break; case TC_CVT_ENUM: val.set_enum(c->get_val_enum()); break; case TC_CVT_FLOAT: val.set_obj(CVmObjBigNum::create( vmg_ FALSE, c->get_val_float(), c->get_val_float_len())); break; case TC_CVT_LIST: case TC_CVT_FUNCPTR: case TC_CVT_ANONFUNCPTR: // $$$ implement later if necessary val.set_nil(); break; case TC_CVT_NIL: case TC_CVT_UNK: default: val.set_nil(); break; } /* set the property in the object */ vm_objp(vmg_ obj)->set_prop(vmg_ G_undo, obj, prop, &val); } /* validate a property value */ virtual int validate_prop(tctarg_prop_id_t prop) { VMGLOB_PTR(vmg); return prop <= G_image_loader->get_last_prop(vmg0_); } /* validate a built-in function pointer */ virtual int validate_bif(uint set_index, uint func_index) { VMGLOB_PTR(vmg); return G_bif_table->validate_entry(set_index, func_index); } /* validate a constant pool list address */ virtual int validate_pool_str(uint32_t ofs) { /* establish globals */ VMGLOB_PTR(vmg); /* validate the starting offset and full length prefix */ if (!G_const_pool->validate_ofs(ofs) || !G_const_pool->validate_ofs(ofs+1)) return FALSE; /* get the length */ size_t len = vmb_get_len(G_const_pool->get_ptr(ofs)); /* make sure the end of the string is valid as well */ if (!G_const_pool->validate_ofs(ofs + VMB_LEN + len - 1)) return FALSE; /* success */ return TRUE; } /* validate a constant pool string address */ virtual int validate_pool_list(uint32_t ofs) { /* establish globals */ VMGLOB_PTR(vmg); /* validate the starting offset and full length prefix */ if (!G_const_pool->validate_ofs(ofs) || !G_const_pool->validate_ofs(ofs+1)) return FALSE; /* get the length */ const char *p = G_const_pool->get_ptr(ofs); size_t len = vmb_get_len(p); p += VMB_LEN; /* make sure the end of the string is valid as well */ if (!G_const_pool->validate_ofs( ofs + VMB_LEN + (len*VMB_DATAHOLDER) - 1)) return FALSE; /* validate that each element is valid */ for ( ; len != 0 ; p += VMB_DATAHOLDER, --len) { if (*p < 0 || *p >= (int)VM_FIRST_INVALID_TYPE) return FALSE; } /* success */ return TRUE; } /* discard items pushed on the stack for gc protection */ void discard() { VMGLOB_PTR(vmg); G_stk->discard(gc_cnt); } protected: /* number of objects we've pushed for gc protection */ int gc_cnt; /* VM globals */ vm_globals *vmg; }; /* ------------------------------------------------------------------------ */ /* * Dynamic compiler results object */ /* throw an error based on the results */ void CVmDynCompResults::throw_error(VMG0_) { /* push the message text as the exception constructor argument */ if (msgbuf != 0) { /* push the message as a string object */ G_stk->push()->set_obj(CVmObjString::create( vmg_ FALSE, msgbuf, strlen(msgbuf))); /* done with the message buffer */ free_msgbuf(); } else { /* no message - push a nil argument */ G_stk->push()->set_nil(); } /* throw the error */ G_interpreter->throw_new_class(vmg_ G_predef->compiler_exc, 1, "code compilation failed"); } /* ------------------------------------------------------------------------ */ /* * Dynamic Compiler Interface */ /* * Construction */ CVmDynamicCompiler::CVmDynamicCompiler(VMG0_) { /* create our host interface */ hostifc_ = new CTcHostIfcDynComp(); /* initialize the compiler */ CTcMain::init(hostifc_, G_host_ifc->get_sys_res_loader(), "utf8"); /* * set up the compiler size globals from the corresponding load image * size records, so that dynamically generated byte code conforms to * the image file settings */ G_sizes.mhdr = G_interpreter->get_funchdr_size(); G_sizes.exc_entry = G_exc_entry_size; G_sizes.dbg_hdr = G_dbg_hdr_size; G_sizes.dbg_line = G_line_entry_size; G_sizes.lcl_hdr = G_dbg_lclsym_hdr_size; G_sizes.dbg_fmt_vsn = G_dbg_fmt_vsn; G_sizes.dbg_frame = G_dbg_frame_size; /* point the compiler to the loaded metaclass table */ G_metaclass_tab = G_meta_table; } /* * Destruction */ CVmDynamicCompiler::~CVmDynamicCompiler() { /* terminate the compiler */ CTcMain::terminate(); /* delete our host interface */ delete hostifc_; } /* * Get or create the global singleton instance. */ CVmDynamicCompiler *CVmDynamicCompiler::get(VMG0_) { /* if there's no compiler object, create one */ if (G_dyncomp == 0) G_dyncomp = new CVmDynamicCompiler(vmg0_); /* return the instance */ return G_dyncomp; } /* * Compile source code */ vm_obj_id_t CVmDynamicCompiler::compile( VMG_ int in_root_set, vm_obj_id_t globals, vm_obj_id_t locals, vm_obj_id_t macros, const vm_val_t *srcval, const char *src, size_t srclen, CVmDynCompMode mode, CVmDynCompDebug *dbg, CVmDynCompResults *results) { vm_obj_id_t coid = 0; CTcPrsSymtab *old_global_symtab; CTcMacroTable *old_macros; CVmDynFuncMacros *new_macros = 0; int pushcnt = 0; CTcPrsNode *node = 0; int frame_has_self = FALSE; CVmObjFrameDesc *framedesc = 0; CVmObjFrameRef *frameref = 0; /* * save the parser memory pool state, so we can reset it when we're * done (this allows us to discard any parser memory we allocate while * we're working - we only need it while compiling, and can discard it * when we're done) */ tcprsmem_state_t prsmem_state; G_prsmem->save_state(&prsmem_state); /* * Set the local symbol table context. If the caller provided a * debugger context, use the debugger symbol table; otherwise there's * no local symbol context. */ CTcPrsDbgSymtab *old_symtab = G_prs->set_debug_symtab( dbg != 0 ? dbg->symtab : 0); /* don't allow #define or other directives for dynamic compilation */ G_tok->enable_pp(FALSE); /* enable the special debugger syntax if it's for the debugger */ G_prs->set_debug_expr(dbg != 0); /* set up a dynamic VM interface for the compiler */ CVmDynFuncVMIfc vmifc Pvmg0_P; G_vmifc = &vmifc; /* * If there's no debugger context, set up the global symbol table and * the new macro table. */ if (dbg == 0) { /* install a new symbol table; remember the old one to restore */ old_global_symtab = G_prs->set_global_symtab( new CVmDynFuncSymtab(vmg_ globals, locals)); /* install a new macro table; remember the old one to restore */ old_macros = G_tok->set_defines_table( new_macros = new CVmDynFuncMacros(vmg_ macros)); } /* presume no error will occur */ int errcode = 0, errflag = FALSE; results->err = 0; results->free_msgbuf(); /* presume we won't generate a statement node */ CTPNCodeBody *cb = 0; /* catch any errors that occur during parsing or code generation */ err_try { /* * If there's a local stack frame object, note if it has a method * context ('self', etc - it's sufficient to check that 'self' is * not nil). If so, code defined with 'function()' uses the method * context of the enclosing stack frame, in analogy to an anonymous * function using its enclosing lexical scope's method context. If * we have multiple stack frames, consider only the innermost. */ if (locals != VM_INVALID_OBJ) { /* presume we have just a single frame object */ vm_obj_id_t frame = locals; /* * if it's not in fact a stack frame object, and it's * list-like, it's a collection of frames ordered from * innermost to outermost: so retrieve the first element to get * the innermost frame */ CVmObject *localp = vm_objp(vmg_ locals); if (!CVmObjFrameDesc::is_framedesc_obj(vmg_ locals) && localp->is_listlike(vmg_ locals) && localp->ll_length(vmg_ locals) > 0) { /* * it's a list - retrieve its first element; if that's an * object, use it as the frame object */ vm_val_t ele, v1; v1.set_int(1); localp->index_val_ov(vmg_ &ele, locals, &v1); if (ele.typ == VM_OBJ) frame = ele.val.obj; } /* if we now have a frame object, check its 'self' */ if (CVmObjFrameDesc::is_framedesc_obj(vmg_ frame)) { /* get the frame object, suitably cast */ framedesc = (CVmObjFrameDesc *)vm_objp(vmg_ frame); frameref = framedesc->get_frame_ref(vmg0_); /* get 'self' from the frame */ vm_val_t self; frameref->get_self(vmg_ &self); /* if that's not nil, we have a method context */ frame_has_self = (self.typ != VM_NIL); } } /* reset the compiler error counters for the new parse */ G_tcmain->reset_error_counts(); /* clear any old error messages in our host interface */ hostifc_->reset_messages(); /* point the tokenizer to the source string */ G_tok->set_source_buf(src, srclen); /* read the first token */ G_tok->next(); /* parse the source code */ switch (mode) { case DCModeExpression: /* parse an expression */ if ((node = G_prs->parse_expr()) != 0) { /* check some extra information for the debugger */ // $$$ does this need to be deferred until after folding // constants and adjust_for_dyn??? if (dbg != 0 && node != 0) { /* the debugger wants to know if it's an lvalue */ dbg->is_lval = node->check_lvalue_resolved( G_prs->get_global_symtab()); } /* wrap it in a 'return' and a code body */ cb = new CTPNCodeBody(G_prs->get_global_symtab(), 0, new CTPNStmReturn(node), 0, 0, FALSE, FALSE, 0, 0, dbg != 0 ? dbg->self_valid : FALSE, 0); /* mark it as a dynamic function */ cb->set_dyn_func(TRUE); } break; case DCModeAuto: /* * Auto-sensing mode - parse a function or expression based on * what the string looks like. * * If the first token is 'function', we have a function * definition of the form "function(args) { body }". This is * compiled as an ordinary function with no name. * * If the first token is 'method', we have a method definition * of the form "method(args) { body }". This is compiled as a * floating method definition with no name. * * Otherwise, we have an expression. We compile this as an * expression, then wrap it in a "return" statement and a * function code body with zero arguments. */ if (G_tok->cur() == TOKT_FUNCTION) { /* skip the 'function' token */ G_tok->next(); /* parse the code body */ cb = G_prs->parse_code_body(FALSE, FALSE, frame_has_self, 0, 0, 0, 0, 0, 0, &errflag, 0, TCPRS_CB_NORMAL, 0, 0, 0, 0); /* mark it as a dynamic function */ cb->set_dyn_func(TRUE); } else if (G_tok->cur() == TOKT_METHOD) { /* skip the 'method' token */ G_tok->next(); /* parse the code body */ cb = G_prs->parse_code_body(FALSE, FALSE, TRUE, 0, 0, 0, 0, 0, 0, &errflag, 0, TCPRS_CB_NORMAL, 0, 0, 0, 0); /* mark it as a dynamic method */ cb->set_dyn_method(TRUE); } else { /* parse an expression */ if ((node = G_prs->parse_expr()) != 0) { /* wrap it in a 'return' an a zero-argument code body */ cb = new CTPNCodeBody(G_prs->get_global_symtab(), 0, new CTPNStmReturn(node), 0, 0, FALSE, FALSE, 0, 0, dbg != 0 ? dbg->self_valid : FALSE, 0); /* mark it as a dynamic function */ cb->set_dyn_func(TRUE); } } break; case DCModeGramAlt: /* parse a grammar alternative list */ { /* set up a dummy symbol to represent the match object */ CTcSymObj *gram_obj = new CTcSymObj( ".anon", 5, FALSE, VM_INVALID_OBJ, FALSE, TC_META_TADSOBJ, 0); /* set up a production to hold the list */ CTcGramProdEntry *prod = new (G_prsmem) CTcGramProdEntry(gram_obj); /* parse the alternative list */ CTcGramPropArrows arrows; CTcGramAltFuncsDyn funcs(G_prs->get_global_symtab()); G_prs->parse_gram_alts( &errflag, gram_obj, prod, &arrows, &funcs); /* * if the parse was successful, send the parsed data back * to the caller */ if (!errflag && G_tcmain->get_error_count() == 0) results->save_grammar(vmg_ prod->get_alt_head(), &arrows); } break; } /* * if we didn't get a parse node out of the deal, or any compiler * errors occurred, fail */ if (cb == 0 || errflag || G_tcmain->get_error_count() != 0) goto compilation_done; /* assign an object ID for CodeBody for the main code we parsed */ coid = vm_new_id(vmg_ in_root_set, TRUE, FALSE); cb->set_dyn_obj_id(coid); /* * create a dummy object for it - we'll replace it with a real * object when actually generating the code */ new (vmg_ coid) CVmDynamicFunc(); /* save it on the stack for GC protection */ G_stk->push_check()->set_obj(coid); ++pushcnt; /* * Allocate object IDs for the nested code bodies. Each nested * code body requires its own separate CodeBody object. Nested * code bodies are typically referenced from the main code body, * and might cross-reference one another, so in order to generate * code for any of the code bodies, we have to pre-assign an object * ID to *all* code bodies first. */ for (CTPNStmTop *ts = G_prs->get_first_nested_stm() ; ts != 0 ; ts = ts->get_next_stm_top()) { /* allocate and assign the object ID */ vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmDynamicFunc(); ts->set_dyn_obj_id(id); /* save it on the stack for GC protection */ G_stk->push_check()->set_obj(id); ++pushcnt; } /* generate the code for the main object */ if (!gen_code_body(vmg_ cb, srcval, dbg)) goto compilation_done; /* * If necessary, store the method context in the object. This is * required if this is defined with 'function' rather than * 'method', and it references 'self' or other method context * variables, and there's a local frame to use for the method * context. */ if (frame_has_self && cb->is_dyn_func() && (cb->self_referenced() || cb->full_method_ctx_referenced())) { /* * We need the method context. The context is simply the * 'self' object if the rest of the context wasn't referenced, * otherwise it's the context object suitable for the LOADCTX * opcode. */ vm_val_t mctx; if (cb->full_method_ctx_referenced()) { /* we need the full method context object for LOADCTX */ frameref->create_loadctx_obj(vmg_ &mctx); } else { /* the context is simply the 'self' object */ frameref->get_self(vmg_ &mctx); } /* save the method context in the DynamicFunc */ ((CVmDynamicFunc *)vm_objp(vmg_ coid))->set_method_ctx(&mctx); } /* generate each nested statement code body */ for (CTPNStmTop *ts = G_prs->get_first_nested_stm() ; ts != 0 ; ts = ts->get_next_stm_top()) { if (!gen_code_body(vmg_ ts, 0, dbg)) goto compilation_done; } compilation_done: ; } err_catch(exc) { /* note the error code */ errcode = exc->get_error_code(); /* if there were no logged errors, use the error from the exception */ if (G_tcmain->get_error_count() == 0) { /* delete any old message text */ results->free_msgbuf(); /* format the message into the result object */ const char *msg = tcerr_get_msg(errcode, FALSE); results->msgbuf = err_format_msg(msg, exc); } /* we don't have a valid object */ coid = VM_INVALID_OBJ; } err_end; /* if a compilation error occurred, return failure */ if (errcode != 0 || errflag) { /* parsing failed - stop now */ coid = VM_INVALID_OBJ; goto done; } else if (G_tcmain->get_error_count() != 0 || coid == VM_INVALID_OBJ) { /* parse errors were reported - retrieve the first and stop */ errcode = G_tcmain->get_first_error(); coid = VM_INVALID_OBJ; goto done; } done: /* restore the original symbol table and macros in the parser */ G_prs->set_debug_symtab(old_symtab); /* reset the code generator streams */ G_ds->reset(); G_cs->reset(); G_os->reset(); /* restore the old global symbol and macro tables */ if (dbg == 0) { G_prs->set_global_symtab(old_global_symtab); G_tok->set_defines_table(old_macros); } /* delete the macro table if we created one */ if (new_macros != 0) delete new_macros; /* reset the parser and tokenizer */ G_prs->reset(); G_tok->reset(); /* we're done with the compiler's working state */ G_prsmem->reset(&prsmem_state); /* * if there were logged compiler errors, and we haven't already * returned an exception message, return the captured compiler error * messages from the host interface */ if (G_tcmain->get_error_count() != 0 && results->msgbuf == 0) results->msgbuf = lib_copy_str(hostifc_->get_error_msg()); /* return the error code result */ results->err = errcode; /* discard our GC protection, plus the vmifc's */ G_stk->discard(pushcnt); vmifc.discard(); /* return the DynamicFunc we created, if any */ return coid; } /* * Generate code for a parsed code body. Parsing a single block of source * code might yield multiple parsed code bodies, because anonymous * functions and other nested structures require separate top-level code * bodies. */ int CVmDynamicCompiler::gen_code_body( VMG_ CTPNStmTop *node, const vm_val_t *srcval, CVmDynCompDebug *dbg) { int i; CTcIdFixup *fixup; /* clear the object fixup list - we want just this object's fixups */ G_objfixup = 0; /* our code will start at the current code stream write pointer */ ulong start_ofs = G_cs->get_ofs(); /* fold constants */ if (node->fold_constants(G_prs->get_global_symtab()) == 0) return FALSE; /* * Make adjustments for dynamic compilation. If the caller provided * debugger evaluation options, use the dynamic adjustment options * specified there; otherwise use defaults. */ tcpn_dyncomp_info di; if (node->adjust_for_dyn(dbg != 0 ? &dbg->di : &di) == 0) return FALSE; /* * Set the code generation mode. If we have debugger options, set * debug mode; otherwise set dynamic compilation mode. */ if (dbg != 0) G_cg->set_debug_eval(dbg->di.speculative, dbg->di.stack_level); else G_cg->set_dyn_eval(); /* * Keep object fixups - we need these to find object references in the * generated bytecode. We don't need property or enum fixups, since * we're building against a loaded image file with all of those types * already resolved. */ G_keep_objfixups = TRUE; /* make the global symbol table active for code generation */ G_cs->set_symtab(G_prs->get_global_symtab()); /* generate the code */ node->gen_code(FALSE, FALSE); /* if any errors occurred during code generation, return failure */ if (G_tcmain->get_error_count() != 0) return FALSE; /* * Go through the object fixup list and count up references to objects * that aren't in the root set. We need to keep a list of these * references in the generated code object, because the code object * needs to manage them like any other object manages its live * references: mark them during garbage collection, fix them up during * save/restore, etc. */ int refcnt = 0; for (fixup = G_objfixup ; fixup != 0 ; fixup = fixup->nxt_) { /* if it refers to a non-root-set object, count it */ if (!G_obj_table->is_obj_in_root_set((vm_obj_id_t)fixup->id_)) ++refcnt; } /* figure the bytecode length for allocating the DynamicFunc instance */ size_t bytecode_rem = (size_t)(G_cs->get_ofs() - start_ofs); /* the caller already allocated our object ID - retrieve it */ vm_obj_id_t coid = node->get_dyn_obj_id(); /* create the C++ object */ CVmDynamicFunc *co = new (vmg_ coid) CVmDynamicFunc( vmg_ coid, srcval, bytecode_rem, refcnt); /* save the object reference list */ for (i = 0, fixup = G_objfixup ; fixup != 0 ; fixup = fixup->nxt_) { /* if it refers to a non-root-set object, save it */ if (!G_obj_table->is_obj_in_root_set((vm_obj_id_t)fixup->id_)) co->get_ext()->obj_refs[i++] = fixup->ofs_ - start_ofs; } /* copy the code */ char *bc = co->get_ext()->get_bytecode_ptr(); for (size_t blkofs = 0 ; bytecode_rem != 0 ; ) { /* get the next block */ ulong blklen; const char *blkp = G_cs->get_block_ptr( start_ofs + blkofs, bytecode_rem, &blklen); /* copy the data */ memcpy(bc + blkofs, blkp, blklen); /* advance past the copied data */ blkofs += blklen; bytecode_rem -= blklen; } /* success */ return TRUE; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmdynfunc.h�����������������������������������������������������������������������0000664�0000000�0000000�00000043200�12650170723�0016037�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 2010 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmdynfunc.h - Code Object Function A Code Object is an intrinsic class object that contains a block of byte code. This can be used for things like debugger expressions and run-time evaluation and code creation. A Code Object is immutable, like a String or List. It contains the original source code text, as a UTF-8 string, and the corresponding compiled byte code. Notes Modified 12/13/09 MJRoberts - Creation */ #ifndef VMDYNFUNC_H #define VMDYNFUNC_H #include "t3std.h" #include "vmtype.h" #include "tcprstyp.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" /* ------------------------------------------------------------------------ */ /* * The image file data block is arranged as follows: * *. UINT2 code_length *. UINT2 obj_ref_cnt *. DATAHOLDER src_string *. BYTE[code_length] bytecode *. UINT2[ref_cnt] obj_refs * * code_length is the size of the bytecode stream, in bytes. * * obj_ref_cnt is the number of object references. These are stored * immediately after the bytecode stream. Each reference array entry is a * UINT2 giving the byte offset in the bytecode array of the reference, * which is a UINT4 stored in portable format giving the object ID. */ /* ------------------------------------------------------------------------ */ /* * Our in-memory extension data structure, which mimics the image file * structure but uses native types. */ /* prefix header size */ const size_t VMCO_PREFIX_LENGTH = VMB_OBJECT_ID; /* extension structure */ struct vm_dynfunc_ext { /* allocate the structure */ static vm_dynfunc_ext *alloc_ext(VMG_ class CVmDynamicFunc *self, size_t bytecode_len, int obj_ref_cnt); /* the source string object */ vm_val_t src; /* * Method context object. For a function that's compiled with a local * stack frame context, this is the 'self' object or the complete * method context object (suitable for the LOADCTX opcode), as * applicable, for the enclosing method. This allows the function to * establish the dynamic enclosing method context at entry. */ vm_val_t method_ctx; /* the size in bytes of the byte code data */ size_t bytecode_len; /* number of object references in the fixup list */ int obj_ref_cnt; /* * Object references. The structure is allocated with enough memory * for 'obj_ref_cnt' entries in this array. */ uint obj_refs[1]; /* * The structure is allocated with memory following the 'obj_refs' * array for the following dynamic elements: * *. char dynamic_code_header[VMB_OBJECT_ID] *. char bytecode[bytecode_len] * * Explanation of the dynamic object header: * * The VM internally refers to executing code by a pointer directly * into the code's physical memory. It always keeps an Entry Pointer * value giving a pointer to the first byte of the method; this makes * it easy to get metadata about any active code block by serving as an * identifier for the code. * * Regular code is stored at compile-time in the Code Pool, which is a * special block of read-only memory especially for code. The FUNCPTR * and CODEPTR primitive datatypes contain offsets into the Code Pool. * A function generated by the compiler is represented by a FUNCPTR * value. To get a FUNCPTR value given an Entry Pointer, we simply * translate the physical memory pointer back into a Code Pool offset, * and wrap the result in a FUNCPTR value. * * Dynamic code objects are not in the Code Pool, however, since that's * read-only memory that we can't add to at run-time. Dynamic code * objects are instead allocated in the garbage-collected heap like * other objects. So we need some way to translate from an Entry * Pointer that points into a dynamic object back into a vm_val_t. The * vm_val_t representation of a dynamic object is a reference to the * object - i.e., an OBJ value containing the code object's ID. * There's no generic way to look up an object ID given the extension * pointer, which is what the Entry Pointer for a dynamic code object * basically is. (The Entry Pointer doesn't actually point to the * extension - it points to the start of the 'buf' element of the * extension, because that's where our method header starts.) So, we * need our own way of working from the Entry Point back to our object * ID. To do this, we simply add our object ID just ahead of the * method header. To generate a vm_val_t from an Entry Pointer value, * then, we first look to see if the pointer is a Code Pool element; if * so, we use a FUNCPTR value; if not, it must be a dynamic code * object, so we retrieve the OBJECT_ID value at (Entry Pointer - * VMB_OBJECT_ID) as an object ID, and create an OBJ value with that * ID. */ /* get a pointer to the dynamic code object method header prefix */ char *get_prefix_ptr() { /* the prefix starts after the obj_ref array */ return (char *)&obj_refs[obj_ref_cnt]; } /* get a pointer to the start of the byte code */ char *get_bytecode_ptr() { /* the byte code starts after the dynamic code prefix */ return get_prefix_ptr() + VMCO_PREFIX_LENGTH; } }; /* ------------------------------------------------------------------------ */ /* * DynamicFunc metaclass */ class CVmDynamicFunc: public CVmObject { friend class CVmMetaclassDynamicFunc; friend class CVmDynamicCompiler; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is this a DynamicFunc object? */ static int is_dynfunc_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* * Given a pointer to a method header, retrieve the object ID of the * DynamicFunc that owns the bytecode. Returns VM_INVALID_OBJ if we * can't find a valid owner. */ static vm_obj_id_t get_obj_from_prefix(VMG_ const uchar *p); /* create from a string, without saving the source text */ static vm_obj_id_t create(VMG_ int in_root_set, vm_obj_id_t globals, vm_obj_id_t locals, vm_obj_id_t macros, const char *src, size_t src_len) { vm_val_t v; v.set_nil(); return create(vmg_ in_root_set, globals, locals, macros, &v, src, src_len); } /* create from a string value */ static vm_obj_id_t create(VMG_ int in_root_set, vm_obj_id_t globals, vm_obj_id_t locals, vm_obj_id_t macros, const vm_val_t *src); /* create from a string, optionally saving the string value */ static vm_obj_id_t create(VMG_ int in_root_set, vm_obj_id_t symtab, vm_obj_id_t locals, vm_obj_id_t macros, const vm_val_t *src_val, const char *src, size_t src_len); /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self) { /* we cannot be converted to constant data */ } /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *, vm_obj_id_t) { /* we don't reference anything */ } /* index the object */ virtual int index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val); /* set a property */ void set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { /* we have no settable properties */ err_throw(VMERR_INVALID_SETPROP); } /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* invoke */ int get_invoker(VMG_ vm_val_t *val); /* undo operations - we're immutable, so we can ignore these */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { } void discard_undo(VMG_ struct CVmUndoRecord *) { } void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* mark our references (we reference our source string) */ void mark_refs(VMG_ uint); /* we don't keep any weak references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* we're immutable, so we're definitely not changed since loading */ int is_changed_since_load() const { return FALSE; } protected: /* get my extension data */ vm_dynfunc_ext *get_ext() const { return (vm_dynfunc_ext *)ext_; } /* load or reload image data */ void load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* set the method context object */ void set_method_ctx(const vm_val_t *val) { get_ext()->method_ctx = *val; } /* create a with no initial contents */ CVmDynamicFunc() { ext_ = 0; } /* create with the given byte code array length */ CVmDynamicFunc(VMG_ vm_obj_id_t self, const vm_val_t *src, size_t bytecode_len, int obj_ref_cnt); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - get my source text */ int getp_get_source(VMG_ vm_obj_id_t, vm_val_t *, uint *); /* property evaluation function table */ static int (CVmDynamicFunc::*func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * DynamicFunc registration table object */ class CVmMetaclassDynamicFunc: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "dynamic-func/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmDynamicFunc(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmDynamicFunc(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmDynamicFunc::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmDynamicFunc::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; /* ------------------------------------------------------------------------ */ /* * Dynamic compiler interface */ /* compilation modes */ enum CVmDynCompMode { /* compile an expression */ DCModeExpression, /* auto-sensing: 'function' syntax, 'method' syntax, or an expression */ DCModeAuto, /* compile a grammar production rule (alternative) list */ DCModeGramAlt }; /* debugger expression context */ struct CVmDynCompDebug { CVmDynCompDebug(class CTcPrsDbgSymtab *symtab, tcpn_dyncomp_info &di, int self_valid) { this->symtab = symtab; this->di = di; this->self_valid = self_valid; } /* IN: local symbol table for debugger expression evaluation */ class CTcPrsDbgSymtab *symtab; /* IN: debugger evaluation context settings */ tcpn_dyncomp_info di; /* IN: is there a valid 'self' for this expression? */ int self_valid; /* OUT: the parsed expression is an lvalue */ int is_lval; }; /* results structure */ struct CVmDynCompResults { CVmDynCompResults() { /* clear the error code */ err = 0; /* no error message */ msgbuf = 0; } virtual ~CVmDynCompResults() { free_msgbuf(); } /* Explicitly free the message buffer */ void free_msgbuf() { if (msgbuf != 0) { t3free(msgbuf); msgbuf = 0; } } /* throw a dynamic compilation error based on the results */ void throw_error(VMG0_); /* * In grammar mode (DCModeGramAlt), the compiler passes back the parsed * alt list to the caller by calling this method. Callers that don't * parse grammar rules can leave this as a no-op. 'alts' is the head * of the alternative list for the parsed grammar rule. */ virtual void save_grammar(VMG_ class CTcGramProdAlt * /*alts*/, struct CTcGramPropArrows * /*arrows*/) { } /* compiler error code */ int err; /* * Message buffer. The compiler allocates this via t3malloc() if an * error occurs. */ char *msgbuf; }; /* * dynamic compiler */ class CVmDynamicCompiler { public: /* create */ CVmDynamicCompiler(VMG0_); /* destroy */ ~CVmDynamicCompiler(); /* * Get or create the global singleton instance. This creates an * instance if it doesn't already exist, so that we don't instantiate * the compiler structures until they're actually needed. Once we * create the object, we keep it around in a global. */ static CVmDynamicCompiler *get(VMG0_); /* * Compile source code into byte code, returning a new DynamicFunc * instance containing the compiled function. The return value is the * ID of the new object if the compilation was successful, or * VM_INVALID_OBJ if the compilation failed. * * The source code is the string given by 'src' and 'srclen'. If * 'srcval' is non-null, it should be a VM_SSTR or VM_OBJ value with * the VM representation of the source code string; we'll store this * with the DynamicFunc for retrieval with getSource(). If 'srcval' is * null, the DynamicFunc won't store the source, so getSource() will * return nil. * * 'mode' gives the compilation mode, which controls how the source * string is parsed. * * If 'dbg' is non-null, we compile for the debugger, with the options * in 'dbg'. Debugger evaluation is slightly different from regular * evaluation because it can access local variables from an enclosing * scope. * * If 'errp' is non-null, we'll fill it in with the TCERR_xxx number * for the first compiler error, if any. * * If 'msgbuf' is non-null, we'll fill it in with an allocated string * buffer containing the compiler error message(s). Multiple messages * are separated by newline '\n' characters. The caller must free this * buffer with t3free() when done with it. */ vm_obj_id_t compile(VMG_ int in_root_set, vm_obj_id_t globals, vm_obj_id_t locals, vm_obj_id_t macros, const vm_val_t *srcval, const char *src, size_t srclen, CVmDynCompMode mode, CVmDynCompDebug *dbg, CVmDynCompResults *results); protected: /* generate code for a code body */ int gen_code_body( VMG_ class CTPNStmTop *node, const vm_val_t *srcval, CVmDynCompDebug *dbg); /* parser */ class CTcParser *prs_; /* compiler host interface */ class CTcHostIfcDynComp *hostifc_; }; #endif /* VMDYNFUNC_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmDynamicFunc) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmerr.cpp�������������������������������������������������������������������������0000664�0000000�0000000�00000064552�12650170723�0015531�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/vmerr.cpp,v 1.4 1999/07/11 00:46:59 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmerr.cpp - VM error handling Function This module contains global variables required for the error handler. Notes Modified 10/20/98 MJRoberts - Creation */ #include <stdarg.h> #include <stdlib.h> #include <assert.h> #include "t3std.h" #include "vmtype.h" #include "vmerr.h" /* ------------------------------------------------------------------------ */ /* * Global error context pointer, and reference count for the error * subsystem. */ OS_DECL_TLS(err_frame_t *, G_err_frame); static int G_err_refs = 0; /* ------------------------------------------------------------------------ */ /* * Initialize the global error context */ void err_init(size_t /*param_stack_size*/) { /* increase the error system reference counter */ if (++G_err_refs == 1) { /* * first initialization - allocate the thread-local variable slot * for the error frame pointer */ os_tls_create(G_err_frame); } } /* ------------------------------------------------------------------------ */ /* * Delete the global error context */ void err_terminate() { /* decrease the error system reference counter */ --G_err_refs; /* if that leaves no references, delete the error context */ if (G_err_refs == 0) { /* delete external messages, if we loaded them */ err_delete_message_array(&vm_messages, &vm_message_count, vm_messages_english, vm_message_count_english); /* delete the thrad-local slot for the error frame pointer */ os_tls_delete(G_err_frame); } } /* ------------------------------------------------------------------------ */ /* * Throw the error currently on the stack */ static void err_throw_current() { err_state_t new_state; /* * Figure out what state the enclosing frame will be in after this * jump. If we're currently in a 'trying' block, we'll now be in an * 'exception' state. Otherwise, we must have been in a 'catch' or * 'finally' already - in these cases, the new state is 'rethrown', * because we have an exception within an exception handler. */ err_state_t old_state = os_tls_get(err_frame_t *, G_err_frame)->state_; err_state_t f = (old_state & ERR_STATE_FINALLY); old_state &= ~ERR_STATE_FINALLY; if (old_state == ERR_STATE_TRYING) new_state = ERR_STATE_EXCEPTION | f; else new_state = ERR_STATE_RETHROWN | f; /* jump to the enclosing frame's exception handler */ longjmp(os_tls_get(err_frame_t *, G_err_frame)->jmpbuf_, new_state); } /* ------------------------------------------------------------------------ */ /* * Throw an exception */ void err_throw(err_id_t error_code) { /* throw the error, with no parameters */ err_throw_a(error_code, 0); } /* ------------------------------------------------------------------------ */ /* * store a string parameter */ static char *err_store_str(char* &strspace, const char *str, size_t len) { /* remember the starting position of the stored string */ char *ret = strspace; /* copy it into the string space */ memcpy(strspace, str, len); /* advance the allocation pointer */ strspace += len; /* add a trailing null */ *strspace++ = '\0'; /* return the start of the string */ return ret; } /* ------------------------------------------------------------------------ */ /* * Throw an exception with parameters in va_list format */ static size_t err_throw_v(err_id_t error_code, int param_count, va_list va, CVmException *exc) { /* * Assert that the size of the err_param_type enum is no larger than * the size of the native 'int' type. Since this routine is called * with a varargs list, and since ANSI requires compilers to promote * any enum type smaller than int to int when passing to a varargs * function, we must retrieve our err_param_type arguments (via the * va_arg() macro) with type 'int' rather than type 'enum * err_param_type'. * * The promotion to int is mandatory, so retrieving our values as * 'int' values is the correct, portable thing to do; the only * potential problem is that our enum type could be defined to be * *larger* than int, in which case the compiler would pass it to us * as the larger type, not int, and our retrieval would be incorrect. * The only way a compiler would be allowed to do this is if our enum * type actually required a type larger than unsigned int (in other * words, we had enum values higher than the largest unsigned int * value for the local platform). This is extremely unlikely, but * we'll assert our assumption here to ensure that the problem is * readily apparent should it ever occur. */ assert(sizeof(err_param_type) <= sizeof(int)); /* * Figure out how much space we need. Start with the size of the base * CVmException class, since we always need a CVmException structure. * This structure has space for one argument descriptor built in, so * subtract that off from our base size, since we'll add in space for * the argument descriptors separately. */ size_t siz = sizeof(CVmException) - sizeof(CVmExcParam); /* * Add in space the parameter descriptors. We need one CVmExcParam * structure to describe each parameter. */ siz += param_count * sizeof(CVmExcParam); /* fill in our base structure, if allocated */ CVmExcParam *param = 0; char *strspace = 0; if (exc != 0) { /* set the error code and parameter count */ exc->error_code_ = error_code; exc->param_count_ = param_count; /* presume this is not an out-of-date version error */ exc->version_flag_ = FALSE; exc->metaclass_ = 0; exc->funcset_ = 0; /* * If the current stack frame is already handling an error, delete * the current frame's exception object, since the current frame * will no longer be accessible after this throw. */ if (os_tls_get(err_frame_t *, G_err_frame)->state_ & (ERR_STATE_EXCEPTION | ERR_STATE_CAUGHT | ERR_STATE_RETHROWN)) t3free(os_tls_get(err_frame_t *, G_err_frame)->exc_); /* this is now the current exception */ os_tls_get(err_frame_t *, G_err_frame)->exc_ = exc; /* start at the first parameter slot */ param = exc->params_; /* * store strings in the allocated space after the base struct and * (varying-size) parameter array */ strspace = (char *)&exc->params_[param_count]; } /* * We now have our base exception structure, with enough space for the * parameter descriptors, but we still need to store the parameter * values themselves. */ for (int i = 0 ; i < param_count ; ++i, ++param) { /* get the type indicator, and store it in the descriptor */ err_param_type typ = (err_param_type)va_arg(va, int); /* store the type */ if (exc != 0) param->type_ = typ; /* store the argument's value */ int ival; ulong ulval; const char *sptr; size_t slen; switch(typ) { case ERR_TYPE_INT: /* store the integer */ ival = va_arg(va, int); if (exc != 0) param->val_.intval_ = ival; break; case ERR_TYPE_ULONG: /* store the unsigned long */ ulval = va_arg(va, unsigned long); if (exc != 0) param->val_.ulong_ = ulval; break; case ERR_TYPE_TEXTCHAR: /* * It's a (textchar_t *) string, null-terminated. Get the * string pointer and calculate its length. */ sptr = va_arg(va, textchar_t *); slen = get_strlen(sptr); /* count the string space needed */ siz += slen + 1; /* store it in parameter memory */ if (exc != 0) param->val_.strval_ = err_store_str(strspace, sptr, slen); break; case ERR_TYPE_TEXTCHAR_LEN: /* * It's a (textchar_t *) string with an explicit length given * as a separate size_t parameter. */ sptr = va_arg(va, textchar_t *); slen = va_arg(va, size_t); /* count the string space */ siz += slen + 1; /* store it in parameter memory */ if (exc != 0) { param->type_ = ERR_TYPE_TEXTCHAR; param->val_.strval_ = err_store_str(strspace, sptr, slen); } break; case ERR_TYPE_CHAR: /* it's a (char *) string, null-terminated */ sptr = va_arg(va, char *); slen = strlen(sptr); /* count the string space */ siz += slen + 1; /* store it */ if (exc != 0) param->val_.charval_ = err_store_str(strspace, sptr, slen); break; case ERR_TYPE_CHAR_LEN: /* it's a (char *) string with an explicit size_t size */ sptr = va_arg(va, char *); slen = va_arg(va, size_t); /* count the string space */ siz += slen + 1; /* store it */ if (exc != 0) { param->val_.charval_ = err_store_str(strspace, sptr, slen); param->type_ = ERR_TYPE_CHAR; } break; case ERR_TYPE_FUNCSET: /* * It's a char* string with a function set ID. These are not * stored in the parameters, but go in the funcset_ slot in the * exception object. */ sptr = va_arg(va, char *); siz += strlen(sptr) + 1; if (exc != 0) exc->funcset_ = err_store_str(strspace, sptr, strlen(sptr)); break; case ERR_TYPE_METACLASS: /* * It's a char* string with a metaclass ID. These are not * stored in the parameters, but go in the metaclass_ slot in * the exception object. */ sptr = va_arg(va, char *); siz += strlen(sptr) + 1; if (exc != 0) exc->metaclass_ = err_store_str(strspace, sptr, strlen(sptr)); break; case ERR_TYPE_VERSION_FLAG: /* * This parameter is a flag indicating that the error is due to * an out-of-date interpreter build. This has no parameter * data; we simply set the flag in the exception to indicate * the version error type. */ if (exc != 0) exc->version_flag_ = TRUE; break; } } /* * if we have an exception, throw it; if not, we're doing a dry run to * compute the exception object size, so simply return the computed * size */ if (exc != 0) { /* throw the exception object that we just populated */ err_throw_current(); VMERR_AFTER_ERR_THROW(return 0;) } else { /* we're only computing the size on this pass */ return siz; } } /* ------------------------------------------------------------------------ */ #ifdef MICROSOFT /* * Microsoft Visual C++ optimizer workaround - not applicable to other * systems. * * For MSVC, we need to turn off warning 4702 ("unreachable code"). MSVC * is too clever by half for our implementation here of err_throw_a(), and * the only recourse (empirically) seems to be to turn off this warning for * the duration of this function. * * The issue is that err_throw_a() makes a call to err_throw_v() within a * va_start()...va_end() pair. The MSVC optimizer recognizes that * err_throw_v() never returns, so it marks any code following a call to * same as unreachable. But we *have to* include the va_end() call after * the err_throw_v() call. The va_end() is *required* for portability - * some compilers expand va_end() to structural C code (for example, to * insert a close brace to balance an open brace inserted by va_start()). * So we can't omit the va_end() call regardless of its run-time * reachability. * * So, we have code that's both unreachable and required. The only * apparent solution is to disable the unreachable-code warning for the * duration of this function. * * (Conceivably, we could trick the optimizer by moving the err_throw_a() * implementation to a separate module; the optimizer probably couldn't * track the does-not-return status of err_throw_v() across modules. But * that wouldn't be any cleaner in any sense - it would just trick the * compiler in a different way. Better to explicitly turn off the warning; * at least that way the workaround is plain and explicit.) */ #pragma warning(push) #pragma warning(disable: 4702) #endif /* * Throw an exception with parameters */ void err_throw_a(err_id_t error_code, int param_count, ...) { va_list marker; /* do a dry run to determine the exception object size */ va_start(marker, param_count); size_t siz = err_throw_v(error_code, param_count, marker, 0); va_end(marker); /* allocate the new exception object */ CVmException *exc = (CVmException *)t3malloc(siz); /* build the argument list and throw the error */ va_start(marker, param_count); err_throw_v(error_code, param_count, marker, exc); va_end(marker); } /* MSVC - restore previous warning state (see above) */ #ifdef MICROSOFT #pragma warning(pop) #endif /* ------------------------------------------------------------------------ */ /* * Re-throw the current exception. This is valid only from 'catch' * blocks. */ void err_rethrow() { /* throw the error currently on the stack */ err_throw_current(); } /* ------------------------------------------------------------------------ */ /* * Abort the program with a serious, unrecoverable error */ void err_abort(const char *message) { printf("%s\n", message); exit(2); } /* ------------------------------------------------------------------------ */ /* * Retrieve the current exception being handled in the nearest enclosing * err_catch frame. */ CVmException *err_get_cur_exc() { /* * search the error frame stack for a frame in the 'caught exception' * state, starting at the current (innermost) frame */ for (err_frame_t *fr = os_tls_get(err_frame_t *, G_err_frame) ; fr != 0 ; fr = fr->prv_) { /* if this frame is in the 'caught' state, return its exception */ if ((fr->state_ & ERR_STATE_CAUGHT) != 0) return fr->exc_; } /* didn't find an exception */ return 0; } /* ------------------------------------------------------------------------ */ /* * Try loading a message file. Returns zero on success, non-zero if an * error occurred. */ int err_load_message_file(osfildef *fp, const err_msg_t **arr, size_t *arr_size, const err_msg_t *default_arr, size_t default_arr_size) { char buf[128]; size_t i; err_msg_t *msg; /* read the file signature */ if (osfrb(fp, buf, sizeof(VM_MESSAGE_FILE_SIGNATURE)) || memcmp(buf, VM_MESSAGE_FILE_SIGNATURE, sizeof(VM_MESSAGE_FILE_SIGNATURE)) != 0) goto fail; /* delete any previously-loaded message array */ err_delete_message_array(arr, arr_size, default_arr, default_arr_size); /* read the message count */ if (osfrb(fp, buf, 2)) goto fail; /* set the new message count */ *arr_size = osrp2(buf); /* allocate the message array */ *arr = (err_msg_t *)t3malloc(*arr_size * sizeof(err_msg_t)); if (*arr == 0) goto fail; /* clear the memory */ memset((err_msg_t *)*arr, 0, *arr_size * sizeof(err_msg_t)); /* read the individual messages */ for (i = 0, msg = (err_msg_t *)*arr ; i < *arr_size ; ++i, ++msg) { size_t len1, len2; /* read the current message ID and the length of the two messages */ if (osfrb(fp, buf, 8)) goto fail; /* set the message ID */ msg->msgnum = (int)t3rp4u(buf); /* get the short and long mesage lengths */ len1 = osrp2(buf + 4); len2 = osrp2(buf + 6); /* allocate buffers */ msg->short_msgtxt = (char *)t3malloc(len1 + 1); msg->long_msgtxt = (char *)t3malloc(len2 + 1); /* if either one failed, give up */ if (msg->short_msgtxt == 0 || msg->long_msgtxt == 0) goto fail; /* read the two messages */ if (osfrb(fp, (char *)msg->short_msgtxt, len1) || osfrb(fp, (char *)msg->long_msgtxt, len2)) goto fail; /* null-terminate the strings */ *(char *)(msg->short_msgtxt + len1) = '\0'; *(char *)(msg->long_msgtxt + len2) = '\0'; } /* success */ return 0; fail: /* revert back to the built-in array */ err_delete_message_array(arr, arr_size, default_arr, default_arr_size); /* indicate failure */ return 1; } /* * Determine if an external message file has been loaded for the default * VM message set */ int err_is_message_file_loaded() { /* * if we're not using the compiled-in message array, we must have * loaded an external message set */ return vm_messages != &vm_messages_english[0]; } /* * Delete the message array, if one is loaded */ void err_delete_message_array(const err_msg_t **arr, size_t *arr_size, const err_msg_t *default_arr, size_t default_arr_size) { /* * If the message array is valid, and it's not set to point to the * built-in array of English messages, we must have allocated it, so * we must now free it. We don't need to free it if it points to * the English array, because that's static data linked into the VM * executable. */ if (*arr != 0 && *arr != default_arr) { size_t i; err_msg_t *msg; /* delete each message in the array */ for (i = 0, msg = (err_msg_t *)*arr ; i < *arr_size ; ++i, ++msg) { /* delete the strings for this entry */ if (msg->short_msgtxt != 0) t3free((char *)msg->short_msgtxt); if (msg->long_msgtxt != 0) t3free((char *)msg->long_msgtxt); } /* delete the message array itself */ t3free((err_msg_t *)*arr); } /* set the messages array back to the built-in english messages */ *arr = default_arr; *arr_size = default_arr_size; } /* ------------------------------------------------------------------------ */ /* * Find an error message */ const char *err_get_msg(const err_msg_t *msg_array, size_t msg_count, int msgnum, int verbose) { int hi, lo, cur; /* perform a binary search of the message list */ lo = 0; hi = msg_count - 1; while (lo <= hi) { /* split the difference */ cur = lo + (hi - lo)/2; /* is it a match? */ if (msg_array[cur].msgnum == msgnum) { /* it's the one - return the text */ return (verbose ? msg_array[cur].long_msgtxt : msg_array[cur].short_msgtxt); } else if (msgnum > msg_array[cur].msgnum) { /* we need to go higher */ lo = (cur == lo ? cur + 1 : cur); } else { /* we need to go lower */ hi = (cur == hi ? cur - 1 : cur); } } /* no such message */ return 0; } /* ------------------------------------------------------------------------ */ /* * Format a message, allocating a buffer */ char *err_format_msg(const char *msg, const CVmException *exc) { /* * format the message into a null buffer to calculate the length; add a * byte for null termination */ size_t len = err_format_msg(0, 0, msg, exc) + 1; /* allocate a buffer */ char *buf = (char *)t3malloc(len); /* format the message into the new buffer */ err_format_msg(buf, len, msg, exc); /* return the buffer */ return buf; } /* * Format a message with the parameters contained in an exception object. * Suports the following format codes: * * %s - String. Formats an ERR_TYPE_CHAR or ERR_TYPE_TEXTCHAR value, * including the counted-length versions. * * %d, %u, %x - signed/unsigned decimal integer, hexadecimal integer. * Formats an ERR_TYPE_INT value or an ERR_TYPE_ULONG value. * Automatically uses the correct size for the argument. * * %% - Formats as a single percent sign. */ size_t err_format_msg(char *outbuf, size_t outbuflen, const char *msg, const CVmException *exc) { int curarg; const char *p; char *dst; int exc_argc; size_t need = 0; size_t rem = outbuflen; /* get the number of parameters in the exception object */ exc_argc = (exc == 0 ? 0 : exc->get_param_count()); /* start with the first parameter */ curarg = 0; /* start at the beginning of the buffer */ dst = outbuf; /* if there's no message, there's nothing to return */ if (msg == 0) { ++need; if (outbuflen != 0) *dst = '\0'; return need; } /* scan the format string for formatting codes */ for (p = msg ; *p != '\0' ; ++p) { /* if it's a format specifier, translate it */ if (*p == '%') { const char *src; char srcbuf[30]; err_param_type typ; size_t len; int use_strlen; /* * if no more parameters are available, ignore the * formatting code entirely, and leave it in the string as * it is */ if (curarg >= exc_argc) { ++need; if (rem > 1) --rem, *dst++ = *p; continue; } /* get the type of the current parameter */ typ = exc->get_param_type(curarg); /* * presume we'll want to use strlen to get the length of the * source value */ use_strlen = TRUE; /* skip the '%' and determine what follows */ ++p; switch (*p) { case 's': /* get the string value using the appropriate type */ if (typ == ERR_TYPE_TEXTCHAR) src = exc->get_param_text(curarg); else if (typ == ERR_TYPE_CHAR) src = exc->get_param_char(curarg); else if (typ == ERR_TYPE_CHAR_LEN) { /* get the string value and its length */ src = exc->get_param_char_len(curarg, &len); /* * src isn't null terminated, so don't use strlen to * get its length - we already have it from the * parameter data */ use_strlen = FALSE; } else src = "s"; break; case 'd': src = srcbuf; if (typ == ERR_TYPE_INT) sprintf(srcbuf, "%d", exc->get_param_int(curarg)); else if (typ == ERR_TYPE_ULONG) sprintf(srcbuf, "%ld", exc->get_param_ulong(curarg)); else src = "d"; break; case 'u': src = srcbuf; if (typ == ERR_TYPE_INT) sprintf(srcbuf, "%u", exc->get_param_int(curarg)); else if (typ == ERR_TYPE_ULONG) sprintf(srcbuf, "%lu", exc->get_param_ulong(curarg)); else src = "u"; break; case 'x': src = srcbuf; if (typ == ERR_TYPE_INT) sprintf(srcbuf, "%x", exc->get_param_int(curarg)); else if (typ == ERR_TYPE_ULONG) sprintf(srcbuf, "%lx", exc->get_param_ulong(curarg)); else src = "x"; break; case '%': /* add a single percent sign */ src = "%"; break; default: /* invalid format character; leave the whole thing intact */ src = srcbuf; srcbuf[0] = '%'; srcbuf[1] = *p; srcbuf[2] = '\0'; break; } /* get the length, if it's null-terminated */ if (use_strlen) len = strlen(src); /* count the full 'len' in our space needs */ need +=len; /* copy as much as we can */ if (rem > 1) { /* limit it to the remaining space, minus a null byte */ if (len > rem - 1) len = rem - 1; /* copy the value and advance past it in the output buffer */ memcpy(dst, src, len); dst += len; rem -= len; } /* consume the argument */ ++curarg; } else { /* just copy the current character as it is */ ++need; if (rem > 1) --rem, *dst++ = *p; } } /* add the trailing null */ if (rem != 0) *dst++ = '\0'; /* return the space required */ return need; } ������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmerr.h���������������������������������������������������������������������������0000664�0000000�0000000�00000042132�12650170723�0015164�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/vmerr.h,v 1.3 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmerr.h - VM exception handling Function Defines error macros for try/throw exception handling. To throw an exception, use err_throw(exception_object), where the exception_object is an object describing the exception. This object must be allocated with 'new'. Control will immediately transfer to the nearest enclosing err_catch() block. Protected code is defined as shown below. The blocks must occur in the order shown, but the err_catch and err_finally blocks are optional (although it would be pointless to have an err_try that omits both). err_try { // protected code } err_catch(exception_variable) // or just err_catch_disc { // Exception handler - this code is executed only if an // exception occurs in the protected block. // // If an exception is thrown here (with no nested exception // handler to catch it), the err_finally block will be executed // and then the exception will be thrown to the enclosing block. } err_finally { // Code that is executed regardless whether exception occurs // or not. If no exception occurs, this code is executed as // soon as the protected block finishes. If an exception // occurs, this code is executed, then the exception is // re-thrown. // // If an exception is thrown here, the finally block will // be aborted and the enclosing error handler will be activated. // Care should be taken to ensure that code within this block // is properly protected against exceptions if necessary. } err_end; err_catch automatically defines the given exception_variable as a local variable of type (CVmException *) in the scope of the err_catch block. The referenced CVmException object is valid *only* within the err_catch block; after the err_catch block exits, the CVmException object will no longer be present. If you don't need to access the CVmException information, use err_catch_disc instead of err_catch - this has the same effect as err_catch, but does not declare a local variable to point to the exception object. To re-throw the exception being handled from an err_catch() block, use err_rethrow(). IMPORTANT: control must NOT be transferred out of an err_try, err_catch, or err_finally block via break, goto, or return. Actual execution of the err_end is required in order to properly unwind the error stack. The easiest way to leave one of these blocks prematurely, if necessary, is with a goto: err_try { // some code if (done_with_this_block) goto done; // more code done: ; } err_catch_disc { // etc... } err_end; Notes Modified 10/20/98 MJRoberts - Creation */ #ifndef VMERR_H #define VMERR_H #include <setjmp.h> #include <stdarg.h> #include "t3std.h" #include "vmerrnum.h" /* ------------------------------------------------------------------------ */ /* * Error Message Definition structure */ struct err_msg_t { /* message number */ int msgnum; /* concise message text */ const char *short_msgtxt; /* verbose message text */ const char *long_msgtxt; #ifdef VMERR_BOOK_MSG const char *book_msgtxt; #endif }; /* VM error message array */ extern const err_msg_t *vm_messages; extern size_t vm_message_count; /* * VM error message array - English version. This version of the * messages is linked directly into the VM; at run time, we can attempt * to replace this version with another language version obtained from * an external file. We link in the English version so that we will * always have a valid set of messages even if the user doesn't have a * message file installed. */ extern const err_msg_t vm_messages_english[]; extern size_t vm_message_count_english; /* external message file signature */ #define VM_MESSAGE_FILE_SIGNATURE "TADS3.Message.0001\n\r\032" /* * load an external message file - returns zero on success, non-zero on * failure */ int err_load_message_file(osfildef *fp, const err_msg_t **arr, size_t *arr_size, const err_msg_t *default_arr, size_t default_arr_size); /* load default message file */ #define err_load_vm_message_file(fp) \ err_load_message_file((fp), &vm_messages, &vm_message_count, \ vm_messages_english, vm_message_count_english) /* * check to see if an external message file has been loaded for the * default VM message set */ int err_is_message_file_loaded(); /* * delete messages previously loaded with err_load_message_file (this is * called automatically by err_terminate, so clients generally will not * need to call this directly) */ void err_delete_message_array(const err_msg_t **arr, size_t *arr_size, const err_msg_t *default_arr, size_t default_arr_size); /* * Search an array of messages for a given message number. The array * must be sorted by message ID. */ const char *err_get_msg(const err_msg_t *msg_array, size_t msg_count, int msgnum, int verbose); /* * Format a message with the parameters contained in an exception object. * Returns the size in bytes of the formatted message. If the output * buffer is null or too small, we'll fill it up as much as possible and * return the actual length required for the full message text. * * Suports the following format codes: * * %s - String. Formats an ERR_TYPE_CHAR, ERR_TYPE_TEXTCHAR, or * ERR_TYPE_TEXTCHAR_LEN value. * * %d, %u, %x - signed/unsigned decimal integer, hexadecimal integer. * Formats an ERR_TYPE_INT value or an ERR_TYPE_ULONG value. Automatically * uses the correct size for the argument. * * %% - Formats as a single percent sign. */ size_t err_format_msg(char *outbuf, size_t outbuflen, const char *msg, const struct CVmException *exc); /* * Format a message, allocating a buffer to store the result. The caller * must free the buffer with t3free(). */ char *err_format_msg(const char *msg, const struct CVmException *exc); /* ------------------------------------------------------------------------ */ /* * exception ID - this identifies an error */ typedef uint err_id_t; /* * Error parameter type codes */ enum err_param_type { /* ---------------- Display Parameters ---------------- */ /* * The following parameter types are for display purposes. These are * substituted into the message string in err_format_msg(). */ /* parameter is a native 'int' value */ ERR_TYPE_INT, /* parameter is a native 'unsigned long' value */ ERR_TYPE_ULONG, /* parameter is a 'textchar_t *' value (null terminated) */ ERR_TYPE_TEXTCHAR, /* parameter is a 'char *' value (null terminated) */ ERR_TYPE_CHAR, /* * parameter is a 'textchar_t *' value followed by a 'size_t' value * giving the number of bytes in the string */ ERR_TYPE_TEXTCHAR_LEN, /* parameter is a 'char *' value with a separate length */ ERR_TYPE_CHAR_LEN, /* ---------------- Non-Display Parameters ---------------- */ /* * The following parameter types are stored in the exception but are * NOT used in formatting the error message. */ /* * a char* value giving the ID/Version string for the metaclass * involved in the error (for example, "metaclass missing" or * "metaclass version not available") */ ERR_TYPE_METACLASS, /* a char* value the ID/Version string for the function set involved */ ERR_TYPE_FUNCSET, /* * void (no parameter): this is a flag indicating that the error is a * VM version error. This type of error can usually be solved by * updating the interpreter to the latest version. */ ERR_TYPE_VERSION_FLAG }; /* * Exception parameter */ struct CVmExcParam { /* type of this parameter */ err_param_type type_; /* value of the parameter */ union { /* as an integer */ int intval_; /* as an unsigned long */ unsigned long ulong_; /* as a text string */ const textchar_t *strval_; /* as a char string */ const char *charval_; /* as char string with separate length counter */ struct { const char *str_; size_t len_; } charlenval_; } val_; }; /* * Exception object */ struct CVmException { /* get the error code */ int get_error_code() const { return error_code_; } /* get the number of parameters */ int get_param_count() const { return param_count_; } /* get the type of the nth parameter */ err_param_type get_param_type(int n) const { return params_[n].type_; } /* get the nth parameter as an integer */ int get_param_int(int n) const { return params_[n].val_.intval_; } /* get the nth parameter as an unsigned long */ unsigned long get_param_ulong(int n) const { return params_[n].val_.ulong_; } /* get the nth parameter as a string */ const textchar_t *get_param_text(int n) const { return params_[n].val_.strval_; } /* get the nth parameter as a char string */ const char *get_param_char(int n) const { return params_[n].val_.charval_; } /* get the nth parameter as a counted-length string */ const char *get_param_char_len(int n, size_t *len) const { /* set the length return */ *len = params_[n].val_.charlenval_.len_; /* return the string pointer */ return params_[n].val_.charlenval_.str_; } /* set a parameter - null-terminated string value */ void set_param_str(int n, const char *str) { params_[n].type_ = ERR_TYPE_CHAR; params_[n].val_.charval_ = str; } /* set a parameter - counted-length string value */ void set_param_str(int n, const char *str, size_t len) { params_[n].type_ = ERR_TYPE_CHAR_LEN; params_[n].val_.charlenval_.str_ = str; params_[n].val_.charlenval_.len_ = len; } /* set a parameter - integer value */ void set_param_int(int n, int val) { params_[n].type_ = ERR_TYPE_INT; params_[n].val_.intval_ = val; } /* the error code */ err_id_t error_code_; /* * Is this a version-related error? If this is true, the error is due * to an out-of-date interpreter, and can usually be solved by * upgrading to the latest version. */ int version_flag_; /* * Some version-related errors are due to component versions, namely * metaclasses or function sets. For errors due to component versions, * we set the version flag AND set the appropriate identifier here to * indicate the required component version. (This is the component * version that's set as a dependency in the byte-code program we're * trying to run.) */ const char *metaclass_; const char *funcset_; /* number of parameters stored in the exception */ int param_count_; /* parameters (actual array size is given by param_cnt_) */ CVmExcParam params_[1]; }; /* ------------------------------------------------------------------------ */ /* * Error states. The states are bit flags. The following combinations are * possible: * * ERR_STATE_TRYING - the initial call to setjmp() in the err_try has just * returned * * ERR_STATE_EXCEPTION - we've just longjmp()'d into the frame from a * throw, and we haven't handled the exception yet * * ERR_STATE_EXCEPTION | ERR_STATE_FINALLY - we've longjmp()'d into the * frame from a throw, and we've entered the err_finally without * encountering an err_catch * * ERR_STATE_CAUGHT - we've longjmp()'d into the frame from a throw, and * we've entered the err_catch clause * * ERR_STATE_CAUGHT | ERR_FINALLY - we've longjmp()'d into the frame from a * throw, gone through the err_catch, and entered the err_finally * * ERR_STATE_RETHROWN - a new throw occurred within our err_catch or * err_finally clause * * ERR_STATE_RETHROWN | ERR_STATE_FINALLY - a new error occurred within our * err_catch or err_finally clause, and we've entered the err_finally */ typedef int err_state_t; /* * Trying - initial state, before any exception has been thrown. Note that * this must have value 0, because we initialize to this state from the * setjmp return going into the error frame, and setjmp always returns 0 on * the initial call. */ const int ERR_STATE_TRYING = 0; /* exception in progress, and has not been caught */ const int ERR_STATE_EXCEPTION = 0x0001; /* exception has been caught */ const int ERR_STATE_CAUGHT = 0x0002; /* error thrown while an err_catch or err_finally is in progress */ const int ERR_STATE_RETHROWN = 0x4000; /* * 'finally' block entered. This is OR'd into the other states when we * first enter the 'finally', so that we'll know not to re-enter the block * if another exception occurs within. */ const int ERR_STATE_FINALLY = 0x8000; /* ------------------------------------------------------------------------ */ /* * Error frame - allocated by err_try. This object is not manipulated * directly by the client; this is handled automatically by the error * macros. */ struct err_frame_t { /* current state */ err_state_t state_; /* enclosing error frame */ err_frame_t *prv_; /* exception being handled in this frame */ CVmException *exc_; /* jmpbuf for this handler */ jmp_buf jmpbuf_; }; /* * The current error frame active in this thread. Each time we enter an * err_try, we set up a new frame on the stack and set this thread-global * to point to the new frame, and we store the old value (the enclosing * frame) within the new frame, forming a stack via a linked list of * frames. When we leave the stack at err_end or via a throw, we pop the * frame stack by setting G_err_frame to the enclosing element. */ extern OS_DECL_TLS(err_frame_t *, G_err_frame); /* * Initialize the global error context. Should be called at program * initialization. 'param_stack_size' is no longer used. */ void err_init(size_t param_stack_size); /* * Delete the global error context. Should be called at program * termination. */ void err_terminate(); /* * Throw an exception. The first form takes no parameters except the * error code; the second form takes a parameter count followed by that * number of parameters. Each parameter requires two arguments: the * first is a type code of type err_param_type, and the second the * value, whose interpretation depends on the type code. */ void err_throw(err_id_t error_code); void err_throw_a(err_id_t error_code, int param_count, ...); /* * Rethrow the current exception. This is valid only in 'catch' blocks. */ void err_rethrow(); /* * Get the current exception being handled in the nearest enclosing * err_catch block. This searches the error frame stack for a frame in the * 'caught' state, and returns the exception object from that frame. This * allows retrieving the current exception being handled even when we enter * a new err_try block within an err_catch handler. */ CVmException *err_get_cur_exc(); /* * Fatal error - abort program */ void err_abort(const char *message); #define err_try \ { \ err_frame_t err_cur__; \ err_cur__.prv_ = os_tls_get(err_frame_t *, G_err_frame); \ os_tls_set(G_err_frame, &err_cur__); \ if ((err_cur__.state_ = \ (err_state_t)setjmp(err_cur__.jmpbuf_)) == ERR_STATE_TRYING) \ { \ #define err_catch(exc) \ } \ if (err_cur__.state_ == ERR_STATE_EXCEPTION) \ { \ CVmException *exc; \ exc = err_cur__.exc_; \ err_cur__.state_ = ERR_STATE_CAUGHT; #define err_catch_disc \ } \ if (err_cur__.state_ == ERR_STATE_EXCEPTION) \ { \ err_cur__.state_ = ERR_STATE_CAUGHT; #define err_finally \ } \ if ((err_cur__.state_ & ERR_STATE_FINALLY) == 0) \ { \ err_cur__.state_ |= ERR_STATE_FINALLY; #define err_end \ } \ os_tls_set(G_err_frame, err_cur__.prv_); \ if (err_cur__.state_ & (ERR_STATE_EXCEPTION | ERR_STATE_RETHROWN)) \ { \ if (os_tls_get(err_frame_t *, G_err_frame)->state_ \ & ERR_STATE_CAUGHT) \ t3free(os_tls_get(err_frame_t *, G_err_frame)->exc_); \ os_tls_get(err_frame_t *, G_err_frame)->exc_ = err_cur__.exc_; \ err_rethrow(); \ } \ if (err_cur__.state_ & ERR_STATE_CAUGHT) \ t3free(err_cur__.exc_); \ } #endif /* VMERR_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmerrmsg.cpp����������������������������������������������������������������������0000664�0000000�0000000�00000107524�12650170723�0016235�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmerrmsg.cpp - T3 VM error message strings Function Defines the message text for VM errors. All error text is isolated into this module for easy replacement in translated versions. Notes Modified 05/13/00 MJRoberts - Creation */ #include <stdarg.h> #include <stdlib.h> #include "t3std.h" #include "vmerr.h" /* ------------------------------------------------------------------------ */ /* * Enable or disable verbose messages. To conserve memory, verbose * messages can be omitted. To omit verbose messages, the platform * makefile should define the preprocessor symbol VMERR_OMIT_VERBOSE. */ #ifdef VMERR_OMIT_VERBOSE # define VMsg(msg) "" #else # define VMsg(msg) msg #endif #ifdef VMERR_BOOK_MSG # define VBook(msg) , msg #else # define VBook(msg) #endif /* * To conserve even more memory, the messages can be omitted entirely. To * disable all compiled-in messages, define VMERR_OMIT_MESSAGES. */ /* ------------------------------------------------------------------------ */ /* * T3 VM Error Messages * * The messages must be sorted by message number, so that we can perform * a binary search to look up a message by number. */ const err_msg_t vm_messages_english[] = { #ifdef VMERR_OMIT_MESSAGES { 0, 0, 0 VBook(0) } #else /* VMERR_OMIT_MESSAGES */ { VMERR_READ_FILE, "error reading file", VMsg("Error reading file. The file might be corrupted or a media error " "might have occurred.") }, { VMERR_WRITE_FILE, "error writing file", VMsg("Error writing file. The media might be full, or another media " "error might have occurred.") }, { VMERR_FILE_NOT_FOUND, "file not found", VMsg("Error opening file. The specified file might not exist, you " "might not have sufficient privileges to open the file, or " "a sharing violation might have occurred.") }, { VMERR_CREATE_FILE, "error creating file", VMsg("Error creating file. You might not have sufficient privileges " "to open the file, or a sharing violation might have occurred.") }, { VMERR_CLOSE_FILE, "error closing file", VMsg("Error closing file. Some or all changes made to the file might " "not have been properly written to the physical disk/media.") }, { VMERR_DELETE_FILE, "error deleting file", VMsg("Error deleting file. This could because you don't have " "sufficient privileges, the file is marked as read-only, another " "program is using the file, or a physical media error occurred.") }, { VMERR_PACK_PARSE, "data packer format string parsing error at character index %d", VMsg("The format string for the data packing or unpacking has a syntax " "error at character index %d.") }, { VMERR_PACK_ARG_MISMATCH, "data packer argument type mismatch at format string index %d", VMsg("Data packer argument type mismatch. The type of the argument " "doesn't match the format code at character index %d in the " "format string.") }, { VMERR_PACK_ARGC_MISMATCH, "wrong number of data packer arguments at format string index %d", VMsg("Wrong number of arguments to the data packer. The number of " "argument values doesn't match the number of elements in the " "format string.") }, { VMERR_NET_FILE_NOIMPL, "this file operation isn't supported for storage server files", VMsg("This file operation isn't supported for storage server files. " "This operation can only be used with local disk files.") }, { VMERR_RENAME_FILE, "error renaming file", VMsg("An error occurred renaming the file. The new name might already " "be used by an existing file, you might not have the necessary " "permissions in the new or old directory locations, or the " "new name might be in an incompatible location, such as on " "a different device or volume." ) }, { VMERR_OBJ_IN_USE, "object ID in use - the image/save file might be corrupted", VMsg("An object ID requested by the image/save file is already in use " "and cannot be allocated to the file. This might indicate that " "the file is corrupted.") }, { VMERR_OUT_OF_MEMORY, "out of memory", VMsg("Out of memory. Try making more memory available by closing " "other applications if possible.") }, { VMERR_NO_MEM_FOR_PAGE, "out of memory allocating pool page", VMsg("Out of memory allocating pool page. Try making more memory " "available by closing other applications.") }, { VMERR_BAD_POOL_PAGE_SIZE, "invalid page size - file is not valid", VMsg("Invalid page size. The file being loaded is not valid.") }, { VMERR_OUT_OF_PROPIDS, "no more property ID's are available", VMsg("Out of property ID's. No more properties can be allocated.") }, { VMERR_CIRCULAR_INIT, "circular initialization dependency in intrinsic class (internal error)", VMsg("Circular initialization dependency detected in intrinsic class. " "This indicates an internal error in the interpreter. Please " "report this error to the interpreter's maintainer.") }, { VMERR_UNKNOWN_METACLASS, "this interpreter version cannot run this program (program requires " "intrinsic class %s, which is not available in this interpreter)", VMsg("This image file requires an intrinsic class with the identifier " "\"%s\", but the class is not available in this interpreter. This " "program cannot be executed with this interpreter.") }, { VMERR_UNKNOWN_FUNC_SET, "this interpreter version cannot run this program (program requires " "intrinsic function set %s, which is not available in this interpreter)", VMsg("This image file requires a function set with the identifier " "\"%s\", but the function set is not available in this " "intepreter. This program cannot be executed with this " "interpreter.") }, { VMERR_READ_PAST_IMG_END, "reading past end of image file - program might be corrupted", VMsg("Reading past end of image file. The image file might " "be corrupted.") }, { VMERR_NOT_AN_IMAGE_FILE, "this is not an image file (no valid signature found)", VMsg("This file is not a valid image file - the file has an invalid " "signature. The image file might be corrupted.") }, { VMERR_UNKNOWN_IMAGE_BLOCK, "this interpreter version cannot run this program (unknown block type " "in image file)", VMsg("Unknown block type. This image file is either incompatible with " "this version of the interpreter, or has been corrupted.") }, { VMERR_IMAGE_BLOCK_TOO_SMALL, "data block too small", VMsg("A data block in the image file is too small. The image file might " "be corrupted.") }, { VMERR_IMAGE_POOL_BEFORE_DEF, "invalid image file: pool page before pool definition", VMsg("This image file is invalid because it specifies a pool page " "before the pool's definition. The image file might be " "corrupted.") }, { VMERR_IMAGE_POOL_BAD_PAGE, "invalid image file: pool page out of range of definition", VMsg("This image file is invalid because it specifies a pool page " "outside of the range of the pool's definition. The image file " "might be corrupted.") }, { VMERR_IMAGE_BAD_POOL_ID, "invalid image file: invalid pool ID", VMsg("This image file is invalid because it specifies an invalid " "pool ID. The image file might be corrupted.") }, { VMERR_LOAD_BAD_PAGE_IDX, "invalid image file: bad page index", VMsg("This image file is invalid because it specifies an invalid " "page index. The image file might be corrupted.") }, { VMERR_LOAD_UNDEF_PAGE, "loading undefined pool page", VMsg("The program is attempting to load a pool page that is not present " "in the image file. The image file might be corrupted.") }, { VMERR_IMAGE_POOL_REDEF, "invalid image file: pool is defined more than once", VMsg("This image file is invalid because it defines a pool more than " "once. The image file might be corrupted.") }, { VMERR_IMAGE_METADEP_REDEF, "invalid image file: multiple intrinsic class dependency tables found", VMsg("This image file is invalid because it contains multiple " "intrinsic class tables. The image file might be corrupted.") }, { VMERR_IMAGE_NO_METADEP, "invalid image file: no intrinsic class dependency table found", VMsg("This image file is invalid because it contains no intrinsic class " "tables. The image file might be corrupted.") }, { VMERR_IMAGE_FUNCDEP_REDEF, "invalid image file: multiple function set dependency tables found", VMsg("This image file is invalid because it contains multiple " "function set tables. The image file might be corrupted.") }, { VMERR_IMAGE_NO_FUNCDEP, "invalid image file: no function set dependency table found", VMsg("This image file is invalid because it contains no function set " "tables. The image file might be corrupted.") }, { VMERR_IMAGE_ENTRYPT_REDEF, "invalid image file: multiple entrypoints found", VMsg("This image file is invalid because it contains multiple entrypoint " "definitions. The image file might be corrupted.") }, { VMERR_IMAGE_NO_ENTRYPT, "invalid image file: no entrypoint found", VMsg("This image file is invalid because it contains no entrypoint " "specification. The image file might be corrupted.") }, { VMERR_IMAGE_INCOMPAT_VSN, "incompatible image file format version", VMsg("This image file has an incompatible format version. You must " "obtain a newer version of the interpreter to execute this " "program.") }, { VMERR_IMAGE_NO_CODE, "image contains no code", VMsg("This image file contains no executable code. The file might " "be corrupted.") }, { VMERR_IMAGE_INCOMPAT_HDR_FMT, "incomptabile image file format: method header too old", VMsg("This image file has an incompatible method header format. " "This is an older image file version which this interpreter " "does not support.") }, { VMERR_UNAVAIL_INTRINSIC, "unavailable intrinsic function called (index %d in function set \"%s\")", VMsg("Unavailable intrinsic function called (the function is at " "index %d in function set \"%s\"). This function is not available " "in this version of the interpreter and cannot be called when " "running the program with this version. This normally indicates " "either (a) that the \"preinit\" function (or code invoked by " "preinit) called an intrinsic that isn't available during " "this phase, such as an advanced display function; or (b) that " "the program used '&' to refer to a function address, and" "the function isn't available in this interpreter version.") }, { VMERR_UNKNOWN_METACLASS_INTERNAL, "unknown internal intrinsic class ID %x", VMsg("Unknown internal intrinsic class ID %x. This indicates an " "internal error in the interpreter. Please report this error " "to the interpreter's maintainer.") }, { VMERR_XOR_MASK_BAD_IN_MEM, "page mask is not allowed for in-memory image file", VMsg("This image file cannot be loaded from memory because it contains " "masked data. Masked data is not valid with in-memory files. " "This probably indicates that the program file was not installed " "properly; you must convert this program file for in-memory use " "before you can load the program with this version of the " "interpreter.") }, { VMERR_NO_IMAGE_IN_EXE, "no embedded image file found in executable", VMsg("This executable does not contain an embedded image file. The " "application might not be configured properly or might need to " "be rebuilt. Re-install the application or obtain an updated " "version from the application's author.") }, { VMERR_OBJ_SIZE_OVERFLOW, "object size exceeds hardware limits of this computer", VMsg("An object defined in this program file exceeds the hardware limits " "of this computer. This program cannot be executed on this type " "of computer or operating system. Contact the program's author " "for assistance.") }, { VMERR_METACLASS_TOO_OLD, "this interpreter is too old to run this program (program requires " "intrinsic class version %s, interpreter provides version %s)", VMsg("This program needs the intrinsic class \"%s\". This VM " "implementation does not provide a sufficiently recent version " "of this intrinsic class; the latest version available in this " "VM is \"%s\". This program cannot run with this version of the " "VM; you must use a more recent version of the VM to execute this " "program.") }, { VMERR_INVAL_METACLASS_DATA, "invalid intrinsic class data - image file may be corrupted", VMsg("Invalid data were detected in an intrinsic class. This might " "indicate that the image file has been corrupted. You might need " "to re-install the program.") }, { VMERR_BAD_STATIC_NEW, "invalid object - class does not allow loading", VMsg("An object in the image file cannot be loaded because its class " "does not allow creation of objects of the class. This usually " "means that the class is abstract and cannot be instantiated " "as a concrete object.") }, { VMERR_FUNCSET_TOO_OLD, "this interpreter is too old to run this program (program requires " "function set version %s, interpreter provides version %s)", VMsg("This program needs the function set \"%s\". This VM " "implementation does not provide a sufficiently recent version " "of this function set; the latest version available in this VM " "is \"%s\". This program cannot run with this version of the " "VM; you must use a more recent version of the VM to execute " "this program.") }, { VMERR_INVAL_EXPORT_TYPE, "exported symbol \"%s\" is of incorrect datatype", VMsg("The exported symbol \"%s\" is of the incorrect datatype. Check " "the program and the library version.") }, { VMERR_INVAL_IMAGE_MACRO, "invalid data in macro definitions in image file (error code %d)", VMsg("The image file contains invalid data in the macro symbols " "in the debugging records: macro loader error code %d. This " "might indicate that the image file is corrupted.") }, { VMERR_NO_MAINRESTORE, "this program is not capable of restoring a saved state on startup", VMsg("This program is not capable of restoring a saved state on " "startup. To restore the saved state, you must run the program " "normally, then use the appropriate command or operation within " "the running program to restore the saved position file.") }, { VMERR_IMAGE_INCOMPAT_VSN_DBG, "image file is incompatible with debugger - recompile the program", VMsg("This image file was created with a version of the compiler " "that is incompatible with this debugger. Recompile the program " "with the compiler that's bundled with this debugger. If no " "compiler is bundled, check the debugger release notes for " "information on which compiler to use. ") }, { VMERR_NETWORK_SAFETY, "this operation is not allowed by the network safety level settings", VMsg("This operation is not allowed by the current network safety level " "settings. The program is attempting to access network features " "that you have disabled with the network safety level options. " "If you wish to allow this operation, you must restart the " "program with new network safety settings.") }, { VMERR_INVALID_SETPROP, "property cannot be set for object", VMsg("Invalid property change - this property cannot be set for this " "object. This normally indicates that the object is of a type " "that does not allow setting of properties at all, or at least " "of certain properties. For example, a string object does not " "allow setting properties at all.") }, { VMERR_NOT_SAVED_STATE, "file is not a valid saved state file", VMsg("This file is not a valid saved state file. Either the file was " "not created as a saved state file, or its contents have been " "corrupted.") }, { VMERR_WRONG_SAVED_STATE, "saved state is for a different program or a different version of " "this program", VMsg("This file does not contain saved state information for " "this program. The file was saved by another program, or " "by a different version of this program; in either case, it " "cannot be restored with this version of this program.") }, { VMERR_SAVED_META_TOO_LONG, "intrinsic class name in saved state file is too long", VMsg("An intrinsic class name in the saved state file is too long. " "The file might be corrupted, or might have been saved by an " "incompatible version of the interpreter.") }, { VMERR_SAVED_OBJ_ID_INVALID, "invalid object ID in saved state file", VMsg("The saved state file contains an invalid object ID. The saved " "state file might be corrupted.") }, { VMERR_BAD_SAVED_STATE, "saved state file is corrupted (incorrect checksum)", VMsg("The saved state file's checksum is invalid. This usually " "indicates that the file has been corrupted (which could be " "due to a media error, modification by another application, " "or a file transfer that lost or changed data).") }, { VMERR_STORAGE_SERVER_ERR, "storage server error", VMsg("An error occurred accessing the storage server. This could " "be due to a network problem, invalid user credentials, or " "a configuration problem on the game server.") }, { VMERR_DESC_TAB_OVERFLOW, "saved file metadata table exceeds 64k bytes", VMsg("The metadata table for the saved state file is too large. " "This table is limited to 64k bytes in length.") }, { VMERR_BAD_SAVED_META_DATA, "invalid intrinsic class data in saved state file", VMsg("The saved state file contains intrinsic class data that " "is not valid. This usually means that the file was saved " "with an incompatible version of the interpreter program.") }, { VMERR_NO_STR_CONV, "cannot convert value to string", VMsg("This value cannot be converted to a string.") }, { VMERR_CONV_BUF_OVF, "string conversion buffer overflow", VMsg("An internal buffer overflow occurred converting this value to " "a string.") }, { VMERR_BAD_TYPE_ADD, "invalid datatypes for addition operator", VMsg("Invalid datatypes for addition operator. The values being added " "cannot be combined in this manner.") }, { VMERR_NUM_VAL_REQD, "numeric value required", VMsg("Invalid value type - a numeric value is required.") }, { VMERR_INT_VAL_REQD, "integer value required", VMsg("Invalid value type - an integer value is required.") }, { VMERR_NO_LOG_CONV, "cannot convert value to logical (true/nil)", VMsg("This value cannot be converted to a logical (true/nil) value.") }, { VMERR_BAD_TYPE_SUB, "invalid datatypes for subtraction operator", VMsg("Invalid datatypes for subtraction operator. The values used " "cannot be combined in this manner.") }, { VMERR_DIVIDE_BY_ZERO, "division by zero", VMsg("Arithmetic error - Division by zero.") }, { VMERR_INVALID_COMPARISON, "invalid comparison", VMsg("Invalid comparison - these values cannot be compared " "to one another.") }, { VMERR_OBJ_VAL_REQD, "object value required", VMsg("An object value is required.") }, { VMERR_PROPPTR_VAL_REQD, "property pointer required", VMsg("A property pointer value is required.") }, { VMERR_LOG_VAL_REQD, "logical value required", VMsg("A logical (true/nil) value is required.") }, { VMERR_FUNCPTR_VAL_REQD, "function pointer required", VMsg("A function pointer value is required.") }, { VMERR_CANNOT_INDEX_TYPE, "invalid index operation - this type of value cannot be indexed", VMsg("This type of value cannot be indexed.") }, { VMERR_INDEX_OUT_OF_RANGE, "index out of range", VMsg("The index value is out of range for the value being indexed (it is " "too low or too high).") }, { VMERR_BAD_METACLASS_INDEX, "invalid intrinsic class index", VMsg("The intrinsic class index is out of range. This probably " "indicates that the image file is corrupted.") }, { VMERR_BAD_DYNAMIC_NEW, "invalid dynamic object creation (intrinsic class does not support NEW)", VMsg("This type of object cannot be dynamically created, because the " "intrinsic class does not support dynamic creation.") }, { VMERR_OBJ_VAL_REQD_SC, "object value required for base class", VMsg("An object value must be specified for the base class of a dynamic " "object creation operation. The superclass value is of a " "non-object type.") }, { VMERR_STRING_VAL_REQD, "string value required", VMsg("A string value is required.") }, { VMERR_LIST_VAL_REQD, "list value required", VMsg("A list value is required.") }, { VMERR_DICT_NO_CONST, "list or string reference found in dictionary (entry \"%s\") - this " "dictionary cannot be saved in the image file", VMsg("A dictionary entry (for the string \"%s\") referred to a string " "or list value for its associated value data. This dictionary " "cannot be stored in the image file, so the image file cannot " "be created. Check dictionary word additions and ensure that " "only objects are added to the dictionary.") }, { VMERR_INVAL_OBJ_TYPE, "invalid object type - cannot convert to required object type", VMsg("An object is not of the correct type. The object specified " "cannot be converted to the required object type.") }, { VMERR_NUM_OVERFLOW, "numeric overflow", VMsg("A numeric calculation overflowed the limits of the datatype.") }, { VMERR_BAD_TYPE_MUL, "invalid datatypes for multiplication operator", VMsg("Invalid datatypes for multiplication operator. The values " "being added cannot be combined in this manner.") }, { VMERR_BAD_TYPE_DIV, "invalid datatypes for division operator", VMsg("Invalid datatypes for division operator. The values being added " "cannot be combined in this manner.") }, { VMERR_BAD_TYPE_NEG, "invalid datatype for arithmetic negation operator", VMsg("Invalid datatype for arithmetic negation operator. The value " "cannot be negated.") }, { VMERR_OUT_OF_RANGE, "value is out of range", VMsg("A value that was outside of the legal range of inputs was " "specified for a calculation.") }, { VMERR_STR_TOO_LONG, "string is too long", VMsg("A string value is limited to 65535 bytes in length. This " "string exceeds the length limit.") }, { VMERR_LIST_TOO_LONG, "list too long", VMsg("A list value is limited to about 13100 elements. This list " "exceeds the limit.") }, { VMERR_TREE_TOO_DEEP_EQ, "maximum equality test/hash recursion depth exceeded", VMsg("This equality comparison or hash calculation is too complex and " "cannot be performed. This usually indicates that a value " "contains circular references, such as a Vector that contains " "a reference to itself, or to another Vector that contains a " "reference to the first one. This type of value cannot be " "compared for equality or used in a LookupTable.") }, { VMERR_NO_INT_CONV, "cannot convert value to integer", VMsg("This value cannot be converted to an integer.") }, { VMERR_BAD_TYPE_MOD, "invalid datatype for modulo operator", VMsg("Invalid datatype for the modulo operator. These values can't be " "combined with this operator.") }, { VMERR_BAD_TYPE_BIT_AND, "invalid datatype for bitwise AND operator", VMsg("Invalid datatype for the bitwise AND operator. These values can't " "be combined with this operator.") }, { VMERR_BAD_TYPE_BIT_OR, "invalid datatype for bitwise OR operator", VMsg("Invalid datatype for the bitwise OR operator. These values can't " "be combined with this operator.") }, { VMERR_BAD_TYPE_XOR, "invalid datatype for XOR operator", VMsg("Invalid datatype for the XOR operator. These values can't " "be combined with this operator.") }, { VMERR_BAD_TYPE_SHL, "invalid datatype for left-shift operator '<<'", VMsg("Invalid datatype for the left-shift operator '<<'. These values " "can't be combined with this operator.") }, { VMERR_BAD_TYPE_ASHR, "invalid datatype for arithmetic right-shift operator '>>'", VMsg("Invalid datatype for the arithmetic right-shift operator '>>'. " "These values can't be combined with this operator.") }, { VMERR_BAD_TYPE_BIT_NOT, "invalid datatype for bitwise NOT operator", VMsg("Invalid datatype for the bitwise NOT operator. These values can't " "be combined with this operator.") }, { VMERR_CODEPTR_VAL_REQD, "code pointer value required", VMsg("Invalid type - code pointer value required. (This probably " "indicates an internal problem in the interpreter.)") }, { VMERR_EXCEPTION_OBJ_REQD, "exception object required, but 'new' did not yield an object", VMsg("The VM tried to construct a new program-defined exception object " "to represent a run-time error that occurred, but 'new' did not " "yield an object. Note that another underlying run-time error " "occurred that triggered the throw in the first place, but " "information on that error is not available now because of the " "problem creating the exception object to represent that error.") }, { VMERR_NO_DOUBLE_CONV, "cannot convert value to native floating point", VMsg("The value cannot be converted to a floating-point type.") }, { VMERR_NO_NUM_CONV, "cannot convert value to a numeric type", VMsg("The value cannot be converted to a numeric type. Only values " "that can be converted to integer or BigNumber can be used in " "this context.") }, { VMERR_BAD_TYPE_LSHR, "invalid datatype for logical right-shift operator '>>>'", VMsg("Invalid datatype for the logical right-shift operator '>>>'. " "These values can't be combined with this operator.") }, { VMERR_WRONG_NUM_OF_ARGS, "wrong number of arguments", VMsg("The wrong number of arguments was passed to a function or method " "in the invocation of the function or method.") }, { VMERR_WRONG_NUM_OF_ARGS_CALLING, "argument mismatch calling %s - function definition is incorrect", VMsg("The number of arguments doesn't match the number expected " "calling %s. Check the function or method and correct the " "number of parameters that it is declared to receive.") }, { VMERR_NIL_DEREF, "nil object reference", VMsg("The value 'nil' was used to reference an object property. Only " "valid object references can be used in property evaluations.") }, { VMERR_MISSING_NAMED_ARG, "missing named argument '%s'", VMsg("The named argument '%s' was expected in a function or method " "call, but it wasn't provided by the caller.") }, { VMERR_BAD_TYPE_CALL, "invalid type for call", VMsg("The value cannot be invoked as a method or function.") }, { VMERR_NIL_SELF, "nil 'self' value is not allowed", VMsg("'self' cannot be nil. The function or method context has " "a nil value for 'self', which is not allowed.") }, { VMERR_CANNOT_CREATE_INST, "cannot create instance of object - object is not a class", VMsg("An instance of this object cannot be created, because this " "object is not a class.") }, { VMERR_ILLEGAL_NEW, "cannot create instance - class does not allow dynamic construction", VMsg("An instance of this class cannot be created, because this class " "does not allow dynamic construction.") }, { VMERR_INVALID_OPCODE, "invalid opcode - possible image file corruption", VMsg("Invalid instruction opcode - the image file might be corrupted.") }, { VMERR_UNHANDLED_EXC, "unhandled exception", VMsg("An exception was thrown but was not caught by the program. " "The interpreter is terminating execution of the program.") }, { VMERR_STACK_OVERFLOW, "stack overflow", VMsg("Stack overflow. This indicates that function or method calls " "were nested too deeply; this might have occurred because of " "unterminated recursion, which can happen when a function or method " "calls itself (either directly or indirectly).") }, { VMERR_BAD_TYPE_BIF, "invalid type for intrinsic function argument", VMsg("An invalid datatype was provided for an intrinsic " "function argument.") }, { VMERR_SAY_IS_NOT_DEFINED, "default output function is not defined", VMsg("The default output function is not defined. Implicit string " "display is not allowed until a default output function " "is specified.") }, { VMERR_BAD_VAL_BIF, "invalid value for intrinsic function argument", VMsg("An invalid value was specified for an intrinsic function argument. " "The value is out of range or is not an allowed value.") }, { VMERR_BREAKPOINT, "breakpoint encountered", VMsg("A breakpoint instruction was encountered, and no debugger is " "active. The compiler might have inserted this breakpoint to " "indicate an invalid or unreachable location in the code, so " "executing this instruction probably indicates an error in the " "program.") }, { VMERR_CALLEXT_NOT_IMPL, "external function calls are not implemented in this version", VMsg("This version of the interpreter does not implement external " "function calls. This program requires an interpreter that " "provides external function call capabilities, so this program " "is not compatible with this interpreter.") }, { VMERR_INVALID_OPCODE_MOD, "invalid opcode modifier - possible image file corruption", VMsg("Invalid instruction opcode modifier - the image file might " "be corrupted.") }, /* * Note - do NOT use the VMsg() macro on this message, since we always * want to have this verbose message available. */ { VMERR_NO_CHARMAP_FILE, "No mapping file available for local character set \"%.32s\"", "[Warning: no mapping file is available for the local character set " "\"%.32s\". The system will use a default ASCII character set " "mapping instead, so accented characters will be displayed without " "their accents.]" }, { VMERR_UNHANDLED_EXC_PARAM, "Unhandled exception: %s", VMsg("Unhandled exception: %s") }, { VMERR_VM_EXC_PARAM, "VM Error: %s", VMsg("VM Error: %s") VBook("This is used as a generic template for VM run-time " "exception messages. The interpreter uses this to " "display unhandled exceptions that terminate the program.") }, { VMERR_VM_EXC_CODE, "VM Error: code %d", VMsg("VM Error: code %d") VBook("This is used as a generic template for VM run-time " "exceptions. The interpreter uses this to report unhandled " "exceptions that terminate the program. When it can't find " "any message for the VM error code, the interpreter simply " "displays the error number using this template.") }, { VMERR_EXC_IN_STATIC_INIT, "Exception in static initializer for %s.%s: %s", VMsg("An exception occurred in the static initializer for " "%s.%s: %s") }, { VMERR_INTCLS_GENERAL_ERROR, "intrinsic class exception: %s", VMsg("Exception in intrinsic class method: %s") }, { VMERR_STACK_OUT_OF_BOUNDS, "stack access is out of bounds", VMsg("The program attempted to access a stack location that isn't " "part of the current expression storage area. This probably " "indicates a problem with the compiler that was used to create " "this program, or a corrupted program file.") }, { VMERR_DBG_ABORT, "'abort' signal", VMsg("'abort' signal") VBook("This exception is used internally by the debugger to " "signal program termination via the debugger UI.") }, { VMERR_DBG_RESTART, "'restart' signal", VMsg("'restart' signal") VBook("This exception is used internally by the debugger to " "signal program restart via the debugger UI.") }, { VMERR_DBG_HALT, "debugger VM halt", VMsg("debugger VM halt") VBook("This exception is used internally by the debugger to " "signal program termination via the debugger UI.") }, { VMERR_DBG_INTERRUPT, "interrupted by user", VMsg("The program was interrupted by a user interrupt key or " "other action.") }, { VMERR_NO_DEBUGGER, "no debugger available", VMsg("An instruction was encountered that requires the debugger, " "but this interpreter version doesn't include debugging " "capaabilities.") }, { VMERR_BAD_FRAME, "invalid frame in debugger local/parameter evaluation", VMsg("An invalid stack frame was specified in a debugger local/parameter " "evaluation. This probably indicates an internal problem in the " "debugger.") }, { VMERR_BAD_SPEC_EVAL, "invalid speculative expression", VMsg("This expression cannot be executed speculatively. (This does not " "indicate a problem; it's merely an internal condition in the " "debugger.)") }, { VMERR_INVAL_DBG_EXPR, "invalid debugger expression", VMsg("This expression cannot be evaluated in the debugger.") }, { VMERR_NO_IMAGE_DBG_INFO, "image file has no debugging information - recompile for debugging", VMsg("The image file has no debugging information. You must recompile " "the source code for this program with debugging information in " "order to run the program under the debugger.") }, { VMERR_BIGNUM_NO_REGS, "out of temporary floating point registers (calculation too complex)", VMsg("The interpreter is out of temporary floating point registers. " "This probably indicates that an excessively complex calculation " "has been attempted.") }, { VMERR_NO_BIGNUM_CONV, "cannot convert value to BigNumber", VMsg("This value cannot be converted to a BigNumber.") } #endif /* VMERR_OMIT_MESSAGES */ }; /* size of the english message array */ size_t vm_message_count_english = sizeof(vm_messages_english)/sizeof(vm_messages_english[0]); /* * the actual message array - we'll initialize this to the * english list that's linked in, but if we find an external message * file, we'll use the external file instead */ const err_msg_t *vm_messages = vm_messages_english; /* message count - initialize to the english message array count */ size_t vm_message_count = sizeof(vm_messages_english)/sizeof(vm_messages_english[0]); /* ------------------------------------------------------------------------ */ /* * we don't need the VMsg() (verbose message) cover macro any more */ #undef VMsg ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmerrnum.h������������������������������������������������������������������������0000664�0000000�0000000�00000035771�12650170723�0015717�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMERRNUM.H,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmerrnum.h - VM error numbers Function Notes VM Error Numbers are in the range 1 to 9999. Modified 10/28/98 MJRoberts - Creation */ #ifndef VMERRNUM_H #define VMERRNUM_H /* ------------------------------------------------------------------------ */ /* * File errors */ /* error reading file */ #define VMERR_READ_FILE 101 /* error writing file */ #define VMERR_WRITE_FILE 102 /* file not found */ #define VMERR_FILE_NOT_FOUND 103 /* error creating file */ #define VMERR_CREATE_FILE 104 /* error closing/committing file */ #define VMERR_CLOSE_FILE 105 /* error deleting file */ #define VMERR_DELETE_FILE 106 /* data packer parsing error at format string index %d */ #define VMERR_PACK_PARSE 107 /* data packer argument mismatch at format string index %d */ #define VMERR_PACK_ARG_MISMATCH 108 /* data packer argument count mismatch at format string index %d */ #define VMERR_PACK_ARGC_MISMATCH 109 /* operation not supported for network server files */ #define VMERR_NET_FILE_NOIMPL 110 /* error renaming file */ #define VMERR_RENAME_FILE 111 /* ------------------------------------------------------------------------ */ /* * Memory manager errors */ /* * requested object ID is already allocated to another object (this * generally indicates that an image file or a saved state file contains * invalid data) */ #define VMERR_OBJ_IN_USE 201 /* out of memory */ #define VMERR_OUT_OF_MEMORY 202 /* out of memory allocating pool page (use a smaller in-memory cache) */ #define VMERR_NO_MEM_FOR_PAGE 203 /* invalid page size - must be a power of two */ #define VMERR_BAD_POOL_PAGE_SIZE 204 /* no more property ID's */ #define VMERR_OUT_OF_PROPIDS 205 /* circular initialization dependency in intrinsic class (internal error) */ #define VMERR_CIRCULAR_INIT 206 /* ------------------------------------------------------------------------ */ /* * Image file errors */ /* * Unsupported metaclass -- the image file requires a metaclass that * this VM implementation does not provide. This may indicate a version * mismatch (a newer version of the VM is required), or that the image * file is designed for a different host application environment. */ #define VMERR_UNKNOWN_METACLASS 301 /* * Unsupported function set "%s" -- the image file requires a function * set that this VM implementation does not provide. This may indicate * a version mismatch (a newer version of the VM is required), or that * the image file is designed for a different host application * environment. */ #define VMERR_UNKNOWN_FUNC_SET 302 /* attempted to read past end of image file */ #define VMERR_READ_PAST_IMG_END 303 /* not an image file (invalid signature) */ #define VMERR_NOT_AN_IMAGE_FILE 304 /* unrecognized block type in image file */ #define VMERR_UNKNOWN_IMAGE_BLOCK 305 /* data block too small (does not contain required data) */ #define VMERR_IMAGE_BLOCK_TOO_SMALL 306 /* ill-formed image file: pool page before pool definition */ #define VMERR_IMAGE_POOL_BEFORE_DEF 307 /* ill-formed image file: pool page out of range of definition */ #define VMERR_IMAGE_POOL_BAD_PAGE 308 /* invalid pool ID in image file */ #define VMERR_IMAGE_BAD_POOL_ID 309 /* attempted to load a pool page at an invalid offset */ #define VMERR_LOAD_BAD_PAGE_IDX 310 /* attempted to load a pool page that hasn't been defined in the image */ #define VMERR_LOAD_UNDEF_PAGE 311 /* ill-formed image file: pool is defined more than once */ #define VMERR_IMAGE_POOL_REDEF 312 /* file contains more than one metaclass dependency table */ #define VMERR_IMAGE_METADEP_REDEF 313 /* file does not contain a metaclass dependency table */ #define VMERR_IMAGE_NO_METADEP 314 /* file contains more than one than one function set dependency table */ #define VMERR_IMAGE_FUNCDEP_REDEF 315 /* file does not contain a function set dependency table */ #define VMERR_IMAGE_NO_FUNCDEP 316 /* file contains multiple entrypoint definitions */ #define VMERR_IMAGE_ENTRYPT_REDEF 317 /* file does not contain an entrypoint definition */ #define VMERR_IMAGE_NO_ENTRYPT 318 /* incompatible image file format */ #define VMERR_IMAGE_INCOMPAT_VSN 319 /* cannot execute - image contains no code */ #define VMERR_IMAGE_NO_CODE 320 /* image file has incompatible format: method header too old */ #define VMERR_IMAGE_INCOMPAT_HDR_FMT 321 /* * Unavailable intrinsic function called (index %d in function set "%s") * -- this indicates that a function was invoked in a function set that * provides only a subset of its functions. This normally can occur * only when running 'preinit' or similar special situations when * intrinsics are resolved on each call rather than during the initial * load. */ #define VMERR_UNAVAIL_INTRINSIC 322 /* unknown internal metaclass ID %d */ #define VMERR_UNKNOWN_METACLASS_INTERNAL 323 /* page mask not allowed for in-memory image file */ #define VMERR_XOR_MASK_BAD_IN_MEM 324 /* no image file found in executable */ #define VMERR_NO_IMAGE_IN_EXE 325 /* object size exceeds hardware limits of this computer */ #define VMERR_OBJ_SIZE_OVERFLOW 326 /* metaclass "%s" version is not available (latest available is "%s") */ #define VMERR_METACLASS_TOO_OLD 327 /* incorrect metaclass data (corrupted image file) */ #define VMERR_INVAL_METACLASS_DATA 328 /* object cannot be created */ #define VMERR_BAD_STATIC_NEW 329 /* function set "%s" version is not available (latest available is "%s") */ #define VMERR_FUNCSET_TOO_OLD 330 /* invalid type for exported symbol %s */ #define VMERR_INVAL_EXPORT_TYPE 331 /* invalid data in image file macro definitions (code %d) */ #define VMERR_INVAL_IMAGE_MACRO 332 /* no "mainRestore" export - cannot restore saved state on startup */ #define VMERR_NO_MAINRESTORE 333 /* incompatible debugger - image file must be recompiled for this debugger */ #define VMERR_IMAGE_INCOMPAT_VSN_DBG 334 /* ------------------------------------------------------------------------ */ /* * Network errors */ /* network safety level error (operation prohibited by safety settings) */ #define VMERR_NETWORK_SAFETY 400 /* ------------------------------------------------------------------------ */ /* * Property-related errors */ /* this property cannot be set for this object */ #define VMERR_INVALID_SETPROP 1001 /* ------------------------------------------------------------------------ */ /* * Saved state file errors */ /* file is not a valid saved state file */ #define VMERR_NOT_SAVED_STATE 1201 /* file was not saved by the same image or image version */ #define VMERR_WRONG_SAVED_STATE 1202 /* metaclass name is too long in saved file */ #define VMERR_SAVED_META_TOO_LONG 1203 /* object ID in saved state is invalid */ #define VMERR_SAVED_OBJ_ID_INVALID 1206 /* saved state file is corrupted (checksum does not match) */ #define VMERR_BAD_SAVED_STATE 1207 /* invalid data in saved metaclass */ #define VMERR_BAD_SAVED_META_DATA 1208 /* storage server error */ #define VMERR_STORAGE_SERVER_ERR 1209 /* save file description table too large (64k format limit) */ #define VMERR_DESC_TAB_OVERFLOW 1210 /* ------------------------------------------------------------------------ */ /* * Data manipulation and conversion errors */ /* cannot convert value to a string */ #define VMERR_NO_STR_CONV 2001 /* conversion buffer overflow */ #define VMERR_CONV_BUF_OVF 2002 /* invalid datatype for "add" operator */ #define VMERR_BAD_TYPE_ADD 2003 /* numeric value required */ #define VMERR_NUM_VAL_REQD 2004 /* integer value required */ #define VMERR_INT_VAL_REQD 2005 /* cannot convert to logical value */ #define VMERR_NO_LOG_CONV 2006 /* invalid datatype for "subtract" operator */ #define VMERR_BAD_TYPE_SUB 2007 /* division by zero */ #define VMERR_DIVIDE_BY_ZERO 2008 /* invalid comparison */ #define VMERR_INVALID_COMPARISON 2009 /* object value required */ #define VMERR_OBJ_VAL_REQD 2010 /* property pointer value required */ #define VMERR_PROPPTR_VAL_REQD 2011 /* logical value required */ #define VMERR_LOG_VAL_REQD 2012 /* function pointer value required */ #define VMERR_FUNCPTR_VAL_REQD 2013 /* indexing operation cannot be applied to this datatype */ #define VMERR_CANNOT_INDEX_TYPE 2014 /* index value out of range */ #define VMERR_INDEX_OUT_OF_RANGE 2015 /* metaclass index out of range (probable image file error) */ #define VMERR_BAD_METACLASS_INDEX 2016 /* invalid dynamic object creation (metaclass does not support NEW) */ #define VMERR_BAD_DYNAMIC_NEW 2017 /* object value required for superclass */ #define VMERR_OBJ_VAL_REQD_SC 2018 /* string value required */ #define VMERR_STRING_VAL_REQD 2019 /* list value required */ #define VMERR_LIST_VAL_REQD 2020 /* list or string reference found in dictionary - can't convert to const */ #define VMERR_DICT_NO_CONST 2021 /* invalid object type */ #define VMERR_INVAL_OBJ_TYPE 2022 /* numeric overflow */ #define VMERR_NUM_OVERFLOW 2023 /* invalid datatype for "multiply" operator */ #define VMERR_BAD_TYPE_MUL 2024 /* invalid datatype for "divide" operator */ #define VMERR_BAD_TYPE_DIV 2025 /* invalid datatype for "negate" operator */ #define VMERR_BAD_TYPE_NEG 2026 /* value out of range */ #define VMERR_OUT_OF_RANGE 2027 /* string too large */ #define VMERR_STR_TOO_LONG 2028 /* list too large */ #define VMERR_LIST_TOO_LONG 2029 /* tree depth too large for equality test/hash calculation */ #define VMERR_TREE_TOO_DEEP_EQ 2030 /* cannot convert value to integer */ #define VMERR_NO_INT_CONV 2031 /* bad type for modulo */ #define VMERR_BAD_TYPE_MOD 2032 /* bad type for bitwise AND */ #define VMERR_BAD_TYPE_BIT_AND 2033 /* bad type for bitwise OR */ #define VMERR_BAD_TYPE_BIT_OR 2034 /* bad type for XOR */ #define VMERR_BAD_TYPE_XOR 2035 /* bad type for left shift */ #define VMERR_BAD_TYPE_SHL 2036 /* bad type for arithmetic right shift */ #define VMERR_BAD_TYPE_ASHR 2037 /* bad type for bitwise NOT */ #define VMERR_BAD_TYPE_BIT_NOT 2038 /* code pointer value required */ #define VMERR_CODEPTR_VAL_REQD 2039 /* * exception object required (the VM threw an error based on an exception * class defined in the bytecode program, but 'new X' didn't yield an * object) */ #define VMERR_EXCEPTION_OBJ_REQD 2040 /* cannot convert value to double */ #define VMERR_NO_DOUBLE_CONV 2041 /* cannot convert value to number */ #define VMERR_NO_NUM_CONV 2042 /* bad type for logical right shift */ #define VMERR_BAD_TYPE_LSHR 2043 /* ------------------------------------------------------------------------ */ /* * Method and function invocation errors */ /* wrong number of arguments */ #define VMERR_WRONG_NUM_OF_ARGS 2201 /* wrong number of arguments calling %s */ #define VMERR_WRONG_NUM_OF_ARGS_CALLING 2202 /* nil object reference */ #define VMERR_NIL_DEREF 2203 /* missing named argument */ #define VMERR_MISSING_NAMED_ARG 2204 /* invalid type for call */ #define VMERR_BAD_TYPE_CALL 2205 /* 'self' cannot be nil */ #define VMERR_NIL_SELF 2206 /* ------------------------------------------------------------------------ */ /* * Object creation errors */ /* * cannot create an instance of the object, because the object is not a * class */ #define VMERR_CANNOT_CREATE_INST 2270 /* class cannot be created */ #define VMERR_ILLEGAL_NEW 2271 /* ------------------------------------------------------------------------ */ /* * General execution errors */ /* invalid opcode */ #define VMERR_INVALID_OPCODE 2301 /* unhandled exception */ #define VMERR_UNHANDLED_EXC 2302 /* stack overflow */ #define VMERR_STACK_OVERFLOW 2303 /* invalid type for built-in function */ #define VMERR_BAD_TYPE_BIF 2304 /* default string output function not defined */ #define VMERR_SAY_IS_NOT_DEFINED 2305 /* invalid value for built-in function (value out of range) */ #define VMERR_BAD_VAL_BIF 2306 /* breakpoint encountered */ #define VMERR_BREAKPOINT 2307 /* external function calls not implemented */ #define VMERR_CALLEXT_NOT_IMPL 2308 /* invalid opcode modifier */ #define VMERR_INVALID_OPCODE_MOD 2309 /* no character mapping file available */ #define VMERR_NO_CHARMAP_FILE 2310 /* unhandled exception: %s */ #define VMERR_UNHANDLED_EXC_PARAM 2311 /* VM Error: %s */ #define VMERR_VM_EXC_PARAM 2312 /* VM Error: code %d */ #define VMERR_VM_EXC_CODE 2313 /* exception executing static initializer for %*s.%*s: %s */ #define VMERR_EXC_IN_STATIC_INIT 2314 /* * intrinsic class error (this is used as a fallback when an intrinsic * class wants to throw an imported exception class, but the import * doesn't exist; this doesn't provide any real information, but if the * program doesn't export the required class then it's obviously not set * up to receive any information anyway) * * this exception takes a string describing the error */ #define VMERR_INTCLS_GENERAL_ERROR 2315 /* stack access out of bounds */ #define VMERR_STACK_OUT_OF_BOUNDS 2316 /* ------------------------------------------------------------------------ */ /* * Debugger interface errors. These are errors that the debugger throws * in response to the corresponding UI commands, to allow the user some * rough interactive control over execution in the debugger environment. */ /* abort - abort current command */ #define VMERR_DBG_ABORT 2391 /* restart - restart program execution */ #define VMERR_DBG_RESTART 2392 /* halt - terminate the program */ #define VMERR_DBG_HALT 2394 /* interrupt - user interrupted current operation (Ctrl+Break, etc) */ #define VMERR_DBG_INTERRUPT 2395 /* debugger not available */ #define VMERR_NO_DEBUGGER 2396 /* ------------------------------------------------------------------------ */ /* * Debugger-related errors */ /* invalid frame (in debug local/parameter operation) */ #define VMERR_BAD_FRAME 2500 /* invalid speculative evaluation */ #define VMERR_BAD_SPEC_EVAL 2501 /* invalid in debugger expression */ #define VMERR_INVAL_DBG_EXPR 2502 /* image file has no debugging information (must be recompiled) */ #define VMERR_NO_IMAGE_DBG_INFO 2503 /* ------------------------------------------------------------------------ */ /* * BigNumber package errors */ /* out of temporary registers - BigNumber calculation too complex */ #define VMERR_BIGNUM_NO_REGS 2600 /* value can't be converted to BigNumber */ #define VMERR_NO_BIGNUM_CONV 2601 #endif /* VMERRNUM_H */ �������qtads-2.1.7/tads3/vmfile.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000002371�12650170723�0015647�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMFILE.CPP,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmfile.cpp - VM file implementation Function Notes Modified 10/28/98 MJRoberts - Creation */ #include "t3std.h" #include "vmfile.h" #include "vmerr.h" #include "vmdatasrc.h" /* * delete the file object */ CVmFile::~CVmFile() { /* if we still have an underlying OS file, close it */ if (fp_ != 0) osfcls(fp_); } /* * open for reading */ void CVmFile::open_read(const char *fname, os_filetype_t typ) { /* try opening the underlying OS file for binary reading */ fp_ = osfoprb(fname, typ); /* if that failed, throw an error */ if (fp_ == 0) err_throw(VMERR_FILE_NOT_FOUND); } /* * open for writing */ void CVmFile::open_write(const char *fname, os_filetype_t typ) { /* try opening the underlying OS file for binary writing */ fp_ = osfopwb(fname, typ); /* if that failed, throw an error */ if (fp_ == 0) err_throw(VMERR_CREATE_FILE); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmfile.h��������������������������������������������������������������������������0000664�0000000�0000000�00000075617�12650170723�0015331�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMFILE.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmfile.h - VM external file interface Function Notes Modified 10/28/98 MJRoberts - Creation */ #ifndef VMFILE_H #define VMFILE_H #include "t3std.h" #include "vmerr.h" #include "osifcnet.h" /* ------------------------------------------------------------------------ */ /* * VM external file interface. The VM uses this interface for * manipulating files that contain program images and saved state. */ class CVmFile { public: /* create a new file object */ CVmFile() { /* no file yet */ fp_ = 0; /* presume the base seek position is at the start of the file */ seek_base_ = 0; } CVmFile(osfildef *fp, long seek_base) { fp_ = fp; seek_base_ = seek_base; } /* * Duplicate the file handle, a la stdio freopen(). 'mode' is a * simplified fopen()-style mode string, with the same syntax as used * in osfdup(). */ CVmFile *dup(const char *mode) { /* duplicate our file handle */ osfildef *fpdup = osfdup(fp_, mode); if (fpdup == 0) return 0; /* create a new CVmFile wrapper for the new handle */ return new CVmFile(fpdup, seek_base_); } /* delete the file */ ~CVmFile(); /* * Set the file to use an existing underlying OS file handle. The * seek_base value gives the byte offset within the OS file handle * of our virtual byte stream; this is useful if the file object is * handling a byte stream embedded within a larger OS file (such as * a resource within a file containing multiple resources). If * we're simply providing access to the entire underlying file, the * seek_base value should be zero. */ void set_file(osfildef *fp, long seek_base) { /* remember the file handle and seek position */ fp_ = fp; seek_base_ = seek_base; } /* * Detach from the underlying OS file handle. Normally, when we are * deleted, we'll close our underlying OS file handle. If the * caller has a separate reference to the OS file handle and wants * to keep the handle open after deleting us, the caller should use * this function to detach us from the OS file handle before * deleting us. * * After calling this routine, no operations can be performed on the * underlying file through this object, since we will have forgotten * the file handle. */ void detach_file() { /* forget our file handle */ fp_ = 0; } /* * Open an existing file for reading. Throws an error if the file * does not exist. */ void open_read(const char *fname, os_filetype_t typ); /* * Create a file for writing, replacing any existing file. Throws * an error if the file cannot be created. */ void open_write(const char *fname, os_filetype_t typ); /* close the underlying file */ void close() { osfcls(fp_); fp_ = 0; } /* flush buffers */ void flush() { if (osfflush(fp_)) err_throw(VMERR_WRITE_FILE); } /* * Read various types from the file. These routines throw an error * if the data cannot be read. */ uchar read_byte() { char b; read_bytes(&b, 1); return (uchar)b; } uint read_uint2() { char b[2]; read_bytes(b, 2); return osrp2(b); } int read_int2() { char b[2]; read_bytes(b, 2); return osrp2s(b); } long read_int4() { char b[4]; read_bytes(b, 4); return osrp4s(b); } ulong read_uint4() { char b[4]; read_bytes(b, 4); return t3rp4u(b); } /* read bytes - throws an error if all of the bytes cannot be read */ void read_bytes(char *buf, size_t buflen) { if (buflen != 0 && osfrb(fp_, buf, buflen)) err_throw(VMERR_READ_FILE); } /* * Read bytes, with no error on partial read. If the full request * can't be satisfied (because of end of file, partial read on pipe or * socket, etc), reads as many as are available and returns the count. * If no bytes are available, returns zero. This routine can still * throw an error if some other error occurs besides end-of-file (file * not open, etc). */ size_t read_nbytes(char *buf, size_t buflen) { return buflen == 0 ? 0 : osfrbc(fp_, buf, buflen); } /* read a line (fgets semantics) */ char *read_line(char *buf, size_t buflen) { return osfgets(buf, buflen, fp_); } /* * read a string with a one-byte length prefix, adding a null * terminator; returns the length */ size_t read_str_byte_prefix(char *buf, size_t buflen) { size_t len = read_byte(); if (len+1 > buflen) err_throw(VMERR_READ_FILE); read_bytes(buf, len); buf[len] = '\0'; return len; } /* * read a string with a two-byte length prefix, adding a null * terminator; returns the length */ size_t read_str_short_prefix(char *buf, size_t buflen) { size_t len = read_uint2(); if (len+1 > buflen) err_throw(VMERR_READ_FILE); read_bytes(buf, len); buf[len] = '\0'; return len; } /* * Read a string with a two-byte length prefix, allocating the buffer * with lib_alloc_str() (the caller frees it with lib_free_str()). * We'll null-terminate the result, and optionally return the length in * '*lenp'. 'lenp' call be null if the length isn't needed. */ char *read_str_short_prefix(size_t *lenp) { size_t len = read_uint2(); char *buf = lib_alloc_str(len); read_bytes(buf, len); buf[len] = '\0'; if (lenp != 0) *lenp = len; return buf; } /* * Write various types to the file. These routines throw an error * if the data cannot be written. */ void write_byte(char c) { char b[1]; b[0] = c; write_bytes(b, 1); } void write_int2(int v) { char b[2]; oswp2s(b, v); write_bytes(b, 2); } void write_uint2(uint v) { char b[2]; oswp2(b, v); write_bytes(b, 2); } void write_int4(int v) { char b[4]; oswp4s(b, v); write_bytes(b, 4); } void write_uint4(uint v) { char b[4]; oswp4(b, v); write_bytes(b, 4); } /* * Write a string with a single-byte length prefix. Throws an error if * the string is too long. */ void write_str_byte_prefix(const char *str, size_t len) { /* make sure it fits */ if (len > 255) err_throw(VMERR_WRITE_FILE); /* write the prefix and the string */ write_byte((char)len); write_bytes(str, len); } void write_str_byte_prefix(const char *str) { write_str_byte_prefix(str, strlen(str)); } /* write a string with a two-byte length prefix */ void write_str_short_prefix(const char *str, size_t len) { /* make sure it fits */ if (len > 65535) err_throw(VMERR_WRITE_FILE); /* write the prefix and string */ write_uint2((int)len); write_bytes(str, len); } void write_str_short_prefix(const char *str) { write_str_short_prefix(str, strlen(str)); } /* write bytes - throws an error if the bytes cannot be written */ void write_bytes(const char *buf, size_t buflen) { if (buflen != 0 && osfwb(fp_, buf, buflen)) err_throw(VMERR_WRITE_FILE); } /* get the current seek position */ long get_pos() const { return osfpos(fp_) - seek_base_; } /* seek to a new position */ void set_pos(long seekpos) { /* seek relative to the base seek position */ osfseek(fp_, seekpos + seek_base_, OSFSK_SET); } /* seek to a position relative to the end of the file */ void set_pos_from_eof(long pos) { osfseek(fp_, pos, OSFSK_END); } /* seek to a position relative to the current file position */ void set_pos_from_cur(long pos) { osfseek(fp_, pos, OSFSK_CUR); } protected: /* our underlying OS file handle */ osfildef *fp_; /* * Base seek position - this is useful for virtual byte streams that * are embedded in larger files (resource files, for example). * set_pos() is always relative to this position. Note that * set_pos_from_eof() is *not* relative to this position, so * embedded byte streams must not use set_pos_from_eof() unless the * embedded stream ends at the end of the enclosing file. */ long seek_base_; }; /* ------------------------------------------------------------------------ */ /* * Generic stream interface. This is a higher-level type of file interface * than CVmFile: this interface can be implemented on different kinds of * underlying storage formats to provide generic stream access independent * of the actual storage implementation. * * We provide default implementations of the type readers in terms of the * low-level byte reader virtual method, which must be implemented by each * concrete subclass. However, all of the type readers are virtual, so * subclasses can override these if they can be implemented more * efficiently on the underlying stream (for example, some implementations * might have ready access to the data in a buffer already, in which case * it might be faster to avoid the extra buffer copy of the default * implementations of the type readers). */ class CVmStream { public: virtual ~CVmStream() { } /* clone the stream - see CVmDataSource::clone() for details */ virtual CVmStream *clone(VMG_ const char *mode) = 0; /* read/write a byte */ virtual uchar read_byte() { char b; read_bytes(&b, 1); return (uchar)b; } virtual void write_byte(uchar b) { write_bytes((char *)&b, 1); } /* * read various integer types (signed 2-byte, unsigned 2-byte, signed * 4-byte, unsigned 4-byte) */ virtual int read_int2() { char b[2]; read_bytes(b, 2); return osrp2s(b); } virtual uint read_uint2() { char b[2]; read_bytes(b, 2); return osrp2(b); } virtual long read_int4() { char b[4]; read_bytes(b, 4); return osrp4s(b); } virtual ulong read_uint4() { char b[4]; read_bytes(b, 4); return t3rp4u(b); } /* write an integer value */ virtual void write_byte(char c) { char b[1]; b[0] = c; write_bytes(b, 1); } virtual void write_int2(int i) { char b[2]; oswp2s(b, i); write_bytes(b, 2); } virtual void write_uint2(uint i) { char b[2]; oswp2(b, i); write_bytes(b, 2); } virtual void write_int4(long l) { char b[4]; oswp4s(b, l); write_bytes(b, 4); } virtual void write_uint4(ulong l) { char b[4]; oswp4(b, l); write_bytes(b, 4); } /* * Read bytes - this must be provided by the implementation. Throws an * error if the full number of bytes requested cannot be read. */ virtual void read_bytes(char *buf, size_t len) = 0; /* * Read bytes - reads up to the amount requested, but treats a partial * read (due to end of file, partial read on a pipe or socket, etc) as * a success. Returns the actual number of bytes read, which may be * less than the requested number, and can even be zero if the file is * already at EOF. */ virtual size_t read_nbytes(char *buf, size_t len) = 0; /* read a line - fgets semantics */ virtual char *read_line(char *buf, size_t len) = 0; /* write bytes */ virtual void write_bytes(const char *buf, size_t len) = 0; /* get the current seek offset */ virtual long get_seek_pos() const = 0; /* * set the seek offset - this is only valid with offsets previously * obtained with get_seek_pos() */ virtual void set_seek_pos(long pos) = 0; /* get the length of the stream's contents */ virtual long get_len() = 0; }; /* * Implementation of the generic stream with an underlying CVmFile object */ class CVmFileStream: public CVmStream { public: /* create based on the underlying file object */ CVmFileStream(CVmFile *fp) { fp_ = fp; } /* clone the stream */ virtual CVmStream *clone(VMG_ const char *mode) { /* duplicate the file handle */ CVmFile *fpdup = fp_->dup(mode); if (fpdup == 0) return 0; /* return a new file stream wrapper for the duplicate handle */ return new CVmFileStream(fpdup); } /* read bytes */ void read_bytes(char *buf, size_t len) { fp_->read_bytes(buf, len); } /* read bytes with success on partial read */ size_t read_nbytes(char *buf, size_t len) { return fp_->read_nbytes(buf, len); } /* read a line */ char *read_line(char *buf, size_t len) { return fp_->read_line(buf, len); } /* write bytes */ void write_bytes(const char *buf, size_t len) { fp_->write_bytes(buf, len); } /* get/set the seek position */ long get_seek_pos() const { return fp_->get_pos(); } void set_seek_pos(long pos) { fp_->set_pos(pos); } /* get the length of the stream */ long get_len() { /* remember the current seek position */ long orig = fp_->get_pos(); /* seek to the end of the file */ fp_->set_pos_from_eof(0); /* the seek position is the file length */ long len = fp_->get_pos(); /* seek back to where we started */ fp_->set_pos(orig); /* return the stream length */ return len; } private: /* our underlying file stream */ CVmFile *fp_; }; /* * Reference-counted long int value (service class for CVmCountingStream) */ class CVmRefCntLong: public CVmRefCntObj { public: CVmRefCntLong() { val = 0; } long val; }; /* * Counting stream; write-only. This is a stream for measuring the size of * data for writing. We don't actually write anything; we just count the * bytes written. */ class CVmCountingStream: public CVmStream { public: CVmCountingStream() { pos_ = 0; len_ = new CVmRefCntLong(); } CVmCountingStream(CVmRefCntLong *l) { l->add_ref(); len_ = l; } ~CVmCountingStream() { len_->release_ref(); } CVmStream *clone(VMG_ const char * /*mode*/) { return new CVmCountingStream(len_); } void read_bytes(char *buf, size_t len) { err_throw(VMERR_READ_FILE); } size_t read_nbytes(char *buf, size_t len) { err_throw(VMERR_READ_FILE); AFTER_ERR_THROW(return 0); } char *read_line(char *buf, size_t len) { err_throw(VMERR_READ_FILE); AFTER_ERR_THROW(return 0); } void write_bytes(const char *buf, size_t len) { pos_ += len; if (pos_ > len_->val) len_->val = pos_; } long get_seek_pos() const { return pos_; } void set_seek_pos(long pos) { pos_ = (pos < 0 ? 0 : pos > len_->val ? len_->val : pos); } long get_len() { return len_->val; } protected: /* current seek position */ long pos_; /* number of bytes written */ CVmRefCntLong *len_; }; /* * Implementation of the generic stream reader for reading or writing a * memory buffer. This uses a fixed, pre-allocated buffer provided by the * caller, so writing is limited to the space in the buffer. (For a more * flexible memory stream writer that allocates space as needed, use * CVmExpandableMemoryStream.) */ class CVmMemoryStream: public CVmStream { public: /* create given the underlying memory buffer */ CVmMemoryStream(char *buf, size_t len) { /* remember the buffer */ buf_ = buf; buflen_ = len; /* start at the start of the buffer */ p_ = buf; } /* clone */ virtual CVmStream *clone(VMG_ const char * /*mode*/) { return new CVmMemoryStream(buf_, buflen_); } /* read bytes */ void read_bytes(char *buf, size_t len) { /* if we don't have enough bytes left, throw an error */ if (len > (size_t)((buf_ + buflen_) - p_)) err_throw(VMERR_READ_FILE); /* copy the data */ memcpy(buf, p_, len); /* advance past the data we just read */ p_ += len; } /* read bytes, partial read OK */ size_t read_nbytes(char *buf, size_t len) { /* limit the request to the number of bytes available */ long avail = (buf_ + buflen_) - p_; if ((long)len > avail) len = (size_t)avail; /* copy the data */ if (len != 0) memcpy(buf, p_, len); /* advance past the data we just read */ p_ += len; /* return the number of bytes read */ return len; } /* read a line */ char *read_line(char *buf, size_t len) { /* if already at EOF, or there's no buffer, return null */ if (p_ >= buf_ + buflen_ || buf == 0 || len == 0) return 0; /* read bytes until we fill the buffer or reach '\n' or EOF */ char *dst; size_t rem; for (dst = buf, rem = len ; rem > 1 && p_ < buf_ + buflen_ ; --rem) { /* get this character */ char c = *p_++; /* check for newlines */ if (c == '\n' || c == '\r') { /* write CR and LF as simply CR */ *dst++ = '\n'; /* if we have a CR-LF or LF-CR sequence, skip the pair */ if (p_ < buf_ + buflen_ && ((c == '\n' && *p_ == '\r') || (c == '\r' && *p_ == '\n'))) p_ += 1; /* done */ break; } /* copy this character */ *dst++ = c; } /* add the trailing null and return the buffer */ *dst = '\0'; return buf; } /* write bytes */ void write_bytes(const char *buf, size_t len) { /* if we don't have space, throw an error */ if (len > (size_t)((buf_ + buflen_) - p_)) err_throw(VMERR_WRITE_FILE); /* copy the data */ memcpy(p_, buf, len); /* advance past the data */ p_ += len; } /* get the position - return an offset from the start of our buffer */ long get_seek_pos() const { return p_ - buf_; } /* set the position, as an offset from the start of our buffer */ void set_seek_pos(long pos) { /* limit it to the available space */ if (pos < 0) pos = 0; else if (pos > (long)buflen_) pos = buflen_; /* set the position */ p_ = buf_ + pos; } /* get the stream length */ long get_len() { return buflen_; } /* set the stream length */ void set_len(size_t len) { buflen_ = len; } protected: /* our buffer */ char *buf_; /* size of our buffer */ size_t buflen_; /* current read/write pointer */ char *p_; }; /* * reference-counted buffer */ class CVmRefCntBuf: public CVmRefCntObj { public: CVmRefCntBuf(size_t len) { buf_ = lib_alloc_str(len); } CVmRefCntBuf(const char *str, size_t len) { buf_ = lib_copy_str(str, len); } char *buf_; protected: ~CVmRefCntBuf() { lib_free_str(buf_); } }; /* * Memory stream, using a private allocated buffer */ class CVmPrivateMemoryStream: public CVmMemoryStream { public: CVmPrivateMemoryStream(size_t len) : CVmMemoryStream(0, len) { bufobj_ = new CVmRefCntBuf(len); buf_ = bufobj_->buf_; } CVmPrivateMemoryStream(const char *str, size_t len) : CVmMemoryStream(0, len) { bufobj_ = new CVmRefCntBuf(str, len); buf_ = bufobj_->buf_; } CVmPrivateMemoryStream(CVmRefCntBuf *bufobj, size_t len) : CVmMemoryStream(bufobj->buf_, len) { bufobj->add_ref(); bufobj_ = bufobj; } CVmStream *clone(VMG_ const char * /*mode*/) { return new CVmPrivateMemoryStream(bufobj_, buflen_); } ~CVmPrivateMemoryStream() { bufobj_->release_ref(); } protected: CVmRefCntBuf *bufobj_; }; /* * Read-only memory stream */ class CVmReadOnlyMemoryStream: public CVmMemoryStream { public: CVmReadOnlyMemoryStream(const char *buf, size_t len) : CVmMemoryStream((char *)buf, len) { } /* disallow writing */ void write_bytes(const char *, size_t) { err_throw(VMERR_WRITE_FILE); } }; /* expandable memory stream block */ struct CVmExpandableMemoryStreamBlock { CVmExpandableMemoryStreamBlock(long ofs) { this->ofs = ofs; nxt = 0; } /* block length */ static const int BlockLen = 4096; /* next block in the list */ CVmExpandableMemoryStreamBlock *nxt; /* byte offset in the overall stream of the start of this block */ long ofs; /* block buffer */ char buf[BlockLen]; }; /* expandable memory stream block list */ class CVmExpandableMemoryStreamList: public CVmRefCntObj { public: static const int BlockLen = CVmExpandableMemoryStreamBlock::BlockLen; CVmExpandableMemoryStreamList(long init_len) { /* no blocks yet */ first_block_ = last_block_ = 0; /* always allocate at least the initial block */ add_block(); init_len -= BlockLen; /* set the seek position to the start of the first block */ cur_block_ = first_block_; cur_block_ofs_ = 0; /* add more blocks until we've satisfied the length request */ for ( ; init_len > 0 ; init_len -= BlockLen) add_block(); /* the file is currently empty */ len_ = 0; } /* read bytes */ void read_bytes(char *buf, size_t len) { /* * if the request would take us past the current content length, * it's an error */ if (cur_block_->ofs + cur_block_ofs_ + (long)len > len_) err_throw(VMERR_READ_FILE); /* keep going until we satisfy the request */ while (len != 0) { /* figure how much we can read from the current block */ size_t rem = BlockLen - cur_block_ofs_; size_t cur = (len < rem ? len : rem); /* read that much */ if (cur != 0) { memcpy(buf, cur_block_->buf + cur_block_ofs_, cur); cur_block_ofs_ += cur; len -= cur; buf += cur; } /* if there's more to read, advance to the next block */ if (len != 0) { /* if this is the last block, we can't satisfy the request */ if (cur_block_->nxt == 0) err_throw(VMERR_READ_FILE); /* advance to the start of the next block */ cur_block_ = cur_block_->nxt; cur_block_ofs_ = 0; } } } /* read bytes, partial read OK */ size_t read_nbytes(char *buf, size_t len) { /* figure the number of bytes available */ long avail = (long)len_ - (cur_block_->ofs + cur_block_ofs_); /* limit the read to the available bytes */ if ((long)len > avail) len = (size_t)avail; /* if that leaves us with any bytes to read, do the copy */ if (len != 0) read_bytes(buf, len); /* return the number of bytes transfered */ return len; } /* read a line */ char *read_line(char *buf, size_t len) { /* if already at EOF, or there's no buffer, return null */ if (get_seek_pos() >= len_) return 0; /* read bytes until we fill the buffer or reach '\n' or EOF */ char *dst; size_t rem; for (dst = buf, rem = len ; rem > 1 ; --rem) { /* get this character - stop at EOF */ char c; if (!mread_byte(&c, TRUE)) break; /* check for newlines */ if (c == '\n' || c == '\r') { /* write CR and LF as simply CR */ *dst++ = '\n'; /* if we have a CR-LF or LF-CR sequence, skip the pair */ char c2; if (mread_byte(&c2, FALSE) && ((c == '\n' && c2 == '\r') || (c == '\r' && c2 == '\n'))) { /* skip the second character of the pair */ mread_byte(&c2, TRUE); } /* done */ break; } /* copy this character */ *dst++ = c; } /* add the trailing null and return the buffer */ *dst = '\0'; return buf; } /* write bytes */ void write_bytes(const char *buf, size_t len) { /* keep going until we satisfy the request */ while (len != 0) { /* figure how much we can write to the current block */ size_t rem = BlockLen - cur_block_ofs_; size_t cur = (len < rem ? len : rem); /* write that much */ if (cur != 0) { memcpy(cur_block_->buf + cur_block_ofs_, buf, cur); cur_block_ofs_ += cur; len -= cur; buf += cur; } /* if there's more to write, advance to the next block */ if (len != 0) { /* if this is the last block, add a new one */ if (cur_block_->nxt == 0) { add_block(); if (cur_block_->nxt == 0) err_throw(VMERR_WRITE_FILE); } /* advance to the next block */ cur_block_ = cur_block_->nxt; cur_block_ofs_ = 0; } } /* * if the seek pointer is past the current content length, we've * written past the old end of the file and thus expanded the file * - note the new length */ if (cur_block_->ofs + cur_block_ofs_ > len_) len_ = cur_block_->ofs + cur_block_ofs_; } /* get the current seek offset */ long get_seek_pos() const { /* * figure the seek position as the current block's base offset plus * the current offset within that block */ return cur_block_->ofs + cur_block_ofs_; } /* set the seek offset */ void set_seek_pos(long pos) { /* limit the position to the file's bounds */ if (pos < 0) { /* at start of file */ pos = 0; } else if (pos >= len_) { /* * At end of file. This is a special case for setting the * block pointer, because we could be positioned at the last * byte of the last block, which we won't find in our scan. * Explicitly set the position here. */ pos = len_; cur_block_ = last_block_; cur_block_ofs_ = pos - last_block_->ofs; /* we've set the block position, so skip the usual search */ return; } /* find the block containing the seek offset */ CVmExpandableMemoryStreamBlock *b; for (b = first_block_ ; b != 0 ; b = b->nxt) { /* if the offset is within this block, we're done */ if (pos >= b->ofs && pos < b->ofs + BlockLen) { /* set the current pointers */ cur_block_ = b; cur_block_ofs_ = pos - b->ofs; /* stop scanning */ break; } } } /* get the length of the stream's contents */ long get_len() { return len_; } protected: ~CVmExpandableMemoryStreamList() { /* delete our block list */ CVmExpandableMemoryStreamBlock *cur, *nxt; for (cur = first_block_ ; cur != 0 ; cur = nxt) { nxt = cur->nxt; delete cur; } } /* add a block to the end of the list */ void add_block() { long ofs = last_block_ == 0 ? 0 : last_block_->ofs + BlockLen; CVmExpandableMemoryStreamBlock *b = new CVmExpandableMemoryStreamBlock(ofs); if (last_block_ != 0) last_block_->nxt = b; else first_block_ = b; last_block_ = b; } /* * read one byte, incrementing the read pointer if desired; returns * true on success, false on EOF */ int mread_byte(char *c, int inc) { /* check for EOF */ if (cur_block_->ofs + cur_block_ofs_ >= len_) return FALSE; /* if we're out of bytes in this block, move to the next block */ if (cur_block_ofs_ >= BlockLen) { cur_block_ = cur_block_->nxt; cur_block_ofs_ = 0; } /* set the return byte */ *c = cur_block_->buf[cur_block_ofs_]; /* increment the pointer if desired */ if (inc) cur_block_ofs_ += 1; /* success */ return TRUE; } /* current seek block and offset */ CVmExpandableMemoryStreamBlock *cur_block_; long cur_block_ofs_; /* total file length */ long len_; /* head/tail of block list */ CVmExpandableMemoryStreamBlock *first_block_; CVmExpandableMemoryStreamBlock *last_block_; }; /* * Expandable memory stream. This automatically allocates additional * buffer space as needed. */ class CVmExpandableMemoryStream: public CVmStream { public: CVmExpandableMemoryStream(long init_len) { bl = new CVmExpandableMemoryStreamList(init_len); } virtual CVmStream *clone(VMG_ const char * /*mode*/) { return new CVmExpandableMemoryStream(bl); } ~CVmExpandableMemoryStream() { bl->release_ref(); } /* read bytes */ virtual void read_bytes(char *buf, size_t len) { bl->read_bytes(buf, len); } /* read bytes, partial read OK */ virtual size_t read_nbytes(char *buf, size_t len) { return bl->read_nbytes(buf, len); } /* read a line */ virtual char *read_line(char *buf, size_t len) { return bl->read_line(buf, len); } /* write bytes */ virtual void write_bytes(const char *buf, size_t len) { bl->write_bytes(buf, len); } /* get the current seek offset */ virtual long get_seek_pos() const { return bl->get_seek_pos(); } /* set the seek offset */ virtual void set_seek_pos(long pos) { bl->set_seek_pos(pos); } /* get the length of the stream's contents */ virtual long get_len() { return bl->get_len(); } protected: CVmExpandableMemoryStream(CVmExpandableMemoryStreamList *bl) { bl->add_ref(); this->bl = bl; } CVmExpandableMemoryStreamList *bl; }; #endif /* VMFILE_H */ �����������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmfilnam.cpp����������������������������������������������������������������������0000664�0000000�0000000�00000166336�12650170723�0016212�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2012 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmfilnam.cpp - CVmObjFileName object Function Notes Modified 03/03/12 MJRoberts - Creation */ #include <stdlib.h> #include <string.h> #include <assert.h> #include "t3std.h" #include "vmfilnam.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmfilobj.h" #include "vmbif.h" #include "vmmeta.h" #include "vmstack.h" #include "vmrun.h" #include "vmstr.h" #include "utf8.h" #include "charmap.h" #include "vmnetfil.h" #include "vmpredef.h" /* ------------------------------------------------------------------------ */ /* * Allocate an extension structure */ vm_filnam_ext *vm_filnam_ext::alloc_ext( VMG_ CVmObjFileName *self, int32_t sfid, const char *str, size_t len) { /* * Calculate how much space we need - we need space for the name string * plus its length prefix; store the name string with a null terminator * for convenience in calling osifc routines. */ size_t siz = (sizeof(vm_filnam_ext)-1) + (VMB_LEN + len + 1); /* allocate the memory */ vm_filnam_ext *ext = (vm_filnam_ext *)G_mem->get_var_heap()->alloc_mem( siz, self); /* store the special file ID; assume it's valid */ ext->sfid = sfid; ext->sfid_valid = TRUE; /* assume that the file didn't come from a manual user interaction */ ext->from_ui = 0; /* set the string length and null terminator */ vmb_put_len(ext->str, len); ext->str[VMB_LEN + len] = '\0'; /* copy the string, if provided */ if (str != 0) memcpy(ext->str + VMB_LEN, str, len); /* return the new extension */ return ext; } /* ------------------------------------------------------------------------ */ /* * CVmObjFileName object statics */ /* metaclass registration object */ static CVmMetaclassFileName metaclass_reg_obj; CVmMetaclass *CVmObjFileName::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjFileName::*CVmObjFileName::func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjFileName::getp_undef, /* 0 */ &CVmObjFileName::getp_getName, /* 1 */ &CVmObjFileName::getp_getRootName, /* 2 */ &CVmObjFileName::getp_getPath, /* 3 */ &CVmObjFileName::getp_fromUniversal, /* 4 */ &CVmObjFileName::getp_toUniversal, /* 5 */ &CVmObjFileName::getp_addToPath, /* 6 */ &CVmObjFileName::getp_isAbsolute, /* 7 */ &CVmObjFileName::getp_getAbsolutePath, /* 8 */ &CVmObjFileName::getp_getRootDirs, /* 9 */ &CVmObjFileName::getp_getFileType, /* 10 */ &CVmObjFileName::getp_getFileInfo, /* 11 */ &CVmObjFileName::getp_deleteFile, /* 12 */ &CVmObjFileName::getp_renameFile, /* 13 */ &CVmObjFileName::getp_listDir, /* 14 */ &CVmObjFileName::getp_forEachFile, /* 15 */ &CVmObjFileName::getp_createDirectory, /* 16 */ &CVmObjFileName::getp_removeDirectory /* 17 */ }; /* property indices */ const int VMOFN_FROMUNIVERSAL = 4; const int VMOFN_GETROOTDIRS = 9; const int VMOFN_GETFILETYPE = 10; const int VMOFN_GETFILEINFO = 11; const int VMOFN_DELETEFILE = 12; const int VMOFN_RENAMEFILE = 13; const int VMOFN_LISTDIR = 14; const int VMOFN_FOREACHFILE = 15; const int VMOFN_CREATEDIR = 16; const int VMOFN_REMOVEDIR = 17; /* ------------------------------------------------------------------------ */ /* * Cover functions for the various OS path operations. These test the * network storage mode: if we're in network mode, we use the storage * server syntax, which follows Unix rules regardless of the local OS. * Otherwise we use the local OS rules, via the osifc functions. */ /* canonicalize a network path: resolve . and .. links */ static void nf_canonicalize(char *path) { /* note if it's an absolute path */ int abs = path[0] == '/'; /* make a list of the path elements */ char **ele = new char*[strlen(path)]; int cnt; for (cnt = 0, ele[0] = strtok(path, "/") ; ele[cnt] != 0 ; ele[++cnt] = strtok(0, "/")) ; /* remove '.' and empty elements, and cancel '..' against prior elements */ int src, dst; for (src = dst = 0 ; src < cnt ; ) { if (ele[src][0] == '\0' || strcmp(ele[src], ".") == 0) { /* empty or '.' - simply omit it */ ++src; } else if (strcmp(ele[src], "..") == 0) { /* * '..' - cancel against the previous element if there is one * and it's not '..', otherwise keep it */ if (dst > 0 && strcmp(ele[dst-1], "..") != 0) --dst, ++src; else ele[dst++] = ele[src++]; } else { /* ordinary element - copy it */ ele[dst++] = ele[src++]; } } /* reconstruct the string */ char *p = path; if (abs) *p++ = '/'; for (int i = 0 ; i < dst ; ++i) { size_t l = strlen(ele[i]); memcpy(p, ele[i], l); p += l; if (i + 1 < dst) *p++ = '/'; } *p = '\0'; /* if it's complete empty, make it '.' */ if (path[0] == '\0') path[0] = '.', path[1] = '\0'; /* done with the path list */ delete [] ele; } /* build a full path */ static void fn_build_full_path( VMG_ char *buf, size_t buflen, const char *dir, const char *fname, int literal) { if (CVmNetFile::is_net_mode(vmg0_)) { /* * Network storage mode - use Unix rules. First, if the filename * is in absolute format already, use it unchanged. If not, append * the filename to the directory, adding a '/' between the two if * the directory doesn't already end with a slash. */ if (fname[0] == '/') { /* the file is absolute - return it unchanged */ lib_strcpy(buf, buflen, fname); } else { /* note the last character of the directory path */ size_t dirlen = strlen(dir); char dirlast = dirlen != 0 ? dir[dirlen-1] : 0; /* build the result */ t3sprintf(buf, buflen, "%s%s%s", dir, dirlast == '/' ? "" : "/", fname); } /* if not in literal mode, canonicalize the result */ if (!literal) nf_canonicalize(buf); } else { /* local storage mode - use local OS rules */ if (literal) os_combine_paths(buf, buflen, dir, fname); else os_build_full_path(buf, buflen, dir, fname); } } /* get the root name from a path string */ static const char *fn_get_root_name(VMG_ const char *str) { if (CVmNetFile::is_net_mode(vmg0_)) { /* * network storage mode - use Unix rules, so simply find the * rightmost '/' and return the portion after it (or the whole * name, if there's no '/') */ const char *r = strrchr(str, '/'); return r != 0 ? r + 1 : str; } else { /* local storage mode - use local OS rules */ return os_get_root_name(str); } } /* get the path name portion from a filename string */ static void fn_get_path_name(VMG_ char *buf, size_t buflen, const char *str) { if (CVmNetFile::is_net_mode(vmg0_)) { /* * Network storage mode - use Unix rules, so simply find the * rightmost '/' and return the portion before it (or an empty * string, if there's no '/'). If the right most '/' is the first * character, or is only preceded by '/' characters, it's a root * path, so return '/' as the path. */ const char *r = strrchr(str, '/'); if (r == 0) { /* there's no '/' at all, so there's no path */ buf[0] = '\0'; } else { /* skip any more '/'s immediately preceding the one we found */ for ( ; r > str && *(r-1) == '/' ; --r) ; /* if we're at the first character, it's a root path */ if (r == str) { /* return "/" for the root path */ lib_strcpy(buf, buflen, "/"); } else { /* return the portion up to but not including the '/' */ lib_strcpy(buf, buflen, str, r - str); } } } else { /* local storage mode - use local OS rules */ return os_get_path_name(buf, buflen, str); } } /* is the file in absolute path format? */ static int fn_is_file_absolute(VMG_ const char *str) { if (CVmNetFile::is_net_mode(vmg0_)) { /* network storage mode - it's absolute if it starts with '/' */ return str[0] == '/'; } else { /* local storage mode - use local OS rules */ return os_is_file_absolute(str); } } /* get the absolute path */ static int fn_get_abs_filename(VMG_ char *buf, size_t buflen, const char *str) { if (CVmNetFile::is_net_mode(vmg0_)) { /* * network storage mode - the storage server doesn't have such a * thing as an absolute path; all paths are relative to the game's * sandbox folder */ return FALSE; } else { /* local storage mode - use local OS rules */ return os_get_abs_filename(buf, buflen, str); } } /* check for a special filename */ static int fn_is_special_file(VMG_ const char *str) { if (CVmNetFile::is_net_mode(vmg0_)) { /* network storage mode treat "." and ".." as special */ if (strcmp(str, ".") == 0) return OS_SPECFILE_SELF; else if (strcmp(str, "..") == 0) return OS_SPECFILE_PARENT; else return OS_SPECFILE_NONE; } else { /* local storage mode - use local OS rules */ return os_is_special_file(str); } } /* get the special directory path for the working directory */ static const char *fn_pwd_dir(VMG0_) { if (CVmNetFile::is_net_mode(vmg0_)) return "."; else return OSPATHPWD; } /* convert from universal notation to local notation */ static void fn_cvt_url_dir(VMG_ char *buf, size_t buflen, const char *str) { if (CVmNetFile::is_net_mode(vmg0_)) { /* * network storage mode - the universal and local notation are the * same, since the storage server uses unix-style syntax */ lib_strcpy(buf, buflen, str); } else { /* local storage mode - use the local OS conversion */ os_cvt_url_dir(buf, buflen, str); } } /* convert from local notation to universal notation */ static void fn_cvt_dir_url(VMG_ char *buf, size_t buflen, const char *str) { if (CVmNetFile::is_net_mode(vmg0_)) { /* network storage mode - local and universal notation are the same */ lib_strcpy(buf, buflen, str); } else { /* local storage mode - use the local OS conversion */ os_cvt_dir_url(buf, buflen, str); } } /* compare filenames */ static int fn_file_names_equal(VMG_ const char *a, const char *b) { if (CVmNetFile::is_net_mode(vmg0_)) { /* * network storage mode - compare using Unix rules; this is a * simple case-sensitive string comparison */ return strcmp(a, b) == 0; } else { /* local storage mode - use the local OS routine */ return os_file_names_equal(a, b); } } /* ------------------------------------------------------------------------ */ /* * CVmObjFileName intrinsic class implementation */ /* * construction */ CVmObjFileName::CVmObjFileName(VMG_ int32_t sfid, const char *str, size_t len) { /* allocate our extension structure */ ext_ = (char *)vm_filnam_ext::alloc_ext(vmg_ this, sfid, str, len); } /* * Get the local path string for a given value. If the value is a string, * we'll treat it as a local path name. If it's a FileName, we'll return * its local file name. In either case, returns a pointer to the string in * our usual VMB_LEN prefix format. */ const char *CVmObjFileName::get_local_path(VMG_ const vm_val_t *val) { CVmObjFileName *fn; const char *str; if ((fn = vm_val_cast(CVmObjFileName, val)) != 0) { /* it's a FileName object - return a copy of its filename string */ return fn->get_ext()->str; } else if ((str = val->get_as_string(vmg0_)) != 0) { /* it's a string, so convert from URL notation to local path syntax */ return str; } else { /* it's not a string or FileName */ return 0; } } /* * Combine two local path elements to create a full local path. Returns a * new object representing the combined path. */ vm_obj_id_t CVmObjFileName::combine_path( VMG_ const char *path, size_t pathl, const char *file, size_t filel, int literal) { /* * Estimate the space we'll need for the combined result. This is just * the sum of the two sizes on most systems, plus a path separator. On * some older systems this might add a bit more overhead, such as VMS * bracket notation for the directory. Pad it out a bit to be safe. */ size_t buflen = pathl + filel + 32; if (buflen < OSFNMAX) buflen = OSFNMAX; char *buf = 0, *pathz = 0, *filez = 0; vm_obj_id_t id = VM_INVALID_OBJ; err_try { /* allocate null-terminated copies of the source strings */ pathz = lib_copy_str(path, pathl); filez = lib_copy_str(file, filel); /* allocate the result buffer */ buf = lib_alloc_str(buflen); /* build the full path */ fn_build_full_path(vmg_ buf, buflen, pathz, filez, literal); /* create the FileName for the combined path */ id = create_from_local(vmg_ buf, strlen(buf)); } err_finally { /* delete the allocated strings */ lib_free_str(buf); lib_free_str(pathz); lib_free_str(filez); } err_end; /* return the new object */ return id; } /* * Convert a string from URL to local filename notation. Allocates the * result string; the caller must free the string with lib_free_str(). */ char *CVmObjFileName::url_to_local( VMG_ const char *str, size_t len, int nullterm) { char *strz = 0, *buf = 0; err_try { /* * Estimate the length of the return string we'll need. Most * systems these days have Unix-like file naming, where the result * path will be the same length as the source path. There are * older systems with more complex conventions; for example, VMS * puts directories in brackets. To allow for this kind of * variation, allocate space for the combined string plus some * overhead. In any case, use the local maximum filename length as * the lower limit. */ size_t buflen = len + 32; if (buflen < OSFNMAX) buflen = OSFNMAX; /* make a null-terminated copy of the string if necessary */ if (!nullterm) strz = lib_copy_str(str, len); /* allocate a conversion buffer */ buf = lib_alloc_str(buflen); /* convert the name */ fn_cvt_url_dir(vmg_ buf, buflen, nullterm ? str : strz); } err_catch_disc { /* delete the buffer and re-throw the error */ lib_free_str(buf); err_rethrow(); } err_finally { /* we're done with copy of the source string (if we made one) */ lib_free_str(strz); } err_end; /* return the buffer */ return buf; } /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjFileName::create_from_stack( VMG_ const uchar **pc_ptr, uint argc) { /* parse arguments */ const char *path, *file; vm_obj_id_t id = VM_INVALID_OBJ; if (argc == 0) { /* no arguments - create a file representing the working directory */ const char *pwd = fn_pwd_dir(vmg0_); id = create_from_local(vmg_ pwd, strlen(pwd)); } else if (argc == 1 && (file = G_stk->get(0)->get_as_string(vmg0_)) != 0) { /* it's a string in local filename notation */ id = create_from_local(vmg_ file + VMB_LEN, vmb_get_len(file)); } else if (argc == 1 && G_stk->get(0)->is_numeric(vmg0_)) { /* it's a special file ID - resolve to a path */ int32_t sfid = G_stk->get(0)->num_to_int(vmg0_); char buf[OSFNMAX]; if (!CVmObjFile::sfid_to_path(vmg_ buf, sizeof(buf), sfid)) err_throw(VMERR_BAD_VAL_BIF); /* create the file */ id = create_from_sfid(vmg_ sfid, buf, strlen(buf)); } else if (argc == 2 && (path = get_local_path(vmg_ G_stk->get(0))) != 0 && (file = get_local_path(vmg_ G_stk->get(1))) != 0) { /* create a FileName from the combination of the two path strings */ id = combine_path(vmg_ path + VMB_LEN, vmb_get_len(path), file + VMB_LEN, vmb_get_len(file), FALSE); } else if (argc >= 3) { /* invalid number of arguments */ err_throw(VMERR_WRONG_NUM_OF_ARGS); } else { /* wrong types */ err_throw(VMERR_BAD_TYPE_BIF); } /* discard arguments */ G_stk->discard(argc); /* return the new ID */ return id; } /* * create from URL notation */ vm_obj_id_t CVmObjFileName::create_from_url(VMG_ const char *str, size_t len) { char *lcl = 0; vm_obj_id_t fn = VM_INVALID_OBJ; err_try { /* convert to local notation */ lcl = url_to_local(vmg_ str, len, FALSE); /* create the FileName object */ fn = create_from_local(vmg_ lcl, strlen(lcl)); } err_finally { /* we're done with the conversion buffer and path */ lib_free_str(lcl); } err_end; /* return the filename object */ return fn; } /* * create from a local path string */ vm_obj_id_t CVmObjFileName::create_from_local(VMG_ const char *str, size_t len) { /* allocate the new ID */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, FALSE, FALSE); /* create the object */ new (vmg_ id) CVmObjFileName(vmg_ 0, str, len); /* return the new ID */ return id; } /* * create from a special file ID */ vm_obj_id_t CVmObjFileName::create_from_sfid(VMG_ int32_t sfid, const char *str, size_t len) { /* allocate the new ID */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, FALSE, FALSE); /* create the object */ new (vmg_ id) CVmObjFileName(vmg_ sfid, str, len); /* return the new ID */ return id; } /* ------------------------------------------------------------------------ */ /* * notify of deletion */ void CVmObjFileName::notify_delete(VMG_ int /*in_root_set*/) { /* free our additional data, if we have any */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjFileName::set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) { /* no settable properties - throw an error */ err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * get a property */ int CVmObjFileName::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { /* translate the property into a function vector index */ uint func_idx = G_meta_table->prop_to_vector_idx( metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from our base class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * call a static property */ int CVmObjFileName::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* look up the function */ uint midx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); switch (midx) { case VMOFN_FROMUNIVERSAL: return s_getp_fromUniversal(vmg_ result, argc); case VMOFN_GETROOTDIRS: return s_getp_getRootDirs(vmg_ result, argc); default: /* it's not ours - inherit the default handling */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } } /* ------------------------------------------------------------------------ */ /* * load from an image file */ void CVmObjFileName::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from the image file */ void CVmObjFileName::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); } /* * load or reload data from the image */ void CVmObjFileName::load_image_data(VMG_ const char *ptr, size_t siz) { /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* get the special file ID */ int32_t sfid = osrp4s(ptr); /* if there's a special file ID, look it up; otherwise load the name */ vm_filnam_ext *ext; if (sfid != 0) { /* translate the special file ID to a path */ char sfbuf[OSFNMAX]; int ok = CVmObjFile::sfid_to_path(vmg_ sfbuf, sizeof(sfbuf), sfid); /* allocate the extension */ ext = vm_filnam_ext::alloc_ext(vmg_ this, sfid, sfbuf, strlen(sfbuf)); /* note whether the special file ID is valid */ ext->sfid_valid = (char)ok; } else { /* get the filename pointer and length */ const char *str = ptr + 4; size_t len = vmb_get_len(str); str += VMB_LEN; char *lcl = 0; err_try { /* convert the universal path string to local notation */ lcl = url_to_local(vmg_ str, len, FALSE); /* allocate the extension */ ext = vm_filnam_ext::alloc_ext(vmg_ this, 0, lcl, strlen(lcl)); } err_finally { lib_free_str(lcl); } err_end; } /* store the extension */ ext_ = (char *)ext; } /* ------------------------------------------------------------------------ */ /* * save to a file */ void CVmObjFileName::save_to_file(VMG_ class CVmFile *fp) { /* get our extension */ vm_filnam_ext *ext = get_ext(); /* write the special file ID */ fp->write_int4(ext->sfid); /* if it's an ordinary file, write the path name */ if (ext->sfid == 0) { char *u = 0; err_try { /* write the path in universal notation */ u = to_universal(vmg0_); fp->write_str_short_prefix(u, strlen(u)); } err_finally { lib_free_str(u); } err_end; } } /* * restore from a file */ void CVmObjFileName::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* read the special file ID */ int32_t sfid = fp->read_int4(); /* if there's a special file ID, look it up; otherwise read the name */ vm_filnam_ext *ext; if (sfid != 0) { /* get the special file path */ char sfbuf[OSFNMAX]; int ok = CVmObjFile::sfid_to_path(vmg_ sfbuf, sizeof(sfbuf), sfid); /* create the extension */ ext = vm_filnam_ext::alloc_ext(vmg_ this, sfid, sfbuf, strlen(sfbuf)); /* note whether the ID is valid on this platform */ ext->sfid_valid = (char)ok; } else { char *uni = 0, *lcl = 0; err_try { /* read the filename length */ size_t len = fp->read_uint2(); /* load the saved universal path */ uni = lib_alloc_str(len + 1); fp->read_bytes(uni, len); uni[len] = '\0'; /* convert the saved universal path to local notation */ lcl = url_to_local(vmg_ uni, len, TRUE); /* allocate the extension structure */ ext = vm_filnam_ext::alloc_ext(vmg_ this, 0, lcl, strlen(lcl)); } err_finally { /* we're done with the allocated buffers */ lib_free_str(lcl); lib_free_str(uni); } err_end; } /* remember the new extension */ ext_ = (char *)ext; } /* ------------------------------------------------------------------------ */ /* * Get my path string. This validates the special file ID, and throws an * error if it's not valid. It's possible to create a FileName with an * invalid special file ID by loading a saved game that was created on a * platform with a newer version that includes new SFIDs that don't exist * in this interpreter version. In such a case we load the special file, * and it can be saved again, but it's not usable locally and has no path * information. */ const char *CVmObjFileName::get_path_string() const { /* make sure this isn't an invalid special file */ if (get_ext()->sfid && !get_ext()->sfid_valid) err_throw(VMERR_FILE_NOT_FOUND); /* return my path */ return get_ext()->str; } /* ------------------------------------------------------------------------ */ /* * Compare filenames by the path strings */ int CVmObjFileName::equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int /*depth*/) const { /* check the type of the other value */ CVmObjFileName *fn; const char *str; if ((fn = vm_val_cast(CVmObjFileName, val)) != 0) { /* it's another FileName object - compare its path */ return fn_file_names_equal( vmg_ get_ext()->get_str(), fn->get_ext()->get_str()); } else if ((str = val->get_as_string(vmg0_)) != 0) { char *strz = 0; int eq; err_try { /* make a null-terminated copy of the other name */ strz = lib_copy_str(str + VMB_LEN, vmb_get_len(str)); /* compare the names */ eq = fn_file_names_equal(vmg_ get_ext()->get_str(), strz); } err_finally { lib_free_str(strz); } err_end; /* return the result */ return eq; } else { /* other types are definitely not equal */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Add a value - this appends a path element */ int CVmObjFileName::add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { /* * get the other value's path - this can come from a FileName object or * a string */ const char *other = get_local_path(vmg_ val); if (other == 0) err_throw(VMERR_BAD_TYPE_ADD); /* build a new object from the combined paths */ result->set_obj(combine_path( vmg_ get_ext()->get_str(), get_ext()->get_len(), other + VMB_LEN, vmb_get_len(other), FALSE)); /* success */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * fromUniversal static method */ int CVmObjFileName::s_getp_fromUniversal(VMG_ vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the filename argument as a string */ const char *str = G_stk->get(0)->get_as_string(vmg0_); if (str == 0) err_throw(VMERR_BAD_VAL_BIF); /* create and return the new FileName from the url syntax */ retval->set_obj(create_from_url(vmg_ str + VMB_LEN, vmb_get_len(str))); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Get the filename string in univeral (URL) format. This allocates a * buffer, which the caller must free with lib_free_str() when done. */ char *CVmObjFileName::to_universal(VMG0_) const { /* get our local filename string */ const char *fname = get_ext()->get_str(); size_t fnamel = get_ext()->get_len(); /* * estimate the result size - the URL string should be about the same * length as the source string, but leave a little extra space just in * case we have to add some path separators that are implied in the * local format */ size_t buflen = fnamel + 32; if (buflen < OSFNMAX) buflen = OSFNMAX; /* allocate buffer */ char *buf = lib_alloc_str(buflen); /* do the conversion */ fn_cvt_dir_url(vmg_ buf, buflen, fname); /* return the new string */ return buf; } /* ------------------------------------------------------------------------ */ /* * toUniversal method */ int CVmObjFileName::getp_toUniversal(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; char *buf = 0; err_try { /* allocate the temporary result buffer */ buf = to_universal(vmg0_); /* return a new String object from the result */ retval->set_obj(CVmObjString::create(vmg_ FALSE, buf, strlen(buf))); } err_finally { /* free the temp buffer */ lib_free_str(buf); } err_end; /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getName method */ int CVmObjFileName::getp_getName(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get our extension */ vm_filnam_ext *ext = get_ext(); /* return the path string */ retval->set_obj(CVmObjString::create( vmg_ FALSE, ext->get_str(), ext->get_len())); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getRootName method */ int CVmObjFileName::getp_getRootName(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* presume we won't find a root name */ retval->set_nil(); /* extract the root name from our filename string */ const char *r = fn_get_root_name(vmg_ get_ext()->get_str()); /* if we found a root name, return it as a string */ if (r != 0 && r[0] != '\0') retval->set_obj(CVmObjString::create(vmg_ FALSE, r, strlen(r))); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getPath method */ int CVmObjFileName::getp_getPath(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* presume we'll return nil */ retval->set_nil(); /* get our extension */ vm_filnam_ext *ext = get_ext(); /* get our filename string */ const char *fname = ext->get_str(); size_t fnamel = ext->get_len(); /* * Estimate how much space we'll need for the extracted path name. The * path portion should be shorter than the whole name, but add some * padding for overhead in case the local system needs to add any * syntax (e.g., VMS directory brackets). */ size_t buflen = fnamel + 32; if (buflen < OSFNMAX) buflen = OSFNMAX; char *buf = 0; err_try { /* allocate a temporary result buffer */ buf = lib_alloc_str(buflen); /* extract the path name */ fn_get_path_name(vmg_ buf, buflen, fname); /* return the path as a FileName, or nil if there's no path */ if (buf[0] != '\0') retval->set_obj(create_from_local(vmg_ buf, strlen(buf))); } err_finally { /* free the temp buffer */ lib_free_str(buf); } err_end; /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * addToPath method */ int CVmObjFileName::getp_addToPath(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the target path - it can be a FileName or string */ const char *file = get_local_path(vmg_ G_stk->get(0)); if (file == 0) err_throw(VMERR_BAD_TYPE_BIF); /* create a new object from the combined path */ retval->set_obj(combine_path( vmg_ get_ext()->get_str(), get_ext()->get_len(), file + VMB_LEN, vmb_get_len(file), FALSE)); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * isAbsolute method */ int CVmObjFileName::getp_isAbsolute(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* ask the OS if we're absolute, and set the return value accordingly */ retval->set_logical(fn_is_file_absolute(vmg_ get_ext()->get_str())); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getAbsolutePath method */ int CVmObjFileName::getp_getAbsolutePath(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get our local filename string */ const char *fname = get_ext()->get_str(); size_t fnamel = get_ext()->get_len(); /* we haven't allocated any strings yet */ char *buf = 0, *outbuf = 0; err_try { /* * if it's not absolute already, combine it with our internal * working directory to get the fully qualified path */ const char *src = fname; if (!fn_is_file_absolute(vmg_ fname)) { /* allocate space for the combined path */ size_t buflen = fnamel + strlen(G_file_path) + 32; if (buflen < OSFNMAX) buflen = OSFNMAX; /* build the combined path */ buf = lib_alloc_str(buflen); fn_build_full_path(vmg_ buf, buflen, G_file_path, fname, TRUE); /* use this as the new source path */ src = buf; } /* * Even though the name should already be absolute, run it through * os_get_abs_filename() anyway, since that will convert the name * to a more canonical format on many systems. */ /* figure how much space we'll need, adding some padding */ size_t buflen = strlen(src) + 32; if (buflen < OSFNMAX) buflen = OSFNMAX; /* allocate the buffer and do the conversion */ outbuf = lib_alloc_str(buflen); if (fn_get_abs_filename(vmg_ outbuf, buflen, src)) { /* success - return the result string */ retval->set_obj(CVmObjString::create( vmg_ FALSE, outbuf, strlen(outbuf))); } else { /* failure - return nil */ retval->set_nil(); } } err_finally { /* free the allocated string */ lib_free_str(buf); lib_free_str(outbuf); } err_end; /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getRootDirs method */ int CVmObjFileName::s_getp_getRootDirs(VMG_ vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* * See how much space we need for the root list. If we're in network * storage mode, there's no concept of a root folder, since everything * is relative to the user+game folder. */ size_t buflen; if (!CVmNetFile::is_net_mode(vmg0_) && (buflen = os_get_root_dirs(0, 0)) != 0) { /* allocate space */ char *buf = new char[buflen]; char *lclstr = 0; err_try { /* get the root list for real this time */ os_get_root_dirs(buf, buflen); /* count the strings in the list, filtered for file safety */ int cnt = 0; char *p; for (p = buf ; *p != '\0' ; p += strlen(p) + 1) { /* if we're allowed to get metadata on this path, count it */ if (CVmObjFile::query_safety_for_open( vmg_ p, VMOBJFILE_ACCESS_GETINFO)) ++cnt; } /* create the list; clear it and push it for gc protection */ retval->set_obj(CVmObjList::create(vmg_ FALSE, cnt)); CVmObjList *lst = vm_objid_cast(CVmObjList, retval->val.obj); lst->cons_clear(); G_stk->push(retval); /* build the list */ for (p = buf, cnt = 0 ; *p != '\0' ; p += strlen(p) + 1) { /* check to see if we're allowed to access this root */ if (CVmObjFile::query_safety_for_open( vmg_ p, VMOBJFILE_ACCESS_GETINFO)) { /* map the string to utf8 */ size_t len = G_cmap_from_fname->map_str_alo(&lclstr, p); /* create the FileName object */ vm_val_t ele; ele.set_obj(create_from_local(vmg_ lclstr, len)); /* done with the mapped string */ t3free(lclstr); lclstr = 0; /* add it to the list */ lst->cons_set_element(cnt++, &ele); } } /* done with our gc protection for the list */ G_stk->discard(); } err_finally { /* free the root list buffer */ delete [] buf; /* free the mapped filename string */ if (lclstr != 0) t3free(lclstr); } err_end; } else { /* no root list - return an empty list */ retval->set_obj(CVmObjList::create(vmg_ FALSE, (size_t)0)); } /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * deleteFile method */ int CVmObjFileName::getp_deleteFile(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the filename; use the DELETE access mode */ vm_rcdesc rc(vmg_ "FileName.deleteFile", metaclass_reg_->get_class_obj(vmg0_), VMOFN_DELETEFILE, G_stk->get(0), argc); CVmNetFile *netfile = CVmObjFile::get_filename_from_obj( vmg_ self, &rc, VMOBJFILE_ACCESS_DELETE, OSFTUNK, "application/octet-stream"); /* close the network file descriptor - this will delete the file */ netfile->close(vmg0_); /* no return value */ retval->set_nil(); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * renameFile method */ int CVmObjFileName::getp_renameFile(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* set up our recursive callback descriptor in case netfile needs it */ vm_rcdesc rc(vmg_ "FileName.renameFile", metaclass_reg_->get_class_obj(vmg0_), VMOFN_RENAMEFILE, G_stk->get(0), argc); CVmNetFile *oldfile = 0, *newfile = 0; err_try { /* get the old filename from self; use the RENAME FROM access mode */ oldfile = CVmObjFile::get_filename_from_obj( vmg_ self, &rc, VMOBJFILE_ACCESS_RENAME_FROM, OSFTUNK, "application/octet-stream"); /* get the new filename argument; use RENAME TO access mode */ newfile = CVmObjFile::get_filename_arg( vmg_ G_stk->get(0), &rc, VMOBJFILE_ACCESS_RENAME_TO, FALSE, OSFTUNK, "application/octet-stream"); /* rename the file */ oldfile->rename_to(vmg_ newfile); } err_finally { /* done with the netfile objects */ if (oldfile != 0) oldfile->abandon(vmg0_); if (newfile != 0) newfile->abandon(vmg0_); } err_end; /* no return value */ retval->set_nil(); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Translate an os_filemode_t value to a FileTypeXxx value */ static int32_t filemode_to_filetype(int osmode) { /* * mappings from OSFMODE_xxx values to FileTypeXxx values (see * include/filename.h in the system headers) */ static const struct { unsigned long osmode; int32_t tadsmode; } xlat[] = { { OSFMODE_FILE, 0x0001 }, // FileTypeFile { OSFMODE_DIR, 0x0002 }, // FileTypeDir { OSFMODE_CHAR, 0x0004 }, // FileTypeChar { OSFMODE_BLK, 0x0008 }, // FileTypeBlock { OSFMODE_PIPE, 0x0010 }, // FileTypePipe { OSFMODE_SOCKET, 0x0020 }, // FileTypeSocket { OSFMODE_LINK, 0x0040 } // FileTypeLink }; /* run through the bit flags, and set the translated bits */ int32_t tadsmode = 0; for (int i = 0 ; i < countof(xlat) ; ++i) { /* if this OS mode bit is set, set the corresponding TADS bit */ if ((osmode & xlat[i].osmode) != 0) tadsmode |= xlat[i].tadsmode; } /* return the result */ return tadsmode; } /* * Translate OSFATTR_xxx values to FileAttrXxx values */ static int32_t map_file_attrs(unsigned long osattrs) { /* * mappings from OSFATTR_xxx values to FileAttrXxx values (see * include/filename.h in the system headers) */ static const struct { unsigned long osattr; int32_t tadsattr; } xlat[] = { { OSFATTR_HIDDEN, 0x0001 }, // FileTypeHidden { OSFATTR_SYSTEM, 0x0002 }, // FileTypeSystem { OSFATTR_READ, 0x0004 }, // FileTypeRead { OSFATTR_WRITE, 0x0008 } // FileTypeWrite }; /* run through the bit flags, and set the translated bits */ int32_t tadsattrs = 0; for (int i = 0 ; i < countof(xlat) ; ++i) { /* if this OS mode bit is set, set the corresponding TADS bit */ if ((osattrs & xlat[i].osattr) != 0) tadsattrs |= xlat[i].tadsattr; } /* return the result */ return tadsattrs; } /* * Push a file timestamp value */ static void push_file_time(VMG_ os_time_t t) { /* * if the time value is zero, it means that the local system doesn't * support this type of timestamp; push it as nil */ if (t == 0) { /* no time value - push nil */ G_stk->push()->set_nil(); } else { /* we have a valid time, so create a Date object to represent it */ G_stk->push()->set_obj(CVmObjDate::create_from_time_t(vmg_ FALSE, t)); } } /* * Get my special file type flags, if applicable. This inspects the root * name to see if it's a special relative link, such as Unix "." or "..". */ int32_t CVmObjFileName::special_filetype_flags(VMG0_) { /* no special flags so far */ int32_t flags = 0; /* get my extension */ vm_filnam_ext *ext = get_ext(); /* get the root name from my filename string */ const char *root = fn_get_root_name(vmg_ (char *)ext->get_str()); /* check the root name to see if it's special */ switch (fn_is_special_file(vmg_ root)) { case OS_SPECFILE_SELF: flags |= 0x0080; // FileTypeSelfLink break; case OS_SPECFILE_PARENT: flags |= 0x0100; // FileTypeParentLink break; default: break; } /* return the flags */ return flags; } /* ------------------------------------------------------------------------ */ /* * getFileType method */ int CVmObjFileName::getp_getFileType(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the filename; use the GETINFO access mode */ vm_rcdesc rc(vmg_ "FileName.getFileType", metaclass_reg_->get_class_obj(vmg0_), VMOFN_GETFILETYPE, G_stk->get(0), argc); CVmNetFile *netfile = CVmObjFile::get_filename_from_obj( vmg_ self, &rc, VMOBJFILE_ACCESS_GETINFO, OSFTUNK, "application/octet-stream"); /* check to see if we're following links (follow them by default) */ int follow_links = TRUE; if (argc >= 1) { /* * note that our 'asLink' parameter has the opposite sense from * 'follow_links', so invert the parameter value */ follow_links = !G_stk->get(0)->get_logical(); if (!G_stk->get(0)->is_logical()) err_throw(VMERR_BAD_VAL_BIF); } int ok; unsigned long osmode, osattr; err_try { /* get the file type from the netfile object */ ok = netfile->get_file_mode(vmg_ &osmode, &osattr, follow_links); } err_finally { /* done with the netfile object */ netfile->abandon(vmg0_); } err_end; /* figure the return value */ if (ok) { /* success - return the file mode, translated to FileTypeXxx bits */ retval->set_int( filemode_to_filetype(osmode) | special_filetype_flags(vmg0_)); } else { /* failed - simply return nil */ retval->set_nil(); } /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getFileInfo method */ int CVmObjFileName::getp_getFileInfo(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the filename; use the GETINFO access mode */ vm_rcdesc rc(vmg_ "FileName.getFileInfo", metaclass_reg_->get_class_obj(vmg0_), VMOFN_GETFILETYPE, G_stk->get(0), argc); CVmNetFile *netfile = CVmObjFile::get_filename_from_obj( vmg_ self, &rc, VMOBJFILE_ACCESS_GETINFO, OSFTUNK, "application/octet-stream"); /* check to see if we're following links (follow them by default) */ int follow_links = TRUE; if (argc >= 1) { /* * note that our 'asLink' parameter has the opposite sense from * 'follow_links', so invert the parameter value */ follow_links = G_stk->get(0)->get_logical(); if (!G_stk->get(0)->is_logical()) err_throw(VMERR_BAD_VAL_BIF); } int ok; os_file_stat_t stat; err_try { /* get the file type from the netfile object */ ok = netfile->get_file_stat(vmg_ &stat, follow_links); } err_finally { /* done with the netfile object */ netfile->abandon(vmg0_); } err_end; /* build the return value */ if (ok) { /* * Success - build a new FileInfo(mode, size, ctime, mtime, atime, * target, attrs). Start by pushing the constructor arguments * (last to first). */ /* push the file attributes */ G_stk->push()->set_int(map_file_attrs(stat.attrs)); /* if this is a symbolic link file, get the target path */ char target[OSFNMAX]; if ((stat.mode & OSFMODE_LINK) != 0 && netfile->resolve_symlink(vmg_ target, sizeof(target))) { /* push the name as a string in UTF8 */ G_stk->push()->set_obj(CVmObjString::create( vmg_ FALSE, target, strlen(target), G_cmap_from_fname)); } else { /* no symbolic link name - push nil as the symlink argument */ G_stk->push()->set_nil(); } /* push the timestamps */ push_file_time(vmg_ stat.acc_time); push_file_time(vmg_ stat.mod_time); push_file_time(vmg_ stat.cre_time); /* * Push the file size. If the high part is non-zero, or the low * part is greater than 0x7fffffff, the value won't fit in an * int32_t, so we need to push this as a BigNumber value. */ if (stat.sizehi != 0 || stat.sizelo > 0x7FFFFFFFU) { /* push it as a BigNumber value */ G_interpreter->push_obj(vmg_ CVmObjBigNum::create_int64( vmg_ FALSE, stat.sizehi, stat.sizelo)); } else { /* it'll fit in a simple integer value */ G_interpreter->push_int(vmg_ (int32_t)stat.sizelo); } /* push the file mode, translated to FileTypeXxx bits */ G_interpreter->push_int( vmg_ filemode_to_filetype(stat.mode) | special_filetype_flags(vmg0_)); /* * Call the constructor. If there's a FileInfo object exported, * create one of those. Otherwise just create a list from the * values. */ const int argc = 7; vm_obj_id_t fi = G_predef->file_info; if (fi != VM_INVALID_OBJ) { /* create the FileInfo instance */ vm_objp(vmg_ fi)->create_instance(vmg_ fi, 0, argc); /* make sure we got an object back */ if (G_interpreter->get_r0()->typ != VM_OBJ) err_throw(VMERR_OBJ_VAL_REQD); /* set the object return value */ retval->set_obj(G_interpreter->get_r0()->val.obj); } else { /* return the information as a list */ retval->set_obj(CVmObjList::create_from_stack(vmg_ 0, argc)); } } else { /* failed - simply return nil */ retval->set_nil(); } /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * listDir method */ int CVmObjFileName::getp_listDir(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the filename; use the READ DIR access mode */ vm_rcdesc rc(vmg_ "FileName.listDir", metaclass_reg_->get_class_obj(vmg0_), VMOFN_LISTDIR, G_stk->get(0), argc); CVmNetFile *netfile = CVmObjFile::get_filename_from_obj( vmg_ self, &rc, VMOBJFILE_ACCESS_READDIR, OSFTUNK, "application/octet-stream"); err_try { /* get the directory listing */ if (!netfile->readdir(vmg_ get_ext()->get_str(), retval)) G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc, 0, "error reading directory"); } err_finally { /* done with the netfile object */ netfile->abandon(vmg0_); } err_end; /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * forEachFile method */ int CVmObjFileName::getp_forEachFile(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the filename; use the READ DIR access mode */ vm_rcdesc rc(vmg_ "FileName.forEachFile", metaclass_reg_->get_class_obj(vmg0_), VMOFN_FOREACHFILE, G_stk->get(0), argc); CVmNetFile *netfile = CVmObjFile::get_filename_from_obj( vmg_ self, &rc, VMOBJFILE_ACCESS_READDIR, OSFTUNK, "application/octet-stream"); /* get the 'recursive' flag, if present */ int recursive = FALSE; if (argc >= 2) { if (G_stk->get(1)->is_logical()) recursive = G_stk->get(1)->get_logical(); else err_throw(VMERR_BAD_TYPE_BIF); } err_try { /* do the directory enumeration through the callback */ if (!netfile->readdir_cb(vmg_ get_ext()->get_str(), &rc, G_stk->get(0), recursive)) G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc, 0, "error reading directory"); } err_finally { /* done with the netfile object */ netfile->abandon(vmg0_); } err_end; /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * createDirectory method */ int CVmObjFileName::getp_createDirectory(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the filename; use MKDIR access mode */ vm_rcdesc rc(vmg_ "FileName.createDirectory", metaclass_reg_->get_class_obj(vmg0_), VMOFN_CREATEDIR, G_stk->get(0), argc); CVmNetFile *netfile = CVmObjFile::get_filename_from_obj( vmg_ self, &rc, VMOBJFILE_ACCESS_MKDIR, OSFTUNK, "application/octet-stream"); err_try { /* get the "create parents" parameter */ int create_parents = FALSE; if (argc >= 1) { /* check that it's a true/nil value */ if (!G_stk->get(0)->is_logical()) err_throw(VMERR_BAD_TYPE_BIF); /* retrieve the flag */ create_parents = G_stk->get(0)->get_logical(); } /* create the directory */ netfile->mkdir(vmg_ create_parents); } err_finally { /* done with the netfile object */ netfile->abandon(vmg0_); } err_end; /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * removeDirectory method */ int CVmObjFileName::getp_removeDirectory(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the filename; use RMDIR access mode */ vm_rcdesc rc(vmg_ "FileName.removeDirectory", metaclass_reg_->get_class_obj(vmg0_), VMOFN_REMOVEDIR, G_stk->get(0), argc); CVmNetFile *netfile = CVmObjFile::get_filename_from_obj( vmg_ self, &rc, VMOBJFILE_ACCESS_RMDIR, OSFTUNK, "application/octet-stream"); err_try { /* get the "remove contents" parameter */ int remove_contents = FALSE; if (argc >= 1) { /* check that it's a true/nil value */ if (!G_stk->get(0)->is_logical()) err_throw(VMERR_BAD_TYPE_BIF); /* retrieve the flag */ remove_contents = G_stk->get(0)->get_logical(); } /* remove the directory */ netfile->rmdir(vmg_ remove_contents); } err_finally { /* done with the netfile object */ netfile->abandon(vmg0_); } err_end; /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmfilnam.h������������������������������������������������������������������������0000664�0000000�0000000�00000030130�12650170723�0015635�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 2012 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmfilnam.h - CVmObjFileName object Function A FileName represents a local filename string. This is essentially just a string containing the filename, but provides methods to manipulate the name portably, using the various osifc path manipulation routines. Notes Modified 03/03/12 MJRoberts - Creation */ #ifndef VMFILNAM_H #define VMFILNAM_H #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" /* ------------------------------------------------------------------------ */ /* * The image file data block is arranged as follows: * *. UINT2 special_id = special file ID (0 for regular named files) *. UINT2 str_len = length of the string in byts *. UTF8 str = the filename string in universal notation * * If the special file ID is non-zero, the str_len and str fields aren't * stored. */ /* ------------------------------------------------------------------------ */ /* * Our in-memory extension data structure. For most instances, this is * simply the filename string in our usual VMB_LEN prefix format. */ struct vm_filnam_ext { /* allocate the structure */ static vm_filnam_ext *alloc_ext(VMG_ class CVmObjFileName *self, int32_t sfid, const char *str, size_t len); /* get the length of the string, and a pointer to the string buffer */ size_t get_len() { return vmb_get_len(str); } const char *get_str() { return str + VMB_LEN; } /* special file ID */ int32_t sfid; /* flag: is the sfid valid? */ char sfid_valid; /* * Flag: the filename was manually selected by the user through a file * dialog. If this is non-zero, it's an OS_AFP_xxx value giving the * type of dialog that was presented to the user to obtain this * filename. Files selected by the user override the file safety * settings, since the user implicitly grants us permission to use a * file by manually selecting it. */ char from_ui; /* * the actual data is in the same format as an ordinary internal string * - a two-byte length prefix in little-endian order, followed by the * UTF8 bytes of the string */ char str[1]; }; /* ------------------------------------------------------------------------ */ /* * CVmObjFileName intrinsic class definition */ class CVmObjFileName: public CVmObject { friend class CVmMetaclassFileName; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is this a CVmObjFileName object? */ static int is_vmfilnam_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* create from URL notation */ static vm_obj_id_t create_from_url(VMG_ const char *str, size_t len); /* create from a local path */ static vm_obj_id_t create_from_local(VMG_ const char *str, size_t len); /* create from a special file ID (and its resolved local path) */ static vm_obj_id_t create_from_sfid( VMG_ int32_t sfid, const char *path, size_t len); /* * Combine two path local paths into a new FileName object. If * 'literal' is true, it means that we build the literal combined path, * otherwise we build the canonical path (e.g., resolving '.' and '..' * relative links). */ static vm_obj_id_t combine_path( VMG_ const char *path, size_t pathl, const char *file, size_t filel, int literal); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* compare filenames by filename string */ virtual int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const; /* add - this appends a path element */ virtual int add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* cast to string */ virtual const char *cast_to_string(VMG_ vm_obj_id_t /*self*/, vm_val_t * /*new_str*/) const { return get_path_string(); } /* get my path string */ const char *get_path_string() const; /* get my special file information */ int is_special_file() const { return get_ext()->sfid != 0; } int32_t get_sfid() const { return get_ext()->sfid; } /* * Get/set the "from UI" flag. If the user manually selected the file * through a file dialog, this is an OS_AFP_xxx value specifying the * type of dialog that was presented. */ void set_from_ui(int typ) { get_ext()->from_ui = (char)typ; } int get_from_ui() const { return get_ext()->from_ui; } /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * receive savepoint notification - we don't keep any * savepoint-relative records, so we don't need to do anything here */ void notify_new_savept() { } /* apply an undo record (we're immutable -> no undo) */ void apply_undo(VMG_ struct CVmUndoRecord *) { } /* discard an undo record */ void discard_undo(VMG_ struct CVmUndoRecord *) { } /* mark our undo record references */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* mark our references (we have none) */ void mark_refs(VMG_ uint) { } /* remove weak references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* determine if we've changed since loading; we're immutable, so no */ int is_changed_since_load() const { return FALSE; } protected: /* get my extension data */ vm_filnam_ext *get_ext() const { return (vm_filnam_ext *)ext_; } /* get the local path for a given string or FileName value */ static const char *get_local_path(VMG_ const vm_val_t *val); /* load or reload image data */ void load_image_data(VMG_ const char *ptr, size_t siz); /* create a with no initial contents */ CVmObjFileName() { ext_ = 0; } /* create with contents */ CVmObjFileName(VMG_ int32_t sfid, const char *str, size_t len); /* convert a URL string to a local path string */ static char *url_to_local(VMG_ const char *str, size_t len, int nullterm); /* * convert my filename to universal format, returning an allocated * buffer (which the caller must free with lib_free_str() when done) */ char *to_universal(VMG0_) const; /* * get the special file type flags - this returns FileTypeSelfLink or * FileTypeParentLink, if applicable, based on the root name */ int32_t special_filetype_flags(VMG0_); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* getName method */ int getp_getName(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getRootName method */ int getp_getRootName(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getPath method */ int getp_getPath(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* fromUniversal static method */ int getp_fromUniversal(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc) { return s_getp_fromUniversal(vmg_ retval, argc); } /* fromUniversal static handler */ static int s_getp_fromUniversal(VMG_ vm_val_t *retval, uint *argc); /* toUniversal method */ int getp_toUniversal( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* addToPath method */ int getp_addToPath(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* isAbsolute method */ int getp_isAbsolute(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getAbsolutePath method */ int getp_getAbsolutePath( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getRootDirs method */ int getp_getRootDirs(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc) { return s_getp_getRootDirs(vmg_ retval, argc); } /* static getRootDirs method */ static int s_getp_getRootDirs(VMG_ vm_val_t *retval, uint *argc); /* getFileType method */ int getp_getFileType(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getFileInfo method */ int getp_getFileInfo(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* deleteFile method */ int getp_deleteFile(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* renameFile method */ int getp_renameFile(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getFilesInDir method */ int getp_listDir(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* forEachFile method */ int getp_forEachFile(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* createDirectory method */ int getp_createDirectory( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* removeDirectory method */ int getp_removeDirectory( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluation function table */ static int (CVmObjFileName::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * CVmObjFileName metaclass registration table object */ class CVmMetaclassFileName: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "filename/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjFileName(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjFileName(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjFileName::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjFileName:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMFILNAM_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjFileName) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmfilobj.cpp����������������������������������������������������������������������0000664�0000000�0000000�00000264636�12650170723�0016213�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmfilobj.h - File object metaclass Function Implements an intrinsic class interface to operating system file I/O. Notes Modified 06/28/01 MJRoberts - Creation */ #include <assert.h> #include "t3std.h" #include "charmap.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmbif.h" #include "vmcset.h" #include "vmstack.h" #include "vmmeta.h" #include "vmrun.h" #include "vmglob.h" #include "vmfile.h" #include "vmobj.h" #include "vmstr.h" #include "vmlst.h" #include "vmdate.h" #include "vmfilobj.h" #include "vmpredef.h" #include "vmundo.h" #include "vmbytarr.h" #include "vmbignum.h" #include "vmbiftad.h" #include "vmhost.h" #include "vmimage.h" #include "vmnetfil.h" #include "vmnet.h" #include "vmfilnam.h" #include "vmhash.h" #include "vmpack.h" #include "sha2.h" #include "md5.h" /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassFile metaclass_reg_obj; CVmMetaclass *CVmObjFile::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjFile:: *CVmObjFile::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjFile::getp_undef, /* index 0 */ &CVmObjFile::getp_open_text, /* 1 */ &CVmObjFile::getp_open_data, /* 2 */ &CVmObjFile::getp_open_raw, /* 3 */ &CVmObjFile::getp_get_charset, /* 4 */ &CVmObjFile::getp_set_charset, /* 5 */ &CVmObjFile::getp_close_file, /* 6 */ &CVmObjFile::getp_read_file, /* 7 */ &CVmObjFile::getp_write_file, /* 8 */ &CVmObjFile::getp_read_bytes, /* 9 */ &CVmObjFile::getp_write_bytes, /* 10 */ &CVmObjFile::getp_get_pos, /* 11 */ &CVmObjFile::getp_set_pos, /* 12 */ &CVmObjFile::getp_set_pos_end, /* 13 */ &CVmObjFile::getp_open_res_text, /* 14 */ &CVmObjFile::getp_open_res_raw, /* 15 */ &CVmObjFile::getp_get_size, /* 16 */ &CVmObjFile::getp_get_mode, /* 17 */ &CVmObjFile::getp_getRootName, /* 18 */ &CVmObjFile::getp_deleteFile, /* 19 */ &CVmObjFile::getp_setMode, /* 20 */ &CVmObjFile::getp_packBytes, /* 21 */ &CVmObjFile::getp_unpackBytes, /* 22 */ &CVmObjFile::getp_sha256, /* 23 */ &CVmObjFile::getp_digestMD5 /* 24 */ }; /* * Vector indices - we only need to define these for the static functions, * since we only need them to decode calls to call_stat_prop(). */ enum vmobjfil_meta_fnset { VMOBJFILE_OPEN_TEXT = 1, VMOBJFILE_OPEN_DATA = 2, VMOBJFILE_OPEN_RAW = 3, VMOBJFILE_OPEN_RES_TEXT = 14, VMOBJFILE_OPEN_RES_RAW = 15, VMOBJFILE_GET_ROOT_NAME = 18, VMOBJFILE_DELETE_FILE = 19, }; /* ------------------------------------------------------------------------ */ /* * Create from stack */ vm_obj_id_t CVmObjFile::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { /* * we can't be created with 'new' - we can only be created via our * static creator methods (openTextFile, openDataFile, openRawFile) */ err_throw(VMERR_BAD_DYNAMIC_NEW); /* not reached, but the compiler might not know that */ AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* ------------------------------------------------------------------------ */ /* * Create with no contents */ vm_obj_id_t CVmObjFile::create(VMG_ int in_root_set) { /* instantiate the object */ vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmObjFile(); /* files are always transient */ G_obj_table->set_obj_transient(id); /* return the new ID */ return id; } /* * Create with the given character set object and file handle. */ vm_obj_id_t CVmObjFile::create(VMG_ int in_root_set, CVmNetFile *netfile, vm_obj_id_t charset, CVmDataSource *fp, int mode, int access, int create_readbuf) { /* instantiate the object */ vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmObjFile( vmg_ netfile, charset, fp, mode, access, create_readbuf); /* files are always transient */ G_obj_table->set_obj_transient(id); /* return the ID */ return id; } /* ------------------------------------------------------------------------ */ /* * Instantiate */ CVmObjFile::CVmObjFile(VMG_ CVmNetFile *netfile, vm_obj_id_t charset, CVmDataSource *fp, int mode, int access, int create_readbuf) { /* allocate and initialize our extension */ ext_ = 0; alloc_ext(vmg_ netfile, charset, fp, 0, mode, access, create_readbuf); } /* * Allocate and initialize our extension */ void CVmObjFile::alloc_ext(VMG_ CVmNetFile *netfile, vm_obj_id_t charset, CVmDataSource *fp, unsigned long flags, int mode, int access, int create_readbuf) { /* * if we already have an extension, delete it (and release our * underlying system file, if any) */ notify_delete(vmg_ FALSE); /* * Figure the needed size. We need at least the standard extension; * if we also need a read buffer, figure in space for that as well. */ size_t siz = sizeof(vmobjfile_ext_t); if (create_readbuf) siz += sizeof(vmobjfile_readbuf_t); /* allocate space for our extension structure */ ext_ = (char *)G_mem->get_var_heap()->alloc_mem(siz, this); /* store the data we received from the caller */ get_ext()->netfile = netfile; get_ext()->fp = fp; get_ext()->charset = charset; get_ext()->flags = flags; get_ext()->mode = (unsigned char)mode; get_ext()->access = (unsigned char)access; /* * point to our read buffer, for which we allocated space contiguously * with and immediately following our extension, if we have one */ if (create_readbuf) { /* point to our read buffer object */ vmobjfile_readbuf_t *rb = (vmobjfile_readbuf_t *)(get_ext() + 1); get_ext()->readbuf = rb; /* initialize the read buffer with no initial data */ rb->rem = 0; rb->ptr = rb->buf; } else { /* there's no read buffer at all */ get_ext()->readbuf = 0; } } /* ------------------------------------------------------------------------ */ /* * Notify of deletion */ void CVmObjFile::notify_delete(VMG_ int /*in_root_set*/) { /* if we have an extension, clean it up */ if (ext_ != 0) { /* close our file if we have one */ if (get_ext()->fp != 0) delete get_ext()->fp; /* close the network file */ err_try { if (get_ext()->netfile != 0) get_ext()->netfile->close(vmg0_); } err_catch_disc { /* * Since we're being deleted by the garbage collector, there's * no way to relay this error to the bytecode program - it * evidently lost track of the file, so it must not care about * the ending disposition. Just discard the error. */ } err_end; /* free our extension */ G_mem->get_var_heap()->free_mem(ext_); } } /* ------------------------------------------------------------------------ */ /* * Get the data source */ CVmDataSource *CVmObjFile::get_datasource() const { return get_ext()->fp; } /* * Get the file spec from the netfile */ void CVmObjFile::get_filespec(vm_val_t *val) const { /* presume we won't find a value */ val->set_nil(); /* if we have a net file object, get its filespec */ if (get_ext()->netfile != 0) val->set_obj(get_ext()->netfile->filespec); } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjFile::set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * get a property */ int CVmObjFile::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property index to an index into our function table */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * call a static property */ int CVmObjFile::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* look up the function */ uint midx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* * set up a blank recursive call descriptor, to be filled in when we * know which function we're calling */ vm_rcdesc rc(vmg_ "", CVmObjFile::metaclass_reg_->get_class_obj(vmg0_), (ushort)midx, G_stk->get(0), argc != 0 ? *argc : 0); /* translate the property into a function vector index */ switch(midx) { case VMOBJFILE_OPEN_TEXT: rc.name = "File.openTextFile"; return s_getp_open_text(vmg_ result, argc, FALSE, &rc); case VMOBJFILE_OPEN_DATA: rc.name = "File.openDataFile"; return s_getp_open_data(vmg_ result, argc, &rc); case VMOBJFILE_OPEN_RAW: rc.name = "File.openRawFile"; return s_getp_open_raw(vmg_ result, argc, FALSE, &rc); case VMOBJFILE_OPEN_RES_TEXT: rc.name = "File.openTextResource"; return s_getp_open_text(vmg_ result, argc, TRUE, &rc); case VMOBJFILE_OPEN_RES_RAW: rc.name = "File.openRawResource"; return s_getp_open_raw(vmg_ result, argc, TRUE, &rc); case VMOBJFILE_GET_ROOT_NAME: return s_getp_getRootName(vmg_ result, argc); case VMOBJFILE_DELETE_FILE: return s_getp_deleteFile(vmg_ result, argc); default: /* it's not one of ours - inherit from the base object metaclass */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } } /* ------------------------------------------------------------------------ */ /* * Static "open" methods */ int CVmObjFile::getp_open_text(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_rcdesc rc(vmg_ "File.openTextFile", CVmObjFile::metaclass_reg_->get_class_obj(vmg0_), VMOBJFILE_OPEN_TEXT, G_stk->get(0), argc); return s_getp_open_text(vmg_ retval, argc, FALSE, &rc); } int CVmObjFile::getp_open_data(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_rcdesc rc(vmg_ "File.openDataFile", CVmObjFile::metaclass_reg_->get_class_obj(vmg0_), VMOBJFILE_OPEN_DATA, G_stk->get(0), argc); return s_getp_open_data(vmg_ retval, argc, &rc); } int CVmObjFile::getp_open_raw(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_rcdesc rc(vmg_ "File.openRawFile", CVmObjFile::metaclass_reg_->get_class_obj(vmg0_), VMOBJFILE_OPEN_RAW, G_stk->get(0), argc); return s_getp_open_raw(vmg_ retval, argc, FALSE, &rc); } int CVmObjFile::getp_open_res_text(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_rcdesc rc(vmg_ "File.openTextResource", CVmObjFile::metaclass_reg_->get_class_obj(vmg0_), VMOBJFILE_OPEN_RES_TEXT, G_stk->get(0), argc); return s_getp_open_text(vmg_ retval, argc, TRUE, &rc); } int CVmObjFile::getp_open_res_raw(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_rcdesc rc(vmg_ "File.openRawResource", CVmObjFile::metaclass_reg_->get_class_obj(vmg0_), VMOBJFILE_OPEN_RES_RAW, G_stk->get(0), argc); return s_getp_open_raw(vmg_ retval, argc, TRUE, &rc); } /* ------------------------------------------------------------------------ */ /* * load from an image file */ void CVmObjFile::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load from the image data */ load_image_data(vmg_ ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload the object from image data */ void CVmObjFile::reload_from_image(VMG_ vm_obj_id_t /*self*/, const char *ptr, size_t siz) { /* load the image data */ load_image_data(vmg_ ptr, siz); } /* * load or re-load image data */ void CVmObjFile::load_image_data(VMG_ const char *ptr, size_t siz) { /* read our character set */ vm_obj_id_t charset = vmb_get_objid(ptr); ptr += VMB_OBJECT_ID; /* get the mode and access values */ int mode = (unsigned char)*ptr++; int access = (unsigned char)*ptr++; /* get the flags */ unsigned long flags = t3rp4u(ptr); /* * add in the out-of-sync flag, since we've restored the state from a * past state and thus we're out of sync with the external file system * environment */ flags |= VMOBJFILE_OUT_OF_SYNC; /* * Initialize our extension - we have no underlying network file * descriptor or native file handle, since the file is out of sync by * virtue of being loaded from a previously saved image state. Note * that we don't need a read buffer because the file is inherently out * of sync and thus cannot be read. */ alloc_ext(vmg_ 0, charset, 0, flags, mode, access, FALSE); } /* ------------------------------------------------------------------------ */ /* * save to a file */ void CVmObjFile::save_to_file(VMG_ class CVmFile *fp) { /* files are always transient, so should never be saved */ assert(FALSE); } /* * restore from a file */ void CVmObjFile::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *) { /* files are always transient, so should never be saved */ } /* ------------------------------------------------------------------------ */ /* * Mark as referenced all of the objects to which we refer */ void CVmObjFile::mark_refs(VMG_ uint state) { /* mark our character set object, if we have one */ if (get_ext()->charset != VM_INVALID_OBJ) G_obj_table->mark_all_refs(get_ext()->charset, state); /* mark our network file descriptor's references */ if (get_ext()->netfile != 0) get_ext()->netfile->mark_refs(vmg_ state); } /* ------------------------------------------------------------------------ */ /* * Note that we're seeking within the file. */ void CVmObjFile::note_file_seek(VMG_ int is_explicit) { /* * if it's an explicit seek, invalidate our internal read buffer and * note that the stdio buffers have been invalidated */ if (is_explicit) { /* the read buffer (if we have one) is invalid after a seek */ if (get_ext()->readbuf != 0) get_ext()->readbuf->rem = 0; /* * mark the last operation as clearing stdio buffering - when we * explicitly seek, stdio automatically invalidates its internal * buffers */ get_ext()->flags &= ~VMOBJFILE_STDIO_BUF_DIRTY; } } /* * Update stdio buffering for a switch between read and write mode, if * necessary. Any time we perform consecutive read and write operations, * stdio requires us to explicitly seek when performing consecutive * dissimilar operations. In other words, if we read then write, we must * seek before the write, and likewise if we write then read. */ void CVmObjFile::switch_read_write_mode(VMG_ int writing) { /* if we're writing, invalidate the read buffer */ if (writing && get_ext()->readbuf != 0) get_ext()->readbuf->rem = 0; /* * if we just performed a read or write operation, we must seek if * we're performing the opposite type of operation now */ if ((get_ext()->flags & VMOBJFILE_STDIO_BUF_DIRTY) != 0) { int was_writing; /* check what type of operation we did last */ was_writing = ((get_ext()->flags & VMOBJFILE_LAST_OP_WRITE) != 0); /* * if we're switching operations, explicitly seek to the current * location to flush the stdio buffers */ if ((writing && !was_writing) || (!writing && was_writing)) get_ext()->fp->seek(get_pos(vmg0_), OSFSK_SET); } /* * remember that this operation is stdio-buffered, so that we'll know * we need to seek if we perform the opposite operation after this one */ get_ext()->flags |= VMOBJFILE_STDIO_BUF_DIRTY; /* remember which type of operation we're performing */ if (writing) get_ext()->flags |= VMOBJFILE_LAST_OP_WRITE; else get_ext()->flags &= ~VMOBJFILE_LAST_OP_WRITE; } /* ------------------------------------------------------------------------ */ /* * Retrieve the filename argument, and optionally the access mode argument. * Sets up network storage server access to the file, if applicable. * Returns a network/local file descriptor that the caller can use to open * the local file system object. * * If '*access' is zero at entry, we'll retrieve the access argument from * the stack for a non-resource file. (Resource files always use access * mode READ, so there's no extra argument for those.) If '*access' is * non-zero, we'll simply use the given access mode. * * This leaves the argument values on the stack - the caller must discard * when done with them. */ CVmNetFile *CVmObjFile::get_filename_and_access( VMG_ const vm_rcdesc *rc, int *access, int is_resource_file, os_filetype_t file_type, const char *mime_type) { /* * If it's a resource file, the access mode is always READ, and there's * no access mode argument. If the caller provided a non-zero access * mode, there's also no argument - we just use the caller's mode. If * the caller specified zero as the mode, it means we have to read the * mode from an extra integer argument. */ if (is_resource_file) { /* resource file - the only access mode possible is READ */ *access = VMOBJFILE_ACCESS_READ; } else if (*access == 0) { /* get the access mode from the stack */ *access = G_stk->get(1)->num_to_int(vmg0_); /* make sure it's within the valid range for bytecode access codes */ if (*access > VMOBJFILE_ACCESS_USERMAX) err_throw(VMERR_BAD_VAL_BIF); } /* get the filename from the first argument */ return get_filename_arg( vmg_ G_stk->get(0), rc, *access, is_resource_file, file_type, mime_type); } /* * Get the filename for a given argument value and access mode */ CVmNetFile *CVmObjFile::get_filename_from_obj( VMG_ vm_obj_id_t obj, const vm_rcdesc *rc, int access, os_filetype_t file_type, const char *mime_type) { vm_val_t arg; arg.set_obj(obj); return get_filename_arg(vmg_ &arg, rc, access, FALSE, file_type, mime_type); } /* * Get the filename for a given argument value and access mode */ CVmNetFile *CVmObjFile::get_filename_arg( VMG_ const vm_val_t *arg, const vm_rcdesc *rc, int access, int is_resource_file, os_filetype_t file_type, const char *mime_type) { /* figure the network file access mode */ int nmode; switch (access) { case VMOBJFILE_ACCESS_READ: /* read access; file must exist */ nmode = NETF_READ; break; case VMOBJFILE_ACCESS_WRITE: /* write access; create or replace existing file */ nmode = NETF_WRITE | NETF_CREATE | NETF_TRUNC; break; case VMOBJFILE_ACCESS_RW_KEEP: /* read/write access; keep an existing file or create a new one */ nmode = NETF_READ | NETF_WRITE | NETF_CREATE; break; case VMOBJFILE_ACCESS_RW_TRUNC: /* read/write access; truncate an existing file or create a new one */ nmode = NETF_READ | NETF_WRITE | NETF_CREATE | NETF_TRUNC; break; case VMOBJFILE_ACCESS_DELETE: /* delete mode */ nmode = NETF_DELETE; break; case VMOBJFILE_ACCESS_GETINFO: case VMOBJFILE_ACCESS_MKDIR: case VMOBJFILE_ACCESS_RMDIR: case VMOBJFILE_ACCESS_READDIR: case VMOBJFILE_ACCESS_RENAME_TO: case VMOBJFILE_ACCESS_RENAME_FROM: /* * getFileType/getFileInfo/rename/directory manipulation - we don't * need to open the file at all for these operations */ nmode = 0; break; default: /* invalid mode */ err_throw(VMERR_BAD_VAL_BIF); } /* we don't have a network file descriptor yet */ CVmNetFile *netfile = 0; char fname[OSFNMAX]; /* check to see what kind of file spec we have */ if (is_resource_file) { /* a resource file must be given as a string name - retrieve it */ CVmBif::get_str_val_fname(vmg_ fname, sizeof(fname), CVmBif::get_str_val(vmg_ arg)); /* resources are always local, so create a local file descriptor */ netfile = CVmNetFile::open_local(vmg_ fname, 0, nmode, file_type); } else { /* regular file - create the network file descriptor */ netfile = CVmNetFile::open( vmg_ arg, rc, nmode, file_type, mime_type); } /* * If this isn't a resource file, check the file safety mode to see if * the operation is allowed. Reading resources is always allowed, * regardless of the safety mode, since resources are read-only and are * inherently constrained in the paths they can access. */ if (!is_resource_file) { err_try { check_safety_for_open(vmg_ netfile, access); } err_catch_disc { /* abandon the net file descriptor and bubble up the error */ netfile->abandon(vmg0_); err_rethrow(); } err_end; } /* return the network file descriptor */ return netfile; } /* * Resolve a special file ID to a local filename path */ int CVmObjFile::sfid_to_path(VMG_ char *buf, size_t buflen, int32_t sfid) { const char *fname = 0; char path[OSFNMAX]; switch (sfid) { case SFID_LIB_DEFAULTS: /* library defaults file - T3_APP_DATA/settings.txt */ fname = "settings.txt"; goto app_data_file; case SFID_WEBUI_PREFS: /* Web UI preferences - T3_APP_DATA/webprefs.txt */ fname = "webprefs.txt"; goto app_data_file; app_data_file: /* get the system application data path */ G_host_ifc->get_special_file_path( path, sizeof(path), OS_GSP_T3_APP_DATA); /* add the filename */ os_build_full_path(buf, buflen, path, fname); /* success */ return TRUE; default: /* invalid ID value */ buf[0] = '\0'; return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Pop a character set mapper argument. If there's no argument, we'll * create and return a mapper for the default character set. */ vm_obj_id_t CVmObjFile::get_charset_arg(VMG_ int argn, int argc) { /* presume we won't find a valid argument value */ vm_obj_id_t obj = VM_INVALID_OBJ; /* if there's an argument, pop it */ if (argn < argc) obj = CVmBif::get_charset_obj(vmg_ argn); /* if we don't have a character set yet, create the default */ if (obj == VM_INVALID_OBJ) { /* get the default local character set for file contents */ char csname[32]; os_get_charmap(csname, OS_CHARMAP_FILECONTENTS); /* create the mapper */ obj = CVmObjCharSet::create(vmg_ FALSE, csname, strlen(csname)); } /* return the character set */ return obj; } /* ------------------------------------------------------------------------ */ /* * Static property evaluator - open a text file */ int CVmObjFile::s_getp_open_text(VMG_ vm_val_t *retval, uint *in_argc, int is_resource_file, const vm_rcdesc *rc) { /* check arguments */ uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc_file(2, 1); static CVmNativeCodeDesc desc_res(1, 1); if (get_prop_check_argc(retval, in_argc, is_resource_file ? &desc_res : &desc_file)) return TRUE; /* * retrieve the filename and access mode arguments, and set up network * storage server access if applicable */ int access = 0; CVmNetFile *netfile = get_filename_and_access( vmg_ rc, &access, is_resource_file, OSFTTEXT, "text/plain"); err_try { /* presume we won't need a read buffer */ int create_readbuf = FALSE; /* retrieve the character set object */ vm_obj_id_t cset_obj = get_charset_arg(vmg_ 2, argc); /* push the character map object onto the stack for gc protection */ G_stk->push()->set_obj(cset_obj); /* open the file for reading or writing, as appropriate */ CVmDataSource *ds = 0; osfildef *fp = 0; switch(access) { case VMOBJFILE_ACCESS_READ: /* open a resource file or file system file, as appropriate */ if (is_resource_file) { /* it's a resource - open it */ unsigned long res_len; fp = G_host_ifc->find_resource( netfile->lclfname, strlen(netfile->lclfname), &res_len); /* * if we found the resource, note the start and end seek * positions, so we can limit reading of the underlying * file to the section that contains the resource data */ if (fp != 0) { /* * find_resource() handed us back a file handle * positioned at the start of the resource data stream, * possibly within a larger file; note the current * offset as the starting offset in case we need to * seek within the resource later */ long res_start = osfpos(fp); /* * note where the resource ends - it might be embedded * within a larger file, so we need to limit reading to * the extent from the resource map */ long res_end = res_start + res_len; /* create the data source */ ds = new CVmResFileSource(fp, res_start, res_end); } } else { /* * It's not a resource - open an ordinary text file for * reading. Even though we're going to treat the file as a * text file, open it in binary mode, since we'll do our * own universal newline translations; this allows us to * work with files in any character set, and using almost * any newline conventions, so files copied from other * systems will be fully usable even if they haven't been * fixed up to local conventions. */ fp = osfoprb(netfile->lclfname, OSFTTEXT); /* if that worked, create the data source */ if (fp != 0) ds = new CVmFileSource(fp); } /* make sure we opened it successfully */ if (fp == 0) G_interpreter->throw_new_class( vmg_ G_predef->file_not_found_exc, 0, "file not found"); /* we need a read buffer */ create_readbuf = TRUE; break; case VMOBJFILE_ACCESS_WRITE: /* open for writing */ fp = osfopwb(netfile->lclfname, OSFTTEXT); /* make sure we created it successfully */ if (fp == 0) G_interpreter->throw_new_class( vmg_ G_predef->file_creation_exc, 0, "error creating file"); /* create the data source */ ds = new CVmFileSource(fp); break; case VMOBJFILE_ACCESS_RW_KEEP: /* open for read/write, keeping existing contents */ fp = osfoprwb(netfile->lclfname, OSFTTEXT); /* make sure we were able to find or create the file */ if (fp == 0) G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc, 0, "error opening file"); /* create the data source */ ds = new CVmFileSource(fp); /* we need a read buffer */ create_readbuf = TRUE; break; case VMOBJFILE_ACCESS_RW_TRUNC: /* open for read/write, truncating existing contents */ fp = osfoprwtb(netfile->lclfname, OSFTTEXT); /* make sure we were successful */ if (fp == 0) G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc, 0, "error opening file"); /* create the data source */ ds = new CVmFileSource(fp); /* we need a read buffer */ create_readbuf = TRUE; break; default: /* invalid access mode */ fp = 0; err_throw(VMERR_BAD_VAL_BIF); } /* create our file object */ retval->set_obj(create(vmg_ FALSE, netfile, cset_obj, ds, VMOBJFILE_MODE_TEXT, access, create_readbuf)); /* * the network file descriptor is stored in the File object now, so * we no longer need to delete it */ netfile = 0; /* discard arguments and gc protection */ G_stk->discard(argc + 1); } err_finally { /* * If we created a network file descriptor and didn't hand it off * to the File object, we're abandoning the object. */ if (netfile != 0) netfile->abandon(vmg0_); } err_end; /* handled */ return TRUE; } /* * Static property evaluator - open a data file */ int CVmObjFile::s_getp_open_data(VMG_ vm_val_t *retval, uint *argc, const vm_rcdesc *rc) { /* use the generic binary file opener in 'data' mode */ return open_binary(vmg_ retval, argc, VMOBJFILE_MODE_DATA, FALSE, rc); } /* * Static property evaluator - open a raw file */ int CVmObjFile::s_getp_open_raw(VMG_ vm_val_t *retval, uint *argc, int is_resource_file, const vm_rcdesc *rc) { /* use the generic binary file opener in 'raw' mode */ return open_binary(vmg_ retval, argc, VMOBJFILE_MODE_RAW, is_resource_file, rc); } /* * Generic binary file opener - common to 'data' and 'raw' files */ int CVmObjFile::open_binary(VMG_ vm_val_t *retval, uint *in_argc, int mode, int is_resource_file, const vm_rcdesc *rc) { /* check arguments */ uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc file_desc(2); static CVmNativeCodeDesc res_desc(1); if (get_prop_check_argc(retval, in_argc, is_resource_file ? &res_desc : &file_desc)) return TRUE; /* retrieve the filename and access mode */ int access = 0; CVmNetFile *netfile = get_filename_and_access( vmg_ rc, &access, is_resource_file, OSFTBIN, "application/octet-stream"); err_try { osfildef *fp; CVmDataSource *ds = 0; /* open the file in binary mode, with the desired access type */ switch(access) { case VMOBJFILE_ACCESS_READ: /* open the resource or ordinary file, as appropriate */ if (is_resource_file) { unsigned long res_len; /* it's a resource - open it */ fp = G_host_ifc->find_resource( netfile->lclfname, strlen(netfile->lclfname), &res_len); /* * if we found the resource, note the start and end seek * positions, so we can limit reading of the underlying * file to the section that contains the resource data */ if (fp != 0) { /* * find_resource() hands us a file handle positioned at * the start of the resource data - note the seek * offset in case we need to seek around within the * resource stream later on */ long res_start = osfpos(fp); /* * note the end of the resource, in case it's embedded * within a larger physical file */ long res_end = res_start + res_len; /* create the data source */ ds = new CVmResFileSource(fp, res_start, res_end); } } else { /* open the ordinary file in binary mode for reading only */ fp = osfoprb(netfile->lclfname, OSFTBIN); /* create the data source */ if (fp != 0) ds = new CVmFileSource(fp); } /* make sure we were able to find it and open it */ if (fp == 0) G_interpreter->throw_new_class( vmg_ G_predef->file_not_found_exc, 0, "file not found"); break; case VMOBJFILE_ACCESS_WRITE: /* open in binary mode for writing only */ fp = osfopwb(netfile->lclfname, OSFTBIN); /* make sure we were able to create the file successfully */ if (fp == 0) G_interpreter->throw_new_class( vmg_ G_predef->file_creation_exc, 0, "error creating file"); /* create the data source */ ds = new CVmFileSource(fp); break; case VMOBJFILE_ACCESS_RW_KEEP: /* open for read/write, keeping existing contents */ fp = osfoprwb(netfile->lclfname, OSFTBIN); /* make sure we were able to find or create the file */ if (fp == 0) G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc, 0, "error opening file"); /* create the data source */ ds = new CVmFileSource(fp); break; case VMOBJFILE_ACCESS_RW_TRUNC: /* open for read/write, truncating existing contents */ fp = osfoprwtb(netfile->lclfname, OSFTBIN); /* make sure we were successful */ if (fp == 0) G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc, 0, "error opening file"); /* create the data source */ ds = new CVmFileSource(fp); break; default: fp = 0; err_throw(VMERR_BAD_VAL_BIF); } /* create our file object */ retval->set_obj(create( vmg_ FALSE, netfile, VM_INVALID_OBJ, ds, mode, access, FALSE)); /* * we've handed off the network file descriptor to the File object, * so we no longer have responsibility for it */ netfile = 0; } err_finally { /* * if we created a network file descriptor and didn't hand it off * to the new File object, we're abandoning it */ if (netfile != 0) netfile->abandon(vmg0_); } err_end; /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Check the safety settings to determine if an open operation is allowed. * If the access is not allowed, we'll throw an error. */ void CVmObjFile::check_safety_for_open(VMG_ CVmNetFile *nf, int access) { /* * Special files are inherently sandboxed, since the system determines * their paths. These are always allowed for ordinary read/write. */ if (nf->sfid != 0 && (access == VMOBJFILE_ACCESS_READ || access == VMOBJFILE_ACCESS_WRITE || access == VMOBJFILE_ACCESS_RW_KEEP || access == VMOBJFILE_ACCESS_RW_TRUNC || access == VMOBJFILE_ACCESS_GETINFO || access == VMOBJFILE_ACCESS_READDIR)) return; /* * Temporary file paths can only be obtained from the VM. Ordinary * read/write operations are allowed, as is delete. */ if (nf->is_temp && (access == VMOBJFILE_ACCESS_READ || access == VMOBJFILE_ACCESS_WRITE || access == VMOBJFILE_ACCESS_RW_KEEP || access == VMOBJFILE_ACCESS_RW_TRUNC || access == VMOBJFILE_ACCESS_DELETE || access == VMOBJFILE_ACCESS_GETINFO || access == VMOBJFILE_ACCESS_READDIR)) return; /* * Network files are subject to separate permission rules enforced by * the storage server. Local file safety settings don't apply. */ if (nf->is_net_file()) return; /* * If the file was specifically selected by the user via a file dialog, * allow access of the type proposed by the dialog, even if it's * outside the sandbox. The user has implicitly granted us permission * on this individual file by manually selecting it via a UI * interaction. */ CVmObjFileName *ofn; if ((ofn = vm_objid_cast(CVmObjFileName, nf->filespec)) != 0 && ofn->get_from_ui() != 0) { /* grant permission based on the dialog mode */ switch (ofn->get_from_ui()) { case OS_AFP_OPEN: /* allow reading for a file selected with an Open dialog */ if (access == VMOBJFILE_ACCESS_READ) return; break; case OS_AFP_SAVE: /* allow writing for a file selected with a Save dialog */ if (access == VMOBJFILE_ACCESS_WRITE) return; break; } /* * For anything we didn't override above based on the dialog mode, * don't give up - the file safety permissions might still allow * it. All we've determined at this point is that the dialog * selection doesn't grant extraordinary permission for the * attempted access mode. */ } /* * The file wasn't specified by a type that confers any special * privileges, so determine if it's accessible according to the local * file name's handling under the file safety and sandbox rules. */ return check_safety_for_open(vmg_ nf->lclfname, access); } /* * Check file safety for opening the given local file path. */ void CVmObjFile::check_safety_for_open(VMG_ const char *lclfname, int access) { /* check the safety settings */ if (!query_safety_for_open(vmg_ lclfname, access)) { /* this operation is not allowed - throw an error */ G_interpreter->throw_new_class(vmg_ G_predef->file_safety_exc, 0, "prohibited file access"); } } /* * Query the safety settings. Returns true if the access is allowed, false * if it's prohibited. */ int CVmObjFile::query_safety_for_open(VMG_ const char *lclfname, int access) { /* get the current file safety level from the host application */ int read_level = G_host_ifc->get_io_safety_read(); int write_level = G_host_ifc->get_io_safety_write(); /* * First check to see if the safety level allows or disallows the * requested access without regard to location. If so, we can save a * little work by skipping the sandbox resolution. */ switch (access) { case VMOBJFILE_ACCESS_READ: case VMOBJFILE_ACCESS_GETINFO: case VMOBJFILE_ACCESS_READDIR: /* * Read a file or its metadata. If the safety level is READ_ANY or * below, we can read any file regardless of location. If it's * MAXIMUM, we can't read anything. */ if (read_level <= VM_IO_SAFETY_READ_ANY_WRITE_CUR) return TRUE; if (read_level >= VM_IO_SAFETY_MAXIMUM) return FALSE; break; case VMOBJFILE_ACCESS_RMDIR: case VMOBJFILE_ACCESS_WRITE: case VMOBJFILE_ACCESS_RW_KEEP: case VMOBJFILE_ACCESS_RW_TRUNC: case VMOBJFILE_ACCESS_DELETE: case VMOBJFILE_ACCESS_MKDIR: case VMOBJFILE_ACCESS_RENAME_FROM: case VMOBJFILE_ACCESS_RENAME_TO: /* * Writing, deleting, renaming, creating/removing a directory. If * the safety level is MINIMUM, we're allowed to write anywhere. * If it's READ_CUR or higher, writing isn't allowed anywhere. */ if (write_level <= VM_IO_SAFETY_MINIMUM) return TRUE; if (write_level >= VM_IO_SAFETY_READ_CUR) return FALSE; break; } /* * If we get this far, it means we're in sandbox mode, so the access is * conditional on the file's location relative to the sandbox folder. * Get the sandbox path, defaulting to the file base path. */ const char *sandbox = (G_sandbox_path != 0 ? G_sandbox_path : G_file_path); /* assume the file isn't in the sandbox */ int in_sandbox = FALSE; /* * Most operations in sandbox mode don't consider the sandbox directory * itself to be part of the sandbox - e.g., you can't delete the * sandbox folder. */ int match_self = FALSE; /* * The effective sandbox extends beyond the actual contents the sandbox * directory for a couple of modes: * * - For READDIR mode (reading the contents of a directory), the * sandbox directory itself is considered part of the sandbox, since * the information we're accessing in creating a directory listing is * really the directory's contents. * * - For GETINFO mode (read file metadata), extend the sandbox to * include the sandbox directory itself, its parent, its parent's * parent, etc. It's pretty obvious that we should have metadata read * access to the sandbox directory itself, given that we can list and * access its contents. The rationale for also including parents of * the sandbox folder is that they're effectively part of its metadata, * since they're visible in its full path name. */ switch (access) { case VMOBJFILE_ACCESS_GETINFO: /* * read metadata - the sandbox directory is part of the sandbox for * this operation, as is its parent, its parent's parent, etc * (which is to say, any folder that contains the sandbox) */ in_sandbox |= os_is_file_in_dir(sandbox, lclfname, TRUE, TRUE); break; case VMOBJFILE_ACCESS_READDIR: /* * read directory contents - the sandbox directory itself is part * of the sandbox for this mode */ match_self = TRUE; break; } /* * Check to see if the file is in the sandbox folder (or any subfolder * thereof). If not, we may have to disallow the operation based on * safety level settings, but for now just note the location status. */ in_sandbox |= os_is_file_in_dir(lclfname, sandbox, TRUE, match_self); /* if the file isn't in the sandbox, access isn't allowed */ if (!in_sandbox) return FALSE; /* didn't find any problems - allow the access */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - get the character set */ int CVmObjFile::getp_get_charset(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* if we have a character set object, return it; otherwise return nil */ if (get_ext()->charset != VM_INVALID_OBJ) retval->set_obj(get_ext()->charset); else retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - set the character set */ int CVmObjFile::getp_set_charset(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* retrieve and save the character set argument */ get_ext()->charset = get_charset_arg(vmg_ 0, 1); /* discard arguments */ G_stk->discard(); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Check that we're in a valid state to perform file operations. */ void CVmObjFile::check_valid_file(VMG0_) { /* if we're 'out of sync', we can't perform any operations on it */ if ((get_ext()->flags & VMOBJFILE_OUT_OF_SYNC) != 0) G_interpreter->throw_new_class(vmg_ G_predef->file_sync_exc, 0, "file out of sync"); /* if the file has been closed, we can't perform any operations on it */ if (get_ext()->fp == 0) G_interpreter->throw_new_class(vmg_ G_predef->file_closed_exc, 0, "operation attempted on closed file"); } /* * Check that we have read access */ void CVmObjFile::check_read_access(VMG0_) { /* we have read access unless we're in write-only mode */ if (get_ext()->access == VMOBJFILE_ACCESS_WRITE) G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc, 0, "wrong file mode"); } /* * Check that we have write access */ void CVmObjFile::check_write_access(VMG0_) { /* we have write access unless we're in read-only mode */ if (get_ext()->access == VMOBJFILE_ACCESS_READ) G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc, 0, "wrong file mode"); } /* * Check that we're in raw mode */ void CVmObjFile::check_raw_mode(VMG0_) { /* if we're not in raw mode, throw an error */ if (get_ext()->mode != VMOBJFILE_MODE_RAW) G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc, 0, "wrong file mode"); } /* * Check that we've a valid raw readable file */ void CVmObjFile::check_raw_read(VMG0_) { check_valid_file(vmg0_); check_read_access(vmg0_); check_raw_mode(vmg0_); } /* * Check that we've a valid raw writable file */ void CVmObjFile::check_raw_write(VMG0_) { check_valid_file(vmg0_); check_write_access(vmg0_); check_raw_mode(vmg0_); } /* ------------------------------------------------------------------------ */ /* * Property evaluator - close the file */ int CVmObjFile::getp_close_file(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* * take over the network file descriptor from the File object - we're * going to either close it or abandon it, which deletes it, so we * don't need to delete it again when the File object is collected */ CVmNetFile *netfile = get_ext()->netfile; get_ext()->netfile = 0; /* * Explicitly flush the file, to detect any errors flushing our final * write buffers. The regular osifc close ignores errors, so we have * to check with an explicit flush if we want this information. */ err_try { /* flush buffers */ get_ext()->fp->flush(); } err_catch_disc { /* * The flush failed, so the close effectively failed. Close the * file anyway to release system resources. */ delete get_ext()->fp; /* * Abandon the network file object. Since we failed to close the * local copy properly, we don't want to update the server copy (if * it exists) with corrupt data. */ if (netfile != 0) netfile->abandon(vmg0_); /* rethrow the error */ err_rethrow(); } err_end; /* close and explicitly forget the data source */ delete get_ext()->fp; get_ext()->fp = 0; /* if there's a network file, close it */ if (netfile != 0) netfile->close(vmg0_); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - read from the file */ int CVmObjFile::getp_read_file(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* push a self-reference for gc protection */ G_stk->push()->set_obj(self); /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* check that we have read access */ check_read_access(vmg0_); /* note the implicit seeking */ note_file_seek(vmg_ FALSE); /* flush stdio buffers as needed and note the read operation */ switch_read_write_mode(vmg_ FALSE); /* read according to our mode */ switch(get_ext()->mode) { case VMOBJFILE_MODE_TEXT: /* read a line of text */ read_text_mode(vmg_ retval); break; case VMOBJFILE_MODE_DATA: /* read in data mode */ read_data_mode(vmg_ retval); break; case VMOBJFILE_MODE_RAW: /* can't use this call on this type of file */ G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc, 0, "wrong file mode"); break; } /* discard the gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* * Read a value in text mode */ void CVmObjFile::read_text_mode(VMG_ vm_val_t *retval) { /* get our file data source and read buffer from the extension */ CVmDataSource *fp = get_ext()->fp; vmobjfile_readbuf_t *readbuf = get_ext()->readbuf; /* get our character mapper */ CCharmapToUni *charmap = ((CVmObjCharSet *)vm_objp(vmg_ get_ext()->charset)) ->get_to_uni(vmg0_); /* replenish the buffer if it's empty */ if (readbuf->rem == 0 && !readbuf->refill(fp)) { /* end of file - return nil */ retval->set_nil(); return; } /* * Allocate a string object for the result. We have no idea how long * the line will be, so start with an arbitrary guess; we'll adjust the * buffer up or down as needed. */ vm_obj_id_t strid = CVmObjString::create(vmg_ FALSE, 128); CVmObjString *str = (CVmObjString *)vm_objp(vmg_ strid); char *dst = str->cons_get_buf(); /* this string will be the return value */ retval->set_obj(strid); /* push it for gc protection */ G_stk->push(retval); /* * Read a line of text from the file into our buffer. Keep going * until we read an entire line; we might have to read the line in * chunks, since the line might end up being longer than our buffer. */ for (;;) { /* read the next character; if we can't, we're at EOF */ wchar_t ch; size_t chlen; if (!readbuf->getch(ch, fp, charmap)) break; /* if this is a CR, LF, or U+2028, it's the end of the line */ if (ch == '\n' || ch == '\r' || ch == 0x2028) { /* newline - skip CR-LF and LR-CR pairs */ wchar_t nxt = (ch == '\n' ? '\r' : ch == '\r' ? '\n' : 0); size_t b; if (nxt != 0 && readbuf->peekch(ch, fp, charmap, b) && ch == nxt) readbuf->commit_peek(b); /* whichever type of newline we read, store it as LF */ ch = 10; chlen = 1; } else { /* ordinary character - figure the output character length */ chlen = utf8_ptr::s_wchar_size(ch); } /* ensure we have enough space for it in our string */ dst = str->cons_ensure_space(vmg_ dst, chlen, 128); /* add this character to the string */ dst += utf8_ptr::s_putch(dst, ch); /* if we just read a newline, we're done */ if (ch == '\n') break; } /* close the string */ str->cons_shrink_buffer(vmg_ dst); /* * we now can discard the string we've been keeping on the stack to * for garbage collection protection */ G_stk->discard(); } /* * Read a value in 'data' mode */ void CVmObjFile::read_data_mode(VMG_ vm_val_t *retval) { char buf[32]; CVmObjString *str_obj; vm_obj_id_t str_id; CVmDataSource *fp = get_ext()->fp; /* read the type flag */ if (fp->read(buf, 1)) { /* end of file - return nil */ retval->set_nil(); return; } /* see what we have */ switch(buf[0]) { case VMOBJFILE_TAG_INT: /* read the INT4 value */ if (fp->read(buf, 4)) goto io_error; /* set the integer value from the buffer */ retval->set_int(osrp4s(buf)); break; case VMOBJFILE_TAG_ENUM: /* read the UINT4 value */ if (fp->read(buf, 4)) goto io_error; /* set the 'enum' value */ retval->set_enum(t3rp4u(buf)); break; case VMOBJFILE_TAG_STRING: /* * read the string's length - note that this length is two * higher than the actual length of the string, because it * includes the length prefix bytes */ if (fp->read(buf, 2)) goto io_error; /* * allocate a new string of the required size (deducting two * bytes from the indicated size, since the string allocator * only wants to know about the bytes of the string we want to * store, not the length prefix part) */ str_id = CVmObjString::create(vmg_ FALSE, osrp2(buf) - 2); str_obj = (CVmObjString *)vm_objp(vmg_ str_id); /* read the bytes of the string into the object's buffer */ if (fp->read(str_obj->cons_get_buf(), osrp2(buf) - 2)) goto io_error; /* success - set the string return value, and we're done */ retval->set_obj(str_id); break; case VMOBJFILE_TAG_TRUE: /* it's a simple 'true' value */ retval->set_true(); break; case VMOBJFILE_TAG_BIGNUM: /* read the BigNumber value and return a new BigNumber object */ if (CVmObjBigNum::read_from_data_file(vmg_ retval, fp)) goto io_error; break; case VMOBJFILE_TAG_BYTEARRAY: /* read the ByteArray value and return a new ByteArray object */ if (CVmObjByteArray::read_from_data_file(vmg_ retval, fp)) goto io_error; break; default: /* invalid data - throw an error */ G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc, 0, "file I/O error"); } /* done */ return; io_error: /* * we'll come here if we read the type tag correctly but encounter * an I/O error reading the value - this indicates a corrupted input * stream, so throw an I/O error */ G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc, 0, "file I/O error"); } /* * Internal read routine */ int CVmObjFile::read_file(VMG_ char *buf, int32_t &len) { /* get the data source */ CVmDataSource *fp = get_ext()->fp; /* make sure it's valid */ if ((get_ext()->flags & VMOBJFILE_OUT_OF_SYNC) != 0 || fp == 0) return FALSE; /* deal with stdio buffering if we're changing modes */ switch_read_write_mode(vmg_ FALSE); /* read according to the mode */ switch (get_ext()->mode) { case VMOBJFILE_MODE_TEXT: { /* get the read buffer and character mapper */ vmobjfile_readbuf_t *readbuf = get_ext()->readbuf; CCharmapToUni *charmap = ((CVmObjCharSet *)vm_objp(vmg_ get_ext()->charset)) ->get_to_uni(vmg0_); /* loop until we satisfy the request */ int32_t actual; for (actual = 0 ; ; ) { /* peek at the next character */ wchar_t ch; size_t b; if (!readbuf->peekch(ch, fp, charmap, b)) break; /* make sure it'll fit */ size_t csiz = utf8_ptr::s_wchar_size(ch); if (actual + (int32_t)csiz > len) break; /* store this character */ actual += utf8_ptr::s_putch(buf + actual, ch); /* advance to the next character */ readbuf->commit_peek(b); } /* set the actual return length */ len = actual; } break; case VMOBJFILE_MODE_RAW: /* do the direct read */ len = fp->readc(buf, (size_t)len); break; default: /* can't handle others */ return FALSE; } /* success */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - write to the file */ int CVmObjFile::getp_write_file(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(1); const vm_val_t *argval; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* * get a pointer to the argument value, but leave it on the stack * for now to protect against losing it in garbage collection */ argval = G_stk->get(0); /* push a self-reference for gc protection */ G_stk->push()->set_obj(self); /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* check that we have write access */ check_write_access(vmg0_); /* deal with stdio buffering if we're changing modes */ switch_read_write_mode(vmg_ TRUE); /* read according to our mode */ switch(get_ext()->mode) { case VMOBJFILE_MODE_TEXT: /* read a line of text */ write_text_mode(vmg_ argval); break; case VMOBJFILE_MODE_DATA: /* read in data mode */ write_data_mode(vmg_ argval); break; case VMOBJFILE_MODE_RAW: /* can't use this call on this type of file */ G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc, 0, "wrong file mode"); break; } /* discard our gc protection and argument */ G_stk->discard(2); /* no return value - return nil by default */ retval->set_nil(); /* handled */ return TRUE; } /* * Write a value in text mode */ void CVmObjFile::write_text_mode(VMG_ const vm_val_t *val) { char conv_buf[128]; vm_val_t new_str; CCharmapToLocal *charmap; const char *constp; const char *p, *startp; size_t rem; /* get our character mapper */ charmap = ((CVmObjCharSet *)vm_objp(vmg_ get_ext()->charset)) ->get_to_local(vmg0_); /* convert the value to a string */ constp = CVmObjString::cvt_to_str( vmg_ &new_str, conv_buf, sizeof(conv_buf), val, 10, 0); /* protect the new string (if any) from gc while working */ G_stk->push(&new_str); /* scan for newlines - we need to write newline sequences specially */ for (startp = constp + VMB_LEN, rem = vmb_get_len(constp) ; rem != 0 ; ) { /* scan to the next newline */ for (p = startp ; rem != 0 && *p != '\n' ; ++p, --rem) ; /* write this chunk through the character mapper */ if (p != startp && charmap->write_file(get_ext()->fp, startp, p - startp)) { /* the write failed - throw an I/O exception */ G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc, 0, "file I/O error"); } /* write the newline, if applicable */ if (rem != 0 && charmap->write_file(get_ext()->fp, OS_NEWLINE_SEQ, strlen(OS_NEWLINE_SEQ))) { /* the write failed - throw an I/O exception */ G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc, 0, "file I/O error"); } /* if we're at a newline, skip it */ if (rem != 0) ++p, --rem; /* note where the new chunk starts */ startp = p; } /* done with the gc protection */ G_stk->discard(); /* invalidate any read buffer */ note_file_seek(vmg_ FALSE); } /* * Write a value in 'data' mode */ void CVmObjFile::write_data_mode(VMG_ const vm_val_t *val) { char buf[32]; CVmDataSource *fp = get_ext()->fp; vm_val_t new_str; const char *constp; /* see what type of data we want to put */ switch(val->typ) { case VM_INT: /* put the type in the buffer */ buf[0] = VMOBJFILE_TAG_INT; /* add the value in INT4 format */ oswp4s(buf + 1, val->val.intval); /* write out the type prefix plus the value */ if (fp->write(buf, 5)) goto io_error; /* done */ break; case VM_ENUM: /* put the type in the buffer */ buf[0] = VMOBJFILE_TAG_ENUM; /* add the value in INT4 format */ oswp4(buf + 1, val->val.enumval); /* write out the type prefix plus the value */ if (fp->write(buf, 5)) goto io_error; /* done */ break; case VM_SSTRING: /* get the string value pointer */ constp = val->get_as_string(vmg0_); write_binary_string: /* write the type prefix byte */ buf[0] = VMOBJFILE_TAG_STRING; if (fp->write(buf, 1)) goto io_error; /* * write the length prefix - for TADS 2 compatibility, include * the bytes of the prefix itself in the length count */ oswp2(buf, vmb_get_len(constp) + 2); if (fp->write(buf, 2)) goto io_error; /* write the string's bytes */ if (fp->write(constp + VMB_LEN, vmb_get_len(constp))) goto io_error; /* done */ break; case VM_OBJ: /* * Write BigNumber and ByteArray types in special formats. For * other types, try converting to a string. */ if (CVmObjBigNum::is_bignum_obj(vmg_ val->val.obj)) { CVmObjBigNum *bignum; /* we know it's a BigNumber - cast it properly */ bignum = (CVmObjBigNum *)vm_objp(vmg_ val->val.obj); /* write the type tag */ buf[0] = VMOBJFILE_TAG_BIGNUM; if (fp->write(buf, 1)) goto io_error; /* write it out */ if (bignum->write_to_data_file(fp)) goto io_error; } else if (CVmObjByteArray::is_byte_array(vmg_ val->val.obj)) { CVmObjByteArray *bytarr; /* we know it's a ByteArray - cast it properly */ bytarr = (CVmObjByteArray *)vm_objp(vmg_ val->val.obj); /* write the type tag */ buf[0] = VMOBJFILE_TAG_BYTEARRAY; if (fp->write(buf, 1)) goto io_error; /* write the array */ if (bytarr->write_to_data_file(fp)) goto io_error; } else { /* * Cast it to a string value and write that out. Note that * we can ignore garbage collection for any new string we've * created, since we're just calling the OS-level file * writer, which will never invoke garbage collection. */ constp = vm_objp(vmg_ val->val.obj) ->cast_to_string(vmg_ val->val.obj, &new_str); goto write_binary_string; } break; case VM_TRUE: /* * All we need for this is the type tag. Note that we can't * write nil because we'd have no way of reading it back in - a * nil return from file_read indicates that we've reached the * end of the file. So there's no point in writing nil to a * file. */ buf[0] = VMOBJFILE_TAG_TRUE; if (fp->write(buf, 1)) goto io_error; /* done */ break; default: /* other types are not acceptable */ err_throw(VMERR_BAD_TYPE_BIF); } /* done */ return; io_error: /* the write failed - throw an i/o exception */ G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc, 0, "file I/O error"); } /* ------------------------------------------------------------------------ */ /* * Property evaluator - read raw bytes from the file */ int CVmObjFile::getp_read_bytes(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { static CVmNativeCodeDesc desc(1, 2); uint argc = (in_argc != 0 ? *in_argc : 0); vm_val_t arr_val; CVmObjByteArray *arr; unsigned long idx; unsigned long len; /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* check that we have read access */ check_read_access(vmg0_); /* we can only use this call on 'raw' files */ if (get_ext()->mode != VMOBJFILE_MODE_RAW) G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc, 0, "wrong file mode"); /* retrieve the ByteArray destination */ G_stk->pop(&arr_val); /* make sure it's really a ByteArray object */ if (arr_val.typ != VM_OBJ || !CVmObjByteArray::is_byte_array(vmg_ arr_val.val.obj)) err_throw(VMERR_BAD_TYPE_BIF); /* we know it's a byte array object, so cast it */ arr = (CVmObjByteArray *)vm_objp(vmg_ arr_val.val.obj); /* presume we'll try to fill the entire array */ idx = 1; len = arr->get_element_count(); /* if we have a starting index argument, retrieve it */ if (argc >= 2) idx = (unsigned long)CVmBif::pop_int_val(vmg0_); /* if we have a length argument, retrieve it */ if (argc >= 3) len = (unsigned long)CVmBif::pop_int_val(vmg0_); /* push a self-reference for gc protection */ G_stk->push()->set_obj(self); /* note the implicit seeking */ note_file_seek(vmg_ FALSE); /* deal with stdio buffering issues if necessary */ switch_read_write_mode(vmg_ FALSE); /* * read the data into the array, and return the number of bytes we * actually manage to read */ retval->set_int(arr->read_from_file( vmg_ arr_val.val.obj, get_ext()->fp, idx, len, TRUE)); /* discard our gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - write raw bytes to the file */ int CVmObjFile::getp_write_bytes(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { static CVmNativeCodeDesc desc(1, 2); uint argc = (in_argc != 0 ? *in_argc : 0); vm_val_t srcval; CVmObjByteArray *arr = 0; CVmObjFile *file = 0; unsigned long idx, has_idx = FALSE; unsigned long len; /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* make sure it's a valid file, with write access, in raw mode */ check_valid_file(vmg0_); check_write_access(vmg0_); check_raw_mode(vmg0_); /* pop the source object argument */ G_stk->pop(&srcval); if (srcval.typ != VM_OBJ) err_throw(VMERR_OBJ_VAL_REQD); /* check the object type */ if (CVmObjByteArray::is_byte_array(vmg_ srcval.val.obj)) { /* get the ByteArray object, suitably cast */ arr = (CVmObjByteArray *)vm_objp(vmg_ srcval.val.obj); /* assume we'll write the entire byte array */ idx = 1; len = arr->get_element_count(); } else if (CVmObjFile::is_file_obj(vmg_ srcval.val.obj)) { /* get the file object, suitably cast */ file = (CVmObjFile *)vm_objp(vmg_ srcval.val.obj); /* make sure it's in raw mode */ if (file->get_ext()->mode != VMOBJFILE_MODE_RAW) G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc, 0, "wrong file mode"); /* assume we'll copy the file from the current seek offset onward */ idx = file->get_pos(vmg0_); len = file->get_file_size(vmg0_); } else { /* invalid source type */ err_throw(VMERR_BAD_TYPE_BIF); } /* if we have a starting index, retrieve it */ if (argc >= 2) { /* if it's nil, just ignore it, otherwise pop the int value */ if (G_stk->get(0)->typ == VM_NIL) G_stk->discard(); else { idx = (unsigned long)CVmBif::pop_int_val(vmg0_); has_idx = TRUE; } } /* if we have a length, retrieve it */ if (argc >= 3) { /* if it's nil, just ignore it, otherwise pop the int value */ if (G_stk->get(0)->typ == VM_NIL) G_stk->discard(); else len = (unsigned long)CVmBif::pop_int_val(vmg0_); } /* push a self-reference and a source reference for gc protection */ G_stk->push()->set_obj(self); G_stk->push(&srcval); /* flush stdio buffers as needed and note the read operation */ switch_read_write_mode(vmg_ TRUE); /* do the copy according to the source type */ if (arr != 0) { /* * write the bytes to the file - on success (zero write_to_file * return), return nil, on failure (non-zero write_to_file return), * return true */ if (arr->write_to_file(get_ext()->fp, idx, len)) { /* we failed to write the bytes - throw an I/O exception */ G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc, 0, "file I/O error"); } } else if (file != 0) { /* * if there's an explicit starting seek position, go there; if it * wasn't specified, the default is the current seek location, so * we just leave well enough alone in that case */ if (has_idx) file->set_pos(vmg_ idx); /* copy the requested byte range */ while (len != 0) { /* read the next chunk of bytes */ char buf[1024]; int32_t cur = (len < (int32_t)sizeof(buf) ? len : sizeof(buf)); if (!file->read_file(vmg_ buf, cur)) G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc, 0, "file I/O error"); /* if we read zero bytes, we've reached EOF */ if (cur == 0) break; /* write out this chunk */ if (get_ext()->fp->write(buf, (size_t)cur)) G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc, 0, "file I/O error"); /* deduct this hunk from the remaining length */ len -= cur; } } /* discard our gc protection */ G_stk->discard(2); /* no return value - return nil by default */ retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - get the seek position */ int CVmObjFile::getp_get_pos(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* get the position */ retval->set_int(get_pos(vmg0_)); /* handled */ return TRUE; } /* * Get the current file position */ long CVmObjFile::get_pos(VMG0_) { /* get the seek position in the underlying file */ long pos = get_ext()->fp->get_pos(); /* * if we have a read buffer, adjust for buffering: the underlying file * corresponds to the end of the read buffer, so subtract the number of * bytes remaining in the read buffer */ if (get_ext()->readbuf != 0) pos -= get_ext()->readbuf->rem; /* return the result */ return pos; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - set the seek position */ int CVmObjFile::getp_set_pos(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(1); unsigned long pos; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* note the seeking operation */ note_file_seek(vmg_ TRUE); /* retrieve the target seek position */ pos = CVmBif::pop_long_val(vmg0_); /* seek to the new position */ get_ext()->fp->seek(pos, OSFSK_SET); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* * Set position - internal version */ int CVmObjFile::set_pos(VMG_ long pos) { /* make sure it's valid */ if ((get_ext()->flags & VMOBJFILE_OUT_OF_SYNC) != 0 || get_ext()->fp == 0) return FALSE; /* note the seek */ note_file_seek(vmg_ TRUE); /* set the seek position */ return !get_ext()->fp->seek(pos, OSFSK_SET); } /* ------------------------------------------------------------------------ */ /* * Property evaluator - set position to end of file */ int CVmObjFile::getp_set_pos_end(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* note the seeking operation */ note_file_seek(vmg_ TRUE); /* seek to the end position */ get_ext()->fp->seek(0, OSFSK_END); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - get size */ int CVmObjFile::getp_get_size(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* get the size */ retval->set_int(get_file_size(vmg0_)); /* handled */ return TRUE; } /* * Get the size, internal version */ long CVmObjFile::get_file_size(VMG0_) { /* make sure it's valid */ if ((get_ext()->flags & VMOBJFILE_OUT_OF_SYNC) != 0 || get_ext()->fp == 0) return -1; /* note the seeking operation */ note_file_seek(vmg_ TRUE); /* get the size */ return get_ext()->fp->get_size(); } /* ------------------------------------------------------------------------ */ /* * Property evaluator - get the file mode */ int CVmObjFile::getp_get_mode(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* return the mode */ retval->set_int(get_ext()->mode); /* handled */ return TRUE; } /* * Get the mode, internal version */ int CVmObjFile::get_file_mode(VMG0_) { return get_ext()->mode; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - setMode - change the file mode */ int CVmObjFile::getp_setMode(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ static CVmNativeCodeDesc desc(1, 1); int argc = (oargc != 0 ? *oargc : 0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* retrieve the mode argument */ int mode = CVmBif::pop_int_val(vmg0_); --argc; /* presume no character set or read buffer */ vm_obj_id_t cset = VM_INVALID_OBJ; int create_readbuf = FALSE; /* save the retained settings from the current extension */ vmobjfile_ext_t *ext = get_ext(); CVmDataSource *fp = ext->fp; CVmNetFile *netfile = ext->netfile; unsigned long flags = ext->flags; int access = ext->access; /* check the mode */ switch (mode) { case VMOBJFILE_MODE_TEXT: /* text mode - we need a character mapper in this mode */ cset = get_charset_arg(vmg_ 0, argc); /* * if we're open with read access, we need a read buffer in order * to do the character set translation */ if (access == VMOBJFILE_ACCESS_READ) create_readbuf = TRUE; break; case VMOBJFILE_MODE_DATA: case VMOBJFILE_MODE_RAW: /* * data mode or raw mode - we don't use a character mapper for this * mode, so if the argument is present, simply discard it */ if (argc >= 2) G_stk->discard(); break; default: /* invalid mode */ err_throw(VMERR_BAD_VAL_BIF); } /* push the character set object (if any) for gc protection */ G_stk->push_obj_or_nil(vmg_ cset); /* * remove the data source and network file descriptor from the * extension, so that they're not deleted when we reallocate it */ ext->fp = 0; ext->netfile = 0; /* * Reallocate the extension. This will set up the correct structures * for the new file mode. */ alloc_ext(vmg_ netfile, cset, fp, flags, mode, access, create_readbuf); /* discard arguments and gc protection */ G_stk->discard(argc + 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getRootName property evaluator - get the root filename of a given * string. This is a static (class) method. */ int CVmObjFile::s_getp_getRootName(VMG_ vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(1); char buf[OSFNMAX*2]; const char *ret; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the string argument into our buffer */ CVmBif::pop_fname_val(vmg_ buf, sizeof(buf)); /* set up a no-access file descriptor to determine where the file is */ CVmNetFile *nf = CVmNetFile::open(vmg_ buf, 0, 0, OSFTUNK, 0); /* * If the file is on the network storage server, we always use the Unix * convention, regardless of the local naming rules. Otherwise, use * the local OS API to get the root name. */ if (nf->is_net_file()) { /* * We're in network mode, so use the Unix path conventions. Simply * scan for the last '/' character; if we find it, the root name is * the part after the '/', otherwise it's the whole string. */ const char *sl = strrchr(buf, '/'); ret = (sl != 0 ? sl + 1 : buf); } else { /* it's not a network file - use the local filename rules */ ret = os_get_root_name(buf); } /* done with the net file desriptor */ nf->abandon(vmg0_); /* return the string */ retval->set_obj(CVmObjString::create(vmg_ FALSE, ret, strlen(ret))); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * deleteFile() property evaluator - deletes a file */ int CVmObjFile::s_getp_deleteFile(VMG_ vm_val_t *retval, uint *in_argc) { /* check arguments */ uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* get the filename; use the DELETE access mode */ vm_rcdesc rc(vmg_ "File.deleteFile", CVmObjFile::metaclass_reg_->get_class_obj(vmg0_), VMOBJFILE_DELETE_FILE, G_stk->get(0), argc); int access = VMOBJFILE_ACCESS_DELETE; CVmNetFile *netfile = get_filename_and_access( vmg_ &rc, &access, FALSE, OSFTUNK, "application/octet-stream"); /* close the network file descriptor - this will delete the file */ netfile->close(vmg0_); /* discard arguments */ G_stk->discard(1); /* no return */ retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - unpackBytes */ int CVmObjFile::getp_unpackBytes(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* check that the it's a valid file open with read access in "raw" mode */ check_raw_read(vmg0_); /* get the format string, but leave it on the stack */ const char *fmt = G_stk->get(0)->get_as_string(vmg0_); if (fmt == 0) err_throw(VMERR_BAD_TYPE_BIF); /* get the format string pointer and length */ size_t fmtlen = vmb_get_len(fmt); fmt += VMB_LEN; /* save 'self' for gc protection */ G_stk->push_obj(vmg_ self); /* unpack the data from our file stream into a list, returning the list */ CVmPack::unpack(vmg_ retval, fmt, fmtlen, get_ext()->fp); /* discard our arguments and gc protection */ G_stk->discard(argc + 1); /* handled */ return TRUE; } /* * property evaluator - packBytes */ int CVmObjFile::getp_packBytes(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1, 0, TRUE); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* check that the it's a valid file open with write access in "raw" mode */ check_raw_write(vmg0_); /* save 'self' for gc protection */ G_stk->push_obj(vmg_ self); /* remember the starting seek position */ long start = get_pos(vmg0_); /* pack the arguments into our file data stream */ CVmPack::pack(vmg_ 1, argc, get_ext()->fp); /* discard our gc protection and our arguments */ G_stk->discard(argc + 1); /* return the number of bytes packed */ retval->set_int(get_pos(vmg0_) - start); /* handled */ return TRUE; } /* * property evaluator - sha256 */ int CVmObjFile::getp_sha256(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* check that the it's a valid file open with read access in "raw" mode */ check_raw_read(vmg0_); /* retrieve the length value, if present; if not, use the file size */ long len = (argc >= 1 && G_stk->get(0)->typ != VM_NIL ? G_stk->get(0)->num_to_int(vmg0_) : get_file_size(vmg0_)); /* save 'self' for gc protection */ G_stk->push_obj(vmg_ self); /* calculate the hash */ char hash[65]; sha256_datasrc(hash, get_ext()->fp, len); /* return the hash value */ retval->set_obj(CVmObjString::create(vmg_ FALSE, hash, 64)); /* discard our gc protection and our arguments */ G_stk->discard(argc + 1); /* handled */ return TRUE; } /* * property evaluator - digestMD5 */ int CVmObjFile::getp_digestMD5(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* check that the it's a valid file open with read access in "raw" mode */ check_raw_read(vmg0_); /* retrieve the length value, if present; if not, use the file size */ long len = (argc >= 1 && G_stk->get(0)->typ != VM_NIL ? G_stk->get(0)->num_to_int(vmg0_) : get_file_size(vmg0_)); /* save 'self' for gc protection */ G_stk->push_obj(vmg_ self); /* calculate the hash */ char hash[33]; md5_datasrc(hash, get_ext()->fp, len); /* return the hash value */ retval->set_obj(CVmObjString::create(vmg_ FALSE, hash, 32)); /* discard our gc protection and our arguments */ G_stk->discard(argc + 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Refill the read buffer. Returns true if the buffer contains any data * on return, false if we're at end of file. */ int vmobjfile_readbuf_t::refill(CVmDataSource *fp) { /* if the buffer is already full, do nothing */ if (rem == sizeof(buf)) return FALSE; /* * if there's anything left in the buffer, move it to the start of the * buffer */ if (rem != 0) { memmove(buf, ptr, rem); ptr = buf + rem; } else ptr = buf; /* fill up the rest of the buffer */ size_t cur = fp->readc(ptr, sizeof(buf) - rem); rem += cur; /* return success if we managed to read any bytes on this round */ return (cur != 0); } /* * Read a character */ int vmobjfile_readbuf_t::getch(wchar_t &ch, CVmDataSource *fp, class CCharmapToUni *charmap) { /* keep going until we map a character or run out of input */ for (;;) { /* try mapping a character - if we get one, return success */ if (charmap->mapchar(ch, (const char *&)ptr, rem)) return TRUE; /* * we don't have enough bytes to map a whole character, so refill * our buffer; if that fails, we're at EOF */ if (!refill(fp)) return FALSE; } } /* * Peek at the next character */ int vmobjfile_readbuf_t::peekch(wchar_t &ch, CVmDataSource *fp, class CCharmapToUni *charmap, size_t &in_bytes) { /* keep going until we map a character or run out of input */ for (;;) { /* try mapping a character - if we get one, return success */ char *ptr2 = ptr; size_t rem2 = rem; if (charmap->mapchar(ch, (const char *&)ptr2, rem2)) { in_bytes = rem - rem2; return TRUE; } /* * we don't have enough bytes to map a whole character, so refill * our buffer; if that fails, we're at EOF */ if (!refill(fp)) return FALSE; } } ��������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmfilobj.h������������������������������������������������������������������������0000664�0000000�0000000�00000052460�12650170723�0015646�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmfilobj.h - File object metaclass Function Implements an intrinsic class interface to operating system file I/O. Notes Modified 06/28/01 MJRoberts - Creation */ #ifndef VMFILOBJ_H #define VMFILOBJ_H #include "os.h" #include "utf8.h" #include "vmglob.h" #include "vmobj.h" #include "vmstr.h" #include "vmdatasrc.h" /* ------------------------------------------------------------------------ */ /* * File intrinsic class. Our extension keeps track of our data source (our * abstract file handle), our character set object, and our flags. * * CVmDataSource *fp *. objid charset *. byte mode (text/data/raw) *. byte access (read/write/both) *. uint32 flags * * Our image file data leaves out the file handle, since it can't be * loaded: * * objid charset *. byte mode *. byte access *. uint32 flags * * An open file is inherently transient, so its state cannot be loaded, * restored, or undone. */ class CVmObjFile: public CVmObject { friend class CVmMetaclassFile; public: /* * Special filename designators. These integer values can be passed to * the Open methods (from bytecode) in place of the filename string. * These designate system files whose actual location is determined by * the interpreter. * * The special file mechanism is designed to give games a well-defined * portable way to access global, cross-game files outside of the game * sandbox, while still protecting the local file system against * malicious (or just buggy) games. The actual system path and * filename of a given special file is determined by the interpreter, * so games can't use this to access arbitrary files - only the * designated special files are accessible. * * Note that the integer values defined here must not be changed in * future releases, since they're a published part of the API - i.e., * bytecode programs hard-code the integer values. */ /* * Library defaults file. This is defined as a system file, common to * all games, where we can store global cross-game preference settings. * This is generally stored in the interpreter install directory. */ static const int32_t SFID_LIB_DEFAULTS = 1; /* * Web UI preferences file. This is a system file common to all games * that stores the UI settings for the Web UI client. */ static const int32_t SFID_WEBUI_PREFS = 2; /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is 'obj' a File object? */ static int is_file_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create from stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *, vm_obj_id_t /*self*/) { /* we can't be converted to constant data */ } /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *, vm_obj_id_t /*self*/) { /* * we can't be converted to constant data and reference nothing * that can be converted to constant data */ } /* create an empty file object */ static vm_obj_id_t create(VMG_ int in_root_set); /* create with the given character set and file handle */ static vm_obj_id_t create(VMG_ int in_root_set, class CVmNetFile *netfile, vm_obj_id_t charset, CVmDataSource *fp, int mode, int access, int create_readbuf); /* get the data source for the file */ class CVmDataSource *get_datasource() const; /* get the filespec used to create the file */ void get_filespec(vm_val_t *val) const; /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* call a static property */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop); /* undo operations */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { } /* mark references */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } void mark_refs(VMG_ uint state); /* we keep no weak references */ void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } void remove_stale_weak_refs(VMG0_) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t); /* reload from an image file (on restart) */ void reload_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* evaluate a property */ virtual int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * Translate a filename argument to a netfile object. Checks file * safety against the given access mode. */ static CVmNetFile *get_filename_arg( VMG_ const vm_val_t *arg, const vm_rcdesc *rc, int access, int is_resource_file, os_filetype_t file_type, const char *mime_type); /* * get the filename from an object argument; this can only be used for * regular files (not resources) */ static CVmNetFile *get_filename_from_obj( VMG_ vm_obj_id_t obj, const vm_rcdesc *rc, int access, os_filetype_t file_type, const char *mime_type); /* resolve a special file ID to a local path */ static int sfid_to_path(VMG_ char *buf, size_t buflen, int32_t sfid); /* * Check the safety settings to determine if an open is allowed on the * given file with the given access mode. If the access is not * allowed, we'll throw an error. */ static void check_safety_for_open(VMG_ class CVmNetFile *f, int access); /* check the safety settings for opening the given local file path */ static void check_safety_for_open(VMG_ const char *lclfname, int access); static int query_safety_for_open(VMG_ const char *lclfname, int access); /* get the file's size; returns -1 if the file isn't open or valid */ long get_file_size(VMG0_); /* get the mode - returns a VMOBJFILE_MODE_xxx value */ int get_file_mode(VMG0_); /* seek; returns true on success, false on failure */ int set_pos(VMG_ long pos); /* * Read the file, storing the result in the caller's buffer. The * request is a suggested read size; we'll attempt to fill this as * closely as possible, but we might return less than requested (never * more) due to reaching the end of the file or due to character * conversions. Returns true on success, false on failure. */ int read_file(VMG_ char *buf, int32_t &len); protected: /* create with no initial contents */ CVmObjFile() { ext_ = 0; } /* create with the given character set and file object */ CVmObjFile(VMG_ class CVmNetFile *netfile, vm_obj_id_t charset, CVmDataSource *fp, int mode, int access, int create_readbuf); /* allocate our extension */ void alloc_ext(VMG_ class CVmNetFile *netfile, vm_obj_id_t charset, CVmDataSource *fp, unsigned long flags, int mode, int access, int create_readbuf); /* load or reload data from the image file */ void load_image_data(VMG_ const char *ptr, size_t siz); /* property evaluator - undefined property */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - get my character set */ int getp_get_charset(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - set my character set */ int getp_set_charset(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - close the file */ int getp_close_file(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - read from the file */ int getp_read_file(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - write to the file */ int getp_write_file(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - read bytes from the file */ int getp_read_bytes(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - write bytes to the file */ int getp_write_bytes(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get seek position */ int getp_get_pos(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - set seek position */ int getp_set_pos(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the seek position */ long get_pos(VMG0_); /* property evaluator - set seek position to end of file */ int getp_set_pos_end(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - openFileText */ int getp_open_text(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - openFileData */ int getp_open_data(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - openFileRaw */ int getp_open_raw(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - openResourceText */ int getp_open_res_text(VMG_ vm_obj_id_t self, vm_val_t *ret, uint *argc); /* property evaluator - openResourceRaw */ int getp_open_res_raw(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get size */ int getp_get_size(VMG_ vm_obj_id_t self, vm_val_t *ret, uint *argc); /* get the file's mode */ int getp_get_mode(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* static property evaluators for the openFileXxx creator methods */ static int s_getp_open_text(VMG_ vm_val_t *retval, uint *argc, int is_resource_file, const struct vm_rcdesc *rc); static int s_getp_open_data(VMG_ vm_val_t *retval, uint *argc, const struct vm_rcdesc *rc); static int s_getp_open_raw(VMG_ vm_val_t *retval, uint *argc, int is_resource_file, const struct vm_rcdesc *rc); /* property evaluator - getRootName */ int getp_getRootName(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc) { return s_getp_getRootName(vmg_ retval, argc); } /* static property evaluator for getRootName */ static int s_getp_getRootName(VMG_ vm_val_t *retval, uint *argc); /* property evaluator - deleteFile */ int getp_deleteFile(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc) { return s_getp_deleteFile(vmg_ retval, argc); } /* static property evaluator for deleteFile */ static int s_getp_deleteFile(VMG_ vm_val_t *retval, uint *argc); /* property evaluator - setMode */ int getp_setMode(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - packBytes */ int getp_packBytes(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - unpackBytes */ int getp_unpackBytes(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - sha256 */ int getp_sha256(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - digestMD5 */ int getp_digestMD5(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* retrieve the filename and access arguments for an 'open' method */ static class CVmNetFile *get_filename_and_access( VMG_ const struct vm_rcdesc *rc, int *access, int is_resource_file, os_filetype_t file_type, const char *mime_type); /* pop a character set mapper argument */ static vm_obj_id_t get_charset_arg(VMG_ int argn, int argc); /* generic binary file opener - common to 'raw' and 'data' files */ static int open_binary(VMG_ vm_val_t *retval, uint *argc, int mode, int is_resource_file, const struct vm_rcdesc *rc); /* read a value in text/data mode */ void read_text_mode(VMG_ vm_val_t *retval); void read_data_mode(VMG_ vm_val_t *retval); /* write a value in text/data mode */ void write_text_mode(VMG_ const vm_val_t *val); void write_data_mode(VMG_ const vm_val_t *val); /* * check to make sure we can perform operations on the file - if we're * in the 'out of sync' state, we'll throw an exception */ void check_valid_file(VMG0_); /* * check that we have read or write access to the file - throws a mode * exception if we don't have the requested access mode */ void check_read_access(VMG0_); void check_write_access(VMG0_); /* check that we're open in raw mode */ void check_raw_mode(VMG0_); /* check that we're valid, in raw mode, and read/write */ void check_raw_read(VMG0_); void check_raw_write(VMG0_); /* * Note a file seek position change. * * 'is_explicit' indicates whether the seek is an explicit osfseek() * operation or simply a change in position due to a read or write. If * we're explicitly seeking to a new location with osfseek(), * 'is_explicit' is true; if we're simply reading or writing, which * affects the file position implicitly, 'is_explicit' is false. */ void note_file_seek(VMG_ int is_explicit); /* * check for a switch between reading and writing, flushing our * underlying stdio buffers if necessary */ void switch_read_write_mode(VMG_ int writing); /* get my extension, properly cast */ struct vmobjfile_ext_t *get_ext() const { return (struct vmobjfile_ext_t *)ext_; } /* property evaluation function table */ static int (CVmObjFile::*func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* * File object extension */ struct vmobjfile_ext_t { /* * Our network/local file descriptor. This handles network storage * server operations when we're running in web server mode. */ class CVmNetFile *netfile; /* our abstract data source (usually an OS file) */ CVmDataSource *fp; /* our character set object */ vm_obj_id_t charset; /* format mode */ unsigned char mode; /* read/write access */ unsigned char access; /* flags */ unsigned long flags; /* read buffer, if we have one */ struct vmobjfile_readbuf_t *readbuf; }; /* * File object text read buffer. This is attached to the file extension * if the file is opened in text mode with read access. */ struct vmobjfile_readbuf_t { /* current read pointer */ char *ptr; /* bytes remaining in read buffer */ size_t rem; /* read buffer */ char buf[512]; /* read a character */ int getch(wchar_t &ch, CVmDataSource *fp, class CCharmapToUni *charmap); /* peek at the next character */ int peekch(wchar_t &ch, CVmDataSource *fp, class CCharmapToUni *charmap, size_t &in_bytes); /* commit a 'peekch' */ void commit_peek(size_t last_in_bytes) { ptr += last_in_bytes; rem -= last_in_bytes; } /* refill the buffer, if it's empty */ int refill(CVmDataSource *fp); }; /* * file extension flags */ /* * File out of sync. This is set when we load a File object from an image * file; since the file cannot remain open across a preinit-save-load * cycle, no operations can be performed on the File object after it's * loaded from an image file. */ #define VMOBJFILE_OUT_OF_SYNC 0x0001 /* * last operation left the underlying stdio buffers dirty, so we must take * care to seek explicitly in the file if changing read/write mode on the * next operation */ #define VMOBJFILE_STDIO_BUF_DIRTY 0x0002 /* * last operation was 'write' - we must keep track of the last operation * so that we will know to explicitly seek when switching between read and * write operations */ #define VMOBJFILE_LAST_OP_WRITE 0x0004 /* * modes */ #define VMOBJFILE_MODE_TEXT 0x01 #define VMOBJFILE_MODE_DATA 0x02 #define VMOBJFILE_MODE_RAW 0x03 /* * access types */ #define VMOBJFILE_ACCESS_READ 0x0001 #define VMOBJFILE_ACCESS_WRITE 0x0002 #define VMOBJFILE_ACCESS_RW_KEEP 0x0003 #define VMOBJFILE_ACCESS_RW_TRUNC 0x0004 #define VMOBJFILE_ACCESS_USERMAX 0x0004 /* internal access types - not allowed as user arguments */ #define VMOBJFILE_ACCESS_INTERNAL 0x1000 #define VMOBJFILE_ACCESS_DELETE 0x1000 /* delete file */ #define VMOBJFILE_ACCESS_GETINFO 0x1001 /* get file type/info */ #define VMOBJFILE_ACCESS_MKDIR 0x1002 /* mkdir */ #define VMOBJFILE_ACCESS_RMDIR 0x1003 /* rmdir */ #define VMOBJFILE_ACCESS_READDIR 0x1004 /* read a directory (list files) */ #define VMOBJFILE_ACCESS_RENAME_FROM 0x1005 /* rename FROM this name */ #define VMOBJFILE_ACCESS_RENAME_TO 0x1006 /* rename TO this name */ /* ------------------------------------------------------------------------ */ /* * 'Data' mode type tags. Each item in a data-mode file is written with * a byte giving the type tag, followed immediately by the data value. * * For compatibility with TADS 2 'binary' mode files, we use the same * type tags that TADS 2 used for types in common. We are upwardly * compatible with the TADS 2 format: we recognize all TADS 2 type tags, * but we provide support for additional types that TADS 2 does not use. * All of the tag values below 0x20 are compatible with TADS 2; those * tag values 0x20 and above are not backwards compatible, so files that * include these values will not be readable by TADS 2 programs. */ /* * integer - 32-bit int in little-endian format (4 bytes) - TADS 2 * compatible */ #define VMOBJFILE_TAG_INT 0x01 /* * string - 16-bit byte-length prefix in little-endian format followed * by the bytes of the string, in UTF-8 format - TADS 2 compatible, * although any string which includes characters outside of the ASCII * set (Unicode code points 0 to 127 inclusive) will not be properly * interpreted by TADS 2 programs. * * For compatibility with TADS 2, the length prefix includes in its byte * count the two bytes of the length prefix itself, so this value is * actually two higher than the number of bytes in the string proper. */ #define VMOBJFILE_TAG_STRING 0x03 /* true - no associated value - TADS 2 compatible */ #define VMOBJFILE_TAG_TRUE 0x08 /* enum - 32-bit enum value in little-endian format */ #define VMOBJFILE_TAG_ENUM 0x20 /* * BigNumber - 16-bit length prefix in little-endian format followed by * the bytes of the BigNumber */ #define VMOBJFILE_TAG_BIGNUM 0x21 /* * ByteArray - 32-bit length prefix in little-endian format followed by * the bytes of the byte array */ #define VMOBJFILE_TAG_BYTEARRAY 0x22 /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassFile: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "file/030003"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjFile(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjFile(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjFile::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjFile::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMFILOBJ_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjFile) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmfindrep.h�����������������������������������������������������������������������0000664�0000000�0000000�00000122613�12650170723�0016026�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* Copyright (c) 2012 by Michael J. Roberts. All Rights Reserved. */ /* Name vmfindrep.h - find/replace common code template Function Defines the common code template for String.findReplace() and rexReplace(). The two functions are very similar, but have some slight interface differences. Implementing as a common subroutine has a measurable negative impact on performance; typical TADS games make heavy use of these functions, so optimizing them for speed is beneficial. By using a common code template, we can avoid the extra calls and run-time condition checking that's necessary in a common subroutine; in the template version, we essentially unroll the code and apply the conditions and test the conditions statically. This makes for larger but faster code. Notes Modified 06/17/12 MJRoberts - Creation */ #ifndef VMFINDREP_H #define VMFINDREP_H /* ------------------------------------------------------------------------ */ /* * re_replace flags */ /* replace all matches (if omitted, replaces only the first match) */ #define VMBIFTADS_REPLACE_ALL 0x0001 /* ignore case in matching the search string */ #define VMBIFTADS_REPLACE_NOCASE 0x0002 /* * follow case: lower-case characters in the the replacement text are * converted to follow the case pattern of the matched text (all lower, * initial capital, or all capitals) */ #define VMBIFTADS_REPLACE_FOLLOW_CASE 0x0004 /* * serial replacement: when a list of search patterns is used, we replace * each occurrence of the first pattern over the whole string, then we * start over with the result and scan for occurrences of the second * pattern, replacing each of these, and so on. If this flag isn't * specified, the default is parallel scanning: we find the leftmost * occurrence of any of the patterns and replace it; then we scan the * remainder of the string for the leftmost occurrence of any pattern, and * replace that one; and so on. The serial case is equivalent to making a * series of calls to this function with the individual search patterns. */ #define VMBIFTADS_REPLACE_SERIAL 0x0008 /* * Replace only the first occurrence. */ #define VMBIFTADS_REPLACE_ONCE 0x0010 /* * re_replace pattern/replacement structure. This is used to handle arrays * of arguments: each element represents one pattern->replacement mapping. */ struct re_replace_arg { re_replace_arg() { s = 0; pat = 0; pat_str = 0; rpl_func.set_nil(); match_valid = FALSE; } ~re_replace_arg() { /* if we created the pattern object, delete it */ if (pat != 0 && our_pat) CRegexParser::free_pattern(pat); if (s != 0) delete s; } void set(VMG_ const vm_val_t *patv, const vm_val_t *rplv, int str_to_pat, int32_t flags) { /* save the flags */ this->flags = flags; /* retrieve the compiled RexPattern or uncompiled pattern string */ const char *str; if (patv->typ == VM_OBJ && CVmObjPattern::is_pattern_obj(vmg_ patv->val.obj)) { /* create the searcher, if we haven't already */ create_searcher(vmg0_); /* it's a pattern object - get its compiled pattern structure */ pat = ((CVmObjPattern *)vm_objp(vmg_ patv->val.obj)) ->get_pattern(vmg0_); /* we didn't create the pattern object */ our_pat = FALSE; } else if ((str = patv->get_as_string(vmg0_)) != 0) { /* it's a string - decide how to treat it */ if (str_to_pat) { /* create the searcher */ create_searcher(vmg0_); /* we treat strings as regular expressions - compile it */ re_status_t stat; stat = G_bif_tads_globals->rex_parser->compile_pattern( str + VMB_LEN, vmb_get_len(str), &pat); /* if that failed, we don't have a pattern */ if (stat != RE_STATUS_SUCCESS) pat = 0; /* make a note that we allocated the pattern */ our_pat = TRUE; } else { /* we treat strings as simple literal search strings */ pat_str = str; } } else { /* invalid type */ err_throw(VMERR_BAD_TYPE_BIF); } /* * Save the replacement value. This can be a string, nil (which we * treat like an empty string), or a callback function. */ if (rplv->typ == VM_NIL) { /* treat it as an empty string */ rpl_str = "\000\000"; } else if ((str = rplv->get_as_string(vmg0_)) != 0) { /* save the string value */ rpl_str = str; } else if (rplv->is_func_ptr(vmg0_)) { /* it's a function or invokable object */ rpl_str = 0; rpl_func = *rplv; /* get the number of arguments it expects */ CVmFuncPtr f(vmg_ rplv); rpl_argc = f.is_varargs() ? -1 : f.get_max_argc(); } else { /* invalid type */ err_throw(VMERR_BAD_TYPE_BIF); } } /* create the regex searcher */ void create_searcher(VMG0_) { /* create our searcher if we haven't yet */ if (s == 0) { /* create the searcher object */ s = new CRegexSearcherSimple(G_bif_tads_globals->rex_parser); /* turn off case sensitivity in the searcher if desired */ if ((flags & VMBIFTADS_REPLACE_NOCASE) != 0) s->set_default_case_sensitive(FALSE); } } void search(VMG_ const char *str, int start_idx, const char *last_str) { /* if we have a pattern, search for it */ if (pat_str != 0) { /* * whether or not we find a match, the search will be valid * when we return */ match_valid = TRUE; match_len = 0; /* set up a pointer to the current position */ utf8_ptr p((char *)str + VMB_LEN + start_idx); /* figure the number of bytes left in the string */ size_t rem = vmb_get_len(str) - start_idx; /* note the length of the search string */ size_t pat_len = vmb_get_len(pat_str); /* scan for a match, according to case sensitivity */ if (!(flags & VMBIFTADS_REPLACE_NOCASE)) { /* case sensitive - scan for a match */ for ( ; rem >= pat_len ; p.inc(&rem)) { /* no case folding, so simply compare byte by byte */ if (memcmp(p.getptr(), pat_str + VMB_LEN, pat_len) == 0) { /* success - remember the match length and position */ match_len = pat_len; match_idx = p.getptr() - (str + VMB_LEN); /* we're done */ return; } } } else { /* case-insensitive - scan for a match */ for ( ; rem >= pat_len ; p.inc(&rem)) { /* compare with case folding */ size_t ml; if (t3_compare_case_fold( pat_str + VMB_LEN, pat_len, p.getptr(), rem, &ml) == 0) { /* success - set the match index, and we're done */ match_idx = p.getptr() - (str + VMB_LEN); match_len = ml; return; } } } /* we didn't a match - set the match index to so indicate */ match_idx = -1; } if (pat != 0) { /* search for the regular expression */ match_idx = s->search_for_pattern( pat, str + VMB_LEN, last_str, vmb_get_len(str) - start_idx, &match_len); /* if we found it, adjust to the absolute offset in the string */ if (match_idx >= 0) match_idx += start_idx; } else { /* no pattern -> no match */ match_idx = -1; } /* whether or not we matched, our result is now valid */ match_valid = TRUE; } /* our search pattern, or null if we're searching for a literal string */ re_compiled_pattern *pat; /* our search string, or null if we're searching for a pattern */ const char *pat_str; /* Did we create the pattern? If so, delete it on destruction. */ int our_pat; /* our replacement string, or null if it's a callback function */ const char *rpl_str; /* our replacement function, in lieu of a string */ vm_val_t rpl_func; /* the number of arguments rpl_func expects, or -1 for varargs */ int rpl_argc; /* search flags */ int32_t flags; /* the byte index and length in the source string of our last match */ int match_idx; int match_len; /* * Is this last match data valid? This is false if we haven't done the * first search yet, or if the last replacement overlapped our matching * text at all. */ int match_valid; /* searcher - this holds the group registers for the last match */ CRegexSearcherSimple *s; }; /* ------------------------------------------------------------------------ */ /* * <mode> values for vm_find_replace<mode> */ #define VMFINDREPLACE_StringFindReplace 0 #define VMFINDREPLACE_rexReplace 1 /* * Common find-and-replace handler for String.findReplace() and * rexReplace(). The two functions are essentially the same; the only * differences are: * * 1. The arguments are in a slightly different order. *. rexReplace(pattern, subject, replacement, flags?, index?) *. subject.findReplace(pattern, replacement, flags?, index?) * * Note the placement of the subject string argument; it moves outside the * argument list to the target object position in the String method call * version, so the subsequent arguments all move down one position. * * 2. In String.findReplace(), the 'pattern' argument is taken as literal * text if a string value is passed. In rexReplace(), a string value for * 'pattern' is taken as an uncompiled regular expression. For either * function, a RexPattern object can be passed, in which case it's treated * as a regular expression (obviously). * * 3. If 'pattern' is a literal string, 'replacement' is also treated as * literal text; if 'pattern' is a regular expression (either explicitly as * a RexPattern object, or as a string taken to be an uncompiled regular * expression in rexReplace()), '%' sequences are used for group * replacements; e.g., '%1' is replaced with the matching text for the * first parenthesized group in the regex, '%2' by the second group's * matching text, etc. */ template<int mode> inline void vm_find_replace( VMG_ vm_val_t *result, int argc, const vm_val_t *subj, const char *subj_str) { int i; vm_rcdesc rc; const int new_str_ofs = (mode == VMFINDREPLACE_StringFindReplace ? 1 : 2); const int pat_str_is_regex = (mode == VMFINDREPLACE_rexReplace); /* remember the original subject string */ const vm_val_t *orig_subj = subj; /* get the flags; use ReplaceAll if omitted */ int32_t flags = VMBIFTADS_REPLACE_ALL; const vm_val_t *flagp; if (new_str_ofs + 1 < argc && (flagp = G_stk->get(new_str_ofs + 1))->typ != VM_NIL) { /* check the type */ if (flagp->typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); /* retrieve the value */ flags = flagp->val.intval; } /* * Check for old flags. Before 3.1, there was only one flag bit * defined: ALL=1. This means there were only two valid values for * 'flags': 0 for ONCE mode, 1 for ALL mode. * * In 3.1, we added a bunch of new flags. At the same time, we made * the default ALL mode, because this is the more common case. * Unfortunately, this creates a compatibility issue. A new program * that specifies one of the new flags might leave out the ONCE or ALL * bits, since ALL is the default. However, we can't just take the * absence of the ONCE bit as meaning ALL, because that would hose old * programs that explicitly specify ONCE as 0 (no bits set). * * Here's how we deal with this: we prohibit new programs from passing * 0 for the flags, requiring them to specify at least one bit. So if * the flags value is zero, we must have an old program that passed * ONCE. In this case, explicitly set the ONCE bit. If we have any * non-zero value, we must have either a new program OR an old program * that included the ALL bit. In either case, ALL is the default, so * if the ONCE bit ISN'T set, explicitly set the ALL bit. */ if (flags == 0) { /* old program with the old ONCE flag - set the new ONCE flag */ flags = VMBIFTADS_REPLACE_ONCE; } else if (!(flags & VMBIFTADS_REPLACE_ONCE)) { /* * new program without the ONCE flag, OR an old program with the * ALL flag - explicitly set the ALL flag */ flags |= VMBIFTADS_REPLACE_ALL; } /* assume that the limit is one or infinity, per the 'flags' value */ int limit = ((flags & VMBIFTADS_REPLACE_ONCE) != 0 ? 1 : -1); /* if there's an explicit 'limit' argument, fetch it */ if (new_str_ofs + 3 < argc) { /* nil means explicitly unlimited; otherwise it's an int */ vm_val_t *limitp = G_stk->get(new_str_ofs + 3); if (limitp->typ == VM_NIL) { /* unlimited - signify with -1 */ limit = -1; } else if (limitp->typ == VM_INT) { /* get the limit, and make sure it's non-negative */ if ((limit = limitp->val.intval) < 0) err_throw(VMERR_BAD_VAL_BIF); /* * if the limit is zero, the result string will obviously be * identical to the source string, so just return it now */ if (limit == 0) { *result = *subj; return; } } else err_throw(VMERR_INT_VAL_REQD); } /* get the search substring or pattern value */ const vm_val_t *pat = G_stk->get(0); /* assume we have a single pattern to search for */ int pat_cnt = 1; int pat_is_list = FALSE; /* * if we're treating strings as literals (rather than uncompiled * regular expressions), assume we won't have any REs to search for */ int have_pat = pat_str_is_regex; /* check whether the pattern is given as an array or as a single value */ if (pat->is_listlike(vmg0_) && (pat_cnt = pat->ll_length(vmg0_)) >= 0) { /* It's a list. Check first to see if it's empty. */ if (pat_cnt == 0) { /* * empty list, so there's no work to do - simply return the * original subject string unchanged */ *result = *subj; return; } /* flag it as a list */ pat_is_list = TRUE; } /* get the replacement value from the stack */ const vm_val_t *rpl = G_stk->get(new_str_ofs); /* assume we have a single replacement value */ int rpl_cnt = 1; int rpl_is_list = FALSE; /* check to see if the replacement is a list */ if (rpl->is_listlike(vmg0_) && (rpl_cnt = rpl->ll_length(vmg0_)) >= 0) { /* flag it as a list */ rpl_is_list = TRUE; } /* allocate the argument array */ re_replace_arg *pats = new re_replace_arg[pat_cnt]; /* catch any errors so that we can free our arg array on the way out */ err_try { /* assume we won't need a recursive callback context */ int need_rc = FALSE; /* set up the pattern/replacement array from the arguments */ for (i = 0 ; i < pat_cnt ; ++i) { /* get the next pattern from the list, or the single pattern */ vm_val_t patele; if (pat_is_list) { /* we have a list - get the next element */ pat->ll_index(vmg_ &patele, i + 1); } else { /* we have a single pattern item */ patele = *pat; } /* * Get the next replacement from the list, or the single * replacement. If we have a single value for the replacement, * every pattern has the same replacement. If we have a list, * the pattern has the replacement at the corresponding index. * If it's a list and we're past the last replacement index, * use an empty string. */ vm_val_t rplele; if (rpl_is_list) { /* * we have a list - if we have an item at the current * index, it's the corresponding replacement for the * current pattern; if we've exhausted the replacement * list, use an empty string as the replacement */ if (i < rpl_cnt) rpl->ll_index(vmg_ &rplele, i + 1); else rplele.set_nil(); } else { /* single replacement item - it applies to all patterns */ rplele = *rpl; } /* fill in this argument item */ pats[i].set(vmg_ &patele, &rplele, pat_str_is_regex, flags); /* note if we have a regex */ have_pat |= (pats[i].pat != 0); /* we need a callback context if the replacement is a function */ need_rc |= (pats[i].rpl_str == 0); } /* initialize the recursive callback context if needed */ if (need_rc) { if (mode == VMFINDREPLACE_StringFindReplace) rc.init(vmg_ "String.findReplace", orig_subj, 11, G_stk->get(0), argc); else rc.init(vmg_ "rexReplace", CVmBifTADS::bif_table, 12, G_stk->get(0), argc); } /* get the starting index, if present; use index 1 if not present */ int start_char_idx = 1; const vm_val_t *idxp; if (new_str_ofs + 2 < argc && (idxp = G_stk->get(new_str_ofs + 2))->typ != VM_NIL) { /* check the type */ if (idxp->typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); /* get the value */ start_char_idx = idxp->val.intval; } /* * push a nil placeholder for the result value (we keep it on the * stack for gc protection) */ G_stk->push()->set_nil(); /* if this is a serial search, we'll start at the first pattern */ int serial_idx = 0; /* make sure the search string is indeed a string */ const char *str = subj_str; if (str == 0 && (str = subj->get_as_string(vmg0_)) == 0) err_throw(VMERR_STRING_VAL_REQD); /* set up a utf8 pointer to the search string */ utf8_ptr strp((char *)str + VMB_LEN); /* adjust the starting index */ start_char_idx += (start_char_idx < 0 ? (int)strp.len(vmb_get_len(str)) : -1); restart_search: /* * remember the last search string globally, for group extraction; * and forget any old group registers, since they'd point into the * old string we're superseding */ G_bif_tads_globals->last_rex_str->val = *subj; if (have_pat) G_bif_tads_globals->rex_searcher->clear_group_regs(); /* * don't allocate anything for the result yet - we'll wait to do * that until we actually find a match, so that we don't allocate * memory unnecessarily */ vm_obj_id_t ret_obj = VM_INVALID_OBJ; CVmObjString *ret_str = 0; /* set up a null output string pointer for now */ utf8_ptr dstp; dstp.set((char *)0); /* * figure out how many bytes at the start of the string to skip * before our first replacement */ utf8_ptr p; size_t rem; for (p.set((char *)str + VMB_LEN), rem = vmb_get_len(str), i = 0 ; i < start_char_idx && rem != 0 ; ++i, p.inc(&rem)) ; /* the current offset in the string is the byte skip offset */ int skip_bytes = p.getptr() - (str + VMB_LEN); /* * Start searching from the beginning of the string. Build the * result string as we go. */ int start_idx; for (start_idx = skip_bytes ; (size_t)start_idx < vmb_get_len(str) ; ) { /* figure out where the next search starts */ const char *last_str = str + VMB_LEN + start_idx; /* we haven't found a matching pattern yet */ int best_pat = -1; /* do the next serial or parallel search */ if ((flags & VMBIFTADS_REPLACE_SERIAL) != 0) { /* * Serial search: search for one item at a time. If we're * out of items, we're done. */ if (serial_idx >= pat_cnt) break; /* search for the next item */ pats[serial_idx].search(vmg_ str, start_idx, last_str); /* if we didn't get a match, we're done */ if (pats[serial_idx].match_idx < 0) break; /* this is our replacement match */ best_pat = serial_idx; } else { /* * Parallel search: search for all of the items, and * replace the leftmost match. We might still have valid * search results for some items on past iterations, but * others might have overlapped replacement text, in which * case we'll have to refresh them. So do a search for * each item that's marked as invalid. */ /* search for each item */ for (i = 0 ; i < pat_cnt ; ++i) { /* refresh this search, if it's invalid */ if (!pats[i].match_valid) pats[i].search(vmg_ str, start_idx, last_str); /* if this is the leftmost result so far, remember it */ if (pats[i].match_idx >= 0 && (best_pat < 0 || pats[i].match_idx < pats[best_pat].match_idx)) best_pat = i; } /* if we didn't find a match, we're done */ if (best_pat < 0) break; } /* * Keep the leftmost pattern we matched. Note that we want a * relative offset from last_str, so adjust from the absolute * offset in the string that the pats[] entry uses. */ int match_idx = pats[best_pat].match_idx - start_idx; int match_len = pats[best_pat].match_len; const char *rplstr = pats[best_pat].rpl_str; vm_val_t rplfunc = pats[best_pat].rpl_func; /* * if we have the 'follow case' flag, note the capitalization * pattern of the match */ int match_has_upper = FALSE, match_has_lower = FALSE; if ((flags & VMBIFTADS_REPLACE_FOLLOW_CASE) != 0) { /* scan the match text */ for (p.set((char *)last_str + match_idx), rem = match_len ; rem != 0 ; p.inc(&rem)) { /* get this character */ wchar_t ch = p.getch(); /* note whether it's upper or lower case */ match_has_upper |= t3_is_upper(ch); match_has_lower |= t3_is_lower(ch); } } /* remember the group info, if we have a regex pattern */ int group_cnt = 0; if (pats[best_pat].pat != 0) { /* note the group count */ group_cnt = pats[best_pat].pat->group_cnt; /* copy the group registers to the global searcher registers */ G_bif_tads_globals->rex_searcher->copy_group_regs( pats[best_pat].s); } /* if we have a replacement limit, count this against the limit */ if (limit > 0) --limit; /* * If we haven't allocated a result string yet, do so now, * since we finally know we actually need one. */ if (ret_obj == VM_INVALID_OBJ) { /* * We don't know yet how much space we'll need for the * result, so this is only a temporary allocation. As a * rough guess, use three times the length of the input * string. We'll expand this as needed as we build the * string, and shrink it down to the real size when we're * done. */ ret_obj = CVmObjString::create(vmg_ FALSE, vmb_get_len(str)*3); /* save it in our stack slot, for gc protection */ G_stk->get(0)->set_obj(ret_obj); /* get the string object pointer */ ret_str = (CVmObjString *)vm_objp(vmg_ ret_obj); /* get a pointer to the result buffer */ dstp.set(ret_str->cons_get_buf()); /* copy the initial part that we skipped */ if (skip_bytes != 0) { memcpy(dstp.getptr(), str + VMB_LEN, skip_bytes); dstp.set(dstp.getptr() + skip_bytes); } } /* copy the part up to the start of the matched text, if any */ if (match_idx > 0) { /* ensure space */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), match_idx, 512)); /* copy the part from the last match to this match */ memcpy(dstp.getptr(), last_str, match_idx); /* advance the output pointer */ dstp.set(dstp.getptr() + match_idx); } /* apply the replacement text */ if (rplstr != 0) { /* get the length of the string */ size_t rpllen = vmb_get_len(rplstr); /* we haven't substituted any alphabetic character yet */ int alpha_rpl_cnt = 0; /* note whether we have a literal search string or a regex */ int pat_is_str = (pats[best_pat].pat == 0); /* * if we're replacing a string pattern (not a regex), and * there's no case following, we can plop in the whole * replacement string without examining it */ if (pat_is_str && !(flags & VMBIFTADS_REPLACE_FOLLOW_CASE)) { /* ensure space */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), rpllen, 512)); /* copy the replacement text */ memcpy(dstp.getptr(), rplstr + VMB_LEN, rpllen); /* advance the pointer */ dstp.set(dstp.getptr() + rpllen); } else { /* * copy the replacement string into the output string, * expanding group substitutions if we have a regex */ for (p.set((char *)rplstr + VMB_LEN), rem = rpllen ; rem != 0 ; p.inc(&rem)) { /* fetch the character */ wchar_t ch = p.getch(); /* if replacing a regex, check for '%' sequences */ if (pat_is_str || ch != '%' || rem == 1) { /* * ensure we have space for this character (one * UTF8 needs 3 bytes max) */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), 3, 512)); /* * if we're in 'follow case' mode, adjust the * case of lower-case literal letters in the * replacement text */ const wchar_t *u = 0; if ((flags & VMBIFTADS_REPLACE_FOLLOW_CASE) != 0 && t3_is_lower(ch)) { /* * Check the mode: if we have all * upper-case in the match, convert the * replacement to all caps; all lower-case * in the match -> all lower in the * replacement; mixed -> capitalize the * first letter only */ if (match_has_upper && !match_has_lower) { /* all upper -> convert to upper */ u = t3_to_upper(ch); } else if (match_has_upper && match_has_lower && alpha_rpl_cnt++ == 0) { /* mixed case -> use title case */ u = t3_to_title(ch); } } /* if we found a case conversion, apply it */ if (u != 0) { /* ensure we have room for the conversion */ size_t need = utf8_ptr::s_wstr_size(u); dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), need, 512)); /* store the converted characters */ dstp.setwcharsz(u, need); } else { /* no case conversion - copy it as-is */ dstp.setch(ch); } } else { /* skip the '%' and see what follows */ p.inc(&rem); switch(p.getch()) { case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { /* get the group number */ int groupno = value_of_digit(p.getch()) - 1; /* if the group is valid, add its length */ if (groupno < group_cnt) { /* get the register */ const re_group_register *reg = G_bif_tads_globals->rex_searcher ->get_group_reg(groupno); /* if it's been set, add its text */ if (reg->start_ofs != -1 && reg->end_ofs != -1) { /* get the group length */ size_t glen = reg->end_ofs - reg->start_ofs; /* ensure space */ dstp.set( ret_str->cons_ensure_space( vmg_ dstp.getptr(), glen, 512)); /* copy the data */ memcpy(dstp.getptr(), str + VMB_LEN + reg->start_ofs, glen); /* advance past it */ dstp.set(dstp.getptr() + glen); } } } break; case '*': /* ensure space */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), match_len, 512)); /* add the entire matched string */ memcpy(dstp.getptr(), last_str + match_idx, match_len); dstp.set(dstp.getptr() + match_len); break; case '%': /* ensure space (the '%' is just one byte) */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), 1, 512)); /* add a single '%' */ dstp.setch('%'); break; default: /* * ensure space (we need 1 byte for the * '%', up to 3 for the other character) */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), 4, 512)); /* add the entire sequence unchanged */ dstp.setch('%'); dstp.setch(p.getch()); break; } } } } } else { /* push the callback args - matchStr, matchIdx, subject */ const int pushed_argc = 3; G_stk->push(orig_subj); G_interpreter->push_int( vmg_ strp.len(start_idx + match_idx) + 1); G_interpreter->push_obj(vmg_ CVmObjString::create( vmg_ FALSE, last_str + match_idx, match_len)); /* adjust argc for what the callback actually wants */ int fargc = pats[best_pat].rpl_argc; if (fargc < 0 || fargc > pushed_argc) fargc = pushed_argc; /* call the callback */ G_interpreter->call_func_ptr(vmg_ &rplfunc, fargc, &rc, 0); /* discard extra arguments */ G_stk->discard(pushed_argc - fargc); /* if the return value isn't nil, copy it into the result */ if (G_interpreter->get_r0()->typ != VM_NIL) { /* get the string */ const char *r = G_interpreter->get_r0()->get_as_string(vmg0_); if (r == 0) err_throw(VMERR_STRING_VAL_REQD); /* ensure space for it in the result */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), vmb_get_len(r), 512)); /* store it */ memcpy(dstp.getptr(), r + VMB_LEN, vmb_get_len(r)); dstp.set(dstp.getptr() + vmb_get_len(r)); } } /* advance past this matched string for the next search */ start_idx += match_idx + match_len; /* skip to the next character if it was a zero-length match */ if (match_len == 0 && (size_t)start_idx < vmb_get_len(str)) { /* ensure space */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), 3, 512)); /* copy the character we're skipping to the output */ p.set((char *)str + VMB_LEN + start_idx); dstp.setch(p.getch()); /* move on to the next character */ start_idx += 1; } /* * In a parallel search, discard any match that started before * the new starting point. Those are no longer valid because * they matched original text that was wholly or partially * replaced by the current iteration. */ for (i = 0 ; i < pat_cnt ; ++i) { /* invalidate this match if it's before the replacement */ if (pats[i].match_idx >= 0 && pats[i].match_idx < start_idx) pats[i].match_valid = FALSE; } /* if we've reached the replacement limit, stop now */ if (limit == 0) break; } /* if we did any replacements on this round, finish the string */ if (ret_obj != VM_INVALID_OBJ) { /* ensure space for the remainder after the last match */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), vmb_get_len(str) - start_idx, 512)); /* add the part after the end of the matched text */ if ((size_t)start_idx < vmb_get_len(str)) { memcpy(dstp.getptr(), str + VMB_LEN + start_idx, vmb_get_len(str) - start_idx); dstp.set(dstp.getptr() + vmb_get_len(str) - start_idx); } /* set the actual length of the string */ ret_str->cons_shrink_buffer(vmg_ dstp.getptr()); /* return the string */ result->set_obj(ret_obj); } else { /* we didn't replace anything, so keep the original string */ *result = *subj; } /* * If this is a serial search, and we have another item in the * search list, go back and start over with the current result as * the new search string. Exception: if we've reached the * replacement count limit, we're done. */ if ((flags & VMBIFTADS_REPLACE_SERIAL) != 0 && limit != 0 && ++serial_idx < pat_cnt) { /* use the return value as the new search value */ subj = result; *G_stk->get(2) = *subj; /* get the string again in case we're doing a serial iteration */ str = subj->get_as_string(vmg0_); /* go back for a brand new round */ goto restart_search; } /* discard our gc protection */ G_stk->discard(1); } err_finally { /* if we allocated an argument array, delete it */ if (pats != 0) delete [] pats; } err_end; } #endif /* VMFINDREP_H */ ���������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmfref.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000107334�12650170723�0015657�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmfref.cpp - stack frame reference object Function Notes Modified 02/18/10 MJRoberts - Creation */ #include <stdlib.h> #include "vmtype.h" #include "vmobj.h" #include "vmfref.h" #include "vmglob.h" #include "vmmeta.h" #include "vmstack.h" #include "vmundo.h" #include "vmlst.h" #include "vmfile.h" #include "vmstr.h" #include "vmvec.h" #include "vmlookup.h" #include "vmfunc.h" #include "vmerr.h" #include "vmerrnum.h" /* ------------------------------------------------------------------------ */ /* * Stack frame descriptor */ /* * statics */ static CVmMetaclassFrameDesc desc_metaclass_reg_obj; CVmMetaclass *CVmObjFrameDesc::metaclass_reg_ = &desc_metaclass_reg_obj; /* * function table */ int (CVmObjFrameDesc:: *CVmObjFrameDesc::func_table_[]) (VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjFrameDesc::getp_undef, &CVmObjFrameDesc::getp_is_active, &CVmObjFrameDesc::getp_get_vars, &CVmObjFrameDesc::getp_get_self, &CVmObjFrameDesc::getp_get_defobj, &CVmObjFrameDesc::getp_get_targobj, &CVmObjFrameDesc::getp_get_targprop, &CVmObjFrameDesc::getp_get_invokee, }; /* * construction */ CVmObjFrameDesc::CVmObjFrameDesc(VMG_ vm_obj_id_t fref, int frame_idx, uint ret_ofs) { /* allocate our extension */ ext_ = 0; alloc_ext(vmg_ fref, frame_idx, ret_ofs); } /* * create */ vm_obj_id_t CVmObjFrameDesc::create( VMG_ vm_obj_id_t fref, int frame_idx, uint ret_ofs) { /* allocate an object ID */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, TRUE, FALSE); /* instantiate the new object */ new (vmg_ id) CVmObjFrameDesc(vmg_ fref, frame_idx, ret_ofs); /* return the new object's ID */ return id; } /* * allocate our extension */ vm_framedesc_ext *CVmObjFrameDesc::alloc_ext( VMG_ vm_obj_id_t fref, int frame_idx, uint ret_ofs) { /* delete any existing extension */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* allocate the extension */ vm_framedesc_ext *ext = (vm_framedesc_ext *)G_mem->get_var_heap()->alloc_mem( sizeof(vm_framedesc_ext), this); /* save the data values */ ext->fref = fref; ext->frame_idx = frame_idx; ext->ret_ofs = ret_ofs; /* save the new extension in 'self' */ ext_ = (char *)ext; /* return the new extension */ return ext; } /* * notify of deletion */ void CVmObjFrameDesc::notify_delete(VMG_ int) { /* free our extension */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* * mark references */ void CVmObjFrameDesc::mark_refs(VMG_ uint state) { /* get the extension */ vm_framedesc_ext *ext = get_ext(); /* mark our frame ref object */ G_obj_table->mark_all_refs(ext->fref, state); } /* * load from the image file */ void CVmObjFrameDesc::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ self, ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from the image */ void CVmObjFrameDesc::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ self, ptr, siz); } /* * load or reload our image data */ void CVmObjFrameDesc::load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* read the frame information */ vm_obj_id_t fref = vmb_get_objid(ptr); int frame_idx = osrp2(ptr + VMB_OBJECT_ID); uint ret_ofs = osrp2(ptr + VMB_OBJECT_ID + 2); /* allocate the extension */ alloc_ext(vmg_ fref, frame_idx, ret_ofs); } /* * save */ void CVmObjFrameDesc::save_to_file(VMG_ class CVmFile *fp) { /* get our extension */ vm_framedesc_ext *ext = get_ext(); /* save our extension data */ fp->write_uint4(ext->fref); fp->write_uint2(ext->frame_idx); fp->write_uint2(ext->ret_ofs); } /* * restore */ void CVmObjFrameDesc::restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups) { /* read the values */ vm_obj_id_t fref = fixups->get_new_id(vmg_ (vm_obj_id_t)fp->read_uint4()); int frame_idx = fp->read_int2(); uint ret_ofs = fp->read_uint2(); /* allocate the extension */ alloc_ext(vmg_ fref, frame_idx, ret_ofs); } /* * Find a symbol. If we find the name, we fill in 'symp' with the * description of the variable and return true; otherwise we return false. */ int CVmObjFrameDesc::find_local(VMG_ const textchar_t *name, size_t namelen, CVmDbgFrameSymPtr *symp) { /* get our extension and the underlying frame's extension */ vm_framedesc_ext *ext = get_ext(); vm_frameref_ext *fext = get_frame_ref(vmg0_)->get_ext(); /* if the caller doesn't care about the symbol information, use a local */ CVmDbgFrameSymPtr oursym; if (symp == 0) symp = &oursym; /* if there's no frame, there are no locals */ if (ext->frame_idx == 0) return FALSE; /* set up pointer to this method's debug records */ CVmDbgTablePtr dp; if (!dp.set(fext->entryp)) return FALSE; /* set up a pointer to our frame */ CVmDbgFramePtr dfp; dp.set_frame_ptr(vmg_ &dfp, ext->frame_idx); /* walk up the list of frames from innermost to outermost */ for (;;) { /* set up a pointer to the first symbol */ dfp.set_first_sym_ptr(vmg_ symp); /* scan this frame's local symbol list */ int sym_cnt = dfp.get_sym_count(); for (int i = 0 ; i < sym_cnt ; ++i, symp->inc(vmg0_)) { /* check for a match */ if (symp->get_sym_len(vmg0_) == namelen && memcmp(symp->get_sym(vmg0_), name, namelen) == 0) { /* this is it - return with symp pointing to the symbol */ return TRUE; } } /* didn't find it - move up to the enclosing frame */ int parent = dfp.get_enclosing_frame(); if (parent == 0) break; /* set up dfp to point to the parent fraem */ dp.set_frame_ptr(vmg_ &dfp, parent); } /* we didn't find the symbol */ return FALSE; } /* * find a local variable in our frame by name, given a VM string value */ int CVmObjFrameDesc::find_local(VMG_ const vm_val_t *nval, CVmDbgFrameSymPtr *symp) { /* make sure the name is a string */ const char *name = nval->get_as_string(vmg0_); if (name == 0) err_throw(VMERR_BAD_TYPE_BIF); /* parse the length */ size_t namelen = vmb_get_len(name); name += VMB_LEN; /* look up the symbol */ return find_local(vmg_ name, namelen, symp); } /* * Get the value of a local by name */ int CVmObjFrameDesc::get_local_val(VMG_ vm_val_t *result, const vm_val_t *name) { /* look up the symbol by name */ CVmDbgFrameSymPtr sym; if (!find_local(vmg_ name, &sym)) return FALSE; /* retrieve the value based on the descriptor */ get_frame_ref(vmg0_)->get_local_val(vmg_ result, &sym); /* found it */ return TRUE; } /* * Set the value of a local by name */ int CVmObjFrameDesc::set_local_val(VMG_ const vm_val_t *name, const vm_val_t *new_val) { /* look up the symbol by name */ CVmDbgFrameSymPtr sym; if (!find_local(vmg_ name, &sym)) return FALSE; /* retrieve the value based on the descriptor */ get_frame_ref(vmg0_)->set_local_val(vmg_ &sym, new_val); /* found it */ return TRUE; } /* * index the frame: this looks up a variable's value by name */ int CVmObjFrameDesc::index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val) { /* check the index type */ if (index_val->get_as_string(vmg0_)) { /* it's a string - look up the value by name */ if (!get_local_val(vmg_ result, index_val)) err_throw(VMERR_INDEX_OUT_OF_RANGE); } else { /* invalid index type */ err_throw(VMERR_INDEX_OUT_OF_RANGE); } /* handled */ return TRUE; } /* * assign an indexed value: this sets a variable's value by name */ int CVmObjFrameDesc::set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val) { /* check the index type */ if (index_val->get_as_string(vmg0_)) { /* it's a string - look up the value by name */ if (!set_local_val(vmg_ index_val, new_val)) err_throw(VMERR_INDEX_OUT_OF_RANGE); } else { /* invalid index type */ err_throw(VMERR_INDEX_OUT_OF_RANGE); } /* the container doesn't change */ new_container->set_obj(self); /* handled */ return TRUE; } /* * get a property */ int CVmObjFrameDesc::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from the base object class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * property evaluator - determine if the frame is still alive */ int CVmObjFrameDesc::getp_is_active(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* the frame is valid if we still have a frame pointer */ retval->set_logical(get_frame_ref(vmg0_)->is_active()); /* handled */ return TRUE; } /* * property evaluator - set 'self' from the frame */ int CVmObjFrameDesc::getp_get_self(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* return the 'self' object */ get_frame_ref(vmg0_)->get_self(vmg_ retval); /* handled */ return TRUE; } /* * property evaluator - set 'definingobj' from the frame */ int CVmObjFrameDesc::getp_get_defobj(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get 'definingobj' from the frame */ get_frame_ref(vmg0_)->get_defobj(vmg_ retval); /* handled */ return TRUE; } /* * property evaluator - set 'targetobj' from the frame */ int CVmObjFrameDesc::getp_get_targobj(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get 'targetobj' from the frame */ get_frame_ref(vmg0_)->get_targobj(vmg_ retval); /* handled */ return TRUE; } /* * property evaluator - get 'targetprop' from the frame */ int CVmObjFrameDesc::getp_get_targprop(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get 'targetprop' from the frame */ get_frame_ref(vmg0_)->get_targprop(vmg_ retval); /* handled */ return TRUE; } /* * property evaluator - get 'invokee' from the frame */ int CVmObjFrameDesc::getp_get_invokee(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get 'invokee' from the frame */ get_frame_ref(vmg0_)->get_invokee(vmg_ retval); /* handled */ return TRUE; } /* * property evaluator - get a lookup table of the local variables, with * their current values, keyed by name */ int CVmObjFrameDesc::getp_get_vars(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* create our lookup table object */ retval->set_obj(CVmObjLookupTable::create(vmg_ FALSE, 32, 64)); CVmObjLookupTable *tab = (CVmObjLookupTable *) vm_objp(vmg_ retval->val.obj); /* get our extension */ vm_framedesc_ext *ext = get_ext(); /* get our underlying frame ref and its extension */ CVmObjFrameRef *fr = get_frame_ref(vmg0_); vm_frameref_ext *fext = fr->get_ext(); /* set up pointer to this method's debug records */ CVmDbgTablePtr dp; if (!dp.set(fext->entryp)) return TRUE; /* set up a pointer to our frame */ CVmDbgFramePtr dfp; dp.set_frame_ptr(vmg_ &dfp, ext->frame_idx); /* push the table for gc protection */ G_stk->push(retval); /* walk up the list of frames from innermost to outermost */ for (;;) { /* set up a pointer to the first symbol */ CVmDbgFrameSymPtr sym; dfp.set_first_sym_ptr(vmg_ &sym); /* scan this frame's local symbol list */ int sym_cnt = dfp.get_sym_count(); for (int i = 0 ; i < sym_cnt ; ++i, sym.inc(vmg0_)) { /* set up a string value for the key */ vm_val_t key; sym.get_str_val(vmg_ &key); G_stk->push(&key); /* * If this entry isn't already in the table, add it. Don't * bother if it already exists: we work from inner to outer * scopes, and inner scopes hide things in outer scopes, so if * we find an entry in the table already it means that it was * an inner-scope entry that hides the one we're processing * now. */ vm_val_t val; if (!tab->index_check(vmg_ &val, &key)) { /* get the value */ fr->get_local_val(vmg_ &val, &sym); /* add the entry to the table */ tab->add_entry(vmg_ &key, &val); } /* done with the key for gc protection */ G_stk->discard(); } /* move up to the enclosing frame */ int parent = dfp.get_enclosing_frame(); if (parent == 0) break; /* set up dfp to point to the parent fraem */ dp.set_frame_ptr(vmg_ &dfp, parent); } /* discard the table ref we pushed for gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Stack frame reference */ /* * statics */ static CVmMetaclassFrameRef ref_metaclass_reg_obj; CVmMetaclass *CVmObjFrameRef::metaclass_reg_ = &ref_metaclass_reg_obj; /* * construction */ CVmObjFrameRef::CVmObjFrameRef(VMG_ vm_val_t *fp, const uchar *entry) { /* set up a function pointer */ CVmFuncPtr f(entry); /* * figure the total number of variable snapshot slots we need to * allocate: this is the number of local variables plus the number of * actual arguments */ int nlocals = f.get_local_cnt(); int nparams = G_interpreter->get_argc_from_frame(vmg_ fp); /* allocate our extension */ ext_ = 0; vm_frameref_ext *ext = alloc_ext(vmg_ nlocals, nparams); /* save the extra frame information */ ext->fp = fp; ext->entryp = entry; /* get the function pointer value for the entry pointer */ f.get_fnptr(vmg_ &ext->entry); /* initialize the variable slots to nil */ for (int i = 0 ; i < nlocals + nparams ; ++i) ext->vars[i].set_nil(); /* * save the method context variables - these are immutable, so we can * save them immediately */ ext->self = *G_interpreter->get_self_val_from_frame(vmg_ fp); ext->defobj = G_interpreter->get_defining_obj_from_frame(vmg_ fp); ext->targobj = G_interpreter->get_orig_target_obj_from_frame(vmg_ fp); ext->defobj = G_interpreter->get_defining_obj_from_frame(vmg_ fp); } /* * create */ vm_obj_id_t CVmObjFrameRef::create( VMG_ vm_val_t *fp, const uchar *entry) { /* allocate an object ID */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, TRUE, FALSE); /* instantiate the new object */ new (vmg_ id) CVmObjFrameRef(vmg_ fp, entry); /* return the new object's ID */ return id; } /* * allocate our extension */ vm_frameref_ext *CVmObjFrameRef::alloc_ext(VMG_ int nlocals, int nparams) { /* delete any existing extension */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* figure the allocation size */ size_t siz = sizeof(vm_frameref_ext) + (nparams + nlocals - 1)*sizeof(vm_val_t); /* allocate the extension */ vm_frameref_ext *ext = (vm_frameref_ext *)G_mem->get_var_heap()->alloc_mem(siz, this); /* save the variable counts */ ext->nlocals = nlocals; ext->nparams = nparams; /* save the new extension in 'self' */ ext_ = (char *)ext; /* return the new extension */ return ext; } /* * notify of deletion */ void CVmObjFrameRef::notify_delete(VMG_ int) { /* free our extension */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* * mark references */ void CVmObjFrameRef::mark_refs(VMG_ uint state) { /* get the extension */ vm_frameref_ext *ext = get_ext(); /* mark the function */ if (ext->entry.typ == VM_OBJ) G_obj_table->mark_all_refs(ext->entry.val.obj, state); /* mark the method context objects */ if (ext->self.typ == VM_OBJ) G_obj_table->mark_all_refs(ext->self.val.obj, state); if (ext->defobj != VM_INVALID_OBJ) G_obj_table->mark_all_refs(ext->defobj, state); if (ext->targobj != VM_INVALID_OBJ) G_obj_table->mark_all_refs(ext->targobj, state); /* mark the invokee */ if (ext->invokee.typ == VM_OBJ) G_obj_table->mark_all_refs(ext->invokee.val.obj, state); /* mark each snapshot variable */ int i; const vm_val_t *v; for (i = ext->nlocals + ext->nparams, v = ext->vars ; i > 0 ; --i, ++v) { if (v->typ == VM_OBJ) G_obj_table->mark_all_refs(v->val.obj, state); } } /* * load from the image file */ void CVmObjFrameRef::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ self, ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from the image */ void CVmObjFrameRef::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ self, ptr, siz); } /* * load or reload our image data */ void CVmObjFrameRef::load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* read the number of variables */ int nlocals = osrp2(ptr); int nparams = osrp2(ptr+2); ptr += 4; /* allocate the extension */ vm_frameref_ext *ext = alloc_ext(vmg_ nlocals, nparams); /* * Since stack frames are inherently transient, a saved frame ref * object can't point back to a live stack frame, so on restore we have * to assume that our stack frame is inactive. */ ext->fp = 0; /* load the entry pointer */ vmb_get_dh(ptr, &ext->entry); ptr += VMB_DATAHOLDER; /* after other objects are loaded, resolve the entry pointer */ G_obj_table->request_post_load_init(self); /* load the method context variables */ vmb_get_dh(ptr, &ext->self); ptr += VMB_DATAHOLDER; ext->defobj = vmb_get_objid(ptr); ptr += VMB_OBJECT_ID; ext->targobj = vmb_get_objid(ptr); ptr += VMB_OBJECT_ID; ext->targprop = vmb_get_propid(ptr); ptr += VMB_PROP_ID; vmb_get_dh(ptr, &ext->invokee); ptr += VMB_DATAHOLDER; /* read the variable snapshot values */ int i; vm_val_t *v; for (i = nlocals + nparams, v = ext->vars ; i > 0 ; --i, ++v) { vmb_get_dh(ptr, v); ptr += VMB_DATAHOLDER; } } /* * save */ void CVmObjFrameRef::save_to_file(VMG_ class CVmFile *fp) { char buf[VMB_DATAHOLDER]; /* get our extension */ vm_frameref_ext *ext = get_ext(); /* * If our frame is still active, make a snapshot of the variables. The * stack frame itself is inherently transient, so we can only save the * snapshot version. */ if (ext->fp != 0) make_snapshot(vmg0_); /* save the variable counts */ fp->write_uint2(ext->nlocals); fp->write_uint2(ext->nparams); /* save the entry pointer */ vmb_put_dh(buf, &ext->entry); fp->write_bytes(buf, VMB_DATAHOLDER); /* save the method context values */ vmb_put_dh(buf, &ext->self); fp->write_bytes(buf, VMB_DATAHOLDER); fp->write_uint4(ext->defobj); fp->write_uint4(ext->targobj); fp->write_uint2(ext->targprop); vmb_put_dh(buf, &ext->invokee); fp->write_bytes(buf, VMB_DATAHOLDER); /* save the variable snapshot values */ int i; const vm_val_t *v; for (i = ext->nparams + ext->nlocals, v = ext->vars ; i > 0 ; --i, ++v) { vmb_put_dh(buf, v); fp->write_bytes(buf, VMB_DATAHOLDER); } } /* * restore */ void CVmObjFrameRef::restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups) { char buf[VMB_DATAHOLDER]; /* read the variables counts */ int nlocals = fp->read_int2(); int nparams = fp->read_int2(); /* allocate the extension */ vm_frameref_ext *ext = alloc_ext(vmg_ nlocals, nparams); /* * Since stack frames are inherently transient, a saved frame ref * object can't point back to a live stack frame, so on restore we have * to assume that our stack frame is inactive. */ get_ext()->fp = 0; /* load the entry pointer */ fp->read_bytes(buf, VMB_DATAHOLDER); fixups->fix_dh(vmg_ buf); vmb_get_dh(buf, &ext->entry); /* after other objects are loaded, resolve the entry pointer */ G_obj_table->request_post_load_init(self); /* restore the method context values */ fp->read_bytes(buf, VMB_DATAHOLDER); fixups->fix_dh(vmg_ buf); vmb_get_dh(buf, &ext->self); ext->defobj = (vm_obj_id_t)fp->read_uint4(); if (ext->defobj != VM_INVALID_OBJ) ext->defobj = fixups->get_new_id(vmg_ ext->defobj); ext->targobj = (vm_obj_id_t)fp->read_uint4(); if (ext->targobj != VM_INVALID_OBJ) ext->targobj = fixups->get_new_id(vmg_ ext->targobj); ext->targprop = (vm_prop_id_t)fp->read_uint2(); fp->read_bytes(buf, VMB_DATAHOLDER); fixups->fix_dh(vmg_ buf); vmb_get_dh(buf, &ext->invokee); /* load the snapshot values */ int i; vm_val_t *v; for (i = nlocals + nparams, v = ext->vars ; i > 0 ; --i, ++v) { fp->read_bytes(buf, VMB_DATAHOLDER); fixups->fix_dh(vmg_ buf); vmb_get_dh(buf, v); } } /* * After loading, resolve the entry pointer. We need to wait until loading * is complete to do this, since the entry pointer might refer to another * object, in which case the other object needs to be loaded before we can * resolve a pointer to it. */ void CVmObjFrameRef::post_load_init(VMG_ vm_obj_id_t self) { /* get our extension */ vm_frameref_ext *ext = get_ext(); /* resolve the entry pointer */ CVmFuncPtr f(vmg_ &ext->entry); ext->entryp = f.get(); } /* * Invalidate the frame. The VM calls this just before our frame exits. * We make a snapshot of the local variables in the frame, then we set our * frame pointer to null to indicate that we no longer have an active frame * in the stack. */ void CVmObjFrameRef::inval_frame(VMG0_) { if (ext_ != 0 && get_ext()->fp != 0) { /* make a snapshot of the frame */ make_snapshot(vmg0_); /* forget our frame pointer */ get_ext()->fp = 0; } } /* * make a snapshot of the local variables in the frame */ void CVmObjFrameRef::make_snapshot(VMG0_) { int i; vm_val_t *v; vm_frameref_ext *ext = get_ext(); vm_val_t *fp = ext->fp; /* make a copy of each local */ for (i = 0, v = ext->vars ; i < ext->nlocals ; ++i, ++v) *v = *G_interpreter->get_local_from_frame(vmg_ fp, i); /* make a copy of each parameter */ for (i = 0 ; i < ext->nparams ; ++i, ++v) *v = *G_interpreter->get_param_from_frame(vmg_ fp, i); } /* * index the frame: this looks up a variable by integer frame index */ int CVmObjFrameRef::index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val) { /* get our extension */ vm_frameref_ext *ext = get_ext(); /* check the type */ if (index_val->typ == VM_INT) { /* * It's a direct index into the frame. Make sure the value is in * range. */ int n = index_val->val.intval; if (n < 0 || n >= ext->nlocals + ext->nparams) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* * If we have an active stack frame, read the value from the stack; * otherwise read it from the snapshot array. */ if (ext->fp != 0) { /* get it from the stack frame */ if (n < ext->nlocals) { /* it's in the local variable range */ *result = *G_interpreter->get_local_from_frame(vmg_ ext->fp, n); } else { /* it's in the parameter range */ *result = *G_interpreter->get_param_from_frame( vmg_ ext->fp, n - ext->nlocals); } } else { /* there's no frame - get it from the snapshot array */ *result = ext->vars[n]; } } else { /* invalid index type */ err_throw(VMERR_INDEX_OUT_OF_RANGE); } /* handled */ return TRUE; } /* * assign an indexed value: this sets a variable's value by index */ int CVmObjFrameRef::set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val) { /* get our extension */ vm_frameref_ext *ext = get_ext(); /* check the type */ if (index_val->typ == VM_INT) { /* * It's a direct index into the frame. Make sure the value is in * range. */ int n = index_val->val.intval; if (n < 0 || n >= ext->nlocals + ext->nparams) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* * If we have an active stack frame, read the value from the stack; * otherwise read it from the snapshot array. */ if (ext->fp != 0) { /* get it from the stack frame */ if (n < ext->nlocals) { /* it's in the local variable range */ *G_interpreter->get_local_from_frame(vmg_ ext->fp, n) = *new_val; } else { /* it's in the parameter range */ *G_interpreter->get_param_from_frame( vmg_ ext->fp, n - ext->nlocals) = *new_val; } } else { /* there's no frame - get it from the snapshot array */ ext->vars[n] = *new_val; } } else { /* invalid index type */ err_throw(VMERR_INDEX_OUT_OF_RANGE); } /* the container doesn't change */ new_container->set_obj(self); /* handled */ return TRUE; } /* * Get the frame index for a local. This is the integer value that can be * used with the [] operator to index the FrameRef object to retrieve or * assign the value of the local. For a regular local, this is simply the * same as the local variable number in the frame. For a parameter, this * is 'n + nlocals', where 'n' is the parameter number in the frame. */ int CVmObjFrameRef::get_var_frame_index(const CVmDbgFrameSymPtr *symp) { /* check to see if it's a local or a parameter */ if (symp->is_local()) { /* it's a local - use the plain frame index */ return symp->get_var_num(); } else { /* it's a parameter - use the frame index plus the param offset */ return symp->get_var_num() + get_ext()->nlocals; } } /* * Get the value of a local given the variable descriptor */ void CVmObjFrameRef::get_local_val(VMG_ vm_val_t *result, const CVmDbgFrameSymPtr *sym) { /* * if we have an active stack frame, get the value from the frame; * otherwise get the value from our local snapshot */ vm_frameref_ext *ext = get_ext(); if (ext->fp != 0) { /* we have a frame - get the value from the frame */ G_interpreter->get_local_from_frame(vmg_ result, ext->fp, sym); } else { /* * There's no frame, so we must retrieve the value from the * snapshot. First, get the value of the local or parameter. Our * snapshot array consists of all of the locals followed by all of * the parameters, so local N is at vars[N] and parameter N is at * vars[nlocals + N]. */ vm_val_t *v = &ext->vars[sym->get_var_num() + (sym->is_param() ? ext->nlocals : 0)]; /* * If it's a context local, index the local by the context index. * Otherwise the value is simply the value in the snapshot array. */ if (sym->is_ctx_local()) v->ll_index(vmg_ result, sym->get_ctx_arr_idx()); else *result = *v; } } /* * Set the value of a local given the variable descriptor */ void CVmObjFrameRef::set_local_val(VMG_ const CVmDbgFrameSymPtr *sym, const vm_val_t *new_val) { /* * if we have an active stack frame, get the value from the frame; * otherwise get the value from our local snapshot */ vm_frameref_ext *ext = get_ext(); if (ext->fp != 0) { /* we have a frame - get the value from the frame */ G_interpreter->set_local_in_frame(vmg_ new_val, ext->fp, sym); } else { /* * There's no frame, so we must retrieve the value from the * snapshot. First, get the value of the local or parameter. Our * snapshot array consists of all of the locals followed by all of * the parameters, so local N is at vars[N] and parameter N is at * vars[nlocals + N]. */ vm_val_t *v = &ext->vars[sym->get_var_num() + (sym->is_param() ? ext->nlocals : 0)]; /* * If it's a context local, index the local by the context index. * Otherwise the value is simply the value in the snapshot array. */ if (sym->is_ctx_local()) { vm_val_t cont; vm_val_t ival; ival.set_int(sym->get_ctx_arr_idx()); if (v->typ == VM_OBJ) vm_objp(vmg_ v->val.obj)->set_index_val_ov( vmg_ &cont, v->val.obj, &ival, new_val); else err_throw(VMERR_CANNOT_INDEX_TYPE); } else *v = *new_val; } } /* * Get 'self' from the frame */ void CVmObjFrameRef::get_self(VMG_ vm_val_t *result) { /* return the 'self' value from the frame, if active, or our snapshot */ if (get_ext()->fp != 0) *result = *G_interpreter->get_self_val_from_frame(vmg_ get_ext()->fp); else *result = get_ext()->self; /* if the return value is an invalid object ID, return nil */ if (result->typ == VM_OBJ && result->val.obj == VM_INVALID_OBJ) result->set_nil(); } /* * Get 'definingobj' from the frame */ void CVmObjFrameRef::get_defobj(VMG_ vm_val_t *result) { /* get it from the active frame, or our snapshot if the frame has exited */ result->set_obj_or_nil( get_ext()->fp != 0 ? G_interpreter->get_defining_obj_from_frame(vmg_ get_ext()->fp) : get_ext()->defobj); } /* * Get 'targetobj' from the frame */ void CVmObjFrameRef::get_targobj(VMG_ vm_val_t *result) { /* get 'targetobj' from the frame, if active, or our snapshot */ result->set_obj_or_nil( get_ext()->fp != 0 ? G_interpreter->get_orig_target_obj_from_frame(vmg_ get_ext()->fp) : get_ext()->targobj); } /* * Get 'targetprop' from the frame */ void CVmObjFrameRef::get_targprop(VMG_ vm_val_t *result) { /* get 'targetobj' from the frame, if active, or our snapshot */ vm_prop_id_t prop = get_ext()->fp != 0 ? G_interpreter->get_target_prop_from_frame(vmg_ get_ext()->fp) : get_ext()->targprop; /* set the return value to the property, or nil if there isn't one */ if (prop != VM_INVALID_PROP) result->set_propid(prop); else result->set_nil(); } /* * Get 'invokee' from the frame */ void CVmObjFrameRef::get_invokee(VMG_ vm_val_t *result) { /* get 'invokee' from the frame, if active, or our snapshot */ *result = get_ext()->fp != 0 ? *G_interpreter->get_invokee_from_frame(vmg_ get_ext()->fp) : get_ext()->invokee; } /* * Create a method context object */ void CVmObjFrameRef::create_loadctx_obj(VMG_ vm_val_t *result) { vm_frameref_ext *ext = get_ext(); vm_val_t *fp = ext->fp; vm_obj_id_t self, defobj, targobj; vm_prop_id_t targprop; /* retrieve the method context elements */ if (fp != 0) { /* get the context elements from the live stack frame */ self = G_interpreter->get_self_from_frame(vmg_ fp); defobj = G_interpreter->get_defining_obj_from_frame(vmg_ fp); targobj = G_interpreter->get_orig_target_obj_from_frame(vmg_ fp); targprop = G_interpreter->get_target_prop_from_frame(vmg_ fp); } else { /* the stack frame has exited, so use our snapshot */ self = (ext->self.typ == VM_OBJ ? ext->self.val.obj : VM_INVALID_OBJ); defobj = ext->defobj; targobj = ext->targobj; targprop = ext->targprop; } /* create the context object */ CVmRun::create_loadctx_obj(vmg_ result, self, defobj, targobj, targprop); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmfref.h��������������������������������������������������������������������������0000664�0000000�0000000�00000041556�12650170723�0015327�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmfref.h - stack frame reference object Function There are two objects comprising a stack frame reference: - A StackFrameDesc object. This object is visible to the bytecode program and provides the API. This object records a particular return location in a function; it stores a return address, scope index, and a pointer to a StackFrameRef object. - The StackFrameRef object. This object is a reference to an activation frame. It records the live stack frame pointer and entry address, and stores a snapshot of the local variables, parameters, and method context variables after the frame terminates. This is an internal object with no public API; its interface is used only by StackFrameDesc. The reason we need two objects is that a given activation frame can have many execution points over the course of its lifetime. Each execution point has to be recorded separately, since a single function can have multiple local variable scopes with different symbol bindings. Notes Modified 04/22/00 MJRoberts - Creation */ #ifndef VMFREF_H #define VMFREF_H #include <stdlib.h> #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" /* ------------------------------------------------------------------------ */ /* * Extension for the frame descriptor object */ struct vm_framedesc_ext { /* the underlying StackFrameRef object */ vm_obj_id_t fref; /* frame index in the debug table for the method */ int frame_idx; /* the return offset */ uint ret_ofs; }; /* * Extension for stack frame reference object */ struct vm_frameref_ext { /* * Stack frame pointer. The VM automatically sets this to null when * the frame exits. This is also set to null when we restore a frame * from a saved state file, since stack frames are inherently transient * and thus show up as null references in a save file. */ vm_val_t *fp; /* method entry pointer value */ vm_val_t entry; /* resolved pointer to the method entry byte code */ const uchar *entryp; /* number of local variables and parameters */ int nlocals; int nparams; /* * Snapshot copy of the method context variables. We copy these values * at the same time we copy the local variables when detaching the * frame (see below). */ vm_val_t self; vm_obj_id_t defobj; vm_obj_id_t targobj; vm_prop_id_t targprop; vm_val_t invokee; /* * Snapshot copy of the locals and parameters. Just before the * associated routine returns to its caller, rendering the true stack * frame invalid, we make a private copy of the locals and parameters * from the frame here. This allows callers to access the variable * values after the frame becomes inactive, as long as 'self' continues * to be reachable. This effectively detaches the frame from the * stack, analogously to the way "context locals" for anonymous * functions work. The difference from anonymous functions is that our * mechanism here can detach any frame on the fly, without the compiler * having to make special arrangements in advance. * * We overallocate the structure to make room for nparams + nlocals * slots. The locals come first, followed by the parameters. */ vm_val_t vars[1]; }; /* ------------------------------------------------------------------------ */ /* * Stack frame descriptor object */ class CVmObjFrameDesc: public CVmObject { friend class CVmMetaclassFrameDesc; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is 'obj' a frame descriptor object? */ static int is_framedesc_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* get my StackFrameRef object */ vm_obj_id_t get_frame_ref_id() const { return get_ext()->fref; } class CVmObjFrameRef *get_frame_ref(VMG0_) const { return (class CVmObjFrameRef *)vm_objp(vmg_ get_frame_ref_id()); } /* find a local in our frame */ int find_local(VMG_ const vm_val_t *nval, class CVmDbgFrameSymPtr *symp); int find_local(VMG_ const textchar_t *sym, size_t len, class CVmDbgFrameSymPtr *symp); /* get the value of a local by name */ int get_local_val(VMG_ vm_val_t *result, const vm_val_t *name); /* set the value of a local by name */ int set_local_val(VMG_ const vm_val_t *name, const vm_val_t *new_val); /* create */ static vm_obj_id_t create(VMG_ vm_obj_id_t fref, int frame_idx, uint ret_ofs); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* set a property */ void set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { /* cannot set frame reference properties */ err_throw(VMERR_INVALID_SETPROP); } /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* index the frame: this looks up a variable's value by name */ virtual int index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val); /* assign an indexed value: this sets a variable's value by name */ virtual int set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* undo methods - we don't participate in undo, so these are no-ops */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { } /* mark references */ void mark_refs(VMG_ uint); /* we're immutable, so we don't have to worry about undo */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* we don't have any weak references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* image file operations */ void load_from_image(VMG_ vm_obj_id_t, const char *, size_t); void reload_from_image(VMG_ vm_obj_id_t, const char *, size_t); ulong rebuild_image(VMG_ char *, ulong); /* we're not loadable and we don't change */ int is_changed_since_load() const { return FALSE; } /* save/restore */ void save_to_file(VMG_ class CVmFile *fp); void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); protected: /* construction */ CVmObjFrameDesc() { ext_ = 0; } CVmObjFrameDesc(VMG_ vm_obj_id_t fref, int frame_idx, uint ret_ofs); /* allocate our extension */ vm_framedesc_ext *alloc_ext(VMG_ vm_obj_id_t fref, int frame_idx, uint ret_ofs); /* get my extension, properly cast */ vm_framedesc_ext *get_ext() const { return (vm_framedesc_ext *)ext_; } /* load the image data */ void load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* property evaluator - undefined property */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - is this frame still activie? */ virtual int getp_is_active(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get 'self' in the frame */ virtual int getp_get_self(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get 'definingobj' in the frame */ virtual int getp_get_defobj(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get 'targetobj' in the frame */ virtual int getp_get_targobj(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get 'targetprop' in the frame */ virtual int getp_get_targprop(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get a lookup table of the variables */ virtual int getp_get_vars(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get 'invokee' in the frame */ virtual int getp_get_invokee(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* function table */ static int (CVmObjFrameDesc::*func_table_[]) (VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * Stack frame reference object */ class CVmObjFrameRef: public CVmObject { friend class CVmMetaclassFrameRef; friend class CVmObjFrameDesc; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is 'obj' a frame reference object? */ static int is_frameref_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create */ static vm_obj_id_t create(VMG_ vm_val_t *fp, const uchar *entry); /* * Invalidate the frame reference. The bytecode interpreter calls this * when exiting a frame by 'return', 'throw', etc. */ void inval_frame(VMG0_); /* is the frame active? */ int is_active() { return get_ext()->fp != 0; } /* get 'self' from the frame */ void get_self(VMG_ vm_val_t *result); /* get 'definingobj' from the frame */ void get_defobj(VMG_ vm_val_t *result); /* get 'targetobj' from the frame */ void get_targobj(VMG_ vm_val_t *result); /* get 'targetprop' from the frame */ void get_targprop(VMG_ vm_val_t *result); /* get 'invokee' from the frame */ void get_invokee(VMG_ vm_val_t *result); /* create a method context object for the LOADCTX instruction */ void create_loadctx_obj(VMG_ vm_val_t *result); /* get the value of a local given its frame descriptor */ void get_local_val(VMG_ vm_val_t *result, const class CVmDbgFrameSymPtr *sym); /* set the value of a local given its frame descriptor */ void set_local_val(VMG_ const CVmDbgFrameSymPtr *sym, const vm_val_t *new_val); /* get the integer frame index for a variable given its descriptor */ int get_var_frame_index(const CVmDbgFrameSymPtr *symp); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* set a property */ void set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { /* cannot set frame reference properties */ err_throw(VMERR_INVALID_SETPROP); } /* index the frame: this looks up a variable by frame index */ virtual int index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val); /* assign an indexed value: this sets a variable by frame index */ virtual int set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* undo methods - we don't participate in undo, so these are no-ops */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { } /* mark references */ void mark_refs(VMG_ uint); /* we're immutable, so we don't have to worry about undo */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* we don't have any weak references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* image file operations */ void load_from_image(VMG_ vm_obj_id_t, const char *, size_t); void reload_from_image(VMG_ vm_obj_id_t, const char *, size_t); ulong rebuild_image(VMG_ char *, ulong); /* we're not loadable and we don't change */ int is_changed_since_load() const { return FALSE; } /* save/restore */ void save_to_file(VMG_ class CVmFile *fp); void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* post-load initialization */ void post_load_init(VMG_ vm_obj_id_t self); protected: /* construction */ CVmObjFrameRef() { ext_ = 0; } CVmObjFrameRef(VMG_ vm_val_t *fp, const uchar *entry); /* allocate our extension */ vm_frameref_ext *alloc_ext(VMG_ int nlocals, int nparams); /* get my extension, properly cast */ vm_frameref_ext *get_ext() const { return (vm_frameref_ext *)ext_; } /* make a snapshot of the locals */ void make_snapshot(VMG0_); /* load the image data */ void load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); }; /* ------------------------------------------------------------------------ */ /* * Registration table objects */ class CVmMetaclassFrameDesc: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "stack-frame-desc/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjFrameDesc(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjFrameDesc(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { err_throw(VMERR_BAD_DYNAMIC_NEW); AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjFrameDesc::call_stat_prop( vmg_ result, pc_ptr, argc, prop); } }; class CVmMetaclassFrameRef: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "stack-frame-ref/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjFrameRef(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjFrameRef(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { err_throw(VMERR_BAD_DYNAMIC_NEW); AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjFrameRef::call_stat_prop( vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMFREF_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjFrameRef) VM_REGISTER_METACLASS(CVmObjFrameDesc) ��������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmfunc.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000014424�12650170723�0015665�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmfunc.cpp - T3 VM function header operations Function Notes Modified 11/24/99 MJRoberts - Creation */ #include "vmfunc.h" #include "vmpool.h" #include "vmglob.h" #include "vmtype.h" #include "vmobj.h" #include "vmbif.h" #include "vmstr.h" #include "vmdynfunc.h" /* ------------------------------------------------------------------------ */ /* * Initialize from a code pool address */ CVmFuncPtr::CVmFuncPtr(VMG_ pool_ofs_t ofs) { p_ = (const uchar *)G_code_pool->get_ptr(ofs); } /* * Initialize from a vm_val_t */ int CVmFuncPtr::set(VMG_ const vm_val_t *val) { vm_val_t invoker; /* we might need to re-resolve from an invoker, so loop until resolved */ for (;;) { /* check the type */ switch (val->typ) { case VM_OBJ: case VM_OBJX: /* object - check to see if it's invokable */ if (vm_objp(vmg_ val->val.obj)->get_invoker(vmg_ &invoker)) { /* it's invokable - re-resolve using the invoker */ val = &invoker; } else { /* other object types are not function pointers */ set((uchar *)0); return FALSE; } break; case VM_FUNCPTR: case VM_CODEOFS: /* function pointer - get the address from the code pool */ set((const uchar *)G_code_pool->get_ptr(val->val.ofs)); return TRUE; case VM_CODEPTR: /* direct code pointer - the value contains the address */ set((const uchar *)val->val.ptr); return TRUE; case VM_BIFPTR: case VM_BIFPTRX: /* built-in function pointer */ { /* * decode the function set information from the value, and * look up the bif descriptor in the registration table */ const vm_bif_desc *desc = G_bif_table->get_desc( val->val.bifptr.set_idx, val->val.bifptr.func_idx); /* fail if there's no descriptor */ if (desc == 0) return FALSE; /* point to the synthetic header */ set((const uchar *)desc->synth_hdr); } return TRUE; default: /* other types are invalid */ set((uchar *)0); return FALSE; } } } /* ------------------------------------------------------------------------ */ /* * Get a vm_val_t pointer to this function */ int CVmFuncPtr::get_fnptr(VMG_ vm_val_t *v) { /* * Try translating the method address to a code pool offset. If that * succeeds, it's a regular static function. */ pool_ofs_t entry_ofs; if (G_code_pool->get_ofs((const char *)p_, &entry_ofs)) { /* it's a regular function call */ v->set_fnptr(entry_ofs); return TRUE; } /* * It's not a static function pointer, so it must be a dynamic * function. A DynamicFunc stores a prefix header before the start of * our method header, containing the DynamicFunc object ID. Read the * object ID, and return it as a VM_OBJ value. */ vm_obj_id_t obj = CVmDynamicFunc::get_obj_from_prefix(vmg_ p_); if (obj != VM_INVALID_OBJ) { v->set_obj(obj); return TRUE; } /* we couldn't find any valid object */ v->set_nil(); return FALSE; } /* ------------------------------------------------------------------------ */ /* * Set up an exception table pointer for this function. Returns true if * successful, false if there's no exception table. */ int CVmFuncPtr::set_exc_ptr(CVmExcTablePtr *exc_ptr) const { /* set the exception pointer based on my address */ return exc_ptr->set(p_); } /* * Set up a debug table pointer for this function. Returns true if * successful, false if there's no debug table. */ int CVmFuncPtr::set_dbg_ptr(CVmDbgTablePtr *dbg_ptr)const { /* set the debug pointer based on my address */ return dbg_ptr->set(p_); } /* ------------------------------------------------------------------------ */ /* * Get the symbol data from a local variable symbol in the frame table */ const char *CVmDbgFrameSymPtr::get_symptr(VMG0_) const { /* get a pointer to the symbol field in this record */ const char *symp = (const char *)p_ + G_dbg_lclsym_hdr_size; /* * if it's stored inline, this points directly to the symbol data; * otherwise it's a constant pool offset pointing to the symbol data */ if (is_sym_inline()) return symp; else return G_const_pool->get_ptr(osrp4(symp)); } void CVmDbgFrameSymPtr::get_str_val(VMG_ vm_val_t *val) const { /* get a pointer to the symbol field in this record */ const char *symp = (const char *)p_ + G_dbg_lclsym_hdr_size; /* check whether the symbol string is in-line or in the constant pool */ if (is_sym_inline()) { /* it's inline, so we need to create a string object for it */ val->set_obj(CVmObjString::create(vmg_ FALSE, symp + 2, osrp2(symp))); } else { /* it's in the constant pool, so we can just return a VM_SSTR */ val->set_sstring(osrp4(symp)); } } /* ------------------------------------------------------------------------ */ /* * is this frame nested in the given frame? */ int CVmDbgFramePtr::is_nested_in(VMG_ const class CVmDbgTablePtr *dp, int i) { /* scan our parent list for 'i' */ for (int parent = get_enclosing_frame() ; parent != 0 ; ) { /* if this parent is 'i', we're inside 'i' */ if (parent == i) return TRUE; /* set up a pointer to the parent frame */ CVmDbgFramePtr fp; dp->set_frame_ptr(vmg_ &fp, parent); /* get its enclosing frame */ parent = fp.get_enclosing_frame(); } /* it's not a nested frame */ return FALSE; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmfunc.h��������������������������������������������������������������������������0000664�0000000�0000000�00000043505�12650170723�0015334�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/vmfunc.h,v 1.3 1999/07/11 00:46:59 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmfunc.h - T3 VM Function Header definitions Function Defines the layout of a function header, which is the block of data immediately preceding the first byte code instruction in every function. The function header is stored in binary portable format, so that image files can be loaded directly into memory and executed without translation. Notes Modified 11/20/98 MJRoberts - Creation */ #ifndef VMFUNC_H #define VMFUNC_H #include "t3std.h" #include "vmtype.h" #include "vmglob.h" /* ------------------------------------------------------------------------ */ /* * FUNCTION HEADER */ /* * The function header is a packed array of bytes, with each element stored * in a canonical binary format for binary portability. No padding is * present except where otherwise specified. The fields in the function * header, starting at offset zero, are: * * UBYTE argc - the number of parameters the function expects to receive. * If the high-order bit is set (i.e., (argc & 0x80) != 0), then the * function takes a variable number of parameters, with a minimum of (argc * & 0x7f) and no maximum. If the high-order bit is clear, argc gives the * exact number of parameters required. * * UBYTE optional_argc - number of additional optional parameters the * function can receive, beyond the fixed arguments given by the 'argc' * field. * * UINT2 locals - the number of local variables the function uses. This * does not count the implicit argument counter local variable, which is * always pushed by the VM after setting up a new activation frame. * * UINT2 total_stack - the total number of stack slots required by the * function, including local variables, intermediate results of * calculations, and actual parameters to functions invoked by this code. * * UINT2 exception_table_ofs - the byte offset from the start of this * method header of the function's exception table. This value is zero if * the function has no exception table. * * UINT2 debug_ofs - the byte offset from the start of this method header * of the function's debugger records. This value is zero if the function * has no debugger records. */ /* minimum function header size supported by this version of the VM */ const size_t VMFUNC_HDR_MIN_SIZE = 10; class CVmFuncPtr { public: CVmFuncPtr() { p_ = 0; } CVmFuncPtr(VMG_ pool_ofs_t ofs); CVmFuncPtr(const char *p) { p_ = (const uchar *)p; } CVmFuncPtr(const uchar *p) { p_ = p; } CVmFuncPtr(VMG_ const vm_val_t *val) { set(vmg_ val); } /* initialize with a pointer to the start of the function */ void set(const uchar *p) { p_ = p; } /* set from a vm_val_t; returns true on success, false on failure */ int set(VMG_ const vm_val_t *val); /* get the header pointer */ const uchar *get() const { return p_; } /* copy from another function pointer */ void copy_from(const CVmFuncPtr *fp) { p_ = fp->p_; } /* get a vm_val_t pointer to this function */ int get_fnptr(VMG_ vm_val_t *v); /* get the minimum argument count */ int get_min_argc() const { /* get the argument count, but mask out the varargs bit */ return (int)(get_argc() & 0x7f); } /* get the maximum argument count, not counting varargs */ int get_max_argc() const { return get_min_argc() + (int)get_opt_argc(); } /* is this a varargs function? */ int is_varargs() const { return ((get_argc() & 0x80) != 0); } /* * check an actual parameter count for correctness; returns true if * the count is correct for this function, false if not */ int argc_ok(int argc) const { /* check for match to the min-max range */ if (argc >= get_min_argc() && argc <= get_max_argc()) { /* we have an exact match, so we're fine */ return TRUE; } else if (is_varargs() && argc > get_min_argc()) { /* * we have variable arguments, and we have at least the * minimum, so we're okay */ return TRUE; } else { /* * either we don't have variable arguments, or we don't have * the minimum varargs count - in either case, we have an * argument count mistmatch */ return FALSE; } } /* * Get the internal argument count. This has the high bit set for a * varargs function, and the low-order seven bits give the nominal * argument count. If this is a varargs function, the nominal * argument count is the minimum count: any actual number of arguments * at least the nominal count is valid. If this is not a varargs * function, the nominal count is the exact count: the actual number * of arguments must match the nominal count. */ uchar get_argc() const { return *(p_ + 0); } /* get the additional optional argument count */ uint get_opt_argc() const { return *(p_ + 1); } /* get the number of locals */ uint get_local_cnt() const { return osrp2(p_ + 2); } /* get the total stack slots required by the function */ uint get_stack_depth() const { return osrp2(p_ + 4); } /* get the exception table offset */ uint get_exc_ofs() const { return osrp2(p_ + 6); } /* get the debugger records table offset */ uint get_debug_ofs() const { return osrp2(p_ + 8); } /* * Set up an exception table pointer for this function. Returns * true if successful, false if there's no exception table. */ int set_exc_ptr(class CVmExcTablePtr *exc_ptr) const; /* * Set up a debug table pointer for this function. Returns true if * successful, false if there's no debug table. */ int set_dbg_ptr(class CVmDbgTablePtr *dbg_ptr) const; private: /* pointer to the method header */ const uchar *p_; }; /* ------------------------------------------------------------------------ */ /* * EXCEPTION TABLE */ /* * The exception table starts with a count indicating how many elements * are in the table, followed by the table entries. Each entry in the * table specifies the handler for one protected range of code. We * search the table in forward order, so the handlers must be stored in * order of precedence. * * Each table entry contains: * * UINT2 start_ofs - the starting offset (as a byte offset from the * start of the function) of the protected range for this handler. * * UINT2 end_ofs - the ending offset (as a byte offset from the start of * the function) of the protected range for this handler. The range is * inclusive of this offset, so a one-byte range would have start_ofs * and end_ofs set to the same value. * * UINT4 exception_class - the object ID of the class of exception * handled by this handler. * * UINT2 handler_ofs - the handler offset (as a byte offset from the * start of the function). This is the offset of the first instruction * of the code to be invoked to handle this exception. */ /* * Exception Table Entry Pointer */ class CVmExcEntryPtr { /* let CVmExcTablePtr initialize us */ friend class CVmExcTablePtr; public: /* get the starting/ending offset from this entry */ uint get_start_ofs() const { return osrp2(p_); } uint get_end_ofs() const { return osrp2(p_ + 2); } /* get the exception class's object ID */ vm_obj_id_t get_exception() const { return (vm_obj_id_t)t3rp4u(p_ + 4); } /* get the handler byte code offset */ uint get_handler_ofs() const { return osrp2(p_ + 8); } /* increment the pointer to the next entry in the table */ void inc(VMG0_) { p_ += G_exc_entry_size; } private: /* initialize with a pointer to the first byte of our entry */ void set(const uchar *p) { p_ = p; } /* pointer to the first byte of our entry */ const uchar *p_; }; /* * Exception Table Pointer */ class CVmExcTablePtr { public: /* * Initialize with a pointer to the start of the function -- we'll * read the exception table offset out of the method header. * Returns true if the function has an exception table, false if * there is no exception table defined in the function. */ int set(const uchar *p) { CVmFuncPtr func; /* set up the function pointer */ func.set(p); /* if there's no exception table, simply return this information */ if (func.get_exc_ofs() == 0) return FALSE; /* set up our pointer by reading from the header */ p_ = p + func.get_exc_ofs(); /* indicate that there is a valid exception table */ return TRUE; } /* get the number of entries in the table */ size_t get_count() const { return osrp2(p_); } /* initialize a CVmExcEntryPtr with the entry at the given index */ void set_entry_ptr(VMG_ CVmExcEntryPtr *entry, size_t idx) const { entry->set(p_ + 2 + (idx * G_exc_entry_size)); } private: /* pointer to the first byte of the exception table */ const uchar *p_; }; /* ------------------------------------------------------------------------ */ /* * DEBUGGER RECORDS TABLE */ /* * The debugger table consists of three sections. The first section is * the header, with general information on the method or function. The * second section is the line records, which give the code boundaries of * the source lines. The third section is the frame records, giving the * local symbol tables. */ /* * Debugger source line entry pointer */ class CVmDbgLinePtr { /* let CVmDbgTablePtr initialize us */ friend class CVmDbgTablePtr; public: /* * get the byte-code offset (from method start) of the start of the * byte-code for this line */ uint get_start_ofs() const { return osrp2(p_); } /* get the source file ID (an index into the global source file list) */ uint get_source_id() const { return osrp2(p_ + 2); } /* get the line number in the source file */ ulong get_source_line() const { return t3rp4u(p_ + 4); } /* get the frame ID (a 1-based index into our local frame table) */ uint get_frame_id() const { return osrp2(p_ + 8); } /* increment the pointer to the next entry in the table */ void inc(VMG0_) { p_ += G_line_entry_size; } /* set from another line pointer */ void copy_from(const CVmDbgLinePtr *p) { p_ = p->p_; } private: /* initialize with a pointer to the first byte of our entry */ void set(const uchar *p) { p_ = p; } /* pointer to the first byte of our entry */ const uchar *p_; }; /* * Debugger frame symbol entry pointer */ class CVmDbgFrameSymPtr { /* let CVmDbgFramePtr initialize us */ friend class CVmDbgFramePtr; public: /* get the local/parameter number */ uint get_var_num() const { return osrp2(p_); } /* get the context array index (for context locals) */ vm_prop_id_t get_ctx_arr_idx() const { return (vm_prop_id_t)osrp2(p_ + 4); } /* determine if I'm a local or a parameter */ int is_local() const { return (get_flags() & 1) == 0; } int is_param() const { return (((get_flags() & 1) != 0) && !is_ctx_local()); } /* determine if I'm a context local */ int is_ctx_local() const { return (get_flags() & 2) != 0; } /* get the length of my name string */ uint get_sym_len(VMG0_) const { return osrp2(get_symptr(vmg0_)); } /* get a pointer to my name string - this is not null-terminated */ const char *get_sym(VMG0_) const { return get_symptr(vmg0_) + 2; } /* increment this pointer to point to the next symbol in the frame */ void inc(VMG0_) { /* skip the in-line symbol, or the pointer */ if (is_sym_inline()) p_ += get_sym_len(vmg0_) + 2; else p_ += 4; /* skip the header */ p_ += G_dbg_lclsym_hdr_size; } /* is my symbol in-line or in the constant pool? */ int is_sym_inline() const { return !(get_flags() & 0x0004); } /* * Set up a vm_val_t for the symbol. For an in-line symbol, this * creates a new string object; for a constant pool element, this * returns a VM_SSTR pointer to it. */ void get_str_val(VMG_ vm_val_t *val) const; private: /* get my flags value */ uint get_flags() const { return osrp2(p_ + 2); } /* * get a pointer to my symbol data - this points to a UINT2 length * prefix followed by the bytes of the UTF-8 string */ const char *get_symptr(VMG0_) const; /* initialize with a pointer to the first byte of our entry */ void set(const uchar *p) { p_ = p; } /* pointer to the first byte of our entry */ const uchar *p_; }; /* * Debugger frame entry pointer */ class CVmDbgFramePtr { /* let CVmDbgTablePtr initialize us */ friend class CVmDbgTablePtr; public: /* copy from another frame pointer */ void copy_from(const CVmDbgFramePtr *frame) { /* copy the original frame's pointer */ p_ = frame->p_; } /* get the ID of the enclosing frame */ uint get_enclosing_frame() const { return osrp2(p_); } /* get the number of symbols in the frame */ uint get_sym_count() const { return osrp2(p_ + 2); } /* set up a pointer to the first symbol */ void set_first_sym_ptr(VMG_ CVmDbgFrameSymPtr *entry) { entry->set(p_ + G_dbg_frame_size); } /* get the bytecode range covered by the frame */ uint get_start_ofs(VMG0_) { return G_dbg_frame_size >= 8 ? osrp2(p_ + 4) : 0; } uint get_end_ofs(VMG0_) { return G_dbg_frame_size >= 8 ? osrp2(p_ + 6) : 0; } /* is this frame nested in the given frame? */ int is_nested_in(VMG_ const class CVmDbgTablePtr *dp, int i); private: /* initialize with a pointer to the first byte of our entry */ void set(const uchar *p) { p_ = p; } /* pointer to the first byte of our entry */ const uchar *p_; }; /* * Debugger Records Table Pointer */ class CVmDbgTablePtr { public: /* * Initialize with a pointer to the start of the function -- we'll * read the debugger table offset out of the method header. Returns * true if the function has debugger records, false if there is no * debugger table defined in the function. */ int set(const uchar *p) { CVmFuncPtr func; /* if the pointer is null, there's obviously no function pointer */ if (p == 0) return FALSE; /* set up the function pointer */ func.set(p); /* if there's no debugger table, simply return this information */ if (func.get_debug_ofs() == 0) return FALSE; /* set up our pointer by reading from the header */ p_ = p + func.get_debug_ofs(); /* indicate that there is a valid debugger records table */ return TRUE; } /* copy from another debug table pointer */ void copy_from(const CVmDbgTablePtr *table) { /* copy the other table's location */ p_ = table->p_; } /* get the number of source line entries in the table */ size_t get_line_count(VMG0_) const { return osrp2(p_ + G_dbg_hdr_size); } /* get the number of frame entries in the table */ size_t get_frame_count(VMG0_) const { return osrp2(p_ + get_frame_ofs(vmg0_)); } /* initialize a CVmDbgLinePtr with the entry at the given index */ void set_line_ptr(VMG_ CVmDbgLinePtr *entry, size_t idx) const { entry->set(p_ + G_dbg_hdr_size + 2 + (idx * G_line_entry_size)); } /* initialize a CVmDbgFramePtr with the entry at the given index */ void set_frame_ptr(VMG_ CVmDbgFramePtr *entry, size_t idx) const { size_t index_ofs; size_t frame_ofs; /* * Compute the location of the index table entry - note that * 'idx' is a one-based index value, so we must decrement it * before performing our offset arithmetic. Note also that we * must add two bytes to get past the count field at the start * of the frame table. Each index entry is two bytes long. * * (If we were clever, we would distribute the multiply-by-two, * which would yield a constant subtraction of two, which would * cancel the constant addition of two. Let's hope the C++ * catches on to this, because we would rather not be clever and * instead write it explicitly for greater clarity.) */ index_ofs = get_frame_ofs(vmg0_) + 2 + (2 * (idx - 1)); /* read the frame offset from the entry */ frame_ofs = osrp2(p_ + index_ofs); /* * the frame offset in the table is relative to the location of * the table location containing the entry, so add the index * offset to the frame offset to get the actual location of the * frame entry */ entry->set(p_ + index_ofs + frame_ofs); } private: /* get the offset to the start of the frame table */ size_t get_frame_ofs(VMG0_) const { /* * the frame table follows the line records, which follow the * debug table header and the line counter, plus another two * bytes for the post-frame offset pointer */ return (G_dbg_hdr_size + 2 + (get_line_count(vmg0_) * G_line_entry_size) + 2); } /* pointer to the first byte of the debugger records table */ const uchar *p_; }; #endif /* VMFUNC_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmglob.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000003400�12650170723�0015645�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/vmglob.cpp,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmglob.cpp - global definitions Function Defines the global variables. Notes Modified 11/28/98 MJRoberts - Creation */ /* actually define the variables (i.e., don't make them 'extern') */ #define VMGLOB_DECLARE /* include the globals header */ #include "t3std.h" #include "vmglob.h" /* and some other headers that have special global definitions */ #include "vmerr.h" #include "vmstack.h" #include "vmrun.h" /* ------------------------------------------------------------------------ */ /* * In the VARS configuration, we need to provide storage for all of the * variables. */ #ifdef VMGLOB_VARS /* we need to include headers for objects we define in-line */ #include "vmrun.h" #include "vmstack.h" #include "vmpool.h" #include "vmparam.h" #include "vmpredef.h" #include "vminit.h" #include "vmtobj.h" /* remove the declaring macros for the globals */ #undef VM_GLOBAL_OBJDEF #undef VM_GLOBAL_PREOBJDEF #undef VM_GLOBAL_PRECOBJDEF #undef VM_GLOBAL_VARDEF #undef VM_GLOBAL_ARRAYDEF /* provide new defining macros for the globals */ #define VM_GLOBAL_OBJDEF(typ, var) typ *G_##var##_X; #define VM_GLOBAL_PREOBJDEF(typ, var) typ G_##var##_X; #define VM_GLOBAL_PRECOBJDEF(typ, var, ctor_args) typ G_##var##_X ctor_args; #define VM_GLOBAL_VARDEF(typ, var) typ G_##var##_X; #define VM_GLOBAL_ARRAYDEF(typ, var, eles) typ G_##var##_X[eles]; /* include the variable definitions */ #include "vmglobv.h" #endif /* VMGLOB_VARS */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmglob.h��������������������������������������������������������������������������0000664�0000000�0000000�00000053062�12650170723�0015323�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/vmglob.h,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmglob.h - T3 VM globals Function The T3 VM requires a number of static variables that are shared by several subsystems. This file defines the globals and a scheme for accessing them. Important: note that some VM globals aren't part of the scheme described and defined below. In particular, the try-catch-throw exception handling subsystem doesn't use it; the dynamic compiler doesn't use it; read-only statics don't use it; and some global caches don't use it. The exception subsystem opts out because its globals constitute a critical performance bottleneck, plus they need to be per-thread for multi-threaded builds; so they use native thread-local storage if threads are used, otherwise they're just ordinary globals. The dynamic compiler doesn't use the VM globals scheme because the compiler wasn't originally designed to be used within the VM at all; by the time we adapted it, it was too late and would have required too much work to bring it into the scheme. Read-only statics can be excluded because they can be safely shared among threads and/or instances without any extra work. And caches can safely opt out as long as the information cached is truly global, not private to a VM instance; but note that caches might need to use per-thread storage, or else mutexes, if they can be accessed from multiple threads. The globals can be configured in four different ways, depending on how the T3 VM is to be used: 1. As individual external variables. This is the most efficient way to configure the globals, but this can't be used when multiple independent VM instances are needed in a single process, because each VM would need its own copy of the globals. This is the fastest way to lay out the globals, because it allows the compiler to generate code that accesses internal members of performance-critical global objects directly, without any pointer dereferences. 2. As members of a static global structure. This is the second most efficient way to configure the globals, but this can't be used when multiple independent VM instances are needed, because each VM would need its own copy of the globals, which isn't possible in this configuration. (Note that accessing members of a global static structure is identical to accessing global variables, since the compiler can compute the relative address of a structure member at compile time and hence does not need to perform any run-time pointer arithmetic or any other computation beyond what it would normally use for global variable access. However, in this configuration, it's not possible to allocate any of the global objects in-line within the master global structure, so each global object's internal members must be accessed through one pointer dereference.) 3. As members of an allocated structure whose address is contained in a global static variable. This is slightly less efficient than method 1, in that the global pointer must be dereferenced on each global variable access. This method allows multiple VM instances to be used, as long as the host application specifically stores the correct structure pointer in the global static variable before each call to the VM; this can be used when the host system uses the VM in only one thread. 4. As members of an allocated structure whose address is passed as a parameter to each function that needs global variable access. As with method 2, the host application creates a structure per VM instance it requires, and then passes a pointer to the correct structure on each call to a VM function; the VM internally passes this pointer in its own function calls where needed. This is the most flexible method, in that the VM can be used in multiple thread simultaneously (as long as each thread has a separate VM global structure instance), but is the least efficient, because the structure pointer must be passed around on all calls. To select a configuration, define one of the following preprocessor symbols in the makefile: VMGLOB_VARS - method 1 - individual global variables VMGLOB_STRUCT - method 2 - global static structure VMGLOB_POINTER - method 3 - global static pointer to allocated structure VMGLOB_PARAM - method 4 - pointer to structure passed as parameter We provide all of these different possible configurations because of the trade-offs involved in selecting one. The host application's needs should be determined, and the most efficient configuration that meets those needs should be chosen. Notes Modified 11/28/98 MJRoberts - Creation */ #ifndef VMGLOB_H #define VMGLOB_H #include <stddef.h> #include "t3std.h" #include "vmpoolsl.h" /* ------------------------------------------------------------------------ */ /* * HOST SYSTEM GLOBAL INITIALIZATION * * The host system should declare a local variable as follows: * * vm_globals *vmg__; // two underscores * * The program must call vmglob_alloc() each time it wants to initialize a * global variable context. This routine must always be called at least * once. (In the VMGLOB_STRUCT or VMGLOB_VARS configuration, this routine * has no effect, but should still be called so that the code will work * unchanged in other configurations.) Calling this routine will overwrite * the current static global variable pointer in the VMGLOB_POINTER * configuration, so the caller must take care to keep track of each set of * globals it allocates for later restoration. * * When calling vmglob_alloc(), assign the return value to vmg__. */ /* * HOST SYSTEM GLOBAL TERMINATION * * The caller should invoke vmglob_delete() for each set of globals it * allocated. */ /* * HOST SYSTEM VM FUNCTION CALLS * * In each call to a VM function, the host system should include the * macro "vmg_" (one underscore) at the start of each parameter list; do * not put a comma after vmg_. Call functions that take no other * parameters with vmg0_ instead of vmg_. */ /* ------------------------------------------------------------------------ */ /* * INTERNAL FUNCTION DECLARATION AND CALLING * * Every function that requires global access, or which calls a function * that requires global access, must put "VMG_" at the very start of its * formal parameters declaration. Do not put a comma after VMG_, but * simply list the first parameter. If the function has no parameters * at all, use VMG0_ instead of VMG_. For example: * * void CVmClass::func1(VMG_ const textchar_t *str, size_t len) { ... } *. void CVmClass::func2(VMG0_) { ... } * * In each call to a function that uses global variables or calls * functions that use global variables, put "vmg_" at the very start of * the actual parmaeters list in the call; do not put a comma after the * vmg_. If the function takes no parameters at all, use vmg0_ instead * of vmg_. * * func1(vmg_ "test", 4); *. func2(vmg0_); */ /* ------------------------------------------------------------------------ */ /* * Functions that themselves can't participate in the prototype * mechanism, because they present an external API to other subsystems, * must keep track of the globals on their own. To do this, they must * stash the global pointer in their own context structure - use * VMGLOB_ADDR to get the address of the global structure. When passing * this stashed pointer back to function calls, use vmg_var(x) (for the * first argument to a function with multiple arguments) or vmg_var0(x) * (for a single-argument call), as appropriate, where x is the local * copy. * * To access globals from this type of routine, put the VMGLOB_PTR(x) * macro in with the local variable decalarations for the function, * passing as the argument the stashed address of the global structure. * This will set things up so that the G_xxx variables are accessible * through a local pointer, when necessary, and will also make the vmg_ * and vmg0_ argument macros work properly. See the examples below. */ #if 0 /* EXAMPLES ONLY - THIS CODE IS FOR DOCUMENTATION ONLY */ struct my_context_def { /* holder for VM globals structure pointer */ vm_globals *vmg; }; int func_calling_external_interfaces(VMG_ int x, int y, int z) { my_context_def ctx; /* stash the address of the VM globals in my private context */ ctx.vmg = VMGLOB_ADDR; /* call my external API function */ external_api_func(&ctx); } int func_called_from_external_interface(my_context_def *ctx, int x, int y) { /* set up access to the VM globals through my stashed context */ VMGLOB_PTR(ctx->vmg); /* access a global variable */ G_myvar->do_something(); /* call a function using a VMG_ prototype */ G_myvar->do_something_else(vmg_ x, y); } #endif /* 0 */ /* ------------------------------------------------------------------------ */ /* * Conditional access to globals. * * Some code that accesses global variables might need to be able to run * during startup or shutdown. On platforms where the globals are * dynamically allocated, such code might need to test to see not only * whether or not particular global variable has been allocated, but also * whether or not the memory containing the global variables themselves * exists. E.g., if you want to access the console object in code that * might be called during early startup or late termination, it's not good * enough to test if G_console constains a non-null object pointer, since * merely accessing G_console itself will dereference the global variable * structure pointer, and this pointer might be null on platforms where the * globals are allocated. * * For such situations, use VMGLOB_IF_AVAIL(varname) to cover the global * variable name. On platforms where the globals are allocated, this will * return null if the globals themselves aren't yet allocated or have * already been freed, otherwise it'll return the variable's value. */ #if 0 /* EXAMPLE ONLY - THIS CODE IS FOR DOCUMENTATION PURPOSES */ CVmConsole *con = VMGLOB_IF_AVAIL(G_console); #endif /* ------------------------------------------------------------------------ */ /* * Set up to define the global variables. For the POINTER and PARAM * configurations, put pointers to the global structures in a structure. * For the static STRUCT configuration, actually allocate all of the * objects statically. * * Use VM_GLOBAL_OBJDEF() to define the entry for an object (struct or * class) type. The global variable will be a pointer to this type. * * Use VM_GLOBAL_PREOBJDEF to define the entry for an object type that's to * be defined as a pre-allocated static object in the VARS configuration. * This can be used for objects that (1) have very frequent access and thus * should have their fields reachable directly as statics rather than via * static pointers, and (2) need no constructor parameters and thus can be * pre-allocated at compile-time. In the VARS configuration, these * variables can be allocated at compile-time, which allows the compiler to * generate code that accesses the objects' internal members without any * pointer dereferences. * * Any variable defined with VM_GLOBAL_PREOBJDEF MUST have its accessor * defined through VMGLOB_PREACCESS() rather than VMGLOB_ACCESS(). * * Use VM_GLOBAL_VARDEF to define a scalar variable. */ #if defined(VMGLOB_POINTER) || defined(VMGLOB_PARAM) || defined(VMGLOB_STRUCT) #define VM_GLOBALS_BEGIN struct vm_globals { #define VM_GLOBAL_OBJDEF(typ, var) typ *var; #define VM_GLOBAL_PREOBJDEF(typ, var) typ *var; #define VM_GLOBAL_PRECOBJDEF(typ, var, ctor_args) typ *var; #define VM_GLOBAL_VARDEF(typ, var) typ var; #define VM_GLOBAL_ARRAYDEF(typ, var, eles) typ var[eles]; #define VM_GLOBALS_END }; /* * we do allocate all global objects, including external objects; hence * external globals are non-static */ #define VM_IF_ALLOC_PRE_GLOBAL(x) x #define VM_IFELSE_ALLOC_PRE_GLOBAL(x, y) x #define VM_PRE_GLOBALS_ARE_STATIC FALSE #endif #if defined(VMGLOB_VARS) #define VM_GLOBALS_BEGIN #define VM_GLOBAL_OBJDEF(typ, var) extern typ *G_##var##_X; #define VM_GLOBAL_PREOBJDEF(typ, var) extern typ G_##var##_X; #define VM_GLOBAL_PRECOBJDEF(typ, var, ctor_args) extern typ G_##var##_X; #define VM_GLOBAL_VARDEF(typ, var) extern typ G_##var##_X; #define VM_GLOBAL_ARRAYDEF(typ, var, eles) extern typ G_##var##_X[eles]; #define VM_GLOBALS_END /* we don't actually need a structure for the globals; use a dummy */ struct vm_globals { int x; }; /* external global objects are statically allocated */ #define VM_IF_ALLOC_PRE_GLOBAL(x) #define VM_IFELSE_ALLOC_PRE_GLOBAL(x, y) y #define VM_PRE_GLOBALS_ARE_STATIC TRUE #endif /* define the globals */ #include "vmglobv.h" /* ------------------------------------------------------------------------ */ /* * If we're not including from the global-defining source file, merely * declare the globals. */ #ifndef VMGLOB_DECLARE #define VMGLOB_DECLARE extern #endif /* ------------------------------------------------------------------------ */ /* * INDIVIDUAL STATIC GLOBAL VARIABLES configuration. In this * configuration, the globals are defined as individual global variables. * This is the most efficient configuration, but it only allows one VM * instance in a given process. */ #ifdef VMGLOB_VARS /* initialization - this has no effect in this mode */ inline vm_globals *vmglob_alloc() { return 0; } /* termination - this has no effect in this mode */ inline void vmglob_delete(vm_globals *) { } /* * we don't require anything for the parameter declaration or usage, since * we don't use the local parameter mechanism at all */ #define VMG_ #define VMG0_ #define vmg_ #define vmg0_ #define VMGNULL_ #define VMG0NULL_ #define Pvmg0_P /* "vmg0_" in parens, for constructor arguments */ /* * get the address of the globals - this doesn't do anything in this * configuration, as there's not really a global variables structure */ #define VMGLOB_ADDR 0 /* pass a stashed copy of the global pointer, if necessary */ #define vmg_var(x) #define vmg_var0(x) /* declare a local variable to access the globals */ #define VMGLOB_PTR(x) /* global variables are statically declared so they're always available */ #define VMGLOB_IF_AVAIL(x) x /* we access globals directly as individual statics */ #define VMGLOB_ACCESS(var) (G_##var##_X) #define VMGLOB_PREACCESS(var) (&G_##var##_X) #endif /* VMGLOB_VARS */ /* ------------------------------------------------------------------------ */ /* * STATIC GLOBAL STRUCTURE configuration. In this configuration, the * globals are defined in a single static global structure. This is the * second most efficient configuration, but it only allows one VM instance * per process. */ #ifdef VMGLOB_STRUCT /* define the global variables structure */ VMGLOB_DECLARE vm_globals G_vmglobals; /* initialization - this has no effect in this mode */ inline vm_globals *vmglob_alloc() { return &G_vmglobals; } /* termination - this has no effect in this mode */ inline void vmglob_delete(vm_globals *) { } /* * we don't require anything for the parameter declaration or usage, * since we don't use the local parameter mechanism at all */ #define VMG_ #define VMG0_ #define vmg_ #define vmg0_ #define VMGNULL_ #define VMG0NULL_ #define Pvmg0_P /* get the address of the globals */ #define VMGLOB_ADDR (&G_vmglobals) /* pass a stashed copy of the global pointer, if necessary */ #define vmg_var(x) #define vmg_var0(x) /* declare a local variable to access the globals */ #define VMGLOB_PTR(x) /* global variables are statically declared so they're always available */ #define VMGLOB_IF_AVAIL(x) x /* we access globals directly as individual statics */ #define VMGLOB_ACCESS(var) (G_vmglobals.var) #define VMGLOB_PREACCESS(var) (G_vmglobals.var) #endif /* VMGLOB_STRUCT */ /* ------------------------------------------------------------------------ */ /* * STATIC GLOBAL POINTER configuration. In this configuration, the * globals are stored in an allocated structure whose address is stored * in a global static pointer variable. */ #ifdef VMGLOB_POINTER /* define our global static pointer to the global variables */ VMGLOB_DECLARE vm_globals *G_vmglobals; /* initialization - allocate a new set of globals */ inline vm_globals *vmglob_alloc() { G_vmglobals = new vm_globals(); return G_vmglobals; } /* termination - delete a set of globals */ inline void vmglob_delete(vm_globals *glob) { delete glob; } /* * we don't require anything for the parameter declaration or usage, * since we don't use the local parameter mechanism at all */ #define VMG_ #define VMG0_ #define vmg_ #define vmg0_ #define VMGNULL_ #define VMG0NULL_ #define Pvmg0_P /* get the address of the globals */ #define VMGLOB_ADDR G_vmglobals /* pass a stashed copy of the global pointer, if necessary */ #define vmg_var(x) #define vmg_var0(x) /* declare a local variable to access the globals */ #define VMGLOB_PTR(x) /* test to see if a global variable is available */ #define VMGLOB_IF_AVAIL(x) (G_vmglobals != 0 ? x : 0) /* accessing a global requires dereferencing the global pointer */ #define VMGLOB_ACCESS(var) (G_vmglobals->var) #define VMGLOB_PREACCESS(var) (G_vmglobals_var) #endif /* VMGLOB_POINTER */ /* ------------------------------------------------------------------------ */ /* * PARAMETER configuration. In this configuration, the globals are * stored in an allocated structure whose address is passed to each VM * function as a parameter. */ #ifdef VMGLOB_PARAM /* initialization - allocate a new set of globals */ inline vm_globals *vmglob_alloc() { return new vm_globals(); } /* termination - delete a set of globals */ inline void vmglob_delete(vm_globals *glob) { delete glob; } /* function declaration for the global pointer parameter */ #define VMG_ vm_globals *vmg__, #define VMG0_ vm_globals *vmg__ /* parameter reference for passing to a function */ #define vmg_ vmg__, #define vmg0_ vmg__ #define VMGNULL_ ((vm_globals *)NULL), #define VMG0NULL_ ((vm_globals *)NULL) #define Pvmg0_P (vmg__) /* get the address of the globals */ #define VMGLOB_ADDR vmg__ /* pass a stashed copy of the global pointer, if necessary */ #define vmg_var(x) x, #define vmg_var0(x) x /* declare a local variable to access the globals */ #define VMGLOB_PTR(x) vm_globals *vmg__ = x /* test to see if a global variable is available */ #define VMGLOB_IF_AVAIL(x) (vmg__ != 0 ? x : 0) /* accessing a global variable requires dereferencing the parameter */ #define VMGLOB_ACCESS(var) (vmg__->var) #define VMGLOB_PREACCESS(var) (vmg__->var) #endif /* VMGLOB_PARAM */ /* ------------------------------------------------------------------------ */ /* * Global variable accessors. For convenience, we define these cover * macros that access the global variables in the appropriate manner for * our configuration. Code can use these G_xxx symbols syntactically as * though they were normal global variables. */ #define G_mem VMGLOB_ACCESS(mem) #define G_undo VMGLOB_ACCESS(undo) #define G_meta_table VMGLOB_ACCESS(meta_table) #define G_bif_table VMGLOB_ACCESS(bif_table) #define G_varheap VMGLOB_ACCESS(varheap) #define G_preinit_mode VMGLOB_ACCESS(preinit_mode) #define G_bif_tads_globals VMGLOB_ACCESS(bif_tads_globals) #define G_host_ifc VMGLOB_ACCESS(host_ifc) #define G_image_loader VMGLOB_ACCESS(image_loader) #define G_disp_cset_name VMGLOB_ACCESS(disp_cset_name) #define G_cmap_from_fname VMGLOB_ACCESS(cmap_from_fname) #define G_cmap_to_fname VMGLOB_ACCESS(cmap_to_fname) #define G_cmap_from_ui VMGLOB_ACCESS(cmap_from_ui) #define G_cmap_to_ui VMGLOB_ACCESS(cmap_to_ui) #define G_cmap_from_file VMGLOB_ACCESS(cmap_from_file) #define G_cmap_to_file VMGLOB_ACCESS(cmap_to_file) #define G_cmap_to_log VMGLOB_ACCESS(cmap_to_log) #define G_console VMGLOB_ACCESS(console) #define G_exc_entry_size VMGLOB_ACCESS(exc_entry_size) #define G_line_entry_size VMGLOB_ACCESS(line_entry_size) #define G_dbg_hdr_size VMGLOB_ACCESS(dbg_hdr_size) #define G_srcf_table VMGLOB_ACCESS(srcf_table) #define G_dbg_lclsym_hdr_size VMGLOB_ACCESS(dbg_lclsym_hdr_size) #define G_dbg_fmt_vsn VMGLOB_ACCESS(dbg_fmt_vsn) #define G_dbg_frame_size VMGLOB_ACCESS(dbg_frame_size) #define G_dyncomp VMGLOB_ACCESS(dyncomp) #define G_net_queue VMGLOB_ACCESS(net_queue) #define G_net_config VMGLOB_ACCESS(net_config) #define G_iter_get_next VMGLOB_ACCESS(iter_get_next) #define G_iter_next_avail VMGLOB_ACCESS(iter_next_avail) #define G_tadsobj_queue VMGLOB_PREACCESS(tadsobj_queue) #define G_predef VMGLOB_PREACCESS(predef) #define G_stk G_interpreter #define G_interpreter VMGLOB_PREACCESS(interpreter) #define G_const_pool VMGLOB_PREACCESS(const_pool) #define G_code_pool VMGLOB_PREACCESS(code_pool) #define G_obj_table VMGLOB_PREACCESS(obj_table) #define G_syslogfile VMGLOB_ACCESS(syslogfile) #define G_file_path VMGLOB_ACCESS(file_path) #define G_sandbox_path VMGLOB_ACCESS(sandbox_path) #define G_tzcache VMGLOB_ACCESS(tzcache) #define G_debugger VMGLOB_ACCESS(debugger) #endif /* VMGLOB_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmglobv.h�������������������������������������������������������������������������0000664�0000000�0000000�00000013716�12650170723�0015513�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmglobv.h - global variable definitions Function Defines the global variables for the T3 VM Notes This file is NOT protected against multiple inclusion, because it's designed to be included more than once with different definitions for the macros. Modified 09/18/02 MJRoberts - Creation */ VM_GLOBALS_BEGIN /* object memory manager */ VM_GLOBAL_OBJDEF(class CVmMemory, mem) /* variable-size block heap manager */ VM_GLOBAL_OBJDEF(class CVmVarHeap, varheap) /* undo manager */ VM_GLOBAL_OBJDEF(class CVmUndo, undo) /* constant pool manager */ VM_GLOBAL_PREOBJDEF(class CVmPool_CLASS, const_pool) /* code pool manager */ VM_GLOBAL_PREOBJDEF(class CVmPool_CLASS, code_pool) /* metaclass dependency table for loaded image file */ VM_GLOBAL_OBJDEF(class CVmMetaTable, meta_table) /* built-in function set table */ VM_GLOBAL_OBJDEF(class CVmBifTable, bif_table) /* source file list (for debugger) */ VM_GLOBAL_OBJDEF(class CVmSrcfTable, srcf_table) /* global symbol table (for debugger) */ VM_GLOBAL_OBJDEF(class CTcPrsSymtab, sym_table) /* byte code interpreter */ VM_GLOBAL_PRECOBJDEF(class CVmRun, interpreter, (VM_STACK_SIZE, vm_init_stack_reserve())) /* size of each exception table entry in the image file */ VM_GLOBAL_VARDEF(size_t, exc_entry_size) /* size of each debugger source line entry in the image file */ VM_GLOBAL_VARDEF(size_t, line_entry_size) /* pre-defined objects and properties */ VM_GLOBAL_PREOBJDEF(struct CVmPredef, predef) /* preinit mode flag */ VM_GLOBAL_VARDEF(int, preinit_mode) /* flag: error subsystem initialized outside of VM globals */ VM_GLOBAL_VARDEF(int, err_pre_inited) /* TADS built-in function globals */ VM_GLOBAL_OBJDEF(class CVmBifTADSGlobals, bif_tads_globals) /* host application interface */ VM_GLOBAL_OBJDEF(class CVmHostIfc, host_ifc) /* image file loader */ VM_GLOBAL_OBJDEF(class CVmImageLoader, image_loader) /* name of the UI character set, if specified explicitly */ VM_GLOBAL_OBJDEF(char, disp_cset_name) /* * Character mappings to and from the local filename character set. * This is the character set that's used by the local file system to * represent filenames. Note that this isn't the set for the * *contents* of files - just for their names. */ VM_GLOBAL_OBJDEF(class CCharmapToUni, cmap_from_fname) VM_GLOBAL_OBJDEF(class CCharmapToLocal, cmap_to_fname) /* * Character mappings to and from the default local file-contents * character set. * * At best, this is only a default. In actual practice files can be in * any character set (or even in no character sets at all, or in * multiple character sets, in the case of binary or structured files). * Even so, on most systems, there will be a character set that most * ordinary text files use; this is that set. */ VM_GLOBAL_OBJDEF(class CCharmapToUni, cmap_from_file) VM_GLOBAL_OBJDEF(class CCharmapToLocal, cmap_to_file) /* * Character mapping to the log file. This is the mapping from unicode * to the log file. */ VM_GLOBAL_OBJDEF(class CCharmapToLocal, cmap_to_log) /* * Character mappings to and from the local character set for the * console/display user interface. This character set is used for * formatting output to the display and reading input from the * keyboard. */ VM_GLOBAL_OBJDEF(class CCharmapToUni, cmap_from_ui) VM_GLOBAL_OBJDEF(class CCharmapToLocal, cmap_to_ui) /* user interface primary console */ VM_GLOBAL_OBJDEF(class CVmConsoleMain, console) /* TadsObject inheritance path analysis queue */ VM_GLOBAL_PREOBJDEF(class CVmObjTadsInhQueue, tadsobj_queue) /* dynamic compiler */ VM_GLOBAL_OBJDEF(class CVmDynamicCompiler, dyncomp) /* web host configuration */ VM_GLOBAL_OBJDEF(class TadsNetConfig, net_config) /* network I/O message queue */ VM_GLOBAL_OBJDEF(class TadsMessageQueue, net_queue) /* * The isNextAvailable() and getNext() properties from the Collection * entry in the metaclass table. We initialize these after loading the * program so that CVmObject::iter_next() has quick access to these * properties for executing iterator loops. */ VM_GLOBAL_VARDEF(uint16_t, iter_get_next) VM_GLOBAL_VARDEF(uint16_t, iter_next_avail) /* system debug log file name */ VM_GLOBAL_ARRAYDEF(char, syslogfile, OSFNMAX) /* * Base path for file I/O operations on files with relative filenames. * This is used instead of the operating system's native notion of the * working directory to provide better consistency across platforms, * since some systems change the working directory after certain file * operations. */ VM_GLOBAL_VARDEF(char *, file_path) /* * Sandbox path for the file safety feature. If the file safety * settings only allow access within the sandbox directory and its * children, this is the root path for the sandbox. */ VM_GLOBAL_VARDEF(char *, sandbox_path) /* time zone cache */ VM_GLOBAL_OBJDEF(class CVmTimeZoneCache, tzcache) /* size of header of each method's debug table */ VM_GLOBAL_VARDEF(size_t, dbg_hdr_size) /* size of each debugger local symbol header */ VM_GLOBAL_VARDEF(size_t, dbg_lclsym_hdr_size) /* debug record format version */ VM_GLOBAL_VARDEF(int, dbg_fmt_vsn) /* debug frame record size */ VM_GLOBAL_VARDEF(int, dbg_frame_size) /* debugger API */ VM_GLOBAL_OBJDEF(class CVmDebug, debugger) /* object table */ VM_GLOBAL_PREOBJDEF(class CVmObjTable, obj_table) VM_GLOBALS_END ��������������������������������������������������qtads-2.1.7/tads3/vmgram.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000346263�12650170723�0015671�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmgram.cpp - T3 Grammar Production metaclass Function Notes Modified 02/15/00 MJRoberts - Creation */ #include <stdlib.h> #include <string.h> #include <assert.h> #include "t3std.h" #include "tct3drv.h" #include "tcprs.h" #include "vmtype.h" #include "vmstack.h" #include "vmrun.h" #include "vmlst.h" #include "vmstr.h" #include "vmgram.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmmeta.h" #include "vmdict.h" #include "vmtobj.h" #include "vmpredef.h" #include "vmfile.h" #include "vmbif.h" #include "vmdynfunc.h" #include "vmpredef.h" /* ------------------------------------------------------------------------ */ /* * Grammar production object - undo record. This records a deleted or * replaced alternative at a given index. */ #define VMGRAM_UNDO_DELETED 1 /* alternative was deleted */ #define VMGRAM_UNDO_REPLACED 2 /* alternative was replaced */ #define VMGRAM_UNDO_ADDED 3 /* alternative was added */ struct vmgram_undo_rec { vmgram_undo_rec(int op, int idx, vmgram_alt_info *alt) { this->idx = idx; this->op = op; this->alt = alt; } ~vmgram_undo_rec() { /* delete our alternative */ if (alt != 0) delete alt; } /* original index of 'alt' in the production's alternative list */ int idx; /* operation that was performed */ int op; /* the original alternative rule data */ vmgram_alt_info *alt; }; /* ------------------------------------------------------------------------ */ /* * Grammar production object - memory pool. This is a simple * suballocator that obtains large blocks from the system allocator, * then hands out pieces of those blocks. Allocating is very cheap * (just a pointer increment in most cases), and we don't track * individual allocations and deletions but just throw away all * suballocated memory at once. */ /* size of each memory block */ const size_t VMGRAMPROD_MEM_BLOCK_SIZE = 16*1024; /* * memory pool block */ struct CVmGramProdMemBlock { /* next block in chain */ CVmGramProdMemBlock *nxt_; /* bytes of the block */ char buf_[VMGRAMPROD_MEM_BLOCK_SIZE]; }; /* * memory pool object */ class CVmGramProdMem { public: CVmGramProdMem() { /* no memory blocks yet */ block_head_ = 0; block_cur_ = 0; /* we don't yet have a block */ cur_rem_ = 0; cur_free_ = 0; } ~CVmGramProdMem() { /* delete each of our blocks */ while (block_head_ != 0) { CVmGramProdMemBlock *nxt; /* remember the next block */ nxt = block_head_->nxt_; /* delete this block */ t3free(block_head_); /* move on to the next */ block_head_ = nxt; } } /* reset - delete all previously sub-allocated memory */ void reset() { /* start over with the first block */ block_cur_ = block_head_; /* initialize the free pointer for the new block, if we have one */ if (block_cur_ != 0) { /* start at the beginning of the new block */ cur_free_ = block_cur_->buf_; /* this entire block is available */ cur_rem_ = VMGRAMPROD_MEM_BLOCK_SIZE; } else { /* there are no blocks, so there's no memory available yet */ cur_rem_ = 0; cur_free_ = 0; } } /* allocate memory */ void *alloc(size_t siz) { void *ret; /* round the size to the local hardware boundary */ siz = osrndsz(siz); /* if it exceeds the block size, fail */ if (siz > VMGRAMPROD_MEM_BLOCK_SIZE) return 0; /* if we don't have enough space in this block, go to the next */ if (siz > cur_rem_) { /* allocate a new block if necessary */ if (block_cur_ == 0) { /* there's nothing in the list yet - set up the first block */ block_head_ = (CVmGramProdMemBlock *) t3malloc(sizeof(CVmGramProdMemBlock)); /* activate the new block */ block_cur_ = block_head_; /* there's nothing after this block */ block_cur_->nxt_ = 0; } else if (block_cur_->nxt_ == 0) { /* we're at the end of the list - add a block */ block_cur_->nxt_ = (CVmGramProdMemBlock *) t3malloc(sizeof(CVmGramProdMemBlock)); /* advance to the new block */ block_cur_ = block_cur_->nxt_; /* the new block is the last in the chain */ block_cur_->nxt_ = 0; } else { /* another block follows - advance to it */ block_cur_ = block_cur_->nxt_; } /* start at the beginning of the new block */ cur_free_ = block_cur_->buf_; /* this entire block is available */ cur_rem_ = VMGRAMPROD_MEM_BLOCK_SIZE; } /* the return value will be the current free pointer */ ret = cur_free_; /* consume the space */ cur_free_ += siz; cur_rem_ -= siz; /* return the block location */ return ret; } private: /* head of block list */ CVmGramProdMemBlock *block_head_; /* block we're currently suballocating out of */ CVmGramProdMemBlock *block_cur_; /* current free pointer in current block */ char *cur_free_; /* amount of space remaining in current block */ size_t cur_rem_; }; /* ------------------------------------------------------------------------ */ /* * Alternative object */ vmgram_alt_info::vmgram_alt_info(size_t ntoks) { toks = new vmgram_tok_info[ntoks]; tok_cnt = ntoks; score = 0; badness = 0; del = FALSE; } vmgram_alt_info::~vmgram_alt_info() { delete [] toks; } /* ------------------------------------------------------------------------ */ /* * Input token array entry. We make a private copy of the input token list * (i.e., the token list we're trying to match to our grammar rules) during * parsing using one of these structures for each token. */ struct vmgramprod_tok { /* original token value */ vm_val_t val_; /* token type */ ulong typ_; /* token text */ const char *txt_; /* length of the token text */ size_t len_; /* hash value of the token */ unsigned int hash_; /* number of vocabulary matches associated with the word */ size_t match_cnt_; /* pointer to list of matches for the word */ vmgram_match_info *matches_; }; /* ------------------------------------------------------------------------ */ /* * Parsing state match object. A match can be terminal, in which case * it refers to a single token of input, or it can be a production, in * which case it refers to a subtree of match objects. */ struct CVmGramProdMatch { /* allocate */ static CVmGramProdMatch *alloc(CVmGramProdMem *mem, size_t tok_pos, const vm_val_t *tok_match_result, int matched_star, vm_prop_id_t target_prop, vm_obj_id_t proc_obj, const CVmGramProdMatch **sub_match_list, size_t sub_match_cnt) { CVmGramProdMatch *match; /* allocate space */ match = (CVmGramProdMatch *)mem->alloc(sizeof(CVmGramProdMatch)); /* initialize */ match->tok_pos_ = tok_pos; match->matched_star_ = matched_star; match->target_prop_ = target_prop; match->proc_obj_ = proc_obj; match->sub_match_list_ = sub_match_list; match->sub_match_cnt_ = sub_match_cnt; /* save the token match value, if one was given */ if (tok_match_result != 0) match->tok_match_result_ = *tok_match_result; else match->tok_match_result_.set_nil(); /* return the new match */ return match; } /* property ID with which this match is associated */ vm_prop_id_t target_prop_; /* * token position of the matching token; ignored if the production * subtree information is valid */ size_t tok_pos_; /* * The token match value. This is the result code from the Dictionary * comparator's matchValues() function, which typically encodes * information about how the value matched (truncation, case folding, * accent approximation, etc) in bitwise flags. */ vm_val_t tok_match_result_; /* * flag: this is a '*' token in the rule; these don't really match any * tokens at all, since they merely serve to stop parsing regardless * of whether or not more input tokens are present */ int matched_star_; /* * object ID of match processor - if this is valid, this match * refers to a production; otherwise, the match refers to a single * token */ vm_obj_id_t proc_obj_; /* pointer to array of sub-matches */ const CVmGramProdMatch **sub_match_list_; /* number of sub-match entries */ size_t sub_match_cnt_; }; /* * Top-level match list holder */ struct CVmGramProdMatchEntry { /* the match tree */ CVmGramProdMatch *match_; /* token position at the end of the match */ size_t tok_pos_; /* next entry in the list */ CVmGramProdMatchEntry *nxt_; }; /* * Parsing state object. At any given time, we will have one or more of * these state objects in our processing queue. A state object tracks a * parsing position - the token position, the alternative position, the * match list so far for the alternative (i.e., the match for each item * in the alternative up to but not including the current position), and * the enclosing parsing state object (i.e., the parsing state that * "recursed" into this alternative). */ struct CVmGramProdState { /* allocate */ static CVmGramProdState *alloc(CVmGramProdMem *mem, int tok_pos, int alt_pos, CVmGramProdState *enclosing, const vmgram_alt_info *altp, vm_obj_id_t prod_obj, int circular_alt) { size_t match_cnt; CVmGramProdState *state; /* get the number of match list slots we'll need */ match_cnt = altp->tok_cnt; /* allocate space, including space for the match list */ state = (CVmGramProdState *) mem->alloc(sizeof(CVmGramProdState) + (match_cnt - 1)*sizeof(CVmGramProdMatch *)); /* initialize */ state->init(tok_pos, alt_pos, enclosing, altp, prod_obj, circular_alt); /* return the new item */ return state; } /* initialize */ void init(int tok_pos, int alt_pos, CVmGramProdState *enclosing, const vmgram_alt_info *altp, vm_obj_id_t prod_obj, int circular_alt) { /* remember the position parameters */ tok_pos_ = tok_pos; alt_pos_ = alt_pos; /* we haven't matched a '*' token yet */ matched_star_ = FALSE; /* remember our stack position */ enclosing_ = enclosing; /* remember our alternative image data pointer */ altp_ = altp; /* remember the production object */ prod_obj_ = prod_obj; /* note if we had circular alternatives in the same production */ circular_alt_ = circular_alt; /* no target property for sub-production yet */ sub_target_prop_ = VM_INVALID_PROP; /* we're not in the queue yet */ nxt_ = 0; } /* clone the state */ CVmGramProdState *clone(CVmGramProdMem *mem) { CVmGramProdState *new_state; CVmGramProdState *new_enclosing; /* clone our enclosing state */ new_enclosing = (enclosing_ != 0 ? enclosing_->clone(mem) : 0); /* create a new state object */ new_state = alloc(mem, tok_pos_, alt_pos_, new_enclosing, altp_, prod_obj_, circular_alt_); /* copy the target sub-production property */ new_state->sub_target_prop_ = sub_target_prop_; /* copy the valid portion of my match list */ if (alt_pos_ != 0) memcpy(new_state->match_list_, match_list_, alt_pos_ * sizeof(match_list_[0])); /* we're not yet in any queue */ new_state->nxt_ = 0; /* set the '*' flag in the cloned state */ new_state->matched_star_ = matched_star_; /* return the clone */ return new_state; } /* current token position, as an index into the token list */ size_t tok_pos_; /* * flag: we've matched a '*' token in a production or subproduction, * so we should consider all remaining tokens to have been consumed * (we don't advance tok_pos_ past all of the tokens, because we * separately want to keep track of the number of tokens we matched * for everything up to the '*') */ int matched_star_; /* * current alternative position, as an index into the alternative's * list of items */ size_t alt_pos_; /* * the enclosing state object - this is the state object that * "recursed" to our position */ CVmGramProdState *enclosing_; /* pointer to alternative definition */ const vmgram_alt_info *altp_; /* object ID of the production object */ vm_obj_id_t prod_obj_; /* the target property for the pending sub-production */ vm_prop_id_t sub_target_prop_; /* next production state in the processing queue */ CVmGramProdState *nxt_; /* the production contains circular alternatives */ int circular_alt_; /* * Match list. This list is allocated with enough space for one * match for each of our items (the number of items is given by * vmgram_alt_tokcnt(altp_)). At any given time, these will be * valid up to but not including the current alt_pos_ index. */ const struct CVmGramProdMatch *match_list_[1]; }; /* * Queues. We maintain three queues: the primary work queue, which * contains pending parsing states that we're still attempting to match; * the success queue, which contains a list of match trees for * successfully completed matches; and the badness queue, which contains * a list of parsing states that we can try as fallbacks if we fail to * find a match in any of the work queue items. */ struct CVmGramProdQueue { /* clear the queues */ void clear() { work_queue_ = 0; badness_queue_ = 0; success_list_ = 0; } /* head of work queue */ CVmGramProdState *work_queue_; /* head of badness queue */ CVmGramProdState *badness_queue_; /* head of success list */ CVmGramProdMatchEntry *success_list_; }; /* ------------------------------------------------------------------------ */ /* * Grammar-Production object statics */ /* metaclass registration object */ static CVmMetaclassGramProd metaclass_reg_obj; CVmMetaclass *CVmObjGramProd::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjGramProd:: *CVmObjGramProd::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjGramProd::getp_undef, /* 0 */ &CVmObjGramProd::getp_parse, /* 1 */ &CVmObjGramProd::getp_get_gram_info, /* 2 */ &CVmObjGramProd::getp_addAlt, /* 3 */ &CVmObjGramProd::getp_deleteAlt, /* 4 */ &CVmObjGramProd::getp_clearAlts /* 5 */ }; /* property indices */ const int PROPIDX_deleteAlt = 4; /* ------------------------------------------------------------------------ */ /* * Grammar-Production metaclass implementation */ /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjGramProd::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { /* create an empty new grammar production */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, TRUE, TRUE); new (vmg_ id) CVmObjGramProd(vmg0_); CVmObjGramProd *prod = (CVmObjGramProd *)vm_objp(vmg_ id); /* * mark the object as modified-since-load, since it was never part of * the image file */ prod->get_ext()->modified_ = TRUE; /* check arguments */ if (argc == 0) { /* no arguments - create a new production with no rules defined */ } else { /* invalid argument list */ err_throw(VMERR_WRONG_NUM_OF_ARGS); } /* discard arguments */ G_stk->discard(argc); /* return the new ID */ return id; } /* * constructor */ CVmObjGramProd::CVmObjGramProd(VMG0_) { /* allocate our extension structure from the variable heap */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(sizeof(vm_gram_ext), this); /* we have no image data yet */ get_ext()->image_data_ = 0; get_ext()->image_data_size_ = 0; /* we haven't been modified yet */ get_ext()->modified_ = FALSE; /* we haven't set up our alternatives yet */ get_ext()->alts_ = 0; get_ext()->alt_cnt_ = 0; get_ext()->alt_alo_ = 0; /* we haven't cached any hash values yet */ get_ext()->hashes_cached_ = FALSE; get_ext()->comparator_ = VM_INVALID_OBJ; /* presume we have no circular rules */ get_ext()->has_circular_alt = FALSE; /* create our memory pool */ get_ext()->mem_ = new CVmGramProdMem(); /* * allocate initial property enumeration space (we'll expand this as * needed, so the initial size is just a guess) */ get_ext()->prop_enum_max_ = 64; get_ext()->prop_enum_arr_ = (vmgram_match_info *) t3malloc(get_ext()->prop_enum_max_ * sizeof(vmgram_match_info)); } /* * notify of deletion */ void CVmObjGramProd::notify_delete(VMG_ int /*in_root_set*/) { /* free our additional data */ if (ext_ != 0) { /* if we've allocated alternatives, delete them */ vmgram_alt_info **alts = get_ext()->alts_; if (alts != 0) { /* delete the alternative objects */ clear_alts(); /* free the alternative array itself */ t3free(alts); } /* delete our memory pool */ delete get_ext()->mem_; /* delete our property enumeration space */ t3free(get_ext()->prop_enum_arr_); /* free the extension */ G_mem->get_var_heap()->free_mem(ext_); } } /* ------------------------------------------------------------------------ */ /* * clear the alternative list */ void CVmObjGramProd::clear_alts() { /* delete each alternative */ for (size_t i = 0 ; i < get_ext()->alt_cnt_ ; ++i) delete get_ext()->alts_[i]; /* there are no more alternatives in the list */ get_ext()->alt_cnt_ = 0; } /* * ensure there's room for the given number of alternatives */ void CVmObjGramProd::ensure_alts(size_t cnt, size_t margin) { /* if there's not room, allocate a bigger array */ if (cnt > get_ext()->alt_alo_) { /* allocate or reallocate at the required size plus the margin */ cnt += margin; size_t siz = cnt * sizeof(get_ext()->alts_[0]); if (get_ext()->alts_ != 0) { get_ext()->alts_ = (vmgram_alt_info **)t3realloc( get_ext()->alts_, siz); } else { get_ext()->alts_ = (vmgram_alt_info **)t3malloc(siz); } /* set the new allocated count */ get_ext()->alt_alo_ = cnt; } } /* * insert an alternative at the given index (no undo) */ void CVmObjGramProd::insert_alt( vm_obj_id_t self, size_t idx, vmgram_alt_info *alt) { /* make sure we have room for one more alternative */ ensure_alts(get_ext()->alt_cnt_ + 1, 8); /* get the alt list */ vmgram_alt_info **alts = get_ext()->alts_; /* open a hole in the array for the new alternative */ for (size_t i = get_ext()->alt_cnt_ ; i > idx ; --i) alts[i] = alts[i-1]; /* adjust the count */ get_ext()->alt_cnt_ += 1; /* plug in the new alternative */ alts[idx] = alt; /* we've been modified since load time */ get_ext()->modified_ = TRUE; /* note if this adds a circular rule */ if (alt->tok_cnt != 0 && alt->toks->typ == VMGRAM_MATCH_PROD && alt->toks->typinfo.prod_obj == self) get_ext()->has_circular_alt = TRUE; } /* * delete the alternative at the given index (no undo) */ void CVmObjGramProd::delete_alt(size_t idx) { /* remove it from the array and close the gap */ for ( ; idx + 1 < get_ext()->alt_cnt_ ; ++idx) get_ext()->alts_[idx] = get_ext()->alts_[idx+1]; /* adjust the count */ get_ext()->alt_cnt_ -= 1; /* we've been modified since load time */ get_ext()->modified_ = TRUE; } /* ------------------------------------------------------------------------ */ /* * get a property */ int CVmObjGramProd::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property index to an index into our function table */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * set a property */ void CVmObjGramProd::set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) { /* no settable properties - throw an error */ err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * load from an image file */ void CVmObjGramProd::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* remember where our image data comes from */ get_ext()->image_data_ = ptr; get_ext()->image_data_size_ = siz; /* * Rebuild the list from the image file data. Note that an image file * doesn't have a fixup table, since it defines the object numbering * space in the first place. */ CVmReadOnlyMemoryStream src(ptr, siz); load_alts(vmg_ self, &src, 0); /* we haven't been modified since being loaded from the image */ get_ext()->modified_ = FALSE; } /* * reset to the image file */ void CVmObjGramProd::reset_to_image(VMG_ vm_obj_id_t self) { /* if we we were modified and have image data, reload the image data */ if (get_ext()->modified_ && get_ext()->image_data_ != 0) { /* load from our original data stream */ CVmReadOnlyMemoryStream src( get_ext()->image_data_, get_ext()->image_data_size_); load_alts(vmg_ self, &src, 0); /* we haven't been modified since being loaded from the image */ get_ext()->modified_ = FALSE; /* reset the comparator caching information */ get_ext()->comparator_ = VM_INVALID_OBJ; get_ext()->hashes_cached_ = FALSE; } } /* * save to a file */ void CVmObjGramProd::save_to_file(VMG_ CVmFile *fp) { /* if we've been dynamically modified, save our contents */ if (get_ext()->modified_) { /* save to the file */ CVmFileStream dst(fp); save_to_stream(vmg_ &dst); } } /* * Save to a data stream */ void CVmObjGramProd::save_to_stream(VMG_ CVmStream *fp) { /* write the number of alternatives */ fp->write_uint2(get_ext()->alt_cnt_); /* write the alternatives */ vmgram_alt_info **altp = get_ext()->alts_; for (size_t i = 0 ; i < get_ext()->alt_cnt_ ; ++i, ++altp) { /* get the alternative pointer */ vmgram_alt_info *alt = *altp; /* write the alternative header */ fp->write_int2(alt->score); fp->write_int2(alt->badness); fp->write_uint4((ulong)alt->proc_obj); fp->write_uint2(alt->tok_cnt); /* write the token list */ vmgram_tok_info *tok = alt->toks; for (size_t j = 0 ; j < alt->tok_cnt ; ++j, ++tok) { /* write the fixed part of the token */ fp->write_uint2((uint)tok->prop); fp->write_byte((uchar)tok->typ); /* write the extra data according to the token type */ switch (tok->typ) { case VMGRAM_MATCH_PROD: /* write the object ID */ fp->write_uint4((ulong)tok->typinfo.prod_obj); break; case VMGRAM_MATCH_SPEECH: /* write the part-of-speech property */ fp->write_uint2((uint)tok->typinfo.speech_prop); break; case VMGRAM_MATCH_NSPEECH: /* array of part-of-speech properties - write the count */ fp->write_uint2(tok->typinfo.nspeech.cnt); /* write the array elements */ for (size_t k = 0 ; k < tok->typinfo.nspeech.cnt ; ++k) fp->write_uint2((uint)tok->typinfo.nspeech.props[k]); /* done */ break; case VMGRAM_MATCH_LITERAL: /* literal string - write the length, then the bytes */ fp->write_uint2(tok->typinfo.lit.len); fp->write_bytes(tok->typinfo.lit.str, tok->typinfo.lit.len); break; case VMGRAM_MATCH_TOKTYPE: /* token type */ fp->write_uint4((ulong)tok->typinfo.toktyp_enum); break; case VMGRAM_MATCH_STAR: /* star - no additional data */ break; } } } } /* * restore from a file */ void CVmObjGramProd::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { /* load our alternative list from the file stream */ CVmFileStream src(fp); load_alts(vmg_ self, &src, fixups); } /* * Load alternative data from a data source. This is our common handler * for loading from the image file and restoring from a saved game file. */ void CVmObjGramProd::load_alts(VMG_ vm_obj_id_t self, CVmStream *src, CVmObjFixup *fixups) { /* delete any existing alternative list */ clear_alts(); /* presume there will be no circular alternatives */ get_ext()->has_circular_alt = FALSE; /* read the number of alternatives */ size_t alt_cnt = src->read_uint2(); /* ensure there's room for the new list */ ensure_alts(alt_cnt, 0); /* load the alternatives */ for (size_t i = 0 ; i < alt_cnt ; ++i) { /* read the alternative header */ int score = src->read_int2(); int badness = src->read_int2(); vm_obj_id_t proc_obj = (vm_obj_id_t)src->read_uint4(); uint tokcnt = src->read_uint2(); /* fix up the processor object ID */ if (fixups != 0) proc_obj = fixups->get_new_id(vmg_ proc_obj); /* create this alternative */ vmgram_alt_info *alt = new vmgram_alt_info(tokcnt); alt->score = score; alt->badness = badness; alt->proc_obj = proc_obj; /* add it to the list in our extension */ get_ext()->alt_cnt_ += 1; get_ext()->alts_[i] = alt; /* get the first token */ vmgram_tok_info *tok = alt->toks; /* read the tokens */ for (size_t j = 0 ; j < tokcnt ; ++j, ++tok) { /* read the fixed part of the token */ tok->prop = (vm_prop_id_t)src->read_uint2(); tok->typ = (vmgram_match_type)src->read_byte(); /* read the extra data according to the token type */ switch (tok->typ) { case VMGRAM_MATCH_PROD: /* read the object ID */ tok->typinfo.prod_obj = (vm_obj_id_t)src->read_uint4(); /* fix it up if necessary */ if (fixups != 0) tok->typinfo.prod_obj = fixups->get_new_id( vmg_ tok->typinfo.prod_obj); break; case VMGRAM_MATCH_SPEECH: /* read the part-of-speech property */ tok->typinfo.speech_prop = (vm_prop_id_t)src->read_uint2(); break; case VMGRAM_MATCH_NSPEECH: /* array of part-of-speech properties */ { /* read the count */ size_t propcnt = src->read_uint2(); /* allocate the array */ tok->set_nspeech(propcnt); /* read the properties */ for (size_t k = 0 ; k < propcnt ; ++k) { tok->typinfo.nspeech.props[k] = (vm_prop_id_t)src->read_uint2(); } } break; case VMGRAM_MATCH_LITERAL: /* literal string */ { /* read the length */ size_t len = src->read_uint2(); /* allocate the string */ tok->set_literal(len); /* read the string */ src->read_bytes(tok->typinfo.lit.str, len); } break; case VMGRAM_MATCH_TOKTYPE: /* token type */ tok->typinfo.toktyp_enum = src->read_uint4(); break; case VMGRAM_MATCH_STAR: /* star - no additional data */ break; } } /* * If the first token of the alternative is a recursive reference * to this production itself, we have a circular reference. Note * this, since we have to handle it specially when parsing. */ tok = alt->toks; if (tokcnt != 0 && tok->typ == VMGRAM_MATCH_PROD && tok->typinfo.prod_obj == self) { /* note the existence of a circular reference */ get_ext()->has_circular_alt = TRUE; } } } /* ------------------------------------------------------------------------ */ /* * apply undo */ void CVmObjGramProd::apply_undo(VMG_ CVmUndoRecord *undo_rec) { /* we only store pointer records, so ignore other types */ if (undo_rec->id.ptrval != 0) { /* get our private undo record, properly cast */ vmgram_undo_rec *rec = (vmgram_undo_rec *)undo_rec->id.ptrval; /* get the 'self' object from the undo record */ vm_obj_id_t self = undo_rec->obj; /* get the original alternative object and its original index */ vmgram_alt_info *alt = rec->alt; int idx = rec->idx; /* get our alternative list and count */ vmgram_alt_info **alts = get_ext()->alts_; /* apply undo based on the event type */ switch (rec->op) { case VMGRAM_UNDO_DELETED: /* deleted a record - re-insert it at the original index */ insert_alt(self, idx, alt); /* the grammar prod object owns it now - clear it in the undo */ rec->alt = 0; break; case VMGRAM_UNDO_REPLACED: /* replaced a record - restore it at the original index */ alts[idx] = alt; break; case VMGRAM_UNDO_ADDED: /* added a new record - delete the record at this index */ delete_alt(idx); /* check for circular references */ check_circular_refs(self); break; } } } /* * discard extra undo record information */ void CVmObjGramProd::discard_undo(VMG_ CVmUndoRecord *undo_rec) { /* check for our private record type */ if (undo_rec->id.ptrval != 0) { /* get our private undo record, properly cast */ vmgram_undo_rec *rec = (vmgram_undo_rec *)undo_rec->id.ptrval; /* delete it */ delete rec; } } /* ------------------------------------------------------------------------ */ /* * mark object references in an undo record */ void CVmObjGramProd::mark_undo_ref(VMG_ CVmUndoRecord *undo_rec) { /* check for our private record type */ if (undo_rec->id.ptrval != 0) { /* get our private undo record, properly cast */ vmgram_undo_rec *rec = (vmgram_undo_rec *)undo_rec->id.ptrval; /* if it record an alternative, mark its references */ if (rec->alt != 0) mark_alt_refs(vmg_ rec->alt, VMOBJ_REACHABLE); } } /* * mark references */ void CVmObjGramProd::mark_refs(VMG_ uint state) { /* if we've been dynamically modified, we could have non-root refs */ if (get_ext()->modified_) { /* run through our alternative list */ vmgram_alt_info *const *altp = get_ext()->alts_; for (size_t i = 0 ; i < get_ext()->alt_cnt_ ; ++i, ++altp) mark_alt_refs(vmg_ *altp, state); } } /* * mark references in an alternative */ void CVmObjGramProd::mark_alt_refs(VMG_ const vmgram_alt_info *alt, uint state) { /* mark the match object */ if (alt->proc_obj != VM_INVALID_OBJ) G_obj_table->mark_all_refs(alt->proc_obj, state); /* run through the token list */ vmgram_tok_info *tok = alt->toks; for (size_t j = 0 ; j < alt->tok_cnt ; ++j, ++tok) { /* only production matches have object references */ if (tok->typ == VMGRAM_MATCH_PROD && tok->typinfo.prod_obj != VM_INVALID_OBJ) { /* mark the reference */ G_obj_table->mark_all_refs(tok->typinfo.prod_obj, state); } } } /* * Remove stale weak references. */ void CVmObjGramProd::remove_stale_weak_refs(VMG0_) { /* * Our reference to the dictionary comparator object is weak (we're * only caching it - we don't want to prevent the object from being * collected if no one else wants it). So, forget it if the comparator * is being deleted. */ if (get_ext()->comparator_ != VM_INVALID_OBJ && G_obj_table->is_obj_deletable(get_ext()->comparator_)) { /* forget the comparator */ get_ext()->comparator_ = VM_INVALID_OBJ; /* * our cached hash values depend on the comparator, so they're now * invalid - forget about them */ get_ext()->hashes_cached_ = FALSE; } } /* ------------------------------------------------------------------------ */ /* * Context for enum_props_cb */ struct enum_props_ctx { /* object extension */ vm_gram_ext *ext; /* number of properties in our list so far */ size_t cnt; }; /* * Callback for enumerating word properties */ void CVmObjGramProd::enum_props_cb(VMG_ void *ctx0, vm_prop_id_t prop, const vm_val_t * /*match_val*/) { enum_props_ctx *ctx = (enum_props_ctx *)ctx0; vmgram_match_info *ep; size_t i; /* if this property is already in our list, don't add it again */ for (i = 0, ep = ctx->ext->prop_enum_arr_ ; i < ctx->cnt ; ++i, ++ep) { /* if we already have this one, we can ignore it */ if (ep->prop == prop) return; } /* we need to add it - if the array is full, expand it */ if (ctx->cnt == ctx->ext->prop_enum_max_) { /* reallocate the array with more space */ ctx->ext->prop_enum_max_ += 64; ctx->ext->prop_enum_arr_ = (vmgram_match_info *) t3realloc(ctx->ext->prop_enum_arr_, ctx->ext->prop_enum_max_ * sizeof(vmgram_match_info)); } /* add the property to the array */ ep = &ctx->ext->prop_enum_arr_[ctx->cnt]; ep->prop = prop; /* count the new entry */ ctx->cnt++; } /* ------------------------------------------------------------------------ */ /* * Execute the parseTokens method */ int CVmObjGramProd::getp_parse(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_val_t *tokval; vm_val_t dictval; CVmObjDict *dict; int tok_cnt; int i; vmgramprod_tok *tok; int orig_argc = (argc != 0 ? *argc : 0); CVmGramProdQueue queues; CVmObjList *lst; int succ_cnt; CVmGramProdMatchEntry *match; static CVmNativeCodeDesc desc(2); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* reset our memory pool */ get_ext()->mem_->reset(); /* clear the work queues */ queues.clear(); /* * get the tokenList argument and make sure it's a list; leave it on * the stack for now so it remains reachable for the garbage * collector */ tokval = G_stk->get(0); if (!tokval->is_listlike(vmg0_) || (tok_cnt = tokval->ll_length(vmg0_)) < 0) err_throw(VMERR_LIST_VAL_REQD); /* check for a dictionary argument */ if (orig_argc >= 2) { /* get the dictionary argument */ dictval = *G_stk->get(1); /* make sure it's an object or nil */ if (dictval.typ != VM_OBJ && dictval.typ != VM_NIL) err_throw(VMERR_OBJ_VAL_REQD); } else { /* use a nil dictionary by default */ dictval.set_nil(); } /* get the dictionary object and check that it's really a dictionary */ if (dictval.typ != VM_NIL) { /* if it's not a CVmObjDict object, it's the wrong type */ if (!CVmObjDict::is_dictionary_obj(vmg_ dictval.val.obj)) { /* wrong type - throw an error */ err_throw(VMERR_INVAL_OBJ_TYPE); } /* get the VM object pointer */ dict = (CVmObjDict *)vm_objp(vmg_ dictval.val.obj); } else { /* there's no dictionary object */ dict = 0; } /* * For quick and easy access, make our own private copy of the token * and type lists. First, allocate an array of token structures. */ tok = (vmgramprod_tok *)get_ext()->mem_ ->alloc(tok_cnt * sizeof(vmgramprod_tok)); /* copy the token string references and types into our list */ for (i = 0 ; i < tok_cnt ; ++i) { vm_val_t ele_val; vm_val_t tok_typ_val; vm_val_t tok_str_val; int subcnt; const char *tokstrp; /* presume we won't have any vocabulary properties for the word */ tok[i].match_cnt_ = 0; tok[i].matches_ = 0; /* get this element from the list, and treat it as a list */ tokval->ll_index(vmg_ &ele_val, i + 1); /* make sure the sub-val is a list */ if (!ele_val.is_listlike(vmg0_) || (subcnt = ele_val.ll_length(vmg0_)) < 0) err_throw(VMERR_BAD_TYPE_BIF); /* * parse the token sublist: the first element is the token value, * and the second element is the token type */ ele_val.ll_index(vmg_ &tok_str_val, 1); ele_val.ll_index(vmg_ &tok_typ_val, 2); /* use the value if it's an enumerator; if not, use zero */ if (tok_typ_val.typ == VM_ENUM) tok[i].typ_ = tok_typ_val.val.enumval; else tok[i].typ_ = 0; /* get the token value as a string */ tokstrp = tok_str_val.get_as_string(vmg0_); /* if it's a string, copy it; otherwise, ignore the value */ if (tokstrp != 0) { /* remember the string value */ tok[i].val_ = tok_str_val; /* point the token to the string */ tok[i].txt_ = tokstrp + VMB_LEN; tok[i].len_ = vmb_get_len(tokstrp); /* calculate and store the token's hash value */ tok[i].hash_ = calc_str_hash( vmg_ dict, &tok_str_val, tokstrp + VMB_LEN, vmb_get_len(tokstrp)); /* * if we have a dictionary, enumerate the properties * associated with the word */ if (dict != 0) { enum_props_ctx ctx; /* set up our callback context */ ctx.ext = get_ext(); ctx.cnt = 0; /* enumerate the properties */ dict->enum_word_props(vmg_ &enum_props_cb, &ctx, &tok_str_val, tok[i].txt_, tok[i].len_); /* * if we found any properties for the word, make a copy * for the token list */ if (ctx.cnt != 0) { /* allocate space for the list */ tok[i].match_cnt_ = ctx.cnt; tok[i].matches_ = (vmgram_match_info *)get_ext()->mem_ ->alloc(ctx.cnt * sizeof(vmgram_match_info)); /* copy the list */ memcpy(tok[i].matches_, get_ext()->prop_enum_arr_, ctx.cnt * sizeof(tok[i].matches_[0])); } } } else { /* it's not a string - use a null string value for this token */ tok[i].txt_ = 0; tok[i].len_ = 0; } } /* enqueue a match state item for each of our alternatives */ enqueue_alts(vmg_ get_ext()->mem_, tok, tok_cnt, 0, 0, &queues, self, FALSE, 0, dict); /* process the work queue */ process_work_queue(vmg_ get_ext()->mem_, tok, tok_cnt, &queues, dict); /* count the entries in the success list */ for (succ_cnt = 0, match = queues.success_list_ ; match != 0 ; ++succ_cnt, match = match->nxt_) ; /* allocate the return list */ retval->set_obj(CVmObjList::create(vmg_ FALSE, succ_cnt)); lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* initially clear the elements of the return list to nil */ lst->cons_clear(); /* push the list momentarily to protect it from garbage collection */ G_stk->push(retval); /* set the elements */ for (succ_cnt = 0, match = queues.success_list_ ; match != 0 ; ++succ_cnt, match = match->nxt_) { vm_val_t tree_val; vm_val_t tok_match_val; size_t first_tok; size_t last_tok; /* * Create a list to hold the token match list - this is a list of * the dictionary's comparator's matchValue() results for the * tokens that matched literals in the grammar rule. We need a * list with one element per token that we matched. * * We can't actually populate this list yet, but all we need for * the moment are references to it. So, create the list; we'll * fill it in as we traverse the match to build the result tree. */ tok_match_val.set_obj( CVmObjList::create(vmg_ FALSE, match->tok_pos_)); /* push the token match value list for gc protection */ G_stk->push(&tok_match_val); /* clear it out */ ((CVmObjList *)vm_objp(vmg_ tok_match_val.val.obj))->cons_clear(); /* build the object tree for this success object */ build_match_tree(vmg_ match->match_, tokval, &tok_match_val, &tree_val, &first_tok, &last_tok); /* discard our token match list's gc protection stack entry */ G_stk->discard(); /* * add the match tree's root object to the main return list (note * that this protects it from garbage collection by virtue of the * main return list being protected from garbage collection) */ lst->cons_set_element(succ_cnt, &tree_val); } /* discard the gc protection */ G_stk->discard(); /* discard arguments */ G_stk->discard(orig_argc); /* evaluation successful */ return TRUE; } /* * Build an object tree for a match */ void CVmObjGramProd::build_match_tree(VMG_ const CVmGramProdMatch *match, const vm_val_t *toklist, const vm_val_t *tokmatchlist, vm_val_t *retval, size_t *first_tok, size_t *last_tok) { /* check to see what kind of match we have */ if (match->proc_obj_ != VM_INVALID_OBJ) { vm_obj_id_t obj_id; CVmObjTads *objp; size_t i; /* * Create the object to hold the current tree level. The only * constructor argument is the superclass, which is the match's * processor object. */ G_stk->push()->set_obj(match->proc_obj_); obj_id = CVmObjTads::create_from_stack(vmg_ 0, 1); /* get a pointer to the new object */ objp = (CVmObjTads *)vm_objp(vmg_ obj_id); /* push this object to protect it from gc for a moment */ G_stk->push()->set_obj(obj_id); /* * Initialize the caller's first/last token indices to an invalid * range, in case we have no subproductions. A production with no * tokens and no subproductions matches no tokens at all; we * indicate this by using an invalid range, with the first token * index higher than the last token index. Note that it's still * important that we accurately reflect the end of our range for a * zero-token match, since our enclosing nodes will need our * position to figure out where they go. */ *first_tok = match->tok_pos_ + 1; *last_tok = match->tok_pos_; /* build the sub-match entries */ for (i = 0 ; i < match->sub_match_cnt_ ; ++i) { size_t first_sub_tok; size_t last_sub_tok; vm_val_t val; /* build this subtree */ build_match_tree(vmg_ match->sub_match_list_[i], toklist, tokmatchlist, &val, &first_sub_tok, &last_sub_tok); /* * If this is the first subtree, use it as the tentative * limits for our overall match so far; otherwise, expand our * limits if they are outside our range so far. * * If the submatch doesn't include any tokens, it obviously * has no effect on our range. The submatch range will * indicate that the first token index is greater than the * last token index if the submatch includes no tokens. */ if (i == 0) { /* it's the first subtree - it's all we know as yet */ *first_tok = first_sub_tok; *last_tok = last_sub_tok; } else if (first_sub_tok <= last_sub_tok) { /* check to see if it expands our current range */ if (first_sub_tok < *first_tok) *first_tok = first_sub_tok; if (last_sub_tok > *last_tok) *last_tok = last_sub_tok; } /* * save the subtree with the current match object if there's a * property in which to save it */ if (match->sub_match_list_[i]->target_prop_ != VM_INVALID_PROP) { /* * Set the processor object property for the value. Note * that we don't have to keep undo for this change, since * we just created the tree object ourselves, and we don't * create any undo savepoints - an object created after * the most recent undo savepoint doesn't need to keep * undo information, since the entire object will be * deleted if we undo to the savepoint. */ objp->set_prop(vmg_ 0, obj_id, match->sub_match_list_[i]->target_prop_, &val); } } /* * if we have exported properties for recording the token index * range, save them with the object */ if (G_predef->gramprod_first_tok != VM_INVALID_PROP && G_predef->gramprod_last_tok != VM_INVALID_PROP) { vm_val_t val; /* save the first token index */ val.set_int((int)*first_tok + 1); objp->set_prop(vmg_ 0, obj_id, G_predef->gramprod_first_tok, &val); /* save the last token index */ val.set_int((int)*last_tok + 1); objp->set_prop(vmg_ 0, obj_id, G_predef->gramprod_last_tok, &val); } /* * if we have the token list property exported, set it to the * original token list reference */ if (G_predef->gramprod_token_list != VM_INVALID_PROP) { /* save the token list reference */ objp->set_prop(vmg_ 0, obj_id, G_predef->gramprod_token_list, toklist); } /* if we have the token match list property exported, set it */ if (G_predef->gramprod_token_match_list != VM_INVALID_PROP) { /* save the token match list reference */ objp->set_prop(vmg_ 0, obj_id, G_predef->gramprod_token_match_list, tokmatchlist); } /* discard our gc protection */ G_stk->discard(); /* the return value is the object */ retval->set_obj(obj_id); } else { /* get the token list */ int lstcnt = toklist->ll_length(vmg0_); /* make sure the index is in range */ if ((int)match->tok_pos_ < lstcnt) { vm_val_t ele_val; CVmObjList *match_lst; /* get the token from the list */ toklist->ll_index(vmg_ &ele_val, match->tok_pos_ + 1); /* * the token is itself a list, whose first element is the * token's value - retrieve the value */ ele_val.ll_index(vmg_ retval, 1); /* * Store the token match result in the result list. If this is * a '*' match, it doesn't have a result list contribution, * because '*' doesn't actually match any tokens (it merely * stops parsing). */ if (!match->matched_star_) { /* get the match list */ match_lst = (CVmObjList *)vm_objp(vmg_ tokmatchlist->val.obj); /* * set the element at this token position in the match list * to the token match result */ match_lst->cons_set_element( match->tok_pos_, &match->tok_match_result_); } } else { /* * the index is past the end of the list - this must be a * '*' token that matched nothing (i.e., the token list was * fully consumed before we reached the '*'), so just return * nil for the match value */ retval->set_nil(); } /* * We match one token, so it's the first and last index. Note * that if this is a '*' match, we don't match any tokens. */ if (!match->matched_star_) *first_tok = *last_tok = match->tok_pos_; } } /* * Process the work queue */ void CVmObjGramProd::process_work_queue(VMG_ CVmGramProdMem *mem, const vmgramprod_tok *tok, size_t tok_cnt, CVmGramProdQueue *queues, CVmObjDict *dict) { /* keep going until the work queue and badness queue are empty */ for (;;) { /* * If the work queue is empty, fall back on the badness queue. * Ignore the badness queue if we have any successful matches, * since non-badness matches always take precedence over badness * items. */ if (queues->work_queue_ == 0 && queues->success_list_ == 0) { int first; int min_badness; CVmGramProdState *cur; CVmGramProdState *prv; CVmGramProdState *nxt; /* find the lowest badness rating in the queue */ for (first = TRUE, min_badness = 0, cur = queues->badness_queue_ ; cur != 0 ; cur = cur->nxt_, first = FALSE) { /* * if we're on the first item, note its badness as the * tentative minimum; otherwise, note the current item's * badness if it's lower than the lowest we've seen so * far */ if (first || cur->altp_->badness < min_badness) min_badness = cur->altp_->badness; } /* * move each of the items whose badness matches the minimum * badness out of the badness queue and into the work queue */ for (cur = queues->badness_queue_, prv = 0 ; cur != 0 ; cur = nxt) { /* remember the next item, in case we remove this one */ nxt = cur->nxt_; /* if this item has minimum badness, move it */ if (cur->altp_->badness == min_badness) { /* unlink it from the badness queue */ if (prv == 0) queues->badness_queue_ = cur->nxt_; else prv->nxt_ = cur->nxt_; /* link it into the work queue */ cur->nxt_ = queues->work_queue_; queues->work_queue_ = cur; } else { /* * this item is staying in the list - note it as the * item preceding the next item */ prv = cur; } } } /* if the work queue is still empty, we're out of work to do */ if (queues->work_queue_ == 0) break; /* process the head of the work queue */ process_work_queue_head(vmg_ mem, tok, tok_cnt, queues, dict); } } /* * Process a work queue entry */ void CVmObjGramProd::process_work_queue_head(VMG_ CVmGramProdMem *mem, const vmgramprod_tok *tok, size_t tok_cnt, CVmGramProdQueue *queues, CVmObjDict *dict) { CVmGramProdState *state; CVmGramProdMatch *match; const vmgram_tok_info *tokp; int tok_matched_star; vm_prop_id_t enclosing_target_prop; /* get the first entry from the queue */ state = queues->work_queue_; /* unlink this entry from the queue */ queues->work_queue_ = state->nxt_; state->nxt_ = 0; /* get the token pointer for the next active entry in the alternative */ tokp = state->altp_->toks + state->alt_pos_; /* presume we won't match a '*' token */ tok_matched_star = FALSE; /* process the remaining items in the entry */ while (state->alt_pos_ < state->altp_->tok_cnt) { int match; vm_val_t match_result; /* presume we won't find a match */ match_result.set_nil(); /* see what we have for this token */ switch(tokp->typ) { case VMGRAM_MATCH_PROD: { vm_obj_id_t sub_obj_id; CVmObjGramProd *sub_objp; /* * This is a sub-production node. Get the * sub-production object. */ sub_obj_id = tokp->typinfo.prod_obj; /* make sure it's of the correct metaclass */ if (!CVmObjGramProd::is_gramprod_obj(vmg_ sub_obj_id)) { /* wrong type - throw an error */ err_throw(VMERR_INVAL_OBJ_TYPE); } /* get the sub-production object */ sub_objp = (CVmObjGramProd *)vm_objp(vmg_ sub_obj_id); /* * set my subproduction target property, so that the * sub-production can set the target property correctly */ state->sub_target_prop_ = tokp->prop; /* enqueue the alternatives for the sub-production */ sub_objp->enqueue_alts(vmg_ mem, tok, tok_cnt, state->tok_pos_, state, queues, sub_obj_id, FALSE, 0, dict); } /* * Do not process the current state any further for now - * we'll get back to it when (and if) we finish processing * the sub-production. Note that we don't even put the * current state back in the queue - it's stacked behind the * sub-production, and will be re-enqueued when we * successfully finish with the sub-production. */ return; case VMGRAM_MATCH_SPEECH: /* part of speech - check to see if the current token matches */ match = (state->tok_pos_ < tok_cnt && find_prop_in_tok(&tok[state->tok_pos_], tokp->typinfo.speech_prop)); break; case VMGRAM_MATCH_NSPEECH: /* * multiple parts of speech - check to see if the current token * matches any of the parts of the speech */ if (state->tok_pos_ < tok_cnt) { size_t rem; const vm_prop_id_t *prop; /* presume we won't find a match */ match = FALSE; /* check each item in the list */ for (prop = tokp->typinfo.nspeech.props, rem = tokp->typinfo.nspeech.cnt ; rem != 0 ; --rem, ++prop) { /* if this one matches, we have a match */ if (find_prop_in_tok(&tok[state->tok_pos_], *prop)) { /* note the match */ match = TRUE; /* no need to look any further at this token */ break; } } } else { /* * we're out of tokens, so we definitely don't have a * match, since we must match at least one of the possible * parts of speech of this item */ match = FALSE; } break; case VMGRAM_MATCH_LITERAL: /* * Literal - check for a match to the string. Test the hash * values first, as this is much faster than comparing the * strings, and at least tells us if the strings fail to match * (and failing to match being by far the most common case, * this saves us doing a full comparison most of the time). */ match = (state->tok_pos_ < tok_cnt && tokp->typinfo.lit.hash == tok[state->tok_pos_].hash_ && tok_equals_lit(vmg_ &tok[state->tok_pos_], tokp->typinfo.lit.str, tokp->typinfo.lit.len, dict, &match_result)); break; case VMGRAM_MATCH_TOKTYPE: /* token type */ match = (state->tok_pos_ < tok_cnt && (tok[state->tok_pos_].typ_ == tokp->typinfo.toktyp_enum)); break; case VMGRAM_MATCH_STAR: /* * this matches anything remaining (it also matches if * there's nothing remaining) */ match = TRUE; /* note that we matched a star in the state */ state->matched_star_ = TRUE; /* note that we matched a star for this particular token */ tok_matched_star = TRUE; break; } /* check for a match */ if (match) { /* * we matched this token - add a token match to our state's * match list for the current position */ state->match_list_[state->alt_pos_] = CVmGramProdMatch::alloc(mem, state->tok_pos_, &match_result, tok_matched_star, tokp->prop, VM_INVALID_OBJ, 0, 0); /* we're done with this alternative item - move on */ state->alt_pos_++; /* * If we matched a real token, consume the matched input * token. For a '*', we don't want to consume anything, since * a '*' merely stops parsing and doesn't actually match * anything. */ if (!state->matched_star_) state->tok_pos_++; /* move on to the next alternative token item */ ++tokp; } else { /* * This is not a match - reject this alternative. We do not * need to process this item further, so we can stop now, * without returning this state to the work queue. Simply * abandon this work queue item. */ return; } } /* * If we make it here, we've reached the end of this alternative and * have matched everything in it. This means that the alternative * was successful, so we can perform a 'reduce' operation to replace * the matched tokens with this production. */ /* if we have an enclosing state, get its target property */ enclosing_target_prop = (state->enclosing_ != 0 ? state->enclosing_->sub_target_prop_ : VM_INVALID_PROP); /* create a match for the entire alternative (i.e., the subproduction) */ match = CVmGramProdMatch::alloc( mem, state->tok_pos_, 0, FALSE, enclosing_target_prop, state->altp_->proc_obj, &state->match_list_[0], state->altp_->tok_cnt); /* * If the state we just matched was marked on creation as coming * from an alternative with circular alternatives, we've just come * up with a match for each circular alternative. To avoid infinite * recursion, we never enqueue the circular alternatives; however, * now that we know we have a match for the first element of each * circular alternative, we can check each of them to see if we can * enqueue them. */ if (state->circular_alt_) { vm_obj_id_t prod_obj_id; CVmObjGramProd *prod_objp; /* * Get the production from which this alternative came (there * should be no need to check the metaclass, since we could only * have created the state object from a valid production in the * first place). * * First, make sure it's of the correct class. (It almost * certainly is, since the compiler should have generated this * reference automatically, so there should be no possibility of a * user error. However, if it's not a production object, we'll * probably crash by blindly casting it to one; so we'll check * here, just to be sure that the compiler didn't do something * wrong and the image file didn't become corrupted.) */ if (!CVmObjGramProd::is_gramprod_obj(vmg_ state->prod_obj_)) { /* wrong type - throw an error */ err_throw(VMERR_INVAL_OBJ_TYPE); } /* get the object, properly cast */ prod_obj_id = state->prod_obj_; prod_objp = (CVmObjGramProd *)vm_objp(vmg_ prod_obj_id); /* * Enqueue the matching circular alternatives from the original * production. Our match becomes the first token match in the * circular alternative (i.e., it matches the leading circular * reference element). */ prod_objp->enqueue_alts(vmg_ mem, tok, tok_cnt, state->tok_pos_, state->enclosing_, queues, prod_obj_id, TRUE, match, dict); } /* check for an enclosing state to pop */ if (state->enclosing_ != 0) { /* add the match to the enclosing state's match list */ state->enclosing_->match_list_[state->enclosing_->alt_pos_] = match; state->enclosing_->alt_pos_++; /* * Move the enclosing state's token position to our token position * - since it now encompasses our match, it has consumed all of * the tokens therein. Likewise, set the '*' flag in the parent * to our own setting. */ state->enclosing_->tok_pos_ = state->tok_pos_; state->enclosing_->matched_star_ = state->matched_star_; /* enqueue the enclosing state so we can continue parsing it */ enqueue_state(state->enclosing_, queues); } else { CVmGramProdMatchEntry *entry; /* * This is a top-level state, so we've completed parsing. If * we've consumed all input, or we matched a '*' token, we were * successful. If there's more input remaining, this is not a * match. */ if (state->tok_pos_ < tok_cnt && !state->matched_star_) { /* * There's more input remaining, so this isn't a match. * Reject this alternative. We do not need to process this * item further, so stop now without returning this state * object to the work queue. */ /* abandon this work queue item */ return; } /* create a new entry for the match list */ entry = (CVmGramProdMatchEntry *)mem->alloc(sizeof(*entry)); entry->match_ = match; entry->tok_pos_ = state->tok_pos_; /* link the entry into the list */ entry->nxt_ = queues->success_list_; queues->success_list_ = entry; } } /* * Visit my alternatives and enqueue each one that is a possible match * for a given token list. */ void CVmObjGramProd::enqueue_alts(VMG_ CVmGramProdMem *mem, const vmgramprod_tok *tok, size_t tok_cnt, size_t start_tok_pos, CVmGramProdState *enclosing_state, CVmGramProdQueue *queues, vm_obj_id_t self, int circ_only, CVmGramProdMatch *circ_match, CVmObjDict *dict) { vmgram_alt_info *const *altp; size_t i; int need_to_clone; int has_circ; vm_obj_id_t comparator; /* * We don't need to clone the enclosing state until we've set up one * new state referring back to it. However, if we're doing circular * alternatives, the enclosing state could also be enqueued * separately as its own state, so do clone all required copies in * this case. */ need_to_clone = circ_only; /* note whether or not we have any circular alternatives */ has_circ = get_ext()->has_circular_alt; /* * Cache hash values for all of our literals. We need to do this if we * haven't done it before, or if the comparator associated with the * dictionary is different than it was last time we cached the values. */ comparator = (dict != 0 ? dict->get_comparator() : VM_INVALID_OBJ); if (!get_ext()->hashes_cached_ || get_ext()->comparator_ != comparator) cache_hashes(vmg_ dict); /* * run through our alternatives and enqueue each one that looks * plausible */ for (altp = get_ext()->alts_, i = get_ext()->alt_cnt_ ; i != 0 ; --i, ++altp) { /* start at the first token remaining in the string */ size_t tok_idx = start_tok_pos; /* get the first token for this alternative */ vmgram_tok_info *first_tokp = (*altp)->toks; /* start at the first token */ vmgram_tok_info *tokp = first_tokp; /* get the number of tokens in the alternative */ size_t alt_tok_cnt = (*altp)->tok_cnt; /* presume we will find no mismatches in this check */ int found_mismatch = FALSE; /* * note whether this alternative is a direct circular reference * or not (it is directly circular if the first token points * back to this same production) */ int is_circ = (alt_tok_cnt != 0 && tokp->typ == VMGRAM_MATCH_PROD && tokp->typinfo.prod_obj == self); /* * we don't have a production before the current item (this is * important because it determines whether we must consider the * current token or any following token when trying to match a * literal, part of speech, or token type) */ int prod_before = FALSE; /* scan the tokens in the alternative */ for (size_t cur_alt_tok = 0 ; cur_alt_tok < alt_tok_cnt ; ++cur_alt_tok, ++tokp) { int match; int match_star; int ignore_item; vm_val_t match_result; /* presume we won't find a match */ match = FALSE; match_star = FALSE; /* presume we won't be able to ignore the item */ ignore_item = FALSE; /* * Try to find a match to the current alternative item. If * we've already found a mismatch, don't bother, since we need * no further evidence to skip this alternative; in this case * we're still looping over the alternative items simply to * find the beginning of the next alternative. */ for ( ; !found_mismatch ; ++tok_idx) { /* see what kind of item we have */ switch(tokp->typ) { case VMGRAM_MATCH_PROD: /* * We can't rule out a production without checking * it fully, which we're not doing on this pass; * however, note that we found a production, because * it means that we must from now on try skipping * any number of tokens before ruling out a match on * other grounds, since the production could * potentially match any number of tokens. * * If we're doing circular matches only, and this is * a circular production, and we're on the first * token in our list, we already know it matches, so * don't even count it as a production before the * item. */ if (is_circ && circ_only && cur_alt_tok == 0) { /* * ignore the fact that it's a production, since * we already know it matches - we don't want to * be flexible about tokens following this item * since we know the exact number that match it */ } else { /* note the production item */ prod_before = TRUE; } /* * we can ignore this item for the purposes of * detecting a mismatch, because we can't tell * during this superficial scan whether it matches */ ignore_item = TRUE; break; case VMGRAM_MATCH_SPEECH: /* we must match a part of speech */ if (tok_idx < tok_cnt && find_prop_in_tok(&tok[tok_idx], tokp->typinfo.speech_prop)) { /* it's a match */ match = TRUE; } break; case VMGRAM_MATCH_NSPEECH: /* multiple parts of speech */ if (tok_idx < tok_cnt) { vm_prop_id_t *prop; size_t rem; /* presume we won't find a match */ match = FALSE; /* check each item in the list */ for (prop = tokp->typinfo.nspeech.props, rem = tokp->typinfo.nspeech.cnt ; rem != 0 ; --rem, ++prop) { /* if this one matches, we have a match */ if (find_prop_in_tok(&tok[tok_idx], *prop)) { /* note the match */ match = TRUE; /* no need to look any further */ break; } } } else { /* we're out of tokens, so we have no match */ match = FALSE; } break; case VMGRAM_MATCH_LITERAL: /* * Match a literal. Compare the hash values first, * since a negative match on the hash values will tell * us for sure that the text won't match; since not * matching is by far the most common case, this saves * us the work of doing full string comparisons most of * the time. */ if (tok_idx < tok_cnt && tok[tok_idx].txt_ != 0 && tokp->typinfo.lit.hash == tok[tok_idx].hash_ && tok_equals_lit(vmg_ &tok[tok_idx], tokp->typinfo.lit.str, tokp->typinfo.lit.len, dict, &match_result)) { /* it's a match */ match = TRUE; } break; case VMGRAM_MATCH_TOKTYPE: /* we must match a token type */ if (tok_idx < tok_cnt && tok[tok_idx].typ_ == tokp->typinfo.toktyp_enum) match = TRUE; break; case VMGRAM_MATCH_STAR: /* consume everything remaining on the line */ tok_idx = tok_cnt; /* this is a match */ match = TRUE; match_star = TRUE; break; } /* if we found a match, we can stop scanning now */ if (match) break; /* * we didn't match this token - if we have a production * before this point, we must keep scanning token, since * the production could end up matching any number of * tokens; if we don't have a production item, though, * we don't need to look any further, and we can rule * out this alternative immediately */ if (!prod_before) break; /* * if we're ignoring this item (because we can't decide * now whether it's a match or not, and hence can't rule * it out), don't scan any tokens for the item */ if (ignore_item) break; /* * if we didn't find a match, and we're out of tokens, * stop looking - we have no more tokens to look at to * find a match */ if (!match && tok_idx >= tok_cnt) break; } /* check to see if we found a match */ if (match) { /* * we found a match - skip the current token (unless we * matched a '*' item, in which case we've already * consumed all remaining tokens) */ if (!match_star && tok_idx < tok_cnt) ++tok_idx; } else if (!ignore_item) { /* * we didn't match, and we can't ignore this item - note * that the alternative does not match */ found_mismatch = TRUE; /* finish up with this alternative and proceed to the next */ break; } } /* * If we didn't find a reason to rule out this alternative, * enqueue the alternative. Never enqueue circular references, * unless we're ONLY enqueuing circular references. */ if (!found_mismatch && ((!circ_only && !is_circ) || (circ_only && is_circ))) { CVmGramProdState *state; /* create and enqueue the new state */ state = enqueue_new_state(mem, start_tok_pos, enclosing_state, *altp, self, &need_to_clone, queues, has_circ); /* * if we are enqueuing circular references, we already have * a match for the first (circular) token, so fill it in */ if (circ_only) { CVmGramProdMatch *match; /* create a match for the alternative */ match = CVmGramProdMatch:: alloc(mem, 0, 0, FALSE, first_tokp->prop, circ_match->proc_obj_, circ_match->sub_match_list_, circ_match->sub_match_cnt_); /* set the first match in the token */ state->match_list_[0] = match; /* set up at the second token */ state->alt_pos_ = 1; } } } } /* * Cache hash values for all of our literal tokens */ void CVmObjGramProd::cache_hashes(VMG_ CVmObjDict *dict) { vm_obj_id_t comparator; vmgram_alt_info **alt; size_t i; /* get the comparator */ comparator = (dict != 0 ? dict->get_comparator() : VM_INVALID_OBJ); /* run through our alternatives */ for (i = get_ext()->alt_cnt_, alt = get_ext()->alts_ ; i != 0 ; --i, ++alt) { vmgram_tok_info *tok; size_t j; /* run through our tokens */ for (j = (*alt)->tok_cnt, tok = (*alt)->toks ; j != 0 ; --j, ++tok) { /* if this is a literal token, calculate its hash value */ if (tok->typ == VMGRAM_MATCH_LITERAL) { /* calculate and store our hash value */ tok->typinfo.lit.hash = calc_str_hash( vmg_ dict, 0, tok->typinfo.lit.str, tok->typinfo.lit.len); } } } /* note that we've cached hash values, and note the comparator we used */ get_ext()->hashes_cached_ = TRUE; get_ext()->comparator_ = comparator; } /* * Create and enqueue a new state */ CVmGramProdState *CVmObjGramProd:: enqueue_new_state(CVmGramProdMem *mem, size_t start_tok_pos, CVmGramProdState *enclosing_state, const vmgram_alt_info *altp, vm_obj_id_t self, int *need_to_clone, CVmGramProdQueue *queues, int circular_alt) { CVmGramProdState *state; /* create the new state */ state = create_new_state(mem, start_tok_pos, enclosing_state, altp, self, need_to_clone, circular_alt); /* * Add the item to the appropriate queue. If the item has an * associated badness, add it to the badness queue. Otherwise, add * it to the work queue. */ if (altp->badness != 0) { /* * we have a badness rating - add it to the badness queue, since * we don't want to process it until we entirely exhaust better * possibilities */ state->nxt_ = queues->badness_queue_; queues->badness_queue_ = state; } else { /* enqueue the state */ enqueue_state(state, queues); } /* return the new state */ return state; } /* * Create a new state */ CVmGramProdState *CVmObjGramProd:: create_new_state(CVmGramProdMem *mem, size_t start_tok_pos, CVmGramProdState *enclosing_state, const vmgram_alt_info *altp, vm_obj_id_t self, int *need_to_clone, int circular_alt) { /* * if necessary, clone the enclosing state; we need to do this if we * enqueue more than one nested alternative, since each nested * alternative could parse the rest of the token list differently * and thus provide a different result to the enclosing state */ if (*need_to_clone && enclosing_state != 0) enclosing_state = enclosing_state->clone(mem); /* * we will need to clone the enclosing state if we create another * alternative after this one */ *need_to_clone = TRUE; /* create a new state object for the alternative */ return CVmGramProdState::alloc(mem, start_tok_pos, 0, enclosing_state, altp, self, circular_alt); } /* * Enqueue a state in the work queue */ void CVmObjGramProd::enqueue_state(CVmGramProdState *state, CVmGramProdQueue *queues) { /* add it to the work queue */ state->nxt_ = queues->work_queue_; queues->work_queue_ = state; } /* * Determine if a token from the input matches a literal token, allowing * the input token to be a truncated version of the literal token if the * given truncation length is non-zero. */ int CVmObjGramProd::tok_equals_lit(VMG_ const struct vmgramprod_tok *tok, const char *lit, size_t lit_len, CVmObjDict *dict, vm_val_t *result_val) { /* * if there's a dictionary, ask it to do the comparison; otherwise, * use an exact literal match */ if (dict != 0) { /* ask the dictionary for the comparison */ return dict->match_strings(vmg_ &tok->val_, tok->txt_, tok->len_, lit, lit_len, result_val); } else { /* perform an exact comparison */ result_val->set_int(tok->len_ == lit_len && memcmp(tok->txt_, lit, lit_len) == 0); return result_val->val.intval; } } /* * Calculate the hash value for a literal token, using our dictionary's * comparator. */ unsigned int CVmObjGramProd::calc_str_hash(VMG_ CVmObjDict *dict, const vm_val_t *strval, const char *str, size_t len) { /* * if we have a dictionary, ask it to ask it for the hash value, as the * dictionary will ask its comparator to do the work; otherwise, * calculate our own hash value */ if (dict != 0) { /* * We're doing comparisons through the dictionary's comparator, so * we must calculate hash values using the comparator as well to * keep the hash and match operations consistent. */ return dict->calc_str_hash(vmg_ strval, str, len); } else { uint hash; /* * We don't have a dictionary, so we're doing our own string * comparisons, which we do as exact byte-for-byte matches. We can * thus calculate an arbitrary hash value of our own devising. */ for (hash = 0 ; len != 0 ; ++str, --len) hash += (unsigned char)*str; /* return the hash value we calculated */ return hash; } } /* * Find a property in a token's list. Returns true if the property is * there, false if not. */ int CVmObjGramProd::find_prop_in_tok(const vmgramprod_tok *tok, vm_prop_id_t prop) { size_t i; const vmgram_match_info *p; /* search for the property */ for (i = tok->match_cnt_, p = tok->matches_ ; i != 0 ; --i, ++p) { /* if this is the one we're looking for, return success */ if (p->prop == prop) return TRUE; } /* we didn't find it */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Execute the getGrammarInfo() method - builds and returns an information * structure describing this grammar production object. */ int CVmObjGramProd::getp_get_gram_info(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_obj_id_t alt_lst_id; CVmObjList *alt_lst; size_t i; vmgram_alt_info **altp; static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* * This routine uses considerable extra stack in the course of its * work, so check in advance that we have enough space to run. */ if (!G_stk->check_space(8)) err_throw(VMERR_STACK_OVERFLOW); /* create a list to hold the rule alternatives */ alt_lst_id = CVmObjList::create(vmg_ FALSE, get_ext()->alt_cnt_); alt_lst = (CVmObjList *)vm_objp(vmg_ alt_lst_id); alt_lst->cons_clear(); /* push it onto the stack for protection from garbage collection */ G_stk->push()->set_obj(alt_lst_id); /* create the rule-alternative descriptions */ for (i = 0, altp = get_ext()->alts_ ; i < get_ext()->alt_cnt_ ; ++i, ++altp) { size_t j; vmgram_tok_info *tokp; vm_val_t alt_obj_val; vm_obj_id_t tok_lst_id; CVmObjList *tok_lst; /* * if our imported GrammarAltInfo class isn't defined, we can't * provide rule-alternative information, so just fill in the list * with a nil */ if (G_predef->gramprod_gram_alt_info == VM_INVALID_OBJ) { /* add a nil to the list */ alt_obj_val.set_nil(); alt_lst->cons_set_element(i, &alt_obj_val); /* done with this slot */ continue; } /* create a list to hold the token descriptors */ tok_lst_id = CVmObjList::create(vmg_ FALSE, (*altp)->tok_cnt); tok_lst = (CVmObjList *)vm_objp(vmg_ tok_lst_id); tok_lst->cons_clear(); /* push this momentarily for gc protection */ G_stk->push()->set_obj(tok_lst_id); /* * Run through the tokens in the rule alternative, and build a list * of token descriptor structures. */ for (j = 0, tokp = (*altp)->toks ; j < (*altp)->tok_cnt ; ++j, ++tokp) { vm_val_t tok_obj_val; /* * if our imported GrammarAltTokInfo class isn't defined, we * can't provide token information, so just fill in the list * with a nil */ if (G_predef->gramprod_gram_alt_tok_info == VM_INVALID_OBJ) { /* add a nil to the list */ tok_obj_val.set_nil(); tok_lst->cons_set_element(j, &tok_obj_val); /* done with this slot */ continue; } /* * The constructor arguments are the target property ID, the * token type code, and extra information that depends on the * type code. We have to push the arguments backwards per the * normal protocol, so start with the variant part. */ switch (tokp->typ) { case VMGRAM_MATCH_PROD: /* the extra information is the sub-production object */ G_stk->push()->set_obj(tokp->typinfo.prod_obj); break; case VMGRAM_MATCH_SPEECH: /* the extra info is the part-of-speech property */ G_stk->push()->set_propid(tokp->typinfo.speech_prop); break; case VMGRAM_MATCH_NSPEECH: /* the extra info is a list of part-of-speech properties */ { vm_obj_id_t pos_lst_id; CVmObjList *pos_lst; vm_prop_id_t *pp; size_t k; /* create the property list */ pos_lst_id = CVmObjList::create( vmg_ FALSE, tokp->typinfo.nspeech.cnt); pos_lst = (CVmObjList *)vm_objp(vmg_ pos_lst_id); /* push it */ G_stk->push()->set_obj(pos_lst_id); /* populate it */ for (k = 0, pp = tokp->typinfo.nspeech.props ; k < tokp->typinfo.nspeech.cnt ; ++k, ++pp) { vm_val_t val; /* fill in this element */ val.set_propid(*pp); pos_lst->cons_set_element(k, &val); } } break; case VMGRAM_MATCH_LITERAL: /* the extra info is the literal string value */ G_stk->push()->set_obj(CVmObjString::create( vmg_ FALSE, tokp->typinfo.lit.str, tokp->typinfo.lit.len)); break; case VMGRAM_MATCH_TOKTYPE: /* the extra information is the token class enum */ G_stk->push()->set_enum(tokp->typinfo.toktyp_enum); break; case VMGRAM_MATCH_STAR: /* * for a 'star' type, there is no extra information, but * push nil to keep the argument list uniform */ G_stk->push()->set_nil(); break; default: assert(FALSE); break; } /* now push the target property and type code */ G_stk->push()->set_int(tokp->typ); if (tokp->prop != VM_INVALID_PROP) G_stk->push()->set_propid(tokp->prop); else G_stk->push()->set_nil(); /* * the first argument to the constructor is the base class, * which is the imported GrammarAltTokInfo class object */ G_stk->push()->set_obj(G_predef->gramprod_gram_alt_tok_info); /* create the object, at long last */ tok_obj_val.set_obj(CVmObjTads::create_from_stack(vmg_ 0, 4)); /* add it to our token list */ tok_lst->cons_set_element(j, &tok_obj_val); } /* * Create the GrammarAltInfo object to desribe the alternative. * Pass in the score, badness, match object, and token list as * arguments to the constructor, which we'll count on to stash the * information away in the new object. * * Note that the first constructor argument is the superclass, * which is the imported GrammarAltInfo class object. */ G_stk->push()->set_obj(tok_lst_id); G_stk->push()->set_obj((*altp)->proc_obj); G_stk->push()->set_int((*altp)->badness); G_stk->push()->set_int((*altp)->score); G_stk->push()->set_obj(G_predef->gramprod_gram_alt_info); alt_obj_val.set_obj(CVmObjTads::create_from_stack(vmg_ 0, 5)); /* add the new GrammarAltInfo to the main alternative list */ alt_lst->cons_set_element(i, &alt_obj_val); /* * It's safe to discard the gc protection for the token list now, * since a reference to is now stashed away in the GrammarAltInfo * object, which in turn is referenced from our main alternative * list, which we stashed on the stack earlier for gc protection. */ G_stk->discard(); } /* it's safe to discard our gc protection for the main list now */ G_stk->discard(); /* return the main list */ retval->set_obj(alt_lst_id); /* successful evaluation */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * deleteAlt() method - remove an alternative from our rule list */ int CVmObjGramProd::getp_deleteAlt(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { const vm_val_t *v; /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the ID argument */ vm_val_t *id = G_stk->get(0); /* get the dictionary, if present */ vm_obj_id_t dict = VM_INVALID_OBJ; if (argc >= 2 && (v = G_stk->get(1))->typ != VM_NIL && (v->typ != VM_OBJ || !CVmObjDict::is_dictionary_obj(vmg_ dict = v->val.obj))) err_throw(VMERR_BAD_TYPE_BIF); /* push 'self' for gc protection */ G_interpreter->push_obj(vmg_ self); /* check the ID type */ const char *idstr; if (id->typ == VM_INT) { /* get the index of the alternative to delete */ int idx = id->val.intval; /* make sure it's in range */ if (idx < 1 || idx > (int)get_ext()->alt_cnt_) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* mark the item for deletion */ get_ext()->alts_[idx-1]->del = TRUE; } else if ((idstr = id->get_as_string(vmg0_)) != 0) { /* get the length and buffer pointer for the tag */ size_t idlen = vmb_get_len(idstr); idstr += VMB_LEN; /* get the garmmarTag property import */ vm_prop_id_t grammarTag = G_predef->gramprod_tag; /* set up a recursive invoker for grammarTag */ vm_rcdesc rc(vmg_ "GrammarProd.deleteAlt", self, PROPIDX_deleteAlt, id, argc); /* * Delete by tag name. Run through the alternatives and delete * each one whose match object's grammarTag property matches the * given tag name. Work backwards through the array to avoid the * overhead of closing gaps when we delete items. */ for (int idx = get_ext()->alt_cnt_ ; idx != 0 ; ) { /* on to the next index */ --idx; /* get this item's match object */ vm_obj_id_t m = get_ext()->alts_[idx]->proc_obj; /* * if there's a match object, and the 'grammarTag' import is * defined, check the match object's grammarTag value */ if (m != VM_INVALID_OBJ && grammarTag != VM_INVALID_PROP) { /* evaluate m.grammarProd */ vm_val_t mval; mval.set_obj(m); G_interpreter->get_prop( vmg_ 0, &mval, grammarTag, &mval, 0, &rc); /* if it's a string, compare it */ const char *gt = G_interpreter->get_r0()->get_as_string(vmg0_); if (gt != 0 && vmb_get_len(gt) == idlen && memcmp(gt + VMB_LEN, idstr, idlen) == 0) { /* it's a match - mark it for deletion */ get_ext()->alts_[idx]->del = TRUE; } } } } else if (id->typ == VM_OBJ) { /* * Delete each alternative whose match object equals or is a * subclass of the given object. Work backwards from the end of * the list to avoid the overhead of closing the gap when we delete * an item earlier in the array. */ for (int idx = get_ext()->alt_cnt_ ; idx != 0 ; ) { /* on to the next index */ --idx; /* check for a match to the match object */ vm_obj_id_t m = get_ext()->alts_[idx]->proc_obj; if (m != VM_INVALID_OBJ && (m == id->val.obj || vm_objp(vmg_ m)->is_instance_of(vmg_ id->val.obj))) { /* it's a match - mark it for deletion */ get_ext()->alts_[idx]->del = TRUE; } } } else { /* invalid type */ err_throw(VMERR_BAD_VAL_BIF); } /* delete the marked batch */ delete_marked_alts(vmg_ self, dict); /* return self */ retval->set_obj(self); /* discard arguments and gc protection */ G_stk->discard(argc + 1); /* handled */ return TRUE; } /* * Delete the marked alternatives. This runs through the alternative list * and deletes each alternative marked with the 'del' flag. We save undo * for the deletions and update the dictionary (if provided) to remove * literals associated with this production that are no longer used in any * alternative after the deletions. */ void CVmObjGramProd::delete_marked_alts( VMG_ vm_obj_id_t self, vm_obj_id_t dict) { /* get the alt list */ vmgram_alt_info **alts = get_ext()->alts_; /* if there's a dictionary, delete the words that are no longer in use */ if (dict != VM_INVALID_OBJ && G_predef->misc_vocab != VM_INVALID_PROP) { /* get the dictionary object, properly cast */ CVmObjDict *dictp = (CVmObjDict *)vm_objp(vmg_ dict); /* run through the alternatives being deleted */ for (size_t i = 0 ; i < get_ext()->alt_cnt_ ; ++i) { /* get this item */ vmgram_alt_info *alt = alts[i]; /* if it's not being deleted, skip it */ if (!alt->del) continue; /* run through its token list looking for literals */ vmgram_tok_info *tok = alt->toks; for (size_t j = 0 ; j < alt->tok_cnt ; ++j, ++tok) { /* * if it's a literal, and it's not defined in a surviving * alternative, delete it from the dictionary */ if (tok->typ == VMGRAM_MATCH_LITERAL && !find_literal_in_alts( tok->typinfo.lit.str,tok->typinfo.lit.len)) { /* it's no longer used - remove it from the dictionary */ dictp->del_word( vmg_ dict, tok->typinfo.lit.str, tok->typinfo.lit.len, self, G_predef->misc_vocab); } } } } /* * Perform the deletions. Work backwards from the end of the array to * avoid overhead closing gaps in the array. */ for (size_t i = get_ext()->alt_cnt_ ; i > 0 ; ) { /* get the next item */ vmgram_alt_info *alt = alts[--i]; /* if it's marked for deletion, delete it */ if (alt->del) { /* unmark it, in case we later restore it via undo */ alt->del = FALSE; /* * If this is the only remaining alternative with the same * match object that's being deleted, rebuild the match * object's grammarAltProps list. If there's another * alternative yet to be deleted that uses the same match * object, don't bother doing the rebuild now, since we'll do * it again when the loop reaches the earlier alternative, and * we only need to do it once for the whole batch. */ int rebuild = TRUE; for (size_t j = 0 ; j < i ; ++j) { /* * if this one's being deleted, and it has the same match * object, skip the rebuild now since we'll do it when we * get to 'j' in the main loop */ if (alts[j]->del && alts[j]->proc_obj == alt->proc_obj) { rebuild = FALSE; break; } } /* do the rebuild if this is our last chance */ if (rebuild) build_alt_props(vmg_ alt->proc_obj); /* delete it */ delete_alt_undo(vmg_ self, i); } } /* check for circular references */ check_circular_refs(self); } /* * Scan surviving alternatives (not marked with 'del') for a given literal */ int CVmObjGramProd::find_literal_in_alts(const char *str, size_t len) { /* * Scan the surviving alternatives to see if this same literal token * appears in any of them. If not, it's no longer in use in the * production, so delete it from the dictionary. */ for (size_t i = 0 ; i < get_ext()->alt_cnt_ ; ++i) { /* get this item */ vmgram_alt_info *alt = get_ext()->alts_[i]; /* if it's being deleted, skip it */ if (alt->del) continue; /* it's still alive - scan its tokens */ vmgram_tok_info *tok = alt->toks; for (size_t j = 0 ; j < alt->tok_cnt ; ++j, ++tok) { /* if this is a matching literal, the word is still in use */ if (tok->typ == VMGRAM_MATCH_LITERAL && tok->typinfo.lit.len == len && memcmp(tok->typinfo.lit.str, str, len) == 0) return TRUE; } } /* didn't find it */ return FALSE; } /* * delete an alternative, keeping undo */ void CVmObjGramProd::delete_alt_undo(VMG_ vm_obj_id_t self, int idx) { /* create an undo record for deleting this item */ vmgram_undo_rec *rec = new vmgram_undo_rec( VMGRAM_UNDO_DELETED, idx, get_ext()->alts_[idx]); /* save the record */ if (!G_undo->add_new_record_ptr_key(vmg_ self, rec)) delete rec; /* remove the alternative from the array */ delete_alt(idx); } /* * Check for circular references. After deleting one or more alternatives, * call this to see if we can clear the circular reference flag. This * scans the surviging alternatives, and clears the flag if none of them * are circular. */ void CVmObjGramProd::check_circular_refs(vm_obj_id_t self) { /* presume no circular references */ get_ext()->has_circular_alt = FALSE; /* scan the alternative list */ vmgram_alt_info **altp = get_ext()->alts_; for (size_t i = 0 ; i < get_ext()->alt_cnt_ ; ++i, ++altp) { /* get the alternative */ vmgram_alt_info *alt = *altp; /* check for a circular reference */ if (alt->tok_cnt != 0 && alt->toks->typ == VMGRAM_MATCH_PROD && alt->toks->typinfo.prod_obj == self) { /* it's circular - set the flag */ get_ext()->has_circular_alt = TRUE; /* no need to look any further */ break; } } } /* * Rebuild the grammarAltProps list for a given match object. This creates * a list of all of the properties used in "->" expressions in all of the * alternatives associated with the match object. */ void CVmObjGramProd::build_alt_props(VMG_ vm_obj_id_t match_obj) { /* if there's no grammarAltProps property export, skip this */ if (G_predef->gramprod_alt_props == VM_INVALID_PROP) return; /* first do a scan without an output list to count the properties */ size_t cnt = build_alt_props_list(vmg_ match_obj, 0); /* create the list */ vm_val_t lstval; lstval.set_obj(CVmObjList::create(vmg_ FALSE, cnt)); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lstval.val.obj); /* build the list */ build_alt_props_list(vmg_ match_obj, lst); /* keep only unique properties in the list */ lst->cons_uniquify(vmg0_); /* store it in the match object under grammarAltProps */ vm_objp(vmg_ match_obj)->set_prop( vmg_ G_undo, match_obj, G_predef->gramprod_alt_props, &lstval); } /* * Build the grammarAltProps list for a given object, storing the results * in the given list. Returns the number of properties we found. */ size_t CVmObjGramProd::build_alt_props_list( VMG_ vm_obj_id_t match_obj, CVmObjList *lst) { /* we haven't stored any properties yet */ size_t n = 0; /* run through the surviving alternatives */ vmgram_alt_info **alts = get_ext()->alts_; for (size_t i = 0 ; i < get_ext()->alt_cnt_ ; ++i, ++alts) { /* * if this alternative isn't being deleted, and it's associated * with the given match object, add its properties to the list */ vmgram_alt_info *alt = *alts; if (!alt->del && alt->proc_obj == match_obj) { /* scan its token list */ vmgram_tok_info *tok = alt->toks; for (size_t j = 0 ; j < alt->tok_cnt ; ++j, ++tok) { /* if this token has a target property, add it to the list */ if (tok->prop != VM_INVALID_PROP) { /* if we have a list, store it */ if (lst != 0) { vm_val_t v; v.set_propid(tok->prop); lst->cons_set_element(n, &v); } /* count it */ ++n; } } } } /* return the number of properties we stored */ return n; } /* ------------------------------------------------------------------------ */ /* * clearAlts() method - removes all alternatives */ int CVmObjGramProd::getp_clearAlts(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { const vm_val_t *v; /* check arguments */ int argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the dictionary object, if specified */ vm_obj_id_t dict = VM_INVALID_OBJ; if (argc >= 1 && (v = G_stk->get(0))->typ != VM_NIL && (v->typ != VM_OBJ || !CVmObjDict::is_dictionary_obj(vmg_ dict = v->val.obj))) err_throw(VMERR_BAD_TYPE_BIF); /* mark every alternative for deletion */ for (size_t i = 0 ; i < get_ext()->alt_cnt_ ; ++i) get_ext()->alts_[i]->del = TRUE; /* save 'self' for gc protection */ G_interpreter->push_obj(vmg_ self); /* delete the batch */ delete_marked_alts(vmg_ self, dict); /* return self */ retval->set_obj(self); /* discard arguments and gc protection */ G_stk->discard(argc + 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * addAlt() method - add a new alternative */ /* compiler results/callback object for grammar rule parsing */ struct CVmGramCompResults: public CVmDynCompResults { CVmGramCompResults(vm_obj_id_t self, CVmObjGramProd *selfp, vm_obj_id_t dict, vm_obj_id_t match) { this->self = self; this->selfp = selfp; this->dict = dict; this->match = match; } /* save the parsed grammar rule data in our object */ virtual void save_grammar( VMG_ CTcGramProdAlt *alts, CTcGramPropArrows *arrows) { /* add each alternative */ for ( ; alts != 0 ; alts = alts->get_next()) selfp->add_alt(vmg_ self, alts, dict, match); } /* self - the GrammarProd that we're adding rules to */ vm_obj_id_t self; CVmObjGramProd *selfp; /* the Dictionary object in scope for the grammar rules */ vm_obj_id_t dict; /* the match object for the alternatives */ vm_obj_id_t match; }; /* add an alternative to the production */ void CVmObjGramProd::add_alt(VMG_ vm_obj_id_t self, CTcGramProdAlt *alt, vm_obj_id_t dict, vm_obj_id_t match) { /* add it at the last position */ int idx = get_ext()->alt_cnt_; /* count the tokens in the rule */ CTcGramProdTok *tok; int ntoks; for (ntoks = 0, tok = alt->get_tok_head() ; tok != 0 ; ++ntoks, tok = tok->get_next()) ; /* create our internal alt object */ vmgram_alt_info *ai = new vmgram_alt_info(ntoks); /* set the attributes */ ai->proc_obj = match; ai->score = alt->get_score(); ai->badness = alt->get_badness(); /* translate from the parsed token list to our internal token list */ vmgram_tok_info *ti = ai->toks; for (tok = alt->get_tok_head() ; tok != 0 ; ++ti, tok = tok->get_next()) { /* set the property association */ ti->prop = tok->get_prop_assoc(); /* set the type and type-specific data */ switch (tok->get_type()) { case TCGRAM_UNKNOWN: ti->typ = VMGRAM_MATCH_UNDEF; break; case TCGRAM_PROD: ti->typ = VMGRAM_MATCH_PROD; ti->typinfo.prod_obj = tok->getval_prod()->get_obj_id(); break; case TCGRAM_PART_OF_SPEECH: ti->typ = VMGRAM_MATCH_SPEECH; ti->typinfo.speech_prop = tok->getval_part_of_speech(); break; case TCGRAM_LITERAL: /* set the literal in our token structure */ ti->set_literal( tok->getval_literal_txt(), tok->getval_literal_len()); /* add the token to the dictionary */ if (dict != VM_INVALID_OBJ && G_predef->misc_vocab != VM_INVALID_PROP) { ((CVmObjDict *)vm_objp(vmg_ dict))->add_word( vmg_ dict, ti->typinfo.lit.str, ti->typinfo.lit.len, self, G_predef->misc_vocab); } break; case TCGRAM_TOKEN_TYPE: ti->typ = VMGRAM_MATCH_TOKTYPE; ti->typinfo.toktyp_enum = tok->getval_token_type(); break; case TCGRAM_STAR: ti->typ = VMGRAM_MATCH_STAR; break; case TCGRAM_PART_OF_SPEECH_LIST: ti->set_nspeech(tok->getval_part_list_len()); for (size_t i = 0 ; i < ti->typinfo.nspeech.cnt ; ++i) ti->typinfo.nspeech.props[i] = tok->getval_part_list_ele(i); break; } } /* add it to the object */ insert_alt(self, idx, ai); /* create and add an undo record */ vmgram_undo_rec *rec = new vmgram_undo_rec(VMGRAM_UNDO_ADDED, idx, 0); if (!G_undo->add_new_record_ptr_key(vmg_ self, rec)) delete rec; } /* addAlt() implementation */ int CVmObjGramProd::getp_addAlt(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { const vm_val_t *v; /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(2, 2); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* the first argument is the rule list */ const vm_val_t *alt = G_stk->get(0); const char *altp = alt->get_as_string(vmg0_); if (altp == 0) err_throw(VMERR_STRING_VAL_REQD); /* get the source length and buffer pointer */ size_t altlen = vmb_get_len(altp); altp += VMB_LEN; /* the next argument is the match object class */ vm_obj_id_t match = VM_INVALID_OBJ; v = G_stk->get(1); if (v->typ != VM_OBJ || !CVmObjTads::is_tadsobj_obj(vmg_ match = v->val.obj)) err_throw(VMERR_BAD_TYPE_BIF); /* next comes the optional dictionary argument */ vm_obj_id_t dict = VM_INVALID_OBJ; if (argc >= 3 && (v = G_stk->get(2))->typ != VM_NIL && (v->typ != VM_OBJ || !CVmObjDict::is_dictionary_obj(vmg_ (dict = v->val.obj)))) err_throw(VMERR_INVAL_OBJ_TYPE); /* next comes the optional symbol table */ vm_obj_id_t symtab = VM_INVALID_OBJ; if (argc >= 4 && (v = G_stk->get(3))->typ != VM_NIL) { /* get the value, and make sure it's an object */ symtab = v->val.obj; if (v->typ != VM_OBJ) err_throw(VMERR_OBJ_VAL_REQD); } /* push self for gc protection */ G_interpreter->push_obj(vmg_ self); /* dynamically compile the alt list */ CVmGramCompResults results(self, this, dict, match); CVmDynamicCompiler *comp = CVmDynamicCompiler::get(vmg0_); comp->compile(vmg_ FALSE, symtab, VM_INVALID_OBJ, VM_INVALID_OBJ, alt, altp, altlen, DCModeGramAlt, 0, &results); /* check for errors */ if (results.err != 0) results.throw_error(vmg0_); /* rebuild the grammarAltProps property for the match object */ build_alt_props(vmg_ match); /* discard arguments and gc protection */ G_stk->discard(argc + 1); /* return self */ retval->set_obj(self); /* handled */ return TRUE; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmgram.h��������������������������������������������������������������������������0000664�0000000�0000000�00000043407�12650170723�0015330�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmgram.h - T3 grammar-production metaclass Function Notes Modified 02/15/00 MJRoberts - Creation */ #ifndef VMGRAM_H #define VMGRAM_H #include <stdlib.h> #include <string.h> #include "os.h" #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" /* ------------------------------------------------------------------------ */ /* * intrinsic function vector indices */ enum vmobjgram_meta_fnset { /* undefined function */ VMOBJGRAM_UNDEF = 0, /* parseTokens(tokenList, dict) */ VMOBJGRAM_PARSE = 1 }; /* ------------------------------------------------------------------------ */ /* * Match types */ enum vmgram_match_type { /* uninitialized */ VMGRAM_MATCH_UNDEF = 0, /* production - matches a sub-production */ VMGRAM_MATCH_PROD = 1, /* * part of speech - matches a word that appears in the dictionary * under a particular part of speech */ VMGRAM_MATCH_SPEECH = 2, /* literal - matches a literal string */ VMGRAM_MATCH_LITERAL = 3, /* token type - matches any token of a given type */ VMGRAM_MATCH_TOKTYPE = 4, /* star - matches all remaining input tokens */ VMGRAM_MATCH_STAR = 5, /* * N parts of speech - matches a word that appears in the dictionary * under any of a set of N parts of speech */ VMGRAM_MATCH_NSPEECH = 6 }; /* ------------------------------------------------------------------------ */ /* * Grammar production object - image file format * * UINT2 alt_count *. alternative 1 *. alternative 2 *. etc * * Each alternative has the following structure: * *. INT2 score *. INT2 badness *. UINT4 processor_object_id *. UINT2 token_count *. token 1 *. token 2 *. etc * * Each token has this structure: * * UINT2 property_association *. BYTE token_match_type (see below) *. extra data depending on token_match_type (see below) * * The extra data for the token varies by match type: * * VMGRAM_MATCH_PROD - a UINT4 giving the production object ID * * VMGRAM_MATCH_SPEECH - a UINT2 giving the vocabulary property * * VMGRAM_MATCH_NSPEECH - a UINT2 giving a count, then that many * additional UINT2's giving a list of vocabulary properties * * VMGRAM_MATCH_LITERAL - a UINT2 byte-length prefix followed by the * UTF8-encoded bytes of the literal string * * VMGRAM_MATCH_TOKTYPE - a UINT4 giving the token enum's ID * * VMGRAM_MATCH_STAR - no additional data */ /* property/match result enumeration entry */ struct vmgram_match_info { vm_prop_id_t prop; }; /* * Grammar production object extension */ struct vm_gram_ext { /* pointer to load image data, if any */ const char *image_data_; size_t image_data_size_; /* * The last comparator object we used to calculate hash values for * literals. Each time we need literal hash values, we'll check to see * if we're using the same comparator we were last time; if so, we'll * use the cached hash values, otherwise we'll recalculate them. We * reference this object weakly. */ vm_obj_id_t comparator_; /* flag: our rule list has been modified since load time */ uint modified_ : 1; /* flag: we've cached hash values for our literals */ uint hashes_cached_ : 1; /* * flag: there's at least one circular rule among my rules (i.e., * there's a rule whose first element is a self-reference * subproduction) */ uint has_circular_alt : 1; /* private memory pool - we use this to make allocation cheaper */ class CVmGramProdMem *mem_; /* * Property list enumeration space. We use this to build a list of * properties for which a dictionary word is defined. We'll expand * this list as needed when we find we need more space. */ vmgram_match_info *prop_enum_arr_; size_t prop_enum_max_; /* array of rule alternatives */ struct vmgram_alt_info **alts_; /* number of alternatives currently in use */ size_t alt_cnt_; /* number of alternatives allocated */ size_t alt_alo_; }; /* * Alternative object. Each of these objects represents one of our rule * alternatives. */ struct vmgram_alt_info { vmgram_alt_info(size_t ntoks); ~vmgram_alt_info(); /* the alternative's score and badness values */ int score; int badness; /* flag: this alternative is marked for deletion */ int del; /* * the "processor object" for this alternative - this is the class we * instantiate to represent a match to the alternative */ vm_obj_id_t proc_obj; /* array of token elements in the alternative */ struct vmgram_tok_info *toks; size_t tok_cnt; }; /* * Grammar rule token entry. This represents a token in a grammar rule. */ struct vmgram_tok_info { vmgram_tok_info() { typ = VMGRAM_MATCH_UNDEF; } ~vmgram_tok_info() { switch (typ) { case VMGRAM_MATCH_LITERAL: t3free(typinfo.lit.str); break; case VMGRAM_MATCH_NSPEECH: t3free(typinfo.nspeech.props); break; } } void set_literal(const char *str, size_t len) { set_literal(len); memcpy(typinfo.lit.str, str, len); } void set_literal(size_t len) { typ = VMGRAM_MATCH_LITERAL; typinfo.lit.len = len; typinfo.lit.hash = 0; typinfo.lit.str = (char *)t3malloc(len); } void set_nspeech(size_t cnt) { typ = VMGRAM_MATCH_NSPEECH; typinfo.nspeech.cnt = cnt; typinfo.nspeech.props = (vm_prop_id_t *)t3malloc( cnt * sizeof(vm_prop_id_t)); } /* * property association - this is the property of the processor object * that we'll set to point to the match object or input token if we * match this rule token */ vm_prop_id_t prop; /* token type - this is a VMGRAM_MATCH_xxx value */ uchar typ; /* extra data, depending on 'typ' */ union { /* VMGRAM_MATCH_PROD - the sub-production object */ vm_obj_id_t prod_obj; /* VMGRAM_MATCH_SPEECH - the part-of-speech property */ vm_prop_id_t speech_prop; /* VMGRAM_MATCH_NSPEECH - an array of part-of-speech proeprties */ struct { size_t cnt; vm_prop_id_t *props; } nspeech; /* VMGRAM_MATCH_LITERAL - the literal string to match */ struct { /* the literal text and its length */ char *str; size_t len; /* the cached hash value for the literal */ uint hash; } lit; /* VMGRAM_MATCH_TOKTYPE - token type enum */ uint32_t toktyp_enum; } typinfo; }; /* ------------------------------------------------------------------------ */ /* * Grammar-Production object interface */ class CVmObjGramProd: public CVmObject { friend class CVmMetaclassGramProd; friend struct CVmGramCompResults; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* determine if an object is a GrammarProduction object */ static int is_gramprod_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* receive notification of a new undo savepoint */ void notify_new_savept() { } /* apply undo */ void apply_undo(VMG_ struct CVmUndoRecord *rec); /* discard additional information associated with an undo record */ void discard_undo(VMG_ struct CVmUndoRecord *rec); /* mark a reference in an undo record */ void mark_undo_ref(VMG_ struct CVmUndoRecord *rec); /* remove stale weak references from an undo record */ void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* mark object references */ void mark_refs(VMG_ uint state); /* remove weak references */ void remove_stale_weak_refs(VMG0_); /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* restart/save/restore */ void reset_to_image(VMG_ vm_obj_id_t self); void save_to_file(VMG_ class CVmFile *fp); void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* determine if the object has been changed since it was loaded */ int is_changed_since_load() const { return FALSE; } /* * rebuild for image file - we can't change during execution, so our * image file data never change */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *, vm_obj_id_t) { } protected: /* private constructor */ CVmObjGramProd(VMG0_); /* clear the alternative list */ void clear_alts(); /* ensure that there's space for the given number of alternatives */ void ensure_alts(size_t cnt, size_t margin); /* insert/delete an alternative, without keeping undo information */ void insert_alt(vm_obj_id_t self, size_t idx, vmgram_alt_info *alt); void delete_alt(size_t idx); /* check the alternative list for circular references */ void check_circular_refs(vm_obj_id_t self); /* build the grammarAltProps list for a match object */ void build_alt_props(VMG_ vm_obj_id_t match_obj); size_t build_alt_props_list(VMG_ vm_obj_id_t match_obj, class CVmObjList *lst); /* add an alternative from a parsed rule, keeping undo */ void add_alt(VMG_ vm_obj_id_t self, class CTcGramProdAlt *alt, vm_obj_id_t dict, vm_obj_id_t match); /* delete an alternative, keeping undo */ void delete_alt_undo(VMG_ vm_obj_id_t self, int idx); /* delete a batch of alternatives - deletes each item marked with 'del' */ void delete_marked_alts(VMG_ vm_obj_id_t self, vm_obj_id_t dict); /* find a literal in the surviving alternatives */ int find_literal_in_alts(const char *str, size_t len); /* save to a data stream */ void save_to_stream(VMG_ class CVmStream *dst); /* mark references in an alt list (for garbage collection tracing) */ void mark_alt_refs(VMG_ const vmgram_alt_info *alt, uint state); /* load alternatives from a stream, in image file format */ void load_alts(VMG_ vm_obj_id_t self, class CVmStream *src, class CVmObjFixup *fixups); /* property evaluation - undefined property */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluation - parseTokens */ int getp_parse(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluation - getGrammarInfo */ int getp_get_gram_info(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - delete an alternative */ int getp_deleteAlt(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - delete all alternatives */ int getp_clearAlts(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - add an alternative */ int getp_addAlt(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* get my extension, properly cast */ vm_gram_ext *get_ext() const { return (vm_gram_ext *)ext_; } /* callback for dictionary word property enumeration */ static void enum_props_cb(VMG_ void *ctx, vm_prop_id_t prop, const vm_val_t *match_val); /* search a token for a match to the given vocabulary property */ static int find_prop_in_tok(const struct vmgramprod_tok *tok, vm_prop_id_t prop); /* enqueue our alternatives */ void enqueue_alts(VMG_ class CVmGramProdMem *mem, const struct vmgramprod_tok *tok, size_t tok_cnt, size_t start_tok_pos, struct CVmGramProdState *state, struct CVmGramProdQueue *queues, vm_obj_id_t self, int circ_only, struct CVmGramProdMatch *circ_match, class CVmObjDict *dict); /* create and enqueue a new state */ static struct CVmGramProdState * enqueue_new_state(class CVmGramProdMem *mem, size_t start_tok_pos, struct CVmGramProdState *enclosing_state, const vmgram_alt_info *altp, vm_obj_id_t self, int *need_to_clone, struct CVmGramProdQueue *queues, int circular_alt); /* create a new state */ static struct CVmGramProdState * create_new_state(class CVmGramProdMem *mem, size_t start_tok_pos, struct CVmGramProdState *enclosing_state, const vmgram_alt_info *altp, vm_obj_id_t self, int *need_to_clone, int circular_alt); /* enqueue a state */ static void enqueue_state(struct CVmGramProdState *state, struct CVmGramProdQueue *queues); /* process the work queue */ static void process_work_queue(VMG_ CVmGramProdMem *mem, const struct vmgramprod_tok *tok, size_t tok_cnt, struct CVmGramProdQueue *queues, class CVmObjDict *dict); /* process the first work queue entry */ static void process_work_queue_head(VMG_ CVmGramProdMem *mem, const struct vmgramprod_tok *tok, size_t tok_cnt, struct CVmGramProdQueue *queues, class CVmObjDict *dict); /* build a match tree */ static void build_match_tree(VMG_ const struct CVmGramProdMatch *match, const vm_val_t *tok_list, const vm_val_t *tok_match_list, vm_val_t *retval, size_t *first_tok, size_t *last_tok); /* cache the hash values for the literal tokens in our alternatives */ void cache_hashes(VMG_ CVmObjDict *dict); /* calculate the hash value for a literal string */ static unsigned int calc_str_hash(VMG_ class CVmObjDict *dict, const vm_val_t *strval, const char *str, size_t len); /* check to see if a token matches a literal */ static int tok_equals_lit(VMG_ const struct vmgramprod_tok *tok, const char *lit, size_t lit_len, class CVmObjDict *dict, vm_val_t *match_result); /* property evaluation function table */ static int (CVmObjGramProd::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassGramProd: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "grammar-production/030002"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjGramProd(vmg0_); G_obj_table->set_obj_gc_characteristics(id, TRUE, TRUE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjGramProd(vmg0_); G_obj_table->set_obj_gc_characteristics(id, TRUE, TRUE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjGramProd::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjGramProd:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMGRAM_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjGramProd) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmhash.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000037405�12650170723�0015661�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/vmhash.cpp,v 1.3 1999/07/11 00:46:59 MJRoberts Exp $"; #endif /* * Copyright (c) 1997, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmhash.cpp - hash table implementation Function Notes Modified 10/25/97 MJRoberts - Creation */ #include <assert.h> #include <memory.h> #include <string.h> #ifndef STD_H #include "t3std.h" #endif #ifndef VMHASH_H #include "vmhash.h" #endif /* ------------------------------------------------------------------------ */ /* * Simple case-insensitive hash function implementation. */ unsigned int CVmHashFuncCI::compute_hash(const char *s, size_t l) const { uint acc; /* * Add up all the character values in the string, converting all * characters to upper-case. */ for (acc = 0 ; l != 0 ; ++s, --l) { uchar c; c = (uchar)(is_lower(*s) ? to_upper(*s) : *s); acc += c; } /* return the accumulated value */ return acc; } /* ------------------------------------------------------------------------ */ /* * Simple case-sensitive hash function implementation */ unsigned int CVmHashFuncCS::compute_hash(const char *s, size_t l) const { uint acc; /* * add up all the character values in the string, treating case as * significant */ for (acc = 0 ; l != 0 ; ++s, --l) { uchar c; c = (uchar)*s; acc += c; } /* return the accumulated value */ return acc; } /* ------------------------------------------------------------------------ */ /* * Hash table symbol entry. This is an abstract class; subclasses must * provide a symbol-matching method. */ CVmHashEntry::CVmHashEntry(const char *str, size_t len, int copy) { /* not linked into a list yet */ nxt_ = 0; /* see if we can use the original string or need to make a private copy */ if (copy) { char *buf; /* allocate space for a copy */ buf = new char[len]; /* copy it into our buffer */ memcpy(buf, str, len * sizeof(*buf)); /* remember it */ str_ = buf; } else { /* we can use the original */ str_ = str; } /* remember the length */ len_ = len; /* remember whether or not we own the string */ copy_ = copy; } CVmHashEntry::~CVmHashEntry() { /* if we made a private copy of the string, we own it, so delete it */ if (copy_) delete [] (char *)str_; } /* ------------------------------------------------------------------------ */ /* * Concrete subclass of CVmHashEntry providing a case-insensitive * symbol match implementation */ int CVmHashEntryCI::matches(const char *str, size_t len) const { /* * it's a match if the strings are the same length and all * characters match, ignoring case */ return (len == len_ && memicmp(str, str_, len * sizeof(*str)) == 0); } /* ------------------------------------------------------------------------ */ /* * Concrete subclass of CVmHashEntry providing a case-sensitive symbol * match implementation */ int CVmHashEntryCS::matches(const char *str, size_t len) const { /* * it's a match if the strings are the same length and all * characters match, treating case as significant */ return (len == len_ && memcmp(str, str_, len * sizeof(*str)) == 0); } /* ------------------------------------------------------------------------ */ /* * Hash table */ void CVmHashTable::init(int hash_table_size, CVmHashFunc *hash_function, int own_hash_func, CVmHashEntry **hash_array) { CVmHashEntry **entry; size_t i; /* make sure it's a power of two */ assert(is_power_of_two(hash_table_size)); /* make sure we got a hash function */ assert(hash_function != 0); /* save the hash function */ hash_function_ = hash_function; own_hash_func_ = own_hash_func; /* check to see if the caller provided the hash array */ if (hash_array != 0) { /* the caller allocated the table - store a reference to it */ table_ = hash_array; table_size_ = hash_table_size; /* note that the table belongs to the caller, so we don't delete it */ own_hash_table_ = FALSE; } else { /* allocate the table */ table_ = new CVmHashEntry *[hash_table_size]; table_size_ = hash_table_size; /* note that we own the table and must delete it */ own_hash_table_ = TRUE; } /* clear the table */ for (entry = table_, i = 0 ; i < table_size_ ; ++i, ++entry) *entry = 0; } CVmHashTable::~CVmHashTable() { /* delete the hash function object if I own it */ if (own_hash_func_) delete hash_function_; /* delete each entry in the hash table */ delete_all_entries(); /* delete the hash table */ if (own_hash_table_) delete [] table_; } /* * delete all entries in the hash table, but keep the table itself */ void CVmHashTable::delete_all_entries() { CVmHashEntry **tableptr; size_t i; for (tableptr = table_, i = 0 ; i < table_size_ ; ++i, ++tableptr) { CVmHashEntry *entry; CVmHashEntry *nxt; /* delete each entry in the list at this element */ for (entry = *tableptr ; entry ; entry = nxt) { /* remember the next entry */ nxt = entry->nxt_; /* delete this entry */ delete entry; } /* there's nothing at this table entry now */ *tableptr = 0; } } /* * Verify that a value is a power of two. Hash table sizes must be * powers of two. */ int CVmHashTable::is_power_of_two(int n) { /* divide by two until we have an odd number */ while ((n & 1) == 0) n >>= 1; /* make sure the result is 1 */ return (n == 1); } /* * Compute the hash value for an entry */ unsigned int CVmHashTable::compute_hash(CVmHashEntry *entry) const { return compute_hash(entry->getstr(), entry->getlen()); } /* * Compute the hash value for a string */ unsigned int CVmHashTable::compute_hash(const char *str, size_t len) const { return adjust_hash(hash_function_->compute_hash(str, len)); } /* * Add an object to the table */ void CVmHashTable::add(CVmHashEntry *entry) { unsigned int hash; /* compute the hash value for this entry */ hash = compute_hash(entry); /* link it into the slot for this hash value */ entry->nxt_ = table_[hash]; table_[hash] = entry; } /* * Remove an object */ void CVmHashTable::remove(CVmHashEntry *entry) { unsigned int hash; /* compute the hash value for this entry */ hash = compute_hash(entry); /* * if it's the first item in the chain, advance the head over it; * otherwise, we'll need to find the previous item to unlink it */ if (table_[hash] == entry) { /* it's the first item - simply advance the head to the next item */ table_[hash] = entry->nxt_; } else { CVmHashEntry *prv; /* find the previous item in the list for this hash value */ for (prv = table_[hash] ; prv != 0 && prv->nxt_ != entry ; prv = prv->nxt_) ; /* if we found it, unlink this item */ if (prv != 0) prv->nxt_ = entry->nxt_; } } /* * Find an object in the table matching a given string. */ CVmHashEntry *CVmHashTable::find(const char *str, size_t len) const { unsigned int hash; CVmHashEntry *entry; /* compute the hash value for this entry */ hash = compute_hash(str, len); /* scan the list at this hash value looking for a match */ for (entry = table_[hash] ; entry ; entry = entry->nxt_) { /* if this one matches, return it */ if (entry->matches(str, len)) return entry; } /* didn't find anything */ return 0; } /* * Enumerate hash matches for a given string */ void CVmHashTable::enum_hash_matches(const char *str, size_t len, void (*cb)(void *cbctx, CVmHashEntry *entry), void *cbctx) { unsigned int hash; /* compute the hash value for this entry */ hash = compute_hash(str, len); /* enumerate matches at the hash value */ enum_hash_matches(hash, cb, cbctx); } /* * Enumerate hash matches for a given hash code */ void CVmHashTable::enum_hash_matches(unsigned int hash, void (*cb)(void *cbctx, CVmHashEntry *entry), void *cbctx) { CVmHashEntry *entry; /* adjust the hash value for the table size */ hash = adjust_hash(hash); /* enumerate the complete list of entries at this hash value */ for (entry = table_[hash] ; entry ; entry = entry->nxt_) { /* call the callback with this entry */ (*cb)(cbctx, entry); } } /* * Find an object in the table matching a given leading substring. * We'll return the longest-named entry that matches a leading substring * of the given string. For example, if there's are entires A, AB, ABC, * and ABCE, and this routine is called to find something matching * ABCDEFGH, we'll return ABC as the match (not ABCE, since it doesn't * match any leading substring of the given string, and not A or AB, * even though they match, since ABC also matches and it's longer). */ CVmHashEntry *CVmHashTable::find_leading_substr(const char *str, size_t len) { size_t sublen; CVmHashEntry *entry; /* * try to find each leading substring, starting with the longest, * decreasing by one character on each iteration, until we've used * the whole string */ for (sublen = len ; sublen > 0 ; --sublen) { /* if this substring matches, use it */ if ((entry = find(str, sublen)) != 0) return entry; } /* we didn't find it */ return 0; } /* * Enumerate all entries */ void CVmHashTable::enum_entries(void (*func)(void *, CVmHashEntry *), void *ctx) { CVmHashEntry **tableptr; size_t i; /* go through each hash value */ for (tableptr = table_, i = 0 ; i < table_size_ ; ++i, ++tableptr) { CVmHashEntry *entry; CVmHashEntry *nxt; /* go through each entry at this hash value */ for (entry = *tableptr ; entry ; entry = nxt) { /* * remember the next entry, in case the callback deletes the * current entry */ nxt = entry->nxt_; /* invoke the callback on this entry */ (*func)(ctx, entry); } } } /* * Enumerate all entries - ultra-safe version. This version should be * used when the callback code might delete arbitrary entries from the * hash table. This version is slower than the standard enum_entries, but * will tolerate any changes to the table made in the callback. */ void CVmHashTable::safe_enum_entries(void (*func)(void *, CVmHashEntry *), void *ctx) { CVmHashEntry **tableptr; size_t i; /* go through each hash value */ for (tableptr = table_, i = 0 ; i < table_size_ ; ++i, ++tableptr) { size_t list_idx; /* * start at the first (0th) entry in the current hash chain, and * keep going until we run out of entries in the chain */ for (list_idx = 0 ; ; ++list_idx) { CVmHashEntry *entry; size_t j; /* * Scan the hash chain for the current entry index. * * This is the part that makes this version slower than the * standard version and safer than the standard version. It's * slower than enum_entries() because we must scan the chain * list on every iteration to find the next entry, whereas * enum_entries() simply keeps a pointer to the next entry. * It's safer because we don't keep any pointers - if next * element is deleted in the callback in enum_entries(), that * stored next pointer would be invalid, but we store no * pointers that could become stale. */ for (j = 0, entry = *tableptr ; j < list_idx && entry != 0 ; entry = entry->nxt_, ++j) ; /* if we failed to find the entry, we're done with this chain */ if (entry == 0) break; /* invoke the callback on this entry */ (*func)(ctx, entry); } } } /* * Move all entries in this table to a new table */ void CVmHashTable::move_entries_to(CVmHashTable *new_tab) { CVmHashEntry **tableptr; size_t i; /* go through each hash value */ for (tableptr = table_, i = 0 ; i < table_size_ ; ++i, ++tableptr) { CVmHashEntry *entry; CVmHashEntry *nxt; /* go through each entry at this hash value */ for (entry = *tableptr ; entry ; entry = nxt) { /* * remember the next entry, since we'll be unlinking it from * this table, which will render the nxt_ member unusable * for the purposes of completing this enumeration */ nxt = entry->nxt_; /* * clear the 'next' pointer in this entry, to unlink it from * our table - since everything is being removed, there's no * need to worry about what came before us */ entry->nxt_ = 0; /* add the entry to the new hash table */ new_tab->add(entry); } /* * clear this hash value chain head - we've now removed * everything from it */ *tableptr = 0; } } /* ------------------------------------------------------------------------ */ /* * Debugging Functions */ #ifdef T3_DEBUG /* * dump information the hash table to stderr */ void CVmHashTable::debug_dump() const { long total; long longest; long avg; int over_avg; int empty; size_t i; /* gather statistics on the hash table */ for (total = longest = 0, empty = 0, i = 0 ; i < table_size_ ; ++i) { CVmHashEntry *cur; int curlen; /* scan this chain */ for (curlen = 0, cur = table_[i] ; cur != 0 ; cur = cur->nxt_) { ++curlen; ++total; } /* if it's empty, so note */ if (curlen == 0) ++empty; /* if it's longer than the longest, so note */ if (curlen > longest) longest = curlen; } /* calculate the average length */ avg = total/table_size_; /* count chains over average length */ for (over_avg = 0, i = 0 ; i < table_size_ ; ++i) { CVmHashEntry *cur; int curlen; /* scan this chain */ for (curlen = 0, cur = table_[i] ; cur != 0 ; cur = cur->nxt_) ++curlen; /* if it's over average length, note it */ if (curlen > avg) ++over_avg; } /* display the statistics */ fprintf(stderr, "hash table: total %ld, longest %ld, average %ld\n" "number of buckets over average length: %d\n" "number of empty buckets: %d\n", total, longest, avg, over_avg, empty); } #else /* T3_DEBUG */ /* dummy functions for release builds */ void CVmHashTable::debug_dump() const { } #endif /* T3_DEBUG */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmhash.h��������������������������������������������������������������������������0000664�0000000�0000000�00000020157�12650170723�0015322�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/vmhash.h,v 1.3 1999/07/11 00:46:59 MJRoberts Exp $ */ /* * Copyright (c) 1997, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmhash.h - hash table implementation Function Notes Modified 10/25/97 MJRoberts - Creation */ #ifndef VMHASH_H #define VMHASH_H #ifndef STD_H #include "t3std.h" #endif /* ------------------------------------------------------------------------ */ /* * Hash function interface class. Hash table clients must implement an * appropriate hash function to use with the hash table; this abstract * class provides the necessary interface. */ class CVmHashFunc { public: virtual ~CVmHashFunc() { } virtual unsigned int compute_hash(const char *str, size_t len) const = 0; }; /* ------------------------------------------------------------------------ */ /* * Hash table symbol entry. This is an abstract class; subclasses must * provide a symbol-matching method. */ class CVmHashEntry { public: /* * Construct the hash entry. 'copy' indicates whether we should * make a private copy of the value; if not, the caller must keep * the original string around as long as this hash entry is around. * If 'copy' is true, we'll make a private copy of the string * immediately, so the caller need not keep it around after * constructing the entry. */ CVmHashEntry(const char *str, size_t len, int copy); virtual ~CVmHashEntry(); /* determine if this entry matches a given string */ virtual int matches(const char *str, size_t len) const = 0; /* list link */ CVmHashEntry *nxt_; /* get the string pointer and length */ const char *getstr() const { return str_; } size_t getlen() const { return len_; } protected: const char *str_; size_t len_; int copy_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Hash table */ class CVmHashTable { public: /* * Construct a hash table. If own_hash_func is true, the hash table * object takes ownership of the hash function object, so the hash * table object will delete the hash function object when the table * is deleted. */ CVmHashTable(int hash_table_size, CVmHashFunc *hash_function, int own_hash_func) { /* initialize, allocating our own hash array */ init(hash_table_size, hash_function, own_hash_func, 0); } /* * Construct a hash table, using memory allocated and owned by the * caller for the hash array. */ CVmHashTable(int hash_table_size, CVmHashFunc *hash_function, int own_hash_func, CVmHashEntry **hash_array) { /* initialize, using the provided hash array */ init(hash_table_size, hash_function, own_hash_func, hash_array); } /* delete the hash table */ ~CVmHashTable(); /* * Add a symbol. If 'copy' is true, it means that we need to make * a private copy of the string; otherwise, the caller must ensure * that the string remains valid as long as the hash table entry * remains valid, since we'll just store a pointer to the original * string. IMPORTANT: the hash table takes over ownership of the * hash table entry; the hash table will delete this object when the * hash table is deleted, so the client must not delete the entry * once it's been added to the table. */ void add(CVmHashEntry *entry); /* * Remove an object from the cache. This routine does not delete * the object. */ void remove(CVmHashEntry *entry); /* * Delete all entries in the table */ void delete_all_entries(); /* * Find an entry in the table matching the given string */ CVmHashEntry *find(const char *str, size_t len) const; /* enumerate all entries that match the given string's hash value */ void enum_hash_matches(const char *str, size_t len, void (*cb)(void *cbctx, CVmHashEntry *entry), void *cbctx); /* enumerate all entries with the given hash value */ void enum_hash_matches(uint hash, void (*cb)(void *cbctx, CVmHashEntry *entry), void *cbctx); /* * Find an entry that matches the longest leading substring of the * given string. (For this routine, we find a match where the * dictionary word is SHORTER than the given word.) */ CVmHashEntry *find_leading_substr(const char *str, size_t len); /* * Enumerate all entries, invoking a callback for each entry in the * table */ void enum_entries(void (*func)(void *ctx, class CVmHashEntry *entry), void *ctx); /* * Enumerate all entries safely. This does the same thing as * enum_entries(), but this version is safe to use regardless of any * changes to the table that the callback makes. */ void safe_enum_entries(void (*func)(void *ctx, class CVmHashEntry *entry), void *ctx); /* * Move all of the entries in this hash table to a new hash table. * After this is finished, this hash table will be empty, and the new * hash table will be populated with all of the entries that were * formerly in this table. We don't reallocate any of the entries - * they simply are unlinked from this table and moved into the new * table. This can be used to rebuild a hash table with a new bucket * count or hash function. */ void move_entries_to(CVmHashTable *new_tab); /* dump information on the hash table to stderr for debugging */ void debug_dump() const; /* compute the hash value for an entry/a string */ unsigned int compute_hash(CVmHashEntry *entry) const; unsigned int compute_hash(const char *str, size_t len) const; private: /* adjust a hash to the table size */ unsigned int adjust_hash(unsigned int hash) const { return hash & (table_size_ - 1); } /* initialize */ void init(int hash_table_size, CVmHashFunc *hash_function, int own_hash_func, CVmHashEntry **hash_array); /* internal service routine for checking hash table sizes for validity */ int is_power_of_two(int n); /* the table of hash entries */ CVmHashEntry **table_; size_t table_size_; /* hash function */ CVmHashFunc *hash_function_; /* flag: I own the hash function and must delete it when done */ unsigned int own_hash_func_ : 1; /* flag: I own the hash table array and must delete it when done */ unsigned int own_hash_table_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Simple case-insensitive hash function */ class CVmHashFuncCI: public CVmHashFunc { public: unsigned int compute_hash(const char *str, size_t len) const; }; /* ------------------------------------------------------------------------ */ /* * Simple case-sensitive hash function implementation */ class CVmHashFuncCS: public CVmHashFunc { public: unsigned int compute_hash(const char *str, size_t len) const; }; /* ------------------------------------------------------------------------ */ /* * Concrete subclass of CVmHashEntry providing a case-insensitive * symbol match implementation */ class CVmHashEntryCI: public CVmHashEntry { public: CVmHashEntryCI(const char *str, size_t len, int copy) : CVmHashEntry(str, len, copy) { } virtual int matches(const char *str, size_t len) const; }; /* ------------------------------------------------------------------------ */ /* * Concrete subclass of CVmHashEntry providing a case-sensitive symbol * match implementation */ class CVmHashEntryCS: public CVmHashEntry { public: CVmHashEntryCS(const char *str, size_t len, int copy) : CVmHashEntry(str, len, copy) { } virtual int matches(const char *str, size_t len) const; }; #endif /* VMHASH_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmhost.h��������������������������������������������������������������������������0000664�0000000�0000000�00000024041�12650170723�0015350�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmhost.h - T3 VM host interface Function The host interface defines a set of services that the VM obtains from its host application environment. The VM uses the host interface to obtain these services so that the VM doesn't have to make too many assumptions about the larger application of which it is a subsystem. Every application containing the VM must provide the VM with an implementation of this interface. Notes Modified 07/29/99 MJRoberts - Creation */ #ifndef VMHOST_H #define VMHOST_H #include "os.h" /* ------------------------------------------------------------------------ */ /* * I/O Safety Levels. These are defined as integers, not an enum, because * they form a hierarchy; a higher value imposes all of the restrictions of * all lower values, plus additional restrictions of its own. * * In the past, there was a single safety level that controlled both read * and write. In 3.1 we separated the read and write levels. Because * of the history, each level specifies separate read and write permissions * that were formerly combined in that level. The descriptions still apply * even though we track the levels separately - some levels must be * interpreted differently for reading vs writing. For example, if the * read level is 3, it allows current-directory reading, but if the write * level is 3, it denies all write access. Separating the levels allows * for combinations that weren't formerly possible: for exmaple, -s04 (read * 0, write 4) allows reading anywhere but writing nowhere. */ /* level 0: minimum safety; read/write any file */ const int VM_IO_SAFETY_MINIMUM = 0; /* level 1: read any file, write only to files in the current directory */ const int VM_IO_SAFETY_READ_ANY_WRITE_CUR = 1; /* level 2: read/write in current directory only */ const int VM_IO_SAFETY_READWRITE_CUR = 2; /* level 3: read from current directory only, no writing allowed */ const int VM_IO_SAFETY_READ_CUR = 3; /* level 4: maximum safety; no file reading or writing allowed */ const int VM_IO_SAFETY_MAXIMUM = 4; /* ------------------------------------------------------------------------ */ /* * Network safety levels. */ /* level 0: minimum safety; all network access */ const int VM_NET_SAFETY_MINIMUM = 0; /* level 1: localhost access only */ const int VM_NET_SAFETY_LOCALHOST = 1; /* level 2: no network access */ const int VM_NET_SAFETY_MAXIMUM = 2; /* ------------------------------------------------------------------------ */ /* * get_image_name return codes */ enum vmhost_gin_t { /* * get_image_name() call ignored - this is returned if the host * system doesn't have a way of asking the user for an image name, * so the caller must rely on whatever other way it has of finding * the image name (which usually means that it can't find a image * name at all, given that it would only call get_image_name() when * it can't otherwise find the name) */ VMHOST_GIN_IGNORED, /* * user chose not to supply an image name - this is returned when, * for example, the user cancelled out of a file selector dialog */ VMHOST_GIN_CANCEL, /* * error - the host system can't prompt for a filename because of * some kind of error (not enough memory to load a dialog, for * example) */ VMHOST_GIN_ERROR, /* success */ VMHOST_GIN_SUCCESS }; /* ------------------------------------------------------------------------ */ /* * T3 VM Host Application Interface. This is an abstract class; it must * be implemented by each application that embeds the VM. */ class CVmHostIfc { public: virtual ~CVmHostIfc() { } /* * Get the file I/O safety read and write levels. These allow the host * application to control the file operations that a program running * under the VM may perform. See the VM_IO_SAFETY_xxx values above. */ virtual int get_io_safety_read() = 0; virtual int get_io_safety_write() = 0; /* * set the I/O safety level - this should only be done in response * to a user preference setting (such as via a command-line option), * never as a result of some programmatic operation by the executing * image */ virtual void set_io_safety(int read_level, int write_level) = 0; /* * Get the network safety level settings. This works like the I/O * safety level, but applies to network access. The client level * controls the game's access to network services as a client, such as * the game making http requests of a web server. The server level * controls the game's ability to create network services that accept * incoming connections from other processes and machines. */ virtual void get_net_safety(int *client_level, int *server_level) = 0; /* * Set the network safety level. */ virtual void set_net_safety(int client_level, int server_level) = 0; /* * Get the resource loader for system resources (character mapping * tables, the timezone database, etc). This resource loader should be * set up to load resources out of the VM executable or out of the * directory containing the VM executable, since these resources are * associated with the VM itself, not the T3 program executing under * the VM. */ virtual class CResLoader *get_sys_res_loader() = 0; /* * Set the image file name. The VM calls this after it learns the * name of the image file so that the host system can access the * file if necessary. */ virtual void set_image_name(const char *fname) = 0; /* * Set the root directory path for individual resources (such as * individual image and sound resources) that we don't find in the * image file or any attached resource collection file. If this is * never called, the directory containing the image file should be used * as the resource root directory. */ virtual void set_res_dir(const char *fname) = 0; /* * Add a resource collection file. The return value is a non-zero file * number assigned by the host system; the VM uses this number in * subsequent calls to add_resource() to add resources from this file. * The VM cannot add any resources for a file until it first adds the * file with this routine. */ virtual int add_resfile(const char *fname) = 0; /* * Determine if additional resource files are supported - if this * returns true, add_resfile() can be used, otherwise add_resfile() * will have no effect. */ virtual int can_add_resfiles() = 0; /* * Add a resource map entry. 'ofs' is the byte offset of the start * of the resource within the file, and 'siz' is the size in bytes * of the resource data. The resource is stored as contiguous bytes * starting at the given file offset and running for the given size. * The 'fileno' is zero for the image file, or the number assigned * by the host system in add_resfile() for other resource files. */ virtual void add_resource(unsigned long ofs, unsigned long siz, const char *res_name, size_t res_name_len, int fileno) = 0; /* * Add a resource link map entry. This creates an association between * a compiled resource name and a filename in the local file system. * This is used primarily for debugging purposes, so that the compiler * can avoid copying the resource data, instead simply placing a link * for each resource to its local file copy. */ virtual void add_resource(const char *fname, size_t fnamelen, const char *res_name, size_t res_name_len) = 0; /* * Find a resource. Returns an osfildef* handle to the open resource * file if the resource can be found; the file on return will have its * seek position set to the first byte of the resource in the file, * and *res_size will be set to the size in bytes of the resource * data. If there is no such resource, returns null. */ virtual osfildef *find_resource(const char *resname, size_t resname_len, unsigned long *res_size) = 0; /* * Get the external resource file path. If we should look for * resource files in a different location than the image file, the * host system can set this to a path that we should use to look for * resource files. If this is null, the VM should simply look in * the same directory that contains the image file. If the host * system provides a path via this routine, the path string must * have a trailing separator character if required, so that we can * directly append a name to this path to form a valid * fully-qualified filename. */ virtual const char *get_res_path() = 0; /* * Determine if a resource exists. Returns true if so, false if * not. */ virtual int resfile_exists(const char *res_name, size_t res_name_len) = 0; /* * Ask the user to supply an image file name. We'll call this * routine only if we can't obtain an image file from command line * arguments or from an attachment to the executable. */ virtual vmhost_gin_t get_image_name(char *buf, size_t buflen) = 0; /* * Get a special system filename path. This is essentially a wrapper * for os_get_special_path() that encapsulates the information needed * for that function. The 'id' value has the same meaning as in * os_get_special_path() (see tads2/osifc.h). */ virtual void get_special_file_path(char *buf, size_t buflen, int id) = 0; }; #endif /* VMHOST_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmhostsi.h������������������������������������������������������������������������0000664�0000000�0000000�00000005163�12650170723�0015710�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmhostsi.h - simple stdio-based VM host application environment Function Provides a simple implementation of the VM host application interface. This implementation is suitable for simple command-line tools with minimal user interface; more complete implementations should be used for most applications that embed the VM. Notes Modified 07/29/99 MJRoberts - Creation */ #ifndef VMHOSTSI_H #define VMHOSTSI_H #include "vmhost.h" #include "vmhosttx.h" class CVmHostIfcStdio: public CVmHostIfcText { public: /* create */ CVmHostIfcStdio(const char *argv0); /* delete */ virtual ~CVmHostIfcStdio(); /* get the I/O safety level */ virtual int get_io_safety_read() { return io_safety_read_; } virtual int get_io_safety_write() { return io_safety_write_; } /* set I/O safety level */ virtual void set_io_safety(int read_level, int write_level) { io_safety_read_ = read_level; io_safety_write_ = write_level; } /* get the network safety level */ virtual void get_net_safety(int *client_level, int *server_level) { *client_level = net_client_safety_; *server_level = net_server_safety_; } /* set the network safety level */ virtual void set_net_safety(int client_level, int server_level) { net_client_safety_ = client_level; net_server_safety_ = server_level; } /* get the system resource loader */ virtual class CResLoader *get_sys_res_loader() { return sys_res_loader_; } /* get the resource path */ virtual const char *get_res_path() { return 0; } /* get an image file name */ virtual vmhost_gin_t get_image_name(char *, size_t) { return VMHOST_GIN_IGNORED; } /* get a special file system path */ virtual void get_special_file_path(char *buf, size_t buflen, int id) { os_get_special_path(buf, buflen, argv0_, id); } protected: /* * the original main program's argv[0] - we need to remember this * because it's sometimes needed to resolve special file system paths * on the local system */ char *argv0_; /* system resource loader (character maps, etc) */ class CResLoader *sys_res_loader_; /* current I/O safety levels */ int io_safety_read_; int io_safety_write_; /* current network safety levels */ int net_client_safety_; int net_server_safety_; }; #endif /* VMHOSTSI_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmhosttx.h������������������������������������������������������������������������0000664�0000000�0000000�00000004254�12650170723�0015730�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmhosttx.h - host interface base class for text-only implementations Function Provides a base class for text-only T3 VM Host Interface implementations. In particular, this class provides a simple resource manager implementation, which is usually needed in text-only interpreter systems because there is no larger host application (such as HTML TADS) that provides its own resource manager. Our resource manager implementation simply stores the names of the embedded resources in a hash table. Notes Modified 07/29/99 MJRoberts - Creation */ #ifndef VMHOSTTX_H #define VMHOSTTX_H #include "vmhost.h" class CVmHostIfcText: public CVmHostIfc { public: CVmHostIfcText(); ~CVmHostIfcText(); /* set the image file name */ virtual void set_image_name(const char *fname); /* set the resource directory */ virtual void set_res_dir(const char *dir); /* add a resource file */ virtual int add_resfile(const char *fname); /* we do support external resource files */ virtual int can_add_resfiles() { return TRUE; } /* add a resource */ virtual void add_resource(unsigned long ofs, unsigned long siz, const char *resname, size_t resnamelen, int fileno); /* add a resource file link */ virtual void add_resource(const char *fname, size_t fnamelen, const char *resname, size_t resnamelen); /* find a resource */ virtual osfildef *find_resource(const char *resname, size_t resname_len, unsigned long *res_size); /* determine if a resource exists */ virtual int resfile_exists(const char *resname, size_t resnamelen); protected: /* resource map hash table */ class CVmHashTable *restab_; /* array of external resource bundle filenames */ char **ext_; size_t ext_cnt_; size_t ext_max_; /* resource root directory */ char *res_dir_; }; #endif /* VMHOSTTX_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmhttpreq.h�����������������������������������������������������������������������0000664�0000000�0000000�00000026352�12650170723�0016071�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 2010 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmhttpreq.h - CVmObjHTTPRequest object Function CVmObjHTTPRequest is the TADS intrinsic class wrapper for our native (C++) object TadsHttpRequest. The native object is part of the vmnet layer, which uses reference counting to manage memory. This VM object wrapper brings the object into the VM's garbage collection system, and also provides method access to the byte-code program. The native network layer's HTTP server creates the C++ TadsHttpRequest object when a request comes in from a network client. The request object goes into the message queue, which the main game thread reads via the getNetEvent() intrinsic function. When that function pulls a request object off the queue, it synthesizes the appropriate wrapper to return to the byte-code program. That's when CVmObjHTTPRequest is instatiated. The wrapper then keeps track of the VM's reference on the underlying request object, so that the request object stays in memory as long as the VM has a reference. Once the VM wrapper goes out of scope and is collected by the garbage collector, the wrapper releases its reference on the underlying C++ object. Notes Modified MJRoberts - Creation */ #ifndef VMHTTPREQ_H #define VMHTTPREQ_H #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" /* ------------------------------------------------------------------------ */ /* * Image file data: this intrinsic class is inherently transient, so it's * not stored in an image file. We thus don't need any image file data * structure. */ /* ------------------------------------------------------------------------ */ /* * Our in-memory extension data structure. This is usually a 'struct' * that contains the same information as in the image file, but using * native types. */ struct vm_httpreq_ext { /* allocate the structure */ static vm_httpreq_ext *alloc_ext(VMG_ class CVmObjHTTPRequest *self); /* the network message object */ class TadsHttpRequest *req; /* list of cookies to be sent with the reply */ struct vm_httpreq_cookie *cookies; /* the HTTPServer object for the server that created the request */ vm_obj_id_t server; }; /* * Cookie tracker */ struct vm_httpreq_cookie { vm_httpreq_cookie(const char *name, size_t namelen, const char *val, size_t vallen) { this->name = lib_copy_str(name, namelen); this->val = lib_copy_str(val, vallen); nxt = 0; } ~vm_httpreq_cookie() { lib_free_str(name); lib_free_str(val); if (nxt != 0) delete nxt; } /* clone this cookie and our list tail */ vm_httpreq_cookie *clone() { /* clone myself */ vm_httpreq_cookie *c = new vm_httpreq_cookie( name, strlen(name), val, val != 0 ? strlen(val) : 0); /* clone my list tail */ if (nxt != 0) c->nxt = nxt->clone(); /* return the clone */ return c; } /* send the cookie; returns true on success, false on failure */ int send(class TadsServerThread *t); /* name of the cookie */ char *name; /* value (including expiration, path, domain, etc) */ char *val; /* next in list */ vm_httpreq_cookie *nxt; }; /* ------------------------------------------------------------------------ */ /* * CVmObjHTTPRequest intrinsic class definition */ class CVmObjHTTPRequest: public CVmObject { friend class CVmMetaclassHTTPRequest; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is this a CVmObjHTTPRequest object? */ static int is_vmhttpreq_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* * Create the request object. 'srv_obj' is the HTTPServer object for * the listener that received the request. */ static vm_obj_id_t create(VMG_ int in_root_set, class TadsHttpRequest *req, vm_obj_id_t srv_obj); /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * receive savepoint notification - we don't keep any * savepoint-relative records, so we don't need to do anything here */ void notify_new_savept() { } /* we don't participate in undo */ void apply_undo(VMG_ struct CVmUndoRecord *) { } void discard_undo(VMG_ struct CVmUndoRecord *) { } void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* mark references */ void mark_refs(VMG_ uint state); /* we don't have any weak references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* * determine if we've been changed since loading - assume we have (if * we haven't, the only harm is the cost of unnecessarily reloading or * saving) */ int is_changed_since_load() const { return TRUE; } protected: /* get my extension data */ vm_httpreq_ext *get_ext() const { return (vm_httpreq_ext *)ext_; } /* load or reload image data */ void load_image_data(VMG_ const char *ptr, size_t siz); /* create a with no initial contents */ CVmObjHTTPRequest() { ext_ = 0; } /* create with contents ('flag' is just for overloading resolution) */ CVmObjHTTPRequest(VMG_ int flag); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* get the server object */ int getp_getServer(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the HTTP verb */ int getp_getVerb(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the query string */ int getp_getQuery(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* parse the query string into the resource name and parameters */ int getp_parseQuery(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* parse the query string and find a given parameter by name */ int getp_getQueryParam(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the request headers */ int getp_getHeaders(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get a single cookie */ int getp_getCookie(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the cookies */ int getp_getCookies(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* set a cookie for the reply */ int getp_setCookie(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the cookies */ int getp_getFormFields(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the request message body */ int getp_getBody(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the client network address */ int getp_getClientAddress( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* send a reply */ int getp_sendReply(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* common handler for sendReply and sendReplyAsync */ int common_sendReply( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc, int wait); /* start a chunked reply */ int getp_startChunkedReply(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* send a piece of a chunked reply */ int getp_sendReplyChunk(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* finish a chunked reply */ int getp_endChunkedReply(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* send a reply asynchronously */ int getp_sendReplyAsync( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluation function table */ static int (CVmObjHTTPRequest::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * CVmObjHTTPRequest metaclass registration table object */ class CVmMetaclassHTTPRequest: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "http-request/030001"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjHTTPRequest(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); G_obj_table->set_obj_transient(id); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjHTTPRequest(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); G_obj_table->set_obj_transient(id); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjHTTPRequest::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjHTTPRequest:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMHTTPREQ_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjHTTPRequest) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmhttpsrv.h�����������������������������������������������������������������������0000664�0000000�0000000�00000016657�12650170723�0016123�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 2010 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmhttpsrv.h - CVmObjHTTPServer object Function Notes Modified 04/22/10 MJRoberts - Creation */ #ifndef VMHTTPSRV_H #define VMHTTPSRV_H #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmnet.h" /* ------------------------------------------------------------------------ */ /* * HTTP server objects have no image file representation, because these * objects are inherently transient. */ /* ------------------------------------------------------------------------ */ /* * Our in-memory extension data structure. This is usually a 'struct' * that contains the same information as in the image file, but using * native types. */ struct vm_httpsrv_ext { /* allocate the structure */ static vm_httpsrv_ext *alloc_ext(VMG_ class CVmObjHTTPServer *self); /* the listener object */ class TadsListener *l; /* original requested listener address */ char *addr; }; /* ------------------------------------------------------------------------ */ /* * CVmObjHTTPServer intrinsic class definition */ class CVmObjHTTPServer: public CVmObject { friend class CVmMetaclassHTTPServer; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is this a CVmObjHTTPServer object? */ static int is_httpsrv_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * receive savepoint notification - we don't keep any * savepoint-relative records, so we don't need to do anything here */ void notify_new_savept() { } /* apply an undo record - we don't keep any undo */ void apply_undo(VMG_ struct CVmUndoRecord *) { } /* discard an undo record */ void discard_undo(VMG_ struct CVmUndoRecord *) { } /* mark our undo record references - we don't have undo or references */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* mark our references - we don't have any to mark */ void mark_refs(VMG_ uint) { } /* remove weak references - we don't have any weak refs */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* * determine if we've been changed since loading - assume we have (if * we haven't, the only harm is the cost of unnecessarily reloading or * saving) */ int is_changed_since_load() const { return TRUE; } /* * Get my listening address and port number. 'host' is the host name * original specified in the constructor, and 'ip' is the listener IP * address in decimal 1.2.3.4 notation. 'port' is the actual listening * port number. The 'host' and 'ip' strings must be deleted by the * caller via delete[]. */ int get_listener_addr(char *&addr, char *&ip, int &port) const; protected: /* get my extension data */ vm_httpsrv_ext *get_ext() const { return (vm_httpsrv_ext *)ext_; } /* load or reload image data */ void load_image_data(VMG_ const char *ptr, size_t siz); /* create a with no initial contents */ CVmObjHTTPServer() { ext_ = 0; } /* create with contents ('flag' is just for overloading resolution) */ CVmObjHTTPServer(VMG_ int flag); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* shut down the server */ int getp_shutdown(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the port number */ int getp_get_port(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the original listening address */ int getp_get_addr(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the listening address in IP format */ int getp_get_ip(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluation function table */ static int (CVmObjHTTPServer::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * CVmObjHTTPServer metaclass registration table object */ class CVmMetaclassHTTPServer: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "http-server/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjHTTPServer(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); G_obj_table->set_obj_transient(id); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjHTTPServer(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); G_obj_table->set_obj_transient(id); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjHTTPServer::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjHTTPServer:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMHTTPSRV_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjHTTPServer) ���������������������������������������������������������������������������������qtads-2.1.7/tads3/vmimage.cpp�����������������������������������������������������������������������0000664�0000000�0000000�00000271355�12650170723�0016024�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/vmimage.cpp,v 1.3 1999/07/11 00:46:59 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmimage.cpp - VM image file loader Function Notes Modified 12/12/98 MJRoberts - Creation */ #include <stdlib.h> #include <string.h> #include <memory.h> #include "os.h" #include "t3std.h" #include "vmtype.h" #include "vminit.h" #include "vmimage.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmfile.h" #include "vmmeta.h" #include "vmbif.h" #include "vmpredef.h" #include "vmrun.h" #include "vmtobj.h" #include "vmhost.h" #include "vmobj.h" #include "vmstr.h" #include "vmlst.h" #include "vmsave.h" #include "vmrunsym.h" #include "vmlookup.h" #include "vmstack.h" #include "tcprstyp.h" #include "vmhash.h" #include "vmsrcf.h" #include "vmvec.h" #include "charmap.h" /* ------------------------------------------------------------------------ */ /* * Hash table entry for the exported symbols table */ class CVmHashEntryExport: public CVmHashEntryCS { public: CVmHashEntryExport(const char *nm, size_t len, int copy_nm, const vm_val_t *val) : CVmHashEntryCS(nm, len, copy_nm) { /* remember the value */ val_ = *val; } /* the value of this symbol */ vm_val_t val_; }; /* ------------------------------------------------------------------------ */ /* * Implementation of CVmImageLoaderMres interface for regular image file * resource loading */ class CVmImageLoaderMres_std: public CVmImageLoaderMres { public: CVmImageLoaderMres_std(int fileno, CVmHostIfc *hostifc) { fileno_ = fileno; hostifc_ = hostifc; } /* add a resource */ void add_resource(uint32_t seek_ofs, uint32_t siz, const char *res_name, size_t res_name_len) { /* call the host system interface to add the resource */ hostifc_->add_resource(seek_ofs, siz, res_name, res_name_len, fileno_); } /* add a local file resource link */ void add_resource(const char *fname, size_t fnamelen, const char *res_name, size_t res_name_len) { /* call the host system inetrface to add it */ hostifc_->add_resource(fname, fnamelen, res_name, res_name_len); } private: /* host interface object */ CVmHostIfc *hostifc_; /* file number to pass to host interface */ int fileno_; }; /* ------------------------------------------------------------------------ */ /* * Image file loader implementation */ /* * instantiation */ CVmImageLoader::CVmImageLoader(CVmImageFile *fp, const char *fname, long base_ofs) { size_t i; char fname_abs[OSFNMAX], path[OSFNMAX]; /* remember the underlying image file interface */ fp_ = fp; /* remember the image filename and the base seek offset */ fname_ = lib_copy_str(fname); base_seek_ofs_ = base_ofs; /* save the file's fully-qualified, absolute directory path */ os_get_abs_filename(fname_abs, sizeof(fname_abs), fname); os_get_path_name(path, sizeof(path), fname_abs); path_ = lib_copy_str(path); /* we don't know the file version yet */ ver_ = 0; /* allocate the pool tracking objects */ for (i = 0 ; i < sizeof(pools_)/sizeof(pools_[0]) ; ++i) pools_[i] = new CVmImagePool(); /* no metaclass dependency table loaded yet */ loaded_meta_dep_ = FALSE; /* no function set dependency table loaded yet */ loaded_funcset_dep_ = FALSE; /* no entrypoint loaded yet */ loaded_entrypt_ = FALSE; /* no GSYM block yet */ has_gsym_ = FALSE; /* no runtime symbols loaded yet */ runtime_symtab_ = 0; runtime_macros_ = 0; /* there's no reflection LookupTable yet */ reflection_symtab_ = VM_INVALID_OBJ; reflection_macros_ = VM_INVALID_OBJ; /* create the exported symbol hash table */ exports_ = new CVmHashTable(64, new CVmHashFuncCS(), TRUE); /* create the synthesized exports hash table */ synth_exports_ = new CVmHashTable(16, new CVmHashFuncCS(), TRUE); /* no static initializer pages yet */ static_head_ = static_tail_ = 0; /* we don't have a static initializer code offset yet */ static_cs_ofs_ = 0; } /* * destruction */ CVmImageLoader::~CVmImageLoader() { size_t i; /* delete the pool tracking objects */ for (i = 0 ; i < sizeof(pools_)/sizeof(pools_[0]) ; ++i) delete pools_[i]; /* delete the filename string and path */ lib_free_str(fname_); lib_free_str(path_); /* if we have our own runtime symbol/macro tables, delete them */ if (runtime_symtab_ != 0) delete runtime_symtab_; if (runtime_macros_ != 0) delete runtime_macros_; /* delete the exports tables */ delete exports_; delete synth_exports_; /* delete the static initializer pages */ while (static_head_ != 0) { CVmStaticInitPage *nxt; /* remember the next one */ nxt = static_head_->nxt_; /* free the image-allocated data associated with the page */ fp_->free_mem(static_head_->data_); /* delete this one */ delete static_head_; /* move on to the next */ static_head_ = nxt; } } /* ------------------------------------------------------------------------ */ /* * load the image */ void CVmImageLoader::load(VMG0_) { char buf[128]; int done; /* set myself to be the global image loader */ G_image_loader = this; /* * perform additional initialization now that we're about to load * the image file */ vm_init_before_load(vmg_ fname_); /* tell the host application the name of the image file */ G_host_ifc->set_image_name(fname_); /* read and validate the header */ read_image_header(); /* load the data blocks */ for (done = FALSE ; !done ; ) { ulong siz; uint flags; /* read the next data block header */ fp_->copy_data(buf, 10); /* get the size */ siz = t3rp4u(buf + 4); /* get the flags */ flags = osrp2(buf + 8); /* load the block according to its type */ if (block_type_is(buf, "CPPG")) { /* load the constant pool page block */ load_const_pool_page(siz); } else if (block_type_is(buf, "ENTP")) { /* load the entrypoint */ load_entrypt(vmg_ siz); } else if (block_type_is(buf, "OBJS")) { /* load static object block */ load_static_objs(vmg_ siz); } else if (block_type_is(buf, "CPDF")) { /* load the constant pool definition block */ load_const_pool_def(siz); } else if (block_type_is(buf, "MRES")) { /* * load the multimedia resource block (the main image file * is always file number zero) */ CVmImageLoaderMres_std res_ifc(0, G_host_ifc); load_mres(siz, &res_ifc); } else if (block_type_is(buf, "MREL")) { /* load the multimedia resource link block */ CVmImageLoaderMres_std res_ifc(0, G_host_ifc); load_mres_link(siz, &res_ifc); } else if (block_type_is(buf, "MCLD")) { /* load metaclass dependency block */ load_meta_dep(vmg_ siz); } else if (block_type_is(buf, "FNSD")) { /* load function set dependency list block */ load_funcset_dep(vmg_ siz); } else if (block_type_is(buf, "SYMD")) { /* load symbolic names export block */ load_sym_names(vmg_ siz); } else if (block_type_is(buf, "SRCF")) { /* load the source file list */ load_srcfiles(vmg_ siz); } else if (block_type_is(buf, "GSYM")) { /* load the global symbols block */ load_gsym(vmg_ siz); } else if (block_type_is(buf, "MACR")) { /* load the macro symbols block */ load_macros(vmg_ siz); } else if (block_type_is(buf, "MHLS")) { /* load the method header list block */ load_mhls(vmg_ siz); } else if (block_type_is(buf, "SINI")) { /* load the static initializer block */ load_sini(vmg_ siz); } else if (block_type_is(buf, "EOF ")) { /* end of file - we can stop looking now */ done = TRUE; } else { /* * This block type is unknown, so ignore it. If a new block * type is added in the future, an older VM version won't * recognize the new block, but it can still load the image * file simply by omitting any unrecognized new blocks. Since * the image file will not be complete in this case, it may not * work properly. * * To allow for future block types which contain advisory data, * which can safely be ignored by older VM versions, while also * allowing for the possibility of changes that create * incompatibilities, we have a flag in the header that * indicates whether the block is required or not. If this * block is marked as mandatory, throw an error, since we don't * recognize the block. Note that this is a version-related * error (i.e., a VM update should fix it), so set the version * flag in the exception. */ if ((flags & VMIMAGE_DBF_MANDATORY) != 0) err_throw_a(VMERR_UNKNOWN_IMAGE_BLOCK, 1, ERR_TYPE_VERSION_FLAG); /* skip past the block */ fp_->skip_ahead(siz); } } /* the image file is required to contain an entrypoint definition */ if (!loaded_entrypt_) err_throw(VMERR_IMAGE_NO_ENTRYPT); /* the image file is required to contain a metaclass dependency table */ if (!loaded_meta_dep_) err_throw(VMERR_IMAGE_NO_METADEP); /* the image is required to have a function set dependency table */ if (!loaded_funcset_dep_) err_throw(VMERR_IMAGE_NO_FUNCDEP); /* complete the dynamic linking */ do_dynamic_link(vmg0_); /* fix up the global symbol table metaclasses, if applicable */ fix_gsym_meta(vmg0_); /* * create an IntrinsicClass instance for each metaclass that the * image file is using and for which the compiler didn't supply its * own IntrinsicClass object */ G_meta_table->create_intrinsic_class_instances(vmg0_); /* * Cache certain metaclass method table entries that are used by the * VM: Iterator.getNext [Iterator#1], Iterator.isNextAvailable * [Iterator#2]. */ vm_meta_entry_t *mcoll = G_meta_table->get_entry_by_id("iterator"); if (mcoll != 0) { G_iter_get_next = mcoll->xlat_func(1); G_iter_next_avail = mcoll->xlat_func(2); } /* * Attach the code pool and constant pool to their backing stores, * which are the pool objects we loaded from the image file. */ G_code_pool->attach_backing_store(pools_[0]); G_const_pool->attach_backing_store(pools_[1]); /* perform any requested post-load object initializations */ G_obj_table->do_all_post_load_init(vmg0_); /* load external resource files if possible */ if (G_host_ifc->can_add_resfiles()) load_ext_resfiles(vmg0_); /* * perform additional initialization now that we've finished * loading the image file */ vm_init_after_load(vmg0_); /* * Let the UI inspect the built-ins linked by the program. The UI * might use different initial display configurations depending on the * function sets and/or intrinsic classes the program uses. For * example, Workbench on Windows initially hides the HTML TADS window * if the network functions are linked, since these are normally used * to implement a Web UI, which uses a separate display window. */ os_init_ui_after_load(G_bif_table, G_meta_table); /* forget the image loader */ G_image_loader = 0; } /* ------------------------------------------------------------------------ */ /* * Load external resource files associated with the image file */ void CVmImageLoader::load_ext_resfiles(VMG0_) { int i; char suffix_lc[4]; char suffix_uc[4]; /* set up the templates for the resource file suffix */ strcpy(suffix_lc, "3r0"); strcpy(suffix_uc, "3R0"); /* * Search for resource files with the same name as the image file, but * with the extension replaced with .3r0 through .3r9. Try both * lower-case and upper-case names (in that order), in case the file * system is case-sensitive. */ for (i = 0 ; i < 10 ; ++i) { char resfile[OSFNMAX]; /* substitute the current suffix number */ suffix_lc[2] = suffix_uc[2] = i + '0'; /* * if there's an explicit resource path, use it, otherwise use * the same directory that contains the image file */ if (G_host_ifc->get_res_path() != 0) { /* * there's an explicit resource path - build the full path to * the resource file using the resource path and the root name * of the image file */ os_build_full_path(resfile, sizeof(resfile), G_host_ifc->get_res_path(), os_get_root_name(fname_)); } else { /* * there's no resoruce path - use the image file full name, * including any directory path information */ strcpy(resfile, fname_); } /* replace the old image file extension with the resource suffix */ os_remext(resfile); os_addext(resfile, suffix_lc); /* if this file doesn't exist, try the upper-case name */ if (osfacc(resfile)) { /* replace the suffix with the upper-case version */ os_remext(resfile); os_addext(resfile, suffix_uc); } /* check to see if this file exists */ if (!osfacc(resfile)) { int fileno; CVmFile *fp; CVmImageFile *volatile imagefp = 0; CVmImageLoader *volatile loader = 0; /* ask the host system to assign a file number */ fileno = G_host_ifc->add_resfile(resfile); /* create a file object for reading the file */ fp = new CVmFile(); err_try { CVmImageLoaderMres_std res_ifc(fileno, G_host_ifc); /* open the file */ fp->open_read(resfile, OSFTT3IMG); /* set up the loader */ imagefp = new CVmImageFileExt(fp); loader = new CVmImageLoader(imagefp, resfile, 0); /* load the resource-only file */ loader->load_resource_file(&res_ifc); } err_finally { /* delete the objects we created */ if (loader != 0) delete loader; if (imagefp != 0) delete imagefp; delete fp; } err_end; } } } /* * Load a resource file from the current seek location in the given file * handle */ void CVmImageLoader::load_resources_from_fp(osfildef *fp, const char *fname, CVmHostIfc *hostifc) { int fileno; /* ask the host system to assign a number to the file */ fileno = hostifc->add_resfile(fname); /* set up a resource interface with the file number */ CVmImageLoaderMres_std res_ifc(fileno, hostifc); err_try { /* load the resources */ load_resources_from_fp(fp, fname, &res_ifc); } err_catch_disc { /* ignore the error */ } err_end; } /* * Load a resource file from the current seek location in the given file * handle */ void CVmImageLoader::load_resources_from_fp(osfildef *fp, const char *fname, CVmImageLoaderMres *res_ifc) { CVmFile *file; CVmImageFile *volatile imagefp = 0; CVmImageLoader *volatile loader = 0; /* create a file object for reading the file */ file = new CVmFile(); /* set up the file with our file handler */ file->set_file(fp, 0); err_try { /* set up the loader */ imagefp = new CVmImageFileExt(file); loader = new CVmImageLoader(imagefp, fname, 0); /* load the resource-only file */ loader->load_resource_file(res_ifc); } err_finally { /* * detach our CVmFile object from the caller's file handle, * since we want to leave the caller's file handle open */ file->detach_file(); /* delete the objects we created */ if (loader != 0) delete loader; if (imagefp != 0) delete imagefp; delete file; } err_end; } /* ------------------------------------------------------------------------ */ /* * Load a resource-only file. 'fileno' is the file number assigned by * the host application (via the add_resfile() interface). */ void CVmImageLoader::load_resource_file(CVmImageLoaderMres *res_ifc) { int done; /* read and validate the image header */ read_image_header(); /* load the blocks */ for (done = FALSE ; !done ; ) { ulong siz; uint flags; char buf[128]; /* read the next data block header */ fp_->copy_data(buf, 10); /* get the size */ siz = t3rp4u(buf + 4); /* get the flags */ flags = osrp2(buf + 8); /* check the block type */ if (block_type_is(buf, "EOF ")) { /* we're done */ done = TRUE; } else if (block_type_is(buf, "MRES")) { /* load the multimedia resource block */ load_mres(siz, res_ifc); } else if (block_type_is(buf, "MREL")) { /* load the multimedia resource link block */ load_mres_link(siz, res_ifc); } else { /* * Unknown block type - ignore it, unless it's mandatory, in * which case this is an error. This is a version-related * error (fixable by updating to the latest version), so set * the version flag in the exception, if we throw one. */ if ((flags & VMIMAGE_DBF_MANDATORY) != 0) err_throw_a(VMERR_UNKNOWN_IMAGE_BLOCK, 1, ERR_TYPE_VERSION_FLAG); /* skip past the block */ fp_->skip_ahead(siz); } } } /* ------------------------------------------------------------------------ */ /* * Read and validate an image file header. */ void CVmImageLoader::read_image_header() { char buf[128]; /* * Read the header. The header consists of the signature string, a * UINT2 with the file format version number, 32 reserved bytes, * then the compilation timestamp (24 bytes). */ fp_->copy_data(buf, sizeof(VMIMAGE_SIG)-1 + 2 + 32 + 24); /* verify the signature */ if (memcmp(buf, VMIMAGE_SIG, sizeof(VMIMAGE_SIG)-1) != 0) err_throw(VMERR_NOT_AN_IMAGE_FILE); /* get the version number */ ver_ = osrp2(buf + sizeof(VMIMAGE_SIG)-1); /* store the timestamp */ memcpy(timestamp_, buf + sizeof(VMIMAGE_SIG)-1 + 2 + 32, 24); /* * Check the version to ensure that it's within the range that this * loader implementation supports. If not, throw an error, and mark is * as a version-related error. */ if (ver_ > 1) err_throw_a(VMERR_IMAGE_INCOMPAT_VSN, 1, ERR_TYPE_VERSION_FLAG); } /* ------------------------------------------------------------------------ */ /* * Execute the image */ void CVmImageLoader::run(VMG_ const char *const *argv, int argc, CVmRuntimeSymbols *global_symtab, CVmRuntimeSymbols *macro_symtab, const char *saved_state) { CVmRuntimeSymbols *orig_runtime_symtab, *orig_runtime_macros; pool_ofs_t entry_code_ofs; int entry_code_argc; CCharmapToUni *argmap = 0; char *argstr = 0; /* make sure we found a code pool definition */ if (pools_[0]->vmpbs_get_page_count() < 1) err_throw(VMERR_IMAGE_NO_CODE); /* * Find the entrypoint. If we have a saved state, call the function * given by the exported symbol "mainRestore". Otherwise, call the * exported main entrypoint function. */ if (saved_state != 0) { /* * We're restoring a saved state file immediately on startup - call * the exported "mainRestore" function. If there is no such * export, we can't run the program with an initial saved state. */ if (G_predef->main_restore_func == 0) err_throw(VMERR_NO_MAINRESTORE); /* use the mainRestore export */ entry_code_ofs = G_predef->main_restore_func; } else { /* ordinary startup - use the exported primary entrypoint */ entry_code_ofs = entrypt_; } /* we have no arguments to the entrypoint function yet */ entry_code_argc = 0; /* set myself as the global image loader */ G_image_loader = this; /* remember the original runtime symbol table */ orig_runtime_symtab = runtime_symtab_; orig_runtime_macros = runtime_macros_; /* we haven't set the file path */ int file_path_set = FALSE; /* catch any errors so we restore globals on the way out */ err_try { int i; /* if there's no file path, use the image file folder by default */ if (G_file_path == 0) { G_file_path = lib_copy_str(get_path()); file_path_set = TRUE; } /* * if the caller gave us a runtime symbol table, use it over any * we have already stored */ if (global_symtab != 0) runtime_symtab_ = global_symtab; if (macro_symtab != 0) runtime_macros_ = macro_symtab; /* create a LookupTable for the reflection symbols, if any */ create_global_symtab_lookup_table(vmg0_); /* * if we successfully created a reflection symbol table, push it * onto the stack - this will ensure that the object won't be * discarded as long as the program is running */ if (reflection_symtab_ != VM_INVALID_OBJ) { /* set up an object value for the table and push it */ G_stk->push()->set_obj(reflection_symtab_); } /* create and stack the reflection LookupTable for macros */ create_macro_symtab_lookup_table(vmg0_); if (reflection_macros_ != VM_INVALID_OBJ) G_stk->push()->set_obj(reflection_macros_); /* * run static initializers (do this after creating the symbol * table, in case any of the initializers want to access the * symbol table) */ run_static_init(vmg0_); /* if there's a saved state file to restore, push it */ if (saved_state != 0) { /* create a string object for the filename, and push it */ G_stk->push()->set_obj( CVmObjString::create(vmg_ FALSE, saved_state, strlen(saved_state))); /* count the extra startup argument */ ++entry_code_argc; } /* get a character mapper for the command-line arguments */ if (argc > 0) { /* * If we have a net configuration, assume that the arguments * are in UTF-8 format, since they're coming from a web server. * Otherwise, ask the OS which character set to use for * command-line parameters. */ char argcs[40]; if (G_net_config != 0) strcpy(argcs, "utf8"); else os_get_charmap(argcs, OS_CHARMAP_CMDLINE); /* load the character set */ argmap = CCharmapToUni::load( G_host_ifc->get_sys_res_loader(), argcs); } /* * push a string object for each argument - push them onto the * stack to ensure that they're referenced in case garbage * collection occurs while we're working */ for (i = 0 ; i < argc ; ++i) { /* if we have a character mapper, map the argument string */ size_t slen; if (argmap != 0) { /* map the argument to UTF8 */ slen = argmap->map_str_alo(&argstr, argv[i]); } else { /* make a copy of the string */ slen = strlen(argv[i]); argstr = (char *)t3malloc(slen + 1); memcpy(argstr, argv[i], slen + 1); /* make sure it's well-formed UTF-8 */ CCharmapToUni::validate(argstr, slen); } /* push the mapped/adjusted string argument */ G_interpreter->push_string(vmg_ argstr, slen); /* done with the mapped/adjusted string */ t3free(argstr); argstr = 0; } /* create a list to hold the strings */ vm_obj_id_t lst_obj = CVmObjList::create(vmg_ FALSE, argc); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lst_obj); /* set the list elements to the strings */ for (i = 0 ; i < argc ; ++i) { vm_val_t *strp; /* * Get this item from the stack. Note that the most recently * pushed item is number 0, then number 1, and so on - the * last argument string (at index argc) is thus number 0, and * the first (at index 0) is number argc. */ strp = G_stk->get(argc - i - 1); /* set this list element */ lst->cons_set_element(i, strp); } /* * we don't need the strings themselves any more, as we can reach * them through the list, so drop the strings from the stack */ G_stk->discard(argc); /* push the list - it's the argument to the program entrypoint */ G_stk->push()->set_obj(lst_obj); ++entry_code_argc; /* * Invoke the entrypoint function - it takes two arguments (the * list of argument strings, and the name of the initial saved * state file to restore, if any). */ G_stk->push()->set_propid(VM_INVALID_PROP); G_stk->push()->set_nil(); G_stk->push()->set_nil(); G_stk->push()->set_nil_obj(); G_stk->push()->set_fnptr(entry_code_ofs); vm_rcdesc rc("Main entrypoint"); G_interpreter->do_call( vmg_ 0, (const uchar *)G_code_pool->get_ptr(entry_code_ofs), entry_code_argc, &rc); /* * if we pushed a global symbol table LookupTable object, pop it - * we left it on the stack to protect it from the garbage * collector, since it's always implicitly referenced from the * intrinsic function that retrieves the symbol table object */ if (reflection_macros_ != VM_INVALID_OBJ) G_stk->discard(); if (reflection_symtab_ != VM_INVALID_OBJ) G_stk->discard(); } err_finally { /* forget the image loader */ G_image_loader = 0; /* restore the original runtime symbol tables */ runtime_symtab_ = orig_runtime_symtab; runtime_macros_ = orig_runtime_macros; /* release the command-line character mapper */ if (argmap != 0) argmap->release_ref(); /* release any argument string we were working on */ if (argstr != 0) t3free(argstr); /* * if we set the file path, un-set it, so that callers don't think * they have to do so */ if (file_path_set) { lib_free_str(G_file_path); G_file_path = 0; } } err_end; } /* * Run static initializers */ void CVmImageLoader::run_static_init(VMG0_) { CVmStaticInitPage *pg; /* run through the list of static initializers */ for (pg = static_head_ ; pg != 0 ; pg = pg->nxt_) { size_t i; /* run through the initializers on this page */ for (i = 0 ; i < pg->cnt_ ; ++i) { vm_obj_id_t obj; vm_prop_id_t prop; vm_val_t target; /* get this initializer's object and property ID */ obj = pg->get_obj_id(i); prop = pg->get_prop_id(i); target.set_obj(obj); /* invoke this object.property */ err_try { /* evaluate the property */ G_interpreter->get_prop(vmg_ 0, &target, prop, &target, 0, 0); } err_catch(exc) { /* * If the "error" is a debugger terminate, pass it through * - this simply means that the user is manually ending the * program via the debugger UI. Likewise for restarts or * interrupts. */ if (exc->get_error_code() == VMERR_DBG_HALT || exc->get_error_code() == VMERR_DBG_RESTART || exc->get_error_code() == VMERR_DBG_INTERRUPT) err_rethrow(); /* get the message for the exception that occurred */ char errbuf[512]; CVmRun::get_exc_message(vmg_ exc, errbuf, sizeof(errbuf), FALSE); /* presume we won't find names */ size_t obj_len = 0, prop_len = 0; const char *obj_name = 0, *prop_name = 0; /* find the obj and prop names in the global symbols */ if (runtime_symtab_ != 0) { /* find the object name */ obj_name = runtime_symtab_ ->find_obj_name(vmg_ obj, &obj_len); /* find the property name */ prop_name = runtime_symtab_ ->find_prop_name(vmg_ prop, &prop_len); } /* if we didn't find the names, use placeholders */ if (obj_name == 0) { obj_name = "[object name not available]"; obj_len = strlen(obj_name); } if (prop_name == 0) { prop_name = "[property name not available]"; prop_len = strlen(prop_name); } /* throw a new error for the static initializer failure */ err_throw_a(VMERR_EXC_IN_STATIC_INIT, 3, ERR_TYPE_TEXTCHAR_LEN, obj_name, obj_len, ERR_TYPE_TEXTCHAR_LEN, prop_name, prop_len, ERR_TYPE_CHAR, errbuf); } err_end; } } } /* * Unload the image. This detaches the pools from the backing stores, * which must be done before the loaded copy of the image file can be * deleted. */ void CVmImageLoader::unload(VMG0_) { /* detach the code pool from its backing store */ G_code_pool->detach_backing_store(); /* detach the constant pool from its backing store */ G_const_pool->detach_backing_store(); } /* ------------------------------------------------------------------------ */ /* * Create a LookupTable to hold the symbols in the global symbol table. */ void CVmImageLoader::create_global_symtab_lookup_table(VMG0_) { CVmObjLookupTable *lookup; vm_runtime_sym *sym; /* * if we don't have a runtime symbol table, we can't create the * reflection LookupTable */ if (runtime_symtab_ == 0) return; /* * if we already have a reflection symbol table, there's no need to * create another */ if (reflection_symtab_ != VM_INVALID_OBJ) return; /* create a LookupTable to hold the symbols */ reflection_symtab_ = CVmObjLookupTable:: create(vmg_ FALSE, 256, runtime_symtab_->get_sym_count()); /* get the object, properly cast */ lookup = (CVmObjLookupTable *)vm_objp(vmg_ reflection_symtab_); /* push the lookup table onto the stack for gc protection */ G_stk->push()->set_obj(reflection_symtab_); /* run through the symbols and populate the LookupTable */ for (sym = runtime_symtab_->get_head() ; sym != 0 ; sym = sym->nxt) { vm_val_t str; /* * skip intrinsic class modifier objects (for the same reason we * filter these out of firstObj/nextObj iterations: they're not * meaningful objects as far as the program is concerned, so * there's no reason for the program to see them) */ if (sym->val.typ == VM_OBJ && CVmObjIntClsMod::is_intcls_mod_obj(vmg_ sym->val.val.obj)) { /* it's an intrinsic class modifier - skip it */ continue; } /* create a string object to hold this symbol */ str.set_obj(CVmObjString::create(vmg_ FALSE, sym->sym, sym->len)); /* * add it to the lookup table - the symbol string is the key, and * the symbol's value is the lookup entry value */ lookup->add_entry(vmg_ &str, &sym->val); } /* done working - discard our gc protection */ G_stk->discard(); } /* * Create a LookupTable to hold the symbols in the macro table */ void CVmImageLoader::create_macro_symtab_lookup_table(VMG0_) { CVmObjLookupTable *lookup; vm_runtime_sym *sym; /* * if we don't have a runtime macro table, we can't create the * reflection LookupTable */ if (runtime_macros_ == 0) return; /* if we already created the table, there's no need to create another */ if (reflection_macros_ != VM_INVALID_OBJ) return; /* create a LookupTable to hold the symbols */ reflection_macros_ = CVmObjLookupTable:: create(vmg_ FALSE, 256, runtime_macros_->get_sym_count()); /* get the object, properly cast */ lookup = (CVmObjLookupTable *)vm_objp(vmg_ reflection_macros_); /* push the lookup table onto the stack for gc protection */ G_stk->push()->set_obj(reflection_macros_); /* run through the symbols and populate the LookupTable */ for (sym = runtime_macros_->get_head() ; sym != 0 ; sym = sym->nxt) { vm_val_t str; vm_val_t lst; vm_val_t args; vm_val_t v; int i; /* create a string object to hold this symbol */ str.set_obj(CVmObjString::create(vmg_ FALSE, sym->sym, sym->len)); /* stack it for gc protection */ G_stk->push(&str); /* * Create a list to hold the definition. We represent this as * [expansion_string, [argument_list], flags]. */ lst.set_obj(CVmObjList::create(vmg_ FALSE, 3)); G_stk->push(&lst); CVmObjList *lstp = (CVmObjList *)vm_objp(vmg_ lst.val.obj); lstp->cons_clear(); /* create and store the expansion string */ v.set_obj(CVmObjString::create( vmg_ FALSE, sym->macro_expansion, sym->macro_exp_len)); lstp->cons_set_element(0, &v); /* create the argument list */ args.set_obj(CVmObjList::create(vmg_ FALSE, sym->macro_argc)); CVmObjList *argp = (CVmObjList *)vm_objp(vmg_ args.val.obj); argp->cons_clear(); /* store the argument list in the definition list */ lstp->cons_set_element(1, &args); /* store the arguments */ for (i = 0 ; i < sym->macro_argc ; ++i) { /* create the string */ char *p = sym->macro_args[i]; size_t len = strlen(p); v.set_obj(CVmObjString::create(vmg_ FALSE, p, len)); /* store it in the argument list */ argp->cons_set_element(i, &v); } /* store the flags */ v.set_int(sym->macro_flags); lstp->cons_set_element(2, &v); /* * add the macro to the lookup table - the symbol string is the * key, and the symbol's value is the definition list */ lookup->add_entry(vmg_ &str, &lst); /* done with the symbol and list gc protection */ G_stk->discard(2); } /* done working - discard our gc protection */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * Load an Entrypoint (ENTP) block */ void CVmImageLoader::load_entrypt(VMG_ ulong siz) { char buf[32]; /* if we've already loaded an entrypoint, throw an error */ if (loaded_entrypt_) err_throw(VMERR_IMAGE_ENTRYPT_REDEF); /* read the entrypoint offset */ read_data(buf, 16, &siz); /* set the entrypoint */ entrypt_ = t3rp4u(buf); /* set the method header size in the interpreter */ G_interpreter->set_funchdr_size(osrp2(buf+4)); /* set the exception table entry size global */ G_exc_entry_size = osrp2(buf+6); /* set the debugger source line record size global */ G_line_entry_size = osrp2(buf+8); /* set the debug table header size */ G_dbg_hdr_size = osrp2(buf+10); /* set the debug local symbol header size */ G_dbg_lclsym_hdr_size = osrp2(buf+12); /* set the debug version ID */ G_dbg_fmt_vsn = osrp2(buf+14); /* set the frame size */ G_dbg_frame_size = 4; if (siz >= 2) { read_data(buf, 2, &siz); G_dbg_frame_size = osrp2(buf); } /* note that we've loaded it */ loaded_entrypt_ = TRUE; } /* ------------------------------------------------------------------------ */ /* * Load a Constant Pool Definition (CPDF) data block */ void CVmImageLoader::load_const_pool_def(ulong siz) { char buf[32]; uint pool_id; ulong page_count; ulong page_size; /* read the block data */ read_data(buf, 10, &siz); /* decode the pool identifier, page count, and page size */ pool_id = osrp2(buf); page_count = t3rp4u(buf + 2); page_size = t3rp4u(buf + 6); /* ensure that the pool ID is valid */ if (pool_id < 1 || pool_id > sizeof(pools_)/sizeof(pools_[0])) err_throw(VMERR_IMAGE_BAD_POOL_ID); /* adjust the pool to 0-based range */ --pool_id; /* initialize the pool */ pools_[pool_id]->init(fp_, page_count, page_size); } /* ------------------------------------------------------------------------ */ /* * Load a Constant Pool Page (CPPG) data block */ void CVmImageLoader::load_const_pool_page(ulong siz) { char buf[32]; uint pool_id; ulong idx; uchar xor_mask; /* read the header */ read_data(buf, 7, &siz); /* decode the pool ID and page index */ pool_id = osrp2(buf); idx = t3rp4u(buf + 2); xor_mask = buf[6]; /* ensure that the pool ID is valid */ if (pool_id < 1 || pool_id > sizeof(pools_)/sizeof(pools_[0])) err_throw(VMERR_IMAGE_BAD_POOL_ID); /* adjust the pool to 0-based range */ --pool_id; /* set the page information */ pools_[pool_id]->set_page_info(idx, fp_->get_seek(), siz, xor_mask); /* * Skip past the page data, which follow the header - we don't need * to load the page data right now, but merely note where it is for * later loading. The caller may choose to load page data only as * required, rather than all at once, so we don't want to bring it * into memory right now. */ fp_->skip_ahead(siz); } /* ------------------------------------------------------------------------ */ /* * Load a Static Object (OBJS) data block */ void CVmImageLoader::load_static_objs(VMG_ ulong siz) { char buf[32]; uint obj_count; uint meta_idx; int header_size; int large_objs; uint flags; int trans; /* read the number of objects, the metaclass index, and the flags */ read_data(buf, 6, &siz); /* decode the object count and metaclass index values */ obj_count = osrp2(buf); meta_idx = osrp2(buf + 2); /* decode the flags */ flags = osrp2(buf + 4); large_objs = ((flags & 1) != 0); trans = ((flags & 2) != 0); /* calculate the per-object header size */ header_size = 4 + (large_objs ? 4 : 2); /* read the objects */ for ( ; obj_count != 0 ; --obj_count) { vm_obj_id_t obj_id; size_t obj_size; const char *data_ptr; /* read the object ID and data size */ read_data(buf, header_size, &siz); /* get the object ID */ obj_id = (vm_obj_id_t)t3rp4u(buf); /* get the size */ if (large_objs) { /* * Make sure the object size doesn't overflow the local * hardware limits. (This test is only necessary if such an * overflow is possible given the datatypes. On most modern * architectures, it simply isn't possible because OSMALMAX is * at least as large as any 32-bit value we could read here. * Note that the value we read here is explicitly a 32-bit * value, so the biggest it could possibly be is 0xFFFFFFFF.) */ #if 0xFFFFFFFFU > OSMALMAX if (t3rp4u(buf + 4) > (unsigned long)OSMALMAX) err_throw(VMERR_OBJ_SIZE_OVERFLOW); #endif /* read the object size */ obj_size = (size_t)t3rp4u(buf + 4); } else { /* read the 16-bit object size */ obj_size = osrp2(buf + 4); } /* load the data, allocating space for it */ data_ptr = alloc_and_read(obj_size, &siz); /* create a new object of the required metaclass */ G_meta_table->create_from_image(vmg_ meta_idx, obj_id, data_ptr, obj_size); /* mark the object as transient, if appropriate */ if (trans) G_obj_table->set_obj_transient(obj_id); } } /* ------------------------------------------------------------------------ */ /* * Load a multi-media resource (MRES) block */ void CVmImageLoader::load_mres(ulong siz, CVmImageLoaderMres *res_ifc) { char buf[16]; uint entry_cnt; long base_ofs; uint i; /* * Note the current physical seek position - each entry's seek position * is stored in the table of contents as an offset from this location. * * Compute the physical base offset: this is the logical base offset in * our image stream, plus the offset of the image stream within the * image file. We need the physical base offset because the underlying * interpreter's resource loader works with the physical file, not the * logical image stream. */ base_ofs = fp_->get_seek() + base_seek_ofs_; /* read the entry count and size of the table of contents */ read_data(buf, 2, &siz); entry_cnt = osrp2(buf); /* read the entries */ for (i = 0 ; i < entry_cnt ; ++i) { uint32_t entry_ofs; uint32_t entry_size; uint entry_name_len; char name_buf[256]; char *p; size_t rem; /* read the fixed part of the table-of-contents entry */ read_data(buf, 9, &siz); entry_ofs = t3rp4u(buf); entry_size = t3rp4u(buf + 4); entry_name_len = (uchar)*(buf + 8); /* read the name */ read_data(name_buf, entry_name_len, &siz); /* null-terminate the name */ name_buf[entry_name_len] = '\0'; /* XOR the bytes of the name with 0xFF */ for (p = name_buf, rem = entry_name_len ; rem != 0 ; ++p, --rem) *p ^= 0xFF; /* add the resource to the resource interface */ res_ifc->add_resource(base_ofs + entry_ofs, entry_size, name_buf, entry_name_len); } /* * skip the data portion of the block, since we now have a map of * the data and can load individual resources on demand */ if (siz != 0) fp_->skip_ahead(siz); } /* ------------------------------------------------------------------------ */ /* * Load a multi-media resource link (MREL) block */ void CVmImageLoader::load_mres_link(ulong siz, CVmImageLoaderMres *res_ifc) { char buf[16]; uint entry_cnt; uint i; /* read the entry count and size of the table of contents */ read_data(buf, 2, &siz); entry_cnt = osrp2(buf); /* read the entries */ for (i = 0 ; i < entry_cnt ; ++i) { char buf[1]; uint resname_len, fname_len; char resname_buf[256], fname_buf[256]; /* read the resource name */ read_data(buf, 1, &siz); resname_len = (uchar)buf[0]; read_data(resname_buf, resname_len, &siz); resname_buf[resname_len] = '\0'; /* read the local filename this resource is linked to */ read_data(buf, 1, &siz); fname_len = (uchar)buf[0]; read_data(fname_buf, fname_len, &siz); fname_buf[fname_len] = '\0'; /* add the resource to the resource interface */ res_ifc->add_resource(fname_buf, fname_len, resname_buf, resname_len); } /* * skip the data portion of the block, since we now have a map of * the data and can load individual resources on demand */ if (siz != 0) fp_->skip_ahead(siz); } /* ------------------------------------------------------------------------ */ /* * load a Metaclass Dependency block */ void CVmImageLoader::load_meta_dep(VMG_ ulong siz) { char buf[256 + 10]; uint entry_cnt; uint i; /* it's an error if we've already seen a dependency block */ if (loaded_meta_dep_) err_throw(VMERR_IMAGE_METADEP_REDEF); /* note that we've loaded a dependency block */ loaded_meta_dep_ = TRUE; /* * reset the global metaclass table, in case there was anything * defined by a previously-loaded image - since a metaclass's index * in the table is implicit in the load order, we need to make sure * we're starting with a completely clean configuration */ G_meta_table->clear(); /* read the number of entries in the table */ read_data(buf, 2, &siz); entry_cnt = osrp2(buf); /* read the entries */ for (i = 0 ; i < entry_cnt ; ++i) { ushort j; uint len; uint prop_cnt; uint prop_len; vm_meta_entry_t *entry; char *p; ulong done_size; long prop_start_ofs; ulong prop_start_siz; vm_prop_id_t max_prop; vm_prop_id_t min_prop; /* * read the record size, and calculate the value of 'siz' that * we expect when we've parsed this entire record - it's the * current size minus the size of the record (note that we have * to add back in the two bytes of the size record itself, since * 'siz' will have been moved past the size record once we've * read it) */ read_data(buf, 2, &siz); done_size = (siz + 2) - osrp2(buf); /* read the length of the entry */ read_data(buf, 1, &siz); len = (uchar)buf[0]; /* read the name and null-terminate it */ read_data(buf, len, &siz); buf[len] = '\0'; p = buf + len + 1; /* read the property table information */ read_data(p, 4, &siz); prop_cnt = osrp2(p); prop_len = osrp2(p + 2); /* * set min and max to 'invalid' to start with, in case there are * no properties at all in the table */ min_prop = max_prop = VM_INVALID_PROP; /* * Read the properties - pass one: find the minimum and maximum * property ID in the table. Before we begin, remember where * the properties start in the file, so we can go back and read * the table again on the second pass. */ prop_start_ofs = fp_->get_seek(); prop_start_siz = siz; for (j = 0 ; j < prop_cnt ; ++j) { vm_prop_id_t cur; /* read the next entry */ read_data(p, 2, &siz); /* skip any additional data in the record */ if (prop_len > 2) skip_data(prop_len - 2, &siz); /* get the property */ cur = (vm_prop_id_t)osrp2(p); /* * if this is the first property, it's the max and min so * far; otherwise, remember it if it's the highest or lowest * so far */ if (j == 0) { /* this is the first one, so note it unconditionally */ min_prop = max_prop = cur; } else { /* if it's below the current minimum, it's the new minimum */ if (cur < min_prop) min_prop = cur; /* if it's above the current maximum, it's the new maximum */ if (cur > max_prop) max_prop = cur; } } /* * Now that we know the minimum and maximum properties in the * table, we can create the dependency record. */ G_meta_table->add_entry(buf, prop_cnt, min_prop, max_prop); /* get a pointer to my new entry */ entry = G_meta_table->get_entry(i); /* * Pass two: read the actual properties into the translation * table. Seek back to the start of the properties in the file, * then read the ID's. * * Note that add_prop_xlat() expects a 1-based function table * index. We are in fact reading a function table, so run our * function index counter from 1 to the entry count to yield the * proper 1-based index values. */ fp_->seek(prop_start_ofs); siz = prop_start_siz; for (j = 1 ; j <= prop_cnt ; ++j) { /* read the next entry */ read_data(p, 2, &siz); /* skip any additional data in the record */ if (prop_len > 2) skip_data(prop_len - 2, &siz); /* add this entry */ entry->add_prop_xlat((vm_prop_id_t)osrp2(p), j); } /* skip any remaining data in the record */ if (siz > done_size) skip_data(siz - done_size, &siz); } /* * always add the root object metaclass to the table, whether the * image file defines it or not */ G_meta_table ->add_entry_if_new(CVmObject::metaclass_reg_->get_reg_idx(), 0, VM_INVALID_PROP, VM_INVALID_PROP); } /* ------------------------------------------------------------------------ */ /* * load a Function Set Dependency block */ void CVmImageLoader::load_funcset_dep(VMG_ ulong siz) { char buf[256]; uint entry_cnt; uint i; /* it's an error if we've already seen a dependency block */ if (loaded_funcset_dep_) err_throw(VMERR_IMAGE_FUNCDEP_REDEF); /* note that we've loaded a dependency block */ loaded_funcset_dep_ = TRUE; /* * reset the global function set dependency table, in case there was * anything defined by a previously-loaded image - since a function * set index in the table is implicit in the load order, we need to * make sure we're starting with a completely clean configuration */ G_bif_table->clear(vmg0_); /* read the number of entries in the table */ read_data(buf, 2, &siz); entry_cnt = osrp2(buf); /* read the entries */ for (i = 0 ; i < entry_cnt ; ++i) { uint len; /* read the length of the entry */ read_data(buf, 1, &siz); len = (uchar)buf[0]; /* read the name and null-terminate it */ read_data(buf, len, &siz); buf[len] = '\0'; /* add the dependency */ G_bif_table->add_entry(vmg_ buf); } } /* ------------------------------------------------------------------------ */ /* * load a Symbolic Names export block */ void CVmImageLoader::load_sym_names(VMG_ ulong siz) { char buf[256]; uint entry_cnt; uint i; /* read the number of entries */ read_data(buf, 2, &siz); entry_cnt = osrp2(buf); /* read the entries */ for (i = 0 ; i < entry_cnt ; ++i) { vm_val_t val; uint namelen; /* read the data holder and name length */ read_data(buf, VMB_DATAHOLDER + 1, &siz); vmb_get_dh(buf, &val); namelen = (uchar)*(buf + VMB_DATAHOLDER); /* read the name */ read_data(buf, namelen, &siz); /* add this as a new entry to our exports table */ exports_->add(new CVmHashEntryExport(buf, namelen, TRUE, &val)); } } /* ------------------------------------------------------------------------ */ /* * Load a Global Symbols (GSYM) block into the runtime symbol table */ void CVmImageLoader::load_runtime_symtab_from_gsym(VMG_ ulong siz) { const size_t TOK_SYM_MAX_LEN = 80; char buf[TOK_SYM_MAX_LEN + 128]; ulong cnt; /* allocate the symbol table if we haven't already done so */ if (runtime_symtab_ == 0) runtime_symtab_ = new CVmRuntimeSymbols(); /* read the symbol count */ read_data(buf, 4, &siz); cnt = t3rp4u(buf); /* read the symbols and populate the symbol table */ for ( ; cnt != 0 ; --cnt) { char *sym_name; size_t sym_len; size_t dat_len; tc_symtype_t sym_type; char *dat; vm_val_t val; /* read the symbol's length, extra data length, and type code */ read_data(buf, 6, &siz); sym_len = osrp2(buf); dat_len = osrp2(buf + 2); sym_type = (tc_symtype_t)osrp2(buf + 4); /* check the lengths to make sure they don't overflow our buffer */ if (sym_len > TOK_SYM_MAX_LEN) { /* * this symbol name is too long - skip the symbol and its * extra data entirely */ skip_data(sym_len + dat_len, &siz); /* go on to the next symbol */ continue; } else if (dat_len + sym_len > sizeof(buf)) { /* * The extra data block is too long for our fixed buffer. * Truncate the extra data so that we don't overflow our * buffer, but proceed anyway with the truncated extra data. * Note that we won't lose any data we care about, since any * extra data beyond our buffer size is additional future * format data that we don't know how to handle anyway. By * design, we're allowed to skip extra data that we don't know * how to read. */ read_data(buf, sizeof(buf), &siz); /* skip the remainder of the extra data */ skip_data(sym_len + dat_len - sizeof(buf), &siz); } else { /* read the symbol's name and its type-specific data */ read_data(buf, sym_len + dat_len, &siz); } /* the symbol name is at the start of the buffer */ sym_name = buf; /* get the pointer to the extra data (it comes after the name) */ dat = buf + sym_len; /* create the new symbol table entry, depending on its type */ switch(sym_type) { case TC_SYM_FUNC: /* set up the function pointer value */ val.set_fnptr(t3rp4u(dat)); break; case TC_SYM_OBJ: /* set up the object value */ val.set_obj(t3rp4u(dat)); break; case TC_SYM_PROP: /* set up the property value */ val.set_propid(osrp2(dat)); /* check the 'dictionary property' flag */ if (dat_len >= 3 && (dat[0] & 0x01) != 0) { // $$$ it's a dictionary property } break; case TC_SYM_ENUM: /* set up the enum value */ val.set_enum(t3rp4u(dat)); /* check the 'enum token' flag */ if (dat_len >= 5 && (dat[0] & 0x01) != 0) { // $$$ it's an 'enum token' } break; case TC_SYM_METACLASS: /* set up the metaclass object value */ val.set_obj(t3rp4u(dat + 2)); break; case TC_SYM_BIF: /* built-in function */ val.set_bifptr(osrp2(dat + 2), osrp2(dat)); break; default: /* * ignore other types; mark the value as 'empty' so we know we * don't have anything to add to the table */ val.set_empty(); break; } /* if we found a valid type, add it to the table */ if (val.typ != VM_EMPTY) runtime_symtab_->add_sym(sym_name, sym_len, &val); } } /* ------------------------------------------------------------------------ */ /* * Load a Macro Definitions (MACR) block into the runtime symbol table */ void CVmImageLoader::load_runtime_symtab_from_macr(VMG_ ulong siz) { const size_t TOK_SYM_MAX_LEN = 80; char buf[10]; char sym[TOK_SYM_MAX_LEN]; ulong cnt; /* allocate the macro table if we haven't already done so */ if (runtime_macros_ == 0) runtime_macros_ = new CVmRuntimeSymbols(); /* read the symbol count */ read_data(buf, 4, &siz); cnt = t3rp4u(buf); /* read the symbols and populate the symbol table */ for ( ; cnt != 0 ; --cnt) { size_t sym_len; size_t exp_len; int argc; unsigned int flags; int i; /* read the symbol's length */ read_data(buf, 2, &siz); sym_len = osrp2(buf); /* if it fits, read it */ if (sym_len <= TOK_SYM_MAX_LEN) { /* read the name */ read_data(sym, sym_len, &siz); } else { /* it's too long - skip it */ skip_data(sym_len, &siz); } /* read the flags and argument count */ read_data(buf, 4, &siz); flags = osrp2(buf); argc = osrp2(buf + 2); /* remember the current position */ long start_pos = fp_->get_seek(); ulong start_siz = siz; /* count up the argument sizes */ size_t arg_len = 0; for (i = 0 ; i < argc ; ++i) { /* read this argument length */ read_data(buf, 2, &siz); size_t l = osrp2(buf); /* add it to the total, plus a null byte */ arg_len += l + 1; /* skip it in the file */ skip_data(l, &siz); } /* read the expansion size */ read_data(buf, 4, &siz); exp_len = osrp4(buf); /* allocate the symbol entry */ vm_runtime_sym *entry = runtime_macros_->add_macro( sym, sym_len, exp_len, flags, argc, arg_len); /* seek back to the start of the argument list */ fp_->seek(start_pos); siz = start_siz; /* read the arguments */ for (i = 0 ; i < argc ; ++i) { /* read the argument length */ read_data(buf, 2, &siz); size_t l = osrp2(buf); /* read this argument, and null-terminate it */ read_data(entry->macro_args[i], l, &siz); entry->macro_args[i][l] = '\0'; /* commit the storage */ entry->commit_macro_arg(i, l + 1); } /* read the expansion */ skip_data(4, &siz); read_data(entry->macro_expansion, exp_len, &siz); } } /* ------------------------------------------------------------------------ */ /* * load a Source File List block */ void CVmImageLoader::load_srcfiles(VMG_ ulong siz) { size_t i; char buf[64]; size_t entry_cnt; size_t line_size; /* * if there's no source file table, we're not in debug mode and hence * have no use for this type of information, so skip the block */ if (G_srcf_table == 0) { /* skip the entire block in the file */ fp_->skip_ahead(siz); /* we're done */ return; } /* clear any existing information in the table */ G_srcf_table->clear(); /* read the number of entries in the table, and the line record size */ read_data(buf, 4, &siz); entry_cnt = osrp2(buf); line_size = osrp2(buf + 2); /* read the entries */ for (i = 0 ; i < entry_cnt ; ++i) { long start_pos; long next_pos; int orig_index; size_t len; CVmSrcfEntry *entry; ulong line_cnt; ulong skip_amt; /* note the starting file location of this record */ start_pos = fp_->get_seek(); /* * read the size of this file record, the original index, and the * length of the filename */ read_data(buf, 8, &siz); orig_index = osrp2(buf + 4); len = osrp2(buf + 6); /* * calculate the file location of the start of the next block by * adding the starting position and size of this block */ next_pos = start_pos + t3rp4u(buf); /* allocate the entry */ entry = G_srcf_table->add_entry(orig_index, len); /* read the data into this entry's buffer */ read_data(entry->get_name_buf(), len, &siz); /* null-terminate the buffer */ entry->get_name_buf()[len] = '\0'; /* read the number of line records */ read_data(buf, 4, &siz); line_cnt = t3rp4u(buf); /* * if the line records are too big for our buffer, or smaller than * our required minimum size, don't bother reading them - we'll * just skip them entirely */ if (line_size <= sizeof(buf) && line_size >= 8) { /* tell the source file table entry how many lines we have */ entry->alloc_line_records(line_cnt); /* read the line records */ for ( ; line_cnt != 0 ; --line_cnt) { ulong linenum; ulong code_addr; /* read the record */ read_data(buf, line_size, &siz); /* get the source line number and byte code offset */ linenum = t3rp4u(buf); code_addr = t3rp4u(buf + 4); /* add the line to the source file table entry */ entry->add_line_record(linenum, code_addr); } } /* skip ahead to the start of the next record */ skip_amt = next_pos - fp_->get_seek(); if (skip_amt != 0) skip_data(skip_amt, &siz); } } /* ------------------------------------------------------------------------ */ /* * Perform dynamic linking after loading. */ void CVmImageLoader::do_dynamic_link(VMG0_) { struct sym_assoc_t { /* the symbol name */ const char *sym; /* datatype */ vm_datatype_t typ; /* address of predef table entry for this value */ void *addr; /* * flag: this is a synthetic import, which means that we don't * look for it in the image file, but only in the restored state */ int is_synthetic; }; sym_assoc_t imports[] = { /* build the table by including the import list */ #define VM_IMPORT_OBJ(sym, mem) { sym, VM_OBJ, &G_predef->mem, FALSE }, #define VM_NOIMPORT_OBJ(sym, mem) { sym, VM_OBJ, &G_predef->mem, TRUE }, #define VM_IMPORT_PROP(sym, mem) { sym, VM_PROP, &G_predef->mem, FALSE }, #define VM_NOIMPORT_PROP(sym, mem) { sym, VM_PROP, &G_predef->mem, TRUE }, #define VM_IMPORT_FUNC(sym, mem) { sym, VM_FUNCPTR, &G_predef->mem, FALSE }, #include "vmimport.h" /* end the table */ { 0, VM_NIL, 0, FALSE } }; const sym_assoc_t *ip; /* reset all of the predefs to undefined */ G_predef->reset(); /* * Run through our list of symbols to import from the load file, and * load each one. */ for (ip = imports ; ip->sym != 0 ; ++ip) { CVmHashEntryExport *entry; /* presume we won't find it */ entry = 0; /* * if it's not synthetic, look up this symbol in the image file's * export table (don't look up synthetic symbols, because we * always want to create these ourselves, not load them from an * image file) */ if (!ip->is_synthetic) { /* it's a regular import - look for it in the image exports */ entry = (CVmHashEntryExport *)exports_ ->find(ip->sym, strlen(ip->sym)); } /* * if we didn't find the entry in the image file's exports, look * in the synthesized exports, in case we're restoring state from * a previous session */ if (entry == 0) entry = (CVmHashEntryExport *)synth_exports_ ->find(ip->sym, strlen(ip->sym)); /* if the symbol isn't exported, skip it and proceed to the next */ if (entry == 0) continue; /* * make sure the type of the symbol exported by the image file * matches the type of value we want to import for the symbol */ if (ip->typ != entry->val_.typ) { /* the exported symbol has the wrong type - throw an error */ err_throw_a(VMERR_INVAL_EXPORT_TYPE, 1, ERR_TYPE_CHAR, ip->sym); } /* set the value according to its type */ switch(ip->typ) { case VM_OBJ: /* store the object value */ *(vm_obj_id_t *)ip->addr = entry->val_.val.obj; break; case VM_PROP: *(vm_prop_id_t *)ip->addr = entry->val_.val.prop; break; case VM_FUNCPTR: *(pool_ofs_t *)ip->addr = entry->val_.val.ofs; break; default: /* we don't import any other types */ break; } } /* * If we don't already have one, create a vector to keep track of the * last property ID allocated. We store this in a vector object so * that the property allocation mechanism easily integrates with the * undo and save/restore mechanisms. */ if (G_predef->last_prop_obj == VM_INVALID_OBJ) { /* create the object */ G_predef->last_prop_obj = CVmObjVector::create(vmg_ FALSE, 1); /* add it to the synthesized export list */ add_synth_export_obj(VM_IMPORT_NAME_LASTPROPOBJ, G_predef->last_prop_obj); /* * set up the object with the current last property, as indicated * in the image file */ set_last_prop(vmg_ G_predef->last_prop); } /* * if the image didn't define an exceptionMessage property, allocate a * new property ID for it */ if (G_predef->rterrmsg_prop == VM_INVALID_PROP) { /* allocate the property ID */ G_predef->rterrmsg_prop = alloc_new_prop(vmg0_); /* add it to the synthesized export list */ add_synth_export_prop(VM_IMPORT_NAME_RTERRMSG, G_predef->rterrmsg_prop); } /* * Create the constant string and list placeholder objects - these are * never imported from the image file, but must be dynamically created * after loading is complete. * * Create these objects without values, since the values are * irrelevant - they're needed only for their type information. */ if (G_predef->const_str_obj == VM_INVALID_OBJ) { /* allocate it */ G_predef->const_str_obj = CVmObjString::create(vmg_ FALSE, 0); /* add it to the synthesized export list */ add_synth_export_obj(VM_IMPORT_NAME_CONSTSTR, G_predef->const_str_obj); } if (G_predef->const_lst_obj == VM_INVALID_OBJ) { /* allocate it */ G_predef->const_lst_obj = CVmObjList::create(vmg_ FALSE, (size_t)0); /* add it to the synthesized export list */ add_synth_export_obj(VM_IMPORT_NAME_CONSTLST, G_predef->const_lst_obj); } } /* ------------------------------------------------------------------------ */ /* * Load a static initializer list block */ void CVmImageLoader::load_sini(VMG_ ulong siz) { char hdr[12]; ulong siz_rem; ulong hdr_siz; ulong init_cnt; ulong init_rem; /* * read the header size, static code starting offset, and * initializer count */ fp_->copy_data(hdr, 12); hdr_siz = t3rp4u(hdr); static_cs_ofs_ = t3rp4u(hdr + 4); init_cnt = t3rp4u(hdr + 8); /* skip any extra header information */ if (hdr_siz > 12) fp_->skip_ahead(hdr_siz - 12); /* account for having read the header in our size remaining */ siz_rem = siz - hdr_siz; /* read in chunks of VM_STATIC_INIT_PAGE_MAX initializers */ for (init_rem = init_cnt ; init_rem != 0 ; ) { size_t cur; CVmStaticInitPage *pg; /* read the page max, or the actual number remaining if fewer */ cur = VM_STATIC_INIT_PAGE_MAX; if (init_rem < cur) cur = (size_t)init_rem; /* allocate the next page */ pg = new CVmStaticInitPage(cur); /* link in the page */ if (static_tail_ != 0) static_tail_->nxt_ = pg; else static_head_ = pg; static_tail_ = pg; /* read the page */ pg->data_ = fp_->alloc_and_read(cur * 6, 0, siz_rem); /* adjust our counters for the chunk */ init_rem -= cur; siz_rem -= cur * 6; } /* skip any data we don't use in this version */ if (siz_rem != 0) fp_->skip_ahead(siz_rem); } /* ------------------------------------------------------------------------ */ /* * Add a synthesized object export */ void CVmImageLoader::add_synth_export_obj(const char *nm, vm_obj_id_t obj) { vm_val_t val; /* add the new entry with an object value */ val.set_obj(obj); synth_exports_->add(new CVmHashEntryExport(nm, strlen(nm), TRUE, &val)); } /* * Add a synthesized property export */ void CVmImageLoader::add_synth_export_prop(const char *nm, vm_prop_id_t prop) { vm_val_t val; /* add the new entry with a property value */ val.set_propid(prop); synth_exports_->add(new CVmHashEntryExport(nm, strlen(nm), TRUE, &val)); } /* * Discard all synthesized exports. When we're resetting to the image * file state, or when we're about to restore a saved state, we must * discard the synthesized exports from the prior load so that we start * from the base image file state again. */ void CVmImageLoader::discard_synth_exports() { /* delete all of the entries in the table */ synth_exports_->delete_all_entries(); } /* ------------------------------------------------------------------------ */ /* * Callback context for enumerating the synthesized export symbols for * saving the list to a saved state file */ struct synth_save_cb_ctx { /* the file to which we're writing */ CVmFile *fp; /* number of entries */ long cnt; }; /* * Save the synthesized exports to a saved state file */ void CVmImageLoader::save_synth_exports(VMG_ CVmFile *fp) { synth_save_cb_ctx ctx; long pos; long end_pos; /* set up our context */ ctx.fp = fp; ctx.cnt = 0; /* * write a placeholder count of zero, but remember where it is so we * can come back and fix it up later */ pos = fp->get_pos(); fp->write_uint4(0); /* enumerate all of the entries through our save callback */ synth_exports_->enum_entries(&save_synth_export_cb, &ctx); /* go back and fix up the count, then seek back to the end */ end_pos = fp->get_pos(); fp->set_pos(pos); fp->write_uint4(ctx.cnt); fp->set_pos(end_pos); } /* * Synthesized export enumeration callback - save to file */ void CVmImageLoader::save_synth_export_cb(void *ctx0, CVmHashEntry *entry0) { synth_save_cb_ctx *ctx = (synth_save_cb_ctx *)ctx0; CVmHashEntryExport *entry = (CVmHashEntryExport *)entry0; char buf[VMB_DATAHOLDER]; /* write this entry's length and name to the file */ ctx->fp->write_uint2(entry->getlen()); ctx->fp->write_bytes(entry->getstr(), entry->getlen()); /* write the value as a dataholder */ vmb_put_dh(buf, &entry->val_); ctx->fp->write_bytes(buf, VMB_DATAHOLDER); /* count the entry */ ++(ctx->cnt); } /* * Restore the synthesized exports from a saved state file */ int CVmImageLoader::restore_synth_exports(VMG_ CVmFile *fp, CVmObjFixup *fixups) { long cnt; /* * discard the old synthesized exports - we want to entirely replace * these with what we're about to load from the file */ G_image_loader->discard_synth_exports(); /* read the number of synthesized exports */ cnt = fp->read_uint4(); /* read the exports */ for ( ; cnt != 0 ; --cnt) { size_t len; char buf[256]; char dh[VMB_DATAHOLDER]; vm_val_t val; /* read the length of the name and the name */ len = fp->read_uint2(); fp->read_bytes(buf, len > sizeof(buf) ? sizeof(buf) : len); /* skip any part of the name that didn't fit in the buffer */ if (len > sizeof(buf)) { /* skip the extra bytes */ fp->set_pos(fp->get_pos() + (len - sizeof(buf))); /* limit the length we use to the buffer size */ len = sizeof(buf); } /* read the value */ fp->read_bytes(dh, VMB_DATAHOLDER); /* if the value is an object reference, fix it up */ fixups->fix_dh(vmg_ dh); /* get the value */ vmb_get_dh(dh, &val); /* add the export */ synth_exports_->add(new CVmHashEntryExport(buf, len, TRUE, &val)); } /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Allocate a new property ID. */ vm_prop_id_t CVmImageLoader::alloc_new_prop(VMG0_) { CVmObjVector *vec; vm_val_t idx_val; vm_val_t val; vm_val_t new_cont; /* get the LastPropObj vector */ vec = (CVmObjVector *)vm_objp(vmg_ G_predef->last_prop_obj); /* * get the element at index 1 - our last property value is always at * the first element */ idx_val.set_int(1); /* get the value from the vector */ vec->index_val(vmg_ &val, G_predef->last_prop_obj, &idx_val); /* if we've allocated every available property ID, throw an error */ if (val.val.prop == 65535) err_throw(VMERR_OUT_OF_PROPIDS); /* allocate the new property ID by incrementing the last used ID */ ++val.val.prop; /* * update the vector with the new value (this new property ID is now * the last property ID in use) */ vec->set_index_val(vmg_ &new_cont, G_predef->last_prop_obj, &idx_val, &val); /* return the new last ID */ return val.val.prop; } /* * Get the last property ID. This returns the last valid property ID * currently in use in the program. */ vm_prop_id_t CVmImageLoader::get_last_prop(VMG0_) { /* get LastPropObj[1] */ vm_val_t vec, ele; vec.set_obj(G_predef->last_prop_obj); vec.ll_index(vmg_ &ele, 1); /* return the value */ return ele.val.prop; } /* * Set the last property ID. This updates the LastPropObj synthetic * export object, so that the last property ID is properly integrated with * the undo and save/restore mechanisms. */ void CVmImageLoader::set_last_prop(VMG_ vm_prop_id_t last_prop) { /* get the LastPropObj vector */ CVmObjVector *vec = (CVmObjVector *)vm_objp(vmg_ G_predef->last_prop_obj); /* * set up the values - the index is always 1, since the vector * contains only one element; and the value contains the new last * property ID */ vm_val_t idx_val, new_val; idx_val.set_int(1); new_val.set_propid(G_predef->last_prop); /* update the vector */ vm_val_t new_cont; vec->set_index_val(vmg_ &new_cont, G_predef->last_prop_obj, &idx_val, &new_val); } /* ------------------------------------------------------------------------ */ /* * Copy data from the file into a buffer, decrementing a size counter. * We'll throw a BLOCK_TOO_SMALL error if the read length exceeds the * remaining size. */ void CVmImageLoader::read_data(char *buf, size_t read_len, ulong *remaining_size) { /* ensure we have enough data left in our block */ if (read_len > *remaining_size) err_throw(VMERR_IMAGE_BLOCK_TOO_SMALL); /* decrement the remaining size counter for the data we just read */ *remaining_size -= read_len; /* read the data */ fp_->copy_data(buf, read_len); } /* * skip data */ void CVmImageLoader::skip_data(size_t skip_len, ulong *remaining_size) { /* ensure we have enough data left in our block */ if (skip_len > *remaining_size) err_throw(VMERR_IMAGE_BLOCK_TOO_SMALL); /* decrement the remaining size counter for the data we're skipping */ *remaining_size -= skip_len; /* skip ahead in the underlying file */ fp_->skip_ahead(skip_len); } /* * Allocate memory for data and read the data from the file, * decrementing the amount read from a size counter. Throws * BLOCK_TOO_SMALL if the read length exceeds the remaining size. */ const char *CVmImageLoader::alloc_and_read(size_t read_len, ulong *remaining_size) { const char *p; /* ensure we have enough data left in our block */ if (read_len > *remaining_size) err_throw(VMERR_IMAGE_BLOCK_TOO_SMALL); /* allocate space, and read the data into the new space */ p = fp_->alloc_and_read(read_len, 0, *remaining_size); /* decrement the remaining size counter for the data we just read */ *remaining_size -= read_len; /* return the pointer to the new space */ return p; } /* ------------------------------------------------------------------------ */ /* * Image Pool tracker implementation */ CVmImagePool::CVmImagePool() { /* we don't have any information about the pool yet */ page_count_ = 0; page_size_ = 0; page_info_ = 0; fp_ = 0; } CVmImagePool::~CVmImagePool() { /* if we allocated a set of page offset arrays, delete them */ if (page_info_ != 0) { size_t i; size_t cnt; /* compute the number of subarrays we have */ cnt = get_subarray_count(); /* delete each subarray */ for (i = 0 ; i < cnt ; ++i) t3free(page_info_[i]); /* delete the master array */ t3free(page_info_); } } /* * initialize */ void CVmImagePool::init(CVmImageFile *fp, ulong page_count, ulong page_size) { size_t cnt; size_t i; /* remember the page count and page size */ page_count_ = page_count; page_size_ = page_size; /* remember the underlying image file */ fp_ = fp; /* if this pool has already been defined, throw an error */ if (page_info_ != 0) err_throw(VMERR_IMAGE_POOL_REDEF); /* * Allocate the main page array. We need one entry for each * subarray, so figure the subarray count and allocate the * appropriate amount of space. */ cnt = get_subarray_count(); page_info_ = (CVmImagePool_pg **)t3malloc(cnt * sizeof(page_info_[0])); /* throw an error if that failed */ if (page_info_ == 0) err_throw(VMERR_OUT_OF_MEMORY); /* clear the array */ memset(page_info_, 0, cnt * sizeof(page_info_[0])); /* allocate the subarrays */ for (i = 0 ; i < cnt ; ++i) { /* allocate this page, which contains file offsets */ page_info_[i] = (CVmImagePool_pg *)t3malloc(VMIMAGE_POOL_SUBARRAY_SIZE * sizeof(page_info_[i][0])); /* throw an error if that failed */ if (page_info_[i] == 0) err_throw(VMERR_OUT_OF_MEMORY); /* clear the memory */ memset(page_info_[i], 0, VMIMAGE_POOL_SUBARRAY_SIZE * sizeof(page_info_[i][0])); } } /* * Set a page seek offset */ void CVmImagePool::set_page_info(ulong page_idx, long seek_pos, size_t page_size, uchar xor_mask) { CVmImagePool_pg *info; /* * make sure we've allocated the page array and that this index is * in range -- throw an error if not */ if (page_info_ == 0) err_throw(VMERR_IMAGE_POOL_BEFORE_DEF); if (page_idx >= page_count_) err_throw(VMERR_IMAGE_POOL_BAD_PAGE); /* get the entry for this page */ info = get_page_info(page_idx); /* set the information */ info->seek_pos = seek_pos; info->page_size = page_size; info->xor_mask = xor_mask; } /* * allocate and load a page */ const char *CVmImagePool:: vmpbs_alloc_and_load_page(pool_ofs_t ofs, size_t /*page_size*/, size_t load_size) { CVmImagePool_pg *info; /* get the page information */ info = get_page_info_ofs(ofs); /* seek to the correct location in the image file */ seek_page_ofs(ofs); /* ask the underlying file to load the data */ return fp_->alloc_and_read(load_size, info->xor_mask, load_size); } /* * load a page into the given memory block */ void CVmImagePool::vmpbs_load_page(pool_ofs_t ofs, size_t /*page_size*/, size_t load_size, char *mem) { CVmImagePool_pg *info; /* get the page information */ info = get_page_info_ofs(ofs); /* seek to the correct location in the image file */ seek_page_ofs(ofs); /* ask the underlying file to load the data */ fp_->copy_data(mem, load_size); /* apply the XOR mask to the loaded data */ apply_xor_mask(mem, load_size, info->xor_mask); } /* * free a page previously loaded */ void CVmImagePool::vmpbs_free_page(const char *mem, pool_ofs_t /*ofs*/, size_t /*page_size*/) { /* tell the file to free the memory */ fp_->free_mem(mem); } /* * Determine if the backing store pages are writable. Let the * underlying file decide. */ int CVmImagePool::vmpbs_is_writable() { /* * if the underlying file allows writing to its allocated pages, * allow writing */ return fp_->allow_write_to_alloc(); } /* * seek to the image file data for the page at the given pool offset */ void CVmImagePool::seek_page_ofs(pool_ofs_t ofs) { ulong page_idx; CVmImagePool_pg *info; /* * get the page index - pages are all of a fixed length, so we can * find the page index simply by dividing the offset by the page * size */ page_idx = ofs / page_size_; /* make sure the page is valid */ if (page_idx >= page_count_) err_throw(VMERR_LOAD_BAD_PAGE_IDX); /* make sure the page has been defined */ info = get_page_info(page_idx); /* make sure this page's information has been loaded */ if (info->seek_pos == 0) err_throw(VMERR_LOAD_UNDEF_PAGE); /* seek to this block's position in the file */ fp_->seek(info->seek_pos); } /* ------------------------------------------------------------------------ */ /* * Image file interface - external disk file implementation */ /* * Delete the image file loader. This deletes all of the memory * associated with loaded objects, so the image file loader must not be * deleted until all of the loaded root-set objects are deleted. */ CVmImageFileExt::~CVmImageFileExt() { CVmImageFileExt_blk *cur; CVmImageFileExt_blk *nxt; /* run through the list of allocated blocks and delete each one */ for (cur = mem_head_ ; cur != 0 ; cur = nxt) { /* remember the next block, since we're deleting this one */ nxt = cur->nxt_; /* delete this one */ delete cur; } } /* * copy data to the caller's buffer */ void CVmImageFileExt::copy_data(char *buf, size_t len) { /* read directly from the file into the caller's buffer */ fp_->read_bytes(buf, len); } /* * allocate memory for and read data */ const char *CVmImageFileExt::alloc_and_read(size_t len, uchar xor_mask, ulong remaining_in_page) { char *mem; /* allocate memory */ mem = alloc_mem(len, remaining_in_page); if (mem == 0) err_throw(VMERR_OUT_OF_MEMORY); /* read the data */ fp_->read_bytes(mem, len); /* if there's an XOR mask, apply it to each byte we read */ CVmImagePool::apply_xor_mask(mem, len, xor_mask); /* return the memory block */ return mem; } /* * seek */ void CVmImageFileExt::seek(long pos) { /* seek to the given position in the underlying file */ fp_->set_pos(pos); } /* * get current seek position */ long CVmImageFileExt::get_seek() const { /* get the position from the underlying file */ return fp_->get_pos(); } /* * skip bytes */ void CVmImageFileExt::skip_ahead(long len) { /* seek ahead in the underlying file */ fp_->set_pos_from_cur(len); } /* * Allocate data for loading */ char *CVmImageFileExt::alloc_mem(size_t siz, ulong remaining_in_page) { /* * If the requested size is more than an eigth of our block size, * give the new allocation its own block. Our blocks are intended * to reduce overhead for small blocks; large blocks not only won't * create a lot of overhead as individual "malloc" allocations, but * would probably leave our block underutilized by leaving a lot of * it free, defeating the purpose. */ if (siz > VMIMAGE_EXT_BLK_SIZE / 8) { CVmImageFileExt_blk *new_block; /* it's a big request, so give it its own block */ new_block = new CVmImageFileExt_blk(siz); /* * link it at the tail of the list (we don't want to suballocate * anything more out of this, obviously, since we're going to * consume the entire block, but we do want to keep it in our * list so that we can track and eventually delete the memory) */ new_block->nxt_ = 0; new_block->prv_ = mem_tail_; /* set the forward pointer of the previous entry */ if (mem_tail_ != 0) mem_tail_->nxt_ = new_block; else mem_head_ = new_block; /* it's the new tail */ mem_tail_ = new_block; /* "suballocate" out of the new block */ return mem_tail_->suballoc(siz); } else { /* * It's a small request, so suballocate it out of a block. * First, make sure we have space in the current block; if not, * allocate a new block. */ if (mem_head_ == 0 || siz > mem_head_->rem_) { CVmImageFileExt_blk *new_block; size_t new_block_size; /* * There's no space left in the current block - allocate a new * block. Leave space in the new block for further * allocations, so we don't have to allocate lots of little * blocks; however, as an upper bound, allocate only as much * space as remains in the current page being read out of the * image file, so that we don't leave a bunch of unused space * after reading the last objects from the image. */ new_block_size = VMIMAGE_EXT_BLK_SIZE; if (new_block_size > remaining_in_page) new_block_size = (size_t)remaining_in_page; /* allocate the new block */ new_block = new CVmImageFileExt_blk(new_block_size); /* link it in at the head of our list */ new_block->prv_ = 0; new_block->nxt_ = mem_head_; /* set the back pointer of the next entry */ if (mem_head_ != 0) mem_head_->prv_ = new_block; else mem_tail_ = new_block; /* it's the new head */ mem_head_ = new_block; } /* suballocate it */ return mem_head_->suballoc(siz); } } /* ------------------------------------------------------------------------ */ /* * Memory tracker for external file loader */ CVmImageFileExt_blk::CVmImageFileExt_blk(size_t siz) { /* allocate the associated memory block */ block_ptr_ = (char *)t3malloc(siz); /* remember the amount of space remaining */ rem_ = siz; /* the next byte free is the first byte */ free_ptr_ = block_ptr_; /* nothing else is in our list yet */ nxt_ = prv_ = 0; } CVmImageFileExt_blk::~CVmImageFileExt_blk() { /* free our memory block */ t3free(block_ptr_); } char *CVmImageFileExt_blk::suballoc(size_t siz) { char *ret; /* if we don't have enough memory available, fail the request */ if (rem_ < siz) return 0; /* our return value will be the current free pointer */ ret = free_ptr_; /* move the free pointer past the allocated block */ free_ptr_ += siz; /* deduct the amount we just allocated from the space available */ rem_ -= siz; /* return the memory */ return ret; } /* ------------------------------------------------------------------------ */ /* * Image file interface - memory-mapped file implementation */ /* * copy data to the caller's buffer */ void CVmImageFileMem::copy_data(char *buf, size_t len) { /* if we're past the end of the file, throw an error */ if (pos_ + len > len_) err_throw(VMERR_READ_PAST_IMG_END); /* copy data into the caller's buffer */ memcpy(buf, mem_ + pos_, len); /* seek past the data */ pos_ += len; } /* * allocate memory for and read data */ const char *CVmImageFileMem::alloc_and_read(size_t len, uchar xor_mask, ulong /*remaining_in_page*/) { const char *ret; /* if we're past the end of the file, throw an error */ if (pos_ + len > len_) err_throw(VMERR_READ_PAST_IMG_END); /* the data are already in memory - figure out where */ ret = mem_ + pos_; /* seek past the data */ pos_ += len; /* * we can't apply an xor mask to an in-memory page - if the mask is * non-zero, it's an error */ if (xor_mask != 0) err_throw(VMERR_XOR_MASK_BAD_IN_MEM); /* return the pointer */ return ret; } /* ------------------------------------------------------------------------ */ /* * Generic stream implementation for an image file block */ /* * read bytes from the stream */ void CVmImageFileStream::read_bytes(char *buf, size_t len) { /* if we don't have enough data to satisfy the request, it's an error */ if (len > len_) err_throw(VMERR_IMAGE_BLOCK_TOO_SMALL); /* deduct the space we're reading from the remaining data size */ len_ -= len; /* copy the data from the image file to the caller's buffer */ fp_->copy_data(buf, len); } /* * read bytes, allowing short reads without error */ size_t CVmImageFileStream::read_nbytes(char *buf, size_t len) { /* limit the read to the available size */ if (len > len_) len = len_; /* deduct the space we're reading from the remaining data size */ len_ -= len; /* copy the data from the image file to the caller's buffer */ if (len != 0) fp_->copy_data(buf, len); /* return the length actually read */ return len; } /* * write bytes to the stream */ void CVmImageFileStream::write_bytes(const char *, size_t) { /* this is a read-only stream, so writing isn't allowed */ err_throw(VMERR_WRITE_FILE); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmimage.h�������������������������������������������������������������������������0000664�0000000�0000000�00000074310�12650170723�0015461�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/vmimage.h,v 1.3 1999/07/11 00:46:59 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmimage.h - VM image file loader Function Loads an image file for execution. The VM loads and executes an image by creating an appropriate concrete CVmImageFile subclass, then creating a CVmImageLoader using the CVmImageFile object. CVmImageLoader parses the input file, sets up page mappings for constant pools, and initializes objects found in the file. The lifespan of the CVmImageLoader object is the lifespan of the loaded image. As long as the VM is executing the image, it must keep the CVmImageLoader object in existence. The CVmImageLoader object can be deleted after the VM terminates execution of the image. The loader comes in two varieties: an external file loader, and a memory-mapped file loader. The external file loader reads data from a disk file, allocating memory for the information read from the file. The memory-mapped file loader takes a chunk of memory that's already been loaded, and initializes the VM to use the pre-allocated memory, rather than allocating a separate copy of the image data. The memory-mapped loader is meant for systems with no external storage, such as hand-held devices. It can also be used on systems with large, flat address spaces to speed up loading by isolating all disk access into a single bulk load of the image into memory. The external file loader is useful for systems with smaller address spaces, and can be used with a swapping pool implementation to allow the VM to operate when available memory is smaller than the image file. Notes Modified 12/12/98 MJRoberts - Creation */ #ifndef VMIMAGE_H #define VMIMAGE_H #include <memory.h> #include "vmpool.h" #include "vmglob.h" #include "vmtype.h" #include "vmfile.h" /* ------------------------------------------------------------------------ */ /* * Image file constants */ /* * signature - this is at the beginning of every image file so that we * can easily detect a completely invalid file */ #define VMIMAGE_SIG "T3-image\015\012\032" /* ------------------------------------------------------------------------ */ /* * Data Block Flags */ /* * Mandatory: this block must be recognized and loaded. When new types * of blocks are added in future versions, the compiler can use this * flag to indicate whether or not an old VM can safely ignore the new * block type. An old VM will not be able to load a new block, since * that new block won't have been part of the spec the VM was designed * for; in some cases, an image file can be successfully loaded and used * even when a particular block is ignored (possibly with the loss of * the new feature enabled by the data in the block). When a block can * be ignored by an old VM without losing the ability to correctly load * the image for the old VM's functionality, the mandatory flag will be * set to 0; when a new block carries information without which the * image cannot be properly loaded, the mandatory flag will be set to 1. */ #define VMIMAGE_DBF_MANDATORY 0x0001 /* ------------------------------------------------------------------------ */ /* * Constant pool image tracking structure. For each constant pool, we * maintain information on the locations in the image file of the pages * in the pool. */ /* number of entries in each subarray */ const size_t VMIMAGE_POOL_SUBARRAY_SIZE = 4096; /* page information structure */ struct CVmImagePool_pg { /* seek offset of the page */ long seek_pos; /* size of the page */ size_t page_size; /* XOR mask for the page */ uchar xor_mask; }; class CVmImagePool: public CVmPoolBackingStore { public: CVmImagePool(); ~CVmImagePool(); /* * Initialize the pool with a given number of pages and page size. * This can only be called once, and must be called before any page * locations can be established. */ void init(class CVmImageFile *fp, ulong page_count, ulong page_size); /* set a page's information */ void set_page_info(ulong page_idx, long seek_pos, size_t page_size, uchar xor_mask); /* apply an XOR mask to a block of bytes */ static void apply_xor_mask(char *p, size_t len, uchar xor_mask) { /* * apply the mask only if it's non-zero - xor'ing zero with * anything yields the original value, so we can avoid a lot of * pointless memory traversal by checking this first */ if (xor_mask != 0) { /* xor each byte with the xor mask */ for ( ; len != 0 ; --len, ++p) *p ^= xor_mask; } } /* -------------------------------------------------------------------- */ /* * CVmPoolBackingStore implementation */ /* get the total number of pages */ size_t vmpbs_get_page_count() { return page_count_; } /* get the common page size */ size_t vmpbs_get_common_page_size() { return page_size_; } /* get the size of a given page */ size_t vmpbs_get_page_size(pool_ofs_t ofs, size_t page_size) { return get_page_info_ofs(ofs)->page_size; } /* allocate and load a given page */ const char *vmpbs_alloc_and_load_page(pool_ofs_t ofs, size_t page_size, size_t load_size); /* free a page */ void vmpbs_free_page(const char *mem, pool_ofs_t ofs, size_t page_size); /* load a page into a given memory block */ void vmpbs_load_page(pool_ofs_t ofs, size_t page_size, size_t load_size, char *mem); /* determine if the backing store pages are writable */ int vmpbs_is_writable(); /* -------------------------------------------------------------------- */ private: /* compute the number of subarray pages we have */ size_t get_subarray_count() const { return (size_t)((page_count_ + VMIMAGE_POOL_SUBARRAY_SIZE - 1) / VMIMAGE_POOL_SUBARRAY_SIZE); } /* given a page index, get the information structure at the index */ CVmImagePool_pg *get_page_info(ulong idx) const { return &(page_info_[idx / VMIMAGE_POOL_SUBARRAY_SIZE] [idx % VMIMAGE_POOL_SUBARRAY_SIZE]); } /* given a pool offset, get the information structure for the page */ CVmImagePool_pg *get_page_info_ofs(pool_ofs_t ofs) const { return get_page_info(ofs / page_size_); } /* * Given a pool offset, seek to the image file data for the page, in * preparation for loading the data from the image file into memory. */ void seek_page_ofs(pool_ofs_t ofs); /* number of pages in the pool */ ulong page_count_; /* page size - each page in the pool has a common size */ ulong page_size_; /* * Page seek array. To accommodate 16-bit platforms, we keep this as a * set of arrays, with each subarray smaller than 64k. */ CVmImagePool_pg **page_info_; /* underlying image file */ class CVmImageFile *fp_; }; /* ------------------------------------------------------------------------ */ /* * Image loader. This takes an image file interface object (see below), * and loads the underlying image data into memory. */ class CVmImageLoader { public: /* initialize with an image file interface */ CVmImageLoader(class CVmImageFile *fp, const char *fname, long base_ofs); /* destruction */ ~CVmImageLoader(); /* * Load the image. */ void load(VMG0_); /* * Load a resource-only image file. 'fileno' is the file number * assigned by the host application (via the add_resfile() * interface) to the resource file. */ void load_resource_file(class CVmImageLoaderMres *res_ifc); /* load resources from a file handle at the current seek location */ static void load_resources_from_fp(osfildef *fp, const char *fname, class CVmHostIfc *hostifc); /* load resources from a file handle at the current seek location */ static void load_resources_from_fp(osfildef *fp, const char *fname, class CVmImageLoaderMres *res_ifc); /* * Run the image. This transfers control to the entrypoint defined in * the image file. This function doesn't return until the program * defined in the image terminates its execution by returning from the * entrypoint function or throwing an unhandled exception. * * If 'saved_state' is not null, it gives a null-terminated character * string with the name of a saved state file to be restored * immediately. We'll pass this information to the program's * entrypoint so it can handle the restore appropriately. * * The caller must create the code and constant pools before invoking * this. We'll set up the pools with their backing stores as loaded * from the image file. * * The global_symtab argument optionally provides the global symbol * table; if this is null, we'll use our own global symbol table that * we loaded from the debug records, if we found any. * * If an unhandled exception is thrown, this function throws * VMERR_UNHANDLED_EXC, with the exception object as the first * parameter. */ void run(VMG_ const char *const *argv, int argc, class CVmRuntimeSymbols *global_symtab, class CVmRuntimeSymbols *macro_symtab, const char *saved_state); /* run all static initializers */ void run_static_init(VMG0_); /* * Unload the image. This should be called after execution is * finished to disactivate the pools, which must be done before the * image file is deleted. */ void unload(VMG0_); /* * Create a global LookupTable to hold the symbols in the global * symbol table. */ void create_global_symtab_lookup_table(VMG0_); /* create a global LookupTable to hold the symbols in the macro table */ void create_macro_symtab_lookup_table(VMG0_); /* determine if the given block type identifiers match */ static int block_type_is(const char *type1, const char *type2) { /* compare the four-byte identifiers to see if they're identical */ return (memcmp(type1, type2, 4) == 0); } /* * Get the image file's timestamp. This is a 24-byte array in the * format "Sun Aug 01 17:05:20 1999". The purpose of the image file * timestamp is to provide a reasonably unique identifier that can * be stored in a saved state file and then checked upon loading the * file to ensure that it was created by the identical version of * the image file. */ const char *get_timestamp() const { return ×tamp_[0]; } /* get the filename of the loaded image */ const char *get_filename() const { return fname_; } /* get the fully-qualified, absolute directory path of the loaded image */ const char *get_path() const { return path_; } /* * check to see if the image file has a global symbol table (GSYM * block) */ int has_gsym() const { return has_gsym_ != 0; } /* * get the object ID of the LookupTable with the global symbol table * for reflection purposes */ vm_obj_id_t get_reflection_symtab() const { return reflection_symtab_; } /* get the object ID of the LookupTable with the macro table */ vm_obj_id_t get_reflection_macros() const { return reflection_macros_; } /* * perform dynamic linking after loading, resetting, or restoring */ void do_dynamic_link(VMG0_); /* * delete all synthesized exports - this must be called just prior to * resetting to image file state or loading a saved state */ void discard_synth_exports(); /* allocate a new property ID */ vm_prop_id_t alloc_new_prop(VMG0_); /* get the last property ID currently in use */ vm_prop_id_t get_last_prop(VMG0_); /* save/restore the synthesized export table */ void save_synth_exports(VMG_ class CVmFile *fp); int restore_synth_exports(VMG_ class CVmFile *fp, class CVmObjFixup *fixups); /* get the starting offset of static initializers in the code pool */ ulong get_static_cs_ofs() const { return static_cs_ofs_; } /* get the entrypoint function's code pool offset */ uint32_t get_entrypt() const { return entrypt_; } private: /* load external resource files associated with an image file */ void load_ext_resfiles(VMG0_); /* read and verify an image file header */ void read_image_header(); /* load an Entrypoint block */ void load_entrypt(VMG_ ulong siz); /* load a Static Object (OBJS) block */ void load_static_objs(VMG_ ulong siz); /* load a Constant Pool Definition (CPDF) block */ void load_const_pool_def(ulong siz); /* load a Constant Pool Page (CPPG) block */ void load_const_pool_page(ulong siz); /* load a Multimedia Resource (MRES) block */ void load_mres(ulong siz, class CVmImageLoaderMres *res_ifc); /* load a Multimedia Resource Link (MREL) block */ void load_mres_link(ulong size, class CVmImageLoaderMres *res_ifc); /* load a Metaclass Dependency block */ void load_meta_dep(VMG_ ulong siz); /* load a Function Set Dependency block */ void load_funcset_dep(VMG_ ulong siz); /* load a Symbolic Names block */ void load_sym_names(VMG_ ulong siz); /* load a Source Filenames block */ void load_srcfiles(VMG_ ulong siz); /* load a Global Symbols block */ void load_gsym(VMG_ ulong siz); /* load a Global Symbols (GSYM) block into the runtime symbol table */ void load_runtime_symtab_from_gsym(VMG_ ulong siz); /* load a Macro Symbols (MACR) block into the runtime symbol table */ void load_runtime_symtab_from_macr(VMG_ ulong siz); /* load a Macro Symbols (MACR) block */ void load_macros(VMG_ ulong siz); /* load a Method Header List block */ void load_mhls(VMG_ ulong siz); /* load a Static Initializer List block */ void load_sini(VMG_ ulong siz); /* * Fix up the debugging global symbol table's object entries with the * correct metaclass IDs. This has to wait until the whole image file * is loaded so that we're sure we have all of the objects loaded * already. */ void fix_gsym_meta(VMG0_); /* * Copy data from the file into a buffer, decrementing a size * counter. We'll throw a BLOCK_TOO_SMALL error if the read length * exceeds the remaining size. */ void read_data(char *buf, size_t read_len, ulong *remaining_size); /* skip data */ void skip_data(size_t skipo_len, ulong *remaining_size); /* * Allocate memory for data and read the data from the file, * decrementing the amount read from a size counter. Throws * BLOCK_TOO_SMALL if the read length exceeds the remaining size. */ const char *alloc_and_read(size_t read_len, ulong *remaining_size); /* add a resource to our resource table */ void add_resource(class CVmResource *res); /* * Add a symbol to the list of synthesized exports. Each time we * synthesize a value because we didn't find the associated symbol * exported from the image file, we must add an entry to this table. * On saving state, we'll save these symbols to the saved state file * so they will be restored on load. */ void add_synth_export_obj(const char *nm, vm_obj_id_t val); void add_synth_export_prop(const char *nm, vm_prop_id_t val); /* set the last property ID value */ void set_last_prop(VMG_ vm_prop_id_t last_prop); /* callback for synthesized export enumeration: save to file */ static void save_synth_export_cb(void *ctx, class CVmHashEntry *entry); /* underlying image file interface */ class CVmImageFile *fp_; /* image filename */ char *fname_; /* * fully-qualified, absolute directory path to the file (this is just * the directory path, sans the filename) */ char *path_; /* the base seek offset of the image stream within the image file */ long base_seek_ofs_; /* image file version number */ uint ver_; /* image file timestamp */ char timestamp_[24]; /* code pool offset of entrypoint function */ uint32_t entrypt_; /* pool tracking objects */ class CVmImagePool *pools_[2]; /* * The image's exported symbols. These are the symbols that the * program explicitly exported for dynamic linking from the VM. */ class CVmHashTable *exports_; /* * List of exports synthesized after loading by the VM. These exports * are not in the image file, so they must be saved in the saved state * file so that we can reattach to the same objects and properties on * restore. */ class CVmHashTable *synth_exports_; /* * The runtime global symbol table, if we have one. We'll build this * from the debug records if we find any, or from the records passed * in from the compiler when we run preinitialization. * * Note that the runtime global symbols are not the same as the * exported symbols. The exports are the symbols explicitly exported * for dynamic linking, so that the VM can attach to particular * objects defined in the image file. The runtime globals are all of * the compile-time global symbols as reflected in the debugging * records, and are used for reflection-type operations. */ class CVmRuntimeSymbols *runtime_symtab_; /* * The runtime macro definitions table, if we have one. As with the * runtime global symbol table, we build this from the debug records, * or from the records passed in from the compiler during preinit. */ class CVmRuntimeSymbols *runtime_macros_; /* object ID of LookupTable containing the global symbol table */ vm_obj_id_t reflection_symtab_; /* object ID of LookupTable containing the macro symbol table */ vm_obj_id_t reflection_macros_; /* head/tail of list of static initializer pages */ class CVmStaticInitPage *static_head_; class CVmStaticInitPage *static_tail_; /* * starting offset in code pool of static initializer code - the * compiler groups all static initializer code, and only static * initializer code, above this point, so after preinit, we can omit * all code above this point from the rewritten image file */ ulong static_cs_ofs_; /* flag: metaclass dependency table loaded */ uint loaded_meta_dep_ : 1; /* flag: function set dependency table loaded */ uint loaded_funcset_dep_ : 1; /* flag: entrypoint loaded */ uint loaded_entrypt_ : 1; /* * flag: the image file has a GSYM (global symbol table), which * implies that it was compiled for debugging */ uint has_gsym_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Static initializer page. Each page contains a fixed number of * initializers. */ const size_t VM_STATIC_INIT_PAGE_MAX = 1000; class CVmStaticInitPage { public: CVmStaticInitPage(size_t cnt) { /* remember the number of records */ cnt_ = cnt; /* no data yet */ data_ = 0; /* not in a list yet */ nxt_ = 0; } /* get an object/property ID given the index of a record */ vm_obj_id_t get_obj_id(size_t idx) { return vmb_get_objid(get_rec(idx)); } vm_prop_id_t get_prop_id(size_t idx) { return vmb_get_propid(get_rec(idx) + VMB_OBJECT_ID); } /* get a raw record pointer given an index */ const char *get_rec(size_t idx) { return data_ + (idx * 6); } /* next page in list */ CVmStaticInitPage *nxt_; /* number of records in the page */ size_t cnt_; /* * The data of the page. Each record is six bytes long, in portable * format: a UINT4 for the object ID, and a UINT2 for the property * ID. */ const char *data_; }; /* ------------------------------------------------------------------------ */ /* * Image file resource loader interface. This is an abstract class * interface that must be provided to load_resource_file() to provide * per-resource loading. */ class CVmImageLoaderMres { public: virtual ~CVmImageLoaderMres() { } /* load a resource */ virtual void add_resource(uint32_t seek_ofs, uint32_t siz, const char *res_name, size_t res_name_len) = 0; /* load a resource link */ virtual void add_resource(const char *fname, size_t fnamelen, const char *res_name, size_t res_name_len) = 0; }; /* ------------------------------------------------------------------------ */ /* * Image file interface. This is an abstract interface that provides * access to the data in an image file independently of the location of * the data. */ class CVmImageFile { public: /* delete the loader */ virtual ~CVmImageFile() { } /* duplicate the image file interface, a la stdio freopen() */ virtual CVmImageFile *dup(const char *mode) = 0; /* * Copy data from the image file to the caller's buffer. Reads from * the current file position. */ virtual void copy_data(char *buf, size_t len) = 0; /* * Allocate memory for and load data from the image file. Reads from * the current file position. Returns a pointer to the allocated data. * * 'remaining_in_page' is the amount of space remaining in the current * block being read from the image, including the space being read * here; this can be used as an upper bound for a new allocation if * the concrete subclass wishes to allocate blocks for suballocation. */ virtual const char *alloc_and_read(size_t len, uchar xor_mask, ulong remaining_in_page) = 0; /* * Determine if memory read with alloc_and_read() is writable. * Returns true if so, false if not. If the memory that * alloc_and_read() returns is a copy of the external file (rather * than mapped to the original external data, as might be the case * on a small machine without external storage, such as a palm-top * computer), this should return true. */ virtual int allow_write_to_alloc() = 0; /* free memory previously allocated by alloc_and_read */ virtual void free_mem(const char *mem) = 0; /* seek to a new file position, as an offset from the start of the file */ virtual void seek(long pos) = 0; /* get the current seek position */ virtual long get_seek() const = 0; /* skip the given number of bytes */ virtual void skip_ahead(long len) = 0; }; /* * Implementation of the generic stream interface for an image file block. * This will limit reading to the data in the block. */ class CVmImageFileStream: public CVmStream { public: CVmImageFileStream(CVmImageFile *fp, size_t len) { /* * remember the underlying image file, and the amount of space in * our data block */ fp_ = fp; len_ = len; } CVmStream *clone(VMG_ const char *mode) { /* duplicate our file handle */ CVmImageFile *fpdup = fp_->dup(mode); if (fpdup == 0) return 0; /* create a new image file stream wrapper for the duplicate handle */ return new CVmImageFileStream(fpdup, len_); } /* read bytes into a buffer */ virtual void read_bytes(char *buf, size_t len); virtual size_t read_nbytes(char *buf, size_t len); /* read a line (not used for this object) */ virtual char *read_line(char *buf, size_t len) { return 0; } /* write bytes */ virtual void write_bytes(const char *, size_t); /* get/set the seek position */ virtual long get_seek_pos() const { return fp_->get_seek(); } virtual void set_seek_pos(long pos) { fp_->seek(pos); } virtual long get_len() { return len_; } private: /* our underlying image file reader */ CVmImageFile *fp_; /* remaining data length in the underlying block */ size_t len_; }; /* ------------------------------------------------------------------------ */ /* * Image file interface - external disk file */ class CVmImageFileExt: public CVmImageFile { public: /* delete the image file loader */ ~CVmImageFileExt(); /* initialize with an underlying file */ CVmImageFileExt(class CVmFile *fp) { /* remember our file */ fp_ = fp; /* we don't have any suballocation blocks yet */ mem_head_ = mem_tail_ = 0; } /* duplicate the file interface */ virtual CVmImageFile *dup(const char *mode) { /* duplicate our file handle */ CVmFile *fpdup = fp_->dup(mode); if (fpdup == 0) return 0; /* return a new wrapper object for the duplicate handle */ return new CVmImageFileExt(fpdup); } /* * CVmImageFile interface implementation */ /* copy data to the caller's buffer */ void copy_data(char *buf, size_t len); /* allocate memory for and read data */ const char *alloc_and_read(size_t len, uchar xor_mask, ulong remaining_in_page); /* allow writing to alloc_and_read blocks */ virtual int allow_write_to_alloc() { return TRUE; } /* * Free memory previously allocated with alloc_and_read. We don't * need to do anything here; once we allocate and load a block, we * keep it in memory until the entire load image is deleted, at * which time we free all of the associated memory. */ void free_mem(const char *) { } /* seek to a new file position */ void seek(long pos); /* get the current seek position */ long get_seek() const; /* skip the given number of bytes */ void skip_ahead(long len); private: /* allocate memory for loading data */ char *alloc_mem(size_t siz, ulong remaining_in_page); /* the underlying file */ class CVmFile *fp_; /* * Memory block list. We keep a set of memory blocks for loading * data via the alloc_and_read() method. Rather than allocating an * individual "malloc" block for each alloc_and_read() call, we * allocate a large block, and suballocate memory out of the large * block. Since we don't know exactly how much memory we'll need in * advance, we take a guess at how large a block we need, * suballocate from the block until we fill it up, then allocate * another block and fill it up, then allocate another, and so on. * We keep all of the blocks we allocate in a linked list. */ class CVmImageFileExt_blk *mem_head_; class CVmImageFileExt_blk *mem_tail_; }; /* * aggregate allocation block size - use a size that should be reasonably * safe for 16-bit platforms (not over 64k, and a bit less to allow for * some malloc overhead) */ const size_t VMIMAGE_EXT_BLK_SIZE = 65000; /* * Memory tracking structure for external file reader. For each large * memory block we allocate (for suballocation), we allocate one of * these structures. */ class CVmImageFileExt_blk { public: /* create the block, allocating the given number of bytes for the block */ CVmImageFileExt_blk(size_t siz); /* delete the block */ ~CVmImageFileExt_blk(); /* suballocate memory out of the current block */ char *suballoc(size_t siz); /* next block in the list */ CVmImageFileExt_blk *nxt_; /* previous block in the list */ CVmImageFileExt_blk *prv_; /* pointer to the start of the block */ char *block_ptr_; /* number of bytes remaining in the block for future suballocations */ size_t rem_; /* pointer to next free byte of the block */ char *free_ptr_; }; /* ------------------------------------------------------------------------ */ /* * Image file interface - memory-mapped implementation. This * implementation assumes that the file is loaded into memory in a * contiguous chunk, which can be addressed linearly. */ class CVmImageFileMem: public CVmImageFile { public: ~CVmImageFileMem() { } /* initialize with an underlying block of pre-loaded data */ CVmImageFileMem(const char *mem, long len) { /* remember where our data are */ mem_ = mem; len_ = len; /* start at the beginning of the data */ pos_ = 0; } /* duplicate the file interface */ CVmImageFile *dup(const char *mode) { return new CVmImageFileMem(mem_, len_); } /* * CVmImageFile interface implementation */ /* copy data to the caller's buffer */ void copy_data(char *buf, size_t len); /* allocate memory for and read data */ const char *alloc_and_read(size_t len, uchar xor_mask, ulong remaining_in_page); /* * do not allow writing to alloc_and_read blocks, since we map these * blocks directly to the underlying in-memory data */ virtual int allow_write_to_alloc() { return FALSE; } /* * Free memory allocated by alloc_and_read. Since our underlying * file is entirely in memory to start with, we don't actually ever * allocate any memory; hence, we don't actually need to free any * memory here. */ void free_mem(const char *) { } /* seek to a new file position */ void seek(long pos) { pos_ = pos; } /* get the current seek position */ long get_seek() const { return pos_; } /* skip the given number of bytes */ void skip_ahead(long len) { pos_ += len; } private: /* the underlying memory block */ const char *mem_; /* size in bytes of the underlying memory block */ ulong len_; /* current offset within the memory block */ long pos_; }; #endif /* VMIMAGE_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmimg_nd.cpp����������������������������������������������������������������������0000664�0000000�0000000�00000003532�12650170723�0016165�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmimg_nd.cpp - T3 VM image file reader - NON-DEBUG implementation Function This file implements certain image file reader functions for non-debug versions of the interpreter. This implementation allows linking a smaller version of the interpreter executable that doesn't require a bunch of functionality only needed for an interactive debugger. Notes Modified 12/03/99 MJRoberts - Creation */ #include <stdlib.h> #include "t3std.h" #include "vmtype.h" #include "vmimage.h" #include "vmrunsym.h" /* ------------------------------------------------------------------------ */ /* * load a Method Header List block - this block is needed only for * interactive debugging, so we can simply ignore this information */ void CVmImageLoader::load_mhls(VMG_ ulong siz) { /* skip the block */ fp_->skip_ahead(siz); } /* ------------------------------------------------------------------------ */ /* * load a Global Symbols block */ void CVmImageLoader::load_gsym(VMG_ ulong siz) { /* load the data into the runtime reflection symbol table */ load_runtime_symtab_from_gsym(vmg_ siz); } /* * Fix up the global symbol table */ void CVmImageLoader::fix_gsym_meta(VMG0_) { } /* ------------------------------------------------------------------------ */ /* * load a Macro Symbols block - this block is used only in interactive * debugging, so simply ignore this information in this implementation */ void CVmImageLoader::load_macros(VMG_ ulong siz) { /* load the data into the runtime reflection macro table */ load_runtime_symtab_from_macr(vmg_ siz); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmimgrb.h�������������������������������������������������������������������������0000664�0000000�0000000�00000012612�12650170723�0015474�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmimgrb.h - image rebuilder Function Re-builds an image file from the loaded program's state. Useful for writing an image file after pre-initialization has bee completed. Notes Modified 07/21/99 MJRoberts - Creation */ #ifndef VMIMGRB_H #define VMIMGRB_H #include "vmtype.h" /* ------------------------------------------------------------------------ */ /* * Rewrite the image file. Copies an original image file to the given * file. */ void vm_rewrite_image(VMG_ CVmFile *origfp, CVmFile *newfp, ulong static_cs_start_ofs); /* ------------------------------------------------------------------------ */ /* * Object-to-Constant Mapper. * * When we're rebuilding an image file, we might want to convert all * string and list objects (i.e., instances of metaclass String or List) * into constant string and list values, respectively, adding these * values to the image file's constant pool rather than storing them as * object instances. Using constant pool data in an image file * increases a program's efficiency by reducing the number of active * instances required at run-time. * * During the conversion process, we need to store the constant data * that will go in the additional constant pool pages. We also need to * maintain a translation table that gives us the constant pool address * of each converted string or list value given its object ID. This * object maintains these tables. */ class CVmConstMapper { public: CVmConstMapper(VMG0_); ~CVmConstMapper(); /* * Get an object's constant pool address. Returns zero if the * object does not have an address mapped yet. */ ulong get_pool_addr(vm_obj_id_t obj_id); /* * Reserve space for an object's data in the new constant pool. * Returns the constant pool address of the reserved space, and adds * a translation mapping for the object to our table, so that future * calls to get_pool_addr(obj_id) will return this address. * * If the object is too large to fit on a constant pool page, we'll * return zero to indicate that the object cannot be stored in the * constant pool; in this case, the object must remain an object * instance rather than a constant item. * * This doesn't actually copy any data, but merely reserves space. */ ulong alloc_pool_space(vm_obj_id_t obj_id, size_t len); /* * Prepare to begin storing data. This must be called once, after * all pool space has been reserved and before the first object's * data are actually stored. */ void prepare_to_store_data(); /* * Store the object's data in its pre-reserved space in the rebuilt * constant pool. The space must have previously been allocated * with alloc_pool_space(), and the size used must be exactly the * same as the space allocated originally. */ void store_data(vm_obj_id_t obj_id, const void *ptr, size_t len); /* * get the number of pages we've stored - this is valid only after * prepare_to_store_data() has been called */ size_t get_page_count() const { return pages_cnt_; } /* * Write our pages to an image file. This routine can be called * only after all of the objects have been stored in the constant * pool. */ void write_to_image_file(class CVmImageWriter *writer, uchar xor_mask); protected: /* * Determine how many extra pages we need to add to the original * image file's constant pool for our mapped data. */ size_t calc_page_count() const { /* * calculate the total number of pages by dividing the page size * into the total number of bytes we've allocated, rounded up to * the next page whole page */ return (size_t)((next_free_ + (page_size_ - 1) - base_addr_) / page_size_); } /* * index of our first page - this will be the next page after the * last page used in the original image file */ size_t first_page_idx_; /* constant pool page size */ size_t page_size_; /* first address that we allocated */ ulong base_addr_; /* next free address for the allocation pass */ ulong next_free_; /* amount of space remaining on current pool allocation page */ size_t rem_; /* * Translation page array - each element of this array points to a * page that contains 1024 object ID translations. */ ulong **obj_addr_; /* number of page slots in obj_addr_ array */ size_t obj_addr_cnt_; /* * constant pool page data - we allocate the pages in * prepare_to_store_data(), which is called after we know how many * pages we'll need */ struct vm_const_mapper_page **pages_; size_t pages_cnt_; }; /* * mapper constant pool page */ struct vm_const_mapper_page { /* * high-water mark for data stored in the page, as an offset from * the start of the page's data */ size_t max_ofs_used; /* the page's data */ char buf[1]; }; #endif /* VMIMGRB_H */ ����������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmimport.h������������������������������������������������������������������������0000664�0000000�0000000�00000017423�12650170723�0015713�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmimport.h - T3 VM imported symbol list Function Defines the list of symbols to import from the image file. Each imported symbol must have an entry in this file. Notes Note that this file is NOT protected against multiple inclusion, because it is included in several different ways to generate different tables. Each includer must define the generator macros (VM_IMPORT, VM_IMP_xxx, etc) just before including this file. The macros are to be defined as needed by the includer to generate the appropriate table entries for that inclusion. Modified 02/23/01 MJRoberts - Creation */ /* if any of our macros are undefined, provide empty definitions now */ #ifndef VM_IMPORT_OBJ #define VM_IMPORT_OBJ(sym, member_name) #endif #ifndef VM_IMPORT_PROP #define VM_IMPORT_PROP(sym, member_name) #endif #ifndef VM_NOIMPORT_OBJ #define VM_NOIMPORT_OBJ(sym, member_name) #endif #ifndef VM_NOIMPORT_PROP #define VM_NOIMPORT_PROP(sym, member_name) #endif #ifndef VM_IMPORT_FUNC #define VM_IMPORT_FUNC(symm, member_name) #endif /* ------------------------------------------------------------------------ */ /* * This is the imported symbol table. Add symbols here as needed. Define * each imported symbol as follows: * * VM_IMPORT_OBJ("object_symbol_name", predef_member_name) - import an * object * * VM_IMPORT_PROP("prop_symbol_name", predef_member_name) - import a * property * * VM_NOIMPORT_OBJ("object_symbol_name", predef_member_name) - define a * named object, but do not generate an import for it; objects defined in * this way are created after load. Any object that the VM creates on its * own after load must be defined in this manner, so that the object can * be identified in saved state files and restored properly. * * VM_NOIMPORT_PROP("prop_symbol_name", predef_member_name) - define a * named property, but do not generate an import for it. */ /* the base class for VM run-time exceptions */ VM_IMPORT_OBJ("RuntimeError", rterr) /* * the property in which we store the exception message text for VM * run-time exceptions */ VM_IMPORT_PROP(VM_IMPORT_NAME_RTERRMSG, rterrmsg_prop) /* the constructor and destructor (finalizer) properties */ VM_IMPORT_PROP("Constructor", obj_construct) VM_IMPORT_PROP("Destructor", obj_destruct) /* the last property ID allocated by the compiler */ VM_IMPORT_PROP("LastProp", last_prop) /* the property used to invoke the function in an anonymous function object */ VM_IMPORT_PROP("ObjectCallProp", obj_call_prop) /* property invoked when an undefined property is invoked */ VM_IMPORT_PROP("propNotDefined", prop_not_defined_prop) /* object representing a frame in a stack trace (t3GetStackTrace) */ VM_IMPORT_OBJ("T3StackInfo", stack_info_cls) /* entrypoint function for restoring a saved state on startup */ VM_IMPORT_FUNC("mainRestore", main_restore_func) /* "length" method property */ VM_IMPORT_PROP("length", length_prop) /* * the objects used to represent constant strings and lists, respectively, * in method calls to such values */ VM_NOIMPORT_OBJ(VM_IMPORT_NAME_CONSTSTR, const_str_obj) VM_NOIMPORT_OBJ(VM_IMPORT_NAME_CONSTLST, const_lst_obj) /* * the array object that we use to keep track of the last allocated * property ID (we keep this in an object so that property ID allocations * can easily participate in the undo mechanism and are easily saved and * restored) */ VM_NOIMPORT_OBJ(VM_IMPORT_NAME_LASTPROPOBJ, last_prop_obj) /* storage server error */ VM_IMPORT_OBJ("StorageServerError", storage_server_error) /* String class imports */ VM_IMPORT_PROP("String.specialsToHtml.flags", string_sth_flags) VM_IMPORT_PROP("String.specialsToHtml.tag", string_sth_tag) /* * the properties that the GrammarProd intrinsic class uses to indicate in * a match tree the first and last token index of each match */ VM_IMPORT_PROP("GrammarProd.firstTokenIndex", gramprod_first_tok) VM_IMPORT_PROP("GrammarProd.lastTokenIndex", gramprod_last_tok) VM_IMPORT_PROP("GrammarProd.tokenList", gramprod_token_list) VM_IMPORT_PROP("GrammarProd.tokenMatchList", gramprod_token_match_list) VM_IMPORT_PROP("GrammarProd.grammarTag", gramprod_tag) VM_IMPORT_PROP("GrammarProd.miscVocab", misc_vocab) VM_IMPORT_PROP("GrammarProd.altProps", gramprod_alt_props) /* * classes that GrammarProd.getGrammarInfo() instantiates to store the * production description */ VM_IMPORT_OBJ("GrammarProd.GrammarAltInfo", gramprod_gram_alt_info) VM_IMPORT_OBJ("GrammarProd.GrammarAltTokInfo", gramprod_gram_alt_tok_info) /* * for the CharacterSet intrinsic class, the exception class for unknown * local character mappings */ VM_IMPORT_OBJ("CharacterSet.UnknownCharSetException", charset_unknown_exc) /* * exceptions for the File intrinsic class */ VM_IMPORT_OBJ("File.FileNotFoundException", file_not_found_exc) VM_IMPORT_OBJ("File.FileCreationException", file_creation_exc) VM_IMPORT_OBJ("File.FileOpenException", file_open_exc) VM_IMPORT_OBJ("File.FileIOException", file_io_exc) VM_IMPORT_OBJ("File.FileSyncException", file_sync_exc) VM_IMPORT_OBJ("File.FileClosedException", file_closed_exc) VM_IMPORT_OBJ("File.FileModeException", file_mode_exc) VM_IMPORT_OBJ("File.FileSafetyException", file_safety_exc) VM_IMPORT_PROP("FileSpec.getFilename", filespec_getFilename) VM_IMPORT_PROP("FileSpec.closeFile", filespec_closeFile) VM_IMPORT_OBJ("File.FileInfo", file_info) /* exceptions for the DynamicFunc intrinsic class */ VM_IMPORT_OBJ("DynamicFunc.CompilerException", compiler_exc) /* properties for comparators */ VM_IMPORT_PROP("IfcComparator.calcHash", calc_hash_prop) VM_IMPORT_PROP("IfcComparator.matchValues", match_values_prop) /* properties for overloaded operators */ VM_IMPORT_PROP("operator +", operator_add) VM_IMPORT_PROP("operator -", operator_sub) VM_IMPORT_PROP("operator *", operator_mul) VM_IMPORT_PROP("operator /", operator_div) VM_IMPORT_PROP("operator %", operator_mod) VM_IMPORT_PROP("operator ^", operator_xor) VM_IMPORT_PROP("operator <<", operator_shl) VM_IMPORT_PROP("operator >>", operator_ashr) VM_IMPORT_PROP("operator >>>", operator_lshr) VM_IMPORT_PROP("operator ~", operator_bit_not) VM_IMPORT_PROP("operator &", operator_bit_and) VM_IMPORT_PROP("operator |", operator_bit_or) VM_IMPORT_PROP("operator negate", operator_neg) VM_IMPORT_PROP("operator []", operator_idx) VM_IMPORT_PROP("operator []=", operator_setidx) /* objects and properties for the TADS Networking package */ VM_IMPORT_OBJ("TadsNet.NetException", net_exception) VM_IMPORT_OBJ("TadsNet.SocketDisconnectException", net_reset_exception) VM_IMPORT_OBJ("TadsNet.NetSafetyException", net_safety_exception) VM_IMPORT_OBJ("TadsNet.NetEvent", net_event) VM_IMPORT_OBJ("TadsNet.NetRequestEvent", net_request_event) VM_IMPORT_OBJ("TadsNet.NetTimeoutEvent", net_timeout_event) VM_IMPORT_OBJ("TadsNet.NetReplyEvent", net_reply_event) VM_IMPORT_OBJ("TadsNet.NetReplyDoneEvent", net_reply_done_event) VM_IMPORT_OBJ("TadsNet.FileUpload", file_upload_cl) VM_IMPORT_PROP("TadsNet.FileUpload.file", file_upload_fileObj) VM_IMPORT_PROP("TadsNet.FileUpload.contentType", file_upload_contentType) VM_IMPORT_PROP("TadsNet.FileUpload.filename", file_upload_filename) /* reflection services */ VM_IMPORT_OBJ("reflection.reflectionServices", reflection_services) VM_IMPORT_PROP("reflection.valToSymbol", reflection_valToSymbol) /* general-purpose object-to-string formatting */ VM_IMPORT_PROP("objToString", objToString) /* * now that we've built the table, undefine the macros used to build it, * so that future includers can redefine the macros differently */ #undef VM_IMPORT_OBJ #undef VM_IMPORT_PROP #undef VM_NOIMPORT_OBJ #undef VM_NOIMPORT_PROP #undef VM_IMPORT_FUNC ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmini_nd.cpp����������������������������������������������������������������������0000664�0000000�0000000�00000002750�12650170723�0016171�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmini_nd.cpp - VM initialization - non-debug version Function Stub routines for initialization related to the debugger. This module should be linked into non-debug builds of the interpreter, so that debugger-related files are omitted from the executable. Notes Modified 12/03/99 MJRoberts - Creation */ #include "t3std.h" #include "vminit.h" #include "vmparam.h" /* * Initialize the debugger */ void vm_init_debugger(VMG0_) { /* non-debug version - nothing to do */ } /* * Initialization, phase 2: just before loading image file */ void vm_init_before_load(VMG_ const char *image_filename) { /* non-debug version - nothing to do */ } /* * Initialization, phase 3: just after loading the image file */ void vm_init_after_load(VMG0_) { /* non-debug version - nothing to do */ } /* * Termination - shut down the debugger */ void vm_terminate_debug_shutdown(VMG0_) { /* non-debug version - nothing to do */ } /* * Termination - delete debugger objects */ void vm_terminate_debug_delete(VMG0_) { /* non-debug version - nothing to do */ } /* * In the non-debug version, use the basic stack reserve parameter. */ size_t vm_init_stack_reserve() { return VM_STACK_RESERVE; } ������������������������qtads-2.1.7/tads3/vminit.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000027073�12650170723�0015701�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMINIT.CPP,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vminit.cpp - initialize and terminate VM processing Function Provides functions to create and destroy global objects for a VM session. Notes Modified 04/06/99 MJRoberts - Creation */ #include <string.h> #include "t3std.h" #include "charmap.h" #include "vminit.h" #include "vmerr.h" #include "vmfile.h" #include "vmimage.h" #include "vmpool.h" #include "vmobj.h" #include "vmstack.h" #include "vmundo.h" #include "vmmeta.h" #include "vmbif.h" #include "vmrun.h" #include "vmpredef.h" #include "vmmcreg.h" #include "vmbiftad.h" #include "resload.h" #include "vmhost.h" #include "vmconsol.h" #include "vmbignum.h" #include "vmsrcf.h" #include "vmparam.h" #include "vmmain.h" #include "vmtobj.h" #include "osifcnet.h" #include "vmhash.h" #include "vmtz.h" /* ------------------------------------------------------------------------ */ /* * Perform base initialization. This is an internal routine called only * by higher-level initialization routines; we perform all of the * generic, configuration-independent initialization. */ void vm_init_base(vm_globals **vmg, const vm_init_options *opts) { /* * Allocate globals according to build-time configuration, then * assign the global pointer to a local named vmg__. This will * ensure that the globals are accessible for all of the different * build-time configurations. */ vm_globals *vmg__ = *vmg = vmglob_alloc(); /* * in configurations where globals aren't passed as parameters, vmg__ * will never be referenced; explicitly reference it so that the * compiler knows the assignment above is intentional even if it's not * ever used in this particular build configuration */ (void)vmg__; /* initialize the error stack */ err_init(VM_ERR_STACK_BYTES); /* get the system resource loader from the host interface */ CResLoader *map_loader = opts->hostifc->get_sys_res_loader(); /* if an external message set hasn't been loaded, try loading one */ if (!err_is_message_file_loaded() && map_loader != 0) { /* try finding a message file */ osfildef *fp = map_loader->open_res_file( VM_ERR_MSG_FNAME, 0, VM_ERR_MSG_RESTYPE); if (fp != 0) { /* * try loading it - if that fails, we'll just be left with * the built-in messages, so we won't have lost anything for * trying */ err_load_vm_message_file(fp); /* we're done with the file */ osfcls(fp); } } /* remember the host interface */ G_host_ifc = opts->hostifc; /* initialize the system debug log file name */ char path[OSFNMAX]; opts->hostifc->get_special_file_path(path, sizeof(path), OS_GSP_LOGFILE); os_build_full_path(G_syslogfile, sizeof(G_syslogfile), path, "tadslog.txt"); /* create the object table */ VM_IF_ALLOC_PRE_GLOBAL(G_obj_table = new CVmObjTable()); G_obj_table->init(vmg0_); /* * Create the memory manager. Empirically, our hybrid heap allocator * is faster than the standard C++ run-time library's allocator on many * platforms, so use it instead of hte basic 'malloc' allocator. */ G_varheap = new CVmVarHeapHybrid(G_obj_table); // G_varheap = new CVmVarHeapMalloc(); to use the system 'malloc' instead G_mem = new CVmMemory(vmg_ G_varheap); /* create the undo manager */ G_undo = new CVmUndo(VM_UNDO_MAX_RECORDS, VM_UNDO_MAX_SAVEPTS); /* create the metafile and function set tables */ G_meta_table = new CVmMetaTable(5); G_bif_table = new CVmBifTable(5); /* create the time zone cache */ G_tzcache = new CVmTimeZoneCache(); /* initialize the metaclass registration tables */ vm_register_metaclasses(); /* initialize the TadsObject class */ CVmObjTads::class_init(vmg0_); /* create the byte-code interpreter */ VM_IFELSE_ALLOC_PRE_GLOBAL( G_interpreter = new CVmRun(VM_STACK_SIZE, vm_init_stack_reserve()), G_interpreter->init()); /* presume we won't create debugger information */ G_debugger = 0; G_srcf_table = 0; /* presume we don't have a network configuration */ G_net_config = 0; /* initialize the debugger if present */ vm_init_debugger(vmg0_); /* create the source file table */ G_srcf_table = new CVmSrcfTable(); /* create the pre-defined object mapper */ VM_IFELSE_ALLOC_PRE_GLOBAL(G_predef = new CVmPredef, G_predef->reset()); /* presume we're in normal execution mode (not preinit) */ G_preinit_mode = FALSE; /* allocate the TADS intrinsic function set's globals */ G_bif_tads_globals = new CVmBifTADSGlobals(vmg0_); /* allocate the BigNumber register cache */ CVmObjBigNum::init_cache(); /* no image loader yet */ G_image_loader = 0; /* * If the caller explicitly specified a character set, use it. * Otherwise, ask the OS layer for the default character set we * should use. */ char disp_mapname[32]; const char *charset = opts->charset; if (charset == 0) { /* the user did not specify a mapping - ask the OS for the default */ os_get_charmap(disp_mapname, OS_CHARMAP_DISPLAY); /* use the name we got from the OS */ charset = disp_mapname; /* there's no explicit global character set name setting to store */ G_disp_cset_name = 0; } else { /* save the global character set name */ G_disp_cset_name = lib_copy_str(charset); } /* create the display character maps */ G_cmap_from_ui = CCharmapToUni::load(map_loader, charset); G_cmap_to_ui = CCharmapToLocal::load(map_loader, charset); /* create the filename character maps */ char filename_mapname[32]; os_get_charmap(filename_mapname, OS_CHARMAP_FILENAME); G_cmap_from_fname = CCharmapToUni::load(map_loader, filename_mapname); G_cmap_to_fname = CCharmapToLocal::load(map_loader, filename_mapname); /* create the file-contents character maps */ char filecont_mapname[32]; os_get_charmap(filecont_mapname, OS_CHARMAP_FILECONTENTS); G_cmap_from_file = CCharmapToUni::load(map_loader, filecont_mapname); G_cmap_to_file = CCharmapToLocal::load(map_loader, filecont_mapname); /* * If the caller specified a separate log-file character set, create * the mapping. Otherwise, just use the to-file mapper for log files. */ if (opts->log_charset != 0) { /* load the specified log file output mapping */ G_cmap_to_log = CCharmapToLocal::load(map_loader, opts->log_charset); } else { /* no log file mapping is specified, so use the generic file map */ if ((G_cmap_to_log = G_cmap_to_file) != 0) G_cmap_to_log->add_ref(); } /* make a note of whether we had any problems loading the maps */ int disp_map_err = (G_cmap_from_ui == 0 || G_cmap_to_ui == 0); /* if we failed to create any of the maps, load defaults */ if (G_cmap_from_ui == 0) G_cmap_from_ui = CCharmapToUni::load(map_loader, "us-ascii"); if (G_cmap_to_ui == 0) G_cmap_to_ui = CCharmapToLocal::load(map_loader, "us-ascii"); if (G_cmap_from_fname == 0) G_cmap_from_fname = CCharmapToUni::load(map_loader, "us-ascii"); if (G_cmap_to_fname == 0) G_cmap_to_fname = CCharmapToLocal::load(map_loader, "us-ascii"); if (G_cmap_from_file == 0) G_cmap_from_file = CCharmapToUni::load(map_loader, "us-ascii"); if (G_cmap_to_file == 0) G_cmap_to_file = CCharmapToLocal::load(map_loader, "us-ascii"); if (G_cmap_to_log == 0) G_cmap_to_log = CCharmapToLocal::load(map_loader, "us-ascii"); /* create the primary console */ G_console = opts->clientifc->create_console(VMGLOB_ADDR); /* * if we had any trouble opening the display character mapping file, * make a note that we are using a default mapping */ if (disp_map_err) { const char *msg; char buf[256]; /* get the message */ msg = err_get_msg(vm_messages, vm_message_count, VMERR_NO_CHARMAP_FILE, TRUE); /* format it */ sprintf(buf, msg, charset); /* display it */ opts->clientifc->display_error(VMGLOB_ADDR, 0, buf, TRUE); } } /* ------------------------------------------------------------------------ */ /* * Terminate the VM */ void vm_terminate(vm_globals *vmg__, CVmMainClientIfc *clientifc) { /* tell the debugger to shut down, if necessary */ vm_terminate_debug_shutdown(vmg0_); /* drop all undo information */ G_undo->drop_undo(vmg0_); /* delete the main console */ clientifc->delete_console(VMGLOB_ADDR, G_console); /* release references on the character mappers */ G_cmap_from_fname->release_ref(); G_cmap_to_fname->release_ref(); G_cmap_from_ui->release_ref(); G_cmap_to_ui->release_ref(); G_cmap_from_file->release_ref(); G_cmap_to_file->release_ref(); G_cmap_to_log->release_ref(); /* delete the saved UI character set name, if any */ lib_free_str(G_disp_cset_name); /* delete the BigNumber register cache */ CVmObjBigNum::term_cache(); /* delete the TADS intrinsic function set's globals */ delete G_bif_tads_globals; /* delete the predefined object table */ VM_IF_ALLOC_PRE_GLOBAL(delete G_predef); /* delete the interpreter */ VM_IFELSE_ALLOC_PRE_GLOBAL(delete G_interpreter, G_interpreter->terminate()); /* terminate the TadsObject class */ CVmObjTads::class_term(vmg0_); /* delete the source file table */ delete G_srcf_table; /* delete debugger objects */ vm_terminate_debug_delete(vmg0_); /* delete dynamic compilation objects */ if (G_dyncomp != 0) { delete G_dyncomp; G_dyncomp = 0; } /* delete the constant pools */ VM_IFELSE_ALLOC_PRE_GLOBAL(delete G_code_pool, G_code_pool->terminate()); VM_IFELSE_ALLOC_PRE_GLOBAL(delete G_const_pool, G_const_pool->terminate()); /* * Clear the object table. This deletes garbage-collected objects but * doesn't delete the object table itself; we'll do that after we've * cleared out the metaclass and function-set tables. We need to * remove the gc objects before the metaclasses and function tables * because the gc objects sometimes depend on their metaclasses. But * the metaclasses and function sets sometimes keep VM globals, so we * need to keep the object table around until after they're cleaned up. */ G_obj_table->clear_obj_table(vmg0_); /* delete the dependency tables */ G_bif_table->clear(vmg0_); delete G_meta_table; delete G_bif_table; /* delete the undo manager */ delete G_undo; /* delete the object table */ G_obj_table->delete_obj_table(vmg0_); VM_IF_ALLOC_PRE_GLOBAL(delete G_obj_table); /* delete the memory manager and heap manager */ delete G_mem; delete G_varheap; /* delete the time zone cache */ delete G_tzcache; /* delete the error context */ err_terminate(); /* delete the globals */ vmglob_delete(vmg__); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vminit.h��������������������������������������������������������������������������0000664�0000000�0000000�00000010720�12650170723�0015335�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMINIT.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vminit.h - initialize and terminate a VM session Function Notes VM initialization is configurable via the linker. To select a configuration, link in exactly one of the vmcfgxxx.cpp object files. Modified 04/06/99 MJRoberts - Creation */ #ifndef VMINIT_H #define VMINIT_H #include "vmglob.h" /* * Generic initialization function. Client code calls this function to * initialize the VM. The actual implementation of this function is * selected at link time from the several provided configurations. * * 'charset' is the name of the display character mapping that we should * use. If 'charset' is null, we'll use the default character set * obtained from the OS layer. */ void vm_initialize(struct vm_globals **vmg, const struct vm_init_options *opts); /* * Initialization options - this structure is passed to vm_initialize to * specify the configuration. (We pass this as a structure, rather than as * individual parameters, because we internally have several different * implementations of the initialization function that we choose from based * on the VM link-time configuration. Packaging these arguments as a * structure generally lets us avoid changes to the various implementations * when we add new configuration parameters.) */ struct vm_init_options { vm_init_options(class CVmHostIfc *hi, class CVmMainClientIfc *ci, const char *cs = 0, const char *lcs = 0) { hostifc = hi; clientifc = ci; charset = cs; log_charset = lcs; } /* the host interface */ class CVmHostIfc *hostifc; /* the client interface */ class CVmMainClientIfc *clientifc; /* the display character set (or null to use the OS default) */ const char *charset; /* the log file character set (or null to use the OS default) */ const char *log_charset; }; /* * Initialization, phase 2 - just before loading the image file */ void vm_init_before_load(VMG_ const char *image_filename); /* * Initialization, phase 3 - just after loading the image file */ void vm_init_after_load(VMG0_); /* * Terminate the VM. Deletes all objects created by vm_init(). This * can be used to clean up memory after a program has finished * executing. */ void vm_terminate(struct vm_globals *vmg, class CVmMainClientIfc *clientifc); /* ------------------------------------------------------------------------ */ /* * Private interface. Client code does not call any of the following * routines; they're for use internally by the initialization mechanism * only. */ /* * Initialize the VM with in-memory pools. Creates all global objects * necessary for the VM to run. */ void vm_init_in_mem(struct vm_globals **vmg, const vm_init_options *opts); /* initialize the VM with the "flat" memory pool manager */ void vm_init_flat(struct vm_globals **vmg, const vm_init_options *opts); /* * Initialize the VM with swapping pools with the given maximum number * of pages in memory. */ void vm_init_swap(struct vm_globals **vmg, size_t max_mem_pages, const vm_init_options *opts); /* * Internal base initialization routine. Clients do not call this * routine directly; it's for use by vm_init_in_mem(), vm_init_swap(), * and any other vm_init_xxx routines. */ void vm_init_base(struct vm_globals **vmg, const vm_init_options *opts); /* * Initialize debugger-related objects. For non-debug versions, this * doesn't need to do anything. */ void vm_init_debugger(VMG0_); /* * Get the amount of stack space to allocate as a reserve for handling * stack overflows in the debugger. For non-debug versions, we don't * normally use a reserve, since the reserve is only useful for manually * recovering from stack overflows in the debugger. */ size_t vm_init_stack_reserve(); /* * Termination - shut down the debugger. Notifies the debugger UI that * we're terminating the executable. */ void vm_terminate_debug_shutdown(VMG0_); /* * termination - delete debugger-related objects, if any were allocated * in vm_init_debugger() */ void vm_terminate_debug_delete(VMG0_); #endif /* VMINIT_H */ ������������������������������������������������qtads-2.1.7/tads3/vminitfl.cpp����������������������������������������������������������������������0000664�0000000�0000000�00000001754�12650170723�0016221�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vminitim.cpp - VM initialization - "flat" pool implementation Function Notes Modified 07/21/99 MJRoberts - Creation */ #include "vminit.h" #include "vmpool.h" #include "vmglob.h" /* ------------------------------------------------------------------------ */ /* * Initialize the VM with the "flat" pool manager */ void vm_init_flat(vm_globals **vmg, const vm_init_options *opts) { vm_globals *vmg__; /* initialize the base VM structures */ vm_init_base(vmg, opts); /* * assign the global pointer to the special vmg__ local for * globals-on-stack configuration */ vmg__ = *vmg; /* create the flat pools */ VM_IF_ALLOC_PRE_GLOBAL(G_code_pool = new CVmPoolFlat()); VM_IF_ALLOC_PRE_GLOBAL(G_const_pool = new CVmPoolFlat()); } ��������������������qtads-2.1.7/tads3/vmintcls.cpp����������������������������������������������������������������������0000664�0000000�0000000�00000046142�12650170723�0016230�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmintcls.cpp - T3 metaclass - intrinsic class Function Notes Modified 03/08/00 MJRoberts - Creation */ #include <stdlib.h> #include <assert.h> #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" #include "vmintcls.h" #include "vmmeta.h" #include "vmfile.h" #include "vmlst.h" #include "vmstack.h" #include "vmtobj.h" /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassClass metaclass_reg_obj; CVmMetaclass *CVmObjClass::metaclass_reg_ = &metaclass_reg_obj; /* ------------------------------------------------------------------------ */ /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjClass::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { /* it is illegal to create this type of object dynamically */ err_throw(VMERR_ILLEGAL_NEW); AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* * create with no initial contents */ vm_obj_id_t CVmObjClass::create(VMG_ int in_root_set) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmObjClass(); return id; } /* * create with a given dependency index */ vm_obj_id_t CVmObjClass::create_dyn(VMG_ uint meta_idx) { vm_obj_id_t id = vm_new_id(vmg_ FALSE, TRUE, FALSE); new (vmg_ id) CVmObjClass(vmg_ FALSE, meta_idx, id); return id; } /* allocate our extension */ vm_intcls_ext *CVmObjClass::alloc_ext(VMG0_) { /* if I already have an extension, return it */ if (ext_ != 0) return (vm_intcls_ext *)ext_; /* allocate the new extension */ vm_intcls_ext *ext = (vm_intcls_ext *)G_mem->get_var_heap()->alloc_mem( sizeof(vm_intcls_ext), this); /* save it */ ext_ = (char *)ext; /* return it */ return ext; } /* * create with a given dependency index */ CVmObjClass::CVmObjClass(VMG_ int in_root_set, uint meta_idx, vm_obj_id_t self) { /* calls of this form can't come from the image file */ assert(!in_root_set); /* allocate the extension */ ext_ = 0; vm_intcls_ext *ext = alloc_ext(vmg0_); /* set up the extension */ ext->meta_idx = meta_idx; ext->mod_obj = VM_INVALID_OBJ; ext->class_state.set_nil(); ext->changed = FALSE; /* register myself with the metaclass table */ register_meta(vmg_ self); } /* * notify of deletion */ void CVmObjClass::notify_delete(VMG_ int in_root_set) { /* free our extension */ if (ext_ != 0 && !in_root_set) G_mem->get_var_heap()->free_mem(ext_); } /* set a property */ void CVmObjClass::set_prop(VMG_ CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) { /* try treating the request as a static property of the metaclass */ vm_meta_entry_t *entry = get_meta_entry(vmg0_); if (entry != 0 && entry->meta_->set_stat_prop( vmg_ undo, self, &get_ext()->class_state, prop, val)) return; /* the class doesn't handle it, so check for a modifier object */ vm_obj_id_t mod_obj = get_mod_obj(); if (mod_obj != VM_INVALID_OBJ) { /* we have a modifier - set the property in the modifier */ vm_objp(vmg_ mod_obj)->set_prop(vmg_ undo, mod_obj, prop, val); } else { /* if we don't have a modifier, we can't set the property */ err_throw(VMERR_INVALID_SETPROP); } } /* * Get a list of our properties */ void CVmObjClass::build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval) { vm_obj_id_t mod_obj; vm_meta_entry_t *entry; size_t my_prop_cnt; size_t mod_prop_cnt; vm_val_t mod_val; CVmObjList *lst; CVmObjList *mod_lst; /* presume we won't find any static properties of our own */ my_prop_cnt = 0; /* get my metaclass table entry */ entry = get_meta_entry(vmg0_); /* if we have an entry, count the properties */ if (entry != 0) my_prop_cnt = list_class_props(vmg_ self, entry, 0, 0, FALSE); /* if we have a modifier object, get its property list */ if ((mod_obj = get_mod_obj()) != VM_INVALID_OBJ) { /* get the modifier's property list - we'll add it to our own */ vm_objp(vmg_ mod_obj)->build_prop_list(vmg_ self, &mod_val); /* get the result as a list object, properly cast */ mod_lst = (CVmObjList *)vm_objp(vmg_ mod_val.val.obj); /* * As an optimization, if we don't have any properties of our own * to add to the modifier list, just return the modifier list * directly (thus avoiding unnecessarily creating another copy of * the list with no changes). */ if (my_prop_cnt == 0) { /* the modifier list is the entire return value */ *retval = mod_val; return; } /* get the size of the modifier list */ mod_prop_cnt = vmb_get_len(mod_lst->get_as_list()); } else { /* * we have no modifier object - for the result list, we simply * need our own list, so set the modifier list to nil */ mod_val.set_nil(); mod_prop_cnt = 0; mod_lst = 0; } /* for gc protection, push the modifier's list */ G_stk->push(&mod_val); /* * Allocate a list big enough to hold the modifier's list plus our own * list. */ retval->set_obj(CVmObjList:: create(vmg_ FALSE, my_prop_cnt + mod_prop_cnt)); /* push the return value list for gc protection */ G_stk->push(retval); /* get it as a list object, properly cast */ lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); lst->cons_clear(); /* start the list with our own properties */ if (entry != 0) list_class_props(vmg_ self, entry, lst, 0, FALSE); /* copy the modifier list into the results, if there is a modifier list */ if (mod_prop_cnt != 0) lst->cons_copy_elements(my_prop_cnt, mod_lst->get_as_list()); /* done with the gc protection */ G_stk->discard(2); } /* * mark references for garbage collection */ void CVmObjClass::mark_refs(VMG_ uint state) { /* if we have an object in our class state value, mark it */ vm_intcls_ext *ext = get_ext(); if (ext->class_state.typ == VM_OBJ) G_obj_table->mark_all_refs(ext->class_state.val.obj, state); } /* * List my metaclass's properties. Returns the number of properties we * find. We'll add the properties to the given list; if the list is null, * we'll simply return the count. */ size_t CVmObjClass::list_class_props(VMG_ vm_obj_id_t self, vm_meta_entry_t *entry, CVmObjList *lst, size_t starting_idx, int static_only) { ushort i; size_t cnt; size_t idx; /* count the valid entries */ for (i = 1, idx = starting_idx, cnt = 0 ; i <= entry->func_xlat_cnt_ ; ++i) { vm_prop_id_t prop; vm_val_t val; vm_obj_id_t source_obj; /* get this property */ prop = entry->xlat_func(i); /* * If this one's valid, check to see if it's we're interested in * it. If we want only statics, include it only if it's * implemented as a static method on this intrinsic class object; * otherwise, include it unconditionally. * * To determine if the property is implemented as a static, call * the property on self without an argc pointer - this is the * special form of the call which merely retrieves the value of the * property without evaluating it. If the property is defined on * the object, and the source of the definition is self, include * this one in the list of directly-defined properties. */ if (prop != VM_INVALID_PROP && (!static_only || (get_prop(vmg_ prop, &val, self, &source_obj, 0) && source_obj == self))) { /* we're interested - add it to the list if we have one */ if (lst != 0) { /* set up a value containing the property pointer */ val.set_propid(prop); /* add it to the list at the next free slot */ lst->cons_set_element(idx, &val); /* advance to the next slot */ ++idx; } /* count it */ ++cnt; } } /* return the number of properties we found */ return cnt; } /* * get a static property */ int CVmObjClass::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* translate the property into a function vector index */ switch(G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop)) { case 1: /* isIntrinsicClass(x) */ return s_getp_is_int_class(vmg_ result, argc); default: /* * we don't define this one - inherit the default from the base * object metaclass */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } } /* * static method: isIntrinsicClass(x) */ int CVmObjClass::s_getp_is_int_class(VMG_ vm_val_t *result, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(result, argc, &desc)) return TRUE; /* return true if we have an object of type intrinsic class */ vm_val_t *v = G_stk->get(0); result->set_logical(v->typ == VM_OBJ && is_intcls_obj(vmg_ v->val.obj)); /* discard the argument */ G_stk->discard(); /* handled */ return TRUE; } /* * get a property */ int CVmObjClass::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { /* * pass the property request as a static property of the metaclass * with which we're associated */ vm_meta_entry_t *entry = get_meta_entry(vmg0_); if (entry != 0 && entry->meta_->call_stat_prop(vmg_ val, 0, argc, prop)) { /* the metaclass object handled it, so we are the definer */ *source_obj = self; return TRUE; } /* * Try handling it through the modifier object, if we have one. Note * that we must search our own intrinsic superclass chain, because our * own true intrinsic class is 'intrinsic class,' but we want to expose * the nominal superclass hierarchy of the intrinsic class itself (for * example, we want to search List->Collection->Object, not * List->IntrinsicClass->Object). */ if (get_prop_from_mod(vmg_ prop, val, self, source_obj, argc)) return TRUE; /* inherit default handling */ return CVmObject::get_prop(vmg_ prop, val, self, source_obj, argc); } /* * Get a property from an intrinsic class modifier object. Look up the * property in my own modifier, and recursively in my superclass modifiers. */ int CVmObjClass::get_prop_from_mod(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { vm_obj_id_t sc; vm_obj_id_t mod_obj; /* if we have a modifier object, look it up in the modifier */ if ((mod_obj = get_mod_obj()) != VM_INVALID_OBJ && vm_objp(vmg_ mod_obj)->get_prop(vmg_ prop, val, mod_obj, source_obj, argc)) { /* got it - return it from the modifier */ return TRUE; } /* * it's not in our modifier(s); check with any intrinsic superclass * modifiers */ if (get_superclass_count(vmg_ self) != 0 && (sc = get_superclass(vmg_ self, 0)) != VM_INVALID_OBJ) { /* we have a superclass - check it recursively */ return ((CVmObjClass *)vm_objp(vmg_ sc)) ->get_prop_from_mod(vmg_ prop, val, sc, source_obj, argc); } /* * we didn't find it, and we have no superclass, so there's nowhere * else to look - return failure */ return FALSE; } /* * Find the intrinsic class which the given modifier object modifies. This * can only be used with a modifier that modifies me or one of my * superclasses. */ vm_obj_id_t CVmObjClass::find_mod_src_obj(VMG_ vm_obj_id_t self, vm_obj_id_t mod_obj) { vm_obj_id_t my_mod_obj; vm_obj_id_t sc; /* * Is this one of my modifier objects? It is if it's my most * specialized modifier object (i.e., get_mod_obj()), or if my most * specialized modifier object descends from the given object. */ my_mod_obj = get_mod_obj(); if (my_mod_obj != VM_INVALID_OBJ && (mod_obj == my_mod_obj || vm_objp(vmg_ my_mod_obj)->is_instance_of(vmg_ mod_obj))) { /* it's one of mine, so I'm the intrinsic class mod_obj modifies */ return self; } /* * It's not one of mine, so check my superclasses recursively. If we * have no direct superclass, we've failed to find the object. */ if (get_superclass_count(vmg_ self) == 0 || (sc = get_superclass(vmg_ self, 0)) == VM_INVALID_OBJ) return VM_INVALID_OBJ; /* ask the superclass to find the modifier */ return ((CVmObjClass *)vm_objp(vmg_ sc)) ->find_mod_src_obj(vmg_ sc, mod_obj); } /* * Get my metaclass table entry */ vm_meta_entry_t *CVmObjClass::get_meta_entry(VMG0_) const { /* get my metaclass table index */ uint meta_idx = get_meta_idx(); /* look up my metaclass table entry, if we have one */ if (meta_idx < G_meta_table->get_count()) return G_meta_table->get_entry(meta_idx); else return 0; } /* * save to a file */ void CVmObjClass::save_to_file(VMG_ class CVmFile *fp) { /* write our data: data size, meta table index, mod ID, class state */ vm_intcls_ext *ext = get_ext(); fp->write_uint2(2 + 2 + 4 + VMB_DATAHOLDER); fp->write_uint2(ext->meta_idx); fp->write_uint4(ext->mod_obj); char buf[VMB_DATAHOLDER]; vmb_put_dh(buf, &ext->class_state); fp->write_bytes(buf, VMB_DATAHOLDER); } /* * restore from a file */ void CVmObjClass::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { /* allocate/reallocate the extension */ vm_intcls_ext *ext = alloc_ext(vmg0_); /* read the length */ size_t len = fp->read_uint2(); const size_t expected_len = 2+2+4+VMB_DATAHOLDER; /* * read the metaclass index and modifier object ID; note that modifier * objects are always root set objects, so there's no need to worry * about fixups */ ext->meta_idx = fp->read_uint2(); ext->mod_obj = (vm_obj_id_t)fp->read_uint4(); /* if we have a class state value, read it */ ext->class_state.set_nil(); if (len >= 2+2+4+VMB_DATAHOLDER) { char buf[VMB_DATAHOLDER]; fp->read_bytes(buf, VMB_DATAHOLDER); fixups->fix_dh(vmg_ buf); vmb_get_dh(buf, &ext->class_state); } /* skip any extra data we don't recognize */ if (len > expected_len) fp->set_pos_from_cur(len - expected_len); /* register myself with the metaclass table */ register_meta(vmg_ self); } /* load from an image file */ void CVmObjClass::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load the image data */ load_image_data(vmg_ self, ptr, siz); /* we need the image pointer saved for re-loading on RESTART */ G_obj_table->save_image_pointer(self, ptr, siz); } /* reload from an image file */ void CVmObjClass::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load the image data */ load_image_data(vmg_ self, ptr, siz); } /* load image data */ void CVmObjClass::load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* make sure the length is valid */ if (siz < 8) err_throw(VMERR_INVAL_METACLASS_DATA); /* allocate or reallocate the extension */ vm_intcls_ext *ext = alloc_ext(vmg0_); /* * read the metaclass index and modifier object ID; note that modifier * objects are always root set objects, so there's no need to worry * about fixups */ ext->meta_idx = osrp2(ptr+2); ext->mod_obj = (vm_obj_id_t)t3rp4u(ptr+4); /* if we have a class state value, read it */ ext->class_state.set_nil(); if (siz >= 2+4+VMB_DATAHOLDER) vmb_get_dh(ptr+8, &ext->class_state); /* register myself */ register_meta(vmg_ self); } /* * Register myself with the metaclass dependency table - this establishes * the association between the metaclass in the dependency table and this * instance, so that the metaclass knows about the instance that * represents it. */ void CVmObjClass::register_meta(VMG_ vm_obj_id_t self) { /* get my metaclass table entry */ vm_meta_entry_t *entry = get_meta_entry(vmg0_); /* * if we have a valid entry, store a reference to myself in the * metaclass table - this will let the metaclass that we represent find * us when asked for its class object */ if (entry != 0) entry->class_obj_ = self; } /* * Get the number of superclasses */ int CVmObjClass::get_superclass_count(VMG_ vm_obj_id_t) const { /* get my metaclass table entry */ vm_meta_entry_t *entry = get_meta_entry(vmg0_); /* * if we have a valid entry, ask the metaclass object to tell us the * superclass count */ if (entry != 0) return entry->meta_->get_supermeta_count(vmg0_); else return 0; } /* * Get a superclass */ vm_obj_id_t CVmObjClass::get_superclass(VMG_ vm_obj_id_t, int sc_idx) const { /* get my metaclass table entry */ vm_meta_entry_t *entry = get_meta_entry(vmg0_); /* * if we have a valid entry, ask the metaclass object to retrieve the * superclass */ if (entry != 0) return entry->meta_->get_supermeta(vmg_ sc_idx); else return VM_INVALID_OBJ; } /* * Determine if I'm an instance of the given object */ int CVmObjClass::is_instance_of(VMG_ vm_obj_id_t obj) { /* get my metaclass table entry */ vm_meta_entry_t *entry = get_meta_entry(vmg0_); /* if we have a valid entry, ask the metaclass object */ if (entry != 0) { /* ask the metaclass if it's an instance of the given object */ return entry->meta_->is_meta_instance_of(vmg_ obj); } else { /* not a valid metaclass - we can't make sense of this */ return FALSE; } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmintcls.h������������������������������������������������������������������������0000664�0000000�0000000�00000026417�12650170723�0015700�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. */ /* Name vmintcls.h - T3 metaclass - intrinsic class Function Notes Modified 03/08/00 MJRoberts - Creation */ #ifndef VMINTCLS_H #define VMINTCLS_H #include <stdlib.h> #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" /* * An IntrinsicClass object represents the class of an instance of an * intrinsic class. For example, if we create a BigNumber instance, then * ask for its class, the result is the IntrinsicClass object associated * with the BigNumber intrinsic class: * * Each metaclass in the metaclass dependency table will be associated with * an IntrinsicClass object. * * The image file format for an IntrinsicClass object consists of the * following: * * UINT2 byte_count (currently, this is always 8) *. UINT2 metaclass_dependency_table_index *. UINT4 modifier_object_id *. DATAHOLDER class_state * * The 'class_state' entry is a generic data holder value for use by the * intrinsic class for storing static class state. We save and restore * this value automatically and can keep undo information for it. This can * be used to implement static class-level setprop/getprop to store special * values associated with the class object. */ /* intrinsic class extension */ struct vm_intcls_ext { /* metaclass dependency table index */ int meta_idx; /* modifier object */ vm_obj_id_t mod_obj; /* class state */ vm_val_t class_state; /* has the class state value changed since load time? */ int changed; }; /* * intrinsic class object */ class CVmObjClass: public CVmObject { friend class CVmMetaclassClass; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is the given object an IntrinsicClass object? */ static int is_intcls_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* * get my metaclass registration table index - this can be compared to * the metaclass_reg_ element for a given C++ intrinsic class * implementation to determine if this intrinsic class object is the * intrinsic class object for a given C++ intrinsic class */ uint get_meta_idx() const { return get_ext()->meta_idx; } /* get my metaclass table entry */ struct vm_meta_entry_t *get_meta_entry(VMG0_) const; /* * Get the class state value. This is for use by the class * implementation to store static (class) state. */ const vm_val_t *get_class_state() { return &get_ext()->class_state; } /* Set the class state value, without saving undo. */ void set_class_state(const vm_val_t *val) { get_ext()->class_state = *val; get_ext()->changed = TRUE; } /* * Set the class state value with undo. This is unimplemented for now, * since no one needs it. In principle it could be useful to be able * to store a scalar value in the class state slot, in which case we'd * need to have special code here in order to undo changes. So far, * though, we always store an object reference in class_state, such as * a Vector or TadsObject, since we need to be able to store more than * just a single scalar value. Since all of the state data are then * stored within those container objects, and the container classes we * use all handle undo themselves, there's no need for any undo action * at the class_state level. */ // Unimplemented! void set_class_state_undo(VMG_ class CVmUndo *undo, const vm_val_t *val); /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* create for a given dependency table index */ static vm_obj_id_t create_dyn(VMG_ uint meta_idx); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop); /* determine if I'm an instance of the given object */ virtual int is_instance_of(VMG_ vm_obj_id_t obj); /* get superclass information */ int get_superclass_count(VMG_ vm_obj_id_t self) const; vm_obj_id_t get_superclass(VMG_ vm_obj_id_t self, int idx) const; /* * Determine if this is a class object. All intrinsic class objects * indicate true. */ virtual int is_class_object(VMG_ vm_obj_id_t /*self*/) const { return TRUE; } /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self) { /* * we reference no other data and cannot be converted to constant * data ourselves, so there's nothing to do here */ } /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self) { /* * we reference no other data and cannot be converted to constant * data ourselves, so there's nothing to do here */ } /* create with no initial contents */ static vm_obj_id_t create(VMG_ int in_root_set); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* build a list of my properties */ void build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval); /* undo operations - classes are immutable and hence keep no undo */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { } void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* mark references */ void mark_refs(VMG_ uint state); /* remove weak references - we have none */ void remove_stale_weak_refs(VMG0_) { } /* have we changed since load time? */ int is_changed_since_load() const { return get_ext()->changed; } /* load/reload from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* get the user modifier object for the intrinsic class */ vm_obj_id_t get_mod_obj() const { return get_ext()->mod_obj; } /* * find the intrinsic class for the given modifier object, searching * myself and my intrinsic superclasses */ vm_obj_id_t find_mod_src_obj(VMG_ vm_obj_id_t self, vm_obj_id_t mod_obj); /* static method: isIntrinsicClass(x) */ static int s_getp_is_int_class(VMG_ vm_val_t *result, uint *argc); protected: /* create with no initial contents */ CVmObjClass() { ext_ = 0; } /* create with a given dependency table index */ CVmObjClass(VMG_ int in_root_set, uint meta_idx, vm_obj_id_t self); /* allocate or reallocate my extension */ vm_intcls_ext *alloc_ext(VMG0_); /* get my extension */ vm_intcls_ext *get_ext() const { return (vm_intcls_ext *)ext_; } /* load/reload the image data */ void load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* list our intrinsic class's properties */ size_t list_class_props(VMG_ vm_obj_id_t self, struct vm_meta_entry_t *entry, class CVmObjList *lst, size_t starting_idx, int static_only); /* * Find the intrinsic class for the given modifier object. We override * this because we want an intrinsic class's effective intrinsic class * to be its intrinsic superclass, not its metaclass. The metaclass of * an intrinsic class object is always IntrinsicClass; instead, we want * to see, for example, List->Collection->Object, which is the * intrinsic superclass hierarchy. */ vm_obj_id_t find_intcls_for_mod(VMG_ vm_obj_id_t self, vm_obj_id_t mod_obj) { /* * The implementation is very simple: just look for a modifier * object attached to this object or one of its intrinsic * superclasses. The difference between this and the regular * CVmObject implementation is that the CVmObject implementation * looks in the object's metaclass; we simply look in our intrinsic * superclasses directly, since, for reflection purposes, we are * our own metaclass. */ return find_mod_src_obj(vmg_ self, mod_obj); } /* * search for a property among our modifiers, searching our own * modifier and modifiers for our superclasses */ int get_prop_from_mod(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* register myself with the dependency table */ void register_meta(VMG_ vm_obj_id_t self); }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassClass: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "intrinsic-class/030001"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjClass(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjClass(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjClass::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjClass::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMINTCLS_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjClass) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmisaac.cpp�����������������������������������������������������������������������0000664�0000000�0000000�00000011575�12650170723�0016016�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmisaac.cpp - T3 VM ISAAC random number generator implementation Function Notes Modified 04/11/10 MJRoberts - Creation */ #include "vmisaac.h" /* service macros for ISAAC random number generator */ #define isaac_ind(mm,x) ((mm)[(x>>2)&(ISAAC_RANDSIZ-1)]) #define isaac_step(mix,a,b,mm,m,m2,r,x) \ { \ x = *m; \ a = ((a^(mix)) + *(m2++)) & 0xffffffff; \ *(m++) = y = (isaac_ind(mm,x) + a + b) & 0xffffffff; \ *(r++) = b = (isaac_ind(mm,y>>ISAAC_RANDSIZL) + x) & 0xffffffff; \ } #define isaac_mix(a,b,c,d,e,f,g,h) \ { \ a^=b<<11; d+=a; b+=c; \ b^=c>>2; e+=b; c+=d; \ c^=d<<8; f+=c; d+=e; \ d^=e>>16; g+=d; e+=f; \ e^=f<<10; h+=e; f+=g; \ f^=g>>4; a+=f; g+=h; \ g^=h<<8; b+=g; h+=a; \ h^=a>>9; c+=h; a+=b; \ } /* generate the group of numbers */ void isaac_gen_group(isaacctx *ctx) { uint32_t a; uint32_t b; uint32_t x; uint32_t y; uint32_t *m; uint32_t *mm; uint32_t *m2; uint32_t *r; uint32_t *mend; mm = ctx->mem; r = ctx->rsl; a = ctx->a; b = (ctx->b + (++ctx->c)) & 0xffffffff; for (m = mm, mend = m2 = m + (ISAAC_RANDSIZ/2) ; m<mend ; ) { isaac_step(a<<13, a, b, mm, m, m2, r, x); isaac_step(a>>6, a, b, mm, m, m2, r, x); isaac_step(a<<2, a, b, mm, m, m2, r, x); isaac_step(a>>16, a, b, mm, m, m2, r, x); } for (m2 = mm; m2<mend; ) { isaac_step(a<<13, a, b, mm, m, m2, r, x); isaac_step(a>>6, a, b, mm, m, m2, r, x); isaac_step(a<<2, a, b, mm, m, m2, r, x); isaac_step(a>>16, a, b, mm, m, m2, r, x); } ctx->b = b; ctx->a = a; } /* * Initialize. If flag is true, then use the contents of ctx->rsl[] to * initialize ctx->mm[]; otherwise, we'll use a fixed starting * configuration. */ void isaac_init(isaacctx *ctx, int flag) { int i; uint32_t a; uint32_t b; uint32_t c; uint32_t d; uint32_t e; uint32_t f; uint32_t g; uint32_t h; uint32_t *m; uint32_t *r; ctx->a = ctx->b = ctx->c = 0; m = ctx->mem; r = ctx->rsl; a = b = c = d = e = f = g = h = 0x9e3779b9; /* the golden ratio */ /* scramble the initial settings */ for (i = 0 ; i < 4 ; ++i) { isaac_mix(a, b, c, d, e, f, g, h); } if (flag) { /* initialize using the contents of ctx->rsl[] as the seed */ for (i = 0 ; i < ISAAC_RANDSIZ ; i += 8) { a += r[i]; b += r[i+1]; c += r[i+2]; d += r[i+3]; e += r[i+4]; f += r[i+5]; g += r[i+6]; h += r[i+7]; isaac_mix(a, b, c, d, e, f, g, h); m[i] = a; m[i+1] = b; m[i+2] = c; m[i+3] = d; m[i+4] = e; m[i+5] = f; m[i+6] = g; m[i+7] = h; } /* do a second pass to make all of the seed affect all of m */ for (i = 0 ; i < ISAAC_RANDSIZ ; i += 8) { a += m[i]; b += m[i+1]; c += m[i+2]; d += m[i+3]; e += m[i+4]; f += m[i+5]; g += m[i+6]; h += m[i+7]; isaac_mix(a, b, c, d, e, f, g, h); m[i] = a; m[i+1] = b; m[i+2] = c; m[i+3] = d; m[i+4] = e; m[i+5] = f; m[i+6] = g; m[i+7] = h; } } else { /* initialize using fixed initial settings */ for (i = 0 ; i < ISAAC_RANDSIZ ; i += 8) { isaac_mix(a, b, c, d, e, f, g, h); m[i] = a; m[i+1] = b; m[i+2] = c; m[i+3] = d; m[i+4] = e; m[i+5] = f; m[i+6] = g; m[i+7] = h; } } /* fill in the first set of results */ isaac_gen_group(ctx); /* prepare to use the first set of results */ ctx->cnt = ISAAC_RANDSIZ; } /* * Get the internal state */ size_t isaac_get_state(isaacctx *ctx, char *buf) { /* if the didn't provide a buffer, it's a size-needed query */ const size_t sz = 4 * (4 + ISAAC_RANDSIZ + ISAAC_RANDSIZ); if (buf == 0) return sz; /* copy the scalar members */ oswp4(buf, ctx->cnt); buf += 4; oswp4(buf, ctx->a); buf += 4; oswp4(buf, ctx->b); buf += 4; oswp4(buf, ctx->c); buf += 4; /* copy the array members */ for (int i = 0 ; i < ISAAC_RANDSIZ ; ++i, buf += 8) { oswp4(buf, ctx->rsl[i]); oswp4(buf+4, ctx->mem[i]); } return sz; } /* * Set the internal state */ void isaac_set_state(isaacctx *ctx, const char *buf) { /* decode the buffer into our context */ ctx->cnt = osrp4(buf); buf += 4; ctx->a = osrp4(buf); buf += 4; ctx->b = osrp4(buf); buf += 4; ctx->c = osrp4(buf); buf += 4; for (int i = 0 ; i < ISAAC_RANDSIZ ; ++i, buf += 8) { ctx->rsl[i] = osrp4(buf); ctx->mem[i] = osrp4(buf+4); } /* sanity check the result: make sure 'cnt' is in range */ if (ctx->cnt > ISAAC_RANDSIZ) ctx->cnt = 0; } �����������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmisaac.h�������������������������������������������������������������������������0000664�0000000�0000000�00000003114�12650170723�0015451�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmisaac.h - TADS 3 VM ISAAC random number generator implementation Function Notes Modified 04/11/10 MJRoberts - Creation */ #ifndef VMISAAC_H #define VMISAAC_H #include "t3std.h" /* data block size */ #define ISAAC_RANDSIZL (8) #define ISAAC_RANDSIZ (1<<ISAAC_RANDSIZL) /* ISAAC RNG context */ struct isaacctx { /* RNG context */ uint32_t cnt; uint32_t rsl[ISAAC_RANDSIZ]; uint32_t mem[ISAAC_RANDSIZ]; uint32_t a; uint32_t b; uint32_t c; }; #define isaac_rand(r) \ ((r)->cnt-- == 0 ? \ (isaac_gen_group(r), (r)->cnt=ISAAC_RANDSIZ-1, (r)->rsl[(r)->cnt]) : \ (r)->rsl[(r)->cnt]) /* * Initialize. If flag is true, then use the contents of ctx->rsl[] to * initialize ctx->mm[]; otherwise, we'll use a fixed starting * configuration. */ void isaac_init(isaacctx *ctx, int flag); /* * Get/set the internal state. This allows saving and restoring the * internal state of the RNG. 'get' returns the size of the byte buffer * required; call with buf==0 to determine the size needed. * * 'get' and 'set' both expect the caller to know the correct size from a * size-query call to 'get'. The size passed to 'set' must always exactly * match the size returned from 'get', since anything else could corrupt * the internal state. */ size_t isaac_get_state(isaacctx *ctx, char *buf); void isaac_set_state(isaacctx *ctx, const char *buf); /* generate a group of random values */ void isaac_gen_group(isaacctx *ctx); #endif /* VMISAAC_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmiter.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000030024�12650170723�0015667�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmiter.cpp - T3 iterator metaclass Function Notes Modified 04/22/00 MJRoberts - Creation */ #include <stdlib.h> #include "vmtype.h" #include "vmobj.h" #include "vmiter.h" #include "vmglob.h" #include "vmmeta.h" #include "vmstack.h" #include "vmundo.h" #include "vmlst.h" #include "vmfile.h" /* ------------------------------------------------------------------------ */ /* * Base Iterator metaclass */ /* * statics */ /* metaclass registration object */ static CVmMetaclassIter iter_metaclass_reg_obj; CVmMetaclass *CVmObjIter::metaclass_reg_ = &iter_metaclass_reg_obj; /* function table */ int (CVmObjIter:: *CVmObjIter::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjIter::getp_undef, &CVmObjIter::getp_get_next, &CVmObjIter::getp_is_next_avail, &CVmObjIter::getp_reset_iter, &CVmObjIter::getp_get_cur_key, &CVmObjIter::getp_get_cur_val }; /* * get a property */ int CVmObjIter::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { /* translate the property into a function vector index */ uint func_idx = G_meta_table->prop_to_vector_idx( metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from the base object class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * Indexed Iterator metaclass */ /* * statics */ /* metaclass registration object */ static CVmMetaclassIterIdx idx_metaclass_reg_obj; CVmMetaclass *CVmObjIterIdx::metaclass_reg_ = &idx_metaclass_reg_obj; /* create a list with no initial contents */ vm_obj_id_t CVmObjIterIdx::create_for_coll(VMG_ const vm_val_t *coll, long first_valid_index, long last_valid_index) { vm_obj_id_t id; /* push the collection object reference for gc protection */ G_stk->push(coll); /* create a non-root-set object */ id = vm_new_id(vmg_ FALSE, TRUE, FALSE); /* instantiate the iterator */ new (vmg_ id) CVmObjIterIdx(vmg_ coll, first_valid_index, last_valid_index); /* done with the gc protection */ G_stk->discard(); /* return the new object's ID */ return id; } /* * constructor */ CVmObjIterIdx::CVmObjIterIdx(VMG_ const vm_val_t *coll, long first_valid_index, long last_valid_index) { /* allocate space for our extension data */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(VMOBJITERIDX_EXT_SIZE, this); /* save the collection value */ vmb_put_dh(ext_, coll); /* * set the current index to the first index minus 1, so that we start * with the first element when we make our first call to getNext() */ set_cur_index_no_undo(first_valid_index - 1); /* remember the first and last valid index values */ set_first_valid(first_valid_index); set_last_valid(last_valid_index); /* clear the flags */ set_flags(0); } /* * notify of deletion */ void CVmObjIterIdx::notify_delete(VMG_ int) { /* free our extension */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* * property evaluator - get the current item's value */ int CVmObjIterIdx::getp_get_cur_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { long idx; static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the current index */ idx = get_cur_index(); /* if the current value is out of range, throw an error */ if (idx < 1 || idx > get_last_valid()) err_throw(VMERR_OUT_OF_RANGE); /* retrieve the value for this index */ get_indexed_val(vmg_ idx, retval); /* handled */ return TRUE; } /* * property evaluator - get the current item's key */ int CVmObjIterIdx::getp_get_cur_key(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { long idx; static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the current index */ idx = get_cur_index(); /* if the current value is out of range, throw an error */ if (idx < 1 || idx > get_last_valid()) err_throw(VMERR_OUT_OF_RANGE); /* return the index */ retval->set_int(idx); /* handled */ return TRUE; } /* * property evaluator - get the next item */ int CVmObjIterIdx::getp_get_next(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the next index, which is one higher than the current index */ long idx = get_cur_index() + 1; /* if the current value is out of range, throw an error */ if (idx > get_last_valid()) err_throw(VMERR_OUT_OF_RANGE); /* retrieve the value */ get_indexed_val(vmg_ idx, retval); /* save the current index in our internal state */ set_cur_index(vmg_ self, idx); /* handled */ return TRUE; } /* * get the next iteration item */ int CVmObjIterIdx::iter_next(VMG_ vm_obj_id_t self, vm_val_t *val) { /* get the next index, which is one higher than the current index */ long idx = get_cur_index() + 1; /* if the current value is out of range, throw an error */ if (idx <= get_last_valid()) { /* retrieve the value */ get_indexed_val(vmg_ idx, val); /* save the current index in our internal state */ set_cur_index(vmg_ self, idx); /* we got a value */ return TRUE; } else { /* no more values available */ return FALSE; } } /* * Retrieve an indexed value from my collection */ void CVmObjIterIdx::get_indexed_val(VMG_ long idx, vm_val_t *retval) { vm_val_t coll; vm_val_t idx_val; /* get my collection value and the next index value */ get_coll_val(&coll); /* check to see if we have an object or a constant list */ switch(coll.typ) { case VM_LIST: /* it's a constant list - index the constant value */ CVmObjList::index_list(vmg_ retval, coll.get_as_list(vmg0_), idx); break; case VM_OBJ: /* it's an object - index it */ idx_val.set_int(idx); vm_objp(vmg_ coll.val.obj) ->index_val_ov(vmg_ retval, coll.val.obj, &idx_val); break; default: /* * Anything else is an error. We really should never be able to * get here, since the only way to instantiate an iterator should * be via the collection object's createIter() method; so if we * get here it must be an internal error, hence we could probably * assert failure here. Nonetheless, just throw an error, since * this will make for a more pleasant indication of the problem. */ err_throw(VMERR_CANNOT_INDEX_TYPE); break; } } /* * property evaluator - is next value available? */ int CVmObjIterIdx::getp_is_next_avail(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* * if the current index plus one is less than or equal to the last * valid index, another item is available */ retval->set_logical(get_cur_index() + 1 <= get_last_valid()); /* handled */ return TRUE; } /* * property evaluator - reset to first item */ int CVmObjIterIdx::getp_reset_iter(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* set the index to the first valid index minus one */ set_cur_index(vmg_ self, get_first_valid() - 1); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* * Set the current index value, saving undo if necessary */ void CVmObjIterIdx::set_cur_index(VMG_ vm_obj_id_t self, long idx) { /* save undo if necessary */ if (G_undo != 0 && !(get_flags() & VMOBJITERIDX_UNDO)) { vm_val_t dummy; /* * Add the undo record. Note that the only information we need * to store is the index value, so we can store this as the key * value - supply a dummy payload, since we have no use for it. */ dummy.set_nil(); G_undo->add_new_record_int_key(vmg_ self, get_cur_index(), &dummy); /* * set the undo bit so we don't save redundant undo for this * savepoint */ set_flags(get_flags() | VMOBJITERIDX_UNDO); } /* set the index */ set_cur_index_no_undo(idx); } /* * apply undo */ void CVmObjIterIdx::apply_undo(VMG_ CVmUndoRecord *rec) { /* * the integer key in the undo record is my saved index value (and * is the only thing in an indexed iterator that can ever change) */ set_cur_index_no_undo(rec->id.intval); } /* * mark references */ void CVmObjIterIdx::mark_refs(VMG_ uint state) { vm_val_t coll; /* if my collection is an object, mark it as referenced */ get_coll_val(&coll); if (coll.typ == VM_OBJ) G_obj_table->mark_all_refs(coll.val.obj, state); } /* * load from an image file */ void CVmObjIterIdx::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* if we already have memory allocated, free it */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* * Allocate a new extension. Make sure it's at least as large as * the current standard extension size. */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(siz < VMOBJITERIDX_EXT_SIZE ? VMOBJITERIDX_EXT_SIZE : siz, this); /* copy the image data to our extension */ memcpy(ext_, ptr, siz); /* clear the undo flag */ set_flags(get_flags() & ~VMOBJITERIDX_UNDO); /* save our image data pointer, so we can use it for reloading */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from an image file */ void CVmObjIterIdx::reload_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t siz) { /* copy the image data over our data */ memcpy(ext_, ptr, siz); /* clear the undo flag */ set_flags(get_flags() & ~VMOBJITERIDX_UNDO); } /* * save */ void CVmObjIterIdx::save_to_file(VMG_ CVmFile *fp) { /* write my extension */ fp->write_bytes(ext_, VMOBJITERIDX_EXT_SIZE); } /* * restore */ void CVmObjIterIdx::restore_from_file(VMG_ vm_obj_id_t, CVmFile *fp, CVmObjFixup *fixups) { /* free any existing extension */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* allocate a new extension */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(VMOBJITERIDX_EXT_SIZE, this); /* read my extension */ fp->read_bytes(ext_, VMOBJITERIDX_EXT_SIZE); /* fix up my collection object reference */ fixups->fix_dh(vmg_ ext_); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmiter.h��������������������������������������������������������������������������0000664�0000000�0000000�00000027070�12650170723�0015343�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmiter.h - Iterator metaclass Function Notes Modified 04/22/00 MJRoberts - Creation */ #ifndef VMITER_H #define VMITER_H #include <stdlib.h> #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" /* ------------------------------------------------------------------------ */ /* * Base Iterator class */ class CVmObjIter: public CVmObject { friend class CVmMetaclassIter; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* set a property */ void set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { /* cannot set iterator properties */ err_throw(VMERR_INVALID_SETPROP); } /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); protected: /* property evaluator - undefined property */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - get next value */ virtual int getp_get_next(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = 0; /* property evaluator - is next value available? */ virtual int getp_is_next_avail(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = 0; /* property evaluator - reset to first item */ virtual int getp_reset_iter(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = 0; /* property evaluator - get current key */ virtual int getp_get_cur_key(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = 0; /* property evaluator - get current value */ virtual int getp_get_cur_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = 0; /* function table */ static int (CVmObjIter::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * Indexed Iterator subclass. An indexed iterator works with arrays, * lists, and other collections that can be accessed via an integer * index. */ /* * The extension data for an indexed iterator consists of a reference to * the associated indexed collection, the index value of the next item * to be retrieved, and the first and last valid index values: Note that * the collection value can be an object ID or a constant VM_LIST value. * * DATAHOLDER collection_value *. UINT4 cur_index *. UINT4 first_valid *. UINT4 last_valid *. UINT4 flags * * The flag values are: * * VMOBJITERIDX_UNDO - we've saved undo for this savepoint. If this is * set, we won't save additional undo for the same savepoint. */ /* total extension size */ #define VMOBJITERIDX_EXT_SIZE (VMB_DATAHOLDER + 16) /* * flag bits */ /* we've saved undo for the current savepoint */ #define VMOBJITERIDX_UNDO 0x0001 /* * indexed iterator class */ class CVmObjIterIdx: public CVmObjIter { friend class CVmMetaclassIterIdx; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObjIter::is_of_metaclass(meta)); } /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjIter::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* * Create an indexed iterator. This method is to be called by a * list, array, or other indexed collection object to create an * iterator for its value. */ static vm_obj_id_t create_for_coll(VMG_ const vm_val_t *coll, long first_valid_index, long last_valid_index); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* * notify of a new savepoint - clear the 'undo' flag, since we * cannot have created any undo information yet for the new * savepoint */ void notify_new_savept() { set_flags(get_flags() & ~VMOBJITERIDX_UNDO); } /* apply undo */ void apply_undo(VMG_ struct CVmUndoRecord *rec); /* mark references */ void mark_refs(VMG_ uint state); /* there are no references in our undo stream */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* we keep only strong references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* restore to image file state */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* * determine if the object has been changed since it was loaded - * assume we have, since saving and reloading are very cheap */ int is_changed_since_load() const { return TRUE; } /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* get the next iteration item */ virtual int iter_next(VMG_ vm_obj_id_t self, vm_val_t *val); protected: /* create */ CVmObjIterIdx() { ext_ = 0; } /* create */ CVmObjIterIdx(VMG_ const vm_val_t *coll, long first_valid_index, long last_valid_index); /* get the value from the collection for a given index */ void get_indexed_val(VMG_ long idx, vm_val_t *retval); /* property evaluator - get next value */ virtual int getp_get_next(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - is next value available? */ virtual int getp_is_next_avail(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - reset to first item */ virtual int getp_reset_iter(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get current key */ virtual int getp_get_cur_key(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get current value */ virtual int getp_get_cur_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get my collection value */ void get_coll_val(vm_val_t *val) { vmb_get_dh(ext_, val); } /* get/set the current index (without saving undo) */ long get_cur_index() const { return t3rp4u(ext_ + VMB_DATAHOLDER); } void set_cur_index_no_undo(long idx) { oswp4(ext_ + VMB_DATAHOLDER, idx); } /* set the index value, saving undo if necessary */ void set_cur_index(VMG_ vm_obj_id_t self, long idx); /* get my first/last valid index values */ long get_first_valid() const { return t3rp4u(ext_ + VMB_DATAHOLDER + 4); } long get_last_valid() const { return t3rp4u(ext_ + VMB_DATAHOLDER + 8); } /* set my first/last valid index values - for construction only */ void set_first_valid(long idx) const { oswp4(ext_ + VMB_DATAHOLDER + 4, idx); } void set_last_valid(long idx) const { oswp4(ext_ + VMB_DATAHOLDER + 8, idx); } /* get/set the flags */ unsigned long get_flags() const { return t3rp4u(ext_ + VMB_DATAHOLDER + 12); } void set_flags(unsigned long flags) const { oswp4(ext_ + VMB_DATAHOLDER + 12, flags); } }; /* ------------------------------------------------------------------------ */ /* * Registration table object for the base iterator class */ class CVmMetaclassIter: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "iterator/030001"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { err_throw(VMERR_BAD_STATIC_NEW); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { err_throw(VMERR_BAD_STATIC_NEW); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { err_throw(VMERR_BAD_DYNAMIC_NEW); AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjIter::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; /* * Registration table object for indexed iterators */ class CVmMetaclassIterIdx: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "indexed-iterator/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjIterIdx(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjIterIdx(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { err_throw(VMERR_BAD_DYNAMIC_NEW); AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjIterIdx::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMITER_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjIter) VM_REGISTER_METACLASS(CVmObjIterIdx) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmlog.cpp�������������������������������������������������������������������������0000664�0000000�0000000�00000004176�12650170723�0015516�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* Copyright (c) 2011 by Michael J. Roberts. All Rights Reserved. */ /* Name vmlog.cpp - system log file Function Notes Modified 12/01/11 MJRoberts - Creation */ #include <time.h> #include "t3std.h" #include "vmglob.h" #include "vmlog.h" #include "vmfile.h" #include "vmdatasrc.h" #include "charmap.h" /* ------------------------------------------------------------------------ */ /* * System log file */ /* * Log a message with sprintf-style formatting */ void vm_log_fmt(VMG_ const char *fmt, ...) { /* format the message */ va_list args; va_start(args, fmt); char *str = t3vsprintf_alloc(fmt, args); va_end(args); /* log the message */ vm_log(vmg_ str, strlen(str)); /* done with the string */ t3free(str); } /* * Log a string with a given length */ void vm_log(VMG_ const char *str, size_t len) { /* open the system log file */ osfildef *fp = osfoprwt(G_syslogfile, OSFTTEXT); if (fp != 0) { /* wrap it in a data source */ CVmFileSource ds(fp); /* get a printable timestamp */ os_time_t timer = os_time(0); struct tm *tblk = os_localtime(&timer); char *tmsg = asctime(tblk); /* remove the trailing '\n' from the asctime message */ size_t tmsgl = strlen(tmsg); if (tmsgl > 0 && tmsg[tmsgl-1] == '\n') tmsg[--tmsgl] = '\0'; /* build the full message: [<timestamp>] <message> <newline> */ char *msg = t3sprintf_alloc("[%s] %.*s\n", tmsg, (int)len, str); size_t msglen = strlen(msg); /* seek to the end of the file */ ds.seek(0, OSFSK_END); /* if we can convert to a local character set, do so */ if (G_cmap_to_log != 0) { /* write the message in the local character set */ G_cmap_to_file->write_file(&ds, msg, msglen); } else { /* write the message with no character set conversion */ (void)osfwb(fp, msg, msglen); } /* done with the formatted text string */ t3free(msg); } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmlog.h���������������������������������������������������������������������������0000664�0000000�0000000�00000002057�12650170723�0015157�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* Copyright (c) 2011 by Michael J. Roberts. All Rights Reserved. */ /* Name vmlog.h - system log file Function Functions for writing messages to the system log file Notes Strings passed to vm_log_xxx functions are expected to be in the local file system character set, unless otherwise noted per function. These functions generally just write the bytes of the strings as given, without any character set mapping, so it's up to the caller to map to the file character set if necessary. Plain ASCII strings don't need mapping, since virtually all modern machines use character sets that include ASCII as a subset - this includes UTF-8, ISO-8859-X, Win CP12xx, etc, but excludes 16-bit Unicode mappings (UCS-2) and EBCDIC. Modified 12/01/11 MJRoberts - Creation */ #ifndef VMLOG_H #define VMLOG_H #include "t3std.h" #include "vmglob.h" /* log a message with sprintf-style formatting */ void vm_log_fmt(VMG_ const char *fmt, ...); /* log a message string */ void vm_log(VMG_ const char *str, size_t len); #endif /* VMLOG_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmlookup.cpp����������������������������������������������������������������������0000664�0000000�0000000�00000200456�12650170723�0016245�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmlookup.cpp - LookupTable metaclass implementation Function Notes Modified 02/06/01 MJRoberts - Creation */ #include <stdlib.h> #include <string.h> #include <assert.h> #include "t3std.h" #include "vmlookup.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmbif.h" #include "vmmeta.h" #include "vmstack.h" #include "vmiter.h" #include "vmrun.h" #include "vmlst.h" #include "vmvec.h" /* ------------------------------------------------------------------------ */ /* * LookupTable undo record. We attach this record to the standard undo * record via the 'ptrval' field. */ struct lookuptab_undo_rec { /* action type */ enum lookuptab_undo_action action; /* key value */ vm_val_t key; }; /* ------------------------------------------------------------------------ */ /* * Allocate a new extension structure */ vm_lookup_ext *vm_lookup_ext::alloc_ext(VMG_ CVmObjLookupTable *self, uint bucket_cnt, uint value_cnt) { size_t siz; vm_lookup_ext *ext; /* calculate how much space we need */ siz = sizeof(vm_lookup_ext) + (bucket_cnt - 1) * sizeof(ext->buckets[0]) + value_cnt * sizeof(vm_lookup_val); /* allocate the memory */ ext = (vm_lookup_ext *)G_mem->get_var_heap()->alloc_mem(siz, self); /* remember the table sizes */ ext->bucket_cnt = bucket_cnt; ext->value_cnt = value_cnt; /* set the default value to nil initially */ ext->default_value.set_nil(); /* return the new extension */ return ext; } /* * initialize the buckets and free list */ void vm_lookup_ext::init_ext() { uint i; vm_lookup_val **bucketp; vm_lookup_val *valp; /* all of the buckets are initially empty */ for (i = bucket_cnt, bucketp = buckets ; i != 0 ; --i, ++bucketp) *bucketp = 0; /* * Initialize the value free list. We suballocate the value entry * structures out of our main allocation block; the available memory * for the structures comes immediately after the last bucket, so * bucketp points to the first value entry. */ valp = (vm_lookup_val *)(void *)bucketp; first_free = valp; for (first_free = valp, i = value_cnt ; i != 0 ; --i, ++valp) { /* link it into the free list */ valp->nxt = valp + 1; /* mark it as empty */ valp->key.set_empty(); valp->val.set_empty(); } /* terminate the free list at the last element */ (valp - 1)->nxt = 0; } /* * Reallocate an extension structure, adding more value entries. */ vm_lookup_ext *vm_lookup_ext::expand_ext(VMG_ CVmObjLookupTable *self, vm_lookup_ext *old_ext, uint new_value_cnt) { vm_lookup_ext *new_ext; /* allocate a new extension structure of the requested size */ new_ext = alloc_ext(vmg_ self, old_ext->bucket_cnt, new_value_cnt); /* copy the old extension data into the new extension */ new_ext->copy_ext_from(old_ext); /* delete the old memory */ G_mem->get_var_heap()->free_mem(old_ext); /* return the new extension */ return new_ext; } /* * Copy extension data */ void vm_lookup_ext::copy_ext_from(vm_lookup_ext *old_ext) { vm_lookup_val **oldbp; vm_lookup_val **newbp; vm_lookup_val *oldval; vm_lookup_val *newval; char *oldbase; char *newbase; uint i; /* the bucket counts must be the same in both extensions */ assert(bucket_cnt == old_ext->bucket_cnt); /* we must have at least as many entries as the old one had */ assert(value_cnt >= old_ext->value_cnt); /* copy the default value */ default_value = old_ext->default_value; /* get the new and old base values for faster pointer arithmetic */ oldbase = (char *)old_ext->idx_to_val(0); newbase = (char *)idx_to_val(0); /* copy the hash buckets from the old extension to the new extension */ for (i = old_ext->bucket_cnt, oldbp = old_ext->buckets, newbp = buckets ; i != 0 ; --i, ++newbp, ++oldbp) { /* copy the bucket pointer, adjusting to our local pointer scheme */ *newbp = (*oldbp == 0 ? 0 : (vm_lookup_val *)(((char *)*oldbp - oldbase) + newbase)); } /* copy the values from the old extension to the new extension */ for (i = old_ext->value_cnt, oldval = old_ext->idx_to_val(0), newval = idx_to_val(0) ; i != 0 ; --i, ++oldval, ++newval) { /* copy this entry */ newval->key = oldval->key; newval->val = oldval->val; newval->nxt = (oldval->nxt == 0 ? 0 : (vm_lookup_val *)(((char *)oldval->nxt - oldbase) + newbase)); } /* link all of the remaining free values into the free list */ first_free = newval; for (i = value_cnt - old_ext->value_cnt ; i != 0 ; --i, ++newval) { /* link this value into the free list */ newval->nxt = newval + 1; /* mark this free value as empty */ newval->key.set_empty(); newval->val.set_empty(); } /* terminate the free list */ (newval - 1)->nxt = 0; } /* ------------------------------------------------------------------------ */ /* * LookupTable object statics */ /* metaclass registration object */ static CVmMetaclassLookupTable metaclass_reg_obj; CVmMetaclass *CVmObjLookupTable::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjLookupTable:: *CVmObjLookupTable::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjLookupTable::getp_undef, /* 0 */ &CVmObjLookupTable::getp_key_present, /* 1 */ &CVmObjLookupTable::getp_remove_entry, /* 2 */ &CVmObjLookupTable::getp_apply_all, /* 3 */ &CVmObjLookupTable::getp_for_each, /* 4 */ &CVmObjLookupTable::getp_count_buckets, /* 5 */ &CVmObjLookupTable::getp_count_entries, /* 6 */ &CVmObjLookupTable::getp_for_each_assoc, /* 7 */ &CVmObjLookupTable::getp_keys_to_list, /* 8 */ &CVmObjLookupTable::getp_vals_to_list, /* 9 */ &CVmObjLookupTable::getp_get_def_val, /* 10 */ &CVmObjLookupTable::getp_set_def_val, /* 11 */ &CVmObjLookupTable::getp_nthKey, /* 12 */ &CVmObjLookupTable::getp_nthVal /* 13 */ }; /* ------------------------------------------------------------------------ */ /* * Lookup Table metaclass implementation */ /* * create a lookup table with a given hash table size and the given * initial entry table size */ CVmObjLookupTable::CVmObjLookupTable(VMG_ size_t bucket_cnt, size_t val_cnt) { vm_val_t empty; /* set up an empty value */ empty.set_empty(); /* allocate our extension structure from the variable heap */ ext_ = (char *)vm_lookup_ext::alloc_ext(vmg_ this, bucket_cnt, val_cnt); /* initialize the extension */ get_ext()->init_ext(); } /* * create */ vm_obj_id_t CVmObjLookupTable::create(VMG_ int in_root_set, uint bucket_count, uint init_capacity) { vm_obj_id_t id; /* allocate the object ID */ id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); /* create the object */ new (vmg_ id) CVmObjLookupTable(vmg_ bucket_count, init_capacity); /* return the new ID */ return id; } /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjLookupTable::create_from_stack( VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id; size_t bucket_count; size_t init_capacity; vm_val_t src_obj; /* parse the arguments */ get_constructor_args(vmg_ argc, &bucket_count, &init_capacity, &src_obj); /* * allocate the object ID - this type of construction never creates a * root object */ id = vm_new_id(vmg_ FALSE, TRUE, FALSE); /* create the object */ new (vmg_ id) CVmObjLookupTable(vmg_ bucket_count, init_capacity); /* populate it with the source list if desired */ ((CVmObjLookupTable *)vm_objp(vmg_ id))-> populate_from_list(vmg_ &src_obj); /* discard the source object gc protection */ G_stk->discard(); /* return the new ID */ return id; } /* * get and check constructor arguments */ void CVmObjLookupTable::get_constructor_args(VMG_ uint argc, size_t *bucket_count, size_t *init_capacity, vm_val_t *src_obj) { /* presume no source object */ src_obj->set_nil(); /* check arguments */ if (argc == 0) { /* no arguments - they want default parameters */ *bucket_count = 32; *init_capacity = 64; } else if (argc == 1) { int cnt; /* * One argument - they want to create from a list. Retrieve the * list or vector object. */ G_stk->pop(src_obj); /* make sure it's a list or vector */ if (!src_obj->is_listlike(vmg0_) || (cnt = src_obj->ll_length(vmg0_)) < 0) err_throw(VMERR_BAD_VAL_BIF); /* use 1.5x the pair count as the bucket count and capacity */ *bucket_count = *init_capacity = cnt*3 / 2; } else if (argc == 2) { /* * two arguments - they specified the bucket count and initial * capacity; pop the parameters */ *bucket_count = (size_t)CVmBif::pop_long_val(vmg0_); *init_capacity = (size_t)CVmBif::pop_long_val(vmg0_); } else { /* invalid arguments */ err_throw(VMERR_WRONG_NUM_OF_ARGS); } /* re-push the source object to protect it from gc */ G_stk->push(src_obj); /* make sure both are positive */ if (*bucket_count <= 0 || *init_capacity <= 0) err_throw(VMERR_BAD_VAL_BIF); } /* * Populate the table from a source list or vector. This should only be * used during construction, because it doesn't save undo. (It's not * necessary to save undo during construction because the whole object * creation and construction is atomic for undo purposes: if we undo any of * this, we undo the whole creation of the object, so there's no need to * worry about saving old state internal to the object.) */ void CVmObjLookupTable::populate_from_list(VMG_ const vm_val_t *src) { /* copy elements from the source object */ int cnt = src->ll_length(vmg0_); int i; for (i = 1 ; i + 1 <= cnt ; ) { vm_val_t key, val; /* get the next two elements - they're always key, value pairs */ src->ll_index(vmg_ &key, i++); src->ll_index(vmg_ &val, i++); /* set them in the table */ set_or_add_entry(vmg_ &key, &val); } /* if there's a single remaining entry, it's the default value */ if (i <= cnt) src->ll_index(vmg_ &get_ext()->default_value, i); } /* * Create a copy of this object */ vm_obj_id_t CVmObjLookupTable::create_copy(VMG0_) { vm_obj_id_t id; CVmObjLookupTable *new_obj; /* allocate the object ID */ id = vm_new_id(vmg_ FALSE, TRUE, FALSE); /* allocate a new object */ new_obj = new (vmg_ id) CVmObjLookupTable(); /* * set up the new object with our same sizes, but don't initialize it * - we're going to blast all of our data directly into the new * extension, so we don't need to waste time setting up an empty * initial state */ new_obj->ext_ = (char *)vm_lookup_ext::alloc_ext( vmg_ new_obj, get_bucket_count(), get_entry_count()); /* copy my data to the next object */ new_obj->get_ext()->copy_ext_from(get_ext()); /* return the new object's id */ return id; } /* * notify of deletion */ void CVmObjLookupTable::notify_delete(VMG_ int /*in_root_set*/) { /* free our additional data, if we have any */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* * set a property */ void CVmObjLookupTable::set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) { /* no settable properties - throw an error */ err_throw(VMERR_INVALID_SETPROP); } /* * get a property */ int CVmObjLookupTable::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from our base class */ return CVmObjCollection:: get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * apply an undo record */ void CVmObjLookupTable::apply_undo(VMG_ struct CVmUndoRecord *undo_rec) { if (undo_rec->id.ptrval != 0) { lookuptab_undo_rec *rec; /* get our private record from the standard undo record */ rec = (lookuptab_undo_rec *)undo_rec->id.ptrval; /* check the action in the record */ switch(rec->action) { case LOOKUPTAB_UNDO_NULL: /* * null record, which means that it had a weak reference to an * object that has been deleted - there's no way to reinstate * such records, so ignore it */ break; case LOOKUPTAB_UNDO_ADD: /* we added the entry, so we must now delete it */ del_entry(vmg_ &rec->key); break; case LOOKUPTAB_UNDO_DEL: /* we deleted the entry, so we must now add it */ add_entry(vmg_ &rec->key, &undo_rec->oldval); break; case LOOKUPTAB_UNDO_MOD: /* we modified the entry, so we must change it back */ mod_entry(vmg_ &rec->key, &undo_rec->oldval); break; case LOOKUPTAB_UNDO_DEFVAL: /* we modified the default value; change it back */ get_ext()->default_value = undo_rec->oldval; break; } /* discard the private record */ t3free(rec); /* clear the pointer in the main record so we know it's gone */ undo_rec->id.ptrval = 0; } } /* * discard extra undo information */ void CVmObjLookupTable::discard_undo(VMG_ CVmUndoRecord *rec) { /* delete our extra information record */ if (rec->id.ptrval != 0) { /* free the record */ t3free((lookuptab_undo_rec *)rec->id.ptrval); /* clear the pointer so we know it's gone */ rec->id.ptrval = 0; } } /* * Mark undo references. */ void CVmObjLookupTable:: mark_undo_ref(VMG_ struct CVmUndoRecord *undo_rec) { lookuptab_undo_rec *rec; /* get our private record from the standard undo record */ rec = (lookuptab_undo_rec *)undo_rec->id.ptrval; /* if the key in the record is an object, mark it referenced */ if (rec->key.typ == VM_OBJ) G_obj_table->mark_all_refs(rec->key.val.obj, VMOBJ_REACHABLE); /* if the value in the record is an object, mark it as well */ if (undo_rec->oldval.typ == VM_OBJ) G_obj_table->mark_all_refs(undo_rec->oldval.val.obj, VMOBJ_REACHABLE); } /* * Mark references. Keys (but not values) are strongly referenced. */ void CVmObjLookupTable::mark_refs(VMG_ uint state) { /* get my extension */ vm_lookup_ext *ext = get_ext(); /* mark the default value */ const vm_val_t *val = &ext->default_value; if (val->typ == VM_OBJ) G_obj_table->mark_all_refs(val->val.obj, state); /* run through my buckets */ vm_lookup_val **bp = ext->buckets; size_t i = ext->bucket_cnt; for ( ; i != 0 ; ++bp, --i) { /* run through all entries attached to this bucket */ for (const vm_lookup_val *entry = *bp ; entry != 0 ; entry = entry->nxt) { /* if the key is an object, mark it as referenced */ val = &entry->key; if (val->typ == VM_OBJ) G_obj_table->mark_all_refs(val->val.obj, state); /* if the entry is an object, mark it as referenced */ val = &entry->val; if (val->typ == VM_OBJ) G_obj_table->mark_all_refs(val->val.obj, state); } } } /* * load from an image file */ void CVmObjLookupTable::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from the image file */ void CVmObjLookupTable::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); } /* * load or reload data from the image */ void CVmObjLookupTable::load_image_data(VMG_ const char *ptr, size_t siz) { uint bucket_cnt; uint val_cnt; uint i; const char *ibp; vm_lookup_val **ebp; vm_lookup_val *eval; vm_lookup_ext *ext; /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* read the bucket and value counts from the header */ bucket_cnt = osrp2(ptr); val_cnt = osrp2(ptr + 2); /* allocate space for a copy of the image data */ ext = vm_lookup_ext::alloc_ext(vmg_ this, bucket_cnt, val_cnt); ext_ = (char *)ext; /* initialize the free list pointer, given as a 1-based entry index */ ext->first_free = ext->img_idx_to_val(osrp2(ptr + 4)); /* initialize the table from the load image data */ for (i = bucket_cnt, ibp = ptr + 6, ebp = ext->buckets ; i != 0 ; --i, ibp += 2, ++ebp) { /* translate the image file index to a value pointer */ *ebp = ext->img_idx_to_val(osrp2(ibp)); } /* initialize the value entries */ for (i = val_cnt, eval = ext->idx_to_val(0) ; i != 0 ; --i, ibp += VMLOOKUP_VALUE_SIZE, ++eval) { /* read the key and value */ vmb_get_dh(ibp, &eval->key); vmb_get_dh(ibp + VMB_DATAHOLDER, &eval->val); /* remember the next pointer, which is given as a 1-based index */ eval->nxt = ext->img_idx_to_val(osrp2(ibp + VMB_DATAHOLDER*2)); } /* if a default value is included, read it */ ext->default_value.set_nil(); if (ibp + VMB_DATAHOLDER <= ptr + siz) { /* read the default value and skip it in the source data */ vmb_get_dh(ibp, &ext->default_value); ibp += VMB_DATAHOLDER; } } /* * save to a file */ void CVmObjLookupTable::save_to_file(VMG_ class CVmFile *fp) { vm_lookup_ext *ext = get_ext(); uint i; vm_lookup_val **bp; vm_lookup_val *val; char buf[VMLOOKUP_VALUE_SIZE]; /* write our bucket count, value count, and first-free index */ fp->write_uint2(ext->bucket_cnt); fp->write_uint2(ext->value_cnt); fp->write_uint2(ext->val_to_img_idx(ext->first_free)); /* * If the image file was compiled for version 030003 or later of the * LookupTable object, save the default value. The default value * wasn't added until 030003, so source code compiled against earlier * versions of the metaclass spec can't change it from the 'nil' * default. Furthermore, these image files could be loaded on VMs that * have older versions of the metaclass that won't know to look for a * default value in the loaded data, so to be compatible with those * readers we must omit the value entirely. If the image file was * compiled with 030003 or later, it can only be loaded by VMs with * LookupTable implementations that know to read the value. */ if (image_file_version_ge(vmg_ "030003")) { vmb_put_dh(buf, &ext->default_value); fp->write_bytes(buf, VMB_DATAHOLDER); } /* write the buckets */ for (i = ext->bucket_cnt, bp = ext->buckets ; i != 0 ; --i, ++bp) fp->write_uint2(ext->val_to_img_idx(*bp)); /* write the values */ for (i = ext->value_cnt, val = ext->idx_to_val(0) ; i != 0 ; --i, ++val) { uint idx; /* store the key, value, and index */ vmb_put_dh(buf, &val->key); vmb_put_dh(buf + VMB_DATAHOLDER, &val->val); idx = ext->val_to_img_idx(val->nxt); oswp2(buf + VMB_DATAHOLDER*2, idx); /* write the data */ fp->write_bytes(buf, VMLOOKUP_VALUE_SIZE); } } /* * restore from a file */ void CVmObjLookupTable::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { vm_lookup_ext *ext; char buf[64]; size_t bucket_cnt; size_t val_cnt; size_t i; vm_lookup_val **bp; vm_lookup_val *entry; uint idx; /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* read the fixed fields */ fp->read_bytes(buf, 6); bucket_cnt = vmb_get_uint2(buf); val_cnt = vmb_get_uint2(buf + 2); /* allocate the extension structure */ ext = vm_lookup_ext::alloc_ext(vmg_ this, bucket_cnt, val_cnt); ext_ = (char *)ext; /* store the free pointer */ ext->first_free = ext->img_idx_to_val(vmb_get_uint2(buf + 4)); /* * if the saver was using version 030003 or later, there's a default * value; otherwise the default value is fixed at 'nil' */ ext->default_value.set_nil(); if (image_file_version_ge(vmg_ "030003")) { /* read the default value */ fp->read_bytes(buf, VMB_DATAHOLDER); /* apply fixups */ fixups->fix_dh(vmg_ buf); /* decode it into the default value in the extension */ vmb_get_dh(buf, &ext->default_value); } /* read the buckets */ for (i = bucket_cnt, bp = ext->buckets ; i != 0 ; ++bp, --i) { /* read this bucket pointer and store it */ idx = fp->read_uint2(); *bp = ext->img_idx_to_val(idx); } /* read the value entries */ for (i = val_cnt, entry = ext->idx_to_val(0) ; i != 0 ; --i, ++entry) { /* read this value entry */ fp->read_bytes(buf, VMLOOKUP_VALUE_SIZE); /* fix up the key and value dataholders */ fixups->fix_dh_array(vmg_ buf, 2); /* store the key and value */ vmb_get_dh(buf, &entry->key); vmb_get_dh(buf + VMB_DATAHOLDER, &entry->val); /* store the next pointer */ idx = osrp2(buf + VMB_DATAHOLDER*2); entry->nxt = ext->img_idx_to_val(idx); } } /* * create an iterator */ void CVmObjLookupTable::new_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val) { vm_val_t copy_val; /* save a copy of myself for protection against garbage collection */ G_stk->push(self_val); /* * create a copy of my object, so that we can iterate over a snapshot * of our state even if we subsequently change our state */ copy_val.set_obj(create_copy(vmg0_)); /* set up a new lookup table iterator object on the copy */ retval->set_obj(CVmObjIterLookupTable::create_for_coll(vmg_ ©_val)); /* done with the gc protection */ G_stk->discard(); } /* * create a live iterator */ void CVmObjLookupTable::new_live_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val) { /* set up a new lookup table iterator directly on myself */ retval->set_obj(CVmObjIterLookupTable::create_for_coll(vmg_ self_val)); } /* * add an undo record */ void CVmObjLookupTable::add_undo_rec(VMG_ vm_obj_id_t self, lookuptab_undo_action action, const vm_val_t *key, const vm_val_t *old_entry_val) { lookuptab_undo_rec *rec; vm_val_t nil_val; /* allocate our record extension */ rec = (lookuptab_undo_rec *)t3malloc(sizeof(lookuptab_undo_rec)); /* if either the key or old value are null pointers, use a nil value */ nil_val.set_nil(); if (key == 0) key = &nil_val; if (old_entry_val == 0) old_entry_val = &nil_val; /* set up the record */ rec->action = action; rec->key = *key; /* add the record to the global undo stream */ if (!G_undo->add_new_record_ptr_key(vmg_ self, rec, old_entry_val)) { /* * we didn't add an undo record, so our extra undo information * isn't actually going to be stored in the undo system - hence we * must delete our extra information */ t3free(rec); } } /* * Add an entry; does not generate undo. */ void CVmObjLookupTable::add_entry(VMG_ const vm_val_t *key, const vm_val_t *val) { uint hashval; vm_lookup_val *entry; /* push the key and value as gc protection for a moment */ G_stk->push(key); G_stk->push(val); /* calculate the hash value for the key */ hashval = calc_key_hash(vmg_ key); /* allocate an entry */ entry = alloc_new_entry(vmg0_); /* link this entry into the chain at this hash value */ entry->nxt = get_ext()->buckets[hashval]; get_ext()->buckets[hashval] = entry; /* set the key and value for this entry */ entry->key = *key; entry->val = *val; /* discard the gc protection */ G_stk->discard(2); } /* * Add an entry, generating undo */ void CVmObjLookupTable::add_entry_undo(VMG_ vm_obj_id_t self, const vm_val_t *key, const vm_val_t *val) { /* * Add undo for the change. Since we're adding a new record, there's * no old value for the record. */ add_undo_rec(vmg_ self, LOOKUPTAB_UNDO_ADD, key, 0); /* add the entry */ add_entry(vmg_ key, val); } /* * Delete an entry; does not generate undo. */ void CVmObjLookupTable::del_entry(VMG_ const vm_val_t *key) { uint hashval; vm_lookup_val *entry; vm_lookup_val *prv_entry; /* find the entry for the key */ entry = find_entry(vmg_ key, &hashval, &prv_entry); /* if we found it, unlink it */ if (entry != 0) unlink_entry(vmg_ entry, hashval, prv_entry); } /* * Unlink an entry */ void CVmObjLookupTable::unlink_entry(VMG_ vm_lookup_val *entry, uint hashval, vm_lookup_val *prv_entry) { vm_lookup_ext *ext = get_ext(); /* * set the previous item's next pointer, or the head of the chain, as * appropriate */ if (prv_entry == 0) ext->buckets[hashval] = entry->nxt; else prv_entry->nxt = entry->nxt; /* link the entry into the free list */ entry->nxt = ext->first_free; ext->first_free = entry; /* set this entry's key and value to 'empty' to indicate that it's free */ entry->key.set_empty(); entry->val.set_empty(); } /* * Modify an entry; does not generate undo. */ void CVmObjLookupTable::mod_entry(VMG_ const vm_val_t *key, const vm_val_t *val) { vm_lookup_val *entry; /* find the entry for the key */ entry = find_entry(vmg_ key, 0, 0); /* if we found it, change the value to the given value */ if (entry != 0) entry->val = *val; } /* * Set an entry's value, generating undo */ void CVmObjLookupTable::set_entry_val_undo(VMG_ vm_obj_id_t self, vm_lookup_val *entry, const vm_val_t *val) { /* generate undo for the change */ add_undo_rec(vmg_ self, LOOKUPTAB_UNDO_MOD, &entry->key, &entry->val); /* update the entry */ entry->val = *val; } /* * Find an entry for a given key. Returns the entry index, and fills in * the hash value and previous-entry output variables if provided. * Returns zero if there is no such key. */ vm_lookup_val *CVmObjLookupTable::find_entry(VMG_ const vm_val_t *key, uint *hashval_p, vm_lookup_val **prv_entry_p) { vm_lookup_ext *ext = get_ext(); uint hashval; vm_lookup_val *entry; vm_lookup_val *prv_entry; /* compute the hash value (and return it to the caller if desired) */ hashval = calc_key_hash(vmg_ key); if (hashval_p != 0) *hashval_p = hashval; /* scan the hash chain for this entry */ for (prv_entry = 0, entry = ext->buckets[hashval] ; entry != 0 ; prv_entry = entry, entry = entry->nxt) { /* if it matches the key we're looking for, this is the one */ if (entry->key.equals(vmg_ key)) break; } /* if the caller wanted to know the previous entry, tell them */ if (prv_entry_p != 0) *prv_entry_p = prv_entry; /* return the entry where we found the key, if indeed we found it */ return entry; } /* * Calculate a hash value for a key */ uint CVmObjLookupTable::calc_key_hash(VMG_ const vm_val_t *key) { uint hash; /* calculate the raw hash for the value */ hash = key->calc_hash(vmg0_); /* reduce the hash value modulo the hash table size */ hash %= get_bucket_count(); /* return the hash value */ return hash; } /* * Allocate a new entry */ vm_lookup_val *CVmObjLookupTable::alloc_new_entry(VMG0_) { /* if necessary, add more entry slots to the table */ expand_if_needed(vmg0_); /* get the first entry from the free list */ vm_lookup_val *entry = get_first_free(); /* unlink this entry from the free list */ set_first_free(entry->nxt); entry->nxt = 0; /* return the free entry */ return entry; } /* * Check for free space, and expand the table if necessary. */ void CVmObjLookupTable::expand_if_needed(VMG0_) { /* if we have any free entries left, we need do nothing */ if (get_first_free() != 0) return; /* increase the entry count by 50%, or 16 slots, whichever is more */ uint new_entry_cnt = get_entry_count() + (get_entry_count() >> 1); if (new_entry_cnt < get_entry_count() + 16) new_entry_cnt = get_entry_count() + 16; /* reallocate the extension at the new size */ ext_ = (char *)vm_lookup_ext::expand_ext( vmg_ this, get_ext(), new_entry_cnt); } /* ------------------------------------------------------------------------ */ /* * Get a value by index */ int CVmObjLookupTable::index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val) { vm_lookup_val *entry; /* find the entry */ entry = find_entry(vmg_ index_val, 0, 0); /* if we found it, return it; otherwise, return nil */ if (entry != 0) { /* get the value from the entry and hand it back as the result */ *result = entry->val; } else { /* not found - return the default value */ *result = get_ext()->default_value; } /* handled */ return TRUE; } /* * Get a value by index, with an indication of whether or not the value * exists. */ int CVmObjLookupTable::index_check(VMG_ vm_val_t *result, const vm_val_t *idx) { /* find the entry */ vm_lookup_val *entry = find_entry(vmg_ idx, 0, 0); /* if we found it, return it; otherwise, return nil */ if (entry != 0) { /* get the indexed value */ *result = entry->val; /* indicate that the key is present is in the table */ return TRUE; } else { /* * not found - set a nil result value and indicate that the key * isn't in the table */ result->set_nil(); return FALSE; } } /* * Set or add an entry, with no undo */ void CVmObjLookupTable::set_or_add_entry(VMG_ const vm_val_t *key, const vm_val_t *val) { vm_lookup_val *entry; /* look for an existing entry with this key */ entry = find_entry(vmg_ key, 0, 0); /* if we found an existing entry, modify it; otherwise, add a new one */ if (entry != 0) { /* set the new value for this entry */ entry->val = *val; } else { /* add a new entry with the given key */ add_entry(vmg_ key, val); } } /* * Set a value by index */ int CVmObjLookupTable::set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val) { vm_lookup_val *entry; /* look for an existing entry with this key */ entry = find_entry(vmg_ index_val, 0, 0); /* if we found an existing entry, modify it; otherwise, add a new one */ if (entry != 0) { /* set the new value for this entry */ set_entry_val_undo(vmg_ self, entry, new_val); } else { /* add a new entry with the given key */ add_entry_undo(vmg_ self, index_val, new_val); } /* we can be modified, hence the new container is the original one */ new_container->set_obj(self); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - determine if the given key is in the table */ int CVmObjLookupTable::getp_key_present(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_lookup_val *entry; vm_val_t key; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the key */ G_stk->pop(&key); /* find the entry for this key */ entry = find_entry(vmg_ &key, 0, 0); /* return true if we found it, nil if not */ retval->set_logical(entry != 0); /* handled */ return TRUE; } /* * Property evaluator - remove an entry given a key */ int CVmObjLookupTable::getp_remove_entry(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_lookup_val *entry; vm_val_t key; uint hashval; vm_lookup_val *prv_entry; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the key */ G_stk->pop(&key); /* find the entry for this key */ entry = find_entry(vmg_ &key, &hashval, &prv_entry); /* if we found the entry, delete it */ if (entry != 0) { /* the return value is the value stored at this key */ *retval = entry->val; /* generate undo for the change */ add_undo_rec(vmg_ self, LOOKUPTAB_UNDO_DEL, &key, retval); /* unlink the entry */ unlink_entry(vmg_ entry, hashval, prv_entry); } else { /* not found - the return value is nil */ retval->set_nil(); } /* handled */ return TRUE; } /* * Property evaluator - apply a callback to each entry in the table */ int CVmObjLookupTable::getp_apply_all(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_val_t *fn; vm_lookup_val *entry; static CVmNativeCodeDesc desc(1); uint i; vm_rcdesc rc(vmg_ "LookupTable.applyAll", self, 3, G_stk->get(0), argc); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* the function to invoke is the top argument */ fn = G_stk->get(0); /* push a self-reference for gc protection */ G_stk->push()->set_obj(self); /* iterate over each entry */ for (i = get_entry_count(), entry = get_ext()->idx_to_val(0) ; i != 0 ; ++entry, --i) { /* * if the entry is in use (it is if the key is non-empty), invoke * the callback on it */ if (entry->key.typ != VM_EMPTY) { /* push the arguments - (key, val) - push in reverse order */ G_stk->push(&entry->val); G_stk->push(&entry->key); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ fn, 2, &rc, 0); /* replace this element with the result */ set_entry_val_undo(vmg_ self, entry, G_interpreter->get_r0()); } } /* discard the arguments and gc protection */ G_stk->discard(2); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* * Property evaluator - invoke a callback for each entry in the table */ int CVmObjLookupTable::getp_for_each(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* call the general processor */ vm_rcdesc rc(vmg_ "LookupTable.forEach", self, 4, G_stk->get(0), argc); return for_each_gen(vmg_ self, retval, argc, FALSE, &rc); } /* * Property evaluator - invoke a callback for each entry in the table */ int CVmObjLookupTable::getp_for_each_assoc(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* call the general processor */ vm_rcdesc rc(vmg_ "List.forEachAssoc", self, 7, G_stk->get(0), argc); return for_each_gen(vmg_ self, retval, argc, TRUE, &rc); } /* * general processor for forEach/forEachAssoc */ int CVmObjLookupTable::for_each_gen(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int pass_key_to_cb, vm_rcdesc *rc) { vm_val_t *fn; vm_lookup_val *entry; static CVmNativeCodeDesc desc(1); uint i; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* the function to invoke is the top argument */ fn = G_stk->get(0); /* push a self-reference for gc protection */ G_stk->push()->set_obj(self); /* iterate over each bucket */ for (i = get_entry_count(), entry = get_ext()->idx_to_val(0) ; i != 0 ; --i, ++entry) { /* * if it's in use (i.e., if the key is non-empty), invoke the * callback for the entry */ if (entry->key.typ != VM_EMPTY) { /* push the current value argument */ G_stk->push(&entry->val); /* push the key if desired */ if (pass_key_to_cb) G_stk->push(&entry->key); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ fn, pass_key_to_cb ? 2 : 1, rc, 0); } } /* discard the arguments and gc protection */ G_stk->discard(2); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* * iterate over the table's contents through a callback */ void CVmObjLookupTable::for_each( VMG_ void (*cb)(VMG_ const vm_val_t *key, const vm_val_t *val, void *ctx), void *ctx) { vm_lookup_val *entry; uint i; /* iterate over each bucket */ for (i = get_entry_count(), entry = get_ext()->idx_to_val(0) ; i != 0 ; --i, ++entry) { /* * if it's in use (i.e., if the key is non-empty), invoke the * callback for the entry */ if (entry->key.typ != VM_EMPTY) (*cb)(vmg_ &entry->key, &entry->val, ctx); } } /* * Property evaluator - get the number of buckets */ int CVmObjLookupTable::getp_count_buckets(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* return the number of buckets */ retval->set_int(get_bucket_count()); /* handled */ return TRUE; } /* * Property evaluator - get the number of entries in use */ int CVmObjLookupTable::getp_count_entries(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); uint i; int cnt; vm_lookup_val *entry; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* run through the table and count in-use entries */ for (cnt = 0, i = get_entry_count(), entry = get_ext()->idx_to_val(0) ; i != 0 ; --i, ++entry) { /* if the entry is not marked as free, count it as used */ if (entry->key.typ != VM_EMPTY) ++cnt; } /* return the number of in-use entries we counted */ retval->set_int(cnt); /* handled */ return TRUE; } /* * Property evaluator - make a list of all of the keys in the table */ int CVmObjLookupTable::getp_keys_to_list(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use the general list maker, listing only the keys */ return getp_make_list(vmg_ self, retval, argc, TRUE); } /* * Property evaluator - make a list of all of the values in the table */ int CVmObjLookupTable::getp_vals_to_list(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use the general list maker, listing only the values */ return getp_make_list(vmg_ self, retval, argc, FALSE); } /* * General handler for keys_to_list and vals_to_list */ int CVmObjLookupTable::getp_make_list(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int store_keys) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* push self while we're working, for gc protection */ G_stk->push()->set_obj(self); /* make the list */ make_list(vmg_ retval, store_keys, 0); /* discard our gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* * Low-level list maker. Creates a list of all of the keys or values in * the table. */ void CVmObjLookupTable::make_list(VMG_ vm_val_t *retval, int store_keys, int (*filter)(VMG_ const vm_val_t *, const vm_val_t *)) { vm_lookup_val *entry; uint i; int cnt; CVmObjList *lst; /* run through the table and count in-use entries */ for (cnt = 0, i = get_entry_count(), entry = get_ext()->idx_to_val(0) ; i != 0 ; ++entry, --i) { /* skip empties */ if (entry->key.typ == VM_EMPTY) continue; /* skip values that the filter function rejects */ if (filter != 0 && !filter(vmg_ &entry->key, &entry->val)) continue; /* count it */ ++cnt; } /* allocate a list to store the results */ retval->set_obj(CVmObjList::create(vmg_ FALSE, cnt)); /* get the list object */ lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); lst->cons_clear(); /* populate the list */ for (cnt = 0, i = get_entry_count(), entry = get_ext()->idx_to_val(0) ; i != 0 ; ++entry, --i) { /* if the entry is not marked as free, count it as used */ if (entry->key.typ != VM_EMPTY) { /* skip values that the filter function rejects */ if (filter != 0 && !filter(vmg_ &entry->key, &entry->val)) continue; /* store the key or value, as appropriate */ if (store_keys) { /* store the key */ lst->cons_set_element(cnt, &entry->key); } else { /* store the value */ lst->cons_set_element(cnt, &entry->val); } /* update the destination index */ ++cnt; } } } /* * Property evaluator: get the default value */ int CVmObjLookupTable::getp_get_def_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* return the default value */ *retval = get_ext()->default_value; return TRUE; } /* get the default value - internal API version */ void CVmObjLookupTable::get_default_val(vm_val_t *val) { *val = get_ext()->default_value; } /* * Property evaluator: set the default value */ int CVmObjLookupTable::getp_set_def_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* save undo */ add_undo_rec(vmg_ self, LOOKUPTAB_UNDO_DEFVAL, 0, &get_ext()->default_value); /* set the default value to the argument value */ G_stk->pop(&get_ext()->default_value); /* return self */ retval->set_obj(self); return TRUE; } /* * Property evaluator: get the nth key */ int CVmObjLookupTable::getp_nthKey(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the nth key value */ get_nth_ele(vmg_ retval, 0, self, CVmBif::pop_long_val(vmg0_)); /* handled */ return TRUE; } /* * Property evaluator: get the nth value */ int CVmObjLookupTable::getp_nthVal(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the nth value */ get_nth_ele(vmg_ 0, retval, self, CVmBif::pop_long_val(vmg0_)); /* handled */ return TRUE; } /* * Get the nth element's key and/or value */ void CVmObjLookupTable::get_nth_ele(VMG_ vm_val_t *key, vm_val_t *val, vm_obj_id_t self, long idx) { uint i; vm_lookup_val *entry; /* * if the index is zero, and we're asking for the value, return the * default value */ if (idx == 0) { if (key != 0) key->set_nil(); if (val != 0) *val = get_ext()->default_value; return; } /* iterate over our buckets */ for (i = get_entry_count(), entry = get_ext()->idx_to_val(0) ; i != 0 ; --i, ++entry) { /* if it's in use (i.e., the key isn't empty), count it */ if (entry->key.typ != VM_EMPTY) { /* count it */ --idx; /* if this is the one we're looking for, return it */ if (idx == 0) { /* fill in the key and/or value elements */ if (key != 0) *key = entry->key; if (val != 0) *val = entry->val; /* done */ return; } } } /* we didn't find it - the index is out of range */ err_throw(VMERR_INDEX_OUT_OF_RANGE); } /* ------------------------------------------------------------------------ */ /* * WeakRefLookupTable */ /* * statics */ /* metaclass registration object */ static CVmMetaclassWeakRefLookupTable weak_metaclass_reg_obj; CVmMetaclass *CVmObjWeakRefLookupTable::metaclass_reg_ = &weak_metaclass_reg_obj; /* * create */ vm_obj_id_t CVmObjWeakRefLookupTable:: create(VMG_ int in_root_set, uint bucket_count, uint init_capacity) { vm_obj_id_t id; /* allocate the object ID */ id = vm_new_id(vmg_ in_root_set, TRUE, TRUE); /* create the object */ new (vmg_ id) CVmObjWeakRefLookupTable(vmg_ bucket_count, init_capacity); /* return the new ID */ return id; } /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjWeakRefLookupTable::create_from_stack( VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id; size_t bucket_count; size_t init_capacity; vm_val_t src_obj; /* parse the arguments */ get_constructor_args(vmg_ argc, &bucket_count, &init_capacity, &src_obj); /* * allocate the object ID - this type of construction never creates a * root object */ id = vm_new_id(vmg_ FALSE, TRUE, TRUE); /* create the object */ new (vmg_ id) CVmObjWeakRefLookupTable(vmg_ bucket_count, init_capacity); /* populate it with the source list if desired */ ((CVmObjLookupTable *)vm_objp(vmg_ id))-> populate_from_list(vmg_ &src_obj); /* discard the source object gc protection */ G_stk->discard(); /* return the new ID */ return id; } /* * Mark undo references. Keys are strongly referenced, so mark the key in * the record, if present. */ void CVmObjWeakRefLookupTable:: mark_undo_ref(VMG_ struct CVmUndoRecord *undo_rec) { lookuptab_undo_rec *rec; /* get our private record from the standard undo record */ rec = (lookuptab_undo_rec *)undo_rec->id.ptrval; /* if the key in the record is an object, mark it referenced */ if (rec->key.typ == VM_OBJ) G_obj_table->mark_all_refs(rec->key.val.obj, VMOBJ_REACHABLE); } /* * Mark references. Keys (but not values) are strongly referenced. */ void CVmObjWeakRefLookupTable::mark_refs(VMG_ uint state) { vm_lookup_val **bp; uint i; /* note that the default value is weakly referenced, so don't mark it */ /* run through my buckets */ for (i = get_ext()->bucket_cnt, bp = get_ext()->buckets ; i != 0 ; --i, ++bp) { const vm_lookup_val *entry; /* run through all entries attached to this bucket */ for (entry = *bp ; entry != 0 ; entry = entry->nxt) { /* if the key is an object, mark it as referenced */ if (entry->key.typ == VM_OBJ) G_obj_table->mark_all_refs(entry->key.val.obj, state); /* * note that we only reference our value weakly, so do not * mark it here */ } } } /* * Remove stale weak references from an undo record. Value entries are * weakly referenced. */ void CVmObjWeakRefLookupTable:: remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *undo_rec) { int forget_record; /* presume we won't want to forget the record */ forget_record = FALSE; /* * if the old value in the undo record refers to an object, forget * about it */ if (G_obj_table->is_obj_deletable(&undo_rec->oldval)) { /* * the object is being deleted - since we only weakly reference * our objects, we must forget about this object now; this also * means we can forget about the record entirely, since there is * no need to apply it in the future now that it doesn't do * anything */ undo_rec->oldval.set_nil(); forget_record = TRUE; } /* delete our extended record if we're forgetting the record */ if (undo_rec->id.ptrval != 0 && forget_record) { lookuptab_undo_rec *rec; /* get our private record from the standard undo record */ rec = (lookuptab_undo_rec *)undo_rec->id.ptrval; /* we don't care about the key value in this record any more */ rec->key.set_nil(); /* mark the record as forgotten */ rec->action = LOOKUPTAB_UNDO_NULL; } } /* * Remove stale weak references. Our values are weak references, so * remove any entries that have values that are about to be deleted. */ void CVmObjWeakRefLookupTable::remove_stale_weak_refs(VMG0_) { vm_lookup_val **bucket; uint i; uint hashval; /* remove the default value if it's gone stale */ if (G_obj_table->is_obj_deletable(&get_ext()->default_value)) get_ext()->default_value.set_nil(); /* run through each bucket */ for (hashval = 0, bucket = get_ext()->buckets, i = get_bucket_count() ; i != 0 ; ++bucket, --i, ++hashval) { vm_lookup_val *entry; vm_lookup_val *prv; vm_lookup_val *nxt; /* run through all entries attached to this bucket */ for (prv = 0, entry = *bucket ; entry != 0 ; entry = nxt) { /* remember the next entry, in case we delete this one */ nxt = entry->nxt; /* * if the value has an object reference that has become stale, * delete the entry */ if (G_obj_table->is_obj_deletable(&entry->val)) { /* it's deletable, so delete the entire record */ unlink_entry(vmg_ entry, hashval, prv); /* * note that we do NOT advance the prv pointer - we've * deleted this entry, so the one preceding it is now the * previous for the one following this one */ } else { /* we're keeping this entry - make it the new previous */ prv = entry; } } } } /* ------------------------------------------------------------------------ */ /* * LookupTable Iterator metaclass */ /* * statics */ /* metaclass registration object */ static CVmMetaclassIterLookupTable iter_metaclass_reg_obj; CVmMetaclass *CVmObjIterLookupTable::metaclass_reg_ = &iter_metaclass_reg_obj; /* create a list with no initial contents */ vm_obj_id_t CVmObjIterLookupTable::create_for_coll(VMG_ const vm_val_t *coll) { vm_obj_id_t id; /* push the collection object reference for gc protection */ G_stk->push(coll); /* create a non-root-set object */ id = vm_new_id(vmg_ FALSE, TRUE, TRUE); /* instantiate the iterator */ new (vmg_ id) CVmObjIterLookupTable(vmg_ coll); /* done with the gc protection */ G_stk->discard(); /* return the new object's ID */ return id; } /* * constructor */ CVmObjIterLookupTable::CVmObjIterLookupTable(VMG_ const vm_val_t *coll) { /* allocate space for our extension data */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(VMOBJITERLOOKUPTABLE_EXT_SIZE, this); /* save the collection value */ vmb_put_dh(ext_, coll); /* clear the flags */ set_flags(0); /* * set up at index zero - we're not at a valid entry until initialized * with getNext */ set_entry_index(0); } /* * Find the first valid entry, starting at the given entry index. If the * given index refers to a valid entry, we return it. If there is no * valid next entry, we return zero. */ uint CVmObjIterLookupTable::find_first_valid_entry(VMG_ uint entry) const { CVmObjLookupTable *ltab; vm_val_t coll; /* get my lookup table object */ get_coll_val(&coll); ltab = (CVmObjLookupTable *)vm_objp(vmg_ coll.val.obj); /* if we're already past the last item, we have nothing to find */ if (entry > ltab->get_entry_count()) return 0; /* * scan entries until we find one with a non-empty key (an empty key * value indicates that the entry is free) */ for ( ; entry <= ltab->get_entry_count() ; ++entry) { vm_lookup_val *entryp; /* translate the 1-based index to an entry pointer */ entryp = ltab->get_ext()->idx_to_val(entry - 1); /* if it's non-empty, this is the one we want */ if (entryp->key.typ != VM_EMPTY) return entry; } /* we didn't find a valid entry */ return 0; } /* * notify of deletion */ void CVmObjIterLookupTable::notify_delete(VMG_ int) { /* free our extension */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* * property evaluator - get the next item */ int CVmObjIterLookupTable::getp_get_next(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { uint entry; static CVmNativeCodeDesc desc(0); CVmObjLookupTable *ltab; vm_val_t coll; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get my collection object */ get_coll_val(&coll); ltab = (CVmObjLookupTable *)vm_objp(vmg_ coll.val.obj); /* advance to the next valid index after the current one */ entry = find_first_valid_entry(vmg_ get_entry_index() + 1); /* * if we're past the end of the table, set the index to zero to * indicate that we're done */ if (entry == 0) err_throw(VMERR_OUT_OF_RANGE); /* retrieve the current entry */ *retval = ltab->get_ext()->idx_to_val(entry - 1)->val; /* update our internal state, saving undo */ set_entry_index_undo(vmg_ self, entry); /* handled */ return TRUE; } /* * property evaluator - get current value */ int CVmObjIterLookupTable::getp_get_cur_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the entry's value */ get_cur_entry(vmg_ retval, 0); /* handled */ return TRUE; } /* * property evaluator - get current key */ int CVmObjIterLookupTable::getp_get_cur_key(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the entry's key */ get_cur_entry(vmg_ 0, retval); /* handled */ return TRUE; } /* * Get the current entry index, value, and key for the current entry. Any * of the output pointers can be null if the corresponding value is not * needed by the caller. */ void CVmObjIterLookupTable::get_cur_entry(VMG_ vm_val_t *valp, vm_val_t *keyp) const { uint entry; vm_val_t coll; CVmObjLookupTable *ltab; vm_lookup_val *entryp; /* get my collection object */ get_coll_val(&coll); ltab = (CVmObjLookupTable *)vm_objp(vmg_ coll.val.obj); /* make sure the index is in range */ entry = get_entry_index(); if (entry < 1 || entry > ltab->get_entry_count()) err_throw(VMERR_OUT_OF_RANGE); /* translate our 1-based index to an entry pointer */ entryp = ltab->get_ext()->idx_to_val(entry - 1); /* if the current entry has been deleted, we have no valid current item */ if (entryp->key.typ == VM_EMPTY) err_throw(VMERR_OUT_OF_RANGE); /* retrieve this entry's value and key for the caller, as desired */ if (valp != 0) *valp = entryp->val; if (keyp != 0) *keyp = entryp->key; } /* * property evaluator - is next value available? */ int CVmObjIterLookupTable::getp_is_next_avail(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { uint entry; static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* find the index of the next valid entry */ entry = find_first_valid_entry(vmg_ get_entry_index() + 1); /* return true if we have a valid index, false if not */ retval->set_logical(entry != 0); /* handled */ return TRUE; } /* * property evaluator - reset to first item */ int CVmObjIterLookupTable::getp_reset_iter(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* set our entry index back to zero, saving undo */ set_entry_index_undo(vmg_ self, 0); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* * Set the next index value, saving undo if necessary */ void CVmObjIterLookupTable::set_entry_index_undo( VMG_ vm_obj_id_t self, uint entry_idx) { /* save undo if necessary */ if (G_undo != 0 && !(get_flags() & VMOBJITERLOOKUPTABLE_UNDO)) { vm_val_t val; /* * Add the undo record. Store the entry index as the key (it's * not really a key, but it'll do as a place to store the data). * We don't need anything in the payload, so store a dummy nil * value. */ val.set_nil(); G_undo->add_new_record_int_key(vmg_ self, get_entry_index(), &val); /* * set the undo bit so we don't save redundant undo for this * savepoint */ set_flags(get_flags() | VMOBJITERLOOKUPTABLE_UNDO); } /* set the index */ set_entry_index(entry_idx); } /* * apply undo */ void CVmObjIterLookupTable::apply_undo(VMG_ CVmUndoRecord *rec) { /* * the integer key in the undo record is my saved index value (and is * the only thing in our state that can ever change) */ set_entry_index((uint)rec->id.intval); } /* * mark references */ void CVmObjIterLookupTable::mark_refs(VMG_ uint state) { vm_val_t coll; /* my collection is always an object - mark it as referenced */ get_coll_val(&coll); G_obj_table->mark_all_refs(coll.val.obj, state); } /* * load from an image file */ void CVmObjIterLookupTable::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* if we already have memory allocated, free it */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* * Allocate a new extension. Make sure it's at least as large as the * current standard extension size. */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(siz < VMOBJITERLOOKUPTABLE_EXT_SIZE ? VMOBJITERLOOKUPTABLE_EXT_SIZE : siz, this); /* copy the image data to our extension */ memcpy(ext_, ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); /* clear the undo flag */ set_flags(get_flags() & ~VMOBJITERLOOKUPTABLE_UNDO); } /* * re-load from an image file */ void CVmObjIterLookupTable::reload_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t siz) { /* re-copy the image data */ memcpy(ext_, ptr, siz); /* clear the undo flag */ set_flags(get_flags() & ~VMOBJITERLOOKUPTABLE_UNDO); } /* * save */ void CVmObjIterLookupTable::save_to_file(VMG_ CVmFile *fp) { /* write my extension */ fp->write_bytes(ext_, VMOBJITERLOOKUPTABLE_EXT_SIZE); } /* * restore */ void CVmObjIterLookupTable::restore_from_file(VMG_ vm_obj_id_t, CVmFile *fp, CVmObjFixup *fixups) { /* free any existing extension */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* allocate a new extension */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(VMOBJITERLOOKUPTABLE_EXT_SIZE, this); /* read my extension */ fp->read_bytes(ext_, VMOBJITERLOOKUPTABLE_EXT_SIZE); /* fix up my collection object reference */ fixups->fix_dh(vmg_ ext_); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmlookup.h������������������������������������������������������������������������0000664�0000000�0000000�00000075753�12650170723�0015724�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmlookup.h - LookupTable metaclass Function Notes Modified 02/06/01 MJRoberts - Creation */ #ifndef VMLOOKUP_H #define VMLOOKUP_H #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmcoll.h" #include "vmiter.h" /* ------------------------------------------------------------------------ */ /* * The image file data block is arranged as follows: * *. UINT2 bucket_count *. UINT2 value_count *. UINT2 first_free_index *. UINT2 bucket_index[1] *. UINT2 bucket_index[2] *. ... *. UINT2 bucket_index[N] *. value[1] *. value[2] *. ... *. value[N] *. --- the following are only used in version 030003 and later --- *. DATAHOLDER default_value * * value_count gives the number of value slots allocated. Free value slots * are kept in a linked list, the head of which is at the 1-based index * given by first_free_index. If first_free_index is zero, there are no * free value slots. * * Each bucket_index[i] is the 1-based index of the first value in the * chain for that hash bucket. If the value in a bucket_index[i] is zero, * there are values for that bucket. * * Each free entry has a VM_EMPTY value stored in its key to indicate that * it's empty. * * Each value[i] looks like this: * *. DATAHOLDER key *. DATAHOLDER value *. UINT2 next_index * * next_index gives the 1-based index of the next value in the chain for * that bucket; a value of zero indicates that this is the last value in * the chain. * * For version 030003 and later, additional data MAY be supplied. This can * be detected by checking the data block size: if more bytes follow the * last value slot, the extended version 030003 elements are present. * * default_value is the default value to return when the table is indexed * by a key that doesn't exist in the table. This is nil by default. */ /* value entry size */ #define VMLOOKUP_VALUE_SIZE (VMB_DATAHOLDER + VMB_DATAHOLDER + VMB_UINT2) /* ------------------------------------------------------------------------ */ /* * in-memory value entry structure */ struct vm_lookup_val { /* the key */ vm_val_t key; /* the value */ vm_val_t val; /* next entry in same hash bucket */ vm_lookup_val *nxt; }; /* * Our in-memory extension data structure, which mimics the image file * structure but uses native types. */ struct vm_lookup_ext { /* allocate the structure, given the number of buckets and values */ static vm_lookup_ext *alloc_ext(VMG_ class CVmObjLookupTable *self, uint bucket_cnt, uint value_cnt); /* * Initialize the extension - puts all values into the free list and * clears all buckets. We don't do this automatically as part of * allocation, because some types of allocation set up the buckets and * free list from a known data set and thus are more efficient if they * skip the initialization step. */ void init_ext(); /* * Reallocate the structure with a larger number of values. Copies * all of the data from the original hash table into the new hash * table, and deletes the old structure. */ static vm_lookup_ext *expand_ext(VMG_ class CVmObjLookupTable *self, vm_lookup_ext *old_ext, uint new_value_cnt); /* * Copy the given extension's data into myself. This can only be used * when we have the same bucket count as the original (the entry count * need not be the same, but it must be large enough to hold all of * the data from the original). * * This loses any data previously in the table. */ void copy_ext_from(vm_lookup_ext *old_ext); /* allocate a value entry out of my free list */ vm_lookup_val *alloc_val_entry() { /* if the free list is empty, return failure */ if (first_free == 0) return 0; /* take the first item off the free list */ vm_lookup_val *entry = first_free; /* unlink it from the free list */ first_free = first_free->nxt; /* return the allocated item */ return entry; } /* * Add a value into the given hash bucket. The caller is responsible * for ensuring there's enough room. */ void add_val(uint hash, const vm_val_t *key, const vm_val_t *val) { /* allocate a new entry */ vm_lookup_val *entry = alloc_val_entry(); /* set it up with the new data */ entry->key = *key; entry->val = *val; /* link it into the given bucket */ entry->nxt = buckets[hash]; buckets[hash] = entry; } /* * Given a pool index, retrieve a value entry from our pool of value * entries. This has nothing to do with the hash bucket lists or the * free list - this is simply the nth entry in the master pool of all * values. */ vm_lookup_val *idx_to_val(uint idx) const { vm_lookup_val *pool; /* the pool of values starts immediately after the buckets */ pool = (vm_lookup_val *)(void *)&buckets[bucket_cnt]; /* return the nth element of the pool */ return &pool[idx]; } /* given a value entry, get the pool index */ uint val_to_idx(vm_lookup_val *val) const { return (val - idx_to_val(0)); } /* * Convert an image-file or save-file index to a value pointer. These * are given as 1-based pointers, with the special value zero used to * indicate a null pointer. */ vm_lookup_val *img_idx_to_val(uint idx) const { if (idx == 0) return 0; else return idx_to_val(idx - 1); } /* convert a value pointer to an image file index */ uint val_to_img_idx(vm_lookup_val *val) { /* * use zero for a null pointer; otherwise, use a 1-based index in * our master value pool */ if (val == 0) return 0; else return (val - idx_to_val(0)) + 1; } /* number of buckets and number of allocated value entries */ uint bucket_cnt; uint value_cnt; /* pointer to the first free value */ vm_lookup_val *first_free; /* * default value - this is returned when we index the table by a key * that doesn't exist in the table */ vm_val_t default_value; /* * buckets (we overallocate the structure to make room): each bucket * points to the first entry in the list of entries at this hash value */ vm_lookup_val *buckets[1]; }; /* ------------------------------------------------------------------------ */ /* * undo action codes */ enum lookuptab_undo_action { /* * null record - we use this to mark a record that has become * irrelevant because of a stale weak reference */ LOOKUPTAB_UNDO_NULL, /* we added this word to the dictionary (undo by deleting it) */ LOOKUPTAB_UNDO_ADD, /* we deleted this word from the dictionary (undo by adding it back) */ LOOKUPTAB_UNDO_DEL, /* we modified the value for a given key */ LOOKUPTAB_UNDO_MOD, /* we changed the default value for the table */ LOOKUPTAB_UNDO_DEFVAL }; /* ------------------------------------------------------------------------ */ /* * LookupTable metaclass */ class CVmObjLookupTable: public CVmObjCollection { friend class CVmObjIterLookupTable; friend class CVmMetaclassLookupTable; friend class CVmObjWeakRefLookupTable; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObjCollection::is_of_metaclass(meta)); } /* is this a lookup table object? */ static int is_lookup_table_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create */ static vm_obj_id_t create(VMG_ int in_root_set, uint bucket_count, uint init_capacity); /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjCollection:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self) { /* we cannot be converted to constant data */ } /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * receive savepoint notification - we don't keep any * savepoint-relative records, so we don't need to do anything here */ void notify_new_savept() { } /* apply an undo record */ void apply_undo(VMG_ struct CVmUndoRecord *rec); /* discard an undo record */ void discard_undo(VMG_ struct CVmUndoRecord *); /* mark undo references */ void mark_undo_ref(VMG_ struct CVmUndoRecord *rec); /* mark references */ void mark_refs(VMG_ uint); /* we keep only strong references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* * determine if we've been changed since loading - assume we have (if * we haven't, the only harm is the cost of unnecessarily reloading or * saving) */ int is_changed_since_load() const { return TRUE; } /* get an entry; returns true if the entry exists, false if not */ int index_check(VMG_ vm_val_t *result, const vm_val_t *index_val); /* get a value by index */ int index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val); /* set a value by index */ int set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val); /* add an entry - does not generate undo */ void add_entry(VMG_ const vm_val_t *key, const vm_val_t *val); /* set or add an entry - does not generate undo */ void set_or_add_entry(VMG_ const vm_val_t *key, const vm_val_t *val); /* make a list of keys/values in the table */ void keys_to_list(VMG_ vm_val_t *lst, int (*filter)(VMG_ const vm_val_t *, const vm_val_t *) = 0) { make_list(vmg_ lst, TRUE, filter); } void vals_to_list(VMG_ vm_val_t *lst, int (*filter)(VMG_ const vm_val_t *, const vm_val_t *) = 0) { make_list(vmg_ lst, FALSE, filter); } /* iterate over the table's contents through a callback */ void for_each(VMG_ void (*cb)(VMG_ const vm_val_t *key, const vm_val_t *val, void *ctx), void *ctx); /* get the default value */ void get_default_val(vm_val_t *val); protected: /* get and range-check the constructor arguments */ static void get_constructor_args(VMG_ uint argc, size_t *bucket_count, size_t *init_capacity, vm_val_t *src_obj); /* populate the table from a Key->Value list or vector */ void populate_from_list(VMG_ const vm_val_t *src_obj); /* load or reload image data */ void load_image_data(VMG_ const char *ptr, size_t siz); /* create a new object as a copy of this object */ vm_obj_id_t create_copy(VMG0_); /* add an entry, generating undo */ void add_entry_undo(VMG_ vm_obj_id_t self, const vm_val_t *key, const vm_val_t *val); /* delete an entry - does not generate undo */ void del_entry(VMG_ const vm_val_t *key); /* * Unlink an entry - does not generate undo. 'prv_entry' is the * previous entry in the hash chain containing this entry, and * 'hashval' is the bucket containing the entry. Pass null for * 'prv_entry' when the entry to unlink is the first entry in its hash * chain. */ void unlink_entry(VMG_ vm_lookup_val *entry, uint hashval, vm_lookup_val *prv_entry); /* * modify an entry - changes the value associated with the given key; * does not generate undo */ void mod_entry(VMG_ const vm_val_t *key, const vm_val_t *val); /* find an entry */ vm_lookup_val *find_entry(VMG_ const vm_val_t *key, uint *hashval_p, vm_lookup_val **prv_entry_p); /* * Check the table to make sure there's enough free space to add one * new item, and expand the table if necessary. */ void expand_if_needed(VMG0_); /* allocate a new entry, expanding the table if necessary */ vm_lookup_val *alloc_new_entry(VMG0_); /* calculate a value's hash code */ uint calc_key_hash(VMG_ const vm_val_t *key); /* get my extension data */ vm_lookup_ext *get_ext() const { return (vm_lookup_ext *)ext_; } /* get the hash bucket count */ uint get_bucket_count() const { return get_ext()->bucket_cnt; } /* get the value entry count */ uint get_entry_count() const { return get_ext()->value_cnt; } /* get/set the first-free item */ vm_lookup_val *get_first_free() const { return get_ext()->first_free; } void set_first_free(vm_lookup_val *p) { get_ext()->first_free = p; } /* get a bucket's first entry given a hash code */ vm_lookup_val **get_bucket(uint hash) const { return &get_ext()->buckets[hash]; } /* set a bucket's contents given a hash code */ void set_bucket(uint hash, vm_lookup_val *p) { get_ext()->buckets[hash] = p; } /* set an entry, keeping undo for the change */ void set_entry_val_undo(VMG_ vm_obj_id_t self, vm_lookup_val *entry, const vm_val_t *val); /* create an iterator */ virtual void new_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val); /* create a live iterator */ virtual void new_live_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val); /* create a lookup table with no initial contents */ CVmObjLookupTable() { ext_ = 0; } /* * Create a lookup table with a given number of hash table buckets, * and the given number of entry slots. The hash table bucket count * is fixed for the life of the object. The entry slot count is * merely advisory: the table size will be increased as necessary to * accommodate new elements. */ CVmObjLookupTable(VMG_ size_t hash_count, size_t entry_count); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - remove an entry given the key */ int getp_remove_entry(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - determine if a given key is in the table */ int getp_key_present(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - apply a callback to each element */ int getp_apply_all(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - call a callback on each element */ int getp_for_each(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - call a callback on each element */ int getp_for_each_assoc(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* general forEach/forEachAssoc processor */ int for_each_gen(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc, int pass_key_to_cb, struct vm_rcdesc *rc); /* get the number of buckets in the table */ int getp_count_buckets(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* get the number of entries in the table */ int getp_count_entries(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* make a list of all of the keys in the table */ int getp_keys_to_list(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* make a list of all of the values in the table */ int getp_vals_to_list(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* get the default value */ int getp_get_def_val(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* set the default value */ int getp_set_def_val(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* general handler for making a list of keys or values */ int getp_make_list(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int store_keys); /* get the nth key */ int getp_nthKey(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* get the nth value */ int getp_nthVal(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* * Get the nth element's key and/or value. This only counts in-use * buckets. If idx is zero, we return the default value. If idx is * out of range, we throw an error. */ void get_nth_ele(VMG_ vm_val_t *key, vm_val_t *val, vm_obj_id_t self, long idx); /* make a list of the keys or values in the table */ void make_list(VMG_ vm_val_t *retval, int store_keys, int (*filter)(VMG_ const vm_val_t *, const vm_val_t *)); /* add a record to the global undo stream */ void add_undo_rec(VMG_ vm_obj_id_t self, enum lookuptab_undo_action action, const vm_val_t *key, const vm_val_t *old_entry_val); /* property evaluation function table */ static int (CVmObjLookupTable::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * WeakRefLookupTable - a subclass of LookupTable that places weak * references on its values. The keys are still strong references. */ class CVmObjWeakRefLookupTable: public CVmObjLookupTable { friend class CVmMetaclassWeakRefLookupTable; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObjLookupTable::is_of_metaclass(meta)); } /* create */ static vm_obj_id_t create(VMG_ int in_root_set, uint bucket_count, uint init_capacity); /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjLookupTable:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* mark references */ void mark_refs(VMG_ uint); /* mark undo references */ void mark_undo_ref(VMG_ struct CVmUndoRecord *rec); /* remove stale weak references */ void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *rec); /* remove stale weak references */ void remove_stale_weak_refs(VMG0_); protected: /* create a lookup table with no initial contents */ CVmObjWeakRefLookupTable() { ext_ = 0; } /* construct */ CVmObjWeakRefLookupTable(VMG_ size_t hash_count, size_t entry_count) : CVmObjLookupTable(vmg_ hash_count, entry_count) { } }; /* ------------------------------------------------------------------------ */ /* * LookupTable Registration table object */ class CVmMetaclassLookupTable: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "lookuptable/030003"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjLookupTable(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjLookupTable(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjLookupTable::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjLookupTable:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* I'm a Collection object */ CVmMetaclass *get_supermeta_reg() const { return CVmObjCollection::metaclass_reg_; } }; /* * WeakRefLookupTable registration object */ class CVmMetaclassWeakRefLookupTable: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "weakreflookuptable/030001"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjWeakRefLookupTable(); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjWeakRefLookupTable(); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjWeakRefLookupTable:: create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjWeakRefLookupTable:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* I'm a LookupTable object */ CVmMetaclass *get_supermeta_reg() const { return CVmObjLookupTable::metaclass_reg_; } }; /* ------------------------------------------------------------------------ */ /* * LookupTable Iterator subclass. This iterator is tightly coupled with * the LookupTable class, since we must look into the internal * implementation of the LookupTable to implement an iterator on it. */ /* * The extension data for an lookup table iterator consists of a reference * to the associated LookupTable object, the current bucket index, and the * index of the current entry. * * DATAHOLDER lookuptable_value *. UINT2 cur_entry_index (1-based; zero is invalid) *. UINT2 flags * * The flag values are: * * VMOBJITERLOOKUPTABLE_UNDO - we've saved undo for this savepoint. If * this is set, we won't save additional undo for the same savepoint. */ /* total extension size */ #define VMOBJITERLOOKUPTABLE_EXT_SIZE (VMB_DATAHOLDER + 2 + 2) /* * flag bits */ /* we've saved undo for the current savepoint */ #define VMOBJITERLOOKUPTABLE_UNDO 0x0001 /* * LookupTable iterator class */ class CVmObjIterLookupTable: public CVmObjIter { friend class CVmMetaclassIterLookupTable; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObjIter::is_of_metaclass(meta)); } /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjIter::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* * Create a lookup table iterator. This method is to be called by the * lookup table to create an iterator for its value. */ static vm_obj_id_t create_for_coll(VMG_ const vm_val_t *coll); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* * notify of a new savepoint - clear the 'undo' flag, since we cannot * have created any undo information yet for the new savepoint */ void notify_new_savept() { set_flags(get_flags() & ~VMOBJITERLOOKUPTABLE_UNDO); } /* apply undo */ void apply_undo(VMG_ struct CVmUndoRecord *rec); /* mark references */ void mark_refs(VMG_ uint state); /* there are no references in our undo stream */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* we keep only strong references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* * determine if the object has been changed since it was loaded - * assume we have */ int is_changed_since_load() const { return TRUE; } /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); protected: /* create */ CVmObjIterLookupTable() { ext_ = 0; } /* create */ CVmObjIterLookupTable(VMG_ const vm_val_t *coll); /* find the first valid entry, starting at the given index */ uint find_first_valid_entry(VMG_ uint entry) const; /* property evaluator - get next value */ virtual int getp_get_next(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - is next value available? */ virtual int getp_is_next_avail(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - reset to first item */ virtual int getp_reset_iter(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get current key */ virtual int getp_get_cur_key(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get current value */ virtual int getp_get_cur_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get information on the current entry */ void get_cur_entry(VMG_ vm_val_t *valp, vm_val_t *keyp) const; /* get my collection value */ void get_coll_val(vm_val_t *val) const { vmb_get_dh(ext_, val); } /* get/set the current entry index (without saving undo) */ long get_entry_index() const { return osrp2(ext_ + VMB_DATAHOLDER); } void set_entry_index(uint idx) { oswp2(ext_ + VMB_DATAHOLDER, idx); } /* update the entry index value, saving undo if necessary */ void set_entry_index_undo(VMG_ vm_obj_id_t self, uint entry); /* get/set the flags */ uint get_flags() const { return osrp2(ext_ + VMB_DATAHOLDER + 2); } void set_flags(uint flags) const { oswp2(ext_ + VMB_DATAHOLDER + 2, flags); } }; /* * Registration table object for lookup table iterators */ class CVmMetaclassIterLookupTable: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "lookuptable-iterator/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjIterLookupTable(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjIterLookupTable(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { err_throw(VMERR_BAD_DYNAMIC_NEW); AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjIterLookupTable:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMLOOKUP_H */ /* * Register the classes */ VM_REGISTER_METACLASS(CVmObjLookupTable) VM_REGISTER_METACLASS(CVmObjWeakRefLookupTable) VM_REGISTER_METACLASS(CVmObjIterLookupTable) ���������������������qtads-2.1.7/tads3/vmlst.cpp�������������������������������������������������������������������������0000664�0000000�0000000�00000273042�12650170723�0015537�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMLST.CPP,v 1.3 1999/05/17 02:52:28 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmlst.cpp - list metaclass Function Notes Modified 10/29/98 MJRoberts - Creation */ #include <stdlib.h> #include <string.h> #include "t3std.h" #include "vmmcreg.h" #include "vmtype.h" #include "vmlst.h" #include "vmobj.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmfile.h" #include "vmpool.h" #include "vmstack.h" #include "vmmeta.h" #include "vmrun.h" #include "vmbif.h" #include "vmpredef.h" #include "vmiter.h" #include "vmsort.h" #include "vmstr.h" #include "vmbiftad.h" /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassList metaclass_reg_obj; CVmMetaclass *CVmObjList::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (*CVmObjList::func_table_[])(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) = { &CVmObjList::getp_undef, /* 0 */ &CVmObjList::getp_subset, /* 1 */ &CVmObjList::getp_map, /* 2 */ &CVmObjList::getp_len, /* 3 */ &CVmObjList::getp_sublist, /* 4 */ &CVmObjList::getp_intersect, /* 5 */ &CVmObjList::getp_index_of, /* 6 */ &CVmObjList::getp_car, /* 7 */ &CVmObjList::getp_cdr, /* 8 */ &CVmObjList::getp_index_which, /* 9 */ &CVmObjList::getp_for_each, /* 10 */ &CVmObjList::getp_val_which, /* 11 */ &CVmObjList::getp_last_index_of, /* 12 */ &CVmObjList::getp_last_index_which, /* 13 */ &CVmObjList::getp_last_val_which, /* 14 */ &CVmObjList::getp_count_of, /* 15 */ &CVmObjList::getp_count_which, /* 16 */ &CVmObjList::getp_get_unique, /* 17 */ &CVmObjList::getp_append_unique, /* 18 */ &CVmObjList::getp_append, /* 19 */ &CVmObjList::getp_sort, /* 20 */ &CVmObjList::getp_prepend, /* 21 */ &CVmObjList::getp_insert_at, /* 22 */ &CVmObjList::getp_remove_element_at, /* 23 */ &CVmObjList::getp_remove_range, /* 24 */ &CVmObjList::getp_for_each_assoc, /* 25 */ &CVmObjList::getp_generate, /* 26 */ &CVmObjList::getp_splice, /* 27 */ &CVmObjList::getp_join, /* 28 */ &CVmObjList::getp_indexOfMin, /* 29 */ &CVmObjList::getp_minVal, /* 30 */ &CVmObjList::getp_indexOfMax, /* 31 */ &CVmObjList::getp_maxVal /* 32 */ }; /* static property indices */ const int PROPIDX_generate = 26; /* ------------------------------------------------------------------------ */ /* * Static creation methods. These routines allocate an object ID and * create a new list object. */ /* create dynamically using stack arguments */ vm_obj_id_t CVmObjList::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id; CVmObjList *lst; size_t idx; /* * create the list - this type of construction is never used for * root set objects */ id = vm_new_id(vmg_ FALSE, TRUE, FALSE); /* create a list with one element per argument */ lst = new (vmg_ id) CVmObjList(vmg_ argc); /* add each argument */ for (idx = 0 ; idx < argc ; ++idx) { /* retrieve the next element from the stack and add it to the list */ lst->cons_set_element(idx, G_stk->get(idx)); } /* discard the stack parameters */ G_stk->discard(argc); /* return the new object */ return id; } /* * create dynamically from the current method's parameters */ vm_obj_id_t CVmObjList::create_from_params(VMG_ uint param_idx, uint cnt) { vm_obj_id_t id; CVmObjList *lst; size_t idx; /* create the new list object as a non-root-set object */ id = vm_new_id(vmg_ FALSE, TRUE, FALSE); lst = new (vmg_ id) CVmObjList(vmg_ cnt); /* copy each parameter into the new list */ for (idx = 0 ; cnt != 0 ; --cnt, ++param_idx, ++idx) { /* retrieve the next element and add it to the list */ lst->cons_set_element( idx, G_interpreter->get_param(vmg_ param_idx)); } /* return the new object */ return id; } /* create a list with no initial contents */ vm_obj_id_t CVmObjList::create(VMG_ int in_root_set) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmObjList(); return id; } /* create a list with a given number of elements */ vm_obj_id_t CVmObjList::create(VMG_ int in_root_set, size_t element_count) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmObjList(vmg_ element_count); return id; } /* create a list by copying a constant list */ vm_obj_id_t CVmObjList::create(VMG_ int in_root_set, const char *lst) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmObjList(vmg_ lst); return id; } /* ------------------------------------------------------------------------ */ /* * Constructors. These are called indirectly through our static * creation methods. */ /* * create a list object from a constant list */ CVmObjList::CVmObjList(VMG_ const char *lst) { /* get the element count from the original list */ size_t cnt = vmb_get_len(lst); /* allocate space */ alloc_list(vmg_ cnt); /* copy the list's contents */ memcpy(ext_, lst, calc_alloc(cnt)); } /* * Create a list with a given number of elements. This can be used to * construct a list element-by-element. */ CVmObjList::CVmObjList(VMG_ size_t cnt) { /* allocate space */ alloc_list(vmg_ cnt); /* * Clear the list. Since the caller is responsible for populating the * list in this version of the constructor, it's possible that GC will * run between now and the time the list is fully populated. We must * initialize the list to ensure that we don't misinterpret the * contents as valid should we run GC between now and the time the * caller has finished populating the list. It's adequate to set the * list to all zeros, since we won't try to interpret the contents as * valid if the type markers are all invalid. */ memset(ext_ + VMB_LEN, 0, calc_alloc(cnt) - VMB_LEN); } /* ------------------------------------------------------------------------ */ /* * allocate space for a list with a given number of elements */ void CVmObjList::alloc_list(VMG_ size_t cnt) { /* calculate the allocation size */ size_t alo = calc_alloc(cnt); /* * ensure we're within the limit (NB: this really is 65535 on ALL * PLATFORMS - this is a portable limit imposed by the storage format, * not a platform-specific size limit */ if (alo > 65535) { ext_ = 0; err_throw(VMERR_LIST_TOO_LONG); } /* allocate space for the given number of elements */ ext_ = (char *)G_mem->get_var_heap()->alloc_mem(alo, this); /* set the element count */ vmb_put_len(ext_, cnt); } /* ------------------------------------------------------------------------ */ /* * construction: set an element */ void CVmObjList::cons_set_element(size_t idx, const vm_val_t *val) { /* set the element's value */ vmb_put_dh(get_element_ptr(idx), val); } /* * construction: copy a list into our list */ void CVmObjList::cons_copy_elements(size_t idx, const char *orig_list) { /* copy the elements */ memcpy(get_element_ptr(idx), orig_list + VMB_LEN, (vmb_get_len(orig_list) * VMB_DATAHOLDER)); } /* * construction: copy element data into our list */ void CVmObjList::cons_copy_data(size_t idx, const char *ele_array, size_t ele_count) { /* copy the elements */ memcpy(get_element_ptr(idx), ele_array, ele_count * VMB_DATAHOLDER); } /* * construction: ensure there's enough space for the given number of * elements, expanding the list if necessary */ void CVmObjList::cons_ensure_space(VMG_ size_t idx, size_t margin) { /* if there's not enough space, expand the list */ size_t old_cnt = vmb_get_len(ext_); if (idx >= old_cnt) { /* calculate the new size */ size_t new_cnt = idx + 1 + margin; size_t new_siz = calc_alloc(new_cnt); /* make sure it's in range */ if (new_siz > 65535) err_throw(VMERR_LIST_TOO_LONG); /* reallocate the list at the new size */ ext_ = (char *)G_mem->get_var_heap()->realloc_mem(new_siz, ext_, this); /* set the new length */ vmb_put_len(ext_, new_cnt); /* * Because the caller is iteratively building the list, we should * assume that garbage collection might be triggered during the * process. Make sure that if the gc visits our elements during * construction, it finds only valid data (not, for example, stray * pointers to invalid objects). */ cons_clear(old_cnt, new_cnt - 1); } } /* * construction: clear a portion of the list, setting each element in the * range (inclusive of the endpoints) to nil */ void CVmObjList::cons_clear(size_t start_idx, size_t end_idx) { /* proceed only if we have a non-zero number of elements */ size_t cnt = get_ele_count(); if (cnt != 0) { /* limit end_idx to the last element */ if (end_idx >= cnt) end_idx = cnt - 1; /* clear the range */ size_t i; char *p; for (i = start_idx, p = get_element_ptr(i) ; i <= end_idx ; ++i, p += VMB_DATAHOLDER) vmb_put_dh_nil(p); } } /* ------------------------------------------------------------------------ */ /* * receive notification of deletion */ void CVmObjList::notify_delete(VMG_ int in_root_set) { /* free our extension */ if (ext_ != 0 && !in_root_set) G_mem->get_var_heap()->free_mem(ext_); } /* ------------------------------------------------------------------------ */ /* * call a static property */ int CVmObjList::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* get the function table index */ int idx = G_meta_table->prop_to_vector_idx( metaclass_reg_->get_reg_idx(), prop); /* check for static methods */ switch (idx) { case PROPIDX_generate: return static_getp_generate(vmg_ result, argc); default: /* inherit the default handling */ return CVmObjCollection::call_stat_prop( vmg_ result, pc_ptr, argc, prop); } } /* ------------------------------------------------------------------------ */ /* * Set a property. Lists have no settable properties, so simply signal * an error indicating that the set-prop call is invalid. */ void CVmObjList::set_prop(VMG_ CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * Save the object to a file */ void CVmObjList::save_to_file(VMG_ CVmFile *fp) { size_t cnt; /* get our element count */ cnt = vmb_get_len(ext_); /* write the count and the elements */ fp->write_bytes(ext_, calc_alloc(cnt)); } /* * Restore the object from a file */ void CVmObjList::restore_from_file(VMG_ vm_obj_id_t, CVmFile *fp, CVmObjFixup *fixups) { size_t cnt; /* read the element count */ cnt = fp->read_uint2(); /* free any existing extension */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* allocate the space */ alloc_list(vmg_ cnt); /* store our element count */ vmb_put_len(ext_, cnt); /* read the contents, if there are any elements */ fp->read_bytes(ext_ + VMB_LEN, cnt * VMB_DATAHOLDER); /* fix object references */ fixups->fix_dh_array(vmg_ ext_ + VMB_LEN, cnt); } /* ------------------------------------------------------------------------ */ /* * Mark references */ void CVmObjList::mark_refs(VMG_ uint state) { size_t cnt; char *p; /* get my element count */ cnt = vmb_get_len(ext_); /* mark as referenced each object in our list */ for (p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p)) { /* * if this is an object, mark it as referenced, and mark its * references as referenced */ if (vmb_get_dh_type(p) == VM_OBJ) G_obj_table->mark_all_refs(vmb_get_dh_obj(p), state); } } /* ------------------------------------------------------------------------ */ /* * Add a value to the list. This yields a new list, with the value * appended to the existing list. If the value to be appended is itself * a list (constant or object), we'll append each element of that list * to our list (rather than appending a single element containing a * sublist). */ int CVmObjList::add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { /* * Use the generic list adder, using my extension as the constant * list. We store our extension in the general list format required * by the static adder. */ add_to_list(vmg_ result, self, ext_, val); /* handled */ return TRUE; } /* * Static list adder. This creates a new list object that results from * appending the given value to the given list constant. This is * defined statically so that this code can be shared for adding to * constant pool lists and adding to CVmObjList objects. * * 'lstval' must point to a constant list. The first two bytes of the * list are stored in portable UINT2 format and give the number of * elements in the list; this is immediately followed by a packed array * of data holders in portable format. * * Note that we *always* create a new object to hold the result, even if * the new string is identical to the first, so that we consistently * return a distinct reference from the original. */ void CVmObjList::add_to_list(VMG_ vm_val_t *result, vm_obj_id_t self, const char *lstval, const vm_val_t *rhs) { int lhs_cnt, rhs_cnt, alo_cnt; vm_obj_id_t obj; CVmObjList *objptr; /* push self and the other list for protection against GC */ G_stk->push()->set_obj(self); G_stk->push(rhs); /* get the number of elements in the left-hand ('self') side */ lhs_cnt = vmb_get_len(lstval); /* get the number of elements the right-hand side concatenates */ rhs_cnt = (rhs->is_listlike(vmg0_) ? rhs->ll_length(vmg0_) : -1); /* if it's not a list, allocate on element */ alo_cnt = (rhs_cnt < 0 ? 1 : rhs_cnt); /* allocate a new object to hold the new list */ obj = create(vmg_ FALSE, lhs_cnt + alo_cnt); objptr = (CVmObjList *)vm_objp(vmg_ obj); /* copy the first list into the new object's list buffer */ objptr->cons_copy_elements(0, lstval); /* add the value or its contents */ if (rhs_cnt < 0) { /* single value - add it as-is */ objptr->cons_set_element(lhs_cnt, rhs); } else { /* * clear the rest of the list, in case gc runs while retrieving * elements from the rhs */ objptr->cons_clear(lhs_cnt, lhs_cnt + alo_cnt - 1); /* add each element from the right-hand side */ for (int i = 1 ; i <= rhs_cnt ; ++i) { /* retrieve this element of the rhs */ vm_val_t val; rhs->ll_index(vmg_ &val, i); /* store the element in the new list */ objptr->cons_set_element(lhs_cnt + i - 1, &val); } } /* set the result to the new list */ result->set_obj(obj); /* discard the GC protection items */ G_stk->discard(2); } /* ------------------------------------------------------------------------ */ /* * Subtract a value from the list. */ int CVmObjList::sub_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { vm_val_t self_val; /* * Invoke our static list subtraction routine, using our extension * as the constant list. Our extension is stored in the same format * as a constant list, so we can use the same code to handle * subtraction from a list object as we would for subtraction from a * constant list. */ self_val.set_obj(self); sub_from_list(vmg_ result, &self_val, ext_, val); /* handled */ return TRUE; } /* * Subtract a value from a constant list. */ void CVmObjList::sub_from_list(VMG_ vm_val_t *result, const vm_val_t *lstval, const char *lstmem, const vm_val_t *rhs) { int lhs_cnt, rhs_cnt; vm_obj_id_t obj; CVmObjList *objptr; char *dst; size_t dst_cnt; int i; /* push self and the other list for protection against GC */ G_stk->push(lstval); G_stk->push(rhs); /* get the number of elements in the right-hand side */ lhs_cnt = vmb_get_len(lstmem); /* * allocate a new object to hold the new list, which will be no * bigger than the original left-hand side, since we're doing * nothing but (possibly) taking elements out */ obj = create(vmg_ FALSE, lhs_cnt); objptr = (CVmObjList *)vm_objp(vmg_ obj); /* get the number of elements to consider from the right-hand side */ rhs_cnt = (rhs->is_listlike(vmg0_) ? rhs->ll_length(vmg0_) : -1); /* copy the first list into the new object's list buffer */ objptr->cons_copy_elements(0, lstmem); /* consider each element of the left-hand side */ for (i = 0, dst = objptr->get_element_ptr(0), dst_cnt = 0 ; i < lhs_cnt ; ++i) { vm_val_t src_val; int keep; /* * if our list is from constant memory, get its address again -- * the address could have changed due to swapping if we * traversed into another list */ VM_IF_SWAPPING_POOL(if (lstval != 0 && lstval->typ == VM_LIST) lstmem = G_const_pool->get_ptr(lstval->val.ofs)); /* get this element */ vmb_get_dh(get_element_ptr_const(lstmem, i), &src_val); /* presume we'll keep it */ keep = TRUE; /* * scan the right side to see if we can find this value - if we * can, it's to be removed, so we don't want to copy it to the * result list */ if (rhs_cnt < 0) { /* the rhs isn't a list - consider the value directly */ if (rhs->equals(vmg_ &src_val)) { /* it matches, so we're removing it */ keep = FALSE; } } else { /* the rhs is a list - consider each element */ for (int j = 1 ; j <= rhs_cnt ; ++j) { /* retrieve this rhs value */ vm_val_t rem_val; rhs->ll_index(vmg_ &rem_val, j); /* if this value matches, we're removing it */ if (rem_val.equals(vmg_ &src_val)) { /* it's to be removed */ keep = FALSE; /* no need to look any further in the rhs list */ break; } } } /* if we're keeping the value, put it in the result list */ if (keep) { /* store it in the result list */ vmb_put_dh(dst, &src_val); /* advance the result pointer */ inc_element_ptr(&dst); /* count it */ ++dst_cnt; } } /* set the length of the result list */ objptr->cons_set_len(dst_cnt); /* set the result to the new list */ result->set_obj(obj); /* discard the GC protection items */ G_stk->discard(2); } /* ------------------------------------------------------------------------ */ /* * Index the list */ int CVmObjList::index_val_q(VMG_ vm_val_t *result, vm_obj_id_t /*self*/, const vm_val_t *index_val) { /* * use the constant list indexing routine, using our extension data * as the list data */ index_list(vmg_ result, ext_, index_val); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Index a constant list */ void CVmObjList::index_list(VMG_ vm_val_t *result, const char *lst, const vm_val_t *index_val) { /* get the index value as an integer */ uint32_t idx = index_val->num_to_int(vmg0_); /* index the list */ index_list(vmg_ result, lst, idx); } /* * Index a constant list by an integer value */ void CVmObjList::index_list(VMG_ vm_val_t *result, const char *lst, uint idx) { /* make sure it's in range - 1 to our element count, inclusive */ if (idx < 1 || idx > vmb_get_len(lst)) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* * get the indexed element and store it in the result, adjusting the * index to the C-style 0-based range */ get_element_const(lst, idx - 1, result); } /* ------------------------------------------------------------------------ */ /* * Set an element of the list */ int CVmObjList::set_index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val) { /* put the list on the stack to avoid garbage collection */ G_stk->push()->set_obj(self); /* * use the constant list set-index routine, using our extension data * as the list data */ set_index_list(vmg_ result, ext_, index_val, new_val); /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Set an element in a constant list */ void CVmObjList::set_index_list(VMG_ vm_val_t *result, const char *lst, const vm_val_t *index_val, const vm_val_t *new_val) { /* get the index value as an integer */ uint32_t idx = index_val->num_to_int(vmg0_); /* push the new value for gc protection during the create */ G_stk->push(new_val); /* make sure it's in range - 1 to our element count, inclusive */ if (idx < 1 || idx > vmb_get_len(lst)) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* create a new list as a copy of this list */ result->set_obj(create(vmg_ FALSE, lst)); /* get the new list object */ CVmObjList *obj = (CVmObjList *)vm_objp(vmg_ result->val.obj); /* update the element of the new list */ obj->cons_set_element(idx - 1, new_val); /* discard our gc protection */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * Check a value for equality */ int CVmObjList::equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const { /* if it's a reference to myself, we certainly match */ if (val->typ == VM_OBJ && val->val.obj == self) return TRUE; /* * compare via the constant list comparison routine, using our * extension data as the list data */ return const_equals(vmg_ 0, ext_, val, depth); } /* ------------------------------------------------------------------------ */ /* * Constant list comparison routine */ int CVmObjList::const_equals(VMG_ const vm_val_t *lstval, const char *lstmem, const vm_val_t *val, int depth) { size_t cnt; size_t idx; /* if the other value isn't a list, it's no match */ if (!val->is_listlike(vmg0_)) return FALSE; /* if the lists don't have the same length, they don't match */ cnt = vmb_get_len(lstmem); if ((int)cnt != val->ll_length(vmg0_)) return FALSE; /* compare each element in the list */ for (idx = 0 ; idx < cnt ; ++idx) { vm_val_t val1; vm_val_t val2; /* * if either list comes from constant memory, re-translate its * pointer, in case we did any swapping while traversing the * previous element */ VM_IF_SWAPPING_POOL(if (lstval != 0 && lstval->typ == VM_LIST) lstmem = G_const_pool->get_ptr(lstval->val.ofs)); /* get the two elements */ vmb_get_dh(get_element_ptr_const(lstmem, idx), &val1); val->ll_index(vmg_ &val2, idx + 1); /* * If these elements don't match, the lists don't match. Note that * lists can't contain circular references (by their very nature as * immutable objects), so we don't need to increase the depth. */ if (!val1.equals(vmg_ &val2, depth)) return FALSE; } /* if we got here, we didn't find any differences, so they match */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Hash value calculation */ uint CVmObjList::calc_hash(VMG_ vm_obj_id_t self, int depth) const { vm_val_t self_val; /* set up our 'self' value pointer */ self_val.set_obj(self); /* calculate the value */ return const_calc_hash(vmg_ &self_val, ext_, depth); } /* * Hash value calculation */ uint CVmObjList::const_calc_hash(VMG_ const vm_val_t *self_val, const char *lst, int depth) { size_t len; size_t i; uint hash; /* get and skip the length prefix */ len = vmb_get_len(lst); /* calculate a hash combining the hash of each element in the list */ for (hash = 0, i = 0 ; i < len ; ++i) { vm_val_t ele; /* re-translate in case of swapping */ VM_IF_SWAPPING_POOL(if (self_val->typ == VM_LIST) lst = G_const_pool->get_ptr(self_val->val.ofs)); /* get this element */ get_element_const(lst, i, &ele); /* * Compute its hash value and add it into the total. Note that * even though we're recursively calculating the hash of an * element, we don't need to increase the recursion depth, because * it's impossible for a list to have cycles. * * (It's not possible for a list to have cycles because a list is * always constructed with its contents, and can never be changed. * This means that there's no possibility of storing a reference to * the new list inside the list itself, or inside any other list * the list refers to. It *is* possible to put the reference to * the new list in a mutable object to which the list refers, but * in such cases, that mutable object will be capable of having * cycles in its references, so it will be responsible for * increasing the depth counter when it recurses.) */ hash += ele.calc_hash(vmg_ depth); } /* return the hash value */ return hash; } /* ------------------------------------------------------------------------ */ /* * Find a value in a list */ int CVmObjList::find_in_list(VMG_ const vm_val_t *lst, const vm_val_t *val, size_t *idxp) { int cnt; int idx; /* get the length of the list */ cnt = lst->ll_length(vmg0_); /* scan the list for the value */ for (idx = 1 ; idx <= cnt ; ++idx) { vm_val_t curval; /* get this list element */ lst->ll_index(vmg_ &curval, idx); /* compare this value to the one we're looking for */ if (curval.equals(vmg_ val)) { /* this is the one - set the return index */ if (idxp != 0) *idxp = idx; /* indicate that we found the value */ return TRUE; } } /* we didn't find the value */ return FALSE; } /* * Find the last match for a value in a list */ int CVmObjList::find_last_in_list(VMG_ const vm_val_t *lst, const vm_val_t *val, size_t *idxp) { int cnt; int idx; /* get the length of the list */ cnt = lst->ll_length(vmg0_); /* scan the list for the value */ for (idx = cnt ; idx > 0 ; --idx) { vm_val_t curval; /* get this list element */ lst->ll_index(vmg_ &curval, idx); /* compare this value to the one we're looking for */ if (curval.equals(vmg_ val)) { /* this is the one - set the return index */ if (idxp != 0) *idxp = idx; /* indicate that we found the value */ return TRUE; } } /* we didn't find the value */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Compute the intersection of two lists. Returns a new list with the * elements that occur in both lists. */ vm_obj_id_t CVmObjList::intersect(VMG_ const vm_val_t *l1, const vm_val_t *l2) { int cnt1; int cnt2; int idx; vm_obj_id_t resobj; CVmObjList *reslst; size_t residx; /* get the lengths of the lists */ cnt1 = l1->ll_length(vmg0_); cnt2 = l2->ll_length(vmg0_); /* if the first list is larger than the second, swap them */ if (cnt1 > cnt2) { /* swap the vm_val_t pointers */ const vm_val_t *tmp = l1; l1 = l2; l2 = tmp; /* forget the larger count; just copy the smaller list */ cnt1 = cnt2; } /* * Allocate our result list. The result list can't have any more * elements in it than the shorter of the two lists, whose length is * now in cnt1. */ resobj = create(vmg_ FALSE, cnt1); reslst = (CVmObjList *)vm_objp(vmg_ resobj); reslst->cons_clear(); /* we haven't put any elements in the result list yet */ residx = 0; /* * for each element in the first list, find the element in the * second list */ for (idx = 1 ; idx <= cnt1 ; ++idx) { vm_val_t curval; /* get this element from the first list */ l1->ll_index(vmg_ &curval, idx); /* find the element in the second list */ if (find_in_list(vmg_ l2, &curval, 0)) { /* we found it - copy it into the result list */ reslst->cons_set_element(residx, &curval); /* count the new entry in the result list */ ++residx; } } /* * set the actual result length, which might be shorter than the * amount we allocated */ reslst->cons_set_len(residx); /* return the result list */ return resobj; } /* ------------------------------------------------------------------------ */ /* * Uniquify the list; modifies the list in place, so this can only be * used during construction of a new list */ void CVmObjList::cons_uniquify(VMG0_) { size_t cnt; size_t src, dst; /* get the length of the list */ cnt = vmb_get_len(ext_); /* loop through the list and look for repeated values */ for (src = dst = 0 ; src < cnt ; ++src) { size_t idx; vm_val_t src_val; int found; /* * look for a copy of this source value already in the output * list */ index_list(vmg_ &src_val, ext_, src + 1); for (idx = 0, found = FALSE ; idx < dst ; ++idx) { vm_val_t idx_val; /* get this value */ index_list(vmg_ &idx_val, ext_, idx + 1); /* if it's equal to the current source value, note it */ if (src_val.equals(vmg_ &idx_val)) { /* note that we found it */ found = TRUE; /* no need to look any further */ break; } } /* if we didn't find the value, copy it to the output list */ if (!found) { /* add it to the output list */ cons_set_element(dst, &src_val); /* count it */ ++dst; } } /* adjust the size of the result list */ cons_set_len(dst); } /* ------------------------------------------------------------------------ */ /* * Create an iterator */ void CVmObjList::new_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val) { size_t len; /* get the number of elements in the list */ len = vmb_get_len(self_val->get_as_list(vmg0_)); /* * Set up a new indexed iterator object. The first valid index for * a list is always 1, and the last valid index is the same as the * number of elements in the list. */ retval->set_obj(CVmObjIterIdx::create_for_coll(vmg_ self_val, 1, len)); } /* ------------------------------------------------------------------------ */ /* * Evaluate a property */ int CVmObjList::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { vm_val_t self_val; /* use the constant evaluator */ self_val.set_obj(self); if (const_get_prop(vmg_ retval, &self_val, ext_, prop, source_obj, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from the base object class */ return CVmObjCollection::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * Evaluate a property of a constant list value */ int CVmObjList::const_get_prop(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, vm_prop_id_t prop, vm_obj_id_t *src_obj, uint *argc) { uint func_idx; /* presume no source object */ *src_obj = VM_INVALID_OBJ; /* translate the property index to an index into our function table */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((*func_table_[func_idx])(vmg_ retval, self_val, lst, argc)) return TRUE; /* * If this is a constant list (which is indicated by a non-object type * 'self'), try inheriting the default object interpretation, passing * the constant list placeholder object for its type information. */ if (self_val->typ != VM_OBJ) { /* try going to our base class, CVmCollection */ if (((CVmObjCollection *)vm_objp(vmg_ G_predef->const_lst_obj)) ->const_get_coll_prop(vmg_ prop, retval, self_val, src_obj, argc)) return TRUE; /* try going to our next base class, CVmObject */ if (vm_objp(vmg_ G_predef->const_lst_obj) ->CVmObject::get_prop(vmg_ prop, retval, G_predef->const_lst_obj, src_obj, argc)) return TRUE; } /* not handled */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - select a subset through a callback */ int CVmObjList::getp_subset(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { uint orig_argc = (argc != 0 ? *argc : 0); const vm_val_t *func_val; size_t src; size_t dst; size_t cnt; char *new_lst; CVmObjList *new_lst_obj; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* set up the recursive caller context */ vm_rcdesc rc(vmg_ "List.subset", self_val, 1, G_stk->get(0), orig_argc); /* get the function pointer argument, but leave it on the stack */ func_val = G_stk->get(0); /* push a self-reference while allocating to protect from gc */ G_stk->push(self_val); /* * Make a copy of our list for the return value. The result value * will be at most the same size as our current list; since we have * no way of knowing exactly how large it will be, and since we * don't want to run through the selection functions twice, we'll * just allocate at the maximum size and leave it partially unused * if we don't need all of the space. By making a copy of the input * list, we also can avoid worrying about whether the input list was * a constant, and hence we don't have to worry about the * possibility of constant page swapping. */ retval->set_obj(create(vmg_ FALSE, lst)); /* get the return value list data */ new_lst_obj = (CVmObjList *)vm_objp(vmg_ retval->val.obj); new_lst = new_lst_obj->ext_; /* get the length of the list */ cnt = vmb_get_len(new_lst); /* * push a reference to the new list to protect it from the garbage * collector, which could be invoked in the course of executing the * user callback */ G_stk->push(retval); /* * Go through each element of our list, and invoke the callback on * each element. If the element passes, write it to the current * output location in the list; otherwise, just skip it. * * Note that we're using the same list as source and destination, * which is easy because the list will either shrink or stay the * same - we'll never need to insert new elements. */ for (src = dst = 0 ; src < cnt ; ++src) { vm_val_t ele; const vm_val_t *val; /* * get this element (using a 1-based index), and push it as the * callback's argument */ index_list(vmg_ &ele, new_lst, src + 1); G_stk->push(&ele); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ func_val, 1, &rc, 0); /* get the result from R0 */ val = G_interpreter->get_r0(); /* * if the callback returned non-nil and non-zero, include this * element in the result */ if (val->typ == VM_NIL || (val->typ == VM_INT && val->val.intval == 0)) { /* it's nil or zero - don't include it in the result */ } else { /* include this element in the result */ new_lst_obj->cons_set_element(dst, &ele); /* advance the output index */ ++dst; } } /* * set the result list length to the number of elements we actually * copied */ new_lst_obj->cons_set_len(dst); /* discard our gc protection (self, return value) and our arguments */ G_stk->discard(3); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - map through a callback */ int CVmObjList::getp_map(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { uint orig_argc = (argc != 0 ? *argc : 0); const vm_val_t *func_val; size_t cnt; size_t idx; char *new_lst; CVmObjList *new_lst_obj; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* set up the recursive caller context */ vm_rcdesc rc(vmg_ "List.mapAll", self_val, 2, G_stk->get(0), orig_argc); /* get the function pointer argument, but leave it on the stack */ func_val = G_stk->get(0); /* push a self-reference while allocating to protect from gc */ G_stk->push(self_val); /* * Make a copy of our list for the return value, since the result * value is always the same size as our current list. By making a * copy of the input list, we also can avoid worrying about whether * the input list was a constant, and hence we don't have to worry * about the possibility of constant page swapping - we'll just * update elements of the copy in-place. */ retval->set_obj(create(vmg_ FALSE, lst)); /* get the return value list data */ new_lst_obj = (CVmObjList *)vm_objp(vmg_ retval->val.obj); new_lst = new_lst_obj->ext_; /* get the length of the list */ cnt = vmb_get_len(new_lst); /* * push a reference to the new list to protect it from the garbage * collector, which could be invoked in the course of executing the * user callback */ G_stk->push(retval); /* * Go through each element of our list, and invoke the callback on * each element. Replace each element with the result of the * callback. */ for (idx = 0 ; idx < cnt ; ++idx) { /* * get this element (using a 1-based index), and push it as the * callback's argument */ index_and_push(vmg_ new_lst, idx + 1); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ func_val, 1, &rc, 0); /* store the result in the list */ new_lst_obj->cons_set_element(idx, G_interpreter->get_r0()); } /* discard our gc protection (self, return value) and our arguments */ G_stk->discard(3); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - length */ int CVmObjList::getp_len(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* return the element count */ retval->set_int(vmb_get_len(lst)); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - sublist */ int CVmObjList::getp_sublist(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *in_argc) { /* check arguments */ uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(1, 1); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* get the original element count */ int32_t old_cnt = vmb_get_len(lst); /* pop the starting index; negative counts from the end of the list */ int32_t start = CVmBif::pop_long_val(vmg0_); if (start < 0) start += old_cnt + 1; /* * pop the length, if present; if not, use the current element * count, which will ensure that we use all available elements of * the sublist */ int32_t len = (argc >= 2 ? CVmBif::pop_long_val(vmg0_) : old_cnt); /* push the 'self' as protection from GC */ G_stk->push(self_val); /* skip to the first element */ lst += VMB_LEN; /* skip to the desired first element */ int32_t new_cnt; if (start >= 1 && start <= old_cnt) { /* it's in range - skip to the desired first element */ lst += (start - 1) * VMB_DATAHOLDER; new_cnt = old_cnt - (start - 1); } else { /* there's nothing left */ new_cnt = 0; } /* * limit the result to the desired new count, if it's shorter than * what we have left (we obviously can't give them more elements * than we have remaining) */ if (len < 0) new_cnt += len; else if (len < new_cnt) new_cnt = len; /* make sure the new length is non-negative */ if (new_cnt < 0) new_cnt = 0; /* create the new list */ vm_obj_id_t obj = create(vmg_ FALSE, new_cnt); /* copy the elements */ ((CVmObjList *)vm_objp(vmg_ obj))->cons_copy_data(0, lst, new_cnt); /* return the new object */ retval->set_obj(obj); /* discard GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - intersect */ int CVmObjList::getp_intersect(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { vm_val_t val2; vm_obj_id_t obj; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the second list, but leave it on the stack for GC protection */ val2 = *G_stk->get(0); /* put myself on the stack for GC protection as well */ G_stk->push(self_val); /* compute the intersection */ obj = intersect(vmg_ self_val, &val2); /* discard the argument lists */ G_stk->discard(2); /* return the new object */ retval->set_obj(obj); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - indexOf */ int CVmObjList::getp_index_of(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { vm_val_t subval; size_t idx; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the value to find */ G_stk->pop(&subval); /* find the value in the list */ if (find_in_list(vmg_ self_val, &subval, &idx)) { /* found it - adjust to 1-based index for return */ retval->set_int(idx); } else { /* didn't find it - return nil */ retval->set_nil(); } /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - car */ int CVmObjList::getp_car(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* * if the list has at least one element, return it; otherwise return * nil */ if (vmb_get_len(lst) == 0) { /* no elements - return nil */ retval->set_nil(); } else { /* it has at least one element - return the first element */ vmb_get_dh(lst + VMB_LEN, retval); } /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - cdr */ int CVmObjList::getp_cdr(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* push a self-reference for GC protection */ G_stk->push(self_val); /* * if the list has no elements, return nil; otherwise, return the * sublist starting with the second element (thus return an empty * list if the original list has only one element) */ if (vmb_get_len(lst) == 0) { /* no elements - return nil */ retval->set_nil(); } else { vm_obj_id_t obj; size_t new_cnt; /* reduce the list count by one */ new_cnt = vmb_get_len(lst) - 1; /* skip past the first element */ lst += VMB_LEN + VMB_DATAHOLDER; /* create the new list */ obj = create(vmg_ FALSE, new_cnt); /* copy the elements */ ((CVmObjList *)vm_objp(vmg_ obj))->cons_copy_data(0, lst, new_cnt); /* return the new object */ retval->set_obj(obj); } /* discard the stack protection */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - indexWhich */ int CVmObjList::getp_index_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { /* use the generic index-which routine, stepping forward */ vm_rcdesc rc(vmg_ "List.indexWhich", self_val, 9, G_stk->get(0), argc); return gen_index_which(vmg_ retval, self_val, lst, argc, TRUE, &rc); } /* * general index finder for indexWhich and lastIndexWhich - steps either * forward or backward through the list */ int CVmObjList::gen_index_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc, int forward, vm_rcdesc *rc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the function pointer argument, but leave it on the stack */ const vm_val_t *func_val = G_stk->get(0); /* push a self-reference while allocating to protect from gc */ G_stk->push(self_val); /* get the length of the list */ size_t cnt = vmb_get_len(lst); /* presume that we won't find any element that satisfies the condition */ retval->set_nil(); /* * start at either the first or last index, depending on which way * we're stepping */ size_t idx = (forward ? 1 : cnt); /* * Go through each element of our list, and invoke the callback on * the element. Stop when we reach the first element that returns * true, or when we run out of elements. */ for (;;) { /* if we're out of elements, stop now */ if (forward ? idx > cnt : idx == 0) break; /* re-translate the list address in case of swapping */ VM_IF_SWAPPING_POOL(if (self_val->typ == VM_LIST) lst = self_val->get_as_list(vmg0_);) /* get this element, and push it as the callback's argument */ index_and_push(vmg_ lst, idx); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ func_val, 1, rc, 0); /* * if the callback returned true, we've found the element we're * looking for */ if (G_interpreter->get_r0()->typ == VM_NIL || (G_interpreter->get_r0()->typ == VM_INT && G_interpreter->get_r0()->val.intval == 0)) { /* nil or zero - this one failed the test, so keep looking */ } else { /* it passed the test - return its index */ retval->set_int(idx); /* no need to keep searching - we found what we're looking for */ break; } /* advance to the next element */ if (forward) ++idx; else --idx; } /* discard our gc protection (self) and our arguments */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - forEach */ int CVmObjList::getp_for_each(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { /* use the generic forEach/forEachAssoc processor */ vm_rcdesc rc(vmg_ "List.forEach", self_val, 10, G_stk->get(0), argc); return for_each_gen(vmg_ retval, self_val, lst, argc, FALSE, &rc); } /* * property evaluator - forEachAssoc */ int CVmObjList::getp_for_each_assoc(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { /* use the generic forEach/forEachAssoc processor */ vm_rcdesc rc(vmg_ "List.forEachAssoc", self_val, 25, G_stk->get(0), argc); return for_each_gen(vmg_ retval, self_val, lst, argc, TRUE, &rc); } /* * General forEach processor - combines the functionality of forEach and * forEachAssoc, using a flag to specify whether or not to pass the index * of each element to the callback. */ int CVmObjList::for_each_gen(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc, int send_idx_to_cb, vm_rcdesc *rc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the function pointer argument, but leave it on the stack */ const vm_val_t *func_val = G_stk->get(0); /* push a self-reference while allocating to protect from gc */ G_stk->push(self_val); /* get the length of the list */ size_t cnt = vmb_get_len(lst); /* no return value */ retval->set_nil(); /* invoke the callback on each element */ for (size_t idx = 1 ; idx <= cnt ; ++idx) { /* re-translate the list address in case of swapping */ VM_IF_SWAPPING_POOL(if (self_val->typ == VM_LIST) lst = self_val->get_as_list(vmg0_);) /* * get this element (using a 1-based index) and push it as the * callback's argument */ index_and_push(vmg_ lst, idx); /* push the index, if desired */ if (send_idx_to_cb) G_stk->push()->set_int(idx); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ func_val, send_idx_to_cb ? 2 : 1, rc, 0); } /* discard our gc protection (self) and our arguments */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - valWhich */ int CVmObjList::getp_val_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { /* get the index of the value using indexWhich */ getp_index_which(vmg_ retval, self_val, lst, argc); /* if the return value is a valid index, get the value at the index */ if (retval->typ == VM_INT) { int idx; /* re-translate the list address in case of swapping */ VM_IF_SWAPPING_POOL(if (self_val->typ == VM_LIST) lst = self_val->get_as_list(vmg0_);) /* get the element as the return value */ idx = (int)retval->val.intval; index_list(vmg_ retval, lst, idx); } /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - lastIndexOf */ int CVmObjList::getp_last_index_of(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { vm_val_t subval; size_t idx; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the value to find */ G_stk->pop(&subval); /* find the value in the list */ if (find_last_in_list(vmg_ self_val, &subval, &idx)) { /* found it - set the return value*/ retval->set_int(idx); } else { /* didn't find it - return nil */ retval->set_nil(); } /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - lastIndexWhich */ int CVmObjList::getp_last_index_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { /* use the generic index-which routine, stepping backward */ vm_rcdesc rc(vmg_ "List.lastIndexWhich", self_val, 13, G_stk->get(0), argc); return gen_index_which(vmg_ retval, self_val, lst, argc, FALSE, &rc); } /* ------------------------------------------------------------------------ */ /* * property evaluator - lastValWhich */ int CVmObjList::getp_last_val_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { /* get the index of the value using lastIndexWhich */ getp_last_index_which(vmg_ retval, self_val, lst, argc); /* if the return value is a valid index, get the value at the index */ if (retval->typ == VM_INT) { int idx; /* re-translate the list address in case of swapping */ VM_IF_SWAPPING_POOL(if (self_val->typ == VM_LIST) lst = self_val->get_as_list(vmg0_);) /* get the element as the return value */ idx = (int)retval->val.intval; index_list(vmg_ retval, lst, idx); } /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - countOf */ int CVmObjList::getp_count_of(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { vm_val_t *val; size_t idx; size_t cnt; size_t val_cnt; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the value to find, but leave it on the stack for gc protection */ val = G_stk->get(0); /* lave the self value on the stack for gc protection */ G_stk->push(self_val); /* get the number of elements in the list */ cnt = vmb_get_len(lst); /* scan the list and count the elements */ for (idx = 0, val_cnt = 0 ; idx < cnt ; ++idx) { vm_val_t ele; /* re-translate the list address in case of swapping */ VM_IF_SWAPPING_POOL(if (self_val->typ == VM_LIST) lst = self_val->get_as_list(vmg0_);) /* get this list element */ vmb_get_dh(get_element_ptr_const(lst, idx), &ele); /* if it's the one we're looking for, count it */ if (ele.equals(vmg_ val)) ++val_cnt; } /* discard our gc protection */ G_stk->discard(2); /* return the count */ retval->set_int(val_cnt); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - countWhich */ int CVmObjList::getp_count_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the function pointer argument, but leave it on the stack */ const vm_val_t *func_val = G_stk->get(0); /* set up a native callback descriptor */ vm_rcdesc rc(vmg_ "List.countWhich", self_val, 16, G_stk->get(0), argc); /* push a self-reference while allocating to protect from gc */ G_stk->push(self_val); /* get the length of the list */ size_t cnt = vmb_get_len(lst); /* no return value */ retval->set_nil(); /* invoke the callback on each element */ size_t idx; int val_cnt; for (idx = 1, val_cnt = 0 ; idx <= cnt ; ++idx) { vm_val_t *val; /* re-translate the list address in case of swapping */ VM_IF_SWAPPING_POOL(if (self_val->typ == VM_LIST) lst = self_val->get_as_list(vmg0_);) /* * get this element (using a 1-based index), and push it as the * callback's argument */ index_and_push(vmg_ lst, idx); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ func_val, 1, &rc, 0); /* get the result from R0 */ val = G_interpreter->get_r0(); /* if the callback returned non-nil and non-zero, count it */ if (val->typ == VM_NIL || (val->typ == VM_INT && val->val.intval == 0)) { /* it's nil or zero - don't include it in the result */ } else { /* count it */ ++val_cnt; } } /* discard our gc protection (self) and our arguments */ G_stk->discard(2); /* return the count */ retval->set_int(val_cnt); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - getUnique */ int CVmObjList::getp_get_unique(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { CVmObjList *new_lst_obj; static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* put myself on the stack for GC protection */ G_stk->push(self_val); /* * Make a copy of our list for the return value, since the result * value will never be larger than the original list. By making a * copy of the input list, we also can avoid worrying about whether * the input list was a constant, and hence we don't have to worry * about the possibility of constant page swapping - we'll just * update elements of the copy in-place. */ retval->set_obj(create(vmg_ FALSE, lst)); /* get the return value list data */ new_lst_obj = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* push a reference to the new list for gc protection */ G_stk->push(retval); /* uniquify the list */ new_lst_obj->cons_uniquify(vmg0_); /* discard the gc protection */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - appendUnique */ int CVmObjList::getp_append_unique(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { vm_val_t val2; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the second list, but leave it on the stack for GC protection */ val2 = *G_stk->get(0); /* put myself on the stack for GC protection as well */ G_stk->push(self_val); /* do the append */ append_unique(vmg_ retval, self_val, &val2); /* discard the gc protection and arguments */ G_stk->discard(2); /* handled */ return TRUE; } /* * Append unique (internal interface) */ void CVmObjList::append_unique(VMG_ vm_val_t *retval, const vm_val_t *self, const vm_val_t *val2) { const char *lst; int lst2_len; size_t lst_len; CVmObjList *new_lst; /* get my internal list pointer */ if ((lst = self->get_as_list(vmg0_)) == 0) err_throw(VMERR_LIST_VAL_REQD); /* remember the length of my list */ lst_len = vmb_get_len(lst); /* make sure the other value is indeed a list */ if (!val2->is_listlike(vmg0_) || (lst2_len = val2->ll_length(vmg0_)) < 0) err_throw(VMERR_LIST_VAL_REQD); /* * Create a new list for the return value. Allocate space for the * current list plus the list to be added - this is an upper bound, * since the actual result list can be shorter */ retval->set_obj(create(vmg_ FALSE, lst_len + lst2_len)); /* push a reference to the new list for gc protection */ G_stk->push(retval); /* get the return value list data */ new_lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* * copy the first list into the result list (including only the data * elements, not the length prefix) */ new_lst->cons_copy_elements(0, lst); /* clear the remainder of the list */ new_lst->cons_clear(lst_len, lst_len + lst2_len - 1); /* append the second list to the result list */ for (int i = 1 ; i <= lst2_len ; ++i) { vm_val_t ele; val2->ll_index(vmg_ &ele, i); new_lst->cons_set_element(lst_len + i - 1, &ele); } /* make the list unique */ new_lst->cons_uniquify(vmg0_); /* discard the gc protection */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * General insertion routine - this is used to handle append, prepend, and * insertAt property evaluators. Inserts elements from the argument list * at the given index, with zero indicating insertion before the first * existing element. */ void CVmObjList::insert_elements(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint argc, int idx) { /* number of temporary gc items we leave on the stack */ const int stack_temp_cnt = 2; /* remember the length of my list */ size_t lst_len = vmb_get_len(lst); /* the index must be in the range 0 to the number of elements */ if (idx < 0 || (size_t)idx > lst_len) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* put myself on the stack for GC protection as well */ G_stk->push(self_val); /* * Create a new list for the return value. Allocate space for the * current list plus one new element for each argument. */ retval->set_obj(create(vmg_ FALSE, lst_len + argc)); /* push a reference to the new list for gc protection */ G_stk->push(retval); /* get the return value list data */ CVmObjList *new_lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* get the original list data */ lst = self_val->get_as_list(vmg0_); /* * Copy the first list into the result list (including only the data * elements, not the length prefix). Copy it in two pieces: first, * copy the elements before the insertion point. */ if (idx != 0) new_lst->cons_copy_data(0, get_element_ptr_const(lst, 0), idx); /* second, copy the elements after the insertion point */ if ((size_t)idx != lst_len) new_lst->cons_copy_data(idx + argc, get_element_ptr_const(lst, idx), lst_len - idx); /* copy each argument into the proper position in the new list */ for (uint i = 0 ; i < argc ; ++i) { /* * get a pointer to this argument value - the arguments are just * after the temporary items we've pushed onto the stack */ const vm_val_t *argp = G_stk->get(stack_temp_cnt + i); /* copy the argument into the list */ new_lst->cons_set_element((uint)idx + i, argp); } /* discard the gc protection and arguments */ G_stk->discard(argc + stack_temp_cnt); } /* ------------------------------------------------------------------------ */ /* * property evaluator - append */ int CVmObjList::getp_append(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* insert the element (there's just one) at the end of the list */ insert_elements(vmg_ retval, self_val, lst, 1, vmb_get_len(lst)); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - prepend */ int CVmObjList::getp_prepend(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* insert the element (there's just one) at the start of the list */ insert_elements(vmg_ retval, self_val, lst, 1, 0); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - insert new elements */ int CVmObjList::getp_insert_at(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *in_argc) { /* check arguments - we need at least two */ uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(2, 0, TRUE); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* pop the index value; adjust negative values */ int idx = CVmBif::pop_int_val(vmg0_); if (idx <= 0) idx += vmb_get_len(lst) + 1; /* adjust to zero-based indexing */ idx -= 1; /* * Insert the element (there's just one) at the start of the list. * Note that we must decrement the argument count we got, since we * already took off the first argument (the index value). */ insert_elements(vmg_ retval, self_val, lst, argc - 1, idx); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * General property evaluator for removing a range of elements - this * handles removeElementAt and removeRange. */ void CVmObjList::remove_range(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, int start_idx, int del_cnt) { size_t lst_len; CVmObjList *new_lst; /* push myself onto the stack for GC protection */ G_stk->push(self_val); /* get the original list length */ lst_len = vmb_get_len(lst); /* * allocate a new list with space for the original list minus the * elements to be deleted */ retval->set_obj(create(vmg_ FALSE, lst_len - del_cnt)); /* push a reference to teh new list for gc protection */ G_stk->push(retval); /* get the return value list data */ new_lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* get the original list data as well */ lst = self_val->get_as_list(vmg0_); /* * copy elements from the original list up to the first item to be * removed */ if (start_idx != 0) new_lst->cons_copy_data(0, get_element_ptr_const(lst, 0), start_idx); /* * copy elements of the original list following the last item to be * removed */ if ((size_t)(start_idx + del_cnt) < lst_len) new_lst-> cons_copy_data(start_idx, get_element_ptr_const(lst, start_idx + del_cnt), lst_len - (start_idx + del_cnt)); /* discard the gc protection */ G_stk->discard(2); } /* ------------------------------------------------------------------------ */ /* * property evaluator - remove the element at the given index */ int CVmObjList::getp_remove_element_at(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* retrieve the index value, and adjust for negative values */ int idx = CVmBif::pop_int_val(vmg0_); if (idx < 0) idx += vmb_get_len(lst) + 1; /* adjust to zero-based indexing */ --idx; /* make sure it's in range - it must refer to a valid element */ if (idx < 0 || idx >= (int)vmb_get_len(lst)) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* remove one element at the given index */ remove_range(vmg_ retval, self_val, lst, idx, 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - remove the element at the given index */ int CVmObjList::getp_remove_range(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(2); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* retrieve the starting and ending index values */ int start_idx = CVmBif::pop_int_val(vmg0_); int end_idx = CVmBif::pop_int_val(vmg0_); /* negative index values count from the end of the list */ if (start_idx < 0) start_idx += vmb_get_len(lst) + 1; if (end_idx < 0) end_idx += vmb_get_len(lst) + 1; /* adjust to zero-based indexing */ --start_idx; --end_idx; /* * make sure the index values are in range - both must refer to valid * elements, and the ending index must be at least as high as the * starting index */ if (start_idx < 0 || (size_t)start_idx >= vmb_get_len(lst) || end_idx < 0 || (size_t)end_idx >= vmb_get_len(lst) || end_idx < start_idx) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* remove the specified elements */ remove_range(vmg_ retval, self_val, lst, start_idx, end_idx - start_idx + 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * sorter for list data */ class CVmQSortList: public CVmQSortVal { public: CVmQSortList() { lst_ = 0; } /* get an element */ void get_ele(VMG_ size_t idx, vm_val_t *val) { vmb_get_dh(get_ele_ptr(idx), val); } /* set an element */ void set_ele(VMG_ size_t idx, const vm_val_t *val) { vmb_put_dh(get_ele_ptr(idx), val); } /* get an element pointer */ char *get_ele_ptr(size_t idx) { return lst_ + VMB_LEN + (idx * VMB_DATAHOLDER); } /* our list data */ char *lst_; }; /* * property evaluator - sort */ int CVmObjList::getp_sort(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *in_argc) { size_t lst_len; CVmObjList *new_lst; uint argc = (in_argc == 0 ? 0 : *in_argc); CVmQSortList sorter; static CVmNativeCodeDesc desc(0, 2); /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* remember the length of my list */ lst_len = vmb_get_len(lst); /* if we have an 'descending' flag, note it */ if (argc >= 1) sorter.descending_ = (G_stk->get(0)->typ != VM_NIL); /* * if we have a comparison function, note it, but leave it on the * stack for gc protection */ if (argc >= 2) { /* get the function */ sorter.compare_fn_ = *G_stk->get(1); /* initialize the native caller context */ sorter.rc.init(vmg_ "List.sort", self_val, 20, G_stk->get(0), argc); } /* put myself on the stack for GC protection as well */ G_stk->push(self_val); /* create a copy of the list as the return value */ retval->set_obj(create(vmg_ FALSE, lst)); /* push a reference to the new list for gc protection */ G_stk->push(retval); /* get the return value list data */ new_lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* set the list pointer in the sorter */ sorter.lst_ = new_lst->ext_; /* sort the new list if it has any elements */ if (lst_len != 0) sorter.sort(vmg_ 0, lst_len - 1); /* discard the gc protection and arguments */ G_stk->discard(2 + argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - splice */ int CVmObjList::getp_splice(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *in_argc) { /* check arguments */ uint argc = (in_argc == 0 ? 0 : *in_argc); static CVmNativeCodeDesc desc(2, 0, TRUE); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* remember the length of my list */ int lst_cnt = vmb_get_len(lst); /* retrieve the starting index and deletion length arguments */ int start_idx = CVmBif::pop_int_val(vmg0_); int del_cnt = CVmBif::pop_int_val(vmg0_); /* a negative starting index counts from the end of the list */ if (start_idx <= 0) start_idx += lst_cnt + 1; /* adjust to zero-based indexing */ --start_idx; /* make sure the values are in range */ start_idx = (start_idx < 0 ? 0 : start_idx > lst_cnt ? lst_cnt : start_idx); del_cnt = (del_cnt < 0 ? 0 : del_cnt > lst_cnt - start_idx ? lst_cnt - start_idx : del_cnt); /* figure the insertion size: each argument is a value to insert */ int ins_cnt = argc - 2; /* if we're making any changes, build the new list */ if (del_cnt != 0 || ins_cnt != 0) { /* put myself on the stack for GC protection */ G_stk->push(self_val); /* create a list to store the return value */ retval->set_obj(create(vmg_ FALSE, lst_cnt + ins_cnt - del_cnt)); CVmObjList *new_lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* push a reference to the new list for gc protection */ G_stk->push(retval); /* copy elements from the original up to the starting index */ int dst = 0; if (start_idx > 0) { new_lst->cons_copy_data(0, lst + VMB_LEN, start_idx); dst = start_idx; } /* * Add the inserted items from the arguments. We popped our first * two arguments, but then we pushed two items for gc protection, * so conveniently the inserted items are just args 2..argc-1 */ for (uint i = 2 ; i < argc ; ++i) new_lst->cons_set_element(dst++, G_stk->get(i)); /* copy the remaining elements after the deleted segment, if any */ if (lst_cnt > start_idx + del_cnt) { new_lst->cons_copy_data( dst, get_element_ptr_const(lst, start_idx + del_cnt), lst_cnt - (start_idx + del_cnt)); } /* * Discard the gc protection and insertion arguments. We added two * elements for gc protection, and popped two arguments, so the net * is the original argument count. */ G_stk->discard(argc); } else { /* * There's nothing to do - just return the original list. (There's * nothing to discard from the stack: we have no more arguments, * since ins_cnt is zero, and we haven't pushed any gc protection * elements.) */ *retval = *self_val; } /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - join. This is a generic routine that can be called * with any list-like object. */ int CVmObjList::getp_join(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *in_argc) { /* check arguments */ uint argc = (in_argc == 0 ? 0 : *in_argc); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* make sure the 'separator' argument is valid */ int sep_len = 0; const char *sep = 0; if (argc >= 1 && G_stk->get(0)->typ != VM_NIL) { /* it's there - make sure it's a string */ if ((sep = G_stk->get(0)->get_as_string(vmg0_)) == 0) err_throw(VMERR_BAD_TYPE_BIF); /* note its length, and get a pointer to its string bytes */ sep_len = vmb_get_len(sep); sep += VMB_LEN; } /* do the join */ join(vmg_ retval, self_val, sep, sep_len); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* * Join list elements into a string */ void CVmObjList::join(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *sep, size_t sep_len) { /* push 'self' for gc protection */ G_stk->push(self_val); /* * Do a first pass through the list to measure the size we'll need. * For string elements we'll know exactly what we need; for * non-strings, make a guess, and we'll expand the buffer later if * needed. */ vm_val_t ele; size_t alo_len = 0; int i, lst_len = self_val->ll_length(vmg0_); for (i = 1 ; i <= lst_len ; ++i) { /* get this element */ self_val->ll_index(vmg_ &ele, i); /* if it's a string, get its length; for other types, make a guess */ const char *es = ele.get_as_string(vmg0_); if (es != 0) { /* it's a string - add its length to the running total */ alo_len += vmb_get_len(es); } else { /* for other types, guess based on the type */ switch (ele.typ) { case VM_INT: /* integers - leave room for 10 characters */ alo_len += 10; break; case VM_OBJ: /* * object conversion could take almost any amount of space; * make a wild guess for now */ alo_len += 128; break; default: /* assume nothing for other types */ break; } } } /* add in the space needed for the separators */ if (lst_len > 0) alo_len += sep_len * (lst_len - 1); /* create a string with the allocated size */ retval->set_obj(CVmObjString::create(vmg_ FALSE, alo_len)); CVmObjString *strp = (CVmObjString *)vm_objp(vmg_ retval->val.obj); /* get a write pointer to the buffer */ char *dst = strp->cons_get_buf(); /* push it for gc protection */ G_stk->push(retval); /* build the string */ for (i = 1 ; i <= lst_len ; ++i) { char buf[128]; vm_val_t tmp_str; /* get this element */ self_val->ll_index(vmg_ &ele, i); /* assume we won't need a temporary string */ tmp_str.set_nil(); /* try getting it as a string directly */ const char *es = ele.get_as_string(vmg0_); size_t es_len; if (es != 0) { /* it's a string - get its length and buffer */ es_len = vmb_get_len(es); es += VMB_LEN; } else { /* for other types, do the implied string conversion */ switch (ele.typ) { case VM_INT: /* integer - format in decimal */ sprintf(buf, "%ld", (long)ele.val.intval); es = buf; es_len = strlen(es); break; case VM_NIL: /* nil - use an empty string */ es = ""; es_len = 0; break; default: /* for anything else, see what casting comes up with */ if ((es = ele.cast_to_string(vmg_ &tmp_str)) == 0) err_throw(VMERR_BAD_TYPE_BIF); /* get the length and buffer */ es_len = vmb_get_len(es); es += VMB_LEN; break; } } /* push the temp string (if any) for gc protection */ G_stk->push(&tmp_str); /* append the current string, making sure we have enough space */ dst = strp->cons_ensure_space(vmg_ dst, es_len, 256); memcpy(dst, es, es_len); dst += es_len; /* if there's another element, append the separator */ if (i < lst_len && sep_len != 0) { dst = strp->cons_ensure_space(vmg_ dst, sep_len, 32); memcpy(dst, sep, sep_len); dst += sep_len; } /* discard the temp string */ G_stk->discard(1); } /* set the string to its final length */ strp->cons_shrink_buffer(vmg_ dst); /* remove gc protection */ G_stk->discard(2); } /* * explicit string conversion, using toString() semantics */ const char *CVmObjList::list_to_string( VMG_ vm_val_t *retval, const vm_val_t *self, int radix, int flags) { /* count our elements */ int n = self->ll_length(vmg0_); /* create a list with the same number of elements */ vm_val_t newlstval; newlstval.set_obj(create(vmg_ FALSE, n)); CVmObjList *newlst = vm_objid_cast(CVmObjList, newlstval.val.obj); newlst->cons_clear(); /* push the new list for gc protection */ G_stk->push(&newlstval); /* set up the new list as self.mapAll({x: toString(x)}) */ for (int i = 0 ; i < n ; ++i) { /* get this element of the old list */ vm_val_t ele; self->ll_index(vmg_ &ele, i+1); /* convert it to a string */ vm_val_t newele; CVmBifTADS::toString(vmg_ &newele, &ele, radix, flags); /* store the result in the new list */ newlst->cons_set_element(i, &newele); } /* convert the new list to a string using the basic join with commas */ join(vmg_ retval, &newlstval, ",", 1); /* discard gc protection */ G_stk->discard(1); /* return the new string */ return retval->get_as_string(vmg0_); } /* ------------------------------------------------------------------------ */ /* * Property evaluator - generate (static property) */ int CVmObjList::static_getp_generate(VMG_ vm_val_t *retval, uint *in_argc) { /* check arguments */ uint argc = (in_argc == 0 ? 0 : *in_argc); static CVmNativeCodeDesc desc(2); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* get the callback function argument */ vm_val_t *func = G_stk->get(0); /* make sure it's a function; set up a header pointer if it is */ CVmFuncPtr funcdesc; if (!funcdesc.set(vmg_ func)) err_throw(VMERR_BAD_TYPE_BIF); /* get the count */ int32_t cnt = G_stk->get(1)->num_to_int(vmg0_); /* make sure it's not negative */ if (cnt < 0) err_throw(VMERR_BAD_VAL_BIF); /* create the list */ retval->set_obj(CVmObjList::create(vmg_ FALSE, cnt)); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* push the list value for gc protection */ G_stk->push(retval); /* zero the element count, so that the gc doesn't deref garbage */ lst->cons_set_len(0); /* find out how many arguments the function wants */ int fargc = funcdesc.is_varargs() ? -1 : funcdesc.get_max_argc(); /* set up our recursive call descriptor */ vm_rcdesc rc(vmg_ "List.generate", CVmObjList::metaclass_reg_->get_class_obj(vmg0_), PROPIDX_generate, func, argc); /* generate the elements */ for (int i = 0 ; i < cnt ; ++i) { /* push the callback arguments */ const int pushed_argc = 1; G_stk->push()->set_int(i+1); /* adjust the arguments for what the callback actually wants */ int sent_argc = fargc < 0 || fargc > pushed_argc ? pushed_argc : fargc; /* call the callback */ G_interpreter->call_func_ptr(vmg_ func, sent_argc, &rc, 0); /* discard excess arguments */ G_stk->discard(pushed_argc - sent_argc); /* add the result to the list */ lst->cons_set_len(i+1); lst->cons_set_element(i, G_interpreter->get_r0()); } /* discard arguments and gc protection */ G_stk->discard(argc + 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator: get the index of the element with the minimum value */ int CVmObjList::getp_indexOfMin(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *in_argc) { /* do the general calculation to select the minimum value ("-1") */ vm_rcdesc rc(vmg_ "List.indexOfMin", self_val, 29, G_stk->get(0), in_argc != 0 ? *in_argc : 0); return get_minmax(vmg_ retval, self_val, lst, in_argc, &rc, -1, TRUE); } /* * Property evaluator: get the value of the element with the minimum value */ int CVmObjList::getp_minVal(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *in_argc) { /* do the general calculation to select the minimum value ("-1") */ vm_rcdesc rc(vmg_ "List.minVal", self_val, 30, G_stk->get(0), in_argc != 0 ? *in_argc : 0); return get_minmax(vmg_ retval, self_val, lst, in_argc, &rc, -1, FALSE); } /* * Property evaluator: get the index of the element with the maximum value */ int CVmObjList::getp_indexOfMax(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *in_argc) { /* do the general calculation to select the maximum value ("+1") */ vm_rcdesc rc(vmg_ "List.indexOfMax", self_val, 31, G_stk->get(0), in_argc != 0 ? *in_argc : 0); return get_minmax(vmg_ retval, self_val, lst, in_argc, &rc, 1, TRUE); } /* * Property evaluator: get the value of the element with the maximum value */ int CVmObjList::getp_maxVal(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *in_argc) { /* do the general calculation to select the maximum value ("+1") */ vm_rcdesc rc(vmg_ "List.maxVal", self_val, 32, G_stk->get(0), in_argc != 0 ? *in_argc : 0); return get_minmax(vmg_ retval, self_val, lst, in_argc, &rc, 1, FALSE); } /* * General handling for minIndex, minVal, maxIndex, and maxVal. This finds * the highest/lowest value in the list, optionally mapped through a * callback function. Returns the winning element's index and value. * * 'sense' is the sense of the search: -1 for minimum, 1 for maximum. * 'want_index' is true if 'retval' should be set to the index of the * winning element, false if 'retval' should be set to the winning * element's value. */ int CVmObjList::get_minmax(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *in_argc, const vm_rcdesc *rc, int sense, int want_index) { /* check arguments */ uint argc = (in_argc == 0 ? 0 : *in_argc); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* make sure the 'func' argument is valid */ const vm_val_t *cb = 0; if (argc >= 1) { cb = G_stk->get(0); if (!cb->is_func_ptr(vmg0_)) err_throw(VMERR_BAD_TYPE_BIF); } /* push 'self' for gc protection */ G_stk->push(self_val); /* if there are no elements, this is an error */ int cnt = vmb_get_len(lst); if (cnt == 0) err_throw(VMERR_BAD_VAL_BIF); /* we don't have a winner yet */ int winner_idx = 0; vm_val_t winner; /* run through the list */ for (int i = 0 ; i < cnt ; ++i) { /* get this element */ vm_val_t ele; get_element_const(lst, i, &ele); /* if there's a callback, invoke it */ if (cb != 0) { /* invoke the callback with the element value as the argument */ G_stk->push(&ele); G_interpreter->call_func_ptr(vmg_ cb, 1, rc, 0); /* use the result as the comparison value */ ele = *G_interpreter->get_r0(); } /* compare it to the winner so far, and keep the lower value */ if (i == 0) { /* it's the first element, so it's the default winner so far */ winner_idx = i; winner = ele; } else { /* compare it to the winner so far */ int cmp = ele.compare_to(vmg_ &winner); /* keep it if it's the min/max so far */ if ((sense > 0 && cmp > 0) || (sense < 0 && cmp < 0)) { winner_idx = i; winner = ele; } } } /* remove gc protection and arguments */ G_stk->discard(argc + 1); /* return the winning index (note: 1-based) or value, as desired */ if (want_index) retval->set_int(winner_idx + 1); else *retval = winner; /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Constant-pool list object */ /* * create */ vm_obj_id_t CVmObjListConst::create(VMG_ const char *const_ptr) { /* create our new ID */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, FALSE, FALSE); /* create our list object, pointing directly to the constant pool */ new (vmg_ id) CVmObjListConst(vmg_ const_ptr); /* return the new ID */ return id; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmlst.h���������������������������������������������������������������������������0000664�0000000�0000000�00000073614�12650170723�0015207�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMLST.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmlst.h - VM dynamic list implementation Function Notes Modified 10/29/98 MJRoberts - Creation */ #ifndef VMLST_H #define VMLST_H #include <stdlib.h> #include "vmtype.h" #include "vmobj.h" #include "vmcoll.h" #include "vmglob.h" #include "vmstack.h" #include "vmrun.h" class CVmObjList: public CVmObjCollection { friend class CVmMetaclassList; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObjCollection::is_of_metaclass(meta)); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * Create dynamically from parameters in the stack; we do not remove * the elements from the stack, but simply create a list from the * parameters. 'idx' is the parameter index of the first parameter, * and 'cnt' is the number of parameters to use. */ static vm_obj_id_t create_from_params(VMG_ uint idx, uint cnt); /* call a static property */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop); /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* get my datatype when converted to constant data */ virtual vm_datatype_t get_convert_to_const_data_type() const { return VM_LIST; } /* create a list with no initial contents */ static vm_obj_id_t create(VMG_ int in_root_set); /* * create a list with a given number of elements, for construction * of the list element-by-element */ static vm_obj_id_t create(VMG_ int in_root_set, size_t element_count); /* * create a list from a constant list, for construction of the list * as a modified copy of an original list */ static vm_obj_id_t create(VMG_ int in_root_set, const char *lst); /* get an element, given a zero-based index */ void get_element(size_t idx, vm_val_t *val) const { /* get the data from the data holder in our extension */ vmb_get_dh(get_element_ptr(idx), val); } /* * List construction: set an element. List contents are immutable, so * they cannot be changed after the list is constructed. However, it * is often convenient to construct a list one element at a time, so a * caller can create the list with the appropriate number of elements, * then use this routine to set each element of the list individually. * * idx is the index of the element in the list; the first element is at * index zero. Note that this routine does *not* allocate memory; the * list must be pre-allocated to its full number of elements. Use * cons_ensure_space to expand the list as needed. */ void cons_set_element(size_t idx, const vm_val_t *val); /* update the list in place so that each value is unique */ void cons_uniquify(VMG0_); /* * Copy an existing list into our list, starting at a given index. The * caller must ensure that our list buffer is large enough to * accommodate the new elements. The 'orig_list' value must point to a * standard list constant value: a UINT2 element count prefix followed * by DATAHOLDER elements. */ void cons_copy_elements(size_t start_idx, const char *orig_list); /* * Copy existing list elements into our list, starting at the given * index. The caller must ensure that our list buffer is large enough * to accommodate the new elements. The 'ele_array' is an array of * DATAHOLDER values. */ void cons_copy_data(size_t start_idx, const char *ele_array, size_t ele_count); /* * Set the length of the list. This can be used when constructing a * list, and the actual number of elements is unknown before * construction is complete (however, the maximum number of elements * must be known in advance, since this merely sets the length, and * does NOT reallocate the list -- hence, this call can only be used * to shrink the list below its allocated size, never to expand it). */ void cons_set_len(size_t len) { vmb_put_len(ext_, len); } /* * During construction, ensure there's enough space in the list to hold * an added item at the given index (0-based). If not, expands the * list to make it large enough to hold the given new elements plus the * margin. The margin is to ensure that we don't keep reallocating one * element at a time when the caller is adding items iteratively. The * added elements are automatically set to nil. */ void cons_ensure_space(VMG_ size_t idx, size_t margin); /* * During construction, clear a range of the list by setting each * element in the range (inclusive of the endpoints) to nil. If the * construction process could trigger garbage collection, it's * important for the caller to clear the list first. Uninitialized * elements could happen to look like pointers to invalid objects, so * if the gc runs, it could try to follow one of these invalid pointers * and cause a crash. The index values are 0-based. */ void cons_clear(size_t start_idx, size_t end_idx); /* clear the entire list (set the whole list to nil) */ void cons_clear() { if (get_ele_count() != 0) cons_clear(0, get_ele_count() - 1); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* undo operations - lists are immutable and hence keep no undo */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { } void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* mark references */ void mark_refs(VMG_ uint state); /* * remove weak references - we keep only normal (strong) references, * so this routine doesn't need to do anything */ void remove_stale_weak_refs(VMG0_) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t) { ext_ = (char *)ptr; } /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* * cast to string - returns a string consisting of the list elements * converted to strings and concatenated together with commas * separating elements; the result is the same as self.join(',') */ const char *cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const { /* set up a 'self' value */ vm_val_t self_val; self_val.set_obj(self); /* join the list elements into a string, using commas as separators */ join(vmg_ new_str, &self_val, ",", 1); /* return the string result */ return new_str->get_as_string(vmg0_); } /* explicitly convert to string using toString() semantics */ virtual const char *explicit_to_string( VMG_ vm_obj_id_t self, vm_val_t *new_str, int radix, int flags) const { /* set up a 'self' value */ vm_val_t self_val; self_val.set_obj(self); /* convert to string */ return list_to_string(vmg_ new_str, &self_val, radix, flags); } /* * explicitly convert a list or list-like value to a string using * toString() semantics */ static const char *list_to_string( VMG_ vm_val_t *result, const vm_val_t *self, int radix, int flags); /* * Add a value to the list. If the value to add is a list (constant * or object), we'll append each element of the list to this list; * otherwise, we'll just append the value itself to the list. In * any case, we don't modify this list itself, but create a new list * object to hold the result. */ int add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* * Index the list */ int index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val); /* * Set an indexed element of the list. Since the contents of a list * object cannot be changed, we'll return in *new_container a new * list object that we create with the modified contents. */ int set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val); /* * Subtract a value from the list. This creates a new list with the * element matching the given value removed from the original list. * We do not modify the original list; instead, we create a new list * object with the new value. */ int sub_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* * get as a list - simply return our extension, which is in the * required portable list format */ const char *get_as_list() const { return ext_; } /* I'm the original list-like object */ int is_listlike(VMG_ vm_obj_id_t /*self*/) { return TRUE; } int ll_length(VMG_ vm_obj_id_t /*self*/) { return get_ele_count(); } /* * Check a value for equality. We will match any constant list that * contains the same data as our list, and any other list object * with the same underlying data. */ int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const; /* * Static list adder. This creates a new list object that results * from appending the given value to the given list constant. This * is defined statically so that this code can be shared for adding * to constant pool lists and adding to CVmObjList objects. * * 'lstval' must point to a constant list. The first two bytes of * the list are stored in portable UINT2 format and give the number * of elements in the list; this is immediately followed by a packed * array of data holders in portable format. */ static void add_to_list(VMG_ vm_val_t *result, vm_obj_id_t self, const char *lstval, const vm_val_t *val); /* * Static list subtraction routine. This creates a new list object * that results from removing the given value from the list * constant. This is defined statically so that this code can be * shared for subtracting from constant pool lists and subtracting * from CVmObjList objects. * * 'lstmem' must point to a constant list in the same format as * required for add_to_list. If 'lstmem' comes from the constant * pool, then 'lstval' must be provided to give us the constant pool * address; otherwise, 'lstval' should be null. */ static void sub_from_list(VMG_ vm_val_t *result, const vm_val_t *lstval, const char *lstmem, const vm_val_t *val); /* * Constant list comparison routine. Compares the given list * constant (in portable format, with leading UINT2 element count * prefix followed by the list's elements in portable data holder * format) to the other value. Returns true if the other value is a * list constant or object whose contents match the list constant, * false if not. * * If 'lstmem' comes from the constant pool, then 'lstval' must be * provided to give us the constant pool address; otherwise, * 'lstval' should be null. */ static int const_equals(VMG_ const vm_val_t *lstval, const char *lstmem, const vm_val_t *val, int depth); /* * Calculate a hash value for the list */ uint calc_hash(VMG_ vm_obj_id_t self, int depth) const; /* * Constant list hash value calculation */ static uint const_calc_hash(VMG_ const vm_val_t *self_val, const char *lst, int depth); /* * Constant list indexing routine. Indexes the given constant list * (which must be in portable format, with leading UINT2 element * count followed by the list's elements in portable data holder * format), looking up the value at the index number given by the * index value, and puts the result in *result. */ static void index_list(VMG_ vm_val_t *result, const char *lst, const vm_val_t *index_val); /* index a list, using a 1-based index */ static void index_list(VMG_ vm_val_t *result, const char *lst, uint idx); /* push the indexed element, using a 1-based index */ static void index_and_push(VMG_ const char *lst, uint idx) { vm_val_t *p; /* push a new stack element */ p = G_stk->push(); /* index the list and store the value directly in the stack */ index_list(vmg_ p, lst, idx); } /* * Constant list set-index routine. Creates a new list object as a * copy of this list, with the element at the given index set to the * given new value. */ static void set_index_list(VMG_ vm_val_t *result, const char *lst, const vm_val_t *index_val, const vm_val_t *new_val); /* * Find a value within a list. If we find the value, we'll set *idxp * to the index (starting at 1 for the first element) of the item we * found, and we'll return true; if we don't find the value, we'll * return false. */ static int find_in_list(VMG_ const vm_val_t *lst, const vm_val_t *val, size_t *idxp); /* find the last match for a value */ static int find_last_in_list(VMG_ const vm_val_t *lst, const vm_val_t *val, size_t *idxp); /* * Evaluate a property of a constant list value. Returns true if we * successfully evaluated the property, false if the property is not * one of the properties that the list class defines. */ static int const_get_prop(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, vm_prop_id_t prop, vm_obj_id_t *srcobj, uint *argc); /* property evaluator - undefined property */ static int getp_undef(VMG_ vm_val_t *, const vm_val_t *, const char *, uint *) { return FALSE; } /* property evaluator - select a subset through a callback */ static int getp_subset(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* property evaluator - apply a callback to each element */ static int getp_map(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* get the length */ static int getp_len(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* sublist */ static int getp_sublist(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* intersect */ static int getp_intersect(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* indexOf */ static int getp_index_of(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* car */ static int getp_car(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* cdr */ static int getp_cdr(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* indexWhich */ static int getp_index_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* forEach */ static int getp_for_each(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* forEachAssoc */ static int getp_for_each_assoc(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* valWhich */ static int getp_val_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* lastIndexOf */ static int getp_last_index_of(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* lastIndexWhich */ static int getp_last_index_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* lastValWhich */ static int getp_last_val_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* countOf */ static int getp_count_of(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* countWhich */ static int getp_count_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* general routine for indexWhich and lastIndexWhich */ static int gen_index_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc, int forward, vm_rcdesc *rc); /* getUnique */ static int getp_get_unique(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* appendUnique */ static int getp_append_unique(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* appendUnique - internal interface */ static void append_unique(VMG_ vm_val_t *retval, const vm_val_t *self, const vm_val_t *other); /* append */ static int getp_append(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* sort */ static int getp_sort(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* insertAt */ static int getp_insert_at(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* prepend */ static int getp_prepend(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* property evaluator - remove a single element at a given index */ static int getp_remove_element_at(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* property evaluator - removeRange */ static int getp_remove_range(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* property evaluator - splice */ static int getp_splice(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* property evaluator - join */ static int getp_join(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* * join the elements of a list into a string; this is equivalent to the * bytecode-level join() method, but can be called more conveniently * from C++ code */ static void join(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *sep, size_t sep_len); /* property evaluator - generate */ static int getp_generate(VMG_ vm_val_t *retval, const vm_val_t * /*self*/, const char * /*lst*/, uint *argc) { return static_getp_generate(vmg_ retval, argc); } /* static property evaluator - generate */ static int static_getp_generate(VMG_ vm_val_t *retval, uint *argc); /* property evaluator - index of minimum value */ static int getp_indexOfMin(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* property evaluator - minimum value in list */ static int getp_minVal(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* property evaluator - index of maximum value in list */ static int getp_indexOfMax(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* property evaluator - maximum value in list */ static int getp_maxVal(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); protected: /* general processor for forEach and forEachAssoc */ static int for_each_gen(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc, int send_idx_to_cb, vm_rcdesc *rc); /* general min/max handler, for minIndex, minVal, maxIndex, maxVal */ static int get_minmax(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *in_argc, const vm_rcdesc *rc, int sense, int want_index); /* * Compute the intersection of two lists. Returns a new list with the * elements that occur in both lists. */ static vm_obj_id_t intersect(VMG_ const vm_val_t *l1, const vm_val_t *l2); /* insert the arguments into the list at the given index */ static void insert_elements(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint argc, int idx); /* remove elements */ static void remove_range(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, int start_idx, int del_cnt); /* create an iterator */ virtual void new_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val); /* * create a live iterator - for a list, there is no difference * between snapshot and live iterators, since a list is immutable */ virtual void new_live_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val) { new_iterator(vmg_ retval, self_val); } /* get the number of elements in the list */ size_t get_ele_count() const { return vmb_get_len(ext_); } /* get the number of elements in a constant list */ static size_t get_ele_count_const(const char *lstval) { return vmb_get_len(lstval); } /* get an element from a constant list, given a zero-based index */ static void get_element_const(const char *lstval, size_t idx, vm_val_t *val) { /* get the data from the data holder in the constant list */ vmb_get_dh(get_element_ptr_const(lstval, idx), val); } /* given an index, get a pointer to the element's data in the list */ char *get_element_ptr(size_t idx) const { /* * figure out where this element's data holder is by skipping * the count prefix, then skipping past preceding data holders */ return ext_ + VMB_LEN + (idx * VMB_DATAHOLDER); } /* * given an index, and a pointer to a constant list, get a pointer * to the element's data in the list constant */ static const char *get_element_ptr_const(const char *lstval, size_t idx) { /* * figure out where this element's data holder is by skipping * the count prefix, then skipping past preceding data holders */ return lstval + VMB_LEN + (idx * VMB_DATAHOLDER); } /* * given a pointer to a list element, increment the pointer so that * it points to the next element */ static void inc_element_ptr(char **p) { /* add the size of a data holder to the current pointer */ *p += VMB_DATAHOLDER; } /* increment a constant element pointer */ static void inc_const_element_ptr(const char **p) { /* add the size of a data holder to the current pointer */ *p += VMB_DATAHOLDER; } /* create a list with no initial contents */ CVmObjList() { ext_ = 0; } /* * create a list with a given number of elements, for construction * of the list element-by-element */ CVmObjList(VMG_ size_t element_count); /* create a list from a constant list */ CVmObjList(VMG_ const char *lst); /* * Calculate the amount of space we need to store a list of a given * length. We require two bytes for the length prefix, plus the * space for each element. */ static size_t calc_alloc(size_t elecnt) { return (VMB_LEN + (elecnt * VMB_DATAHOLDER)); } /* allocate space for the list, given the number of elements */ void alloc_list(VMG_ size_t element_count); /* property evaluation function table */ static int (*func_table_[])(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * A constant list is exactly like an ordinary list, except that our * contents come from the constant pool. We store a pointer directly to * our constant pool data rather than making a separate copy. The only * thing we have to do differently from an ordinary list is that we don't * delete our extension when we're deleted, since our extension is really * just a pointer into the constant pool. */ class CVmObjListConst: public CVmObjList { public: /* notify of deletion */ void notify_delete(VMG_ int /*in_root_set*/) { /* * do nothing, since our extension is just a pointer into the * constant pool */ } /* create from constant pool data */ static vm_obj_id_t create(VMG_ const char *const_ptr); protected: /* construct from constant pool data */ CVmObjListConst(VMG_ const char *const_ptr) { /* point our extension directly to the constant pool data */ ext_ = (char *)const_ptr; } }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassList: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "list/030008"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjList(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjList(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjList::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjList::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* I'm a Collection object */ CVmMetaclass *get_supermeta_reg() const { return CVmObjCollection::metaclass_reg_; } }; #endif /* VMLST_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjList) ��������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmmain.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000146012�12650170723�0015655�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmmain.cpp - T3 VM main entrypoint - execute an image file Function Main entrypoint for executing a T3 image file. Loads an image file and begins execution. Returns when execution terminates. Notes Modified 10/07/99 MJRoberts - Creation */ #include <stdlib.h> #include <string.h> #include <stdio.h> #include <ctype.h> #include "os.h" #include "t3std.h" #include "vmerr.h" #include "vmfile.h" #include "vmimage.h" #include "vmrun.h" #include "vmimgrb.h" #include "vmmain.h" #include "vmhost.h" #include "vmhostsi.h" #include "vminit.h" #include "vmpredef.h" #include "vmobj.h" #include "vmvsn.h" #include "charmap.h" #include "vmsave.h" #include "vmtype.h" #include "vmrunsym.h" #include "vmmcreg.h" #include "vmbifreg.h" #include "vmbiftad.h" #include "sha2.h" #include "vmnet.h" /* ------------------------------------------------------------------------ */ /* * Convert a directory path parameter to an absolute path, and return an * allocated copy of the string. */ static char *path_param_to_abs(const char *param) { /* make sure the path is expressed in absolute format */ char abuf[OSFNMAX]; os_get_abs_filename(abuf, sizeof(abuf), param); /* return a copy of the path */ return lib_copy_str(abuf); } /* * Extract the path from a filename, convert it to an absolute path, and * return an allocated copy of the string. */ static char *file_param_to_abs_path(const char *param) { /* extract the path portion */ char path[OSFNMAX]; os_get_path_name(path, sizeof(path), param); /* return it in absolute form */ return path_param_to_abs(path); } /* ------------------------------------------------------------------------ */ /* * Execute an image file. If an exception occurs, we'll display a * message on the console, and we'll return the error code; we'll return * zero on success. If an error occurs, we'll fill in 'errbuf' with a * message describing the problem. */ int vm_run_image(const vm_run_image_params *params) { CVmFile *fp = 0; CVmImageLoader *volatile loader = 0; CVmImageFile *volatile imagefp = 0; unsigned long image_file_base = 0; int retval; vm_globals *vmg__; /* * get the full absolute path for the image file, to ensure that future * references are independent of working directory changes */ os_get_abs_filename(G_os_gamename, sizeof(G_os_gamename), params->image_file_name); /* presume we will return success */ retval = 0; /* make sure the local time zone settings are initialized */ os_tzset(); /* create the file object */ fp = new CVmFile(); /* initialize the VM */ vm_init_options opts(params->hostifc, params->clientifc, params->charset, params->log_charset); vm_initialize(&vmg__, &opts); /* catch any errors that occur during loading and running */ err_try { /* if we have a resource root path, tell the host interface */ if (params->res_dir != 0) params->hostifc->set_res_dir(params->res_dir); /* * Save the default working directory for file operations, if * specified. If it's not specified, use the image file directory * as the default. */ if (params->file_dir != 0) G_file_path = path_param_to_abs(params->file_dir); /* set the sandbox directory (for file safety restrictions) */ if (params->sandbox_dir != 0) G_sandbox_path = path_param_to_abs(params->sandbox_dir); else G_sandbox_path = file_param_to_abs_path(params->image_file_name); /* set the networking configuration in the globals */ G_net_config = params->netconfig; /* tell the client system to initialize */ params->clientifc->client_init( VMGLOB_ADDR, params->script_file, params->script_quiet, params->log_file, params->cmd_log_file, params->show_banner ? T3VM_BANNER_STRING : 0, params->more_mode); /* open the byte-code file */ if (params->load_from_exe) { /* find the image within the executable */ osfildef *exe_fp = os_exeseek(G_os_gamename, "TGAM"); if (exe_fp == 0) err_throw(VMERR_NO_IMAGE_IN_EXE); /* * set up to read from the executable at the location of the * embedded image file that we just found */ image_file_base = osfpos(exe_fp); fp->set_file(exe_fp, image_file_base); } else { /* reading from a normal file - open the file */ fp->open_read(G_os_gamename, OSFTT3IMG); } /* create the loader */ imagefp = new CVmImageFileExt(fp); loader = new CVmImageLoader(imagefp, G_os_gamename, image_file_base); /* load the image */ loader->load(vmg0_); /* let the client prepare for execution */ params->clientifc->pre_exec(VMGLOB_ADDR); /* if desired, seed the random number generator */ if (params->seed_rand) CVmBifTADS::randomize(vmg_ 0); #ifdef TADSNET /* * if we have a network configuration, get the game's IFID - this * is needed to identify the game to the storage server */ if (params->netconfig != 0) { char *ifid = vm_get_ifid(params->hostifc); if (ifid != 0) { params->netconfig->set("ifid", ifid); t3free(ifid); } } #endif /* TADSNET */ /* run the program from the main entrypoint */ loader->run(vmg_ params->prog_argv, params->prog_argc, 0, 0, params->saved_state); /* tell the client we're done with execution */ params->clientifc->post_exec(VMGLOB_ADDR); } err_catch(exc) { /* * report anything except DEBUG VM HALT, which is special: it * indicates that the user explicitly terminated execution via the * debugger UI, so there's obviously no need to report an error */ if (exc->get_error_code() != VMERR_DBG_HALT) { /* tell the client execution failed due to an error */ params->clientifc->post_exec_err(VMGLOB_ADDR); /* note the error code for returning to the caller */ retval = exc->get_error_code(); /* get the message for the error */ char errbuf[512]; CVmRun::get_exc_message(vmg_ exc, errbuf, sizeof(errbuf), TRUE); /* display the message */ params->clientifc->display_error(VMGLOB_ADDR, exc, errbuf, FALSE); } } err_end; /* done with the file base path and sandbox path */ lib_free_str(G_file_path); lib_free_str(G_sandbox_path); /* unload the image */ if (loader != 0) loader->unload(vmg0_); /* delete the loader and the image file object */ if (loader != 0) delete loader; if (imagefp != 0) delete imagefp; /* notify the client */ params->clientifc->client_terminate(VMGLOB_ADDR); #ifdef TADSNET /* * Delete the network configuration, if we have one. Note that a * network configuration might have been created dyanmically during * execution even if the caller didn't originally provide one. */ if (G_net_config != 0) delete G_net_config; #endif /* terminate the VM */ vm_terminate(vmg__, params->clientifc); /* delete the file */ if (fp != 0) delete fp; /* return the status code */ return retval; } /* ------------------------------------------------------------------------ */ /* * Read an option argument from an option that allows its arguments to be * either appended directly to the end of the option (as in "-otest") or * to follow as the subsequent vector item (as in "-o test"). optlen * gives the length of the option prefix itself, including the hyphen * prefix (so an option "-o" would have length 2). We'll increment * *curarg if we consume the extra vector position. Returns null if there * isn't an argument for the option. */ static char *get_opt_arg(int argc, char **argv, int *curarg, int optlen) { /* * if we have an argument (i.e., any non-empty text) appended directly * to the option vector item, we don't need to consume an extra vector * position */ if (argv[*curarg][optlen] != '\0') { /* the argument is merely the remainder of this vector item */ return &argv[*curarg][optlen]; } /* advance to the next vector position */ ++(*curarg); /* if we don't have any vector items left, there's no argument */ if (*curarg >= argc) return 0; /* this vector item is the next argument */ return argv[*curarg]; } /* ------------------------------------------------------------------------ */ /* * Create the network configuration object, if we haven't already. */ #ifdef TADSNET static void create_netconfig( CVmMainClientIfc *clientifc, TadsNetConfig *&netconfig, char **argv) { if (netconfig == 0) { /* create the object */ netconfig = new TadsNetConfig(); /* build the path to the web config file */ char confname[OSFNMAX], confpath[OSFNMAX]; os_get_special_path(confpath, sizeof(confpath), argv[0], OS_GSP_T3_SYSCONFIG); os_build_full_path(confname, sizeof(confname), confpath, "tadsweb.config"); /* if there's a net configuration file, read it */ osfildef *fp = osfoprb(confname, OSFTTEXT); if (fp != 0) { /* read the file into the configuration object */ netconfig->read(fp, clientifc); /* done with the file */ osfcls(fp); } else { /* warn that the file is missing */ char *msg = t3sprintf_alloc( "Warning: -webhost mode specified, but couldn't " "read web configuration file \"%s\"", confname); clientifc->display_error(0, 0, msg, FALSE); t3free(msg); } } } #endif /* TADSNET */ /* ------------------------------------------------------------------------ */ /* * Main Entrypoint for command-line invocations. For simplicity, a * normal C main() or equivalent entrypoint can invoke this routine * directly, using the usual argc/argv conventions. * * Returns a status code suitable for use with exit(): OSEXSUCC if we * successfully loaded and ran an executable, OSEXFAIL on failure. If * an error occurs, we'll fill in 'errbuf' with a message describing the * problem. */ int vm_run_image_main(CVmMainClientIfc *clientifc, const char *executable_name, int argc, char **argv, int defext, int test_mode, CVmHostIfc *hostifc) { char image_file_name[OSFNMAX]; vm_run_image_params params(clientifc, hostifc, image_file_name); int curarg; int stat; int found_image = FALSE; int hide_usage = FALSE; int usage_err = FALSE; #ifdef TADSNET char *storage_sid = 0; #endif /* check to see if we can load from the .exe file */ { /* look for an image file attached to the executable */ osfildef *fp = os_exeseek(argv[0], "TGAM"); if (fp != 0) { /* close the file */ osfcls(fp); /* note that we want to load from the executable */ params.load_from_exe = TRUE; } /* * if loading from the exe file, get the custom saved game suffix, * if defined */ if (params.load_from_exe && (fp = os_exeseek(argv[0], "SAVX")) != 0) { ushort len; char buf[OSFNMAX]; /* read the length and the name */ if (!osfrb(fp, &len, sizeof(len)) && len < sizeof(buf) && !osfrb(fp, buf, len)) { /* null-terminate the string */ buf[len] = '\0'; /* save it in the OS layer */ os_set_save_ext(buf); } /* close the file */ osfcls(fp); } } /* scan options */ for (curarg = 1 ; curarg < argc && argv[curarg][0] == '-' ; ++curarg) { /* * if the argument is just '-', it means we're explicitly leaving * the image filename blank and skipping to the arguments to the VM * program itself */ if (argv[curarg][1] == '\0') break; /* check the argument */ switch(argv[curarg][1]) { case 'b': if (strcmp(argv[curarg], "-banner") == 0) { /* make a note to show the banner */ params.show_banner = TRUE; } else goto opt_error; break; case 'c': if (strcmp(argv[curarg], "-cs") == 0) { /* set the console character set name */ if (++curarg < argc) params.charset = argv[curarg]; else goto opt_error; } else if (strcmp(argv[curarg], "-csl") == 0) { /* set the log file character set name */ if (++curarg < argc) params.log_charset = argv[curarg]; else goto opt_error; } else goto opt_error; break; case 'n': if (argv[curarg][2] == 's') { /* network safety level - parse the ## arguments */ const char *cli = &argv[curarg][3]; const char *srv = &argv[curarg][4]; /* if there's no server level, use the one level for both */ if (*cli == '\0' || *srv == '\0') srv = cli; /* validate the arguments */ if (*cli < '0' || *cli > '2' || *srv < '0' || *srv > '2') goto opt_error; /* set the level */ hostifc->set_net_safety(*cli - '0', *srv - '0'); } else if (strcmp(argv[curarg], "-nobanner") == 0) { /* make a note not to show the banner */ params.show_banner = FALSE; } else if (strcmp(argv[curarg], "-norand") == 0 || strcmp(argv[curarg], "-norandomize") == 0) { /* make a note not to seed the RNG */ params.seed_rand = FALSE; } else if (strcmp(argv[curarg], "-nomore") == 0) { /* turn off MORE mode */ params.more_mode = FALSE; } else goto opt_error; break; case 's': if (argv[curarg][2] == 'd') { /* sandbox directory */ if (++curarg < argc) params.sandbox_dir = argv[curarg]; else goto opt_error; } else { /* * file safety level - get the read and write levels, if * specified separately */ const char *rd = &argv[curarg][2]; const char *wrt = &argv[curarg][3]; /* if there's no write level, base it on the read level */ if (*rd == '\0' || *wrt == '\0') wrt = rd; /* check the ranges */ if (*rd < '0' || *rd > '4' || *wrt < '0' || *wrt > '4' || *(wrt+1) != '\0') { /* invalid level */ goto opt_error; } else { /* set the level in the host application */ hostifc->set_io_safety(*rd - '0', *wrt - '0'); } } break; case 'i': case 'I': /* * read from a script file (little 'i' reads silently, big 'I' * echoes the log as it goes) - the next argument, or the * remainder of this argument, is the filename */ params.script_quiet = (argv[curarg][1] == 'i'); params.script_file = get_opt_arg(argc, argv, &curarg, 2); if (params.script_file == 0) goto opt_error; break; case 'l': /* log output to file */ params.log_file = get_opt_arg(argc, argv, &curarg, 2); if (params.log_file == 0) goto opt_error; break; case 'o': /* log commands to file */ params.cmd_log_file = get_opt_arg(argc, argv, &curarg, 2); if (params.cmd_log_file == 0) goto opt_error; break; case 'p': /* check what follows */ if (strcmp(argv[curarg], "-plain") == 0) { /* tell the client to set plain ASCII mode */ clientifc->set_plain_mode(); break; } else goto opt_error; break; case 'r': /* get the name of the saved state file to restore */ params.saved_state = get_opt_arg(argc, argv, &curarg, 2); if (params.saved_state == 0) goto opt_error; break; case 'R': /* note the resource root directory */ params.res_dir = get_opt_arg(argc, argv, &curarg, 2); if (params.res_dir == 0) goto opt_error; break; case 'd': /* note the working directory for file operations */ params.file_dir = get_opt_arg(argc, argv, &curarg, 2); if (params.file_dir == 0) goto opt_error; break; case 'X': /* special system operations */ if (strcmp(argv[curarg], "-Xinterfaces") == 0 && curarg+1 < argc) { /* write out the interface report, then terminate */ vm_interface_report(clientifc, argv[++curarg]); stat = OSEXSUCC; goto done; } else goto opt_error; break; #ifdef TADSNET case 'w': /* -webhost, -websid, -webimage */ if (strcmp(argv[curarg], "-webhost") == 0 && curarg+1 < argc) { /* if necessary, create the web hosting config object */ create_netconfig(clientifc, params.netconfig, argv); /* set the web host name */ params.netconfig->set("hostname", argv[++curarg]); } else if (strcmp(argv[curarg], "-websid") == 0 && curarg+1 < argc) { /* web storage server SID - save it for later */ storage_sid = argv[++curarg]; } else if (strcmp(argv[curarg], "-webimage") == 0 && curarg+1 < argc) { /* * Original URL of image file. This is provided for * logging purposes only; */ create_netconfig(clientifc, params.netconfig, argv); params.netconfig->set("image.url", argv[++curarg]); } else { /* invalid option - flag an error */ goto opt_error; } break; #endif /* TADSNET */ default: opt_error: /* discard remaining arguments */ curarg = argc; /* note the error */ usage_err = TRUE; break; } } /* * If there was no usage error so far, but we don't have an image * filename argument, try to find the image file some other way. If we * found an image file embedded in the executable, don't even bother * looking for an image-file argument - we can only run the embedded * image in this case. */ if (usage_err) { /* there was a usage error - don't bother looking for an image file */ } else if (!params.load_from_exe && curarg + 1 <= argc && strcmp(argv[curarg], "-") != 0) { /* the last argument is the image file name */ strcpy(image_file_name, argv[curarg]); found_image = TRUE; /* * If the given filename exists, use it as-is; otherwise, if * we're allowed to add an extension, try applying a default * extension of "t3" (formerly "t3x") to the given name. */ if (defext && osfacc(image_file_name)) { /* the given name doesn't exist - try a default extension */ os_defext(image_file_name, "t3"); /* formerly "t3x" */ } } else { /* * if we're loading from the executable, try using the executable * filename as the image file */ if (params.load_from_exe && os_get_exe_filename(image_file_name, sizeof(image_file_name), argv[0])) found_image = TRUE; /* * If we still haven't found an image file, try to get the image * file from the saved state file, if one was specified. Don't * attempt this if we're loading the image from the executable, as * we don't want to allow running a different image file in that * case. */ if (!params.load_from_exe && !found_image && params.saved_state != 0) { osfildef *save_fp; /* open the saved state file */ save_fp = osfoprb(params.saved_state, OSFTT3SAV); if (save_fp != 0) { /* get the name of the image file */ if (CVmSaveFile::restore_get_image( save_fp, image_file_name, sizeof(image_file_name)) == 0) { /* we successfully obtained the filename */ found_image = TRUE; } /* close the file */ osfcls(save_fp); } } /* * if we haven't found the image, and the host system provides a * way of asking the user for a filename, try that */ if (!params.load_from_exe && !found_image) { /* ask the host system for a game name */ switch (hostifc->get_image_name(image_file_name, sizeof(image_file_name))) { case VMHOST_GIN_IGNORED: /* no effect - we have no new information */ break; case VMHOST_GIN_CANCEL: /* * the user cancelled the dialog - we don't have a * filename, but we also don't want to show usage, since * the user chose not to proceed */ hide_usage = TRUE; break; case VMHOST_GIN_ERROR: /* * an error occurred showing the dialog - there's not * much we can do except show the usage message */ break; case VMHOST_GIN_SUCCESS: /* that was successful - we have an image file now */ found_image = TRUE; break; } } } /* * if we don't have an image file name by this point, we can't * proceed - show the usage message and terminate */ if (usage_err || !found_image) { char buf[OSFNMAX + 1024]; /* show the usage message if allowed */ if (params.load_from_exe && !usage_err) { sprintf(buf, "An error occurred loading the T3 VM program from " "the embedded data file. This application executable " "file might be corrupted.\n"); /* display the message */ clientifc->display_error(0, 0, buf, FALSE); } else if (!hide_usage) { /* build the usage message */ sprintf(buf, "%s\n" "usage: %s [options] %sarguments]\n" "options:\n" " -banner - show the version/copyright banner\n" " -cs xxx - use character set 'xxx' for keyboard " "and display\n" " -csl xxx - use character set 'xxx' for log files\n" " -d path - set default directory for file operations\n" " -i file - read command input from file (quiet mode)\n" " -I file - read command input from file (echo mode)\n" " -l file - log all console input/output to file\n" " -norand - don't seed the random number generator\n" " -ns## - set the network safety level for Client " "and Server (each # is from\n" " 0 to 2 - 0 is the least restrictive, 2 " "allows no network access)\n" " -o file - log console input to file\n" " -plain - run in plain mode (no cursor positioning, " "colors, etc.)\n" " -r file - restore saved state from file\n" " -R dir - set directory for external resources\n" " -s## - set file safety level for read & write " "(each # is from 0 to 4;\n" " 0 is the least restrictive, 4 prohibits all " "file access)\n" " -sd dir - set the sandbox directory for file " "safety restrictions\n" "\n" "If provided, the optional extra arguments are passed " "to the program's\n" "main entrypoint.\n", T3VM_BANNER_STRING, executable_name, params.load_from_exe ? "[- " : "<image-file-name> ["); /* display the message */ clientifc->display_error(0, 0, buf, FALSE); } /* return failure */ stat = OSEXFAIL; goto done; } /* * if we're in test mode, replace the first argument to the program * with its root name, so that we don't include any path information * in the argument list */ if (test_mode && curarg <= argc && argv[curarg] != 0) argv[curarg] = os_get_root_name(argv[curarg]); #ifdef TADSNET /* * if there's a storage server SID, add it to the net configuration */ if (params.netconfig != 0 && storage_sid != 0) params.netconfig->set("storage.sessionid", storage_sid); #endif /* TADSNET */ /* pass the arguments beyond the image file name to the .t3's main() */ params.prog_argv = argv + curarg; params.prog_argc = argc - curarg; /* run the program */ stat = vm_run_image(¶ms); done: /* return the status code */ return stat; } /* ------------------------------------------------------------------------ */ /* * Copy a filename with a given size limit. This works essentially like * strncpy(), but we guarantee that the result is null-terminated even if * it's truncated. */ static void strcpy_limit(char *dst, const char *src, size_t limit) { size_t copy_len; /* get the length of the source string */ copy_len = strlen(src); /* * if it exceeds the available space, leaving space for the null * terminator, limit the copy to the available space */ if (copy_len > limit - 1) copy_len = limit - 1; /* copy as much as we can */ memcpy(dst, src, copy_len); /* null-terminate what we managed to copy */ dst[copy_len] = '\0'; } /* ------------------------------------------------------------------------ */ /* * Given a saved game file, try to identify the game file that created the * saved game. */ static int vm_get_game_file_from_savefile(const char *savefile, char *fname, size_t fnamelen) { /* open the saved game file */ osfildef *fp = osfoprb(savefile, OSFTBIN); /* if that failed, there's no way to read the game file name */ if (fp == 0) return FALSE; /* presume failure */ int ret = FALSE; /* read the first few bytes */ char buf[128]; if (!osfrb(fp, buf, 16)) { /* check for a saved game signature we recognize */ if (memcmp(buf, "TADS2 save/g\012\015\032\000", 16) == 0) { /* * It's a TADS 2 saved game with embedded .GAM file * information. The filename immediately follows the signature * (the 15 bytes we just matched), with a two-byte length * prefix. Seek to the length prefix and read it. */ size_t len; if (!osfseek(fp, 16, OSFSK_SET) && !osfrb(fp, buf, 2)) len = osrp2(buf); else len = 0; /* limit the read length to our caller's available buffer */ if (len > fnamelen - 1) len = fnamelen - 1; /* read the filename */ if (!osfrb(fp, fname, len)) { /* null-terminate it */ fname[len] = '\0'; /* success */ ret = TRUE; } } else if (memcmp(buf, "T3-state-v", 10) == 0) { /* * It's a T3 saved state file. The image filename is always * embedded in this type of file, so seek back to the start of * the file and read the filename. * * Note that restore_get_image() returns zero on success, so we * want to return true if and only if that routine returns * zero. */ osfseek(fp, 0, OSFSK_SET); ret = (CVmSaveFile::restore_get_image(fp, fname, fnamelen) == 0); } else { /* * it's not a signature we know, so it must not be a saved * state file (at least not one we can deal with) */ } } /* we're done with the file now, so close it */ osfcls(fp); /* return the result */ return ret; } /* ------------------------------------------------------------------------ */ /* * Parse a command line to determine the name of the game file specified * by the arguments. We'll return the index of the argv element that * specifies the name of the game, or a negative number if there is no * filename argument. * * Note that our parsing will work for TADS 2 or TADS 3 interpreter * command lines, so this routine can be used to extract the filename from * an ambiguous command line in order to check the file for its type and * thereby resolve which interpreter to use. */ int vm_get_game_arg(int argc, const char *const *argv, char *buf, size_t buflen, int *engine_type) { /* presume we won't find a file to restore */ const char *restore_file = 0; /* presume we won't identify the engine type based soley on arguments */ *engine_type = VM_GGT_INVALID; /* * Scan the arguments for the union of the TADS 2 and TADS 3 command * line options. Start at the second element of the argument vector, * since the first element is the executable name. Keep going until * we run out of options, each of which must start with a '-'. * * Note that we don't care about the meanings of the options - we * simply want to skip past them. This is more complicated than * merely looking for the first argument without a '-' prefix, because * some of the options allow arguments, and an option argument can * sometimes - depending on the argument - take the next position in * the argument vector after the option itself. So, we must determine * the meaning of each option well enough that we can tell whether or * not the next argv element after the option is part of the option. */ int i; for (i = 1 ; i < argc && argv[i][0] == '-' ; ++i) { /* * check the first character after the hyphen to determine which * option we have */ switch(argv[i][1]) { case 'b': /* * tads 3 "-banner" (no arguments, so just consume it and * continue) */ break; case 'c': /* * tads 3 "-cs charset" or "-csl charset"; tads 2 "-ctab-" or * "-ctab tab" */ if (strcmp(argv[i], "-ctab") == 0 || strcmp(argv[i], "-cs") == 0 || strcmp(argv[i], "-csl") == 0) { /* there's another argument giving the filename */ ++i; } break; case 'd': /* tads 2 "-double[+-]" (no arguments), tads 3 -d <path> */ if (memcmp(argv[i], "-double", 7) == 0 && (argv[i][7] == '\0' || argv[i][7] == '+' || argv[i][7] == '-')) { /* tads 2 -double - no further arguments */ } else if (argv[i][2] == '\0') { /* tads 3 -d <path> */ ++i; } break; case 'm': /* tads 2 "-msSize", "-mhSize", "-mSize" */ switch(argv[i][2]) { case 's': case 'h': /* * argument required - if nothing follows the second * letter, consume the next vector item as the option * argument */ if (argv[i][3] == '\0') ++i; break; case '\0': /* * argument required, but nothing follows the "-m" - * consume the next vector item */ ++i; break; default: /* argument required and present */ break; } break; case 't': /* tads 2 "-tfFile", "-tsSize", "-tp[+-]", "-t[+0]" */ switch(argv[i][2]) { case 'f': case 's': /* * argument required - consume the next vector item if * nothing is attached to this item */ if (argv[i][3] == '\0') ++i; break; case 'p': /* no arguments */ break; default: /* no arguments */ break; } break; case 'u': /* * tads 2 "-uSize" - argument required, so consume the next * vector item if necessary */ if (argv[i][2] == '\0') ++i; break; case 'n': /* * tads3 "-ns", "-nobanner", "-norand" take no arguments (-ns * does have extra parameter characters, but they have to be * concatenated as part of the same argv entry, as in "-ns11") */ break; case 's': if (argv[i][2] == 'd') { /* tads 3 -sd <dir> - skip the <dir> parameter */ ++i; } else if (argv[i][2] == '\0') { /* * tads 2/3 "-s#" (#=0,1,2,3,4); in tads 2, the # can be * separated by a space from the -s, so we'll allow it this * way in general */ ++i; } break; case 'i': /* tads 2/3 "-iFile" - consume an argument */ if (argv[i][2] == '\0') ++i; break; case 'l': /* tads 2/3 "-lFile" - consume an argument */ if (argv[i][2] == '\0') ++i; break; case 'o': /* tads 2/3 "-oFile" - consume an argument */ if (argv[i][2] == '\0') ++i; break; case 'p': /* tads 2/3 "-plain"; tads 2 "-p[+-]" - no arguments */ break; case 'R': /* tads 3 '-Rdir' - consume an argument */ if (argv[i][2] == '\0') ++i; break; case 'r': /* tads 2/3 "-rFile" - consume an argument */ if (argv[i][2] != '\0') { /* the file to be restored is appended to the "-r" */ restore_file = argv[i] + 2; } else { /* the file to be restored is the next argument */ ++i; restore_file = argv[i]; } break; case 'X': /* TADS 3 special system commands */ if (strcmp(argv[i], "-Xinterfaces") == 0) { /* * this is a stand-alone command that doesn't require a * game file; return the engine type directly */ strcpy_limit(buf, "", buflen); *engine_type = VM_GGT_TADS3; return TRUE; } break; case 'w': /* tads 3 -webhost, -websid */ if (strcmp(argv[i], "-webhost") == 0 || strcmp(argv[i], "-websid") == 0) { /* this takes an additional argument */ ++i; } break; } } /* * We have no more options, so the next argument is the game filename. * If we're out of argv elements, then no game filename was directly * specified, in which case we'll try looking for a file spec in the * restore file, if present. */ if (i < argc) { /* there's a game file argument - copy it to the caller's buffer */ strcpy_limit(buf, argv[i], buflen); /* return success */ return TRUE; } else if (restore_file != 0) { /* * There's no game file argument, but there is a restore file * argument. Try identifying the game file from the original game * file specification stored in the saved state file. */ return vm_get_game_file_from_savefile(restore_file, buf, buflen); } else { /* * there's no game file or restore file argument, so they've given * us nothing to go on - there's no game file */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Given the name of a file, determine the engine (TADS 2 or TADS 3) for * which the file was compiled, based on the file's signature. Returns * one of the VM_GGT_xxx codes. */ static int vm_get_game_type_for_file(const char *filename) { osfildef *fp; char buf[16]; int ret; /* try opening the filename exactly as given */ fp = osfoprb(filename, OSFTBIN); /* if the file doesn't exist, tell the caller */ if (fp == 0) return VM_GGT_NOT_FOUND; /* read the first few bytes of the file, where the signature resides */ if (osfrb(fp, buf, 16)) { /* * error reading the file - any valid game file is going to be at * least long enough to hold the number of bytes we asked for, so * it must not be a valid file */ ret = VM_GGT_INVALID; } else { /* check the signature we read against the known signatures */ if (memcmp(buf, "TADS2 bin\012\015\032", 12) == 0) ret = VM_GGT_TADS2; else if (memcmp(buf, "T3-image\015\012\032", 11) == 0) ret = VM_GGT_TADS3; else ret = VM_GGT_INVALID; } /* close the file */ osfcls(fp); /* return the version identifier */ return ret; } /* * Given a game file argument, determine which engine (TADS 2 or TADS 3) * should be used to run the game. */ int vm_get_game_type(const char *filename, char *actual_fname, size_t actual_fname_len, const char *const *defexts, size_t defext_count) { int good_count; int last_good_ver; size_t i; /* * If the exact filename as given exists, determine the file type * directly from this file without trying any default extensions. */ if (osfacc(filename) == 0) { /* the actual filename is exactly what we were given */ if (actual_fname_len != 0) { /* copy the filename, limiting it to the buffer length */ strncpy(actual_fname, filename, actual_fname_len); actual_fname[actual_fname_len - 1] = '\0'; } /* return the type according to the file's signature */ return vm_get_game_type_for_file(filename); } /* presume we won't find any good files using default extensions */ good_count = 0; /* try each default extension supplied */ for (i = 0 ; i < defext_count ; ++i) { int cur_ver; char cur_fname[OSFNMAX]; /* * build the default filename from the given filename and the * current default suffix */ strcpy(cur_fname, filename); os_defext(cur_fname, defexts[i]); /* get the version for this file */ cur_ver = vm_get_game_type_for_file(cur_fname); /* if it's a valid code, note it and remember it */ if (vm_ggt_is_valid(cur_ver)) { /* it's a valid file - count it */ ++good_count; /* remember its version as the last good file's version */ last_good_ver = cur_ver; /* remember its name as the last good file's name */ if (actual_fname_len != 0) { /* copy the filename, limiting it to the buffer length */ strncpy(actual_fname, cur_fname, actual_fname_len); actual_fname[actual_fname_len - 1] = '\0'; } } } /* * If we had exactly one good match, return it. We will already have * filled in actual_fname with the last good filename, so all we need * to do in this case is return the version ID for the last good file. */ if (good_count == 1) return last_good_ver; /* * If we didn't find any matches, tell the caller there is no match * for the given filename. */ if (good_count == 0) return VM_GGT_NOT_FOUND; /* we found more than one match, so the type is ambiguous */ return VM_GGT_AMBIG; } /* ------------------------------------------------------------------------ */ /* * Generate the interface report */ void vm_interface_report(CVmMainClientIfc *cli, const char *fname) { int i; char nm[256]; char buf[300]; char *p; /* open the output file */ osfildef *fp = osfopwt(fname, OSFTTEXT); if (fp == 0) { cli->display_error( 0, 0, "-Xinterfaces failed: unable to open output file", FALSE); return; } /* start the XML file */ os_fprintz(fp, "<?xml version=\"1.0\"?>\n" "<tads3 xmlns=" "\"http://www.tads.org/xmlns/tads3/UpdateData\">\n"); /* write the metaclass interface list */ os_fprintz(fp, " <metaclasses>\n"); for (i = 0 ; G_meta_reg_table[i].meta != 0 ; ++i) { /* copy the name string */ lib_strcpy(nm, sizeof(nm), (*G_meta_reg_table[i].meta)->get_meta_name()); /* null-terminate at the version delimiter */ p = strchr(nm, '/'); *p++ = '\0'; /* display the information */ t3sprintf(buf, sizeof(buf), " <metaclass>\n" " <id>%s</id>\n" " <version>%s</version>\n" " </metaclass>\n", nm, p); os_fprintz(fp, buf); } os_fprintz(fp, " </metaclasses>\n"); /* write the built-in function information */ os_fprintz(fp, " <functionsets>\n"); for (i = 0 ; G_bif_reg_table[i].func_set_id != 0 ; ++i) { /* get the function set name/version string */ lib_strcpy(nm, sizeof(nm), G_bif_reg_table[i].func_set_id); /* null-terminate at the version delimiter */ p = strchr(nm, '/'); *p++ = '\0'; /* display the information */ t3sprintf(buf, sizeof(buf), " <functionset>\n" " <id>%s</id>\n" " <version>%s</version>\n" " </functionset>\n", nm, p); os_fprintz(fp, buf); } os_fprintz(fp, " </functionsets>\n"); /* end the XML file */ os_fprintz(fp, "</tads3>\n"); /* done with the file */ osfcls(fp); } /* ------------------------------------------------------------------------ */ /* * Retrieve the IFID from the gameinfo.txt resource */ char *vm_get_ifid(CVmHostIfc *hostifc) { /* first, find the gameinfo.txt resource */ unsigned long siz; osfildef *fp = hostifc->find_resource("gameinfo.txt", 12, &siz); /* if we didn't find the resource, there's no IFID */ if (fp == 0) return 0; /* * allocate a buffer to load the gameinfo.txt contents into (leave room * for an extra newline at the end, for uniformity) */ char *buf = (char *)t3malloc(siz + 1); if (buf == 0) return 0; /* presume we won't find an IFID */ char *ifid = 0; /* load the contents */ if (!osfrb(fp, buf, siz)) { char *p, *dst; /* get the buffer end pointer */ char *endp = buf + siz; /* change CR-LF, LF-CR, and CR to '\n' */ for (p = dst = buf ; p < endp ; ) { if (*p == 10) { /* LF - store as '\n', and skip the CR in an LF-CR pair */ *dst++ = '\n'; if (++p < endp && *p == 13) ++p; } else if (*p == 13) { /* CR - store as '\n', and skip the LF in a CR-LF pair */ *dst++ = '\n'; if (++p < endp && *p == 10) ++p; } else { /* copy others as-is */ *dst++ = *p++; } } /* note the new end pointer */ endp = dst; /* remove comments and blank lines, and merge continuation lines */ for (p = dst = buf ; p < endp ; ) { /* skip leading whitespace */ for ( ; p < endp && isspace(*p) ; ++p) ; /* if that's the end of the buffer, we're done */ if (p == endp) break; /* skip blank lines */ if (*p == '\n') { ++p; continue; } /* skip comment lines */ if (*p == '#') { /* skip to the end of the line */ for ( ; p < endp && *p != '\n' ; ++p) ; continue; } /* * We're on a line with actual contents. Merge continuation * lines: a continuation line is a line following this line * that starts with a space and contains at least one * non-whitespace character. */ while (p < endp) { /* scan and copy to the end of this line */ for ( ; p < endp && *p != '\n' ; *dst++ = *p++) ; /* check the next line to see if it's a continuation line */ if (p < endp && *(p+1) != '\n' && isspace(*(p+1))) { /* scan ahead for non-whitespace */ char *q; for (q = p + 1 ; q < endp && *q != '\n' && isspace(*q) ; ++q) ; /* if we found something, it's a continuation line */ if (q < endp && *q != '\n') { /* convert the newline to whitespace in the output */ *dst++ = ' '; /* skip the whitespace */ p = q; /* resume scanning this line */ continue; } } /* * It's not a continuation line. Store a newline at the * end of the line. */ *dst++ = '\n'; /* skip the newline */ if (p < endp) ++p; /* done processing this line */ break; } } /* note the new end pointer */ endp = dst; /* * Okay, the buffer is in a nice canonical format, so we can search * for the IFID name/value pair. */ for (p = buf ; p < endp ; ++p) { /* if we're at the IFID line, retrieve the value */ if (memicmp(p, "ifid:", 5) == 0) { /* this is the IFID line - skip the IFID: and whitespace */ for (p += 5 ; p < endp && isspace(*p) ; ++p) ; /* * the IFID is the portion up to the next space, newline, * or comma */ char *start; for (start = p ; p < endp && *p != '\n' && !isspace(*p) ; ++p) ; /* allocate a copy of the IFID */ ifid = lib_copy_str(start, p - start); /* we're done */ break; } /* scan ahead to the end of the line */ for ( ; p < endp && *p != '\n' ; ++p) ; } } /* done with the gameinfo.txt file and the buffer we loaded it into */ osfcls(fp); t3free(buf); /* return the IFID, if we found one */ return ifid; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmmain.h��������������������������������������������������������������������������0000664�0000000�0000000�00000050307�12650170723�0015323�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmmain.h - main entrypoint to run a T3 image file Function Notes Modified 10/07/99 MJRoberts - Creation */ #ifndef VMMAIN_H #define VMMAIN_H #include "vmglob.h" /* * Parse a command line to determine the name of the game file specified by * the arguments. If we can find a game file specification, we'll fill in * 'buf' with the filename and return true; if there's no file name * specified, we'll return false. * * Note that our parsing will work for TADS 2 or TADS 3 interpreter command * lines, so this routine can be used to extract the filename from an * ambiguous command line in order to check the file for its type and * thereby resolve which interpreter to use. * * Note that the filename might not come directly from the command * arguments, since it might be implied. If there's no game file directly * specified, but there is an explicit "-r" option to restore a saved game, * we'll pull the game filename out of the saved game file if possible. * Saved game files in both TADS 2 and TADS 3 can store the original game * file name that was being executed at the time the game was saved. */ int vm_get_game_arg(int argc, const char *const *argv, char *buf, size_t buflen, int *engine_type); /* * Given a game file argument, determine which engine (TADS 2 or TADS 3) * should be used to run the game. * * We'll first check to see if the given file exists. If it does, we'll * read header information from the file to try to identify the game. If * the file does not exist, we will proceed to check default suffixes, if * the suffix arguments are non-null. * * If defexts is not null, it gives an array of default filename suffix * strings, suitable for use with os_defext(). When the name as given * doesn't refer to an existing file, we'll try looking for files with * these suffixes, one at a time. * * Returns one of the VM_GGT_xxx codes. * * If the return value is 2 or 3, we'll fill in the actual_filename buffer * with the full name of the file; if we added a default suffix, the * suffix will be included in this result. */ int vm_get_game_type(const char *filename, char *actual_filename, size_t actual_filename_buffer_length, const char *const *defexts, size_t defext_count); /* * Returns codes for vm_get_game_type() */ /* game type is TADS 2 */ #define VM_GGT_TADS2 2 /* game type is TADS 3 */ #define VM_GGT_TADS3 3 /* game file not found (even after trying default extensions) */ #define VM_GGT_NOT_FOUND (-1) /* game file exists but isn't a valid tads 2 or tads 3 game */ #define VM_GGT_INVALID (-2) /* * ambiguous filename - the exact filename doesn't exist, and more than * one default suffix version exists */ #define VM_GGT_AMBIG (-3) /* * determine if a VM_GGT_xxx code refers to a valid engine version: * returns true if the code is an engine version, false if the code is an * error indication */ #define vm_ggt_is_valid(code) ((code) > 0) /* * Parameters structure for vm_run_image. vm_run_image() essentially takes * the parsed version of the execution parameters, and loads and executes * the .t3 file accordingly. The caller is responsible for parsing the * command-line arguments or equivalent. You can use vm_run_image_main() * if you have a standard Unix-style argv/argc and want to do the standard * t3run parsing. If you don't have a standard argv/argc (e.g., you get * your parameters from a GUI dialog, an external config file), you can * bypass the normal t3run argv parsing by setting up this structure from * your parameters and calling vm_run_image(). You can also use this * approach if you want override the standard t3run option syntax and do * your own argv parsing. */ struct vm_run_image_params { vm_run_image_params(class CVmMainClientIfc *clientifc, class CVmHostIfc *hostifc, const char *image_file_name) { /* set the required parameters */ this->clientifc = clientifc; this->hostifc = hostifc; this->image_file_name = image_file_name; /* assume no program arguments */ prog_argv = 0; prog_argc = 0; /* assume no script or log files */ script_file = 0; log_file = 0; cmd_log_file = 0; script_quiet = FALSE; /* assume we'll use [More] mode for interactive output */ more_mode = TRUE; /* assume we're loading from a separate .t3 file */ load_from_exe = FALSE; /* assume we won't show the VM banner */ show_banner = FALSE; /* assume we will seed the RNG */ seed_rand = TRUE; /* use the default character sets from the system */ charset = 0; log_charset = 0; /* assume we're not loading a saved state file */ saved_state = 0; /* use default directories */ res_dir = 0; file_dir = 0; sandbox_dir = 0; /* assume no network configuration */ netconfig = 0; } /* * the client interface - this lets the host application environment * provide a custom implementation of the main console interface for * the game */ class CVmMainClientIfc *clientifc; /* * the host interface - this lets the host application environment * customize certain preference settings and data sources */ class CVmHostIfc *hostifc; /* the image file name */ const char *image_file_name; /* * The program command-line parameters - these are argv-style parameter * strings to pass to the .t3 program's main(). (Note that these * aren't the arguments to the interpreter - that's what this entire * structure is about, as it contains the pre-digested results of * parsing those parameters. These are instead unparsed parameters * that we're to pass along to the .t3 program.) */ const char *const *prog_argv; int prog_argc; /* * The input script file. If this is not null, the interpreter will * read console input from the given file and feed it to the .t3 * program via inputLine(), etc. If script_quiet is true, we'll read * this input silently, without echoing it or program output to the * console for the duration of the script input. */ const char *script_file; int script_quiet; /* * The log file. If this is not null, we'll log console output to the * given file. */ const char *log_file; /* * Command log file. If this is not null, we'll log input lines read * from the console to this file. */ const char *cmd_log_file; /* * In [More] mode, we pause and await a keystroke after each screenful * of text to give the user a chance to read text before it scrolls off * the screen. This is the default, but sometimes users will want to * run in batch/stdio mode, where there's no pausing. */ int more_mode; /* * Flag: load from the application executable file. If this is true, * the given image filename is actually the name of the native * application executable file that we're running (i.e., the main * program's argv[0] or equivalent), and it contains an embedded or * attached copy of the .t3 file, embedded via some OS-specific * mechanism. */ int load_from_exe; /* flag: show the VM version/copyright banner at startup */ int show_banner; /* * flag: seed the random number generator automatically at startup; * this uses some OS-specific source of true randomness to generate an * initial random state for the RNG */ int seed_rand; /* * Character set name for the main console. If this is not null, we'll * use this charater set for text displayed to and read from the * interactive console. */ const char *charset; /* log file character set (if not null) */ const char *log_charset; /* * Saved state file to restore on startup. If this is not null, we'll * restore this saved state file immediately after loading the .t3 * file. */ const char *saved_state; /* resource directory */ const char *res_dir; /* * Default working directory for File operations. If this is not null, * the File class will use this as the working directory for file * operations when file names are given as relative paths. If this is * null, the folder containing the .t3 file is the default. */ const char *file_dir; /* * Sandbox directory for file safety enforcement. If this is not null, * the File class uses this as the sandbox directory for file safety * purposes. If this is null, the default sandbox is the file_dir if * specified, otherwise the folder containing the .t3 file. */ const char *sandbox_dir; /* * Network configuration object. If not null, we're running in web * host mode, with the network parameters specified in this object. */ class TadsNetConfig *netconfig; }; /* * Execute an image file. We'll return zero on success, or a VM error code * on failure. * * If 'load_from_exe' is true, the image filename given is actually the * name of the native executable file that we're running, and we should * load the image file that's attached to the native executable file via * the system-specific os_exeseek() mechanism. * * If 'script_file' is not null, we'll read console input from the given * file. If 'log_file' is not null, we'll log console output to the given * file. If 'cmd_log_file' is not null, we'll log each line we read from * the console to the given command logging file. * * 'charset' optionally selects a character set to use for text displayed * to or read from the user interface. If this is null, we'll use the * current system character set as indicated by the osifc layer. 'charset' * should usually be null unless explicitly specified by the user. */ int vm_run_image(const vm_run_image_params *params); /* * Execute an image file using argc/argv conventions. We'll parse the * command line and invoke the program. * * The 'executable_name' is the name of the host program; this is used to * prepare "usage" messages. * * If 'defext' is true, we'll try adding a default extension ("t3", * formerly "t3x") to the name of the image file we find if the given * filename doesn't exist. We'll always check to see if the file exists * with the exact given name before we do this, so that we don't add an * extension where none is needed. If the caller doesn't want us to try * adding an extension at all, pass in 'defext' as false. * * If 'test_mode' is true, we'll make some small changes to the program * invocation protocol appropriate to running system tests. In * particular, we'll build the program argument list with only the root * name of the image file, not the full path - this allows the program to * display the argument list without any dependencies on local path name * conventions or the local directory structure, allowing for more easily * portable test scripts. * * If 'hostifc' is null, we'll provide our own default interface. The * caller can provide a custom host interface by passing in a non-null * 'hostifc' value. */ int vm_run_image_main(class CVmMainClientIfc *clientifc, const char *executable_name, int argc, char **argv, int defext, int test_mode, class CVmHostIfc *hostifc); /* * Show the interface report. This writes an XML listing of the * metaclasses and intrinsic function sets to the standard output. */ void vm_interface_report(class CVmMainClientIfc *cli, const char *fname); /* * Get the IFID from the GameInfo resource. This looks for the * gameinfo.txt resource in the image file or resource directory, then * scans the contents for its IFID field. If we find an IFID, we return an * allocated buffer containing the IFID string (or the first IFID string, * if the GameInfo contains multiple IFIDs); otherwise we return null. The * caller is responsible for freeing the returned buffer with * lib_free_str(). * * Note that the host interface's resource map for the image file is * generally initialized when the image file is loaded, so this routine * must be called AFTER the image file has been loaded through the regular * loader. If you want a more general-purpose GameInfo extractor that * doesn't rely on the interpreter's image loader, you should use the Babel * API tools, which parse the image file directly. This routine is * intended as a lightweight alternative to the Babel tools for use in the * interpreter, where we're certainly already going to invoke the full * image loader anyway. */ char *vm_get_ifid(class CVmHostIfc *hostifc); /* ------------------------------------------------------------------------ */ /* * VM Main client services interface. Callers of the vm_run_image * functions must provide an implementation of this interface. */ class CVmMainClientIfc { public: virtual ~CVmMainClientIfc() { } /* * Set "plain" mode. This should set the console to plain ASCII output * mode, if appropriate. Note that this can be called before * client_init(), and no globals are generally present at this point. * * In most cases, this can make a call to os_plain() to set the * OS-level console to plain mode. Non-console applications generally * need not do anything here at all. */ virtual void set_plain_mode() = 0; /* * Create the main system console, if desired. This is called during * VM initialization, so it is called prior to client_init(). Returns * the main console object, if desired. If no main console is desired * for this application, return null. */ virtual class CVmConsoleMain *create_console( struct vm_globals *globals) = 0; /* * Delete the console, if we created one. This is called during VM * termination, so it's called after client_terminate(). If * create_console() doesn't create a console, this routine need do * nothing. */ virtual void delete_console(struct vm_globals *globals, class CVmConsoleMain *console) = 0; /* * Initialization - we'll invoke this immediately after initializing * the VM (via vm_initialize), so the client can perform any global * initialization desired. The globals are valid at this point because * we have completed VM initialization. * * If script_file is non-null, it gives the name of a file to use as * the source of console input. The client implementation should set * up accordingly; if the standard console (G_console) is being used, * the client can simply use G_console->open_script_file() to set up * scripting. * * If log_file is non-null, it gives the name of a file to use to log * console output. The client shoudl set up logging; if the standard * console if being used, G_console->open_log_file() will do the trick. * * If cmd_log_file is non-null, it gives the name of a file to use to * log commands read from the input (i.e., only command input should be * logged, not other console output). If the standard console is being * used, G_console->open_command_log() will set things up properly. * * If banner_str is non-null, it gives a VM banner string that should * be displayed to the user. If the standard console is being used, * this can be displayed using G_console->format_text(). * * The parameters (script_file, log_file, cmd_log_file, and the * presence or absence of banner_str) are taken from the startup * parameters. For a command-line version, for example, these come * from command line options. So, these are necessarily passed down in * some form from the client to begin with; so a client that never * passes these to vm_run_image() or vm_run_image_main() doesn't need * to handle these parameters at all here. */ virtual void client_init(struct vm_globals *globals, const char *script_file, int script_quiet, const char *log_file, const char *cmd_log_file, const char *banner_str, int more_mode) = 0; /* * Termination - we'll invoke this immediately before terminating the * VM (via vm_terminate). Globals are still valid at this point, but * will be destroyed after this returns. */ virtual void client_terminate(struct vm_globals *globals) = 0; /* * pre-execution notification - we'll invoke this function just before * starting execution in the loaded image */ virtual void pre_exec(struct vm_globals *globals) = 0; /* * terminate - we'll invoke this just after execution in the loaded * image terminates */ virtual void post_exec(struct vm_globals *globals) = 0; /* * Terminate with error - we'll invoke this upon catching an * exception that the image file doesn't handle and which thus * terminates execution. Note that if this is called, post_exec() * will not be called; however, if post_exec() itself throws an * exception, we'll invoke this routine. */ virtual void post_exec_err(struct vm_globals *globals) = 0; /* * Display an error message. We'll call this with a complete error * message to display. Note that we won't add a newline at the end of * the message, so if the message is to be displayed on a stdio-style * terminal, this routine should display a newline after the message. * * If the implementation normally writes the text to the main output * console (G_console), it must take into the account the possibility * that we have not opened a system console at all (i.e., G_console * could be null), or have not allocated any globals at all (i.e., * 'globals' could be null). * * If 'add_blank_line' is true, the implementation should add a blank * line after the error, if appropriate for the display device. If * we're displaying the message in an alert box on a GUI, for example, * this can be ignored. */ virtual void display_error(struct vm_globals *globals, const struct CVmException *exc, const char *msg, int add_blank_line) = 0; }; /* ------------------------------------------------------------------------ */ /* * Very basic client interface implementation using stdio */ class CVmMainClientIfcStdio: public CVmMainClientIfc { public: virtual void set_plain_mode() { } virtual class CVmConsoleMain *create_console(struct vm_globals *) { return 0; } virtual void delete_console(struct vm_globals *, class CVmConsoleMain *) { } virtual void client_init(struct vm_globals *, const char *, int, const char *, const char *, const char *, int) { } virtual void client_terminate(struct vm_globals *) { } virtual void pre_exec(struct vm_globals *) { } virtual void post_exec(struct vm_globals *) { } virtual void post_exec_err(struct vm_globals *) { } virtual void display_error(struct vm_globals *, const struct CVmException *, const char *msg, int add_blank_line) { printf(add_blank_line ? "%s\n" : "%s", msg); } }; #endif /* VMMAIN_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmmaincn.h������������������������������������������������������������������������0000664�0000000�0000000�00000011633�12650170723�0015643�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmmaincn.h - VM main startup - console helper version Function Implements some helpers for the vmmain module and its users, for implementations using the standard system console (G_console and os_printz): - provides a CVmMainClientIfc implementation that writes errors to G_console (or os_printz, if no G_console is available), and ignores other notifications Notes Modified 04/05/02 MJRoberts - Creation */ #include "os.h" #include "t3std.h" #include "vmmain.h" #include "vmconsol.h" /* ------------------------------------------------------------------------ */ /* * Client services interface for T3 VM - console-based version */ class CVmMainClientConsole: public CVmMainClientIfc { public: /* set plain ASCII mode */ void set_plain_mode() { /* set plain mode in the OS-level console */ os_plain(); } /* create the console */ CVmConsoleMain *create_console(struct vm_globals *vmg) { /* set up for global access */ VMGLOB_PTR(vmg); /* create a standard console and return it */ return new CVmConsoleMain(vmg0_); } /* delete the console */ void delete_console(struct vm_globals *vmg, CVmConsoleMain *con) { /* set up for global access */ VMGLOB_PTR(vmg); /* flush any pending buffered output */ con->flush(vmg_ VM_NL_NONE); /* delete the output formatter */ con->delete_obj(vmg0_); } /* initialize */ void client_init(struct vm_globals *vmg, const char *script_file, int script_quiet, const char *log_file, const char *cmd_log_file, const char *banner_str, int more_mode) { /* set up for global access */ VMGLOB_PTR(vmg); /* set the [More] mode as desired */ G_console->set_more_state(more_mode); /* if we have a script file, set up script input on the console */ if (script_file != 0) { G_console->open_script_file( vmg_ script_file, script_quiet, FALSE); } /* if we have a log file, set up logging on the console */ if (log_file != 0) G_console->open_log_file(vmg_ log_file); /* set up command logging on the console if desired */ if (cmd_log_file != 0) G_console->open_command_log(vmg_ cmd_log_file, TRUE); /* tell the HTML renderer that we're a T3 caller */ G_console->format_text(vmg_ "<?T3>"); /* show the banner on the console, if desired */ if (banner_str != 0) { G_console->format_text(vmg_ banner_str); G_console->write_blank_line(vmg0_); } } /* terminate */ void client_terminate(struct vm_globals *) { } /* pre-execution initialization */ void pre_exec(struct vm_globals *) { } /* post-execution termination/error termination */ void post_exec(struct vm_globals *) { } void post_exec_err(struct vm_globals *) { } /* display an error */ void display_error(struct vm_globals *vmg, const struct CVmException *exc, const char *msg, int add_blank_line) { CVmConsole *con; size_t len; /* check for a newline at the end of the message */ int has_nl = ((len = strlen(msg)) != 0 && msg[len-1] == '\n'); /* set up for global access */ VMGLOB_PTR(vmg); /* if we have globals, get the console */ con = VMGLOB_IF_AVAIL(G_console); /* if we have a console, write to it */ if (con != 0) { int old_obey; /* flush any pending buffered output */ con->flush(vmg_ VM_NL_NONE); /* put the console in obey-whitespace mode for our message */ old_obey = con->set_obey_whitespace(TRUE); /* display the message on the console */ con->format_text(vmg_ msg); /* add a blank line if desired */ if (add_blank_line) con->write_blank_line(vmg0_); /* restore console mode */ con->set_obey_whitespace(old_obey); } else { /* display the error on the OS-level console */ os_printz(msg); /* add a blank line if desired */ if (add_blank_line) { /* add one newline */ os_printz("\n"); /* * if the message itself didn't end with a newline, add * another newline */ if (!has_nl) os_printz("\n"); } } } }; �����������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmmccore.h������������������������������������������������������������������������0000664�0000000�0000000�00000004274�12650170723�0015651�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMMCCORE.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmmccore.h - Core Metaclass Registrations Function Notes Modified 12/01/98 MJRoberts - Creation */ /* * NOTE - this file is INTENTIONALLY not protected against multiple * inclusion. Because of the funny business involved in buildling the * registration tables, we must include this file more than once in * different configurations. Therefore, there's no #ifndef * VMMCCORE_INCLUDED test at all in this file. */ /* ------------------------------------------------------------------------ */ /* * Before we begin, if we're building the registration table, redefine * the CENTRAL REGISTRATION BUILDER version of the metaclass * registration macro. We do this here rather than in vmmcreg.cpp to * make it easier to create separate versions of vmmcreg.cpp for * separate subsystems. This core file is always included, even in * special configurations, before any other metaclass headers. */ #ifdef VMMCCORE_BUILD_TABLE #ifdef VM_REGISTER_METACLASS #undef VM_REGISTER_METACLASS #endif #define VM_REGISTER_METACLASS(meta_class) \ { &meta_class::metaclass_reg_ }, #endif /* VMMCCORE_BUILD_TABLE */ /* ------------------------------------------------------------------------ */ /* * Now include each header for file the core metaclasses */ #include "vmobj.h" #include "vmtobj.h" #include "vmstr.h" #include "vmlst.h" #include "vmdict.h" #include "vmgram.h" #include "vmbignum.h" #include "vmintcls.h" #include "vmanonfn.h" #include "vmcoll.h" #include "vmiter.h" #include "vmvec.h" #include "vmlookup.h" #include "vmbytarr.h" #include "vmcset.h" #include "vmfilobj.h" #include "vmtmpfil.h" #include "vmfilnam.h" #include "vmpat.h" #include "vmstrcmp.h" #include "vmstrbuf.h" #include "vmdynfunc.h" #include "vmfref.h" #include "vmdate.h" #include "vmtzobj.h" /* if networking is enabled, include the networking metaclasses */ #ifdef TADSNET #include "vmmcnet.h" #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmmcreg.cpp�����������������������������������������������������������������������0000664�0000000�0000000�00000005374�12650170723�0016033�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMMCREG.CPP,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmmcreg.cpp - Metaclass Registry Function The registry provides a static constant global table of the metaclasses that are linked in to this VM implementation. This file is dependent on the host application environment configuration. This particular file builds a table for the base set of T3 VM metaclasses. Some host environments may define an extended set of metaclasses; if you're building a host system with added metaclasses, do the following: 1. Make a copy of this file (DO NOT MODIFY THE ORIGINAL). 2. Remove this file (and/or files mechanically derived from this file, such as the compiled object file) from your makefile, and add your modified version of this file (and/or attendant derived files) instead. 3. At EACH line marked with a "!!!" comment below, add a #include for each of your added metaclass header files. (You don't need to include the core metaclasses, such as list and string, since you should still include "vmmccore.h", which takes care of including the core files.) Notes Modified 12/01/98 MJRoberts - Creation */ #include "vmmcreg.h" // !!! INCLUDE HOST-SPECIFIC METACLASS HEADERS HERE (FIRST OF TWO) /* the global registry table */ vm_meta_reg_t G_meta_reg_table[] = { /* * enable table-building mode, and include the core metaclasses * common to all T3 VM implementations */ #define VMMCCORE_BUILD_TABLE #include "vmmccore.h" // !!! INCLUDE HOST-SPECIFIC METACLASS HEADERS HERE (SECOND OF TWO) /* * last entry in the table (this is only required because of the * trailing comma in the registration macro) */ { 0 } }; /* ------------------------------------------------------------------------ */ /* * Register the metaclasses */ void vm_register_metaclasses() { uint i; vm_meta_reg_t *entry; /* * run through the metaclass table and tell each metaclass its * registration table index */ for (i = 0, entry = G_meta_reg_table ; entry->meta != 0 ; ++i, ++entry) { /* * Call this entry's class method to set its registration index. * This is static information that never changes throughout the * program's execution - we simply establish the registration * table index for each metaclass once upon initialization. */ (*entry->meta)->set_metaclass_reg_index(i); } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmmcreg.h�������������������������������������������������������������������������0000664�0000000�0000000�00000004200�12650170723�0015463�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMMCREG.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmmcreg.h - Metaclass Registry definitions Function Notes Modified 12/01/98 MJRoberts - Creation */ #ifndef VMMCREG_H #define VMMCREG_H /* ------------------------------------------------------------------------ */ /* * Define the NORMAL version of the metaclass registration macro. This * version is used on all inclusions of metaclass header files in normal * contexts, and does nothing. * * This macro is redefined for SPECIAL versions, where metaclass header * files are included in order to build certain tables, such as the * central registration table. */ #define VM_REGISTER_METACLASS(metaclass) #include "t3std.h" #include "vmtype.h" #include "vmglob.h" /* * Include all of the core headers first, to ensure that we skip * everything but the registration macros on subsequent inclusions while * building the tables. (Since each header is protected against * multiple inclusion, except for the registration macros, we need only * include each file once up front to ensure that these definitions * won't show up again.) */ #include "vmmccore.h" /* ------------------------------------------------------------------------ */ /* * Metaclass registration entry. Each registered metaclass defines an * entry like this. */ struct vm_meta_reg_t { /* * The CVmMetaclass object that describes the metaclass */ class CVmMetaclass **meta; }; /* ------------------------------------------------------------------------ */ /* * Declare the global static table */ extern vm_meta_reg_t G_meta_reg_table[]; /* ------------------------------------------------------------------------ */ /* * Register metaclasses. This must be called during VM initialization * to assign each metaclass its registration index. */ void vm_register_metaclasses(); #endif /* VMMCREG_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmmersenne.h����������������������������������������������������������������������0000664�0000000�0000000�00000012644�12650170723�0016215�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* Copyright (c) 2012 by Michael J. Roberts. All Rights Reserved. */ /* Name vmmersenne.h - Mersenne twister random number generator definitions Function Notes Modified 02/28/12 MJRoberts - Creation */ #ifndef VMMERSENNE_H #define VMMERSENNE_H #include "t3std.h" /* * Mersenne Twister MT19937 algorithm */ class CVmMT19937 { public: CVmMT19937() { idx = 0; } /* seed from OS-generated random data */ void seed_random() { /* fill the mt[] array with random bytes */ os_gen_rand_bytes((unsigned char *)mt, sizeof(mt)); } /* * Seed from an integer value. This uses an LCG to extend the integer * to fill the array. This follows the reference implementation's * seeding mechanism, which lets us test against reference output. */ void seed(uint32_t s) { /* do a pass through the mt array with LCG values from the seed */ base_seed(s); /* * discard a few initial values to prime the pump - the MT * algorithm is considered slow at getting started (i.e., it needs * to run through a few iterations before it starts generating good * numbers) */ for (int i = 0 ; i < 37 ; ++i) rand(); } /* * Seed the generator from bytes. The seed can be any size; we'll use * a combination of hashing and LCG generation to populate the internal * state vector. */ void seed(const char *buf, size_t len) { /* * Use the mechanism from the reference implementation for seeding * from multiple values. First do a fixed seeding of the mt array * to fill it with nonzero bits. */ base_seed(19650218); /* treat the source as an array of int32s */ size_t icnt = (len + 3)/4; /* if there's a partial int32 at the end, figure its value */ uint32_t partial = 0; if (icnt*4 > len) { int mul = 1; for (size_t i = (len/4)*4 ; i < len ; ++i, mul *= 256) partial += buf[i]*mul; } /* * do a pass over the mt array, doing the sort of LCG * initialization we do with a single int seed, but folding each * seed value into the running LCG feedback register */ int i = 1, j = 0, k = NMT > icnt ? NMT : icnt; for ( ; k != 0 ; --k) { /* get the current seed (the last one might be partial) */ uint32_t seed = (j+1)*4 <= (int)len ? osrp4(buf + j*4) : partial; /* generate an LCG value mixing in this seed value */ mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 0x19660D)) + seed + j; /* on to the next mt index, wrapping at the end */ if (++i >= NMT) { mt[0] = mt[NMT-1]; i = 1; } /* on to the next seed index, wrapping at the end */ if (++j >= (int)icnt) j = 0; } /* make another pass over the mt array */ for (k = NMT-1 ; k != 0 ; --k) { mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 0x5D588B65)) - i; if (++i >= NMT) { mt[0] = mt[NMT-1]; i = 1; } } /* and finally, make sure the initial array isn't all zeros */ mt[0] = 0x80000000UL; /* flush out the first full mt array */ idx = 0; for (i = 0 ; i <= NMT ; ++i) rand(); } /* generate a random number in the full uint32_t range */ uint32_t rand() { /* if we're out of untempered numbers, generate the next set */ if (idx >= NMT) { generate_untempered(); idx = 0; } /* generate a tempered result */ uint32_t y = mt[idx]; y ^= (y >> 11); y ^= ((y << 7) & 0x9d2c5680); y ^= ((y << 15) & 0xefc60000); y ^= (y >> 18); /* on to the next index */ ++idx; /* return the result */ return y; } /* get the size of the serialized state vector */ size_t get_state_size() const { return 2 + NMT*4; } /* serialize the state into a byte buffer */ void get_state(char *buf) { oswp2(buf, (short)idx); for (int i = 0, dst = 2 ; i < NMT ; ++i, dst += 4) oswp4(buf + dst, mt[i]); } /* restore the state from a byte buffer */ void put_state(const char *buf) { idx = osrp2(buf) % NMT; for (int i = 0, src = 2 ; i < NMT ; ++i, src += 4) mt[i] = osrp4(buf + src); } protected: /* basic seed generator */ void base_seed(uint32_t s) { mt[0] = s; for (idx = 1 ; idx < NMT ; ++idx) mt[idx] = 0x6c078965 * (mt[idx-1] ^ (mt[idx-1] >> 30)) + idx; } /* generate a new set of 624 untempered numbers */ void generate_untempered() { for (int i = 0 ; i < NMT ; ++i) { uint32_t y = (mt[i] & 0x80000000) + (mt[(i+1) % NMT] & 0x7fffffff); mt[i] = mt[(i + 397) % NMT] ^ (y >> 1); if ((y & 1) != 0) mt[i] ^= 0x9908b0df; } } /* internal state vector */ static const int NMT = 624; uint32_t mt[NMT]; /* current element index */ int idx; }; #endif /* VMMERSENNE_H */ ��������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmmeta.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000052572�12650170723�0015666�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMMETA.CPP,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmmeta.cpp - metaclass dependency table Function Notes Modified 12/01/98 MJRoberts - Creation */ #include <stdlib.h> #include <string.h> #include "t3std.h" #include "vmtype.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmglob.h" #include "vmmeta.h" #include "vmobj.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmintcls.h" /* ------------------------------------------------------------------------ */ /* * Create the metaclass dependency table with a given number of initial * entries */ CVmMetaTable::CVmMetaTable(size_t init_entries) { vm_meta_reg_t *entry; uint cnt; uint i; /* allocate space for our entries */ if (init_entries != 0) { /* allocate the space */ table_ = (vm_meta_entry_t *) t3malloc(init_entries * sizeof(table_[0])); } else { /* we have no entries */ table_ = 0; } /* no entries are defined yet */ count_ = 0; /* remember the allocation size */ alloc_ = init_entries; /* count the number of registered metaclasses */ for (entry = G_meta_reg_table, cnt = 0 ; entry->meta != 0 ; ++entry, ++cnt) ; /* * allocate the reverse dependency table -- this table lets us get * the dependency index of a metaclass given its registration table * index */ reverse_map_ = (int *)t3malloc(cnt * sizeof(reverse_map_[0])); /* * set each element of the table to -1, since we have no dependency * table entries yet */ for (i = 0 ; i < cnt ; ++i) reverse_map_[i] = -1; } /* ------------------------------------------------------------------------ */ /* * Delete the table */ CVmMetaTable::~CVmMetaTable() { /* clear the table */ clear(); /* free the table, if we ever allocated one */ if (table_ != 0) t3free(table_); /* free the reverse map */ t3free(reverse_map_); } /* ------------------------------------------------------------------------ */ /* * Clear all entries from the table */ void CVmMetaTable::clear() { size_t i; /* delete all of the property tables in the entries */ for (i = 0 ; i < count_ ; ++i) table_[i].release_mem(); /* * Reset the entry counter. Note that this doesn't affect any * allocation; we keep a separate count of the number of table slots * we have allocated. Table slots don't have any additional * associated memory, so we don't need to worry about cleaning * anything up at this point. */ count_ = 0; } /* ------------------------------------------------------------------------ */ /* * Ensure that we have space for a given number of entries */ void CVmMetaTable::ensure_space(size_t entries, size_t increment) { /* if we don't have enough space, allocate more */ if (entries >= alloc_) { size_t new_size; /* increase the allocation size by the given increment */ alloc_ += increment; /* if it's still too small, bump it up to the required size */ if (alloc_ < entries) alloc_ = entries; /* compute the new size */ new_size = alloc_ * sizeof(table_[0]); /* * if we have a table already, reallocate it at the larger size; * otherwise, allocate a new table */ if (table_ != 0) table_ = (vm_meta_entry_t *)t3realloc(table_, new_size); else table_ = (vm_meta_entry_t *)t3malloc(new_size); } } /* ------------------------------------------------------------------------ */ /* * Add an entry to the table */ void CVmMetaTable::add_entry(const char *metaclass_id, size_t func_cnt, vm_prop_id_t min_prop, vm_prop_id_t max_prop) { vm_meta_reg_t *entry; uint idx; const char *vsn; size_t name_len; /* find the version suffix in the metaclass name, if any */ vsn = lib_find_vsn_suffix(metaclass_id, '/', "000000", &name_len); /* ensure we have space for one more entry */ ensure_space(count_ + 1, 5); /* look up the metaclass by name */ for (idx = 0, entry = G_meta_reg_table ; entry->meta != 0 ; ++entry, ++idx) { const char *entry_vsn; size_t entry_name_len; /* find the version number in this entry */ entry_vsn = lib_find_vsn_suffix((*entry->meta)->get_meta_name(), '/', "000000", &entry_name_len); /* see if this is a match */ if (name_len == entry_name_len && memcmp(metaclass_id, (*entry->meta)->get_meta_name(), name_len) == 0) { /* * make sure the version provided in the VM is at least as * high as the requested version */ if (strcmp(vsn, entry_vsn) > 0) { /* * throw the error; this is a version mismatch error, so * flag it as a version error and include the metaclass * dependency information */ err_throw_a(VMERR_METACLASS_TOO_OLD, 4, ERR_TYPE_TEXTCHAR, metaclass_id, ERR_TYPE_TEXTCHAR, entry_vsn, ERR_TYPE_METACLASS, metaclass_id, ERR_TYPE_VERSION_FLAG); } /* add this entry */ add_entry(metaclass_id, idx, func_cnt, min_prop, max_prop); /* we're done */ return; } } /* * We didn't find it. This is probably an out-of-date VM issue, so * flag it as a version error, and include the metaclass dependency * information. */ err_throw_a(VMERR_UNKNOWN_METACLASS, 3, ERR_TYPE_TEXTCHAR, metaclass_id, ERR_TYPE_METACLASS, metaclass_id, ERR_TYPE_VERSION_FLAG); } /* ------------------------------------------------------------------------ */ /* * Add an entry to the table for a given registration table index */ void CVmMetaTable::add_entry(const char *metaclass_id, uint idx, size_t func_cnt, vm_prop_id_t min_prop, vm_prop_id_t max_prop) { /* get the registration table entry from the index */ vm_meta_reg_t *entry = &G_meta_reg_table[idx]; /* remember the defining class object */ table_[count_].meta_ = *entry->meta; /* save the name */ table_[count_].set_meta_name(metaclass_id); /* calculate the number of entries in the table */ size_t prop_xlat_cnt; if (min_prop == VM_INVALID_PROP || max_prop == VM_INVALID_PROP) prop_xlat_cnt = 0; else prop_xlat_cnt = max_prop - min_prop + 1; /* set the property count */ table_[count_].min_prop_ = min_prop; table_[count_].prop_xlat_cnt_ = prop_xlat_cnt; /* set the function count */ table_[count_].func_xlat_cnt_ = func_cnt; /* we don't know the intrinsic class's representative object yet */ table_[count_].class_obj_ = VM_INVALID_OBJ; /* allocate space for the property table, if we have any translations */ if (prop_xlat_cnt != 0) { size_t siz; /* calculate the table size */ siz = prop_xlat_cnt * sizeof(table_[count_].prop_xlat_[0]); /* allocate the table */ table_[count_].prop_xlat_ = (unsigned short *)t3malloc(siz); /* * initialize each entry in the table to zero - this will ensure * that each entry that is never set to a valid function index * will properly yield function index zero, which is defined as * the "not found" invalid function index */ memset(table_[count_].prop_xlat_, 0, siz); } else { /* there are no properties to translate, so we don't need a table */ table_[count_].prop_xlat_ = 0; } /* allocate the function-to-property translation table */ if (func_cnt != 0) { size_t i; vm_prop_id_t *prop_ptr; /* allocate the table */ table_[count_].func_xlat_ = (vm_prop_id_t *)t3malloc(func_cnt * sizeof(vm_prop_id_t)); /* clear the table - mark each entry as an invalid property ID */ for (i = 0, prop_ptr = table_[count_].func_xlat_ ; i < func_cnt ; ++i, ++prop_ptr) *prop_ptr = VM_INVALID_PROP; } else { /* no properties, so we don't need a table */ table_[count_].func_xlat_ = 0; } /* * store the reverse mapping for this entry, so that we can find the * dependency entry given the registration index */ reverse_map_[idx] = count_; /* count the new entry */ ++count_; } /* ------------------------------------------------------------------------ */ /* * Add an entry to the dependency table only if it doesn't already * appear in the dependency table. We add the entry based on its * registration table index. */ void CVmMetaTable::add_entry_if_new(uint reg_table_idx, size_t func_cnt, vm_prop_id_t min_prop, vm_prop_id_t max_prop) { /* * if the entry already has a valid dependency table index, as * obtained from the reverse mapping, we need not add it again */ if (reverse_map_[reg_table_idx] != -1) return; /* add the entry */ add_entry((*G_meta_reg_table[reg_table_idx].meta)->get_meta_name(), reg_table_idx, func_cnt, min_prop, max_prop); } /* ------------------------------------------------------------------------ */ /* * Look up an entry by external ID */ vm_meta_entry_t *CVmMetaTable::get_entry_by_id(const char *id) const { /* find the version suffix in the metaclass name, if any */ size_t name_len; const char *vsn = lib_find_vsn_suffix(id, '/', "000000", &name_len); /* look up the metaclass by name */ size_t i; vm_meta_entry_t *entry; for (i = 0, entry = table_ ; i < count_ ; ++entry, ++i) { /* find the version number in this entry */ size_t entry_name_len; const char *entry_vsn = lib_find_vsn_suffix( entry->image_meta_name_, '/', "000000", &entry_name_len); /* see if this is a match */ if (name_len == entry_name_len && memcmp(id, entry->image_meta_name_, name_len) == 0) { /* * we found a match to the name; make sure the version is at * least as high as requested */ if (strcmp(vsn, entry_vsn) <= 0) { /* the loaded version is at least as new, so return it */ return entry; } else { /* it's too old, so it's effectively not available */ return 0; } } } /* didn't find a match */ return 0; } /* ------------------------------------------------------------------------ */ /* * Invoke the VM-stack-based constructor for the metaclass at the given * index */ vm_obj_id_t CVmMetaTable::create_from_stack(VMG_ const uchar **pc_ptr, uint idx, uint argc) { /* make sure the entry is defined */ if (idx >= count_) err_throw(VMERR_BAD_METACLASS_INDEX); /* invoke the appropriate constructor */ return table_[idx].meta_->create_from_stack(vmg_ pc_ptr, argc); } /* ------------------------------------------------------------------------ */ /* * Call a static property in the metaclass at the given index */ int CVmMetaTable::call_static_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint idx, uint *argc, vm_prop_id_t prop) { /* make sure the entry is defined */ if (idx >= count_) err_throw(VMERR_BAD_METACLASS_INDEX); /* invoke the appropriate static property evaluator */ return table_[idx].meta_->call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* ------------------------------------------------------------------------ */ /* * Create an object with the given ID and load the object from the image * file. */ void CVmMetaTable::create_from_image(VMG_ uint idx, vm_obj_id_t id, const char *ptr, size_t siz) { /* make sure the entry is defined */ if (idx >= count_) err_throw(VMERR_BAD_METACLASS_INDEX); /* create the object table entry in the memory manager */ G_obj_table->alloc_obj_with_id(id, TRUE); /* invoke the appropriate constructor */ table_[idx].meta_->create_for_image_load(vmg_ id); /* load the object */ vm_objp(vmg_ id)->load_from_image(vmg_ id, ptr, siz); } /* * Create an object of the given metaclass with the given ID, in * preparation for restoring the object's data from saved state * information. This doesn't fill in the object with the saved state * data, but merely creates the object. * * The caller is responsible for having allocated the object ID before * calling this function. */ void CVmMetaTable::create_for_restore(VMG_ uint idx, vm_obj_id_t id) { /* make sure the entry is defined in our table of metaclasses */ if (idx >= count_) err_throw(VMERR_BAD_METACLASS_INDEX); /* * Invoke the appropriate constructor. Note that the caller must * already have allocated the object ID, so we simply use the given ID * without further consideration. */ table_[idx].meta_->create_for_restore(vmg_ id); } /* ------------------------------------------------------------------------ */ /* * Write the table to a file. */ void CVmMetaTable::write_to_file(CVmFile *fp) { size_t i; /* write the number of entries */ fp->write_uint2(get_count()); /* write each entry */ for (i = 0 ; i < get_count() ; ++i) { const char *nm; const vm_meta_entry_t *entry; ushort j; /* get the entry */ entry = get_entry(i); /* get this entry's name */ nm = entry->image_meta_name_; /* write the length of the name, followed by the name */ fp->write_uint2(strlen(nm)); fp->write_bytes(nm, strlen(nm)); /* write our associated IntrinsicClass object's ID */ fp->write_uint4(entry->class_obj_); /* * Write the property table information - write the number of * function entries, and the minimum and maximum property ID's. */ fp->write_uint2(entry->func_xlat_cnt_); fp->write_uint2(entry->min_prop_); fp->write_uint2(entry->min_prop_ + entry->prop_xlat_cnt_); /* * Write out the property translation table. The function * translation table will always be smaller than (at worst, it * will be the same size as) the property translation table; * both tables contain the same information, so since we only * need to write out one or the other, write out the smaller of * the two. * * Note that xlat_func() requires a 1-based function index, so * run our counter from 1 to the function table count. */ for (j = 1 ; j <= entry->func_xlat_cnt_ ; ++j) fp->write_uint2(entry->xlat_func(j)); } } /* * Read the table from a file. */ int CVmMetaTable::read_from_file(CVmFile *fp) { size_t cnt; size_t i; /* clear the existing table */ clear(); /* read the number of entries */ cnt = fp->read_uint2(); /* read the entries */ for (i = 0 ; i < cnt ; ++i) { char buf[256]; size_t len; vm_prop_id_t min_prop; vm_prop_id_t max_prop; ushort func_cnt; ushort j; vm_meta_entry_t *entry; vm_obj_id_t class_obj; /* read the length of this entry, and make sure it's valid */ len = fp->read_uint2(); if (len > sizeof(buf) - 1) return VMERR_SAVED_META_TOO_LONG; /* read the name and null-terminate it */ fp->read_bytes(buf, len); buf[len] = '\0'; /* read the associated IntrinsicClass object */ class_obj = (vm_obj_id_t)fp->read_uint4(); /* read the property table description */ func_cnt = (ushort)fp->read_uint2(); min_prop = (vm_prop_id_t)fp->read_uint2(); max_prop = (vm_prop_id_t)fp->read_uint2(); /* add this entry */ add_entry(buf, func_cnt, min_prop, max_prop); /* get the new entry's record */ entry = get_entry(i); /* set the class ID */ entry->class_obj_ = class_obj; /* * Read the property mappings. We stored the function table * entries, so we simply need to load those entries. Note that * the function table has a 1-based index, so run our counter * from 1 to the function count. */ for (j = 1 ; j <= func_cnt ; ++j) entry->add_prop_xlat((short)fp->read_int2(), j); } /* success */ return 0; } #if 0 // moved inline, since it's small and used a lot /* * Get the function vector index for a property given the metaclass's * registration table index. */ int CVmMetaTable::prop_to_vector_idx(uint reg_table_idx, vm_prop_id_t prop) { /* get the entry for the registration table index */ vm_meta_entry_t *entry = get_entry_from_reg(reg_table_idx); /* * if there's no entry for this registration index, there's * obviously no metaclass function mapping for the property */ if (entry == 0) return 0; /* return the property translation */ return entry->xlat_prop(prop); } #endif /* * Create an IntrinsicClass object for each metaclass that doesn't * already have one. */ void CVmMetaTable::create_intrinsic_class_instances(VMG0_) { size_t idx; vm_meta_entry_t *entry; /* go through our table */ for (idx = 0, entry = table_ ; idx < count_ ; ++idx, ++entry) { /* if this entry has no associated class object, create one for it */ if (entry->class_obj_ == VM_INVALID_OBJ) { /* * create the class object - it will automatically register * itself to fill in the entry */ CVmObjClass::create_dyn(vmg_ idx); } /* * Add this object to the machine globals, if it's not a root * object. Any dynamically-created intrinsic class instance must * be made a machine global because it will always be reachable as * the class object for the metaclass, but the metclass won't trace * the object during garbage collection, so we have to set up a * global to make sure the gc knows it's always reachable. */ G_obj_table->add_to_globals(entry->class_obj_); } } /* * Forget the IntrinsicClass objects we created dynamically in * create_intrinsic_class_instances(). This simply drops our references * to those objects from the metaclass table. */ void CVmMetaTable::forget_intrinsic_class_instances(VMG0_) { size_t idx; vm_meta_entry_t *entry; /* go through our table and do class-level initializations */ for (idx = 0, entry = table_ ; idx < count_ ; ++idx, ++entry) entry->class_obj_ = VM_INVALID_OBJ; } /* ------------------------------------------------------------------------ */ /* * Build a translation table from run-time metaclass index to compiler * TC_META_xxx ID. */ tc_metaclass_t *CVmMetaTable::build_runtime_to_compiler_id_table(VMG0_) { vm_meta_reg_t *entry; int i, cnt; /* count the metaclass registration table size */ for (entry = G_meta_reg_table, cnt = 0 ; entry->meta != 0 ; ++entry, ++cnt) ; /* * allocate the translation table - this is simply an array indexed by * metaclass registration index and yielding the corresponding compiler * metaclass ID */ tc_metaclass_t *xlat = (tc_metaclass_t *)t3malloc(cnt * sizeof(xlat[0])); /* scan the registration table for compiler-recognized classes */ for (entry = G_meta_reg_table, i = 0 ; entry->meta != 0 ; ++entry, ++i) { /* get its name */ const char *nm = (*entry->meta)->get_meta_name(); /* scan for the version number suffix - we'll ignore it if found */ const char *p; for (p = nm ; *p != '\0' && *p != '/' ; ++p) ; /* note the length up to the version suffix delimiter */ size_t len = p - nm; /* check for the known names */ if (len == 11 && memcmp(nm, "tads-object", 11) == 0) xlat[i] = TC_META_TADSOBJ; else if (len == 11 && memcmp(nm, "dictionary2", 11) == 0) xlat[i] = TC_META_DICT; else if (len == 18 && memcmp(nm, "grammar-production", 18) == 0) xlat[i] = TC_META_GRAMPROD; else if (len == 13 && memcmp(nm, "int-class-mod", 13) == 0) xlat[i] = TC_META_ICMOD; else xlat[i] = TC_META_UNKNOWN; } /* return the translation table */ return xlat; } ��������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmmeta.h��������������������������������������������������������������������������0000664�0000000�0000000�00000034077�12650170723�0015333�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMMETA.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmmeta.h - metaclass dependency table Function The metaclass dependency table establishes the correspondence between metaclass index numbers and metaclasses. The load image file contains a list of the metaclasses that the program requires, identifying each required metaclass with its universally unique identifier string. At load time, we scan this list in the load image and create a dependency table. Notes Modified 12/01/98 MJRoberts - Creation */ #ifndef VMMETA_H #define VMMETA_H #include <stdlib.h> #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "tcprstyp.h" /* ------------------------------------------------------------------------ */ /* * Metaclass table entry. This contains information on the metaclass at * one index in the table. */ struct vm_meta_entry_t { /* the class descriptor object for the metaclass */ class CVmMetaclass *meta_; /* * name of metaclass as read from image file (this is important for * rewriting an image file during preinit, since it ensures that we * store only the version number required by the compiled image * file, not the version number actually implemented in the preinit * interpreter) */ char *image_meta_name_; /* * the IntrinsicClass object (a CVmObjClass object) that represents * this class */ vm_obj_id_t class_obj_; /* * Property translation list. The translation list is an array of * internal function numbers, indexed by property ID. However, not * all property ID's are necessarily represented in the index - * instead, the index contains entries only from a minimum property * ID, and only contains a specified number of entries. So, given * property ID 'prop_id', we find the internal function number for * the property at index (prop_id - min_prop_id) in the table. * * We use an unsigned short (16 bits) for each internal function * number - this limits the number of functions per metaclass to * 65535, which should be more than adequate while keeping memory * usage within reason. */ vm_prop_id_t min_prop_; size_t prop_xlat_cnt_; unsigned short *prop_xlat_; /* * Function table. This is the reverse of the property translation * list: this table gives the property ID for each of the metaclass * functions. The first entry is the property of function index 1, * the second entry is function index 2, and so on. */ vm_prop_id_t *func_xlat_; size_t func_xlat_cnt_; /* relese storage */ void release_mem() { /* if this entry has a property table, delete it */ if (prop_xlat_ != 0) { /* free it */ t3free(prop_xlat_); /* forget the pointer */ prop_xlat_ = 0; } /* if this entry has a function translation table, delete it */ if (func_xlat_ != 0) { /* free it */ t3free(func_xlat_); /* forget the pointer */ func_xlat_ = 0; } /* relesae our stored name */ lib_free_str(image_meta_name_); } /* store the metaclass name as loaded from the image file */ void set_meta_name(const char *nm) { /* allocate space and store the name */ image_meta_name_ = lib_copy_str(nm); } /* * Add a property to the table, given the property ID and the * 1-based index of the corresponding function in the metaclass's * internal function table. */ void add_prop_xlat(vm_prop_id_t prop, ushort func_idx) { /* * Add the translation entry from property ID to function index * - note that we store this as a 1-based function table index * value, because we reserve function index 0 to indicate that * the property is invalid and has no function translation */ prop_xlat_[prop - min_prop_] = func_idx; /* * Add the translation entry from function index to property ID. * Note that we must adjust the 1-based function index down one * to get the index into our internal array. */ func_xlat_[func_idx - 1] = prop; } /* * Get the translation for a given property ID. This returns zero * if the property does not map to a valid function index for the * metaclass, or the 1-based index of the function in the * metaclass's "vtable" if the property is defined. */ unsigned short xlat_prop(vm_prop_id_t prop) const { /* * Check to see if the property is represented in the table. If * it's not within the range of properties in the table, there * is no translation for the property. */ if (prop < min_prop_ || prop >= min_prop_ + prop_xlat_cnt_) { /* the property isn't in the table, so it has no translation */ return 0; } /* return the function index from the table */ return prop_xlat_[prop - min_prop_]; } /* * Translate a function index to a property ID. The function index * is given as a 1-based index, for consistency with the return * value from xlat_prop(). */ vm_prop_id_t xlat_func(unsigned short func_idx) const { /* * if it's zero, or it's greater than the number of functions in * our table, it has no property translation */ if (func_idx == 0 || func_idx > func_xlat_cnt_) return VM_INVALID_PROP; /* * return the entry from our table for the given index, adjusted * to a 1-based index value */ return func_xlat_[func_idx - 1]; } }; /* ------------------------------------------------------------------------ */ /* * The metaclass table. One global instance of this table is generally * created when the image file is loaded. */ class CVmMetaTable { public: /* * Create a table with a given number of initial entries. The table * may be expanded in the future if necessary, but if the caller can * predict the maximum number of entries required, we can * preallocate the table at its final size and thus avoid the * overhead and memory fragmentation of expanding the table. */ CVmMetaTable(size_t init_entries); /* delete the table */ ~CVmMetaTable(); /* clear all existing entries from the table */ void clear(); /* * Build a translation table from runtime metaclass index to the * compiler's internal TC_META_xxx scheme. The table is simply an * array of TC_META_xxx values indexed by metaclass index number. The * caller is responsible for freeing the returned memory via t3free() * when done with it. */ static tc_metaclass_t *build_runtime_to_compiler_id_table(VMG0_); /* * Add an entry to the table, given the metaclass identifier (a * string giving the universally unique name for the metaclass). * Fills in the next available slot. Throws an error if the * metaclass is not present in the system. * * If min_prop and max_prop are both VM_INVALID_PROP, the metaclass * has no properties in the translation table. */ void add_entry(const char *metaclass_id, size_t func_cnt, vm_prop_id_t min_prop, vm_prop_id_t max_prop); /* add an entry to the table given the registration table index */ void add_entry(const char *metaclass_id, uint idx, size_t func_cnt, vm_prop_id_t min_prop, vm_prop_id_t max_prop); /* * Add an entry to the table given the registration table index, but * only if the metaclass at this index isn't already defined in the * dependency table; if the metaclass is already in the dependency * table, this function has no effect. */ void add_entry_if_new(uint reg_table_idx, size_t func_cnt, vm_prop_id_t min_prop, vm_prop_id_t max_prop); /* * Get the dependency table index for a metaclass, given the * registration table index. If the metaclass is not present in the * metaclass table, we'll return -1. */ int get_dependency_index(uint reg_table_idx) const { return reverse_map_[reg_table_idx]; } /* * Get the entry for a given external metaclass ID. This returns the * table entry if the metaclass is loaded, null if not. If the name is * given with a "/version" suffix, we'll return null if the loaded * version is older than the specified version. */ vm_meta_entry_t *get_entry_by_id(const char *id) const; /* * get the depencency table entry for a metaclass, given the * registration table index */ vm_meta_entry_t *get_entry_from_reg(int reg_idx) { /* translate the registration table index into a dependency index */ int dep_idx = get_dependency_index(reg_idx); /* * if we have a valid dependency index, return it; otherwise, * return null */ if (dep_idx >= 0) return &table_[dep_idx]; else return 0; } /* * Given a registration table index for a metaclass and a property * ID, retrieve the metaclass function vector index for the property * according to the image file's property mapping for the metaclass. * Returns zero if the property is not in the mapping, 1 for the * first function in the vector, 2 for the second function, and so * on. * * Note that we use the registration table index, NOT the dependency * index, because this function is provided for use by the metaclass * itself, which only has convenient access to its registration * index. */ int prop_to_vector_idx(uint reg_table_idx, vm_prop_id_t prop) { /* get the entry for the registration table index */ vm_meta_entry_t *entry = get_entry_from_reg(reg_table_idx); /* * if there's no entry for this registration index, there's * obviously no metaclass function mapping for the property */ if (entry == 0) return 0; /* return the property translation */ return entry->xlat_prop(prop); } /* get the entry at a given dependency table index */ vm_meta_entry_t *get_entry(int idx) { return &table_[idx]; } /* get the total number of entries in the table */ size_t get_count() const { return count_; } /* * Invoke the VM-stack-based constructor for the metaclass at the * given index to create a new instance of that metaclass. Throws * an error if no such entry is defined. Returns the object ID of * the constructed object. */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint idx, uint argc); /* invoke a static property of a metaclass */ int call_static_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint idx, uint *argc, vm_prop_id_t prop); /* * Create an object with the given ID and load the object from the * image file. */ void create_from_image(VMG_ uint idx, vm_obj_id_t id, const char *ptr, size_t siz); /* * create an object in preparation for loading the object from a * saved state file */ void create_for_restore(VMG_ uint idx, vm_obj_id_t id); /* * write the dependency table to a file, for later reloading with * read_from_file() */ void write_to_file(class CVmFile *fp); /* * Read the dependency table from a file, as previously written by * write_to_file(). Returns zero on success, or an error code on * failure. We will entirely clear the current table before * loading, so the new table will replace the existing table. */ int read_from_file(class CVmFile *fp); /* rebuild the image file (for use after preinitialization) */ void rebuild_image(class CVmImageWriter *writer); /* * Create an IntrinsicClass object for each metaclass that doesn't * already have an IntrinsicClass object. This ensures that every * intrinsic class in use has an associated IntrinsicClass instance, * even if the compiler didn't generate one. */ void create_intrinsic_class_instances(VMG0_); /* forget IntrinsicClass objects we created at startup */ void forget_intrinsic_class_instances(VMG0_); private: /* * Ensure we have space for a given number of entries, allocating * more if necessary. If we must allocate more space, we'll * increase the current allocation size by at least the given * increment (more if necessary to bring it up to the required * size). */ void ensure_space(size_t entries, size_t increment); /* the table array */ vm_meta_entry_t *table_; /* * reverse dependency map - each entry in this table is the * dependency table index of the metaclass at the given registration * table index: * * reserve_map[registration_table_index] = dependency_table_index * * This information lets us determine if a given metaclass is in the * current dependency table at all, and if so what it's index is. * We store -1 in each entry that doesn't appear in the dependency * table. */ int *reverse_map_; /* number of entries defined in the table */ size_t count_; /* number of entries allocated for the table */ size_t alloc_; }; #endif /* VMMETA_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmnet.h���������������������������������������������������������������������������0000664�0000000�0000000�00000141160�12650170723�0015163�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmnet.h - TADS 3 networking Function Defines the networking layer for TADS 3. This is used for the web server configurations. This is portable code implementing high-level network operations, such as an HTTP server, using the low-level socket and thread API defined in osifcnet.h. Notes Modified 04/11/10 MJRoberts - Creation */ #ifndef VMNET_H #define VMNET_H /* include this module only if the networking subsystem is enabled */ #ifdef TADSNET #include <time.h> #include <wchar.h> #include "osifcnet.h" #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmerr.h" #include "vmdbg.h" #include "vmglob.h" #include "vmstrref.h" /* ------------------------------------------------------------------------ */ /* * Web server configuration. We create a global singleton of this * structure when the interpreter reads a web configuration file at * startup. */ class TadsNetConfig { public: TadsNetConfig() { first_var = 0; last_var = 0; } ~TadsNetConfig(); /* read a config file */ void read(osfildef *fp, class CVmMainClientIfc *clientifc); /* look up a configuration variable */ const char *get(const char *name) const; /* look up a configuration variable, applying a default if not defined */ const char *get(const char *name, const char *defval) { const char *val = get(name); return (val != 0 ? val : defval); } /* does the given configuration variable equal the given string? */ const int match(const char *name, const char *strval) { const char *val = get(name, ""); return strcmp(val, strval) == 0; } /* * Does the given configuration variable equal the given integer? If * the configuration variable isn't defined, returns false. Otherwise, * converts the configuration string value to an integer with atoi(), * and compares the result to the given value. */ const int match(const char *name, int intval) { const char *val = get(name); return (val != 0 ? atoi(val) == intval : FALSE); } /* set a variable */ class TadsNetConfigVar *set(const char *name, const char *val); protected: /* look up a variable */ class TadsNetConfigVar *getvar(const char *name) const; /* head/tail of list of variables */ class TadsNetConfigVar *first_var; class TadsNetConfigVar *last_var; }; /* variable entry in a TadsNetConfig */ class TadsNetConfigVar { public: TadsNetConfigVar(const char *name, const char *val) { this->name = lib_copy_str(name); this->val = lib_copy_str(val); this->nxt = 0; } ~TadsNetConfigVar() { lib_free_str(name); lib_free_str(val); } /* get the name/value */ const char *getname() const { return name; } const char *getval() const { return val; } /* change the value */ void setval(const char *val) { lib_free_str(this->val); this->val = lib_copy_str(val); } /* does our name match the given name? */ int matchname(const char *name) { return stricmp(name, this->name) == 0; } /* get/set the next item pointer */ TadsNetConfigVar *getnext() const { return nxt; } void setnext(TadsNetConfigVar *nxt) { this->nxt = nxt; } protected: char *name; char *val; TadsNetConfigVar *nxt; }; /* ------------------------------------------------------------------------ */ /* * Check a storage server API reply, and throw an error if appropriate. * Storage server API replies conventionally are plain text buffers of the * form: * * Code Message * * where 'Code' is an alphanumeric token giving an error code, and Message * is a human-readable result message. On success, the Code token is "OK"; * other token strings indicate errors. * * Functions that also return regular body data (such as GETFILE) return * the status message in an X-IFDBStorage-Status header instead of the * body. If the status header is present, we'll use that rather than * checking the reply body. * * If the HTML status, headers, or reply indicates an error, we'll throw a * storage server run-time error, so this won't return. On success, we * simply return. */ void vmnet_check_storagesrv_reply(VMG_ int htmlstat, CVmDataSource *reply, const char *headers); /* * Retrieve the storage server status from the reply. This returns an * allocated string buffer containing the status code as the first * space-delimited token, with a printable error message following. * * The status code is "OK" on success. If the request failed due to a * storage server error, the first token is a non-numeric error ID. If the * request failed due to an HTTP, the first token is the numeric HTTP * status. If the request failed due to a lower-level network error, the * code is a negative numeric code giving the internal OS_HttpClient ErrXxx * code. * * The caller must delete the returned buffer with t3free(). */ char *vmnet_get_storagesrv_stat(VMG_ int htmlstat, CVmDataSource *reply, const char *headers); /* * Check the reply code returned by vmnet_get_storagesrv_reply(). On * success, frees the status code buffer and returns. On failure, frees * the status code buffer and throws a StorageServerError exception. Note * that we free the buffer in either case. */ void vmnet_check_storagesrv_stat(VMG_ char *stat); /* ------------------------------------------------------------------------ */ /* * Master thread list. This is a portable helper object for keeping a list * of threads. The OS implementation can use this to maintain a list of * all of the threads the program has started. This is useful mainly so * that the program can wait for background threads to exit on their own * before terminating the overall process. It could also be useful for * debugging purposes, such as to display a list of active threads on the * console at various times. * * If the OS layer uses this object, OS_Thread should register each new * thread in its constructor, and unregister each outgoing thread in its * destructor. Doing so is optional - it's purely for the OS layer's * benefit, so if the OS layer doesn't need the master thread list * information, it can ignore this object. */ /* * thread list link */ struct TadsThreadLink { TadsThreadLink(OS_Thread *thread, TadsThreadLink *nxt) { /* save a weak reference to the thread */ this->thread = thread->get_weak_ref(); this->nxt = nxt; } ~TadsThreadLink() { thread->release_ref(); } OS_Thread *get_thread() { return (OS_Thread *)thread->ref(); } /* weak reference to our thread */ CVmWeakRef *thread; /* next link in list */ TadsThreadLink *nxt; }; /* * thread list */ class TadsThreadList: public CVmRefCntObj { public: TadsThreadList() { mu = new OS_Mutex(); head = 0; } ~TadsThreadList() { mu->release_ref(); } /* * add a thread to the master list - the OS_Thread constructor must * call this */ void add(OS_Thread *thread) { /* lock the list */ mu->lock(); /* link the thread into our list */ head = new TadsThreadLink(thread, head); /* done with the list lock */ mu->unlock(); } /* clean the list - clear dead threads */ void clean() { /* lock the list */ mu->lock(); /* * Unlink the thread from the list. It's possible that it's not in * the list any longer, since wait_all() removes threads before * waiting for them to exit. */ TadsThreadLink *cur, *prv, *nxt; for (prv = 0, cur = head ; cur != 0 ; cur = nxt) { /* remember the next thread */ nxt = cur->nxt; /* check to see if this thread is still alive */ if (cur->thread->test()) { /* it's still alive - keep it in the list */ prv = cur; } else { /* this thread is gone - remove the link */ if (prv != 0) prv->nxt = nxt; else head = nxt; /* delete the link */ delete cur; } } /* done with the list lock */ mu->unlock(); } /* * Wait for all threads to exit. The 'timeout' (given in milliseconds) * is applied to each individual thread. Returns true if all threads * exited successfully, false if one or more threads timed out. */ int wait_all(long timeout) { /* presume success */ int ok = TRUE; /* keep going until we're out of threads */ for (;;) { /* lock the list and pull off the first thread */ mu->lock(); TadsThreadLink *link = head; /* if we got an element, remove it from the list */ if (link != 0) { /* * Unlink it from the list. Note that our local variable * 't' has now assumed the reference count that the list * previously held on the thread. */ head = link->nxt; } /* done with the list lock */ mu->unlock(); /* if there are no more threads, we're done */ if (link == 0) break; /* if this thread is still alive, wait for it to exit */ OS_Thread *t = link->get_thread(); if (t != 0) { /* wait for this thread to exit */ if (t->wait(timeout) != OSWAIT_EVENT) { /* * the wait timed out or failed - note that we have at * least one thread that didn't exit successfully * within the timeout */ ok = FALSE; } /* we're done with the thread */ t->release_ref(); } /* delete the link */ delete link; } /* return the status result */ return ok; } protected: /* head of thread list */ TadsThreadLink *head; /* list mutex */ OS_Mutex *mu; }; /* ------------------------------------------------------------------------ */ /* * Network service registration. * * The usual way that a client finds a server on the Web is via an Internet * address and a "well-known" port number. For example, any time you * connect to a Web server, you generally connect to port 80, which is the * standard port for HTTP servers. * * TADS games DON'T use these standard well-known ports. This is an * important part of the design, because it allows many TADS games to run * on a single shared server. If each game tried to take over the same * well-known port for its network listener, you could only run one game * per machine (or per network adapter, anyway). Instead of using * well-known ports, then, TADS game servers use port numbers assigned * dynamically by the operating system. A game can't know its port number * until it's actually running. Therefore, we need some mechanism to * convey the port number to the client so that the client knows where to * connect. * * Registration is the solution. When a game starts a server, it registers * that server, by sending its IP address and listening port number to a * registration server. The registration server makes an entry in its * database of active servers, and then makes this information available to * clients through its own well-known port. The registration server CAN * use a well-known port because we only need one registration server per * machine. */ /* ------------------------------------------------------------------------ */ /* * Server manager. This is the global object that manages all of the * servers in the process. */ class TadsServerManager: public CVmRefCntObj { public: TadsServerManager(); ~TadsServerManager(); /* * Generate a random ID string - this can be used for anything * requiring a universally unique identifier, such as a session ID. * * 'obj' is a pointer to the object on whose behalf we're constructing * the string. This is used to contribute to the randomness of the * result, but can be null if no object is involved. * * This returns an allocated buffer, which the caller must free with * lib_free_str() when done with it. */ static char *gen_rand_id(void *obj); /* get a random number */ ulong rand(); protected: /* our ISAAC random number generator */ struct isaacctx *ic; /* resource protection mutex */ OS_Mutex *mutex; }; /* ------------------------------------------------------------------------ */ /* * Network listener. This object creates a thread that binds to a network * port and listens for incoming connections. */ class TadsListener: public CVmRefCntObj { public: /* * Create a web server listener. This binds to the given network port, * then launches the given listener thread to listen for new * connections on the port. */ static TadsListener *launch(const char *hostname, ushort portno, class TadsListenerThread *thread); /* * Shut down the listener. This sends a control message to the * listener thread telling it to shut down. The listener will close * its network port and exit its thread. * * This returns immediately without waiting for the listener to finish * shutting down. If you want to wait until the thread has exited, * wait for the thread object. */ void shutdown(); /* the listener thread object */ class TadsListenerThread *get_thread() const { return thread; } protected: /* * construction - this is protected because callers create this object * via the static method launch() */ TadsListener(TadsListenerThread *thread); /* deletion - protected because we're managed by reference counting */ ~TadsListener(); /* my thread object */ class TadsListenerThread *thread; }; /* * Listener thread. This is the generic class for listener threads; it * must be subclassed for each specific type of network object. */ class TadsListenerThread: public OS_Thread { friend class TadsListener; friend class TadsServerThread; public: /* construction */ TadsListenerThread(OS_Event *quit_evt) { /* no port yet */ port = 0; /* no error message yet */ errmsg = 0; /* * use the caller's quit event, if they supplied one, otherwise * create our own; if we create one, it's sticky - once signaled, * it stays signaled, no matter how many threads are waiting on it */ if (quit_evt == 0) this->quit_evt = new OS_Event(TRUE); else (this->quit_evt = quit_evt)->add_ref(); /* create our private shutdown event */ shutdown_evt = new OS_Event(TRUE); /* create our resource protection mutex */ mutex = new OS_Mutex(); /* we haven't started any server threads yet */ servers = 0; /* note the time we started running */ os_time(&start_time); /* start with thread #1 */ next_thread_id = 1; /* generate a random password */ password = TadsServerManager::gen_rand_id(this); } /* destruction */ ~TadsListenerThread(); /* * Create the server thread object. This must be subclassed for each * type of server to create the appropriate thread subclass. */ virtual class TadsServerThread *create_server_thread(OS_Socket *s) = 0; /* get the IP address and port number */ int get_local_addr(char *&ip, int &portno) const { return port != 0 ? port->get_local_addr(ip, portno) : 0; } /* set the listener port - we assume the caller's reference */ void set_port(OS_Listener *p) { port = p; } /* main thread entrypoint */ void thread_main(); /* * Check for an error message. If the server encounters an error it * can't recover from, it'll store a status message and exit the * listener thread. */ const char *get_errmsg() const { return errmsg; } /* generate a human-readable status report listing my threads */ void list_threads(struct NetString *buf); /* get the control password */ const char *get_password() const { return password; } /* get the start time as an ASCII string */ const char *asc_start_time() const { return asctime(os_localtime(&start_time)); } /* get the application-wide 'quit' event object */ OS_Event *get_quit_evt() const { return quit_evt; } /* get the listener shutdown event */ OS_Event *get_shutdown_evt() const { return shutdown_evt; } protected: /* add a thread to our list */ void add_thread(class TadsServerThread *t); /* remove a thread from our list */ void remove_thread(class TadsServerThread *t); /* time the server started running */ os_time_t start_time; /* * Password: this is a random string generated at startup, to secure * network access to control functions. The idea is that the server * displays this on its console, so only someone with console access to * the server will be able to also gain remote access to the server * control functions. */ char *password; /* next thread serial number */ int next_thread_id; /* our listener port object */ OS_Listener *port; /* * Application-wide quit event. This is the global event object that * signals application termination. We'll abort any blocking operation * when this event is signaled so that we can promptly terminate the * listener thread when the application is being terminated. */ OS_Event *quit_evt; /* * Listener shutdown event. This is the local event object that lets * our owner tell us to shut down the listener thread. This only * applies to this thread, not to the rest of the application, so it * can be used to selectively terminate this single listener while * leaving other server threads running. */ OS_Event *shutdown_evt; /* * error message - if the server encounters an error that it can't * recover from, it will store status information here and shut down */ char *errmsg; /* head of our list of active server threads */ class TadsServerThread *servers; /* mutex protecting our shared resources against concurrent access */ OS_Mutex *mutex; }; /* * Base class for server threasds. */ class TadsServerThread: public OS_Thread { friend class TadsListenerThread; public: TadsServerThread(TadsListenerThread *l, OS_Socket *s) { /* we take over the socket reference from the caller */ socket = s; /* remember the client's IP address */ socket->get_peer_addr(client_ip, client_port); /* remember the listener thread */ listener = l; l->add_ref(); /* create our resource protection mutex */ mutex = new OS_Mutex(); /* set the initial state string */ state = new StringRef("Initializing"); } ~TadsServerThread() { /* release our resources */ socket->release_ref(); listener->release_ref(); mutex->release_ref(); state->release_ref(); if (client_ip != 0) t3free(client_ip); } /* get the client IP address */ const char *get_client_ip() const { return client_ip; } int get_client_port() const { return client_port; } /* main thread entrypoint */ void thread_main(); /* * Process a request. The main server loop simply calls this * repeatedly as long as it returns true. This should read a request * from the socket and send the response, then return a success or * failure indication to the caller. Returns true on success, false if * an unrecoverable error occurs. A false return tells the server loop * to close the socket and terminate the thread. */ virtual int process_request() = 0; /* * Read data from our peer. This blocks until at least one byte is * available, then returns as much data as can be read without * blocking. On success, returns the number of bytes read. On error, * returns -1. * * If 'buf' is null, we'll read and discard data until we've read * 'buflen' bytes. * * 'minlen' gives the minimum number of bytes desired. This can be * less than the buffer length, since we might not know in advance how * many bytes the client will be sending. We'll read up to the buffer * size, but we won't return until we read the minimum size (or * encounter an error or timeout). */ long read(char *buf, size_t buflen, long minlen, unsigned long timeout = OS_FOREVER); /* * Send data to our peer. This blocks until the request completes, but * we'll abort immediately if the 'quit' event in our listener fires. * Returns true on success, false on error or a 'quit' signal. */ int send(const char *buf, size_t len); int send(const char *buf) { return send(buf, strlen(buf)); } /* get the last send/receive error from the socket */ int last_error() const { return socket->last_error(); } /* * Get the state string. The caller must release the reference when * done with it. */ StringRef *get_state() const { /* make sure no one changes it under us while we're working */ mutex->lock(); /* make a copy for the caller */ StringRef *s = state; s->add_ref(); /* our copy is safe now */ mutex->unlock(); /* return the caller's copy */ return s; } /* set the current run state */ void set_run_state(const char *s) { /* set up a StringRef for the string */ StringRef *r = new StringRef(s); /* set it as the new state */ set_run_state(r); /* done with our reference to the new StringRef */ r->release_ref(); } void set_run_state(StringRef *s) { /* lock against concurrent access while working */ mutex->lock(); /* release the old state string, and save the new one */ if (state != 0) state->release_ref(); if ((state = s) != 0) s->add_ref(); /* done with the mutex */ mutex->unlock(); } /* close my socket */ void close_socket() { if (socket != 0) socket->close(); } protected: /* * Thread ID - this is a serial number set by the listener when we * start up, used for human-readable identification. It has no other * significance; in particular, it's NOT an operating system thread * handle or ID. */ int thread_id; /* * our connection socket - this is the two-way network channel we use * to communicate with our client */ OS_Socket *socket; /* our listener */ TadsListenerThread *listener; /* next thread in our listener's list of threads */ TadsServerThread *next_server; /* resource protection mutex */ OS_Mutex *mutex; /* current run state message */ StringRef *state; /* client IP address and port */ char *client_ip; int client_port; }; /* ------------------------------------------------------------------------ */ /* * HTTP server thread */ class TadsHttpServerThread: public TadsServerThread { public: /* construction */ TadsHttpServerThread(class TadsHttpListenerThread *l, TadsMessageQueue *q, long ulim, OS_Socket *s); /* deletion */ ~TadsHttpServerThread(); /* get my listener thread object */ TadsHttpListenerThread *get_listener() const { return (TadsHttpListenerThread *)listener; } /* process a request */ int process_request(); /* * Send a simple HTTP response. Returns true on success, false on * error. The status code and mime type strings are required. The * message body is optional; if it's null, we'll simply send the * headers with no body. If there is a message body, we'll send it * with a Content-Length header. The extra headers are optional; if * provided, this must be in standard header format, with each header * (INCLUDING the last one) terminated by a CR-LF (\r\n) sequence. If * this is null we'll only include a set of standard headers describing * the message body (content-type, content-length, cache-control, * connection). */ int send_simple(const char *status_code, const char *mime_type, const char *msg_body, size_t msg_len, const char *extra_headers); /* send a simple message body */ int send_simple(const char *status_code, const char *mime_type, const char *msg_body) { return send_simple(status_code, mime_type, msg_body, strlen(msg_body), 0); } /* send the contents of a file as the message body */ int send_file(const char *fname, const char *mime_type); protected: /* * Read until we reach the desired number of newlines. The states are * 0->base, 1->first CR, 2->first LF, 3->second CR, 4->second LF. To * stop after one contiguous newline, stop at state 2; to stop after * two newlines, stop at state 4. */ int read_to_nl(StringRef *dst, long startofs, int init_state, int end_state); /* the message queue */ TadsMessageQueue *queue; /* upload limit */ long upload_limit; }; /* ------------------------------------------------------------------------ */ /* * HTTP server listener */ class TadsHttpListenerThread: public TadsListenerThread { public: TadsHttpListenerThread(vm_obj_id_t srv_obj, class TadsMessageQueue *q, long upload_limit); /* our associated server thread class is TadsHttpServer */ virtual class TadsServerThread *create_server_thread(OS_Socket *s) { return new TadsHttpServerThread(this, queue, upload_limit, s); } /* * Get the HTTPServer object that created the listener. * * For thread safety, this routine should only be called from the main * VM thread. The garbage collector can delete the server object and * thus invalidate the value returned here. The GC runs in the main VM * thread, so it's always safe to interrogate this value from the main * thread. */ vm_obj_id_t get_server_obj() const { return srv_obj; } /* * Detach from the HTTPServer object. The HTTPServer calls this when * it's about to be deleted by the garbage collector. We have a * reference on the HTTPServer object, but we're not ourselves a * garbage-collected object, so our reference won't keep the HTTPServer * alive. It can thus be collected while we're still pointing to it. * To deal with this, the HTTPServer lets us know when it's about to be * deleted, so that we can clear our reference. * * This routine should only be called from the main VM thread. This is * designed to be called from the HTTPServer object's notify_delete() * method, which is called by the garbage collector, which always runs * in the main VM thread. */ void detach_server_obj() { srv_obj = VM_INVALID_OBJ; } protected: ~TadsHttpListenerThread(); /* the message queue we use to handle incoming requests */ class TadsMessageQueue *queue; /* upload limit for each incoming request */ long upload_limit; /* the HTTPServer object that owns the listener */ vm_obj_id_t srv_obj; }; /* ------------------------------------------------------------------------ */ /* * XML helper class */ class TadsXml { public: /* given an XML buffer, find the end of the <?XML?> prefix */ const char *strip_xml_header(const char *buf); }; /* ------------------------------------------------------------------------ */ /* * Message queue. Server threads handle requests by sending messages to * the main thread, via the message queue. This approach allows the * network connections to be handled by dedicated threads, while keeping * the byte-code program itself single-threaded. The queue handles the * communications between threads, and also serializes requests so that * they can be serviced by a single thread. */ /* * Base message object */ class TadsMessage: public CVmRefCntObj { public: TadsMessage(const char *typ, OS_Event *quit_evt) { /* remember the message type */ this->typ = typ; /* we're not in a queue yet */ nxt = 0; /* set up an event to signal completion of the message */ ev = new OS_Event(FALSE); completed = FALSE; /* remember the quit event */ if ((this->quit_evt = quit_evt) != 0) quit_evt->add_ref(); } virtual ~TadsMessage() { /* destroy our completion event */ ev->release_ref(); /* forget our quit event */ if (quit_evt != 0) quit_evt->release_ref(); } /* * wait for completion: returns true on success, false if the wait * failed (due to error, timeout, or Quit) */ int wait_for_completion(unsigned long timeout) { /* wait on the event, or the quit event */ OS_Waitable *w[] = { ev, quit_evt }; return OS_Waitable::multi_wait(quit_evt != 0 ? 2 : 1, w, timeout) == OSWAIT_EVENT + 0; } /* mark the message as completed */ void complete() { /* set the completion event */ ev->signal(); /* flag it internally */ completed = TRUE; } /* message type - use the name of the class */ const char *typ; /* next message in queue */ TadsMessage *nxt; /* * completion event - this is signaled when the message has been * processed by the recipient, and if a reply is expected, the reply is * available */ OS_Event *ev; /* * the 'quit' event - our owner signals this event to tell us to shut * down the server without doing any more processing */ OS_Event *quit_evt; /* flag: has completion been signaled yet? */ int completed; }; /* * Safely cast an event message */ #define cast_tads_message(cls, msg) \ (msg != 0 && strcmp((msg)->typ, #cls) == 0 ? (cls *)msg : 0) /* * Network Event message. This is the base class for messages used within * the TADS 3 getNetEvent() queue for sending messages to the byte-code * program. */ class TadsEventMessage: public TadsMessage { public: TadsEventMessage(OS_Event *quit_evt) : TadsMessage("TadsEventMessage", quit_evt) { } /* * Set up the event object for the byte-code program. This pushes the * constructor arguments onto the run-time stack, and returns the * appropriate NetEvent subclass object ID (from G_predef). * * '*argc' is to be filled in with the number of constructor arguments * pushed. Note that the caller will push one additional constructor * argument giving the request type code (which is common to all * NetEvent subclass constructors), so this routine doesn't have to * push that value. * * '*evt_code' is to be filled in with the event type code. This is an * integer giving the type of the event, as defined in the TADS header * include/tadsnet.h - see the NetEvXxx code list. * * Each server type must subclass this to generate the suitable * information for the request. */ virtual vm_obj_id_t prep_event_obj(VMG_ int *argc, int *evt_code) = 0; }; /* * Message queue. */ class TadsMessageQueue: public CVmRefCntObj { public: TadsMessageQueue(OS_Event *quit_evt) { /* create our mutex object */ mu = new OS_Mutex(); /* create our event - this signals when a message arrives */ ev = new OS_Event(FALSE); /* no messages in the queue yet */ head = tail = 0; /* remember my global quit event */ if ((this->quit_evt = quit_evt) != 0) quit_evt->add_ref(); } /* are we in the process of shutting down the server? */ int is_quitting() { return quit_evt != 0 && quit_evt->test(); } /* * Add a message to the queue, without waiting for completion. This is * a one-way send: posting effectively transfers ownership of the * reference on the message to the queue, and thence to the recipient. * The caller thus can't access the message after posting it - for * their purposes it's effectively gone after they send it. If the * caller does want to hang onto its own reference to the message after * sending it, just use add_ref() on the message (BEFORE posting, of * course, in keeping with the usual ref-count transaction rules). */ void post(TadsMessage *m) { /* synchronize on the mutex */ mu->lock(); /* link the message at the end of the queue */ if (tail != 0) tail->nxt = m; else head = m; tail = m; m->nxt = 0; /* * if the message doesn't have a 'quit' event, and we do, copy our * quit event to the message */ if (m->quit_evt == 0 && quit_evt != 0) (m->quit_evt = quit_evt)->add_ref(); /* signal that a message is available in the queue */ ev->signal(); /* done with the mutex */ mu->unlock(); } /* * Send a message: post it to the queue and wait for completion. * Returns true on success, false if the wait failed (due to error or * timeout). * * Unlike post(), the caller explicitly keeps its own reference to the * message with send(), because the message object also provides * storage for the reply (if any). */ int send(TadsMessage *m, unsigned long timeout) { /* * Add a reference to the message on behalf of our caller. The * caller's original reference is about to be transfered to the * queue (and later to the recipient) via post(), but the caller * also wants to hang on to the message, so it needs a second * reference for its own copy. */ m->add_ref(); /* post the message */ post(m); /* wait for completion */ return m->wait_for_completion(timeout); } /* * Wait for a message. Blocks until a message is available in the * queue, or the timeout expires. If there's a message, removes the * message from the queue and returns OSWAIT_EVENT. Otherwise, returns * another OSWAIT_xxx code indicating what happened. * * The message queue has a 'quit' event object that's used to signal a * general shutdown. If this event fires, we'll return OSWAIT_EVENT+1 * and fill in *msgp with null. * * The message queue also has a 'debug break' event that's used to * signal that the user wants to pause execution and break into the * debugger. If this event is signaled, we'll return OSWAIT_EVENT+2 * and fill in *msgp with null. * * If we return a message, the caller takes over our reference on it. * The caller is thus responsible for calling release_ref() when done * with the message object. */ int wait(VMG_ unsigned long timeout, TadsMessage **msgp); /* * Pull the next message out of the queue without waiting. The caller * assumes our reference on the message, so they must call Release() * when done with it. */ TadsMessage *get() { /* synchronize on the mutex */ mu->lock(); /* stash the first message for returning to the caller */ TadsMessage *m = head; /* pull it off the queue (if there's a message at all) */ if (m != 0) { head = head->nxt; if (head == 0) tail = 0; } /* done with the mutex */ mu->unlock(); /* return the message */ return m; } /* * Abandon a message: release any references the queue has on the given * message. This can be used if the sender times out waiting for a * response, to ensure that the queue won't keep the message in memory. */ void abandon(TadsMessage *msg) { /* lock the queue while working */ mu->lock(); /* scan the queue for this message */ TadsMessage *cur, *prv; for (cur = head, prv = 0 ; cur != 0 ; prv = cur, cur = cur->nxt) { /* if this is the message we're looking for, unlink it */ if (cur == msg) { /* unlink the message */ if (prv != 0) prv->nxt = cur->nxt; else head = cur->nxt; /* if it's the tail, move the tail back one */ if (cur == tail) tail = prv; /* release our reference on the message */ cur->release_ref(); /* no need to keep looking */ break; } } /* done with the queue lock */ mu->unlock(); } /* * Flush the queue: discard any messages in our list. */ void flush() { /* keep going until the queue is empty */ for (;;) { /* lock the queue, and pull the first element off the list */ mu->lock(); TadsMessage *m = head; /* if we got an element, unlink it */ if (m != 0) { head = m->nxt; if (head == 0) tail = 0; } /* unlock the queue */ mu->unlock(); /* if we're out of queue elements, we're done */ if (m == 0) break; /* release this message */ m->release_ref(); } } /* get my message arrival event */ OS_Event *get_event_obj() { return ev; } /* get my quit event object */ OS_Event *get_quit_evt() { return quit_evt; } protected: /* mutex for access to our statics */ OS_Mutex *mu; /* event for signaling a message arrival */ OS_Event *ev; /* * the server's general 'quit' event - the server signals this event to * tell message queues to shut down without further processing */ OS_Event *quit_evt; /* head/tail of message queue */ TadsMessage *head, *tail; ~TadsMessageQueue() { /* discard any messages still in the queue */ flush(); /* destroy our synchronization resources */ mu->release_ref(); ev->release_ref(); if (quit_evt != 0) quit_evt->release_ref(); } }; /* ------------------------------------------------------------------------ */ /* * HTTP request header. */ struct TadsHttpRequestHeader { /* * Initialize given the start of a header line; returns a pointer to * the start of the next header line. This modifies the buffer in * place: we add a null at the end of the header name, and we add a * null at the newline at the end of the header. */ TadsHttpRequestHeader(char *&p, int parse) { /* no next item in the list yet */ nxt = 0; /* the name is always at the start of the line */ name = p; /* presume we won't find a value - set it to blank */ value = ""; /* parse out the name and value, if desired */ if (parse) { /* * find the end of the header name - it ends at the first * whitespace or colon character */ for ( ; *p != '\0' && strchr(":\r\n \t", *p) == 0 ; ++p) ; /* check what we found */ if (*p == ':') { /* found the delimiting colon - null it out and skip spaces */ for (*p++ = '\0' ; isspace(*p) ; ++p) ; /* the value is the rest of the line */ value = p; } else if (isspace(*p)) { /* stopped at a space - null it out and skip any more spaces */ for (*p++ = '\0' ; isspace(*p) ; ++p) ; /* make sure we're at the colon */ if (*p == ':') { /* skip the colon and any subsequent spaces */ for (++p ; isspace(*p) ; ++p) ; /* the value starts here */ value = p; } } } /* find the end of the line */ for ( ; *p != '\0' && (*p != '\r' || *(p+1) != '\n') ; ++p) ; /* if we found the CR-LF, null-terminate and skip the pair */ if (*p == '\r' && *(p+1) == '\n') { *p = '\0'; p += 2; } } ~TadsHttpRequestHeader() { /* delete the rest of the list */ if (nxt != 0) delete nxt; } /* * Simplify multi-line headers into the single-line equivalents */ static void fix_multiline_headers(StringRef *hdrs, int start_ofs) { /* * Look for CR LF <space>+ <^space> sequences, and delete the line * breaks in each one. */ char *p, *dst, *endp = hdrs->getend(); for (dst = p = hdrs->get() + start_ofs ; p < endp ; ) { /* check for a line break */ if (*p == '\r' && *(p+1) == '\n') { /* * if the next line starts with whitespace, it's a * continuation line; otherwise it's a new header */ if (isspace(*(p+2) && *(p+2) != '\r' && *(p+2) != '\n')) { /* * it's a continuation line - replace the CR-LF with a * single space, then skip all of the whitespace at the * start of this line */ *dst++ = ' '; for (p += 2 ; isspace(*p) && *p != '\r' && *p != '\n' ; ++p) ; } else { /* it's not a continuation line, so copy it as-is */ *dst++ = *p++; *dst++ = *p++; } } else if (*p == '\r' || *p == '\n') { /* unpaired CR or LF - convert it to a space */ *dst++ = ' '; ++p; } else { /* ordinary character - copy it unchanged */ *dst++ = *p++; } } /* set the new size of the headers */ hdrs->truncate(dst - hdrs->get()); } /* parse headers from a StringRef starting at the given offset */ static void parse_headers(TadsHttpRequestHeader *&hdr_list, TadsHttpRequestHeader *&hdr_tail, int parse_first, StringRef *str, long ofs) { /* fix multiline headers */ fix_multiline_headers(str, ofs); /* set up at the starting offset */ char *p = str->get() + ofs; /* if we're already at a blank line, there are no headers */ if (*p == '\0' || *p == '\r' || *p == '\n') return; /* * if there aren't any headers yet, parse the first one; otherwise * we'll just add to the existing list */ if (hdr_list == 0) hdr_list = hdr_tail = new TadsHttpRequestHeader(p, parse_first); /* now parse until we find a blank line */ while (*p != '\r' && *p != '\n' && *p != '\0') hdr_tail = hdr_tail->nxt = new TadsHttpRequestHeader(p, TRUE); } /* find a header in the list following this item */ const char *find(const char *name) { /* scan the list, starting with this element */ for (TadsHttpRequestHeader *h = this ; h != 0 ; h = h->nxt) { /* if this name matches, return the value */ if (stricmp(h->name, name) == 0) return h->value; } /* didn't find a match */ return 0; } /* name - pointer into a buffer owned by the request object */ const char *name; /* value - pointer into a buffer owned by the request object */ const char *value; /* next header in list */ TadsHttpRequestHeader *nxt; }; /* ------------------------------------------------------------------------ */ /* * HTTP Request Message. */ class TadsHttpRequest: public TadsEventMessage { public: TadsHttpRequest(TadsHttpServerThread *t, const char *verb, size_t verb_len, StringRef *hdrs, TadsHttpRequestHeader *hdr_list, StringRef *body, int overflow, const char *resource_name, size_t res_name_len, OS_Event *quit_evt) : TadsEventMessage(quit_evt) { /* remember the server thread */ if ((this->thread = t) != 0) t->add_ref(); /* remember the verb and resource name */ this->verb = new StringRef(verb, verb_len); this->resource_name = new StringRef(resource_name, res_name_len); /* keep a copy of the headers */ this->headers = hdrs; hdrs->add_ref(); /* keep a copy of the body */ this->overflow = overflow; if ((this->body = body) != 0) body->add_ref(); /* take ownership of the parsed header list */ this->hdr_list = hdr_list; } ~TadsHttpRequest() { /* done with our thread and resource name string references */ resource_name->release_ref(); verb->release_ref(); thread->release_ref(); headers->release_ref(); delete hdr_list; if (body != 0) body->release_ref(); } /* mark the request as completed */ void complete(); /* * Prepare the event object. This creates an HTTPRequest object (the * intrinsic class representing an incoming HTTP request), and sets up * a NetRequestEvent (the byte-code object representing a request) to * wrap it. */ virtual vm_obj_id_t prep_event_obj(VMG_ int *argc, int *evt_type); /* server thread */ TadsHttpServerThread *thread; /* the HTTP verb */ StringRef *verb; /* the request string */ StringRef *resource_name; /* buffer containing the headers, parsed into null-delimited strings */ StringRef *headers; /* list of starts of the headers in the buffer */ TadsHttpRequestHeader *hdr_list; /* message body, if any */ StringRef *body; /* * Flag: the message body exceeded the upload size limit. If this is * set, body is null, since we discard content that's too large. */ int overflow; }; /* ------------------------------------------------------------------------ */ /* * UI Close Event. In the stand-alone local interpreter configuration, * where the browser UI is an integrated part of the interpreter, this * event is sent when the user explicitly closes the UI window. In most * cases, the game will simply want to terminate when this happens, because * it indicates that the user has effectively dismissed the application. */ class TadsUICloseEvent: public TadsEventMessage { public: TadsUICloseEvent(OS_Event *quit_evt) : TadsEventMessage(quit_evt) { } virtual vm_obj_id_t prep_event_obj(VMG_ int *argc, int *evt_type); }; #endif /* TADSNET */ #endif /* VMNET_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmnetfil.h������������������������������������������������������������������������0000664�0000000�0000000�00000037110�12650170723�0015655�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmnetfil.h - network file interface Function This module was added in 3.1 to manage access to files stored on a network storage server when the game is running in web server mode. In this mode, files are not stored on the same machine as the game server, nor on the user's client PC, but on a third machine known as the storage server. The purpose of separating the game (execution) server and the storage server is that it allows execution servers to be interchangeable, as they don't store any state. This lets us launch a game on any available server to distribute execution load. The execution location is transparent to the user since all execution servers access the same storage pool. Access to remote files is handled by local caching. When we open a file for reading, we download a temporary copy from the storage server via HTTP, and perform the local file operations on the temp copy. If the file is opened for writing, we upload our temporary copy to the storage server on close, again via HTTP. The interface is implemented in two versions. The Network version senses at run-time whether or not we're in web server mode; if so, it does the HTTP GET/PUT operations to download and upload the temp files, and if not it just opens files in the local file system. The Local version can be used in builds that don't include the networking functionality at all; this version unconditionally maps to local files, and is a very thin layer over the regular file handling. Use the TADSNET macro to control the build configuration. If the macro is defined, the network run-time checking is included in the build. If not, the local-only version is selected, in which case the CVmNetFile object is basically a no-op that just passes through the local filename. Notes Modified 09/08/10 MJRoberts - Creation */ #ifndef VMNETFIL_H #define VMNETFIL_H #include "t3std.h" #include "vmtype.h" #include "os.h" /* ------------------------------------------------------------------------ */ /* * File modes for CVmNetFile::open() */ #define NETF_READ 0x0001 /* open with read access */ #define NETF_WRITE 0x0002 /* open with write access */ #define NETF_CREATE 0x0004 /* create the file if it doesn't exist */ #define NETF_TRUNC 0x0008 /* discard existing contents if file exists */ #define NETF_DELETE 0x0010 /* no content access; delete the file */ /* composite flags for generic new file creation (create or replace) */ #define NETF_NEW (NETF_WRITE | NETF_CREATE | NETF_TRUNC) /* ------------------------------------------------------------------------ */ /* * Network file object. * * The protocol for operating on a user file is relatively simple to use: * * 1. Prepare the network file by calling CVmNetFile::open(). This returns * a CVmNetFile object that you hang onto for the duration of the file * manipulation - call this 'nfile'. * * 2. Use any local file system API (e.g., any of the osfopxxx() functions) * to open 'nfile->lclfname'. * * 3. Use the local file system APIs (osfread(), osfwrite(), etc) to * read/write the local file. * * 4. Close the local file (with osfcls(), say). * * 5. Close the network file by calling 'nfile->close()'. This has the * side effect of destroying 'nfile', so you don't have to manually * 'delete' it or otherwise dispose it. * * To retrofit this into existing osifc file management code, all that's * required is to bracket the existing code with the CVmNetFile::open() and * close() calls. Note that open() and close() can throw errors, so * protect these calls with err_try if you have to do any resource cleanup * if an error is thrown. */ class CVmNetFile { public: /* * Are we in network storage server mode? This returns true if we have * a storage server, false if we're storing files in the local file * system. */ static int is_net_mode(VMG0_) #ifdef TADSNET ; #else /* local-only implementation - we definitely use local storage only */ { return FALSE; } #endif /* * Open a file. In local mode, this simply returns a CVmNetFile object * with the given name in the lclfname field, so the caller will * directly access the local file. For a network file, if the existing * contents are needed, this downloads a copy of the network file to a * local temporary file, otherwise it simply generates a name for a new * local temporary file; in any case, it returns the temp file name in * the lclfname field. * * If 'sfid' is non-zero, it's a CVmObjFile::SFID_xxx value specifying * the special file ID. Use zero for an ordinary file. * * This throws an error on any download failure. */ static CVmNetFile *open(VMG_ const char *fname, int sfid, int mode, os_filetype_t typ, const char *mime_type) #ifdef TADSNET ; #else { /* * Local implementation - access the local file directly. We don't * care about MIME types for the local implementation, since our * local file system interfaces don't use them. */ return new CVmNetFile(fname, sfid, 0, mode, typ, 0); } #endif /* * Open a file, based on an argument value from the byte-code program. * This accepts string filenames, TemporaryFile (CVmObjTemporaryFile) * objects, or TadsObject objects providing the file spec interface. */ static CVmNetFile *open( VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc, int mode, os_filetype_t typ, const char *mime_type); /* * Open a file in local mode. This is essentially a no-op that allows * a caller to explicitly manipulate a local file through code that's * written to support network files, without adding a special case to * that code. This routine creates a local file descriptor regardless * of the interpreter mode. */ static CVmNetFile *open_local(VMG_ const char *fname, int sfid, int mode, os_filetype_t typ) { /* return an explicitly local file descriptor */ return new CVmNetFile(fname, sfid, 0, mode, typ, 0); } /* rename a file */ void rename_to_local(VMG_ CVmNetFile *newname); void rename_to(VMG_ CVmNetFile *newname) #ifdef TADSNET ; #else { rename_to_local(vmg_ newname); } #endif /* create a directory as named in the file object */ void mkdir_local(VMG_ int create_parents); void mkdir(VMG_ int create_parents) #ifdef TADSNET ; #else { mkdir_local(vmg_ create_parents); } #endif /* remove the directory named in the file object */ void rmdir_local(VMG_ int remove_contents); void rmdir(VMG_ int remove_contents) #ifdef TADSNET ; #else { rmdir_local(vmg_ remove_contents); } #endif /* * Is this a network file? This returns true if the file is stored on * the network, false if it's in the local file system. */ int is_net_file() const { /* it's a network file if it has a server-side filename */ return srvfname != 0; } /* * Get the file mode, per osfmode(). For a network file, the only * possible mode is "file", since we don't support directories or other * non-file types. On success, fills in *mode with a bitwise * combination of OSFMODE_xxx flags and returns true; returns false on * failure. */ int get_file_mode(VMG_ unsigned long *mode, unsigned long *attrs, int follow_links) #ifdef TADSNET ; #else { return osfmode(lclfname, follow_links, mode, attrs); } #endif /* * Get the file stat() information, per os_file_stat(). This isn't * supported for network files. */ int get_file_stat(VMG_ os_file_stat_t *stat, int follow_links) #ifdef TADSNET ; #else { return os_file_stat(lclfname, follow_links, stat); } #endif /* * Resolve a symbolic link. If this file is a symbolic link, this * fills in 'target' with the target path and returns true. Otherwise * returns false. */ int resolve_symlink(VMG_ char *target, size_t target_size) #ifdef TADSNET ; #else { return os_resolve_symlink(lclfname, target, target_size); } #endif /* * Get a listing of files in the directory. Creates a list object, and * fills in the list of FileName (CVmObjFileName) objects giving the * names of the files in the directory. Returns true on success, false * on failure. * * 'nominal_path' is an optional string to use as the displayed * directory path name for the constructed FileName objects. If this * is null, we'll use our actual internal local file path. The nominal * path can be used to preserve the original relative path name, which * might be desirable when the netfile object is resolved to an * absolute path based on a working directory. */ int readdir(VMG_ const char *nominal_path, vm_val_t *retval) #ifdef TADSNET ; #else { return readdir_local(vmg_ nominal_path, retval, 0, 0, 0); } #endif /* * Enumerate files in the directory through a callback function. */ int readdir_cb(VMG_ const char *nominal_path, const struct vm_rcdesc *rc, const vm_val_t *cb, int recursive) #ifdef TADSNET ; #else { return readdir_local(vmg_ nominal_path, 0, rc, cb, recursive); } #endif /* read a local directory */ int readdir_local(VMG_ const char *nominal_path, vm_val_t *retval, const struct vm_rcdesc *rc, const vm_val_t *cb, int recursive); /* * Does the given file exist? Returns true if so, nil if not. */ static int exists(VMG_ const char *fname, int sfid) #ifdef TADSNET ; #else { /* * local-only mode: check the local file system; it exists if * osfacc() returns success (zero) */ return !osfacc(fname); } #endif /* * Test to see if we can write to the given file. We attempt to open * the file for writing (retaining any existing data in the file, or * creating a new file if it doesn't exist). If we succeed, we close * the file (and delete it if it didn't exist), and return TRUE. If we * fail, return FALSE. */ static int can_write(VMG_ const char *fname, int sfid) #ifdef TADSNET ; #else { return can_write_local(fname); } #endif /* * Close the file. Call this after closing the local file handle. For * a network file opened with write access, this will copy the local * temp copy back to the storage server, then delete the local temp * copy. For any network file, deletes the local temp file. For all * files, deletes the CVmNetFile object. * * Throws an error on upload failure. The CVmNetFile object is always * reliably deleted, whether or not an error is thrown. */ void close(VMG0_) #ifdef TADSNET ; #else { /* * Local file: * * - if we opened it in "delete" mode, delete it *. - if we opened it in "create" mode, set the file's OS type code */ int err = 0; if ((mode & NETF_DELETE) != 0) { if (osfdel(lclfname)) err = VMERR_DELETE_FILE; } else if ((mode & NETF_CREATE) != 0) os_settype(lclfname, typ); /* delete self */ delete this; /* if an error occurred, throw the error */ if (err != 0) err_throw(err); } #endif /* * Abandon the network file. Call this if the local file manipulation * fails, and the caller decides it doesn't want to save any changes * back to the network after all. This deletes any local temp file, * and frees the CVmNetFile object. Call this only after closing the * local file handle. */ void abandon(VMG0_) { #ifdef TADSNET /* delete the temp file */ if (srvfname != 0) osfdel(lclfname); #endif /* free self */ delete this; } /* * Mark references for garbage collection. This marks our reference to * the filespec object, if we have one. */ void mark_refs(VMG_ uint state); /* * The local filename. After obtaining this object from vmnet_fopen(), * the caller can use any osfopxxx() function to open the file. If * we're using the network storage server, this will name a temp file * in the system temp directory; if the file isn't on the storage * server, this is simply the local file name. */ char *lclfname; /* special file ID */ int sfid; /* * The server filename. When we're operating on a storage server file, * this is the name of the file on the server; otherwise it's null. */ char *srvfname; /* * The file spec object. This can be a TemporaryFile object, or a * TadsObject object with a file spec interface. If the file was * opened based on a string filename, this is nil. */ vm_obj_id_t filespec; /* the file mode */ int mode; /* is this a temporary file? */ int is_temp; /* the OS file type */ os_filetype_t typ; /* MIME type of the file */ char *mime_type; protected: CVmNetFile(const char *lclfname, int sfid, const char *srvfname, int mode, os_filetype_t typ, const char *mime_type) { /* save the filenames, mode, and type */ this->lclfname = lib_copy_str(lclfname); this->srvfname = lib_copy_str(srvfname); this->sfid = sfid; this->mode = mode; this->typ = typ; this->mime_type = lib_copy_str(mime_type); this->filespec = VM_INVALID_OBJ; this->is_temp = FALSE; } ~CVmNetFile() { lib_free_str(lclfname); lib_free_str(srvfname); lib_free_str(mime_type); } /* build the full server-side filename for a given file */ static const char *build_server_filename( VMG_ char *dst, size_t dstsiz, const char *fname, int sfid); /* check to see if we can create/write to a local file */ static int can_write_local(const char *fname) { /* note whether or not the file exists already */ int existed = !osfacc(fname); /* * try opening for read/write, keeping the existing contents or * creating a new file if it doesn't exist */ osfildef *fp = osfoprwb(fname, OSFTBIN); if (fp != 0) { /* * Successfully opened the file, so we evidently can write it. * We don't actually need to do anything with the file other * than check that we could open it, so close it. */ osfcls(fp); /* if the file didn't exist before, restore the status quo ante */ if (!existed) osfdel(fname); /* indicate success */ return TRUE; } else { /* couldn't open the file - return failure */ return FALSE; } } }; #endif /* VMNETFIL_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmnetfillcl.cpp�������������������������������������������������������������������0000664�0000000�0000000�00000033174�12650170723�0016711�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmnetfil.cpp - network file operations, local mode Function This module contains network file functions that are needed in both server and local modes. This file should be linked into all builds, whether or not the server mode is used. Notes Modified 09/08/10 MJRoberts - Creation */ #include "t3std.h" #include "os.h" #include "osifcnet.h" #include "vmnetfil.h" #include "vmnet.h" #include "vmfile.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmrun.h" #include "vmglob.h" #include "vmpredef.h" #include "vmimport.h" #include "sha2.h" #include "vmhash.h" #include "vmbif.h" #include "vmtype.h" #include "vmobj.h" #include "vmtmpfil.h" #include "vmtobj.h" #include "vmimage.h" #include "vmstack.h" #include "charmap.h" #include "vmlst.h" #include "vmfilnam.h" #include "vmfilobj.h" #include "vmstr.h" /* ------------------------------------------------------------------------ */ /* * Open a network file */ CVmNetFile *CVmNetFile::open(VMG_ const vm_val_t *val, const vm_rcdesc *rc, int mode, os_filetype_t typ, const char *mime_type) { vm_val_t filespec; /* check for a TadsObject implementing getFilename */ if (G_predef->filespec_getFilename != VM_INVALID_PROP && vm_val_cast_ok(CVmObjTads, val)) { /* call getFilename - the return value is the file spec */ G_interpreter->get_prop( vmg_ 0, val, G_predef->filespec_getFilename, val, 0, rc); /* the result is the real file spec */ filespec = *G_interpreter->get_r0(); } else { /* it's not a TadsObject, so it must directly have the file name */ filespec = *val; } /* check the file spec argument type */ CVmNetFile *nf = 0; CVmObjTemporaryFile *tmp = 0; CVmObjFileName *ofn = 0; if ((tmp = vm_val_cast(CVmObjTemporaryFile, &filespec)) != 0) { /* if the temporary file object is invalid, it's an error */ if (tmp->get_fname() == 0) err_throw(VMERR_CREATE_FILE); /* create the local file descriptor for the temp file path */ nf = open_local(vmg_ tmp->get_fname(), 0, mode, typ); /* mark it as a temp file */ nf->is_temp = TRUE; } else if (filespec.is_numeric(vmg0_) || ((ofn = vm_val_cast(CVmObjFileName, &filespec)) != 0 && ofn->is_special_file())) { /* * It's a special file ID, either as an integer or as a FileName * wrapping a special file int. Get the value. */ int32_t sfid = (ofn != 0 ? ofn->get_sfid() : filespec.num_to_int(vmg0_)); /* resolve the file system path for the given special file ID */ char fname[OSFNMAX] = { '\0' }; if (!CVmObjFile::sfid_to_path(vmg_ fname, sizeof(fname), sfid)) err_throw(VMERR_BAD_VAL_BIF); /* create the special file descriptor */ nf = open(vmg_ fname, sfid, mode, typ, mime_type); } else { /* anything else has to be a string */ char fname[OSFNMAX]; CVmBif::get_fname_val(vmg_ fname, sizeof(fname), &filespec); /* * if it's a local file, and it has a relative path, explicitly * apply the image file path as the default working directory */ if (!os_is_file_absolute(fname) && !is_net_mode(vmg0_)) { /* build the full, absolute name based on the file base path */ char fnabs[OSFNMAX]; os_build_full_path(fnabs, sizeof(fnabs), G_file_path, fname); /* replace the relative path with the new absolute path */ lib_strcpy(fname, sizeof(fname), fnabs); } /* create the regular network file descriptor */ nf = open(vmg_ fname, 0, mode, typ, mime_type); } /* if they gave us an object as our file spec, remember it */ if (nf != 0 && val->typ == VM_OBJ) nf->filespec = val->val.obj; /* return the network file descriptor */ return nf; } /* ------------------------------------------------------------------------ */ /* * Rename a file */ void CVmNetFile::rename_to_local(VMG_ CVmNetFile *newname) { /* if the new name isn't local, this isn't supported */ if (newname->is_net_file()) err_throw(VMERR_RENAME_FILE); /* if the destination file already exists, it's an error */ if (!osfacc(newname->lclfname)) err_throw(VMERR_RENAME_FILE); /* do the rename */ if (!os_rename_file(lclfname, newname->lclfname)) err_throw(VMERR_RENAME_FILE); } /* ------------------------------------------------------------------------ */ /* * Create a local directory */ void CVmNetFile::mkdir_local(VMG_ int create_parents) { /* try creating the directory */ if (!os_mkdir(lclfname, create_parents)) err_throw(VMERR_CREATE_FILE); } /* ------------------------------------------------------------------------ */ /* * Empty a local directory */ static void empty_dir(VMG_ const char *dir) { /* open the directory search */ osdirhdl_t dirhdl; if (os_open_dir(dir, &dirhdl)) { err_try { /* keep going until we're out of files */ char fname[OSFNMAX]; while (os_read_dir(dirhdl, fname, sizeof(fname))) { /* get the full path */ char path[OSFNMAX]; os_build_full_path(path, sizeof(path), dir, fname); /* get the mode */ unsigned long fmode; unsigned long fattr; if (osfmode(path, FALSE, &fmode, &fattr)) { /* check whether it's a directory or an ordinary file */ if ((fmode & OSFMODE_DIR) != 0) { /* * directory - skip the special '.' and '..' links, * since they'd get us stuck in a loop */ os_specfile_t st = os_is_special_file(fname); if (st != OS_SPECFILE_SELF && st != OS_SPECFILE_PARENT) { /* recursively empty the directory */ empty_dir(vmg_ path); /* remove this directory */ if (!os_rmdir(path)) err_throw(VMERR_DELETE_FILE); } } else { /* ordinary file - delete it */ if (osfdel(path)) err_throw(VMERR_DELETE_FILE); } } } } err_finally { /* close the directory search handle */ os_close_dir(dirhdl); } err_end; } } /* * Remove a local directory */ void CVmNetFile::rmdir_local(VMG_ int remove_contents) { /* if desired, recursively remove the directory's contents */ if (remove_contents) empty_dir(vmg_ lclfname); /* try removing the directory */ if (!os_rmdir(lclfname)) err_throw(VMERR_DELETE_FILE); } /* ------------------------------------------------------------------------ */ /* * static directory lister */ static int s_readdir_local(VMG_ const char *lclfname, const char *nominal_path, vm_val_t *retval, const vm_rcdesc *rc, const vm_val_t *cb, int recursive) { /* note the nominal path string length */ size_t nominal_path_len = strlen(nominal_path); /* * If the caller wants a result list, create a list for the return * value. We don't know how many elements the list will need, so * create it with an arbitrary initial size, and expand it later as * needed. */ CVmObjList *lst = 0; if (retval != 0) { /* create the list */ const size_t initlen = 32; retval->set_obj(CVmObjList::create(vmg_ FALSE, initlen)); lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* clear it */ lst->cons_clear(0, initlen - 1); /* save the new list on the stack for gc protection */ G_stk->push(retval); } /* presume success */ int ok = TRUE; /* open the directory */ osdirhdl_t dirhdl; if (os_open_dir(lclfname, &dirhdl)) { err_try { /* iterate over the directory's contents */ int idx = 0; char curname[OSFNMAX]; while (os_read_dir(dirhdl, curname, sizeof(curname))) { char *unm = 0; vm_obj_id_t fnobj = VM_INVALID_OBJ; err_try { /* map the filename to UTF8 */ size_t unmlen = G_cmap_from_fname->map_str_alo( &unm, curname); /* create the FileName object */ fnobj = CVmObjFileName::combine_path( vmg_ nominal_path, nominal_path_len, unm, unmlen, TRUE); /* push it for gc protection */ G_stk->push()->set_obj(fnobj); } err_finally { if (unm != 0) t3free(unm); } err_end; /* if we're building a list, add the file to the list */ if (retval != 0) { vm_val_t ele; ele.set_obj(fnobj); lst->cons_ensure_space(vmg_ idx, 16); lst->cons_set_element(idx, &ele); lst->cons_set_len(++idx); } /* if there's a callback, invoke it */ if (cb != 0) { G_stk->push()->set_obj(fnobj); G_interpreter->call_func_ptr(vmg_ cb, 1, rc, 0); } /* * If we're doing a recursive listing, and this is a * directory, list its ocntents. Skip self and parent * directory links ('.' and '..' on Unix), since those * would cause infinite recursion. */ os_specfile_t st; if (recursive && (st = os_is_special_file(curname)) != OS_SPECFILE_SELF && st != OS_SPECFILE_PARENT) { /* build the full path name */ char fullname[OSFNMAX]; os_build_full_path(fullname, sizeof(fullname), lclfname, curname); /* check to see if it's a directory */ unsigned long fmode; unsigned long fattr; if (osfmode(fullname, FALSE, &fmode, &fattr) && (fmode & OSFMODE_DIR) != 0) { /* get the combined path from the FileName object */ const char *path = vm_objid_cast(CVmObjFileName, fnobj) ->get_path_string(); /* build the actual combined path */ char subfname[OSFNMAX]; os_build_full_path( subfname, sizeof(subfname), lclfname, curname); /* do the recursive listing */ ok |= s_readdir_local(vmg_ subfname, path + VMB_LEN, retval, rc, cb, TRUE); } } /* we're done with the FileName object for this iteration */ G_stk->discard(1); } } err_finally { /* close the directory handle */ os_close_dir(dirhdl); } err_end; } /* discard the gc protection for the list, if applicable */ if (retval != 0) G_stk->discard(1); /* return the result */ return ok; } /* * Get a local directory listing, returning a list of FileName objects * and/or invoking a callback for each file found. */ int CVmNetFile::readdir_local(VMG_ const char *nominal_path, vm_val_t *retval, const struct vm_rcdesc *rc, const vm_val_t *cb, int recursive) { /* verify that the path exists and refers to a directory */ unsigned long mode; unsigned long attr; if (!osfmode(lclfname, TRUE, &mode, &attr) || (mode & OSFMODE_DIR) == 0) return FALSE; /* * if the caller didn't specify a nominal path to use in constructed * FileName objects, use the actual local path */ if (nominal_path == 0) nominal_path = lclfname; /* call our static implementation with our local filename path */ return s_readdir_local(vmg_ lclfname, nominal_path, retval, rc, cb, recursive); } /* ------------------------------------------------------------------------ */ /* * Mark references for the garbage collector */ void CVmNetFile::mark_refs(VMG_ uint state) { /* if we have a filespec object, mark it */ if (filespec != VM_INVALID_OBJ) G_obj_table->mark_all_refs(filespec, state); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmobj.cpp�������������������������������������������������������������������������0000664�0000000�0000000�00000343554�12650170723�0015515�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMOBJ.CPP,v 1.4 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmobj.cpp - VM object manager Function Notes Modified 10/28/98 MJRoberts - Creation */ #include <stdlib.h> #include <memory.h> #include <assert.h> #include "t3std.h" #include "vmtype.h" #include "vmobj.h" #include "vmstack.h" #include "vmundo.h" #include "vmrun.h" #include "vmfile.h" #include "vmmeta.h" #include "vmlst.h" #include "vmstr.h" #include "vmintcls.h" #include "vmpool.h" #include "vmfunc.h" #include "vmpredef.h" #include "vmhash.h" #include "vmtobj.h" #include "vmanonfn.h" #include "vmdynfunc.h" /* ------------------------------------------------------------------------ */ /* * Statistics gathering */ #ifdef VMOBJ_GC_STATS # define IF_GC_STATS(x) x struct { void gc_stats() { runs = 0; tot_freed = 0; cur_freed = 0; max_freed = 0; t = 0; } void begin_pass() { t0 = os_get_sys_clock_ms(); runs++; pass_start_bytes = cur_bytes; cur_freed = 0; } void end_pass() { t += os_get_sys_clock_ms() - t0; if (cur_freed > max_freed) max_freed = cur_freed; long garbage_bytes = pass_start_bytes - cur_bytes; if (garbage_bytes > max_garbage_bytes) max_garbage_bytes = garbage_bytes; } void count_free() { ++cur_freed; ++tot_freed; } void count_alloc_bytes(size_t siz) { cur_bytes += siz; if (cur_bytes > max_bytes) max_bytes = cur_bytes; } void count_realloc_bytes(size_t oldsiz, size_t newsiz) { count_alloc_bytes(newsiz); count_free_bytes(oldsiz); } void count_free_bytes(size_t siz) { cur_bytes -= siz; } void display() { printf("Garbage collection statistics:\n" " collection runs: %ld\n" " objects freed: %ld\n" " average freed per run: %ld\n" " max freed in one run: %ld\n" " peak heap bytes: %ld\n" " peak garbage bytes: %ld\n" " total gc time (ms): %ld\n" " average gc time (ms): %ld\n", runs, tot_freed, runs != 0 ? tot_freed/runs : 0, max_freed, max_bytes, max_garbage_bytes, t, runs != 0 ? t/runs : 0); } /* number of times the gc has run */ long runs; /* total number of objects collected */ long tot_freed; /* number of objects collected on this pass */ long cur_freed; /* maximum number collected on any one pass */ long max_freed; /* current total heap manager allocated bytes */ long cur_bytes; /* allocated bytes at start of last gc pass */ long pass_start_bytes; /* peak allocated bytes */ long max_bytes; /* peak garbage bytes */ long max_garbage_bytes; /* elapsed time in garbage collector */ long t; /* starting time in ticks of current run */ long t0; } gc_stats; #else /* VMOBJ_GC_STATS */ # define IF_GC_STATS(x) #endif /* VMOBJ_GC_STATS */ /* ------------------------------------------------------------------------ */ /* * Base fixed-size object entry implementation */ /* * Metaclass registration object for the root object class. Note that a * root object can never be instantiated; this entry is purely for the * use of the type system. */ static CVmMetaclassRoot metaclass_reg_obj; CVmMetaclass *CVmObject::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObject:: *CVmObject::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t prop, vm_obj_id_t *source_obj) = { &CVmObject::getp_undef, &CVmObject::getp_of_kind, &CVmObject::getp_sclist, &CVmObject::getp_propdef, &CVmObject::getp_proptype, &CVmObject::getp_get_prop_list, &CVmObject::getp_get_prop_params, &CVmObject::getp_is_class, &CVmObject::getp_propinh, &CVmObject::getp_is_transient }; /* * Allocate space for an object from a page table, given the object ID. * The caller must allocate the object ID prior to new'ing the object; * operator new will store the memory for the new object in the object * slot the caller allocated. */ void *CVmObject::operator new(size_t siz, VMG_ vm_obj_id_t obj_id) { /* * The size must be the size of an object entry. This size never * changes, even for subclasses of the object type, since all * variable-size data must be stored in the variable-size portion of * the object. Here we are only concerned with allocating the * fixed-size object descriptor. */ assert(siz == sizeof(CVmObject)); /* return the memory contained in the object entry */ return G_obj_table->get_obj(obj_id); } /* * Determine if this object is an instance of the given object. By * default, we will simply check to see if the given object is the * IntrinsicClass instance that represents our metaclass or one of its * superclasses. */ int CVmObject::is_instance_of(VMG_ vm_obj_id_t obj) { vm_meta_entry_t *entry; /* * we can only be an instance of the object if the object is an * IntrinsicClass instance, since by default we have only intrinsic * classes among our superclasses */ if (!CVmObjClass::is_intcls_obj(vmg_ obj)) return FALSE; /* * look up my metaclass in the metaclass dependency table, and * determine if my dependency table entry's record of the * IntrinsicClass object for the metaclass matches the given object */ entry = (G_meta_table ->get_entry_from_reg(get_metaclass_reg()->get_reg_idx())); /* * if we have an entry, ask our superclass object if it is an * instance of the given object; otherwise, we must not be an * instance */ if (entry != 0) { /* if this is our direct superclass, we're an instance */ if (entry->class_obj_ == obj) return TRUE; /* if there's no intrinsic class object, return false */ if (entry->class_obj_ == VM_INVALID_OBJ) return FALSE; /* ask the superclass if it inherits from the given object */ return vm_objp(vmg_ entry->class_obj_)->is_instance_of(vmg_ obj); } else { /* * no metaclass table entry - we can't really make any * determination, so indicate that we're not an instance */ return FALSE; } } /* * Get the nth superclass. By default, an object's superclass is * represented by the intrinsic class object for the metaclass. */ vm_obj_id_t CVmObject::get_superclass(VMG_ vm_obj_id_t /*self*/, int sc_idx) const { vm_meta_entry_t *entry; /* we only have one superclass */ if (sc_idx != 0) return VM_INVALID_OBJ; /* look up the metaclass entry */ entry = (G_meta_table ->get_entry_from_reg(get_metaclass_reg()->get_reg_idx())); /* return the IntrinsicClass object that represents this metaclass */ return (entry != 0 ? entry->class_obj_ : VM_INVALID_OBJ); } /* * Get a property */ int CVmObject::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property index to an index into our function table */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* if we find it, the source object is the 'Object' intrinsic class */ *source_obj = metaclass_reg_->get_class_obj(vmg0_); /* call the appropriate function */ return (this->*func_table_[func_idx])(vmg_ self, retval, argc, prop, source_obj); } /* * Inherit a property */ int CVmObject::inh_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t orig_target_obj, vm_obj_id_t defining_obj, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* * We're inheriting. This is never called from native code, as native * code does its inheriting directly through C++ calls to base class * native code; hence, we can only be called from a byte-code modifier * object. * * First, try looking for a native implementation. We can reach this * point if a byte-code object overrides an intrinsic method, then * inherits from the byte-code override. */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); if (func_idx != 0) { /* the source object is the 'Object' intrinsic class */ *source_obj = metaclass_reg_->get_class_obj(vmg0_); /* call the native implementation */ return (this->*func_table_[func_idx])(vmg_ self, retval, argc, prop, source_obj); } /* * We didn't find it among the intrinsic methods, so look at the * modifier objects. */ return find_modifier_prop(vmg_ prop, retval, self, orig_target_obj, defining_obj, source_obj, argc); } /* * Cast the object to a string. If we got here, it means that there's no * metaclass override, so use reflection services if available, or the * basic "object#xxx" template. */ const char *CVmObject::cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const { /* set up a vm_val_t for 'self' */ vm_val_t val; val.set_obj(self); /* try calling self.objToString */ vm_prop_id_t objToString = G_predef->objToString; if (objToString != VM_INVALID_PROP) { /* call self.objToString */ G_interpreter->get_prop(vmg_ 0, &val, objToString, &val, 0, 0); /* if that yielded a string, return it */ const char *str = G_interpreter->get_r0()->get_as_string(vmg0_); if (str != 0) { /* set the return value */ *new_str = *G_interpreter->get_r0(); /* return the string buffer */ return str; } } /* try getting a symbolic name, otherwise use the object#xxx template */ return CVmObjString::reflect_to_str( vmg_ new_str, 0, 0, &val, "object#%ld", (long)self); } /* * Any object can be listlike, even if it doesn't natively implement * indexing, if it defines operator[] and 'length' as user-code methods. */ int CVmObject::is_listlike(VMG_ vm_obj_id_t self) { vm_val_t v; vm_obj_id_t o; int minargs, optargs, varargs; /* check for the required imports */ if (G_predef->operator_idx == VM_INVALID_PROP || G_predef->length_prop == VM_INVALID_PROP) return FALSE; /* check for operator [] */ if (!get_prop(vmg_ G_predef->operator_idx, &v, self, &o, 0)) return FALSE; /* check for a 'length' method taking no arguments */ if (!get_prop_interface(vmg_ self, G_predef->length_prop, minargs, optargs, varargs) || minargs > 0) return FALSE; /* it's list-like */ return TRUE; } /* * An object that doesn't have a native list-like interface can still * provide list-like operations by defining operator[] and 'length' in user * code. */ int CVmObject::ll_length(VMG_ vm_obj_id_t self) { /* make a recursive call to 'length' */ vm_val_t vself; vself.set_obj(self); vm_rcdesc rc("Object.ll_length"); G_interpreter->get_prop(vmg_ 0, &vself, G_predef->length_prop, &vself, 0, &rc); /* the return value in R0 is the length */ vm_val_t *r0 = G_interpreter->get_r0(); if (r0->typ == VM_INT) return r0->val.intval; else return -1; } /* * Index the object, with overloading if there's no native implementation. */ void CVmObject::index_val_ov(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val) { /* try the native operation first */ if (!index_val_q(vmg_ result, self, index_val)) { /* no native indexing - try the operator[] overload */ vm_val_t vself; vself.set_obj(self); G_stk->push(index_val); G_interpreter->op_overload(vmg_ 0, -1, &vself, G_predef->operator_idx, 1, VMERR_CANNOT_INDEX_TYPE); /* return the result from R0 */ *result = *G_interpreter->get_r0(); } } /* * Index the object, with overloading if there's no native implementation. */ void CVmObject::set_index_val_ov(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val) { /* try the native indexing first */ if (!set_index_val_q(vmg_ new_container, self, index_val, new_val)) { /* no native indexing - try the operator[]= overload */ vm_val_t vself; vself.set_obj(self); G_stk->push(new_val); G_stk->push(index_val); G_interpreter->op_overload(vmg_ 0, -1, &vself, G_predef->operator_setidx, 2, VMERR_CANNOT_INDEX_TYPE); /* return the new container result from R0 */ *new_container = *G_interpreter->get_r0(); } } /* * Get the next value from an iteration */ int CVmObject::iter_next(VMG_ vm_obj_id_t selfobj, vm_val_t *val) { /* check that the iterator properties are defined by the program */ if (G_iter_next_avail == VM_INVALID_PROP || G_iter_get_next == VM_INVALID_PROP) return FALSE; /* call isNextAvailable */ vm_rcdesc rc("for..in"); vm_val_t self; self.set_obj(selfobj); G_interpreter->get_prop(vmg_ 0, &self, G_iter_next_avail, &self, 0, &rc); /* if that returned false (nil or 0), return false */ vm_val_t *ret = G_interpreter->get_r0(); if (ret->typ == VM_NIL || (ret->typ == VM_INT && ret->val.intval == 0)) return FALSE; /* call getNext and return the result */ G_interpreter->get_prop(vmg_ 0, &self, G_iter_get_next, &self, 0, &rc); *val = *G_interpreter->get_r0(); return TRUE; } /* * Get a property that isn't defined in our property table */ int CVmObject::getp_undef(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t prop, vm_obj_id_t *source_obj) { /* * We didn't find a native implementation of the method, but there's * still one more place to look: the "modifier" object for our class * tree. Modifier objects are byte-code objects that can provide * implementations of methods that add to intrinsic classes (modifiers * can't override intrinsic methods, but they can add new methods). * * Since we're looking for a property on an initial get-property call * (not an inheritance call), we don't yet have a defining object to * find and skip in the inheritance tree, so use VM_INVALID_OBJ as the * defining object. In addition, we're directly calling the method, so * the target object is the same as the 'self' object. */ return find_modifier_prop(vmg_ prop, retval, self, self, VM_INVALID_OBJ, source_obj, argc); } /* * Find a modifier property. */ int CVmObject::find_modifier_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t orig_target_obj, vm_obj_id_t defining_obj, vm_obj_id_t *source_obj, uint *argc) { vm_meta_entry_t *entry; int found_def_obj; /* we haven't yet found the defining superclass */ found_def_obj = FALSE; /* get my metaclass from the dependency table */ entry = (G_meta_table ->get_entry_from_reg(get_metaclass_reg()->get_reg_idx())); /* * if there's an associated intrinsic class object, check to see if * it provides a user modifier object for this intrinsic class */ while (entry != 0 && entry->class_obj_ != VM_INVALID_OBJ) { vm_obj_id_t mod_obj; /* ask the intrinsic class object for the user modifier object */ mod_obj = ((CVmObjClass *)vm_objp(vmg_ entry->class_obj_)) ->get_mod_obj(); /* * If we have a defining object, we must ignore objects in the * superclass tree until we find the defining object. Therefore, * scan up the superclass tree for mod_obj and see if we can find * the defining object; when we find it, we can start looking at * objects for real at the defining object's superclass. * * (Superclasses in modifier objects aren't real superclasses, * because modifier objects are classless. Instead, the superclass * list simply implements the 'modify' chain.) */ if (mod_obj != VM_INVALID_OBJ && defining_obj != VM_INVALID_OBJ && !found_def_obj) { /* * if the defining object isn't among the byte-code * superclasses of the modifier object, we must skip this * entire intrinsic class and move to the intrinsic superclass */ if (mod_obj == defining_obj || vm_objp(vmg_ mod_obj)->is_instance_of(vmg_ defining_obj)) { /* * the defining object is among my modifier family - this * means that this is the intrinsic superclass where we * found the modifier method */ found_def_obj = TRUE; } else { /* * The current defining object is not part of the modifier * chain for this intrinsic class, so we've already skipped * past this point in the intrinsic superclass tree on past * inheritances. Simply move to the next intrinsic class * and look at its modifier. */ goto next_intrinsic_sc; } } /* * If there's a modifier object, send the property request to it. * We are effectively delegating the method call to the modifier * object, so we must use the "inherited property" call, not the * plain get_prop() call: 'self' is the original self, but the * target object is the intrinsic class modifier object. */ if (mod_obj != VM_INVALID_OBJ && vm_objp(vmg_ mod_obj)->inh_prop( vmg_ prop, retval, self, mod_obj, defining_obj, source_obj, argc)) return TRUE; /* we didn't find it in this object, so look at its super-metaclass */ next_intrinsic_sc: if (entry->meta_->get_supermeta_reg() != 0) { /* get the super-metaclass ID */ entry = (G_meta_table ->get_entry_from_reg(entry->meta_ ->get_supermeta_reg() ->get_reg_idx())); /* * if we've already found the previous defining intrinsic * class, we can forget about the previous defining modifier * object now: since we're moving to a new intrinsic * superclass, we will have no superclass relation to the * previous defining object in the new modifier family, so we * can simply use the next definition of the property we find */ if (found_def_obj) defining_obj = VM_INVALID_OBJ; } else { /* no super-metaclass - give up */ break; } } /* we don't have a modifier object, so the property is undefined */ return FALSE; } /* * property evaluator - ofKind */ int CVmObject::getp_of_kind(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t, vm_obj_id_t *) { vm_val_t sc; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the superclass, and make sure it's an object */ G_stk->pop(&sc); if (sc.typ != VM_OBJ) err_throw(VMERR_OBJ_VAL_REQD); /* check for an identity test */ if (sc.val.obj == self) { /* x.ofKind(x) == true */ retval->set_true(); } else { /* check to see if the object is a superclass of ours */ retval->set_logical(is_instance_of(vmg_ sc.val.obj)); } /* handled */ return TRUE; } /* * property evaluator - isClass */ int CVmObject::getp_is_class(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t, vm_obj_id_t *) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* indicate whether or not we're a class object */ retval->set_logical(is_class_object(vmg_ self)); /* handled */ return TRUE; } /* * property evaluator - isTransient */ int CVmObject::getp_is_transient(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t, vm_obj_id_t *) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* indicate whether or not we're transient */ retval->set_logical(G_obj_table->is_obj_transient(self)); /* handled */ return TRUE; } /* * property evaluator - getSuperclassList */ int CVmObject::getp_sclist(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t, vm_obj_id_t *) { size_t sc_cnt; vm_obj_id_t lst_obj; CVmObjList *lstp; size_t i; static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* push a self-reference for GC protection */ G_interpreter->push_obj(vmg_ self); /* get the number of superclasses */ sc_cnt = get_superclass_count(vmg_ self); /* allocate a list for the results */ lst_obj = CVmObjList::create(vmg_ FALSE, sc_cnt); lstp = (CVmObjList *)vm_objp(vmg_ lst_obj); /* build the superclass list */ for (i = 0 ; i < sc_cnt ; ++i) { vm_val_t ele_val; /* get this superclass */ ele_val.set_obj(get_superclass(vmg_ self, i)); /* set the list element */ lstp->cons_set_element(i, &ele_val); } /* discard our GC protection */ G_stk->discard(); /* return the list */ retval->set_obj(lst_obj); /* handled */ return TRUE; } /* * property evaluator - propDefined */ int CVmObject::getp_propdef(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc, vm_prop_id_t, vm_obj_id_t *) { uint argc = (in_argc != 0 ? *in_argc : 0); vm_val_t val; int flags; int found; vm_prop_id_t prop; vm_obj_id_t source_obj; static CVmNativeCodeDesc desc(1, 1); /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* pop the property address to test */ G_interpreter->pop_prop(vmg_ &val); prop = val.val.prop; /* if we have the flag argument, get it; otherwise, use the default */ if (argc >= 2) { /* get the flag value */ G_interpreter->pop_int(vmg_ &val); flags = (int)val.val.intval; } else { /* use the default flags */ flags = VMOBJ_PROPDEF_ANY; } /* presume we won't find a valid source object */ source_obj = VM_INVALID_OBJ; /* look up the property */ found = get_prop(vmg_ prop, &val, self, &source_obj, 0); /* * If we found a result, check to see if it's an intrinsic class * modifier object. If it is, replace it with its intrinsic class: * modifier objects are invisible through the reflection mechanism, and * appear to be the actual intrinsic classes they modify. */ if (found && CVmObjIntClsMod::is_intcls_mod_obj(vmg_ source_obj)) source_obj = find_intcls_for_mod(vmg_ self, source_obj); /* check the flags */ switch(flags) { case VMOBJ_PROPDEF_ANY: /* return true if the property is defined */ retval->set_logical(found); break; case VMOBJ_PROPDEF_DIRECTLY: /* return true if the property is defined directly */ retval->set_logical(found && source_obj == self); break; case VMOBJ_PROPDEF_INHERITS: /* return true if the property is inherited only */ retval->set_logical(found && source_obj != self); break; case VMOBJ_PROPDEF_GET_CLASS: /* return the defining class, or nil if it's not defined */ if (found) { /* * If we got a valid source object, return it. If we didn't * get a valid source object, but we found the property, * return 'self' as the result; this isn't exactly right, but * this should only be possible when the source object is an * intrinsic class for which no intrinsic class object is * defined, in which case the best we can do is provide 'self' * as the answer. */ retval->set_obj(source_obj != VM_INVALID_OBJ ? source_obj : self); } else { /* didn't find it - the return value is nil */ retval->set_nil(); } break; default: /* other flags are invalid */ err_throw(VMERR_BAD_VAL_BIF); break; } /* handled */ return TRUE; } /* * Find the intrinsic class which the given modifier object modifies. This * can only be used with a modifier that modifies my intrinsic class or one * of its intrinsic superclasses. */ vm_obj_id_t CVmObject::find_intcls_for_mod(VMG_ vm_obj_id_t self, vm_obj_id_t mod_obj) { /* get my metaclass from the dependency table */ vm_meta_entry_t *entry = (G_meta_table ->get_entry_from_reg(get_metaclass_reg()->get_reg_idx())); /* * if there's an intrinsic class object for the metaclass, ask it to do * the work */ if (entry != 0 && entry->class_obj_ != VM_INVALID_OBJ) return (((CVmObjClass *)vm_objp(vmg_ entry->class_obj_)) ->find_mod_src_obj(vmg_ entry->class_obj_, mod_obj)); /* there's no intrinsic metaclass, so we can't find what we need */ return VM_INVALID_OBJ; } /* * property evaluator - propInherited */ int CVmObject::getp_propinh(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc, vm_prop_id_t, vm_obj_id_t *) { uint argc = (in_argc != 0 ? *in_argc : 0); vm_val_t val; int flags; int found; vm_prop_id_t prop; vm_obj_id_t source_obj; vm_obj_id_t orig_target_obj; vm_obj_id_t defining_obj; static CVmNativeCodeDesc desc(3, 1); /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* pop the property address to test */ G_interpreter->pop_prop(vmg_ &val); prop = val.val.prop; /* get the original target object */ G_interpreter->pop_obj(vmg_ &val); orig_target_obj = val.val.obj; /* get the defining object */ G_interpreter->pop_obj(vmg_ &val); defining_obj = val.val.obj; /* if we have the flag argument, get it; otherwise, use the default */ if (argc >= 4) { /* get the flag value */ G_interpreter->pop_int(vmg_ &val); flags = (int)val.val.intval; } else { /* use the default flags */ flags = VMOBJ_PROPDEF_ANY; } /* presume we won't find a valid source object */ source_obj = VM_INVALID_OBJ; /* look up the property */ found = inh_prop(vmg_ prop, &val, self, orig_target_obj, defining_obj, &source_obj, 0); /* check the flags */ switch(flags) { case VMOBJ_PROPDEF_ANY: /* return true if the property is defined */ retval->set_logical(found); break; case VMOBJ_PROPDEF_GET_CLASS: /* return the defining class, or nil if it's not defined */ if (found) { /* return the source object, or 'self' if we didn't find one */ retval->set_obj(source_obj != VM_INVALID_OBJ ? source_obj : self); } else { /* didn't find it - the return value is nil */ retval->set_nil(); } break; default: /* other flags are invalid */ err_throw(VMERR_BAD_VAL_BIF); break; } /* handled */ return TRUE; } /* * property evaluator - propType */ int CVmObject::getp_proptype(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t, vm_obj_id_t *) { vm_val_t val; vm_prop_id_t prop; vm_obj_id_t source_obj; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the property address to test */ G_interpreter->pop_prop(vmg_ &val); prop = val.val.prop; /* get the property value */ if (!get_prop(vmg_ prop, &val, self, &source_obj, 0)) { /* the property isn't defined on the object - the result is nil */ retval->set_nil(); } else { /* * Fix up OBJX (execute-on-eval object reference) and BIFPTRX * (execute-on-eval built-in function pointer) values to reflect * the underlying datatype. Treat an anonymous function or dynamic * function as a CODEOFS value; treat a string object as a DSTRING * value. */ if (val.typ == VM_OBJX) { if (CVmObjAnonFn::is_anonfn_obj(vmg_ val.val.obj) || CVmDynamicFunc::is_dynfunc_obj(vmg_ val.val.obj)) val.typ = VM_CODEOFS; else if (CVmObjString::is_string_obj(vmg_ val.val.obj)) val.typ = VM_DSTRING; else val.typ = VM_OBJ; } else if (val.typ == VM_BIFPTRX) val.typ = VM_BIFPTR; /* set the return value to the property's datatype value */ retval->set_datatype(vmg_ &val); } /* handled */ return TRUE; } /* * property evaluator - getPropList */ int CVmObject::getp_get_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t, vm_obj_id_t *) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* push a self-reference for gc protection */ G_stk->push()->set_obj(self); /* build my property list */ build_prop_list(vmg_ self, retval); /* discard the gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* * Build a list of properties directly defined by this instance */ void CVmObject::build_prop_list(VMG_ vm_obj_id_t /*self*/, vm_val_t *retval) { /* * by default, object instances have no directly defined properties, * so create and return an empty list */ retval->set_obj(CVmObjList::create(vmg_ FALSE, (size_t)0)); } /* * property evaluator - getPropParams */ int CVmObject::getp_get_prop_params(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t, vm_obj_id_t *) { vm_val_t val; vm_prop_id_t prop; int min_args, opt_args, varargs; static CVmNativeCodeDesc desc(1); CVmObjList *lst; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the property address to test */ G_interpreter->pop_prop(vmg_ &val); prop = val.val.prop; /* push a self-reference while we're working */ G_stk->push()->set_obj(self); /* get the property information */ get_prop_interface(vmg_ self, prop, min_args, opt_args, varargs); /* * Allocate our return list. We need three elements: [minArgs, * optionalArgs, isVarargs]. */ retval->set_obj(CVmObjList::create(vmg_ FALSE, 3)); /* get the list object, properly cast */ lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* set the minimum argument count */ val.set_int(min_args); lst->cons_set_element(0, &val); /* set the optional argument count */ val.set_int(opt_args); lst->cons_set_element(1, &val); /* set the varargs flag */ val.set_logical(varargs); lst->cons_set_element(2, &val); /* discard our self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * Get the method interface to a given object property */ int CVmObject::get_prop_interface(VMG_ vm_obj_id_t self, vm_prop_id_t prop, int &min_args, int &opt_args, int &varargs) { vm_val_t val; vm_obj_id_t source_obj; /* presume we won't find an argument interface */ min_args = opt_args = 0; varargs = FALSE; /* get the property value */ if (!get_prop(vmg_ prop, &val, self, &source_obj, 0)) { /* we didn't find the property at all */ return FALSE; } else if (val.typ == VM_CODEOFS || val.typ == VM_OBJX || val.typ == VM_BIFPTRX) { /* * It's a direct pointer to static code, an object that we execute * on evaluation, or a built-in function pointer. Try getting a * pointer to the method header. CODEOFS and BIFPTRX values always * have method headers; OBJX values do if the underlying object is * invokable. If the object *isn't* invokable, it won't have a * method header; the only way this can happen is that we have a * String object as an OBJX property, which acts like a DSTRING * when evaluated. That has the default zero-argument interface, * so we can simply leave the return value with the defaults we've * already set up in this case. */ CVmFuncPtr func; if (func.set(vmg_ &val)) { /* it's an invokable - get the interface info from the header */ min_args = func.get_min_argc(); opt_args = func.get_opt_argc(); varargs = func.is_varargs(); } /* got it */ return TRUE; } else if (val.typ == VM_NATIVE_CODE) { /* get the arguments from the native code descriptor */ min_args = val.val.native_desc->min_argc_; opt_args = val.val.native_desc->opt_argc_; varargs = val.val.native_desc->varargs_; /* we found the property */ return TRUE; } else { /* * it's not a function, so there are no arguments, but we did find * the property */ return TRUE; } } /* * Call a static property */ int CVmObject::call_stat_prop(VMG_ vm_val_t *retval, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* not handled */ return FALSE; } /* * Get my image file version string */ const char *CVmObject::get_image_file_version(VMG0_) const { /* get the metaclass table entry */ vm_meta_entry_t *entry = G_meta_table->get_entry_from_reg( get_metaclass_reg()->get_reg_idx()); /* if we found it, get the image metaclass string from the entry */ if (entry != 0) { /* get the image metaclass name - "name/version" */ const char *n = entry->image_meta_name_; /* scan for the version slash */ n = strchr(n, '/'); /* if we found it, the version follows the slash */ if (n != 0) return n + 1; } /* we couldn't find the version data, so return an empty string */ return ""; } /* ------------------------------------------------------------------------ */ /* * CVmMetaclass implementation */ /* * Get a metaclass's super-metaclass. We'll look up our super-metaclass * in the metaclass registration table and return the IntrinsicClass * object we find referenced there. */ vm_obj_id_t CVmMetaclass::get_supermeta(VMG_ int idx) const { vm_meta_entry_t *entry; /* we only have one supermetaclass */ if (idx != 0) return VM_INVALID_OBJ; /* if I don't have a supermetaclass at all, return nil */ if (get_supermeta_reg() == 0) return VM_INVALID_OBJ; /* look up my supermetaclass entry */ entry = (G_meta_table->get_entry_from_reg( get_supermeta_reg()->get_reg_idx())); /* return the IntrinsicClass object that represents this metaclass */ return (entry != 0 ? entry->class_obj_ : VM_INVALID_OBJ); } /* * Determine if I'm an instance of the given object. Most metaclasses * inherit directly from CVmObject, so we'll return true only if the * object is the CVmObject IntrinsicClass object */ int CVmMetaclass::is_meta_instance_of(VMG_ vm_obj_id_t obj) const { /* iterate over my supermetaclasses */ for (CVmMetaclass *sc = get_supermeta_reg() ; sc != 0 ; sc = sc->get_supermeta_reg()) { /* look up the metaclass entry for this supermetaclass */ vm_meta_entry_t *entry = (G_meta_table->get_entry_from_reg(sc->get_reg_idx())); /* * if the object matches the current superclass's IntrinsicClass * object, we're a subclass of that object; otherwise we're not */ if (entry != 0 && entry->class_obj_ == obj) return TRUE; } /* it's not one of my superclasses */ return FALSE; } /* * Get my intrinsic class object */ vm_obj_id_t CVmMetaclass::get_class_obj(VMG0_) const { /* get my metacalss entry */ vm_meta_entry_t *entry = G_meta_table->get_entry_from_reg(get_reg_idx()); /* if we found our entry, return the class from the entry */ return (entry != 0 ? entry->class_obj_ : VM_INVALID_OBJ); } /* ------------------------------------------------------------------------ */ /* * The nil object. This is a special pseudo-object we create for object * #0, to provide graceful error handling if byte code tries to dereference * nil in any way. */ class CVmObjNil: public CVmObject { public: virtual class CVmMetaclass *get_metaclass_reg() const { err_throw(VMERR_NIL_DEREF); AFTER_ERR_THROW(return 0;) } virtual void set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { err_throw(VMERR_NIL_DEREF); } virtual int get_prop(VMG_ vm_prop_id_t, vm_val_t *, vm_obj_id_t, vm_obj_id_t *, uint *) { err_throw(VMERR_NIL_DEREF); AFTER_ERR_THROW(return FALSE;) } virtual int is_instance_of(VMG_ vm_obj_id_t) { err_throw(VMERR_NIL_DEREF); AFTER_ERR_THROW(return FALSE;) } virtual int get_superclass_count(VMG_ vm_obj_id_t) const { err_throw(VMERR_NIL_DEREF); AFTER_ERR_THROW(return 0;) } virtual vm_obj_id_t get_superclass(VMG_ vm_obj_id_t, int) const { err_throw(VMERR_NIL_DEREF); AFTER_ERR_THROW(return VM_INVALID_OBJ;) } virtual void enum_props(VMG_ vm_obj_id_t, void (*cb)(VMG_ void *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *), void *) { err_throw(VMERR_NIL_DEREF); } virtual int get_prop_interface(VMG_ vm_obj_id_t, vm_prop_id_t, int &, int &, int &) { err_throw(VMERR_NIL_DEREF); AFTER_ERR_THROW(return FALSE;) } virtual int inh_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t orig_target_obj, vm_obj_id_t defining_obj, vm_obj_id_t *source_obj, uint *argc) { err_throw(VMERR_NIL_DEREF); AFTER_ERR_THROW(return FALSE;) } virtual void build_prop_list(VMG_ vm_obj_id_t, vm_val_t *) { err_throw(VMERR_NIL_DEREF); } virtual void notify_delete(VMG_ int) { } virtual void mark_refs(VMG_ uint) { } virtual void remove_stale_weak_refs(VMG0_) { } virtual void notify_new_savept() { } virtual void apply_undo(VMG_ struct CVmUndoRecord *) { } virtual void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } virtual void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } virtual void load_from_image(VMG_ vm_obj_id_t, const char *, size_t) { } virtual void save_to_file(VMG_ class CVmFile *) { } virtual void restore_from_file(VMG_ vm_obj_id_t, class CVmFile *, class CVmObjFixup *) { } }; /* ------------------------------------------------------------------------ */ /* * object table implementation */ /* * construction - create an empty table */ CVmObjTable::CVmObjTable() { pages_ = 0; page_slots_ = 0; pages_used_ = 0; image_ptr_head_ = 0; globals_ = 0; global_var_head_ = 0; post_load_init_table_ = 0; } /* * allocate object table */ void CVmObjTable::init(VMG0_) { /* allocate the initial set of page slots */ page_slots_ = 10; pages_ = (CVmObjPageEntry **)t3malloc(page_slots_ * sizeof(*pages_)); /* if that failed, throw an error */ if (pages_ == 0) err_throw(VMERR_OUT_OF_MEMORY); /* no pages are in use yet */ pages_used_ = 0; /* there are no free objects yet */ first_free_ = 0; /* there's nothing in the GC work queue yet */ gc_queue_head_ = VM_INVALID_OBJ; /* nothing in the finalizer work queue yet */ finalize_queue_head_ = VM_INVALID_OBJ; /* we haven't allocated anything yet */ allocs_since_gc_ = 0; bytes_since_gc_ = 0; /* * Set the upper limit for new objects and allocated bytes between * garbage collection passes. * * In choosing these parameters, there's a trade-off. Running more * frequently reduces GC "stall time" of each pass (the time that the * system is unresponsive while scanning for unreachable objects), * because the total scanning on each pass will be smaller. More * frequent GC'ing also minimizes the working memory size by freeing * garbage objects sooner after they become unreachable. But running * more frequently reduces *overall* program speed, since we spend more * time in the collector in aggregate. We have to scan the reachable * objects on every pass, so the more frequently we run, the more * unproductive time we'll spend scanning the same reachable objects * over and over. * * TADS is specialized for highly interactive programs, so minimizing * stall time is the overriding priority. In practical testing, the * stall time is imperceptible up to at least 30,000 objects between * passes. (It stays below 5 ms per run on an average 2007 desktop. * The usual threshold of perceptibility is about 50-100 ms.) Overall * throughput seems to level out at about 20,000 objects between * passes. Beyond that, decreasing the frequency of gc runs yields * diminishing returns. The knee is the point where we're waiting long * enough between passes that unreachable objects dominate. Once * you're at that frequency, going longer between passes won't improve * speed much because you're typically just adding more garbage for the * next pass. The time you save by not doing a pass now gets canceled * out by making the next pass longer by giving it more garbage to * collect. This is in contrast to running too frequently, where * scanning reachable objects dominates: this time typically plateaus * the longer you go between passes, since the reachable set itself * tends to plateau in size over time, so while we're still in this * zone, the longer you go between passes the better.) * * Although 20,000 allocations seems to be the best frequency for * throughput for our test set, we currently set a somewhat more * conservative limit (i.e., collect garbage more frequently). This is * because the assumptions in our test set don't apply to all programs. * Some programs might allocate larger objects more frequently than our * tests, and in those cases we don't want the working set to grow too * quickly. More frequenty GC will help avoid this. * * In addition to the simple object allocation count, we also count * bytes allocated between passes, and collect garbage if we go above a * byte threshold independently of the object count. This is to ensure * that we collect garbage more frequently when very large objects are * being used, to keep the working set from growing too fast. Very * large working sets can hurt performance by reducing locality of * reference (for CPU caching) and even triggering OS-level disk * swapping. */ // max_allocs_between_gc_ = 17500; max_allocs_between_gc_ = 10000; max_bytes_between_gc_ = 6*1024*1024; /* enable the garbage collector */ gc_enabled_ = TRUE; /* there are no saved image data pointers yet */ image_ptr_head_ = 0; image_ptr_tail_ = 0; image_ptr_last_cnt_ = 0; /* no global pages yet */ globals_ = 0; /* no global variables yet */ global_var_head_ = 0; /* create the post_load_init() request table */ post_load_init_table_ = new CVmHashTable(128, new CVmHashFuncCS(), TRUE); /* allocate the first object page */ alloc_new_page(); /* * Initialize object #0 with the nil pseudo-object. This isn't an * actual object, but it provides graceful error handling if the byte * code attempts to invoke any methods on nil. */ new (vmg_ VM_INVALID_OBJ) CVmObjNil; CVmObjPageEntry *obj0 = get_entry(0); obj0->free_ = TRUE; obj0->in_root_set_ = TRUE; obj0->reachable_ = VMOBJ_REACHABLE; obj0->finalize_state_ = VMOBJ_UNFINALIZABLE; obj0->in_undo_ = FALSE; obj0->transient_ = FALSE; obj0->requested_post_load_init_ = FALSE; obj0->can_have_refs_ = FALSE; obj0->can_have_weak_refs_ = FALSE; } /* * delete object table */ CVmObjTable::~CVmObjTable() { /* show statistics if applicable */ IF_GC_STATS(gc_stats.display()); } /* * Clear the object table. This deletes all garbage-collected objects in * preparation for VM shutdown. */ void CVmObjTable::clear_obj_table(VMG0_) { /* delete all entries in the post-load initialization table */ post_load_init_table_->delete_all_entries(); /* go through the pages and delete the entries */ for (size_t i = 0 ; i < pages_used_ ; ++i) { /* delete all of the objects on the page */ int j; CVmObjPageEntry *entry; for (j = 0, entry = pages_[i] ; j < VM_OBJ_PAGE_CNT ; ++j, ++entry) { /* if this entry is still in use, delete it */ if (!entry->free_) entry->get_vm_obj()->notify_delete(vmg_ entry->in_root_set_); } } } /* * Delete the table. (We need to separate this out into a method so * that we can get access to the global variables.) */ void CVmObjTable::delete_obj_table(VMG0_) { /* delete each page we've allocated */ for (size_t i = 0 ; i < pages_used_ ; ++i) t3free(pages_[i]); /* free the master page table */ t3free(pages_); /* we no longer have any pages */ pages_ = 0; page_slots_ = 0; pages_used_ = 0; /* empty out the object lists */ first_free_ = VM_INVALID_OBJ; gc_queue_head_ = VM_INVALID_OBJ; finalize_queue_head_ = VM_INVALID_OBJ; /* clear the gc statistics */ allocs_since_gc_ = 0; bytes_since_gc_ = 0; /* delete each object image data pointer page */ for (vm_image_ptr_page *ip_page = image_ptr_head_, *ip_next = 0 ; ip_page != 0 ; ip_page = ip_next) { /* remember the next page (before we delete this one) */ ip_next = ip_page->next_; /* delete this page */ t3free(ip_page); } /* there's nothing left in the image data list */ image_ptr_head_ = image_ptr_tail_ = 0; /* delete the linked list of globals */ if (globals_ != 0) { delete globals_; globals_ = 0; } /* * delete any left-over global variables (these should always be * deleted by subsystems before we get here, but this is the last * chance, so clean them up manually) */ while (global_var_head_ != 0) delete_global_var(global_var_head_); /* delete the post-load initialization table */ delete post_load_init_table_; post_load_init_table_ = 0; } /* * Enable or disable garbage collection */ int CVmObjTable::enable_gc(VMG_ int enable) { /* remember the old status for returning */ int old_enable = gc_enabled_; /* set the new status */ gc_enabled_ = enable; /* * if we're enabling GC after it was previously disabled, check to * see if we should perform a GC pass now (but don't count this as a * separate allocation) */ if (!old_enable && enable) alloc_check_gc(vmg_ FALSE); /* return the previous status */ return old_enable; } /* * allocate a new object ID */ vm_obj_id_t CVmObjTable::alloc_obj(VMG_ int in_root_set, int can_have_refs, int can_have_weak_refs) { /* count the allocation and maybe perform garbage collection */ alloc_check_gc(vmg_ TRUE); /* if the free list is empty, allocate a new page of object entries */ if (first_free_ == VM_INVALID_OBJ) alloc_new_page(); /* remember the first item in the free list - this is our result */ vm_obj_id_t ret = first_free_; /* get the object table entry for the ID */ CVmObjPageEntry *entry = get_entry(ret); /* unlink new entry from the free list */ first_free_ = entry->next_obj_; if (entry->next_obj_ != VM_INVALID_OBJ) get_entry(entry->next_obj_)->ptr_.prev_free_ = entry->ptr_.prev_free_; /* initialize the entry */ init_entry_for_alloc(ret, entry, in_root_set, can_have_refs, can_have_weak_refs); /* return the free object */ return ret; } /* * Run garbage collection before allocating an object */ void CVmObjTable::gc_before_alloc(VMG0_) { /* count it if in statistics mode */ IF_GC_STATS(gc_stats.begin_pass()); /* run a full garbage collection pass */ gc_pass_init(vmg0_); gc_pass_finish(vmg0_); /* count it if in statistics mode */ IF_GC_STATS(gc_stats.end_pass()); } /* * Allocate an object with a particular ID */ void CVmObjTable::alloc_obj_with_id(vm_obj_id_t id, int in_root_set, int can_have_refs, int can_have_weak_refs) { CVmObjPageEntry *entry; /* * if the page containing the given object ID hasn't been allocated, * allocate pages until it's available */ while (id >= pages_used_ * VM_OBJ_PAGE_CNT) alloc_new_page(); /* get the object table entry for the ID */ entry = get_entry(id); /* if the desired object ID is already taken, it's an error */ if (!entry->free_) err_throw(VMERR_OBJ_IN_USE); /* unlink the item - set the previous item's forward pointer... */ if (entry->ptr_.prev_free_ == VM_INVALID_OBJ) first_free_ = entry->next_obj_; else get_entry(entry->ptr_.prev_free_)->next_obj_ = entry->next_obj_; /* ...and the next items back pointer */ if (entry->next_obj_ != VM_INVALID_OBJ) get_entry(entry->next_obj_)->ptr_.prev_free_ = entry->ptr_.prev_free_; /* initialize the entry for allocation */ init_entry_for_alloc(id, entry, in_root_set, can_have_refs, can_have_weak_refs); } /* * Initialize an object table entry that we've just allocated */ void CVmObjTable::init_entry_for_alloc(vm_obj_id_t id, CVmObjPageEntry *entry, int in_root_set, int can_have_refs, int can_have_weak_refs) { /* mark the entry as in use */ entry->free_ = FALSE; /* no undo savepoint has been created since the object was created */ entry->in_undo_ = FALSE; /* mark the object as being in the root set if appropriate */ entry->in_root_set_ = in_root_set; /* presume it's an ordinary persistent object */ entry->transient_ = FALSE; /* presume it won't need post-load initialization */ entry->requested_post_load_init_ = FALSE; /* set the GC characteristics as requested */ entry->can_have_refs_ = can_have_refs; entry->can_have_weak_refs_ = can_have_weak_refs; /* * Mark the object as initially unreachable and unfinalizable. It's * not necessarily really unreachable at this point, but we mark it * as such because the garbage collector hasn't explicitly traced * the object to be reachable. The initial conditions for garbage * collection are that all objects not in the root set and not * finalizable are marked as unreachable; since we're not in a gc * pass right now (we can't be - memory cannot be allocated during a * gc pass), we know that we must establish initial gc conditions * for the next time we start a gc pass. */ entry->reachable_ = VMOBJ_UNREACHABLE; entry->finalize_state_ = VMOBJ_UNFINALIZABLE; /* add it to the GC work queue for the next GC pass */ if (in_root_set) add_to_gc_queue(id, entry, VMOBJ_REACHABLE); } #if 0 // moved to in-line in header, since it's called *a lot* /* * get the page entry for a given ID */ CVmObjPageEntry *CVmObjTable::get_entry(vm_obj_id_t id) const { size_t main_idx; size_t page_idx; /* get the index of the page in the main array */ main_idx = (size_t)(id >> VM_OBJ_PAGE_CNT_LOG2); /* get the index within the page */ page_idx = (size_t)(id & (VM_OBJ_PAGE_CNT - 1)); /* get the object */ return &pages_[main_idx][page_idx]; } #endif /* * Delete an object, given the object table entry. */ void CVmObjTable::delete_entry(VMG_ vm_obj_id_t id, CVmObjPageEntry *entry) { /* mark the object table entry as free */ entry->free_ = TRUE; /* it's not in the root set if it's free */ entry->in_root_set_ = FALSE; /* * notify the object that it's being deleted - this will let the * object release any additional resources (such as variable-size * heap space) that it's holding */ entry->get_vm_obj()->notify_delete(vmg_ FALSE); /* * remove any post-load initialization request for the object, if it * ever requested post-load initialization */ if (entry->requested_post_load_init_) remove_post_load_init(id); /* link this object into the head of the free list */ entry->next_obj_ = first_free_; /* link the previous head back to this object */ if (first_free_ != VM_INVALID_OBJ) get_entry(first_free_)->ptr_.prev_free_ = id; /* this object doesn't have a previous entry */ entry->ptr_.prev_free_ = VM_INVALID_OBJ; /* it's now the first entry in the list */ first_free_ = id; } /* * allocate a new page of objects */ void CVmObjTable::alloc_new_page() { size_t i; vm_obj_id_t id; CVmObjPageEntry *entry; /* first, make sure we have room in the master page list */ if (pages_used_ == page_slots_) { /* increase the number of page slots */ page_slots_ += 10; /* allocate space for the increased number of slots */ pages_ = (CVmObjPageEntry **)t3realloc( pages_, page_slots_ * sizeof(*pages_)); } /* allocate a new page */ pages_[pages_used_] = (CVmObjPageEntry *)t3malloc(VM_OBJ_PAGE_CNT * sizeof(*pages_[0])); /* if that failed, throw an error */ if (pages_[pages_used_] == 0) err_throw(VMERR_OUT_OF_MEMORY); /* * initialize the new page to be entirely free - add each element of * the page to the free list */ entry = pages_[pages_used_]; i = 0; id = pages_used_ * VM_OBJ_PAGE_CNT; /* * if this is the start of the very first page, leave off the first * object, since its ID is invalid */ if (id == VM_INVALID_OBJ) { /* mark the object as free so we don't try to free it later */ entry->free_ = TRUE; /* ...and it's certainly not a collectable object */ entry->in_root_set_ = TRUE; /* skip it for the impending free list construction */ ++i; ++entry; ++id; } /* loop over each object in this page */ for ( ; i < VM_OBJ_PAGE_CNT ; ++i, ++entry, ++id) { /* set the forward pointer in this object */ entry->next_obj_ = first_free_; /* set the back pointer in the previous object in the list */ if (first_free_ != VM_INVALID_OBJ) get_entry(first_free_)->ptr_.prev_free_ = id; /* there's nothing before this entry yet */ entry->ptr_.prev_free_ = VM_INVALID_OBJ; /* this is now the head of the list */ first_free_ = id; /* mark it free */ entry->free_ = TRUE; /* presume it's not part of the root set */ entry->in_root_set_ = FALSE; /* * mark it initially unreachable and finalized, since it's not * even allocated yet */ entry->reachable_ = VMOBJ_UNREACHABLE; entry->finalize_state_ = VMOBJ_FINALIZED; } /* count the new page we've allocated */ ++pages_used_; } /* * Add an object to the list of machine globals. */ void CVmObjTable::add_to_globals(vm_obj_id_t obj) { CVmObjGlobPage *pg; /* * if this is a root set object, there is no need to mark it as * global, since it is inherently uncollectable as an image file * object to begin with */ if (get_entry(obj)->in_root_set_) return; /* if we have any global pages allocated, try adding to the head page */ if (globals_ != 0 && globals_->add_entry(obj)) { /* we successfully added it to the head page - we're done */ return; } /* * either the head page is full, or we haven't allocated any global * pages at all yet; in either case, allocate a new page and link it * at the head of our list */ pg = new CVmObjGlobPage(); pg->nxt_ = globals_; globals_ = pg; /* * add the object to the new page - it must fit, since the new page is * empty */ globals_->add_entry(obj); } /* * Collect all garbage. This does a complete garbage collection pass, * returning only after all unreachable objects have been collected. If * incremental garbage collection is not required, the caller can simply * invoke this routine to do the entire operation in a single call. */ void CVmObjTable::gc_full(VMG0_) { /* count it if in statistics mode */ IF_GC_STATS(gc_stats.begin_pass()); /* * run the initial pass to mark globally-reachable objects, then run * the garbage collector to completion */ gc_pass_init(vmg0_); gc_pass_finish(vmg0_); /* count it if in statistics mode */ IF_GC_STATS(gc_stats.end_pass()); } /* * Garbage collector - initialize. Add all globally-reachable objects * to the work queue. * * We assume that the following initial conditions hold: all objects * except root set objects are marked as unreferenced, and all root set * objects are marked as referenced; all root set objects are in the GC * work queue. So, we don't need to worry about finding root objects or * initializing the other objects at this point. */ void CVmObjTable::gc_pass_init(VMG0_) { /* * reset the allocations-since-gc counter, since this is now the * last gc pass, and we obviously haven't performed any allocations * since this gc pass yet */ allocs_since_gc_ = 0; bytes_since_gc_ = 0; /* trace objects reachable from the stack */ gc_trace_stack(vmg0_); /* trace objects reachable from imports */ gc_trace_imports(vmg0_); /* trace objects reachable from machine globals */ gc_trace_globals(vmg0_); /* * Process undo records - for each undo record, mark any referenced * objects as reachable. Undo records are part of the root set. */ G_undo->gc_mark_refs(vmg0_); } /* * Garbage collection - continue processing the work queue. This * processes a set of entries from the work queue. This routine can be * used for incremental garbage collection: after calling * gc_pass_init(), the caller can repeatedly invoke this routine until * it returns false. Since this routine will return after a short time * even if there's more work left to do, other operations (such as * processing user input) can be interleaved in a single thread with * garbage collection. * * The actual number of entries that we process is configurable at VM * compile-time via VM_GC_WORK_INCREMENT. The point of running the GC * incrementally is to allow GC work to be interleaved with long-running * user I/O operations (such as reading a line of text from the * keyboard) in the foreground thread, so the work increment should be * chosen so that each call to this routine completes quickly enough * that the user will perceive no delay. * * Returns true if more work remains to be done, false if not. The * caller should invoke this routine repeatedly until it returns false. */ int CVmObjTable::gc_pass_continue(VMG_ int trace_transient) { int cnt; /* * keep going until we exhaust the queue or run for our maximum * number of iterations */ for (cnt = VM_GC_WORK_INCREMENT ; cnt != 0 && gc_queue_head_ != VM_INVALID_OBJ ; --cnt) { vm_obj_id_t cur; CVmObjPageEntry *entry; /* get the next item from the work queue */ cur = gc_queue_head_; /* get this object's entry */ entry = get_entry(cur); /* remove this entry from the work queue */ gc_queue_head_ = entry->next_obj_; /* * Tell this object to mark its references. Mark the referenced * objects with the same state as this object, if they're not * already marked with a stronger state. * * If we're not tracing transients, do not trace this object if * it's transient. */ if (trace_transient || !entry->transient_) entry->get_vm_obj()->mark_refs(vmg_ entry->reachable_); } /* * return true if there's more work to do; there's more work to do * if we have any objects left in the gc work queue */ return gc_queue_head_ != VM_INVALID_OBJ; } /* * Finish garbage collection. We'll finish any work remaining in the * work queue, so this is safe to call at any time after gc_pass_init(), * after any number of calls (even zero) to gc_pass_continue(). */ void CVmObjTable::gc_pass_finish(VMG0_) { CVmObjPageEntry **pg; CVmObjPageEntry *entry; size_t i; size_t j; vm_obj_id_t id; /* * Make sure we're done processing the work queue -- keep calling * gc_pass_continue() until it indicates that it's finished. If * we're skipping finalizers, stop as soon as the state structure * indicates that we've started running finalizers. */ gc_trace_work_queue(vmg_ TRUE); /* * We've now marked everything that's reachable from the root set as * VMOBJ_REACHABLE. We can therefore determine the set of objects * that are newly 'finalizable' - an object becomes finalizable when * it was previously 'unfinalizable' and is not reachable, because we * can finalize an object any time after it first becomes unreachable. * So, scan all objects for eligibility for the 'finalizable' * transition, and make the transition in those objects. */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { /* go through each entry on this page */ for (j = VM_OBJ_PAGE_CNT, entry = *pg ; j > 0 ; --j, ++entry, ++id) { /* * if this entry is not free and is not in the root set, check * to see if its finalization status is changing */ if (!entry->free_ && !entry->in_root_set_) { /* * If the entry is not reachable, and was previously * unfinalizable, it is now finalizable. * * Note that an object must actually be reachable to avoid * become finalizable at this point. Not only do * unreachable objects become finalizable, but * 'f-reachable' objects do, too, since these are only * reachable from other finalizable objects. */ if (entry->reachable_ != VMOBJ_REACHABLE && entry->finalize_state_ == VMOBJ_UNFINALIZABLE) { /* * This object is newly finalizable. If it has no * finalizer, the object can go directly to the * 'finalized' state; otherwise, add it to the queue * of objects with pending finalizers. */ if (entry->get_vm_obj()->has_finalizer(vmg_ id)) { /* * This object is not reachable from the root set * and was previously unfinalizable. Make the * object finalizable. */ entry->finalize_state_ = VMOBJ_FINALIZABLE; } else { /* * the entry has no finalizer, so we can make this * object 'finalized' immediately */ entry->finalize_state_ = VMOBJ_FINALIZED; } /* count it as a collected object */ IF_GC_STATS(gc_stats.count_free()); } /* * If this object is finalizable, add it to the work queue * in state "f-reachable." We must mark everything this * object references, directly or indirectly, as * f-reachable, which we'll do with another pass through * the gc queue momentarily. */ if (entry->finalize_state_ == VMOBJ_FINALIZABLE) { /* * this object and all of the objects it references * are "f-reachable" */ add_to_gc_queue(id, entry, VMOBJ_F_REACHABLE); } } } } /* * During the scan above, we put all of the finalizable objects in the * work queue in reachability state 'f-reachable'. (Actually, * finalizable objects that were fully reachable were not put in the * work queue, because they are in a stronger reachability state that * we've already fully scanned.) Trace the work queue so that we mark * everything reachable indirectly from an f-reachable object as also * being at least f-reachable. */ gc_trace_work_queue(vmg_ TRUE); /* * We have now marked everything that's fully reachable as being in * state 'reachable', and everything that's reachable from a * finalizable object as being in state 'f-reachable'. Anything that * is still in state 'unreachable' is garbage and can be collected. */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { /* go through each entry on this page */ for (j = VM_OBJ_PAGE_CNT, entry = *pg ; j > 0 ; --j, ++entry, ++id) { /* if it's not already free, process it */ if (!entry->free_) { /* if the object is deletable, delete it */ if (entry->is_deletable()) { /* * This object is completely unreachable, and it has * already been finalized. This means there is no * possibility that the object could ever become * reachable again, hence we can discard the object. */ delete_entry(vmg_ id, entry); } else { /* * The object is reachable, so keep it around. Since * it's staying around, and since we know which * objects we're deleting and which are staying, we * can ask this object to remove all of its "stale * weak references" - that is, weak references to * objects that we're about to delete. Don't bother * notifying the object if it's incapable of keeping * weak references. */ if (entry->can_have_weak_refs_) entry->get_vm_obj()->remove_stale_weak_refs(vmg0_); /* * If this object is finalizable, put it in the * finalizer queue, so that we can run its finalizer * when we're done scanning the table. */ if (entry->finalize_state_ == VMOBJ_FINALIZABLE) add_to_finalize_queue(id, entry); /* * restore initial conditions for this object, so that * we're properly set up for the next GC pass */ gc_set_init_conditions(id, entry); } } } } /* * Go through the undo records and clear any stale weak references * contained in the undo list. */ G_undo->gc_remove_stale_weak_refs(vmg0_); /* * All of the finalizable objects are now in the finalizer queue. Run * through the finalizer queue and run each such object's finalizer. */ run_finalizers(vmg0_); } /* * Trace all objects reachable from the work queue. */ void CVmObjTable::gc_trace_work_queue(VMG_ int trace_transient) { /* * trace everything reachable directly from the work queue, until we * exhaust the queue */ while (gc_pass_continue(vmg_ trace_transient)) ; } /* * Garbage collection: trace objects reachable from the stack */ void CVmObjTable::gc_trace_stack(VMG0_) { size_t i; size_t depth; vm_val_t *val; /* * Process the stack. For each stack element that refers to an * object, mark the object as referenced and add it to the work * queue. * * Note that it makes no difference in what order we process the * stack elements; we go from depth down to 0 merely as a trivial * micro-optimization to avoid evaluating the stack depth on every * iteration of the loop. */ for (i = 0, depth = G_stk->get_depth() ; i < depth ; ++i) { /* * If this element refers to an object, and the object hasn't * already been marked as referenced, mark it as reachable and * add it to the work queue. * * Note that we only have to worry about objects here. We don't * have to worry about constant lists, even though they can * contain object references, because any object reference in a * constant list must be a root set object, and we've already * processed all root set objects. */ val = G_stk->get(i); if (val->typ == VM_OBJ && val->val.obj != VM_INVALID_OBJ) add_to_gc_queue(val->val.obj, VMOBJ_REACHABLE); } } /* * Trace objects reachable from imports */ void CVmObjTable::gc_trace_imports(VMG0_) { /* * generate the list of object imports; for each one, if we have a * valid object for the import, mark it as reachable */ #define VM_IMPORT_OBJ(sym, mem) \ if (G_predef->mem != VM_INVALID_OBJ) \ add_to_gc_queue(G_predef->mem, VMOBJ_REACHABLE); #define VM_NOIMPORT_OBJ(sym, mem) VM_IMPORT_OBJ(sym, mem) #include "vmimport.h" } /* * Garbage collection: trace objects reachable from the machine globals */ void CVmObjTable::gc_trace_globals(VMG0_) { CVmObjGlobPage *pg; vm_val_t *val; vm_globalvar_t *var; /* trace each page of globals */ for (pg = globals_ ; pg != 0 ; pg = pg->nxt_) { size_t i; vm_obj_id_t *objp; /* trace each item on this page */ for (objp = pg->objs_, i = pg->used_ ; i != 0 ; ++objp, --i) { /* trace this global */ add_to_gc_queue(*objp, VMOBJ_REACHABLE); } } /* the return value register (R0) is a machine global */ val = G_interpreter->get_r0(); if (val->typ == VM_OBJ && val->val.obj != VM_INVALID_OBJ) add_to_gc_queue(val->val.obj, VMOBJ_REACHABLE); /* trace the global variables defined by other subsystems */ for (var = global_var_head_ ; var != 0 ; var = var->nxt) { /* if this global variable contains an object, trace it */ if (var->val.typ == VM_OBJ && var->val.val.obj != VM_INVALID_OBJ) add_to_gc_queue(var->val.val.obj, VMOBJ_REACHABLE); } } #if 0 // moved inline, as it's small and is called a fair amount /* * Set the initial conditions for an object, in preparation for the next * GC pass. */ void CVmObjTable::gc_set_init_conditions(vm_obj_id_t id, CVmObjPageEntry *entry) { /* * Mark the object as unreachable -- at the start of each GC pass, * all non-root-set objects must be marked unreachable. */ entry->reachable_ = VMOBJ_UNREACHABLE; /* * If it's in the root set, add it to the GC work queue -- all * root-set objects must be in the work queue and marked as reachable * at the start of each GC pass. * * If the object is not in the root set, check to see if it's * finalizable. If so, add it to the finalizer queue, so that we * eventually run its finalizer. */ if (entry->in_root_set_) add_to_gc_queue(id, entry, VMOBJ_REACHABLE); } #endif /* * Run finalizers */ void CVmObjTable::run_finalizers(VMG0_) { /* keep going until we run out of work or reach our work limit */ while (finalize_queue_head_ != VM_INVALID_OBJ) { CVmObjPageEntry *entry; vm_obj_id_t id; /* get the next object from the queue */ id = finalize_queue_head_; /* get the entry */ entry = get_entry(id); /* remove the entry form the queue */ finalize_queue_head_ = entry->next_obj_; /* mark the object as finalized */ entry->finalize_state_ = VMOBJ_FINALIZED; /* * the entry is no longer in any queue, so we must mark it as * unreachable -- this ensures that the initial conditions are * correct for the next garbage collection pass, since all * objects not in the work queue must be marked as unreachable * (it doesn't matter whether the object is actually reachable; * the garbage collector will make that determination when it * next runs) */ entry->reachable_ = VMOBJ_UNREACHABLE; /* invoke its finalizer */ entry->get_vm_obj()->invoke_finalizer(vmg_ id); } } /* * Receive notification that we're creating a new undo savepoint */ void CVmObjTable::notify_new_savept() { CVmObjPageEntry **pg; CVmObjPageEntry *entry; size_t i; size_t j; vm_obj_id_t id; /* go through each page of objects */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { /* go through each entry on this page */ for (j = VM_OBJ_PAGE_CNT, entry = *pg ; j > 0 ; --j, ++entry, ++id) { /* if this entry is active, tell it about the new savepoint */ if (!entry->free_) { /* * the object existed at the start of this savepoint, so * it must keep undo information throughout the savepoint */ entry->in_undo_ = TRUE; /* notify the object of the new savepoint */ entry->get_vm_obj()->notify_new_savept(); } } } } /* * Call a callback for each object in the table */ void CVmObjTable::for_each(VMG_ void (*func)(VMG_ vm_obj_id_t, void *), void *ctx) { CVmObjPageEntry **pg; size_t i; vm_obj_id_t id; /* go through each page in the object table */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { /* start at the start of the page */ size_t j = VM_OBJ_PAGE_CNT; CVmObjPageEntry *entry = *pg; /* go through each entry on this page */ for ( ; j > 0 ; --j, ++entry, ++id) { /* if this entry is in use, invoke the callback */ if (!entry->free_) (*func)(vmg_ id, ctx); } } } /* * Apply undo */ void CVmObjTable::apply_undo(VMG_ CVmUndoRecord *rec) { /* tell the object to apply the undo */ if (rec->obj != VM_INVALID_OBJ) get_obj(rec->obj)->apply_undo(vmg_ rec); } /* * Scan all objects and add metaclass entries to the metaclass * dependency table for any metaclasses of which there are existing * instances. */ void CVmObjTable::add_metadeps_for_instances(VMG0_) { CVmObjPageEntry **pg; size_t i; vm_obj_id_t id; /* go through each page in the object table */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { size_t j; CVmObjPageEntry *entry; /* start at the start of the page, but skip object ID = 0 */ j = VM_OBJ_PAGE_CNT; entry = *pg; /* go through each entry on this page */ for ( ; j > 0 ; --j, ++entry, ++id) { /* if this entry is in use, add its metaclass if necessary */ if (!entry->free_) G_meta_table->add_entry_if_new( entry->get_vm_obj()->get_metaclass_reg()->get_reg_idx(), 0, VM_INVALID_PROP, VM_INVALID_PROP); } } } /* ------------------------------------------------------------------------ */ /* * creation */ CVmObjFixup::CVmObjFixup(ulong entry_cnt) { uint i; /* remember the number of entries */ cnt_ = entry_cnt; /* no entries are used yet */ used_ = 0; /* if we have no entries, there's nothing to do */ if (cnt_ == 0) { arr_ = 0; pages_ = 0; return; } /* calculate the number of subarrays we need */ pages_ = (entry_cnt + VMOBJFIXUP_SUB_SIZE - 1) / VMOBJFIXUP_SUB_SIZE; /* allocate the necessary number of subarrays */ arr_ = (obj_fixup_entry **)t3malloc(pages_ * sizeof(arr_[0])); /* allocate the subarrays */ for (i = 0 ; i < pages_ ; ++i) { size_t cur_cnt; /* * allocate a full page, except for the last page, which might be * only partially used */ cur_cnt = VMOBJFIXUP_SUB_SIZE; if (i + 1 == pages_) cur_cnt = ((entry_cnt - 1) % VMOBJFIXUP_SUB_SIZE) + 1; /* allocate it */ arr_[i] = (obj_fixup_entry *)t3malloc(cur_cnt * sizeof(arr_[i][i])); } } /* * deletion */ CVmObjFixup::~CVmObjFixup() { uint i; /* if we never allocated an array, there's nothing to do */ if (arr_ == 0) return; /* delete each subarray */ for (i = 0 ; i < pages_ ; ++i) t3free(arr_[i]); /* delete the main array */ t3free(arr_); } /* * add a fixup */ void CVmObjFixup::add_fixup(vm_obj_id_t old_id, vm_obj_id_t new_id) { obj_fixup_entry *entry; /* allocate the next available entry */ entry = get_entry(used_++); /* store it */ entry->old_id = old_id; entry->new_id = new_id; } /* * translate an ID */ vm_obj_id_t CVmObjFixup::get_new_id(VMG_ vm_obj_id_t old_id) { /* * if it's a root-set object, don't bother even trying to translate * it, because root-set objects have stable ID's that never change on * saving or restoring */ if (G_obj_table->is_obj_id_valid(old_id) && G_obj_table->is_obj_in_root_set(old_id)) return old_id; /* find the entry by the object ID */ obj_fixup_entry *entry = find_entry(old_id); /* * if we found it, return the new ID; otherwise, return the old ID * unchanged, since the ID evidently doesn't require mapping */ return (entry != 0 ? entry->new_id : old_id); } /* * find an entry given the old object ID */ obj_fixup_entry *CVmObjFixup::find_entry(vm_obj_id_t old_id) { /* do a binary search for the entry */ ulong cur; ulong lo = 0; ulong hi = cnt_ - 1; while (lo <= hi) { /* split the difference */ cur = lo + (hi - lo)/2; obj_fixup_entry *entry = get_entry(cur); /* is it the one we're looking for? */ if (entry->old_id == old_id) { /* it's the one - return the entry */ return entry; } else if (old_id > entry->old_id) { /* we need to go higher */ lo = (cur == lo ? cur + 1 : cur); } else { /* we need to go lower */ hi = (cur == hi ? cur - 1 : cur); } } /* didn't find it */ return 0; } /* * Fix a DATAHOLDER value */ void CVmObjFixup::fix_dh(VMG_ char *dh) { /* if it's an object, translate the ID */ if (vmb_get_dh_type(dh) == VM_OBJ) { vm_obj_id_t id; /* get the object value */ id = vmb_get_dh_obj(dh); /* translate it */ id = get_new_id(vmg_ id); /* * if it's invalid, set the dataholder value to nil; otherwise, * set it to the new object ID */ if (id == VM_INVALID_OBJ) vmb_put_dh_nil(dh); else vmb_put_dh_obj(dh, id); } } /* * Fix up an array of DATAHOLDER values. */ void CVmObjFixup::fix_dh_array(VMG_ char *arr, size_t cnt) { /* scan the array of dataholders */ for ( ; cnt != 0 ; --cnt, arr += VMB_DATAHOLDER) fix_dh(vmg_ arr); } /* * Fix a portable VMB_OBJECT_ID field */ void CVmObjFixup::fix_vmb_obj(VMG_ char *p) { vm_obj_id_t id; /* get the old ID */ id = vmb_get_objid(p); /* fix it up */ id = get_new_id(vmg_ id); /* store it back */ vmb_put_objid(p, id); } /* * Fix an array of portable VMB_OBJECT_ID fields */ void CVmObjFixup::fix_vmb_obj_array(VMG_ char *p, size_t cnt) { /* scan the array */ for ( ; cnt != 0 ; --cnt, p += VMB_OBJECT_ID) fix_vmb_obj(vmg_ p); } /* ------------------------------------------------------------------------ */ /* * Save/restore */ /* * table-of-contents flags */ /* object is transient (so the object isn't saved to the state file) */ #define VMOBJ_TOC_TRANSIENT 0x0001 /* * Save state to a file */ void CVmObjTable::save(VMG_ CVmFile *fp) { CVmObjPageEntry **pg; CVmObjPageEntry *entry; size_t i; size_t j; vm_obj_id_t id; size_t toc_cnt; size_t save_cnt; long toc_cnt_pos; long end_pos; /* * Before we save, perform a full GC pass. This will ensure that we * have removed all objects that are referenced only weakly, and * cleaned up the weak references to them; this is important because * we don't trace weak references for the purposes of calculating the * set of objects that must be saved, and hence won't save any objects * that are only weakly referenced, which would leave dangling * references in the saved state if those weak references weren't * cleaned up before the objects containing them are saved. */ gc_full(vmg0_); /* * Make sure that all of the metaclasses that we are actually using * are in the metaclass dependency table. We store the table in the * file, because the table provides the mapping from file-local * metaclass ID's to actual metaclasses; we must make sure that the * table is complete (i.e., contains an entry for each metaclass of * which there is an instance) before storing the table. */ add_metadeps_for_instances(vmg0_); /* save the metaclass table */ G_meta_table->write_to_file(fp); /* * Figure out what objects we need to save. We only need to save * objects that are directly reachable from the root object set, from * the imports, or from the globals. * * We don't need to save objects that are only accessible from the * undo, because we don't save any undo information in the file. We * also don't need to save any objects that are reachable only from * the stack, since the stack is inherently transient. * * Note that we don't need to trace from transient objects, since we * won't be saving the transient objects and thus won't need to save * anything referenced only from transient objects. * * So, we merely trace objects reachable from the imports, globals, * and work queue. At any time between GC passes, the work queue * contains the complete list of root-set objects, hence we can simply * trace from the current work queue. */ gc_trace_imports(vmg0_); gc_trace_globals(vmg0_); gc_trace_work_queue(vmg_ FALSE); /* * Before we save the objects themselves, save a table of contents of * the dynamically-allocated objects to be saved. This table of * contents will allow us to fix up references to objects on reloading * the file with the new object numbers we assign them at that time. * First, write a placeholder for the table of contents entry count. * * Note that we must store the table of contents in ascending order of * object ID. This happens naturally, since we scan the table in * order of object ID. */ toc_cnt = 0; save_cnt = 0; toc_cnt_pos = fp->get_pos(); fp->write_uint4(0); /* now scan the object pages and save the table of contents */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { /* scan all objects on this page */ for (j = VM_OBJ_PAGE_CNT, entry = *pg ; j > 0 ; -- j, ++entry, ++id) { /* * If the entry is currently reachable, and it was dynamically * allocated (which means it's not in the root set), then add * it to the table of contents. Note that we won't * necessarily be saving the object, because the object could * be transient - but if so, then we still want the record of * the transient object, so we'll know on reloading that the * object is no longer available. */ if (!entry->free_ && entry->reachable_ == VMOBJ_REACHABLE && !entry->in_root_set_) { ulong flags; /* set up the flags */ flags = 0; if (entry->transient_) flags |= VMOBJ_TOC_TRANSIENT; /* write the object ID and flags */ fp->write_uint4(id); fp->write_uint4(flags); /* count it */ ++toc_cnt; } /* if it's saveable, count it among the saveable objects */ if (entry->is_saveable()) ++save_cnt; } } /* go back and fix up the size prefix for the table of contents */ end_pos = fp->get_pos(); fp->set_pos(toc_cnt_pos); fp->write_uint4(toc_cnt); fp->set_pos(end_pos); /* write the saveable object count, which we calculated above */ fp->write_uint4(save_cnt); /* scan all object pages, and save each reachable object */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { /* scan all objects on this page */ for (j = VM_OBJ_PAGE_CNT, entry = *pg ; j > 0 ; -- j, ++entry, ++id) { /* if this object is saveable, save it */ if (entry->is_saveable()) { uint idx; char buf[2]; /* write the object ID */ fp->write_uint4(id); /* store the root-set flag */ buf[0] = (entry->in_root_set_ != 0); /* store the dependency table index */ idx = entry->get_vm_obj()-> get_metaclass_reg()->get_reg_idx(); buf[1] = (char)G_meta_table->get_dependency_index(idx); /* write the data */ fp->write_bytes(buf, 2); /* save the metaclass-specific state */ entry->get_vm_obj()->save_to_file(vmg_ fp); } /* * restore this object to the appropriate conditions in * preparation for the next GC pass, so that we leave things * as we found them -- saving the VM's state thus has no * effect on the VM's state */ gc_set_init_conditions(id, entry); } } } /* * Restore state from a file */ int CVmObjTable::restore(VMG_ CVmFile *fp, CVmObjFixup **fixups) { int err; ulong cnt; /* presume we won't create a fixup table */ *fixups = 0; /* load the metaclass table */ if ((err = G_meta_table->read_from_file(fp)) != 0) return err; /* * Reset all objects to the initial image file load state. Note that * we wait until after we've read the metaclass table to reset the * objects, because any intrinsic class objects in the root set will * need to re-initialize their presence in the metaclass table, which * they can't do until after the metaclass table has itself been * reloaded. */ G_obj_table->reset_to_image(vmg0_); /* read the size of the table of contents */ cnt = fp->read_uint4(); /* allocate the fixup table */ *fixups = new CVmObjFixup(cnt); /* read the fixup table */ for ( ; cnt != 0 ; --cnt) { vm_obj_id_t old_id; vm_obj_id_t new_id; ulong flags; /* read the next entry */ old_id = (vm_obj_id_t)fp->read_uint4(); flags = fp->read_uint4(); /* * Allocate a new object ID for this entry. If the object was * transient, then it won't actually have been saved, so we must * fix up references to the object to nil. */ if (!(flags & VMOBJ_TOC_TRANSIENT)) new_id = vm_new_id(vmg_ FALSE); else new_id = VM_INVALID_OBJ; /* * Add the entry. Note that the table of contents is stored in * ascending order of old ID (i.e., the ID's in the saved state * file's numbering system); this is the same ordering required by * the fixup table, so we can simply read entries from the file * and add them directly to the fixup table. */ (*fixups)->add_fixup(old_id, new_id); } /* read the number of saved objects */ cnt = fp->read_uint4(); /* read the objects */ for ( ; cnt != 0 ; --cnt) { char buf[2]; vm_obj_id_t id; int in_root_set; uint meta_idx; CVmObject *obj; /* read the original object ID */ id = (vm_obj_id_t)fp->read_uint4(); /* read the root-set flag and dependency table index */ fp->read_bytes(buf, 2); in_root_set = buf[0]; meta_idx = (uchar)buf[1]; /* * if it's not in the root set, we need to create a new object; * otherwise, the object must already exist, since it came from * the object file */ if (in_root_set) { /* * make sure the object is valid - since it's supposedly in * the root set, it must already exist */ if (!is_obj_id_valid(id) || get_entry(id)->free_) return VMERR_SAVED_OBJ_ID_INVALID; } else { /* * the object was dynamically allocated, so it will have a new * object number - fix up the object ID to the new numbering * system */ id = (*fixups)->get_new_id(vmg_ id); /* create the object */ G_meta_table->create_for_restore(vmg_ meta_idx, id); } /* read the object's data */ obj = get_obj(id); obj->restore_from_file(vmg_ id, fp, *fixups); } /* success */ return 0; } /* * Save an image data pointer */ void CVmObjTable::save_image_pointer(vm_obj_id_t obj_id, const char *ptr, size_t siz) { vm_image_ptr *slot; /* allocate a new page if we're out of slots on the current page */ if (image_ptr_head_ == 0) { /* no pages yet - allocate the first page */ image_ptr_head_ = (vm_image_ptr_page *) t3malloc(sizeof(vm_image_ptr_page)); if (image_ptr_head_ == 0) err_throw(VMERR_OUT_OF_MEMORY); /* it's also the last page */ image_ptr_tail_ = image_ptr_head_; image_ptr_tail_->next_ = 0; /* no slots used on this page yet */ image_ptr_last_cnt_ = 0; } else if (image_ptr_last_cnt_ == VM_IMAGE_PTRS_PER_PAGE) { /* the last page is full - allocate another one */ image_ptr_tail_->next_ = (vm_image_ptr_page *) t3malloc(sizeof(vm_image_ptr_page)); if (image_ptr_tail_->next_ == 0) err_throw(VMERR_OUT_OF_MEMORY); /* it's the new last page */ image_ptr_tail_ = image_ptr_tail_->next_; image_ptr_tail_->next_ = 0; /* no slots used on this page yet */ image_ptr_last_cnt_ = 0; } /* get the next available slot */ slot = &image_ptr_tail_->ptrs_[image_ptr_last_cnt_]; /* save the data */ slot->obj_id_ = obj_id; slot->image_data_ptr_ = ptr; slot->image_data_len_ = siz; /* count the new record */ ++image_ptr_last_cnt_; } /* * Reset to initial image file state */ void CVmObjTable::reset_to_image(VMG0_) { CVmObjPageEntry **pg; CVmObjPageEntry *entry; size_t i; size_t j; vm_obj_id_t id; vm_image_ptr_page *ip_page; /* * Drop all undo information. Since we're resetting to the initial * state, the undo for our outgoing state will no longer be * relevant. */ G_undo->drop_undo(vmg0_); /* delete all of the globals */ if (globals_ != 0) { delete globals_; globals_ = 0; } /* * Go through the object table and reset each non-transient object in * the root set to its initial conditions. */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { /* scan all objects on this page */ for (j = VM_OBJ_PAGE_CNT, entry = *pg ; j > 0 ; --j, ++entry, ++id) { /* * if it's not free, and it's in the root set, and it's not * transient, reset it */ if (!entry->free_ && entry->in_root_set_ && !entry->transient_) { /* * This object is part of the root set, so it's part of * the state immediately after loading the image. Reset * the object to its load file conditions. */ entry->get_vm_obj()->reset_to_image(vmg_ id); } } } /* * Go through all of the objects for which we've explicitly saved the * original image file location, and ask them to reset using the image * data. */ for (ip_page = image_ptr_head_ ; ip_page != 0 ; ip_page = ip_page->next_) { size_t cnt; vm_image_ptr *slot; /* * get the count for this page - if this is the last page, it's * the last page counter; otherwise, it's a full page, since we * fill up each page before creating a new one */ if (ip_page->next_ == 0) cnt = image_ptr_last_cnt_; else cnt = VM_IMAGE_PTRS_PER_PAGE; /* go through the records on the page */ for (slot = ip_page->ptrs_ ; cnt != 0 ; --cnt, ++slot) { /* it this object is non-transient, reload it */ if (!get_entry(slot->obj_id_)->transient_) { /* reload it using the saved image data */ vm_objp(vmg_ slot->obj_id_) ->reload_from_image(vmg_ slot->obj_id_, slot->image_data_ptr_, slot->image_data_len_); } } } } /* * Create a global variable */ vm_globalvar_t *CVmObjTable::create_global_var() { vm_globalvar_t *var; /* create the new variable */ var = new vm_globalvar_t(); /* initialize the variable's value to nil */ var->val.set_nil(); /* link it into our list of globals */ var->nxt = global_var_head_; var->prv = 0; if (global_var_head_ != 0) global_var_head_->prv = var; global_var_head_ = var; /* return the variable */ return var; } /* * Delete a global variable */ void CVmObjTable::delete_global_var(vm_globalvar_t *var) { /* unlink it from the list of globals */ if (var->nxt != 0) var->nxt->prv = var->prv; if (var->prv != 0) var->prv->nxt = var->nxt; else global_var_head_ = var->nxt; /* delete the memory */ delete var; } /* ------------------------------------------------------------------------ */ /* * post-load initialization status */ enum pli_stat_t { PLI_UNINITED, /* not yet initialized */ PLI_IN_PROGRESS, /* initialization in progress */ PLI_INITED /* initialization completed */ }; /* * Post-load initialization hash table entry. We use the object ID, * treating the binary representation as a string of bytes, as the hash * key. */ class CVmHashEntryPLI: public CVmHashEntryCS { public: CVmHashEntryPLI(vm_obj_id_t id) : CVmHashEntryCS((char *)&id, sizeof(id), TRUE) { /* * remember our object ID for easy access (technically, it's stored * as our key value as well, so this is redundant; but it's * transformed into a block of bytes for the key, so it's easier to * keep a separate copy of the true type) */ id_ = id; /* initialize our status */ status = PLI_UNINITED; } /* our object ID */ vm_obj_id_t id_; /* status for current operation */ pli_stat_t status; }; /* * Request post-load initialization. */ void CVmObjTable::request_post_load_init(vm_obj_id_t obj) { CVmHashEntryPLI *entry; /* check for an existing entry - if there's not one already, add one */ entry = (CVmHashEntryPLI *) post_load_init_table_->find((char *)&obj, sizeof(obj)); if (entry == 0) { /* it's not there yet - add a new entry */ post_load_init_table_->add(new CVmHashEntryPLI(obj)); /* mark the object as having requested post-load initialization */ get_entry(obj)->requested_post_load_init_ = TRUE; } } /* * Explicitly invoke post-load initialization on a given object */ void CVmObjTable::ensure_post_load_init(VMG_ vm_obj_id_t obj) { CVmHashEntryPLI *entry; /* find the entry */ entry = (CVmHashEntryPLI *) post_load_init_table_->find((char *)&obj, sizeof(obj)); /* if we found it, invoke its initialization method */ if (entry != 0) call_post_load_init(vmg_ entry); } /* * Invoke post-load initialization on the given object, if appropriate */ void CVmObjTable::call_post_load_init(VMG_ CVmHashEntryPLI *entry) { /* check the status */ switch (entry->status) { case PLI_UNINITED: /* * It's not yet initialized, so we need to initialize it now. Mark * it as having its initialization in progress. */ entry->status = PLI_IN_PROGRESS; /* * push the entry on the stack to protect it from gc while we're * initializing it */ G_stk->push()->set_obj(entry->id_); /* invoke its initialization */ vm_objp(vmg_ entry->id_)->post_load_init(vmg_ entry->id_); /* mark the object as having completed initialization */ entry->status = PLI_INITED; /* discard our GC protection */ G_stk->discard(); /* done */ break; case PLI_IN_PROGRESS: /* * The object is in the process of being initialized. This must * mean that the object's initializer is calling us indirectly, * probably through another object's initializer that it invoked * explicitly as a dependency, which in turn means that we have a * circular dependency. This is illegal, so abort with an error. */ err_throw(VMERR_CIRCULAR_INIT); break; case PLI_INITED: /* it's already been initialized, so there's nothing to do */ break; } } /* * Remove a post-load initialization record */ void CVmObjTable::remove_post_load_init(vm_obj_id_t obj) { CVmHashEntryPLI *entry; /* find the entry */ entry = (CVmHashEntryPLI *) post_load_init_table_->find((char *)&obj, sizeof(obj)); /* if we found the entry, remove it from the table and delete it */ if (entry != 0) { /* remove it */ post_load_init_table_->remove(entry); /* delete it */ delete entry; /* mark the entry as no longer being registered for post-load init */ get_entry(obj)->requested_post_load_init_ = FALSE; } } /* * post-load initialization enumeration context */ struct pli_enum_ctx { vm_globals *globals; }; /* * Invoke post-load initialization on all objects that have requested it. * This must be called after initial program load, restarts, and restore * operations. */ void CVmObjTable::do_all_post_load_init(VMG0_) { pli_enum_ctx ctx; /* set up our context */ ctx.globals = VMGLOB_ADDR; /* first, mark all entries as having status 'uninitialized' */ post_load_init_table_->enum_entries(&pli_status_cb, &ctx); /* next, invoke the initializer method for each entry */ post_load_init_table_->enum_entries(&pli_invoke_cb, &ctx); } /* * post-load initialization enumeration callback: mark entries as having * status 'uninitialized' */ void CVmObjTable::pli_status_cb(void *ctx0, CVmHashEntry *entry0) { CVmHashEntryPLI *entry = (CVmHashEntryPLI *)entry0; /* mark the entry as having status 'uninitialized' */ entry->status = PLI_UNINITED; } /* * post-load initialization enumeration callback: mark entries as having * status 'uninitialized' */ void CVmObjTable::pli_invoke_cb(void *ctx0, CVmHashEntry *entry0) { CVmHashEntryPLI *entry = (CVmHashEntryPLI *)entry0; VMGLOB_PTR(((pli_enum_ctx *)ctx0)->globals); /* invoke post-load initialization on the object */ call_post_load_init(vmg_ entry); } /* ------------------------------------------------------------------------ */ /* * Memory manager implementation */ CVmMemory::CVmMemory(VMG_ CVmVarHeap *varheap) { /* remember our variable-size heap */ varheap_ = varheap; /* initialize the variable-size heap */ varheap_->init(vmg0_); } /* ------------------------------------------------------------------------ */ /* * Hybrid cell/malloc memory manager - cell sub-block manager */ /* * Allocate an object. Since we always allocate blocks of a fixed size, * we can ignore the size parameter; the heap manager will only route us * requests that fit in our blocks. */ CVmVarHeapHybrid_hdr *CVmVarHeapHybrid_head::alloc(size_t) { /* if there isn't an entry, allocate another array */ if (first_free_ == 0) { CVmVarHeapHybrid_array *arr; size_t real_cell_size; char *p; size_t cnt; /* * Allocate another page. We need space for the array header * itself, plus space for all of the cells. We want page_count_ * cells, each of size cell_size_ plus the size of the per-cell * header, rounded to the worst-case alignment size for the * platform. */ real_cell_size = osrndsz(cell_size_ + osrndsz(sizeof(CVmVarHeapHybrid_hdr))); arr = (CVmVarHeapHybrid_array *) t3malloc(sizeof(CVmVarHeapHybrid_array) + (page_count_ * real_cell_size)); /* if that failed, throw an error */ if (arr == 0) err_throw(VMERR_OUT_OF_MEMORY); /* link the array into the master list */ arr->next_array = mem_mgr_->first_array_; mem_mgr_->first_array_ = arr; /* * Build the free list. Each cell goes into the free list; the * 'next' pointer is stored in the data area of the cell. */ for (p = arr->mem, cnt = page_count_ ; cnt > 0 ; p += real_cell_size, --cnt) { /* link this one into the free list */ *(void **)p = first_free_; first_free_ = (void *)p; } } /* remember the return value */ CVmVarHeapHybrid_hdr *ret = (CVmVarHeapHybrid_hdr *)first_free_; /* * when we initialized or last freed this entry, we stored a pointer * to the next item in the free list in the object's data - retrieve * this pointer now, and update our free list head to point to the * next item */ first_free_ = *(void **)first_free_; /* fill in the block's pointer to the allocating heap (i.e., this) */ ret->block = this; /* return the item */ return ret; } /* * Reallocate */ void *CVmVarHeapHybrid_head::realloc(CVmVarHeapHybrid_hdr *mem, size_t siz, CVmObject *obj) { /* * if the new block fits in our cell size, return the original * memory unchanged; note that we must adjust the pointer so that we * return the client-visible portion */ if (siz <= cell_size_) return (void *)(mem + 1); /* * The memory won't fit in our cell size, so not only can't we * re-use the existing cell, but we can't allocate the memory from * our own sub-block at all. Allocate an entirely new block from * the heap manager. */ void *new_mem = mem_mgr_->alloc_mem(siz, obj); /* * Copy the old cell's contents to the new memory. Note that the * user-visible portion of the old cell starts immediately after the * header; don't copy the old header, since it's not applicable to * the new object. Note also that we got a pointer directly to the * user-visible portion of the new object, so we don't need to make * any adjustments to the new pointer. */ memcpy(new_mem, (void *)(mem + 1), cell_size_); /* free the old memory */ free(mem); /* return the new memory */ return new_mem; } /* * Release memory */ void CVmVarHeapHybrid_head::free(CVmVarHeapHybrid_hdr *mem) { /* link the block into our free list */ *(void **)mem = first_free_; first_free_ = (void *)mem; } /* ------------------------------------------------------------------------ */ /* * Hybrid cell/malloc heap manager */ /* * construct */ CVmVarHeapHybrid::CVmVarHeapHybrid(CVmObjTable *objtab) { /* remember my object table */ objtab_ = objtab; /* set the cell heap count */ cell_heap_cnt_ = 5; /* allocate our cell heap pointer array */ cell_heaps_ = (CVmVarHeapHybrid_head **) t3malloc(cell_heap_cnt_ * sizeof(CVmVarHeapHybrid_head *)); /* if that failed, throw an error */ if (cell_heaps_ == 0) err_throw(VMERR_OUT_OF_MEMORY); /* * Allocate our cell heaps. Set up the heaps so that the pages run * about 32k each. */ cell_heaps_[0] = new CVmVarHeapHybrid_head(this, 32, 850); cell_heaps_[1] = new CVmVarHeapHybrid_head(this, 64, 400); cell_heaps_[2] = new CVmVarHeapHybrid_head(this, 128, 200); cell_heaps_[3] = new CVmVarHeapHybrid_head(this, 256, 100); cell_heaps_[4] = new CVmVarHeapHybrid_head(this, 512, 50); /* allocate our malloc heap manager */ malloc_heap_ = new CVmVarHeapHybrid_malloc(); /* we haven't allocated any cell array pages yet */ first_array_ = 0; } /* * delete */ CVmVarHeapHybrid::~CVmVarHeapHybrid() { size_t i; /* delete our cell heaps */ for (i = 0 ; i < cell_heap_cnt_ ; ++i) delete cell_heaps_[i]; /* delete the cell heap pointer array */ t3free(cell_heaps_); /* delete all of the arrays */ while (first_array_ != 0) { CVmVarHeapHybrid_array *nxt; /* remember the next one */ nxt = first_array_->next_array; /* delete this one */ t3free(first_array_); /* move on to the next one */ first_array_ = nxt; } /* delete the malloc-based subheap manager */ delete malloc_heap_; } /* * allocate memory */ void *CVmVarHeapHybrid::alloc_mem(size_t siz, CVmObject *) { CVmVarHeapHybrid_head **subheap; size_t i; /* count the gc statistics if desired */ IF_GC_STATS(gc_stats.count_alloc_bytes(siz)); /* count the allocation */ objtab_->count_alloc(siz); /* scan for a cell-based subheap that can handle the request */ for (i = 0, subheap = cell_heaps_ ; i < cell_heap_cnt_ ; ++i, ++subheap) { /* * If it will fit in this one's cell size, allocate it from this * subheap. Note that we must adjust the return pointer so that * it points to the caller-visible portion of the block returned * from the subheap, which immediately follows the internal * header. */ if (siz <= (*subheap)->get_cell_size()) { CVmVarHeapHybrid_hdr *hdr = (*subheap)->alloc(siz); IF_GC_STATS(hdr->siz = siz); return (void *)(hdr + 1); } } /* * We couldn't find a cell-based manager that can handle a block * this large. Allocate the block from the default malloc heap. * Note that the caller-visible block is the part that immediately * follows our internal header, so we must adjust the return pointer * accordingly. */ CVmVarHeapHybrid_hdr *hdr = malloc_heap_->alloc(siz); IF_GC_STATS(hdr->siz = siz); return (void *)(hdr + 1); } /* * reallocate memory */ void *CVmVarHeapHybrid::realloc_mem(size_t siz, void *mem, CVmObject *obj) { CVmVarHeapHybrid_hdr *hdr; /* * Count the allocation. This isn't really a new allocation of 'siz' * bytes, since we're only expanding an object that already has a * non-zero size. But in many cases this will simply allocate new * memory anyway, copying the old object into the new memory, so it * could actually count against our OS-level working set. That makes * it worthwhile to count it against the GC quota. */ objtab_->count_alloc(siz); /* * get the block header, which immediately precedes the * caller-visible block */ hdr = ((CVmVarHeapHybrid_hdr *)mem) - 1; /* count the gc statistics if desired */ IF_GC_STATS((gc_stats.count_realloc_bytes(hdr->siz, siz), hdr->siz = siz)); /* * read the header to get the block manager that originally * allocated the memory, and ask it to reallocate the memory */ return hdr->block->realloc(hdr, siz, obj); } /* * free memory */ void CVmVarHeapHybrid::free_mem(void *mem) { CVmVarHeapHybrid_hdr *hdr; /* * get the block header, which immediately precedes the * caller-visible block */ hdr = ((CVmVarHeapHybrid_hdr *)mem) - 1; /* count the gc statistics if desired */ IF_GC_STATS(gc_stats.count_free_bytes(hdr->siz)); /* * read the header to get the block manager that originally * allocated the memory, and ask it to free the memory */ hdr->block->free(hdr); } ����������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmobj.h���������������������������������������������������������������������������0000664�0000000�0000000�00000350220�12650170723�0015146�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMOBJ.H,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmobj.h - VM object memory manager Function Notes Modified 10/20/98 MJRoberts - Creation */ /* define this symbol to collect and display GC statistics, for tuning */ // #define VMOBJ_GC_STATS #ifndef VMOBJ_H #define VMOBJ_H #include <stdlib.h> #include <memory.h> #include "vmglob.h" #include "vmtype.h" #include "vmerr.h" #include "vmerrnum.h" /* ------------------------------------------------------------------------ */ /* * GC statistics conditional compilation macro */ #ifdef VMOBJ_GC_STATS # define IF_GC_STATS(x) x #else # define IF_GC_STATS(x) #endif /* ------------------------------------------------------------------------ */ /* * If the VM_REGISTER_METACLASS macro hasn't been defined, define it * now. This macro is defined different ways on different inclusions, * but when it's not defined, it should always be defined to do nothing. */ #ifndef VM_REGISTER_METACLASS # define VM_REGISTER_METACLASS(metaclass) #endif /* ------------------------------------------------------------------------ */ /* * NOTE TO METACLASS IMPLEMENTORS - each final concrete class derived * from CVmObject that is to be available to VM programs for static * image file loading and/or dynamic creation (via the "NEW" * instructions) must specify a VM_REGISTER_METACLASS declaration to * register the metaclass. This declaration must occur in the * metaclass's header file, and must be OUTSIDE of the region protected * against multiple inclusion. The definition should look like this: * * VM_REGISTER_METACLASS("metaclass-string-name", CVmClassName) * * The string name must be the universally unique metaclass identifier * string registered with the T3 VM Specification Maintainer. The class * name is the C++ class (derived from CVmObject). * * Each CVmObject subclass must define certain static construction * methods in addition to the virtual methods defined as abstract in * CVmObject. See the comments on CVmObject for details. */ /* ------------------------------------------------------------------------ */ /* * To get a pointer to an object (CVmObject *) given an object ID * (vm_obj_id_t), use this: * * CVmObject *obj = vm_objp(vmg_ id); * * To allocate a new object: * * vm_obj_id_t id = vm_newid(vmg_ in_root_set); *. CVmObject *obj = new (vmg_ id) CVmObjXxx(constructor params); * * The functions vm_objp() and vm_newid() are defined later in this file. */ /* ------------------------------------------------------------------------ */ /* * Garbage collector work increment. This is the number of objects that * the GC will process on each call to gc_pass_queue(). * * The point of running the GC incrementally is to allow GC work to be * interleaved with long-running user I/O operations (such as reading a * line of text from the keyboard) in the foreground thread, so the work * increment should be chosen so that each call to this routine * completes quickly enough that the user will perceive no delay. * However, making this number too small will introduce additional * overhead by making an excessive number of function calls. */ const int VM_GC_WORK_INCREMENT = 500; /* ------------------------------------------------------------------------ */ /* * flag values for propDefined() */ #define VMOBJ_PROPDEF_ANY 1 #define VMOBJ_PROPDEF_DIRECTLY 2 #define VMOBJ_PROPDEF_INHERITS 3 #define VMOBJ_PROPDEF_GET_CLASS 4 /* * flag values for explicit_to_string() */ #define TOSTR_UNSIGNED 0x0001 /* treat VM_INT values as unsigned */ #define TOSTR_ROUND 0x0002 /* round floating point values to integer */ /* ------------------------------------------------------------------------ */ /* * Objects are composed of two parts. The first is a fixed-size * portion, or header, which consists of an abstract interface (in terms * of stored data, this is simply a vtable pointer) and an extension * pointer. The extension pointer points to the variable-size second * part of the object, which contains all of the additional instance * data for the object. */ /* ------------------------------------------------------------------------ */ /* * * The fixed-size parts, or object headers, are allocated from a set of * arrays. A master array has a pointer to the sub-arrays, and each * sub-array has a fixed number of slots for the fixed-size parts. * Thus, it's very fast to find an object header given an object ID. * * Because each fixed-size part has an abstract interface, we can have * multiple implementations for the objects. This would allow us, for * example, to include native objects (with an appropriate interface) as * though they were normal VM objects. It also allows us to have * different types of implementations for VM objects. * * The fixed-size object parts never move in memory, so we can refer to * them with pointers. We can efficiently re-use space as object * headers are allocated and deleted, because a new object will always * take up exactly the same size as a previous object whose space was * freed. * * The fixed-size objects are always allocated within the page table, * thus operator new is overridden to allocate memory within the page * table. */ /* * In addition to the virtual methods listed, every object must define a * data member as follows: * * public: static class CVmMetaclass *metaclass_reg_; * * This must be a static singleton instance of the CVmMetaclass subclass * (see below) for the CVmObject subclass. Each CVmObject subclass must * have a corresponding CVmMetaclass subclass; the singleton member * variable metaclass_reg_ provides the registration table entry that * allows instances of this object to be dynamically linked from the * image file. */ class CVmObject { friend class CVmVarHeap; public: /* metaclass registration object (for the root object implementation) */ static class CVmMetaclass *metaclass_reg_; /* * Default implementation for calling a static property. We don't * have any static properties at this level, so we'll simply return * false to indicate that the property wasn't evaluated. */ static int call_stat_prop(VMG_ vm_val_t *retval, const uchar **pc_ptr, uint *argc, vm_prop_id_t); /* get the registration object for this metaclass */ virtual class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* * Get the image file version string for my metaclass object. This is * the version of the metaclass that the image file actually depends * upon, which might be an earlier version than the one actually * present in the implementation. This can be used for varying * behavior to preserve compatibility with older versions. */ const char *get_image_file_version(VMG0_) const; /* * Is the image file version of the metaclass AT LEAST the given * version? This returns true if the image file is dependent upon a * version of the metaclass equal to or later than (higher than) the * given version string. The string should be given in the usual * metaclass format - "030001", for example. */ int image_file_version_ge(VMG_ const char *ver) { return strcmp(get_image_file_version(vmg0_), ver) >= 0; } /* * Is this object of the given metaclass? Returns true if the * object is an instance of 'meta' or inherits from the metaclass. * Each object class must override this. */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { return (meta == metaclass_reg_); } /* * Receive notification that this object is being deleted - the * garbage collector calls this function when the object is * unreachable. * * Note that we don't use the real destructor, since we use our own * memory management; instead, we have this virtual finalizer that * we explicitly call when it's time to delete the object. (This * isn't entirely symmetrical with the overridden operator new, but * the GC is the only code that can delete objects, and this saves * us the trouble of overriding operator delete for the object.) */ virtual void notify_delete(VMG_ int in_root_set) = 0; /* * Create an instance of this class. If this object does not * represent a class, or cannot be instanced, throw an error. By * default, objects cannot be instanced, so we'll simply throw an * error. If successful, leaves the new object in register R0. * * Parameters to the constructor are passed on the VM stack; 'argc' * gives the number of arguments on the stack. This routine will * remove the arguments from the stack before returning. */ virtual void create_instance(VMG_ vm_obj_id_t self, const uchar **pc_ptr, uint argc) { /* throw the error */ err_throw(VMERR_CANNOT_CREATE_INST); } /* * Determine if the object has a non-trivial finalizer. Returns * true if the object has a non-trivial finalizer, false if it has * no finalizer or the finalizer is trivial and hence can be * ignored. We'll return false by default. */ virtual int has_finalizer(VMG_ vm_obj_id_t /*self*/) { return FALSE; } /* * Invoke the object's finalizer. This need do nothing if the * object does not define or inherit a finalizer method. Any * exceptions thrown in the course of executing the finalizer should * be caught and discarded. By default, we'll do nothing at all. */ virtual void invoke_finalizer(VMG_ vm_obj_id_t /*self*/) { } /* * Determine if this is a class object. This returns true if this * object is a class, false if it's an instance. */ virtual int is_class_object(VMG_ vm_obj_id_t /*self*/) const { return FALSE; } /* * Determine if this object is an instance of another object. * Returns true if this object derives from the other object, * directly or indirectly. If this object derives from an object * which in turn derives from the given object, then this object * derives (indirectly) from the given object. */ virtual int is_instance_of(VMG_ vm_obj_id_t obj); /* * Get the number of superclasses of the object, and get the nth * superclass. By default, we have one superclass, which is the * IntrinsicClass object that represents this metaclass. */ virtual int get_superclass_count(VMG_ vm_obj_id_t /*self*/) const { return 1; } virtual vm_obj_id_t get_superclass(VMG_ vm_obj_id_t /*self*/, int /*superclass_index*/) const; /* * Determine if the object has properties that can be enumerated. * Returns true if so, false if not. Note that this should return * true for an object of a type that provides properties, even if * the instance happens to have zero properties. */ virtual int provides_props(VMG0_) const { return FALSE; } /* * Enumerate properties of the object. Invoke the callback for each * property. The callback is not permitted to make any changes to * the object or its properties and should not invoke garbage * collection. */ virtual void enum_props(VMG_ vm_obj_id_t self, void (*cb)(VMG_ void *ctx, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val), void *cbctx) { /* by default, assume we have nothing to enumerate */ } /* set a property value */ virtual void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) = 0; /* * Get a property value. We do not evaluate the property, but * merely get the raw value; thus, if the property value is of type * code, we simply retrieve the code offset pointer. Returns true * if the property was found, false if not. * * If we find the object, we'll set *source_obj to the ID of the * object in which we found the object. If the property was * supplied by this object directly, we'll simply set source_id to * self; if we inherited the property from a superclass, we'll set * *source_id to the ID of the superclass object that actually * supplied the value. * * If 'argc' is not null, this function can consume arguments from * the run-time stack, and set *argc to zero to indicate that the * arguments have been consumed. * * If 'argc' is null, and evaluating the property would involve * running system code (with or without consuming arguments), this * function should return TRUE, but should NOT run the system code - * instead, set val->typ to VM_NATIVE_CODE to indicate that the * value is a "native code" value (i.e., evaluating it requires * executing system code). */ virtual int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * Get the invocation routine. This retrieves the bytecode location to * invoke when calling the object as though it were a function. If the * object has a function interface, set up a FUNCPTR or CODEPTR value * to point to the code to invoke. Regular objects aren't invokable. * * Returns true if this is an invokable object, false if not. 'val' * can be a null pointer if the caller merely wishes to determine * whether or not this object is invokable. */ virtual int get_invoker(VMG_ vm_val_t *val) { return FALSE; } /* get the interface to the given property */ int get_prop_interface(VMG_ vm_obj_id_t self, vm_prop_id_t prop, int &min_args, int &opt_args, int &varargs); /* * Inherit a property value. This works similarly to get_prop, but * finds an inherited definition of the property, as though * orig_target_obj.prop (and anything overriding orig_target_obj.prop) * were undefined. * * In broad terms, the algorithm for this method is to do the same * thing as get_prop(), but to ignore every definition of the property * found in the class tree until after reaching and skipping * orig_target_obj.prop. Once orig_target_obj.prop is found, this * method simply continues searching in the same manner as get_prop() * and returns the next definition it finds. * * 'defining_obj' is the object containing the method currently * running. This is not necessarily 'self', because the method * currently running might already have been inherited from a * superclass of 'self'. * * 'orig_target_obj' is the object that was originally targeted for the * get_prop() operation that invoked the calling method (or invoked the * method that inherited the calling method, or so on). This gives us * the starting point in the search, so that we can continue the * original inheritance tree search that started with get_prop(). * * 'argc' has the same meaning as for get_prop(). * * Objects that cannot be subclassed via byte-code can ignore this * method. The base class implementation follows the inheritance chain * of "modifier" objects, which allow byte-code methods to be plugged * in under the native code class tree. */ virtual int inh_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t orig_target_obj, vm_obj_id_t defining_obj, vm_obj_id_t *source_obj, uint *argc); /* * Build a list of the properties directly defined on this object * instance. On return, retval must be filled in with the new list * object. * * Note that a self-reference must be pushed by the caller to protect * against garbage collection of self while this routine is running. * * Most object types do not define any properties in the instance, so * this default implementation will simply return an empty list. * Classes that can define properties in instances (such as * TadsObjects), and the "intrinsic class" class that represents * classes, must override this to build their lists. */ virtual void build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval); /* * Mark all strongly-referenced objects. Calls * obj_table->mark_refs() for each referenced object. */ virtual void mark_refs(VMG_ uint state) = 0; /* * Remove stale weak references. For each weakly-referenced object, * check to see if the object is marked as reachable; if not, it's * about to be deleted, so forget the weak reference. */ virtual void remove_stale_weak_refs(VMG0_) = 0; /* * Receive notification that the undo manager is creating a new * savepoint. */ virtual void notify_new_savept() = 0; /* * apply an undo record created by this object; if the record has * any additional data associated with it (allocated by the object * when the undo record was created), this should also discard the * additional data */ virtual void apply_undo(VMG_ struct CVmUndoRecord *rec) = 0; /* * Discard any extra information associated with this undo record. * Note that this will not be called if apply_undo() is called, * since apply_undo() is expected to discard any extra information * itself after applying the record. */ virtual void discard_undo(VMG_ struct CVmUndoRecord *) { } /* * Mark an object object reference. If this object keeps strong * references, this should mark any object contained in the undo * record's saved value as referenced; if this object keeps only * weak references, this doesn't need to do anything. */ virtual void mark_undo_ref(VMG_ struct CVmUndoRecord *rec) = 0; /* * Remove any stale weak reference contained in an undo record. For * objects with ordinary strong references, this doesn't need to do * anything. For objects that keep weak references to other * objects, this should check the object referenced in the undo * record, if any, to determine if the object is about to be * deleted, and if so clear the undo record (by setting the object * in the old value to 'invalid'). */ virtual void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *rec) = 0; /* * Post-load initialization. This routine is called only if the object * specifically requests this by calling request_post_load_init(). * This routine is called exactly once for each initial program load, * restart, or restore, and is called after ALL objects are loaded. * * The purpose of this routine is to allow an object to perform * initializations that depend upon other objects. During the normal * load/restore/reset methods, an object cannot assume that any other * objects are already loaded, because the order of loading is * arbitrary. When this routine is called, it is guaranteed that all * objects are already loaded. */ virtual void post_load_init(VMG_ vm_obj_id_t /*self*/) { /* we do nothing by default */ } /* * Load the object from an image file. The object's data block is * at the given address and has the given size. * * The underlying memory is owned by the image file loader. The * object must not attempt to deallocate this memory. */ virtual void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) = 0; /* * Reload the object from an image file. The object's data block is at * the given address and has the given size. Discards any changes made * since the object was loaded and restores its state as it was * immediately after it was loaded from the image file. By default, we * do nothing. * * NOTE 1: this routine can be implemented instead of reset_to_image(). * If an object doesn't have any other need to store a pointer to its * image file data in its own extension, but the image file data is * necessary to effect a reset, then this routine should be used, to * reduce the size of the object's extension for non-image instances. * * NOTE 2: in order to use this routine, the object MUST call the * object table's save_image_pointer() routine during the initial * loading (i.e., in the object's load_from_image()). During a reset, * the object table will only call this routine on objects whose image * pointers were previously saved with save_image_pointer(). */ virtual void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { } /* * Reset to the image file state. Discards any changes made since the * object was loaded and restores its state as it was immediately * after it was loaded from the image file. By default, we do nothing. * * NOTE: this routine doesn't have to do anything if * reload_from_image() is implemented for the object. */ virtual void reset_to_image(VMG_ vm_obj_id_t /*self*/) { } /* * Determine if the object has been changed since it was loaded from * the image file. This can only be called for objects that were * originally loaded from the image file. Returns true if the object's * internal state has been changed since loading, false if the object * is in exactly the same state that's stored in the image file. * * If this returns false, then it's not necessary to save the object to * a saved state file, because the object's state can be restored * simply by resetting to the image file state. */ virtual int is_changed_since_load() const { return FALSE; } /* * save this object to a file, so that it can be restored to its * current state via restore_from_file */ virtual void save_to_file(VMG_ class CVmFile *fp) = 0; /* * Restore the state of the object from a file into which state was * previously stored via save_to_file. This routine may need * access to the memory manager because it may need to allocate * memory to make room for the variable data. */ virtual void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups) = 0; /* * Compare to another value for equality. Returns true if the value is * equal, false if not. By default, we return true only if the other * value is an object with the same ID (i.e., a reference to exactly * the same instance). However, subclasses can override this to * provide different behavior; the string class, for example, may * override this so that it compares equal to any string object or * constant containing the same string text. * * 'depth' has the same meaning as in calc_hash(). */ virtual int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int /*depth*/) const { /* return true if the other value is a reference to this object */ return (val->typ == VM_OBJ && val->val.obj == self); } /* * Compare magnitude of this object and another object. Returns a * positive value if this object is greater than the other object, a * negative value if this object is less than the other object, or * zero if this object equals the other object. Throws an error if * a magnitude comparison is not meaningful between the two objects. * * By default, magnitude comparisons between objects are not * meaningful, so we throw an error. */ virtual int compare_to(VMG_ vm_obj_id_t /*self*/, const vm_val_t *) const { /* by default, magnitude comparisons between objects are illegal */ err_throw(VMERR_INVALID_COMPARISON); /* the compiler doesn't know that we'll never get here */ AFTER_ERR_THROW(return 0;) } /* * Calculate a hash value for the object. * * 'depth' is the recursion depth of the hash calculation. Objects * that can potentially contain cyclical references back to themselves, * and which follow those references to calculate the hash value * recursively, must check the depth counter to see if it exceeds * VM_MAX_TREE_DEPTH_EQ, throwing VMERR_TREE_TOO_DEEP_EQ if so; and * they must increment the depth counter in the recursive traversals. * Objects that don't traverse into their contents can ignore the depth * counter. */ virtual uint calc_hash(VMG_ vm_obj_id_t self, int /*depth*/) const { /* * by default, use a 16-bit hash of our object ID as the hash * value */ return (uint)(((ulong)self & 0xffff) ^ (((ulong)self & 0xffff0000) >> 16)); } /* * Add a value to this object, returning the result in *result. This * may create a new object to hold the result, or may modify the * existing object in place, depending on the subclass implementation. * 'self' is the object ID of this object. * * Returns true if the add was implemented, false if not. If the * operation isn't implemented as a native op, don't throw an error - * simply return false to tell the caller to try an overloaded operator * in byte code. */ virtual int add_val(VMG_ vm_val_t * /*result*/, vm_obj_id_t /*self*/, const vm_val_t * /*val*/) { return FALSE; } /* * Subtract a value from this object, returning the result in * *result. This may create a new object to hold the result, or may * modify the existing object in place, depending upon the subclass * implementation. 'self' is the object ID of this object. */ virtual int sub_val(VMG_ vm_val_t * /*result*/, vm_obj_id_t /*self*/, const vm_val_t * /*val*/) { return FALSE; } /* multiply this object by a value, returning the result in *result */ virtual int mul_val(VMG_ vm_val_t * /* result*/, vm_obj_id_t /*self*/, const vm_val_t * /*val*/) { return FALSE; } /* divide a value into this object, returning the result in *result */ virtual int div_val(VMG_ vm_val_t * /* result*/, vm_obj_id_t /*self*/, const vm_val_t * /*val*/) { return FALSE; } /* get the arithmetic negative of self, returning the result in *result */ virtual int neg_val(VMG_ vm_val_t * /* result*/, vm_obj_id_t /*self*/) { return FALSE; } /* * Index with overloading. If the object implements native indexing, * we'll invoke that; otherwise we'll check for operator[] and invoke * that if present; otherwise we'll throw an error. */ void index_val_ov(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val); /* index the object, throwing an error if not implemented for the type */ void index_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val) { if (!index_val_q(vmg_ result, self, index_val)) err_throw(VMERR_CANNOT_INDEX_TYPE); } /* * Index the object, query mode: retrieve the indexed value and return * true if the object supports the operation, or simply return false * (with no error) if not. Most object types cannot be indexed, so by * default we'll return false to indicate this is not an implemented * operator. Subclasses that can be indexed (such as lists) should * look up the element given by the index value, and store the value at * that index in *result. */ virtual int index_val_q(VMG_ vm_val_t * /*result*/, vm_obj_id_t /*self*/, const vm_val_t * /*index_val*/) { return FALSE; } /* * Assign to an indexed element of the object, with overloading. If * the object implements native indexing, we'll invoke that; otherwise * we'll check for operator[]= and invoke that if present; otherwise * we'll throw an error. */ void set_index_val_ov(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val); /* * Set an indexed element of the object, and fill in *new_container * with the new object that results if an entire new object is * created to hold the modified contents, or with the original * object if the contents of the original object are actually * modified by this operation. Lists, for example, cannot be * modified, so always create a new list object when an element is * set; arrays, in contrast, can be directly modified, so will * simply return 'self' in *new_container. * * By default, we'll throw an error, since default objects cannot be * indexed. */ void set_index_val(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val) { if (!set_index_val_q(vmg_ new_container, self, index_val, new_val)) err_throw(VMERR_CANNOT_INDEX_TYPE); } /* * Set an index element of an object, query mode. Returns true (and * performs the set-index operation) if this is a supported operator, * false if not. */ virtual int set_index_val_q(VMG_ vm_val_t * /*new_container*/, vm_obj_id_t /*self*/, const vm_val_t * /*index_val*/, const vm_val_t * /*new_val*/) { return FALSE; } /* * Get the next iterator value. If another value is available, fills * in *val with the value and returns true. If not, returns false * without changing *val. * * For an ordinary object, we call the method isNextAvailable(); if * that returns true, we'll call getNext(), fill in *val with its * return value, and return true, otherwise we'll return false. * * The built-in iterator classes override this for better performance. */ virtual int iter_next(VMG_ vm_obj_id_t self, vm_val_t *val); /* * Cast the value to integer. If there's a suitable integer * representation, return it; otherwise throw VMERR_NO_INT_CONV. * * For a string, parse the string and return the integer value. For a * BigNumber, round to the nearest integer. Most other types do not * have an integer representation. */ virtual long cast_to_int(VMG0_) const { /* by default, just throw a "cannot convert" error */ err_throw(VMERR_NO_INT_CONV); /* we can't get here, but the compiler doesn't always know that */ AFTER_ERR_THROW(return 0;) } /* * Cast the value to a numeric type. Fills in 'val' with the new * value. If there's a suitable integer representation, return it; * otherwise, if there's a BigNumber representation, return it; if * there's some other numeric type similar to integer or BigNumber that * we can convert to, return that conversion; otherwise throw * VMERR_NO_NUM_CONV. * * Currently, the only types that count as numeric are integer (VM_INT) * and BigNumber. This interface is designed to be extensible if new * numeric types are added in the future. Basically, the idea is that * we should return (a) the original value, if it's already numeric, or * (b) the "simplest" numeric type that can precisely represent the * value. By "simplest", we mean the type that uses the least memory * to represent the given value and is most efficient for computations. * VM_INT is much more efficient than BigNumber, since VM_INT types * don't require memory allocations and perform their arithmetic with * native hardware integer arithmetic. If we ever were to add a native * "double" type, that would rank between VM_INT and BigNumber, since * even native hardware doubles are generally slower than native * integers for computations, but they're still much faster than * BigNumber. */ virtual void cast_to_num(VMG_ vm_val_t *val, vm_obj_id_t self) const { /* by default, just throw a no-conversion error */ err_throw(VMERR_NO_NUM_CONV); } /* * Get a string representation of the object. If necessary, this * can create a new string object to represent the result. * * Returns the string representation in portable string format: the * first two bytes give the byte length of the rest of the string in * portable UINT2 format, and immediately following the length * prefix are the bytes of the string's contents. The length prefix * does not count the length prefix itself in the length of the * string. * * If a new string object is created, new_str must be set to a * reference to the new string object. If a pointer into our data * is returned, we must set new_str to self. If no object data are * involved, new_str can be set to nil. * * If it is not possible to create a string representation of the * object, throw an error (VMERR_NO_STR_CONV). * * By default, we'll throw an error indicating that the object * cannot be converted to a string. */ virtual const char *cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const; /* * Same as cast_to_string, but the conversion is explicitly due to a * toString() call with a radix. By default, we'll simply use the * basic cast_to_string() processing, but objects representing numbers * (e.g., BigNumber) should override this to respect the radix. * * 'flags' is a combination of TOSTR_xxx bit flags: * *. TOSTR_UNSIGNED - interpret a VM_INT value as unsigned *. TOSTR_ROUND - round a floating point value to the nearest integer */ virtual const char *explicit_to_string( VMG_ vm_obj_id_t self, vm_val_t *new_str, int /*radix*/, int /*flags*/) const { return cast_to_string(vmg_ self, new_str); } /* * Is this a numeric type? Returns true if the value represents some * kind of number, false if not. For example, the BigNumber type * returns true. Types that are merely convertible to numbers don't * count as numeric types; for example, String isn't a numeric type * even though some string values can be parsed as numbers. */ virtual int is_numeric() const { return FALSE; } /* * Get the integer value of the object, if it has one. Returns true if * there's an integer value, false if not. * * This differs from cast_to_int() in that we only convert values that * are already scalar numbers. In particular, we don't attempt to * convert string values. This is a weaker type of conversion than * casting, for use in situations where different types might be * allowed, but the only type of numeric value allowed is an integer * value. */ virtual int get_as_int(long *val) const { return FALSE; } /* * Get the 'double' value of the object, if it has one. Returns true * if there's a double value, false if not. * * This only converts values that are already scalar numbers. We don't * parse strings, for example. */ virtual int get_as_double(VMG_ double *val) const { return FALSE; } /* * Promote an integer to the type of this object. For example, if * 'this' is a BigNumber, this creates a BigNumber representation of * the integer. This allows us to perform arithmetic where the left * operand is an integer and the right operand is a different numeric * type. '*val' contains the integer value on entry, and we replace it * with the promoted value. */ virtual void promote_int(VMG_ vm_val_t * /*val*/) const { err_throw(VMERR_NUM_VAL_REQD); } /* * Get the list contained in the object, if possible, or null if * not. If the value contained in the object is a list, this * returns a pointer to the list value in portable format (the first * two bytes are the length prefix as a portable UINT2, and the * subsequent bytes form a packed array of data holders in portable * format). * * Most object classes will return null for this, since they do not * store lists. By default, we return null. */ virtual const char *get_as_list() const { return 0; } /* * Is this a list-like object? Returns true if the object has a length * and is indexable with index values 1..length. */ virtual int is_listlike(VMG_ vm_obj_id_t self); /* * For a list-like object, get the number of elements. Returns -1 if * this isn't a list-like object after all. */ virtual int ll_length(VMG_ vm_obj_id_t self); /* * Get the string contained in the object, if possible, or null if * not. If the value contained in the object is a string, this * returns a pointer to the string value in portable format (the * first two bytes are the string prefix as a portable UINT2, and * the bytes of the string's text immediately follow in UTF8 format). * * Most object classes will return null for this, since they do not * store strings. By default, we return null. */ virtual const char *get_as_string(VMG0_) const { return 0; } /* * Rebuild the image data for the object. This is used to write the * program state to a new image file after executing the program for * a while, such as after a 'preinit' step after compilation. * * This should write the complete metaclass-specific record needed * for an OBJS data block entry, not including the generic header. * * On success, return the size in bytes of the data stored to the * buffer; these bytes must be copied directly to the new image * file. If the given buffer isn't big enough, this should return * the size needed and otherwise leave the buffer empty. If the * object doesn't need to be written at all, this should return * zero. */ virtual ulong rebuild_image(VMG_ char *, ulong) { return 0; } /* * Allocate space for conversion to constant data for storage in a * rebuilt image file. If this object can be stored as constant * data in a rebuilt image file, this should reserve space in the * provided constant mapper object for the object's data in * preparation for conversion. * * Most objects cannot be converted to constant data, so the default * here does nothing. Those that can, such as strings and lists, * should override this. */ virtual void reserve_const_data(VMG_ class CVmConstMapper *, vm_obj_id_t /*self*/) { /* default does nothing */ } /* * Convert to constant data. If this object can be stored as * constant data in a rebuilt image file, it should override this * routine to store its data in the provided constant mapper object. * If this object makes reference to other objects, it must check * each object that it references to determine if the object has a * non-zero address in the mapper, and if so must convert its * reference to the object to a constant data item at the given * address. There is no default implementation of this routine, * because it must be overridden by essentially all objects (at * least, it must be overridden by objects that can be converted or * that can refer to objects that can be converted). */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *, vm_obj_id_t /*self*/) { /* default does nothing */ } /* * Get the type that this object uses when converted to constant * data. Objects that can be converted to constant data via * convert_to_const_data() should return the appropriate type * (VM_SSTRING, VM_LIST, etc); others can return VM_NIL to indicate * that they have no conversion. This will be called only when an * object actually has been converted as indicated in a mapper * (CVmConstMapper) object. We'll return NIL by default. */ virtual vm_datatype_t get_convert_to_const_data_type() const { return VM_NIL; } /* * Allocate memory for the fixed part from a memory manager. We * override operator new so that we can provide custom memory * management for object headers. * * To create an object, use a sequence like this: * * obj_id = mem_mgr->get_obj_table()->alloc_obj(vmg_ in_root_set); *. new (vmg_ obj_id) CVmObjString(subclass constructor params) * * The caller need not store the result of 'new'; the caller * identifies the object by the obj_id value. * * Each CVmObject subclass should provide static methods that cover * the above sequence, since it's a bit verbose to sprinkle * throughout client code. */ void *operator new(size_t siz, VMG_ vm_obj_id_t obj_id); protected: /* * Find a modifier property. This is an internal service routine that * we use to traverse the hierarchy of modifier objects associated with * our intrinsic class hierarchy. */ int find_modifier_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t orig_target_obj, vm_obj_id_t defining_obj, vm_obj_id_t *source_obj, uint *argc); /* * Service routine - check a get_prop() argument count for executing * a native code method implementation. If 'argc' is null, we'll * set the value to a VM_NATIVE_CODE indication and return true, * which the caller should do from get_prop() as well. If 'argc' is * non-null, we'll check it against the given required argument * count, and throw an error if it doesn't match. If 'argc' is * non-null and it matches the given argument count, we'll set *argc * to zero to indicate that we've consumed the arguments, and return * false - get_prop() should in this case execute the native code * and return the appropriate result value. */ static int get_prop_check_argc(vm_val_t *val, uint *argc, const CVmNativeCodeDesc *desc) { /* * if there's no 'argc', we can't execute the native code - we're * simply being asked for a description of the method, not to * evaluate its result */ if (argc == 0) { /* indicate a native code evaluation is required */ val->set_native(desc); /* tell get_prop() to return without further work */ return TRUE; } /* check the argument count - throw an error if out of range */ if (!desc->args_ok(*argc)) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* everything's fine - consume the arguments */ *argc = 0; /* tell get_prop() to proceed with the native evaluation */ return FALSE; } #if 0 /* * Important note: * * This function has been removed because we no longer consider it * likely that we will ever wish to implement a relocating heap * manager. Given the large memory sizes of modern computers, and * recent academic research calling into question the conventional * wisdom that heap fragmentation is actually a problem in practical * applications, we no longer feel that maintaining the possibility of * a relocating heap manager is justified. * * Note that some code in the present implementation assumes that the * heap is non-relocating, so if a relocating heap manager is ever * implemented, those assumptions will have to be addressed. For * example, the CVmObjTads code stores direct object pointers inside * its objects, which is only possible with a non-relocating heap. */ /* * Adjust the extension pointer for a change - this must be called by * the variable heap page when compacting the heap or when the object * must be reallocated. * * This routine can be called ONLY during garbage collection. The * heap manager is not allowed to move variable-size blocks at any * other time. * * This method is virtual because a given object could have more than * one block allocated in the variable heap. By default, we'll fix up * the address of our extension if the block being moved (as * identified by its old address) matches our existing extension * pointer. */ virtual void move_var_part(void *old_pos, void *new_pos) { /* if the block being moved is our extension, note the new location */ if (ext_ == (char *)old_pos) ext_ = (char *)new_pos; } #endif /* * Pointer to extension. This pointer may be used in any way * desired by the subclassed implementation of the CVmObject * interface; normally, this pointer will contain the address of the * data associated with the object. */ char *ext_; /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t prop, vm_obj_id_t *source_obj); /* property evaluator - ofKind */ int getp_of_kind(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t prop, vm_obj_id_t *source_obj); /* property evaluator - getSuperclassList */ int getp_sclist(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t prop, vm_obj_id_t *source_obj); /* property evaluator - propDefined */ int getp_propdef(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t prop, vm_obj_id_t *source_obj); /* property evaluator - propType */ int getp_proptype(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t prop, vm_obj_id_t *source_obj); /* property evaluator - getPropList */ int getp_get_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t prop, vm_obj_id_t *source_obj); /* property evaluator - getPropParams */ int getp_get_prop_params(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t prop, vm_obj_id_t *source_obj); /* property evaluator - isClass */ int getp_is_class(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t prop, vm_obj_id_t *source_obj); /* property evaluator - propInherited */ int getp_propinh(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t prop, vm_obj_id_t *source_obj); /* property evaluator - isTransient */ int getp_is_transient(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t prop, vm_obj_id_t *source_obj); /* find the intrinsic class for the given modifier object */ virtual vm_obj_id_t find_intcls_for_mod(VMG_ vm_obj_id_t self, vm_obj_id_t mod_obj); /* property evaluation function table */ static int (CVmObject::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t prop, vm_obj_id_t *source_obj); }; /* * Function table indices for 'Object' intrinsic class methods. Each of * these gives the index in our function table for the property ID (as * defined in the image file) corresponding to that method. */ const int VMOBJ_IDX_OF_KIND = 1; const int VMOBJ_IDX_SCLIST = 2; const int VMOBJ_IDX_PROPDEF = 3; const int VMOBJ_IDX_PROPTYPE = 4; const int VMOBJ_IDX_GET_PROP_LIST = 5; const int VMOBJ_IDX_GET_PROP_PARAMS = 6; const int VMOBJ_IDX_IS_CLASS = 7; const int VMOBJ_IDX_PROPINH = 8; const int VMOBJ_IDX_IS_TRANSIENT = 9; /* ------------------------------------------------------------------------ */ /* * Each CVmObject subclass must define a singleton instance of * CVmMetaclass that describes the class and instantiates objects of the * class. */ /* * The "registration index" is set at startup, when we register the * metaclasses. This is static to the class - it isn't a per-instance * value. The purpose of setting this value is to allow * get_registration_index() in the instance to get the class value. * This information is used when saving our state to save information on * an instance's metaclass, so that the instance can be re-created when * the saved state is restored. * * The registration index is NOT persistent data. The registration * index of a particular metaclass can vary from execution to execution * (although it will remain fixed throughout the lifetime of one set of * VM globals, hence throughout an image file's execution). The * registration index allows us to find the registration table entry, * which in turn will give us the metaclass global ID, which is suitable * for persistent storage. */ class CVmMetaclass { public: virtual ~CVmMetaclass() { } /* * Get the metaclass registration table index. This gives the index * of the metaclass in the system registration table. This value is * fixed during execution; it is set during startup, when the * registration table is being built, and never changes after that. */ uint get_reg_idx() const { return meta_reg_idx_; } /* * Get the metaclass name. This is the universally unique * identifier assigned to the metaclass, and is the name used to * dynamically link the image file to the metaclass. */ virtual const char *get_meta_name() const = 0; /* * Create an instance of the metaclass using arguments from the VM * stack. Returns the ID of the newly-created object. If the class * has a byte-code constructor, invoke the constructor using the * normal call-procedure protocol. Leave the result in register R0. */ virtual vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) = 0; /* * Create an instance of the metaclass with the given ID. The * object table entry will already be allocated by the caller; this * ro utine only needs to invoke the metaclass-specific constructor * to initialize the object. The object must be initialized in such * a way that it can subsequently be loaded from the image file with * load_from_image(). In general, this routine only needs to do * something like this: * * new (vmg_ id) CVmObjXxx(); */ virtual void create_for_image_load(VMG_ vm_obj_id_t id) = 0; /* * Create an instance of the metaclass with the given ID in * preparation for restoring the object from a saved state file. */ virtual void create_for_restore(VMG_ vm_obj_id_t id) = 0; /* * Call a static property of the metaclass */ virtual int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) = 0; /* * Set a static property (for setprop ops on the class itself). * Returns true if the setprop succeeded, false if not. This shouldn't * throw an error if the property isn't settable on the class; simply * return false instead. (It's okay to throw errors for other reasons, * such as an invalid type for a property that can be set. But if it's * not a supported property at all, we want to continue on to check for * setting a modifier property rather than throwing an error at this * phase.) * * 'class_state' is the class state holder. This is a direct pointer * to the state data in the CVmObjClass object. The callee can modify * this directly, in which case the updated value will be stored (and * will be handled properly for save/restore), but doing so won't save * undo. To save undo, use CVmObjClass::set_class_state_undo(). The * typical scenario for the state value is that it stores a reference * to a container object, such as a Vector or TadsObject, which is then * used to store the actual class state - we typically need a separate * container because we have more state than we could stuff into a * single vm_val_t. When using a separate container, that container * will usually handle the undo, in which case there's no need to worry * about undo at the class_state level. */ virtual int set_stat_prop(VMG_ class CVmUndo *, vm_obj_id_t /*self*/, vm_val_t * /* class_state */, vm_prop_id_t /*prop*/, const vm_val_t * /*val*/) { /* by default, class objects have no settable properties */ return FALSE; } /* * Get the number of superclasses of the metaclass, and get the * super-metaclass at the given index. All metaclasses have exactly * one super-metaclass, except for the root object metaclass, which * has no super-metaclass. */ virtual int get_supermeta_count(VMG0_) const { return 1; } virtual vm_obj_id_t get_supermeta(VMG_ int idx) const; /* determine if I'm an instance of the given object */ virtual int is_meta_instance_of(VMG_ vm_obj_id_t obj) const; /* * set the metaclass registration table index - this can only be * done by vm_register_metaclass() during initialization */ void set_metaclass_reg_index(uint idx) { meta_reg_idx_ = idx; } /* most metaclasses are simply derived from Object */ virtual CVmMetaclass *get_supermeta_reg() const { return CVmObject::metaclass_reg_; } /* * get my class object - we'll look up our class object in the * metaclass registration table */ vm_obj_id_t get_class_obj(VMG0_) const; private: /* system metaclass registration table index */ uint meta_reg_idx_; }; /* ------------------------------------------------------------------------ */ /* * Root Object definition. The root object can never be instantiated; * it is defined purely to provide an object to represent as the root in * the type system. */ class CVmMetaclassRoot: public CVmMetaclass { public: /* get the metaclass name */ const char *get_meta_name() const { return "root-object/030004"; } /* create an instance - this class cannot be instantiated */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { err_throw(VMERR_BAD_DYNAMIC_NEW); AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* create an object - this class cannot be instantiated */ void create_for_image_load(VMG_ vm_obj_id_t id) { err_throw(VMERR_BAD_STATIC_NEW); } /* create an instance for loading from a saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { err_throw(VMERR_BAD_STATIC_NEW); } /* * call a static property (for getprop on the class itself, e.g. * TadsObject.createInstanceOf()) */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* call the base object implementation */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* the root object has no supermetaclasses */ int get_supermeta_count(VMG0_) const { return 0; } vm_obj_id_t get_supermeta(VMG_ int) const { return VM_INVALID_OBJ; } /* * determine if I'm an instance of the given object - the root * object is not a subclass of anything */ virtual int is_meta_instance_of(VMG_ vm_obj_id_t obj) const { return FALSE; } /* the base Object metaclass has no supermetaclass */ virtual CVmMetaclass *get_supermeta_reg() const { return 0; } }; /* ------------------------------------------------------------------------ */ /* * Object fixup table entry */ struct obj_fixup_entry { /* old ID */ vm_obj_id_t old_id; /* new ID */ vm_obj_id_t new_id; }; /* * Object ID Fixup Table. This is used during state restoration to map * from the saved state file's object numbering system to the new * in-memory numbering system. * * The objects must be added to the table IN ASCENDING ORDER OF OLD ID. * We assume this sorting order to perform a binary lookup when asked to * map an ID. */ /* fixup table subarray size */ #define VMOBJFIXUP_SUB_SIZE 2048 class CVmObjFixup { public: CVmObjFixup(ulong entry_cnt); ~CVmObjFixup(); /* add a fixup to the table */ void add_fixup(vm_obj_id_t old_id, vm_obj_id_t new_id); /* * Translate from the file numbering system to the new numbering * system. If the object isn't found, it must be a static object and * hence doesn't require translation, so we'll return the original ID * unchanged. */ vm_obj_id_t get_new_id(VMG_ vm_obj_id_t old_id); /* fix up a DATAHOLDER value */ void fix_dh(VMG_ char *dh); /* fix up an array of DATAHOLDER values */ void fix_dh_array(VMG_ char *arr, size_t cnt); /* fix a portable VMB_OBJECT_ID field */ void fix_vmb_obj(VMG_ char *p); /* fix an array of portable VMB_OBJECT_ID fields */ void fix_vmb_obj_array(VMG_ char *p, size_t cnt); private: /* find an entry given the old object ID */ struct obj_fixup_entry *find_entry(vm_obj_id_t old_entry); /* get an entry at the given array index */ struct obj_fixup_entry *get_entry(ulong idx) const { return &arr_[idx / VMOBJFIXUP_SUB_SIZE][idx % VMOBJFIXUP_SUB_SIZE]; } /* array of subarrays */ struct obj_fixup_entry **arr_; /* number of subarray pages */ ulong pages_; /* number of entries in the array */ ulong cnt_; /* number of entries used so far */ ulong used_; }; /* ------------------------------------------------------------------------ */ /* * Global variable structure. We maintain a linked list of these * structures for miscellaneous global variables required by other * subsystems. */ struct vm_globalvar_t { /* the variable's value */ vm_val_t val; /* next and previous pointer in linked list of global variables */ vm_globalvar_t *nxt; vm_globalvar_t *prv; }; /* ------------------------------------------------------------------------ */ /* * Global object page. We keep a linked list of pages of globals that * refer to objects that are always reachable but were not loaded from the * image file. */ class CVmObjGlobPage { public: CVmObjGlobPage() { /* we're not in a list yet */ nxt_ = 0; /* we have no allocated entries yet */ used_ = 0; } ~CVmObjGlobPage() { /* delete the next page */ delete nxt_; } /* * add an entry to this page; returns true on success, false if we're * too full to add another entry */ int add_entry(vm_obj_id_t obj) { /* if we're full, indicate failure */ if (used_ == sizeof(objs_)/sizeof(objs_[0])) return FALSE; /* store the entry and count it */ objs_[used_] = obj; ++used_; /* indicate success */ return TRUE; } /* next page in list */ CVmObjGlobPage *nxt_; /* number of entries on this page that are in use */ size_t used_; /* array of entries on this page */ vm_obj_id_t objs_[30]; }; /* ------------------------------------------------------------------------ */ /* * Object Header Manager */ /* * Number of objects in a page. We constrain this to be a power of two * to make certain calculations fast (in particular, so that division by * and modulo the page count can be done as bit shifts and masks). */ const unsigned int VM_OBJ_PAGE_CNT_LOG2 = 12; const unsigned int VM_OBJ_PAGE_CNT = (1 << VM_OBJ_PAGE_CNT_LOG2); /* * Reachability states. A Reachable object is in the root set, or can * be reached directly or indirectly from the root set. A * Finalizer-Reachable object can be reached directly or indirectly from * a finalizable object that is Finalizer-Reachable or Unreachable, but * not from any reachable object. An Unreachable object cannot be * reached from any Reachable or Finalizable object. * * We deliberately arrange the objects in a hierarchical order: * Finalizer-Reachable is "more reachable" than Unreachable, and * Reachable is more reachable than Finalizer-Reachable. The numeric * values of these states are arranged so that a higher number indicates * stronger reachability. */ #define VMOBJ_UNREACHABLE 0x00 #define VMOBJ_F_REACHABLE 0x01 #define VMOBJ_REACHABLE 0x02 /* * Finalization states. An Unfinalizable object is one which has not * ever been detected by the garbage collector to be less than fully * Reachable. A Finalizable object is one which has been found during a * garbage collection pass to be either F-Reachable or Unreachable, but * which has not yet been finalized. A Finalized object is one which * has had its finalizer method invoked. */ #define VMOBJ_UNFINALIZABLE 0x00 #define VMOBJ_FINALIZABLE 0x01 #define VMOBJ_FINALIZED 0x02 /* * Object table page entry. */ struct CVmObjPageEntry { /* * An entry is either a member of the free list, or it's a valid * object. */ union { /* * If it's not in the free list, then it's a VM object. We * can't actually embed a true CVmObject here - that's an * abstract type and we therefore can't directly instantiate it * through embedding. So, just allocate enough space for the * object; the memory manager will claim this space (via * operator new) and store the actual CVmObject here when the * slot is allocated to an object. */ char obj_[sizeof(CVmObject)]; /* * if it's in the free list, we just have a pointer to the * previous element of the free list */ vm_obj_id_t prev_free_; } ptr_; /* next object in list (either the GC work queue or the free list) */ vm_obj_id_t next_obj_; /* get my VM object pointer */ CVmObject *get_vm_obj() const { return (CVmObject *)ptr_.obj_; } /* flag: the object is in the free list */ unsigned int free_ : 1; /* * flag: the object is part of the root set (that is, there's a * reference to this object from some static location outside of the * root set, such as in p-code or in a constant list) */ unsigned int in_root_set_ : 1; /* * Reachability state. This indicates whether the object is * reachable, reachable from finalizable objects only, or * unreachable. This is set during garbage collection. */ uint reachable_ : 2; /* * Finalization state. This indicates whether an object is * unfinalizable, finalizable, or finalized. */ uint finalize_state_ : 2; /* * Flag: the object is part of an undo savepoint. This is cleared * when an object is initially created, and set for all existing * objects when an undo savepoint is created - this means that this * will be set for an object only if an undo savepoint has been * created since the object was created. * * When the undo mechanism is asked to create an undo record * associated with an object, it will do nothing if the object is not * part of the undo savepoint. This means that we won't save undo * records for objects created since the start of the most recent * savepoint - keeping undo for such objects is unnecessary, since if * we roll back to the savepoint, the object won't even be in * existence any more and hence has no need to restore any of its * state. */ uint in_undo_ : 1; /* * Flag: the object is "transient." A transient object does not * participate in undo, is not saved or restored to a saved state, and * is not affected by restarting. */ uint transient_ : 1; /* flag: the object has requested post-load initialization */ uint requested_post_load_init_ : 1; /* * Garbage collection hint flags. These flags provide hints on how the * object's metaclass interacts with the garbage collector. These do * NOT indicate the object's current status, but rather indicate the * metaclass's capabilities - so 'can_have_refs_' does not indicate * that the object current has or doesn't have any references to other * objects, but rather indicates if it's POSSIBLE for the object EVER * to have references to other objects. For example, all Strings would * set 'can_have_refs_' to false, and all TadsObjects would set it to * true. * * We set these flags to true by default, for the maximally * conservative settings. A metaclass can simply ignore these settings * and be assured of correct GC behavior. However, if a metaclass * knows that it can correctly set one of these flags to false, it * should do so after instances are created, because doing so allows * the garbage collector to reduce the amount of work it must do for * the object. * * 'can_have_refs_' indicates if the object can ever contain references * to other objects. By default, this is always set to true, but a * metaclass that is not capable of storing references to other objects * should set this to false. When this is set to false, the garbage * collector will avoid tracing into this object when tracing * references, because it will know in advance that tracing into the * object will have no effect. * * 'can_have_weak_refs_' indicates if the object can ever contain weak * references to other objects. By default, this is set to true, but a * metaclass that never uses weak references can set it to false. When * this is set to false, the garbage collector can avoid notifying this * object of the need to remove stale weak references. * * IMPORTANT: We assume that a metaclass that cannot have * references/weak references can ALSO never have the corresponding * thing in its undo information. It's hard to imagine a case where * we'd have no possibility of one kind of reference in an object but * then have the same kind of reference in the object's undo records. * But should such a case arise, the metaclass must indicate that it * does have the possibility - in other words, if an object can have a * given reference type in its undo, it must also say it can have that * type in its own data, by setting the appropriate flag here. */ uint can_have_refs_ : 1; uint can_have_weak_refs_ : 1; /* * An entry is deletable if it's unreachable and has been finalized. * If the entry is marked as free, it's already been deleted, hence * is certainly deletable. */ int is_deletable() const { return (free_ || (reachable_ == VMOBJ_UNREACHABLE && finalize_state_ == VMOBJ_FINALIZED)); } /* * Determine if the object should participate in undo. An object * participates in undo if it existed as of the most recent savepoint, * and the object is not transient. */ int is_in_undo() const { return in_undo_ && !transient_; } /* * A "saveable" object is one which must be written to a saved state * file. * * To be saveable, an object must not be free, must not be transient, * must be fully reachable, and must have been modified since loading * (or simply have been created dynamically, since all an object that * was created dynamically has inherently been modified since the * program was loaded, as it didn't even exist when the program was * loaded). * * Do not save objects that are only reachable through a finalizer; * assume that these objects do not figure into the persistent VM * state, but are still around merely because they have some external * resource deallocation to which they must yet tend. * * Do not save objects that are in the root set and which haven't been * modified since loading. We always reset before restoring to the * initial image file state, so there's no need to save data for any * object that's simply in its initial image file state. */ int is_saveable() const { return (!free_ && !transient_ && reachable_ == VMOBJ_REACHABLE && (!in_root_set_ || get_vm_obj()->is_changed_since_load())); } /* * Determine if the object is "persistent." A persistent object is * one which will survive saving and restoring machine state. An * object is persistent if it is not transient, and either it is * saveable (in which case it will be explicitly saved and restored), * or it is merely in the root set (in which case it is always * present). */ int is_persistent() const { return !transient_ && (in_root_set_ || is_saveable()); } }; /* ------------------------------------------------------------------------ */ /* * Object table. */ class CVmObjTable { public: /* create the table */ CVmObjTable(); /* initialize */ void init(VMG0_); /* * Clear the object table. This deletes the garbage collected objects, * but leaves the table itself intact. */ void clear_obj_table(VMG0_); /* * Destroy the table - call this rather BEFORE using operator delete * directly. After this routine is called, the object table can be * deleted. */ void delete_obj_table(VMG0_); /* clients must call delete_obj_table() before deleting the object */ ~CVmObjTable(); /* get an object given an object ID */ inline CVmObject *get_obj(vm_obj_id_t id) const { /* get the page entry, and get the object from the entry */ return (CVmObject *)&get_entry(id)->ptr_.obj_; } /* * Turn garbage collection on or off. When performing a series of * allocations of values that won't be stored on the stack, this can * be used to ensure that the intermediate allocations aren't * collected as unreferenced before the group of operations is * completed. Returns previous status for later restoration. */ int enable_gc(VMG_ int enable); /* allocate a new object ID */ vm_obj_id_t alloc_obj(VMG_ int in_root_set) { /* allocate, using maximally conservative GC characteristics */ return alloc_obj(vmg_ in_root_set, TRUE, TRUE); } /* allocate a new object ID */ vm_obj_id_t alloc_obj(VMG_ int in_root_set, int can_have_refs, int can_have_weak_refs); /* * Allocate an object at a particular object ID. This is used when * loading objects from an image file or restoring objects from a * saved state file, since objects must be loaded or restored with * the same object number which they were originally assigned. This * routine throws an error if the object is already allocated. */ void alloc_obj_with_id(vm_obj_id_t id, int in_root_set) { /* allocate with maximally conservative GC characteristics */ alloc_obj_with_id(id, in_root_set, TRUE, TRUE); } /* allocate an object with a given ID */ void alloc_obj_with_id(vm_obj_id_t id, int in_root_set, int can_have_refs, int can_have_weak_refs); /* * Collect all garbage. This runs an entire garbage collection pass * to completion with a single call. This can be used for * simplicity when the caller does not require incremental operation * of the garbage collector. * * This function actually performs two garbage collection passes to * ensure that all collectible objects are collected. We perform * one pass to detect finalizable objects, then finalize all objects * that we can, then make one more pass to sweep up all of the * finalized objects that can be deleted. */ void gc_full(VMG0_); /* * Incremental garbage collection. Call gc_pass_init() to * initialize the pass. Call gc_pass_continue() repeatedly to * perform incremental collection; this routine runs for a short * time and then returns. gc_pass_continue() returns true if * there's more work to do, false if not, so the caller can stop * invoking it as soon as it returns false. Call gc_pass_finish() * to complete the garbage collection. gc_pass_finish() will call * gc_pass_continue() if necessary to finish its work, so the caller * need not keep invoking gc_pass_continue() if it runs out of work * to interleave with the garbage collector. * * Once garbage collection is started, it must be finished before * any other VM activity occurs. So, after a call to * gc_pass_init(), the caller is not allowed to perform any other VM * operations until gc_pass_finish() returns (in particular, no new * objects may be created, and no references to existing objects may * be created or changed). */ void gc_pass_init(VMG0_); int gc_pass_continue(VMG0_) { return gc_pass_continue(vmg_ TRUE); } void gc_pass_finish(VMG0_); /* * Run pending finalizers. This can be run at any time other than * during garbage collection (i.e., between gc_pass_init() and * gc_pass_finish()). */ void run_finalizers(VMG0_); /* * Determine if a given object is subject to deletion. This only * gives meaningful results during the final garbage collector pass. * Returns true if the object is ready for deletion, which can only * happen when the object is both Unreachable and Finalized, or * false if it not. A true return means that the object can be * deleted at any time. This can be used by weak referencers to * determine if objects they are referencing are about to be * deleted, and thus that the weak reference must be forgotten. */ int is_obj_deletable(vm_obj_id_t obj) const { /* * If it's not a valid object, consider it deletable, since it * has indeed already been deleted; otherwise, it's deletable if * its object table entry is deletable. */ return (obj == VM_INVALID_OBJ || get_entry(obj)->is_deletable()); } /* * Mark object references (for GC tracing) made in an object's undo * record. If the object is marked as having no possibility of * containing references to other objects, we won't bother invoking * the object's tracing method, as we can be assured that the undo * records won't contain any references either. */ void mark_obj_undo_rec(VMG_ vm_obj_id_t obj, struct CVmUndoRecord *undo_rec) { /* get the object entry */ CVmObjPageEntry *entry = get_entry(obj); /* * if the object can have any references, mark any references the * object makes from the undo record; if the object can't have any * references, assume its undo records cannot either, in which * case there should be nothing to mark */ if (entry->can_have_refs_) entry->get_vm_obj()->mark_undo_ref(vmg_ undo_rec); } /* * Remove stale weak undo references for an object. If the object is * marked as having no possibility of weak references, we won't bother * invoking the object's weak undo reference remover method, since we * know it won't do anything. */ void remove_obj_stale_undo_weak_ref(VMG_ vm_obj_id_t obj, struct CVmUndoRecord *undo_rec) { /* get the object entry */ CVmObjPageEntry *entry = get_entry(obj); /* * if the object can have weak references, notify it; if not, * there's no need to do anything */ if (entry->can_have_weak_refs_) entry->get_vm_obj()->remove_stale_undo_weak_ref(vmg_ undo_rec); } /* * Determine if the given object is part of the latest undo savepoint. * Returns true if an undo savepoint has been created since the object * was created, false if not. */ int is_obj_in_undo(vm_obj_id_t obj) const { return (obj != VM_INVALID_OBJ && get_entry(obj)->is_in_undo()); } /* * Determine if a vm_val_t contains a reference to a deletable object. * This is a simple convenience routine. This returns true only if * the value contains a valid object reference, and the object is * currently deletable. */ int is_obj_deletable(const vm_val_t *val) const { return (val->typ == VM_OBJ && val->val.obj != VM_INVALID_OBJ && is_obj_deletable(val->val.obj)); } /* * Determine if the object is saveable. If this returns true, the * object should be saved to a saved state file. If not, the object * should not be included in the saved state file. Note that a * non-saveable object might still be a persistent object - if the * object is a root set object and hasn't been modified since * loading, it's still peristent even though it doesn't get written * to the saved state file. */ int is_obj_saveable(vm_obj_id_t obj) const { return (obj != VM_INVALID_OBJ && get_entry(obj)->is_saveable()); } /* * Determine if the object is persistent in a saved state. If this * returns true, the object will survive saving and restoring the * machine state; if not, the object will not be present after the * machine state is restored. This can be used to test if a weak * reference should be included in a saved state file. */ int is_obj_persistent(vm_obj_id_t obj) const { return (obj != VM_INVALID_OBJ && get_entry(obj)->is_persistent()); } /* determine if the given object is transient */ int is_obj_transient(vm_obj_id_t obj) const { return (obj != VM_INVALID_OBJ && get_entry(obj)->transient_); } /* mark an object as transient */ void set_obj_transient(vm_obj_id_t obj) const { /* set the 'transient' flag in the object */ get_entry(obj)->transient_ = TRUE; } /* set an object's garbage collection characteristics */ void set_obj_gc_characteristics( vm_obj_id_t obj, int can_have_refs, int can_have_weak_refs) const { CVmObjPageEntry *entry; /* get the object's entry */ entry = get_entry(obj); /* set the entry's GC flags as specified */ entry->can_have_refs_ = can_have_refs; entry->can_have_weak_refs_ = can_have_weak_refs; } /* determine if the given object is in the root set */ int is_obj_in_root_set(vm_obj_id_t obj) const { return (obj != VM_INVALID_OBJ && get_entry(obj)->in_root_set_); } /* * Mark the given object as referenced, and recursively mark all of * the objects to which it refers as referenced. */ void mark_all_refs(vm_obj_id_t obj, uint state) { add_to_gc_queue(obj, state); } /* * Receive notification from the undo manager that we're starting a * new savepoint. We'll simply notify all of the objects of this. */ void notify_new_savept(); /* * Apply an undo record */ void apply_undo(VMG_ struct CVmUndoRecord *rec); /* call a callback for each object in the table */ void for_each(VMG_ void (*func)(VMG_ vm_obj_id_t, void *), void *ctx); /* * Rebuild the image file's OBJS blocks for a particular metaclass. * We'll write all of the objects of the given metaclass to one or * more OBJS blocks in the given output file. This can be used to * dump the program state to a new image file after running * 'preinit' or a similar compile-time pre-initialization procedure. * * 'meta_dep_idx' is the index in the metaclass dependency table of * the metaclass to be written. */ void rebuild_image(VMG_ int meta_dep_idx, class CVmImageWriter *writer, class CVmConstMapper *mapper); /* * Scan all objects and add metaclass entries to the metaclass * dependency table for any metaclasses of which there are existing * instances. */ void add_metadeps_for_instances(VMG0_); /* * Scan all active objects and convert objects to constant data * where possible. Certain object metaclasses, such as strings and * lists, can be represented in a rebuilt image file as constant * data; this routine makes all of these conversions. */ void rebuild_image_convert_const_data(VMG_ class CVmConstMapper *const_mapper); /* * Get the maximum object ID that has ever been allocated. This * establishes an upper bound on the object ID's that can be found * among the active objects. */ vm_obj_id_t get_max_used_obj_id() const { return pages_used_ * VM_OBJ_PAGE_CNT; } /* determine if an object ID refers to a valid object */ int is_obj_id_valid(vm_obj_id_t obj) const { /* * the object is valid as long as it's not free, and the ID is * within the valid range */ return (obj != VM_INVALID_OBJ && obj < get_max_used_obj_id() && !get_entry(obj)->free_); } /* * Get the object state. This is intended primarily as a debugging * and testing aid for the VM itself; this value should be of no * interest to normal programs. Returns a value suitable for use * with CVmBifT3Test::get_obj_gc_state(). */ ulong get_obj_internal_state(vm_obj_id_t id) const { /* if the object ID is invalid, return 0xF000 to so indicate */ if (id >= get_max_used_obj_id()) return 0xF000; /* * return the state as a combination of these bits: * * free ? 0 : 1 *. Unreachable=0x00, F-Reachable=0x10, Reachable=0x20 *. Unfinalizable=0x000, Finalizable=0x100, Finalized=0x200 */ return ((get_entry(id)->free_ ? 0 : 1) + (((ulong)get_entry(id)->reachable_) << 4) + (((ulong)get_entry(id)->finalize_state_) << 8)); } /* * Reset to the initial image file state. Discards all objects not * in the root set, skipping finalizers, and resets all objects to * their initial image file state. */ void reset_to_image(VMG0_); /* * Save state to a file. We write out each object's state to the * file so that the state can be restored later. */ void save(VMG_ class CVmFile *fp); /* * Restore state from a previously saved file. Returns zero on * success, or a VMERR_xxx code on failure. * * This routine creates an object fixup table, and returns it in * *fixups. The caller is responsible for deleting this object if a * non-null pointer is returned in *fixups. */ int restore(VMG_ class CVmFile *fp, class CVmObjFixup **fixups); /* * Save an object's image data pointer. An object's load_from_image() * routine may call this routine (it cannot be called from anywhere * else) to save the loaded image location for the object. For each * object that calls this routine, we will call the object's * reload_from_image() method during a reset to initial image file * state. */ void save_image_pointer(vm_obj_id_t obj, const char *ptr, size_t siz); /* * Request post-load initialization. An object can call this to * request that its post_load_init() method be called after an initial * program load, restart, or restore operation. The post_load_init() * method will not be called until the load/restart/restore operation * has loaded every object, so the method can be used to perform any * initialization that depends upon other objects being loaded. */ void request_post_load_init(vm_obj_id_t obj); /* remove a post-load initialization request */ void remove_post_load_init(vm_obj_id_t obj); /* invoke all registered post-load initializations */ void do_all_post_load_init(VMG0_); /* * Ensure that the given object has had its post-load initialization * performed during the current load/restart/restore. If an object's * post-load initialization depends upon another object having already * been initialized, the first object should call this to ensure that * the other object it depends upon has been initialized. This allows * objects to ensure that initialization dependencies are handled in * the correct order, regardless of the order in which the objects were * loaded. * * Post-load initialization is guaranteed to be executed exactly once * per load/restart/restore cycle. When this routine is called, we * check to see if the target object has already been initialized * during this operation, and we do nothing if so. If we do invoke the * target object's post_load_init(), then this will be the only * invocation for this operation. * * Circular dependencies are prohibited. If object A's * post_load_init() method calls this to initialize object B, we will * invoke object B's post_load_init(). If object B in turn calls this * routine to initialize object A, we will observe that object A is * already in the process of being initialized and throw an error. */ void ensure_post_load_init(VMG_ vm_obj_id_t obj); /* * Add an object to the list of machine globals. An object added to * this list will never be deleted. If the object is in the root set * (which means it was loaded from the image file), this has no effect, * since a root object is inherently global. */ void add_to_globals(vm_obj_id_t obj); /* * Create a "global variable." A global variable is part of the root * set: any value in a variable allocated here will be traced during * garbage collection. Global variables are meant for use by other * subsystems, so that other subsystems can include their own static * and global variables in the root set. * * Global variables aren't affected by RESTORE, RESTART, or UNDO (like * the stack, global variables are transient). * * The caller can use delete_global_var() to delete the variable when * done with it. Any global variable that's never explicitly deleted * will be automatically deleted when the object table itself is * destroyed. */ vm_globalvar_t *create_global_var(); void delete_global_var(vm_globalvar_t *var); /* count an allocation */ void count_alloc(size_t siz) { bytes_since_gc_ += siz; } private: /* rebuild the image, writing only transient or only persistent objects */ void rebuild_image(VMG_ int meta_dep_idx, CVmImageWriter *writer, class CVmConstMapper *mapper, int trans); /* invoke a post-load initialization method */ static void call_post_load_init(VMG_ class CVmHashEntryPLI *entry); /* enumeration callbacks for post-load initialization */ static void pli_status_cb(void *ctx, class CVmHashEntry *entry); static void pli_invoke_cb(void *ctx, class CVmHashEntry *entry); /* get the page entry for a given ID */ inline CVmObjPageEntry *get_entry(vm_obj_id_t id) const { return &pages_[id >> VM_OBJ_PAGE_CNT_LOG2][id & (VM_OBJ_PAGE_CNT - 1)]; } /* delete an entry */ void delete_entry(VMG_ vm_obj_id_t id, CVmObjPageEntry *entry); /* allocate a new page of objects */ void alloc_new_page(); /* * initialize a newly-allocated object table entry -- removes the * entry from the free list, marks the entry as allocated, marks it * in or out of the root set as appropriate, and initializes its GC * status as appropriate */ void init_entry_for_alloc(vm_obj_id_t id, CVmObjPageEntry *entry, int in_root_set, int can_have_refs, int can_have_weak_refs); /* * Mark an object as referenced for the garbage collector and add it * to the garbage collector's work queue. If the object is already * marked as referenced, this does nothing. */ void add_to_gc_queue(vm_obj_id_t id, uint state) { /* get the object header and add it to the work queue */ add_to_gc_queue(id, get_entry(id), state); } /* add an object to the gc work queue given the object header entry */ void add_to_gc_queue(vm_obj_id_t id, CVmObjPageEntry *entry, uint state) { /* * If it's not already referenced somehow, add it to the queue. If * it's marked as referenced, it's already in the queue (or it's * already been in the queue and it's been processed). * * If the object can't have references to other objects, don't add * it to the queue - simply elevate its reachability state. We put * objects in the queue in order to trace into the objects they * reference, so an object that can't reference any other objects * doesn't need to go in the queue. */ if (entry->can_have_refs_ && entry->reachable_ == VMOBJ_UNREACHABLE) { /* add it to the work queue */ entry->next_obj_ = gc_queue_head_; gc_queue_head_ = id; /* * Since the entry is unreachable, and unreachable is the * lowest reachability state, we know that 'state' is at least * as reachable, so we can just set the new state without even * bothering with another comparison. */ entry->reachable_ = state; } else { /* * Elevate the reachability state. Never reduce an object's * reachability state: Finalizer-Reachable is higher than * Unreachable, and Reachable is higher than * Finalizer-Reachable. * * In other words, if an object is already marked Reachable, * never reduce its state to Finalizer-Reachable just because * we find that it can also be reached from a * Finalizer-Reachable object, when we already know that it * can be reached from a root-set object. */ if (state > entry->reachable_) entry->reachable_ = state; } } /* * Add an object to the finalizer work queue. An object can only be * in one queue - it can't be in both the finalizer queue and the * main gc work queue. */ void add_to_finalize_queue(vm_obj_id_t id, CVmObjPageEntry *entry) { /* mark the object as finalizer-reachable */ entry->reachable_ = VMOBJ_F_REACHABLE; /* link it into the finalize list */ entry->next_obj_ = finalize_queue_head_; finalize_queue_head_ = id; } /* * Count an allocation and check to see if we should run garbage * collection. We run gc after a certain number of consecutive * allocations to ensure that allocation-intensive operations don't * fill up memory if we can avoid it by removing unreferenced * objects. */ void alloc_check_gc(VMG_ int do_count) { /* count the allocation if desired */ if (do_count) ++allocs_since_gc_; /* if we've passed our threshhold for collecting garbage, do so now */ if (gc_enabled_ && (allocs_since_gc_ > max_allocs_between_gc_ || bytes_since_gc_ > max_bytes_between_gc_)) gc_before_alloc(vmg0_); } /* * Run a garbage collection in preparation to allocate memory. Runs * one garbage collection pass then one finalizer pass. */ void gc_before_alloc(VMG0_); /* garbage collection: trace objects reachable from the stack */ void gc_trace_stack(VMG0_); /* garbage collection: trace objects reachable from the imports */ void gc_trace_imports(VMG0_); /* garbage collection: trace objects reachable from machine globals */ void gc_trace_globals(VMG0_); /* garbage collection: trace all objects reachable from the work queue */ void gc_trace_work_queue(VMG_ int trace_transient); /* continue a GC pass */ int gc_pass_continue(VMG_ int trace_transient); /* * set the initial GC conditions for an object -- this puts the * object into the appropriate queue and sets the appropriate * reachability state in preparation for the start of the next GC * pass */ void gc_set_init_conditions(vm_obj_id_t id, struct CVmObjPageEntry *entry) { /* * Mark the object as unreachable -- at the start of each GC pass, * all non-root-set objects must be marked unreachable. (It * doesn't matter how we mark root set objects, so we simply mark * everything as reachable to avoid an unnecessary test.) */ entry->reachable_ = VMOBJ_UNREACHABLE; /* * If it's in the root set, add it to the GC work queue -- all * root-set objects must be in the work queue and marked as * reachable at the start of each GC pass. */ if (entry->in_root_set_) add_to_gc_queue(id, entry, VMOBJ_REACHABLE); } /* hash table of objects requested post_load_init() service */ class CVmHashTable *post_load_init_table_; /* * Head of global object page list. The global objects are objects * that are always reachable but which weren't necessarily loaded from * the image file; these objects must be treated as dynamically * created for purposes such as saving, but must never be deleted as * long as they are globally reachable. */ CVmObjGlobPage *globals_; /* head of global variable list */ struct vm_globalvar_t *global_var_head_; /* * Master page table. This is an array of pointers to pages. Each * page contains a fixed number of slots for fixed-size parts. */ CVmObjPageEntry **pages_; /* number of page slots allocated, and the number actually used */ size_t page_slots_; size_t pages_used_; /* first free object */ vm_obj_id_t first_free_; /* first page of saved image data pointers */ struct vm_image_ptr_page *image_ptr_head_; /* last page of saved image data pointers */ struct vm_image_ptr_page *image_ptr_tail_; /* number of image data pointers stored on last image pointer page */ size_t image_ptr_last_cnt_; /* head of garbage collection work queue */ vm_obj_id_t gc_queue_head_; /* head of finalizer queue */ vm_obj_id_t finalize_queue_head_; /* * Allocations since last garbage collection. We increment this on * each allocation, and reset it to zero each time we collect * garbage. When we've performed too many allocations since the * last garbage collection, we force a gc pass. */ uint allocs_since_gc_; /* * Bytes allocated since the last garbage collection pass. We add each * heap allocation request to this, and zero it each time we collect * garbage. This lets us trigger an early collection pass (before we'd * collect based on object count) if a lot of large objects are being * thrown around, to ensure that our OS-level working set doesn't grow * too fast due to rapid creation of large unreachable objects. */ ulong bytes_since_gc_; /* * Maximum number of objects and heap bytes allocated before we run the * garbage collector. */ uint max_allocs_between_gc_; ulong max_bytes_between_gc_; /* garbage collection enabled */ uint gc_enabled_ : 1; }; /* ------------------------------------------------------------------------ */ /* * An image data pointer. The object table uses this structure to save * the image data location for a given object when the object requests * that this information be saved. */ struct vm_image_ptr { /* object ID */ vm_obj_id_t obj_id_; /* pointer to image data and length of the data */ const char *image_data_ptr_; size_t image_data_len_; }; /* * Maximum number of image pointers stored per image pointer page */ const size_t VM_IMAGE_PTRS_PER_PAGE = 400; /* * A page of image pointers. */ struct vm_image_ptr_page { /* next page in the list of pages */ vm_image_ptr_page *next_; /* array of image pointers */ vm_image_ptr ptrs_[VM_IMAGE_PTRS_PER_PAGE]; }; /* ------------------------------------------------------------------------ */ /* * The variable-size parts are stored in a heap separately from the object * headers. Because the variable-size objects can expand or contract * dynamically, objects in the heap can move to new addresses. * * A particular block of memory in the heap is referenced by zero or one * object at any given time; a heap block is never shared among multiple * objects. Furthermore, the only thing that can directly reference a * heap block is an object's fixed portion (through its extension * pointer). Hence, we manage the variable heap directly through the * object headers: when an object becomes unreferenced, we explicitly free * the associated variable part. * * It is possible for a single object to allocate more than one heap * block. In practice, most objects will allocate only one heap block (if * they allocate a heap block at all), but there is no reason an object * can't allocate more than one block. * * Note that polymorphism in the variable parts is handled via the fixed * part. Because the fixed part is responsible for interactions with the * variable part, each fixed part implementation will be mated to a * particular variable part implementation. These implementations may * themselves be polymorphic, of course, but this isn't directly necessary * in the base class. */ /* * Variable-size object heap interface. */ class CVmVarHeap { public: virtual ~CVmVarHeap() { } /* * Initialize. The global object table is valid at this point, and * will remain valid until after terminate() is called. */ virtual void init(VMG0_) = 0; /* * Terminate. The global object table will remain valid until after * this function returns. * * The object table is expected to free each object's variable part * explicitly, so this function need not deallocate the memory used * by variable parts. */ virtual void terminate() = 0; /* * Allocate a variable-size part. 'siz' is the size requested in * bytes, and 'obj' is a pointer to the object header. The object * header will never move in memory, so this pointer is valid for as * long as the object remains allocated, hence the heap manager can * store the header pointer with the memory block if desired. */ virtual void *alloc_mem(size_t siz, CVmObject *obj) = 0; /* * Resize a variable-size part. The 'siz' is the new size requested * in bytes, and 'varpart' is the old variable-size memory block. * This should return a new variable-size memory block containing a * copy of the data in the original block, but the block should be * resized to at least 'siz' bytes. Returns a pointer to the new * block, which may move to a new memory location. * * If we move the memory to a new location, we are responsible for * freeing the old block of memory that the variable part occupied, * if necessary. * * We do not need to worry about informing the object header of any * change to the address of the variable part; the caller is * responsible for making any necessary changes in the object header * based on our return value. */ virtual void *realloc_mem(size_t siz, void *varpart, CVmObject *obj) = 0; /* * Free an object in the heap. The object header may no longer be * valid after this function returns, so the heap manager should not * store the object header pointer after this function returns. */ virtual void free_mem(void *varpart) = 0; #if 0 /* * This is not currently used by the heap implementation (and doesn't * even have any theoretical reason to exist at the moment, since the * adoption of a non-moveable heap policy and the removal of * move_var_part()), so it's been removed to avoid the unnecessary * overhead of calling an empty method. */ /* * Receive notification of the completion of a garbage collection * pass. If the heap manager is capable of closing gaps in memory * by moving objects around, this is a good time to perform this * work, since we have just deleted all unreachable objects. * * If any object moves during processing here, we must call the * associated CVmObject's move_var_part() routine to tell it about * its new location. * * This routine isn't required to do anything at all. It's simply * provided as a notification for heap managers that can take * advantage of the opportunity to compact the heap. */ virtual void finish_gc_pass() = 0; #endif }; /* ------------------------------------------------------------------------ */ /* * Simple variable-size object heap implementation. This implementation * uses the normal C heap manager (malloc and free) to manage the heap. */ /* * block header - we use the header to keep track of the size of the * object's data area */ struct CVmVarHeapMallocHdr { /* size of the object */ size_t siz_; }; /* * heap implementation */ class CVmVarHeapMalloc: public CVmVarHeap { public: CVmVarHeapMalloc() { } ~CVmVarHeapMalloc() { } /* initialize */ void init(VMG0_) { } /* terminate */ void terminate() { } /* allocate memory */ void *alloc_mem(size_t siz, CVmObject *) { /* allocate space for the block plus the header */ CVmVarHeapMallocHdr *hdr = (CVmVarHeapMallocHdr *) t3malloc(siz + sizeof(CVmVarHeapMallocHdr)); /* set up the header */ hdr->siz_ = siz; /* return the start of the part immediately after the header */ return (void *)(hdr + 1); } /* reallocate memory */ void *realloc_mem(size_t siz, void *varpart, CVmObject *) { CVmVarHeapMallocHdr *hdr; /* * get the original header, which immediately precedes the * original variable part in memory */ hdr = ((CVmVarHeapMallocHdr *)varpart) - 1; /* * Reallocate it - the header is the actual memory block as far * as malloc was concerned, so realloc that. Note that we must * add in the space needed for our header in the resized block. */ hdr = (CVmVarHeapMallocHdr *) t3realloc(hdr, siz + sizeof(CVmVarHeapMallocHdr)); /* adjust the size of the block in the header */ hdr->siz_ = siz; /* return the part immediately after the header */ return (void *)(hdr + 1); } /* free memory */ void free_mem(void *varpart) { CVmVarHeapMallocHdr *hdr; /* * get the original header, which immediately precedes the * original variable part in memory */ hdr = ((CVmVarHeapMallocHdr *)varpart) - 1; /* * free the header, which is the actual memory block as far as * malloc was concerned */ t3free(hdr); } #if 0 /* removed with the removal of move_var_part() */ /* * complete garbage collection pass - we don't have to do anything * here, since we can't move objects around to consolidate free * space */ void finish_gc_pass() { } #endif private: }; /* ------------------------------------------------------------------------ */ /* * Hybrid cell-based and malloc-based heap allocator. This heap manager * uses arrays of fixed-size blocks to allocate small objects, and falls * back on malloc for allocating large objects. Small-block allocations * and frees are fast, require very little memory overhead, and minimize * heap fragmentation by packing large blocks of fixed-size items into * arrays, then suballocating out of free lists built from the arrays. */ /* * Each item we allocate from a small-object array has a header that * points back to the array's master list. This is necessary so that we * can put the object back in the appropriate free list when it is * deleted. */ struct CVmVarHeapHybrid_hdr { /* * the block interface that allocated this object -- we use this * when we free the object so that we can call the free() routine in * the block manager that originally did the allocation */ class CVmVarHeapHybrid_block *block; /* requested allocation size of the block, if collecting statistics */ IF_GC_STATS(size_t siz;) }; /* * Hybrid heap allocator - sub-block interface. Each small-object cell * list is represented by one of these objects, as is the fallback * malloc allocator. */ class CVmVarHeapHybrid_block { public: virtual ~CVmVarHeapHybrid_block() {} /* allocate memory */ virtual struct CVmVarHeapHybrid_hdr *alloc(size_t siz) = 0; /* free memory */ virtual void free(struct CVmVarHeapHybrid_hdr *) = 0; /* * Reallocate memory. If necessary, allocate new memory, copy the * data to the new memory, and delete the old memory. We receive * the heap manager as an argument so that we can call it to * allocate new memory if necessary. */ virtual void *realloc(struct CVmVarHeapHybrid_hdr *mem, size_t siz, class CVmObject *obj) = 0; /* requested allocation size of the block, if collecting statistics */ IF_GC_STATS(size_t siz;) }; /* * Malloc suballocator */ class CVmVarHeapHybrid_malloc: public CVmVarHeapHybrid_block { public: /* allocate memory */ virtual struct CVmVarHeapHybrid_hdr *alloc(size_t siz) { CVmVarHeapHybrid_hdr *ptr; /* adjust the size to add in the required header */ siz = osrndsz(siz + sizeof(CVmVarHeapHybrid_hdr)); /* allocate directly via the default system heap manager */ ptr = (CVmVarHeapHybrid_hdr *)t3malloc(siz); /* fill in the header */ ptr->block = this; /* return the new block */ return ptr; } /* release memory */ virtual void free(CVmVarHeapHybrid_hdr *mem) { /* release the memory directly to the default system heap manager */ t3free(mem); } /* reallocate memory */ virtual void *realloc(struct CVmVarHeapHybrid_hdr *mem, size_t siz, CVmObject *) { CVmVarHeapHybrid_hdr *ptr; /* adjust the new size to add in the required header */ siz = osrndsz(siz + sizeof(CVmVarHeapHybrid_hdr)); /* reallocate the block */ ptr = (CVmVarHeapHybrid_hdr *)t3realloc(mem, siz); /* fill in the header in the new block */ ptr->block = this; /* return the caller-visible part of the new block */ return (void *)(ptr + 1); } }; /* * Small-object array list head. We suballocate small objects from * these arrays. We maintain one of these objects for each distinct * cell size; this object manages all of the storage for blocks of the * cell size. */ class CVmVarHeapHybrid_head: public CVmVarHeapHybrid_block { public: CVmVarHeapHybrid_head(class CVmVarHeapHybrid *mem_mgr, size_t cell_size, size_t page_count) { /* remember our memory manager */ mem_mgr_ = mem_mgr; /* remember our cell size and number of items per array */ cell_size_ = cell_size; page_count_ = page_count; /* we have nothing in our free list yet */ first_free_ = 0; } /* allocate an object from my pool, expanding the pool if necessary */ CVmVarHeapHybrid_hdr *alloc(size_t siz); /* free a cell */ void free(CVmVarHeapHybrid_hdr *mem); /* reallocate */ virtual void *realloc(struct CVmVarHeapHybrid_hdr *mem, size_t siz, class CVmObject *obj); /* get the cell size for this cell manager */ size_t get_cell_size() const { return cell_size_; } private: /* size of each cell in the array */ size_t cell_size_; /* number of items we allocate per array */ size_t page_count_; /* head of the free list of cells in this array */ void *first_free_; /* our memory manager */ CVmVarHeapHybrid *mem_mgr_; }; /* * Small-object array list block. We dynamically allocate these array * blocks as needed to hold blocks of a particular size. */ struct CVmVarHeapHybrid_array { /* next array in the master list */ CVmVarHeapHybrid_array *next_array; /* * memory for allocation (we over-allocate the structure to make * room for some number of our fixed-size cells) */ char mem[1]; }; /* * heap implementation */ class CVmVarHeapHybrid: public CVmVarHeap { friend class CVmVarHeapHybrid_head; public: CVmVarHeapHybrid(CVmObjTable *objtab); ~CVmVarHeapHybrid(); /* initialize */ void init(VMG0_) { } /* terminate */ void terminate() { } /* allocate memory */ void *alloc_mem(size_t siz, CVmObject *obj); /* reallocate memory */ void *realloc_mem(size_t siz, void *varpart, CVmObject *obj); /* free memory */ void free_mem(void *varpart); #if 0 /* removed with the removal of move_var_part() */ /* * complete garbage collection pass - we don't have to do anything * here, since we can't move objects around to consolidate free * space */ void finish_gc_pass() { } #endif private: /* * Head of list of arrays. We keep this list so that we can delete * all of the arrays when we delete this heap manager object itself. */ CVmVarHeapHybrid_array *first_array_; /* * Array of cell-based subheap managers. This array will be ordered * from smallest to largest, so we can search it for the best fit to * a requested size. */ CVmVarHeapHybrid_head **cell_heaps_; /* number of cell heap managers */ size_t cell_heap_cnt_; /* * Our fallback malloc heap manager. We'll use this allocator for * any blocks that we can't allocate from one of our cell-based * memory managers. */ CVmVarHeapHybrid_malloc *malloc_heap_; /* object table */ CVmObjTable *objtab_; }; /* ------------------------------------------------------------------------ */ /* * Memory Manager - this is the primary interface to the object memory * subsystem. */ class CVmMemory { public: /* create the memory manager, using a given variable-size heap */ CVmMemory(VMG_ CVmVarHeap *varheap); /* delete the memory manager */ ~CVmMemory() { /* tell the variable-size heap to disengage */ varheap_->terminate(); } /* get the variable heap manager */ CVmVarHeap *get_var_heap() const { return varheap_; } private: /* variable-size object heap */ CVmVarHeap *varheap_; /* our constant pool manager */ class CVmPool *constant_pool_; }; /* ------------------------------------------------------------------------ */ /* * Allocate a new object ID */ inline vm_obj_id_t vm_new_id(VMG_ int in_root_set) { /* ask the global object table to allocate a new ID */ return G_obj_table->alloc_obj(vmg_ in_root_set); } /* * Allocate a new object ID, setting GC characteristics */ inline vm_obj_id_t vm_new_id(VMG_ int in_root_set, int can_have_refs, int can_have_weak_refs) { /* ask the global object table to allocate a new ID */ return G_obj_table->alloc_obj(vmg_ in_root_set, can_have_refs, can_have_weak_refs); } /* * Given an object ID, get a pointer to the object */ inline CVmObject *vm_objp(VMG_ vm_obj_id_t id) { /* ask the global object table to translate the ID */ return G_obj_table->get_obj(id); } /* ------------------------------------------------------------------------ */ /* * Dynamic cast of a vm_val_t to a given object type. Returns null if the * vm_val_t isn't an object of the given type. */ #define vm_val_cast(cls, vv) \ (vm_val_cast_ok(cls, vv) ? (cls *)vm_objp(vmg_ (vv)->val.obj) : 0) /* * Will a dynamic cast of a vm_val_t to the given object type succeed? */ #define vm_val_cast_ok(cls, vv) \ ((vv)->typ == VM_OBJ && \ vm_objp(vmg_ ((vv)->val.obj))->is_of_metaclass(cls::metaclass_reg_)) /* * Cast a vm_obj_id_t to a given object type */ #define vm_objid_cast(cls, id) \ (id != VM_INVALID_OBJ \ && vm_objp(vmg_ id)->is_of_metaclass(cls::metaclass_reg_) \ ? (cls *)vm_objp(vmg_ id) : 0) /* * Get the class object for a given metaclass */ #define vm_classobj_for(cls) \ vm_objid_cast(CVmObjClass, cls::metaclass_reg_->get_class_obj(vmg0_)) #endif /* VMOBJ_H */ /* * Register the root object class */ VM_REGISTER_METACLASS(CVmObject) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmop.cpp��������������������������������������������������������������������������0000664�0000000�0000000�00000051304�12650170723�0015346�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* Copyright (c) 2012 by Michael J. Roberts. All Rights Reserved. */ /* Name vmop.cpp - TADS 3 VM opcode definitions Function Notes Modified 03/14/12 MJRoberts - Creation */ #include "t3std.h" #include "vmop.h" #include "vmtype.h" /* * Get the size of an opcode */ size_t CVmOpcodes::get_op_size(const uchar *op) { switch (*op) { case OPC_PUSHSTRI: return 3 + osrp2(op+1); case OPC_SWITCH: return 1 + 2 + (VMB_DATAHOLDER + 2)*osrp2(op+1) + 2; case OPC_NAMEDARGTAB: return 1 + 2 + osrp2(op+1); default: /* all others have fixed sizes */ return op_siz[*op]; } } /* * Table of T3 VM opcode sizes */ const uchar CVmOpcodes::op_siz[] = { 0, /* 0x00 - unused */ 1, /* 0x01 - OPC_PUSH_0 */ 1, /* 0x02 - OPC_PUSH_1 */ 2, /* 0x03 - OPC_PUSHINT8 */ 5, /* 0x04 - OPC_PUSHINT */ 5, /* 0x05 - OPC_PUSHSTR */ 5, /* 0x06 - OPC_PUSHLST */ 5, /* 0x07 - OPC_PUSHOBJ */ 1, /* 0x08 - OPC_PUSHNIL */ 1, /* 0x09 - OPC_PUSHTRUE */ 3, /* 0x0A - OPC_PUSHPROPID */ 5, /* 0x0B - OPC_PUSHFNPTR */ 0, /* 0x0C - OPC_PUSHSTRI - variable-size instruction */ 2, /* 0x0D - OPC_PUSHPARLST */ 1, /* 0x0E - OPC_MAKELSTPAR */ 5, /* 0x0F - OPC_PUSHENUM */ 5, /* 0x10 - OPC_PUSHBIFPTR */ 1, /* 0x11 - unused */ 1, /* 0x12 - unused */ 1, /* 0x13 - unused */ 1, /* 0x14 - unused */ 1, /* 0x15 - unused */ 1, /* 0x16 - unused */ 1, /* 0x17 - unused */ 1, /* 0x18 - unused */ 1, /* 0x19 - unused */ 1, /* 0x1A - unused */ 1, /* 0x1B - unused */ 1, /* 0x1C - unused */ 1, /* 0x1D - unused */ 1, /* 0x1E - unused */ 1, /* 0x1F - unused */ 1, /* 0x20 - OPC_NEG */ 1, /* 0x21 - OPC_BNOT */ 1, /* 0x22 - OPC_ADD */ 1, /* 0x23 - OPC_SUB */ 1, /* 0x24 - OPC_MUL */ 1, /* 0x25 - OPC_BAND */ 1, /* 0x26 - OPC_BOR */ 1, /* 0x27 - OPC_SHL */ 1, /* 0x28 - OPC_ASHR */ 1, /* 0x29 - OPC_XOR */ 1, /* 0x2A - OPC_DIV */ 1, /* 0x2B - OPC_MOD */ 1, /* 0x2C - OPC_NOT */ 1, /* 0x2D - OPC_BOOLIZE */ 1, /* 0x2E - OPC_INC */ 1, /* 0x2F - OPC_DEC */ 1, /* 0x31 - OPC_LSHR */ 1, /* 0x31 - unused */ 1, /* 0x32 - unused */ 1, /* 0x33 - unused */ 1, /* 0x34 - unused */ 1, /* 0x35 - unused */ 1, /* 0x36 - unused */ 1, /* 0x37 - unused */ 1, /* 0x38 - unused */ 1, /* 0x39 - unused */ 1, /* 0x3A - unused */ 1, /* 0x3B - unused */ 1, /* 0x3C - unused */ 1, /* 0x3D - unused */ 1, /* 0x3E - unused */ 1, /* 0x3F - unused */ 1, /* 0x40 - OPC_EQ */ 1, /* 0x41 - OPC_NE */ 1, /* 0x42 - OPC_LT */ 1, /* 0x43 - OPC_LE */ 1, /* 0x44 - OPC_GT */ 1, /* 0x45 - OPC_GE */ 1, /* 0x46 - unused */ 1, /* 0x47 - unused */ 1, /* 0x48 - unused */ 1, /* 0x49 - unused */ 1, /* 0x4A - unused */ 1, /* 0x4B - unused */ 1, /* 0x4C - unused */ 1, /* 0x4D - unused */ 1, /* 0x4E - unused */ 1, /* 0x4F - unused */ 1, /* 0x50 - OPC_RETVAL */ 1, /* 0x51 - OPC_RETNIL */ 1, /* 0x52 - OPC_RETTRUE */ 1, /* 0x53 - unused */ 1, /* 0x54 - OPC_RET */ 1, /* 0x55 - unused */ 4, /* 0x56 - OPC_NAMEDARGPTR */ 0, /* 0x57 - OPC_NAMEDARGTAB - variable-size operands */ 6, /* 0x58 - OPC_CALL */ 2, /* 0x59 - OPC_PTRCALL */ 1, /* 0x5A - unused */ 1, /* 0x5B - unused */ 1, /* 0x5C - unused */ 1, /* 0x5D - unused */ 1, /* 0x5E - unused */ 1, /* 0x5F - unused */ 3, /* 0x60 - OPC_GETPROP */ 4, /* 0x61 - OPC_CALLPROP */ 2, /* 0x62 - OPC_PTRCALLPROP */ 3, /* 0x63 - OPC_GETPROPSELF */ 4, /* 0x64 - OPC_CALLPROPSELF */ 2, /* 0x65 - OPC_PTRCALLPROPSELF */ 7, /* 0x66 - OPC_OBJGETPROP */ 8, /* 0x67 - OPC_OBJCALLPROP */ 3, /* 0x68 - OPC_GETPROPDATA */ 1, /* 0x69 - OPC_PTRGETPROPDATA */ 4, /* 0x6A - OPC_GETPROPLCL1 */ 5, /* 0x6B - OPC_CALLPROPLCL1 */ 3, /* 0x6C - OPC_GETPROPR0 */ 4, /* 0x6D - OPC_CALLPROPR0 */ 1, /* 0x6E - unused */ 1, /* 0x6F - unused */ 1, /* 0x70 - unused */ 1, /* 0x71 - unused */ 4, /* 0x72 - OPC_INHERIT */ 2, /* 0x73 - OPC_PTRINHERIT */ 8, /* 0x74 - OPC_EXPINHERIT */ 6, /* 0x75 - OPC_PTREXPINHERIT */ 1, /* 0x76 - OPC_VARARGC */ 4, /* 0x77 - OPC_DELEGATE */ 2, /* 0x78 - OPC_PTRDELEGATE */ 1, /* 0x79 - unused */ 1, /* 0x7A - SWAP2 */ 3, /* 0x7B - SWAPN */ 1, /* 0x7C - OPC_GETARGN0 */ 1, /* 0x7D - OPC_GETARGN1 */ 1, /* 0x7E - OPC_GETARGN2 */ 1, /* 0x7F - OPC_GETARGN3 */ 2, /* 0x80 - OPC_GETLCL1 */ 3, /* 0x81 - OPC_GETLCL2 */ 2, /* 0x82 - OPC_GETARG1 */ 3, /* 0x83 - OPC_GETARG2 */ 1, /* 0x84 - OPC_PUSHSELF */ 5, /* 0x85 - OPC_GETDBLCL */ 5, /* 0x86 - OPC_GETDBARG */ 1, /* 0x87 - OPC_GETARGC */ 1, /* 0x88 - OPC_DUP */ 1, /* 0x89 - OPC_DISC */ 2, /* 0x8A - OPC_DISC1 */ 1, /* 0x8B - OPC_GETR0 */ 3, /* 0x8C - OPC_GETDBARGC */ 1, /* 0x8D - OPC_SWAP */ 2, /* 0x8E - OPC_PUSHCTXELE */ 1, /* 0x8F - DUP2 */ 0, /* 0x90 - OPC_SWITCH - variable-size instruction */ 3, /* 0x91 - OPC_JMP */ 3, /* 0x92 - OPC_JT */ 3, /* 0x93 - OPC_JF */ 3, /* 0x94 - OPC_JE */ 3, /* 0x95 - OPC_JNE */ 3, /* 0x96 - OPC_JGT */ 3, /* 0x97 - OPC_JGE */ 3, /* 0x98 - OPC_JLT */ 3, /* 0x99 - OPC_JLE */ 3, /* 0x9A - OPC_JST */ 3, /* 0x9B - OPC_JSF */ 3, /* 0x9C - OPC_LJSR */ 3, /* 0x9D - OPC_LRET */ 3, /* 0x9E - OPC_JNIL */ 3, /* 0x9F - OPC_JNOTNIL */ 3, /* 0xA0 - OPC_JR0T */ 3, /* 0xA1 - OPC_JR0F */ 5, /* 0xA2 - OPC_ITERNEXT */ 2, /* 0xA3 - GETSETLCL1R0 */ 2, /* 0xA4 - GETSETLCL1 */ 1, /* 0xA5 - DUPR0 */ 2, /* 0xA6 - GETSPN */ 1, /* 0xA7 - unused */ 1, /* 0xA8 - unused */ 1, /* 0xA9 - unused */ 1, /* 0xAA - GETLCLN0 */ 1, /* 0xAB - GETLCLN1 */ 1, /* 0xAC - GETLCLN2 */ 1, /* 0xAD - GETLCLN3 */ 1, /* 0xAE - GETLCLN4 */ 1, /* 0xAF - GETLCLN5 */ 5, /* 0xB0 - OPC_SAY */ 3, /* 0xB1 - OPC_BUILTIN_A */ 3, /* 0xB2 - OPC_BUILTIN_B */ 3, /* 0xB3 - OPC_BUILTIN_C */ 3, /* 0xB4 - OPC_BUILTIN_D */ 3, /* 0xB5 - OPC_BUILTIN1 */ 4, /* 0xB6 - OPC_BUILTIN2 */ 0, /* 0xB7 - OPC_CALLEXT (reserved; not currently implemented) */ 1, /* 0xB8 - OPC_THROW */ 1, /* 0xB9 - OPC_SAYVAL */ 1, /* 0xBA - OPC_INDEX */ 3, /* 0xBB - OPC_IDXLCL1INT8 */ 2, /* 0xBC - OPC_IDXLINT8 */ 1, /* 0xBD - unused */ 1, /* 0xBE - unused */ 1, /* 0xBF - unused */ 3, /* 0xC0 - OPC_NEW1 */ 5, /* 0xC1 - OPC_NEW2 */ 3, /* 0xC2 - OPC_TRNEW1 */ 5, /* 0xC3 - OPC_TRNEW2 */ 1, /* 0xC4 - unused */ 1, /* 0xC5 - unused */ 1, /* 0xC6 - unused */ 1, /* 0xC7 - unused */ 1, /* 0xC8 - unused */ 1, /* 0xC9 - unused */ 1, /* 0xCA - unused */ 1, /* 0xCB - unused */ 1, /* 0xCC - unused */ 1, /* 0xCD - unused */ 1, /* 0xCE - unused */ 1, /* 0xCF - unused */ 3, /* 0xD0 - OPC_INCLCL */ 3, /* 0xD1 - OPC_DECLCL */ 3, /* 0xD2 - OPC_ADDILCL1 */ 7, /* 0xD3 - OPC_ADDILCL4 */ 3, /* 0xD4 - OPC_ADDTOLCL */ 3, /* 0xD5 - OPC_SUBFROMLCL */ 2, /* 0xD6 - OPC_ZEROLCL1 */ 3, /* 0xD7 - OPC_ZEROLCL2 */ 2, /* 0xD8 - OPC_NILLCL1 */ 3, /* 0xD9 - OPC_NILLCL2 */ 2, /* 0xDA - OPC_ONELCL1 */ 3, /* 0xDB - OPC_ONELCL2 */ 1, /* 0xDC - unused */ 1, /* 0xDD - unused */ 1, /* 0xDE - unused */ 1, /* 0xDF - unused */ 2, /* 0xE0 - OPC_SETLCL1 */ 3, /* 0xE1 - OPC_SETLCL2 */ 2, /* 0xE2 - OPC_SETARG1 */ 3, /* 0xE3 - OPC_SETARG2 */ 1, /* 0xE4 - OPC_SETIND */ 3, /* 0xE5 - OPC_SETPROP */ 1, /* 0xE6 - OPC_PTRSETPROP */ 3, /* 0xE7 - OPC_SETPROPSELF */ 7, /* 0xE8 - OPC_OBJSETPROP */ 5, /* 0xE9 - OPC_SETDBLCL */ 5, /* 0xEA - OPC_SETDBARG */ 1, /* 0xEB - OPC_SETSELF */ 1, /* 0xEC - OPC_LOADCTX */ 1, /* 0xED - OPC_STORECTX */ 2, /* 0xEE - OPC_SETLCL1R0 */ 3, /* 0xEF - OPC_SETINDLCL1I8 */ 1, /* 0xF0 - unused */ 1, /* 0xF1 - OPC_BP */ 1, /* 0xF2 - OPC_NOP */ 1, /* 0xF3 - unused */ 1, /* 0xF4 - unused */ 1, /* 0xF5 - unused */ 1, /* 0xF6 - unused */ 1, /* 0xF7 - unused */ 1, /* 0xF8 - unused */ 1, /* 0xF9 - unused */ 1, /* 0xFA - unused */ 1, /* 0xFB - unused */ 1, /* 0xFC - unused */ 1, /* 0xFD - unused */ 1, /* 0xFE - unused */ 255 /* 0xFF - unused */ }; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmop.h����������������������������������������������������������������������������0000664�0000000�0000000�00000034072�12650170723�0015016�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/vmop.h,v 1.4 1999/07/11 00:46:59 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmop.h - T3 VM Opcodes Function Notes Modified 11/14/98 MJRoberts - Creation */ #ifndef VMOP_H #define VMOP_H /* * T3 VM opcode definitions */ class CVmOpcodes { public: /* * Opcode size table. Index by opcode; each entry gives the size in * bytes of the instruction. A value of 0 is special - it means that * the instruction is varying-length. */ static const uchar op_siz[]; /* * Get the size in bytes of an opcode. This computes the actual size * of varying-length instructions. */ static size_t get_op_size(const uchar *op); }; #define OPC_PUSH_0 0x01 /* push constant integer 0 */ #define OPC_PUSH_1 0x02 /* push constant integer 1 */ #define OPC_PUSHINT8 0x03 /* push SBYTE operand as integer */ #define OPC_PUSHINT 0x04 /* push INT4 operand as integer */ #define OPC_PUSHSTR 0x05 /* push UINT4 operand as string constant */ #define OPC_PUSHLST 0x06 /* push UINT4 operand as list constant */ #define OPC_PUSHOBJ 0x07 /* push UINT4 operand as object ID */ #define OPC_PUSHNIL 0x08 /* push nil */ #define OPC_PUSHTRUE 0x09 /* push true */ #define OPC_PUSHPROPID 0x0A /* push UINT2 operand as property ID */ #define OPC_PUSHFNPTR 0x0B /* push UINT4 code offset */ #define OPC_PUSHSTRI 0x0C /* push inline string constant */ #define OPC_PUSHPARLST 0x0D /* push varargs parameter list */ #define OPC_MAKELSTPAR 0x0E /* push varargs parameter from list */ #define OPC_PUSHENUM 0x0F /* push an enum value */ #define OPC_PUSHBIFPTR 0x10 /* push a pointer to a built-in function */ #define OPC_NEG 0x20 /* negate */ #define OPC_BNOT 0x21 /* bitwise NOT */ #define OPC_ADD 0x22 /* add */ #define OPC_SUB 0x23 /* subtract */ #define OPC_MUL 0x24 /* multiply */ #define OPC_BAND 0x25 /* bitwise AND */ #define OPC_BOR 0x26 /* bitwise OR */ #define OPC_SHL 0x27 /* shift left */ #define OPC_ASHR 0x28 /* arithmetic shift right */ #define OPC_XOR 0x29 /* bitwise/logical XOR */ #define OPC_DIV 0x2A /* divide */ #define OPC_MOD 0x2B /* MOD (remainder) */ #define OPC_NOT 0x2C /* logical NOT */ #define OPC_BOOLIZE 0x2D /* convert top of stack to true/nil */ #define OPC_INC 0x2E /* increment value at top of stack */ #define OPC_DEC 0x2F /* decrement value at top of stack */ #define OPC_LSHR 0x30 /* logical shift right */ #define OPC_EQ 0x40 /* equals */ #define OPC_NE 0x41 /* not equals */ #define OPC_LT 0x42 /* less than */ #define OPC_LE 0x43 /* less than or equal to */ #define OPC_GT 0x44 /* greater than */ #define OPC_GE 0x45 /* greater than or equal to */ #define OPC_RETVAL 0x50 /* return with value at top of stack */ #define OPC_RETNIL 0x51 /* return nil */ #define OPC_RETTRUE 0x52 /* return true */ #define OPC_RET 0x54 /* return with no value */ #define OPC_NAMEDARGPTR 0x56 /* pointer to named argument table */ #define OPC_NAMEDARGTAB 0x57 /* named argument table */ #define OPC_CALL 0x58 /* function call */ #define OPC_PTRCALL 0x59 /* function call through pointer */ #define OPC_GETPROP 0x60 /* get property */ #define OPC_CALLPROP 0x61 /* call property with arguments */ #define OPC_PTRCALLPROP 0x62 /* call property through pointer with args */ #define OPC_GETPROPSELF 0x63 /* get property of 'self' */ #define OPC_CALLPROPSELF 0x64 /* call method of 'self' */ #define OPC_PTRCALLPROPSELF 0x65 /* call method of 'self' through pointer */ #define OPC_OBJGETPROP 0x66 /* get property of specific object */ #define OPC_OBJCALLPROP 0x67 /* call method of specific object */ #define OPC_GETPROPDATA 0x68 /* get property, disallowing side effects */ #define OPC_PTRGETPROPDATA 0x69 /* get prop through pointer, data only */ #define OPC_GETPROPLCL1 0x6A /* get property of local variable */ #define OPC_CALLPROPLCL1 0x6B /* call property of local variable */ #define OPC_GETPROPR0 0x6C /* get property of R0 */ #define OPC_CALLPROPR0 0x6D /* call property of R0 */ #define OPC_INHERIT 0x72 /* inherit from superclass */ #define OPC_PTRINHERIT 0x73 /* inherit through property pointer */ #define OPC_EXPINHERIT 0x74 /* inherit from an explicit superclass */ #define OPC_PTREXPINHERIT 0x75 /* inherit from explicit sc through prop ptr */ #define OPC_VARARGC 0x76 /* modifier: next call is var arg count */ #define OPC_DELEGATE 0x77 /* delegate to object on stack */ #define OPC_PTRDELEGATE 0x78 /* delegate through property pointer */ #define OPC_SWAP2 0x7A /* swap top two elements with next two */ #define OPC_SWAPN 0x7B /* swap elements at operand indices */ #define OPC_GETARGN0 0x7C /* get argument #0 */ #define OPC_GETARGN1 0x7D /* get argument #1 */ #define OPC_GETARGN2 0x7E /* get argument #2 */ #define OPC_GETARGN3 0x7F /* get argument #3 */ #define OPC_GETLCL1 0x80 /* push a local variable */ #define OPC_GETLCL2 0x81 /* push a local (2-byte index) */ #define OPC_GETARG1 0x82 /* push an argument */ #define OPC_GETARG2 0x83 /* push an argument (2-byte index) */ #define OPC_PUSHSELF 0x84 /* push 'self' */ #define OPC_GETDBLCL 0x85 /* push debug frame local */ #define OPC_GETDBARG 0x86 /* push debug frame argument */ #define OPC_GETARGC 0x87 /* get current argument count */ #define OPC_DUP 0x88 /* duplicate top of stack */ #define OPC_DISC 0x89 /* discard top of stack */ #define OPC_DISC1 0x8A /* discard n items from stack */ #define OPC_GETR0 0x8B /* push the R0 register onto the stack */ #define OPC_GETDBARGC 0x8C /* push debug frame argument count */ #define OPC_SWAP 0x8D /* swap top two stack elements */ #define OPC_PUSHCTXELE 0x8E /* push a method context value */ #define PUSHCTXELE_TARGPROP 0x01 /* push target property */ #define PUSHCTXELE_TARGOBJ 0x02 /* push target object */ #define PUSHCTXELE_DEFOBJ 0x03 /* push defining object */ #define PUSHCTXELE_INVOKEE 0x04 /* push the invokee */ #define OPC_DUP2 0x8F /* duplicate the top two stack elements */ #define OPC_SWITCH 0x90 /* jump through case table */ #define OPC_JMP 0x91 /* unconditional branch */ #define OPC_JT 0x92 /* jump if true */ #define OPC_JF 0x93 /* jump if false */ #define OPC_JE 0x94 /* jump if equal */ #define OPC_JNE 0x95 /* jump if not equal */ #define OPC_JGT 0x96 /* jump if greater than */ #define OPC_JGE 0x97 /* jump if greater or equal */ #define OPC_JLT 0x98 /* jump if less than */ #define OPC_JLE 0x99 /* jump if less than or equal */ #define OPC_JST 0x9A /* jump and save if true */ #define OPC_JSF 0x9B /* jump and save if false */ #define OPC_LJSR 0x9C /* local jump to subroutine */ #define OPC_LRET 0x9D /* local return from subroutine */ #define OPC_JNIL 0x9E /* jump if nil */ #define OPC_JNOTNIL 0x9F /* jump if not nil */ #define OPC_JR0T 0xA0 /* jump if R0 is true */ #define OPC_JR0F 0xA1 /* jump if R0 is false */ #define OPC_ITERNEXT 0xA2 /* iterator next */ #define OPC_GETSETLCL1R0 0xA3 /* set local from R0 and leave value on stack */ #define OPC_GETSETLCL1 0xA4 /* set local and leave value on stack */ #define OPC_DUPR0 0xA5 /* push R0 twice */ #define OPC_GETSPN 0xA6 /* get stack element at given index */ #define OPC_GETLCLN0 0xAA /* get local #0 */ #define OPC_GETLCLN1 0xAB /* get local #1 */ #define OPC_GETLCLN2 0xAC /* get local #2 */ #define OPC_GETLCLN3 0xAD /* get local #3 */ #define OPC_GETLCLN4 0xAE /* get local #4 */ #define OPC_GETLCLN5 0xAF /* get local #5 */ #define OPC_SAY 0xB0 /* display a constant string */ #define OPC_BUILTIN_A 0xB1 /* call built-in func from set 0 */ #define OPC_BUILTIN_B 0xB2 /* call built-in from set 1 */ #define OPC_BUILTIN_C 0xB3 /* call built-in from set 2 */ #define OPC_BUILTIN_D 0xB4 /* call built-in from set 3 */ #define OPC_BUILTIN1 0xB5 /* call built-in from any set, 8-bit index */ #define OPC_BUILTIN2 0xB6 /* call built-in from any set, 16-bit index */ #define OPC_CALLEXT 0xB7 /* call external function */ #define OPC_THROW 0xB8 /* throw an exception */ #define OPC_SAYVAL 0xB9 /* display the value at top of stack */ #define OPC_INDEX 0xBA /* index a list */ #define OPC_IDXLCL1INT8 0xBB /* index a local variable by an int8 value */ #define OPC_IDXINT8 0xBC /* index by an int8 value */ #define OPC_NEW1 0xC0 /* create new object instance */ #define OPC_NEW2 0xC1 /* create new object (2-byte operands) */ #define OPC_TRNEW1 0xC2 /* create new transient instance */ #define OPC_TRNEW2 0xC3 /* create transient object (2-byte operands) */ #define OPC_INCLCL 0xD0 /* increment local variable by 1 */ #define OPC_DECLCL 0xD1 /* decrement local variable by 1 */ #define OPC_ADDILCL1 0xD2 /* add immediate 1-byte int to local */ #define OPC_ADDILCL4 0xD3 /* add immediate 4-byte int to local */ #define OPC_ADDTOLCL 0xD4 /* add value to local variable */ #define OPC_SUBFROMLCL 0xD5 /* subtract value from local variable */ #define OPC_ZEROLCL1 0xD6 /* set local to zero */ #define OPC_ZEROLCL2 0xD7 /* set local to zero */ #define OPC_NILLCL1 0xD8 /* set local to nil */ #define OPC_NILLCL2 0xD9 /* set local to nil */ #define OPC_ONELCL1 0xDA /* set local to numeric value 1 */ #define OPC_ONELCL2 0xDB /* set local to numeric value 1 */ #define OPC_SETLCL1 0xE0 /* set local (1-byte local number) */ #define OPC_SETLCL2 0xE1 /* set local (2-byte local number) */ #define OPC_SETARG1 0xE2 /* set parameter (1-byte param number) */ #define OPC_SETARG2 0xE3 /* set parameter (2-byte param number) */ #define OPC_SETIND 0xE4 /* set value at index */ #define OPC_SETPROP 0xE5 /* set property in object */ #define OPC_PTRSETPROP 0xE6 /* set property through prop pointer */ #define OPC_SETPROPSELF 0xE7 /* set property in self */ #define OPC_OBJSETPROP 0xE8 /* set property in immediate object */ #define OPC_SETDBLCL 0xE9 /* set debugger local variable */ #define OPC_SETDBARG 0xEA /* set debugger parameter variable */ #define OPC_SETSELF 0xEB /* set 'self' */ #define OPC_LOADCTX 0xEC /* load method context from stack */ #define OPC_STORECTX 0xED /* store method context and push on stack */ #define OPC_SETLCL1R0 0xEE /* set local (1-byte local number) from R0 */ #define OPC_SETINDLCL1I8 0xEF /* set indexed local */ #define OPC_BP 0xF1 /* debugger breakpoint */ #define OPC_NOP 0xF2 /* no operation */ #endif /* VMOP_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmpack.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000316330�12650170723�0015651�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmpack.cpp - binary data stream pack/unpack Function Notes Modified 10/01/10 MJRoberts - Creation */ #include <float.h> #include <math.h> #include <limits.h> #include "t3std.h" #include "vmtype.h" #include "vmpack.h" #include "vmdatasrc.h" #include "vmstack.h" #include "vmrun.h" #include "vmstr.h" #include "vmlst.h" #include "vmbignum.h" #include "vmbytarr.h" #include "utf8.h" #include "charmap.h" /* ------------------------------------------------------------------------ */ /* * Type code classifiers */ /* * Is the type code valid? */ static inline int is_valid_type(wchar_t c) { return c <= 127 && strchr("aAbuUwWhHcCsSlLqQkfdxX@\"{", (char)c) != 0; } /* * Is the type repeatable? A repeatable type is a fixed-length type like L * or C. String types are not repeatable; they instead treat a count as a * length for the string. */ static inline int is_repeatable(wchar_t c) { /* it's repeatable if it's not one of the string types */ return strchr("aAbuUwWhHxX@", (char)c) == 0; } /* * Is this a varying-length item? */ static inline int is_varying_length(wchar_t c) { return strchr("aAbuUwWhH", (char)c) != 0; } /* * Get the character padding for a type. A, U, and W use space padding; * everything else uses Nul padding. */ static inline char get_padding_char(wchar_t c) { return strchr("AUW", (char)c) != 0 ? ' ' : 0; } /* * Is this a type that consumes an argument? */ static inline int type_consumes_arg(wchar_t c) { return strchr("xX@\"{", (char)c) == 0; } /* ------------------------------------------------------------------------ */ /* * string parser position */ struct CVmPackPos { CVmPackPos(const char *p, size_t len) : p((char *)p), len(len), idx(1) { } CVmPackPos(const CVmPackPos *pos) : p(pos->p.getptr()), len(pos->len), idx(pos->idx) { } CVmPackPos() : p(), len(0), idx(0) { } /* set to another position */ void set(const CVmPackPos *pos) { p.set(&pos->p); len = pos->len; idx = pos->idx; } /* is another character available? */ int more() { /* get the current character (skipping spaces) */ getch(); /* indicate whether or not we have more in the buffer */ return len != 0; } /* get the current charater, without skipping spaces */ wchar_t getch_raw() { /* if there's a character left, return it */ return len != 0 ? p.getch() : 0; } /* get the current character, skipping spaces */ wchar_t getch() { /* skip spaces */ while (len != 0 && is_space(p.getch())) { ++idx; p.inc(&len); } /* return the current character */ return getch_raw(); } /* skip the current character */ void inc() { if (len != 0) { p.inc(&len); ++idx; } } /* get and skip the current character, skipping whitespace */ wchar_t nextch() { /* skip spaces */ while (len != 0 && is_space(p.getch())) { p.inc(&len); ++idx; } /* if there's anything left, get and skip a character */ if (len != 0) { /* we have a character available - get the current character */ wchar_t ch = p.getch(); /* skip it */ p.inc(&len); ++idx; /* return the last character */ return ch; } else { /* no more characters available - return nul */ return 0; } } /* parse an integer value */ int parse_int() { /* remember the starting location, for error reporting */ int start_idx = idx; /* parse digits until we find something else */ int acc; wchar_t c; for (acc = 0 ; is_digit(c = getch()) ; inc()) { /* add the digit into the accumulator */ acc *= 10; acc += value_of_digit(c); /* if this overflows an int, it's an error */ if (acc < 0) err_throw_a(VMERR_PACK_PARSE, 1, ERR_TYPE_INT, start_idx); } /* return the result */ return acc; } /* get the current character index (1-based, for reporting to the user) */ int index() const { return idx; } /* current pointer and remaining length */ utf8_ptr p; size_t len; /* current character index from the beginning of the string */ int idx; }; /* ------------------------------------------------------------------------ */ /* * Special repeat counts */ const int ITER_NONE = -1; /* no repeat count specified */ const int ITER_STAR = -2; /* star '*' as the repeat count */ /* ------------------------------------------------------------------------ */ /* * type code descriptor - this captures the type information for a single * type code entry, including prefix and postfix flags */ struct CVmPackType { CVmPackType() { type_code = 0; big_endian = FALSE; count = ITER_NONE; count_as_type = 0; count_in_bytes = FALSE; up_to_count = FALSE; bang = FALSE; tilde = FALSE; pct = FALSE; fmtidx = 0; null_term = FALSE; qu = FALSE; } CVmPackType(const CVmPackGroup *g); /* type code character */ wchar_t type_code; /* * for type codes '"' and '{', the literal text segment, as a pointer * into the original format string buffer */ CVmPackPos lit; /* is it big-endian? */ int big_endian; /* * repeat count - this is the number of repetitions specified in the * repeat count suffix, or a special ITER_xxx value */ int count; /* is the repeat count in bytes (:b suffix)? */ int count_in_bytes; /* * Repeat count as a type code. If the :X modifier if used, the X is * another type code, and the repeat count is taken as the size of that * type. This stores the type code in this case. We also set 'count' * to the size of the type, so this is just preserving the extra * information about the source of the size. */ wchar_t count_as_type; /* * Should we treat the count as an upper bound? This is set if we * combine '*' with a number, as in "H30*" - this means pack/unpack up * to 30 items, but stop if we run out of source material first. */ int up_to_count; /* is there a '!' qualifier? */ int bang; /* is there a '~' qualifier? */ int tilde; /* is there a '%' qualifier? */ int pct; /* is there a '?' qualifier? */ int qu; /* is it null-terminated? */ int null_term; /* format string index */ int fmtidx; /* get the count - returns 1 as the implied count if none was specified */ int get_count() const { return has_count() ? count : 1; } /* is there an explicit count specified? */ int has_count() const { return count >= 0; } }; /* ------------------------------------------------------------------------ */ /* * Pack string group. This represents a ( ) group within the string. The * overall string is the root group. Each parenthesized group has its own * set of modifiers, a repeat count, and establishes a starting byte offset * for the "@" operator. */ struct CVmPackGroup { /* set up the defaults for the root group */ CVmPackGroup(CVmPackPos *pos, CVmDataSource *ds) : start(pos) { this->ds = ds; parent = 0; big_endian = FALSE; tilde = FALSE; pct = FALSE; stream_ofs = ds->get_pos(); cur_iter = 1; num_iters = 1; up_to_iters = FALSE; close_paren = 0; } /* set up a new nested group */ CVmPackGroup(CVmPackGroup *parent, wchar_t open_paren, CVmPackPos *pos, const CVmPackType *t) : parent(parent), start(pos) { ds = parent->ds; stream_ofs = ds->get_pos(); cur_iter = 1; big_endian = t->big_endian; tilde = t->tilde; pct = t->pct; num_iters = t->count; up_to_iters = t->up_to_count; close_paren = (open_paren == '(' ? ')' : open_paren == '[' ? ']' : open_paren == '{' ? '}' : 0); } /* parent group */ CVmPackGroup *parent; /* the paren type that closes the group */ wchar_t close_paren; /* * Default endian-ness within this group. The root group is * little-endian by default, and each nested group inherits the * endian-ness of its containing group. */ int big_endian; /* * Default 'tilde' (~) modifier mode within this group. The root group * has this turned off by default, and each nested group inherits its * containing group's status. */ int tilde; /* * Default 'percent' (%) modifier mode within this group. This is * inherited like the endian-ness and tilde modifiers. */ int pct; /* * Group start position in template string. This is the position of * the first character after the open paren of the group. */ CVmPackPos start; /* data stream */ CVmDataSource *ds; /* * Group starting offset in the data stream. Each iteration of a group * resets the zero point for '@' codes within the group. */ long stream_ofs; /* get the stream offset of the outermost parent */ long root_stream_ofs() const { return (parent != 0 ? parent->root_stream_ofs() : stream_ofs); } /* current iteration for this group */ int cur_iter; /* total number of iterations for this group */ int num_iters; /* is this an "up to" iteration count (e.g., 30*)? */ int up_to_iters; }; /* ------------------------------------------------------------------------ */ /* * CVmPackType implementation */ CVmPackType::CVmPackType(const CVmPackGroup *g) { /* clear the type code and count */ type_code = 0; count = ITER_NONE; count_as_type = 0; count_in_bytes = FALSE; bang = FALSE; qu = FALSE; null_term = FALSE; /* inherit group attributes */ big_endian = g->big_endian; tilde = g->tilde; pct = g->pct; } /* ------------------------------------------------------------------------ */ /* * Packing argument list. There are two kinds of argument list: function * arguments on the stack, or a list-like object. */ struct CVmPackArgs { /* are there more arguments? */ virtual int more() = 0; /* number of remaining arguments */ virtual int nrem() = 0; /* get the current argument */ virtual vm_val_t *get(vm_val_t *val) = 0; /* advance to the next argument */ virtual void next() = 0; }; /* * StackArgs represents arguments directly from a function/method call * frame on the stack */ struct StackArgs: CVmPackArgs { StackArgs(VMG_ int first_arg, int argc) { this->stk = G_stk; this->curarg = first_arg; this->lastarg = first_arg + argc - 1; } virtual int more() { return curarg <= lastarg; } virtual int nrem() { return curarg <= lastarg ? lastarg - curarg + 1 : 0; } virtual vm_val_t *get(vm_val_t *val) { if (curarg <= lastarg) *val = *stk->get(curarg); else val->set_nil(); return val; } virtual void next() { ++curarg; } CVmStack *stk; int curarg; int lastarg; }; /* * ListArgs represents arguments contained in a list. For a repeated item, * the corresponding argument can be a list, in which case the repetitions * are taken from the list: * * packBytes('a10 c5', 'hello', [1, 2, 3, 4, 5]); */ struct ListArgs: CVmPackArgs { ListArgs(VMG_ vm_val_t *lst) { set(vmg_ lst); } void set(VMG_ vm_val_t *lst) { this->vmg = VMGLOB_ADDR; this->lst = *lst; this->idx = 1; this->len = lst->ll_length(vmg0_); } virtual int more() { return idx <= (len >= 0 ? len : 1); } virtual int nrem() { int last = (len >= 0 ? len : 1); return idx <= last ? last - idx + 1 : 0; } virtual vm_val_t *get(vm_val_t *val) { if (len < 0 && idx == 1) { /* * we don't have a list, so pretend the value was wrapped in a * single-element list by returning it at index 1 */ *val = lst; } else if (idx <= len) { /* we have a valid list and a valid index - get the element */ VMGLOB_PTR(vmg); lst.ll_index(vmg_ val, idx); } else { /* we're past the end of the list - return nil */ val->set_nil(); } return val; } virtual void next() { ++idx; } /* get the effective count - the number of items we'll return */ int effective_count() { return len < 0 ? 1 : len; } vm_globals *vmg; vm_val_t lst; int idx; int len; }; /* * A single argument. This encapsulates an argument value that we've * already resolved and represents it as an argument list with one element. */ struct SingleArg: CVmPackArgs { SingleArg(const vm_val_t *val) { this->val = *val; idx = 0; } SingleArg(long l) { val.set_int(l); idx = 0; } SingleArg() { val.set_nil(); idx = 0; } virtual int more() { return idx == 0; } virtual int nrem() { return idx == 0 ? 1 : 0; } virtual vm_val_t *get(vm_val_t *val) { if (idx == 0) *val = this->val; else val->set_nil(); return val; } virtual void next() { ++idx; } vm_val_t val; int idx; }; /* ------------------------------------------------------------------------ */ /* * Pack data from stack arguments into the given data stream */ void CVmPack::pack(VMG_ int arg_index, int argc, CVmDataSource *dst) { /* the first argument is the format string - retrieve it */ const char *fmt = G_stk->get(arg_index)->get_as_string(vmg0_); if (fmt == 0) err_throw(VMERR_STRING_VAL_REQD); /* get the format string length and buffer pointer */ size_t fmtlen = vmb_get_len(fmt); fmt += VMB_LEN; /* pack the root group */ CVmPackPos p(fmt, fmtlen); CVmPackGroup root(&p, dst); StackArgs args(vmg_ arg_index + 1, argc - 1); pack_group(vmg_ &p, &args, &root, FALSE); /* make sure we exhausted the string */ if (p.more()) err_throw_a(VMERR_PACK_PARSE, 1, ERR_TYPE_INT, p.index()); /* * if we didn't consume all of the arguments, it means that the format * string didn't have enough entries for the supplied arguments - flag * an error */ if (args.more()) err_throw_a(VMERR_PACK_ARGC_MISMATCH, 1, ERR_TYPE_INT, (int)fmtlen+1); } /* * Pack a group */ void CVmPack::pack_group(VMG_ CVmPackPos *p, CVmPackArgs *args_main, CVmPackGroup *group, int list_per_iter) { /* assume we're unpacking from the main argument list */ CVmPackArgs *args = args_main; /* * if there's a list per group iteration, the next argument should be a * list from which we'll take arguments for this single iteration */ vm_val_t listval; ListArgs listargs(vmg_ args->get(&listval)); int more = args->more(); if (list_per_iter) { /* pull out and switch to the sublist for the first iteration */ more = args_main->more(); args = &listargs; args_main->next(); } /* * if this is an up-to count, and the source list is empty, packing * zero iterations is valid */ if ((group->num_iters == ITER_STAR || group->up_to_iters) && !more) { /* skip the group in the source */ p->set(&group->start); skip_group(p, group->close_paren); /* we're done */ return; } /* run through the format list */ while (p->more()) { /* check for group entry/exit */ wchar_t c = p->getch(); if (c == '(' || c == '[') { /* start of a group - pack the sub-group */ pack_subgroup(vmg_ p, args, group, 0); } else if (c == group->close_paren) { /* * if we're reading arguments from a separate sublist per * iteration of the group, move on to the next sublist */ int more = args->more(); if (list_per_iter) { /* switch to the next sublist from the main arguments */ more = args_main->more(); listargs.set(vmg_ args_main->get(&listval)); args_main->next(); } /* * Determine if we're done. If the group has a '*' repeat * count, we're done when we're out of arguments. If it has an * up-to count, we're done if we're either out of arguments or * at the iteration count limit. Otherwise, we're done when * we've reached the iteration count limit. */ if ((group->num_iters == ITER_STAR ? !more : group->cur_iter >= group->num_iters) || (group->up_to_iters && !more)) { /* * if this is an up-to count, make sure we consumed all * arguments */ if (group->up_to_iters) { if (list_per_iter) for ( ; args_main->more() ; args_main->next()) ; else for ( ; args->more() ; args->next()) ; } /* done */ return; } /* * We're going back for another iteration of the group. * Increment the iteration count, and go back to the start of * the group in the template string. */ group->cur_iter++; group->stream_ofs = group->ds->get_pos(); p->set(&group->start); } else { /* * It's not a group, so it must be a single item. Parse the * item definition. */ CVmPackType t; parse_type(p, &t, group); /* check to see if this is a length prefix to the next item */ if (p->getch() == '/') { /* skip the '/' */ p->inc(); /* check for a group */ if ((c = p->getch()) == '(' || c == '[') { /* go pack the subgroup with the prefix count */ pack_subgroup(vmg_ p, args, group, &t); } else { /* * It's not a group, so it must be a single item. * Parse the item definition. */ CVmPackType t2; parse_type(p, &t2, group); /* pack the item with the count prefix */ pack_item(vmg_ group, args, &t2, &t); } } else { /* there's no count prefix - go pack the single item */ pack_item(vmg_ group, args, &t, 0); } } } } /* * Pack a subgroup */ void CVmPack::pack_subgroup(VMG_ CVmPackPos *p, CVmPackArgs *args, CVmPackGroup *group, CVmPackType *prefix_count) { /* get the output stream */ CVmDataSource *dst = group->ds; /* parse the group modifiers */ CVmPackType gt(group); parse_group_mods(p, >); /* note the open paren */ wchar_t paren = p->getch(); /* set up a child group definition reflecting the modifiers */ p->inc(); CVmPackGroup child(group, paren, p, >); /* set up a list argument reader, in case we have a list for the group */ vm_val_t arg; ListArgs la(vmg_ args->get(&arg)); /* * Set up the child argument list. If the open paren for the list in * the template is '[', the arguments come from a list; otherwise they * come from the main argument list. */ CVmPackArgs *child_args = args; int list_per_iter = FALSE; if (paren == '[') { /* [] group - pack from a list or a set of lists */ if (gt.bang) { /* ! modifier - each iteration is a separate sublist */ list_per_iter = TRUE; } else { /* "[ ]" group - the whole group comes from a single sublist */ child_args = &la; args->next(); } } /* check for a prefix count */ long prefix_pos = 0; if (prefix_count != 0) { /* remember the location of the prefix */ prefix_pos = dst->get_pos(); /* pack a zero count as a placeholder */ SingleArg count((long)0); pack_one_item(vmg_ group, &count, prefix_count, 0); /* the prefix count overrides any suffix count with '*' */ child.num_iters = ITER_STAR; } /* recursively pack the child group */ pack_group(vmg_ p, child_args, &child, list_per_iter); /* make sure we stopped at the close paren */ if (p->nextch() != child.close_paren) err_throw_a(VMERR_PACK_PARSE, 1, ERR_TYPE_INT, p->index()); /* * if there was a prefix count, go back and patch it with the actual * iteration count */ if (prefix_count != 0) { /* save the end-of-group position, and seek back to the count */ long endpos = dst->get_pos(); dst->seek(prefix_pos, OSFSK_SET); /* it's an error if the prefix count is a variable-length item */ if (prefix_count->type_code == 'k' || (is_varying_length(prefix_count->type_code) && prefix_count->count == ITER_STAR)) err_throw_a(VMERR_PACK_ARG_MISMATCH, 1, ERR_TYPE_INT, prefix_count->fmtidx); /* write the count */ SingleArg count(child.cur_iter); pack_one_item(vmg_ group, &count, prefix_count, 0); /* seek back to the end of the group */ dst->seek(endpos, OSFSK_SET); } /* skip any modifiers after the group */ parse_mods(p, >); } /* ------------------------------------------------------------------------ */ /* * Write padding bytes. We write alternating ch1 and ch2 bytes to fill out * the byte count. */ void CVmPack::write_padding(CVmDataSource *dst, char ch1, char ch2, long cnt) { /* set up a buffer full of the padding bytes */ char buf[256]; for (size_t i = 0 ; i < sizeof(buf) ; buf[i++] = ch1, buf[i++] = ch2) ; /* keep going until we satisfy the request */ while (cnt > 0) { /* write out one buffer-full, or the remaining amount if less */ size_t cur = (cnt > (long)sizeof(buf) ? sizeof(buf) : (size_t)cnt); if (dst->write(buf, cur)) err_throw(VMERR_WRITE_FILE); /* deduct this from the remaining request */ cnt -= cur; } } void CVmPack::write_padding(CVmDataSource *dst, char ch, long cnt) { write_padding(dst, ch, ch, cnt); } /* ------------------------------------------------------------------------ */ /* * get an integer value, enforcing the given limits */ static long intval(VMG_ const vm_val_t *val, long minval, long maxval, int pct, unsigned long mask) { /* get the value */ long l; switch (val->typ) { case VM_NIL: /* use 0 for nil */ l = 0; break; case VM_INT: /* regular integer */ l = val->val.intval; break; case VM_OBJ: /* * convert BigNumber to integer explicitly; use the type cast for * other types */ if (CVmObjBigNum::is_bignum_obj(vmg_ val->val.obj)) { /* explicitly cast the BigNumber so that we can catch overflows */ int ov; if (minval < 0) { l = ((CVmObjBigNum *)vm_objp(vmg_ val->val.obj)) ->convert_to_int(ov); } else { l = (long)((CVmObjBigNum *)vm_objp(vmg_ val->val.obj)) ->convert_to_uint(ov); } /* check for overflow */ if (ov && !pct) err_throw(VMERR_NUM_OVERFLOW);; /* done */ break; } /* FALL THROUGH to default */ default: /* use the generic type cast */ l = val->cast_to_int(vmg0_); break; } /* if the '%' flag is set, truncate the value, otherwise check the range */ if (pct) { /* % modifier - truncate the value to fit the type */ l &= mask; } else { /* no % modifier - enforce the range */ if (l < minval || l > maxval) err_throw(VMERR_NUM_OVERFLOW); } /* return the value */ return l; } /* * integer decomposition - get the low-order byte and shift */ static inline char lsb(long &l, int is_signed) { /* note the sign */ int neg = l < 0; /* the return value is the low-order byte */ char ret = (char)(l & 0xff); /* shift the value to remove the low-order byte */ l >>= 8; /* mask off the high byte */ l &= 0x00ffffff; /* if it's a signed negative value, put 1 bits in the high byte */ if (is_signed && neg) l |= 0xff000000; /* return the low byte */ return ret; } /* * reverse the byte order of a buffer */ static void reverse_bytes(char *buf, size_t len) { /* * Run through the buffer from opposite ends, swapping each byte pair. * Stop when our left index crosses our right index. If the buffer * length is even, the two indices will pass each other; if the length * is even, they'll both land on the middle byte of the buffer at the * same time. Simply keep going as long as the left index is less than * the right index. */ int i, j; for (i = 0, j = len - 1 ; i < j ; ++i, --j) { /* swap the current high/low pair */ char tmp = buf[i]; buf[i] = buf[j]; buf[j] = tmp; } } /* ------------------------------------------------------------------------ */ /* * String source for packing. We can pack strings and ByteArrays into * character fields; this class is an abstraction for the different types * of sources. */ class CharFieldSource { public: virtual ~CharFieldSource() { } /* create a concrete CharFieldSource for a value */ static CharFieldSource *create(VMG_ const vm_val_t *val, vm_val_t *newval); /* get the array length to use for the given field type */ virtual int arraylen(CVmPackType *t) = 0; /* is another character available? */ virtual int more() = 0; /* get the next character */ virtual wchar_t getch() = 0; }; /* string source */ class CharFieldSourceString: public CharFieldSource { public: CharFieldSourceString(const char *src, size_t len) : p((char *)src), len(len) { } virtual int more() { return len != 0; } virtual wchar_t getch() { if (len != 0) { wchar_t ch = p.getch(); p.inc(&len); return ch; } else return 0; } virtual int arraylen(CVmPackType *t) { switch (t->type_code) { case 'a': case 'A': case 'b': /* the length is in bytes, and we write one byte per character */ return p.len(len); case 'w': case 'W': /* * the default length is in characters; if counting in bytes, * we need two bytes per character */ return (t->count_in_bytes ? 2 : 1) * p.len(len); case 'h': case 'H': /* * the length is in source nibbles, one nibble per character; * in bytes, we need half as many bytes as characters, rounded * up */ return t->count_in_bytes ? (p.len(len)+1)/2 : p.len(len); case 'u': case 'U': /* * the length is in bytes, in UTF-8 format; this is the same * format as our underlying string, so we can simply return its * byte length */ return len; default: return 0; } } protected: /* utf8 pointer and remaining byte length */ utf8_ptr p; size_t len; }; /* ByteArray source */ class CharFieldSourceByteArray: public CharFieldSource { public: CharFieldSourceByteArray(VMG_ vm_obj_id_t id) { arr = (CVmObjByteArray *)vm_objp(vmg_ id); idx = 0; len = arr->get_element_count(); } virtual int more() { return idx < len; } virtual wchar_t getch() { return idx < len ? arr->get_element(++idx) : 0; } virtual int arraylen(CVmPackType *t) { switch (t->type_code) { case 'a': case 'A': case 'b': /* * the length is in bytes, and we write one byte per character; * we treat each byte in the array as a Latin-1 character, so * this is the same as our array length */ return (int)len; case 'w': case 'W': /* * The length is in characters, or two bytes per character. We * treat our bytes as latin-1 characters, so the character * count is the saqme as the array length. */ return (int)(t->count_in_bytes ? len*2 : len); case 'h': case 'H': /* * one character per array element, or one byte per pair of * array elements */ return (int)(t->count_in_bytes ? (len+1)/2 : len); case 'u': case 'U': /* * The length is in bytes, in UTF-8 format. We treat our array * elements as Latin-1 characters. In UTF-8, characters in the * range 0-127 take one byte, and 128-255 take two bytes. Run * through our array and add up the required lengths. */ { int ret; unsigned long i; for (ret = 0, i = 1 ; i <= len ; ++i) ret += arr->get_element(i) < 128 ? 1 : 2; /* return the length */ return ret; } default: return 0; } } protected: /* ByteArray object */ CVmObjByteArray *arr; /* current byte index */ unsigned long idx; /* length of the array */ unsigned long len; }; /* create an appropriate CharFieldSource subclass for a given value */ CharFieldSource *CharFieldSource::create( VMG_ const vm_val_t *val, vm_val_t *newval) { /* presume we won't create a new value */ newval->set_nil(); /* check to see if it's a ByteArray */ if (val->typ == VM_OBJ && CVmObjByteArray::is_byte_array(vmg_ val->val.obj)) return new CharFieldSourceByteArray(vmg_ val->val.obj); /* it's not another acceptable type, so it has to be a string */ const char *str = val->cast_to_string(vmg_ newval); return new CharFieldSourceString(str + VMB_LEN, vmb_get_len(str)); } /* ------------------------------------------------------------------------ */ /* * String character-to-byte converters. Each one of these has a singleton * instance that we use in the item packer. These objects have no state * (they're really just a convenient way to write function vectors, via * their virtual methods), so the static singletons are inherently * thread-safe. */ class CharConv { public: /* convert one character from source to packed format */ virtual void putch(char *&buf, wchar_t ch, int idx, int pct) = 0; /* * Close out a packed string. This is required for formats that write * multiple source characters per byte, such as 'hH', to make sure we * write out any partially built last byte. Formats that write one or * more complete bytes per source character need do nothing here. */ virtual void putch_end(char *&buf, int idx) { } /* write a null terminator */ virtual void null_term(CVmDataSource *dst) { if (dst->write("\0", 1)) err_throw(VMERR_WRITE_FILE); } /* * Get the field width in bytes for the given descriptor. If the * descriptor's repeat count is explicitly stated in bytes, this simply * returns the repeat count. Otherwise, we translate the repeat count * to a byte count according to the underlying character encoding. */ int get_byte_count(const CVmPackType *t) { return t->count_in_bytes ? t->get_count() : count_to_bytes(t); } /* translate a repeat count to a byte length; assume 1-to-1 by default */ virtual int count_to_bytes(const CVmPackType *t) { return t->get_count(); } /* translate a byte length to a repeat count; assume 1-to-1 by default */ virtual long bytes_to_count(long bytes) { return bytes; } /* write space or nul padding - by default, write single-byte padding */ virtual void pad(CVmDataSource *dst, char ch, long str_bytes, const CVmPackType *t) { CVmPack::write_padding(dst, ch, get_byte_count(t) - str_bytes); } /* unpack file data */ virtual void unpack(VMG_ vm_val_t *val, CVmDataSource *src, int cnt, char pad) { /* if the requested length is zero, return an empty string */ if (cnt == 0) { val->set_obj(CVmObjString::create(vmg_ FALSE, 0)); return; } /* * By default, we return a string. Set up a wide character buffer * to hold the input. */ wchar_t *wbuf = new wchar_t[cnt]; if (wbuf == 0) err_throw(VMERR_OUT_OF_MEMORY); err_try { /* read the data */ read(wbuf, src, cnt); /* remove padding characters from the end of the string */ for ( ; cnt > 0 && wbuf[cnt-1] == pad ; --cnt) ; /* create the string */ val->set_obj(CVmObjString::create(vmg_ FALSE, wbuf, cnt)); } err_finally { delete [] wbuf; } err_end; } /* unpack file data for a varying-length field with null termination */ void unpackz(VMG_ vm_val_t *val, CVmDataSource *src) { /* set up a string with an initial guess at the size */ val->set_obj(CVmObjString::create(vmg_ FALSE, 256)); CVmObjString *str = (CVmObjString *)vm_objp(vmg_ val->val.obj); G_stk->push(val); /* read characters until we reach the null terminator */ char *dst = str->cons_get_buf(); for (;;) { /* read a character */ wchar_t ch = readch(src); /* if it's the null character, we're done */ if (ch == 0) break; /* append it to the string */ dst = str->cons_append(vmg_ dst, ch, 256); } /* set the final size of the string */ str->cons_shrink_buffer(vmg_ dst); } /* read a single character */ virtual wchar_t readch(CVmDataSource *src) { /* read a byte into our buffer */ char buf[1]; if (src->read(buf, 1)) err_throw(VMERR_READ_FILE); /* return the character */ return buf[0]; } /* get the character converter for a given type code */ static CharConv *conv_for_type(const CVmPackType *t); protected: /* read from the data source, converting to wide characters */ virtual void read(wchar_t *buf, CVmDataSource *src, int cnt) = 0; }; /* * Latin-1 converter, for 'a' and 'A' formats */ class CharConvLatin1: public CharConv { public: virtual void putch(char *&buf, wchar_t ch, int idx, int pct) { /* latin-1 - store the character if it's in range, '?' otherwise */ *buf++ = (ch <= 255 ? (char)ch : pct ? (char)ch & 0xff : '?'); } virtual void read(wchar_t *buf, CVmDataSource *src, int cnt) { /* read the latin-1 character bytes into the buffer */ if (src->read(buf, cnt)) err_throw(VMERR_READ_FILE); /* * The file characters are bytes, but we want wide characters. The * bytewise read packed the Latin-1 byte representations into the * lower half of the buffer. Run through the buffer from the end, * expanding each byte into a wide character. This is trivial for * Latin-1 since a Latin-1 code point is identical to the * corresponding Unicode code point. */ for (int i = cnt - 1 ; i >= 0 ; --i) buf[i] = (wchar_t)(((unsigned char *)buf)[i]); } }; static CharConvLatin1 s_CharConvLatin1; /* * Byte converter, for 'b' format */ class CharConvByte: public CharConv { virtual void putch(char *&buf, wchar_t ch, int idx, int pct) { /* raw bytes - make sure the byte is in range */ if (ch > 255) { /* % modifier -> truncate to byte, otherwise it's an error */ if (pct) ch &= 0xff; else err_throw(VMERR_NUM_OVERFLOW); } /* store it */ *buf++ = (char)ch; } /* unpack file data */ virtual void unpack(VMG_ vm_val_t *val, CVmDataSource *src, int cnt, char pad) { /* * For the byte type, we unpack into a ByteArray object rather than * a string, and we leave any padding intact. Create a byte array * with the required size (and push it for gc protection). */ val->set_obj(CVmObjByteArray::create(vmg_ FALSE, cnt)); CVmObjByteArray *arr = (CVmObjByteArray *)vm_objp(vmg_ val->val.obj); G_stk->push(val); /* read the data (don't save undo, since it's a new object) */ if ((int)arr->read_from_file( vmg_ val->val.obj, src, 1, cnt, FALSE) != cnt) err_throw(VMERR_READ_FILE); /* done with the gc protection */ G_stk->discard(); } /* we don't need to read characters, since we unpack into a byte array */ virtual void read(wchar_t *, CVmDataSource *, int) { } }; static CharConvByte s_CharConvByte; /* * UTF8 converter, for 'u' and 'U' formats */ class CharConvUTF8: public CharConv { public: virtual void putch(char *&buf, wchar_t ch, int idx, int pct) { /* utf8 - store the utf8 sequence for the character */ buf += utf8_ptr::s_putch(buf, ch); } /* read a single character */ virtual wchar_t readch(CVmDataSource *src) { /* read the first byte */ char buf[10]; if (src->read(buf, 1)) err_throw(VMERR_READ_FILE); /* figure how many more bytes we have to read */ size_t len = utf8_ptr::s_charsize(buf[0]); if (len > sizeof(buf)) len = sizeof(buf); /* read the rest of the bytes */ if (len > 1 && src->read(buf+1, len-1)) err_throw(VMERR_READ_FILE); /* return the translated character */ return utf8_ptr::s_getch(buf); } /* unpack file data */ virtual void unpack(VMG_ vm_val_t *val, CVmDataSource *src, int cnt, char pad) { /* * For UTF-8, we can simply read the bytes directly into a string * buffer. Allocate the string at the required byte size, and push * for gc protection. */ val->set_obj(CVmObjString::create(vmg_ FALSE, cnt)); CVmObjString *str = (CVmObjString *)vm_objp(vmg_ val->val.obj); G_stk->push(val); /* if the read count is non-zero, read the data */ if (cnt != 0) { /* read the data directly into the string buffer */ if (src->read(str->cons_get_buf(), cnt)) err_throw(VMERR_READ_FILE); /* validate the UTF-8 */ CCharmapToUni::validate(str->cons_get_buf(), cnt); /* remove trailing padding */ utf8_ptr p(str->cons_get_buf() + cnt); for (size_t rem = 0 ; (int)rem < cnt ; ) { /* * move to the previous character; if it's not the padding * character, we're done */ p.dec(&rem); if (p.getch() != pad) { p.inc(&rem); break; } } /* set the string length to the length without the padding */ str->cons_shrink_buffer(vmg_ p.getptr()); } /* done with the gc protection */ G_stk->discard(); } /* we don't need a low-level wchar_t reader */ virtual void read(wchar_t *, CVmDataSource *, int) { } }; static CharConvUTF8 s_CharConvUTF8; /* * Base class for UCS2 converters, for 'w' and 'W' formats */ class CharConvUCS2: public CharConv { public: virtual void read(wchar_t *buf, CVmDataSource *src, int cnt) { /* * Read the wide characters (two bytes each). The local wchar_t * type could be bigger than two bytes, but can't be less, so we * can be sure we have enough space for the file read. */ if (src->read(buf, cnt*2)) err_throw(VMERR_READ_FILE); /* * Convert each character from little-endian to local byte order. * The local wchar_t type could be bigger than two bytes, so work * from the end of the buffer downwards - this will ensure we won't * overwrite bytes before we translate them. */ for (int i = cnt - 1 ; i > 0 ; --i) buf[i] = file_to_machine((unsigned char *)buf + i*2); } /* UCS-2 nulls are two bytes, like every other wide character */ virtual void null_term(CVmDataSource *dst) { if (dst->write("\0\0", 2)) err_throw(VMERR_WRITE_FILE); } /* read a single character */ virtual wchar_t readch(CVmDataSource *src) { /* read a two-byte character into our buffer */ unsigned char buf[2]; if (src->read(buf, 2)) err_throw(VMERR_READ_FILE); /* return the character */ return file_to_machine(buf); } /* * the wide-char formats treat the repeat count as a character count, * so the byte count is double the repeat count */ virtual int count_to_bytes(const CVmPackType *t) { return t->get_count() * 2; } virtual long bytes_to_count(long bytes) { return (bytes + 1) / 2; } protected: /* convert a wchar_t from file byte order to local machine byte order */ virtual wchar_t file_to_machine(const unsigned char *ch) = 0; }; /* * Little-endian UCS2 converter, for 'w' and 'W' formats */ class CharConvUCS2L: public CharConvUCS2 { public: virtual void putch(char *&buf, wchar_t ch, int idx, int pct) { /* UCS-2 little-endian - store the low byte first */ *buf++ = (char)(ch & 0xff); *buf++ = (char)((ch >> 8) & 0xff); } virtual void pad(CVmDataSource *dst, char ch, long str_bytes, const CVmPackType *t) { CVmPack::write_padding(dst, ch, 0, get_byte_count(t) - str_bytes); } virtual wchar_t file_to_machine(const unsigned char *p) { return (wchar_t)p[0] + (((wchar_t)p[1]) << 8); } }; static CharConvUCS2L s_CharConvUCS2L; /* * Big-endian UCS2 converter, for 'w' and 'W' formats */ class CharConvUCS2B: public CharConvUCS2 { public: virtual void putch(char *&buf, wchar_t ch, int idx, int pct) { /* UCS2 big-endian - store the high byte first */ *buf++ = (char)((ch >> 8) & 0xff); *buf++ = (char)(ch & 0xff); } virtual void pad(CVmDataSource *dst, char ch, long str_bytes, const CVmPackType *t) { CVmPack::write_padding(dst, 0, ch, get_byte_count(t) - str_bytes); } virtual wchar_t file_to_machine(const unsigned char *p) { return (wchar_t)p[1] + (((wchar_t)p[0]) << 8); } }; static CharConvUCS2B s_CharConvUCS2B; /* * Hex string converter, for 'h' and 'H' foramts */ class CharConvHex: public CharConv { public: virtual void read(wchar_t *buf, CVmDataSource *src, int cnt) { /* * Read the packed bytes. We need half a byte per hex nibble, * rounding up if we want an odd number of nibbles. */ if (src->read(buf, (cnt+1)/2)) err_throw(VMERR_READ_FILE); /* * Convert each byte into a pair of hex nibbles. Since the * unpacked representation is larger than the bytes that we read, * work from the top end of the buffer backwards. */ for (int i = cnt - 1 ; i >= 0 ; --i) { unsigned char *p = ((unsigned char *)buf) + i/2; buf[i] = int_to_xdigit(byte_to_nibble(*p, i & 1)); } } virtual void putch(char *&buf, wchar_t ch, int idx, int pct) { /* get the nibble value from the character */ int d = is_xdigit(ch) ? value_of_xdigit(ch) : 0; int n = nibble_to_byte(d, idx & 1); /* * if this is an even index, it's the first nibble of the byte, so * set the whole byte to the nibble value; if it's an odd index, * it's the second nibble for the byte, so OR it into the byte and * increment the output pointer */ if (idx & 1) *buf++ |= (char)n; else *buf = (char)n; } virtual void putch_end(char *&buf, int idx) { /* * If we wrote an odd number of source characters, we've only * written a partial last byte. */ if (idx & 1) ++buf; } /* * The repeat count for this format is in nibbles of the unpacked hex * string, which corresponds to half-bytes in the packed stream. Round * up if it's odd. */ virtual int count_to_bytes(const CVmPackType *t) { return (t->get_count() + 1)/2; } virtual long bytes_to_count(long bytes) { return bytes * 2; } protected: /* * Extract a hex nibble from a byte. 'idx' is zero for the first * nibble of a pair, 1 for the second nibble of a pair, in unpacked * string order. */ virtual int byte_to_nibble(int b, int idx) = 0; /* convert a nibble value to a byte value */ virtual int nibble_to_byte(int n, int idx) = 0; }; /* * Hex string converter, low nibble first, for 'h' format */ class CharConvHexL: public CharConvHex { public: virtual int byte_to_nibble(int b, int idx) { return (idx ? (b >> 4) : b) & 0x0F; } virtual int nibble_to_byte(int b, int idx) { return (idx ? (b << 4) & 0xF0 : b & 0x0F); } }; static CharConvHexL s_CharConvHexL; /* * Hex string converter, high nibble first, for 'H' format */ class CharConvHexB: public CharConvHex { public: virtual int byte_to_nibble(int b, int idx) { return (idx ? b : (b >> 4)) & 0x0F; } virtual int nibble_to_byte(int b, int idx) { return (idx ? b & 0x0F : (b << 4) & 0xF0); } }; static CharConvHexB s_CharConvHexB; /* * Get the converter for a given type code */ CharConv *CharConv::conv_for_type(const CVmPackType *t) { switch (t->type_code) { case 'a': case 'A': return &s_CharConvLatin1; case 'b': return &s_CharConvByte; case 'u': case 'U': return &s_CharConvUTF8; case 'w': case 'W': if (t->big_endian) return &s_CharConvUCS2B; else return &s_CharConvUCS2L; case 'h': return &s_CharConvHexL; case 'H': return &s_CharConvHexB; default: /* it's not a string type */ return 0; } } /* ------------------------------------------------------------------------ */ /* * Write the bytes for a numeric value */ static void write_num_bytes(class CVmDataSource *dst, char *buf, const CVmPackType *t, size_t len) { /* * we always prepare our buffers in our standard little-endian byte * order, so if the type has a big-endian flag, we need to reverse the * byte order */ if (len > 1 && t->big_endian) reverse_bytes(buf, len); /* write the bytes */ if (dst->write(buf, len)) err_throw(VMERR_WRITE_FILE); } /* * Encode a BER compressed unsigned integer value. This format stores an * unsigned integer as a series of bytes representing base-128 digits. The * most significant digit is always stored first (there's no little-endian * variant of this format). Each byte except the last has its high bit * (0x80) set, to indicate that more bytes follow. We can store integers * or BigNumber values in this format. */ static void encode_ber(char *buf, size_t &blen, uint32_t l) { /* nothing in the buffer yet */ blen = 0; /* if it's zero, the result is trivial */ if (l == 0) { buf[blen++] = 0; return; } /* generate the bytes of the absolute value */ int shift, mask; for (shift = 28, mask = 0x08 ; shift >= 0 ; shift -= 7, mask = 0x7f) { /* get the next most significant 128-based "digit" */ char b = (char)((l >> shift) & mask); /* if it's significant (non-zero or not the first digit), write it */ if (b != 0 || blen != 0) buf[blen++] = b; } /* set the high bit in every byte except the last */ for (size_t i = 0 ; i < blen - 1 ; ++i) buf[i] |= 0x80; } /* * Decode a BER-encoded integer. */ static int32_t decode_ber(const char *buf, size_t len) { /* decode the bytes, starting from the least significant */ size_t i = len; int shift = 0; int32_t val = 0; do { val += buf[--i] << shift; shift += 7; } while (i != 0); /* return the result */ return val; } /* ------------------------------------------------------------------------ */ /* * Pack an item or iterated list of items */ void CVmPack::pack_item(VMG_ CVmPackGroup *group, CVmPackArgs *args, CVmPackType *t, CVmPackType *prefix_count) { /* * Retrieve the current argument, to test if it's a list. Note that if * we're out of arguments, it's no problem - get() will fill in val * with nil, and the is_listlike test on nil will return false. */ vm_val_t val; args->get(&val); /* * Note if we have an iterated repeat count. The repeat count is the * number of times to repeat the field type with subsequent arguments, * except for the string types and xX@. */ int repeatable = is_repeatable(t->type_code); /* if we have a count prefix, the item count is implicitly '*' */ int count = t->count; if (prefix_count != 0) count = ITER_STAR; /* check to see if we have a list-like argument */ if (val.is_listlike(vmg0_)) { /* it's a list - set up a list argument reader */ ListArgs lst(vmg_ &val); /* if we have a type that consumes arguments, consume the list */ if (type_consumes_arg((char)t->type_code)) args->next(); /* if there's a prefix count, pack it based on the list length */ if (prefix_count != 0) { /* pack the length of the list as the count */ SingleArg cnt(lst.len); pack_one_item(vmg_ group, &cnt, prefix_count, 0); } /* * Figure the number of list items to pack. If we have an explicit * numeric repeat count, loop exactly that many times. If we no * repeat count, loop once. If we have '*' as the repeat count, or * a prefix count, loop over the list length. */ int iters = (count == ITER_STAR ? lst.len : count < 1 ? 1 : count); /* if this is an up-to count, limit it to the list length */ if (t->up_to_count && iters > lst.len) iters = lst.len; /* iterate over the list */ for (int i = 0 ; i < iters ; ++i) pack_one_item(vmg_ group, &lst, t, 0); } else if (repeatable && (count == ITER_STAR || count > 1)) { /* * We have '*' as the repeat count, which means that we repeat this * item for the remaining arguments at this level. */ if (prefix_count != 0) { /* write the number of remaining arguments */ SingleArg cntarg(args->nrem()); pack_one_item(vmg_ group, &cntarg, prefix_count, 0); } /* pack items until we're out of arguments or reach the repeat count */ int n; for (n = 0 ; count == ITER_STAR ? args->more() : n < count ; ++n) { /* if this is an up-to count and we're out of arguments, stop */ if (t->up_to_count && !args->more()) break; /* pack the item */ pack_one_item(vmg_ group, args, t, 0); } /* if it's an up-to count, consume the rest of the arguments */ if (t->up_to_count) for ( ; args->more() ; args->next()) ; } else { /* * Either there's no prefix count at all, in which case we're * writing a singleton; or there's a prefix count for a string * value, in which case the prefix is the string length. In either * case, we have a single item to write rather than an iterated * list. */ pack_one_item(vmg_ group, args, t, prefix_count); } } /* * Figure the padding for alignment to a given size boundary */ static int alignment_padding(const CVmPackType *t, const CVmPackGroup *group, int dir) { /* get the alignment size; if one or less, add no padding */ int c = t->count; if (c <= 1) return 0; /* get the current offset from the root starting position */ long ofs = group->ds->get_pos() - group->root_stream_ofs(); /* if we're already at a multiple of the size, no padding is needed */ if (ofs % c == 0) return 0; /* figure padding in the desired direction */ if (dir > 0) { /* forward padding - pad to the next multiple of 'c' */ return c - (int)(ofs % c); } else { /* reverse padding - pad to the previous multiple of 'c' */ return -(int)(ofs % c); } } /* * Pack a single item */ void CVmPack::pack_one_item(VMG_ CVmPackGroup *group, CVmPackArgs *args, CVmPackType *t, CVmPackType *prefix_count) { vm_val_t val, cval; int len; char buf[256]; CVmDataSource *dst = group->ds; /* check for non-argument codes */ switch (t->type_code) { case 'x': /* Nul byte(s) */ if (t->bang) write_padding(dst, '\0', alignment_padding(t, group, 1)); else write_padding(dst, '\0', t->get_count()); return; case 'X': /* back up by 'count' bytes */ if (t->bang) dst->seek(alignment_padding(t, group, -1), OSFSK_CUR); else dst->seek(-t->get_count(), OSFSK_CUR); return; case '"': /* literal bytes */ len = 0; for (CVmPackPos p(&t->lit) ; p.len != 0 ; p.inc()) { /* get the next character */ wchar_t ch = p.getch_raw(); /* if it's a quote, it must be stuttered, so skip one more */ if (ch == '"') p.inc(); /* if it's outside the 0-255 range, it's an error */ if (ch > 255) err_throw(VMERR_NUM_OVERFLOW); /* write the charater */ buf[len++] = (char)ch; /* flush the buffer if it's full */ if (len == sizeof(buf)) { dst->write(buf, len); len = 0; } } /* write the final buffer */ if (len != 0) dst->write(buf, len); /* done */ return; case '{': /* in-line hex bytes */ len = 0; for (CVmPackPos p(&t->lit) ; p.more() ; ) { /* get the next two hex digits */ int b = (value_of_xdigit(p.nextch()) << 4); b += value_of_xdigit(p.nextch()); /* add it to the buffer */ buf[len++] = (char)b; /* flush the buffer if it's full */ if (len == sizeof(buf)) { dst->write(buf, len); len = 0; } } /* write the final buffer */ if (len != 0) dst->write(buf, len); /* done */ return; case '@': /* Nul-fill to the absolute position; @? does nothing on packing */ if (!t->qu) { /* * Figure the seek position as an offset from the current file * position. If there's a !, it's relative to the start of the * whole string, otherwise relative to the start of the current * group. */ long l = (t->bang ? group->root_stream_ofs() : group->stream_ofs) + t->get_count() - dst->get_pos(); /* if positive, write Nul bytes; if negative, back up */ if (l > 0) write_padding(dst, '\0', l); else if (l < 0) dst->seek(l, OSFSK_CUR); } return; } /* note if it's upper or lower case */ int lc = is_lower(t->type_code); /* retrieve and consume the next argument */ args->get(&val); args->next(); /* check for a character string conversion */ CharConv *cvtchar = CharConv::conv_for_type(t); if (cvtchar != 0) { /* create the field data source based on the object type */ CharFieldSource *src = CharFieldSource::create(vmg_ &val, &cval); err_try { /* push any new value created by the cast, for gc protection */ G_stk->push(&cval); /* if there's an prefix count, write the string length */ if (prefix_count != 0) { /* write the string length */ SingleArg alen(src->arraylen(t)); pack_one_item(vmg_ group, &alen, prefix_count, 0); } /* * Figure the byte length of the write. The mapping between * the repeat count and the byte length varies by format, so * ask the format handler. If we have a prefix count or a '*' * repeat count, though, we simply write the exact length of * the string. */ int nbytes; if (prefix_count != 0 || t->count == ITER_STAR || (t->count == ITER_NONE && t->null_term)) { /* prefix count, *, or null-term - use the actual length */ nbytes = ITER_STAR; } else { /* specific count - translate characters to bytes */ nbytes = cvtchar->get_byte_count(t); } /* convert the string to the specified representation */ long tot; char *p; int srcidx; for (tot = 0, srcidx = 0, p = buf ; src->more() ; ) { /* * If there's not room for another character in the buffer, * flush the buffer. To make things easy, we assume the * conversion will require the most space of any of our * conversions, which is three bytes for some utf8 * characters. */ if (p - buf > sizeof(buf) - 3) { /* flush the buffer */ if (dst->write(buf, p - buf)) err_throw(VMERR_WRITE_FILE); /* start over at the beginning of the buffer */ p = buf; } /* add the next character */ char *prvp = p; cvtchar->putch(p, src->getch(), srcidx++, t->pct); /* add this to the running total */ tot += p - prvp; /* * If this would push us over the repeat count, back out * this character. */ if (nbytes > 0 && tot > nbytes) { /* back it out and stop adding */ tot -= p - prvp; p = prvp; break; } } /* close out the buffer */ cvtchar->putch_end(p, srcidx); /* if there's anything in the buffer, flush it */ if (p > buf && dst->write(buf, p - buf)) err_throw(VMERR_WRITE_FILE); /* * If we have a repeat count, and we didn't reach the maximum * length, add the padding. Skip this if it's an up-to count, * since an up-to count only packs up to the actual length if * that's less than the count limit. */ if (tot < nbytes && !t->up_to_count) { /* * figure the padding - use space padding for A, U, and W, * and nul padding for everything else */ char pad = get_padding_char(t->type_code); /* write it out */ cvtchar->pad(dst, pad, tot, t); } /* if it's null-terminated, write the null character */ if (t->null_term) cvtchar->null_term(dst); /* discard the gc protection */ G_stk->discard(); } err_finally { /* delete the field data source object */ delete src; } err_end; /* done */ return; } /* it's not a character string type; check other types */ switch (t->type_code) { case 'c': /* signed byte */ buf[0] = (char)intval(vmg_ &val, -128, 127, t->pct, 0xff); write_num_bytes(dst, buf, t, 1); break; case 'C': /* unsigned byte */ buf[0] = (char)intval(vmg_ &val, 0, 255, t->pct, 0xff); write_num_bytes(dst, buf, t, 1); break; case 's': /* signed short (16 bits) */ oswp2s(buf, intval(vmg_ &val, -32768, 32767, t->pct, 0xffff)); write_num_bytes(dst, buf, t, 2); break; case 'S': /* unsigned short (16 bits) */ oswp2(buf, intval(vmg_ &val, 0, 65535, t->pct, 0xffff)); write_num_bytes(dst, buf, t, 2); break; case 'l': /* signed long (32 bits) */ oswp4s(buf, intval(vmg_ &val, -2147483647-1, 2147483647, t->pct, 0xffffffff)); write_num_bytes(dst, buf, t, 4); break; case 'L': /* * Unsigned long (32 bits). TADS doesn't have unsigned longs as a * VM datatype, but a BigNumber can hold a value above LONG_MAX. * So if we have a BigNumber or a string, get the BigNumber value * and see if it fits in a 32-bit unsigned long. Otherwise, it has * to be a positive integer. */ if ((val.typ == VM_OBJ && CVmObjBigNum::is_bignum_obj(vmg_ val.val.obj)) || val.get_as_string(vmg0_) != 0) { /* cast it to a BigNumber */ CVmObjBigNum::cast_to_bignum(vmg_ &cval, &val); /* convert to ulong */ int ov; unsigned long l = ((CVmObjBigNum *)vm_objp(vmg_ cval.val.obj)) ->convert_to_uint(ov); /* make sure it fits in a 32-bit long (unless '%' is set) */ if (ov || l > 4294967295U) { if (t->pct) l &= 0xffffffff; else err_throw(VMERR_NUM_OVERFLOW); } /* save it */ oswp4(buf, l); write_num_bytes(dst, buf, t, 4); } else { /* * It's not a BigNumber, so just cast to int. Since TADS * doesn't have an unsigned 32-bit type, we have to limit the * range to positive integers. */ oswp4(buf, intval(vmg_ &val, 0, 2147483647, t->pct, 0xffffffff)); write_num_bytes(dst, buf, t, 4); } break; case 'q': case 'Q': /* * Long-long (64-bit) types. We can source these values from * integers or BigNumber values. */ if (val.typ == VM_INT) { /* * Integer value. Write the low-order 32 bits, and sign-extend * into the high-order 32 bits. The sign extension is all zero * bits for a positive or zero value, and all 1 bits (0xff * bytes) for a negative value. An unsigned value is * inherently non-negative, so the 1-bit extension applies only * to negative signed values. */ unsigned char sign_ext = 0; if (lc) { /* signed - write the low-order 32 bits, little-endian */ oswp4s(buf, val.val.intval); /* if it's negative, sign-extend with 0xff bytes */ if (val.val.intval < 0) sign_ext = 0xff; } else { /* unsigned - make sure it's not negative */ if (val.val.intval < 0 && !t->pct) err_throw(VMERR_NUM_OVERFLOW); /* write the low-order 32-bits, little-endian, unsigned */ oswp4(buf, val.val.intval); } /* sign-extend or zero-extend into the high-order 32 bits */ memset(buf + 4, sign_ext, 4); } else if (val.typ == VM_NIL) { /* nil - write 0 */ memset(buf, 0, 8); } else { /* it's not an int, so cast it to BigNumber */ CVmObjBigNum::cast_to_bignum(vmg_ &cval, &val); /* write the 8-byte portable little-endian value */ int ov; if (lc) ((CVmObjBigNum * )vm_objp(vmg_ cval.val.obj))->wp8s(buf, ov); else ((CVmObjBigNum * )vm_objp(vmg_ cval.val.obj))->wp8(buf, ov); /* check for overflow */ if (ov && !t->pct) err_throw(VMERR_NUM_OVERFLOW); } /* write the buffer */ write_num_bytes(dst, buf, t, 8); break; case 'k': { /* BER compressed integer */ size_t blen = 0; if (val.typ == VM_INT) { /* make sure it's positive, or that 'pct' is set */ long l = val.val.intval; if (l < 0) { /* * if % is set, use the absolute value; otherwise it's * an error */ if (t->pct) l = -l; else err_throw(VMERR_NUM_OVERFLOW); } /* encode the value */ encode_ber(buf, blen, l); } else if (val.typ == VM_NIL) { /* encode 0 */ buf[blen++] = 0; } else { /* it's not an int, so cast it to BigNumber */ CVmObjBigNum::cast_to_bignum(vmg_ &cval, &val); /* encode the BigNumber in BER format */ int ov; ((CVmObjBigNum *)vm_objp(vmg_ cval.val.obj))->encode_ber( buf, sizeof(buf), blen, ov); /* check for overflow */ if (ov && !t->pct) err_throw(VMERR_NUM_OVERFLOW); } /* write the buffer */ if (dst->write(buf, blen)) err_throw(VMERR_WRITE_FILE); } break; case 'f': len = 4; goto float_fmt; case 'd': len = 8; float_fmt: /* float/double - cast to BigNumber */ if (val.typ == VM_NIL) { /* nil - return as zero, which is all 0 bits in all formats */ memset(buf, 0, len); } else { /* cast the value to BigNumber */ CVmObjBigNum::cast_to_bignum(vmg_ &cval, &val); G_stk->push(&cval); /* convert to the 32-bit or 64-bit IEEE 754-2008 format */ int ov; ((CVmObjBigNum *)vm_objp(vmg_ cval.val.obj)) ->convert_to_ieee754(vmg_ buf, len*8, ov); /* check for overflow */ if (ov && !t->pct) err_throw(VMERR_NUM_OVERFLOW); /* discard the gc protection */ G_stk->discard(); } /* write out the result */ write_num_bytes(dst, buf, t, len); break; } } /* ------------------------------------------------------------------------ */ /* * Unpack data from the given stream into a list */ void CVmPack::unpack(VMG_ vm_val_t *retval, const char *fmt, size_t fmtlen, CVmDataSource *src) { /* * Set up a list for the return value. We don't know how many elements * we'll need, so allocate an initial chunk now and expand later as * needed. (We can't necessarily know the return size even by * pre-parsing the format string, since it could have variable-size * items with array counts.) */ retval->set_obj(CVmObjList::create(vmg_ FALSE, 10)); CVmObjList *retlst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* * since we're going to build the list iteratively, clear it out, so * that it doesn't contain any uninitialized elements that the garbage * collector might encounter */ retlst->cons_clear(); /* save it on the stack for gc protection */ G_stk->push(retval); /* we don't have any elements in our list yet */ int retcnt = 0; /* unpack the root group */ CVmPackPos p(fmt, fmtlen); CVmPackGroup root(&p, src); unpack_group(vmg_ &p, retlst, &retcnt, &root, FALSE); /* make sure we made it all the way through the format string */ if (p.more()) err_throw_a(VMERR_PACK_PARSE, 1, ERR_TYPE_INT, p.index()); /* set the return list's final length */ retlst->cons_set_len(retcnt); /* discard the gc protection */ G_stk->discard(); } /* * Unpack a group */ void CVmPack::unpack_group(VMG_ CVmPackPos *p, CVmObjList *retlst_par, int *retcnt_par, CVmPackGroup *group, int list_per_iter) { /* get the data source */ CVmDataSource *src = group->ds; /* * If the group has an "up to" count (e.g., "30*"), check to see if * we're already at EOF, and if so don't unpack anything - an iteration * count of zero is valid for an up-to count. */ if ((group->num_iters == ITER_STAR || group->up_to_iters) && src->get_pos() >= src->get_size()) { /* skip the group in the source */ p->set(&group->start); skip_group(p, group->close_paren); /* we're done */ return; } /* assume we'll unpack directly into the parent list */ CVmObjList *retlst = retlst_par; int *retcnt = retcnt_par; /* * if we're unpacking into a per-instance sublist, create the sublist * for the first iteration, and set this as the output list instead of * unpacking directly into the parent list */ int grpcnt = 0; if (list_per_iter) { retlst = create_group_sublist(vmg_ retlst_par, retcnt_par); retcnt = &grpcnt; } /* run through the format list */ while (p->more()) { /* check for group entry/exit and repeat counts */ wchar_t c = p->getch(); if (c == '(' || c == '[') { /* start of a group - unpack the sub-group */ unpack_subgroup(vmg_ p, retlst, *retcnt, group, 0); } else if (c == group->close_paren) { /* end of group - count the completed iteration */ group->cur_iter++; /* * if we're unpacking into a per-iteration sublist, note the * final group count in the sublist */ if (list_per_iter) retlst->cons_set_len(*retcnt); /* * We're done with the group if (a) we have a '*' iteration * count, and we're at end of file, or (b) we've reached the * desired iteration count. If done, simply return to the * enclosing group. */ if (group->num_iters == ITER_STAR ? src->get_pos() >= src->get_size() : group->cur_iter > group->num_iters) return; /* * if we have an 'up to' iteration count, stop if we're at EOF * even if we haven't exhausted the numeric iteration count */ if (group->up_to_iters && src->get_pos() >= src->get_size()) return; /* return to the start of the group template for the next round */ p->set(&group->start); group->stream_ofs = group->ds->get_pos(); /* * If we're unpacking into a sublist per iteration, create the * new sublist for the new iteration. */ if (list_per_iter) { retlst = create_group_sublist(vmg_ retlst_par, retcnt_par); grpcnt = 0; } } else if (c == '/') { /* * Repeat count prefix. Remove the preceding return list * element and interpret it as an integer. Make sure there * actually is an item in the list - it's an error if not. */ if (retcnt == 0) err_throw_a(VMERR_PACK_PARSE, 1, ERR_TYPE_INT, p->index()); /* remove the item and cast it to integer */ vm_val_t cntval; retlst->get_element(--(*retcnt), &cntval); int cnt = (int)cntval.cast_to_int(vmg0_); /* skip the '/' and check what follows */ p->inc(); if ((c = p->getch()) == '(' || c == '[') { /* it's a group - unpack the group with the count */ unpack_subgroup(vmg_ p, retlst, *retcnt, group, &cnt); } else { /* it's a single item - unpack the repeated items */ unpack_iter_item(vmg_ p, retlst, *retcnt, group, &cnt); } } else { /* it's a regular item, possibly iterated - unpack it */ unpack_iter_item(vmg_ p, retlst, *retcnt, group, 0); } } } /* * Unpack a subgroup */ void CVmPack::unpack_subgroup(VMG_ CVmPackPos *p, CVmObjList *retlst, int &retcnt, CVmPackGroup *group, const int *prefix_count) { /* group - parse the group modifiers */ CVmPackType gt(group); parse_group_mods(p, >); /* note the open paren type */ wchar_t paren = p->getch(); /* skip the paren and set up the child group */ p->inc(); CVmPackGroup child(group, paren, p, >); /* if there's a prefix count, it overrides the suffix count */ if (prefix_count != 0) child.num_iters = *prefix_count; /* * If the group started with '[', it means that it's to be unpacked * into a list. Otherwise, it's unpacked directly into the enclosing * list. Create a new list if necessary. */ if (paren == '[') { /* * if there's a '!' modifier, we unpack each iteration into a * separate list; otherwise we unpack the whole group into a single * list */ if (gt.bang) { /* * unpack into individual per-iteration sublists, each of which * goes into the main list */ unpack_group(vmg_ p, retlst, &retcnt, &child, TRUE); } else { /* create the result list for the whole group */ CVmObjList *grplst = create_group_sublist(vmg_ retlst, &retcnt); int grpcnt = 0; /* unpack into the sublist */ unpack_group(vmg_ p, grplst, &grpcnt, &child, FALSE); /* set the final number of elements in the list */ grplst->cons_set_len(grpcnt); } } else { /* no sublist - unpack into the main list */ unpack_group(vmg_ p, retlst, &retcnt, &child, FALSE); } /* make sure we stopped at the close paren */ if (p->nextch() != child.close_paren) err_throw_a(VMERR_PACK_PARSE, 1, ERR_TYPE_INT, p->index()); /* skip any modifiers for the group */ parse_mods(p, >); } /* * Create a sublist for unpacking a "[ ]" group */ CVmObjList *CVmPack::create_group_sublist( VMG_ CVmObjList *parent_list, int *parent_cnt) { /* create the new sublist */ vm_val_t ele; ele.set_obj(CVmObjList::create(vmg_ FALSE, 10)); CVmObjList *sublst = (CVmObjList *)vm_objp(vmg_ ele.val.obj); /* clear it, since we'll be filling it in iteratively */ sublst->cons_clear(); /* * Add it to the parent list. The parent list is on the stack (or in a * list that's on the stack, or in a list that's in a list on the * stack, etc) - that will protect the new list from premature garbage * collection. */ parent_list->cons_ensure_space(vmg_ *parent_cnt, 10); parent_list->cons_set_element((*parent_cnt)++, &ele); /* return the sublist */ return sublst; } /* ------------------------------------------------------------------------ */ /* * Unpack an iterated item */ void CVmPack::unpack_iter_item(VMG_ CVmPackPos *p, CVmObjList *retlst, int &retcnt, CVmPackGroup *group, const int *prefix_count) { int len; /* parse the template item */ CVmPackType t; parse_type(p, &t, group); /* get the data source */ CVmDataSource *src = group->ds; /* check for non-value types and string types */ int is_string_type = FALSE; switch (t.type_code) { case 'x': /* Nul bytes: on input, just skip bytes */ if (t.bang) src->seek(alignment_padding(&t, group, 1), OSFSK_CUR); else src->seek(t.get_count(), OSFSK_CUR); return; case 'X': /* back up by 'count' bytes */ if (t.bang) src->seek(alignment_padding(&t, group, -1), OSFSK_CUR); else src->seek(-t.get_count(), OSFSK_CUR); return; case '"': /* count the characters in the string */ len = 0; for (CVmPackPos p(&t.lit) ; p.len != 0 ; p.inc(), ++len) { /* if this is a quote, it must be stuttered, so skip it */ if (p.getch_raw() == '"') p.inc(); } /* skip the bytes, and we're done */ src->seek(len * t.get_count(), OSFSK_CUR); return; case '{': /* count the hex digit pairs */ len = 0; for (CVmPackPos p(&t.lit) ; p.more() ; ++len, p.inc(), p.inc()) ; /* skip the bytes, and we're done */ src->seek(len * t.get_count(), OSFSK_CUR); return; case '@': /* seek to position or retrieve current position */ if (t.qu) { /* @? retrieves the current position as an output value */ unpack_into_list(vmg_ retlst, retcnt, src, &t, group); } else { /* * Absolute positioning: seek to the starting position plus the * specified offset. The starting position is the current * group's starting position, or the whole string's start if * the ! qualifier is present. */ src->seek((t.bang ? group->root_stream_ofs() : group->stream_ofs) + t.get_count() - src->get_pos(), OSFSK_CUR); } return; case 'a': case 'A': case 'b': case 'u': case 'U': case 'w': case 'W': case 'h': case 'H': /* make a note that this is a string type */ is_string_type = TRUE; break; } /* * We have a regular unpacking type (not one of the special positioning * codes). If there's a prefix count, it overrides any count specified * in the type. */ if (prefix_count != 0) t.count = *prefix_count; /* * If it's a string type, the repeat count specifies the length of the * string in the file (in bytes, characters, nibbles, or other units * that vary by template type). Otherwise, the repeat count is the * number of copies of the item to unpack. */ if (is_string_type) { /* * It's a string type. The count doesn't specify the number of * strings to write, but rather specifies the number of bytes to * read into one string. */ unpack_into_list(vmg_ retlst, retcnt, src, &t, group); } else if (t.count == ITER_STAR) { /* unpack this item repeatedly until we run out of source */ long src_size = src->get_size(); while (src->get_pos() < src_size) unpack_into_list(vmg_ retlst, retcnt, src, &t, group); } else if (t.up_to_count) { /* * unpack this item the number of times given by the repeat count, * but stop if we reach EOF first */ long src_size = src->get_size(); for (int i = 0 ; i < t.get_count() && src->get_pos() < src_size ; ++i) unpack_into_list(vmg_ retlst, retcnt, src, &t, group); } else { /* unpack this item the number of times given by the repeat count */ for (int i = 0 ; i < t.get_count() ; ++i) unpack_into_list(vmg_ retlst, retcnt, src, &t, group); } } /* * Unpack an item and add it to the list */ void CVmPack::unpack_into_list(VMG_ CVmObjList *retlst, int &retcnt, CVmDataSource *src, const CVmPackType *t, const CVmPackGroup *group) { /* unpack the item */ vm_val_t ele; unpack_item(vmg_ &ele, src, t, group); /* add it to the list */ retlst->cons_ensure_space(vmg_ retcnt, 10); retlst->cons_set_element(retcnt++, &ele); } /* ------------------------------------------------------------------------ */ /* * Read a numeric (int/float/double) buffer, normalizing into little-endian * order. */ static void read_num_bytes(char *buf, CVmDataSource *src, size_t len, const CVmPackType *t) { /* read the bytes */ if (src->read(buf, len)) err_throw(VMERR_READ_FILE); /* * if the source is in big-endian order, swap the bytes to get our * standard little-endian order */ if (len > 1 && t->big_endian) reverse_bytes(buf, len); } /* ------------------------------------------------------------------------ */ /* * Unpack one item */ void CVmPack::unpack_item(VMG_ vm_val_t *val, CVmDataSource *src, const CVmPackType *t, const CVmPackGroup *group) { /* check for a character conversion */ CharConv *cvtchar = CharConv::conv_for_type(t); if (cvtchar != 0 && t->null_term && t->count <= 0) { /* * It's a null-terminated string type without a fixed field width. * Use the special unpacking method for this type. */ cvtchar->unpackz(vmg_ val, src); /* done */ return; } if (cvtchar != 0) { /* character string - the repeat count is the length */ int count = t->get_count(); /* if the count is in bytes, convert it */ if (t->count_in_bytes) count = cvtchar->bytes_to_count(count); /* if the character count is zero, return an empty string */ if (count == 0) { val->set_obj(CVmObjString::create(vmg_ FALSE, 0)); return; } /* if it's '*', we read the rest of the data source */ if (t->count == ITER_STAR || t->up_to_count) { /* * get the remaining length, and convert it to a repeat count * appropriate to the string conversion type */ long l = cvtchar->bytes_to_count(src->get_size() - src->get_pos()); /* use 'l' as a maximum for 'n*', or the actual count for '*' */ if (t->count == ITER_STAR) { /* * '*' means read the whole rest of the file - that's a * long value, so cast it to int, making sure we don't * overflow */ count = (int)l; if (l > INT_MAX) err_throw(VMERR_NUM_OVERFLOW); } else { /* * 'n*' means read up to n, stopping at EOF, so stop at 'l' * if it's less than the specified count */ if (l < count) count = (int)l; } } /* figure the padding type */ char pad = get_padding_char(t->type_code); /* unpack the string */ cvtchar->unpack(vmg_ val, src, count, pad); /* check for null termination */ if (t->null_term) { /* skip the null, which is tacked on after the fixed length */ cvtchar->readch(src); /* terminate the string at the first null, if present */ if (val->typ == VM_OBJ && CVmObjString::is_string_obj(vmg_ val->val.obj)) { /* get the string */ CVmObjString *str = (CVmObjString *)vm_objp(vmg_ val->val.obj); /* scan for a null byte */ char *nul = (char *)memchr( str->cons_get_buf(), 0, str->cons_get_len()); /* if we found a null byte, terminate the string there */ if (nul != 0) str->cons_shrink_buffer(vmg_ nul); } } /* done */ return; } /* it's not a string; check the numeric types */ char buf[256]; switch (t->type_code) { case '@': /* * @? retrieves the current position, relative to the current group * or, if ! is specified, relative to the start of the string */ val->set_int(src->get_pos() - (t->bang ? group->root_stream_ofs() : group->stream_ofs)); break; case 'c': /* signed byte */ read_num_bytes(buf, src, 1, t); val->set_int((char)buf[0]); break; case 'C': /* unsigned byte */ read_num_bytes(buf, src, 1, t); val->set_int((unsigned char)buf[0]); break; case 's': /* signed 16-bit short */ read_num_bytes(buf, src, 2, t); val->set_int(osrp2s(buf)); break; case 'S': /* unsigned 16-bit short */ read_num_bytes(buf, src, 2, t); val->set_int((unsigned short)osrp2(buf)); break; case 'l': /* signed 32-bit long */ read_num_bytes(buf, src, 4, t); val->set_int(osrp4s(buf)); break; case 'L': /* * Unsigned 32-bit long. The VM_INT type is a signed long, so we * can't use this to represent the full range of unsigned longs. * Instead, use BigNumber. Note that we could check the file value * and use VM_INT if the value actually fits (i.e., it's less than * 0x7fffffff); this might be desirable in some cases because * VM_INT is much more efficient than BigNumber. However, since * the caller is asking for type 'L', they're clearly expecting the * full unsigned 32-bit range, which might mean they plan to do * further arithmetic on the value we read, in which case they'd * expect this arithmetic to be safe on unsigned 32-bit values. * The only way we can guarantee this is to use a BigNumber for the * value regardless of whether or not it fits in a VM_INT. * * If the ~ qualifier is set, unpack into an int if the value fits * in a signed 32-bit int. */ { read_num_bytes(buf, src, 4, t); unsigned long lu = osrp4(buf); if (t->tilde && lu <= 0x7fffffff) val->set_int((long)lu); else val->set_obj(CVmObjBigNum::createu(vmg_ FALSE, lu, 10)); } break; case 'q': /* * "Long long" 64-bit integer, signed. There's no VM_xxx primitive * type that can handle a value this large, so use BigNumber. Read * the 8-byte file value. * * If the ~ qualifier is set, and the high part of the value is all * zeros or all '1' bits, return it as an integer. Note that the * buffer is in our standard osrp8 format, which is little-endian * 2's complement, so we can inspect the byte values portably. */ read_num_bytes(buf, src, 8, t); if (t->tilde && ((memcmp(buf+4, "\0\0\0\0", 4) == 0 && (buf[3] & 0x80) == 0) || (memcmp(buf+4, "\377\377\377\377", 4) == 0 && (buf[3] & 0x80) != 0))) val->set_int(osrp4s(buf)); else val->set_obj(CVmObjBigNum::create_rp8s(vmg_ FALSE, buf)); break; case 'Q': /* * "Long long" 64-bit integer, unsigned. Unpack as a BigNumber, * unless the ~ qualifier is set and the value will fit in an int. */ read_num_bytes(buf, src, 8, t); if (t->tilde && memcmp(buf+4, "\0\0\0\0", 4) == 0 && (buf[3] & 0x80) == 0) val->set_int(osrp4s(buf)); else val->set_obj(CVmObjBigNum::create_rp8(vmg_ FALSE, buf)); break; case 'k': /* * BER compressed unsigned integer. This is a sequence of 7-bit, * base-128 "digits", most significant digit first. The high bit * (0x80) is set on each digit except the last, which tells us * where the value ends. Read bytes one at a time until we reach * the last byte or run out of buffer space. */ { size_t i; for (i = 0 ; ; ) { /* if we're out of buffer space, throw an error */ if (i >= sizeof(buf)) err_throw(VMERR_NUM_OVERFLOW); /* read the next byte */ if (src->read(buf + i, 1)) err_throw(VMERR_READ_FILE); /* if the high bit isn't set, this is the last byte */ if ((buf[i++] & 0x80) == 0) break; /* keep just the low-order 7 bits of the value */ buf[i-1] &= 0x7F; } /* * If the value is 31 bits or fewer, return it as an integer. * Otherwise, return it as a BigNumber. 31 bits fits in 4 * bytes (7*4=28) plus 3 bits of the MSB, so if we have more * than 5 bytes, or any of bits 4-8 of the MSB are set, we need * a BigNumber. (Why 31 bits and not 32? Because BER values * are unsigned, and our int32 type is signed, meaning it only * holds 31 bits for a positive value.) */ if (i < 5 || (i == 5 && (buf[0] & 0xF8) == 0)) { /* it fits in a 32-bit integer - decode it */ val->set_int(decode_ber(buf, i)); } else { /* more than 31 bits - decode into a BigNumber */ val->set_obj(CVmObjBigNum::create_from_ber(vmg_ FALSE, buf, i)); } } break; case 'f': /* * Float - read the 32-bit float value, and return it as a * BigNumber value. Our standard IEEE 754-2008 32-bit format * represents 7.22 decimal digits of precision, so keep 8 digits. */ read_num_bytes(buf, src, 4, t); val->set_obj(CVmObjBigNum::create_from_ieee754(vmg_ FALSE, buf, 32)); break; case 'd': /* * Double - read the 64-bit double value, and return it as a * BigNumber value. Our standard IEEE 754-2008 64-bit format * represents 15.95 decimal digits of precision. */ read_num_bytes(buf, src, 8, t); val->set_obj(CVmObjBigNum::create_from_ieee754(vmg_ FALSE, buf, 64)); break; } } /* ------------------------------------------------------------------------ */ /* * Parse a type code */ void CVmPack::parse_type(CVmPackPos *p, CVmPackType *info, const CVmPackGroup *group) { /* get the type code */ wchar_t c = p->nextch(); if (!is_valid_type(c)) err_throw_a(VMERR_PACK_PARSE, 1, ERR_TYPE_INT, p->index() - 1); /* set the format string index */ info->fmtidx = p->index() - 1; /* this is the type code - save it */ info->type_code = c; /* * if it's a quote, parse the characters to the next quote; if it's an * open brace, parse the hex digits to the close brace */ if (c == '"') { /* note the start of the literal section */ info->lit.set(p); /* scan for the closing quote */ for ( ; ; p->inc()) { /* if at end of string, it's an error */ if (!p->more()) err_throw_a(VMERR_PACK_PARSE, 1, ERR_TYPE_INT, p->index()); /* check for the close quote */ if (p->getch() == '"') { /* this might be it - note the length if so */ size_t len = info->lit.len - p->len; /* skip it */ p->inc(); /* if it's not a stuttered quote, it's really the end */ if (p->getch() != '"') { info->lit.len = len; break; } } } } else if (c == '{') { /* note the start of the literal section */ info->lit.set(p); /* scan for the closing brace - only hex digits are allowed */ for ( ; ; p->inc()) { /* if at end of string, it's an error */ if (!p->more()) err_throw_a(VMERR_PACK_PARSE, 1, ERR_TYPE_INT, p->index()); /* make sure it's a hex digit or close brace */ if (p->getch() == '}') { /* it's the close brace - note it, skip it, and we're done */ info->lit.len -= p->len; p->inc(); break; } else if (!is_xdigit(p->getch())) err_throw_a(VMERR_PACK_PARSE, ERR_TYPE_INT, p->index()); } } /* set the default modifiers */ info->big_endian = group->big_endian; info->tilde = group->tilde; info->pct = group->pct; info->count = ITER_NONE; info->count_as_type = 0; info->count_in_bytes = FALSE; info->bang = info->qu = FALSE; info->null_term = FALSE; /* check for modifiers */ parse_mods(p, info); } /* * Parse group modifiers. Call this at the open parenthesis of a group to * find the matching close paren and parse the suffix qualifiers. */ void CVmPack::parse_group_mods(const CVmPackPos *p, CVmPackType *gt) { /* set up a copy of the pointer, so we don't alter the original */ CVmPackPos p2(p); /* parse and skip the group and modifiers */ skip_group_mods(&p2, gt); } /* * Parse and skip a group and its modifiers. Call this at the open paren * of a group to find the matching close paren and parse past the suffix * qualifiers. This leaves 'p' positioned after the group's qualifiers. */ void CVmPack::skip_group_mods(CVmPackPos *p, CVmPackType *gt) { /* note the close paren type we're looking for */ wchar_t c = p->getch(); wchar_t close_paren = (c == '(' ? ')' : c == '[' ? ']' : c == '{' ? '}' : 0); /* skip to the close paren */ p->inc(); skip_group(p, close_paren); /* skip the close paren */ p->inc(); /* parse the group modifiers */ parse_mods(p, gt); } /* * Skip to the close paren of a group. Call this at the character after * the open paren; we'll read ahead until we find the close paren, and * return positioned at the paren. */ void CVmPack::skip_group(CVmPackPos *p, wchar_t close_paren) { /* scan ahead to the matching close paren */ int depth = 1; for (p->inc() ; p->more() ; p->inc()) { /* note any further nesting */ wchar_t c = p->getch(); switch (c) { case '(': case '[': case '{': depth++; break; case ')': case ']': case '}': depth--; break; } /* * if this is our close paren, and we're not in nested parens or * other brackets, we're done */ if (c == close_paren && depth == 0) break; } /* make sure we found the matching close paren */ if (!p->more()) err_throw_a(VMERR_PACK_PARSE, 1, ERR_TYPE_INT, p->index()); } /* * Figure the '*' repeat count value for a given type code */ static int star_to_count(wchar_t type_code) { switch (type_code) { case 'x': case 'X': case '@': return 0; default: return ITER_STAR; } } /* * Parse modifiers */ void CVmPack::parse_mods(CVmPackPos *p, CVmPackType *t) { /* keep going until we stop finding modifiers */ while (p->more()) { /* get the next character */ wchar_t c = p->getch(); /* check what we have */ if (c == '<') { /* little-endian flag - note it and skip it */ t->big_endian = FALSE; p->inc(); } else if (c == '>') { /* big-endian flag - note it and skip it */ t->big_endian = TRUE; p->inc(); } else if (c == '0') { /* this can only be used with certain string types */ if (strchr("auwhH", (char)t->type_code) == 0) err_throw_a(VMERR_PACK_PARSE, 1, ERR_TYPE_INT, p->index()); /* null-termination */ t->null_term = TRUE; p->inc(); } else if (is_digit(c)) { /* * if we already have a '*', the combination of a numeric count * and a '*' makes it an up-to count */ if (t->count == ITER_STAR) t->up_to_count = TRUE; /* it's a digit, so this is a simple numeric repeat count */ int count_idx = p->index(); t->count = p->parse_int(); t->count_as_type = 0; /* enforce a minimum value of 1 for most types, 0 for @ */ int minval = (t->type_code == '@' ? 0 : 1); if (t->count < minval) err_throw_a(VMERR_PACK_PARSE, 1, ERR_TYPE_INT, count_idx); } else if (c == ':') { /* repeat count as type - get and remember the type code */ p->inc(); c = p->nextch(); /* validate the type code and get its size */ switch (c) { case 'a': case 'A': case 'b': case 'u': case 'U': case 'h': case 'H': case 'k': t->count_as_type = c; t->count = 1; case 'w': case 'W': t->count_as_type = c; t->count = 2; break; case 'c': case 'C': t->count_as_type = c; t->count = 1; break; case 's': case 'S': t->count_as_type = c; t->count = 2; break; case 'l': case 'L': case 'f': t->count_as_type = c; t->count = 4; break; case 'q': case 'Q': case 'd': t->count_as_type = c; t->count = 8; break; default: /* invalid type */ err_throw_a(VMERR_PACK_PARSE, 1, ERR_TYPE_INT, p->index()); } } else if (c == '*') { /* * if we already have a numeric count, this makes it an up-to * count */ if (t->count >= 0) { /* mark it as an up-to count, but leave the count intact */ t->up_to_count = TRUE; } else { /* there's no count yet, so it's an indefinite counter type */ t->count = star_to_count(t->type_code); t->count_as_type = 0; } /* skip the '*' */ p->inc(); } else if (c == '!') { /* * ! means "count in bytes" for string types wWhH; for other * types just record the ! */ if (t->type_code != 0 && strchr("wWhH", (char)t->type_code) != 0) t->count_in_bytes = TRUE; else t->bang = TRUE; p->inc(); } else if (c == '~') { /* note the ~ qualifier */ t->tilde = TRUE; p->inc(); } else if (c == '?') { /* note the ? qualifier */ t->qu = TRUE; p->inc(); } else if (c == '%') { /* note the % qualifier */ t->pct = TRUE; p->inc(); } else { /* it's not a modifier character, so we're done */ break; } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmpack.h��������������������������������������������������������������������������0000664�0000000�0000000�00000011623�12650170723�0015313�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmpack.h - binary data stream pack/unpack Function Converts between a binary data stream and TADS types, using a list of type codes to drive the conversion. This is a convenient way of reading and writing structured binary files. Our implementation uses generic data streams, so it can work with various underlying data sources; in particular, we provide File and ByteArray interfaces. Notes Modified 10/01/10 MJRoberts - Creation */ #ifndef VMPACK_H #define VMPACK_H #include "t3std.h" #include "vmtype.h" class CVmPack { public: /* * Pack data from stack arguments into the given stream. 'arg_index' * is the index in the stack of the format string argument; the data * value arguments come after it on the stack. 'argc' is the total * number of arguments, including the format string. */ static void pack(VMG_ int arg_index, int argc, class CVmDataSource *dst); /* * Unpack data from the given stream into a list. */ static void unpack(VMG_ vm_val_t *retval, const char *fmt, size_t fmtlen, class CVmDataSource *src); /* write padding bytes */ static void write_padding(class CVmDataSource *dst, char ch, long cnt); static void write_padding(class CVmDataSource *dst, char ch1, char ch2, long cnt); protected: /* * Pack a group. pack() calls this to start packing the main string, * and we recurse for each parenthesized group we find. */ static void pack_group(VMG_ struct CVmPackPos *pos, struct CVmPackArgs *args, struct CVmPackGroup *group, int list_per_iter); /* pack a subgroup */ static void pack_subgroup(VMG_ struct CVmPackPos *p, struct CVmPackArgs *args, struct CVmPackGroup *parent, struct CVmPackType *prefix_count); /* pack an item or iterated list of items */ static void pack_item(VMG_ struct CVmPackGroup *group, struct CVmPackArgs *args, struct CVmPackType *typ, struct CVmPackType *prefix_count); /* pack a single item */ static void pack_one_item(VMG_ struct CVmPackGroup *group, struct CVmPackArgs *args, struct CVmPackType *t, struct CVmPackType *prefix_count); /* unpack a group */ static void unpack_group(VMG_ struct CVmPackPos *p, class CVmObjList *retlst, int *retcnt, struct CVmPackGroup *group, int list_per_iter); /* unpack a subgroup */ static void unpack_subgroup(VMG_ CVmPackPos *p, class CVmObjList *retlst, int &retcnt, struct CVmPackGroup *group, const int *prefix_count); /* create a sublist for unpacking a "[ ]!" group */ static CVmObjList *create_group_sublist( VMG_ CVmObjList *parent_list, int *parent_cnt); /* unpack an iterated item */ static void unpack_iter_item(VMG_ struct CVmPackPos *p, class CVmObjList *retlst, int &retcnt, struct CVmPackGroup *group, const int *prefix_count); /* unpack an item into the list */ static void unpack_into_list(VMG_ class CVmObjList *retlst, int &retcnt, class CVmDataSource *src, const struct CVmPackType *t, const struct CVmPackGroup *group); /* unpack a single item */ static void unpack_item(VMG_ vm_val_t *val, CVmDataSource *src, const struct CVmPackType *t, const struct CVmPackGroup *group); /* parse a type code; increments the pointer past the parsed input */ static void parse_type(struct CVmPackPos *p, struct CVmPackType *info, const struct CVmPackGroup *group); /* * parse the suffix modifiers for a group, given a pointer to the * opening parenthesis of the group */ static void parse_group_mods(const struct CVmPackPos *p, struct CVmPackType *gt); /* find the close parenthesis/bracket of a group */ static void skip_group(struct CVmPackPos *p, wchar_t close_paren); /* parse and skip a group and its modifiers */ static void skip_group_mods(struct CVmPackPos *p, struct CVmPackType *gt); /* parse suffix modifiers */ static void parse_mods(struct CVmPackPos *p, struct CVmPackType *t); }; #endif /* VMPACK_H */ �������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmparam.h�������������������������������������������������������������������������0000664�0000000�0000000�00000012340�12650170723�0015472�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmparam.h - T3 Virtual Machine Initialization Parameters Function Defines a set of parameters that we use to initialize the VM during program startup. These parameters are #define'd here if not already #define'd elsewhere - this allows the system makefile to override the parameters as desired for larger or smaller local limits. For example, with a conventional Unix makefile, one could override the stack size parameter like so: make -DVM_STACK_SIZE=4096 -funix/makefile Notes Modified 10/07/01 MJRoberts - Creation */ #ifndef VMPARAM_H #define VMPARAM_H /* * Error stack size. This is the number of bytes allocated per thread for * our custom exception mechanism (see vmerr.h). err_throw() uses this * space to store our custom exception structures and their parameters * * This limit doesn't affect exception handling for VM byte code, which * uses the VM stack and VM objects. This parameter only affects our * internal exception handling mechanism at the C++ level (err_try, * err_catch, etc). It's unlikely that this will need to be modified for * porting. */ #ifndef VM_ERR_STACK_SIZE # define VM_ERR_STACK_BYTES 1024 #endif /* * Name and os_exe_seek resource type of VM error message file. This file * is optional; if present, it overrides the error message strings linked * into the VM directly, to allow translated versions of the messages. * These messages describe run-time exceptions. */ #ifndef VM_ERR_MSG_FNAME # define VM_ERR_MSG_FNAME "T3_VM.msg" #endif #ifndef VM_ERR_MSG_RESTYPE # define VM_ERR_MSG_RESTYPE "XMSG" #endif /* * VM stack size, in elements. This is the number of slots allocated for * the VM stack, which is used to store activation frames and intermediate * calculation results during byte-code execution. On a typical 32-bit * machine, each slot takes 8 bytes. */ #ifndef VM_STACK_SIZE # define VM_STACK_SIZE 4096 #endif /* * VM stack reserve for stack overflow exception handling. This is the * number of slots we allocate to process a stack overflow exception. This * gives us a little space to work with in creating the exception object, * so that the program can recover from the exception. */ #ifndef VM_STACK_RESERVE # define VM_STACK_RESERVE 64 #endif /* * Debugger VM stack reserve size, in elements. This is the number of * slots we allocate for emergency use by the debugger in recovering from a * stack overflow exception. * * In debug builds only, this is used in lieu of the normal * VM_STACK_RESERVE. This is usually a bit larger than the normal reserve, * to create enough headroom that the user can evaluate expressions and * otherwise carry on with interactive debugging activities while stopped * at the point of the overflow. */ #ifndef VM_STACK_DBG_RESERVE # define VM_STACK_DBG_RESERVE 256 #endif /* * Undo record limit and savepoint limit. This controls the space * allocated for the undo mechanism, which allows a snapshot of the * machine state (called a "snapshot") to be taken such that the exact * machine state as it was at the snapshot can be restored at a later * time. Undo information is kept as deltas from the savepoint, so the * amount of space needed is proportional to the number of changes made * since the snapshot, and is not related to the total program size. * * The record limit specifies the maximum number of individual changes can * be stored in the undo log. Each time a property, array element, or * other individual scalar field of some object is changed, an undo record * is consumed. Empirically, the standard library seems to use around 200 * records per player turn, but this can vary substantially depending on * what actions are performed, and a game with many daemons carrying on * background operations could conceivably use a lot more space per turn. * * The savepoint limit specifies the maximum number of savepoints that can * be created simultaneously. There is a hard upper limit of 255 * savepoints. * * There is no visible error condition generated by exhausting the undo * space - the undo mechanism simply discards the oldest savepoint or * savepoints as needed to make room for new records. * * The default record limit is meant to allow about ten turns of undo to * be saved. We choose a much higher savepoint limit because savepoints * consume almost no space in the undo log, so there is no cost of making * this limit high enough that the undo record limit is almost certain to * dominate - in other words, since savepoints cost nothing, there is no * point in artificially limiting them. * * An undo record takes up about 16 bytes (on a machine with 32-bit * pointers and 32-bit alignment; this will obviously vary for hardware * with different sizes or alignment requirements). */ #ifndef VM_UNDO_MAX_RECORDS # define VM_UNDO_MAX_RECORDS 4096 #endif #ifndef VM_UNDO_MAX_SAVEPTS # define VM_UNDO_MAX_SAVEPTS 64 #endif #endif /* VMPARAM_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmpat.cpp�������������������������������������������������������������������������0000664�0000000�0000000�00000021502�12650170723�0015511�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmpat.cpp - regular-expression compiled pattern object Function Notes Modified 08/27/02 MJRoberts - Creation */ #include <stdlib.h> #include <os.h> #include "vmtype.h" #include "vmobj.h" #include "vmmeta.h" #include "vmglob.h" #include "vmregex.h" #include "vmpat.h" #include "vmstack.h" #include "vmbif.h" #include "vmbiftad.h" #include "vmfile.h" #include "vmrun.h" /* ------------------------------------------------------------------------ */ /* * Statics */ static CVmMetaclassPattern metaclass_reg_obj; CVmMetaclass *CVmObjPattern::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjPattern:: *CVmObjPattern::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjPattern::getp_undef, &CVmObjPattern::getp_get_str }; /* ------------------------------------------------------------------------ */ /* * create */ CVmObjPattern::CVmObjPattern(VMG_ re_compiled_pattern *pat, const vm_val_t *src_str) { /* allocate my extension data */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(sizeof(vmobj_pat_ext), this); /* remember my source data */ set_orig_str(src_str); /* remember the compiled pattern */ set_pattern(pat); } /* ------------------------------------------------------------------------ */ /* * notify of deletion */ void CVmObjPattern::notify_delete(VMG_ int in_root_set) { /* free my extension data */ if (ext_ != 0) { /* * Free my pattern, if I've compiled it. (Note that we must not * call get_pattern() here, because doing so would unnecessarily * create a pattern if we haven't already done so - that would be * stupid, because the only reason we're asking for it is so that * we can delete it.) */ if (get_ext()->pat != 0) CRegexParser::free_pattern(get_ext()->pat); /* free the extension */ if (!in_root_set) G_mem->get_var_heap()->free_mem(ext_); } } /* ------------------------------------------------------------------------ */ /* * Create from the stack */ vm_obj_id_t CVmObjPattern::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { const char *strval; re_status_t stat; re_compiled_pattern *pat; vm_obj_id_t id; /* check arguments */ if (argc != 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* retrieve the string, but leave it on the stack */ strval = G_stk->get(0)->get_as_string(vmg0_); if (strval == 0) err_throw(VMERR_STRING_VAL_REQD); /* compile the string */ stat = G_bif_tads_globals->rex_parser->compile_pattern( strval + VMB_LEN, vmb_get_len(strval), &pat); /* if we failed to compile the pattern, throw an error */ if (stat != RE_STATUS_SUCCESS) err_throw(VMERR_BAD_TYPE_BIF); /* create a new pattern object to hold the pattern */ id = vm_new_id(vmg_ FALSE, TRUE, FALSE); new (vmg_ id) CVmObjPattern(vmg_ pat, G_stk->get(0)); /* discard arguments */ G_stk->discard(); /* return the new object */ return id; } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjPattern::set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { /* we have no properties to set */ err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * Get a property */ int CVmObjPattern::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the function, if we found it */ if ((this->*func_table_[func_idx])(vmg_ self, val, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* not found - inherit default handling */ return CVmObject::get_prop(vmg_ prop, val, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * Mark references */ void CVmObjPattern::mark_refs(VMG_ uint state) { const vm_val_t *valp; /* if our source value is an object reference, mark it */ if (get_ext() != 0 && (valp = get_orig_str())->typ == VM_OBJ && valp->val.obj != VM_INVALID_OBJ) { /* it's a reference, so mark it */ G_obj_table->mark_all_refs(valp->val.obj, state); } } /* ------------------------------------------------------------------------ */ /* * Load from an image file */ void CVmObjPattern::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t len) { /* if we don't already have an extension, allocate one */ if (ext_ == 0) ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(sizeof(vmobj_pat_ext), this); /* get our source value */ vmb_get_dh(ptr, &get_ext()->str); /* * We haven't compiled our pattern yet. Note that it might not be * possible to obtain the text of our string at this point, because it * might be another object and thus might not have been loaded yet. * So, note that we have no pattern yet, and request post-load * initialization, so that we can compile our pattern after we know all * of the other objects have been loaded. */ set_pattern(0); G_obj_table->request_post_load_init(self); } /* ------------------------------------------------------------------------ */ /* * Perform post-load initialization: we compile our pattern here. Note * that we need to wait until now to compile our pattern, since our source * string could be another object, which isn't guaranteed to have been * loaded until we get here. */ void CVmObjPattern::post_load_init(VMG_ vm_obj_id_t self) { /* make sure the original string object is initialized */ const vm_val_t *origval = get_orig_str(); if (origval->typ == VM_OBJ) G_obj_table->ensure_post_load_init(vmg_ origval->val.obj); /* get the string value */ const char *strval = get_orig_str()->get_as_string(vmg0_); if (strval != 0) { /* if we already have a compiled pattern, delete it */ if (get_ext()->pat != 0) CRegexParser::free_pattern(get_ext()->pat); /* compile the pattern and store the result */ G_bif_tads_globals->rex_parser->compile_pattern( strval + VMB_LEN, vmb_get_len(strval), &get_ext()->pat); } } /* ------------------------------------------------------------------------ */ /* * save to a file */ void CVmObjPattern::save_to_file(VMG_ class CVmFile *fp) { /* write the source string reference */ char buf[VMB_DATAHOLDER]; vmb_put_dh(buf, get_orig_str()); fp->write_bytes(buf, VMB_DATAHOLDER); } /* ------------------------------------------------------------------------ */ /* * restore from a file */ void CVmObjPattern::restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, CVmObjFixup *fixups) { /* if we don't already have an extension, allocate one */ if (ext_ == 0) ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(sizeof(vmobj_pat_ext), this); /* read the source string reference */ char buf[VMB_DATAHOLDER]; fp->read_bytes(buf, VMB_DATAHOLDER); /* fix it up */ fixups->fix_dh(vmg_ buf); /* remember it in our extension */ vmb_get_dh(buf, &get_ext()->str); /* * clear out our pattern and request post-load initialization - we * can't necessarily compile it yet, because we might not have loaded * the source string data, so just make a note that we need to compile * it the next time we need it */ set_pattern(0); G_obj_table->request_post_load_init(self); } /* ------------------------------------------------------------------------ */ /* * property evaluator - get my original string */ int CVmObjPattern::getp_get_str(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* retrieve my original string value */ *retval = *get_orig_str(); /* handled */ return TRUE; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmpat.h���������������������������������������������������������������������������0000664�0000000�0000000�00000016265�12650170723�0015170�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmpat.h - regular-expression compiled pattern object Function Encapsulates a compiled regular expression as an object. This object allows a compiled regular expression to be saved for re-use; since regex compilation is time-consuming, it's much more efficient to re-use a compiled pattern in repeated searches than to recompile the expression each time it's needed. Notes Modified 08/27/02 MJRoberts - Creation */ #ifndef VMPAT_H #define VMPAT_H #include <stdlib.h> #include <os.h> #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" #include "vmregex.h" /* ------------------------------------------------------------------------ */ /* * Our serialized data stream, in both the image file and a saved file, * consists of: * * DATAHOLDER src_val * * 'src_val' is the source value - this is the string that was compiled to * create the pattern. */ /* ------------------------------------------------------------------------ */ /* * Our in-memory extension consists of a simple structure with a pointer * to the compiled pattern data (the re_compiled_pattern structure) and * the original string value that was used to create the pattern (we hold * onto the original string mostly for debugging purposes). */ struct vmobj_pat_ext { /* the compiled pattern data */ re_compiled_pattern *pat; /* the original pattern source string */ vm_val_t str; }; /* ------------------------------------------------------------------------ */ /* * Pattern intrinsic class */ class CVmObjPattern: public CVmObject { friend class CVmMetaclassPattern; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is this a pattern object? */ static int is_pattern_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* defer to our base class */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* cast to string - return the original pattern string */ const char *cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const { /* return the original string value */ *new_str = *get_orig_str(); return new_str->get_as_string(vmg0_); } /* undo operations - we are immutable and hence keep no undo */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { } void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* mark references */ void mark_refs(VMG_ uint); /* remove stale weak references - we have no weak references */ void remove_stale_weak_refs(VMG0_) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t); /* perform post-load initialization */ void post_load_init(VMG_ vm_obj_id_t self); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixup); /* get my compiled pattern */ re_compiled_pattern *get_pattern(VMG0_) { return get_ext()->pat; } /* get my string object */ const vm_val_t *get_orig_str() const { return &get_ext()->str; } protected: /* create with no extension */ CVmObjPattern() { ext_ = 0; } /* create with a given pattern object and source string value */ CVmObjPattern(VMG_ re_compiled_pattern *pat, const vm_val_t *src_str); /* set my compiled pattern data structure */ void set_pattern(re_compiled_pattern *pat) { get_ext()->pat = pat; } /* set my original source string value */ void set_orig_str(const vm_val_t *val) { get_ext()->str = *val; } /* get my extension data */ vmobj_pat_ext *get_ext() const { return (vmobj_pat_ext *)ext_; } /* property evaluator - undefined property */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - get my original string */ int getp_get_str(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc); /* property evaluation function table */ static int (CVmObjPattern::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassPattern: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "regex-pattern/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjPattern(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjPattern(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjPattern::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjPattern::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMPAT_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjPattern) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmpool.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000016642�12650170723�0015707�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmpool.cpp - constant pool implementation Function Notes Modified 10/20/98 MJRoberts - Creation */ #include <stdlib.h> #include <memory.h> #include "t3std.h" #include "vmpool.h" /* ------------------------------------------------------------------------ */ /* * Basic pool implementation */ /* * Get the number of pages in the pool */ size_t CVmPool::get_page_count() const { /* get the page count from the backing store */ return backing_store_->vmpbs_get_page_count(); } /* * Attach to a backing store */ void CVmPool::attach_backing_store(CVmPoolBackingStore *backing_store) { /* remember the backing store */ backing_store_ = backing_store; /* get the page size from the backing store */ page_size_ = backing_store->vmpbs_get_common_page_size(); } /* ------------------------------------------------------------------------ */ /* * Base paged pool implementation */ /* * delete our page list */ void CVmPoolPaged::delete_page_list() { /* if there's a page list, delete it */ if (pages_ != 0) { /* free the page array */ t3free(pages_); /* forget it */ pages_ = 0; page_slots_ = 0; page_slots_max_ = 0; } /* we can no longer have a backing store */ backing_store_ = 0; } /* * Attach to a backing store */ void CVmPoolPaged::attach_backing_store(CVmPoolBackingStore *backing_store) { size_t cur; size_t log2; /* delete any existing page list */ delete_page_list(); /* inherit default handling */ CVmPool::attach_backing_store(backing_store); /* * if the page size is zero, there must not be any pages at all - * use a dummy default page size */ if (page_size_ == 0) page_size_ = 1024; /* * Compute log2 of the page size. If the page size isn't a power of * two, throw an error. */ for (cur = page_size_, log2 = 0 ; (cur & 1) == 0 ; cur >>= 1, ++log2) ; if (cur != 1) err_throw(VMERR_BAD_POOL_PAGE_SIZE); /* store log2(page_size_) in log2_page_size_ */ log2_page_size_ = log2; /* allocate the pages */ alloc_page_slots(backing_store_->vmpbs_get_page_count()); } /* * Allocate a page slot */ void CVmPoolPaged::alloc_page_slots(size_t nslots) { size_t old_slots; size_t i; /* note the old slot count */ old_slots = page_slots_; /* if the new size isn't bigger than the old size, ignore the request */ if (nslots <= page_slots_) return; /* if necessary, expand the master page array */ if (nslots > page_slots_max_) { size_t siz; /* * Increase the maximum, leaving some room for dynamically added * pages. */ page_slots_max_ = nslots + 10; /* calculate the new allocation size */ siz = page_slots_max_ * sizeof(pages_[0]); /* allocate or reallocate at the new size */ if (pages_ == 0) pages_ = (CVmPool_pg *)t3malloc(siz); else pages_ = (CVmPool_pg *)t3realloc(pages_, siz); } /* set the new size */ page_slots_ = nslots; /* clear the new subarrays */ for (i = old_slots ; i < page_slots_ ; ++i) pages_[i].mem = 0; } /* ------------------------------------------------------------------------ */ /* * Two-level paged pool implementation. This is a variation of the regular * paged pool that uses a two-level page table. This implementation is not * currently used, because it is less efficient than the single-page pool * and is not needed for modern machines with large contiguous address * spaces; however, we retain it in the event it's needed for 16-bit * segmented architectures, where it might not be possible or convenient to * allocate a sufficiently large master page table and thus a two-level * table is needed. */ #if 0 /* * delete the pool - deletes all allocated pages */ CVmPoolPaged2::~CVmPoolPaged2() { /* free our page memory */ delete_page_list(); } /* * delete our page list */ void CVmPoolPaged2::delete_page_list() { /* if there's a page list, delete it */ if (pages_ != 0) { size_t i; size_t cnt; /* free each subarray */ cnt = get_subarray_count(); for (i = 0 ; i < cnt ; ++i) t3free(pages_[i]); /* free the master array */ t3free(pages_); /* forget about the page list */ pages_ = 0; } } /* * Attach to a backing store */ void CVmPoolPaged2::attach_backing_store(CVmPoolBackingStore *backing_store) { size_t cur; size_t log2; /* delete any existing page list */ delete_page_list(); /* remember the backing store */ backing_store_ = backing_store; /* get the page size from the backing store */ page_size_ = backing_store_->vmpbs_get_common_page_size(); /* * if the page size is zero, there must not be any pages at all - use a * dummy default page size */ if (page_size_ == 0) page_size_ = 1024; /* * Compute log2 of the page size. If the page size isn't a power of * two, throw an error. */ for (cur = page_size_, log2 = 0 ; (cur & 1) == 0 ; cur >>= 1, ++log2) ; if (cur != 1) err_throw(VMERR_BAD_POOL_PAGE_SIZE); /* store log2(page_size_) in log2_page_size_ */ log2_page_size_ = log2; /* allocate the pages */ alloc_page_slots(backing_store_->vmpbs_get_page_count()); } /* * Allocate a page slot */ void CVmPoolPaged2::alloc_page_slots(size_t nslots) { size_t old_slots; size_t old_sub_cnt; size_t new_sub_cnt; /* note the old slot count */ old_slots = page_slots_; /* if the new size isn't bigger than the old size, ignore the request */ if (nslots <= page_slots_) return; /* note the original subarray count */ old_sub_cnt = get_subarray_count(); /* set the new size */ page_slots_ = nslots; /* note the new subarray count */ new_sub_cnt = get_subarray_count(); /* allocate or expand the master array */ if (new_sub_cnt > old_sub_cnt) { size_t siz; size_t i; /* figure the new size */ siz = new_sub_cnt * sizeof(pages_[0]); /* allocate or re-allocate the master array */ if (pages_ == 0) pages_ = (CVmPool_pg **)t3malloc(siz); else pages_ = (CVmPool_pg **)t3realloc(pages_, siz); /* throw an error if that failed */ if (pages_ == 0) err_throw(VMERR_OUT_OF_MEMORY); /* clear the new slots */ memset(pages_ + old_sub_cnt, 0, (new_sub_cnt - old_sub_cnt) * sizeof(pages_[0])); /* allocate the subarrays */ for (i = old_sub_cnt ; i < new_sub_cnt ; ++i) { /* allocate this subarray */ pages_[i] = (CVmPool_pg *)t3malloc(VMPOOL_SUBARRAY_SIZE * sizeof(pages_[i][0])); /* make sure we got the space */ if (pages_[i] == 0) err_throw(VMERR_OUT_OF_MEMORY); /* clear the page */ memset(pages_[i], 0, VMPOOL_SUBARRAY_SIZE * sizeof(pages_[i][0])); } } } #endif /* removed 2-paged pool */ ����������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmpool.h��������������������������������������������������������������������������0000664�0000000�0000000�00000047340�12650170723�0015353�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmpool.h - VM constant pool manager Function Notes Modified 10/20/98 MJRoberts - Creation */ #ifndef VMPOOL_H #define VMPOOL_H #include <stdlib.h> #include <memory.h> #include "vmtype.h" /* include the pool selection mechanism */ #include "vmpoolsl.h" /* ------------------------------------------------------------------------ */ /* * Constant pool page information. This structure tracks memory for one * page. */ struct CVmPool_pg { /* memory containing the data in this page */ const char *mem; /* actual size of the page data */ size_t siz; }; /* ------------------------------------------------------------------------ */ /* * Constant Pool Backing Store Interface. This is an abstract interface * that pool clients must implement to provide the pool with the means * of loading pages. * * Note that the backing store, like the pool itself, is considered * read-only by the pool manager. The pool manager never needs to write * data to the backing store, and expects the backing store to remain * constant throughout the existence of the pool (hence the pool never * needs to reload any data from the backing store that it already has * in cache). */ class CVmPoolBackingStore { public: /* * since this class is abstract, make sure all subclasses have virtual * destructors */ virtual ~CVmPoolBackingStore() { } /* * Determine the total number of pages that are available to be * loaded. Implementations of the pool manager that pre-load all * pages use this function to determine how many pages are available * for loading. */ virtual size_t vmpbs_get_page_count() = 0; /* * Get the common page size in the underying store. Individual * pages may not use the entire page size, but no page may be larger * than the common size. */ virtual size_t vmpbs_get_common_page_size() = 0; /* * Given a starting offset and a page size, calculate how much space is * actually needed for the page at the offset. This is provided to * allow for partial pages, which don't need the full page size * allocated. Simple implementations can simply always return the full * page size. */ virtual size_t vmpbs_get_page_size(pool_ofs_t ofs, size_t page_size) = 0; /* * Given a starting offset, allocate space for the given page and * load it into memory. page_size is the normal page size in bytes, * and load_size is the actual number of bytes to be allocated and * loaded (this will be the value previously returned by * vmpbs_get_page_size for the page). * * This should throw an exception if an error occurs. */ virtual const char *vmpbs_alloc_and_load_page(pool_ofs_t ofs, size_t page_size, size_t load_size) = 0; /* * Delete memory allocated by vmpbs_alloc_and_load_page(). The pool * will call this for each page previously allocated. 'page_size' * is the normal page size in bytes for the entire pool. */ virtual void vmpbs_free_page(const char *mem, pool_ofs_t ofs, size_t page_size) = 0; /* * Given a starting offset, load the page into the given memory, * which is allocated and managed by the caller. page_size is the * normal page size in bytes, and load_size is the actual number of * bytes to be loaded (this will be the value previously returned by * vmpbs_get_page_size for the page). * * This should throw an exception if an error occurs. */ virtual void vmpbs_load_page(pool_ofs_t ofs, size_t page_size, size_t load_size, char *mem) = 0; /* * Determine if my pages are writable. Returns true if so, false if * not. If the pool pages are directly mapped to the underlying * data file, this should return false. For example, an * implementation for a palm-top computer without an external * storage device might simply store the image file directly in * memory, and the backing store would map directly onto this memory * so that the original copy of the image file in memory can be used * as the loaded version as well. In such cases, the backing store * should certainly not be writable. For an implementation that * copies data from an external storage device (typically a hard * disk), writing to the backing store copy would cause no change to * the original image file data, hence this can return true in such * cases. */ virtual int vmpbs_is_writable() { return FALSE; } }; /* ------------------------------------------------------------------------ */ /* * Base constant memory pool class */ class CVmPool { public: CVmPool() { } virtual ~CVmPool() { } /* * Attach to the given backing store to provide the the page data. */ virtual void attach_backing_store(class CVmPoolBackingStore *backing_store) = 0; /* * Detach from backing store - this must be called before the backing * store object can be deleted. */ virtual void detach_backing_store() { backing_store_ = 0; } /* * Translate an address from a pool offset to a physical location. * Note that translating an address may invalidate a previously * translated address in a swapping implementation of the pool manager, * so callers should take care to assume only one translated address in * a given pool is valid at a time. * * Because this routine is called extremely frequently, we don't make * it a virtual. Instead, we depend upon the final subclass to define * the method as a non-virtual, so that it can be in-lined. This means * that pool object references must all be declared with the final * subclass. */ /* virtual const char *get_ptr(pool_ofs_t ofs) = 0; */ /* * Given a pointer into physical memory, get the pool offset. Returns * true if the pointer is a valid pointer into the pool, false if not. */ /* virtual int get_ofs(const char *p, pool_ofs_t *ofs) = 0; */ /* * Get the page size. This reflects the size of the pages in the * backing store (usually the image file); this doesn't necessarily * indicate anything about the way we manage the pool memory. */ size_t get_page_size() const { return page_size_; } /* get the number of pages in the pool */ size_t get_page_count() const; /* validate an offset */ /* virtual int validate_ofs(pool_ofs_t ofs) = 0; */ protected: /* * page size in bytes - this is simply the number of bytes on each page * (each page in the pool has the same number of bytes) */ size_t page_size_; /* backing store */ class CVmPoolBackingStore *backing_store_; }; /* ------------------------------------------------------------------------ */ /* * "Flat" memory pool. This type of pool loads all pages into a single * contiguous chunk of memory. This is suitable for platforms with large * linear memory spaces, such as 32-bit platforms. */ class CVmPoolFlat: public CVmPool { public: CVmPoolFlat() { /* we don't have our memory chunk yet */ mem_ = 0; } ~CVmPoolFlat(); /* terminate - we don't need to do anything here */ void terminate() { } /* attach to the backing store - loads all pages */ void attach_backing_store(class CVmPoolBackingStore *backing_store); /* detach from the backing store */ void detach_backing_store(); /* * Translate an address. Since all of our memory is in one large * contiguous chunk, this is extremely simple: just return the base of * our memory block, offset by the pool offset. */ inline const char *get_ptr(pool_ofs_t ofs) { return mem_ + ofs; } /* validate an address */ inline int validate_ofs(pool_ofs_t ofs) { return (ofs > 0 && ofs < (pool_ofs_t)siz_); } /* get the pool offset given a pointer */ inline int get_ofs(const char *p, pool_ofs_t *ofs) { /* if it's in our memory block, it's a valid pointer */ if (p >= mem_ && p < mem_ + siz_) { *ofs = p - mem_; return TRUE; } else return FALSE; } /* our single contiguous allocation block */ char *mem_; /* total size of the memory block */ long siz_; }; /* ------------------------------------------------------------------------ */ /* * Paged constant pool. * * This type of pool is divided into pages. A given object must be * entirely contained in a single page. * * Each object is referenced by its address in the constant pool. An * object address is simply an offset into the pool. */ class CVmPoolPaged: public CVmPool { public: /* create the pool */ CVmPoolPaged() { /* no page slots allocated yet */ pages_ = 0; page_slots_ = 0; page_slots_max_ = 0; /* we don't have a backing store yet */ backing_store_ = 0; /* we don't know the page size yet */ page_size_ = 0; log2_page_size_ = 0; } /* * Delete the pool. Call our non-virtual termination routine, as we * can't use virtuals in destructors (not in the normal fashion, * anyway). */ virtual ~CVmPoolPaged() { terminate_nv(); } /* delete everything in the pool using our base terminator routine */ virtual void terminate() { terminate_nv(); } /* * Attach to the given backing store to provide the the page data. */ virtual void attach_backing_store(class CVmPoolBackingStore *backing_store); protected: /* non-virtual termination routine */ void terminate_nv() { /* free our page memory */ delete_page_list(); } /* delete our page list, if any */ void delete_page_list(); /* allocate or expand the page slot list */ void alloc_page_slots(size_t slots); /* * Calculate which page we need, and the offset within the page, for * a given pool offset. The page is the offset divided by the page * size; since the page size is a power of two, this is simply a bit * shift by log2(page_size). The page offset is the remainder after * dividing the offset by the page size; again because the page size * is a power of two, we can calculate this remainder simply by * AND'ing the offset with the page size minus one. (Using these * shift and mask operations may seem a little obscure, but it's so * much faster on most machines than integer division that we're * willing to be a little obscure in exchange for the performance * gain.) */ inline size_t get_page_for_ofs(pool_ofs_t ofs) const { return (size_t)(ofs >> log2_page_size_); } inline size_t get_ofs_for_ofs(pool_ofs_t ofs) const { return (size_t)(ofs & (page_size_ - 1)); } /* get the starting offset on the given page */ pool_ofs_t get_page_start_ofs(size_t pg) const { return ((pool_ofs_t)pg) << log2_page_size_; } /* * The page list. This is an array of CVmPool_pg structures; each * structure keeps track of one page in the pool. * * The page identified by the first page information structure contains * pool offsets 0 through (page_size - 1); the next contains offsets * page_size through (2*page_size - 1); and so on. */ CVmPool_pg *pages_; /* * The number of page slots in the page list. This starts at the * initial page size and can grow dynamically as more pages are added. */ size_t page_slots_; /* * The maximum of allocated pages_ array entries. This might be larger * than page_slots_, because we sometimes allocate more slots than we * currently need to avoid having to allocate on every new page * addition. */ size_t page_slots_max_; /* log2 of the page size */ int log2_page_size_; }; /* ------------------------------------------------------------------------ */ /* * Two-tiered paged pool. This is similar to the paged pool * implementation, but uses a two-level page table: the first-level page * table containers pointers to the second-level tables, and the * second-level tables contain the pointers to the actual pages. * * This class is not currently used, because the two-level scheme isn't * required in practice for modern machines and is less efficient than the * single-level page table implemented in CVmPoolPaged. We retain this * two-level code in case it's ever needed, though, because the two-level * scheme might be useful for 16-bit segmented architectures. * * The advantage of the two-level scheme is that it allows very large * memory spaces to be addressable without any single large allocations; * the single-tier paged pool requires a single allocation equal to the * total aggregate memory size divided by the page size times the size of a * page pointer, which could be a fairly large single allocation for an * extremely large aggregate pool size. However, it doesn't currently * appear that the single-tier paging scheme will impose any limits that * will be encountered in actual practice. */ #if 0 /* number of page information structures in each subarray */ const size_t VMPOOL_SUBARRAY_SIZE = 4096; class CVmPoolPaged2 { public: /* create the pool */ CVmPoolPaged2() { /* no page slots allocated yet */ pages_ = 0; page_slots_ = 0; /* we don't have a backing store yet */ backing_store_ = 0; /* we don't know the page size yet */ page_size_ = 0; log2_page_size_ = 0; } /* delete the pool */ virtual ~CVmPoolPaged2(); /* * Attach to the given backing store to provide the the page data. */ virtual void attach_backing_store(class CVmPoolBackingStore *backing_store); protected: /* delete our page list, if any */ void delete_page_list(); /* allocate or expand the page slot list */ void alloc_page_slots(size_t slots); /* * Calculate which page we need, and the offset within the page, for a * given pool offset. The page is the offset divided by the page size; * since the page size is a power of two, this is simply a bit shift by * log2(page_size). The page offset is the remainder after dividing * the offset by the page size; again because the page size is a power * of two, we can calculate this remainder simply by AND'ing the offset * with the page size minus one. (Using these shift and mask * operations may seem a little obscure, but it's so much faster on * most machines than integer division that we're willing to be a * little obscure in exchange for the performance gain.) */ inline size_t get_page_for_ofs(pool_ofs_t ofs) const { return (size_t)(ofs >> log2_page_size_); } inline size_t get_ofs_for_ofs(pool_ofs_t ofs) const { return (size_t)(ofs & (page_size_ - 1)); } /* get the starting offset on the given page */ pool_ofs_t get_page_start_ofs(size_t pg) const { return ((pool_ofs_t)pg) << log2_page_size_; } /* get the number of subarrays */ size_t get_subarray_count() const { return ((page_slots_ + VMPOOL_SUBARRAY_SIZE - 1) / VMPOOL_SUBARRAY_SIZE); } /* get the page information structure for a given index */ inline CVmPool_pg *get_page_info(size_t idx) const { return &(pages_[idx / VMPOOL_SUBARRAY_SIZE] [idx % VMPOOL_SUBARRAY_SIZE]); } /* * The page list. Each entry in this array is a subarray containing * VMPOOL_SUBARRAY_SIZE page information structures, each of contains * information on a page. Conceptually, the two-tiered array forms a * single array; we keep two levels of arrays to accommodate 16-bit * machines where a single large could be too large for a single 64k * segment. * * The page identified by the first page information structure contains * pool offsets 0 through (page_size - 1); the next contains offsets * page_size through (2*page_size - 1); and so on. */ CVmPool_pg **pages_; /* * The number of slots allocated in the page list. This starts at * the initial page size and can grow dynamically as more pages are * added. */ size_t page_slots_; /* log2 of the page size */ int log2_page_size_; }; #endif /* 0 */ /* ------------------------------------------------------------------------ */ /* * In-memory pool implementation. This pool implementation pre-loads * all available pages in the pool and keeps the complete pool in memory * at all times. */ class CVmPoolInMem: public CVmPoolPaged { public: CVmPoolInMem() { } /* * delete - call our non-virtual terminator (use the non-virtual * version, as this will just do our local termination; since we'll * implicitly inherit the base class destructor, we don't want to * explicitly inherit its termination as well) */ ~CVmPoolInMem() { terminate_nv(); } /* terminate */ void terminate() { /* call our own non-virtual termination routine */ terminate_nv(); /* inherit our base class handling */ CVmPoolPaged::terminate(); } /* attach to the backing store - loads all pages */ void attach_backing_store(class CVmPoolBackingStore *backing_store); /* detach from the backing store */ void detach_backing_store(); /* * translate an address - since the pool is always in memory, we can * translate an address simply by doing the arithmetic and finding * the needed page, which is always loaded */ inline const char *get_ptr(pool_ofs_t ofs) { /* translate the address through our page array */ return (pages_[get_page_for_ofs(ofs)].mem + get_ofs_for_ofs(ofs)); } /* validate an offset value */ inline int validate_ofs(pool_ofs_t ofs) { /* get the page and the offset in the page */ size_t pg = get_page_for_ofs(ofs); size_t pgofs = get_ofs_for_ofs(ofs); /* * to be valid, it must be within the range of valid pages, the * page must be allocated, and the offset in the page must be * within the page's actual allocated size */ return (pg < page_slots_ && pages_[pg].mem != 0 && pgofs < pages_[pg].siz); } /* get the pool offset given a pointer */ inline int get_ofs(const char *p, pool_ofs_t *ofs) { /* check each page */ pool_ofs_t page_ofs = 0; for (size_t i = 0 ; i < page_slots_ ; ++i, page_ofs += page_size_) { /* if it's in this page, it's a valid address */ const char *mem = pages_[i].mem; if (p >= mem && p < mem + pages_[i].siz) { *ofs = (p - mem) + page_ofs; return TRUE; } } /* didn't find it */ return FALSE; } private: /* non-virtual termination */ void terminate_nv(); /* free any pages we allocated from the backing store */ void free_backing_pages(); }; #endif /* VMPOOL_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmpoolfl.cpp����������������������������������������������������������������������0000664�0000000�0000000�00000003727�12650170723�0016231�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmpoolfl.cpp - "flat" memory pool Function Implements the flat memory pool, which allocates the entire pool's memory in a single contiguous chunk. Notes Modified 09/18/02 MJRoberts - Creation */ #include <stdlib.h> #include <memory.h> #include "t3std.h" #include "vmpool.h" /* * delete */ CVmPoolFlat::~CVmPoolFlat() { /* delete our memory block */ if (mem_ != 0) t3free(mem_); } /* * Attach to the backing store */ void CVmPoolFlat::attach_backing_store(CVmPoolBackingStore *backing_store) { size_t i; char *dst; size_t pg_cnt; pool_ofs_t ofs; /* inherit the base implementation */ CVmPool::attach_backing_store(backing_store); /* delete any existing memory */ if (mem_ != 0) t3free(mem_); /* * Allocate our memory. We need to allocate a single chunk large * enough for all of the pages. */ pg_cnt = backing_store_->vmpbs_get_page_count(); siz_ = page_size_ * pg_cnt; mem_ = (char *)t3malloc(siz_); if (mem_ == 0) err_throw(VMERR_OUT_OF_MEMORY); /* load all of the pages */ for (i = 0, ofs = 0, dst = mem_ ; i < pg_cnt ; ++i, dst += page_size_, ofs += page_size_) { size_t cur_pg_siz; /* get the load size for this page */ cur_pg_siz = backing_store_->vmpbs_get_page_size(ofs, page_size_); /* load this page from the backing store directly into our memory */ backing_store_->vmpbs_load_page(ofs, page_size_, cur_pg_siz, dst); } } /* * detach from the backing store */ void CVmPoolFlat::detach_backing_store() { /* free our memory */ if (mem_ != 0) { t3free(mem_); mem_ = 0; } /* inherit default */ CVmPool::detach_backing_store(); } �����������������������������������������qtads-2.1.7/tads3/vmpoolsl.h������������������������������������������������������������������������0000664�0000000�0000000�00000005171�12650170723�0015706�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmpoolsl.h - pool manager selector definitions Function This header makes some definitions that vary according to which type of memory pool manager is selected. This can be included without including the entire memory pool subsystem header. Notes As of this writing, only the all-in-memory pool manager is supported. The swapping pool manager is no longer supported and is not planned to be supported in the future. However, we are retaining the selection mechanism for now, in the event that we want to restore the swapping pool, or add other pool configuration options, in the future (which seems unlikely, but still). Modified 08/17/02 MJRoberts - Creation */ #ifndef VMPOOLSL_H #define VMPOOLSL_H /* ------------------------------------------------------------------------ */ /* * Conditionally include code needed for the swapping pool manager. When * the swapping pool manager is used, code that translates memory addresses * must be mindful of the fact that translating one memory address can * render previously-translated addresses invalid. Such code is * unnecessary with non-swapping pool managers. * * To include the swapping pool manager in the build, you must #define * VM_SWAPPING_POOL globally for all modules - normally, this should be * done in the CFLAGS in the makefile, or with the equivalent local * convention, so that every module has the symbol defined. */ /* ------------------------------------------------------------------------ */ #ifdef VM_SWAPPING_POOL /* * swapping mode - include swapping-specific code */ #define VM_IF_SWAPPING_POOL(x) x /* the final pool manager subclass - use the swapping pool class */ #define CVmPool_CLASS CVmPoolSwap /* ------------------------------------------------------------------------ */ #else /* VM_SWAPPING_POOL */ /* * non-swapping mode - omit swapping-specific code */ #define VM_IF_SWAPPING_POOL(x) /* * The non-swapping pool comes in two varieties. Select the FLAT or PAGED * pool, as desired. The FLAT pool is slightly faster, but it doesn't have * any dynamic memory capabilities, which are required for the debugger. */ #ifdef VM_FLAT_POOL /* select the non-swapped FLAT pool */ #define CVmPool_CLASS CVmPoolFlat #else /* VM_FLAT_POOL */ /* select the non-swapped PAGED pool */ #define CVmPool_CLASS CVmPoolInMem #endif /* VM_FLAT_POOL */ #endif /* VM_SWAPPING_POOL */ #endif /* VMPOOLSL_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmpredef.h������������������������������������������������������������������������0000664�0000000�0000000�00000004712�12650170723�0015643�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMPREDEF.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmpredef.h - pre-defined objects and properties Function Defines a global structure containing the pre-defined objects and properties. These values are provided by the load image file's predefined value symbol table; at load time, we cache these values for quick access. Notes Modified 12/09/98 MJRoberts - Creation */ #ifndef VMPREDEF_H #define VMPREDEF_H #include "t3std.h" #include "vmtype.h" /* * some special import names - these are exports that we might need to * synthesize, so we need to know their names in the image loader code as * well as in the imports table */ #define VM_IMPORT_NAME_LASTPROPOBJ "*LastPropObj" #define VM_IMPORT_NAME_RTERRMSG "exceptionMessage" #define VM_IMPORT_NAME_CONSTSTR "*ConstStrObj" #define VM_IMPORT_NAME_CONSTLST "*ConstLstObj" #define VM_IMPORT_NAME_ISARGTABLE "_isNamedArgTable" /* * pre-defined values */ struct CVmPredef { /* initialize */ CVmPredef() { /* * the pre-defined variables are all undefined initially */ reset(); } /* * Reset the predef variables to their initial undefined values */ void reset() { /* * include the import list to generate initializations for the * predef variables */ #define VM_IMPORT_OBJ(sym, mem) mem = VM_INVALID_OBJ; #define VM_NOIMPORT_OBJ(sym, mem) VM_IMPORT_OBJ(sym, mem) #define VM_IMPORT_PROP(sym, mem) mem = VM_INVALID_PROP; #define VM_NOIMPORT_PROP(sym, mem) VM_IMPORT_PROP(sym, mem) #define VM_IMPORT_FUNC(sym, mem) mem = 0; #include "vmimport.h" } /* * Now include the import list to generate the actual member variables * for the imported symbols. During image file load, the loader will * set these members to the actual values imported from the image * file. */ #define VM_IMPORT_OBJ(sym, mem) vm_obj_id_t mem; #define VM_NOIMPORT_OBJ(sym, mem) VM_IMPORT_OBJ(sym, mem) #define VM_IMPORT_PROP(sym, mem) vm_prop_id_t mem; #define VM_NOIMPORT_PROP(sym, mem) VM_IMPORT_PROP(sym, mem) #define VM_IMPORT_FUNC(sym, mem) pool_ofs_t mem; #include "vmimport.h" }; #endif /* VMPREDEF_H */ ������������������������������������������������������qtads-2.1.7/tads3/vmprof.h��������������������������������������������������������������������������0000664�0000000�0000000�00000012021�12650170723�0015334�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmprof.h - T3 VM Profiler Function Definitions for the execution profiler, which captures information on the time the program spends in each function. Profiling information is useful for performance optimization, because it identifies the areas where the program is spending all of its time. Profiling can be optionally included when the VM is compiled. Builds of the VM intended for use by end users should normally not include the profiler, because they will have no use for profiling, and including the profiler in the build enlarges the code size and adds run-time overhead. Normally, the profiler should be included only in builds that include the interactive debugger. Notes Modified 08/03/02 MJRoberts - Creation */ #ifndef VMPROF_H #define VMPROF_H #include "vmtype.h" #include "vmprofty.h" /* ------------------------------------------------------------------------ */ /* * Enable the profiler when compiling the VM by #defining VM_PROFILER on * the compiler command line (usually done through the makefile's CFLAGS * compiler options variable or equivalent). When VM_PROFILE is not * defined at compile-time, the profiling code will be omitted, making for * a smaller executable and less run-time overhead. */ #ifdef VM_PROFILER /* include profiler-only code, and do not include non-profiler-only code */ #define VM_IF_PROFILER(x) x #define VM_IF_NOT_PROFILER(x) #else /* VM_PROFILER */ /* do not include profiler code; do include non-profiler code */ #define VM_IF_PROFILER(x) #define VM_IF_NOT_PROFILER(x) x #endif /* VM_PROFILER */ /* ------------------------------------------------------------------------ */ /* * Profiler function call record. * * We keep a stack of these records alongside the regular VM stack, to * track the amount of time spent in a function. A record in this stack * corresponds to an activation frame in the regular VM stack. * * We also keep a master table of these entries indexed by obj.prop/func * identifier. These records keep the running totals for each function. */ struct vm_profiler_rec { /* the object.property of the function, for a method */ vm_obj_id_t obj; vm_prop_id_t prop; /* the entrypoint of the method */ const uchar *func; /* the cumulative time DIRECTLY in this function (not in children) */ vm_prof_time sum_direct; /* * the cumulative time in this function's children (but not directly in * the function itself) */ vm_prof_time sum_chi; /* * invocation count (this is used in the running total records only, * not in the stack) */ unsigned long call_cnt; }; /* ------------------------------------------------------------------------ */ /* * OS-specific profiler functions. These must be provided by the OS * implementation. */ /* * Get the current high-precision timer, filling in *t with the current * time. This indicates the time that has elapsed since an arbitrary zero * point, defined by the OS code. We only care about deltas from one time * to another, so the zero point doesn't matter to us, as long as all times * have a consistent zero time and can thus be subtracted to find elapsed * time values. The units are arbitrary; because this routine will be * called very frequently, it should be as fast as possible, and thus * should return the time in the native units the OS uses. */ void os_prof_curtime(vm_prof_time *t); /* * Convert the elapsed time value to microseconds and return it as an * unsigned long. This should simply multiply the time value by the number * of microseconds per OS-dependent unit that os_prof_curtime() returns, * and return the value as a 32-bit unsigned long. * * We use a separate routine to convert the OS time units to microseconds * so that we don't have to perform the conversion every time we note the * current time, which we must do very frequently; instead, we defer the * conversions until we're finished with a profiler run, at which point we * have a set of summary data that is typically much smaller than the raw * set we collect throughout execution. * * Note that our return precision of 32 bits can only represent time values * up to about 4000 seconds, or about 70 minutes. This is obviously not * enough for complete generality, but it's a fairly long time by profiler * standards, since the values we're measuring are the cumulative elapsed * time spent in particular functions. Given how much easier it is to work * with 32-bit values than anything longer, and given that 70 minutes is a * fairly generous ceiling for this particular application, we accept the * loss of generality in exchange for the simplifaction. */ unsigned long os_prof_time_to_ms(const vm_prof_time *t); #endif /* VMPROF_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmprofty.h������������������������������������������������������������������������0000664�0000000�0000000�00000002253�12650170723�0015717�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmprofty.h - basic type definitions for the profiler Function This defines some basic types for the profiler. We use a separate header to allow finer-grained inclusions. Notes Modified 08/03/02 MJRoberts - Creation */ #ifndef VMPROFTY_H #define VMPROFTY_H /* ------------------------------------------------------------------------ */ /* * Profiler time record. We use a 64-bit value for the time; this gives * the time as a delta from some arbitrary zero point, defined by the OS, * as a 64-bit quantity in units defined by the OS. * * We use a 64-bit value to allow for OS-provided timers with very high * precision. The OS doesn't necessarily have to use the full 64 bits; if * only 32-bit timer values are available, the OS code can simply set the * high-order part to zero. */ struct vm_prof_time { /* the high-order and low-order 32 bits of the time value */ unsigned long hi; unsigned long lo; }; #endif /* VMPROFTY_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmrefcnt.h������������������������������������������������������������������������0000664�0000000�0000000�00000013234�12650170723�0015656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmrefcnt.h - thread-safe reference-counted objects Function Defines classes for reference counting, including weak references. Notes This header needs to be "interleaved" with the osifcnet.h class definitions. The classes we define here depend on the OS-specific thread-safe counter object already being defined in-line, while other OS-specific classes depend on our classes. This header must thus be #included within the OS-specific networking header after defining the counter class but before defining any of the other classes. Modified 05/24/10 MJRoberts - Creation */ #ifndef VMREFCNT_H #define VMREFCNT_H /* ------------------------------------------------------------------------ */ /* * Reference-counted object. This is the base class for objects with * memory management by reference count. Construction sets the initial * reference count to 1, on behalf of the caller that performed the 'new' * to create the object. Callers must manually release references as they * go out of scope. When the reference count reaches zero, the object is * destroyed. */ class CVmRefCntObj { public: /* * on construction, set the counter to 1, to count the initial * reference on behalf of the caller that performed the 'new' */ CVmRefCntObj() : cnt(1) { } /* add a reference */ void add_ref() { cnt.inc(); } /* * release a reference; automatically deletes the object when the * reference count reaches zero */ void release_ref() { if (cnt.dec() == 0) delete this; } /* * debugging add/remove ref - print a console message showing where the * add/remove came from */ void add_ref(void *from, const char *msg) { add_ref(); printf("add_ref(this=%lx, cnt=%ld, from=%lx, %s)\n", (long)this, cnt.get(), (long)from, msg); } void release_ref(void *from, const char *msg) { printf("release_ref(this=%lx, cnt=%ld, from=%lx, %s)\n", (long)this, cnt.get()-1, (long)from, msg); release_ref(); } /* get the current reference count */ long get_ref_count() const { return cnt.get(); } protected: /* * the destructor must be virtual because this class is designed for * subclassing, and we invoke the destructor from this base class */ virtual ~CVmRefCntObj() { } /* reference counter */ OS_Counter cnt; }; /* ------------------------------------------------------------------------ */ /* * Weak reference. This is a reference to a WeakRefable object that * doesn't count in the reference counting for the referenced object. */ class CVmWeakRef: public CVmRefCntObj { friend class CVmWeakRefable; public: /* * Get the referenced object. If the object is still alive, this * creates a new strong reference to the object and returns the strong * reference. The caller is responsible for releasing the returned * strong reference when it's done with the reference. If the object * has already been deleted, this returns null. */ class CVmWeakRefable *ref(); /* * Test the reference to see if the object is still alive. Returns * true if the object is alive, false if not. * * If this returns true, there's of course no guarantee that the object * won't be deleted at any time. If this returns false, however, you * can be certain that it will return false (and ref() will return nil) * from now on, because it means the underlying object has been * deleted. test() is thus useful for cases where you only need to * know for sure that an object has been deleted, such as when * periodically cleaning up a cache containing weak references. When * you need to use the object after determining if it's alive, you * should use ref() instead, since that atomically tests to see if the * object is alive and creates a strong reference to it if it is. */ int test() { return ref_ != 0; } protected: CVmWeakRef(class CVmWeakRefable *r); virtual ~CVmWeakRef(); /* * Clear our reference. The referenced object calls this when it's * about to be deleted, to let us know that we can no longer access it. * * The referenced object only calls this after locking the mutex. */ void clear_ref() { ref_ = 0; } /* the object we weakly reference */ class CVmWeakRefable *ref_; /* the mutex we share with the referenced object */ class OS_Mutex *mu; }; /* * Weak-referenceable object. This is a specialized reference-counted * object that can have weak references in addition to regular strong * references. A weak reference is a reference that doesn't prevent the * object from being deleted, but still allows access as long as the object * is alive. */ class CVmWeakRefable: public CVmRefCntObj { friend class CVmWeakRef; public: CVmWeakRefable(); /* release a reference */ void release_ref(); /* * Get the weak reference to this object */ CVmWeakRef *get_weak_ref(); /* debugging version */ void release_ref(void *from, const char *msg) { printf("release_ref(this=%lx, cnt=%ld, from=%lx, %s)\n", (long)this, cnt.get()-1, (long)from, msg); release_ref(); } protected: virtual ~CVmWeakRefable(); /* mutex protecting against concurrent access */ class OS_Mutex *mu; /* our weak reference object */ CVmWeakRef *weakref; }; #endif /* VMREFCNT_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmregex.cpp�����������������������������������������������������������������������0000664�0000000�0000000�00000462214�12650170723�0016050�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 1998, 2012 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name regex.c - Regular Expression Parser and Recognizer for T3 Function Parses and recognizes regular expressions. Adapted to C++ and UTF-8 from the TADS 2 implementation. Notes Regular expression syntax: abc|def either abc or def (abc) abc abc+ abc, abcc, abccc, ... abc* ab, abc, abcc, ... abc? ab or abc x{n} x exactly 'n' times x{n,} x at least 'n' times x{,n} x 0 to 'n' times x{n,m} x from 'n' to 'm' times . any single character abc$ abc at the end of the line ^abc abc at the beginning of the line %^abc literally ^abc [abcx-z] matches a, b, c, x, y, or z [^abcx-z] matches any character except a, b, c, x, y, or z [^]-q] matches any character except ], -, or q x+? use *shortest* working match to x+ x*? use shortest match to x* x{}? use shortest match to x{} Note that using ']' or '-' in a character range expression requires special ordering. If ']' is to be used, it must be the first character after the '^', if present, within the brackets. If '-' is to be used, it must be the first character after the '^' and/or ']', if present. The following special sequences match literal characters by name (the names insensitive to case): <lparen> ( <rparen> ) <lsquare> [ <rsquare> ] <lbrace> { <rbrace> } <langle> < <rangle> > <vbar> | <caret> ^ <period> . <dot> . <squote> ' <dquote> " <star> * <plus> + <percent> % <question> ? <dollar> $ <backslash> \ <return> carriage return (character code 0x000D) <linefeed> line feed (character code 0x000A) <tab> tab (character code 0x0009) <nul> null character (character code 0x0000) <null> null character (character code 0x0000) The following special sequences match character classes (these class names, like the character literal names above, are insensitive to case): <Alpha> any alphabetic character <Upper> any upper-case alphabetic character <Lower> any lower-case alphabetic character <Digit> any digit <AlphaNum> any alphabetic character or digit <Space> space character <VSpace> vertical whitespace <Punct> punctuation character <Newline> any newline character: \r, \n, \b, 0x2028, 0x2029 Character classes can be combined by separating class names with the '|' delimiter. In addition, a class expression can be complemented to make it an exclusion expression by putting a '^' after the opening bracket. <Upper|Digit> any uppercase letter or any digit <^Alpha> anything except an alphabetic character <^Upper|Digit> anything except an uppercase letter or a digit (note that the exclusion applies to the ENTIRE expression, so in this case both uppercase letters and digits are excluded) In addition, character classes and named literals can be combined: <Upper|Star|Plus> Any uppercase letter, or '*' or '+' Finally, literal characters and literal ranges resembling '[]' ranges can be used in angle-bracket expressions, with the limitation that each character or range must be separated with the '|' delimiter: <a|b|c> 'a' or 'b' or 'c' <Upper|a|b|c> any uppercase letter, or 'a', 'b', or 'c' <Upper|a-m> any uppercase letter, or lowercase letters 'a' to 'm' '%' is used to escape the special characters: | . ( ) * ? + ^ $ % [ (We use '%' rather than a backslash because it's less trouble to enter in a TADS string -- a backslash needs to be quoted with another backslash, which is error-prone and hard to read. '%' doesn't need any special quoting in a TADS string, which makes it a lot more readable.) In addition, '%' is used to introduce additional special sequences: %1 text matching first parenthesized expression %9 text matching ninth parenthesized experssion %< matches at the beginning of a word only %> matches at the end of a word only %w matches any word character %W matches any non-word character %b matches at any word boundary (beginning or end of word) %B matches except at a word boundary %s <space> %S <^space> %v <vspace> %V <^vspace> %d <digit> %D <^digit> For the word matching sequences, a word is any sequence of letters and numbers. The following special sequences modify the search rules: <Case> - makes the search case-sensitive. This is the default. <NoCase> - makes the search case-insensitive. <Case> and <NoCase> are both global - the entire expression is either case-sensitive or case-insensitive. <FE> or <FirstEnd> - select the substring that ends earliest, in case of an ambiguous match. <FirstBegin> is the default. <FB> or <FirstBegin> - select the substring that starts earliest, in case of an ambiguous match. This is the default. <Min> - select the shortest matching substring, in case of an ambiguous match. <Max> is the default. <Max> - select the longest matching substring, in case of an ambiguous match. Modified 09/06/98 MJRoberts - Creation */ #include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include "t3std.h" #include "vmregex.h" #include "utf8.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmuni.h" #include "vmfile.h" /* ------------------------------------------------------------------------ */ /* * Initialize. */ CRegexParser::CRegexParser() { /* no tuple array yet */ tuple_arr_ = 0; tuples_alloc_ = 0; /* clear states */ next_state_ = RE_STATE_FIRST_VALID; /* no range buffer yet */ range_buf_ = 0; range_buf_cnt_ = 0; range_buf_max_ = 0; } /* ------------------------------------------------------------------------ */ /* * Reset compiler - clears states and tuples */ void CRegexParser::reset() { /* delete any range tables we've allocated */ int i; re_tuple *t; for (i = 0, t = tuple_arr_ ; i < next_state_ ; ++i, ++t) { /* if it's a range, delete the allocated range */ if ((t->typ == RE_RANGE || t->typ == RE_RANGE_EXCL) && t->info.range.char_range != 0) { delete [] t->info.range.char_range; t->info.range.char_range = 0; } else if (t->typ == RE_LITSTR && t->info.str.str != 0) { delete [] t->info.str.str; t->info.str.str = 0; } /* clear the tuple type */ t->typ = RE_INVALID; } /* clear states */ next_state_ = RE_STATE_FIRST_VALID; } /* ------------------------------------------------------------------------ */ /* * Delete */ CRegexParser::~CRegexParser() { /* reset state (to delete most of our memory structures) */ reset(); /* if we've allocated an array, delete it */ if (tuple_arr_ != 0) { t3free(tuple_arr_); tuple_arr_ = 0; } /* delete our range buffer if we have one */ if (range_buf_ != 0) { t3free(range_buf_); range_buf_ = 0; } } /* ------------------------------------------------------------------------ */ /* * Add an entry to our range buffer */ void CRegexParser::add_range_char(wchar_t ch1, wchar_t ch2) { /* * if the first character in the range is null, it requires special * representation */ if (ch1 == 0) { /* * A null character must be represented using the character class * notation for RE_NULLCHAR, because a null lead character in a * pair has the special meaning of introducing a class specifier * in the second character of the pair. So, first add the null * character as a class character. */ add_range_class(RE_NULLCHAR); /* * if the second character of the range is null, we're done, since * all we needed was the null itself */ if (ch2 == 0) return; /* * we've added the null character, so simply add the rest of the * range starting at character code 1 */ ch1 = 1; } /* make sure we have space in the range buffer for the added entry */ ensure_range_buf_space(); /* add the new entry */ range_buf_[range_buf_cnt_++] = ch1; range_buf_[range_buf_cnt_++] = ch2; } /* * Add a class to a character range. The class identifier is one of the * RE_xxx recognizer types representing a class: RE_ALPHA, RE_DIGIT, and * so on. */ void CRegexParser::add_range_class(re_recog_type cl) { /* make sure we have space in the range buffer */ ensure_range_buf_space(); /* * add the new entry - the first character of the pair is a null * character to indicate that the entry represents a class, and the * second of the pair is the type code */ range_buf_[range_buf_cnt_++] = 0; range_buf_[range_buf_cnt_++] = (wchar_t)cl; } /* * Check for space in the range buffer for a new range, expanding the * buffer if necessary. */ void CRegexParser::ensure_range_buf_space() { /* make sure we have room for another entry */ if (range_buf_ == 0) { /* * allocate an initial size - it must be even, since we always * consume elements in pairs */ range_buf_max_ = 128; range_buf_ = (wchar_t *)t3malloc(range_buf_max_ * sizeof(wchar_t)); /* no entries yet */ range_buf_cnt_ = 0; } else if (range_buf_cnt_ == range_buf_max_) { /* * reallocate the buffer at a larger size (the size must always be * even, since we always add to the buffer in pairs) */ range_buf_max_ += 128; range_buf_ = (wchar_t *)t3realloc(range_buf_, range_buf_max_ * sizeof(wchar_t)); } /* if the range buffer is null, throw an error */ if (range_buf_ == 0) err_throw(VMERR_OUT_OF_MEMORY); } /* ------------------------------------------------------------------------ */ /* * Allocate a new state ID */ re_state_id CRegexParser::alloc_state() { /* * If we don't have enough room for another state, expand the array */ if (next_state_ >= tuples_alloc_) { int new_alloc; /* bump the size by a bit */ new_alloc = tuples_alloc_ + 100; /* allocate or expand the array */ if (tuples_alloc_ == 0) { /* allocate the initial memory block */ tuple_arr_ = (re_tuple *)t3malloc(new_alloc * sizeof(re_tuple)); } else { /* re-allocate the existing memory block */ tuple_arr_ = (re_tuple *)t3realloc(tuple_arr_, new_alloc * sizeof(re_tuple)); } /* remember the new allocation size */ tuples_alloc_ = new_alloc; } /* initialize the next state */ tuple_arr_[next_state_].next_state_1 = RE_STATE_INVALID; tuple_arr_[next_state_].next_state_2 = RE_STATE_INVALID; tuple_arr_[next_state_].info.ch = RE_EPSILON; tuple_arr_[next_state_].flags = 0; tuple_arr_[next_state_].typ = RE_INVALID; /* return the new state's ID */ return next_state_++; } /* ------------------------------------------------------------------------ */ /* * Set a transition from a state to a given destination state. */ void CRegexParser::set_trans(re_state_id id, re_state_id dest_id, re_recog_type typ, wchar_t ch) { /* ignore invalid states */ if (id == RE_STATE_INVALID) return; /* * get the tuple containing the transitions for this state ID - the * state ID is the index of the state's transition tuple in the * array */ re_tuple *tuple = &tuple_arr_[id]; /* * If the first state pointer hasn't been set yet, set it to the new * destination. Otherwise, set the second state pointer. * * Only set the character on setting the first state. When setting * the second state, we must assume that the character for the state * has already been set, since any given state can have only one * character setting. */ if (tuple->next_state_1 == RE_STATE_INVALID) { /* set the transition type and character ID */ tuple->typ = typ; tuple->info.ch = ch; /* set the first transition */ tuple->next_state_1 = dest_id; } else { /* * set only the second transition state - don't set the character * ID or transition type */ tuple->next_state_2 = dest_id; } } /* ------------------------------------------------------------------------ */ /* * Initialize a new machine, giving it an initial and final state */ void CRegexParser::init_machine(re_machine *machine) { machine->init = alloc_state(); machine->final = alloc_state(); } /* * Build a character recognizer */ void CRegexParser::build_char(re_machine *machine, wchar_t ch) { /* initialize our new machine */ init_machine(machine); /* allocate a transition tuple for the new state */ set_trans(machine->init, machine->final, RE_LITERAL, ch); } /* * Build a special type recognizer */ void CRegexParser::build_special(re_machine *machine, re_recog_type typ, wchar_t ch) { /* initialize our new machine */ init_machine(machine); /* allocate a transition tuple for the new state */ set_trans(machine->init, machine->final, typ, ch); } /* * Build a character range recognizer. */ void CRegexParser::build_char_range(re_machine *machine, int exclusion) { /* initialize our new machine */ init_machine(machine); /* allocate a transition table for the new state */ set_trans(machine->init, machine->final, (exclusion ? RE_RANGE_EXCL : RE_RANGE), 0); /* allocate a copy of the range vector */ wchar_t *range_copy = new wchar_t[range_buf_cnt_]; /* copy the caller's range */ memcpy(range_copy, range_buf_, range_buf_cnt_ * sizeof(wchar_t)); /* store it in the tuple */ tuple_arr_[machine->init].info.range.char_range = range_copy; tuple_arr_[machine->init].info.range.char_range_cnt = range_buf_cnt_; } /* * Build a group recognizer. This is almost the same as a character * recognizer, but matches a previous group rather than a literal * character. */ void CRegexParser::build_group_matcher(re_machine *machine, int group_num) { /* initialize our new machine */ init_machine(machine); /* * Allocate a transition tuple for the new state for the group ID. * Rather than storing a literal character code, store the group ID * in the character code slot. */ set_trans(machine->init, machine->final, RE_GROUP_MATCH, (wchar_t)group_num); } /* * Build a concatenation recognizer */ void CRegexParser::build_concat(re_machine *new_machine, re_machine *lhs, re_machine *rhs) { /* initialize the new machine */ init_machine(new_machine); /* * set up an epsilon transition from the new machine's initial state * to the first submachine's initial state */ set_trans(new_machine->init, lhs->init, RE_EPSILON, 0); /* * Set up an epsilon transition from the first submachine's final * state to the second submachine's initial state */ set_trans(lhs->final, rhs->init, RE_EPSILON, 0); /* * Set up an epsilon transition from the second submachine's final * state to our new machine's final state */ set_trans(rhs->final, new_machine->final, RE_EPSILON, 0); } /* * Build a group machine. sub_machine contains the machine that * expresses the group's contents; we'll fill in new_machine with a * newly-created machine that encloses and marks the group. */ void CRegexParser::build_group(re_machine *new_machine, re_machine *sub_machine, int group_id) { /* initialize the container machine */ init_machine(new_machine); /* * Set up a group-entry transition from the new machine's initial * state into the initial state of the group, and a group-exit * transition from the group's final state into the container's final * state. For both transitions, store the group ID in the character * field of the transition, to identify which group is affected. */ set_trans(new_machine->init, sub_machine->init, RE_GROUP_ENTER, (wchar_t)group_id); set_trans(sub_machine->final, new_machine->final, RE_GROUP_EXIT, (wchar_t)group_id); } /* * Build an assertion machine */ void CRegexParser::build_assert(re_machine *new_machine, re_machine *sub_machine, int is_negative, int is_lookback) { /* initialize the container machine */ init_machine(new_machine); /* get a pointer to its initial state */ re_tuple *t = &tuple_arr_[new_machine->init]; /* allocate a transition tuple for the new state */ set_trans(new_machine->init, new_machine->final, (is_lookback ? (is_negative ? RE_ASSERT_BACKNEG : RE_ASSERT_BACKPOS) : (is_negative ? RE_ASSERT_NEG : RE_ASSERT_POS)), 0); /* set the sub-state */ t->info.sub.init = sub_machine->init; t->info.sub.final = sub_machine->final; /* * For lookback assertions, we need to know the range of possible match * lengths, to optimize the reverse search for a match. The * sub-machine for the assertion is fully built now and is by nature * self-contained, so we can calculate its length range now. */ if (is_lookback) get_match_length(sub_machine->init, sub_machine->final, &t->info.sub.minlen, &t->info.sub.maxlen, 0); } /* * Build an alternation recognizer */ void CRegexParser::build_alter(re_machine *new_machine, re_machine *lhs, re_machine *rhs) { /* initialize the new machine */ init_machine(new_machine); /* * Set up an epsilon transition from our new machine's initial state * to the initial state of each submachine */ set_trans(new_machine->init, lhs->init, RE_EPSILON, 0); set_trans(new_machine->init, rhs->init, RE_EPSILON, 0); /* * Set up an epsilon transition from the final state of each * submachine to our final state */ set_trans(lhs->final, new_machine->final, RE_EPSILON, 0); set_trans(rhs->final, new_machine->final, RE_EPSILON, 0); } /* * Build a closure recognizer */ void CRegexParser::build_closure(re_machine *new_machine, re_machine *sub, wchar_t specifier, int shortest) { /* initialize the new machine */ init_machine(new_machine); /* * Set up an epsilon transition from our initial state to the * submachine's initial state. However, if we're in shortest mode, * wait to do this until after we've generated the rest of the * machine, and instead set up the transition from the submachine's * final state to our final state. The order is important because * we will favor the first branch taken when we find two matches of * equal total length; hence we want to make the branch that will * give us either the longest match or shortest match for this * closure first, depending on which way we want to go. */ if (!shortest) set_trans(new_machine->init, sub->init, RE_EPSILON, 0); else set_trans(sub->final, new_machine->final, RE_EPSILON, 0); /* * If this is an unbounded closure ('*' or '+', but not '?'), set up * the loop transition that takes us from the new machine's final * state back to its initial state. We don't do this on the * zero-or-one closure, because we can only match the expression * once. */ if (specifier != '?') { /* set the transition */ set_trans(sub->final, sub->init, RE_EPSILON, 0); /* if we have a 'shortest' modifier, flag it in this branch */ if (shortest) tuple_arr_[sub->final].flags |= RE_STATE_SHORTEST; } /* * If this is a zero-or-one closure or a zero-or-more closure, set * up an epsilon transition from our initial state to our final * state, since we can skip the entire subexpression. We don't do * this on the one-or-more closure, because we can't skip the * subexpression in this case. */ if (specifier != '+') { /* set the transition */ set_trans(new_machine->init, new_machine->final, RE_EPSILON, 0); /* if we have a 'shortest' modifier, flag it in this branch */ if (shortest) tuple_arr_[new_machine->init].flags |= RE_STATE_SHORTEST; } /* * Set up a transition from the submachine's final state to our * final state. We waited until here to ensure proper ordering for * longest-preferred. If we're in shortest-preferred mode, though, * set up the initial transition to the submachine instead. */ if (!shortest) set_trans(sub->final, new_machine->final, RE_EPSILON, 0); else set_trans(new_machine->init, sub->init, RE_EPSILON, 0); } void CRegexParser::build_interval(re_machine *new_machine, re_machine *sub, int min_val, int max_val, int var_id, int shortest) { re_machine inner_machine; /* initialize the outer (new) machine */ init_machine(new_machine); /* initialize the inner machine */ init_machine(&inner_machine); /* * Set the loop transition into the submachine, and set the other to * bypass the submachine. If we have a 'shortest' modifier, take * the bypass branch first, otherwise take the enter branch first. */ if (shortest) { set_trans(inner_machine.init, new_machine->final, RE_LOOP_BRANCH, 0); set_trans(inner_machine.init, sub->init, RE_LOOP_BRANCH, 0); } else { set_trans(inner_machine.init, sub->init, RE_LOOP_BRANCH, 0); set_trans(inner_machine.init, new_machine->final, RE_LOOP_BRANCH, 0); } /* * set the final transition of the submachine to come back to the * loop branch point */ set_trans(sub->final, inner_machine.init, RE_EPSILON, 0); /* * set the outer machine to transition into the inner machine and * zero the loop variable */ set_trans(new_machine->init, inner_machine.init, RE_ZERO_VAR, 0); /* set the variable ID in the ZERO_VAR node */ tuple_arr_[new_machine->init].info.loop.loop_var = var_id; /* set up the loop parameters in the loop node */ tuple_arr_[inner_machine.init].info.loop.loop_var = var_id; tuple_arr_[inner_machine.init].info.loop.loop_min = min_val; tuple_arr_[inner_machine.init].info.loop.loop_max = max_val; /* * if there's a 'shortest' modifier, note it in the loop node, so * that we can take the bypass branch first whenever possible */ if (shortest) tuple_arr_[inner_machine.init].flags |= RE_STATE_SHORTEST; } /* * Build a null machine */ void CRegexParser::build_null_machine(re_machine *machine) { machine->init = machine->final = RE_STATE_INVALID; } /* ------------------------------------------------------------------------ */ /* * Determine if a machine is null */ int CRegexParser::is_machine_null(re_machine *machine) { return (machine->init == RE_STATE_INVALID); } /* ------------------------------------------------------------------------ */ /* * Concatenate the second machine onto the first machine, replacing the * first machine with the resulting machine. If the first machine is a * null machine (created with re_build_null_machine), we'll simply copy * the second machine into the first. */ void CRegexParser::concat_onto(re_machine *dest, re_machine *rhs) { /* check for a null destination machine */ if (is_machine_null(dest)) { /* * the first machine is null - simply copy the second machine * onto the first unchanged */ *dest = *rhs; } else { re_machine new_machine; /* build the concatenated machine */ build_concat(&new_machine, dest, rhs); /* copy the concatenated machine onto the first machine */ *dest = new_machine; } } /* * Alternate the second machine onto the first machine, replacing the * first machine with the resulting machine. If the first machine is a * null machine, this simply replaces the first machine with the second * machine. If the second machine is null, this simply leaves the first * machine unchanged. */ void CRegexParser::alternate_onto(re_machine *dest, re_machine *rhs) { /* check to see if the first machine is null */ if (is_machine_null(dest)) { /* * the first machine is null - simply copy the second machine * onto the first */ *dest = *rhs; } else { /* * if the second machine is null, don't do anything; otherwise, * build the alternation */ if (!is_machine_null(rhs)) { re_machine new_machine; /* build the alternation */ build_alter(&new_machine, dest, rhs); /* replace the first machine with the alternation */ *dest = new_machine; } } } /* ------------------------------------------------------------------------ */ /* * Compile an expression */ re_status_t CRegexParser::compile(const char *expr_str, size_t exprlen, re_compiled_pattern_base *pat) { re_machine cur_machine; re_machine alter_machine; re_machine new_machine; int group_stack_level; struct { re_machine old_cur; re_machine old_alter; int group_id; int capturing; int neg_assertion; int pos_assertion; int back_assertion; } group_stack[RE_GROUP_NESTING_MAX]; utf8_ptr expr; /* reset everything */ reset(); /* we have no groups yet */ pat->group_cnt = 0; /* we have no looping variables yet */ pat->loop_var_cnt = 0; /* get the length of the expression in characters */ size_t exprchars = utf8_ptr::s_len(expr_str, exprlen); /* * set the default match modes - maximum, first-beginning, * case-sensitive */ pat->longest_match = TRUE; pat->first_begin = TRUE; pat->case_sensitive = TRUE; pat->case_sensitivity_specified = FALSE; /* start out with no current machine and no alternate machine */ build_null_machine(&cur_machine); build_null_machine(&alter_machine); /* nothing on the stack yet */ group_stack_level = 0; /* loop until we run out of expression to parse */ for (expr.set((char *)expr_str) ; exprchars != 0 ; expr.inc(), --exprchars) { switch(expr.getch()) { case '^': /* * beginning of line - if we're not at the beginning of the * current expression (i.e., we already have some * concatentations accumulated), treat it as an ordinary * character */ if (!is_machine_null(&cur_machine)) goto normal_char; /* build a new start-of-text recognizer */ build_special(&new_machine, RE_TEXT_BEGIN, 0); /* * concatenate it onto the string - note that this can't * have any postfix operators */ concat_onto(&cur_machine, &new_machine); break; case '$': /* * End of line specifier - if there's anything left after * the '$' other than a close parens or alternation * specifier, treat it as a normal character */ if (exprchars > 1 && (expr.getch_at(1) != ')' && expr.getch_at(1) != '|')) goto normal_char; /* build a new end-of-text recognizer */ build_special(&new_machine, RE_TEXT_END, 0); /* * concatenate it onto the string - note that this can't * have any postfix operators */ concat_onto(&cur_machine, &new_machine); break; case '(': { int capturing; int pos_assertion; int neg_assertion; int back_assertion; /* presume it's a capturing group */ capturing = TRUE; /* presume it's not an assertion */ pos_assertion = FALSE; neg_assertion = FALSE; back_assertion = FALSE; /* * Add a nesting level. Push the current machine and * alternate machines onto the group stack, and clear * everything out for the new group. */ if (group_stack_level > RE_GROUP_NESTING_MAX) { /* we cannot proceed - return an error */ return RE_STATUS_GROUP_NESTING_TOO_DEEP; } /* save the current state on the stack */ group_stack[group_stack_level].old_cur = cur_machine; group_stack[group_stack_level].old_alter = alter_machine; /* * Assign the group a group ID - groups are numbered in * order of their opening (left) parentheses, so we want * to assign a group number now. We won't actually need * to know the group number until we get to the matching * close paren, but we need to assign it now, so store * it in the group stack. */ group_stack[group_stack_level].group_id = pat->group_cnt; /* check for special group flags */ if (exprchars > 2 && expr.getch_at(1) == '?') { switch(expr.getch_at(2)) { case '<': /* look-back assertion */ if (exprchars > 3 && (expr.getch_at(3) == '=' || expr.getch_at(3) == '!')) { /* it's a look-back assertion */ back_assertion = TRUE; /* assertions don't capture */ capturing = FALSE; /* note whether it's positive or negative */ if (expr.getch_at(3) == '=') pos_assertion = TRUE; else neg_assertion = TRUE; /* skip the '?<=' or '?<!' part */ expr.inc(); expr.inc(); expr.inc(); exprchars -= 3; } break; case ':': /* it's a non-capturing group */ capturing = FALSE; /* skip two extra characters for the '?:' part */ expr.inc(); expr.inc(); exprchars -= 2; break; case '=': /* it's a positive assertion group */ pos_assertion = TRUE; /* assertions don't capture */ capturing = FALSE; /* skip two extra characters for the '?=' part */ expr.inc(); expr.inc(); exprchars -= 2; break; case '!': /* it's a negative assertion group */ neg_assertion = TRUE; /* assertions don't capture */ capturing = FALSE; /* skip two extra characters for the '?!' part */ expr.inc(); expr.inc(); exprchars -= 2; break; default: /* it's not a recognized sequence - ignore it */ break; } } /* remember if the group is capturing */ group_stack[group_stack_level].capturing = capturing; /* remember if it's an assertion of some kind */ group_stack[group_stack_level].pos_assertion = pos_assertion; group_stack[group_stack_level].neg_assertion = neg_assertion; group_stack[group_stack_level].back_assertion = back_assertion; /* consume the group number if it's a capturing group */ if (capturing) ++(pat->group_cnt); /* push the level */ ++group_stack_level; /* start the new group with empty machines */ build_null_machine(&cur_machine); build_null_machine(&alter_machine); } break; case ')': do_close_paren: /* if there's nothing on the stack, ignore this */ if (group_stack_level == 0) break; /* take a level off the stack */ --group_stack_level; /* * For a lookback assertion, add the lookback parent position * match assertion to the group. This ensures that we can only * match the group when the match leaves us at the same * position where the lookback assertion was matched. */ if (group_stack[group_stack_level].back_assertion) { build_special(&new_machine, RE_LOOKBACK_POS, 0); concat_onto(&cur_machine, &new_machine); } /* * Remove a nesting level. If we have a pending alternate * expression, build the alternation expression. This will * leave the entire group expression in alter_machine, * regardless of whether an alternation was in progress or * not. */ alternate_onto(&alter_machine, &cur_machine); /* * Create a group machine that encloses the group and marks * it with a group number. We assigned the group number * when we parsed the open paren, so read that group number * from the stack. * * Note that this will leave 'new_machine' with the entire * group machine. * * If this is a non-capturing group, don't bother building * the new machine - just copy the current alternation * machine onto the new machine. */ if (group_stack[group_stack_level].capturing) { /* it's a regular capturing group - add the group machine */ build_group(&new_machine, &alter_machine, group_stack[group_stack_level].group_id); } else if (group_stack[group_stack_level].pos_assertion || group_stack[group_stack_level].neg_assertion) { /* it's an assertion - build the assertion group */ build_assert(&new_machine, &alter_machine, group_stack[group_stack_level].neg_assertion, group_stack[group_stack_level].back_assertion); } else { /* it's a non-capturing group - just add the group tree */ new_machine = alter_machine; } /* * Pop the stack - restore the alternation and current * machines that were in progress before the group started. */ cur_machine = group_stack[group_stack_level].old_cur; alter_machine = group_stack[group_stack_level].old_alter; /* * Check the group expression (in new_machine) for postfix * expressions */ goto apply_postfix; case '|': /* * Start a new alternation. This ends the current * alternation; if we have a previous pending alternate, * build an alternation machine out of the previous * alternate and the current machine and move that to the * alternate; otherwise, simply move the current machine to * the pending alternate. */ alternate_onto(&alter_machine, &cur_machine); /* * the alternation starts out with a blank slate, so null * out the current machine */ build_null_machine(&cur_machine); break; case '<': /* check for our various special directives */ if ((exprchars >= 4 && memicmp(expr.getptr(), "<FE>", 4) == 0) || (exprchars >= 10 && memicmp(expr.getptr(), "<FirstEnd>", 10) == 0)) { /* turn off first-begin mode */ pat->first_begin = FALSE; } else if ((exprchars >= 4 && memicmp(expr.getptr(), "<FB>", 4) == 0) || (exprchars >= 12 && memicmp(expr.getptr(), "<FirstBegin>", 12) == 0)) { /* turn on first-begin mode */ pat->first_begin = TRUE; } else if (exprchars >= 5 && memicmp(expr.getptr(), "<Max>", 5) == 0) { /* turn on longest-match mode */ pat->longest_match = TRUE; } else if (exprchars >= 5 && memicmp(expr.getptr(), "<Min>", 5) == 0) { /* turn off longest-match mode */ pat->longest_match = FALSE; } else if (exprchars >= 6 && memicmp(expr.getptr(), "<Case>", 6) == 0) { /* turn on case sensitivity */ pat->case_sensitive = TRUE; pat->case_sensitivity_specified = TRUE; } else if (exprchars >= 8 && memicmp(expr.getptr(), "<NoCase>", 8) == 0) { /* turn off case sensitivity */ pat->case_sensitive = FALSE; pat->case_sensitivity_specified = TRUE; } else { /* * It's nothing else we recognize, so it must be a * character class or class range expression, which * consists of one or more classes, single characters, or * character ranges separated by '|' delimiters. */ if (compile_char_class_expr(&expr, &exprchars, &new_machine)) { /* success - look for postfix operators */ goto apply_postfix; } else { /* * failure - treat the whole thing as ordinary * characters */ goto normal_char; } } /* skip everything up to the closing ">" */ while (exprchars > 0 && expr.getch() != '>') expr.inc(), --exprchars ; break; case '%': /* * quoted character - skip the quote mark and see what we * have */ expr.inc(); --exprchars; /* check to see if we're at the end of the expression */ if (exprchars == 0) { /* * end of the string - ignore it, but undo the extra * increment of the expression index so that we exit the * enclosing loop properly */ expr.dec(); ++exprchars; break; } /* see what we have */ switch(expr.getch()) { case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* group match - build a new literal group recognizer */ build_group_matcher(&new_machine, value_of_digit(expr.getch()) - 1); /* apply any postfix expression to the group recognizer */ goto apply_postfix; case '<': /* build a beginning-of-word recognizer */ build_special(&new_machine, RE_WORD_BEGIN, 0); /* it can't be postfixed - just concatenate it */ concat_onto(&cur_machine, &new_machine); break; case '>': /* build an end-of-word recognizer */ build_special(&new_machine, RE_WORD_END, 0); /* it can't be postfixed - just concatenate it */ concat_onto(&cur_machine, &new_machine); break; case 'w': /* word character */ build_special(&new_machine, RE_WORD_CHAR, 0); goto apply_postfix; case 'W': /* non-word character */ build_special(&new_machine, RE_NON_WORD_CHAR, 0); goto apply_postfix; case 'b': /* word boundary */ build_special(&new_machine, RE_WORD_BOUNDARY, 0); /* it can't be postfixed */ concat_onto(&cur_machine, &new_machine); break; case 'B': /* not a word boundary */ build_special(&new_machine, RE_NON_WORD_BOUNDARY, 0); /* it can't be postfixed */ concat_onto(&cur_machine, &new_machine); break; case 's': /* %s -> same as <space> */ build_special(&new_machine, RE_SPACE, 0); goto apply_postfix; case 'S': /* %S -> same as <^space> */ build_special(&new_machine, RE_NON_SPACE, 0); goto apply_postfix; case 'd': /* %d -> same as <digit> */ build_special(&new_machine, RE_DIGIT, 0); goto apply_postfix; case 'D': /* %D -> same as <^digit> */ build_special(&new_machine, RE_NON_DIGIT, 0); goto apply_postfix; case 'v': /* %v -> same as <vspace> */ build_special(&new_machine, RE_VSPACE, 0); goto apply_postfix; case 'V': /* %V -> same as <^vspace> */ build_special(&new_machine, RE_NON_VSPACE, 0); goto apply_postfix; default: /* build a new literal character recognizer */ build_char(&new_machine, expr.getch()); /* apply any postfix expression to the character */ goto apply_postfix; } break; case '.': /* * wildcard character - build a single character recognizer * for the special wildcard symbol, then go check it for a * postfix operator */ build_special(&new_machine, RE_WILDCARD, 0); goto apply_postfix; break; case '[': /* range expression */ { int is_exclusive = FALSE; /* we have no entries yet */ range_buf_cnt_ = 0; /* first, skip the open bracket */ expr.inc(); --exprchars; /* check to see if starts with the exclusion character */ if (exprchars != 0 && expr.getch() == '^') { /* skip the exclusion specifier */ expr.inc(); --exprchars; /* note it */ is_exclusive = TRUE; } /* * if the first character is a ']', include it in the * range */ if (exprchars != 0 && expr.getch() == ']') { add_range_char(']'); expr.inc(); --exprchars; } /* * if the next character is a '-', include it in the * range */ if (exprchars != 0 && expr.getch() == '-') { add_range_char('-'); expr.inc(); --exprchars; } /* scan the character set */ while (exprchars != 0 && expr.getch() != ']') { /* note this character */ wchar_t ch = expr.getch(); /* skip this character of the expression */ expr.inc(); --exprchars; /* check for a range */ if (exprchars != 0 && expr.getch() == '-') { /* skip the '-' */ expr.inc(); --exprchars; if (exprchars != 0) { /* get the other end of the range */ wchar_t ch2 = expr.getch(); /* skip the second character */ expr.inc(); --exprchars; /* if the range is reversed, swap it */ if (ch > ch2) { wchar_t tmp = ch; ch = ch2; ch2 = tmp; } /* add the range */ add_range_char(ch, ch2); } } else { /* no range - add the one-character range */ add_range_char(ch); } } /* create a character range machine */ build_char_range(&new_machine, is_exclusive); /* apply any postfix operator */ goto apply_postfix; } break; default: normal_char: /* * it's an ordinary character - build a single character * recognizer machine, and then concatenate it onto any * existing machine */ build_char(&new_machine, expr.getch()); apply_postfix: /* * Check for a postfix operator, and apply it to the machine * in 'new_machine' if present. In any case, concatenate * the 'new_machine' (modified by a postix operator or not) * to the current machien. */ if (exprchars > 1) { switch(expr.getch_at(1)) { case '*': case '+': case '?': /* * We have a postfix closure operator. Build a new * closure machine out of 'new_machine'. */ { /* move onto the closure operator */ expr.inc(); --exprchars; /* * if the next character is '?', it's a modifier * that indicates that we are to use the * shortest match - note it if so */ int shortest = (exprchars > 1 && expr.getch_at(1) == '?'); /* build the closure machine */ re_machine closure_machine; build_closure(&closure_machine, &new_machine, expr.getch(), shortest); /* replace the original machine with the closure */ new_machine = closure_machine; } /* * skip any redundant closure symbols, keeping only the * first one we saw */ skip_closures: while (exprchars > 1) { /* check for a simple closure suffix */ if (expr.getch_at(1) == '?' || expr.getch_at(1) == '+' || expr.getch_at(1) == '*') { /* skip it and keep looping */ expr.inc(); --exprchars; continue; } /* check for an interval */ if (expr.getch_at(1) == '{') { /* skip until we find the matching '}' */ while (exprchars > 1 && expr.getch_at(1) != '}') expr.inc(), --exprchars; /* go back for anything that follows */ continue; } /* if it's anything else, we're done discarding */ break; } break; case '{': /* interval specifier */ { int min_val; int max_val; re_machine interval_machine; int shortest; int var_id; /* * loops can never overlap, but can be nested; * so the only thing we have to worry about in * assigning a loop variable is the group * nesting depth */ var_id = group_stack_level; /* note the highest variable ID we've seen */ if (var_id >= pat->loop_var_cnt) pat->loop_var_cnt = var_id + 1; /* presume neither min nor max will be specified */ min_val = -1; max_val = -1; /* skip the current character and the '{' */ expr.inc(); expr.inc(); exprchars -= 2; /* parse the minimum count, if provided */ min_val = parse_int(&expr, &exprchars); /* if there's a comma, parse the maximum value */ if (exprchars >= 1 && expr.getch() == ',') { /* skip the ',' and parse the number */ expr.inc(); --exprchars; max_val = parse_int(&expr, &exprchars); } else { /* * there's no other value, so this is a * simple loop with the same value for min * and max */ max_val = min_val; } /* * if we're not looking at a '}', skip * characters until we are */ while (exprchars != 0 && expr.getch() != '}') expr.inc(&exprchars); /* * if there's a '?' following, it's a 'shortest' * modifier - note it */ shortest = FALSE; if (exprchars > 1 && expr.getch_at(1) == '?') { /* note the modifier */ shortest = TRUE; /* skip another character for the modifier */ expr.inc(); --exprchars; } /* set up an interval node */ build_interval(&interval_machine, &new_machine, min_val, max_val, var_id, shortest); /* replace the original machine with the interval */ new_machine = interval_machine; /* skip any closure modifiers that follow */ goto skip_closures; } break; default: /* no postfix operator */ break; } } /* * Concatenate the new machine onto the current machine * under construction. */ concat_onto(&cur_machine, &new_machine); break; } /* if we've run down the expression string, go no further */ if (exprchars == 0) break; } /* if there are any open parens outstanding, close them */ if (group_stack_level != 0) goto do_close_paren; /* complete any pending alternation */ alternate_onto(&alter_machine, &cur_machine); /* check for and break any infinite loops */ break_loops(&alter_machine); /* remove meaningless branch-to-branch transitions */ remove_branch_to_branch(&alter_machine); /* consolidate consecutive character transitions into strings */ consolidate_strings(&alter_machine); /* store the results in the caller's base pattern description */ pat->machine = alter_machine; pat->tuple_cnt = next_state_; /* limit the group count to the maximum */ if (pat->group_cnt > RE_GROUP_REG_CNT) pat->group_cnt = RE_GROUP_REG_CNT; /* limit the variable count to the maximum */ if (pat->loop_var_cnt > RE_LOOP_VARS_MAX) pat->loop_var_cnt = RE_LOOP_VARS_MAX; /* no errors encountered */ return RE_STATUS_SUCCESS; } /* * Parse a character class or class range expresion. Returns true on * success, in which case we will have built a class range expression in * new_machine and updated expr and exprlen to skip the expression. * Returns false on syntax error or other failure, in which case expr and * exprlen will be unchanged. */ int CRegexParser::compile_char_class_expr(utf8_ptr *expr, size_t *exprchars, re_machine *result_machine) { /* start at the character after the '<' */ utf8_ptr p = *expr; size_t chrem = *exprchars; p.inc(); --chrem; /* presume it won't be exclusive */ int is_exclusive = FALSE; /* check for an exclusion flag */ if (chrem != 0 && p.getch() == '^') { /* note the exclusion */ is_exclusive = TRUE; /* skip the exclusion prefix */ p.inc(); --chrem; } /* clear out the range builder buffer */ range_buf_cnt_ = 0; /* * keep going until we find the closing delimiter (or run out of * expression string) */ for (;;) { utf8_ptr start; wchar_t delim; size_t curcharlen; size_t curbytelen; /* remember where the current part starts */ start = p; /* scan for the '>' or '|' */ for (curcharlen = 0 ; chrem != 0 ; p.inc(), --chrem, ++curcharlen) { /* get the next character */ delim = p.getch(); /* if it's '>' or '|', we're done */ if (delim == '>' || delim == '|') break; } /* * If we reached the end of the expression without finding a * closing delimiter, the expression is invalid - treat the whole * thing (starting with the '<') as ordinary characters. */ if (chrem == 0) return FALSE; /* get the length of this part */ curbytelen = (size_t)(p.getptr() - start.getptr()); /* * See what we have. If we have a single character, it's a * literal. If we have a character, a hyphen, and another * character, it's a literal range. Otherwise, it must be one of * our named classes. */ if (curcharlen == 1) { /* it's a literal - add the character */ add_range_char(start.getch()); } else if (curcharlen == 3 && start.getch_at(1) == '-') { /* it's a literal range - add it */ add_range_char(start.getch(), start.getch_at(2)); } else { struct char_class_t { /* expression name for the class */ const char *name; /* length of the class name */ size_t name_len; /* * literal character, if the name represents a character * rather than a class - this is used only if code == * RE_LITERAL */ wchar_t ch; /* RE_xxx code for the class */ re_recog_type code; }; static const char_class_t classes[] = { { "alpha", 5, 0, RE_ALPHA }, { "digit", 5, 0, RE_DIGIT }, { "upper", 5, 0, RE_UPPER }, { "lower", 5, 0, RE_LOWER }, { "alphanum", 8, 0, RE_ALPHANUM }, { "space", 5, 0, RE_SPACE }, { "punct", 5, 0, RE_PUNCT }, { "newline", 7, 0, RE_NEWLINE }, { "vspace", 6, 0, RE_VSPACE }, { "langle", 6, '<', RE_LITERAL }, { "rangle", 6, '>', RE_LITERAL }, { "vbar", 4, '|', RE_LITERAL }, { "caret", 5, '^', RE_LITERAL }, { "squote", 6, '\'', RE_LITERAL }, { "dquote", 6, '"', RE_LITERAL }, { "star", 4, '*', RE_LITERAL }, { "question", 8, '?', RE_LITERAL }, { "percent", 7, '%', RE_LITERAL }, { "dot", 3, '.', RE_LITERAL }, { "period", 6, '.', RE_LITERAL }, { "plus", 4, '+', RE_LITERAL }, { "lsquare", 7, '[', RE_LITERAL }, { "rsquare", 7, ']', RE_LITERAL }, { "lparen", 6, '(', RE_LITERAL }, { "rparen", 6, ')', RE_LITERAL}, { "lbrace", 6, '{', RE_LITERAL }, { "rbrace", 6, '}', RE_LITERAL }, { "dollar", 6, '$', RE_LITERAL }, { "backslash",9, '\\', RE_LITERAL }, { "return", 6, 0x000D, RE_LITERAL }, { "linefeed", 8, 0x000A, RE_LITERAL }, { "tab", 3, 0x0009, RE_LITERAL }, { "nul", 3, 0x0000, RE_LITERAL }, { "null", 4, 0x0000, RE_LITERAL } }; const char_class_t *cp; size_t i; int found; /* scan our name list for a match */ for (cp = classes, i = 0, found = FALSE ; i < sizeof(classes)/sizeof(classes[0]) ; ++i, ++cp) { /* * if the length matches and the name matches (ignoring * case), this is the one we want */ if (curbytelen == cp->name_len && memicmp(start.getptr(), cp->name, curbytelen) == 0) { /* * this is it - add either a class range or literal * range, depending on the meaning of the name */ if (cp->code == RE_LITERAL) { /* it's a name for a literal */ add_range_char(cp->ch); } else { /* * It's a name for a character class. As a * special case for efficiency, if this is the one * and only thing in this class expression, don't * create a range expression but instead create a * special for the class. * * Note that we can't do this for an exclusive * class, since we don't have any special matcher * for those - implement those with a character * range as usual. */ if (range_buf_cnt_ == 0 && delim == '>' && !is_exclusive) { /* * This is the only thing, so build a special * to match this class - this is more * efficient to store and to match than a * range expression. */ build_special(result_machine, cp->code, 0); /* skip to the '>' */ *expr = p; *exprchars = chrem; /* * we're done with the expresion - tell the * caller we were successful */ return TRUE; } else { /* * it's not the only thing, so add the class * to the range list */ add_range_class(cp->code); } } /* note that we found a match */ found = TRUE; /* no need to scan further in our table */ break; } } /* if we didn't find a match, the whole expression is invalid */ if (!found) return FALSE; } /* * if we found the '>', we're done; if we found a '|', skip it and * keep going */ if (delim == '|') { /* skip the delimiter, and back for another round */ p.inc(&chrem); } else { /* we found the '>', so we're done - add the range recognizer */ build_char_range(result_machine, is_exclusive); /* skip up to the '>' */ *expr = p; *exprchars = chrem; /* tell the caller we were successful */ return TRUE; } } } /* * Parse an integer value. Returns -1 if there's no number. */ int CRegexParser::parse_int(utf8_ptr *p, size_t *chrem) { /* if it's not a number to start with, simply return -1 */ if (*chrem == 0 || !is_digit(p->getch())) return -1; /* keep going as long as we find digits */ int acc; for (acc = 0 ; *chrem > 0 && is_digit(p->getch()) ; p->inc(), --*chrem) { /* add this digit into the accumulator */ acc *= 10; acc += value_of_digit(p->getch()); } /* return the accumulated result */ return acc; } /* * Expression scan stack frame. We use this to keep track of the callers * in a recursive scan of the expression array, so that we can detect when * we're entering an infinite recursion. */ struct regex_scan_frame { regex_scan_frame(regex_scan_frame *parent) { this->st = RE_STATE_INVALID; this->parent = parent; } /* * Check to see if the current state is recursing. If the current * frame's state also appears in a parent frame, the state is * recursing. */ int is_recursing() { /* search up the parent chain for a duplicate of my state */ for (regex_scan_frame *fr = parent ; fr != 0 ; fr = fr->parent) { if (fr->st == st) return TRUE; } /* didn't find it */ return FALSE; } /* the state ID we're visiting in this frame */ re_state_id st; /* the parent frame */ regex_scan_frame *parent; }; /* * Break any infinite loops in the machine. Check for cycles that * consist solely of epsilon transitions, and break each cycle at the * last alternating transition. */ void CRegexParser::break_loops(re_machine *machine) { /* break loops starting at the initial state */ if (break_loops(machine->init, machine->final, 0)) { /* * The initial state was a looping state, so the entire expression * will consume no input and will just loop forever. Consider this * an always-matching zero-length expression, so set the initial * state to point to the final state. */ machine->final = machine->init; } } /* * Find and break loops from the current state back to the given initial * state completely via epsilon transitions. Returns true if we found a * loop back to the initial state, false if not. */ int CRegexParser::break_loops(re_state_id init, re_state_id final, regex_scan_frame *stack) { /* set up the scan frame for this level */ regex_scan_frame fr(stack); /* scan the chain of states from the current state */ for (re_state_id cur_state = init ; ; ) { /* if this is the final state, we're done */ if (cur_state == final) return FALSE; /* get the tuple for this state */ re_tuple *tuple = &tuple_arr_[cur_state]; /* set the current state in the recursion frame */ fr.st = cur_state; /* if it's a two-transition epsilon state, recurse */ if (tuple->typ == RE_EPSILON && tuple->next_state_2 != RE_STATE_INVALID) { /* * This is a branching state, so we need to recursively break * loops in each branch. If we're looping on the current * state, tell the caller that this whole branch is a loop. */ if (fr.is_recursing()) return TRUE; /* try breaking loops in the first branch */ if (break_loops(tuple->next_state_2, final, &fr)) { /* the second branch loops - skip it */ tuple->next_state_2 = RE_STATE_INVALID; } /* check the other branch */ if (break_loops(tuple->next_state_1, final, &fr)) { /* * the first branch loops - move the second branch to * the first slot, and clear the second slot (if we only * have one branch, it must always be in the first slot) */ tuple->next_state_1 = tuple->next_state_2; tuple->next_state_2 = RE_STATE_INVALID; } /* * If both branches loop, this entire state is a loop, so tell * the caller to break its branch to here. Otherwise, we've * finished with the rest of this chain. */ return (tuple->next_state_1 == RE_STATE_INVALID); } /* see what kind of transition this is */ switch(tuple->typ) { case RE_EPSILON: case RE_GROUP_ENTER: case RE_GROUP_EXIT: /* * epsilon or group transition - this could definitely be part * of a loop, so move on to the next state and keep looking */ cur_state = tuple->next_state_1; /* * if this took us to an invalid state, we must have reached * the final state of a sub-machine - we can go no further * from here, so there are no loops in this branch */ if (cur_state == RE_STATE_INVALID) return FALSE; break; case RE_TEXT_BEGIN: case RE_TEXT_END: case RE_LOOKBACK_POS: case RE_WORD_BEGIN: case RE_WORD_END: case RE_WORD_BOUNDARY: case RE_NON_WORD_BOUNDARY: case RE_ZERO_VAR: /* * none of these transitions consumes input, so any of these * could result in an infinite loop - continue down the * current path */ cur_state = tuple->next_state_1; break; case RE_ASSERT_POS: case RE_ASSERT_NEG: case RE_ASSERT_BACKPOS: case RE_ASSERT_BACKNEG: /* * Assertions have sub-machines that are independent of the * main machine - there are no state transitions between the * main machine and a sub-machine. Break loops in the * sub-machine recursively. Note that this is a whole new loop * break test - we don't share the stack with the enclosing * machine. */ if (break_loops(tuple->info.sub.init, tuple->info.sub.final, 0)) { /* * The whole sub-machine was a loop, which makes it an * automatic zero-length match - jump immediately to its * final state on evaluation. */ tuple->info.sub.init = tuple->info.sub.final; } /* assertions don't consume input, so continue on this path */ cur_state = tuple->next_state_1; break; default: /* * all other states consume input, so this branch definitely * can't loop back to the original state without consuming * input - we do not need to proceed any further down the * current branch, since it's not an infinite epsilon loop * even if it does ultimately find its way back to the * initial state */ return FALSE; } } } /* * Optimization: remove meaningless branch-to-branch transitions. An * "epsilon" state that has only one outgoing transition does nothing * except move to the next state, so any transition that points to such a * state could just as well point to the destination of the epsilon's one * outgoing transition. */ void CRegexParser::remove_branch_to_branch(re_machine *machine) { /* make sure the initial state points to the first meaningful state */ optimize_transition(machine, &machine->init); /* scan all active states */ re_state_id cur; re_tuple *tuple; for (cur = 0, tuple = tuple_arr_ ; cur < next_state_ ; ++cur, ++tuple) { /* if this isn't the final state, check it */ if (cur != machine->final) { /* check both of our outgoing transitions */ optimize_transition(machine, &tuple->next_state_1); optimize_transition(machine, &tuple->next_state_2); } } } /* * Optimize a single transition to remove meaningless branch-to-branch * transitions. */ void CRegexParser::optimize_transition(const re_machine *machine, re_state_id *trans) { /* keep going as long as we find meaningless forwarding branches */ for (;;) { re_tuple *tuple_nxt; /* * if this transition points to the machine's final state, there's * nowhere else to go from here */ if (*trans == RE_STATE_INVALID || *trans == machine->final) return; /* get the state the '*trans' is currently pointing to */ tuple_nxt = &tuple_arr_[*trans]; /* * if the transition points to anything other than a single-branch * epsilon, we point to a meaningful next state, so there's no * further branch-to-branch elimination we can perform */ if (tuple_nxt->typ != RE_EPSILON || tuple_nxt->next_state_2 != RE_STATE_INVALID) return; /* * This transition points to a meaningless intermediate state, so * we can simply skip the intermediate state and go directly from * the current state to the target state's single target. Once * we've done this, continue scanning, because we might find that * the new target state itself is a meaningless intermediate state * that we can skip past as well (and so on, and so on - keep * going until we find a real target state). */ *trans = tuple_nxt->next_state_1; } } /* * Sub-machine match length arithmetic. This is just like ordinary * arithmetic, but with the added twist of infinities (-1 represents * infinity in our scheme). */ inline static int gml_add(int a, int b) { /* infinity + x == infinity for all x; otherwise return the normal sum */ return (a < 0 || b < 0 ? -1 : a + b); } inline static void gml_add(int *a, int b) { *a = gml_add(*a, b); } inline static int gml_mul(int a, int b) { /* infinity * x == infinity for all x */ return (a < 0 || b < 0 ? -1 : a * b); } inline static int gml_min(int a, int b) { /* x < infinity for all x */ return (a < 0 ? b : b < 0 || a < b ? a : b); } inline static int gml_max(int a, int b) { /* infinity > x for all x */ return (a < 0 || b < 0 ? -1 : a > b ? a : b); } /* calculate the match length of a submachine */ void CRegexParser::get_match_length(re_state_id init, re_state_id final, int *minlen, int *maxlen, regex_scan_frame *stack) { /* set up a stack frame in case we need to recurse */ regex_scan_frame fr(stack); /* start with zero lengths */ *minlen = *maxlen = 0; /* iterate through the machine */ for (re_state_id i = init ; i != final ; ) { /* remember the current state in our stack frame */ fr.st = i; /* get this tuple */ re_tuple *t = &tuple_arr_[i]; /* mark this state as visited, to detect infinite loops */ t->flags |= RE_STATE_CYCLE_TEST; /* check what we have */ switch (t->typ) { case RE_TEXT_BEGIN: case RE_TEXT_END: case RE_LOOKBACK_POS: case RE_WORD_BEGIN: case RE_WORD_END: case RE_WORD_BOUNDARY: case RE_NON_WORD_BOUNDARY: case RE_GROUP_ENTER: case RE_GROUP_EXIT: case RE_ASSERT_POS: case RE_ASSERT_NEG: case RE_ASSERT_BACKPOS: case RE_ASSERT_BACKNEG: case RE_ZERO_VAR: /* * assertions, group entries, and loop entries contribute * nothing to the match length - continue to the onward branch */ i = t->next_state_1; break; case RE_EPSILON: /* * for single-transition epsilons, simply proceed to the next * state */ if (t->next_state_2 == RE_STATE_INVALID) { i = t->next_state_1; break; } /* * Epsilon transitions have two lengths: the length of the * first branch, and the length of the second branch. * Calculate the two branches separately, and take the lower of * the minimum values and the higher of the maximum values. * * Before recursing, check to see if we're looping. If we've * recursed from the current state already in this search, * we're in a loop, so we have a maximum length of infinity. */ if (fr.is_recursing()) { /* we're looping, so we have an unbounded maximum length */ *maxlen = -1; } else { /* * we're not looping yet, so we can recursively calculate * the length of each sub-branch */ int min1, min2, max1, max2; get_match_length(t->next_state_1, final, &min1, &max1, &fr); get_match_length(t->next_state_2, final, &min2, &max2, &fr); /* add the lower min and the higher max to the total */ gml_add(minlen, gml_min(min1, min2)); gml_add(maxlen, gml_max(max1, max2)); } /* we've fully considered both branches, so we're done */ return; case RE_LOOP_BRANCH: /* * A loop branch contributes (min..max) * the length of the * "enter" branch (the first epsilon transition). Check that * we're not recursing first; if so, we have an unbounded * maximum length. */ if (fr.is_recursing()) { /* looping - the maximum is unbounded */ *maxlen = -1; } else { /* * Calculate the length of the loop branch. Note that the * loop ends at the current state. */ int loopmin, loopmax; get_match_length(t->next_state_1, i, &loopmin, &loopmax, &fr); /* add the loop min and max to the running total */ gml_add(minlen, gml_mul(loopmin, t->info.loop.loop_min)); gml_add(maxlen, gml_mul(loopmax, t->info.loop.loop_max)); } /* continue from the post-loop state */ i = t->next_state_2; break; case RE_GROUP_MATCH: /* * A group matcher matches the same length as its corresponding * group. Find the group entry state, then measure its length * and use it as the result. * * As usual, before recursively calculating the group length, * we need to make sure we're not recursing from the current * state. */ if (fr.is_recursing()) { /* looping, so the length of this branch is unbounded */ *maxlen = -1; } else { /* locate the enter/exit states for the group */ re_state_id g, genter, gexit; re_tuple *gt; for (g = 0, gt = tuple_arr_, genter = gexit = -1 ; g < next_state_ ; ++g, ++gt) { if (gt->typ == RE_GROUP_ENTER && gt->info.ch == t->info.ch) genter = g; else if (gt->typ == RE_GROUP_EXIT && gt->info.ch == t->info.ch) gexit = g; } /* if we found the group, figure its length */ if (genter >= 0 && gexit >= 0) { /* figure the group length range */ int gmin, gmax; get_match_length(genter, gexit, &gmin, &gmax, &fr); /* add the group min and max to the running totals */ gml_add(minlen, gmin); gml_add(maxlen, gmax); } } /* move on to the next state */ i = t->next_state_1; break; default: /* everything else matches a single character */ gml_add(minlen, 1); gml_add(maxlen, 1); /* on to the next state */ i = t->next_state_1; break; } } } /* * Consolidate consecutive character transitions into strings */ void CRegexParser::consolidate_strings(re_machine *machine) { /* run through the tuples looking for runs of characters */ re_state_id i; re_tuple *t; for (i = 0, t = tuple_arr_ ; i < next_state_ ; ++i, ++t) { /* if this is a literal, try consolidating it */ if (t->typ == RE_LITERAL) { /* count up the literals that follow */ int cnt = 1; for (re_state_id j = t->next_state_1 ; j != RE_STATE_INVALID && tuple_arr_[j].typ == RE_LITERAL ; j = tuple_arr_[j].next_state_1, ++cnt) ; /* if we found at least one more literal, consolidate it */ if (cnt > 1) { /* convert this node to a string node */ wchar_t ch = t->info.ch; t->typ = RE_LITSTR; t->info.str.str = new wchar_t[cnt+1]; t->info.str.str[0] = ch; t->info.str.str[cnt] = 0; /* copy the string characters */ int k = 1; for (re_tuple *tj = &tuple_arr_[t->next_state_1] ; k < cnt ; tj = &tuple_arr_[tj->next_state_1], ++k) { /* add this character to the string */ t->info.str.str[k] = tj->info.ch; /* skip the state in the starting state */ t->next_state_1 = tj->next_state_1; /* make this state an alias of the original */ tj->typ = RE_LITSTRA; tj->info.str.str = &t->info.str.str[k]; tj->info.str.src = i; } } } } } /* ------------------------------------------------------------------------ */ /* * Compile an expression and return a newly-allocated pattern object. */ re_status_t CRegexParser::compile_pattern(const char *expr_str, size_t exprlen, re_compiled_pattern **pattern) { /* presume we won't allocate a new pattern object */ *pattern = 0; /* compile the pattern */ re_compiled_pattern_base base_pat; re_status_t stat = compile(expr_str, exprlen, &base_pat); if (stat != RE_STATUS_SUCCESS) return stat; /* * Start off with our basic space needs: we need space for the base * structure, plus space for re_tuple array (actually, space for the * number of allocated tuples minus one, since there's one built into * the base structure). */ re_compiled_pattern *pat = 0; size_t siz = sizeof(re_compiled_pattern) + (base_pat.tuple_cnt - 1)*sizeof(pat->tuples[0]); /* * Run through the tuples in the result machine and add up the amount * of extra space we'll need for extra allocations (specifically, * character ranges). */ re_state_id i; re_tuple *t; for (i = 0, t = tuple_arr_ ; i < base_pat.tuple_cnt ; ++i, ++t) { /* check what kind of tuple this is */ switch(t->typ) { case RE_RANGE: case RE_RANGE_EXCL: /* range - add in space for the character range data */ siz += sizeof(t->info.range.char_range[0]) * t->info.range.char_range_cnt; break; case RE_LITSTR: /* literal string - add in space for the string data */ siz += (wcslen(t->info.str.str)+1) * sizeof(t->info.str.str); break; default: /* other types require no extra storage */ break; } } /* allocate space for the structure */ *pattern = pat = (re_compiled_pattern *)t3malloc(siz); /* copy the base pattern to the result */ memcpy(pat, &base_pat, sizeof(base_pat)); /* copy the tuple array */ memcpy(pat->tuples, tuple_arr_, pat->tuple_cnt * sizeof(pat->tuples[0])); /* * Pack the range data onto the end of the tuple array in the data * structure. We already calculated how much space we'll need for this * and included the space in the structure's allocation, so we simply * need to find the location of the range data at the end of our tuple * array. */ wchar_t *rp = (wchar_t *)&pat->tuples[pat->tuple_cnt]; /* * run through the new tuple array and pack the range data into the new * allocation unit */ for (i = 0, t = pat->tuples ; i < next_state_ ; ++i, ++t) { /* check what kind of tuple this is */ switch(t->typ) { case RE_RANGE: case RE_RANGE_EXCL: /* * Character range. Copy the original character range data * into the new allocation unit, at the next available location * in the allocation unit. */ memcpy(rp, t->info.range.char_range, t->info.range.char_range_cnt * sizeof(t->info.range.char_range[0])); /* fix up the tuple to point to the packed copy */ t->info.range.char_range = rp; /* move past the space in our allocation unit */ rp += t->info.range.char_range_cnt; break; case RE_LITSTR: /* literal string - copy it into the allocation unit */ wcscpy(rp, t->info.str.str); /* fix up the tuple to point to the packed copy */ t->info.str.str = (wchar_t *)rp; /* move past the space in our allocation unit */ rp += wcslen(rp) + 1; break; case RE_LITSTRA: /* literal string alias */ { /* get the original base string from the source state */ re_state_id src = t->info.str.src; wchar_t *str = tuple_arr_[src].info.str.str; /* get the offset */ int ofs = t->info.str.str - str; /* point to the the copied string the new state copy */ t->info.str.str = pat->tuples[src].info.str.str + ofs; } break; default: /* other types contain no range data */ break; } } /* success */ return stat; } /* * free a pattern previously created with compile_pattern() */ void CRegexParser::free_pattern(re_compiled_pattern *pattern) { /* we allocate each pattern as a single unit, so it's easy to free */ t3free(pattern); } /* ------------------------------------------------------------------------ */ /* * Register delta list. * * When we discard a stack frame, we need to propagate its register deltas * to the parent frame. Each frame only stores the deltas from its initial * state, so whenever we decide to discard a frame and keep that branch, we * have to add its deltas to its parent frame. * * This class is a little tricky. It points directly into the CRegexStack * object's memory, so it's only valid as long as you don't push another * frame. For our immediate purposes this is fine, since we only need to * preserve an old frame long enough to propagate its changes into its * parent. But be aware if you're repurposing this class that an instance * lifetime is limited by external factors (i.e., pushing a new stack * frame). (Also note that we're NOT talking about the native machine * stack - there are no native interrupt reentrancy issues to worry about, * for example.) */ class reg_deltas { public: reg_deltas(CRegexStack &s) { /* record the current top frame information */ regex_stack_entry *fp = (regex_stack_entry *)(s.buf_ + s.sp_); varp = (regex_stack_var *)(fp + 1); varend = (regex_stack_var *)(s.buf_ + s.used_); } /* * Propagate the initial values from our stack frame to the current top * of stack. This pushes the saved initial values from this frame into * the current frame. */ void propagate(CRegexStack &s) { for (regex_stack_var *v = varp ; v < varend ; ++v) s.propagate(v); } protected: /* variable list range */ regex_stack_var *varp, *varend; }; /* ------------------------------------------------------------------------ */ /* * Pattern recognizer */ /* * construct */ CRegexSearcher::CRegexSearcher() { /* by default, use case-sensitive searches if not otherwise specified */ default_case_sensitive_ = TRUE; } /* * delete */ CRegexSearcher::~CRegexSearcher() { } /* * Match a string to a compiled expression. Returns the length of the * match if successful, or -1 if no match was found. */ int CRegexSearcher::match(const char *entire_str, size_t entire_str_len, const char *str, const size_t origlen, const re_compiled_pattern_base *pattern, const re_tuple *tuple_arr, const re_machine *machine, re_group_register *regs, short *loop_vars) { /* * if the case sensitivity was specified, it overrides the current * search defaults; otherwise apply the search defaults */ int case_sensitive = (pattern->case_sensitivity_specified ? pattern->case_sensitive : default_case_sensitive_); /* macro to perform a "local return" */ int _retval_; #define local_return(retval) \ _retval_ = (retval); \ goto do_local_return; /* reset the stack */ stack_.reset(); /* start at the machine's initial and final states */ re_state_id cur_state = machine->init; re_state_id final_state = machine->final; /* * if we're starting in the final state, this is a zero-length * pattern, which matches anything - immediately return success with a * zero-length match */ if (cur_state == final_state) return 0; /* start at the beginning of the string */ utf8_ptr p((char *)str); size_t curlen = origlen; /* note the offset from the start of the entire string */ int start_ofs = str - entire_str; /* note where the overall string ends */ const char *entire_str_end = entire_str + entire_str_len; /* run the machine */ for (;;) { /* get the tuple for this state */ const re_tuple *tuple = &tuple_arr[cur_state]; /* check the type of state we're processing */ switch(tuple->typ) { case RE_ZERO_VAR: /* save the variable in the stack frame if necessary */ stack_.save_loop_var(tuple->info.loop.loop_var, loop_vars); /* zero the loop variable and proceed */ loop_vars[tuple->info.loop.loop_var] = 0; break; case RE_LOOP_BRANCH: /* * This is a loop branch. Check the variable to see if we've * satisfied the loop condition yet: * * - If we haven't yet reached the minimum loop count, simply * transition into the loop (i.e., take the 'enter' branch * unconditionally). * * - If we have reached the maximum loop count (if there is * one - if max is -1 then there's no upper bound), simply * transition past the loop (i.e., take the 'bypass' branch * unconditionally). * * - Otherwise, we must treat this just like an ordinary * two-way epsilon branch. * * Note that, if we have a 'shortest' modifier, the bypass * branch will be first, to encourage the indeterminate check * to choose the short branch whenever possible; otherwise the * enter branch will be first, so we take the long branch * whenever possible. */ { short *varptr; /* get the variable value */ varptr = &loop_vars[tuple->info.loop.loop_var]; /* save the variable in the stack frame if necessary */ stack_.save_loop_var(tuple->info.loop.loop_var, loop_vars); /* * if we're not at the loop minimum yet, transition into * the loop body */ if (*varptr < tuple->info.loop.loop_min) { /* increment the loop counter */ ++(*varptr); /* unconditionally transfer into the loop */ if ((tuple->flags & RE_STATE_SHORTEST) == 0) cur_state = tuple->next_state_1; else cur_state = tuple->next_state_2; /* we're done processing this state */ goto got_next_state; } /* * if we've reached the loop maximum (if there is one), * transition past the loop */ if (tuple->info.loop.loop_max >= 0 && *varptr >= tuple->info.loop.loop_max) { /* unconditionally skip the loop */ if ((tuple->flags & RE_STATE_SHORTEST) == 0) cur_state = tuple->next_state_2; else cur_state = tuple->next_state_1; /* we're done with this state */ goto got_next_state; } /* * We don't know which way to go, so we must treat this as * a two-way epsilon branch. Count this as another loop * iteration, since the branch that enters the loop will * come back here for another try. The branch that skips * the loop doesn't care about the loop counter any more, * so we can just increment it and ignore the skip branch. */ ++(*varptr); goto two_way_epsilon; } /* not reached */ case RE_GROUP_MATCH: { int group_num; re_group_register *group_reg; size_t reg_len; /* it's a group - get the group number */ group_num = (int)tuple->info.ch; group_reg = ®s[group_num]; /* * if this register isn't defined, there's nothing to * match, so fail */ if (group_reg->start_ofs == -1 || group_reg->end_ofs == -1) { local_return(-1); } /* calculate the length of the register value */ reg_len = group_reg->end_ofs - group_reg->start_ofs; /* if we don't have enough left to match, it fails */ if (curlen < reg_len) { local_return(-1); } /* if the string doesn't match exactly, we fail */ if (memcmp(p.getptr(), entire_str + group_reg->start_ofs, reg_len) != 0) { local_return(-1); } /* * It matches exactly - skip the entire byte length of the * register in the source string */ p.set(p.getptr() + reg_len); curlen -= reg_len; } break; case RE_TEXT_BEGIN: /* * Match only the exact beginning of the string - if we're * anywhere else, this isn't a match. If this succeeds, we * don't skip any characters. */ if (p.getptr() != entire_str) { local_return(-1); } break; case RE_TEXT_END: /* * Match only the exact end of the string - if we're anywhere * else, this isn't a match. Don't skip any characters on * success. */ if (p.getptr() != entire_str_end) { local_return(-1); } break; case RE_LOOKBACK_POS: /* * Match the position of the parent lookback assertion. If * we're anywhere else, this isn't a match. This ensures that * we only match lookback assertions that end up at the match * position of the assertion. */ if (p.getptr() - entire_str != stack_.get_lookback_pos()) { local_return(-1); } break; case RE_WORD_BEGIN: /* * If the previous character is a word character, we're not at * the beginning of a word. If we're at the beginning of the * entire string, we need not check anything previous - * there's no previous character, so we can't have a preceding * word character. */ if (p.getptr() != entire_str && is_word_char(p.getch_before(1))) { local_return(-1); } /* * if we're at the end of the string, or the current character * isn't a word character, we're not at the beginning of a * word */ if (p.getptr() == entire_str_end || !is_word_char(p.getch())) { local_return(-1); } break; case RE_WORD_END: /* * if the current character is a word character, we're not at * the end of a word */ if (p.getptr() != entire_str_end && is_word_char(p.getch())) { local_return(-1); } /* * if we're at the beginning of the string, or the previous * character is not a word character, we're not at the end of * a word */ if (p.getptr() == entire_str || !is_word_char(p.getch_before(1))) { local_return(-1); } break; case RE_WORD_CHAR: /* if it's not a word character, it's a failure */ if (curlen == 0 || !is_word_char(p.getch())) { local_return(-1); } /* skip this character of input */ p.inc(&curlen); break; case RE_NON_WORD_CHAR: /* if it's a word character, it's a failure */ if (curlen == 0 || is_word_char(p.getch())) { local_return(-1); } /* skip the input */ p.inc(&curlen); break; case RE_WORD_BOUNDARY: case RE_NON_WORD_BOUNDARY: { /* * Determine if the previous character is a word character. * If we're at the beginning of the string, it's obviously * not, otherwise check the classification of the previous * character. */ int prev_is_word = (p.getptr() != entire_str && is_word_char(p.getch_before(1))); /* make the same check for the current character */ int next_is_word = (p.getptr() != entire_str_end && is_word_char(p.getch())); /* * Determine if this is a boundary - it is if the two * states are different */ int boundary = ((prev_is_word != 0) ^ (next_is_word != 0)); /* * make sure it matches what was desired, and return * failure if not */ if ((tuple->typ == RE_WORD_BOUNDARY && !boundary) || (tuple->typ == RE_NON_WORD_BOUNDARY && boundary)) { local_return(-1); } } break; case RE_WILDCARD: /* make sure we have a character to match */ if (curlen == 0) { local_return(-1); } /* skip this character */ p.inc(&curlen); break; case RE_RANGE: case RE_RANGE_EXCL: { /* make sure we have a character to match */ if (curlen == 0) { local_return(-1); } /* get this character */ wchar_t ch = p.getch(); /* search for the character in the range */ size_t i; wchar_t *rp; for (i = tuple->info.range.char_range_cnt, rp = tuple->info.range.char_range ; i != 0 ; i -= 2, rp += 2) { /* * check for a class specifier; if it's not a class * specifier, treat it as a literal range, and check * case sensitivity */ if (rp[0] == '\0') { /* * The first character of the range pair is null, * which means that this isn't a literal range but * rather a class. Check for a match to the * class. */ int match; switch(rp[1]) { case RE_ALPHA: match = t3_is_alpha(ch); break; case RE_DIGIT: match = t3_is_digit(ch); break; case RE_UPPER: match = t3_is_upper(ch); break; case RE_LOWER: match = t3_is_lower(ch); break; case RE_ALPHANUM: match = t3_is_alpha(ch) || t3_is_digit(ch); break; case RE_SPACE: match = t3_is_space(ch); break; case RE_VSPACE: match = t3_is_vspace(ch); break; case RE_PUNCT: match = t3_is_punct(ch); break; case RE_NEWLINE: match = (ch == 0x000A || ch == 0x000D || ch == 0x000B || ch == 0x2028 || ch == 0x2029); break; case RE_NULLCHAR: match = (ch == 0); break; default: /* this shouldn't happen */ match = FALSE; break; } /* * if we matched, we can stop looking; otherwise, * simply keep going, since there might be another * entry that does match */ if (match) break; } else if (case_sensitive) { /* * the search is case-sensitive - compare the * character to the range without case conversion */ if (ch >= rp[0] && ch <= rp[1]) break; } else if (rp[0] == rp[1]) { /* * single character, case-insensitive - try * matching literally, and against the full case * folding of this one character */ size_t matchlen; if (ch == rp[0]) { /* exact match to the literal */ break; } if (t3_compare_case_fold( &rp[0], 1, p.getptr(), curlen, &matchlen) == 0) { /* * matched - skip the folded match, then back * up one to make up for the common inc() we * always do */ p.inc_bytes(matchlen); curlen -= matchlen; p.dec(&curlen); /* stop looking */ break; } } else { /* * code point range, case-sensitive - use simple * case folding for all three characters */ wchar_t fch = t3_simple_case_fold(ch); if (fch >= t3_simple_case_fold(rp[0]) && fch <= t3_simple_case_fold(rp[1])) break; } } /* we matched if we stopped before exhausting the list */ int match = (i != 0); /* make sure we got what we wanted */ if ((tuple->typ == RE_RANGE && !match) || (tuple->typ == RE_RANGE_EXCL && match)) { local_return(-1); } /* skip this character of the input */ p.inc(&curlen); } break; case RE_ALPHA: /* check for an alphabetic character */ if (curlen == 0 || !t3_is_alpha(p.getch())) { local_return(-1); } /* skip this character of the input, and continue */ p.inc(&curlen); break; case RE_DIGIT: /* check for a digit character */ if (curlen == 0 || !t3_is_digit(p.getch())) { local_return(-1); } /* skip this character of the input, and continue */ p.inc(&curlen); break; case RE_NON_DIGIT: /* check for a non-digit character */ if (curlen == 0 || t3_is_digit(p.getch())) { local_return(-1); } /* skip this character of the input, and continue */ p.inc(&curlen); break; case RE_UPPER: /* check for an upper-case alphabetic character */ if (curlen == 0 || !t3_is_upper(p.getch())) { local_return(-1); } /* skip this character of the input, and continue */ p.inc(&curlen); break; case RE_LOWER: /* check for a lower-case alphabetic character */ if (curlen == 0 || !t3_is_lower(p.getch())) { local_return(-1); } /* skip this character of the input, and continue */ p.inc(&curlen); break; case RE_ALPHANUM: /* check for an alphabetic or digit character */ if (curlen == 0 || (!t3_is_alpha(p.getch()) && !t3_is_digit(p.getch()))) { local_return(-1); } /* skip this character of the input, and continue */ p.inc(&curlen); break; case RE_SPACE: /* check for a space of some kind */ if (curlen == 0 || !t3_is_space(p.getch())) { local_return(-1); } /* skip this character of the input, and continue */ p.inc(&curlen); break; case RE_NON_SPACE: /* check for a space of some kind */ if (curlen == 0 || t3_is_space(p.getch())) { local_return(-1); } /* skip this character of the input, and continue */ p.inc(&curlen); break; case RE_VSPACE: /* check for a vertical space of some kind */ if (curlen == 0 || !t3_is_vspace(p.getch())) { local_return(-1); } /* skip this character of the input, and continue */ p.inc(&curlen); break; case RE_NON_VSPACE: /* check for a vertical space of some kind */ if (curlen == 0 || t3_is_vspace(p.getch())) { local_return(-1); } /* skip this character of the input, and continue */ p.inc(&curlen); break; case RE_PUNCT: /* check for a punctuation character of some kind */ if (curlen == 0 || !t3_is_punct(p.getch())) { local_return(-1); } /* skip this character of the input, and continue */ p.inc(&curlen); break; case RE_NEWLINE: /* check for a newline character of some kind */ { wchar_t ch; /* if we're out of characters, we don't have a match */ if (curlen == 0) { local_return(-1); } /* get the character */ ch = p.getch(); /* if it's not a newline, we fail this match */ if (ch != 0x000A && ch != 0x000d && ch != 0x000b && ch != 0x2028 && ch != 0x2029) { local_return(-1); } } /* skip this character of input and continue */ p.inc(&curlen); break; case RE_ASSERT_POS: case RE_ASSERT_NEG: case RE_ASSERT_BACKPOS: case RE_ASSERT_BACKNEG: /* * It's an assertion. Run this as a sub-state: push the * current state so that we can come back to it later. */ stack_.push(ST_ASSERT, start_ofs, p.getptr() - entire_str, curlen, cur_state, final_state, 0); /* * In the sub-state, start with the sub-machine's initial * state and finish with the sub-machine's final state. */ cur_state = tuple->info.sub.init; final_state = tuple->info.sub.final; /* in the sub-state, the sub-string to match starts here */ start_ofs = p.getptr() - entire_str; /* * in the sub-state, we can use the entire source string, since * it's only an assertion - any text the assertion matches * after the end of the search region won't be consumed in the * enclosing state, so it's fair game within the assertion */ curlen = entire_str_len - start_ofs; /* * If this is a back assertion, figure the range of match * lengths of the assertion. We will need to back up by the * minimum match length and try the assertion once; if that * fails for a positive assertion or succeeds for a negative * assertion, we will then need to back up one more character * and try again, and iterate until we've exhausted the range * or found a success (positive) or failure (negative). */ if (tuple->typ == RE_ASSERT_BACKPOS || tuple->typ == RE_ASSERT_BACKNEG) { /* * Back up by the minimum match length. We can't possibly * have a match for the assertion that starts after this * point, because it has to end by the current position and * be at least the minimum length long. */ int l = tuple->info.sub.minlen; for ( ; l > 0 && p.getptr() != entire_str ; p.dec(&curlen), --l) ; /* * If we were unable to back up by the required minimum * length, we definitely cannot have a match for the * assertion. If this is a positive assertion, return * failure; if this is a negative assertion, it succeeds by * virtue of being unable to match. */ if (l != 0) { /* not enough characters -> definitely can't match */ local_return(-1); } } /* * just proceed from here; we'll finish up with the assertion * test when we reach the final sub-machine state and pop the * stack state we pushed */ goto got_next_state; case RE_LITERAL: /* * ordinary character match - if there's not another * character, we obviously fail */ if (curlen == 0) { local_return(-1); } /* * check case sensitivity - if we're not in case-sensitive * mode, and both characters are alphabetic, perform a * case-insensitive comparison; otherwise, perform an exact * comparison */ if (tuple->info.ch == p.getch()) { /* * we have an exact match; there's no need to check for * any case conversions */ p.inc(&curlen); } else if (!case_sensitive) { /* * case-insensitive - compare the single pattern character * against the source string using full case folding */ size_t matchlen; if (t3_compare_case_fold( &tuple->info.ch, 1, p.getptr(), curlen, &matchlen) != 0) { local_return(-1); } /* matched - skip the matched string characters */ p.inc_bytes(matchlen); curlen -= matchlen; } else { /* * the search is case-sensitive, or this pattern character * is non-alphabetic; since we didn't find an exact match, * the string does not match the pattern */ local_return(-1); } break; case RE_LITSTR: case RE_LITSTRA: /* literal string - if we're out of characters, fail */ if (curlen == 0) { local_return(-1); } /* compare identically or with case folding, as appropriate */ if (case_sensitive) { /* do an exact character-by-character match */ utf8_ptr p2 = p; size_t curlen2 = curlen; wchar_t *cp = tuple->info.str.str; for ( ; *cp != 0 && curlen2 != 0 ; ++cp, p2.inc(&curlen2)) { if (p2.getch() != *cp) { local_return(-1); } } /* if we didn't match the whole pattern literal, fail */ if (*cp != 0) { local_return(-1); } /* matched - consume the matched source text */ p = p2; curlen = curlen2; } else { /* match with case folding */ size_t matchlen; if (t3_compare_case_fold( tuple->info.str.str, wcslen(tuple->info.str.str), p.getptr(), curlen, &matchlen) != 0) { local_return(-1); } /* matched - skip the matched string characters */ p.inc_bytes(matchlen); curlen -= matchlen; } break; case RE_GROUP_ENTER: /* * if the group index (given by the state's character ID) is * in range, note the location in the string where the group * starts */ if (tuple->info.ch < RE_GROUP_REG_CNT) { /* save the register in the stack frame if necessary */ stack_.save_group_reg(tuple->info.ch, regs); /* store the new value */ regs[tuple->info.ch].start_ofs = p.getptr() - entire_str; } break; case RE_GROUP_EXIT: /* * note the string location of the end of the group, if the * group ID is in range */ if (tuple->info.ch < RE_GROUP_REG_CNT) { /* save the register in the stack frame if necessary */ stack_.save_group_reg(tuple->info.ch, regs); /* store the new value */ regs[tuple->info.ch].end_ofs = p.getptr() - entire_str; } break; case RE_EPSILON: /* it's an epsilon transition */ if (tuple->next_state_2 == RE_STATE_INVALID) { /* * We have only one transition, so this state is entirely * deterministic. Simply move on to the next state. (We * try to optimize these types of transitions out of the * machine when compiling, but we handle them anyway in * case any survive optimization.) */ cur_state = tuple->next_state_1; } else { two_way_epsilon: /* * This state has two possible transitions, and we don't * know which one to take. So, try both, see which one * works better, and return the result. Try the first * transition first. * * To test both branches, we push the machine state onto * the stack, test the first branch, then restore the * machine state and test the second branch. We return * the better of the two branches. * * Set up to test the first branch by pushing the current * machine state, noting that we're in the first branch of * a two-branch epsilon so that we'll know to proceed to * the second branch when we finish with the first one. */ stack_.push(ST_EPS1, start_ofs, p.getptr() - entire_str, curlen, cur_state, final_state, 0); /* the next state is the first branch of the epsilon */ cur_state = tuple->next_state_1; /* match the sub-string from here */ start_ofs = p.getptr() - entire_str; /* we have the next state set to go */ goto got_next_state; } break; default: /* invalid node type */ assert(FALSE); local_return(-1); } /* * if we got this far, we were successful - move on to the next * state */ cur_state = tuple->next_state_1; got_next_state: /* we come here when we've already figured out the next state */ ; /* * If we're in the final state, it means we've matched the * pattern. Return success by indicating the length of the string * we matched. */ if (cur_state == final_state) { local_return(p.getptr() - (entire_str + start_ofs)); } /* * if we're in an invalid state, the expression must have been * ill-formed; return failure */ if (cur_state == RE_STATE_INVALID) { local_return(-1); } /* resume the loop */ continue; do_local_return: /* check what kind of state we're returning to */ switch(stack_.get_top_type()) { int str_ofs; int ret1, ret2; int ret1_is_winner; int iter; re_recog_type typ; int lblen; case -1: /* * There's nothing left on the state stack, so we're actually * returning to our caller. Simply return the given return * value. */ return _retval_; case ST_EPS1: /* * We're finishing the first branch of a two-branch epsilon. * Swap the current state with the saved state on the stack, * and push a new state for the second branch. */ str_ofs = p.getptr() - entire_str; stack_.swap_and_push(_retval_, ST_EPS2, &start_ofs, &str_ofs, &curlen, &cur_state, &final_state, regs, loop_vars); /* the next state is the second branch */ cur_state = tuple_arr[cur_state].next_state_2; /* set up at the original string position */ p.set((char *)entire_str + str_ofs); /* the second branch substring starts at the current character */ start_ofs = p.getptr() - entire_str; /* continue from here */ break; case ST_EPS2: /* * We're finishing the second branch of a two-branch epsilon. * First, get the two return values - the first branch is the * return value saved in the second-from-the-top stack frame, * and the second branch is the current return value. */ ret1 = stack_.get_frame(1)->retval; ret2 = _retval_; /* * If they both failed, the whole thing failed. Otherwise, * return the longer or shorter of the two, depending on the * current match mode, plus the length we ourselves matched * previously. Note that we return the register set from the * winning match. */ if (ret1 < 0 && ret2 < 0) { /* * They both failed - backtrack to the initial state by * popping the second branch's frame (which stores the * branch initial state) and discarding the first branch's * frame (which stores the first branch's *final* state). * Note that the only thing we care about from the stack is * the group registers and loop variables; everything else * will be restored from the enclosing frame that we're * returning to. */ stack_.pop(&start_ofs, &str_ofs, &curlen, &cur_state, &final_state, regs, loop_vars, &iter); stack_.discard(); /* set the string pointer */ p.set((char *)entire_str + str_ofs); /* return failure */ local_return(-1); } /* * Choose the winner based on the match mode. The match mode * of interest is the one in the original two-way epsilon, * which is the state in the stack frame at the top of the * stack. */ if (pattern->longest_match && !(tuple_arr[stack_.get_frame(0)->state].flags & RE_STATE_SHORTEST)) { /* * longest match - choose ret1 if ret2 isn't a good match, * or ret1 is longer than ret2 */ ret1_is_winner = (ret2 < 0 || ret1 >= ret2); } else { /* * shortest match - choose ret1 if it's the only good * match, or if they're both good and it's the shorter one */ ret1_is_winner = ((ret1 >= 0 && ret2 < 0) || (ret1 >= 0 && ret2 >= 0 && ret1 <= ret2)); } /* choose the winner */ if (ret1_is_winner) { /* * The winner is the first branch. First, pop the second * branch's initial state, since this is the baseline for * the first branch's final state. */ stack_.pop(&start_ofs, &str_ofs, &curlen, &cur_state, &final_state, regs, loop_vars, &iter); /* add in the length up to the initial state at the epsilon */ ret1 += str_ofs - start_ofs; /* * Get the list of register deltas in this stack frame. * We're keeping the final state for this branch for now, * so we'll need to propagate its deltas to the parent * frame. */ reg_deltas d(stack_); /* * Swap and pop the first branch's final state. This is * stored relative to the initial state of the branch, so * we're ready to restore it now. We swap the state so * that we have the initial values of the group registers * back in the frame, for propagation to the parent frame. */ stack_.swap_and_pop(&start_ofs, &str_ofs, &curlen, &cur_state, &final_state, regs, loop_vars, &iter); /* propagate the register deltas to the parent frame */ d.propagate(stack_); /* set the string pointer */ p.set((char *)entire_str + str_ofs); /* return the result */ local_return(ret1); } else { /* * The winner is the second branch. The current machine * state is the final state for the second branch, so we * simply need to discard the saved state for the two * branches. */ /* add in the length up to the initial state at the epsilon */ regex_stack_entry *fp = stack_.get_frame(0); ret2 += fp->str_ofs - fp->start_ofs; /* * Get the list of register deltas in the top frame. The * top frame is the initial state for the second branch, * which is the branch we're keeping, so we'll need to * propagate its deltas up to the parent frame. */ reg_deltas d(stack_); /* discard the two branch states */ stack_.discard(); stack_.discard(); /* propagate the register deltas to the parent frame */ d.propagate(stack_); /* return the result */ local_return(ret2); } case ST_ASSERT: /* * We're finishing an assertion sub-machine. First, pop the * machine state to get back to where we were. */ stack_.pop(&start_ofs, &str_ofs, &curlen, &cur_state, &final_state, regs, loop_vars, &iter); /* we look at the type a lot, so get it into a local */ typ = tuple_arr[cur_state].typ; /* * For a look-back assertion, figure the look-back length of * the current iteration. We start at iteration zero and work * up from there, so the lookback length is the minimum length * plus the iteration number. */ lblen = iter + tuple_arr[cur_state].info.sub.minlen; /* * set the string pointer and remaining length for the string * offset we popped from the state */ p.set((char *)entire_str + str_ofs); /* * If this is a look-back assertion, and it's not a match, * proceed to the next iteration. We have to do this for both * positive and negative look-backs: if it's positive and it's * not a match, we can't assume failure until we've looked for * the other possible match lengths; if it's negative and it's * not a match, we can't assume success until we've looked for * other possible matches. */ if (_retval_ < 0 && (typ == RE_ASSERT_BACKPOS || typ == RE_ASSERT_BACKNEG)) { /* get the next look-back length */ ++iter; ++lblen; /* * If this is still in range, go back for another try at * the new length. Note that a maximum of -1 means that * the range is unbounded. */ int maxlen = tuple_arr[cur_state].info.sub.maxlen; utf8_ptr ep((char *)entire_str); if ((maxlen < 0 || lblen <= maxlen) && ep.len(p.getptr() - entire_str) >= (size_t)lblen) { /* re-push our assertion sub-state */ stack_.push(ST_ASSERT, start_ofs, p.getptr() - entire_str, curlen, cur_state, final_state, iter); /* back up by lblen characters */ for ( ; lblen > 0 && p.getptr() != entire_str ; p.dec(&curlen), --lblen) ; /* recurse into the group */ final_state = tuple_arr[cur_state].info.sub.final; cur_state = tuple_arr[cur_state].info.sub.init; goto got_next_state; } } /* * If this is a positive assertion and it didn't match, OR it's * a negative assertion and it DID match, return failure */ if ((typ == RE_ASSERT_POS || typ == RE_ASSERT_BACKPOS) ? _retval_ < 0 : _retval_ >= 0) { local_return(-1); } /* resume where we left off */ cur_state = tuple_arr[cur_state].next_state_1; break; } /* resume from here */ goto got_next_state; } } /* ------------------------------------------------------------------------ */ /* * Search for a regular expression within a string. Returns -1 if the * string cannot be found, otherwise returns the offset from the start * of the string to be searched of the start of the first match for the * pattern. */ int CRegexSearcher::search(const char *entirestr, const char *str, size_t len, const re_compiled_pattern_base *pattern, const re_tuple *tuple_arr, const re_machine *machine, re_group_register *regs, int *result_len) { re_group_register best_match_regs[RE_GROUP_REG_CNT]; short loop_vars[RE_LOOP_VARS_MAX]; /* we don't have a match yet */ int best_match_len = -1; int best_match_start = -1; /* search the entire string */ const char *max_start_pos = str + len; /* figure the length of the overall string */ size_t entirelen = len + (str - entirestr); /* * Starting at the first character in the string, search for the * pattern at each subsequent character until we either find the * pattern or run out of string to test. */ utf8_ptr p; for (p.set((char *)str) ; p.getptr() <= max_start_pos ; p.inc(&len)) { /* check for a match */ int matchlen = match(entirestr, entirelen, p.getptr(), len, pattern, tuple_arr, machine, regs, loop_vars); if (matchlen >= 0) { /* check our first-begin/first-end mode */ if (pattern->first_begin) { /* * We're in first-begin mode: return immediately, * because we want to find the match that starts at the * earliest point in the string; having found a match * here, there's no point in looking for a later match, * since it obviously won't start any earlier than this * one does. */ *result_len = matchlen; return p.getptr() - str; } else { int keep; /* * We're in first-end mode. We can't return yet, * because we might find something that ends before this * string does. * * If this is the first or best match so far, note it; * otherwise, ignore it. In any case, continue looking. */ if (best_match_len == -1) { /* * this is our first match - it's definitely the * best so far */ keep = TRUE; } else { /* calculate the ending index of this match */ int end_idx = (p.getptr() - str) + matchlen; /* see what we have */ if (end_idx < best_match_start + best_match_len) { /* * this one ends earlier than the previous best * match so far -- we definitely want to keep * this one */ keep = TRUE; } else if (end_idx == best_match_start + best_match_len) { /* * This one ends at exactly the same point as * our best match so far. Decide which one to * keep based on the lengths. If we're in * longest-match mode, keep the longer one, * otherwise keep the shorter one. */ if (pattern->longest_match) { /* * longest-match mode - keep the old one, * since it's longer (it starts earlier and * ends at the same point) */ keep = FALSE; } else { /* * shortest-match mode - keep the new one, * since it's shorter (it starts later and * ends at the same point) */ keep = TRUE; } } else { /* * this one isn't as good as the previous best * -- ignore this one and keep our previous * winner */ keep = FALSE; } } /* if we're keeping this match, save it */ if (keep) { /* save the start index, length, and register contents */ best_match_start = p.getptr() - str; best_match_len = matchlen; memcpy(best_match_regs, regs, sizeof(best_match_regs)); /* * There's no point in looking for strings that * start beyond the end of the current match, * because they certainly won't end before this * match ends. So, adjust our end-of-loop marker to * point to the next character past the end of our * match. */ max_start_pos = p.getptr() + matchlen; } } } } /* if we found a previous match, return it */ if (best_match_len != -1) { /* set the caller's match length */ *result_len = best_match_len; /* copy the saved match registers into the caller's registers */ memcpy(regs, best_match_regs, sizeof(best_match_regs)); /* return the starting index of the match */ return best_match_start; } /* we didn't find a match */ return -1; } /* * Search backwards. In reverse searches, everything is mirrored. The * starting position 'str' is actually the boundary for the right side of * the match, so the match must end before this character. Likewise, the * endpoints and directions for <FirstBegin> and <FirstEnd> are mirrored. * The "beginning" endpoint is the right side of the match, and the * "ending" endpoint is the left side. "First" means closest to the * starting position, so a higher index is firstier than a lower index. */ int CRegexSearcher::search_back( const char *entirestr, const char *str, size_t len, const re_compiled_pattern_base *pattern, const re_tuple *tuple_arr, const re_machine *machine, re_group_register *regs, int *result_len) { re_group_register best_match_regs[RE_GROUP_REG_CNT]; short loop_vars[RE_LOOP_VARS_MAX]; /* we don't have a match yet */ int best_match_len = -1; int best_match_start = -1; /* figure the overall string length */ size_t entirelen = len + (str - entirestr); /* * For a reverse search, we only want matches up to the starting * position, so the length of the matchable portion for the first * search at the starting position is zero. Note that we still need * the entire string length, since patterns can have assertions that go * past the end of the matched text. */ len = 0; /* * Starting at the current position, search for the pattern at each * earlier character until we either find the pattern or run out of * string to test. */ utf8_ptr p; for (p.set((char *)str) ; ; p.dec(&len)) { /* check for a match */ int matchlen = match(entirestr, entirelen, p.getptr(), len, pattern, tuple_arr, machine, regs, loop_vars); if (matchlen >= 0) { /* check our first-begin/first-end mode */ if (pattern->first_begin) { /* * We're in first-beginning mode, which in a reverse search * means that we want the *end* of the match to have the * highest index. The end of the match is the endpoint * facing the starting point, so we want that endpoint to * be as close to the starting point as possible, which * means the one with the highest index. */ int keep; int end_idx = (p.getptr() - str) + matchlen; if (best_match_len == -1 || end_idx > best_match_start + best_match_len) { /* * this is the first match, or it's closer to the * starting point than the previous match - keep it */ keep = TRUE; } else if (end_idx == best_match_start + best_match_len && pattern->longest_match) { /* * this match has the same end index as the previous * match; and we know it has an earlier start index * (because our loop walks backwards through the * string), which means it's longer than the previous * match; and we're in longest match mode - so this is * a longer match that's equally close, so it's a * keeper */ keep = TRUE; } else { /* otherwise, we're not keeping this match */ keep = FALSE; } /* if we're keeping this match, save it */ if (keep) { /* save the start index, length, and register contents */ best_match_start = p.getptr() - str; best_match_len = matchlen; memcpy(best_match_regs, regs, sizeof(best_match_regs)); } } else { /* * We're in first-ending mode. In a reverse search, this * means that we want the *start* of the match (the end * farther from the starting point) to have the *highest* * index (so it's closer to the starting point). This is * the easy case for a reverse search, because the start * point is the loop iteration variable: every future match * will have a lower starting index (further away from the * starting point) since that's how we walk through the * string. So we'll never find a match with a starting * point as close as this one - meaning we can return this * one without doing any more looking. */ *result_len = matchlen; return str - p.getptr(); } } /* if we've reached the start of the string, there's nowhere to go */ if (p.getptr() == entirestr) break; } /* if we found a previous match, return it */ if (best_match_len != -1) { /* set the caller's match length */ *result_len = best_match_len; /* copy the saved match registers into the caller's registers */ memcpy(regs, best_match_regs, sizeof(best_match_regs)); /* * Return the starting index of the match. Note that the return * value is the number of bytes *before* the starting point, * whereas our internal counter is the index offset, which is * negative; so simply negate it for the return value. */ return -best_match_start; } /* we didn't find a match */ return -1; } /* ------------------------------------------------------------------------ */ /* * Search for a previously-compiled pattern within the given string. * Returns the offset of the match, or -1 if no match was found. */ int CRegexSearcher::search_for_pattern( const re_compiled_pattern *pattern, const char *entirestr, const char *searchstr, size_t searchlen, int *result_len, re_group_register *regs) { /* * search for the pattern in our copy of the string - use the copy so * that the group registers stay valid even if the caller deallocates * the original string after we return */ return search(entirestr, searchstr, searchlen, pattern, pattern->tuples, &pattern->machine, regs, result_len); } /* * Search backwards for a pattern */ int CRegexSearcher::search_back_for_pattern( const re_compiled_pattern *pattern, const char *entirestr, const char *searchstr, size_t searchlen, int *result_len, re_group_register *regs) { /* * search for the pattern in our copy of the string - use the copy so * that the group registers stay valid even if the caller deallocates * the original string after we return */ return search_back(entirestr, searchstr, searchlen, pattern, pattern->tuples, &pattern->machine, regs, result_len); } /* ------------------------------------------------------------------------ */ /* * Check for a match to a previously compiled expression. Returns the * length of the match if we found a match, -1 if we found no match. This * is not a search function; we merely match the leading substring of the * given string to the given pattern. */ int CRegexSearcher::match_pattern( const re_compiled_pattern *pattern, const char *entirestr, const char *searchstr, size_t searchlen, re_group_register *regs) { short loop_vars[RE_LOOP_VARS_MAX]; /* match the string */ return match(entirestr, searchlen + (searchstr - entirestr), searchstr, searchlen, pattern, pattern->tuples, &pattern->machine, regs, loop_vars); } /* ------------------------------------------------------------------------ */ /* * Compile an expression and check for a match. Returns the length of the * match if we found a match, -1 if we found no match. This is not a * search function; we merely match the leading substring of the given * string to the given pattern. * * If a pattern pattern will be used repeatedly in multiple searches or * matches, it is more efficient to compile the pattern once and then * re-use the compiled pattern for each search or match, since compiling a * pattern takes time. */ int CRegexSearcherSimple::compile_and_match( const char *patstr, size_t patlen, const char *entirestr, const char *searchstr, size_t searchlen) { re_compiled_pattern_base pat; short loop_vars[RE_LOOP_VARS_MAX]; /* no groups yet */ group_cnt_ = 0; /* clear the group registers */ clear_group_regs(); /* compile the expression - return failure if we get an error */ if (parser_->compile(patstr, patlen, &pat) != RE_STATUS_SUCCESS) return FALSE; /* remember the group count from the compiled pattern */ group_cnt_ = pat.group_cnt; /* match the string */ int m = match(entirestr, searchlen + (searchstr - entirestr), searchstr, searchlen, &pat, parser_->tuple_arr_, &pat.machine, regs_, loop_vars); /* save the match information on success */ if (m >= 0) { match_.start_ofs = searchstr - entirestr; match_.end_ofs = match_.start_ofs + m; } /* return the result */ return m; } /* ------------------------------------------------------------------------ */ /* * Compile an expression and search for a match within the given string. * Returns the offset of the match, or -1 if no match was found. * * If a pattern will be used repeatedly in multiple searches or matches, it * is more efficient to compile the pattern once, using compile_pattern(), * and then re-use the compiled pattern for each search, since compiling a * pattern takes time. */ int CRegexSearcherSimple::compile_and_search( const char *patstr, size_t patlen, const char *entirestr, const char *searchstr, size_t searchlen, int *result_len) { /* no groups yet */ group_cnt_ = 0; /* clear the group registers */ clear_group_regs(); /* compile the expression - return failure if we get an error */ re_compiled_pattern_base pat; if (parser_->compile(patstr, patlen, &pat) != RE_STATUS_SUCCESS) return -1; /* remember the group count from the compiled pattern */ group_cnt_ = pat.group_cnt; /* * search for the pattern in our copy of the string - use the copy so * that the group registers stay valid even if the caller deallocates * the original string after we return */ int m = search(entirestr, searchstr, searchlen, &pat, parser_->tuple_arr_, &pat.machine, regs_, result_len); /* save the match information on success */ if (m >= 0) { match_.start_ofs = m; match_.end_ofs = m + *result_len; } /* return the result */ return m; } /* * compile and search backwards */ int CRegexSearcherSimple::compile_and_search_back( const char *patstr, size_t patlen, const char *entirestr, const char *searchstr, size_t searchlen, int *result_len) { /* no groups yet */ group_cnt_ = 0; /* clear the group registers */ clear_group_regs(); /* compile the expression - return failure if we get an error */ re_compiled_pattern_base pat; if (parser_->compile(patstr, patlen, &pat) != RE_STATUS_SUCCESS) return -1; /* remember the group count from the compiled pattern */ group_cnt_ = pat.group_cnt; /* * search for the pattern in our copy of the string - use the copy so * that the group registers stay valid even if the caller deallocates * the original string after we return */ int m = search_back(entirestr, searchstr, searchlen, &pat, parser_->tuple_arr_, &pat.machine, regs_, result_len); /* save the match information on success */ if (m >= 0) { match_.start_ofs = searchstr - entirestr - m; match_.end_ofs = match_.start_ofs + *result_len; } /* return the result */ return m; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmregex.h�������������������������������������������������������������������������0000664�0000000�0000000�00000123320�12650170723�0015505�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 1998, 2012 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmregex.h - regular expression parser for T3 Function Notes Adapted from the TADS 2 regular expression parser. This version uses UTF-8 strings rather than simple single-byte character strings, and is organized into a C++ class. Modified 04/11/99 CNebel - Fix warnings. 10/07/98 MJRoberts - Creation */ #ifndef VMREGEX_H #define VMREGEX_H #include <stdlib.h> #include "t3std.h" #include "vmuni.h" #include "utf8.h" #include "vmerr.h" #include "vmerrnum.h" /* state ID */ typedef int re_state_id; /* invalid state ID - used to mark null machines */ #define RE_STATE_INVALID ((re_state_id)-1) /* first valid state ID */ #define RE_STATE_FIRST_VALID ((re_state_id)0) /* ------------------------------------------------------------------------ */ /* * forward declarations */ typedef struct regex_scan_frame regex_scan_frame; /* ------------------------------------------------------------------------ */ /* * Group register structure. Each register keeps track of the starting * and ending offset of the group's text within the original search * string. */ struct re_group_register { int start_ofs; int end_ofs; }; /* maximum number of group registers we keep */ #define RE_GROUP_REG_CNT 10 /* maximum group nesting depth */ #define RE_GROUP_NESTING_MAX 20 /* * the maximum number of separate loop variables we need is the same as * the group nesting level, since we only need one loop variable per * nested group */ #define RE_LOOP_VARS_MAX RE_GROUP_NESTING_MAX /* ------------------------------------------------------------------------ */ /* * Recognizer types. */ enum re_recog_type { /* invalid/uninitialized */ RE_INVALID, /* literal (single) character recognizer */ RE_LITERAL, /* literal string recognizer */ RE_LITSTR, /* aliased literal string (points to string allocated in another state) */ RE_LITSTRA, /* "epsilon" recognizer - match without consuming anything */ RE_EPSILON, /* wildcard character */ RE_WILDCARD, /* beginning and end of text */ RE_TEXT_BEGIN, RE_TEXT_END, /* * same position as parent lookback assertion - this is used for * lookback assertions to assert that the assertion ends at the point * where the assertion occurs in the match string */ RE_LOOKBACK_POS, /* start and end of a word */ RE_WORD_BEGIN, RE_WORD_END, /* word-char and non-word-char */ RE_WORD_CHAR, RE_NON_WORD_CHAR, /* word-boundary and non-word-boundary */ RE_WORD_BOUNDARY, RE_NON_WORD_BOUNDARY, /* a character range/exclusion range */ RE_RANGE, RE_RANGE_EXCL, /* group entry/exit transition */ RE_GROUP_ENTER, RE_GROUP_EXIT, /* * group matcher - the character code has the group number (0 for * group 0, etc) rather than a literal to match */ RE_GROUP_MATCH, /* any alphabetic character */ RE_ALPHA, /* any digit */ RE_DIGIT, RE_NON_DIGIT, /* any upper-case alphabetic */ RE_UPPER, /* any lower-case alphabetic */ RE_LOWER, /* any alphanumeric */ RE_ALPHANUM, /* space character */ RE_SPACE, RE_NON_SPACE, /* punctuation character */ RE_PUNCT, /* newline character */ RE_NEWLINE, /* null character (used in range recognizers) */ RE_NULLCHAR, /* positive look-ahead assertion */ RE_ASSERT_POS, /* negative look-ahead assertion */ RE_ASSERT_NEG, /* positive look-back assertion */ RE_ASSERT_BACKPOS, /* negative look-back assertion */ RE_ASSERT_BACKNEG, /* loop entry: zero the associated loop variable */ RE_ZERO_VAR, /* loop branch: inspect loop criteria and branch accordingly */ RE_LOOP_BRANCH, /* vertical whitespace */ RE_VSPACE, RE_NON_VSPACE }; /* ------------------------------------------------------------------------ */ /* * Denormalized state transition tuple. Each tuple represents the * complete set of transitions out of a particular state. A particular * state can have one character transition, or two epsilon transitions. * Note that we don't need to store the state ID of the tuple itself in * the tuple, because the state ID is the index of the tuple in an array * of state tuples. */ struct re_tuple { /* recognizer type */ re_recog_type typ; /* the character we must match to transition to the target state */ union { /* * if this is a character transition, this is the character (used * as the character literal in RE_LITERAL, and as the group ID in * RE_GROUP_MATCH and in RE_EPSILON nodes with the group flag set) */ wchar_t ch; /* * If this is a character string transition, this is the string; * it's stored as a null-terminated string of wchar_t's, allocated * with new wchar_t[]. 'src' is the original state number for an * aliased string that points to another state's original. */ struct { wchar_t *str; re_state_id src; } str; /* * if this has a sub-machine, this is the start and end info (used * for the assertion entry states: RE_ASSERT_POS, RE_ASSERT_NEG, * RE_ASSERT_BACKPOS, RE_ASSERT_BACKNEG) */ struct { /* sub-machine start/end states */ re_state_id init; re_state_id final; /* for look-back assertions, the match length range */ int minlen; int maxlen; } sub; /* * if this is a loop, the loop parameters (used for RE_ZERO_VAR, * RE_LOOP_BRANCH) */ struct { int loop_min; int loop_max; int loop_var; } loop; /* * Character range match table - this is used if the recognizer * type is RE_RANGE or RE_RANGE_EXCL; for other recognizer types, * this is not used. * * If used, this is an array of pairs of characters. In each pair, * the first is the low end of the range, and the second is the * high end of the range, both ends inclusive. A single character * takes up two entries, both identical, to specify a range of only * one character. * * If the first character is '\0', then neither wchar_t is a * character in the ordinary sense described above. Instead, the * second wchar_t is actually one of the recognizer type codes * (re_recog_type) for a character class (RE_ALPHA, RE_DIGIT, etc). * The pair in this case is to be taken to match (or exclude) the * entire class. * * To represent a match for '\0', use '\0' for the first wchar_t * and RE_NULLCHAR for the second wchar_t. Note that the special * meaning of '\0' in the first character of a pair makes it * impossible to represent a range including a null byte with a * single pair; instead, representing a range like [\000-\017] * requires two pairs: the first pair is ('\0', RE_NULLCHAR), and * the second pair is ('\001', '\017'). */ struct { wchar_t *char_range; size_t char_range_cnt; } range; } info; /* the target states */ re_state_id next_state_1; re_state_id next_state_2; /* flags */ unsigned char flags; }; /* * Tuple flags */ /* this state is being tested for a cycle */ #define RE_STATE_CYCLE_TEST 0x08 /* * for branching states: take the shortest, rather than longest, branch * when both branches are successful */ #define RE_STATE_SHORTEST 0x10 /* ------------------------------------------------------------------------ */ /* * A "machine" description. A machines is fully described by its initial * and final state ID's. */ struct re_machine { /* the machine's initial state */ re_state_id init; /* the machine's final state */ re_state_id final; }; /* ------------------------------------------------------------------------ */ /* * Compiled pattern description. This is not a complete compiled pattern, * since the tuple array is separate; this is just a description of the * compiled pattern that can be combined with the tuple array to form a * full compiled pattern. */ struct re_compiled_pattern_base { /* the pattern's machine description */ re_machine machine; /* the number of tuples in the tuple array */ re_state_id tuple_cnt; /* number of capturing groups in the expression */ int group_cnt; /* maximum number of looping variables in the expression */ int loop_var_cnt; /* * <Case> or <NoCase> mode. If this flag is clear, the search is not * case-sensitive, so alphabetic characters in the pattern are matched * without regard to case. */ unsigned int case_sensitive : 1; /* is the case sensitivity explicit or defaulted? */ unsigned int case_sensitivity_specified : 1; /* * <MIN> or <MAX> match mode -- if this flag is set, we match the * longest string in case of ambiguity; otherwise we match the * shortest. */ unsigned int longest_match : 1; /* * <FirstEnd> or <FirstBeg> match mode -- if this flag is set, we * match (in a search) the string that starts first in case of * ambiguity; otherwise, we match the string that ends first */ unsigned int first_begin : 1; }; /* * Compiled pattern object. This is a pattern compiled and saved for use * in searches and matches. This is a compiled pattern description * coupled with its tuple array, which in combination provide a complete * compiled pattern. */ struct re_compiled_pattern: re_compiled_pattern_base { /* * the tuple array (the structure is overallocated to make room for * tuple_cnt entries in this array) */ re_tuple tuples[1]; }; /* ------------------------------------------------------------------------ */ /* * Status codes */ typedef enum { /* success */ RE_STATUS_SUCCESS = 0, /* compilation error - group nesting too deep */ RE_STATUS_GROUP_NESTING_TOO_DEEP } re_status_t; /* ------------------------------------------------------------------------ */ /* * Regular expression compilation context structure. This tracks the * state of the compilation and stores the resources associated with the * compiled expression. */ class CRegexParser { friend class CRegexSearcher; friend class CRegexSearcherSimple; public: /* initialize */ CRegexParser(); /* delete */ ~CRegexParser(); /* * Compile an expression and create a compiled pattern object, filling * in *pattern with a pointer to the newly-allocated pattern object. * The caller is responsible for freeing the pattern by calling * free_pattern(pattern). */ re_status_t compile_pattern(const char *expr_str, size_t exprlen, re_compiled_pattern **pattern); /* free a pattern previously created with compile_pattern() */ static void free_pattern(re_compiled_pattern *pattern); protected: /* reset the parser */ void reset(); /* allocate a new state ID */ re_state_id alloc_state(); /* set a transition from a state to a given destination state */ void set_trans(re_state_id id, re_state_id dest_id, re_recog_type typ, wchar_t ch); /* initialize a new machine, setting up the initial and final state */ void init_machine(struct re_machine *machine); /* build a character recognizer */ void build_char(struct re_machine *machine, wchar_t ch); /* build a special recognizer */ void build_special(struct re_machine *machine, re_recog_type typ, wchar_t ch); /* build a character range recognizer */ void build_char_range(struct re_machine *machine, int exclusion); /* build a group recognizer */ void build_group_matcher(struct re_machine *machine, int group_num); /* build a concatenation recognizer */ void build_concat(struct re_machine *new_machine, struct re_machine *lhs, struct re_machine *rhs); /* build a group machine */ void build_group(struct re_machine *new_machine, struct re_machine *sub_machine, int group_id); /* build a positive or negative assertion machine */ void build_assert(struct re_machine *new_machine, struct re_machine *sub_machine, int is_negative, int is_lookback); /* build an alternation recognizer */ void build_alter(struct re_machine *new_machine, struct re_machine *lhs, struct re_machine *rhs); /* build a closure recognizer */ void build_closure(struct re_machine *new_machine, struct re_machine *sub, wchar_t specifier, int shortest); /* build an interval matcher */ void build_interval(struct re_machine *new_machine, struct re_machine *sub, int min_val, int max_val, int var_id, int shortest); /* build a null machine */ void build_null_machine(struct re_machine *machine); /* determine if a machine is null */ int is_machine_null(struct re_machine *machine); /* concate the second machine onto the first machine */ void concat_onto(struct re_machine *dest, struct re_machine *rhs); /* alternate the second machine onto the first */ void alternate_onto(struct re_machine *dest, struct re_machine *rhs); /* compile an expression */ re_status_t compile(const char *expr_str, size_t exprlen, re_compiled_pattern_base *pat); /* compile a character class or class range expression */ int compile_char_class_expr(utf8_ptr *expr, size_t *exprchars, re_machine *result_machine); /* parse an integer value */ int parse_int(utf8_ptr *p, size_t *chars_rem); /* add a character to our range buffer */ void add_range_char(wchar_t ch) { add_range_char(ch, ch); } void add_range_char(wchar_t ch_lo, wchar_t ch_hi); /* add a character class to our range buffer */ void add_range_class(re_recog_type cl); /* ensure space in the range buffer for another entry */ void ensure_range_buf_space(); /* break any infinite loops in the machine */ void break_loops(re_machine *machine); /* get the match length for a state */ void get_match_length(re_state_id init, re_state_id final, int *minlen, int *maxlen, regex_scan_frame *stack); /* find an infinite loop back to the given state */ int break_loops(re_state_id init, re_state_id final, regex_scan_frame *stack); /* optimize away meaningless branch-to-branch transitions */ void remove_branch_to_branch(re_machine *machine); void optimize_transition(const re_machine *machine, re_state_id *trans); /* consolidate runs of characters into strings */ void consolidate_strings(re_machine *machine); /* next available state ID */ re_state_id next_state_; /* * The array of transition tuples. We'll allocate this array and * expand it as necessary. */ re_tuple *tuple_arr_; /* number of transition tuples allocated in the array */ int tuples_alloc_; /* buffer for building range exprssions */ wchar_t *range_buf_; /* current number of entries in range buffer */ size_t range_buf_cnt_; /* maximum number of entries in range buffer */ size_t range_buf_max_; }; /* ------------------------------------------------------------------------ */ /* * Pattern recognizer state stack. Each time we need to process a * sub-state (a two-way epsilon, or a nested assertion), we stack the * current state so that we can backtrack when we're done with the * sub-expression. The state we store consists of: * * - backtrack type - this is an arbitrary uchar identifier that the * pattern matcher uses to identify where to go when we pop the state * * - the current state ID * * - the current offset in the string being matched * * - the state ID of the terminating state of the machine * * - saved group registers; we only store the ones we've actually * modified, to avoid unnecessary copying * * - saved loop variables; we only store the ones we've actually modified, * to avoid unnecessary copying */ enum regex_frame_type { ST_EPS1 = 1, /* doing first branch of two-branch epsilon */ ST_EPS2 = 2, /* doing second branch of two-branch epsilon */ ST_ASSERT = 3 /* doing an assertion */ }; /* the base structure for a stacked state */ struct regex_stack_entry { /* the backtrack type identifier */ regex_frame_type typ; /* the starting offset in the string */ int start_ofs; /* the current offset in the string */ int str_ofs; /* the length of the search portion of the string */ int curlen; /* the pattern state */ re_state_id state; /* the final state of the machine */ re_state_id final; /* the return value for this state */ int retval; /* * The look-back iteration. For a look-back assertion, we have to * check each starting point over the range of possible match lengths, * until we've either found a success condition or exhausted the range * of possible match lengths. This keeps track of where we are in the * iteration. */ int iter; /* stack offset of previous frame */ int prv_sp; }; /* saved group/loop entry */ struct regex_stack_var { /* * The ID - this is in the range 0..RE_GROUP_REG_CNT-1 for group * registers, RE_GROUP_REG_CNT..RE_GROUP_REG_CNT+RE_LOOP_VARS_MAX-1 * for loop variables. In other words, a loop variable is identified * by its loop variable number plus RE_GROUP_REG_CNT. The special ID * value -1 indicates the integer 'retval' value (a saved return value * for the stack state). */ int id; /* the value */ union { re_group_register group; short loopvar; int retval; } val; }; /* state stack class */ class CRegexStack { friend class reg_deltas; public: CRegexStack() { /* allocate the initial state buffer */ bufsiz_ = 8192; buf_ = (char *)t3malloc(bufsiz_); /* we don't have anything on the stack yet */ sp_ = -1; used_ = 0; } ~CRegexStack() { /* delete the stack buffer */ t3free(buf_); } /* reset the stack */ void reset() { /* empty the stack */ sp_ = -1; used_ = 0; } /* push a new state */ void push(regex_frame_type typ, int start_ofs, int str_ofs, int curlen, re_state_id state, re_state_id final, int iter) { /* * Ensure we have enough space for the base state structure plus a * full complement of group registers, loop variables, and return * value. We might not actually need all of the registers and * loop variables, so we won't commit all of this space yet, but * check in advance to make sure we have it so that we don't have * to check again when and if we get around to consuming * group/loop slots. */ ensure_space(sizeof(regex_stack_entry) + ((RE_GROUP_REG_CNT + RE_LOOP_VARS_MAX + 1) *sizeof(regex_stack_var))); /* allocate the base stack frame */ regex_stack_entry *fp = (regex_stack_entry *)alloc_space(sizeof(regex_stack_entry)); /* set it up */ fp->typ = typ; fp->start_ofs = start_ofs; fp->str_ofs = str_ofs; fp->curlen = curlen; fp->state = state; fp->final = final; fp->iter = iter; /* push it onto the stack */ fp->prv_sp = sp_; sp_ = (char *)fp - buf_; } /* save a group register */ void save_group_reg(int id, const re_group_register *regs) { /* allocate a new slot if needed and save the value */ regex_stack_var *var; if (sp_ != -1 && (var = new_reg_or_var(id)) != 0) var->val.group = regs[id]; } /* save a loop variable */ void save_loop_var(int id, const short *loop_vars) { /* * allocate a new slot if needed and save the value; note that * loop variables are identified by the loop variable ID plus the * base index RE_GROUP_REG_CNT */ regex_stack_var *var; if (sp_ != -1 && (var = new_reg_or_var(id + RE_GROUP_REG_CNT)) != 0) var->val.loopvar = loop_vars[id]; } /* propagate a group register or loop variable from another frame */ void propagate(const regex_stack_var *src) { regex_stack_var *dst; if (sp_ != -1 && (dst = new_reg_or_var(src->id)) != 0) memcpy(dst, src, sizeof(*dst)); } /* * get the type of the state at top of stack; if there is no state, * returns -1 */ int get_top_type() { /* * if there's nothing on the stack, so indicate, otherwise get the * type from the top stack element */ if (sp_ == -1) return -1; else return ((regex_stack_entry *)(buf_ + sp_))->typ; } /* get the stack frame at the given depth (0 is top of stack) */ regex_stack_entry *get_frame(int depth) { /* traverse the given number of frames from the top of the stack */ regex_stack_entry *fp; for (fp = (regex_stack_entry *)(buf_ + sp_) ; depth != 0 && fp != 0 ; --depth, fp = get_parent_frame(fp)) ; /* return the frame pointer */ return fp; } /* * Get the parent lookback assertion match position. This scans up the * stack for the nearest assertion frame, and returns its match * position. */ int get_lookback_pos() { /* scan up the stack for a lookback frame */ for (regex_stack_entry *fp = (regex_stack_entry *)(buf_ + sp_) ; fp != 0 ; fp = get_parent_frame(fp)) { /* if this is an assert frame, return the position */ if (fp->typ == ST_ASSERT) return fp->str_ofs; } /* didn't find it */ return -1; } /* get the parent stack entry */ regex_stack_entry *get_parent_frame(regex_stack_entry *fp) { if (fp->prv_sp < 0) return 0; else return (regex_stack_entry *)(buf_ + fp->prv_sp); } /* pop a state */ void pop(int *start_ofs, int *str_ofs, size_t *curlen, re_state_id *state, re_state_id *final, re_group_register *regs, short *loop_vars, int *iter) { /* get the stack pointer */ regex_stack_entry *fp = (regex_stack_entry *)(buf_ + sp_); /* restore the string offset and state ID */ *start_ofs = fp->start_ofs; *str_ofs = fp->str_ofs; *curlen = fp->curlen; *state = fp->state; *final = fp->final; *iter = fp->iter; /* run through the saved registers/variables in the state */ for (regex_stack_var *var = (regex_stack_var *)(fp + 1) ; var < (regex_stack_var *)(buf_ + used_) ; ++var) { /* sense the type */ if (var->id < RE_GROUP_REG_CNT) { /* it's a group register */ regs[var->id] = var->val.group; } else { /* it's a loop variable */ loop_vars[var->id - RE_GROUP_REG_CNT] = var->val.loopvar; } } /* we're done with the stop stack frame, so discard it */ discard(); } /* discard the top stack state */ void discard() { /* get the stack pointer */ regex_stack_entry *fp = (regex_stack_entry *)(buf_ + sp_); /* unwind the stack */ used_ = (size_t)sp_; sp_ = fp->prv_sp; } /* * Swap the current state with the state at the top of the stack, and * push a second copy of the restored state. This is used to traverse * the second branch of a two-branch epsilon: we first have to save the * results of the first branch, including the return value and its * registers, and we then have to restore the initial register/loop * state as it was before the first branch. * * We save the final state and restore the initial state by swapping * the group registers in the saved state with those in the current * state. This brings back the initial conditions to the current * machine state, while saving everything that's changed in the current * machine state in the stack frame. We'll likewise swap the machine * state and string offset. Later, this same final machine state can * be restored by first restoring the machine state to the initial * state, then popping this frame. * * On return, the stack frame that was active on entry will be set to * contain the current machine state, and the current machine state * will be replaced with what was in that stack frame. In addition, * we'll have pushed a new stack frame for the new current machine * state. */ void swap_and_push(int retval, regex_frame_type typ, int *start_ofs, int *str_ofs, size_t *curlen, re_state_id *state, re_state_id *final, re_group_register *regs, short *loop_vars) { /* swap the current state wtih the top of stack */ swap(start_ofs, str_ofs, curlen, state, final, regs, loop_vars); /* save the return value from the outgoing state */ regex_stack_entry *fp = (regex_stack_entry *)(buf_ + sp_); fp->retval = retval; /* push a copy of the restored state */ push(typ, *start_ofs, *str_ofs, *curlen, *state, *final, 0); } /* * Swap the current active state with the top of stack, then pop the * frame. */ void swap_and_pop(int *start_ofs, int *str_ofs, size_t *curlen, re_state_id *state, re_state_id *final, re_group_register *regs, short *loop_vars, int *iter) { /* swap the current state wtih the top of stack */ swap(start_ofs, str_ofs, curlen, state, final, regs, loop_vars); /* save the return value from the outgoing state */ regex_stack_entry *fp = (regex_stack_entry *)(buf_ + sp_); *iter = fp->iter; /* discard the frame */ discard(); } /* * Swap the current state with the top stack frame */ void swap(int *start_ofs, int *str_ofs, size_t *curlen, re_state_id *state, re_state_id *final, re_group_register *regs, short *loop_vars) { /* get the stack pointer */ regex_stack_entry *fp = (regex_stack_entry *)(buf_ + sp_); /* swap the string offset */ int tmp_ofs = *str_ofs; *str_ofs = fp->str_ofs; fp->str_ofs = tmp_ofs; /* swap the starting offset */ tmp_ofs = *start_ofs; *start_ofs = fp->start_ofs; fp->start_ofs = tmp_ofs; /* swap the search length */ size_t tmp_len = *curlen; *curlen = fp->curlen; fp->curlen = tmp_len; /* swap the current machine state */ re_state_id tmp_id = *state; *state = fp->state; fp->state = tmp_id; /* swap the final machine state */ tmp_id = *final; *final = fp->final; fp->final = tmp_id; /* swap all group and loop registers with the current state */ for (regex_stack_var *var = (regex_stack_var *)(fp + 1) ; var < (regex_stack_var *)(buf_ + used_) ; ++var) { /* sense the type */ if (var->id < RE_GROUP_REG_CNT) { re_group_register tmp; /* it's a group register */ tmp = regs[var->id]; regs[var->id] = var->val.group; var->val.group = tmp; } else { short tmp; /* it's a loop variable */ tmp = loop_vars[var->id - RE_GROUP_REG_CNT]; loop_vars[var->id - RE_GROUP_REG_CNT] = var->val.loopvar; var->val.loopvar = tmp; } } } protected: /* * allocate a new register or group variable in the stack frame; if we * find an existing copy of the same variable, we'll return null to * indicate that we don't have to save it again */ regex_stack_var *new_reg_or_var(int id) { /* get the stack pointer */ regex_stack_entry *fp = (regex_stack_entry *)(buf_ + sp_); /* scan the frame for a register/variable with this ID */ regex_stack_var *var; for (var = (regex_stack_var *)(fp + 1) ; var < (regex_stack_var *)(buf_ + used_) ; ++var) { /* if this is the one, we don't need to save it again */ if (var->id == id) return 0; } /* we didn't find it, so return a new entry with the given ID */ var = (regex_stack_var *)alloc_space(sizeof(regex_stack_var)); var->id = id; return var; } /* ensure space in our stack buffer */ void ensure_space(size_t siz) { /* if it's within range, we're fine */ if (used_ + siz <= bufsiz_) return; /* expand */ bufsiz_ += 8192; /* if it's too large, throw an error */ if (bufsiz_ > OSMALMAX) err_throw(VMERR_OUT_OF_MEMORY); /* reallocate at the new size */ buf_ = (char *)t3realloc(buf_, bufsiz_); /* make sure we're not out of memory */ if (buf_ == 0) err_throw(VMERR_OUT_OF_MEMORY); } /* * allocate space - the caller must have already checked that space is * available */ char *alloc_space(size_t siz) { /* figure out where the new object goes */ char *ret = buf_ + used_; /* consume the space */ used_ += siz; /* return the allocated space */ return ret; } /* the stack buffer */ char *buf_; size_t bufsiz_; /* offset of current stack frame */ int sp_; /* number of bytes used so far */ size_t used_; }; /* ------------------------------------------------------------------------ */ /* * Regular Expression Searcher/Matcher. This object encapsulates the * group registers associated with a search. */ class CRegexSearcher { public: CRegexSearcher(); ~CRegexSearcher(); /* * Search for a compiled pattern. Returns the byte offset of the * match, or -1 if no match was found. *result_len is filled in with * the byte length of the match if we found one. Note that the * returned index and result_len values are byte lengths, not * character lengths. * * The caller is responsible for providing a set of group registers, * which must be an array of registers of size RE_GROUP_REG_CNT. The * caller also must save the original search string if it will be * necessary to extract substrings based on the group registers. */ int search_for_pattern(const re_compiled_pattern *pattern, const char *entirestr, const char *searchstr, size_t searchlen, int *result_len, re_group_register *regs); /* * Search backwards for a compiled pattern. This is similar to * search_for_pattern(), but searches backwards from the starting * position. The match must end BEFORE the character at the starting * position; the match can't overlap or include that character. * * On success, the return value is non-negative, and gives the number * of bytes before the starting position where the match begins. To * get the starting byte pointer, subtract the return value from the * 'searchstr' pointer. On failure, returns -1. */ int search_back_for_pattern(const re_compiled_pattern *pattern, const char *entirestr, const char *searchstr, size_t searchlen, int *result_len, re_group_register *regs); /* * Check for a match to a previously compiled expression. Returns the * length of the match if we found a match, -1 if we found no match. * This is not a search function; we merely match the leading * substring of the given string to the given pattern. Note that the * returned length is a byte length, not a character length. * * The caller is responsible for providing a set of group registers, * which must be an array of registers of size RE_GROUP_REG_CNT. The * caller also must save the original search string if it will be * necessary to extract substrings based on the group registers. */ int match_pattern(const re_compiled_pattern *pattern, const char *entirestr, const char *searchstr, size_t searchlen, re_group_register *regs); /* * Get/set the default case sensititivy for searching and matching. * This controls the case sensitivity for patterns that don't include * explicit <case> or <nocase> flags. Explicit flags in the pattern * override this default. */ int get_default_case_sensitive() const { return default_case_sensitive_; } void set_default_case_sensitive(int f) { default_case_sensitive_ = f; } protected: /* match a string to a compiled expression */ int match(const char *entire_str, size_t entire_len, const char *str, size_t origlen, const re_compiled_pattern_base *pattern, const re_tuple *tuple_arr, const struct re_machine *machine, re_group_register *regs, short *loop_vars); /* search for a regular expression within a string */ int search(const char *entire_str, const char *str, size_t len, const re_compiled_pattern_base *pattern, const re_tuple *tuple_arr, const struct re_machine *machine, re_group_register *regs, int *result_len); /* search backwards for a regular expression within a string */ int search_back(const char *entire_str, const char *str, size_t len, const re_compiled_pattern_base *pattern, const re_tuple *tuple_arr, const struct re_machine *machine, re_group_register *regs, int *result_len); /* clear a set of group registers */ void clear_group_regs(re_group_register *regs) { /* set the start and end offsets for all registers to -1 */ int i; re_group_register *r; for (r = regs, i = 0 ; i < RE_GROUP_REG_CNT ; ++i, ++r) r->start_ofs = r->end_ofs = -1; } /* * Determine if a character is part of a word. We consider letters * and numbers to be word characters. */ static int is_word_char(wchar_t c) { return (t3_is_alpha(c) || t3_is_digit(c)); } /* match state stack */ CRegexStack stack_; /* default case sensitivity, for patterns that don't specify it */ unsigned int default_case_sensitive_ : 1; }; /* * Simplified Searcher - this class provides some high-level methods that * simplify one-off searches that combine compilation and searching into * one step. */ class CRegexSearcherSimple: public CRegexSearcher { public: CRegexSearcherSimple(class CRegexParser *parser) { /* remember my parser */ parser_ = parser; } ~CRegexSearcherSimple() { } /* clear out our group registers */ void clear_group_regs() { /* clear the last match register */ match_.start_ofs = match_.end_ofs = -1; /* clear the actual group registers */ CRegexSearcher::clear_group_regs(regs_); } /* search for a pattern, using our internal group registers */ int search_for_pattern(const re_compiled_pattern *pattern, const char *entirestr, const char *searchstr, size_t searchlen, int *result_len) { /* remember the group count from the compiled pattern */ group_cnt_ = pattern->group_cnt; /* clear the group registers */ clear_group_regs(); /* search for the compiled pattern using our group register */ int m = CRegexSearcher::search_for_pattern( pattern, entirestr, searchstr, searchlen, result_len, regs_); /* save the match information on success */ if (m >= 0) { match_.start_ofs = m; match_.end_ofs = m + *result_len; } /* return the result */ return m; } /* search backwards for a pattern, using internal group registers */ int search_back_for_pattern(const re_compiled_pattern *pattern, const char *entirestr, const char *searchstr, size_t searchlen, int *result_len) { /* remember the group count from the compiled pattern */ group_cnt_ = pattern->group_cnt; /* clear the group registers */ clear_group_regs(); /* search for the compiled pattern using our group register */ int m = CRegexSearcher::search_back_for_pattern( pattern, entirestr, searchstr, searchlen, result_len, regs_); /* save the match information on success */ if (m >= 0) { match_.start_ofs = searchstr - entirestr - m; match_.end_ofs = match_.start_ofs + *result_len; } /* return the result */ return m; } /* match a pattern, using our internal group registers */ int match_pattern(const re_compiled_pattern *pattern, const char *entirestr, const char *searchstr, size_t searchlen) { /* remember the group count from the compiled pattern */ group_cnt_ = pattern->group_cnt; /* clear the group registers */ clear_group_regs(); /* search for the compiled pattern using our group register */ int m = CRegexSearcher::match_pattern( pattern, entirestr, searchstr, searchlen, regs_); /* save the match information on success */ if (m >= 0) { match_.start_ofs = searchstr - entirestr; match_.end_ofs = match_.start_ofs + m; } /* return the result */ return m; } /* * Compile an expression and search for a match within the given * string. Returns the byte offset of the match, or -1 if no match * was found. *result_len is filled in with the byte length of the * match if we found one. Note that the returned index and result_len * values are byte lengths, not character lengths. */ int compile_and_search(const char *pattern, size_t patlen, const char *entirestr, const char *searchstr, size_t searchlen, int *result_len); /* compile and search backwards */ int compile_and_search_back(const char *pattern, size_t patlen, const char *entirestr, const char *searchstr, size_t searchlen, int *result_len); /* * Compile an expression and check for a match. Returns the byte * length of the match if we found a match, -1 if we found no match. * This is not a search function; we merely match the leading * substring of the given string to the given pattern. Note that the * returned length is a byte length, not a character length. */ int compile_and_match(const char *pattern, size_t patlen, const char *entirestr, const char *searchstr, size_t searchlen); /* * Get a group register. 0 refers to the first group; groups are * numbered in left-to-right order by their opening parenthesis. */ const re_group_register *get_group_reg(int i) const { return ®s_[i]; } /* get the last match */ const re_group_register *get_last_match() const { return &match_; } /* get the number of groups in the last pattern we searched */ int get_group_cnt() const { return group_cnt_; } /* copy group registers from another search */ void copy_group_regs(CRegexSearcherSimple *s) { memcpy(regs_, s->regs_, sizeof(regs_)); group_cnt_ = s->group_cnt_; } protected: /* group registers */ re_group_register regs_[RE_GROUP_REG_CNT]; /* last match location */ re_group_register match_; /* number of groups in last pattern we searched */ int group_cnt_; /* my regular expression parser */ class CRegexParser *parser_; }; #endif /* VMREGEX_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmrun.cpp�������������������������������������������������������������������������0000664�0000000�0000000�00000666773�12650170723�0015562�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMRUN.CPP,v 1.4 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmrun.cpp - VM Execution Function Notes Modified 11/12/98 MJRoberts - Creation */ #include <stdio.h> #include "t3std.h" #include "os.h" #include "vmrun.h" #include "vmdbg.h" #include "vmop.h" #include "vmstack.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmobj.h" #include "vmlst.h" #include "vmstr.h" #include "vmtobj.h" #include "vmfunc.h" #include "vmmeta.h" #include "vmbif.h" #include "vmpredef.h" #include "vmfile.h" #include "vmsave.h" #include "vmprof.h" #include "vmhash.h" #include "vmlookup.h" #include "vmfref.h" #include "vmop.h" #include "vmbignum.h" /* ------------------------------------------------------------------------ */ /* * Define static global variables for certain VM registers, if we're * compiling the system in a static global configuration. * * Empirically, it makes a measurable difference (on the order of 2% * compared to the struct configuration) to keep these in globals and to * keep them together. These are the VM's equivalent of CPU registers, and * they get hit a lot during bytecode execution; keeping this small cluster * of hot memory locations together seems to enable the CPU to keep them in * cache. */ VM_IF_REGS_IN_GLOBALS(vm_val_t *sp_;) VM_IF_REGS_IN_GLOBALS(vm_val_t *frame_ptr_;) VM_IF_REGS_IN_GLOBALS(vm_val_t r0_;) VM_IF_REGS_IN_GLOBALS(const uchar *entry_ptr_native_;) VM_IF_REGS_IN_GLOBALS(const uchar **pc_ptr_;) /* ------------------------------------------------------------------------ */ /* * Initialize */ CVmRun::CVmRun(size_t max_depth, size_t reserve_depth) : CVmStack(max_depth, reserve_depth) { init(); } void CVmRun::init() { /* inherit base class handling */ CVmStack::init(); /* start out with 'nil' in R0 */ r0_.set_nil(); /* there's no frame yet */ frame_ptr_ = 0; /* there's no entry pointer yet */ entry_ptr_native_ = 0; /* function header size is not yet known */ funchdr_size_ = 0; /* we have no 'say' function yet */ say_func_ = 0; /* no default 'say' method */ say_method_ = VM_INVALID_PROP; /* no debugger halt requested yet */ halt_vm_ = FALSE; /* we have no program counter yet */ pc_ptr_ = 0; /* * If we're including the profiler in the build, allocate and * initialize its memory structures. */ #ifdef VM_PROFILER /* * Allocate the profiler stack. This stack will contain one record per * activation frame in the regular VM stack. */ prof_stack_max_ = 250; (prof_stack_ = (vm_profiler_rec *) t3malloc(prof_stack_max_ * sizeof(prof_stack_[0]))); /* we don't have anything on the profiler stack yet */ prof_stack_idx_ = 0; /* create the profiler master hash table */ prof_master_table_ = new CVmHashTable(512, new CVmHashFuncCI(), TRUE); /* we're not running the profiler yet */ profiling_ = FALSE; #endif /* VM_PROFILER */ } /* ------------------------------------------------------------------------ */ /* * Terminate */ CVmRun::~CVmRun() { terminate(); } void CVmRun::terminate() { /* * If we're including the profiler in the build, delete its memory * structures. */ #ifdef VM_PROFILER /* delete the profiler stack */ if (prof_stack_ != 0) { t3free(prof_stack_); prof_stack_ = 0; } /* delete the profiler master hash table */ if (prof_master_table_ != 0) { delete prof_master_table_; prof_master_table_ = 0; } #endif /* VM_PROFILER */ } /* ------------------------------------------------------------------------ */ /* * Set the function header size */ void CVmRun::set_funchdr_size(size_t siz) { /* remember the new size */ funchdr_size_ = siz; /* * Ensure that the size is at least as large as our required function * header block - if it's not, this version of the VM can't run this * image file. If we throw an error, flag it as a version mismatch * error. */ if (siz < VMFUNC_HDR_MIN_SIZE) err_throw_a(VMERR_IMAGE_INCOMPAT_HDR_FMT, 1, ERR_TYPE_VERSION_FLAG); } /* ------------------------------------------------------------------------ */ /* * Add two values, leaving the result in *val1 */ int CVmRun::compute_sum(VMG_ vm_val_t *val1, const vm_val_t *val2) { /* the meaning of "add" depends on the type of the first operand */ check_type: switch(val1->typ) { case VM_SSTRING: /* * string constant - add the second value to the string, using * the static string add method */ CVmObjString::add_to_str(vmg_ val1, val1, val2); return TRUE; case VM_LIST: /* * list constant - add the second value to the list, using the * static list add method */ CVmObjList::add_to_list(vmg_ val1, VM_INVALID_OBJ, get_const_ptr(vmg_ val1->val.ofs), val2); return TRUE; case VM_OBJ: /* * object - add the second value to the object, using the * object's virtual metaclass add method */ return vm_objp(vmg_ val1->val.obj)->add_val( vmg_ val1, val1->val.obj, val2); case VM_INT: /* * callers handle the int+int case inline, so we can skip that; we * just need to check for other numeric types */ if (val2->is_numeric(vmg0_)) { /* * the other value is numeric but not an integer; promote the * integer to the other type, then re-do the type check */ val2->promote_int(vmg_ val1); goto check_type; } else { /* can't add a non-numeric value to an integer */ err_throw(VMERR_NUM_VAL_REQD); AFTER_ERR_THROW(return FALSE;) } default: /* other types don't understand '+' as a native operator */ return FALSE; } } /* * Compute the sum for an INC instruction */ const uchar *CVmRun::compute_sum_inc(VMG_ const uchar *p) { /* add 1 to the value at TOS, leaving it on the stack */ vm_val_t val2; val2.set_int(1); if (compute_sum(vmg_ get(0), &val2)) { return p; } else { /* no native implementation - check for an overload */ vm_val_t val; pop(&val); push(&val2); return op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_add, 1, VMERR_BAD_TYPE_ADD); } } /* * Compute the sum for an ADD instruction */ const uchar *CVmRun::compute_sum_add(VMG_ const uchar *p) { if (compute_sum(vmg_ get(1), get(0))) { /* success - the result is at TOS-1 */ discard(); return p; } else { /* try the operator overload */ vm_val_t val; pop_left_op(vmg_ &val); return op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_add, 1, VMERR_BAD_TYPE_ADD); } } /* * Compute the sum of a local and an immediate value, assigning the result * to the local */ const uchar *CVmRun::compute_sum_lcl_imm(VMG_ vm_val_t *lclp, const vm_val_t *ival, int lclidx, const uchar *p) { /* compute the sum, leaving the result in the local */ if (compute_sum(vmg_ lclp, ival)) { /* success */ return p; } else { /* check for an overload */ push(ival); return op_overload(vmg_ p - entry_ptr_native_, lclidx, lclp, G_predef->operator_add, 1, VMERR_BAD_TYPE_ADD); } } /* ------------------------------------------------------------------------ */ /* * Compute the difference of two values, leaving the result in *val1 */ int CVmRun::compute_diff(VMG_ vm_val_t *val1, vm_val_t *val2) { /* the meaning of "subtract" depends on the type of the first operand */ check_type: switch(val1->typ) { case VM_LIST: /* * list constant - remove the second value from the list, using * the static list subtraction method */ CVmObjList::sub_from_list(vmg_ val1, val1, get_const_ptr(vmg_ val1->val.ofs), val2); return TRUE; case VM_OBJ: /* object - use the object's virtual subtraction method */ return vm_objp(vmg_ val1->val.obj)->sub_val( vmg_ val1, val1->val.obj, val2); case VM_INT: /* * callers handle the int-int case inline, so we can skip that; * check for other numeric types */ if (val2->is_numeric(vmg0_)) { /* * the other value is numeric but not an integer; promote the * integer to the other type, then re-do the type check */ val2->promote_int(vmg_ val1); goto check_type; } else { /* other values can't be subtracted */ err_throw(VMERR_NUM_VAL_REQD); AFTER_ERR_THROW(return FALSE;) } default: /* other types cannot be subtracted */ return FALSE; } } /* * Compute the difference for a DEC instruction */ const uchar *CVmRun::compute_diff_dec(VMG_ const uchar *p) { /* compute TOS - 1, leaving the result in TOS */ vm_val_t val2; val2.set_int(1); if (compute_diff(vmg_ get(0), &val2)) { return p; } else { /* no native implementation - check for an overload */ vm_val_t val; pop(&val); push(&val2); return op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_sub, 1, VMERR_BAD_TYPE_SUB); } } /* * Compute the difference for a SUB instruction */ const uchar *CVmRun::compute_diff_sub(VMG_ const uchar *p) { if (compute_diff(vmg_ get(1), get(0))) { /* the difference is at TOS-1 - discard the second value */ discard(); return p; } else { /* try for an operator overload */ vm_val_t val; pop_left_op(vmg_ &val); return op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_sub, 1, VMERR_BAD_TYPE_SUB); } } /* ------------------------------------------------------------------------ */ /* * Compute the product val1 * val2, leaving the result in val1 */ int CVmRun::compute_product(VMG_ vm_val_t *val1, vm_val_t *val2) { check_type: switch(val1->typ) { case VM_OBJ: /* use the object's virtual multiplication method */ return vm_objp(vmg_ val1->val.obj)->mul_val( vmg_ val1, val1->val.obj, val2); case VM_INT: /* callers handle int*int inline; check for other numeric types */ if (val2->is_numeric(vmg0_)) { /* * int+other numeric - promote the integer to the other type, * then restart the calculation with the promoted value */ val2->promote_int(vmg_ val1); goto check_type; } else { /* other types are invalid */ err_throw(VMERR_NUM_VAL_REQD); AFTER_ERR_THROW(return FALSE;) } default: /* other types are invalid */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Compute the quotient val1/val2, leaving the result in val1. */ int CVmRun::compute_quotient(VMG_ vm_val_t *val1, vm_val_t *val2) { check_type: switch(val1->typ) { case VM_OBJ: /* use the object's virtual division method */ return vm_objp(vmg_ val1->val.obj)->div_val( vmg_ val1, val1->val.obj, val2); break; case VM_INT: /* callers handle int/int inline; check for other numeric types */ if (val2->is_numeric(vmg0_)) { /* promote to integer, then restart with the promoted value */ val2->promote_int(vmg_ val1); goto check_type; } else { err_throw(VMERR_NUM_VAL_REQD); AFTER_ERR_THROW(return FALSE;) } default: /* other types are invalid */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * XOR two values and push the result. The values can be numeric or * logical. If either value is logical, the result will be logical; * otherwise, the result will be a bitwise XOR of the integers. */ int CVmRun::xor_and_push(VMG_ vm_val_t *val1, vm_val_t *val2) { /* figure what to do based on the types */ if (val1->is_logical() && val2->is_logical()) { /* both values are logical - compute the logical XOR */ val1->set_logical(val1->get_logical() ^ val2->get_logical()); } else if (val1->is_logical() || val2->is_logical()) { /* * one value is logical, but not both - convert the other value * from a number to a logical and compute the result as a * logical value */ if (!val1->is_logical()) val1->num_to_logical(); else if (!val2->is_logical()) val2->num_to_logical(); /* compute the logical xor */ val1->set_logical(val1->get_logical() ^ val2->get_logical()); } else if (val1->typ == VM_INT && val2->typ == VM_INT) { /* compute and store the bitwise XOR */ val1->val.intval = val1->val.intval ^ val2->val.intval; } else { /* there's no logical conversion, so we can't compute it here */ return FALSE; } /* push the result */ pushval(vmg_ val1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Set an indexed value. Updates *container_val with the modified * container, if the operation requires this. (For example, setting an * indexed element of a list will create a new list, and return the new * list in *container_val. Setting an element of a vector simply modifies * the vector in place, hence the container reference is unchanged.) */ int CVmRun::set_index(VMG_ vm_val_t *container_val, const vm_val_t *index_val, const vm_val_t *new_val) { switch(container_val->typ) { case VM_LIST: /* list constant - use the static list set-index method */ CVmObjList::set_index_list(vmg_ container_val, get_const_ptr(vmg_ container_val->val.ofs), index_val, new_val); return TRUE; case VM_OBJ: /* object - use the object's virtual set-index method */ return vm_objp(vmg_ container_val->val.obj) ->set_index_val_q(vmg_ container_val, container_val->val.obj, index_val, new_val); default: /* other values cannot be indexed */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Create a new object and store it in R0 */ const uchar *CVmRun::new_and_store_r0(VMG_ const uchar *pc, uint metaclass_idx, uint argc, int is_transient) { vm_obj_id_t obj; /* create the object */ obj = G_meta_table->create_from_stack(vmg_ &pc, metaclass_idx, argc); /* if we got a valid object, store a reference to it in R0 */ if (obj != VM_INVALID_OBJ) { /* set the object return value */ r0_.set_obj(obj); /* make the object transient if desired */ if (is_transient) G_obj_table->set_obj_transient(obj); } else { /* failed - return nil */ r0_.set_nil(); } /* return the new instruction pointer */ return pc; } /* ------------------------------------------------------------------------ */ /* * Process a MAKELSTPAR instruction */ void CVmRun::makelstpar(VMG0_) { /* pop the value and the argument counter so far */ vm_val_t val, val2; popval(vmg_ &val); pop_int(vmg_ &val2); /* if it's not a list, just push it again unchanged */ int lstcnt; if (!val.is_listlike(vmg0_) || (lstcnt = val.ll_length(vmg0_)) < 0) { /* put it back on the stack */ pushval(vmg_ &val); /* increment the argument count and push it */ ++val2.val.intval; pushval(vmg_ &val2); /* our work here is done */ return; } /* set up a pointer to the current function header */ CVmFuncPtr hdr_ptr; hdr_ptr.set(entry_ptr_native_); /* get the depth required for the header */ uint hdr_depth = hdr_ptr.get_stack_depth(); /* * deduct the amount stack space we've already used from the amount * noted in the header, because that's the amount more that we could * need for the fixed stuff */ hdr_depth -= (get_depth_rel(frame_ptr_) - 1); /* make sure we have enough stack space available */ if (!check_space(lstcnt + hdr_depth)) err_throw(VMERR_STACK_OVERFLOW); /* push the elements of the list from last to first */ for (uint i = lstcnt ; i != 0 ; --i) { /* push this element's value */ val.ll_index(vmg_ push(), i); } /* increment and push the argument count */ val2.val.intval += lstcnt; pushval(vmg_ &val2); } /* ------------------------------------------------------------------------ */ /* * Index a value and push the result. */ int CVmRun::apply_index(VMG_ vm_val_t *result, const vm_val_t *container_val, const vm_val_t *index_val) { /* check the type of the value we're indexing */ switch(container_val->typ) { case VM_LIST: /* list constant - use the static list indexing method */ CVmObjList::index_list( vmg_ result, get_const_ptr(vmg_ container_val->val.ofs), index_val); return TRUE; case VM_OBJ: /* object - use the object's virtual indexing method */ { vm_obj_id_t obj = container_val->val.obj; return vm_objp(vmg_ obj)->index_val_q(vmg_ result, obj, index_val); } default: /* other values cannot be indexed */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Property evaluation invoker. This is a streamlined invoker for simple * evaluations where targetobj == self. */ class vmrun_prop_eval { public: /* caller offset */ uint caller_ofs; /* target property */ vm_prop_id_t target_prop; /* self and target object */ vm_val_t self; /* number of arguments */ uint argc; /* the property value */ vm_val_t val; /* defining object */ vm_obj_id_t defining_obj; const uchar *get_prop( VMG_ uint caller_ofs, vm_obj_id_t self, vm_prop_id_t target_prop) { this->self.set_obj(self); this->caller_ofs = caller_ofs; this->argc = 0; this->target_prop = target_prop; return get_prop(vmg0_); } const uchar *get_prop( VMG_ uint caller_ofs, vm_prop_id_t target_prop) { this->caller_ofs = caller_ofs; this->argc = 0; this->target_prop = target_prop; return get_prop(vmg0_); } const uchar *get_prop( VMG_ uint caller_ofs, vm_prop_id_t target_prop, uint argc) { this->caller_ofs = caller_ofs; this->argc = argc; this->target_prop = target_prop; return get_prop(vmg0_); } /* * Evaluate a property of an object. The caller must fill in * caller_ofs, target_obj, target_prop, self, and argc. */ const uchar *get_prop(VMG0_) { /* find the property without evaluating it */ if (get_prop_no_eval(vmg0_)) return eval_prop_val(vmg0_); /* try propNotDefined */ if (G_predef->prop_not_defined_prop != VM_INVALID_PROP) { /* save the original target property */ vm_prop_id_t real_target_prop = target_prop; /* look up propNotDefined */ target_prop = G_predef->prop_not_defined_prop; if (get_prop_no_eval(vmg0_)) { /* if we found a method, set up to call it */ if (val.typ == VM_CODEOFS || val.typ == VM_OBJX || val.typ == VM_BIFPTRX) { /* * add the target property as the additional first * argument to propNotDefined (we push backwards, so * this will conveniently become the new first * argument) */ G_stk->push()->set_propid(real_target_prop); /* count the additional argument */ ++argc; } return eval_prop_val(vmg0_); } } /* * the property or method is not defined - discard arguments and * set R0 to nil */ G_stk->discard(argc); G_interpreter->get_r0()->set_nil(); /* resume execution where we left off */ return G_interpreter->get_entry_ptr() + caller_ofs; } /* * Look up a property without evaluating it. */ int get_prop_no_eval(VMG0_) { int found; const char *target_ptr; int (*f_const_get_prop)(VMG_ vm_val_t *, const vm_val_t *, const char *, vm_prop_id_t, vm_obj_id_t *, uint *); vm_obj_id_t (*f_const_create)(VMG_ const char *); /* * we can evaluate properties of regular objects, as well as string * and list constants - see what we have */ switch(self.typ) { case VM_OBJ: /* get the property value from the target object */ return vm_objp(vmg_ self.val.obj) ->get_prop(vmg_ target_prop, &val, self.val.obj, &defining_obj, &argc); case VM_LIST: f_const_get_prop = &CVmObjList::const_get_prop; f_const_create = &CVmObjListConst::create; goto const_common; case VM_SSTRING: f_const_get_prop = &CVmObjString::const_get_prop; f_const_create = &CVmObjStringConst::create; const_common: /* translate the list offset to a physical pointer */ target_ptr = G_const_pool->get_ptr(self.val.ofs); /* evaluate the constant list property */ found = f_const_get_prop( vmg_ &val, &self, target_ptr, target_prop, &defining_obj, &argc); /* * If the result is a method to run, we need an actual object * for 'self'. In this case, create a dynamic list object with * the same contents as the constant list value. */ if (found && (val.typ == VM_CODEOFS || val.typ == VM_OBJX || val.typ == VM_BIFPTRX)) { /* create the list */ self.set_obj(f_const_create(vmg_ target_ptr)); } /* go evaluate the result as normal */ return found; case VM_NIL: /* nil pointer dereferenced */ err_throw(VMERR_NIL_DEREF); AFTER_ERR_THROW(return FALSE;) default: /* we can't evaluate properties of anything else */ err_throw(VMERR_OBJ_VAL_REQD); AFTER_ERR_THROW(return FALSE;) } } /* * Given a value that has been retrieved from an object property, * evaluate the value. If the value contains code, we'll execute the * code; if it contains a self-printing string, we'll display the * string; otherwise, we'll just store the value in R0. * * If the value wasn't found, the caller sets the value type to 'empty' * to indicate that there's no value. */ const uchar *eval_prop_val(VMG0_) { /* take appropriate action based on the datatype of the result */ switch(val.typ) { case VM_CODEOFS: /* * It's a method - invoke the method. This will set us up to * start executing this new code, so there's nothing more we * need to do here. */ /* push targetprop, targetobj, definingobj, self, and invokee */ { vm_val_t *fp = G_stk->push(5); (fp++)->set_propid(target_prop); (fp++)->set_obj(self.val.obj); (fp++)->set_obj(defining_obj); (fp++)->set_obj(self.val.obj); (fp++)->set_fnptr(val.val.ofs); } /* call the function */ return G_interpreter->do_call( vmg_ caller_ofs, (const uchar *)G_code_pool->get_ptr(val.val.ofs), argc, 0); default: /* for any other value, no arguments are allowed */ if (argc != 0) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* store the result in R0 */ *G_interpreter->get_r0() = val; /* resume execution where we left off */ return G_interpreter->get_entry_ptr() + caller_ofs; case VM_DSTRING: /* no arguments are allowed */ if (argc != 0) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* * it's a self-printing string - invoke the default string * output function (this is effectively a do_call()) */ return G_interpreter->disp_dstring( vmg_ val.val.ofs, caller_ofs, self.val.obj); case VM_OBJX: /* * Execute-on-eval object. If the value is an anonymous * function or other invokable object, call it. If it's a * string, print it. */ if (vm_objp(vmg_ val.val.obj)->get_invoker(vmg_ 0)) { /* convert to an ordinary anonymous function object */ vm_val_t funcptr; funcptr.set_obj(val.val.obj); /* prepare the invocation frame */ vm_val_t *fp = G_stk->push(5); (fp++)->set_propid(target_prop); (fp++)->set_obj(self.val.obj); (fp++)->set_obj(defining_obj); (fp++)->set_obj(self.val.obj); (fp++)->set_obj(val.val.obj); /* invoke the function */ return G_interpreter->call_func_ptr_fr( vmg_ &funcptr, argc, 0, caller_ofs); } else if (CVmObjString::is_string_obj(vmg_ val.val.obj)) { /* print the string */ G_interpreter->push_obj(vmg_ val.val.obj); return G_interpreter->disp_string_val( vmg_ caller_ofs, self.val.obj); } err_throw(VMERR_BAD_TYPE_CALL); case VM_BIFPTRX: /* Execute-on-eval built-in function. Call the function. */ G_interpreter->call_bif( vmg_ val.val.bifptr.set_idx, val.val.bifptr.func_idx, argc); /* resume execution where we left off */ return G_interpreter->get_entry_ptr() + caller_ofs; } } }; /* ------------------------------------------------------------------------ */ /* * Integer arithmetic with promotions to BigNumber on overflow */ #define VMRUN_PROMOTE_INTS_ON_OVERFLOW #ifdef VMRUN_PROMOTE_INTS_ON_OVERFLOW static void promote_int_add(VMG_ vm_val_t *aval, int32_t b) { bignum_t<10> sum((long)aval->val.intval); sum += (long)b; aval->set_obj(CVmObjBigNum::create(vmg_ FALSE, sum)); } static inline void int_add(VMG_ vm_val_t *aval, int32_t b) { int32_t a = aval->val.intval, sum = a + b; if (a >= 0 ? b <= 0 || sum >= a : b >= 0 || sum <= a) aval->val.intval = sum; else promote_int_add(vmg_ aval, b); } static void promote_int_sub(VMG_ vm_val_t *aval, int32_t b) { bignum_t<10> diff((long)aval->val.intval); diff -= (long)b; aval->set_obj(CVmObjBigNum::create(vmg_ FALSE, diff)); } static inline void int_sub(VMG_ vm_val_t *aval, int32_t b) { int32_t a = aval->val.intval, diff = a - b; if (a >= 0 ? b >= 0 || diff >= a : b < 0 || diff <= a) aval->val.intval = diff; else promote_int_sub(vmg_ aval, b); } static void promote_int_mul(VMG_ vm_val_t *aval, int32_t b) { bignum_t<20> prod((long)aval->val.intval); prod *= (long)b; aval->set_obj(CVmObjBigNum::create(vmg_ FALSE, prod)); } static inline void int_mul(VMG_ vm_val_t *aval, int32_t b) { int64_t a = aval->val.intval, prod = a * b; if (prod <= (int64_t)INT32MAXVAL && prod >= (int64_t)INT32MINVAL) aval->val.intval = (int32_t)prod; else promote_int_mul(vmg_ aval, b); } static void promote_int_div(VMG_ vm_val_t *aval, int32_t b) { bignum_t<10> quo((long)aval->val.intval); quo /= (long)b; aval->set_obj(CVmObjBigNum::create(vmg_ FALSE, quo)); } static inline void int_div(VMG_ vm_val_t *aval, int32_t b) { /* check for division by zero */ if (b == 0) err_throw(VMERR_DIVIDE_BY_ZERO); /* * NB - assuming 2's complement notation; the only integer division * that can result in overflow is INT32MINVAL/-1 */ int32_t a = aval->val.intval; if (a != INT32MINVAL || b != -1) aval->val.intval = a / b; else promote_int_div(vmg_ aval, b); } static void promote_int_neg(VMG_ vm_val_t *aval) { bignum_t<10> neg((long)aval->val.intval); neg = -neg; aval->set_obj(CVmObjBigNum::create(vmg_ FALSE, neg)); } static inline void int_neg(VMG_ vm_val_t *aval) { /* * NB - assuming 2's complement representation; the only integer * negation that can result in overflow is -INT32MINVAL */ if (aval->val.intval != INT32MINVAL) aval->val.intval = -aval->val.intval; else promote_int_neg(vmg_ aval); } #define INT_ADD(aval, b) int_add(vmg_ aval, b) #define INT_SUB(aval, b) int_sub(vmg_ aval, b) #define INT_MUL(aval, b) int_mul(vmg_ aval, b) #define INT_DIV(aval, b) int_div(vmg_ aval, b) #define INT_NEG(aval) int_neg(vmg_ aval) #else #define INT_ADD(aval, b) ((aval)->val.intval += (b)) #define INT_SUB(aval, b) ((aval)->val.intval -= (b)) #define INT_MUL(aval, b) ((aval)->val.intval *= (b)) #define INT_DIV(aval, b) \ if (b == 0) err_throw(VMERR_DIVIDE_BY_ZERO); \ else ((aval)->val.intval /= (b)) #define INT_NEG(aval) ((aval)->val.intval = -(aval)->val.intval) #endif /* ------------------------------------------------------------------------ */ /* * Calculations for conditionals */ #if 1 # define false_for_cond(v) \ ((v)->typ == VM_NIL || ((v)->typ == VM_INT && (v)->val.intval == 0)) # define true_for_cond(v) \ ((v)->typ == VM_TRUE \ || (v)->typ == VM_ENUM \ || ((v)->typ == VM_INT && !(v)->val.intval == 0)) # define is_valid_for_jst(v) \ ((v)->typ == VM_NIL || (v)->typ == VM_INT) #else # define false_for_cond(v) \ ((v)->typ == VM_NIL \ || ((v)->is_numeric(vmg0_) && (v)->num_is_zero(vmg0_))) # define true_for_cond(v) \ ((v)->typ == VM_TRUE \ || (v)->typ == VM_ENUM \ || ((v)->is_numeric(vmg0_) && !(v)->num_is_zero(vmg0_))) # define is_valid_for_jst(v) \ ((v)->typ == VM_NIL || (v)->is_numeric(vmg0_)) #endif /* ------------------------------------------------------------------------ */ /* * Execute byte code */ void CVmRun::run(VMG_ const uchar *start_pc) { /* * If you're concerned about a compiler warning on the following * 'register' declaration, refer to the footnote at the bottom of this * file (search for [REGISTER_P_FOOTNOTE]). Executive summary: you can * safely ignore the warning, and I'm keeping the code as it is because * it causes better optimization on some platforms, and is harmless * when it doesn't help with optimization. */ register const uchar *p = start_pc; vmrun_prop_eval propev; vm_val_t *valp; vm_val_t *valp2; vm_val_t val; vm_val_t val2; vm_val_t val3; int done; vm_obj_id_t obj; vm_prop_id_t prop; uint argc; vm_obj_id_t unhandled_exc; const uchar *last_pc = start_pc; const uchar **old_pc_ptr; /* save the enclosing program counter pointer, and remember the new one */ old_pc_ptr = pc_ptr_; pc_ptr_ = &last_pc; /* we're not done yet */ done = FALSE; /* no unhandled exception yet */ unhandled_exc = VM_INVALID_OBJ; /* * Come back here whenever we catch a run-time exception and find a * byte-code error handler to process it in the stack. We'll * re-enter our exception handler and resume byte-code execution at * the handler. */ resume_execution: /* * Execute all code within an exception frame. If any routine we * call throws an exception, we'll catch the exception and process * it as a run-time error. */ err_try { /* execute code until something makes us stop */ for (;;) { #ifdef VM_DEBUGGER /* * check for user-requested break, and step into the debugger * if we find it */ static int brkchk = 0; if (++brkchk > 10000) { /* reset the break counter */ brkchk = 0; /* check for break, and step into debugger if found */ if (os_break()) { if (G_debugger->is_in_debugger()) err_throw(VMERR_DBG_INTERRUPT); else G_debugger->set_break_stop(); } } /* if we're single-stepping, break into the debugger */ if (G_debugger->is_single_step()) G_debugger->step(vmg_ &p, entry_ptr_native_, FALSE, 0); /* check for a halt request from the debugger */ if (halt_vm_) err_throw(VMERR_DBG_HALT); exec_instruction: #endif /* VM_DEBUGGER */ /* * Remember the location of this instruction in a non-register * variable, in case there's an exception. (We know that * last_pc is guaranteed to be a non-register variable because * we take its address and store it in our pc_ptr_ member.) * * We need to know the location of the last instruction when * an exception occurs so that we can find the exception * handler. We want to encourage the compiler to enregister * 'p', since we access it so frequently in this routine; but * if it's in a register, there's a risk we'd get the * setjmp-time value in our exception handler. To handle both * needs, simply copy the value to our non-register variable * last_pc; this will still let the vast majority of our * access to 'p' use fast register operations if the compiler * allows this, while ensuring we have a safe copy around in * case of exceptions. */ last_pc = p; /* * Execute the current instruction. * * The order of the case table is in descending order of * frequency of instruction usage for the first 30 or so * instructions, according to empirical testing. This is * intended to keep the code for the most common instructions * within a short jump of the 'switch', to increase the chances * that an instruction jump will go to a location in cache. * This seems to yield a slight speed improvement on Intel * machines, and even if it doesn't help on a given machine, it * shouldn't do any harm relative to a randomly ordered case * table. */ switch(*p++) { case OPC_GETARGN0: push(get_param(vmg_ 0)); continue; case OPC_GETPROPSELF: /* evaluate the property of 'self' */ prop = get_op_uint16(&p); p = propev.get_prop( vmg_ p - entry_ptr_native_, get_self(vmg0_), prop); continue; case OPC_GETR0: /* push the contents of R0 */ push(&r0_); continue; case OPC_DUPR0: /* push the contents of R0 twice */ push(&r0_); push(&r0_); continue; case OPC_GETSETLCL1R0: /* set local from R0 and leave value on stack */ push(&r0_); *get_local(vmg_ get_op_uint8(&p)) = r0_; continue; case OPC_GETSETLCL1: /* set local and leave value on stack */ *get_local(vmg_ get_op_uint8(&p)) = *get(0); continue; case OPC_SETPROPSELF: /* get the value to set */ pop(&val); /* set it */ set_prop(vmg_ get_self(vmg0_), get_op_uint16(&p), &val); continue; case OPC_SETLCL1R0: /* store R0 in the specific local */ *get_local(vmg_ get_op_uint8(&p)) = r0_; continue; case OPC_GETARGN1: push(get_param(vmg_ 1)); continue; case OPC_GETLCLN0: push(get_local(vmg_ 0)); continue; case OPC_SETLCL1: /* get a pointer to the local */ valp = get_local(vmg_ get_op_uint8(&p)); /* pop the value into the local */ pop(valp); continue; case OPC_PUSHSELF: /* push 'self' */ push(get_self_val(vmg0_)); continue; case OPC_RETNIL: /* store nil in R0 */ r0_.set_nil(); /* return */ if ((p = do_return(vmg0_)) == 0) goto exit_loop; continue; case OPC_RETVAL: /* pop the return value into R0 */ pop(&r0_); /* return */ if ((p = do_return(vmg0_)) == 0) goto exit_loop; continue; case OPC_GETPROPLCL1: /* get the local whose property we're evaluating */ propev.self = *get_local(vmg_ get_op_uint8(&p)); /* evaluate the property of the local variable */ prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop); continue; case OPC_JNIL: /* jump if top of stack is nil */ valp = get(0); p += (valp->typ == VM_NIL ? osrp2s(p) : 2); /* discard the top value, regardless of what happened */ discard(); continue; case OPC_RET: /* return, leaving R0 unchanged */ if ((p = do_return(vmg0_)) == 0) goto exit_loop; continue; case OPC_PUSHENUM: /* push a UINT4 operand value */ push()->set_enum(get_op_uint32(&p)); continue; case OPC_JMP: /* unconditionally jump to the given offset */ p += osrp2s(p); continue; case OPC_JNE: /* jump if the two values at top of stack are not equal */ p += (!pop2_equal(vmg0_) ? osrp2s(p) : 2); continue; case OPC_JR0F: /* * if R0 is true, or it's a non-zero numeric value, or any * non-numeric and non-boolean value, stay put; otherwise, * jump */ if (false_for_cond(&r0_)) { /* it's zero or nil - jump */ p += osrp2s(p); } else { /* it's non-zero and non-nil - do not jump */ p += 2; } continue; case OPC_GETARGN2: push(get_param(vmg_ 2)); continue; case OPC_JGT: /* jump if greater */ p += (pop2_compare_gt(vmg0_) ? osrp2s(p) : 2); continue; case OPC_CALLPROPSELF: /* get the argument count */ argc = get_op_uint8(&p); do_opc_callpropself: /* evaluate the property of 'self' */ propev.self.set_obj(get_self(vmg0_)); prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop, argc); continue; case OPC_INDEX: /* index TOS-1 by TOS, storing the result at TOS-1 */ valp = get(1); if (apply_index(vmg_ valp, valp, get(0))) { /* discard the index value */ discard(); } else { /* try an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_idx, 1, VMERR_CANNOT_INDEX_TYPE); } continue; case OPC_DUP: /* re-push the item at top of stack */ push(get(0)); continue; case OPC_IDXLCL1INT8: /* get the local */ valp = get_local(vmg_ get_op_uint8(&p)); /* get the index value */ val2.set_int(get_op_uint8(&p)); /* * look up the indexed value of the local, storing the * result in a newly-pushed stack element */ if (!apply_index(vmg_ push(), valp, &val2)) { /* * try an operator overload - push the index value as * the argument */ push(&val2); p = op_overload(vmg_ p - entry_ptr_native_, -1, valp, G_predef->operator_idx, 1, VMERR_CANNOT_INDEX_TYPE); } continue; case OPC_GETLCLN2: push(get_local(vmg_ 2)); continue; case OPC_CALLPROP: /* get the argument count */ argc = get_op_uint8(&p); do_opc_callprop: /* pop the object whose property we're fetching */ pop(&propev.self); /* evaluate the property given by the immediate data */ prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop, argc); continue; case OPC_GETLCLN1: push(get_local(vmg_ 1)); continue; case OPC_GETARGN3: push(get_param(vmg_ 3)); continue; case OPC_GETLCLN3: push(get_local(vmg_ 3)); continue; case OPC_JNOTNIL: /* jump if top of stack is not nil */ valp = get(0); p += (valp->typ != VM_NIL ? osrp2s(p) : 2); /* discard the top value, regardless of what happened */ discard(); continue; case OPC_ITERNEXT: /* get the iterator object from the local */ valp = get_local(vmg_ get_op_uint16(&p)); /* get the next value from the iterator */ if (valp->typ == VM_OBJ && vm_objp(vmg_ valp->val.obj)->iter_next( vmg_ valp->val.obj, &val)) { /* another value is available - push it and proceed */ push(&val); p += 2; } else { /* no more values available - exit the loop */ p += osrp2s(p); } break; case OPC_PUSH_0: /* push the constant value 0 */ push()->set_int(0); continue; case OPC_GETPROP: /* get the object whose property we're fetching */ pop(&propev.self); /* evaluate the property */ prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop); continue; case OPC_GETLCLN4: push(get_local(vmg_ 4)); continue; case OPC_JE: /* jump if the two values at top of stack are equal */ p += (pop2_equal(vmg0_) ? osrp2s(p) : 2); continue; /* * End of case table sorting by instruction execution * frequency. The order of the remainder is more or less * arbitrary; we get diminishing returns from the frequency * ordering past a certain point, because CPU caches are * only so large. */ case OPC_PUSHNIL: /* push nil */ push()->set_nil(); continue; case OPC_PUSHTRUE: /* push true */ push()->set_true(); continue; case OPC_PUSH_1: /* push the constant value 1 */ push()->set_int(1); continue; case OPC_PUSHINT8: /* push an SBYTE operand value */ push()->set_int(get_op_int8(&p)); continue; case OPC_PUSHINT: /* push a UINT4 operand value */ push()->set_int(get_op_int32(&p)); continue; case OPC_INC: /* * Increment the value at top of stack. We must perform * the same type conversions as the ADD instruction does. * As an optimization, check to see if we have an integer * on top of the stack, and if so simply increment its * value without popping and repushing. */ if ((valp = get(0))->typ == VM_INT) { /* it's an integer - increment it, and we're done */ ++(valp->val.intval); } else { /* for other types, use the general handler */ p = compute_sum_inc(vmg_ p); } continue; case OPC_ADD: /* if they're both integers, add them the quick way */ valp = get(0); valp2 = get(1); if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the sum */ INT_ADD(valp2, valp->val.intval); /* discard the second value */ discard(); } else { /* for other types, use the general handler */ p = compute_sum_add(vmg_ p); } continue; case OPC_DEC: /* * Decrement the value at top of stack. We must perform * the same type conversions as the SUB instruction does. * As an optimization, check to see if we have an integer * on top of the stack, and if so simply decrement its * value without popping and repushing. */ if ((valp = get(0))->typ == VM_INT) { /* it's an integer - decrement it, and we're done */ INT_SUB(valp, 1); } else { /* for other types, use the general handler */ p = compute_diff_dec(vmg_ p); } continue; case OPC_SUB: /* if they're both integers, subtract them the quick way */ valp = get(0); valp2 = get(1); if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the difference */ INT_SUB(valp2, valp->val.intval); /* discard the second value */ discard(); } else { /* for other types, use the general handler */ p = compute_diff_sub(vmg_ p); } continue; case OPC_PUSHSTR: /* push UINT4 offset operand as a string */ push()->set_sstring(get_op_uint32(&p)); continue; case OPC_DISC: /* discard the item at the top of the stack */ discard(); continue; case OPC_DISC1: /* discard n items */ discard(get_op_uint8(&p)); continue; case OPC_PUSHLST: /* push UINT4 offset operand as a list */ push()->set_list(get_op_uint32(&p)); continue; case OPC_PUSHOBJ: /* push UINT4 object ID operand */ push()->set_obj(get_op_uint32(&p)); continue; case OPC_PUSHPROPID: /* push UINT2 property ID operand */ push()->set_propid(get_op_uint16(&p)); continue; case OPC_PUSHFNPTR: /* push a function pointer operand */ push()->set_fnptr(get_op_uint32(&p)); continue; case OPC_PUSHPARLST: { /* get the number of fixed parameters */ uint cnt = *p++; /* allocate the list from the parameters */ obj = CVmObjList::create_from_params( vmg_ cnt, get_cur_argc(vmg0_) - cnt); /* push the new list */ push()->set_obj(obj); } continue; case OPC_MAKELSTPAR: makelstpar(vmg0_); continue; case OPC_NEG: /* if it's an integer value, do the calculation inline */ if ((valp = get(0))->typ == VM_INT) { /* negate number in place */ INT_NEG(valp); } else if (valp->typ == VM_OBJ && vm_objp(vmg_ valp->val.obj)->neg_val( vmg_ &val2, valp->val.obj)) { /* the object defined it natively - store the result */ *valp = val2; } else { /* try the overloaded operator */ pop(&val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_neg, 0, VMERR_BAD_TYPE_NEG); } continue; case OPC_BNOT: /* check the type */ if ((valp = get(0))->typ == VM_INT) { /* bitwise NOT the integer on top of stack */ valp->val.intval = ~valp->val.intval; } else { /* try the overloaded operator */ pop(&val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_bit_not, 0, VMERR_BAD_TYPE_BIT_NOT); } continue; case OPC_MUL: /* if they're both integers, this is easy */ valp = get(0); valp2 = get(1); if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the product */ INT_MUL(valp2, valp->val.intval); /* discard the second value */ discard(); } else if (compute_product(vmg_ valp2, valp)) { /* success - discard the second value */ discard(); } else { /* try for an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_mul, 1, VMERR_BAD_TYPE_MUL); } continue; case OPC_DIV: /* if they're both integers, do the division inline */ valp = get(0); valp2 = get(1); if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the result of the division */ INT_DIV(valp2, valp->val.intval); /* discard the second value */ discard(); } else if (compute_quotient(vmg_ valp2, valp)) { /* success - discard the second operand */ discard(); } else { /* try for an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_div, 1, VMERR_BAD_TYPE_DIV); } continue; case OPC_MOD: /* remainder number at (TOS-1) by number at top of stack */ valp = get(0); valp2 = get(1); /* if they're both integers, do a quick local operation */ if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* check for division by zero */ if (valp->val.intval == 0) err_throw(VMERR_DIVIDE_BY_ZERO); /* * compute the remainder (TOS-1) % (TOS), leaving the * result at (TOS-1), and discard the second operand */ valp2->val.intval = os_remainder_long( valp2->val.intval, valp->val.intval); /* discard the second value */ discard(); } else { /* try for an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_mod, 1, VMERR_BAD_TYPE_MOD); } continue; case OPC_BAND: /* bitwise AND two integers on top of stack */ valp = get(0); valp2 = get(1); /* if we have two integers, do it the quick way */ if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the result and discard the second operand */ valp2->val.intval &= valp->val.intval; /* discard the second value */ discard(); } else { /* try for an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_bit_and, 1, VMERR_BAD_TYPE_BIT_AND); } continue; case OPC_BOR: /* bitwise OR two integers on top of stack */ valp = get(0); valp2 = get(1); /* check the types */ if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the result */ valp2->val.intval |= valp->val.intval; /* discard the second operand */ discard(); } else { /* try for an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_bit_or, 1, VMERR_BAD_TYPE_BIT_OR); } continue; case OPC_SHL: /* * bit-shift left integer at (TOS-1) by integer at top * of stack */ valp = get(0); valp2 = get(1); /* check the types */ if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the result */ valp2->val.intval <<= valp->val.intval; /* discard the second operand */ discard(); } else { /* try for an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_shl, 1, VMERR_BAD_TYPE_SHL); } continue; case OPC_ASHR: /* * arithmetic shift right integer at (TOS-1) by integer at * top of stack */ valp = get(0); valp2 = get(1); /* check types */ if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the result */ valp2->val.intval = t3_ashr(valp2->val.intval, valp->val.intval); /* discard the second operand */ discard(); } else { /* try for an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_ashr, 1, VMERR_BAD_TYPE_ASHR); } continue; case OPC_LSHR: /* * logical shift right integer at (TOS-1) by integer at * top of stack */ valp = get(0); valp2 = get(1); /* check types */ if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the result */ valp2->val.intval = t3_lshr(valp2->val.intval, valp->val.intval); /* discard the second operand */ discard(); } else { /* try for an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_lshr, 1, VMERR_BAD_TYPE_LSHR); } continue; case OPC_XOR: /* XOR two values at top of stack */ popval_2(vmg_ &val, &val2); if (!xor_and_push(vmg_ &val, &val2)) { /* try for an operator overload */ push(&val2); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_xor, 1, VMERR_BAD_TYPE_XOR); } continue; case OPC_NOT: /* * invert the logic value; if the value is a number, * treat 0 as nil and non-zero as true */ valp = get(0); switch(valp->typ) { case VM_NIL: /* !nil -> true */ valp->set_true(); continue; case VM_OBJ: /* !obj -> true if obj is nil, nil otherwise */ valp->set_logical(valp->val.obj == VM_INVALID_OBJ); continue; case VM_TRUE: case VM_PROP: case VM_SSTRING: case VM_LIST: case VM_CODEOFS: case VM_FUNCPTR: case VM_ENUM: /* these are all considered true, so !them -> nil */ valp->set_nil(); continue; case VM_INT: /* !int -> true if int is 0, nil otherwise */ valp->set_logical(valp->val.intval == 0); continue; default: err_throw(VMERR_NO_LOG_CONV); } continue; case OPC_BOOLIZE: /* set to a boolean value */ valp = get(0); switch(valp->typ) { case VM_NIL: case VM_TRUE: /* it's already a logical value - leave it alone */ continue; case VM_INT: /* integer: 0 -> nil, non-zero -> true */ valp->set_logical(valp->val.intval); continue; case VM_ENUM: /* an enum is always non-nil */ valp->set_true(); continue; default: err_throw(VMERR_NO_LOG_CONV); } continue; case OPC_EQ: /* compare two values at top of stack for equality */ push_bool(vmg_ pop2_equal(vmg0_)); continue; case OPC_NE: /* compare two values at top of stack for inequality */ push_bool(vmg_ !pop2_equal(vmg0_)); continue; case OPC_LT: /* compare values at top of stack - true if (TOS-1) < TOS */ push_bool(vmg_ pop2_compare_lt(vmg0_)); continue; case OPC_LE: /* compare values at top of stack - true if (TOS-1) <= TOS */ push_bool(vmg_ pop2_compare_le(vmg0_)); continue; case OPC_GT: /* compare values at top of stack - true if (TOS-1) > TOS */ push_bool(vmg_ pop2_compare_gt(vmg0_)); continue; case OPC_GE: /* compare values at top of stack - true if (TOS-1) >= TOS */ push_bool(vmg_ pop2_compare_ge(vmg0_)); continue; case OPC_VARARGC: { /* get the modified opcode */ uchar opc = *p++; /* * skip the immediate data argument count - this is * superseded by our dynamic argument counter */ ++p; /* pop the argument counter */ pop_int(vmg_ &val); argc = val.val.intval; /* execute the appropriate next opcode */ switch(opc) { case OPC_CALL: goto do_opc_call; case OPC_PTRCALL: goto do_opc_ptrcall; case OPC_CALLPROP: goto do_opc_callprop; case OPC_PTRCALLPROP: goto do_opc_ptrcallprop; case OPC_CALLPROPSELF: goto do_opc_callpropself; case OPC_PTRCALLPROPSELF: goto do_opc_ptrcallpropself; case OPC_OBJCALLPROP: goto do_opc_objcallprop; case OPC_CALLPROPLCL1: goto do_opc_callproplcl1; case OPC_CALLPROPR0: goto do_opc_callpropr0; case OPC_INHERIT: goto do_opc_inherit; case OPC_PTRINHERIT: goto do_opc_ptrinherit; case OPC_EXPINHERIT: goto do_opc_expinherit; case OPC_PTREXPINHERIT: goto do_opc_ptrexpinherit; case OPC_DELEGATE: goto do_opc_delegate; case OPC_PTRDELEGATE: goto do_opc_ptrdelegate; case OPC_BUILTIN_A: goto do_opc_builtin_a; case OPC_BUILTIN_B: goto do_opc_builtin_b; case OPC_BUILTIN_C: goto do_opc_builtin_c; case OPC_BUILTIN_D: goto do_opc_builtin_d; case OPC_BUILTIN1: goto do_opc_builtin1; case OPC_BUILTIN2: goto do_opc_builtin2; case OPC_NEW1: goto do_opc_new1; case OPC_TRNEW1: goto do_opc_trnew1; case OPC_NEW2: goto do_opc_new2; case OPC_TRNEW2: goto do_opc_trnew2; default: err_throw(VMERR_INVALID_OPCODE_MOD); continue; } } continue; case OPC_NAMEDARGPTR: /* * Pointer to named argument table. Discard the named * arguments (the count is given by a one-byte operand), * then skip the table pointer (a two-byte operand). */ discard(get_op_uint8(&p)); p += 2; continue; case OPC_NAMEDARGTAB: /* * Named argument table. As with NAMEDARGPTR, we must * discard the named arguments. Then we simply skip the * table - the table is for the benefit of callees. */ { pool_ofs_t ofs = get_op_uint16(&p); discard(get_op_uint16(&p)); p += ofs - 2; } continue; case OPC_CALL: /* get the argument count */ argc = get_op_uint8(&p); do_opc_call: { /* get the code offset to invoke */ pool_ofs_t ofs = get_op_int32(&p); /* call it */ p = do_call_func_nr(vmg_ p - entry_ptr_native_, ofs, argc); } continue; case OPC_PTRCALL: /* get the argument count */ argc = get_op_uint8(&p); do_opc_ptrcall: /* retrieve the target of the call */ popval(vmg_ &val); /* * if it's a prop ID, and there's a valid "self" object, * treat it as a PTRCALLPROPSELF */ if (val.typ == VM_PROP && get_self_check(vmg0_) != VM_INVALID_OBJ) goto do_opc_ptrcallpropself_val; /* call the function */ p = call_func_ptr(vmg_ &val, argc, 0, p - entry_ptr_native_); continue; case OPC_RETTRUE: /* store true in R0 */ r0_.set_true(); /* return */ if ((p = do_return(vmg0_)) == 0) goto exit_loop; continue; case OPC_GETPROPR0: /* evaluate the property of R0 */ propev.self = r0_; prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop); continue; case OPC_CALLPROPLCL1: /* get the argument count */ argc = get_op_uint8(&p); do_opc_callproplcl1: /* get the local whose property we're calling */ propev.self = *get_local(vmg_ get_op_uint8(&p)); /* call the property of the local */ prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop, argc); continue; case OPC_CALLPROPR0: /* get the argument count */ argc = get_op_uint8(&p); do_opc_callpropr0: /* call the property of R0 */ propev.self = r0_; prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop, argc); continue; case OPC_PTRCALLPROP: /* get the argument count */ argc = get_op_uint8(&p); do_opc_ptrcallprop: /* * pop the property to be evaluated, and the object whose * property we're evaluating */ pop_prop(vmg_ &val); pop(&propev.self); /* evaluate the property */ p = propev.get_prop(vmg_ p - entry_ptr_native_, val.val.prop, argc); continue; case OPC_PTRCALLPROPSELF: /* get the argument count */ argc = get_op_uint8(&p); do_opc_ptrcallpropself: /* get the property to be evaluated */ pop_prop(vmg_ &val); do_opc_ptrcallpropself_val: /* evaluate the property of 'self' */ propev.self.set_obj(get_self(vmg0_)); p = propev.get_prop(vmg_ p - entry_ptr_native_, val.val.prop, argc); continue; case OPC_OBJGETPROP: /* get the object */ propev.self.set_obj((vm_obj_id_t)get_op_uint32(&p)); /* evaluate the property */ prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop); continue; case OPC_OBJCALLPROP: /* get the argument count */ argc = get_op_uint8(&p); do_opc_objcallprop: /* get the object */ propev.self.set_obj((vm_obj_id_t)get_op_uint32(&p)); /* evaluate the property */ prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop, argc); continue; case OPC_GETLCL1: /* push the local */ pushval(vmg_ get_local(vmg_ get_op_uint8(&p))); continue; case OPC_GETLCLN5: pushval(vmg_ get_local(vmg_ 5)); continue; case OPC_GETLCL2: /* push the local */ pushval(vmg_ get_local(vmg_ get_op_uint16(&p))); continue; case OPC_GETARG1: /* push the argument */ pushval(vmg_ get_param(vmg_ get_op_uint8(&p))); continue; case OPC_GETARG2: /* push the argument */ pushval(vmg_ get_param(vmg_ get_op_uint16(&p))); continue; case OPC_SETSELF: /* retrieve the 'self' object */ pop(&val); /* if it's nil, set the 'obj' field to null */ if (val.typ == VM_NIL) val.val.obj = VM_INVALID_OBJ; /* set 'self' */ set_self(vmg_ &val); continue; case OPC_STORECTX: /* create the context object */ create_loadctx_obj(vmg_ push(), get_self(vmg0_), get_defining_obj(vmg0_), get_orig_target_obj(vmg0_), get_target_prop(vmg0_)); continue; case OPC_LOADCTX: { /* * convert the context object (at top of stack) to a * list pointer */ const char *lstp = get(0)->get_as_list(vmg0_); /* throw an error if it's not what we're expecting */ if (lstp == 0 || vmb_get_len(lstp) < 4) err_throw(VMERR_LIST_VAL_REQD); /* retrieve and store the context elements */ set_method_ctx( vmg_ vmb_get_dh_obj(lstp + VMB_LEN), vmb_get_dh_prop(lstp + VMB_LEN + VMB_DATAHOLDER), vmb_get_dh_obj(lstp + VMB_LEN + 2*VMB_DATAHOLDER), vmb_get_dh_obj(lstp + VMB_LEN + 3*VMB_DATAHOLDER)); /* discard the context object at top of stack */ discard(); } continue; case OPC_PUSHCTXELE: /* check our context element type */ switch(*p++) { case PUSHCTXELE_TARGPROP: /* push the target property ID */ push(get_from_frame(frame_ptr_, VMRUN_FPOFS_PROP)); continue; case PUSHCTXELE_TARGOBJ: /* push the original target object ID */ push(get_from_frame(frame_ptr_, VMRUN_FPOFS_ORIGTARG)); continue; case PUSHCTXELE_DEFOBJ: /* push the defining object */ push(get_from_frame(frame_ptr_, VMRUN_FPOFS_DEFOBJ)); continue; case PUSHCTXELE_INVOKEE: /* push the invokee object */ push(get_from_frame(frame_ptr_, VMRUN_FPOFS_INVOKEE)); continue; default: /* the opcode is not valid in this VM version */ err_throw(VMERR_INVALID_OPCODE); } continue; case OPC_GETARGC: /* push the argument counter */ push_int(vmg_ get_cur_argc(vmg0_)); continue; case OPC_DUP2: /* * duplicate the top two elements: first push the * second-from-top, then push the old top (which will now * be the second-from-top, thanks to our first push) */ pushval(vmg_ get(1)); pushval(vmg_ get(1)); continue; case OPC_SWITCH: { /* get the control value */ valp = get(0); /* get the case count */ uint cnt = get_op_uint16(&p); /* iterate through the case table */ for ( ; cnt != 0 ; p += 7, --cnt) { /* get this value */ vmb_get_dh((const char *)p, &val2); /* check if the values match */ if (valp->equals(vmg_ &val2)) { /* it matches - jump to this offset */ p += VMB_DATAHOLDER; p += osrp2s(p); /* no need to look any further */ break; } } /* discard the control value */ discard(); /* if we didn't find it, jump to the default case */ if (cnt == 0) p += osrp2s(p); } continue; case OPC_JT: /* get the value */ valp = get(0); /* * if it's true, or a non-zero numeric value, or any * non-numeric and non-boolean value, jump */ if (false_for_cond(valp)) { /* it's zero or nil - do not jump */ p += 2; } else { /* it's non-zero and non-nil - jump */ p += osrp2s(p); } /* discard the value */ discard(); continue; case OPC_JR0T: /* * if R0 is true, or it's a non-zero numeric value, or any * non-numeric and non-boolean value, jump */ if (false_for_cond(&r0_)) { /* it's zero or nil - do not jump */ p += 2; } else { /* it's non-zero and non-nil - jump */ p += osrp2s(p); } continue; case OPC_JF: /* get the value */ valp = get(0); /* * if it's true, or a non-zero numeric value, or any * non-numeric and non-boolean value, do not jump; * otherwise, jump */ if (false_for_cond(valp)) { /* it's zero or nil - jump */ p += osrp2s(p); } else { /* it's non-zero and non-nil - do not jump */ p += 2; } /* discard the value */ discard(); continue; case OPC_JGE: /* jump if greater or equal */ p += (pop2_compare_ge(vmg0_) ? osrp2s(p) : 2); continue; case OPC_JLT: /* jump if less */ p += (pop2_compare_lt(vmg0_) ? osrp2s(p) : 2); continue; case OPC_JLE: /* jump if less or equal */ p += (pop2_compare_le(vmg0_) ? osrp2s(p) : 2); continue; case OPC_JST: /* get (do not remove) the element at top of stack */ valp = get(0); /* * if it's true, an enum value, or a non-zero number, jump, * saving the value; otherwise, require that it be a * logical value, pop it, and proceed */ if (true_for_cond(valp)) { /* it's true - save it and jump */ p += osrp2s(p); } else { /* * it's not true - discard the value, but require * that it be a valid logical value */ if (!is_valid_for_jst(valp)) err_throw(VMERR_LOG_VAL_REQD); discard(); /* skip to the next instruction */ p += 2; } continue; case OPC_JSF: /* get (do not remove) the element at top of stack */ valp = get(0); /* * if it's nil or zero, jump, saving the value; * otherwise, discard the value and proceed */ if (false_for_cond(valp)) { /* it's nil or zero - save it and jump */ p += osrp2s(p); } else { /* it's something non-false - discard it */ discard(); /* skip to the next instruction */ p += 2; } continue; case OPC_LJSR: /* * compute and push the offset of the next instruction * (at +2 because of the branch offset operand) from our * method header - this will be the return address, * which in this offset format will survive any code * swapping that might occur in subsequent execution */ push_int(vmg_ pc_to_method_ofs(p + 2)); /* jump to the target address */ p += osrp2s(p); continue; case OPC_LRET: /* get the indicated local variable */ valp = get_local(vmg_ get_op_uint16(&p)); /* the value must be an integer */ if (valp->typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); /* * jump to the code address obtained from adding the * integer value in the given local variable to the * current method header pointer */ p = entry_ptr_native_ + valp->val.intval; continue; case OPC_SWAP: /* swap the top two elements on the stack */ valp = get(0); valp2 = get(1); /* make a working copy of TOS */ val = *valp; /* copy TOS-1 over TOS */ *valp = *valp2; /* copy the working copy of TOS over TOS-1 */ *valp2 = val; continue; case OPC_SWAP2: /* swap the top two elements with the next two */ valp = get(0); valp2 = get(1); /* make a working copy of elements 2 & 3 */ val = *get(2); val2 = *get(3); /* copy 0,1 over 2,3 */ *get(2) = *valp; *get(3) = *valp2; /* copy the saved 2,3 over 0,1 */ *valp = val; *valp2 = val2; continue; case OPC_SWAPN: /* swap elements at two given stack indices */ valp = get(get_op_uint8(&p)); valp2 = get(get_op_uint8(&p)); /* make a working copy of val1 */ val = *valp; /* write val2 over val1 */ *valp = *valp2; /* write the copy of val1 over val2 */ *valp2 = val; continue; case OPC_GETSPN: /* push stack element at index */ push(get(get_op_uint8(&p))); continue; case OPC_SAY: { /* get the string offset */ pool_ofs_t ofs = get_op_int32(&p); /* display it */ p = disp_dstring(vmg_ ofs, p - entry_ptr_native_, get_self_check(vmg0_)); } continue; case OPC_SAYVAL: /* invoke the default string display function */ p = disp_string_val(vmg_ p - entry_ptr_native_, get_self_check(vmg0_)); continue; case OPC_INHERIT: /* get the argument count */ argc = get_op_uint8(&p); do_opc_inherit: /* inherit the property */ prop = (vm_prop_id_t)get_op_uint16(&p); p = inh_prop(vmg_ p - entry_ptr_native_, prop, argc); continue; case OPC_PTRINHERIT: /* get the argument count */ argc = get_op_uint8(&p); do_opc_ptrinherit: /* pop the property to be inherited */ pop_prop(vmg_ &val); /* inherit it */ p = inh_prop(vmg_ p - entry_ptr_native_, val.val.prop, argc); continue; case OPC_EXPINHERIT: /* get the argument count */ argc = get_op_uint8(&p); do_opc_expinherit: /* get the property to inherit */ prop = (vm_prop_id_t)get_op_uint16(&p); /* get the superclass to inherit it from */ val.set_obj((vm_obj_id_t)get_op_uint32(&p)); /* * inherit it -- process this essentially the same way * as a normal CALLPROP, since we're going to evaluate * the given property of the given object, but retain * the current 'self' object */ val2.set_obj(get_self(vmg0_)); p = get_prop(vmg_ p - entry_ptr_native_, &val, prop, &val2, argc, 0); continue; case OPC_PTREXPINHERIT: /* get the argument count */ argc = get_op_uint8(&p); do_opc_ptrexpinherit: /* pop the property to inherit */ pop_prop(vmg_ &val); /* get the superclass to inherit it from */ val3.set_obj((vm_obj_id_t)get_op_uint32(&p)); /* inherit it */ val2.set_obj(get_self(vmg0_)); p = get_prop(vmg_ p - entry_ptr_native_, &val3, val.val.prop, &val2, argc, 0); continue; case OPC_DELEGATE: /* get the argument count */ argc = get_op_uint8(&p); do_opc_delegate: /* get the property to inherit */ prop = (vm_prop_id_t)get_op_uint16(&p); /* get the object to delegate to */ pop(&val); /* delegate it */ val2.set_obj(get_self(vmg0_)); p = get_prop(vmg_ p - entry_ptr_native_, &val, prop, &val2, argc, 0); continue; case OPC_PTRDELEGATE: /* get the argument count */ argc = get_op_uint8(&p); do_opc_ptrdelegate: /* pop the property to delegate to */ pop_prop(vmg_ &val); /* pop the object to delegate to */ pop(&val2); /* delegate it */ val3.set_obj(get_self(vmg0_)); p = get_prop(vmg_ p - entry_ptr_native_, &val2, val.val.prop, &val3, argc, 0); continue; case OPC_BUILTIN_A: /* get the argument count */ argc = get_op_uint8(&p); do_opc_builtin_a: { /* get the function index */ uint idx = get_op_uint8(&p); /* call the function in set #0 */ call_bif(vmg_ 0, idx, argc); } continue; case OPC_BUILTIN_B: /* get the argument count */ argc = get_op_uint8(&p); do_opc_builtin_b: { /* get the function index */ uint idx = get_op_uint8(&p); /* call the function in set #1 */ call_bif(vmg_ 1, idx, argc); } continue; case OPC_BUILTIN_C: /* get the argument count */ argc = get_op_uint8(&p); do_opc_builtin_c: { /* get the function index */ uint idx = get_op_uint8(&p); /* call the function in set #2 */ call_bif(vmg_ 2, idx, argc); } continue; case OPC_BUILTIN_D: /* get the argument count */ argc = get_op_uint8(&p); do_opc_builtin_d: { /* get the function index */ uint idx = get_op_uint8(&p); /* call the function in set #3 */ call_bif(vmg_ 3, idx, argc); } continue; case OPC_BUILTIN1: /* get the argument count */ argc = get_op_uint8(&p); do_opc_builtin1: { /* get the function index and function set ID */ uint idx = get_op_uint8(&p); uint set_idx = get_op_uint8(&p); /* call the function */ call_bif(vmg_ set_idx, idx, argc); } continue; case OPC_BUILTIN2: /* get the argument count */ argc = get_op_uint8(&p); do_opc_builtin2: { /* get the function index and function set ID */ uint idx = get_op_uint16(&p); uint set_idx = get_op_uint8(&p); /* call the function in set #0 */ call_bif(vmg_ set_idx, idx, argc); } continue; case OPC_IDXINT8: /* * make a copy of the value to index, so we can overwrite * the stack slot with the result */ val = *(valp = get(0)); /* set up the index value */ val2.set_int(get_op_uint8(&p)); /* apply the index, storing the result at TOS */ if (!apply_index(vmg_ valp, &val, &val2)) { /* * try an operator overload - push the index value as * the argument */ pop(&val); push(&val2); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_idx, 1, VMERR_CANNOT_INDEX_TYPE); } continue; case OPC_NEW1: /* get the argument count */ argc = get_op_uint8(&p); /* fall through to do_opc_new1 */ do_opc_new1: { /* get the metaclass ID and create the object */ uint idx = get_op_uint8(&p); p = new_and_store_r0(vmg_ p, idx, argc, FALSE); } continue; case OPC_TRNEW1: /* get the argument count */ argc = get_op_uint8(&p); /* fall through to do_opc_trnew1 */ do_opc_trnew1: { /* get the metaclass ID and create the object */ uint idx = get_op_uint8(&p); p = new_and_store_r0(vmg_ p, idx, argc, TRUE); } continue; case OPC_NEW2: /* get the argument count */ argc = get_op_uint16(&p); /* fall through to do_opc_new2 */ do_opc_new2: { /* get the metaclass ID */ uint idx = get_op_uint16(&p); /* create the new object */ p = new_and_store_r0(vmg_ p, idx, argc, FALSE); } continue; case OPC_TRNEW2: /* get the argument count */ argc = get_op_uint16(&p); /* fall through to do_opc_trnew2 */ do_opc_trnew2: { /* get the metaclass ID */ uint idx = get_op_uint16(&p); /* create the new object */ p = new_and_store_r0(vmg_ p, idx, argc, TRUE); } continue; case OPC_NOP: /* NO OP - no effect */ continue; case OPC_INCLCL: /* get the local */ { int itmp = get_op_uint16(&p); valp = get_local(vmg_ itmp); /* do the increment inline if it's an integer */ if (valp->typ == VM_INT) { /* it's a number - just increment the value */ INT_ADD(valp, 1); } else { /* it's a non-numeric value - do the full addition */ val2.set_int(1); if (!compute_sum(vmg_ valp, &val2)) { /* * Check for an operator overload. We need to * do this with a recursive call to update the * local on return. */ push(&val2); p = op_overload(vmg_ p - entry_ptr_native_, itmp, valp, G_predef->operator_add, 1, VMERR_BAD_TYPE_ADD); } } } continue; case OPC_DECLCL: { /* get the local */ int itmp = get_op_uint16(&p); valp = get_local(vmg_ itmp); /* handle integers inline */ if (valp->typ == VM_INT) { /* it's a number - just decrement the value */ INT_SUB(valp, 1); } else { /* non-int - we must do the full subtraction work */ val2.set_int(1); if (!compute_diff(vmg_ valp, &val2)) { /* * Check for an operator overload. We need to * do this with a recursive call to update the * local on return. */ push(&val2); p = op_overload(vmg_ p - entry_ptr_native_, itmp, valp, G_predef->operator_sub, 1, VMERR_BAD_TYPE_SUB); } } } continue; case OPC_ADDILCL1: { /* get the local */ int itmp = get_op_uint8(&p); valp = get_local(vmg_ itmp); /* handle it inline if it's an integer */ if (valp->typ == VM_INT) { /* it's a number - just add the value */ INT_ADD(valp, get_op_int8(&p)); } else { /* get the number to add */ val2.set_int(get_op_int8(&p)); /* compute the sum */ p = compute_sum_lcl_imm(vmg_ valp, &val2, itmp, p); } } continue; case OPC_ADDILCL4: { /* get the local */ int itmp = get_op_uint16(&p); valp = get_local(vmg_ itmp); /* if it's an integer, handle it in-line */ if (valp->typ == VM_INT) { /* it's a number - just add the value */ INT_ADD(valp, get_op_int32(&p)); } else { /* get the number to add */ val2.set_int(get_op_int32(&p)); /* compute the sum */ p = compute_sum_lcl_imm(vmg_ valp, &val2, itmp, p); } } continue; case OPC_ADDTOLCL: { /* get the local */ int itmp = get_op_uint16(&p); valp = get_local(vmg_ itmp); /* get the value to add */ valp2 = get(0); /* if they're both integers, handle in-line */ if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* add the value to the local */ INT_ADD(valp, valp2->val.intval); /* done with the addend */ discard(); } else { /* compute the sum, leaving the result in the local */ if (compute_sum(vmg_ valp, valp2)) { /* done - discard the addend */ discard(); } else { /* * check for an overload - do it recursively, * since we need to update the local on return */ push(valp2); p = op_overload(vmg_ p - entry_ptr_native_, itmp, valp, G_predef->operator_add, 1, VMERR_BAD_TYPE_ADD); } } } continue; case OPC_SUBFROMLCL: { /* get the local */ int itmp = get_op_uint16(&p); valp = get_local(vmg_ itmp); /* get the value to add */ valp2 = get(0); /* if they're both integers, handle in-line */ if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* subtract the value from the local */ INT_SUB(valp, valp2->val.intval); /* discard the subtrahend */ discard(); } else if (compute_diff(vmg_ valp, valp2)) { /* done - discard the subtrahend */ discard(); } else { /* * no native implementation - check for an * overload; do this recursively since we need to * update the local on return */ push(valp2); p = op_overload(vmg_ p - entry_ptr_native_, itmp, valp, G_predef->operator_sub, 1, VMERR_BAD_TYPE_SUB); } } continue; case OPC_ZEROLCL1: /* get the local and set it to zero */ get_local(vmg_ get_op_uint8(&p))->set_int(0); continue; case OPC_ZEROLCL2: /* get the local and set it to zero */ get_local(vmg_ get_op_uint16(&p))->set_int(0); continue; case OPC_NILLCL1: /* get the local and set it to zero */ get_local(vmg_ get_op_uint8(&p))->set_nil(); continue; case OPC_NILLCL2: /* get the local and set it to zero */ get_local(vmg_ get_op_uint16(&p))->set_nil(); continue; case OPC_ONELCL1: /* get the local and set it to zero */ get_local(vmg_ get_op_uint8(&p))->set_int(1); continue; case OPC_ONELCL2: /* get the local and set it to zero */ get_local(vmg_ get_op_uint16(&p))->set_int(1); continue; case OPC_SETLCL2: /* get a pointer to the local */ valp = get_local(vmg_ get_op_uint16(&p)); /* pop the value into the local */ popval(vmg_ valp); continue; case OPC_SETARG1: /* get a pointer to the parameter */ valp = get_param(vmg_ get_op_uint8(&p)); /* pop the value into the parameter */ popval(vmg_ valp); continue; case OPC_SETARG2: /* get a pointer to the parameter */ valp = get_param(vmg_ get_op_uint16(&p)); /* pop the value into the parameter */ popval(vmg_ valp); continue; case OPC_SETIND: /* pop the index */ popval(vmg_ &val2); /* pop the value to be indexed */ popval(vmg_ &val); /* pop the value to assign */ popval(vmg_ &val3); /* assign into the index */ if (set_index(vmg_ &val, &val2, &val3)) { /* push the new container value */ pushval(vmg_ &val); } else { /* try operator overloading */ push(&val3); push(&val2); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_setidx, 2, VMERR_CANNOT_INDEX_TYPE); } continue; case OPC_SETINDLCL1I8: { /* get the local */ int itmp = get_op_uint8(&p); valp = get_local(vmg_ itmp); /* get the index value */ val2.set_int(get_op_uint8(&p)); /* pop the value to assign */ popval(vmg_ &val3); /* * set the index value - this will update the local * variable directly if the container value changes */ if (!set_index(vmg_ valp, &val2, &val3)) { /* try operator overloading */ push(&val3); push(&val2); p = op_overload(vmg_ p - entry_ptr_native_, itmp, valp, G_predef->operator_setidx, 2, VMERR_CANNOT_INDEX_TYPE); } } continue; case OPC_SETPROP: /* get the object whose property we're setting */ pop_obj(vmg_ &val); /* pop the value we're setting */ popval(vmg_ &val2); /* set the value */ set_prop(vmg_ val.val.obj, get_op_uint16(&p), &val2); continue; case OPC_PTRSETPROP: /* get the property and object to set */ pop_prop(vmg_ &val); pop_obj(vmg_ &val2); /* get the value to set */ popval(vmg_ &val3); /* set it */ set_prop(vmg_ val2.val.obj, val.val.prop, &val3); continue; case OPC_OBJSETPROP: /* get the object */ obj = (vm_obj_id_t)get_op_uint32(&p); /* get the new value */ popval(vmg_ &val); /* set the property */ set_prop(vmg_ obj, get_op_uint16(&p), &val); continue; case OPC_PUSHSTRI: /* push inline string */ { /* get the length prefix */ uint cnt = get_op_uint16(&p); /* create the new string from the inline data */ obj = CVmObjString::create( vmg_ FALSE, (const char *)p, cnt); /* skip past the string's bytes */ p += cnt; /* push the new string */ push()->set_obj(obj); } continue; case OPC_PUSHBIFPTR: { /* push pointer to built-in function */ uint idx = get_op_uint16(&p); push()->set_bifptr(get_op_uint16(&p), (ushort)idx); validate_bifptr(vmg0_); } continue; case OPC_THROW: /* pop the exception object */ pop_obj(vmg_ &val); /* * Throw it. Note that we pass the start of the current * instruction as the program counter, since we want to * find the exception handler (if any) for the current * instruction, not for the next instruction. */ if ((p = do_throw(vmg_ p - 1, val.val.obj)) == 0) { /* remember the unhandled exception for re-throwing */ unhandled_exc = val.val.obj; /* terminate execution */ goto exit_loop; } continue; #ifdef VM_DEBUGGER case OPC_GETPROPDATA: /* get the object whose property we're fetching */ pop(&propev.self); /* * if the object is not an object, it's one of the native * types, in which case we'll definitely run native code to * evaluate the property, in which case it's not valid for * speculative evaluation */ if (propev.self.typ != VM_OBJ) err_throw(VMERR_BAD_SPEC_EVAL); /* get the property */ prop = (vm_prop_id_t)get_op_uint16(&p); /* check validity for speculative evaluation */ check_prop_spec_eval(vmg_ propev.self.val.obj, prop); /* evaluate the property given by the immediate data */ p = propev.get_prop(vmg_ p - entry_ptr_native_, prop); continue; case OPC_PTRGETPROPDATA: /* get the property and object to evaluate */ pop_prop(vmg_ &val); pop(&propev.self); /* * if the object is not an object, it's one of the native * types, in which case we'll definitely run native code to * evaluate the property, in which case it's not valid for * speculative evaluation */ if (propev.self.typ != VM_OBJ) err_throw(VMERR_BAD_SPEC_EVAL); /* check validity for speculative evaluation */ check_prop_spec_eval(vmg_ propev.self.val.obj, val.val.prop); /* evaluate it */ p = propev.get_prop(vmg_ p - entry_ptr_native_, val.val.prop); continue; case OPC_GETDBARGC: /* push the argument count from the selected frame */ push_int(vmg_ get_argc_at_level(vmg_ get_op_uint16(&p) + 1)); continue; case OPC_GETDBLCL: { /* get the local variable number and stack level */ uint idx = get_op_uint16(&p); int level = get_op_uint16(&p); /* push the value */ pushval(vmg_ get_local_at_level(vmg_ idx, level + 1)); } continue; case OPC_GETDBARG: { /* get the parameter variable number and stack level */ uint idx = get_op_uint16(&p); int level = get_op_uint16(&p); /* push the value */ pushval(vmg_ get_param_at_level(vmg_ idx, level + 1)); } continue; case OPC_SETDBLCL: { /* get the local variable number and stack level */ uint idx = get_op_uint16(&p); int level = get_op_uint16(&p); /* get the local pointer */ vm_val_t *valp = get_local_at_level(vmg_ idx, level + 1); /* pop the value into the local */ popval(vmg_ valp); } continue; case OPC_SETDBARG: { /* get the parameter variable number and stack level */ uint idx = get_op_uint16(&p); int level = get_op_uint16(&p); /* get the parameter pointer */ vm_val_t *valp = get_param_at_level(vmg_ idx, level + 1); /* pop the value into the local */ popval(vmg_ valp); } continue; case OPC_BP: /* step back to the breakpoint location itself */ --p; /* let the debugger take control */ G_debugger->step(vmg_ &p, entry_ptr_native_, TRUE, 0); /* if we're halting, get out of here */ if (halt_vm_) err_throw(VMERR_DBG_HALT); /* * go back and execute the current instruction - bypass * single-step tracing into the debugger in this case, * since the debugger expects when it returns that one * instruction will always be traced before the debugger is * re-entered */ goto exec_instruction; #else /* VM_DEBUGGER */ case OPC_GETDBARGC: case OPC_GETDBLCL: case OPC_GETDBARG: case OPC_SETDBLCL: case OPC_SETDBARG: case OPC_GETPROPDATA: case OPC_PTRGETPROPDATA: err_throw(VMERR_NO_DEBUGGER); continue; case OPC_BP: /* if there's no debugger, it's an error */ err_throw(VMERR_BREAKPOINT); continue; #endif /* VM_DEBUGGER */ case OPC_CALLEXT: //$$$ err_throw(VMERR_CALLEXT_NOT_IMPL); continue; #ifdef OS_FILL_OUT_CASE_TABLES /* * Since we this switch is the innermost inner loop of the VM, * we go to some extra lengths to optimize it where possible. * See tads2/osifc.h for information on how to use * OS_FILL_OUT_CASE_TABLES and OS_IMPOSSIBLE_DEFAULT_CASE. * * Our controlling expression is an unsigned character value, * so we know the range of possible values will be limited to * 0-255. Therefore, we simply need to provide a "case" * alternative for every invalid opcode. To further encourage * the compiler to favor speed here, we specifically put * different code in every one of these case alternatives, to * force the compiler to generate a separate jump location for * each one; some compilers will generate a two-level jump * table if many cases point to shared code, to reduce the size * of the table, but we don't want that here because this * switch is critical to VM performance so we want it as fast * as possible. */ case 0x00: val.val.intval = 0x00; case 0x11: val.val.intval = 0x11; case 0x12: val.val.intval = 0x12; case 0x13: val.val.intval = 0x13; case 0x14: val.val.intval = 0x14; case 0x15: val.val.intval = 0x15; case 0x16: val.val.intval = 0x16; case 0x17: val.val.intval = 0x17; case 0x18: val.val.intval = 0x18; case 0x19: val.val.intval = 0x19; case 0x1A: val.val.intval = 0x1A; case 0x1B: val.val.intval = 0x1B; case 0x1C: val.val.intval = 0x1C; case 0x1D: val.val.intval = 0x1D; case 0x1E: val.val.intval = 0x1E; case 0x1F: val.val.intval = 0x1F; case 0x31: val.val.intval = 0x31; case 0x32: val.val.intval = 0x32; case 0x33: val.val.intval = 0x33; case 0x34: val.val.intval = 0x34; case 0x35: val.val.intval = 0x35; case 0x36: val.val.intval = 0x36; case 0x37: val.val.intval = 0x37; case 0x38: val.val.intval = 0x38; case 0x39: val.val.intval = 0x39; case 0x3A: val.val.intval = 0x3A; case 0x3B: val.val.intval = 0x3B; case 0x3C: val.val.intval = 0x3C; case 0x3D: val.val.intval = 0x3D; case 0x3E: val.val.intval = 0x3E; case 0x3F: val.val.intval = 0x3F; case 0x46: val.val.intval = 0x46; case 0x47: val.val.intval = 0x47; case 0x48: val.val.intval = 0x48; case 0x49: val.val.intval = 0x49; case 0x4A: val.val.intval = 0x4A; case 0x4B: val.val.intval = 0x4B; case 0x4C: val.val.intval = 0x4C; case 0x4D: val.val.intval = 0x4D; case 0x4E: val.val.intval = 0x4E; case 0x4F: val.val.intval = 0x4F; case 0x53: val.val.intval = 0x53; case 0x55: val.val.intval = 0x55; case 0x5A: val.val.intval = 0x5A; case 0x5B: val.val.intval = 0x5B; case 0x5C: val.val.intval = 0x5C; case 0x5D: val.val.intval = 0x5D; case 0x5E: val.val.intval = 0x5E; case 0x5F: val.val.intval = 0x5F; case 0x6E: val.val.intval = 0x6E; case 0x6F: val.val.intval = 0x6F; case 0x70: val.val.intval = 0x70; case 0x71: val.val.intval = 0x71; case 0x79: val.val.intval = 0x7D; // case 0xA2: val.val.intval = 0xA2; // case 0xA3: val.val.intval = 0xA3; // case 0xA4: val.val.intval = 0xA4; // case 0xA5: val.val.intval = 0xA5; case 0xA7: val.val.intval = 0xA7; case 0xA8: val.val.intval = 0xA8; case 0xA9: val.val.intval = 0xA9; case 0xBD: val.val.intval = 0xBD; case 0xBE: val.val.intval = 0xBE; case 0xBF: val.val.intval = 0xBF; case 0xC4: val.val.intval = 0xC4; case 0xC5: val.val.intval = 0xC5; case 0xC6: val.val.intval = 0xC6; case 0xC7: val.val.intval = 0xC7; case 0xC8: val.val.intval = 0xC8; case 0xC9: val.val.intval = 0xC9; case 0xCA: val.val.intval = 0xCA; case 0xCB: val.val.intval = 0xCB; case 0xCC: val.val.intval = 0xCC; case 0xCD: val.val.intval = 0xCD; case 0xCE: val.val.intval = 0xCE; case 0xCF: val.val.intval = 0xCF; case 0xDC: val.val.intval = 0xDC; case 0xDD: val.val.intval = 0xDD; case 0xDE: val.val.intval = 0xDE; case 0xDF: val.val.intval = 0xDF; case 0xF0: val.val.intval = 0xF0; case 0xF3: val.val.intval = 0xF3; case 0xF4: val.val.intval = 0xF4; case 0xF5: val.val.intval = 0xF5; case 0xF6: val.val.intval = 0xF6; case 0xF7: val.val.intval = 0xF7; case 0xF8: val.val.intval = 0xF8; case 0xF9: val.val.intval = 0xF9; case 0xFA: val.val.intval = 0xFA; case 0xFB: val.val.intval = 0xFB; case 0xFC: val.val.intval = 0xFC; case 0xFD: val.val.intval = 0xFD; case 0xFE: val.val.intval = 0xFE; case 0xFF: val.val.intval = 0xFF; err_throw(VMERR_INVALID_OPCODE); OS_IMPOSSIBLE_DEFAULT_CASE #else /* OS_FILL_OUT_CASE_TABLES */ case 0: /* * Explicitly call out this invalid instruction case so * that we can avoid extra work in computing the switch. * Some compilers will be smart enough to observe that we * populate the full range of possible values (0-255) for * the datatype of the switch control expression, and thus * will build jump tables that can be jumped through * without range-checking the value. (No range checking * is necessary, because a uchar simply cannot hold any * values outside of the 0-255 range.) This doesn't * guarantee that the compiler will be smart, but it does * help with some compilers and shouldn't hurt performance * with those that don't make any use of the situation. */ err_throw(VMERR_INVALID_OPCODE); case 0xFF: /* * explicitly call out this invalid instruction for the * same reasons we call out case 0 above */ err_throw(VMERR_INVALID_OPCODE); default: /* unrecognized opcode */ err_throw(VMERR_INVALID_OPCODE); continue; #endif /* OS_FILL_OUT_CASE_TABLES */ } } /* * We jump to this label when it's time to terminate execution * and return to the host environment which called us. */ exit_loop: /* note that we're ready to return */ done = TRUE; } err_catch(err) { int i; volatile int released_reserve = FALSE; /* if the debugger has halted the VM, re-throw the error */ VM_IF_DEBUGGER(if (halt_vm_) err_rethrow();) err_try { /* * Return to the start of the most recent instruction - we've * already at least partially decoded the instruction, so we * won't be pointing to its first byte. Note that last_pc is * a non-register variable (because we take its address to * store in pc_ptr_), so it will correctly indicate the * current instruction even though we've jumped here via * longjmp. */ p = last_pc; /* * Create a new exception object to describe the error. The * arguments to the constructor are the error number and the * error parameters. * * If the error code is "unhandled exception," it means that * an exception occurred in a recursive interpreter * invocation, and the exception wasn't handled within the * code called recursively; in this case, we can simply * re-throw the original error, and perhaps handle it in the * context of the current code. */ if (err->get_error_code() == VMERR_UNHANDLED_EXC) { /* get the original exception object from the error stack */ obj = (vm_obj_id_t)err->get_param_ulong(0); } else { /* step into the debugger, if it's present */ VM_IF_DEBUGGER( { const uchar *dbgp; /* * If we're in the process of halting the VM, don't * bother stepping into the debugger. We'll check the * same thing in a moment, after we get back from * stepping into the debugger, but this check isn't * redundant: we could already be halting even before * we enter the debugger here, because we could be * unwinding the native (C++) error stack on our way * out from such a halt. */ if (halt_vm_) { done = TRUE; goto skip_throw; } /* make a copy of the PC for the debugger's use */ dbgp = p; /* step into the debugger */ G_debugger->step(vmg_ &dbgp, entry_ptr_native_, FALSE, err->get_error_code()); /* * If the VM was halted while in the debugger, throw * the DEBUG HALT error. (We formerly just exited the * byte-code loop, but that has the effect of returning * to a native method or function caller, and allowing * the native caller to continue to run. We don't want * that to happen because we can't process the normal * unwinding on the return - that would require VM ops, * and we've halted the VM. So, to explicitly unwind * any native callers, we have to throw an exception.) */ if (halt_vm_) err_throw(VMERR_DBG_HALT); /* * if they moved the execution pointer, resume * execution at the new point, discarding the * exception */ if (dbgp != p) { /* resume execution at the new location */ p = dbgp; /* discard the exception and resume execution */ goto skip_throw; } } ); /* * If this is a stack overflow exception, there's probably * not enough stack left to create the exception object. * Fortunately, we have an emergency stack reserve just for * such conditions, so release it now, hopefully giving us * enough room to work with to construct the exception. */ if (err->get_error_code() == VMERR_STACK_OVERFLOW) released_reserve = release_reserve(); /* push the error parameters (in reverse order) */ for (i = err->get_param_count() ; i > 0 ; ) { /* go to the next parameter */ --i; /* see what we have and push an appropriate value */ switch(err->get_param_type(i-1)) { case ERR_TYPE_INT: /* push the integer value */ push_int(vmg_ err->get_param_int(i)); break; case ERR_TYPE_ULONG: /* push the value */ push_int(vmg_ (int32_t)err->get_param_ulong(i)); break; case ERR_TYPE_TEXTCHAR: /* push a new string with the text */ push_obj(vmg_ CVmObjString::create(vmg_ FALSE, err->get_param_text(i), get_strlen(err->get_param_text(i)))); break; case ERR_TYPE_CHAR: /* push a new string with the text */ push_obj(vmg_ CVmObjString::create(vmg_ FALSE, err->get_param_char(i), strlen(err->get_param_char(i)))); break; default: /* unrecognized type - push nil for now */ push_nil(vmg0_); break; } } /* * if there's a RuntimeError base class defined, create an * instance; otherwise, create a simple instance of the * basic object type to throw as a placeholder, since the * program hasn't made any provision to catch run-time * errors */ if (G_predef->rterr != VM_INVALID_OBJ) { /* push the error number */ push_int(vmg_ err->get_error_code()); /* * Create the new RuntimeException instance. Run the * constructor in a recursive invocation of the * interpreter (by passing a null PC pointer). */ vm_objp(vmg_ G_predef->rterr) ->create_instance(vmg_ G_predef->rterr, 0, err->get_param_count() + 1); /* get the object from R0 */ if (r0_.typ != VM_OBJ) err_throw(VMERR_EXCEPTION_OBJ_REQD); obj = r0_.val.obj; } else { /* * There's no RuntimeError object defined by the image * file, so create a basic object to throw. This * won't convey any information to the program except * that it's not one of the errors they're expecting; * this is fine, since they have made no provisions to * catch VM errors, as demonstrated by their lack of a * RuntimeError definition. */ obj = CVmObjTads::create(vmg_ FALSE, 0, 1); } /* * if possible, set the exceptionMessage property in the * new exception object to the default error message for * the run-time error we're processing */ if (G_predef->rterrmsg_prop != VM_INVALID_PROP) { const char *msg; char buf[256]; vm_obj_id_t str_obj; /* format the message text */ msg = err_get_msg(vm_messages, vm_message_count, err->get_error_code(), FALSE); err_format_msg(buf, sizeof(buf), msg, err); /* * momentarily push the new exception object, so we * don't lose track of it if we run garbage collection * here */ push_obj(vmg_ obj); /* create a string object with the message text */ str_obj = CVmObjString::create(vmg_ FALSE, buf, strlen(buf)); /* * before we can build a stack trace, let the debugger * synchronize its current position information */ VM_IF_DEBUGGER( G_debugger->sync_exec_pos(vmg_ p, entry_ptr_native_)); /* set the property in the new object */ val.set_obj(str_obj); vm_objp(vmg_ obj) ->set_prop(vmg_ G_undo, obj, G_predef->rterrmsg_prop, &val); /* we don't need gc protection any more */ discard(); } } /* * If we released the stack reserve, take it back. We've * finished creating the exception object, so we don't need the * emergency stack space any more. We want to put it back now * that we're done with it so that it'll be there for us if we * should run into another stack overflow in the future. */ if (released_reserve) recover_reserve(); /* throw the exception */ if ((p = do_throw(vmg_ p, obj)) == 0) { /* remember the unhandled exception for a moment */ unhandled_exc = obj; } /* come here to skip throwing the exception */ VM_IF_DEBUGGER(skip_throw: ); } err_catch_disc { /* * we got another exception trying to handle the first * exception - just throw the error again, but at least clean * up statics on the way out */ pc_ptr_ = old_pc_ptr; /* if we released the stack reserve, take it back */ if (released_reserve) recover_reserve(); /* re-throw the error */ err_rethrow(); } err_end; } err_end; /* * If an unhandled exception occurred, re-throw it. This will wrap our * exception object in a C++ object and throw it through our C++ * err_try/err_catch exception mechanism, so that the exception is * thrown out of the recursive native-code invoker. */ if (unhandled_exc != VM_INVALID_OBJ) { /* restore the enclosing PC pointer */ pc_ptr_ = old_pc_ptr; /* re-throw the unhandled exception */ err_throw_a(VMERR_UNHANDLED_EXC, 1, ERR_TYPE_ULONG, (unsigned long)unhandled_exc); } /* if we're not done, go back and resume execution */ if (!done) goto resume_execution; /* restore the enclosing PC pointer */ pc_ptr_ = old_pc_ptr; } /* ------------------------------------------------------------------------ */ /* * Validate the built-in function pointer at top of stack */ void CVmRun::validate_bifptr(VMG0_) { /* get the top of stack */ vm_val_t *bifp = G_stk->get(0); /* validate the entry in the bif table */ if (!G_bif_table->validate_entry( bifp->val.bifptr.set_idx, bifp->val.bifptr.func_idx)) { /* invalid entry - throw an error */ char buf[20]; sprintf(buf, "#%d", bifp->val.bifptr.set_idx); err_throw_a(VMERR_UNAVAIL_INTRINSIC, 2, ERR_TYPE_INT, bifp->val.bifptr.func_idx, ERR_TYPE_CHAR, buf); } } /* ------------------------------------------------------------------------ */ /* * Create a method context object suitable for use with the STORECTX and * LOADCTX instructions. */ void CVmRun::create_loadctx_obj(VMG_ vm_val_t *result, vm_obj_id_t self, vm_obj_id_t defobj, vm_obj_id_t targobj, vm_prop_id_t targprop) { char buf[VMB_LEN + 4*VMB_DATAHOLDER]; /* initialize a list template for a four-element list */ vmb_put_len(buf, 4); /* * put the list elements: 'self', targetprop, original target object, * and defining object */ vmb_put_dh_obj(buf + VMB_LEN, self); vmb_put_dh_prop(buf + VMB_LEN + VMB_DATAHOLDER, targprop); vmb_put_dh_obj(buf + VMB_LEN + 2*VMB_DATAHOLDER, targobj); vmb_put_dh_obj(buf + VMB_LEN + 3*VMB_DATAHOLDER, defobj); /* create and return a new list copied from our prepared buffer */ result->set_obj(CVmObjList::create(vmg_ FALSE, buf)); } /* ------------------------------------------------------------------------ */ /* * Get a local variable, parameter, or context local from a stack frame. */ void CVmRun::get_local_from_frame(VMG_ vm_val_t *val, vm_val_t *fp, const CVmDbgFrameSymPtr *symp) { get_local_from_frame(vmg_ val, fp, symp->get_var_num(), symp->is_param(), symp->is_ctx_local(), symp->get_ctx_arr_idx()); } void CVmRun::get_local_from_frame(VMG_ vm_val_t *val, vm_val_t *fp, int varnum, int is_param, int is_ctx_local, int ctx_arr_idx) { /* check which kind of variable we have */ if (is_param) { /* parameter - get it from the frame */ *val = *get_param_from_frame(vmg_ fp, varnum); } else if (is_ctx_local) { /* context local - get the context local */ vm_val_t *cl = get_local_from_frame(vmg_ fp, varnum); /* index it to get the local */ vm_val_t idxval; idxval.set_int(ctx_arr_idx); apply_index(vmg_ val, cl, &idxval); } else { /* regular local - get it from the frame */ *val = *get_local_from_frame(vmg_ fp, varnum); } } /* ------------------------------------------------------------------------ */ /* * Set a local variable, parameter, or context local in a stack frame. */ void CVmRun::set_local_in_frame(VMG_ const vm_val_t *val, vm_val_t *fp, const CVmDbgFrameSymPtr *symp) { set_local_in_frame(vmg_ val, fp, symp->get_var_num(), symp->is_param(), symp->is_ctx_local(), symp->get_ctx_arr_idx()); } void CVmRun::set_local_in_frame(VMG_ const vm_val_t *val, vm_val_t *fp, int varnum, int is_param, int is_ctx_local, int ctx_arr_idx) { /* check which kind of variable we have */ if (is_param) { /* parameter - get it from the frame */ *get_param_from_frame(vmg_ fp, varnum) = *val; } else if (is_ctx_local) { /* context local - get the context local */ vm_val_t *cl = get_local_from_frame(vmg_ fp, varnum); /* index it to set the local */ vm_val_t idxval; idxval.set_int(ctx_arr_idx); set_index(vmg_ cl, &idxval, val); } else { /* regular local - set it in the frame */ *get_local_from_frame(vmg_ fp, varnum) = *val; } } /* ------------------------------------------------------------------------ */ /* * Get a named argument table from a stack frame. Returns a pointer to the * table if we find one, null if not. Fills in arg0 with the first named * argument in the stack frame; the nth named argument is at (arg0+n). */ const uchar *CVmRun::get_named_args_from_frame( VMG_ vm_val_t *fp, vm_val_t **arg0) { /* get the return address */ pool_ofs_t ofs = G_interpreter->get_return_ofs_from_frame(vmg_ fp); const uchar *ep = G_interpreter->get_enclosing_entry_ptr_from_frame(vmg_ fp); const vm_rcdesc *rc = G_interpreter->get_rcdesc_from_frame(vmg_ fp); /* check for a recursive native code caller */ if (ofs == 0 && rc != 0 && rc->has_return_addr()) { /* get the return address from the descriptor */ ofs = rc->get_return_addr() - ep; } /* if there's no return address, there's no table */ if (ep == 0 || ofs == 0) return 0; /* get the return instruction pointer */ const uchar *ip = ep + ofs; /* if it's a NamedArgPtr instruction, follows the pointer */ if (*ip == OPC_NAMEDARGPTR) ip += 2 + osrp2(ip+2); /* make sure we're at a NamedArgTab instruction */ if (*ip == OPC_NAMEDARGTAB) { /* * We have a named argument table. Fill in *arg0 with a pointer to * the first named argument value in the stack frame. * * If there's a native caller at this level, get the arguments to * the native caller instead of the arguments to the bytecode * callee. Native callers never send named arguments, so we can * ignore the callee's arguments and look only at the native * caller's arguments. */ int argc; if (rc != 0) { /* native caller - get the native code's arguments */ argc = rc->argc; *arg0 = rc->argp - argc; } else { /* bytecode caller - get the current level's arguments */ argc = G_interpreter->get_argc_from_frame(vmg_ fp); *arg0 = G_interpreter->get_param_from_frame(vmg_ fp, argc); } /* skip the NamedArgTab opcode and table length prefix */ ip += 1 + 2; /* * Adjust arg0 for the number of arguments. The compiler generates * the names in the same order as the pushes, so the first name is * the first value pushed. */ *arg0 -= (osrp2(ip) - 1); /* * return the table pointer - it starts after the NamedArgTab * instruction and table byte length prefix */ return ip; } /* there's no named argument table in this return frame */ return 0; } /* ------------------------------------------------------------------------ */ /* * Throw an exception of the given class, with the constructor arguments * on the stack. */ void CVmRun::throw_new_class(VMG_ vm_obj_id_t cls, uint argc, const char *fallback_msg) { /* if the class is defined, create an instance and throw it */ if (cls != VM_INVALID_OBJ) { /* create the object */ vm_objp(vmg_ cls)->create_instance(vmg_ cls, 0, argc); /* make sure we created an object */ if (r0_.typ == VM_OBJ) { vm_obj_id_t exc_obj; /* get the object from R0 */ exc_obj = r0_.val.obj; /* * throw an 'unhandled exception' with this object as the * parameter; the execution loop will catch it and dispatch it * properly */ err_throw_a(VMERR_UNHANDLED_EXC, 1, ERR_TYPE_ULONG, (unsigned long)exc_obj); } else err_throw(VMERR_EXCEPTION_OBJ_REQD); } /* * the imported exception class isn't defined, or we failed to create * it; throw a generic intrinsic class exception with the fallback * message string */ err_throw_a(VMERR_INTCLS_GENERAL_ERROR, 1, ERR_TYPE_CHAR, fallback_msg); } /* * Throw a new RuntimeError subclass. */ void CVmRun::throw_new_rtesub(VMG_ vm_obj_id_t cls, uint argc, int errnum) { /* if the subclass is defined, create an instance and throw it */ if (cls != VM_INVALID_OBJ) { /* push the error number as the first argument */ push_int(vmg_ errnum); /* throw the subclass error */ throw_new_class(vmg_ cls, argc + 1, 0); } /* the subclass isn't defined, so throw a generic RuntimeError */ err_throw(errnum); } /* ------------------------------------------------------------------------ */ /* * Throw an exception. Returns true if an exception handler was found, * which means that execution can proceed; returns false if no handler * was found, in which case the execution loop must throw the exception * to its caller. */ const uchar *CVmRun::do_throw(VMG_ const uchar *pc, vm_obj_id_t exception_obj) { /* * Search the stack for a handler for this exception class. Start * at the current stack frame; if we find a handler here, use it; * otherwise, unwind the stack to the enclosing frame and search for * a handler there; repeat until we exhaust the stack. */ for (;;) { CVmExcTablePtr tab; const uchar *func_start; uint ofs; /* get a pointer to the start of the current function */ func_start = entry_ptr_native_; /* set up a pointer to the current exception table */ if (func_start != 0 && tab.set(func_start)) { size_t cnt; size_t i; CVmExcEntryPtr entry; /* calculate our offset in the current function */ ofs = pc - func_start; /* set up a pointer to the first table entry */ tab.set_entry_ptr(vmg_ &entry, 0); /* loop through the entries */ for (i = 0, cnt = tab.get_count() ; i < cnt ; ++i, entry.inc(vmg0_)) { /* * Check to see if we're in the range for this entry. * If this entry covers the appropriate range, and the * exception we're handling is of the class handled by * this exception (or derives from that class), this * handler handles this exception. */ if (ofs >= entry.get_start_ofs() && ofs <= entry.get_end_ofs() && (entry.get_exception() == VM_INVALID_OBJ || exception_obj == entry.get_exception() || (vm_objp(vmg_ exception_obj) ->is_instance_of(vmg_ entry.get_exception())))) { /* * this is it - move the program counter to the * first byte of the handler's code */ pc = func_start + entry.get_handler_ofs(); /* push the exception so that the handler can get at it */ push_obj(vmg_ exception_obj); /* return the new program counter at which to resume */ return pc; } } } /* * We didn't find a handler in the current function - unwind the * stack one level, using an ordinary RETURN operation (we're not * really returning, though, so we don't need to provide a return * value). First, though, check to make sure there is an enclosing * frame at all - if there's not, we can simply return immediately. */ if (frame_ptr_ == 0) { /* there's no enclosing frame, so there's nowhere to go */ return 0; } /* try unwinding the stack a level */ if ((pc = do_return(vmg0_)) == 0) { /* * The enclosing frame is a recursive invocation, so we cannot * unwind any further at this point. Return null to indicate * that the exception was not handled and should be thrown out * of the current recursive VM invocation. */ return 0; } } } /* ------------------------------------------------------------------------ */ /* * Call a built-in function */ void CVmRun::call_bif(VMG_ uint set_index, uint func_index, uint argc) { /* * Call the function -- presume the compiler has ensured that the * function set index is valid for the load image, and that the * function index is valid for the function set; all of this can be * determined at compile time, since function sets are statically * defined. */ G_bif_table->call_func(vmg_ set_index, func_index, argc); } /* ------------------------------------------------------------------------ */ /* * Call a function pointer */ const uchar *CVmRun::call_func_ptr(VMG_ const vm_val_t *funcptr, uint argc, const vm_rcdesc *recurse_ctx, uint caller_ofs) { /* * Prepare the invocation frame. Even though we're calling this as a * function, if the value is an object, pass it as the 'self' value, * for compatibility with the old anonymous function invocation * protocol. */ vm_val_t *fp = G_stk->push(5); (fp++)->set_propid(VM_INVALID_PROP); (fp++)->set_nil(); (fp++)->set_nil(); if (funcptr->typ == VM_OBJ) (fp++)->set_obj(funcptr->val.obj); else (fp++)->set_nil_obj(); *(fp++) = *funcptr; /* call the function */ return call_func_ptr_fr(vmg_ funcptr, argc, recurse_ctx, caller_ofs); } /* * Call a function pointer, with the invocation frame already set up */ const uchar *CVmRun::call_func_ptr_fr(VMG_ const vm_val_t *funcptr, uint argc, const vm_rcdesc *recurse_ctx, uint caller_ofs) { vm_val_t invoker; CVmObject *io; /* check what we have */ switch (funcptr->typ) { case VM_OBJ: /* it's an object - check to see if it's invokable */ if ((io = vm_objp(vmg_ funcptr->val.obj))->get_invoker(vmg_ &invoker)) { /* get the invocation address, according to the invoker type */ const void *invoke_addr; switch (invoker.typ) { case VM_FUNCPTR: invoke_addr = G_code_pool->get_ptr(invoker.val.ofs); break; case VM_CODEPTR: invoke_addr = invoker.val.ptr; break; default: invoke_addr = 0; break; } /* if we got a valid address, invoke the code */ if (invoke_addr != 0) { /* call the function and return the new program counter */ return do_call( vmg_ caller_ofs, (const uchar *)invoke_addr, argc, recurse_ctx); } } /* it's not invokable - throw an error */ err_throw(VMERR_FUNCPTR_VAL_REQD); AFTER_ERR_THROW(return 0;) case VM_FUNCPTR: /* call the function */ return do_call(vmg_ caller_ofs, (const uchar *)G_code_pool->get_ptr(funcptr->val.ofs), argc, recurse_ctx); case VM_BIFPTR: /* * Built-in function. First, discard the invocation frame the * caller set up for a regular method/function call, as builtins * don't use those. Then, decode the SetIndex:FuncIndex value and * call the function. */ discard(5); call_bif(vmg_ funcptr->val.bifptr.set_idx, funcptr->val.bifptr.func_idx, argc); /* proceed from the caller's address */ return entry_ptr_native_ + caller_ofs; default: /* invalid type */ err_throw(VMERR_FUNCPTR_VAL_REQD); AFTER_ERR_THROW(return 0;) } } /* ------------------------------------------------------------------------ */ /* * Call a function, non-recursively. * * This is a separate form of do_call(), but simplified for cases where we * know in advance that we won't need to check for recursion and when we * know in advance that we're calling a function and thus have no 'self' * or other method context objects. These simplifications reduce the * amount of work we have to do, so that ordinary function calls run a * little faster than they would if we used the full do_call() routine. */ const uchar *CVmRun::do_call_func_nr(VMG_ uint caller_ofs, pool_ofs_t target_ofs, uint argc) { const uchar *target_ofs_ptr; CVmFuncPtr hdr_ptr; uint i; vm_val_t *fp; int lcl_cnt; /* store nil in R0 */ r0_.set_nil(); /* translate the target address */ target_ofs_ptr = (const uchar *)G_code_pool->get_ptr(target_ofs); /* set up a pointer to the new function header */ hdr_ptr.set(target_ofs_ptr); /* get the number of locals from the header */ lcl_cnt = hdr_ptr.get_local_cnt(); /* get the target's stack space needs and check for stack overflow */ if (!check_space(hdr_ptr.get_stack_depth() + 11)) err_throw(VMERR_STACK_OVERFLOW); /* allocate the stack frame */ fp = push(11 + lcl_cnt); /* there's no target property, target object, defining object, or self */ (fp++)->set_propid(VM_INVALID_PROP); (fp++)->set_nil(); (fp++)->set_nil(); (fp++)->set_nil_obj(); /* push the invokee */ (fp++)->set_fnptr(target_ofs); /* push the frame reference slot */ (fp++)->set_nil(); /* push the native code descriptor (none for a non-recursive call) */ (fp++)->set_codeptr(0); /* push the caller's code offset */ (fp++)->set_codeofs(caller_ofs); /* push the current entrypoint code offset */ (fp++)->set_codeptr(entry_ptr_native_); /* push the actual parameter count */ (fp++)->set_int((int32_t)argc); /* push the current frame pointer */ (fp++)->set_stack(frame_ptr_); /* verify the argument count */ if (!hdr_ptr.argc_ok(argc)) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* set up the new stack frame */ frame_ptr_ = fp; /* load EP with the new code offset */ entry_ptr_native_ = target_ofs_ptr; /* push nil for each local */ for (i = lcl_cnt ; i != 0 ; --i) (fp++)->set_nil(); /* create and activate the new function's profiler frame */ VM_IF_PROFILER(if (profiling_) prof_enter(vmg_ target_ofs_ptr)); /* return the new program counter */ return target_ofs_ptr + get_funchdr_size(); } /* ------------------------------------------------------------------------ */ /* * Recursive call descriptor */ /* set up a descriptor for a system caller */ void vm_rcdesc::init_ret(VMG_ const char *name) { this->name = name; bifptr.set_nil(); self.set_nil(); method_idx = 0; argc = 0; argp = 0; caller_addr = G_interpreter->get_last_pc(); } /* set up a descriptor for a built-in function */ void vm_rcdesc::init(VMG_ const char *name, const vm_bif_desc *funcset, int idx, vm_val_t *argp, int argc) { this->name = name; this->self.set_nil(); this->method_idx = 0; this->bifptr = funcset[idx].bifptr; this->argp = argp; this->argc = argc; this->caller_addr = G_interpreter->get_last_pc(); } /* initialize for an intrinsic class method */ void vm_rcdesc::init(VMG_ const char *name, vm_obj_id_t self, unsigned short method_idx, vm_val_t *argp, int argc) { this->name = name; this->bifptr.set_nil(); this->self.set_obj(self); this->method_idx = method_idx; this->argp = argp; this->argc = argc; this->caller_addr = G_interpreter->get_last_pc(); } /* initialize for an intrinsic class method */ void vm_rcdesc::init(VMG_ const char *name, const vm_val_t *self, unsigned short method_idx, vm_val_t *argp, int argc) { this->name = name; this->bifptr.set_nil(); this->self = *self; this->method_idx = method_idx; this->argp = argp; this->argc = argc; this->caller_addr = G_interpreter->get_last_pc(); } /* * Calculate the return address in the calling frame */ const uchar *vm_rcdesc::get_return_addr() const { /* if there's no caller address, there's no return address */ if (caller_addr == 0) return 0; /* * caller_addr is a pointer to the instruction that triggered the call * to the native code that in turn made the recursive call represented * by 'this'. The return address is the next instruction, so we just * need to calculate the size of the instruction at caller_addr and add * it to the caller address. Start with one byte for the opcode. */ const uchar *p = caller_addr; /* check for VARARGC, which can prefix many call instructions */ if (*p == OPC_VARARGC) ++p; /* advance past the actual call instruction */ p += CVmOpcodes::get_op_size(p); /* return the final address */ return p; } /* ------------------------------------------------------------------------ */ /* * Call a function or method */ const uchar *CVmRun::do_call(VMG_ uint caller_ofs, const uchar *target_ptr, uint argc, const vm_rcdesc *recurse_ctx) { CVmFuncPtr hdr_ptr; uint i; vm_val_t *fp; int lcl_cnt; /* store nil in R0 */ r0_.set_nil(); /* set up a pointer to the new function header */ hdr_ptr.set(target_ptr); /* get the number of locals from the header */ lcl_cnt = hdr_ptr.get_local_cnt(); /* * Get the space needs of the new function, and ensure we have enough * stack space available. Include the size of the frame that we store * (the original target object, the target property, the defining * object, the 'self' object, the caller's code offset, the caller's * entrypoint offset, the actual parameter count, and the enclosing * frame pointer) in our space needs. */ if (!check_space(hdr_ptr.get_stack_depth() + 11)) err_throw(VMERR_STACK_OVERFLOW); /* * Allocate the stack frame. Note that the caller has already pushed * targetprop, targetobj, definingobj, and self. */ fp = push(6 + lcl_cnt); /* push the frame reference slot */ (fp++)->set_nil(); /* push the native code descriptor */ (fp++)->set_codeptr(recurse_ctx); /* * Push the caller's code offset. Note that if the caller's offset is * zero, it indicates that the caller is not the byte-code interpreter * and that this is a recursive invocation; we represent recursive * frames using a zero caller offset, so we can just use the zero * value as given in this case. */ (fp++)->set_codeofs(caller_ofs); /* push the current entrypoint code offset */ (fp++)->set_codeptr(entry_ptr_native_); /* push the actual parameter count */ (fp++)->set_int((int32_t)argc); /* push the current frame pointer */ (fp++)->set_stack(frame_ptr_); /* * check the argument count - do this before establishing the new * frame and entry pointers, so that if we report a stack traceback in * the debugger, we'll report the error in the calling frame, which is * where it really belongs */ if (!hdr_ptr.argc_ok(argc)) { /* * if we're making a recursive call, throw an error indicating * what kind of recursive call we're making */ if (recurse_ctx != 0) { /* throw the named generic argument mismatch error */ err_throw_a(VMERR_WRONG_NUM_OF_ARGS_CALLING, 1, ERR_TYPE_CHAR, recurse_ctx->name); } else { /* throw the generic argument mismatch error */ err_throw(VMERR_WRONG_NUM_OF_ARGS); } } /* * set up the new frame so that the frame pointer points to the old * frame pointer stored in the stack */ frame_ptr_ = fp; /* load EP with the new code offset */ entry_ptr_native_ = target_ptr; /* push nil for each local */ for (i = lcl_cnt ; i != 0 ; --i) (fp++)->set_nil(); /* create and activate the new function's profiler frame */ VM_IF_PROFILER(if (profiling_) prof_enter(vmg_ target_ptr)); /* if desired, make a recursive call into the byte code interpreter */ if (caller_ofs != 0) { /* * return the new program counter at the first byte of code in the * new function, which immediately follows the header */ return target_ptr + get_funchdr_size(); } else { /* recursively call the interpreter loop */ run(vmg_ target_ptr + get_funchdr_size()); /* * this was a recursive call, so there's no program counter to * return - just return null */ return 0; } } /* * Return from the current function. Returns true if execution can * proceed, false if this returns us out of the outermost function, in * which case the execution loop must terminate and return control to * the host environment. */ const uchar *CVmRun::do_return(VMG0_) { /* invalidate the frame reference object, if present */ vm_val_t *fro = get_frameref_slot(vmg_ frame_ptr_); if (fro->typ == VM_OBJ && CVmObjFrameRef::is_frameref_obj(vmg_ fro->val.obj)) ((CVmObjFrameRef *)vm_objp(vmg_ fro->val.obj))->inval_frame(vmg0_); /* * The frame pointer always points to the location on the stack * where we pushed the enclosing frame pointer. Reset the stack * pointer to the current frame pointer, then pop the enclosing * frame pointer. */ set_sp(frame_ptr_); frame_ptr_ = (vm_val_t *)get(0)->val.ptr; /* get the enclosing argument count */ int argc = get(1)->val.intval; /* restore the enclosing entry pointer */ entry_ptr_native_ = (const uchar *)get(2)->val.ptr; /* restore the enclosing code offset */ pool_ofs_t caller_ofs = get(3)->val.ofs; /* * Discard the actual parameters, plus the 'self', defining object, * original target object, and target property values. While we're at * it, also discard the enclosing frame pointer, enclosing argument * count, enclosing entry pointer, and enclosing code offset, which * we've already restored. */ discard(argc + 11); /* leave the profiler stack level */ VM_IF_PROFILER(if (profiling_) prof_leave()); /* if we're debugging, notify the debugger of the return */ VM_IF_DEBUGGER(G_debugger->step_return(vmg0_)); /* * if this is a special return address code, execute the appropraite * local subroutine */ if (vmrun_is_special_return(caller_ofs)) { vm_val_t v; switch (caller_ofs) { case VMRUN_RET_RECURSIVE: /* * return from recursive VM invocation - just return 0 as the * finishing offset to tell the caller to exit the bytecode * execution loop */ return 0; case VMRUN_RET_OP: /* * returning from operator evaluation - push R0 as the operand * result, and return to the address currently at TOS */ caller_ofs = get(0)->val.intval; *get(0) = r0_; break; case VMRUN_RET_OP_ASILCL: /* * Returning from an operator evaluation, with assignment to a * local. The local variable number is at TOS; assign it, then * return to the address next on the stack. */ pop(&v); *get_local(vmg_ v.val.intval) = r0_; pop(&v); caller_ofs = v.val.intval; break; default: /* * invalid address; we're going to dump into random code if we * continue, so nip it in the bud and throw an invalid op now */ err_throw(VMERR_INVALID_OPCODE); } } /* * Empirically, about half the time, the caller will push the return * value onto the stack. It's a little faster to test for that case * here and handle it specially. */ if (*(entry_ptr_native_ + caller_ofs) == OPC_GETR0) { push(&r0_); caller_ofs += 1; } /* * return the new program counter - calculate the PC offset by adding * the offset within the method to the entry pointer */ return entry_ptr_native_ + caller_ofs; } /* ------------------------------------------------------------------------ */ /* * Execution context save/restore - these are for the debugger's use, so * that it can save the machine registers while executing a debugger * expression. */ #ifdef VM_DEBUGGER /* * save the execution context */ void CVmRun::save_context(VMG_ vmrun_save_ctx *ctx) { /* save our registers */ ctx->entry_ptr_native_ = entry_ptr_native_; ctx->frame_ptr_ = frame_ptr_; ctx->pc_ptr_ = pc_ptr_; /* save the stack depth */ ctx->old_stack_depth_ = get_depth(); } /* * restore the execution context */ void CVmRun::restore_context(VMG_ vmrun_save_ctx *ctx) { /* restore our registers */ entry_ptr_native_ = ctx->entry_ptr_native_; frame_ptr_ = ctx->frame_ptr_; pc_ptr_ = ctx->pc_ptr_; /* if there's anything extra left on the stack, discard it */ if (get_depth() > ctx->old_stack_depth_) discard(get_depth() - ctx->old_stack_depth_); } #endif /* VM_DEBUGGER */ /* ------------------------------------------------------------------------ */ /* * Append a stack trace to a string. This is only meaningful in a * debugger-equipped version. */ #if VM_DEBUGGER /* * callback context for stack trace appender */ struct append_stack_ctx { /* the string so far */ vm_obj_id_t str_obj; /* globals */ vm_globals *vmg; /* frame pointer where we pushed our string for gc protection */ vm_val_t *gc_fp; }; /* * stack trace callback */ static void append_stack_cb(void *ctx0, const char *str, int strl) { append_stack_ctx *ctx = (append_stack_ctx *)ctx0; size_t new_len; size_t old_len; const char *old_str; char *new_str; /* set up access to globals */ VMGLOB_PTR(ctx->vmg); /* get the original string text */ old_str = vm_objp(vmg_ ctx->str_obj)->get_as_string(vmg0_); old_len = vmb_get_len(old_str); old_str += VMB_LEN; /* * allocate a new string, big enough for the old string plus the new * text, plus a newline */ new_len = old_len + strl + 1; ctx->str_obj = CVmObjString::create(vmg_ FALSE, new_len); /* get the new string buffer */ new_str = ((CVmObjString *)vm_objp(vmg_ ctx->str_obj))->cons_get_buf(); /* build the new string */ memcpy(new_str, old_str, old_len); new_str[old_len] = '\n'; memcpy(new_str + old_len + 1, str, strl); /* * replace our gc-protective stack reference to the old string with * the new string - we're done with the old string now, so it's okay * if it gets collected, but we obviously want to keep the new one * around */ G_interpreter->get_from_frame(ctx->gc_fp, 0)->set_obj(ctx->str_obj); } /* * append a stack trace to the given string */ vm_obj_id_t CVmRun::append_stack_trace(VMG_ vm_obj_id_t str_obj) { append_stack_ctx ctx; /* push the string for protection from gc */ push_obj(vmg_ str_obj); /* call the debugger to set up the stack traceback */ ctx.str_obj = str_obj; ctx.vmg = VMGLOB_ADDR; ctx.gc_fp = get_sp(); G_debugger->build_stack_listing(vmg_ &append_stack_cb, &ctx, TRUE); /* discard the gc protection */ discard(); /* return the result string */ return ctx.str_obj; } #endif /* VM_DEBUGGER */ /* ------------------------------------------------------------------------ */ /* * Set a property of an object */ void CVmRun::set_prop(VMG_ vm_obj_id_t obj, vm_prop_id_t prop, const vm_val_t *new_val) { /* set the property */ vm_objp(vmg_ obj)->set_prop(vmg_ G_undo, obj, prop, new_val); } /* ------------------------------------------------------------------------ */ /* * Evaluate an operator overload. 'obj' is the operand of a unary op, or * the left operand of a binary op; 'prop' is the operator overload * property ID; 'argc' is the number of additional operands (0 for a unary * op, 1 for a binary op); 'err' is the VMERR_xxx error number for applying * the operator to a value that doesn't implement it. * * If the object doesn't provide the property, we'll throw the error, since * we're attempting to apply an operator to a value that doesn't implement * it. This is in contrast to ordinary property evaluations, which just * return nil for an unimplemented property. * * The 'prop' usually comes from G_predef, since it's an imported name from * the image file. If this is undefined (VM_INVALID_PROP), we certainly * can't have an overload for the operator, so we'll simply throw the * error. * * The 'obj' value doesn't have to be an object, since the program could * attempt to apply the operator to any type. If the value is of a type * that doesn't support property evaluations, we'll throw the same error. * * If 'asi_lcl' is 0 or greater, it's the local variable number to assign * the value to on return from the operator overload. If this is negative, * the return value is to be pushed onto the stack on return. */ const uchar *CVmRun::op_overload(VMG_ uint caller_ofs, int asi_lcl, const vm_val_t *obj, vm_prop_id_t prop, uint argc, int err) { vm_val_t val; vm_obj_id_t srcobj; vm_val_t new_self; /* * if the program defines any overloads for the operator, it must * define the export property; so if the export property isn't defined, * we can't have any overloading for the operator */ if (prop == VM_INVALID_PROP) err_throw(err); /* * if the value isn't an object, list, or string, there's no property, * so throw the specified error */ switch (obj->typ) { case VM_OBJ: case VM_SSTRING: case VM_LIST: /* these can have property values - look it up */ if (get_prop_no_eval(vmg_ &obj, prop, &argc, &srcobj, &val, &obj, &new_self)) { /* presume this is not actually a procedure call */ int is_call = FALSE; /* * Got it. Set up the appropriate special local subroutine for * processing the return value. * * - For a recursive call, the caller will provide the special * handling on return, so there's nothing special to do. * * - If there's no local to assign, we simply push the value * onto the stack on return, then do a local return to the * actual caller. Push our return offset as the local return * address. * * - If there's a local, we assign the value to the local on * return. Push the return offset as the local return address, * and push the local number after that. */ if (val.typ == VM_CODEOFS || val.typ == VM_DSTRING || val.typ == VM_OBJX) { /* * These will all result in subroutine calls. Set up the * return frame patch accordingly. */ is_call = TRUE; if (caller_ofs == 0) { /* * this is a recursive call - no return processing * patch is required */ } else if (asi_lcl < 0) { /* open one stack slot above the arguments */ insert(argc, 1); /* on return, push the value onto the stack */ get(argc)->set_int(caller_ofs); caller_ofs = VMRUN_RET_OP; } else { /* open two stack slots above the arguments */ insert(argc, 2); /* on return, store the value in locl #asi_lcl */ get(argc + 1)->set_int(caller_ofs); get(argc)->set_int(asi_lcl); caller_ofs = VMRUN_RET_OP_ASILCL; } } /* evaluate the property */ const uchar *ret = eval_prop_val( vmg_ TRUE, caller_ofs, &val, obj->val.obj, prop, obj, srcobj, argc, 0); /* if this is a direct evaluation, do the return patch now */ if (!is_call) { if (caller_ofs == 0) { /* recursive call - the caller will take care of it */ } else if (asi_lcl < 0) { /* normal operator eval - push the result onto the stack */ push(get_r0()); } else { /* local assignment on return */ *get_local(vmg_ asi_lcl) = *get_r0(); } } /* return the result */ return ret; } /* the property isn't defined - throw the caller's error */ err_throw(err); AFTER_ERR_THROW(return 0;) default: /* other types don't have properties - throw the caller's error */ err_throw(err); AFTER_ERR_THROW(return 0;) } } /* ------------------------------------------------------------------------ */ /* * Evaluate a property of an object */ const uchar *CVmRun::get_prop(VMG_ uint caller_ofs, const vm_val_t *target_obj, vm_prop_id_t target_prop, const vm_val_t *self, uint argc, const vm_rcdesc *rc) { vm_val_t val; vm_obj_id_t srcobj; int found; vm_val_t new_self; /* find the property without evaluating it */ found = get_prop_no_eval(vmg_ &target_obj, target_prop, &argc, &srcobj, &val, &self, &new_self); /* if we didn't find it, try propNotDefined */ if (!found && G_predef->prop_not_defined_prop != VM_INVALID_PROP) { /* look up propNotDefined */ found = get_prop_no_eval(vmg_ &target_obj, G_predef->prop_not_defined_prop, &argc, &srcobj, &val, &self, &new_self); /* if we found a method, set up to call it */ if (found && (val.typ == VM_CODEOFS || val.typ == VM_OBJX || val.typ == VM_BIFPTRX)) { /* * add the target property as the additional first argument to * propNotDefined (we push backwards, so this will conveniently * become the new first argument) */ push_prop(vmg_ target_prop); /* count the additional argument */ ++argc; /* the property we're actually calling now is propNotDefined */ target_prop = G_predef->prop_not_defined_prop; } } /* evaluate whatever we found or didn't find */ return eval_prop_val(vmg_ found, caller_ofs, &val, self->val.obj, target_prop, target_obj, srcobj, argc, rc); } /* * Simplified property evaluator */ void CVmRun::get_prop(VMG_ vm_val_t *result, const vm_val_t *obj, vm_prop_id_t prop, uint argc, const vm_rcdesc *rc) { /* use nil as the default in case we don't find a value */ result->set_nil(); /* if the property is invalid, we definitely can't evaluate it */ if (prop == VM_INVALID_PROP) return; /* evaluate the property into R0 */ get_prop(vmg_ 0, obj, prop, obj, argc, rc); /* copy the result to the caller's parameter */ *result = r0_; } /* * Look up a property without evaluating it. */ inline int CVmRun::get_prop_no_eval(VMG_ const vm_val_t **target_obj, vm_prop_id_t target_prop, uint *argc, vm_obj_id_t *srcobj, vm_val_t *val, const vm_val_t **self, vm_val_t *new_self) { int found; const char *target_ptr; /* * we can evaluate properties of regular objects, as well as string * and list constants - see what we have */ switch((*target_obj)->typ) { case VM_LIST: /* 'self' must be the same as the target for a constant list */ if ((*self)->typ != (*target_obj)->typ || (*self)->val.ofs != (*target_obj)->val.ofs) err_throw(VMERR_OBJ_VAL_REQD); /* translate the list offset to a physical pointer */ target_ptr = G_const_pool->get_ptr((*target_obj)->val.ofs); /* evaluate the constant list property */ found = CVmObjList::const_get_prop(vmg_ val, *target_obj, target_ptr, target_prop, srcobj, argc); /* * If the result is a method to run, we need an actual object for * 'self'. In this case, create a dynamic list object with the * same contents as the constant list value. */ if (found && (val->typ == VM_CODEOFS || val->typ == VM_OBJX || val->typ == VM_BIFPTRX)) { /* create the list */ new_self->set_obj(CVmObjListConst::create(vmg_ target_ptr)); /* use it as the new 'self' and the new effective target */ *self = new_self; *target_obj = new_self; } /* go evaluate the result as normal */ break; case VM_SSTRING: /* 'self' must be the same as the target for a constant string */ if ((*self)->typ != (*target_obj)->typ || (*self)->val.ofs != (*target_obj)->val.ofs) err_throw(VMERR_OBJ_VAL_REQD); /* translate the string offset to a physical pointer */ target_ptr = G_const_pool->get_ptr((*target_obj)->val.ofs); /* evaluate the constant string property */ found = CVmObjString::const_get_prop(vmg_ val, *target_obj, target_ptr, target_prop, srcobj, argc); /* * If the result is a method to run, we need an actual object for * 'self'. In this case, create a dynamic string object with the * same contents as the constant string value. */ if (found && (val->typ == VM_CODEOFS || val->typ == VM_OBJX || val->typ == VM_BIFPTRX)) { /* create the string */ new_self->set_obj(CVmObjStringConst::create(vmg_ target_ptr)); /* it's the new 'self' and the new effective target object */ *self = new_self; *target_obj = new_self; } /* go evaluate the result as normal */ break; case VM_OBJ: /* get the property value from the target object */ found = vm_objp(vmg_ (*target_obj)->val.obj) ->get_prop(vmg_ target_prop, val, (*target_obj)->val.obj, srcobj, argc); /* 'self' must be an object as well */ if ((*self)->typ != VM_OBJ) err_throw(VMERR_OBJ_VAL_REQD); break; case VM_NIL: /* nil pointer dereferenced */ err_throw(VMERR_NIL_DEREF); default: /* we can't evaluate properties of anything else */ err_throw(VMERR_OBJ_VAL_REQD); } /* return the 'found' indication */ return found; } /* ------------------------------------------------------------------------ */ /* * Given a value that has been retrieved from an object property, * evaluate the value. If the value contains code, we'll execute the * code; if it contains a self-printing string, we'll display the * string; otherwise, we'll just store the value in R0. * * 'found' indicates whether or not the property value is defined. * False indicates that the property value is not defined by the object; * true indicates that it is. */ const uchar *CVmRun::eval_prop_val(VMG_ int found, uint caller_ofs, const vm_val_t *val, vm_obj_id_t self, vm_prop_id_t target_prop, const vm_val_t *orig_target_obj, vm_obj_id_t defining_obj, uint argc, const vm_rcdesc *rc) { /* check whether or not the property is defined */ if (found) { /* take appropriate action based on the datatype of the result */ switch(val->typ) { case VM_CODEOFS: /* * It's a method - invoke the method. This will set us up * to start executing this new code, so there's nothing more * we need to do here. */ /* push targetprop, targetobj, definingobj, self, and invokee */ { vm_val_t *fp = push(5); (fp++)->set_propid(target_prop); (fp++)->set_obj(orig_target_obj->val.obj); (fp++)->set_obj(defining_obj); (fp++)->set_obj(self); (fp++)->set_fnptr(val->val.ofs); } /* call the function */ return do_call(vmg_ caller_ofs, (const uchar *)G_code_pool->get_ptr(val->val.ofs), argc, rc); case VM_DSTRING: /* no arguments are allowed */ if (argc != 0) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* * it's a self-printing string - invoke the default string * output function (this is effectively a do_call()) */ return disp_dstring(vmg_ val->val.ofs, caller_ofs, self); case VM_OBJX: /* * Execute-on-eval object. If the value is an anonymous * function or other invokable object, call it. If it's a * string, print it. */ if (vm_objp(vmg_ val->val.obj)->get_invoker(vmg_ 0)) { /* push targetprop, targetobj, definingobj, self, invokee */ vm_val_t *fp = push(5); (fp++)->set_propid(target_prop); (fp++)->set_obj(orig_target_obj->val.obj); (fp++)->set_obj(defining_obj); (fp++)->set_obj(self); (fp++)->set_obj(val->val.obj); /* convert to an ordinary anonymous function object */ vm_val_t funcptr; funcptr.set_obj(val->val.obj); /* invoke it */ return call_func_ptr_fr(vmg_ &funcptr, argc, 0, caller_ofs); } else if (CVmObjString::is_string_obj(vmg_ val->val.obj)) { /* print the string */ push_obj(vmg_ val->val.obj); return disp_string_val(vmg_ caller_ofs, self); } err_throw(VMERR_BAD_TYPE_CALL); case VM_BIFPTRX: /* Execute-on-eval built-in function. Call the function. */ call_bif(vmg_ val->val.bifptr.set_idx, val->val.bifptr.func_idx, argc); /* resume execution where we left off */ return entry_ptr_native_ + caller_ofs; default: /* for any other value, no arguments are allowed */ if (argc != 0) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* store the result in R0 */ r0_ = *val; /* resume execution where we left off */ return entry_ptr_native_ + caller_ofs; } } else { /* * the property or method is not defined - discard arguments and * set R0 to nil */ discard(argc); r0_.set_nil(); /* resume execution where we left off */ return entry_ptr_native_ + caller_ofs; } } /* ------------------------------------------------------------------------ */ /* * Inherit a property or method from the appropriate superclass of the * object that defines currently executing code. */ const uchar *CVmRun::inh_prop(VMG_ uint caller_ofs, vm_prop_id_t prop, uint argc) { vm_val_t orig_target_obj; vm_obj_id_t defining_obj; vm_val_t val; vm_obj_id_t srcobj; int found; vm_obj_id_t self; /* get the defining object from the stack frame */ defining_obj = get_defining_obj(vmg0_); /* get the original target object from the stack frame */ orig_target_obj.set_obj(get_orig_target_obj(vmg0_)); /* get the 'self' object */ self = get_self(vmg0_); /* get the inherited property value */ found = vm_objp(vmg_ self)->inh_prop(vmg_ prop, &val, self, orig_target_obj.val.obj, defining_obj, &srcobj, &argc); /* if we didn't find it, try inheriting propNotDefined */ if (!found && G_predef->prop_not_defined_prop != VM_INVALID_PROP) { /* * Look up propNotDefined using the same search conditions we used * to find the original inherited property. This lets us look up * the "inherited" propNotDefined. */ found = vm_objp(vmg_ self)->inh_prop(vmg_ G_predef->prop_not_defined_prop, &val, self, orig_target_obj.val.obj, defining_obj, &srcobj, &argc); /* * if we found it, and it's code, push the original property ID we * were attempting to inherit - this becomes the new first * parameter to the propNotDefined method */ if (found && (val.typ == VM_CODEOFS || val.typ == VM_OBJX || val.typ == VM_BIFPTRX)) { /* add the original property pointer argument */ push_prop(vmg_ prop); /* count the additional argument */ ++argc; /* the target property changes to propNotDefined */ prop = G_predef->prop_not_defined_prop; } } /* * evaluate and store the result - note that "self" remains the * current "self" object, since we're inheriting within the context * of the original method call */ return eval_prop_val(vmg_ found, caller_ofs, &val, self, prop, &orig_target_obj, srcobj, argc, 0); } /* ------------------------------------------------------------------------ */ /* * Display a dstring via the default string display mechanism */ const uchar *CVmRun::disp_dstring(VMG_ pool_ofs_t ofs, uint caller_ofs, vm_obj_id_t self) { /* push the string */ push()->set_sstring(ofs); /* invoke the default "say" function */ return disp_string_val(vmg_ caller_ofs, self); } /* * Display the value at top of stack via the default string display * mechanism */ const uchar *CVmRun::disp_string_val(VMG_ uint caller_ofs, vm_obj_id_t self) { /* * if there's a valid 'self' object, and there's a default display * method defined, and 'self' defines or inherits that method, * invoke the method */ if (say_method_ != VM_INVALID_PROP && self != VM_INVALID_OBJ) { vm_obj_id_t src_obj; vm_val_t val; /* * look up the property - if we find it, and it's a regular * method, invoke it */ if (vm_objp(vmg_ self)->get_prop(vmg_ say_method_, &val, self, &src_obj, 0) && (val.typ == VM_CODEOFS || val.typ == VM_OBJX || val.typ == VM_BIFPTRX)) { /* set up a 'self' value - this is the target object */ vm_val_t self_val; self_val.set_obj(self); /* there's a default display method - invoke it */ return eval_prop_val(vmg_ TRUE, caller_ofs, &val, self, say_method_, &self_val, src_obj, 1, 0); } } /* if the "say" function isn't initialized, it's an error */ if (say_func_ == 0 || say_func_->val.typ == VM_NIL) err_throw(VMERR_SAY_IS_NOT_DEFINED); /* call the "say" function with the argument at top of stack */ return call_func_ptr(vmg_ &say_func_->val, 1, 0, caller_ofs); } /* * Set the "say" function. */ void CVmRun::set_say_func(VMG_ const vm_val_t *val) { /* * if we haven't yet allocated a global to hold the 'say' function, * allocate one now */ if (say_func_ == 0) say_func_ = G_obj_table->create_global_var(); /* remember the new function */ say_func_->val = *val; } /* * Get the current "say" function */ void CVmRun::get_say_func(vm_val_t *val) const { /* * if we ever allocated a global to hold the 'say' function, return its * value; otherwise, there's no 'say' function, so the result is nil */ if (say_func_ != 0) *val = say_func_->val; else val->set_nil(); } /* ------------------------------------------------------------------------ */ /* * Push a string value */ void CVmRun::push_string(VMG_ const char *str, size_t len) { if (str != 0) push()->set_obj(CVmObjString::create(vmg_ FALSE, str, len)); else push()->set_nil(); } /* * Push a printf-formatted string */ void CVmRun::push_stringf(VMG_ const char *fmt, ...) { /* package the arguments as a va_list and invoke our va_list version */ va_list args; va_start(args, fmt); push_stringvf(vmg_ fmt, args); va_end(args); } /* * Push a vprintf-formatted string */ void CVmRun::push_stringvf(VMG_ const char *fmt, va_list args) { /* allocate and format the string */ char *str = t3vsprintf_alloc(fmt, args); /* push it */ push_string(vmg_ str); /* free the allocated string buffer */ t3free(str); } /* ------------------------------------------------------------------------ */ /* * Check a property for speculative evaluation */ void CVmRun::check_prop_spec_eval(VMG_ vm_obj_id_t obj, vm_prop_id_t prop) { vm_val_t val; vm_obj_id_t srcobj; /* get the property value */ if (vm_objp(vmg_ obj)->get_prop(vmg_ prop, &val, obj, &srcobj, 0)) { /* check the type of the value */ switch(val.typ) { case VM_CODEOFS: case VM_OBJX: case VM_BIFPTRX: case VM_DSTRING: case VM_NATIVE_CODE: /* * evaulating these types could result in side effects, so * this property cannot be evaulated during a speculative * evaluation */ err_throw(VMERR_BAD_SPEC_EVAL); break; default: /* evaluating other types causes no side effects, so proceed */ break; } } } /* ------------------------------------------------------------------------ */ /* * Set up a function header pointer for the current function */ void CVmRun::set_current_func_ptr(VMG_ CVmFuncPtr *func_ptr) { /* set up the pointer based on the current Entry Pointer register */ func_ptr->set(entry_ptr_native_); } /* * Set up a function header pointer for the return address of the given * stack frame */ void CVmRun::set_return_funcptr_from_frame(VMG_ CVmFuncPtr *func_ptr, vm_val_t *frame_ptr) { /* set up the function pointer for the entry pointer */ func_ptr->set(get_enclosing_entry_ptr_from_frame(vmg_ frame_ptr)); } /* ------------------------------------------------------------------------ */ /* * Get the frame pointer at a given stack level */ vm_val_t *CVmRun::get_fp_at_level(VMG_ int level) VM_REG_CONST { vm_val_t *fp; const vm_rcdesc *rc; /* walk up the stack to the desired level */ for (fp = frame_ptr_, rc = 0 ; fp != 0 && level != 0 ; --level) { /* if this is a recursive level, count it */ if (rc != 0 && rc->has_return_addr()) { /* move to the bytecode caller at this level */ rc = 0; /* count the level */ if (--level == 0) break; } /* move to the enclosing level */ rc = get_rcdesc_from_frame(vmg_ fp); fp = get_enclosing_frame_ptr(vmg_ fp); } /* * if we ran out of frames before we reached the desired level, or we * stopped at a native caller, the requested frame doesn't exist */ if (fp == 0 || (rc != 0 && rc->has_return_addr())) err_throw(VMERR_BAD_FRAME); /* return the frame */ return fp; } /* ------------------------------------------------------------------------ */ /* * Get the message from an exception object */ void CVmRun::get_exc_message(VMG_ const CVmException *exc, char *buf, size_t buflen, int add_unh_prefix) { CVmException tmpexc; const char *tmpmsg; const char *msg; /* set up our temporary exception object with no parameters by default */ tmpexc.param_count_ = 0; /* check for unhandled program exceptions */ if (exc->get_error_code() == VMERR_UNHANDLED_EXC) { size_t msg_len; /* * This is not a VM error, but is simply an exception that the * program itself threw but did not handle. We might be able to * find an informational message in the exception object itself. */ /* get the exception's message, if available */ msg = get_exc_message(vmg_ exc, &msg_len); if (msg != 0) { /* * we got a message from the exception object - use it */ /* set up our parameters for the formatting */ tmpexc.param_count_ = 1; tmpexc.set_param_str(0, msg, msg_len); /* * If they want an "unhandled exception" prefix, get the * message for the prefix; otherwise, just use the message * from the exception without further adornment. */ if (add_unh_prefix) { /* they want a prefix - get the prefix message */ tmpmsg = err_get_msg(vm_messages, vm_message_count, VMERR_UNHANDLED_EXC_PARAM, FALSE); } else { /* no prefix desired - just use the message as we got it */ tmpmsg = "%s"; } /* format the message */ err_format_msg(buf, buflen, tmpmsg, &tmpexc); } else { /* no message - use a generic exception message */ tmpmsg = err_get_msg(vm_messages, vm_message_count, VMERR_UNHANDLED_EXC, FALSE); err_format_msg(buf, buflen, tmpmsg, &tmpexc); } } else { /* * It's a VM exception, so we can determine the error's meaning * from the error code. Look up the message for the error code * in our error message list. */ msg = err_get_msg(vm_messages, vm_message_count, exc->get_error_code(), FALSE); /* if that failed, just show the error number */ if (msg == 0) { /* no message - just show the error code */ tmpmsg = err_get_msg(vm_messages, vm_message_count, VMERR_VM_EXC_CODE, FALSE); /* set up our parameters for formatting */ tmpexc.param_count_ = 1; tmpexc.set_param_int(0, exc->get_error_code()); /* format the message */ err_format_msg(buf, buflen, tmpmsg, &tmpexc); } else { char tmpbuf[256]; /* format the message from the exception parameters */ err_format_msg(tmpbuf, sizeof(tmpbuf), msg, exc); /* get the prefix message */ tmpmsg = err_get_msg(vm_messages, vm_message_count, VMERR_VM_EXC_PARAM, FALSE); /* set up our parameters for the formatting */ tmpexc.param_count_ = 1; tmpexc.set_param_str(0, tmpbuf); /* format the message */ err_format_msg(buf, buflen, tmpmsg, &tmpexc); } } } /* * Get the message from an "unhandled exception" error object */ const char *CVmRun::get_exc_message(VMG_ const CVmException *exc, size_t *msg_len) { vm_obj_id_t exc_obj; /* * if the error isn't "unhandled exception," there's not a stored * exception object; likewise, if there's no object parameter in the * exception, there's nothing to use to obtain the message */ if (exc->get_error_code() != VMERR_UNHANDLED_EXC || exc->get_param_count() < 1) return 0; /* get the exception object */ exc_obj = (vm_obj_id_t)exc->get_param_ulong(0); /* get the message from the object */ return get_exc_message(vmg_ exc_obj, msg_len); } /* * Get the message from an exception object */ const char *CVmRun::get_exc_message(VMG_ vm_obj_id_t exc_obj, size_t *msg_len) { vm_val_t val; vm_obj_id_t src_obj; const char *str; uint argc; /* if there's no object, there's no message */ if (exc_obj == VM_INVALID_OBJ) return 0; /* * get the exceptionMessage property value from the object; if * there's not a valid exceptionMessage property defined, or the * object doesn't have a value for the property, there's no message */ argc = 0; if (G_predef->rterrmsg_prop == VM_INVALID_PROP || (!vm_objp(vmg_ exc_obj)->get_prop(vmg_ G_predef->rterrmsg_prop, &val, exc_obj, &src_obj, &argc))) return 0; /* * We got the property. If it's a string or an object containing a * string, retrieve the string. */ switch(val.typ) { case VM_SSTRING: /* get the constant string */ str = G_const_pool->get_ptr(val.val.ofs); break; case VM_OBJ: /* get the string value of the object, if possible */ str = vm_objp(vmg_ val.val.obj)->get_as_string(vmg0_); break; default: /* it's not a string - we can't use it */ str = 0; break; } /* check to see if we got a string */ if (str != 0) { /* * The string is in the standard VM internal format, which means * it has a 2-byte length prefix followed by the bytes of the * string (with no null termination). Read the length prefix, * then skip past it so the caller doesn't have to. */ *msg_len = osrp2(str); str += VMB_LEN; } /* return the string pointer */ return str; } /* ------------------------------------------------------------------------ */ /* * Get the boundaries of the current statement, based on debugging * information. Returns true if valid debugging information was found for * the given code location, false if not. */ int CVmRun::get_stm_bounds(VMG_ const CVmFuncPtr *func_ptr, ulong method_ofs, CVmDbgLinePtr *caller_line_ptr, const uchar **stm_start, const uchar **stm_end) { CVmDbgTablePtr dbg_ptr; int lo; int hi; int cur; /* presume we won't find anything */ *stm_start = *stm_end = 0; /* * if the current method has no line records, we can't find the * boundaries */ if (!func_ptr->set_dbg_ptr(&dbg_ptr) || dbg_ptr.get_line_count(vmg0_) == 0) { /* indicate that we didn't find debug information */ return FALSE; } /* * We must perform a binary search of the line records for the line * that contains this program counter offset. */ lo = 0; hi = dbg_ptr.get_line_count(vmg0_) - 1; while (lo <= hi) { ulong start_ofs; ulong end_ofs; CVmDbgLinePtr line_ptr; /* split the difference and get the current entry */ cur = lo + (hi - lo)/2; dbg_ptr.set_line_ptr(vmg_ &line_ptr, cur); /* get the current statement's start relative to the method header */ start_ofs = line_ptr.get_start_ofs(); /* * Get the next statement's start offset, which gives us the end * of this statement. If this is the last statement in the table, * it runs to the end of the function; use the debug records table * offset as the upper bound in this case. */ if (cur == (int)dbg_ptr.get_line_count(vmg0_) - 1) { /* * it's the last record - use the debug table offset as an * upper bound, since we know the function can't have any * executable code past this point */ end_ofs = func_ptr->get_debug_ofs(); } else { CVmDbgLinePtr next_line_ptr; /* another record follows this one - use it */ next_line_ptr.copy_from(&line_ptr); next_line_ptr.inc(vmg0_); end_ofs = next_line_ptr.get_start_ofs(); } /* see where we are relative to this line record */ if (method_ofs >= end_ofs) { /* we need to go higher */ lo = (cur == lo ? cur + 1 : cur); } else if (method_ofs < start_ofs) { /* we need to go lower */ hi = (cur == hi ? hi - 1 : cur); } else { /* found it - set the bounds to this record's limits */ *stm_start = func_ptr->get() + start_ofs; *stm_end = func_ptr->get() + end_ofs; /* fill in the caller's line pointer if desired */ if (caller_line_ptr != 0) caller_line_ptr->copy_from(&line_ptr); /* indicate that we found the line boundaries successfully */ return TRUE; } } /* return failure */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Profiler functions */ #ifdef VM_PROFILER /* * Profiler master hash table entry */ class CVmHashEntryProfiler: public CVmHashEntryCI { public: CVmHashEntryProfiler(const char *str, size_t len, const vm_profiler_rec *rec) : CVmHashEntryCI(str, len, TRUE) { /* copy the profiler record's identifying portion */ rec_.func = rec->func; rec_.obj = rec->obj; rec_.prop = rec->prop; /* initialize the timers and counters to zero */ rec_.sum_direct.hi = rec_.sum_direct.lo = 0; rec_.sum_chi.hi = rec_.sum_chi.lo = 0; rec_.call_cnt = 0; } /* our profiler record */ vm_profiler_rec rec_; }; /* * Begin profiling */ void CVmRun::start_profiling() { /* clear any old profiler data from the master hash table */ prof_master_table_->delete_all_entries(); /* reset the profiler stack */ prof_stack_idx_ = 0; /* turn on profiling */ profiling_ = TRUE; } /* * End profiling */ void CVmRun::end_profiling() { /* turn off profiling */ profiling_ = FALSE; /* leave all active profiler stack levels */ while (prof_stack_idx_ != 0) prof_leave(); } /* context for our profiling callback */ struct vmrun_prof_enum { /* globals object */ vm_globals *globals; /* interpreter object */ CVmRun *terp; /* debugger object */ CVmDebug *dbg; /* client callback and its context */ void (*cb)(void *, const char *, unsigned long, unsigned long, unsigned long); void *cb_ctx; }; /* * Get the profiling data */ void CVmRun::get_profiling_data(VMG_ void (*cb)(void *, const char *, unsigned long, unsigned long, unsigned long), void *cb_ctx) { vmrun_prof_enum our_ctx; /* if there's no debugger, we can't get symbols, so we can't proceed */ if (G_debugger == 0) return; /* set up our callback context */ our_ctx.globals = VMGLOB_ADDR; our_ctx.terp = this; our_ctx.dbg = G_debugger; our_ctx.cb = cb; our_ctx.cb_ctx = cb_ctx; /* enumerate the master table entries through our callback */ prof_master_table_->enum_entries(&prof_enum_cb, &our_ctx); } /* * Callback for enumerating the profiling data */ void CVmRun::prof_enum_cb(void *ctx0, CVmHashEntry *entry0) { vmrun_prof_enum *ctx = (vmrun_prof_enum *)ctx0; VMGLOB_PTR(ctx->globals); CVmHashEntryProfiler *entry = (CVmHashEntryProfiler *)entry0; char namebuf[128]; const char *p; /* generate the name of the function or method */ if (entry->rec_.obj != VM_INVALID_OBJ) { char *dst; /* look up the object name */ p = ctx->dbg->objid_to_sym(entry->rec_.obj); /* get the original name, if this is a synthetic 'modify' object */ p = ctx->dbg->get_modifying_sym(p); /* * if we got an object name, use it; otherwise, synthesize a name * using the object number */ if (p != 0) strcpy(namebuf, p); else sprintf(namebuf, "obj#%lx", (long)entry->rec_.obj); /* add a period */ dst = namebuf + strlen(namebuf); *dst++ = '.'; /* look up the property name */ p = ctx->dbg->propid_to_sym(entry->rec_.prop); if (p != 0) strcpy(dst, p); else sprintf(dst, "prop#%x", (int)entry->rec_.prop); } else if (entry->rec_.func != 0) { /* look up the function at the code offset */ char buf[256]; p = ctx->dbg->funchdr_to_sym(vmg_ entry->rec_.func, buf); if (p != 0) strcpy(namebuf, p); else sprintf(namebuf, "func#%lx", (long)entry->rec_.func); } else { /* it must be system code */ strcpy(namebuf, "<System>"); } /* invoke the callback with the data */ (*ctx->cb)(ctx->cb_ctx, namebuf, os_prof_time_to_ms(&entry->rec_.sum_direct), os_prof_time_to_ms(&entry->rec_.sum_chi), entry->rec_.call_cnt); } /* * Profile entry into a new function or method */ void CVmRun::prof_enter(VMG_ const uchar *fptr) { vm_prof_time cur; /* pull the target property and defining object from the frame */ vm_obj_id_t obj = get_defining_obj(vmg0_); vm_prop_id_t prop = get_target_prop(vmg0_); /* get the current time */ os_prof_curtime(&cur); /* if we have a valid previous entry, suspend it */ if (prof_stack_idx_ > 0 && prof_stack_idx_ - 1 < prof_stack_max_) { vm_profiler_rec *p; vm_prof_time delta; /* get a pointer to the outgoing entry */ p = &prof_stack_[prof_stack_idx_ - 1]; /* * add the time since the last start to the cumulative time spent * in this function */ prof_calc_elapsed(&delta, &cur, &prof_start_); prof_add_elapsed(&p->sum_direct, &delta); } /* if we have room on the profiler stack, add a new level */ if (prof_stack_idx_ < prof_stack_max_) { vm_profiler_rec *p; /* get a pointer to the new entry */ p = &prof_stack_[prof_stack_idx_]; /* remember the identifying data for the method or function */ p->func = fptr; p->obj = obj; p->prop = prop; /* we have no cumulative time yet */ p->sum_direct.hi = p->sum_direct.lo = 0; p->sum_chi.hi = p->sum_chi.lo = 0; } /* count the level */ ++prof_stack_idx_; /* remember the start time in the new current function */ os_prof_curtime(&prof_start_); } /* * Profile returning from a function or method */ void CVmRun::prof_leave() { vm_prof_time delta; vm_prof_time cur; vm_prof_time chi; /* get the current time */ os_prof_curtime(&cur); /* move to the last level */ --prof_stack_idx_; /* presume we won't know the child time */ chi.hi = chi.lo = 0; /* if we're on a valid level, finish the call */ if (prof_stack_idx_ < prof_stack_max_) { vm_profiler_rec *p; CVmHashEntryProfiler *entry; /* get a pointer to the outgoing entry */ p = &prof_stack_[prof_stack_idx_]; /* * add the time since the last start to the cumulative time spent * in this function */ prof_calc_elapsed(&delta, &cur, &prof_start_); prof_add_elapsed(&p->sum_direct, &delta); /* * Find or create the master record for the terminating function or * method, and add the cumulative times from this call to the * master record's cumulative times. Also count the invocation in * the master record. */ entry = prof_find_master_rec(p); prof_add_elapsed(&entry->rec_.sum_direct, &p->sum_direct); prof_add_elapsed(&entry->rec_.sum_chi, &p->sum_chi); ++(entry->rec_.call_cnt); /* * Calculate the cumulative time in the outgoing function - this is * the total time directly in the function plus the cumulative time * in all of its children. We must add this to the caller's * cumulative child time, since this function and all of its * children are children of the caller and thus must count in the * caller's total child time. */ chi = p->sum_direct; prof_add_elapsed(&chi, &p->sum_chi); } /* if we're leaving to a valid level, re-activate it */ if (prof_stack_idx_ > 0 && prof_stack_idx_ < prof_stack_max_) { vm_profiler_rec *p; /* get a pointer to the resuming entry */ p = &prof_stack_[prof_stack_idx_ - 1]; /* * add the time spent in the child and its children to our * cumulative child time */ prof_add_elapsed(&p->sum_chi, &chi); } /* * remember the new start time for the function we're resuming - we * must reset this to the current time, since we measure deltas from * the last call or return on each call or return */ os_prof_curtime(&prof_start_); } /* * Calculate an elapsed 64-bit time value */ void CVmRun::prof_calc_elapsed(vm_prof_time *diff, const vm_prof_time *a, const vm_prof_time *b) { /* calculate the differences of the low and high parts */ diff->lo = a->lo - b->lo; diff->hi = a->hi - b->hi; /* * if the low part ended up higher than it started, then we * underflowed, and hence must borrow from the high part */ if (diff->lo > a->lo) --(diff->hi); } /* * Add one elapsed time value to another */ void CVmRun::prof_add_elapsed(vm_prof_time *sum, const vm_prof_time *val) { unsigned long orig_lo; /* remember the original low part */ orig_lo = sum->lo; /* add the low parts and high parts */ sum->lo += val->lo; sum->hi += val->hi; /* * if the low part of the sum is less than where it started, then it * overflowed, and we must hence carry to the high part */ if (sum->lo < orig_lo) ++(sum->hi); } /* * Find or create a hash table entry for a profiler record */ CVmHashEntryProfiler *CVmRun::prof_find_master_rec(const vm_profiler_rec *p) { const size_t id_siz = sizeof(p->func) + sizeof(p->obj) + sizeof(p->prop); char id[id_siz]; CVmHashEntryProfiler *entry; /* * Build the ID string, which we'll use as our hash key. We never have * to serialize this, so it doesn't matter that it's dependent on byte * order and word size. */ memcpy(id, &p->func, sizeof(p->func)); memcpy(id + sizeof(p->func), &p->obj, sizeof(p->obj)); memcpy(id + sizeof(p->func) + sizeof(p->obj), &p->prop, sizeof(p->prop)); /* try to find an existing entry */ entry = (CVmHashEntryProfiler *)prof_master_table_->find(id, id_siz); /* if we didn't find an entry, create one */ if (entry == 0) { /* create a new entry */ entry = new CVmHashEntryProfiler(id, id_siz, p); /* add it to the table */ prof_master_table_->add(entry); } /* return the entry */ return entry; } #endif /* VM_PROFILER */ /* ------------------------------------------------------------------------ */ /* * Footnote - for the referring code, search the code above for * [REGISTER_P_FOOTNOTE]. * * This footnote pertains to a 'register' declaration that causes gcc (and * probably some other compilers) to generate a warning message. The * 'register' declaration is useful on some compilers and will be retained. * Here's a note I sent to Nikos Chantziaras (who asked about the warning) * explaining why I'm choosing to leave the 'register' declaration in, and * why I think this 'register' declaration is actually correct and useful * despite the warning it generates on some compilers. * * The basic issue is that the code takes the address of the variable in * question in expressions passed as parameters to certain function calls. * These function calls all happen to be in-linable functions, and it * happens that in each function, the address operator is always canceled * out by a '*' dereference operator - in other words, we have '*&p', which * the compiler can turn into just plain 'p' when the calls are in-lined, * eliminating the need to actually take the address of 'p'. * * Nikos: *. >I'm no expert, but I think GCC barks at this because it isn't possible *. >at all to store the variable in a register if the code wants its *. >address, therefore the 'register' in the declaration does nothing. * * That's correct, but a compiler is always free to ignore 'register' * declarations *anyway*, even if enregistration is possible. Therefore a * warning that it's not possible to obey 'register' is unnecessary, * because it's explicit in the language definition that 'register' is not * binding. It simply is not possible for an ignored 'register' attribute * to cause unexpected behavior. Warnings really should only be generated * for situations where it is likely that the programmer expects different * behavior than the compiler will deliver; in the case of an ignored * 'register' attribute, the programmer is *required* to expect that the * attribute might be ignored, so a warning to this effect is superfluous. * * Now, I understand why they generate the warning - it's because the * compiler believes that the program code itself makes enregistration * impossible, not because the compiler has chosen for optimization * purposes to ignore the 'register' request. However, as we'll see * shortly, the program code doesn't truly make enregistration impossible; * it is merely impossible in some interpretations of the code. Therefore * we really are back to the compiler choosing to ignore the 'register' * request due to its own optimization decisions; the 'register' request is * made impossible far downstream of the actual decisions that the compiler * makes (which have to do with in-line vs out-of-line calls), but it * really is compiler decisions that make it impossible, not the inherent * structure of the code. * *. >Furthermore, I'm not sure I understand the relationship *. >between 'register' and inlining; why should "*(&p)" do something *. >else "in calls to inlines" than its obvious meaning? * * When a function is in-lined, the compiler is not required to generate * the same code it would generate for the most general case of the same * function call, as long as the meaning is the same. * * For example, suppose we have some code that contains a call to a * function like so: * * a = myFunc(a + 7, 3); * * In the general out-of-line case, the compiler must generate some * machine-code instructions like this: * *. push #3 *. mov [a], d0 *. add #7, d0 *. push d0 *. call #myFunc *. mov d0, [a] * * The compiler doesn't have access to the inner workings of myFunc, so it * must generate the appropriate code for the generic interface to an * external function. * * Now, suppose the function is defined like so: * * int myFunc(int a, int b) { return a - 6; } * * and further suppose that the compiler decides to in-line this function. * In-lining means the compiler will generate the code that implements the * function directly in the caller; there will be no call to an external * linkage point. This means the compiler can implement the linkage to the * function with a custom one-off interface for this particular invocation * - every in-line invocation can be customized to the exact context where * it appears. So, for example, if we call myFunc right now and registers * d1 and d2 happens to be available, we can put the parameters in d1 and * d2, and the generated function will refer to those registers for the * parameters rather than having to look in the stack. Later on, if we * generate a separate call to the same function, but registers d3 and d7 * are the ones available, we can use those instead. Each generated copy * of the function can fit its exact context. * * Furthermore, looking at this function and at the arguments passed, we * can see that the formal parameter 'b' has no effect on the function's * results, and the actual parameter '3' passed for 'b' has no side * effects. Therefore, the compiler is free to completely ignore this * parameter - there's no need to generate any code for it at all, since we * have sufficient knowledge to see that it has no effect on the meaning of * the code. * * Further still, we can globally optimize the entire function. So, we can * see that myFunc(a+7, 3) is going to turn into the expression (a+7-6). * We can fold constants to arrive at (a+1) as the result of the function. * We can therefore generate the entire code for the function's invocation * like so: * * inc [a] * * Okay, now let's look at the &p case. In the specific examples in * vmrun.cpp, we have a bunch of function invocations like this: * * register const char *p; *. int x = myfunc(&p); * * In the most general case, we have to generate code like this: * *. lea [p], d0 ; load effective address *. push d0 *. call #myfunc *. mov d0, [x] * * So, in the most general case of a call with external linkage, we need * 'p' to have a main memory address so that we can push it on the stack as * the parameter to this call. Registers don't have main memory addresses, * so 'p' can't go in a register. * * However, we know what myfunc() looks like: * *. char myfunc(const char **p) *. { *. char c = **p; *. *p += 1; *. return c; *. } * * If the compiler chooses to in-line this function, it can globally * optimize its linkage and implementation as we saw earlier. So, the * compiler can rewrite the code like so: * * register const char *p; *. int x = **(&p); *. *(&p) += 1; * * which can be further rewritten to: * *. register const char *p; *. int x = *p; *. p += 1; * * Now we can generate the machine code for the final optimized form: * *. mov [p], a0 ; get the *value* of p into index register 0 *. mov.byte [a0+0], d0 ; get the value index register 0 points to *. mov.byte d0, [x] ; store it in x *. inc [p] ; inc the value of p * * Nowhere do we need a main memory address for p. This means the compiler * can keep p in a register, say d5: * *. mov d5, a0 *. mov.byte [a0+0], d0 *. mov.byte d0, [x] *. inc d5 * * And this is indeed exactly what the code that comes out of vc++ looks * like (changed from my abstract machine to 32-bit x86, of course). * * So: if the compiler chooses to in-line the functions that are called * with '&p' as a parameter, and the compiler performs the available * optimizations on those calls once they're in-lined, then a memory * address for 'p' is never needed. Thus there is a valid interpretation * of the code where 'register p' can be obeyed. If the compiler doesn't * choose to in-line the functions or make those optimizations, then the * compiler will be unable to satisfy the 'register p' request and will be * forced to put 'p' in addressable main memory. But it really is entirely * up to the compiler whether to obey the 'register p' request; the * program's structure does not make the request impossible to satisfy. * Therefore there is no reason for the compiler to warn about this, any * more than there would be if the compiler chose not to obey the 'register * p' simply because it thought it could make more optimal use of the * available registers. That gcc warns is understandable, in that a * superficial reading of the code would not reveal the optimization * opportunity; but the warning is nonetheless unnecessary, and the * 'register' does provide useful optimization hinting to at least vc++, so * I think it's best to leave it in and ignore the warning. */ �����qtads-2.1.7/tads3/vmrun.h���������������������������������������������������������������������������0000664�0000000�0000000�00000155574�12650170723�0015217�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/vmrun.h,v 1.4 1999/07/11 00:46:59 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmrun.h - VM Execution Function Notes Modified 11/12/98 MJRoberts - Creation */ #ifndef VMRUN_H #define VMRUN_H #include "vmglob.h" #include "vmtype.h" #include "vmstack.h" #include "vmpool.h" #include "vmobj.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmprofty.h" #include "vmfunc.h" /* ------------------------------------------------------------------------ */ /* * for debugger use - interpreter context save structure */ struct vmrun_save_ctx { const uchar *entry_ptr_native_; vm_val_t *frame_ptr_; size_t old_stack_depth_; const uchar **pc_ptr_; }; /* ------------------------------------------------------------------------ */ /* * Recursive VM call descriptor. When making a recursive call into the VM, * a caller can set up one of these structures to describe the caller. * We'll store this in the stack in the recursive context slot. */ struct vm_rcdesc { /* no-op construction */ vm_rcdesc() { } /* construct for a system caller */ vm_rcdesc(const char *name) { init(name); } /* initialize for a system caller */ void init(const char *name) { this->name = name; bifptr.set_nil(); self.set_nil(); method_idx = 0; argc = 0; argp = 0; caller_addr = 0; } /* initialize for a system caller, including a return address */ void init_ret(VMG_ const char *name); /* construct for a built-in function */ vm_rcdesc(VMG_ const char *name, const struct vm_bif_desc *funcset, int idx, vm_val_t *argp, int argc) { init(vmg_ name, funcset, idx, argp, argc); } /* initialize for a built-in function */ void init(VMG_ const char *name, const struct vm_bif_desc *funcset, int idx, vm_val_t *argp, int argc); /* construct for an intrinsic class method */ vm_rcdesc(VMG_ const char *name, vm_obj_id_t self, unsigned short method_idx, vm_val_t *argp, int argc) { init(vmg_ name, self, method_idx, argp, argc); } /* initialize for an intrinsic class method */ void init(VMG_ const char *name, vm_obj_id_t self, unsigned short method_idx, vm_val_t *argp, int argc); /* construct for an intrinsic class method */ vm_rcdesc(VMG_ const char *name, vm_obj_id_t self, unsigned short method_idx, vm_val_t *argp, uint *argc) { init(vmg_ name, self, method_idx, argp, argc != 0 ? *argc : 0); } /* initialize for an intrinsic class method */ void init(VMG_ const char *name, const vm_val_t *self, unsigned short method_idx, vm_val_t *argp, int argc); /* construct for an intrinsic class method */ vm_rcdesc(VMG_ const char *name, const vm_val_t *self, unsigned short method_idx, vm_val_t *argp, int argc) { init(vmg_ name, self, method_idx, argp, argc); } /* construct for an intrinsic class method */ vm_rcdesc(VMG_ const char *name, const vm_val_t *self, unsigned short method_idx, vm_val_t *argp, uint *argc) { init(vmg_ name, self, method_idx, argp, argc != 0 ? *argc : 0); } /* is there a return address available in the calling frame? */ int has_return_addr() const { return caller_addr != 0; } /* * Compute the return address in the calling frame. This requires * figuring the size of the instruction at caller_addr (which is * relatively quick, but not quick enough that we want to precompute it * on every call). */ const uchar *get_return_addr() const; /* pointer to first argument in stack */ vm_val_t *argp; /* number of arguments */ int argc; /* descriptive name, for error messages */ const char *name; /* built-in function pointer, if the caller is a bif */ vm_val_t bifptr; /* 'self', if the caller is an intrinsic class */ vm_val_t self; /* for an intrinsic class caller, the method index of the caller */ unsigned short method_idx; /* * Byte-code address of the instruction in the calling frame that * triggered the call. The return address can be computed by adding * the length of that instruction to this address. */ const uchar *caller_addr; }; /* ------------------------------------------------------------------------ */ /* * Pseudo subroutine addresses. * * There are some situations where the bytecode engine needs to patch in * what's effectively a local subroutine to invoke on return from a * function or method call. (A local subroutine is one that doesn't * involve a method header change - it's just a matter of pushing a return * address and jumping, similar to the way LJSR and LRET work.) * * In a real machine, we'd just load the subroutine's instructions into * some reserved area of system memory, and use the address of the * subroutine code as the return address when making the call. We'd also * push the *real* return address before this, so that the subroutine would * be able to do the local return to the actual calling address upon * completion. * * Due to the way we manage memory, though, we can't do this. Return * addresses are always offsets from a method header, since the only * addresses we're allowed to resolve are method header addresses. (This * is largely because memory can be discontiguous outside of a single * method block.) * * So what we do instead is define a range of return addresses that are * impossible, and use those to signal these special internal subroutines. * A return address is always an offset from the start of a method header, * so, conveniently, any offset less than the size of a header is * inherently invalid as an actual return address. This gives us a range * of addresses that are safe to use to signal special meanings. The * original T3 method header is 10 bytes, so any return offset from 0 to 9 * is invalid and thus can have a special meaning. */ /* is the given offset a special return address? */ inline int vmrun_is_special_return(uint ofs) { return ofs < 10; } /* * Special return address: Recursive Call Return. This value for the * return offset indicates that this is a recursive call into the VM, so * the VM bytecode execution loop should simply return when this frame * exits. */ const uint VMRUN_RET_RECURSIVE = 0; /* * Special return address: Return from Operator Overload. This value for * the return offset indicates that this is a call to evaluate an * overloaded operator. On return from this frame, execute the following * local subroutine: * *. GETR0 ; push return value from R0 onto the stack *. SWAP ; arrange stack: [sp]=return offset, [sp+1]=return value *. LRET [sp+] ; pop stack -> X and return to offset X in current method * * The stack at entry is arranged as follows: [sp]=return offset */ const uint VMRUN_RET_OP = 1; /* * Special return address: Return from Operator Overload And Assign Local. * This value for the return address indicates that this is a call to * evaluate an overloaded operator, and on return, we're to assign the * result to a local variable. On return from this frame, execute the * following local subroutine: * *. SETLCLR0 [sp+] ; pop stack -> V and assign R0 -> local variable #V *. LRET [sp+] ; pop stack -> X and return to offset X in current method * * The stack is arranged at entry as follows: [sp]=local variable number to * assign, [sp+1]=return offset */ const uint VMRUN_RET_OP_ASILCL = 2; /* ------------------------------------------------------------------------ */ /* * Procedure activation frame. The activation frame is arranged as * follows (the stack index increases reading down the list): * *. argument N *. argument N-1 *. ... *. argument 2 *. argument 1 *. target property *. original target object *. defining object *. self *. offset in calling method of next instruction to execute *. caller's entry pointer (EP) register value *. actual parameter count *. caller's frame pointer <<<--- CURRENT FRAME POINTER *. local variable 1 *. local variable 2 *. local variable 3 * * So, local variable 1 is at (FP+1), local variable 2 is at (FP+2), and * so on; the argument count is at (FP-1); 'self' is at (FP-4); argument 1 * is at (FP-5), argument 2 is at (FP-6), and so on. */ /* offset from FP of first argument */ const int VMRUN_FPOFS_ARG1 = -11; /* offset from FP of target property */ const int VMRUN_FPOFS_PROP = -10; /* offset from FP of original target object */ const int VMRUN_FPOFS_ORIGTARG = -9; /* offset from FP of defining object (definer of current method) */ const int VMRUN_FPOFS_DEFOBJ = -8; /* offset from FP of 'self' */ const int VMRUN_FPOFS_SELF = -7; /* invokee (this is the FuncPtr, DynamicFunc, AnonFunc, etc being invoked) */ const int VMRUN_FPOFS_INVOKEE = -6; /* frame reference (for reflection access to the frame contents) */ const int VMRUN_FPOFS_FRAMEREF = -5; /* recursive VM invocation native caller context */ const int VMRUN_FPOFS_RCDESC = -4; /* offset from FP of return address */ const int VMRUN_FPOFS_RET = -3; /* offset from FP of enclosing entry pointer */ const int VMRUN_FPOFS_ENC_EP = -2; /* offset from FP of argument count */ const int VMRUN_FPOFS_ARGC = -1; /* offset from FP of enclosing frame pointer */ const int VMRUN_FPOFS_ENC_FP = 0; /* offset from FP of first local variable */ const int VMRUN_FPOFS_LCL1 = 1; /* ------------------------------------------------------------------------ */ /* * Define certain of the VM CPU registers in global variables, if * applicable. */ VM_IF_REGS_IN_GLOBALS(extern vm_val_t r0_;) VM_IF_REGS_IN_GLOBALS(extern const uchar *entry_ptr_native_;) VM_IF_REGS_IN_GLOBALS(extern vm_val_t *frame_ptr_;) VM_IF_REGS_IN_GLOBALS(extern const uchar **pc_ptr_;) /* ------------------------------------------------------------------------ */ /* * VM Execution Engine class. This class handles execution of byte * code. */ class CVmRun: public CVmStack { friend class CVmDebug; friend class vmrun_prop_eval; public: CVmRun(size_t max_depth, size_t reserve_depth); ~CVmRun(); /* initialize */ void init(); /* terminate */ void terminate(); /* * Get/set the method header size. This size is stored in the image * file; the image loader sets this at load time to the value * retrieved from the image file. All method headers in an image * file use the same size. */ void set_funchdr_size(size_t siz); size_t get_funchdr_size() const { return funchdr_size_; } /* * Call a function or method. 'target_ptr' is the byte pointer to the * code to invoke, and 'argc' is the number of arguments that the * caller has pushed onto the stack. * * Before calling this, the caller must push onto the stack targetprop, * targetobj, definingobj, and self, in that order. For a function, * targetprop is VM_INVALID_PROP and all of the others are nil. * * 'caller_ofs' is the method offset (the byte code offset from the * current entry pointer) in the caller. If 'caller_ofs' is non-zero, * we'll set up to begin execution in the target code and return the * new program counter. If 'caller_ofs' is zero, we'll invoke the VM * byte code interpreter recursively, so this function will return only * after the called code returns. When calling recursively, set * 'recurse_calling' to a descriptive string that can be used to show * the system code calling the recursive code in case of error. * * When calling a function, 'self' should be VM_INVALID_OBJ. * Otherwise, this value gives the object whose method is being * invoked. * * The return value is the new program counter. For recursive * invocations, this will simply return null. */ const uchar *do_call(VMG_ uint caller_ofs, const uchar *target_ptr, uint argc, const vm_rcdesc *recurse_ctx); /* call a function, non-recursively */ const uchar *do_call_func_nr(VMG_ uint caller_ofs, pool_ofs_t ofs, uint argc); /* * Call a function pointer value. If 'funcptr' contains a function * pointer, we'll simply call the function; if it contains an * anonymous function object, we'll call the anonymous function. */ const uchar *call_func_ptr(VMG_ const vm_val_t *funcptr, uint argc, const vm_rcdesc *recurse_ctx, uint caller_ofs); /* call a function pointer, with the invocation frame already set up */ const uchar *call_func_ptr_fr(VMG_ const vm_val_t *funcptr, uint argc, const vm_rcdesc *recurse_ctx, uint caller_ofs); /* * Get the descriptive message, if any, from an exception object. * The returned string will not be null-terminated, but the length * will be stored in *msg_len. The returned string might point to * constant pool data or data in an object, so it might not remain * valid after a constant pool translation or garbage collection * operation. If the exception has no message, we will return a * null pointer. */ static const char *get_exc_message(VMG_ const CVmException *exc, size_t *msg_len); static const char *get_exc_message(VMG_ vm_obj_id_t obj, size_t *msg_len); /* * Get the descriptive message from an exception. If the exception * has a program-generated exception object, we'll try to get the * message from that object; if it's a VM exception with no * underlying object, we'll retrieve the VM message. * * If add_unh_prefix is true, we'll add an "unhandled exception:" * prefix to the message if we retrieve the message from a * program-defined exception. Otherwise, if it's a program * exception, we won't add any prefix at all. */ static void get_exc_message(VMG_ const CVmException *exc, char *buf, size_t buflen, int add_unh_prefix); /* * Evaluate a property of an object. 'target_obj' is the object whose * property is to be evaluated, 'target_prop' is the ID of the * property to evaluate, and 'argc' is the number of arguments to the * method. * * 'caller_ofs' is the current method offset (the offset from the * current entry pointer to the current program counter) in the * caller. If this is zero, we'll make a recursive call to the * interpreter loop to execute any method code; thus, any method code * will have run to completion by the time we return in this case. * * 'self' is the object in whose context we're to perform the code * execution, if the property is a method. Note that 'self' may * differ from 'target_obj' in some cases, particularly when * inheriting. * * The return value is the new program counter from which execution * should resume. This will be null (and can be ignored) for * recursive invocations. */ const uchar *get_prop(VMG_ uint caller_ofs, const vm_val_t *target_obj, vm_prop_id_t target_prop, const vm_val_t *self, uint argc, const vm_rcdesc *rc); /* * Simplified get_prop, for cases where the caller just wants to * evaluate a property of a value (with recursive VM entry). */ void get_prop(VMG_ vm_val_t *result, const vm_val_t *obj, vm_prop_id_t prop, uint argc, const vm_rcdesc *rc); /* * Evaluate a property for operator overloading. */ const uchar *op_overload(VMG_ uint caller_ofs, int asi_lcl, const vm_val_t *obj, vm_prop_id_t prop, uint argc, int err); /* * Set a property of an object */ void set_prop(VMG_ vm_obj_id_t obj, vm_prop_id_t prop, const vm_val_t *new_val); /* get data register 0 (R0) */ VM_REG_ACCESS vm_val_t *get_r0() { return &r0_; } /* set the default "say" function */ void set_say_func(VMG_ const vm_val_t *val); /* get the current default "say" function */ void get_say_func(vm_val_t *val) const; /* set the default "say" method */ void set_say_method(vm_prop_id_t prop) { /* remember the property */ say_method_ = prop; } /* get the current "say" method */ vm_prop_id_t get_say_method() const { return say_method_; } /* pop an integer value; throws an error if the value is not an integer */ void pop_int(VMG_ vm_val_t *val) { pop(val); if (val->typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); } /* pop an object value */ void pop_obj(VMG_ vm_val_t *val) { pop(val); if (val->typ != VM_OBJ) err_throw(VMERR_OBJ_VAL_REQD); } /* pop a property pointer value */ void pop_prop(VMG_ vm_val_t *val) { pop(val); if (val->typ != VM_PROP) err_throw(VMERR_PROPPTR_VAL_REQD); } /* pop a function pointer value */ void pop_funcptr(VMG_ vm_val_t *val) { pop(val); if (val->typ != VM_FUNCPTR) err_throw(VMERR_FUNCPTR_VAL_REQD); } /* * Pop two values from the stack. The values are popped in reverse * order, so val2 has the value at the top of the stack. If the * left operand was pushed first, this results in placing the left * operand in val1 and the right operand in val2. */ void popval_2(VMG_ vm_val_t *val1, vm_val_t *val2) { popval(vmg_ val2); popval(vmg_ val1); } /* * Pop the left-hand operand of a two-operand operator, leaving the * right-hand operand on the stack. For binary operators, the * left-hand value is pushed first, then the right-hand value; this * means that the right value is at top-of-stack. So we basically have * to pull the left-hand value out of the stack, then move the right * value up one slot to close the gap. */ void pop_left_op(VMG_ vm_val_t *left) { *left = *get(1); *get(1) = *get(0); discard(); } /* * Pop two integers, throwing an error if either value is not an * integer. Pops the item at the top of the stack into val2, and * the next value into val1. */ void pop_int_2(VMG_ vm_val_t *val1, vm_val_t *val2) { popval(vmg_ val2); popval(vmg_ val1); if (val1->typ != VM_INT || val2->typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); } /* * get the active function's argument count - we read the value from * the first item below the frame pointer in the current frame */ VM_REG_ACCESS int get_cur_argc(VMG0_) VM_REG_CONST { return get_from_frame(frame_ptr_, VMRUN_FPOFS_ARGC)->val.intval; } /* * Get a parameter value; 0 is the first parameter, 1 is the second, * and so on. */ VM_REG_ACCESS vm_val_t *get_param(VMG_ int idx) VM_REG_CONST { return get_param_from_frame(vmg_ frame_ptr_, idx); } /* get a parameter from a given frame */ VM_REG_ACCESS vm_val_t *get_param_from_frame( VMG_ vm_val_t *fp, int idx) VM_REG_CONST { return get_from_frame(fp, VMRUN_FPOFS_ARG1 - idx); } /* get a parameter at the given stack level */ VM_REG_ACCESS vm_val_t *get_param_at_level( VMG_ int idx, int level) VM_REG_CONST { return get_param_from_frame(vmg_ get_fp_at_level(vmg_ level), idx); } /* get the named parameter table, if any, from a frame */ static const uchar *get_named_args_from_frame( VMG_ vm_val_t *fp, vm_val_t **arg0); /* * get a local variable's value; 0 is the first local variable, 1 is * the second, and so on */ VM_REG_ACCESS vm_val_t *get_local(VMG_ int idx) VM_REG_CONST { return get_local_from_frame(vmg_ frame_ptr_, idx); } /* get a local from a given frame */ VM_REG_ACCESS vm_val_t *get_local_from_frame( VMG_ vm_val_t *fp, int idx) VM_REG_CONST { return get_from_frame(fp, VMRUN_FPOFS_LCL1 + idx); } /* get a local at the given stack level */ VM_REG_ACCESS vm_val_t *get_local_at_level( VMG_ int idx, int level) VM_REG_CONST { return get_local_from_frame(vmg_ get_fp_at_level(vmg_ level), idx); } /* get a local, parameter, or context local at a given stack level */ VM_REG_ACCESS void get_local_from_frame( VMG_ vm_val_t *val, vm_val_t *fp, const class CVmDbgFrameSymPtr *symp); VM_REG_ACCESS void get_local_from_frame( VMG_ vm_val_t *val, vm_val_t *fp, int varnum, int is_param, int is_ctx_local, int ctx_arr_idx); /* set a local, parameter, or context local at a given stack level */ VM_REG_ACCESS void set_local_in_frame( VMG_ const vm_val_t *val, vm_val_t *fp, const class CVmDbgFrameSymPtr *symp); VM_REG_ACCESS void set_local_in_frame( VMG_ const vm_val_t *val, vm_val_t *fp, int varnum, int is_param, int is_ctx_local, int ctx_arr_idx); /* * Get the frame pointer at the given stack level. Level 0 is the * currently active frame, 1 is the first enclosing level, and so * on. Throws an error if the enclosing frame is invalid. */ VM_REG_ACCESS vm_val_t *get_fp_at_level(VMG_ int level) VM_REG_CONST; /* * Get the current frame depth. This is the stack depth of the * current frame pointer. This can be used to compare two frame * pointers to determine if one encloses the other - the pointer * with the smaller depth value encloses the larger one. */ size_t get_frame_depth(VMG0_) const { return ptr_to_index(frame_ptr_); } /* get the current frame pointer */ VM_REG_ACCESS vm_val_t *get_frame_ptr() VM_REG_CONST { return frame_ptr_; } /* given a frame pointer, get the enclosing frame pointer */ static vm_val_t *get_enclosing_frame_ptr(VMG_ vm_val_t *fp) { return (vm_val_t *)get_from_frame(fp, VMRUN_FPOFS_ENC_FP)->val.ptr; } /* get the number of arguments from a given frame */ VM_REG_ACCESS int get_argc_from_frame(VMG_ vm_val_t *fp) VM_REG_CONST { return get_from_frame(fp, VMRUN_FPOFS_ARGC)->val.intval; } /* get the argument counter from a given stack level */ VM_REG_ACCESS int get_argc_at_level(VMG_ int level) VM_REG_CONST { return get_argc_from_frame(vmg_ get_fp_at_level(vmg_ level)); } /* given a frame pointer, get the 'self' object for the frame */ static vm_obj_id_t get_self_from_frame(VMG_ vm_val_t *fp) { /* get the 'self' slot on the stack */ vm_val_t *self_val = get_from_frame(fp, VMRUN_FPOFS_SELF); /* return the appropriate value */ return (self_val->typ == VM_NIL ? VM_INVALID_OBJ : self_val->val.obj); } /* get the 'self' object at a given stack level */ VM_REG_ACCESS vm_obj_id_t get_self_at_level(VMG_ int level) VM_REG_CONST { return get_self_from_frame(vmg_ get_fp_at_level(vmg_ level)); } /* given a frame pointer, get the target property for the frame */ static vm_prop_id_t get_target_prop_from_frame(VMG_ vm_val_t *fp) { vm_val_t *val; /* get the 'self' slot on the stack */ val = get_from_frame(fp, VMRUN_FPOFS_PROP); /* return the appropriate value */ return (val->typ == VM_NIL ? VM_INVALID_PROP : val->val.prop); } /* get the target property at a given stack level */ VM_REG_ACCESS vm_prop_id_t get_target_prop_at_level( VMG_ int level) VM_REG_CONST { return get_target_prop_from_frame(vmg_ get_fp_at_level(vmg_ level)); } /* given a frame pointer, get the defining object from the frame */ VM_REG_ACCESS vm_obj_id_t get_defining_obj_from_frame(VMG_ vm_val_t *fp) VM_REG_CONST { /* get the defining object slot on the stack */ vm_val_t *val = get_from_frame(fp, VMRUN_FPOFS_DEFOBJ); /* return the appropriate value */ return (val->typ != VM_OBJ ? VM_INVALID_OBJ : val->val.obj); } /* get the defining object at a given stack level */ VM_REG_ACCESS vm_obj_id_t get_defining_obj_at_level(VMG_ int level) VM_REG_CONST { return get_defining_obj_from_frame(vmg_ get_fp_at_level(vmg_ level)); } /* given a frame pointer, get the original target object */ VM_REG_ACCESS vm_obj_id_t get_orig_target_obj_from_frame( VMG_ vm_val_t *fp) VM_REG_CONST { /* get the original target object slot on the stack */ vm_val_t *val = get_from_frame(fp, VMRUN_FPOFS_ORIGTARG); /* return the appropriate value */ return (val->typ == VM_NIL ? VM_INVALID_OBJ : val->val.obj); } /* get the current original target object at a given stack level */ VM_REG_ACCESS vm_obj_id_t get_orig_target_obj_at_level(VMG_ int level) VM_REG_CONST { return get_orig_target_obj_from_frame( vmg_ get_fp_at_level(vmg_ level)); } /* get the enclosing entry pointer from a given frame */ static const uchar *get_enclosing_entry_ptr_from_frame(VMG_ vm_val_t *fp) { return (const uchar *)get_from_frame(fp, VMRUN_FPOFS_ENC_EP)->val.ptr; } /* get the recursive native caller contxt from the frame */ static const vm_rcdesc *get_rcdesc_from_frame(VMG_ vm_val_t *fp) { return (vm_rcdesc *)get_from_frame(fp, VMRUN_FPOFS_RCDESC)->val.ptr; } /* get the frame reference object from a frame */ static vm_val_t *get_frameref_slot(VMG_ vm_val_t *fp) { return get_from_frame(fp, VMRUN_FPOFS_FRAMEREF); } /* get the invokee from a frame */ static vm_val_t *get_invokee_from_frame(VMG_ vm_val_t *fp) { return get_from_frame(fp, VMRUN_FPOFS_INVOKEE); } /* get the invokee of the current function */ VM_REG_ACCESS vm_val_t *get_invokee(VMG0_) { return get_invokee_from_frame(vmg_ frame_ptr_); } /* * Get the return offset from a given frame. This is the offset of * the return address from the start of the method header for the * frame. */ static ulong get_return_ofs_from_frame(VMG_ vm_val_t *fp) { return get_from_frame(fp, VMRUN_FPOFS_RET)->val.ofs; } /* * Get the return address from a given frame. (The return address * is the third item pushed before the enclosing frame pointer, * hence it's at offset -3 from the frame pointer.) Returns zero if * we were called by recursive invocation of the VM - this is not * ambiguous with an actual return address of zero, since zero is * never a valid code address. */ static const uchar *get_return_addr_from_frame(VMG_ vm_val_t *fp) { pool_ofs_t ofs; /* get the return method offset from the stack */ ofs = get_return_ofs_from_frame(vmg_ fp); /* check for special offset values */ switch (ofs) { case VMRUN_RET_RECURSIVE: /* * recursive VM call - there's no byte-code return address in * this case since the return takes us back to native code, so * return 0 to indicate this */ return 0; case VMRUN_RET_OP: /* * return from operator overload; the true return address is in * the stack slot just above the last argument */ ofs = G_interpreter->get_param_from_frame( vmg_ fp, G_interpreter->get_argc_from_frame(vmg_ fp)) ->val.intval; break; case VMRUN_RET_OP_ASILCL: /* * return from operator overload and assign local: the true * return address is two slots above the last argument */ ofs = G_interpreter->get_param_from_frame( vmg_ fp, G_interpreter->get_argc_from_frame(vmg_ fp) + 1) ->val.intval; break; } /* * add the offset to the enclosing entry pointer to yield the * absolute pool address of the return point */ return ofs + get_enclosing_entry_ptr_from_frame(vmg_ fp); } /* * Given a frame pointer, set up a function pointer for the return * address from the frame. */ static void set_return_funcptr_from_frame(VMG_ class CVmFuncPtr *func_ptr, vm_val_t *frame_ptr); /* reset the machine registers to the initial conditions */ void reset(VMG0_); /* * Get the current "self" object. The "self" object is always the * implicit first parameter to any method. Note that this version of * the method *doesn't* check for nil - it assumes that the caller * knows for sure that there's a valid "self", so dispenses with any * checks to save time. */ VM_REG_ACCESS vm_obj_id_t get_self(VMG0_) VM_REG_CONST { /* get the object value of the 'self' slot in the current frame */ return get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF)->val.obj; } /* get the pointer to the current "self" value */ VM_REG_ACCESS vm_val_t *get_self_val(VMG0_) VM_REG_CONST { return get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF); } /* get the self value from a frame */ VM_REG_ACCESS vm_val_t *get_self_val_from_frame(VMG_ vm_val_t *fp) { return get_from_frame(fp, VMRUN_FPOFS_SELF); } /* * Get the current "self" object, checking for nil. If "self" is nil, * we'll return VM_INVALID_OBJ. This version (not get_self()) should * be used whenever it's not certain from context that there's a valid * "self". */ VM_REG_ACCESS vm_obj_id_t get_self_check(VMG0_) VM_REG_CONST { /* get the 'self' slot from the stack frame */ vm_val_t *valp = get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF); /* if it's an object, return the ID, otherwise invalid */ return (valp->typ == VM_OBJ ? valp->val.obj : VM_INVALID_OBJ); } /* set the current 'self' object */ VM_REG_ACCESS void set_self(VMG_ const vm_val_t *val) { /* store the given value in the 'self' slot in the current frame */ *get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF) = *val; } /* create a method context object suitable for LOADCTX */ static void create_loadctx_obj(VMG_ vm_val_t *result, vm_obj_id_t self, vm_obj_id_t defobj, vm_obj_id_t targobj, vm_prop_id_t targprop); /* * Set the current execution context: the 'self' value, the target * property, the original target object, and the defining object. */ VM_REG_ACCESS void set_method_ctx( VMG_ vm_obj_id_t new_self, vm_prop_id_t new_target_prop, vm_obj_id_t new_target_obj, vm_obj_id_t new_defining_obj) { /* set the "self" slot in the current stack frame */ get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF)->set_obj(new_self); /* set the target property slot in the frame */ get_from_frame(frame_ptr_, VMRUN_FPOFS_PROP) ->set_propid(new_target_prop); /* set the original target object slot in the frame */ get_from_frame(frame_ptr_, VMRUN_FPOFS_ORIGTARG) ->set_obj(new_target_obj); /* set the defining object slot in the frame */ get_from_frame(frame_ptr_, VMRUN_FPOFS_DEFOBJ) ->set_obj(new_defining_obj); } /* get the current target property value */ VM_REG_ACCESS vm_prop_id_t get_target_prop(VMG0_) VM_REG_CONST { return get_from_frame(frame_ptr_, VMRUN_FPOFS_PROP)->val.prop; } /* get the current defining object */ VM_REG_ACCESS vm_obj_id_t get_defining_obj(VMG0_) VM_REG_CONST { return get_from_frame(frame_ptr_, VMRUN_FPOFS_DEFOBJ)->get_as_obj(); } /* get the current original target object */ VM_REG_ACCESS vm_obj_id_t get_orig_target_obj(VMG0_) VM_REG_CONST { return get_from_frame(frame_ptr_, VMRUN_FPOFS_ORIGTARG)->val.obj; } /* push an object ID */ void push_obj(VMG_ vm_obj_id_t obj) { push()->set_obj(obj); } /* push an object ID, or nil if it's an invalid object */ void push_obj_or_nil(VMG_ vm_obj_id_t obj) { if (obj == VM_INVALID_OBJ) push()->set_nil(); else push()->set_obj(obj); } /* push a property ID */ void push_prop(VMG_ vm_prop_id_t prop) { push()->set_propid(prop); } /* push a property ID, or nil if it's invalid */ void push_prop_or_nil(VMG_ vm_prop_id_t prop) { if (prop == VM_INVALID_PROP) push()->set_nil(); else push()->set_propid(prop); } /* push a boolean value */ void push_bool(VMG_ int flag) { push()->set_logical(flag); } /* push nil */ void push_nil(VMG0_) { push()->set_nil(); } /* push a code offset value */ void push_codeofs(VMG_ pool_ofs_t ofs) { push()->set_codeofs(ofs); } /* push a code pointer value */ void push_codeptr(VMG_ const void *p) { push()->set_codeptr(p); } /* push a stack pointer value */ void push_stackptr(VMG_ vm_val_t *stack_ptr) { push()->set_stack((void *)stack_ptr); } /* push an integer value */ void push_int(VMG_ int32_t intval) { push()->set_int(intval); } /* push an enumerator value */ void push_enum(VMG_ uint32_t intval) { push()->set_enum(intval); } /* push a C string value */ void push_string(VMG_ const char *str, size_t len); void push_string(VMG_ const char *str) { push_string(vmg_ str, strlen(str)); } /* push a printf-formatted string */ void push_stringf(VMG_ const char *fmt, ...); void push_stringvf(VMG_ const char *fmt, va_list va); /* get the function entrypoint address */ VM_REG_ACCESS const uchar *get_entry_ptr() VM_REG_CONST { return entry_ptr_native_; } /* get the current program counter offset from the entry pointer */ VM_REG_ACCESS uint get_method_ofs() VM_REG_CONST { /* * Return the current program counter minus the current entry * pointer. If there is no current program counter, we're not * executing in byte code, so there's no method offset. */ if (pc_ptr_ != 0) return *pc_ptr_ - entry_ptr_native_; else return 0; } /* * Convert a pointer to the currently executing method into an offset * from the start of the current method. */ VM_REG_ACCESS ulong pc_to_method_ofs(const uchar *p) { /* * get the memory address of the current entry pointer, and * subtract that from the given memory pointer to get an offset * from the start of the current method */ return p - entry_ptr_native_; } /* * Create an exception of the given imported class and throw it. If * the class is not exported, we'll create a basic run-time exception; * if that's not defined, we'll create an arbitrary object. * * Arguments to the exception constructor are on the stack, with the * argument count in argc. If the imported class doesn't exist, we'll * instead throw an intrinsic-class-general-error exception with the * given fallback message as explanatory text. */ void throw_new_class(VMG_ vm_obj_id_t cls, uint argc, const char *fallback_msg); /* * Create an exception of the given RuntimeError subclass and throw it. * If the class isn't exported, we'll create a base RuntimeError. * * Push any constructor arguments *besides* the error number onto the * stack before calling this. A RuntimeError subclass constructor will * always take an additional first argument giving the error number, * but don't push that or include it in the 'argc' value. */ void throw_new_rtesub(VMG_ vm_obj_id_t cls, uint argc, int errnum); /* * Save/restore the interpreter context. This is for use by the * debugger when evaluating an expression in the course of execution, * to ensure that everything is reset properly to the enclosing * execution context when it's finished. */ void save_context(VMG_ vmrun_save_ctx *ctx); void restore_context(VMG_ vmrun_save_ctx *ctx); /* * Get the boundaries of the given source-code statement in the given * function. Fills in the line pointer and *stm_start and *stm_end * with information on the source line containing the given offset in * the given method. Returns true if source information is * successfully located for the given machine code address, false if * not. * * If no debugging information is available for the given code * location, this function cannot get the source-code statement * bounds, and returns false. */ static int get_stm_bounds(VMG_ const class CVmFuncPtr *func_ptr, ulong pc_ofs, class CVmDbgLinePtr *line_ptr, const uchar **stm_start, const uchar **stm_end); /* -------------------------------------------------------------------- */ /* * Set the HALT VM flag. This allows the debugger to terminate the * program immediately, without allowing any more byte-code * instructions to execute. */ void set_halt_vm(int f) { halt_vm_ = f; } /* -------------------------------------------------------------------- */ /* * Start profiling. This deletes any old profiling records and starts * a new profiling session. We'll capture profiling data until * end_profiling() is called. This function is only included in the * build if the profiler is included in the build. */ void start_profiling(); /* end profiling */ void end_profiling(); /* * get the profiling data - we'll invoke the callback once for each * function in our table of data */ void get_profiling_data(VMG_ void (*cb)(void *ctx, const char *func_name, unsigned long time_direct, unsigned long time_in_children, unsigned long call_cnt), void *cb_ctx); /* get the last program counter address */ VM_REG_ACCESS const uchar *get_last_pc() VM_REG_CONST { return pc_ptr_ != 0 ? *pc_ptr_ : 0; } protected: /* * Execute byte code starting at a given address. This function * retains control until the byte code function invoked returns or * throws an unhandled exception. * * If an exception occurs and is not handled by the byte code, we'll * throw VMERR_UNHANDLED_EXC with the exception object as the first * parameter. */ void run(VMG_ const uchar *p); /* * Display a dstring via the default string display function. This * function pushes a string value (with the given constant pool * offset), then does the same work as do_call() to invoke a function * with one argument. * * The string is identified by its offset in the constant pool. */ const uchar *disp_dstring(VMG_ pool_ofs_t ofs, uint caller_ofs, vm_obj_id_t self); /* * Display the value at top of stack via the default string display * function. does the same work as do_call() to invoke the function * with one argument, which must already be on the stack. */ const uchar *disp_string_val(VMG_ uint caller_ofs, vm_obj_id_t self); /* * Set up a function header pointer for the current function */ void set_current_func_ptr(VMG_ class CVmFuncPtr *func_ptr); /* call a built-in function */ void call_bif(VMG_ uint set_index, uint func_index, uint argc); /* * Throw an exception. Returns a non-null program counter if a * handler was found, false if not. If a handler was found, byte-code * execution can proceed; if not, the byte-code execution loop must * pass the exception up to its caller. */ const uchar *do_throw(VMG_ const uchar *pc, vm_obj_id_t exception_obj); /* * Inherit a property - this is essentially the same as get_prop, * but rather than getting the property from the given object, this * ignores any such property defined directly by the object and goes * directly to the inherited definition. However, the "self" object * is still the same as the current "self" object, since we want to * evaluate the inherited method in the context of the original * target "self" object. */ const uchar *inh_prop(VMG_ uint caller_ofs, vm_prop_id_t prop, uint argc); /* * Look up a property value without evaluating it. Returns true if we * found the property, false if not. */ inline static int get_prop_no_eval(VMG_ const vm_val_t **target_obj, vm_prop_id_t target_prop, uint *argc, vm_obj_id_t *srcobj, vm_val_t *val, const vm_val_t **self, vm_val_t *new_self); /* * Evaluate a property value. If the value contains code, we'll * execute the code; if it contains a self-printing string, we'll * display the string; otherwise, we'll push the value onto the stack. * * 'found' indicates whether or not the property is defined by the * object. False indicates that the property is not defined, true * that it is defined. If the property isn't defined, we'll simply * discard arguments and push nil. * * If 'caller_ofs' is zero, we'll recursively invoke the interpreter * loop if it's necessary to run a method; otherwise, we'll set up at * the beginning of the method's code and let the caller proceed into * the code. */ const uchar *eval_prop_val(VMG_ int found, uint caller_ofs, const vm_val_t *val, vm_obj_id_t self, vm_prop_id_t target_prop, const vm_val_t *orig_target_obj, vm_obj_id_t defining_obj, uint argc, const vm_rcdesc *rc); /* * Check a property for validity in a speculative evaluation. If * evaulating the property would cause any side effects, we'll throw * an error (VMERR_BAD_SPEC_EXPR); otherwise, we won't do anything. * Side effects include displaying a dstring or invoking a method. */ void check_prop_spec_eval(VMG_ vm_obj_id_t obj, vm_prop_id_t prop); /* * Return from a function or method. Returns the new program counter * at which to continue execution, and restore machine registers to * the enclosing frame. * * Returns a non-null program counter if execution should proceed, * null if we're returning from the outermost stack level. When we * return null, the caller must return control to the host * environment, since the host environment called the function from * which we're returning. */ const uchar *do_return(VMG0_); /* * append a stack trace to the given string, returning a new string * object */ vm_obj_id_t append_stack_trace(VMG_ vm_obj_id_t str_obj); /* * Validate a local stack index. This verifies that an index used in a * GETSPN, GETSPX, SETSPN, or SETSPX instruction refers to the * temporary storage area for the current function. */ void validate_local_stack_index(VMG_ unsigned int idx) { /* get a pointer to the current function header */ CVmFuncPtr hdr_ptr; hdr_ptr.set(entry_ptr_native_); /* * Check that the index is below the index of the last local * variable. The locals start just below the frame pointer. */ if (idx >= get_depth_rel(frame_ptr_) - hdr_ptr.get_local_cnt()) err_throw(VMERR_STACK_OVERFLOW); } /* push a value onto the stack */ void pushval(VMG_ const vm_val_t *val) { push(val); } /* pop a value off the stack */ void popval(VMG_ vm_val_t *val) { pop(val); } /* add two values, leaving the result in *val1 */ int compute_sum(VMG_ vm_val_t *val1, const vm_val_t *val2); /* compute a sum, specialized for individual opcodes */ const uchar *compute_sum_inc(VMG_ const uchar *p); const uchar *compute_sum_add(VMG_ const uchar *p); const uchar *compute_sum_lcl_imm(VMG_ vm_val_t *lclp, const vm_val_t *ival, int lclidx, const uchar *p); /* subtract one value from another, leaving the result in *val1 */ int compute_diff(VMG_ vm_val_t *val1, vm_val_t *val2); /* compute a difference, specialized for individual opcodes */ const uchar *compute_diff_dec(VMG_ const uchar *p); const uchar *compute_diff_sub(VMG_ const uchar *p); /* compute the product, leaving the result in *val1 */ int compute_product(VMG_ vm_val_t *val1, vm_val_t *val2); /* compute the quotient val1/val2, leaving the result in *val2 */ int compute_quotient(VMG_ vm_val_t *val1, vm_val_t *val2); /* compute the modulo val1%val2, leaving the result in *val2 */ int compute_mod(VMG_ vm_val_t *val1, vm_val_t *val2); /* XOR two values and push the result */ int xor_and_push(VMG_ vm_val_t *val1, vm_val_t *val2); /* process a MAKELSTPAR instruction */ void makelstpar(VMG0_); /* * index container_val by index_val (i.e., compute * container_val[index_val]), storing the result at *result */ VM_REG_ACCESS int apply_index(VMG_ vm_val_t *result, const vm_val_t *container_val, const vm_val_t *index_val); /* * Set the element at index index_val in container_val to new_val, * and push the new container value. The container may be a new * object, since some types (lists, for example) cannot have their * values changed but instead create new objects when an indexed * element is modified. */ VM_REG_ACCESS int set_index(VMG_ vm_val_t *container_val, const vm_val_t *index_val, const vm_val_t *new_val); /* * create a new object of the given index into the metaclass * dependency table for the load image file, using the given number * of parameters; removes the parameters from the stack, and leaves * the new object reference in register R0 */ const uchar *new_and_store_r0(VMG_ const uchar *pc, uint metaclass_idx, uint argc, int is_transient); /* * Compare the two values at top of stack for equality; returns true * if the values are equal, false if not. Removes the two values from * the stack. */ int pop2_equal(VMG0_) { /* compare the values and return the result */ int ret = get(1)->equals(vmg_ get(0)); /* discard the values */ discard(2); /* return the result */ return ret; } /* * Compare the magnitude of the two values at the top of the stack. * Returns 1 if the value at (TOS-1) is greater than the value at TOS, * -1 if (TOS-1) is less than (TOS), and 0 if the two value are equal. * Removes the two values from the stack. */ int pop2_compare(VMG0_) { /* compare the values and return the result */ int ret = get(1)->compare_to(vmg_ get(0)); /* discard the values */ discard(2); /* return the result */ return ret; } /* is TOS-1 < TOS ? */ int pop2_compare_lt(VMG0_) { /* compare the values and return the result */ int ret = get(1)->is_lt(vmg_ get(0)); /* discard the values */ discard(2); /* return the result */ return ret; } /* is TOS-1 <= TOS ? */ int pop2_compare_le(VMG0_) { /* compare the values and return the result */ int ret = get(1)->is_le(vmg_ get(0)); /* discard the values */ discard(2); /* return the result */ return ret; } /* is TOS-1 > TOS ? */ int pop2_compare_gt(VMG0_) { /* compare the values and return the result */ int ret = get(1)->is_gt(vmg_ get(0)); /* discard the values */ discard(2); /* return the result */ return ret; } /* is TOS-1 >= TOS ? */ int pop2_compare_ge(VMG0_) { /* compare the values and return the result */ int ret = get(1)->is_ge(vmg_ get(0)); /* discard the values */ discard(2); /* return the result */ return ret; } /* given a constant pool offset, get a pointer to the constant data */ static const char *get_const_ptr(VMG_ pool_ofs_t ofs) { return G_const_pool->get_ptr(ofs); } /* * get a signed 16-bit byte-code operand, incrementing the * instruction pointer past the operand */ int16_t get_op_int16(const uchar **p) { int16_t ret = (int16_t)osrp2s(*p); *p += 2; return ret; } /* get an unsigned 16-bit byte-code operand */ uint16_t get_op_uint16(const uchar **p) { uint16_t ret = (uint16_t)osrp2(*p); *p += 2; return ret; } /* get a signed 32-bit byte-code operand */ int32_t get_op_int32(const uchar **p) { int32_t ret = (int32_t)osrp4s(*p); *p += 4; return ret; } /* get an unsigned 32-bit byte-code operand */ uint32_t get_op_uint32(const uchar **p) { uint32_t ret = (uint32_t)t3rp4u(*p); *p += 4; return ret; } /* get a signed 8-bit byte-code operand */ int get_op_int8(const uchar **p) { int ret = (int)(signed char)**p; ++(*p); return ret; } /* get an unsigned 8-bit byte-code operand */ uint get_op_uint8(const uchar **p) { uint ret = (uint)**p; ++(*p); return ret; } /* record a function or method entry in the profiler data */ void prof_enter(VMG_ const uchar *fptr); /* record a function or method exit in the profiler data */ void prof_leave(); /* find or create a function entry in the master profiler table */ class CVmHashEntryProfiler *prof_find_master_rec(const struct vm_profiler_rec *p); /* calculate an elapsed time */ void prof_calc_elapsed(vm_prof_time *diff, const vm_prof_time *a, const vm_prof_time *b); /* add an elapsed time value to a cumulative elapsed time value */ void prof_add_elapsed(vm_prof_time *sum, const vm_prof_time *val); /* hash table enumeration callback for dumping profiler data */ static void prof_enum_cb(void *ctx0, class CVmHashEntry *entry0); /* validate the built-in function pointer at top of stack */ void validate_bifptr(VMG0_); /* * Function header size - obtained from the image file upon loading */ size_t funchdr_size_; /* * A pointer to a global variable in the object table (CVmObjTable) * containing the function to invoke for the SAY and SAYVAL opcodes. * This can be a function pointer, a function object, or nil. If this * is nil, the SAY opcode will throw an error. */ struct vm_globalvar_t *say_func_; /* * The method to invoke for the SAY and SAYVAL opcodes when a valid * "self" object is available. If no method is defined, this will * be set to VM_INVALID_PROP. */ vm_prop_id_t say_method_; /* * R0 - data register 0. This register stores function return * values. */ VM_IF_REGS_IN_STRUCT(vm_val_t r0_;) /* * native entry pointer value - this is simply the translated value of * the entry pointer (i.e., G_code_pool->get_ptr(entry_ptr_)) */ VM_IF_REGS_IN_STRUCT(const uchar *entry_ptr_native_;) /* * FP - frame pointer register. This points to the base of the * current stack activation frame. Local variables and parameters * are reachable relative to this register. */ VM_IF_REGS_IN_STRUCT(vm_val_t *frame_ptr_;) /* * Pointer to program counter - we use this in the debugger to create * pseudo-stack frames for system code when we recursively invoke the * VM, and for finding the current PC from intrinsic function code. */ VM_IF_REGS_IN_STRUCT(const uchar **pc_ptr_;) /* * Flag: VM is halting. This is used by the debugger to force the * program to stop executing. This is not used except with the * debug-mode interpreter. */ int halt_vm_; /* flag: profiling is active */ int profiling_; /* in case we have a profiler, include the profiler stack */ struct vm_profiler_rec *prof_stack_; size_t prof_stack_max_; /* next available index in the profiler stack */ size_t prof_stack_idx_; /* * Start of execution in the currently active function, since the last * call or return. This uses the OS-specific high-precision timer * (defined by os_prof_curtime() in vmprof.h). * * Each time we call a function or return from a function, we measure * the delta from this value, then add that in to the cumulative time * for the function. */ vm_prof_time prof_start_; /* * profiler master hash table - this is a table with one entry for * every function and method called since we began profiling, keyed by * method or function ID (the object.property for a method, or the * entrypoint code offset for a function) */ class CVmHashTable *prof_master_table_; }; #endif /* VMRUN_H */ ������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmrunsym.cpp����������������������������������������������������������������������0000664�0000000�0000000�00000007512�12650170723�0016267�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmrunsym.cpp - runtime symbol table Function Notes Modified 02/17/01 MJRoberts - Creation */ #include "t3std.h" #include "vmtype.h" #include "vmrunsym.h" /* * delete - deletes all of our symbol list entries */ CVmRuntimeSymbols::~CVmRuntimeSymbols() { vm_runtime_sym *sym; vm_runtime_sym *nxt; /* delete each symbol */ for (sym = head_ ; sym != 0 ; sym = nxt) { /* * remember the next one, since we're deleting our link pointer * along with the structure */ nxt = sym->nxt; /* delete it */ t3free(sym); } } /* * add a symbol */ void CVmRuntimeSymbols::add_sym(const char *sym, size_t len, const vm_val_t *val) { vm_runtime_sym *new_sym; /* allocate a new structure */ new_sym = (vm_runtime_sym *)t3malloc(osrndsz( sizeof(vm_runtime_sym) + len)); /* copy the data */ new_sym->val = *val; new_sym->len = len; memcpy(new_sym->sym = (char *)(new_sym + 1), sym, len); /* there are no macro parameters */ new_sym->macro_expansion = 0; new_sym->macro_exp_len = 0; new_sym->macro_argc = 0; new_sym->macro_args = 0; new_sym->macro_flags = 0; /* link it into our list */ new_sym->nxt = 0; if (tail_ == 0) head_ = new_sym; else tail_->nxt = new_sym; /* it's the new tail */ tail_ = new_sym; /* count it */ ++cnt_; } /* * Add a macro definition */ vm_runtime_sym *CVmRuntimeSymbols::add_macro(const char *sym, size_t len, size_t explen, unsigned int flags, int argc, size_t arglen) { /* allocate the structure */ vm_runtime_sym *new_sym = (vm_runtime_sym *)t3malloc(osrndsz( sizeof(vm_runtime_sym) + (argc * sizeof(char*)) + len + explen + arglen)); /* set up the internal allocation pointers within the structure */ if (argc != 0) { new_sym->macro_args = (char **)(new_sym + 1); new_sym->macro_expansion = (char *)(new_sym->macro_args + argc); new_sym->macro_args[0] = new_sym->macro_expansion + explen; new_sym->sym = new_sym->macro_args[0] + arglen; } else { new_sym->macro_args = 0; new_sym->macro_expansion = (char *)(new_sym + 1); new_sym->sym = new_sym->macro_expansion + explen; } /* copy the basic data */ new_sym->val.set_nil(); new_sym->len = len; memcpy(new_sym->sym, sym, len); /* copy the basic macro data */ new_sym->macro_argc = argc; new_sym->macro_flags = flags; new_sym->macro_exp_len = explen; /* link it into our list */ new_sym->nxt = 0; if (tail_ == 0) head_ = new_sym; else tail_->nxt = new_sym; /* it's the new tail */ tail_ = new_sym; /* count it */ ++cnt_; /* return the new symbol */ return new_sym; } /* * Find the name for a value */ const char *CVmRuntimeSymbols::find_val_name(VMG_ const vm_val_t *val, size_t *name_len) const { vm_runtime_sym *sym; /* search the table */ for (sym = head_ ; sym != 0 ; sym = sym->nxt) { /* check for a match to the value */ if (val->equals(vmg_ &sym->val)) { /* it's a match - return the name */ *name_len = sym->len; return sym->sym; } } /* didn't find it - return null */ *name_len = 0; return 0; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmrunsym.h������������������������������������������������������������������������0000664�0000000�0000000�00000012422�12650170723�0015730�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmrunsym.h - run-time global symbol table Function Defines a symbol table structure that allows us to convey the global symbols from the compiler or image loader to the interpreter. When the compiler runs pre-initialization, or when the image loader finds debug symbols in a program, the interpreter builds a LookupTable object containing the global symbol table. This provides a "reflection" mechanism that lets the running program inspect its own symbol table. Because we have two very different ways of getting the symbol information -- from the compiler, or from the image file's debug records -- we define this class as an intermediary that provides a common mechanism for conveying the information to the interpreter. Note that, when the information is coming from the compiler, there's usually not a debug symbol table in the image file, because the compiler usually only runs pre-initialization on programs compiled for release (i.e., with no debug information), so we can't count on debug records in the image file as a common conveyance mechanism. This is a very simple linked list storage class, because we have no need to search this symbol table. The only things we do with this type of symbol table are to load it with all of the symbols from the compiler or image file's debug records, and then enumerate all of our symbols to build a run-time LookupTable. One final note: we could conceivably avoid having this other data representation by having the compiler build a LookupTable directly and storing it in the image file. However, this would put the LookupTable into the root set, so it could never be deleted. By building the LookupTable dynamically during pre-initialization, the table will be automatically garbage collected before the image file is finalized if the program doesn't retain a reference to it in a location accessible from the root set. This allows the program to control the presence of this extra information in a very natural way. Notes Modified 02/17/01 MJRoberts - Creation */ #ifndef VMRUNSYM_H #define VMRUNSYM_H #include "vmtype.h" class CVmRuntimeSymbols { public: /* construct */ CVmRuntimeSymbols() { /* nothing in the list yet - empty the list and clear the count */ head_ = tail_ = 0; cnt_ = 0; } /* delete */ ~CVmRuntimeSymbols(); /* add a symbol */ void add_sym(const char *sym, size_t len, const vm_val_t *val); /* add a macro definition */ struct vm_runtime_sym *add_macro(const char *sym, size_t len, size_t explen, unsigned int flags, int argc, size_t arglen); /* get the first symbol */ struct vm_runtime_sym *get_head() const { return head_; } /* get the number of symbols */ size_t get_sym_count() const { return cnt_; } /* find an object name - returns null if the object isn't found */ const char *find_obj_name(VMG_ vm_obj_id_t obj, size_t *name_len) const { vm_val_t val; /* set up an object value */ val.set_obj(obj); /* find the value */ return find_val_name(vmg_ &val, name_len); } /* find a property name - returns null if not found */ const char *find_prop_name(VMG_ vm_prop_id_t prop, size_t *name_len) const { vm_val_t val; /* set up a property ID value */ val.set_propid(prop); /* find the value */ return find_val_name(vmg_ &val, name_len); } /* * Find the name for a value. Fills in the length pointer with the * length of the return string, which is not null-terminated. * Returns null if no such value is present in the table. */ const char *find_val_name(VMG_ const vm_val_t *val, size_t *name_len) const; protected: /* head and tail of symbol list */ struct vm_runtime_sym *head_; struct vm_runtime_sym *tail_; /* number of symbols in the list */ size_t cnt_; }; /* * A Symbol */ struct vm_runtime_sym { /* next symbol in the list */ vm_runtime_sym *nxt; /* the symbol name */ char *sym; /* the length of the name */ size_t len; /* the value of the symbol */ vm_val_t val; /* for a macro, the flags from the MACR record */ unsigned int macro_flags; /* for a macro, the expansion of the macro */ char *macro_expansion; size_t macro_exp_len; /* for a macro, the number of formal parameters */ int macro_argc; /* for a macro, the argument list */ char **macro_args; /* * Commit storage for a macro argument. Arguments MUST be stored * sequentially, starting from number 0. The caller must * null-terminate each name, AND must include the null byte in the * 'len' value. */ void commit_macro_arg(int i, size_t len) { /* if this isn't the last argument, set the next allocation pointer */ if (i + 1 < macro_argc) macro_args[i + 1] = macro_args[i] + len; } }; #endif /* VMRUNSYM_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmsa.cpp��������������������������������������������������������������������������0000664�0000000�0000000�00000013361�12650170723�0015334�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmsa.cpp - stand-alone interpreter entrypoints Function Defines certain entrypoints that are not needed in a normal stand-alone version of the interpreter. The entrypoints defined here are dummy implementations that should never be called. Certain functions are needed only in special VM versions that support, for example, rebuilding of an image file after running 'preinit'. Notes Modified 07/21/99 MJRoberts - Creation */ #include <assert.h> #include "t3std.h" #include "vmobj.h" #include "vmglob.h" #include "vmdict.h" #include "vmstr.h" #include "vmlst.h" #include "vmbignum.h" #include "vmtobj.h" #include "vmgram.h" #include "vmmeta.h" #include "vmintcls.h" #include "vmiter.h" #include "vmvec.h" #include "vmlookup.h" #include "vmbytarr.h" #include "vmcset.h" #include "vmfilobj.h" #include "vmtmpfil.h" #include "vmfilnam.h" #include "vmpat.h" #include "vmstrcmp.h" #include "vmstrbuf.h" #include "vmdynfunc.h" #include "vmfref.h" #include "vmdate.h" #include "vmtzobj.h" #ifdef TADSNET #include "vmhttpsrv.h" #include "vmhttpreq.h" #endif ulong CVmObjTads::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjTads::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjString::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjString::reserve_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } void CVmObjString::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjList::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjList::reserve_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } void CVmObjList::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjDict::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjDict::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjGramProd::rebuild_image(VMG_ char *buf, ulong buflen) { assert(FALSE); return 0; } void CVmMetaTable::rebuild_image(class CVmImageWriter *) { } ulong CVmObjBigNum::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjBigNum::reserve_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } void CVmObjBigNum::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjClass::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmObjIterIdx::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjIterIdx::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjVector::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjVector::reserve_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } void CVmObjVector::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjLookupTable::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmObjStringBuffer::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjLookupTable::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjIterLookupTable::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjIterLookupTable::convert_to_const_data( VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjByteArray::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmObjCharSet::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmObjFile::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmObjTemporaryFile::rebuild_image(VMG_ char *buf, ulong buflen) { assert(FALSE); return 0; } ulong CVmObjFileName::rebuild_image(VMG_ char *buf, ulong buflen) { assert(FALSE); return 0; } ulong CVmObjPattern::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjPattern::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjStrComp::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmDynamicFunc::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmObjFrameDesc::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmObjFrameRef::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmObjDate::rebuild_image(VMG_ char *buf, ulong buflen) { assert(FALSE); return 0; } ulong CVmObjTimeZone::rebuild_image(VMG_ char *buf, ulong buflen) { assert(FALSE); return 0; } /* ------------------------------------------------------------------------ */ /* * networking support, if present */ #ifdef TADSNET ulong CVmObjHTTPServer::rebuild_image(VMG_ char *buf, ulong buflen) { assert(FALSE); return 0; } ulong CVmObjHTTPRequest::rebuild_image(VMG_ char *buf, ulong buflen) { assert(FALSE); return 0; } #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmsave.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000035616�12650170723�0015676�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmsave.cpp - T3 save/restore state Function Notes Modified 08/02/99 MJRoberts - Creation */ /* * Saved game header structure: * *. <17 bytes> - signature, "T3-state-vXXX\015\012\032", where XXX is *. the hex version number *. UINT4 - stream size, counting from the timestamp on *. UINT4 - CRC32 checksum of stream, from the timestamp on *. <24 bytes> - timestamp of the game file, to ensure that this saved *. state file is only applied to the correct image file *. UINT2 - length of filename in bytes *. <? bytes> - image file name; length given by preceding UINT2 *. UINT2 - number of bytes in metadata table *. <? bytes> - metadata table *. <? bytes> - object stream data * * The metadata table contains any number of name/value string pairs. * These are arbitrary values that allow the game to store descriptive * information about the saved game that the interpreter and other tools * can display to the user, to jog the user's memory when reviewing a * collection of saved game files. The format of the table is: * *. UINT2 - number of string/value pairs *. <? bytes> - first pair *. <? bytes> - second pair *. ...etc... * * Each pair has this format: * *. UINT2 - length of the name string *. UINT2 - length of the value string *. <? bytes> - name string *. <? bytes> - value string */ #include "t3std.h" #include "os.h" #include "vmglob.h" #include "vmsave.h" #include "vmfile.h" #include "vmimage.h" #include "vmobj.h" #include "vmrun.h" #include "vmstack.h" #include "vmundo.h" #include "vmmeta.h" #include "vmcrc.h" #include "vmlookup.h" #include "vmstr.h" /* ------------------------------------------------------------------------ */ /* * Saved state signature. This signature is tied to a specific format * version; it should be changed whenever the format version is modified * so that it is incompatible with older versions. * * Note that incompatible changes to intrinsic class serialization formats * will require updating the version number. * * Incompatible changes to the format are not a particularly big deal. * Saved states tend to be local to a particular machine, since they're * mostly used to suspend sessions for later resumption and to "branch" * the state evolution (i.e., to allow playing a game from a particular * point, then returning later to that same point to play again, but doing * different things this time; this is used particularly to save "good" * positions as a precaution against later getting into unwinnable * states). */ #define VMSAVEFILE_SIG "T3-state-v000A\015\012\032" /* ------------------------------------------------------------------------ */ /* * Compute the checksum of the contents of a file stream. We'll compute * the checksum of the given file, starting at the current seek position * and running for the requested number of bytes. */ static unsigned long compute_checksum(CVmFile *fp, unsigned long len) { CVmCRC32 crc; /* read the file and compute the CRC value for its contents */ while (len != 0) { char buf[256]; size_t cur_len; /* figure out how much we can load from the file */ cur_len = sizeof(buf); if (cur_len > len) cur_len = (size_t)len; /* load the data from the file */ fp->read_bytes(buf, cur_len); /* deduct the amount we read from the overall file length remaining */ len -= cur_len; /* scan this block into the checksum */ crc.scan_bytes(buf, cur_len); } /* return the computed value */ return crc.get_crc_val(); } /* ------------------------------------------------------------------------ */ /* * Callback for enumerating the LookupTable entries for the metadata table * while saving a game */ struct metatab_saver { metatab_saver(CVmFile *fp) { this->fp = fp; this->cnt = 0; } /* number of name/value pairs we've written so far */ int cnt; /* file we're writing to */ CVmFile *fp; /* LookupTable::for_each callback */ static void cb(VMG_ const vm_val_t *key, const vm_val_t *val, void *ctx) { /* the context is our 'this' pointer */ metatab_saver *self = (metatab_saver *)ctx; CVmFile *fp = self->fp; /* both the key and value must be strings for us to save them */ const char *keystr = key->get_as_string(vmg0_); const char *valstr = val->get_as_string(vmg0_); if (keystr != 0 && valstr != 0) { size_t len; /* save the key string */ fp->write_uint2(len = vmb_get_len(keystr)); fp->write_bytes(keystr + VMB_LEN, len); /* save the value string */ fp->write_uint2(len = vmb_get_len(valstr)); fp->write_bytes(valstr + VMB_LEN, len); /* count the pair */ self->cnt += 1; } } }; /* ------------------------------------------------------------------------ */ /* * Save VM state to a file */ void CVmSaveFile::save(VMG_ CVmFile *fp, CVmObjLookupTable *metatab) { /* write the signature */ fp->write_bytes(VMSAVEFILE_SIG, sizeof(VMSAVEFILE_SIG)-1); /* note the seek position of the start of the file header */ long startpos = fp->get_pos(); /* write a placeholder for the stream size and checksum */ fp->write_uint4(0); fp->write_uint4(0); /* write the image file's timestamp */ fp->write_bytes(G_image_loader->get_timestamp(), 24); /* get the image filename */ const char *fname = G_image_loader->get_filename(); size_t fname_len = strlen(fname); /* * write the image filename, so we can figure out what image file to * load if we start the interpreter specifying only the saved state * to be restored */ fp->write_uint2(fname_len); fp->write_bytes(fname, fname_len); /* if there's a metadata table, write it out */ if (metatab != 0) { /* write a placeholder for the length and entry count */ long metapos = fp->get_pos(); fp->write_uint2(0); fp->write_uint2(0); /* run through the table and write out the entries */ metatab_saver ctx(fp); metatab->for_each(vmg_ &ctx.cb, &ctx); /* go back and fix up the table length and entry count */ long endpos = fp->get_pos(); fp->set_pos(metapos); fp->write_uint2((int)(endpos - metapos - 2)); fp->write_uint2(ctx.cnt); /* it's an error if this table exceeds 64k */ if (endpos - metapos > 0xffff) err_throw(VMERR_DESC_TAB_OVERFLOW); /* seek back to the end of the table */ fp->set_pos(endpos); } else { /* there's no metadata table - just write a zero-byte length */ fp->write_uint2(0); } /* save all modified object state */ G_obj_table->save(vmg_ fp); /* save the synthesized exports */ G_image_loader->save_synth_exports(vmg_ fp); /* remember where the file ends */ long endpos = fp->get_pos(); /* * compute the size of the data stream - this includes everything * after the size/checksum fields */ unsigned long datasize = endpos - startpos - 8; /* * seek back to just after the size/checksum header - this is the * start of the section of the file for which we must compute the * checksum */ fp->set_pos(startpos + 8); /* compute the checksum */ unsigned long crcval = compute_checksum(fp, datasize); /* * seek back to the size/checksum header, and fill in those fields now * that we know their values */ fp->set_pos(startpos); fp->write_uint4(datasize); fp->write_uint4(crcval); /* seek back to the end of the file */ fp->set_pos(endpos); } /* ------------------------------------------------------------------------ */ /* * Given a saved state file, get the name of the image file that was * loaded when the state file was created. */ int CVmSaveFile::restore_get_image(osfildef *fp, char *fname_buf, size_t fname_buf_len) { /* read the signature, size/checksum, and timestamp fields */ char buf[128]; if (osfrb(fp, buf, sizeof(VMSAVEFILE_SIG)-1 + 8 + 24)) return VMERR_READ_FILE; /* check the signature */ if (memcmp(buf, VMSAVEFILE_SIG, sizeof(VMSAVEFILE_SIG)-1) != 0) return VMERR_NOT_SAVED_STATE; /* read the length of the image file name */ if (osfrb(fp, buf, 2)) return VMERR_READ_FILE; /* get the length from the buffer */ size_t len = osrp2(buf); /* if it won't fit in the buffer, return an error */ if (len + 1 > fname_buf_len) return VMERR_READ_FILE; /* read the name into the caller's buffer */ if (osfrb(fp, fname_buf, len)) return VMERR_READ_FILE; /* null-terminate the name */ fname_buf[len] = '\0'; /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Restore VM state from a file. Returns zero on success, non-zero on * error. */ int CVmSaveFile::restore(VMG_ CVmFile *fp) { /* we don't have a fixup table yet (the object loader will create one) */ CVmObjFixup *fixups = 0; /* read the file's signature */ char buf[128]; fp->read_bytes(buf, sizeof(VMSAVEFILE_SIG)-1); /* check the signature */ if (memcmp(buf, VMSAVEFILE_SIG, sizeof(VMSAVEFILE_SIG)-1) != 0) return VMERR_NOT_SAVED_STATE; /* read the size/checksum fields */ unsigned long datasize = fp->read_uint4(); unsigned long old_crcval = fp->read_uint4(); /* note the starting position of the datastream */ long startpos = fp->get_pos(); /* compute the checksum of the file data */ unsigned long new_crcval = compute_checksum(fp, datasize); /* * if the checksum we computed doesn't match the one stored in the * file, the file is corrupted */ if (new_crcval != old_crcval) return VMERR_BAD_SAVED_STATE; /* seek back to the starting position */ fp->set_pos(startpos); /* check the timestamp */ fp->read_bytes(buf, 24); if (memcmp(buf, G_image_loader->get_timestamp(), 24) != 0) return VMERR_WRONG_SAVED_STATE; /* * skip the image filename - since we already have an image file * loaded, this information is of no use to us here (it's only * useful when we want to restore a saved state before we know what * the image file is) */ fp->set_pos_from_cur(fp->read_int2()); /* * skip the metadata table - it's provided for browsing tools, and we * have no use for it ourselves */ fp->set_pos_from_cur(fp->read_int2()); /* * discard all undo information - any undo information we currently * have obviously can't be applied to the restored state */ G_undo->drop_undo(vmg0_); /* * Disable garbage collection while restoring. This is necessary * because there are possible intermediate states where we have * restored some of the objects but not all of them, so objects that * are reachable from the fully restored state won't necessarily appear * to be reachable from all possible intermediate states. */ int old_gc_enabled = G_obj_table->enable_gc(vmg_ FALSE); int err = 0; err_try { /* forget any IntrinsicClass instances we created at startup */ G_meta_table->forget_intrinsic_class_instances(vmg0_); /* load the object data from the file */ if ((err = G_obj_table->restore(vmg_ fp, &fixups)) != 0) goto read_done; /* load the synthesized exports from the file */ err = G_image_loader->restore_synth_exports(vmg_ fp, fixups); if (err != 0) goto read_done; /* * re-link to the exports and synthesized exports loaded from the * saved session */ G_image_loader->do_dynamic_link(vmg0_); /* create any missing IntrinsicClass instances */ G_meta_table->create_intrinsic_class_instances(vmg0_); /* perform any requested post-load object initializations */ G_obj_table->do_all_post_load_init(vmg0_); read_done: ; } err_catch(exc) { /* remember the error code */ err = exc->get_error_code(); } err_end; /* we're done with the fixup table, so delete it if we created one */ if (fixups != 0) delete fixups; /* restore the garbage collector's enabled state */ G_obj_table->enable_gc(vmg_ old_gc_enabled); /* if any error occurred, throw the error */ if (err != 0) err_throw(err); /* * explicitly run garbage collection, since any dynamic objects that * were reachable before the restore only through non-transient * references will no longer be reachable, all of the non-transient * references having been replaced now */ G_obj_table->gc_full(vmg0_); /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Reset to initial image file state */ void CVmSaveFile::reset(VMG0_) { /* * discard undo information, since it applies only to the current VM * state and obviously is no longer relevant after we reset to the * initial state */ G_undo->drop_undo(vmg0_); /* * discard all synthesized exports, since we want to dynamically link * to the base image file state */ G_image_loader->discard_synth_exports(); /* forget any IntrinsicClass instances we created at startup */ G_meta_table->forget_intrinsic_class_instances(vmg0_); /* reset all objects to initial image file load state */ G_obj_table->reset_to_image(vmg0_); /* * forget the previous dynamic linking information and relink to the * image file again - this will ensure that any objects created after * load are properly re-created now */ G_image_loader->do_dynamic_link(vmg0_); /* create any missing IntrinsicClass instances */ G_meta_table->create_intrinsic_class_instances(vmg0_); /* perform any requested post-load object initializations */ G_obj_table->do_all_post_load_init(vmg0_); /* * explicitly run garbage collection to clean up dynamic objects that * are no longer reachable from the initial state */ G_obj_table->gc_full(vmg0_); /* run the static initializers */ G_image_loader->run_static_init(vmg0_); } ������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmsave.h��������������������������������������������������������������������������0000664�0000000�0000000�00000003607�12650170723�0015336�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmsave.h - save/restore VM state Function Saves/restores VM state to/from a file. Notes Modified 08/02/99 MJRoberts - Creation */ #ifndef VMSAVE_H #define VMSAVE_H #include "vmglob.h" /* * Save/restore API. (This class is all static methods, so it's never * instatiated; it's really just a namespace.) */ class CVmSaveFile { public: /* * Save state to a file. Writes the state information to the given * open file stream. * * 'metadata' is an optional LookupTable object containing * string->string associations. We'll write each key/value pair to the * save file in the metadata header section. This section allows the * interpreter and external tools to display user-readable information * on the saved game. For example, you could store things like the * current location, chapter number, score, etc - things that would * help the user identify the context of the saved game when looking * for the one he/she wishes to restore. */ static void save(VMG_ class CVmFile *fp, class CVmObjLookupTable *metadata); /* * given a saved state file, read the name of the image file that * created it */ static int restore_get_image(osfildef *fp, char *fname_buf, size_t fname_buf_len); /* * Restore state from a file. Returns zero on success, or a * VMERR_xxx code indicating the problem on failure. */ static int restore(VMG_ class CVmFile *fp); /* reset the VM to the initial image file state */ static void reset(VMG0_); protected: }; #endif /* VMSAVE_H */ �������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmsort.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000004601�12650170723�0015715�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmsort.cpp - T3 VM quicksort implementation Function Implements quicksort. We use our own implementation rather than the standard C library's qsort() routine for two reasons. First, we might want to throw an exception out of the comparison routine, and it is not clear that it is safe to longjmp() past qsort() on every type of machine and every C run-time implementation. Second, the standard C library's qsort() routine doesn't provide any means to pass a context to the comparison callback, and further insists that the data to be sorted be arranged as an array; we provide a higher-level abstraction for the comparison callback. Notes Modified 05/14/00 MJRoberts - Creation */ #include <stdlib.h> #include "t3std.h" #include "vmglob.h" #include "vmsort.h" /* ------------------------------------------------------------------------ */ /* * perform a quicksort */ void CVmQSortData::sort(VMG_ size_t l, size_t r) { /* proceed if we have a non-empty range */ if (r > l) { size_t i, j; size_t v_idx = r; /* start at the ends of the range */ i = l - 1; j = r; /* partition the range */ do { /* find the leftmost element >= the right element */ do { ++i; } while (i != r && compare(vmg_ i, v_idx) < 0); /* find the rightmost element <= the right element */ do { --j; } while (j != l && compare(vmg_ j, v_idx) > 0); /* exchange elements i and j */ exchange(vmg_ i, j); /* if we moved the 'v' element, follow that in the index */ if (v_idx == i) v_idx = j; else if (v_idx == j) v_idx = i; } while (j > i); /* undo the last exchange */ exchange(vmg_ i, j); /* exchange the right value into the pivot point */ exchange(vmg_ i, r); /* recursively sort the subranges */ if (i > l) sort(vmg_ l, i - 1); if (i < r) sort(vmg_ i + 1, r); } } �������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmsort.h��������������������������������������������������������������������������0000664�0000000�0000000�00000004017�12650170723�0015363�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmsort.h - T3 VM quicksort implementation Function Notes Modified 05/14/00 MJRoberts - Creation */ #ifndef VMSORT_H #define VMSORT_H #include <stdlib.h> #include "t3std.h" #include "vmglob.h" #include "vmtype.h" #include "vmrun.h" /* ------------------------------------------------------------------------ */ /* * Quicksort data interface */ class CVmQSortData { public: /* * sort a range; to sort the entire array, provide the indices of * the first and last elements of the array, inclusive */ void sort(VMG_ size_t l, size_t r); /* * compare two elements by index - returns -1 if the first element * is less than the second, 0 if they're equal, 1 if the first is * greater than the second */ virtual int compare(VMG_ size_t idx_a, size_t idx_b) = 0; /* exchange two elements */ virtual void exchange(VMG_ size_t idx_a, size_t idx_b) = 0; }; /* ------------------------------------------------------------------------ */ /* * Sorter implementation for sets of vm_val_t data */ class CVmQSortVal: public CVmQSortData { public: CVmQSortVal() { compare_fn_.set_nil(); descending_ = FALSE; } /* get/set an element */ virtual void get_ele(VMG_ size_t idx, vm_val_t *val) = 0; virtual void set_ele(VMG_ size_t idx, const vm_val_t *val) = 0; /* compare */ virtual int compare(VMG_ size_t idx_a, size_t idx_b); /* exchange */ virtual void exchange(VMG_ size_t idx_a, size_t idx_b); /* * comparison function - if this is nil, we'll compare the values as * ordinary values */ vm_val_t compare_fn_; /* flag: sort descending */ int descending_; /* recursive native caller context */ vm_rcdesc rc; }; #endif /* VMSORT_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmsortv.cpp�����������������������������������������������������������������������0000664�0000000�0000000�00000003635�12650170723�0016111�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmsortv.cpp - CVmSortVal implementation Function Notes Modified 05/14/00 MJRoberts - Creation */ #include <stdlib.h> #include "t3std.h" #include "vmglob.h" #include "vmsort.h" #include "vmstack.h" #include "vmrun.h" #include "vmerr.h" #include "vmerrnum.h" /* ------------------------------------------------------------------------ */ /* * compare two vm_val_t values */ int CVmQSortVal::compare(VMG_ size_t a, size_t b) { int result; vm_val_t val_a; vm_val_t val_b; /* get the two values */ get_ele(vmg_ a, &val_a); get_ele(vmg_ b, &val_b); /* check for an explicit comparison function */ if (compare_fn_.typ != VM_NIL) { vm_val_t val; /* push the values (in reverse order) */ G_stk->push(&val_b); G_stk->push(&val_a); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ &compare_fn_, 2, &rc, 0); /* get the result */ val = *G_interpreter->get_r0(); /* get the result value */ result = val.num_to_int(vmg0_); } else { /* compare the values */ result = val_a.compare_to(vmg_ &val_b); } /* if we're sorting in descending order, reverse the result */ if (descending_) result = -result; /* return the result */ return result; } /* * exchange two vm_val_t elements */ void CVmQSortVal::exchange(VMG_ size_t a, size_t b) { vm_val_t val_a; vm_val_t val_b; /* get the two elements */ get_ele(vmg_ a, &val_a); get_ele(vmg_ b, &val_b); /* store the two elements, swapping the positions */ set_ele(vmg_ b, &val_a); set_ele(vmg_ a, &val_b); } ���������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmsrcf.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000015320�12650170723�0015663�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmsrcf.cpp - T3 VM source file list Function Notes Modified 12/01/99 MJRoberts - Creation */ #include "t3std.h" #include "vmsrcf.h" /* ------------------------------------------------------------------------ */ /* * Source file table implementation */ /* * Initialize */ CVmSrcfTable::CVmSrcfTable() { /* no entries yet */ list_ = 0; list_used_ = 0; list_alloc_ = 0; } /* * Delete */ CVmSrcfTable::~CVmSrcfTable() { /* delete the list if we allocated one */ if (list_ != 0) { /* clear the table */ clear(); /* delete the list */ t3free(list_); } } /* * Clear the table */ void CVmSrcfTable::clear() { size_t i; /* delete each entry */ for (i = 0 ; i < list_used_ ; ++i) delete(list_[i]); /* * we no longer have any entries, but leave the list allocated in * case we add entries again */ list_used_ = 0; } /* * Add a new entry to the table */ CVmSrcfEntry *CVmSrcfTable::add_entry(int orig_index, size_t name_len) { CVmSrcfEntry *entry; /* allocate the new entry */ entry = new CVmSrcfEntry(orig_index, (uint)orig_index == list_used_, name_len); /* * make sure we have room in our list for a new entry; if not, * expand the list */ if (list_used_ >= list_alloc_) { size_t siz; /* calculate the expanded list size */ list_alloc_ += 10; siz = list_alloc_ * sizeof(list_[0]); /* allocate or reallocate the list */ if (list_ == 0) list_ = (CVmSrcfEntry **)t3malloc(siz); else list_ = (CVmSrcfEntry **)t3realloc(list_, siz); } /* add the new entry */ list_[list_used_] = entry; /* count the new entry */ ++list_used_; /* return the new entry */ return entry; } /* ------------------------------------------------------------------------ */ /* * Source file record implementation */ /* * Allocate or expand the line records array */ void CVmSrcfEntry::alloc_line_records(ulong cnt) { ulong siz; /* * if the current array size is already big enough for the given * count, there's nothing to do */ if (cnt <= lines_alo_) return; /* calculate the allocation size */ siz = cnt * sizeof(lines_[0]); /* * if the new size exceeds the maximum local system's architectural * limit for a single allocation, restrict the size to the maximum * allocation */ if (siz > OSMALMAX) { /* recalculate the size for the maximum architectural allocation */ cnt = OSMALMAX / sizeof(lines_[0]); siz = cnt * sizeof(lines_[0]); /* if we're already there, ignore the request */ if (cnt <= lines_alo_) return; } /* allocate or reallocate the line record array */ if (lines_ == 0) lines_ = (CVmSrcfLine *)t3malloc((size_t)siz); else lines_ = (CVmSrcfLine *)t3realloc(lines_, (size_t)siz); /* remember the new array size */ lines_alo_ = cnt; } /* * add a line record */ void CVmSrcfEntry::add_line_record(ulong linenum, ulong code_addr) { /* make sure we have enough space */ if (lines_cnt_ >= lines_alo_) { /* expand the line records array */ alloc_line_records(lines_alo_ + 1024); /* if that didn't create enough space, ignore the new record */ if (lines_cnt_ >= lines_alo_) return; } /* add the new record */ lines_[lines_cnt_].linenum = linenum; lines_[lines_cnt_].code_addr = code_addr; /* count the new record */ ++lines_cnt_; } /* * Find a source line */ ulong CVmSrcfEntry::find_src_addr(ulong *linenum, int exact) { long hi, lo, cur; /* if there are no line records, return failure */ if (lines_cnt_ == 0) return 0; /* perform a binary search of the line record array */ lo = 0; hi = (long)lines_cnt_ - 1; while (lo <= hi) { int match; /* split the difference */ cur = lo + (hi - lo)/2; /* * Check for a match. If they require an exact match, we must * find the line number exactly. Otherwise, check to see if * this is the next executable line after the given line. */ if (exact) { /* exact match required */ match = (lines_[cur].linenum == *linenum); } else { /* * exact match not required - match if this is the next * executable line after the requested line, or this is the * last executable line and the requested line is higher */ if (cur == 0) { /* * this is the first executable line - if the requested * line is before this one, this is our match */ match = (*linenum <= lines_[cur].linenum); } else if (cur == (long)lines_cnt_ - 1) { /* * this is the last executable line - if the requested * line is after this one, this is our match */ match = (*linenum >= lines_[cur].linenum); } else { /* * we're somewhere in the middle of the file - if the * requested line is before this one, but after the * previous executable line, this is our match */ match = (*linenum <= lines_[cur].linenum && *linenum > lines_[cur - 1].linenum); } } /* if we have a match, return it; otherwise, keep searching */ if (match) { /* * if this is a non-exact match, update the caller's line * number with the actual line number we found */ *linenum = lines_[cur].linenum; /* return our code address */ return lines_[cur].code_addr; } else if (*linenum > lines_[cur].linenum) { /* we need to go higher */ lo = (cur == lo ? cur + 1 : cur); } else { /* we need to go lower */ hi = (cur == hi ? hi - 1 : cur); } } /* failure */ return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmsrcf.h��������������������������������������������������������������������������0000664�0000000�0000000�00000010335�12650170723�0015331�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmsrcf.h - T3 VM source file table Function Maintains a list of the source file records found in the "SRCF" block in the image file Notes Modified 12/01/99 MJRoberts - Creation */ #ifndef VMSRCF_H #define VMSRCF_H #include "t3std.h" /* * Source file line record */ struct CVmSrcfLine { /* line number in source file */ ulong linenum; /* code address of generated code for source line */ ulong code_addr; }; /* * Source file entry */ class CVmSrcfEntry { public: CVmSrcfEntry(int orig_index, int is_orig, size_t name_len) { /* no source lines yet */ lines_ = 0; lines_cnt_ = lines_alo_ = 0; /* set the original index */ orig_index_ = orig_index; /* remember whether this is the master entry */ is_orig_ = is_orig; /* allocate space for our name buffer */ name_buf_ = (char *)t3malloc(name_len + 1); } ~CVmSrcfEntry() { /* delete our line records, if we have any */ if (lines_ != 0) t3free(lines_); /* delete our name buffer */ t3free(name_buf_); } /* determine if the entry is the master record */ int is_master() const { return is_orig_; } /* get my name */ const char *get_name() const { return name_buf_; } /* get a pointer to my name buffer */ char *get_name_buf() { return name_buf_; } /* * allocate space for source line entries - this can be used to * pre-allocate space if the number of line entries is known in * advance */ void alloc_line_records(ulong cnt); /* * Add a source line entry. Line entries must be added in ascending * order of line number. */ void add_line_record(ulong linenum, ulong code_addr); /* * Find the code address for a given source line. Returns zero if * we are unsuccessful, non-zero if successful (an executable line * can never have a source address equal to zero, since if a method * is at such a low address at all, its header would take up the * first few bytes, putting the first executable instruction at a * non-zero address). * * If 'exact' is true, we'll fail unless we find an exact match; * otherwise, we'll return the code address for the next executable * line after the given line, or the last executable line in the * file if there are no more executable lines after the given line. * * If we find a non-exact match, we'll update *linenum to give the * actual line number where we found the executable line. */ ulong find_src_addr(ulong *linenum, int exact); private: /* name buffer */ char *name_buf_; /* index of original entry for this filename */ int orig_index_; /* flag: I'm the original entry */ uint is_orig_ : 1; /* * Line records array. For simplicity, this is simply a single * array. This could be a little constraining for 16-bit machines, * but even on a 16-bit machine, this will accommodate 8000 records, * which should be enough for even fairly large source files. */ CVmSrcfLine *lines_; /* number of line records actually in use */ ulong lines_cnt_; /* number of lines allocated */ ulong lines_alo_; }; /* * Source file table */ class CVmSrcfTable { public: CVmSrcfTable(); ~CVmSrcfTable(); /* clear the table */ void clear(); /* add a new entry */ CVmSrcfEntry *add_entry(int orig_index, size_t name_len); /* get an entry given the 0-based index */ CVmSrcfEntry *get_entry(size_t idx) const { return (idx < list_used_ ? list_[idx] : 0); } /* get the number of entries */ size_t get_count() const { return list_used_; } private: /* array of entries */ CVmSrcfEntry **list_; /* number of entries currently in use in the list */ size_t list_used_; /* number of entries allocated in the list */ size_t list_alloc_; }; #endif /* VMSRCF_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmstack.cpp�����������������������������������������������������������������������0000664�0000000�0000000�00000002734�12650170723�0016040�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMSTACK.CPP,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmstack.cpp - VM stack implementation Function Notes Modified 10/28/98 MJRoberts - Creation */ #include "t3std.h" #include "vmtype.h" #include "vmstack.h" #include "vmfile.h" /* * allocate the stack */ CVmStack::CVmStack(size_t max_depth, size_t reserve) { /* * Allocate the array of stack elements. Allocate the requested * maximum depth plus the requested reserve space. Overallocate by a * few elements to leave ourselves a little buffer against mild * overages - for the most part, we count on the compiler to check for * proper stack usage at entry to each function, but intrinsics * sometimes push a few elements without checking. */ arr_ = (vm_val_t *)t3malloc((max_depth + reserve + 25) * sizeof(arr_[0])); /* remember the maximum depth and the reserve depth */ max_depth_ = max_depth; reserve_depth_ = reserve; /* the reserve is not yet in use */ reserve_in_use_ = FALSE; /* initialize the stack pointer */ init(); } /* * delete the stack */ CVmStack::~CVmStack() { /* delete the stack element array */ t3free(arr_); } ������������������������������������qtads-2.1.7/tads3/vmstack.h�������������������������������������������������������������������������0000664�0000000�0000000�00000026075�12650170723�0015511�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMSTACK.H,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmstack.h - VM stack manager Function Notes Modified 10/28/98 MJRoberts - Creation */ #ifndef VMSTACK_H #define VMSTACK_H #include "vmtype.h" #include "vmerr.h" #include "vmerrnum.h" /* ------------------------------------------------------------------------ */ /* * The stack pointer and a few other VM registers are critical to * performance, since they're accessed so frequently. If we're compiling * in VMGLOB_VARS or VMGLOB_STRUCT mode, pull out the key registers as * global static variables; this results in smaller and faster code on most * machines than when it's part of the CVmStack structure. Don't do this * when compiling in other global modes, since the local implementation * must have a reason to keep everything in parameters (probably a shared * memory situation, such as threads or a shared library). */ #if defined(VMGLOB_VARS) || defined(VMGLOB_STRUCT) /* global variable mode - define registers as separate globals */ # define VM_IF_REGS_IN_STRUCT(decl) # define VM_IF_REGS_IN_GLOBALS(decl) decl # define VM_REG_ACCESS static # define VM_REG_CONST #else /* structure mode - define registers as part of CVmStack/CVmRun */ # define VM_IF_REGS_IN_STRUCT(decl) decl # define VM_IF_REGS_IN_GLOBALS(decl) # define VM_REG_ACCESS # define VM_REG_CONST const #endif /* declare the stack pointer register as an extern global, if appropriate */ VM_IF_REGS_IN_GLOBALS(extern vm_val_t *sp_;) /* ------------------------------------------------------------------------ */ /* * VM stack interface */ class CVmStack { public: /* * Allocate the stack. The maximum depth that the stack can achieve * is fixed when the stack is created. */ CVmStack(size_t max_depth, size_t reserve_depth); /* initialize */ void init() { /* start the stack pointer at the first element */ sp_ = arr_; } /* delete the stack */ ~CVmStack(); /* * get the current stack depth - this gives the number of active * elements in the stack */ size_t get_depth() const { return sp_ - arr_; } /* * Translate between pointer and index values. An index value is * simply an integer giving the index in the stack of the given * pointer; this value can be used, for example, for saving a stack * location persistently. * * We return zero for a null stack pointer; we always return * non-zero for a non-null stack pointer. */ ulong ptr_to_index(vm_val_t *p) const { /* if it's null, return index 0; otherwise, return a non-zero index */ return (p == 0 ? 0 : p - arr_ + 1); } vm_val_t *index_to_ptr(ulong idx) const { /* if the index is zero, it's null; otherwise, get the pointer */ return (idx == 0 ? 0 : arr_ + idx - 1); } /* * Get the depth relative to a given frame pointer. This returns * the number of items on the stack beyond the given pointer. If * the given frame pointer is beyond the current stack pointer * (i.e., values have been popped since the frame pointer equalled * the stack pointer), the return value will be negative. */ VM_REG_ACCESS int get_depth_rel(vm_val_t *fp) VM_REG_CONST { return sp_ - fp; } /* get the current stack pointer */ VM_REG_ACCESS vm_val_t *get_sp() VM_REG_CONST { return sp_; } /* * Set the current stack pointer. The pointer must always be a * value previously returned by get_sp(). */ VM_REG_ACCESS void set_sp(vm_val_t *p) { sp_ = p; } /* * Get an element relative to a frame pointer (a frame pointer is a * stack position that was previously obtained via get_sp() and * stored by the caller). The offset is negative for a value pushed * prior to the frame pointer, zero for the value at the frame * pointer, or positive for values pushed after the frame pointer. */ static vm_val_t *get_from_frame(vm_val_t *fp, int i) { return (fp + i - 1); } /* push a value */ VM_REG_ACCESS void push(const vm_val_t *val) { *sp_++ = *val; } /* * Push an element, returning a pointer to the element; this can be * used to fill in a new stack element directly, without copying a * value. The new element is not filled in yet on return, so the * caller should immediately fill in the element with a valid value. */ VM_REG_ACCESS vm_val_t *push() { return sp_++; } /* * Push a number of elements: this allocates a block of contiguous * stack elements that the caller can fill in individually. The stack * elements are uninitialized, so the caller must set the values * immediately on return. A pointer to the first pushed element is * returned; subsequent elements are addressed at the return value * plus 1, plus 2, and so on. */ VM_REG_ACCESS vm_val_t *push(unsigned int n) { /* remember the current stack pointer, which is what we return */ vm_val_t *ret = sp_; /* allocate the elements */ sp_ += n; /* return the base of the allocated block */ return ret; } /* push, checking space */ void push_check(const vm_val_t *val) { *push_check() = *val; } vm_val_t *push_check() { check_throw(1); return push(); } vm_val_t *push_check(unsigned int n) { check_throw(n); return push(n); } /* * Insert space for 'num' slots at index 'idx'. If 'idx' is zero, this * is the same as pushing 'num' slots. Returns a pointer to the first * slot allocated. */ vm_val_t *insert(size_t idx, size_t num) { /* make sure there's room */ check_space(num); /* add the space */ sp_ += num; /* if idx is non-zero, move the idx slots by num to make room */ if (idx != 0) memmove(sp_ - idx, sp_ - idx - num, idx*sizeof(*sp_)); /* return the start of the inserted block */ return sp_ - idx - num; } /* * Get an element. Elements are numbered from zero to (depth - 1). * Element number zero is the item most recently pushed onto the * stack; element (depth-1) is the oldest element on the stack. */ VM_REG_ACCESS vm_val_t *get(size_t i) VM_REG_CONST { return (sp_ - i - 1); } /* pop the top element off the stack */ VM_REG_ACCESS void pop(vm_val_t *val) { *val = *--sp_; } /* discard the top element */ VM_REG_ACCESS void discard() { --sp_; } /* discard a given number of elements from the top of the stack */ VM_REG_ACCESS void discard(int n) { sp_ -= n; } /* * Probe the stack for a given allocation. Returns true if the given * number of slots are available, false if not. Does NOT actually * allocate the space; merely checks for availability. * * Compilers are expected to produce function headers that check for * the maximum amount of stack space needed locally in the function on * entry, which allows us to check once at the start of the function * for available stack space, relieving us of the need to check for * available space in every push operation. * * Returns true if the required amount of space is available, false if * not. * * (NB: 'slots' really should be a size_t, as it's not meaningful to * reserve negative space. However, a compiler bug in 3.0.17, * 3.0.17.1, and 3.0.18 caused the compiler to generate the wrong * reservation sizes in headers; affected code occasionally makes * OPC_MAKELSTPAR compute a negative value for its size request here. * Making 'slots' signed makes the result of (get_depth() + <negative>) * less than the current depth, as it should be, whereas a size_t type * makes it yield a huge positive value on some systems, which looks * like a stack overflow. These faulty .t3 files are generally benign * despite the stack size miscalculation, because they're usually not * so far off that we'd actually blow past the real memory limits of * the stack, thanks to the exception-handling reserve. In other * words, we'd usually catch an actual stack overflow in a faulty .t3 * before it crashed the interpreter. So it's desirable to be * compatible with such files.) */ int check_space(int nslots) const { return (get_depth() + nslots <= max_depth_); } /* check space for 'nslots' new slots, throwing an error on overflow */ void check_throw(int nslots) const { if (!check_space(nslots)) err_throw(VMERR_STACK_OVERFLOW); } /* * Release the reserve. Debuggers can use this to allow manual * recovery from stack overflows, by making some extra stack * temporarily available for the debugger's use in handling the * overflow. This releases the reserve, if available, that was * specified when the stack was allocated. Returns true if reserve * space is available for release, false if not. */ int release_reserve() { /* if the reserve is already in use, we can't release it again */ if (reserve_in_use_) return FALSE; /* add the reserve space to the maximum stack depth */ max_depth_ += reserve_depth_; /* note that the reserve has been released */ reserve_in_use_ = TRUE; /* indicate that we successfully released the reserve */ return TRUE; } /* * Recover the reserve. If the debugger releases the reserve to handle * a stack overflow, it can call this once the situation has been dealt * with to take the reserve back out of play, so that the debugger can * deal with any future overflow in the same manner. */ void recover_reserve() { /* if the reserve is in use, put it back in reserve */ if (reserve_in_use_) { /* remove the reserve from the stack */ max_depth_ -= reserve_depth_; /* mark the reserve as available again */ reserve_in_use_ = FALSE; } } private: /* the array of value holders making up the stack */ vm_val_t *arr_; /* * Next available stack slot - the stack pointer starts out pointing at * arr_[0], and is incremented after storing each element. This is * defined as a member variable only if we're not defining it as a * separate static global. */ VM_IF_REGS_IN_STRUCT(vm_val_t *sp_;) /* maximum depth that the stack is capable of holding */ size_t max_depth_; /* extra reserve space */ size_t reserve_depth_; /* flag: the reserve has been released for VM use */ int reserve_in_use_; }; #endif /* VMSTACK_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmstr.cpp�������������������������������������������������������������������������0000664�0000000�0000000�00000411100�12650170723�0015532�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMSTR.CPP,v 1.3 1999/05/17 02:52:28 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmstr.cpp - VM string metaclass implementation Function Notes Modified 10/28/98 MJRoberts - Creation */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <limits.h> #include <stdarg.h> #include "t3std.h" #include "vmmcreg.h" #include "vmobj.h" #include "vmstr.h" #include "utf8.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmfile.h" #include "vmstack.h" #include "vmpool.h" #include "vmmeta.h" #include "vmrun.h" #include "vmbif.h" #include "vmpredef.h" #include "vmlst.h" #include "vmuni.h" #include "vmcset.h" #include "vmbytarr.h" #include "vmstrbuf.h" #include "charmap.h" #include "vmnet.h" #include "vmstrref.h" #include "sha2.h" #include "md5.h" #include "vmpack.h" #include "vmdatasrc.h" #include "vmpat.h" #include "vmregex.h" #include "vmbiftad.h" #include "vmfindrep.h" /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassString metaclass_reg_obj; CVmMetaclass *CVmObjString::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (*CVmObjString::func_table_[])(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) = { &CVmObjString::getp_undef, /* 0 */ &CVmObjString::getp_len, /* 1 */ &CVmObjString::getp_substr, /* 2 */ &CVmObjString::getp_upper, /* 3 */ &CVmObjString::getp_lower, /* 4 */ &CVmObjString::getp_find, /* 5 */ &CVmObjString::getp_to_uni, /* 6 */ &CVmObjString::getp_htmlify, /* 7 */ &CVmObjString::getp_starts_with, /* 8 */ &CVmObjString::getp_ends_with, /* 9 */ &CVmObjString::getp_to_byte_array, /* 10 */ &CVmObjString::getp_replace, /* 11 */ &CVmObjString::getp_splice, /* 12 */ &CVmObjString::getp_split, /* 13 */ &CVmObjString::getp_specialsToHtml, /* 14 */ &CVmObjString::getp_specialsToText, /* 15 */ &CVmObjString::getp_urlEncode, /* 16 */ &CVmObjString::getp_urlDecode, /* 17 */ &CVmObjString::getp_sha256, /* 18 */ &CVmObjString::getp_md5, /* 19 */ &CVmObjString::getp_packBytes, /* 20 */ &CVmObjString::getp_unpackBytes, /* 21 */ &CVmObjString::getp_toTitleCase, /* 22 */ &CVmObjString::getp_toFoldedCase, /* 23 */ &CVmObjString::getp_compareTo, /* 24 */ &CVmObjString::getp_compareIgnoreCase, /* 25 */ &CVmObjString::getp_findLast, /* 26 */ &CVmObjString::getp_findAll, /* 27 */ &CVmObjString::getp_match /* 28 */ }; /* static property indices */ const int PROPIDX_packBytes = 20; /* ------------------------------------------------------------------------ */ /* * Static creation methods */ /* create dynamically using stack arguments */ vm_obj_id_t CVmObjString::create_from_stack(VMG_ const uchar **, uint) { /* dynamic string construction is not currently supported */ err_throw(VMERR_BAD_DYNAMIC_NEW); /* the compiler doesn't know we won't make it here */ AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* create a string with no initial contents */ vm_obj_id_t CVmObjString::create(VMG_ int in_root_set) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjString(); return id; } /* create with a given buffer size */ vm_obj_id_t CVmObjString::create(VMG_ int in_root_set, size_t byte_size) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjString(vmg_ byte_size); return id; } /* create from a constant UTF-8 string */ vm_obj_id_t CVmObjString::create(VMG_ int in_root_set, const char *str, size_t bytelen) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjString(vmg_ str, bytelen); return id; } /* create from a wide-character string */ vm_obj_id_t CVmObjString::create(VMG_ int in_root_set, const wchar_t *str, size_t charlen) { /* get the utf8 byte length of the string */ size_t i, bytelen; const wchar_t *src; for (src = str, i = 0, bytelen = 0 ; i < charlen ; ++src, ++i) bytelen += utf8_ptr::s_wchar_size(*src); /* allocate space */ vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjString(vmg_ bytelen); CVmObjString *s = (CVmObjString *)vm_objp(vmg_ id); /* build the string */ utf8_ptr dst(s->cons_get_buf()); for (src = str, i = 0 ; i < charlen ; ++src, ++i) dst.setch(*src); /* return the new string object ID */ return id; } /* create from the given character set */ vm_obj_id_t CVmObjString::create(VMG_ int in_root_set, const char *src, size_t srclen, CCharmapToUni *cmap) { /* figure the size needed for the conversion */ size_t ulen = cmap->map_str(0, 0, src, srclen); /* create a string of the mapped length */ vm_obj_id_t ret = create(vmg_ in_root_set, ulen); CVmObjString *retstr = (CVmObjString *)vm_objp(vmg_ ret); /* map into the string buffer */ cmap->map_str(retstr->cons_get_buf(), ulen, src, srclen); /* return the new string */ return ret; } /* create from a byte stream, interpreting the bytes as Latin-1 characters */ vm_obj_id_t CVmObjString::create_latin1(VMG_ int in_root_set, CVmDataSource *src) { /* scan the memory stream to determine the string byte length */ long len = copy_latin1_to_string(0, 0, src); /* make sure the length is within the acceptable range */ if (len > 65535) err_throw(VMERR_STR_TOO_LONG); /* allocate the string object */ vm_obj_id_t id = create(vmg_ FALSE, len); CVmObjString *str = (CVmObjString *)vm_objp(vmg_ id); /* copy the stream to the string */ copy_latin1_to_string(str->cons_get_buf(), len, src); /* return the new object ID */ return id; } /* ------------------------------------------------------------------------ */ /* * Copy a byte stream to a string, treating each byte of the source stream * as a Latin-1 character. * * If the output buffer is null or is too short, we'll just count up the * length without storing anything. Returns the total number of bytes * needed to store the full source byte array. */ size_t CVmObjString::copy_latin1_to_string(char *str, size_t len, CVmDataSource *src) { /* we haven't copied any bytes out yet */ size_t tot = 0; /* seek to the start of the source */ src->seek(0, OSFSK_SET); /* keep going until we reach EOF */ for (;;) { /* read the next chunk of bytes from the source */ char buf[1024]; int cur = src->readc(buf, sizeof(buf)); /* if there's nothing left, we're done */ if (cur == 0) return tot; /* scan the buffer */ for (char *p = buf ; cur != 0 ; ++p, --cur) { /* * if this character is from 0 to 127, store one byte; * otherwise store it as two bytes in UTF-8 format */ unsigned char c = *(unsigned char *)p; if (c <= 127) { /* 0..127 -> single byte UTF-8 character */ tot += 1; if (str != 0 && tot <= len) *str++ = c; } else { /* 128.255 -> two-byte UTF-8 character */ tot += 2; if (str != 0 && tot <= len) { *str++ = (char)(0xC0 | ((c >> 6) & 0x1F)); *str++ = (char)(0x80 | (c & 0x3F)); } } } } } /* ------------------------------------------------------------------------ */ /* * Constructors */ /* * create a string object with a given buffer size */ CVmObjString::CVmObjString(VMG_ size_t len) { /* * the length is limited to an unsigned 16-bit value (NB: it really is * 65535 on ALL PLATFORMS - this is a portable limit imposed by the * portable storage format, not a local platform limit) */ if (len > 65535) { ext_ = 0; err_throw(VMERR_STR_TOO_LONG); } /* * allocate space for the buffer plus the length prefix in the * variable heap */ ext_ = (char *)G_mem->get_var_heap()->alloc_mem(len + VMB_LEN, this); /* set the length */ vmb_put_len(ext_, len); } /* * create a string object from a given UTF8 string constant */ CVmObjString::CVmObjString(VMG_ const char *str, size_t len) { /* check for the length limit */ if (len > 65535) { ext_ = 0; err_throw(VMERR_STR_TOO_LONG); } /* * allocate space for the string plus the length prefix in the * variable heap */ ext_ = (char *)G_mem->get_var_heap()->alloc_mem(len + VMB_LEN, this); /* * store the length prefix in portable format (so that we can easily * write our contents to a saved state file) */ vmb_put_len(ext_, len); /* copy the string's bytes */ memcpy(ext_ + VMB_LEN, str, len); } /* ------------------------------------------------------------------------ */ /* * Ensure space for construction */ char *CVmObjString::cons_ensure_space(VMG_ char *ptr, size_t len, size_t margin) { /* figure the offset of the pointer into the current buffer */ int ofs = ptr - (ext_ + VMB_LEN); /* calculate the needed size: this is the offset plus the added length */ size_t need = ofs + len; /* if we already have enough space, we're done */ if (vmb_get_len(ext_) >= need) return ptr; /* expand to the needed size plus the margin */ size_t newlen = need + margin; ext_ = (char *)G_mem->get_var_heap()->realloc_mem( newlen + VMB_LEN, ext_, this); /* set the new buffer size */ vmb_put_len(ext_, newlen); /* return the pointer to the new buffer at the original offset */ return ext_ + VMB_LEN + ofs; } /* * Append a string during construction */ char *CVmObjString::cons_append(VMG_ char *ptr, const char *addstr, size_t addlen, size_t margin) { /* ensure we have space for the added string */ ptr = cons_ensure_space(vmg_ ptr, addlen, margin); /* add the new string onto the end of the buffer */ memcpy(ptr, addstr, addlen); /* return a pointer to the end of the added data */ return ptr + addlen; } /* * Append a character during construction */ char *CVmObjString::cons_append(VMG_ char *ptr, wchar_t ch, size_t margin) { /* encode the character as utf8 */ char buf[3]; size_t len = utf8_ptr::s_putch(buf, ch); /* append the utf8 string for the character */ return cons_append(vmg_ ptr, buf, len, margin); } /* * Shrink the buffer to the actual final size */ void CVmObjString::cons_shrink_buffer(VMG_ char *ptr) { /* figure the offset of the pointer into the current buffer */ size_t siz = (size_t)(ptr - (ext_ + VMB_LEN)); /* * If the savings are substantial enough, reallocate. If the size is * unchanged, or it's within a reasonable margin of the target size, do * nothing. Reallocation isn't free (we have to find memory and make a * copy of the current buffer), so it's more efficient to waste a * little memory if the savings are substantial. */ if (vmb_get_len(ext_) - siz >= 256) { /* reallocate at the new size */ ext_ = (char *)G_mem->get_var_heap()->realloc_mem( siz + VMB_LEN, ext_, this); } /* write the final length */ vmb_put_len(ext_, siz); } /* ------------------------------------------------------------------------ */ /* * receive notification of deletion */ void CVmObjString::notify_delete(VMG_ int in_root_set) { /* free our extension */ if (ext_ != 0 && !in_root_set) G_mem->get_var_heap()->free_mem(ext_); } /* ------------------------------------------------------------------------ */ /* * call a static property */ int CVmObjString::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* get the function table index */ int idx = G_meta_table->prop_to_vector_idx( metaclass_reg_->get_reg_idx(), prop); /* check for static methods */ switch (idx) { case PROPIDX_packBytes: return static_getp_packBytes(vmg_ result, argc); default: /* inherit the default handling */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } } /* ------------------------------------------------------------------------ */ /* * Set a property. Strings have no settable properties, so simply * signal an error indicating that the set-prop call is invalid. */ void CVmObjString::set_prop(VMG_ CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * Save the object to a file */ void CVmObjString::save_to_file(VMG_ CVmFile *fp) { /* get our length */ size_t len = vmb_get_len(ext_); /* write the length prefix and the string */ fp->write_bytes(ext_, len + VMB_LEN); } /* * Restore the object from a file */ void CVmObjString::restore_from_file(VMG_ vm_obj_id_t, CVmFile *fp, CVmObjFixup *) { /* read the length prefix */ size_t len = fp->read_uint2(); /* free any existing extension */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* * allocate our extension - make room for the length prefix plus the * bytes of the string */ ext_ = (char *)G_mem->get_var_heap()->alloc_mem(len + VMB_LEN, this); /* store our length prefix */ vmb_put_len(ext_, len); /* read the string */ fp->read_bytes(ext_ + VMB_LEN, len); } /* ------------------------------------------------------------------------ */ /* * Cast to integer. We parse the string as a numeric value, using the same * rules as toInteger(). */ long CVmObjString::cast_to_int(VMG0_) const { /* * parse the string as an integer in decimal format; this is an * explicit int cast, so don't allow BigNumber promotions */ vm_val_t val; parse_num_val(vmg_ &val, ext_ + VMB_LEN, vmb_get_len(ext_), 10, TRUE); /* return the integer value */ return val.val.intval; } /* * Cast to number. */ void CVmObjString::cast_to_num(VMG_ vm_val_t *val, vm_obj_id_t self) const { /* * Parse the string as a number in decimal format. We're allowed to * return whatever numeric type is needed to represent the value, so * allow BigNumber promotions if necessary. */ parse_num_val(vmg_ val, ext_ + VMB_LEN, vmb_get_len(ext_), 10, FALSE); } /* * Parse a string as an integer value */ void CVmObjString::parse_num_val(VMG_ vm_val_t *retval, const char *str, size_t len, int radix, int int_only) { utf8_ptr p; size_t rem; /* skip leading spaces */ for (p.set((char *)str), rem = len ; rem != 0 && t3_is_whitespace(p.getch()) ; p.inc(&rem)) ; /* presume it's positive */ int is_neg = FALSE; /* check for a leading + or - */ if (rem != 0) { if (p.getch() == '-') { /* note the sign and skip the character */ is_neg = TRUE; p.inc(&rem); } else if (p.getch() == '+') { /* skip the character */ p.inc(&rem); } } /* skip any additional spaces after the sign */ for ( ; rem != 0 && t3_is_whitespace(p.getch()) ; p.inc(&rem)) ; /* clear the accumulator */ unsigned long acc = 0; /* scan the digits */ for ( ; rem != 0 ; p.inc(&rem)) { /* get the next digit */ wchar_t ch = p.getch(); /* * If we're allowed to promote to BigNumber, and the radix is * decimal, check for '.' and 'E' (for exponent) notation. These * could indicate a floating point value. */ if (radix == 10 && !int_only && (ch == '.' || ch == 'e' || ch == 'E')) { /* presume we're going to promote to BigNumber */ int promote = TRUE; /* * If it's an exponent, parse further to make sure it looks * like a valid exponent: we can have an optional + or - sign, * then need at least one digit. */ if (ch == 'e' || ch == 'E') { /* set up a second pointer */ utf8_ptr p2(p); size_t rem2 = rem; /* skip the 'e' */ p2.inc(&rem); /* if there's a sign, skip it */ if (rem2 != 0 && (p2.getch() == '+' || p2.getch() == '-')) p2.inc(&rem2); /* we need a digit at this point, or it's not an exponent */ promote = (rem2 != 0 && is_digit(p2.getch())); } /* if we're promoting after all, go do the promotion */ if (promote) { /* re-parse it as a BigNumber value */ retval->set_obj(CVmObjBigNum::create_radix( vmg_ FALSE, str, len, radix)); /* return this value */ return; } } /* get the integer value of the digit, treating A-Z as digits 10-35 */ int dig; if (ch >= '0' && ch <= '9') dig = (int)(ch - '0'); else if (ch >= 'a' && ch <= 'z') dig = (int)(ch - 'a' + 10); else if (ch >= 'A' && ch <= 'Z') dig = (int)(ch - 'A' + 10); else break; /* if it's outside the radix limit, it's not a digit after all */ if (dig >= radix) break; /* * Make sure we're not going to overflow. If the base is 2, 8, or * 16, and there wasn't a "-" sign, allow the value to go up to the * unsigned 32-bit limit, 0xffffffff. Otherwise, limit it to * 32-bit signed, -0x8000000 to +0x7ffffff. */ unsigned long limit = (!is_neg && (radix == 2 || radix == 8 || radix == 16)) ? 0xffffffff : is_neg ? 0x80000000 : 0x7fffffff; /* shift the accumulator by the radix, checking for overflow */ if (acc > limit/radix) goto overflow; acc *= radix; /* add the digit, checking for overflow */ if (acc > limit - dig) goto overflow; acc += dig; } /* apply the sign, if appropriate, and set the return value */ retval->set_int(is_neg && acc <= 0x7FFFFFFF ? -(long)acc : (long)acc); return; overflow: /* * We overflowed the plain integer type. If we're allowed to promote * to a BigNumber, re-parse the value as a BigNumber. If not, it's an * error. */ if (int_only) { /* we can't promote to BigNumber - throw an overflow error */ err_throw(VMERR_NUM_OVERFLOW); } else { /* re-parse it as a BigNumber value */ retval->set_obj(CVmObjBigNum::create_radix( vmg_ FALSE, str, len, radix)); } } /* ------------------------------------------------------------------------ */ /* * Add a value to this string */ int CVmObjString::add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { /* set up a 'self' value holder */ vm_val_t selfval; selfval.set_obj(self); /* * Use the generic string adder, using my extension as the constant * string. We store our extension in the general string format * required by the static adder. */ add_to_str(vmg_ result, &selfval, val); /* handled */ return TRUE; } /* * Static string adder. This creates a new string object that results * from appending the given value to the given string constant. This is * defined statically so that this same code can be shared for adding to * constant pool strings and adding to CVmObjString objects. * * 'strval' must point to a constant string. The first two bytes of the * string are stored in portable UINT2 format and give the length in * bytes of the string, not including the length prefix; immediately * following the length prefix are the bytes of the string. * * Note that we *always* create a new object to hold the result, even if * the new string is identical to the first, so that we consistently * return a distinct reference from the original. */ void CVmObjString::add_to_str(VMG_ vm_val_t *result, const vm_val_t *self, const vm_val_t *val) { const char *strval1, *strval2; char buf[128]; vm_obj_id_t obj; size_t len1, len2; CVmObjString *objptr; vm_val_t new_obj2; /* * Get the string buffer pointers. The left value is already a string, * or we wouldn't be here. The right value can be anything, though, so * we need to apply an implicit string conversion if it's another type. */ strval1 = self->get_as_string(vmg0_); strval2 = cvt_to_str(vmg_ &new_obj2, buf, sizeof(buf), val, 10, 0); /* get the lengths of the two strings */ len1 = vmb_get_len(strval1); len2 = vmb_get_len(strval2); /* * If the right-hand value is zero length, or it's nil, simply return * the left-hand value. If the left-hand value is zero length AND the * right-hand value is already a string (constant or object), return * the right-hand value unchanged. Otherwise, we actually need to * build a whole new string with the concatenated texts. */ if (len2 == 0 || val->typ == VM_NIL) { /* we're appending nothing to the string; just return 'self' */ *result = *self; } else if (len1 == 0 && val->get_as_string(vmg0_) != 0) { /* * we're appending the right value to an empty string, AND the * right value is already a string itself, so we can simply return * the right value unchanged */ *result = *val; } else { /* * push the new string (if any) and self, to protect the two * strings from garbage collection */ G_stk->push(self); G_stk->push(&new_obj2); /* create a new string object to hold the result */ obj = create(vmg_ FALSE, len1 + len2); objptr = (CVmObjString *)vm_objp(vmg_ obj); /* copy the two strings into the new object's string buffer */ objptr->copy_into_str(0, strval1 + VMB_LEN, len1); objptr->copy_into_str(len1, strval2 + VMB_LEN, len2); /* we're done with the garbage collection protection */ G_stk->discard(2); /* return the new object in the result */ result->set_obj(obj); } } /* ------------------------------------------------------------------------ */ /* * Allocate a string buffer large enough to hold a given value. We'll * use the provided buffer if possible. * * If the provided buffer is null or is not large enough, we'll allocate * a new string object with a large enough buffer to hold the value, and * return the object's extension as the buffer. This object will never * be referenced by anyone, so it will be deleted at the next garbage * collection. * * The buffer size and requested size are in bytes. */ char *CVmObjString::alloc_str_buf(VMG_ vm_val_t *new_obj, char *buf, size_t buf_size, size_t required_size) { /* if the provided buffer is large enough, use it */ if (buf != 0 && buf_size >= required_size) { /* there's no new object */ new_obj->set_nil(); /* return the buffer */ return buf; } /* allocate a new string object */ new_obj->set_obj(CVmObjString::create(vmg_ FALSE, required_size)); /* return the new object's full string buffer from the length prefix */ return ((CVmObjString *)vm_objp(vmg_ new_obj->val.obj))->ext_; } /* ------------------------------------------------------------------------ */ /* * Try converting a value to a string via reflection services, imported * from the bytecode program. Failing that, use a boilerplate format. */ const char *CVmObjString::reflect_to_str( VMG_ vm_val_t *new_str, char *result_buf, size_t result_buf_size, const vm_val_t *val, const char *fmt, ...) { /* look for an exported reflectionServices object from the byteocde */ vm_val_t refl; refl.set_obj(G_predef->reflection_services); vm_prop_id_t valToSym = G_predef->reflection_valToSymbol; if (refl.val.obj != VM_INVALID_OBJ && valToSym != VM_INVALID_PROP) { /* invoke reflectionServices.valToSymbol(val) */ G_stk->push(val); G_interpreter->get_prop(vmg_ 0, &refl, valToSym, &refl, 1, 0); /* if we got a string, return it */ const char *str = G_interpreter->get_r0()->get_as_string(vmg0_); if (str != 0) { /* set the return value */ *new_str = *G_interpreter->get_r0(); /* return the string buffer */ return str; } } /* * No reflection services - fall back on the boilerplate format. Start * by calculating the amount of space we need for the formatted string, * adding a byte for the trailing null. */ va_list args; va_start(args, fmt); size_t need = t3vsprintf(0, 0, fmt, args) + 1; va_end(args); /* allocate space, including room for the length prefix */ result_buf = alloc_str_buf( vmg_ new_str, result_buf, result_buf_size, need + VMB_LEN); /* do the formatting */ va_start(args, fmt); t3vsprintf(result_buf + VMB_LEN, need, fmt, args); va_end(args); /* set the length prefix */ vmb_put_len(result_buf, need); /* return the buffer */ return result_buf; } /* ------------------------------------------------------------------------ */ /* * Convert a value to a string */ const char *CVmObjString::cvt_to_str(VMG_ vm_val_t *new_str, char *result_buf, size_t result_buf_size, const vm_val_t *val, int radix, int flags) { /* presume we won't need to create a new string object */ new_str->set_nil(); /* check the type of the value */ switch(val->typ) { case VM_SSTRING: /* it's a string constant - no conversion is necessary */ return G_const_pool->get_ptr(val->val.ofs); case VM_OBJ: /* it's an object - ask it for its string representation */ return vm_objp(vmg_ val->val.obj)->explicit_to_string( vmg_ val->val.obj, new_str, radix, flags); case VM_INT: /* * It's a number - convert it to a string. Use the provided result * buffer if possible, but make sure we have room for the number. * The unicode values we're storing are in the ascii range, so we * only need one byte per character. The longest buffer we'd need, * then, is 33 bytes, for a conversion to a bit string (i.e., base * 2: 32 1s or 0s, plus a sign). The conversion also needs two * bytes for the length prefix; add a few extra bytes as insurance * against future algorithm changes that might need more padding. */ result_buf = alloc_str_buf(vmg_ new_str, result_buf, result_buf_size, 40); /* generate the string */ return cvt_int_to_str(result_buf, 40, val->val.intval, radix, flags); case VM_NIL: /* nil - use the literal string "nil" */ return "\003\000nil"; case VM_TRUE: /* true - use the literal string "true" */ return "\004\000true"; case VM_LIST: return CVmObjList::list_to_string(vmg_ new_str, val, radix, flags); case VM_PROP: return reflect_to_str(vmg_ new_str, result_buf, result_buf_size, val, "property#%d", (int)val->val.prop); case VM_FUNCPTR: return reflect_to_str(vmg_ new_str, result_buf, result_buf_size, val, "function#%lx", (long)val->val.ofs); case VM_ENUM: return reflect_to_str(vmg_ new_str, result_buf, result_buf_size, val, "enum#%ld", (long)val->val.enumval); case VM_BIFPTR: return reflect_to_str(vmg_ new_str, result_buf, result_buf_size, val, "builtin#%d.%d", (int)val->val.bifptr.set_idx, (int)val->val.bifptr.func_idx); default: /* other types cannot be added to a string */ err_throw(VMERR_NO_STR_CONV); /* we never really get here, but the compiler doesn't know that */ AFTER_ERR_THROW(return 0;) } } /* ------------------------------------------------------------------------ */ /* * Convert an integer to a string, storing the result in the given buffer * in portable string format (with length prefix). We accept any radix * from 2 to 36 (36 can be represented with "digits" 0-9A-Z). * * If the value is signed, a leading dash is included if the number is * negative. Otherwise we treat the original value as unsigned, which on * most hardware means we'll show the two's complement value. * * For efficiency, we store the number at the end of the buffer (this makes * it easy to generate the number, since we need to generate numerals in * reverse order). We return a pointer to the result, which may not start * at the beginning of the buffer. */ char *CVmObjString::cvt_int_to_str(char *buf, size_t buflen, int32_t inval, int radix, int flags) { int neg; uint32_t val; char *p; size_t len; /* start at the end of the buffer */ p = buf + buflen; /* * if it's negative, and we're converting to a signed representation, * use a leading minus sign plus the absolute value; otherwise, treat * the value as unsigned */ if (inval >= 0 || (flags & TOSTR_UNSIGNED) != 0) { /* * the value is non-negative, either because the signed * interpretation is non-negative or because we're using an * unsigned interpretation (in which case there's no such thing as * negative) */ neg = FALSE; /* use the value as-is */ val = (uint32_t)inval; } else { /* it's signed and negative - note that we need a minus sign */ neg = TRUE; /* use the positive value for the conversion */ val = (uint32_t)(-inval); } /* store numerals in reverse order */ do { char c; /* if we have no more room, throw an error */ if (p == buf) err_throw(VMERR_CONV_BUF_OVF); /* move on to the next available character in the buffer */ --p; /* figure the character representation of this numeral */ c = (char)(val % radix); if (c < 10) c += '0'; else c += 'A' - 10; /* store the numeral at the current location */ *p = c; /* divide the remaining number by the radix */ val /= radix; } while (val != 0); /* store the leading minus sign if necessary */ if (neg) { /* if we don't have room, throw an error */ if (p == buf) err_throw(VMERR_CONV_BUF_OVF); /* move to the next byte */ --p; /* store the minus sign */ *p = '-'; } /* calculate the length */ len = buflen - (p - buf); /* make sure we have room for the length prefix */ if (p < buf + 2) err_throw(VMERR_CONV_BUF_OVF); /* store the length prefix */ p -= 2; vmb_put_len(p, len); /* return the pointer to the start of the number */ return p; } /* ------------------------------------------------------------------------ */ /* * Check a value for equality */ int CVmObjString::equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int /*depth*/) const { /* if the other value is a reference to myself, we certainly match */ if (val->typ == VM_OBJ && val->val.obj == self) return TRUE; /* * use the constant string comparison routine, using our underlying * string as the constant string data */ return const_equals(vmg_ ext_, val); } /* * Constant string equality test */ int CVmObjString::const_equals(VMG_ const char *str, const vm_val_t *val) { const char *str2; size_t len; /* if the other value is a string buffer, compare to its contents */ if (val->typ == VM_OBJ && CVmObjStringBuffer::is_string_buffer_obj(vmg_ val->val.obj)) return ((CVmObjStringBuffer *)vm_objp(vmg_ val->val.obj)) ->equals_str(str); /* get the other value as a string */ str2 = val->get_as_string(vmg0_); /* if the object doesn't have an underlying string, we don't match */ if (str2 == 0) return FALSE; /* * if their lengths match, and the bytes match exactly, we have a * match; otherwise, they're not equal */ len = vmb_get_len(str); return (len == vmb_get_len(str2) && memcmp(str + VMB_LEN, str2 + VMB_LEN, len) == 0); } /* ------------------------------------------------------------------------ */ /* * Hash value */ uint CVmObjString::calc_hash(VMG_ vm_obj_id_t self, int /*depth*/) const { return const_calc_hash(ext_); } /* * Hash value calculation */ uint CVmObjString::const_calc_hash(const char *str) { size_t len; uint hash; utf8_ptr p; /* get and skip the length prefix */ len = vmb_get_len(str); str += VMB_LEN; /* scan the string and calculate the hash */ for (p.set((char *)str), hash = 0 ; len != 0 ; p.inc(&len)) hash += p.getch(); /* return the result */ return hash; } /* ------------------------------------------------------------------------ */ /* * Compare this string to another value */ int CVmObjString::compare_to(VMG_ vm_obj_id_t /*self*/, const vm_val_t *val) const { /* use the static string magnitude comparison routine */ return const_compare(vmg_ ext_, val); } /* * Compare a constant string value to another value. Returns a positive * number if the constant string is lexically greater than the other * value, a negative number if the constant string is lexically less * than the other value, or zero if the constant string is lexically * identical to the other value. * * The other value must be a string constant or an object with an * underlying string value. We'll throw an error for any other type of * value. */ int CVmObjString::const_compare(VMG_ const char *str1, const vm_val_t *val) { const char *str2; size_t len1, len2; /* if the other value is a string buffer, compare to its contents */ if (val->typ == VM_OBJ && CVmObjStringBuffer::is_string_buffer_obj(vmg_ val->val.obj)) { /* * ask the string buffer to do the comparison, but reverse the * sense of the comparison, since from its perspective the * comparison is in the opposite order */ return -((CVmObjStringBuffer *)vm_objp(vmg_ val->val.obj)) ->compare_str(str1); } /* get the other value as a string */ str2 = val->get_as_string(vmg0_); /* if it's not a string, we can't compare it */ if (str2 == 0) err_throw(VMERR_INVALID_COMPARISON); /* get the lengths of the two strings */ len1 = vmb_get_len(str1); len2 = vmb_get_len(str2); /* perform a lexical comparison and return the result */ return utf8_ptr::s_compare_to(str1 + VMB_LEN, len1, str2 + VMB_LEN, len2); } /* ------------------------------------------------------------------------ */ /* * Find a substring within a string */ const char *CVmObjString::find_substr(VMG_ const char *str, int32_t start_idx, const char *substr, size_t *idxp) { /* get the lengths */ size_t rem = vmb_get_len(str); size_t sublen = vmb_get_len(substr); /* set up utf8 pointer into the string */ utf8_ptr p((char *)str + 2); /* if the index is negative, it's from the end of the string */ start_idx += (start_idx < 0 ? (int)p.len(rem) : -1); /* skip to the starting index */ for (int32_t i = start_idx ; i > 0 && rem >= sublen ; --i, p.inc(&rem)) ; /* scan for the substring */ for (size_t char_ofs = 0 ; rem != 0 && rem >= sublen ; ++char_ofs, p.inc(&rem)) { /* check for a match */ if (memcmp(p.getptr(), substr + VMB_LEN, sublen) == 0) { /* it's a match - set the return index if they are interested */ if (idxp != 0) *idxp = char_ofs + start_idx; /* return the current pointer */ return p.getptr(); } } /* we didn't find it - so indicate by returning null */ return 0; } /* * Find a substring or pattern. 'basestr' is the entire string, and 'str' * is a pointer into the string giving the starting position for the * search; this can be the same as 'baseptr' to search from the very * beginning of the string, or to a character in the middle of the string. * * The returned 'match_idx' value is the character index relative to the * starting search position 'str'. If the match starts at the beginning of * 'str' then 'match_idx' is returned as 0. Note that this is relative to * the starting point 'str' rather than the overall string 'basestr'. */ const char *CVmObjString::find_substr( VMG_ const vm_val_t *strval, const char *basestr, const char *str, size_t len, const char *substr, CVmObjPattern *pat, int *match_idx, int *match_len) { /* search for the string or pattern */ if (substr != 0) { /* get the substring length and buffer pointer */ size_t sublen = vmb_get_len(substr); substr += VMB_LEN; /* search for the substring */ utf8_ptr p((char *)str); for (int i = 0 ; len >= sublen ; p.inc(&len), ++i) { /* check for a match */ if (memcmp(p.getptr(), substr, sublen) == 0) { /* got it - the match length is simply the substring length */ *match_idx = i; *match_len = sublen; return p.getptr(); } } } else if (pat != 0) { /* get the compiled pattern */ re_compiled_pattern *cpat = pat->get_pattern(vmg0_); /* save the last search source string */ G_bif_tads_globals->last_rex_str->val = *strval; G_bif_tads_globals->rex_searcher->clear_group_regs(); /* search for the pattern */ int idx = G_bif_tads_globals->rex_searcher->search_for_pattern( cpat, basestr, str, len, match_len); /* if we found the match, return it */ if (idx >= 0) { /* set the match index and length */ *match_idx = utf8_ptr::s_len(str, idx); return str + idx; } } /* no match */ *match_idx = -1; *match_len = 0; return 0; } /* * Find a substring, searching from the end of the string toward the * beginning. This works like find_substr(), but searchs backwards. The * match must end before the starting position 'str' - it can't overlap * this character. To search the entire string from the end, set 'str' to * the byte just after the last character of the string. * * As with find_substr(), we return with 'match_idx' set to the character * index of the start of the match relative to 'str'. This means that * 'match_idx' will be zero or negative on a successful match. (And the * only way it returns with zero is when the matched text is zero length, * because of the requirement that the match can't overlap the starting * character.) */ const char *CVmObjString::find_last_substr( VMG_ const vm_val_t *strval, const char *basestr, const char *str, size_t len, const char *substr, CVmObjPattern *pat, int *match_idx, int *match_len) { /* search for the string or pattern */ if (substr != 0) { /* get the substring length and buffer pointer */ size_t sublen = vmb_get_len(substr); substr += VMB_LEN; /* get the character length of the search string */ size_t subclen = utf8_ptr::s_len(substr, sublen); /* set up at the starting position */ utf8_ptr p((char *)str); /* * back up by the number of characters 'c' in the search string - * the match can't include the from_idx position, so we can't match * anything later in the string than 'c' characters before from_idx */ size_t c; for (c = subclen ; c != 0 && p.getptr() != basestr ; --c, p.dec()) ; /* if we couldn't back up far enough, there's no chance of a match */ if (c > 0) { *match_idx = -1; *match_len = 0; return 0; } /* search backwards for the string */ for (int i = -(int)subclen ; ; p.dec(), --i) { /* check for a match at the current position */ if (memcmp(p.getptr(), substr, sublen) == 0) { /* * got it - the match index is the negative of the * character offset from the match position to the search * starting position, and the match length is simply * substring byte length */ *match_idx = -(int)utf8_ptr::s_len( p.getptr(), str - p.getptr()); *match_len = sublen; return p.getptr(); } /* if we're at the start of the base string, we're done */ if (p.getptr() == basestr) break; } } else if (pat != 0) { /* get the compiled pattern */ re_compiled_pattern *cpat = pat->get_pattern(vmg0_); /* save the last search source string */ G_bif_tads_globals->last_rex_str->val = *strval; G_bif_tads_globals->rex_searcher->clear_group_regs(); /* search for the pattern */ int idx = G_bif_tads_globals->rex_searcher->search_back_for_pattern( cpat, basestr, str, len, match_len); /* if we found the match, return it */ if (idx >= 0) { /* set the match index and length */ *match_idx = -(int)utf8_ptr::s_len(str - idx, idx); return str + idx; } } /* no match */ *match_idx = -1; *match_len = 0; return 0; } /* ------------------------------------------------------------------------ */ /* * Evaluate a property */ int CVmObjString::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { vm_val_t self_val; /* use the constant evaluator */ self_val.set_obj(self); if (const_get_prop(vmg_ retval, &self_val, ext_, prop, source_obj, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from the base object class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * Evaluate a property of a constant string value */ int CVmObjString::const_get_prop(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, vm_prop_id_t prop, vm_obj_id_t *src_obj, uint *argc) { /* presume no source object */ *src_obj = VM_INVALID_OBJ; /* translate the property index to an index into our function table */ uint func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((*func_table_[func_idx])(vmg_ retval, self_val, str, argc)) return TRUE; /* * If this is a constant string (which is indicated by an invalid * 'self' object ID), try inheriting the default object * interpretation, passing the constant string placeholder object * for its type information. */ if (self_val->typ != VM_OBJ) { /* try going to CVmObject directly */ if (vm_objp(vmg_ G_predef->const_str_obj) ->CVmObject::get_prop(vmg_ prop, retval, G_predef->const_str_obj, src_obj, argc)) return TRUE; } /* not handled */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - get the length */ int CVmObjString::getp_len(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* set up a utf-8 pointer to the string's contents */ utf8_ptr p; p.set((char *)str + VMB_LEN); /* return the character length of the string */ retval->set_int(p.len(vmb_get_len(str))); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - extract a substring */ int CVmObjString::getp_substr(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *in_argc) { long start; long len = 0; size_t rem; utf8_ptr p; utf8_ptr start_p; size_t start_rem; size_t new_len; vm_obj_id_t obj; /* check arguments */ uint argc = (in_argc == 0 ? 0 : *in_argc); static CVmNativeCodeDesc desc(1, 1); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* pop the starting index */ start = CVmBif::pop_long_val(vmg0_); /* pop the length, if present */ if (argc >= 2) len = CVmBif::pop_long_val(vmg0_); /* push a self-reference to protect against GC */ G_stk->push(self_val); /* set up a utf8 pointer to traverse the string */ p.set((char *)str + VMB_LEN); /* get the byte length of the string */ rem = vmb_get_len(str); /* * Skip ahead to the starting index. If the index is positive, it's * an index from the start of the string; if it's negative, it's an * offset from the end of the string. */ if (start > 0) { /* * it's an index from the start - skip ahead by start-1 characters * (since a start value of 1 tells us to start at the first * character) */ for ( ; start > 1 && rem != 0 ; --start) p.inc(&rem); } else if (start < 0) { /* * It's an index from the end of the string: -1 tells us to start * at the last character, -2 at the second to last, and so on. * Move to the first byte past the end of the string, and work * backwards by the given number of characters. */ for (p.set((char *)str + VMB_LEN + rem), rem = 0 ; start < 0 && p.getptr() != (char *)str + VMB_LEN ; ++start) { /* move back one character */ p.dec(&rem); } } /* this is the starting position */ start_p = p; start_rem = rem; /* * if a length was specified, calculate the number of bytes in the * given length; otherwise, use the entire remainder of the string */ if (argc >= 2) { /* figure the positive or negative length */ if (len >= 0) { /* * Positive length - this specifies the number of characters to * keep starting at the starting index. Skip ahead by the * desired length to figure the end pointer. */ for ( ; len > 0 && rem != 0 ; --len) p.inc(&rem); } else { /* * Negative length - this specifies the number of characters to * remove from the end of the string. Move to the end of the * string, then skip back by |len| characters to find the end * pointer. */ for (p.set(p.getptr() + rem), rem = 0 ; len < 0 && rem < start_rem ; ++len) p.dec(&rem); } /* use the difference in lengths from the starting point to here */ new_len = start_rem - rem; } else { /* use the entire remainder of the string */ new_len = start_rem; } /* create the new string */ obj = CVmObjString::create(vmg_ FALSE, start_p.getptr(), new_len); /* return the new object */ retval->set_obj(obj); /* discard the GC protection references */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - toUpper */ int CVmObjString::getp_upper(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return gen_getp_case_conv(vmg_ retval, self_val, str, argc, &t3_to_upper); } /* * property evaluator - toLower */ int CVmObjString::getp_lower(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return gen_getp_case_conv(vmg_ retval, self_val, str, argc, &t3_to_lower); } /* * property evaluator - toTitleCase */ int CVmObjString::getp_toTitleCase( VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return gen_getp_case_conv(vmg_ retval, self_val, str, argc, &t3_to_title); } /* * property evaluator - toFoldedCase */ int CVmObjString::getp_toFoldedCase( VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return gen_getp_case_conv(vmg_ retval, self_val, str, argc, &t3_to_fold); } /* * General case converter for toUpper, toLower, toTitleCase, toFoldedCase */ int CVmObjString::gen_getp_case_conv( VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc, const wchar_t *(*conv)(wchar_t)) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get my length */ size_t srclen = vmb_get_len(str); /* leave the string on the stack as GC protection */ G_stk->push(self_val); /* * Scan the string to determine how long the result will be. The * result won't necessarily be the same length as the original: some * case conversions are 1:N characters, and even when they're 1:1, a * two-byte character in the original could turn into a three-byte * character in the result, and vice versa. */ size_t dstlen; utf8_ptr srcp, dstp; size_t rem; for (dstlen = 0, srcp.set((char *)str + VMB_LEN), rem = srclen ; rem != 0 ; srcp.inc(&rem)) { /* get the mapping for this character */ wchar_t ch = srcp.getch(); const wchar_t *u = conv(ch); /* add up the byte length of the mapping */ dstlen += (u != 0 ? utf8_ptr::s_wstr_size(u) : utf8_ptr::s_wchar_size(ch)); } /* allocate the result string */ vm_obj_id_t result_obj = CVmObjString::create(vmg_ FALSE, dstlen); /* get a pointer to the result buffer */ dstp.set(((CVmObjString *)vm_objp(vmg_ result_obj))->cons_get_buf()); /* write the string */ for (srcp.set((char *)str + VMB_LEN), rem = srclen ; rem != 0 ; srcp.inc(&rem)) { /* get the mapping for this character */ wchar_t ch = srcp.getch(); const wchar_t *u = conv(ch); /* put the character(s) */ if (u != 0) dstlen -= dstp.setwcharsz(u, dstlen); else dstp.setch(ch, &dstlen); } /* return the value */ retval->set_obj(result_obj); /* discard GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Common handler for find() and findLast() methods. 'dir' specifies the * search direction: 1 for forward (left to right), -1 for reverse (right * to left). */ template<int dir> inline int CVmObjString::find_common( VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ uint orig_argc = (argc != 0 ? *argc : 0); static CVmNativeCodeDesc desc(1, 1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the string length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* remember where the base string strings */ const char *basestr = str; /* retrieve the string or regex pattern to find */ vm_val_t *val2 = G_stk->get(0); const char *str2 = val2->get_as_string(vmg0_); CVmObjPattern *pat2 = 0; if (str2 != 0) { /* we have a simple string to find */ } else if (val2->typ == VM_OBJ && CVmObjPattern::is_pattern_obj(vmg_ val2->val.obj)) { /* it's a pattern */ pat2 = (CVmObjPattern *)vm_objp(vmg_ val2->val.obj); } else { /* we need a string or a pattern; other values are invalid */ err_throw(VMERR_BAD_TYPE_BIF); } /* if there's a starting index, skip that many characters */ int32_t start_idx = 0; if (orig_argc >= 2 && G_stk->get(1)->typ != VM_NIL) { /* get the starting index value */ start_idx = G_stk->get(1)->num_to_int(vmg0_); /* set up a UTF-8 pointer for traversing the string */ utf8_ptr strp((char *)str); /* * A positive index value is a 1-based index from the start of the * string. A negative index is from the end of the string, with -1 * pointing to the last character. For reverse searches, 0 is the * position past the last character. In any case, adjust to a * zero-based index. */ start_idx += (start_idx == 0 && dir < 0 ? (int)strp.len(len) : start_idx < 0 ? (int)strp.len(len) : -1); /* if there's a substring, we can limit the skip */ size_t min_len = (dir > 0 && str2 != 0 ? vmb_get_len(str2) : 0); /* skip that many characters */ int32_t i; for (i = 0 ; i < start_idx && len > min_len ; ++i, strp.inc(&len)) ; /* * if the start index was past the end of the string (or past the * point where the remaining subject string is too short for the * target string), we definitely can't match */ if (i < start_idx) { retval->set_nil(); goto done; } /* start the search here */ str = strp.getptr(); } else if (dir < 0) { /* * we're searching backwards, and there's no starting index, so the * starting point is the end of the string */ str = str + len; start_idx = utf8_ptr::s_len(basestr, len); len = 0; } /* find the substring */ int match_idx, match_len; if (dir > 0 ? find_substr(vmg_ self_val, basestr, str, len, str2, pat2, &match_idx, &match_len) != 0 : find_last_substr(vmg_ self_val, basestr, str, len, str2, pat2, &match_idx, &match_len) != 0) { /* found it - adjust to a 1-based value for return */ retval->set_int(start_idx + match_idx + 1); } else { /* didn't find it - return nil */ retval->set_nil(); } done: /* discard arguments */ G_stk->discard(orig_argc); /* handled */ return TRUE; } /* * property evaluator - find */ int CVmObjString::getp_find(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return find_common<1>(vmg_ retval, self_val, str, argc); } /* * property evaluator - findLast */ int CVmObjString::getp_findLast(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return find_common<-1>(vmg_ retval, self_val, str, argc); } /* ------------------------------------------------------------------------ */ /* * property evaluator - findAll */ int CVmObjString::getp_findAll(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ uint orig_argc = (argc != 0 ? *argc : 0); static CVmNativeCodeDesc desc(1, 1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the string length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* remember the base string */ const char *basestr = str; /* retrieve the string or regex pattern to find */ vm_val_t *targ = G_stk->get(0); const char *targstr = 0; CVmObjPattern *targpat = 0; if ((targstr = targ->get_as_string(vmg0_)) != 0) { /* we have a simple string to find */ } else if ((targpat = vm_val_cast(CVmObjPattern, targ)) != 0) { /* it's a pattern */ } else { /* we need a string or a pattern; other values are invalid */ err_throw(VMERR_BAD_TYPE_BIF); } /* retrieve the filter callback function, if present */ vm_val_t *func = 0; int func_argc_min = 0, func_argc_max = 0; vm_rcdesc rc; if (orig_argc >= 2 && (func = G_stk->get(1))->typ != VM_NIL) { /* make sure it's a function */ if (!func->is_func_ptr(vmg0_)) err_throw(VMERR_BAD_TYPE_BIF); /* note how many arguments the function accepts */ CVmFuncPtr fd(vmg_ func); func_argc_min = fd.get_min_argc(); func_argc_max = fd.is_varargs() ? -1 : fd.get_max_argc(); } /* * create a tentative list for the results (we'll resize this as needed * as we add elements) */ retval->set_obj(CVmObjList::create(vmg_ FALSE, 10)); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* clear it initially and push it for gc protection */ lst->cons_clear(); G_stk->push(retval); /* repeatedly search for the pattern */ int cnt; for (cnt = 0 ; len != 0 ; ++cnt) { /* search for the next element */ int match_cofs, match_len, match_bofs; const char *nxt = find_substr( vmg_ self_val, basestr, str, len, targstr, targpat, &match_cofs, &match_len); /* if we didn't find a match, we're done */ if (nxt == 0) break; /* figure the byte offset of the match */ match_bofs = nxt - str; /* call the callback, or just return the match text directly */ vm_val_t ele; if (func == 0) { /* no callback - just use the match text, as a new string */ ele.set_obj(CVmObjString::create(vmg_ FALSE, nxt, match_len)); } else { /* we have a callback - push arguments */ int fargc = 0; /* * Figure out how many groups are populated, and how many the * function expects. If the function takes varargs, it expects * all of the groups. If it takes fixed arguments, it expects * at most max_argc-2. In addition, the function expects a * minimum of min_argc-2 groups, and expects us to add nil * arguments for any unpopulated groups to fill out that * minimum. */ int gc_actual = G_bif_tads_globals->rex_searcher->get_group_cnt(); int gc_wanted = (func_argc_max == -1 ? gc_actual : func_argc_max - 2); if (gc_wanted < func_argc_min - 2) gc_wanted = func_argc_min - 2; /* push the groups, last to first */ for (int i = gc_wanted ; i > 0 ; ) { /* get the group, if it exists */ const re_group_register *reg = 0; if (--i < gc_actual) { /* valid group - get the group information */ reg = G_bif_tads_globals->rex_searcher->get_group_reg(i); } /* if the group is populated, push its text, otherwise nil */ if (reg != 0 && reg->start_ofs >= 0 && reg->end_ofs >= 0) { /* push the group text as a new string */ G_stk->push()->set_obj(CVmObjString::create( vmg_ FALSE, basestr + reg->start_ofs, reg->end_ofs - reg->start_ofs)); } else { /* unpopulated group - push nil */ G_stk->push()->set_nil(); } /* count it */ ++fargc; } /* if the index value is desired, push it */ if (func_argc_max == -1 || func_argc_max >= 2) { /* push the index of the match relative to basestr */ G_stk->push()->set_int( utf8_ptr::s_len(basestr, nxt - basestr) + 1); ++fargc; } /* if the match string value is desired, push it */ if (func_argc_max == -1 || func_argc_max >= 1) { /* push the match text */ G_stk->push()->set_obj( CVmObjString::create(vmg_ FALSE, nxt, match_len)); ++fargc; } /* call the callback */ G_interpreter->call_func_ptr(vmg_ func, fargc, &rc, 0); /* retrieve the return value */ ele = *G_interpreter->get_r0(); } /* add it to the list */ lst->cons_ensure_space(vmg_ cnt, 0); lst->cons_set_element(cnt, &ele); /* skip the matched text */ size_t skip = match_bofs + match_len; str += skip; len -= skip; /* * if we matched zero characters, skip one character to prevent an * infinite loop */ if (match_len == 0 && len != 0) str += utf8_ptr::s_bytelen(str, 1); } /* finalize the list length */ lst->cons_set_len(cnt); /* discard arguments and gc protection */ G_stk->discard(orig_argc + 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - match */ int CVmObjString::getp_match(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ uint orig_argc = (argc != 0 ? *argc : 0); static CVmNativeCodeDesc desc(1, 1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the string length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* remember the base string */ const char *basestr = str; /* retrieve the string or regex pattern to match */ vm_val_t *targ = G_stk->get(0); const char *targstr = 0; CVmObjPattern *targpat = 0; size_t targstrlen = 0; if ((targstr = targ->get_as_string(vmg0_)) != 0) { /* it's a literal string - get its length and text pointer */ targstrlen = vmb_get_len(targstr); targstr += VMB_LEN; } else if ((targpat = vm_val_cast(CVmObjPattern, targ)) != 0) { /* it's a pattern */ } else { /* we need a string or a pattern; other values are invalid */ err_throw(VMERR_BAD_TYPE_BIF); } /* retrieve the starting index, if present */ int32_t start_idx = 0; if (orig_argc >= 2 && G_stk->get(1)->typ != VM_NIL) { /* get the starting index value */ start_idx = G_stk->get(1)->num_to_int(vmg0_); /* set up a UTF-8 pointer for traversing the string */ utf8_ptr strp((char *)str); /* * A positive index value is a 1-based index from the start of the * string. A negative index is from the end of the string, with -1 * pointing to the last character. */ start_idx += (start_idx < 0 ? (int)strp.len(len) : -1); /* skip that many characters */ int32_t i; for (i = 0 ; i < start_idx && len > targstrlen ; ++i, strp.inc(&len)) ; /* * if the start index was past the end of the string (or past the * point where the remaining subject string is too short for the * target string), we definitely can't match */ if (i < start_idx) { retval->set_nil(); goto done; } /* start the search here */ str = strp.getptr(); } /* match the string or pattern */ if (targstr != 0) { /* make sure it's long enough, then match the text literally */ if (len >= targstrlen && memcmp(targstr, str, targstrlen) == 0) { /* matched - return the match length */ retval->set_int(targstrlen); } else { /* no match */ retval->set_nil(); } } else { /* RexPattern - get the compiled pattern */ re_compiled_pattern *cpat = targpat->get_pattern(vmg0_); /* save the last search source string */ G_bif_tads_globals->last_rex_str->val = *self_val; G_bif_tads_globals->rex_searcher->clear_group_regs(); /* match the pattern */ int matchlen = G_bif_tads_globals->rex_searcher->match_pattern( cpat, basestr, str, len); /* if it matched (len >= 0), return the length, otherwise nil */ if (matchlen >= 0) retval->set_int(matchlen); else retval->set_nil(); } done: /* discard arguments */ G_stk->discard(orig_argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * findReplace() method - replace one or all occurrences of a given * substring or regular expression pattern within the subject string (self) * with a given replacement string. This uses the common find/replace * handler defined in vmfindrep.h. */ int CVmObjString::getp_replace(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ uint orig_argc = (argc != 0 ? *argc : 0); static CVmNativeCodeDesc desc(2, 3); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* do the search-and-replace */ vm_find_replace<VMFINDREPLACE_StringFindReplace>( vmg_ retval, orig_argc, self_val, str); /* discard arguments */ G_stk->discard(orig_argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - convert to unicode */ int CVmObjString::getp_to_uni(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *in_argc) { size_t bytelen; long idx = 1; utf8_ptr p; /* check arguments */ uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* retrieve the index argument if present */ if (argc >= 1) idx = CVmBif::pop_long_val(vmg0_); /* push a self-reference as GC protection */ G_stk->push(self_val); /* get and skip the string's length prefix */ bytelen = vmb_get_len(str); str += VMB_LEN; /* set up a utf8 pointer to the string */ p.set((char *)str); /* if the index is negative, it's an index from the end of the string */ if (idx < 0) idx += p.len(bytelen) + 1; /* check for an index argument */ if (argc >= 1) { /* skip through the string until we get to the desired index */ for ( ; idx > 1 && bytelen != 0 ; --idx, p.inc(&bytelen)) ; /* check to see if we have a character available */ if (idx == 1 && bytelen != 0) { /* the index is valid - return the character here */ retval->set_int((long)p.getch()); } else { /* * the index is past the end of the string or is less than 1 * - return nil to indicate that there's no character here */ retval->set_nil(); } } else { size_t charlen; vm_obj_id_t lst_obj; CVmObjList *lst; size_t i; /* * There's no index argument - they want a list of all of the * code points in the string. First, get the number of * characters in the string. */ charlen = p.len(bytelen); /* create a list to hold the results */ lst_obj = CVmObjList::create(vmg_ FALSE, charlen); lst = (CVmObjList *)vm_objp(vmg_ lst_obj); /* set the list's elements to the unicode characters values */ for (i = 0 ; i < charlen ; ++i, p.inc()) { wchar_t ch; vm_val_t ele_val; /* get this character */ ch = p.getch(); /* set this list element */ ele_val.set_int((long)ch); lst->cons_set_element(i, &ele_val); } /* return the list object */ retval->set_obj(lst_obj); } /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - htmlify */ /* * htmlify flags */ /* preserve spaces */ #define VMSTR_HTMLIFY_KEEP_SPACES 0x0001 /* preserve newlines */ #define VMSTR_HTMLIFY_KEEP_NEWLINES 0x0002 /* preserve tabs */ #define VMSTR_HTMLIFY_KEEP_TABS 0x0004 /* * htmlify implementation */ int CVmObjString::getp_htmlify(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *in_argc) { size_t bytelen; utf8_ptr p; utf8_ptr dstp; size_t rem; size_t extra; long flags; vm_obj_id_t result_obj; int prv_was_sp; /* check arguments */ uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* if they specified flags, pop them */ if (argc >= 1) { /* retrieve the flags */ flags = CVmBif::pop_long_val(vmg0_); } else { /* no flags */ flags = 0; } /* push a self-reference as GC protection */ G_stk->push(self_val); /* get and skip the string's length prefix */ bytelen = vmb_get_len(str); str += VMB_LEN; /* * scan the string to determine how much space we'll have to add to * generate the htmlified version */ for (prv_was_sp = FALSE, extra = 0, p.set((char *)str), rem = bytelen ; rem != 0 ; p.inc(&rem)) { int this_is_sp; /* presume it's not a space */ this_is_sp = FALSE; /* check what we have */ switch(p.getch()) { case '&': /* we must replace '&' with '&' - this adds four bytes */ extra += 4; break; case '<': /* we must replace '<' with '<' - this adds three bytes */ extra += 3; break; case '>': /* replace '>' with '>' */ extra += 3; break; case ' ': /* * If we're in preserve-spaces mode, and the previous space * was some kind of whitespace character, change this to * ' ' - this adds five bytes */ if (prv_was_sp && (flags & VMSTR_HTMLIFY_KEEP_SPACES) != 0) extra += 5; /* note that this was a whitespace character */ this_is_sp = TRUE; break; case '\t': /* if we're in preserve-tabs mode, change this to '<tab>' */ if ((flags & VMSTR_HTMLIFY_KEEP_TABS) != 0) extra += 4; /* note that this was a whitespace character */ this_is_sp = TRUE; break; case '\n': case 0x2028: /* if we're in preserve-newlines mode, change this to '<br>' */ if ((flags & VMSTR_HTMLIFY_KEEP_NEWLINES) != 0) extra += 3; /* note that this was a whitespace character */ this_is_sp = TRUE; break; } /* for next time, remember whether this is a space */ prv_was_sp = this_is_sp; } /* allocate space for the new string */ result_obj = create(vmg_ FALSE, bytelen + extra); /* get a pointer to the result buffer */ dstp.set(((CVmObjString *)vm_objp(vmg_ result_obj))->cons_get_buf()); /* translate the string and write the result */ for (prv_was_sp = FALSE, p.set((char *)str), rem = bytelen ; rem != 0 ; p.inc(&rem)) { wchar_t ch; int this_is_sp; /* get this character */ ch = p.getch(); /* presume it's not a space */ this_is_sp = FALSE; /* check what we have */ switch(ch) { case '&': /* replace '&' with '&' */ dstp.setch_str("&"); break; case '<': /* we must replace '<' with '<' - this adds three bytes */ dstp.setch_str("<"); break; case '>': dstp.setch_str(">"); break; case ' ': /* note that this was a whitespace character */ this_is_sp = TRUE; /* * ignore it if not in preserve-spaces mode, or if the * previous character wasn't whitespace of some kind */ if (!prv_was_sp || (flags & VMSTR_HTMLIFY_KEEP_SPACES) == 0) goto do_default; /* add the nbsp */ dstp.setch_str(" "); break; case '\t': /* note that this was a whitespace character */ this_is_sp = TRUE; /* ignore if not in preserve-tabs mode */ if ((flags & VMSTR_HTMLIFY_KEEP_TABS) == 0) goto do_default; /* add the <tab> */ dstp.setch_str("<tab>"); break; case '\n': case 0x2028: /* note that this was a whitespace character */ this_is_sp = TRUE; /* if we're not in preserve-newlines mode, ignore it */ if ((flags & VMSTR_HTMLIFY_KEEP_NEWLINES) == 0) goto do_default; /* add the <br> */ dstp.setch_str("<br>"); break; default: do_default: /* copy this character unchanged */ dstp.setch(ch); break; } /* for next time, remember whether this is a space */ prv_was_sp = this_is_sp; } /* return the new string */ retval->set_obj(result_obj); /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - startsWith */ int CVmObjString::getp_starts_with(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { const char *str2; size_t len; size_t len2; /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* retrieve the other string */ str2 = CVmBif::pop_str_val(vmg0_); /* get the lengths of the two strings */ len = vmb_get_len(str); len2 = vmb_get_len(str2); /* move to the contents of each string */ str += VMB_LEN; str2 += VMB_LEN; /* * if the other string is no longer than our string, and the other * string matches our string exactly for the other string's entire * length, we start with the other string */ retval->set_logical(len2 <= len && memcmp(str, str2, len2) == 0); /* handled */ return TRUE; } /* * property evaluator - endsWith */ int CVmObjString::getp_ends_with(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { const char *str2; size_t len; size_t len2; /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* retrieve the other string */ str2 = CVmBif::pop_str_val(vmg0_); /* get the lengths of the two strings */ len = vmb_get_len(str); len2 = vmb_get_len(str2); /* move to the contents of each string */ str += VMB_LEN; str2 += VMB_LEN; /* * If the other string is no longer than our string, and the other * string matches our string at the end exactly for the other string's * entire length, we start with the other string. Note we don't need * to worry about finding a valid character index in our string for * the ending offset, because all we care about is whether or not we * have an exact byte match between our suffix and the other string. */ retval->set_logical(len2 <= len && memcmp(str + len - len2, str2, len2) == 0); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - mapToByteArray */ int CVmObjString::getp_to_byte_array(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *oargc) { /* check arguments */ int argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* note the mapper argument */ const vm_val_t *cmap = (argc >= 1 ? G_stk->get(0) : 0); /* save the string for gc protection */ G_stk->push(self_val); /* create the byte array */ retval->set_obj(CVmObjByteArray::create_from_string( vmg_ self_val, str, cmap)); /* discard arguments and gc protection */ G_stk->discard(argc + 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - splice */ int CVmObjString::getp_splice(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { char ins_buf[128]; /* check arguments */ uint oargc = (argc != 0 ? *argc : 0); static CVmNativeCodeDesc desc(2, 1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* retrieve the starting index and deletion length */ int start_idx = CVmBif::pop_int_val(vmg0_); int del_len = CVmBif::pop_int_val(vmg0_); /* get and skip our length */ size_t len = vmb_get_len(str); str += VMB_LEN; utf8_ptr p((char *)str); int charlen = p.len(len); /* adjust the index to 0-based, and figure end-of-string offsets */ start_idx += (start_idx < 0 ? charlen : -1); /* make sure the starting index and deletion length are in bounds */ start_idx = (start_idx < 0 ? 0 : start_idx > (int)charlen ? charlen : start_idx); del_len = (del_len < 0 ? 0 : del_len > (int)(charlen - start_idx) ? charlen - start_idx : del_len); /* get the starting byte index */ size_t start_byte_idx = p.bytelen(start_idx); /* save 'self' as gc protection */ G_stk->push(self_val); /* * If there's an insertion string, get it as a string. Treat nil as an * empty insertion. */ size_t ins_len = 0; const char *ins = 0; vm_val_t new_ins_str; new_ins_str.set_nil(); if (oargc >= 3 && G_stk->get(1)->typ != VM_NIL) { /* * Leave the insertion string on the stack for gc protection, and * do an explicit conversion to string. */ ins = cvt_to_str(vmg_ &new_ins_str, ins_buf, sizeof(ins_buf), G_stk->get(1), 10, 0); ins_len = vmb_get_len(ins); ins += VMB_LEN; } /* push the new insertion string (if any) for gc protection */ G_stk->push(&new_ins_str); /* check to see if we're making any changes */ if (del_len != 0 || ins_len != 0) { /* allocate a new string at the proper length */ retval->set_obj(create(vmg_ FALSE, len + ins_len - del_len)); CVmObjString *new_str = (CVmObjString *)vm_objp(vmg_ retval->val.obj); char *newp = new_str->cons_get_buf(); /* copy the part of our string up to the starting index */ if (start_byte_idx > 0) { memcpy(newp, str, start_byte_idx); newp += start_byte_idx; } /* if we have an insertion string, copy it */ if (ins_len != 0) { memcpy(newp, ins, ins_len); newp += ins_len; } /* skip past the deleted material in the original string */ p.set((char *)str + start_byte_idx); for (len -= start_byte_idx ; del_len != 0 && len != 0 ; --del_len) p.inc(&len); /* if we have any more of the spliced string, copy it */ if (len != 0) memcpy(newp, p.getptr(), len); } else { /* we're making no changes - return the original string */ *retval = *self_val; } /* * discard the remaining arguments and the gc protection (original * argc, minus 2 arg pops, plus 2 gc protection pushes -> oargc) */ G_stk->discard(oargc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - split */ int CVmObjString::getp_split(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argcp) { /* check arguments */ static CVmNativeCodeDesc desc(0, 2); uint argc = (argcp != 0 ? *argcp : 0); if (get_prop_check_argc(retval, argcp, &desc)) return TRUE; /* get the string buffer and length */ size_t len = vmb_get_len(str); str += VMB_LEN; /* remember the start of the base string */ const char *basestr = str; /* * Get the delimiter or split size, leaving it on the stack for gc * protection. This can be a string, a RexPattern, or an integer. */ const vm_val_t *delim = G_stk->get(0); const char *delim_str = 0; CVmObjPattern *delim_pat = 0; int split_len = 0; if (argc < 1 || delim->typ == VM_NIL) { /* there's no delimiter at all, so use the default split length 1 */ split_len = 1; } else if (delim->typ == VM_INT) { /* it's a simple split length */ split_len = delim->val.intval; if (split_len <= 0) err_throw(VMERR_BAD_VAL_BIF); } else if ((delim_str = delim->get_as_string(vmg0_)) == 0) { /* if it's not a length or string, it has to be a RexPattern object */ if (delim->typ != VM_OBJ || !CVmObjPattern::is_pattern_obj(vmg_ delim->val.obj)) err_throw(VMERR_BAD_TYPE_BIF); /* get the pattern object, properly cast */ delim_pat = (CVmObjPattern *)vm_objp(vmg_ delim->val.obj); } /* get the split count limit, if there is one */ int32_t limit = -1; if (argc >= 2 && G_stk->get(1)->typ != VM_NIL) { /* there's an explicit limit; fetch it and make sure it's at least 1 */ if ((limit = G_stk->get(1)->num_to_int(vmg0_)) < 1) err_throw(VMERR_BAD_VAL_BIF); } /* push 'self' for gc protection */ G_stk->push(self_val); /* * Set up a return list. If we have a limit, the list can't go over * that length, so just create the list at the limit. Otherwise, if we * have a split length, we can figure the required list length by * dividing the string length by the split length (rounding up). * Otherwise we have no idea how many list elements we'll need, so just * create the list at an arbitrary default length and expand later if * needed. */ int init_list_len; if (limit > 0) { /* there's a limit, so we won't need more than this many elements */ init_list_len = limit; } else if (split_len > 0) { /* split length -> divide the string length by the split length */ init_list_len = (len + split_len - 1)/split_len; /* set the length to at least one, unless it's an empty string */ if (init_list_len < 1 && len != 0) init_list_len = 1; } else { /* no limit or length, so start with an arbitrary guess */ init_list_len = 10; } /* create the list */ retval->set_obj(CVmObjList::create(vmg_ FALSE, init_list_len)); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* clear the list to nils, since we're building it incrementally */ lst->cons_clear(); /* push it for gc protection */ G_stk->push(retval); /* repeatedly search for the delimiter and split the string */ int cnt; vm_val_t ele; for (cnt = 0 ; (limit < 0 || cnt + 1 < limit) && len != 0 ; ++cnt) { /* search for the next delimiter or next 'split_len' characters */ int match_ofs, match_len; if (split_len > 0) { /* figure the number of bytes in split_len characters */ match_ofs = utf8_ptr::s_bytelen(str, split_len); /* if that uses the whole rest of the string, we're done */ if (match_ofs >= (int)len) break; /* there's no delimiter, so the delimiter match length is zero */ match_len = 0; } else { /* search for the substring or pattern */ const char *nxt = find_substr( vmg_ self_val, basestr, str, len, delim_str, delim_pat, &match_ofs, &match_len); /* if we didn't find it, we're done */ if (nxt == 0) break; /* figure the byte offset to the match */ match_ofs = nxt - str; } /* add the substring from 'str' to 'p' to the list */ lst->cons_ensure_space(vmg_ cnt, 0); ele.set_obj(CVmObjString::create(vmg_ FALSE, str, match_ofs)); lst->cons_set_element(cnt, &ele); /* skip to the position after the match */ size_t skip = match_ofs + match_len; str += skip; len -= skip; /* * if we matched zero characters, skip one character to prevent an * infinite loop */ if (split_len == 0 && match_len == 0 && len != 0) str += utf8_ptr::s_bytelen(str, 1); } /* add a final element for the remainder of the string */ if (len != 0) { lst->cons_ensure_space(vmg_ cnt, 0); ele.set_obj(CVmObjString::create(vmg_ FALSE, str, len)); lst->cons_set_element(cnt, &ele); /* count it */ ++cnt; } /* set the final size of the list */ lst->cons_set_len(cnt); /* discard arguments plus gc protection */ G_stk->discard(argc + 2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Helper routines for specialsToHtml */ /* find the tag name within a tag */ static void find_tag_name(StringRef *tag, const char *&name, size_t &len, int &is_end_tag) { const char *p; /* skip leading spaces */ for (p = tag->get() ; isspace(*p) ; ++p) ; /* if it's an end tag, skip the '/' and any intervening spaces */ if ((is_end_tag = (*p == '/')) != 0) for (++p ; isspace(*p) ; ++p) ; /* scan the name - it's from here to the next space */ for (name = p ; *p != '\0' && !isspace(*p) ; ++p) ; /* note the length */ len = p - name; } /* parse the name from a tag */ static void parse_tag_name(StringRef *tag, char *&name, int &is_end_tag) { /* get the tag name */ size_t len; const char *namec; find_tag_name(tag, namec, len, is_end_tag); /* allocate a copy of the tag name */ name = lib_copy_str(namec, len); } /* find an attribute value, given a pointer to the end of the attr name */ static int find_attr_val(const char *name, size_t name_len, const char *&val, size_t &val_len) { const char *p; /* go to the end of the attribute name */ p = name + name_len; /* if there's an '=', there's a value */ if (*p == '=') { /* the attribute value starts after the '=' */ val = ++p; /* skip the token */ for (char qu = 0 ; *p != 0 && (!isspace(*p) || qu != 0) ; ++p) { /* note if entering or leaving a quoted section */ if (qu == 0 && (*p == '"' || *p == '\'')) qu = *p; else if (*p == qu) qu = 0; } /* note the length of the value */ val_len = p - val; /* there's an explicit value for this attribute */ return TRUE; } else { /* the attribute name is its own implied value */ val = name; val_len = name_len; /* there's no explicit value */ return FALSE; } } /* get an attribute value in a tag */ static char *parse_attr_val(StringRef *tag, const char *attr_name) { const char *p; size_t len; int is_end_tag; size_t attr_len = strlen(attr_name); /* find and skip the tag name */ find_tag_name(tag, p, len, is_end_tag); p += len; /* keep going until we find the attribute or run out of text */ for (;;) { /* skip leading spaces */ for ( ; isspace(*p) ; ++p) ; /* if we're at the end of the text, we're done */ if (*p == '\0') break; /* scan the attribute name */ const char *an; size_t anlen; for (an = p ; *p != '\0' && *p != '=' && !isspace(*p) ; ++p) ; anlen = p - an; /* get the attribute value */ const char *av; size_t avlen; find_attr_val(an, anlen, av, avlen); /* check for a match */ if (anlen == attr_len && memicmp(attr_name, an, attr_len) == 0) { /* it's our attribute name - allocate a copy */ char *retval = lib_copy_str(av, avlen); /* remove quotes */ char *dst, qu; for (p = dst = retval, qu = 0 ; *p != '\0' ; ++p) { /* check for entering/leaving a quoted section */ if (qu == 0 && (*p == '"' || *p == '\'')) { /* entering quotes - note the quote, don't copy it */ qu = *p; } else if (qu == *p) { /* leaving quotes - don't copy the quote */ qu = 0; } else { /* copy anything else as given */ *dst++ = *p; } } /* null-terminate the updated string and return it */ *dst = '\0'; return retval; } /* skip the value and move on to the next attribute */ p = av + avlen; } /* didn't find the value */ return 0; } /* * property evaluator - specialsToHtml */ int CVmObjString::getp_specialsToHtml(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return specialsTo(vmg_ retval, self_val, str, argc, TRUE); } /* * property evaluator - specialsToText */ int CVmObjString::getp_specialsToText(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return specialsTo(vmg_ retval, self_val, str, argc, FALSE); } /* * common property evaluator for specialsToText and specialsToHtml */ int CVmObjString::specialsTo(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc, int html) { vm_rcdesc rc; vm_prop_id_t flags_prop = VM_INVALID_OBJ, tag_prop = VM_INVALID_OBJ; /* check arguments */ uint oargc = (argc != 0 ? *argc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* set up the default initial state */ int in_line = FALSE; int caps = FALSE, nocaps = FALSE; int space = FALSE, qspace = FALSE; int in_tag = FALSE, in_entity = FALSE; int qlevel = 0; char attrq = 0; int col = 0; StringRef *tag = new StringRef(128); /* get the string length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* set up a buffer for the output - anticipate a little expansion */ StringRef *buf = new StringRef(len*5/4); err_try { /* get the state object, if present */ vm_val_t stateobj; stateobj.set_nil(); if (oargc >= 1) { /* the argument is there - get the object or nil */ if (G_stk->get(0)->typ == VM_OBJ) { /* get the state object */ stateobj = *G_stk->get(0); /* set up the recursive evaluation descriptor */ rc.init(vmg_ "String.specialsToHtml", self_val, 13, G_stk->get(0), oargc); /* get the 'flags_' and 'tag_' property IDs */ flags_prop = G_predef->string_sth_flags; tag_prop = G_predef->string_sth_tag; /* get the flags */ int32_t flags = 0; if (flags_prop != VM_INVALID_OBJ) { /* get the property */ G_interpreter->get_prop(vmg_ 0, &stateobj, flags_prop, &stateobj, 0, &rc); /* if it's an integer, get its value */ if (G_interpreter->get_r0()->typ == VM_INT) flags = G_interpreter->get_r0()->val.intval; } /* decode the flags */ in_line = (flags & 0x0001) != 0; caps = (flags & 0x0002) != 0; nocaps = (flags & 0x0004) != 0; in_tag = (flags & 0x0008) != 0; attrq = (flags & 0x0010 ? '"' : flags & 0x0020 ? '\'' : 0); space = (flags & 0x0040) != 0; qspace = (flags & 0x0080) != 0; qlevel = (flags & 0x0100) != 0; in_entity = (flags & 0x200) != 0; col = (flags & 0x3000) >> 12; /* get the tag value */ const char *tagp; if (tag_prop != VM_INVALID_OBJ) { /* get the property */ G_interpreter->get_prop(vmg_ 0, &stateobj, tag_prop, &stateobj, 0, &rc); /* get the string value, if any */ tagp = G_interpreter->get_r0()->get_as_string(vmg0_); /* copy the tag string, if we found it */ if (tagp != 0) tag->append(tagp + VMB_LEN, vmb_get_len(tagp)); } } else if (G_stk->get(0)->typ != VM_NIL) { /* anything other than an object or nil is an error */ err_throw(VMERR_BAD_TYPE_BIF); } } /* parse the string */ utf8_ptr p; for (p.set((char *)str) ; len != 0 ; p.inc(&len)) { /* get the next character */ wchar_t ch = p.getch(); /* if we're in a tag, add this character to the tag */ if (in_tag) { /* convert specials in the tag */ switch (ch) { case '\n': case 0x0B: case 0x15: case '\t': /* convert newlines, quoted spaces, and tabs to spaces */ tag->append(" "); break; case 0x0E: case 0x0F: /* \^ and \v have no place in tags */ break; case '"': case '\'': /* note that we're entering or leaving quoted text */ if (attrq == 0) attrq = (char)ch; else if (attrq == ch) attrq = 0; /* append the character */ tag->append_utf8(ch); break; case '>': /* ignore it if we're in quoted text */ if (attrq != 0) { tag->append_utf8(ch); } else { /* end the tag */ in_tag = FALSE; /* parse the tag name */ char *tagname; int is_end_tag; parse_tag_name(tag, tagname, is_end_tag); /* assume we'll copy it verbatim */ int keep_tag = TRUE; /* check to see if it's one of our special tags */ if (stricmp(tagname, "q") == 0) { /* * <Q> or </Q> - translate to &ldquo..&rdquo or * &lsquo..&rsquo */ static const char *qentity[] = { "“", "”", "‘", "’" }; static const char *qtxt[] = { "\"", "\"", "'", "'" }; /* if it's </Q>, pre-decrement the level */ if (is_end_tag) qlevel = !qlevel; /* * figure which type of quote to use: double * quotes at even levels, single quotes at odd * levels; left quotes for open tags, right * quotes for close tags */ int idx = (qlevel ? 2 : 0) + (is_end_tag ? 1 : 0); /* add the quote */ buf->append(html ? qentity[idx] : qtxt[idx]); col++; /* if it's <Q>, post-increment the level */ if (!is_end_tag) qlevel = !qlevel; /* we've translated the tag */ keep_tag = FALSE; } else if (stricmp(tagname, "br") == 0) { /* * if there's a HEIGHT attribute, it's special; * otherwise it's just a regular <br> */ char *htv = parse_attr_val(tag, "height"); if (htv != 0) { /* get the numeric value */ int ht = atoi(htv); /* * if we're at the beginning of a line, * translate this to 'ht' <BR> tags; if * we're within a line, use 'ht+1' <BR> * tags */ if (in_line) ht += 1; /* write out the <BR> tags */ while (ht-- > 0) buf->append(html ? "<br>" : "\n"); /* done with the height value */ lib_free_str(htv); /* we've translated the tag */ keep_tag = FALSE; } else if (!html) { /* regular text mode - convert to \n */ buf->append("\n"); } /* we're no longer in a line */ in_line = FALSE; col = 0; } else if (stricmp(tagname, "p") == 0) { /* in text mode, add a newline or two */ if (!html) { buf->append("\n"); if (in_line) buf->append("\n"); } /* this counts as a line break */ in_line = FALSE; col = 0; } else if (stricmp(tagname, "div") == 0 || stricmp(tagname, "center") == 0 || stricmp(tagname, "table") == 0 || stricmp(tagname, "td") == 0 || stricmp(tagname, "th") == 0 || stricmp(tagname, "caption") == 0) { /* this counts as a line break */ in_line = FALSE; col = 0; /* in text mode, add a newline */ if (!html) buf->append("\n"); } /* * if we're keeping the original tag, copy it to * the output */ if (html && keep_tag) { buf->append("<"); buf->append(tag->get(), tag->getlen()); buf->append(">"); } /* done with the tag name */ lib_free_str(tagname); /* we're done with the tag - clear the buffer */ tag->truncate(0); } break; default: /* append anything else as-is */ tag->append_utf8(ch); break; } /* we're done processing this character */ continue; } /* continue gathering entity text if applicable */ if (in_entity) { /* * If the entity is more than 10 character long, or this is * a semicolon, end the entity. Also stop if we're * processing a &# entity and this isn't a digit. */ if (ch == ';' || tag->getlen() >= 10 || (tag->get()[0] == '#' && !is_digit(ch))) { /* check what we have in the tag buffer */ char *n = tag->get(); if (n[0] == '#') { /* get the decimal unicode value */ buf->append_utf8((wchar_t)atoi(n+1)); /* * if we stopped on something other than a ';', add * it as well */ if (ch != ';') buf->append_utf8(ch); } else if (stricmp(n, "nbsp") == 0) buf->append_utf8(' '); else if (stricmp(n, "gt") == 0) buf->append_utf8('>'); else if (stricmp(n, "lt") == 0) buf->append_utf8('<'); else if (stricmp(n, "amp") == 0) buf->append_utf8('&'); else if (stricmp(n, "ldquo") == 0 || stricmp(n, "rdquo") == 0 || stricmp(n, "quot") == 0) buf->append_utf8('\"'); else if (stricmp(n, "lsquo") == 0 || stricmp(n, "rsquo") == 0) buf->append_utf8('\''); else { /* unknown entity - copy it as-is */ buf->append(n); } /* we're no longer in an entity */ in_entity = FALSE; tag->truncate(0); } else { /* * we're still working on gathering this entity - * simply buffer the character for now */ tag->append_utf8(ch); } /* we've handled this character */ continue; } /* if we have a pending space, write it out */ if (space) { /* * We have a pending regular space. If the next character * is a quoted space, omit the regular space entirely, as * regular spaces combine with adjacent quoted spaces. * Otherwise, write it as a regular space. */ if (ch != 0x15) { buf->append(" "); col++; } /* we've now processed this pending space */ space = FALSE; } else if (qspace) { /* * We have a pending quoted space. * * If the next character is a regular space, skip the space * and keep the pending quoted space flag set - the regular * space combines with the quoted space, so we can remove * it, but we don't yet know how to handle the quoted space * itself since we have to see the next non-space character * to determine that. * * If the next character is another quoted space, write the * pending quoted space as  , since adjacent quoted * spaces don't combine. * * If the next character is anything else (not a space or * quoted space), write the pending quoted space as a * regular space, since the html renderer won't combine the * space with the following non-space character, and we do * want to allow a line break after a quoted space. */ if (ch == ' ') continue; else if (html) { buf->append(ch == 0x15 ? " " : " "); col++; } else { buf->append(" "); col++; } /* we've now processed this pending quoted space */ qspace = FALSE; } /* handle the character */ switch (ch) { case '\n': /* add a <br> if we're within a line */ if (in_line) buf->append(html ? "<br>" : "\n"); /* we're now at the start of a new line */ in_line = FALSE; col = 0; caps = nocaps = FALSE; break; case 0x0B: /* * '\b' - blank line: add two <BR>'s if we're within a * line, otherwise just add one */ buf->append(html ? "<br>" : "\n"); if (in_line) buf->append(html ? "<br>" : "\n"); /* we're now at the start of a new line */ in_line = FALSE; col = 0; caps = nocaps = FALSE; break; case 0x0F: /* * '\^' - this doesn't write anything to the output, but * simply sets a flag that tells us to capitalize the next * printing character */ caps = TRUE; nocaps = FALSE; break; case 0x0E: /* * '\v' - this doesn't write anything to the output, but * simply sets a flag that tells us to lower-case the next * printing character */ caps = FALSE; nocaps = TRUE; break; case 0x15: /* * '\ ' - quoted space: this is a non-combining space (it * can't be consolidated with adjacent spaces) that allows * line breaks. Don't write anything now, but set the * quoted space flag for the next character: if the next * character out is a regular or quoted space, we'll write * the quoted space as a non-breaking space; otherwise * we'll write the quoted space as a regular space, so that * we can break after it. */ qspace = TRUE; break; case ' ': /* * Regular space. Don't actually write anything yet, since * we'll have to turn this into a non-breaking space if the * next character turns out to be a quoted space. Just set * the space-pending flag for now. */ space = TRUE; break; case '\t': /* * Tab - append spaces to take us out to the next 4-column * tab stop. We want the renderer to obey the spaces * (rather than consolidating them), so use non-breaking * spaces ( ). However, we want to allow a line break * after the tab, so use an ordinary space for the last * one. The renderer won't combine an ordinary space with * an  , so it'll render the number of spaces we ask * for, but we'll be able to break the line after that * last, ordinary space. * * In regular text mode, just write out the tab character. */ if (html) { col = ((col + 1) & 3) - 1; for ( ; col + 1 < 3 ; ++col) buf->append(" "); buf->append(" "); ++col; } else buf->append("\t"); break; case '<': /* start a tag */ in_tag = TRUE; break; case '&': /* if in text mode, gather the entity information */ if (!html) in_entity = TRUE; else buf->append("&"); break; default: /* Ordinary character. Check for caps/nocaps conversions. */ { const wchar_t *u = 0; if (caps) { u = t3_to_upper(ch); caps = FALSE; } else if (nocaps) { u = t3_to_lower(ch); nocaps = FALSE; } /* we're now within a line */ in_line = TRUE; /* add this character or string to the output */ if (u != 0) { for ( ; *u != 0 ; buf->append_utf8(*u++)) ; } else { buf->append_utf8(ch); } /* advance the tab-stop column */ col++; } break; } } /* * If we have a state object, write the final state back to the * state object, so that the next call can pick up where we left * off. */ if (stateobj.typ == VM_OBJ) { /* encode the flags */ vm_val_t v; v.set_int((in_line ? 0x0001 : 0) | (caps ? 0x0002 : 0) | (nocaps ? 0x0004 : 0) | (in_tag ? 0x0008 : 0) | (attrq == '"' ? 0x0010 : attrq == '\'' ? 0x0020 : 0) | (space ? 0x0040 : 0) | (qspace ? 0x0080 : 0) | (qlevel ? 0x0100 : 0) | (in_entity ? 0x0200 : 0) | ((col & 3) << 12)); /* set the flags */ if (flags_prop != VM_INVALID_OBJ) { G_interpreter->set_prop( vmg_ stateobj.val.obj, flags_prop, &v); } /* set the tag */ if (tag_prop != VM_INVALID_OBJ) { if (in_tag || in_entity) { /* we have a tag - set stateobj.tags_ to the string */ G_interpreter->push_string( vmg_ tag->get(), tag->getlen()); G_interpreter->set_prop(vmg_ stateobj.val.obj, tag_prop, G_stk->get(0)); /* done with the stacked string */ G_stk->discard(); } else { /* no tag - set stateobj.tags_ to nil */ v.set_nil(); G_interpreter->set_prop( vmg_ stateobj.val.obj, tag_prop, &v); } } } /* return the result as a string value */ G_interpreter->push_string(vmg_ buf->get(), buf->getlen()); G_stk->pop(retval); } err_finally { /* release resources */ tag->release_ref(); buf->release_ref(); } err_end; /* discard arguments and gc protection items */ G_stk->discard(oargc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - urlEncode() */ int CVmObjString::getp_urlEncode(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* save my own value for gc protection */ G_stk->push(self_val); /* get the length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* scan the string and determine the size of the expansion */ size_t outlen; const char *p; size_t rem; for (outlen = 0, p = str, rem = len ; rem != 0 ; ++p, --rem) { /* * if this is a space, letter, digit, or '-' or '_', it requires * one byte in the output; otherwise it requires three bytes for * the "%xx" sequence */ char c = *p; if (c == ' ' || c == '-' || c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) outlen += 1; else outlen += 3; } /* allocate the return string */ retval->set_obj(create(vmg_ FALSE, outlen)); char *dst = ((CVmObjString *)vm_objp(vmg_ retval->val.obj))->cons_get_buf(); /* build the return string */ for (p = str, rem = len ; rem != 0 ; ++p, --rem) { /* translate this byte */ char c = *p; if (c == ' ') { /* translate ' ' to '+' */ *dst++ = '+'; } else if (c == '-' || c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) { /* letter, digit, '-', '_' - leave unchanged */ *dst++ = c; } else { /* translate anything else to a %xx sequence */ *dst++ = '%'; byte_to_xdigits(dst, (unsigned char)c); dst += 2; } } /* discard gc protection */ G_stk->discard(1); /* done */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - urlDecode() */ int CVmObjString::getp_urlDecode(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* save my own value for gc protection */ G_stk->push(self_val); /* get the length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* scan the string and determine the size of the result */ size_t outlen; const char *p; size_t rem; for (outlen = 0, p = str, rem = len ; rem != 0 ; ++p, --rem, ++outlen) { /* * if this is a '%xx' sequence, it translates to a single byte; * otherwise just copy the byte as-is */ if (*p == '%' && rem >= 3 && is_xdigit(*(p+1)) && is_xdigit(*(p+2))) { /* * it's a %xx sequence - skip the whole thing and count is as * just one byte of output */ p += 2; rem -= 2; } } /* allocate the return string */ retval->set_obj(create(vmg_ FALSE, outlen)); char *outbuf = ((CVmObjString *)vm_objp(vmg_ retval->val.obj))->cons_get_buf(); char *dst = outbuf; /* build the return string */ for (p = str, rem = len ; rem != 0 ; ++p, --rem, ++dst) { /* * if it's a '+', translate to a space; otherwise, if this is a * '%xx' sequence, it translates to a single byte; otherwise just * copy the byte as-is */ if (*p == '+') { *dst = ' '; } else if (*p == '%' && rem >= 3 && is_xdigit(*(p+1)) && is_xdigit(*(p+2))) { /* it's a %xx sequence - translate to a byte */ *dst = (value_of_xdigit(*(p+1)) << 4) | value_of_xdigit(*(p+2)); /* skip the %xx sequence in the input */ p += 2; rem -= 2; } else { /* copy anything else as-is */ *dst = *p; } } /* validate the resulting UTF8 */ CCharmapToUni::validate(outbuf, outlen); /* discard gc protection */ G_stk->discard(1); /* done */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - sha256() */ int CVmObjString::getp_sha256(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* calculate the hash */ char hash[65]; sha256_ez(hash, str, len); /* allocate the return string */ retval->set_obj(create(vmg_ FALSE, hash, 64)); /* done */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - digestMD5() */ int CVmObjString::getp_md5(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* calculate the hash */ char hash[33]; md5_ez(hash, str, len); /* allocate the return string */ retval->set_obj(create(vmg_ FALSE, hash, 32)); /* done */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - packBytes() */ int CVmObjString::static_getp_packBytes(VMG_ vm_val_t *retval, uint *pargc) { /* check arguments */ uint argc = (pargc != 0 ? *pargc : 0); static CVmNativeCodeDesc desc(1, 0, TRUE); if (get_prop_check_argc(retval, pargc, &desc)) return TRUE; /* set up an in-memory data stream to receive the packed data */ CVmMemorySource *dst = new CVmMemorySource(0L); err_try { /* do the packing */ CVmPack::pack(vmg_ 0, argc, dst); /* create a string from the byte stream */ retval->set_obj(create_latin1(vmg_ FALSE, dst)); } err_finally { /* done with the in-memory data source */ delete dst; } err_end; /* discard arguments */ G_stk->discard(argc); /* done */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * UTF-8 String data source. This translates characters from the string to * bytes, treating the character code as the byte value. If we try to read * a character outside of the 0..255 range, we'll throw an error. */ class CVmUTF8Source: public CVmDataSource { public: CVmUTF8Source(const char *str, size_t len) { /* remember the string and its byte length */ this->str = str; this->bytelen = len; /* calculate the character length */ utf8_ptr p((char *)str); this->charlen = p.len(len); /* start at the beginning of the string */ this->charidx = 0; this->byteidx = 0; } virtual CVmDataSource *clone(VMG_ const char * /*mode*/) { return 0; } /* read bytes - returns 0 on success, non-zero on EOF or error */ virtual int read(void *buf, size_t len) { /* do the read; if the request isn't filled exactly, return failure */ return (size_t)readc(buf, len) != len; } /* read bytes - returns the number of bytes read; 0 means EOF or error */ virtual int readc(void *buf0, size_t len) { /* get the output buffer pointer as a character pointer */ char *buf = (char *)buf0; /* * limit the length to the remaining number of characters from the * current position */ size_t rem = charlen - charidx; if (len > rem) len = rem; /* do the copy */ utf8_ptr p((char *)str + byteidx); for (size_t i = 0 ; i < len ; ++i, p.inc(), ++charidx) { /* get this character */ wchar_t ch = p.getch(); /* if it's outside of the 0..255 range, it's an error */ if (ch > 255) err_throw(VMERR_NUM_OVERFLOW); /* store it */ *buf++ = (char)ch; } /* calculate the new byte index */ byteidx = (size_t)(p.getptr() - str); /* return the length we copied */ return len; } /* write bytes - we're read-only, so just return an error */ virtual int write(const void *, size_t) { return 1; } /* * Get the length of the file in bytes. We translate each character * from the string into into one byte in the stream, so our byte length * equals our character length. */ virtual long get_size() { return charlen; } /* get the current seek location */ virtual long get_pos() { return charidx; } /* set the current seek location - 'mode' is an OSFSK_xxx mode */ virtual int seek(long ofs, int mode) { /* figure the absolute position based on the mode */ switch (mode) { case OSFSK_SET: /* set - the offset is already absolute */ break; case OSFSK_CUR: /* relative to current index */ ofs += charidx; break; case OSFSK_END: /* relative to the end of file */ ofs += charlen; break; default: /* invalid mode - return an error */ return 1; } /* limit the position to the bounds of the string */ if (ofs < 0) ofs = 0; else if (ofs > (long)charlen) ofs = (long)charlen; /* * The caller is working with the stream: the byte index in the * stream equals the character index in the string. */ charidx = (size_t)ofs; /* calculate the byte index for the new character index */ utf8_ptr p((char *)str); byteidx = p.bytelen(charidx); /* success */ return 0; } /* flush - no effect, since we don't buffer anything */ virtual int flush() { return 0; } /* close - we have no system resources, so this is a no-op */ virtual void close() { } protected: /* the string */ const char *str; /* byte and character length of the string */ size_t bytelen, charlen; /* current byte and character seek position in the string */ size_t byteidx, charidx; }; /* * property evaluator - unpackBytes() */ int CVmObjString::getp_unpackBytes(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* get the format string (leave it on the stack for gc protection) */ const char *fmt = G_stk->get(0)->get_as_string(vmg0_); if (fmt == 0) err_throw(VMERR_STRING_VAL_REQD); /* get its buffer and length */ size_t fmtlen = vmb_get_len(fmt); fmt += VMB_LEN; /* unpack the string */ CVmUTF8Source src(str, len); CVmPack::unpack(vmg_ retval, fmt, fmtlen, &src); /* discard arguments */ G_stk->discard(1); /* done */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - compareTo() */ int CVmObjString::getp_compareTo(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* get the other string (leave it on the stack for gc protection) */ const char *other = G_stk->get(0)->get_as_string(vmg0_); if (other == 0) err_throw(VMERR_STRING_VAL_REQD); /* get its buffer and length */ size_t olen = vmb_get_len(other); other += VMB_LEN; /* no result yet */ retval->set_nil(); /* compare character by character */ utf8_ptr a((char *)str); utf8_ptr b((char *)other); for ( ; len != 0 && olen != 0 ; a.inc(&len), b.inc(&olen)) { int delta = (int)a.getch() - (int)b.getch(); if (delta != 0) { retval->set_int(delta); break; } } /* if we didn't find any differences, the shorter string sorts first */ if (retval->typ == VM_NIL) retval->set_int(len - olen); /* discard arguments */ G_stk->discard(1); /* done */ return TRUE; } /* * property evaluator - compareIgnoreCase() */ int CVmObjString::getp_compareIgnoreCase(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* get the other string (leave it on the stack for gc protection) */ const char *other = G_stk->get(0)->get_as_string(vmg0_); if (other == 0) err_throw(VMERR_STRING_VAL_REQD); /* get its buffer and length */ size_t olen = vmb_get_len(other); other += VMB_LEN; /* compare the strings */ retval->set_int(t3_compare_case_fold(str, len, other, olen, 0)); /* discard arguments */ G_stk->discard(1); /* done */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Constant-pool string object */ /* * create */ vm_obj_id_t CVmObjStringConst::create(VMG_ const char *const_ptr) { /* create our new ID */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, FALSE, FALSE); /* create our string object, pointing directly to the constant pool */ new (vmg_ id) CVmObjStringConst(vmg_ const_ptr); /* return the new ID */ return id; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmstr.h���������������������������������������������������������������������������0000664�0000000�0000000�00000066643�12650170723�0015221�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMSTR.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmstr.h - VM dynamic string implementation Function Notes Modified 10/28/98 MJRoberts - Creation */ #ifndef VMSTR_H #define VMSTR_H #include "vmglob.h" #include "vmobj.h" /* * String object */ class CVmObjString: public CVmObject { friend class CVmMetaclassString; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is the given object reference a string object? */ static int is_string_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create from stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* get my datatype when converted to constant data */ virtual vm_datatype_t get_convert_to_const_data_type() const { return VM_SSTRING; } /* create a string with no initial contents */ static vm_obj_id_t create(VMG_ int in_root_set); /* create a string to hold a string of the given byte length */ static vm_obj_id_t create(VMG_ int in_root_set, size_t bytelen); /* create from a constant UTF-8 string */ static vm_obj_id_t create(VMG_ int in_root_set, const char *str, size_t bytelen); /* create from a wide unicode string */ static vm_obj_id_t create(VMG_ int in_root_set, const wchar_t *str, size_t charlen); /* create from a byte stream, treating the bytes as Latin-1 characters */ static vm_obj_id_t create_latin1(VMG_ int in_root_set, class CVmDataSource *src); /* create from a string, mapping through the given character mapper */ static vm_obj_id_t create(VMG_ int in_root_set, const char *str, size_t bytelen, CCharmapToUni *cmap); /* * For construction: get a pointer to the string's underlying * buffer. Returns a pointer into which the caller can write. The * buffer starts after the length prefix. */ char *cons_get_buf() const { return ext_ + 2; } /* get the current construction length in bytes */ size_t cons_get_len() const { return vmb_get_len(ext_); } /* * Ensure space for a string under construction. This expands the * buffer if necessary. 'ptr' is a pointer into our buffer, giving the * current write position, and 'len' is the amount of data we want to * add. If we already have enough space, we'll simply return 'ptr'. * If not, we'll reallocate the buffer with enough space for the added * chunk plus the given overhead margin, and return a new pointer that * points to the same offset in the new buffer as 'ptr' did in the * original. */ char *cons_ensure_space(VMG_ char *ptr, size_t len, size_t margin); /* * Append a string to the buffer during construction, expanding the * buffer if necessary. 'ptr' is a pointer into our buffer to where * the string is to be appended. Returns the updated pointer to the * end of the appended data. */ char *cons_append(VMG_ char *ptr, const char *addstr, size_t addlen, size_t margin); /* append a character to the buffer during construction */ char *cons_append(VMG_ char *ptr, wchar_t ch, size_t margin); /* * Shrink the buffer to the given actual size to finalize construction. * 'ptr' is a pointer into the construction buffer; we'll reallocate * the buffer so that it's just big enough for the text up to the byte * before 'ptr', and set the string length accordingly. * * If the buffer is within a reasonable margin of the actual used size, * we'll ignore the reallocation request. Reallocation takes some work * (to find new memory and copy the string contents), so if the memory * savings won't be substantial, it's more efficient just to waste the * memory. In all likelihood the string will be collected as garbage * eventually anyway, so the extra space will probably only be tied up * temporarily. * * The caller must not call cons_set_len() prior to calling this, * because we take the current length from our internal length element. */ void cons_shrink_buffer(VMG_ char *ptr); /* * For construction: set my length. This can be used if the string * stored is smaller than the buffer allocated. This cannot be used to * expand the buffer, since this merely writes the length prefix and * does not reallocate the buffer. * * When used with a pointer, the pointer must point within our buffer; * we'll set the length according to the offset of the pointer from the * start of the buffer. This can be used for dynamic construction with * the final write pointer after filling in the buffer's contents. */ void cons_set_len(size_t len) { vmb_put_len(ext_, len); } void cons_set_len(char *ptr) { vmb_put_len(ext_, ptr - ext_ - 2); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* call a static property */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop); /* undo operations - strings are immutable and hence keep no undo */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { }; void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* reference operations - strings reference no other objects */ void mark_refs(VMG_ uint state) { } void remove_stale_weak_refs(VMG0_) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t) { ext_ = (char *)ptr; } /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* * add a value to the string -- this creates a new string by * appending the value to this string */ int add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* * Get a string representation of the object. This is trivial for a * string object - we simply return our extension, which contains * the string in the required format. */ const char *cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const { /* we are the string object */ new_str->set_obj(self); /* return our extension directly */ return ext_; } /* convert a value to string via reflection services in the bytecode */ static const char *reflect_to_str( VMG_ vm_val_t *new_str, char *result_buf, size_t result_buf_size, const vm_val_t *val, const char *fmt, ...); /* get the underlying string */ const char *get_as_string(VMG0_) const { return ext_; } /* cast to integer */ virtual long cast_to_int(VMG0_) const; /* cast to number */ virtual void cast_to_num(VMG_ vm_val_t *val, vm_obj_id_t self) const; /* parse a string as an integer value or (optionally) a BigNumber */ static void parse_num_val(VMG_ vm_val_t *retval, const char *str, size_t len, int radix, int int_only); /* * Static routine to add a value to a string constant. Creates a * new string by appending the given value to the given string * constant. The string constant must be stored in portable format: * the first two bytes are the length prefix, in UINT2 format, * giving the length of the string's contents not counting the * prefix itself; immediately following the length prefix are the * bytes of the string's contents. */ static void add_to_str(VMG_ vm_val_t *result, const vm_val_t *self, const vm_val_t *val); /* * Check a value for equality. We will match any constant string * that contains the same text as our string, and any other string * object with the same text. */ int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const; /* * Compare the string to another value. If the other value is a * constant string or string object, we'll perform a lexical * comparison of the string; other types are not comparable to * strings, so we'll throw an error for any other type. */ int compare_to(VMG_ vm_obj_id_t self, const vm_val_t *val) const; /* calculate a hash */ uint calc_hash(VMG_ vm_obj_id_t self, int depth) const; /* * Convert a value to a string. Throws an error if the value is not * convertible to a string. * * The result is stored in the given buffer, if possible, in portable * string format (with a portable UINT2 length prefix followed by the * string's bytes). If the buffer is not provided or is not large * enough to contain the result, we will allocate a new string object * and return its contents; since the string object will never be * referenced by anyone, it will be deleted in the next garbage * collection pass. In any case, we will return a pointer to a buffer * containing the result string. * * We'll fill in *new_obj with the new string object value, or nil if * we don't create a new string; this allows the caller to protect the * allocated object from garbage collection if necessary. * * 'flags' is a combination of TOSTR_xxx values (see vmobj.h). */ static const char *cvt_to_str(VMG_ vm_val_t *new_obj, char *result_buf, size_t result_buf_size, const vm_val_t *val, int radix, int flags); /* * Convert an integer to a string, storing the result in the given * buffer in portable string format (with length prefix). The radix * can be from 2 to 36. * * If is_signed is true, we'll show a hyphen as the first character. * Otherwise we'll treat the value as unsigned. * * For efficiency, we store the number at the end of the buffer (this * makes it easy to generate the number, since we need to generate * numerals in reverse order). We return a pointer to the result, * which may not start at the beginning of the buffer. * * 'flags' is a combination of TOSTR_xxx flags (see vmobj.h). */ static char *cvt_int_to_str(char *buf, size_t buflen, int32_t inval, int radix, int flags); /* * Allocate a string buffer large enough to hold a given value. * We'll use the provided buffer if possible. * * If the provided buffer is null or is not large enough, we'll * allocate a new string object with a large enough buffer to hold * the value, and return the object's extension as the buffer. * * The buffer size and requested size are in bytes. * * If we allocate a new object, we'll set new_obj to the object * value; otherwise we'll set new_obj to nil. */ static char *alloc_str_buf(VMG_ vm_val_t *new_obj, char *buf, size_t buf_size, size_t required_size); /* * Constant string equality test routine. Compares the given * constant string (in portable format, with leading UINT2 length * prefix followed by the string's text in UTF8 format) to the other * value. Returns true if the values are lexically identical, false * if not or if the other value is not a string of some kind. */ static int const_equals(VMG_ const char *str, const vm_val_t *val); /* * Constant string hash value calculation */ static uint const_calc_hash(const char *str); /* * Constant string magnitude comparison routine. Compares the given * constant string (in portable format) to the other value. Returns * a positive value if the constant string is lexically greater than * the other value, a negative value if the constant string is * lexically less than the other value, or zero if the two values * are identical. Throws an error for any other type of value. */ static int const_compare(VMG_ const char *str, const vm_val_t *val); /* * Find a substring within a string. Returns a pointer to to the start * of the substring within the string, or null if the substring isn't * found. If 'idxp' is non-null, we'll fill in *idxp with the * character index, starting at zero for the first character, of the * substring within the string. * * start_idx is the 1-based index where we start the search. If this * is negative, it's an index from the end of the string (-1 for the * last character). * * Both strings are in standard constant string format, with UINT2 * length prefixes. */ static const char *find_substr( VMG_ const char *str, int32_t start_idx, const char *substr, size_t *idxp); /* * Find a substring or RexPattern. Returns a pointer to the matching * substring, or null if no match is found. * * 'basestr' is the base string we're searching, and 'str' and 'len' * give the starting point and remaining length of the substring of * 'basestr' where the search actually begins. 'basestr' and 'str' * point directly to the bytes to search, not a VMB_LEN prefix (which * is why we have 'len' as a separate argument). We won't actually * search the portion of the base string before 'str', but we need to * know where the overall string starts to properly handle certain * regex assertions (e.g., '^'). * * 'substr' is the tads-style (VMB_LEN-prefixed) substring we're * looking for; if 'substr' is null, 'pat' is the regular expression * object to match, otherwise 'pat' is ignored. * * 'match_idx' returns with the CHARACTER index from 'str' (starting at * 0 for the first byte of 'str') of the substring we found, if * successful; otherwise this is undefined. * * 'match_len' returns with the BYTE length of the substring we * matched, if successful; otherwise this is undefined. */ static const char *find_substr( VMG_ const vm_val_t *strval, const char *basestr, const char *str, size_t len, const char *substr, class CVmObjPattern *pat, int *match_idx, int *match_len); /* find the last matching substring or pattern */ static const char *find_last_substr( VMG_ const vm_val_t *strval, const char *basestr, const char *str, size_t len, const char *substr, class CVmObjPattern *pat, int *match_idx, int *match_len); /* * Evaluate a property of a constant string value. Returns true if * we successfully evaluated the property, false if the property is * not one of the properties that the string class defines. */ static int const_get_prop(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, vm_prop_id_t prop, vm_obj_id_t *srcobj, uint *argc); /* evaluate a property */ virtual int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* property evaluator - undefined property */ static int getp_undef(VMG_ vm_val_t *, const vm_val_t *, const char *, uint *) { return FALSE; } /* property evaluator - get the length */ static int getp_len(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - extract a substring */ static int getp_substr(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - toUpper */ static int getp_upper(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - toLower */ static int getp_lower(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - toTitleCase */ static int getp_toTitleCase( VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - toFoldedCase */ static int getp_toFoldedCase(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* common handler for case conversions (toUpper, toLower, etc) */ static int gen_getp_case_conv(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc, const wchar_t *(*conv)(wchar_t)); /* property evaluator - find substring */ static int getp_find(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* common handler for find() and findLast() */ template<int dir> static inline int find_common( VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - convert to unicode */ static int getp_to_uni(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - htmlify */ static int getp_htmlify(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - startsWith */ static int getp_starts_with(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - endsWith */ static int getp_ends_with(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - mapToByteArray */ static int getp_to_byte_array(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - findReplace() - replace substring */ static int getp_replace(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - splice */ static int getp_splice(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - split */ static int getp_split(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - specialsToHtml */ static int getp_specialsToHtml(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - specialsToText */ static int getp_specialsToText(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - urlEncode */ static int getp_urlEncode(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - urlDecode */ static int getp_urlDecode(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - sha256 hash */ static int getp_sha256(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - md5 hash */ static int getp_md5(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - packBytes */ static int getp_packBytes(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return static_getp_packBytes(vmg_ retval, argc); } /* static property - packBytes */ static int static_getp_packBytes(VMG_ vm_val_t *retval, uint *argc); /* property evaluator - unpackBytes */ static int getp_unpackBytes(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - compareTo */ static int getp_compareTo(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - compareIgnoreCase */ static int getp_compareIgnoreCase(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - findLast */ static int getp_findLast(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - findAll */ static int getp_findAll(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - match */ static int getp_match(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); protected: /* create a string with no initial contents */ CVmObjString() { ext_ = 0; } /* create with a given buffer size in bytes */ CVmObjString(VMG_ size_t bytelen); /* create from a constant UTF-8 string */ CVmObjString(VMG_ const char *str, size_t bytelen); /* * Set the length of the string. This can be used after a string is * constructed to set the size of the actual stored string. */ void set_length(size_t bytelen) { vmb_put_len(ext_, bytelen); } /* copy bytes into the string buffer */ void copy_into_str(const char *str, size_t bytelen) { memcpy(ext_ + VMB_LEN, str, bytelen); } /* copy bytes into the string buffer starting at the given byte offset */ void copy_into_str(size_t ofs, const char *str, size_t bytelen) { memcpy(ext_ + VMB_LEN + ofs, str, bytelen); } /* * Copy bytes from the byte array into the string buffer, treating the * bytes as Latin-1 characters. Returns the number of bytes used in * the output buffer. If the output exceeds the available length in * 'len', we won't store any bytes past 'len', but we'll still * calculate the full needed length and return it. Call with len == 0 * to scan the string and get the required allocation length. */ static size_t copy_latin1_to_string(char *str, size_t len, class CVmDataSource *src); /* common handler for specialsToText and specialsToHtml */ static int specialsTo(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc, int html); /* property evaluation function table */ static int (*func_table_[])(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * A constant string is exactly like an ordinary string, except that our * contents come from the constant pool. We store a pointer directly to * our constant pool data rather than making a separate copy. The only * thing we have to do differently from an ordinary string is that we don't * delete our extension when we're deleted, since our extension is really * just a pointer into the constant pool. */ class CVmObjStringConst: public CVmObjString { public: /* notify of deletion */ void notify_delete(VMG_ int /*in_root_set*/) { /* * do nothing, since our extension is just a pointer into the * constant pool */ } /* create from constant pool data */ static vm_obj_id_t create(VMG_ const char *const_ptr); protected: /* construct from constant pool data */ CVmObjStringConst(VMG_ const char *const_ptr) { /* point our extension directly to the constant pool data */ ext_ = (char *)const_ptr; } }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassString: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "string/030008"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjString(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjString(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjString::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjString::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMSTR_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjString) ���������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmstrbuf.cpp����������������������������������������������������������������������0000664�0000000�0000000�00000110013�12650170723�0016226�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2010 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmstrbuf.cpp - StringBuffer object Function Notes Modified 12/13/09 MJRoberts - Creation */ #include <stdlib.h> #include <string.h> #include <assert.h> #include "t3std.h" #include "vmstrbuf.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmbif.h" #include "vmmeta.h" #include "vmstack.h" #include "vmrun.h" #include "vmstr.h" #include "utf8.h" /* ------------------------------------------------------------------------ */ /* * The largest buffer length we can store is the smaller of OSMALMAX or the * maximum (positive) value for an int32_t (2^31-1). */ const int32_t STRBUF_MAX_LEN = (OSMALMAX < 0x7fffffff ? OSMALMAX : 0x7fffffff); /* ------------------------------------------------------------------------ */ /* * Extended undo record. We attach this record to the standard undo record * via the 'ptrval' field. */ struct strbuf_undo_rec { /* action type */ enum strbuf_undo_action action; /* index of start of replacement */ int32_t idx; /* length of old replaced data */ int32_t old_len; /* length of new data */ int32_t new_len; /* old data */ wchar_t str[1]; }; /* ------------------------------------------------------------------------ */ /* * Allocate an extension structure */ vm_strbuf_ext *vm_strbuf_ext::alloc_ext(VMG_ CVmObjStringBuffer *self, int32_t alo, int32_t inc) { /* calculate how much space we need */ int32_t siz = sizeof(vm_strbuf_ext) + (alo-1)*sizeof(wchar_t); /* allocate the memory */ vm_strbuf_ext *ext = (vm_strbuf_ext *)G_mem->get_var_heap()->alloc_mem( siz, self); /* remember the sizes */ ext->alo = alo; ext->inc = inc; /* the buffer is currently empty */ ext->len = 0; /* return the new extension */ return ext; } /* * Expand an extension structure to make room for the given string length */ vm_strbuf_ext *vm_strbuf_ext::expand_ext(VMG_ CVmObjStringBuffer *self, vm_strbuf_ext *old_ext, int32_t new_len) { /* if the buffer is big enough to hold the new length, we're done */ if (old_ext->alo >= new_len) return old_ext; /* * figure the required new allocation size: we allocate in increments * of 'inc' from the old extension, so allocate enough for the new * length, rounded up */ int32_t new_alo = ((new_len + old_ext->inc - 1) / old_ext->inc) * old_ext->inc; vm_strbuf_ext *new_ext = alloc_ext(vmg_ self, new_alo, old_ext->inc); /* copy the current string buffer to the new extension */ new_ext->len = old_ext->len; memcpy(new_ext->buf, old_ext->buf, old_ext->len * sizeof(old_ext->buf[0])); /* delete the old memory */ G_mem->get_var_heap()->free_mem(old_ext); /* return the new extension */ return new_ext; } /* ------------------------------------------------------------------------ */ /* * StringBuffer object statics */ /* metaclass registration object */ static CVmMetaclassStringBuffer metaclass_reg_obj; CVmMetaclass *CVmObjStringBuffer::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjStringBuffer:: *CVmObjStringBuffer::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjStringBuffer::getp_undef, /* 0 */ &CVmObjStringBuffer::getp_len, /* 1 */ &CVmObjStringBuffer::getp_char_at, /* 2 */ &CVmObjStringBuffer::getp_append, /* 3 */ &CVmObjStringBuffer::getp_insert, /* 4 */ &CVmObjStringBuffer::getp_copyChars, /* 5 */ &CVmObjStringBuffer::getp_delete, /* 6 */ &CVmObjStringBuffer::getp_splice, /* 7 */ &CVmObjStringBuffer::getp_substr /* 8 */ }; /* ------------------------------------------------------------------------ */ /* * StringBuffer metaclass implementation */ /* * construction */ CVmObjStringBuffer::CVmObjStringBuffer(VMG_ int32_t alo, int32_t inc) { /* allocate our extension structure */ ext_ = (char *)vm_strbuf_ext::alloc_ext(vmg_ this, alo, inc); } /* * create a StringBuffer with a given allocation size */ vm_obj_id_t CVmObjStringBuffer::create(VMG_ int in_root_set, int32_t alo, int32_t inc) { /* allocate the object ID */ vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); /* create the C++ object */ new (vmg_ id) CVmObjStringBuffer(vmg_ alo, inc); /* return the new ID */ return id; } /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjStringBuffer::create_from_stack( VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id; int alo, inc; /* get the arguments */ if (argc == 0) { /* no arguments - use a default size */ alo = 256; inc = 256; } else if (argc == 1) { /* one argument - it's the initial size */ alo = CVmBif::pop_int_val(vmg0_); /* * figure a default increment: for small initial buffers, expand by * the buffer size; for medium sizes, expand by half the buffer * size; for large sizes, expand by a fixed maximum */ if (alo < 256) inc = alo; else if (alo < 4096) inc = alo/2; else inc = 2048; } else if (argc == 2) { /* the initial size and increment are both specified */ alo = CVmBif::pop_int_val(vmg0_); inc = CVmBif::pop_int_val(vmg0_); } else { /* invalid arguments */ err_throw(VMERR_WRONG_NUM_OF_ARGS); } /* enforce minimum and maximum sizes */ if (alo < 16) alo = 16; else if (alo > STRBUF_MAX_LEN) alo = STRBUF_MAX_LEN; if (inc < 16) inc = 16; else if (inc > STRBUF_MAX_LEN) inc = STRBUF_MAX_LEN; /* * allocate the object ID - this type of construction never creates a * root object */ id = vm_new_id(vmg_ FALSE, FALSE, FALSE); /* create the object */ new (vmg_ id) CVmObjStringBuffer(vmg_ alo, inc); /* return the new ID */ return id; } /* * notify of deletion */ void CVmObjStringBuffer::notify_delete(VMG_ int /*in_root_set*/) { /* free our additional data, if we have any */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* * set a property */ void CVmObjStringBuffer::set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) { /* no settable properties - throw an error */ err_throw(VMERR_INVALID_SETPROP); } /* * get a property */ int CVmObjStringBuffer::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from our base class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * apply an undo record */ void CVmObjStringBuffer::apply_undo(VMG_ struct CVmUndoRecord *undo_rec) { if (undo_rec->id.ptrval != 0) { strbuf_undo_rec *rec; vm_obj_id_t self = undo_rec->obj; /* get our private record from the standard undo record */ rec = (strbuf_undo_rec *)undo_rec->id.ptrval; /* check the action in the record */ switch(rec->action) { case STRBUF_UNDO_INS: /* we inserted text into the buffer; delete the new text */ delete_text(vmg_ self, rec->idx, rec->new_len, FALSE); break; case STRBUF_UNDO_DEL: /* we deleted text; re-insert the old text */ insert_text(vmg_ self, rec->idx, rec->str, rec->old_len, FALSE); break; case STRBUF_UNDO_REPL: /* we replaced text; splice the old text in place of the new */ splice_text(vmg_ self, rec->idx, rec->new_len, rec->str, rec->old_len, FALSE); break; } /* discard the private record */ t3free(rec); /* clear the pointer in the main record so we know it's gone */ undo_rec->id.ptrval = 0; } } /* * discard extra undo information */ void CVmObjStringBuffer::discard_undo(VMG_ CVmUndoRecord *rec) { /* delete our extra information record */ if (rec->id.ptrval != 0) { /* free the record */ t3free((strbuf_undo_rec *)rec->id.ptrval); /* clear the pointer so we know it's gone */ rec->id.ptrval = 0; } } /* * load from an image file */ void CVmObjStringBuffer::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from the image file */ void CVmObjStringBuffer::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); } /* * load or reload data from the image */ void CVmObjStringBuffer::load_image_data(VMG_ const char *ptr, size_t siz) { /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* read the string and allocation data from the image data */ uint32_t alo = osrp4(ptr); uint32_t inc = osrp4(ptr + 4); uint32_t len = osrp4(ptr + 8); /* limit the sizes */ if (alo < 16) alo = 16; else if (alo > STRBUF_MAX_LEN) alo = STRBUF_MAX_LEN; if (inc < 16) inc = 16; else if (inc > STRBUF_MAX_LEN) inc = STRBUF_MAX_LEN; if (len > alo) len = alo; /* skip to the string characters */ ptr += 12; /* allocate the extension */ vm_strbuf_ext *ext = vm_strbuf_ext::alloc_ext(vmg_ this, alo, inc); ext_ = (char *)ext; /* set the length */ ext->len = len; /* read the string data into the buffer */ for (wchar_t *dst = ext->buf ; len != 0 ; *dst++ = osrp2(ptr), ptr += 2, --len) ; } /* * save to a file */ void CVmObjStringBuffer::save_to_file(VMG_ class CVmFile *fp) { vm_strbuf_ext *ext = get_ext(); /* write our allocation and string length data */ fp->write_uint4(ext->alo); fp->write_uint4(ext->inc); fp->write_uint4(ext->len); /* write the string contents */ const wchar_t *src; int i; for (i = ext->len, src = ext->buf ; i != 0 ; --i) fp->write_uint2(*src++); } /* * restore from a file */ void CVmObjStringBuffer::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { const int32_t bufchars = 256; char buf[bufchars * sizeof(uint16_t)]; /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* read the fixed fields */ fp->read_bytes(buf, 12); int32_t alo = vmb_get_int4(buf); int32_t inc = vmb_get_int4(buf + 4); int32_t len = vmb_get_int4(buf + 8); /* allocate the extension structure */ vm_strbuf_ext *ext = vm_strbuf_ext::alloc_ext(vmg_ this, alo, inc); ext_ = (char *)ext; /* limit the length */ if (len > alo) len = alo; /* store it in the extension */ ext->len = len; /* read the string contents */ wchar_t *dst; int32_t rem; for (dst = ext->buf, rem = len ; rem != 0 ; ) { /* read one buffer-full, or 'rem', whichever is less */ int32_t cur = rem > bufchars ? bufchars : rem; fp->read_bytes(buf, cur * sizeof(uint16_t)); /* deduct this read from the total */ rem -= cur; /* decode the characters */ for (const char *src = buf ; cur != 0 ; *dst++ = osrp2(src), src += 2, --cur) ; } } /* * add an undo record */ void CVmObjStringBuffer::add_undo_rec( VMG_ vm_obj_id_t self, strbuf_undo_action action, int32_t idx, int32_t old_len, int32_t new_len) { /* * if we're not inserting or deleting anything, there's no change, so * there's no undo information to save */ if (old_len == 0 && new_len == 0) return; /* * replacing zero bytes is simply an insertion; substituting zero bytes * is a deletion */ if (action == STRBUF_UNDO_REPL && old_len == 0) action = STRBUF_UNDO_INS; if (action == STRBUF_UNDO_REPL && new_len == 0) action = STRBUF_UNDO_DEL; /* * Figure the size for the extension record. For an insertion, we * don't need any extra data, since we're replacing zero characters. * For a deletion or replacement, we need to make a copy of the * characters we're removing, so we need old_len extra wchar_t's. */ int32_t siz = sizeof(strbuf_undo_rec); if (action == STRBUF_UNDO_DEL || action == STRBUF_UNDO_REPL) siz += (old_len - 1) * sizeof(wchar_t); /* allocate our record extension */ strbuf_undo_rec *rec = (strbuf_undo_rec *)t3malloc(siz); /* set up the record */ rec->action = action; rec->idx = idx; rec->old_len = old_len; rec->new_len = new_len; /* if we're deleting or replacing text, save the old text */ if (action == STRBUF_UNDO_DEL || action == STRBUF_UNDO_REPL) memcpy(rec->str, get_ext()->buf + idx, old_len * sizeof(rec->str[0])); /* * Add the record to the global undo stream. (We don't have anything * to store in the 'value' field, so just store nil.) */ vm_val_t nilval; nilval.set_nil(); if (!G_undo->add_new_record_ptr_key(vmg_ self, rec, &nilval)) { /* * we didn't add an undo record, so our extra undo information * isn't actually going to be stored in the undo system - hence we * must delete our extra information */ t3free(rec); } } /* * Create a string representation of the number */ const char *CVmObjStringBuffer::cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const { /* make a string out of the whole buffer */ return substr_to_string(vmg_ new_str, 0, get_ext()->len); } /* * Test for equality */ int CVmObjStringBuffer::equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int /*depth*/) const { /* * we can compare our contents to another string buffer, or to a * regular string object */ const char *str; if (val->typ == VM_OBJ && is_string_buffer_obj(vmg_ val->val.obj)) { /* compare the buffers character by character */ CVmObjStringBuffer *other = (CVmObjStringBuffer *)vm_objp(vmg_ val->val.obj); int32_t len1 = get_ext()->len, len2 = other->get_ext()->len; const wchar_t *buf1 = get_ext()->buf, *buf2 = other->get_ext()->buf; /* if the lengths aren't equal, they're not equal */ if (len1 != len2) return FALSE; /* they're equal if the contents match exactly */ return memcmp(buf1, buf2, len1*sizeof(*buf1)) == 0; } else if ((str = val->get_as_string(vmg0_)) != 0) { /* compare to a constant string */ return equals_str(str); } else { /* the other value isn't comparable to string */ err_throw(VMERR_INVALID_COMPARISON); AFTER_ERR_THROW(return 0;) } } /* * test for equality against a constant string */ int CVmObjStringBuffer::equals_str(const char *str) const { /* get the buffer pointers and character lengths */ const wchar_t *p1 = get_ext()->buf; utf8_ptr p2((char *)str + VMB_LEN); int32_t len1 = get_ext()->len, len2 = p2.len(vmb_get_len(str)); /* if the lengths don't match, we can't be equal */ if (len1 != len2) return FALSE; /* compare character by character */ for (int32_t i = 0 ; i < len1 ; ++i, ++p1, p2.inc()) { if (*p1 != p2.getch()) return FALSE; } /* the characters match */ return TRUE; } /* * Compare value */ int CVmObjStringBuffer::compare_to(VMG_ vm_obj_id_t self, const vm_val_t *val) const { /* * we can compare our contents to another string buffer, or to a * regular string object */ const char *str; if (val->typ == VM_OBJ && is_string_buffer_obj(vmg_ val->val.obj)) { /* compare the buffers character by character */ CVmObjStringBuffer *other = (CVmObjStringBuffer *)vm_objp(vmg_ val->val.obj); int32_t len1 = get_ext()->len, len2 = other->get_ext()->len; const wchar_t *buf1 = get_ext()->buf, *buf2 = other->get_ext()->buf; for (int32_t i = 0 ; i < len1 && i < len2 ; ++i, ++buf1, ++buf2) { if (*buf1 < *buf2) return -1; else if (*buf1 > *buf2) return 1; } /* * They're equal up to the full length of the shorter string. If * we ran out of strings at the same time, they're equal; otherwise * the longer string compares greater */ return len1 - len2; } else if ((str = val->get_as_string(vmg0_)) != 0) { /* compare to a constant string */ return compare_str(str); } else { /* the other value isn't comparable to string */ err_throw(VMERR_INVALID_COMPARISON); AFTER_ERR_THROW(return 0;) } } /* * compare to a constant string */ int CVmObjStringBuffer::compare_str(const char *str) const { /* get the buffer pointers and character lengths */ const wchar_t *p1 = get_ext()->buf; utf8_ptr p2((char *)str + VMB_LEN); int32_t len1 = get_ext()->len, len2 = p2.len(vmb_get_len(str)); /* compare character by character */ for (int32_t i = 0 ; i < len1 ; ++i, ++p1, p2.inc()) { wchar_t c1 = *p1, c2 = p2.getch(); if (c1 < c2) return -1; else if (c1 > c2) return 1; } /* * the strings are equal up to the length of the shorter string; the * longer string compares higher */ return len1 - len2; } /* * Calculate my hash value. We yield the same hash value as a regular * string with our contents. (This is a requirement, since we compare * equal to a regular string with the same contents.) */ uint CVmObjStringBuffer::calc_hash(VMG_ vm_obj_id_t self, int) const { uint hash = 0; const wchar_t *p = get_ext()->buf; int32_t len = get_ext()->len; /* calculate the hash */ for (int32_t i = 0 ; i < len ; ++i) hash += *p++; /* return the result */ return hash; } /* * Adjust arguments to limits. '*idx' is the 0-based index of the start of * the segment we're extracting or replacing. If 'len' is non-null, '*len' * is the length of the existing chunk we're extracting, deleting, or * replacing. If 'ins' is non-null, '*ins' is the length of the new chunk * we're inserting or substituting for the deleted chunk. * * Note that if both 'len' and 'ins' are provided, we assume the operation * will delete '*len' characters and insert '*ins' characters in their * place. */ void CVmObjStringBuffer::adjust_args( int32_t *idx, int32_t *len, int32_t *ins) const { vm_strbuf_ext *ext = get_ext(); /* make sure the index is in the range 1..len */ if (*idx < 0) *idx = 0; else if (*idx > ext->len) *idx = ext->len; /* make sure the length is in range 0..(len-idx) */ if (len != 0) { if (*len < 0) *len = 0; else if (*idx + *len > ext->len) *len = ext->len - *idx; } /* make sure the insertion wouldn't push us past the limit */ if (ins != 0) { int32_t del = (len != 0 ? *len : 0); if (*ins < 0) *ins = 0; else if (*idx + *ins + ext->len - del > STRBUF_MAX_LEN) err_throw(VMERR_STR_TOO_LONG); } } /* * Calculate the length in bytes in UTF-8 format */ int32_t CVmObjStringBuffer::utf8_length() const { vm_strbuf_ext *ext = get_ext(); /* get our character length */ int32_t rem = ext->len; /* start with a zero length */ int32_t bytelen = 0; /* scan our characters */ for (const wchar_t *p = ext->buf ; rem > 0 ; ++p, --rem) bytelen += utf8_ptr::s_wchar_size(*p); /* return the result */ return bytelen; } /* * Copy a portion of the string to a buffer as UTF-8 bytes. * * 'idx' is an in-out variable. On input, it's the starting character * index requested for the copy. On output, it's updated to the index of * the next character after the last character copied. This can be used * for piecewise copies, since it's updated to point to the start of the * next piece after copying each piece. * * 'bytelen' is an in-out variable. On input, this is the number of bytes * requested to copy to the buffer. On output, it's the actual number of * bytes copied. This might be smaller than the request size, because (a) * we won't copy past the end of the string, and (b) we'll only copy whole, * well-formed character sequences, so if the requested number of bytes * would copy a fractional character, we'll omit that fractional character * and stop at the previous whole character. */ void CVmObjStringBuffer::to_utf8(char *buf, int32_t &idx, int32_t &bytelen) { vm_strbuf_ext *ext = get_ext(); /* keep the index within our limits */ adjust_args(&idx, 0, 0); /* we haven't copied any bytes yet */ int32_t actual = 0; /* set up a utf8 pointer for the output buffer */ utf8_ptr dst(buf); /* * copy characters until we reach the end of the string, or exhaust the * requested number of bytes */ for (actual = 0 ; actual < bytelen && idx < (int32_t)ext->len ; ++idx) { /* get the next source character */ wchar_t c = ext->buf[idx]; int clen = utf8_ptr::s_wchar_size(c); /* make sure it fits in the remaining output buffer space */ if (clen > bytelen - actual) break; /* store the character */ dst.setch(c); /* count it in the output size */ actual += clen; } /* return the actual length copied in bytelen */ bytelen = actual; } /* * Make a String object out of a substring of the buffer */ const char *CVmObjStringBuffer::substr_to_string( VMG_ vm_val_t *new_str, int32_t idx, int32_t len) const { vm_strbuf_ext *ext = get_ext(); /* keep the arguments within our limits */ adjust_args(&idx, &len, 0); /* calculate the size in bytes of the UTF-8 version of the string */ utf8_ptr p; int32_t bytelen = p.setwchars(ext->buf + idx, len, 0); /* allocate a string */ new_str->set_obj(CVmObjString::create(vmg_ FALSE, bytelen)); CVmObjString *str = (CVmObjString *)vm_objp(vmg_ new_str->val.obj); /* translate the string to UTF-8 into the buffer */ p.set(str->cons_get_buf()); p.setwchars(ext->buf + idx, len, bytelen); /* return the new string buffer */ return str->get_as_string(vmg0_); } /* * Index the buffer - returns the character at the given index */ int CVmObjStringBuffer::index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val) { /* get the index as an integer */ int32_t idx = index_val->num_to_int(vmg0_); /* adjust to zero-based indexing or a negative end-based index */ idx += (idx < 0 ? get_ext()->len : -1); /* ensure idx is in range */ if (idx < 0 || idx >= (int32_t)get_ext()->len) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* return a one-character string of the character at this index */ substr_to_string(vmg_ result, idx, 1); /* handled */ return TRUE; } /* * Set a character in the buffer by index */ int CVmObjStringBuffer::set_index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val) { /* the index must be an integer */ int32_t idx = index_val->num_to_int(vmg0_); /* adjust to zero-based indexing or a negative end-based index */ idx += (idx < 0 ? get_ext()->len : -1); /* ensure idx is in range */ if (idx < 0 || idx >= (int32_t)get_ext()->len) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* * If the new value is an integer, interpret it as a unicode character * value to set at the given character. Otherwise, cast it to a string * and take its first character. */ wchar_t ch; if (new_val->is_numeric(vmg0_)) { /* it's an integer - treat it as a unicode character value */ int32_t ich = new_val->num_to_int(vmg0_); if (ich < 0 || ich > 65535) err_throw(VMERR_BAD_VAL_BIF); /* it's in range - cast to wchar_t */ ch = (wchar_t)ich; } else { /* treat it as a string */ const char *str = new_val->get_as_string(vmg0_); /* get the first character */ utf8_ptr p((char *)str + VMB_LEN); ch = (vmb_get_len(str) > 0 ? p.getch() : 0); } /* splice the character at the desired index */ splice_text(vmg_ self, (int)idx, 1, &ch, 1, TRUE); /* we change in place, so the result is 'self' */ result->set_obj(self); /* handled */ return TRUE; } /* * Ensure that the buffer is big enough to hold the given number of * characters */ void CVmObjStringBuffer::ensure_space(VMG_ int32_t len) { /* if the desired new length exceeds the limit, it's an error */ if (len > STRBUF_MAX_LEN) err_throw(VMERR_STR_TOO_LONG); /* if the desired new length exceeds the current allocation, expand it */ if (len > (int32_t)get_ext()->alo) { ext_ = (char *)vm_strbuf_ext::expand_ext( vmg_ this, get_ext(), len); } } /* * Open a gap for an insertion and/or delete characters for a deletion. */ void CVmObjStringBuffer::splice_move( VMG_ int32_t idx, int32_t del, int32_t ins) { /* * if we're adding more than we're deleting, expand the buffer if * necessary to accommodate the added length */ if (ins > del) ensure_added_space(vmg_ ins - del); /* * if we're doing a net insertion or deletion, move the part of the * string after the deleted text so that it'll be aligned at the end of * the inserted text */ if (ins != del && idx + del < get_ext()->len) memmove(get_ext()->buf + idx + ins, get_ext()->buf + idx + del, (get_ext()->len - (idx + del)) * sizeof(wchar_t)); /* adjust the buffer length */ get_ext()->len += ins - del; } /* * splice text into the buffer */ void CVmObjStringBuffer::splice_text(VMG_ vm_obj_id_t self, int32_t idx, int32_t del_chars, const wchar_t *src, int32_t ins_chars, int undo) { /* check arguments */ adjust_args(&idx, &del_chars, &ins_chars); /* if desired, save undo */ if (undo) add_undo_rec(vmg_ self, STRBUF_UNDO_REPL, idx, del_chars, ins_chars); /* delete the old characters and open a gap for the new characters */ splice_move(vmg_ idx, del_chars, ins_chars); /* copy in the characters */ memcpy(get_ext()->buf + idx, src, ins_chars * sizeof(get_ext()->buf[0])); } /* * splice UTF-8 text into the buffer */ void CVmObjStringBuffer::splice_text(VMG_ vm_obj_id_t self, int32_t idx, int32_t del_chars, const char *src, int32_t ins_bytes, int undo) { /* figure the character length of the string */ utf8_ptr p((char *)src); int32_t ins_chars = p.len(ins_bytes); /* check arguments */ adjust_args(&idx, &del_chars, &ins_chars); /* if desired, save undo */ if (undo) add_undo_rec(vmg_ self, STRBUF_UNDO_REPL, idx, del_chars, ins_chars); /* open up a gap in the buffer */ splice_move(vmg_ idx, del_chars, ins_chars); /* decode characters into the buffer */ p.to_wchar(get_ext()->buf + idx, ins_chars, src, ins_bytes); } /* * property evaluator - get length */ int CVmObjStringBuffer::getp_len(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* return the content length */ retval->set_int(get_ext()->len); /* handled */ return TRUE; } /* * property evaluator - charAt */ int CVmObjStringBuffer::getp_char_at(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the index and adjust to 0-based or end-based */ int32_t idx = CVmBif::pop_int_val(vmg0_); idx += (idx < 0 ? get_ext()->len : -1); /* make sure it's in range */ if (idx < 0 || idx >= (int32_t)get_ext()->len) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* return the character at the given index, as an integer Unicode code */ retval->set_int(get_ext()->buf[idx]); /* handled */ return TRUE; } /* * property evaluator - append text */ int CVmObjStringBuffer::getp_append(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get or cast the string; leave it on the stack for gc protection */ vm_val_t strval; const char *str = G_stk->get(0)->cast_to_string(vmg_ &strval); *G_stk->get(0) = strval; /* insert the string at the end of the buffer */ insert_text(vmg_ self, get_ext()->len, str + VMB_LEN, vmb_get_len(str), TRUE); /* discard gc protection */ G_stk->discard(); /* handled */ retval->set_obj(self); return TRUE; } /* * property evaluator - insert */ int CVmObjStringBuffer::getp_insert(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(2); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the insertion index; adjust to 0-based or end-based indexing */ int32_t idx = CVmBif::pop_int_val(vmg0_); idx += (idx < 0 ? get_ext()->len : -1); /* get or cast the string; leave it on the stack for gc protection */ vm_val_t strval; const char *str = G_stk->get(0)->cast_to_string(vmg_ &strval); *G_stk->get(0) = strval; /* do the insert */ insert_text(vmg_ self, idx, str + VMB_LEN, vmb_get_len(str), TRUE); /* discard gc protection */ G_stk->discard(); /* handled */ retval->set_obj(self); return TRUE; } /* * property evaluator - copyChars */ int CVmObjStringBuffer::getp_copyChars(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(2); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the insertion index; adjust to 0-based or end-based indexing */ int32_t idx = CVmBif::pop_int_val(vmg0_); idx += (idx < 0 ? get_ext()->len : -1); /* get or cast the string; leave it on the stack for gc protection */ vm_val_t srcval; const char *src = G_stk->get(0)->cast_to_string(vmg_ &srcval); *G_stk->get(0) = srcval; /* * Get the character length of the new text. This is the number of * characters we're overwriting (effectively deleting) in the current * buffer. */ size_t src_bytes = vmb_get_len(src); src += VMB_LEN; /* get the new text's character length */ utf8_ptr srcp((char *)src); size_t src_chars = srcp.len(src_bytes); /* splice the text */ splice_text(vmg_ self, idx, src_chars, src, src_bytes, TRUE); /* discard gc protection */ G_stk->discard(); /* handled */ retval->set_obj(self); return TRUE; } /* * property evaluator - delete */ int CVmObjStringBuffer::getp_delete(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1, 2); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* pop the deletion index and adjust to 0-based or end-based */ int32_t idx = CVmBif::pop_int_val(vmg0_); idx += (idx < 0 ? get_ext()->len : -1); /* pop the length, if provided; if not, delete the rest of the string */ int32_t len; if (argc >= 2) len = CVmBif::pop_int_val(vmg0_); else len = get_ext()->len; /* do the delete */ delete_text(vmg_ self, idx, len, TRUE); /* handled */ retval->set_obj(self); return TRUE; } /* * property evaluator - splice */ int CVmObjStringBuffer::getp_splice(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(3); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the insertion index and adjust to 0-based or end-based */ int32_t idx = CVmBif::pop_int_val(vmg0_); idx += (idx < 0 ? get_ext()->len : -1); /* pop the deletion length */ int32_t del = CVmBif::pop_int_val(vmg0_); /* get or cast the string; leave it on the stack for gc protection */ vm_val_t strval; const char *str = G_stk->get(0)->cast_to_string(vmg_ &strval); *G_stk->get(0) = strval; /* do the insert */ splice_text(vmg_ self, idx, del, str + VMB_LEN, vmb_get_len(str), TRUE); /* discard gc protection */ G_stk->discard(); /* handled */ retval->set_obj(self); return TRUE; } /* * property evaluator - substr */ int CVmObjStringBuffer::getp_substr(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* remember the argument count */ uint argc = (oargc != 0 ? *oargc : 0); /* check arguments */ static CVmNativeCodeDesc desc(1, 2); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* retrieve the starting index, and adjust for 0-based or end-based */ int32_t idx = CVmBif::pop_long_val(vmg0_); idx += (idx < 0 ? get_ext()->len : -1); /* if there's a length, pop it; otherwise use the rest of the string */ int32_t len = get_ext()->len; if (argc >= 2) len = CVmBif::pop_long_val(vmg0_); /* retrieve the substring */ substr_to_string(vmg_ retval, idx, len); /* handled */ return TRUE; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmstrbuf.h������������������������������������������������������������������������0000664�0000000�0000000�00000033770�12650170723�0015711�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 2010 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmstrbuf.h - StringBuffer object Function Notes Modified 12/13/09 MJRoberts - Creation */ #ifndef VMSTRBUF_H #define VMSTRBUF_H #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" /* ------------------------------------------------------------------------ */ /* * The image file data block is arranged as follows: * *. UINT4 buffer_length *. UINT4 buffer_increment *. UINT4 string_length *. UINT2 string[string_length] * * buffer_length is the allocated size of the buffer. * * buffer_increment is the incremental allocation size; when we need to * expand the buffer, we'll allocate in multiples of this size. * * string_length is the length of the string data; this is the amount of * buffer space actually used. * * string is the string data, as 16-bit Unicode character values. (Note * that this is not stored in UTF-8 encoding as most other VM strings are. * This is instead an array of 16-bit character values.) */ /* ------------------------------------------------------------------------ */ /* * Our in-memory extension data structure, which mimics the image file * structure but uses native types. */ struct vm_strbuf_ext { /* allocate the structure */ static vm_strbuf_ext *alloc_ext(VMG_ class CVmObjStringBuffer *self, int32_t alo, int32_t inc); /* expand an existing extension */ static vm_strbuf_ext *expand_ext(VMG_ class CVmObjStringBuffer *self, vm_strbuf_ext *old_ext, int32_t new_len); /* the length of the string data (the part of the buffer in use) */ int32_t len; /* the allocated buffer size */ int32_t alo; /* the incremental allocation size */ int32_t inc; /* * The string data, as 16-bit unicode character values. We * overallocate the structure to make room for an actual array length * of [alo] elements here. */ wchar_t buf[1]; }; /* ------------------------------------------------------------------------ */ /* * undo action codes */ enum strbuf_undo_action { /* we inserted text into the buffer */ STRBUF_UNDO_INS, /* we deleted text from the buffer */ STRBUF_UNDO_DEL, /* we replaced text in the buffer */ STRBUF_UNDO_REPL }; /* ------------------------------------------------------------------------ */ /* * StringBuffer metaclass */ class CVmObjStringBuffer: public CVmObject { friend class CVmMetaclassStringBuffer; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is this a StringBuffer object? */ static int is_string_buffer_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create */ static vm_obj_id_t create(VMG_ int in_root_set, int32_t alo, int32_t inc); /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self) { /* we cannot be converted to constant data */ } /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *, vm_obj_id_t) { /* we don't reference anything */ } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * receive savepoint notification - we don't keep any * savepoint-relative records, so we don't need to do anything here */ void notify_new_savept() { } /* apply an undo record */ void apply_undo(VMG_ struct CVmUndoRecord *rec); /* discard an undo record */ void discard_undo(VMG_ struct CVmUndoRecord *); /* we don't reference anything */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* we don't reference anything */ void mark_refs(VMG_ uint) { } /* we don't keep any references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* * determine if we've been changed since loading - assume we have (if * we haven't, the only harm is the cost of unnecessarily reloading or * saving) */ int is_changed_since_load() const { return TRUE; } /* get a value by index */ int index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val); /* set a value by index */ int set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val); /* add an entry - does not generate undo */ void add_entry(VMG_ const vm_val_t *key, const vm_val_t *val); /* set or add an entry - does not generate undo */ void set_or_add_entry(VMG_ const vm_val_t *key, const vm_val_t *val); /* cast to string */ virtual const char *cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const; /* equals - we equal a string or string buffer with the same text */ virtual int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int) const; /* test for equality to a constant string */ int equals_str(const char *str) const; /* compare - we can compare to a string or string buffer */ virtual int compare_to(VMG_ vm_obj_id_t self, const vm_val_t *val) const; /* compare to a constant string - strcmp semantics */ int compare_str(const char *str) const; /* calculate my hash value */ virtual uint calc_hash(VMG_ vm_obj_id_t self, int /*depth*/) const; /* calculate the length in bytes of the contents in UTF-8 format */ int32_t utf8_length() const; /* * Copy a portion of the string to a buffer as UTF-8 bytes. * * 'idx' is an in-out variable. On input, it's the starting character * index requested for the copy. On output, it's updated to the index * of the next character after the last character copied. This can be * used for piecewise copies, since it's updated to point to the start * of the next piece after copying each piece. * * 'bytelen' is an in-out variable. On input, this is the number of * bytes requested to copy to the buffer. On output, it's the actual * number of bytes copied. This might be smaller than the request * size, because (a) we won't copy past the end of the string, and (b) * we'll only copy whole, well-formed character sequences, so if the * requested number of bytes would copy a fractional character, we'll * omit that fractional character and stop at the previous whole * character. */ void to_utf8(char *buf, int32_t &idx, int32_t &bytelen); /* ensure there's enough space for the given buffer length */ void ensure_space(VMG_ int32_t len); /* ensure there's enough space for the given ADDED buffer length */ void ensure_added_space(VMG_ int32_t len) { ensure_space(vmg_ get_ext()->len + len); } /* * Get my string buffer. Use with caution; the underlying buffer * pointer can change if we modify the contents. */ const wchar_t *get_buf() const { return get_ext()->buf; } /* get the current character length */ size_t get_len() const { return get_ext()->len; } protected: /* get my extension data */ vm_strbuf_ext *get_ext() const { return (vm_strbuf_ext *)ext_; } /* create a string object from a substring of the buffer */ const char *substr_to_string( VMG_ vm_val_t *new_str, int32_t idx, int32_t len) const; /* adjust arguments to make sure they're within the buffer */ void adjust_args(int32_t *idx, int32_t *len, int32_t *ins) const; /* move the tail of the buffer for a splice operation */ void splice_move(VMG_ int32_t idx, int32_t del, int32_t ins); /* load or reload image data */ void load_image_data(VMG_ const char *ptr, size_t siz); /* create a with no initial contents */ CVmObjStringBuffer() { ext_ = 0; } /* create a string buffer with the given buffer size */ CVmObjStringBuffer(VMG_ int32_t alo, int32_t inc); /* insert text into the buffer */ void insert_text(VMG_ vm_obj_id_t self, int32_t idx, const wchar_t *src, int32_t chars, int undo) { splice_text(vmg_ self, idx, 0, src, chars, undo); } /* insert a character */ void insert_char(VMG_ vm_obj_id_t self, int32_t idx, wchar_t ch, int undo) { splice_text(vmg_ self, idx, 0, &ch, 1, undo); } /* insert UTF-8 text into the buffer */ void insert_text(VMG_ vm_obj_id_t self, int32_t idx, const char *src, int32_t bytes, int undo) { splice_text(vmg_ self, idx, 0, src, bytes, undo); } /* delete text from the buffer */ void delete_text(VMG_ vm_obj_id_t self, int32_t idx, int32_t chars, int undo) { splice_text(vmg_ self, idx, chars, (wchar_t *)0, 0, undo); } /* splice text into the buffer */ void splice_text(VMG_ vm_obj_id_t self, int32_t idx, int32_t del_chars, const wchar_t *src, int32_t ins_chars, int undo); void splice_text(VMG_ vm_obj_id_t self, int32_t idx, int32_t del_chars, const char *src, int32_t ins_bytes, int undo); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - get length */ int getp_len(VMG_ vm_obj_id_t, vm_val_t *, uint *); /* property evaluator - get a character at an index position */ int getp_char_at(VMG_ vm_obj_id_t, vm_val_t *, uint *); /* property evaluator - append */ int getp_append(VMG_ vm_obj_id_t, vm_val_t *, uint *); /* property evaluator - copyChars */ int getp_copyChars(VMG_ vm_obj_id_t, vm_val_t *, uint *); /* property evaluator - insert */ int getp_insert(VMG_ vm_obj_id_t, vm_val_t *, uint *); /* property evaluator - delete */ int getp_delete(VMG_ vm_obj_id_t, vm_val_t *, uint *); /* property evaluator - splice */ int getp_splice(VMG_ vm_obj_id_t, vm_val_t *, uint *); /* property evaluator - substr */ int getp_substr(VMG_ vm_obj_id_t, vm_val_t *, uint *); /* add a record to the global undo stream */ void add_undo_rec(VMG_ vm_obj_id_t self, enum strbuf_undo_action action, int32_t idx, int32_t old_len, int32_t new_len); /* property evaluation function table */ static int (CVmObjStringBuffer::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * StringBuffer registration table object */ class CVmMetaclassStringBuffer: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "stringbuffer/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjStringBuffer(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjStringBuffer(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjStringBuffer::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjStringBuffer:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMSTRBUF_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjStringBuffer) ��������qtads-2.1.7/tads3/vmstrcmp.cpp����������������������������������������������������������������������0000664�0000000�0000000�00000105314�12650170723�0016241�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmstrcmp.cpp - T3 String Comparator intrinsic class Function Notes Modified 09/05/02 MJRoberts - Creation */ #include <stdlib.h> #include <os.h> #include "utf8.h" #include "vmuni.h" #include "vmtype.h" #include "vmobj.h" #include "vmmeta.h" #include "vmglob.h" #include "vmstrcmp.h" #include "vmstack.h" #include "vmbif.h" #include "vmfile.h" #include "vmlst.h" #include "vmstr.h" #include "vmrun.h" /* ------------------------------------------------------------------------ */ /* * Statics */ /* metaclass registration */ static CVmMetaclassStrComp metaclass_reg_obj; CVmMetaclass *CVmObjStrComp::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjStrComp:: *CVmObjStrComp::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjStrComp::getp_undef, &CVmObjStrComp::getp_calc_hash, &CVmObjStrComp::getp_match_values }; /* ------------------------------------------------------------------------ */ /* * notify of deletion */ void CVmObjStrComp::notify_delete(VMG_ int in_root_set) { /* delete my extension data */ delete_ext(vmg0_); } /* * Delete my extension data */ void CVmObjStrComp::delete_ext(VMG0_) { vmobj_strcmp_ext *ext = get_ext(); /* if I have an extension, delete it */ if (ext != 0) { size_t i; /* delete each first-level mapping table */ for (i = 0 ; i < countof(ext->equiv) ; ++i) { /* if this table is present, delete it */ if (ext->equiv[i] != 0) t3free(ext->equiv[i]); } /* delete and forget our extension */ G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjStrComp::set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { /* we have no properties to set */ err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * Get a property */ int CVmObjStrComp::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the function, if we found it */ if ((this->*func_table_[func_idx])(vmg_ self, val, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* not found - inherit default handling */ return CVmObject::get_prop(vmg_ prop, val, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * Abstract equivalence mapping reader. This can be implemented on * different underlying data sources. */ class CVmObjStrCompMapReader { public: /* * Read the next equivalence mapping. We fill in *ref_ch with the * reference character, *uc_result_flags with the upper-case result * flags, and *lc_result_flags with the lower-case result flags. On * input, *value_ch_cnt is the maximum number of value characters we * can store in the buffer at val_buf; on return, this is the number * of characters in the mapping, which might be higher than the number * originally in the buffer. In any case, we will write no more than * the allowed buffer size as given by *value_ch_cnt on input (so we * might indicate a higher number than we actually wrote: we always * return the actual value mapping size, even if we couldn't store the * whole thing because of a lack of buffer space). * * This routine should retrieve one mapping each time it's called, and * then move on to the next mapping. The caller is responsible for * making sure that this routine is called the correct number of * times, so we don't have to worry about running out of mappings. */ virtual void read_mapping(VMG_ wchar_t *ref_ch, unsigned long *uc_result_flags, unsigned long *lc_result_flags, wchar_t *val_buf, size_t *value_ch_cnt) = 0; }; /* * Stream-based mapping reader. This reads mappings from a stream that * uses our serialization format for image and saved-state files. */ class CVmObjStrCompMapReaderStream: public CVmObjStrCompMapReader { public: CVmObjStrCompMapReaderStream(CVmStream *str) { str_ = str; } /* read a mapping */ virtual void read_mapping(VMG_ wchar_t *ref_ch, unsigned long *uc_result_flags, unsigned long *lc_result_flags, wchar_t *val_buf, size_t *value_ch_cnt) { size_t copy_limit; size_t copy_size; size_t i; /* limit our value character copying to the actual buffer size */ copy_limit = *value_ch_cnt; /* read the header values */ *ref_ch = (wchar_t)str_->read_uint2(); copy_size = *value_ch_cnt = str_->read_byte(); *uc_result_flags = str_->read_uint4(); *lc_result_flags = str_->read_uint4(); /* limit copying to the actual buffer size */ if (copy_size > copy_limit) copy_size = copy_limit; /* read the values */ for (i = 0 ; i < copy_size ; ++i) *val_buf++ = (wchar_t)str_->read_uint2(); /* skip any values from the input that we weren't able to store */ for ( ; i < *value_ch_cnt ; ++i) str_->read_uint2(); } protected: /* my stream */ CVmStream *str_; }; /* * Constructor-list mapping reader. This reads mappings from list data in * the format that we use in our constructor. */ class CVmObjStrCompMapReaderList: public CVmObjStrCompMapReader { public: CVmObjStrCompMapReaderList(const vm_val_t *lst) { /* remember the list */ lst_ = lst; /* start at the first element */ idx_ = 1; } /* read a mapping */ virtual void read_mapping(VMG_ wchar_t *ref_ch, unsigned long *uc_result_flags, unsigned long *lc_result_flags, wchar_t *val_buf, size_t *value_ch_cnt) { vm_val_t val; vm_val_t sublst; const char *refstr; const char *valstr; size_t copy_rem; size_t rem; utf8_ptr p; /* note the size limit of the caller's value string buffer */ copy_rem = *value_ch_cnt; /* retrieve the next element of the list */ lst_->ll_index(vmg_ &sublst, idx_); /* get the value as a sublist */ if (!sublst.is_listlike(vmg0_) || sublst.ll_length(vmg0_) < 0) err_throw(VMERR_BAD_TYPE_BIF); /* retrieve the reference character string (sublst's 1st element) */ sublst.ll_index(vmg_ &val, 1); refstr = val.get_as_string(vmg0_); /* retrieve the value string (sublst's 2nd element) */ sublst.ll_index(vmg_ &val, 2); valstr = val.get_as_string(vmg0_); /* make sure the reference and value strings are indeed strings */ if (refstr == 0 || valstr == 0) err_throw(VMERR_BAD_TYPE_BIF); /* we need at least one character in each string */ if (vmb_get_len(refstr) == 0 || vmb_get_len(valstr) == 0) err_throw(VMERR_BAD_VAL_BIF); /* fill in the caller's reference character from the ref string */ *ref_ch = utf8_ptr::s_getch(refstr + VMB_LEN); /* store the characters of the value string, up to the buffer limit */ p.set((char *)valstr + VMB_LEN); rem = vmb_get_len(valstr); for (*value_ch_cnt = 0 ; rem != 0 ; p.inc(&rem)) { /* if we have room, copy this character */ if (copy_rem != 0) { /* copy the character */ *val_buf++ = p.getch(); /* count it */ --copy_rem; } /* count it in the actual length */ ++(*value_ch_cnt); } /* get the upper-case flags (sublst's 3rd element) */ sublst.ll_index(vmg_ &val, 3); if (val.typ != VM_INT) err_throw(VMERR_BAD_TYPE_BIF); *uc_result_flags = val.val.intval; /* get the lower-case flags (sublst's 4th element) */ sublst.ll_index(vmg_ &val, 4); if (val.typ != VM_INT) err_throw(VMERR_BAD_TYPE_BIF); *lc_result_flags = val.val.intval; /* we're done with this mapping, so advance to the next one */ ++idx_; } protected: /* my list data */ const vm_val_t *lst_; /* the list index of the next mapping to retrieve */ size_t idx_; }; /* ------------------------------------------------------------------------ */ /* * Create from stack. We take the following constructor arguments: * * trunc_len (int: truncation length) *. case_sensitive (bool: case sensitivity flag) *. mappings (list: equivalence mappings) * * Our equivalence mappings are given as a list of lists; the main list * consists of one sublist per mapping. Each mapping sublist looks like * this: * * ['ref_char', 'val_string', uc_flags, lc_flag] * * The 'ref_char' is a one-character string giving the reference character * of the mapping, and 'val_string' is a string of one or more characters * that can match the reference character in a value (input) string. * uc_flags and lc_flags are integers giving the upper-case and lower-case * flags (respectively) that are to be added to the match result code when * the mapping is used to match a pair of strings. */ vm_obj_id_t CVmObjStrComp::create_from_stack( VMG_ const uchar **pc_ptr, uint argc) { size_t trunc_len; int case_sensitive; const vm_val_t *lst; vm_obj_id_t id; CVmObjStrComp *obj; size_t equiv_cnt; size_t total_chars; /* check arguments */ if (argc != 3) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* pop the truncation length parameter */ if (G_stk->get(0)->typ == VM_NIL) { /* it's nil, so truncation is not allowed */ trunc_len = 0; G_stk->discard(); } else { /* retrieve the truncation length as an integer */ trunc_len = CVmBif::pop_int_val(vmg0_); } /* get the case sensitivity flag */ case_sensitive = CVmBif::pop_bool_val(vmg0_); /* * retrieve the mapping list, but leave it on the stack (for gc * protection) */ if (G_stk->get(0)->typ == VM_NIL) { /* there are no mappings */ lst = 0; equiv_cnt = 0; total_chars = 0; } else { size_t i; int ec; /* get the list value from the argument */ lst = G_stk->get(0); if (!lst->is_listlike(vmg0_) || (ec = lst->ll_length(vmg0_)) < 0) err_throw(VMERR_BAD_TYPE_BIF); /* run through the list and count the value string characters */ for (i = 1, total_chars = 0, equiv_cnt = ec ; i <= equiv_cnt ; ++i) { vm_val_t sublst; vm_val_t val; const char *strp; utf8_ptr ustrp; /* get this mapping from the list */ lst->ll_index(vmg_ &sublst, i); /* make sure it's a sublist */ if (!sublst.is_listlike(vmg0_)) err_throw(VMERR_BAD_TYPE_BIF); /* * get the second element of the mapping sublist - this is the * value string */ sublst.ll_index(vmg_ &val, 2); if ((strp = val.get_as_string(vmg0_)) == 0) err_throw(VMERR_BAD_TYPE_BIF); /* add the character length of the string to the total */ ustrp.set((char *)strp + VMB_LEN); total_chars += ustrp.len(vmb_get_len(strp)); } } /* create the new object */ id = vm_new_id(vmg_ FALSE, FALSE, FALSE); obj = new (vmg_ id) CVmObjStrComp(); /* set up a list-based mapping reader */ CVmObjStrCompMapReaderList reader(lst); /* allocate and initialize the new object's extension */ obj->alloc_ext(vmg_ trunc_len, case_sensitive, equiv_cnt, total_chars, &reader); /* discard the gc protection */ G_stk->discard(); /* return the new object */ return id; } /* ------------------------------------------------------------------------ */ /* * Load from an image file */ void CVmObjStrComp::load_from_image(VMG_ vm_obj_id_t /*self*/, const char *ptr, size_t len) { /* load my image data */ CVmReadOnlyMemoryStream str(ptr, len); load_from_stream(vmg_ &str); } /* * Load from an abstract stream */ void CVmObjStrComp::load_from_stream(VMG_ CVmStream *str) { unsigned int trunc_len; unsigned int flags; unsigned int equiv_cnt; unsigned int total_chars; /* load the fixed header */ trunc_len = str->read_uint2(); flags = str->read_uint2(); equiv_cnt = str->read_uint2(); total_chars = str->read_uint2(); /* set up a stream-based mapping reader */ CVmObjStrCompMapReaderStream reader(str); /* allocate and initialize our extension */ alloc_ext(vmg_ trunc_len, (flags & 0x0001) != 0, equiv_cnt, total_chars, &reader); } /* * Allocate and initialize our extension */ void CVmObjStrComp::alloc_ext(VMG_ size_t trunc_len, int case_sensitive, size_t equiv_cnt, size_t total_chars, CVmObjStrCompMapReader *reader) { size_t siz; vmobj_strcmp_ext *ext; vmobj_strcmp_equiv *nxt_equiv; wchar_t *nxt_ch; size_t ch_rem; size_t i; size_t idx1, idx2; /* delete my extension, if I have one already */ delete_ext(vmg0_); /* * Calculate how much space we need for our extension. In addition to * the fixed part, allocate space for one vmobj_strcmp_equiv structure * per mapping, plus the wchar_t's for the value mappings. */ siz = sizeof(vmobj_strcmp_ext) + (equiv_cnt * sizeof(vmobj_strcmp_equiv)) + (total_chars * sizeof(wchar_t)); /* allocate our new extension */ ext_ = (char *)G_mem->get_var_heap()->alloc_mem(siz, this); ext = get_ext(); /* * set up our suballocation pool pointers: put the equivalence mapping * structures after the fixed part of the extension, and put the * wchar_t's after the equivalence mappings */ nxt_equiv = (vmobj_strcmp_equiv *)(ext + 1); nxt_ch = (wchar_t *)(&nxt_equiv[equiv_cnt]); ch_rem = total_chars; /* initialize the extension structure */ ext->trunc_len = trunc_len; ext->case_sensitive = case_sensitive; /* * we have no equivalence mappings installed yet, so clear out the * first tier of the mapping array */ for (i = 0 ; i < countof(ext->equiv) ; ++i) ext->equiv[i] = 0; /* load the mappings */ for (i = 0 ; i < equiv_cnt ; ++i, ++nxt_equiv) { wchar_t ref_ch; /* * set up our equivalent's value buffer with the remainder of our * main buffer */ nxt_equiv->val_ch = nxt_ch; nxt_equiv->val_ch_cnt = ch_rem; /* read the mapping */ reader->read_mapping(vmg_ &ref_ch, &nxt_equiv->uc_result_flags, &nxt_equiv->lc_result_flags, nxt_equiv->val_ch, &nxt_equiv->val_ch_cnt); /* deduct this mapping from our main value character buffer */ nxt_ch += nxt_equiv->val_ch_cnt; ch_rem -= nxt_equiv->val_ch_cnt; /* if we don't have a first-tier table for this character, add one */ idx1 = (ref_ch >> 8) & 0xFF; idx2 = (ref_ch & 0xFF); if (ext->equiv[idx1] == 0) { vmobj_strcmp_equiv **p; size_t j; /* allocate a first-tier table for this index */ ext->equiv[idx1] = p = (vmobj_strcmp_equiv **)t3malloc( 256 * sizeof(vmobj_strcmp_equiv *)); /* clear out the first-tier table */ for (j = 0 ; j < 256 ; ++j, ++p) *p = 0; } /* set the mapping for this character */ ext->equiv[idx1][idx2] = nxt_equiv; } } /* ------------------------------------------------------------------------ */ /* * save to a file */ void CVmObjStrComp::save_to_file(VMG_ class CVmFile *fp) { /* write our data to the file */ CVmFileStream str(fp); write_to_stream(vmg_ &str, 0); } /* * Serialize to a stream */ ulong CVmObjStrComp::write_to_stream(VMG_ CVmStream *str, ulong *bytes_avail) { wchar_t ref_ch_base; vmobj_strcmp_ext *ext = get_ext(); size_t i; vmobj_strcmp_equiv ***p; size_t total_value_ch; size_t equiv_cnt; size_t need_size; /* get the mapping totals */ count_equiv_mappings(&equiv_cnt, &total_value_ch); /* * Calculate our space needs. We need 8 bytes for the fixed header, * 11 bytes per equivalent mapping, and 2 bytes per value string * character. */ need_size = 8 + (11 * equiv_cnt) + (2 * total_value_ch); /* if we have a size limit, check to make sure we can abide by it */ if (bytes_avail != 0 && need_size > *bytes_avail) { /* * there's not enough space in the output stream for us, so don't * write anything at all; simply return the amount of space we * need */ return need_size; } /* write out the serialization structure header */ str->write_uint2(ext->trunc_len); str->write_uint2(ext->case_sensitive ? 0x0001 : 0x0000); str->write_uint2(equiv_cnt); str->write_uint2(total_value_ch); /* run through our equivalence table again and write the mappings */ for (ref_ch_base = 0, i = 0, p = ext->equiv ; i < countof(ext->equiv) ; ++i, ++p, ref_ch_base += 256) { vmobj_strcmp_equiv **ep; size_t j; /* if this first-tier mapping is unused, skip it */ if (*p == 0) continue; /* run through our second-level table */ for (j = 0, ep = *p ; j < 256 ; ++j, ++ep) { /* if this mapping is used, write it out */ if (*ep != 0) { size_t k; wchar_t *vp; /* write the fixed part of the mapping */ str->write_uint2(ref_ch_base + j); str->write_byte((uchar)(*ep)->val_ch_cnt); str->write_uint4((*ep)->uc_result_flags); str->write_uint4((*ep)->lc_result_flags); /* write the value mapping characters */ for (k = (*ep)->val_ch_cnt, vp = (*ep)->val_ch ; k != 0 ; --k, ++vp) { /* write this character */ str->write_uint2(*vp); } } } } /* return our space needs */ return need_size; } /* * Count the equivalence mappings. */ void CVmObjStrComp::count_equiv_mappings(size_t *equiv_cnt, size_t *total_value_ch) { vmobj_strcmp_ext *ext = get_ext(); size_t i; vmobj_strcmp_equiv ***p; /* run through our table and count up the mappings */ for (*total_value_ch = 0, *equiv_cnt = 0, i = 0, p = ext->equiv ; i < countof(ext->equiv) ; ++i, ++p) { vmobj_strcmp_equiv **ep; size_t j; /* if this first-tier mapping is unused, skip it */ if (*p == 0) continue; /* run through our second-level table */ for (j = 0, ep = *p ; j < 256 ; ++j, ++ep) { /* if this mapping is used, count it */ if (*ep != 0) { /* count this equivalent mapping */ ++(*equiv_cnt); /* count its value mapping characters in the total */ *total_value_ch += (*ep)->val_ch_cnt; } } } } /* ------------------------------------------------------------------------ */ /* * restore from a file */ void CVmObjStrComp::restore_from_file(VMG_ vm_obj_id_t /*self*/, class CVmFile *fp, CVmObjFixup *) { /* load from the file */ CVmFileStream str(fp); load_from_stream(vmg_ &str); } /* ------------------------------------------------------------------------ */ /* * property evaluator - calculate a hash value */ int CVmObjStrComp::getp_calc_hash(VMG_ vm_obj_id_t /*self*/, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(1); const char *strp; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* * retrieve the string argument (it must be a string), but leave it on * the stack for now, for gc protection */ strp = G_stk->get(0)->get_as_string(vmg0_); if (strp == 0) err_throw(VMERR_BAD_TYPE_BIF); /* calculate the hash value and return it */ retval->set_int(calc_str_hash(strp + VMB_LEN, vmb_get_len(strp))); /* discard gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* * Hash adder */ struct StrCompHashAdder { StrCompHashAdder(size_t trunc_len) { this->trunc_len = trunc_len; this->has_trunc = (trunc_len != 0); hash = 0; } int add(wchar_t ch) { /* add the character to the hash */ hash += ch; hash &= 0xFFFF; /* if there's a truncation limit, count this against the limit */ if (has_trunc && --trunc_len == 0) { /* we've reached the limit */ return FALSE; } else { /* haven't reached the limit yet */ return TRUE; } } int done() const { return has_trunc && trunc_len == 0; } /* truncation limit */ size_t trunc_len; /* is there a truncation limit? */ int has_trunc; /* the hash code */ unsigned int hash; }; /* * Calculate a hash value */ unsigned int CVmObjStrComp::calc_str_hash(const char *strp, size_t len) { /* get my extension */ vmobj_strcmp_ext *ext = get_ext(); /* set up to scan the string */ utf8_ptr p((char *)strp); /* * Scan the string. Limit the scan to our truncation length, counting * substitution expansions and case folding expansions, because we * don't want to distinguish hash buckets beyond the truncation point. * If we did, a truncated string wouldn't hash into the same bucket as * a longer string it matches; all matching strings are required to go * into the same bucket, so we can't have such a hash mismatch. */ StrCompHashAdder hash(ext->trunc_len); for ( ; len != 0 ; p.inc(&len)) { /* get the current character */ wchar_t ch = p.getch(); /* check for a substitution mapping for this character */ vmobj_strcmp_equiv **t1, *eq; if ((t1 = ext->equiv[(ch >> 8) & 0xFF]) != 0 && (eq = t1[ch & 0xFF]) != 0) { /* * This character has a mapping, so add the contribution from * the canonical form of the character, which is the value * side of the mapping. */ wchar_t *vp; size_t vlen; for (vp = eq->val_ch, vlen = eq->val_ch_cnt ; vlen != 0 ; ++vp, --vlen) { /* get this character */ ch = *vp; /* * use case folding if we're insensitive to case and the * character has a case folding */ const wchar_t *f; if (!ext->case_sensitive && (f = t3_to_fold(ch)) != 0) { /* add the folded expansion to the hash */ for (const wchar_t *f = t3_to_fold(ch) ; *f != 0 ; ++f) { if (!hash.add(*f)) return hash.hash; } } else { if (!hash.add(ch)) return hash.hash; } } } else { /* * if we're not sensitive to case, and there's a case folding * available, use the case-folded representation of a character * for its hash value */ const wchar_t *f; if (!ext->case_sensitive && (f = t3_to_fold(ch)) != 0) { /* add the folded expansion to the hash */ for (const wchar_t *f = t3_to_fold(ch) ; *f != 0 ; ++f) { if (!hash.add(*f)) return hash.hash; } } else { /* exact case only - add the character as-is */ if (!hash.add(ch)) return hash.hash; } } } /* return the hash code */ return hash.hash; } /* ------------------------------------------------------------------------ */ /* * Pre-defined return flag values. */ #define RF_MATCH 0x0001 /* the string matched */ #define RF_CASEFOLD 0x0002 /* matched with case folding */ #define RF_TRUNC 0x0004 /* matched with truncation */ /* * property evaluator - calculate a hash value */ int CVmObjStrComp::getp_match_values(VMG_ vm_obj_id_t /*self*/, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(2); const char *valstr; const char *refstr; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* retrieve the strings, but leave them on the stack for gc protection */ valstr = G_stk->get(0)->get_as_string(vmg0_); refstr = G_stk->get(1)->get_as_string(vmg0_); /* make sure they're valid strings */ if (valstr == 0 || refstr == 0) err_throw(VMERR_BAD_TYPE_BIF); /* compare the strings and return the result */ retval->set_int(match_strings(valstr + VMB_LEN, vmb_get_len(valstr), refstr + VMB_LEN, vmb_get_len(refstr))); /* discard the gc protection */ G_stk->discard(2); /* handled */ return TRUE; } /* * Match two strings */ unsigned long CVmObjStrComp::match_strings(const char *valstr, size_t vallen, const char *refstr, size_t reflen) { vmobj_strcmp_ext *ext = get_ext(); utf8_ptr valp; utf8_ptr refp; unsigned long ret; int fold_case = !(ext->case_sensitive); /* set up to scan the strings */ valp.set((char *)valstr); refp.set((char *)refstr); /* start with no return flags */ ret = 0; /* scan the strings */ while (vallen != 0 && reflen != 0) { /* get each character */ wchar_t valch = valp.getch(); wchar_t refch = refp.getch(); /* check for an exact match first */ if (refch == valch) { /* it's an exact match - skip this character in each string */ valp.inc(&vallen); refp.inc(&reflen); continue; } /* check for a case-folded match if we're insensitive to case */ if (fold_case && t3_compare_case_fold_min(valp, vallen, refp, reflen) == 0) { /* note in the flags that we have differing cases in the match */ ret |= RF_CASEFOLD; /* keep going */ continue; } /* check for a reference equivalence mapping */ vmobj_strcmp_equiv **t1; vmobj_strcmp_equiv *eq; if ((t1 = ext->equiv[(refch >> 8) & 0xFF]) != 0 && (eq = t1[refch & 0xFF]) != 0) { /* * In case we match, apply the appropriate flags added for the * equivalence mapping, based on the case of the first value * character we're testing. (If we don't match, we'll simply * return failure, so it won't matter that we messed with the * flags.) */ ret |= (t3_is_upper(valch) ? eq->uc_result_flags : eq->lc_result_flags); /* match each character from the mapping string */ const wchar_t *vp; size_t vlen; for (vp = eq->val_ch, vlen = eq->val_ch_cnt ; vallen != 0 && vlen != 0 ; ) { /* get this character */ refch = *vp; /* if we have an exact match, keep going */ if (refch == valch) { /* matched - skip this character in each string */ valp.inc(&vallen); ++vp, --vlen; /* keep going */ continue; } /* check for a case-folded match if appropriate */ if (fold_case && t3_compare_case_fold_min(valp, vallen, vp, vlen) == 0) { /* note the case-folded match and keep going */ ret |= RF_CASEFOLD; continue; } /* no match */ return 0; } /* * if we didn't make it to the end of the equivalence string, * we must have run out of source before we matched the whole * string, so we don'to have a match */ if (vlen != 0) return 0; /* * If we make it here, we matched the equivalence mapping. * We've already skipped the input we matched, so skip the * reference character and keep going. */ refp.inc(&reflen); continue; } /* we don't have anything else to try, so we don't have a match */ return 0; } /* * If we ran out of reference string before we ran out of value * string, we definitely do not have a match. If we ran out of value * string before we ran out reference string, we have a match as long * as we matched at least the truncation length. */ if (reflen == 0 && vallen == 0) { /* * We ran out of both at the same time - it's a match. Return the * result code up to this point OR'd with RF_MATCH, which is our * pre-defined bit that we set for every match. */ return (ret | RF_MATCH); } else if (vallen != 0) { /* we ran out of reference string first - it's not a match */ return 0; } else { /* * We ran out of value string first, so it's a truncated match if * we matched at least up to the truncation length (assuming we * allow truncation at all). If we didn't make it to the * truncation length, or we don't allow truncation, it's not a * match. */ size_t valcharlen = utf8_ptr::s_len(valstr, valp.getptr() - valstr); if (ext->trunc_len != 0 && valcharlen >= ext->trunc_len) { /* * it's a truncated match - return the result code up to this * point, OR'd with RF_MATCH (our pre-defined bit we set for * every match) and RF_TRUNC (our pre-defined bit we set for * truncated matches) */ return (ret | RF_MATCH | RF_TRUNC); } else { /* didn't make it to the truncation length, so it's not a match */ return 0; } } } /* * Check for an approximation match to a given character. This checks the * given input string for a match to the approximation for a given * reference character. Returns the number of characters in the match, or * zero if there's no match. */ size_t CVmObjStrComp::match_chars(const wchar_t *valstr, size_t vallen, wchar_t refch) { vmobj_strcmp_ext *ext = get_ext(); int fold_case = !(ext->case_sensitive); vmobj_strcmp_equiv **t1; vmobj_strcmp_equiv *eq; wchar_t valch = *valstr; const wchar_t *start = valstr; /* check for an exact match first */ if (refch == valch) return 1; /* check for a case-folded match if we're insensitive to case */ if (fold_case) { /* try a minimum case-folded match to the one-character ref string */ size_t ovallen = vallen, reflen = 1; const wchar_t *refstr = &refch; if (t3_compare_case_fold_min(valstr, vallen, refstr, reflen) == 0) { /* got it - return the length we consumed from the value string */ return ovallen - vallen; } } /* check for a reference equivalence mapping */ if ((t1 = ext->equiv[(refch >> 8) & 0xFF]) != 0 && (eq = t1[refch & 0xFF]) != 0) { /* match each character from the mapping string */ const wchar_t *vp; size_t vlen; for (vp = eq->val_ch, vlen = eq->val_ch_cnt ; vallen != 0 && vlen != 0 ; ) { /* get this mapping character */ refch = *vp; /* if we have an exact match, keep going */ if (refch == valch) { /* matched exactly - skip this character and keep going */ ++valstr, --vallen; ++vp, --vlen; continue; } /* check for a case-folded match if appropriate */ if (fold_case && t3_compare_case_fold_min(valstr, vallen, vp, vlen) == 0) { /* matched - keep going */ continue; } /* no match */ return 0; } /* we've matched the mapping - return the match length */ return valstr - start; } /* it doesn't match any of the possibilities */ return 0; } /* * Get the truncation length */ size_t CVmObjStrComp::trunc_len() const { return get_ext()->trunc_len; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmstrcmp.h������������������������������������������������������������������������0000664�0000000�0000000�00000027636�12650170723�0015720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmstrcmp.h - T3 String Comparator intrinsic class Function Defines the String Comparator intrinsic class, which provides native code that performs complex, parameterized string comparisons. We offer the following customizable options for our comparisons: - We can match exactly on case, or without regard to case. - We can optionally match a value to a truncated reference value (which allows user input to use abbreviated forms of dictionary words, for example). The minimum truncation length is a settable option. - We can use equivalence mappings that allow a given character in a reference string to match different characters in value strings. For example, we could specify that an "a" with an acute accent in a reference string matches an unaccented "a" in a value string. Each such mapping can specify result flag bits, so a caller can determine if particular equivalence mappings were used in making a match. This class implements the generic "comparator" interface, by providing a hash value calculator method and a value comparison method, so a String Comparator instance can be used as a Dictionary's comparator object. StringComparator objects are immutable; all of our parameters are set in the constructor. This is desirable because it allows the object to be installed in a Dictionary (or any other hash table-based structure) without any danger that the hash table will need to be rebuilt as long as the same comparator is installed. Notes Modified 09/01/02 MJRoberts - Creation */ #ifndef VMSTRCMP_H #define VMSTRCMP_H #include <stdlib.h> #include <os.h> #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" /* ------------------------------------------------------------------------ */ /* * Our serialized data stream, in both the image file and a saved file, * consists of: * * UINT2 truncation_length *. UINT2 flags *. UINT2 equivalence_mapping_count *. UINT2 equivalence_total_value_chars *. equivalence_mappings * * The 'flags' value consists of the following combination of bit fields: * * 0x0001 - the match is case-sensitive * * The 'equivalence_total_value_chars' gives the sum total of the value * string characters in ALL of the equivalence mappings. This value is * stored simply to make it easier to calculate the memory allocation * needs when loading this object. * * Each 'equivalence_mapping' entry is arranged like this: * * UINT2 reference_char *. UBYTE value_char_count *. UINT4 uc_result_flags *. UINT4 lc_result_flags *. UINT2 value_char[value_char_count] * * Each character is given as a 16-bit Unicode value. These values map * directly to the corresponding vmobj_strcmp_equiv structure entries. */ /* ------------------------------------------------------------------------ */ /* * Our in-memory extension. */ struct vmobj_strcmp_ext { /* * The truncation length for reference strings, or zero if no * truncation is allowed. This is the minimum length that we must * match when the value string is shorter than the reference string. */ size_t trunc_len; /* * Case sensitivity. If this is true, then our matches are sensitive * to case, which means that we must match each character exactly on * case. If this is false, then our matches are insensitive to case, * so we can match an upper-case letter to the corresponding * lower-case letter. */ int case_sensitive; /* * Equivalence mapping table, giving the mapping for each "reference" * string character. This is a two-tiered array: the first tier is * indexed by the high-order 8 bits of a reference character, and * gives a pointer to the second tier array, or a null pointer if * there is no mapping for any character with the given high-order 8 * bits. The second tier is indexed by the low-order 8 bits, and * gives a pointer to the equivalence mapping structure for the * character, or a null pointer if there is no mapping for the * character. */ struct vmobj_strcmp_equiv **equiv[256]; }; /* * Equivalence mapping entry. Note that we don't store the reference * character in a mapping structure, because we can only reach these * mapping structures by indexing the mapping array with the reference * character, and thus must always already know the reference character * before we can even reach one of these. */ struct vmobj_strcmp_equiv { /* string of value characters matching this reference character */ size_t val_ch_cnt; wchar_t *val_ch; /* * Additive result flags for upper-case input matches: this value is * bitwise-OR'd into the result code when this equivalence mapping is * used to match the value to an upper-case input letter. */ unsigned long uc_result_flags; /* additive result flags for lower-case input matches */ unsigned long lc_result_flags; }; /* ------------------------------------------------------------------------ */ /* * String Comparator intrinsic class */ class CVmObjStrComp: public CVmObject { friend class CVmMetaclassStrComp; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* am I a StringComparator object? */ static int is_strcmp_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* defer to our base class */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* undo operations - we are immutable and hence keep no undo */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { } void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* we reference no other objects */ void mark_refs(VMG_ uint) { } void remove_stale_weak_refs(VMG0_) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixup); /* * Direct Interface. These functions correspond to methods we expose * through the get_prop() interface, but can be called directly from * the C++ code of other intrinsic classes (such as Dictionary) to * avoid the overhead of going through the get_prop() mechanism. * These are virtual to allow derived intrinsic classes to override * the implementation of the public VM-visible interface. */ /* calculate a hash value for a constant string */ virtual unsigned int calc_str_hash(const char *str, size_t len); /* match two strings */ virtual unsigned long match_strings(const char *valstr, size_t vallen, const char *refstr, size_t reflen); /* * Check for an approximation match to a given character. This checks * the given input string for a match to the approximation for a given * reference character. Returns the number of characters in the match, * or zero if there's no match. */ virtual size_t match_chars(const wchar_t *valstr, size_t valcnt, wchar_t refchar); /* truncation length, in characters; 0 means no truncation length */ virtual size_t trunc_len() const; protected: /* create with no extension */ CVmObjStrComp() { ext_ = 0; } /* delete my extension */ void delete_ext(VMG0_); /* get my extension data */ vmobj_strcmp_ext *get_ext() const { return (vmobj_strcmp_ext *)ext_; } /* load from an abstact stream object */ void load_from_stream(VMG_ class CVmStream *str); /* * Write to an abstract stream object. Returns the number of bytes * actually needed to store the object. * * If 'bytes_avail' is non-null, it indicates the maximum number of * bytes available for writing; if we need more than this amount, we * won't write anything at all, but will simply return the number of * bytes we actually need. */ ulong write_to_stream(VMG_ class CVmStream *str, ulong *bytes_avail); /* allocate and initialize our extension */ void alloc_ext(VMG_ size_t trunc_len, int case_sensitive, size_t equiv_cnt, size_t total_chars, class CVmObjStrCompMapReader *reader); /* count of equivalence mappings */ void count_equiv_mappings(size_t *equiv_cnt, size_t *total_ch_cnt); /* property evaluator - undefined property */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - calculate a hash value */ int getp_calc_hash(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc); /* property evaluator - match two values */ int getp_match_values(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc); /* property evaluation function table */ static int (CVmObjStrComp::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassStrComp: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "string-comparator/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjStrComp(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjStrComp(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjStrComp::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjStrComp::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMSTRCMP_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjStrComp) ��������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmstrref.h������������������������������������������������������������������������0000664�0000000�0000000�00000011604�12650170723�0015701�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmstrref.h - reference-counted string class Function Notes Modified 08/11/10 MJRoberts - Creation */ #ifndef VMSTRREF_H #define VMSTRREF_H #include "osifcnet.h" class StringRef: public CVmRefCntObj { public: /* create by copying a literal string */ StringRef() { init(0, 0, 256); } StringRef(long init_alo) { init(0, 0, init_alo); } StringRef(const char *s) { size_t l = strlen(s); init(s, l, l); } StringRef(const char *s, long l) { init(s, l, l); } StringRef(const char *s, long l, long init_alo) { init(s, l, init_alo); } /* get the string's contents and length */ char *get() const { return str; } char *getend() const { return str + len; } long getlen() const { return len; } void setlen(long len) { this->len = len; } void addlen(long len) { this->len += len; } /* create from a sprintf-style format */ static StringRef *format(const char *fmt, ...) { /* create an empty StringRef to hold the new string */ StringRef *s = new StringRef(); /* format the string into an allocated buffer */ va_list argp; va_start(argp, fmt); s->str = t3sprintf_alloc(fmt, argp); va_end(argp); /* note the length */ s->alo = s->len = strlen(s->str); /* return the new string */ return s; } /* append */ void append(const char *s) { append(s, strlen(s)); } void append(const char *s, long l) { /* if necessary, reallocate the buffer */ if (len + l > alo) { /* * calculate the expansion size: go to the needed length plus * half again that total for headroom, with minimum headroom of * 256 bytes */ alo = len + l + (len+l > 512 ? (len+l)/2 : 256); /* allocate the new buffer */ str = (char *)t3realloc(str, alo + 1); } /* copy the new text */ memcpy(str + len, s, l); /* null-terminate it */ str[len + l] = '\0'; /* note the new length */ len += l; } /* append formatted */ void appendf(const char *fmt, ...) { /* do the formatting */ va_list argp; va_start(argp, fmt); char *s = t3sprintf_alloc(fmt, argp); va_end(argp); /* append the result to the string */ append(s); /* we're done with the formatted result */ t3free(s); } /* append a utf-8 character */ void append_utf8(wchar_t ch) { char buf[3]; if (ch <= 0x7f) { /* it's in the range 0..7f - encode as one byte */ buf[0] = (char)ch; append(buf, 1); } else if (ch <= 0x7ff) { /* it's in the range 80..7ff - encode as two bytes */ buf[0] = (char)(0xC0 | ((ch >> 6) & 0x1F)); buf[1] = (char)(0x80 | (ch & 0x3F)); append(buf, 2); } else { /* it's in the range 800..ffff - encode as three bytes */ buf[0] = (char)(0xE0 | ((ch >> 12) & 0x0F)); buf[1] = (char)(0x80 | ((ch >> 6) & 0x3F)); buf[2] = (char)(0x80 | (ch & 0x3F)); append(buf, 3); } } /* * ensure there's space for 'n' added bytes */ void ensure(long n) { if (n + len > alo) { /* * calculate the expansion size: go to the needed length plus * half again that total for headroom, with minimum headroom of * 256 bytes */ alo = len + n + (len+n > 512 ? (len+n)/2 : 256); /* allocate the new buffer */ str = (char *)t3realloc(str, alo + 1); } } /* truncate to the given size */ void truncate(long l) { /* if we're actually shrinking the buffer, make the change */ if (l < len) { /* set the new length, and null-terminate at the new length */ len = l; str[l] = '\0'; } } protected: ~StringRef() { if (str != 0) t3free(str); } void init(const char *s, size_t l, size_t init_alo) { /* make sure the initial allocation is at least 'l' */ if (init_alo < l) init_alo = l; /* allocate the buffer */ alo = init_alo; str = (char *)t3malloc(alo + 1); /* copy the string */ if (str != 0) memcpy(str, s, l); /* null-terminate it */ str[l] = '\0'; /* set the length */ len = l; } /* the string */ char *str; /* stored length of the string */ long len; /* size of the allocated buffer */ long alo; }; #endif /* VMSTRREF_H */ ����������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmstrres.h������������������������������������������������������������������������0000664�0000000�0000000�00000003061�12650170723�0015714�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/TADS2/RES.H,v 1.1 1999/07/11 00:46:30 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmstrres.h - string resource definitions for TADS 3 Function Defines the ID's for string resources. These strings must be loaded when needed with os_get_str_rsc(), since the specific mechanism by which these strings are loaded varies by operating system. We assign the resources sequential numbers starting at 1, to make it easier to map the resource loader to an operating system mechanism where such a mechanism exists. Notes Modified 06/25/99 MJRoberts - Creation */ #ifndef VMSTRRES_H #define VMSTRRES_H /* * Dialog buttons. These provide the text for standard buttons in * dialogs created with os_input_dialog(). * * These labels can use "&" to indicate a shortcut letter, per the * normal os_input_dialog() interface; for example, if the Yes button * label is "&Yes", the button has the shortcut letter "Y". * * The text of these buttons may vary by system, since these should * conform to local conventions where there are local conventions. In * addition, of course, these strings will vary by language. */ /* OK and Cancel buttons */ #define VMRESID_BTN_OK 1 #define VMRESID_BTN_CANCEL 2 /* "Yes" and "No" buttons */ #define VMRESID_BTN_YES 3 #define VMRESID_BTN_NO 4 #endif /* VMSTRRES_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmtmpfil.cpp����������������������������������������������������������������������0000664�0000000�0000000�00000020367�12650170723�0016230�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2010 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtmpfil.cpp - CVmObjTemporaryFile object Function Notes Modified 11/20/10 MJRoberts - Creation */ #include <stdlib.h> #include <string.h> #include <assert.h> #include "t3std.h" #include "vmtmpfil.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmbif.h" #include "vmmeta.h" #include "vmstack.h" #include "vmrun.h" #include "vmstr.h" #include "utf8.h" /* ------------------------------------------------------------------------ */ /* * Allocate an extension structure */ vm_tmpfil_ext *vm_tmpfil_ext::alloc_ext(VMG_ CVmObjTemporaryFile *self, const char *fname) { /* calculate how much space we need */ size_t siz = sizeof(vm_tmpfil_ext) + strlen(fname); /* allocate the memory */ vm_tmpfil_ext *ext = (vm_tmpfil_ext *)G_mem->get_var_heap()->alloc_mem( siz, self); /* copy the filename */ strcpy(ext->filename, fname); /* return the new extension */ return ext; } /* ------------------------------------------------------------------------ */ /* * CVmObjTemporaryFile object statics */ /* metaclass registration object */ static CVmMetaclassTemporaryFile metaclass_reg_obj; CVmMetaclass *CVmObjTemporaryFile::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjTemporaryFile::*CVmObjTemporaryFile::func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjTemporaryFile::getp_undef, /* 0 */ &CVmObjTemporaryFile::getp_getFilename, /* 1 */ &CVmObjTemporaryFile::getp_deleteFile /* 2 */ }; /* ------------------------------------------------------------------------ */ /* * CVmObjTemporaryFile intrinsic class implementation */ /* * construction */ CVmObjTemporaryFile::CVmObjTemporaryFile(VMG_ const char *fname) { /* allocate our extension structure */ ext_ = (char *)vm_tmpfil_ext::alloc_ext(vmg_ this, fname); } /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjTemporaryFile::create_from_stack( VMG_ const uchar **pc_ptr, uint argc) { /* check arguments */ if (argc != 0) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* generate a temporary filename in the local file system */ char fname[OSFNMAX]; if (!os_gen_temp_filename(fname, sizeof(fname))) err_throw(VMERR_CREATE_FILE); /* allocate the object ID and create the object */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, FALSE, FALSE); new (vmg_ id) CVmObjTemporaryFile(vmg_ fname); /* temporary file objects are always transient */ G_obj_table->set_obj_transient(id); /* return the new ID */ return id; } /* * notify of deletion */ void CVmObjTemporaryFile::notify_delete(VMG_ int /*in_root_set*/) { /* free our additional data, if we have any */ if (ext_ != 0) { /* delete the local file system object */ if (get_ext()->filename[0] != '\0') osfdel(get_ext()->filename); /* free the extension */ G_mem->get_var_heap()->free_mem(ext_); } } /* * set a property */ void CVmObjTemporaryFile::set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) { /* no settable properties - throw an error */ err_throw(VMERR_INVALID_SETPROP); } /* * get a property */ int CVmObjTemporaryFile::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from our base class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * apply an undo record */ void CVmObjTemporaryFile::apply_undo(VMG_ struct CVmUndoRecord *) { /* we're immutable, so there's no undo to worry about */ } /* * discard extra undo information */ void CVmObjTemporaryFile::discard_undo(VMG_ CVmUndoRecord *) { /* we don't create undo records */ } /* * load from an image file */ void CVmObjTemporaryFile::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from the image file */ void CVmObjTemporaryFile::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); } /* * load or reload data from the image */ void CVmObjTemporaryFile::load_image_data(VMG_ const char *ptr, size_t siz) { /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* * Allocate the extension. A temp file object loaded from the image is * invalid; these objects are inherently transient. So simply use an * empty filename to indicate that the temp file is not openable. */ vm_tmpfil_ext *ext = vm_tmpfil_ext::alloc_ext(vmg_ this, ""); ext_ = (char *)ext; } /* * save to a file */ void CVmObjTemporaryFile::save_to_file(VMG_ class CVmFile *) { /* temporary files are always transient, so should never be saved */ assert(FALSE); } /* * restore from a file */ void CVmObjTemporaryFile::restore_from_file(VMG_ vm_obj_id_t /*self*/, CVmFile *, CVmObjFixup *) { /* temporary files are transient, so should never be saved */ } /* ------------------------------------------------------------------------ */ /* * getFilename() method - get the local file system object's name. */ int CVmObjTemporaryFile::getp_getFilename(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* if we have a filename, return it, otherwise return nil */ vm_tmpfil_ext *ext = get_ext(); if (ext->filename[0] != '\0') { /* return the filename */ retval->set_obj(CVmBif::str_from_ui_str(vmg_ ext->filename)); } else { /* no filename */ retval->set_nil(); } /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * deleteFile() method. This explicitly releases the temp file, deleting * the underlying file system object. It's not necessary for the program * to call this, as we'll automatically release the file when this object * is deleted by the garbage collector, but this method allows the program * to explicitly release the resource as soon as it's done with it. */ int CVmObjTemporaryFile::getp_deleteFile(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* if we have a filename, delete the file */ vm_tmpfil_ext *ext = get_ext(); if (ext->filename[0] != '\0') { /* delete the system file */ osfdel(ext->filename); } /* no return value */ retval->set_nil(); /* handled */ return TRUE; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmtmpfil.h������������������������������������������������������������������������0000664�0000000�0000000�00000017733�12650170723�0015700�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 2010 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtmpfil.h - CVmObjTemporaryFile object Function The TemporaryFile intrinsic class describes a temp file on disk. This isn't an open instance of a temp file; rather, it basically represents a filename in the local file system. The reason to make this an object is that it provides a natural way of identifying and freeing temp files. The File class can validate that the program is supplying a valid temporary file generated by the system, because only the system can create a TemporaryFile object. This prevents a malicious program from trying to circumvent the file safety levels by faking a temp file name; it's essentially impossible to invent a fake name since the name is encapsulated in the system object. The system can tell when it's safe to delete the underlying file system object through the magic of garbage collection: when the TemporaryFile object is collected, the program is definitely done with it, since the program doesn't have any references to it. Notes Modified 11/20/10 MJRoberts - Creation */ #ifndef VMTMPFIL_H #define VMTMPFIL_H #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" /* ------------------------------------------------------------------------ */ /* * Image file data block: * * This object doesn't store anything in an image file. A temporary file * object is completely transient and doesn't have anything to store. */ /* ------------------------------------------------------------------------ */ /* * Our in-memory extension data structure. This is usually a 'struct' * that contains the same information as in the image file, but using * native types. */ struct vm_tmpfil_ext { /* allocate the structure */ static vm_tmpfil_ext *alloc_ext( VMG_ class CVmObjTemporaryFile *self, const char *filename); /* * The filename, as a null-terminated string. We overallocate the * structure with enough space. If this is set to an empty string, it * means that the temporary file is no longer valid. This happens if * the object is loaded from an image file or explicitly released via * the deleteFile() method. */ char filename[1]; }; /* ------------------------------------------------------------------------ */ /* * CVmObjTemporaryFile intrinsic class definition */ class CVmObjTemporaryFile: public CVmObject { friend class CVmMetaclassTemporaryFile; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is this a CVmObjTemporaryFile object? */ static int is_tmpfil_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* * Get the name of the temp file (as a null-terminated string). * Returns null if the temp file is invalid. */ const char *get_fname() const { const char *f = get_ext()->filename; return f[0] != 0 ? f : 0; } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * receive savepoint notification - we don't keep any * savepoint-relative records, so we don't need to do anything here */ void notify_new_savept() { } /* apply an undo record */ void apply_undo(VMG_ struct CVmUndoRecord *rec); /* discard an undo record */ void discard_undo(VMG_ struct CVmUndoRecord *); /* mark our undo record references */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* mark our references */ void mark_refs(VMG_ uint) { } /* remove weak references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* we're immutable, so we can't have changed since loading */ int is_changed_since_load() const { return FALSE; } protected: /* get my extension data */ vm_tmpfil_ext *get_ext() const { return (vm_tmpfil_ext *)ext_; } /* load or reload image data */ void load_image_data(VMG_ const char *ptr, size_t siz); /* create a with no initial contents */ CVmObjTemporaryFile() { ext_ = 0; } /* create with contents */ CVmObjTemporaryFile(VMG_ const char *fname); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* getFilename() method */ int getp_getFilename(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* deleteFile() method */ int getp_deleteFile(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluation function table */ static int (CVmObjTemporaryFile::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * CVmObjTemporaryFile metaclass registration table object */ class CVmMetaclassTemporaryFile: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "tempfile/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjTemporaryFile(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjTemporaryFile(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjTemporaryFile::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjTemporaryFile:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMTMPFIL_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjTemporaryFile) �������������������������������������qtads-2.1.7/tads3/vmtobj.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000234576�12650170723�0015704�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMTOBJ.CPP,v 1.3 1999/05/17 02:52:28 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtobj.cpp - TADS object implementation Function Notes Modified 10/30/98 MJRoberts - Creation */ #include <stdlib.h> #include <assert.h> #include <stdarg.h> #include "t3std.h" #include "vmglob.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmobj.h" #include "vmtobj.h" #include "vmundo.h" #include "vmtype.h" #include "vmfile.h" #include "vmstack.h" #include "vmrun.h" #include "vmpredef.h" #include "vmmeta.h" #include "vmlst.h" #include "vmintcls.h" #include "vmbif.h" #include "vmstr.h" /* ------------------------------------------------------------------------ */ /* * Allocate a new object header */ vm_tadsobj_hdr *vm_tadsobj_hdr::alloc(VMG_ CVmObjTads *self, unsigned short sc_cnt, unsigned short prop_cnt) { ushort hash_siz; size_t siz; size_t i; vm_tadsobj_hdr *hdr; char *mem; vm_tadsobj_prop **hashp; /* * Figure the size of the hash table to allocate. Empirically, a good * size seems to be about one hash slot per property. */ if (prop_cnt <= 2) hash_siz = 2; else if (prop_cnt <= 4) hash_siz = 4; else if (prop_cnt <= 8) hash_siz = 8; else if (prop_cnt <= 16) hash_siz = 16; else if (prop_cnt <= 32) hash_siz = 32; else if (prop_cnt <= 64) hash_siz = 64; else if (prop_cnt <= 128) hash_siz = 128; else hash_siz = 256; /* * increase the requested property count to the hash size at a minimum * - this will avoid the need to reallocate the object to make room * for more properties until we'd have to resize the hash table, at * which point we have to reallocate the object anyway */ if (prop_cnt < hash_siz) prop_cnt = hash_siz; /* figure the size of the structure we need */ siz = sizeof(vm_tadsobj_hdr) + (sc_cnt - 1) * sizeof(hdr->sc[0]) + (hash_siz) * sizeof(hdr->hash_arr[0]) + prop_cnt * sizeof(hdr->prop_entry_arr[0]); /* allocate the memory */ hdr = (vm_tadsobj_hdr *)G_mem->get_var_heap()->alloc_mem(siz, self); /* * Set up to suballocate out of this block. Free memory in the block * starts after our structure and the array of superclass entries. */ mem = (char *)&hdr->sc[sc_cnt]; /* clear our flags and load-image flags */ hdr->li_obj_flags = 0; hdr->intern_obj_flags = 0; /* the object has no precalculated inheritance path yet */ hdr->inh_path = 0; /* suballocate the hash buckets */ hdr->hash_siz = hash_siz; hdr->hash_arr = (vm_tadsobj_prop **)mem; /* clear out the hash buckets */ for (hashp = hdr->hash_arr, i = hash_siz ; i != 0 ; ++hashp, --i) *hashp = 0; /* move past the memory taken by the hash buckets */ mem = (char *)(hdr->hash_arr + hash_siz); /* suballocate the array of hash entries */ hdr->prop_entry_cnt = prop_cnt; hdr->prop_entry_arr = (vm_tadsobj_prop *)mem; /* all entries are currently free, so point to the first entry */ hdr->prop_entry_free = 0; /* remember the superclass count */ hdr->sc_cnt = sc_cnt; /* return the new object */ return hdr; } /* * Free */ void vm_tadsobj_hdr::free_mem() { /* if I have a precalculated inheritance path, delete it */ if (inh_path != 0) t3free(inh_path); } /* * Expand an existing object header to make room for more properties */ vm_tadsobj_hdr *vm_tadsobj_hdr::expand(VMG_ CVmObjTads *self, vm_tadsobj_hdr *hdr) { unsigned short prop_cnt; /* * Move up to the next property count increment. If we're not huge, * simply double the current size. If we're getting large, expand by * 50%. */ prop_cnt = hdr->prop_entry_cnt; if (prop_cnt <= 128) prop_cnt *= 2; else prop_cnt += prop_cnt/2; /* expand to the new size */ return expand_to(vmg_ self, hdr, hdr->sc_cnt, prop_cnt); } /* * Expand an existing header to the given minimum property table size */ vm_tadsobj_hdr *vm_tadsobj_hdr::expand_to(VMG_ CVmObjTads *self, vm_tadsobj_hdr *hdr, size_t new_sc_cnt, size_t new_prop_cnt) { vm_tadsobj_hdr *new_hdr; size_t i; vm_tadsobj_prop *entryp; /* allocate a new object at the expanded property table size */ new_hdr = alloc(vmg_ self, (ushort)new_sc_cnt, (ushort)new_prop_cnt); /* copy the superclasses from the original object */ memcpy(new_hdr->sc, hdr->sc, (hdr->sc_cnt < new_sc_cnt ? hdr->sc_cnt : new_sc_cnt) * sizeof(hdr->sc[0])); /* use the same flags from the original object */ new_hdr->li_obj_flags = hdr->li_obj_flags; new_hdr->intern_obj_flags = hdr->intern_obj_flags; /* * if the superclass count is changing, we're obviously changing the * inheritance structure, in which case the old cached inheritance path * is invalid - delete it if so */ if (new_sc_cnt != hdr->sc_cnt) hdr->inval_inh_path(); /* copy the old inheritance path (if we still have one) */ new_hdr->inh_path = hdr->inh_path; /* * Run through all of the existing properties and duplicate them in the * new object, to build the new object's hash table. Note that the * free index is inherently equivalent to the count of properties in * use. */ for (i = hdr->prop_entry_free, entryp = hdr->prop_entry_arr ; i != 0 ; --i, ++entryp) { /* add this property to the new table */ new_hdr->alloc_prop_entry(entryp->prop, &entryp->val, entryp->flags); } /* delete the old header */ G_mem->get_var_heap()->free_mem(hdr); /* return the new header */ return new_hdr; } /* * Allocate an entry for given property from the free pool. The caller is * responsible for checking that there's space in the free pool. We do * not check for an existing entry with the same caller ID, so the caller * is responsible for making sure the property doesn't already exist in * our table. */ vm_tadsobj_prop *vm_tadsobj_hdr::alloc_prop_entry( vm_prop_id_t prop, const vm_val_t *val, unsigned int flags) { /* get the hash code for the property */ unsigned int hash = calc_hash(prop); /* use the next free entry */ vm_tadsobj_prop *entry = &prop_entry_arr[prop_entry_free]; /* link this entry into the list for its hash bucket */ entry->nxt = hash_arr[hash]; hash_arr[hash] = entry; /* count our use of the free entry */ ++prop_entry_free; /* set the new entry's property ID */ entry->prop = prop; /* set the value and flags */ entry->val = *val; entry->flags = (unsigned char)flags; /* return the entry */ return entry; } /* * Find an entry */ vm_tadsobj_prop *vm_tadsobj_hdr::find_prop_entry(uint prop) { /* scan the list of entries in this bucket */ for (vm_tadsobj_prop *entry = hash_arr[calc_hash(prop)] ; entry != 0 ; entry = entry->nxt) { /* if this entry matches, return it */ if (entry->prop == prop) return entry; } /* didn't find it */ return 0; } /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassTads metaclass_reg_obj; CVmMetaclass *CVmObjTads::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjTads:: *CVmObjTads::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjTads::getp_undef, &CVmObjTads::getp_create_instance, &CVmObjTads::getp_create_clone, &CVmObjTads::getp_create_trans_instance, &CVmObjTads::getp_create_instance_of, &CVmObjTads::getp_create_trans_instance_of, &CVmObjTads::getp_set_sc_list, &CVmObjTads::getp_get_method, &CVmObjTads::getp_set_method }; /* * Function table indices. We only need constant definitions for these * for our static methods, since in other cases we translate through the * function table. */ const int PROPIDX_CREATE_INSTANCE = 1; const int PROPIDX_CREATE_CLONE = 2; const int PROPIDX_CREATE_TRANS_INSTANCE = 3; const int PROPIDX_CREATE_INSTANCE_OF = 4; const int PROPIDX_CREATE_TRANS_INSTANCE_OF = 5; /* ------------------------------------------------------------------------ */ /* * Static class initialization */ void CVmObjTads::class_init(VMG0_) { /* allocate the inheritance analysis object */ VM_IFELSE_ALLOC_PRE_GLOBAL( G_tadsobj_queue = new CVmObjTadsInhQueue(), G_tadsobj_queue->init()); } /* * Static class termination */ void CVmObjTads::class_term(VMG0_) { /* delete the inheritance analysis object */ VM_IF_ALLOC_PRE_GLOBAL( delete G_tadsobj_queue; G_tadsobj_queue = 0; ) } /* ------------------------------------------------------------------------ */ /* * Static creation methods */ /* create dynamically using stack arguments */ vm_obj_id_t CVmObjTads::create_from_stack_intern( VMG_ const uchar **pc_ptr, uint argc, int is_transient) { vm_obj_id_t id; CVmObjTads *obj; vm_val_t val; vm_obj_id_t srcobj; /* check arguments */ if (argc == 0) { /* no superclass argument - create a base object */ val.set_nil(); } else { /* * We have arguments. The first is the superclass argument, which * must be an object or nil. Retrieve it and make sure it's * valid. */ G_stk->pop(&val); if (val.typ != VM_OBJ && val.typ != VM_NIL) err_throw(VMERR_OBJ_VAL_REQD_SC); /* if it's the invalid object, treat it as nil */ if (val.typ == VM_OBJ && val.val.obj == VM_INVALID_OBJ) val.set_nil(); /* we cannot create an instance of a transient object */ if (val.typ != VM_NIL && G_obj_table->is_obj_transient(val.val.obj)) err_throw(VMERR_BAD_DYNAMIC_NEW); /* count the removal of the first argument */ --argc; } /* * create the object - this type of construction is never used for * root set objects */ id = vm_new_id(vmg_ FALSE, TRUE, FALSE); /* make the object transient if desired */ if (is_transient) G_obj_table->set_obj_transient(id); /* * create a TADS object with the appropriate number of superclasses * (0 if no superclass was specified, 1 if one was), and the default * number of initial mutable properties */ obj = new (vmg_ id) CVmObjTads(vmg_ (val.typ == VM_NIL ? 0 : 1), VMTOBJ_PROP_INIT); /* set the object's superclass */ if (val.typ != VM_NIL) obj->set_sc(vmg_ 0, val.val.obj); /* * Invoke the object's "construct" method, passing it the arguments * that are still on the stack. If the new object doesn't define or * inherit the "construct" method, simply push the new object * reference onto the stack directly. */ if (obj->get_prop(vmg_ G_predef->obj_construct, &val, id, &srcobj, 0)) { vm_val_t srcobj_val; vm_val_t id_val; const uchar *dummy_pc_ptr; uint caller_ofs; vm_rcdesc rc, *rcp; /* use the null PC pointer if the caller didn't supply one */ if (pc_ptr == 0) { /* there's no caller PC pointer - use a dummy value */ pc_ptr = &dummy_pc_ptr; caller_ofs = 0; /* set up the recursive context */ rcp = &rc; rc.init_ret(vmg_ "TadsObject.construct"); } else { /* get the caller's offset */ caller_ofs = G_interpreter->pc_to_method_ofs(*pc_ptr); rcp = 0; } /* * A "construct" method is defined - have the interpreter invoke * it, which will set up the interpreter to start executing its * byte-code. This is all we need to do, since we assume and * require that the constructor will return the new object as * its return value when it's done. */ srcobj_val.set_obj(srcobj); id_val.set_obj(id); *pc_ptr = G_interpreter->get_prop(vmg_ caller_ofs, &srcobj_val, G_predef->obj_construct, &id_val, argc, rcp); } else { /* * there's no "construct" method defined - if we have any * arguments, its an error */ if (argc != 0) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* leave the new object value in R0 */ G_interpreter->get_r0()->set_obj(id); } /* return the new object */ return id; } /* create an object with no initial extension */ vm_obj_id_t CVmObjTads::create(VMG_ int in_root_set) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmObjTads(); return id; } /* * Create an object with a given number of superclasses, and a given * property table size. Each superclass must be set before the object * can be used, and the property table is initially empty. * * This form is used to create objects dynamically; this call is never * used to load an object from an image file. */ vm_obj_id_t CVmObjTads::create(VMG_ int in_root_set, ushort superclass_count, ushort prop_count) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmObjTads(vmg_ superclass_count, prop_count); return id; } /* * Create an instance based on multiple superclasses, using the * createInstanceOf() interface. Arguments are passed on the stack. Each * argument gives a superclass, and optionally the arguments for its * inherited constructor. If an argument is a simple object/class, then we * won't inherit that object's constructor at all. If an argument is a * list, then the first element of the list gives the class, and the * remaining elements of the list give the arguments to pass to that * class's inherited constructor. */ vm_obj_id_t CVmObjTads::create_from_stack_multi( VMG_ uint argc, int is_transient) { /* allocate an object ID */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, TRUE, FALSE); if (is_transient) G_obj_table->set_obj_transient(id); /* create the new object */ CVmObjTads *obj = new (vmg_ id) CVmObjTads( vmg_ (ushort)argc, VMTOBJ_PROP_INIT); /* push the new object, for garbage collector protection */ G_interpreter->push_obj(vmg_ id); /* set the superclasses */ for (ushort i = 0 ; i < argc ; ++i) { /* * get this argument (it's at i+1 because of the extra item we * pushed for gc protection) */ vm_val_t *arg = G_stk->get(i + 1); /* * if it's a list, the superclass is the first element; otherwise, * the argument is the superclass */ vm_val_t sc; const char *lstp = arg->get_as_list(vmg0_); if (lstp != 0) { /* it's a list - the first element is the superclass */ CVmObjList::index_list(vmg_ &sc, lstp, 1); } else { /* not a list - the argument is the superclass */ sc = *arg; } /* make sure it's a TadsObject */ if (sc.typ != VM_OBJ || !is_tadsobj_obj(vmg_ sc.val.obj)) err_throw(VMERR_BAD_TYPE_BIF); /* can't create an instance of a transient object */ if (G_obj_table->is_obj_transient(sc.val.obj)) err_throw(VMERR_BAD_DYNAMIC_NEW); /* set this superclass */ obj->set_sc(vmg_ i, sc.val.obj); } /* set up the resursive invocation context */ vm_rcdesc rc("TadsObject.contructMulti"); /* * The new object is ready to go. All that remains is invoking any * inherited construtors that the caller wants us to invoked. * Constructor invocation is indicated by passing a list argument for * the corresponding superclass, so run through the arguments and * invoke each indicated constructor. */ for (ushort i = 0 ; i < argc ; ++i) { /* get the next argument */ vm_val_t *arg = G_stk->get(i + 1); /* if it's not a list, we don't want to invoke this constructor */ const char *lstp = arg->get_as_list(vmg0_); if (lstp == 0) { /* no constructor call is wanted - just keep going */ continue; } /* get the superclass from the list */ vm_val_t sc; CVmObjList::index_list(vmg_ &sc, lstp, 1); /* get the number of list elements */ uint lst_cnt = vmb_get_len(lstp); /* make sure we have room to push the arguments */ if (!G_stk->check_space(lst_cnt - 1)) err_throw(VMERR_STACK_OVERFLOW); /* * push the list elements in reverse order; don't push the first * element, since it's the superclass itself rather than an * argument to the constructor */ for (uint j = lst_cnt ; j > 1 ; --j) CVmObjList::index_and_push(vmg_ lstp, j); /* * Invoke the constructor via a recursive call into the VM. Note * that we're inheriting the property, so 'self' is the new object, * but the 'target' object is the superclass whose constructor * we're invoking. */ vm_val_t new_obj_val; new_obj_val.set_obj(id); G_interpreter->get_prop(vmg_ 0, &sc, G_predef->obj_construct, &new_obj_val, lst_cnt - 1, &rc); } /* discard the arguments plus our own gc protection */ G_stk->discard(argc + 1); /* return the new object */ return id; } /* ------------------------------------------------------------------------ */ /* * Constructors */ /* * Create an object with a given number of superclasses, and a given * property table size. The superclasses must be individually set * before the object can be used, and the property table is initially * empty. * * This constructor is used only when creating a new object dynamically, * and is never used to load an object from an image file. */ CVmObjTads::CVmObjTads(VMG_ ushort superclass_count, ushort prop_count) { /* allocate our header */ ext_ = (char *)vm_tadsobj_hdr::alloc(vmg_ this, superclass_count, prop_count); } /* ------------------------------------------------------------------------ */ /* * receive notification of deletion */ void CVmObjTads::notify_delete(VMG_ int in_root_set) { /* free our extension */ if (ext_ != 0) { /* tell the header to delete its memory */ get_hdr()->free_mem(); /* delete the extension */ G_mem->get_var_heap()->free_mem(ext_); } } /* ------------------------------------------------------------------------ */ /* * Create an instance of this class */ void CVmObjTads::create_instance(VMG_ vm_obj_id_t self, const uchar **pc_ptr, uint argc) { /* push myself as the superclass */ G_stk->push()->set_obj(self); /* use the normal stack creation routine */ create_from_stack(vmg_ pc_ptr, argc+1); } /* ------------------------------------------------------------------------ */ /* * Determine if the object has a finalizer method */ int CVmObjTads::has_finalizer(VMG_ vm_obj_id_t self) { vm_val_t val; vm_obj_id_t srcobj; /* * look up the finalization method - if it's defined, and it's a * method, invoke it; otherwise do nothing */ return (G_predef->obj_destruct != VM_INVALID_PROP && get_prop(vmg_ G_predef->obj_destruct, &val, self, &srcobj, 0) && (val.typ == VM_CODEOFS || val.typ == VM_NATIVE_CODE || val.typ == VM_OBJX || val.typ == VM_BIFPTRX)); } /* ------------------------------------------------------------------------ */ /* * Invoke the object's finalizer */ void CVmObjTads::invoke_finalizer(VMG_ vm_obj_id_t self) { vm_val_t val; vm_obj_id_t srcobj; /* * look up the finalization method - if it's defined, and it's a * method, invoke it; otherwise do nothing */ if (G_predef->obj_destruct != VM_INVALID_PROP && get_prop(vmg_ G_predef->obj_destruct, &val, self, &srcobj, 0) && (val.typ == VM_CODEOFS || val.typ == VM_NATIVE_CODE || val.typ == VM_OBJX || val.typ == VM_BIFPTRX)) { /* * invoke the finalizer in a protected frame, to ensure that we * catch any exceptions that are thrown out of the finalizer */ err_try { vm_val_t srcobj_val; vm_val_t self_val; /* * Invoke the finalizer. Use a recursive VM invocation, * since the VM must return to the garbage collector, not to * what it was doing in the enclosing stack frame. */ srcobj_val.set_obj(srcobj); self_val.set_obj(self); vm_rcdesc rc("TadsObject.finalize"); G_interpreter->get_prop(vmg_ 0, &srcobj_val, G_predef->obj_destruct, &self_val, 0, &rc); } err_catch_disc { /* silently ignore the error */ } err_end; } } /* ------------------------------------------------------------------------ */ /* * Clear the undo flags for all properties */ void CVmObjTads::clear_undo_flags() { vm_tadsobj_prop *entry; uint i; vm_tadsobj_hdr *hdr = get_hdr(); /* scan all property entries and clear their undo flags */ for (i = hdr->prop_entry_free, entry = hdr->prop_entry_arr ; i != 0 ; --i, ++entry) { /* clear this entry's undo flag */ entry->flags &= ~VMTO_PROP_UNDO; } } /* ------------------------------------------------------------------------ */ /* * Set a property */ void CVmObjTads::set_prop(VMG_ CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) { /* get my header */ vm_tadsobj_hdr *hdr = get_hdr(); /* look for an existing property entry */ vm_tadsobj_prop *entry = hdr->find_prop_entry(prop); /* check for an existing entry for the property */ vm_val_t oldval; if (entry != 0) { /* found an existing entry - note the old value */ oldval = entry->val; /* store the new value in the existing entry */ entry->val = *val; } else { /* * We didn't find an existing entry for the property, so we have to * add a new one. If we don't have any free property slots left, * expand the object to create some more property slots. */ if (!hdr->has_free_entries(1)) { /* expand the extension to make room for more properties */ ext_ = (char *)vm_tadsobj_hdr::expand(vmg_ this, hdr); /* get the reallocated header */ hdr = get_hdr(); } /* allocate a new entry */ entry = hdr->alloc_prop_entry(prop, val, 0); /* * The old value didn't exist, so mark it emtpy, with an intval of * zero. The zero indicates that this is a newly created property * entry, and thus should be deleted on undo. */ oldval.set_empty(); oldval.val.intval = 0; } /* * If we already have undo for this property for the current * savepoint, as indicated by the undo flag for the property, we don't * need to save undo for this change, since we already have an undo * record in the current savepoint. Otherwise, we need to add an undo * record for this savepoint. */ if (undo != 0 && (entry->flags & VMTO_PROP_UNDO) == 0) { /* save the undo record */ undo->add_new_record_prop_key(vmg_ self, prop, &oldval); /* mark the property as now having undo in this savepoint */ entry->flags |= VMTO_PROP_UNDO; /* * If the entry wasn't previously marked as modified, remember this * by storing an extra 'empty' undo record with intval 1 after the * record we just saved. When we see an 'empty' undo value with an * intval of 1, we recognize it as this special marker that tells * us to remove the 'modified' flag from the property. Note that * we don't need to bother if the old value was already empty, * since that deletes the whole property on undo, making the * 'modified' flag irrelevant. */ if ((entry->flags & VMTO_PROP_MOD) == 0 && oldval.typ != VM_EMPTY) { /* store an empty undo record with intval 1 */ oldval.set_empty(); oldval.val.intval = 1; undo->add_new_record_prop_key(vmg_ self, prop, &oldval); } } /* mark the property entry as modified */ entry->flags |= VMTO_PROP_MOD; /* mark the overall object as modified */ mark_modified(vmg_ undo, self); } /* ------------------------------------------------------------------------ */ /* * Mark the object as modified */ void CVmObjTads::mark_modified(VMG_ CVmUndo *undo, vm_obj_id_t self) { /* get my header */ vm_tadsobj_hdr *hdr = get_hdr(); /* if we're not already marked as modified, set the flag */ if ((hdr->intern_obj_flags & VMTO_OBJ_MOD) == 0) { /* add the modified flag */ hdr->intern_obj_flags |= VMTO_OBJ_MOD; /* add an undo record for it */ if (undo != 0) { /* * property == 'invalid' and value == int(1) is our special * record indicating an unmodified-to-modified transition */ vm_val_t v; v.set_int(1); undo->add_new_record_prop_key(vmg_ self, VM_INVALID_PROP, &v); } } } /* ------------------------------------------------------------------------ */ /* * Build a list of my properties */ void CVmObjTads::build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval) { size_t cnt; size_t idx; CVmObjList *lst; vm_tadsobj_prop *entry; vm_tadsobj_hdr *hdr = get_hdr(); /* the next free index is also the number of properties we have */ cnt = hdr->prop_entry_free; /* allocate a list big enough for all of our properties */ retval->set_obj(CVmObjList::create(vmg_ FALSE, cnt)); /* get the list object, property cast */ lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* add our image file properties to the list */ for (idx = 0, entry = hdr->prop_entry_arr ; cnt != 0 ; --cnt, ++entry) { /* make a value for this property ID */ vm_val_t val; val.set_propid(entry->prop); /* add it to the list */ lst->cons_set_element(idx++, &val); } /* set the final length of the list */ lst->cons_set_len(idx); } /* ------------------------------------------------------------------------ */ /* * Call a static method. */ int CVmObjTads::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { int idx; /* convert the property to an index in our method vector */ idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* check what property they're evaluating */ switch(idx) { case PROPIDX_CREATE_INSTANCE: case PROPIDX_CREATE_TRANS_INSTANCE: { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(result, argc, &desc)) return TRUE; /* * They want to create an instance of TadsObject, which is * just a plain base object with no superclass. Push null as * the base class and call our from-stack constructor. */ result->set_obj(create_from_stack_intern( vmg_ pc_ptr, 0, idx == PROPIDX_CREATE_TRANS_INSTANCE)); } /* handled */ return TRUE; case PROPIDX_CREATE_INSTANCE_OF: case PROPIDX_CREATE_TRANS_INSTANCE_OF: { static CVmNativeCodeDesc desc(0, 0, TRUE); uint in_argc = (argc == 0 ? 0 : *argc); /* check arguments */ if (get_prop_check_argc(result, argc, &desc)) return TRUE; /* * They want to create an instance of TadsObject, which is just * a plain base object with no superclass. Push null as the * base class and call our from-stack constructor. */ result->set_obj(create_from_stack_multi( vmg_ in_argc, idx == PROPIDX_CREATE_TRANS_INSTANCE_OF)); } /* handled */ return TRUE; default: /* it's not one of ours; inherit the base class statics */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } } /* ------------------------------------------------------------------------ */ /* * Superclass inheritance search context. This keeps track of our position * in searching the inheritance tree of a given class. */ struct tadsobj_sc_search_ctx { /* initialize at a given object */ tadsobj_sc_search_ctx(VMG_ vm_obj_id_t obj, CVmObjTads *objp) { /* start at the given object */ cur = obj; curhdr = objp->get_hdr(); /* we have no path yet */ path_sc = 0; } /* current object ID */ vm_obj_id_t cur; /* if we're using a multiple-inheritance search path, the path */ tadsobj_objid_and_ptr *path_sc; /* current object header */ vm_tadsobj_hdr *curhdr; /* * Find the given property, searching our superclass list until we find * an object providing the property. Returns true if found, and fills * in *val and *source. Returns false if not found. */ int find_prop(VMG_ uint prop, vm_val_t *val, vm_obj_id_t *source) { /* keep going until we find the property */ do { /* look for this property in the current object */ vm_tadsobj_prop *entry = curhdr->find_prop_entry(prop); if (entry != 0) { /* we found the property - return it */ *val = entry->val; *source = cur; return TRUE; } } while (to_next(vmg0_)); /* we've exhausted the search path - return failure */ return FALSE; } /* * Move to the next superclass. This updates 'cur' to refer to the * next object in inheritance order. Returns true if there is a next * element, false if not. * * It's legal to call this with 'cur' uninitialized, as we don't need * the old value of 'cur' to do our work. This is important because it * allows a search position to be initialized knowing only an object's * 'this' pointer, not its object ID. */ int to_next(VMG0_) { /* check for a path */ try_again: if (path_sc == 0) { /* * we're not working on a path at all - this means we're * working directly on a (so far) single-inheritance superclass * chain, so simply follow the chain up to the next superclass */ /* we have no path, so look at our object's superclasses */ switch(curhdr->sc_cnt) { case 1: /* we have exactly one superclass, so traverse to it */ { const vm_tadsobj_sc *sc = curhdr->sc; cur = sc->id; curhdr = sc->objp->get_hdr(); } return TRUE; case 0: /* we have no superclasses, so there's nowhere to go */ return FALSE; default: /* we have multiple superclasses, so set up a search path */ path_sc = curhdr->get_inh_search_path(vmg0_); /* start over with the search path */ goto try_again; } } else if ((cur = path_sc->id) != VM_INVALID_OBJ) { /* * we're working on a path, and we have elements remaining - * move on to the next element */ curhdr = path_sc->objp->get_hdr(); ++path_sc; /* got it */ return TRUE; } else { /* * we're working on a path, and we're out of elements - we have * nowhere else to go */ return FALSE; } } /* * Skip to the given object. If we find the object in the path, we'll * leave the current position set to the given object and return true; * if we fail to find the object, we'll return false. */ int skip_to(VMG_ vm_obj_id_t target) { /* keep going until the current object matches the target */ while (cur != target) { /* move to the next element */ if (!to_next(vmg0_)) { /* there's nothing left - return failure */ return FALSE; } } /* found it */ return TRUE; } }; /* * Get the inheritance search path for this object */ tadsobj_objid_and_ptr *vm_tadsobj_hdr::get_inh_search_path(VMG0_) { /* if we have a cached path, return it */ if (inh_path != 0) return inh_path; /* get the queue builder global */ CVmObjTadsInhQueue *q = G_tadsobj_queue; /* * We haven't already cached a search path for this object, so build * the search path now and save it for future searches. Start by * clearing the work queue. */ q->clear(); /* we're not yet processing the first element */ pfq_ele *q_ele = 0; /* start with self */ vm_tadsobj_hdr *curhdr = this; /* keep going until we run out of queue elements */ for (;;) { /* get the superclass count for this object */ uint cnt = curhdr->sc_cnt; /* insert my superclasses right after me */ pfq_ele *q_ins = q_ele; /* enqueue the current object's superclasses */ uint i; vm_tadsobj_sc *scp; for (i = 0, scp = curhdr->sc ; i < cnt ; ++i, ++scp) { /* get the current superclass */ vm_obj_id_t sc = scp->id; CVmObjTads *scobj = scp->objp; /* if it's not a TadsObject, skip it */ if (scobj->get_metaclass_reg() != CVmObjTads::metaclass_reg_) continue; /* enqueue this superclass */ q_ins = q->insert_obj(vmg_ sc, scobj, q_ins); } /* move to the next valid element */ do { /* get the next queue element */ q_ele = (q_ele == 0 ? q->get_head() : q_ele->nxt); /* if we're out of elements, we're done */ if (q_ele == 0) goto done; } while (q_ele->obj == VM_INVALID_OBJ); /* get the object header for the queue item */ curhdr = q_ele->objp->get_hdr(); } done: /* create and cache a linearized path for the queue, and return it */ return inh_path = q->create_path(); } /* * Search for a property via inheritance, starting after the given defining * object. */ int CVmObjTads::search_for_prop_from(VMG_ uint prop, vm_val_t *val, vm_obj_id_t orig_target_obj, vm_obj_id_t *source_obj, vm_obj_id_t defining_obj) { /* set up a search position */ tadsobj_sc_search_ctx curpos(vmg_ orig_target_obj, (CVmObjTads *)vm_objp(vmg_ orig_target_obj)); /* if we have a starting point, skip past it */ if (defining_obj != VM_INVALID_OBJ) { /* skip until we're at defining_obj */ if (!curpos.skip_to(vmg_ defining_obj)) return FALSE; /* skip defining_obj itself */ if (!curpos.to_next(vmg0_)) return FALSE; } /* find the property */ return curpos.find_prop(vmg_ prop, val, source_obj); } /* ------------------------------------------------------------------------ */ /* * Get a property. We first look in this object; if we can't find the * property here, we look for it in one of our superclasses. */ int CVmObjTads::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { /* * try finding the property in my own direct property list or a * superclass property list */ tadsobj_sc_search_ctx curpos(vmg_ self, this); if (curpos.find_prop(vmg_ prop, val, source_obj)) return TRUE; /* * we didn't find the property in a property list, so try the * intrinsic class methods */ if (get_prop_intrinsic(vmg_ prop, val, self, source_obj, argc)) return TRUE; /* * we didn't find the property among our methods, so try inheriting it * from the base metaclass */ return CVmObject::get_prop(vmg_ prop, val, self, source_obj, argc); } /* * Inherit a property. */ int CVmObjTads::inh_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t orig_target_obj, vm_obj_id_t defining_obj, vm_obj_id_t *source_obj, uint *argc) { /* * check to see if we're already inheriting from an intrinsic class or * an intrinsic class modifier */ if (defining_obj == VM_INVALID_OBJ || (!CVmObjIntClsMod::is_intcls_mod_obj(vmg_ defining_obj) && !CVmObjClass::is_intcls_obj(vmg_ defining_obj))) { /* * The previous defining object wasn't itself an intrinsic class or * modifier object, so continue searching for TadsObject * superclasses. */ if (search_for_prop_from(vmg_ prop, val, orig_target_obj, source_obj, defining_obj)) return TRUE; /* * We didn't find the property in a property list. Since we were * inheriting, we must have originally found it in a property list, * but we've found no more inherited properties. Next, check the * intrinsic methods of the intrinsic class. */ if (get_prop_intrinsic(vmg_ prop, val, self, source_obj, argc)) return TRUE; /* * We didn't find it among our TadsObject superclasses or as an * intrinsic method. There's still one possibility: it could be * defined in an intrinsic class modifier for TadsObject or one of * its intrinsic superclasses (aka supermetaclasses). * * This represents a new starting point in the search. No longer * are we looking for TadsObject overrides; we're now looking for * modifier objects. The modifier objects effectively form a * separate class hierarchy alongside the intrinsic class hierarchy * they modify. Since we're starting a new search in this new * context, forget the previous defining object - it has a * different meaning in the new context, and we want to start the * new search from the beginning. * * Note that if this search does turn up a modifier object, and * that modifier object further inherits, we'll come back through * this method again to find the base class method. At that point, * however we'll notice that the previous defining object was a * modifier, so we will not go through this branch again - we'll go * directly to the base metaclass and continue the inheritance * search there. */ defining_obj = VM_INVALID_OBJ; } /* continue searching via our base metaclass */ return CVmObject::inh_prop(vmg_ prop, val, self, orig_target_obj, defining_obj, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * Get a property from the intrinsic class. */ int CVmObjTads::get_prop_intrinsic(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function in our function vector */ if ((this->*func_table_[func_idx])(vmg_ self, val, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* didn't find it */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Enumerate properties */ void CVmObjTads::enum_props(VMG_ vm_obj_id_t self, void (*cb)(VMG_ void *ctx, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val), void *cbctx) { size_t i; size_t sc_cnt; vm_tadsobj_prop *entry; vm_tadsobj_hdr *hdr = get_hdr(); /* run through our properties */ for (i = hdr->prop_entry_free, entry = hdr->prop_entry_arr ; i != 0 ; --i, ++entry) { /* invoke the callback */ (*cb)(vmg_ cbctx, self, entry->prop, &entry->val); } /* enumerate properties in each superclass */ sc_cnt = get_sc_count(); for (i = 0 ; i < sc_cnt ; ++i) { vm_obj_id_t sc; /* get this superclass */ sc = get_sc(i); /* enumerate its properties */ vm_objp(vmg_ sc)->enum_props(vmg_ sc, cb, cbctx); } } /* ------------------------------------------------------------------------ */ /* * Determine if I'm an instance of the given object */ int CVmObjTads::is_instance_of(VMG_ vm_obj_id_t obj) { /* * Set up a superclass search position. Since the first thing we'll * do is call 'to_next', and since 'to_next' doesn't require a valid * current object ID (only a valid 'this' pointer), we don't need to * know our own object ID - simply set the initial object ID to the * invalid ID. */ tadsobj_sc_search_ctx curpos(vmg_ VM_INVALID_OBJ, this); /* * scan through the search list, comparing each superclass to the * object of interest; if we find it among our superclasses, we're an * instance of the given object */ for (;;) { /* skip to the next object */ if (!curpos.to_next(vmg0_)) { /* we've run out of superclasses without finding it */ break; } /* * if the current superclass is the object we're looking for, then * we're an instance of that object */ if (curpos.cur == obj) return TRUE; } /* * None of our superclasses match the given object, and none of the * superclasses derive from the given object, so we must not derive * from the given object. Our last recourse is to determine if the * object represents our metaclass; inherit the default handling to * make this check. */ return CVmObject::is_instance_of(vmg_ obj); } /* ------------------------------------------------------------------------ */ /* * Apply undo */ void CVmObjTads::apply_undo(VMG_ CVmUndoRecord *rec) { /* get my header */ vm_tadsobj_hdr *hdr = get_hdr(); /* * if the property is valid, it's a simple property change record; * otherwise it's some other object-level change */ if (rec->id.prop == VM_INVALID_PROP) { /* * Invalid property ID, so it's an object-level change: * * - int(1) -> object is newly modified (VMTO_OBJ_MOD flag was * newly set) * * - list -> superclass list change */ switch (rec->oldval.typ) { case VM_INT: switch (rec->oldval.val.intval) { case 1: /* VMTO_OBJ_MOD flag newly set - clear the flag */ hdr->intern_obj_flags &= ~VMTO_OBJ_MOD; break; } break; default: /* check for a list, which indicates a superclass change */ if (rec->oldval.is_listlike(vmg0_)) { /* set the new superclass list */ change_superclass_list( vmg_ &rec->oldval, rec->oldval.ll_length(vmg0_)); } break; } } else { /* find the property entry for the property being undone */ vm_tadsobj_prop *entry = hdr->find_prop_entry(rec->id.prop); if (entry == 0) { /* can't find the property - something is out of whack */ assert(FALSE); return; } /* restore the value from the record */ entry->val = rec->oldval; /* if the old value is 'empty', it requires special handling */ if (rec->oldval.typ == VM_EMPTY) { vm_tadsobj_prop *cur, **prv; unsigned int hash = hdr->calc_hash(rec->id.prop); /* * We use 'empty' records for multiple purposes, with the * specific one indicated by the intval field. */ switch (rec->oldval.val.intval) { case 0: /* * Empty with intval 0 indicates a property addition, which * we undo by deleting the property. First, find it in the * hash chain. */ for (prv = &hdr->hash_arr[hash] ; (cur = *prv) != 0 && cur != entry ; prv = &cur->nxt) ; /* make sure we found it */ if (cur == entry) { /* unlink it */ *prv = entry->nxt; /* return it to the free list */ hdr->prop_entry_free -= 1; assert(entry == &hdr->prop_entry_arr[hdr->prop_entry_free]); } else { /* this should be impossible */ assert(FALSE); } break; case 1: /* * Empty with intval 1 indicates that we marked the slot as * newly modified, which we undo by clearing the modified * flag. */ entry->flags &= ~VMTO_PROP_MOD; break; } } } } /* ------------------------------------------------------------------------ */ /* * Mark as referenced all of the objects to which we refer */ void CVmObjTads::mark_refs(VMG_ uint state) { /* get my header */ vm_tadsobj_hdr *hdr = get_hdr(); /* * Go through all of our property slots and mark each object value. * Note that we only need to worry about the modified properties; * everything referenced in the load image list is necessarily part of * the root set, or it couldn't have been in the load image, so we * don't need to bother marking any of those objects, since they can * never be deleted by virtue of being in the root set. */ vm_tadsobj_prop *entry = hdr->prop_entry_arr; vm_tadsobj_prop *first_free = entry + hdr->prop_entry_free; for ( ; entry != first_free ; ++entry) { /* * if the slot is marked as modified and contains an object * reference, mark the reference */ if ((entry->val.typ == VM_OBJ || entry->val.typ == VM_OBJX) && (entry->flags & VMTO_PROP_MOD) != 0) { /* mark the reference */ G_obj_table->mark_all_refs(entry->val.val.obj, state); } } /* mark our superclasses as referenced */ vm_tadsobj_sc *scp = hdr->sc, *sclast = scp + hdr->sc_cnt; for ( ; scp != sclast ; ++scp) G_obj_table->mark_all_refs(scp->id, state); } /* ------------------------------------------------------------------------ */ /* * Mark a reference in an undo record */ void CVmObjTads::mark_undo_ref(VMG_ CVmUndoRecord *undo) { /* if the undo record refers to an object, mark the object */ if (undo->oldval.typ == VM_OBJ || undo->oldval.typ == VM_OBJX) G_obj_table->mark_all_refs(undo->oldval.val.obj, VMOBJ_REACHABLE); } /* ------------------------------------------------------------------------ */ /* * Determine if the object has been changed since it was loaded from the * image file. If the object has no properties stored in the modified * properties table, it is in exactly the same state as is stored in the * image file. */ int CVmObjTads::is_changed_since_load() const { /* return our 'modified' flag */ return ((get_hdr()->intern_obj_flags & VMTO_OBJ_MOD) != 0); } /* ------------------------------------------------------------------------ */ /* * Save the object's state to a file. We only need to save the modified * property list, because the load image list never changes. */ void CVmObjTads::save_to_file(VMG_ CVmFile *fp) { size_t i; vm_tadsobj_prop *entry; uint cnt; vm_tadsobj_hdr *hdr = get_hdr(); /* count the number of properties that have actually been modified */ for (cnt = 0, i = hdr->prop_entry_free, entry = hdr->prop_entry_arr ; i != 0 ; --i, ++entry) { /* if the slot is modified, count it */ if ((entry->flags & VMTO_PROP_MOD) != 0) ++cnt; } /* write the number of modified properties */ fp->write_uint2(cnt); /* write the number of superclasses */ fp->write_uint2(get_sc_count()); /* write the superclasses */ for (i = 0 ; i < get_sc_count() ; ++i) fp->write_uint4(get_sc(i)); /* write each modified property */ for (cnt = 0, i = hdr->prop_entry_free, entry = hdr->prop_entry_arr ; i != 0 ; --i, ++entry) { /* if the slot is modified, write it out */ if ((entry->flags & VMTO_PROP_MOD) != 0) { char slot[16]; /* prepare the slot data */ oswp2(slot, entry->prop); vmb_put_dh(slot + 2, &entry->val); /* write the slot */ fp->write_bytes(slot, 2 + VMB_DATAHOLDER); } } } /* ------------------------------------------------------------------------ */ /* * Restore the object from a file */ void CVmObjTads::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { /* read number of modified properties */ ushort mod_count = (ushort)fp->read_uint2(); /* read the number of superclasses */ ushort sc_cnt = (ushort)fp->read_uint2(); /* * If we don't have an extension yet, allocate one. The only way we * won't have an extension is if we weren't loaded from the image * file, since we always create the extension upon construction when * loading from an image file. */ if (ext_ == 0) { /* allocate our extension */ ext_ = (char *)vm_tadsobj_hdr::alloc(vmg_ this, sc_cnt, mod_count); } else { /* * We already have an extension, so we must have come from the * image file. Make sure we have enough memory to hold this many * properties, and make sure we have space for the superclasses. */ vm_tadsobj_hdr *hdr = get_hdr(); if (!hdr->has_free_entries(mod_count) || sc_cnt > hdr->sc_cnt) { /* * we need to expand the header to accomodate the modified * properties and/or the modified superclass list */ ext_ = (char *)vm_tadsobj_hdr::expand_to( vmg_ this, hdr, sc_cnt, hdr->prop_entry_cnt + mod_count); } } /* get the extension header */ vm_tadsobj_hdr *hdr = get_hdr(); /* read the superclass list */ hdr->sc_cnt = sc_cnt; for (ushort i = 0 ; i < sc_cnt ; ++i) { /* read the next superclass */ vm_obj_id_t sc = (vm_obj_id_t)fp->read_uint4(); /* fix it up to the new (post-restore) memory numbering system */ sc = fixups->get_new_id(vmg_ sc); /* * store it - as when loading from the image file, we can't count * on the superclass having been loaded yet, so we can only store * the superclass's ID, not its actual object pointer */ hdr->sc[i].id = sc; hdr->sc[i].objp = 0; } /* request post-load initialization, to set up the superclass list */ G_obj_table->request_post_load_init(self); /* * invalidate any existing inheritance path, in case the superclass * list changed */ hdr->inval_inh_path(); /* read the modified properties */ for (ushort i = 0 ; i < mod_count ; ++i) { /* read the next slot */ char buf[32]; fp->read_bytes(buf, 2 + VMB_DATAHOLDER); /* fix up this entry */ fixups->fix_dh(vmg_ buf + 2); /* decode the entry */ vm_prop_id_t prop = (vm_prop_id_t)osrp2(buf); vm_val_t val; vmb_get_dh(buf + 2, &val); /* * store the entry (don't save any undo for the operation, as we * can't undo a load) */ set_prop(vmg_ 0, self, prop, &val); } /* clear all undo information */ clear_undo_flags(); /* * If we were saved to a file in the first place, it's because we were * modified or newly created relative to the image file data. So on * restore, we're necessarily a modified object that will need to be * saved again, even if we're not modified between now and the next * save. */ get_hdr()->intern_obj_flags |= VMTO_OBJ_MOD; } /* ------------------------------------------------------------------------ */ /* * Post-load initialization: cache the object pointers for our * superclasses. */ void CVmObjTads::post_load_init(VMG_ vm_obj_id_t self) { /* get my header */ vm_tadsobj_hdr *hdr = get_hdr(); /* cache the superclass object pointers */ for (int i = 0 ; i < hdr->sc_cnt ; ++i) hdr->sc[i].objp = (CVmObjTads *)vm_objp(vmg_ hdr->sc[i].id); } /* ------------------------------------------------------------------------ */ /* * Load the object from an image file */ void CVmObjTads::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* save our image data pointer for reloading */ G_obj_table->save_image_pointer(self, ptr, siz); /* if we already have memory allocated, free it */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* get the number of superclasses */ ushort sc_cnt = osrp2(ptr); /* get the number of load image properties */ ushort li_cnt = osrp2(ptr + 2); /* allocate our header */ ext_ = (char *)vm_tadsobj_hdr::alloc(vmg_ this, sc_cnt, li_cnt); vm_tadsobj_hdr *hdr = get_hdr(); /* read the object flags from the image file and store them */ hdr->li_obj_flags = osrp2(ptr + 4); /* * set our internal flags - we come from the load image file, and we're * not yet modified from the load image data */ hdr->intern_obj_flags |= VMTO_OBJ_IMAGE; hdr->intern_obj_flags &= ~VMTO_OBJ_MOD; /* load the image file properties */ load_image_props_and_scs(vmg_ ptr, siz); /* request post-load initialization, to set up the superclass list */ G_obj_table->request_post_load_init(self); } /* * Reset to image file state. Discards all modified properties, so that * we have only the image file properties. */ void CVmObjTads::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* get my header */ vm_tadsobj_hdr *hdr = get_hdr(); /* get the number of superclasses */ ushort sc_cnt = osrp2(ptr); /* * Clear the property table. We don't have to worry about the new * property table being larger than the existing property table, * because we can't have shrunk since we were originally loaded. So, * all we need to do is mark all property entries as free and clear * out the hash table. */ hdr->prop_entry_free = 0; memset(hdr->hash_arr, 0, hdr->hash_siz * sizeof(hdr->hash_arr[0])); /* if we need space for more superclasses, reallocate the header */ if (sc_cnt > hdr->sc_cnt) { /* allocate the new header */ ext_ = (char *)vm_tadsobj_hdr::expand_to( vmg_ this, hdr, sc_cnt, hdr->prop_entry_cnt); } /* reload the image properties and superclasses */ load_image_props_and_scs(vmg_ ptr, siz); /* we're now unmodified from the image file state */ hdr->intern_obj_flags &= ~VMTO_OBJ_MOD; /* request post-load initialization, to set up the superclass list */ G_obj_table->request_post_load_init(self); } /* * Load the property list from the image data */ void CVmObjTads::load_image_props_and_scs(VMG_ const char *ptr, size_t siz) { /* get my header */ vm_tadsobj_hdr *hdr = get_hdr(); /* get the number of superclasses */ ushort sc_cnt = osrp2(ptr); /* get the number of load image properties */ ushort li_cnt = osrp2(ptr + 2); /* read the superclasses from the load image and store them */ ushort i; const char *p; for (i = 0, p = ptr + 6 ; i < sc_cnt ; ++i, p += 4) { /* store the object ID */ hdr->sc[i].id = (vm_obj_id_t)t3rp4u(p); /* * We can't store the superclass pointer yet, as the superclass * object might not be loaded yet. Store null for now; we'll fix * this up in post_load_init() after all the other objects have * been loaded. */ hdr->sc[i].objp = 0; } /* read the properties from the load image and store them */ for (i = 0 ; i < li_cnt ; ++i, p += 2 + VMB_DATAHOLDER) { /* decode the property data */ vm_prop_id_t prop = (vm_prop_id_t)osrp2(p); vm_val_t val; vmb_get_dh(p + 2, &val); /* store the property */ hdr->alloc_prop_entry(prop, &val, 0); } } /* ------------------------------------------------------------------------ */ /* * Property evaluator - createInstance */ int CVmObjTads::getp_create_instance(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { /* create a persistent instance */ return getp_create_common(vmg_ self, retval, in_argc, FALSE); } /* * Property evaluator - createTransientInstance */ int CVmObjTads::getp_create_trans_instance(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { /* create a transient instance */ return getp_create_common(vmg_ self, retval, in_argc, TRUE); } /* * Common handler for createInstance() and createTransientInstance() */ int CVmObjTads::getp_create_common(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc, int is_transient) { uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(0, 0, TRUE); /* check arguments - any number are allowed */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* * push myself as the first argument - 'self' is the superclass of the * object to be created */ G_interpreter->push_obj(vmg_ self); /* * Create an instance - this will recursively execute the new object's * constructor, if it has one. Note that we have one more argument * than provided by the caller, because we've pushed the implicit * argument ('self') that create_from_stack uses to identify the * superclass. */ retval->set_obj(create_from_stack_intern(vmg_ 0, argc + 1, is_transient)); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - createClone */ int CVmObjTads::getp_create_clone(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); vm_obj_id_t new_obj; CVmObjTads *tobj; vm_tadsobj_prop *entry; ushort i; vm_tadsobj_hdr *hdr = get_hdr(); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* * create a new object with the same number of superclasses as I have, * and with space for all of my properties */ new_obj = create(vmg_ FALSE, get_sc_count(), hdr->prop_entry_free); tobj = (CVmObjTads *)vm_objp(vmg_ new_obj); /* copy my superclass list to the new object */ for (i = 0 ; i < get_sc_count() ; ++i) tobj->set_sc(vmg_ i, get_sc(i)); /* copy my properties to the new object */ for (i = hdr->prop_entry_free, entry = hdr->prop_entry_arr ; i != 0 ; --i, ++entry) { /* * Store the property in the new object. We don't need to store * undo for the property, as the object is entirely new since the * last savepoint (as there can't have been a savepoint while we've * been working, obviously). */ tobj->set_prop(vmg_ 0, self, entry->prop, &entry->val); } /* the return value is the new object ID */ retval->set_obj(new_obj); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - createInstanceOf */ int CVmObjTads::getp_create_instance_of(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { /* create a persistent instance */ return getp_create_multi_common(vmg_ self, retval, in_argc, FALSE); } /* * Property evaluator - createTransientInstanceOf */ int CVmObjTads::getp_create_trans_instance_of( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { /* create a persistent instance */ return getp_create_multi_common(vmg_ self, retval, in_argc, TRUE); } /* * Common handler for createInstanceOf() and createTransientInstanceOf() */ int CVmObjTads::getp_create_multi_common(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc, int is_transient) { uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(0, 0, TRUE); /* check arguments - any number are allowed */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* create the new instance */ retval->set_obj(create_from_stack_multi(vmg_ argc, is_transient)); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Iteration callback for getp_set_sc_list. On changing an object's * superclass list, we have to rebuild the inheritance path cache for any * object that has the modified object anywhere in its path. */ struct set_sc_cb_ctx { set_sc_cb_ctx(vm_obj_id_t obj) { this->obj = obj; } vm_obj_id_t obj; }; void CVmObjTads::set_sc_cb(VMG_ vm_obj_id_t obj, void *ctx0) { /* if this is a TadsObject instance, update it */ if (CVmObjTads::is_tadsobj_obj(vmg_ obj)) { /* cast the context to our private structure */ set_sc_cb_ctx *ctx = (set_sc_cb_ctx *)ctx0; /* get this object's header */ vm_tadsobj_hdr *hdr = ((CVmObjTads *)vm_objp(vmg_ obj))->get_hdr(); /* * if it has a cached inheritance path, check to see if it contains * the object being changed */ tadsobj_objid_and_ptr *path = hdr->inh_path; if (path != 0) { /* scan the path for 'obj' */ for ( ; path->id != VM_INVALID_OBJ ; ++path) { /* if this superclass is 'obj', we must delete the path */ if (path->id == ctx->obj) { /* we need to drop this path */ hdr->inval_inh_path(); /* no need to look any further */ break; } } } } } /* ------------------------------------------------------------------------ */ /* * Property evaluator - setSuperclassList */ int CVmObjTads::getp_set_sc_list(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { static CVmNativeCodeDesc desc(1); int cnt; int i; vm_val_t ele; int sc_cnt; vm_tadsobj_hdr *hdr = get_hdr(); vm_val_t *lst; /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* get the list argument (but leave it on the stack for now) */ lst = G_stk->get(0); if (!lst->is_listlike(vmg0_) || (cnt = lst->ll_length(vmg0_)) < 0) err_throw(VMERR_BAD_TYPE_BIF); /* we need at least one argument - the minimal root is TadsObject */ if (cnt < 1) err_throw(VMERR_BAD_VAL_BIF); /* * Check for a special case: our entire superclass list consists of * [TadsObject]. In this case, we have nothing in our internal * superclass list, since our only superclass is our metaclass. */ lst->ll_index(vmg_ &ele, 1); if (cnt == 1 && ele.typ == VM_OBJ && ele.val.obj == metaclass_reg_->get_class_obj(vmg0_)) { /* use an empty internal superclass list */ sc_cnt = 0; } else { /* * Scan the superclasses. Each superclass must be a TadsObject, * with the one exception that if we have only one superclass, it * can be the TadsObject intrinsic class itself, signifying that we * have no superclasses. */ for (i = 1 ; i <= cnt ; ++i) { /* get this element from the list */ lst->ll_index(vmg_ &ele, i); /* it has to be an object of type TadsObject */ if (ele.typ != VM_OBJ || !is_tadsobj_obj(vmg_ ele.val.obj)) err_throw(VMERR_BAD_VAL_BIF); /* * make sure that this superclass doesn't inherit from 'self' - * if it does, that would create a circular inheritance * hierarchy, which is illegal */ if (vm_objp(vmg_ ele.val.obj)->is_instance_of(vmg_ self)) err_throw(VMERR_BAD_VAL_BIF); } /* the list is valid - we need one superclass per list element */ sc_cnt = cnt; } /* mark the overall object as modified if it's not already */ mark_modified(vmg_ G_undo, self); /* if there's a system undo object, add undo for the change */ if (G_undo != 0) { /* allocate a list for the results */ vm_val_t oldv; oldv.set_obj(CVmObjList::create(vmg_ FALSE, hdr->sc_cnt)); CVmObjList *oldp = (CVmObjList *)vm_objp(vmg_ oldv.val.obj); /* build the superclass list */ for (i = 0 ; i < hdr->sc_cnt ; ++i) { /* add this superclass to the list */ ele.set_obj(hdr->sc[i].id); oldp->cons_set_element(i, &ele); } /* * Add an undo record with the original superclass list as the old * value. Use the 'invalid' property as the property key; when * this property is used with a list value, it indicates a * superclass list change. */ G_undo->add_new_record_prop_key(vmg_ self, VM_INVALID_PROP, &oldv); } /* update the superclass list with the given list */ change_superclass_list(vmg_ lst, sc_cnt); /* discard arguments */ G_stk->discard(); /* no return value */ retval->set_nil(); /* we need to clear all cached superclass path lists involving 'self' */ set_sc_cb_ctx ctx(self); G_obj_table->for_each(vmg_ &set_sc_cb, &ctx); /* handled */ return TRUE; } /* * Change the superclass list to the given list. 'lstp' is the new * superclass list, in constant list format (i.e., a packed array of * dataholder values). */ void CVmObjTads::change_superclass_list(VMG_ const vm_val_t *lst, int cnt) { vm_tadsobj_hdr *hdr = get_hdr(); int i; /* keep the count within range */ cnt = (cnt > UINT16MAXVAL ? UINT16MAXVAL : cnt < 0 ? 0 : cnt); /* * if we're increasing the number of superclasses, expand our object * header to make room */ if (cnt > (int)hdr->sc_cnt) { /* expand the header to accomodate the new superclass list */ ext_ = (char *)vm_tadsobj_hdr::expand_to( vmg_ this, hdr, cnt, hdr->prop_entry_cnt); /* get the new header */ hdr = get_hdr(); } /* set the new superclass count */ hdr->sc_cnt = (ushort)cnt; /* set the new superclasses */ for (i = 0 ; i < cnt ; ++i) { vm_val_t ele; /* get this element from the list */ lst->ll_index(vmg_ &ele, i+1); /* set this superclass in the header */ hdr->sc[i].id = ele.val.obj; hdr->sc[i].objp = (CVmObjTads *)vm_objp(vmg_ ele.val.obj); } /* invalidate the cached inheritance path */ hdr->inval_inh_path(); } /* ------------------------------------------------------------------------ */ /* * Get a method pointer. This is almost like an ordinary getprop, but if * the property contains a method, returns a function pointer to the method * rather than evaluating it. */ int CVmObjTads::getp_get_method(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(1); /* check arguments: getMethod(&propid) */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* retrieve the property ID */ vm_obj_id_t prop = CVmBif::pop_propid_val(vmg0_); /* find the property */ vm_obj_id_t source_obj; tadsobj_sc_search_ctx curpos(vmg_ self, this); if (curpos.find_prop(vmg_ prop, retval, &source_obj)) { /* the property is definfed - check its type */ switch (retval->typ) { case VM_CODEOFS: /* it's a direct call to code - convert to a function pointer */ retval->typ = VM_FUNCPTR; break; case VM_DSTRING: /* it's a self-printing constant string - convert to string */ retval->typ = VM_SSTRING; break; case VM_OBJX: /* * It's an executable object, which is either a self-printing * non-constant string or an execute-on-invoke anonymous * function. In either case, simply convert it back to an * ordinary value. */ retval->typ = VM_OBJ; break; case VM_BIFPTRX: /* * executable built-in function pointer - convert back to a * regular bif pointer */ retval->typ = VM_BIFPTR; break; default: /* other types aren't executable methods, so return nil */ retval->set_nil(); break; } } else { /* this property isn't defined - return nil */ retval->set_nil(); } /* handled */ return TRUE; } /* * Set a method pointer. This is almost like an ordinary setprop, but if * the value to assign contains a function pointer, this sets the property * to a method rather than to a pointer-to-function value. */ int CVmObjTads::getp_set_method(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments: setMethod(&propid, val) */ static CVmNativeCodeDesc desc(2); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* * retrieve the property ID and the method value to set (leave the * value on the stack as gc protection) */ vm_prop_id_t prop = CVmBif::pop_propid_val(vmg0_); vm_val_t mval = *G_stk->get(0); /* check the type */ switch (mval.typ) { case VM_FUNCPTR: /* * a function pointer translates directly to a regular method, by * directly calling the code body */ mval.typ = VM_CODEOFS; break; case VM_OBJ: /* * We can accept string and invokable objects. For either one of * these, we store the object reference and mark it as an * execute-on-eval object rather than an ordinary object value. */ if (CVmObjString::is_string_obj(vmg_ mval.val.obj) || vm_objp(vmg_ mval.val.obj)->get_invoker(vmg_ 0)) { /* * We can handle this object by executing it on evaluation. * Mark it as a special execute-on-eval object rather than an * ordinary object. */ mval.typ = VM_OBJX; } else { /* it's not an acceptable type of object */ err_throw(VMERR_BAD_TYPE_BIF); } break; case VM_BIFPTR: /* built-in function pointer - mark it as an execute-on-eval bif */ mval.typ = VM_BIFPTRX; break; case VM_SSTRING: /* * A constant string translates into a self-printing string. * SSTRING and DSTRING have the same data representation, so we * merely need to switch the type to flag it as print-on-eval. */ mval.typ = VM_DSTRING; break; default: /* other types are not acceptable */ err_throw(VMERR_BAD_TYPE_BIF); } /* set the property value */ set_prop(vmg_ G_undo, self, prop, &mval); /* discard the method value */ G_stk->discard(); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Intrinsic Class Modifier object implementation */ /* metaclass registration object */ static CVmMetaclassIntClsMod metaclass_reg_obj_icm; CVmMetaclass *CVmObjIntClsMod::metaclass_reg_ = &metaclass_reg_obj_icm; /* * Get a property. Intrinsic class modifiers do not have intrinsic * superclasses, because they're effectively mix-in classes. Therefore, * do not look for intrinsic properties or intrinsic superclass properties * to resolve the property lookup. */ int CVmObjIntClsMod::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { /* * try finding the property in our property list or a superclass * property list */ tadsobj_sc_search_ctx curpos(vmg_ self, this); if (curpos.find_prop(vmg_ prop, val, source_obj)) return TRUE; /* * We didn't find it in our list, so we don't have the property. * Because we're an intrinsic mix-in, we don't look for an intrinsic * implementation or an intrinsic superclass implementation. */ return FALSE; } /* * Inherit a property. As with get_prop(), we don't want to inherit from * any intrinsic superclass if we don't find the property in our property * list or an inherited property list. */ int CVmObjIntClsMod::inh_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t orig_target_obj, vm_obj_id_t defining_obj, vm_obj_id_t *source_obj, uint *argc) { /* * try finding the property in our property list or a superclass * property list */ if (search_for_prop_from(vmg_ prop, val, orig_target_obj, source_obj, defining_obj)) return TRUE; /* * we didn't find it in our list, and we don't want to inherit from any * intrinsic superclass, so we don't have the property */ return FALSE; } /* * Build my property list. We build the complete list of methods defined * in the intrinsic class modifier for all classes, including any modify * base classes that we further modify. */ void CVmObjIntClsMod::build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval) { /* push a self-reference for gc protection */ G_stk->push()->set_obj(self); /* build our own list */ CVmObjTads::build_prop_list(vmg_ self, retval); /* if we have a base class that we further modify, add its list */ if (get_sc_count() != 0) { vm_obj_id_t base_id; CVmObject *base_obj; /* get the base class */ base_id = get_sc(0); base_obj = vm_objp(vmg_ base_id); /* get its list only if it's of our same metaclass */ if (base_obj->get_metaclass_reg() == get_metaclass_reg()) { vm_val_t base_val; /* save our list for gc protection */ G_stk->push(retval); /* get our base class's list */ base_obj->build_prop_list(vmg_ base_id, &base_val); /* add this list to our result list */ vm_objp(vmg_ retval->val.obj)-> add_val(vmg_ retval, retval->val.obj, &base_val); /* discard our gc protection */ G_stk->discard(); } } /* discard gc protection */ G_stk->discard(); } ����������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmtobj.h��������������������������������������������������������������������������0000664�0000000�0000000�00000106222�12650170723�0015333�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMTOBJ.H,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtobj.h - VM TADS Object implementation Function Notes This implementation assumes a non-relocating memory manager, both for the fixed part (the CVmObject part) and the variable part (the "extension," located in the variable-part heap) of our objects. In the present implementation, the memory manager satisfies this requirement, and there are no plans to change this. The memory manager is designed *in principal* to allow for object relocation (specifically as a means to reduce heap fragmentation), so it's not a foregone conclusion that such a thing will never be implemented. However, given the large memories of modern machines (especially relative to the size of a typical tads application), and given that recent academic research has been calling into question the conventional wisdom that heap fragmentation is actually a problem in practice, we consider the probability that we will want to implement a relocation memory manager low, and thus we feel it's better to exploit the efficiencies of using and storing direct object pointers in some places in this code. Modified 10/30/98 MJRoberts - Creation */ #ifndef VMTOBJ_H #define VMTOBJ_H #include <stdlib.h> #include <string.h> #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" /* forward-declare our main class */ class CVmObjTads; /* ------------------------------------------------------------------------ */ /* * TADS-Object image file data. The image file state is loaded into an * image object data block, and we set up our own internal data based on * it at load time. The image file data block is arranged as follows: * *. UINT2 superclass_count *. UINT2 load_image_property_count *. UINT2 flags *. UINT4 superclass_1 *. ... *. UINT4 superclass_N *. UINT2 load_image_property_ID_1 *. DATAHOLDER load_image_property_value_1 *. ... *. UINT2 load_image_property_ID_N *. DATAHOLDER load_image_property_value_N */ /* superclass structure for object extension */ struct vm_tadsobj_sc { vm_obj_id_t id; CVmObjTads *objp; }; /* * For our in-memory object extension, we use a structure that stores the * object data. We store the properties in a hash table keyed on property * ID. */ struct vm_tadsobj_hdr { /* allocate */ static vm_tadsobj_hdr *alloc(VMG_ class CVmObjTads *self, unsigned short sc_cnt, unsigned short prop_cnt); /* delete */ void free_mem(); /* reallocate an existing object to expand its property table */ static vm_tadsobj_hdr *expand(VMG_ class CVmObjTads *self, vm_tadsobj_hdr *obj); /* * reallocate an existing object to expand its property table to the * given minimum number of property entries */ static vm_tadsobj_hdr *expand_to(VMG_ class CVmObjTads *self, vm_tadsobj_hdr *obj, size_t new_sc_cnt, size_t min_prop_cnt); /* invalidate the cached inheritance path, if any */ void inval_inh_path() { /* if we have an inheritance path cached, forget it */ if (inh_path != 0) { /* forget the path, so that we recalculate it on demand */ t3free(inh_path); inh_path = 0; } } /* find a property entry */ struct vm_tadsobj_prop *find_prop_entry(uint prop); /* allocate a new hash entry */ vm_tadsobj_prop *alloc_prop_entry(vm_prop_id_t prop, const vm_val_t *val, unsigned int flags); /* calculate the hash code for a property */ unsigned int calc_hash(uint prop) const { /* simply take the property ID modulo the table size */ return (unsigned int)(prop & (hash_siz - 1)); } /* check to see if we have the required number of free entries */ int has_free_entries(size_t cnt) const { return cnt <= (size_t)(prop_entry_free - prop_entry_cnt); } /* cache and return the inheritance search path for this object */ struct tadsobj_objid_and_ptr *get_inh_search_path(VMG0_); /* * Inheritance search table. We build and save the search path for any * class with multiple superclasses, because the inheritance path for a * class with multiple base classes can be somewhat time-consuming to * determine. For objects with only one base class, we don't bother * caching a path, since the path is trivial to calculate in these * cases. */ struct tadsobj_objid_and_ptr *inh_path; /* load image object flags (a combination of VMTOBJ_OBJF_xxx values) */ unsigned short li_obj_flags; /* internal object flags (a combination of VMTO_OBJ_xxx values) */ unsigned short intern_obj_flags; /* * Number of hash buckets, and a pointer to the bucket array. (The * hash bucket array is allocated as part of the same memory block as * this structure - we suballocate it from the memory block when * allocating the structure.) 'hash_arr[hash]' points to the head of * a list of property entries with the given hash value. */ unsigned short hash_siz; struct vm_tadsobj_prop **hash_arr; /* * Pointer to our allocation array of hash buckets. We suballocate * this out of our allocation block. (Note that this isn't the hash * table; this is the pool of elements out of which hash table entries * - not buckets, but the entries in the lists pointed to by the * buckets - are allocated.) */ struct vm_tadsobj_prop *prop_entry_arr; /* total number of hash entries allocated */ unsigned short prop_entry_cnt; /* * Index of next available hash entry. Hash entries are never * deleted, so we don't have to worry about returning entries to the * free pool. So, the free pool simply consists of entries from this * index to the maximum index (prop_entry_cnt - 1). * * When we run out of entries, we must reallocate this entire * structure to make room for more. This means that reallocation is * fairly expensive, but this is acceptable because we will always * want to resize the hash table at the same time anyway. We always * resize the hash table on exhausting our current allocation size * because we pick the hash table size based on the expected maximum * number of entries; once we exceed that maximum, we must reconsider * the hash table size. */ unsigned short prop_entry_free; /* * Number of superclasses, and the array of superclasses. We * overallocate the structure to make room for enough superclasses. * (Note that this means the 'sc' field must be the last thing in the * structure.) */ unsigned short sc_cnt; vm_tadsobj_sc sc[1]; }; /* * Tads-object property entry. Each hash table entry points to a linked * list of these entries. */ struct vm_tadsobj_prop { /* my property ID */ vm_prop_id_t prop; /* pointer to the next entry at the same hash value */ vm_tadsobj_prop *nxt; /* flags */ unsigned char flags; /* my value */ vm_val_t val; }; /* * Internal object flags */ /* from load image - object originally came from image file */ #define VMTO_OBJ_IMAGE 0x0001 /* modified - object has been modified since being loaded from image */ #define VMTO_OBJ_MOD 0x0002 /* * Property entry flags */ /* modified - this property is not from the load image file */ #define VMTO_PROP_MOD 0x01 /* we've stored undo for this property since the last savepoint */ #define VMTO_PROP_UNDO 0x02 /* ------------------------------------------------------------------------ */ /* * Load Image Object Flag Values - these values are stored in the image * file object header. */ /* class - the object represents a class, not an instance */ #define VMTOBJ_OBJF_CLASS 0x0001 /* ------------------------------------------------------------------------ */ /* * Initial empty property table size. When we initially load an object, * we'll allocate this many empty slots for modifiable properties. */ const ushort VMTOBJ_PROP_INIT = 16; /* ------------------------------------------------------------------------ */ /* * TADS object interface. */ class CVmObjTads: public CVmObject { friend class CVmMetaclassTads; friend struct tadsobj_sc_search_ctx; friend struct vm_tadsobj_hdr; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is the given object a TadsObject object? */ static int is_tadsobj_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return create_from_stack_intern(vmg_ pc_ptr, argc, FALSE); } /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop); /* create an object with no initial extension */ static vm_obj_id_t create(VMG_ int in_root_set); /* * Create an object with a given number of superclasses, and a given * number of property slots. The property slots are all initially * allocated to modified properties. */ static vm_obj_id_t create(VMG_ int in_root_set, ushort superclass_count, ushort prop_slots); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* create an instance of this object */ void create_instance(VMG_ vm_obj_id_t self, const uchar **pc_ptr, uint argc); /* determine if the object has a finalizer method */ virtual int has_finalizer(VMG_ vm_obj_id_t /*self*/); /* invoke the object's finalizer */ virtual void invoke_finalizer(VMG_ vm_obj_id_t self); // $$$ testing virtual int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const { if (((vm_tadsobj_hdr *)ext_)->hash_siz == 0) return TRUE; else return (val->typ == VM_OBJ && val->val.obj == self); } virtual uint calc_hash(VMG_ vm_obj_id_t self, int /*depth*/) const { if (((vm_tadsobj_hdr *)ext_)->hash_siz == 0) return 0; else return (uint)(((ulong)self & 0xffff) ^ (((ulong)self & 0xffff0000) >> 16)); } // $$$ end testing /* get the number of superclasses of this object */ virtual int get_superclass_count(VMG_ vm_obj_id_t self) const { /* * if we have no superclass, inherit the default, since we * inherit from the system TadsObject class; if we have our own * superclasses, return them */ if (get_sc_count() == 0) return CVmObject::get_superclass_count(vmg_ self); else return get_sc_count(); } /* get the nth superclass of this object */ virtual vm_obj_id_t get_superclass(VMG_ vm_obj_id_t self, int idx) const { /* * if we have no superclass, inherit the default, since we * inherit from the system TadsObject class; if we have our own * superclasses, return them */ if (get_sc_count() == 0) return CVmObject::get_superclass(vmg_ self, idx); else if (idx >= get_sc_count()) return VM_INVALID_OBJ; else return get_sc(idx); } /* determine if I'm a class object */ virtual int is_class_object(VMG_ vm_obj_id_t /*self*/) const { return (get_li_obj_flags() & VMTOBJ_OBJF_CLASS) != 0; } /* determine if I'm an instance of the given object */ int is_instance_of(VMG_ vm_obj_id_t obj); /* this object type provides properties */ int provides_props(VMG0_) const { return TRUE; } /* enumerate properties */ void enum_props(VMG_ vm_obj_id_t self, void (*cb)(VMG_ void *ctx, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val), void *cbctx); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* inherit a property */ int inh_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t orig_target_obj, vm_obj_id_t defining_obj, vm_obj_id_t *source_obj, uint *argc); /* build my property list */ void build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval); /* * Receive notification of a new savepoint. We keep track of * whether or not we've saved undo information for each modifiable * property in the current savepoint, so that we can avoid saving * redundant undo information when repeatedly changing a property * value (since only the first change in a given savepoint needs to * be recorded). When we start a new savepoint, we obviously * haven't yet stored any undo information for the new savepoint, so * we can simply clear all of the undo records. */ void notify_new_savept() { clear_undo_flags(); } /* apply undo */ void apply_undo(VMG_ struct CVmUndoRecord *rec); /* mark a reference in an undo record */ void mark_undo_ref(VMG_ struct CVmUndoRecord *undo); /* * remove stale weak references from an undo record -- we keep only * normal strong references, so we don't need to do anything here */ void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* mark references */ void mark_refs(VMG_ uint state); /* * remove weak references - we keep only normal (strong) references, * so this routine doesn't need to do anything */ void remove_stale_weak_refs(VMG0_) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* restore to image file state */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* perform post-load initialization */ void post_load_init(VMG_ vm_obj_id_t self); /* determine if the object has been changed since it was loaded */ int is_changed_since_load() const; /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* get the nth superclass */ vm_obj_id_t get_sc(uint n) const { return get_hdr()->sc[n].id; } /* get a pointer to the object for the nth superclass */ CVmObjTads *get_sc_objp(VMG_ ushort n) const { return get_hdr()->sc[n].objp; } /* set the nth superclass to the given object */ void set_sc(VMG_ ushort n, vm_obj_id_t obj) { get_hdr()->sc[n].id = obj; get_hdr()->sc[n].objp = (CVmObjTads *)vm_objp(vmg_ obj); } /* static class initialization/termination */ static void class_init(VMG0_); static void class_term(VMG0_); protected: /* create an object with no initial extension */ CVmObjTads() { ext_ = 0; } /* * Create an object with a given number of superclasses, and a given * number of property slots. All property slots are initially * allocated to the modifiable property list. */ CVmObjTads(VMG_ ushort superclass_count, ushort prop_count); /* internal handler to create from stack arguments */ static vm_obj_id_t create_from_stack_intern(VMG_ const uchar **pc_ptr, uint argc, int is_transient); /* * internal handler to create with multiple inheritance from arguments * passed on the stack */ static vm_obj_id_t create_from_stack_multi(VMG_ uint argc, int is_transient); /* get the load image object flags */ uint get_li_obj_flags() const { return get_hdr()->li_obj_flags; } /* set the object flags */ void set_li_obj_flags(ushort flags) { get_hdr()->li_obj_flags = flags; } /* * mark the object as modified; sets the VMTO_OBJ_MOD flag, and adds an * undo record for the transition if the flag wasn't previously set */ void mark_modified(VMG_ class CVmUndo *undo, vm_obj_id_t self); /* * Allocate memory - this replaces any existing extension, so the * caller must take care to free the extension (if one has already * been allocated) before calling this routine. * * If 'from_image' is true, we're allocating memory for use with an * object loaded from an image file, so we'll ignore the superclass * count and leave the image_data pointer in the header unchanged. * If 'from_image' is false, we're allocating memory for a dynamic * object that does not have a presence in the image file, so we'll * allocate space for the superclass list as part of the extension * and set the image_data pointer in the header to refer the extra * space after the modifiable property array and undo bit array. */ void alloc_mem(VMG_ ushort sc_count, ushort mod_prop_count, int from_image); /* get a property from the intrinsic class */ int get_prop_intrinsic(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * Search for a property, continuing a previous search from the given * point. defining_obj is the starting point for the search: we start * searching in the target object's inheritance tree after * defining_obj. This is used to continue an inheritance search from * a given point, as needed for the 'inherited' operator, for example. */ static int search_for_prop_from(VMG_ uint prop, vm_val_t *val, vm_obj_id_t orig_target_obj, vm_obj_id_t *source_obj, vm_obj_id_t defining_obj); /* load the image file properties and superclasses */ void load_image_props_and_scs(VMG_ const char *ptr, size_t siz); /* get/set the superclass count */ ushort get_sc_count() const { return get_hdr()->sc_cnt; } void set_sc_count(ushort cnt) { get_hdr()->sc_cnt = cnt; } /* change the superclass list */ void change_superclass_list(VMG_ const vm_val_t *lst, int cnt); /* clear all undo flags */ void clear_undo_flags(); /* -------------------------------------------------------------------- */ /* * Low-level format management - these routines encapsulate the byte * layout of the object in memory. This is a bit nasty because we * keep the object's contents in the portable image format. */ /* get my header */ inline struct vm_tadsobj_hdr *get_hdr() const { return (vm_tadsobj_hdr *)ext_; } /* property evaluator - undefined property */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - createInstance */ int getp_create_instance(VMG_ vm_obj_id_t, vm_val_t *, uint *); /* property evaluator - createClone */ int getp_create_clone(VMG_ vm_obj_id_t, vm_val_t *, uint *); /* property evaluator - createTransientInstance */ int getp_create_trans_instance(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc); /* property evaluator - createInstanceOf */ int getp_create_instance_of(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc); /* property evaluator - createTransientInstanceOf */ int getp_create_trans_instance_of(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc); /* common handler for createInstance and createTransientInstance */ int getp_create_common(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc, int is_transient); /* common handler for createInstanceOf and createTransientInstanceOf */ int getp_create_multi_common(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc, int is_transient); /* property evaluator - setSuperclassList */ int getp_set_sc_list(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc); /* object table iteration callback for setSuperclassList */ static void set_sc_cb(VMG_ vm_obj_id_t obj, void *ctx); /* property evaluator - getMethod */ int getp_get_method(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc); /* property evaluator - setMethod */ int getp_set_method(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc); /* property evaluation function table */ static int (CVmObjTads::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassTads: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "tads-object/030005"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjTads(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjTads(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjTads::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjTads::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; /* ------------------------------------------------------------------------ */ /* * Intrinsic class modifier object. This object is for use as a modifier * object for an intrinsic class. * * This is a simple subclass of the regular TADS-Object class. The only * difference is that we resolve properties a little differently: unlike * regular TADS Objects, this class is essentially a mix-in, and has no * intrinsic superclass at all. This means that the only place we look * for a property in get_prop is in our property list; we specifically do * not look for an intrinsic property, nor do we look for a superclass * that provides an intrinsic property. */ class CVmObjIntClsMod: public CVmObjTads { friend class CVmMetaclassIntClsMod; public: static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObjTads::is_of_metaclass(meta)); } /* is the given object an intrinsic class modifier object? */ static int is_intcls_mod_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { /* can't create instances of intrinsic class modifiers */ err_throw(VMERR_ILLEGAL_NEW); AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjTads::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* inherit a property */ int inh_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t orig_target_obj, vm_obj_id_t defining_obj, vm_obj_id_t *source_obj, uint *argc); /* build my property list */ void build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval); /* create an object with no initial extension */ CVmObjIntClsMod() { ext_ = 0; } /* * Create an object with a given number of superclasses, and a given * number of property slots. All property slots are initially * allocated to the modifiable property list. */ CVmObjIntClsMod(VMG_ ushort superclass_count, ushort prop_count) : CVmObjTads(vmg_ superclass_count, prop_count) { } }; /* * Registration table object */ class CVmMetaclassIntClsMod: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "int-class-mod/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjIntClsMod(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjIntClsMod(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjIntClsMod::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjIntClsMod:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; /* ------------------------------------------------------------------------ */ /* * object ID + pointer structure */ struct tadsobj_objid_and_ptr { vm_obj_id_t id; CVmObjTads *objp; }; /* ------------------------------------------------------------------------ */ /* * Queue element for the inheritance path search queue */ struct pfq_ele { /* object ID of this element */ vm_obj_id_t obj; /* pointer to the object */ CVmObjTads *objp; /* next queue element */ pfq_ele *nxt; }; /* allocation page */ struct pfq_page { /* next page in the list */ pfq_page *nxt; /* the elements for this page */ pfq_ele eles[50]; }; /* ------------------------------------------------------------------------ */ /* * Queue for search_for_prop(). This implements a special-purpose work * queue that we use to keep track of the objects yet to be processed in * our depth-first search across the inheritance tree. */ class CVmObjTadsInhQueue { public: CVmObjTadsInhQueue() { init(); } void init() { /* there's nothing in the free list or the queue yet */ head_ = 0; free_ = 0; /* we have no elements yet */ alloc_ = 0; } ~CVmObjTadsInhQueue() { pfq_page *cur; pfq_page *nxt; /* delete all of the allocated pages */ for (cur = alloc_ ; cur != 0 ; cur = nxt) { /* remember the next page */ nxt = cur->nxt; /* free this page */ t3free(cur); } } /* get the head of the queue */ pfq_ele *get_head() const { return head_; } /* remove the head of the queue and return the object ID */ vm_obj_id_t remove_head() { /* if there's a head element, remove it */ if (head_ != 0) { pfq_ele *ele; /* note the element */ ele = head_; /* unlink it from the list */ head_ = head_->nxt; /* link the element into the free list */ ele->nxt = free_; free_ = ele; /* return the object ID from the element we removed */ return ele->obj; } else { /* there's nothing in the queue */ return VM_INVALID_OBJ; } } /* clear the queue */ void clear() { /* move everything from the queue to the free list */ while (head_ != 0) { pfq_ele *cur; /* unlink this element from the queue */ cur = head_; head_ = cur->nxt; /* link it into the free list */ cur->nxt = free_; free_ = cur; } } /* determine if the queue is empty */ int is_empty() const { /* we're empty if there's no head element in the list */ return (head_ == 0); } /* allocate a path from the contents of the queue */ tadsobj_objid_and_ptr *create_path() const { /* count the elements in the queue */ int cnt; pfq_ele *cur; for (cnt = 0, cur = head_ ; cur != 0 ; cur = cur->nxt) { /* only non-nil elements count */ if (cur->obj != VM_INVALID_OBJ) ++cnt; } /* allocate the path */ tadsobj_objid_and_ptr *path = (tadsobj_objid_and_ptr *)t3malloc( (cnt + 1) * sizeof(tadsobj_objid_and_ptr)); /* initialize the path */ tadsobj_objid_and_ptr *dst; for (dst = path, cur = head_ ; cur != 0 ; cur = cur->nxt) { /* only store non-nil elements */ if (cur->obj != VM_INVALID_OBJ) { dst->id = cur->obj; dst->objp = cur->objp; ++dst; } } /* set the null last entry */ dst->id = VM_INVALID_OBJ; dst->objp = 0; /* return the new path */ return path; } /* * Insert an object into the queue. We'll insert after the given * element (null indicates that we insert at the head of the queue). * Returns a pointer to the newly-inserted element. */ pfq_ele *insert_obj(VMG_ vm_obj_id_t obj, CVmObjTads *objp, pfq_ele *ins_pt) { pfq_ele *ele; /* * If the exact same element is already in the queue, delete the * old copy. This will happen in situations where we have multiple * superclasses that all inherit from a common base class: we want * the common base class to come in inheritance order after the * last superclass that inherits from the common base. By deleting * previous queue entries that match new queue entries, we ensure * that the common class will move to follow (in inheritance order) * the last class that derives from it. */ for (ele = head_ ; ele != 0 ; ele = ele->nxt) { /* if this is the same thing we're inserting, remove it */ if (ele->obj == obj) { /* * clear the element (don't unlink it, as this could cause * confusion for the caller, who's tracking an insertion * point and traversal point) */ ele->obj = VM_INVALID_OBJ; ele->objp = 0; /* * no need to look any further - we know we can never have * the same element appear twice in the queue, thanks to * this very code */ break; } } /* allocate our new element */ ele = alloc_ele(); ele->obj = obj; ele->objp = objp; /* insert it at the insertion point */ if (ins_pt == 0) { /* insert at the head */ ele->nxt = head_; head_ = ele; } else { /* insert after the selected item */ ele->nxt = ins_pt->nxt; ins_pt->nxt = ele; } /* return the new element */ return ele; } protected: /* allocate a new element */ pfq_ele *alloc_ele() { pfq_ele *ele; /* if we have nothing in the free list, allocate more elements */ if (free_ == 0) { pfq_page *pg; size_t i; /* allocate another page */ pg = (pfq_page *)t3malloc(sizeof(pfq_page)); /* link it into our master page list */ pg->nxt = alloc_; alloc_ = pg; /* link all of its elements into the free list */ for (ele = pg->eles, i = sizeof(pg->eles)/sizeof(pg->eles[0]) ; i != 0 ; --i, ++ele) { /* link this one into the free list */ ele->nxt = free_; free_ = ele; } } /* take the next element off the free list */ ele = free_; free_ = free_->nxt; /* return the element */ return ele; } /* head of the active queue */ pfq_ele *head_; /* head of the free element list */ pfq_ele *free_; /* * Linked list of element pages. We allocate memory for elements in * blocks, to reduce allocation overhead. */ pfq_page *alloc_; }; #endif /* VMTOBJ_H */ /* * Register the classes */ VM_REGISTER_METACLASS(CVmObjTads) VM_REGISTER_METACLASS(CVmObjIntClsMod) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmtype.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000043321�12650170723�0015711�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMTYPE.CPP,v 1.3 1999/05/17 02:52:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtype.cpp - VM types Function Notes Modified 11/18/98 MJRoberts - Creation */ #include "t3std.h" #include "vmtype.h" #include "vmobj.h" #include "vmstr.h" #include "vmlst.h" #include "vmpool.h" /* ------------------------------------------------------------------------ */ /* * Compare this value to another value to determine if the two values * are equal. */ int vm_val_t::equals(VMG_ const vm_val_t *v, int depth) const { /* * if the second value is an object and the first isn't, use the object * comparison of the second */ if (v->typ == VM_OBJ && typ != VM_OBJ) return vm_objp(vmg_ v->val.obj) ->equals(vmg_ v->val.obj, this, depth); /* figure out what to do based on my type */ switch(typ) { case VM_NIL: case VM_TRUE: /* we match only if the other value is the same boolean value */ return (v->typ == typ); case VM_STACK: case VM_CODEPTR: /* * we match only if the other value has the same type and its * pointer value matches */ return (v->typ == typ && v->val.ptr == this->val.ptr); case VM_OBJ: /* use the object's polymorphic equality test routine */ return vm_objp(vmg_ this->val.obj) ->equals(vmg_ this->val.obj, v, depth); case VM_PROP: /* we match if the other value is the same property ID */ return (v->typ == VM_PROP && v->val.prop == this->val.prop); case VM_INT: /* we match if the other value is the same integer */ return (v->typ == VM_INT && v->val.intval == this->val.intval); case VM_BIFPTR: case VM_BIFPTRX: /* we match if the other value refers to the same function */ return (v->typ == VM_BIFPTR && v->val.bifptr.set_idx == this->val.bifptr.set_idx && v->val.bifptr.func_idx == this->val.bifptr.func_idx); case VM_ENUM: return (v->typ == VM_ENUM && v->val.enumval == this->val.enumval); case VM_SSTRING: /* use the standard string comparison routine */ return CVmObjString:: const_equals(vmg_ G_const_pool->get_ptr(this->val.ofs), v); case VM_LIST: /* use the standard list comparison routine */ return CVmObjList:: const_equals(vmg_ this, G_const_pool->get_ptr(this->val.ofs), v, depth); case VM_OBJX: /* match if it's the same object */ return (v->typ == typ && v->val.obj == this->val.obj); case VM_CODEOFS: case VM_FUNCPTR: /* we match if the other value is the same code offset */ return (v->typ == typ && v->val.ofs == this->val.ofs); case VM_EMPTY: /* empty never matches anything */ return FALSE; case VM_DSTRING: /* dstrings have no value, and are thus never equal to anything */ return FALSE; default: /* other types are not recognized */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Calculate a hash value */ uint vm_val_t::calc_hash(VMG_ int depth) const { /* see what we have */ switch(typ) { case VM_NIL: /* this is rather arbitrary */ return 0; case VM_TRUE: /* this is arbitrary, but at least make it different from nil */ return 1; case VM_EMPTY: /* also arbitrary */ return 2; case VM_CODEOFS: case VM_FUNCPTR: /* use a 16-bit hash of the code address */ return (uint)((val.ofs & 0xffff) ^ ((val.ofs & 0xffff0000) >> 16)); case VM_OBJX: /* use a 16-bit hash of the object ID */ return (uint)((val.obj & 0xffff) ^ ((val.obj & 0xffff0000) >> 16)); case VM_PROP: /* use the property ID as the hash */ return (uint)val.prop; case VM_INT: case VM_BIFPTR: case VM_BIFPTRX: /* * the set index and function index both tend to be small integers; * multiply them and keep the low-order 16 bits */ return (uint)(val.bifptr.set_idx * val.bifptr.func_idx) & 0xffff; case VM_ENUM: /* use a 16-bit hash of the enum value */ return (uint)((val.enumval & 0xffff) ^ ((val.enumval & 0xffff0000) >> 16)); case VM_OBJ: /* ask the object to calculate its hash value */ return vm_objp(vmg_ val.obj)->calc_hash(vmg_ val.obj, depth); break; case VM_SSTRING: /* get the hash of the constant string */ return CVmObjString:: const_calc_hash(G_const_pool->get_ptr(val.ofs)); break; case VM_LIST: /* get the hash of the constant list */ return CVmObjList:: const_calc_hash(vmg_ this, G_const_pool->get_ptr(val.ofs), depth); default: /* return an arbitrary value for any other type */ return 3; } } /* ------------------------------------------------------------------------ */ /* * Compare this value to the given value. Returns a positive value if * this value is greater than 'val', a negative value if this value is * less than 'val', and 0 if the two values are equal. Throws an error * if a magnitude comparison is not meaningful for the involved types. */ int vm_val_t::gen_compare_to(VMG_ const vm_val_t *v) const { /* the comparison depends on my type */ switch(typ) { case VM_OBJ: /* let the object perform the comparison */ return vm_objp(vmg_ this->val.obj) ->compare_to(vmg_ this->val.obj, v); case VM_SSTRING: /* compare the string */ return CVmObjString:: const_compare(vmg_ G_const_pool->get_ptr(this->val.ofs), v); case VM_INT: if (v->typ == VM_INT) { /* comparing two integers */ int32_t a = this->val.intval, b = v->val.intval; return a - b; } else if (v->typ == VM_OBJ) { /* the other value is an object, so let it do the comparison */ return -(vm_objp(vmg_ v->val.obj)-> compare_to(vmg_ v->val.obj, this)); } else { /* other types can't be compared to integer */ err_throw(VMERR_INVALID_COMPARISON); AFTER_ERR_THROW(return 0;) } default: /* if the other value is an object, let it do the comparison */ if (v->typ == VM_OBJ) { return -(vm_objp(vmg_ v->val.obj)-> compare_to(vmg_ v->val.obj, this)); } else { /* other types cannot be compared for magnitude */ err_throw(VMERR_INVALID_COMPARISON); AFTER_ERR_THROW(return 0;) } } } /* ------------------------------------------------------------------------ */ /* * Is this a numeric value? Returns true for integers and BigNumber * values. */ int vm_val_t::nonint_is_numeric(VMG0_) const { /* some objects represent numeric types (e.g., BigNumber) */ if (typ == VM_OBJ) return vm_objp(vmg_ val.obj)->is_numeric(); /* we shouldn't be called with an int, but just in case */ if (typ == VM_INT) return TRUE; /* other types are non-numeric */ return FALSE; } /* * Convert a numeric type to integer */ int32_t vm_val_t::nonint_num_to_int(VMG0_) const { /* if it's an object, ask the object for its integer value */ long l; if (typ == VM_OBJ && vm_objp(vmg_ val.obj)->get_as_int(&l)) return l; /* we shouldn't be called with an int type, but just in case */ if (typ == VM_INT) return val.intval; /* other types are not convertible */ err_throw(VMERR_NUM_VAL_REQD); AFTER_ERR_THROW(return 0); } /* * Convert a numeric type to doule */ double vm_val_t::nonint_num_to_double(VMG0_) const { /* if it's an object, ask the object for its integer value */ double d; if (typ == VM_OBJ && vm_objp(vmg_ val.obj)->get_as_double(vmg_ &d)) return d; /* we shouldn't be called with an int type, but just in case */ if (typ == VM_INT) return (double)val.intval; /* other types are not convertible */ err_throw(VMERR_NUM_VAL_REQD); AFTER_ERR_THROW(return 0.); } /* * Cast to integer */ int32_t vm_val_t::nonint_cast_to_int(VMG0_) const { /* check the type */ switch (typ) { case VM_TRUE: return 1; case VM_NIL: return 0; case VM_INT: /* we shouldn't ever be called with an int value, but just in case */ return val.intval; case VM_SSTRING: { /* get the string constant */ const char *p = G_const_pool->get_ptr(val.ofs); /* parse it as an integer */ vm_val_t i; CVmObjString::parse_num_val( vmg_ &i, p + VMB_LEN, vmb_get_len(p), 10, TRUE); /* return the integer */ return i.val.intval; } case VM_OBJ: /* ask the object to do the conversion */ return vm_objp(vmg_ val.obj)->cast_to_int(vmg0_); default: /* there's no integer conversion */ err_throw(VMERR_NO_INT_CONV); AFTER_ERR_THROW(return 0;) } } /* * Cast to numeric (integer or BigNumber) */ void vm_val_t::cast_to_num(VMG_ vm_val_t *retval) const { /* check the type */ switch (typ) { case VM_TRUE: retval->set_int(1); break; case VM_NIL: retval->set_int(0); break; case VM_INT: /* it's already numeric */ retval->set_int(val.intval); break; case VM_SSTRING: { /* get the string constant */ const char *p = G_const_pool->get_ptr(val.ofs); /* parse it as an integer */ CVmObjString::parse_num_val( vmg_ retval, p + VMB_LEN, vmb_get_len(p), 10, FALSE); } break; case VM_OBJ: /* ask the object to do the conversion */ vm_objp(vmg_ val.obj)->cast_to_num(vmg_ retval, val.obj); break; default: /* there's no numeric conversion */ err_throw(VMERR_NO_NUM_CONV); } } /* * Promote an integer to match my type */ void vm_val_t::promote_int(VMG_ vm_val_t *val) const { if (typ == VM_OBJ) { /* ask the object to perform the promotion */ vm_objp(vmg_ this->val.obj)->promote_int(vmg_ val); } else { /* * we can't promote integers to other types; this is a "numeric * value required" error, since we only try to promote integers * when performing arithmetic involving an int and another type */ err_throw(VMERR_NUM_VAL_REQD); } } /* ------------------------------------------------------------------------ */ /* * Cast to string */ const char *vm_val_t::cast_to_string(VMG_ vm_val_t *new_str) const { /* presume the value is just our own value */ *new_str = *this; switch (typ) { case VM_SSTRING: /* return the string as is */ return get_as_string(vmg0_); case VM_OBJ: /* cast the object to string */ return vm_objp(vmg_ val.obj)->cast_to_string(vmg_ val.obj, new_str); case VM_INT: /* create a printable decimal representation of the string */ { /* format the number into a temporary buffer */ char buf[20]; sprintf(buf, "%ld", (long)val.intval); /* create a new string object for the number */ new_str->set_obj( CVmObjString::create(vmg_ FALSE, buf, strlen(buf))); /* return the string buffer */ return new_str->get_as_string(vmg0_); } case VM_NIL: /* return an empty string */ new_str->set_obj(CVmObjString::create(vmg_ FALSE, 0)); return new_str->get_as_string(vmg0_); case VM_TRUE: /* return 'true' */ new_str->set_obj(CVmObjString::create(vmg_ FALSE, "true", 4)); return new_str->get_as_string(vmg0_); case VM_LIST: /* join the list into a string, separating items with commas */ CVmObjList::join(vmg_ new_str, this, ",", 1); return new_str->get_as_string(vmg0_); default: err_throw(VMERR_NO_STR_CONV); AFTER_ERR_THROW(return 0;) } } /* * Get the underlying string constant value. */ const char *vm_val_t::get_as_string(VMG0_) const { /* check my type */ if (typ == VM_SSTRING) { /* it's a constant string - return its text from the constant pool */ return G_const_pool->get_ptr(val.ofs); } else if (typ == VM_OBJ) { /* it's an object - ask for its underlying string, if any */ return vm_objp(vmg_ val.obj)->get_as_string(vmg0_); } else { /* other types do not have underlying strings */ return 0; } } /* ------------------------------------------------------------------------ */ /* * Get as a code pointer */ const uchar *vm_val_t::get_as_codeptr(VMG0_) const { /* check my type */ switch (typ) { case VM_CODEOFS: case VM_FUNCPTR: /* * these types contain code pool offsets - translate the offset to * a physical pointer */ return (const uchar *)G_code_pool->get_ptr(val.ofs); case VM_CODEPTR: /* it's already a physical code pointer */ return (const uchar *)val.ptr; default: /* other types do not contain code pointers */ return 0; } } /* ------------------------------------------------------------------------ */ /* * Get the underlying list constant value. */ const char *vm_val_t::get_as_list(VMG0_) const { /* check my type */ if (typ == VM_LIST) { /* it's a constant list - return its data from the constant pool */ return G_const_pool->get_ptr(val.ofs); } else if (typ == VM_OBJ) { /* it's an object - ask for its underlying list, if any */ return vm_objp(vmg_ val.obj)->get_as_list(); } else { /* other types do not have underlying lists */ return 0; } } /* ------------------------------------------------------------------------ */ /* * Is this indexable as a list? */ int vm_val_t::is_listlike(VMG0_) const { return (get_as_list(vmg0_) != 0 || (typ == VM_OBJ && (vm_objp(vmg_ val.obj)->get_as_list() || vm_objp(vmg_ val.obj)->is_listlike(vmg_ val.obj)))); } /* * Get the number of elements in a list-like object */ int vm_val_t::ll_length(VMG0_) const { /* try it as a regular list first */ const char *p; if ((p = get_as_list(vmg0_)) != 0) return vmb_get_len(p); /* try it as an object */ if (typ == VM_OBJ) return vm_objp(vmg_ val.obj)->ll_length(vmg_ val.obj); /* we don't have a length */ return -1; } /* * Get an indexed value */ void vm_val_t::ll_index(VMG_ vm_val_t *ret, const vm_val_t *idx) const { /* try it as a regular list first, then as an object */ const char *p; if ((p = get_as_list(vmg0_)) != 0) { /* get the index as an integer value */ int i = idx->cast_to_int(vmg0_); /* check the range */ if (i < 1 || i > (int)vmb_get_len(p)) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* it's in range - retrieve the element */ vmb_get_dh(p + VMB_LEN + (size_t)((i - 1) * VMB_DATAHOLDER), ret); } else if (typ == VM_OBJ) { /* use the object method, allowing operator overloading */ vm_objp(vmg_ val.obj)->index_val_ov(vmg_ ret, val.obj, idx); } else if ((p = get_as_string(vmg0_)) != 0) { /* string - parse the string as an integer in decimal format */ CVmObjString::parse_num_val( vmg_ ret, p + VMB_LEN, vmb_get_len(p), 10, TRUE); } else { /* no value */ ret->set_nil(); } } /* ------------------------------------------------------------------------ */ /* * Is this an object of the given class? */ int vm_val_t::is_instance_of(VMG_ vm_obj_id_t cls) const { return (typ == VM_OBJ && cls != VM_INVALID_OBJ && vm_objp(vmg_ val.obj)->is_instance_of(vmg_ cls)); } /* ------------------------------------------------------------------------ */ /* * Set to an integer giving the datatype code for the given value */ void vm_val_t::set_datatype(VMG_ const vm_val_t *v) { /* check for an object */ if (v->typ == VM_OBJ) { /* check for List and String values */ if (v->get_as_string(vmg0_) != 0) { /* treat this the same as a string constant */ set_int((int)VM_SSTRING); } else if (v->get_as_list(vmg0_) != 0) { /* treat this the same as a list constant */ set_int((int)VM_LIST); } else { /* any other type of object is simply an object */ set_int((int)VM_OBJ); } } else { /* for any other type, return our internal type code */ set_int((int)v->typ); } } /* ------------------------------------------------------------------------ */ /* * Is this a function pointer of some kind? */ int vm_val_t::is_func_ptr(VMG0_) const { return (typ == VM_FUNCPTR || typ == VM_BIFPTR || (typ == VM_OBJ && vm_objp(vmg_ val.obj)->get_invoker(vmg_ 0))); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmtype.h��������������������������������������������������������������������������0000664�0000000�0000000�00000064533�12650170723�0015366�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMTYPE.H,v 1.3 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtype.h - VM types Function Notes Modified 10/21/98 MJRoberts - Creation */ #ifndef VMTYPE_H #define VMTYPE_H #include <string.h> #include <stdlib.h> #include "t3std.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmglob.h" /* * Constant pool/code offset. This is an address of an object in the * pool. Pool offsets are 32-bit values. */ typedef uint32_t pool_ofs_t; /* * Savepoint ID's are stored in a single byte; since we store many * copies of savepoint ID's (since they need to be stored with each undo * list head), we want to save some space on this type. This limits us * to 256 simultaneous savepoints, but this should be more than we * actually want to keep around anyway, because of the amount of memory * it would consume to try to keep more than that around. */ typedef uchar vm_savept_t; const vm_savept_t VM_SAVEPT_MAX = 255; /* * Object ID type. VM_INVALID_OBJ is a distinguished value that serves * as an invalid object ID (a null pointer, effectively); no object can * ever have this ID. */ typedef uint32_t vm_obj_id_t; const vm_obj_id_t VM_INVALID_OBJ = 0; /* * Property ID. Property ID's are 16-bit values. VM_INVALID_PROP is a * distinguished value that serves as an invalid property ID, which can * be used to indicate the absence of a property value. */ typedef uint16_t vm_prop_id_t; const vm_prop_id_t VM_INVALID_PROP = 0; /* * Maximum recursion depth for recursive equality tests and hash * calculations. * * When we're comparing or hashing a tree of references by value, such as * when we're comparing two vectors or hashing a vector, we'll keep track * of the recursion depth of our tree traversal. If we reach this depth, * we'll throw an error on the assumption that the tree contains cycles and * thus cannot be hashed or compared by value. This depth is chosen to be * large enough that it's unlikely we'll exceed it with acyclical trees, * but small enough that we probably won't blow the C++ stack before we * reach this depth. */ const int VM_MAX_TREE_DEPTH_EQ = 256; /* * Datatypes */ enum vm_datatype_t { /* nil - doubles as a null pointer and a boolean false */ VM_NIL = 1, /* true - boolean true */ VM_TRUE, /* * Stack pointer (this is used to store a pointer to the enclosing * frame in a stack frame). This is a native machine pointer. */ VM_STACK, /* * Code pointer (this is used to store a pointer to the return * address in a stack frame, for example). This is a native machine * pointer. This differs from VM_CODEOFS in that this is a native * machine pointer. */ VM_CODEPTR, /* object reference */ VM_OBJ, /* property ID */ VM_PROP, /* 32-bit signed integer */ VM_INT, /* * string constant value - the value is an offset into the constant * pool of the string descriptor */ VM_SSTRING, /* * self-printing string value - the value is an offset into the * constant pool of the string descriptor */ VM_DSTRING, /* * list constant - the value is an offset into the constant pool of * the list descriptor */ VM_LIST, /* * byte-code constant offset - this is an offset into the byte-code * pool. This differs from VM_CODEPTR in that this is an offset in * the byte-code constant pool rather than a native machine pointer. * */ VM_CODEOFS, /* * function pointer - this is represented as an offset into the * byte-code pool. This differs from VM_CODEOFS in that the code * referenced by a VM_CODEOFS value is generally invoked directly * whenever the value is evaluated, whereas VM_FUNCPTR values are * used to convey function pointers, so the underlying code is not * executed implicitly on evaluation of such a value but must be * explicitly invoked. */ VM_FUNCPTR, /* * This is a special pseudo-type used to indicate that a value is * not present. This differs from nil, in that nil is a null * reference or false value, whereas this indicates that there's no * specified value at all. This is used, for example, to indicate * in an undo record that a property did not previously exist. */ VM_EMPTY, /* * This is a special pseudo-type used to indicate that evaluating an * expression requires executing system code. The value stored is a * pointer to a constant CVmNativeCodeDesc object, which describes a * native code method. */ VM_NATIVE_CODE, /* * Enumerated constant */ VM_ENUM, /* * Built-in function pointer. This is represented as an integer value * with the BIF table entry index (the function set ID) in the high 16 * bits, and the function index (the index of the function within its * function set) in the low 16 bits. */ VM_BIFPTR, /* * Special internal pseudo-type for execute-on-evaluation objects. * This is used when an anonymous object or dynamically created string * is stored in an object property *as a method*, meaning that the * object is executed upon evaluation rather than simply returned as an * object value. This is never stored in an image file, but it can be * stored in a saved game file. It's never used in a value that's * visible to user code; it's only for internal storage within * TadsObject property tables and the like. */ VM_OBJX, /* the equivalent of VM_OBJX for built-in function pointers */ VM_BIFPTRX, /* * First invalid type ID. Tools (such as compilers and debuggers) * can use this ID and any higher ID values to flag their own * internal types. */ VM_FIRST_INVALID_TYPE }; /* * Macro to create a private type constant for internal use in a tool. The * compiler uses this to create special non-VM values for its own use * during compilation. Types created with this macro must never appear in * an image file, and must never be exposed to user bytecode via any APIs. */ #define VM_MAKE_INTERNAL_TYPE(idx) \ ((vm_datatype_t)(((int)VM_FIRST_INVALID_TYPE) + (idx))) /* * Value container. Local variables, stack locations, and other value * holders use this structure to store a value and its type. */ struct vm_val_t { vm_datatype_t typ; union { /* stack/code pointer */ const void *ptr; /* object reference */ vm_obj_id_t obj; /* property ID */ vm_prop_id_t prop; /* 32-bit integer */ int32_t intval; /* enumerated constant */ uint32_t enumval; /* sstring/dstring/list constant pool offset/pcode pool offset */ pool_ofs_t ofs; /* native code descriptor */ const class CVmNativeCodeDesc *native_desc; /* pointer to built-in function */ struct { unsigned short set_idx; unsigned short func_idx; } bifptr; } val; /* set various types of values */ void set_empty() { typ = VM_EMPTY; } void set_nil() { typ = VM_NIL; } void set_true() { typ = VM_TRUE; } void set_stack(void *ptr) { typ = VM_STACK; val.ptr = ptr; } void set_codeptr(const void *ptr) { typ = VM_CODEPTR; val.ptr = ptr; } void set_obj(vm_obj_id_t obj) { typ = VM_OBJ; val.obj = obj; } void set_propid(vm_prop_id_t prop) { typ = VM_PROP; val.prop = prop; } void set_int(int32_t intval) { typ = VM_INT; val.intval = intval; } void set_enum(uint32_t enumval) { typ = VM_ENUM; val.enumval = enumval; } void set_sstring(pool_ofs_t ofs) { typ = VM_SSTRING; val.ofs = ofs; } void set_dstring(pool_ofs_t ofs) { typ = VM_DSTRING; val.ofs = ofs; } void set_list(pool_ofs_t ofs) { typ = VM_LIST; val.ofs = ofs; } void set_codeofs(pool_ofs_t ofs) { typ = VM_CODEOFS; val.ofs = ofs; } void set_fnptr(pool_ofs_t ofs) { typ = VM_FUNCPTR; val.ofs = ofs; } void set_bifptr(ushort set_idx, ushort func_idx) { typ = VM_BIFPTR; val.bifptr.set_idx = set_idx; val.bifptr.func_idx = func_idx; } void set_native(const class CVmNativeCodeDesc *desc) { typ = VM_NATIVE_CODE; val.native_desc = desc; } /* * Set nil, specifically for a value that might be interpreted as an * object ID. This sets the object ID field to invalid in addition to * setting the value type to nil, so that callers that just look at the * object ID will see an explicitly invalid object value rather than * something random. */ void set_nil_obj() { typ = VM_NIL; val.obj = VM_INVALID_OBJ; } /* * set an object or nil value: if the object ID is VM_INVALID_OBJ, * we'll set the type to nil */ void set_obj_or_nil(vm_obj_id_t obj) { /* set the object value initially */ typ = VM_OBJ; val.obj = obj; /* if the object is invalid, set the type to nil */ if (obj == VM_INVALID_OBJ) typ = VM_NIL; } /* get an object, or a null pointer if it's not an object */ vm_obj_id_t get_as_obj() const { return typ == VM_OBJ ? val.obj : 0; } /* is this an object of the given class? */ int is_instance_of(VMG_ vm_obj_id_t cls) const; /* set to an integer giving the datatype of the given value */ void set_datatype(VMG_ const vm_val_t *val); /* set to nil if 'val' is zero, true if 'val' is non-zero */ void set_logical(int v) { typ = (v != 0 ? VM_TRUE : VM_NIL); } /* determine if the value is logical (nil or true) */ int is_logical() const { return (typ == VM_NIL || typ == VM_TRUE); } /* * Get a logical as numeric TRUE or FALSE. This doesn't perform * any type checking; the caller must ensure that the value is * either true or nil, or this may return meaningless results. */ int get_logical() const { return (typ == VM_TRUE); } /* get as logical, checking type */ int get_logical_only() const { if (typ == VM_TRUE) return TRUE; else if (typ == VM_NIL) return FALSE; else { err_throw(VMERR_BAD_TYPE_BIF); AFTER_ERR_THROW(return FALSE;) } } /* * Get the underlying string constant value. If the value does not * have an underlying string constant (because it is of a type that * does not store a string value), this will return null. */ const char *get_as_string(VMG0_) const; /* * Cast the value to a string. This attempts to reinterpret the value * as a string. */ const char *cast_to_string(VMG_ vm_val_t *new_str) const; /* * Get the underlying list constant value. If the value does not * have an underlying list constant (because it is of a type that * does not store list data), this returns null. */ const char *get_as_list(VMG0_) const; /* * Is the value indexable as a list? This returns true for types that * have contiguous integer indexes from 1..number of elements. */ int is_listlike(VMG0_) const; /* * For a list-like object, get the number of elements. If this isn't a * list-like object, returns -1. */ int ll_length(VMG0_) const; /* * For a list-like object, get an indexed element. The index is * 1-based, per our usual conventions. */ void ll_index(VMG_ vm_val_t *ret, const vm_val_t *idx) const; /* index with a 1-based integer */ void ll_index(VMG_ vm_val_t *ret, int idx) const { vm_val_t vidx; vidx.set_int(idx); ll_index(vmg_ ret, &vidx); } /* get as a native code pointer value */ const uchar *get_as_codeptr(VMG0_) const; /* * Is this a function pointer of some kind? This returns true if it's * a plain function pointer, a built-in function pointer, or an object * with an invoker (which covers anonymous and dynamic functions). */ int is_func_ptr(VMG0_) const; /* * Convert a numeric value to an integer value. If the value isn't * numeric, throws an error. */ void num_to_logical() { /* check the type */ if (typ == VM_INT) { /* it's an integer - treat 0 as nil, all else as true */ typ = (val.intval == 0 ? VM_NIL : VM_TRUE); } else { /* it's not a number - throw an error */ err_throw(VMERR_NO_LOG_CONV); } } /* determine if the value is an integer */ int is_int() const { return (typ == VM_INT); } /* get the value as an integer, throwing an error if it's any other type */ int32_t get_as_int() const { /* check the type */ if (typ == VM_INT) { /* it's an integer already - return the value directly */ return val.intval; } else { /* not an integer - throw an error */ err_throw(VMERR_NUM_VAL_REQD); AFTER_ERR_THROW(return 0;) } } /* * determine if the type is numeric - this returns true for integer and * BigNumber values, and could match future types that are numeric */ int is_numeric(VMG0_) const { return typ == VM_INT || nonint_is_numeric(vmg0_); } /* * Promote an integer to this numeric type. For example, if 'this' is * a BigNumber, this creates a BigNumber representation of val. This * converts the value in place, replacing '*val' with the promoted * value. */ void promote_int(VMG_ vm_val_t *val) const; /* * Convert a numeric value to an integer. This converts any type for * which is_numeric() returns true, but doesn't convert non-numeric * types. */ int32_t num_to_int(VMG0_) const { return typ == VM_INT ? val.intval : nonint_num_to_int(vmg0_); } /* * Convert a numeric value to a double. This converts any type for * which is_numeric() returns true, but doesn't convert non-numeric * types. */ double num_to_double(VMG0_) const { return typ == VM_INT ? (double)val.intval : nonint_num_to_double(vmg0_); } /* * Cast to an integer. This is more aggressive than num_to_int(), in * that we actively try to convert non-numeric types. */ int32_t cast_to_int(VMG0_) const { return typ == VM_INT ? val.intval : nonint_cast_to_int(vmg0_); } /* * Cast to a numeric type - integer or BigNumber. If the value is * already numeric, we'll return the value unchanged. Otherwise, if an * automatic conversion to a numeric type is possible, we'll return the * converted value. We'll return the "simplest" type that can * precisely represent the value, meaning the type that uses the least * memory and that's fastest for computations. */ void cast_to_num(VMG_ vm_val_t *val) const; /* * determine if the numeric value is zero; throws an error if the * value is not numeric */ int num_is_zero() const { /* check the type */ if (typ == VM_INT) { /* check the integer value to see if it's zero */ return (val.intval == 0); } else { /* it's not a number */ err_throw(VMERR_NUM_VAL_REQD); /* in case the compiler doesn't know we'll never get here */ AFTER_ERR_THROW(return 0;) } } /* * Determine if this value equals a given value. The nature of the * match depends on the type of this value: * * integers, property ID's, code offsets: the types and values must * match exactly. * * string and list constants: the other value must either be the same * type of constant, or an object that has an underlying value of the * same type; and the contents of the strings or lists must match. * * objects: the match depends on the type of the object. We invoke the * object's virtual equals() routine to make this determination. * * 'depth' has the same meaning as in calc_hash(). */ int equals(VMG_ const vm_val_t *val, int depth = 0) const; /* * Calculate a hash for the value. The meaning of the hash varies by * type, but is stable for a given value. 'depth' is a recursion depth * counter, with the same meaning as in CVmObject::calc_hash(). */ uint calc_hash(VMG_ int depth = 0) const; /* * Compare this value to the given value. Returns a value greater than * zero if this value is greater than 'val', a value less than zero if * this value is less than 'val', or 0 if the two values are equal. * Throws an error if the two values are not comparable. * * By far the most common type of comparison is between integers, so we * test in-line to see if we have two integer values, and if so, use a * fast in-line comparison. If we don't have two integers, we'll use * our full out-of-line test, which will look at other more interesting * type combinations. */ int compare_to(VMG_ const vm_val_t *b) const { if (typ == VM_INT && b->typ == VM_INT) return (val.intval > b->val.intval ? 1 : val.intval < b->val.intval ? -1 : 0); else return gen_compare_to(vmg_ b); } /* * relative value comparisons */ /* self > b */ int is_gt(VMG_ const vm_val_t *b) const { if (typ == VM_INT && b->typ == VM_INT) return val.intval > b->val.intval; else return gen_compare_to(vmg_ b) > 0; } /* self >= b */ int is_ge(VMG_ const vm_val_t *b) const { if (typ == VM_INT && b->typ == VM_INT) return val.intval >= b->val.intval; else return gen_compare_to(vmg_ b) >= 0; } /* self < b */ int is_lt(VMG_ const vm_val_t *b) const { if (typ == VM_INT && b->typ == VM_INT) return val.intval < b->val.intval; else return gen_compare_to(vmg_ b) < 0; } /* self <= b */ int is_le(VMG_ const vm_val_t *b) const { if (typ == VM_INT && b->typ == VM_INT) return val.intval <= b->val.intval; else return gen_compare_to(vmg_ b) <= 0; } private: /* out-of-line comparison, used when we don't have two integers */ int gen_compare_to(VMG_ const vm_val_t *val) const; /* * internal int conversion and casting - we separate these from * num_to_int and cast_to_int so that cast_to_int can be inlined for * the common case where the value is already an int */ int nonint_is_numeric(VMG0_) const; int32_t nonint_num_to_int(VMG0_) const; double nonint_num_to_double(VMG0_) const; int32_t nonint_cast_to_int(VMG0_) const; }; /* ------------------------------------------------------------------------ */ /* * Native code descriptor. This describes a native method call of an * intrinsic class object. */ class CVmNativeCodeDesc { public: /* create a descriptor with an exact number of arguments */ CVmNativeCodeDesc(int argc) { /* remember the parameters - there are no optional arguments */ min_argc_ = argc; opt_argc_ = 0; varargs_ = FALSE; } /* create a descriptor with optional arguments (but not varargs) */ CVmNativeCodeDesc(int min_argc, int opt_argc) { /* remember the parameters */ min_argc_ = min_argc; opt_argc_ = opt_argc; varargs_ = FALSE; } /* create a descriptor with optional arguments and/or varargs */ CVmNativeCodeDesc(int min_argc, int opt_argc, int varargs) { /* remember the parameters */ min_argc_ = min_argc; opt_argc_ = opt_argc; varargs_ = varargs; } /* check the given number of arguments for validity */ int args_ok(int argc) const { /* * the actual parameters must number at least the minimum, and * cannot exceed the maximum (i.e., the minimum plus the * optionals) unless we have varargs, in which case there is no * maximum */ return (argc >= min_argc_ && (varargs_ || argc <= min_argc_ + opt_argc_)); } /* minimum argument count */ int min_argc_; /* number of optional named arguments beyond the minimum */ int opt_argc_; /* * true -> varargs: any number of arguments greater than or equal to * min_argc_ are valid */ int varargs_; }; /* ------------------------------------------------------------------------ */ /* * String handling - these routines are provided as covers to allow for * easier adjustment for Unicode or other encodings. Don't include these * if we're compiling interface routines for the HTML TADS environment, * since HTML TADS has its own similar definitions for these, and we don't * need these for interface code. * * NOTE: The purpose of this macro is to indicate that we're being * #included by an HTML TADS source file - that is, the current compile run * is for an htmltads/xxx.cpp file. Do NOT #define this macro when * compiling tads3/xxx.cpp files. The tads3/xxx.cpp files have absolutely * no explicit dependencies on the htmltads subsystem and thus don't * #include the htmltads headers that would provide the conflicting * interface definitions for these functions. The conflict is in the other * direction: some of the htmltads source files explicitly depend on tads3 * headers, so they indirectly #include this header. So, this macro needs * to be defined only when compiling htmltads/xxx.cpp files. */ #ifdef T3_COMPILING_FOR_HTML #include "tadshtml.h" #else inline size_t get_strlen(const textchar_t *str) { return strlen(str); } inline void do_strcpy(textchar_t *dst, const textchar_t *src) { strcpy(dst, src); } #endif /* T3_COMPILING_FOR_HTML */ /* ------------------------------------------------------------------------ */ /* * Portable Binary Representations. When we store certain types of * information in memory, we store it in a format that is identical to * the format we use in portable binary files; using this format allows * us to read and write binary files as byte images, without any * interpretation, which greatly improves I/O performance in many cases. */ /* * Portable binary LENGTH indicator. This is used to store length * prefixes for strings, lists, and similar objects. We use a UINT2 * (16-bit unsigned integer) for this type of value. */ const size_t VMB_LEN = 2; inline void vmb_put_len(char *buf, size_t len) { oswp2(buf, len); } inline size_t vmb_get_len(const char *buf) { return osrp2(buf); } /* * Portable binary unsigned 2-byte integer */ const size_t VMB_UINT2 = 2; inline void vmb_put_uint2(char *buf, uint16_t i) { oswp2(buf, i); } inline uint16_t vmb_get_uint2(const char *buf) { return osrp2(buf); } /* * Portable binary unsigned 4-byte integer */ const size_t VMB_UINT4 = 4; inline void vmb_put_uint4(char *buf, uint32_t i) { oswp4(buf, i); } inline uint32_t vmb_get_uint4(const char *buf) { return osrp4(buf); } /* * Portable binary signed 4-byte integer */ const size_t VMB_INT4 = 4; inline void vmb_put_int4(char *buf, int32_t i) { oswp4s(buf, i); } inline int32_t vmb_get_int4(const char *buf) { return osrp4s(buf); } /* * Portable binary object ID. */ const size_t VMB_OBJECT_ID = 4; inline void vmb_put_objid(char *buf, vm_obj_id_t obj) { oswp4(buf, obj); } inline vm_obj_id_t vmb_get_objid(const char *buf) { return t3rp4u(buf); } /* * Portable binary property ID */ const size_t VMB_PROP_ID = 2; inline void vmb_put_propid(char *buf, vm_obj_id_t prop) { oswp2(buf, prop); } inline vm_prop_id_t vmb_get_propid(const char *buf) { return osrp2(buf); } /* * Portable data holder. This is used to store varying-type data items; * for example, this is used to store an element in a list, or the value * of a property in an object. This type of value stores a one-byte * prefix indicating the type of the value, and a four-byte area in * which the value is stored. The actual use of the four-byte value * area depends on the type. */ const size_t VMB_DATAHOLDER = 5; /* offset from a portable data holder pointer to the data value */ const size_t VMB_DH_DATAOFS = 1; /* store a portable dataholder from a vm_val_t */ void vmb_put_dh(char *buf, const vm_val_t *val); /* store a nil value in a portable dataholder */ inline void vmb_put_dh_nil(char *buf) { buf[0] = VM_NIL; } /* store an object value in a portable dataholder */ inline void vmb_put_dh_obj(char *buf, vm_obj_id_t obj) { buf[0] = VM_OBJ; vmb_put_objid(buf + 1, obj); } /* store a property value in a portable dataholder */ inline void vmb_put_dh_prop(char *buf, vm_prop_id_t prop) { buf[0] = VM_PROP; vmb_put_propid(buf + 1, prop); } /* get the value portion of a vm_val_t from a portable dataholder */ void vmb_get_dh_val(const char *buf, vm_val_t *val); /* get the type from a portable dataholder */ inline vm_datatype_t vmb_get_dh_type(const char *buf) { return (vm_datatype_t)buf[0]; } /* get a vm_val_t from a portable dataholder */ inline void vmb_get_dh(const char *buf, vm_val_t *val) { val->typ = vmb_get_dh_type(buf); vmb_get_dh_val(buf, val); } /* get an object value from a portable dataholder */ inline vm_obj_id_t vmb_get_dh_obj(const char *buf) { return (vm_obj_id_t)t3rp4u(buf+1); } /* get an integer value from a portable dataholder */ inline int32_t vmb_get_dh_int(const char *buf) { return (int32_t)osrp4s(buf+1); } /* get a property ID value from a portable dataholder */ inline vm_prop_id_t vmb_get_dh_prop(const char *buf) { return (vm_prop_id_t)osrp2(buf+1); } /* get a constant offset value from a portable dataholder */ inline pool_ofs_t vmb_get_dh_ofs(const char *buf) { return (pool_ofs_t)t3rp4u(buf+1); } #endif /* VMTYPE_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmtypedh.cpp����������������������������������������������������������������������0000664�0000000�0000000�00000006445�12650170723�0016233�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMTYPEDH.CPP,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtypedh.cpp - type manipulations for data holders Function Notes This is separated from vmtype.cpp so that these functions can be linked into an application without dragging in a bunch of things that other vmtype.cpp code depends upon. Modified 05/11/99 MJRoberts - Creation */ #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmstack.h" /* ------------------------------------------------------------------------ */ /* * Portable data holder manipulation */ /* * store a vm_val_t value in a portable data holder */ void vmb_put_dh(char *buf, const vm_val_t *val) { /* store the type code */ buf[0] = (char)val->typ; /* store the value, in the appropriate type-dependent format */ switch(val->typ) { case VM_OBJ: case VM_OBJX: /* store the object ID as a UINT4 */ oswp4(buf+1, val->val.obj); break; case VM_PROP: /* store the property ID as a UINT2 */ oswp2(buf+1, val->val.prop); break; case VM_INT: oswp4(buf+1, val->val.intval); break; case VM_BIFPTR: case VM_BIFPTRX: /* store the integer as two UINT2s: function index, set index */ oswp2(buf+1, val->val.bifptr.func_idx); oswp2(buf+3, val->val.bifptr.set_idx); break; case VM_ENUM: /* store the enumerated constant value as a UINT4 */ oswp4(buf+1, val->val.enumval); break; case VM_SSTRING: case VM_DSTRING: case VM_LIST: case VM_CODEOFS: case VM_FUNCPTR: /* store the offset value as a UINT4 */ oswp4(buf+1, val->val.ofs); break; default: /* other types have no extra data or cannot be put into a DH */ break; } } /* * Get only the value portion of a vm_val_t from a portable data holder */ void vmb_get_dh_val(const char *buf, vm_val_t *val) { /* read the format appropriate to the type */ switch((vm_datatype_t)buf[0]) { case VM_OBJ: case VM_OBJX: /* get the object ID from the UINT4 */ val->val.obj = (vm_obj_id_t)t3rp4u(buf+1); break; case VM_PROP: /* get the property ID from the UINT2 */ val->val.prop = (vm_prop_id_t)osrp2(buf+1); break; case VM_INT: val->val.intval = osrp4(buf+1); break; case VM_BIFPTR: case VM_BIFPTRX: /* read the function index and set index as UINT2s */ val->val.bifptr.func_idx = osrp2(buf+1); val->val.bifptr.set_idx = osrp2(buf+3); break; case VM_ENUM: /* get the enumerated constant value from the UINT4 */ val->val.enumval = t3rp4u(buf+1); break; case VM_SSTRING: case VM_DSTRING: case VM_LIST: case VM_CODEOFS: case VM_FUNCPTR: /* get the offset value from the UINT4 */ val->val.ofs = t3rp4u(buf+1); break; default: /* other types have no additional data or cannot be put in a DH */ break; } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmtz.cpp��������������������������������������������������������������������������0000664�0000000�0000000�00000220317�12650170723�0015367�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtz.cpp - TADS time zone management Function Implements the time zone cache manager and time zone objects. Notes None Modified 02/05/12 MJRoberts - creation */ #include <assert.h> #include <time.h> #include <ctype.h> #include <stdlib.h> #include <math.h> #include "t3std.h" #include "osstzprs.h" #include "vmtz.h" #include "vmhash.h" #include "vmglob.h" #include "resload.h" #include "vmhost.h" #include "vmdate.h" #include "vmlst.h" #include "vmstr.h" #include "vmtzobj.h" /* ------------------------------------------------------------------------ */ /* * zone table hash entry */ /* hash table entry types */ #define ZONE_HASH_DB 1 /* defined in the zoneinfo db file */ #define ZONE_HASH_LINK 2 /* link (alias) to another zone */ #define ZONE_HASH_SYNTH 3 /* synthesized from an on os_tzinfo_t */ class ZoneHashEntry: public CVmHashEntryCI { public: ZoneHashEntry(const char *str, size_t len, int copy, int typ) : CVmHashEntryCI(str, len, copy) { this->tz = 0; this->typ = typ; this->nxt = 0; this->link_from = 0; } virtual ~ZoneHashEntry() { /* if we loaded a timezone object, delete it */ if (tz != 0) delete tz; } /* resolve links - by default, we don't have any links */ virtual ZoneHashEntry *resolve_links() { return this; } /* get my country code, if I have one */ virtual const char *get_country() { return 0; } /* match my country code to that of another zone */ int match_country(const char *country) { const char *this_country = get_country(); return (this_country != 0 && country[0] != '\0' && memcmp(this_country, country, 2) == 0); } /* entry type (ZONE_HASH_xxx) */ int typ; /* the cached zone object, if loaded */ CVmTimeZone *tz; /* the head of the list of zones that link to us */ class ZoneHashEntryLink *link_from; /* * In addition to the hash table, we keep a simple linked list of all * of the loaded zone objects. This lets us do iteraitons over the * loaded list more easily (and quickly) than using the hash table. */ ZoneHashEntry *nxt; }; class ZoneHashEntryDb: public ZoneHashEntry { public: ZoneHashEntryDb(const char *str, size_t len, int copy, uint32_t seekpos, const char *country) : ZoneHashEntry(str, len, copy, ZONE_HASH_DB), seekpos(seekpos) { memcpy(this->country, country, 2); } /* seek position in the database file */ uint32_t seekpos; /* country code */ virtual const char *get_country() { return country; } char country[2]; }; class ZoneHashEntryLink: public ZoneHashEntry { public: ZoneHashEntryLink(const char *str, size_t len, int copy, ZoneHashEntry *link_to) : ZoneHashEntry(str, len, copy, ZONE_HASH_LINK) { /* add myself to the target's 'from' list */ this->link_to = link_to->resolve_links(); link_nxt = link_to->link_from; this->link_to->link_from = this; } /* resolve links - get the target hash entry for a linked hash entry */ ZoneHashEntry *resolve_links() { return link_to->resolve_links(); } /* get the country from my resolved link */ const char *get_country() { return resolve_links()->get_country(); } /* the zone we link to */ ZoneHashEntry *link_to; /* the next link that links to our link_to */ ZoneHashEntryLink *link_nxt; }; class ZoneHashEntrySynth: public ZoneHashEntry { public: ZoneHashEntrySynth(const char *str, size_t len, int copy, const os_tzinfo_t *desc) : ZoneHashEntry(str, len, copy, ZONE_HASH_SYNTH) { /* copy the description */ memcpy(&synth, desc, sizeof(synth)); } /* synthetic zone description */ os_tzinfo_t synth; }; /* ------------------------------------------------------------------------ */ /* * zone search spec */ struct ZoneSearchSpec { ZoneSearchSpec() { abbr[0] = '\0'; gmtofs = INT32MAXVAL; dst = 'S'; } ZoneSearchSpec(const char *abbr, int32_t gmtofs, char dst) : gmtofs(gmtofs), dst(dst) { set_abbr(abbr); } void set(const char *abbr, int32_t gmtofs, char dst) { set_abbr(abbr); this->gmtofs = gmtofs; this->dst = dst; } void set(const char *abbr, size_t abbrlen, int32_t gmtofs, char dst) { set_abbr(abbr, abbrlen); this->gmtofs = gmtofs; this->dst = dst; } /* abbreviation string */ char abbr[16]; void set_abbr(const char *str, size_t len) { lib_strcpy(abbr, sizeof(abbr), str, len); } void set_abbr(const char *str) { set_abbr(str, str != 0 ? strlen(str) : 0); } /* GMT offset in milliseconds */ int32_t gmtofs; /* 'D' for daylight time, 'S' for standard time */ char dst; }; /* ------------------------------------------------------------------------ */ /* * Abbreviation hash entry. This records a mapping from a zone name * abbreviation (e.g., "EST") to a list of associated zone objects. */ /* individual mapping from an abbreviation to a zone */ struct AbbrToZone { /* the zone we link to */ ZoneHashEntry *zone; /* the GMT offset for this abbreviation in this zone, in milliseconds */ int32_t gmtofs; /* * 'D' if this is daylight savings time, 'S' for standard time, 'B' if * it the abbreviation can refer to both types (Australian zones work * this way: EST is Australian Eastern Standard Time AND Eastern Summer * Time). When we have a 'B' zone, we'll also have the regular 'D' and * 'S' entries for it, so that searches for the specific types will * succeed; the 'B' entry is for get_zone_by_abbr(), to let it know * that the zone isn't to be treated as a single fixed offset the way, * say, PDT would be. */ char dst; }; /* hash entry */ class AbbrHashEntry: public CVmHashEntryCI { public: AbbrHashEntry(const char *str, size_t len, int copy, int entry_cnt) : CVmHashEntryCI(str, len, copy) { this->entry_cnt = entry_cnt; this->entries = new AbbrToZone[entry_cnt]; } ~AbbrHashEntry() { delete [] entries; } void set_zone(int i, ZoneHashEntry *zone, int32_t gmtofs, char dst) { entries[i].zone = zone; entries[i].gmtofs = gmtofs; entries[i].dst = dst; } /* search for a match to a search spec */ ZoneHashEntry *search(int start_idx, const ZoneSearchSpec *spec) { for (int i = start_idx ; i < entry_cnt ; ++i) { if (entries[i].gmtofs == spec->gmtofs && entries[i].dst == spec->dst) return entries[i].zone; } return 0; } /* search for a match to a search spec and a zone object */ ZoneHashEntry *search(int start_idx, const ZoneSearchSpec *spec, const ZoneHashEntry *zone) { for (int i = start_idx ; i < entry_cnt ; ++i) { if (entries[i].zone == zone && entries[i].gmtofs == spec->gmtofs && entries[i].dst == spec->dst) return entries[i].zone; } return 0; } int entry_cnt; AbbrToZone *entries; }; /* ------------------------------------------------------------------------ */ /* * Time zone cache manager */ /* * construction */ CVmTimeZoneCache::CVmTimeZoneCache() { /* create the zone tables */ db_tab_ = new CVmHashTable(64, new CVmHashFuncCI(), TRUE); synth_tab_ = new CVmHashTable(64, new CVmHashFuncCI(), TRUE); /* we haven't loaded our zone database yet */ index_loaded_ = index_loaded_ok_ = FALSE; zone_bytes_ = 0; link_bytes_ = 0; abbr_tab_ = 0; abbr_bytes_ = 0; first_loaded_ = 0; local_zone_ = 0; } /* * destruction */ CVmTimeZoneCache::~CVmTimeZoneCache() { /* delete our zone table, zone index, and abbreviation table */ if (db_tab_ != 0) delete db_tab_; if (synth_tab_ != 0) delete synth_tab_; if (abbr_tab_ != 0) delete abbr_tab_; /* if we have a local zone object, delete it */ if (local_zone_ != 0) delete local_zone_; /* if we loaded the file data sections, delete them */ if (zone_bytes_ != 0) delete [] zone_bytes_; if (link_bytes_ != 0) delete [] link_bytes_; if (abbr_bytes_ != 0) delete [] abbr_bytes_; } /* ------------------------------------------------------------------------ */ /* * Translate a ZoneHashEntry to a CVmTimeZone object */ CVmTimeZone *CVmTimeZoneCache::tz_from_hash(VMG_ ZoneHashEntry *entry) { /* if there's no table entry, there's nothing to load */ if (entry == 0) return 0; /* resolve links */ entry = entry->resolve_links(); /* if we haven't loaded the timezone object yet, do so now */ if (entry->tz == 0) { /* load it */ entry->tz = CVmTimeZone::load(vmg_ entry); /* if successful, add it to the loaded zone list */ if (entry->tz != 0) { entry->nxt = first_loaded_; first_loaded_ = entry; } } /* return the cached object */ return entry->tz; } /* ------------------------------------------------------------------------ */ /* * Parse a zone name in various formats */ CVmTimeZone *CVmTimeZoneCache::parse_zone(VMG_ const char *name, size_t len) { /* treat a null, empty string, or ":local" as the local zone */ if (name == 0 || len == 0 || (len == 6 && memcmp(name, ":local", 6) == 0)) return get_local_zone(vmg0_); /* check for special zones: "Z", "UTC", "GMT" all mean UTC+0 */ if (lib_stricmp(name, len, "z") == 0 || lib_stricmp(name, len, "utc") == 0 || lib_stricmp(name, len, "gmt") == 0) return get_gmtofs_zone(vmg_ 0); /* try +8, -830, +8:30, -8:00:00 */ CVmTimeZone *tz; if ((tz = parse_zone_hhmmss(vmg_ "", name, len)) != 0) return tz; /* try UTC+8, UTC-830, UTC+8:30, UTC-8:00:00 */ if ((tz = parse_zone_hhmmss(vmg_ "utc", name, len)) != 0) return tz; /* try parsing as a POSIX-style EST5EDT string */ if ((tz = parse_zone_posixTZ(vmg_ name, len)) != 0) return tz; /* it's not one of our special formats; look it up in the database */ if ((tz = G_tzcache->get_db_zone(vmg_ name, len)) != 0) return tz; /* return failure */ return 0; } /* * Parse a time zone spec in simplified POSIX TZ notation: EST-5, EST-5EDT, * EST-5EDT-4. */ CVmTimeZone *CVmTimeZoneCache::parse_zone_posixTZ( VMG_ const char *name, size_t len) { /* * Run it through the POSIX TZ parser. Note that the zoneinfo format * uses negative offsets going west (e.g., EST-5), in contrast to the * original TZ format. */ os_tzinfo_t desc; if (oss_parse_posix_tz(&desc, name, len, FALSE)) { /* got it - check to see if it's already defined */ ZoneHashEntry *entry = (ZoneHashEntry *)synth_tab_->find(name, len); if (entry == 0) { /* it's not there yet - create a new entry for it */ entry = new ZoneHashEntrySynth(name, len, TRUE, &desc); synth_tab_->add(entry); } /* find or create its timezone object */ return tz_from_hash(vmg_ entry); } /* it's not ours */ return 0; } /* * get a zone specified in a +hh, +hmm, +hhmm, +hh:mm or +hh:mm:ss format */ CVmTimeZone *CVmTimeZoneCache::parse_zone_hhmmss( VMG_ const char *prefix, const char *name, size_t len) { /* check for the prefix - if it doesn't match, return failure */ size_t plen = strlen(prefix); if (plen != 0 && (len < plen || memicmp(name, prefix, plen) != 0)) return 0; /* skip the prefix */ name += plen; len -= plen; /* parse the offset */ int32_t ofs; if (!parse_hhmmss(ofs, name, len, TRUE) || len != 0) return 0; /* convert the hh:mm:ss value to seconds and look up the zone */ return get_gmtofs_zone(vmg_ ofs); } /* * parse a [+-]h[:mm[:ss]] time/offset value */ int CVmTimeZoneCache::parse_hhmmss( int32_t &ofs, const char *&name, size_t &len, int sign_required) { /* check for the sign */ int s = 1; if (len > 0 && name[0] == '+') ++name, --len; else if (len > 0 && name[0] == '-') ++name, --len, s = -1; else if (sign_required) return FALSE; /* parse the hours */ const char *hhp = name; int hh = lib_atoi_adv(name, len); /* if we had no digits, or more than four in a row, fail */ size_t hhlen = name - hhp; if (hhlen < 1 || hhlen > 4) return FALSE; /* if we had three or more consecutive digits, it's hmm or hhmm format */ int mm = 0, ss = 0; if (hhlen >= 3) { /* the hour is actually hours/minutes - divide out the components */ mm = hh % 100; hh /= 100; } else { /* not hmm/hhmm format, so it's hh[:mm[:ss]] - check for minutes */ if (len >= 3 && *name == ':' && isdigit(name[1]) && isdigit(name[2])) { /* parse the minutes */ ++name, --len; mm = lib_atoi_adv(name, len); /* check for seconds */ if (len >= 3 && *name == ':' && isdigit(name[1]) && isdigit(name[2])) { ++name, --len; ss = lib_atoi_adv(name, len); } } } /* success - compute the offset value in seconds */ ofs = s*(hh*60*60 + mm*60 + ss); return TRUE; } /* ------------------------------------------------------------------------ */ /* * Look up or load a time zone from the database */ CVmTimeZone *CVmTimeZoneCache::get_db_zone(VMG_ const char *name, size_t len) { /* if we haven't loaded the zone index data yet, do so now */ load_db_index(vmg0_); /* look up the hash table entry */ return tz_from_hash(vmg_ (ZoneHashEntry *)db_tab_->find(name, len)); } /* * Generate an offset string in the minimal format: h, h:mm, h:mm:ss. */ #define F_HIDE_ZERO 0x0001 /* suppress a zero offset */ #define F_PLUS_SIGN 0x0002 /* use a plus sign for positive offsets */ static void gen_ofs_string(char *buf, size_t buflen, const char *prefix, int32_t ofs, int flags) { /* figure the sign */ const char *signch = ""; if (ofs < 0) ofs = -ofs, signch = "-"; else if ((flags & F_PLUS_SIGN) != 0) signch = "+"; /* decompose the offset into hours, minutes, and seconds */ int hh = ofs / (60*60); int mm = ofs/60 % 60; int ss = ofs % 60; /* * figure the format: prefix, sign character, then h:mm:ss if we have * non-zero seconds, h:mm if we have non-zero minutes but no seconds, * or just the hours if the minutes and seconds are both zero */ const char *fmt = (ss != 0 ? "%s%s%d:%02d:%02d" : mm != 0 ? "%s%s%d:%02d" : hh != 0 || (flags & F_HIDE_ZERO) == 0 ? "%s%s%d" : "%s"); /* generate the string */ t3sprintf(buf, buflen, fmt, prefix, signch, hh, mm, ss); } /* * Look up or load a time zone for a GMT offset, in seconds */ CVmTimeZone *CVmTimeZoneCache::get_gmtofs_zone(VMG_ int32_t gmtofs_secs) { /* build our special name string */ char name[50]; gen_ofs_string(name, sizeof(name), "UTC", gmtofs_secs, F_HIDE_ZERO | F_PLUS_SIGN); /* if we already have an entry cached, return it */ ZoneHashEntry *entry = (ZoneHashEntry *)synth_tab_->find(name, strlen(name)); if (entry != 0) return tz_from_hash(vmg_ entry); /* create a new synthetic entry for the zone */ os_tzinfo_t desc; memset(&desc, 0, sizeof(desc)); desc.std_ofs = gmtofs_secs; lib_strcpy(desc.std_abbr, sizeof(desc.std_abbr), name); entry = new ZoneHashEntrySynth(name, strlen(name), TRUE, &desc); /* add it to the hash table */ synth_tab_->add(entry); /* translate it to a CVmTimeZone object */ return tz_from_hash(vmg_ entry); } /* * Look up a zone by abbreviation. * * Zone abbreviations are inherently ambiguous, and there's no reliable way * to figure out which zone a user might mean. The ideal solution would be * for users to stop using zone abbreviations entirely and switch to * zoneinfo location keys instead. But that's a tough sell, especially * since most users are blissfully unaware that the problem even exists - * who in the US knows that "CST" is also the name for time zones in Cuba, * China, and Australia? It's unreasonable not to expect users to plug in * abbreviations when entering date strings - especially given that the * Date parser is otherwise very liberal about accepting free-form, * colloquial formats. So we feel it's best to give it the college try at * understanding zone abbreviations, but recognizing that it's ultimately a * "best effort" sort of task, in that it's not something we can cleanly * and definitively solve. * * We fill in *gmtofs_ms with the GMT offset implied by the zone * abbreviation. If this is one of the funny Australian zones that uses * the same abbreviation for both standard and daylight time, we return * INT32MINVAL in *gmtofs_ms instead, to indicate that the abbreviation * doesn't specify an offset by itself, but only specifies the zone; the * offset in this case must be looked up via a date-sensitive query as * though the zone had been specified by zoneinfo name instead of by * abbreviation. */ CVmTimeZone *CVmTimeZoneCache::get_zone_by_abbr( VMG_ int32_t *gmtofs_ms, const char *name, size_t len) { vmtzquery lresult; /* if we haven't loaded the zone index data yet, do so now */ load_db_index(vmg0_); /* make sure we have an abbreviation table */ if (abbr_tab_ == 0) return 0; /* look up the abbrevation */ AbbrHashEntry *ah = (AbbrHashEntry *)abbr_tab_->find(name, len); /* if there's no table entry, or it's empty, there's nothing to find */ if (ah == 0 || ah->entry_cnt == 0) return 0; /* get the local zone information */ CVmTimeZone *ltz = get_local_zone(vmg0_); const char *country = ltz->country(); /* we don't have our selection yet */ AbbrToZone *atoz = 0, *ep; /* * First, look to see if this is an abbreviation used in our system's * local time zone. If so use that, since the most likely case is that * the user is referring to her own local time. */ int i; for (i = 0, ep = ah->entries ; i < ah->entry_cnt ; ++i, ++ep) { if (ep->zone->tz == ltz) { atoz = ep; goto done; } } /* * If that didn't work, look for a zone in the same country as the * local time zone. This seems like the best bet for matching the * user's expectations. */ for (i = 0, ep = ah->entries ; i < ah->entry_cnt ; ++i, ++ep) { if (ep->zone->match_country(country)) { atoz = ep; goto done; } } /* * If that failed, look for something within +- 3 hours of the local * zone. That will at least get something in the same part of the * world, and probably in the same country or a nearby country. Most * zones that use the same abbreviation probably aren't right next door * to each other, either, so this should be pretty selective. (There * are exceptions. CST in North America is Central Standard Time at * -6, but it's also Cuba Standard Time at -5. Hopefully most nearby * zone authorities aren't as mutually antagonistic as the US and Cuba * and do a better job of coordinating their zone naming.) */ ltz->query(&lresult); for (i = 0, ep = ah->entries ; i < ah->entry_cnt ; ++i, ++ep) { if (abs(lresult.gmtofs - ep->gmtofs) < 3*60*60*1000) { atoz = ep; goto done; } } /* * If all else fails, match the first zone in the list. Our zoneinfo * compiler places a specially hand-chosen primary zone for each * abbreviation at the head of the abbreviation's zone list. The hand * picks favor US and European zones, so this isn't the ideal way to * handle this globally, but in the absence of any other selection * criteria there's not much else we can do. */ atoz = &ah->entries[0]; done: /* * Pass the abbreviation's offset back to the caller. Most zone * abbreviations refer to a fixed offset; e.g., PST is always -8, even * in the summer when the US Pacific Time zone is observing daylight * time for wall clock time. Writing "PST" explicitly means that we're * talking about the -8 setting, rather than the current wall clock * setting. * * However, some zones (all in Australia, in the current zoneinfo * database) use a single abbreviation for both standard and daylight * time, so these zones *don't* carry the special fixed offset * information with the abbreviation. For example, Australian EST is * Eastern Standard Time in the winter and Eastern Summer Time in the * summer - same abbreviation, two different clock settings. So if we * determine that we're talking about Australian EST, we need to * determine the offset from the time of year, as we would for a spec * like America/Los_Angeles (as opposed to PST). Abbreviations with * multiple offsets in one zone are marked with 'B' as in the DST * field. */ *gmtofs_ms = (atoz->dst != 'B' ? atoz->gmtofs : INT32MINVAL); /* return the timezone object */ return tz_from_hash(vmg_ atoz->zone); } /* ------------------------------------------------------------------------ */ /* * Get the local system time zone. * * The local time zone is special, in that it's always represented as a * distinct CVmTimeZone object (and a distinct entry in our hash table) * from the object for the zone it represents. This allows callers to * distinguish a date that's implicitly in the local zone from one that's * explicitly in the zone that happens to be the local zone. For example, * in the CVmObjDate package, a date specified as "1 pm" is implicitly in * the local time zone, whereas a date specified as "1 pm PST" is * explicitly in Pacific Standard Time. If the local zone happens to be * PST, both dates will be in PST now; but if the objects are saved and * later restored on another machine that's in New York, the first date ("1 * pm") will be rendered in EST, whereas the second ("1 pm PST") will * continue to be rendered in PST. To make this possible, we have to know * that the first date's CVmTimeZone object is the special "local" object, * separate from the second date's CVmTimeZone object - even though both * represent the same underlying database entry. * * To implement this, if we resolve the local zone to a database entry, we * load a separate copy of the database entry so that the local zone is a * distinct object. */ CVmTimeZone *CVmTimeZoneCache::get_local_zone(VMG0_) { /* if we have a cached local zone, return it */ if (local_zone_ != 0) return local_zone_; /* we haven't found our zone entry yet */ ZoneHashEntry *entry = 0; /* see if we can get a zoneinfo database key from the operating system */ char name[128]; if (os_get_zoneinfo_key(name, sizeof(name)) && load_db_index(vmg0_) && (entry = (ZoneHashEntry *)db_tab_->find(name, strlen(name))) != 0) { /* we found a database entry - load a separate copy */ local_zone_ = CVmTimeZone::load(vmg_ entry); } /* if we didn't find it, try the lower-level timezone description */ os_tzinfo_t info; if (local_zone_ == 0 && os_get_timezone_info(&info)) { /* * If they were able to give us separate standard and daylight * offsets and abbreviations, search for the pair. Otherwise * search for the current one. * * Note that the os_tzinfo_t specifies offsets in seconds, whereas * the ZoneSearchSpec uses milliseconds, so multiply accordingly. */ if (info.std_ofs != info.dst_ofs && info.std_abbr[0] != '\0' && info.dst_abbr[0] != '\0') { ZoneSearchSpec spec[2]; spec[0].set(info.std_abbr, info.std_ofs*1000, 'S'); spec[1].set(info.dst_abbr, info.dst_ofs*1000, 'D'); entry = search(vmg_ spec, 2); } else { /* * they couldn't distinguish standard and daylight, so just use * the one that's currently in effect */ ZoneSearchSpec spec; if (info.is_dst) spec.set(info.dst_abbr, info.dst_ofs*1000, 'D'); else spec.set(info.std_abbr, info.std_ofs*1000, 'S'); entry = search(vmg_ &spec, 1); } /* check to see if we found a matching entry in the database */ if (entry != 0) { /* we found an entry - load a separate copy */ local_zone_ = CVmTimeZone::load(vmg_ entry); } else { /* * we didn't find an entry, so synthesize a new one based on * the zone description we got from the OS */ local_zone_ = new CVmTimeZone(&info); } } /* * If we still don't have anything, try the the standard C++ library's * time functions. These won't give us as much information as we were * hoping to get from the OS, and probably won't work at all given that * the OS routines probably went to the same primary sources that the * CRTL will look at, but it's worth trying as a last resort. */ if (local_zone_ == 0) { /* strftime(%Z) format gives us the local time zone */ os_time_t t = os_time(0); struct tm *tm = os_localtime(&t); strftime(name, sizeof(name), "%Z", tm); /* if we got a zone, look it up */ if (name[0] != '\0') { /* * Get the current minutes in the day in local time, and the * local time day number. For the day number, use the year * times 366 plus the day of the year; this doesn't correspond * to a serial day number in our caldate_t system or really any * other system, but it does produce monotonic values, which is * all we need: our only use for this day number if to compare * the UTC and local day numbers to see if the respective * clocks are on different days, and if so in which direction. */ int ss_local = tm->tm_hour*60*60 + tm->tm_min*60 + tm->tm_sec; long dayno_local = tm->tm_year*366L + tm->tm_yday; char dst = tm->tm_isdst ? 'D' : 'S'; /* repeat the exercise in local time */ tm = os_gmtime(&t); int ss_utc = tm->tm_hour*60*60 + tm->tm_min*60 + tm->tm_sec; long dayno_utc = tm->tm_year*366L + tm->tm_yday; /* if the clocks are on different days, adjust accordingly */ if (dayno_utc > dayno_local) ss_utc += 24*60*60; else if (dayno_utc < dayno_local) ss_utc -= 24*60*60; /* * Search for the zone. For the GMT offset, figure the * difference in milliseconds between local and UTC. */ ZoneSearchSpec spec(name, (ss_local - ss_utc)*1000, dst); if ((entry = search(vmg_ &spec, 1)) != 0) { /* we found a database entry - load a private copy */ local_zone_ = CVmTimeZone::load(vmg_ entry); } else { /* * no database match; synthesize an entry based on the time * zone info we got from the C time functions */ os_tzinfo_t desc; memset(&desc, 0, sizeof(desc)); desc.std_ofs = desc.dst_ofs = ss_local - ss_utc; lib_strcpy(desc.std_abbr, sizeof(desc.std_abbr), name); lib_strcpy(desc.dst_abbr, sizeof(desc.dst_abbr), name); desc.is_dst = tm->tm_isdst; local_zone_ = new CVmTimeZone(&desc); } } } /* if after all that we still have nothing, use UTC */ if (local_zone_ == 0) { /* try looking up the basic UTC zone */ os_tzinfo_t desc; memset(&desc, 0, sizeof(desc)); strcpy(desc.std_abbr, "UTC"); local_zone_ = new CVmTimeZone(&desc); } /* return the local zone we came up with */ return local_zone_; } /* ------------------------------------------------------------------------ */ /* * Create a missing database entry. This represents a database zone that's * not defined in the version of the database we're using, but was imported * from a saved game created with another database version. */ CVmTimeZone *CVmTimeZoneCache::create_missing_zone( VMG_ const char *name, size_t namelen, const os_tzinfo_t *desc) { /* create the entry */ ZoneHashEntry *e = new ZoneHashEntrySynth(name, namelen, TRUE, desc); /* add it to our database table */ db_tab_->add(e); /* return the new entry */ return tz_from_hash(vmg_ e); } /* ------------------------------------------------------------------------ */ /* * search for a zone that matches all of the given search specs */ ZoneHashEntry *CVmTimeZoneCache::search( VMG_ const ZoneSearchSpec *specs, int cnt) { /* if there are no abbreviations, fail */ if (!load_db_index(vmg0_) || abbr_tab_ == 0) return 0; /* if we're searching just one list, this is easy... */ if (cnt == 1) { /* look up this abbreviation */ AbbrHashEntry *e = (AbbrHashEntry *)abbr_tab_->find( specs[0].abbr, strlen(specs[0].abbr)); if (e == 0) return 0; /* return the first entry that matches the spec */ return e->search(0, specs); } else if (cnt == 2) { /* look up the two abbreviations */ AbbrHashEntry *e0 = (AbbrHashEntry *)abbr_tab_->find( specs[0].abbr, strlen(specs[0].abbr)); AbbrHashEntry *e1 = (AbbrHashEntry *)abbr_tab_->find( specs[1].abbr, strlen(specs[1].abbr)); /* if either abbreviation is missing, fail */ if (e0 == 0 || e0->entry_cnt == 0 || e1 == 0 || e1->entry_cnt == 0) return 0; /* find the earliest intersection */ for (int i = 0, j = 0 ; i < e0->entry_cnt && j < e1->entry_cnt ; ++i, ++j) { /* find the next first-list element matching the first spec */ ZoneHashEntry *z0 = e0->search(i, &specs[0]); if (z0 == 0) return 0; /* * look for a second-list element that matches the second spec * AND the first-list zone we just matched */ ZoneHashEntry *z1 = e1->search(j, &specs[1], z0); if (z1 != 0) return z1; /* * no luck; find the next second-list element matching just the * second spec, then find a first-list item matching z1 and the * first spec */ z1 = e1->search(j, &specs[1]); if ((z0 = e0->search(i+1, &specs[0], z1)) != 0) return z0; } /* didn't find it */ return 0; } /* * we're really only used for STD+DST searches; if we're called upon * for anything else, it's unexpected */ return 0; } /* ------------------------------------------------------------------------ */ /* * Load the zoneinfo database index. Returns true on success, false on * failure. */ int CVmTimeZoneCache::load_db_index(VMG0_) { /* if we've already loaded the index (or tried to), we're done */ if (index_loaded_) return index_loaded_ok_; /* we've now tried loading the index */ index_loaded_ = TRUE; /* open the zone file */ osfildef *fp = open_zoneinfo_file(vmg0_); if (fp == 0) return FALSE; /* read the signature */ char buf[24]; int ok = !osfrb(fp, buf, 24) && memcmp(buf, "T3TZ", 4) == 0; /* if the signature checked out, read the zone table */ uint32_t zone_table_len = 0, zone_cnt = 0; if (ok) { /* decode the zone name table length */ zone_table_len = osrp4(buf + 16) - 4; zone_cnt = osrp4(buf + 20); /* allocate space to load the table */ zone_bytes_ = new char[zone_table_len]; /* read the table */ ok = zone_bytes_ != 0 && !osfrb(fp, zone_bytes_, zone_table_len); } /* if everything is working so far, read the link table */ uint32_t link_table_len = 0; if (ok && !osfrb(fp, buf, 8)) { /* load the link table */ link_table_len = osrp4(buf) - 4; /* uint32_t link_cnt = osrp4(buf+4) - not currently needed */ link_bytes_ = new char[link_table_len]; ok = link_bytes_ != 0 && !osfrb(fp, link_bytes_, link_table_len); } /* if we're still good, read the abbreviation mappings */ uint32_t abbr_table_len = 0; if (ok && !osfrb(fp, buf, 8)) { /* load the abbreviation table */ abbr_table_len = osrp4(buf) - 4; /* uint32_t abbr_cnt = osrp4(buf + 4) - not currently needed */ abbr_bytes_ = new char[abbr_table_len]; ok = abbr_bytes_ != 0 && !osfrb(fp, abbr_bytes_, abbr_table_len); } /* we're done with the file */ osfcls(fp); /* if we read the zone table successfully, create a hash table from it */ ZoneHashEntry **zone_index = 0; if (ok) { /* create the numerical index */ zone_index = new ZoneHashEntry *[zone_cnt]; /* populate it */ const char *zone_end = zone_bytes_ + zone_table_len; int zi = 0; for (const char *p = zone_bytes_ ; p < zone_end ; ) { /* read and skip the name length prefix */ size_t len = osrp1(p); ++p; /* create this entry */ ZoneHashEntry *e = new ZoneHashEntryDb( p, len, FALSE, osrp4(p+len+1), p+len+1+4); /* add it to the hash table and zone index */ db_tab_->add(e); zone_index[zi++] = e; /* skip the name, seek pointer, and country code */ p += len + 1 + 4 + 2; } /* read the links */ const char *link_end = link_bytes_ + link_table_len; for (const char *p = link_bytes_ ; p < link_end ; ) { /* read and skip the name length */ size_t len = osrp1(p); ++p; /* read the TO index */ unsigned int to = osrp4(p + len + 1); /* look up the TO field */ ZoneHashEntry *e_to = (to < zone_cnt ? zone_index[to] : 0); /* if we found it, create the new entry for the FROM */ if (e_to != 0) db_tab_->add(new ZoneHashEntryLink(p, len, FALSE, e_to)); /* skip the entry */ p += len + 1 + 4; } } /* if there are no errors so far, read the abbreviation table */ if (ok) { /* create the abbreviation hash table */ abbr_tab_ = new CVmHashTable(64, new CVmHashFuncCI(), TRUE); /* populate it */ const char *abbr_end = abbr_bytes_ + abbr_table_len; for (const char *p = abbr_bytes_ ; p < abbr_end ; ) { /* read and skip the abbreviation length prefix */ size_t len = osrp1(p); ++p; /* read the number of zone entries */ int entry_cnt = osrp1(p + len + 1); /* create and add the hash table entry */ AbbrHashEntry *e = new AbbrHashEntry(p, len, FALSE, entry_cnt); abbr_tab_->add(e); /* populate the entry */ int ei; for (p += len + 1 + 1, ei = 0 ; ei < entry_cnt ; p += 9, ++ei) { /* look up the zone */ uint32_t zi = osrp4(p); ZoneHashEntry *zone = (zi < zone_cnt ? zone_index[zi] : 0); /* add the entry */ e->set_zone(ei, zone, osrp4s(p+4), p[8]); } } } /* we're done with the numerical zone index */ if (zone_index != 0) delete [] zone_index; /* return the status */ index_loaded_ok_ = ok; return ok; } /* * Open the zoneinfo file */ osfildef *CVmTimeZoneCache::open_zoneinfo_file(VMG0_) { /* open the file through the system resource loader */ return G_host_ifc->get_sys_res_loader()->open_res_file( "timezones.t3tz", 0, "TMZN"); } /* ------------------------------------------------------------------------ */ /* * Time zone object */ /* file data block sizes */ const int TRANS_SIZE = 9; const int TYPE_SIZE = 9; const int RULE_SIZE = 17; /* * construction */ CVmTimeZone::CVmTimeZone(ZoneHashEntryDb *entry, const char *file_data, unsigned int trans_cnt, unsigned int type_cnt, unsigned int rule_cnt, unsigned int abbr_bytes) { /* keep a pointer to our hash table entry */ hashentry_ = entry; /* since we have a hash entry, we don't need a separate name */ name_ = 0; /* allocate our arrays (do this first, as they have cross references) */ trans_ = (trans_cnt_ = trans_cnt) != 0 ? new vmtz_trans[trans_cnt] : 0; rule_ = (rule_cnt_ = rule_cnt) != 0 ? new vmtz_rule[rule_cnt] : 0; ttype_ = (ttype_cnt_ = type_cnt) != 0 ? new vmtz_ttype[type_cnt] : 0; abbr_ = (abbr_bytes != 0 ? new char[abbr_bytes] : 0); /* assume we have no descriptive data */ country_[0] = '\0'; coords_[0] = '\0'; desc_ = 0; /* set up to read from the file data block */ const char *p = file_data; unsigned int i; /* decode the transition list */ vmtz_trans *t; for (t = trans_, i = 0 ; i < trans_cnt ; ++i, ++t, p += TRANS_SIZE) { t->dayno = osrp4s(p); t->daytime = osrp4s(p+4); unsigned int idx = osrp1(p+8); t->ttype = idx < type_cnt ? &ttype_[idx] : 0; } /* decode the rule list */ vmtz_rule *r; for (r = rule_, i = 0 ; i < rule_cnt ; ++i, ++r, p += RULE_SIZE) { unsigned int idx = osrp1(p); r->fmt = idx < abbr_bytes ? abbr_ + idx : 0; r->mm = osrp1(p+1); r->when = osrp1(p+2); r->dd = osrp1(p+3); r->weekday = osrp1(p+4); r->at = osrp4s(p+5); r->gmtofs = osrp4s(p+9); r->save = osrp4s(p+13); /* decode the time zone from the AT data */ r->at_zone = (r->at >> 24) & 0xff; r->at &= 0x00ffffff; } /* decode the type list */ vmtz_ttype *tt; for (tt = ttype_, i = 0 ; i < type_cnt ; ++i, ++tt, p += TYPE_SIZE) { tt->gmtofs = osrp4s(p); tt->save = osrp4s(p+4); unsigned int idx = osrp1(p+8); tt->fmt = idx < abbr_bytes ? abbr_ + idx : 0; } /* copy the format abbreviation strings */ if (abbr_bytes != 0) { memcpy(abbr_, p, abbr_bytes); p += abbr_bytes; } /* save the descriptive data, if available */ if (*p != '\0') { /* save the country code */ lib_strcpy(country_, sizeof(country_), p, 2); p += 2; /* save the coordinate string */ lib_strcpy(coords_, sizeof(coords_), p, 15); p += 16; /* save the description */ size_t dlen = osrp1(p); desc_ = new char[dlen + 1]; lib_strcpy(desc_, dlen + 1, p+1, dlen); p += dlen + 2; } else { /* null byte for country - no descriptive information */ } #if 0// DEBUG $$$ for (t = trans_, i = 0 ; i < trans_cnt ; ++i, ++t) { caldate_t cd(t->dayno); printf("%04d-%02d-%02d %02d:%02d -> GMT%+d (%+d) %s\n", cd.y, cd.m, cd.d, (int)t->daytime/(60*60), (int)->daytime/60 % 60, t->ttype->gmtofs, t->ttype->save, t->ttype->fmt); } #endif } /* * Create a synthesized zone from an os descriptor */ CVmTimeZone::CVmTimeZone(ZoneHashEntrySynth *entry) { /* keep a pointer to our zone entry */ hashentry_ = entry; /* since we have a hash entry, we don't need a separate name */ name_ = 0; /* initialize from the descriptor */ init(&entry->synth); } /* * Create a synthesized zone from an os descriptor */ CVmTimeZone::CVmTimeZone(const os_tzinfo_t *desc) { /* no hash entry */ hashentry_ = 0; /* * Synthesize a POSIX-style name from the abbreviation and offset. If * we have both standard and daylight abbreviations and distinct * offsets, generate a POSIX-style "EST5DST" string. Otherwise just * use the active abbreviation and offset. * * Note that the offset in POSIX strings uses the opposite sign from * what we use - EST is +5 in POSIX strings vs -5 in the zoneinfo * scheme (which is what we use). */ char buf[128]; if (desc->std_abbr[0] != 0 && desc->dst_abbr[0] != 0 && desc->std_ofs != desc->dst_ofs) { /* * Use both abbreviations, as in EST5DST. If the daylight offset * is one hour higher than the standard offset, omit the offset * from the daylight part, since that's the default; otherwise * include it. */ char buf1[128], buf2[128]; gen_ofs_string(buf1, sizeof(buf1), desc->std_abbr, -desc->std_ofs, 0); if (desc->dst_ofs == desc->std_ofs + 3600) lib_strcpy(buf2, sizeof(buf2), desc->dst_abbr); else gen_ofs_string(buf2, sizeof(buf2), desc->dst_abbr, -desc->dst_ofs, 0); /* build the combined string */ t3sprintf(buf, sizeof(buf), "%s%s", buf1, buf2); } else if (desc->is_dst) { /* use the daylight abbreviation only */ gen_ofs_string(buf, sizeof(buf), desc->dst_abbr, -desc->dst_ofs, 0); } else { /* use the standard time abbreviation only */ gen_ofs_string(buf, sizeof(buf), desc->std_abbr, -desc->std_ofs, 0); } /* save the name */ name_ = lib_copy_str(buf); /* initialize from the descriptor */ init(desc); } /* * Initialize from an OS timezone descriptor */ void CVmTimeZone::init(const os_tzinfo_t *desc) { /* we don't have any transitions or rules */ trans_ = 0; trans_cnt_ = 0; rule_ = 0; rule_cnt_ = 0; /* make a copy of our one abbreviation(s) */ size_t std_abbr_len = strlen(desc->std_abbr); size_t dst_abbr_len = strlen(desc->dst_abbr); abbr_ = new char[std_abbr_len + dst_abbr_len + 2]; char *std_abbr = abbr_; strcpy(std_abbr, desc->std_abbr); char *dst_abbr = abbr_ + std_abbr_len + 1; strcpy(dst_abbr, desc->dst_abbr); /* if rules for the start/end dates are specified, use them */ if ((desc->dst_start.jday != 0 || desc->dst_start.yday != 0 || desc->dst_start.month != 0) && (desc->dst_end.jday != 0 || desc->dst_end.yday != 0 || desc->dst_end.month != 0)) { /* we have start/end rules - create our copies */ rule_cnt_ = 2; rule_ = new vmtz_rule[2]; /* * Set the rules from the OS description. Note that the OS * description gives offsets in seconds, whereas we store * everything in milliseconds. */ rule_[0].set(&desc->dst_start, dst_abbr, desc->std_ofs*1000, (desc->dst_ofs - desc->std_ofs)*1000); rule_[1].set(&desc->dst_end, std_abbr, desc->std_ofs*1000, 0); /* * allocate space for two types - one for standard time, one for * daylight time */ ttype_ = new vmtz_ttype[2]; ttype_cnt_ = 2; /* set up the standard time information */ ttype_[0].gmtofs = desc->std_ofs * 1000; ttype_[0].save = 0; ttype_[0].fmt = std_abbr; /* set up the daylight time information */ ttype_[1].gmtofs = desc->dst_ofs * 1000; ttype_[1].save = (desc->dst_ofs - desc->std_ofs) * 1000; ttype_[1].fmt = dst_abbr; } else { /* * There are no transition rules, so we only have use for whichever * setting is currently in effect. */ ttype_ = new vmtz_ttype[1]; ttype_cnt_ = 1; /* use the current setting only */ ttype_[0].save = 0; if (desc->is_dst) { ttype_[0].gmtofs = desc->dst_ofs * 1000; ttype_[0].fmt = dst_abbr; } else { ttype_[0].gmtofs = desc->std_ofs * 1000; ttype_[0].fmt = std_abbr; } } /* we have no descriptive information */ country_[0] = '\0'; coords_[0] = '\0'; desc_ = 0; } /* * destruction */ CVmTimeZone::~CVmTimeZone() { /* delete allocated items */ lib_free_str(name_); if (trans_ != 0) delete [] trans_; if (ttype_ != 0) delete [] ttype_; if (rule_ != 0) delete [] rule_; if (abbr_ != 0) delete [] abbr_; if (desc_ != 0) delete [] desc_; } /* * Load or create a CVmTimeZone object from a hash table entry. This * creates a new object, even if the hash table entry already has an * associated object. */ CVmTimeZone *CVmTimeZone::load(VMG_ ZoneHashEntry *entry) { /* load according to the data source in the hash table entry */ if (entry->typ == ZONE_HASH_DB) { /* it's in the database file - open the file */ osfildef *fp = CVmTimeZoneCache::open_zoneinfo_file(vmg0_); if (fp == 0) return 0; /* seek to our data */ osfseek(fp, ((ZoneHashEntryDb *)entry)->seekpos, OSFSK_SET); /* read the length prefix */ char pfx[2]; int ok = !osfrb(fp, pfx, 2); /* allocate space for the file data */ unsigned int alo = osrp2(pfx); char *buf = ok ? new char[alo] : 0; /* read the file data */ ok = buf != 0 && !osfrb(fp, buf, alo); /* decode the header */ unsigned int trans_cnt = osrp2(buf); unsigned int type_cnt = osrp2(buf+2); unsigned int rule_cnt = osrp1(buf+4); unsigned int abbr_bytes = osrp1(buf+5); /* create the time zone object */ CVmTimeZone *tz = 0; if (ok) { tz = new CVmTimeZone( (ZoneHashEntryDb *)entry, buf+6, trans_cnt, type_cnt, rule_cnt, abbr_bytes); } /* release resources */ if (buf != 0) delete [] buf; osfcls(fp); /* return the loaded object */ return tz; } else if (entry->typ == ZONE_HASH_LINK) { /* load from the linked object */ return load(vmg_ entry->resolve_links()); } else if (entry->typ == ZONE_HASH_SYNTH) { /* create from the zone descriptor */ return new CVmTimeZone((ZoneHashEntrySynth *)entry); } else { /* unknown type */ return 0; } } /* ------------------------------------------------------------------------ */ /* * Get the zone name */ const char *CVmTimeZone::get_name(size_t &len) const { /* use the name, or the hash table name, or the primary abbreviation */ if (name_ != 0) { /* we have an explicit name field - use it */ len = strlen(name_); return name_; } else if (hashentry_ != 0) { /* get the name from our hash table entry */ len = hashentry_->getlen(); return hashentry_->getstr(); } else { /* no name or hash entry - use our primary abbreviation */ len = strlen(abbr_); return abbr_; } } /* * Populate a List object with our alias list. This sets the first element * to our primary name, and remaining elements to aliases. */ vm_obj_id_t CVmTimeZone::get_name_list(VMG0_) const { /* no names found so far */ int cnt = 0; /* count names, depending on what information we have */ if (hashentry_ != 0) { /* the hash entry gives us our primary name */ cnt += 1; /* count incoming alias links */ for (ZoneHashEntryLink *e = hashentry_->link_from ; e != 0 ; e = e->link_nxt, ++cnt) ; } else if (name_ != 0) { /* we have only the one primary name */ cnt += 1; } else if (abbr_ != 0 && abbr_[0] != '\0') { /* no name, but we at least have an abbreviation we can use in lieu */ cnt += 1; } /* allocate the result list; stack it for gc protection */ vm_obj_id_t lstid = CVmObjList::create(vmg_ FALSE, cnt); G_stk->push()->set_obj(lstid); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lstid); lst->cons_clear(); /* start at index 0 */ int idx = 0; vm_val_t ele; /* * add our primary name - this is our explicit name_ string, our hash * entry name, or our primary abbreviation (in order of priority) */ if (hashentry_ != 0) { /* set the name from our hash entry */ ele.set_obj(CVmObjString::create( vmg_ FALSE, hashentry_->getstr(), hashentry_->getlen())); lst->cons_set_element(idx++, &ele); /* add our aliases */ for (ZoneHashEntryLink *link = hashentry_->link_from ; link != 0 ; link = link->link_nxt) { ele.set_obj(CVmObjString::create( vmg_ FALSE, link->getstr(), link->getlen())); lst->cons_set_element(idx++, &ele); } } else if (name_ != 0) { /* the first item is our primary name */ ele.set_obj(CVmObjString::create(vmg_ FALSE, name_, strlen(name_))); lst->cons_set_element(idx++, &ele); } else if (abbr_ != 0 && abbr_[0] != '\0') { /* use our primary abbreviation as our name */ ele.set_obj(CVmObjString::create(vmg_ FALSE, abbr_, strlen(abbr_))); lst->cons_set_element(idx++, &ele); } /* discard the gc protection */ G_stk->discard(); /* return the new list object */ return lstid; } /* * Get a history item for a given date */ vm_obj_id_t CVmTimeZone::get_history_item( VMG_ int32_t dayno, int32_t daytime) const { /* query the transition for the given date/time */ vmtzquery result; query(&result, dayno, daytime, FALSE); /* synthesize a transition description */ vmtz_ttype tty(&result); vmtz_trans t(result.start_dayno, result.start_daytime, &tty); /* build and return the List representation */ return t.get_as_list(vmg0_); } /* * Get a list of history items */ vm_obj_id_t CVmTimeZone::get_history_list(VMG0_) const { /* allocate the result list; stack it for gc protection */ vm_obj_id_t lstid = CVmObjList::create(vmg_ FALSE, trans_cnt_ + 1); G_stk->push()->set_obj(lstid); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lstid); lst->cons_clear(); /* set the special first item for the pre-establishment settings */ vmtz_trans t_pre(INT32MINVAL, INT32MINVAL, &ttype_[0]); vm_val_t ele; ele.set_obj(t_pre.get_as_list(vmg0_)); lst->cons_set_element(0, &ele); /* run through the transitions */ vmtz_trans *t = trans_; for (int i = 1 ; i <= trans_cnt_ ; ++i, ++t) { /* add this transition to the list */ ele.set_obj(t->get_as_list(vmg0_)); lst->cons_set_element(i, &ele); } /* discard the gc protection and return the result list */ G_stk->discard(1); return lstid; } /* * Get a list of the ongoing rules */ vm_obj_id_t CVmTimeZone::get_rule_list(VMG0_) const { /* allocate the result list; stack it for gc protection */ vm_obj_id_t lstid = CVmObjList::create(vmg_ FALSE, rule_cnt_); G_stk->push()->set_obj(lstid); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lstid); lst->cons_clear(); /* run through the rules */ vmtz_rule *r = rule_; for (int i = 0 ; i < rule_cnt_ ; ++i, ++r) { /* create a sublist to describe this rule */ vm_val_t ele; ele.set_obj(CVmObjList::create(vmg_ FALSE, 10)); lst->cons_set_element(i, &ele); /* get the sublist and clear it out */ CVmObjList *elst = (CVmObjList *)vm_objp(vmg_ ele.val.obj); elst->cons_clear(); /* [1] = abbreviation */ ele.set_obj(CVmObjString::create(vmg_ FALSE, r->fmt, strlen(r->fmt))); elst->cons_set_element(0, &ele); /* [2] = standard time offset in milliseconds */ ele.set_int(r->gmtofs); elst->cons_set_element(1, &ele); /* [3] = daylight savings delta in milliseconds */ ele.set_int(r->save); elst->cons_set_element(2, &ele); /* [4] = encoded 'when' rule: "Mar lastSun", "Mar Sun>=7", "Mar 7" */ char buf[128]; static const char *mon = "JanFebMarAprMayJunJulAugSepOctNov"; static const char *wkdy = "SunMonTueWedThuFriSat"; static const char *tpl[] = { "%.0s%02d", "last %.3s", "%.3s>=%d", "%.3s<=d" }; if (r->when == 0 && r->dd > 31) { /* day of month past 31 -> it's actually a day of the year */ t3sprintf(buf, sizeof(buf), "DOY %d", r->dd); } else if (r->when >= 0 && r->when < countof(tpl) && r->weekday >= 1 && r->weekday <= 7) { /* valid 'when' code */ memcpy(buf, mon + (r->mm - 1)*3, 3); buf[3] = ' '; t3sprintf(buf+4, sizeof(buf)-4, tpl[(unsigned char)r->when], wkdy + (r->weekday-1)*3, r->dd); } else { /* invalid 'when' code */ t3sprintf(buf, sizeof(buf), "INVAL(when=%d, weekday=%d, dd=%d)", r->when, r->weekday, r->dd); } ele.set_obj(CVmObjString::create(vmg_ FALSE, buf, strlen(buf))); elst->cons_set_element(3, &ele); /* [5] = month */ ele.set_int(r->mm); elst->cons_set_element(4, &ele); /* [6] = when */ ele.set_int(r->when); elst->cons_set_element(5, &ele); /* [7] = day of the month */ ele.set_int(r->dd); elst->cons_set_element(6, &ele); /* [8] = weekday */ ele.set_int(r->weekday); elst->cons_set_element(7, &ele); /* [9] = at time */ ele.set_int(r->at); elst->cons_set_element(8, &ele); /* [10] = at zone */ char u = (r->at_zone == 0x80 ? 'u' : r->at_zone == 0x40 ? 's' : 'w'); ele.set_obj(CVmObjString::create(vmg_ FALSE, &u, 1)); elst->cons_set_element(9, &ele); } /* discard the gc protection and return the result list */ G_stk->discard(1); return lstid; } /* ------------------------------------------------------------------------ */ /* * Query the time zone information at the current moment */ void CVmTimeZone::query(vmtzquery *result) const { /* get the current system time */ int32_t dayno, daytime; caldate_t::now(dayno, daytime); /* query the time zone information */ query(result, dayno, daytime, FALSE); } /* * Query the time zone information at the given time (UTC); daytime is * milliseconds after midnight. */ void CVmTimeZone::query(vmtzquery *result, int32_t dayno, int32_t daytime, int local) const { /* * if we're after the last transition, or we have no transitions, check * for ongoing rules */ if (trans_cnt_ == 0 || trans_[trans_cnt_-1].compare_to(dayno, daytime, local) <= 0) { /* * We won't find our answer in the transition list, so check for * ongoing rules that cover out-of-bounds dates. */ if (rule_cnt_ != 0) { /* * We have ongoing rules. * * Starting in the year AFTER the target date, search for the * last (in time) rule that fires on or before the target date. * This is the rule that defines the time zone for the target * date. If we don't find a rule on or before the target date, * back up one year and repeat. * * The reason we have to start next year is that there's a * possibility that the target date will be in the following * year in local time; rules are generally stated in local * time, so this difference could matter. For example, if the * target date is Dec 31 2010 23:59 UTC, that could be Jan 1 * 2011 07:59 local time. If we then had a rule that fired at * 01:00 on Jan 1, that rule in 2011 would be the correct * result of our search. This is a pathological case that's * unlikely to arise in the wild, but it's not impossible. * Likewise, a target date on Jan 1 could end up preceding a * rule that fires on Dec 31 the previous year; looping * backwards on year until we find a rule that fires on or * before the target will catch these cases. * * Note that our zoneinfo compiler guarantees that it will * produce at least one complete cycle fixed transitions (in * our trans_ list) for the open-ended rules. Since we're * after our last transition, we know that the period before a * rule's trigger date is controlled by the previous rule (in * time order, treating the list as circular) and not by any * other zone history change. This makes it possible to work * backwards through the list, since we don't have to look for * other possible zone definitions in the period before each * rule. */ caldate_t cd(dayno); for (int yy = cd.y + 1 ; yy >= cd.y - 2 ; --yy) { /* look for the last rule firing on or after the target */ int latest = -1; int32_t latest_rday = 0, latest_rtime = 0; for (int i = 0 ; i < rule_cnt_ ; ++i) { /* * we might need the previous rule to resolve the * current rule; the list is circular because it forms * an annual cycle */ int iprv = (i == 0 ? rule_cnt_ : i) - 1; /* get the concrete date for this rule in this year */ int32_t rday, rtime; rule_[i].resolve(rday, rtime, yy, &rule_[iprv]); /* adjust to local time if that's what we're looking for */ if (local) { rtime += rule_[i].gmtofs + rule_[i].save; caldate_t::normalize(rday, rtime); } /* * If the target date is after the resolved rule firing * time, and this rule is later than the last one we * matched, this is the best match so far. (Assume * that we never have two rules on the same day, so we * don't need to bother checking rtime against the last * matching rule.) Note that we don't match exactly at * the rule time - our policy is to put the moment of a * transition in the previous period. */ if ((dayno > rday || (dayno == rday && daytime > rtime)) && (latest < 0 || rday > latest_rday)) { latest = i; latest_rday = rday; latest_rtime = rtime; } } /* if we found a match, return its settings */ if (latest >= 0) { result->set(&rule_[latest], latest_rday, latest_rtime); return; } } /* * We should never get here - it should be *almost* impossible * not to find a match in the prior year, but not quite * impossible: if a zone only has rules that fire late on Dec * 31, we could have a target early on Jan 1 UTC which, when * translated to local time, is before all of the Dec 31 rules. * But it should be truly impossible not to find a match in * target year minus 2 - no UTC-to-local difference could take * us back a whole year. So there's something wrong in our * logic if we get here... */ assert(FALSE); } else if (trans_cnt_ != 0) { /* * There are no rules, and we're after the last transition, so * the last transition is permanent. */ result->set(&trans_[trans_cnt_-1]); } else { /* * There are no rules and no transitions. This means that the * zone is defined entirely by its first time type entry. */ result->set(&ttype_[0], INT32MINVAL, INT32MINVAL); return; } } /* * If we're at or before the first transition, use the * pre-establishment settings. This is usually the local mean time * settings from before a standard time zone was first established at * this location. */ if (trans_[0].compare_to(dayno, daytime, local) >= 0) { /* the pre-establishment settings are in ttype[0] */ result->set(&ttype_[0], INT32MINVAL, INT32MINVAL); return; } /* * The date is within our transition list, so search for the nearest * transition on or before the target date. We're looking for a * date/time after our first transition and before our last, so it's a * date covered by a history item. Search our transition list for the * target date/time. */ int lo = 0, hi = trans_cnt_ - 1, cur; while (lo < hi) { /* split the difference */ cur = (hi + lo)/2; /* check this entry */ vmtz_trans *tcur = &trans_[cur]; int dir = tcur->compare_to(dayno, daytime, local); if (dir < 0) { /* tcur < target - search the upper half */ lo = cur + 1; } else if (dir > 0) { /* tcur > target - search the lower half */ hi = cur - 1; } else { /* * exact match - treat the moment of the transition as being in * the previous interval, since we usually define the moment of * the transition in terms of the local time that was in effect * up until that moment */ result->set(tcur > 0 ? tcur - 1 : tcur); return; } } /* check the final 'lo' value; it might have ended up one too high */ cur = trans_[lo].compare_to(dayno, daytime, local) >= 0 && lo > 0 && trans_[lo-1].compare_to(dayno, daytime, local) < 0 ? lo - 1 : lo; /* set the result */ result->set(&trans_[cur]); } /* ------------------------------------------------------------------------ */ /* * Compare my date to the given date, which can be expressed in either UTC * or local wall clock time. */ int vmtz_trans::compare_to(int32_t dayno, int32_t daytime, int local) const { /* get my value, converting to local time if required */ int32_t dd = this->dayno, tt = this->daytime; if (local) { tt += ttype->gmtofs + ttype->save; caldate_t::normalize(dd, tt); } /* compare by day, then by time if on the same day */ return (dd != dayno ? dd - dayno : tt - daytime); } /* * Create a List object representing the transition */ vm_obj_id_t vmtz_trans::get_as_list(VMG0_) const { /* create a result list for this item - [date, ofs, save, fmt] */ vm_obj_id_t lstid = CVmObjList::create(vmg_ FALSE, 4); G_stk->push()->set_obj(lstid); /* get the list and clear it out, since we're building it in pieces */ CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lstid); lst->cons_clear(); /* * Element [1] is the transition time as a Date object, or nil if the * date is INT32MINVALs (which represents the beginning of time, for * the pre-establishment settings). */ vm_val_t ele; if (dayno == INT32MINVAL && daytime == INT32MINVAL) ele.set_nil(); else ele.set_obj(CVmObjDate::create(vmg_ FALSE, dayno, daytime)); lst->cons_set_element(0, &ele); /* element [2] is the UTC offset */ ele.set_int(ttype->gmtofs); lst->cons_set_element(1, &ele); /* element [3] is the daylight savings delta */ ele.set_int(ttype->save); lst->cons_set_element(2, &ele); /* element [4] is the abbreviation string */ ele.set_obj(CVmObjString::create( vmg_ FALSE, ttype->fmt, strlen(ttype->fmt))); lst->cons_set_element(3, &ele); /* discard gc protection and return the list object */ G_stk->discard(1); return lstid; } /* ------------------------------------------------------------------------ */ /* * synthesize a ttype from a query result */ vmtz_ttype::vmtz_ttype(const vmtzquery *query) { gmtofs = query->gmtofs; save = query->save; fmt = query->abbr; } /* ------------------------------------------------------------------------ */ /* * Resolve a rule in the given year. Returns the firing time in UTC. */ void vmtz_rule::resolve(int32_t &dayno, int32_t &daytime, int year, const vmtz_rule *prev_rule) { /* set up our year and month; we have yet to work out the day */ int w, delta; caldate_t cd(year, mm, 0); /* figure out the day according to our type */ switch (when) { case 0: /* the rule takes effect on a the fixed day 'dd' */ cd.d = dd; break; case 1: /* * The rule takes effect on the last <weekday> of the month. Get * the weekday of the last day of this month (which we can figure * using day 0 of next month). */ cd.m += 1; w = cd.weekday() + 1; /* figure the days from mm/last to the previous target weekday */ delta = w - weekday; if (delta < 0) delta += 7; /* go back that many days */ cd.d -= delta; break; case 2: /* first <weekday> on or after <dd> - get the weekday of <dd> */ cd.d = dd; w = cd.weekday() + 1; /* figure days between <dd> and the next <weekday> */ delta = weekday - w; if (delta < 0) delta += 7; /* go ahead that many days */ cd.d += delta; break; case 3: /* first <weekday> on or before <dd> - get the weekday of <dd> */ cd.d = dd; w = cd.weekday() + 1; /* figure the days from <dd> to the next <weekday> */ delta = weekday - w; if (delta < 0) delta += 7; /* go ahead that many days and back a week */ cd.d += delta - 7; break; } /* we now have our effective date and time */ dayno = cd.dayno(); daytime = at; /* adjust the time zone */ switch (at_zone) { case 0x80: /* the rule was stated in UTC, which is what we're after */ break; case 0x00: /* * The rule was stated in local wall clock time, in the period in * effect just before the rule. That means we use the zone * settings of the *previous* rule, which defines the period * leading up to this rule's firing time. Convert to UTC by * subtracting the standard time offset plus the daylight offset * for the prior period. */ daytime -= prev_rule->gmtofs + prev_rule->save; break; case 0x40: /* * the rule was stated in local standard time, so subtract the * standard time offset for the prior period */ daytime -= prev_rule->gmtofs; break; } /* normalize the time to keep it within 0-23:59:59 */ caldate_t::normalize(dayno, daytime); } /* ------------------------------------------------------------------------ */ /* * Set from an OS descriptor */ void vmtz_rule::set(const os_tzrule_t *desc, const char *abbr, int32_t gmtofs, int32_t save) { /* set the basic description */ this->fmt = abbr; this->gmtofs = gmtofs; this->save = save; /* the OS description always uses local wall clock time */ this->at = desc->time * 1000; this->at_zone = 0; /* transcode the date to our format */ if (desc->jday != 0) { /* * "Jn" = nth day of the year, NOT counting Feb 29. Since the date * mapping is the same for leap years and non-leap years, this is * just an obtuse way of specifying a month and day. So, figure * the mm/dd date that the Jn date represents, and map it to our * mode 0 (day <dd> of <month>). We can figure the month/day by * setting up a calendar with "January N" in any non-leap year - * since it's a non-leap year, it won't have a Feb 29, so we'll get * the right mapping for dates after J59. */ caldate_t cd(2011, 1, desc->jday); cd.normalize_date(); this->when = 0; this->mm = (char)cd.m; this->dd = (short)cd.d; } else if (desc->yday != 0) { /* * "n" = nth day of the year, counting Feb 29. We can encode this * as "January n", even if n > 31, since our calendar calculator * handle overflows in the day of the month by carrying them into * subsequent months. */ this->when = 0; this->mm = 1; this->dd = (short)desc->yday; } else if (desc->month != 0) { /* Month/week/day spec */ this->mm = (char)desc->month; this->weekday = (char)desc->day; if (desc->week == 5) { /* week 5 means "last <weekday>", which is our mode 1 */ this->when = 1; } else { /* * week 1-4 means "nth <weekday>"; we can encode this as mode * 2, "weekday>=n", where n is 1 for the first week, 8 for the * second week, 15 for the third week, 22 for the fourth week */ this->when = 2; this->dd = (char)(desc->week - 1)*7 + 1; } } } /* ------------------------------------------------------------------------ */ /* * Convert a UTC time to local */ const char *CVmTimeZone::utc_to_local( int32_t &dayno, int32_t &daytime, int32_t &tzofs) { /* look up the time zone information at the given time */ vmtzquery result; query(&result, dayno, daytime, FALSE); /* figure the full offset */ tzofs = result.gmtofs + result.save; /* adjust the time to local by adding the GMT offset */ daytime += tzofs; caldate_t::normalize(dayno, daytime); /* return the abbreviation */ return result.abbr; } /* * Convert a local time to UTC */ void CVmTimeZone::local_to_utc(int32_t &dayno, int32_t &daytime) { /* look up the time zone information at the given local time */ vmtzquery result; query(&result, dayno, daytime, TRUE); /* adjust the time to local by subtracting the GMT offset */ daytime -= result.gmtofs + result.save; caldate_t::normalize(dayno, daytime); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmtz.h����������������������������������������������������������������������������0000664�0000000�0000000�00000042102�12650170723�0015026�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtz.h - TADS time zone management Function TADS uses the IANA zoneinfo database (aka the Olson database) for converting time values between universal time (UTC) and local time representations. This is used in particular in the Date intrinsic class, which provides a bytecode API for time and date conversions, formatting, and arithmetic. This zoneinfo database lets us determine the correct local time, given a UTC time, in just about any regional time zone on just about any date since time zones were invented. It takes into account the ever-changing daylight savings rules as well as historical time zone realignments and redefinitions. We don't just project the current time zone rules back in time, as most operating systems do in their time managers; the zoneinfo database tells us the actual changes that occurred. For example, in the United States, we'll properly start daylight savings time from the last Sunday in April in 1986, but use the second Sunday in April in 2007 and later. Likewise, we'll properly display EWT (Eastern War Time) if asked for a local time in New York in 1943. We use a custom binary version of the zoneinfo database. The TADS source code distribution includes a compiler (written in TADS) that reads the zoneinfo sources files as distributed by IANA and generates our custom binary file. We don't compile the file merely to have it in binary form, but rather because the source form of the information requires quite a lot of work to interpret. It uses a considerable amount of abstraction, both to make the zone histories and rules more readily human-readable and to make them more compact and reusable (e.g., it allows for expressing rules at the national level that span multiple time zones, such as in the US). The main reason to compile the material is to boil down the abstraction to a form that can be used efficiently at run-time. While we're at it we generate a binary file format that's fast and easy to read. This package includes a cache manager that reads zone data from the binary file on demand. The zoneinfo database (and thus our custom binary version of it) contains quite a lot of information, since it has historical records on each of about 450 time zones around the world. It amounts to about 300k of compiled binary data in our format. Rather than read all of that into memory, we'll read it as needed per time zone. Most programs will use only the local time zone of the local system, so in most cases we'll never need to read more than one zone. Notes None Modified 02/05/12 MJRoberts - creation */ #ifndef VMTZ_H #define VMTZ_H #include "t3std.h" #include "vmglob.h" #include "vmobj.h" /* ------------------------------------------------------------------------ */ /* * Time zone cache. This loads time zone data on demand. */ class CVmTimeZoneCache { friend class CVmTimeZone; public: CVmTimeZoneCache(); ~CVmTimeZoneCache(); /* * Parse a timezone name, returning a CVmTimeZone object representing * the zone. This accepts names in the following formats: * *. :local - the local zone * *. UTC+offset - or GMT+ofs, Z+ofs, or simply +ofs: a zone at the *. given hh[:mm[:ss]] offset from UTC. This type of *. zone uses a fixed offset year round and for all *. dates, not tied to any location's history. * *. zoneinfoName - the name of a zoneinfo database entry, such as *. "America/Los_Angeles". This type uses the zone *. history from the database. */ class CVmTimeZone *parse_zone(VMG_ const char *name, size_t len); /* * Get a time zone object by name from the zoneinfo database; returns * the cached copy if present, otherwise loads the entry from disk. */ class CVmTimeZone *get_db_zone(VMG_ const char *name, size_t len); class CVmTimeZone *get_db_zone(VMG_ const char *name) { return get_db_zone(vmg_ name, strlen(name)); } /* * Get a time zone object by GMT offset, in seconds. Returns a zone * that represents the fixed GMT offset, not for any particular * location. */ class CVmTimeZone *get_gmtofs_zone(VMG_ int32_t gmtofs_secs); /* * Get a time zone object by abbreviation. This searches the * abbreviation list for a match, and returns the corresponding * timezone object if found. We also fill in the GMT offset for the * abbreviation - this is required when the zone is specified by * abbreviation because an abbreviation carries a standard time or * daylight time designation, which we'd normally infer from the zone * and date information. An explicit daylight or standard time * designation overrides the time that's normally in effect on the * given date. For example, "1/1/2012 12:00 PDT" corresopnds to * 1/1/2012 19:00 GMT, whereas "1/1/2012 12:00" America/Los_Angeles * corresponds to 20:00 GMT. The difference is that standard time is * normally in effect on that date: a time specified by location * (America/Los_Angeles) is interpreted as a wall clock time for that * date according to the prevailing standard/daylight rules for the * zone, whereas a time explicitly in PDT is interpreted as daylight * time regardless of whether daylight or standard time is actually in * effect in the zone on the given date. */ class CVmTimeZone *get_zone_by_abbr(VMG_ int32_t *gmtofs_ms, const char *name, size_t len); /* * Create a missing database zone. This creates a placeholder zone * representing a zone imported from a saved game using a different * version of the database that includes a zone that's not defined in * the version we're using. */ class CVmTimeZone *create_missing_zone( VMG_ const char *name, size_t namelen, const os_tzinfo_t *desc); /* open the zoneinfo binary file */ static osfildef *open_zoneinfo_file(VMG0_); /* * Get the system default local time zone. Returns a CVmTimeZone * object representing the local zone. */ class CVmTimeZone *get_local_zone(VMG0_); /* is the given zone the special local zone? */ int is_local_zone(class CVmTimeZone *zone) const { return zone == local_zone_; } protected: /* load the zoneinfo database index from the binary file */ int load_db_index(VMG0_); /* get or load the CVmTimeZone object for a hash entry */ CVmTimeZone *tz_from_hash(VMG_ class ZoneHashEntry *entry); /* parse a "UTC+-h[:mm[:ss]]" offset string into a zone */ CVmTimeZone *parse_zone_hhmmss( VMG_ const char *prefix, const char *name, size_t len); /* parse a zone spec in POSIX TZ format (EST5, EST5EDT, EST5EDT4) */ CVmTimeZone *parse_zone_posixTZ(VMG_ const char *name, size_t len); /* parse an [+-]h[:mm[:ss]] time/offset value */ int parse_hhmmss(int32_t &ofs, const char *&name, size_t &len, int sign_required); /* search for a set of zone specifications */ class ZoneHashEntry *search( VMG_ const struct ZoneSearchSpec *specs, int cnt); class ZoneHashEntry *search( VMG_ const struct ZoneSearchSpec *specs, class AbbrHashEntry **e, int cnt, class ZoneHashEntry *zone); /* have we attempted to load the index yet? */ int index_loaded_; /* did we successfully load the index? */ int index_loaded_ok_; /* * Cached local zone object. Determining the local time zone can be a * non-trivial amount of work, so we cache this after we figure it out * the first time. */ class CVmTimeZone *local_zone_; /* zone name hash table for zones loaded from the zoneinfo database */ class CVmHashTable *db_tab_; /* zone name hash table for synthesized zones - GMT+ofs, EST5EDT, etc */ class CVmHashTable *synth_tab_; /* abbreviation hash table */ class CVmHashTable *abbr_tab_; /* head of linked list of *loaded* zone objects */ class ZoneHashEntry *first_loaded_; /* zone index, loaded from the binary file */ char *zone_bytes_; /* link index, loaded from the binary file */ char *link_bytes_; /* abbreviation mapping index, loaded from the binary file */ char *abbr_bytes_; }; /* ------------------------------------------------------------------------ */ /* * Time type. This describes the settings for a time zone during the * period covered by a transition. */ struct vmtz_ttype { vmtz_ttype() { } vmtz_ttype(const struct vmtzquery *query); vmtz_ttype(int32_t gmtofs, int32_t save, const char *fmt) { this->gmtofs = gmtofs; this->save = save; this->fmt = fmt; } /* * standard time ofset, in milliseconds - add this to UTC to get local * standard time */ int32_t gmtofs; /* * daylight time offset, in milliseconds - add to local standard time * to get local wall clock time */ int32_t save; /* time zone format string abbreviation ("EDT", etc) */ const char *fmt; }; /* * Transition - this records a change in a time zone's rules. Each * transition covers the period from its FROM date (inclusive) to (but not * including) the next transition's FROM date. */ struct vmtz_trans { vmtz_trans() { } vmtz_trans(int32_t dayno, int32_t daytime, vmtz_ttype *ttype) { this->dayno = dayno; this->daytime = daytime; this->ttype = ttype; } /* * Date/time of the transition, relative to 3/1/0000 UTC; the time is * in milliseconds after midnight. This is starting time for the * transition - it's the moment that our ttype comes into effect. */ int32_t dayno; int32_t daytime; /* compare my date to the given date/time (time in milliseconds) */ int compare_to(int32_t dayno, int32_t daytime, int local) const; /* create a List object representing the transition */ vm_obj_id_t get_as_list(VMG0_) const; /* time zone settings in effect on and after this transition */ struct vmtz_ttype *ttype; }; /* * Time zone daylight savings rule. This describes a switch between * standard time and daylight time. Any given rule fires once a year. * Rules apply to the period after the last enumerated transition in the * time zone; this allows us to project the current rules that apply in the * zone forward in time indefinitely, without having to enumerate all of * the possible transitions (which would be impractical given that our time * type can in principle represent millions of years into the future). */ struct vmtz_rule { /* the format abbreviation to use when the rule is in effect */ const char *fmt; /* the gmt offset for standard time during this rule, in milliseconds */ int32_t gmtofs; /* daylight savings time offset from standard time, in milliseconds */ int32_t save; /* the month the rule takes effect (1=January) */ char mm; /* * When in the month the rule takes effect: * *. 0 -> takes effect on the fixed day of the month <dd> *. 1 -> last <weekday> of the month *. 2 -> first <weekday> on or after <dd> *. 3 -> first <weekday> on or before <dd> */ char when; /* * The day of the month the rule takes effect (1-366). (This can be * greater than 31, because it's also used to encode POSIX TZ-style * rules with "Julian day of year counting Feb 29". Our calendar * calculator can handle overflows in the day of month, carrying them * automatically into subsequent months, so we can encode the Nth day * of the year as simply January Nth, and we'll get the right result * even if N > 31. */ short dd; /* the day of the week the rule takes effect (1=Sunday) */ char weekday; /* * Time zone for the effective time/date: 0 -> local wall clock time * (with the zone settings for the period immediately before the rule * takes effect); 0x40 -> local standard time (for the period * immediately before the rule takes effect), 0x80 -> UTC */ uchar at_zone; /* time of day the rule takes effect, as milliseconds after midnight */ int32_t at; /* resolve this rule in the given year */ void resolve(int32_t &dayno, int32_t &daytime, int year, const vmtz_rule *prev_rule); /* set from an OS DST start/end date descriptor */ void set(const os_tzrule_t *desc, const char *abbr, int32_t gmtofs, int32_t save); }; /* ------------------------------------------------------------------------ */ /* * Time zone info query structure */ struct vmtzquery { /* starting date/time of the transition or rule that applies */ int32_t start_dayno; int32_t start_daytime; /* GMT offset for standard time, in milliseconds */ int32_t gmtofs; /* daylight savings offset from standard time, in milliseconds */ int32_t save; /* zone name abbreviation ("EST", etc) */ const char *abbr; /* set from a transition item */ void set(const vmtz_trans *trans) { set(trans->ttype, trans->dayno, trans->daytime); } /* set from a time type */ void set(const vmtz_ttype *tty, int32_t dayno, int32_t daytime) { start_dayno = dayno; start_daytime = daytime; gmtofs = tty->gmtofs; save = tty->save; abbr = tty->fmt; } /* set from a rule */ void set(const vmtz_rule *rule, int32_t dayno, int32_t daytime) { start_dayno = dayno; start_daytime = daytime; gmtofs = rule->gmtofs; save = rule->save; abbr = rule->fmt; } }; /* ------------------------------------------------------------------------ */ /* * Time zone object. */ class CVmTimeZone { public: /* create from a timezone descriptor */ CVmTimeZone(const os_tzinfo_t *desc); /* destruction */ ~CVmTimeZone(); /* load from the zoneinfo database file */ static CVmTimeZone *load(VMG_ class ZoneHashEntry *entry); /* get the primary name of the zone */ const char *get_name(size_t &len) const; /* create a List object with this zone's names and aliases */ vm_obj_id_t get_name_list(VMG0_) const; /* get the single history item that applies to the given date */ vm_obj_id_t get_history_item(VMG_ int32_t dayno, int32_t daytime) const; /* create a List object populated with the transition list */ vm_obj_id_t get_history_list(VMG0_) const; /* create a List object populated with the ongoing rules */ vm_obj_id_t get_rule_list(VMG0_) const; /* country code, if available (ISO3166 two-letter country code) */ const char *country() const { return country_; } /* coordinates, if available (+DDMM+DDDMM or +DDMMSS+DDDMMSS format) */ const char *coords() const { return coords_; } /* zone.tab description/comment */ const char *desc() const { return desc_; } /* * Convert a local wall clock time in this zone to UTC. 'daytime' is * in milliseconds after midnight. */ void local_to_utc(int32_t &dayno, int32_t &daytime); /* * Convert a UTC time to local time in this zone. Returns the format * abbreviation for the converted time (e.g., PDT for Pacific Daylight * Time). 'daytime' is in milliseconds after midnight. Fills in * 'tzofs' with the time zone's offset from UTC, in milliseconds. */ const char *utc_to_local(int32_t &dayno, int32_t &daytime, int32_t &tzofs); /* * Query the time zone information at the given time/right now. The * time is in milliseconds after midnight. */ void query(vmtzquery *result, int32_t dayno, int32_t daytime, int local) const; void query(vmtzquery *result) const; protected: /* create from binary data loaded from the file */ CVmTimeZone(class ZoneHashEntryDb *entry, const char *file_data, unsigned int trans_cnt, unsigned int type_cnt, unsigned int rule_cnt, unsigned int abbr_bytes); /* create a synthetic zone */ CVmTimeZone(class ZoneHashEntrySynth *entry); /* initialize from an OS timezone descriptor */ void init(const os_tzinfo_t *desc); /* my hash table entry */ class ZoneHashEntry *hashentry_; /* my name; used only if we don't have a hash entry */ char *name_; /* transition list */ int trans_cnt_; vmtz_trans *trans_; /* time type list */ int ttype_cnt_; vmtz_ttype *ttype_; /* rule list */ int rule_cnt_; vmtz_rule *rule_; /* abbreviations */ char *abbr_; /* country code */ char country_[3]; /* coordinates */ char coords_[16]; /* comments/description from zone.tab */ char *desc_; }; #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmtzobj.cpp�����������������������������������������������������������������������0000664�0000000�0000000�00000035343�12650170723�0016065�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2012 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtzobj.cpp - CVmObjTimeZone object Function Notes Modified 02/06/12 MJRoberts - Creation */ #include <stdlib.h> #include <string.h> #include <assert.h> #include "t3std.h" #include "vmtzobj.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmbif.h" #include "vmmeta.h" #include "vmstack.h" #include "vmrun.h" #include "vmstr.h" #include "vmlst.h" #include "vmtz.h" #include "vmdate.h" #include "utf8.h" /* ------------------------------------------------------------------------ */ /* * Allocate an extension structure */ vm_tzobj_ext *vm_tzobj_ext::alloc_ext(VMG_ CVmObjTimeZone *self, CVmTimeZone *tz) { /* calculate how much space we need */ size_t siz = sizeof(vm_tzobj_ext); /* allocate the memory */ vm_tzobj_ext *ext = (vm_tzobj_ext *)G_mem->get_var_heap()->alloc_mem( siz, self); /* save our TimeZone object */ ext->tz = tz; /* return the new extension */ return ext; } /* ------------------------------------------------------------------------ */ /* * CVmObjTimeZone object statics */ /* metaclass registration object */ static CVmMetaclassTimeZone metaclass_reg_obj; CVmMetaclass *CVmObjTimeZone::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjTimeZone::*CVmObjTimeZone::func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjTimeZone::getp_undef, /* 0 */ &CVmObjTimeZone::getp_getNames, /* 1 */ &CVmObjTimeZone::getp_getHistory, /* 2 */ &CVmObjTimeZone::getp_getRules, /* 3 */ &CVmObjTimeZone::getp_getLocation /* 4 */ }; /* ------------------------------------------------------------------------ */ /* * construction */ CVmObjTimeZone::CVmObjTimeZone(VMG_ CVmTimeZone *tz) { /* allocate our extension structure */ ext_ = (char *)vm_tzobj_ext::alloc_ext(vmg_ this, tz); } /* ------------------------------------------------------------------------ */ /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjTimeZone::create_from_stack( VMG_ const uchar **pc_ptr, uint argc) { /* parse the constructor arguments */ CVmTimeZone *tz = parse_ctor_args(vmg_ G_stk->get(0), argc); /* allocate the object ID */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, FALSE, FALSE); /* create the object */ new (vmg_ id) CVmObjTimeZone(vmg_ tz); /* return the new ID */ return id; } /* * Parse stack arguments for a timezone constructor, returning a * CVmTimeZone object if the arguments specify a valid timezone. If not, * throws an error - we never return null. This doesn't create the garbage * collected TimeZone object; it just parses the arguments and creates the * underlying cache object. */ CVmTimeZone *CVmObjTimeZone::parse_ctor_args( VMG_ const vm_val_t *argp, int argc) { /* check arguments */ const char *str; CVmTimeZone *tz = 0; if (argc <= 0) { /* no arguments; create a local timezone object */ tz = G_tzcache->get_local_zone(vmg0_); } else if (argc == 1 && (str = argp->get_as_string(vmg0_)) != 0) { /* get the string buffer and length */ size_t len = vmb_get_len(str); str += VMB_LEN; /* parse the zone name */ tz = G_tzcache->parse_zone(vmg_ str, len); } else if (argc == 1 && argp->is_numeric(vmg0_)) { /* construct from a gmt offset */ tz = G_tzcache->get_gmtofs_zone(vmg_ argp->num_to_int(vmg0_)); } else if (argc > 1) { /* wrong number of arguments */ err_throw(VMERR_WRONG_NUM_OF_ARGS); } else { /* wrong type */ err_throw(VMERR_BAD_TYPE_BIF); } /* if we didn't find a zone, it's an error */ if (tz == 0) err_throw(VMERR_BAD_VAL_BIF); /* return the zone */ return tz; } /* ------------------------------------------------------------------------ */ /* * Create from a given CVmTimeZone object. */ vm_obj_id_t CVmObjTimeZone::create(VMG_ CVmTimeZone *tz) { /* allocate the object ID and create the object */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, FALSE, FALSE); new (vmg_ id) CVmObjTimeZone(vmg_ tz); /* return the new ID */ return id; } /* ------------------------------------------------------------------------ */ /* * notify of deletion */ void CVmObjTimeZone::notify_delete(VMG_ int /*in_root_set*/) { /* free our additional data, if we have any */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjTimeZone::set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) { /* no settable properties - throw an error */ err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * get a property */ int CVmObjTimeZone::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from our base class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * load from an image file */ void CVmObjTimeZone::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ self, ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from the image file */ void CVmObjTimeZone::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ self, ptr, siz); } /* * load or reload data from the image */ void CVmObjTimeZone::load_image_data( VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* look up our timezone object */ CVmTimeZone *tz = G_tzcache->parse_zone(vmg_ ptr+9, osrp1(ptr+8)); /* if we didn't find it, create a dummy entry for it */ if (tz == 0) { /* * Read the default GMT offset and daylight savings offset. Note * that the saved values are in milliseconds, and os_tzinfo_t uses * seconds, so adjust accordingly. */ os_tzinfo_t desc; memset(&desc, 0, sizeof(desc)); desc.std_ofs = osrp4s(ptr) / 1000; desc.dst_ofs = desc.std_ofs + osrp4s(ptr+4) / 1000; /* read the default abbreviation */ const char *abbr = ptr + 8 + osrp1(ptr+8) + 1; lib_strcpy(desc.std_abbr, sizeof(desc.std_abbr), abbr+1, osrp1(abbr)); lib_strcpy(desc.dst_abbr, sizeof(desc.dst_abbr), abbr+1, osrp1(abbr)); /* create the dummy */ tz = G_tzcache->create_missing_zone(vmg_ ptr+9, osrp1(ptr+8), &desc); } /* allocate the extension */ vm_tzobj_ext *ext = vm_tzobj_ext::alloc_ext(vmg_ this, tz); ext_ = (char *)ext; } /* ------------------------------------------------------------------------ */ /* * save to a file */ void CVmObjTimeZone::save_to_file(VMG_ class CVmFile *fp) { /* get our timezone name */ CVmTimeZone *tz = get_ext()->tz; size_t namelen; const char *name = tz->get_name(namelen); /* if this is the system's local zone, use ":local" as the name */ if (G_tzcache->is_local_zone(tz)) name = ":local", namelen = 6; /* query the zone data */ vmtzquery q; tz->query(&q); /* write our GMT offset, DST offset, abbreviation, and timezone name */ fp->write_int4(q.gmtofs); fp->write_int4(q.save); fp->write_str_byte_prefix(q.abbr); fp->write_str_byte_prefix(name, namelen); } /* ------------------------------------------------------------------------ */ /* * restore from a file */ void CVmObjTimeZone::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* * Read the GMT and DST offsets (in case we need a synthetic entry). * Note that the saved values are in milliseconds, but os_tzinfo_t * works in seconds, so adjust accordingly. */ os_tzinfo_t desc; memset(&desc, 0, sizeof(desc)); desc.std_ofs = fp->read_int4() / 1000; desc.dst_ofs = desc.std_ofs + fp->read_int4() / 1000; /* read the abbreviation */ fp->read_str_byte_prefix(desc.std_abbr, sizeof(desc.std_abbr)); strcpy(desc.dst_abbr, desc.std_abbr); /* read the length and name */ char name[256]; size_t len = fp->read_str_byte_prefix(name, sizeof(name)); /* look up our timezone object */ CVmTimeZone *tz = G_tzcache->parse_zone(vmg_ name, len); /* if we didn't find it, create a dummy entry for it */ if (tz == 0) tz = G_tzcache->create_missing_zone(vmg_ name, len, &desc); /* allocate the extension structure */ vm_tzobj_ext *ext = vm_tzobj_ext::alloc_ext(vmg_ this, tz); ext_ = (char *)ext; } /* ------------------------------------------------------------------------ */ /* * cast to a string - returns the time zone name */ const char *CVmObjTimeZone::cast_to_string( VMG_ vm_obj_id_t self, vm_val_t *newstr) const { /* get the time zone name */ size_t len; const char *name = get_tz()->get_name(len); /* create the return string */ newstr->set_obj(CVmObjString::create(vmg_ FALSE, name, len)); return newstr->get_as_string(vmg0_); } /* ------------------------------------------------------------------------ */ /* * getNames method */ int CVmObjTimeZone::getp_getNames(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* create the name list */ retval->set_obj(get_tz()->get_name_list(vmg0_)); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getHistory method */ int CVmObjTimeZone::getp_getHistory(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* check for a Date argument */ if (argc >= 1 && G_stk->get(0)->typ != VM_NIL) { /* there's a date - retrieve it */ CVmObjDate *date = vm_val_cast(CVmObjDate, G_stk->get(0)); if (date == 0) err_throw(VMERR_BAD_TYPE_BIF); /* get the history for the given date only */ retval->set_obj(get_tz()->get_history_item( vmg_ date->get_dayno(), date->get_daytime())); } else { /* no date argument - get the full history */ retval->set_obj(get_tz()->get_history_list(vmg0_)); } /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getRules method */ int CVmObjTimeZone::getp_getRules(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the rule list */ retval->set_obj(get_tz()->get_rule_list(vmg0_)); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getLocation method */ int CVmObjTimeZone::getp_getLocation(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get our underlying CVmTimeZone object */ CVmTimeZone *tz = get_ext()->tz; /* create the return list - [country, lat, long, desc] */ retval->set_obj(CVmObjList::create(vmg_ FALSE, 4)); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); G_stk->push(retval); lst->cons_clear(); /* set the country string */ vm_val_t ele; const char *str = tz->country(); ele.set_obj(CVmObjString::create(vmg_ FALSE, str, strlen(str))); lst->cons_set_element(0, &ele); /* get the lat/long string */ str = tz->coords(); /* find the +/- that separates the lat/long portions */ const char *p = str; if (*p == '+' || *p == '-') ++p; for ( ; *p != '\0' && *p != '-' && *p != '+' ; ++p) ; /* set the two substrings */ ele.set_obj(CVmObjString::create(vmg_ FALSE, str, p - str)); lst->cons_set_element(1, &ele); ele.set_obj(CVmObjString::create(vmg_ FALSE, p, strlen(p))); lst->cons_set_element(2, &ele); /* set the description string */ str = tz->desc(); if (str != 0 && str[0] != '\0') ele.set_obj(CVmObjString::create(vmg_ FALSE, str, strlen(str))); else ele.set_nil(); lst->cons_set_element(3, &ele); /* discard arguments and gc protection */ G_stk->discard(argc + 1); /* handled */ return TRUE; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmtzobj.h�������������������������������������������������������������������������0000664�0000000�0000000�00000016600�12650170723�0015525�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2012 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtzobj.h - CVmObjTimeZone object Function Notes Modified 02/06/12 MJRoberts - Creation */ #ifndef VMTZOBJ_H #define VMTZOBJ_H #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" /* ------------------------------------------------------------------------ */ /* * The image file data simply contains the name of the time zone, as an * ASCII string, with a one-byte length prefix. The block is arranged as * follows: * *. UBYTE name_length *. ASCII name */ /* ------------------------------------------------------------------------ */ /* * Our in-memory extension data structure. */ struct vm_tzobj_ext { /* allocate the structure */ static vm_tzobj_ext *alloc_ext(VMG_ class CVmObjTimeZone *self, class CVmTimeZone *tz); /* our CVmTimeZone object */ class CVmTimeZone *tz; }; /* ------------------------------------------------------------------------ */ /* * CVmObjTimeZone intrinsic class definition */ class CVmObjTimeZone: public CVmObject { friend class CVmMetaclassTimeZone; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* get my CVmTimeZone object */ class CVmTimeZone *get_tz() const { return get_ext()->tz; } /* is this a CVmObjTimeZone object? */ static int is_CVmObjTimeZone_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* * Parse TimeZone constructor arguments, returning a CVmTimeZone cache * object if the arguments match, throwing an error if not. Doesn't * actually create a TimeZone object; simply finds the underlying cache * object. */ static class CVmTimeZone *parse_ctor_args( VMG_ const vm_val_t *argp, int argc); /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* create from a given CVmTimeZone object */ static vm_obj_id_t create(VMG_ class CVmTimeZone *tz); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * receive savepoint notification - we don't keep any * savepoint-relative records, so we don't need to do anything here */ void notify_new_savept() { } /* we're immutable, so there's no undo to mess with */ void apply_undo(VMG_ struct CVmUndoRecord *) { } void discard_undo(VMG_ struct CVmUndoRecord *) { } void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* we have no outgoing references */ void mark_refs(VMG_ uint) { } void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* time zone objects are immutable */ int is_changed_since_load() const { return FALSE; } /* cast to a string - returns the time zone name */ const char *cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *newstr) const; protected: /* get my extension data */ vm_tzobj_ext *get_ext() const { return (vm_tzobj_ext *)ext_; } /* load or reload image data */ void load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* create a with no initial contents */ CVmObjTimeZone() { ext_ = 0; } /* create with contents */ CVmObjTimeZone(VMG_ class CVmTimeZone *tz); /* * parse a zone name by GMT offset, specified as a string in +-hhmm or * +-hh[:mm[:ss]] format, with the given prefix */ static class CVmTimeZone *parse_zone_hhmmss( VMG_ const char *prefix, const char *name, size_t len); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* getNames method */ int getp_getNames(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getHistory method */ int getp_getHistory(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getRules method */ int getp_getRules(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getLocation method */ int getp_getLocation(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluation function table */ static int (CVmObjTimeZone::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * CVmObjTimeZone metaclass registration table object */ class CVmMetaclassTimeZone: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "timezone/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjTimeZone(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjTimeZone(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjTimeZone::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjTimeZone:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMTZOBJ_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjTimeZone) ��������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmundo.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000037173�12650170723�0015705�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMUNDO.CPP,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmundo.cpp - undo manager Function Notes Modified 10/29/98 MJRoberts - Creation */ #include "t3std.h" #include "vmtype.h" #include "vmobj.h" #include "vmundo.h" /* * create the undo manager */ CVmUndo::CVmUndo(size_t undo_record_cnt, uint max_savepts) { /* remember the maximum number of savepoints */ max_savepts_ = (vm_savept_t)max_savepts; /* * initialize the savepoint to number 0 (this is arbitrary, but we * might as well start here) */ cur_savept_ = 0; /* no savepoints have been created yet */ savept_cnt_ = 0; /* create the undo record array */ rec_arr_size_ = undo_record_cnt; rec_arr_ = (CVmUndoMeta *)t3malloc(rec_arr_size_ * sizeof(rec_arr_[0])); /* we have no records at all yet, so we have no "firsts" */ cur_first_ = 0; oldest_first_ = 0; /* start allocating from the first entry in the array */ next_free_ = rec_arr_; } /* * delete the undo manager */ CVmUndo::~CVmUndo() { /* delete the array of undo records */ t3free(rec_arr_); } /* * create a savepoint */ void CVmUndo::create_savept(VMG0_) { CVmUndoMeta *rec; /* * Allocate an undo record to serve as the link pointer for this * savepoint. If this fails, we cannot create a savepoint; we won't * need to do anything else in this case, since we will have deleted * all existing savepoints if we ran out of records. */ rec = alloc_rec(vmg0_); if (rec == 0) return; /* if we have an old savepoint, the new record starts the next one */ if (cur_first_ != 0) cur_first_->link.next_first = rec; /* the old savepoint (if any) is our previous savepoint */ rec->link.prev_first = cur_first_; /* there's nothing after us yet */ rec->link.next_first = 0; /* this record is now the current savepoint's first record */ cur_first_ = rec; /* * if we didn't have any savepoints recorded yet, this is now the * first record in the oldest savepoint */ if (oldest_first_ == 0) oldest_first_ = rec; /* * increment the savepoint number, wrapping to zero if we reach the * highest value we can store */ if (cur_savept_ == VM_SAVEPT_MAX) cur_savept_ = 0; else ++cur_savept_; /* * if we're already storing the maximum number of savepoints we're * allowed to store simultaneously, drop the oldest savepoint; * otherwise, simply increment the savepoint counter */ if (savept_cnt_ == max_savepts_) { /* we have the maximum number of savepoints; discard the oldest */ drop_oldest_savept(vmg0_); } /* count the new savepoint */ ++savept_cnt_; /* notify the objects that we're starting a new savepoint */ G_obj_table->notify_new_savept(); } /* * Discard the oldest savepoint */ void CVmUndo::drop_oldest_savept(VMG0_) { /* if there are no savepoints, there's nothing to do */ if (savept_cnt_ == 0) return; /* decrement the savepoint count, now that this one is gone */ --savept_cnt_; /* forget the oldest lead pointer */ if (oldest_first_ != 0) { /* * discard records from the oldest lead pointer to the next * oldest lead pointer */ CVmUndoMeta *meta; for (meta = oldest_first_, inc_rec_ptr(&meta) ; meta != oldest_first_->link.next_first && meta != next_free_ ; ) { /* discard this entry, if it still exists */ if (meta->rec.obj != VM_INVALID_OBJ) vm_objp(vmg_ meta->rec.obj)->discard_undo(vmg_ &meta->rec); /* advance to the next record */ inc_rec_ptr(&meta); } /* advance the oldest pointer to the next savepoint's first record */ oldest_first_ = oldest_first_->link.next_first; /* there's now nothing before this one */ if (oldest_first_ != 0) oldest_first_->link.prev_first = 0; } /* if we don't have an oldest, we also don't have a current */ if (oldest_first_ == 0) cur_first_ = 0; } /* * Drop all undo information */ void CVmUndo::drop_undo(VMG0_) { /* delete each savepoint until we have none left */ while (savept_cnt_ != 0) drop_oldest_savept(vmg0_); /* reset the savepoint number */ cur_savept_ = 0; /* we have no "firsts" */ cur_first_ = 0; oldest_first_ = 0; /* start allocating from the start of the array */ next_free_ = rec_arr_; } /* * Allocate an undo record. If we run out of free records, delete * savepoints, starting with the oldest savepoint, until we have a free * record. */ CVmUndoMeta *CVmUndo::alloc_rec(VMG0_) { /* * keep trying until we find an entry or run out of savepoints to * delete */ for (;;) { /* * If we have no savepoints at all, all records are free. * Otherwise, if the next free pointer hasn't bumped up against * the oldest allocated record yet, we can allocate another * record. */ if (savept_cnt_ == 0 || next_free_ != oldest_first_) { CVmUndoMeta *ret; /* remember the current free record */ ret = next_free_; /* * advance to the next record, wrapping if we reach the end * of the array (since we treat the array as circular) */ inc_rec_ptr(&next_free_); /* return the record */ return ret; } /* * If we have at least one old savepoint, discard the oldest * savepoint, which may free up some records, and try again; * otherwise, give up. */ if (savept_cnt_ > 1) { /* * we have at least one old savepoint we can discard - get * rid of it and try again */ drop_oldest_savept(vmg0_); } else { /* * we are down to our last savepoint (or none at all) - it's * no longer valid (since we can't keep it complete due to * the lack of available records), so delete it and return * failure */ drop_oldest_savept(vmg0_); return 0; } } } /* * Add a new record with a property ID key */ void CVmUndo::add_new_record_prop_key(VMG_ vm_obj_id_t obj, vm_prop_id_t key, const vm_val_t *val) { CVmUndoRecord *rec; /* allocate the new record */ rec = add_new_record(vmg_ obj); /* if we successfully allocated a record, set it up */ if (rec != 0) { /* set the object */ rec->obj = obj; /* set the key */ rec->id.prop = key; /* set the value */ rec->oldval = *val; } } /* * Add a new record with an integer key */ void CVmUndo::add_new_record_int_key(VMG_ vm_obj_id_t obj, uint32_t key, const vm_val_t *val) { CVmUndoRecord *rec; /* allocate the new record */ rec = add_new_record(vmg_ obj); /* if we successfully allocated a record, set it up */ if (rec != 0) { /* set the object */ rec->obj = obj; /* set the key */ rec->id.intval = key; /* set the value */ rec->oldval = *val; } } /* * Add a new record with a pointer key */ int CVmUndo::add_new_record_ptr_key(VMG_ vm_obj_id_t obj, void *key, const vm_val_t *val) { CVmUndoRecord *rec; /* allocate the new record */ rec = add_new_record(vmg_ obj); /* if we successfully allocated a record, set it up */ if (rec != 0) { /* set the object */ rec->obj = obj; /* set the key */ rec->id.ptrval = key; /* set the value */ rec->oldval = *val; } /* return an indication as to whether the record was created */ return (rec != 0); } /* * Add a new undo record */ CVmUndoRecord *CVmUndo::add_new_record(VMG_ vm_obj_id_t controlling_obj) { CVmUndoMeta *meta; /* if there is not an active savepoint, do not keep undo */ if (savept_cnt_ == 0) return 0; /* * if the object was created after the current savepoint was created, * there is no need to keep undo for it during this savepoint, since * rolling back to the savepoint will roll back to a point at which * the object didn't even exist */ if (!G_obj_table->is_obj_in_undo(controlling_obj)) return 0; /* allocate a new record */ meta = alloc_rec(vmg0_); /* return the undo record, if we succeeded */ return (meta == 0 ? 0 : &meta->rec); } /* * Apply undo to the most recent savepoint. */ void CVmUndo::undo_to_savept(VMG0_) { CVmUndoMeta *meta; /* if we don't have any savepoints, there's nothing to do */ if (savept_cnt_ == 0) return; /* * Starting with the most recently-added record, apply each record * in sequence until we reach the first savepoint in the undo list. */ meta = next_free_; for (;;) { /* * move to the previous record, wrapping to the end of the array * at the first record (since we treat the array as circular) */ if (meta == rec_arr_) meta = rec_arr_ + rec_arr_size_; --meta; /* * if we're at the first record in the current savepoint, we're * done */ if (meta == cur_first_) break; /* apply this undo record */ G_obj_table->apply_undo(vmg_ &meta->rec); } /* * Unwind the undo stack -- get the first record in the previous * savepoint from the link pointer. */ cur_first_ = cur_first_->link.prev_first; /* * there's nothing following the current savepoint any more -- the * savepoint we just applied is gone now */ if (cur_first_ != 0) cur_first_->link.next_first = 0; else oldest_first_ = 0; /* that's one less savepoint */ --savept_cnt_; /* make the previous savepoint current */ --cur_savept_; if (cur_savept_ == 0) cur_savept_ = VM_SAVEPT_MAX; /* the savepoint link we just removed is now the next free record */ next_free_ = meta; /* * notify objects that a new savepoint is in effect - the savepoint * isn't new in the sense of being newly created, but in the sense of * being a different savepoint than was previously in effect */ G_obj_table->notify_new_savept(); } /* * Garbage collection: mark referenced objects. This goes through all * of the undo records; for each undo record, we have the object that * owns the undo record mark as referenced any object to which the * record refers. */ void CVmUndo::gc_mark_refs(VMG0_) { CVmUndoMeta *cur; CVmUndoMeta *next_link; /* if we don't have any records, there's nothing to do */ if (oldest_first_ == 0) return; /* the first record is a linking record */ next_link = oldest_first_; /* start at the first record */ cur = oldest_first_; /* * Go through all undo records, from the oldest to the newest. Note * that we must keep track of each "link" record, because these are * not ordinary undo records and must be handled differently. */ for (;;) { /* check to see if this is a linking record or an ordinary record */ if (cur == next_link) { /* * this is a linking record - simply note the next linking * record and otherwise ignore this record */ next_link = cur->link.next_first; } else if (cur->rec.obj != VM_INVALID_OBJ) { /* * It's an ordinary undo record -- mark its references. * Note that the fact that an object has undo records does * not mean that the object is reachable -- this is * effectively a weak link to the object, because if it's * not reachable through other means, there's no need to * keep any undo information for it; so, we don't mark the * owner object itself as reachable at this point. */ G_obj_table->mark_obj_undo_rec(vmg_ cur->rec.obj, &cur->rec); } /* advance to the next record, wrapping at the end of the array */ ++cur; if (cur == rec_arr_ + rec_arr_size_) cur = rec_arr_; /* stop if we've reached the last record */ if (cur == next_free_) break; } } /* * Garbage collection: delete obsolete weak references. This goes * through all of the undo records; for each undo record, if the object * that owns the record is unreachable, we'll delete the undo record; * otherwise, we'll tell the object that owns the record to clean up the * record if it contains an unreachable weak reference. */ void CVmUndo::gc_remove_stale_weak_refs(VMG0_) { CVmUndoMeta *cur; CVmUndoMeta *next_link; /* if we don't have any records, there's nothing to do */ if (oldest_first_ == 0) return; /* the first record is a linking record */ next_link = oldest_first_; /* start at the first record */ cur = oldest_first_; /* * Go through all undo records, from the oldest to the newest. Note * that we must keep track of each "link" record, because these are * not ordinary undo records and must be handled differently. */ for (;;) { /* check to see if this is a linking record or an ordinary record */ if (cur == next_link) { /* * this is a linking record - simply note the next linking * record and otherwise ignore this record */ next_link = cur->link.next_first; } else if (cur->rec.obj != VM_INVALID_OBJ) { /* * It's an ordinary undo record -- delete its stale * references. If the record owner is itself about to be * deleted, don't bother with the object, but rather delete * the entire undo record -- undo records themselves only * weakly reference their owning objects. */ if (!G_obj_table->is_obj_deletable(cur->rec.obj)) { /* it's not ready for deletion - clean up its weak refs */ G_obj_table->remove_obj_stale_undo_weak_ref( vmg_ cur->rec.obj, &cur->rec); } else { /* * it's no longer reachable - delete the undo record by * setting the owning object to 'invalid' */ cur->rec.obj = VM_INVALID_OBJ; } } /* advance to the next record, wrapping at the end of the array */ ++cur; if (cur == rec_arr_ + rec_arr_size_) cur = rec_arr_; /* stop if we've reached the last record */ if (cur == next_free_) break; } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmundo.h��������������������������������������������������������������������������0000664�0000000�0000000�00000020730�12650170723�0015341�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMUNDO.H,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmundo.h - VM undo manager Function Notes Modified 10/29/98 MJRoberts - Creation */ #ifndef VMUNDO_H #define VMUNDO_H #include "t3std.h" #include "vmtype.h" /* ------------------------------------------------------------------------ */ /* * Undo record. The VM undo manager maintains a list of these records; * each record contains the information necessary to undo one particular * data change in an object. Each object class (TADS object, array, * etc) keeps a linked list of undo records for its own actions. */ struct CVmUndoRecord { /* * Object ID. All undoable state is stored in objects, hence each * undo record is associated with an object. */ vm_obj_id_t obj; /* * Identifier - the meaning of this member is defined by the object * that created the record. For TADS objects, this is a property ID * specifying the property that was changed; for arrays, it's the * index of the array element that changed. Other object * metaclasses may assign different meanings. */ union { /* property value key */ vm_prop_id_t prop; /* integer value key */ uint32_t intval; /* pointer value key */ void *ptrval; } id; /* * Data value. This is the data value prior to the change. In some * cases, the type may be 'empty' to indicate that the original * value did not exist; for example, when a property is added to a * TADS object and the property was not present previously, we * create an undo record with type 'empty' to indicate that the * property must be deleted on undo. */ vm_val_t oldval; }; /* * Undo meta-record. Each slot in the undo array is one of these * meta-records, which is a union of the normal undo record and an undo * link pointer. The first undo record in any savepoint is always a * link pointer; all of the other records are normal undo records. */ union CVmUndoMeta { /* the first entry in each savepoint is a link entry */ struct { /* pointer to the first record in the previous savepoint */ CVmUndoMeta *prev_first; /* pointer to the first record in the next savepoint */ CVmUndoMeta *next_first; } link; /* every entry but the first in a savepoint is an ordinary undo record */ CVmUndoRecord rec; }; /* ------------------------------------------------------------------------ */ /* * Undo manager */ class CVmUndo { public: /* * create the undo manager, specifying the upper limit for memory * usage and retained savepoints */ CVmUndo(size_t undo_record_cnt, uint max_savepts); /* delete the undo manager */ ~CVmUndo(); /* * Create a savepoint. If doing so would exceed the maximum number * of savepoints, we'll discard the oldest savepoint. */ void create_savept(VMG0_); /* get the current savepoint ID */ vm_savept_t get_cur_savept() const { return cur_savept_; } /* determine how many savepoints exist */ uint get_savept_cnt() const { return savept_cnt_; } /* drop all undo information */ void drop_undo(VMG0_); /* * Allocate and initialize an undo record with a property key or * with an integer key. * * We check that the undo manager has an active savepoint. It may * not have an active savepoint if no savepoint has been created, * all undo records have been applied, or we've run out of space in * the current savepoint; in any of these cases, since there's no * savepoint, we don't need to keep any undo records. */ void add_new_record_prop_key(VMG_ vm_obj_id_t obj, vm_prop_id_t key, const vm_val_t *val); void add_new_record_int_key(VMG_ vm_obj_id_t obj, uint32_t key, const vm_val_t *val); /* * Add a new undo record with a pointer key; returns true if the * record was created, false if not. (This version of * add_new_record_xxx_key returns an indication of success because * the pointer value might have been allocated, in which case the * caller must deallocate the value if we didn't add an undo * record.) */ int add_new_record_ptr_key(VMG_ vm_obj_id_t obj, void *key, const vm_val_t *val); /* add a new record with a pointer key and no separate value data */ int add_new_record_ptr_key(VMG_ vm_obj_id_t obj, void *key) { /* set up a nil value to fill the value slot in the record */ vm_val_t nilval; nilval.set_nil(); /* save the record */ return add_new_record_ptr_key(vmg_ obj, key, &nilval); } /* * Apply undo to the latest savepoint. After applying the undo * records, we delete all undo records to the savepoint; this leaves * the previous savepoint as the new most recent savepoint, so * repeating this will undo to the previous savepoint. */ void undo_to_savept(VMG0_); /* * Garbage collection: mark referenced objects. This goes through * all of the undo records; for each undo record, we have the object * that owns the undo record mark as referenced any object to which * the record refers. */ void gc_mark_refs(VMG0_); /* * Garbage collection: delete obsolete weak references. This goes * through all of the undo records; for each undo record, if the * object that owns the record is unreachable, we'll delete the undo * record; otherwise, we'll tell the object that owns the record to * clean up the record if it contains an unreachable weak reference. */ void gc_remove_stale_weak_refs(VMG0_); private: /* * Allocate an undo record. Returns null if no records are * available, in which case the caller should simply skip saving * undo. */ CVmUndoMeta *alloc_rec(VMG0_); /* * increment a record pointer, wrapping back at the end of the array * to the first record */ void inc_rec_ptr(CVmUndoMeta **rec) { /* increment the record pointer */ ++(*rec); /* if it's at the end of the array, wrap back to the start */ if (*rec == rec_arr_ + rec_arr_size_) *rec = rec_arr_; } /* * Add a new record and return the new record. If we don't have an * active savepoint, this will return null, since there's no need to * keep undo. */ CVmUndoRecord *add_new_record(VMG_ vm_obj_id_t controlling_obj); /* discard the oldest savepoint */ void drop_oldest_savept(VMG0_); /* current savepoint ID */ vm_savept_t cur_savept_; /* * Number of active savepoint ID's. If this is zero, it means that * we have no undo information and we're not keeping any undo * information. If this is one, it means that we only have the * current savepoint stored. */ uint savept_cnt_; /* * The nominal maximum number of savepoints to keep. Any time that * we attempt to create a new savepoint, and doing so would exceed * this number of stored savepoints, we will discard the oldest * savepoint. Note that we may not always store this many * savepoints, since we will also discard old savepoints when we run * out of available undo records attempting to create a new * savepoint. */ vm_savept_t max_savepts_; /* * Pointer to the first record in the current savepoint. This isn't * a real undo record -- it's a link record. */ CVmUndoMeta *cur_first_; /* * Pointer to the first undo record in the oldest savepoint. This * is a link record. */ CVmUndoMeta *oldest_first_; /* pointer to the next free undo record */ CVmUndoMeta *next_free_; /* * Master array of undo records, and the number of records in this * list. We pre-allocate a fixed array of these records. If we run * out, we start discarding old undo. */ CVmUndoMeta *rec_arr_; size_t rec_arr_size_; }; #endif /* VMUNDO_H */ ����������������������������������������qtads-2.1.7/tads3/vmuni.h���������������������������������������������������������������������������0000664�0000000�0000000�00000014113�12650170723�0015165�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmuni.h - T3 VM Unicode-specific functions Function Notes Modified 08/27/99 MJRoberts - Creation */ #ifndef VMUNI_H #define VMUNI_H #include <stdlib.h> #include "wchar.h" #include "t3std.h" /* ------------------------------------------------------------------------ */ /* * Upper/lower case classification and conversion functions for Unicode * character values. These routines are implemented in a * machine-generated source file (the source file is mechanically * derived from the Unicode character database). */ int t3_is_lower(wchar_t ch); int t3_is_upper(wchar_t ch); int t3_is_title(wchar_t ch); const wchar_t *t3_to_lower(wchar_t ch); const wchar_t *t3_to_upper(wchar_t ch); const wchar_t *t3_to_title(wchar_t ch); const wchar_t *t3_to_fold(wchar_t ch); wchar_t t3_simple_case_fold(wchar_t ch); /* * Character types. Types are mutually exclusive, so a character has * exactly one type. */ #define T3_CTYPE_UNDEF 0 /* character isn't defined */ #define T3_CTYPE_ALPHA 1 /* alphabetic, with no case information */ #define T3_CTYPE_UPPER 2 /* upper-case alphabetic */ #define T3_CTYPE_TITLE 3 /* title-case alphabetic */ #define T3_CTYPE_LOWER 4 /* lower-case alphabetic */ #define T3_CTYPE_DIGIT 5 /* digit */ #define T3_CTYPE_SPACE 6 /* horizontal whitespace */ #define T3_CTYPE_VSPAC 7 /* vertical whitespace */ #define T3_CTYPE_PUNCT 8 /* punctuation */ #define T3_CTYPE_OTHER 9 /* character doesn't fit any other category */ /* macro name strings, in order of appearance */ #define T3_CTYPE_NAMES \ "T3_CTYPE_UNDEF", "T3_CTYPE_ALPHA", "T3_CTYPE_UPPER", "T3_CTYPE_TITLE", \ "T3_CTYPE_LOWER", "T3_CTYPE_DIGIT", "T3_CTYPE_SPACE", "T3_CTYPE_VSPAC", \ "T3_CTYPE_PUNCT", "T3_CTYPE_OTHER" /* get the character type */ int t3_get_chartype(wchar_t ch); /* * character classification functions */ /* alphabetic? */ inline int t3_is_alpha(wchar_t ch) { int ctype = t3_get_chartype(ch); return (ctype >= T3_CTYPE_ALPHA && ctype <= T3_CTYPE_LOWER); } /* uppercase? */ inline int t3_is_upper(wchar_t ch) { int ctype = t3_get_chartype(ch); return (ctype == T3_CTYPE_UPPER || ctype == T3_CTYPE_TITLE); } /* lowercase? */ inline int t3_is_lower(wchar_t ch) { return (t3_get_chartype(ch) == T3_CTYPE_LOWER); } /* digit? */ inline int t3_is_digit(wchar_t ch) { return (t3_get_chartype(ch) == T3_CTYPE_DIGIT); } /* horizontal whitespace? */ inline int t3_is_space(wchar_t ch) { return (t3_get_chartype(ch) == T3_CTYPE_SPACE); } /* vertical whitespace? */ inline int t3_is_vspace(wchar_t ch) { return (t3_get_chartype(ch) == T3_CTYPE_VSPAC); } /* any whitespace, horizontal or vertical? */ inline int t3_is_whitespace(wchar_t ch) { int t = t3_get_chartype(ch); return (t == T3_CTYPE_SPACE || t == T3_CTYPE_VSPAC); } /* punctuation? */ inline int t3_is_punct(wchar_t ch) { return (t3_get_chartype(ch) == T3_CTYPE_PUNCT); } /* is it a defined unicode character? */ inline int t3_is_unichar(wchar_t ch) { return (t3_get_chartype(ch) != T3_CTYPE_UNDEF); } /* ------------------------------------------------------------------------ */ /* * Case folding wchar_t string reader */ class CVmCaseFoldStr { public: CVmCaseFoldStr(const wchar_t *s) { init(s, wcslen(s)); } CVmCaseFoldStr(const wchar_t *s, size_t len) { init(s, len); } /* get the current pointer */ const wchar_t *getptr() const { return p; } /* is another character available? */ int more() const { return rem != 0 || *fp != 0 || fp == ie; } /* are we at a character boundary? */ int at_boundary() const { return fp != fpbase && *fp == 0; } /* get the next character */ wchar_t getch() { /* if the expansion is exhausted, expand the next source character */ if (fp != ie && *fp == 0) { /* if there's nothing left in the source string, we're done */ if (rem == 0) return 0; /* get the next source character */ wchar_t ch = *p++; --rem; /* get its case-folded expansion */ if ((fp = t3_to_fold(ch)) == 0) { ie[0] = ch; fpbase = fp = ie; } } /* return the next expansion character */ return *fp++; } /* * Case-insensitive wide-string comparison routine. Returns true if * the strings are equal, false if not. */ static int wstreq(const wchar_t *a, const wchar_t *b) { /* loop over characters until we find a mismatch */ for (CVmCaseFoldStr fa(a), fb(b) ; ; ) { /* get the next character of each folded string */ wchar_t ca = fa.getch(); wchar_t cb = fb.getch(); /* if they differ, return not equal */ if (ca != cb) return FALSE; /* if they both ended here, we have a match */ if (ca == 0) return TRUE; } } private: void init(const wchar_t *s, size_t len) { /* set up at the start of the string */ this->p = s; this->rem = len; /* null-terminate our special one-byte identity conversion buffer */ ie[1] = 0; /* start without anything loaded */ fpbase = ie; fp = &ie[1]; } /* current position in case folding expansion of current source char */ const wchar_t *fp; /* start of current folding expansion */ const wchar_t *fpbase; /* buffer for identity expansions */ wchar_t ie[2]; /* original source string and character length */ const wchar_t *p; size_t rem; }; #endif /* VMUNI_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmvec.cpp�������������������������������������������������������������������������0000664�0000000�0000000�00000277122�12650170723�0015515�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmvec.cpp - T3 VM Vector metaclass Function Notes Modified 05/14/00 MJRoberts - Creation */ #include <stdlib.h> #include <stdarg.h> #include "t3std.h" #include "vmtype.h" #include "vmmcreg.h" #include "vmmeta.h" #include "vmglob.h" #include "vmobj.h" #include "vmvec.h" #include "vmlst.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmfile.h" #include "vmstack.h" #include "vmbif.h" #include "vmundo.h" #include "vmrun.h" #include "vmiter.h" #include "vmsort.h" /* ------------------------------------------------------------------------ */ /* * The largest number of items we can store is the smaller of the maximum * value for a UINT2 (unsigned 16-bit integer, so 2^16-1) or the maximum * allocation size (OSMALMAX) divided by the element size (DATAHOLDER) * divided minus overhead. */ const int32_t VEC_MAX_BY_ALO = (int32_t)(OSMALMAX/(VMB_DATAHOLDER + 1.0/8)) - 4; const int32_t VEC_MAX_ELEMENTS = (VEC_MAX_BY_ALO < 0xFFFF ? VEC_MAX_BY_ALO : 0xFFFF); /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassVector metaclass_reg_obj; CVmMetaclass *CVmObjVector::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjVector:: *CVmObjVector::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjVector::getp_undef, /* 0 */ &CVmObjVector::getp_to_list, /* 1 */ &CVmObjVector::getp_get_size, /* 2 */ &CVmObjVector::getp_copy_from, /* 3 */ &CVmObjVector::getp_fill_val, /* 4 */ &CVmObjVector::getp_subset, /* 5 */ &CVmObjVector::getp_apply_all, /* 6 */ &CVmObjVector::getp_index_which, /* 7 */ &CVmObjVector::getp_for_each, /* 8 */ &CVmObjVector::getp_for_each_assoc, /* 9 */ &CVmObjVector::getp_map_all, /* 10 */ &CVmObjVector::getp_index_of, /* 11 */ &CVmObjVector::getp_val_which, /* 12 */ &CVmObjVector::getp_last_index_of, /* 13 */ &CVmObjVector::getp_last_index_which, /* 14 */ &CVmObjVector::getp_last_val_which, /* 15 */ &CVmObjVector::getp_count_of, /* 16 */ &CVmObjVector::getp_count_which, /* 17 */ &CVmObjVector::getp_get_unique, /* 18 */ &CVmObjVector::getp_append_unique, /* 19 */ &CVmObjVector::getp_sort, /* 20 */ &CVmObjVector::getp_set_length, /* 21 */ &CVmObjVector::getp_insert_at, /* 22 */ &CVmObjVector::getp_remove_element_at, /* 23 */ &CVmObjVector::getp_remove_range, /* 24 */ &CVmObjVector::getp_append, /* 25 */ &CVmObjVector::getp_prepend, /* 26 */ &CVmObjVector::getp_append_all, /* 27 */ &CVmObjVector::getp_remove_element, /* 28 */ &CVmObjVector::getp_splice, /* 29 */ &CVmObjVector::getp_join, /* 30 */ &CVmObjVector::getp_generate, /* 31 */ &CVmObjVector::getp_indexOfMin, /* 32 */ &CVmObjVector::getp_minVal, /* 33 */ &CVmObjVector::getp_indexOfMax, /* 34 */ &CVmObjVector::getp_maxVal /* 35 */ }; /* static method indices */ static const int PROPIDX_generate = 31; /* ------------------------------------------------------------------------ */ /* * create with a given number of elements */ CVmObjVector::CVmObjVector(VMG_ size_t element_count) { /* allocate space */ alloc_vector(vmg_ element_count); /* * a vector intially has no elements in use; the element_count * merely gives the number of slots to be allocated initially (this * won't affect the allocation count, which is stored separately) */ set_element_count(0); } /* ------------------------------------------------------------------------ */ /* * allocate space */ void CVmObjVector::alloc_vector(VMG_ size_t cnt) { /* allocate space for the given number of elements */ ext_ = (char *)G_mem->get_var_heap()->alloc_mem(calc_alloc(cnt), this); /* set the element count and allocated count */ set_allocated_count(cnt); set_element_count(cnt); /* clear the undo bits */ clear_undo_bits(); } /* ------------------------------------------------------------------------ */ /* * notify of deletion */ void CVmObjVector::notify_delete(VMG_ int) { /* free our extension */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* ------------------------------------------------------------------------ */ /* * Check an argument to see if it's a source list or vector */ static int get_src_obj(VMG_ const vm_val_t *arg, size_t *copy_cnt) { /* check for a list */ int c; if (arg->is_listlike(vmg0_) && (c = arg->ll_length(vmg0_)) >= 0) { /* it's a list-like object */ *copy_cnt = c; return TRUE; } /* it's not a list */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjVector::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id; CVmObjVector *vec; size_t cnt = 0; size_t copy_cnt = 0; vm_val_t *lstval = 0; size_t i; /* check arguments */ if (argc > 2) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* if there are no arguments, use a default initial allocation size */ if (argc == 0) cnt = 10; /* parse the first argument, if present */ if (argc >= 1) { /* get the first argument */ vm_val_t *arg1 = G_stk->get(0); /* check for an initial allocation length or source object */ if (arg1->typ == VM_INT) { /* get the number of elements to allocate */ cnt = (size_t)arg1->val.intval; } else if (get_src_obj(vmg_ arg1, ©_cnt)) { /* it's a source object */ lstval = arg1; } else { /* anything else is invalid */ err_throw(VMERR_BAD_TYPE_BIF); } } /* parse the second argument, if present */ if (argc >= 2) { /* get the second argument */ vm_val_t *arg2 = G_stk->get(1); /* see what we have */ if (get_src_obj(vmg_ arg2, ©_cnt)) { /* it's a source object */ lstval = arg2; } else if (arg2->typ == VM_INT) { /* * They want an explicit initial size, with the elements filled * in with nil values. Set the copy size; we'll know to fill * in nil values since there's no source object to copy from. */ copy_cnt = (size_t)arg2->val.intval; } else { /* invalid source type */ err_throw(VMERR_BAD_TYPE_BIF); } } /* * make sure we allocate enough initial space to store the source * object we're copying */ if (copy_cnt > cnt) cnt = copy_cnt; /* create the vector with the given number of elements */ id = vm_new_id(vmg_ FALSE, TRUE, FALSE); vec = new (vmg_ id) CVmObjVector(vmg_ cnt); /* copy or initialize the elements */ for (i = 0 ; i < copy_cnt ; ++i) { vm_val_t ele; /* * get my element at this index: if we have a source list or * vector, take the current element from that list or vector; * otherwise, initialize the element to nil */ if (lstval != 0) lstval->ll_index(vmg_ &ele, i + 1); else ele.set_nil(); /* * set my element at this index - this is construction, so there's * no need to save undo information */ vec->set_element(i, &ele); } /* set the initial size */ vec->set_element_count(copy_cnt); /* discard arguments */ G_stk->discard(argc); /* return the new object */ return id; } /* ------------------------------------------------------------------------ */ /* * save to a file */ void CVmObjVector::save_to_file(VMG_ class CVmFile *fp) { size_t ele_cnt; size_t alloc_cnt; /* get our element count and full allocation count */ alloc_cnt = get_allocated_count(); ele_cnt = get_element_count(); /* write the counts and the elements */ fp->write_uint2(alloc_cnt); fp->write_uint2(ele_cnt); fp->write_bytes(get_element_ptr(0), calc_alloc_ele(ele_cnt)); } /* * restore from a file */ void CVmObjVector::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { size_t ele_cnt; size_t alloc_cnt; /* read the element count and the full allocation count */ alloc_cnt = fp->read_uint2(); ele_cnt = fp->read_uint2(); /* free any existing extension */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* allocate the space at the allocation count */ alloc_vector(vmg_ alloc_cnt); /* set the actual in-use element count */ set_element_count(ele_cnt); /* read the contents */ fp->read_bytes(get_element_ptr(0), calc_alloc_ele(ele_cnt)); /* fix up object references in the values */ fixups->fix_dh_array(vmg_ get_element_ptr(0), ele_cnt); } /* ------------------------------------------------------------------------ */ /* * create with no initial contents */ vm_obj_id_t CVmObjVector::create(VMG_ int in_root_set) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmObjVector(); return id; } /* * create with a given number of elements */ vm_obj_id_t CVmObjVector::create(VMG_ int in_root_set, size_t element_count) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmObjVector(vmg_ element_count); return id; } /* ------------------------------------------------------------------------ */ /* * create with a given list of values, given as varargs vm_val_t values */ vm_obj_id_t CVmObjVector::create_fill(VMG_ int in_root_set, size_t element_count, ...) { /* create the object */ vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); CVmObjVector *vec = new (vmg_ id) CVmObjVector(vmg_ element_count); /* populate the elements */ va_list args; va_start(args, element_count); for (size_t i = 0 ; i < element_count ; ++i) { /* get the next value */ const vm_val_t *val = va_arg(args, const vm_val_t *); /* store it */ vec->set_element(i, val); } va_end(args); /* set the element count */ vec->set_element_count(element_count); /* return the new object's ID */ return id; } /* ------------------------------------------------------------------------ */ /* * Create a copy of this object */ vm_obj_id_t CVmObjVector::create_copy(VMG_ const vm_val_t *self_val) { vm_obj_id_t new_id; CVmObjVector *new_vec; /* save the original object on the stack for gc protection */ G_stk->push(self_val); /* create a new vector with the same parameters as this one */ new_id = vm_new_id(vmg_ FALSE, TRUE, FALSE); new_vec = new (vmg_ new_id) CVmObjVector(vmg_ get_allocated_count()); new_vec->set_element_count(get_element_count()); /* copy the elements from the old vector to the new vector */ memcpy(new_vec->get_element_ptr(0), get_element_ptr(0), calc_alloc_ele(get_element_count())); /* done with the gc protection - discard it */ G_stk->discard(1); /* return the new vector's object ID */ return new_id; } /* ------------------------------------------------------------------------ */ /* * Set the allocation size - this is the number of elements we've allocated * space for, which might exceed the number of elements we're actually * storing currently. */ void CVmObjVector::set_allocated_count(size_t cnt) { /* if the new size exceeds the maximum allowed size, throw an error */ if (cnt > 65535) err_throw(VMERR_OUT_OF_RANGE); /* set the new allocated size */ vmb_put_len(cons_get_vector_ext_ptr(), cnt); } /* ------------------------------------------------------------------------ */ /* * Create an iterator */ void CVmObjVector::new_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val) { vm_val_t copy_val; /* * Create a copy of the vector - since a vector can change, we must * always create an iterator based on a copy, so that the iterator has * a consistent view of the original as of the time the iterator was * created. */ copy_val.set_obj(create_copy(vmg_ self_val)); /* push the copy for gc protection */ G_stk->push(©_val); /* * Set up a new indexed iterator object. The first valid index for a * vector is always 1, and the last valid index is the same as the * number of elements. */ retval->set_obj(CVmObjIterIdx::create_for_coll(vmg_ ©_val, 1, get_element_count())); /* discard gc protection */ G_stk->discard(); } /* * Create a live iterator */ void CVmObjVector::new_live_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val) { /* * Set up a new indexed iterator object. The first valid index for a * vector is always 1, and the last valid index is the same as the * number of elements. Since we want a "live" iterator, create the * iterator with a direct reference to the vector. */ retval->set_obj(CVmObjIterIdx::create_for_coll(vmg_ self_val, 1, get_element_count())); } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjVector::set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * call a static property */ int CVmObjVector::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* get the function table index */ int idx = G_meta_table->prop_to_vector_idx( metaclass_reg_->get_reg_idx(), prop); /* check for static methods */ switch (idx) { case PROPIDX_generate: return static_getp_generate(vmg_ result, argc); default: /* inherit the default handling */ return CVmObjCollection::call_stat_prop( vmg_ result, pc_ptr, argc, prop); } } /* ------------------------------------------------------------------------ */ /* * get a property */ int CVmObjVector::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property index to an index into our function table */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit our base class handling */ return CVmObjCollection:: get_prop(vmg_ prop, retval, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * Expand the vector to accommodate the given number of new elements. * We'll increase the element count, and we'll save undo for the change. * If necessary, we'll re-allocate our memory extension block at a * larger size. */ void CVmObjVector::expand_by(VMG_ vm_obj_id_t self, size_t added_elements) { /* calculate the element count */ size_t new_ele_cnt = get_element_count() + added_elements; /* remember the new size, saving undo */ set_element_count_undo(vmg_ self, new_ele_cnt); } /* ------------------------------------------------------------------------ */ /* * Append an element, with no undo */ void CVmObjVector::append_element(VMG_ vm_obj_id_t self, const vm_val_t *val) { /* expand the vector if necessary */ size_t cnt = get_element_count(); if (cnt >= get_allocated_count()) { /* expand by 50% of the current size, or at least 5 new elements */ int inc = cnt/2; if (inc < 5) inc = 5; /* do the expansion */ expand_by(vmg_ self, inc); } /* set the element */ set_element(cnt, val); /* count it in the vector */ set_element_count(cnt+1); } /* ------------------------------------------------------------------------ */ /* * Set the element count, keeping undo for the change */ void CVmObjVector::set_element_count_undo(VMG_ vm_obj_id_t self, size_t new_ele_cnt) { /* add undo if necessary */ if (G_undo != 0) { size_t old_ele_cnt; vm_val_t old_val; size_t idx; vm_val_t nil_val; /* get the old size */ old_ele_cnt = get_element_count(); /* * If we're shrinking the vector, save undo for each element we're * dropping off the end; to do this, simply set each element we're * losing to nil, since this will add an undo record with the * original value of the element. * * Note that we must save undo for dropped elements BEFORE we set * the vector's new sizd. We must perform the steps in this order * because undo is applied in reverse order: when we read back the * undo, we'll first change the vector size to its old size (since * that's the last saved undo instruction), then we'll set the * elements to their old values. */ nil_val.set_nil(); for (idx = new_ele_cnt ; idx < old_ele_cnt ; ++idx) set_element_undo(vmg_ self, idx, &nil_val); /* set up an integer with the pre-modification element count */ old_val.set_int(old_ele_cnt); /* * add the undo record - use the special index value 0xffffffff * as the key; this will never be a valid index for a real * element, because we could never address an element with an * index this high in a 32-bit address space */ G_undo->add_new_record_int_key(vmg_ self, 0xffffffffU, &old_val); } /* * if the new size is larger than our current allocation size, * increase the allocated size */ if (new_ele_cnt > get_allocated_count()) { size_t new_alloc_cnt; size_t new_mem_size; /* bump up the allocated size */ new_alloc_cnt = get_allocated_count() + get_alloc_count_increment(); /* if that's still not big enough, go up to the requested size */ if (new_alloc_cnt < new_ele_cnt) new_alloc_cnt = new_ele_cnt; /* get the new size we need */ new_mem_size = calc_alloc(new_alloc_cnt); /* reallocate our memory at the new, larger size */ ext_ = (char *)G_mem->get_var_heap() ->realloc_mem(new_mem_size, ext_, this); /* set the new allocation size */ set_allocated_count(new_alloc_cnt); /* * clear the undo bits (it's easier than moving them, and the only * cost is that we might generate some redundant undo records) */ clear_undo_bits(); } /* * if we're expanding the in-use size of the vector, set each * newly-added element to nil */ if (new_ele_cnt > get_element_count()) { size_t i; vm_val_t nil_val; /* * set the old value for each new element to nil (we don't have to * save undo for this, since the undo operation for the change in * size will simply discard all of the new elements) */ nil_val.set_nil(); for (i = get_element_count() ; i < new_ele_cnt ; ++i) set_element(i, &nil_val); } /* set the new element count */ set_element_count(new_ele_cnt); } /* ------------------------------------------------------------------------ */ /* * notify of new undo savepoint */ void CVmObjVector::notify_new_savept() { /* * clear the undo bits - we obviously have no undo for the new * savepoint yet */ clear_undo_bits(); } /* ------------------------------------------------------------------------ */ /* * apply undo */ void CVmObjVector::apply_undo(VMG_ struct CVmUndoRecord *rec) { /* * if the record refers to index 0xffffffff, this is a size-change * record; otherwise, it's an ordinary element change record */ if ((size_t)rec->id.intval == 0xffffffffU) { /* * it's a size change - apply the new size, which is stored as * an integer in the old value record */ set_element_count((size_t)rec->oldval.val.intval); } else { /* it's an ordinary index record - set the element from undo data */ set_element((size_t)rec->id.intval, &rec->oldval); } } /* ------------------------------------------------------------------------ */ /* * mark references from an undo record */ void CVmObjVector::mark_undo_ref(VMG_ CVmUndoRecord *rec) { /* if the undo record refers to an object, mark the object */ if (rec->oldval.typ == VM_OBJ) G_obj_table->mark_all_refs(rec->oldval.val.obj, VMOBJ_REACHABLE); } /* ------------------------------------------------------------------------ */ /* * mark references */ void CVmObjVector::mark_refs(VMG_ uint state) { /* get my element count */ size_t cnt = get_element_count(); /* mark as referenced each object in the vector */ for (char *p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p)) { /* * if this is an object, mark it as referenced, and mark its * references as referenced */ if (vmb_get_dh_type(p) == VM_OBJ) G_obj_table->mark_all_refs(vmb_get_dh_obj(p), state); } } /* ------------------------------------------------------------------------ */ /* * load from an image file */ void CVmObjVector::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load from the image data */ load_image_data(vmg_ ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload the object from image data */ void CVmObjVector::reload_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t siz) { /* load the image data */ load_image_data(vmg_ ptr, siz); } /* * load the data from an image file */ void CVmObjVector::load_image_data(VMG_ const char *ptr, size_t siz) { size_t alloc_cnt; size_t ele_cnt; /* if we already have memory allocated, free it */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* get the allocation size and the actual in-use size */ alloc_cnt = vmb_get_len(ptr); ele_cnt = vmb_get_len(ptr + VMB_LEN); /* make sure we allocate at least as much as is actually in use */ if (alloc_cnt < ele_cnt) alloc_cnt = ele_cnt; /* make sure the size isn't larger than we'd expect */ if (siz > VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt)) siz = VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt); /* * allocate memory at the new allocation size as indicated in the * image data */ alloc_vector(vmg_ alloc_cnt); /* set the in-use element count */ set_element_count(ele_cnt); /* * if the size is smaller than we'd expect from the initialized * element count, set extra elements to nil */ if (siz < VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt)) { size_t i; vm_val_t nil_val; /* set all elements to nil */ nil_val.set_nil(); for (i = 0 ; i < ele_cnt ; ++i) set_element(i, &nil_val); } /* copy the initialized elements from the image data */ memcpy(get_element_ptr(0), ptr + VMB_LEN*2, siz - VMB_LEN*2); /* we're resetting to initial state, so forget all undo */ clear_undo_bits(); } /* ------------------------------------------------------------------------ */ /* * index the vector */ int CVmObjVector::index_val_q(VMG_ vm_val_t *result, vm_obj_id_t, const vm_val_t *index_val) { /* get the index value as an integer */ int32_t idx = index_val->num_to_int(vmg0_); /* make sure it's in range - 1 to our element count, inclusive */ if (idx < 1 || (uint32_t)idx > get_element_count()) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* * get the indexed element and store it in the result, adjusting the * index to the C-style 0-based range */ get_element(idx - 1, result); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * set an indexed element of the vector */ int CVmObjVector::set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val) { /* get the index value as an integer */ int32_t idx = index_val->num_to_int(vmg0_); /* make sure it's at least 1 */ if (idx < 1) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* * if it's higher than the current length, extend the vector with nil * entries to the requested size */ if ((uint32_t)idx > get_element_count()) { size_t i; vm_val_t nil_val; /* note the first new element index */ i = get_element_count(); /* extend the vector */ set_element_count_undo(vmg_ self, idx); /* * Fill in entries between the old length and the new length with * nil. Note that we don't have to fill in the very last element, * since we'll explicitly set it to the new value momentarily * anyway. Note also that we don't need to keep undo for the * initializations, since on undo we'll truncate the vector to * remove the newly-added elements and thus won't need to restore * any values for the slots. */ for (nil_val.set_nil() ; i < (size_t)idx - 1 ; ++i) set_element(i, &nil_val); /* * set the new value - this doesn't require undo since we had to * expand the vector to make room for it */ set_element(idx - 1, new_val); } else { /* set the element and record undo, using a zero-based index */ set_element_undo(vmg_ self, (size_t)idx - 1, new_val); } /* the result is the original vector value */ new_container->set_obj(self); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Set an element and record undo for the change - takes a C-style 0-based * index. */ void CVmObjVector::set_element_undo(VMG_ vm_obj_id_t self, size_t idx, const vm_val_t *new_val) { /* if we don't have undo for this element already, save undo now */ if (G_undo != 0 && !get_undo_bit(idx)) { vm_val_t old_val; /* get the pre-modification value of this element */ get_element(idx, &old_val); /* add the undo record */ G_undo->add_new_record_int_key(vmg_ self, idx, &old_val); /* * set the undo bit to indicate that we now have undo for this * element */ set_undo_bit(idx, TRUE); } /* get the indexed element and store it in the result */ set_element(idx, new_val); } /* ------------------------------------------------------------------------ */ /* * Check a value for equality. We will match any list or vector with the * same number of elements and the same value for each element. */ int CVmObjVector::equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const { int cnt; int cnt2; int idx; /* if the recursion depth is excessive, throw an error */ if (depth > VM_MAX_TREE_DEPTH_EQ) err_throw(VMERR_TREE_TOO_DEEP_EQ); /* if the other value is a reference to myself, we certainly match */ if (val->typ == VM_OBJ && val->val.obj == self) { /* no need to look at the contents if this is a reference to me */ return TRUE; } /* if it's not list-like, it's not a match */ if (!val->is_listlike(vmg0_) || (cnt2 = val->ll_length(vmg0_)) < 0) return FALSE; /* if the sizes don't match, the values are not equal */ cnt = get_element_count(); if (cnt != cnt2) return FALSE; /* compare element by element */ for (idx = 0 ; idx < cnt ; ++idx) { vm_val_t val1; vm_val_t val2; /* get this element of self */ get_element(idx, &val1); /* get this element of the other value */ val->ll_index(vmg_ &val2, idx + 1); /* if these elements aren't equal, our values aren't equal */ if (!val1.equals(vmg_ &val2, depth + 1)) return FALSE; } /* we didn't find any differences, so the values are equal */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Hash value calculation */ uint CVmObjVector::calc_hash(VMG_ vm_obj_id_t self, int depth) const { uint hash; uint i; /* if the recursion depth is excessive, throw an error */ if (depth > VM_MAX_TREE_DEPTH_EQ) err_throw(VMERR_TREE_TOO_DEEP_EQ); /* combine the hash values of the members of the vector */ for (hash = 0, i = 0 ; i < get_element_count() ; ++i) { vm_val_t ele; /* get this element */ get_element(i, &ele); /* * Calculate its hash value and add it into the combined hash. * * Note that we have to increase the recursion depth, because we're * recursively calculating the hash value of our contents, and our * contents can refer (directly or indirectly) back to this object. * In other words, we can have cycles in the reference tree from * this object back to itself, so we need to keep track of the * recursion depth to ensure we don't loop forever. */ hash += ele.calc_hash(vmg_ depth + 1); } /* return the combined hash */ return hash; } /* ------------------------------------------------------------------------ */ /* * add a value to the vector */ int CVmObjVector::add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { int idx; int rhs_cnt, alo_cnt; int i; CVmObjVector *new_vec; /* push the value to append, for gc protection */ G_stk->push(val); /* * remember the index of the first new element - this is simply one * higher than the last valid current index */ idx = get_element_count(); /* get the number of elements to add */ rhs_cnt = (val->is_listlike(vmg0_) ? val->ll_length(vmg0_) : -1); /* if it's not list-like, just add it directly as a single element */ alo_cnt = (rhs_cnt < 0 ? 1 : rhs_cnt); /* * allocate a copy of the vector for the result - make it big enough * for my elements plus the elements to be appended */ result->set_obj(create(vmg_ FALSE, idx + alo_cnt)); /* get the return value as a vector */ new_vec = (CVmObjVector *)vm_objp(vmg_ result->val.obj); /* push the result for gc protection */ G_stk->push(result); /* copy my elements into the result */ memcpy(new_vec->get_element_ptr(0), get_element_ptr(0), calc_alloc_ele(idx)); /* * set the element count to include the elements copied so far (it's a * new vector, so there's no need to save undo) */ new_vec->set_element_count(idx); /* add the new element or elements */ if (rhs_cnt < 0) { /* add a single element */ new_vec->set_element(idx, val); new_vec->set_element_count(idx + 1); } else { /* add the contents of the rhs */ for (i = 1 ; i <= rhs_cnt ; ++i, ++idx) { /* get this element from the right-hand side */ vm_val_t ele; val->ll_index(vmg_ &ele, i); /* store the element in the result */ new_vec->set_element(idx, &ele); new_vec->set_element_count(idx + 1); } } /* discard the gc protect */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * subtract a value from the vector */ int CVmObjVector::sub_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { int ele_cnt, rhs_cnt; int i; CVmObjVector *new_vec; size_t new_cnt; /* push the value to append, for gc protection */ G_stk->push(val); /* note the initial element count */ ele_cnt = get_element_count(); /* get the number of elements to remove */ rhs_cnt = (val->is_listlike(vmg0_) ? val->ll_length(vmg0_) : -1); /* * allocate a new vector for the result - the new object will be no * larger than the current object, so allocate at the same size */ result->set_obj(create(vmg_ FALSE, ele_cnt)); /* push the result for gc protection */ G_stk->push(result); /* get the return value as a vector */ new_vec = (CVmObjVector *)vm_objp(vmg_ result->val.obj); /* * copy each element from me to the new vector, but don't copy * elements found in the right-hand side */ for (i = 0, new_cnt = 0 ; i < ele_cnt ; ++i) { vm_val_t ele; int found = FALSE; /* get this element of self */ get_element(i, &ele); /* check the right-hand side for a match */ if (rhs_cnt < 0) { /* single element - just check this element */ if (val->equals(vmg_ &ele)) found = TRUE; } else { /* scan the right side for the element */ for (int j = 1 ; j <= rhs_cnt ; ++j) { /* get this right-hand element */ vm_val_t rh_ele; val->ll_index(vmg_ &rh_ele, j); /* * if this right-hand element matches this left-hand * element, note that we have a match */ if (rh_ele.equals(vmg_ &ele)) { /* note that we found it */ found = TRUE; /* no need to look any further - one match is enough */ break; } } } /* * If we didn't find the value, include it in the result vector. * Note that we don't have to save undo, since we're still * constructing the new vector. */ if (!found) { /* store the element in the result vector */ new_vec->set_element(new_cnt, &ele); /* count it */ ++new_cnt; } } /* * update the new vector's size - we don't have to save undo for this * change, because we're still constructing the new vector */ new_vec->set_element_count(new_cnt); /* discard the gc protection */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - convert to a list */ int CVmObjVector::getp_to_list(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { int src_cnt; int dst_cnt; int start_idx; CVmObjList *lst; int idx; uint orig_argc = (argc != 0 ? *argc : 0); static CVmNativeCodeDesc desc(0, 2); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* note our size */ src_cnt = get_element_count(); /* * if there's a starting index, retrieve it; otherwise, start at our * first element */ if (orig_argc >= 1) start_idx = (size_t)CVmBif::pop_int_val(vmg0_); else start_idx = 1; /* negative index values count from the end of the list */ if (start_idx < 0) start_idx += src_cnt + 1; /* if the starting index is below 1, force it to 1 */ if (start_idx < 1) start_idx = 1; /* adjust the starting index to a 0-based index */ --start_idx; /* * if there's a size argument, retrieve it; if it's not specified, * use our actual size for the output size */ if (orig_argc >= 2) dst_cnt = (size_t)CVmBif::pop_int_val(vmg0_); else dst_cnt = src_cnt; /* * in no case will the result list have more elements than we can * actually supply */ if (start_idx >= src_cnt) { /* we're starting past our last element - we can't supply anything */ dst_cnt = 0; } else if (src_cnt - start_idx < dst_cnt) { /* we can't supply as many values as requested - lower the size */ dst_cnt = src_cnt - start_idx; } /* push a self-reference for garbage collection protection */ G_stk->push()->set_obj(self); /* create the new list */ retval->set_obj(CVmObjList::create(vmg_ FALSE, dst_cnt)); lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* set the list elements */ for (idx = 0 ; idx < dst_cnt ; ++idx) { vm_val_t val; /* get my element at this index */ get_element(idx + start_idx, &val); /* store the element in the list */ lst->cons_set_element(idx, &val); } /* discard the self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - copy from another vector or list */ int CVmObjVector::getp_copy_from(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(4); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* retrieve the source value */ vm_val_t src_val; G_stk->pop(&src_val); /* make sure the source argument is list-like, and get its length */ int src_cnt; if (!src_val.is_listlike(vmg0_) || (src_cnt = src_val.ll_length(vmg0_)) < 0) err_throw(VMERR_BAD_TYPE_BIF); /* get the source starting index, adjusting negatives to count from end */ int src_start = CVmBif::pop_int_val(vmg0_); if (src_start < 0) src_start += src_cnt + 1; /* note the destination length (i.e., my element count) */ int dst_cnt = get_element_count(); /* get the destination starting index, adjusting negative values */ int dst_start = CVmBif::pop_int_val(vmg0_); if (dst_start < 0) dst_start += dst_cnt + 1; /* get the number of elements to copy */ int copy_cnt = CVmBif::pop_int_val(vmg0_); /* if either starting index is below 1, force it to 1 */ if (src_start < 1) src_start = 1; if (dst_start < 1) dst_start = 1; /* adjust the starting indices to 0-based values */ --src_start; --dst_start; /* limit our copying to the remaining elements of the source */ if (src_start >= src_cnt) copy_cnt = 0; else if (src_start + copy_cnt >= src_cnt) copy_cnt = src_cnt - src_start; /* expand the vector if necessary to make room for added elements */ if (dst_start + copy_cnt > dst_cnt) set_element_count_undo(vmg_ self, dst_start + copy_cnt); /* set the list elements */ for (int i = 0 ; i < copy_cnt ; ++i) { /* get the source element at this index */ vm_val_t val; src_val.ll_index(vmg_ &val, i + src_start + 1); /* set my element at this index, recording undo for the change */ set_element_undo(vmg_ self, i + dst_start, &val); } /* the return value is 'self' */ retval->set_obj(self); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - fill with a value */ int CVmObjVector::getp_fill_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ uint orig_argc = (argc != 0 ? *argc : 0); static CVmNativeCodeDesc desc(1, 2); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* note our size */ int cnt = get_element_count(); /* the return value is 'self' */ retval->set_obj(self); /* get the fill value */ vm_val_t fill_val; G_stk->pop(&fill_val); /* get the starting index; the default is 1 if it's not specified */ int start_idx = (orig_argc >= 2 ? CVmBif::pop_int_val(vmg0_) : 1); /* a negative index counts from the end of the vector */ if (start_idx < 0) start_idx += cnt + 1; /* if the starting index is below 1, force it to 1 */ if (start_idx < 1) start_idx = 1; /* adjust the starting index to a 0-based index */ --start_idx; /* * if there's a count argument, retrieve it; if it's not specified, * use our actual size for the count */ int end_idx = (orig_argc >= 3 ? start_idx + CVmBif::pop_int_val(vmg0_) : cnt); /* push self and the fill value for gc protection */ G_stk->push(retval); G_stk->push(&fill_val); /* expand the vector to the requested size */ if (end_idx > cnt) set_element_count_undo(vmg_ self, end_idx); /* set the elements to the fill value */ for (int idx = start_idx ; idx < end_idx ; ++idx) { /* set the element to the fill value, recording undo for the change */ set_element_undo(vmg_ self, idx, &fill_val); } /* discard GC protection */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - get the number of elements */ int CVmObjVector::getp_get_size(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* return my element count */ retval->set_int(get_element_count()); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - select a subset; returns a new vector consisting * of the subset of the original vector */ int CVmObjVector::getp_subset(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { const vm_val_t *func_val; size_t src; size_t dst; size_t cnt; CVmObjVector *new_vec; static CVmNativeCodeDesc desc(1); vm_rcdesc rc(vmg_ "Vector.subset", self, 5, G_stk->get(0), argc); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the function pointer argument, but leave it on the stack */ func_val = G_stk->get(0); /* push a self-reference while allocating to protect from gc */ G_interpreter->push_obj(vmg_ self); /* * allocate the new vector that we'll return - all we know at this * point is that the new vector won't be larger than the current * vector, so allocate at the current size */ cnt = get_element_count(); retval->set_obj(create(vmg_ FALSE, cnt)); /* get the return value as an vector */ new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj); /* * push a reference to the new list to protect it from the garbage * collector, which could be invoked in the course of executing the * user callback */ G_stk->push(retval); /* * Go through each element of our list, and invoke the callback on * each element. If the element passes, write it to the current * output location in the list; otherwise, just skip it. * * Note that we're using the same list as source and destination, * which is easy because the list will either shrink or stay the * same - we'll never need to insert new elements. */ for (src = dst = 0 ; src < cnt ; ++src) { vm_val_t ele; const vm_val_t *val; /* get this element from the source vector */ get_element(src, &ele); /* push the element as the callback's argument */ G_stk->push(&ele); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ func_val, 1, &rc, 0); /* get the result from R0 */ val = G_interpreter->get_r0(); /* * if the callback returned non-nil and non-zero, include this * element in the result */ if (val->typ == VM_NIL || (val->typ == VM_INT && val->val.intval == 0)) { /* it's nil or zero - don't include it in the result */ } else { /* * include this element in the result (there's no need to * save undo, since the whole vector is new) */ new_vec->set_element(dst, &ele); /* advance the output index */ ++dst; } } /* set the actual number of elements in the result */ new_vec->set_element_count(dst); /* discard our gc protection (self, return value) and our arguments */ G_stk->discard(3); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - apply a callback to each element */ int CVmObjVector::getp_apply_all(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { const vm_val_t *func_val; size_t idx; static CVmNativeCodeDesc desc(1); vm_rcdesc rc(vmg_ "Vector.applyAll", self, 6, G_stk->get(0), argc); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the function pointer argument, but leave it on the stack */ func_val = G_stk->get(0); /* push a self-reference while working to protect from gc */ G_interpreter->push_obj(vmg_ self); /* * we're going to return the 'self' object, since we update the vector * in-place */ retval->set_obj(self); /* * Go through each element of our vector, invoking the callback on * each element. Replace each element with the result of the * callback. Note that we intentionally re-check the element count on * each iteration, in case the callback changes the number of * elements. */ for (idx = 0 ; idx < get_element_count() ; ++idx) { /* push this element as the callback's argument */ push_element(vmg_ idx); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ func_val, 1, &rc, 0); /* replace this element with the result */ set_element_undo(vmg_ self, idx, G_interpreter->get_r0()); } /* discard our gc protection (self) and our arguments */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - find the first element matching a condition */ int CVmObjVector::getp_index_which(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use our general handler, working forwards */ vm_rcdesc rc(vmg_ "Vector.indexWhich", self, 7, G_stk->get(0), argc); return gen_index_which(vmg_ self, retval, argc, TRUE, FALSE, &rc); } /* * property evaluator - lastIndexWhich */ int CVmObjVector::getp_last_index_which(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use our general handler, working backwards */ vm_rcdesc rc(vmg_ "Vector.lastIndexWhich", self, 14, G_stk->get(0), argc); return gen_index_which(vmg_ self, retval, argc, FALSE, FALSE, &rc); } /* * general handler for indexWhich and lastIndexWhich */ int CVmObjVector::gen_index_which(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int forward, int count_only, vm_rcdesc *rc) { const vm_val_t *func_val; size_t cnt; size_t idx; int val_cnt; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the function pointer argument, but leave it on the stack */ func_val = G_stk->get(0); /* push a self-reference while working to protect from gc */ G_interpreter->push_obj(vmg_ self); /* presume we won't find a matching element */ retval->set_nil(); val_cnt = 0; /* get the number of elements to visit */ cnt = get_element_count(); /* start at the first or last element, depending on direction */ idx = (forward ? 0 : cnt); /* * Go through each element of our vector, and invoke the callback on * each element, looking for the first one that returns true. */ for (;;) { /* if we've reached the last element, we can stop looking */ if (forward ? idx >= cnt : idx == 0) break; /* if we're going backwards, decrement the index */ if (!forward) --idx; /* push the element as the callback's argument */ push_element(vmg_ idx); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ func_val, 1, rc, 0); /* * if the callback returned true, we've found the element we're * looking for */ if (G_interpreter->get_r0()->typ == VM_NIL || (G_interpreter->get_r0()->typ == VM_INT && G_interpreter->get_r0()->val.intval == 0)) { /* nil or zero - this one failed the test, so keep looking */ } else { /* it passed the test - check what we're doing */ if (count_only) { /* we only want the count */ ++val_cnt; } else { /* we want the (1-based) index - return it */ retval->set_int(idx + 1); /* found it - no need to keep looking */ break; } } /* if we're going forwards, increment the index */ if (forward) ++idx; } /* discard our gc protection (self) and our arguments */ G_stk->discard(2); /* return the count if desired */ if (count_only) retval->set_int(val_cnt); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - lastValWhich */ int CVmObjVector::getp_last_val_which(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* get the index of the value using lastIndexWhich */ getp_last_index_which(vmg_ self, retval, argc); /* if the return value is a valid index, get the value at the index */ if (retval->typ == VM_INT) { int idx; /* get the element as the return value */ idx = (int)retval->val.intval; get_element(idx - 1, retval); } /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - valWhich */ int CVmObjVector::getp_val_which(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* get the index of the value using indexWhich */ getp_index_which(vmg_ self, retval, argc); /* if the return value is a valid index, get the value at the index */ if (retval->typ == VM_INT) { int idx; /* get the element as the return value */ idx = (int)retval->val.intval; get_element(idx - 1, retval); } /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - call a callback for each element */ int CVmObjVector::getp_for_each(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use the general forEach processor */ vm_rcdesc rc(vmg_ "Vector.forEach", self, 8, G_stk->get(0), argc); return for_each_gen(vmg_ self, retval, argc, FALSE, &rc); } /* * property evaluator - call a callback for each element */ int CVmObjVector::getp_for_each_assoc(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use the general forEach processor */ vm_rcdesc rc(vmg_ "Vector.forEachAssoc", self, 9, G_stk->get(0), argc); return for_each_gen(vmg_ self, retval, argc, TRUE, &rc); } /* * General forEach/forEachAssoc processor */ int CVmObjVector::for_each_gen(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int pass_key_to_cb, vm_rcdesc *rc) { const vm_val_t *func_val; size_t idx; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the function pointer argument, but leave it on the stack */ func_val = G_stk->get(0); /* push a self-reference while working to protect from gc */ G_interpreter->push_obj(vmg_ self); /* no return value */ retval->set_nil(); /* * Invoke the callback on each element. Note that we intentionally do * not cache the element count, since it is possible that a program * could change the vector size in the course of an iteration; if we * cached the size, and the actual size were reduced during the * iteration, we would visit invalid elements past the new end of the * vector. To avoid this possibility, we re-check the current element * count on each iteration to make sure we haven't run off the end of * the vector. */ for (idx = 0 ; idx < get_element_count() ; ++idx) { /* push the element as the callback's argument */ push_element(vmg_ idx); /* push the index argument if desired */ if (pass_key_to_cb) G_stk->push()->set_int(idx + 1); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ func_val, pass_key_to_cb ? 2 : 1, rc, 0); } /* discard our gc protection (self) and our arguments */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - mapAll */ int CVmObjVector::getp_map_all(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { const vm_val_t *func_val; size_t idx; CVmObjVector *new_vec; static CVmNativeCodeDesc desc(1); vm_rcdesc rc(vmg_ "Vector.mapAll", self, 10, G_stk->get(0), argc); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the function pointer argument, but leave it on the stack */ func_val = G_stk->get(0); /* push a self-reference while working to protect from gc */ G_interpreter->push_obj(vmg_ self); /* * allocate a new vector for the return value - the new vector will * have the same size as the original, since we're mapping each * element of the old vector to the corresponding element of the new * vector */ retval->set_obj(create(vmg_ FALSE, get_allocated_count())); /* get the return value as an vector */ new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj); /* * push a reference to the new list to protect it from the garbage * collector, which could be invoked in the course of executing the * user callback */ G_stk->push(retval); /* * Go through each element of our vector, and invoke the callback on * each element, storing the result in the corresponding element of * the new vector. Note that we re-check the element count on each * iteration, in case the callback changes it on us. */ for (idx = 0 ; idx < get_element_count() ; ++idx) { /* push the element as the callback's argument */ push_element(vmg_ idx); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ func_val, 1, &rc, 0); /* * replace this element with the result (there's no need to save * undo, since the whole vector is new) */ new_vec->set_element(idx, G_interpreter->get_r0()); new_vec->set_element_count(idx + 1); } /* discard our gc protection (self, new vector) and our arguments */ G_stk->discard(3); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - indexOf */ int CVmObjVector::getp_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use our general handler, going forwards */ return gen_index_of(vmg_ self, retval, argc, TRUE, FALSE); } /* * property evaluator - lastIndexOf */ int CVmObjVector::getp_last_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use our general handler, going backwards */ return gen_index_of(vmg_ self, retval, argc, FALSE, FALSE); } /* * general handler for indexOf and lastIndexOf - searches the vector * either forwards of backwards for a given value */ int CVmObjVector::gen_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int forward, int count_only) { const vm_val_t *val; size_t cnt; size_t idx; int val_cnt; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the value, but leave it on the stack */ val = G_stk->get(0); /* push a self-reference while working to protect from gc */ G_interpreter->push_obj(vmg_ self); /* presume we won't find a matching element */ retval->set_nil(); val_cnt = 0; /* get the number of elements to visit */ cnt = get_element_count(); /* start at the first or last element, depending on the direction */ idx = (forward ? 0 : cnt); /* scan the list, looking for the element */ for (;;) { vm_val_t ele; /* if we've reached the last element, stop looking */ if (forward ? idx >= cnt : idx == 0) break; /* if we're going backwards, move to the next element position */ if (!forward) --idx; /* get this element */ get_element(idx, &ele); /* if the element matches the search value, return its index */ if (ele.equals(vmg_ val)) { /* it matches - see what we're doing */ if (count_only) { /* they only want the count */ ++val_cnt; } else { /* this is the one - return its 1-based index */ retval->set_int(idx + 1); /* foind it - no need to keep searching */ break; } } /* if we're going forwards, move to the next element */ if (forward) ++idx; } /* discard our gc protection (self) and our arguments */ G_stk->discard(2); /* return the count if desired */ if (count_only) retval->set_int(val_cnt); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - countOf */ int CVmObjVector::getp_count_of(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use our general handler to obtain the count */ return gen_index_of(vmg_ self, retval, argc, TRUE, TRUE); } /* ------------------------------------------------------------------------ */ /* * property evaluator - countWhich */ int CVmObjVector::getp_count_which(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use our general handler to obtain the count */ vm_rcdesc rc(vmg_ "Vector.countWhich", self, 17, G_stk->get(0), argc); return gen_index_which(vmg_ self, retval, argc, TRUE, TRUE, &rc); } /* ------------------------------------------------------------------------ */ /* * property evaluator - getUnique; returns a new vector consisting of the * unique elements of the original vector */ int CVmObjVector::getp_get_unique(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { CVmObjVector *new_vec; size_t cnt; static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* put myself on the stack for GC protection */ G_interpreter->push_obj(vmg_ self); /* * allocate a new vector for the return value - the new vector will * never be any larger than the original */ cnt = get_element_count(); retval->set_obj(create(vmg_ FALSE, cnt)); /* push a reference to the new list for gc protection */ G_stk->push(retval); /* get the return value as a vector */ new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj); /* start out with the result element count the same as my own */ new_vec->set_element_count(cnt); /* copy my elements to the new vector */ memcpy(new_vec->get_element_ptr(0), get_element_ptr(0), calc_alloc_ele(cnt)); /* uniquify the result */ new_vec->cons_uniquify(vmg0_); /* discard the gc protection */ G_stk->discard(2); /* handled */ return TRUE; } /* * For construction, eliminate repeated elements of the vector, leaving * only the unique elements. Reduces the size of the vector to the size * required to accommodate the unique elements. */ void CVmObjVector::cons_uniquify(VMG0_) { size_t cnt; size_t src, dst; /* get the number of elements */ cnt = get_element_count(); /* loop through the list and look for repeated values */ for (src = dst = 0 ; src < cnt ; ++src) { size_t idx; vm_val_t src_val; int found; /* * look for a copy of this source value already in the output list */ get_element(src, &src_val); for (idx = 0, found = FALSE ; idx < dst ; ++idx) { vm_val_t idx_val; /* get this value */ get_element(idx, &idx_val); /* if it's equal to the current source value, note it */ if (src_val.equals(vmg_ &idx_val)) { /* note that we found it */ found = TRUE; /* no need to look any further */ break; } } /* if we didn't find the value, copy it to the output list */ if (!found) { /* * add it to the output vector - since this is a * construction-only method, there's no need to save undo (if * we apply undo, we'll undo the entire construction of the * vector, hence there's no need to track changes made since * creation) */ set_element(dst, &src_val); /* count it in the output */ ++dst; } } /* adjust the size of the result list */ set_element_count(dst); } /* ------------------------------------------------------------------------ */ /* * property evaluator - appendUnique */ int CVmObjVector::getp_append_unique(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_val_t *val2; int cnt2; int cnt; static CVmNativeCodeDesc desc(1); int i; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the other value, but leave it on the stack */ val2 = G_stk->get(0); /* the return value is 'self' */ retval->set_obj(self); /* put myself on the stack for GC protection */ G_stk->push(retval); /* get the number of elements in the original */ cnt = get_element_count(); /* get the number of elements to append */ cnt2 = (val2->is_listlike(vmg0_) ? val2->ll_length(vmg0_) : -1); /* expand myself to make room for the new elements */ expand_by(vmg_ self, cnt2 < 0 ? 1 : cnt2); /* append each element of the right-hand side */ if (cnt2 < 0) { /* the rhs isn't a list, so just append it directly */ set_element_undo(vmg_ self, cnt, val2); } else { /* append each element of the rhs list */ for (i = 1 ; i <= cnt2 ; ++i) { /* get this element */ vm_val_t ele; val2->ll_index(vmg_ &ele, i); /* store it */ set_element_undo(vmg_ self, cnt + i - 1, &ele); } } /* uniquify the result */ uniquify_for_append(vmg_ self); /* discard the gc protection and arguments */ G_stk->discard(2); /* handled */ return TRUE; } /* * uniquify after an appendUnique operation */ void CVmObjVector::uniquify_for_append(VMG_ vm_obj_id_t self) { size_t cnt; size_t src, dst; /* get the number of elements */ cnt = get_element_count(); /* loop through the list and look for repeated values */ for (src = dst = 0 ; src < cnt ; ++src) { size_t idx; vm_val_t src_val; int found; /* look for a copy of this value elsewhere in the vector */ get_element(src, &src_val); for (idx = 0, found = FALSE ; idx < dst ; ++idx) { vm_val_t idx_val; /* get this value */ get_element(idx, &idx_val); /* if it's equal to the current source value, note it */ if (src_val.equals(vmg_ &idx_val)) { /* note that we found it */ found = TRUE; /* no need to look any further */ break; } } /* if we didn't find the value, copy it to the output list */ if (!found) { /* * Add it to the output vector if we're actually changing this * element (i.e., the destination and source indices are * unequal). */ if (dst != src) set_element_undo(vmg_ self, dst, &src_val); /* count it in the output */ ++dst; } } /* adjust the size of the result list */ set_element_count_undo(vmg_ self, dst); } /* ------------------------------------------------------------------------ */ /* * sorter for vector */ class CVmQSortVector: public CVmQSortVal { public: CVmQSortVector() { vec_ = 0; self_ = VM_INVALID_OBJ; } /* get an element from the vector */ void get_ele(VMG_ size_t idx, vm_val_t *val) { vec_->get_element(idx, val); } /* store an element */ void set_ele(VMG_ size_t idx, const vm_val_t *val) { vec_->set_element_undo(vmg_ self_, idx, val); } /* our vector object */ CVmObjVector *vec_; vm_obj_id_t self_; }; /* * property evaluator - sort */ int CVmObjVector::getp_sort(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { size_t len; uint argc = (in_argc == 0 ? 0 : *in_argc); CVmQSortVector sorter; static CVmNativeCodeDesc desc(0, 2); /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* remember the length of my list */ len = get_element_count(); /* set the vector object in the sorter */ sorter.vec_ = this; sorter.self_ = self; /* if we have an 'descending' flag, note it */ if (argc >= 1) sorter.descending_ = (G_stk->get(0)->typ != VM_NIL); /* * if we have a comparison function, note it, but leave it on the * stack for gc protection */ if (argc >= 2) { /* get the function */ sorter.compare_fn_ = *G_stk->get(1); /* initialize the recursive caller descriptor for it */ sorter.rc.init(vmg_ "Vector.sort", self, 20, G_stk->get(0), argc); } /* put myself on the stack for GC protection */ G_interpreter->push_obj(vmg_ self); /* sort the vector, if we have any elements */ if (len != 0) sorter.sort(vmg_ 0, len - 1); /* discard the gc protection and arguments */ G_stk->discard(1 + argc); /* return myself */ retval->set_obj(self); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - set the length */ int CVmObjVector::getp_set_length(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { size_t old_len; int32_t new_len; int32_t idx; vm_val_t nil_val; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* the return value is 'self' */ retval->set_obj(self); /* note the old length */ old_len = get_element_count(); /* get the new length */ new_len = CVmBif::pop_long_val(vmg0_); /* can't go less than zero */ if (new_len < 0 || new_len >= VEC_MAX_ELEMENTS) err_throw(VMERR_BAD_VAL_BIF); /* set the vector to its new size */ set_element_count_undo(vmg_ self, (size_t)new_len); /* * Set each newly added element to nil. Note that we don't bother * saving undo for these changes: to undo this change, the only thing * we'll have to do is reduce the vector size to its previous size, * and we've already saved undo for the size change. */ nil_val.set_nil(); for (idx = old_len ; idx < new_len ; ++idx) set_element((size_t)idx, &nil_val); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - insert one or more elements at a given index */ int CVmObjVector::getp_insert_at(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { /* we must have at least two arguments */ uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(2, 0, TRUE); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* note the original size of the vector */ int old_cnt = get_element_count(); /* * get the starting insertion index; negatives count from the end, and * zero means to insert after the last existing element */ int ins_idx = CVmBif::pop_int_val(vmg0_); if (ins_idx <= 0) ins_idx += old_cnt + 1; /* the return value is 'self' */ retval->set_obj(self); /* * note the number of items we're adding - we're adding each * argument, other than the first */ int add_cnt = argc - 1; /* * if the starting index is out of range, it's an error - it can * range from 1 to one higher than the current element count (the * top end of the range allows us to insert elements after all of * the current elements) */ if (ins_idx < 1 || ins_idx > old_cnt + 1) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* adjust the starting index to a zero-based value */ --ins_idx; /* do the insertion */ insert_elements_undo(vmg_ self, ins_idx, add_cnt); /* set the new element values */ for (int idx = ins_idx ; add_cnt != 0 ; ++idx, --add_cnt) { /* pop the next argument value */ vm_val_t val; G_stk->pop(&val); /* store the item, keeping undo only if it's in the old size range */ if (idx < old_cnt) set_element_undo(vmg_ self, idx, &val); else set_element(idx, &val); } /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - append an element */ int CVmObjVector::getp_prepend(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* insert one element at the start of the vector */ insert_elements_undo(vmg_ self, 0, 1); /* * set the first element - note that there's no need to save undo, * since undoing this operation will simply discard this element (in * other words, there's no previous value for this element since it's * being created anew here) */ set_element(0, G_stk->get(0)); /* the return value is 'self' */ retval->set_obj(self); /* discard arguments */ G_stk->discard(1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - remove an element at the given position */ int CVmObjVector::getp_remove_element_at(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the deletion index; a negative index counts from the end */ int start_idx = CVmBif::pop_int_val(vmg0_); if (start_idx < 0) start_idx += get_element_count() + 1; /* make sure the index is in range */ if (start_idx < 1 || start_idx > (int)get_element_count()) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* adjust to a zero-based index */ --start_idx; /* the return value is 'self' */ retval->set_obj(self); /* delete one element */ remove_elements_undo(vmg_ self, start_idx, 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - remove element(s) matching a given value */ int CVmObjVector::getp_remove_element(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_val_t *del_val; static CVmNativeCodeDesc desc(1); size_t src; size_t dst; size_t old_cnt; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the value to be removed */ del_val = G_stk->get(0); /* the return value is 'self'; leave it on the stack */ retval->set_obj(self); G_stk->push(retval); /* get the original number of elements */ old_cnt = get_element_count(); /* delete each element matching the given value */ for (src = dst = 0 ; src < old_cnt ; ++src) { vm_val_t ele_val; /* get this element */ get_element(src, &ele_val); /* * if this element doesn't match the value to be deleted, add it * to the result vector */ if (!ele_val.equals(vmg_ del_val)) { /* * This element is a keeper - if we're moving it to a new * position (i.e., we've deleted any elements before this * one), store it at its new position, keeping undo * information. If we're storing it at its same position, * there's no need to re-store the value or keep any undo, * since no change is involved. */ if (dst != src) set_element_undo(vmg_ self, dst, &ele_val); /* increment the write index past this element */ ++dst; } } /* if the element count changed, set the new element count */ if (dst != old_cnt) set_element_count_undo(vmg_ self, dst); /* discard gc protection and arguments */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - remove a range of elements */ int CVmObjVector::getp_remove_range(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { int start_idx; int end_idx; static CVmNativeCodeDesc desc(2); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the starting index; a negative value counts from the end */ start_idx = CVmBif::pop_int_val(vmg0_); if (start_idx < 0) start_idx += get_element_count() + 1; /* * make sure the index is in range - it must refer to an existing * element */ if (start_idx < 1 || start_idx > (int)get_element_count()) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* get the ending index, and adjust for negative values */ end_idx = CVmBif::pop_int_val(vmg0_); if (end_idx < 0) end_idx += get_element_count() + 1; /* * make sure it's in range - it must refer to an existing element, * and it must be greater than or equal to the starting index */ if (end_idx < start_idx || end_idx > (int)get_element_count()) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* adjust to zero-based indices */ --start_idx; --end_idx; /* the return value is 'self' */ retval->set_obj(self); /* * delete the elements - the number of elements we're deleting is * one higher than the difference of the starting and ending indices * (if the two indices are the same, we're deleting just the one * element) */ remove_elements_undo(vmg_ self, start_idx, end_idx - start_idx + 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - append an element */ int CVmObjVector::getp_append(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_val_t *valp; size_t cnt; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the value to append, but leave it on the stack */ valp = G_stk->get(0); /* get the old size */ cnt = get_element_count(); /* the result object is 'self' */ retval->set_obj(self); /* push a self-reference for gc protection */ G_stk->push(retval); /* expand myself by one element to make room for the addition */ expand_by(vmg_ self, 1); /* add the new element, saving undo */ set_element_undo(vmg_ self, cnt, valp); /* discard the argument and gc protection */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - append elements from a collection, or append a * single non-collection element */ int CVmObjVector::getp_append_all(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the value to append, but leave it on the stack */ vm_val_t *valp = G_stk->get(0); /* the result object is 'self' */ retval->set_obj(self); /* push a self-reference for gc protection */ G_stk->push(retval); /* get the old size */ int cnt = get_element_count(); /* get the number of items to add */ int add_cnt = valp->ll_length(vmg0_); /* expand myself to make room for the new elements */ expand_by(vmg_ self, add_cnt); /* add the new elements */ for (int i = 1 ; i <= add_cnt ; ++i) { /* get the next new element */ vm_val_t ele; valp->ll_index(vmg_ &ele, i); /* * add the new element - note that we don't need to keep undo * because we created this new element in this same operation, and * hence undoing the operation will truncate the vector to exclude * the element, and hence we don't need an old value for the * element */ set_element(cnt + i - 1, &ele); } /* discard the argument and gc protection */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - splice */ int CVmObjVector::getp_splice(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(2, 0, TRUE); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the current vector size */ int old_cnt = get_element_count(); /* get the starting index, and adjust a zero or negative index */ int start_idx = CVmBif::pop_int_val(vmg0_); if (start_idx <= 0) start_idx += old_cnt + 1; /* adjust to a zero-based starting index */ --start_idx; /* make sure the starting index is in range */ if (start_idx < 0 || start_idx > old_cnt) err_throw(VMERR_INDEX_OUT_OF_RANGE);; /* get the deletion count */ int del_cnt = CVmBif::pop_int_val(vmg0_); /* adjust the deletion count to the number of elements remaining */ if (del_cnt > old_cnt - start_idx) del_cnt = old_cnt - start_idx; /* get a pointer to the first inserted item, and the number of items */ vm_val_t *ins = G_stk->get(0); int ins_cnt = argc - 2; /* the result object is 'self'; push self for gc protection */ retval->set_obj(self); G_stk->push(retval); /* * If we're doing a net deletion, contract the vector. If we're doing * a net expansion, expand it. */ if (del_cnt > ins_cnt) { /* net deletion - remove the excess of deletions over insertions */ remove_elements_undo( vmg_ self, start_idx + ins_cnt, del_cnt - ins_cnt); } else if (del_cnt < ins_cnt) { /* net insertion - add room for the excess inserted items */ insert_elements_undo( vmg_ self, start_idx + del_cnt, ins_cnt - del_cnt); } /* set the new elements */ for (int i = 0 ; i < ins_cnt ; ++i, --ins) { /* * If this element is within the old vector's size range, save * undo, since we're overwriting a pre-existing element. If it's * beyond the end of the old vector, there's no need to keep undo * since the overall undo operation will simply shrink the vector * to eliminate the slot. */ if (i < old_cnt) set_element_undo(vmg_ self, start_idx + i, ins); else set_element(start_idx + i, ins); } /* discard the arguments and gc protection */ G_stk->discard(ins_cnt + 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Insert room for new elements. Inserts the given number of elements at * the given starting index. Leaves the old elements in the added slots * unchanged; the caller is responsible for setting the element values. */ void CVmObjVector::insert_elements_undo(VMG_ vm_obj_id_t self, int ins_idx, int add_cnt) { /* note the new and old sizes */ int old_cnt = get_element_count(); int new_cnt = old_cnt + add_cnt; /* create the new elements */ set_element_count_undo(vmg_ self, new_cnt); /* * Move the existing elements to their new slots. Start with the * starting index, and move it to its new position, since the starting * index is the first element that needs to be moved. Note that we * start at the top and work down, to avoid overwriting any overlapping * part of the new position block and the old position block. * * If we're inserting after the last element, there's no moving that * needs to be done. */ if (ins_idx < old_cnt) { for (int idx = old_cnt - 1 ; idx >= ins_idx ; --idx) { /* get the value at this slot */ vm_val_t ele; get_element(idx, &ele); /* calculate the new slot */ int new_idx = idx + add_cnt; /* * if the new index is within the old size range, keep undo for * the change; otherwise, no undo is necessary, since when we * undo we'll be completely dropping this new slot */ if (new_idx < old_cnt) set_element_undo(vmg_ self, new_idx, &ele); else set_element(new_idx, &ele); } } } /* * Delete a range of elements */ void CVmObjVector::remove_elements_undo(VMG_ vm_obj_id_t self, size_t start_idx, size_t del_cnt) { /* note the original size of the vector */ size_t cnt = get_element_count(); /* move all of the elements to their new slots, keeping undo */ for (size_t idx = start_idx ; idx < cnt ; ++idx) { /* * calculate the index of the element to replace this one - it's * past us by the number of items being deleted */ size_t move_idx = idx + del_cnt; /* * if we're moving from a valid index in the old vector, get the * element; otherwise, replace the value with nil (but still * replace the value, since we want to keep undo for its original * value) */ vm_val_t val; if (move_idx < cnt) get_element(move_idx, &val); else val.set_nil(); /* store the element at its new position */ set_element_undo(vmg_ self, idx, &val); } /* * Reduce the size, keeping undo. Note that it's important we do * this last: undo is applied in reverse order, so when applying * undo, we first want to increase the vector's size to its original * size, then we want to apply the element value restorations. */ set_element_count_undo(vmg_ self, cnt - del_cnt); } /* ------------------------------------------------------------------------ */ /* * Property evaluator - join into a string */ int CVmObjVector::getp_join(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use the generic list joiner */ vm_val_t vself; vself.set_obj(self); return CVmObjList::getp_join(vmg_ retval, &vself, 0, argc); } /* * C++ interface to join() */ void CVmObjVector::join(VMG_ vm_val_t *retval, vm_obj_id_t self, const char *sep, size_t sep_len) const { vm_val_t vself; vself.set_obj(self); return CVmObjList::join(vmg_ retval, &vself, sep, sep_len); } /* ------------------------------------------------------------------------ */ /* * Explicitly convert to a string */ const char *CVmObjVector::explicit_to_string( VMG_ vm_obj_id_t self, vm_val_t *new_str, int radix, int flags) const { /* use the generic list converter */ vm_val_t vself; vself.set_obj(self); return CVmObjList::list_to_string(vmg_ new_str, &vself, radix, flags); } /* ------------------------------------------------------------------------ */ /* * Property evaluator - generate a new vector */ int CVmObjVector::static_getp_generate(VMG_ vm_val_t *retval, uint *in_argc) { /* check arguments */ uint argc = (in_argc == 0 ? 0 : *in_argc); static CVmNativeCodeDesc desc(2); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* get the callback function argument */ vm_val_t *func = G_stk->get(0); /* make sure it's a function; set up a header pointer if it is */ CVmFuncPtr funcdesc; if (!funcdesc.set(vmg_ func)) err_throw(VMERR_BAD_TYPE_BIF); /* get the count */ int32_t cnt = G_stk->get(1)->num_to_int(vmg0_); /* make sure it's not negative */ if (cnt < 0) err_throw(VMERR_BAD_VAL_BIF); /* create the vector */ retval->set_obj(CVmObjVector::create(vmg_ FALSE, cnt)); CVmObjVector *vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj); /* push the new vector for gc protection */ G_stk->push(retval); /* find out how many arguments the function wants */ int fargc = funcdesc.is_varargs() ? -1 : funcdesc.get_max_argc(); /* set up our recursive call descriptor */ vm_rcdesc rc(vmg_ "Vector.generate", CVmObjVector::metaclass_reg_->get_class_obj(vmg0_), PROPIDX_generate, func, argc); /* generate the elements */ for (int i = 0 ; i < cnt ; ++i) { /* push the callback arguments */ const int pushed_argc = 1; G_stk->push()->set_int(i+1); /* adjust the arguments for what the callback actually wants */ int sent_argc = fargc < 0 || fargc > pushed_argc ? pushed_argc : fargc; /* call the callback */ G_interpreter->call_func_ptr(vmg_ func, sent_argc, &rc, 0); /* discard excess arguments */ G_stk->discard(pushed_argc - sent_argc); /* add the result to the list */ vec->set_element(i, G_interpreter->get_r0()); vec->set_element_count(i+1); } /* discard arguments and gc protection */ G_stk->discard(argc + 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator: get the index of the element with the minimum value */ int CVmObjVector::getp_indexOfMin(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* do the general calculation to select the minimum value ("-1") */ vm_rcdesc rc(vmg_ "Vector.indexOfMin", self, 32, G_stk->get(0), argc != 0 ? *argc : 0); return get_minmax(vmg_ self, retval, argc, &rc, -1, TRUE); } /* * Property evaluator: get the value of the element with the minimum value */ int CVmObjVector::getp_minVal(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* do the general calculation to select the minimum value ("-1") */ vm_rcdesc rc(vmg_ "Vector.minVal", self, 33, G_stk->get(0), argc != 0 ? *argc : 0); return get_minmax(vmg_ self, retval, argc, &rc, -1, FALSE); } /* * Property evaluator: get the index of the element with the maximum value */ int CVmObjVector::getp_indexOfMax(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* do the general calculation to select the maximum value ("+1") */ vm_rcdesc rc(vmg_ "Vector.indexOfMax", self, 34, G_stk->get(0), argc != 0 ? *argc : 0); return get_minmax(vmg_ self, retval, argc, &rc, 1, TRUE); } /* * Property evaluator: get the value of the element with the maximum value */ int CVmObjVector::getp_maxVal(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* do the general calculation to select the maximum value ("+1") */ vm_rcdesc rc(vmg_ "Vector.maxVal", self, 35, G_stk->get(0), argc != 0 ? *argc : 0); return get_minmax(vmg_ self, retval, argc, &rc, 1, FALSE); } /* * General handling for minIndex, minVal, maxIndex, and maxVal. This finds * the highest/lowest value in the list, optionally mapped through a * callback function. Returns the winning element's index and value. * * 'sense' is the sense of the search: -1 for minimum, 1 for maximum. * 'want_index' is true if 'retval' should be set to the index of the * winning element, false if 'retval' should be set to the winning * element's value. */ int CVmObjVector::get_minmax(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc, const vm_rcdesc *rc, int sense, int want_index) { /* check arguments */ uint argc = (in_argc == 0 ? 0 : *in_argc); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* make sure the 'func' argument is valid */ const vm_val_t *cb = 0; if (argc >= 1) { cb = G_stk->get(0); if (!cb->is_func_ptr(vmg0_)) err_throw(VMERR_BAD_TYPE_BIF); } /* push 'self' for gc protection */ G_stk->push()->set_obj(self); /* if there are no elements, this is an error */ int cnt = get_element_count(); if (cnt == 0) err_throw(VMERR_BAD_VAL_BIF); /* we don't have a winner yet */ int winner_idx = 0; vm_val_t winner; /* run through the list */ for (int i = 0 ; i < cnt ; ++i) { /* get this element */ vm_val_t ele; get_element(i, &ele); /* if there's a callback, invoke it */ if (cb != 0) { /* invoke the callback with the element value as the argument */ G_stk->push(&ele); G_interpreter->call_func_ptr(vmg_ cb, 1, rc, 0); /* use the result as the comparison value */ ele = *G_interpreter->get_r0(); } /* compare it to the winner so far, and keep the lower value */ if (i == 0) { /* it's the first element, so it's the default winner so far */ winner_idx = i; winner = ele; } else { /* compare it to the winner so far */ int cmp = ele.compare_to(vmg_ &winner); /* keep it if it's the min/max so far */ if ((sense > 0 && cmp > 0) || (sense < 0 && cmp < 0)) { winner_idx = i; winner = ele; } } } /* remove gc protection and arguments */ G_stk->discard(argc + 1); /* return the winning index (note: 1-based) or value, as desired */ if (want_index) retval->set_int(winner_idx + 1); else *retval = winner; /* handled */ return TRUE; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmvec.h���������������������������������������������������������������������������0000664�0000000�0000000�00000052502�12650170723�0015153�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmvec.h - T3 VM Vector class Function A Vector is a subclass of Collection. A Vector is a List whose elements can be changed in-place, and whose size can change. Notes Modified 05/14/00 MJRoberts - Creation */ #ifndef VMVEC_H #define VMVEC_H #include <stdlib.h> #include "vmtype.h" #include "vmobj.h" #include "vmcoll.h" #include "vmglob.h" #include "vmstack.h" #include "vmrun.h" /* ------------------------------------------------------------------------ */ /* * Vectors are very similar to lists, but a vector's contents can be * modified during execution. Because vectors are dynamic, a vector's * image file data are copied into the heap. * * A vector's image file data structure looks like this: * * UINT2 elements_allocated *. UINT2 number_of_elements_used *. DATAHOLDER element[1] *. DATAHOLDER element[2] *. etc, up to number_of_elements_used (NOT the number allocated) * * The object extension for a vector is almost identical, but adds a prefix * giving the "allocated" size of the vector, and a suffix with a pointer * to our original image data (if applicable) plus one bit per element * after the end of the element[] list for undo bits: * * UINT2 allocated_elements *. UINT2 number_of_elements *. DATAHOLDER element[1] *. DATAHOLDER element[2] *. etc *. undo_bits * * The allocated_elements counter gives the total number of elements * allocated in the vector; this can be greater than the number of elements * actually in use, in which case we have free space that we can use to add * elements without reallocating the vector's memory. * * The undo_bits are packed 8 bits per byte. Note that the undo_bits * follow all allocated slots, so these do not change location when we * change the number of elements actually in use. */ /* ------------------------------------------------------------------------ */ /* * Vector metaclass */ class CVmObjVector: public CVmObjCollection { friend class CVmMetaclassVector; friend class CVmQSortVector; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObjCollection::is_of_metaclass(meta)); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* create with no initial contents */ static vm_obj_id_t create(VMG_ int in_root_set); /* create with a given number of elements */ static vm_obj_id_t create(VMG_ int in_root_set, size_t element_count); /* create with initial contents */ static vm_obj_id_t create_fill(VMG_ int in_root_set, size_t element_count, ...); /* * determine if an object is a vector - it is if the object's * virtual metaclass registration index matches the class's static * index */ static int is_vector_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* vectors are list-like for read access */ int is_listlike(VMG_ vm_obj_id_t) { return TRUE; } int ll_length(VMG_ vm_obj_id_t) { return get_element_count(); } /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* call a static property */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* cast to string */ const char *cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const { /* join the vector into a string, using commas as separators */ join(vmg_ new_str, self, ",", 1); /* return the new string */ return new_str->get_as_string(vmg0_); } virtual const char *explicit_to_string( VMG_ vm_obj_id_t self, vm_val_t *new_str, int radix, int flags) const; /* add a value to the vector, yielding a new vector */ int add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* subtract a value from the vector, yielding a new vector */ int sub_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* undo operations */ void notify_new_savept(); void apply_undo(VMG_ struct CVmUndoRecord *rec); void mark_undo_ref(VMG_ struct CVmUndoRecord *rec); /* we keep only strong references */ void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* mark references */ void mark_refs(VMG_ uint state); /* * remove weak references - we keep only normal (strong) references, * so this routine doesn't need to do anything */ void remove_stale_weak_refs(VMG0_) { } /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from the image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* index the vector */ int index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val); /* set an indexed element of the vector */ int set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val); /* * Check a value for equality. We will match any list or vector with * the same number of elements and the same value for each element. */ int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const; /* calculate a hash value for the vector */ uint calc_hash(VMG_ vm_obj_id_t self, int depth) const; /* * assume that we've been changed since loading, if we came from the * image file */ int is_changed_since_load() const { return TRUE; } /* * get the allocated number of elements of the vector - this will be * greater than or equal to get_element_count(), and reflects the * actual allocated size */ size_t get_allocated_count() const { return vmb_get_len(get_vector_ext_ptr()); } /* get the number of elements in the vector */ size_t get_element_count() const { return vmb_get_len(get_vector_ext_ptr() + 2); } /* get an element without any range checking; 0-based index */ void get_element(size_t idx, vm_val_t *val) const { /* get the data from the data holder in our extension */ vmb_get_dh(get_element_ptr(idx), val); } /* append an element, with no undo */ void append_element(VMG_ vm_obj_id_t self, const vm_val_t *val); /* set an element; 0-based index; no bounds checking or undo */ void set_element(size_t idx, const vm_val_t *val) { /* set the element's data holder from the value */ vmb_put_dh(get_element_ptr(idx), val); } /* set an element, recording undo for the change; 0-based index */ void set_element_undo(VMG_ vm_obj_id_t self, size_t idx, const vm_val_t *val); /* * join the list into a string - this is the C++ interface to the * self.join() method */ void join(VMG_ vm_val_t *retval, vm_obj_id_t self, const char *sep, size_t sep_len) const; protected: /* load image data */ void load_image_data(VMG_ const char *ptr, size_t siz); /* for construction, remove all redundant elements */ void cons_uniquify(VMG0_); /* uniquify the value for an appendUnique operation */ void uniquify_for_append(VMG_ vm_obj_id_t self); /* create an iterator */ virtual void new_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val); /* create a live iterator */ virtual void new_live_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val); /* create a copy of the object */ vm_obj_id_t create_copy(VMG_ const vm_val_t *self_val); /* create with no initial contents */ CVmObjVector() { ext_ = 0; } /* create with a given number of elements */ CVmObjVector(VMG_ size_t element_count); /* get a pointer to my vector data */ const char *get_vector_ext_ptr() const { return ext_; } /* * get my extension data pointer for construction purposes - this is a * writable pointer, so that a caller can fill our data buffer during * construction */ char *cons_get_vector_ext_ptr() const { return ext_; } /* * Calculate the amount of space we need to store a list of a given * length. We require two bytes for the length prefix, plus the space * for each element, plus the space for the undo bits (one bit per * element, but we pack eight bits per byte). */ static size_t calc_alloc(size_t elecnt) { return (calc_alloc_prefix_and_ele(elecnt) + ((elecnt + 7) & ~7)); } /* * calculate the allocation amount, including only the count prefix * and element data */ static size_t calc_alloc_prefix_and_ele(size_t elecnt) { return (2*VMB_LEN + calc_alloc_ele(elecnt)); } /* calculate the allocation amount, including only the element storage */ static size_t calc_alloc_ele(size_t elecnt) { return elecnt * VMB_DATAHOLDER; } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* allocate space for the vector, given the number of elements */ void alloc_vector(VMG_ size_t element_count); /* expand to accommodate the given number of new elements */ void expand_by(VMG_ vm_obj_id_t self, size_t added_elements); /* get the allocation count increment */ size_t get_alloc_count_increment() const { /* * we might want to make this parameterizable; for now use a * fixed increment size */ return 16; } /* push an element onto the stack */ void push_element(VMG_ size_t idx) const { /* push a stack element */ vm_val_t *p = G_stk->push(); /* * get the data from the data holder in our extension directly into * our new stack element */ vmb_get_dh(get_element_ptr(idx), p); } /* set the allocated size */ void set_allocated_count(size_t cnt); /* given an index, get a pointer to the element's data in the list */ char *get_element_ptr(size_t idx) const { /* * figure out where this element's data holder is by skipping the * count prefix, then skipping past preceding data holders */ return cons_get_vector_ext_ptr() + 2*VMB_LEN + (idx * VMB_DATAHOLDER); } /* get a constant pointer to an element */ static const char *get_element_ptr_const(const char *ext, size_t idx) { return ext + 2*VMB_LEN + (idx * VMB_DATAHOLDER); } /* increment an element pointer */ static void inc_element_ptr(char **ptr) { *ptr += VMB_DATAHOLDER; } /* get a given undo bit */ int get_undo_bit(size_t idx) const { char *p; int bitnum; /* get the byte containing the bit for this index */ p = get_undo_ptr() + (idx >> 3); /* get the bit number within the byte */ bitnum = (idx & 7); /* get this bit */ return ((*p & (1 << bitnum)) != 0); } /* set a given undo bit */ void set_undo_bit(size_t idx, int val) { char *p; int bitnum; /* get the byte containing the bit for this index */ p = get_undo_ptr() + (idx >> 3); /* get the bit number within the byte */ bitnum = (idx & 7); /* set or clear the bit */ if (val) *p |= (1 << bitnum); else *p &= ~(1 << bitnum); } /* clear all undo bits */ void clear_undo_bits() { memset(get_undo_ptr(), 0, ((get_allocated_count() + 7) & ~7)); } /* get a pointer to the first byte of the undo bits */ char *get_undo_ptr() const { /* the undo bits follow the last element data */ return (cons_get_vector_ext_ptr() + 2*VMB_LEN + get_allocated_count()*VMB_DATAHOLDER); } /* set the number of elements in the vector; no undo or size checking */ void set_element_count(size_t cnt) { vmb_put_len(cons_get_vector_ext_ptr() + 2, cnt); } /* set the element count, saving undo for the change */ void set_element_count_undo(VMG_ vm_obj_id_t self, size_t new_element_count); /* insert elements into the vector, keeping undo */ void insert_elements_undo(VMG_ vm_obj_id_t self, int ins_idx, int add_cnt); /* delete elements from the vector, keeping undo */ void remove_elements_undo(VMG_ vm_obj_id_t self, size_t start_idx, size_t del_cnt); /* general handler for indexOf, lastIndexOf, and countOf */ int gen_index_of(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc, int forward, int count_only); /* general handler for indexWhich, lastIndexWhich, and countWhich */ int gen_index_which(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc, int forward, int count_only, vm_rcdesc *rc); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - convert to a list */ int getp_to_list(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - get the number of elements */ int getp_get_size(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - copy from another vector or list */ int getp_copy_from(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - fill with a value */ int getp_fill_val(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - select a subset */ int getp_subset(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - apply a callback to each element */ int getp_apply_all(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - find the first element satisfying a condition */ int getp_index_which(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - call a callback on each element */ int getp_for_each(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - call a callback(key,val) on each element */ int getp_for_each_assoc(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* general processor for forEach/forEachAssoc */ int for_each_gen(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc, int pass_key_to_cb, vm_rcdesc *rc); /* property evaluator - mapAll */ int getp_map_all(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - indexOf */ int getp_index_of(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - valWhich */ int getp_val_which(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - lastIndexOf */ int getp_last_index_of(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - lastIndexWhich */ int getp_last_index_which(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - valWhich */ int getp_last_val_which(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - countOf */ int getp_count_of(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - countWhich */ int getp_count_which(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - getUnique */ int getp_get_unique(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - appendUnique */ int getp_append_unique(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - sort */ int getp_sort(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - set the length */ int getp_set_length(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - insertAt */ int getp_insert_at(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - remove a single element at a given index */ int getp_remove_element_at(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - removeRange */ int getp_remove_range(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - append a value */ int getp_append(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - prepend a value */ int getp_prepend(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - append everything from a collection */ int getp_append_all(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - remove any elements with a given value */ int getp_remove_element(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - join into a string */ int getp_join(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - generate */ int getp_generate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { return static_getp_generate(vmg_ retval, argc); } /* static property evaluator - generate */ static int static_getp_generate(VMG_ vm_val_t *retval, uint *argc); /* property evaluator - splice */ int getp_splice(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get the index of the minimum element */ int getp_indexOfMin(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get the minimum value in the list */ int getp_minVal(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get the index of the maximum element */ int getp_indexOfMax(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get the maximum value in the list */ int getp_maxVal(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* general handler for the various min/max methods */ int get_minmax(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, const vm_rcdesc *rc, int sense, int want_index); /* property evaluation function table */ static int (CVmObjVector::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassVector: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "vector/030005"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjVector(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjVector(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjVector::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjVector::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* I'm a Collection object */ CVmMetaclass *get_supermeta_reg() const { return CVmObjCollection::metaclass_reg_; } }; #endif /* VMVEC_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjVector) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/tads3/vmvsn.h���������������������������������������������������������������������������0000664�0000000�0000000�00000002670�12650170723�0015205�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 1999, 2006 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmvsn.h - VM Version Information Function Notes Modified 07/12/99 MJRoberts - Creation */ #ifndef VMVSN_H #define VMVSN_H /* * The VM version number. A VM program can obtain this value through * the get_vm_vsn() function in the T3VM intrinsic function set. * * The value is encoded as a 32-bit value with the major version number * in the high-order 16 bits, the minor version number in the next 8 * bits, and the patch release number in the low-order 8 bits. So, the * release 1.2.3 would be encoded as 0x00010203. */ #define MAKE_VERSION_NUMBER(major,minor,maint) \ (((major) << 16) | ((minor) << 8) | (maint)) #define T3VM_VSN_NUMBER MAKE_VERSION_NUMBER(3,1,3) /* * The VM identification string */ #define T3VM_IDENTIFICATION "mjr-T3" /* * The VM short version string. This contains merely the version number, * in display format. */ #define T3VM_VSN_STRING "3.1.3" /* * The VM banner string. A VM program can obtain this value through the * get_vm_banner() function in the T3VM intrinsic function set. */ /* copyright-date-string */ #define T3VM_BANNER_STRING \ "T3 VM " T3VM_VSN_STRING " - Copyright 1999, 2012 Michael J. Roberts" #endif /* VMVSN_H */ ������������������������������������������������������������������������qtads-2.1.7/tads3/vmwrtimg.h������������������������������������������������������������������������0000664�0000000�0000000�00000026122�12650170723�0015706�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/vmwrtimg.h,v 1.4 1999/07/11 00:46:59 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmwrtimg.h - T3 Image File Writer utility functions Function Notes Modified 04/04/99 MJRoberts - Creation */ #ifndef VMWRTIMG_H #define VMWRTIMG_H #include "t3std.h" #include "vmtype.h" class CVmImageWriter { public: /* create an image writer with a given output file */ CVmImageWriter(class CVmFile *fp); /* delete the image writer */ ~CVmImageWriter(); /* get the current seek location in the underlying file */ long get_pos() const; /* * Prepare the file: write out the fixed header information. This * should be called once, before doing any other writing. 'vsn' is * the image file version number to store in the file. */ void prepare(uint vsn, const char tool_data[4]); /* * Begin a block with the specified ID (a four-byte string). Writes * the block header, and remembers where the block starts so that * the size prefix can be stored when the block is finished. Blocks * cannot be nested; starting a new block automatically ends the * previous block. */ void begin_block(const char *block_id, int mandatory); /* * End the current block. */ void end_block(); /* write raw bytes to the image file */ void write_bytes(const char *ptr, uint32_t siz); /* * Write a complete entrypoint block, given the code offset of the * entrypoint and the various table entry sizes. If a block is in * progress, this will terminate it. */ void write_entrypt(uint32_t entry_ofs, size_t method_header_size, size_t exc_entry_size, size_t line_entry_size, size_t dbg_hdr_size, size_t dbg_lclsym_hdr_size, size_t dbg_frame_hdr_size, int dbg_vsn_id); /* * Write a complete function set dependency block, given an array of * function set names. If a block is in progress, this will * terminate it. */ void write_func_dep(const char **funcset_names, int count); /* * Write a complete metaclass dependency block, given an array of * metaclass names. If a block is in progress, this will terminate * it. */ void write_meta_dep(const char **metaclass_names, int count); /* * Write a metaclass dependency block in pieces. Call * begin_meta_dep() with the number of metaclasses, then call * write_meta_dep_item() to write each item, and finally call * end_meta_dep() to finish the block. */ void begin_meta_dep(int count); void write_meta_dep_item(const char *metaclass_name); void write_meta_item_prop(uint prop_id); void end_meta_prop_list(); void end_meta_dep(); /* * Write a function set dependency block in pieces */ void begin_func_dep(int count) { begin_dep_block("FNSD", count); } void write_func_dep_item(const char *funcset_name) { write_dep_block_item(funcset_name); } void end_func_dep() { end_dep_block(); } /* * Write a constant pool definition block. */ void write_pool_def(uint pool_id, uint32_t page_count, uint32_t page_size, int mandatory); /* * Fix up a constant pool definition block with the actual number of * pages. This can be used, if desired, to wait to determine the * actual number of pages in a given constant pool until after * writing the pages. Since the pool definition block must precede * the pool's first page block in the image file, it's impossible to * wait until after writing all of the pages to write the definition * block. Instead, the caller must write the pool definition block * first, using a temporary placeholder value for the page_count (0 * will suffice), then write the pool pages, then use this to fix up * the pool definition block with the actual number of pages. The * caller must note the seek position prior to writing the pool * definition block, so that we can seek back to that position to * fix up the definition block. * * Callers need not use this function if they know the actual number * of pages in the pool when writing the original pool definition * block. */ void fix_pool_def(long def_seek_ofs, uint32_t page_count); /* * Write a constant/code pool page. This writes the entire page * with its header and data in a single operation; this is the * easiest way to write a page when the page has been fully * constructed in a single memory block in advance. */ void write_pool_page(uint pool_id, uint32_t page_index, const char *page_data, uint32_t page_data_size, int mandatory, uchar xor_mask); /* * Write a constant/code pool page in pieces. These routines can be * used when the data in the page are not contiguous in memory and * must be written in pieces. Start by calling begin_pool_page(), * then call write_pool_page_bytes() for each item; the items are * written contiguously to the page. Finish by calling * end_pool_page(). */ void begin_pool_page(uint pool_id, uint32_t page_index, int mandatory, uchar xor_mask); void write_pool_page_bytes(const char *buf, uint32_t siz, uchar xor_mask); void end_pool_page(); /* * Write items in the symbolic names block. Start with * begin_sym_block(), then call the write_sym_item_xxx() functions * to write the names. Finally, call end_sym_block() when done. */ void begin_sym_block(); void write_sym_item_objid(const char *nm, size_t len, ulong obj_id); void write_sym_item_propid(const char *nm, size_t len, uint prop_id); void write_sym_item_func(const char *nm, size_t len, ulong code_ofs); void write_sym_item(const char *nm, size_t nmlen, const struct vm_val_t *val); void write_sym_item_objid(const char *nm, ulong obj_id) { write_sym_item_objid(nm, get_strlen(nm), obj_id); } void write_sym_item_propid(const char *nm, uint prop_id) { write_sym_item_propid(nm, get_strlen(nm), prop_id); } void write_sym_item(const char *nm, const struct vm_val_t *val) { write_sym_item(nm, get_strlen(nm), val); } void end_sym_block(); /* * Write items in an OBJS (static object data) block. Start with * begin_objs_block(), then call write_objs_bytes() repeatedly to * write the bytes. Finally, call end_objs_block() when done. * * If the 'large_objects' field is set, the objects in the block use * 32-bit size fields; otherwise the objects use 16-bit size fields. * * If 'trans' is true, the objects in this block are transient; * otherwise, the objects are non-transient (i.e., persistent). */ void begin_objs_block(uint metaclass_idx, int large_objects, int trans); void write_objs_bytes(const char *buf, uint32_t siz); void end_objs_block(uint object_count); /* * Write the items in a SRCF (source file descriptor) block. Start * with begin_srcf_block(), then write the file entries. For each * entry, call begin_src_entry(), then call write_src_line_entry() * for each source line debug record, then call end_srcf_entry(). * Call end_srcf_block() when done with all file entries. */ void begin_srcf_block(int count); void begin_srcf_entry(int orig_index, const char *fname); void write_srcf_line_entry(ulong linenum, ulong addr); void end_srcf_entry(); void end_srcf_block(); /* * Write the items in a GSYM (global symbol table) block. Start * with begin_gsym_block(), then call write_gsym_entry() repeatedly * to write the entries. Call end_gsym_block() when done. */ void begin_gsym_block(); void write_gsym_entry(const char *sym, size_t sym_len, int type_id, const char *dat, size_t dat_len); void end_gsym_block(ulong count); /* * Begin MHLS block, write an item, and end the block */ void begin_mhls_block(); void write_mhls_entry(ulong code_addr); void end_mhls_block(); /* * Begin/end SINI block. static_cs_ofs is the offset in the code * segment of the first static initializer; this is useful in the * image file because we can delete all of the code pages starting * at this point after pre-initialization is complete. */ void begin_sini_block(ulong static_cs_ofs, ulong init_cnt); void end_sini_block(); /* begin/end a MACR (macro symbols) block */ void begin_macr_block(); void end_macr_block(); /* * Finish the file. Automatically ends the current block if a block * is open, and writes the end-of-file marker. */ void finish(); /* * get the underlying file object; for some types of blocks, it's * simplest for the caller to write the data directly to the underlying * file stream without any help from us */ class CVmFile *get_fp() const { return fp_; } private: /* write a generic dependency (function set, metaclass) block */ void write_dep_block(const char *block_id, const char **names, int count); /* write a dependency block in pieces */ void begin_dep_block(const char *block_id, int count); void write_dep_block_item(const char *nm); void end_dep_block(); /* XOR a block of bytes with a mask and write the results to the file */ void xor_and_write_bytes(const char *p, uint32_t len, uchar xor_mask); /* underlying file */ class CVmFile *fp_; /* * Seek position of start of current block. If this is zero, no * block is currently open. */ long block_start_; /* count of symbolic names written so far (for writing SYMD block) */ int symd_cnt_; /* location of metaclass entry next-record offset */ long mcld_ofs_pos_; /* location of metaclass property list count prefix */ long mcld_propcnt_pos_; /* count of properties writeen in a metaclass prop list so far */ int mcld_prop_cnt_; /* seek location of SYMD count prefix, for fixing up at end of block */ long symd_prefix_; /* seek location of OBJS count prefix, for fixup up at end of block */ long objs_prefix_; /* seek location of GSYM count prefix, for fixup at end of block */ long gsym_prefix_; /* start of current SRCF file entry */ long srcf_entry_pos_; /* count of SRCF line entries */ long srcf_line_cnt_; /* position of SRCF line entry for the current file */ long srcf_line_pos_; /* position of MHLS block count entry, and MHLS entry count so far */ long mhls_cnt_pos_; ulong mhls_cnt_; }; #endif /* VMWRTIMG_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������qtads-2.1.7/w32_linkscript��������������������������������������������������������������������������0000664�0000000�0000000�00000000070�12650170723�0015435�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������SECTIONS { .rsrc : { KEEP(*(.rsrc)) } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������